From 66ed46dfb83abc7cd926b0145d849c8b43163c09 Mon Sep 17 00:00:00 2001 From: John Harwell Date: Wed, 29 Aug 2018 08:41:44 -0500 Subject: [PATCH 001/427] Resolve WB-819: Thin annotation lines are difficult to select: When drawing the lines for identification, use a minimum thickness of about 5 pixels. This makes selection of the lines much easier. --- ...inOpenGLAnnotationDrawingFixedPipeline.cxx | 35 +++++++++++++++++-- ...rainOpenGLAnnotationDrawingFixedPipeline.h | 3 ++ 2 files changed, 35 insertions(+), 3 deletions(-) diff --git a/src/Brain/BrainOpenGLAnnotationDrawingFixedPipeline.cxx b/src/Brain/BrainOpenGLAnnotationDrawingFixedPipeline.cxx index 1fa7cc490..0f3e46989 100644 --- a/src/Brain/BrainOpenGLAnnotationDrawingFixedPipeline.cxx +++ b/src/Brain/BrainOpenGLAnnotationDrawingFixedPipeline.cxx @@ -58,6 +58,7 @@ #include "GraphicsPrimitiveV3fC4f.h" #include "GraphicsPrimitiveV3fT3f.h" #include "GraphicsShape.h" +#include "GraphicsUtilitiesOpenGL.h" #include "IdentificationWithColor.h" #include "MathFunctions.h" #include "Matrix4x4.h" @@ -1203,13 +1204,14 @@ BrainOpenGLAnnotationDrawingFixedPipeline::drawBox(AnnotationFile* annotationFil * Drawing foreground as line will still allow user to * select annotation that are inside of the box */ + const float percentHeight = getLineWidthPercentageInSelectionMode(box); GraphicsShape::drawBoxOutlineByteColor(bottomLeft, bottomRight, topRight, topLeft, selectionColorRGBA, GraphicsPrimitive::LineWidthType::PERCENTAGE_VIEWPORT_HEIGHT, - box->getLineWidthPercentage()); + percentHeight); } m_selectionInfo.push_back(SelectionInfo(annotationFile, @@ -2127,11 +2129,12 @@ BrainOpenGLAnnotationDrawingFixedPipeline::drawOval(AnnotationFile* annotationFi * Drawing foreground as line will still allow user to * select annotation that are inside of the box */ + const float percentHeight = getLineWidthPercentageInSelectionMode(oval); GraphicsShape::drawEllipseOutlineByteColor(majorAxis * 2.0f, minorAxis * 2.0f, selectionColorRGBA, GraphicsPrimitive::LineWidthType::PERCENTAGE_VIEWPORT_HEIGHT, - oval->getLineWidthPercentage()); + percentHeight); } m_selectionInfo.push_back(SelectionInfo(annotationFile, @@ -2923,10 +2926,11 @@ BrainOpenGLAnnotationDrawingFixedPipeline::drawLine(AnnotationFile* annotationFi uint8_t selectionColorRGBA[4]; getIdentificationColor(selectionColorRGBA); + const float percentHeight = getLineWidthPercentageInSelectionMode(line); GraphicsShape::drawLinesByteColor(lineCoordinates, selectionColorRGBA, GraphicsPrimitive::LineWidthType::PERCENTAGE_VIEWPORT_HEIGHT, - line->getLineWidthPercentage()); + percentHeight); if ( ! startArrowCoordinates.empty()) { GraphicsShape::drawLineStripMiterJoinByteColor(startArrowCoordinates, selectionColorRGBA, @@ -2980,6 +2984,31 @@ BrainOpenGLAnnotationDrawingFixedPipeline::drawLine(AnnotationFile* annotationFi return drawnFlag; } +/** + * When lines are very thin, they can be difficult to select. This method will + * return the line thickness percentage, adjusted so that is thick enough that + * the user will be able to select the annotation. + * + * @param + * Annotation drawn as line + * @return + * Percentage thickness for drawing that may be increased to ensure that + the annotation is selectable. + */ +float +BrainOpenGLAnnotationDrawingFixedPipeline::getLineWidthPercentageInSelectionMode(const Annotation* annotation) const +{ + CaretAssert(annotation); + const float minPercentHeight = GraphicsUtilitiesOpenGL::convertPixelsToPercentageOfViewportHeight(s_selectionLineMinimumPixelWidth); + float percentHeight = annotation->getLineWidthPercentage(); + if (percentHeight < minPercentHeight) { + percentHeight = minPercentHeight; + } + + return percentHeight; +} + + /** * Draw a sizing handle at the given coordinate. * diff --git a/src/Brain/BrainOpenGLAnnotationDrawingFixedPipeline.h b/src/Brain/BrainOpenGLAnnotationDrawingFixedPipeline.h index 921a0f9f4..c37739535 100644 --- a/src/Brain/BrainOpenGLAnnotationDrawingFixedPipeline.h +++ b/src/Brain/BrainOpenGLAnnotationDrawingFixedPipeline.h @@ -311,6 +311,8 @@ namespace caret { float getLineWidthFromPercentageHeight(const float percentageHeight) const; + float getLineWidthPercentageInSelectionMode(const Annotation* annotation) const; + float estimateColorBarWidth(const AnnotationColorBar* colorBar, const float textHeightInPixels) const; @@ -359,6 +361,7 @@ namespace caret { std::unique_ptr m_transformEvent; static constexpr float s_sizingHandleLineWidthInPixels = 2.0f; + static constexpr float s_selectionLineMinimumPixelWidth = 5.0f; // ADD_NEW_MEMBERS_HERE -- GitLab From ed57ca8e02b1928a194351a177e89c50fbed16b0 Mon Sep 17 00:00:00 2001 From: John Harwell Date: Wed, 29 Aug 2018 09:24:36 -0500 Subject: [PATCH 002/427] Implement WB-820 Improve performance when in Annotations Mode: Avoid a graphics update when in Annotations Mode as the cursor is moved. Needs to be tested on Linux and Windows. --- src/GuiQt/UserInputModeAnnotations.cxx | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/GuiQt/UserInputModeAnnotations.cxx b/src/GuiQt/UserInputModeAnnotations.cxx index eeded0423..768259f72 100644 --- a/src/GuiQt/UserInputModeAnnotations.cxx +++ b/src/GuiQt/UserInputModeAnnotations.cxx @@ -1007,11 +1007,17 @@ UserInputModeAnnotations::setAnnotationUnderMouse(const MouseEvent& mouseEvent, } openGLWidget->updateCursor(); + + /* + * Update graphics only if an annotation was passed to this method (WB-820) + */ + if (annotationIDIn != NULL) { #if BRAIN_OPENGL_INFO_SUPPORTS_DISPLAY_LISTS - openGLWidget->updateGL(); + openGLWidget->updateGL(); #else - openGLWidget->update(); + openGLWidget->update(); #endif + } } /** -- GitLab From 359e133fbe4810036ec16ed413ca1b44c28979a7 Mon Sep 17 00:00:00 2001 From: Tim Coalson Date: Thu, 30 Aug 2018 22:14:18 -0500 Subject: [PATCH 003/427] new -double-correlation option to -cifti-correlation-gradient seems to work, doesn't yet pay attention to memory limit --- .../AlgorithmCiftiCorrelationGradient.cxx | 684 +++++++++++------- .../AlgorithmCiftiCorrelationGradient.h | 42 +- 2 files changed, 456 insertions(+), 270 deletions(-) diff --git a/src/Algorithms/AlgorithmCiftiCorrelationGradient.cxx b/src/Algorithms/AlgorithmCiftiCorrelationGradient.cxx index ec0401d7f..4af9eea50 100644 --- a/src/Algorithms/AlgorithmCiftiCorrelationGradient.cxx +++ b/src/Algorithms/AlgorithmCiftiCorrelationGradient.cxx @@ -32,6 +32,8 @@ #include "Vector3D.h" #include "VolumeFile.h" #include "dot_wrapper.h" + +#include #include using namespace caret; @@ -90,6 +92,11 @@ OperationParameters* AlgorithmCiftiCorrelationGradient::getParameters() OptionalParameter* memLimitOpt = ret->createOptionalParameter(11, "-mem-limit", "restrict memory usage"); memLimitOpt->addDoubleParameter(1, "limit-GB", "memory limit in gigabytes"); + OptionalParameter* secondCorrOpt = ret->createOptionalParameter(14, "-double-correlation", "do two correlations before taking the gradient"); + secondCorrOpt->createOptionalParameter(1, "-fisher-z-first", "after the FIRST correlation, apply fisher small z transform (ie, artanh)"); + secondCorrOpt->createOptionalParameter(2, "-no-demean-first", "instead of correlation for the FIRST operation, do dot product of rows, then normalize by diagonal"); + secondCorrOpt->createOptionalParameter(3, "-covariance-first", "instead of correlation for the FIRST operation, compute covariance"); + ret->setHelpText( AString("For each structure, compute the correlation of the rows in the structure, and take the gradients of ") + "the resulting rows, then average them. " + @@ -179,8 +186,18 @@ void AlgorithmCiftiCorrelationGradient::useParameters(OperationParameters* myPar } } bool covariance = myParams->getOptionalParameter(13)->m_present; + bool doubleCorr = false, firstFisher = false, firstNoDemean = false, firstCovar = false; + OptionalParameter* doubleCorrOpt = myParams->getOptionalParameter(14); + if (doubleCorrOpt->m_present) + { + doubleCorr = true; + firstFisher = doubleCorrOpt->getOptionalParameter(1)->m_present; + firstNoDemean = doubleCorrOpt->getOptionalParameter(2)->m_present; + firstCovar = doubleCorrOpt->getOptionalParameter(3)->m_present; + } AlgorithmCiftiCorrelationGradient(myProgObj, myCifti, myCiftiOut, myLeftSurf, myRightSurf, myCerebSurf, myLeftAreas, myRightAreas, myCerebAreas, - surfKern, volKern, undoFisherInput, applyFisher, surfaceExclude, volumeExclude, covariance, memLimitGB); + surfKern, volKern, undoFisherInput, applyFisher, surfaceExclude, volumeExclude, covariance, memLimitGB, + doubleCorr, firstFisher, firstNoDemean, firstCovar); } AlgorithmCiftiCorrelationGradient::AlgorithmCiftiCorrelationGradient(ProgressObject* myProgObj, const CiftiFile* myCifti, CiftiFile* myCiftiOut, @@ -189,17 +206,19 @@ AlgorithmCiftiCorrelationGradient::AlgorithmCiftiCorrelationGradient(ProgressObj const float& surfKern, const float& volKern, const bool& undoFisherInput, const bool& applyFisher, const float& surfaceExclude, const float& volumeExclude, const bool& covariance, - const float& memLimitGB) : AbstractAlgorithm(myProgObj) + const float& memLimitGB, + const bool doubleCorr, const bool firstFisher, const bool firstNoDemean, const bool firstCovar) : AbstractAlgorithm(myProgObj) { LevelProgress myProgress(myProgObj); - init(myCifti, undoFisherInput, applyFisher, covariance); - const CiftiXMLOld& myXML = myCifti->getCiftiXMLOld(); - CiftiXMLOld myNewXML = myXML; - myNewXML.resetDirectionToScalars(CiftiXMLOld::ALONG_ROW, 1); - myNewXML.setMapNameForIndex(CiftiXMLOld::ALONG_ROW, 0, "gradient"); + init(myCifti, memLimitGB, undoFisherInput, applyFisher, covariance, doubleCorr, firstFisher, firstNoDemean, firstCovar); + const CiftiXML& myXML = myCifti->getCiftiXML(); + CiftiXML myNewXML = myXML; + CiftiScalarsMap newMap(1); + newMap.setMapName(0, "gradient"); + myNewXML.setMap(CiftiXML::ALONG_ROW, newMap); myCiftiOut->setCiftiXML(myNewXML); - vector surfaceList, volumeList; - myXML.getStructureListsForColumns(surfaceList, volumeList); + const CiftiBrainModelsMap& spaceMap = myXML.getBrainModelsMap(CiftiXML::ALONG_COLUMN); + vector surfaceList = spaceMap.getSurfaceStructureList(), volumeList = spaceMap.getVolumeStructureList(); for (int whichStruct = 0; whichStruct < (int)surfaceList.size(); ++whichStruct) {//sanity check surfaces SurfaceFile* mySurf = NULL; @@ -279,6 +298,107 @@ AlgorithmCiftiCorrelationGradient::AlgorithmCiftiCorrelationGradient(ProgressObj myCiftiOut->setColumn(m_outColumn.data(), 0); } +namespace +{ + + //expects rows to already be demeaned, if demeaning is to be done + float correlate(const float* row1, const float& rrs1, const float* row2, const float& rrs2, const int64_t length, const bool covariance, const bool fisherz) + { + double r; + if (row1 == row2 && !covariance) + { + r = 1.0;//short circuit for same row - works because one row is always in the cache range + } else { + double accum = dsdot(row1, row2, length);//these have already had the row means subtracted out + if (covariance) + { + r = accum / length; + } else { + r = accum / (rrs1 * rrs2); + } + } + if (!covariance) + { + if (fisherz) + { + if (r > 0.999999) r = 0.999999;//prevent inf + if (r < -0.999999) r = -0.999999;//prevent -inf + r = 0.5 * log((1 + r) / (1 - r)); + } else { + if (r > 1.0) r = 1.0;//don't output anything silly + if (r < -1.0) r = -1.0; + } + } + return r; + } + + void adjustRow(float* rowOut, int64_t length, AlgorithmCiftiCorrelationGradient::RowInfo& rowInfo, const bool undoFisher, const bool covariance, const bool noDemean) + { + if (undoFisher) + { + for (int64_t i = 0; i < length; ++i) + { + double temp = exp(2 * rowOut[i]); + rowOut[i] = (float)((temp - 1)/(temp + 1)); + } + } + if (!rowInfo.m_haveCalculated) + { + double accum = 0.0; + float mean = 0.0f; + if (!noDemean) + { + for (int64_t i = 0; i < length; ++i) + { + accum += rowOut[i]; + } + mean = accum / length; + } + rowInfo.m_mean = mean;//note: mean is intentionally 0 when noDemean is true + if (!covariance)//rrs not used in covariance + { + accum = 0.0; + for (int i = 0; i < length; ++i) + { + float tempf = rowOut[i] - mean; + accum += tempf * tempf; + if (!noDemean)//do one less pass over the data the first time the mean is computed + { + rowOut[i] = tempf; + } + } + rowInfo.m_rootResidSqr = sqrt(accum); + } else { + rowInfo.m_rootResidSqr = 0.0f; + } + rowInfo.m_haveCalculated = true; + } else { + if (!noDemean) + { + float mean = rowInfo.m_mean; + for (int64_t i = 0; i < length; ++i) + { + rowOut[i] -= mean; + } + } + } + } + + struct FirstCorrPlan + { + bool m_cacheFullInput; + int64_t m_chunkSize; + }; + + FirstCorrPlan firstCorrMemoryPlan(int64_t totalRows) + { + FirstCorrPlan ret;//FIXME: actually do something with the memory limit + ret.m_cacheFullInput = true; + ret.m_chunkSize = totalRows; + return ret; + } +} + void AlgorithmCiftiCorrelationGradient::processSurfaceComponent(StructureEnum::Enum& myStructure, const float& surfKern, const float& memLimitGB, SurfaceFile* mySurf, const MetricFile* myAreas) { const CiftiXMLOld& myXML = m_inputCifti->getCiftiXMLOld(); @@ -287,10 +407,10 @@ void AlgorithmCiftiCorrelationGradient::processSurfaceComponent(StructureEnum::E int mapSize = (int)myMap.size(); vector accum(mapSize, 0.0); int numCacheRows = mapSize; - bool cacheFullInput = true; + bool cacheFullInput = true;//numRowsForMem() sets this if (memLimitGB >= 0.0f) { - numCacheRows = numRowsForMem(memLimitGB, m_numCols * sizeof(float), (mySurf->getNumberOfNodes() * (sizeof(float) * 8 + 1)) / 8, mapSize, cacheFullInput); + numCacheRows = numRowsForMem(m_numCols * sizeof(float), (mySurf->getNumberOfNodes() * (sizeof(float) * 8 + 1)) / 8, mapSize, cacheFullInput); } if (numCacheRows > mapSize) { @@ -311,7 +431,7 @@ void AlgorithmCiftiCorrelationGradient::processSurfaceComponent(StructureEnum::E MetricFile myRoi; myRoi.setNumberOfNodesAndColumns(mySurf->getNumberOfNodes(), 1); myRoi.initializeColumn(0); - vector rowsToCache; + vector rowsToCache; for (int i = 0; i < mapSize; ++i) { myRoi.setValue(myMap[i].m_surfaceNode, 0, 1.0f); @@ -345,35 +465,46 @@ void AlgorithmCiftiCorrelationGradient::processSurfaceComponent(StructureEnum::E int curRow = 0;//because we can't trust the order threads hit the critical section MetricFile computeMetric; computeMetric.setNumberOfNodesAndColumns(mySurf->getNumberOfNodes(), endpos - startpos); -#pragma omp CARET_PARFOR schedule(dynamic) - for (int i = 0; i < mapSize; ++i) +#pragma omp CARET_PAR { - float movingRrs; - const float* movingRow; - int myrow; -#pragma omp critical - {//CiftiFile may explode if we request multiple rows concurrently (needs mutexes), but we should force sequential requests anyway - myrow = curRow;//so, manually force it to read sequentially - ++curRow; - movingRow = getRow(myMap[myrow].m_ciftiIndex, movingRrs); - } - for (int j = startpos; j < endpos; ++j) + vector scratchRow1(m_numCols), scratchRow2(m_numCols); +#pragma omp CARET_FOR schedule(dynamic) + for (int i = 0; i < mapSize; ++i) { - if (myrow >= startpos && myrow < endpos) + float movingRrs; + const float* movingRow = NULL; + int myrow; +#pragma omp critical + {//CiftiFile may explode if we request multiple rows concurrently (needs mutexes), but we should force sequential requests anyway + myrow = curRow;//so, manually force it to read sequentially + ++curRow; + if (!m_doubleCorr) + {//when not doing double corr, we want to read single rows in order on disk + movingRow = getRow(myMap[myrow].m_ciftiIndex, movingRrs, scratchRow1.data()); + } + } + if (m_doubleCorr) + {//when doing double corr, let the threads compute correlations in parallel + movingRow = getRow(myMap[myrow].m_ciftiIndex, movingRrs, scratchRow1.data()); + } + for (int j = startpos; j < endpos; ++j) { - if (j >= myrow) + if (myrow >= startpos && myrow < endpos) { + if (j >= myrow) + { + float cacheRrs; + const float* cacheRow = getRow(myMap[j].m_ciftiIndex, cacheRrs, scratchRow2.data()); + float result = correlate(movingRow, movingRrs, cacheRow, cacheRrs, m_numCols, m_covariance, m_applyFisher); + computeMetric.setValue(myMap[myrow].m_surfaceNode, j - startpos, result); + computeMetric.setValue(myMap[j].m_surfaceNode, myrow - startpos, result); + } + } else { float cacheRrs; - const float* cacheRow = getRow(myMap[j].m_ciftiIndex, cacheRrs, true); - float result = correlate(movingRow, movingRrs, cacheRow, cacheRrs); + const float* cacheRow = getRow(myMap[j].m_ciftiIndex, cacheRrs, scratchRow2.data()); + float result = correlate(movingRow, movingRrs, cacheRow, cacheRrs, m_numCols, m_covariance, m_applyFisher); computeMetric.setValue(myMap[myrow].m_surfaceNode, j - startpos, result); - computeMetric.setValue(myMap[j].m_surfaceNode, myrow - startpos, result); } - } else { - float cacheRrs; - const float* cacheRow = getRow(myMap[j].m_ciftiIndex, cacheRrs, true); - float result = correlate(movingRow, movingRrs, cacheRow, cacheRrs); - computeMetric.setValue(myMap[myrow].m_surfaceNode, j - startpos, result); } } } @@ -419,7 +550,7 @@ void AlgorithmCiftiCorrelationGradient::processSurfaceComponent(StructureEnum::E bool cacheFullInput = true; if (memLimitGB >= 0.0f) { - numCacheRows = numRowsForMem(memLimitGB, m_numCols * sizeof(float), (mySurf->getNumberOfNodes() * (sizeof(float) * 8 + 1)) / 8, mapSize, cacheFullInput); + numCacheRows = numRowsForMem(m_numCols * sizeof(float), (mySurf->getNumberOfNodes() * (sizeof(float) * 8 + 1)) / 8, mapSize, cacheFullInput); } if (numCacheRows > mapSize) { @@ -444,7 +575,7 @@ void AlgorithmCiftiCorrelationGradient::processSurfaceComponent(StructureEnum::E vector > roiLookup(numCacheRows);//this gets bit compressed vector origRoi(mySurf->getNumberOfNodes()); vector > excludeNodes(numCacheRows); - vector rowsToCache; + vector rowsToCache; for (int i = 0; i < mapSize; ++i) { myRoi.setValue(myMap[i].m_surfaceNode, 0, 1.0f); @@ -501,37 +632,48 @@ void AlgorithmCiftiCorrelationGradient::processSurfaceComponent(StructureEnum::E int curRow = 0;//because we can't trust the order threads hit the critical section MetricFile computeMetric; computeMetric.setNumberOfNodesAndColumns(mySurf->getNumberOfNodes(), endpos - startpos); -#pragma omp CARET_PARFOR schedule(dynamic) - for (int i = 0; i < mapSize; ++i) +#pragma omp CARET_PAR { - float movingRrs; - const float* movingRow; - int myrow; -#pragma omp critical - {//CiftiFile may explode if we request multiple rows concurrently (needs mutexes), but we should force sequential requests anyway - myrow = curRow;//so, manually force it to read sequentially - ++curRow; - movingRow = getRow(myMap[myrow].m_ciftiIndex, movingRrs); - } - for (int j = startpos; j < endpos; ++j) + vector scratchRow1(m_numCols), scratchRow2(m_numCols); +#pragma omp CARET_FOR schedule(dynamic) + for (int i = 0; i < mapSize; ++i) { - if (roiLookup[j - startpos][myMap[myrow].m_surfaceNode]) + float movingRrs; + const float* movingRow = NULL; + int myrow; +#pragma omp critical + {//CiftiFile may explode if we request multiple rows concurrently (needs mutexes), but we should force sequential requests anyway + myrow = curRow;//so, manually force it to read sequentially + ++curRow; + if (!m_doubleCorr) + {//when not doing double corr, we want to read single rows in order on disk + movingRow = getRow(myMap[myrow].m_ciftiIndex, movingRrs, scratchRow1.data()); + } + } + if (m_doubleCorr) + {//when doing double corr, let the threads compute correlations in parallel + movingRow = getRow(myMap[myrow].m_ciftiIndex, movingRrs, scratchRow1.data()); + } + for (int j = startpos; j < endpos; ++j) { - if (myrow >= startpos && myrow < endpos) + if (roiLookup[j - startpos][myMap[myrow].m_surfaceNode]) { - if (j >= myrow) + if (myrow >= startpos && myrow < endpos) { + if (j >= myrow) + { + float cacheRrs; + const float* cacheRow = getRow(myMap[j].m_ciftiIndex, cacheRrs, scratchRow2.data()); + float result = correlate(movingRow, movingRrs, cacheRow, cacheRrs, m_numCols, m_covariance, m_applyFisher); + computeMetric.setValue(myMap[myrow].m_surfaceNode, j - startpos, result); + computeMetric.setValue(myMap[j].m_surfaceNode, myrow - startpos, result); + } + } else { float cacheRrs; - const float* cacheRow = getRow(myMap[j].m_ciftiIndex, cacheRrs, true); - float result = correlate(movingRow, movingRrs, cacheRow, cacheRrs); + const float* cacheRow = getRow(myMap[j].m_ciftiIndex, cacheRrs, scratchRow2.data()); + float result = correlate(movingRow, movingRrs, cacheRow, cacheRrs, m_numCols, m_covariance, m_applyFisher); computeMetric.setValue(myMap[myrow].m_surfaceNode, j - startpos, result); - computeMetric.setValue(myMap[j].m_surfaceNode, myrow - startpos, result); } - } else { - float cacheRrs; - const float* cacheRow = getRow(myMap[j].m_ciftiIndex, cacheRrs, true); - float result = correlate(movingRow, movingRrs, cacheRow, cacheRrs); - computeMetric.setValue(myMap[myrow].m_surfaceNode, j - startpos, result); } } } @@ -622,7 +764,7 @@ void AlgorithmCiftiCorrelationGradient::processVolumeComponent(StructureEnum::En } if (memLimitGB >= 0.0f) { - numCacheRows = numRowsForMem(memLimitGB, m_numCols * sizeof(float), newdims[0] * newdims[1] * newdims[2] * sizeof(float), mapSize, cacheFullInput); + numCacheRows = numRowsForMem(m_numCols * sizeof(float), newdims[0] * newdims[1] * newdims[2] * sizeof(float), mapSize, cacheFullInput); } if (numCacheRows > mapSize) { @@ -640,7 +782,7 @@ void AlgorithmCiftiCorrelationGradient::processVolumeComponent(StructureEnum::En myXML.getVolumeDimsAndSForm(ciftiDims, ciftiSform); VolumeFile volRoi(newdims, ciftiSform); volRoi.setValueAllVoxels(0.0f); - vector rowsToCache; + vector rowsToCache; for (int i = 0; i < mapSize; ++i) { volRoi.setValue(1.0f, myMap[i].m_ijk[0] - offset[0], myMap[i].m_ijk[1] - offset[1], myMap[i].m_ijk[2] - offset[2]); @@ -670,35 +812,46 @@ void AlgorithmCiftiCorrelationGradient::processVolumeComponent(StructureEnum::En vector computeDims = newdims; computeDims.push_back(endpos - startpos); VolumeFile computeVol(computeDims, ciftiSform); -#pragma omp CARET_PARFOR schedule(dynamic) - for (int i = 0; i < mapSize; ++i) +#pragma omp CARET_PAR { - float movingRrs; - const float* movingRow; - int myrow; -#pragma omp critical - {//CiftiFile may explode if we request multiple rows concurrently (needs mutexes), but we should force sequential requests anyway - myrow = curRow;//so, manually force it to read sequentially - ++curRow; - movingRow = getRow(myMap[myrow].m_ciftiIndex, movingRrs); - } - for (int j = startpos; j < endpos; ++j) + vector scratchRow1(m_numCols), scratchRow2(m_numCols); +#pragma omp CARET_FOR schedule(dynamic) + for (int i = 0; i < mapSize; ++i) { - if (myrow >= startpos && myrow < endpos) + float movingRrs; + const float* movingRow = NULL; + int myrow; +#pragma omp critical + {//CiftiFile may explode if we request multiple rows concurrently (needs mutexes), but we should force sequential requests anyway + myrow = curRow;//so, manually force it to read sequentially + ++curRow; + if (!m_doubleCorr) + {//when not doing double corr, we want to read single rows in order on disk + movingRow = getRow(myMap[myrow].m_ciftiIndex, movingRrs, scratchRow1.data()); + } + } + if (m_doubleCorr) + {//when doing double corr, let the threads compute correlations in parallel + movingRow = getRow(myMap[myrow].m_ciftiIndex, movingRrs, scratchRow1.data()); + } + for (int j = startpos; j < endpos; ++j) { - if (j >= myrow) + if (myrow >= startpos && myrow < endpos) { + if (j >= myrow) + { + float cacheRrs; + const float* cacheRow = getRow(myMap[j].m_ciftiIndex, cacheRrs, scratchRow2.data()); + float result = correlate(movingRow, movingRrs, cacheRow, cacheRrs, m_numCols, m_covariance, m_applyFisher); + computeVol.setValue(result, myMap[myrow].m_ijk[0] - offset[0], myMap[myrow].m_ijk[1] - offset[1], myMap[myrow].m_ijk[2] - offset[2], j - startpos); + computeVol.setValue(result, myMap[j].m_ijk[0] - offset[0], myMap[j].m_ijk[1] - offset[1], myMap[j].m_ijk[2] - offset[2], myrow - startpos); + } + } else { float cacheRrs; - const float* cacheRow = getRow(myMap[j].m_ciftiIndex, cacheRrs, true); - float result = correlate(movingRow, movingRrs, cacheRow, cacheRrs); + const float* cacheRow = getRow(myMap[j].m_ciftiIndex, cacheRrs, scratchRow2.data()); + float result = correlate(movingRow, movingRrs, cacheRow, cacheRrs, m_numCols, m_covariance, m_applyFisher); computeVol.setValue(result, myMap[myrow].m_ijk[0] - offset[0], myMap[myrow].m_ijk[1] - offset[1], myMap[myrow].m_ijk[2] - offset[2], j - startpos); - computeVol.setValue(result, myMap[j].m_ijk[0] - offset[0], myMap[j].m_ijk[1] - offset[1], myMap[j].m_ijk[2] - offset[2], myrow - startpos); } - } else { - float cacheRrs; - const float* cacheRow = getRow(myMap[j].m_ciftiIndex, cacheRrs, true); - float result = correlate(movingRow, movingRrs, cacheRow, cacheRrs); - computeVol.setValue(result, myMap[myrow].m_ijk[0] - offset[0], myMap[myrow].m_ijk[1] - offset[1], myMap[myrow].m_ijk[2] - offset[2], j - startpos); } } } @@ -760,7 +913,7 @@ void AlgorithmCiftiCorrelationGradient::processVolumeComponent(StructureEnum::En } if (memLimitGB >= 0.0f) { - numCacheRows = numRowsForMem(memLimitGB, m_numCols * sizeof(float), newdims[0] * newdims[1] * newdims[2] * sizeof(float), mapSize, cacheFullInput); + numCacheRows = numRowsForMem(m_numCols * sizeof(float), newdims[0] * newdims[1] * newdims[2] * sizeof(float), mapSize, cacheFullInput); } if (numCacheRows > mapSize) { @@ -778,7 +931,7 @@ void AlgorithmCiftiCorrelationGradient::processVolumeComponent(StructureEnum::En myXML.getVolumeDimsAndSForm(ciftiDims, ciftiSform); VolumeFile volRoi(newdims, ciftiSform); volRoi.setValueAllVoxels(0.0f); - vector rowsToCache; + vector rowsToCache; for (int i = 0; i < mapSize; ++i) { volRoi.setValue(1.0f, myMap[i].m_ijk[0] - offset[0], myMap[i].m_ijk[1] - offset[1], myMap[i].m_ijk[2] - offset[2]); @@ -808,41 +961,52 @@ void AlgorithmCiftiCorrelationGradient::processVolumeComponent(StructureEnum::En vector computeDims = newdims; computeDims.push_back(endpos - startpos); VolumeFile computeVol(computeDims, ciftiSform); -#pragma omp CARET_PARFOR schedule(dynamic) - for (int i = 0; i < mapSize; ++i) +#pragma omp CARET_PAR { - float movingRrs; - const float* movingRow; - int myrow; -#pragma omp critical - {//CiftiFile may explode if we request multiple rows concurrently (needs mutexes), but we should force sequential requests anyway - myrow = curRow;//so, manually force it to read sequentially - ++curRow; - movingRow = getRow(myMap[myrow].m_ciftiIndex, movingRrs); - } - Vector3D movingLoc; - volRoi.indexToSpace(myMap[myrow].m_ijk, movingLoc);//NOTE: this is outside the cropped volume, but matches the real location in the full volume, because we didn't fix the center - for (int j = startpos; j < endpos; ++j) + vector scratchRow1(m_numCols), scratchRow2(m_numCols); +#pragma omp CARET_FOR schedule(dynamic) + for (int i = 0; i < mapSize; ++i) { - Vector3D seedLoc; - volRoi.indexToSpace(myMap[j].m_ijk, seedLoc);//ditto - if ((movingLoc - seedLoc).length() > volExclude)//don't correlate if closer than the exclude range + float movingRrs; + const float* movingRow = NULL; + int myrow; +#pragma omp critical + {//CiftiFile does use mutexes now (in NiftiIO), but sequential IO is better + myrow = curRow;//so, manually force it to read sequentially + ++curRow; + if (!m_doubleCorr) + {//when not doing double corr, we want to read single rows in order on disk + movingRow = getRow(myMap[myrow].m_ciftiIndex, movingRrs, scratchRow1.data()); + } + } + if (m_doubleCorr) + {//when doing double corr, let the threads compute correlations in parallel + movingRow = getRow(myMap[myrow].m_ciftiIndex, movingRrs, scratchRow1.data()); + } + Vector3D movingLoc; + volRoi.indexToSpace(myMap[myrow].m_ijk, movingLoc);//NOTE: this is outside the cropped volume, but matches the real location in the full volume, because we didn't fix the center + for (int j = startpos; j < endpos; ++j) { - if (myrow >= startpos && myrow < endpos) + Vector3D seedLoc; + volRoi.indexToSpace(myMap[j].m_ijk, seedLoc);//ditto + if ((movingLoc - seedLoc).length() > volExclude)//don't correlate if closer than the exclude range { - if (j >= myrow) + if (myrow >= startpos && myrow < endpos) { + if (j >= myrow) + { + float cacheRrs; + const float* cacheRow = getRow(myMap[j].m_ciftiIndex, cacheRrs, scratchRow2.data()); + float result = correlate(movingRow, movingRrs, cacheRow, cacheRrs, m_numCols, m_covariance, m_applyFisher); + computeVol.setValue(result, myMap[myrow].m_ijk[0] - offset[0], myMap[myrow].m_ijk[1] - offset[1], myMap[myrow].m_ijk[2] - offset[2], j - startpos); + computeVol.setValue(result, myMap[j].m_ijk[0] - offset[0], myMap[j].m_ijk[1] - offset[1], myMap[j].m_ijk[2] - offset[2], myrow - startpos); + } + } else { float cacheRrs; - const float* cacheRow = getRow(myMap[j].m_ciftiIndex, cacheRrs, true); - float result = correlate(movingRow, movingRrs, cacheRow, cacheRrs); + const float* cacheRow = getRow(myMap[j].m_ciftiIndex, cacheRrs, scratchRow2.data()); + float result = correlate(movingRow, movingRrs, cacheRow, cacheRrs, m_numCols, m_covariance, m_applyFisher); computeVol.setValue(result, myMap[myrow].m_ijk[0] - offset[0], myMap[myrow].m_ijk[1] - offset[1], myMap[myrow].m_ijk[2] - offset[2], j - startpos); - computeVol.setValue(result, myMap[j].m_ijk[0] - offset[0], myMap[j].m_ijk[1] - offset[1], myMap[j].m_ijk[2] - offset[2], myrow - startpos); } - } else { - float cacheRrs; - const float* cacheRow = getRow(myMap[j].m_ciftiIndex, cacheRrs, true); - float result = correlate(movingRow, movingRrs, cacheRow, cacheRrs); - computeVol.setValue(result, myMap[myrow].m_ijk[0] - offset[0], myMap[myrow].m_ijk[1] - offset[1], myMap[myrow].m_ijk[2] - offset[2], j - startpos); } } } @@ -888,38 +1052,8 @@ void AlgorithmCiftiCorrelationGradient::processVolumeComponent(StructureEnum::En } } -float AlgorithmCiftiCorrelationGradient::correlate(const float* row1, const float& rrs1, const float* row2, const float& rrs2) -{ - double r; - if (row1 == row2 && !m_covariance) - { - r = 1.0;//short circuit for same row - } else { - double accum = dsdot(row1, row2, m_numCols);//these have already had the row means subtracted out - if (m_covariance) - { - r = accum / m_numCols; - } else { - r = accum / (rrs1 * rrs2); - } - } - if (!m_covariance) - { - if (m_applyFisher) - { - if (r > 0.999999) r = 0.999999;//prevent inf - if (r < -0.999999) r = -0.999999;//prevent -inf - r = 0.5 * log((1 + r) / (1 - r)); - } else { - if (r > 1.0) r = 1.0;//don't output anything silly - if (r < -1.0) r = -1.0; - } - } - return r; -} - -void AlgorithmCiftiCorrelationGradient::init(const CiftiFile* input, const bool& undoFisherInput, const bool& applyFisher, - const bool& covariance) +void AlgorithmCiftiCorrelationGradient::init(const CiftiFile* input, const float& memLimitGB, const bool& undoFisherInput, const bool& applyFisher, + const bool& covariance, const bool doubleCorr, const bool firstFisher, const bool firstNoDemean, const bool firstCovar) { if (input->getCiftiXML().getMappingType(CiftiXML::ALONG_COLUMN) != CiftiMappingType::BRAIN_MODELS) throw AlgorithmException("input cifti file must have brain models mapping along column"); if (covariance) @@ -930,62 +1064,152 @@ void AlgorithmCiftiCorrelationGradient::init(const CiftiFile* input, const bool& m_applyFisher = applyFisher; m_covariance = covariance; m_inputCifti = input; - m_rowInfo.resize(m_inputCifti->getNumberOfRows()); - m_cacheUsed = 0; - m_numCols = m_inputCifti->getNumberOfColumns(); - m_outColumn.resize(m_inputCifti->getNumberOfRows()); + int64_t colLength = m_inputCifti->getNumberOfRows();//this is correct even for double correlation + m_doubleCorr = doubleCorr; + if (doubleCorr) + { + if (firstCovar && firstFisher) throw AlgorithmException("cannot apply fisher z transformation to first covariance"); + m_numCols = colLength;//virtual intermediate file is square and symmetric, because there is no -roi-override option + m_rowLengthFirst = m_inputCifti->getNumberOfColumns(); + m_firstFisher = firstFisher; + m_firstNoDemean = firstNoDemean; + m_firstCovar = firstCovar; + m_firstCorrInfo.resize(colLength); + //TODO: deal with doubleCorr in getRow/#cacheRows + } else { + m_numCols = m_inputCifti->getNumberOfColumns(); + m_rowLengthFirst = 0;//trick to get the memory computations to work out + } + m_memLimitGB = memLimitGB; + m_rowInfo.resize(colLength); + m_outColumn.resize(colLength); } -void AlgorithmCiftiCorrelationGradient::cacheRows(const vector& ciftiIndices) +void AlgorithmCiftiCorrelationGradient::cacheRows(const vector& ciftiIndices) { clearCache();//clear first, to be sure we never keep a cache around too long - int curIndex = 0, numIndices = (int)ciftiIndices.size();//manually in-order - m_rowCache.reserve(m_cacheUsed + numIndices);//so that pointers to members don't change -#pragma omp CARET_PAR + int64_t numIndices = (int64_t)ciftiIndices.size(); + m_rowCache.resize(numIndices, CacheRow(m_numCols)); + if (m_doubleCorr) { - int myIndex; - float* myPtr; -#pragma omp CARET_FOR schedule(dynamic) - for (int i = 0; i < numIndices; ++i) + for (int64_t i = 0; i < numIndices; ++i)//prepopulate intermediate output lookups, they will be useful to reuse symmetric correlations { - myPtr = NULL; -#pragma omp critical + m_rowCache[i].m_ciftiIndex = ciftiIndices[i]; + m_rowInfo[ciftiIndices[i]].m_cacheIndex = i; + } + vector > preparedInput; + FirstCorrPlan plan = firstCorrMemoryPlan(numIndices); + if (plan.m_cacheFullInput) + { + preparedInput.resize(m_numCols, vector(m_rowLengthFirst)); + for (int64_t i = 0; i < m_numCols; ++i) { - myIndex = curIndex; - ++curIndex; - CaretAssertVectorIndex(m_rowInfo, ciftiIndices[myIndex]); - if (m_rowInfo[ciftiIndices[myIndex]].m_cacheIndex == -1) + m_inputCifti->getRow(preparedInput[i].data(), i); + adjustRow(preparedInput[i].data(), m_rowLengthFirst, m_firstCorrInfo[i], false, m_firstCovar, m_firstNoDemean);//also calculates rrs, needed for -no-demean-first in correlation mode + m_firstCorrInfo[i].m_cacheIndex = i; + } + } else { + preparedInput.resize(plan.m_chunkSize, vector(m_rowLengthFirst)); + } + vector scratchRow(m_rowLengthFirst); + for (int64_t chunkStart = 0; chunkStart < m_numCols; chunkStart += plan.m_chunkSize)//cache chunks are along the complete dimension + { + int64_t chunkEnd = min(m_numCols, chunkStart + plan.m_chunkSize); + if (!(plan.m_cacheFullInput)) + { + for (int64_t i = chunkStart; i < chunkEnd; ++i) { - if (m_cacheUsed >= (int)m_rowCache.size()) + m_inputCifti->getRow(preparedInput[i - chunkStart].data(), i); + adjustRow(preparedInput[i - chunkStart].data(), m_rowLengthFirst, m_firstCorrInfo[i], false, m_firstCovar, m_firstNoDemean); + m_firstCorrInfo[i].m_cacheIndex = i - chunkStart; + } + } + int64_t curIndex = 0;//force manual in-order +#pragma omp CARET_PARFOR schedule(dynamic) + for (int64_t i = 0; i < numIndices; ++i)//myIndex loops through the ciftiIndices array + { + float* movingData = NULL; + int64_t movingRow = -1, myIndex = -1; + bool doAdjust = false; +#pragma omp critical + { + myIndex = curIndex; + ++curIndex; + movingRow = ciftiIndices[myIndex]; + if (m_firstCorrInfo[movingRow].m_cacheIndex != -1) { - m_rowCache.push_back(CacheRow()); - m_rowCache[m_cacheUsed].m_row.resize(m_numCols); + movingData = preparedInput[m_firstCorrInfo[movingRow].m_cacheIndex].data(); + } else { + doAdjust = true; + movingData = scratchRow.data(); + m_inputCifti->getRow(movingData, movingRow); } - m_rowCache[m_cacheUsed].m_ciftiIndex = ciftiIndices[myIndex]; - myPtr = m_rowCache[m_cacheUsed].m_row.data(); - m_inputCifti->getRow(myPtr, ciftiIndices[myIndex]); - m_rowInfo[ciftiIndices[myIndex]].m_cacheIndex = m_cacheUsed; - ++m_cacheUsed; } - }//end critical, now compute while the next thread reads - if (myPtr != NULL) + if (doAdjust) + { + adjustRow(movingData, m_rowLengthFirst, m_firstCorrInfo[movingRow], false, m_firstCovar, m_firstNoDemean); + } + float movingRrs = m_firstCorrInfo[movingRow].m_rootResidSqr;//do not move this up, for some rows it is not computed until adjustRow + for (int64_t j = chunkStart; j < chunkEnd; ++j)//j loops through the complete dimension, in chunks + { + float* cacheData = preparedInput[j - chunkStart].data(); + float cacheRrs = m_firstCorrInfo[j].m_rootResidSqr; + if (m_rowInfo[j].m_cacheIndex == -1 || ciftiIndices[m_rowInfo[j].m_cacheIndex] >= movingRow)//if a symmetric output element exists in the rows to cache, don't do the correlation of the lower one + { + float corrval = correlate(movingData, movingRrs, cacheData, cacheRrs, m_rowLengthFirst, m_firstCovar, m_firstFisher); + m_rowCache[myIndex].m_row[j] = corrval; + if (m_rowInfo[j].m_cacheIndex != -1)//fill the symmetric part if it exists + { + m_rowCache[m_rowInfo[j].m_cacheIndex].m_row[movingRow] = corrval; + } + } + } + } + if (!(plan.m_cacheFullInput)) { - adjustRow(myPtr, ciftiIndices[myIndex]); + for (int64_t i = chunkStart; i < chunkEnd; ++i) + { + m_firstCorrInfo[i].m_cacheIndex = -1; + } } } +#pragma omp CARET_PARFOR schedule(dynamic) + for (int64_t i = 0; i < numIndices; ++i) + { + adjustRow(m_rowCache[i].m_row.data(), m_numCols, m_rowInfo[ciftiIndices[i]], m_undoFisherInput, m_covariance, false); + } + } else { + int64_t curIndex = 0; +#pragma omp CARET_PARFOR schedule(dynamic) + for (int64_t i = 0; i < numIndices; ++i) + { + float* myPtr = NULL; + int64_t myIndex = -1; +#pragma omp critical + {//manually in-order reading + myIndex = curIndex; + ++curIndex; + CaretAssertVectorIndex(m_rowInfo, ciftiIndices[myIndex]); + myPtr = m_rowCache[myIndex].m_row.data(); + m_inputCifti->getRow(myPtr, ciftiIndices[myIndex]); + m_rowCache[myIndex].m_ciftiIndex = ciftiIndices[myIndex]; + m_rowInfo[ciftiIndices[myIndex]].m_cacheIndex = myIndex; + }//end critical, now demean while the next thread reads + adjustRow(myPtr, m_numCols, m_rowInfo[ciftiIndices[myIndex]], m_undoFisherInput, m_covariance, false); + } } } void AlgorithmCiftiCorrelationGradient::clearCache() { - for (int i = 0; i < m_cacheUsed; ++i) + for (int64_t i = 0; i < int64_t(m_rowCache.size()); ++i) { m_rowInfo[m_rowCache[i].m_ciftiIndex].m_cacheIndex = -1; } - m_cacheUsed = 0; + m_rowCache.clear(); } -const float* AlgorithmCiftiCorrelationGradient::getRow(const int& ciftiIndex, float& rootResidSqr, const bool& mustBeCached) +const float* AlgorithmCiftiCorrelationGradient::getRow(const int& ciftiIndex, float& rootResidSqr, float* scratchStorage) { float* ret; CaretAssertVectorIndex(m_rowInfo, ciftiIndex); @@ -993,91 +1217,47 @@ const float* AlgorithmCiftiCorrelationGradient::getRow(const int& ciftiIndex, fl { ret = m_rowCache[m_rowInfo[ciftiIndex].m_cacheIndex].m_row.data(); } else { - CaretAssert(!mustBeCached); - if (mustBeCached)//largely so it doesn't give warning about unused when compiled in release - { - throw AlgorithmException("something very bad happened, notify the developers"); - } - ret = getTempRow(); - m_inputCifti->getRow(ret, ciftiIndex); - adjustRow(ret, ciftiIndex); - } - rootResidSqr = m_rowInfo[ciftiIndex].m_rootResidSqr; - return ret; -} - -void AlgorithmCiftiCorrelationGradient::adjustRow(float* rowOut, const int& ciftiIndex) -{ - if (m_undoFisherInput) - { - for (int i = 0; i < m_numCols; ++i) - { - double temp = exp(2 * rowOut[i]); - rowOut[i] = (float)((temp - 1)/(temp + 1)); - } - } - if (!m_rowInfo[ciftiIndex].m_haveCalculated) - { - double accum = 0.0;//double, for numerical stability - for (int i = 0; i < m_numCols; ++i)//two pass, for numerical stability - { - accum += rowOut[i]; - } - float mean = accum / m_numCols; - float rootResidSqr = 0.0f;//not used in covariance - if (!m_covariance) - { - accum = 0.0; - for (int i = 0; i < m_numCols; ++i) + ret = scratchStorage; + if (m_doubleCorr) + {//will only happen with a memory limit that prevents caching entire row range - need to take a look at this when memory limit is extended to this case + vector fixedRow(m_rowLengthFirst), movingRow(m_rowLengthFirst); + m_inputCifti->getRow(fixedRow.data(), ciftiIndex); + adjustRow(fixedRow.data(), m_rowLengthFirst, m_firstCorrInfo[ciftiIndex], false, m_firstCovar, m_firstNoDemean); + for (int64_t i = 0; i < m_numCols; ++i)//TODO: convert input to in-memory when possible { - float tempf = rowOut[i] - mean; - accum += tempf * tempf; + float* movingData = movingRow.data(); + if (i == ciftiIndex) + {//correlate has logic to give the right answer without doing the corr when the pointers are the same + movingData = fixedRow.data(); + } else { + m_inputCifti->getRow(movingRow.data(), i); + adjustRow(movingRow.data(), m_rowLengthFirst, m_firstCorrInfo[i], false, m_firstCovar, m_firstNoDemean); + } + ret[i] = correlate(fixedRow.data(), m_firstCorrInfo[ciftiIndex].m_rootResidSqr, movingData, m_firstCorrInfo[i].m_rootResidSqr, m_rowLengthFirst, m_firstCovar, m_firstFisher); } - rootResidSqr = sqrt(accum); + } else { + m_inputCifti->getRow(ret, ciftiIndex); } - m_rowInfo[ciftiIndex].m_mean = mean; - m_rowInfo[ciftiIndex].m_rootResidSqr = rootResidSqr; - m_rowInfo[ciftiIndex].m_haveCalculated = true; - } - float mean = m_rowInfo[ciftiIndex].m_mean; - for (int i = 0; i < m_numCols; ++i) - { - rowOut[i] -= mean; + adjustRow(ret, m_numCols, m_rowInfo[ciftiIndex], m_undoFisherInput, m_covariance, false); } + rootResidSqr = m_rowInfo[ciftiIndex].m_rootResidSqr; + return ret; } -float* AlgorithmCiftiCorrelationGradient::getTempRow() +int AlgorithmCiftiCorrelationGradient::numRowsForMem(const int64_t& inrowBytes, const int64_t& outrowBytes, const int& numRows, bool& cacheFullInputOut) { -#ifdef CARET_OMP - int oldsize = (int)m_tempRows.size(); - int threadNum = omp_get_thread_num(); - if (threadNum >= oldsize) + if (m_memLimitGB < 0.0f) { - m_tempRows.resize(threadNum + 1); - for (int i = oldsize; i <= threadNum; ++i) - { - m_tempRows[i] = CaretArray(m_numCols); - } + cacheFullInputOut = true; + return numRows; } - return m_tempRows[threadNum].getArray(); -#else - if (m_tempRows.size() == 0) - { - m_tempRows.resize(1); - m_tempRows[0] = CaretArray(m_numCols); - } - return m_tempRows[0].getArray(); -#endif -} - -int AlgorithmCiftiCorrelationGradient::numRowsForMem(const float& memLimitGB, const int64_t& inrowBytes, const int64_t& outrowBytes, const int& numRows, bool& cacheFullInput) -{ - int64_t targetBytes = (int64_t)(memLimitGB * 1024 * 1024 * 1024); - if (m_inputCifti->isInMemory()) targetBytes -= numRows * inrowBytes;//count in-memory input against the total too + int64_t targetBytes = (int64_t)(m_memLimitGB * 1024 * 1024 * 1024); + bool inputIsMemory = m_inputCifti->isInMemory();//FIXME: double corr + if (inputIsMemory) targetBytes -= numRows * inrowBytes;//count in-memory input against the total too - TODO: if in memory, don't cache input rows at all? targetBytes -= numRows * sizeof(RowInfo) + 2 * outrowBytes;//storage for mean, stdev, and info about caching, output structures if (targetBytes < 1) { - cacheFullInput = false;//the most memory conservation possible, though it will take a LOT of time and do a LOT of IO + cacheFullInputOut = false;//the most memory conservation possible, though it will take a LOT of time and do a LOT of IO return 1; } if (inrowBytes * numRows < targetBytes)//if we can cache the full input, compute the number of passes needed and compare @@ -1104,17 +1284,17 @@ int AlgorithmCiftiCorrelationGradient::numRowsForMem(const float& memLimitGB, co int ret; if (partialCorrSkip > fullCorrSkip * 1.05f)//prefer full caching slightly - include a bias factor in options? {//assume IO and row adjustment (unfisher, subtract mean) won't be the limiting factor, since IO should be balanced during correlation due to evenly sized passes when not fully cached - cacheFullInput = false; + cacheFullInputOut = false; ret = numRowsPartial; } else { - cacheFullInput = true; + cacheFullInputOut = true; ret = numRowsFull; } if (ret < 1) ret = 1;//sanitize, just in case if (ret > numRows) ret = numRows; return ret; } else {//if we can't cache the whole thing, split passes evenly - cacheFullInput = false; + cacheFullInputOut = false; int64_t div = max((int64_t)1, (outrowBytes + inrowBytes) * numRows); #ifdef CARET_OMP targetBytes -= inrowBytes * omp_get_max_threads(); diff --git a/src/Algorithms/AlgorithmCiftiCorrelationGradient.h b/src/Algorithms/AlgorithmCiftiCorrelationGradient.h index 25b90dd12..d60e710de 100644 --- a/src/Algorithms/AlgorithmCiftiCorrelationGradient.h +++ b/src/Algorithms/AlgorithmCiftiCorrelationGradient.h @@ -22,7 +22,6 @@ /*LICENSE_END*/ #include "AbstractAlgorithm.h" -#include "CaretPointer.h" #include "StructureEnum.h" namespace caret { @@ -30,11 +29,7 @@ namespace caret { class AlgorithmCiftiCorrelationGradient : public AbstractAlgorithm { AlgorithmCiftiCorrelationGradient(); - struct CacheRow - { - int m_ciftiIndex; - std::vector m_row; - }; + public: struct RowInfo { bool m_haveCalculated; @@ -46,22 +41,32 @@ namespace caret { m_cacheIndex = -1; } }; + private: + struct CacheRow + { + int m_ciftiIndex; + std::vector m_row; + CacheRow(int64_t rowLength) + { + m_ciftiIndex = -1; + m_row.resize(rowLength); + } + }; std::vector m_rowCache; - std::vector m_rowInfo; - std::vector > m_tempRows;//reuse return values in getRow instead of reallocating + std::vector m_rowInfo, m_firstCorrInfo; std::vector m_outColumn; - int m_cacheUsed;//reuse cache entries instead of reallocating them - int m_numCols; + int64_t m_numCols; bool m_undoFisherInput, m_applyFisher, m_covariance; const CiftiFile* m_inputCifti;//so that accesses work through the cache functions - void cacheRows(const std::vector& ciftiIndices);//grabs the rows and does whatever it needs to, using as much IO bandwidth and CPU resources as available/needed + int64_t m_rowLengthFirst;//for -double-correlation + bool m_doubleCorr, m_firstCovar, m_firstNoDemean, m_firstFisher; + float m_memLimitGB; + void cacheRows(const std::vector& ciftiIndices);//grabs the rows and does whatever it needs to, using as much IO bandwidth and CPU resources as available/needed void clearCache(); - const float* getRow(const int& ciftiIndex, float& rootResidSqr, const bool& mustBeCached = false); - void adjustRow(float* rowOut, const int& ciftiIndex);//does the reverse fisher transform, computes stuff, subtracts mean - float* getTempRow(); - float correlate(const float* row1, const float& rrs1, const float* row2, const float& rrs2); - void init(const CiftiFile* input, const bool& undoFisherInput, const bool& applyFisher, const bool& covariance); - int numRowsForMem(const float& memLimitGB, const int64_t& inrowBytes, const int64_t& outrowBytes, const int& numRows, bool& cacheFullInput); + const float* getRow(const int& ciftiIndex, float& rootResidSqr, float* scratchStorage); + void init(const CiftiFile* input, const float& memLimitGB, const bool& undoFisherInput, const bool& applyFisher, const bool& covariance, + const bool doubleCorr, const bool firstFisher, const bool firstNoDemean, const bool firstCovar); + int numRowsForMem(const int64_t& inrowBytes, const int64_t& outrowBytes, const int& numRows, bool& cacheFullInput); //void processSurfaceComponentLocal(StructureEnum::Enum& myStructure, const float& surfKern, const float& memLimitGB, SurfaceFile* mySurf); void processSurfaceComponent(StructureEnum::Enum& myStructure, const float& surfKern, const float& memLimitGB, SurfaceFile* mySurf, const MetricFile* myAreas); void processSurfaceComponent(StructureEnum::Enum& myStructure, const float& surfKern, const float& surfExclude, const float& memLimitGB, SurfaceFile* mySurf, const MetricFile* myAreas); @@ -78,7 +83,8 @@ namespace caret { const float& surfKern = -1.0f, const float& volKern = -1.0f, const bool& undoFisherInput = false, const bool& applyFisher = false, const float& surfaceExclude = -1.0f, const float& volumeExclude = -1.0f, const bool& covariance = false, - const float& memLimitGB = -1.0f); + const float& memLimitGB = -1.0f, + const bool doubleCorr = false, const bool firstFisher = false, const bool firstNoDemean = false, const bool firstCovar = false); static OperationParameters* getParameters(); static void useParameters(OperationParameters* myParams, ProgressObject* myProgObj); static AString getCommandSwitch(); -- GitLab From af514a0f3b0a938af631fbab24d76d58b74c1b7d Mon Sep 17 00:00:00 2001 From: John Harwell Date: Fri, 31 Aug 2018 10:52:38 -0500 Subject: [PATCH 004/427] Add conditional compilation of 'QOverload" usage only when Qt 5.7 or later. When not Qt 5.7, older, more verbose syntax for signal connection using function-based connections is used. Note that some signals are overloaded and in this case, casting is needed to use the corrected overloaded signal and avoid a compilation error. --- src/GuiQt/AnnotationTextSubstitutionViewController.cxx | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/GuiQt/AnnotationTextSubstitutionViewController.cxx b/src/GuiQt/AnnotationTextSubstitutionViewController.cxx index ca4cb0e57..3cc4e4010 100644 --- a/src/GuiQt/AnnotationTextSubstitutionViewController.cxx +++ b/src/GuiQt/AnnotationTextSubstitutionViewController.cxx @@ -97,8 +97,13 @@ m_browserWindowIndex(browserWindowIndex) QSpinBox* sb = new QSpinBox(); sb->setRange(1, 100); +#if QT_VERSION >= 0x050700 QObject::connect(sb, QOverload::of(&QSpinBox::valueChanged), [=] { valueIndexSpinBoxChanged(i); } ); +#else + QObject::connect(sb, static_cast(&QSpinBox::valueChanged), + [=] { valueIndexSpinBoxChanged(i); } ); +#endif QLabel* fl = new QLabel(); -- GitLab From 4d4b3f52f697952334011e0a4ad36b5e11815474 Mon Sep 17 00:00:00 2001 From: Tim Coalson Date: Wed, 5 Sep 2018 16:48:16 -0500 Subject: [PATCH 005/427] make -cifti-convert read float64 values from text and warn (rather than error) if they don't fit in float32 --- src/Operations/OperationCiftiConvert.cxx | 39 +++++++++++++++++++++--- 1 file changed, 34 insertions(+), 5 deletions(-) diff --git a/src/Operations/OperationCiftiConvert.cxx b/src/Operations/OperationCiftiConvert.cxx index 1df9a95f7..8b101e66e 100644 --- a/src/Operations/OperationCiftiConvert.cxx +++ b/src/Operations/OperationCiftiConvert.cxx @@ -129,6 +129,38 @@ OperationParameters* OperationCiftiConvert::getParameters() return ret; } +namespace +{ + + bool haveWarned = false; + + float toFloat(const AString& input) + { + bool ok = false; + double converted = input.toDouble(&ok); + if (!ok) throw OperationException("failed to convert text to number: '" + input + "'"); + float ret = float(converted);//this will turn some non-inf values into +/- inf, so let's fix that + if (!std::isinf(converted) && (abs(converted) > numeric_limits::max() || abs(converted) < numeric_limits::denorm_min())) + { + if (!haveWarned) + { + CaretLogWarning("input number(s) changed to fit range of float32, first instance: '" + input + "'"); + haveWarned = true; + } + if (std::isinf(ret)) + { + if (ret > 0.0f) + { + ret = numeric_limits::max(); + } else { + ret = -numeric_limits::max(); + } + } + } + return ret; + } +} + void OperationCiftiConvert::useParameters(OperationParameters* myParams, ProgressObject* myProgObj) { LevelProgress myProgress(myProgObj); @@ -498,11 +530,9 @@ void OperationCiftiConvert::useParameters(OperationParameters* myParams, Progres } ciftiOut->setCiftiXML(outXML); vector temprow(textRowLength); - bool ok = false; for (int i = 0; i < textRowLength; ++i) { - temprow[i] = entries[i].toFloat(&ok); - if (!ok) throw OperationException("failed to convert text to number: '" + entries[i] + "'"); + temprow[i] = toFloat(entries[i]); } ciftiOut->setRow(temprow.data(), 0); for (int64_t j = 1; j < numRows; ++j) @@ -517,8 +547,7 @@ void OperationCiftiConvert::useParameters(OperationParameters* myParams, Progres if (entries.size() != textRowLength) throw OperationException("text file has inconsistent line length"); for (int i = 0; i < textRowLength; ++i) { - temprow[i] = entries[i].toFloat(&ok); - if (!ok) throw OperationException("failed to convert text to number: '" + entries[i] + "'"); + temprow[i] = toFloat(entries[i]); } ciftiOut->setRow(temprow.data(), j); } -- GitLab From a3e07dbfdcdb71ebaf779bd489fc9a697efff242 Mon Sep 17 00:00:00 2001 From: John Harwell Date: Fri, 31 Aug 2018 16:58:33 -0500 Subject: [PATCH 006/427] Resolve WB-825: If viewing volume and all underlays are disabled, volume in lowest overlay changes: When there are no overlays containing volume enabled, wb_view changed the lowest layer to the first available volume file in method that searched for the underlay volume. Now, when no layers are enabled, the lowest layer containing a volume, even if disabled, provides the underlay volume. --- src/Brain/OverlaySet.cxx | 26 +++++++++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/src/Brain/OverlaySet.cxx b/src/Brain/OverlaySet.cxx index 052a9dec0..7b2100a33 100644 --- a/src/Brain/OverlaySet.cxx +++ b/src/Brain/OverlaySet.cxx @@ -178,7 +178,8 @@ OverlaySet::getUnderlayVolume() { VolumeMappableInterface* vf = NULL; - for (int32_t i = (getNumberOfDisplayedOverlays() - 1); i >= 0; i--) { + const int32_t numOverlays = getNumberOfDisplayedOverlays(); + for (int32_t i = (numOverlays - 1); i >= 0; i--) { if (m_overlays[i]->isEnabled()) { CaretMappableDataFile* mapFile = NULL; int32_t mapIndex; @@ -195,6 +196,29 @@ OverlaySet::getUnderlayVolume() } } } + + if (vf == NULL) { + /* + * If we are here, either there are no volume files or + * no overlays are enabled containing a volume file. + * So, find the lowest layer than contains a volume file, + * even if the layer is disabled. + */ + for (int32_t i = 0; i < numOverlays; i++) { + CaretMappableDataFile* mapFile = NULL; + int32_t mapIndex; + m_overlays[i]->getSelectionData(mapFile, + mapIndex); + + if (mapFile != NULL) { + if (mapFile->isVolumeMappable()) { + vf = dynamic_cast(mapFile); + break; + } + } + } + } + return vf; } -- GitLab From 23c7c4dc1c6091fa48c25badcca9df49d52b1fb2 Mon Sep 17 00:00:00 2001 From: John Harwell Date: Fri, 7 Sep 2018 15:52:49 -0500 Subject: [PATCH 007/427] Fixed compilation error for Qt 5.5.1. QAction's parent parameter was required in Qt 5.5.1 but made optional in a later version of Qt. --- src/GuiQt/BalsaDatabaseUploadSceneFileDialog.cxx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/GuiQt/BalsaDatabaseUploadSceneFileDialog.cxx b/src/GuiQt/BalsaDatabaseUploadSceneFileDialog.cxx index 406542687..40b27c5a8 100644 --- a/src/GuiQt/BalsaDatabaseUploadSceneFileDialog.cxx +++ b/src/GuiQt/BalsaDatabaseUploadSceneFileDialog.cxx @@ -213,7 +213,7 @@ BalsaDatabaseUploadSceneFileDialog::createLoginWidget() /* * Show password tool button */ - m_showPasswordAction = new QAction("Show"); + m_showPasswordAction = new QAction("Show", this); m_showPasswordAction->setCheckable(true); m_showPasswordAction->setChecked(false); QObject::connect(m_showPasswordAction, &QAction::triggered, -- GitLab From 57f4080cf6ad0def238b04885aa51aad016df7a1 Mon Sep 17 00:00:00 2001 From: Tim Coalson Date: Tue, 11 Sep 2018 15:59:53 -0500 Subject: [PATCH 008/427] improve help info of -surface-sphere-project-unproject --- ...AlgorithmSurfaceSphereProjectUnproject.cxx | 27 ++++++++++++++----- 1 file changed, 20 insertions(+), 7 deletions(-) diff --git a/src/Algorithms/AlgorithmSurfaceSphereProjectUnproject.cxx b/src/Algorithms/AlgorithmSurfaceSphereProjectUnproject.cxx index 9fd61b099..02bf09f4f 100644 --- a/src/Algorithms/AlgorithmSurfaceSphereProjectUnproject.cxx +++ b/src/Algorithms/AlgorithmSurfaceSphereProjectUnproject.cxx @@ -38,25 +38,38 @@ AString AlgorithmSurfaceSphereProjectUnproject::getCommandSwitch() AString AlgorithmSurfaceSphereProjectUnproject::getShortDescription() { - return "DEFORM A SPHERE ACCORDING TO A REGISTRATION"; + return "COPY REGISTRATION DEFORMATIONS TO DIFFERENT SPHERE"; } OperationParameters* AlgorithmSurfaceSphereProjectUnproject::getParameters() { OperationParameters* ret = new OperationParameters(); - ret->addSurfaceParameter(1, "sphere-in", "the sphere with the desired output mesh"); + ret->addSurfaceParameter(1, "sphere-in", "a sphere with the desired output mesh"); ret->addSurfaceParameter(2, "sphere-project-to", "a sphere that aligns with sphere-in"); - ret->addSurfaceParameter(3, "sphere-unproject-from", "sphere-project-to deformed to the output space"); + ret->addSurfaceParameter(3, "sphere-unproject-from", " deformed to the desired output space"); ret->addSurfaceOutputParameter(4, "sphere-out", "the output sphere"); ret->setHelpText( - AString("Each vertex of is projected to to obtain barycentric weights, which are then used to unproject ") + - "from . This results in a sphere with the topology of , but coordinates shifted by the deformation between " + - " and . and must have the same topology as each other, " + - "but may have different topology." + AString("Background: A surface registration starts with an input sphere, and moves its vertices around on the sphere until it matches the template data. ") + + "Since the starting sphere of the registration typically has no vertex correspondence to any other sphere (often, it is a native sphere), it is inconvenient to manipulate or compare these deformations across subjects, etc.\n\n" + + + "The purpose of this command is to be able to apply these deformations onto a new sphere of the user's choice, to make it easier to compare or manipulate them. " + + "Common uses are to concatenate two successive separate registrations (e.g. Human to Chimpanzee, and then Chimpanzee to Macaque) or inversion (for dedrifting or symmetric registration schemes).\n\n" + + + " must already be considered to be in alignment with one of the two ends of the registration (if your registration is Human to Chimpanzee, must be in register with either Human or Chimpanzee). " + + "The 'project-to' sphere must be the side of the registration that is aligned to the input sphere (if your registration is Human to Chimpanzee, and your input sphere is aligned with Human, then 'project-to' should be the original Human sphere). " + + "The 'unproject-from' sphere must be the remaining sphere of the registration (original vs deformed/registered). " + + "The output is as if you had run the same registration with as the starting sphere, in the direction of deforming the 'project-to' sphere to create the 'unproject-from' sphere.\n\n" + + + "Note that this command cannot check for you what spheres are aligned with other spheres, and using the wrong spheres or in the incorrect order will not necessarily cause an error message. " + + "In some cases, it may be useful to use a new, arbitrary sphere as the input, which can be created with the -surface-create-sphere command.\n\n" + + + "Technical details: Each vertex of is projected to a triangle of , and its new position is determined by the position of the corresponding triangle in . " + + "The output is a sphere with the topology of , but coordinates shifted by the deformation from to . " + + " and must have the same topology as each other, but may have any topology." ); return ret; } -- GitLab From bccd341309bdb4ee37b6c484cdad918c731fb39a Mon Sep 17 00:00:00 2001 From: John Harwell Date: Thu, 26 Jul 2018 16:25:04 -0500 Subject: [PATCH 009/427] WB-636 Texture Based Annotations: Partial implementation. --- .../AnnotationSurfaceOffsetVectorTypeEnum.cxx | 5 + .../AnnotationSurfaceOffsetVectorTypeEnum.h | 4 +- ...inOpenGLAnnotationDrawingFixedPipeline.cxx | 149 ++++- ...rainOpenGLAnnotationDrawingFixedPipeline.h | 4 + src/Brain/BrainOpenGLFixedPipeline.cxx | 8 +- src/Brain/BrainOpenGLTextRenderInterface.h | 71 +- src/Brain/DummyFontTextRenderer.cxx | 58 +- src/Brain/DummyFontTextRenderer.h | 15 +- src/Brain/FtglFontTextRenderer.cxx | 621 +++++++++++++++++- src/Brain/FtglFontTextRenderer.h | 37 +- src/GuiQt/QGLWidgetTextRenderer.cxx | 58 +- src/GuiQt/QGLWidgetTextRenderer.h | 15 +- 12 files changed, 1016 insertions(+), 29 deletions(-) diff --git a/src/Annotations/AnnotationSurfaceOffsetVectorTypeEnum.cxx b/src/Annotations/AnnotationSurfaceOffsetVectorTypeEnum.cxx index 48f6d22bb..35e639199 100644 --- a/src/Annotations/AnnotationSurfaceOffsetVectorTypeEnum.cxx +++ b/src/Annotations/AnnotationSurfaceOffsetVectorTypeEnum.cxx @@ -118,6 +118,11 @@ AnnotationSurfaceOffsetVectorTypeEnum::initialize() "SURACE_NORMAL", "N", "Surace Normal")); + + enumData.push_back(AnnotationSurfaceOffsetVectorTypeEnum(TANGENT, + "TANGENT", + "T", + "Tangent")); } /** diff --git a/src/Annotations/AnnotationSurfaceOffsetVectorTypeEnum.h b/src/Annotations/AnnotationSurfaceOffsetVectorTypeEnum.h index 613be01d7..a85e9314d 100644 --- a/src/Annotations/AnnotationSurfaceOffsetVectorTypeEnum.h +++ b/src/Annotations/AnnotationSurfaceOffsetVectorTypeEnum.h @@ -38,7 +38,9 @@ public: /** Vector starts at centroid and extends through vertex */ CENTROID_THRU_VERTEX, /** Vector is surface normal */ - SURACE_NORMAL + SURACE_NORMAL, + /** Tangent to surface */ + TANGENT }; diff --git a/src/Brain/BrainOpenGLAnnotationDrawingFixedPipeline.cxx b/src/Brain/BrainOpenGLAnnotationDrawingFixedPipeline.cxx index 0f3e46989..f691ad931 100644 --- a/src/Brain/BrainOpenGLAnnotationDrawingFixedPipeline.cxx +++ b/src/Brain/BrainOpenGLAnnotationDrawingFixedPipeline.cxx @@ -56,6 +56,7 @@ #include "GraphicsEngineDataOpenGL.h" #include "GraphicsPrimitiveV3f.h" #include "GraphicsPrimitiveV3fC4f.h" +#include "GraphicsPrimitiveV3fN3f.h" #include "GraphicsPrimitiveV3fT3f.h" #include "GraphicsShape.h" #include "GraphicsUtilitiesOpenGL.h" @@ -257,12 +258,18 @@ BrainOpenGLAnnotationDrawingFixedPipeline::getAnnotationDrawingSpaceCoordinate(c surfaceCenter, offsetUnitVector); MathFunctions::normalizeVector(offsetUnitVector); - } break; + } + break; case AnnotationSurfaceOffsetVectorTypeEnum::SURACE_NORMAL: + { const float* normalVector = surfaceDisplayed->getNormalVector(annotationNodeIndex); offsetUnitVector[0] = normalVector[0]; offsetUnitVector[1] = normalVector[1]; offsetUnitVector[2] = normalVector[2]; + } + break; + case AnnotationSurfaceOffsetVectorTypeEnum::TANGENT: + CaretAssertToDoFatal(); break; } @@ -2373,6 +2380,128 @@ BrainOpenGLAnnotationDrawingFixedPipeline::clipLineAtTextBox(const float bottomL } } +/** + * Draw an annotation text with tangent offset + * + * @param annotationFile + * File containing the annotation. + * @param text + * Annotation text to draw. + * @param surfaceDisplayed + * Surface that is displayed (may be NULL). + * @return + * True if the annotation was drawn while NOT selecting annotations. + */ +bool +BrainOpenGLAnnotationDrawingFixedPipeline::drawTextTangentOffset(AnnotationFile* annotationFile, + AnnotationText* text, + const Surface* surfaceDisplayed) +{ + CaretAssert(annotationFile); + CaretAssert(text); + CaretAssert(surfaceDisplayed); + + const AnnotationCoordinate* coord = text->getCoordinate(); + StructureEnum::Enum structure; + int32_t surfaceNumberOfNodes(0); + int32_t vertexIndex(0); + float offsetLength(0.0f); + AnnotationSurfaceOffsetVectorTypeEnum::Enum offsetVectorType; + coord->getSurfaceSpace(structure, surfaceNumberOfNodes, vertexIndex, offsetLength, offsetVectorType); + + if ((structure == surfaceDisplayed->getStructure()) + && (surfaceDisplayed->getNumberOfNodes() == surfaceNumberOfNodes)) { + float vertexXYZ[3]; + surfaceDisplayed->getCoordinate(vertexIndex, + vertexXYZ); + const float* normalXYZ = surfaceDisplayed->getNormalVector(vertexIndex); + const BoundingBox* boundingBox = surfaceDisplayed->getBoundingBox(); + const float surfaceExtentZ = boundingBox->getDifferenceZ(); + + /* + * Need to restore model space + * Recall that all other annotation spaces are drawn in window space + */ + glMatrixMode(GL_PROJECTION); + glPushMatrix(); + glLoadMatrixd(m_modelSpaceProjectionMatrix); + + glMatrixMode(GL_MODELVIEW); + glPushMatrix(); + glLoadMatrixd(m_modelSpaceModelMatrix); + int32_t savedViewport[4]; + glGetIntegerv(GL_VIEWPORT, + savedViewport); + glViewport(m_modelSpaceViewport[0], + m_modelSpaceViewport[1], + m_modelSpaceViewport[2], + m_modelSpaceViewport[3]); + + glPushMatrix(); + + /* + * Matrix place text in plane of vertex's normal vector + */ + Matrix4x4 rotationMatrix; + rotationMatrix.setMatrixToOpenGLRotationFromVector(normalXYZ); + double rotationArray[16]; + rotationMatrix.getMatrixForOpenGL(rotationArray); + + const float rotationAngle = text->getRotationAngle(); + + glPushMatrix(); + glTranslatef(vertexXYZ[0], vertexXYZ[1], vertexXYZ[2]); + glMultMatrixd(rotationArray); + glRotatef(rotationAngle, 0.0, 0.0, 1.0); + + float bottomLeft[3]; + float bottomRight[3]; + float topRight[3]; + float topLeft[3]; + m_brainOpenGLFixedPipeline->getTextRenderer()->getBoundsForTextInModelSpace(*text, surfaceExtentZ, m_textDrawingFlags, + bottomLeft, bottomRight, topRight, topLeft); + + uint8_t selectionColorRGBA[4] = { 0, 0, 0, 0 }; + if (m_selectionModeFlag) { + getIdentificationColor(selectionColorRGBA); + GraphicsPrimitiveV3fN3f primitive(GraphicsPrimitive::PrimitiveType::OPENGL_TRIANGLE_STRIP, + selectionColorRGBA); + primitive.addVertex(topLeft, normalXYZ); + primitive.addVertex(bottomLeft, normalXYZ); + primitive.addVertex(topRight, normalXYZ); + primitive.addVertex(bottomRight, normalXYZ); + GraphicsEngineDataOpenGL::draw(&primitive); + m_selectionInfo.push_back(SelectionInfo(annotationFile, + text, + AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_NONE, + vertexXYZ)); + } + else { + m_brainOpenGLFixedPipeline->getTextRenderer()->drawTextInModelSpace(*text, + surfaceExtentZ, + normalXYZ, + m_textDrawingFlags); + } + + glPopMatrix(); + + glPopMatrix(); /* restore MODELVIEW */ + + glViewport(savedViewport[0], + savedViewport[1], + savedViewport[2], + savedViewport[3]); + glPopMatrix(); + + glMatrixMode(GL_PROJECTION); + glPopMatrix(); + glMatrixMode(GL_MODELVIEW); + } + + + return false; +} + /** * Draw an annotation text. * @@ -2402,7 +2531,23 @@ BrainOpenGLAnnotationDrawingFixedPipeline::drawText(AnnotationFile* annotationFi return false; } } - + + if (text->getCoordinateSpace() == AnnotationCoordinateSpaceEnum::SURFACE) { + switch (text->getCoordinate()->getSurfaceOffsetVectorType()) { + case AnnotationSurfaceOffsetVectorTypeEnum::CENTROID_THRU_VERTEX: + break; + case AnnotationSurfaceOffsetVectorTypeEnum::SURACE_NORMAL: + break; + case AnnotationSurfaceOffsetVectorTypeEnum::TANGENT: + /* + * Tangent offset is drawn in model space + */ + return drawTextTangentOffset(annotationFile, + text, + surfaceDisplayed); + break; + } + } float annXYZ[3]; if ( ! getAnnotationDrawingSpaceCoordinate(text, diff --git a/src/Brain/BrainOpenGLAnnotationDrawingFixedPipeline.h b/src/Brain/BrainOpenGLAnnotationDrawingFixedPipeline.h index c37739535..5628e011e 100644 --- a/src/Brain/BrainOpenGLAnnotationDrawingFixedPipeline.h +++ b/src/Brain/BrainOpenGLAnnotationDrawingFixedPipeline.h @@ -209,6 +209,10 @@ namespace caret { AnnotationText* text, const Surface* surfaceDisplayed); + bool drawTextTangentOffset(AnnotationFile* annotationFile, + AnnotationText* text, + const Surface* surfaceDisplayed); + void drawColorBarSections(const AnnotationColorBar* colorBar, const float bottomLeft[3], const float bottomRight[3], diff --git a/src/Brain/BrainOpenGLFixedPipeline.cxx b/src/Brain/BrainOpenGLFixedPipeline.cxx index 5cd118155..8a583a051 100644 --- a/src/Brain/BrainOpenGLFixedPipeline.cxx +++ b/src/Brain/BrainOpenGLFixedPipeline.cxx @@ -1891,6 +1891,8 @@ BrainOpenGLFixedPipeline::drawSurface(Surface* surface, BrainOpenGLAnnotationDrawingFixedPipeline::Inputs::WINDOW_DRAWING_NO); std::vector emptyColorBars; std::vector emptyViewportAnnotations; + + m_annotationDrawing->drawAnnotations(&inputs, AnnotationCoordinateSpaceEnum::SURFACE, emptyColorBars, @@ -6675,7 +6677,7 @@ BrainOpenGLFixedPipeline::drawTextAtModelCoords(const double modelX, const AnnotationText& annotationText) { if (getTextRenderer() != NULL) { - getTextRenderer()->drawTextAtModelCoords(modelX, + getTextRenderer()->drawTextAtModelCoordsFacingUser(modelX, modelY, modelZ, annotationText, @@ -6697,7 +6699,7 @@ BrainOpenGLFixedPipeline::drawTextAtModelCoords(const double modelXYZ[3], const AnnotationText& annotationText) { if (getTextRenderer() != NULL) { - getTextRenderer()->drawTextAtModelCoords(modelXYZ, + getTextRenderer()->drawTextAtModelCoordsFacingUser(modelXYZ, annotationText, BrainOpenGLTextRenderInterface::DrawingFlags()); } @@ -6717,7 +6719,7 @@ BrainOpenGLFixedPipeline::drawTextAtModelCoords(const float modelXYZ[3], const AnnotationText& annotationText) { if (getTextRenderer() != NULL) { - getTextRenderer()->drawTextAtModelCoords(modelXYZ, + getTextRenderer()->drawTextAtModelCoordsFacingUser(modelXYZ, annotationText, BrainOpenGLTextRenderInterface::DrawingFlags()); } diff --git a/src/Brain/BrainOpenGLTextRenderInterface.h b/src/Brain/BrainOpenGLTextRenderInterface.h index 5450c369e..cf356644a 100644 --- a/src/Brain/BrainOpenGLTextRenderInterface.h +++ b/src/Brain/BrainOpenGLTextRenderInterface.h @@ -74,6 +74,8 @@ namespace caret { * Viewport Y-coordinate. * @param annotationText * Annotation text and attributes. + * @param flags + * Drawing flags. */ virtual void drawTextAtViewportCoords(const double viewportX, const double viewportY, @@ -94,6 +96,8 @@ namespace caret { * Viewport Z-coordinate. * @param annotationText * Annotation text and attributes. + * @param flags + * Drawing flags. */ virtual void drawTextAtViewportCoords(const double viewportX, const double viewportY, @@ -104,6 +108,7 @@ namespace caret { /** * Draw annnotation text at the given model coordinates using * the the annotations attributes for the style of text. + * Text is drawn so that is in the plane of the screen (faces user) * * Depth testing is ENABLED when drawing text with this method. * @@ -115,8 +120,10 @@ namespace caret { * Model Z-coordinate. * @param annotationText * Annotation text and attributes. + * @param flags + * Drawing flags. */ - virtual void drawTextAtModelCoords(const double modelX, + virtual void drawTextAtModelCoordsFacingUser(const double modelX, const double modelY, const double modelZ, const AnnotationText& annotationText, @@ -125,6 +132,7 @@ namespace caret { /** * Draw annnotation text at the given model coordinates using * the the annotations attributes for the style of text. + * Text is drawn so that is in the plane of the screen (faces user) * * Depth testing is ENABLED when drawing text with this method. * @@ -132,16 +140,19 @@ namespace caret { * Model XYZ coordinate. * @param annotationText * Annotation text and attributes. + * @param flags + * Drawing flags. */ - void drawTextAtModelCoords(const double modelXYZ[3], + void drawTextAtModelCoordsFacingUser(const double modelXYZ[3], const AnnotationText& annotationText, const DrawingFlags& flags) { - drawTextAtModelCoords(modelXYZ[0], modelXYZ[1], modelXYZ[2], annotationText, flags); + drawTextAtModelCoordsFacingUser(modelXYZ[0], modelXYZ[1], modelXYZ[2], annotationText, flags); } /** * Draw annnotation text at the given model coordinates using * the the annotations attributes for the style of text. + * Text is drawn so that is in the plane of the screen (faces user) * * Depth testing is ENABLED when drawing text with this method. * @@ -149,13 +160,59 @@ namespace caret { * Model XYZ coordinate. * @param annotationText * Annotation text and attributes. + * @param flags + * Drawing flags. */ - void drawTextAtModelCoords(const float modelXYZ[3], - const AnnotationText& annotationText, - const DrawingFlags& flags) { - drawTextAtModelCoords(modelXYZ[0], modelXYZ[1], modelXYZ[2], annotationText, flags); + void drawTextAtModelCoordsFacingUser(const float modelXYZ[3], + const AnnotationText& annotationText, + const DrawingFlags& flags) { + drawTextAtModelCoordsFacingUser(modelXYZ[0], modelXYZ[1], modelXYZ[2], annotationText, flags); } + /** + * Get the bounds of text drawn in model space using the current model transformations. + * + * @param annotationText + * Text that is to be drawn. + * @param heightOrWidthForPercentageSizeText + * Size of region used when converting percentage size to a fixed size + * @param flags + * Drawing flags. + * @param bottomLeftOut + * The bottom left corner of the text bounds. + * @param bottomRightOut + * The bottom right corner of the text bounds. + * @param topRightOut + * The top right corner of the text bounds. + * @param topLeftOut + * The top left corner of the text bounds. + */ + virtual void getBoundsForTextInModelSpace(const AnnotationText& annotationText, + const float heightOrWidthForPercentageSizeText, + const DrawingFlags& flags, + float bottomLeftOut[3], + float bottomRightOut[3], + float topRightOut[3], + float topLeftOut[3]) = 0; + /** + * Draw text in model space using the current model transformations. + * + * Depth testing is ENABLED when drawing text with this method. + * + * @param annotationText + * Annotation text and attributes. + * @param heightOrWidthForPercentageSizeText + * If positive, use it to override width/height of viewport. + * @param backgroundOverrideRGBA + * If alpha is greater that zero, draw background with this color + * @param flags + * Drawing flags. + */ + virtual void drawTextInModelSpace(const AnnotationText& annotationText, + const float heightOrWidthForPercentageSizeText, + const float normalVector[3], + const DrawingFlags& flags) = 0; + /** * Get the estimated width and height of text (in pixels) using the given text * attributes. diff --git a/src/Brain/DummyFontTextRenderer.cxx b/src/Brain/DummyFontTextRenderer.cxx index 886686e48..1b750a4e7 100644 --- a/src/Brain/DummyFontTextRenderer.cxx +++ b/src/Brain/DummyFontTextRenderer.cxx @@ -116,6 +116,7 @@ DummyFontTextRenderer::drawTextAtViewportCoords(const double /*viewportX*/, /** * Draw annnotation text at the given model coordinates using * the the annotations attributes for the style of text. + * Text is drawn so that is in the plane of the screen (faces user) * * Depth testing is ENABLED when drawing text with this method. * @@ -127,9 +128,11 @@ DummyFontTextRenderer::drawTextAtViewportCoords(const double /*viewportX*/, * Model Z-coordinate. * @param annotationText * Annotation text and attributes. + * @param flags + * Drawing flags. */ void -DummyFontTextRenderer::drawTextAtModelCoords(const double /*modelX*/, +DummyFontTextRenderer::drawTextAtModelCoordsFacingUser(const double /*modelX*/, const double /*modelY*/, const double /*modelZ*/, const AnnotationText& /*annotationText*/, @@ -138,6 +141,59 @@ DummyFontTextRenderer::drawTextAtModelCoords(const double /*modelX*/, } +/** + * Draw text in model space using the current model transformations. + * + * Depth testing is ENABLED when drawing text with this method. + * + * @param annotationText + * Annotation text and attributes. + * @param heightOrWidthForPercentageSizeText + * If positive, use it to override width/height of viewport. + * @param normalVector + * Normal vector of text. + * @param flags + * Drawing flags. + */ +void +DummyFontTextRenderer::drawTextInModelSpace(const AnnotationText& /* annotationText */, + const float /*heightOrWidthForPercentageSizeText*/, + const float* /*normalVector[3]*/, + const DrawingFlags& /* flags */) +{ + +} + +/** + * Get the bounds of text drawn in model space using the current model transformations. + * + * @param annotationText + * Text that is to be drawn. + * @param heightOrWidthForPercentageSizeText + * Size of region used when converting percentage size to a fixed size + * @param flags + * Drawing flags. + * @param bottomLeftOut + * The bottom left corner of the text bounds. + * @param bottomRightOut + * The bottom right corner of the text bounds. + * @param topRightOut + * The top right corner of the text bounds. + * @param topLeftOut + * The top left corner of the text bounds. + */ +void +DummyFontTextRenderer::getBoundsForTextInModelSpace(const AnnotationText& /*annotationText*/, + const float /*heightOrWidthForPercentageSizeText*/, + const DrawingFlags& /*flags*/, + float* /*bottomLeftOut[3]*/, + float* /*bottomRightOut[3]*/, + float* /*topRightOut[3]*/, + float* /*topLeftOut[3]*/) +{ + +} + /** * Get the bounds of text (in pixels) using the given text * attributes. diff --git a/src/Brain/DummyFontTextRenderer.h b/src/Brain/DummyFontTextRenderer.h index 6bfe16db2..7a2852671 100644 --- a/src/Brain/DummyFontTextRenderer.h +++ b/src/Brain/DummyFontTextRenderer.h @@ -48,12 +48,17 @@ namespace caret { const AnnotationText& annotationText, const DrawingFlags& flags) override; - virtual void drawTextAtModelCoords(const double modelX, + virtual void drawTextAtModelCoordsFacingUser(const double modelX, const double modelY, const double modelZ, const AnnotationText& annotationText, const DrawingFlags& flags) override; + virtual void drawTextInModelSpace(const AnnotationText& annotationText, + const float heightOrWidthForPercentageSizeText, + const float normalVector[3], + const DrawingFlags& flags) override; + virtual void getTextWidthHeightInPixels(const AnnotationText& annotationText, const DrawingFlags& flags, const double viewportWidth, @@ -61,6 +66,14 @@ namespace caret { double& widthOut, double& heightOut) override; + virtual void getBoundsForTextInModelSpace(const AnnotationText& annotationText, + const float heightOrWidthForPercentageSizeText, + const DrawingFlags& flags, + float bottomLeftOut[3], + float bottomRightOut[3], + float topRightOut[3], + float topLeftOut[3]) override; + virtual void getBoundsForTextAtViewportCoords(const AnnotationText& annotationText, const DrawingFlags& flags, const double viewportX, diff --git a/src/Brain/FtglFontTextRenderer.cxx b/src/Brain/FtglFontTextRenderer.cxx index 01481d25a..676f0bc53 100644 --- a/src/Brain/FtglFontTextRenderer.cxx +++ b/src/Brain/FtglFontTextRenderer.cxx @@ -80,7 +80,9 @@ #include "CaretAssert.h" #include "CaretLogger.h" #include "CaretOpenGLInclude.h" +#include "GraphicsEngineDataOpenGL.h" #include "GraphicsOpenGLError.h" +#include "GraphicsPrimitiveV3fN3f.h" #include "GraphicsShape.h" #include "GraphicsUtilitiesOpenGL.h" #include "MathFunctions.h" @@ -130,6 +132,7 @@ FtglFontTextRenderer::FtglFontTextRenderer() defaultAnnotationText.setBoldStyleEnabled(false); defaultAnnotationText.setUnderlineStyleEnabled(false); m_defaultFont = getFont(defaultAnnotationText, + FtglFontTypeEnum::TEXTURE, true); #endif // HAVE_FREETYPE m_depthTestingStatus = DEPTH_TEST_NO; @@ -173,6 +176,10 @@ FtglFontTextRenderer::isValid() const * * @param annotationText * Annotation Text that is to be drawn. + * @param ftglFontType + * Type of font. + * @param heightOrWidthForPercentageSizeText + * If positive, use it to override width/height of viewport. * @param creatingDefaultFontFlag * True if creating the default font. * @return @@ -181,12 +188,162 @@ FtglFontTextRenderer::isValid() const */ FTFont* FtglFontTextRenderer::getFont(const AnnotationText& annotationText, + const FtglFontTypeEnum ftglFontType, + const float heightOrWidthForPercentageSizeText, const bool creatingDefaultFontFlag) { #ifdef HAVE_FREETYPE int32_t viewportWidth = m_viewportWidth; int32_t viewportHeight = m_viewportHeight; + switch (annotationText.getCoordinateSpace()) { + case AnnotationCoordinateSpaceEnum::CHART: + break; + case AnnotationCoordinateSpaceEnum::STEREOTAXIC: + break; + case AnnotationCoordinateSpaceEnum::SURFACE: + break; + case AnnotationCoordinateSpaceEnum::TAB: + break; + case AnnotationCoordinateSpaceEnum::VIEWPORT: + { + int32_t vp[4]; + annotationText.getViewportCoordinateSpaceViewport(vp); + viewportWidth = vp[2]; + viewportHeight = vp[3]; + } + break; + case AnnotationCoordinateSpaceEnum::WINDOW: + break; + } + + if (heightOrWidthForPercentageSizeText > 0.0) { + viewportHeight = heightOrWidthForPercentageSizeText; + viewportWidth = heightOrWidthForPercentageSizeText; + } + + AString fontTypeString; + switch (ftglFontType) { + case FtglFontTypeEnum::POLYGON: + fontTypeString = "Polygon_"; + break; + case FtglFontTypeEnum::TEXTURE: + fontTypeString = "Texture_"; + break; + } + + const AString fontName = (fontTypeString + + annotationText.getFontRenderingEncodedName(viewportWidth, + viewportHeight)); + + /* + * Has the font already has been created? + */ + FONT_MAP_ITERATOR fontIter = m_fontNameToFontMap.find(fontName); + if (fontIter != m_fontNameToFontMap.end()) { + FontData* fontData = fontIter->second; + CaretAssert(fontData); + + /* + * Set font "too small" status + */ + const bool tooSmallFlag = (fontData->m_font->FaceSize() <= AnnotationText::getTooSmallTextHeight()); + annotationText.setFontTooSmallWhenLastDrawn(tooSmallFlag); + + return fontData->m_font; + } + + /* + * Create and save the font + */ + FontData* fontData = new FontData(annotationText, + ftglFontType, + viewportWidth, + viewportHeight); + if (fontData->m_valid) { + /* + * Request font is valid. + */ + m_fontNameToFontMap.insert(std::make_pair(fontName, + fontData)); + CaretLogFine("Created font with encoded name " + + fontName); + + /* + * Set font "too small" status + */ + const bool tooSmallFlag = (fontData->m_font->FaceSize() <= AnnotationText::getTooSmallTextHeight()); + annotationText.setFontTooSmallWhenLastDrawn(tooSmallFlag); + + return fontData->m_font; + } + else { + /* + * Error creating font + */ + delete fontData; + fontData = NULL; + + /* + * Issue a message about failure to create font but + * don't print same message more than once. + */ + if (std::find(m_failedFontNames.begin(), + m_failedFontNames.end(), + fontName) == m_failedFontNames.end()) { + m_failedFontNames.insert(fontName); + CaretLogSevere("Failed to create font with encoded name " + + fontName); + } + } + + /* + * Were we trying to create the default font? + */ + if (creatingDefaultFontFlag) { + return NULL; + } + + /* + * Failed so use the default font. + */ + annotationText.setFontTooSmallWhenLastDrawn(false); + return m_defaultFont; + +#else // HAVE_FREETYPE + CaretLogSevere("Trying to use FTGL Font rendering but FTGL is not valid."); + return NULL; +#endif // HAVE_FREETYPE +} + + +/* + * Get the font with the given font attributes. + * If the font is not created, return the default font. + * + * @param annotationText + * Annotation Text that is to be drawn. + * @param ftglFontType + * Type of font. + * @param creatingDefaultFontFlag + * True if creating the default font. + * @return + * The FTGL font. If there are errors this value will + * be NULL. + */ +FTFont* +FtglFontTextRenderer::getFont(const AnnotationText& annotationText, + const FtglFontTypeEnum ftglFontType, + const bool creatingDefaultFontFlag) +{ + return getFont(annotationText, + ftglFontType, + -1.0, + creatingDefaultFontFlag); +#ifdef HAVE_FREETYPE + int32_t viewportWidth = m_viewportWidth; + int32_t viewportHeight = m_viewportHeight; + switch (annotationText.getCoordinateSpace()) { case AnnotationCoordinateSpaceEnum::CHART: break; @@ -231,6 +388,7 @@ FtglFontTextRenderer::getFont(const AnnotationText& annotationText, * Create and save the font */ FontData* fontData = new FontData(annotationText, + ftglFontType, viewportWidth, viewportHeight); if (fontData->m_valid) { @@ -327,6 +485,7 @@ FtglFontTextRenderer::drawTextAtViewportCoordinatesInternal(const AnnotationText { #ifdef HAVE_FREETYPE FTFont* font = getFont(annotationText, + FtglFontTypeEnum::TEXTURE, false); if (! font) { return; @@ -663,6 +822,41 @@ FtglFontTextRenderer::expandBox(float bottomLeft[3], bottomRight[1] += (widthSpacingY - heightSpacingY); } +/** + * Expand a box by given amounts in X and Y. + * + * @param bottomLeft + * Bottom left corner of annotation. + * @param bottomRight + * Bottom right corner of annotation. + * @param topRight + * Top right corner of annotation. + * @param topLeft + * Top left corner of annotation. + * @param extraSpacePercentX + * Extra space to add in X, percentage is zero to one with one interpreted as 100%. + * @param extraSpacePercentY + * Extra space to add in Y, percentage is zero to one with one interpreted as 100%. + */ +void +FtglFontTextRenderer::expandBoxPercentage(float bottomLeft[3], + float bottomRight[3], + float topRight[3], + float topLeft[3], + const float extraSpacePercentX, + const float extraSpacePercentY) +{ + const float width = MathFunctions::distance3D(bottomLeft, bottomRight); + const float height = MathFunctions::distance3D(bottomLeft, topLeft); + + expandBox(bottomLeft, + bottomRight, + topRight, + topLeft, + width * extraSpacePercentX, + height * extraSpacePercentY); +} + /** * Draw annnotation text at the given viewport coordinates using * the the annotations attributes for the style of text. @@ -752,7 +946,9 @@ FtglFontTextRenderer::drawTextAtViewportCoordsInternal(const DepthTestEnum depth return; } - FTFont* font = getFont(annotationText, false); + FTFont* font = getFont(annotationText, + FtglFontTypeEnum::TEXTURE, + false); if ( ! font) { return; } @@ -826,7 +1022,9 @@ FtglFontTextRenderer::getBoundsForTextAtViewportCoords(const AnnotationText& ann m_viewportWidth = viewportWidth; m_viewportHeight = viewportHeight; - FTFont* font = getFont(annotationText, false); + FTFont* font = getFont(annotationText, + FtglFontTypeEnum::TEXTURE, + false); if ( ! font) { return; } @@ -941,7 +1139,9 @@ FtglFontTextRenderer::getBoundsWithoutMarginForTextAtViewportCoords(const Annota m_viewportWidth = viewportWidth; m_viewportHeight = viewportHeight; - FTFont* font = getFont(annotationText, false); + FTFont* font = getFont(annotationText, + FtglFontTypeEnum::TEXTURE, + false); if ( ! font) { return; } @@ -1140,6 +1340,7 @@ FtglFontTextRenderer::getTextWidthHeightInPixels(const AnnotationText& annotatio /** * Draw annnotation text at the given model coordinates using * the the annotations attributes for the style of text. + * Text is drawn so that is in the plane of the screen (faces user) * * Depth testing is ENABLED when drawing text with this method. * @@ -1151,13 +1352,15 @@ FtglFontTextRenderer::getTextWidthHeightInPixels(const AnnotationText& annotatio * Model Z-coordinate. * @param annotationText * Annotation text and attributes. + * @param flags + * Drawing flags. */ void -FtglFontTextRenderer::drawTextAtModelCoords(const double modelX, - const double modelY, - const double modelZ, - const AnnotationText& annotationText, - const DrawingFlags& flags) +FtglFontTextRenderer::drawTextAtModelCoordsFacingUser(const double modelX, + const double modelY, + const double modelZ, + const AnnotationText& annotationText, + const DrawingFlags& flags) { setViewportHeight(); @@ -1209,6 +1412,394 @@ FtglFontTextRenderer::drawTextAtModelCoords(const double modelX, } } +/** + * Get the bounds of text drawn in model space using the current model transformations. + * + * @param annotationText + * Text that is to be drawn. + * @param heightOrWidthForPercentageSizeText + * Size of region used when converting percentage size to a fixed size + * @param flags + * Drawing flags. + * @param bottomLeftOut + * The bottom left corner of the text bounds. + * @param bottomRightOut + * The bottom right corner of the text bounds. + * @param topRightOut + * The top right corner of the text bounds. + * @param topLeftOut + * The top left corner of the text bounds. + */ +void +FtglFontTextRenderer::getBoundsForTextInModelSpace(const AnnotationText& annotationText, + const float heightOrWidthForPercentageSizeText, + const DrawingFlags& flags, + float bottomLeftOut[3], + float bottomRightOut[3], + float topRightOut[3], + float topLeftOut[3]) +{ + std::fill(bottomLeftOut, bottomLeftOut + 3, 0.0f); + std::fill(bottomRightOut, bottomRightOut + 3, 0.0f); + std::fill(topRightOut, topRightOut + 3, 0.0f); + std::fill(topLeftOut, topLeftOut + 3, 0.0f); + +#ifdef HAVE_FREETYPE + FTFont* font = getFont(annotationText, + FtglFontTypeEnum::POLYGON, + heightOrWidthForPercentageSizeText, + false); + if (! font) { + return; + } + + AString text = (flags.isDrawSubstitutedText() + ? annotationText.getTextWithSubstitutionsApplied() + : annotationText.getText()); + if (text.isEmpty()) { + return; + } + + std::wstring wideString = text.toStdWString(); + const wchar_t* wideCharString = wideString.c_str(); + + FTBBox bounds = font->BBox(wideCharString); + if (bounds.IsValid()) { + FTPoint lb = bounds.Lower(); + FTPoint ub = bounds.Upper(); +// std::cout << "Lower: " << lb.X() << ", " << lb.Y() << ", " << lb.Z() << std::endl; +// std::cout << "Upper: " << ub.X() << ", " << ub.Y() << ", " << ub.Z() << std::endl; + + float dx(0.0); + float dy(0.0f); + switch (annotationText.getHorizontalAlignment()) { + case AnnotationTextAlignHorizontalEnum::CENTER: + dx = -((lb.X() + ub.X()) / 2.0); + break; + case AnnotationTextAlignHorizontalEnum::LEFT: + dx = -lb.X(); + break; + case AnnotationTextAlignHorizontalEnum::RIGHT: + dx = -ub.X(); + break; + } + + switch (annotationText.getVerticalAlignment()) { + case AnnotationTextAlignVerticalEnum::BOTTOM: + dy = -lb.Y(); + break; + case AnnotationTextAlignVerticalEnum::MIDDLE: + dy = -((lb.Y() + ub.Y()) / 2.0); + break; + case AnnotationTextAlignVerticalEnum::TOP: + dy = -ub.Y(); + break; + } + + const float z(0.0f); + bottomLeftOut[0] = dx + lb.Xf(); + bottomLeftOut[1] = dy + lb.Yf(); + bottomLeftOut[2] = z; + bottomRightOut[0] = dx + ub.Xf(); + bottomRightOut[1] = dy + lb.Yf(); + bottomRightOut[2] = z; + topLeftOut[0] = dx + lb.Xf(); + topLeftOut[1] = dy + ub.Yf(); + topLeftOut[2] = z; + topRightOut[0] = dx + ub.Xf(); + topRightOut[1] = dy + ub.Yf(); + topRightOut[2] = z; + + const float percent = 0.05; + expandBoxPercentage(bottomLeftOut, bottomRightOut, topRightOut, topLeftOut, percent, percent); + } +#endif // HAVE_FREETYPE + +} + +/** + * Draw text in model space using the current model transformations. + * + * Depth testing is ENABLED when drawing text with this method. + * + * @param annotationText + * Annotation text and attributes. + * @param heightOrWidthForPercentageSizeText + * If positive, use it to override width/height of viewport. + * @param normalVector + * Normal vector of text. + * @param flags + * Drawing flags. + */ +void +FtglFontTextRenderer::drawTextInModelSpace(const AnnotationText& annotationText, + const float heightOrWidthForPercentageSizeText, + const float normalVector[3], + const DrawingFlags& flags) +{ +#ifdef HAVE_FREETYPE + FTFont* font = getFont(annotationText, + FtglFontTypeEnum::POLYGON, + heightOrWidthForPercentageSizeText, + false); + if (! font) { + return; + } + + const bool drawCrossFlag = false; + if (drawCrossFlag) { + const float a(100.0f); + glLineWidth(3.0); + glBegin(GL_LINES); + glVertex3f(-a, 0, 0); + glVertex3f( a, 0, 0); + glVertex3f(0, -a, 0); + glVertex3f(0, a, 0); + glVertex3f(0, 0,-a); + glVertex3f(0, 0, a); + glEnd(); + } + AString text = (flags.isDrawSubstitutedText() + ? annotationText.getTextWithSubstitutionsApplied() + : annotationText.getText()); + if (text.isEmpty()) { + return; + } + + std::wstring wideString = text.toStdWString(); + const wchar_t* wideCharString = wideString.c_str(); + + FTBBox bounds = font->BBox(wideCharString); + if (bounds.IsValid()) { + FTPoint lb = bounds.Lower(); + FTPoint ub = bounds.Upper(); +// std::cout << "Lower: " << lb.X() << ", " << lb.Y() << ", " << lb.Z() << std::endl; +// std::cout << "Upper: " << ub.X() << ", " << ub.Y() << ", " << ub.Z() << std::endl; + + float dx(0.0); + float dy(0.0f); + switch (annotationText.getHorizontalAlignment()) { + case AnnotationTextAlignHorizontalEnum::CENTER: + dx = -((lb.X() + ub.X()) / 2.0); + break; + case AnnotationTextAlignHorizontalEnum::LEFT: + dx = -lb.X(); + break; + case AnnotationTextAlignHorizontalEnum::RIGHT: + dx = -ub.X(); + break; + } + + switch (annotationText.getVerticalAlignment()) { + case AnnotationTextAlignVerticalEnum::BOTTOM: + dy = -lb.Y(); + break; + case AnnotationTextAlignVerticalEnum::MIDDLE: + dy = -((lb.Y() + ub.Y()) / 2.0); + break; + case AnnotationTextAlignVerticalEnum::TOP: + dy = -ub.Y(); + break; + } + + glPushAttrib(GL_POLYGON_BIT); + + uint8_t backgroundColor[4]; + annotationText.getBackgroundColorRGBA(backgroundColor); + + if (backgroundColor[3] > 0) { + float bottomLeft[3] { 0.0, 0.0, 0.0 }; + float bottomRight[3] { 0.0, 0.0, 0.0 }; + float topLeft[3] { 0.0, 0.0, 0.0 }; + float topRight[3] { 0.0, 0.0, 0.0 }; + + getBoundsForTextInModelSpace(annotationText, heightOrWidthForPercentageSizeText, flags, + bottomLeft, bottomRight, topRight, topLeft); + + GraphicsPrimitiveV3fN3f primitive(GraphicsPrimitive::PrimitiveType::OPENGL_TRIANGLE_STRIP, + backgroundColor); + primitive.addVertex(topLeft, normalVector); + primitive.addVertex(bottomLeft, normalVector); + primitive.addVertex(topRight, normalVector); + primitive.addVertex(bottomRight, normalVector); + GraphicsEngineDataOpenGL::draw(&primitive); + + /* + * So that text and background do not mix together + * in the Z-buffer + */ + glEnable(GL_POLYGON_OFFSET_FILL); + glEnable(GL_POLYGON_OFFSET_LINE); + glEnable(GL_POLYGON_OFFSET_POINT); + glPolygonOffset(-1.0, 1.0); + } + + glTranslatef(dx, dy, 0.0); + + uint8_t textRGBA[4]; + annotationText.getTextColorRGBA(textRGBA); + glColor3ubv(textRGBA); + font->Render(wideCharString); + + annotationText.setFontTooSmallWhenLastDrawn(false); + + glPopAttrib(); + } + + +#endif // HAVE_FREETYPE +} + +///** +// * Draw text in model space using the current model transformations. +// * +// * Depth testing is ENABLED when drawing text with this method. +// * +// * @param annotationText +// * Annotation text and attributes. +// * @param heightOrWidthForPercentageSizeText +// * If positive, use it to override width/height of viewport. +// * @param backgroundOverrideRGBA +// * If alpha is greater that zero, draw background with this color +// * @param flags +// * Drawing flags. +// */ +//void +//FtglFontTextRenderer::drawTextInModelSpace(const AnnotationText& annotationText, +// const float heightOrWidthForPercentageSizeText, +// const uint8_t backgroundOverrideRGBA[4], +// const DrawingFlags& flags) +//{ +//#ifdef HAVE_FREETYPE +// FTFont* font = getFont(annotationText, +// FtglFontTypeEnum::POLYGON, +// heightOrWidthForPercentageSizeText, +// false); +// if (! font) { +// return; +// } +// +// const bool drawCrossFlag = true; +// if (drawCrossFlag) { +// const float a(100.0f); +// glLineWidth(3.0); +// glBegin(GL_LINES); +// glVertex3f(-a, 0, 0); +// glVertex3f( a, 0, 0); +// glVertex3f(0, -a, 0); +// glVertex3f(0, a, 0); +// glVertex3f(0, 0,-a); +// glVertex3f(0, 0, a); +// glEnd(); +// } +// AString text = (flags.isDrawSubstitutedText() +// ? annotationText.getTextWithSubstitutionsApplied() +// : annotationText.getText()); +// if (text.isEmpty()) { +// return; +// } +// +// std::wstring wideString = text.toStdWString(); +// const wchar_t* wideCharString = wideString.c_str(); +// +// FTBBox bounds = font->BBox(wideCharString); +// if (bounds.IsValid()) { +// FTPoint lb = bounds.Lower(); +// FTPoint ub = bounds.Upper(); +// std::cout << "Lower: " << lb.X() << ", " << lb.Y() << ", " << lb.Z() << std::endl; +// std::cout << "Upper: " << ub.X() << ", " << ub.Y() << ", " << ub.Z() << std::endl; +// +// float dx(0.0); +// float dy(0.0f); +// switch (annotationText.getHorizontalAlignment()) { +// case AnnotationTextAlignHorizontalEnum::CENTER: +// dx = -((lb.X() + ub.X()) / 2.0); +// break; +// case AnnotationTextAlignHorizontalEnum::LEFT: +// dx = -lb.X(); +// break; +// case AnnotationTextAlignHorizontalEnum::RIGHT: +// dx = -ub.X(); +// break; +// } +// +// switch (annotationText.getVerticalAlignment()) { +// case AnnotationTextAlignVerticalEnum::BOTTOM: +// dy = -lb.Y(); +// break; +// case AnnotationTextAlignVerticalEnum::MIDDLE: +// dy = -((lb.Y() + ub.Y()) / 2.0); +// break; +// case AnnotationTextAlignVerticalEnum::TOP: +// dy = -ub.Y(); +// break; +// } +// glTranslatef(dx, dy, 0.0); +// +// glPushAttrib(GL_POLYGON_BIT); +// +// uint8_t backgroundColor[4]; +// annotationText.getBackgroundColorRGBA(backgroundColor); +// +// if (backgroundOverrideRGBA[3] > 0) { +// backgroundColor[0] = backgroundOverrideRGBA[0]; +// backgroundColor[1] = backgroundOverrideRGBA[1]; +// backgroundColor[2] = backgroundOverrideRGBA[2]; +// backgroundColor[3] = backgroundOverrideRGBA[3]; +// } +// if (backgroundColor[3] > 0) { +//// const float width = ub.X() - lb.X(); +//// const float height = ub.Y() - lb.Y(); +//// +//// const float x = ub.X(); +//// const float y = ub.Y(); +// const float z(0.0f); +//// const float bottomLeft[3] { 0.0f, 0.0f, z }; +//// const float bottomRight[3] { width, 0.0f, z }; +//// const float topLeft[3] { 0.0f, height, z }; +//// const float topRight[3] { width, height, z }; +// float bottomLeft[3] { lb.Xf(), lb.Yf(), z }; +// float bottomRight[3] { ub.Xf(), lb.Yf(), z }; +// float topLeft[3] { lb.Xf(), ub.Yf(), z }; +// float topRight[3] { ub.Xf(), ub.Yf(), z }; +// +// const float percent = 0.05; +// expandBoxPercentage(bottomLeft, bottomRight, topRight, topLeft, percent, percent); +// +// const float normal[3] { -0.93173,-0.270169,0.242669 }; +// +// GraphicsPrimitiveV3fN3f primitive(GraphicsPrimitive::PrimitiveType::OPENGL_TRIANGLE_STRIP, +// backgroundColor); +// primitive.addVertex(topLeft, normal); +// primitive.addVertex(bottomLeft, normal); +// primitive.addVertex(topRight, normal); +// primitive.addVertex(bottomRight, normal); +// GraphicsEngineDataOpenGL::draw(&primitive); +// +// /* +// * So that text and background do not mix together +// * in the Z-buffer +// */ +// std::cout << "Depth is enabled " << AString::fromBool(glIsEnabled(GL_DEPTH_TEST)) << std::endl; +// glEnable(GL_POLYGON_OFFSET_FILL); +// glEnable(GL_POLYGON_OFFSET_LINE); +// glEnable(GL_POLYGON_OFFSET_POINT); +// glPolygonOffset(-1.0, 1.0); +// } +// +// if (backgroundOverrideRGBA[0] == 0) { +// font->Render(wideCharString); +// } +// +// annotationText.setFontTooSmallWhenLastDrawn(false); +// +// glPopAttrib(); +// } +// +// +//#endif // HAVE_FREETYPE +//} + /** * Save the state of OpenGL. * Copied from Qt's qgl.cpp, qt_save_gl_state(). @@ -1273,8 +1864,10 @@ FtglFontTextRenderer::FontData::FontData() * Height of the viewport in which text is drawn. */ FtglFontTextRenderer::FontData::FontData(const AnnotationText& annotationText, + const FtglFontTypeEnum ftglFontType, const int32_t viewportWidth, const int32_t viewportHeight) +: m_ftglFontType(ftglFontType) { m_valid = false; m_font = NULL; @@ -1312,8 +1905,16 @@ FtglFontTextRenderer::FontData::FontData(const AnnotationText& annotationText, /* * Create the FTGL font. */ - m_font = new FTTextureFont((const unsigned char*)m_fontData.data(), - numBytes); + switch (m_ftglFontType) { + case FtglFontTypeEnum::POLYGON: + m_font = new FTGLPolygonFont((const unsigned char*)m_fontData.data(), + numBytes); + break; + case FtglFontTypeEnum::TEXTURE: + m_font = new FTTextureFont((const unsigned char*)m_fontData.data(), + numBytes); + break; + } CaretAssert(m_font); diff --git a/src/Brain/FtglFontTextRenderer.h b/src/Brain/FtglFontTextRenderer.h index c24a8ed4a..8d439a95d 100644 --- a/src/Brain/FtglFontTextRenderer.h +++ b/src/Brain/FtglFontTextRenderer.h @@ -52,12 +52,17 @@ namespace caret { const AnnotationText& annotationText, const DrawingFlags& flags) override; - virtual void drawTextAtModelCoords(const double modelX, + virtual void drawTextAtModelCoordsFacingUser(const double modelX, const double modelY, const double modelZ, const AnnotationText& annotationText, const DrawingFlags& flags) override; + virtual void drawTextInModelSpace(const AnnotationText& annotationText, + const float heightOrWidthForPercentageSizeText, + const float normalVector[3], + const DrawingFlags& flags) override; + virtual void getTextWidthHeightInPixels(const AnnotationText& annotationText, const DrawingFlags& flags, const double viewportWidth, @@ -66,6 +71,14 @@ namespace caret { double& heightOut) override; + virtual void getBoundsForTextInModelSpace(const AnnotationText& annotationText, + const float heightOrWidthForPercentageSizeText, + const DrawingFlags& flags, + float bottomLeftOut[3], + float bottomRightOut[3], + float topRightOut[3], + float topLeftOut[3]) override; + virtual void getBoundsForTextAtViewportCoords(const AnnotationText& annotationText, const DrawingFlags& flags, const double viewportX, @@ -98,6 +111,11 @@ namespace caret { DEPTH_TEST_YES }; + enum class FtglFontTypeEnum { + POLYGON, + TEXTURE + }; + FtglFontTextRenderer(const FtglFontTextRenderer&); FtglFontTextRenderer& operator=(const FtglFontTextRenderer&); @@ -110,6 +128,12 @@ namespace caret { const DrawingFlags& flags); FTFont* getFont(const AnnotationText& annotationText, + const FtglFontTypeEnum ftglFontType, + const bool creatingDefaultFontFlag); + + FTFont* getFont(const AnnotationText& annotationText, + const FtglFontTypeEnum ftglFontType, + const float heightOrWidthForPercentageSizeText, const bool creatingDefaultFontFlag); void drawUnderline(const double lineStartX, @@ -134,14 +158,21 @@ namespace caret { const float extraSpaceX, const float extraSpaceY); - double getLineWidthFromPercentageHeight(const double percentageHeight) const; + static void expandBoxPercentage(float bottomLeft[3], + float bottomRight[3], + float topRight[3], + float topLeft[3], + const float extraSpacePercentX, + const float extraSpacePercentY); + double getLineWidthFromPercentageHeight(const double percentageHeight) const; class FontData { public: FontData(); FontData(const AnnotationText& annotationText, + const FtglFontTypeEnum ftglFontType, const int32_t viewportWidth, const int32_t viewportHeight); @@ -149,6 +180,8 @@ namespace caret { void initialize(const AString& fontFileName); + FtglFontTypeEnum m_ftglFontType = FtglFontTypeEnum::TEXTURE; + QByteArray m_fontData; FTFont* m_font; diff --git a/src/GuiQt/QGLWidgetTextRenderer.cxx b/src/GuiQt/QGLWidgetTextRenderer.cxx index b1868147c..d773965ff 100644 --- a/src/GuiQt/QGLWidgetTextRenderer.cxx +++ b/src/GuiQt/QGLWidgetTextRenderer.cxx @@ -216,6 +216,7 @@ QGLWidgetTextRenderer::drawTextAtViewportCoords(const double viewportX, /** * Draw annnotation text at the given model coordinates using * the the annotations attributes for the style of text. + * Text is drawn so that is in the plane of the screen (faces user) * * Depth testing is ENABLED when drawing text with this method. * @@ -227,9 +228,11 @@ QGLWidgetTextRenderer::drawTextAtViewportCoords(const double viewportX, * Model Z-coordinate. * @param annotationText * Annotation text and attributes. + * @param flags + * Drawing flags. */ void -QGLWidgetTextRenderer::drawTextAtModelCoords(const double modelX, +QGLWidgetTextRenderer::drawTextAtModelCoordsFacingUser(const double modelX, const double modelY, const double modelZ, const AnnotationText& annotationText, @@ -307,6 +310,29 @@ QGLWidgetTextRenderer::drawTextAtModelCoords(const double modelX, } } +/** + * Draw text in model space using the current model transformations. + * + * Depth testing is ENABLED when drawing text with this method. + * + * @param annotationText + * Annotation text and attributes. + * @param heightOrWidthForPercentageSizeText + * If positive, use it to override width/height of viewport. + * @param normalVector + * Normal vector of text. + * @param flags + * Drawing flags. + */ +void +QGLWidgetTextRenderer::drawTextInModelSpace(const AnnotationText& /* annotationText */, + const float /*heightOrWidthForPercentageSizeText*/, + const float* /*normalVector[3]*/, + const DrawingFlags& /* flags */) +{ + CaretAssertMessage(0, "Not implemented"); +} + /** * Draw horizontal annotation text at the given window coordinates. * @@ -735,6 +761,36 @@ QGLWidgetTextRenderer::getTextWidthHeightInPixels(const AnnotationText& annotati heightOut = yMax - yMin; } +/** + * Get the bounds of text drawn in model space using the current model transformations. + * + * @param annotationText + * Text that is to be drawn. + * @param heightOrWidthForPercentageSizeText + * Size of region used when converting percentage size to a fixed size + * @param flags + * Drawing flags. + * @param bottomLeftOut + * The bottom left corner of the text bounds. + * @param bottomRightOut + * The bottom right corner of the text bounds. + * @param topRightOut + * The top right corner of the text bounds. + * @param topLeftOut + * The top left corner of the text bounds. + */ +void +QGLWidgetTextRenderer::getBoundsForTextInModelSpace(const AnnotationText& /*annotationText*/, + const float /*heightOrWidthForPercentageSizeText*/, + const DrawingFlags& /*flags*/, + float* /*bottomLeftOut[3]*/, + float* /*bottomRightOut[3]*/, + float* /*topRightOut[3]*/, + float* /*topLeftOut[3]*/) +{ + +} + /** * Get the bounds of text (in pixels) using the given text * attributes. diff --git a/src/GuiQt/QGLWidgetTextRenderer.h b/src/GuiQt/QGLWidgetTextRenderer.h index c793075f6..2adbb42e9 100644 --- a/src/GuiQt/QGLWidgetTextRenderer.h +++ b/src/GuiQt/QGLWidgetTextRenderer.h @@ -55,12 +55,17 @@ namespace caret { const AnnotationText& annotationText, const BrainOpenGLTextRenderInterface::DrawingFlags& flags) override; - virtual void drawTextAtModelCoords(const double modelX, + virtual void drawTextAtModelCoordsFacingUser(const double modelX, const double modelY, const double modelZ, const AnnotationText& annotationText, const BrainOpenGLTextRenderInterface::DrawingFlags& flags) override; + virtual void drawTextInModelSpace(const AnnotationText& annotationText, + const float heightOrWidthForPercentageSizeText, + const float normalVector[3], + const DrawingFlags& flags) override; + virtual void getTextWidthHeightInPixels(const AnnotationText& annotationText, const BrainOpenGLTextRenderInterface::DrawingFlags& flags, const double viewportWidth, @@ -68,6 +73,14 @@ namespace caret { double& widthOut, double& heightOut) override; + virtual void getBoundsForTextInModelSpace(const AnnotationText& annotationText, + const float heightOrWidthForPercentageSizeText, + const DrawingFlags& flags, + float bottomLeftOut[3], + float bottomRightOut[3], + float topRightOut[3], + float topLeftOut[3]) override; + virtual void getBoundsForTextAtViewportCoords(const AnnotationText& annotationText, const BrainOpenGLTextRenderInterface::DrawingFlags& flags, const double viewportX, -- GitLab From feb14da6de4bc47fe77ec76287012e3f8104b816 Mon Sep 17 00:00:00 2001 From: John Harwell Date: Fri, 27 Jul 2018 09:45:03 -0500 Subject: [PATCH 010/427] Ensure that contents of Features Toolbox Annot Tab has its children enabled properly (Annotation tab is enabled only when there are annotations and Substitution tab is enabled only when a substitution file is enabled. --- .../BrainBrowserWindowOrientedToolBox.cxx | 22 +++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/src/GuiQt/BrainBrowserWindowOrientedToolBox.cxx b/src/GuiQt/BrainBrowserWindowOrientedToolBox.cxx index 4c0c416c2..9ffe7b335 100644 --- a/src/GuiQt/BrainBrowserWindowOrientedToolBox.cxx +++ b/src/GuiQt/BrainBrowserWindowOrientedToolBox.cxx @@ -202,6 +202,7 @@ BrainBrowserWindowOrientedToolBox::BrainBrowserWindowOrientedToolBox(const int32 m_annotationTabWidget->addTab(m_annotationTextSubstitutionViewController, "Substitutions"); m_annotationTabIndex = addToTabWidget(m_annotationTabWidget, "Annot"); + m_annotationTabWidget->setCurrentIndex(0); } if (isFeaturesToolBox) { m_borderSelectionViewController = new BorderSelectionViewController(browserWindowIndex, @@ -618,6 +619,7 @@ BrainBrowserWindowOrientedToolBox::receiveEvent(Event* event) * Determine types of data this is loaded */ bool haveAnnotation = ( ! brain->getSceneAnnotationFile()->isEmpty()); + bool haveAnnSub = false; bool haveBorders = false; bool haveConnFiles = false; bool haveFibers = false; @@ -640,7 +642,7 @@ BrainBrowserWindowOrientedToolBox::receiveEvent(Event* event) haveAnnotation = true; break; case DataFileTypeEnum::ANNOTATION_TEXT_SUBSTITUTION: - haveAnnotation = true; + haveAnnSub = true; break; case DataFileTypeEnum::BORDER: haveBorders = true; @@ -794,7 +796,8 @@ BrainBrowserWindowOrientedToolBox::receiveEvent(Event* event) if (m_connectivityTabIndex >= 0) m_tabWidget->setTabEnabled(m_connectivityTabIndex, haveConnFiles); if (m_volumeSurfaceOutlineTabIndex >= 0) m_tabWidget->setTabEnabled(m_volumeSurfaceOutlineTabIndex, enableVolumeSurfaceOutline); - if (m_annotationTabIndex >= 0) m_tabWidget->setTabEnabled(m_annotationTabIndex, haveAnnotation); + if (m_annotationTabIndex >= 0) m_tabWidget->setTabEnabled(m_annotationTabIndex, (haveAnnotation + || haveAnnSub)); if (m_borderTabIndex >= 0) m_tabWidget->setTabEnabled(m_borderTabIndex, haveBorders); if (m_fiberOrientationTabIndex >= 0) m_tabWidget->setTabEnabled(m_fiberOrientationTabIndex, haveFibers); if (m_fociTabIndex >= 0) m_tabWidget->setTabEnabled(m_fociTabIndex, haveFoci); @@ -803,6 +806,21 @@ BrainBrowserWindowOrientedToolBox::receiveEvent(Event* event) if (m_overlayTabIndex >= 0) m_tabWidget->setTabEnabled(m_overlayTabIndex, enableLayers); + if (m_annotationTabWidget != NULL) { + const int32_t numTabs = m_annotationTabWidget->count(); + for (int32_t iTab = 0; iTab < numTabs; iTab++) { + if (m_annotationTabWidget->widget(iTab) == m_annotationViewController) { + m_annotationTabWidget->setTabEnabled(iTab, haveAnnotation); + } + else if (m_annotationTabWidget->widget(iTab) == m_annotationTextSubstitutionViewController) { + m_annotationTabWidget->setTabEnabled(iTab, haveAnnSub); + } + else { + CaretAssertMessage(0, "Has new annotation sub tab been added?"); + } + } + } + /* * Switch selected tab if it is not valid */ -- GitLab From 02627152683f93d4e1e29fed897383c3b6e30440 Mon Sep 17 00:00:00 2001 From: John Harwell Date: Fri, 27 Jul 2018 12:45:02 -0500 Subject: [PATCH 011/427] WB-636 Texture Based Annotations: Added selection handles and rotation with mouse to surface annotations drawn in texture offset mode. --- ...inOpenGLAnnotationDrawingFixedPipeline.cxx | 139 ++++++++++++---- src/Brain/FtglFontTextRenderer.cxx | 150 ------------------ 2 files changed, 111 insertions(+), 178 deletions(-) diff --git a/src/Brain/BrainOpenGLAnnotationDrawingFixedPipeline.cxx b/src/Brain/BrainOpenGLAnnotationDrawingFixedPipeline.cxx index f691ad931..732b9394f 100644 --- a/src/Brain/BrainOpenGLAnnotationDrawingFixedPipeline.cxx +++ b/src/Brain/BrainOpenGLAnnotationDrawingFixedPipeline.cxx @@ -2477,10 +2477,37 @@ BrainOpenGLAnnotationDrawingFixedPipeline::drawTextTangentOffset(AnnotationFile* vertexXYZ)); } else { + glPushMatrix(); m_brainOpenGLFixedPipeline->getTextRenderer()->drawTextInModelSpace(*text, surfaceExtentZ, normalXYZ, m_textDrawingFlags); + glPopMatrix(); + } + + if (text->isSelectedForEditing(m_inputs->m_windowIndex)) { + glPushAttrib(GL_POLYGON_BIT + | GL_LIGHTING_BIT); + + /* + * So that text and background do not mix together + * in the Z-buffer + */ + glEnable(GL_POLYGON_OFFSET_FILL); + glEnable(GL_POLYGON_OFFSET_LINE); + glEnable(GL_POLYGON_OFFSET_POINT); + glPolygonOffset(-1.0, 1.0); + glDisable(GL_LIGHTING); + + drawAnnotationTwoDimSizingHandles(annotationFile, + text, + bottomLeft, + bottomRight, + topRight, + topLeft, + s_sizingHandleLineWidthInPixels * 2.0, + text->getRotationAngle()); + glPopAttrib(); } glPopMatrix(); @@ -3446,7 +3473,7 @@ BrainOpenGLAnnotationDrawingFixedPipeline::drawAnnotationTwoDimSizingHandles(Ann { float heightVector[3]; MathFunctions::subtractVectors(topLeft, bottomLeft, heightVector); - MathFunctions::normalizeVector(heightVector); + const float boxHeight = MathFunctions::normalizeVector(heightVector); const float innerSpacing = 2.0f + (lineThickness / 2.0f); float handleTopLeft[3]; @@ -3565,6 +3592,7 @@ BrainOpenGLAnnotationDrawingFixedPipeline::drawAnnotationTwoDimSizingHandles(Ann handleTop[2] }; + bool modelSpaceTangentTextFlag = false; if (annotation->getType() == AnnotationTypeEnum::TEXT) { const AnnotationText* textAnn = dynamic_cast(annotation); CaretAssert(textAnn); @@ -3587,35 +3615,90 @@ BrainOpenGLAnnotationDrawingFixedPipeline::drawAnnotationTwoDimSizingHandles(Ann handleOffset[2] = handleTopRight[2]; break; } + + if (textAnn->getCoordinateSpace() == AnnotationCoordinateSpaceEnum::SURFACE) { + StructureEnum::Enum structure; + int32_t numberOfVertices(-1); + int32_t vertexIndex(-1); + float offsetLength(0.0f); + AnnotationSurfaceOffsetVectorTypeEnum::Enum surfaceOffsetVectorType; + textAnn->getCoordinate()->getSurfaceSpace(structure, + numberOfVertices, + vertexIndex, + offsetLength, + surfaceOffsetVectorType); + + switch (surfaceOffsetVectorType) { + case AnnotationSurfaceOffsetVectorTypeEnum::CENTROID_THRU_VERTEX: + break; + case AnnotationSurfaceOffsetVectorTypeEnum::SURACE_NORMAL: + break; + case AnnotationSurfaceOffsetVectorTypeEnum::TANGENT: + modelSpaceTangentTextFlag = true; + break; + } + } } - const float rotationOffset = sizeHandleSize * 3.0; - const float handleRotation[3] = { - handleOffset[0] + (rotationOffset * heightVector[0]), - handleOffset[1] + (rotationOffset * heightVector[1]), - handleOffset[2] + (rotationOffset * heightVector[2]) - }; - - const float handleRotationLineEnd[3] = { - handleOffset[0] + (rotationOffset * 0.75f * heightVector[0] ), - handleOffset[1] + (rotationOffset * 0.75f * heightVector[1]), - handleOffset[2] + (rotationOffset * 0.75f * heightVector[2]) - }; - - /* - * Rotation handle and line connecting rotation handle to selection box - */ - std::vector coords; - coords.insert(coords.end(), handleRotationLineEnd, handleRotationLineEnd + 3); - coords.insert(coords.end(), handleOffset, handleOffset + 3); - GraphicsShape::drawLinesByteColor(coords, m_selectionBoxRGBA, - GraphicsPrimitive::LineWidthType::PIXELS, 2.0f); - drawSizingHandle(AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_ROTATION, - annotationFile, - annotation, - handleRotation, - sizeHandleSize, - rotationAngle); + if (modelSpaceTangentTextFlag) { + const float rotationHandleSize = boxHeight * 0.30; + const float rotationOffset = rotationHandleSize * 1.5; + const float handleRotation[3] = { + handleOffset[0] + (rotationOffset * heightVector[0]), + handleOffset[1] + (rotationOffset * heightVector[1]), + handleOffset[2] + (rotationOffset * heightVector[2]) + }; + + const float handleRotationLineEnd[3] = { + handleOffset[0] + (rotationHandleSize * heightVector[0] ), + handleOffset[1] + (rotationHandleSize * heightVector[1]), + handleOffset[2] + (rotationHandleSize * heightVector[2]) + }; + + /* + * Rotation handle and line connecting rotation handle to selection box + */ + std::vector coords; + coords.insert(coords.end(), handleRotationLineEnd, handleRotationLineEnd + 3); + coords.insert(coords.end(), handleOffset, handleOffset + 3); + GraphicsShape::drawLinesByteColor(coords, m_selectionBoxRGBA, + GraphicsPrimitive::LineWidthType::PIXELS, 2.0f); + drawSizingHandle(AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_ROTATION, + annotationFile, + annotation, + handleRotation, + rotationHandleSize, + rotationAngle); + } + else { + float rotationOffset = sizeHandleSize * 3.0; + const float handleRotation[3] = { + handleOffset[0] + (rotationOffset * heightVector[0]), + handleOffset[1] + (rotationOffset * heightVector[1]), + handleOffset[2] + (rotationOffset * heightVector[2]) + }; + + const float handleRotationLineEnd[3] = { + handleOffset[0] + (rotationOffset * 0.75f * heightVector[0] ), + handleOffset[1] + (rotationOffset * 0.75f * heightVector[1]), + handleOffset[2] + (rotationOffset * 0.75f * heightVector[2]) + }; + + /* + * Rotation handle and line connecting rotation handle to selection box + */ + std::vector coords; + coords.insert(coords.end(), handleRotationLineEnd, handleRotationLineEnd + 3); + coords.insert(coords.end(), handleOffset, handleOffset + 3); + GraphicsShape::drawLinesByteColor(coords, m_selectionBoxRGBA, + GraphicsPrimitive::LineWidthType::PIXELS, 2.0f); + drawSizingHandle(AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_ROTATION, + annotationFile, + annotation, + handleRotation, + sizeHandleSize, + rotationAngle); + } } } diff --git a/src/Brain/FtglFontTextRenderer.cxx b/src/Brain/FtglFontTextRenderer.cxx index 676f0bc53..4ce85b6d1 100644 --- a/src/Brain/FtglFontTextRenderer.cxx +++ b/src/Brain/FtglFontTextRenderer.cxx @@ -1650,156 +1650,6 @@ FtglFontTextRenderer::drawTextInModelSpace(const AnnotationText& annotationText, #endif // HAVE_FREETYPE } -///** -// * Draw text in model space using the current model transformations. -// * -// * Depth testing is ENABLED when drawing text with this method. -// * -// * @param annotationText -// * Annotation text and attributes. -// * @param heightOrWidthForPercentageSizeText -// * If positive, use it to override width/height of viewport. -// * @param backgroundOverrideRGBA -// * If alpha is greater that zero, draw background with this color -// * @param flags -// * Drawing flags. -// */ -//void -//FtglFontTextRenderer::drawTextInModelSpace(const AnnotationText& annotationText, -// const float heightOrWidthForPercentageSizeText, -// const uint8_t backgroundOverrideRGBA[4], -// const DrawingFlags& flags) -//{ -//#ifdef HAVE_FREETYPE -// FTFont* font = getFont(annotationText, -// FtglFontTypeEnum::POLYGON, -// heightOrWidthForPercentageSizeText, -// false); -// if (! font) { -// return; -// } -// -// const bool drawCrossFlag = true; -// if (drawCrossFlag) { -// const float a(100.0f); -// glLineWidth(3.0); -// glBegin(GL_LINES); -// glVertex3f(-a, 0, 0); -// glVertex3f( a, 0, 0); -// glVertex3f(0, -a, 0); -// glVertex3f(0, a, 0); -// glVertex3f(0, 0,-a); -// glVertex3f(0, 0, a); -// glEnd(); -// } -// AString text = (flags.isDrawSubstitutedText() -// ? annotationText.getTextWithSubstitutionsApplied() -// : annotationText.getText()); -// if (text.isEmpty()) { -// return; -// } -// -// std::wstring wideString = text.toStdWString(); -// const wchar_t* wideCharString = wideString.c_str(); -// -// FTBBox bounds = font->BBox(wideCharString); -// if (bounds.IsValid()) { -// FTPoint lb = bounds.Lower(); -// FTPoint ub = bounds.Upper(); -// std::cout << "Lower: " << lb.X() << ", " << lb.Y() << ", " << lb.Z() << std::endl; -// std::cout << "Upper: " << ub.X() << ", " << ub.Y() << ", " << ub.Z() << std::endl; -// -// float dx(0.0); -// float dy(0.0f); -// switch (annotationText.getHorizontalAlignment()) { -// case AnnotationTextAlignHorizontalEnum::CENTER: -// dx = -((lb.X() + ub.X()) / 2.0); -// break; -// case AnnotationTextAlignHorizontalEnum::LEFT: -// dx = -lb.X(); -// break; -// case AnnotationTextAlignHorizontalEnum::RIGHT: -// dx = -ub.X(); -// break; -// } -// -// switch (annotationText.getVerticalAlignment()) { -// case AnnotationTextAlignVerticalEnum::BOTTOM: -// dy = -lb.Y(); -// break; -// case AnnotationTextAlignVerticalEnum::MIDDLE: -// dy = -((lb.Y() + ub.Y()) / 2.0); -// break; -// case AnnotationTextAlignVerticalEnum::TOP: -// dy = -ub.Y(); -// break; -// } -// glTranslatef(dx, dy, 0.0); -// -// glPushAttrib(GL_POLYGON_BIT); -// -// uint8_t backgroundColor[4]; -// annotationText.getBackgroundColorRGBA(backgroundColor); -// -// if (backgroundOverrideRGBA[3] > 0) { -// backgroundColor[0] = backgroundOverrideRGBA[0]; -// backgroundColor[1] = backgroundOverrideRGBA[1]; -// backgroundColor[2] = backgroundOverrideRGBA[2]; -// backgroundColor[3] = backgroundOverrideRGBA[3]; -// } -// if (backgroundColor[3] > 0) { -//// const float width = ub.X() - lb.X(); -//// const float height = ub.Y() - lb.Y(); -//// -//// const float x = ub.X(); -//// const float y = ub.Y(); -// const float z(0.0f); -//// const float bottomLeft[3] { 0.0f, 0.0f, z }; -//// const float bottomRight[3] { width, 0.0f, z }; -//// const float topLeft[3] { 0.0f, height, z }; -//// const float topRight[3] { width, height, z }; -// float bottomLeft[3] { lb.Xf(), lb.Yf(), z }; -// float bottomRight[3] { ub.Xf(), lb.Yf(), z }; -// float topLeft[3] { lb.Xf(), ub.Yf(), z }; -// float topRight[3] { ub.Xf(), ub.Yf(), z }; -// -// const float percent = 0.05; -// expandBoxPercentage(bottomLeft, bottomRight, topRight, topLeft, percent, percent); -// -// const float normal[3] { -0.93173,-0.270169,0.242669 }; -// -// GraphicsPrimitiveV3fN3f primitive(GraphicsPrimitive::PrimitiveType::OPENGL_TRIANGLE_STRIP, -// backgroundColor); -// primitive.addVertex(topLeft, normal); -// primitive.addVertex(bottomLeft, normal); -// primitive.addVertex(topRight, normal); -// primitive.addVertex(bottomRight, normal); -// GraphicsEngineDataOpenGL::draw(&primitive); -// -// /* -// * So that text and background do not mix together -// * in the Z-buffer -// */ -// std::cout << "Depth is enabled " << AString::fromBool(glIsEnabled(GL_DEPTH_TEST)) << std::endl; -// glEnable(GL_POLYGON_OFFSET_FILL); -// glEnable(GL_POLYGON_OFFSET_LINE); -// glEnable(GL_POLYGON_OFFSET_POINT); -// glPolygonOffset(-1.0, 1.0); -// } -// -// if (backgroundOverrideRGBA[0] == 0) { -// font->Render(wideCharString); -// } -// -// annotationText.setFontTooSmallWhenLastDrawn(false); -// -// glPopAttrib(); -// } -// -// -//#endif // HAVE_FREETYPE -//} - /** * Save the state of OpenGL. * Copied from Qt's qgl.cpp, qt_save_gl_state(). -- GitLab From 577c8db2d81337cc25d307d6d063183e31de4df0 Mon Sep 17 00:00:00 2001 From: John Harwell Date: Fri, 27 Jul 2018 15:38:20 -0500 Subject: [PATCH 012/427] WB-636 Texture Based Annotations: Added underlining to surface text annotations drawn in texture offset mode. --- ...inOpenGLAnnotationDrawingFixedPipeline.cxx | 5 +- src/Brain/BrainOpenGLTextRenderInterface.h | 8 ++- src/Brain/DummyFontTextRenderer.cxx | 8 ++- src/Brain/DummyFontTextRenderer.h | 4 +- src/Brain/FtglFontTextRenderer.cxx | 57 +++++++++++++++---- src/Brain/FtglFontTextRenderer.h | 4 +- src/GuiQt/QGLWidgetTextRenderer.cxx | 18 ++++-- src/GuiQt/QGLWidgetTextRenderer.h | 4 +- 8 files changed, 85 insertions(+), 23 deletions(-) diff --git a/src/Brain/BrainOpenGLAnnotationDrawingFixedPipeline.cxx b/src/Brain/BrainOpenGLAnnotationDrawingFixedPipeline.cxx index 732b9394f..4f109d787 100644 --- a/src/Brain/BrainOpenGLAnnotationDrawingFixedPipeline.cxx +++ b/src/Brain/BrainOpenGLAnnotationDrawingFixedPipeline.cxx @@ -2458,8 +2458,11 @@ BrainOpenGLAnnotationDrawingFixedPipeline::drawTextTangentOffset(AnnotationFile* float bottomRight[3]; float topRight[3]; float topLeft[3]; + float underlineStart[3]; + float underlineEnd[3]; m_brainOpenGLFixedPipeline->getTextRenderer()->getBoundsForTextInModelSpace(*text, surfaceExtentZ, m_textDrawingFlags, - bottomLeft, bottomRight, topRight, topLeft); + bottomLeft, bottomRight, topRight, topLeft, + underlineStart, underlineEnd); uint8_t selectionColorRGBA[4] = { 0, 0, 0, 0 }; if (m_selectionModeFlag) { diff --git a/src/Brain/BrainOpenGLTextRenderInterface.h b/src/Brain/BrainOpenGLTextRenderInterface.h index cf356644a..eb605c337 100644 --- a/src/Brain/BrainOpenGLTextRenderInterface.h +++ b/src/Brain/BrainOpenGLTextRenderInterface.h @@ -186,6 +186,10 @@ namespace caret { * The top right corner of the text bounds. * @param topLeftOut * The top left corner of the text bounds. + * @param underlineStartOut + * Starting coordinate for drawing text underline. + * @param underlineEndOut + * Ending coordinate for drawing text underline. */ virtual void getBoundsForTextInModelSpace(const AnnotationText& annotationText, const float heightOrWidthForPercentageSizeText, @@ -193,7 +197,9 @@ namespace caret { float bottomLeftOut[3], float bottomRightOut[3], float topRightOut[3], - float topLeftOut[3]) = 0; + float topLeftOut[3], + float underlineStartOut[3], + float underlineEndOut[3]) = 0; /** * Draw text in model space using the current model transformations. * diff --git a/src/Brain/DummyFontTextRenderer.cxx b/src/Brain/DummyFontTextRenderer.cxx index 1b750a4e7..a16b626af 100644 --- a/src/Brain/DummyFontTextRenderer.cxx +++ b/src/Brain/DummyFontTextRenderer.cxx @@ -181,6 +181,10 @@ DummyFontTextRenderer::drawTextInModelSpace(const AnnotationText& /* annotationT * The top right corner of the text bounds. * @param topLeftOut * The top left corner of the text bounds. + * @param underlineStartOut + * Starting coordinate for drawing text underline. + * @param underlineEndOut + * Ending coordinate for drawing text underline. */ void DummyFontTextRenderer::getBoundsForTextInModelSpace(const AnnotationText& /*annotationText*/, @@ -189,7 +193,9 @@ DummyFontTextRenderer::getBoundsForTextInModelSpace(const AnnotationText& /*anno float* /*bottomLeftOut[3]*/, float* /*bottomRightOut[3]*/, float* /*topRightOut[3]*/, - float* /*topLeftOut[3]*/) + float* /*topLeftOut[3]*/, + float* /*underlineStartOut[3]*/, + float* /*underlineEndOut[3]*/) { } diff --git a/src/Brain/DummyFontTextRenderer.h b/src/Brain/DummyFontTextRenderer.h index 7a2852671..a667f5b24 100644 --- a/src/Brain/DummyFontTextRenderer.h +++ b/src/Brain/DummyFontTextRenderer.h @@ -72,7 +72,9 @@ namespace caret { float bottomLeftOut[3], float bottomRightOut[3], float topRightOut[3], - float topLeftOut[3]) override; + float topLeftOut[3], + float underlineStartOut[3], + float underlineEndOut[3]) override; virtual void getBoundsForTextAtViewportCoords(const AnnotationText& annotationText, const DrawingFlags& flags, diff --git a/src/Brain/FtglFontTextRenderer.cxx b/src/Brain/FtglFontTextRenderer.cxx index 4ce85b6d1..64135c924 100644 --- a/src/Brain/FtglFontTextRenderer.cxx +++ b/src/Brain/FtglFontTextRenderer.cxx @@ -82,6 +82,7 @@ #include "CaretOpenGLInclude.h" #include "GraphicsEngineDataOpenGL.h" #include "GraphicsOpenGLError.h" +#include "GraphicsPrimitiveV3f.h" #include "GraphicsPrimitiveV3fN3f.h" #include "GraphicsShape.h" #include "GraphicsUtilitiesOpenGL.h" @@ -1429,6 +1430,10 @@ FtglFontTextRenderer::drawTextAtModelCoordsFacingUser(const double modelX, * The top right corner of the text bounds. * @param topLeftOut * The top left corner of the text bounds. + * @param underlineStartOut + * Starting coordinate for drawing text underline. + * @param underlineEndOut + * Ending coordinate for drawing text underline. */ void FtglFontTextRenderer::getBoundsForTextInModelSpace(const AnnotationText& annotationText, @@ -1437,7 +1442,9 @@ FtglFontTextRenderer::getBoundsForTextInModelSpace(const AnnotationText& annotat float bottomLeftOut[3], float bottomRightOut[3], float topRightOut[3], - float topLeftOut[3]) + float topLeftOut[3], + float underlineStartOut[3], + float underlineEndOut[3]) { std::fill(bottomLeftOut, bottomLeftOut + 3, 0.0f); std::fill(bottomRightOut, bottomRightOut + 3, 0.0f); @@ -1510,8 +1517,18 @@ FtglFontTextRenderer::getBoundsForTextInModelSpace(const AnnotationText& annotat topRightOut[1] = dy + ub.Yf(); topRightOut[2] = z; + underlineStartOut[0] = bottomLeftOut[0]; + underlineStartOut[1] = bottomLeftOut[1]; + underlineStartOut[2] = bottomLeftOut[2]; + underlineEndOut[0] = bottomRightOut[0]; + underlineEndOut[1] = bottomRightOut[1]; + underlineEndOut[2] = bottomRightOut[2]; + const float percent = 0.05; expandBoxPercentage(bottomLeftOut, bottomRightOut, topRightOut, topLeftOut, percent, percent); + + underlineStartOut[1] = bottomLeftOut[1]; + underlineEndOut[1] = bottomRightOut[1]; } #endif // HAVE_FREETYPE @@ -1604,17 +1621,20 @@ FtglFontTextRenderer::drawTextInModelSpace(const AnnotationText& annotationText, glPushAttrib(GL_POLYGON_BIT); + float bottomLeft[3] { 0.0, 0.0, 0.0 }; + float bottomRight[3] { 0.0, 0.0, 0.0 }; + float topLeft[3] { 0.0, 0.0, 0.0 }; + float topRight[3] { 0.0, 0.0, 0.0 }; + float underlineStart[3] { 0.0, 0.0, 0.0 }; + float underlineEnd[3] { 0.0, 0.0, 0.0 }; + + getBoundsForTextInModelSpace(annotationText, heightOrWidthForPercentageSizeText, flags, + bottomLeft, bottomRight, topRight, topLeft, + underlineStart, underlineEnd); + uint8_t backgroundColor[4]; annotationText.getBackgroundColorRGBA(backgroundColor); - if (backgroundColor[3] > 0) { - float bottomLeft[3] { 0.0, 0.0, 0.0 }; - float bottomRight[3] { 0.0, 0.0, 0.0 }; - float topLeft[3] { 0.0, 0.0, 0.0 }; - float topRight[3] { 0.0, 0.0, 0.0 }; - - getBoundsForTextInModelSpace(annotationText, heightOrWidthForPercentageSizeText, flags, - bottomLeft, bottomRight, topRight, topLeft); GraphicsPrimitiveV3fN3f primitive(GraphicsPrimitive::PrimitiveType::OPENGL_TRIANGLE_STRIP, backgroundColor); @@ -1634,10 +1654,25 @@ FtglFontTextRenderer::drawTextInModelSpace(const AnnotationText& annotationText, glPolygonOffset(-1.0, 1.0); } - glTranslatef(dx, dy, 0.0); - uint8_t textRGBA[4]; annotationText.getTextColorRGBA(textRGBA); + + if (annotationText.isUnderlineStyleEnabled()) { + const float lineThicknessPercentage(1.0f); + float lineThickness = (heightOrWidthForPercentageSizeText + * (lineThicknessPercentage / 100.0)); + + GraphicsPrimitiveV3f linePrimitive(GraphicsPrimitive::PrimitiveType::POLYGONAL_LINES, + textRGBA); + linePrimitive.setLineWidth(GraphicsPrimitiveV3f::LineWidthType::PIXELS, + lineThickness); + linePrimitive.addVertex(underlineStart); + linePrimitive.addVertex(underlineEnd); + GraphicsEngineDataOpenGL::draw(&linePrimitive); + } + + glTranslatef(dx, dy, 0.0); + glColor3ubv(textRGBA); font->Render(wideCharString); diff --git a/src/Brain/FtglFontTextRenderer.h b/src/Brain/FtglFontTextRenderer.h index 8d439a95d..324b0f0c3 100644 --- a/src/Brain/FtglFontTextRenderer.h +++ b/src/Brain/FtglFontTextRenderer.h @@ -77,7 +77,9 @@ namespace caret { float bottomLeftOut[3], float bottomRightOut[3], float topRightOut[3], - float topLeftOut[3]) override; + float topLeftOut[3], + float underlineStartOut[3], + float underlineEndOut[3]) override; virtual void getBoundsForTextAtViewportCoords(const AnnotationText& annotationText, const DrawingFlags& flags, diff --git a/src/GuiQt/QGLWidgetTextRenderer.cxx b/src/GuiQt/QGLWidgetTextRenderer.cxx index d773965ff..b1d3ac9bb 100644 --- a/src/GuiQt/QGLWidgetTextRenderer.cxx +++ b/src/GuiQt/QGLWidgetTextRenderer.cxx @@ -778,15 +778,21 @@ QGLWidgetTextRenderer::getTextWidthHeightInPixels(const AnnotationText& annotati * The top right corner of the text bounds. * @param topLeftOut * The top left corner of the text bounds. + * @param underlineStartOut + * Starting coordinate for drawing text underline. + * @param underlineEndOut + * Ending coordinate for drawing text underline. */ void QGLWidgetTextRenderer::getBoundsForTextInModelSpace(const AnnotationText& /*annotationText*/, - const float /*heightOrWidthForPercentageSizeText*/, - const DrawingFlags& /*flags*/, - float* /*bottomLeftOut[3]*/, - float* /*bottomRightOut[3]*/, - float* /*topRightOut[3]*/, - float* /*topLeftOut[3]*/) + const float /*heightOrWidthForPercentageSizeText*/, + const DrawingFlags& /*flags*/, + float* /*bottomLeftOut[3]*/, + float* /*bottomRightOut[3]*/, + float* /*topRightOut[3]*/, + float* /*topLeftOut[3]*/, + float* /*underlineStartOut[3]*/, + float* /*underlineEndOut[3]*/) { } diff --git a/src/GuiQt/QGLWidgetTextRenderer.h b/src/GuiQt/QGLWidgetTextRenderer.h index 2adbb42e9..0c892b0a3 100644 --- a/src/GuiQt/QGLWidgetTextRenderer.h +++ b/src/GuiQt/QGLWidgetTextRenderer.h @@ -79,7 +79,9 @@ namespace caret { float bottomLeftOut[3], float bottomRightOut[3], float topRightOut[3], - float topLeftOut[3]) override; + float topLeftOut[3], + float underlineStartOut[3], + float underlineEndOut[3]) override; virtual void getBoundsForTextAtViewportCoords(const AnnotationText& annotationText, const BrainOpenGLTextRenderInterface::DrawingFlags& flags, -- GitLab From 9dc5813c9b99da3299388f8d16558d7beaceeb1e Mon Sep 17 00:00:00 2001 From: John Harwell Date: Fri, 10 Aug 2018 16:48:22 -0500 Subject: [PATCH 013/427] WB-636 Texture Based Annotations: Improvements with default orientation of surface texture annotations. --- ...inOpenGLAnnotationDrawingFixedPipeline.cxx | 378 +++++++++++++----- 1 file changed, 268 insertions(+), 110 deletions(-) diff --git a/src/Brain/BrainOpenGLAnnotationDrawingFixedPipeline.cxx b/src/Brain/BrainOpenGLAnnotationDrawingFixedPipeline.cxx index 4f109d787..a44337078 100644 --- a/src/Brain/BrainOpenGLAnnotationDrawingFixedPipeline.cxx +++ b/src/Brain/BrainOpenGLAnnotationDrawingFixedPipeline.cxx @@ -66,6 +66,7 @@ #include "SelectionManager.h" #include "SelectionItemAnnotation.h" #include "Surface.h" +#include "TopologyHelper.h" using namespace caret; @@ -2380,6 +2381,62 @@ BrainOpenGLAnnotationDrawingFixedPipeline::clipLineAtTextBox(const float bottomL } } +double +angleInDegreesBetweenVectors(const float u[3], const float v[3]) +{ + double angle = 0.0; + + const double numerator = MathFunctions::dotProduct(u, v); + const double uLength = MathFunctions::vectorLength(u); + const double vLength = MathFunctions::vectorLength(v); + const double denominator = uLength * vLength; + if (denominator > 0.0) { + double a = numerator / denominator; + if (a > 1.0) { + a = 1.0; + } + else if (a < -1.0) { + a = -1.0; + } + const double angleRadians = std::acos(a); + angle = MathFunctions::toDegrees(angleRadians); + } + return angle; +} + +void +getSurfaceNormalVector(const Surface* surfaceDisplayed, + const int32_t vertexIndex, + float normalVectorOut[3]) +{ + const float* normalXYZ = surfaceDisplayed->getNormalVector(vertexIndex); + normalVectorOut[0] = normalXYZ[0]; + normalVectorOut[1] = normalXYZ[1]; + normalVectorOut[2] = normalXYZ[2]; +// std::cout << "Normal Vector: " << AString::fromNumbers(normalVectorOut, 3, ", ") << std::endl; + + CaretPointer th = surfaceDisplayed->getTopologyHelper(); + int32_t numNeighbors(0); + const int32_t* neighbors = th->getNodeNeighbors(vertexIndex, numNeighbors); + + normalVectorOut[0] = 0.0; + normalVectorOut[1] = 0.0; + normalVectorOut[2] = 0.0; + for (int32_t n = 0; n < numNeighbors; n++) { + const float* normalXYZ = surfaceDisplayed->getNormalVector(neighbors[n]); + normalVectorOut[0] += normalXYZ[0]; + normalVectorOut[1] += normalXYZ[1]; + normalVectorOut[2] += normalXYZ[2]; + } + + if (numNeighbors > 0) { + normalVectorOut[0] /= numNeighbors; + normalVectorOut[1] /= numNeighbors; + normalVectorOut[2] /= numNeighbors; + } +// std::cout << " Average Normal Vector: " << AString::fromNumbers(normalVectorOut, 3, ", ") << std::endl; +} + /** * Draw an annotation text with tangent offset * @@ -2409,127 +2466,228 @@ BrainOpenGLAnnotationDrawingFixedPipeline::drawTextTangentOffset(AnnotationFile* AnnotationSurfaceOffsetVectorTypeEnum::Enum offsetVectorType; coord->getSurfaceSpace(structure, surfaceNumberOfNodes, vertexIndex, offsetLength, offsetVectorType); - if ((structure == surfaceDisplayed->getStructure()) - && (surfaceDisplayed->getNumberOfNodes() == surfaceNumberOfNodes)) { - float vertexXYZ[3]; - surfaceDisplayed->getCoordinate(vertexIndex, - vertexXYZ); - const float* normalXYZ = surfaceDisplayed->getNormalVector(vertexIndex); - const BoundingBox* boundingBox = surfaceDisplayed->getBoundingBox(); - const float surfaceExtentZ = boundingBox->getDifferenceZ(); - + if (structure != surfaceDisplayed->getStructure()) { + return false; + } + if (surfaceDisplayed->getNumberOfNodes() != surfaceNumberOfNodes) { + return false; + } + float vertexXYZ[3]; + surfaceDisplayed->getCoordinate(vertexIndex, + vertexXYZ); + float normalXYZ[3]; + getSurfaceNormalVector(surfaceDisplayed, vertexIndex, normalXYZ); + const BoundingBox* boundingBox = surfaceDisplayed->getBoundingBox(); + const float surfaceExtentZ = boundingBox->getDifferenceZ(); + + /* + * Need to restore model space + * Recall that all other annotation spaces are drawn in window space + */ + glMatrixMode(GL_PROJECTION); + glPushMatrix(); + glLoadMatrixd(m_modelSpaceProjectionMatrix); + + glMatrixMode(GL_MODELVIEW); + glPushMatrix(); + glLoadMatrixd(m_modelSpaceModelMatrix); + int32_t savedViewport[4]; + glGetIntegerv(GL_VIEWPORT, + savedViewport); + glViewport(m_modelSpaceViewport[0], + m_modelSpaceViewport[1], + m_modelSpaceViewport[2], + m_modelSpaceViewport[3]); + + glPushMatrix(); + + /* + * Matrix that rotates text to plane of vertex's normal vector + */ + Matrix4x4 rotationMatrix; + rotationMatrix.setMatrixToOpenGLRotationFromVector(normalXYZ); + double rotationArray[16]; + rotationMatrix.getMatrixForOpenGL(rotationArray); + + + /* + * Translate to the vertex and then translate using offset + * vector of text. + */ + const float offsetVectorXYZ[3] { + normalXYZ[0] * offsetLength, + normalXYZ[1] * offsetLength, + normalXYZ[2] * offsetLength + }; + glTranslatef(vertexXYZ[0], vertexXYZ[1], vertexXYZ[2]); + glTranslatef(offsetVectorXYZ[0], offsetVectorXYZ[1], offsetVectorXYZ[2]); + + /* + * Rotate into plane of surface normal vector + */ + glMultMatrixd(rotationArray); + + /* + * Up vector in local coordinates (text drawing plane) + * Z points out of text + */ + const float len(25.0f); + const float localUpXYZ[3] { + 0.0, + len, + 0.0 + }; + + /* + * Inverse matrix goes back to surface coordinate system + */ + Matrix4x4 inverseMatrix(rotationMatrix); + inverseMatrix.invert(); + + /* + * Create the plane in which text is drawn + * (plane is not used for drawing text but is used + * to orient the text). The plane is constructed + * from the vertex's normal vector and the the + * vertex's XYZ-coordinate. + */ + Plane textDrawingPlane(normalXYZ, + vertexXYZ); + + /* + * Project a point with a large Z-coordinate to the plane + * and use it to creat the 'text up orientation vector'. + */ + const float zBig[3] { 0.0, 0.0, 10000.0 }; + float textUpOrientationVectorXYZ[3]; + textDrawingPlane.projectPointToPlane(zBig, textUpOrientationVectorXYZ); + inverseMatrix.multiplyPoint3(textUpOrientationVectorXYZ); + + if (debugFlag) { /* - * Need to restore model space - * Recall that all other annotation spaces are drawn in window space + * Red line is normal vector + * Green line is "text up" vector + * Blue points to surface Z-vector */ - glMatrixMode(GL_PROJECTION); - glPushMatrix(); - glLoadMatrixd(m_modelSpaceProjectionMatrix); - - glMatrixMode(GL_MODELVIEW); + const float startXYZ[3] { + 0.0, + 0.0, + 0.0 + }; + const float endXYZ[3] { + 0.0, + 0.0, + len + }; + glBegin(GL_LINES); + glColor3f(1.0, 0.0, 0.0); + glVertex3fv(startXYZ); + glVertex3fv(endXYZ); + glColor3f(0.0, 1.0, 0.0); + glVertex3fv(startXYZ); + glVertex3fv(localUpXYZ); + glColor3f(0.0, 0.0, 1.0); + glVertex3fv(startXYZ); + glVertex3fv(textUpOrientationVectorXYZ); + glEnd(); + } + + /* + * Rotate the text so that the horzontal flow of the text + * is orthogonal to the 'text up orientation vector'. + */ + const float angle = angleInDegreesBetweenVectors(localUpXYZ, + textUpOrientationVectorXYZ); + glRotatef(-angle, 0.0, 0.0, 1.0); + + if (debugFlag) { + std::cout << "Annotation: " << text->getText() << std::endl; + std::cout << " Plane: " << textDrawingPlane.toString() << std::endl; + std::cout << " Local Up Vector: " << AString::fromNumbers(localUpXYZ, 3, ", ") << std::endl; + std::cout << " Angle: " << angle << std::endl; + } + + /* + * Apply user rotation of text + */ + const float rotationAngle = text->getRotationAngle(); + glRotatef(-rotationAngle, 0.0, 0.0, 1.0); + + float bottomLeft[3]; + float bottomRight[3]; + float topRight[3]; + float topLeft[3]; + float underlineStart[3]; + float underlineEnd[3]; + m_brainOpenGLFixedPipeline->getTextRenderer()->getBoundsForTextInModelSpace(*text, surfaceExtentZ, m_textDrawingFlags, + bottomLeft, bottomRight, topRight, topLeft, + underlineStart, underlineEnd); + + bool textDrawnFlag = false; + if (m_selectionModeFlag) { + uint8_t selectionColorRGBA[4] = { 0, 0, 0, 0 }; + getIdentificationColor(selectionColorRGBA); + GraphicsPrimitiveV3fN3f primitive(GraphicsPrimitive::PrimitiveType::OPENGL_TRIANGLE_STRIP, + selectionColorRGBA); + primitive.addVertex(topLeft, normalXYZ); + primitive.addVertex(bottomLeft, normalXYZ); + primitive.addVertex(topRight, normalXYZ); + primitive.addVertex(bottomRight, normalXYZ); + GraphicsEngineDataOpenGL::draw(&primitive); + m_selectionInfo.push_back(SelectionInfo(annotationFile, + text, + AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_NONE, + vertexXYZ)); + } + else { glPushMatrix(); - glLoadMatrixd(m_modelSpaceModelMatrix); - int32_t savedViewport[4]; - glGetIntegerv(GL_VIEWPORT, - savedViewport); - glViewport(m_modelSpaceViewport[0], - m_modelSpaceViewport[1], - m_modelSpaceViewport[2], - m_modelSpaceViewport[3]); + m_brainOpenGLFixedPipeline->getTextRenderer()->drawTextInModelSpace(*text, + surfaceExtentZ, + normalXYZ, + m_textDrawingFlags); + glPopMatrix(); - glPushMatrix(); + textDrawnFlag = true; + } + + if (text->isSelectedForEditing(m_inputs->m_windowIndex)) { + glPushAttrib(GL_POLYGON_BIT + | GL_LIGHTING_BIT); /* - * Matrix place text in plane of vertex's normal vector + * So that text and background do not mix together + * in the Z-buffer */ - Matrix4x4 rotationMatrix; - rotationMatrix.setMatrixToOpenGLRotationFromVector(normalXYZ); - double rotationArray[16]; - rotationMatrix.getMatrixForOpenGL(rotationArray); - - const float rotationAngle = text->getRotationAngle(); - - glPushMatrix(); - glTranslatef(vertexXYZ[0], vertexXYZ[1], vertexXYZ[2]); - glMultMatrixd(rotationArray); - glRotatef(rotationAngle, 0.0, 0.0, 1.0); - - float bottomLeft[3]; - float bottomRight[3]; - float topRight[3]; - float topLeft[3]; - float underlineStart[3]; - float underlineEnd[3]; - m_brainOpenGLFixedPipeline->getTextRenderer()->getBoundsForTextInModelSpace(*text, surfaceExtentZ, m_textDrawingFlags, - bottomLeft, bottomRight, topRight, topLeft, - underlineStart, underlineEnd); - - uint8_t selectionColorRGBA[4] = { 0, 0, 0, 0 }; - if (m_selectionModeFlag) { - getIdentificationColor(selectionColorRGBA); - GraphicsPrimitiveV3fN3f primitive(GraphicsPrimitive::PrimitiveType::OPENGL_TRIANGLE_STRIP, - selectionColorRGBA); - primitive.addVertex(topLeft, normalXYZ); - primitive.addVertex(bottomLeft, normalXYZ); - primitive.addVertex(topRight, normalXYZ); - primitive.addVertex(bottomRight, normalXYZ); - GraphicsEngineDataOpenGL::draw(&primitive); - m_selectionInfo.push_back(SelectionInfo(annotationFile, - text, - AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_NONE, - vertexXYZ)); - } - else { - glPushMatrix(); - m_brainOpenGLFixedPipeline->getTextRenderer()->drawTextInModelSpace(*text, - surfaceExtentZ, - normalXYZ, - m_textDrawingFlags); - glPopMatrix(); - } - - if (text->isSelectedForEditing(m_inputs->m_windowIndex)) { - glPushAttrib(GL_POLYGON_BIT - | GL_LIGHTING_BIT); - - /* - * So that text and background do not mix together - * in the Z-buffer - */ - glEnable(GL_POLYGON_OFFSET_FILL); - glEnable(GL_POLYGON_OFFSET_LINE); - glEnable(GL_POLYGON_OFFSET_POINT); - glPolygonOffset(-1.0, 1.0); - glDisable(GL_LIGHTING); - - drawAnnotationTwoDimSizingHandles(annotationFile, - text, - bottomLeft, - bottomRight, - topRight, - topLeft, - s_sizingHandleLineWidthInPixels * 2.0, - text->getRotationAngle()); - glPopAttrib(); - } - - glPopMatrix(); - - glPopMatrix(); /* restore MODELVIEW */ - - glViewport(savedViewport[0], - savedViewport[1], - savedViewport[2], - savedViewport[3]); - glPopMatrix(); + glEnable(GL_POLYGON_OFFSET_FILL); + glEnable(GL_POLYGON_OFFSET_LINE); + glEnable(GL_POLYGON_OFFSET_POINT); + glPolygonOffset(-1.0, 1.0); + glDisable(GL_LIGHTING); - glMatrixMode(GL_PROJECTION); - glPopMatrix(); - glMatrixMode(GL_MODELVIEW); + drawAnnotationTwoDimSizingHandles(annotationFile, + text, + bottomLeft, + bottomRight, + topRight, + topLeft, + s_sizingHandleLineWidthInPixels * 2.0, + text->getRotationAngle()); + glPopAttrib(); } + glPopMatrix(); /* restore MODELVIEW */ + + glViewport(savedViewport[0], + savedViewport[1], + savedViewport[2], + savedViewport[3]); + glPopMatrix(); - return false; + glMatrixMode(GL_PROJECTION); + glPopMatrix(); + glMatrixMode(GL_MODELVIEW); + + return textDrawnFlag; } /** -- GitLab From 3f61d2252e12d3b48ffa9b1bdc936be5d6c1a24f Mon Sep 17 00:00:00 2001 From: John Harwell Date: Mon, 13 Aug 2018 10:40:22 -0500 Subject: [PATCH 014/427] Added some missing annotation defaults for surface space annotations (surface offset type, surface offset length, and connect to brainordinate type). These defaults are used for newly created annotations and are the defaults are set to the most recent selection in the toolbar (from last selected annotation). --- src/Annotations/AnnotationCoordinate.cxx | 46 +++++++++++++++++-- src/Annotations/AnnotationCoordinate.h | 15 +++++- .../AnnotationOneDimensionalShape.cxx | 4 +- .../AnnotationTwoDimensionalShape.cxx | 2 +- src/GuiQt/AnnotationCoordinateWidget.cxx | 37 ++++++++++++++- src/GuiQt/AnnotationCoordinateWidget.h | 4 ++ src/GuiQt/AnnotationTextEditorWidget.cxx | 2 + 7 files changed, 98 insertions(+), 12 deletions(-) diff --git a/src/Annotations/AnnotationCoordinate.cxx b/src/Annotations/AnnotationCoordinate.cxx index 2b5bd4b05..ecbd7c684 100644 --- a/src/Annotations/AnnotationCoordinate.cxx +++ b/src/Annotations/AnnotationCoordinate.cxx @@ -41,8 +41,10 @@ using namespace caret; /** * Constructor. */ -AnnotationCoordinate::AnnotationCoordinate() -: CaretObjectTracksModification() +AnnotationCoordinate::AnnotationCoordinate(const AnnotationAttributesDefaultTypeEnum::Enum attributeDefaultType) +: CaretObjectTracksModification(), +SceneableInterface(), +m_attributeDefaultType(attributeDefaultType) { initializeAnnotationCoordinateMembers(); @@ -63,7 +65,8 @@ AnnotationCoordinate::~AnnotationCoordinate() */ AnnotationCoordinate::AnnotationCoordinate(const AnnotationCoordinate& obj) : CaretObjectTracksModification(obj), -SceneableInterface(obj) +SceneableInterface(obj), +m_attributeDefaultType(obj.m_attributeDefaultType) { initializeAnnotationCoordinateMembers(); this->copyHelperAnnotationCoordinate(obj); @@ -118,8 +121,17 @@ AnnotationCoordinate::initializeAnnotationCoordinateMembers() m_surfaceSpaceStructure = StructureEnum::INVALID; m_surfaceSpaceNumberOfNodes = -1; m_surfaceSpaceNodeIndex = -1; - m_surfaceOffsetLength = getDefaultSurfaceOffsetLength(); - m_surfaceOffsetVectorType = AnnotationSurfaceOffsetVectorTypeEnum::CENTROID_THRU_VERTEX; + + switch (m_attributeDefaultType) { + case AnnotationAttributesDefaultTypeEnum::NORMAL: + m_surfaceOffsetLength = getDefaultSurfaceOffsetLength(); + m_surfaceOffsetVectorType = AnnotationSurfaceOffsetVectorTypeEnum::CENTROID_THRU_VERTEX; + break; + case AnnotationAttributesDefaultTypeEnum::USER: + m_surfaceOffsetLength = s_userDefaultSurfaceOffsetLength; + m_surfaceOffsetVectorType = s_userDefaultSurfaceOffsetVectorType; + break; + } m_sceneAssistant = new SceneClassAssistant(); m_sceneAssistant->addArray("m_xyz", @@ -453,6 +465,30 @@ AnnotationCoordinate::toString() const return "AnnotationCoordinate"; } +/** + * Set the user default for the surface offset vector type. + * + * @param surfaceOffsetVectorType + * new default value. + */ +void +AnnotationCoordinate::setUserDefautlSurfaceOffsetVectorType(const AnnotationSurfaceOffsetVectorTypeEnum::Enum surfaceOffsetVectorType) +{ + s_userDefaultSurfaceOffsetVectorType = surfaceOffsetVectorType; +} + +/** + * Set the user default for the surface offset length. + * + * @param surfaceOffsetLength + * New default value. + */ +void +AnnotationCoordinate::setUserDefaultSurfaceOffsetLength(const float surfaceOffsetLength) +{ + s_userDefaultSurfaceOffsetLength = surfaceOffsetLength; +} + /** * Save information specific to this type of model to the scene. * diff --git a/src/Annotations/AnnotationCoordinate.h b/src/Annotations/AnnotationCoordinate.h index 1234146f7..51a4a9702 100644 --- a/src/Annotations/AnnotationCoordinate.h +++ b/src/Annotations/AnnotationCoordinate.h @@ -21,6 +21,7 @@ */ /*LICENSE_END*/ +#include "AnnotationAttributesDefaultTypeEnum.h" #include "AnnotationSurfaceOffsetVectorTypeEnum.h" #include "CaretObjectTracksModification.h" #include "SceneableInterface.h" @@ -33,7 +34,7 @@ namespace caret { class AnnotationCoordinate : public CaretObjectTracksModification, public SceneableInterface { public: - AnnotationCoordinate(); + AnnotationCoordinate(const AnnotationAttributesDefaultTypeEnum::Enum attributeDefaultType); virtual ~AnnotationCoordinate(); @@ -106,7 +107,9 @@ namespace caret { static float getDefaultSurfaceOffsetLength(); + static void setUserDefautlSurfaceOffsetVectorType(const AnnotationSurfaceOffsetVectorTypeEnum::Enum surfaceOffsetVectorType); + static void setUserDefaultSurfaceOffsetLength(const float surfaceOffsetLength); // If there will be sub-classes of this class that need to save @@ -124,6 +127,8 @@ namespace caret { void initializeAnnotationCoordinateMembers(); + const AnnotationAttributesDefaultTypeEnum::Enum m_attributeDefaultType; + SceneClassAssistant* m_sceneAssistant; float m_xyz[3]; @@ -138,12 +143,18 @@ namespace caret { AnnotationSurfaceOffsetVectorTypeEnum::Enum m_surfaceOffsetVectorType; + + static float s_userDefaultSurfaceOffsetLength; + + static AnnotationSurfaceOffsetVectorTypeEnum::Enum s_userDefaultSurfaceOffsetVectorType; + // ADD_NEW_MEMBERS_HERE }; #ifdef __ANNOTATION_COORDINATE_DECLARE__ - // + float AnnotationCoordinate::s_userDefaultSurfaceOffsetLength = 5.0f; + AnnotationSurfaceOffsetVectorTypeEnum::Enum AnnotationCoordinate::s_userDefaultSurfaceOffsetVectorType = AnnotationSurfaceOffsetVectorTypeEnum::CENTROID_THRU_VERTEX; #endif // __ANNOTATION_COORDINATE_DECLARE__ } // namespace diff --git a/src/Annotations/AnnotationOneDimensionalShape.cxx b/src/Annotations/AnnotationOneDimensionalShape.cxx index 30416ca2d..5c740cc6c 100644 --- a/src/Annotations/AnnotationOneDimensionalShape.cxx +++ b/src/Annotations/AnnotationOneDimensionalShape.cxx @@ -112,8 +112,8 @@ AnnotationOneDimensionalShape::copyHelperAnnotationOneDimensionalShape(const Ann void AnnotationOneDimensionalShape::initializeMembersAnnotationOneDimensionalShape() { - m_startCoordinate.grabNew(new AnnotationCoordinate()); - m_endCoordinate.grabNew(new AnnotationCoordinate()); + m_startCoordinate.grabNew(new AnnotationCoordinate(m_attributeDefaultType)); + m_endCoordinate.grabNew(new AnnotationCoordinate(m_attributeDefaultType)); m_sceneAssistant.grabNew(new SceneClassAssistant()); if (testProperty(Property::SCENE_CONTAINS_ATTRIBUTES)) { diff --git a/src/Annotations/AnnotationTwoDimensionalShape.cxx b/src/Annotations/AnnotationTwoDimensionalShape.cxx index 6bba52e35..6b4d2f972 100644 --- a/src/Annotations/AnnotationTwoDimensionalShape.cxx +++ b/src/Annotations/AnnotationTwoDimensionalShape.cxx @@ -117,7 +117,7 @@ AnnotationTwoDimensionalShape::copyHelperAnnotationTwoDimensionalShape(const Ann void AnnotationTwoDimensionalShape::initializeMembersAnnotationTwoDimensionalShape() { - m_coordinate.grabNew(new AnnotationCoordinate()); + m_coordinate.grabNew(new AnnotationCoordinate(m_attributeDefaultType)); switch (m_attributeDefaultType) { case AnnotationAttributesDefaultTypeEnum::NORMAL: diff --git a/src/GuiQt/AnnotationCoordinateWidget.cxx b/src/GuiQt/AnnotationCoordinateWidget.cxx index eddd54c3d..01f2d9ec9 100644 --- a/src/GuiQt/AnnotationCoordinateWidget.cxx +++ b/src/GuiQt/AnnotationCoordinateWidget.cxx @@ -121,7 +121,7 @@ m_browserWindowIndex(browserWindowIndex) m_surfaceOffsetLengthSpinBox->setSingleStep(1.0); m_surfaceOffsetLengthSpinBox->setToolTip("Offset of annotation from surface vertex"); QObject::connect(m_surfaceOffsetLengthSpinBox, SIGNAL(valueChanged(double)), - this, SLOT(valueChanged())); + this, SLOT(surfaceOffsetLengthValueChanged(double))); const int digitsRightOfDecimal = 1; QLabel* xCoordLabel = new QLabel(" X" + colonString); @@ -162,7 +162,7 @@ m_browserWindowIndex(browserWindowIndex) m_surfaceOffsetVectorTypeComboBox = new EnumComboBoxTemplate(this); m_surfaceOffsetVectorTypeComboBox->setup(); QObject::connect(m_surfaceOffsetVectorTypeComboBox, SIGNAL(itemActivated()), - this, SLOT(valueChanged())); + this, SLOT(surfaceOffsetVectorTypeChanged())); m_surfaceOffsetVectorTypeComboBox->getWidget()->setFixedWidth(45); m_surfaceOffsetVectorTypeComboBox->getWidget()->setToolTip("Vector for surface offset:\n" " C - Centroid thru Vertex\n" @@ -484,6 +484,9 @@ AnnotationCoordinateWidget::updateContent(Annotation* annotation) m_surfaceOffsetLengthSpinBox->blockSignals(true); m_surfaceOffsetLengthSpinBox->setValue(surfaceOffsetLength); m_surfaceOffsetLengthSpinBox->blockSignals(false); + + AnnotationCoordinate::setUserDefautlSurfaceOffsetVectorType(surfaceOffsetVector); + AnnotationCoordinate::setUserDefaultSurfaceOffsetLength(surfaceOffsetLength); } if (viewportSpaceFlag) { @@ -501,6 +504,36 @@ AnnotationCoordinateWidget::updateContent(Annotation* annotation) m_coordinateWidget->setVisible( ! surfaceFlag); } +/** + * Called when surface offset value changed. + * + * @param value + * New value. + */ +void +AnnotationCoordinateWidget::surfaceOffsetLengthValueChanged(double value) +{ + const AnnotationCoordinate* coordinate = getCoordinate(); + if ((m_annotation != NULL) + && (coordinate != NULL)) { + AnnotationCoordinate::setUserDefaultSurfaceOffsetLength(value); + valueChanged(); + } +} + +/** + * Called when surface offset vector type is changed. + */ +void +AnnotationCoordinateWidget::surfaceOffsetVectorTypeChanged() +{ + const AnnotationCoordinate* coordinate = getCoordinate(); + if ((m_annotation != NULL) + && (coordinate != NULL)) { + AnnotationCoordinate::setUserDefautlSurfaceOffsetVectorType(m_surfaceOffsetVectorTypeComboBox->getSelectedItem()); + } +} + /** * Gets called when a coordinate value is changed. */ diff --git a/src/GuiQt/AnnotationCoordinateWidget.h b/src/GuiQt/AnnotationCoordinateWidget.h index 517f6e1a3..e5822eba3 100644 --- a/src/GuiQt/AnnotationCoordinateWidget.h +++ b/src/GuiQt/AnnotationCoordinateWidget.h @@ -67,6 +67,10 @@ namespace caret { void setCoordinateActionTriggered(); + void surfaceOffsetLengthValueChanged(double); + + void surfaceOffsetVectorTypeChanged(); + private: AnnotationCoordinateWidget(const AnnotationCoordinateWidget&); diff --git a/src/GuiQt/AnnotationTextEditorWidget.cxx b/src/GuiQt/AnnotationTextEditorWidget.cxx index 4e80d4441..22e774eb1 100644 --- a/src/GuiQt/AnnotationTextEditorWidget.cxx +++ b/src/GuiQt/AnnotationTextEditorWidget.cxx @@ -120,6 +120,7 @@ AnnotationTextEditorWidget::updateContent(std::vector& annotati AnnotationTextConnectTypeEnum::Enum connectValue = AnnotationTextConnectTypeEnum::ANNOTATION_TEXT_CONNECT_NONE; if (m_annotationText != NULL) { connectValue = m_annotationText->getConnectToBrainordinate(); + AnnotationText::setUserDefaultConnectToBrainordinate(connectValue); } m_annotationTextConnectTypeEnumComboBox->setSelectedItem(connectValue); @@ -245,6 +246,7 @@ AnnotationTextEditorWidget::annotationTextConnectTypeEnumComboBoxItemActivated() WuQMessageBox::errorOk(this, errorMessage); } + AnnotationText::setUserDefaultConnectToBrainordinate(connectType); EventManager::get()->sendSimpleEvent(EventTypeEnum::EVENT_ANNOTATION_TOOLBAR_UPDATE); EventManager::get()->sendEvent(EventGraphicsUpdateAllWindows().getPointer()); } -- GitLab From f49c115e0f8113550aa1ecbeea6f9a04c54ad2b1 Mon Sep 17 00:00:00 2001 From: John Harwell Date: Mon, 13 Aug 2018 16:20:32 -0500 Subject: [PATCH 015/427] WB-636 Texture Based Annotations: Added drawing optional box around text in model space and corrections for sizing of text underline. --- ...inOpenGLAnnotationDrawingFixedPipeline.cxx | 44 +++- ...rainOpenGLAnnotationDrawingFixedPipeline.h | 11 +- src/Brain/BrainOpenGLFixedPipeline.cxx | 40 +++- src/Brain/BrainOpenGLFixedPipeline.h | 6 +- src/Brain/BrainOpenGLTextRenderInterface.h | 6 + src/Brain/DummyFontTextRenderer.cxx | 6 + src/Brain/DummyFontTextRenderer.h | 2 + src/Brain/FtglFontTextRenderer.cxx | 216 +++++++++++++++--- src/Brain/FtglFontTextRenderer.h | 31 ++- src/GuiQt/QGLWidgetTextRenderer.cxx | 6 + src/GuiQt/QGLWidgetTextRenderer.h | 2 + 11 files changed, 307 insertions(+), 63 deletions(-) diff --git a/src/Brain/BrainOpenGLAnnotationDrawingFixedPipeline.cxx b/src/Brain/BrainOpenGLAnnotationDrawingFixedPipeline.cxx index a44337078..58d6664b6 100644 --- a/src/Brain/BrainOpenGLAnnotationDrawingFixedPipeline.cxx +++ b/src/Brain/BrainOpenGLAnnotationDrawingFixedPipeline.cxx @@ -474,6 +474,7 @@ BrainOpenGLAnnotationDrawingFixedPipeline::drawModelSpaceAnnotationsOnVolumeSlic { CaretAssert(inputs); m_inputs = inputs; + m_surfaceViewScaling = 1.0f; m_volumeSpacePlaneValid = false; if (plane.isValidPlane()) { @@ -506,16 +507,20 @@ BrainOpenGLAnnotationDrawingFixedPipeline::drawModelSpaceAnnotationsOnVolumeSlic * Annotations that are not in a file but need to be drawn. * @param surfaceDisplayed * In not NULL, surface no which annotations are drawn. + * @param surfaceViewScaling + * Scaling of the viewed surface. */ void BrainOpenGLAnnotationDrawingFixedPipeline::drawAnnotations(Inputs* inputs, const AnnotationCoordinateSpaceEnum::Enum drawingCoordinateSpace, std::vector& colorBars, std::vector& notInFileAnnotations, - const Surface* surfaceDisplayed) + const Surface* surfaceDisplayed, + const float surfaceViewScaling) { CaretAssert(inputs); m_inputs = inputs; + m_surfaceViewScaling = surfaceViewScaling; m_volumeSpacePlaneValid = false; @@ -2381,8 +2386,17 @@ BrainOpenGLAnnotationDrawingFixedPipeline::clipLineAtTextBox(const float bottomL } } +/** + * @return The angle formed by two vectors. + * cos = (u . v) / (||u|| * ||v||) + * + * @param u + * First vector. + * @param v + * Second vector. + */ double -angleInDegreesBetweenVectors(const float u[3], const float v[3]) +BrainOpenGLAnnotationDrawingFixedPipeline::angleInDegreesBetweenVectors(const float u[3], const float v[3]) const { double angle = 0.0; @@ -2404,16 +2418,30 @@ angleInDegreesBetweenVectors(const float u[3], const float v[3]) return angle; } +/** + * Get the normal vector for a surface vector. + * + * @param surface + * The surface. + * @param vertexIndex + * Index of the vertex. + * @param normalVectorOut + * Output containing the normal vector. + */ void -getSurfaceNormalVector(const Surface* surfaceDisplayed, - const int32_t vertexIndex, - float normalVectorOut[3]) +BrainOpenGLAnnotationDrawingFixedPipeline::getSurfaceNormalVector(const Surface* surfaceDisplayed, + const int32_t vertexIndex, + float normalVectorOut[3]) const { const float* normalXYZ = surfaceDisplayed->getNormalVector(vertexIndex); normalVectorOut[0] = normalXYZ[0]; normalVectorOut[1] = normalXYZ[1]; normalVectorOut[2] = normalXYZ[2]; -// std::cout << "Normal Vector: " << AString::fromNumbers(normalVectorOut, 3, ", ") << std::endl; + + const bool useAverageFlag = false; + if ( ! useAverageFlag) { + return; + } CaretPointer th = surfaceDisplayed->getTopologyHelper(); int32_t numNeighbors(0); @@ -2434,7 +2462,6 @@ getSurfaceNormalVector(const Surface* surfaceDisplayed, normalVectorOut[1] /= numNeighbors; normalVectorOut[2] /= numNeighbors; } -// std::cout << " Average Normal Vector: " << AString::fromNumbers(normalVectorOut, 3, ", ") << std::endl; } /** @@ -2619,7 +2646,7 @@ BrainOpenGLAnnotationDrawingFixedPipeline::drawTextTangentOffset(AnnotationFile* float topLeft[3]; float underlineStart[3]; float underlineEnd[3]; - m_brainOpenGLFixedPipeline->getTextRenderer()->getBoundsForTextInModelSpace(*text, surfaceExtentZ, m_textDrawingFlags, + m_brainOpenGLFixedPipeline->getTextRenderer()->getBoundsForTextInModelSpace(*text, m_surfaceViewScaling, surfaceExtentZ, m_textDrawingFlags, bottomLeft, bottomRight, topRight, topLeft, underlineStart, underlineEnd); @@ -2642,6 +2669,7 @@ BrainOpenGLAnnotationDrawingFixedPipeline::drawTextTangentOffset(AnnotationFile* else { glPushMatrix(); m_brainOpenGLFixedPipeline->getTextRenderer()->drawTextInModelSpace(*text, + m_surfaceViewScaling, surfaceExtentZ, normalXYZ, m_textDrawingFlags); diff --git a/src/Brain/BrainOpenGLAnnotationDrawingFixedPipeline.h b/src/Brain/BrainOpenGLAnnotationDrawingFixedPipeline.h index 5628e011e..6492e9d09 100644 --- a/src/Brain/BrainOpenGLAnnotationDrawingFixedPipeline.h +++ b/src/Brain/BrainOpenGLAnnotationDrawingFixedPipeline.h @@ -90,7 +90,8 @@ namespace caret { const AnnotationCoordinateSpaceEnum::Enum drawingCoordinateSpace, std::vector& colorBars, std::vector& notInFileAnnotations, - const Surface* surfaceDisplayed); + const Surface* surfaceDisplayed, + const float surfaceViewScaling); void drawModelSpaceAnnotationsOnVolumeSlice(Inputs* inputs, const Plane& plane, @@ -320,10 +321,18 @@ namespace caret { float estimateColorBarWidth(const AnnotationColorBar* colorBar, const float textHeightInPixels) const; + double angleInDegreesBetweenVectors(const float u[3], const float v[3]) const; + + void getSurfaceNormalVector(const Surface* surfaceDisplayed, + const int32_t vertexIndex, + float normalVectorOut[3]) const; + BrainOpenGLFixedPipeline* m_brainOpenGLFixedPipeline; Inputs* m_inputs; + float m_surfaceViewScaling; + /** * Dummy annotation file is used for annotations that * do not belong to a file. This includes the diff --git a/src/Brain/BrainOpenGLFixedPipeline.cxx b/src/Brain/BrainOpenGLFixedPipeline.cxx index 8a583a051..96e4c681e 100644 --- a/src/Brain/BrainOpenGLFixedPipeline.cxx +++ b/src/Brain/BrainOpenGLFixedPipeline.cxx @@ -153,6 +153,8 @@ using namespace caret; +static Surface* annotationDrawingNullSurface(NULL); +static float annotationDrawingUnusedSurfaceScaling(1.0f); /** * Constructor. * @@ -822,7 +824,8 @@ BrainOpenGLFixedPipeline::drawChartCoordinateSpaceAnnotations(const BrainOpenGLV AnnotationCoordinateSpaceEnum::CHART, emptyColorBars, emptyViewportAnnotations, - NULL); + NULL, + 1.0); @@ -880,7 +883,8 @@ BrainOpenGLFixedPipeline::drawTabAnnotations(const BrainOpenGLViewportContent* t AnnotationCoordinateSpaceEnum::TAB, m_annotationColorBarsForDrawing, m_specialCaseGraphicsAnnotations, - NULL); + annotationDrawingNullSurface, + annotationDrawingUnusedSurfaceScaling); glPopMatrix(); glMatrixMode(GL_PROJECTION); @@ -940,7 +944,8 @@ BrainOpenGLFixedPipeline::drawWindowAnnotations(const int windowViewport[4]) AnnotationCoordinateSpaceEnum::WINDOW, m_annotationColorBarsForDrawing, m_specialCaseGraphicsAnnotations, - NULL); + annotationDrawingNullSurface, + annotationDrawingUnusedSurfaceScaling); glPopMatrix(); glMatrixMode(GL_PROJECTION); @@ -1017,7 +1022,9 @@ BrainOpenGLFixedPipeline::drawModelInternal(Mode mode, } else if (surfaceModel != NULL) { m_mirroredClippingEnabled = true; - this->drawSurfaceModel(surfaceModel, viewport); + this->drawSurfaceModel(browserTabContent, + surfaceModel, + viewport); } else if (surfaceMontageModel != NULL) { m_mirroredClippingEnabled = true; @@ -1726,14 +1733,17 @@ BrainOpenGLFixedPipeline::disableLineAntiAliasing() /** * Draw contents of a surface model. + * @param browserTabContent + * Browser tab containing surface model. * @param surfaceModel * Model that is drawn. * @param viewport * Viewport for drawing region. */ void -BrainOpenGLFixedPipeline::drawSurfaceModel(ModelSurface* surfaceModel, - const int32_t viewport[4]) +BrainOpenGLFixedPipeline::drawSurfaceModel(BrowserTabContent* browserTabContent, + ModelSurface* surfaceModel, + const int32_t viewport[4]) { Surface* surface = surfaceModel->getSurface(); float center[3]; @@ -1752,6 +1762,7 @@ BrainOpenGLFixedPipeline::drawSurfaceModel(ModelSurface* surfaceModel, this->windowTabIndex); this->drawSurface(surface, + browserTabContent->getScaling(), nodeColoringRGBA, true); } @@ -1782,6 +1793,8 @@ BrainOpenGLFixedPipeline::drawSurfaceAxes() * * @param surface * Surface that is drawn. + * @param surfaceScaling + * User scaling of surface. * @param nodeColoringRGBA * RGBA coloring for the nodes. * @param drawAnnotationsInModelSpaceFlag @@ -1789,6 +1802,7 @@ BrainOpenGLFixedPipeline::drawSurfaceAxes() */ void BrainOpenGLFixedPipeline::drawSurface(Surface* surface, + const float surfaceScaling, const float* nodeColoringRGBA, const bool drawAnnotationsInModelSpaceFlag) { @@ -1897,13 +1911,15 @@ BrainOpenGLFixedPipeline::drawSurface(Surface* surface, AnnotationCoordinateSpaceEnum::SURFACE, emptyColorBars, emptyViewportAnnotations, - surface); + surface, + surfaceScaling); if (drawAnnotationsInModelSpaceFlag) { m_annotationDrawing->drawAnnotations(&inputs, AnnotationCoordinateSpaceEnum::STEREOTAXIC, emptyColorBars, emptyViewportAnnotations, - NULL); + annotationDrawingNullSurface, + annotationDrawingUnusedSurfaceScaling); } } break; @@ -1945,13 +1961,15 @@ BrainOpenGLFixedPipeline::drawSurface(Surface* surface, AnnotationCoordinateSpaceEnum::SURFACE, emptyColorBars, emptyViewportAnnotations, - surface); + surface, + surfaceScaling); if (drawAnnotationsInModelSpaceFlag) { m_annotationDrawing->drawAnnotations(&inputs, AnnotationCoordinateSpaceEnum::STEREOTAXIC, emptyColorBars, emptyViewportAnnotations, - NULL); + annotationDrawingNullSurface, + annotationDrawingUnusedSurfaceScaling); } /* @@ -5052,6 +5070,7 @@ BrainOpenGLFixedPipeline::drawSurfaceMontageModel(BrowserTabContent* browserTabC mvp->getProjectionViewType()); this->drawSurface(mvp->getSurface(), + browserTabContent->getScaling(), nodeColoringRGBA, true); } @@ -5296,6 +5315,7 @@ BrainOpenGLFixedPipeline::drawWholeBrainModel(BrowserTabContent* browserTabConte glPushMatrix(); glTranslatef(dx, dy, dz); this->drawSurface(surface, + browserTabContent->getScaling(), nodeColoringRGBA, drawModelSpaceAnnotationsFlag); glPopMatrix(); diff --git a/src/Brain/BrainOpenGLFixedPipeline.h b/src/Brain/BrainOpenGLFixedPipeline.h index 32ed9dca7..ecec741ff 100644 --- a/src/Brain/BrainOpenGLFixedPipeline.h +++ b/src/Brain/BrainOpenGLFixedPipeline.h @@ -192,10 +192,12 @@ namespace caret { ModelChartTwo* chartData, const int32_t viewport[4]); - void drawSurfaceModel(ModelSurface* surfaceModel, - const int32_t viewport[4]); + void drawSurfaceModel(BrowserTabContent* browserTabContent, + ModelSurface* surfaceModel, + const int32_t viewport[4]); void drawSurface(Surface* surface, + const float surfaceScaling, const float* nodeColoringRGBA, const bool drawAnnotationsInModelSpaceFlag); diff --git a/src/Brain/BrainOpenGLTextRenderInterface.h b/src/Brain/BrainOpenGLTextRenderInterface.h index eb605c337..9625891cf 100644 --- a/src/Brain/BrainOpenGLTextRenderInterface.h +++ b/src/Brain/BrainOpenGLTextRenderInterface.h @@ -174,6 +174,8 @@ namespace caret { * * @param annotationText * Text that is to be drawn. + * @param modelSpaceScaling + * Scaling in the model space. * @param heightOrWidthForPercentageSizeText * Size of region used when converting percentage size to a fixed size * @param flags @@ -192,6 +194,7 @@ namespace caret { * Ending coordinate for drawing text underline. */ virtual void getBoundsForTextInModelSpace(const AnnotationText& annotationText, + const float modelSpaceScaling, const float heightOrWidthForPercentageSizeText, const DrawingFlags& flags, float bottomLeftOut[3], @@ -207,6 +210,8 @@ namespace caret { * * @param annotationText * Annotation text and attributes. + * @param modelSpaceScaling + * Scaling in the model space. * @param heightOrWidthForPercentageSizeText * If positive, use it to override width/height of viewport. * @param backgroundOverrideRGBA @@ -215,6 +220,7 @@ namespace caret { * Drawing flags. */ virtual void drawTextInModelSpace(const AnnotationText& annotationText, + const float modelSpaceScaling, const float heightOrWidthForPercentageSizeText, const float normalVector[3], const DrawingFlags& flags) = 0; diff --git a/src/Brain/DummyFontTextRenderer.cxx b/src/Brain/DummyFontTextRenderer.cxx index a16b626af..4985d9f35 100644 --- a/src/Brain/DummyFontTextRenderer.cxx +++ b/src/Brain/DummyFontTextRenderer.cxx @@ -148,6 +148,8 @@ DummyFontTextRenderer::drawTextAtModelCoordsFacingUser(const double /*modelX*/, * * @param annotationText * Annotation text and attributes. + * @param modelSpaceScaling + * Scaling in the model space. * @param heightOrWidthForPercentageSizeText * If positive, use it to override width/height of viewport. * @param normalVector @@ -157,6 +159,7 @@ DummyFontTextRenderer::drawTextAtModelCoordsFacingUser(const double /*modelX*/, */ void DummyFontTextRenderer::drawTextInModelSpace(const AnnotationText& /* annotationText */, + const float /*modelSpaceScaling*/, const float /*heightOrWidthForPercentageSizeText*/, const float* /*normalVector[3]*/, const DrawingFlags& /* flags */) @@ -169,6 +172,8 @@ DummyFontTextRenderer::drawTextInModelSpace(const AnnotationText& /* annotationT * * @param annotationText * Text that is to be drawn. + * @param modelSpaceScaling + * Scaling in the model space. * @param heightOrWidthForPercentageSizeText * Size of region used when converting percentage size to a fixed size * @param flags @@ -188,6 +193,7 @@ DummyFontTextRenderer::drawTextInModelSpace(const AnnotationText& /* annotationT */ void DummyFontTextRenderer::getBoundsForTextInModelSpace(const AnnotationText& /*annotationText*/, + const float /*modelSpaceScaling*/, const float /*heightOrWidthForPercentageSizeText*/, const DrawingFlags& /*flags*/, float* /*bottomLeftOut[3]*/, diff --git a/src/Brain/DummyFontTextRenderer.h b/src/Brain/DummyFontTextRenderer.h index a667f5b24..98d492cb2 100644 --- a/src/Brain/DummyFontTextRenderer.h +++ b/src/Brain/DummyFontTextRenderer.h @@ -55,6 +55,7 @@ namespace caret { const DrawingFlags& flags) override; virtual void drawTextInModelSpace(const AnnotationText& annotationText, + const float modelSpaceScaling, const float heightOrWidthForPercentageSizeText, const float normalVector[3], const DrawingFlags& flags) override; @@ -67,6 +68,7 @@ namespace caret { double& heightOut) override; virtual void getBoundsForTextInModelSpace(const AnnotationText& annotationText, + const float modelSpaceScaling, const float heightOrWidthForPercentageSizeText, const DrawingFlags& flags, float bottomLeftOut[3], diff --git a/src/Brain/FtglFontTextRenderer.cxx b/src/Brain/FtglFontTextRenderer.cxx index 64135c924..ee6ac14cd 100644 --- a/src/Brain/FtglFontTextRenderer.cxx +++ b/src/Brain/FtglFontTextRenderer.cxx @@ -492,9 +492,6 @@ FtglFontTextRenderer::drawTextAtViewportCoordinatesInternal(const AnnotationText return; } -// const bool tooSmallFlag = (font->FaceSize() <= s_tooSmallFontSize); -// annotationText.setFontTooSmallWhenLastDrawn(tooSmallFlag); - if (annotationText.getText().isEmpty()) { return; } @@ -771,6 +768,46 @@ FtglFontTextRenderer::drawOutline(const double minX, glDisable(GL_BLEND); } +/** + * Draw an outline for 3D using the given coordinates. + * + * @param bottomLeft + * Bottom left corner of annotation. + * @param bottomRight + * Bottom right corner of annotation. + * @param topRight + * Top right corner of annotation. + * @param topLeft + * Top left corner of annotation. + * @param outlineThickness + * Thickness of outline + * @param foregroundRGBA + * Color for drawing outline. + */ +void +FtglFontTextRenderer::drawOutline3D(float bottomLeft[3], + float bottomRight[3], + float topRight[3], + float topLeft[3], + const double outlineThickness, + uint8_t foregroundRgba[4]) +{ + /* + * Need to enable anti-aliasing for smooth lines + */ + glEnable(GL_LINE_SMOOTH); + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + glHint(GL_LINE_SMOOTH_HINT, GL_NICEST); + + GraphicsShape::drawBoxOutlineByteColor(bottomLeft, bottomRight, topRight, topLeft, + foregroundRgba, + GraphicsPrimitive::LineWidthType::PIXELS, outlineThickness); + + glDisable(GL_LINE_SMOOTH); + glDisable(GL_BLEND); +} + /** * Expand a box by given amounts in X and Y. * @@ -823,6 +860,67 @@ FtglFontTextRenderer::expandBox(float bottomLeft[3], bottomRight[1] += (widthSpacingY - heightSpacingY); } +/** + * Expand the end points of a line. + * + * @param u + * First point in line. + * @param v + * Second point in line. + * #param extraSpacePercent + * Percentage amount to expand the points. + */ +void +FtglFontTextRenderer::expandLinePercentage3D(float u[3], + float v[3], + const float extraSpacePercent) +{ + float vector[3]; + MathFunctions::subtractVectors(v, u, vector); + const float length = MathFunctions::normalizeVector(vector) / 2.0; + const float extraVector[3] { + vector[0] * (length * extraSpacePercent), + vector[1] * (length * extraSpacePercent), + vector[2] * (length * extraSpacePercent) + }; + + for (int32_t i = 0; i < 3; i++) { + u[i] -= extraVector[i]; + v[i] += extraVector[i]; + } +} + +/** + * Expand the end points of a line. + * + * @param u + * First point in line. + * @param v + * Second point in line. + * #param extraSpacePixels + * Pixels amount to expand the points. + */ +void +FtglFontTextRenderer::expandLinePixels3D(float u[3], + float v[3], + const float extraSpacePixels) +{ + float vector[3]; + MathFunctions::subtractVectors(v, u, vector); + MathFunctions::normalizeVector(vector); + const float halfExtra = extraSpacePixels / 2.0; + const float extraVector[3] { + vector[0] * halfExtra, + vector[1] * halfExtra, + vector[2] * halfExtra + }; + + for (int32_t i = 0; i < 3; i++) { + u[i] -= extraVector[i]; + v[i] += extraVector[i]; + } +} + /** * Expand a box by given amounts in X and Y. * @@ -834,28 +932,20 @@ FtglFontTextRenderer::expandBox(float bottomLeft[3], * Top right corner of annotation. * @param topLeft * Top left corner of annotation. - * @param extraSpacePercentX - * Extra space to add in X, percentage is zero to one with one interpreted as 100%. - * @param extraSpacePercentY - * Extra space to add in Y, percentage is zero to one with one interpreted as 100%. + * @param extraSpacePercent + * Extra space to add, percentage is zero to one with one interpreted as 100%. */ void -FtglFontTextRenderer::expandBoxPercentage(float bottomLeft[3], - float bottomRight[3], - float topRight[3], - float topLeft[3], - const float extraSpacePercentX, - const float extraSpacePercentY) +FtglFontTextRenderer::expandBoxPixels3D(float bottomLeft[3], + float bottomRight[3], + float topRight[3], + float topLeft[3], + const float extraSpacePixels) { - const float width = MathFunctions::distance3D(bottomLeft, bottomRight); - const float height = MathFunctions::distance3D(bottomLeft, topLeft); - - expandBox(bottomLeft, - bottomRight, - topRight, - topLeft, - width * extraSpacePercentX, - height * extraSpacePercentY); + expandLinePixels3D(bottomLeft, bottomRight, extraSpacePixels); + expandLinePixels3D(topLeft, topRight, extraSpacePixels); + expandLinePixels3D(bottomLeft, topLeft, extraSpacePixels); + expandLinePixels3D(bottomRight, topRight, extraSpacePixels); } /** @@ -954,9 +1044,6 @@ FtglFontTextRenderer::drawTextAtViewportCoordsInternal(const DepthTestEnum depth return; } -// const bool tooSmallFlag = (font->FaceSize() < s_tooSmallFontSize); -// annotationText.setFontTooSmallWhenLastDrawn(tooSmallFlag); - m_depthTestingStatus = depthTesting; if (annotationText.getLineWidthPercentage() <= 0.0f) { @@ -1418,6 +1505,8 @@ FtglFontTextRenderer::drawTextAtModelCoordsFacingUser(const double modelX, * * @param annotationText * Text that is to be drawn. + * @param modelSpaceScaling + * Scaling in the model space. * @param heightOrWidthForPercentageSizeText * Size of region used when converting percentage size to a fixed size * @param flags @@ -1437,6 +1526,7 @@ FtglFontTextRenderer::drawTextAtModelCoordsFacingUser(const double modelX, */ void FtglFontTextRenderer::getBoundsForTextInModelSpace(const AnnotationText& annotationText, + const float modelSpaceScaling, const float heightOrWidthForPercentageSizeText, const DrawingFlags& flags, float bottomLeftOut[3], @@ -1517,6 +1607,17 @@ FtglFontTextRenderer::getBoundsForTextInModelSpace(const AnnotationText& annotat topRightOut[1] = dy + ub.Yf(); topRightOut[2] = z; + float copyTopLeft[3] { + topLeftOut[0], + topLeftOut[1], + topLeftOut[2] + }; + float copyTopRight[3] { + topRightOut[0], + topRightOut[1], + topRightOut[2] + }; + underlineStartOut[0] = bottomLeftOut[0]; underlineStartOut[1] = bottomLeftOut[1]; underlineStartOut[2] = bottomLeftOut[2]; @@ -1524,14 +1625,39 @@ FtglFontTextRenderer::getBoundsForTextInModelSpace(const AnnotationText& annotat underlineEndOut[1] = bottomRightOut[1]; underlineEndOut[2] = bottomRightOut[2]; - const float percent = 0.05; - expandBoxPercentage(bottomLeftOut, bottomRightOut, topRightOut, topLeftOut, percent, percent); - - underlineStartOut[1] = bottomLeftOut[1]; - underlineEndOut[1] = bottomRightOut[1]; + const float boxExpansion(getLineThicknessPixelsInModelSpace(annotationText.getLineWidthPercentage(), + heightOrWidthForPercentageSizeText, + 1.0)); + expandBoxPixels3D(bottomLeftOut, bottomRightOut, topRightOut, topLeftOut, + boxExpansion); + expandLinePixels3D(underlineStartOut, copyTopLeft, boxExpansion * 0.5); + expandLinePixels3D(underlineEndOut, copyTopRight, boxExpansion * 0.5); } #endif // HAVE_FREETYPE +} + +/** + * Get the line thickness for model space. + * + * @param lineWidthPercentage + * The line width percentage. + * @param heightOrWidthForPercentageSizeText + * The height/width of region (surface) for percentage size text. + * @param modelSpaceScaling + * Scaling in model space. + */ +float +FtglFontTextRenderer::getLineThicknessPixelsInModelSpace(const float lineWidthPercentage, + const float heightOrWidthForPercentageSizeText, + const float modelSpaceScaling) const +{ + float lineThicknessPercentage = std::max(0.01f, + (lineWidthPercentage / 100.f)); + float lineThicknessPixels = (heightOrWidthForPercentageSizeText + * lineThicknessPercentage); + lineThicknessPixels *= modelSpaceScaling; + return lineThicknessPixels; } /** @@ -1541,6 +1667,8 @@ FtglFontTextRenderer::getBoundsForTextInModelSpace(const AnnotationText& annotat * * @param annotationText * Annotation text and attributes. + * @param modelSpaceScaling + * Scaling in the model space. * @param heightOrWidthForPercentageSizeText * If positive, use it to override width/height of viewport. * @param normalVector @@ -1550,6 +1678,7 @@ FtglFontTextRenderer::getBoundsForTextInModelSpace(const AnnotationText& annotat */ void FtglFontTextRenderer::drawTextInModelSpace(const AnnotationText& annotationText, + const float modelSpaceScaling, const float heightOrWidthForPercentageSizeText, const float normalVector[3], const DrawingFlags& flags) @@ -1628,7 +1757,7 @@ FtglFontTextRenderer::drawTextInModelSpace(const AnnotationText& annotationText, float underlineStart[3] { 0.0, 0.0, 0.0 }; float underlineEnd[3] { 0.0, 0.0, 0.0 }; - getBoundsForTextInModelSpace(annotationText, heightOrWidthForPercentageSizeText, flags, + getBoundsForTextInModelSpace(annotationText, modelSpaceScaling, heightOrWidthForPercentageSizeText, flags, bottomLeft, bottomRight, topRight, topLeft, underlineStart, underlineEnd); @@ -1657,20 +1786,35 @@ FtglFontTextRenderer::drawTextInModelSpace(const AnnotationText& annotationText, uint8_t textRGBA[4]; annotationText.getTextColorRGBA(textRGBA); + const float lineThicknessPixels = getLineThicknessPixelsInModelSpace(annotationText.getLineWidthPercentage(), + heightOrWidthForPercentageSizeText, + modelSpaceScaling); + if (annotationText.isUnderlineStyleEnabled()) { - const float lineThicknessPercentage(1.0f); - float lineThickness = (heightOrWidthForPercentageSizeText - * (lineThicknessPercentage / 100.0)); - GraphicsPrimitiveV3f linePrimitive(GraphicsPrimitive::PrimitiveType::POLYGONAL_LINES, textRGBA); linePrimitive.setLineWidth(GraphicsPrimitiveV3f::LineWidthType::PIXELS, - lineThickness); + lineThicknessPixels); linePrimitive.addVertex(underlineStart); linePrimitive.addVertex(underlineEnd); GraphicsEngineDataOpenGL::draw(&linePrimitive); } + if (annotationText.getLineColor() != CaretColorEnum::NONE) { + glPushMatrix(); + + uint8_t foregroundRgba[4]; + annotationText.getLineColorRGBA(foregroundRgba); + drawOutline3D(bottomLeft, + bottomRight, + topRight, + topLeft, + lineThicknessPixels, + foregroundRgba); + + glPopMatrix(); + } + glTranslatef(dx, dy, 0.0); glColor3ubv(textRGBA); diff --git a/src/Brain/FtglFontTextRenderer.h b/src/Brain/FtglFontTextRenderer.h index 324b0f0c3..4cae00f3c 100644 --- a/src/Brain/FtglFontTextRenderer.h +++ b/src/Brain/FtglFontTextRenderer.h @@ -59,6 +59,7 @@ namespace caret { const DrawingFlags& flags) override; virtual void drawTextInModelSpace(const AnnotationText& annotationText, + const float modelSpaceScaling, const float heightOrWidthForPercentageSizeText, const float normalVector[3], const DrawingFlags& flags) override; @@ -72,6 +73,7 @@ namespace caret { virtual void getBoundsForTextInModelSpace(const AnnotationText& annotationText, + const float modelSpaceScaling, const float heightOrWidthForPercentageSizeText, const DrawingFlags& flags, float bottomLeftOut[3], @@ -153,6 +155,13 @@ namespace caret { const double outlineThickness, uint8_t foregroundRgba[4]); + void drawOutline3D(float bottomLeft[3], + float bottomRight[3], + float topRight[3], + float topLeft[3], + const double outlineThickness, + uint8_t foregroundRgba[4]); + static void expandBox(float bottomLeft[3], float bottomRight[3], float topRight[3], @@ -160,15 +169,25 @@ namespace caret { const float extraSpaceX, const float extraSpaceY); - static void expandBoxPercentage(float bottomLeft[3], - float bottomRight[3], - float topRight[3], - float topLeft[3], - const float extraSpacePercentX, - const float extraSpacePercentY); + static void expandBoxPixels3D(float bottomLeft[3], + float bottomRight[3], + float topRight[3], + float topLeft[3], + const float extraSpacePixels); + + static void expandLinePercentage3D(float u[3], + float v[3], + const float extraSpacePercent); + static void expandLinePixels3D(float u[3], + float v[3], + const float extraSpacePixels); double getLineWidthFromPercentageHeight(const double percentageHeight) const; + float getLineThicknessPixelsInModelSpace(const float lineWidthPercentage, + const float heightOrWidthForPercentageSizeText, + const float modelSpaceScaling) const; + class FontData { public: FontData(); diff --git a/src/GuiQt/QGLWidgetTextRenderer.cxx b/src/GuiQt/QGLWidgetTextRenderer.cxx index b1d3ac9bb..69a55f252 100644 --- a/src/GuiQt/QGLWidgetTextRenderer.cxx +++ b/src/GuiQt/QGLWidgetTextRenderer.cxx @@ -317,6 +317,8 @@ QGLWidgetTextRenderer::drawTextAtModelCoordsFacingUser(const double modelX, * * @param annotationText * Annotation text and attributes. + * @param modelSpaceScaling + * Scaling in the model space. * @param heightOrWidthForPercentageSizeText * If positive, use it to override width/height of viewport. * @param normalVector @@ -326,6 +328,7 @@ QGLWidgetTextRenderer::drawTextAtModelCoordsFacingUser(const double modelX, */ void QGLWidgetTextRenderer::drawTextInModelSpace(const AnnotationText& /* annotationText */, + const float /*modelSpaceScaling*/, const float /*heightOrWidthForPercentageSizeText*/, const float* /*normalVector[3]*/, const DrawingFlags& /* flags */) @@ -766,6 +769,8 @@ QGLWidgetTextRenderer::getTextWidthHeightInPixels(const AnnotationText& annotati * * @param annotationText * Text that is to be drawn. + * @param modelSpaceScaling + * Scaling in the model space. * @param heightOrWidthForPercentageSizeText * Size of region used when converting percentage size to a fixed size * @param flags @@ -785,6 +790,7 @@ QGLWidgetTextRenderer::getTextWidthHeightInPixels(const AnnotationText& annotati */ void QGLWidgetTextRenderer::getBoundsForTextInModelSpace(const AnnotationText& /*annotationText*/, + const float /*modelSpaceScaling*/, const float /*heightOrWidthForPercentageSizeText*/, const DrawingFlags& /*flags*/, float* /*bottomLeftOut[3]*/, diff --git a/src/GuiQt/QGLWidgetTextRenderer.h b/src/GuiQt/QGLWidgetTextRenderer.h index 0c892b0a3..5428fcbd0 100644 --- a/src/GuiQt/QGLWidgetTextRenderer.h +++ b/src/GuiQt/QGLWidgetTextRenderer.h @@ -62,6 +62,7 @@ namespace caret { const BrainOpenGLTextRenderInterface::DrawingFlags& flags) override; virtual void drawTextInModelSpace(const AnnotationText& annotationText, + const float modelSpaceScaling, const float heightOrWidthForPercentageSizeText, const float normalVector[3], const DrawingFlags& flags) override; @@ -74,6 +75,7 @@ namespace caret { double& heightOut) override; virtual void getBoundsForTextInModelSpace(const AnnotationText& annotationText, + const float modelSpaceScaling, const float heightOrWidthForPercentageSizeText, const DrawingFlags& flags, float bottomLeftOut[3], -- GitLab From e3775a29ec5b17dc7b669fc734dec1da3ee9dca4 Mon Sep 17 00:00:00 2001 From: John Harwell Date: Tue, 14 Aug 2018 10:04:41 -0500 Subject: [PATCH 016/427] WB-636 Texture Based Annotations: Correct textured text underline thickness so that it scales with the surface. --- ...inOpenGLAnnotationDrawingFixedPipeline.cxx | 70 ++++++++++++------- src/Brain/FtglFontTextRenderer.cxx | 14 +++- 2 files changed, 54 insertions(+), 30 deletions(-) diff --git a/src/Brain/BrainOpenGLAnnotationDrawingFixedPipeline.cxx b/src/Brain/BrainOpenGLAnnotationDrawingFixedPipeline.cxx index 58d6664b6..2fd58e462 100644 --- a/src/Brain/BrainOpenGLAnnotationDrawingFixedPipeline.cxx +++ b/src/Brain/BrainOpenGLAnnotationDrawingFixedPipeline.cxx @@ -3660,6 +3660,26 @@ BrainOpenGLAnnotationDrawingFixedPipeline::drawAnnotationTwoDimSizingHandles(Ann const float lineThickness, const float rotationAngle) { + AnnotationText* textAnn(NULL); + bool modelSpaceTangentTextFlag = false; + if (annotation->getType() == AnnotationTypeEnum::TEXT) { + textAnn = dynamic_cast(annotation); + CaretAssert(textAnn); + if (textAnn->getCoordinateSpace() == AnnotationCoordinateSpaceEnum::SURFACE) { + const AnnotationCoordinate* coord = textAnn->getCoordinate(); + switch (coord->getSurfaceOffsetVectorType()) { + case AnnotationSurfaceOffsetVectorTypeEnum::CENTROID_THRU_VERTEX: + break; + case AnnotationSurfaceOffsetVectorTypeEnum::SURACE_NORMAL: + break; + case AnnotationSurfaceOffsetVectorTypeEnum::TANGENT: + modelSpaceTangentTextFlag = true; + break; + } + } + } + + float heightVector[3]; MathFunctions::subtractVectors(topLeft, bottomLeft, heightVector); const float boxHeight = MathFunctions::normalizeVector(heightVector); @@ -3781,11 +3801,7 @@ BrainOpenGLAnnotationDrawingFixedPipeline::drawAnnotationTwoDimSizingHandles(Ann handleTop[2] }; - bool modelSpaceTangentTextFlag = false; - if (annotation->getType() == AnnotationTypeEnum::TEXT) { - const AnnotationText* textAnn = dynamic_cast(annotation); - CaretAssert(textAnn); - + if (textAnn != NULL) { /* * The rotation point of a text annotation * is adjusted for the horizontal alignment. @@ -3805,28 +3821,28 @@ BrainOpenGLAnnotationDrawingFixedPipeline::drawAnnotationTwoDimSizingHandles(Ann break; } - if (textAnn->getCoordinateSpace() == AnnotationCoordinateSpaceEnum::SURFACE) { - StructureEnum::Enum structure; - int32_t numberOfVertices(-1); - int32_t vertexIndex(-1); - float offsetLength(0.0f); - AnnotationSurfaceOffsetVectorTypeEnum::Enum surfaceOffsetVectorType; - textAnn->getCoordinate()->getSurfaceSpace(structure, - numberOfVertices, - vertexIndex, - offsetLength, - surfaceOffsetVectorType); - - switch (surfaceOffsetVectorType) { - case AnnotationSurfaceOffsetVectorTypeEnum::CENTROID_THRU_VERTEX: - break; - case AnnotationSurfaceOffsetVectorTypeEnum::SURACE_NORMAL: - break; - case AnnotationSurfaceOffsetVectorTypeEnum::TANGENT: - modelSpaceTangentTextFlag = true; - break; - } - } +// if (textAnn->getCoordinateSpace() == AnnotationCoordinateSpaceEnum::SURFACE) { +// StructureEnum::Enum structure; +// int32_t numberOfVertices(-1); +// int32_t vertexIndex(-1); +// float offsetLength(0.0f); +// AnnotationSurfaceOffsetVectorTypeEnum::Enum surfaceOffsetVectorType; +// textAnn->getCoordinate()->getSurfaceSpace(structure, +// numberOfVertices, +// vertexIndex, +// offsetLength, +// surfaceOffsetVectorType); +// +// switch (surfaceOffsetVectorType) { +// case AnnotationSurfaceOffsetVectorTypeEnum::CENTROID_THRU_VERTEX: +// break; +// case AnnotationSurfaceOffsetVectorTypeEnum::SURACE_NORMAL: +// break; +// case AnnotationSurfaceOffsetVectorTypeEnum::TANGENT: +// modelSpaceTangentTextFlag = true; +// break; +// } +// } } if (modelSpaceTangentTextFlag) { diff --git a/src/Brain/FtglFontTextRenderer.cxx b/src/Brain/FtglFontTextRenderer.cxx index ee6ac14cd..54b747604 100644 --- a/src/Brain/FtglFontTextRenderer.cxx +++ b/src/Brain/FtglFontTextRenderer.cxx @@ -1526,7 +1526,7 @@ FtglFontTextRenderer::drawTextAtModelCoordsFacingUser(const double modelX, */ void FtglFontTextRenderer::getBoundsForTextInModelSpace(const AnnotationText& annotationText, - const float modelSpaceScaling, + const float /*modelSpaceScaling*/, const float heightOrWidthForPercentageSizeText, const DrawingFlags& flags, float bottomLeftOut[3], @@ -1625,9 +1625,13 @@ FtglFontTextRenderer::getBoundsForTextInModelSpace(const AnnotationText& annotat underlineEndOut[1] = bottomRightOut[1]; underlineEndOut[2] = bottomRightOut[2]; + /* + * No scaling since we want the box to tightly enclose the text. + */ + const float noScalingOfModel(1.0); const float boxExpansion(getLineThicknessPixelsInModelSpace(annotationText.getLineWidthPercentage(), heightOrWidthForPercentageSizeText, - 1.0)); + noScalingOfModel)); expandBoxPixels3D(bottomLeftOut, bottomRightOut, topRightOut, topLeftOut, boxExpansion); expandLinePixels3D(underlineStartOut, copyTopLeft, boxExpansion * 0.5); @@ -1791,10 +1795,14 @@ FtglFontTextRenderer::drawTextInModelSpace(const AnnotationText& annotationText, modelSpaceScaling); if (annotationText.isUnderlineStyleEnabled()) { + float underlineThickness = std::max((font->FaceSize() / 14.0), + 1.0); + underlineThickness *= modelSpaceScaling; + GraphicsPrimitiveV3f linePrimitive(GraphicsPrimitive::PrimitiveType::POLYGONAL_LINES, textRGBA); linePrimitive.setLineWidth(GraphicsPrimitiveV3f::LineWidthType::PIXELS, - lineThicknessPixels); + underlineThickness); // lineThicknessPixels linePrimitive.addVertex(underlineStart); linePrimitive.addVertex(underlineEnd); GraphicsEngineDataOpenGL::draw(&linePrimitive); -- GitLab From d5db45e17ccaffde1bc4807baf1e6418ee97a3d3 Mon Sep 17 00:00:00 2001 From: John Harwell Date: Tue, 14 Aug 2018 16:17:00 -0500 Subject: [PATCH 017/427] WB-636 Texture Based Annotations: Corrected problems with text outline box size and size of the annotation text selection handle. Moved a few geometry-ish functions into MathUtilities. --- ...inOpenGLAnnotationDrawingFixedPipeline.cxx | 393 +++++++++++------- ...rainOpenGLAnnotationDrawingFixedPipeline.h | 18 +- src/Brain/FtglFontTextRenderer.cxx | 150 +------ src/Brain/FtglFontTextRenderer.h | 20 - src/Common/MathFunctions.cxx | 168 ++++++++ src/Common/MathFunctions.h | 27 ++ 6 files changed, 456 insertions(+), 320 deletions(-) diff --git a/src/Brain/BrainOpenGLAnnotationDrawingFixedPipeline.cxx b/src/Brain/BrainOpenGLAnnotationDrawingFixedPipeline.cxx index 2fd58e462..3d7db1ee0 100644 --- a/src/Brain/BrainOpenGLAnnotationDrawingFixedPipeline.cxx +++ b/src/Brain/BrainOpenGLAnnotationDrawingFixedPipeline.cxx @@ -1476,7 +1476,7 @@ BrainOpenGLAnnotationDrawingFixedPipeline::drawColorBar(AnnotationFile* annotati bgTopLeft[i] = topLeft[i]; bgTopRight[i] = topRight[i]; } - expandBox(bgBottomLeft, bgBottomRight, bgTopRight, bgTopLeft, 2, 0); + MathFunctions::expandBox(bgBottomLeft, bgBottomRight, bgTopRight, bgTopLeft, 2, 0); std::vector bgCoords; bgCoords.insert(bgCoords.end(), bgBottomLeft, bgBottomLeft + 3); @@ -1585,7 +1585,7 @@ BrainOpenGLAnnotationDrawingFixedPipeline::drawColorBarTickMarks(const Annotatio * and end ticks are at the ends of the color bar. */ const float tickThickness = 2.0; - expandBox(bottomLeft, bottomRight, topRight, topLeft, (-tickThickness / 2.0), 0); + MathFunctions::expandBox(bottomLeft, bottomRight, topRight, topLeft, (-tickThickness / 2.0), 0); float bottomToTopUnitVector[3]; MathFunctions::createUnitVector(bottomLeft, @@ -2692,14 +2692,14 @@ BrainOpenGLAnnotationDrawingFixedPipeline::drawTextTangentOffset(AnnotationFile* glPolygonOffset(-1.0, 1.0); glDisable(GL_LIGHTING); - drawAnnotationTwoDimSizingHandles(annotationFile, - text, - bottomLeft, - bottomRight, - topRight, - topLeft, - s_sizingHandleLineWidthInPixels * 2.0, - text->getRotationAngle()); + drawAnnotationTwoDimSizingHandlesInModelSpace(annotationFile, + text, + bottomLeft, + bottomRight, + topRight, + topLeft, + s_sizingHandleLineWidthInPixels * 2.0, + text->getRotationAngle()); glPopAttrib(); } @@ -3579,8 +3579,12 @@ BrainOpenGLAnnotationDrawingFixedPipeline::drawAnnotationOneDimSizingHandles(Ann } /** - * Expand a box by given amounts in X and Y. + * Draw sizing handles around a two-dimensional annotation. * + * @param annotationFile + * File containing the annotation. + * @param annotation + * Annotation to draw. * @param bottomLeft * Bottom left corner of annotation. * @param bottomRight @@ -3589,49 +3593,198 @@ BrainOpenGLAnnotationDrawingFixedPipeline::drawAnnotationOneDimSizingHandles(Ann * Top right corner of annotation. * @param topLeft * Top left corner of annotation. - * @param extraSpaceX - * Extra space to add in X. - * @param extraSpaceY - * Extra space to add in Y. + * @param lineThickness + * Thickness of line (when enabled). + * @param rotationAngle + * Rotation of the annotation. */ void -BrainOpenGLAnnotationDrawingFixedPipeline::expandBox(float bottomLeft[3], - float bottomRight[3], - float topRight[3], - float topLeft[3], - const float extraSpaceX, - const float extraSpaceY) +BrainOpenGLAnnotationDrawingFixedPipeline::drawAnnotationTwoDimSizingHandles(AnnotationFile* annotationFile, + Annotation* annotation, + const float bottomLeft[3], + const float bottomRight[3], + const float topRight[3], + const float topLeft[3], + const float lineThickness, + const float rotationAngle) { - float widthVector[3]; - MathFunctions::subtractVectors(topRight, topLeft, widthVector); - MathFunctions::normalizeVector(widthVector); - float heightVector[3]; MathFunctions::subtractVectors(topLeft, bottomLeft, heightVector); MathFunctions::normalizeVector(heightVector); - const float widthSpacingX = extraSpaceX * widthVector[0]; - const float widthSpacingY = extraSpaceY * widthVector[1]; + const float innerSpacing = 2.0f + (lineThickness / 2.0f); + float handleTopLeft[3]; + float handleTopRight[3]; + float handleBottomRight[3]; + float handleBottomLeft[3]; + for (int32_t i = 0; i < 3; i++) { + handleTopLeft[i] = topLeft[i]; + handleTopRight[i] = topRight[i]; + handleBottomRight[i] = bottomRight[i]; + handleBottomLeft[i] = bottomLeft[i]; + } + MathFunctions::expandBox(handleBottomLeft, handleBottomRight, handleTopRight, handleTopLeft, innerSpacing, innerSpacing); + + if (! m_selectionModeFlag) { + GraphicsShape::drawBoxOutlineByteColor(handleBottomLeft, handleBottomRight, handleTopRight, handleTopLeft, + m_selectionBoxRGBA, GraphicsPrimitive::LineWidthType::PIXELS, 2.0f); + } - const float heightSpacingX = extraSpaceX * heightVector[0]; - const float heightSpacingY = extraSpaceY * heightVector[1]; + const float handleLeft[3] = { + (handleBottomLeft[0] + handleTopLeft[0]) / 2.0f, + (handleBottomLeft[1] + handleTopLeft[1]) / 2.0f, + (handleBottomLeft[2] + handleTopLeft[2]) / 2.0f, + }; - - topLeft[0] += (-widthSpacingX + heightSpacingX); - topLeft[1] += (-widthSpacingY + heightSpacingY); + const float handleRight[3] = { + (handleBottomRight[0] + handleTopRight[0]) / 2.0f, + (handleBottomRight[1] + handleTopRight[1]) / 2.0f, + (handleBottomRight[2] + handleTopRight[2]) / 2.0f, + }; - topRight[0] += (widthSpacingX + heightSpacingX); - topRight[1] += (widthSpacingY + heightSpacingY); - - bottomLeft[0] += (-widthSpacingX - heightSpacingX); - bottomLeft[1] += (-widthSpacingY - heightSpacingY); - - bottomRight[0] += (widthSpacingX - heightSpacingX); - bottomRight[1] += (widthSpacingY - heightSpacingY); + const float handleBottom[3] = { + (handleBottomLeft[0] + handleBottomRight[0]) / 2.0f, + (handleBottomLeft[1] + handleBottomRight[1]) / 2.0f, + (handleBottomLeft[2] + handleBottomRight[2]) / 2.0f, + }; + + const float handleTop[3] = { + (handleTopLeft[0] + handleTopRight[0]) / 2.0f, + (handleTopLeft[1] + handleTopRight[1]) / 2.0f, + (handleTopLeft[2] + handleTopRight[2]) / 2.0f, + }; + + const float sizeHandleSize = 5.0; + + if (annotation->isSizeHandleValid(AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_BOX_BOTTOM_LEFT)) { + drawSizingHandle(AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_BOX_BOTTOM_LEFT, + annotationFile, + annotation, + handleBottomLeft, + sizeHandleSize, + rotationAngle); + } + if (annotation->isSizeHandleValid(AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_BOX_BOTTOM_RIGHT)) { + drawSizingHandle(AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_BOX_BOTTOM_RIGHT, + annotationFile, + annotation, + handleBottomRight, + sizeHandleSize, + rotationAngle); + } + if (annotation->isSizeHandleValid(AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_BOX_TOP_RIGHT)) { + drawSizingHandle(AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_BOX_TOP_RIGHT, + annotationFile, + annotation, + handleTopRight, + sizeHandleSize, + rotationAngle); + } + if (annotation->isSizeHandleValid(AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_BOX_TOP_LEFT)) { + drawSizingHandle(AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_BOX_TOP_LEFT, + annotationFile, + annotation, + handleTopLeft, + sizeHandleSize, + rotationAngle); + } + if (annotation->isSizeHandleValid(AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_BOX_TOP)) { + drawSizingHandle(AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_BOX_TOP, + annotationFile, + annotation, + handleTop, + sizeHandleSize, + rotationAngle); + } + if (annotation->isSizeHandleValid(AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_BOX_BOTTOM)) { + drawSizingHandle(AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_BOX_BOTTOM, + annotationFile, + annotation, + handleBottom, + sizeHandleSize, + rotationAngle); + } + if (annotation->isSizeHandleValid(AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_BOX_RIGHT)) { + drawSizingHandle(AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_BOX_RIGHT, + annotationFile, + annotation, + handleRight, + sizeHandleSize, + rotationAngle); + } + if (annotation->isSizeHandleValid(AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_BOX_LEFT)) { + drawSizingHandle(AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_BOX_LEFT, + annotationFile, + annotation, + handleLeft, + sizeHandleSize, + rotationAngle); + } + + if (annotation->isSizeHandleValid(AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_ROTATION)) { + + float handleOffset[3] = { + handleTop[0], + handleTop[1], + handleTop[2] + }; + + if (annotation->getType() == AnnotationTypeEnum::TEXT) { + const AnnotationText* textAnn = dynamic_cast(annotation); + CaretAssert(textAnn); + + /* + * The rotation point of a text annotation + * is adjusted for the horizontal alignment. + */ + switch (textAnn->getHorizontalAlignment()) { + case AnnotationTextAlignHorizontalEnum::CENTER: + break; + case AnnotationTextAlignHorizontalEnum::LEFT: + handleOffset[0] = handleTopLeft[0]; + handleOffset[1] = handleTopLeft[1]; + handleOffset[2] = handleTopLeft[2]; + break; + case AnnotationTextAlignHorizontalEnum::RIGHT: + handleOffset[0] = handleTopRight[0]; + handleOffset[1] = handleTopRight[1]; + handleOffset[2] = handleTopRight[2]; + break; + } + } + + const float rotationOffset = sizeHandleSize * 3.0; + const float handleRotation[3] = { + handleOffset[0] + (rotationOffset * heightVector[0]), + handleOffset[1] + (rotationOffset * heightVector[1]), + handleOffset[2] + (rotationOffset * heightVector[2]) + }; + + const float handleRotationLineEnd[3] = { + handleOffset[0] + (rotationOffset * 0.75f * heightVector[0] ), + handleOffset[1] + (rotationOffset * 0.75f * heightVector[1]), + handleOffset[2] + (rotationOffset * 0.75f * heightVector[2]) + }; + + /* + * Rotation handle and line connecting rotation handle to selection box + */ + std::vector coords; + coords.insert(coords.end(), handleRotationLineEnd, handleRotationLineEnd + 3); + coords.insert(coords.end(), handleOffset, handleOffset + 3); + GraphicsShape::drawLinesByteColor(coords, m_selectionBoxRGBA, + GraphicsPrimitive::LineWidthType::PIXELS, 2.0f); + drawSizingHandle(AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_ROTATION, + annotationFile, + annotation, + handleRotation, + sizeHandleSize, + rotationAngle); + } } /** - * Draw sizing handles around a two-dimensional annotation. + * Draw sizing handles around a two-dimensional annotation that is drawn in "model space" * * @param annotationFile * File containing the annotation. @@ -3651,15 +3804,16 @@ BrainOpenGLAnnotationDrawingFixedPipeline::expandBox(float bottomLeft[3], * Rotation of the annotation. */ void -BrainOpenGLAnnotationDrawingFixedPipeline::drawAnnotationTwoDimSizingHandles(AnnotationFile* annotationFile, - Annotation* annotation, - const float bottomLeft[3], - const float bottomRight[3], - const float topRight[3], - const float topLeft[3], - const float lineThickness, - const float rotationAngle) +BrainOpenGLAnnotationDrawingFixedPipeline::drawAnnotationTwoDimSizingHandlesInModelSpace(AnnotationFile* annotationFile, + Annotation* annotation, + const float bottomLeft[3], + const float bottomRight[3], + const float topRight[3], + const float topLeft[3], + const float /*lineThickness*/, + const float rotationAngle) { + CaretAssert(annotation); AnnotationText* textAnn(NULL); bool modelSpaceTangentTextFlag = false; if (annotation->getType() == AnnotationTypeEnum::TEXT) { @@ -3678,25 +3832,25 @@ BrainOpenGLAnnotationDrawingFixedPipeline::drawAnnotationTwoDimSizingHandles(Ann } } } - + CaretAssert(modelSpaceTangentTextFlag); float heightVector[3]; MathFunctions::subtractVectors(topLeft, bottomLeft, heightVector); const float boxHeight = MathFunctions::normalizeVector(heightVector); - - const float innerSpacing = 2.0f + (lineThickness / 2.0f); - float handleTopLeft[3]; - float handleTopRight[3]; - float handleBottomRight[3]; - float handleBottomLeft[3]; - for (int32_t i = 0; i < 3; i++) { - handleTopLeft[i] = topLeft[i]; - handleTopRight[i] = topRight[i]; - handleBottomRight[i] = bottomRight[i]; - handleBottomLeft[i] = bottomLeft[i]; - } - expandBox(handleBottomLeft, handleBottomRight, handleTopRight, handleTopLeft, innerSpacing, innerSpacing); + float handleTopLeft[3]; + float handleTopRight[3]; + float handleBottomRight[3]; + float handleBottomLeft[3]; + for (int32_t i = 0; i < 3; i++) { + handleTopLeft[i] = topLeft[i]; + handleTopRight[i] = topRight[i]; + handleBottomRight[i] = bottomRight[i]; + handleBottomLeft[i] = bottomLeft[i]; + } + const float percentage(0.1f); + MathFunctions::expandBoxPercentage3D(handleBottomLeft, handleBottomRight, handleTopRight, handleTopLeft, percentage); + if (! m_selectionModeFlag) { GraphicsShape::drawBoxOutlineByteColor(handleBottomLeft, handleBottomRight, handleTopRight, handleTopLeft, m_selectionBoxRGBA, GraphicsPrimitive::LineWidthType::PIXELS, 2.0f); @@ -3794,7 +3948,6 @@ BrainOpenGLAnnotationDrawingFixedPipeline::drawAnnotationTwoDimSizingHandles(Ann } if (annotation->isSizeHandleValid(AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_ROTATION)) { - float handleOffset[3] = { handleTop[0], handleTop[1], @@ -3820,90 +3973,36 @@ BrainOpenGLAnnotationDrawingFixedPipeline::drawAnnotationTwoDimSizingHandles(Ann handleOffset[2] = handleTopRight[2]; break; } - -// if (textAnn->getCoordinateSpace() == AnnotationCoordinateSpaceEnum::SURFACE) { -// StructureEnum::Enum structure; -// int32_t numberOfVertices(-1); -// int32_t vertexIndex(-1); -// float offsetLength(0.0f); -// AnnotationSurfaceOffsetVectorTypeEnum::Enum surfaceOffsetVectorType; -// textAnn->getCoordinate()->getSurfaceSpace(structure, -// numberOfVertices, -// vertexIndex, -// offsetLength, -// surfaceOffsetVectorType); -// -// switch (surfaceOffsetVectorType) { -// case AnnotationSurfaceOffsetVectorTypeEnum::CENTROID_THRU_VERTEX: -// break; -// case AnnotationSurfaceOffsetVectorTypeEnum::SURACE_NORMAL: -// break; -// case AnnotationSurfaceOffsetVectorTypeEnum::TANGENT: -// modelSpaceTangentTextFlag = true; -// break; -// } -// } - } - - if (modelSpaceTangentTextFlag) { - const float rotationHandleSize = boxHeight * 0.30; - const float rotationOffset = rotationHandleSize * 1.5; - const float handleRotation[3] = { - handleOffset[0] + (rotationOffset * heightVector[0]), - handleOffset[1] + (rotationOffset * heightVector[1]), - handleOffset[2] + (rotationOffset * heightVector[2]) - }; - - const float handleRotationLineEnd[3] = { - handleOffset[0] + (rotationHandleSize * heightVector[0] ), - handleOffset[1] + (rotationHandleSize * heightVector[1]), - handleOffset[2] + (rotationHandleSize * heightVector[2]) - }; - - /* - * Rotation handle and line connecting rotation handle to selection box - */ - std::vector coords; - coords.insert(coords.end(), handleRotationLineEnd, handleRotationLineEnd + 3); - coords.insert(coords.end(), handleOffset, handleOffset + 3); - GraphicsShape::drawLinesByteColor(coords, m_selectionBoxRGBA, - GraphicsPrimitive::LineWidthType::PIXELS, 2.0f); - drawSizingHandle(AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_ROTATION, - annotationFile, - annotation, - handleRotation, - rotationHandleSize, - rotationAngle); - } - else { - float rotationOffset = sizeHandleSize * 3.0; - const float handleRotation[3] = { - handleOffset[0] + (rotationOffset * heightVector[0]), - handleOffset[1] + (rotationOffset * heightVector[1]), - handleOffset[2] + (rotationOffset * heightVector[2]) - }; - - const float handleRotationLineEnd[3] = { - handleOffset[0] + (rotationOffset * 0.75f * heightVector[0] ), - handleOffset[1] + (rotationOffset * 0.75f * heightVector[1]), - handleOffset[2] + (rotationOffset * 0.75f * heightVector[2]) - }; - - /* - * Rotation handle and line connecting rotation handle to selection box - */ - std::vector coords; - coords.insert(coords.end(), handleRotationLineEnd, handleRotationLineEnd + 3); - coords.insert(coords.end(), handleOffset, handleOffset + 3); - GraphicsShape::drawLinesByteColor(coords, m_selectionBoxRGBA, - GraphicsPrimitive::LineWidthType::PIXELS, 2.0f); - drawSizingHandle(AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_ROTATION, - annotationFile, - annotation, - handleRotation, - sizeHandleSize, - rotationAngle); } + + const float rotationHandleSize = boxHeight * 0.20; // 0.30; + const float rotationOffset = rotationHandleSize * 1.5; + const float handleRotation[3] = { + handleOffset[0] + (rotationOffset * heightVector[0]), + handleOffset[1] + (rotationOffset * heightVector[1]), + handleOffset[2] + (rotationOffset * heightVector[2]) + }; + + const float handleRotationLineEnd[3] = { + handleOffset[0] + (rotationHandleSize * heightVector[0] ), + handleOffset[1] + (rotationHandleSize * heightVector[1]), + handleOffset[2] + (rotationHandleSize * heightVector[2]) + }; + + /* + * Rotation handle and line connecting rotation handle to selection box + */ + std::vector coords; + coords.insert(coords.end(), handleRotationLineEnd, handleRotationLineEnd + 3); + coords.insert(coords.end(), handleOffset, handleOffset + 3); + GraphicsShape::drawLinesByteColor(coords, m_selectionBoxRGBA, + GraphicsPrimitive::LineWidthType::PIXELS, 2.0f); + drawSizingHandle(AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_ROTATION, + annotationFile, + annotation, + handleRotation, + rotationHandleSize, + rotationAngle); } } diff --git a/src/Brain/BrainOpenGLAnnotationDrawingFixedPipeline.h b/src/Brain/BrainOpenGLAnnotationDrawingFixedPipeline.h index 6492e9d09..2510b8f86 100644 --- a/src/Brain/BrainOpenGLAnnotationDrawingFixedPipeline.h +++ b/src/Brain/BrainOpenGLAnnotationDrawingFixedPipeline.h @@ -253,6 +253,15 @@ namespace caret { const float lineThickness, const float rotationAngle); + void drawAnnotationTwoDimSizingHandlesInModelSpace(AnnotationFile* annotationFile, + Annotation* annotation, + const float bottomLeft[3], + const float bottomRight[3], + const float topRight[3], + const float topLeft[3], + const float lineThickness, + const float rotationAngle); + void drawAnnotationOneDimSizingHandles(AnnotationFile* annotationFile, Annotation* annotation, const float firstPoint[3], @@ -289,14 +298,7 @@ namespace caret { const float topLeft[3], std::vector& lineCoordinatesOut, std::vector& arrowCoordinatesOut) const; - - static void expandBox(float bottomLeft[3], - float bottomRight[3], - float topRight[3], - float topLeft[3], - const float extraSpaceX, - const float extraSpaceY); - + void setSelectionBoxColor(); void startOpenGLForDrawing(GLint* savedShadeModelOut, diff --git a/src/Brain/FtglFontTextRenderer.cxx b/src/Brain/FtglFontTextRenderer.cxx index 54b747604..e18e7eab3 100644 --- a/src/Brain/FtglFontTextRenderer.cxx +++ b/src/Brain/FtglFontTextRenderer.cxx @@ -757,8 +757,8 @@ FtglFontTextRenderer::drawOutline(const double minX, float bottomRight[3] = { (float)maxX, (float)minY, (float)z }; float topRight[3] = { (float)maxX, (float)maxY, (float)z }; float topLeft[3] = { (float)minX, (float)maxY, (float)z }; - expandBox(bottomLeft, bottomRight, topRight, topLeft, - outlineThickness, outlineThickness); + MathFunctions::expandBox(bottomLeft, bottomRight, topRight, topLeft, + outlineThickness, outlineThickness); GraphicsShape::drawBoxOutlineByteColor(bottomLeft, bottomRight, topRight, topLeft, foregroundRgba, @@ -808,146 +808,6 @@ FtglFontTextRenderer::drawOutline3D(float bottomLeft[3], glDisable(GL_BLEND); } -/** - * Expand a box by given amounts in X and Y. - * - * @param bottomLeft - * Bottom left corner of annotation. - * @param bottomRight - * Bottom right corner of annotation. - * @param topRight - * Top right corner of annotation. - * @param topLeft - * Top left corner of annotation. - * @param extraSpaceX - * Extra space to add in X. - * @param extraSpaceY - * Extra space to add in Y. - */ -void -FtglFontTextRenderer::expandBox(float bottomLeft[3], - float bottomRight[3], - float topRight[3], - float topLeft[3], - const float extraSpaceX, - const float extraSpaceY) -{ - float widthVector[3]; - MathFunctions::subtractVectors(topRight, topLeft, widthVector); - MathFunctions::normalizeVector(widthVector); - - float heightVector[3]; - MathFunctions::subtractVectors(topLeft, bottomLeft, heightVector); - MathFunctions::normalizeVector(heightVector); - - const float widthSpacingX = extraSpaceX * widthVector[0]; - const float widthSpacingY = extraSpaceY * widthVector[1]; - - const float heightSpacingX = extraSpaceX * heightVector[0]; - const float heightSpacingY = extraSpaceY * heightVector[1]; - - - topLeft[0] += (-widthSpacingX + heightSpacingX); - topLeft[1] += (-widthSpacingY + heightSpacingY); - - topRight[0] += (widthSpacingX + heightSpacingX); - topRight[1] += (widthSpacingY + heightSpacingY); - - bottomLeft[0] += (-widthSpacingX - heightSpacingX); - bottomLeft[1] += (-widthSpacingY - heightSpacingY); - - bottomRight[0] += (widthSpacingX - heightSpacingX); - bottomRight[1] += (widthSpacingY - heightSpacingY); -} - -/** - * Expand the end points of a line. - * - * @param u - * First point in line. - * @param v - * Second point in line. - * #param extraSpacePercent - * Percentage amount to expand the points. - */ -void -FtglFontTextRenderer::expandLinePercentage3D(float u[3], - float v[3], - const float extraSpacePercent) -{ - float vector[3]; - MathFunctions::subtractVectors(v, u, vector); - const float length = MathFunctions::normalizeVector(vector) / 2.0; - const float extraVector[3] { - vector[0] * (length * extraSpacePercent), - vector[1] * (length * extraSpacePercent), - vector[2] * (length * extraSpacePercent) - }; - - for (int32_t i = 0; i < 3; i++) { - u[i] -= extraVector[i]; - v[i] += extraVector[i]; - } -} - -/** - * Expand the end points of a line. - * - * @param u - * First point in line. - * @param v - * Second point in line. - * #param extraSpacePixels - * Pixels amount to expand the points. - */ -void -FtglFontTextRenderer::expandLinePixels3D(float u[3], - float v[3], - const float extraSpacePixels) -{ - float vector[3]; - MathFunctions::subtractVectors(v, u, vector); - MathFunctions::normalizeVector(vector); - const float halfExtra = extraSpacePixels / 2.0; - const float extraVector[3] { - vector[0] * halfExtra, - vector[1] * halfExtra, - vector[2] * halfExtra - }; - - for (int32_t i = 0; i < 3; i++) { - u[i] -= extraVector[i]; - v[i] += extraVector[i]; - } -} - -/** - * Expand a box by given amounts in X and Y. - * - * @param bottomLeft - * Bottom left corner of annotation. - * @param bottomRight - * Bottom right corner of annotation. - * @param topRight - * Top right corner of annotation. - * @param topLeft - * Top left corner of annotation. - * @param extraSpacePercent - * Extra space to add, percentage is zero to one with one interpreted as 100%. - */ -void -FtglFontTextRenderer::expandBoxPixels3D(float bottomLeft[3], - float bottomRight[3], - float topRight[3], - float topLeft[3], - const float extraSpacePixels) -{ - expandLinePixels3D(bottomLeft, bottomRight, extraSpacePixels); - expandLinePixels3D(topLeft, topRight, extraSpacePixels); - expandLinePixels3D(bottomLeft, topLeft, extraSpacePixels); - expandLinePixels3D(bottomRight, topRight, extraSpacePixels); -} - /** * Draw annnotation text at the given viewport coordinates using * the the annotations attributes for the style of text. @@ -1632,10 +1492,10 @@ FtglFontTextRenderer::getBoundsForTextInModelSpace(const AnnotationText& annotat const float boxExpansion(getLineThicknessPixelsInModelSpace(annotationText.getLineWidthPercentage(), heightOrWidthForPercentageSizeText, noScalingOfModel)); - expandBoxPixels3D(bottomLeftOut, bottomRightOut, topRightOut, topLeftOut, + MathFunctions::expandBoxPixels3D(bottomLeftOut, bottomRightOut, topRightOut, topLeftOut, boxExpansion); - expandLinePixels3D(underlineStartOut, copyTopLeft, boxExpansion * 0.5); - expandLinePixels3D(underlineEndOut, copyTopRight, boxExpansion * 0.5); + MathFunctions::expandLinePixels3D(underlineStartOut, copyTopLeft, boxExpansion * 0.5); + MathFunctions::expandLinePixels3D(underlineEndOut, copyTopRight, boxExpansion * 0.5); } #endif // HAVE_FREETYPE } diff --git a/src/Brain/FtglFontTextRenderer.h b/src/Brain/FtglFontTextRenderer.h index 4cae00f3c..e6a2467fb 100644 --- a/src/Brain/FtglFontTextRenderer.h +++ b/src/Brain/FtglFontTextRenderer.h @@ -162,26 +162,6 @@ namespace caret { const double outlineThickness, uint8_t foregroundRgba[4]); - static void expandBox(float bottomLeft[3], - float bottomRight[3], - float topRight[3], - float topLeft[3], - const float extraSpaceX, - const float extraSpaceY); - - static void expandBoxPixels3D(float bottomLeft[3], - float bottomRight[3], - float topRight[3], - float topLeft[3], - const float extraSpacePixels); - - static void expandLinePercentage3D(float u[3], - float v[3], - const float extraSpacePercent); - static void expandLinePixels3D(float u[3], - float v[3], - const float extraSpacePixels); - double getLineWidthFromPercentageHeight(const double percentageHeight) const; float getLineThicknessPixelsInModelSpace(const float lineWidthPercentage, diff --git a/src/Common/MathFunctions.cxx b/src/Common/MathFunctions.cxx index f741aebcd..230e81503 100644 --- a/src/Common/MathFunctions.cxx +++ b/src/Common/MathFunctions.cxx @@ -2471,3 +2471,171 @@ float MathFunctions::q_func(const float& x) } return ret; } + +/** + * Expand a box by given amounts in X and Y. + * + * @param bottomLeft + * Bottom left corner of annotation. + * @param bottomRight + * Bottom right corner of annotation. + * @param topRight + * Top right corner of annotation. + * @param topLeft + * Top left corner of annotation. + * @param extraSpaceX + * Extra space to add in X. + * @param extraSpaceY + * Extra space to add in Y. + */ +void +MathFunctions::expandBox(float bottomLeft[3], + float bottomRight[3], + float topRight[3], + float topLeft[3], + const float extraSpaceX, + const float extraSpaceY) +{ + float widthVector[3]; + MathFunctions::subtractVectors(topRight, topLeft, widthVector); + MathFunctions::normalizeVector(widthVector); + + float heightVector[3]; + MathFunctions::subtractVectors(topLeft, bottomLeft, heightVector); + MathFunctions::normalizeVector(heightVector); + + const float widthSpacingX = extraSpaceX * widthVector[0]; + const float widthSpacingY = extraSpaceY * widthVector[1]; + + const float heightSpacingX = extraSpaceX * heightVector[0]; + const float heightSpacingY = extraSpaceY * heightVector[1]; + + + topLeft[0] += (-widthSpacingX + heightSpacingX); + topLeft[1] += (-widthSpacingY + heightSpacingY); + + topRight[0] += (widthSpacingX + heightSpacingX); + topRight[1] += (widthSpacingY + heightSpacingY); + + bottomLeft[0] += (-widthSpacingX - heightSpacingX); + bottomLeft[1] += (-widthSpacingY - heightSpacingY); + + bottomRight[0] += (widthSpacingX - heightSpacingX); + bottomRight[1] += (widthSpacingY - heightSpacingY); +} + +/** + * Expand the end points of a line. + * + * @param u + * First point in line. + * @param v + * Second point in line. + * #param extraSpacePercent + * Percentage amount to expand the points. + */ +void +MathFunctions::expandLinePercentage3D(float u[3], + float v[3], + const float extraSpacePercent) +{ + float vector[3]; + MathFunctions::subtractVectors(v, u, vector); + const float length = MathFunctions::normalizeVector(vector) / 2.0; + const float extraVector[3] { + vector[0] * (length * extraSpacePercent), + vector[1] * (length * extraSpacePercent), + vector[2] * (length * extraSpacePercent) + }; + + for (int32_t i = 0; i < 3; i++) { + u[i] -= extraVector[i]; + v[i] += extraVector[i]; + } +} + +/** + * Expand the end points of a line. + * + * @param u + * First point in line. + * @param v + * Second point in line. + * #param extraSpacePixels + * Pixels amount to expand the points. + */ +void +MathFunctions::expandLinePixels3D(float u[3], + float v[3], + const float extraSpacePixels) +{ + float vector[3]; + MathFunctions::subtractVectors(v, u, vector); + MathFunctions::normalizeVector(vector); + const float halfExtra = extraSpacePixels / 2.0; + const float extraVector[3] { + vector[0] * halfExtra, + vector[1] * halfExtra, + vector[2] * halfExtra + }; + + for (int32_t i = 0; i < 3; i++) { + u[i] -= extraVector[i]; + v[i] += extraVector[i]; + } +} + +/** + * Expand a box by given amounts in X and Y. + * + * @param bottomLeft + * Bottom left corner of annotation. + * @param bottomRight + * Bottom right corner of annotation. + * @param topRight + * Top right corner of annotation. + * @param topLeft + * Top left corner of annotation. + * @param extraSpacePixels + * Extra space to add, ion pixels. + */ +void +MathFunctions::expandBoxPixels3D(float bottomLeft[3], + float bottomRight[3], + float topRight[3], + float topLeft[3], + const float extraSpacePixels) +{ + expandLinePixels3D(bottomLeft, bottomRight, extraSpacePixels); + expandLinePixels3D(topLeft, topRight, extraSpacePixels); + expandLinePixels3D(bottomLeft, topLeft, extraSpacePixels); + expandLinePixels3D(bottomRight, topRight, extraSpacePixels); +} + +/** + * Expand a box by given amounts in X and Y. + * + * @param bottomLeft + * Bottom left corner of annotation. + * @param bottomRight + * Bottom right corner of annotation. + * @param topRight + * Top right corner of annotation. + * @param topLeft + * Top left corner of annotation. + * @param extraSpacePercentage + * Extra space to add, percentage is zero to one with one interpreted as 100%. + */ +void +MathFunctions::expandBoxPercentage3D(float bottomLeft[3], + float bottomRight[3], + float topRight[3], + float topLeft[3], + const float extraSpacePercentage) +{ + expandLinePercentage3D(bottomLeft, bottomRight, extraSpacePercentage); + expandLinePercentage3D(topLeft, topRight, extraSpacePercentage); + expandLinePercentage3D(bottomLeft, topLeft, extraSpacePercentage); + expandLinePercentage3D(bottomRight, topRight, extraSpacePercentage); +} + diff --git a/src/Common/MathFunctions.h b/src/Common/MathFunctions.h index 0555612c5..3791923d2 100644 --- a/src/Common/MathFunctions.h +++ b/src/Common/MathFunctions.h @@ -398,6 +398,33 @@ public: ///one minus cdf of standard normal distribution static float q_func(const float& x); + static void expandBox(float bottomLeft[3], + float bottomRight[3], + float topRight[3], + float topLeft[3], + const float extraSpaceX, + const float extraSpaceY); + + static void expandBoxPixels3D(float bottomLeft[3], + float bottomRight[3], + float topRight[3], + float topLeft[3], + const float extraSpacePixels); + + static void expandBoxPercentage3D(float bottomLeft[3], + float bottomRight[3], + float topRight[3], + float topLeft[3], + const float extraSpacePercentage); + + static void expandLinePercentage3D(float u[3], + float v[3], + const float extraSpacePercent); + + static void expandLinePixels3D(float u[3], + float v[3], + const float extraSpacePixels); + }; } // namespace -- GitLab From 71ab33435ccef8429ad0479901da736b6c0afd41 Mon Sep 17 00:00:00 2001 From: John Harwell Date: Wed, 15 Aug 2018 14:45:10 -0500 Subject: [PATCH 018/427] WB-636 Texture Based Annotations: Improved drawing of outline around text drawing in 3D model space. --- src/Brain/FtglFontTextRenderer.cxx | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/src/Brain/FtglFontTextRenderer.cxx b/src/Brain/FtglFontTextRenderer.cxx index e18e7eab3..f78d32e07 100644 --- a/src/Brain/FtglFontTextRenderer.cxx +++ b/src/Brain/FtglFontTextRenderer.cxx @@ -1673,12 +1673,20 @@ FtglFontTextRenderer::drawTextInModelSpace(const AnnotationText& annotationText, uint8_t foregroundRgba[4]; annotationText.getLineColorRGBA(foregroundRgba); - drawOutline3D(bottomLeft, - bottomRight, - topRight, - topLeft, - lineThicknessPixels, - foregroundRgba); +// drawOutline3D(bottomLeft, +// bottomRight, +// topRight, +// topLeft, +// lineThicknessPixels, +// foregroundRgba); + GraphicsPrimitiveV3fN3f primitive(GraphicsPrimitive::PrimitiveType::OPENGL_LINE_LOOP, + foregroundRgba); + primitive.addVertex(topLeft, normalVector); + primitive.addVertex(bottomLeft, normalVector); + primitive.addVertex(bottomRight, normalVector); + primitive.addVertex(topRight, normalVector); + primitive.setLineWidth(GraphicsPrimitive::LineWidthType::PIXELS, lineThicknessPixels); + GraphicsEngineDataOpenGL::draw(&primitive); glPopMatrix(); } -- GitLab From 00ccfde8aec633a8453b794c910d6afcee839d7e Mon Sep 17 00:00:00 2001 From: John Harwell Date: Mon, 20 Aug 2018 14:26:32 -0500 Subject: [PATCH 019/427] WB-636 Texture Based Annotations: Improvements with stacked text for texture offset surface text annotations. --- ...inOpenGLAnnotationDrawingFixedPipeline.cxx | 43 +- src/Brain/BrainOpenGLTextRenderInterface.h | 12 +- src/Brain/DummyFontTextRenderer.cxx | 12 +- src/Brain/DummyFontTextRenderer.h | 12 +- src/Brain/FtglFontTextRenderer.cxx | 630 ++++++++++++++---- src/Brain/FtglFontTextRenderer.h | 44 +- src/Common/MathFunctions.cxx | 22 +- src/Common/MathFunctions.h | 16 +- src/Graphics/GraphicsPrimitiveV3fN3f.cxx | 29 + src/Graphics/GraphicsPrimitiveV3fN3f.h | 3 + src/GuiQt/QGLWidgetTextRenderer.cxx | 12 +- src/GuiQt/QGLWidgetTextRenderer.h | 12 +- 12 files changed, 659 insertions(+), 188 deletions(-) diff --git a/src/Brain/BrainOpenGLAnnotationDrawingFixedPipeline.cxx b/src/Brain/BrainOpenGLAnnotationDrawingFixedPipeline.cxx index 3d7db1ee0..7230b32cd 100644 --- a/src/Brain/BrainOpenGLAnnotationDrawingFixedPipeline.cxx +++ b/src/Brain/BrainOpenGLAnnotationDrawingFixedPipeline.cxx @@ -2640,12 +2640,12 @@ BrainOpenGLAnnotationDrawingFixedPipeline::drawTextTangentOffset(AnnotationFile* const float rotationAngle = text->getRotationAngle(); glRotatef(-rotationAngle, 0.0, 0.0, 1.0); - float bottomLeft[3]; - float bottomRight[3]; - float topRight[3]; - float topLeft[3]; - float underlineStart[3]; - float underlineEnd[3]; + double bottomLeft[3]; + double bottomRight[3]; + double topRight[3]; + double topLeft[3]; + double underlineStart[3]; + double underlineEnd[3]; m_brainOpenGLFixedPipeline->getTextRenderer()->getBoundsForTextInModelSpace(*text, m_surfaceViewScaling, surfaceExtentZ, m_textDrawingFlags, bottomLeft, bottomRight, topRight, topLeft, underlineStart, underlineEnd); @@ -2656,10 +2656,15 @@ BrainOpenGLAnnotationDrawingFixedPipeline::drawTextTangentOffset(AnnotationFile* getIdentificationColor(selectionColorRGBA); GraphicsPrimitiveV3fN3f primitive(GraphicsPrimitive::PrimitiveType::OPENGL_TRIANGLE_STRIP, selectionColorRGBA); - primitive.addVertex(topLeft, normalXYZ); - primitive.addVertex(bottomLeft, normalXYZ); - primitive.addVertex(topRight, normalXYZ); - primitive.addVertex(bottomRight, normalXYZ); + const double doubleNormalXYZ[3] { + normalXYZ[0], + normalXYZ[1], + normalXYZ[2] + }; + primitive.addVertex(topLeft, doubleNormalXYZ); + primitive.addVertex(bottomLeft, doubleNormalXYZ); + primitive.addVertex(topRight, doubleNormalXYZ); + primitive.addVertex(bottomRight, doubleNormalXYZ); GraphicsEngineDataOpenGL::draw(&primitive); m_selectionInfo.push_back(SelectionInfo(annotationFile, text, @@ -2692,12 +2697,22 @@ BrainOpenGLAnnotationDrawingFixedPipeline::drawTextTangentOffset(AnnotationFile* glPolygonOffset(-1.0, 1.0); glDisable(GL_LIGHTING); + float floatBottomLeft[3]; + float floatBottomRight[3]; + float floatTopRight[3]; + float floatTopLeft[3]; + for (int32_t i = 0; i < 3; i++) { + floatBottomLeft[i] = bottomLeft[i]; + floatBottomRight[i] = bottomRight[i]; + floatTopLeft[i] = topLeft[i]; + floatTopRight[i] = topRight[i]; + } drawAnnotationTwoDimSizingHandlesInModelSpace(annotationFile, text, - bottomLeft, - bottomRight, - topRight, - topLeft, + floatBottomLeft, + floatBottomRight, + floatTopRight, + floatTopLeft, s_sizingHandleLineWidthInPixels * 2.0, text->getRotationAngle()); glPopAttrib(); diff --git a/src/Brain/BrainOpenGLTextRenderInterface.h b/src/Brain/BrainOpenGLTextRenderInterface.h index 9625891cf..1b7784dea 100644 --- a/src/Brain/BrainOpenGLTextRenderInterface.h +++ b/src/Brain/BrainOpenGLTextRenderInterface.h @@ -197,12 +197,12 @@ namespace caret { const float modelSpaceScaling, const float heightOrWidthForPercentageSizeText, const DrawingFlags& flags, - float bottomLeftOut[3], - float bottomRightOut[3], - float topRightOut[3], - float topLeftOut[3], - float underlineStartOut[3], - float underlineEndOut[3]) = 0; + double bottomLeftOut[3], + double bottomRightOut[3], + double topRightOut[3], + double topLeftOut[3], + double underlineStartOut[3], + double underlineEndOut[3]) = 0; /** * Draw text in model space using the current model transformations. * diff --git a/src/Brain/DummyFontTextRenderer.cxx b/src/Brain/DummyFontTextRenderer.cxx index 4985d9f35..b5a1ccbd2 100644 --- a/src/Brain/DummyFontTextRenderer.cxx +++ b/src/Brain/DummyFontTextRenderer.cxx @@ -196,12 +196,12 @@ DummyFontTextRenderer::getBoundsForTextInModelSpace(const AnnotationText& /*anno const float /*modelSpaceScaling*/, const float /*heightOrWidthForPercentageSizeText*/, const DrawingFlags& /*flags*/, - float* /*bottomLeftOut[3]*/, - float* /*bottomRightOut[3]*/, - float* /*topRightOut[3]*/, - float* /*topLeftOut[3]*/, - float* /*underlineStartOut[3]*/, - float* /*underlineEndOut[3]*/) + double* /*bottomLeftOut[3]*/, + double* /*bottomRightOut[3]*/, + double* /*topRightOut[3]*/, + double* /*topLeftOut[3]*/, + double* /*underlineStartOut[3]*/, + double* /*underlineEndOut[3]*/) { } diff --git a/src/Brain/DummyFontTextRenderer.h b/src/Brain/DummyFontTextRenderer.h index 98d492cb2..61c114028 100644 --- a/src/Brain/DummyFontTextRenderer.h +++ b/src/Brain/DummyFontTextRenderer.h @@ -71,12 +71,12 @@ namespace caret { const float modelSpaceScaling, const float heightOrWidthForPercentageSizeText, const DrawingFlags& flags, - float bottomLeftOut[3], - float bottomRightOut[3], - float topRightOut[3], - float topLeftOut[3], - float underlineStartOut[3], - float underlineEndOut[3]) override; + double bottomLeftOut[3], + double bottomRightOut[3], + double topRightOut[3], + double topLeftOut[3], + double underlineStartOut[3], + double underlineEndOut[3]) override; virtual void getBoundsForTextAtViewportCoords(const AnnotationText& annotationText, const DrawingFlags& flags, diff --git a/src/Brain/FtglFontTextRenderer.cxx b/src/Brain/FtglFontTextRenderer.cxx index f78d32e07..a625d169a 100644 --- a/src/Brain/FtglFontTextRenderer.cxx +++ b/src/Brain/FtglFontTextRenderer.cxx @@ -75,6 +75,7 @@ #include #include +#include "AnnotationCoordinate.h" #include "AnnotationPointSizeText.h" #include "BrainOpenGL.h" #include "CaretAssert.h" @@ -1389,12 +1390,12 @@ FtglFontTextRenderer::getBoundsForTextInModelSpace(const AnnotationText& annotat const float /*modelSpaceScaling*/, const float heightOrWidthForPercentageSizeText, const DrawingFlags& flags, - float bottomLeftOut[3], - float bottomRightOut[3], - float topRightOut[3], - float topLeftOut[3], - float underlineStartOut[3], - float underlineEndOut[3]) + double bottomLeftOut[3], + double bottomRightOut[3], + double topRightOut[3], + double topLeftOut[3], + double underlineStartOut[3], + double underlineEndOut[3]) { std::fill(bottomLeftOut, bottomLeftOut + 3, 0.0f); std::fill(bottomRightOut, bottomRightOut + 3, 0.0f); @@ -1410,6 +1411,51 @@ FtglFontTextRenderer::getBoundsForTextInModelSpace(const AnnotationText& annotat return; } + + + + const float lineThicknessForViewportHeight(getLineThicknessPixelsInModelSpace(annotationText.getLineWidthPercentage(), + heightOrWidthForPercentageSizeText, + 0.0)); + + TextStringGroup textStringGroup(annotationText, + flags, + font, + 0.0, + 0.0, + 0.0, + annotationText.getRotationAngle(), + lineThicknessForViewportHeight); + + double rotationPointXYZ[3]; + textStringGroup.getViewportBounds(s_textMarginSize, + bottomLeftOut, + bottomRightOut, + topRightOut, + topLeftOut, + rotationPointXYZ); + + underlineStartOut[0] = bottomLeftOut[0]; + underlineStartOut[1] = bottomLeftOut[1]; + underlineStartOut[2] = bottomLeftOut[2]; + underlineEndOut[0] = bottomRightOut[0]; + underlineEndOut[1] = bottomRightOut[1]; + underlineEndOut[2] = bottomRightOut[2]; + + return; + + + + + + /* remove code below !!! */ + CaretAssertToDoFatal(); + + + + + + AString text = (flags.isDrawSubstitutedText() ? annotationText.getTextWithSubstitutionsApplied() : annotationText.getText()); @@ -1467,12 +1513,12 @@ FtglFontTextRenderer::getBoundsForTextInModelSpace(const AnnotationText& annotat topRightOut[1] = dy + ub.Yf(); topRightOut[2] = z; - float copyTopLeft[3] { + double copyTopLeft[3] { topLeftOut[0], topLeftOut[1], topLeftOut[2] }; - float copyTopRight[3] { + double copyTopRight[3] { topRightOut[0], topRightOut[1], topRightOut[2] @@ -1524,6 +1570,7 @@ FtglFontTextRenderer::getLineThicknessPixelsInModelSpace(const float lineWidthPe return lineThicknessPixels; } + /** * Draw text in model space using the current model transformations. * @@ -1576,135 +1623,231 @@ FtglFontTextRenderer::drawTextInModelSpace(const AnnotationText& annotationText, return; } - std::wstring wideString = text.toStdWString(); - const wchar_t* wideCharString = wideString.c_str(); +// std::wstring wideString = text.toStdWString(); +// const wchar_t* wideCharString = wideString.c_str(); - FTBBox bounds = font->BBox(wideCharString); - if (bounds.IsValid()) { - FTPoint lb = bounds.Lower(); - FTPoint ub = bounds.Upper(); -// std::cout << "Lower: " << lb.X() << ", " << lb.Y() << ", " << lb.Z() << std::endl; -// std::cout << "Upper: " << ub.X() << ", " << ub.Y() << ", " << ub.Z() << std::endl; - - float dx(0.0); - float dy(0.0f); - switch (annotationText.getHorizontalAlignment()) { - case AnnotationTextAlignHorizontalEnum::CENTER: - dx = -((lb.X() + ub.X()) / 2.0); - break; - case AnnotationTextAlignHorizontalEnum::LEFT: - dx = -lb.X(); - break; - case AnnotationTextAlignHorizontalEnum::RIGHT: - dx = -ub.X(); - break; - } - - switch (annotationText.getVerticalAlignment()) { - case AnnotationTextAlignVerticalEnum::BOTTOM: - dy = -lb.Y(); - break; - case AnnotationTextAlignVerticalEnum::MIDDLE: - dy = -((lb.Y() + ub.Y()) / 2.0); - break; - case AnnotationTextAlignVerticalEnum::TOP: - dy = -ub.Y(); - break; - } - - glPushAttrib(GL_POLYGON_BIT); + const float lineThicknessForViewportHeight(getLineThicknessPixelsInModelSpace(annotationText.getLineWidthPercentage(), + heightOrWidthForPercentageSizeText, + 0.0)); + TextStringGroup tsg(annotationText, + flags, + font, + 0.0, + 0.0, + 0.0, + annotationText.getRotationAngle(), + lineThicknessForViewportHeight); + //tsg.print(); + + drawTextInModelSpaceInternal(annotationText, + modelSpaceScaling, + tsg, + heightOrWidthForPercentageSizeText, + normalVector, + flags); +#endif // HAVE_FREETYPE +} + + +/** + * Draw the text piceces at their assigned model space. + * + * @param annotationText + * Annotation text and attributes. + * @param textMatrix + * Text broken up into cells with model space coordinates assigned. + * @param heightOrWidthForPercentageSizeText + * If positive, use it to override width/height of viewport. + * + */ +void +FtglFontTextRenderer::drawTextInModelSpaceInternal(const AnnotationText& annotationText, + const float modelSpaceScaling, + const TextStringGroup& textStringGroup, + const float heightOrWidthForPercentageSizeText, + const float normalVector[3], + const DrawingFlags& flags) +{ +#ifdef HAVE_FREETYPE + FTFont* font = getFont(annotationText, + FtglFontTypeEnum::POLYGON, + heightOrWidthForPercentageSizeText, + false); + if (! font) { + return; + } + + if (annotationText.getText().isEmpty()) { + return; + } + + saveStateOfOpenGL(); + + BrainOpenGL::testForOpenGLError("At beginning of " + "FtglFontTextRenderer::drawTextInModelSpaceInternal " + "while drawing text: " + + annotationText.getText()); + + glEnable(GL_DEPTH_TEST); + + + const double underlineOffsetY = (textStringGroup.m_underlineThickness / 2.0); + + double bottomLeft[3], bottomRight[3], topRight[3], topLeft[3], rotationPointXYZ[3]; + textStringGroup.getViewportBounds(s_textMarginSize, + bottomLeft, bottomRight, topRight, topLeft, rotationPointXYZ); +// double underlineStart[3], underlineEnd[3]; +// getBoundsForTextInModelSpace(annotationText, +// modelSpaceScaling, heightOrWidthForPercentageSizeText, flags, bottomLeft, +// bottomRight, topRight, topLeft, underlineStart, underlineEnd); + rotationPointXYZ[2] = 0.0; + + glPushMatrix(); + + glPolygonOffset(1.0, 1.0); + glEnable(GL_POLYGON_OFFSET_FILL); + applyBackgroundColoring(textStringGroup); + glDisable(GL_POLYGON_OFFSET_FILL); + + applyTextColoring(annotationText); + + const float rotationAngle = annotationText.getRotationAngle(); + glTranslated(rotationPointXYZ[0], rotationPointXYZ[1], rotationPointXYZ[2]); + glRotated(rotationAngle, 0.0, 0.0, -1.0); + + for (std::vector::const_iterator iter = textStringGroup.m_textStrings.begin(); + iter != textStringGroup.m_textStrings.end(); + iter++) { + const TextString* ts = *iter; - float bottomLeft[3] { 0.0, 0.0, 0.0 }; - float bottomRight[3] { 0.0, 0.0, 0.0 }; - float topLeft[3] { 0.0, 0.0, 0.0 }; - float topRight[3] { 0.0, 0.0, 0.0 }; - float underlineStart[3] { 0.0, 0.0, 0.0 }; - float underlineEnd[3] { 0.0, 0.0, 0.0 }; + double x = ts->m_viewportX; + double y = ts->m_viewportY; + double z = ts->m_viewportZ; - getBoundsForTextInModelSpace(annotationText, modelSpaceScaling, heightOrWidthForPercentageSizeText, flags, - bottomLeft, bottomRight, topRight, topLeft, - underlineStart, underlineEnd); + applyTextColoring(annotationText); - uint8_t backgroundColor[4]; - annotationText.getBackgroundColorRGBA(backgroundColor); - if (backgroundColor[3] > 0) { + for (std::vector::const_iterator charIter = ts->m_characters.begin(); + charIter != ts->m_characters.end(); + charIter++) { + const TextCharacter* tc = *charIter; + x += tc->m_offsetX; + y += tc->m_offsetY; + z += tc->m_offsetZ; - GraphicsPrimitiveV3fN3f primitive(GraphicsPrimitive::PrimitiveType::OPENGL_TRIANGLE_STRIP, - backgroundColor); - primitive.addVertex(topLeft, normalVector); - primitive.addVertex(bottomLeft, normalVector); - primitive.addVertex(topRight, normalVector); - primitive.addVertex(bottomRight, normalVector); - GraphicsEngineDataOpenGL::draw(&primitive); + const double offsetX = x - rotationPointXYZ[0]; + const double offsetY = y - rotationPointXYZ[1]; + const double offsetZ = z - rotationPointXYZ[2]; - /* - * So that text and background do not mix together - * in the Z-buffer - */ - glEnable(GL_POLYGON_OFFSET_FILL); - glEnable(GL_POLYGON_OFFSET_LINE); - glEnable(GL_POLYGON_OFFSET_POINT); - glPolygonOffset(-1.0, 1.0); + glPushMatrix(); + glTranslated(offsetX, + offsetY, + offsetZ); + font->Render(&tc->m_character, + 1); + glPopMatrix(); } - uint8_t textRGBA[4]; - annotationText.getTextColorRGBA(textRGBA); - - const float lineThicknessPixels = getLineThicknessPixelsInModelSpace(annotationText.getLineWidthPercentage(), - heightOrWidthForPercentageSizeText, - modelSpaceScaling); - - if (annotationText.isUnderlineStyleEnabled()) { - float underlineThickness = std::max((font->FaceSize() / 14.0), - 1.0); - underlineThickness *= modelSpaceScaling; + if (ts->m_underlineThickness > 0.0) { + glPushMatrix(); + glTranslated(ts->m_viewportX - rotationPointXYZ[0], ts->m_viewportY - rotationPointXYZ[1], 0.0); - GraphicsPrimitiveV3f linePrimitive(GraphicsPrimitive::PrimitiveType::POLYGONAL_LINES, - textRGBA); - linePrimitive.setLineWidth(GraphicsPrimitiveV3f::LineWidthType::PIXELS, - underlineThickness); // lineThicknessPixels - linePrimitive.addVertex(underlineStart); - linePrimitive.addVertex(underlineEnd); - GraphicsEngineDataOpenGL::draw(&linePrimitive); + const double underlineY = ts->m_stringGlyphsMinY + underlineOffsetY; + uint8_t foregroundRgba[4]; + annotationText.getTextColorRGBA(foregroundRgba); + drawUnderline(ts->m_stringGlyphsMinX, + ts->m_stringGlyphsMaxX, + underlineY, + z, + ts->m_underlineThickness, + foregroundRgba); + + glPopMatrix(); } - if (annotationText.getLineColor() != CaretColorEnum::NONE) { + if (ts->m_outlineThickness > 0.0) { glPushMatrix(); + glTranslated(ts->m_viewportX - rotationPointXYZ[0], ts->m_viewportY - rotationPointXYZ[1], 0.0); uint8_t foregroundRgba[4]; annotationText.getLineColorRGBA(foregroundRgba); -// drawOutline3D(bottomLeft, -// bottomRight, -// topRight, -// topLeft, -// lineThicknessPixels, -// foregroundRgba); - GraphicsPrimitiveV3fN3f primitive(GraphicsPrimitive::PrimitiveType::OPENGL_LINE_LOOP, - foregroundRgba); - primitive.addVertex(topLeft, normalVector); - primitive.addVertex(bottomLeft, normalVector); - primitive.addVertex(bottomRight, normalVector); - primitive.addVertex(topRight, normalVector); - primitive.setLineWidth(GraphicsPrimitive::LineWidthType::PIXELS, lineThicknessPixels); - GraphicsEngineDataOpenGL::draw(&primitive); + drawOutline(ts->m_stringGlyphsMinX, + ts->m_stringGlyphsMaxX, + ts->m_stringGlyphsMinY, + ts->m_stringGlyphsMaxY, + z, + ts->m_outlineThickness, + foregroundRgba); glPopMatrix(); } + } + +// if (annotationText.isUnderlineStyleEnabled()) { +// float underlineThickness = std::max((font->FaceSize() / 14.0), +// 1.0); +// underlineThickness *= modelSpaceScaling; +// +// uint8_t underlineRGBA[4]; +// annotationText.getTextColorRGBA(underlineRGBA); +// GraphicsPrimitiveV3f linePrimitive(GraphicsPrimitive::PrimitiveType::POLYGONAL_LINES, +// underlineRGBA); +// linePrimitive.setLineWidth(GraphicsPrimitiveV3f::LineWidthType::PIXELS, +// underlineThickness); // lineThicknessPixels +// const float floatUnderlineStart[3] = { +// static_cast(underlineStart[0]), +// static_cast(underlineStart[1]), +// static_cast(underlineStart[2]) +// }; +// const float floatUnderlineEnd[3] = { +// static_cast(underlineEnd[0]), +// static_cast(underlineEnd[1]), +// static_cast(underlineEnd[2]) +// }; +// linePrimitive.addVertex(floatUnderlineStart); +// linePrimitive.addVertex(floatUnderlineEnd); +// GraphicsEngineDataOpenGL::draw(&linePrimitive); +// } + + if (annotationText.getLineColor() != CaretColorEnum::NONE) { + glPushMatrix(); - glTranslatef(dx, dy, 0.0); - - glColor3ubv(textRGBA); - font->Render(wideCharString); - - annotationText.setFontTooSmallWhenLastDrawn(false); + const float lineThicknessPixels = getLineThicknessPixelsInModelSpace(annotationText.getLineWidthPercentage(), + heightOrWidthForPercentageSizeText, + modelSpaceScaling); + uint8_t foregroundRgba[4]; + annotationText.getLineColorRGBA(foregroundRgba); + GraphicsPrimitiveV3fN3f primitive(GraphicsPrimitive::PrimitiveType::OPENGL_LINE_LOOP, + foregroundRgba); + double doubleNormalXYZ[3] { + normalVector[0], + normalVector[1], + normalVector[2] + }; + primitive.addVertex(topLeft, doubleNormalXYZ); + primitive.addVertex(bottomLeft, doubleNormalXYZ); + primitive.addVertex(bottomRight, doubleNormalXYZ); + primitive.addVertex(topRight, doubleNormalXYZ); + primitive.setLineWidth(GraphicsPrimitive::LineWidthType::PIXELS, lineThicknessPixels); + GraphicsEngineDataOpenGL::draw(&primitive); - glPopAttrib(); + glPopMatrix(); } + glPopMatrix(); + + BrainOpenGL::testForOpenGLError("At end of " + "FtglFontTextRenderer::drawTextInModelSpaceInternal " + "while drawing text: " + + annotationText.getText()); + + restoreStateOfOpenGL(); +#else // HAVE_FREETYPE + CaretLogSevere("Trying to use FTGL Font rendering but it cannot be used due to FreeType not found."); #endif // HAVE_FREETYPE } + /** * Save the state of OpenGL. * Copied from Qt's qgl.cpp, qt_save_gl_state(). @@ -1994,7 +2137,9 @@ FtglFontTextRenderer::TextCharacter::print(const AString& offsetString) * * @param textString * The text string. - * @parm orientation + * @param textDrawingSpace + * Text drawn in space. + * @param orientation * Orientation of the text string. * @param underlineThickness * Thickness of underline for the text. @@ -2004,11 +2149,13 @@ FtglFontTextRenderer::TextCharacter::print(const AString& offsetString) * Font for drawing the text string. */ FtglFontTextRenderer::TextString::TextString(const QString& textString, + const TextDrawingSpace textDrawingSpace, const AnnotationTextOrientationEnum::Enum orientation, const double underlineThickness, const double outlineThickness, FTFont* font) -: m_underlineThickness(underlineThickness), +: m_textDrawingSpace(textDrawingSpace), +m_underlineThickness(underlineThickness), m_outlineThickness(outlineThickness), m_viewportX(0.0), m_viewportY(0.0), @@ -2116,7 +2263,7 @@ FtglFontTextRenderer::TextString::initializeTextCharacterOffsets(const Annotatio double stringMinX = std::numeric_limits::max(); double stringMaxX = -std::numeric_limits::max(); - const float verticalSpacing = s_textMarginSize * 2.0; + //const float verticalSpacing = s_textMarginSize * 2.0; /* * For each character, set its offset from the previous character @@ -2135,7 +2282,16 @@ FtglFontTextRenderer::TextString::initializeTextCharacterOffsets(const Annotatio if (stackedTextFlag) { double offsetY1 = prevChar->m_glyphMinY; - double offsetY2 = -verticalSpacing; + double offsetY2 = 0.0; + switch (m_textDrawingSpace) { + case TextDrawingSpace::MODEL: + offsetY2 = prevChar->m_glyphMaxY - prevChar->m_glyphMinY; + offsetY2 = -(s_modelSpaceMarginPercentage * offsetY2); + break; + case TextDrawingSpace::VIEWPORT: + offsetY2 = -(s_textMarginSize * 2.0); + break; + } double offsetY3 = -tc->m_glyphMaxY; offsetY = (offsetY1 + offsetY2 + offsetY3); @@ -2357,11 +2513,19 @@ m_underlineThickness(0.0), m_viewportBoundsMinX(0.0), m_viewportBoundsMaxX(0.0), m_viewportBoundsMinY(0.0), -m_viewportBoundsMaxY(0.0) +m_viewportBoundsMaxY(0.0), +m_textDrawingSpace(TextDrawingSpace::VIEWPORT) { #ifdef HAVE_FREETYPE CaretAssert(font); + m_textDrawingSpace = TextDrawingSpace::VIEWPORT; + if (m_annotationText.getCoordinateSpace() == AnnotationCoordinateSpaceEnum::SURFACE) { + if (m_annotationText.getCoordinate()->getSurfaceOffsetVectorType() == AnnotationSurfaceOffsetVectorTypeEnum::TANGENT) { + m_textDrawingSpace = TextDrawingSpace::MODEL; + } + } + if (annotationText.getText().isEmpty()) { m_viewportBoundsMinX = m_viewportX; m_viewportBoundsMaxX = m_viewportY; @@ -2404,6 +2568,7 @@ m_viewportBoundsMaxY(0.0) for (int32_t i = 0; i < textListSize; i++) { TextString* ts = new TextString(textList.at(i), + m_textDrawingSpace, annotationText.getOrientation(), m_underlineThickness, outlineThickness, @@ -2595,7 +2760,16 @@ FtglFontTextRenderer::TextStringGroup::initializeTextPositions() * Move coordinate DOWN for next ROW of text */ const double offsetY1 = prevTextString->m_stringGlyphsMinY; - const double offsetY2 = -(s_textMarginSize * 2.0); + double offsetY2 = 0.0; + switch (m_textDrawingSpace) { + case TextDrawingSpace::MODEL: + offsetY2 = -(prevTextString->m_stringGlyphsMaxY - prevTextString->m_stringGlyphsMinY); + offsetY2 = s_modelSpaceMarginPercentage * offsetY2; + break; + case TextDrawingSpace::VIEWPORT: + offsetY2 = -(s_textMarginSize * 2.0); + break; + } const double offsetY3 = -textString->m_stringGlyphsMaxY; const double offsetY4 = 0.0; const double offsetY = (offsetY1 + offsetY2 + offsetY3 + offsetY4); @@ -2609,7 +2783,16 @@ FtglFontTextRenderer::TextStringGroup::initializeTextPositions() * Move coordinate RIGHT for next COLUMN of text */ const double offsetX1 = prevTextString->m_stringGlyphsMaxX; - const double offsetX2 = s_textMarginSize; + double offsetX2 = 0.0; + switch (m_textDrawingSpace) { + case TextDrawingSpace::MODEL: + offsetX2 = (prevTextString->m_stringGlyphsMaxX - prevTextString->m_stringGlyphsMinX); + offsetX2 = s_modelSpaceMarginPercentage * offsetX2; + break; + case TextDrawingSpace::VIEWPORT: + offsetX2 = s_textMarginSize; + break; + } const double offsetX3 = -textString->m_stringGlyphsMinX; const double offsetX = (offsetX1 + offsetX2 + offsetX3); @@ -2799,3 +2982,214 @@ FtglFontTextRenderer::TextStringGroup::applyAlignmentsToTextStrings() break; } } + + + + + + + + + + + + + + + + + + + +/** + * Draw text in model space using the current model transformations. + * + * Depth testing is ENABLED when drawing text with this method. + * + * @param annotationText + * Annotation text and attributes. + * @param modelSpaceScaling + * Scaling in the model space. + * @param heightOrWidthForPercentageSizeText + * If positive, use it to override width/height of viewport. + * @param normalVector + * Normal vector of text. + * @param flags + * Drawing flags. + */ +void +FtglFontTextRenderer::OLDdrawTextInModelSpace(const AnnotationText& annotationText, + const float modelSpaceScaling, + const float heightOrWidthForPercentageSizeText, + const float normalVector[3], + const DrawingFlags& flags) +{ +#ifdef HAVE_FREETYPE + FTFont* font = getFont(annotationText, + FtglFontTypeEnum::POLYGON, + heightOrWidthForPercentageSizeText, + false); + if (! font) { + return; + } + + const bool drawCrossFlag = false; + if (drawCrossFlag) { + const float a(100.0f); + glLineWidth(3.0); + glBegin(GL_LINES); + glVertex3f(-a, 0, 0); + glVertex3f( a, 0, 0); + glVertex3f(0, -a, 0); + glVertex3f(0, a, 0); + glVertex3f(0, 0,-a); + glVertex3f(0, 0, a); + glEnd(); + } + AString text = (flags.isDrawSubstitutedText() + ? annotationText.getTextWithSubstitutionsApplied() + : annotationText.getText()); + if (text.isEmpty()) { + return; + } + + std::wstring wideString = text.toStdWString(); + const wchar_t* wideCharString = wideString.c_str(); + + FTBBox bounds = font->BBox(wideCharString); + if (bounds.IsValid()) { + FTPoint lb = bounds.Lower(); + FTPoint ub = bounds.Upper(); + // std::cout << "Lower: " << lb.X() << ", " << lb.Y() << ", " << lb.Z() << std::endl; + // std::cout << "Upper: " << ub.X() << ", " << ub.Y() << ", " << ub.Z() << std::endl; + + float dx(0.0); + float dy(0.0f); + switch (annotationText.getHorizontalAlignment()) { + case AnnotationTextAlignHorizontalEnum::CENTER: + dx = -((lb.X() + ub.X()) / 2.0); + break; + case AnnotationTextAlignHorizontalEnum::LEFT: + dx = -lb.X(); + break; + case AnnotationTextAlignHorizontalEnum::RIGHT: + dx = -ub.X(); + break; + } + + switch (annotationText.getVerticalAlignment()) { + case AnnotationTextAlignVerticalEnum::BOTTOM: + dy = -lb.Y(); + break; + case AnnotationTextAlignVerticalEnum::MIDDLE: + dy = -((lb.Y() + ub.Y()) / 2.0); + break; + case AnnotationTextAlignVerticalEnum::TOP: + dy = -ub.Y(); + break; + } + + glPushAttrib(GL_POLYGON_BIT); + + const double doubleNormalXYZ[3] { + normalVector[0], + normalVector[1], + normalVector[2] + }; + + double bottomLeft[3] { 0.0, 0.0, 0.0 }; + double bottomRight[3] { 0.0, 0.0, 0.0 }; + double topLeft[3] { 0.0, 0.0, 0.0 }; + double topRight[3] { 0.0, 0.0, 0.0 }; + double underlineStart[3] { 0.0, 0.0, 0.0 }; + double underlineEnd[3] { 0.0, 0.0, 0.0 }; + + getBoundsForTextInModelSpace(annotationText, modelSpaceScaling, heightOrWidthForPercentageSizeText, flags, + bottomLeft, bottomRight, topRight, topLeft, + underlineStart, underlineEnd); + + uint8_t backgroundColor[4]; + annotationText.getBackgroundColorRGBA(backgroundColor); + if (backgroundColor[3] > 0) { + + GraphicsPrimitiveV3fN3f primitive(GraphicsPrimitive::PrimitiveType::OPENGL_TRIANGLE_STRIP, + backgroundColor); + primitive.addVertex(topLeft, doubleNormalXYZ); + primitive.addVertex(bottomLeft, doubleNormalXYZ); + primitive.addVertex(topRight, doubleNormalXYZ); + primitive.addVertex(bottomRight, doubleNormalXYZ); + GraphicsEngineDataOpenGL::draw(&primitive); + + /* + * So that text and background do not mix together + * in the Z-buffer + */ + glEnable(GL_POLYGON_OFFSET_FILL); + glEnable(GL_POLYGON_OFFSET_LINE); + glEnable(GL_POLYGON_OFFSET_POINT); + glPolygonOffset(-1.0, 1.0); + } + + uint8_t textRGBA[4]; + annotationText.getTextColorRGBA(textRGBA); + + const float lineThicknessPixels = getLineThicknessPixelsInModelSpace(annotationText.getLineWidthPercentage(), + heightOrWidthForPercentageSizeText, + modelSpaceScaling); + + if (annotationText.isUnderlineStyleEnabled()) { + float underlineThickness = std::max((font->FaceSize() / 14.0), + 1.0); + underlineThickness *= modelSpaceScaling; + + GraphicsPrimitiveV3f linePrimitive(GraphicsPrimitive::PrimitiveType::POLYGONAL_LINES, + textRGBA); + linePrimitive.setLineWidth(GraphicsPrimitiveV3f::LineWidthType::PIXELS, + underlineThickness); // lineThicknessPixels + const float floatUnderlineStart[3] = { + static_cast(underlineStart[0]), + static_cast(underlineStart[1]), + static_cast(underlineStart[2]) + }; + const float floatUnderlineEnd[3] = { + static_cast(underlineEnd[0]), + static_cast(underlineEnd[1]), + static_cast(underlineEnd[2]) + }; + linePrimitive.addVertex(floatUnderlineStart); + linePrimitive.addVertex(floatUnderlineEnd); + GraphicsEngineDataOpenGL::draw(&linePrimitive); + } + + if (annotationText.getLineColor() != CaretColorEnum::NONE) { + glPushMatrix(); + + uint8_t foregroundRgba[4]; + annotationText.getLineColorRGBA(foregroundRgba); + GraphicsPrimitiveV3fN3f primitive(GraphicsPrimitive::PrimitiveType::OPENGL_LINE_LOOP, + foregroundRgba); + primitive.addVertex(topLeft, doubleNormalXYZ); + primitive.addVertex(bottomLeft, doubleNormalXYZ); + primitive.addVertex(bottomRight, doubleNormalXYZ); + primitive.addVertex(topRight, doubleNormalXYZ); + primitive.setLineWidth(GraphicsPrimitive::LineWidthType::PIXELS, lineThicknessPixels); + GraphicsEngineDataOpenGL::draw(&primitive); + + glPopMatrix(); + } + + glTranslatef(dx, dy, 0.0); + + glColor3ubv(textRGBA); + font->Render(wideCharString); + + annotationText.setFontTooSmallWhenLastDrawn(false); + + glPopAttrib(); + } + + +#endif // HAVE_FREETYPE +} + + diff --git a/src/Brain/FtglFontTextRenderer.h b/src/Brain/FtglFontTextRenderer.h index e6a2467fb..a170eab1e 100644 --- a/src/Brain/FtglFontTextRenderer.h +++ b/src/Brain/FtglFontTextRenderer.h @@ -64,6 +64,12 @@ namespace caret { const float normalVector[3], const DrawingFlags& flags) override; + virtual void OLDdrawTextInModelSpace(const AnnotationText& annotationText, + const float modelSpaceScaling, + const float heightOrWidthForPercentageSizeText, + const float normalVector[3], + const DrawingFlags& flags); + virtual void getTextWidthHeightInPixels(const AnnotationText& annotationText, const DrawingFlags& flags, const double viewportWidth, @@ -76,12 +82,12 @@ namespace caret { const float modelSpaceScaling, const float heightOrWidthForPercentageSizeText, const DrawingFlags& flags, - float bottomLeftOut[3], - float bottomRightOut[3], - float topRightOut[3], - float topLeftOut[3], - float underlineStartOut[3], - float underlineEndOut[3]) override; + double bottomLeftOut[3], + double bottomRightOut[3], + double topRightOut[3], + double topLeftOut[3], + double underlineStartOut[3], + double underlineEndOut[3]) override; virtual void getBoundsForTextAtViewportCoords(const AnnotationText& annotationText, const DrawingFlags& flags, @@ -190,7 +196,19 @@ namespace caret { bool m_valid; }; - + /** + * Drawing space of text + */ + enum class TextDrawingSpace { + /** + * Drawn in model space coordinates + */ + MODEL, + /** + * Drawn in viewport coordinates + */ + VIEWPORT + }; /** * A text character @@ -235,6 +253,7 @@ namespace caret { class TextString { public: TextString(const QString& textString, + const TextDrawingSpace textDrawingSpace, const AnnotationTextOrientationEnum::Enum orientation, const double underlineThickness, const double outlineThickness, @@ -251,6 +270,7 @@ namespace caret { double& viewportMinY, double& viewportMaxY) const; + const TextDrawingSpace m_textDrawingSpace; const double m_underlineThickness; const double m_outlineThickness; @@ -322,6 +342,7 @@ namespace caret { double m_viewportBoundsMinY; double m_viewportBoundsMaxY; + TextDrawingSpace m_textDrawingSpace; private: void applyAlignmentsToHorizontalTextStrings(); void applyAlignmentsToStackedTextStrings(); @@ -334,6 +355,13 @@ namespace caret { void drawTextAtViewportCoordinatesInternal(const AnnotationText& annotationText, const TextStringGroup& textStringGroup); + void drawTextInModelSpaceInternal(const AnnotationText& annotationText, + const float modelSpaceScaling, + const TextStringGroup& textStringGroup, + const float heightOrWidthForPercentageSizeText, + const float normalVector[3], + const DrawingFlags& flags); + void applyTextColoring(const AnnotationText& annotationText); void applyBackgroundColoring(const TextStringGroup& textStringGroup); @@ -385,10 +413,12 @@ namespace caret { float m_lineWidthMaximum = 5.0f; static const double s_textMarginSize; + static const double s_modelSpaceMarginPercentage; }; #ifdef __FTGL_FONT_TEXT_RENDERER_DECLARE__ const double FtglFontTextRenderer::s_textMarginSize = 3.0; + const double FtglFontTextRenderer::s_modelSpaceMarginPercentage = 0.2; #endif // __FTGL_FONT_TEXT_RENDERER_DECLARE__ } // namespace diff --git a/src/Common/MathFunctions.cxx b/src/Common/MathFunctions.cxx index 230e81503..75a17ee44 100644 --- a/src/Common/MathFunctions.cxx +++ b/src/Common/MathFunctions.cxx @@ -2565,15 +2565,15 @@ MathFunctions::expandLinePercentage3D(float u[3], * Pixels amount to expand the points. */ void -MathFunctions::expandLinePixels3D(float u[3], - float v[3], - const float extraSpacePixels) +MathFunctions::expandLinePixels3D(double u[3], + double v[3], + const double extraSpacePixels) { - float vector[3]; + double vector[3]; MathFunctions::subtractVectors(v, u, vector); MathFunctions::normalizeVector(vector); - const float halfExtra = extraSpacePixels / 2.0; - const float extraVector[3] { + const double halfExtra = extraSpacePixels / 2.0; + const double extraVector[3] { vector[0] * halfExtra, vector[1] * halfExtra, vector[2] * halfExtra @@ -2600,11 +2600,11 @@ MathFunctions::expandLinePixels3D(float u[3], * Extra space to add, ion pixels. */ void -MathFunctions::expandBoxPixels3D(float bottomLeft[3], - float bottomRight[3], - float topRight[3], - float topLeft[3], - const float extraSpacePixels) +MathFunctions::expandBoxPixels3D(double bottomLeft[3], + double bottomRight[3], + double topRight[3], + double topLeft[3], + const double extraSpacePixels) { expandLinePixels3D(bottomLeft, bottomRight, extraSpacePixels); expandLinePixels3D(topLeft, topRight, extraSpacePixels); diff --git a/src/Common/MathFunctions.h b/src/Common/MathFunctions.h index 3791923d2..0b325b7a2 100644 --- a/src/Common/MathFunctions.h +++ b/src/Common/MathFunctions.h @@ -405,11 +405,11 @@ public: const float extraSpaceX, const float extraSpaceY); - static void expandBoxPixels3D(float bottomLeft[3], - float bottomRight[3], - float topRight[3], - float topLeft[3], - const float extraSpacePixels); + static void expandBoxPixels3D(double bottomLeft[3], + double bottomRight[3], + double topRight[3], + double topLeft[3], + const double extraSpacePixels); static void expandBoxPercentage3D(float bottomLeft[3], float bottomRight[3], @@ -421,9 +421,9 @@ public: float v[3], const float extraSpacePercent); - static void expandLinePixels3D(float u[3], - float v[3], - const float extraSpacePixels); + static void expandLinePixels3D(double u[3], + double v[3], + const double extraSpacePixels); }; diff --git a/src/Graphics/GraphicsPrimitiveV3fN3f.cxx b/src/Graphics/GraphicsPrimitiveV3fN3f.cxx index 2062aef84..e30f31957 100644 --- a/src/Graphics/GraphicsPrimitiveV3fN3f.cxx +++ b/src/Graphics/GraphicsPrimitiveV3fN3f.cxx @@ -137,6 +137,35 @@ GraphicsPrimitiveV3fN3f::addVertex(const float xyz[3], NULL); } +/** + * Add a vertex. + * + * @param xyz + * Coordinate of vertex. + * @param normalXYZ + * Normal vector + */ +void +GraphicsPrimitiveV3fN3f::addVertex(const double xyz[3], + const double normalXYZ[3]) +{ + const float floatXYZ[] { + static_cast(xyz[0]), + static_cast(xyz[1]), + static_cast(xyz[2]) + }; + const float floatNormalXYZ[3] { + static_cast(normalXYZ[0]), + static_cast(normalXYZ[1]), + static_cast(normalXYZ[2]) + }; + addVertexProtected(floatXYZ, + floatNormalXYZ, + m_floatSolidRGBA, + m_unsignedByteSolidRGBA, + NULL); +} + /** * Add a vertex. * diff --git a/src/Graphics/GraphicsPrimitiveV3fN3f.h b/src/Graphics/GraphicsPrimitiveV3fN3f.h index 85a49cc1f..444285110 100644 --- a/src/Graphics/GraphicsPrimitiveV3fN3f.h +++ b/src/Graphics/GraphicsPrimitiveV3fN3f.h @@ -47,6 +47,9 @@ namespace caret { void addVertex(const float xyz[3], const float normalXYZ[3]); + void addVertex(const double xyz[3], + const double normalXYZ[3]); + void addVertex(const float x, const float y, const float z, diff --git a/src/GuiQt/QGLWidgetTextRenderer.cxx b/src/GuiQt/QGLWidgetTextRenderer.cxx index 69a55f252..604751655 100644 --- a/src/GuiQt/QGLWidgetTextRenderer.cxx +++ b/src/GuiQt/QGLWidgetTextRenderer.cxx @@ -793,12 +793,12 @@ QGLWidgetTextRenderer::getBoundsForTextInModelSpace(const AnnotationText& /*anno const float /*modelSpaceScaling*/, const float /*heightOrWidthForPercentageSizeText*/, const DrawingFlags& /*flags*/, - float* /*bottomLeftOut[3]*/, - float* /*bottomRightOut[3]*/, - float* /*topRightOut[3]*/, - float* /*topLeftOut[3]*/, - float* /*underlineStartOut[3]*/, - float* /*underlineEndOut[3]*/) + double* /*bottomLeftOut[3]*/, + double* /*bottomRightOut[3]*/, + double* /*topRightOut[3]*/, + double* /*topLeftOut[3]*/, + double* /*underlineStartOut[3]*/, + double* /*underlineEndOut[3]*/) { } diff --git a/src/GuiQt/QGLWidgetTextRenderer.h b/src/GuiQt/QGLWidgetTextRenderer.h index 5428fcbd0..1dde1e63d 100644 --- a/src/GuiQt/QGLWidgetTextRenderer.h +++ b/src/GuiQt/QGLWidgetTextRenderer.h @@ -78,12 +78,12 @@ namespace caret { const float modelSpaceScaling, const float heightOrWidthForPercentageSizeText, const DrawingFlags& flags, - float bottomLeftOut[3], - float bottomRightOut[3], - float topRightOut[3], - float topLeftOut[3], - float underlineStartOut[3], - float underlineEndOut[3]) override; + double bottomLeftOut[3], + double bottomRightOut[3], + double topRightOut[3], + double topLeftOut[3], + double underlineStartOut[3], + double underlineEndOut[3]) override; virtual void getBoundsForTextAtViewportCoords(const AnnotationText& annotationText, const BrainOpenGLTextRenderInterface::DrawingFlags& flags, -- GitLab From 148820e1ce472611e79ea1bc6d0fc39b14836502 Mon Sep 17 00:00:00 2001 From: John Harwell Date: Mon, 20 Aug 2018 14:40:55 -0500 Subject: [PATCH 020/427] WB-636 Texture Based Annotations: Fixed surface offset vector combo box (changing the selection did nothing). --- src/GuiQt/AnnotationCoordinateWidget.cxx | 1 + 1 file changed, 1 insertion(+) diff --git a/src/GuiQt/AnnotationCoordinateWidget.cxx b/src/GuiQt/AnnotationCoordinateWidget.cxx index 01f2d9ec9..9e91a5a92 100644 --- a/src/GuiQt/AnnotationCoordinateWidget.cxx +++ b/src/GuiQt/AnnotationCoordinateWidget.cxx @@ -531,6 +531,7 @@ AnnotationCoordinateWidget::surfaceOffsetVectorTypeChanged() if ((m_annotation != NULL) && (coordinate != NULL)) { AnnotationCoordinate::setUserDefautlSurfaceOffsetVectorType(m_surfaceOffsetVectorTypeComboBox->getSelectedItem()); + valueChanged(); } } -- GitLab From 20c15696c96a8ff86817fe7797c816380215d915 Mon Sep 17 00:00:00 2001 From: John Harwell Date: Tue, 21 Aug 2018 09:01:31 -0500 Subject: [PATCH 021/427] WB-636 Texture Based Annotations: Corrected problems with texture space, the size of the text background, outline box, and selection handle were too large and are now the correct size. --- ...inOpenGLAnnotationDrawingFixedPipeline.cxx | 250 ++-------------- ...rainOpenGLAnnotationDrawingFixedPipeline.h | 9 - src/Brain/FtglFontTextRenderer.cxx | 271 ++---------------- src/Brain/FtglFontTextRenderer.h | 6 - 4 files changed, 42 insertions(+), 494 deletions(-) diff --git a/src/Brain/BrainOpenGLAnnotationDrawingFixedPipeline.cxx b/src/Brain/BrainOpenGLAnnotationDrawingFixedPipeline.cxx index 7230b32cd..edfc39972 100644 --- a/src/Brain/BrainOpenGLAnnotationDrawingFixedPipeline.cxx +++ b/src/Brain/BrainOpenGLAnnotationDrawingFixedPipeline.cxx @@ -2707,14 +2707,14 @@ BrainOpenGLAnnotationDrawingFixedPipeline::drawTextTangentOffset(AnnotationFile* floatTopLeft[i] = topLeft[i]; floatTopRight[i] = topRight[i]; } - drawAnnotationTwoDimSizingHandlesInModelSpace(annotationFile, - text, - floatBottomLeft, - floatBottomRight, - floatTopRight, - floatTopLeft, - s_sizingHandleLineWidthInPixels * 2.0, - text->getRotationAngle()); + drawAnnotationTwoDimSizingHandles(annotationFile, + text, + floatBottomLeft, + floatBottomRight, + floatTopRight, + floatTopLeft, + s_sizingHandleLineWidthInPixels, + text->getRotationAngle()); glPopAttrib(); } @@ -3622,211 +3622,6 @@ BrainOpenGLAnnotationDrawingFixedPipeline::drawAnnotationTwoDimSizingHandles(Ann const float topLeft[3], const float lineThickness, const float rotationAngle) -{ - float heightVector[3]; - MathFunctions::subtractVectors(topLeft, bottomLeft, heightVector); - MathFunctions::normalizeVector(heightVector); - - const float innerSpacing = 2.0f + (lineThickness / 2.0f); - float handleTopLeft[3]; - float handleTopRight[3]; - float handleBottomRight[3]; - float handleBottomLeft[3]; - for (int32_t i = 0; i < 3; i++) { - handleTopLeft[i] = topLeft[i]; - handleTopRight[i] = topRight[i]; - handleBottomRight[i] = bottomRight[i]; - handleBottomLeft[i] = bottomLeft[i]; - } - MathFunctions::expandBox(handleBottomLeft, handleBottomRight, handleTopRight, handleTopLeft, innerSpacing, innerSpacing); - - if (! m_selectionModeFlag) { - GraphicsShape::drawBoxOutlineByteColor(handleBottomLeft, handleBottomRight, handleTopRight, handleTopLeft, - m_selectionBoxRGBA, GraphicsPrimitive::LineWidthType::PIXELS, 2.0f); - } - - const float handleLeft[3] = { - (handleBottomLeft[0] + handleTopLeft[0]) / 2.0f, - (handleBottomLeft[1] + handleTopLeft[1]) / 2.0f, - (handleBottomLeft[2] + handleTopLeft[2]) / 2.0f, - }; - - const float handleRight[3] = { - (handleBottomRight[0] + handleTopRight[0]) / 2.0f, - (handleBottomRight[1] + handleTopRight[1]) / 2.0f, - (handleBottomRight[2] + handleTopRight[2]) / 2.0f, - }; - - const float handleBottom[3] = { - (handleBottomLeft[0] + handleBottomRight[0]) / 2.0f, - (handleBottomLeft[1] + handleBottomRight[1]) / 2.0f, - (handleBottomLeft[2] + handleBottomRight[2]) / 2.0f, - }; - - const float handleTop[3] = { - (handleTopLeft[0] + handleTopRight[0]) / 2.0f, - (handleTopLeft[1] + handleTopRight[1]) / 2.0f, - (handleTopLeft[2] + handleTopRight[2]) / 2.0f, - }; - - const float sizeHandleSize = 5.0; - - if (annotation->isSizeHandleValid(AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_BOX_BOTTOM_LEFT)) { - drawSizingHandle(AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_BOX_BOTTOM_LEFT, - annotationFile, - annotation, - handleBottomLeft, - sizeHandleSize, - rotationAngle); - } - if (annotation->isSizeHandleValid(AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_BOX_BOTTOM_RIGHT)) { - drawSizingHandle(AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_BOX_BOTTOM_RIGHT, - annotationFile, - annotation, - handleBottomRight, - sizeHandleSize, - rotationAngle); - } - if (annotation->isSizeHandleValid(AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_BOX_TOP_RIGHT)) { - drawSizingHandle(AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_BOX_TOP_RIGHT, - annotationFile, - annotation, - handleTopRight, - sizeHandleSize, - rotationAngle); - } - if (annotation->isSizeHandleValid(AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_BOX_TOP_LEFT)) { - drawSizingHandle(AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_BOX_TOP_LEFT, - annotationFile, - annotation, - handleTopLeft, - sizeHandleSize, - rotationAngle); - } - if (annotation->isSizeHandleValid(AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_BOX_TOP)) { - drawSizingHandle(AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_BOX_TOP, - annotationFile, - annotation, - handleTop, - sizeHandleSize, - rotationAngle); - } - if (annotation->isSizeHandleValid(AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_BOX_BOTTOM)) { - drawSizingHandle(AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_BOX_BOTTOM, - annotationFile, - annotation, - handleBottom, - sizeHandleSize, - rotationAngle); - } - if (annotation->isSizeHandleValid(AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_BOX_RIGHT)) { - drawSizingHandle(AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_BOX_RIGHT, - annotationFile, - annotation, - handleRight, - sizeHandleSize, - rotationAngle); - } - if (annotation->isSizeHandleValid(AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_BOX_LEFT)) { - drawSizingHandle(AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_BOX_LEFT, - annotationFile, - annotation, - handleLeft, - sizeHandleSize, - rotationAngle); - } - - if (annotation->isSizeHandleValid(AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_ROTATION)) { - - float handleOffset[3] = { - handleTop[0], - handleTop[1], - handleTop[2] - }; - - if (annotation->getType() == AnnotationTypeEnum::TEXT) { - const AnnotationText* textAnn = dynamic_cast(annotation); - CaretAssert(textAnn); - - /* - * The rotation point of a text annotation - * is adjusted for the horizontal alignment. - */ - switch (textAnn->getHorizontalAlignment()) { - case AnnotationTextAlignHorizontalEnum::CENTER: - break; - case AnnotationTextAlignHorizontalEnum::LEFT: - handleOffset[0] = handleTopLeft[0]; - handleOffset[1] = handleTopLeft[1]; - handleOffset[2] = handleTopLeft[2]; - break; - case AnnotationTextAlignHorizontalEnum::RIGHT: - handleOffset[0] = handleTopRight[0]; - handleOffset[1] = handleTopRight[1]; - handleOffset[2] = handleTopRight[2]; - break; - } - } - - const float rotationOffset = sizeHandleSize * 3.0; - const float handleRotation[3] = { - handleOffset[0] + (rotationOffset * heightVector[0]), - handleOffset[1] + (rotationOffset * heightVector[1]), - handleOffset[2] + (rotationOffset * heightVector[2]) - }; - - const float handleRotationLineEnd[3] = { - handleOffset[0] + (rotationOffset * 0.75f * heightVector[0] ), - handleOffset[1] + (rotationOffset * 0.75f * heightVector[1]), - handleOffset[2] + (rotationOffset * 0.75f * heightVector[2]) - }; - - /* - * Rotation handle and line connecting rotation handle to selection box - */ - std::vector coords; - coords.insert(coords.end(), handleRotationLineEnd, handleRotationLineEnd + 3); - coords.insert(coords.end(), handleOffset, handleOffset + 3); - GraphicsShape::drawLinesByteColor(coords, m_selectionBoxRGBA, - GraphicsPrimitive::LineWidthType::PIXELS, 2.0f); - drawSizingHandle(AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_ROTATION, - annotationFile, - annotation, - handleRotation, - sizeHandleSize, - rotationAngle); - } -} - -/** - * Draw sizing handles around a two-dimensional annotation that is drawn in "model space" - * - * @param annotationFile - * File containing the annotation. - * @param annotation - * Annotation to draw. - * @param bottomLeft - * Bottom left corner of annotation. - * @param bottomRight - * Bottom right corner of annotation. - * @param topRight - * Top right corner of annotation. - * @param topLeft - * Top left corner of annotation. - * @param lineThickness - * Thickness of line (when enabled). - * @param rotationAngle - * Rotation of the annotation. - */ -void -BrainOpenGLAnnotationDrawingFixedPipeline::drawAnnotationTwoDimSizingHandlesInModelSpace(AnnotationFile* annotationFile, - Annotation* annotation, - const float bottomLeft[3], - const float bottomRight[3], - const float topRight[3], - const float topLeft[3], - const float /*lineThickness*/, - const float rotationAngle) { CaretAssert(annotation); AnnotationText* textAnn(NULL); @@ -3847,12 +3642,15 @@ BrainOpenGLAnnotationDrawingFixedPipeline::drawAnnotationTwoDimSizingHandlesInMo } } } - CaretAssert(modelSpaceTangentTextFlag); float heightVector[3]; MathFunctions::subtractVectors(topLeft, bottomLeft, heightVector); - const float boxHeight = MathFunctions::normalizeVector(heightVector); + MathFunctions::normalizeVector(heightVector); + float innerSpacing = 2.0f + (lineThickness / 2.0f); + if (modelSpaceTangentTextFlag) { + innerSpacing = GraphicsUtilitiesOpenGL::convertPixelsToMillimeters(innerSpacing); + } float handleTopLeft[3]; float handleTopRight[3]; float handleBottomRight[3]; @@ -3863,9 +3661,9 @@ BrainOpenGLAnnotationDrawingFixedPipeline::drawAnnotationTwoDimSizingHandlesInMo handleBottomRight[i] = bottomRight[i]; handleBottomLeft[i] = bottomLeft[i]; } - const float percentage(0.1f); - MathFunctions::expandBoxPercentage3D(handleBottomLeft, handleBottomRight, handleTopRight, handleTopLeft, percentage); - + + MathFunctions::expandBox(handleBottomLeft, handleBottomRight, handleTopRight, handleTopLeft, innerSpacing, innerSpacing); + if (! m_selectionModeFlag) { GraphicsShape::drawBoxOutlineByteColor(handleBottomLeft, handleBottomRight, handleTopRight, handleTopLeft, m_selectionBoxRGBA, GraphicsPrimitive::LineWidthType::PIXELS, 2.0f); @@ -3895,7 +3693,10 @@ BrainOpenGLAnnotationDrawingFixedPipeline::drawAnnotationTwoDimSizingHandlesInMo (handleTopLeft[2] + handleTopRight[2]) / 2.0f, }; - const float sizeHandleSize = 5.0; + float sizeHandleSize = 5.0; + if (modelSpaceTangentTextFlag) { + sizeHandleSize = GraphicsUtilitiesOpenGL::convertPixelsToMillimeters(sizeHandleSize); + } if (annotation->isSizeHandleValid(AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_BOX_BOTTOM_LEFT)) { drawSizingHandle(AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_BOX_BOTTOM_LEFT, @@ -3990,8 +3791,7 @@ BrainOpenGLAnnotationDrawingFixedPipeline::drawAnnotationTwoDimSizingHandlesInMo } } - const float rotationHandleSize = boxHeight * 0.20; // 0.30; - const float rotationOffset = rotationHandleSize * 1.5; + const float rotationOffset = sizeHandleSize * 3.0; const float handleRotation[3] = { handleOffset[0] + (rotationOffset * heightVector[0]), handleOffset[1] + (rotationOffset * heightVector[1]), @@ -3999,9 +3799,9 @@ BrainOpenGLAnnotationDrawingFixedPipeline::drawAnnotationTwoDimSizingHandlesInMo }; const float handleRotationLineEnd[3] = { - handleOffset[0] + (rotationHandleSize * heightVector[0] ), - handleOffset[1] + (rotationHandleSize * heightVector[1]), - handleOffset[2] + (rotationHandleSize * heightVector[2]) + handleOffset[0] + (rotationOffset * 0.75f * heightVector[0] ), + handleOffset[1] + (rotationOffset * 0.75f * heightVector[1]), + handleOffset[2] + (rotationOffset * 0.75f * heightVector[2]) }; /* @@ -4016,7 +3816,7 @@ BrainOpenGLAnnotationDrawingFixedPipeline::drawAnnotationTwoDimSizingHandlesInMo annotationFile, annotation, handleRotation, - rotationHandleSize, + sizeHandleSize, rotationAngle); } } diff --git a/src/Brain/BrainOpenGLAnnotationDrawingFixedPipeline.h b/src/Brain/BrainOpenGLAnnotationDrawingFixedPipeline.h index 2510b8f86..e1f085ccd 100644 --- a/src/Brain/BrainOpenGLAnnotationDrawingFixedPipeline.h +++ b/src/Brain/BrainOpenGLAnnotationDrawingFixedPipeline.h @@ -253,15 +253,6 @@ namespace caret { const float lineThickness, const float rotationAngle); - void drawAnnotationTwoDimSizingHandlesInModelSpace(AnnotationFile* annotationFile, - Annotation* annotation, - const float bottomLeft[3], - const float bottomRight[3], - const float topRight[3], - const float topLeft[3], - const float lineThickness, - const float rotationAngle); - void drawAnnotationOneDimSizingHandles(AnnotationFile* annotationFile, Annotation* annotation, const float firstPoint[3], diff --git a/src/Brain/FtglFontTextRenderer.cxx b/src/Brain/FtglFontTextRenderer.cxx index a625d169a..e7722526e 100644 --- a/src/Brain/FtglFontTextRenderer.cxx +++ b/src/Brain/FtglFontTextRenderer.cxx @@ -1623,9 +1623,6 @@ FtglFontTextRenderer::drawTextInModelSpace(const AnnotationText& annotationText, return; } -// std::wstring wideString = text.toStdWString(); -// const wchar_t* wideCharString = wideString.c_str(); - const float lineThicknessForViewportHeight(getLineThicknessPixelsInModelSpace(annotationText.getLineWidthPercentage(), heightOrWidthForPercentageSizeText, 0.0)); @@ -1637,8 +1634,6 @@ FtglFontTextRenderer::drawTextInModelSpace(const AnnotationText& annotationText, 0.0, annotationText.getRotationAngle(), lineThicknessForViewportHeight); - //tsg.print(); - drawTextInModelSpaceInternal(annotationText, modelSpaceScaling, tsg, @@ -1696,10 +1691,6 @@ FtglFontTextRenderer::drawTextInModelSpaceInternal(const AnnotationText& annotat double bottomLeft[3], bottomRight[3], topRight[3], topLeft[3], rotationPointXYZ[3]; textStringGroup.getViewportBounds(s_textMarginSize, bottomLeft, bottomRight, topRight, topLeft, rotationPointXYZ); -// double underlineStart[3], underlineEnd[3]; -// getBoundsForTextInModelSpace(annotationText, -// modelSpaceScaling, heightOrWidthForPercentageSizeText, flags, bottomLeft, -// bottomRight, topRight, topLeft, underlineStart, underlineEnd); rotationPointXYZ[2] = 0.0; glPushMatrix(); @@ -1782,32 +1773,6 @@ FtglFontTextRenderer::drawTextInModelSpaceInternal(const AnnotationText& annotat } } -// if (annotationText.isUnderlineStyleEnabled()) { -// float underlineThickness = std::max((font->FaceSize() / 14.0), -// 1.0); -// underlineThickness *= modelSpaceScaling; -// -// uint8_t underlineRGBA[4]; -// annotationText.getTextColorRGBA(underlineRGBA); -// GraphicsPrimitiveV3f linePrimitive(GraphicsPrimitive::PrimitiveType::POLYGONAL_LINES, -// underlineRGBA); -// linePrimitive.setLineWidth(GraphicsPrimitiveV3f::LineWidthType::PIXELS, -// underlineThickness); // lineThicknessPixels -// const float floatUnderlineStart[3] = { -// static_cast(underlineStart[0]), -// static_cast(underlineStart[1]), -// static_cast(underlineStart[2]) -// }; -// const float floatUnderlineEnd[3] = { -// static_cast(underlineEnd[0]), -// static_cast(underlineEnd[1]), -// static_cast(underlineEnd[2]) -// }; -// linePrimitive.addVertex(floatUnderlineStart); -// linePrimitive.addVertex(floatUnderlineEnd); -// GraphicsEngineDataOpenGL::draw(&linePrimitive); -// } - if (annotationText.getLineColor() != CaretColorEnum::NONE) { glPushMatrix(); @@ -2718,14 +2683,23 @@ FtglFontTextRenderer::TextStringGroup::getViewportBounds(const double margin, * Margin is NOT included when rotation point is computed * as it will move the rotation point to the wrong position. */ - bottomLeftOut[0] -= margin; - bottomLeftOut[1] -= margin; - bottomRightOut[0] += margin; - bottomRightOut[1] -= margin; - topRightOut[0] += margin; - topRightOut[1] += margin; - topLeftOut[0] -= margin; - topLeftOut[1] += margin; + double boundsMargin = 0.0; + switch (m_textDrawingSpace) { + case TextDrawingSpace::MODEL: + boundsMargin = GraphicsUtilitiesOpenGL::convertPixelsToMillimeters(margin); + break; + case TextDrawingSpace::VIEWPORT: + boundsMargin = margin; + break; + } + bottomLeftOut[0] -= boundsMargin; + bottomLeftOut[1] -= boundsMargin; + bottomRightOut[0] += boundsMargin; + bottomRightOut[1] -= boundsMargin; + topRightOut[0] += boundsMargin; + topRightOut[1] += boundsMargin; + topLeftOut[0] -= boundsMargin; + topLeftOut[1] += boundsMargin; matrix.multiplyPoint3(bottomLeftOut); matrix.multiplyPoint3(bottomRightOut); @@ -2982,214 +2956,3 @@ FtglFontTextRenderer::TextStringGroup::applyAlignmentsToTextStrings() break; } } - - - - - - - - - - - - - - - - - - - -/** - * Draw text in model space using the current model transformations. - * - * Depth testing is ENABLED when drawing text with this method. - * - * @param annotationText - * Annotation text and attributes. - * @param modelSpaceScaling - * Scaling in the model space. - * @param heightOrWidthForPercentageSizeText - * If positive, use it to override width/height of viewport. - * @param normalVector - * Normal vector of text. - * @param flags - * Drawing flags. - */ -void -FtglFontTextRenderer::OLDdrawTextInModelSpace(const AnnotationText& annotationText, - const float modelSpaceScaling, - const float heightOrWidthForPercentageSizeText, - const float normalVector[3], - const DrawingFlags& flags) -{ -#ifdef HAVE_FREETYPE - FTFont* font = getFont(annotationText, - FtglFontTypeEnum::POLYGON, - heightOrWidthForPercentageSizeText, - false); - if (! font) { - return; - } - - const bool drawCrossFlag = false; - if (drawCrossFlag) { - const float a(100.0f); - glLineWidth(3.0); - glBegin(GL_LINES); - glVertex3f(-a, 0, 0); - glVertex3f( a, 0, 0); - glVertex3f(0, -a, 0); - glVertex3f(0, a, 0); - glVertex3f(0, 0,-a); - glVertex3f(0, 0, a); - glEnd(); - } - AString text = (flags.isDrawSubstitutedText() - ? annotationText.getTextWithSubstitutionsApplied() - : annotationText.getText()); - if (text.isEmpty()) { - return; - } - - std::wstring wideString = text.toStdWString(); - const wchar_t* wideCharString = wideString.c_str(); - - FTBBox bounds = font->BBox(wideCharString); - if (bounds.IsValid()) { - FTPoint lb = bounds.Lower(); - FTPoint ub = bounds.Upper(); - // std::cout << "Lower: " << lb.X() << ", " << lb.Y() << ", " << lb.Z() << std::endl; - // std::cout << "Upper: " << ub.X() << ", " << ub.Y() << ", " << ub.Z() << std::endl; - - float dx(0.0); - float dy(0.0f); - switch (annotationText.getHorizontalAlignment()) { - case AnnotationTextAlignHorizontalEnum::CENTER: - dx = -((lb.X() + ub.X()) / 2.0); - break; - case AnnotationTextAlignHorizontalEnum::LEFT: - dx = -lb.X(); - break; - case AnnotationTextAlignHorizontalEnum::RIGHT: - dx = -ub.X(); - break; - } - - switch (annotationText.getVerticalAlignment()) { - case AnnotationTextAlignVerticalEnum::BOTTOM: - dy = -lb.Y(); - break; - case AnnotationTextAlignVerticalEnum::MIDDLE: - dy = -((lb.Y() + ub.Y()) / 2.0); - break; - case AnnotationTextAlignVerticalEnum::TOP: - dy = -ub.Y(); - break; - } - - glPushAttrib(GL_POLYGON_BIT); - - const double doubleNormalXYZ[3] { - normalVector[0], - normalVector[1], - normalVector[2] - }; - - double bottomLeft[3] { 0.0, 0.0, 0.0 }; - double bottomRight[3] { 0.0, 0.0, 0.0 }; - double topLeft[3] { 0.0, 0.0, 0.0 }; - double topRight[3] { 0.0, 0.0, 0.0 }; - double underlineStart[3] { 0.0, 0.0, 0.0 }; - double underlineEnd[3] { 0.0, 0.0, 0.0 }; - - getBoundsForTextInModelSpace(annotationText, modelSpaceScaling, heightOrWidthForPercentageSizeText, flags, - bottomLeft, bottomRight, topRight, topLeft, - underlineStart, underlineEnd); - - uint8_t backgroundColor[4]; - annotationText.getBackgroundColorRGBA(backgroundColor); - if (backgroundColor[3] > 0) { - - GraphicsPrimitiveV3fN3f primitive(GraphicsPrimitive::PrimitiveType::OPENGL_TRIANGLE_STRIP, - backgroundColor); - primitive.addVertex(topLeft, doubleNormalXYZ); - primitive.addVertex(bottomLeft, doubleNormalXYZ); - primitive.addVertex(topRight, doubleNormalXYZ); - primitive.addVertex(bottomRight, doubleNormalXYZ); - GraphicsEngineDataOpenGL::draw(&primitive); - - /* - * So that text and background do not mix together - * in the Z-buffer - */ - glEnable(GL_POLYGON_OFFSET_FILL); - glEnable(GL_POLYGON_OFFSET_LINE); - glEnable(GL_POLYGON_OFFSET_POINT); - glPolygonOffset(-1.0, 1.0); - } - - uint8_t textRGBA[4]; - annotationText.getTextColorRGBA(textRGBA); - - const float lineThicknessPixels = getLineThicknessPixelsInModelSpace(annotationText.getLineWidthPercentage(), - heightOrWidthForPercentageSizeText, - modelSpaceScaling); - - if (annotationText.isUnderlineStyleEnabled()) { - float underlineThickness = std::max((font->FaceSize() / 14.0), - 1.0); - underlineThickness *= modelSpaceScaling; - - GraphicsPrimitiveV3f linePrimitive(GraphicsPrimitive::PrimitiveType::POLYGONAL_LINES, - textRGBA); - linePrimitive.setLineWidth(GraphicsPrimitiveV3f::LineWidthType::PIXELS, - underlineThickness); // lineThicknessPixels - const float floatUnderlineStart[3] = { - static_cast(underlineStart[0]), - static_cast(underlineStart[1]), - static_cast(underlineStart[2]) - }; - const float floatUnderlineEnd[3] = { - static_cast(underlineEnd[0]), - static_cast(underlineEnd[1]), - static_cast(underlineEnd[2]) - }; - linePrimitive.addVertex(floatUnderlineStart); - linePrimitive.addVertex(floatUnderlineEnd); - GraphicsEngineDataOpenGL::draw(&linePrimitive); - } - - if (annotationText.getLineColor() != CaretColorEnum::NONE) { - glPushMatrix(); - - uint8_t foregroundRgba[4]; - annotationText.getLineColorRGBA(foregroundRgba); - GraphicsPrimitiveV3fN3f primitive(GraphicsPrimitive::PrimitiveType::OPENGL_LINE_LOOP, - foregroundRgba); - primitive.addVertex(topLeft, doubleNormalXYZ); - primitive.addVertex(bottomLeft, doubleNormalXYZ); - primitive.addVertex(bottomRight, doubleNormalXYZ); - primitive.addVertex(topRight, doubleNormalXYZ); - primitive.setLineWidth(GraphicsPrimitive::LineWidthType::PIXELS, lineThicknessPixels); - GraphicsEngineDataOpenGL::draw(&primitive); - - glPopMatrix(); - } - - glTranslatef(dx, dy, 0.0); - - glColor3ubv(textRGBA); - font->Render(wideCharString); - - annotationText.setFontTooSmallWhenLastDrawn(false); - - glPopAttrib(); - } - - -#endif // HAVE_FREETYPE -} - - diff --git a/src/Brain/FtglFontTextRenderer.h b/src/Brain/FtglFontTextRenderer.h index a170eab1e..eab99f2bf 100644 --- a/src/Brain/FtglFontTextRenderer.h +++ b/src/Brain/FtglFontTextRenderer.h @@ -64,12 +64,6 @@ namespace caret { const float normalVector[3], const DrawingFlags& flags) override; - virtual void OLDdrawTextInModelSpace(const AnnotationText& annotationText, - const float modelSpaceScaling, - const float heightOrWidthForPercentageSizeText, - const float normalVector[3], - const DrawingFlags& flags); - virtual void getTextWidthHeightInPixels(const AnnotationText& annotationText, const DrawingFlags& flags, const double viewportWidth, -- GitLab From 589694e8c63daa341b5f2d380406c2a8abed5497 Mon Sep 17 00:00:00 2001 From: John Harwell Date: Tue, 21 Aug 2018 15:18:36 -0500 Subject: [PATCH 022/427] Revised and simplified #ifdef/#endif for FTGL supported and unsupported. --- src/Brain/FtglFontTextRenderer.cxx | 44 +++------------------------ src/Brain/FtglFontTextRenderer.h | 6 ++++ src/GuiQt/BrainOpenGLWidget.cxx | 8 ++++- src/Operations/OperationShowScene.cxx | 5 +++ 4 files changed, 22 insertions(+), 41 deletions(-) diff --git a/src/Brain/FtglFontTextRenderer.cxx b/src/Brain/FtglFontTextRenderer.cxx index e7722526e..ae2a4b11a 100644 --- a/src/Brain/FtglFontTextRenderer.cxx +++ b/src/Brain/FtglFontTextRenderer.cxx @@ -63,6 +63,8 @@ ** ****************************************************************************/ +#ifdef HAVE_FREETYPE + #define __FTGL_FONT_TEXT_RENDERER_DECLARE__ #include "FtglFontTextRenderer.h" #undef __FTGL_FONT_TEXT_RENDERER_DECLARE__ @@ -90,10 +92,8 @@ #include "MathFunctions.h" #include "Matrix4x4.h" -#ifdef HAVE_FREETYPE #include using namespace FTGL; -#endif // HAVE_FREETYPE using namespace caret; @@ -126,7 +126,6 @@ FtglFontTextRenderer::FtglFontTextRenderer() : BrainOpenGLTextRenderInterface() { m_defaultFont = NULL; -#ifdef HAVE_FREETYPE AnnotationPointSizeText defaultAnnotationText(AnnotationAttributesDefaultTypeEnum::NORMAL); defaultAnnotationText.setFontPointSize(AnnotationTextFontPointSizeEnum::SIZE14); defaultAnnotationText.setFont(AnnotationTextFontNameEnum::VERA); @@ -136,7 +135,6 @@ FtglFontTextRenderer::FtglFontTextRenderer() m_defaultFont = getFont(defaultAnnotationText, FtglFontTypeEnum::TEXTURE, true); -#endif // HAVE_FREETYPE m_depthTestingStatus = DEPTH_TEST_NO; BrainOpenGL::getMinMaxLineWidth(m_lineWidthMinimum, m_lineWidthMaximum); @@ -147,7 +145,6 @@ FtglFontTextRenderer::FtglFontTextRenderer() */ FtglFontTextRenderer::~FtglFontTextRenderer() { -#ifdef HAVE_FREETYPE for (FONT_MAP_ITERATOR iter = m_fontNameToFontMap.begin(); iter != m_fontNameToFontMap.end(); iter++) { @@ -160,7 +157,6 @@ FtglFontTextRenderer::~FtglFontTextRenderer() * in m_fontNameToFontMap. Doing so would cause * a double delete. */ -#endif // HAVE_FREETYPE } /** @@ -194,7 +190,6 @@ FtglFontTextRenderer::getFont(const AnnotationText& annotationText, const float heightOrWidthForPercentageSizeText, const bool creatingDefaultFontFlag) { -#ifdef HAVE_FREETYPE int32_t viewportWidth = m_viewportWidth; int32_t viewportHeight = m_viewportHeight; @@ -311,11 +306,6 @@ FtglFontTextRenderer::getFont(const AnnotationText& annotationText, */ annotationText.setFontTooSmallWhenLastDrawn(false); return m_defaultFont; - -#else // HAVE_FREETYPE - CaretLogSevere("Trying to use FTGL Font rendering but FTGL is not valid."); - return NULL; -#endif // HAVE_FREETYPE } @@ -342,7 +332,6 @@ FtglFontTextRenderer::getFont(const AnnotationText& annotationText, ftglFontType, -1.0, creatingDefaultFontFlag); -#ifdef HAVE_FREETYPE int32_t viewportWidth = m_viewportWidth; int32_t viewportHeight = m_viewportHeight; @@ -442,11 +431,6 @@ FtglFontTextRenderer::getFont(const AnnotationText& annotationText, */ annotationText.setFontTooSmallWhenLastDrawn(false); return m_defaultFont; - -#else // HAVE_FREETYPE - CaretLogSevere("Trying to use FTGL Font rendering but FTGL is not valid."); - return NULL; -#endif // HAVE_FREETYPE } /** @@ -485,7 +469,6 @@ void FtglFontTextRenderer::drawTextAtViewportCoordinatesInternal(const AnnotationText& annotationText, const TextStringGroup& textStringGroup) { -#ifdef HAVE_FREETYPE FTFont* font = getFont(annotationText, FtglFontTypeEnum::TEXTURE, false); @@ -664,10 +647,6 @@ FtglFontTextRenderer::drawTextAtViewportCoordinatesInternal(const AnnotationText + annotationText.getText()); restoreStateOfOpenGL(); - -#else // HAVE_FREETYPE - CaretLogSevere("Trying to use FTGL Font rendering but it cannot be used due to FreeType not found."); -#endif // HAVE_FREETYPE } /** @@ -1402,7 +1381,6 @@ FtglFontTextRenderer::getBoundsForTextInModelSpace(const AnnotationText& annotat std::fill(topRightOut, topRightOut + 3, 0.0f); std::fill(topLeftOut, topLeftOut + 3, 0.0f); -#ifdef HAVE_FREETYPE FTFont* font = getFont(annotationText, FtglFontTypeEnum::POLYGON, heightOrWidthForPercentageSizeText, @@ -1543,7 +1521,6 @@ FtglFontTextRenderer::getBoundsForTextInModelSpace(const AnnotationText& annotat MathFunctions::expandLinePixels3D(underlineStartOut, copyTopLeft, boxExpansion * 0.5); MathFunctions::expandLinePixels3D(underlineEndOut, copyTopRight, boxExpansion * 0.5); } -#endif // HAVE_FREETYPE } /** @@ -1594,7 +1571,6 @@ FtglFontTextRenderer::drawTextInModelSpace(const AnnotationText& annotationText, const float normalVector[3], const DrawingFlags& flags) { -#ifdef HAVE_FREETYPE FTFont* font = getFont(annotationText, FtglFontTypeEnum::POLYGON, heightOrWidthForPercentageSizeText, @@ -1640,7 +1616,6 @@ FtglFontTextRenderer::drawTextInModelSpace(const AnnotationText& annotationText, heightOrWidthForPercentageSizeText, normalVector, flags); -#endif // HAVE_FREETYPE } @@ -1663,7 +1638,6 @@ FtglFontTextRenderer::drawTextInModelSpaceInternal(const AnnotationText& annotat const float normalVector[3], const DrawingFlags& flags) { -#ifdef HAVE_FREETYPE FTFont* font = getFont(annotationText, FtglFontTypeEnum::POLYGON, heightOrWidthForPercentageSizeText, @@ -1806,10 +1780,6 @@ FtglFontTextRenderer::drawTextInModelSpaceInternal(const AnnotationText& annotat + annotationText.getText()); restoreStateOfOpenGL(); - -#else // HAVE_FREETYPE - CaretLogSevere("Trying to use FTGL Font rendering but it cannot be used due to FreeType not found."); -#endif // HAVE_FREETYPE } @@ -1885,7 +1855,6 @@ FtglFontTextRenderer::FontData::FontData(const AnnotationText& annotationText, m_valid = false; m_font = NULL; -#ifdef HAVE_FREETYPE const AnnotationTextFontNameEnum::Enum fontName = annotationText.getFont(); AString fontFileName = AnnotationTextFontNameEnum::getResourceFontFileName(fontName); @@ -1983,7 +1952,6 @@ FtglFontTextRenderer::FontData::FontData(const AnnotationText& annotationText, m_font = NULL; } } -#endif // HAVE_FREETYPE } /** @@ -1991,12 +1959,10 @@ FtglFontTextRenderer::FontData::FontData(const AnnotationText& annotationText, */ FtglFontTextRenderer::FontData::~FontData() { -#ifdef HAVE_FREETYPE if (m_font != NULL) { delete m_font; m_font = NULL; } -#endif // HAVE_FREETYPE } /** @@ -2130,7 +2096,6 @@ m_stringGlyphsMaxX(0.0), m_stringGlyphsMinY(0.0), m_stringGlyphsMaxY(0.0) { -#ifdef HAVE_FREETYPE /* * Split the string into individual characters. */ @@ -2190,7 +2155,6 @@ m_stringGlyphsMaxY(0.0) * Set the bounds of the characters in this string. */ setGlyphBounds(); -#endif // HAVE_FREETYPE } /** @@ -2481,7 +2445,6 @@ m_viewportBoundsMinY(0.0), m_viewportBoundsMaxY(0.0), m_textDrawingSpace(TextDrawingSpace::VIEWPORT) { -#ifdef HAVE_FREETYPE CaretAssert(font); m_textDrawingSpace = TextDrawingSpace::VIEWPORT; @@ -2551,7 +2514,6 @@ m_textDrawingSpace(TextDrawingSpace::VIEWPORT) * Alignment moves text so bounds need to be updated */ updateTextBounds(); -#endif // HAVE_FREETYPE } /** @@ -2956,3 +2918,5 @@ FtglFontTextRenderer::TextStringGroup::applyAlignmentsToTextStrings() break; } } + +#endif // HAVE_FREETYPE diff --git a/src/Brain/FtglFontTextRenderer.h b/src/Brain/FtglFontTextRenderer.h index eab99f2bf..aadfa2619 100644 --- a/src/Brain/FtglFontTextRenderer.h +++ b/src/Brain/FtglFontTextRenderer.h @@ -1,6 +1,9 @@ + #ifndef __FTGL_FONT_TEXT_RENDERER_H__ #define __FTGL_FONT_TEXT_RENDERER_H__ +#ifdef HAVE_FREETYPE + /*LICENSE_START*/ /* * Copyright (C) 2014 Washington University School of Medicine @@ -416,4 +419,7 @@ namespace caret { #endif // __FTGL_FONT_TEXT_RENDERER_DECLARE__ } // namespace + +#endif // HAVE_FREETYPE + #endif //__FTGL_FONT_TEXT_RENDERER_H__ diff --git a/src/GuiQt/BrainOpenGLWidget.cxx b/src/GuiQt/BrainOpenGLWidget.cxx index e2a9b86c4..7515d232c 100644 --- a/src/GuiQt/BrainOpenGLWidget.cxx +++ b/src/GuiQt/BrainOpenGLWidget.cxx @@ -242,13 +242,19 @@ BrainOpenGLWidget::initializeGL() * OpenGL drawing will take ownership of the text renderer * and handle deletion of the text renderer. */ - BrainOpenGLTextRenderInterface* textRenderer = new FtglFontTextRenderer(); + BrainOpenGLTextRenderInterface* textRenderer = NULL; +#ifdef HAVE_FREETYPE + textRenderer = new FtglFontTextRenderer(); if (! textRenderer->isValid()) { delete textRenderer; textRenderer = NULL; CaretLogWarning("Unable to create FTGL Font Renderer.\n" "No text will be available in graphics window."); } +#else + CaretLogWarning("Unable to create FTGL Font Renderer due to FreeType not found during configuration.\n" + "No text will be available in graphics window."); +#endif if (textRenderer == NULL) { textRenderer = new DummyFontTextRenderer(); } diff --git a/src/Operations/OperationShowScene.cxx b/src/Operations/OperationShowScene.cxx index 0c1977d88..c459cfa87 100644 --- a/src/Operations/OperationShowScene.cxx +++ b/src/Operations/OperationShowScene.cxx @@ -749,6 +749,7 @@ OperationShowScene::createBrainOpenGL() */ BrainOpenGLTextRenderInterface* textRenderer = NULL; if (textRenderer == NULL) { +#ifdef HAVE_FREETYPE textRenderer = new FtglFontTextRenderer(); if (! textRenderer->isValid()) { delete textRenderer; @@ -756,6 +757,10 @@ OperationShowScene::createBrainOpenGL() CaretLogWarning("Unable to create FTGL Font Renderer.\n" "No text will be available in graphics window."); } +#else + CaretLogWarning("Unable to create FTGL Font Renderer due to FreeType not found during configuration.\n" + "No text will be available in graphics window."); +#endif } if (textRenderer == NULL) { textRenderer = new DummyFontTextRenderer(); -- GitLab From 7324dd1929b3b08b0a0bae2ca208c245cc227491 Mon Sep 17 00:00:00 2001 From: John Harwell Date: Tue, 21 Aug 2018 15:33:22 -0500 Subject: [PATCH 023/427] Cleaned out some unused code. --- ...inOpenGLAnnotationDrawingFixedPipeline.cxx | 2 +- src/Brain/FtglFontTextRenderer.cxx | 104 +----------------- 2 files changed, 2 insertions(+), 104 deletions(-) diff --git a/src/Brain/BrainOpenGLAnnotationDrawingFixedPipeline.cxx b/src/Brain/BrainOpenGLAnnotationDrawingFixedPipeline.cxx index edfc39972..98ee6d00c 100644 --- a/src/Brain/BrainOpenGLAnnotationDrawingFixedPipeline.cxx +++ b/src/Brain/BrainOpenGLAnnotationDrawingFixedPipeline.cxx @@ -270,7 +270,7 @@ BrainOpenGLAnnotationDrawingFixedPipeline::getAnnotationDrawingSpaceCoordinate(c } break; case AnnotationSurfaceOffsetVectorTypeEnum::TANGENT: - CaretAssertToDoFatal(); + CaretAssert(0); break; } diff --git a/src/Brain/FtglFontTextRenderer.cxx b/src/Brain/FtglFontTextRenderer.cxx index ae2a4b11a..5ae01f387 100644 --- a/src/Brain/FtglFontTextRenderer.cxx +++ b/src/Brain/FtglFontTextRenderer.cxx @@ -1419,108 +1419,6 @@ FtglFontTextRenderer::getBoundsForTextInModelSpace(const AnnotationText& annotat underlineEndOut[0] = bottomRightOut[0]; underlineEndOut[1] = bottomRightOut[1]; underlineEndOut[2] = bottomRightOut[2]; - - return; - - - - - - /* remove code below !!! */ - CaretAssertToDoFatal(); - - - - - - - AString text = (flags.isDrawSubstitutedText() - ? annotationText.getTextWithSubstitutionsApplied() - : annotationText.getText()); - if (text.isEmpty()) { - return; - } - - std::wstring wideString = text.toStdWString(); - const wchar_t* wideCharString = wideString.c_str(); - - FTBBox bounds = font->BBox(wideCharString); - if (bounds.IsValid()) { - FTPoint lb = bounds.Lower(); - FTPoint ub = bounds.Upper(); -// std::cout << "Lower: " << lb.X() << ", " << lb.Y() << ", " << lb.Z() << std::endl; -// std::cout << "Upper: " << ub.X() << ", " << ub.Y() << ", " << ub.Z() << std::endl; - - float dx(0.0); - float dy(0.0f); - switch (annotationText.getHorizontalAlignment()) { - case AnnotationTextAlignHorizontalEnum::CENTER: - dx = -((lb.X() + ub.X()) / 2.0); - break; - case AnnotationTextAlignHorizontalEnum::LEFT: - dx = -lb.X(); - break; - case AnnotationTextAlignHorizontalEnum::RIGHT: - dx = -ub.X(); - break; - } - - switch (annotationText.getVerticalAlignment()) { - case AnnotationTextAlignVerticalEnum::BOTTOM: - dy = -lb.Y(); - break; - case AnnotationTextAlignVerticalEnum::MIDDLE: - dy = -((lb.Y() + ub.Y()) / 2.0); - break; - case AnnotationTextAlignVerticalEnum::TOP: - dy = -ub.Y(); - break; - } - - const float z(0.0f); - bottomLeftOut[0] = dx + lb.Xf(); - bottomLeftOut[1] = dy + lb.Yf(); - bottomLeftOut[2] = z; - bottomRightOut[0] = dx + ub.Xf(); - bottomRightOut[1] = dy + lb.Yf(); - bottomRightOut[2] = z; - topLeftOut[0] = dx + lb.Xf(); - topLeftOut[1] = dy + ub.Yf(); - topLeftOut[2] = z; - topRightOut[0] = dx + ub.Xf(); - topRightOut[1] = dy + ub.Yf(); - topRightOut[2] = z; - - double copyTopLeft[3] { - topLeftOut[0], - topLeftOut[1], - topLeftOut[2] - }; - double copyTopRight[3] { - topRightOut[0], - topRightOut[1], - topRightOut[2] - }; - - underlineStartOut[0] = bottomLeftOut[0]; - underlineStartOut[1] = bottomLeftOut[1]; - underlineStartOut[2] = bottomLeftOut[2]; - underlineEndOut[0] = bottomRightOut[0]; - underlineEndOut[1] = bottomRightOut[1]; - underlineEndOut[2] = bottomRightOut[2]; - - /* - * No scaling since we want the box to tightly enclose the text. - */ - const float noScalingOfModel(1.0); - const float boxExpansion(getLineThicknessPixelsInModelSpace(annotationText.getLineWidthPercentage(), - heightOrWidthForPercentageSizeText, - noScalingOfModel)); - MathFunctions::expandBoxPixels3D(bottomLeftOut, bottomRightOut, topRightOut, topLeftOut, - boxExpansion); - MathFunctions::expandLinePixels3D(underlineStartOut, copyTopLeft, boxExpansion * 0.5); - MathFunctions::expandLinePixels3D(underlineEndOut, copyTopRight, boxExpansion * 0.5); - } } /** @@ -1636,7 +1534,7 @@ FtglFontTextRenderer::drawTextInModelSpaceInternal(const AnnotationText& annotat const TextStringGroup& textStringGroup, const float heightOrWidthForPercentageSizeText, const float normalVector[3], - const DrawingFlags& flags) + const DrawingFlags& /*flags*/) { FTFont* font = getFont(annotationText, FtglFontTypeEnum::POLYGON, -- GitLab From 1c41746854ce84d67ebd6bfe5c00e0865702c8de Mon Sep 17 00:00:00 2001 From: John Harwell Date: Tue, 21 Aug 2018 16:05:35 -0500 Subject: [PATCH 024/427] Fixed outline around horizontally oriented text that spanned multiple lines due to new line characters. --- src/Brain/FtglFontTextRenderer.cxx | 32 +++++++++++++-------------- src/Graphics/GraphicsPrimitiveV3f.cxx | 12 ++++++++++ src/Graphics/GraphicsPrimitiveV3f.h | 2 ++ 3 files changed, 29 insertions(+), 17 deletions(-) diff --git a/src/Brain/FtglFontTextRenderer.cxx b/src/Brain/FtglFontTextRenderer.cxx index 5ae01f387..be86b99c7 100644 --- a/src/Brain/FtglFontTextRenderer.cxx +++ b/src/Brain/FtglFontTextRenderer.cxx @@ -620,25 +620,23 @@ FtglFontTextRenderer::drawTextAtViewportCoordinatesInternal(const AnnotationText glPopMatrix(); } - - if (ts->m_outlineThickness > 0.0) { - glPushMatrix(); - glTranslated(ts->m_viewportX - rotationPointXYZ[0], ts->m_viewportY - rotationPointXYZ[1], 0.0); - - uint8_t foregroundRgba[4]; - annotationText.getLineColorRGBA(foregroundRgba); - drawOutline(ts->m_stringGlyphsMinX, - ts->m_stringGlyphsMaxX, - ts->m_stringGlyphsMinY, - ts->m_stringGlyphsMaxY, - z, - ts->m_outlineThickness, - foregroundRgba); - - glPopMatrix(); - } } + glLoadIdentity(); + + uint8_t foregroundRgba[4]; + annotationText.getLineColorRGBA(foregroundRgba); + if (foregroundRgba[3] > 0) { + GraphicsPrimitiveV3f primitive(GraphicsPrimitive::PrimitiveType::POLYGONAL_LINE_LOOP_MITER_JOIN, + foregroundRgba); + primitive.addVertex(topLeft); + primitive.addVertex(bottomLeft); + primitive.addVertex(bottomRight); + primitive.addVertex(topRight); + primitive.setLineWidth(GraphicsPrimitive::LineWidthType::PERCENTAGE_VIEWPORT_HEIGHT, + annotationText.getLineWidthPercentage()); + GraphicsEngineDataOpenGL::draw(&primitive); + } glPopMatrix(); BrainOpenGL::testForOpenGLError("At end of " diff --git a/src/Graphics/GraphicsPrimitiveV3f.cxx b/src/Graphics/GraphicsPrimitiveV3f.cxx index f9e36fa1e..06797069d 100644 --- a/src/Graphics/GraphicsPrimitiveV3f.cxx +++ b/src/Graphics/GraphicsPrimitiveV3f.cxx @@ -118,6 +118,18 @@ GraphicsPrimitiveV3f::copyHelperGraphicsPrimitiveV3f(const GraphicsPrimitiveV3f& } } +/** + * Add a vertex. + * + * @param xyz + * Coordinate of vertex. + */ +void +GraphicsPrimitiveV3f::addVertex(const double xyz[3]) +{ + addVertex(xyz[0], xyz[1], xyz[2]); +} + /** * Add a vertex. * diff --git a/src/Graphics/GraphicsPrimitiveV3f.h b/src/Graphics/GraphicsPrimitiveV3f.h index f11925420..ff93e641f 100644 --- a/src/Graphics/GraphicsPrimitiveV3f.h +++ b/src/Graphics/GraphicsPrimitiveV3f.h @@ -46,6 +46,8 @@ namespace caret { void addVertex(const float xyz[3]); + void addVertex(const double xyz[3]); + void addVertex(const float x, const float y, const float z); -- GitLab From a69b58cd7d80b25402415491693ca4adefe5d94b Mon Sep 17 00:00:00 2001 From: John Harwell Date: Wed, 22 Aug 2018 09:45:02 -0500 Subject: [PATCH 025/427] WB-636 Texture Based Annotations: Textured text annotations use a Polygon font (others use a OpenGL texture font). Since there is no minimum size for the polygon font, never set the "too small status" it (shows font size in red in Toolbar). --- src/Brain/FtglFontTextRenderer.cxx | 111 +++-------------------------- 1 file changed, 9 insertions(+), 102 deletions(-) diff --git a/src/Brain/FtglFontTextRenderer.cxx b/src/Brain/FtglFontTextRenderer.cxx index be86b99c7..27cb2b5f6 100644 --- a/src/Brain/FtglFontTextRenderer.cxx +++ b/src/Brain/FtglFontTextRenderer.cxx @@ -219,13 +219,17 @@ FtglFontTextRenderer::getFont(const AnnotationText& annotationText, viewportWidth = heightOrWidthForPercentageSizeText; } + bool tooSmallTextHeightValidFlag = false; AString fontTypeString; switch (ftglFontType) { case FtglFontTypeEnum::POLYGON: fontTypeString = "Polygon_"; + /* Polygon text is never too small */ + tooSmallTextHeightValidFlag = false; break; case FtglFontTypeEnum::TEXTURE: fontTypeString = "Texture_"; + tooSmallTextHeightValidFlag = true; break; } @@ -245,7 +249,8 @@ FtglFontTextRenderer::getFont(const AnnotationText& annotationText, * Set font "too small" status */ const bool tooSmallFlag = (fontData->m_font->FaceSize() <= AnnotationText::getTooSmallTextHeight()); - annotationText.setFontTooSmallWhenLastDrawn(tooSmallFlag); + annotationText.setFontTooSmallWhenLastDrawn(tooSmallFlag + && tooSmallTextHeightValidFlag); return fontData->m_font; } @@ -270,7 +275,8 @@ FtglFontTextRenderer::getFont(const AnnotationText& annotationText, * Set font "too small" status */ const bool tooSmallFlag = (fontData->m_font->FaceSize() <= AnnotationText::getTooSmallTextHeight()); - annotationText.setFontTooSmallWhenLastDrawn(tooSmallFlag); + annotationText.setFontTooSmallWhenLastDrawn(tooSmallFlag + && tooSmallTextHeightValidFlag); return fontData->m_font; } @@ -330,107 +336,8 @@ FtglFontTextRenderer::getFont(const AnnotationText& annotationText, { return getFont(annotationText, ftglFontType, - -1.0, + -1.0, // negative indicates invalid height for percentage text creatingDefaultFontFlag); - int32_t viewportWidth = m_viewportWidth; - int32_t viewportHeight = m_viewportHeight; - - switch (annotationText.getCoordinateSpace()) { - case AnnotationCoordinateSpaceEnum::CHART: - break; - case AnnotationCoordinateSpaceEnum::STEREOTAXIC: - break; - case AnnotationCoordinateSpaceEnum::SURFACE: - break; - case AnnotationCoordinateSpaceEnum::TAB: - break; - case AnnotationCoordinateSpaceEnum::VIEWPORT: - { - int32_t vp[4]; - annotationText.getViewportCoordinateSpaceViewport(vp); - viewportWidth = vp[2]; - viewportHeight = vp[3]; - } - break; - case AnnotationCoordinateSpaceEnum::WINDOW: - break; - } - const AString fontName = annotationText.getFontRenderingEncodedName(viewportWidth, - viewportHeight); - - /* - * Has the font already has been created? - */ - FONT_MAP_ITERATOR fontIter = m_fontNameToFontMap.find(fontName); - if (fontIter != m_fontNameToFontMap.end()) { - FontData* fontData = fontIter->second; - CaretAssert(fontData); - - /* - * Set font "too small" status - */ - const bool tooSmallFlag = (fontData->m_font->FaceSize() <= AnnotationText::getTooSmallTextHeight()); - annotationText.setFontTooSmallWhenLastDrawn(tooSmallFlag); - - return fontData->m_font; - } - - /* - * Create and save the font - */ - FontData* fontData = new FontData(annotationText, - ftglFontType, - viewportWidth, - viewportHeight); - if (fontData->m_valid) { - /* - * Request font is valid. - */ - m_fontNameToFontMap.insert(std::make_pair(fontName, - fontData)); - CaretLogFine("Created font with encoded name " - + fontName); - - /* - * Set font "too small" status - */ - const bool tooSmallFlag = (fontData->m_font->FaceSize() <= AnnotationText::getTooSmallTextHeight()); - annotationText.setFontTooSmallWhenLastDrawn(tooSmallFlag); - - return fontData->m_font; - } - else { - /* - * Error creating font - */ - delete fontData; - fontData = NULL; - - /* - * Issue a message about failure to create font but - * don't print same message more than once. - */ - if (std::find(m_failedFontNames.begin(), - m_failedFontNames.end(), - fontName) == m_failedFontNames.end()) { - m_failedFontNames.insert(fontName); - CaretLogSevere("Failed to create font with encoded name " - + fontName); - } - } - - /* - * Were we trying to create the default font? - */ - if (creatingDefaultFontFlag) { - return NULL; - } - - /* - * Failed so use the default font. - */ - annotationText.setFontTooSmallWhenLastDrawn(false); - return m_defaultFont; } /** -- GitLab From 27d83f61c0395cbc0a9076e48167f0c69cbec513 Mon Sep 17 00:00:00 2001 From: John Harwell Date: Fri, 24 Aug 2018 15:36:54 -0500 Subject: [PATCH 026/427] Draw outline around text drawn in 'tangent space' using polygonal lines. --- src/Brain/FtglFontTextRenderer.cxx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Brain/FtglFontTextRenderer.cxx b/src/Brain/FtglFontTextRenderer.cxx index 27cb2b5f6..dadd767b9 100644 --- a/src/Brain/FtglFontTextRenderer.cxx +++ b/src/Brain/FtglFontTextRenderer.cxx @@ -1558,7 +1558,7 @@ FtglFontTextRenderer::drawTextInModelSpaceInternal(const AnnotationText& annotat modelSpaceScaling); uint8_t foregroundRgba[4]; annotationText.getLineColorRGBA(foregroundRgba); - GraphicsPrimitiveV3fN3f primitive(GraphicsPrimitive::PrimitiveType::OPENGL_LINE_LOOP, + GraphicsPrimitiveV3fN3f primitive(GraphicsPrimitive::PrimitiveType::POLYGONAL_LINE_LOOP_MITER_JOIN, foregroundRgba); double doubleNormalXYZ[3] { normalVector[0], -- GitLab From 749b5c87f23c22c32982ba55b3333b3b4523f884 Mon Sep 17 00:00:00 2001 From: John Harwell Date: Fri, 24 Aug 2018 16:55:36 -0500 Subject: [PATCH 027/427] The enumerated type SURFACE_NORMAL in AnnotationSurfaceOffsetVectorTypeEnum was spelled incorrectly (SURACE_NORMAL missing 'F') and has been corrected. The fromName() method tests for the incorrect spelling so that older scene will work. --- .../AnnotationSurfaceOffsetVectorTypeEnum.cxx | 19 ++++++++++++++----- .../AnnotationSurfaceOffsetVectorTypeEnum.h | 2 +- ...inOpenGLAnnotationDrawingFixedPipeline.cxx | 8 ++++---- 3 files changed, 19 insertions(+), 10 deletions(-) diff --git a/src/Annotations/AnnotationSurfaceOffsetVectorTypeEnum.cxx b/src/Annotations/AnnotationSurfaceOffsetVectorTypeEnum.cxx index 35e639199..1e29aa0dd 100644 --- a/src/Annotations/AnnotationSurfaceOffsetVectorTypeEnum.cxx +++ b/src/Annotations/AnnotationSurfaceOffsetVectorTypeEnum.cxx @@ -114,10 +114,10 @@ AnnotationSurfaceOffsetVectorTypeEnum::initialize() "C", "Centroid Thru Vertex")); - enumData.push_back(AnnotationSurfaceOffsetVectorTypeEnum(SURACE_NORMAL, - "SURACE_NORMAL", + enumData.push_back(AnnotationSurfaceOffsetVectorTypeEnum(SURFACE_NORMAL, + "SURFACE_NORMAL", "N", - "Surace Normal")); + "Surface Normal")); enumData.push_back(AnnotationSurfaceOffsetVectorTypeEnum(TANGENT, "TANGENT", @@ -165,7 +165,7 @@ AnnotationSurfaceOffsetVectorTypeEnum::toName(Enum enumValue) { /** * Get an enumerated value corresponding to its name. - * @param name + * @param nameIn * Name of enumerated value. * @param isValidOut * If not NULL, it is set indicating that a @@ -174,10 +174,19 @@ AnnotationSurfaceOffsetVectorTypeEnum::toName(Enum enumValue) { * Enumerated value. */ AnnotationSurfaceOffsetVectorTypeEnum::Enum -AnnotationSurfaceOffsetVectorTypeEnum::fromName(const AString& name, bool* isValidOut) +AnnotationSurfaceOffsetVectorTypeEnum::fromName(const AString& nameIn, bool* isValidOut) { if (initializedFlag == false) initialize(); + /* + * SURFACE_NORMAL was spelled incorrectly prior to 24aug2018 + * (was missing 'F'). + */ + AString name(nameIn); + if (name == "SURACE_NORMAL") { + name = "SURFACE_NORMAL"; + } + bool validFlag = false; Enum enumValue = AnnotationSurfaceOffsetVectorTypeEnum::enumData[0].enumValue; diff --git a/src/Annotations/AnnotationSurfaceOffsetVectorTypeEnum.h b/src/Annotations/AnnotationSurfaceOffsetVectorTypeEnum.h index a85e9314d..4aa79032f 100644 --- a/src/Annotations/AnnotationSurfaceOffsetVectorTypeEnum.h +++ b/src/Annotations/AnnotationSurfaceOffsetVectorTypeEnum.h @@ -38,7 +38,7 @@ public: /** Vector starts at centroid and extends through vertex */ CENTROID_THRU_VERTEX, /** Vector is surface normal */ - SURACE_NORMAL, + SURFACE_NORMAL, /** Tangent to surface */ TANGENT }; diff --git a/src/Brain/BrainOpenGLAnnotationDrawingFixedPipeline.cxx b/src/Brain/BrainOpenGLAnnotationDrawingFixedPipeline.cxx index 98ee6d00c..9942ea198 100644 --- a/src/Brain/BrainOpenGLAnnotationDrawingFixedPipeline.cxx +++ b/src/Brain/BrainOpenGLAnnotationDrawingFixedPipeline.cxx @@ -244,7 +244,7 @@ BrainOpenGLAnnotationDrawingFixedPipeline::getAnnotationDrawingSpaceCoordinate(c * */ if (surfaceDisplayed->getSurfaceType() == SurfaceTypeEnum::FLAT) { - annotationOffsetVector = AnnotationSurfaceOffsetVectorTypeEnum::SURACE_NORMAL; + annotationOffsetVector = AnnotationSurfaceOffsetVectorTypeEnum::SURFACE_NORMAL; } switch (annotationOffsetVector) { @@ -261,7 +261,7 @@ BrainOpenGLAnnotationDrawingFixedPipeline::getAnnotationDrawingSpaceCoordinate(c MathFunctions::normalizeVector(offsetUnitVector); } break; - case AnnotationSurfaceOffsetVectorTypeEnum::SURACE_NORMAL: + case AnnotationSurfaceOffsetVectorTypeEnum::SURFACE_NORMAL: { const float* normalVector = surfaceDisplayed->getNormalVector(annotationNodeIndex); offsetUnitVector[0] = normalVector[0]; @@ -2767,7 +2767,7 @@ BrainOpenGLAnnotationDrawingFixedPipeline::drawText(AnnotationFile* annotationFi switch (text->getCoordinate()->getSurfaceOffsetVectorType()) { case AnnotationSurfaceOffsetVectorTypeEnum::CENTROID_THRU_VERTEX: break; - case AnnotationSurfaceOffsetVectorTypeEnum::SURACE_NORMAL: + case AnnotationSurfaceOffsetVectorTypeEnum::SURFACE_NORMAL: break; case AnnotationSurfaceOffsetVectorTypeEnum::TANGENT: /* @@ -3634,7 +3634,7 @@ BrainOpenGLAnnotationDrawingFixedPipeline::drawAnnotationTwoDimSizingHandles(Ann switch (coord->getSurfaceOffsetVectorType()) { case AnnotationSurfaceOffsetVectorTypeEnum::CENTROID_THRU_VERTEX: break; - case AnnotationSurfaceOffsetVectorTypeEnum::SURACE_NORMAL: + case AnnotationSurfaceOffsetVectorTypeEnum::SURFACE_NORMAL: break; case AnnotationSurfaceOffsetVectorTypeEnum::TANGENT: modelSpaceTangentTextFlag = true; -- GitLab From e44db4bed805e67860e908a20a84bfc92352beb5 Mon Sep 17 00:00:00 2001 From: John Harwell Date: Tue, 28 Aug 2018 15:25:58 -0500 Subject: [PATCH 028/427] Added methods to subtract from or add to a vector. --- src/Common/MathFunctions.cxx | 34 ++++++++++++++++++++++++++++++++++ src/Common/MathFunctions.h | 6 ++++++ 2 files changed, 40 insertions(+) diff --git a/src/Common/MathFunctions.cxx b/src/Common/MathFunctions.cxx index 75a17ee44..8d8a0d436 100644 --- a/src/Common/MathFunctions.cxx +++ b/src/Common/MathFunctions.cxx @@ -672,6 +672,40 @@ MathFunctions::dotProduct( return p1[0]*p2[0] + p1[1]*p2[1] + p1[2]*p2[2]; } +/** + * Add an offset to a vector + * + * @param v + * The vector + * @param offset + * Offset added to the vector. + */ +void +MathFunctions::addOffsetToVector(double v[3], + const double offset[3]) +{ + v[0] += offset[0]; + v[1] += offset[1]; + v[2] += offset[2]; +} + +/** + * Subtract an offset from a vector + * + * @param v + * The vector + * @param offset + * Offset subtracted from the vector. + */ +void +MathFunctions::subtractOffsetFromVector(double v[3], + const double offset[3]) +{ + v[0] -= offset[0]; + v[1] -= offset[1]; + v[2] -= offset[2]; +} + /** * Calculate the area for a triangle. * @param v1 - XYZ coordinates for vertex 1 diff --git a/src/Common/MathFunctions.h b/src/Common/MathFunctions.h index 0b325b7a2..16917e608 100644 --- a/src/Common/MathFunctions.h +++ b/src/Common/MathFunctions.h @@ -172,6 +172,12 @@ public: const double p1[3], const double p2[3]); + static void addOffsetToVector(double v[3], + const double offset[3]); + + static void subtractOffsetFromVector(double v[3], + const double offset[3]); + static float triangleArea( const float v1[3], const float v2[3], -- GitLab From 327fb3085cf0d125869908f523fe9fb5a6c37900 Mon Sep 17 00:00:00 2001 From: John Harwell Date: Tue, 28 Aug 2018 15:26:56 -0500 Subject: [PATCH 029/427] Added method for drawing a rectangle, as an outline --- src/Graphics/GraphicsShape.cxx | 191 +++++++++++++++++++++++++++++++++ src/Graphics/GraphicsShape.h | 22 ++++ 2 files changed, 213 insertions(+) diff --git a/src/Graphics/GraphicsShape.cxx b/src/Graphics/GraphicsShape.cxx index dd58b47cb..845639ef2 100644 --- a/src/Graphics/GraphicsShape.cxx +++ b/src/Graphics/GraphicsShape.cxx @@ -1178,6 +1178,197 @@ GraphicsShape::updateModelMatrixToFaceViewer() glScalef(sx, sy, sz); } +/** + * Draw an rectangle outline. + * The normal vector is computed from three consecutive vertices in the rectangle. + * + * @param bottomLeft + * Bottom left vertex of the rectangle. + * @param bottomRight + * Bottom right vertex of the rectangle. + * @param topRight + * Top right vertex of the rectangle. + * @param topLeft + * Top left vertex of the rectangle. + * @param thickness + * Thickness of the outline + * @param rgba + * RGBA color for the outline. + * @param verticesInMiddleFlag + * If true, the lines are centered around the vertices. Otherwise, + * the inner sides of the lines are tangent to the vertices. + */ +void +GraphicsShape::drawOutlineRectanglePrivate(const double bottomLeft[3], + const double bottomRight[3], + const double topRight[3], + const double topLeft[3], + const double thicknessIn, + const uint8_t rgba[4], + bool verticesInMiddleFlag) +{ + double bottomLeftInner[3] = { bottomLeft[0], bottomLeft[1], bottomLeft[2] }; + double bottomLeftOuter[3] = { bottomLeft[0], bottomLeft[1], bottomLeft[2] }; + double bottomRightInner[3] = { bottomRight[0], bottomRight[1], bottomRight[2] }; + double bottomRightOuter[3] = { bottomRight[0], bottomRight[1], bottomRight[2] }; + double topRightInner[3] = { topRight[0], topRight[1], topRight[2] }; + double topRightOuter[3] = { topRight[0], topRight[1], topRight[2] }; + double topLeftInner[3] = { topLeft[0], topLeft[1], topLeft[2] }; + double topLeftOuter[3] = { topLeft[0], topLeft[1], topLeft[2] }; + + + /* + * Limit thickness of the outline to half of width or height + * when points in middle + */ + const double boxHeight = MathFunctions::distance3D(bottomLeft, topLeft); + const double boxWidth = MathFunctions::distance3D(bottomLeft, bottomRight); + double thickness = thicknessIn; + thickness /= 2.0; + if (verticesInMiddleFlag) { + thickness = std::min(thickness, + boxWidth / 2.0); + thickness = std::min(thickness, + boxHeight / 2.0); + } + + /* + * Horizontal contraction/expansion of vertices + */ + double horizVector[3]; + MathFunctions::subtractVectors(bottomRight, bottomLeft, horizVector); + MathFunctions::normalizeVector(horizVector); + horizVector[0] *= thickness; + horizVector[1] *= thickness; + horizVector[2] *= thickness; + + MathFunctions::subtractOffsetFromVector(bottomLeftOuter, horizVector); + MathFunctions::subtractOffsetFromVector(topLeftOuter, horizVector); + MathFunctions::addOffsetToVector(bottomRightOuter, horizVector); + MathFunctions::addOffsetToVector(topRightOuter, horizVector); + if (verticesInMiddleFlag) { + MathFunctions::subtractOffsetFromVector(bottomRightInner, horizVector); + MathFunctions::subtractOffsetFromVector(topRightInner, horizVector); + MathFunctions::addOffsetToVector(bottomLeftInner, horizVector); + MathFunctions::addOffsetToVector(topLeftInner, horizVector); + } + + /* + * Vertical contraction/expansion of vertices + */ + double vertVector[3]; + MathFunctions::subtractVectors(topLeft, bottomLeft, vertVector); + MathFunctions::normalizeVector(vertVector); + vertVector[0] *= thickness; + vertVector[1] *= thickness; + vertVector[2] *= thickness; + + MathFunctions::subtractOffsetFromVector(bottomRightOuter, vertVector); + MathFunctions::subtractOffsetFromVector(bottomLeftOuter, vertVector); + MathFunctions::addOffsetToVector(topLeftOuter, vertVector); + MathFunctions::addOffsetToVector(topRightOuter, vertVector); + if (verticesInMiddleFlag) { + MathFunctions::subtractOffsetFromVector(topLeftInner, vertVector); + MathFunctions::subtractOffsetFromVector(topRightInner, vertVector); + MathFunctions::addOffsetToVector(bottomRightInner, vertVector); + MathFunctions::addOffsetToVector(bottomLeftInner, vertVector); + } + + /* + * Use three consecutive vertices to calculate normal vector + */ + double normalVector[3]; + MathFunctions::normalVector(bottomRight, topRight, topLeft, normalVector); + + /* + * Draw the outline as triangles using a triangle strip + */ + GraphicsPrimitiveV3fN3f primitive(GraphicsPrimitive::PrimitiveType::OPENGL_TRIANGLE_STRIP, + rgba); + primitive.addVertex(bottomRightInner, normalVector); + primitive.addVertex(bottomRightOuter, normalVector); + primitive.addVertex(topRightInner, normalVector); + primitive.addVertex(topRightOuter, normalVector); + primitive.addVertex(topLeftInner, normalVector); + primitive.addVertex(topLeftOuter, normalVector); + primitive.addVertex(bottomLeftInner, normalVector); + primitive.addVertex(bottomLeftOuter, normalVector); + primitive.addVertex(bottomRightInner, normalVector); + primitive.addVertex(bottomRightOuter, normalVector); + + GraphicsEngineDataOpenGL::draw(&primitive); +} + +/** + * Draw an rectangle outline. + * The given points are in the middle of outline. + * The normal vector is computed from three consecutive vertices in the rectangle. + * + * @param bottomLeft + * Bottom left vertex of the rectangle. + * @param bottomRight + * Bottom right vertex of the rectangle. + * @param topRight + * Top right vertex of the rectangle. + * @param topLeft + * Top left vertex of the rectangle. + * @param thickness + * Thickness of the outline + * @param rgba + * RGBA color for the outline. + */ +void +GraphicsShape::drawOutlineRectangleVerticesInMiddle(const double bottomLeft[3], + const double bottomRight[3], + const double topRight[3], + const double topLeft[3], + const double thickness, + const uint8_t rgba[4]) +{ + drawOutlineRectanglePrivate(bottomLeft, + bottomRight, + topRight, + topLeft, + thickness, + rgba, + true); +} + +/** + * Draw an rectangle outline. + * The given points are at the inside of outline. + * The normal vector is computed from three consecutive vertices in the rectangle. + * + * @param bottomLeft + * Bottom left vertex of the rectangle. + * @param bottomRight + * Bottom right vertex of the rectangle. + * @param topRight + * Top right vertex of the rectangle. + * @param topLeft + * Top left vertex of the rectangle. + * @param thickness + * Thickness of the outline + * @param rgba + * RGBA color for the outline. + */ +void +GraphicsShape::drawOutlineRectangleVerticesAtInside(const double bottomLeft[3], + const double bottomRight[3], + const double topRight[3], + const double topLeft[3], + const double thickness, + const uint8_t rgba[4]) +{ + drawOutlineRectanglePrivate(bottomLeft, + bottomRight, + topRight, + topLeft, + thickness, + rgba, + false); +} + /** * Get a description of this object's content. * @return String describing this object's content. diff --git a/src/Graphics/GraphicsShape.h b/src/Graphics/GraphicsShape.h index db9faedbe..3634412f6 100644 --- a/src/Graphics/GraphicsShape.h +++ b/src/Graphics/GraphicsShape.h @@ -122,6 +122,20 @@ namespace caret { static void deleteAllPrimitives(); + static void drawOutlineRectangleVerticesInMiddle(const double bottomLeft[3], + const double bottomRight[3], + const double topRight[3], + const double topLeft[3], + const double thickness, + const uint8_t rgba[4]); + + static void drawOutlineRectangleVerticesAtInside(const double bottomLeft[3], + const double bottomRight[3], + const double topRight[3], + const double topLeft[3], + const double thickness, + const uint8_t rgba[4]); + // ADD_NEW_METHODS_HERE virtual AString toString() const; @@ -195,6 +209,14 @@ namespace caret { float xyzOut[3], float normalXyzOut[3]); + static void drawOutlineRectanglePrivate(const double bottomLeft[3], + const double bottomRight[3], + const double topRight[3], + const double topLeft[3], + const double thicknessIn, + const uint8_t rgba[4], + bool verticesInMiddleFlag); + static std::unique_ptr s_byteSquarePrimitive; static std::map s_byteSpherePrimitives; -- GitLab From 5d229f1f8fcba2147b2db4cfff86bf7862d1cfee Mon Sep 17 00:00:00 2001 From: John Harwell Date: Tue, 28 Aug 2018 15:27:48 -0500 Subject: [PATCH 030/427] Fixed drawing of an outline around text in model space so that the outline does not 'z-fight' with the text background. --- src/Brain/FtglFontTextRenderer.cxx | 67 ++++++++---------------------- src/Brain/FtglFontTextRenderer.h | 5 +-- 2 files changed, 19 insertions(+), 53 deletions(-) diff --git a/src/Brain/FtglFontTextRenderer.cxx b/src/Brain/FtglFontTextRenderer.cxx index dadd767b9..3c58d9c29 100644 --- a/src/Brain/FtglFontTextRenderer.cxx +++ b/src/Brain/FtglFontTextRenderer.cxx @@ -1369,9 +1369,9 @@ FtglFontTextRenderer::getLineThicknessPixelsInModelSpace(const float lineWidthPe */ void FtglFontTextRenderer::drawTextInModelSpace(const AnnotationText& annotationText, - const float modelSpaceScaling, + const float /*modelSpaceScaling*/, const float heightOrWidthForPercentageSizeText, - const float normalVector[3], + const float* /*normalVector[3]*/, const DrawingFlags& flags) { FTFont* font = getFont(annotationText, @@ -1414,32 +1414,25 @@ FtglFontTextRenderer::drawTextInModelSpace(const AnnotationText& annotationText, annotationText.getRotationAngle(), lineThicknessForViewportHeight); drawTextInModelSpaceInternal(annotationText, - modelSpaceScaling, tsg, - heightOrWidthForPercentageSizeText, - normalVector, - flags); + heightOrWidthForPercentageSizeText); } - /** * Draw the text piceces at their assigned model space. * * @param annotationText * Annotation text and attributes. - * @param textMatrix - * Text broken up into cells with model space coordinates assigned. + * @param textStringGroup + * Text broken up into characters with positions * @param heightOrWidthForPercentageSizeText * If positive, use it to override width/height of viewport. * */ void FtglFontTextRenderer::drawTextInModelSpaceInternal(const AnnotationText& annotationText, - const float modelSpaceScaling, const TextStringGroup& textStringGroup, - const float heightOrWidthForPercentageSizeText, - const float normalVector[3], - const DrawingFlags& /*flags*/) + const float heightOrWidthForPercentageSizeText) { FTFont* font = getFont(annotationText, FtglFontTypeEnum::POLYGON, @@ -1472,10 +1465,7 @@ FtglFontTextRenderer::drawTextInModelSpaceInternal(const AnnotationText& annotat glPushMatrix(); - glPolygonOffset(1.0, 1.0); - glEnable(GL_POLYGON_OFFSET_FILL); applyBackgroundColoring(textStringGroup); - glDisable(GL_POLYGON_OFFSET_FILL); applyTextColoring(annotationText); @@ -1483,6 +1473,8 @@ FtglFontTextRenderer::drawTextInModelSpaceInternal(const AnnotationText& annotat glTranslated(rotationPointXYZ[0], rotationPointXYZ[1], rotationPointXYZ[2]); glRotated(rotationAngle, 0.0, 0.0, -1.0); + glPolygonOffset(-1.0, -1.0); + glEnable(GL_POLYGON_OFFSET_FILL); for (std::vector::const_iterator iter = textStringGroup.m_textStrings.begin(); iter != textStringGroup.m_textStrings.end(); iter++) { @@ -1531,50 +1523,27 @@ FtglFontTextRenderer::drawTextInModelSpaceInternal(const AnnotationText& annotat glPopMatrix(); } - - if (ts->m_outlineThickness > 0.0) { - glPushMatrix(); - glTranslated(ts->m_viewportX - rotationPointXYZ[0], ts->m_viewportY - rotationPointXYZ[1], 0.0); - - uint8_t foregroundRgba[4]; - annotationText.getLineColorRGBA(foregroundRgba); - drawOutline(ts->m_stringGlyphsMinX, - ts->m_stringGlyphsMaxX, - ts->m_stringGlyphsMinY, - ts->m_stringGlyphsMaxY, - z, - ts->m_outlineThickness, - foregroundRgba); - - glPopMatrix(); - } } if (annotationText.getLineColor() != CaretColorEnum::NONE) { glPushMatrix(); - const float lineThicknessPixels = getLineThicknessPixelsInModelSpace(annotationText.getLineWidthPercentage(), - heightOrWidthForPercentageSizeText, - modelSpaceScaling); uint8_t foregroundRgba[4]; annotationText.getLineColorRGBA(foregroundRgba); - GraphicsPrimitiveV3fN3f primitive(GraphicsPrimitive::PrimitiveType::POLYGONAL_LINE_LOOP_MITER_JOIN, - foregroundRgba); - double doubleNormalXYZ[3] { - normalVector[0], - normalVector[1], - normalVector[2] - }; - primitive.addVertex(topLeft, doubleNormalXYZ); - primitive.addVertex(bottomLeft, doubleNormalXYZ); - primitive.addVertex(bottomRight, doubleNormalXYZ); - primitive.addVertex(topRight, doubleNormalXYZ); - primitive.setLineWidth(GraphicsPrimitive::LineWidthType::PIXELS, lineThicknessPixels); - GraphicsEngineDataOpenGL::draw(&primitive); + + const float thickness = ((annotationText.getLineWidthPercentage() / 100.0) + * heightOrWidthForPercentageSizeText); + GraphicsShape::drawOutlineRectangleVerticesAtInside(bottomLeft, + bottomRight, + topRight, + topLeft, + thickness, + foregroundRgba); glPopMatrix(); } + glDisable(GL_POLYGON_OFFSET_FILL); glPopMatrix(); BrainOpenGL::testForOpenGLError("At end of " diff --git a/src/Brain/FtglFontTextRenderer.h b/src/Brain/FtglFontTextRenderer.h index aadfa2619..c89a6d11a 100644 --- a/src/Brain/FtglFontTextRenderer.h +++ b/src/Brain/FtglFontTextRenderer.h @@ -353,11 +353,8 @@ namespace caret { const TextStringGroup& textStringGroup); void drawTextInModelSpaceInternal(const AnnotationText& annotationText, - const float modelSpaceScaling, const TextStringGroup& textStringGroup, - const float heightOrWidthForPercentageSizeText, - const float normalVector[3], - const DrawingFlags& flags); + const float heightOrWidthForPercentageSizeText); void applyTextColoring(const AnnotationText& annotationText); -- GitLab From d2542b61cc31c57490990a551a0c32bc683edc2f Mon Sep 17 00:00:00 2001 From: John Harwell Date: Wed, 29 Aug 2018 16:29:26 -0500 Subject: [PATCH 031/427] Corrected rotation of text annotations with texture offset (was applying rotation twice). --- ...inOpenGLAnnotationDrawingFixedPipeline.cxx | 34 +++++++++++++++---- 1 file changed, 27 insertions(+), 7 deletions(-) diff --git a/src/Brain/BrainOpenGLAnnotationDrawingFixedPipeline.cxx b/src/Brain/BrainOpenGLAnnotationDrawingFixedPipeline.cxx index 9942ea198..767a0900e 100644 --- a/src/Brain/BrainOpenGLAnnotationDrawingFixedPipeline.cxx +++ b/src/Brain/BrainOpenGLAnnotationDrawingFixedPipeline.cxx @@ -2590,11 +2590,18 @@ BrainOpenGLAnnotationDrawingFixedPipeline::drawTextTangentOffset(AnnotationFile* textDrawingPlane.projectPointToPlane(zBig, textUpOrientationVectorXYZ); inverseMatrix.multiplyPoint3(textUpOrientationVectorXYZ); + /* + * Vector is parallel to surface Z-axis + */ + float vectorSurfaceAxisZ[3] = { 0.0f, 0.0f, 100.0f }; + inverseMatrix.multiplyPoint3(vectorSurfaceAxisZ); + if (debugFlag) { /* * Red line is normal vector * Green line is "text up" vector * Blue points to surface Z-vector + * Yellow is parallel to surface Z-axis */ const float startXYZ[3] { 0.0, @@ -2616,15 +2623,34 @@ BrainOpenGLAnnotationDrawingFixedPipeline::drawTextTangentOffset(AnnotationFile* glColor3f(0.0, 0.0, 1.0); glVertex3fv(startXYZ); glVertex3fv(textUpOrientationVectorXYZ); + glColor3f(1.0, 1.0, 0.0); + glVertex3fv(startXYZ); + glVertex3fv(vectorSurfaceAxisZ); glEnd(); + + { +// /* +// * Vector is parallel to surface Z-axis +// */ +// float bigZ[3] = { 0.0f, 0.0f, 100.0f }; +// inverseMatrix.multiplyPoint3(bigZ); +// glBegin(GL_LINES); +// glColor3f(1.0, 1.0, 0.0); +// glVertex3fv(startXYZ); +// glVertex3fv(bigZ); +// glEnd(); + + } } /* * Rotate the text so that the horzontal flow of the text * is orthogonal to the 'text up orientation vector'. */ +// const float angle = angleInDegreesBetweenVectors(localUpXYZ, +// textUpOrientationVectorXYZ); const float angle = angleInDegreesBetweenVectors(localUpXYZ, - textUpOrientationVectorXYZ); + vectorSurfaceAxisZ); glRotatef(-angle, 0.0, 0.0, 1.0); if (debugFlag) { @@ -2634,12 +2660,6 @@ BrainOpenGLAnnotationDrawingFixedPipeline::drawTextTangentOffset(AnnotationFile* std::cout << " Angle: " << angle << std::endl; } - /* - * Apply user rotation of text - */ - const float rotationAngle = text->getRotationAngle(); - glRotatef(-rotationAngle, 0.0, 0.0, 1.0); - double bottomLeft[3]; double bottomRight[3]; double topRight[3]; -- GitLab From 8cf3fde2b5c929c6f59c9a7bad7776e0c1f82647 Mon Sep 17 00:00:00 2001 From: John Harwell Date: Fri, 31 Aug 2018 15:40:34 -0500 Subject: [PATCH 032/427] Added a method to SurfaceFile that returns the normal in a parameter. --- src/Files/SurfaceFile.cxx | 19 +++++++++++++++++++ src/Files/SurfaceFile.h | 3 +++ 2 files changed, 22 insertions(+) diff --git a/src/Files/SurfaceFile.cxx b/src/Files/SurfaceFile.cxx index f7b5c7aea..c0960bafe 100644 --- a/src/Files/SurfaceFile.cxx +++ b/src/Files/SurfaceFile.cxx @@ -556,6 +556,25 @@ SurfaceFile::getNormalVector(const int32_t nodeIndex) const return &(this->normalVectors[offset]); } +/** + * Get a normal vector for a coordinate. + * + * @param nodeIndex + * Index of coordinate. + * @param normalVectorOut + * Output containing the normal vector for the node. + */ +void +SurfaceFile::getNormalVector(const int32_t nodeIndex, + float normalVectorOut[3]) const +{ + const int32_t offset = nodeIndex * 3; + CaretAssertVectorIndex(this->normalVectors, offset+2); + normalVectorOut[0] = this->normalVectors[offset]; + normalVectorOut[1] = this->normalVectors[offset+1]; + normalVectorOut[2] = this->normalVectors[offset+2]; +} + const float* SurfaceFile::getNormalData() const { return normalVectors.data(); diff --git a/src/Files/SurfaceFile.h b/src/Files/SurfaceFile.h index 1bd8377d6..c96b7a999 100644 --- a/src/Files/SurfaceFile.h +++ b/src/Files/SurfaceFile.h @@ -91,6 +91,9 @@ namespace caret { const float* getNormalVector(const int32_t nodeIndex) const; + void getNormalVector(const int32_t nodeIndex, + float normalVectorOut[3]) const; + const float* getNormalData() const; int getNumberOfTriangles() const; -- GitLab From 919331bc44a4e7a7f653bb3aff8837d855938000 Mon Sep 17 00:00:00 2001 From: John Harwell Date: Tue, 4 Sep 2018 16:59:30 -0500 Subject: [PATCH 033/427] WB-636 Texture Based Annotations: Added drawing of all annotations with textured surface offset. When a line is edited, the surface offset type is displayed only in the first coordinate since both coordinates must use the same surface offset type. --- src/Annotations/Annotation.h | 3 + .../AnnotationOneDimensionalShape.cxx | 10 + .../AnnotationOneDimensionalShape.h | 2 + .../AnnotationTwoDimensionalShape.cxx | 25 + .../AnnotationTwoDimensionalShape.h | 2 + ...inOpenGLAnnotationDrawingFixedPipeline.cxx | 1907 +++++++++++++---- ...rainOpenGLAnnotationDrawingFixedPipeline.h | 51 +- src/GuiQt/AnnotationCoordinateWidget.cxx | 37 +- 8 files changed, 1579 insertions(+), 458 deletions(-) diff --git a/src/Annotations/Annotation.h b/src/Annotations/Annotation.h index 07062994e..e986a82d6 100644 --- a/src/Annotations/Annotation.h +++ b/src/Annotations/Annotation.h @@ -27,6 +27,7 @@ #include "AnnotationCoordinateSpaceEnum.h" #include "AnnotationGroupKey.h" #include "AnnotationSizingHandleTypeEnum.h" +#include "AnnotationSurfaceOffsetVectorTypeEnum.h" #include "AnnotationTypeEnum.h" #include "CaretColorEnum.h" #include "CaretObjectTracksModification.h" @@ -155,6 +156,8 @@ namespace caret { void setCoordinateSpace(const AnnotationCoordinateSpaceEnum::Enum coordinateSpace); + virtual AnnotationSurfaceOffsetVectorTypeEnum::Enum getSurfaceOffsetVectorType() const = 0; + int32_t getTabIndex() const; void setTabIndex(const int32_t tabIndex); diff --git a/src/Annotations/AnnotationOneDimensionalShape.cxx b/src/Annotations/AnnotationOneDimensionalShape.cxx index 5c740cc6c..f89bbe499 100644 --- a/src/Annotations/AnnotationOneDimensionalShape.cxx +++ b/src/Annotations/AnnotationOneDimensionalShape.cxx @@ -162,6 +162,16 @@ AnnotationOneDimensionalShape::getEndCoordinate() const return m_endCoordinate; } +/** + * @return The surface offset vector type for this annotation. + */ +AnnotationSurfaceOffsetVectorTypeEnum::Enum +AnnotationOneDimensionalShape::getSurfaceOffsetVectorType() const +{ + CaretAssert(m_startCoordinate); + return m_startCoordinate->getSurfaceOffsetVectorType(); +} + /** * Is the object modified? * @return true if modified, else false. diff --git a/src/Annotations/AnnotationOneDimensionalShape.h b/src/Annotations/AnnotationOneDimensionalShape.h index e9f05f9dc..3d30304bd 100644 --- a/src/Annotations/AnnotationOneDimensionalShape.h +++ b/src/Annotations/AnnotationOneDimensionalShape.h @@ -49,6 +49,8 @@ namespace caret { const AnnotationCoordinate* getEndCoordinate() const; + virtual AnnotationSurfaceOffsetVectorTypeEnum::Enum getSurfaceOffsetVectorType() const override; + virtual bool isModified() const; virtual void clearModified(); diff --git a/src/Annotations/AnnotationTwoDimensionalShape.cxx b/src/Annotations/AnnotationTwoDimensionalShape.cxx index 6b4d2f972..feae6a7c3 100644 --- a/src/Annotations/AnnotationTwoDimensionalShape.cxx +++ b/src/Annotations/AnnotationTwoDimensionalShape.cxx @@ -169,6 +169,16 @@ AnnotationTwoDimensionalShape::getCoordinate() const return m_coordinate; } +/** + * @return The surface offset vector type for this annotation. + */ +AnnotationSurfaceOffsetVectorTypeEnum::Enum +AnnotationTwoDimensionalShape::getSurfaceOffsetVectorType() const +{ + CaretAssert(m_coordinate); + return m_coordinate->getSurfaceOffsetVectorType(); +} + /** * @return Height for "two-dimensional" annotations in percentage zero to one-hundred. */ @@ -330,12 +340,22 @@ AnnotationTwoDimensionalShape::isSizeHandleValid(const AnnotationSizingHandleTyp { bool viewportFlag = false; + bool surfaceTangentOffsetFlag = false; switch (getCoordinateSpace()) { case AnnotationCoordinateSpaceEnum::CHART: break; case AnnotationCoordinateSpaceEnum::STEREOTAXIC: break; case AnnotationCoordinateSpaceEnum::SURFACE: + switch (getCoordinate()->getSurfaceOffsetVectorType()) { + case AnnotationSurfaceOffsetVectorTypeEnum::CENTROID_THRU_VERTEX: + break; + case AnnotationSurfaceOffsetVectorTypeEnum::SURFACE_NORMAL: + break; + case AnnotationSurfaceOffsetVectorTypeEnum::TANGENT: + surfaceTangentOffsetFlag = true; + break; + } break; case AnnotationCoordinateSpaceEnum::TAB: break; @@ -383,6 +403,11 @@ AnnotationTwoDimensionalShape::isSizeHandleValid(const AnnotationSizingHandleTyp break; } + if (surfaceTangentOffsetFlag) { + allowsCornerResizingFlag = false; + allowsSideResizingFlag = false; + } + bool validFlag = false; if (! viewportFlag) { diff --git a/src/Annotations/AnnotationTwoDimensionalShape.h b/src/Annotations/AnnotationTwoDimensionalShape.h index 9bc0117d9..07e87519e 100644 --- a/src/Annotations/AnnotationTwoDimensionalShape.h +++ b/src/Annotations/AnnotationTwoDimensionalShape.h @@ -46,6 +46,8 @@ namespace caret { const AnnotationCoordinate* getCoordinate() const; + virtual AnnotationSurfaceOffsetVectorTypeEnum::Enum getSurfaceOffsetVectorType() const override; + float getHeight() const; void setHeight(const float height); diff --git a/src/Brain/BrainOpenGLAnnotationDrawingFixedPipeline.cxx b/src/Brain/BrainOpenGLAnnotationDrawingFixedPipeline.cxx index 767a0900e..032765178 100644 --- a/src/Brain/BrainOpenGLAnnotationDrawingFixedPipeline.cxx +++ b/src/Brain/BrainOpenGLAnnotationDrawingFixedPipeline.cxx @@ -147,8 +147,6 @@ BrainOpenGLAnnotationDrawingFixedPipeline::viewportToOpenGLWindowCoordinate(cons * The annotation. * @param coordinate * The annotation coordinate whose window coordinate is computed. - * @param annotationCoordSpace - * The annotation coordinate space. * @param surfaceDisplayed * Surface that is displayed (may be NULL !) * @param xyzOut @@ -162,6 +160,7 @@ BrainOpenGLAnnotationDrawingFixedPipeline::getAnnotationDrawingSpaceCoordinate(c const Surface* surfaceDisplayed, float xyzOut[3]) const { + CaretAssert(annotation); const AnnotationCoordinateSpaceEnum::Enum annotationCoordSpace = annotation->getCoordinateSpace(); float modelXYZ[3] = { 0.0, 0.0, 0.0 }; @@ -220,6 +219,12 @@ BrainOpenGLAnnotationDrawingFixedPipeline::getAnnotationDrawingSpaceCoordinate(c annotationOffsetLength, annotationOffsetVector); + /* + * Always use surface offset vector reported by annotation since + * annotations with multiple coordinates must use the same offset + */ + annotationOffsetVector = annotation->getSurfaceOffsetVectorType(); + const StructureEnum::Enum surfaceStructure = surfaceDisplayed->getStructure(); const int32_t surfaceNumberOfNodes = surfaceDisplayed->getNumberOfNodes(); if ((surfaceStructure == annotationStructure) @@ -1080,12 +1085,49 @@ BrainOpenGLAnnotationDrawingFixedPipeline::drawAnnotation(AnnotationFile* annota bool drawnFlag = false; + bool surfaceTextureSpaceFlag = false; switch (annotation->getCoordinateSpace()) { case AnnotationCoordinateSpaceEnum::CHART: break; case AnnotationCoordinateSpaceEnum::STEREOTAXIC: break; case AnnotationCoordinateSpaceEnum::SURFACE: + if (annotation->getCoordinateSpace() == AnnotationCoordinateSpaceEnum::SURFACE) { + AnnotationTwoDimensionalShape* twoDimAnn = dynamic_cast(annotation); + if (twoDimAnn != NULL) { + switch (twoDimAnn->getCoordinate()->getSurfaceOffsetVectorType()) { + case AnnotationSurfaceOffsetVectorTypeEnum::CENTROID_THRU_VERTEX: + break; + case AnnotationSurfaceOffsetVectorTypeEnum::SURFACE_NORMAL: + break; + case AnnotationSurfaceOffsetVectorTypeEnum::TANGENT: + { + drawnFlag = drawTwoDimAnnotationSurfaceTextureOffset(annotationFile, + twoDimAnn, + surfaceDisplayed); + surfaceTextureSpaceFlag = true; + } + break; + } + } + else { + AnnotationOneDimensionalShape* oneDimAnn = dynamic_cast(annotation); + switch (oneDimAnn->getStartCoordinate()->getSurfaceOffsetVectorType()) { + case AnnotationSurfaceOffsetVectorTypeEnum::CENTROID_THRU_VERTEX: + break; + case AnnotationSurfaceOffsetVectorTypeEnum::SURFACE_NORMAL: + break; + case AnnotationSurfaceOffsetVectorTypeEnum::TANGENT: + { + drawnFlag = drawOneDimAnnotationSurfaceTextureOffset(annotationFile, + oneDimAnn, + surfaceDisplayed); + surfaceTextureSpaceFlag = true; + } + break; + } + } + } break; case AnnotationCoordinateSpaceEnum::TAB: break; @@ -1095,36 +1137,38 @@ BrainOpenGLAnnotationDrawingFixedPipeline::drawAnnotation(AnnotationFile* annota break; } - switch (annotation->getType()) { - case AnnotationTypeEnum::BOX: - drawnFlag = drawBox(annotationFile, - dynamic_cast(annotation), - surfaceDisplayed); - break; - case AnnotationTypeEnum::COLOR_BAR: - drawColorBar(annotationFile, - dynamic_cast(annotation)); - break; - case AnnotationTypeEnum::IMAGE: - drawnFlag = drawImage(annotationFile, - dynamic_cast(annotation), - surfaceDisplayed); - break; - case AnnotationTypeEnum::LINE: - drawnFlag = drawLine(annotationFile, - dynamic_cast(annotation), - surfaceDisplayed); - break; - case AnnotationTypeEnum::OVAL: - drawnFlag = drawOval(annotationFile, - dynamic_cast(annotation), - surfaceDisplayed); - break; - case AnnotationTypeEnum::TEXT: - drawnFlag = drawText(annotationFile, - dynamic_cast(annotation), - surfaceDisplayed); - break; + if ( ! surfaceTextureSpaceFlag) { + switch (annotation->getType()) { + case AnnotationTypeEnum::BOX: + drawnFlag = drawBox(annotationFile, + dynamic_cast(annotation), + surfaceDisplayed); + break; + case AnnotationTypeEnum::COLOR_BAR: + drawColorBar(annotationFile, + dynamic_cast(annotation)); + break; + case AnnotationTypeEnum::IMAGE: + drawnFlag = drawImage(annotationFile, + dynamic_cast(annotation), + surfaceDisplayed); + break; + case AnnotationTypeEnum::LINE: + drawnFlag = drawLine(annotationFile, + dynamic_cast(annotation), + surfaceDisplayed); + break; + case AnnotationTypeEnum::OVAL: + drawnFlag = drawOval(annotationFile, + dynamic_cast(annotation), + surfaceDisplayed); + break; + case AnnotationTypeEnum::TEXT: + drawnFlag = drawText(annotationFile, + dynamic_cast(annotation), + surfaceDisplayed); + break; + } } if (drawnFlag) { @@ -1132,201 +1176,675 @@ BrainOpenGLAnnotationDrawingFixedPipeline::drawAnnotation(AnnotationFile* annota } } - /** - * Draw an annotation box. + * Draw a two-dimensional annotation that is in surface space with a texture offset. * * @param annotationFile * File containing the annotation. - * @param box - * box to draw. + * @param annotation + * Annotation to draw. * @param surfaceDisplayed * Surface that is displayed (may be NULL). * @return - * True if the annotation was drawn while NOT selecting annotations. + * True if the anntotation was drawn, else false. */ bool -BrainOpenGLAnnotationDrawingFixedPipeline::drawBox(AnnotationFile* annotationFile, - AnnotationBox* box, - const Surface* surfaceDisplayed) +BrainOpenGLAnnotationDrawingFixedPipeline::drawTwoDimAnnotationSurfaceTextureOffset(AnnotationFile* annotationFile, + AnnotationTwoDimensionalShape* annotation, + const Surface* surfaceDisplayed) { - CaretAssert(box); - CaretAssert(box->getType() == AnnotationTypeEnum::BOX); + bool drawnFlag = false; - float annXYZ[3]; - if ( ! getAnnotationDrawingSpaceCoordinate(box, - box->getCoordinate(), - surfaceDisplayed, - annXYZ)) { - return false; - } + CaretAssert(annotationFile); + CaretAssert(annotation); + CaretAssert(surfaceDisplayed); - float bottomLeft[3]; - float bottomRight[3]; - float topRight[3]; - float topLeft[3]; - if ( ! getAnnotationTwoDimShapeBounds(box, annXYZ, - bottomLeft, bottomRight, topRight, topLeft)) { - return false; - } - const float selectionCenterXYZ[3] = { - (bottomLeft[0] + bottomRight[0] + topRight[0] + topLeft[0]) / 4.0f, - (bottomLeft[1] + bottomRight[1] + topRight[1] + topLeft[1]) / 4.0f, - (bottomLeft[2] + bottomRight[2] + topRight[2] + topLeft[2]) / 4.0f - }; + const AnnotationCoordinate* coord = annotation->getCoordinate(); + CaretAssert(coord->getSurfaceOffsetVectorType() == AnnotationSurfaceOffsetVectorTypeEnum::TANGENT); + StructureEnum::Enum structure; + int32_t surfaceNumberOfNodes(0); + int32_t vertexIndex(0); + float offsetLength(0.0f); + AnnotationSurfaceOffsetVectorTypeEnum::Enum offsetVectorType; + coord->getSurfaceSpace(structure, surfaceNumberOfNodes, vertexIndex, offsetLength, offsetVectorType); - if (box->getLineWidthPercentage() <= 0.0f) { - convertObsoleteLineWidthPixelsToPercentageWidth(box); + if (structure != surfaceDisplayed->getStructure()) { + return false; } - - const bool depthTestFlag = isDrawnWithDepthTesting(box, - surfaceDisplayed); - const bool savedDepthTestStatus = setDepthTestingStatus(depthTestFlag); - - uint8_t backgroundRGBA[4]; - box->getBackgroundColorRGBA(backgroundRGBA); - uint8_t foregroundRGBA[4]; - box->getLineColorRGBA(foregroundRGBA); - - const bool drawBackgroundFlag = (backgroundRGBA[3] > 0); - const bool drawForegroundFlag = (foregroundRGBA[3] > 0); - const bool drawAnnotationFlag = (drawBackgroundFlag || drawForegroundFlag); - - bool drawnFlag = false; - - if (drawAnnotationFlag) { - if (m_selectionModeFlag) { - uint8_t selectionColorRGBA[4]; - getIdentificationColor(selectionColorRGBA); - - if (drawBackgroundFlag) { - /* - * When selecting draw only background if it is enabled - * since it is opaque and prevents "behind" annotations - * from being selected - */ - GraphicsShape::drawBoxFilledByteColor(bottomLeft, - bottomRight, - topRight, - topLeft, - selectionColorRGBA); - } - else { - /* - * Drawing foreground as line will still allow user to - * select annotation that are inside of the box - */ - const float percentHeight = getLineWidthPercentageInSelectionMode(box); - GraphicsShape::drawBoxOutlineByteColor(bottomLeft, - bottomRight, - topRight, - topLeft, - selectionColorRGBA, - GraphicsPrimitive::LineWidthType::PERCENTAGE_VIEWPORT_HEIGHT, - percentHeight); - } - - m_selectionInfo.push_back(SelectionInfo(annotationFile, - box, - AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_NONE, - selectionCenterXYZ)); - } - else { - if (drawBackgroundFlag) { - GraphicsShape::drawBoxFilledByteColor(bottomLeft, - bottomRight, - topRight, - topLeft, - backgroundRGBA); - drawnFlag = true; - } - - if (drawForegroundFlag) { - GraphicsShape::drawBoxOutlineByteColor(bottomLeft, - bottomRight, - topRight, - topLeft, - foregroundRGBA, - GraphicsPrimitive::LineWidthType::PERCENTAGE_VIEWPORT_HEIGHT, - box->getLineWidthPercentage()); - drawnFlag = true; - } - } - if (box->isSelectedForEditing(m_inputs->m_windowIndex)) { - drawAnnotationTwoDimSizingHandles(annotationFile, - box, - bottomLeft, - bottomRight, - topRight, - topLeft, - s_sizingHandleLineWidthInPixels, - box->getRotationAngle()); - } + if (surfaceDisplayed->getNumberOfNodes() != surfaceNumberOfNodes) { + return false; } - - setDepthTestingStatus(savedDepthTestStatus); - - return drawnFlag; -} - -/** - * Draw an annotation box. - * - * @param annotationFile - * File containing the annotation. - * @param colorBar - * Color bar to draw. - */ -void -BrainOpenGLAnnotationDrawingFixedPipeline::drawColorBar(AnnotationFile* annotationFile, - AnnotationColorBar* colorBar) -{ - CaretAssert(colorBar); - CaretAssert(colorBar->getType() == AnnotationTypeEnum::COLOR_BAR); + float vertexXYZ[3]; + surfaceDisplayed->getCoordinate(vertexIndex, + vertexXYZ); + float normalXYZ[3]; + getSurfaceNormalVector(surfaceDisplayed, vertexIndex, normalXYZ); + const BoundingBox* boundingBox = surfaceDisplayed->getBoundingBox(); + const float surfaceExtentZ = boundingBox->getDifferenceZ(); - float annXYZ[3]; - if ( ! getAnnotationDrawingSpaceCoordinate(colorBar, - colorBar->getCoordinate(), - NULL, - annXYZ)) { - return; - } + /* + * Need to restore model space + * Recall that all other annotation spaces are drawn in window space + */ + glMatrixMode(GL_PROJECTION); + glPushMatrix(); + glLoadMatrixd(m_modelSpaceProjectionMatrix); - float bottomLeft[3]; - float bottomRight[3]; - float topRight[3]; - float topLeft[3]; - if ( ! getAnnotationTwoDimShapeBounds(colorBar, annXYZ, - bottomLeft, bottomRight, topRight, topLeft)) { - return; - } + glMatrixMode(GL_MODELVIEW); + glPushMatrix(); + glLoadMatrixd(m_modelSpaceModelMatrix); + int32_t savedViewport[4]; + glGetIntegerv(GL_VIEWPORT, + savedViewport); + glViewport(m_modelSpaceViewport[0], + m_modelSpaceViewport[1], + m_modelSpaceViewport[2], + m_modelSpaceViewport[3]); - const float viewportHeight = m_modelSpaceViewport[3]; - const float viewportWidth = m_modelSpaceViewport[2]; + glPushMatrix(); /* - * The user sets the total height of the colorbar and the - * height of the text. - * - * There are three items in the colorbar from top to bottom: - * (1) The numeric values - * (2) The tick marks - * (3) The palettes color sections + * Matrix that rotates annotation to plane of vertex's normal vector */ - float totalHeightPercent = colorBar->getHeight(); - float textHeightPercent = colorBar->getFontPercentViewportSize(); - float totalHeightPixels = (viewportHeight - * (totalHeightPercent / 100.0f)); + Matrix4x4 rotationMatrix; + rotationMatrix.setMatrixToOpenGLRotationFromVector(normalXYZ); + double rotationArray[16]; + rotationMatrix.getMatrixForOpenGL(rotationArray); + /* - * Text is aligned at the top of the characters + * Translate to the vertex and then translate using offset + * vector of text. */ - float textHeightPixels = (viewportHeight - * (textHeightPercent / 100.0f)); - - if (debugFlag) { - std::cout << "Color bar heights before corrections (pixels) " << std::endl; + const float offsetVectorXYZ[3] { + normalXYZ[0] * offsetLength, + normalXYZ[1] * offsetLength, + normalXYZ[2] * offsetLength + }; + glTranslatef(vertexXYZ[0], vertexXYZ[1], vertexXYZ[2]); + glTranslatef(offsetVectorXYZ[0], offsetVectorXYZ[1], offsetVectorXYZ[2]); + + /* + * Rotate into plane of surface normal vector + */ + glMultMatrixd(rotationArray); + + /* + * Up vector in local coordinates (text drawing plane) + * Z points out of text + */ + const float len(25.0f); + const float localUpXYZ[3] { + 0.0, + len, + 0.0 + }; + + /* + * Inverse matrix goes back to surface coordinate system + */ + Matrix4x4 inverseMatrix(rotationMatrix); + inverseMatrix.invert(); + + /* + * Create the plane in which text is drawn + * (plane is not used for drawing text but is used + * to orient the text). The plane is constructed + * from the vertex's normal vector and the the + * vertex's XYZ-coordinate. + */ + Plane textDrawingPlane(normalXYZ, + vertexXYZ); + + /* + * Project a point with a large Z-coordinate to the plane + * and use it to creat the 'text up orientation vector'. + */ + const float zBig[3] { 0.0, 0.0, 10000.0 }; + float textUpOrientationVectorXYZ[3]; + textDrawingPlane.projectPointToPlane(zBig, textUpOrientationVectorXYZ); + inverseMatrix.multiplyPoint3(textUpOrientationVectorXYZ); + + /* + * Vector is parallel to surface Z-axis + */ + float vectorSurfaceAxisZ[3] = { 0.0f, 0.0f, 100.0f }; + inverseMatrix.multiplyPoint3(vectorSurfaceAxisZ); + + if (debugFlag) { + /* + * Red line is normal vector + * Green line is "text up" vector + * Blue points to surface Z-vector + * Yellow is parallel to surface Z-axis + */ + const float startXYZ[3] { + 0.0, + 0.0, + 0.0 + }; + const float endXYZ[3] { + 0.0, + 0.0, + len + }; + glBegin(GL_LINES); + glColor3f(1.0, 0.0, 0.0); + glVertex3fv(startXYZ); + glVertex3fv(endXYZ); + glColor3f(0.0, 1.0, 0.0); + glVertex3fv(startXYZ); + glVertex3fv(localUpXYZ); + glColor3f(0.0, 0.0, 1.0); + glVertex3fv(startXYZ); + glVertex3fv(textUpOrientationVectorXYZ); + glColor3f(1.0, 1.0, 0.0); + glVertex3fv(startXYZ); + glVertex3fv(vectorSurfaceAxisZ); + glEnd(); + + { + // /* + // * Vector is parallel to surface Z-axis + // */ + // float bigZ[3] = { 0.0f, 0.0f, 100.0f }; + // inverseMatrix.multiplyPoint3(bigZ); + // glBegin(GL_LINES); + // glColor3f(1.0, 1.0, 0.0); + // glVertex3fv(startXYZ); + // glVertex3fv(bigZ); + // glEnd(); + + } + } + + /* + * Rotate the text so that the horzontal flow of the text + * is orthogonal to the 'text up orientation vector'. + */ + // const float angle = angleInDegreesBetweenVectors(localUpXYZ, + // textUpOrientationVectorXYZ); + const float orientationUpAngle = angleInDegreesBetweenVectors(localUpXYZ, + vectorSurfaceAxisZ); + glRotatef(orientationUpAngle, 0.0, 0.0, -1.0); + + + + if (debugFlag) { + //std::cout << "Annotation: " << text->getText() << std::endl; + std::cout << " Plane: " << textDrawingPlane.toString() << std::endl; + std::cout << " Local Up Vector: " << AString::fromNumbers(localUpXYZ, 3, ", ") << std::endl; + std::cout << " Angle: " << orientationUpAngle << std::endl; + } + + /* + * Note that text is rotated by the text renderer + */ + if (annotation->getType() != AnnotationTypeEnum::TEXT) { + glRotated(annotation->getRotationAngle(), 0.0, 0.0, -1.0); + } + + switch (annotation->getType()) { + case AnnotationTypeEnum::BOX: + drawnFlag = drawBoxSurfaceTangentOffset(annotationFile, + dynamic_cast(annotation), + surfaceDisplayed, + surfaceExtentZ, + vertexXYZ); + break; + case AnnotationTypeEnum::COLOR_BAR: + CaretAssertMessage(0, "Color Bar is NEVER drawn in surface space"); + break; + case AnnotationTypeEnum::IMAGE: + drawImageSurfaceTangentOffset(annotationFile, + dynamic_cast(annotation), + surfaceDisplayed, + surfaceExtentZ, + vertexXYZ); + break; + case AnnotationTypeEnum::LINE: + CaretAssertToDoFatal(); + // drawnFlag = drawLine(annotationFile, + // dynamic_cast(annotation), + // surfaceDisplayed); + break; + case AnnotationTypeEnum::OVAL: + drawnFlag = drawOvalSurfaceTangentOffset(annotationFile, + dynamic_cast(annotation), + surfaceDisplayed, + surfaceExtentZ, + vertexXYZ); + break; + case AnnotationTypeEnum::TEXT: + drawnFlag = drawTextSurfaceTangentOffset(annotationFile, + dynamic_cast(annotation), + surfaceDisplayed, + surfaceExtentZ, + vertexXYZ, + normalXYZ); + break; + } + + glPopMatrix(); /* restore MODELVIEW */ + + glViewport(savedViewport[0], + savedViewport[1], + savedViewport[2], + savedViewport[3]); + glPopMatrix(); + + glMatrixMode(GL_PROJECTION); + glPopMatrix(); + glMatrixMode(GL_MODELVIEW); + + return drawnFlag; +} + +/** + * Draw a one-dimensional annotation that is in surface space with a texture offset. + * + * @param annotationFile + * File containing the annotation. + * @param annotation + * Annotation to draw. + * @param surfaceDisplayed + * Surface that is displayed (may be NULL). + * @return + * True if the anntotation was drawn, else false. + */ +bool +BrainOpenGLAnnotationDrawingFixedPipeline::drawOneDimAnnotationSurfaceTextureOffset(AnnotationFile* annotationFile, + AnnotationOneDimensionalShape* annotation, + const Surface* surfaceDisplayed) +{ + CaretAssert(annotationFile); + CaretAssert(annotation); + CaretAssert(surfaceDisplayed); + + bool drawnFlag = false; + + + const BoundingBox* boundingBox = surfaceDisplayed->getBoundingBox(); + const float surfaceExtentZ = boundingBox->getDifferenceZ(); + + /* + * Need to restore model space + * Recall that all other annotation spaces are drawn in window space + */ + glMatrixMode(GL_PROJECTION); + glPushMatrix(); + glLoadMatrixd(m_modelSpaceProjectionMatrix); + + glMatrixMode(GL_MODELVIEW); + glPushMatrix(); + glLoadMatrixd(m_modelSpaceModelMatrix); + int32_t savedViewport[4]; + glGetIntegerv(GL_VIEWPORT, + savedViewport); + glViewport(m_modelSpaceViewport[0], + m_modelSpaceViewport[1], + m_modelSpaceViewport[2], + m_modelSpaceViewport[3]); + + glPushMatrix(); + switch (annotation->getType()) { + case AnnotationTypeEnum::BOX: + case AnnotationTypeEnum::COLOR_BAR: + case AnnotationTypeEnum::IMAGE: + case AnnotationTypeEnum::OVAL: + case AnnotationTypeEnum::TEXT: + CaretAssert(0); + break; + case AnnotationTypeEnum::LINE: + drawnFlag = drawLineSurfaceTextureOffset(annotationFile, + dynamic_cast(annotation), + surfaceDisplayed, + surfaceExtentZ); + break; + } + + glPopMatrix(); /* restore MODELVIEW */ + + glViewport(savedViewport[0], + savedViewport[1], + savedViewport[2], + savedViewport[3]); + glPopMatrix(); + + glMatrixMode(GL_PROJECTION); + glPopMatrix(); + glMatrixMode(GL_MODELVIEW); + + return drawnFlag; +} + +/** + * Draw an annotation box. + * + * @param annotationFile + * File containing the annotation. + * @param box + * box to draw. + * @param surfaceDisplayed + * Surface that is displayed (may be NULL). + * @return + * True if the annotation was drawn while NOT selecting annotations. + */ +bool +BrainOpenGLAnnotationDrawingFixedPipeline::drawBox(AnnotationFile* annotationFile, + AnnotationBox* box, + const Surface* surfaceDisplayed) +{ + CaretAssert(box); + CaretAssert(box->getType() == AnnotationTypeEnum::BOX); + + float annXYZ[3]; + if ( ! getAnnotationDrawingSpaceCoordinate(box, + box->getCoordinate(), + surfaceDisplayed, + annXYZ)) { + return false; + } + + float bottomLeft[3]; + float bottomRight[3]; + float topRight[3]; + float topLeft[3]; + if ( ! getAnnotationTwoDimShapeBounds(box, annXYZ, + bottomLeft, bottomRight, topRight, topLeft)) { + return false; + } + + const float selectionCenterXYZ[3] = { + (bottomLeft[0] + bottomRight[0] + topRight[0] + topLeft[0]) / 4.0f, + (bottomLeft[1] + bottomRight[1] + topRight[1] + topLeft[1]) / 4.0f, + (bottomLeft[2] + bottomRight[2] + topRight[2] + topLeft[2]) / 4.0f + }; + + if (box->getLineWidthPercentage() <= 0.0f) { + convertObsoleteLineWidthPixelsToPercentageWidth(box); + } + + const bool depthTestFlag = isDrawnWithDepthTesting(box, + surfaceDisplayed); + const bool savedDepthTestStatus = setDepthTestingStatus(depthTestFlag); + + uint8_t backgroundRGBA[4]; + box->getBackgroundColorRGBA(backgroundRGBA); + uint8_t foregroundRGBA[4]; + box->getLineColorRGBA(foregroundRGBA); + + const bool drawBackgroundFlag = (backgroundRGBA[3] > 0); + const bool drawForegroundFlag = (foregroundRGBA[3] > 0); + const bool drawAnnotationFlag = (drawBackgroundFlag || drawForegroundFlag); + + bool drawnFlag = false; + + if (drawAnnotationFlag) { + if (m_selectionModeFlag) { + uint8_t selectionColorRGBA[4]; + getIdentificationColor(selectionColorRGBA); + + if (drawBackgroundFlag) { + /* + * When selecting draw only background if it is enabled + * since it is opaque and prevents "behind" annotations + * from being selected + */ + GraphicsShape::drawBoxFilledByteColor(bottomLeft, + bottomRight, + topRight, + topLeft, + selectionColorRGBA); + } + else { + /* + * Drawing foreground as line will still allow user to + * select annotation that are inside of the box + */ + const float percentHeight = getLineWidthPercentageInSelectionMode(box); + GraphicsShape::drawBoxOutlineByteColor(bottomLeft, + bottomRight, + topRight, + topLeft, + selectionColorRGBA, + GraphicsPrimitive::LineWidthType::PERCENTAGE_VIEWPORT_HEIGHT, + percentHeight); + } + + m_selectionInfo.push_back(SelectionInfo(annotationFile, + box, + AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_NONE, + selectionCenterXYZ)); + } + else { + if (drawBackgroundFlag) { + GraphicsShape::drawBoxFilledByteColor(bottomLeft, + bottomRight, + topRight, + topLeft, + backgroundRGBA); + drawnFlag = true; + } + + if (drawForegroundFlag) { + GraphicsShape::drawBoxOutlineByteColor(bottomLeft, + bottomRight, + topRight, + topLeft, + foregroundRGBA, + GraphicsPrimitive::LineWidthType::PERCENTAGE_VIEWPORT_HEIGHT, + box->getLineWidthPercentage()); + drawnFlag = true; + } + } + if (box->isSelectedForEditing(m_inputs->m_windowIndex)) { + drawAnnotationTwoDimSizingHandles(annotationFile, + box, + bottomLeft, + bottomRight, + topRight, + topLeft, + s_sizingHandleLineWidthInPixels, + box->getRotationAngle()); + } + } + + setDepthTestingStatus(savedDepthTestStatus); + + return drawnFlag; +} + +/** + * Draw an annotation box in surface tangent offset space + * + * @param annotationFile + * File containing the annotation. + * @param box + * box to draw. + * @param surfaceDisplayed + * Surface that is displayed (may be NULL). + * @param surfaceExtentZ + * Z-extent of the surface. + * @param vertexXYZ + * Coordinate of the vertex. + * @return + * True if the annotation was drawn while NOT selecting annotations. + */ +bool +BrainOpenGLAnnotationDrawingFixedPipeline::drawBoxSurfaceTangentOffset(AnnotationFile* annotationFile, + AnnotationBox* box, + const Surface* surfaceDisplayed, + const float surfaceExtentZ, + const float vertexXYZ[3]) +{ + CaretAssert(annotationFile); + CaretAssert(box); + CaretAssert(surfaceDisplayed); + CaretAssert(box->getType() == AnnotationTypeEnum::BOX); + + const float halfWidth = ((box->getWidth() / 100.0) * surfaceExtentZ) / 2.0; + const float halfHeight = ((box->getHeight() / 100.0) * surfaceExtentZ) / 2.0; + float bottomLeft[3] { -halfWidth, -halfHeight, 0.0f }; + float bottomRight[3] { halfWidth, -halfHeight, 0.0f }; + float topRight[3] { halfWidth, halfHeight, 0.0f }; + float topLeft[3] { -halfWidth, halfHeight, 0.0f }; + + const float selectionCenterXYZ[3] = { + vertexXYZ[0], + vertexXYZ[1], + vertexXYZ[2] + }; + + float lineThickness = ((box->getLineWidthPercentage() / 100.0) + * surfaceExtentZ); + if (m_selectionModeFlag) { + lineThickness = std::max(lineThickness, + s_selectionLineMinimumPixelWidth); + } + + uint8_t backgroundRGBA[4]; + box->getBackgroundColorRGBA(backgroundRGBA); + uint8_t foregroundRGBA[4]; + box->getLineColorRGBA(foregroundRGBA); + + const bool drawBackgroundFlag = (backgroundRGBA[3] > 0); + const bool drawForegroundFlag = (foregroundRGBA[3] > 0); + const bool drawAnnotationFlag = (drawBackgroundFlag || drawForegroundFlag); + + bool drawnFlag = false; + + if (drawAnnotationFlag) { + if (m_selectionModeFlag) { + uint8_t selectionColorRGBA[4]; + getIdentificationColor(selectionColorRGBA); + + if (drawBackgroundFlag) { + /* + * When selecting draw only background if it is enabled + * since it is opaque and prevents "behind" annotations + * from being selected + */ + GraphicsShape::drawBoxFilledByteColor(bottomLeft, + bottomRight, + topRight, + topLeft, + selectionColorRGBA); + } + else { + /* + * Drawing foreground as line will still allow user to + * select annotation that are inside of the box + */ + GraphicsShape::drawBoxOutlineByteColor(bottomLeft, + bottomRight, + topRight, + topLeft, + selectionColorRGBA, + GraphicsPrimitive::LineWidthType::PIXELS, + lineThickness); + } + + m_selectionInfo.push_back(SelectionInfo(annotationFile, + box, + AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_NONE, + selectionCenterXYZ)); + } + else { + if (drawBackgroundFlag) { + GraphicsShape::drawBoxFilledByteColor(bottomLeft, + bottomRight, + topRight, + topLeft, + backgroundRGBA); + drawnFlag = true; + } + + if (drawForegroundFlag) { + GraphicsShape::drawBoxOutlineByteColor(bottomLeft, + bottomRight, + topRight, + topLeft, + foregroundRGBA, + GraphicsPrimitive::LineWidthType::PIXELS, + lineThickness); + drawnFlag = true; + } + } + if (box->isSelectedForEditing(m_inputs->m_windowIndex)) { + drawAnnotationTwoDimSizingHandles(annotationFile, + box, + bottomLeft, + bottomRight, + topRight, + topLeft, + s_sizingHandleLineWidthInPixels, + box->getRotationAngle()); + } + } + + return drawnFlag; +} + + +/** + * Draw an annotation color bar. + * + * @param annotationFile + * File containing the annotation. + * @param colorBar + * Color bar to draw. + */ +void +BrainOpenGLAnnotationDrawingFixedPipeline::drawColorBar(AnnotationFile* annotationFile, + AnnotationColorBar* colorBar) +{ + CaretAssert(colorBar); + CaretAssert(colorBar->getType() == AnnotationTypeEnum::COLOR_BAR); + + float annXYZ[3]; + if ( ! getAnnotationDrawingSpaceCoordinate(colorBar, + colorBar->getCoordinate(), + NULL, + annXYZ)) { + return; + } + + float bottomLeft[3]; + float bottomRight[3]; + float topRight[3]; + float topLeft[3]; + if ( ! getAnnotationTwoDimShapeBounds(colorBar, annXYZ, + bottomLeft, bottomRight, topRight, topLeft)) { + return; + } + + const float viewportHeight = m_modelSpaceViewport[3]; + const float viewportWidth = m_modelSpaceViewport[2]; + + /* + * The user sets the total height of the colorbar and the + * height of the text. + * + * There are three items in the colorbar from top to bottom: + * (1) The numeric values + * (2) The tick marks + * (3) The palettes color sections + */ + float totalHeightPercent = colorBar->getHeight(); + float textHeightPercent = colorBar->getFontPercentViewportSize(); + float totalHeightPixels = (viewportHeight + * (totalHeightPercent / 100.0f)); + + /* + * Text is aligned at the top of the characters + */ + float textHeightPixels = (viewportHeight + * (textHeightPercent / 100.0f)); + + if (debugFlag) { + std::cout << "Color bar heights before corrections (pixels) " << std::endl; std::cout << " Text: " << textHeightPixels << std::endl; } @@ -2191,6 +2709,135 @@ BrainOpenGLAnnotationDrawingFixedPipeline::drawOval(AnnotationFile* annotationFi return drawnFlag; } +/** + * Draw an annotation oval in surface tangent offset space + * + * @param annotationFile + * File containing the annotation. + * @param oval + * oval to draw. + * @param surfaceDisplayed + * Surface that is displayed (may be NULL). + * @param surfaceExtentZ + * Z-extent of the surface. + * @param vertexXYZ + * Coordinate of the vertex. + * @return + * True if the annotation was drawn while NOT selecting annotations. + */ +bool +BrainOpenGLAnnotationDrawingFixedPipeline::drawOvalSurfaceTangentOffset(AnnotationFile* annotationFile, + AnnotationOval* oval, + const Surface* surfaceDisplayed, + const float surfaceExtentZ, + const float vertexXYZ[3]) +{ + CaretAssert(annotationFile); + CaretAssert(oval); + CaretAssert(surfaceDisplayed); + CaretAssert(oval->getType() == AnnotationTypeEnum::OVAL); + + const float halfWidth = ((oval->getWidth() / 100.0) * surfaceExtentZ) / 2.0; + const float halfHeight = ((oval->getHeight() / 100.0) * surfaceExtentZ) / 2.0; + float bottomLeft[3] { -halfWidth, -halfHeight, 0.0f }; + float bottomRight[3] { halfWidth, -halfHeight, 0.0f }; + float topRight[3] { halfWidth, halfHeight, 0.0f }; + float topLeft[3] { -halfWidth, halfHeight, 0.0f }; + + const float selectionCenterXYZ[3] = { + vertexXYZ[0], + vertexXYZ[1], + vertexXYZ[2] + }; + + float lineThickness = ((oval->getLineWidthPercentage() / 100.0) + * surfaceExtentZ); + if (m_selectionModeFlag) { + lineThickness = std::max(lineThickness, + s_selectionLineMinimumPixelWidth); + } + + uint8_t backgroundRGBA[4]; + oval->getBackgroundColorRGBA(backgroundRGBA); + uint8_t foregroundRGBA[4]; + oval->getLineColorRGBA(foregroundRGBA); + + const bool drawBackgroundFlag = (backgroundRGBA[3] > 0); + const bool drawForegroundFlag = (foregroundRGBA[3] > 0); + const bool drawAnnotationFlag = (drawBackgroundFlag || drawForegroundFlag); + + bool drawnFlag = false; + + const float majorAxis = ((oval->getWidth() / 100.0f) * surfaceExtentZ); + const float minorAxis = ((oval->getHeight() / 100.0f) * surfaceExtentZ); + + if (drawAnnotationFlag) { + if (m_selectionModeFlag) { + uint8_t selectionColorRGBA[4]; + getIdentificationColor(selectionColorRGBA); + + if (drawBackgroundFlag) { + /* + * When selecting draw only background if it is enabled + * since it is opaque and prevents "behind" annotations + * from being selected + */ + GraphicsShape::drawEllipseFilledByteColor(majorAxis, + minorAxis, + selectionColorRGBA); + } + else { + /* + * Drawing foreground as line will still allow user to + * select annotation that are inside of the box + */ + GraphicsShape::drawEllipseOutlineByteColor(majorAxis, + minorAxis, + selectionColorRGBA, + GraphicsPrimitive::LineWidthType::PIXELS, + lineThickness); + } + + m_selectionInfo.push_back(SelectionInfo(annotationFile, + oval, + AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_NONE, + selectionCenterXYZ)); + } + else { + if (drawBackgroundFlag) { + GraphicsShape::drawEllipseFilledByteColor(majorAxis, + minorAxis, + backgroundRGBA); + drawnFlag = true; + } + + if (drawForegroundFlag) { + glPolygonOffset(-1.0, 1.0); + glEnable(GL_POLYGON_OFFSET_FILL); + GraphicsShape::drawEllipseOutlineByteColor(majorAxis, + minorAxis, + foregroundRGBA, + GraphicsPrimitive::LineWidthType::PIXELS, + lineThickness); + glDisable(GL_POLYGON_OFFSET_FILL); + drawnFlag = true; + } + } + if (oval->isSelectedForEditing(m_inputs->m_windowIndex)) { + drawAnnotationTwoDimSizingHandles(annotationFile, + oval, + bottomLeft, + bottomRight, + topRight, + topLeft, + s_sizingHandleLineWidthInPixels, + oval->getRotationAngle()); + } + } + + return drawnFlag; +} + /** * Get coordinate for drawing a line that connects text to brainordinate. * @@ -2455,209 +3102,53 @@ BrainOpenGLAnnotationDrawingFixedPipeline::getSurfaceNormalVector(const Surface* normalVectorOut[0] += normalXYZ[0]; normalVectorOut[1] += normalXYZ[1]; normalVectorOut[2] += normalXYZ[2]; - } - - if (numNeighbors > 0) { - normalVectorOut[0] /= numNeighbors; - normalVectorOut[1] /= numNeighbors; - normalVectorOut[2] /= numNeighbors; - } -} - -/** - * Draw an annotation text with tangent offset - * - * @param annotationFile - * File containing the annotation. - * @param text - * Annotation text to draw. - * @param surfaceDisplayed - * Surface that is displayed (may be NULL). - * @return - * True if the annotation was drawn while NOT selecting annotations. - */ -bool -BrainOpenGLAnnotationDrawingFixedPipeline::drawTextTangentOffset(AnnotationFile* annotationFile, - AnnotationText* text, - const Surface* surfaceDisplayed) -{ - CaretAssert(annotationFile); - CaretAssert(text); - CaretAssert(surfaceDisplayed); - - const AnnotationCoordinate* coord = text->getCoordinate(); - StructureEnum::Enum structure; - int32_t surfaceNumberOfNodes(0); - int32_t vertexIndex(0); - float offsetLength(0.0f); - AnnotationSurfaceOffsetVectorTypeEnum::Enum offsetVectorType; - coord->getSurfaceSpace(structure, surfaceNumberOfNodes, vertexIndex, offsetLength, offsetVectorType); - - if (structure != surfaceDisplayed->getStructure()) { - return false; - } - if (surfaceDisplayed->getNumberOfNodes() != surfaceNumberOfNodes) { - return false; - } - float vertexXYZ[3]; - surfaceDisplayed->getCoordinate(vertexIndex, - vertexXYZ); - float normalXYZ[3]; - getSurfaceNormalVector(surfaceDisplayed, vertexIndex, normalXYZ); - const BoundingBox* boundingBox = surfaceDisplayed->getBoundingBox(); - const float surfaceExtentZ = boundingBox->getDifferenceZ(); - - /* - * Need to restore model space - * Recall that all other annotation spaces are drawn in window space - */ - glMatrixMode(GL_PROJECTION); - glPushMatrix(); - glLoadMatrixd(m_modelSpaceProjectionMatrix); - - glMatrixMode(GL_MODELVIEW); - glPushMatrix(); - glLoadMatrixd(m_modelSpaceModelMatrix); - int32_t savedViewport[4]; - glGetIntegerv(GL_VIEWPORT, - savedViewport); - glViewport(m_modelSpaceViewport[0], - m_modelSpaceViewport[1], - m_modelSpaceViewport[2], - m_modelSpaceViewport[3]); - - glPushMatrix(); - - /* - * Matrix that rotates text to plane of vertex's normal vector - */ - Matrix4x4 rotationMatrix; - rotationMatrix.setMatrixToOpenGLRotationFromVector(normalXYZ); - double rotationArray[16]; - rotationMatrix.getMatrixForOpenGL(rotationArray); - - - /* - * Translate to the vertex and then translate using offset - * vector of text. - */ - const float offsetVectorXYZ[3] { - normalXYZ[0] * offsetLength, - normalXYZ[1] * offsetLength, - normalXYZ[2] * offsetLength - }; - glTranslatef(vertexXYZ[0], vertexXYZ[1], vertexXYZ[2]); - glTranslatef(offsetVectorXYZ[0], offsetVectorXYZ[1], offsetVectorXYZ[2]); - - /* - * Rotate into plane of surface normal vector - */ - glMultMatrixd(rotationArray); - - /* - * Up vector in local coordinates (text drawing plane) - * Z points out of text - */ - const float len(25.0f); - const float localUpXYZ[3] { - 0.0, - len, - 0.0 - }; - - /* - * Inverse matrix goes back to surface coordinate system - */ - Matrix4x4 inverseMatrix(rotationMatrix); - inverseMatrix.invert(); - - /* - * Create the plane in which text is drawn - * (plane is not used for drawing text but is used - * to orient the text). The plane is constructed - * from the vertex's normal vector and the the - * vertex's XYZ-coordinate. - */ - Plane textDrawingPlane(normalXYZ, - vertexXYZ); - - /* - * Project a point with a large Z-coordinate to the plane - * and use it to creat the 'text up orientation vector'. - */ - const float zBig[3] { 0.0, 0.0, 10000.0 }; - float textUpOrientationVectorXYZ[3]; - textDrawingPlane.projectPointToPlane(zBig, textUpOrientationVectorXYZ); - inverseMatrix.multiplyPoint3(textUpOrientationVectorXYZ); - - /* - * Vector is parallel to surface Z-axis - */ - float vectorSurfaceAxisZ[3] = { 0.0f, 0.0f, 100.0f }; - inverseMatrix.multiplyPoint3(vectorSurfaceAxisZ); + } - if (debugFlag) { - /* - * Red line is normal vector - * Green line is "text up" vector - * Blue points to surface Z-vector - * Yellow is parallel to surface Z-axis - */ - const float startXYZ[3] { - 0.0, - 0.0, - 0.0 - }; - const float endXYZ[3] { - 0.0, - 0.0, - len - }; - glBegin(GL_LINES); - glColor3f(1.0, 0.0, 0.0); - glVertex3fv(startXYZ); - glVertex3fv(endXYZ); - glColor3f(0.0, 1.0, 0.0); - glVertex3fv(startXYZ); - glVertex3fv(localUpXYZ); - glColor3f(0.0, 0.0, 1.0); - glVertex3fv(startXYZ); - glVertex3fv(textUpOrientationVectorXYZ); - glColor3f(1.0, 1.0, 0.0); - glVertex3fv(startXYZ); - glVertex3fv(vectorSurfaceAxisZ); - glEnd(); - - { -// /* -// * Vector is parallel to surface Z-axis -// */ -// float bigZ[3] = { 0.0f, 0.0f, 100.0f }; -// inverseMatrix.multiplyPoint3(bigZ); -// glBegin(GL_LINES); -// glColor3f(1.0, 1.0, 0.0); -// glVertex3fv(startXYZ); -// glVertex3fv(bigZ); -// glEnd(); - - } + if (numNeighbors > 0) { + normalVectorOut[0] /= numNeighbors; + normalVectorOut[1] /= numNeighbors; + normalVectorOut[2] /= numNeighbors; } +} + +/** + * Draw an annotation text with surface tangent offset + * + * @param annotationFile + * File containing the annotation. + * @param text + * Annotation text to draw. + * @param surfaceDisplayed + * Surface that is displayed (may be NULL). + * @param surfaceExtentZ + * Z-extent of the surface. + * @param vertexXYZ, + * Coordinate of the vertex. + * @param vertexNormalXYZ + * Normal vector of vertex. + * @return + * True if the annotation was drawn while NOT selecting annotations. + */ +bool +BrainOpenGLAnnotationDrawingFixedPipeline::drawTextSurfaceTangentOffset(AnnotationFile* annotationFile, + AnnotationText* text, + const Surface* surfaceDisplayed, + const float surfaceExtentZ, + const float vertexXYZ[3], + const float vertexNormalXYZ[3]) +{ + CaretAssert(annotationFile); + CaretAssert(text); + CaretAssert(surfaceDisplayed); /* - * Rotate the text so that the horzontal flow of the text - * is orthogonal to the 'text up orientation vector'. + * Annotations with "DISPLAY_GROUP" propery may be turned on/off by user. */ -// const float angle = angleInDegreesBetweenVectors(localUpXYZ, -// textUpOrientationVectorXYZ); - const float angle = angleInDegreesBetweenVectors(localUpXYZ, - vectorSurfaceAxisZ); - glRotatef(-angle, 0.0, 0.0, 1.0); - - if (debugFlag) { - std::cout << "Annotation: " << text->getText() << std::endl; - std::cout << " Plane: " << textDrawingPlane.toString() << std::endl; - std::cout << " Local Up Vector: " << AString::fromNumbers(localUpXYZ, 3, ", ") << std::endl; - std::cout << " Angle: " << angle << std::endl; + DisplayPropertiesAnnotation* dpa = m_inputs->m_brain->getDisplayPropertiesAnnotation(); + if (text->testProperty(Annotation::Property::DISPLAY_GROUP)) { + if ( ! dpa->isDisplayTextAnnotations()) { + return false; + } } double bottomLeft[3]; @@ -2677,9 +3168,9 @@ BrainOpenGLAnnotationDrawingFixedPipeline::drawTextTangentOffset(AnnotationFile* GraphicsPrimitiveV3fN3f primitive(GraphicsPrimitive::PrimitiveType::OPENGL_TRIANGLE_STRIP, selectionColorRGBA); const double doubleNormalXYZ[3] { - normalXYZ[0], - normalXYZ[1], - normalXYZ[2] + vertexNormalXYZ[0], + vertexNormalXYZ[1], + vertexNormalXYZ[2] }; primitive.addVertex(topLeft, doubleNormalXYZ); primitive.addVertex(bottomLeft, doubleNormalXYZ); @@ -2696,7 +3187,7 @@ BrainOpenGLAnnotationDrawingFixedPipeline::drawTextTangentOffset(AnnotationFile* m_brainOpenGLFixedPipeline->getTextRenderer()->drawTextInModelSpace(*text, m_surfaceViewScaling, surfaceExtentZ, - normalXYZ, + vertexNormalXYZ, m_textDrawingFlags); glPopMatrix(); @@ -2738,21 +3229,309 @@ BrainOpenGLAnnotationDrawingFixedPipeline::drawTextTangentOffset(AnnotationFile* glPopAttrib(); } - glPopMatrix(); /* restore MODELVIEW */ - - glViewport(savedViewport[0], - savedViewport[1], - savedViewport[2], - savedViewport[3]); - glPopMatrix(); - - glMatrixMode(GL_PROJECTION); - glPopMatrix(); - glMatrixMode(GL_MODELVIEW); - return textDrawnFlag; } +///** +// * Draw an annotation text with surface tangent offset +// * +// * @param annotationFile +// * File containing the annotation. +// * @param text +// * Annotation text to draw. +// * @param surfaceDisplayed +// * Surface that is displayed (may be NULL). +// * @return +// * True if the annotation was drawn while NOT selecting annotations. +// */ +//bool +//BrainOpenGLAnnotationDrawingFixedPipeline::drawTextSurfaceTangentOffset(AnnotationFile* annotationFile, +// AnnotationText* text, +// const Surface* surfaceDisplayed) +//{ +// CaretAssert(annotationFile); +// CaretAssert(text); +// CaretAssert(surfaceDisplayed); +// +// /* +// * Annotations with "DISPLAY_GROUP" propery may be turned on/off by user. +// */ +// DisplayPropertiesAnnotation* dpa = m_inputs->m_brain->getDisplayPropertiesAnnotation(); +// if (text->testProperty(Annotation::Property::DISPLAY_GROUP)) { +// if ( ! dpa->isDisplayTextAnnotations()) { +// return false; +// } +// } +// +// const AnnotationCoordinate* coord = text->getCoordinate(); +// CaretAssert(coord->getSurfaceOffsetVectorType() == AnnotationSurfaceOffsetVectorTypeEnum::TANGENT); +// StructureEnum::Enum structure; +// int32_t surfaceNumberOfNodes(0); +// int32_t vertexIndex(0); +// float offsetLength(0.0f); +// AnnotationSurfaceOffsetVectorTypeEnum::Enum offsetVectorType; +// coord->getSurfaceSpace(structure, surfaceNumberOfNodes, vertexIndex, offsetLength, offsetVectorType); +// +// if (structure != surfaceDisplayed->getStructure()) { +// return false; +// } +// if (surfaceDisplayed->getNumberOfNodes() != surfaceNumberOfNodes) { +// return false; +// } +// float vertexXYZ[3]; +// surfaceDisplayed->getCoordinate(vertexIndex, +// vertexXYZ); +// float normalXYZ[3]; +// getSurfaceNormalVector(surfaceDisplayed, vertexIndex, normalXYZ); +// const BoundingBox* boundingBox = surfaceDisplayed->getBoundingBox(); +// const float surfaceExtentZ = boundingBox->getDifferenceZ(); +// +// /* +// * Need to restore model space +// * Recall that all other annotation spaces are drawn in window space +// */ +// glMatrixMode(GL_PROJECTION); +// glPushMatrix(); +// glLoadMatrixd(m_modelSpaceProjectionMatrix); +// +// glMatrixMode(GL_MODELVIEW); +// glPushMatrix(); +// glLoadMatrixd(m_modelSpaceModelMatrix); +// int32_t savedViewport[4]; +// glGetIntegerv(GL_VIEWPORT, +// savedViewport); +// glViewport(m_modelSpaceViewport[0], +// m_modelSpaceViewport[1], +// m_modelSpaceViewport[2], +// m_modelSpaceViewport[3]); +// +// glPushMatrix(); +// +// /* +// * Matrix that rotates text to plane of vertex's normal vector +// */ +// Matrix4x4 rotationMatrix; +// rotationMatrix.setMatrixToOpenGLRotationFromVector(normalXYZ); +// double rotationArray[16]; +// rotationMatrix.getMatrixForOpenGL(rotationArray); +// +// +// /* +// * Translate to the vertex and then translate using offset +// * vector of text. +// */ +// const float offsetVectorXYZ[3] { +// normalXYZ[0] * offsetLength, +// normalXYZ[1] * offsetLength, +// normalXYZ[2] * offsetLength +// }; +// glTranslatef(vertexXYZ[0], vertexXYZ[1], vertexXYZ[2]); +// glTranslatef(offsetVectorXYZ[0], offsetVectorXYZ[1], offsetVectorXYZ[2]); +// +// /* +// * Rotate into plane of surface normal vector +// */ +// glMultMatrixd(rotationArray); +// +// /* +// * Up vector in local coordinates (text drawing plane) +// * Z points out of text +// */ +// const float len(25.0f); +// const float localUpXYZ[3] { +// 0.0, +// len, +// 0.0 +// }; +// +// /* +// * Inverse matrix goes back to surface coordinate system +// */ +// Matrix4x4 inverseMatrix(rotationMatrix); +// inverseMatrix.invert(); +// +// /* +// * Create the plane in which text is drawn +// * (plane is not used for drawing text but is used +// * to orient the text). The plane is constructed +// * from the vertex's normal vector and the the +// * vertex's XYZ-coordinate. +// */ +// Plane textDrawingPlane(normalXYZ, +// vertexXYZ); +// +// /* +// * Project a point with a large Z-coordinate to the plane +// * and use it to creat the 'text up orientation vector'. +// */ +// const float zBig[3] { 0.0, 0.0, 10000.0 }; +// float textUpOrientationVectorXYZ[3]; +// textDrawingPlane.projectPointToPlane(zBig, textUpOrientationVectorXYZ); +// inverseMatrix.multiplyPoint3(textUpOrientationVectorXYZ); +// +// /* +// * Vector is parallel to surface Z-axis +// */ +// float vectorSurfaceAxisZ[3] = { 0.0f, 0.0f, 100.0f }; +// inverseMatrix.multiplyPoint3(vectorSurfaceAxisZ); +// +// if (debugFlag) { +// /* +// * Red line is normal vector +// * Green line is "text up" vector +// * Blue points to surface Z-vector +// * Yellow is parallel to surface Z-axis +// */ +// const float startXYZ[3] { +// 0.0, +// 0.0, +// 0.0 +// }; +// const float endXYZ[3] { +// 0.0, +// 0.0, +// len +// }; +// glBegin(GL_LINES); +// glColor3f(1.0, 0.0, 0.0); +// glVertex3fv(startXYZ); +// glVertex3fv(endXYZ); +// glColor3f(0.0, 1.0, 0.0); +// glVertex3fv(startXYZ); +// glVertex3fv(localUpXYZ); +// glColor3f(0.0, 0.0, 1.0); +// glVertex3fv(startXYZ); +// glVertex3fv(textUpOrientationVectorXYZ); +// glColor3f(1.0, 1.0, 0.0); +// glVertex3fv(startXYZ); +// glVertex3fv(vectorSurfaceAxisZ); +// glEnd(); +// +// { +//// /* +//// * Vector is parallel to surface Z-axis +//// */ +//// float bigZ[3] = { 0.0f, 0.0f, 100.0f }; +//// inverseMatrix.multiplyPoint3(bigZ); +//// glBegin(GL_LINES); +//// glColor3f(1.0, 1.0, 0.0); +//// glVertex3fv(startXYZ); +//// glVertex3fv(bigZ); +//// glEnd(); +// +// } +// } +// +// /* +// * Rotate the text so that the horzontal flow of the text +// * is orthogonal to the 'text up orientation vector'. +// */ +//// const float angle = angleInDegreesBetweenVectors(localUpXYZ, +//// textUpOrientationVectorXYZ); +// const float angle = angleInDegreesBetweenVectors(localUpXYZ, +// vectorSurfaceAxisZ); +// glRotatef(-angle, 0.0, 0.0, 1.0); +// +// if (debugFlag) { +// std::cout << "Annotation: " << text->getText() << std::endl; +// std::cout << " Plane: " << textDrawingPlane.toString() << std::endl; +// std::cout << " Local Up Vector: " << AString::fromNumbers(localUpXYZ, 3, ", ") << std::endl; +// std::cout << " Angle: " << angle << std::endl; +// } +// +// double bottomLeft[3]; +// double bottomRight[3]; +// double topRight[3]; +// double topLeft[3]; +// double underlineStart[3]; +// double underlineEnd[3]; +// m_brainOpenGLFixedPipeline->getTextRenderer()->getBoundsForTextInModelSpace(*text, m_surfaceViewScaling, surfaceExtentZ, m_textDrawingFlags, +// bottomLeft, bottomRight, topRight, topLeft, +// underlineStart, underlineEnd); +// +// bool textDrawnFlag = false; +// if (m_selectionModeFlag) { +// uint8_t selectionColorRGBA[4] = { 0, 0, 0, 0 }; +// getIdentificationColor(selectionColorRGBA); +// GraphicsPrimitiveV3fN3f primitive(GraphicsPrimitive::PrimitiveType::OPENGL_TRIANGLE_STRIP, +// selectionColorRGBA); +// const double doubleNormalXYZ[3] { +// normalXYZ[0], +// normalXYZ[1], +// normalXYZ[2] +// }; +// primitive.addVertex(topLeft, doubleNormalXYZ); +// primitive.addVertex(bottomLeft, doubleNormalXYZ); +// primitive.addVertex(topRight, doubleNormalXYZ); +// primitive.addVertex(bottomRight, doubleNormalXYZ); +// GraphicsEngineDataOpenGL::draw(&primitive); +// m_selectionInfo.push_back(SelectionInfo(annotationFile, +// text, +// AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_NONE, +// vertexXYZ)); +// } +// else { +// glPushMatrix(); +// m_brainOpenGLFixedPipeline->getTextRenderer()->drawTextInModelSpace(*text, +// m_surfaceViewScaling, +// surfaceExtentZ, +// normalXYZ, +// m_textDrawingFlags); +// glPopMatrix(); +// +// textDrawnFlag = true; +// } +// +// if (text->isSelectedForEditing(m_inputs->m_windowIndex)) { +// glPushAttrib(GL_POLYGON_BIT +// | GL_LIGHTING_BIT); +// +// /* +// * So that text and background do not mix together +// * in the Z-buffer +// */ +// glEnable(GL_POLYGON_OFFSET_FILL); +// glEnable(GL_POLYGON_OFFSET_LINE); +// glEnable(GL_POLYGON_OFFSET_POINT); +// glPolygonOffset(-1.0, 1.0); +// glDisable(GL_LIGHTING); +// +// float floatBottomLeft[3]; +// float floatBottomRight[3]; +// float floatTopRight[3]; +// float floatTopLeft[3]; +// for (int32_t i = 0; i < 3; i++) { +// floatBottomLeft[i] = bottomLeft[i]; +// floatBottomRight[i] = bottomRight[i]; +// floatTopLeft[i] = topLeft[i]; +// floatTopRight[i] = topRight[i]; +// } +// drawAnnotationTwoDimSizingHandles(annotationFile, +// text, +// floatBottomLeft, +// floatBottomRight, +// floatTopRight, +// floatTopLeft, +// s_sizingHandleLineWidthInPixels, +// text->getRotationAngle()); +// glPopAttrib(); +// } +// +// glPopMatrix(); /* restore MODELVIEW */ +// +// glViewport(savedViewport[0], +// savedViewport[1], +// savedViewport[2], +// savedViewport[3]); +// glPopMatrix(); +// +// glMatrixMode(GL_PROJECTION); +// glPopMatrix(); +// glMatrixMode(GL_MODELVIEW); +// +// return textDrawnFlag; +//} + /** * Draw an annotation text. * @@ -2783,23 +3562,6 @@ BrainOpenGLAnnotationDrawingFixedPipeline::drawText(AnnotationFile* annotationFi } } - if (text->getCoordinateSpace() == AnnotationCoordinateSpaceEnum::SURFACE) { - switch (text->getCoordinate()->getSurfaceOffsetVectorType()) { - case AnnotationSurfaceOffsetVectorTypeEnum::CENTROID_THRU_VERTEX: - break; - case AnnotationSurfaceOffsetVectorTypeEnum::SURFACE_NORMAL: - break; - case AnnotationSurfaceOffsetVectorTypeEnum::TANGENT: - /* - * Tangent offset is drawn in model space - */ - return drawTextTangentOffset(annotationFile, - text, - surfaceDisplayed); - break; - } - } - float annXYZ[3]; if ( ! getAnnotationDrawingSpaceCoordinate(text, text->getCoordinate(), @@ -3121,6 +3883,123 @@ BrainOpenGLAnnotationDrawingFixedPipeline::drawImage(AnnotationFile* annotationF return drawnFlag; } +/** + * Draw an annotation image in surface tangent offset space + * + * @param annotationFile + * File containing the annotation. + * @param image + * Image to draw. + * @param surfaceDisplayed + * Surface that is displayed (may be NULL). + * @param surfaceExtentZ + * Z-extent of the surface. + * @param vertexXYZ + * Coordinate of the vertex. + * @return + * True if the annotation was drawn while NOT selecting annotations. + */ +bool +BrainOpenGLAnnotationDrawingFixedPipeline::drawImageSurfaceTangentOffset(AnnotationFile* annotationFile, + AnnotationImage* image, + const Surface* surfaceDisplayed, + const float surfaceExtentZ, + const float vertexXYZ[3]) +{ + CaretAssert(annotationFile); + CaretAssert(image); + CaretAssert(surfaceDisplayed); + CaretAssert(image->getType() == AnnotationTypeEnum::IMAGE); + + const float halfWidth = ((image->getWidth() / 100.0) * surfaceExtentZ) / 2.0; + const float halfHeight = ((image->getHeight() / 100.0) * surfaceExtentZ) / 2.0; + float bottomLeft[3] { -halfWidth, -halfHeight, 0.0f }; + float bottomRight[3] { halfWidth, -halfHeight, 0.0f }; + float topRight[3] { halfWidth, halfHeight, 0.0f }; + float topLeft[3] { -halfWidth, halfHeight, 0.0f }; + + const float selectionCenterXYZ[3] = { + vertexXYZ[0], + vertexXYZ[1], + vertexXYZ[2] + }; + + float lineThickness = ((image->getLineWidthPercentage() / 100.0) + * surfaceExtentZ); + if (m_selectionModeFlag) { + lineThickness = std::max(lineThickness, + s_selectionLineMinimumPixelWidth); + } + + uint8_t backgroundRGBA[4]; + image->getBackgroundColorRGBA(backgroundRGBA); + uint8_t foregroundRGBA[4]; + image->getLineColorRGBA(foregroundRGBA); + + const bool drawForegroundFlag = (foregroundRGBA[3] > 0); + const bool drawAnnotationFlag = true; + + bool drawnFlag = false; + + if (drawAnnotationFlag) { + if (m_selectionModeFlag) { + uint8_t selectionColorRGBA[4]; + getIdentificationColor(selectionColorRGBA); + + /* + * When selecting draw only background if it is enabled + * since it is opaque and prevents "behind" annotations + * from being selected + */ + GraphicsShape::drawBoxFilledByteColor(bottomLeft, + bottomRight, + topRight, + topLeft, + selectionColorRGBA); + m_selectionInfo.push_back(SelectionInfo(annotationFile, + image, + AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_NONE, + selectionCenterXYZ)); + } + else { + image->setVertexBounds(bottomLeft, + bottomRight, + topRight, + topLeft); + GraphicsPrimitiveV3fT3f* primitive = image->getGraphicsPrimitive(); + if (primitive != NULL) { + if (primitive->isValid()) { + GraphicsEngineDataOpenGL::draw(primitive); + } + drawnFlag = true; + } + + if (drawForegroundFlag) { + GraphicsShape::drawBoxOutlineByteColor(bottomLeft, + bottomRight, + topRight, + topLeft, + foregroundRGBA, + GraphicsPrimitive::LineWidthType::PIXELS, + lineThickness); + drawnFlag = true; + } + } + if (image->isSelectedForEditing(m_inputs->m_windowIndex)) { + drawAnnotationTwoDimSizingHandles(annotationFile, + image, + bottomLeft, + bottomRight, + topRight, + topLeft, + s_sizingHandleLineWidthInPixels, + image->getRotationAngle()); + } + } + + return drawnFlag; +} + /** * Create the coordinates for drawing a line with optional arrows at the end points. * @@ -3381,7 +4260,156 @@ BrainOpenGLAnnotationDrawingFixedPipeline::drawLine(AnnotationFile* annotationFi } /** - * When lines are very thin, they can be difficult to select. This method will + * Draw an annotation line that is in surface space with tangent offset. + * + * @param annotationFile + * File containing the annotation. + * @param line + * Annotation line to draw. + * @param surfaceDisplayed + * Surface that is displayed (may be NULL). + * @return + * True if the annotation was drawn while NOT selecting annotations. + */ +bool +BrainOpenGLAnnotationDrawingFixedPipeline::drawLineSurfaceTextureOffset(AnnotationFile* annotationFile, + AnnotationLine* line, + const Surface* surfaceDisplayed, + const float surfaceExtentZ) +{ + CaretAssert(line); + CaretAssert(line->getType() == AnnotationTypeEnum::LINE); + + StructureEnum::Enum structureOne(StructureEnum::INVALID); + StructureEnum::Enum structureTwo(StructureEnum::INVALID); + int32_t numberOfVerticesOne(0); + int32_t numberOfVerticesTwo(0); + int32_t vertexIndexOne(-1); + int32_t vertexIndexTwo(-1); + float offsetLengthOne(0.0f); + float offsetLengthTwo(0.0f); + AnnotationSurfaceOffsetVectorTypeEnum::Enum surfaceOffsetVectorOne(AnnotationSurfaceOffsetVectorTypeEnum::TANGENT); + AnnotationSurfaceOffsetVectorTypeEnum::Enum surfaceOffsetVectorTwo(AnnotationSurfaceOffsetVectorTypeEnum::TANGENT); + line->getStartCoordinate()->getSurfaceSpace(structureOne, numberOfVerticesOne, vertexIndexOne, + offsetLengthOne, surfaceOffsetVectorOne); + line->getEndCoordinate()->getSurfaceSpace(structureTwo, numberOfVerticesTwo, vertexIndexTwo, + offsetLengthTwo, surfaceOffsetVectorTwo); + + if ((surfaceDisplayed->getStructure() != structureOne) + || (surfaceDisplayed->getStructure() != structureTwo) + || (surfaceDisplayed->getNumberOfNodes() != numberOfVerticesOne) + || (surfaceDisplayed->getNumberOfNodes() != numberOfVerticesTwo) + || (vertexIndexOne < 0) + || (vertexIndexTwo < 0)) { + return false; + } + float lineHeadXYZ[3]; + float lineTailXYZ[3]; + surfaceDisplayed->getCoordinate(vertexIndexOne, lineHeadXYZ); + surfaceDisplayed->getCoordinate(vertexIndexTwo, lineTailXYZ); + + float offsetVectorOne[3]; + float offsetVectorTwo[3]; + surfaceDisplayed->getNormalVector(vertexIndexOne, + offsetVectorOne); + surfaceDisplayed->getNormalVector(vertexIndexTwo, + offsetVectorTwo); + for (int32_t i = 0; i < 3; i++) { + lineHeadXYZ[i] += (offsetVectorOne[i] * offsetLengthOne); + lineTailXYZ[i] += (offsetVectorTwo[i] * offsetLengthTwo); + } + + std::vector lineCoordinates; + lineCoordinates.insert(lineCoordinates.end(), + lineHeadXYZ, lineHeadXYZ + 3); + lineCoordinates.insert(lineCoordinates.end(), + lineTailXYZ, lineTailXYZ + 3); + + if (line->getLineWidthPercentage() <= 0.0) { + convertObsoleteLineWidthPixelsToPercentageWidth(line); + } + float lineThickness = ((line->getLineWidthPercentage() / 100.0) + * surfaceExtentZ); + if (m_selectionModeFlag) { + lineThickness = std::max(lineThickness, + s_selectionLineMinimumPixelWidth); + } + + const float selectionCenterXYZ[3] = { + (lineHeadXYZ[0] + lineTailXYZ[0]) / 2.0f, + (lineHeadXYZ[1] + lineTailXYZ[1]) / 2.0f, + (lineHeadXYZ[2] + lineTailXYZ[2]) / 2.0f + }; + + uint8_t foregroundRGBA[4]; + line->getLineColorRGBA(foregroundRGBA); + + const bool drawForegroundFlag = (foregroundRGBA[3] > 0.0f); + + bool drawnFlag = false; + + if (drawForegroundFlag) { + if (m_selectionModeFlag) { + uint8_t selectionColorRGBA[4]; + getIdentificationColor(selectionColorRGBA); + + GraphicsShape::drawLinesByteColor(lineCoordinates, + selectionColorRGBA, + GraphicsPrimitive::LineWidthType::PIXELS, + lineThickness); +// if ( ! startArrowCoordinates.empty()) { +// GraphicsShape::drawLineStripMiterJoinByteColor(startArrowCoordinates, +// selectionColorRGBA, +// GraphicsPrimitive::LineWidthType::PERCENTAGE_VIEWPORT_HEIGHT, +// lineThickness); +// } +// if ( ! endArrowCoordinates.empty()) { +// GraphicsShape::drawLineStripMiterJoinByteColor(endArrowCoordinates, +// selectionColorRGBA, +// GraphicsPrimitive::LineWidthType::PERCENTAGE_VIEWPORT_HEIGHT, +// lineThickness); +// } + m_selectionInfo.push_back(SelectionInfo(annotationFile, + line, + AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_NONE, + selectionCenterXYZ)); + } + else { + if (drawForegroundFlag) { + GraphicsShape::drawLinesByteColor(lineCoordinates, + foregroundRGBA, + GraphicsPrimitive::LineWidthType::PIXELS, + lineThickness); +// if ( ! startArrowCoordinates.empty()) { +// GraphicsShape::drawLineStripMiterJoinByteColor(startArrowCoordinates, +// foregroundRGBA, +// GraphicsPrimitive::LineWidthType::PERCENTAGE_VIEWPORT_HEIGHT, +// lineThickness); +// } +// if ( ! endArrowCoordinates.empty()) { +// GraphicsShape::drawLineStripMiterJoinByteColor(endArrowCoordinates, +// foregroundRGBA, +// GraphicsPrimitive::LineWidthType::PERCENTAGE_VIEWPORT_HEIGHT, +// lineThickness); +// } + drawnFlag = true; + } + } + + if (line->isSelectedForEditing(m_inputs->m_windowIndex)) { + drawAnnotationOneDimSizingHandles(annotationFile, + line, + lineHeadXYZ, + lineTailXYZ, + s_sizingHandleLineWidthInPixels); + } + } + + return drawnFlag; +} + +/** + * When lines are very thin, they can be difficult to select. This method will * return the line thickness percentage, adjusted so that is thick enough that * the user will be able to select the annotation. * @@ -3644,13 +4672,12 @@ BrainOpenGLAnnotationDrawingFixedPipeline::drawAnnotationTwoDimSizingHandles(Ann const float rotationAngle) { CaretAssert(annotation); - AnnotationText* textAnn(NULL); + AnnotationText* textAnn = dynamic_cast(annotation); bool modelSpaceTangentTextFlag = false; - if (annotation->getType() == AnnotationTypeEnum::TEXT) { - textAnn = dynamic_cast(annotation); - CaretAssert(textAnn); - if (textAnn->getCoordinateSpace() == AnnotationCoordinateSpaceEnum::SURFACE) { - const AnnotationCoordinate* coord = textAnn->getCoordinate(); + { + AnnotationTwoDimensionalShape* twoDimShape = dynamic_cast(annotation); + if (twoDimShape != NULL) { + const AnnotationCoordinate* coord = twoDimShape->getCoordinate(); switch (coord->getSurfaceOffsetVectorType()) { case AnnotationSurfaceOffsetVectorTypeEnum::CENTROID_THRU_VERTEX: break; diff --git a/src/Brain/BrainOpenGLAnnotationDrawingFixedPipeline.h b/src/Brain/BrainOpenGLAnnotationDrawingFixedPipeline.h index e1f085ccd..3620ad253 100644 --- a/src/Brain/BrainOpenGLAnnotationDrawingFixedPipeline.h +++ b/src/Brain/BrainOpenGLAnnotationDrawingFixedPipeline.h @@ -26,6 +26,7 @@ #include "AnnotationCoordinateSpaceEnum.h" #include "AnnotationSizingHandleTypeEnum.h" +#include "AnnotationSurfaceOffsetVectorTypeEnum.h" #include "BrainOpenGLFixedPipeline.h" #include "BrainOpenGLTextRenderInterface.h" #include "CaretObject.h" @@ -42,6 +43,7 @@ namespace caret { class AnnotationFile; class AnnotationImage; class AnnotationLine; + class AnnotationOneDimensionalShape; class AnnotationOval; class AnnotationText; class AnnotationTwoDimensionalShape; @@ -187,6 +189,14 @@ namespace caret { Annotation* annotation, const Surface* surfaceDisplayed); + bool drawTwoDimAnnotationSurfaceTextureOffset(AnnotationFile* annotationFile, + AnnotationTwoDimensionalShape* annotation, + const Surface* surfaceDisplayed); + + bool drawOneDimAnnotationSurfaceTextureOffset(AnnotationFile* annotationFile, + AnnotationOneDimensionalShape* annotation, + const Surface* surfaceDisplayed); + void drawColorBar(AnnotationFile* annotationFile, AnnotationColorBar* colorBar); @@ -194,25 +204,51 @@ namespace caret { AnnotationBox* box, const Surface* surfaceDisplayed); + bool drawBoxSurfaceTangentOffset(AnnotationFile* annotationFile, + AnnotationBox* box, + const Surface* surfaceDisplayed, + const float surfaceExtentZ, + const float vertexXYZ[3]); + bool drawImage(AnnotationFile* annotationFile, AnnotationImage* image, - const Surface* surfaceDisplayed); + const Surface* surfaceDisplayed); + + bool drawImageSurfaceTangentOffset(AnnotationFile* annotationFile, + AnnotationImage* image, + const Surface* surfaceDisplayed, + const float surfaceExtentZ, + const float vertexXYZ[3]); bool drawLine(AnnotationFile* annotationFile, AnnotationLine* line, const Surface* surfaceDisplayed); + bool drawLineSurfaceTextureOffset(AnnotationFile* annotationFile, + AnnotationLine* line, + const Surface* surfaceDisplayed, + const float surfaceExtentZ); + bool drawOval(AnnotationFile* annotationFile, AnnotationOval* oval, const Surface* surfaceDisplayed); + bool drawOvalSurfaceTangentOffset(AnnotationFile* annotationFile, + AnnotationOval* oval, + const Surface* surfaceDisplayed, + const float surfaceExtentZ, + const float vertexXYZ[3]); + bool drawText(AnnotationFile* annotationFile, AnnotationText* text, const Surface* surfaceDisplayed); - bool drawTextTangentOffset(AnnotationFile* annotationFile, - AnnotationText* text, - const Surface* surfaceDisplayed); + bool drawTextSurfaceTangentOffset(AnnotationFile* annotationFile, + AnnotationText* text, + const Surface* surfaceDisplayed, + const float surfaceExtentZ, + const float vertexXYZ[3], + const float vertexNormalXYZ[3]); void drawColorBarSections(const AnnotationColorBar* colorBar, const float bottomLeft[3], @@ -366,15 +402,16 @@ namespace caret { std::unique_ptr m_transformEvent; - static constexpr float s_sizingHandleLineWidthInPixels = 2.0f; - static constexpr float s_selectionLineMinimumPixelWidth = 5.0f; + static const float s_sizingHandleLineWidthInPixels; + static const float s_selectionLineMinimumPixelWidth; // ADD_NEW_MEMBERS_HERE }; #ifdef __BRAIN_OPEN_G_L_ANNOTATION_DRAWING_FIXED_PIPELINE_DECLARE__ - // + const float BrainOpenGLAnnotationDrawingFixedPipeline::s_sizingHandleLineWidthInPixels = 2.0f; + const float BrainOpenGLAnnotationDrawingFixedPipeline::s_selectionLineMinimumPixelWidth = 5.0f; #endif // __BRAIN_OPEN_G_L_ANNOTATION_DRAWING_FIXED_PIPELINE_DECLARE__ } // namespace diff --git a/src/GuiQt/AnnotationCoordinateWidget.cxx b/src/GuiQt/AnnotationCoordinateWidget.cxx index 9e91a5a92..ba448a943 100644 --- a/src/GuiQt/AnnotationCoordinateWidget.cxx +++ b/src/GuiQt/AnnotationCoordinateWidget.cxx @@ -159,14 +159,22 @@ m_browserWindowIndex(browserWindowIndex) m_yCoordSpinBox->setMaximumWidth(spinBoxMaximumWidth); m_zCoordSpinBox->setMaximumWidth(spinBoxMaximumWidth); - m_surfaceOffsetVectorTypeComboBox = new EnumComboBoxTemplate(this); - m_surfaceOffsetVectorTypeComboBox->setup(); - QObject::connect(m_surfaceOffsetVectorTypeComboBox, SIGNAL(itemActivated()), - this, SLOT(surfaceOffsetVectorTypeChanged())); - m_surfaceOffsetVectorTypeComboBox->getWidget()->setFixedWidth(45); - m_surfaceOffsetVectorTypeComboBox->getWidget()->setToolTip("Vector for surface offset:\n" - " C - Centroid thru Vertex\n" - " N - Vertex Normal"); + m_surfaceOffsetVectorTypeComboBox = NULL; + switch (m_whichCoordinate) { + case COORDINATE_ONE: + m_surfaceOffsetVectorTypeComboBox = new EnumComboBoxTemplate(this); + m_surfaceOffsetVectorTypeComboBox->setup(); + QObject::connect(m_surfaceOffsetVectorTypeComboBox, SIGNAL(itemActivated()), + this, SLOT(surfaceOffsetVectorTypeChanged())); + m_surfaceOffsetVectorTypeComboBox->getWidget()->setFixedWidth(45); + m_surfaceOffsetVectorTypeComboBox->getWidget()->setToolTip("Vector for surface offset:\n" + " C - Centroid thru Vertex\n" + " N - Vertex Normal" + " T - Tangent"); + break; + case COORDINATE_TWO: + break; + } m_plusButtonToolTipText = ("Click the mouse to set the new location for the coordinate.\n" "After clicking the mouse, a dialog allows selection of the\n" @@ -200,7 +208,9 @@ m_browserWindowIndex(browserWindowIndex) surfaceLayout->addWidget(surfaceVertexLabel); surfaceLayout->addWidget(m_surfaceStructureComboBox->getWidget()); surfaceLayout->addWidget(m_surfaceNodeIndexSpinBox); - surfaceLayout->addWidget(m_surfaceOffsetVectorTypeComboBox->getWidget()); + if (m_surfaceOffsetVectorTypeComboBox != NULL) { + surfaceLayout->addWidget(m_surfaceOffsetVectorTypeComboBox->getWidget()); + } surfaceLayout->addWidget(m_surfaceOffsetLengthSpinBox); m_coordinateWidget = new QWidget(); @@ -479,7 +489,9 @@ AnnotationCoordinateWidget::updateContent(Annotation* annotation) m_surfaceNodeIndexSpinBox->setValue(surfaceNodeIndex); m_surfaceNodeIndexSpinBox->blockSignals(false); - m_surfaceOffsetVectorTypeComboBox->setSelectedItem(surfaceOffsetVector); + if (m_surfaceOffsetVectorTypeComboBox != NULL) { + m_surfaceOffsetVectorTypeComboBox->setSelectedItem(surfaceOffsetVector); + } m_surfaceOffsetLengthSpinBox->blockSignals(true); m_surfaceOffsetLengthSpinBox->setValue(surfaceOffsetLength); @@ -530,6 +542,7 @@ AnnotationCoordinateWidget::surfaceOffsetVectorTypeChanged() const AnnotationCoordinate* coordinate = getCoordinate(); if ((m_annotation != NULL) && (coordinate != NULL)) { + CaretAssert(m_surfaceOffsetVectorTypeComboBox); AnnotationCoordinate::setUserDefautlSurfaceOffsetVectorType(m_surfaceOffsetVectorTypeComboBox->getSelectedItem()); valueChanged(); } @@ -581,7 +594,9 @@ AnnotationCoordinateWidget::valueChanged() surfaceNodeIndex = m_surfaceNodeIndexSpinBox->value(); surfaceOffsetLength = m_surfaceOffsetLengthSpinBox->value(); - surfaceOffsetVector = m_surfaceOffsetVectorTypeComboBox->getSelectedItem(); + if (m_surfaceOffsetVectorTypeComboBox != NULL) { + surfaceOffsetVector = m_surfaceOffsetVectorTypeComboBox->getSelectedItem(); + } coordinateCopy.setSurfaceSpace(structure, surfaceNumberOfNodes, -- GitLab From 97c2eed3e0f9261b681ba89053c683020085d648 Mon Sep 17 00:00:00 2001 From: John Harwell Date: Wed, 5 Sep 2018 15:42:32 -0500 Subject: [PATCH 034/427] WB-636 Texture Based Annotations: Improvements with lines drawing and line selection symbols for texture surface offset lines. --- src/Annotations/Annotation.cxx | 35 ++ src/Annotations/Annotation.h | 2 + .../AnnotationTwoDimensionalShape.cxx | 28 +- ...inOpenGLAnnotationDrawingFixedPipeline.cxx | 575 ++++-------------- src/Brain/FtglFontTextRenderer.cxx | 6 +- src/Graphics/GraphicsUtilitiesOpenGL.cxx | 42 ++ src/Graphics/GraphicsUtilitiesOpenGL.h | 2 + 7 files changed, 219 insertions(+), 471 deletions(-) diff --git a/src/Annotations/Annotation.cxx b/src/Annotations/Annotation.cxx index f18baee24..e2ac520a1 100644 --- a/src/Annotations/Annotation.cxx +++ b/src/Annotations/Annotation.cxx @@ -665,6 +665,41 @@ Annotation::setCoordinateSpace(const AnnotationCoordinateSpaceEnum::Enum coordin } } +/** + * @return Is this annotation in surface coordinate space + * with tangent selected for the surface offset vector? + */ +bool +Annotation::isInSurfaceSpaceWithTangentOffset() const +{ + bool flag = false; + + switch (m_coordinateSpace) { + case AnnotationCoordinateSpaceEnum::CHART: + break; + case AnnotationCoordinateSpaceEnum::STEREOTAXIC: + break; + case AnnotationCoordinateSpaceEnum::SURFACE: + switch (getSurfaceOffsetVectorType()) { + case AnnotationSurfaceOffsetVectorTypeEnum::CENTROID_THRU_VERTEX: + break; + case AnnotationSurfaceOffsetVectorTypeEnum::SURFACE_NORMAL: + break; + case AnnotationSurfaceOffsetVectorTypeEnum::TANGENT: + flag = true; + break; + } + break; + case AnnotationCoordinateSpaceEnum::TAB: + break; + case AnnotationCoordinateSpaceEnum::VIEWPORT: + break; + case AnnotationCoordinateSpaceEnum::WINDOW: + break; + } + return flag; +} + /** * @return The tab index. Valid only for tab coordinate space annotations. */ diff --git a/src/Annotations/Annotation.h b/src/Annotations/Annotation.h index e986a82d6..7988bb72e 100644 --- a/src/Annotations/Annotation.h +++ b/src/Annotations/Annotation.h @@ -158,6 +158,8 @@ namespace caret { virtual AnnotationSurfaceOffsetVectorTypeEnum::Enum getSurfaceOffsetVectorType() const = 0; + bool isInSurfaceSpaceWithTangentOffset() const; + int32_t getTabIndex() const; void setTabIndex(const int32_t tabIndex); diff --git a/src/Annotations/AnnotationTwoDimensionalShape.cxx b/src/Annotations/AnnotationTwoDimensionalShape.cxx index feae6a7c3..caf6a72bd 100644 --- a/src/Annotations/AnnotationTwoDimensionalShape.cxx +++ b/src/Annotations/AnnotationTwoDimensionalShape.cxx @@ -338,33 +338,9 @@ AnnotationTwoDimensionalShape::applyCoordinatesSizeAndRotationFromOther(const An bool AnnotationTwoDimensionalShape::isSizeHandleValid(const AnnotationSizingHandleTypeEnum::Enum sizingHandle) const { - bool viewportFlag = false; + const bool viewportFlag = (getCoordinateSpace() == AnnotationCoordinateSpaceEnum::VIEWPORT); - bool surfaceTangentOffsetFlag = false; - switch (getCoordinateSpace()) { - case AnnotationCoordinateSpaceEnum::CHART: - break; - case AnnotationCoordinateSpaceEnum::STEREOTAXIC: - break; - case AnnotationCoordinateSpaceEnum::SURFACE: - switch (getCoordinate()->getSurfaceOffsetVectorType()) { - case AnnotationSurfaceOffsetVectorTypeEnum::CENTROID_THRU_VERTEX: - break; - case AnnotationSurfaceOffsetVectorTypeEnum::SURFACE_NORMAL: - break; - case AnnotationSurfaceOffsetVectorTypeEnum::TANGENT: - surfaceTangentOffsetFlag = true; - break; - } - break; - case AnnotationCoordinateSpaceEnum::TAB: - break; - case AnnotationCoordinateSpaceEnum::VIEWPORT: - viewportFlag = true; - break; - case AnnotationCoordinateSpaceEnum::WINDOW: - break; - } + const bool surfaceTangentOffsetFlag = isInSurfaceSpaceWithTangentOffset(); bool allowsMovingFlag = false; bool allowsCornerResizingFlag = false; diff --git a/src/Brain/BrainOpenGLAnnotationDrawingFixedPipeline.cxx b/src/Brain/BrainOpenGLAnnotationDrawingFixedPipeline.cxx index 032765178..7242b9056 100644 --- a/src/Brain/BrainOpenGLAnnotationDrawingFixedPipeline.cxx +++ b/src/Brain/BrainOpenGLAnnotationDrawingFixedPipeline.cxx @@ -1085,59 +1085,21 @@ BrainOpenGLAnnotationDrawingFixedPipeline::drawAnnotation(AnnotationFile* annota bool drawnFlag = false; - bool surfaceTextureSpaceFlag = false; - switch (annotation->getCoordinateSpace()) { - case AnnotationCoordinateSpaceEnum::CHART: - break; - case AnnotationCoordinateSpaceEnum::STEREOTAXIC: - break; - case AnnotationCoordinateSpaceEnum::SURFACE: - if (annotation->getCoordinateSpace() == AnnotationCoordinateSpaceEnum::SURFACE) { - AnnotationTwoDimensionalShape* twoDimAnn = dynamic_cast(annotation); - if (twoDimAnn != NULL) { - switch (twoDimAnn->getCoordinate()->getSurfaceOffsetVectorType()) { - case AnnotationSurfaceOffsetVectorTypeEnum::CENTROID_THRU_VERTEX: - break; - case AnnotationSurfaceOffsetVectorTypeEnum::SURFACE_NORMAL: - break; - case AnnotationSurfaceOffsetVectorTypeEnum::TANGENT: - { - drawnFlag = drawTwoDimAnnotationSurfaceTextureOffset(annotationFile, - twoDimAnn, - surfaceDisplayed); - surfaceTextureSpaceFlag = true; - } - break; - } - } - else { - AnnotationOneDimensionalShape* oneDimAnn = dynamic_cast(annotation); - switch (oneDimAnn->getStartCoordinate()->getSurfaceOffsetVectorType()) { - case AnnotationSurfaceOffsetVectorTypeEnum::CENTROID_THRU_VERTEX: - break; - case AnnotationSurfaceOffsetVectorTypeEnum::SURFACE_NORMAL: - break; - case AnnotationSurfaceOffsetVectorTypeEnum::TANGENT: - { - drawnFlag = drawOneDimAnnotationSurfaceTextureOffset(annotationFile, - oneDimAnn, - surfaceDisplayed); - surfaceTextureSpaceFlag = true; - } - break; - } - } - } - break; - case AnnotationCoordinateSpaceEnum::TAB: - break; - case AnnotationCoordinateSpaceEnum::VIEWPORT: - break; - case AnnotationCoordinateSpaceEnum::WINDOW: - break; + if (annotation->isInSurfaceSpaceWithTangentOffset()) { + AnnotationTwoDimensionalShape* twoDimAnn = dynamic_cast(annotation); + if (twoDimAnn != NULL) { + drawnFlag = drawTwoDimAnnotationSurfaceTextureOffset(annotationFile, + twoDimAnn, + surfaceDisplayed); + } + else { + AnnotationOneDimensionalShape* oneDimAnn = dynamic_cast(annotation); + drawnFlag = drawOneDimAnnotationSurfaceTextureOffset(annotationFile, + oneDimAnn, + surfaceDisplayed); + } } - - if ( ! surfaceTextureSpaceFlag) { + else { switch (annotation->getType()) { case AnnotationTypeEnum::BOX: drawnFlag = drawBox(annotationFile, @@ -1343,20 +1305,6 @@ BrainOpenGLAnnotationDrawingFixedPipeline::drawTwoDimAnnotationSurfaceTextureOff glVertex3fv(startXYZ); glVertex3fv(vectorSurfaceAxisZ); glEnd(); - - { - // /* - // * Vector is parallel to surface Z-axis - // */ - // float bigZ[3] = { 0.0f, 0.0f, 100.0f }; - // inverseMatrix.multiplyPoint3(bigZ); - // glBegin(GL_LINES); - // glColor3f(1.0, 1.0, 0.0); - // glVertex3fv(startXYZ); - // glVertex3fv(bigZ); - // glEnd(); - - } } /* @@ -1397,17 +1345,14 @@ BrainOpenGLAnnotationDrawingFixedPipeline::drawTwoDimAnnotationSurfaceTextureOff CaretAssertMessage(0, "Color Bar is NEVER drawn in surface space"); break; case AnnotationTypeEnum::IMAGE: - drawImageSurfaceTangentOffset(annotationFile, - dynamic_cast(annotation), - surfaceDisplayed, - surfaceExtentZ, - vertexXYZ); + drawnFlag = drawImageSurfaceTangentOffset(annotationFile, + dynamic_cast(annotation), + surfaceDisplayed, + surfaceExtentZ, + vertexXYZ); break; case AnnotationTypeEnum::LINE: - CaretAssertToDoFatal(); - // drawnFlag = drawLine(annotationFile, - // dynamic_cast(annotation), - // surfaceDisplayed); + CaretAssert(0); break; case AnnotationTypeEnum::OVAL: drawnFlag = drawOvalSurfaceTangentOffset(annotationFile, @@ -3232,306 +3177,6 @@ BrainOpenGLAnnotationDrawingFixedPipeline::drawTextSurfaceTangentOffset(Annotati return textDrawnFlag; } -///** -// * Draw an annotation text with surface tangent offset -// * -// * @param annotationFile -// * File containing the annotation. -// * @param text -// * Annotation text to draw. -// * @param surfaceDisplayed -// * Surface that is displayed (may be NULL). -// * @return -// * True if the annotation was drawn while NOT selecting annotations. -// */ -//bool -//BrainOpenGLAnnotationDrawingFixedPipeline::drawTextSurfaceTangentOffset(AnnotationFile* annotationFile, -// AnnotationText* text, -// const Surface* surfaceDisplayed) -//{ -// CaretAssert(annotationFile); -// CaretAssert(text); -// CaretAssert(surfaceDisplayed); -// -// /* -// * Annotations with "DISPLAY_GROUP" propery may be turned on/off by user. -// */ -// DisplayPropertiesAnnotation* dpa = m_inputs->m_brain->getDisplayPropertiesAnnotation(); -// if (text->testProperty(Annotation::Property::DISPLAY_GROUP)) { -// if ( ! dpa->isDisplayTextAnnotations()) { -// return false; -// } -// } -// -// const AnnotationCoordinate* coord = text->getCoordinate(); -// CaretAssert(coord->getSurfaceOffsetVectorType() == AnnotationSurfaceOffsetVectorTypeEnum::TANGENT); -// StructureEnum::Enum structure; -// int32_t surfaceNumberOfNodes(0); -// int32_t vertexIndex(0); -// float offsetLength(0.0f); -// AnnotationSurfaceOffsetVectorTypeEnum::Enum offsetVectorType; -// coord->getSurfaceSpace(structure, surfaceNumberOfNodes, vertexIndex, offsetLength, offsetVectorType); -// -// if (structure != surfaceDisplayed->getStructure()) { -// return false; -// } -// if (surfaceDisplayed->getNumberOfNodes() != surfaceNumberOfNodes) { -// return false; -// } -// float vertexXYZ[3]; -// surfaceDisplayed->getCoordinate(vertexIndex, -// vertexXYZ); -// float normalXYZ[3]; -// getSurfaceNormalVector(surfaceDisplayed, vertexIndex, normalXYZ); -// const BoundingBox* boundingBox = surfaceDisplayed->getBoundingBox(); -// const float surfaceExtentZ = boundingBox->getDifferenceZ(); -// -// /* -// * Need to restore model space -// * Recall that all other annotation spaces are drawn in window space -// */ -// glMatrixMode(GL_PROJECTION); -// glPushMatrix(); -// glLoadMatrixd(m_modelSpaceProjectionMatrix); -// -// glMatrixMode(GL_MODELVIEW); -// glPushMatrix(); -// glLoadMatrixd(m_modelSpaceModelMatrix); -// int32_t savedViewport[4]; -// glGetIntegerv(GL_VIEWPORT, -// savedViewport); -// glViewport(m_modelSpaceViewport[0], -// m_modelSpaceViewport[1], -// m_modelSpaceViewport[2], -// m_modelSpaceViewport[3]); -// -// glPushMatrix(); -// -// /* -// * Matrix that rotates text to plane of vertex's normal vector -// */ -// Matrix4x4 rotationMatrix; -// rotationMatrix.setMatrixToOpenGLRotationFromVector(normalXYZ); -// double rotationArray[16]; -// rotationMatrix.getMatrixForOpenGL(rotationArray); -// -// -// /* -// * Translate to the vertex and then translate using offset -// * vector of text. -// */ -// const float offsetVectorXYZ[3] { -// normalXYZ[0] * offsetLength, -// normalXYZ[1] * offsetLength, -// normalXYZ[2] * offsetLength -// }; -// glTranslatef(vertexXYZ[0], vertexXYZ[1], vertexXYZ[2]); -// glTranslatef(offsetVectorXYZ[0], offsetVectorXYZ[1], offsetVectorXYZ[2]); -// -// /* -// * Rotate into plane of surface normal vector -// */ -// glMultMatrixd(rotationArray); -// -// /* -// * Up vector in local coordinates (text drawing plane) -// * Z points out of text -// */ -// const float len(25.0f); -// const float localUpXYZ[3] { -// 0.0, -// len, -// 0.0 -// }; -// -// /* -// * Inverse matrix goes back to surface coordinate system -// */ -// Matrix4x4 inverseMatrix(rotationMatrix); -// inverseMatrix.invert(); -// -// /* -// * Create the plane in which text is drawn -// * (plane is not used for drawing text but is used -// * to orient the text). The plane is constructed -// * from the vertex's normal vector and the the -// * vertex's XYZ-coordinate. -// */ -// Plane textDrawingPlane(normalXYZ, -// vertexXYZ); -// -// /* -// * Project a point with a large Z-coordinate to the plane -// * and use it to creat the 'text up orientation vector'. -// */ -// const float zBig[3] { 0.0, 0.0, 10000.0 }; -// float textUpOrientationVectorXYZ[3]; -// textDrawingPlane.projectPointToPlane(zBig, textUpOrientationVectorXYZ); -// inverseMatrix.multiplyPoint3(textUpOrientationVectorXYZ); -// -// /* -// * Vector is parallel to surface Z-axis -// */ -// float vectorSurfaceAxisZ[3] = { 0.0f, 0.0f, 100.0f }; -// inverseMatrix.multiplyPoint3(vectorSurfaceAxisZ); -// -// if (debugFlag) { -// /* -// * Red line is normal vector -// * Green line is "text up" vector -// * Blue points to surface Z-vector -// * Yellow is parallel to surface Z-axis -// */ -// const float startXYZ[3] { -// 0.0, -// 0.0, -// 0.0 -// }; -// const float endXYZ[3] { -// 0.0, -// 0.0, -// len -// }; -// glBegin(GL_LINES); -// glColor3f(1.0, 0.0, 0.0); -// glVertex3fv(startXYZ); -// glVertex3fv(endXYZ); -// glColor3f(0.0, 1.0, 0.0); -// glVertex3fv(startXYZ); -// glVertex3fv(localUpXYZ); -// glColor3f(0.0, 0.0, 1.0); -// glVertex3fv(startXYZ); -// glVertex3fv(textUpOrientationVectorXYZ); -// glColor3f(1.0, 1.0, 0.0); -// glVertex3fv(startXYZ); -// glVertex3fv(vectorSurfaceAxisZ); -// glEnd(); -// -// { -//// /* -//// * Vector is parallel to surface Z-axis -//// */ -//// float bigZ[3] = { 0.0f, 0.0f, 100.0f }; -//// inverseMatrix.multiplyPoint3(bigZ); -//// glBegin(GL_LINES); -//// glColor3f(1.0, 1.0, 0.0); -//// glVertex3fv(startXYZ); -//// glVertex3fv(bigZ); -//// glEnd(); -// -// } -// } -// -// /* -// * Rotate the text so that the horzontal flow of the text -// * is orthogonal to the 'text up orientation vector'. -// */ -//// const float angle = angleInDegreesBetweenVectors(localUpXYZ, -//// textUpOrientationVectorXYZ); -// const float angle = angleInDegreesBetweenVectors(localUpXYZ, -// vectorSurfaceAxisZ); -// glRotatef(-angle, 0.0, 0.0, 1.0); -// -// if (debugFlag) { -// std::cout << "Annotation: " << text->getText() << std::endl; -// std::cout << " Plane: " << textDrawingPlane.toString() << std::endl; -// std::cout << " Local Up Vector: " << AString::fromNumbers(localUpXYZ, 3, ", ") << std::endl; -// std::cout << " Angle: " << angle << std::endl; -// } -// -// double bottomLeft[3]; -// double bottomRight[3]; -// double topRight[3]; -// double topLeft[3]; -// double underlineStart[3]; -// double underlineEnd[3]; -// m_brainOpenGLFixedPipeline->getTextRenderer()->getBoundsForTextInModelSpace(*text, m_surfaceViewScaling, surfaceExtentZ, m_textDrawingFlags, -// bottomLeft, bottomRight, topRight, topLeft, -// underlineStart, underlineEnd); -// -// bool textDrawnFlag = false; -// if (m_selectionModeFlag) { -// uint8_t selectionColorRGBA[4] = { 0, 0, 0, 0 }; -// getIdentificationColor(selectionColorRGBA); -// GraphicsPrimitiveV3fN3f primitive(GraphicsPrimitive::PrimitiveType::OPENGL_TRIANGLE_STRIP, -// selectionColorRGBA); -// const double doubleNormalXYZ[3] { -// normalXYZ[0], -// normalXYZ[1], -// normalXYZ[2] -// }; -// primitive.addVertex(topLeft, doubleNormalXYZ); -// primitive.addVertex(bottomLeft, doubleNormalXYZ); -// primitive.addVertex(topRight, doubleNormalXYZ); -// primitive.addVertex(bottomRight, doubleNormalXYZ); -// GraphicsEngineDataOpenGL::draw(&primitive); -// m_selectionInfo.push_back(SelectionInfo(annotationFile, -// text, -// AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_NONE, -// vertexXYZ)); -// } -// else { -// glPushMatrix(); -// m_brainOpenGLFixedPipeline->getTextRenderer()->drawTextInModelSpace(*text, -// m_surfaceViewScaling, -// surfaceExtentZ, -// normalXYZ, -// m_textDrawingFlags); -// glPopMatrix(); -// -// textDrawnFlag = true; -// } -// -// if (text->isSelectedForEditing(m_inputs->m_windowIndex)) { -// glPushAttrib(GL_POLYGON_BIT -// | GL_LIGHTING_BIT); -// -// /* -// * So that text and background do not mix together -// * in the Z-buffer -// */ -// glEnable(GL_POLYGON_OFFSET_FILL); -// glEnable(GL_POLYGON_OFFSET_LINE); -// glEnable(GL_POLYGON_OFFSET_POINT); -// glPolygonOffset(-1.0, 1.0); -// glDisable(GL_LIGHTING); -// -// float floatBottomLeft[3]; -// float floatBottomRight[3]; -// float floatTopRight[3]; -// float floatTopLeft[3]; -// for (int32_t i = 0; i < 3; i++) { -// floatBottomLeft[i] = bottomLeft[i]; -// floatBottomRight[i] = bottomRight[i]; -// floatTopLeft[i] = topLeft[i]; -// floatTopRight[i] = topRight[i]; -// } -// drawAnnotationTwoDimSizingHandles(annotationFile, -// text, -// floatBottomLeft, -// floatBottomRight, -// floatTopRight, -// floatTopLeft, -// s_sizingHandleLineWidthInPixels, -// text->getRotationAngle()); -// glPopAttrib(); -// } -// -// glPopMatrix(); /* restore MODELVIEW */ -// -// glViewport(savedViewport[0], -// savedViewport[1], -// savedViewport[2], -// savedViewport[3]); -// glPopMatrix(); -// -// glMatrixMode(GL_PROJECTION); -// glPopMatrix(); -// glMatrixMode(GL_MODELVIEW); -// -// return textDrawnFlag; -//} - /** * Draw an annotation text. * @@ -4013,8 +3658,12 @@ BrainOpenGLAnnotationDrawingFixedPipeline::drawImageSurfaceTangentOffset(Annotat * Add an arrow at the start of the line * @param validEndArrow * Add an arrow at the end of the line - * @param coordinatesOut + * @param lineCoordinatesOut * Output containing coordinates for drawing the line + * @param startArrowCoordinatesOut + * Output containing coordinates for drawing the line's start arrow + * @param endArrowCoordinatesOut + * Output containing coordinates for drawing the line's end arrow */ void BrainOpenGLAnnotationDrawingFixedPipeline::createLineCoordinates(const float lineHeadXYZ[3], @@ -4319,35 +3968,43 @@ BrainOpenGLAnnotationDrawingFixedPipeline::drawLineSurfaceTextureOffset(Annotati lineTailXYZ[i] += (offsetVectorTwo[i] * offsetLengthTwo); } - std::vector lineCoordinates; - lineCoordinates.insert(lineCoordinates.end(), - lineHeadXYZ, lineHeadXYZ + 3); - lineCoordinates.insert(lineCoordinates.end(), - lineTailXYZ, lineTailXYZ + 3); + const float selectionCenterXYZ[3] = { + (lineHeadXYZ[0] + lineTailXYZ[0]) / 2.0f, + (lineHeadXYZ[1] + lineTailXYZ[1]) / 2.0f, + (lineHeadXYZ[2] + lineTailXYZ[2]) / 2.0f + }; if (line->getLineWidthPercentage() <= 0.0) { convertObsoleteLineWidthPixelsToPercentageWidth(line); } float lineThickness = ((line->getLineWidthPercentage() / 100.0) * surfaceExtentZ); + lineThickness *= m_surfaceViewScaling; if (m_selectionModeFlag) { lineThickness = std::max(lineThickness, s_selectionLineMinimumPixelWidth); } + lineThickness = GraphicsUtilitiesOpenGL::convertMillimetersToPixels(lineThickness); + + bool drawnFlag = false; - const float selectionCenterXYZ[3] = { - (lineHeadXYZ[0] + lineTailXYZ[0]) / 2.0f, - (lineHeadXYZ[1] + lineTailXYZ[1]) / 2.0f, - (lineHeadXYZ[2] + lineTailXYZ[2]) / 2.0f - }; + std::vector lineCoordinates; + std::vector startArrowCoordinates; + std::vector endArrowCoordinates; + createLineCoordinates(lineHeadXYZ, + lineTailXYZ, + lineThickness, + line->isDisplayStartArrow(), + line->isDisplayEndArrow(), + lineCoordinates, + startArrowCoordinates, + endArrowCoordinates); uint8_t foregroundRGBA[4]; line->getLineColorRGBA(foregroundRGBA); const bool drawForegroundFlag = (foregroundRGBA[3] > 0.0f); - bool drawnFlag = false; - if (drawForegroundFlag) { if (m_selectionModeFlag) { uint8_t selectionColorRGBA[4]; @@ -4357,18 +4014,18 @@ BrainOpenGLAnnotationDrawingFixedPipeline::drawLineSurfaceTextureOffset(Annotati selectionColorRGBA, GraphicsPrimitive::LineWidthType::PIXELS, lineThickness); -// if ( ! startArrowCoordinates.empty()) { -// GraphicsShape::drawLineStripMiterJoinByteColor(startArrowCoordinates, -// selectionColorRGBA, -// GraphicsPrimitive::LineWidthType::PERCENTAGE_VIEWPORT_HEIGHT, -// lineThickness); -// } -// if ( ! endArrowCoordinates.empty()) { -// GraphicsShape::drawLineStripMiterJoinByteColor(endArrowCoordinates, -// selectionColorRGBA, -// GraphicsPrimitive::LineWidthType::PERCENTAGE_VIEWPORT_HEIGHT, -// lineThickness); -// } + // if ( ! startArrowCoordinates.empty()) { + // GraphicsShape::drawLineStripMiterJoinByteColor(startArrowCoordinates, + // selectionColorRGBA, + // GraphicsPrimitive::LineWidthType::PERCENTAGE_VIEWPORT_HEIGHT, + // lineThickness); + // } + // if ( ! endArrowCoordinates.empty()) { + // GraphicsShape::drawLineStripMiterJoinByteColor(endArrowCoordinates, + // selectionColorRGBA, + // GraphicsPrimitive::LineWidthType::PERCENTAGE_VIEWPORT_HEIGHT, + // lineThickness); + // } m_selectionInfo.push_back(SelectionInfo(annotationFile, line, AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_NONE, @@ -4376,32 +4033,55 @@ BrainOpenGLAnnotationDrawingFixedPipeline::drawLineSurfaceTextureOffset(Annotati } else { if (drawForegroundFlag) { + // float floatRGBA[4]; + // line->getLineColorRGBA(floatRGBA); + // m_brainOpenGLFixedPipeline->drawCylinder(floatRGBA, + // lineHeadXYZ, + // lineTailXYZ, + // lineThickness / 2); GraphicsShape::drawLinesByteColor(lineCoordinates, foregroundRGBA, GraphicsPrimitive::LineWidthType::PIXELS, lineThickness); -// if ( ! startArrowCoordinates.empty()) { -// GraphicsShape::drawLineStripMiterJoinByteColor(startArrowCoordinates, -// foregroundRGBA, -// GraphicsPrimitive::LineWidthType::PERCENTAGE_VIEWPORT_HEIGHT, -// lineThickness); -// } -// if ( ! endArrowCoordinates.empty()) { -// GraphicsShape::drawLineStripMiterJoinByteColor(endArrowCoordinates, -// foregroundRGBA, -// GraphicsPrimitive::LineWidthType::PERCENTAGE_VIEWPORT_HEIGHT, -// lineThickness); -// } + // if ( ! startArrowCoordinates.empty()) { + // if (startArrowCoordinates.size() == 9) { + // m_brainOpenGLFixedPipeline->drawCylinder(floatRGBA, + // &startArrowCoordinates[0], + // &startArrowCoordinates[3], + // lineThickness / 2); + // m_brainOpenGLFixedPipeline->drawCylinder(floatRGBA, + // &startArrowCoordinates[3], + // &startArrowCoordinates[6], + // lineThickness / 2); + // } + // else { + // GraphicsShape::drawLineStripMiterJoinByteColor(startArrowCoordinates, + // foregroundRGBA, + // GraphicsPrimitive::LineWidthType::PIXELS, + // lineThickness); + // } + // } + // if ( ! endArrowCoordinates.empty()) { + // GraphicsShape::drawLineStripMiterJoinByteColor(endArrowCoordinates, + // foregroundRGBA, + // GraphicsPrimitive::LineWidthType::PIXELS, + // lineThickness); + // } drawnFlag = true; } } if (line->isSelectedForEditing(m_inputs->m_windowIndex)) { + const float minPixelSize = 30.0; + const float minSizeMM = (GraphicsUtilitiesOpenGL::convertPixelsToMillimeters(minPixelSize) + * m_surfaceViewScaling); + const float handleThickness = std::max(minSizeMM, + lineThickness * 2.0f); drawAnnotationOneDimSizingHandles(annotationFile, line, lineHeadXYZ, lineTailXYZ, - s_sizingHandleLineWidthInPixels); + handleThickness); //lineThickness * 2.0); //s_sizingHandleLineWidthInPixels); } } @@ -4472,6 +4152,7 @@ BrainOpenGLAnnotationDrawingFixedPipeline::drawSizingHandle(const AnnotationSizi bool drawFilledCircleFlag = false; bool drawOutlineCircleFlag = false; bool drawSquareFlag = false; + bool drawSphereFlag = false; switch (handleType) { case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_BOX_BOTTOM: @@ -4499,10 +4180,20 @@ BrainOpenGLAnnotationDrawingFixedPipeline::drawSizingHandle(const AnnotationSizi drawFilledCircleFlag = true; break; case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_LINE_END: - drawFilledCircleFlag = true; + if (annotation->isInSurfaceSpaceWithTangentOffset()) { + drawSphereFlag = true; + } + else { + drawFilledCircleFlag = true; + } break; case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_LINE_START: - drawFilledCircleFlag = true; + if (annotation->isInSurfaceSpaceWithTangentOffset()) { + drawSphereFlag = true; + } + else { + drawFilledCircleFlag = true; + } break; case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_NONE: break; @@ -4547,6 +4238,10 @@ BrainOpenGLAnnotationDrawingFixedPipeline::drawSizingHandle(const AnnotationSizi 1.0f); glPopMatrix(); } + else if (drawSphereFlag) { + float zeros[3] { 0.0f, 0.0f, 0.0f }; + GraphicsShape::drawSphereByteColor(zeros, m_selectionBoxRGBA, halfWidthHeight); + } } glPopMatrix(); @@ -4573,6 +4268,7 @@ BrainOpenGLAnnotationDrawingFixedPipeline::drawAnnotationOneDimSizingHandles(Ann const float secondPoint[3], const float lineThickness) { + CaretAssert(annotation); float lengthVector[3]; MathFunctions::subtractVectors(secondPoint, firstPoint, lengthVector); MathFunctions::normalizeVector(lengthVector); @@ -4580,12 +4276,20 @@ BrainOpenGLAnnotationDrawingFixedPipeline::drawAnnotationOneDimSizingHandles(Ann const float dx = secondPoint[0] - firstPoint[0]; const float dy = secondPoint[1] - firstPoint[1]; - const float cornerSquareSize = 3.0 + lineThickness; - const float directionVector[3] = { - lengthVector[0] * cornerSquareSize, - lengthVector[1] * cornerSquareSize, - 0.0 - }; + const bool tangentSurfaceOffsetFlag = annotation->isInSurfaceSpaceWithTangentOffset(); + + float cornerSquareSize = 3.0 + lineThickness; + if (tangentSurfaceOffsetFlag) { + cornerSquareSize = lineThickness;// * 4.0; + cornerSquareSize = GraphicsUtilitiesOpenGL::convertPixelsToMillimeters(cornerSquareSize); + } + + float directionVector[3] { 0.0f, 0.0f, 0.0f }; + if ( ! tangentSurfaceOffsetFlag) { + directionVector[0] = lengthVector[0] * cornerSquareSize; + directionVector[1] = lengthVector[1] * cornerSquareSize; + directionVector[2] = 0.0; + } const float firstPointSymbolXYZ[3] = { firstPoint[0] - directionVector[0], @@ -4605,15 +4309,19 @@ BrainOpenGLAnnotationDrawingFixedPipeline::drawAnnotationOneDimSizingHandles(Ann rotationAngle = MathFunctions::toDegrees(angleRadians); } - /* - * Symbol for first coordinate is a little bigger - */ if (annotation->isSizeHandleValid(AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_LINE_START)) { + /* + * Symbol for first coordinate is a little bigger + */ + float startSquareSize = cornerSquareSize + 2.0; + if (tangentSurfaceOffsetFlag) { + startSquareSize = cornerSquareSize;// * 1.5; + } drawSizingHandle(AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_LINE_START, annotationFile, annotation, firstPointSymbolXYZ, - cornerSquareSize + 2.0, + startSquareSize, rotationAngle); } @@ -4673,22 +4381,7 @@ BrainOpenGLAnnotationDrawingFixedPipeline::drawAnnotationTwoDimSizingHandles(Ann { CaretAssert(annotation); AnnotationText* textAnn = dynamic_cast(annotation); - bool modelSpaceTangentTextFlag = false; - { - AnnotationTwoDimensionalShape* twoDimShape = dynamic_cast(annotation); - if (twoDimShape != NULL) { - const AnnotationCoordinate* coord = twoDimShape->getCoordinate(); - switch (coord->getSurfaceOffsetVectorType()) { - case AnnotationSurfaceOffsetVectorTypeEnum::CENTROID_THRU_VERTEX: - break; - case AnnotationSurfaceOffsetVectorTypeEnum::SURFACE_NORMAL: - break; - case AnnotationSurfaceOffsetVectorTypeEnum::TANGENT: - modelSpaceTangentTextFlag = true; - break; - } - } - } + const bool modelSpaceTangentTextFlag = annotation->isInSurfaceSpaceWithTangentOffset(); float heightVector[3]; MathFunctions::subtractVectors(topLeft, bottomLeft, heightVector); diff --git a/src/Brain/FtglFontTextRenderer.cxx b/src/Brain/FtglFontTextRenderer.cxx index 3c58d9c29..f87570560 100644 --- a/src/Brain/FtglFontTextRenderer.cxx +++ b/src/Brain/FtglFontTextRenderer.cxx @@ -2220,10 +2220,8 @@ m_textDrawingSpace(TextDrawingSpace::VIEWPORT) CaretAssert(font); m_textDrawingSpace = TextDrawingSpace::VIEWPORT; - if (m_annotationText.getCoordinateSpace() == AnnotationCoordinateSpaceEnum::SURFACE) { - if (m_annotationText.getCoordinate()->getSurfaceOffsetVectorType() == AnnotationSurfaceOffsetVectorTypeEnum::TANGENT) { - m_textDrawingSpace = TextDrawingSpace::MODEL; - } + if (m_annotationText.isInSurfaceSpaceWithTangentOffset()) { + m_textDrawingSpace = TextDrawingSpace::MODEL; } if (annotationText.getText().isEmpty()) { diff --git a/src/Graphics/GraphicsUtilitiesOpenGL.cxx b/src/Graphics/GraphicsUtilitiesOpenGL.cxx index 8f1b4936f..33916758b 100644 --- a/src/Graphics/GraphicsUtilitiesOpenGL.cxx +++ b/src/Graphics/GraphicsUtilitiesOpenGL.cxx @@ -171,6 +171,48 @@ GraphicsUtilitiesOpenGL::convertPixelsToMillimeters(const float pixels) return mm; } +/** + * Convert millimeters to pixels. + * The current transformations must be for drawing in millimeters. + * + * @param millimeters + * The millimeters size + * @return + * Pixels value. + */ +float +GraphicsUtilitiesOpenGL::convertMillimetersToPixels(const float millimeters) +{ + float pixels = 1.0f; + + EventOpenGLObjectToWindowTransform xform(EventOpenGLObjectToWindowTransform::SpaceType::VOLUME_SLICE_MODEL); + EventManager::get()->sendEvent(xform.getPointer()); + if (xform.isValid()) { + const std::array viewport = xform.getViewport(); + + const float windowZ = 0.0f; + float bottomWindowXYZ[3] = { (float)viewport[0], (float)viewport[1], (float)windowZ }; + float topWindowXYZ[3] = { (float)viewport[0], (float)(viewport[1] + viewport[3]), (float)windowZ }; + + float bottomModelXYZ[3]; + float topModelXYZ[3]; + + xform.inverseTransformPoint(bottomWindowXYZ, bottomModelXYZ); + xform.inverseTransformPoint(topWindowXYZ, topModelXYZ); + + const float rangeMillimeters = MathFunctions::distance3D(bottomModelXYZ, + topModelXYZ); + const float rangePixels = viewport[3]; + if ((rangePixels > 0) + && (rangeMillimeters > 0)) { + const float ratio = rangePixels / rangeMillimeters; + pixels = millimeters * ratio; + } + } + + return pixels; +} + /** * Reset and ignore any OpenGL errors. */ diff --git a/src/Graphics/GraphicsUtilitiesOpenGL.h b/src/Graphics/GraphicsUtilitiesOpenGL.h index fda72fb2e..7a1adc2b9 100644 --- a/src/Graphics/GraphicsUtilitiesOpenGL.h +++ b/src/Graphics/GraphicsUtilitiesOpenGL.h @@ -37,6 +37,8 @@ namespace caret { static float convertMillimetersToPercentageOfViewportHeight(const float millimeters); + static float convertMillimetersToPixels(const float millimeters); + static float convertPixelsToPercentageOfViewportHeight(const float pixels); static float convertPixelsToMillimeters(const float pixels); -- GitLab From d14c0be457fe45d8ed4bb416d0f3d85dc477c95b Mon Sep 17 00:00:00 2001 From: John Harwell Date: Wed, 5 Sep 2018 16:05:28 -0500 Subject: [PATCH 035/427] WB-636 Texture Based Annotations: Fixed z-fighting between background and outline for both boxes and images in surface tangent offset space. --- ...inOpenGLAnnotationDrawingFixedPipeline.cxx | 32 +++--- src/Graphics/GraphicsShape.cxx | 108 ++++++++++++++++++ src/Graphics/GraphicsShape.h | 22 ++++ 3 files changed, 148 insertions(+), 14 deletions(-) diff --git a/src/Brain/BrainOpenGLAnnotationDrawingFixedPipeline.cxx b/src/Brain/BrainOpenGLAnnotationDrawingFixedPipeline.cxx index 7242b9056..077b722c2 100644 --- a/src/Brain/BrainOpenGLAnnotationDrawingFixedPipeline.cxx +++ b/src/Brain/BrainOpenGLAnnotationDrawingFixedPipeline.cxx @@ -1707,14 +1707,16 @@ BrainOpenGLAnnotationDrawingFixedPipeline::drawBoxSurfaceTangentOffset(Annotatio } if (drawForegroundFlag) { - GraphicsShape::drawBoxOutlineByteColor(bottomLeft, - bottomRight, - topRight, - topLeft, - foregroundRGBA, - GraphicsPrimitive::LineWidthType::PIXELS, - lineThickness); + glPolygonOffset(-1.0, -1.0); + glEnable(GL_POLYGON_OFFSET_FILL); + GraphicsShape::drawOutlineRectangleVerticesInMiddle(bottomLeft, + bottomRight, + topRight, + topLeft, + lineThickness, + foregroundRGBA); drawnFlag = true; + glDisable(GL_POLYGON_OFFSET_FILL); } } if (box->isSelectedForEditing(m_inputs->m_windowIndex)) { @@ -3620,14 +3622,16 @@ BrainOpenGLAnnotationDrawingFixedPipeline::drawImageSurfaceTangentOffset(Annotat } if (drawForegroundFlag) { - GraphicsShape::drawBoxOutlineByteColor(bottomLeft, - bottomRight, - topRight, - topLeft, - foregroundRGBA, - GraphicsPrimitive::LineWidthType::PIXELS, - lineThickness); + glPolygonOffset(-1.0, -1.0); + glEnable(GL_POLYGON_OFFSET_FILL); + GraphicsShape::drawOutlineRectangleVerticesInMiddle(bottomLeft, + bottomRight, + topRight, + topLeft, + lineThickness, + foregroundRGBA); drawnFlag = true; + glDisable(GL_POLYGON_OFFSET_FILL); } } if (image->isSelectedForEditing(m_inputs->m_windowIndex)) { diff --git a/src/Graphics/GraphicsShape.cxx b/src/Graphics/GraphicsShape.cxx index 845639ef2..b8c1f9c63 100644 --- a/src/Graphics/GraphicsShape.cxx +++ b/src/Graphics/GraphicsShape.cxx @@ -1178,6 +1178,44 @@ GraphicsShape::updateModelMatrixToFaceViewer() glScalef(sx, sy, sz); } + +/** + * Draw an rectangle outline. + * The normal vector is computed from three consecutive vertices in the rectangle. + * + * @param bottomLeft + * Bottom left vertex of the rectangle. + * @param bottomRight + * Bottom right vertex of the rectangle. + * @param topRight + * Top right vertex of the rectangle. + * @param topLeft + * Top left vertex of the rectangle. + * @param thickness + * Thickness of the outline + * @param rgba + * RGBA color for the outline. + * @param verticesInMiddleFlag + * If true, the lines are centered around the vertices. Otherwise, + * the inner sides of the lines are tangent to the vertices. + */ +void +GraphicsShape::drawOutlineRectanglePrivate(const float bottomLeft[3], + const float bottomRight[3], + const float topRight[3], + const float topLeft[3], + const float thicknessIn, + const uint8_t rgba[4], + bool verticesInMiddleFlag) +{ + const double bl[3] { bottomLeft[0], bottomLeft[1], bottomLeft[2] }; + const double br[3] { bottomRight[0], bottomRight[1], bottomRight[2] }; + const double tr[3] { topRight[0], topRight[1], topRight[2] }; + const double tl[3] { topLeft[0], topLeft[1], topLeft[2] }; + + drawOutlineRectanglePrivate(bl, br, tr, tl, thicknessIn, rgba, verticesInMiddleFlag); +} + /** * Draw an rectangle outline. * The normal vector is computed from three consecutive vertices in the rectangle. @@ -1334,6 +1372,76 @@ GraphicsShape::drawOutlineRectangleVerticesInMiddle(const double bottomLeft[3], true); } +/** + * Draw an rectangle outline. + * The given points are in the middle of outline. + * The normal vector is computed from three consecutive vertices in the rectangle. + * + * @param bottomLeft + * Bottom left vertex of the rectangle. + * @param bottomRight + * Bottom right vertex of the rectangle. + * @param topRight + * Top right vertex of the rectangle. + * @param topLeft + * Top left vertex of the rectangle. + * @param thickness + * Thickness of the outline + * @param rgba + * RGBA color for the outline. + */ +void +GraphicsShape::drawOutlineRectangleVerticesInMiddle(const float bottomLeft[3], + const float bottomRight[3], + const float topRight[3], + const float topLeft[3], + const float thickness, + const uint8_t rgba[4]) +{ + drawOutlineRectanglePrivate(bottomLeft, + bottomRight, + topRight, + topLeft, + thickness, + rgba, + true); +} + +/** + * Draw an rectangle outline. + * The given points are at the inside of outline. + * The normal vector is computed from three consecutive vertices in the rectangle. + * + * @param bottomLeft + * Bottom left vertex of the rectangle. + * @param bottomRight + * Bottom right vertex of the rectangle. + * @param topRight + * Top right vertex of the rectangle. + * @param topLeft + * Top left vertex of the rectangle. + * @param thickness + * Thickness of the outline + * @param rgba + * RGBA color for the outline. + */ +void +GraphicsShape::drawOutlineRectangleVerticesAtInside(const float bottomLeft[3], + const float bottomRight[3], + const float topRight[3], + const float topLeft[3], + const float thickness, + const uint8_t rgba[4]) +{ + drawOutlineRectanglePrivate(bottomLeft, + bottomRight, + topRight, + topLeft, + thickness, + rgba, + false); +} + /** * Draw an rectangle outline. * The given points are at the inside of outline. diff --git a/src/Graphics/GraphicsShape.h b/src/Graphics/GraphicsShape.h index 3634412f6..8cec3d418 100644 --- a/src/Graphics/GraphicsShape.h +++ b/src/Graphics/GraphicsShape.h @@ -129,6 +129,13 @@ namespace caret { const double thickness, const uint8_t rgba[4]); + static void drawOutlineRectangleVerticesInMiddle(const float bottomLeft[3], + const float bottomRight[3], + const float topRight[3], + const float topLeft[3], + const float thickness, + const uint8_t rgba[4]); + static void drawOutlineRectangleVerticesAtInside(const double bottomLeft[3], const double bottomRight[3], const double topRight[3], @@ -136,6 +143,13 @@ namespace caret { const double thickness, const uint8_t rgba[4]); + static void drawOutlineRectangleVerticesAtInside(const float bottomLeft[3], + const float bottomRight[3], + const float topRight[3], + const float topLeft[3], + const float thickness, + const uint8_t rgba[4]); + // ADD_NEW_METHODS_HERE virtual AString toString() const; @@ -217,6 +231,14 @@ namespace caret { const uint8_t rgba[4], bool verticesInMiddleFlag); + static void drawOutlineRectanglePrivate(const float bottomLeft[3], + const float bottomRight[3], + const float topRight[3], + const float topLeft[3], + const float thicknessIn, + const uint8_t rgba[4], + bool verticesInMiddleFlag); + static std::unique_ptr s_byteSquarePrimitive; static std::map s_byteSpherePrimitives; -- GitLab From 679747e95bdfda84cd40c80192ff7de4b494f990 Mon Sep 17 00:00:00 2001 From: John Harwell Date: Thu, 6 Sep 2018 09:10:49 -0500 Subject: [PATCH 036/427] Fixed outline thickness for text in tangent surface offset. Corrections to tooltip for annotation surface offset. --- src/Graphics/GraphicsShape.cxx | 2 +- src/GuiQt/AnnotationCoordinateWidget.cxx | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Graphics/GraphicsShape.cxx b/src/Graphics/GraphicsShape.cxx index b8c1f9c63..c055f5203 100644 --- a/src/Graphics/GraphicsShape.cxx +++ b/src/Graphics/GraphicsShape.cxx @@ -1262,8 +1262,8 @@ GraphicsShape::drawOutlineRectanglePrivate(const double bottomLeft[3], const double boxHeight = MathFunctions::distance3D(bottomLeft, topLeft); const double boxWidth = MathFunctions::distance3D(bottomLeft, bottomRight); double thickness = thicknessIn; - thickness /= 2.0; if (verticesInMiddleFlag) { + thickness /= 2.0; thickness = std::min(thickness, boxWidth / 2.0); thickness = std::min(thickness, diff --git a/src/GuiQt/AnnotationCoordinateWidget.cxx b/src/GuiQt/AnnotationCoordinateWidget.cxx index ba448a943..78bc3c8be 100644 --- a/src/GuiQt/AnnotationCoordinateWidget.cxx +++ b/src/GuiQt/AnnotationCoordinateWidget.cxx @@ -168,9 +168,9 @@ m_browserWindowIndex(browserWindowIndex) this, SLOT(surfaceOffsetVectorTypeChanged())); m_surfaceOffsetVectorTypeComboBox->getWidget()->setFixedWidth(45); m_surfaceOffsetVectorTypeComboBox->getWidget()->setToolTip("Vector for surface offset:\n" - " C - Centroid thru Vertex\n" - " N - Vertex Normal" - " T - Tangent"); + " C - Centroid thru Vertex, Faces Viewer\n" + " N - Vertex Normal, Faces Viewer\n" + " T - Tangent, Rotates with Surface"); break; case COORDINATE_TWO: break; -- GitLab From 2d18a2d50175a6f2314fa90a87d6bd81a0c2a8c7 Mon Sep 17 00:00:00 2001 From: John Harwell Date: Thu, 6 Sep 2018 14:43:26 -0500 Subject: [PATCH 037/427] WB-636 Texture Based Annotations: Add support for drawing 'polygonal lines' in model space and use for drawing ovals in surface tangent space. --- ...inOpenGLAnnotationDrawingFixedPipeline.cxx | 14 +- src/Graphics/GraphicsEngineDataOpenGL.cxx | 124 +++++++++++++++--- src/Graphics/GraphicsEngineDataOpenGL.h | 16 ++- .../GraphicsOpenGLPolylineTriangles.cxx | 57 ++++++-- .../GraphicsOpenGLPolylineTriangles.h | 26 +++- src/Graphics/GraphicsPrimitive.cxx | 41 ++++++ src/Graphics/GraphicsPrimitive.h | 34 +++++ .../GraphicsPrimitiveSelectionHelper.cxx | 5 + src/Graphics/GraphicsShape.cxx | 36 +++++ src/Graphics/GraphicsShape.h | 5 + 10 files changed, 318 insertions(+), 40 deletions(-) diff --git a/src/Brain/BrainOpenGLAnnotationDrawingFixedPipeline.cxx b/src/Brain/BrainOpenGLAnnotationDrawingFixedPipeline.cxx index 077b722c2..de5905ffe 100644 --- a/src/Brain/BrainOpenGLAnnotationDrawingFixedPipeline.cxx +++ b/src/Brain/BrainOpenGLAnnotationDrawingFixedPipeline.cxx @@ -2761,11 +2761,15 @@ BrainOpenGLAnnotationDrawingFixedPipeline::drawOvalSurfaceTangentOffset(Annotati if (drawForegroundFlag) { glPolygonOffset(-1.0, 1.0); glEnable(GL_POLYGON_OFFSET_FILL); - GraphicsShape::drawEllipseOutlineByteColor(majorAxis, - minorAxis, - foregroundRGBA, - GraphicsPrimitive::LineWidthType::PIXELS, - lineThickness); + GraphicsShape::drawEllipseOutlineModelSpaceByteColor(majorAxis, + minorAxis, + foregroundRGBA, + lineThickness); +// GraphicsShape::drawEllipseOutlineByteColor(majorAxis, +// minorAxis, +// foregroundRGBA, +// GraphicsPrimitive::LineWidthType::PIXELS, +// lineThickness); glDisable(GL_POLYGON_OFFSET_FILL); drawnFlag = true; } diff --git a/src/Graphics/GraphicsEngineDataOpenGL.cxx b/src/Graphics/GraphicsEngineDataOpenGL.cxx index ab7ebacd6..0ee43c2b9 100644 --- a/src/Graphics/GraphicsEngineDataOpenGL.cxx +++ b/src/Graphics/GraphicsEngineDataOpenGL.cxx @@ -474,9 +474,10 @@ GraphicsEngineDataOpenGL::draw(GraphicsPrimitive* primitive) GraphicsPrimitive* primitiveToDraw = primitive; /* Conversion to window space creates a new primitive that must be deleted */ - std::unique_ptr windowSpacePrimitive; + std::unique_ptr lineConversionPrimitive; - bool workbenchLineFlag = false; + bool modelSpaceLineFlag = false; + bool windowSpaceLineFlag = false; bool millimeterPointsFlag = false; bool spheresFlag = false; switch (primitive->m_primitiveType) { @@ -503,12 +504,19 @@ GraphicsEngineDataOpenGL::draw(GraphicsPrimitive* primitive) break; case GraphicsPrimitive::PrimitiveType::OPENGL_TRIANGLES: break; + case GraphicsPrimitive::PrimitiveType::MODEL_SPACE_POLYGONAL_LINE_LOOP_BEVEL_JOIN: + case GraphicsPrimitive::PrimitiveType::MODEL_SPACE_POLYGONAL_LINE_LOOP_MITER_JOIN: + case GraphicsPrimitive::PrimitiveType::MODEL_SPACE_POLYGONAL_LINE_STRIP_BEVEL_JOIN: + case GraphicsPrimitive::PrimitiveType::MODEL_SPACE_POLYGONAL_LINE_STRIP_MITER_JOIN: + case GraphicsPrimitive::PrimitiveType::MODEL_SPACE_POLYGONAL_LINES: + modelSpaceLineFlag = true; + break; case GraphicsPrimitive::PrimitiveType::POLYGONAL_LINE_LOOP_BEVEL_JOIN: case GraphicsPrimitive::PrimitiveType::POLYGONAL_LINE_LOOP_MITER_JOIN: case GraphicsPrimitive::PrimitiveType::POLYGONAL_LINE_STRIP_BEVEL_JOIN: case GraphicsPrimitive::PrimitiveType::POLYGONAL_LINE_STRIP_MITER_JOIN: case GraphicsPrimitive::PrimitiveType::POLYGONAL_LINES: - workbenchLineFlag = true; + windowSpaceLineFlag = true; break; case GraphicsPrimitive::PrimitiveType::SPHERES: spheresFlag = true; @@ -524,7 +532,8 @@ GraphicsEngineDataOpenGL::draw(GraphicsPrimitive* primitive) else if (spheresFlag) { drawSpheresPrimitive(primitive); } - else if (workbenchLineFlag) { + else if (modelSpaceLineFlag + || windowSpaceLineFlag) { AString errorMessage; primitiveToDraw = GraphicsOpenGLPolylineTriangles::convertWorkbenchLinePrimitiveTypeToOpenGL(primitive, errorMessage); @@ -538,10 +547,22 @@ GraphicsEngineDataOpenGL::draw(GraphicsPrimitive* primitive) #endif return; } - windowSpacePrimitive.reset(primitiveToDraw); - drawWindowSpace(PrivateDrawMode::DRAW_NORMAL, - primitiveToDraw, - NULL); + SpaceMode spaceMode = SpaceMode::WINDOW; + if (modelSpaceLineFlag) { + spaceMode = SpaceMode::MODEL; + } + else if (windowSpaceLineFlag) { + spaceMode = SpaceMode::WINDOW; + } + else { + CaretAssert(0); + } + + lineConversionPrimitive.reset(primitiveToDraw); + drawModelOrWindowSpace(spaceMode, + PrivateDrawMode::DRAW_NORMAL, + primitiveToDraw, + NULL); } else { drawPrivate(PrivateDrawMode::DRAW_NORMAL, @@ -550,9 +571,49 @@ GraphicsEngineDataOpenGL::draw(GraphicsPrimitive* primitive) } } +///** +// * Draw the graphics primitive in window space. +// * +// * @param drawMode +// * Mode for drawing. +// * @param primitive +// * Primitive that is drawn. +// * @param primitiveSelectionHelper +// * Selection helper when draw mode is selection. +// */ +//void +//GraphicsEngineDataOpenGL::drawWindowSpace(const PrivateDrawMode drawMode, +// GraphicsPrimitive* primitive, +// GraphicsPrimitiveSelectionHelper* primitiveSelectionHelper) +//{ +// int32_t polygonMode[2]; +// int32_t viewport[4]; +// saveOpenGLStateForWindowSpaceDrawing(polygonMode, +// viewport); +// +// glMatrixMode(GL_PROJECTION); +// glLoadIdentity(); +// glOrtho(viewport[0], viewport[0] + viewport[2], +// viewport[1], viewport[1] + viewport[3], +// 0, 1); +// glMatrixMode(GL_MODELVIEW); +// glLoadIdentity(); +// +// glPolygonMode(GL_FRONT, GL_FILL); +// +// drawPrivate(drawMode, +// primitive, +// primitiveSelectionHelper); +// +// restoreOpenGLStateForWindowSpaceDrawing(polygonMode, +// viewport); +//} + /** - * Draw the graphics primitive in window space. + * Draw the graphics primitive in model space. * + * @param spaceMode + * Space mode: model or window * @param drawMode * Mode for drawing. * @param primitive @@ -561,25 +622,37 @@ GraphicsEngineDataOpenGL::draw(GraphicsPrimitive* primitive) * Selection helper when draw mode is selection. */ void -GraphicsEngineDataOpenGL::drawWindowSpace(const PrivateDrawMode drawMode, - GraphicsPrimitive* primitive, - GraphicsPrimitiveSelectionHelper* primitiveSelectionHelper) +GraphicsEngineDataOpenGL::drawModelOrWindowSpace(const SpaceMode spaceMode, + const PrivateDrawMode drawMode, + GraphicsPrimitive* primitive, + GraphicsPrimitiveSelectionHelper* primitiveSelectionHelper) { + bool windowSpaceFlag = false; + switch (spaceMode) { + case MODEL: + break; + case WINDOW: + windowSpaceFlag = true; + break; + } + int32_t polygonMode[2]; int32_t viewport[4]; saveOpenGLStateForWindowSpaceDrawing(polygonMode, viewport); - glMatrixMode(GL_PROJECTION); - glLoadIdentity(); - glOrtho(viewport[0], viewport[0] + viewport[2], - viewport[1], viewport[1] + viewport[3], - 0, 1); - glMatrixMode(GL_MODELVIEW); - glLoadIdentity(); + if (windowSpaceFlag) { + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); + glOrtho(viewport[0], viewport[0] + viewport[2], + viewport[1], viewport[1] + viewport[3], + 0, 1); + glMatrixMode(GL_MODELVIEW); + glLoadIdentity(); + } glPolygonMode(GL_FRONT, GL_FILL); - + drawPrivate(drawMode, primitive, primitiveSelectionHelper); @@ -828,6 +901,12 @@ GraphicsEngineDataOpenGL::drawWithSelection(GraphicsPrimitive* primitive, break; case GraphicsPrimitive::PrimitiveType::OPENGL_TRIANGLES: break; + case GraphicsPrimitive::PrimitiveType::MODEL_SPACE_POLYGONAL_LINE_LOOP_BEVEL_JOIN: + case GraphicsPrimitive::PrimitiveType::MODEL_SPACE_POLYGONAL_LINE_LOOP_MITER_JOIN: + case GraphicsPrimitive::PrimitiveType::MODEL_SPACE_POLYGONAL_LINE_STRIP_BEVEL_JOIN: + case GraphicsPrimitive::PrimitiveType::MODEL_SPACE_POLYGONAL_LINE_STRIP_MITER_JOIN: + case GraphicsPrimitive::PrimitiveType::MODEL_SPACE_POLYGONAL_LINES: + break; case GraphicsPrimitive::PrimitiveType::POLYGONAL_LINE_LOOP_BEVEL_JOIN: case GraphicsPrimitive::PrimitiveType::POLYGONAL_LINE_LOOP_MITER_JOIN: break; @@ -1094,17 +1173,22 @@ GraphicsEngineDataOpenGL::drawPrivate(const PrivateDrawMode drawMode, GLenum openGLPrimitiveType = GL_INVALID_ENUM; switch (primitive->m_primitiveType) { case GraphicsPrimitive::PrimitiveType::OPENGL_LINE_LOOP: + case GraphicsPrimitive::PrimitiveType::MODEL_SPACE_POLYGONAL_LINE_LOOP_BEVEL_JOIN: + case GraphicsPrimitive::PrimitiveType::MODEL_SPACE_POLYGONAL_LINE_LOOP_MITER_JOIN: case GraphicsPrimitive::PrimitiveType::POLYGONAL_LINE_LOOP_BEVEL_JOIN: case GraphicsPrimitive::PrimitiveType::POLYGONAL_LINE_LOOP_MITER_JOIN: openGLPrimitiveType = GL_LINE_LOOP; glLineWidth(getLineWidthForDrawingInPixels(primitive)); break; case GraphicsPrimitive::PrimitiveType::OPENGL_LINE_STRIP: + case GraphicsPrimitive::PrimitiveType::MODEL_SPACE_POLYGONAL_LINE_STRIP_BEVEL_JOIN: + case GraphicsPrimitive::PrimitiveType::MODEL_SPACE_POLYGONAL_LINE_STRIP_MITER_JOIN: case GraphicsPrimitive::PrimitiveType::POLYGONAL_LINE_STRIP_BEVEL_JOIN: case GraphicsPrimitive::PrimitiveType::POLYGONAL_LINE_STRIP_MITER_JOIN: openGLPrimitiveType = GL_LINE_STRIP; glLineWidth(getLineWidthForDrawingInPixels(primitive)); break; + case GraphicsPrimitive::PrimitiveType::MODEL_SPACE_POLYGONAL_LINES: case GraphicsPrimitive::PrimitiveType::OPENGL_LINES: case GraphicsPrimitive::PrimitiveType::POLYGONAL_LINES: openGLPrimitiveType = GL_LINES; diff --git a/src/Graphics/GraphicsEngineDataOpenGL.h b/src/Graphics/GraphicsEngineDataOpenGL.h index 6289ae561..ac23f5130 100644 --- a/src/Graphics/GraphicsEngineDataOpenGL.h +++ b/src/Graphics/GraphicsEngineDataOpenGL.h @@ -64,6 +64,11 @@ namespace caret { DRAW_SELECTION, }; + enum SpaceMode { + MODEL, + WINDOW, + }; + GraphicsEngineDataOpenGL(const GraphicsEngineDataOpenGL&); GraphicsEngineDataOpenGL& operator=(const GraphicsEngineDataOpenGL&); @@ -84,9 +89,14 @@ namespace caret { GraphicsPrimitive* primitive, GraphicsPrimitiveSelectionHelper* primitiveSelectionHelper); - static void drawWindowSpace(const PrivateDrawMode drawMode, - GraphicsPrimitive* primitive, - GraphicsPrimitiveSelectionHelper* primitiveSelectionHelper); + static void drawModelOrWindowSpace(const SpaceMode spaceMode, + const PrivateDrawMode drawMode, + GraphicsPrimitive* primitive, + GraphicsPrimitiveSelectionHelper* primitiveSelectionHelper); + +// static void drawWindowSpace(const PrivateDrawMode drawMode, +// GraphicsPrimitive* primitive, +// GraphicsPrimitiveSelectionHelper* primitiveSelectionHelper); static void drawPointsPrimitiveMillimeters(const GraphicsPrimitive* primitive); diff --git a/src/Graphics/GraphicsOpenGLPolylineTriangles.cxx b/src/Graphics/GraphicsOpenGLPolylineTriangles.cxx index fb2426052..50ce01c65 100644 --- a/src/Graphics/GraphicsOpenGLPolylineTriangles.cxx +++ b/src/Graphics/GraphicsOpenGLPolylineTriangles.cxx @@ -63,10 +63,12 @@ using namespace caret; * @param vertexPrimitiveRestartIndices * Contains indices at which the primitive should restart. * The primitive will stop at the index and not connect to the next index. - * @param lineThicknessPixels - * Thickness of lines in pixels. + * @param lineThickness + * Thickness of lines in (pixels when drawingSpace is WINDOW) * @param colorType * Type of color (solid or per-vertex) + * @param drawingSpace + * Drawing space * @param lineType * Type of lines drawn. * @param joinType @@ -76,16 +78,18 @@ GraphicsOpenGLPolylineTriangles::GraphicsOpenGLPolylineTriangles(const std::vect const std::vector& floatRGBA, const std::vector& byteRGBA, const std::set& vertexPrimitiveRestartIndices, - const float lineThicknessPixels, + const float lineThickness, const ColorType colorType, + const DrawingSpace drawingSpace, const LineType lineType, const JoinType joinType) : m_inputXYZ(xyz), m_inputFloatRGBA(floatRGBA), m_inputByteRGBA(byteRGBA), m_vertexPrimitiveRestartIndices(vertexPrimitiveRestartIndices), -m_lineThicknessPixels(lineThicknessPixels), +m_lineThickness(lineThickness), m_colorType(colorType), +m_drawingSpace(drawingSpace), m_lineType(lineType), m_joinType(joinType) { @@ -158,6 +162,7 @@ GraphicsOpenGLPolylineTriangles::convertWorkbenchLinePrimitiveTypeToOpenGL(const break; } + DrawingSpace drawingSpace = DrawingSpace::WINDOW; LineType lineType = LineType::LINES; JoinType joinType = JoinType::NONE; switch (primitive->m_primitiveType) { @@ -182,6 +187,30 @@ GraphicsOpenGLPolylineTriangles::convertWorkbenchLinePrimitiveTypeToOpenGL(const case GraphicsPrimitive::PrimitiveType::OPENGL_TRIANGLES: errorMessageOut = "Input type is OPENGL_TRIANGLES but must be one of the POLYGONAL_LINE* types"; break; + case GraphicsPrimitive::PrimitiveType::MODEL_SPACE_POLYGONAL_LINE_LOOP_BEVEL_JOIN: + drawingSpace = DrawingSpace::MODEL_XY; + lineType = LineType::LINE_LOOP; + joinType = JoinType::BEVEL; + break; + case GraphicsPrimitive::PrimitiveType::MODEL_SPACE_POLYGONAL_LINE_LOOP_MITER_JOIN: + drawingSpace = DrawingSpace::MODEL_XY; + lineType = LineType::LINE_LOOP; + joinType = JoinType::MITER; + break; + case GraphicsPrimitive::PrimitiveType::MODEL_SPACE_POLYGONAL_LINE_STRIP_BEVEL_JOIN: + drawingSpace = DrawingSpace::MODEL_XY; + lineType = LineType::LINE_STRIP; + joinType = JoinType::BEVEL; + break; + case GraphicsPrimitive::PrimitiveType::MODEL_SPACE_POLYGONAL_LINE_STRIP_MITER_JOIN: + drawingSpace = DrawingSpace::MODEL_XY; + lineType = LineType::LINE_STRIP; + joinType = JoinType::MITER; + break; + case GraphicsPrimitive::PrimitiveType::MODEL_SPACE_POLYGONAL_LINES: + drawingSpace = DrawingSpace::MODEL_XY; + lineType = LineType::LINES; + break; case GraphicsPrimitive::PrimitiveType::POLYGONAL_LINE_LOOP_BEVEL_JOIN: lineType = LineType::LINE_LOOP; joinType = JoinType::BEVEL; @@ -216,6 +245,7 @@ GraphicsOpenGLPolylineTriangles::convertWorkbenchLinePrimitiveTypeToOpenGL(const primitive->m_polygonalLinePrimitiveRestartIndices, lineWidthPixels, colorType, + drawingSpace, lineType, joinType); @@ -570,9 +600,18 @@ void GraphicsOpenGLPolylineTriangles::convertFromModelToWindowCoordinate(const float modelXYZ[3], float windowXYZOut[3]) const { - CaretAssert(m_transformEvent); - m_transformEvent->transformPoint(modelXYZ, - windowXYZOut); + switch (m_drawingSpace) { + case DrawingSpace::MODEL_XY: + windowXYZOut[0] = modelXYZ[0]; + windowXYZOut[1] = modelXYZ[1]; + windowXYZOut[2] = modelXYZ[2]; + break; + case DrawingSpace::WINDOW: + CaretAssert(m_transformEvent); + m_transformEvent->transformPoint(modelXYZ, + windowXYZOut); + break; + } } /** @@ -635,7 +674,7 @@ GraphicsOpenGLPolylineTriangles::createTrianglesFromWindowVertices(const int32_t /* * "Width" of rectangle */ - const float halfWidth = m_lineThicknessPixels / 2.0f; + const float halfWidth = m_lineThickness / 2.0f; const float halfWidthX = perpendicularVector[0] * halfWidth; const float halfWidthY = perpendicularVector[1] * halfWidth; @@ -1046,7 +1085,7 @@ GraphicsOpenGLPolylineTriangles::performMiterJoin(const PolylineInfo& polyOne, /* * Miter limit is a multiple of the line thickness */ - const float miterLimit = m_lineThicknessPixels; + const float miterLimit = m_lineThickness; const float miterLimitSquared = miterLimit * miterLimit; /* diff --git a/src/Graphics/GraphicsOpenGLPolylineTriangles.h b/src/Graphics/GraphicsOpenGLPolylineTriangles.h index 6916cd580..cd67d8c9d 100644 --- a/src/Graphics/GraphicsOpenGLPolylineTriangles.h +++ b/src/Graphics/GraphicsOpenGLPolylineTriangles.h @@ -62,6 +62,23 @@ namespace caret { FLOAT_RGBA_SOLID }; + /** + * Drawing space + */ + enum class DrawingSpace { + /** + * Lines are drawn using the current modeling transformations in + * the XY plane. Z-coordinates should all be the same. + */ + MODEL_XY, + /** + * Coordinates of the lines are converted into window (pixel) coordinates + * and the lines are drawn in 'window space'. The lines always + * face the user even with rotations. + */ + WINDOW + }; + /** * Join tyupe of how connected lines are drawn to remove gaps between segments */ @@ -80,7 +97,7 @@ namespace caret { enum class LineType { /** Each pair of vertices is an independent line segment (GL_LINES) */ LINES, - /** A connected set of lines forming a loop (last is automaticall connected to first) */ + /** A connected set of lines forming a loop (last is automatically connected to first) */ LINE_LOOP, /** A connected set of lines (last is not connected to first) */ LINE_STRIP @@ -152,8 +169,9 @@ namespace caret { const std::vector& floatRGBA, const std::vector& byteRGBA, const std::set& vertexPrimitiveRestartIndices, - const float lineThicknessPixels, + const float lineThickness, const ColorType colorType, + const DrawingSpace drawingSpace, const LineType lineType, const JoinType joinType); @@ -214,10 +232,12 @@ namespace caret { std::set m_vertexPrimitiveRestartIndices; - const float m_lineThicknessPixels; + const float m_lineThickness; const ColorType m_colorType; + const DrawingSpace m_drawingSpace; + const LineType m_lineType; const JoinType m_joinType; diff --git a/src/Graphics/GraphicsPrimitive.cxx b/src/Graphics/GraphicsPrimitive.cxx index 4b9b44a79..02689727f 100644 --- a/src/Graphics/GraphicsPrimitive.cxx +++ b/src/Graphics/GraphicsPrimitive.cxx @@ -375,6 +375,8 @@ GraphicsPrimitive::isValid() const switch (m_primitiveType) { case PrimitiveType::OPENGL_LINE_LOOP: + case PrimitiveType::MODEL_SPACE_POLYGONAL_LINE_LOOP_BEVEL_JOIN: + case PrimitiveType::MODEL_SPACE_POLYGONAL_LINE_LOOP_MITER_JOIN: case PrimitiveType::POLYGONAL_LINE_LOOP_BEVEL_JOIN: case PrimitiveType::POLYGONAL_LINE_LOOP_MITER_JOIN: if (numXYZ < 3) { @@ -382,12 +384,15 @@ GraphicsPrimitive::isValid() const } break; case PrimitiveType::OPENGL_LINE_STRIP: + case PrimitiveType::MODEL_SPACE_POLYGONAL_LINE_STRIP_BEVEL_JOIN: + case PrimitiveType::MODEL_SPACE_POLYGONAL_LINE_STRIP_MITER_JOIN: case PrimitiveType::POLYGONAL_LINE_STRIP_BEVEL_JOIN: case PrimitiveType::POLYGONAL_LINE_STRIP_MITER_JOIN: if (numXYZ < 2) { CaretLogWarning("Line strip must have at least 2 vertices."); } break; + case PrimitiveType::MODEL_SPACE_POLYGONAL_LINES: case PrimitiveType::OPENGL_LINES: case PrimitiveType::POLYGONAL_LINES: if (numXYZ < 2) { @@ -486,6 +491,21 @@ GraphicsPrimitive::getPrimitiveTypeAsText() const case PrimitiveType::OPENGL_TRIANGLES: s = "OpenGL Triangles"; break; + case PrimitiveType::MODEL_SPACE_POLYGONAL_LINE_LOOP_BEVEL_JOIN: + s = "Model Space Polygonal Line Loop Bevel Join"; + break; + case PrimitiveType::MODEL_SPACE_POLYGONAL_LINE_LOOP_MITER_JOIN: + s = "Model Space Polygonal Line Loop Meter Join"; + break; + case PrimitiveType::MODEL_SPACE_POLYGONAL_LINE_STRIP_BEVEL_JOIN: + s = "Model Space Polygonal Line Strip Bevel Join"; + break; + case PrimitiveType::MODEL_SPACE_POLYGONAL_LINE_STRIP_MITER_JOIN: + s = "Model Space Polygonal Line Strip Miter Join"; + break; + case PrimitiveType::MODEL_SPACE_POLYGONAL_LINES: + s = "Model Space Polygonal Lines"; + break; case PrimitiveType::POLYGONAL_LINE_LOOP_BEVEL_JOIN: s = "Polygonal Line Loop Bevel Join"; break; @@ -649,6 +669,17 @@ GraphicsPrimitive::toStringPrivate(const bool includeAllDataFlag) const break; case PrimitiveType::OPENGL_TRIANGLES: break; + case PrimitiveType::MODEL_SPACE_POLYGONAL_LINE_LOOP_BEVEL_JOIN: + case PrimitiveType::MODEL_SPACE_POLYGONAL_LINE_LOOP_MITER_JOIN: + addLineWidthFlag = true; + break; + case PrimitiveType::MODEL_SPACE_POLYGONAL_LINE_STRIP_BEVEL_JOIN: + case PrimitiveType::MODEL_SPACE_POLYGONAL_LINE_STRIP_MITER_JOIN: + addLineWidthFlag = true; + break; + case PrimitiveType::MODEL_SPACE_POLYGONAL_LINES: + addLineWidthFlag = true; + break; case PrimitiveType::POLYGONAL_LINE_LOOP_BEVEL_JOIN: case PrimitiveType::POLYGONAL_LINE_LOOP_MITER_JOIN: addLineWidthFlag = true; @@ -1160,6 +1191,16 @@ GraphicsPrimitive::addPrimitiveRestart() break; case PrimitiveType::OPENGL_TRIANGLES: break; + case PrimitiveType::MODEL_SPACE_POLYGONAL_LINE_LOOP_BEVEL_JOIN: + case PrimitiveType::MODEL_SPACE_POLYGONAL_LINE_LOOP_MITER_JOIN: + polygonalLineFlag = true; + break; + case PrimitiveType::MODEL_SPACE_POLYGONAL_LINE_STRIP_BEVEL_JOIN: + case PrimitiveType::MODEL_SPACE_POLYGONAL_LINE_STRIP_MITER_JOIN: + polygonalLineFlag = true; + break; + case PrimitiveType::MODEL_SPACE_POLYGONAL_LINES: + break; case PrimitiveType::POLYGONAL_LINE_LOOP_BEVEL_JOIN: case PrimitiveType::POLYGONAL_LINE_LOOP_MITER_JOIN: polygonalLineFlag = true; diff --git a/src/Graphics/GraphicsPrimitive.h b/src/Graphics/GraphicsPrimitive.h index b8374dcc2..e4a6cf098 100644 --- a/src/Graphics/GraphicsPrimitive.h +++ b/src/Graphics/GraphicsPrimitive.h @@ -146,26 +146,60 @@ namespace caret { OPENGL_TRIANGLES, /** * Like OPENGL_LINE_LOOP but there is no limit on line width as it draws the lines using polygons + * and polygons that form the line use a BEVEL join at vertices. + * Draws in MODEL space so lines are affected by model transformations + */ + MODEL_SPACE_POLYGONAL_LINE_LOOP_BEVEL_JOIN, + /** + * Like OPENGL_LINE_LOOP but there is no limit on line width as it draws the lines using polygons + * and polygons that form the line use a MITER join at vertices + * Draws in MODEL space so lines are affected by model transformations + */ + MODEL_SPACE_POLYGONAL_LINE_LOOP_MITER_JOIN, + /** + * Like OPENGL_LINE_STRIP but there is no limit on line width as it draws the lines using polygons * and polygons that form the line use a BEVEL join at vertices + * Draws in MODEL space so lines are affected by model transformations + */ + MODEL_SPACE_POLYGONAL_LINE_STRIP_BEVEL_JOIN, + /** + * Like OPENGL_LINE_STRIP but there is no limit on line width as it draws the lines using polygons + * and polygons that form the line use a MITER join at vertices + * Draws in MODEL space so lines are affected by model transformations + */ + MODEL_SPACE_POLYGONAL_LINE_STRIP_MITER_JOIN, + /** + * Like OPENGL_LINES but there is no limit on line width as it draws the lines using polygons. + * Draws in MODEL space so lines are affected by model transformations + */ + MODEL_SPACE_POLYGONAL_LINES, + /** + * Like OPENGL_LINE_LOOP but there is no limit on line width as it draws the lines using polygons + * and polygons that form the line use a BEVEL join at vertices. + * Lines drawn in window space so that lines always face user */ POLYGONAL_LINE_LOOP_BEVEL_JOIN, /** * Like OPENGL_LINE_LOOP but there is no limit on line width as it draws the lines using polygons * and polygons that form the line use a MITER join at vertices + * Lines drawn in window space so that lines always face user */ POLYGONAL_LINE_LOOP_MITER_JOIN, /** * Like OPENGL_LINE_STRIP but there is no limit on line width as it draws the lines using polygons * and polygons that form the line use a BEVEL join at vertices + * Lines drawn in window space so that lines always face user */ POLYGONAL_LINE_STRIP_BEVEL_JOIN, /** * Like OPENGL_LINE_STRIP but there is no limit on line width as it draws the lines using polygons * and polygons that form the line use a MITER join at vertices + * Lines drawn in window space so that lines always face user */ POLYGONAL_LINE_STRIP_MITER_JOIN, /** * Like OPENGL_LINES but there is no limit on line width as it draws the lines using polygons. + * Lines drawn in window space so that lines always face user */ POLYGONAL_LINES, /* diff --git a/src/Graphics/GraphicsPrimitiveSelectionHelper.cxx b/src/Graphics/GraphicsPrimitiveSelectionHelper.cxx index e1f2a2d06..114cd32e7 100644 --- a/src/Graphics/GraphicsPrimitiveSelectionHelper.cxx +++ b/src/Graphics/GraphicsPrimitiveSelectionHelper.cxx @@ -74,15 +74,20 @@ GraphicsPrimitiveSelectionHelper::setupSelectionBeforeDrawing() switch (m_parentGraphicsPrimitive->getPrimitiveType()) { case GraphicsPrimitive::PrimitiveType::OPENGL_LINE_LOOP: + case GraphicsPrimitive::PrimitiveType::MODEL_SPACE_POLYGONAL_LINE_LOOP_BEVEL_JOIN: + case GraphicsPrimitive::PrimitiveType::MODEL_SPACE_POLYGONAL_LINE_LOOP_MITER_JOIN: case GraphicsPrimitive::PrimitiveType::POLYGONAL_LINE_LOOP_BEVEL_JOIN: case GraphicsPrimitive::PrimitiveType::POLYGONAL_LINE_LOOP_MITER_JOIN: m_numberOfVerticesPerPrimitive = numberOfVertices; break; + case GraphicsPrimitive::PrimitiveType::MODEL_SPACE_POLYGONAL_LINE_STRIP_BEVEL_JOIN: + case GraphicsPrimitive::PrimitiveType::MODEL_SPACE_POLYGONAL_LINE_STRIP_MITER_JOIN: case GraphicsPrimitive::PrimitiveType::OPENGL_LINE_STRIP: case GraphicsPrimitive::PrimitiveType::POLYGONAL_LINE_STRIP_BEVEL_JOIN: case GraphicsPrimitive::PrimitiveType::POLYGONAL_LINE_STRIP_MITER_JOIN: m_numberOfVerticesPerPrimitive = numberOfVertices; break; + case GraphicsPrimitive::PrimitiveType::MODEL_SPACE_POLYGONAL_LINES: case GraphicsPrimitive::PrimitiveType::OPENGL_LINES: case GraphicsPrimitive::PrimitiveType::POLYGONAL_LINES: m_numberOfVerticesPerPrimitive = 2; diff --git a/src/Graphics/GraphicsShape.cxx b/src/Graphics/GraphicsShape.cxx index c055f5203..687f8dd8b 100644 --- a/src/Graphics/GraphicsShape.cxx +++ b/src/Graphics/GraphicsShape.cxx @@ -287,6 +287,42 @@ GraphicsShape::drawEllipseOutlineByteColor(const double majorAxis, GraphicsEngineDataOpenGL::draw(primitive.get()); } +/** + * Draw an outline ellipse in the XY plane (all Z-coordinates will be zero). + * + * @param majorAxis + * Diameter of the major axis. + * @param minorAxis + * Diameter of the minor axis. + * @param rgba + * Color for drawing. + * @param lineThickness + * Thickness of the line. + */ +void +GraphicsShape::drawEllipseOutlineModelSpaceByteColor(const double majorAxis, + const double minorAxis, + const uint8_t rgba[4], + const double lineThickness) +{ + std::vector ellipseXYZ; + createEllipseVertices(majorAxis, minorAxis, ellipseXYZ); + + std::unique_ptr primitive(GraphicsPrimitive::newPrimitiveV3f(GraphicsPrimitive::PrimitiveType::MODEL_SPACE_POLYGONAL_LINE_LOOP_MITER_JOIN, + rgba)); + const int32_t numVertices = static_cast(ellipseXYZ.size() / 3); + primitive->reserveForNumberOfVertices(numVertices); + for (int32_t i = 0; i < numVertices; i++) { + primitive->addVertex(&ellipseXYZ[i * 3]); + } + + primitive->setLineWidth(GraphicsPrimitive::LineWidthType::PIXELS, + lineThickness); + primitive->setUsageTypeAll(GraphicsPrimitive::UsageType::MODIFIED_ONCE_DRAWN_FEW_TIMES); + + GraphicsEngineDataOpenGL::draw(primitive.get()); +} + /** * Draw a filled ellipse. * diff --git a/src/Graphics/GraphicsShape.h b/src/Graphics/GraphicsShape.h index 8cec3d418..d77d6b6dd 100644 --- a/src/Graphics/GraphicsShape.h +++ b/src/Graphics/GraphicsShape.h @@ -78,6 +78,11 @@ namespace caret { const double minorAxis, const uint8_t rgba[4]); + static void drawEllipseOutlineModelSpaceByteColor(const double majorAxis, + const double minorAxis, + const uint8_t rgba[4], + const double lineThickness); + static void drawLinesByteColor(const std::vector& xyz, const uint8_t rgba[4], const GraphicsPrimitive::LineWidthType lineThicknessType, -- GitLab From 0127524a219795f5614e83b897b147fbd4bc8b24 Mon Sep 17 00:00:00 2001 From: John Harwell Date: Thu, 6 Sep 2018 15:44:35 -0500 Subject: [PATCH 038/427] WB-636 Texture Based Annotations: Add view dependent culling (backface testing) to text annotations with tangent surface vector offsets. The text drawing logic draws text even when it is backfiring. So, test the backfiring status of a text annotation before attempting to draw the text (and its background or foreground outline). --- ...inOpenGLAnnotationDrawingFixedPipeline.cxx | 40 +++++++++++++++++++ ...rainOpenGLAnnotationDrawingFixedPipeline.h | 3 ++ src/Graphics/GraphicsEngineDataOpenGL.cxx | 38 ------------------ 3 files changed, 43 insertions(+), 38 deletions(-) diff --git a/src/Brain/BrainOpenGLAnnotationDrawingFixedPipeline.cxx b/src/Brain/BrainOpenGLAnnotationDrawingFixedPipeline.cxx index de5905ffe..faf1bfa3a 100644 --- a/src/Brain/BrainOpenGLAnnotationDrawingFixedPipeline.cxx +++ b/src/Brain/BrainOpenGLAnnotationDrawingFixedPipeline.cxx @@ -3062,6 +3062,41 @@ BrainOpenGLAnnotationDrawingFixedPipeline::getSurfaceNormalVector(const Surface* } } +/** + * @return True If the coordinate/normal vector backfacing (facing away from viewer)? + * + * @param xyz + * Coordinate. + * @param normal + * Normal vector. + */ +bool +BrainOpenGLAnnotationDrawingFixedPipeline::isBackFacing(const float xyz[3], + const float normal[3]) const +{ + /* + * We don't know where the viewer is located in relation to the view of the model. + * But, we can transform the XYZ coordinate and another XYZ coordinate along the + * normal vector from model space to window space. Then, we can compare the transformed + * Z-coordinates and if the Window Z along the normal vector is greater than Window Z + * of the coordinate, the normal vector is pointing to the viewer (if NOT then backfacing). + */ + CaretAssert(m_transformEvent.get()); + CaretAssert(m_transformEvent->isValid()); + float windowXYZ[3]; + const float length(25); + float offsetXYZ[3] { + xyz[0] + (normal[0] * length), + xyz[1] + (normal[1] * length), + xyz[2] + (normal[2] * length) + }; + m_transformEvent->transformPoint(xyz, windowXYZ); + float windowOffsetXYZ[3]; + m_transformEvent->transformPoint(offsetXYZ, windowOffsetXYZ); + const float diff = windowOffsetXYZ[2] - windowXYZ[2]; + return (diff < 0.0f); +} + /** * Draw an annotation text with surface tangent offset * @@ -3102,6 +3137,11 @@ BrainOpenGLAnnotationDrawingFixedPipeline::drawTextSurfaceTangentOffset(Annotati } } + if (isBackFacing(vertexXYZ, + vertexNormalXYZ)) { + return false; + } + double bottomLeft[3]; double bottomRight[3]; double topRight[3]; diff --git a/src/Brain/BrainOpenGLAnnotationDrawingFixedPipeline.h b/src/Brain/BrainOpenGLAnnotationDrawingFixedPipeline.h index 3620ad253..3214241e8 100644 --- a/src/Brain/BrainOpenGLAnnotationDrawingFixedPipeline.h +++ b/src/Brain/BrainOpenGLAnnotationDrawingFixedPipeline.h @@ -356,6 +356,9 @@ namespace caret { const int32_t vertexIndex, float normalVectorOut[3]) const; + bool isBackFacing(const float xyz[3], + const float normal[3]) const; + BrainOpenGLFixedPipeline* m_brainOpenGLFixedPipeline; Inputs* m_inputs; diff --git a/src/Graphics/GraphicsEngineDataOpenGL.cxx b/src/Graphics/GraphicsEngineDataOpenGL.cxx index 0ee43c2b9..2fc7e3aa1 100644 --- a/src/Graphics/GraphicsEngineDataOpenGL.cxx +++ b/src/Graphics/GraphicsEngineDataOpenGL.cxx @@ -571,44 +571,6 @@ GraphicsEngineDataOpenGL::draw(GraphicsPrimitive* primitive) } } -///** -// * Draw the graphics primitive in window space. -// * -// * @param drawMode -// * Mode for drawing. -// * @param primitive -// * Primitive that is drawn. -// * @param primitiveSelectionHelper -// * Selection helper when draw mode is selection. -// */ -//void -//GraphicsEngineDataOpenGL::drawWindowSpace(const PrivateDrawMode drawMode, -// GraphicsPrimitive* primitive, -// GraphicsPrimitiveSelectionHelper* primitiveSelectionHelper) -//{ -// int32_t polygonMode[2]; -// int32_t viewport[4]; -// saveOpenGLStateForWindowSpaceDrawing(polygonMode, -// viewport); -// -// glMatrixMode(GL_PROJECTION); -// glLoadIdentity(); -// glOrtho(viewport[0], viewport[0] + viewport[2], -// viewport[1], viewport[1] + viewport[3], -// 0, 1); -// glMatrixMode(GL_MODELVIEW); -// glLoadIdentity(); -// -// glPolygonMode(GL_FRONT, GL_FILL); -// -// drawPrivate(drawMode, -// primitive, -// primitiveSelectionHelper); -// -// restoreOpenGLStateForWindowSpaceDrawing(polygonMode, -// viewport); -//} - /** * Draw the graphics primitive in model space. * -- GitLab From 4837b0753b32d3543a98605c20e337e8b5e27c35 Mon Sep 17 00:00:00 2001 From: John Harwell Date: Fri, 7 Sep 2018 16:02:33 -0500 Subject: [PATCH 039/427] Cleanup of AnnotationCoordinateInformation by grouping, by type, related coordinate information. --- src/GuiQt/AnnotationCoordinateInformation.cxx | 337 ++++++++---------- src/GuiQt/AnnotationCoordinateInformation.h | 70 ++-- .../AnnotationCoordinateSelectionWidget.cxx | 154 ++++---- src/GuiQt/AnnotationCreateDialog.cxx | 79 +--- src/GuiQt/AnnotationPasteDialog.cxx | 40 +-- src/GuiQt/UserInputModeAnnotations.cxx | 40 +-- 6 files changed, 308 insertions(+), 412 deletions(-) diff --git a/src/GuiQt/AnnotationCoordinateInformation.cxx b/src/GuiQt/AnnotationCoordinateInformation.cxx index 0fa8a6f4a..75e4be85b 100644 --- a/src/GuiQt/AnnotationCoordinateInformation.cxx +++ b/src/GuiQt/AnnotationCoordinateInformation.cxx @@ -72,38 +72,11 @@ AnnotationCoordinateInformation::~AnnotationCoordinateInformation() */ void AnnotationCoordinateInformation::reset() { - m_modelXYZValid = false; - m_surfaceNodeValid = false; - m_surfaceStructure = StructureEnum::INVALID; - m_surfaceNumberOfNodes = 0; - m_surfaceNodeIndex = -1; - m_surfaceNodeOffset = 0.0; - m_surfaceNodeVector = AnnotationSurfaceOffsetVectorTypeEnum::CENTROID_THRU_VERTEX; - m_tabIndex = -1; - m_tabWidth = 0; - m_tabHeight = 0; - m_windowIndex = -1; - m_windowWidth = 0; - m_windowHeight = 0; - m_modelXYZ[0] = 0.0; - m_modelXYZ[1] = 0.0; - m_modelXYZ[2] = 0.0; - m_tabXYZ[0] = 0.0; - m_tabXYZ[1] = 0.0; - m_tabXYZ[2] = 0.0; - m_tabPixelXYZ[0] = 0.0; - m_tabPixelXYZ[1] = 0.0; - m_tabPixelXYZ[2] = 0.0; - m_windowXYZ[0] = 0.0; - m_windowXYZ[1] = 0.0; - m_windowXYZ[2] = 0.0; - m_windowPixelXYZ[0] = 0.0; - m_windowPixelXYZ[1] = 0.0; - m_windowPixelXYZ[2] = 0.0; - m_chartXYZ[0] = 0.0; - m_chartXYZ[1] = 0.0; - m_chartXYZ[2] = 0.0; - m_chartXYZValid = false; + m_modelSpaceInfo = ModelSpaceInfo(); + m_tabSpaceInfo = TabWindowSpaceInfo(); + m_windowSpaceInfo = TabWindowSpaceInfo(); + m_chartSpaceInfo = ChartSpaceInfo(); + m_surfaceSpaceInfo = SurfaceSpaceInfo(); } bool @@ -113,22 +86,22 @@ AnnotationCoordinateInformation::isCoordinateSpaceValid(const AnnotationCoordina switch (space) { case AnnotationCoordinateSpaceEnum::CHART: - validSpaceFlag = m_chartXYZValid; + validSpaceFlag = m_chartSpaceInfo.m_validFlag; break; case AnnotationCoordinateSpaceEnum::STEREOTAXIC: - validSpaceFlag = m_modelXYZValid; + validSpaceFlag = m_modelSpaceInfo.m_validFlag; break; case AnnotationCoordinateSpaceEnum::SURFACE: - validSpaceFlag = m_surfaceNodeValid; + validSpaceFlag = m_surfaceSpaceInfo.m_validFlag; break; case AnnotationCoordinateSpaceEnum::TAB: - validSpaceFlag = (m_tabIndex >= 0); + validSpaceFlag = (m_tabSpaceInfo.m_index >= 0); break; case AnnotationCoordinateSpaceEnum::VIEWPORT: CaretAssertMessage(0, "Should not create/move viewport annotations"); break; case AnnotationCoordinateSpaceEnum::WINDOW: - validSpaceFlag = (m_windowIndex >= 0); + validSpaceFlag = (m_windowSpaceInfo.m_index >= 0); break; } @@ -187,14 +160,14 @@ AnnotationCoordinateInformation::getValidCoordinateSpaces(const AnnotationCoordi /* * Both coord info's must be in the SAME TAB */ - if (coordInfoOne->m_tabIndex != coordInfoTwo->m_tabIndex) { + if (coordInfoOne->m_tabSpaceInfo.m_index != coordInfoTwo->m_tabSpaceInfo.m_index) { addItFlag = false; } case AnnotationCoordinateSpaceEnum::STEREOTAXIC: /* * Both coord info's must be in the SAME TAB */ - if (coordInfoOne->m_tabIndex != coordInfoTwo->m_tabIndex) { + if (coordInfoOne->m_tabSpaceInfo.m_index != coordInfoTwo->m_tabSpaceInfo.m_index) { addItFlag = false; } case AnnotationCoordinateSpaceEnum::SURFACE: @@ -202,24 +175,24 @@ AnnotationCoordinateInformation::getValidCoordinateSpaces(const AnnotationCoordi * Both coord info's must be on same surface and * in the SAME TAB */ - if ((coordInfoOne->m_tabIndex != coordInfoTwo->m_tabIndex) - || (coordInfoOne->m_surfaceNumberOfNodes != coordInfoTwo->m_surfaceNumberOfNodes) - || (coordInfoOne->m_surfaceStructure != coordInfoTwo->m_surfaceStructure)) { + if ((coordInfoOne->m_tabSpaceInfo.m_index != coordInfoTwo->m_tabSpaceInfo.m_index) + || (coordInfoOne->m_surfaceSpaceInfo.m_numberOfNodes != coordInfoTwo->m_surfaceSpaceInfo.m_numberOfNodes) + || (coordInfoOne->m_surfaceSpaceInfo.m_structure != coordInfoTwo->m_surfaceSpaceInfo.m_structure)) { addItFlag = false; } break; case AnnotationCoordinateSpaceEnum::TAB: - if (coordInfoOne->m_tabIndex != coordInfoTwo->m_tabIndex) { + if (coordInfoOne->m_tabSpaceInfo.m_index != coordInfoTwo->m_tabSpaceInfo.m_index) { addItFlag = false; } break; case AnnotationCoordinateSpaceEnum::VIEWPORT: -// if (coordInfoOne->m_windowIndex != coordInfoTwo->m_windowIndex) { -// addItFlag = false; -// } + //if (coordInfoOne->m_windowIndex != coordInfoTwo->m_windowIndex) { + // addItFlag = false; + //} break; case AnnotationCoordinateSpaceEnum::WINDOW: - if (coordInfoOne->m_windowIndex != coordInfoTwo->m_windowIndex) { + if (coordInfoOne->m_windowSpaceInfo.m_index != coordInfoTwo->m_windowSpaceInfo.m_index) { addItFlag = false; } break; @@ -311,21 +284,23 @@ AnnotationCoordinateInformation::createCoordinateInformationFromXY(BrainOpenGLWi SelectionItemVoxel* voxelID = idManager->getVoxelIdentification(); SelectionItemSurfaceNode* surfaceNodeIdentification = idManager->getSurfaceNodeIdentification(); if (surfaceNodeIdentification->isValid()) { - surfaceNodeIdentification->getModelXYZ(coordInfoOut.m_modelXYZ); - coordInfoOut.m_modelXYZValid = true; + surfaceNodeIdentification->getModelXYZ(coordInfoOut.m_modelSpaceInfo.m_xyz); + coordInfoOut.m_modelSpaceInfo.m_validFlag = true; const Surface* surface = surfaceNodeIdentification->getSurface(); CaretAssert(surface); - coordInfoOut.m_surfaceNumberOfNodes = surface->getNumberOfNodes(); - coordInfoOut.m_surfaceStructure = surface->getStructure(); - coordInfoOut.m_surfaceNodeIndex = surfaceNodeIdentification->getNodeNumber(); - coordInfoOut.m_surfaceNodeOffset = AnnotationCoordinate::getDefaultSurfaceOffsetLength(); - coordInfoOut.m_surfaceNodeVector = AnnotationSurfaceOffsetVectorTypeEnum::CENTROID_THRU_VERTEX; - coordInfoOut.m_surfaceNodeValid = true; + coordInfoOut.m_surfaceSpaceInfo.m_numberOfNodes = surface->getNumberOfNodes(); + coordInfoOut.m_surfaceSpaceInfo.m_structure = surface->getStructure(); + coordInfoOut.m_surfaceSpaceInfo.m_nodeIndex = surfaceNodeIdentification->getNodeNumber(); + coordInfoOut.m_surfaceSpaceInfo.m_nodeOffsetLength = AnnotationCoordinate::getDefaultSurfaceOffsetLength(); + coordInfoOut.m_surfaceSpaceInfo.m_nodeVectorOffsetType = AnnotationSurfaceOffsetVectorTypeEnum::CENTROID_THRU_VERTEX; + surface->getNormalVector(surfaceNodeIdentification->getNodeNumber(), + coordInfoOut.m_surfaceSpaceInfo.m_nodeNormalVector); + coordInfoOut.m_surfaceSpaceInfo.m_validFlag = true; } else if (voxelID->isValid()) { - voxelID->getModelXYZ(coordInfoOut.m_modelXYZ); - coordInfoOut.m_modelXYZValid = true; + voxelID->getModelXYZ(coordInfoOut.m_modelSpaceInfo.m_xyz); + coordInfoOut.m_modelSpaceInfo.m_validFlag = true; } /* @@ -343,15 +318,15 @@ AnnotationCoordinateInformation::createCoordinateInformationFromXY(BrainOpenGLWi && (tabX < 100.0) && (tabY >= 0.0) && (tabY <= 100.0)) { - coordInfoOut.m_tabXYZ[0] = tabX; - coordInfoOut.m_tabXYZ[1] = tabY; - coordInfoOut.m_tabXYZ[2] = 0.0; - coordInfoOut.m_tabPixelXYZ[0] = (windowX - tabViewport[0]); - coordInfoOut.m_tabPixelXYZ[1] = (windowY - tabViewport[1]); - coordInfoOut.m_tabPixelXYZ[2] = 0; - coordInfoOut.m_tabIndex = tabContent->getTabNumber(); - coordInfoOut.m_tabWidth = tabViewport[2]; - coordInfoOut.m_tabHeight = tabViewport[3]; + coordInfoOut.m_tabSpaceInfo.m_xyz[0] = tabX; + coordInfoOut.m_tabSpaceInfo.m_xyz[1] = tabY; + coordInfoOut.m_tabSpaceInfo.m_xyz[2] = 0.0; + coordInfoOut.m_tabSpaceInfo.m_pixelXYZ[0] = (windowX - tabViewport[0]); + coordInfoOut.m_tabSpaceInfo.m_pixelXYZ[1] = (windowY - tabViewport[1]); + coordInfoOut.m_tabSpaceInfo.m_pixelXYZ[2] = 0.0; + coordInfoOut.m_tabSpaceInfo.m_index = tabContent->getTabNumber(); + coordInfoOut.m_tabSpaceInfo.m_width = tabViewport[2]; + coordInfoOut.m_tabSpaceInfo.m_height = tabViewport[3]; } } @@ -377,10 +352,10 @@ AnnotationCoordinateInformation::createCoordinateInformationFromXY(BrainOpenGLWi if (gluUnProject(windowX, windowY, 0.0, modelviewArray, projectionArray, viewport, &chartX, &chartY, &chartZ) == GL_TRUE) { - coordInfoOut.m_chartXYZ[0] = static_cast(chartX); - coordInfoOut.m_chartXYZ[1] = static_cast(chartY); - coordInfoOut.m_chartXYZ[2] = static_cast(chartZ); - coordInfoOut.m_chartXYZValid = true; + coordInfoOut.m_chartSpaceInfo.m_xyz[0] = static_cast(chartX); + coordInfoOut.m_chartSpaceInfo.m_xyz[1] = static_cast(chartY); + coordInfoOut.m_chartSpaceInfo.m_xyz[2] = static_cast(chartZ); + coordInfoOut.m_chartSpaceInfo.m_validFlag = true; } } } @@ -388,19 +363,19 @@ AnnotationCoordinateInformation::createCoordinateInformationFromXY(BrainOpenGLWi int windowViewport[4]; viewportContent->getWindowViewport(windowViewport); - coordInfoOut.m_windowPixelXYZ[0] = windowX - windowViewport[0]; - coordInfoOut.m_windowPixelXYZ[1] = windowY - windowViewport[1]; - coordInfoOut.m_windowPixelXYZ[2] = 0.0; - coordInfoOut.m_windowIndex = viewportContent->getWindowIndex(); - coordInfoOut.m_windowWidth = windowViewport[2]; - coordInfoOut.m_windowHeight = windowViewport[3]; + coordInfoOut.m_windowSpaceInfo.m_pixelXYZ[0] = windowX - windowViewport[0]; + coordInfoOut.m_windowSpaceInfo.m_pixelXYZ[1] = windowY - windowViewport[1]; + coordInfoOut.m_windowSpaceInfo.m_pixelXYZ[2] = 0.0; + coordInfoOut.m_windowSpaceInfo.m_index = viewportContent->getWindowIndex(); + coordInfoOut.m_windowSpaceInfo.m_width = windowViewport[2]; + coordInfoOut.m_windowSpaceInfo.m_height = windowViewport[3]; /* * Normalize window coordinates (width and height range [0, 100] */ - coordInfoOut.m_windowXYZ[0] = 100.0 * (coordInfoOut.m_windowPixelXYZ[0] / windowViewport[2]); - coordInfoOut.m_windowXYZ[1] = 100.0 * (coordInfoOut.m_windowPixelXYZ[1] / windowViewport[3]); - coordInfoOut.m_windowXYZ[2] = 0.0; + coordInfoOut.m_windowSpaceInfo.m_xyz[0] = 100.0 * (coordInfoOut.m_windowSpaceInfo.m_pixelXYZ[0] / windowViewport[2]); + coordInfoOut.m_windowSpaceInfo.m_xyz[1] = 100.0 * (coordInfoOut.m_windowSpaceInfo.m_pixelXYZ[1] / windowViewport[3]); + coordInfoOut.m_windowSpaceInfo.m_xyz[2] = 0.0; } /** @@ -474,44 +449,44 @@ AnnotationCoordinateInformation::setOneDimAnnotationCoordinatesForSpace(Annotati switch (coordinateSpace) { case AnnotationCoordinateSpaceEnum::CHART: - if (coordInfoOne->m_chartXYZValid) { - startCoordinate->setXYZ(coordInfoOne->m_chartXYZ); + if (coordInfoOne->m_chartSpaceInfo.m_validFlag) { + startCoordinate->setXYZ(coordInfoOne->m_chartSpaceInfo.m_xyz); annotation->setCoordinateSpace(AnnotationCoordinateSpaceEnum::CHART); validCoordinateFlag = true; if (coordInfoTwo != NULL) { - if (coordInfoTwo->m_chartXYZValid) { + if (coordInfoTwo->m_chartSpaceInfo.m_validFlag) { if (endCoordinate != NULL) { - endCoordinate->setXYZ(coordInfoTwo->m_chartXYZ); + endCoordinate->setXYZ(coordInfoTwo->m_chartSpaceInfo.m_xyz); } } } } break; case AnnotationCoordinateSpaceEnum::STEREOTAXIC: - if (coordInfoOne->m_modelXYZValid) { - startCoordinate->setXYZ(coordInfoOne->m_modelXYZ); + if (coordInfoOne->m_modelSpaceInfo.m_validFlag) { + startCoordinate->setXYZ(coordInfoOne->m_modelSpaceInfo.m_xyz); annotation->setCoordinateSpace(AnnotationCoordinateSpaceEnum::STEREOTAXIC); validCoordinateFlag = true; if (coordInfoTwo != NULL) { - if (coordInfoTwo->m_modelXYZValid) { + if (coordInfoTwo->m_modelSpaceInfo.m_validFlag) { if (endCoordinate != NULL) { - endCoordinate->setXYZ(coordInfoTwo->m_modelXYZ); + endCoordinate->setXYZ(coordInfoTwo->m_modelSpaceInfo.m_xyz); } } } } break; case AnnotationCoordinateSpaceEnum::SURFACE: - if (coordInfoOne->m_surfaceNodeValid) { + if (coordInfoOne->m_surfaceSpaceInfo.m_validFlag) { const float surfaceOffsetLength = startCoordinate->getSurfaceOffsetLength(); const AnnotationSurfaceOffsetVectorTypeEnum::Enum surfaceOffsetVector = startCoordinate->getSurfaceOffsetVectorType(); - startCoordinate->setSurfaceSpace(coordInfoOne->m_surfaceStructure, - coordInfoOne->m_surfaceNumberOfNodes, - coordInfoOne->m_surfaceNodeIndex, + startCoordinate->setSurfaceSpace(coordInfoOne->m_surfaceSpaceInfo.m_structure, + coordInfoOne->m_surfaceSpaceInfo.m_numberOfNodes, + coordInfoOne->m_surfaceSpaceInfo.m_nodeIndex, surfaceOffsetLength, surfaceOffsetVector); annotation->setCoordinateSpace(AnnotationCoordinateSpaceEnum::SURFACE); @@ -519,12 +494,11 @@ AnnotationCoordinateInformation::setOneDimAnnotationCoordinatesForSpace(Annotati validCoordinateFlag = true; if (coordInfoTwo != NULL) { - if (coordInfoTwo->m_surfaceNodeValid) { + if (coordInfoTwo->m_surfaceSpaceInfo.m_validFlag) { if (endCoordinate != NULL) { - const float surfaceOffsetLength = endCoordinate->getSurfaceOffsetLength(); - endCoordinate->setSurfaceSpace(coordInfoTwo->m_surfaceStructure, - coordInfoTwo->m_surfaceNumberOfNodes, - coordInfoTwo->m_surfaceNodeIndex, + endCoordinate->setSurfaceSpace(coordInfoTwo->m_surfaceSpaceInfo.m_structure, + coordInfoTwo->m_surfaceSpaceInfo.m_numberOfNodes, + coordInfoTwo->m_surfaceSpaceInfo.m_nodeIndex, surfaceOffsetLength, surfaceOffsetVector); } @@ -533,26 +507,26 @@ AnnotationCoordinateInformation::setOneDimAnnotationCoordinatesForSpace(Annotati } break; case AnnotationCoordinateSpaceEnum::TAB: - if (coordInfoOne->m_tabIndex >= 0) { - startCoordinate->setXYZ(coordInfoOne->m_tabXYZ); + if (coordInfoOne->m_tabSpaceInfo.m_index >= 0) { + startCoordinate->setXYZ(coordInfoOne->m_tabSpaceInfo.m_xyz); annotation->setCoordinateSpace(AnnotationCoordinateSpaceEnum::TAB); - annotation->setTabIndex(coordInfoOne->m_tabIndex); + annotation->setTabIndex(coordInfoOne->m_tabSpaceInfo.m_index); validCoordinateFlag = true; if (coordInfoTwo != NULL) { - if (coordInfoTwo->m_tabIndex >= 0) { + if (coordInfoTwo->m_tabSpaceInfo.m_index >= 0) { if (endCoordinate != NULL) { - endCoordinate->setXYZ(coordInfoTwo->m_tabXYZ); + endCoordinate->setXYZ(coordInfoTwo->m_tabSpaceInfo.m_xyz); } } } else if (endCoordinate != NULL) { double xyz[3] = { - coordInfoOne->m_tabXYZ[0], - coordInfoOne->m_tabXYZ[1], - coordInfoOne->m_tabXYZ[2] + coordInfoOne->m_tabSpaceInfo.m_xyz[0], + coordInfoOne->m_tabSpaceInfo.m_xyz[1], + coordInfoOne->m_tabSpaceInfo.m_xyz[2] }; if (xyz[1] > 50.0) { xyz[1] -= 25.0; @@ -560,7 +534,7 @@ AnnotationCoordinateInformation::setOneDimAnnotationCoordinatesForSpace(Annotati } else { xyz[1] += 25.0; - endCoordinate->setXYZ(coordInfoOne->m_tabXYZ); + endCoordinate->setXYZ(coordInfoOne->m_tabSpaceInfo.m_xyz); startCoordinate->setXYZ(xyz); } } @@ -570,25 +544,25 @@ AnnotationCoordinateInformation::setOneDimAnnotationCoordinatesForSpace(Annotati CaretAssert(0); break; case AnnotationCoordinateSpaceEnum::WINDOW: - if (coordInfoOne->m_windowIndex >= 0) { - startCoordinate->setXYZ(coordInfoOne->m_windowXYZ); + if (coordInfoOne->m_windowSpaceInfo.m_index >= 0) { + startCoordinate->setXYZ(coordInfoOne->m_windowSpaceInfo.m_xyz); annotation->setCoordinateSpace(AnnotationCoordinateSpaceEnum::WINDOW); - annotation->setWindowIndex(coordInfoOne->m_windowIndex); + annotation->setWindowIndex(coordInfoOne->m_windowSpaceInfo.m_index); validCoordinateFlag = true; if (coordInfoTwo != NULL) { - if (coordInfoTwo->m_windowIndex >= 0) { + if (coordInfoTwo->m_windowSpaceInfo.m_index >= 0) { if (endCoordinate != NULL) { - endCoordinate->setXYZ(coordInfoTwo->m_windowXYZ); + endCoordinate->setXYZ(coordInfoTwo->m_windowSpaceInfo.m_xyz); } } } else if (endCoordinate != NULL) { double xyz[3] = { - coordInfoOne->m_windowXYZ[0], - coordInfoOne->m_windowXYZ[1], - coordInfoOne->m_windowXYZ[2] + coordInfoOne->m_windowSpaceInfo.m_xyz[0], + coordInfoOne->m_windowSpaceInfo.m_xyz[1], + coordInfoOne->m_windowSpaceInfo.m_xyz[2] }; if (xyz[1] > 50.0) { xyz[1] -= 25.0; @@ -596,7 +570,7 @@ AnnotationCoordinateInformation::setOneDimAnnotationCoordinatesForSpace(Annotati } else { xyz[1] += 25.0; - endCoordinate->setXYZ(coordInfoOne->m_windowXYZ); + endCoordinate->setXYZ(coordInfoOne->m_windowSpaceInfo.m_xyz); startCoordinate->setXYZ(xyz); } } @@ -640,18 +614,18 @@ AnnotationCoordinateInformation::setTwoDimAnnotationCoordinatesForSpace(Annotati switch (coordinateSpace) { case AnnotationCoordinateSpaceEnum::CHART: - if (coordInfoOne->m_chartXYZValid) { - coordinate->setXYZ(coordInfoOne->m_chartXYZ); + if (coordInfoOne->m_chartSpaceInfo.m_validFlag) { + coordinate->setXYZ(coordInfoOne->m_chartSpaceInfo.m_xyz); annotation->setCoordinateSpace(AnnotationCoordinateSpaceEnum::CHART); validCoordinateFlag = true; if (optionalCoordInfoTwo != NULL) { - if (optionalCoordInfoTwo->m_chartXYZValid) { + if (optionalCoordInfoTwo->m_chartSpaceInfo.m_validFlag) { float centerXYZ[3] = { - (float)(coordInfoOne->m_modelXYZ[0] + optionalCoordInfoTwo->m_modelXYZ[0]) / 2.0f, - (float)(coordInfoOne->m_modelXYZ[1] + optionalCoordInfoTwo->m_modelXYZ[1]) / 2.0f, - (float)(coordInfoOne->m_modelXYZ[2] + optionalCoordInfoTwo->m_modelXYZ[2]) / 2.0f + (float)(coordInfoOne->m_chartSpaceInfo.m_xyz[0] + optionalCoordInfoTwo->m_chartSpaceInfo.m_xyz[0]) / 2.0f, + (float)(coordInfoOne->m_chartSpaceInfo.m_xyz[1] + optionalCoordInfoTwo->m_chartSpaceInfo.m_xyz[1]) / 2.0f, + (float)(coordInfoOne->m_chartSpaceInfo.m_xyz[2] + optionalCoordInfoTwo->m_chartSpaceInfo.m_xyz[2]) / 2.0f }; coordinate->setXYZ(centerXYZ); setWidthHeightWithTabCoordsFlag = true; @@ -660,18 +634,18 @@ AnnotationCoordinateInformation::setTwoDimAnnotationCoordinatesForSpace(Annotati } break; case AnnotationCoordinateSpaceEnum::STEREOTAXIC: - if (coordInfoOne->m_modelXYZValid) { - coordinate->setXYZ(coordInfoOne->m_modelXYZ); + if (coordInfoOne->m_modelSpaceInfo.m_validFlag) { + coordinate->setXYZ(coordInfoOne->m_modelSpaceInfo.m_xyz); annotation->setCoordinateSpace(AnnotationCoordinateSpaceEnum::STEREOTAXIC); validCoordinateFlag = true; if (optionalCoordInfoTwo != NULL) { - if (optionalCoordInfoTwo->m_modelXYZValid) { + if (optionalCoordInfoTwo->m_modelSpaceInfo.m_validFlag) { float centerXYZ[3] = { - (float)(coordInfoOne->m_modelXYZ[0] + optionalCoordInfoTwo->m_modelXYZ[0]) / 2.0f, - (float)(coordInfoOne->m_modelXYZ[1] + optionalCoordInfoTwo->m_modelXYZ[1]) / 2.0f, - (float)(coordInfoOne->m_modelXYZ[2] + optionalCoordInfoTwo->m_modelXYZ[2]) / 2.0f + (float)(coordInfoOne->m_modelSpaceInfo.m_xyz[0] + optionalCoordInfoTwo->m_modelSpaceInfo.m_xyz[0]) / 2.0f, + (float)(coordInfoOne->m_modelSpaceInfo.m_xyz[1] + optionalCoordInfoTwo->m_modelSpaceInfo.m_xyz[1]) / 2.0f, + (float)(coordInfoOne->m_modelSpaceInfo.m_xyz[2] + optionalCoordInfoTwo->m_modelSpaceInfo.m_xyz[2]) / 2.0f }; coordinate->setXYZ(centerXYZ); setWidthHeightWithTabCoordsFlag = true; @@ -680,29 +654,30 @@ AnnotationCoordinateInformation::setTwoDimAnnotationCoordinatesForSpace(Annotati } break; case AnnotationCoordinateSpaceEnum::SURFACE: - if (coordInfoOne->m_surfaceNodeValid) { - coordinate->setSurfaceSpace(coordInfoOne->m_surfaceStructure, - coordInfoOne->m_surfaceNumberOfNodes, - coordInfoOne->m_surfaceNodeIndex); + if (coordInfoOne->m_surfaceSpaceInfo.m_validFlag) { + coordinate->setSurfaceSpace(coordInfoOne->m_surfaceSpaceInfo.m_structure, + coordInfoOne->m_surfaceSpaceInfo.m_numberOfNodes, + coordInfoOne->m_surfaceSpaceInfo.m_nodeIndex); annotation->setCoordinateSpace(AnnotationCoordinateSpaceEnum::SURFACE); validCoordinateFlag = true; if (optionalCoordInfoTwo != NULL) { - if ((optionalCoordInfoTwo->m_surfaceNodeValid) - && (optionalCoordInfoTwo->m_surfaceStructure == coordInfoOne->m_surfaceStructure)) { - if ((optionalCoordInfoTwo->m_windowIndex == coordInfoOne->m_windowIndex) - && (coordInfoOne->m_windowIndex >= 0)) { - const float windowWidth = coordInfoOne->m_windowWidth; - const float windowHeight = coordInfoOne->m_windowHeight; - const float x1 = coordInfoOne->m_windowXYZ[0] * windowWidth; - const float y1 = coordInfoOne->m_windowXYZ[1] * windowHeight; - const float x2 = optionalCoordInfoTwo->m_windowXYZ[0] * windowWidth; - const float y2 = optionalCoordInfoTwo->m_windowXYZ[1] * windowHeight; + if ((optionalCoordInfoTwo->m_surfaceSpaceInfo.m_validFlag) + && (optionalCoordInfoTwo->m_surfaceSpaceInfo.m_structure == coordInfoOne->m_surfaceSpaceInfo.m_structure + )) { + if ((optionalCoordInfoTwo->m_windowSpaceInfo.m_index == coordInfoOne->m_windowSpaceInfo.m_index) + && (coordInfoOne->m_windowSpaceInfo.m_index >= 0)) { + const float windowWidth = coordInfoOne->m_windowSpaceInfo.m_width; + const float windowHeight = coordInfoOne->m_windowSpaceInfo.m_height; + const float x1 = coordInfoOne->m_windowSpaceInfo.m_xyz[0] * windowWidth; + const float y1 = coordInfoOne->m_windowSpaceInfo.m_xyz[1] * windowHeight; + const float x2 = optionalCoordInfoTwo->m_windowSpaceInfo.m_xyz[0] * windowWidth; + const float y2 = optionalCoordInfoTwo->m_windowSpaceInfo.m_xyz[1] * windowHeight; const int32_t windowX = static_cast((x1 + x2)) / 2.0; const int32_t windowY = static_cast((y1 + y2)) / 2.0; - EventIdentificationRequest idRequest(coordInfoOne->m_windowIndex, + EventIdentificationRequest idRequest(coordInfoOne->m_windowSpaceInfo.m_index, static_cast(windowX), static_cast(windowY)); EventManager::get()->sendEvent(idRequest.getPointer()); @@ -711,9 +686,9 @@ AnnotationCoordinateInformation::setTwoDimAnnotationCoordinatesForSpace(Annotati const SelectionItemSurfaceNode* nodeID = sm->getSurfaceNodeIdentification(); CaretAssert(nodeID); if (nodeID->isValid()) { - if (nodeID->getSurface()->getStructure() == coordInfoOne->m_surfaceStructure) { - coordinate->setSurfaceSpace(coordInfoOne->m_surfaceStructure, - coordInfoOne->m_surfaceNumberOfNodes, + if (nodeID->getSurface()->getStructure() == coordInfoOne->m_surfaceSpaceInfo.m_structure) { + coordinate->setSurfaceSpace(coordInfoOne->m_surfaceSpaceInfo.m_structure, + coordInfoOne->m_surfaceSpaceInfo.m_numberOfNodes, nodeID->getNodeNumber()); setWidthHeightWithTabCoordsFlag = true; } @@ -725,19 +700,19 @@ AnnotationCoordinateInformation::setTwoDimAnnotationCoordinatesForSpace(Annotati } break; case AnnotationCoordinateSpaceEnum::TAB: - if (coordInfoOne->m_tabIndex >= 0) { - coordinate->setXYZ(coordInfoOne->m_tabXYZ); + if (coordInfoOne->m_tabSpaceInfo.m_index >= 0) { + coordinate->setXYZ(coordInfoOne->m_tabSpaceInfo.m_xyz); annotation->setCoordinateSpace(AnnotationCoordinateSpaceEnum::TAB); - annotation->setTabIndex(coordInfoOne->m_tabIndex); + annotation->setTabIndex(coordInfoOne->m_tabSpaceInfo.m_index); validCoordinateFlag = true; if (optionalCoordInfoTwo != NULL) { - if (optionalCoordInfoTwo->m_tabIndex == coordInfoOne->m_tabIndex) { + if (optionalCoordInfoTwo->m_tabSpaceInfo.m_index == coordInfoOne->m_tabSpaceInfo.m_index) { float centerXYZ[3] = { - (coordInfoOne->m_tabXYZ[0] + optionalCoordInfoTwo->m_tabXYZ[0]) / 2.0f, - (coordInfoOne->m_tabXYZ[1] + optionalCoordInfoTwo->m_tabXYZ[1]) / 2.0f, - (coordInfoOne->m_tabXYZ[2] + optionalCoordInfoTwo->m_tabXYZ[2]) / 2.0f + (coordInfoOne->m_tabSpaceInfo.m_xyz[0] + optionalCoordInfoTwo->m_tabSpaceInfo.m_xyz[0]) / 2.0f, + (coordInfoOne->m_tabSpaceInfo.m_xyz[1] + optionalCoordInfoTwo->m_tabSpaceInfo.m_xyz[1]) / 2.0f, + (coordInfoOne->m_tabSpaceInfo.m_xyz[2] + optionalCoordInfoTwo->m_tabSpaceInfo.m_xyz[2]) / 2.0f }; coordinate->setXYZ(centerXYZ); setWidthHeightWithTabCoordsFlag = true; @@ -749,19 +724,19 @@ AnnotationCoordinateInformation::setTwoDimAnnotationCoordinatesForSpace(Annotati CaretAssert(0); break; case AnnotationCoordinateSpaceEnum::WINDOW: - if (coordInfoOne->m_windowIndex >= 0) { - coordinate->setXYZ(coordInfoOne->m_windowXYZ); + if (coordInfoOne->m_windowSpaceInfo.m_index >= 0) { + coordinate->setXYZ(coordInfoOne->m_windowSpaceInfo.m_xyz); annotation->setCoordinateSpace(AnnotationCoordinateSpaceEnum::WINDOW); - annotation->setWindowIndex(coordInfoOne->m_windowIndex); + annotation->setWindowIndex(coordInfoOne->m_windowSpaceInfo.m_index); validCoordinateFlag = true; if (optionalCoordInfoTwo != NULL) { - if (optionalCoordInfoTwo->m_windowIndex == coordInfoOne->m_windowIndex) { + if (optionalCoordInfoTwo->m_windowSpaceInfo.m_index == coordInfoOne->m_windowSpaceInfo.m_index) { float centerXYZ[3] = { - (coordInfoOne->m_windowXYZ[0] + optionalCoordInfoTwo->m_windowXYZ[0]) / 2.0f, - (coordInfoOne->m_windowXYZ[1] + optionalCoordInfoTwo->m_windowXYZ[1]) / 2.0f, - (coordInfoOne->m_windowXYZ[2] + optionalCoordInfoTwo->m_windowXYZ[2]) / 2.0f + (coordInfoOne->m_windowSpaceInfo.m_xyz[0] + optionalCoordInfoTwo->m_windowSpaceInfo.m_xyz[0]) / 2.0f, + (coordInfoOne->m_windowSpaceInfo.m_xyz[1] + optionalCoordInfoTwo->m_windowSpaceInfo.m_xyz[1]) / 2.0f, + (coordInfoOne->m_windowSpaceInfo.m_xyz[2] + optionalCoordInfoTwo->m_windowSpaceInfo.m_xyz[2]) / 2.0f }; coordinate->setXYZ(centerXYZ); setWidthHeightWithWindowCoordsFlag = true; @@ -772,47 +747,33 @@ AnnotationCoordinateInformation::setTwoDimAnnotationCoordinatesForSpace(Annotati } if (setWidthHeightWithTabCoordsFlag) { - if (coordInfoOne->m_tabIndex >= 0) { + if (coordInfoOne->m_tabSpaceInfo.m_index >= 0) { if (optionalCoordInfoTwo != NULL) { - if (coordInfoOne->m_tabIndex == optionalCoordInfoTwo->m_tabIndex) { - const float tabWidth = coordInfoOne->m_tabWidth; - const float tabHeight = coordInfoOne->m_tabHeight; - - const float oneXYZ[3] = { - coordInfoOne->m_tabXYZ[0], - coordInfoOne->m_tabXYZ[1], - coordInfoOne->m_tabXYZ[2] - }; - const float twoXYZ[3] = { - optionalCoordInfoTwo->m_tabXYZ[0], - optionalCoordInfoTwo->m_tabXYZ[1], - optionalCoordInfoTwo->m_tabXYZ[2] - }; - - annotation->setWidthAndHeightFromBounds(oneXYZ, - twoXYZ, - tabWidth, - tabHeight); + if (coordInfoOne->m_tabSpaceInfo.m_index == optionalCoordInfoTwo->m_tabSpaceInfo.m_index) { + annotation->setWidthAndHeightFromBounds(coordInfoOne->m_tabSpaceInfo.m_xyz, + optionalCoordInfoTwo->m_tabSpaceInfo.m_xyz, + coordInfoOne->m_tabSpaceInfo.m_width, + coordInfoOne->m_tabSpaceInfo.m_height); } } } } else if (setWidthHeightWithWindowCoordsFlag) { - if (coordInfoOne->m_windowIndex >= 0) { + if (coordInfoOne->m_windowSpaceInfo.m_index >= 0) { if (optionalCoordInfoTwo != NULL) { - if (coordInfoOne->m_windowIndex == optionalCoordInfoTwo->m_windowIndex) { - const float windowWidth = coordInfoOne->m_windowWidth; - const float windowHeight = coordInfoOne->m_windowHeight; + if (coordInfoOne->m_windowSpaceInfo.m_index == optionalCoordInfoTwo->m_windowSpaceInfo.m_index) { + const float windowWidth = coordInfoOne->m_windowSpaceInfo.m_width; + const float windowHeight = coordInfoOne->m_windowSpaceInfo.m_height; const float oneXYZ[3] = { - coordInfoOne->m_windowXYZ[0], - coordInfoOne->m_windowXYZ[1], - coordInfoOne->m_windowXYZ[2] + coordInfoOne->m_windowSpaceInfo.m_xyz[0], + coordInfoOne->m_windowSpaceInfo.m_xyz[1], + coordInfoOne->m_windowSpaceInfo.m_xyz[2] }; const float twoXYZ[3] = { - optionalCoordInfoTwo->m_windowXYZ[0], - optionalCoordInfoTwo->m_windowXYZ[1], - optionalCoordInfoTwo->m_windowXYZ[2] + optionalCoordInfoTwo->m_windowSpaceInfo.m_xyz[0], + optionalCoordInfoTwo->m_windowSpaceInfo.m_xyz[1], + optionalCoordInfoTwo->m_windowSpaceInfo.m_xyz[2] }; annotation->setWidthAndHeightFromBounds(oneXYZ, diff --git a/src/GuiQt/AnnotationCoordinateInformation.h b/src/GuiQt/AnnotationCoordinateInformation.h index b951fa170..18782cda3 100644 --- a/src/GuiQt/AnnotationCoordinateInformation.h +++ b/src/GuiQt/AnnotationCoordinateInformation.h @@ -70,31 +70,51 @@ namespace caret { const AnnotationCoordinateInformation* coordInfoOne, const AnnotationCoordinateInformation* coordInfoTwo); - double m_modelXYZ[3]; - bool m_modelXYZValid; - - float m_tabWidth; - float m_tabHeight; - float m_tabXYZ[3]; - float m_tabPixelXYZ[3]; - int32_t m_tabIndex; - - float m_windowWidth; - float m_windowHeight; - float m_windowXYZ[3]; - float m_windowPixelXYZ[3]; - int32_t m_windowIndex; - - StructureEnum::Enum m_surfaceStructure; - int32_t m_surfaceNumberOfNodes; - int32_t m_surfaceNodeIndex; - float m_surfaceNodeOffset; - AnnotationSurfaceOffsetVectorTypeEnum::Enum m_surfaceNodeVector; - bool m_surfaceNodeValid; - - - float m_chartXYZ[3]; - bool m_chartXYZValid; + class SpaceInfo { + public: + bool m_validFlag = false; + }; + + class ModelSpaceInfo : public SpaceInfo { + public: + double m_xyz[3] = { 0.0, 0.0, 0.0 }; + }; + + class TabWindowSpaceInfo : public SpaceInfo { + public: + float m_width = 0.0f; + float m_height = 0.0f; + float m_xyz[3] = { 0.0f, 0.0f, 0.0f }; + float m_pixelXYZ[3] = { 0.0f, 0.0f, 0.0f }; + int32_t m_index = -1; + + }; + + class ChartSpaceInfo : public SpaceInfo { + public: + float m_xyz[3] = { 0.0f, 0.0f, 0.0f }; + }; + + class SurfaceSpaceInfo : public SpaceInfo { + public: + StructureEnum::Enum m_structure = StructureEnum::INVALID; + int32_t m_numberOfNodes = 0; + int32_t m_nodeIndex = -1; + float m_nodeOffsetLength = 0.0f; + float m_nodeNormalVector[3] = { 0.0f, 0.0f, 1.0f }; + AnnotationSurfaceOffsetVectorTypeEnum::Enum m_nodeVectorOffsetType = AnnotationSurfaceOffsetVectorTypeEnum::CENTROID_THRU_VERTEX; + }; + + ModelSpaceInfo m_modelSpaceInfo; + + TabWindowSpaceInfo m_tabSpaceInfo; + + TabWindowSpaceInfo m_windowSpaceInfo; + + ChartSpaceInfo m_chartSpaceInfo; + + SurfaceSpaceInfo m_surfaceSpaceInfo; + private: AnnotationCoordinateInformation(const AnnotationCoordinateInformation&); diff --git a/src/GuiQt/AnnotationCoordinateSelectionWidget.cxx b/src/GuiQt/AnnotationCoordinateSelectionWidget.cxx index 589698e4f..d4883287e 100644 --- a/src/GuiQt/AnnotationCoordinateSelectionWidget.cxx +++ b/src/GuiQt/AnnotationCoordinateSelectionWidget.cxx @@ -165,22 +165,22 @@ m_optionalSecondCoordInfo(optionalSecondCoordInfo) } if (enableTabSpaceFlag) { - if (m_coordInfo.m_tabIndex < 0) { + if (m_coordInfo.m_tabSpaceInfo.m_index < 0) { enableTabSpaceFlag = false; } if (m_optionalSecondCoordInfo != NULL) { - if (m_optionalSecondCoordInfo->m_tabIndex < 0) { + if (m_optionalSecondCoordInfo->m_tabSpaceInfo.m_index < 0) { enableTabSpaceFlag = false; } } } if (enableChartSpaceFlag) { - if ( ! m_coordInfo.m_chartXYZValid) { + if ( ! m_coordInfo.m_chartSpaceInfo.m_validFlag) { enableChartSpaceFlag = false; } if (m_optionalSecondCoordInfo != NULL) { - if ( ! m_optionalSecondCoordInfo->m_chartXYZValid) { + if ( ! m_optionalSecondCoordInfo->m_chartSpaceInfo.m_validFlag) { enableChartSpaceFlag = false; } /* @@ -202,35 +202,35 @@ m_optionalSecondCoordInfo(optionalSecondCoordInfo) const int rowNum = gridLayout->rowCount(); gridLayout->addWidget(rb, rowNum, COLUMN_RADIO_BUTTON); - gridLayout->addWidget(new QLabel(AString::number(m_coordInfo.m_chartXYZ[0], 'f', 1)), + gridLayout->addWidget(new QLabel(AString::number(m_coordInfo.m_chartSpaceInfo.m_xyz[0], 'f', 1)), rowNum, COLUMN_COORD_X, Qt::AlignRight); - gridLayout->addWidget(new QLabel(AString::number(m_coordInfo.m_chartXYZ[1], 'f', 1)), + gridLayout->addWidget(new QLabel(AString::number(m_coordInfo.m_chartSpaceInfo.m_xyz[1], 'f', 1)), rowNum, COLUMN_COORD_Y, Qt::AlignRight); - gridLayout->addWidget(new QLabel(AString::number(m_coordInfo.m_chartXYZ[2], 'f', 1)), + gridLayout->addWidget(new QLabel(AString::number(m_coordInfo.m_chartSpaceInfo.m_xyz[2], 'f', 1)), rowNum, COLUMN_COORD_Z, Qt::AlignRight); if (m_optionalSecondCoordInfo != NULL) { - gridLayout->addWidget(new QLabel(AString::number(m_optionalSecondCoordInfo->m_chartXYZ[0], 'f', 1)), + gridLayout->addWidget(new QLabel(AString::number(m_optionalSecondCoordInfo->m_chartSpaceInfo.m_xyz[0], 'f', 1)), rowNum, COLUMN_COORD_TWO_X, Qt::AlignRight); - gridLayout->addWidget(new QLabel(AString::number(m_optionalSecondCoordInfo->m_chartXYZ[1], 'f', 1)), + gridLayout->addWidget(new QLabel(AString::number(m_optionalSecondCoordInfo->m_chartSpaceInfo.m_xyz[1], 'f', 1)), rowNum, COLUMN_COORD_TWO_Y, Qt::AlignRight); - gridLayout->addWidget(new QLabel(AString::number(m_optionalSecondCoordInfo->m_chartXYZ[2], 'f', 1)), + gridLayout->addWidget(new QLabel(AString::number(m_optionalSecondCoordInfo->m_chartSpaceInfo.m_xyz[2], 'f', 1)), rowNum, COLUMN_COORD_TWO_Z, Qt::AlignRight); } } if (enableModelSpaceFlag) { - if ( ! m_coordInfo.m_modelXYZValid) { + if ( ! m_coordInfo.m_modelSpaceInfo.m_validFlag) { enableModelSpaceFlag = false; } if (m_optionalSecondCoordInfo != NULL) { - if ( ! m_optionalSecondCoordInfo->m_modelXYZValid) { + if ( ! m_optionalSecondCoordInfo->m_modelSpaceInfo.m_validFlag) { enableModelSpaceFlag = false; } /* @@ -251,24 +251,24 @@ m_optionalSecondCoordInfo(optionalSecondCoordInfo) const int rowNum = gridLayout->rowCount(); gridLayout->addWidget(rb, rowNum, COLUMN_RADIO_BUTTON); - gridLayout->addWidget(new QLabel(AString::number(m_coordInfo.m_modelXYZ[0], 'f', 1)), + gridLayout->addWidget(new QLabel(AString::number(m_coordInfo.m_modelSpaceInfo.m_xyz[0], 'f', 1)), rowNum, COLUMN_COORD_X, Qt::AlignRight); - gridLayout->addWidget(new QLabel(AString::number(m_coordInfo.m_modelXYZ[1], 'f', 1)), + gridLayout->addWidget(new QLabel(AString::number(m_coordInfo.m_modelSpaceInfo.m_xyz[1], 'f', 1)), rowNum, COLUMN_COORD_Y, Qt::AlignRight); - gridLayout->addWidget(new QLabel(AString::number(m_coordInfo.m_modelXYZ[2], 'f', 1)), + gridLayout->addWidget(new QLabel(AString::number(m_coordInfo.m_modelSpaceInfo.m_xyz[2], 'f', 1)), rowNum, COLUMN_COORD_Z, Qt::AlignRight); if (m_optionalSecondCoordInfo != NULL) { - gridLayout->addWidget(new QLabel(AString::number(m_optionalSecondCoordInfo->m_modelXYZ[0], 'f', 1)), + gridLayout->addWidget(new QLabel(AString::number(m_optionalSecondCoordInfo->m_modelSpaceInfo.m_xyz[0], 'f', 1)), rowNum, COLUMN_COORD_TWO_X, Qt::AlignRight); - gridLayout->addWidget(new QLabel(AString::number(m_optionalSecondCoordInfo->m_modelXYZ[1], 'f', 1)), + gridLayout->addWidget(new QLabel(AString::number(m_optionalSecondCoordInfo->m_modelSpaceInfo.m_xyz[1], 'f', 1)), rowNum, COLUMN_COORD_TWO_Y, Qt::AlignRight); - gridLayout->addWidget(new QLabel(AString::number(m_optionalSecondCoordInfo->m_modelXYZ[2], 'f', 1)), + gridLayout->addWidget(new QLabel(AString::number(m_optionalSecondCoordInfo->m_modelSpaceInfo.m_xyz[2], 'f', 1)), rowNum, COLUMN_COORD_TWO_Z, Qt::AlignRight); } @@ -278,42 +278,42 @@ m_optionalSecondCoordInfo(optionalSecondCoordInfo) QRadioButton* rb = createRadioButtonForSpace(AnnotationCoordinateSpaceEnum::TAB); rb->setText(rb->text() + " " - + AString::number(m_coordInfo.m_tabIndex + 1)); + + AString::number(m_coordInfo.m_tabSpaceInfo.m_index + 1)); m_spaceButtonGroup->addButton(rb, AnnotationCoordinateSpaceEnum::toIntegerCode(AnnotationCoordinateSpaceEnum::TAB)); const int rowNum = gridLayout->rowCount(); gridLayout->addWidget(rb, rowNum, COLUMN_RADIO_BUTTON); - gridLayout->addWidget(new QLabel(AString::number(m_coordInfo.m_tabXYZ[0], 'f', 1)), + gridLayout->addWidget(new QLabel(AString::number(m_coordInfo.m_tabSpaceInfo.m_xyz[0], 'f', 1)), rowNum, COLUMN_COORD_X, Qt::AlignRight); - gridLayout->addWidget(new QLabel(AString::number(m_coordInfo.m_tabXYZ[1], 'f', 1)), + gridLayout->addWidget(new QLabel(AString::number(m_coordInfo.m_tabSpaceInfo.m_xyz[1], 'f', 1)), rowNum, COLUMN_COORD_Y, Qt::AlignRight); - gridLayout->addWidget(new QLabel(AString::number(m_coordInfo.m_tabXYZ[2], 'f', 1)), + gridLayout->addWidget(new QLabel(AString::number(m_coordInfo.m_tabSpaceInfo.m_xyz[2], 'f', 1)), rowNum, COLUMN_COORD_Z, Qt::AlignRight); if (m_optionalSecondCoordInfo != NULL) { - gridLayout->addWidget(new QLabel(AString::number(m_optionalSecondCoordInfo->m_tabXYZ[0], 'f', 1)), + gridLayout->addWidget(new QLabel(AString::number(m_optionalSecondCoordInfo->m_tabSpaceInfo.m_xyz[0], 'f', 1)), rowNum, COLUMN_COORD_TWO_X, Qt::AlignRight); - gridLayout->addWidget(new QLabel(AString::number(m_optionalSecondCoordInfo->m_tabXYZ[1], 'f', 1)), + gridLayout->addWidget(new QLabel(AString::number(m_optionalSecondCoordInfo->m_tabSpaceInfo.m_xyz[1], 'f', 1)), rowNum, COLUMN_COORD_TWO_Y, Qt::AlignRight); - gridLayout->addWidget(new QLabel(AString::number(m_optionalSecondCoordInfo->m_tabXYZ[2], 'f', 1)), + gridLayout->addWidget(new QLabel(AString::number(m_optionalSecondCoordInfo->m_tabSpaceInfo.m_xyz[2], 'f', 1)), rowNum, COLUMN_COORD_TWO_Z, Qt::AlignRight); } } if (enableWindowSpaceFlag) { - if (m_coordInfo.m_windowIndex < 0) { + if (m_coordInfo.m_windowSpaceInfo.m_index < 0) { enableWindowSpaceFlag = false; } if (m_optionalSecondCoordInfo != NULL) { - if (m_optionalSecondCoordInfo->m_windowIndex < 0) { + if (m_optionalSecondCoordInfo->m_windowSpaceInfo.m_index < 0) { enableWindowSpaceFlag = false; } } @@ -322,7 +322,7 @@ m_optionalSecondCoordInfo(optionalSecondCoordInfo) QRadioButton* rb = createRadioButtonForSpace(AnnotationCoordinateSpaceEnum::WINDOW); rb->setText(rb->text() + " " - + AString::number(m_coordInfo.m_windowIndex + 1)); + + AString::number(m_coordInfo.m_windowSpaceInfo.m_index + 1)); m_spaceButtonGroup->addButton(rb, AnnotationCoordinateSpaceEnum::toIntegerCode(AnnotationCoordinateSpaceEnum::WINDOW)); @@ -330,35 +330,35 @@ m_optionalSecondCoordInfo(optionalSecondCoordInfo) const int rowNum = gridLayout->rowCount(); gridLayout->addWidget(rb, rowNum, COLUMN_RADIO_BUTTON); - gridLayout->addWidget(new QLabel(AString::number(m_coordInfo.m_windowXYZ[0], 'f', 1)), + gridLayout->addWidget(new QLabel(AString::number(m_coordInfo.m_windowSpaceInfo.m_xyz[0], 'f', 1)), rowNum, COLUMN_COORD_X, Qt::AlignRight); - gridLayout->addWidget(new QLabel(AString::number(m_coordInfo.m_windowXYZ[1], 'f', 1)), + gridLayout->addWidget(new QLabel(AString::number(m_coordInfo.m_windowSpaceInfo.m_xyz[1], 'f', 1)), rowNum, COLUMN_COORD_Y, Qt::AlignRight); - gridLayout->addWidget(new QLabel(AString::number(m_coordInfo.m_windowXYZ[2], 'f', 1)), + gridLayout->addWidget(new QLabel(AString::number(m_coordInfo.m_windowSpaceInfo.m_xyz[2], 'f', 1)), rowNum, COLUMN_COORD_Z, Qt::AlignRight); if (m_optionalSecondCoordInfo != NULL) { - gridLayout->addWidget(new QLabel(AString::number(m_optionalSecondCoordInfo->m_windowXYZ[0], 'f', 1)), + gridLayout->addWidget(new QLabel(AString::number(m_optionalSecondCoordInfo->m_windowSpaceInfo.m_xyz[0], 'f', 1)), rowNum, COLUMN_COORD_TWO_X, Qt::AlignRight); - gridLayout->addWidget(new QLabel(AString::number(m_optionalSecondCoordInfo->m_windowXYZ[1], 'f', 1)), + gridLayout->addWidget(new QLabel(AString::number(m_optionalSecondCoordInfo->m_windowSpaceInfo.m_xyz[1], 'f', 1)), rowNum, COLUMN_COORD_TWO_Y, Qt::AlignRight); - gridLayout->addWidget(new QLabel(AString::number(m_optionalSecondCoordInfo->m_windowXYZ[2], 'f', 1)), + gridLayout->addWidget(new QLabel(AString::number(m_optionalSecondCoordInfo->m_windowSpaceInfo.m_xyz[2], 'f', 1)), rowNum, COLUMN_COORD_TWO_Z, Qt::AlignRight); } } if (enableSurfaceSpaceFlag) { - if ( ! m_coordInfo.m_surfaceNodeValid) { + if ( ! m_coordInfo.m_surfaceSpaceInfo.m_validFlag) { enableSurfaceSpaceFlag = false; } if (m_optionalSecondCoordInfo != NULL) { - if ( ! m_optionalSecondCoordInfo->m_surfaceNodeValid) { + if ( ! m_optionalSecondCoordInfo->m_surfaceSpaceInfo.m_validFlag) { enableSurfaceSpaceFlag = false; } } @@ -381,17 +381,17 @@ m_optionalSecondCoordInfo(optionalSecondCoordInfo) const int rowNum = gridLayout->rowCount(); gridLayout->addWidget(rb, rowNum, COLUMN_RADIO_BUTTON); - const AString infoText(StructureEnum::toGuiName(m_coordInfo.m_surfaceStructure) + const AString infoText(StructureEnum::toGuiName(m_coordInfo.m_surfaceSpaceInfo.m_structure) + " Vertex: " - +AString::number(m_coordInfo.m_surfaceNodeIndex)); + +AString::number(m_coordInfo.m_surfaceSpaceInfo.m_nodeIndex)); gridLayout->addWidget(new QLabel(infoText), rowNum, COLUMN_COORD_X, 1, 4); if (m_optionalSecondCoordInfo != NULL) { const int rowNum = gridLayout->rowCount(); - const AString infoText(StructureEnum::toGuiName(m_optionalSecondCoordInfo->m_surfaceStructure) + const AString infoText(StructureEnum::toGuiName(m_optionalSecondCoordInfo->m_surfaceSpaceInfo.m_structure) + " Vertex 2: " - +AString::number(m_optionalSecondCoordInfo->m_surfaceNodeIndex)); + +AString::number(m_optionalSecondCoordInfo->m_surfaceSpaceInfo.m_nodeIndex)); gridLayout->addWidget(new QLabel(infoText), rowNum, COLUMN_COORD_X, 1, 4); } @@ -551,22 +551,22 @@ AnnotationCoordinateSelectionWidget::changeAnnotationCoordinate(Annotation* anno float oldViewportHeight = 0.0; switch (oldSpace) { case AnnotationCoordinateSpaceEnum::CHART: - oldViewportHeight = m_coordInfo.m_tabHeight; + oldViewportHeight = m_coordInfo.m_tabSpaceInfo.m_height; break; case AnnotationCoordinateSpaceEnum::STEREOTAXIC: - oldViewportHeight = m_coordInfo.m_tabHeight; + oldViewportHeight = m_coordInfo.m_tabSpaceInfo.m_height; break; case AnnotationCoordinateSpaceEnum::SURFACE: - oldViewportHeight = m_coordInfo.m_tabHeight; + oldViewportHeight = m_coordInfo.m_tabSpaceInfo.m_height; break; case AnnotationCoordinateSpaceEnum::TAB: - oldViewportHeight = m_coordInfo.m_tabHeight; + oldViewportHeight = m_coordInfo.m_tabSpaceInfo.m_height; break; case AnnotationCoordinateSpaceEnum::VIEWPORT: CaretAssert(0); break; case AnnotationCoordinateSpaceEnum::WINDOW: - oldViewportHeight = m_coordInfo.m_windowHeight; + oldViewportHeight = m_coordInfo.m_windowSpaceInfo.m_height; break; } @@ -614,33 +614,33 @@ AnnotationCoordinateSelectionWidget::changeAnnotationCoordinate(Annotation* anno bool setOtherCoordinateFlag = false; switch (newSpace) { case AnnotationCoordinateSpaceEnum::CHART: - if (m_coordInfo.m_chartXYZValid) { - coordinate->setXYZ(m_coordInfo.m_chartXYZ); + if (m_coordInfo.m_chartSpaceInfo.m_validFlag) { + coordinate->setXYZ(m_coordInfo.m_chartSpaceInfo.m_xyz); redoAnnotation->setCoordinateSpace(AnnotationCoordinateSpaceEnum::CHART); - newViewportHeight = m_coordInfo.m_tabHeight; + newViewportHeight = m_coordInfo.m_tabSpaceInfo.m_height; } break; case AnnotationCoordinateSpaceEnum::STEREOTAXIC: - if (m_coordInfo.m_modelXYZValid) { - coordinate->setXYZ(m_coordInfo.m_modelXYZ); + if (m_coordInfo.m_modelSpaceInfo.m_validFlag) { + coordinate->setXYZ(m_coordInfo.m_modelSpaceInfo.m_xyz); redoAnnotation->setCoordinateSpace(AnnotationCoordinateSpaceEnum::STEREOTAXIC); - newViewportHeight = m_coordInfo.m_tabHeight; + newViewportHeight = m_coordInfo.m_tabSpaceInfo.m_height; } break; case AnnotationCoordinateSpaceEnum::SURFACE: - if (m_coordInfo.m_surfaceNodeValid) { - coordinate->setSurfaceSpace(m_coordInfo.m_surfaceStructure, - m_coordInfo.m_surfaceNumberOfNodes, - m_coordInfo.m_surfaceNodeIndex); + if (m_coordInfo.m_surfaceSpaceInfo.m_validFlag) { + coordinate->setSurfaceSpace(m_coordInfo.m_surfaceSpaceInfo.m_structure, + m_coordInfo.m_surfaceSpaceInfo.m_numberOfNodes, + m_coordInfo.m_surfaceSpaceInfo.m_nodeIndex); redoAnnotation->setCoordinateSpace(AnnotationCoordinateSpaceEnum::SURFACE); - newViewportHeight = m_coordInfo.m_tabHeight; + newViewportHeight = m_coordInfo.m_tabSpaceInfo.m_height; } break; case AnnotationCoordinateSpaceEnum::TAB: - if (m_coordInfo.m_tabIndex >= 0) { + if (m_coordInfo.m_tabSpaceInfo.m_index >= 0) { const int32_t oldTabIndex = redoAnnotation->getTabIndex(); - const int32_t newTabIndex = m_coordInfo.m_tabIndex; - coordinate->setXYZ(m_coordInfo.m_tabXYZ); + const int32_t newTabIndex = m_coordInfo.m_tabSpaceInfo.m_index; + coordinate->setXYZ(m_coordInfo.m_tabSpaceInfo.m_xyz); redoAnnotation->setCoordinateSpace(AnnotationCoordinateSpaceEnum::TAB); redoAnnotation->setTabIndex(newTabIndex); @@ -654,17 +654,17 @@ AnnotationCoordinateSelectionWidget::changeAnnotationCoordinate(Annotation* anno setOtherCoordinateFlag = true; } } - newViewportHeight = m_coordInfo.m_tabHeight; + newViewportHeight = m_coordInfo.m_tabSpaceInfo.m_height; } break; case AnnotationCoordinateSpaceEnum::VIEWPORT: CaretAssert(0); break; case AnnotationCoordinateSpaceEnum::WINDOW: - if (m_coordInfo.m_windowIndex >= 0) { + if (m_coordInfo.m_windowSpaceInfo.m_index >= 0) { const int32_t oldWindowIndex = redoAnnotation->getWindowIndex(); - const int32_t newWindowIndex = m_coordInfo.m_windowIndex; - coordinate->setXYZ(m_coordInfo.m_windowXYZ); + const int32_t newWindowIndex = m_coordInfo.m_windowSpaceInfo.m_index; + coordinate->setXYZ(m_coordInfo.m_windowSpaceInfo.m_xyz); redoAnnotation->setCoordinateSpace(AnnotationCoordinateSpaceEnum::WINDOW); redoAnnotation->setWindowIndex(newWindowIndex); @@ -679,7 +679,7 @@ AnnotationCoordinateSelectionWidget::changeAnnotationCoordinate(Annotation* anno setOtherCoordinateFlag = true; } } - newViewportHeight = m_coordInfo.m_windowHeight; + newViewportHeight = m_coordInfo.m_windowSpaceInfo.m_height; } break; } @@ -783,14 +783,12 @@ AnnotationCoordinateSelectionWidget::setCoordinateForNewAnnotation(Annotation* a coordinateSpace, &m_coordInfo, m_optionalSecondCoordInfo); -// setOneDimAnnotationCoordinates(oneDimAnn); } else if (twoDimAnn != NULL) { validCoordsFlag = AnnotationCoordinateInformation::setAnnotationCoordinatesForSpace(twoDimAnn, coordinateSpace, &m_coordInfo, m_optionalSecondCoordInfo); -// setTwoDimAnnotationCoordinates(twoDimAnn); } else { const QString msg("PROGRAM ERROR: Annotation is neither one nor two dimensional"); @@ -829,27 +827,27 @@ AnnotationCoordinateSelectionWidget::setWidthAndHeightForImage(AnnotationImage* float vpHeight = 0.0; switch (imageAnn->getCoordinateSpace()) { case AnnotationCoordinateSpaceEnum::CHART: - vpWidth = m_coordInfo.m_tabWidth; - vpHeight = m_coordInfo.m_tabHeight; + vpWidth = m_coordInfo.m_tabSpaceInfo.m_width; + vpHeight = m_coordInfo.m_tabSpaceInfo.m_height; break; case AnnotationCoordinateSpaceEnum::STEREOTAXIC: - vpWidth = m_coordInfo.m_tabWidth; - vpHeight = m_coordInfo.m_tabHeight; + vpWidth = m_coordInfo.m_tabSpaceInfo.m_width; + vpHeight = m_coordInfo.m_tabSpaceInfo.m_height; break; case AnnotationCoordinateSpaceEnum::SURFACE: - vpWidth = m_coordInfo.m_tabWidth; - vpHeight = m_coordInfo.m_tabHeight; + vpWidth = m_coordInfo.m_tabSpaceInfo.m_width; + vpHeight = m_coordInfo.m_tabSpaceInfo.m_height; break; case AnnotationCoordinateSpaceEnum::TAB: - vpWidth = m_coordInfo.m_tabWidth; - vpHeight = m_coordInfo.m_tabHeight; + vpWidth = m_coordInfo.m_tabSpaceInfo.m_width; + vpHeight = m_coordInfo.m_tabSpaceInfo.m_height; break; case AnnotationCoordinateSpaceEnum::VIEWPORT: CaretAssert(0); break; case AnnotationCoordinateSpaceEnum::WINDOW: - vpWidth = m_coordInfo.m_windowWidth; - vpHeight = m_coordInfo.m_windowHeight; + vpWidth = m_coordInfo.m_windowSpaceInfo.m_width; + vpHeight = m_coordInfo.m_windowSpaceInfo.m_height; break; } @@ -904,11 +902,11 @@ AnnotationCoordinateSelectionWidget::updateAnnotationDisplayProperties(const Ann case AnnotationCoordinateSpaceEnum::CHART: break; case AnnotationCoordinateSpaceEnum::STEREOTAXIC: - if (m_coordInfo.m_tabIndex >= 0) { + if (m_coordInfo.m_tabSpaceInfo.m_index >= 0) { } break; case AnnotationCoordinateSpaceEnum::SURFACE: - if (m_coordInfo.m_tabIndex >= 0) { + if (m_coordInfo.m_tabSpaceInfo.m_index >= 0) { } break; case AnnotationCoordinateSpaceEnum::TAB: diff --git a/src/GuiQt/AnnotationCreateDialog.cxx b/src/GuiQt/AnnotationCreateDialog.cxx index 42636d045..51fc2ec2f 100644 --- a/src/GuiQt/AnnotationCreateDialog.cxx +++ b/src/GuiQt/AnnotationCreateDialog.cxx @@ -388,7 +388,7 @@ AnnotationCreateDialog::createAnnotation(NewAnnotationInfo& newAnnotationInfo, finishAnnotationCreation(newAnnotationInfo.m_annotationFile, newAnnotation, newAnnotationInfo.m_mouseEvent.getBrowserWindowIndex(), - newAnnotationInfo.m_coordOneInfo.m_tabIndex); + newAnnotationInfo.m_coordOneInfo.m_tabSpaceInfo.m_index); return newAnnotation; } @@ -794,8 +794,6 @@ AnnotationCreateDialog::finishAnnotationCreation(AnnotationFile* annotationFile, * A new chart annotation is displayed only in the tab in which it was created */ if (annotation->getCoordinateSpace() == AnnotationCoordinateSpaceEnum::CHART) { - - //annotation->setItemDisplaySelectedInNoDisplayGroups(); annotation->setItemDisplaySelectedInOneTab(tabIndex); annotation->setItemDisplaySelected(DisplayGroupEnum::DISPLAY_GROUP_TAB, tabIndex, @@ -804,66 +802,6 @@ AnnotationCreateDialog::finishAnnotationCreation(AnnotationFile* annotationFile, } -///** -// * Constructor for information used to create a new annotation. -// * -// * @param mouseEvent -// * The mouse event. -// * @param selectedSpace -// * The space selected by the user. -// * @param annotationType -// * The annotation type. -// * @param useBothCoordinatesFromMouseFlag -// * Use both coords (X/Y and pressed X/Y) -// * @param annotationFile -// * File to which annotation is added. -// */ -//AnnotationCreateDialog::NewAnnotationInfo::NewAnnotationInfo(const MouseEvent& mouseEvent, -// const AnnotationCoordinateSpaceEnum::Enum selectedSpace, -// const AnnotationTypeEnum::Enum annotationType, -// const bool useBothCoordinatesFromMouseFlag, -// AnnotationFile* annotationFile) -//: m_mouseEvent(mouseEvent), -//m_selectedSpace(selectedSpace), -//m_annotationType(annotationType), -//m_annotationFile(annotationFile) -//{ -// CaretAssert(annotationFile); -// -// m_validSpaces.clear(); -// m_coordOneInfo.reset(); -// m_coordTwoInfo.reset(); -// m_coordTwoInfoValid = false; -// m_percentageWidth = -1; -// m_percentageHeight = -1; -// -// AnnotationCoordinateInformation::createCoordinateInformationFromXY(mouseEvent, -// mouseEvent.getX(), -// mouseEvent.getY(), -// m_coordOneInfo); -// -// if (useBothCoordinatesFromMouseFlag) { -// AnnotationCoordinateInformation::createCoordinateInformationFromXY(mouseEvent, -// mouseEvent.getPressedX(), -// mouseEvent.getPressedY(), -// m_coordTwoInfo); -// -// AnnotationCoordinateInformation::getValidCoordinateSpaces(&m_coordOneInfo, -// &m_coordTwoInfo, -// m_validSpaces); -// -// if (isValid()) { -// m_coordTwoInfoValid = true; -// -// processTwoCoordInfo(); -// } -// } -// else { -// AnnotationCoordinateInformation::getValidCoordinateSpaces(&m_coordOneInfo, -// NULL, -// m_validSpaces); -// } -//} /** * Constructor for information used to create a new annotation. * @@ -964,8 +902,8 @@ m_annotationFile(annotationFile) void AnnotationCreateDialog::NewAnnotationInfo::processTwoCoordInfo() { - if ((m_coordOneInfo.m_windowIndex >= 0) - && (m_coordTwoInfo.m_windowIndex >= 0)) { + if ((m_coordOneInfo.m_windowSpaceInfo.m_index >= 0) + && (m_coordTwoInfo.m_windowSpaceInfo.m_index >= 0)) { bool useAverageFlag = false; bool useTextAligmentFlag = false; @@ -992,10 +930,10 @@ AnnotationCreateDialog::NewAnnotationInfo::processTwoCoordInfo() if (useAverageFlag || useTextAligmentFlag) { - int32_t windowPixelX = m_coordOneInfo.m_windowPixelXYZ[0]; - int32_t windowPixelY = m_coordOneInfo.m_windowPixelXYZ[1]; - int32_t windowTwoPixelX = m_coordTwoInfo.m_windowPixelXYZ[0]; - int32_t windowTwoPixelY = m_coordTwoInfo.m_windowPixelXYZ[1]; + int32_t windowPixelX = m_coordOneInfo.m_windowSpaceInfo.m_pixelXYZ[0]; + int32_t windowPixelY = m_coordOneInfo.m_windowSpaceInfo.m_pixelXYZ[1]; + int32_t windowTwoPixelX = m_coordTwoInfo.m_windowSpaceInfo.m_pixelXYZ[0]; + int32_t windowTwoPixelY = m_coordTwoInfo.m_windowSpaceInfo.m_pixelXYZ[1]; if ((windowPixelX >= 0) && (windowPixelY >= 0) @@ -1033,9 +971,6 @@ AnnotationCreateDialog::NewAnnotationInfo::processTwoCoordInfo() viewport, subWidth, subHeight)) { -// std::cout << "Changing " -// << viewportWidth << ", " << viewportHeight << " to " -// << subWidth << ", " << subHeight << std::endl; viewportWidth = subWidth; viewportHeight = subHeight; diff --git a/src/GuiQt/AnnotationPasteDialog.cxx b/src/GuiQt/AnnotationPasteDialog.cxx index 7ada21c49..ffe390c1a 100644 --- a/src/GuiQt/AnnotationPasteDialog.cxx +++ b/src/GuiQt/AnnotationPasteDialog.cxx @@ -178,14 +178,13 @@ AnnotationPasteDialog::pasteAnnotationOnClipboardChangeSpace(const MouseEvent& m AnnotationManager* annotationManager = GuiManager::get()->getBrain()->getAnnotationManager(); if (annotationManager->isAnnotationOnClipboardValid()) { AnnotationFile* annotationFile = annotationManager->getAnnotationFileOnClipboard(); - //Annotation* annotation = annotationManager->getAnnotationOnClipboard()->clone(); AString message("Choose one of the coordinate " "spaces below to paste the annotation or press Cancel to cancel pasting " "of the annotation."); AnnotationPasteDialog pasteDialog(mouseEvent, annotationFile, - annotationManager->getAnnotationOnClipboard(), //annotation, + annotationManager->getAnnotationOnClipboard(), message, mouseEvent.getOpenGLWidget()); if (pasteDialog.exec() == AnnotationPasteDialog::Accepted) { @@ -327,19 +326,19 @@ AnnotationPasteDialog::pasteOneDimensionalShape(AnnotationOneDimensionalShape* o }; if (tabFlag - && (coordInfo.m_tabIndex >= 0)) { - startXYZ[0] = coordInfo.m_tabXYZ[0]; - startXYZ[1] = coordInfo.m_tabXYZ[1]; - startXYZ[2] = coordInfo.m_tabXYZ[2]; - oneDimShape->setTabIndex(coordInfo.m_tabIndex); + && (coordInfo.m_tabSpaceInfo.m_index >= 0)) { + startXYZ[0] = coordInfo.m_tabSpaceInfo.m_xyz[0]; + startXYZ[1] = coordInfo.m_tabSpaceInfo.m_xyz[1]; + startXYZ[2] = coordInfo.m_tabSpaceInfo.m_xyz[2]; + oneDimShape->setTabIndex(coordInfo.m_tabSpaceInfo.m_index); validCoordsFlag = true; } else if (windowFlag - && (coordInfo.m_windowIndex >= 0)) { - startXYZ[0] = coordInfo.m_windowXYZ[0]; - startXYZ[1] = coordInfo.m_windowXYZ[1]; - startXYZ[2] = coordInfo.m_windowXYZ[2]; - oneDimShape->setWindowIndex(coordInfo.m_windowIndex); + && (coordInfo.m_windowSpaceInfo.m_index >= 0)) { + startXYZ[0] = coordInfo.m_windowSpaceInfo.m_xyz[0]; + startXYZ[1] = coordInfo.m_windowSpaceInfo.m_xyz[1]; + startXYZ[2] = coordInfo.m_windowSpaceInfo.m_xyz[2]; + oneDimShape->setWindowIndex(coordInfo.m_windowSpaceInfo.m_index); validCoordsFlag = true; } @@ -516,23 +515,6 @@ AnnotationPasteDialog::adjustTextAnnotationFontHeight(const AnnotationCoordinate heightMultiplier = 1.0 / surfaceMontageRowCount; } } - -// if (previousSpace != AnnotationCoordinateSpaceEnum::SURFACE) { -// if (annotation->getCoordinateSpace() == AnnotationCoordinateSpaceEnum::SURFACE) { -// /* -// * Converting to surface -// */ -// heightMultiplier = surfaceMontageRowCount; -// } -// } -// else { -// if (annotation->getCoordinateSpace() != AnnotationCoordinateSpaceEnum::SURFACE) { -// /* -// * Converting from surface -// */ -// heightMultiplier = 1.0 / surfaceMontageRowCount; -// } -// } if (heightMultiplier != 0.0) { AnnotationPercentSizeText* textAnn = dynamic_cast(annotation); diff --git a/src/GuiQt/UserInputModeAnnotations.cxx b/src/GuiQt/UserInputModeAnnotations.cxx index 768259f72..02953ba50 100644 --- a/src/GuiQt/UserInputModeAnnotations.cxx +++ b/src/GuiQt/UserInputModeAnnotations.cxx @@ -766,22 +766,22 @@ UserInputModeAnnotations::mouseLeftDrag(const MouseEvent& mouseEvent) dx, dy, mouseEvent.isFirstDragging()); - if (coordInfo.m_surfaceNodeValid) { - annSpatialMod.setSurfaceCoordinateAtMouseXY(coordInfo.m_surfaceStructure, - coordInfo.m_surfaceNumberOfNodes, - coordInfo.m_surfaceNodeIndex); + if (coordInfo.m_surfaceSpaceInfo.m_validFlag) { + annSpatialMod.setSurfaceCoordinateAtMouseXY(coordInfo.m_surfaceSpaceInfo.m_structure, + coordInfo.m_surfaceSpaceInfo.m_numberOfNodes, + coordInfo.m_surfaceSpaceInfo.m_nodeIndex); } - if (coordInfo.m_modelXYZValid) { - annSpatialMod.setStereotaxicCoordinateAtMouseXY(coordInfo.m_modelXYZ[0], - coordInfo.m_modelXYZ[1], - coordInfo.m_modelXYZ[2]); + if (coordInfo.m_modelSpaceInfo.m_validFlag) { + annSpatialMod.setStereotaxicCoordinateAtMouseXY(coordInfo.m_modelSpaceInfo.m_xyz[0], + coordInfo.m_modelSpaceInfo.m_xyz[1], + coordInfo.m_modelSpaceInfo.m_xyz[2]); } - if (coordInfo.m_chartXYZValid) { - annSpatialMod.setChartCoordinateAtMouseXY(coordInfo.m_chartXYZ[0], - coordInfo.m_chartXYZ[1], - coordInfo.m_chartXYZ[2]); + if (coordInfo.m_chartSpaceInfo.m_validFlag) { + annSpatialMod.setChartCoordinateAtMouseXY(coordInfo.m_chartSpaceInfo.m_xyz[0], + coordInfo.m_chartSpaceInfo.m_xyz[1], + coordInfo.m_chartSpaceInfo.m_xyz[2]); } if ((dx != 0.0) @@ -792,10 +792,10 @@ UserInputModeAnnotations::mouseLeftDrag(const MouseEvent& mouseEvent) mouseEvent.getX() - dx, mouseEvent.getY() - dy, previousMouseXYCoordInfo); - if (previousMouseXYCoordInfo.m_chartXYZValid) { - annSpatialMod.setChartCoordinateAtPreviousMouseXY(previousMouseXYCoordInfo.m_chartXYZ[0], - previousMouseXYCoordInfo.m_chartXYZ[1], - previousMouseXYCoordInfo.m_chartXYZ[2]); + if (previousMouseXYCoordInfo.m_chartSpaceInfo.m_validFlag) { + annSpatialMod.setChartCoordinateAtPreviousMouseXY(previousMouseXYCoordInfo.m_chartSpaceInfo.m_xyz[0], + previousMouseXYCoordInfo.m_chartSpaceInfo.m_xyz[1], + previousMouseXYCoordInfo.m_chartSpaceInfo.m_xyz[2]); } } @@ -1228,10 +1228,10 @@ UserInputModeAnnotations::processModeSetCoordinate(const MouseEvent& mouseEvent) int32_t numNodes = -1; int32_t nodeIndex = -1; float surfaceOffset = 0.0; - AnnotationSurfaceOffsetVectorTypeEnum::Enum surfaceVector = AnnotationSurfaceOffsetVectorTypeEnum::CENTROID_THRU_VERTEX; - coordinate->getSurfaceSpace(structure, numNodes, nodeIndex, surfaceOffset, surfaceVector); - coordInfo.m_surfaceNodeOffset = surfaceOffset; - coordInfo.m_surfaceNodeVector = surfaceVector; + AnnotationSurfaceOffsetVectorTypeEnum::Enum surfaceVectorType = AnnotationSurfaceOffsetVectorTypeEnum::CENTROID_THRU_VERTEX; + coordinate->getSurfaceSpace(structure, numNodes, nodeIndex, surfaceOffset, surfaceVectorType); + coordInfo.m_surfaceSpaceInfo.m_nodeOffsetLength = surfaceOffset; + coordInfo.m_surfaceSpaceInfo.m_nodeVectorOffsetType = surfaceVectorType; } AnnotationChangeCoordinateDialog changeCoordDialog(coordInfo, -- GitLab From 912d5eee9060d6e916e5e9f3811175b53ed5f65c Mon Sep 17 00:00:00 2001 From: John Harwell Date: Mon, 10 Sep 2018 14:35:37 -0500 Subject: [PATCH 040/427] Improved initialization of the rotation angle for annotations created in surface space with tangent offset. --- src/Annotations/Annotation.cxx | 136 ++++++++++++++++++ src/Annotations/Annotation.h | 4 + src/Annotations/AnnotationCoordinate.cxx | 2 +- src/Annotations/AnnotationCoordinate.h | 2 +- ...inOpenGLAnnotationDrawingFixedPipeline.cxx | 43 +----- ...rainOpenGLAnnotationDrawingFixedPipeline.h | 2 - src/Common/MathFunctions.cxx | 32 +++++ src/Common/MathFunctions.h | 2 + src/GuiQt/AnnotationCoordinateInformation.cxx | 3 +- 9 files changed, 181 insertions(+), 45 deletions(-) diff --git a/src/Annotations/Annotation.cxx b/src/Annotations/Annotation.cxx index e2ac520a1..9be72f5bd 100644 --- a/src/Annotations/Annotation.cxx +++ b/src/Annotations/Annotation.cxx @@ -25,6 +25,7 @@ #include "AnnotationBox.h" #include "AnnotationColorBar.h" +#include "AnnotationCoordinate.h" #include "AnnotationGroup.h" #include "AnnotationImage.h" #include "AnnotationLine.h" @@ -37,6 +38,7 @@ #include "CaretLogger.h" #include "DisplayGroupAndTabItemHelper.h" #include "MathFunctions.h" +#include "Matrix4x4.h" #include "SceneClass.h" #include "SceneClassAssistant.h" @@ -700,6 +702,140 @@ Annotation::isInSurfaceSpaceWithTangentOffset() const return flag; } +/** + * Initialize the rotation for an annotation in surface space with tangent + * offset using the given normal vector from the vertex to which the + * annotation is attached. + * + * @param structure + * The surface structure. + * @param vertexNormal + * Normal vector of surface vertex. + */ +void +Annotation::initializeSurfaceSpaceWithTangentOffsetRotation(const StructureEnum::Enum structure, + const float vertexNormal[3]) +{ + AnnotationTwoDimensionalShape* twoDimAnn = dynamic_cast(this); + if (twoDimAnn != NULL) { + if (isInSurfaceSpaceWithTangentOffset()) { + enum class OrientationType { + LEFT_TO_RIGHT = 0, + RIGHT_TO_LEFT = 1, + POSTERIOR_TO_ANTERIOR = 2, + ANTERIOR_TO_POSTERIOR = 3, + INFERIOR_TO_SUPERIOR = 4, + SUPERIOR_TO_INFERIOR = 5 + }; + + const OrientationType orientations[6] = { + OrientationType::LEFT_TO_RIGHT, + OrientationType::RIGHT_TO_LEFT, + OrientationType::POSTERIOR_TO_ANTERIOR, + OrientationType::ANTERIOR_TO_POSTERIOR, + OrientationType::INFERIOR_TO_SUPERIOR, + OrientationType::SUPERIOR_TO_INFERIOR + }; + const float orientationVectors[6][3] { + { 1.0, 0.0, 0.0 }, + { -1.0, 0.0, 0.0 }, + { 0.0, 1.0, 0.0 }, + { 0.0, -1.0, 0.0 }, + { 0.0, 0.0, 1.0 }, + { 0.0, 0.0, -1.0 } + }; + + + /* + * Find orientation that aligns with the vertex's normal vector + */ + OrientationType matchingOrientation = OrientationType::LEFT_TO_RIGHT; + float matchingAngle = 10000.0f; + for (int32_t i = 0; i < 6; i++) { + const float angle = MathFunctions::angleInDegreesBetweenVectors(orientationVectors[i], + vertexNormal); + if (angle < matchingAngle) { + matchingAngle = angle; + matchingOrientation = orientations[i]; + } + } + + float surfaceUpAxisVector[3] = { 0.0f, 0.0f, 1.0f }; + switch (matchingOrientation) { + case OrientationType::LEFT_TO_RIGHT: + break; + case OrientationType::RIGHT_TO_LEFT: + break; + case OrientationType::POSTERIOR_TO_ANTERIOR: + break; + case OrientationType::ANTERIOR_TO_POSTERIOR: + break; + case OrientationType::INFERIOR_TO_SUPERIOR: + surfaceUpAxisVector[0] = 1.0; + surfaceUpAxisVector[0] = 0.0; + surfaceUpAxisVector[0] = 0.0; + break; + case OrientationType::SUPERIOR_TO_INFERIOR: + surfaceUpAxisVector[0] = -1.0; + surfaceUpAxisVector[0] = 0.0; + surfaceUpAxisVector[0] = 0.0; + break; + } + + /* + * Vector for annotation's Y (vector from bottom to top of annotation) + */ + const float annotationUpYVector[3] { + 0.0, + 1.0, + 0.0 + }; + + /* + * Initialize the rotation angle so that the annotation's vertical axis + * is aligned with the screen vertical axis when the surface is in the + * analogous surface view. For a text annotation, the text should be + * flowing left to right across screen. + */ + Matrix4x4 rotationMatrix; + rotationMatrix.setMatrixToOpenGLRotationFromVector(vertexNormal); + Matrix4x4 inverseMatrix(rotationMatrix); + inverseMatrix.invert(); + inverseMatrix.multiplyPoint3(surfaceUpAxisVector); + const float alignRotationAngle = MathFunctions::angleInDegreesBetweenVectors(annotationUpYVector, + surfaceUpAxisVector); + float rotationAngle = alignRotationAngle; + switch (matchingOrientation) { + case OrientationType::LEFT_TO_RIGHT: + rotationAngle = 360.0 - rotationAngle; + break; + case OrientationType::RIGHT_TO_LEFT: + break; + case OrientationType::POSTERIOR_TO_ANTERIOR: + if (StructureEnum::isRight(structure)) { + rotationAngle = 360.0 - rotationAngle; + } + break; + case OrientationType::ANTERIOR_TO_POSTERIOR: + if (StructureEnum::isRight(structure)) { + rotationAngle = 360.0 - rotationAngle; + } + break; + case OrientationType::INFERIOR_TO_SUPERIOR: + if (StructureEnum::isRight(structure)) { + rotationAngle += 180.0; + } + break; + case OrientationType::SUPERIOR_TO_INFERIOR: + rotationAngle += 90.0; + break; + } + + twoDimAnn->setRotationAngle(rotationAngle); + } + } +} + /** * @return The tab index. Valid only for tab coordinate space annotations. */ diff --git a/src/Annotations/Annotation.h b/src/Annotations/Annotation.h index 7988bb72e..6d0c0a556 100644 --- a/src/Annotations/Annotation.h +++ b/src/Annotations/Annotation.h @@ -33,6 +33,7 @@ #include "CaretObjectTracksModification.h" #include "DisplayGroupAndTabItemInterface.h" #include "SceneableInterface.h" +#include "StructureEnum.h" namespace caret { @@ -160,6 +161,9 @@ namespace caret { bool isInSurfaceSpaceWithTangentOffset() const; + void initializeSurfaceSpaceWithTangentOffsetRotation(const StructureEnum::Enum structure, + const float vertexNormal[3]); + int32_t getTabIndex() const; void setTabIndex(const int32_t tabIndex); diff --git a/src/Annotations/AnnotationCoordinate.cxx b/src/Annotations/AnnotationCoordinate.cxx index ecbd7c684..65a1bc16a 100644 --- a/src/Annotations/AnnotationCoordinate.cxx +++ b/src/Annotations/AnnotationCoordinate.cxx @@ -125,7 +125,7 @@ AnnotationCoordinate::initializeAnnotationCoordinateMembers() switch (m_attributeDefaultType) { case AnnotationAttributesDefaultTypeEnum::NORMAL: m_surfaceOffsetLength = getDefaultSurfaceOffsetLength(); - m_surfaceOffsetVectorType = AnnotationSurfaceOffsetVectorTypeEnum::CENTROID_THRU_VERTEX; + m_surfaceOffsetVectorType = AnnotationSurfaceOffsetVectorTypeEnum::TANGENT; break; case AnnotationAttributesDefaultTypeEnum::USER: m_surfaceOffsetLength = s_userDefaultSurfaceOffsetLength; diff --git a/src/Annotations/AnnotationCoordinate.h b/src/Annotations/AnnotationCoordinate.h index 51a4a9702..744c11506 100644 --- a/src/Annotations/AnnotationCoordinate.h +++ b/src/Annotations/AnnotationCoordinate.h @@ -154,7 +154,7 @@ namespace caret { #ifdef __ANNOTATION_COORDINATE_DECLARE__ float AnnotationCoordinate::s_userDefaultSurfaceOffsetLength = 5.0f; - AnnotationSurfaceOffsetVectorTypeEnum::Enum AnnotationCoordinate::s_userDefaultSurfaceOffsetVectorType = AnnotationSurfaceOffsetVectorTypeEnum::CENTROID_THRU_VERTEX; + AnnotationSurfaceOffsetVectorTypeEnum::Enum AnnotationCoordinate::s_userDefaultSurfaceOffsetVectorType = AnnotationSurfaceOffsetVectorTypeEnum::TANGENT; #endif // __ANNOTATION_COORDINATE_DECLARE__ } // namespace diff --git a/src/Brain/BrainOpenGLAnnotationDrawingFixedPipeline.cxx b/src/Brain/BrainOpenGLAnnotationDrawingFixedPipeline.cxx index faf1bfa3a..9036a6939 100644 --- a/src/Brain/BrainOpenGLAnnotationDrawingFixedPipeline.cxx +++ b/src/Brain/BrainOpenGLAnnotationDrawingFixedPipeline.cxx @@ -1311,16 +1311,11 @@ BrainOpenGLAnnotationDrawingFixedPipeline::drawTwoDimAnnotationSurfaceTextureOff * Rotate the text so that the horzontal flow of the text * is orthogonal to the 'text up orientation vector'. */ - // const float angle = angleInDegreesBetweenVectors(localUpXYZ, - // textUpOrientationVectorXYZ); - const float orientationUpAngle = angleInDegreesBetweenVectors(localUpXYZ, - vectorSurfaceAxisZ); - glRotatef(orientationUpAngle, 0.0, 0.0, -1.0); - - + const float orientationUpAngle = MathFunctions::angleInDegreesBetweenVectors(localUpXYZ, + vectorSurfaceAxisZ); if (debugFlag) { - //std::cout << "Annotation: " << text->getText() << std::endl; + std::cout << "Annotation: " << annotation->toString() << std::endl; std::cout << " Plane: " << textDrawingPlane.toString() << std::endl; std::cout << " Local Up Vector: " << AString::fromNumbers(localUpXYZ, 3, ", ") << std::endl; std::cout << " Angle: " << orientationUpAngle << std::endl; @@ -2984,38 +2979,6 @@ BrainOpenGLAnnotationDrawingFixedPipeline::clipLineAtTextBox(const float bottomL } } -/** - * @return The angle formed by two vectors. - * cos = (u . v) / (||u|| * ||v||) - * - * @param u - * First vector. - * @param v - * Second vector. - */ -double -BrainOpenGLAnnotationDrawingFixedPipeline::angleInDegreesBetweenVectors(const float u[3], const float v[3]) const -{ - double angle = 0.0; - - const double numerator = MathFunctions::dotProduct(u, v); - const double uLength = MathFunctions::vectorLength(u); - const double vLength = MathFunctions::vectorLength(v); - const double denominator = uLength * vLength; - if (denominator > 0.0) { - double a = numerator / denominator; - if (a > 1.0) { - a = 1.0; - } - else if (a < -1.0) { - a = -1.0; - } - const double angleRadians = std::acos(a); - angle = MathFunctions::toDegrees(angleRadians); - } - return angle; -} - /** * Get the normal vector for a surface vector. * diff --git a/src/Brain/BrainOpenGLAnnotationDrawingFixedPipeline.h b/src/Brain/BrainOpenGLAnnotationDrawingFixedPipeline.h index 3214241e8..3d3e391b4 100644 --- a/src/Brain/BrainOpenGLAnnotationDrawingFixedPipeline.h +++ b/src/Brain/BrainOpenGLAnnotationDrawingFixedPipeline.h @@ -350,8 +350,6 @@ namespace caret { float estimateColorBarWidth(const AnnotationColorBar* colorBar, const float textHeightInPixels) const; - double angleInDegreesBetweenVectors(const float u[3], const float v[3]) const; - void getSurfaceNormalVector(const Surface* surfaceDisplayed, const int32_t vertexIndex, float normalVectorOut[3]) const; diff --git a/src/Common/MathFunctions.cxx b/src/Common/MathFunctions.cxx index 8d8a0d436..a2bdc652a 100644 --- a/src/Common/MathFunctions.cxx +++ b/src/Common/MathFunctions.cxx @@ -1973,6 +1973,38 @@ MathFunctions::signedAngle( return phi; } +/** + * @return The angle formed by two vectors. + * cos = (u . v) / (||u|| * ||v||) + * + * @param u + * First vector. + * @param v + * Second vector. + */ +float +MathFunctions::angleInDegreesBetweenVectors(const float u[3], const float v[3]) +{ + float angle = 0.0; + + const float numerator = MathFunctions::dotProduct(u, v); + const float uLength = MathFunctions::vectorLength(u); + const float vLength = MathFunctions::vectorLength(v); + const float denominator = uLength * vLength; + if (denominator > 0.0) { + float a = numerator / denominator; + if (a > 1.0) { + a = 1.0; + } + else if (a < -1.0) { + a = -1.0; + } + const float angleRadians = std::acos(a); + angle = MathFunctions::toDegrees(angleRadians); + } + return angle; +} + /** * Determine if an integer is an odd number. * @param number Integer to test. diff --git a/src/Common/MathFunctions.h b/src/Common/MathFunctions.h index 16917e608..d2300b39f 100644 --- a/src/Common/MathFunctions.h +++ b/src/Common/MathFunctions.h @@ -333,6 +333,8 @@ public: const float pk[3], const float n[3]); + static float angleInDegreesBetweenVectors(const float u[3], const float v[3]); + static bool isOddNumber(const int32_t number); static bool isEvenNumber(const int32_t number); diff --git a/src/GuiQt/AnnotationCoordinateInformation.cxx b/src/GuiQt/AnnotationCoordinateInformation.cxx index 75e4be85b..ba8bbf869 100644 --- a/src/GuiQt/AnnotationCoordinateInformation.cxx +++ b/src/GuiQt/AnnotationCoordinateInformation.cxx @@ -659,7 +659,8 @@ AnnotationCoordinateInformation::setTwoDimAnnotationCoordinatesForSpace(Annotati coordInfoOne->m_surfaceSpaceInfo.m_numberOfNodes, coordInfoOne->m_surfaceSpaceInfo.m_nodeIndex); annotation->setCoordinateSpace(AnnotationCoordinateSpaceEnum::SURFACE); - + annotation->initializeSurfaceSpaceWithTangentOffsetRotation(coordInfoOne->m_surfaceSpaceInfo.m_structure, + coordInfoOne->m_surfaceSpaceInfo.m_nodeNormalVector); validCoordinateFlag = true; if (optionalCoordInfoTwo != NULL) { -- GitLab From a224cdedda783e3578eeba54ef63f427635b52c0 Mon Sep 17 00:00:00 2001 From: John Harwell Date: Tue, 11 Sep 2018 09:43:03 -0500 Subject: [PATCH 041/427] WB-636 Texture Based Annotations: Change default offset length for surface annotations to 1.0. Changed step value to 0.1 in spin box for surface space annotations. --- src/Annotations/AnnotationCoordinate.h | 2 +- src/GuiQt/AnnotationCoordinateWidget.cxx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Annotations/AnnotationCoordinate.h b/src/Annotations/AnnotationCoordinate.h index 744c11506..d62121b85 100644 --- a/src/Annotations/AnnotationCoordinate.h +++ b/src/Annotations/AnnotationCoordinate.h @@ -153,7 +153,7 @@ namespace caret { }; #ifdef __ANNOTATION_COORDINATE_DECLARE__ - float AnnotationCoordinate::s_userDefaultSurfaceOffsetLength = 5.0f; + float AnnotationCoordinate::s_userDefaultSurfaceOffsetLength = 1.0f; AnnotationSurfaceOffsetVectorTypeEnum::Enum AnnotationCoordinate::s_userDefaultSurfaceOffsetVectorType = AnnotationSurfaceOffsetVectorTypeEnum::TANGENT; #endif // __ANNOTATION_COORDINATE_DECLARE__ diff --git a/src/GuiQt/AnnotationCoordinateWidget.cxx b/src/GuiQt/AnnotationCoordinateWidget.cxx index 78bc3c8be..b5f9aed03 100644 --- a/src/GuiQt/AnnotationCoordinateWidget.cxx +++ b/src/GuiQt/AnnotationCoordinateWidget.cxx @@ -118,7 +118,7 @@ m_browserWindowIndex(browserWindowIndex) m_surfaceOffsetLengthSpinBox = new QDoubleSpinBox(); m_surfaceOffsetLengthSpinBox->setRange(0.0, 999.0); - m_surfaceOffsetLengthSpinBox->setSingleStep(1.0); + m_surfaceOffsetLengthSpinBox->setSingleStep(0.1); m_surfaceOffsetLengthSpinBox->setToolTip("Offset of annotation from surface vertex"); QObject::connect(m_surfaceOffsetLengthSpinBox, SIGNAL(valueChanged(double)), this, SLOT(surfaceOffsetLengthValueChanged(double))); -- GitLab From f5c3f1262ed3ca075a9a08a40aac78a24b81f429 Mon Sep 17 00:00:00 2001 From: John Harwell Date: Tue, 11 Sep 2018 14:36:45 -0500 Subject: [PATCH 042/427] WB-636 Texture Based Annotations: Add option on Replace All Scenes Dialog that allows one to change all surface space annotations to use a Tangent offset. --- src/Annotations/Annotation.cxx | 45 +++++++++ src/Annotations/Annotation.h | 12 +++ .../AnnotationOneDimensionalShape.cxx | 36 +++++++ .../AnnotationOneDimensionalShape.h | 8 ++ .../AnnotationTwoDimensionalShape.cxx | 36 +++++++ .../AnnotationTwoDimensionalShape.h | 8 ++ src/GuiQt/CMakeLists.txt | 3 + src/GuiQt/SceneDialog.cxx | 20 +++- src/GuiQt/SceneReplaceAllDialog.cxx | 93 +++++++++++++++++++ src/GuiQt/SceneReplaceAllDialog.h | 67 +++++++++++++ 10 files changed, 325 insertions(+), 3 deletions(-) create mode 100644 src/GuiQt/SceneReplaceAllDialog.cxx create mode 100644 src/GuiQt/SceneReplaceAllDialog.h diff --git a/src/Annotations/Annotation.cxx b/src/Annotations/Annotation.cxx index 9be72f5bd..6551400b4 100644 --- a/src/Annotations/Annotation.cxx +++ b/src/Annotations/Annotation.cxx @@ -702,6 +702,51 @@ Annotation::isInSurfaceSpaceWithTangentOffset() const return flag; } +/** + * Change a surface space annotation to TANGENT offset and update offset length. + * If the annotation is already TANGENT space, no changes are made. + */ +void +Annotation::changeSurfaceSpaceToTangentOffset() +{ + std::vector coords; + if (getCoordinateSpace() == AnnotationCoordinateSpaceEnum::SURFACE) { + AnnotationOneDimensionalShape* oneDimAnn = castToOneDimensionalShape(); + if (oneDimAnn != NULL) { + coords.push_back(oneDimAnn->getStartCoordinate()); + coords.push_back(oneDimAnn->getEndCoordinate()); + } + else { + AnnotationTwoDimensionalShape* twoDimAnn = castToTwoDimensionalShape(); + if (twoDimAnn != NULL) { + coords.push_back(twoDimAnn->getCoordinate()); + } + } + + for (auto c : coords) { + StructureEnum::Enum structure; + int32_t surfaceNumberOfNodes; + int32_t surfaceNodeIndex; + float surfaceOffsetLength; + AnnotationSurfaceOffsetVectorTypeEnum::Enum surfaceOffsetVectorType; + c->getSurfaceSpace(structure, + surfaceNumberOfNodes, + surfaceNodeIndex, + surfaceOffsetLength, + surfaceOffsetVectorType); + if (surfaceOffsetVectorType != AnnotationSurfaceOffsetVectorTypeEnum::TANGENT) { + surfaceOffsetVectorType = AnnotationSurfaceOffsetVectorTypeEnum::TANGENT; + surfaceOffsetLength = 1.0; + c->setSurfaceSpace(structure, + surfaceNumberOfNodes, + surfaceNodeIndex, + surfaceOffsetLength, + surfaceOffsetVectorType); + } + } + } +} + /** * Initialize the rotation for an annotation in surface space with tangent * offset using the given normal vector from the vertex to which the diff --git a/src/Annotations/Annotation.h b/src/Annotations/Annotation.h index 6d0c0a556..ccfa16a24 100644 --- a/src/Annotations/Annotation.h +++ b/src/Annotations/Annotation.h @@ -37,6 +37,8 @@ namespace caret { + class AnnotationOneDimensionalShape; + class AnnotationTwoDimensionalShape; class AnnotationSpatialModification; class DisplayGroupAndTabItemHelper; class SceneClassAssistant; @@ -121,6 +123,14 @@ namespace caret { Annotation* clone() const; + virtual AnnotationOneDimensionalShape* castToOneDimensionalShape() = 0; + + virtual const AnnotationOneDimensionalShape* castToOneDimensionalShape() const = 0; + + virtual AnnotationTwoDimensionalShape* castToTwoDimensionalShape() = 0; + + virtual const AnnotationTwoDimensionalShape* castToTwoDimensionalShape() const = 0; + bool testProperty(const Property property) const; bool testPropertiesAny(const Property propertyOne, @@ -161,6 +171,8 @@ namespace caret { bool isInSurfaceSpaceWithTangentOffset() const; + void changeSurfaceSpaceToTangentOffset(); + void initializeSurfaceSpaceWithTangentOffsetRotation(const StructureEnum::Enum structure, const float vertexNormal[3]); diff --git a/src/Annotations/AnnotationOneDimensionalShape.cxx b/src/Annotations/AnnotationOneDimensionalShape.cxx index f89bbe499..871284e26 100644 --- a/src/Annotations/AnnotationOneDimensionalShape.cxx +++ b/src/Annotations/AnnotationOneDimensionalShape.cxx @@ -126,6 +126,42 @@ AnnotationOneDimensionalShape::initializeMembersAnnotationOneDimensionalShape() } } +/** + * @return 'this' as a one-dimensional shape. NULL if this is not a one-dimensional shape. + */ +AnnotationOneDimensionalShape* +AnnotationOneDimensionalShape::castToOneDimensionalShape() +{ + return this; +} + +/** + * @return 'this' as a one-dimensional shape. NULL if this is not a one-dimensional shape. + */ +const AnnotationOneDimensionalShape* +AnnotationOneDimensionalShape::castToOneDimensionalShape() const +{ + return this; +} + +/** + * @return 'this' as a one-dimensional shape. NULL if this is not a two-dimensional shape. + */ +AnnotationTwoDimensionalShape* +AnnotationOneDimensionalShape::castToTwoDimensionalShape() +{ + return NULL; +} + +/** + * @return 'this' as a one-dimensional shape. NULL if this is not a two-dimensional shape. + */ +const AnnotationTwoDimensionalShape* +AnnotationOneDimensionalShape::castToTwoDimensionalShape() const +{ + return NULL; +} + /** * @return The start coordinate for the one dimensional shape. */ diff --git a/src/Annotations/AnnotationOneDimensionalShape.h b/src/Annotations/AnnotationOneDimensionalShape.h index 3d30304bd..e1f7ff879 100644 --- a/src/Annotations/AnnotationOneDimensionalShape.h +++ b/src/Annotations/AnnotationOneDimensionalShape.h @@ -41,6 +41,14 @@ namespace caret { AnnotationOneDimensionalShape& operator=(const AnnotationOneDimensionalShape& obj); + virtual AnnotationOneDimensionalShape* castToOneDimensionalShape() override; + + virtual const AnnotationOneDimensionalShape* castToOneDimensionalShape() const override; + + virtual AnnotationTwoDimensionalShape* castToTwoDimensionalShape() override; + + virtual const AnnotationTwoDimensionalShape* castToTwoDimensionalShape() const override; + AnnotationCoordinate* getStartCoordinate(); const AnnotationCoordinate* getStartCoordinate() const; diff --git a/src/Annotations/AnnotationTwoDimensionalShape.cxx b/src/Annotations/AnnotationTwoDimensionalShape.cxx index caf6a72bd..2abe0ff46 100644 --- a/src/Annotations/AnnotationTwoDimensionalShape.cxx +++ b/src/Annotations/AnnotationTwoDimensionalShape.cxx @@ -151,6 +151,42 @@ AnnotationTwoDimensionalShape::initializeMembersAnnotationTwoDimensionalShape() } } +/** + * @return 'this' as a one-dimensional shape. NULL if this is not a one-dimensional shape. + */ +AnnotationOneDimensionalShape* +AnnotationTwoDimensionalShape::castToOneDimensionalShape() +{ + return NULL; +} + +/** + * @return 'this' as a one-dimensional shape. NULL if this is not a one-dimensional shape. + */ +const AnnotationOneDimensionalShape* +AnnotationTwoDimensionalShape::castToOneDimensionalShape() const +{ + return NULL; +} + +/** + * @return 'this' as a one-dimensional shape. NULL if this is not a two-dimensional shape. + */ +AnnotationTwoDimensionalShape* +AnnotationTwoDimensionalShape::castToTwoDimensionalShape() +{ + return this; +} + +/** + * @return 'this' as a one-dimensional shape. NULL if this is not a two-dimensional shape. + */ +const AnnotationTwoDimensionalShape* +AnnotationTwoDimensionalShape::castToTwoDimensionalShape() const +{ + return this; +} + /** * @return The coordinate for the two dimensional shape. */ diff --git a/src/Annotations/AnnotationTwoDimensionalShape.h b/src/Annotations/AnnotationTwoDimensionalShape.h index 07e87519e..b57f2f871 100644 --- a/src/Annotations/AnnotationTwoDimensionalShape.h +++ b/src/Annotations/AnnotationTwoDimensionalShape.h @@ -42,6 +42,14 @@ namespace caret { AnnotationTwoDimensionalShape& operator=(const AnnotationTwoDimensionalShape& obj); + virtual AnnotationOneDimensionalShape* castToOneDimensionalShape() override; + + virtual const AnnotationOneDimensionalShape* castToOneDimensionalShape() const override; + + virtual AnnotationTwoDimensionalShape* castToTwoDimensionalShape() override; + + virtual const AnnotationTwoDimensionalShape* castToTwoDimensionalShape() const override; + AnnotationCoordinate* getCoordinate(); const AnnotationCoordinate* getCoordinate() const; diff --git a/src/GuiQt/CMakeLists.txt b/src/GuiQt/CMakeLists.txt index d1fb497fc..ac5daf6f9 100644 --- a/src/GuiQt/CMakeLists.txt +++ b/src/GuiQt/CMakeLists.txt @@ -166,6 +166,7 @@ ELSE() SceneCreateReplaceDialog.h SceneDialog.h ScenePreviewDialog.h + SceneReplaceAllDialog.h SceneShowOptionsDialog.h SpecFileManagementDialog.h SplashScreen.h @@ -397,6 +398,7 @@ SceneBasePathWidget.h SceneCreateReplaceDialog.h SceneDialog.h ScenePreviewDialog.h +SceneReplaceAllDialog.h SceneShowOptionsDialog.h SceneWindowGeometry.h SpecFileManagementDialog.h @@ -624,6 +626,7 @@ SceneBasePathWidget.cxx SceneCreateReplaceDialog.cxx SceneDialog.cxx ScenePreviewDialog.cxx +SceneReplaceAllDialog.cxx SceneShowOptionsDialog.cxx SceneWindowGeometry.cxx SpecFileManagementDialog.cxx diff --git a/src/GuiQt/SceneDialog.cxx b/src/GuiQt/SceneDialog.cxx index f66826937..7d40efd89 100644 --- a/src/GuiQt/SceneDialog.cxx +++ b/src/GuiQt/SceneDialog.cxx @@ -44,6 +44,8 @@ #include "SceneDialog.h" #undef __SCENE_DIALOG_DECLARE__ +#include "Annotation.h" +#include "AnnotationManager.h" #include "ApplicationInformation.h" #include "BalsaDatabaseUploadSceneFileDialog.h" #include "Brain.h" @@ -79,6 +81,7 @@ #include "SceneFile.h" #include "SceneInfo.h" #include "ScenePreviewDialog.h" +#include "SceneReplaceAllDialog.h" #include "SceneShowOptionsDialog.h" #include "SessionManager.h" #include "UsernamePasswordWidget.h" @@ -1222,9 +1225,9 @@ SceneDialog::replaceAllScenesPushButtonClicked() return; } - if ( ! WuQMessageBox::warningOkCancel(m_replaceAllScenesPushButton, - "Replace All Scenes", - m_replaceAllScenesDescription)) { + SceneReplaceAllDialog replaceDialog(m_replaceAllScenesDescription, + m_replaceAllScenesPushButton); + if (replaceDialog.exec() == SceneReplaceAllDialog::Rejected) { return; } @@ -1339,6 +1342,17 @@ SceneDialog::replaceAllScenesPushButtonClicked() newScene->setDescription(origScene->getDescription()); newScene->setBalsaSceneID(origScene->getBalsaSceneID()); + /* + * Process options + */ + if (replaceDialog.isChangeSurfaceAnnotationOffsetToOffset()) { + AnnotationManager* annMan = GuiManager::get()->getBrain()->getAnnotationManager(); + std::vector annotations = annMan->getAllAnnotations(); + for (auto ann : annotations) { + ann->changeSurfaceSpaceToTangentOffset(); + } + } + const std::vector windowIndices = GuiManager::get()->getAllOpenBrainBrowserWindowIndices(); /* diff --git a/src/GuiQt/SceneReplaceAllDialog.cxx b/src/GuiQt/SceneReplaceAllDialog.cxx new file mode 100644 index 000000000..b852eb3e7 --- /dev/null +++ b/src/GuiQt/SceneReplaceAllDialog.cxx @@ -0,0 +1,93 @@ + +/*LICENSE_START*/ +/* + * Copyright (C) 2018 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + +#define __SCENE_REPLACE_ALL_DIALOG_DECLARE__ +#include "SceneReplaceAllDialog.h" +#undef __SCENE_REPLACE_ALL_DIALOG_DECLARE__ + +#include +#include +#include +#include + +#include "CaretAssert.h" +using namespace caret; + + + +/** + * \class caret::SceneReplaceAllDialog + * \brief Dialog for replacing scenes + * \ingroup GuiQt + */ + +/** + * Constructor. + */ +SceneReplaceAllDialog::SceneReplaceAllDialog(const AString& replaceDescription, + QWidget* parent) +: WuQDialogModal("Replace All Scenes", + parent) +{ + QLabel* slowLabel = new QLabel(replaceDescription); + slowLabel->setWordWrap(true); + + m_changeSurfaceAnntotationOffsetCheckBox = new QCheckBox("Change offset of all surface annotations to TANGENT"); + + QGroupBox* optionsGroupBox = new QGroupBox("Options"); + QVBoxLayout* optionsLayout = new QVBoxLayout(optionsGroupBox); + optionsLayout->addWidget(m_changeSurfaceAnntotationOffsetCheckBox); + + QWidget* widget = new QWidget(); + QVBoxLayout* layout = new QVBoxLayout(widget); + layout->addWidget(slowLabel); + layout->addWidget(optionsGroupBox); + + setCentralWidget(widget, + WuQDialogModal::SCROLL_AREA_NEVER); +} + +/** + * Destructor. + */ +SceneReplaceAllDialog::~SceneReplaceAllDialog() +{ +} + +/** + * Is true if the offset for all surface annotations should be changed + * to TANGENT. + */ +bool +SceneReplaceAllDialog::isChangeSurfaceAnnotationOffsetToOffset() const +{ + return m_changeSurfaceAnntotationOffsetCheckBox->isChecked(); +} + +/** + * Called when OK button is clicked. + */ +void +SceneReplaceAllDialog::okButtonClicked() +{ + WuQDialogModal::okButtonClicked(); +} + diff --git a/src/GuiQt/SceneReplaceAllDialog.h b/src/GuiQt/SceneReplaceAllDialog.h new file mode 100644 index 000000000..bbecfd1d4 --- /dev/null +++ b/src/GuiQt/SceneReplaceAllDialog.h @@ -0,0 +1,67 @@ +#ifndef __SCENE_REPLACE_ALL_DIALOG_H__ +#define __SCENE_REPLACE_ALL_DIALOG_H__ + +/*LICENSE_START*/ +/* + * Copyright (C) 2018 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + + + +#include + +#include "WuQDialogModal.h" + +class QCheckBox; + +namespace caret { + + class SceneReplaceAllDialog : public WuQDialogModal { + + Q_OBJECT + + public: + SceneReplaceAllDialog(const AString& replaceDescription, + QWidget* parent = 0); + + virtual ~SceneReplaceAllDialog(); + + SceneReplaceAllDialog(const SceneReplaceAllDialog&) = delete; + + SceneReplaceAllDialog& operator=(const SceneReplaceAllDialog&) = delete; + + bool isChangeSurfaceAnnotationOffsetToOffset() const; + + protected: + virtual void okButtonClicked(); + + // ADD_NEW_METHODS_HERE + + private: + QCheckBox* m_changeSurfaceAnntotationOffsetCheckBox; + + // ADD_NEW_MEMBERS_HERE + + }; + +#ifdef __SCENE_REPLACE_ALL_DIALOG_DECLARE__ + // +#endif // __SCENE_REPLACE_ALL_DIALOG_DECLARE__ + +} // namespace +#endif //__SCENE_REPLACE_ALL_DIALOG_H__ -- GitLab From 0ad0eb085c12662b04bae2eebdc6a637b36b45f9 Mon Sep 17 00:00:00 2001 From: John Harwell Date: Wed, 12 Sep 2018 10:47:03 -0500 Subject: [PATCH 043/427] Fixed compilation warnings for unused variables --- ...inOpenGLAnnotationDrawingFixedPipeline.cxx | 20 ------------------- ...rainOpenGLAnnotationDrawingFixedPipeline.h | 4 ---- 2 files changed, 24 deletions(-) diff --git a/src/Brain/BrainOpenGLAnnotationDrawingFixedPipeline.cxx b/src/Brain/BrainOpenGLAnnotationDrawingFixedPipeline.cxx index 9036a6939..2f555b9ec 100644 --- a/src/Brain/BrainOpenGLAnnotationDrawingFixedPipeline.cxx +++ b/src/Brain/BrainOpenGLAnnotationDrawingFixedPipeline.cxx @@ -1332,7 +1332,6 @@ BrainOpenGLAnnotationDrawingFixedPipeline::drawTwoDimAnnotationSurfaceTextureOff case AnnotationTypeEnum::BOX: drawnFlag = drawBoxSurfaceTangentOffset(annotationFile, dynamic_cast(annotation), - surfaceDisplayed, surfaceExtentZ, vertexXYZ); break; @@ -1342,7 +1341,6 @@ BrainOpenGLAnnotationDrawingFixedPipeline::drawTwoDimAnnotationSurfaceTextureOff case AnnotationTypeEnum::IMAGE: drawnFlag = drawImageSurfaceTangentOffset(annotationFile, dynamic_cast(annotation), - surfaceDisplayed, surfaceExtentZ, vertexXYZ); break; @@ -1352,14 +1350,12 @@ BrainOpenGLAnnotationDrawingFixedPipeline::drawTwoDimAnnotationSurfaceTextureOff case AnnotationTypeEnum::OVAL: drawnFlag = drawOvalSurfaceTangentOffset(annotationFile, dynamic_cast(annotation), - surfaceDisplayed, surfaceExtentZ, vertexXYZ); break; case AnnotationTypeEnum::TEXT: drawnFlag = drawTextSurfaceTangentOffset(annotationFile, dynamic_cast(annotation), - surfaceDisplayed, surfaceExtentZ, vertexXYZ, normalXYZ); @@ -1603,8 +1599,6 @@ BrainOpenGLAnnotationDrawingFixedPipeline::drawBox(AnnotationFile* annotationFil * File containing the annotation. * @param box * box to draw. - * @param surfaceDisplayed - * Surface that is displayed (may be NULL). * @param surfaceExtentZ * Z-extent of the surface. * @param vertexXYZ @@ -1615,13 +1609,11 @@ BrainOpenGLAnnotationDrawingFixedPipeline::drawBox(AnnotationFile* annotationFil bool BrainOpenGLAnnotationDrawingFixedPipeline::drawBoxSurfaceTangentOffset(AnnotationFile* annotationFile, AnnotationBox* box, - const Surface* surfaceDisplayed, const float surfaceExtentZ, const float vertexXYZ[3]) { CaretAssert(annotationFile); CaretAssert(box); - CaretAssert(surfaceDisplayed); CaretAssert(box->getType() == AnnotationTypeEnum::BOX); const float halfWidth = ((box->getWidth() / 100.0) * surfaceExtentZ) / 2.0; @@ -2658,8 +2650,6 @@ BrainOpenGLAnnotationDrawingFixedPipeline::drawOval(AnnotationFile* annotationFi * File containing the annotation. * @param oval * oval to draw. - * @param surfaceDisplayed - * Surface that is displayed (may be NULL). * @param surfaceExtentZ * Z-extent of the surface. * @param vertexXYZ @@ -2670,13 +2660,11 @@ BrainOpenGLAnnotationDrawingFixedPipeline::drawOval(AnnotationFile* annotationFi bool BrainOpenGLAnnotationDrawingFixedPipeline::drawOvalSurfaceTangentOffset(AnnotationFile* annotationFile, AnnotationOval* oval, - const Surface* surfaceDisplayed, const float surfaceExtentZ, const float vertexXYZ[3]) { CaretAssert(annotationFile); CaretAssert(oval); - CaretAssert(surfaceDisplayed); CaretAssert(oval->getType() == AnnotationTypeEnum::OVAL); const float halfWidth = ((oval->getWidth() / 100.0) * surfaceExtentZ) / 2.0; @@ -3067,8 +3055,6 @@ BrainOpenGLAnnotationDrawingFixedPipeline::isBackFacing(const float xyz[3], * File containing the annotation. * @param text * Annotation text to draw. - * @param surfaceDisplayed - * Surface that is displayed (may be NULL). * @param surfaceExtentZ * Z-extent of the surface. * @param vertexXYZ, @@ -3081,14 +3067,12 @@ BrainOpenGLAnnotationDrawingFixedPipeline::isBackFacing(const float xyz[3], bool BrainOpenGLAnnotationDrawingFixedPipeline::drawTextSurfaceTangentOffset(AnnotationFile* annotationFile, AnnotationText* text, - const Surface* surfaceDisplayed, const float surfaceExtentZ, const float vertexXYZ[3], const float vertexNormalXYZ[3]) { CaretAssert(annotationFile); CaretAssert(text); - CaretAssert(surfaceDisplayed); /* * Annotations with "DISPLAY_GROUP" propery may be turned on/off by user. @@ -3544,8 +3528,6 @@ BrainOpenGLAnnotationDrawingFixedPipeline::drawImage(AnnotationFile* annotationF * File containing the annotation. * @param image * Image to draw. - * @param surfaceDisplayed - * Surface that is displayed (may be NULL). * @param surfaceExtentZ * Z-extent of the surface. * @param vertexXYZ @@ -3556,13 +3538,11 @@ BrainOpenGLAnnotationDrawingFixedPipeline::drawImage(AnnotationFile* annotationF bool BrainOpenGLAnnotationDrawingFixedPipeline::drawImageSurfaceTangentOffset(AnnotationFile* annotationFile, AnnotationImage* image, - const Surface* surfaceDisplayed, const float surfaceExtentZ, const float vertexXYZ[3]) { CaretAssert(annotationFile); CaretAssert(image); - CaretAssert(surfaceDisplayed); CaretAssert(image->getType() == AnnotationTypeEnum::IMAGE); const float halfWidth = ((image->getWidth() / 100.0) * surfaceExtentZ) / 2.0; diff --git a/src/Brain/BrainOpenGLAnnotationDrawingFixedPipeline.h b/src/Brain/BrainOpenGLAnnotationDrawingFixedPipeline.h index 3d3e391b4..72e226d37 100644 --- a/src/Brain/BrainOpenGLAnnotationDrawingFixedPipeline.h +++ b/src/Brain/BrainOpenGLAnnotationDrawingFixedPipeline.h @@ -206,7 +206,6 @@ namespace caret { bool drawBoxSurfaceTangentOffset(AnnotationFile* annotationFile, AnnotationBox* box, - const Surface* surfaceDisplayed, const float surfaceExtentZ, const float vertexXYZ[3]); @@ -216,7 +215,6 @@ namespace caret { bool drawImageSurfaceTangentOffset(AnnotationFile* annotationFile, AnnotationImage* image, - const Surface* surfaceDisplayed, const float surfaceExtentZ, const float vertexXYZ[3]); @@ -235,7 +233,6 @@ namespace caret { bool drawOvalSurfaceTangentOffset(AnnotationFile* annotationFile, AnnotationOval* oval, - const Surface* surfaceDisplayed, const float surfaceExtentZ, const float vertexXYZ[3]); @@ -245,7 +242,6 @@ namespace caret { bool drawTextSurfaceTangentOffset(AnnotationFile* annotationFile, AnnotationText* text, - const Surface* surfaceDisplayed, const float surfaceExtentZ, const float vertexXYZ[3], const float vertexNormalXYZ[3]); -- GitLab From 97990e140d5bf35bd9d941ae123d511c332865b1 Mon Sep 17 00:00:00 2001 From: John Harwell Date: Thu, 13 Sep 2018 11:45:09 -0500 Subject: [PATCH 044/427] Partial implementation of WB-669 Data Tooltips: Displays surface XYZ, value from top overlay, border, focus; Voxel IJK/XYZ and value from top layer. Must be enabled by selection of Enable Data Tooltips from the Developer Menu. To see a tooltip move mouse over surface or volume and let mouse rest for a couple of second and tooltip will appear. --- src/Brain/CMakeLists.txt | 2 + src/Brain/DataToolTipsManager.cxx | 526 ++++++++++++++++++++++++++++ src/Brain/DataToolTipsManager.h | 142 ++++++++ src/Brain/SessionManager.cxx | 22 ++ src/Brain/SessionManager.h | 9 + src/Common/DeveloperFlagsEnum.cxx | 4 + src/Common/DeveloperFlagsEnum.h | 3 +- src/Files/CaretMappableDataFile.cxx | 48 +++ src/Files/CaretMappableDataFile.h | 12 + src/Files/CiftiMappableDataFile.h | 2 +- src/Files/GiftiTypeFile.cxx | 129 +++++++ src/Files/GiftiTypeFile.h | 6 + src/Files/VolumeFile.cxx | 47 +++ src/Files/VolumeFile.h | 6 + src/GuiQt/BrainOpenGLWidget.cxx | 59 +++- 15 files changed, 996 insertions(+), 21 deletions(-) create mode 100644 src/Brain/DataToolTipsManager.cxx create mode 100644 src/Brain/DataToolTipsManager.h diff --git a/src/Brain/CMakeLists.txt b/src/Brain/CMakeLists.txt index 84b6f0947..385e4b1c2 100644 --- a/src/Brain/CMakeLists.txt +++ b/src/Brain/CMakeLists.txt @@ -60,6 +60,7 @@ ChartingDataManager.h CiftiConnectivityMatrixDataFileManager.h CiftiFiberTrajectoryManager.h ClippingPlaneGroup.h +DataToolTipsManager.h DisplayProperties.h DisplayPropertiesAnnotation.h DisplayPropertiesAnnotationTextSubstitution.h @@ -209,6 +210,7 @@ ChartingDataManager.cxx CiftiConnectivityMatrixDataFileManager.cxx CiftiFiberTrajectoryManager.cxx ClippingPlaneGroup.cxx +DataToolTipsManager.cxx DisplayProperties.cxx DisplayPropertiesAnnotation.cxx DisplayPropertiesAnnotationTextSubstitution.cxx diff --git a/src/Brain/DataToolTipsManager.cxx b/src/Brain/DataToolTipsManager.cxx new file mode 100644 index 000000000..d69c3a176 --- /dev/null +++ b/src/Brain/DataToolTipsManager.cxx @@ -0,0 +1,526 @@ + +/*LICENSE_START*/ +/* + * Copyright (C) 2018 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + +#define __DATA_TOOL_TIPS_MANAGER_DECLARE__ +#include "DataToolTipsManager.h" +#undef __DATA_TOOL_TIPS_MANAGER_DECLARE__ + +#include "Brain.h" +#include "Border.h" +#include "BorderFile.h" +#include "BrowserTabContent.h" +#include "CaretAssert.h" +#include "CaretMappableDataFile.h" +#include "CiftiMappableDataFile.h" +#include "EventManager.h" +#include "FociFile.h" +#include "Focus.h" +#include "GiftiLabelTable.h" +#include "Overlay.h" +#include "OverlaySet.h" +#include "SceneClass.h" +#include "SceneClassAssistant.h" +#include "SelectionItemBorderSurface.h" +#include "SelectionItemFocusSurface.h" +#include "SelectionItemFocusVolume.h" +#include "SelectionItemSurfaceNode.h" +#include "SelectionItemVoxel.h" +#include "SelectionManager.h" +#include "Surface.h" + +using namespace caret; + + + +/** + * \class caret::DataToolTipsManager + * \brief Manages Data ToolTips. + * \ingroup Brain + */ + +/** + * Constructor. + */ +DataToolTipsManager::DataToolTipsManager() +: CaretObject() +{ + + m_sceneAssistant = std::unique_ptr(new SceneClassAssistant()); + +// EventManager::get()->addEventListener(this, EventTypeEnum::); +} + +/** + * Destructor. + */ +DataToolTipsManager::~DataToolTipsManager() +{ + EventManager::get()->removeAllEventsFromListener(this); +} + +/** + * Get text for the tooltip. + * + * @param brain + * The Brain. + * @param browserTab + * Browser tab in which tooltip is displayed + * @param selectionManager + * The selection manager. + */ +AString +DataToolTipsManager::getToolTip(const Brain* brain, + const BrowserTabContent* browserTab, + const SelectionManager* selectionManager) const +{ + CaretAssert(brain); + CaretAssert(browserTab); + CaretAssert(selectionManager); + + AString text; + + const SelectionItemSurfaceNode* selectedNode = selectionManager->getSurfaceNodeIdentification(); + CaretAssert(selectedNode); + + if (selectedNode->isValid()) { + text = getSurfaceToolTip(brain, + browserTab, + selectionManager, + selectedNode); + } + else { + const SelectionItemVoxel* selectedVoxel = selectionManager->getVoxelIdentification(); + if (selectedVoxel->isValid()) { + text = getVolumeToolTip(brain, + browserTab, + selectionManager, + selectedVoxel); + } + } + + return text; +} + +/** + * Get text for the tooltip for a selected node. + * + * @param brain + * The Brain. + * @param browserTab + * Browser tab in which tooltip is displayed + * @param selectionManager + * The selection manager. + * @param nodeSelection + * Node selection information. + */ +AString +DataToolTipsManager::getSurfaceToolTip(const Brain* brain, + const BrowserTabContent* browserTab, + const SelectionManager* selectionManager, + const SelectionItemSurfaceNode* nodeSelection) const +{ + AString text; + + const Surface* surface = nodeSelection->getSurface(); + CaretAssert(surface); + int32_t surfaceNumberOfNodes = surface->getNumberOfNodes(); + int32_t surfaceNodeIndex = nodeSelection->getNodeNumber(); + StructureEnum::Enum surfaceStructure = surface->getStructure(); + + if ((surfaceStructure != StructureEnum::INVALID) + && (surfaceNumberOfNodes > 0) + && (surfaceNodeIndex >= 0)) { + + bool showSurfaceFlag = m_showSurfaceViewedFlag; + if (m_showSurfacePrimaryAnatomicalFlag) { + const Surface* anatSurface = brain->getPrimaryAnatomicalSurfaceForStructure(surfaceStructure); + if (anatSurface != NULL) { + if (anatSurface->getNumberOfNodes() == surfaceNumberOfNodes) { + float xyz[3]; + anatSurface->getCoordinate(surfaceNodeIndex, + xyz); + text.appendWithNewLine("Anatomy Surface: " + + AString::fromNumbers(xyz, 3, ", ")); + if (surface == anatSurface) { + showSurfaceFlag = false; + } + } + } + } + + if (showSurfaceFlag) { + float xyz[3]; + surface->getCoordinate(surfaceNodeIndex, + xyz); + text.appendWithNewLine("Surface: " + + AString::fromNumbers(xyz, 3, ", ")); + } + + if (m_showTopLayerFlag) { + const OverlaySet* overlaySet = browserTab->getOverlaySet(); + CaretAssert(overlaySet); + Overlay* overlay = const_cast(overlaySet->getOverlay(0)); + CaretAssert(overlay); + CaretMappableDataFile* mapFile(NULL); + int32_t mapIndex(-1); + overlay->getSelectionData(mapFile, + mapIndex); + if ((mapFile != NULL) + && (mapIndex >= 0)) { + std::vector mapIndices { mapIndex }; + AString textValue; + mapFile->getSurfaceNodeIdentificationForMaps(mapIndices, + surfaceStructure, + surfaceNodeIndex, + surfaceNumberOfNodes, + textValue); + if ( ! textValue.isEmpty()) { + text.appendWithNewLine("Top Layer: " + + textValue); + } + } + } + } + + if (m_showBorderFlag) { + const SelectionItemBorderSurface* borderSelection = selectionManager->getSurfaceBorderIdentification(); + CaretAssert(borderSelection); + const BorderFile* borderFile = borderSelection->getBorderFile(); + const int32_t borderIndex = borderSelection->getBorderIndex(); + if ((borderFile != NULL) + && (borderIndex >= 0)) { + const Border* border = borderFile->getBorder(borderIndex); + if (border != NULL) { + text.appendWithNewLine("Border: " + + border->getName()); + } + } + } + + if (m_showFocusFlag) { + const SelectionItemFocusSurface* focusSelection = selectionManager->getSurfaceFocusIdentification(); + CaretAssert(focusSelection); + const FociFile* fociFile = focusSelection->getFociFile(); + const int32_t focusIndex = focusSelection->getFocusIndex(); + if ((fociFile != NULL) + && (focusIndex >= 0)) { + const Focus* focus = fociFile->getFocus(focusIndex); + if (focus != NULL) { + text.appendWithNewLine("Focus: " + + focus->getName()); + } + } + } + + return text; +} + +/** + * Get text for the tooltip for a selected node. + * + * @param brain + * The Brain. + * @param browserTab + * Browser tab in which tooltip is displayed + * @param selectionManager + * The selection manager. + * @param voxelSelection + * Voxel selection information. + */ +AString +DataToolTipsManager::getVolumeToolTip(const Brain* /*brain*/, + const BrowserTabContent* browserTab, + const SelectionManager* /*selectionManager*/, + const SelectionItemVoxel* voxelSelection) const +{ + AString text; + + const VolumeMappableInterface* selectedVolumeInterface = voxelSelection->getVolumeFile(); + AString filename; + { + const VolumeFile* volumeFile = dynamic_cast(selectedVolumeInterface); + if (volumeFile != NULL) { + filename = volumeFile->getFileNameNoPath(); + } + } + { + const CiftiMappableDataFile* ciftiMapFile = dynamic_cast(selectedVolumeInterface); + if (ciftiMapFile != NULL) { + filename = ciftiMapFile->getFileNameNoPath(); + } + } + double modelXYZ[3]; + voxelSelection->getModelXYZ(modelXYZ); + const float floatXYZ[3] { + static_cast(modelXYZ[0]), + static_cast(modelXYZ[1]), + static_cast(modelXYZ[2]) + }; + int64_t ijk[3]; + voxelSelection->getVoxelIJK(ijk); + + text.appendWithNewLine("Voxel IJK: " + + AString::fromNumbers(ijk, 3, ", ") + + " XYZ: " + + AString::fromNumbers(modelXYZ, 3, ", ") + + " " + + filename); + const float value = selectedVolumeInterface->getVoxelValue(floatXYZ); + text.appendWithNewLine(" Value: " + + AString::number(value, 'f')); + + if (m_showTopLayerFlag) { + const OverlaySet* overlaySet = browserTab->getOverlaySet(); + CaretAssert(overlaySet); + Overlay* overlay = const_cast(overlaySet->getOverlay(0)); + CaretAssert(overlay); + CaretMappableDataFile* mapFile(NULL); + int32_t mapIndex(-1); + overlay->getSelectionData(mapFile, + mapIndex); + if ((mapFile != NULL) + && (mapIndex >= 0)) { + std::vector mapIndices { mapIndex }; + AString textValue; + int64_t ijk[3]; + mapFile->getVolumeVoxelIdentificationForMaps(mapIndices, + floatXYZ, + ijk, + textValue); + if ( ! textValue.isEmpty()) { + text.appendWithNewLine("Top Layer: " + + textValue); + } + } + } + return text; +} + + +/** + * @return Is tips enabled? + */ +bool +DataToolTipsManager::isEnabled() const +{ + return m_enabledFlag; +} + +/** + * Set status for tips enabled + * + * @param status + * New status. + */ +void +DataToolTipsManager::setEnabled(const bool status) +{ + m_enabledFlag = status; +} + +/** + * @return Is show primary anatomical surface enabled? + */ +bool +DataToolTipsManager::isShowSurfacePrimaryAnatomical() const +{ + return m_showSurfacePrimaryAnatomicalFlag; +} + +/** + * Set status for primary anatomical surface enabled + * + * @param status + * New status. + */ +void +DataToolTipsManager::setShowSurfacePrimaryAnatomical(const bool status) +{ + m_showSurfacePrimaryAnatomicalFlag = status; +} + +/** + * @return Is show viewed surface enabled? + */ +bool +DataToolTipsManager::isShowSurfaceViewed() const +{ + return m_showSurfaceViewedFlag; +} + +/** + * Set status for viewed surface enabled + * + * @param status + * New status. + */ +void +DataToolTipsManager::setShowSurfaceViewed(const bool status) +{ + m_showSurfaceViewedFlag = status; +} + +/** + * @return Is show top layer enabled? + */ +bool +DataToolTipsManager::isShowTopLayer() const +{ + return m_showTopLayerFlag; +} + +/** + * Set status for top layer enabled + * + * @param status + * New status. + */ +void +DataToolTipsManager::setShowTopLayer(const bool status) +{ + m_showTopLayerFlag = status; +} + +/** + * @return Is show border enabled? + */ +bool +DataToolTipsManager::isShowBorder() const +{ + return m_showBorderFlag; +} + +/** + * Set status for show border + * + * @param status + * New status. + */ +void +DataToolTipsManager::setShowBorder(const bool status) +{ + m_showBorderFlag = status; +} + +/** + * @return Is show focus enabled? + */ +bool +DataToolTipsManager::isShowFocus() const +{ + return m_showFocusFlag; +} + +/** + * Set status for show focus + * + * @param status + * New status. + */ +void +DataToolTipsManager::setShowFocus(const bool status) +{ + m_showFocusFlag = status; +} + +/** + * Get a description of this object's content. + * @return String describing this object's content. + */ +AString +DataToolTipsManager::toString() const +{ + return "DataToolTipsManager"; +} + +/** + * Receive an event. + * + * @param event + * An event for which this instance is listening. + */ +void +DataToolTipsManager::receiveEvent(Event* /*event*/) +{ +// if (event->getEventType() == EventTypeEnum::) { +// eventName = dynamic_cast(event); +// CaretAssert(eventName); +// +// event->setEventProcessed(); +// } +} + +/** + * Save information specific to this type of model to the scene. + * + * @param sceneAttributes + * Attributes for the scene. Scenes may be of different types + * (full, generic, etc) and the attributes should be checked when + * saving the scene. + * + * @param instanceName + * Name of instance in the scene. + */ +SceneClass* +DataToolTipsManager::saveToScene(const SceneAttributes* sceneAttributes, + const AString& instanceName) +{ + SceneClass* sceneClass = new SceneClass(instanceName, + "DataToolTipsManager", + 1); + m_sceneAssistant->saveMembers(sceneAttributes, + sceneClass); + + // Uncomment if sub-classes must save to scene + //saveSubClassDataToScene(sceneAttributes, + // sceneClass); + + return sceneClass; +} + +/** + * Restore information specific to the type of model from the scene. + * + * @param sceneAttributes + * Attributes for the scene. Scenes may be of different types + * (full, generic, etc) and the attributes should be checked when + * restoring the scene. + * + * @param sceneClass + * sceneClass from which model specific information is obtained. + */ +void +DataToolTipsManager::restoreFromScene(const SceneAttributes* sceneAttributes, + const SceneClass* sceneClass) +{ + if (sceneClass == NULL) { + return; + } + + m_sceneAssistant->restoreMembers(sceneAttributes, + sceneClass); + + //Uncomment if sub-classes must restore from scene + //restoreSubClassDataFromScene(sceneAttributes, + // sceneClass); + +} + diff --git a/src/Brain/DataToolTipsManager.h b/src/Brain/DataToolTipsManager.h new file mode 100644 index 000000000..e1a670922 --- /dev/null +++ b/src/Brain/DataToolTipsManager.h @@ -0,0 +1,142 @@ +#ifndef __DATA_TOOL_TIPS_MANAGER_H__ +#define __DATA_TOOL_TIPS_MANAGER_H__ + +/*LICENSE_START*/ +/* + * Copyright (C) 2018 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + + + +#include + +#include "CaretObject.h" + +#include "EventListenerInterface.h" +#include "SceneableInterface.h" + + +namespace caret { + class Brain; + class BrowserTabContent; + class SceneClassAssistant; + class SelectionItemSurfaceNode; + class SelectionItemVoxel; + class SelectionManager; + + class DataToolTipsManager : public CaretObject, public EventListenerInterface, public SceneableInterface { + + public: + DataToolTipsManager(); + + virtual ~DataToolTipsManager(); + + DataToolTipsManager(const DataToolTipsManager&) = delete; + + DataToolTipsManager& operator=(const DataToolTipsManager&) = delete; + + AString getToolTip(const Brain* brain, + const BrowserTabContent* browserTab, + const SelectionManager* selectionManager) const; + + bool isEnabled() const; + + void setEnabled(const bool status); + + bool isShowSurfacePrimaryAnatomical() const; + + void setShowSurfacePrimaryAnatomical(const bool status); + + bool isShowSurfaceViewed() const; + + void setShowSurfaceViewed(const bool status); + + bool isShowTopLayer() const; + + void setShowTopLayer(const bool status); + + bool isShowBorder() const; + + void setShowBorder(const bool status); + + bool isShowFocus() const; + + void setShowFocus(const bool status); + + // ADD_NEW_METHODS_HERE + + virtual AString toString() const; + + virtual void receiveEvent(Event* event); + + virtual SceneClass* saveToScene(const SceneAttributes* sceneAttributes, + const AString& instanceName); + + virtual void restoreFromScene(const SceneAttributes* sceneAttributes, + const SceneClass* sceneClass); + + + + + + +// If there will be sub-classes of this class that need to save +// and restore data from scenes, these pure virtual methods can +// be uncommented to force their implementation by sub-classes. +// protected: +// virtual void saveSubClassDataToScene(const SceneAttributes* sceneAttributes, +// SceneClass* sceneClass) = 0; +// +// virtual void restoreSubClassDataFromScene(const SceneAttributes* sceneAttributes, +// const SceneClass* sceneClass) = 0; + + private: + AString getSurfaceToolTip(const Brain* brain, + const BrowserTabContent* browserTab, + const SelectionManager* selectionManager, + const SelectionItemSurfaceNode* nodeSelection) const; + + AString getVolumeToolTip(const Brain* brain, + const BrowserTabContent* browserTab, + const SelectionManager* selectionManager, + const SelectionItemVoxel* voxelSelection) const; + + std::unique_ptr m_sceneAssistant; + + bool m_enabledFlag = true; + + bool m_showSurfacePrimaryAnatomicalFlag = true; + + bool m_showSurfaceViewedFlag = true; + + bool m_showTopLayerFlag = true; + + bool m_showBorderFlag = true; + + bool m_showFocusFlag = true; + + // ADD_NEW_MEMBERS_HERE + + }; + +#ifdef __DATA_TOOL_TIPS_MANAGER_DECLARE__ + // +#endif // __DATA_TOOL_TIPS_MANAGER_DECLARE__ + +} // namespace +#endif //__DATA_TOOL_TIPS_MANAGER_H__ diff --git a/src/Brain/SessionManager.cxx b/src/Brain/SessionManager.cxx index d1cc889e3..d5f45d543 100644 --- a/src/Brain/SessionManager.cxx +++ b/src/Brain/SessionManager.cxx @@ -35,6 +35,7 @@ #include "CaretPreferences.h" #include "CiftiConnectivityMatrixDataFileManager.h" #include "CiftiFiberTrajectoryManager.h" +#include "DataToolTipsManager.h" #include "ElapsedTimer.h" #include "EventManager.h" #include "EventBrowserTabDelete.h" @@ -76,6 +77,7 @@ SessionManager::SessionManager() m_ciftiConnectivityMatrixDataFileManager = new CiftiConnectivityMatrixDataFileManager(); m_ciftiFiberTrajectoryManager = new CiftiFiberTrajectoryManager(); + m_dataToolTipsManager.reset(new DataToolTipsManager()); for (int32_t i = 0; i < BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS; i++) { m_browserTabs[i] = NULL; @@ -979,6 +981,26 @@ SessionManager::getCiftiFiberTrajectoryManager() const return m_ciftiFiberTrajectoryManager; } +/** + * @return The data tool tips manager + */ +DataToolTipsManager* +SessionManager::getDataToolTipsManager() +{ + return m_dataToolTipsManager.get(); +} + +/** + * @return The data tool tips manager (const method) + */ +const DataToolTipsManager* +SessionManager::getDataToolTipsManager() const +{ + return m_dataToolTipsManager.get(); +} + + + /** * @return Image capture settings for image capture dialog. */ diff --git a/src/Brain/SessionManager.h b/src/Brain/SessionManager.h index 959029327..219d9804d 100644 --- a/src/Brain/SessionManager.h +++ b/src/Brain/SessionManager.h @@ -22,6 +22,7 @@ /*LICENSE_END*/ #include +#include #include "ApplicationTypeEnum.h" #include "BrainConstants.h" @@ -37,6 +38,7 @@ namespace caret { class CaretPreferences; class CiftiConnectivityMatrixDataFileManager; class CiftiFiberTrajectoryManager; + class DataToolTipsManager; class ImageCaptureSettings; class Model; @@ -68,6 +70,10 @@ namespace caret { const CiftiFiberTrajectoryManager* getCiftiFiberTrajectoryManager() const; + DataToolTipsManager* getDataToolTipsManager(); + + const DataToolTipsManager* getDataToolTipsManager() const; + ImageCaptureSettings* getImageCaptureDialogSettings(); const ImageCaptureSettings* getImageCaptureDialogSettings() const; @@ -118,6 +124,9 @@ namespace caret { /** Loads fiber trajectory data */ CiftiFiberTrajectoryManager* m_ciftiFiberTrajectoryManager; + /** Data Tool Tips Manager */ + std::unique_ptr m_dataToolTipsManager; + /** Settings for image capture dialog */ ImageCaptureSettings* m_imageCaptureDialogSettings; diff --git a/src/Common/DeveloperFlagsEnum.cxx b/src/Common/DeveloperFlagsEnum.cxx index 4cae6e410..b911d2a4f 100644 --- a/src/Common/DeveloperFlagsEnum.cxx +++ b/src/Common/DeveloperFlagsEnum.cxx @@ -117,6 +117,10 @@ DeveloperFlagsEnum::initialize() "DEVELOPER_FLAG_FLIP_PALETTE_NOT_DATA", "Flip Palette Not Data", false)); + enumData.push_back(DeveloperFlagsEnum(DEVELOPER_FLAG_BRAIN_TIPS, + "DEVELOPER_FLAG_BRAIN_TIPS", + "Enable Data Tool Tips", + false)); } /** diff --git a/src/Common/DeveloperFlagsEnum.h b/src/Common/DeveloperFlagsEnum.h index 2a352066e..45bfaed06 100644 --- a/src/Common/DeveloperFlagsEnum.h +++ b/src/Common/DeveloperFlagsEnum.h @@ -36,7 +36,8 @@ public: */ enum Enum { DEVELOPER_FLAG_UNUSED, - DEVELOPER_FLAG_FLIP_PALETTE_NOT_DATA + DEVELOPER_FLAG_FLIP_PALETTE_NOT_DATA, + DEVELOPER_FLAG_BRAIN_TIPS }; ~DeveloperFlagsEnum(); diff --git a/src/Files/CaretMappableDataFile.cxx b/src/Files/CaretMappableDataFile.cxx index b5a03a6e4..30fcb3e6b 100644 --- a/src/Files/CaretMappableDataFile.cxx +++ b/src/Files/CaretMappableDataFile.cxx @@ -1473,3 +1473,51 @@ const CiftiXML CaretMappableDataFile::getCiftiXML() const return CiftiXML(); } +/** + * Get the identification information for a surface node in the given maps. + * + * @param mapIndices + * Indices of maps for which identification information is requested. + * @param structure + * Structure of the surface. + * @param nodeIndex + * Index of the node. + * @param numberOfNodes + * Number of nodes in the surface. + * @param textOut + * Output containing identification information. + */ +bool +CaretMappableDataFile::getSurfaceNodeIdentificationForMaps(const std::vector& /*mapIndices*/, + const StructureEnum::Enum /*structure*/, + const int /*nodeIndex*/, + const int32_t /*numberOfNodes*/, + AString& textOut) const +{ + textOut.clear(); + return false; +} + +/** + * Get the identification information for a surface node in the given maps. + * + * @param mapIndices + * Indices of maps for which identification information is requested. + * @param xyz + * Coordinate of voxel. + * @param ijkOut + * Voxel indices of value. + * @param textOut + * Output containing identification information. + */ +bool +CaretMappableDataFile::getVolumeVoxelIdentificationForMaps(const std::vector& /*mapIndices*/, + const float* /*xyz[3]*/, + int64_t* /*ijkOut[3]*/, + AString& textOut) const +{ + textOut.clear(); + return false; +} + + diff --git a/src/Files/CaretMappableDataFile.h b/src/Files/CaretMappableDataFile.h index 6bf46d6a9..7afc6eb44 100644 --- a/src/Files/CaretMappableDataFile.h +++ b/src/Files/CaretMappableDataFile.h @@ -504,6 +504,18 @@ namespace caret { */ virtual BrainordinateMappingMatch getBrainordinateMappingMatch(const CaretMappableDataFile* mapFile) const = 0; + virtual bool getSurfaceNodeIdentificationForMaps(const std::vector& mapIndices, + const StructureEnum::Enum structure, + const int nodeIndex, + const int32_t numberOfNodes, + AString& textOut) const; + + virtual bool getVolumeVoxelIdentificationForMaps(const std::vector& mapIndices, + const float xyz[3], + int64_t ijkOut[3], + AString& textOut) const; + + protected: CaretMappableDataFile(const CaretMappableDataFile&); diff --git a/src/Files/CiftiMappableDataFile.h b/src/Files/CiftiMappableDataFile.h index e75f13a0d..ed17338ee 100644 --- a/src/Files/CiftiMappableDataFile.h +++ b/src/Files/CiftiMappableDataFile.h @@ -401,7 +401,7 @@ namespace caret { const StructureEnum::Enum structure, const int nodeIndex, const int32_t numberOfNodes, - AString& textOut) const; + AString& textOut) const override; int32_t getMappingSurfaceNumberOfNodes(const StructureEnum::Enum structure) const; diff --git a/src/Files/GiftiTypeFile.cxx b/src/Files/GiftiTypeFile.cxx index 67e9b15f2..80315b8d3 100644 --- a/src/Files/GiftiTypeFile.cxx +++ b/src/Files/GiftiTypeFile.cxx @@ -24,10 +24,14 @@ #include "FastStatistics.h" #include "GiftiDataArray.h" #include "GiftiFile.h" +#include "GiftiLabel.h" +#include "GiftiLabelTable.h" #include "GiftiMetaData.h" #include "GiftiTypeFile.h" #include "GiftiMetaDataXmlElements.h" #include "Histogram.h" +#include "LabelFile.h" +#include "MetricFile.h" #include "PaletteColorMapping.h" #include "PaletteColorMappingSaxReader.h" #include "SurfaceFile.h" @@ -1088,4 +1092,129 @@ GiftiTypeFile::getBrainordinateMappingMatch(const CaretMappableDataFile* mapFile return BrainordinateMappingMatch::NO; } +/** + * Get the identification information for a surface node in the given maps. + * + * @param mapIndices + * Indices of maps for which identification information is requested. + * @param structure + * Structure of the surface. + * @param nodeIndex + * Index of the node. + * @param numberOfNodes + * Number of nodes in the surface. + * @param textOut + * Output containing identification information. + */ +bool +GiftiTypeFile::getSurfaceNodeIdentificationForMaps(const std::vector& mapIndices, + const StructureEnum::Enum structure, + const int nodeIndex, + const int32_t numberOfNodes, + AString& textOut) const +{ + textOut.clear(); + + if ((getStructure() == structure) + && (getNumberOfNodes() == numberOfNodes)) { + switch (getDataFileType()) { + case DataFileTypeEnum::ANNOTATION: + break; + case DataFileTypeEnum::ANNOTATION_TEXT_SUBSTITUTION: + break; + case DataFileTypeEnum::BORDER: + break; + case DataFileTypeEnum::CONNECTIVITY_DENSE: + break; + case DataFileTypeEnum::CONNECTIVITY_DENSE_DYNAMIC: + break; + case DataFileTypeEnum::CONNECTIVITY_DENSE_LABEL: + break; + case DataFileTypeEnum::CONNECTIVITY_DENSE_PARCEL: + break; + case DataFileTypeEnum::CONNECTIVITY_DENSE_SCALAR: + break; + case DataFileTypeEnum::CONNECTIVITY_DENSE_TIME_SERIES: + break; + case DataFileTypeEnum::CONNECTIVITY_FIBER_ORIENTATIONS_TEMPORARY: + break; + case DataFileTypeEnum::CONNECTIVITY_FIBER_TRAJECTORY_TEMPORARY: + break; + case DataFileTypeEnum::CONNECTIVITY_PARCEL: + break; + case DataFileTypeEnum::CONNECTIVITY_PARCEL_DENSE: + break; + case DataFileTypeEnum::CONNECTIVITY_PARCEL_LABEL: + break; + case DataFileTypeEnum::CONNECTIVITY_PARCEL_SCALAR: + break; + case DataFileTypeEnum::CONNECTIVITY_PARCEL_SERIES: + break; + case DataFileTypeEnum::CONNECTIVITY_SCALAR_DATA_SERIES: + break; + case DataFileTypeEnum::FOCI: + break; + case DataFileTypeEnum::IMAGE: + break; + case DataFileTypeEnum::LABEL: + { + const LabelFile* lf = dynamic_cast(this); + CaretAssert(lf); + const GiftiLabelTable* labelTable = lf->getLabelTable(); + AString valuesText; + for (const auto mapIndex : mapIndices) { + const int32_t key = lf->getLabelKey(nodeIndex, + mapIndex); + if (key >= 0) { + if ( ! valuesText.isEmpty()) { + valuesText.append(" "); + } + valuesText.append(labelTable->getLabel(key)->getName()); + } + } + + if ( ! valuesText.isEmpty()) { + textOut = valuesText; + } + } + break; + case DataFileTypeEnum::METRIC: + { + const MetricFile* mf = dynamic_cast(this); + CaretAssert(mf); + AString valuesText; + for (const auto mapIndex : mapIndices) { + const float value = mf->getValue(nodeIndex, + mapIndex); + if ( ! valuesText.isEmpty()) { + valuesText.append(" "); + } + valuesText.append(AString::number(value, 'f', 3)); + } + + if ( ! valuesText.isEmpty()) { + textOut = valuesText; + } + } + break; + case DataFileTypeEnum::PALETTE: + break; + case DataFileTypeEnum::RGBA: + break; + case DataFileTypeEnum::SCENE: + break; + case DataFileTypeEnum::SPECIFICATION: + break; + case DataFileTypeEnum::SURFACE: + break; + case DataFileTypeEnum::UNKNOWN: + break; + case DataFileTypeEnum::VOLUME: + break; + } + } + + return ( ! textOut.isEmpty()); +} + diff --git a/src/Files/GiftiTypeFile.h b/src/Files/GiftiTypeFile.h index aef32ce51..b534902e4 100644 --- a/src/Files/GiftiTypeFile.h +++ b/src/Files/GiftiTypeFile.h @@ -170,6 +170,12 @@ namespace caret { virtual BrainordinateMappingMatch getBrainordinateMappingMatch(const CaretMappableDataFile* mapFile) const override; + virtual bool getSurfaceNodeIdentificationForMaps(const std::vector& mapIndices, + const StructureEnum::Enum structure, + const int nodeIndex, + const int32_t numberOfNodes, + AString& textOut) const; + private: void copyHelperGiftiTypeFile(const GiftiTypeFile& gtf); diff --git a/src/Files/VolumeFile.cxx b/src/Files/VolumeFile.cxx index a2c20f513..7978c34c0 100644 --- a/src/Files/VolumeFile.cxx +++ b/src/Files/VolumeFile.cxx @@ -2277,4 +2277,51 @@ VolumeFile::getBrainordinateMappingMatch(const CaretMappableDataFile* mapFile) c return BrainordinateMappingMatch::NO; } +/** + * Get the identification information for a surface node in the given maps. + * + * @param mapIndices + * Indices of maps for which identification information is requested. + * @param xyz + * Coordinate of voxel. + * @param ijkOut + * Voxel indices of value. + * @param textOut + * Output containing identification information. + */ +bool +VolumeFile::getVolumeVoxelIdentificationForMaps(const std::vector& mapIndices, + const float xyz[3], + int64_t ijkOut[3], + AString& textOut) const +{ +// CaretAssert(m_ciftiFile); +// +// const int32_t numberOfMapIndices = static_cast(mapIndices.size()); +// if (numberOfMapIndices <= 0) { +// return false; +// } +// +// textOut = ""; +// +// std::vector numericalValues; +// std::vector numericalValuesValid; +// AString textValue; +// if (getMapVolumeVoxelValues(mapIndices, +// xyz, +// ijkOut, +// numericalValues, +// numericalValuesValid, +// textValue)) { +// textOut = textValue; +// } +// +// if (textOut.isEmpty() == false) { +// return true; +// } + + return false; +} + + diff --git a/src/Files/VolumeFile.h b/src/Files/VolumeFile.h index b53a6accb..bb8fb16d9 100644 --- a/src/Files/VolumeFile.h +++ b/src/Files/VolumeFile.h @@ -369,6 +369,12 @@ namespace caret { std::vector& dataOut) const override; virtual BrainordinateMappingMatch getBrainordinateMappingMatch(const CaretMappableDataFile* mapFile) const override; + + virtual bool getVolumeVoxelIdentificationForMaps(const std::vector& mapIndices, + const float xyz[3], + int64_t ijkOut[3], + AString& textOut) const; + }; } diff --git a/src/GuiQt/BrainOpenGLWidget.cxx b/src/GuiQt/BrainOpenGLWidget.cxx index 7515d232c..dbddee038 100644 --- a/src/GuiQt/BrainOpenGLWidget.cxx +++ b/src/GuiQt/BrainOpenGLWidget.cxx @@ -47,6 +47,8 @@ #include "CaretLogger.h" #include "CaretPreferences.h" #include "CursorManager.h" +#include "DataToolTipsManager.h" +#include "DeveloperFlagsEnum.h" #include "DummyFontTextRenderer.h" #include "EventBrainReset.h" #include "EventImageCapture.h" @@ -655,33 +657,46 @@ BrainOpenGLWidget::paintGL() bool BrainOpenGLWidget::event(QEvent* event) { - const bool toolTipsEnabled = false; + const bool toolTipsEnabled = DeveloperFlagsEnum::isFlag(DeveloperFlagsEnum::DEVELOPER_FLAG_BRAIN_TIPS); if (toolTipsEnabled) { if (event->type() == QEvent::ToolTip) { QHelpEvent* helpEvent = static_cast(event); CaretAssert(helpEvent); QPoint globalXY = helpEvent->globalPos(); - QPoint xy = helpEvent->pos(); + QPoint xyPoint = helpEvent->pos(); + const int32_t x = xyPoint.x(); + const int32_t y = this->height() - xyPoint.y(); - static int counter = 0; + DataToolTipsManager* dttm = SessionManager::get()->getDataToolTipsManager(); + CaretAssert(dttm); - std::cout - << "Displaying tooltip " - << counter++ - << " at global (" - << globalXY.x() - << ", " - << globalXY.y() - << ") at pos (" - << xy.x() - << ", " - << xy.y() - << ")" - << std::endl; + Brain* brain = GuiManager::get()->getBrain(); + CaretAssert(brain); - QToolTip::showText(globalXY, - "This is the tooltip " + AString::number(counter)); + AString toolTipText; + + const BrainOpenGLViewportContent* idViewport = this->getViewportContentAtXY(x, y); + if (idViewport != NULL) { + BrowserTabContent* browserTabContent = idViewport->getBrowserTabContent(); + if (browserTabContent != NULL) { + SelectionManager* selectionManager = performIdentification(x, + y, + false); // include items in background + toolTipText = dttm->getToolTip(GuiManager::get()->getBrain(), + browserTabContent, + selectionManager); + } + } + + if (toolTipText.isEmpty()) { + QToolTip::hideText(); + event->ignore(); + } + else { + QToolTip::showText(globalXY, + toolTipText); + } return true; } @@ -1373,7 +1388,13 @@ BrainOpenGLWidget::performProjection(const int x, */ void BrainOpenGLWidget::mouseMoveEvent(QMouseEvent* me) -{ +{ + /* + * Tooltip will remain displayed for several seconds. + * So if the user moves the mouse, remove the tooltip. + */ + QToolTip::hideText(); + Qt::MouseButton button = me->button(); Qt::KeyboardModifiers keyModifiers = me->modifiers(); Qt::MouseButtons mouseButtons = me->buttons(); -- GitLab From c3bc211d6c84985485c5e05db7a2741d7981fcbf Mon Sep 17 00:00:00 2001 From: John Harwell Date: Thu, 13 Sep 2018 12:39:26 -0500 Subject: [PATCH 045/427] Partial implementation of WB-669 Data Tooltips: Fixed compilation warning and added values from volume file when it is the top layer. Also, add support for label volumes. --- src/Files/GiftiTypeFile.cxx | 4 +-- src/Files/VolumeFile.cxx | 69 ++++++++++++++++++++++++------------- 2 files changed, 47 insertions(+), 26 deletions(-) diff --git a/src/Files/GiftiTypeFile.cxx b/src/Files/GiftiTypeFile.cxx index 80315b8d3..c9815fd23 100644 --- a/src/Files/GiftiTypeFile.cxx +++ b/src/Files/GiftiTypeFile.cxx @@ -1167,7 +1167,7 @@ GiftiTypeFile::getSurfaceNodeIdentificationForMaps(const std::vector& m mapIndex); if (key >= 0) { if ( ! valuesText.isEmpty()) { - valuesText.append(" "); + valuesText.append(", "); } valuesText.append(labelTable->getLabel(key)->getName()); } @@ -1187,7 +1187,7 @@ GiftiTypeFile::getSurfaceNodeIdentificationForMaps(const std::vector& m const float value = mf->getValue(nodeIndex, mapIndex); if ( ! valuesText.isEmpty()) { - valuesText.append(" "); + valuesText.append(", "); } valuesText.append(AString::number(value, 'f', 3)); } diff --git a/src/Files/VolumeFile.cxx b/src/Files/VolumeFile.cxx index 7978c34c0..ab0654d3b 100644 --- a/src/Files/VolumeFile.cxx +++ b/src/Files/VolumeFile.cxx @@ -33,6 +33,7 @@ #include "DataFileContentInformation.h" #include "ElapsedTimer.h" #include "EventManager.h" +#include "GiftiLabel.h" #include "GroupAndNameHierarchyModel.h" #include "FastStatistics.h" #include "Histogram.h" @@ -2295,30 +2296,50 @@ VolumeFile::getVolumeVoxelIdentificationForMaps(const std::vector& mapI int64_t ijkOut[3], AString& textOut) const { -// CaretAssert(m_ciftiFile); -// -// const int32_t numberOfMapIndices = static_cast(mapIndices.size()); -// if (numberOfMapIndices <= 0) { -// return false; -// } -// -// textOut = ""; -// -// std::vector numericalValues; -// std::vector numericalValuesValid; -// AString textValue; -// if (getMapVolumeVoxelValues(mapIndices, -// xyz, -// ijkOut, -// numericalValues, -// numericalValuesValid, -// textValue)) { -// textOut = textValue; -// } -// -// if (textOut.isEmpty() == false) { -// return true; -// } + float floatIJK[3]; + spaceToIndex(xyz, floatIJK); + + ijkOut[0] = floatIJK[0]; + ijkOut[1] = floatIJK[1]; + ijkOut[2] = floatIJK[2]; + + bool anyValidFlag = false; + AString valuesText; + for (const auto mapIndex : mapIndices) { + if ( ! valuesText.isEmpty()) { + valuesText.append(", "); + } + bool validFlag(false); + const float value = getVoxelValue(xyz, + &validFlag, + mapIndex); + if (validFlag) { + anyValidFlag = true; + if (isMappedWithLabelTable()) { + const GiftiLabelTable* labelTable = getMapLabelTable(mapIndex); + CaretAssert(labelTable); + const int32_t key = static_cast(value); + const GiftiLabel* label = labelTable->getLabel(key); + if (label != NULL) { + valuesText.append(label->getName()); + } + else { + valuesText.append("?"); + } + } + else { + valuesText.append(AString::number(value, 'f', 3)); + } + } + else { + valuesText.append("invalid"); + } + } + + if (anyValidFlag) { + textOut = valuesText; + return true; + } return false; } -- GitLab From fe2e593a269d622413bb8d8fb31f928d6bc85519 Mon Sep 17 00:00:00 2001 From: John Harwell Date: Thu, 13 Sep 2018 16:21:49 -0500 Subject: [PATCH 046/427] =?UTF-8?q?WB-636=20Texture=20Based=20Annotations:?= =?UTF-8?q?=20Moved=20the=20=E2=80=9Ctext=20up=E2=80=9D=20rotation=20calcu?= =?UTF-8?q?lation.=20=20It=20was=20setting=20the=20=E2=80=9Ctext=20up?= =?UTF-8?q?=E2=80=9D=20rotation=20angle=20for=20the=20annotation=20during?= =?UTF-8?q?=20the=20conversion=20process=20or=20when=20the=20annotation=20?= =?UTF-8?q?was=20created.=20=20Now,=20the=20=E2=80=9Ctext=20up=E2=80=9D=20?= =?UTF-8?q?rotation=20angle=20for=20=E2=80=98tangent=E2=80=99=20offset=20a?= =?UTF-8?q?nnotations=20is=20performed=20while=20drawing=20the=20annotatio?= =?UTF-8?q?n.=20=20This=20seems=20to=20minimize=20(in=20most=20cases)=20ro?= =?UTF-8?q?tation=20difference=20between=20different=20offset=20types.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Annotations/Annotation.cxx | 174 +++++++++++++++++- src/Annotations/Annotation.h | 3 + ...inOpenGLAnnotationDrawingFixedPipeline.cxx | 10 + 3 files changed, 179 insertions(+), 8 deletions(-) diff --git a/src/Annotations/Annotation.cxx b/src/Annotations/Annotation.cxx index 6551400b4..db2611468 100644 --- a/src/Annotations/Annotation.cxx +++ b/src/Annotations/Annotation.cxx @@ -748,20 +748,24 @@ Annotation::changeSurfaceSpaceToTangentOffset() } /** - * Initialize the rotation for an annotation in surface space with tangent - * offset using the given normal vector from the vertex to which the + * Get the rotation for an annotation in surface space with tangent + * offset using the given normal vector from the vertex to which the * annotation is attached. * * @param structure * The surface structure. * @param vertexNormal * Normal vector of surface vertex. + * @return + * Rotation angle so text is oriented up with 'best axis' */ -void -Annotation::initializeSurfaceSpaceWithTangentOffsetRotation(const StructureEnum::Enum structure, - const float vertexNormal[3]) +float +Annotation::getSurfaceSpaceWithTangentOffsetRotation(const StructureEnum::Enum structure, + const float vertexNormal[3]) const { - AnnotationTwoDimensionalShape* twoDimAnn = dynamic_cast(this); + float angleOut(0.0); + + const AnnotationTwoDimensionalShape* twoDimAnn = dynamic_cast(this); if (twoDimAnn != NULL) { if (isInSurfaceSpaceWithTangentOffset()) { enum class OrientationType { @@ -848,7 +852,7 @@ Annotation::initializeSurfaceSpaceWithTangentOffsetRotation(const StructureEnum: inverseMatrix.invert(); inverseMatrix.multiplyPoint3(surfaceUpAxisVector); const float alignRotationAngle = MathFunctions::angleInDegreesBetweenVectors(annotationUpYVector, - surfaceUpAxisVector); + surfaceUpAxisVector); float rotationAngle = alignRotationAngle; switch (matchingOrientation) { case OrientationType::LEFT_TO_RIGHT: @@ -875,10 +879,164 @@ Annotation::initializeSurfaceSpaceWithTangentOffsetRotation(const StructureEnum: rotationAngle += 90.0; break; } + + angleOut = rotationAngle; + } + } + + return angleOut; +} - twoDimAnn->setRotationAngle(rotationAngle); +/** + * Initialize the rotation for an annotation in surface space with tangent + * offset using the given normal vector from the vertex to which the + * annotation is attached. + * + * @param structure + * The surface structure. + * @param vertexNormal + * Normal vector of surface vertex. + */ +void +Annotation::initializeSurfaceSpaceWithTangentOffsetRotation(const StructureEnum::Enum structure, + const float vertexNormal[3]) +{ + return; + + + AnnotationTwoDimensionalShape* twoDimAnn = castToTwoDimensionalShape(); + if (twoDimAnn != NULL) { + if (isInSurfaceSpaceWithTangentOffset()) { + const float angle = getSurfaceSpaceWithTangentOffsetRotation(structure, + vertexNormal); + twoDimAnn->setRotationAngle(angle); } } + + return; + + + + + +// AnnotationTwoDimensionalShape* twoDimAnn = dynamic_cast(this); +// if (twoDimAnn != NULL) { +// if (isInSurfaceSpaceWithTangentOffset()) { +// enum class OrientationType { +// LEFT_TO_RIGHT = 0, +// RIGHT_TO_LEFT = 1, +// POSTERIOR_TO_ANTERIOR = 2, +// ANTERIOR_TO_POSTERIOR = 3, +// INFERIOR_TO_SUPERIOR = 4, +// SUPERIOR_TO_INFERIOR = 5 +// }; +// +// const OrientationType orientations[6] = { +// OrientationType::LEFT_TO_RIGHT, +// OrientationType::RIGHT_TO_LEFT, +// OrientationType::POSTERIOR_TO_ANTERIOR, +// OrientationType::ANTERIOR_TO_POSTERIOR, +// OrientationType::INFERIOR_TO_SUPERIOR, +// OrientationType::SUPERIOR_TO_INFERIOR +// }; +// const float orientationVectors[6][3] { +// { 1.0, 0.0, 0.0 }, +// { -1.0, 0.0, 0.0 }, +// { 0.0, 1.0, 0.0 }, +// { 0.0, -1.0, 0.0 }, +// { 0.0, 0.0, 1.0 }, +// { 0.0, 0.0, -1.0 } +// }; +// +// +// /* +// * Find orientation that aligns with the vertex's normal vector +// */ +// OrientationType matchingOrientation = OrientationType::LEFT_TO_RIGHT; +// float matchingAngle = 10000.0f; +// for (int32_t i = 0; i < 6; i++) { +// const float angle = MathFunctions::angleInDegreesBetweenVectors(orientationVectors[i], +// vertexNormal); +// if (angle < matchingAngle) { +// matchingAngle = angle; +// matchingOrientation = orientations[i]; +// } +// } +// +// float surfaceUpAxisVector[3] = { 0.0f, 0.0f, 1.0f }; +// switch (matchingOrientation) { +// case OrientationType::LEFT_TO_RIGHT: +// break; +// case OrientationType::RIGHT_TO_LEFT: +// break; +// case OrientationType::POSTERIOR_TO_ANTERIOR: +// break; +// case OrientationType::ANTERIOR_TO_POSTERIOR: +// break; +// case OrientationType::INFERIOR_TO_SUPERIOR: +// surfaceUpAxisVector[0] = 1.0; +// surfaceUpAxisVector[0] = 0.0; +// surfaceUpAxisVector[0] = 0.0; +// break; +// case OrientationType::SUPERIOR_TO_INFERIOR: +// surfaceUpAxisVector[0] = -1.0; +// surfaceUpAxisVector[0] = 0.0; +// surfaceUpAxisVector[0] = 0.0; +// break; +// } +// +// /* +// * Vector for annotation's Y (vector from bottom to top of annotation) +// */ +// const float annotationUpYVector[3] { +// 0.0, +// 1.0, +// 0.0 +// }; +// +// /* +// * Initialize the rotation angle so that the annotation's vertical axis +// * is aligned with the screen vertical axis when the surface is in the +// * analogous surface view. For a text annotation, the text should be +// * flowing left to right across screen. +// */ +// Matrix4x4 rotationMatrix; +// rotationMatrix.setMatrixToOpenGLRotationFromVector(vertexNormal); +// Matrix4x4 inverseMatrix(rotationMatrix); +// inverseMatrix.invert(); +// inverseMatrix.multiplyPoint3(surfaceUpAxisVector); +// const float alignRotationAngle = MathFunctions::angleInDegreesBetweenVectors(annotationUpYVector, +// surfaceUpAxisVector); +// float rotationAngle = alignRotationAngle; +// switch (matchingOrientation) { +// case OrientationType::LEFT_TO_RIGHT: +// rotationAngle = 360.0 - rotationAngle; +// break; +// case OrientationType::RIGHT_TO_LEFT: +// break; +// case OrientationType::POSTERIOR_TO_ANTERIOR: +// if (StructureEnum::isRight(structure)) { +// rotationAngle = 360.0 - rotationAngle; +// } +// break; +// case OrientationType::ANTERIOR_TO_POSTERIOR: +// if (StructureEnum::isRight(structure)) { +// rotationAngle = 360.0 - rotationAngle; +// } +// break; +// case OrientationType::INFERIOR_TO_SUPERIOR: +// if (StructureEnum::isRight(structure)) { +// rotationAngle += 180.0; +// } +// break; +// case OrientationType::SUPERIOR_TO_INFERIOR: +// rotationAngle += 90.0; +// break; +// } +// +// twoDimAnn->setRotationAngle(rotationAngle); +// } +// } } /** diff --git a/src/Annotations/Annotation.h b/src/Annotations/Annotation.h index ccfa16a24..1d4eff719 100644 --- a/src/Annotations/Annotation.h +++ b/src/Annotations/Annotation.h @@ -173,6 +173,9 @@ namespace caret { void changeSurfaceSpaceToTangentOffset(); + float getSurfaceSpaceWithTangentOffsetRotation(const StructureEnum::Enum structure, + const float vertexNormal[3]) const; + void initializeSurfaceSpaceWithTangentOffsetRotation(const StructureEnum::Enum structure, const float vertexNormal[3]); diff --git a/src/Brain/BrainOpenGLAnnotationDrawingFixedPipeline.cxx b/src/Brain/BrainOpenGLAnnotationDrawingFixedPipeline.cxx index 2f555b9ec..6e9b7bc0e 100644 --- a/src/Brain/BrainOpenGLAnnotationDrawingFixedPipeline.cxx +++ b/src/Brain/BrainOpenGLAnnotationDrawingFixedPipeline.cxx @@ -1321,6 +1321,16 @@ BrainOpenGLAnnotationDrawingFixedPipeline::drawTwoDimAnnotationSurfaceTextureOff std::cout << " Angle: " << orientationUpAngle << std::endl; } + /* + * TEST ROTATE HERE + */ + { + const float angle = annotation->getSurfaceSpaceWithTangentOffsetRotation(structure, + normalXYZ); + glRotated(angle, 0.0, 0.0, -1.0); + } + + /* * Note that text is rotated by the text renderer */ -- GitLab From 3ab3a5cbd0bb116e478c5d291c0f87107f83f5e9 Mon Sep 17 00:00:00 2001 From: John Harwell Date: Fri, 14 Sep 2018 09:50:30 -0500 Subject: [PATCH 047/427] WB-636 Texture Based Annotations: Disable "text up" rotation on flat surfaces for annotations with tangent offset. --- src/Brain/BrainOpenGLAnnotationDrawingFixedPipeline.cxx | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/Brain/BrainOpenGLAnnotationDrawingFixedPipeline.cxx b/src/Brain/BrainOpenGLAnnotationDrawingFixedPipeline.cxx index 6e9b7bc0e..21cfef0b0 100644 --- a/src/Brain/BrainOpenGLAnnotationDrawingFixedPipeline.cxx +++ b/src/Brain/BrainOpenGLAnnotationDrawingFixedPipeline.cxx @@ -1322,9 +1322,13 @@ BrainOpenGLAnnotationDrawingFixedPipeline::drawTwoDimAnnotationSurfaceTextureOff } /* - * TEST ROTATE HERE + * Do not adjust tangent text on flat surfaces */ - { + if (surfaceDisplayed->getSurfaceType() != SurfaceTypeEnum::FLAT) { + /* + * Rotates annotation so that its horizontal axis is aligned with the + * 'best matching' cartesian axis. + */ const float angle = annotation->getSurfaceSpaceWithTangentOffsetRotation(structure, normalXYZ); glRotated(angle, 0.0, 0.0, -1.0); -- GitLab From 01590f8f4f484c07a12b3126d7afed77266679e5 Mon Sep 17 00:00:00 2001 From: John Harwell Date: Fri, 14 Sep 2018 14:14:30 -0500 Subject: [PATCH 048/427] Add optional format and precision parameters to the AString::fromNumbers() methods that convert an array/vector of floats/doubles to a string. Their default values are the same as those for QString::number() that does the actual conversion of individual elements. --- src/Common/AString.cxx | 52 ++++++++++++++++++++++++++++++++++-------- src/Common/AString.h | 8 +++---- 2 files changed, 47 insertions(+), 13 deletions(-) diff --git a/src/Common/AString.cxx b/src/Common/AString.cxx index c97ea0bc8..531cd1fad 100644 --- a/src/Common/AString.cxx +++ b/src/Common/AString.cxx @@ -178,19 +178,27 @@ AString::fromNumbers(const std::vector& v, const AString& separator) * The vector of values. * @param separator * Inserted between each pair of values. + * @param format + * e format as [-]9.9e[+|-]999 + * E format as [-]9.9E[+|-]999 + * f format as [-]9.9 + * g use e or f format, whichever is the most concise + * G use E or f format, whichever is the most concise + * @param precision + * Maximum number of digits following decimal. * @return * String containing the vector's values separated * by the separator. */ AString -AString::fromNumbers(const std::vector& v, const AString& separator) +AString::fromNumbers(const std::vector& v, const AString& separator, char format, const int32_t precision) { AString s; for (uint64_t i = 0; i < v.size(); i++) { if (i > 0) { s += separator; } - s += AString::number(v[i]); + s += AString::number(v[i], format, precision); } return s; } @@ -201,19 +209,27 @@ AString::fromNumbers(const std::vector& v, const AString& separator) * The vector of values. * @param separator * Inserted between each pair of values. + * @param format + * e format as [-]9.9e[+|-]999 + * E format as [-]9.9E[+|-]999 + * f format as [-]9.9 + * g use e or f format, whichever is the most concise + * G use E or f format, whichever is the most concise + * @param precision + * Maximum number of digits following decimal. * @return * String containing the vector's values separated * by the separator. */ AString -AString::fromNumbers(const std::vector& v, const AString& separator) +AString::fromNumbers(const std::vector& v, const AString& separator, char format, const int32_t precision) { AString s; for (uint64_t i = 0; i < v.size(); i++) { if (i > 0) { s += separator; } - s += AString::number(v[i]); + s += AString::number(v[i], format, precision); } return s; } @@ -226,19 +242,27 @@ AString::fromNumbers(const std::vector& v, const AString& separator) * Number of elements in the array. * @param separator * Inserted between each pair of values. + * @param format + * e format as [-]9.9e[+|-]999 + * E format as [-]9.9E[+|-]999 + * f format as [-]9.9 + * g use e or f format, whichever is the most concise + * G use E or f format, whichever is the most concise + * @param precision + * Maximum number of digits following decimal. * @return * String containing the array values separated * by the separator. */ AString -AString::fromNumbers(const float* array, const int64_t numberOfElements, const AString& separator) +AString::fromNumbers(const float* array, const int64_t numberOfElements, const AString& separator, char format, const int32_t precision) { AString s; for (int64_t i = 0; i < numberOfElements; i++) { if (i > 0) { s += separator; } - s += AString::number(array[i]); + s += AString::number(array[i], format, precision); } return s; } @@ -360,21 +384,31 @@ AString::fromNumbers(const int64_t* array, * Number of elements in the array. * @param separator * Inserted between each pair of values. + * @param format + * e format as [-]9.9e[+|-]999 + * E format as [-]9.9E[+|-]999 + * f format as [-]9.9 + * g use e or f format, whichever is the most concise + * G use E or f format, whichever is the most concise + * @param precision + * Maximum number of digits following decimal. * @return * String containing the array values separated * by the separator. */ AString AString::fromNumbers(const double* array, - const int64_t numberOfElements, - const AString& separator) + const int64_t numberOfElements, + const AString& separator, + char format, + const int32_t precision) { AString s; for (int64_t i = 0; i < numberOfElements; i++) { if (i > 0) { s += separator; } - s += AString::number(array[i]); + s += AString::number(array[i], format, precision); } return s; } diff --git a/src/Common/AString.h b/src/Common/AString.h index f7fdd49d2..b93131501 100644 --- a/src/Common/AString.h +++ b/src/Common/AString.h @@ -118,14 +118,14 @@ namespace caret { static AString fromNumbers(const std::vector& v, const AString& separator); static AString fromNumbers(const std::vector& v, const AString& separator); static AString fromNumbers(const std::vector& v, const AString& separator); - static AString fromNumbers(const std::vector& v, const AString& separator); - static AString fromNumbers(const std::vector& v, const AString& separator); - static AString fromNumbers(const float* array, const int64_t numberOfElements, const AString& separator); + static AString fromNumbers(const std::vector& v, const AString& separator, const char format = 'g', const int32_t precision = 6); + static AString fromNumbers(const std::vector& v, const AString& separator, const char format = 'g', const int32_t precision = 6); + static AString fromNumbers(const float* array, const int64_t numberOfElements, const AString& separator, char format = 'g', const int32_t precision = 6); static AString fromNumbers(const uint8_t* array,const int64_t numberOfElements, const AString& separator); static AString fromNumbers(const int8_t* array,const int64_t numberOfElements, const AString& separator); static AString fromNumbers(const int32_t* array,const int64_t numberOfElements, const AString& separator); static AString fromNumbers(const int64_t* array,const int64_t numberOfElements, const AString& separator); - static AString fromNumbers(const double* array, const int64_t numberOfElements, const AString& separator); + static AString fromNumbers(const double* array, const int64_t numberOfElements, const AString& separator, const char format = 'g', const int32_t precision = 6); static AString fromBool(const bool b); AString replaceHtmlSpecialCharactersWithEscapeCharacters() const; -- GitLab From aac1c9656df709297736e7ce39de00f1dd40be4c Mon Sep 17 00:00:00 2001 From: John Harwell Date: Mon, 17 Sep 2018 12:04:53 -0500 Subject: [PATCH 049/427] WB-669 Data Tooltips: Add tooltips for charting; move tooltip text generation into IdentificationTextGenerator; remove developer flag that enabled tooltips; added preference for enabling tooltips (defaulted on) to the Preferences Dialog's ID page. --- src/Brain/DataToolTipsManager.cxx | 284 ++-------- src/Brain/DataToolTipsManager.h | 22 +- src/Brain/IdentificationTextGenerator.cxx | 616 +++++++++++++++++----- src/Brain/IdentificationTextGenerator.h | 29 +- src/Brain/OverlaySet.cxx | 46 +- src/Brain/OverlaySet.h | 2 + src/Common/CaretPreferences.cxx | 27 + src/Common/CaretPreferences.h | 8 + src/Common/DeveloperFlagsEnum.cxx | 4 - src/Common/DeveloperFlagsEnum.h | 3 +- src/Common/HtmlStringBuilder.cxx | 45 ++ src/Common/HtmlStringBuilder.h | 4 + src/GuiQt/BrainOpenGLWidget.cxx | 4 +- src/GuiQt/PreferencesDialog.cxx | 9 + src/GuiQt/PreferencesDialog.h | 1 + 15 files changed, 725 insertions(+), 379 deletions(-) diff --git a/src/Brain/DataToolTipsManager.cxx b/src/Brain/DataToolTipsManager.cxx index d69c3a176..193fa729d 100644 --- a/src/Brain/DataToolTipsManager.cxx +++ b/src/Brain/DataToolTipsManager.cxx @@ -24,32 +24,19 @@ #undef __DATA_TOOL_TIPS_MANAGER_DECLARE__ #include "Brain.h" -#include "Border.h" -#include "BorderFile.h" #include "BrowserTabContent.h" #include "CaretAssert.h" -#include "CaretMappableDataFile.h" -#include "CiftiMappableDataFile.h" #include "EventManager.h" -#include "FociFile.h" -#include "Focus.h" -#include "GiftiLabelTable.h" +#include "IdentificationStringBuilder.h" +#include "IdentificationTextGenerator.h" #include "Overlay.h" #include "OverlaySet.h" #include "SceneClass.h" #include "SceneClassAssistant.h" -#include "SelectionItemBorderSurface.h" -#include "SelectionItemFocusSurface.h" -#include "SelectionItemFocusVolume.h" -#include "SelectionItemSurfaceNode.h" -#include "SelectionItemVoxel.h" #include "SelectionManager.h" -#include "Surface.h" using namespace caret; - - /** * \class caret::DataToolTipsManager * \brief Manages Data ToolTips. @@ -65,7 +52,7 @@ DataToolTipsManager::DataToolTipsManager() m_sceneAssistant = std::unique_ptr(new SceneClassAssistant()); -// EventManager::get()->addEventListener(this, EventTypeEnum::); + /*EventManager::get()->addEventListener(this, EventTypeEnum::);*/ } /** @@ -95,226 +82,15 @@ DataToolTipsManager::getToolTip(const Brain* brain, CaretAssert(browserTab); CaretAssert(selectionManager); - AString text; - - const SelectionItemSurfaceNode* selectedNode = selectionManager->getSurfaceNodeIdentification(); - CaretAssert(selectedNode); - - if (selectedNode->isValid()) { - text = getSurfaceToolTip(brain, - browserTab, - selectionManager, - selectedNode); - } - else { - const SelectionItemVoxel* selectedVoxel = selectionManager->getVoxelIdentification(); - if (selectedVoxel->isValid()) { - text = getVolumeToolTip(brain, - browserTab, - selectionManager, - selectedVoxel); - } - } - - return text; -} + IdentificationTextGenerator itg; + const AString text = itg.createToolTipText(brain, + browserTab, + selectionManager, + this); -/** - * Get text for the tooltip for a selected node. - * - * @param brain - * The Brain. - * @param browserTab - * Browser tab in which tooltip is displayed - * @param selectionManager - * The selection manager. - * @param nodeSelection - * Node selection information. - */ -AString -DataToolTipsManager::getSurfaceToolTip(const Brain* brain, - const BrowserTabContent* browserTab, - const SelectionManager* selectionManager, - const SelectionItemSurfaceNode* nodeSelection) const -{ - AString text; - - const Surface* surface = nodeSelection->getSurface(); - CaretAssert(surface); - int32_t surfaceNumberOfNodes = surface->getNumberOfNodes(); - int32_t surfaceNodeIndex = nodeSelection->getNodeNumber(); - StructureEnum::Enum surfaceStructure = surface->getStructure(); - - if ((surfaceStructure != StructureEnum::INVALID) - && (surfaceNumberOfNodes > 0) - && (surfaceNodeIndex >= 0)) { - - bool showSurfaceFlag = m_showSurfaceViewedFlag; - if (m_showSurfacePrimaryAnatomicalFlag) { - const Surface* anatSurface = brain->getPrimaryAnatomicalSurfaceForStructure(surfaceStructure); - if (anatSurface != NULL) { - if (anatSurface->getNumberOfNodes() == surfaceNumberOfNodes) { - float xyz[3]; - anatSurface->getCoordinate(surfaceNodeIndex, - xyz); - text.appendWithNewLine("Anatomy Surface: " - + AString::fromNumbers(xyz, 3, ", ")); - if (surface == anatSurface) { - showSurfaceFlag = false; - } - } - } - } - - if (showSurfaceFlag) { - float xyz[3]; - surface->getCoordinate(surfaceNodeIndex, - xyz); - text.appendWithNewLine("Surface: " - + AString::fromNumbers(xyz, 3, ", ")); - } - - if (m_showTopLayerFlag) { - const OverlaySet* overlaySet = browserTab->getOverlaySet(); - CaretAssert(overlaySet); - Overlay* overlay = const_cast(overlaySet->getOverlay(0)); - CaretAssert(overlay); - CaretMappableDataFile* mapFile(NULL); - int32_t mapIndex(-1); - overlay->getSelectionData(mapFile, - mapIndex); - if ((mapFile != NULL) - && (mapIndex >= 0)) { - std::vector mapIndices { mapIndex }; - AString textValue; - mapFile->getSurfaceNodeIdentificationForMaps(mapIndices, - surfaceStructure, - surfaceNodeIndex, - surfaceNumberOfNodes, - textValue); - if ( ! textValue.isEmpty()) { - text.appendWithNewLine("Top Layer: " - + textValue); - } - } - } - } - - if (m_showBorderFlag) { - const SelectionItemBorderSurface* borderSelection = selectionManager->getSurfaceBorderIdentification(); - CaretAssert(borderSelection); - const BorderFile* borderFile = borderSelection->getBorderFile(); - const int32_t borderIndex = borderSelection->getBorderIndex(); - if ((borderFile != NULL) - && (borderIndex >= 0)) { - const Border* border = borderFile->getBorder(borderIndex); - if (border != NULL) { - text.appendWithNewLine("Border: " - + border->getName()); - } - } - } - - if (m_showFocusFlag) { - const SelectionItemFocusSurface* focusSelection = selectionManager->getSurfaceFocusIdentification(); - CaretAssert(focusSelection); - const FociFile* fociFile = focusSelection->getFociFile(); - const int32_t focusIndex = focusSelection->getFocusIndex(); - if ((fociFile != NULL) - && (focusIndex >= 0)) { - const Focus* focus = fociFile->getFocus(focusIndex); - if (focus != NULL) { - text.appendWithNewLine("Focus: " - + focus->getName()); - } - } - } - return text; } -/** - * Get text for the tooltip for a selected node. - * - * @param brain - * The Brain. - * @param browserTab - * Browser tab in which tooltip is displayed - * @param selectionManager - * The selection manager. - * @param voxelSelection - * Voxel selection information. - */ -AString -DataToolTipsManager::getVolumeToolTip(const Brain* /*brain*/, - const BrowserTabContent* browserTab, - const SelectionManager* /*selectionManager*/, - const SelectionItemVoxel* voxelSelection) const -{ - AString text; - - const VolumeMappableInterface* selectedVolumeInterface = voxelSelection->getVolumeFile(); - AString filename; - { - const VolumeFile* volumeFile = dynamic_cast(selectedVolumeInterface); - if (volumeFile != NULL) { - filename = volumeFile->getFileNameNoPath(); - } - } - { - const CiftiMappableDataFile* ciftiMapFile = dynamic_cast(selectedVolumeInterface); - if (ciftiMapFile != NULL) { - filename = ciftiMapFile->getFileNameNoPath(); - } - } - double modelXYZ[3]; - voxelSelection->getModelXYZ(modelXYZ); - const float floatXYZ[3] { - static_cast(modelXYZ[0]), - static_cast(modelXYZ[1]), - static_cast(modelXYZ[2]) - }; - int64_t ijk[3]; - voxelSelection->getVoxelIJK(ijk); - - text.appendWithNewLine("Voxel IJK: " - + AString::fromNumbers(ijk, 3, ", ") - + " XYZ: " - + AString::fromNumbers(modelXYZ, 3, ", ") - + " " - + filename); - const float value = selectedVolumeInterface->getVoxelValue(floatXYZ); - text.appendWithNewLine(" Value: " - + AString::number(value, 'f')); - - if (m_showTopLayerFlag) { - const OverlaySet* overlaySet = browserTab->getOverlaySet(); - CaretAssert(overlaySet); - Overlay* overlay = const_cast(overlaySet->getOverlay(0)); - CaretAssert(overlay); - CaretMappableDataFile* mapFile(NULL); - int32_t mapIndex(-1); - overlay->getSelectionData(mapFile, - mapIndex); - if ((mapFile != NULL) - && (mapIndex >= 0)) { - std::vector mapIndices { mapIndex }; - AString textValue; - int64_t ijk[3]; - mapFile->getVolumeVoxelIdentificationForMaps(mapIndices, - floatXYZ, - ijk, - textValue); - if ( ! textValue.isEmpty()) { - text.appendWithNewLine("Top Layer: " - + textValue); - } - } - } - return text; -} - - /** * @return Is tips enabled? */ @@ -378,6 +154,28 @@ DataToolTipsManager::setShowSurfaceViewed(const bool status) m_showSurfaceViewedFlag = status; } +/** + * @return Is show volume underlayenabled? + */ +bool +DataToolTipsManager::isShowVolumeUnderlay() const +{ + return m_showVolumeUnderlayFlag; +} + +/** + * Set status for volume underlay enabled + * + * @param status + * New status. + */ +void +DataToolTipsManager::setShowVolumeUnderlay(const bool status) +{ + m_showVolumeUnderlayFlag = status; +} + + /** * @return Is show top layer enabled? */ @@ -441,6 +239,28 @@ DataToolTipsManager::setShowFocus(const bool status) m_showFocusFlag = status; } +/** + * @return Is show chart enabled? + */ +bool +DataToolTipsManager::isShowChart() const +{ + return m_showChartFlag; +} + +/** + * Set status for show chart + * + * @param status + * New status. + */ +void +DataToolTipsManager::setShowChart(const bool status) +{ + m_showChartFlag = status; +} + + /** * Get a description of this object's content. * @return String describing this object's content. diff --git a/src/Brain/DataToolTipsManager.h b/src/Brain/DataToolTipsManager.h index e1a670922..88769b940 100644 --- a/src/Brain/DataToolTipsManager.h +++ b/src/Brain/DataToolTipsManager.h @@ -66,6 +66,10 @@ namespace caret { void setShowSurfaceViewed(const bool status); + bool isShowVolumeUnderlay() const; + + void setShowVolumeUnderlay(const bool status); + bool isShowTopLayer() const; void setShowTopLayer(const bool status); @@ -78,6 +82,10 @@ namespace caret { void setShowFocus(const bool status); + bool isShowChart() const; + + void setShowChart(const bool status); + // ADD_NEW_METHODS_HERE virtual AString toString() const; @@ -106,16 +114,6 @@ namespace caret { // const SceneClass* sceneClass) = 0; private: - AString getSurfaceToolTip(const Brain* brain, - const BrowserTabContent* browserTab, - const SelectionManager* selectionManager, - const SelectionItemSurfaceNode* nodeSelection) const; - - AString getVolumeToolTip(const Brain* brain, - const BrowserTabContent* browserTab, - const SelectionManager* selectionManager, - const SelectionItemVoxel* voxelSelection) const; - std::unique_ptr m_sceneAssistant; bool m_enabledFlag = true; @@ -124,12 +122,16 @@ namespace caret { bool m_showSurfaceViewedFlag = true; + bool m_showVolumeUnderlayFlag = true; + bool m_showTopLayerFlag = true; bool m_showBorderFlag = true; bool m_showFocusFlag = true; + bool m_showChartFlag = true; + // ADD_NEW_MEMBERS_HERE }; diff --git a/src/Brain/IdentificationTextGenerator.cxx b/src/Brain/IdentificationTextGenerator.cxx index d8343cccd..cc4a43450 100644 --- a/src/Brain/IdentificationTextGenerator.cxx +++ b/src/Brain/IdentificationTextGenerator.cxx @@ -41,6 +41,7 @@ #include "CiftiMappableConnectivityMatrixDataFile.h" #include "CiftiMappableDataFile.h" #include "CaretVolumeExtension.h" +#include "DataToolTipsManager.h" #include "EventBrowserTabGetAll.h" #include "EventManager.h" #include "FileInformation.h" @@ -126,17 +127,19 @@ IdentificationTextGenerator::createIdentificationText(const SelectionManager* id surfaceID); this->generateSurfaceBorderIdentifcationText(idText, - idManager->getSurfaceBorderIdentification()); + idManager->getSurfaceBorderIdentification(), + false); this->generateSurfaceFociIdentifcationText(idText, - idManager->getSurfaceFocusIdentification()); + idManager->getSurfaceFocusIdentification(), + false); this->generateVolumeFociIdentifcationText(idText, idManager->getVolumeFocusIdentification()); this->generateVolumeIdentificationText(idText, brain, - idManager->getVoxelIdentification()); + idManager->getVoxelIdentification()); this->generateChartDataSeriesIdentificationText(idText, idManager->getChartDataSeriesIdentification()); @@ -168,6 +171,64 @@ IdentificationTextGenerator::createIdentificationText(const SelectionManager* id return idText.toString(); } +/** + * Get text for the tooltip for a selected node. + * + * @param brain + * The Brain. + * @param browserTab + * Browser tab in which tooltip is displayed + * @param selectionManager + * The selection manager. + * @param dataToolTipsManager + * The data tooltips manager + * @param idText + * String builder for identification text. + */ +AString +IdentificationTextGenerator::createToolTipText(const Brain* brain, + const BrowserTabContent* browserTab, + const SelectionManager* selectionManager, + const DataToolTipsManager* dataToolTipsManager) const +{ + CaretAssert(brain); + CaretAssert(browserTab); + CaretAssert(selectionManager); + CaretAssert(dataToolTipsManager); + + const SelectionItemSurfaceNode* selectedNode = selectionManager->getSurfaceNodeIdentification(); + const SelectionItemVoxel* selectedVoxel = selectionManager->getVoxelIdentification(); + + IdentificationStringBuilder idText; + + if (selectedNode->isValid()) { + generateSurfaceToolTip(brain, + browserTab, + selectionManager, + dataToolTipsManager, + idText); + } + else if (selectedVoxel->isValid()) { + generateVolumeToolTip(browserTab, + selectionManager, + dataToolTipsManager, + idText); + } + else { + generateChartToolTip(selectionManager, + dataToolTipsManager, + idText); + } + + AString text; + if (idText.length() > 0) { + text = idText.toStringWithHtmlBodyForToolTip(); + } + + return text; +} + + /** * Generate identification text for volume voxel identification. * @@ -1146,10 +1207,13 @@ IdentificationTextGenerator::generateChartTimeSeriesIdentificationText(Identific * String builder for identification text. * @param idSurfaceBorder * Information for surface border ID. + * @param toolTipFlag + * True if this is for tooltip. */ void IdentificationTextGenerator::generateSurfaceBorderIdentifcationText(IdentificationStringBuilder& idText, - const SelectionItemBorderSurface* idSurfaceBorder) const + const SelectionItemBorderSurface* idSurfaceBorder, + const bool toolTipFlag) const { if (idSurfaceBorder->isValid()) { const Border* border = idSurfaceBorder->getBorder(); @@ -1157,23 +1221,36 @@ IdentificationTextGenerator::generateSurfaceBorderIdentifcationText(Identificati float xyz[3]; spi->getProjectedPosition(*idSurfaceBorder->getSurface(), xyz, false); - AString boldText = ("BORDER " - + StructureEnum::toGuiName(spi->getStructure()) - + " Name: " - + border->getName()); - if (border->getClassName().isEmpty() == false) { - boldText += (" ClassName: " - + border->getClassName() - + ": "); + if (toolTipFlag) { + bool indentFlag = false; + idText.addLine(indentFlag, + "Border", + border->getName()); + indentFlag = true; + idText.addLine(indentFlag, + "XYZ", + AString::fromNumbers(xyz, 3, ",")); + } + else { + AString boldText = ("BORDER " + + StructureEnum::toGuiName(spi->getStructure()) + + " Name: " + + border->getName()); + if (border->getClassName().isEmpty() == false) { + boldText += (" ClassName: " + + border->getClassName() + + ": "); + } + + const AString text = ("(" + + AString::number(idSurfaceBorder->getBorderIndex()) + + "," + + AString::number(idSurfaceBorder->getBorderPointIndex()) + + ") (" + + AString::fromNumbers(xyz, 3, ",") + + ")"); + idText.addLine(false, boldText, text); } - const AString text = ("(" - + AString::number(idSurfaceBorder->getBorderIndex()) - + "," - + AString::number(idSurfaceBorder->getBorderPointIndex()) - + ") (" - + AString::fromNumbers(xyz, 3, ",") - + ")"); - idText.addLine(false, boldText, text); } } @@ -1183,121 +1260,139 @@ IdentificationTextGenerator::generateSurfaceBorderIdentifcationText(Identificati * String builder for identification text. * @param idSurfaceFocus * Information for surface focus ID. - */void + * @param toolTipFlag + * True if this is for tooltip. + */ +void IdentificationTextGenerator::generateSurfaceFociIdentifcationText(IdentificationStringBuilder& idText, - const SelectionItemFocusSurface* idSurfaceFocus) const + const SelectionItemFocusSurface* idSurfaceFocus, + const bool toolTipFlag) const { if (idSurfaceFocus->isValid()) { const Focus* focus = idSurfaceFocus->getFocus(); - idText.addLine(false, - "FOCUS", - focus->getName()); - - idText.addLine(true, - "Index", - AString::number(idSurfaceFocus->getFocusIndex())); - const int32_t projectionIndex = idSurfaceFocus->getFocusProjectionIndex(); const SurfaceProjectedItem* spi = focus->getProjection(projectionIndex); - float xyzProj[3]; - spi->getProjectedPosition(*idSurfaceFocus->getSurface(), xyzProj, false); float xyzStereo[3]; spi->getStereotaxicXYZ(xyzStereo); - - idText.addLine(true, - "Structure", - StructureEnum::toGuiName(spi->getStructure())); - - if (spi->isStereotaxicXYZValid()) { - idText.addLine(true, - "XYZ (Stereotaxic)", - xyzStereo, - 3, - true); + if (toolTipFlag) { + bool indentFlag = false; + idText.addLine(indentFlag, + "Focus", + focus->getName()); + indentFlag = true; + idText.addLine(indentFlag, + "XYZ", + (spi->isStereotaxicXYZValid() + ? AString::fromNumbers(xyzStereo, 3, ",") + : "Invalid")); } else { + idText.addLine(false, + "FOCUS", + focus->getName()); + idText.addLine(true, - "XYZ (Stereotaxic)", - "Invalid"); - } - - bool projValid = false; - AString xyzProjName = "XYZ (Projected)"; - if (spi->getBarycentricProjection()->isValid()) { - xyzProjName = "XYZ (Projected to Triangle)"; - projValid = true; - } - else if (spi->getVanEssenProjection()->isValid()) { - xyzProjName = "XYZ (Projected to Edge)"; - projValid = true; - } - if (projValid) { - idText.addLine(true, - xyzProjName, - xyzProj, - 3, - true); - } - else { + "Index", + AString::number(idSurfaceFocus->getFocusIndex())); + + float xyzProj[3]; + spi->getProjectedPosition(*idSurfaceFocus->getSurface(), xyzProj, false); + idText.addLine(true, - xyzProjName, - "Invalid"); - } - - const int32_t numberOfProjections = focus->getNumberOfProjections(); - for (int32_t i = 0; i < numberOfProjections; i++) { - if (i != projectionIndex) { - const SurfaceProjectedItem* proj = focus->getProjection(i); - AString projTypeName = ""; - if (proj->getBarycentricProjection()->isValid()) { - projTypeName = "Triangle"; - - } - else if (proj->getVanEssenProjection()->isValid()) { - projTypeName = "Edge"; - } - if (projTypeName.isEmpty() == false) { - const AString txt = (StructureEnum::toGuiName(proj->getStructure()) - + " (" - + projTypeName - + ")"); - - idText.addLine(true, - "Ambiguous Projection", - txt); + "Structure", + StructureEnum::toGuiName(spi->getStructure())); + + if (spi->isStereotaxicXYZValid()) { + idText.addLine(true, + "XYZ (Stereotaxic)", + xyzStereo, + 3, + true); + } + else { + idText.addLine(true, + "XYZ (Stereotaxic)", + "Invalid"); + } + + bool projValid = false; + AString xyzProjName = "XYZ (Projected)"; + if (spi->getBarycentricProjection()->isValid()) { + xyzProjName = "XYZ (Projected to Triangle)"; + projValid = true; + } + else if (spi->getVanEssenProjection()->isValid()) { + xyzProjName = "XYZ (Projected to Edge)"; + projValid = true; + } + if (projValid) { + idText.addLine(true, + xyzProjName, + xyzProj, + 3, + true); + } + else { + idText.addLine(true, + xyzProjName, + "Invalid"); + } + + const int32_t numberOfProjections = focus->getNumberOfProjections(); + for (int32_t i = 0; i < numberOfProjections; i++) { + if (i != projectionIndex) { + const SurfaceProjectedItem* proj = focus->getProjection(i); + AString projTypeName = ""; + if (proj->getBarycentricProjection()->isValid()) { + projTypeName = "Triangle"; + + } + else if (proj->getVanEssenProjection()->isValid()) { + projTypeName = "Edge"; + } + if (projTypeName.isEmpty() == false) { + const AString txt = (StructureEnum::toGuiName(proj->getStructure()) + + " (" + + projTypeName + + ")"); + + idText.addLine(true, + "Ambiguous Projection", + txt); + } } } + + idText.addLine(true, + "Area", + focus->getArea()); + + idText.addLine(true, + "Class Name", + focus->getClassName()); + + idText.addLine(true, + "Comment", + focus->getComment()); + + idText.addLine(true, + "Extent", + focus->getExtent(), + true); + + idText.addLine(true, + "Geography", + focus->getGeography()); + + idText.addLine(true, + "Region of Interest", + focus->getRegionOfInterest()); + + idText.addLine(true, + "Statistic", + focus->getStatistic()); + } - - idText.addLine(true, - "Area", - focus->getArea()); - - idText.addLine(true, - "Class Name", - focus->getClassName()); - - idText.addLine(true, - "Comment", - focus->getComment()); - - idText.addLine(true, - "Extent", - focus->getExtent(), - true); - - idText.addLine(true, - "Geography", - focus->getGeography()); - - idText.addLine(true, - "Region of Interest", - focus->getRegionOfInterest()); - - idText.addLine(true, - "Statistic", - focus->getStatistic()); - } } @@ -1413,6 +1508,287 @@ IdentificationTextGenerator::generateImageIdentificationText(IdentificationStrin } } +/** + * Get text for the tooltip for a selected node. + * + * @param brain + * The Brain. + * @param browserTab + * Browser tab in which tooltip is displayed + * @param selectionManager + * The selection manager. + * @param dataToolTipsManager + * The data tooltips manager + * @param idText + * String builder for identification text. + */ +void +IdentificationTextGenerator::generateSurfaceToolTip(const Brain* brain, + const BrowserTabContent* browserTab, + const SelectionManager* selectionManager, + const DataToolTipsManager* dataToolTipsManager, + IdentificationStringBuilder& idText) const +{ + const SelectionItemSurfaceNode* nodeSelection = selectionManager->getSurfaceNodeIdentification(); + CaretAssert(nodeSelection); + if (nodeSelection->isValid()) { + const Surface* surface = nodeSelection->getSurface(); + CaretAssert(surface); + int32_t surfaceNumberOfNodes = surface->getNumberOfNodes(); + int32_t surfaceNodeIndex = nodeSelection->getNodeNumber(); + StructureEnum::Enum surfaceStructure = surface->getStructure(); + + bool indentFlag = false; + if ((surfaceStructure != StructureEnum::INVALID) + && (surfaceNumberOfNodes > 0) + && (surfaceNodeIndex >= 0)) { + + bool addVertexFlag(false); + bool showSurfaceFlag = dataToolTipsManager->isShowSurfaceViewed(); + if (dataToolTipsManager->isShowSurfacePrimaryAnatomical()) { + const Surface* anatSurface = brain->getPrimaryAnatomicalSurfaceForStructure(surfaceStructure); + if (anatSurface != NULL) { + if (anatSurface->getNumberOfNodes() == surfaceNumberOfNodes) { + float xyz[3]; + anatSurface->getCoordinate(surfaceNodeIndex, + xyz); + idText.addLine(indentFlag, + "Vertex", + AString::number(surfaceNodeIndex)); + indentFlag = true; + addVertexFlag = false; + + idText.addLine(indentFlag, + "Anatomy Surface", + AString::fromNumbers(xyz, 3, ", ", 'f', 2)); + if (surface == anatSurface) { + showSurfaceFlag = false; + } + } + } + } + + if (showSurfaceFlag) { + float xyz[3]; + surface->getCoordinate(surfaceNodeIndex, + xyz); + if (addVertexFlag) { + idText.addLine(indentFlag, + "Vertex", + AString::number(surfaceNodeIndex)); + indentFlag = true; + } + + idText.addLine(indentFlag, + (SurfaceTypeEnum::toGuiName(surface->getSurfaceType()) + + " Surface"), + AString::fromNumbers(xyz, 3, ", ")); + } + + if (dataToolTipsManager->isShowTopLayer()) { + const OverlaySet* overlaySet = browserTab->getOverlaySet(); + CaretAssert(overlaySet); + Overlay* overlay = const_cast(overlaySet->getOverlay(0)); + CaretAssert(overlay); + CaretMappableDataFile* mapFile(NULL); + int32_t mapIndex(-1); + overlay->getSelectionData(mapFile, + mapIndex); + if ((mapFile != NULL) + && (mapIndex >= 0)) { + std::vector mapIndices { mapIndex }; + AString textValue; + mapFile->getSurfaceNodeIdentificationForMaps(mapIndices, + surfaceStructure, + surfaceNodeIndex, + surfaceNumberOfNodes, + textValue); + if ( ! textValue.isEmpty()) { + idText.addLine(indentFlag, + "Top Layer", + textValue); + } + } + } + } + } + + if (dataToolTipsManager->isShowBorder()) { + const SelectionItemBorderSurface* borderSelection = selectionManager->getSurfaceBorderIdentification(); + CaretAssert(borderSelection); + if (borderSelection->isValid()) { + generateSurfaceBorderIdentifcationText(idText, + borderSelection, + true); + +// const BorderFile* borderFile = borderSelection->getBorderFile(); +// const int32_t borderIndex = borderSelection->getBorderIndex(); +// if ((borderFile != NULL) +// && (borderIndex >= 0)) { +// const Border* border = borderFile->getBorder(borderIndex); +// if (border != NULL) { +// text.appendWithNewLine("Border: " +// + border->getName()); +// } +// } + } + } + + if (dataToolTipsManager->isShowFocus()) { + const SelectionItemFocusSurface* focusSelection = selectionManager->getSurfaceFocusIdentification(); + CaretAssert(focusSelection); + if (focusSelection->isValid()) { + generateSurfaceFociIdentifcationText(idText, + focusSelection, + true); + + +// const FociFile* fociFile = focusSelection->getFociFile(); +// const int32_t focusIndex = focusSelection->getFocusIndex(); +// if ((fociFile != NULL) +// && (focusIndex >= 0)) { +// const Focus* focus = fociFile->getFocus(focusIndex); +// if (focus != NULL) { +// text.appendWithNewLine("Focus: " +// + focus->getName()); +// } +// } + } + } +} + +/** + * Get text for the tooltip for a selected node. + * + * @param browserTab + * Browser tab in which tooltip is displayed + * @param selectionManager + * The selection manager. + * @param dataToolTipsManager + * The data tooltips manager + * @param idText + * String builder for identification text. + */ +void +IdentificationTextGenerator::generateVolumeToolTip(const BrowserTabContent* browserTab, + const SelectionManager* selectionManager, + const DataToolTipsManager* dataToolTipsManager, + IdentificationStringBuilder& idText) const +{ + const SelectionItemVoxel* voxelSelection = selectionManager->getVoxelIdentification(); + + OverlaySet* overlaySet = const_cast(browserTab->getOverlaySet()); + CaretAssert(overlaySet); + + double selectionXYZ[3]; + voxelSelection->getModelXYZ(selectionXYZ); + float xyz[3] { + static_cast(selectionXYZ[0]), + static_cast(selectionXYZ[1]), + static_cast(selectionXYZ[2]) + }; + + bool indentFlag = false; + if (dataToolTipsManager->isShowVolumeUnderlay()) { + Overlay* volumeUnderlay = overlaySet->getUnderlayContainingVolume(); + if (volumeUnderlay != NULL) { + CaretMappableDataFile* mapFile = NULL; + int32_t mapIndex(-1); + volumeUnderlay->getSelectionData(mapFile, + mapIndex); + + VolumeMappableInterface* underlayVolumeInterface = NULL; + if (mapFile != NULL) { + underlayVolumeInterface = dynamic_cast(mapFile); + CaretAssert(underlayVolumeInterface == overlaySet->getUnderlayVolume()); + } + + if (underlayVolumeInterface != NULL) { + /* + * Update IJK and XYZ since selection XYZ may be + * a different volume file. + */ + int64_t selectionIJK[3]; + voxelSelection->getVoxelIJK(selectionIJK); + int64_t ijk[3] { selectionIJK[0], selectionIJK[1], selectionIJK[2] }; + + + bool validFlag(false); + const float value = underlayVolumeInterface->getVoxelValue(xyz[0], xyz[1], xyz[2], + &validFlag, + mapIndex); + if (validFlag) { + underlayVolumeInterface->enclosingVoxel(xyz[0], xyz[1], xyz[2], + ijk[0], ijk[1], ijk[2]); + underlayVolumeInterface->indexToSpace(ijk, xyz); + idText.addLine(indentFlag, + "Underlay Value", + AString::number(value, 'f')); + indentFlag = true; + idText.addLine(indentFlag, + "IJK: ", + AString::fromNumbers(ijk, 3, ", ")); + idText.addLine(indentFlag, + "XYZ", + AString::fromNumbers(xyz, 3, ", ", 'f', 1)); + } + } + } + } + + if (dataToolTipsManager->isShowTopLayer()) { + Overlay* overlay = const_cast(overlaySet->getOverlay(0)); + CaretAssert(overlay); + CaretMappableDataFile* mapFile(NULL); + int32_t mapIndex(-1); + overlay->getSelectionData(mapFile, + mapIndex); + if ((mapFile != NULL) + && (mapIndex >= 0)) { + std::vector mapIndices { mapIndex }; + AString textValue; + int64_t ijk[3]; + mapFile->getVolumeVoxelIdentificationForMaps(mapIndices, + xyz, + ijk, + textValue); + if ( ! textValue.isEmpty()) { + idText.addLine(indentFlag, + ("Top Layer: " + + textValue)); + } + } + } + +} + +/** + * Get text for the tooltip for a selected node. + * + * @param selectionManager + * The selection manager. + * @param dataToolTipsManager + * The data tooltips manager + * @param idText + * String builder for identification text. + */ +void +IdentificationTextGenerator::generateChartToolTip(const SelectionManager* selectionManager, + const DataToolTipsManager* dataToolTipsManager, + IdentificationStringBuilder& idText) const +{ + if (dataToolTipsManager->isShowChart()) { + this->generateChartTwoHistogramIdentificationText(idText, + selectionManager->getChartTwoHistogramIdentification()); + + this->generateChartTwoLineSeriesIdentificationText(idText, + selectionManager->getChartTwoLineSeriesIdentification()); + + this->generateChartTwoMatrixIdentificationText(idText, + selectionManager->getChartTwoMatrixIdentification()); + } +} + /** diff --git a/src/Brain/IdentificationTextGenerator.h b/src/Brain/IdentificationTextGenerator.h index 4fa605add..8dbc10621 100644 --- a/src/Brain/IdentificationTextGenerator.h +++ b/src/Brain/IdentificationTextGenerator.h @@ -30,6 +30,7 @@ namespace caret { class BrowserTabContent; class CaretMappableDataFile; class ChartDataSource; + class DataToolTipsManager; class MapFileDataSelector; class SelectionItemBorderSurface; class SelectionItemChartDataSeries; @@ -58,6 +59,11 @@ namespace caret { AString createIdentificationText(const SelectionManager* idManager, const Brain* brain) const; + AString createToolTipText(const Brain* brain, + const BrowserTabContent* browserTab, + const SelectionManager* selectionManager, + const DataToolTipsManager* dataToolTipsManager) const; + private: IdentificationTextGenerator(const IdentificationTextGenerator&); @@ -67,11 +73,28 @@ namespace caret { virtual AString toString() const; private: + void generateSurfaceToolTip(const Brain* brain, + const BrowserTabContent* browserTab, + const SelectionManager* selectionManager, + const DataToolTipsManager* dataToolTipsManager, + IdentificationStringBuilder& idText) const; + + void generateVolumeToolTip(const BrowserTabContent* browserTab, + const SelectionManager* selectionManager, + const DataToolTipsManager* dataToolTipsManager, + IdentificationStringBuilder& idText) const; + + void generateChartToolTip(const SelectionManager* selectionManager, + const DataToolTipsManager* dataToolTipsManager, + IdentificationStringBuilder& idText) const; + void generateSurfaceBorderIdentifcationText(IdentificationStringBuilder& idText, - const SelectionItemBorderSurface* idSurfaceBorder) const; + const SelectionItemBorderSurface* idSurfaceBorder, + const bool toolTipFlag) const; void generateSurfaceFociIdentifcationText(IdentificationStringBuilder& idText, - const SelectionItemFocusSurface* idSurfaceFocus) const; + const SelectionItemFocusSurface* idSurfaceFocus, + const bool toolTipFlag) const; void generateVolumeFociIdentifcationText(IdentificationStringBuilder& idText, const SelectionItemFocusVolume* idVolumeFocus) const; @@ -120,6 +143,8 @@ namespace caret { void generateMapFileSelectorText(IdentificationStringBuilder& idText, const MapFileDataSelector* mapFileDataSelector) const; + + friend class DataToolTipsManager; }; #ifdef __IDENTIFICATION_TEXT_GENERATOR_DECLARE__ diff --git a/src/Brain/OverlaySet.cxx b/src/Brain/OverlaySet.cxx index 7b2100a33..30e152cdd 100644 --- a/src/Brain/OverlaySet.cxx +++ b/src/Brain/OverlaySet.cxx @@ -173,23 +173,25 @@ OverlaySet::getUnderlay() * @return Returns the bottom-most overlay that is set a a volume file. * Will return NULL if no, enabled overlays are set to a volume file. */ -VolumeMappableInterface* -OverlaySet::getUnderlayVolume() +Overlay* +OverlaySet::getUnderlayContainingVolume() { - VolumeMappableInterface* vf = NULL; + Overlay* underlayOut(NULL); const int32_t numOverlays = getNumberOfDisplayedOverlays(); for (int32_t i = (numOverlays - 1); i >= 0; i--) { if (m_overlays[i]->isEnabled()) { CaretMappableDataFile* mapFile = NULL; int32_t mapIndex; + CaretAssertArrayIndex(m_overlays, BrainConstants::MAXIMUM_NUMBER_OF_OVERLAYS, i); m_overlays[i]->getSelectionData(mapFile, mapIndex); if (mapFile != NULL) { if (mapFile->isVolumeMappable()) { - vf = dynamic_cast(mapFile); + const VolumeMappableInterface* vf = dynamic_cast(mapFile); if (vf != NULL) { + underlayOut = m_overlays[i]; break; } } @@ -197,7 +199,7 @@ OverlaySet::getUnderlayVolume() } } - if (vf == NULL) { + if (underlayOut == NULL) { /* * If we are here, either there are no volume files or * no overlays are enabled containing a volume file. @@ -207,18 +209,48 @@ OverlaySet::getUnderlayVolume() for (int32_t i = 0; i < numOverlays; i++) { CaretMappableDataFile* mapFile = NULL; int32_t mapIndex; + CaretAssertArrayIndex(m_overlays, BrainConstants::MAXIMUM_NUMBER_OF_OVERLAYS, i); m_overlays[i]->getSelectionData(mapFile, mapIndex); if (mapFile != NULL) { if (mapFile->isVolumeMappable()) { - vf = dynamic_cast(mapFile); - break; + const VolumeMappableInterface* vf = dynamic_cast(mapFile); + if (vf != NULL) { + underlayOut = m_overlays[i]; + break; + } } } } } + return underlayOut; +} + +/* + * Get the bottom-most overlay that is a volume file for the given + * browser tab and return its volume file. + * @param browserTabContent + * Content of browser tab. + * @return Returns the bottom-most overlay that is set a a volume file. + * Will return NULL if no, enabled overlays are set to a volume file. + */ +VolumeMappableInterface* +OverlaySet::getUnderlayVolume() +{ + VolumeMappableInterface* vf = NULL; + + Overlay* underlay = getUnderlayContainingVolume(); + if (underlay != NULL) { + CaretMappableDataFile* mapFile = NULL; + int32_t mapIndex; + underlay->getSelectionData(mapFile, mapIndex); + if (mapFile != NULL) { + vf = dynamic_cast(mapFile); + } + } + return vf; } diff --git a/src/Brain/OverlaySet.h b/src/Brain/OverlaySet.h index 0fa269756..bd3a57fd3 100644 --- a/src/Brain/OverlaySet.h +++ b/src/Brain/OverlaySet.h @@ -58,6 +58,8 @@ namespace caret { VolumeMappableInterface* getUnderlayVolume(); + Overlay* getUnderlayContainingVolume(); + Overlay* getOverlay(const int32_t overlayNumber); const Overlay* getOverlay(const int32_t overlayNumber) const; diff --git a/src/Common/CaretPreferences.cxx b/src/Common/CaretPreferences.cxx index 0038e6882..84cf1e457 100644 --- a/src/Common/CaretPreferences.cxx +++ b/src/Common/CaretPreferences.cxx @@ -1255,6 +1255,30 @@ CaretPreferences::setDevelopMenuEnabled(const bool enabled) this->qSettings->sync(); } +/** + * @return Is Show Data ToolTips enabled? + */ +bool +CaretPreferences::isShowDataToolTipsEnabled() const +{ + return this->dataToolTipsEnabled; +} + +/** + * Set Show Data ToolTips enabled. + * @param enabled + * New status. + */ +void +CaretPreferences::setShowDataToolTipsEnabled(const bool enabled) +{ + this->dataToolTipsEnabled = enabled; + this->setBoolean(CaretPreferences::NAME_DATA_TOOL_TIPS, + this->dataToolTipsEnabled); + this->qSettings->sync(); +} + + /** * @param Is yoking defaulted on ? */ @@ -1505,6 +1529,9 @@ CaretPreferences::readPreferences() this->developMenuEnabled = this->getBoolean(CaretPreferences::NAME_DEVELOP_MENU, false); + + this->dataToolTipsEnabled = this->getBoolean(CaretPreferences::NAME_DATA_TOOL_TIPS, + true); this->yokingDefaultedOn = this->getBoolean(CaretPreferences::NAME_YOKING_DEFAULT_ON, true); diff --git a/src/Common/CaretPreferences.h b/src/Common/CaretPreferences.h index 20db3f72b..3abf15d54 100644 --- a/src/Common/CaretPreferences.h +++ b/src/Common/CaretPreferences.h @@ -120,6 +120,10 @@ namespace caret { void setDevelopMenuEnabled(const bool enabled); + bool isShowDataToolTipsEnabled() const; + + void setShowDataToolTipsEnabled(const bool enabled); + void readTileTabsConfigurations(const bool performSync = true); std::vector getTileTabsConfigurationsSortedByName() const; @@ -287,6 +291,8 @@ namespace caret { bool yokingDefaultedOn; + bool dataToolTipsEnabled; + AString remoteFileUserName; AString remoteFilePassword; bool remoteFileLoginSaved; @@ -315,6 +321,7 @@ namespace caret { static const AString NAME_COLOR_CHART_MATRIX_GRID_LINES; static const AString NAME_COLOR_CHART_HISTOGRAM_THRESHOLD; static const AString NAME_DEVELOP_MENU; + static const AString NAME_DATA_TOOL_TIPS; static const AString NAME_DYNAMIC_CONNECTIVITY_ON; static const AString NAME_IMAGE_CAPTURE_METHOD; static const AString NAME_LOGGING_LEVEL; @@ -357,6 +364,7 @@ namespace caret { const AString CaretPreferences::NAME_COLOR_CHART_MATRIX_GRID_LINES = "colorChartMatrixGridLines"; const AString CaretPreferences::NAME_COLOR_CHART_HISTOGRAM_THRESHOLD = "colorChartHistogramThreshold"; const AString CaretPreferences::NAME_DEVELOP_MENU = "developMenu"; + const AString CaretPreferences::NAME_DATA_TOOL_TIPS = "dataToolTips"; const AString CaretPreferences::NAME_DYNAMIC_CONNECTIVITY_ON = "dynamicConnectivityDefaultedOn"; const AString CaretPreferences::NAME_IMAGE_CAPTURE_METHOD = "imageCaptureMethod"; const AString CaretPreferences::NAME_LOGGING_LEVEL = "loggingLevel"; diff --git a/src/Common/DeveloperFlagsEnum.cxx b/src/Common/DeveloperFlagsEnum.cxx index b911d2a4f..4cae6e410 100644 --- a/src/Common/DeveloperFlagsEnum.cxx +++ b/src/Common/DeveloperFlagsEnum.cxx @@ -117,10 +117,6 @@ DeveloperFlagsEnum::initialize() "DEVELOPER_FLAG_FLIP_PALETTE_NOT_DATA", "Flip Palette Not Data", false)); - enumData.push_back(DeveloperFlagsEnum(DEVELOPER_FLAG_BRAIN_TIPS, - "DEVELOPER_FLAG_BRAIN_TIPS", - "Enable Data Tool Tips", - false)); } /** diff --git a/src/Common/DeveloperFlagsEnum.h b/src/Common/DeveloperFlagsEnum.h index 45bfaed06..2a352066e 100644 --- a/src/Common/DeveloperFlagsEnum.h +++ b/src/Common/DeveloperFlagsEnum.h @@ -36,8 +36,7 @@ public: */ enum Enum { DEVELOPER_FLAG_UNUSED, - DEVELOPER_FLAG_FLIP_PALETTE_NOT_DATA, - DEVELOPER_FLAG_BRAIN_TIPS + DEVELOPER_FLAG_FLIP_PALETTE_NOT_DATA }; ~DeveloperFlagsEnum(); diff --git a/src/Common/HtmlStringBuilder.cxx b/src/Common/HtmlStringBuilder.cxx index 78be7e493..ab14f8f90 100644 --- a/src/Common/HtmlStringBuilder.cxx +++ b/src/Common/HtmlStringBuilder.cxx @@ -259,6 +259,19 @@ HtmlStringBuilder::toString() const return this->stringBuilder; } +/** + * Convert to a string in HTML format WITH leading and trailing + * HTML and BODY tags. + * + * @return String containing text. + * + */ +AString +HtmlStringBuilder::toStringWithHtmlBodyForToolTip() +{ + return toStringWithHtmlBodyPrivate(true); +} + /** * Convert to a string in HTML format WITH leading and trailing * HTML and BODY tags. @@ -268,14 +281,46 @@ HtmlStringBuilder::toString() const */ AString HtmlStringBuilder::toStringWithHtmlBody() +{ + return toStringWithHtmlBodyPrivate(false); +} + +/** + * Convert to a string in HTML format WITH leading and trailing + * HTML and BODY tags. + * + * @param toolTipFlag + * If true, set the style so text does not wrap when + * placed into a QToolTip (see QToolTip documentation). + * @return String containing text. + * + */ +AString +HtmlStringBuilder::toStringWithHtmlBodyPrivate(const bool toolTipFlag) { AString sb; sb.reserve(this->stringBuilder.length() + 100); sb.append(""); + if (toolTipFlag) { + sb.append("

"); + } + + /* + * If the string ends with a line break, remove the line break + */ + const AString lastBreak("

"); + if (this->stringBuilder.endsWith(lastBreak)) { + this->stringBuilder.resize(this->stringBuilder.length() - lastBreak.length()); + } sb.append(this->stringBuilder); + + if (toolTipFlag) { + sb.append("

"); + } sb.append(""); return sb; } + diff --git a/src/Common/HtmlStringBuilder.h b/src/Common/HtmlStringBuilder.h index 9c4b9800e..c3026c30d 100644 --- a/src/Common/HtmlStringBuilder.h +++ b/src/Common/HtmlStringBuilder.h @@ -83,7 +83,11 @@ namespace caret { AString toStringWithHtmlBody(); + AString toStringWithHtmlBodyForToolTip(); + private: + AString toStringWithHtmlBodyPrivate(const bool toolTipFlag); + AString stringBuilder; }; diff --git a/src/GuiQt/BrainOpenGLWidget.cxx b/src/GuiQt/BrainOpenGLWidget.cxx index dbddee038..c4f5b302c 100644 --- a/src/GuiQt/BrainOpenGLWidget.cxx +++ b/src/GuiQt/BrainOpenGLWidget.cxx @@ -657,8 +657,8 @@ BrainOpenGLWidget::paintGL() bool BrainOpenGLWidget::event(QEvent* event) { - const bool toolTipsEnabled = DeveloperFlagsEnum::isFlag(DeveloperFlagsEnum::DEVELOPER_FLAG_BRAIN_TIPS); - if (toolTipsEnabled) { + const CaretPreferences* prefs = SessionManager::get()->getCaretPreferences(); + if (prefs->isShowDataToolTipsEnabled()) { if (event->type() == QEvent::ToolTip) { QHelpEvent* helpEvent = static_cast(event); CaretAssert(helpEvent); diff --git a/src/GuiQt/PreferencesDialog.cxx b/src/GuiQt/PreferencesDialog.cxx index 6ed6728be..aff20b762 100644 --- a/src/GuiQt/PreferencesDialog.cxx +++ b/src/GuiQt/PreferencesDialog.cxx @@ -458,6 +458,10 @@ PreferencesDialog::createIdentificationSymbolWidget() QObject::connect(m_volumeIdentificationSymbolComboBox, SIGNAL(statusChanged(bool)), this, SLOT(identificationSymbolToggled())); + m_dataToolTipsComboBox = new WuQTrueFalseComboBox("On", "Off", this); + QObject::connect(m_dataToolTipsComboBox, SIGNAL(statusChanged(bool)), + this, SLOT(identificationSymbolToggled())); + QGridLayout* gridLayout = new QGridLayout(); int row = gridLayout->rowCount(); gridLayout->addWidget(infoLabel, @@ -468,6 +472,9 @@ PreferencesDialog::createIdentificationSymbolWidget() addWidgetToLayout(gridLayout, "Show Volume ID Symbols: ", m_volumeIdentificationSymbolComboBox->getWidget()); + addWidgetToLayout(gridLayout, + "Show Data Tool Tips: ", + m_dataToolTipsComboBox->getWidget()); QWidget* widget = new QWidget(); QVBoxLayout* layout = new QVBoxLayout(widget); @@ -487,6 +494,7 @@ PreferencesDialog::updateIdentificationWidget(CaretPreferences* prefs) { m_surfaceIdentificationSymbolComboBox->setStatus(prefs->isShowSurfaceIdentificationSymbols()); m_volumeIdentificationSymbolComboBox->setStatus(prefs->isShowVolumeIdentificationSymbols()); + m_dataToolTipsComboBox->setStatus(prefs->isShowDataToolTipsEnabled()); } /** @@ -498,6 +506,7 @@ PreferencesDialog::identificationSymbolToggled() CaretPreferences* prefs = SessionManager::get()->getCaretPreferences(); prefs->setShowSurfaceIdentificationSymbols(m_surfaceIdentificationSymbolComboBox->isTrue()); prefs->setShowVolumeIdentificationSymbols(m_volumeIdentificationSymbolComboBox->isTrue()); + prefs->setShowDataToolTipsEnabled(m_dataToolTipsComboBox->isTrue()); } /** diff --git a/src/GuiQt/PreferencesDialog.h b/src/GuiQt/PreferencesDialog.h index 5e915f4ff..7bcac0dc6 100644 --- a/src/GuiQt/PreferencesDialog.h +++ b/src/GuiQt/PreferencesDialog.h @@ -153,6 +153,7 @@ namespace caret { WuQTrueFalseComboBox* m_surfaceIdentificationSymbolComboBox; WuQTrueFalseComboBox* m_volumeIdentificationSymbolComboBox; + WuQTrueFalseComboBox* m_dataToolTipsComboBox; WuQWidgetObjectGroup* m_allWidgets; }; -- GitLab From 106ad9cadcf45ea1895fbbb48f1467bdae8b3b9c Mon Sep 17 00:00:00 2001 From: John Harwell Date: Mon, 17 Sep 2018 15:59:37 -0500 Subject: [PATCH 050/427] WB-669 Data Tooltips: For chart line identification or tooltip, display XY-coordinates of start and end points of identified line segment. --- src/Brain/IdentificationTextGenerator.cxx | 33 ++++++++++++++++--- src/Graphics/GraphicsPrimitive.cxx | 19 +++++++++++ src/Graphics/GraphicsPrimitive.h | 3 ++ .../GraphicsPrimitiveSelectionHelper.cxx | 4 +-- src/GuiQt/BrainOpenGLWidget.cxx | 3 -- 5 files changed, 53 insertions(+), 9 deletions(-) diff --git a/src/Brain/IdentificationTextGenerator.cxx b/src/Brain/IdentificationTextGenerator.cxx index cc4a43450..1a0721738 100644 --- a/src/Brain/IdentificationTextGenerator.cxx +++ b/src/Brain/IdentificationTextGenerator.cxx @@ -48,6 +48,8 @@ #include "FociFile.h" #include "Focus.h" #include "GiftiLabel.h" +#include "GraphicsPrimitive.h" +#include "GraphicsPrimitiveV3f.h" #include "Histogram.h" #include "ImageFile.h" #include "MapFileDataSelector.h" @@ -872,16 +874,39 @@ IdentificationTextGenerator::generateChartTwoLineSeriesIdentificationText(Identi const MapFileDataSelector* mapFileDataSelector = cartesianData->getMapFileDataSelector(); CaretAssert(mapFileDataSelector); - const int32_t primitiveIndex = idChartTwoLineSeries->getLineSegmentIndex(); + int32_t primitiveIndex = idChartTwoLineSeries->getLineSegmentIndex(); AString boldText("Line Chart"); idText.addLine(false, boldText, mapFile->getFileNameNoPath()); - idText.addLine(true, - "Line Segment Index", - (AString::number(primitiveIndex))); + cartesianData->getGraphicsPrimitive(); + const GraphicsPrimitive* primitive = cartesianData->getGraphicsPrimitive(); + CaretAssert(primitive); + + if (primitiveIndex >= 1) { + float xyz1[3]; + primitive->getVertexFloatXYZ(primitiveIndex - 1, + xyz1); + float xyz2[3]; + primitive->getVertexFloatXYZ(primitiveIndex, + xyz2); + idText.addLine(true, + "XY Start", + AString::fromNumbers(xyz1, 2, ", ")); + idText.addLine(true, + "XY End ", + AString::fromNumbers(xyz2, 2, ", ")); + } + else { + float xyz[3]; + primitive->getVertexFloatXYZ(primitiveIndex, + xyz); + idText.addLine(true, + "XY", + AString::fromNumbers(xyz, 2, ", ")); + } generateMapFileSelectorText(idText, mapFileDataSelector); diff --git a/src/Graphics/GraphicsPrimitive.cxx b/src/Graphics/GraphicsPrimitive.cxx index 02689727f..4327e4c96 100644 --- a/src/Graphics/GraphicsPrimitive.cxx +++ b/src/Graphics/GraphicsPrimitive.cxx @@ -864,6 +864,25 @@ GraphicsPrimitive::addVertexProtected(const float xyz[3], } } +/** + * Get the XYZ coordinate from the given vertex. + * + * @param vertexIndex + * Index of the vertex. + * @param xyzOut + * Output containing the XYZ coordinat. + */ +void +GraphicsPrimitive::getVertexFloatXYZ(const int32_t vertexIndex, + float xyzOut[3]) const +{ + const int32_t i3 = vertexIndex * 3; + CaretAssertVectorIndex(m_xyz, i3 + 2); + xyzOut[0] = m_xyz[i3]; + xyzOut[1] = m_xyz[i3+1]; + xyzOut[2] = m_xyz[i3+2]; +} + /** * Replace the existing XYZ coordinates with the given * XYZ coordinates. The existing and new coordinates diff --git a/src/Graphics/GraphicsPrimitive.h b/src/Graphics/GraphicsPrimitive.h index e4a6cf098..1ee0ea101 100644 --- a/src/Graphics/GraphicsPrimitive.h +++ b/src/Graphics/GraphicsPrimitive.h @@ -369,6 +369,9 @@ namespace caret { */ const std::vector& getFloatXYZ() const { return m_xyz; } + void getVertexFloatXYZ(const int32_t vertexIndex, + float xyzOut[3]) const; + void replaceFloatXYZ(const std::vector& xyz); /** diff --git a/src/Graphics/GraphicsPrimitiveSelectionHelper.cxx b/src/Graphics/GraphicsPrimitiveSelectionHelper.cxx index 114cd32e7..ca3397ccd 100644 --- a/src/Graphics/GraphicsPrimitiveSelectionHelper.cxx +++ b/src/Graphics/GraphicsPrimitiveSelectionHelper.cxx @@ -78,14 +78,14 @@ GraphicsPrimitiveSelectionHelper::setupSelectionBeforeDrawing() case GraphicsPrimitive::PrimitiveType::MODEL_SPACE_POLYGONAL_LINE_LOOP_MITER_JOIN: case GraphicsPrimitive::PrimitiveType::POLYGONAL_LINE_LOOP_BEVEL_JOIN: case GraphicsPrimitive::PrimitiveType::POLYGONAL_LINE_LOOP_MITER_JOIN: - m_numberOfVerticesPerPrimitive = numberOfVertices; + m_numberOfVerticesPerPrimitive = 1; //numberOfVertices; break; case GraphicsPrimitive::PrimitiveType::MODEL_SPACE_POLYGONAL_LINE_STRIP_BEVEL_JOIN: case GraphicsPrimitive::PrimitiveType::MODEL_SPACE_POLYGONAL_LINE_STRIP_MITER_JOIN: case GraphicsPrimitive::PrimitiveType::OPENGL_LINE_STRIP: case GraphicsPrimitive::PrimitiveType::POLYGONAL_LINE_STRIP_BEVEL_JOIN: case GraphicsPrimitive::PrimitiveType::POLYGONAL_LINE_STRIP_MITER_JOIN: - m_numberOfVerticesPerPrimitive = numberOfVertices; + m_numberOfVerticesPerPrimitive = 1; //numberOfVertices; break; case GraphicsPrimitive::PrimitiveType::MODEL_SPACE_POLYGONAL_LINES: case GraphicsPrimitive::PrimitiveType::OPENGL_LINES: diff --git a/src/GuiQt/BrainOpenGLWidget.cxx b/src/GuiQt/BrainOpenGLWidget.cxx index c4f5b302c..0f51444ad 100644 --- a/src/GuiQt/BrainOpenGLWidget.cxx +++ b/src/GuiQt/BrainOpenGLWidget.cxx @@ -671,9 +671,6 @@ BrainOpenGLWidget::event(QEvent* event) DataToolTipsManager* dttm = SessionManager::get()->getDataToolTipsManager(); CaretAssert(dttm); - Brain* brain = GuiManager::get()->getBrain(); - CaretAssert(brain); - AString toolTipText; const BrainOpenGLViewportContent* idViewport = this->getViewportContentAtXY(x, y); -- GitLab From 6ad307d0ceb3560c35ad5b2f8738075ad5ce4778 Mon Sep 17 00:00:00 2001 From: Tim Coalson Date: Mon, 17 Sep 2018 19:24:33 -0500 Subject: [PATCH 051/427] more help text changes to -surface-sphere-project-unproject --- .../AlgorithmSurfaceSphereProjectUnproject.cxx | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/src/Algorithms/AlgorithmSurfaceSphereProjectUnproject.cxx b/src/Algorithms/AlgorithmSurfaceSphereProjectUnproject.cxx index 02bf09f4f..63cd83852 100644 --- a/src/Algorithms/AlgorithmSurfaceSphereProjectUnproject.cxx +++ b/src/Algorithms/AlgorithmSurfaceSphereProjectUnproject.cxx @@ -54,19 +54,28 @@ OperationParameters* AlgorithmSurfaceSphereProjectUnproject::getParameters() ret->setHelpText( AString("Background: A surface registration starts with an input sphere, and moves its vertices around on the sphere until it matches the template data. ") + - "Since the starting sphere of the registration typically has no vertex correspondence to any other sphere (often, it is a native sphere), it is inconvenient to manipulate or compare these deformations across subjects, etc.\n\n" + + "This means that the registration deformation is actually represented as the difference between two separate files - the starting sphere, and the registered sphere. " + + "Since the starting sphere of the registration may not have vertex correspondence to any other sphere (often, it is a native sphere), it can be inconvenient to manipulate or compare these deformations across subjects, etc.\n\n" + "The purpose of this command is to be able to apply these deformations onto a new sphere of the user's choice, to make it easier to compare or manipulate them. " + "Common uses are to concatenate two successive separate registrations (e.g. Human to Chimpanzee, and then Chimpanzee to Macaque) or inversion (for dedrifting or symmetric registration schemes).\n\n" + " must already be considered to be in alignment with one of the two ends of the registration (if your registration is Human to Chimpanzee, must be in register with either Human or Chimpanzee). " + - "The 'project-to' sphere must be the side of the registration that is aligned to the input sphere (if your registration is Human to Chimpanzee, and your input sphere is aligned with Human, then 'project-to' should be the original Human sphere). " + + "The 'project-to' sphere must be the side of the registration that is aligned with (if your registration is Human to Chimpanzee, and is aligned with Human, then 'project-to' should be the original Human sphere). " + "The 'unproject-from' sphere must be the remaining sphere of the registration (original vs deformed/registered). " + "The output is as if you had run the same registration with as the starting sphere, in the direction of deforming the 'project-to' sphere to create the 'unproject-from' sphere.\n\n" + "Note that this command cannot check for you what spheres are aligned with other spheres, and using the wrong spheres or in the incorrect order will not necessarily cause an error message. " + "In some cases, it may be useful to use a new, arbitrary sphere as the input, which can be created with the -surface-create-sphere command.\n\n" + + "Example 1: You have a Human to Chimpanzee registration, and a Chimpanzee to Macaque registration, and want to combine them. " + + "If you use the Human sphere registered to Chimpanzee as sphere-in, the Chimpanzee standard sphere as project-to, and " + + "the Chimpanzee sphere registered to Macaque as unproject-from, the output will be the Human sphere in register with the Macaque.\n\n" + + + "Example 2: You have a Human to Chimpanzee registration, but what you really want is the inverse, that is, the sphere as if you had run the registration from Chimpanzee to Human. " + + "If you use the Chimpanzee standard sphere as sphere-in, the Human sphere registered to Chimpanzee as project-to, and the standard Human sphere as unproject-from, " + + "the output will be the Chimpanzee sphere in register with the Human.\n\n" + + "Technical details: Each vertex of is projected to a triangle of , and its new position is determined by the position of the corresponding triangle in . " + "The output is a sphere with the topology of , but coordinates shifted by the deformation from to . " + " and must have the same topology as each other, but may have any topology." -- GitLab From 827a0e8df966efa0279b78b63579153b6a93fe48 Mon Sep 17 00:00:00 2001 From: John Harwell Date: Tue, 18 Sep 2018 10:05:59 -0500 Subject: [PATCH 052/427] WB-669 Data Tooltips: Change row/column index to start at 1 for charts in the tooltip text. --- src/Brain/IdentificationTextGenerator.cxx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Brain/IdentificationTextGenerator.cxx b/src/Brain/IdentificationTextGenerator.cxx index 1a0721738..520a31841 100644 --- a/src/Brain/IdentificationTextGenerator.cxx +++ b/src/Brain/IdentificationTextGenerator.cxx @@ -1132,7 +1132,7 @@ IdentificationTextGenerator::generateMapFileSelectorText(IdentificationStringBui mapFileName); idText.addLine(true, "Column Index", - AString::number(columnIndex)); + AString::number(columnIndex + 1)); } break; case MapFileDataSelector::DataSelectionType::ROW_DATA: @@ -1149,7 +1149,7 @@ IdentificationTextGenerator::generateMapFileSelectorText(IdentificationStringBui mapFileName); idText.addLine(true, "Row Index", - AString::number(rowIndex)); + AString::number(rowIndex + 1)); } break; case MapFileDataSelector::DataSelectionType::SURFACE_VERTEX: -- GitLab From 6b40e742405d71c15aca13a512a0140ae6198489 Mon Sep 17 00:00:00 2001 From: John Harwell Date: Tue, 18 Sep 2018 14:47:16 -0500 Subject: [PATCH 053/427] WB-827 Icons in Toolbar have insufficient contrast when on/off: Revised pixmaps used for icons so that they have a clear background (alpha == 0) so that when the corresponding buttons are on, the shaded region fills the button instead of just a border around the pixmap. --- .../BrainBrowserWindowToolBarSlicePlane.cxx | 6 +- ...rainBrowserWindowToolBarSliceSelection.cxx | 52 ++++++++++- .../BrainBrowserWindowToolBarSliceSelection.h | 3 + src/GuiQt/WuQtUtilities.cxx | 90 ++++++++++++++++--- src/GuiQt/WuQtUtilities.h | 29 +++++- 5 files changed, 161 insertions(+), 19 deletions(-) diff --git a/src/GuiQt/BrainBrowserWindowToolBarSlicePlane.cxx b/src/GuiQt/BrainBrowserWindowToolBarSlicePlane.cxx index f18e1332a..0561175cb 100644 --- a/src/GuiQt/BrainBrowserWindowToolBarSlicePlane.cxx +++ b/src/GuiQt/BrainBrowserWindowToolBarSlicePlane.cxx @@ -403,7 +403,8 @@ BrainBrowserWindowToolBarSlicePlane::createCrosshairsIcon(const QWidget* widget) QPixmap pixmap(static_cast(pixmapSize), static_cast(pixmapSize)); QSharedPointer painter = WuQtUtilities::createPixmapWidgetPainterOriginCenter(widget, - pixmap); + pixmap, + static_cast(WuQtUtilities::PixMapCreationOptions::TransparentBackground)); const int startXY = 4; const int endXY = 10; QPen pen(painter->pen()); @@ -441,7 +442,8 @@ BrainBrowserWindowToolBarSlicePlane::createCrosshairLabelsIcon(const QWidget* wi QPixmap pixmap(static_cast(pixmapSize), static_cast(pixmapSize)); QSharedPointer painter = WuQtUtilities::createPixmapWidgetPainter(widget, - pixmap); + pixmap, + static_cast(WuQtUtilities::PixMapCreationOptions::TransparentBackground)); QFont font = painter->font(); font.setPixelSize(10); painter->setFont(font); diff --git a/src/GuiQt/BrainBrowserWindowToolBarSliceSelection.cxx b/src/GuiQt/BrainBrowserWindowToolBarSliceSelection.cxx index b9737c4a1..91f2643c7 100644 --- a/src/GuiQt/BrainBrowserWindowToolBarSliceSelection.cxx +++ b/src/GuiQt/BrainBrowserWindowToolBarSliceSelection.cxx @@ -31,6 +31,8 @@ #include #include #include +#include +#include #include #include @@ -162,13 +164,14 @@ m_parentToolBar(parentToolBar) const bool volumeCrossHairIconValid = WuQtUtilities::loadIcon(":/ToolBar/volume-crosshair-pointer.png", volumeCrossHairIcon); + QToolButton* volumeIDToolButton = new QToolButton; if (volumeCrossHairIconValid) { m_volumeIdentificationUpdatesSlicesAction->setIcon(volumeCrossHairIcon); + m_volumeIdentificationUpdatesSlicesAction->setIcon(createVolumeIdentificationUpdatesSlicesIcon(volumeIDToolButton)); } else { m_volumeIdentificationUpdatesSlicesAction->setText("ID"); } - QToolButton* volumeIDToolButton = new QToolButton; volumeIDToolButton->setDefaultAction(m_volumeIdentificationUpdatesSlicesAction); WuQtUtilities::setToolButtonStyleForQt5Mac(volumeIDToolButton); @@ -826,4 +829,51 @@ BrainBrowserWindowToolBarSliceSelection::updateObliqueMaskingButton() } } +/** + * Create a pixmap for the volume identification updates slice selection button. + * + * @param widget + * To color the pixmap with backround and foreground, + * the palette from the given widget is used. + * @return + * The pixmap. + */ +QPixmap +BrainBrowserWindowToolBarSliceSelection::createVolumeIdentificationUpdatesSlicesIcon(const QWidget* widget) +{ + CaretAssert(widget); + const int pixmapSize = 24; + const int halfSize = pixmapSize / 2; + + QPixmap pixmap(pixmapSize, + pixmapSize); + QSharedPointer painter = WuQtUtilities::createPixmapWidgetPainterOriginCenter(widget, + pixmap, + static_cast(WuQtUtilities::PixMapCreationOptions::TransparentBackground)); + const int startXY = 3; + const int endXY = 8; + QPen pen(painter->pen()); + pen.setWidth(2); + painter->setPen(pen); + const int tx(-3); + const int ty(3); + painter->translate(tx, ty); + painter->drawLine(-startXY, 0, -endXY, 0); + painter->drawLine( startXY, 0, endXY, 0); + painter->drawLine(0, -startXY, 0, -endXY); + painter->drawLine(0, startXY, 0, endXY); + painter->translate(-tx, -ty); + + const int tipX(3); + const int tipY(-3); + const int tailX(halfSize); + const int tailY(-halfSize); + painter->drawLine(tipX, tipY, tailX, tailY); + + const int headLength(3); + painter->drawLine(tipX, tipY, tipX + headLength, tipY); + painter->drawLine(tipX, tipY, tipX, tipY - headLength); + + return pixmap; +} diff --git a/src/GuiQt/BrainBrowserWindowToolBarSliceSelection.h b/src/GuiQt/BrainBrowserWindowToolBarSliceSelection.h index a1c7b966f..1274f65b6 100644 --- a/src/GuiQt/BrainBrowserWindowToolBarSliceSelection.h +++ b/src/GuiQt/BrainBrowserWindowToolBarSliceSelection.h @@ -75,6 +75,9 @@ namespace caret { void updateObliqueMaskingButton(); + QPixmap createVolumeIdentificationUpdatesSlicesIcon(const QWidget* widget); + + BrainBrowserWindowToolBar* m_parentToolBar; WuQWidgetObjectGroup* m_volumeIndicesWidgetGroup; diff --git a/src/GuiQt/WuQtUtilities.cxx b/src/GuiQt/WuQtUtilities.cxx index 1acec9a6c..e4608c8de 100644 --- a/src/GuiQt/WuQtUtilities.cxx +++ b/src/GuiQt/WuQtUtilities.cxx @@ -1296,7 +1296,8 @@ WuQtUtilities::createCaretColorEnumPixmap(const QWidget* widget, QPixmap pixmap(pixmapWidth, pixmapHeight); QSharedPointer painter = WuQtUtilities::createPixmapWidgetPainter(widget, - pixmap); + pixmap, + 0); if (noneColorFlag) { /* @@ -1350,17 +1351,21 @@ WuQtUtilities::createCaretColorEnumPixmap(const QWidget* widget, * Widget used for coloring. * @param pixmap * The Pixmap must be square (width == height). + * @param pixmapOptions + * Options for creation of pixmap. * @return * Shared pointer containing QPainter for drawing to the pixmap. */ QSharedPointer WuQtUtilities::createPixmapWidgetPainterOriginCenter100x100(const QWidget* widget, - QPixmap& pixmap) + QPixmap& pixmap, + const uint32_t pixmapOptions) { CaretAssert(pixmap.width() == pixmap.height()); QSharedPointer painter = createPixmapWidgetPainter(widget, - pixmap); + pixmap, + pixmapOptions); /* * Note: QPainter has its origin at the top left. @@ -1389,17 +1394,21 @@ WuQtUtilities::createPixmapWidgetPainterOriginCenter100x100(const QWidget* widge * Widget used for coloring. * @param pixmap * The Pixmap must be square (width == height). + * @param pixmapOptions + * Options for creation of pixmap. * @return * Shared pointer containing QPainter for drawing to the pixmap. */ QSharedPointer WuQtUtilities::createPixmapWidgetPainterOriginCenter(const QWidget* widget, - QPixmap& pixmap) + QPixmap& pixmap, + const uint32_t pixmapOptions) { CaretAssert(pixmap.width() == pixmap.height()); - QSharedPointer painter = createPixmapWidgetPainter(widget, - pixmap); + QSharedPointer painter = createPixmapWidgetPainterPrivate(widget, + pixmap, + pixmapOptions); /* * Note: QPainter has its origin at the top left. @@ -1427,15 +1436,19 @@ WuQtUtilities::createPixmapWidgetPainterOriginCenter(const QWidget* widget, * Widget used for coloring. * @param pixmap * The Pixmap. + * @param pixmapOptions + * Options for creation of pixmap. * @return * Shared pointer containing QPainter for drawing to the pixmap. */ QSharedPointer WuQtUtilities::createPixmapWidgetPainterOriginBottomLeft(const QWidget* widget, - QPixmap& pixmap) + QPixmap& pixmap, + const uint32_t pixmapOptions) { QSharedPointer painter = createPixmapWidgetPainter(widget, - pixmap); + pixmap, + pixmapOptions); /* * Note: QPainter has its origin at the top left. @@ -1464,12 +1477,43 @@ WuQtUtilities::createPixmapWidgetPainterOriginBottomLeft(const QWidget* widget, * Widget used for coloring. * @param pixmap * The Pixmap. + * @param pixmapOptions + * Options for creation of pixmap. * @return * Shared pointer containing QPainter for drawing to the pixmap. */ QSharedPointer WuQtUtilities::createPixmapWidgetPainter(const QWidget* widget, - QPixmap& pixmap) + QPixmap& pixmap, + const uint32_t pixmapOptions) +{ + return createPixmapWidgetPainterPrivate(widget, + pixmap, + pixmapOptions); +} + +/** + * Create a painter for the given pixmap that will be placed + * into the given widget. The pixmap's background is painted + * with the widget's background color, the painter's pen is set + * to the widget's foreground color, and then the painter is + * returned. + * + * Origin of painter will be in the TOP LEFT corner. + * + * @param widget + * Widget used for coloring. + * @param pixmap + * The Pixmap. + * @param pixmapOptions + * Options for creation of the pixmap. + * @return + * Shared pointer containing QPainter for drawing to the pixmap. + */ +QSharedPointer +WuQtUtilities::createPixmapWidgetPainterPrivate(const QWidget* widget, + QPixmap& pixmap, + const uint32_t pixmapOptions) { CaretAssert(widget); CaretAssert(pixmap.width() > 0); @@ -1486,6 +1530,23 @@ WuQtUtilities::createPixmapWidgetPainter(const QWidget* widget, const QBrush foregroundBrush = palette.brush(foregroundRole); const QColor foregroundColor = foregroundBrush.color(); + const bool transparentBackgroundFlag = (pixmapOptions + & static_cast(PixMapCreationOptions::TransparentBackground)); + if (transparentBackgroundFlag) { + /* + * It is not possible to create a pixmap with alpha using Qt. + * So, create a QImage filled with alpha = 0 and then + * let the pixmap copy from the QImage. + */ + QImage image(pixmap.width(), + pixmap.height(), + QImage::Format_RGBA8888_Premultiplied); + image.fill(QColor(0, 0, 0, 0)); + const bool succssFlag = pixmap.convertFromImage(image); + if ( ! succssFlag) { + CaretLogSevere("Failed to convert image to pixmap"); + } + } /* * Create a painter and fill the pixmap with @@ -1493,9 +1554,14 @@ WuQtUtilities::createPixmapWidgetPainter(const QWidget* widget, */ QSharedPointer painter(new QPainter(&pixmap)); painter->setRenderHint(QPainter::Antialiasing, - true); - painter->setBackgroundMode(Qt::OpaqueMode); - painter->fillRect(pixmap.rect(), backgroundColor); + true); + if (transparentBackgroundFlag) { + painter->setBackgroundMode(Qt::TransparentMode); + } + else { + painter->setBackgroundMode(Qt::OpaqueMode); + painter->fillRect(pixmap.rect(), backgroundColor); + } painter->setPen(foregroundColor); diff --git a/src/GuiQt/WuQtUtilities.h b/src/GuiQt/WuQtUtilities.h index 73a53a6ff..714f708cf 100644 --- a/src/GuiQt/WuQtUtilities.h +++ b/src/GuiQt/WuQtUtilities.h @@ -22,6 +22,7 @@ */ /*LICENSE_END*/ +#include #include #include @@ -52,6 +53,18 @@ namespace caret { class WuQtUtilities { public: + /* + * Options for creating pixmaps + */ + enum class PixMapCreationOptions : uint32_t { + /* + * Create pixmap with transparent background. + * Useful for pixmaps used in toolbar toggle buttons so that selection is shaded + * in the entire button not just around the pixmap + */ + TransparentBackground = 1 + }; + static QAction* createAction(const QString& text, const QString& toolAndStatusTipText, const QKeySequence& shortcut, @@ -91,16 +104,20 @@ namespace caret { const bool outlineFlag); static QSharedPointer createPixmapWidgetPainter(const QWidget* widget, - QPixmap& pixmap); + QPixmap& pixmap, + const uint32_t pixmapOptions = 0); static QSharedPointer createPixmapWidgetPainterOriginBottomLeft(const QWidget* widget, - QPixmap& pixmap); + QPixmap& pixmap, + const uint32_t pixmapOptions = 0); static QSharedPointer createPixmapWidgetPainterOriginCenter(const QWidget* widget, - QPixmap& pixmap); + QPixmap& pixmap, + const uint32_t pixmapOptions = 0); static QSharedPointer createPixmapWidgetPainterOriginCenter100x100(const QWidget* widget, - QPixmap& pixmap); + QPixmap& pixmap, + const uint32_t pixmapOptions = 0); static void moveWindowToOffset(QWidget* parentWidget, QWidget* window, @@ -206,6 +223,10 @@ namespace caret { static void setToolButtonStyleForQt5Mac(QToolButton* toolButton); private: + static QSharedPointer createPixmapWidgetPainterPrivate(const QWidget* widget, + QPixmap& pixmap, + const uint32_t pixmapOptions = 0); + WuQtUtilities(); ~WuQtUtilities(); WuQtUtilities(const WuQtUtilities&); -- GitLab From 98611914b357506f1273b5d2ef398f21fc7cba01 Mon Sep 17 00:00:00 2001 From: John Harwell Date: Tue, 18 Sep 2018 14:59:08 -0500 Subject: [PATCH 054/427] Changed logging to 'fine' of unrecognized element in palette color mapping sax reader. --- src/Palette/PaletteColorMappingSaxReader.cxx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Palette/PaletteColorMappingSaxReader.cxx b/src/Palette/PaletteColorMappingSaxReader.cxx index 2163ec0cb..660d8ce42 100644 --- a/src/Palette/PaletteColorMappingSaxReader.cxx +++ b/src/Palette/PaletteColorMappingSaxReader.cxx @@ -436,7 +436,7 @@ PaletteColorMappingSaxReader::endElement(const AString& /* namspaceURI */, << qName.toStdString() << "\" with content: " << this->elementText.toStdString(); - warning(XmlSaxParserException(AString::fromStdString(str.str()))); + CaretLogFine(AString::fromStdString(str.str())); } break; } -- GitLab From 4fc1a7b91df950d52126bca9b28cb3ae22d4c12c Mon Sep 17 00:00:00 2001 From: John Harwell Date: Tue, 18 Sep 2018 15:56:53 -0500 Subject: [PATCH 055/427] Fix a couple of small issues on Scene File Paths Dialog: Listed with each data file are the indices of scenes that use the data file. These scene indices were starting at 0 and have been corrected to start at 1. Remote files (begin with http) were listed as missing but have been corrected to remove the missing status. --- src/Files/SceneFile.cxx | 4 ++-- src/Files/SceneFile.h | 4 ++-- src/GuiQt/SceneDialog.cxx | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/Files/SceneFile.cxx b/src/Files/SceneFile.cxx index 4f1669be2..ec464862c 100644 --- a/src/Files/SceneFile.cxx +++ b/src/Files/SceneFile.cxx @@ -1149,8 +1149,8 @@ SceneFile::getAllDataFileNamesFromAllScenes() const } if (validDiskFileFlag) { - QFileInfo fileInfo(pathName); - const QString absPathName = fileInfo.absoluteFilePath(); + FileInformation fileInfo(pathName); + const QString absPathName = fileInfo.getAbsoluteFilePath(); if ( ! absPathName.isEmpty()) { pathName = absPathName; } diff --git a/src/Files/SceneFile.h b/src/Files/SceneFile.h index b61e69e20..f18f248ee 100644 --- a/src/Files/SceneFile.h +++ b/src/Files/SceneFile.h @@ -112,7 +112,7 @@ namespace caret { SceneDataFileInfo(const AString& dataFileName, const int32_t sceneIndex) : m_dataFileName(dataFileName) { - m_sceneIndices.push_back(sceneIndex); + m_sceneIndices.push_back(sceneIndex + 1); } bool operator<(const SceneDataFileInfo& rhs) const { @@ -120,7 +120,7 @@ namespace caret { } void addSceneIndex(const int32_t sceneIndex) const { - m_sceneIndices.push_back(sceneIndex); + m_sceneIndices.push_back(sceneIndex + 1); } AString getSceneIndices() const { diff --git a/src/GuiQt/SceneDialog.cxx b/src/GuiQt/SceneDialog.cxx index 7d40efd89..cbd7a9cb5 100644 --- a/src/GuiQt/SceneDialog.cxx +++ b/src/GuiQt/SceneDialog.cxx @@ -1924,7 +1924,7 @@ SceneDialog::showFileStructure() text.appendWithNewLine("Scene File: " + sceneFileName + "

"); - text.append("Data File paths relative to Scene File:"); + text.append("Data File paths relative to Scene File (indices of scenes using file in parenthesis):"); text.append("

    "); for (const auto& fileData : fileSceneInfo) { -- GitLab From 8e93e8b2d15a372aed6f91bc27116536e91a5d2f Mon Sep 17 00:00:00 2001 From: John Harwell Date: Wed, 19 Sep 2018 11:53:53 -0500 Subject: [PATCH 056/427] WB-669 Data Tooltips: Added tool button to main window toolbar for enabling/disabling tooltips. The icon in the tool button is a "speech bubble". --- src/Brain/DataToolTipsManager.cxx | 7 +- src/Brain/DataToolTipsManager.h | 2 +- src/Brain/SessionManager.cxx | 2 +- src/GuiQt/BrainBrowserWindowToolBar.cxx | 11 ++- src/GuiQt/BrainOpenGLWidget.cxx | 3 +- src/GuiQt/GuiManager.cxx | 114 +++++++++++++++++++++++- src/GuiQt/GuiManager.h | 7 ++ src/GuiQt/WuQtUtilities.h | 2 +- 8 files changed, 139 insertions(+), 9 deletions(-) diff --git a/src/Brain/DataToolTipsManager.cxx b/src/Brain/DataToolTipsManager.cxx index 193fa729d..8c17c1eb2 100644 --- a/src/Brain/DataToolTipsManager.cxx +++ b/src/Brain/DataToolTipsManager.cxx @@ -45,11 +45,14 @@ using namespace caret; /** * Constructor. + * + * @param enabledStatus + * Enabled status for data tool tips */ -DataToolTipsManager::DataToolTipsManager() +DataToolTipsManager::DataToolTipsManager(const bool enabledStatus) : CaretObject() { - + m_enabledFlag = enabledStatus; m_sceneAssistant = std::unique_ptr(new SceneClassAssistant()); /*EventManager::get()->addEventListener(this, EventTypeEnum::);*/ diff --git a/src/Brain/DataToolTipsManager.h b/src/Brain/DataToolTipsManager.h index 88769b940..5843c42a6 100644 --- a/src/Brain/DataToolTipsManager.h +++ b/src/Brain/DataToolTipsManager.h @@ -42,7 +42,7 @@ namespace caret { class DataToolTipsManager : public CaretObject, public EventListenerInterface, public SceneableInterface { public: - DataToolTipsManager(); + DataToolTipsManager(const bool enabledStatus); virtual ~DataToolTipsManager(); diff --git a/src/Brain/SessionManager.cxx b/src/Brain/SessionManager.cxx index d5f45d543..0c136efc2 100644 --- a/src/Brain/SessionManager.cxx +++ b/src/Brain/SessionManager.cxx @@ -77,7 +77,7 @@ SessionManager::SessionManager() m_ciftiConnectivityMatrixDataFileManager = new CiftiConnectivityMatrixDataFileManager(); m_ciftiFiberTrajectoryManager = new CiftiFiberTrajectoryManager(); - m_dataToolTipsManager.reset(new DataToolTipsManager()); + m_dataToolTipsManager.reset(new DataToolTipsManager(m_caretPreferences->isShowDataToolTipsEnabled())); for (int32_t i = 0; i < BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS; i++) { m_browserTabs[i] = NULL; diff --git a/src/GuiQt/BrainBrowserWindowToolBar.cxx b/src/GuiQt/BrainBrowserWindowToolBar.cxx index 3d4329805..417b8dd86 100644 --- a/src/GuiQt/BrainBrowserWindowToolBar.cxx +++ b/src/GuiQt/BrainBrowserWindowToolBar.cxx @@ -307,6 +307,9 @@ BrainBrowserWindowToolBar::BrainBrowserWindowToolBar(const int32_t browserWindow QToolButton* layersToolBoxToolButton = new QToolButton(); layersToolBoxToolButton->setDefaultAction(layersToolBoxAction); + QToolButton* dataToolTipsToolButton = new QToolButton(); + dataToolTipsToolButton->setDefaultAction(GuiManager::get()->getDataToolTipsAction(dataToolTipsToolButton)); + /* * Make all tool buttons the same height */ @@ -316,7 +319,8 @@ BrainBrowserWindowToolBar::BrainBrowserWindowToolBar(const int32_t browserWindow sceneDialogToolButton, toolBarToolButton, overlayToolBoxToolButton, - layersToolBoxToolButton); + layersToolBoxToolButton, + dataToolTipsToolButton); WuQtUtilities::setToolButtonStyleForQt5Mac(helpDialogToolButton); WuQtUtilities::setToolButtonStyleForQt5Mac(informationDialogToolButton); @@ -325,6 +329,7 @@ BrainBrowserWindowToolBar::BrainBrowserWindowToolBar(const int32_t browserWindow WuQtUtilities::setToolButtonStyleForQt5Mac(toolBarToolButton); WuQtUtilities::setToolButtonStyleForQt5Mac(overlayToolBoxToolButton); WuQtUtilities::setToolButtonStyleForQt5Mac(layersToolBoxToolButton); + WuQtUtilities::setToolButtonStyleForQt5Mac(dataToolTipsToolButton); /* * Tab bar and controls at far right side of toolbar @@ -333,6 +338,7 @@ BrainBrowserWindowToolBar::BrainBrowserWindowToolBar(const int32_t browserWindow QHBoxLayout* tabBarLayout = new QHBoxLayout(this->tabBarWidget); WuQtUtilities::setLayoutSpacingAndMargins(tabBarLayout, 2, 1); tabBarLayout->addWidget(this->tabBar, 100); + tabBarLayout->addWidget(dataToolTipsToolButton); tabBarLayout->addWidget(helpDialogToolButton); tabBarLayout->addWidget(informationDialogToolButton); tabBarLayout->addWidget(identifyDialogToolButton); @@ -4426,3 +4432,6 @@ BrainBrowserWindowToolBar::getNumberOfTabs() const return this->tabBar->count(); } + + + diff --git a/src/GuiQt/BrainOpenGLWidget.cxx b/src/GuiQt/BrainOpenGLWidget.cxx index 0f51444ad..b7c9b46f5 100644 --- a/src/GuiQt/BrainOpenGLWidget.cxx +++ b/src/GuiQt/BrainOpenGLWidget.cxx @@ -657,8 +657,7 @@ BrainOpenGLWidget::paintGL() bool BrainOpenGLWidget::event(QEvent* event) { - const CaretPreferences* prefs = SessionManager::get()->getCaretPreferences(); - if (prefs->isShowDataToolTipsEnabled()) { + if (SessionManager::get()->getDataToolTipsManager()->isEnabled()) { if (event->type() == QEvent::ToolTip) { QHelpEvent* helpEvent = static_cast(event); CaretAssert(helpEvent); diff --git a/src/GuiQt/GuiManager.cxx b/src/GuiQt/GuiManager.cxx index 37ca65287..f240c934c 100644 --- a/src/GuiQt/GuiManager.cxx +++ b/src/GuiQt/GuiManager.cxx @@ -27,6 +27,8 @@ #include #include #include +#include +#include #include #define __GUI_MANAGER_DEFINE__ @@ -59,6 +61,7 @@ #include "CursorManager.h" #include "CustomViewDialog.h" #include "DataFileException.h" +#include "DataToolTipsManager.h" #include "ElapsedTimer.h" #include "EventAlertUser.h" #include "EventAnnotationGetDrawnInWindow.h" @@ -291,6 +294,11 @@ GuiManager::initializeGuiManager() m_helpViewerDialogDisplayAction->setChecked(false); m_helpViewerDialogDisplayAction->blockSignals(false); + /* + * Data tooltip action is created when requested by a toolbar + */ + m_dataToolTipsEnabledAction = NULL; + EventManager::get()->addEventListener(this, EventTypeEnum::EVENT_ALERT_USER); EventManager::get()->addEventListener(this, EventTypeEnum::EVENT_ANNOTATION_GET_DRAWN_IN_WINDOW); EventManager::get()->addEventListener(this, EventTypeEnum::EVENT_BROWSER_WINDOW_NEW); @@ -1862,6 +1870,96 @@ GuiManager::getHelpViewerDialogDisplayAction() return m_helpViewerDialogDisplayAction; } +/** + * Get the action for the data tooltips. The action + * is lazily initialized since a widget is needed to + * create the tooltip's icon. The background and foreground + * colors are copied from the widget and nothing is dependent + * upon the widget after this method returns. + * + * @param buttonWidget + * Widget that is used the first time this method is called + * to provide foreground/background colors for the pixmap. + * + * @return Action for display of data tool tips. + */ +QAction* +GuiManager::getDataToolTipsAction(QWidget* buttonWidget) +{ + if (m_dataToolTipsEnabledAction == NULL) { + m_dataToolTipsEnabledAction = WuQtUtilities::createAction("Data ToolTips", + "Enable/Disable Data Tool Tips", + this, + this, + SLOT(dataToolTipsActionTriggered(bool))); + m_dataToolTipsEnabledAction->setIcon(createDataToolTipsIcon(buttonWidget)); + m_dataToolTipsEnabledAction->setIconVisibleInMenu(false); + m_dataToolTipsEnabledAction->setCheckable(true); + m_dataToolTipsEnabledAction->setChecked(SessionManager::get()->getDataToolTipsManager()->isEnabled()); + } + + return m_dataToolTipsEnabledAction; +} + +/** + * Create a pixmap for the data tool tips button. + * + * @param widget + * To color the pixmap with backround and foreground, + * the palette from the given widget is used. + * @return + * The pixmap. + */ +QPixmap +GuiManager::createDataToolTipsIcon(const QWidget* widget) +{ + CaretAssert(widget); + const float pixmapSize = 32.0; + + QPixmap pixmap(static_cast(pixmapSize), + static_cast(pixmapSize)); + QSharedPointer painter = WuQtUtilities::createPixmapWidgetPainterOriginCenter(widget, + pixmap, + WuQtUtilities::PixMapCreationOptions::TransparentBackground); + const int leftX(-14); + const int rightX(14); + const int bottomY(-8); + const int topY(14); + const int tipLeftX(-6); + const int tipRightX(6); + const int tipY(-14); + const int tipX(0); + + QPen pen(painter->pen()); + pen.setWidth(2); + painter->setPen(pen); + + /* + * Outline of icon + */ + QPolygon polygon; + polygon.push_back(QPoint(leftX, topY)); + polygon.push_back(QPoint(leftX, bottomY)); + polygon.push_back(QPoint(tipLeftX, bottomY)); + polygon.push_back(QPoint(tipX, tipY)); + polygon.push_back(QPoint(tipRightX, bottomY)); + polygon.push_back(QPoint(rightX, bottomY)); + polygon.push_back(QPoint(rightX, topY)); + painter->drawPolygon(polygon); + + /* + * Horizontal lines inside outline + */ + const int lineLeftX(-8); + const int lineRightX(8); + const int lineOneY(6); + const int lineTwoY(0); + painter->drawLine(lineLeftX, lineOneY, lineRightX, lineOneY); + painter->drawLine(lineLeftX, lineTwoY, lineRightX, lineTwoY); + + return pixmap; +} + /** * Show or hide the help dialog. * @@ -1925,6 +2023,9 @@ GuiManager::helpDialogWasClosed() /** * Called when show help action is triggered + * + * @param status + * New status. */ void GuiManager::showHelpDialogActionToggled(bool status) @@ -1932,6 +2033,18 @@ GuiManager::showHelpDialogActionToggled(bool status) showHideHelpDialog(status, NULL); } +/** + * Called when data tooltips action is triggered + * + * @param status + * New status. + */ +void +GuiManager::dataToolTipsActionTriggered(bool status) +{ + SessionManager::get()->getDataToolTipsManager()->setEnabled(status); +} + /** * @return The action that indicates the enabled status * for display of the information window. @@ -3204,4 +3317,3 @@ GuiManager::processIdentification(const int32_t tabIndex, } - diff --git a/src/GuiQt/GuiManager.h b/src/GuiQt/GuiManager.h index 3caec2612..ef4ca8aa2 100644 --- a/src/GuiQt/GuiManager.h +++ b/src/GuiQt/GuiManager.h @@ -123,6 +123,8 @@ namespace caret { QAction* getHelpViewerDialogDisplayAction(); + QAction* getDataToolTipsAction(QWidget* buttonWidget); + void closeAllOtherWindows(BrainBrowserWindow* browserWindow); void closeOtherWindowsAndReturnTheirTabContent(BrainBrowserWindow* browserWindow, @@ -201,6 +203,7 @@ namespace caret { void helpDialogWasClosed(); void sceneDialogWasClosed(); void identifyBrainordinateDialogWasClosed(); + void dataToolTipsActionTriggered(bool); private: GuiManager(QObject* parent = 0); @@ -233,6 +236,8 @@ namespace caret { void addParentLessNonModalDialog(QWidget* dialog); + QPixmap createDataToolTipsIcon(const QWidget* widget); + /** One instance of the GuiManager */ static GuiManager* singletonGuiManager; @@ -282,6 +287,8 @@ namespace caret { QAction* m_identifyBrainordinateDialogEnabledAction; + QAction* m_dataToolTipsEnabledAction; + BugReportDialog* m_bugReportDialog; QAction* m_helpViewerDialogDisplayAction; diff --git a/src/GuiQt/WuQtUtilities.h b/src/GuiQt/WuQtUtilities.h index 714f708cf..791c3942e 100644 --- a/src/GuiQt/WuQtUtilities.h +++ b/src/GuiQt/WuQtUtilities.h @@ -56,7 +56,7 @@ namespace caret { /* * Options for creating pixmaps */ - enum class PixMapCreationOptions : uint32_t { + enum PixMapCreationOptions { /* * Create pixmap with transparent background. * Useful for pixmaps used in toolbar toggle buttons so that selection is shaded -- GitLab From 8d9a62121761e3b078d381c3156cd44a61bbcbb4 Mon Sep 17 00:00:00 2001 From: John Harwell Date: Thu, 20 Sep 2018 14:14:31 -0500 Subject: [PATCH 057/427] WB-826 Move Scene file to top of Zipped dataset or warn if not in base directory: Initial redesign of "List Files" that allows showing files in relation to Scene File's Path, the Base Path, and Absolute Path. --- src/Files/CMakeLists.txt | 2 + src/Files/SceneDataFileInfo.cxx | 297 +++++++++++++++++++++++ src/Files/SceneDataFileInfo.h | 113 +++++++++ src/Files/SceneFile.cxx | 42 +++- src/Files/SceneFile.h | 13 +- src/GuiQt/CMakeLists.txt | 3 + src/GuiQt/SceneDialog.cxx | 75 +----- src/GuiQt/SceneFileInformationDialog.cxx | 215 ++++++++++++++++ src/GuiQt/SceneFileInformationDialog.h | 75 ++++++ 9 files changed, 759 insertions(+), 76 deletions(-) create mode 100644 src/Files/SceneDataFileInfo.cxx create mode 100644 src/Files/SceneDataFileInfo.h create mode 100644 src/GuiQt/SceneFileInformationDialog.cxx create mode 100644 src/GuiQt/SceneFileInformationDialog.h diff --git a/src/Files/CMakeLists.txt b/src/Files/CMakeLists.txt index 0ffc8dc6f..9305f06e8 100755 --- a/src/Files/CMakeLists.txt +++ b/src/Files/CMakeLists.txt @@ -116,6 +116,7 @@ OxfordSparseThreeFile.h PaletteFile.h RgbaFile.h RibbonMappingHelper.h +SceneDataFileInfo.h SceneFile.h SceneFileSaxReader.h SignedDistanceHelper.h @@ -244,6 +245,7 @@ OxfordSparseThreeFile.cxx PaletteFile.cxx RgbaFile.cxx RibbonMappingHelper.cxx +SceneDataFileInfo.cxx SceneFile.cxx SceneFileSaxReader.cxx SignedDistanceHelper.cxx diff --git a/src/Files/SceneDataFileInfo.cxx b/src/Files/SceneDataFileInfo.cxx new file mode 100644 index 000000000..0e10f8ec9 --- /dev/null +++ b/src/Files/SceneDataFileInfo.cxx @@ -0,0 +1,297 @@ + +/*LICENSE_START*/ +/* + * Copyright (C) 2018 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + +#define __SCENE_DATA_FILE_INFO_DECLARE__ +#include "SceneDataFileInfo.h" +#undef __SCENE_DATA_FILE_INFO_DECLARE__ + +#include "CaretAssert.h" +#include "FileInformation.h" +using namespace caret; + + + +/** + * \class caret::SceneDataFileInfo + * \brief Information about data files in a scene file + * \ingroup Files + */ + +/** + * Constructor. + * + * @param absoluteDataFilePathAndName + * Absolute path and file name of the data file + * @param absoluteBasePath + * The absolute base path + * @param absoluteSceneFilePathAndName + * Absolute path and name of the scene file + * @param sceneIndex + * Index of scene using this data file. + */ +SceneDataFileInfo::SceneDataFileInfo(const AString& absoluteDataFilePathAndName, + const AString& absoluteBasePath, + const AString& absoluteSceneFilePathAndName, + const std::vector& sceneIndices) +: CaretObject() +{ + FileInformation fileInfo(absoluteDataFilePathAndName); + m_remoteFlag = fileInfo.isRemoteFile(); + if ( ! m_remoteFlag) { + m_missingFlag = ( ! fileInfo.exists()); + } + + m_absolutePath = fileInfo.getAbsolutePath(); + + m_dataFileName = fileInfo.getFileName(); + + FileInformation sceneFileInfo(absoluteSceneFilePathAndName); + m_relativePathToSceneFile = SystemUtilities::relativePath(m_absolutePath, + sceneFileInfo.getAbsolutePath()); + + m_relativePathToBasePath = SystemUtilities::relativePath(m_absolutePath, + absoluteBasePath); + + m_sceneIndices.insert(sceneIndices.begin(), + sceneIndices.end()); +} + +/** + * Destructor. + */ +SceneDataFileInfo::~SceneDataFileInfo() +{ +} + +/** + * Copy constructor. + * @param obj + * Object that is copied. + */ +SceneDataFileInfo::SceneDataFileInfo(const SceneDataFileInfo& obj) +: CaretObject(obj) +{ + this->copyHelperSceneDataFileInfo(obj); +} + +/** + * Assignment operator. + * @param obj + * Data copied from obj to this. + * @return + * Reference to this object. + */ +SceneDataFileInfo& +SceneDataFileInfo::operator=(const SceneDataFileInfo& obj) +{ + if (this != &obj) { + CaretObject::operator=(obj); + this->copyHelperSceneDataFileInfo(obj); + } + return *this; +} + +/** + * Less-than operator. + * + * @param rhs + * Other instance for comparison + * @return + * True if this instance is 'less-than' the other instance. + */ +bool +SceneDataFileInfo::operator<(const SceneDataFileInfo& rhs) const { + if (m_absolutePath == rhs.m_absolutePath) { + return (m_dataFileName < rhs.m_dataFileName); + } + return (m_absolutePath < rhs.m_absolutePath); +} + +/** + * Add a scene index that uses this data file. + * + * @param sceneIndex + * Additional scene index. + */ +void +SceneDataFileInfo::addSceneIndex(const int32_t sceneIndex) const +{ + m_sceneIndices.insert(sceneIndex); +} + +/** + * @return The scene indices as a string + */ +AString +SceneDataFileInfo::getSceneIndicesAsString() const +{ + std::vector indicesVector(m_sceneIndices.begin(), + m_sceneIndices.end()); + return AString::fromNumbers(indicesVector, ","); +} + +/** + * Helps with copying an object of this type. + * @param obj + * Object that is copied. + */ +void +SceneDataFileInfo::copyHelperSceneDataFileInfo(const SceneDataFileInfo& obj) +{ + m_absolutePath = obj.m_absolutePath; + m_dataFileName = obj.m_dataFileName; + m_relativePathToBasePath = obj.m_relativePathToBasePath; + m_relativePathToSceneFile = obj.m_relativePathToSceneFile; + m_sceneIndices = obj.m_sceneIndices; +} + +/** + * @return + */ +AString +SceneDataFileInfo::getAbsolutePath() const +{ + return m_absolutePath; +} + +/** + * @return + */ +AString +SceneDataFileInfo::getAbsolutePathAndFileName() const +{ + if (m_absolutePath.isEmpty()) { + return m_dataFileName; + } + const AString s(m_absolutePath + + "/" + + m_dataFileName); + return s; +} + +/** + * @return + */ +AString +SceneDataFileInfo::getDataFileName() const +{ + return m_dataFileName; +} + +/** + * @return + */ +AString +SceneDataFileInfo::getRelativePathToBasePath() const +{ + return m_relativePathToBasePath; +} + +/** + * @return + */ +AString +SceneDataFileInfo::getRelativePathToSceneFile() const +{ + return m_relativePathToSceneFile; +} + +/** + * Get a description of this object's content. + * @return String describing this object's content. + */ +AString +SceneDataFileInfo::toString() const +{ + return "SceneDataFileInfo"; +} + +/** + * @return True if the file is remote. + */ +bool +SceneDataFileInfo::isRemote() const +{ + return m_remoteFlag; +} + +/** + * @return True if this file is missing. + * Note: Remote file is never missing. + */ +bool +SceneDataFileInfo::isMissing() const +{ + if (isRemote()) { + return false; + } + + return m_missingFlag; +} + +/** + * Sort a vector of SceneDataFileInfo objects using the given sort mode + * + * @param sceneDataFileInfo + * Vector of object that is sorted. + * @param sortMode + * Mode for sorting. + */ +void +SceneDataFileInfo::sort(std::vector& sceneDataFileInfo, + const SortMode sortMode) +{ + switch (sortMode) { + case SortMode::AbsolutePath: + break; + case SortMode::RelativeToBasePath: + break; + case SortMode::RelativeToSceneFilePath: + break; + } + + std::sort(sceneDataFileInfo.begin(), + sceneDataFileInfo.end(), + [&sortMode] (const SceneDataFileInfo& lhs, const SceneDataFileInfo& rhs) { + AString lhsPath; + AString rhsPath; + + switch (sortMode) { + case SortMode::AbsolutePath: + lhsPath = lhs.m_absolutePath; + rhsPath = rhs.m_absolutePath; + break; + case SortMode::RelativeToBasePath: + lhsPath = lhs.m_relativePathToBasePath; + rhsPath = rhs.m_relativePathToBasePath; + break; + case SortMode::RelativeToSceneFilePath: + lhsPath = lhs.m_relativePathToSceneFile; + rhsPath = rhs.m_relativePathToSceneFile; + break; + } + + if (lhsPath == rhsPath) { + return (lhs.m_dataFileName < rhs.m_dataFileName); + } + return (lhsPath < rhsPath); + }); +} diff --git a/src/Files/SceneDataFileInfo.h b/src/Files/SceneDataFileInfo.h new file mode 100644 index 000000000..1b3f95d51 --- /dev/null +++ b/src/Files/SceneDataFileInfo.h @@ -0,0 +1,113 @@ +#ifndef __SCENE_DATA_FILE_INFO_H__ +#define __SCENE_DATA_FILE_INFO_H__ + +/*LICENSE_START*/ +/* + * Copyright (C) 2018 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + +#include +#include +#include + +#include "CaretObject.h" + + + +namespace caret { + + class SceneDataFileInfo : public CaretObject { + + public: + /** + * Mode for sorting + */ + enum class SortMode { + /* Sort by file's absolute path and then data file name */ + AbsolutePath, + /* Sort by path relative to base path and then data file name */ + RelativeToBasePath, + /* Sort by path relative to scene file path and then data file name */ + RelativeToSceneFilePath + }; + + SceneDataFileInfo(const AString& absoluteDataFilePathAndName, + const AString& absoluteBasePath, + const AString& absoluteSceneFilePathAndName, + const std::vector& sceneIndices); + + virtual ~SceneDataFileInfo(); + + SceneDataFileInfo(const SceneDataFileInfo& obj); + + SceneDataFileInfo& operator=(const SceneDataFileInfo& obj); + + bool operator<(const SceneDataFileInfo& rhs) const; + + void addSceneIndex(const int32_t sceneIndex) const; + + AString getSceneIndicesAsString() const; + + AString getAbsolutePath() const; + + AString getAbsolutePathAndFileName() const; + + AString getDataFileName() const; + + AString getRelativePathToBasePath() const; + + AString getRelativePathToSceneFile() const; + + bool isRemote() const; + + bool isMissing() const; + + // ADD_NEW_METHODS_HERE + + virtual AString toString() const; + + static void sort(std::vector& sceneDataFileInfo, + const SortMode sortMode); + + private: + void copyHelperSceneDataFileInfo(const SceneDataFileInfo& obj); + + AString m_absolutePath; + + AString m_dataFileName; + + AString m_relativePathToBasePath; + + AString m_relativePathToSceneFile; + + bool m_remoteFlag = false; + + bool m_missingFlag = false; + + mutable std::set m_sceneIndices; + + // ADD_NEW_MEMBERS_HERE and DON'T FORGET TO UPDATE COPY COPY METHOD + + }; + +#ifdef __SCENE_DATA_FILE_INFO_DECLARE__ + // +#endif // __SCENE_DATA_FILE_INFO_DECLARE__ + +} // namespace +#endif //__SCENE_DATA_FILE_INFO_H__ diff --git a/src/Files/SceneFile.cxx b/src/Files/SceneFile.cxx index ec464862c..f1b7541c8 100644 --- a/src/Files/SceneFile.cxx +++ b/src/Files/SceneFile.cxx @@ -891,7 +891,7 @@ SceneFile::findBaseDirectoryForDataFiles(AString& baseDirectoryOut, const AString directorySeparator("/"); std::vector allFileNames; - std::set filesFromScenes = getAllDataFileNamesFromAllScenes(); + std::set filesFromScenes = getAllDataFileNamesFromAllScenes(); for (const auto& nameInfo : filesFromScenes) { allFileNames.push_back(nameInfo.m_dataFileName); } @@ -1041,12 +1041,12 @@ SceneFile::getBaseDirectoryHierarchyForDataFiles(const int32_t maximumAncestorCo /** * @return A vector containing the names of all data files from all scenes. */ -std::set +std::set SceneFile::getAllDataFileNamesFromAllScenes() const { const bool includeSpecFileFlag = false; - std::set fileInfoOut; + std::set fileInfoOut; /** * Find all 'path name' elements from ALL scenes @@ -1079,7 +1079,7 @@ SceneFile::getAllDataFileNamesFromAllScenes() const } if (useNameFlag) { AString pathName = scenePathName->stringValue().trimmed(); - + if ( ! pathName.isEmpty()) { bool validExtensionFlag = false; const DataFileTypeEnum::Enum dataFileType = DataFileTypeEnum::fromFileExtension(pathName, @@ -1168,8 +1168,8 @@ SceneFile::getAllDataFileNamesFromAllScenes() const } if ( ! foundFlag) { - fileInfoOut.insert(SceneDataFileInfo(pathName, - sceneIndex)); + fileInfoOut.insert(FileAndSceneIndicesInfo(pathName, + sceneIndex)); } } } @@ -1182,6 +1182,36 @@ SceneFile::getAllDataFileNamesFromAllScenes() const return fileInfoOut; } +/** + * @return File info for all files in the scene file. + */ +std::vector +SceneFile::getAllDataFileInfoFromAllScenes() const +{ + const std::set allNamesAndIndices = getAllDataFileNamesFromAllScenes(); + + AString basePath; + AString errorMessage; + std::vector missingFiles; + const bool validFlag = findBaseDirectoryForDataFiles(basePath, missingFiles, errorMessage); + if ( ! validFlag) { + CaretLogSevere("Failed to find the base path for scene file: " + + getFileName()); + } + + std::vector fileInfoOut; + + for (const auto nameAndIndices : allNamesAndIndices) { + fileInfoOut.emplace_back(nameAndIndices.m_dataFileName, + basePath, + getFileName(), + nameAndIndices.m_sceneIndices); + } + + return fileInfoOut; +} + + /** * @return Default name for a ZIP file containing the scene file and its data files. */ diff --git a/src/Files/SceneFile.h b/src/Files/SceneFile.h index f18f248ee..84e4d76e0 100644 --- a/src/Files/SceneFile.h +++ b/src/Files/SceneFile.h @@ -24,6 +24,7 @@ #include #include "CaretDataFile.h" +#include "SceneDataFileInfo.h" #include "SceneFileBasePathTypeEnum.h" namespace caret { @@ -107,15 +108,15 @@ namespace caret { std::vector getBaseDirectoryHierarchyForDataFiles(const int32_t maximumAncestorCount = 25); - class SceneDataFileInfo { + class FileAndSceneIndicesInfo { public: - SceneDataFileInfo(const AString& dataFileName, - const int32_t sceneIndex) + FileAndSceneIndicesInfo(const AString& dataFileName, + const int32_t sceneIndex) : m_dataFileName(dataFileName) { m_sceneIndices.push_back(sceneIndex + 1); } - bool operator<(const SceneDataFileInfo& rhs) const { + bool operator<(const FileAndSceneIndicesInfo& rhs) const { return m_dataFileName < rhs.m_dataFileName; } @@ -132,7 +133,9 @@ namespace caret { mutable std::vector m_sceneIndices; }; - std::set getAllDataFileNamesFromAllScenes() const; + std::set getAllDataFileNamesFromAllScenes() const; + + std::vector getAllDataFileInfoFromAllScenes() const; void reorderScenes(std::vector& orderedScenes); diff --git a/src/GuiQt/CMakeLists.txt b/src/GuiQt/CMakeLists.txt index ac5daf6f9..a5f817563 100644 --- a/src/GuiQt/CMakeLists.txt +++ b/src/GuiQt/CMakeLists.txt @@ -165,6 +165,7 @@ ELSE() SceneBasePathWidget.h SceneCreateReplaceDialog.h SceneDialog.h + SceneFileInformationDialog.h ScenePreviewDialog.h SceneReplaceAllDialog.h SceneShowOptionsDialog.h @@ -397,6 +398,7 @@ RegionOfInterestCreateFromBorderDialog.h SceneBasePathWidget.h SceneCreateReplaceDialog.h SceneDialog.h +SceneFileInformationDialog.h ScenePreviewDialog.h SceneReplaceAllDialog.h SceneShowOptionsDialog.h @@ -625,6 +627,7 @@ RegionOfInterestCreateFromBorderDialog.cxx SceneBasePathWidget.cxx SceneCreateReplaceDialog.cxx SceneDialog.cxx +SceneFileInformationDialog.cxx ScenePreviewDialog.cxx SceneReplaceAllDialog.cxx SceneShowOptionsDialog.cxx diff --git a/src/GuiQt/SceneDialog.cxx b/src/GuiQt/SceneDialog.cxx index cbd7a9cb5..8e96e6f5d 100644 --- a/src/GuiQt/SceneDialog.cxx +++ b/src/GuiQt/SceneDialog.cxx @@ -79,6 +79,7 @@ #include "SceneClass.h" #include "SceneCreateReplaceDialog.h" #include "SceneFile.h" +#include "SceneFileInformationDialog.h" #include "SceneInfo.h" #include "ScenePreviewDialog.h" #include "SceneReplaceAllDialog.h" @@ -1901,71 +1902,15 @@ SceneDialog::showFileStructure() SceneFile* sceneFile = getSelectedSceneFile(); CaretAssert(sceneFile); - const std::set fileSceneInfo = sceneFile->getAllDataFileNamesFromAllScenes(); - - if (fileSceneInfo.empty()) { - WuQMessageBox::errorOk(m_showFileStructurePushButton, - "Scene file is empty."); - return; - } - - const AString sceneFileName = sceneFile->getFileName(); - AString text(""); - - AString baseDirectoryName; - std::vector missingFileNames; - AString errorMessage; - const bool validBasePathFlag = sceneFile->findBaseDirectoryForDataFiles(baseDirectoryName, - missingFileNames, - errorMessage); - text.appendWithNewLine("Automatic Base Path: " - + (validBasePathFlag ? baseDirectoryName : ("INVALID: " + errorMessage)) - + "

    "); - text.appendWithNewLine("Scene File: " - + sceneFileName - + "

    "); - text.append("Data File paths relative to Scene File (indices of scenes using file in parenthesis):"); - text.append("

      "); - - for (const auto& fileData : fileSceneInfo) { - AString name(fileData.m_dataFileName); - FileInformation fileInfo(name); - AString missingText; - if ( ! fileInfo.exists()) { - missingText = "MISSING: "; - } - - if (fileInfo.isAbsolute()) { - FileInformation specFileInfo(sceneFileName); - if (specFileInfo.isAbsolute()) { - const AString newPath = SystemUtilities::relativePath(fileInfo.getPathName(), - specFileInfo.getPathName()); - if (newPath.isEmpty()) { - name = fileInfo.getFileName(); - } - else { - name = (newPath - + "/" - + fileInfo.getFileName()); - } - } - } - - text.append("
    • " - + missingText - + " (" - + fileData.getSceneIndices() - + ") " - + name); - } - text.append("
    "); - text.append(""); - - WuQTextEditorDialog::runNonModal("Scene File Paths", - text, - WuQTextEditorDialog::TextMode::HTML, - WuQTextEditorDialog::WrapMode::NO, - this); + CursorDisplayScoped cursor; + cursor.showWaitCursor(); + + SceneFileInformationDialog* infoDialog = new SceneFileInformationDialog(sceneFile, + this); + infoDialog->setVisible(true); + infoDialog->show(); + infoDialog->activateWindow(); + infoDialog->raise(); } /** diff --git a/src/GuiQt/SceneFileInformationDialog.cxx b/src/GuiQt/SceneFileInformationDialog.cxx new file mode 100644 index 000000000..ff0395678 --- /dev/null +++ b/src/GuiQt/SceneFileInformationDialog.cxx @@ -0,0 +1,215 @@ + +/*LICENSE_START*/ +/* + * Copyright (C) 2018 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + +#define __SCENE_FILE_INFORMATION_DIALOG_DECLARE__ +#include "SceneFileInformationDialog.h" +#undef __SCENE_FILE_INFORMATION_DIALOG_DECLARE__ + +#include +#include +#include +#include +#include + +#include "CaretAssert.h" +#include "CursorDisplayScoped.h" +#include "SceneFile.h" +#include "WuQMessageBox.h" + +using namespace caret; + + + +/** + * \class caret::SceneFileInformationDialog + * \brief Dialog for display of scene file organization + * \ingroup GuiQt + */ + +/** + * Constructor. + * + * @param sceneFile + * The scene file whose file information is displayed. + * @param parent + * Dialog's parent widget. + */ +SceneFileInformationDialog::SceneFileInformationDialog(const SceneFile* sceneFile, + QWidget* parent) +: WuQDialogNonModal("Scene File Information", + parent), +m_sceneFile(sceneFile) +{ + setDeleteWhenClosed(true); + setApplyButtonText(""); + + m_modeComboBox = new QComboBox(); + QLabel* modeLabel = new QLabel("Show Files"); + m_modeComboBox->addItem("Absolute Path", + static_cast(SceneDataFileInfo::SortMode::AbsolutePath)); + m_modeComboBox->addItem("Relative to Base Path", + static_cast(SceneDataFileInfo::SortMode::RelativeToBasePath)); + m_modeComboBox->addItem("Relative to Scene File Path", + static_cast(SceneDataFileInfo::SortMode::RelativeToSceneFilePath)); + + QObject::connect(m_modeComboBox, static_cast(&QComboBox::activated), + this, &SceneFileInformationDialog::modeComboBoxActivated); + + m_textEdit = new QTextEdit(); + + QHBoxLayout* modeLayout = new QHBoxLayout(); + modeLayout->addWidget(modeLabel), + modeLayout->addWidget(m_modeComboBox); + modeLayout->addStretch(); + + QWidget* dialogWidget = new QWidget(); + QVBoxLayout* dialogLayout = new QVBoxLayout(dialogWidget); + dialogLayout->addLayout(modeLayout, 0); + dialogLayout->addWidget(m_textEdit, 100); + + setCentralWidget(dialogWidget, WuQDialog::SCROLL_AREA_NEVER); + + const SceneDataFileInfo::SortMode defaultSortMode(SceneDataFileInfo::SortMode::RelativeToSceneFilePath); + QSignalBlocker blocker(m_modeComboBox); + m_modeComboBox->setCurrentIndex(static_cast(defaultSortMode)); + displayFiles(static_cast(defaultSortMode)); + + setSizeOfDialogWhenDisplayed(QSize(600, 800)); +} + +/** + * Destructor. + */ +SceneFileInformationDialog::~SceneFileInformationDialog() +{ +} + +/** + * Called when mode combo box is selected + */ +void +SceneFileInformationDialog::modeComboBoxActivated(int mode) +{ + CursorDisplayScoped cursor; + cursor.showWaitCursor(); + + displayFiles(mode); +} + +/** + * Called when mode combo box is selected + */ +void +SceneFileInformationDialog::displayFiles(int modeInteger) +{ + const SceneDataFileInfo::SortMode sortMode = static_cast(modeInteger); + switch (sortMode) { + case SceneDataFileInfo::SortMode::AbsolutePath: + break; + case SceneDataFileInfo::SortMode::RelativeToBasePath: + break; + case SceneDataFileInfo::SortMode::RelativeToSceneFilePath: + break; + } + CaretAssert(m_sceneFile); + + std::vector fileSceneInfo = m_sceneFile->getAllDataFileInfoFromAllScenes(); + SceneDataFileInfo::sort(fileSceneInfo, + SceneDataFileInfo::SortMode::RelativeToSceneFilePath); + + if (fileSceneInfo.empty()) { + WuQMessageBox::errorOk(this, + "Scene file is empty."); + return; + } + + const AString sceneFileName = m_sceneFile->getFileName(); + AString text(""); + + AString baseDirectoryName; + std::vector missingFileNames; + AString errorMessage; + const bool validBasePathFlag = m_sceneFile->findBaseDirectoryForDataFiles(baseDirectoryName, + missingFileNames, + errorMessage); + text.appendWithNewLine("Automatic Base Path: " + + (validBasePathFlag ? baseDirectoryName : ("INVALID: " + errorMessage)) + + "

    "); + text.appendWithNewLine("Scene File: " + + sceneFileName + + "

    "); + + bool needListEndElementFlag = false; + AString lastPathName("bogus ##(*&$UI()#NFGK path name"); + for (const auto& fileData : fileSceneInfo) { + AString missingText; + + AString pathName; + switch (sortMode) { + case SceneDataFileInfo::SortMode::AbsolutePath: + pathName = fileData.getAbsolutePath(); + break; + case SceneDataFileInfo::SortMode::RelativeToBasePath: + pathName = fileData.getRelativePathToBasePath(); + if (pathName.isEmpty()) { + pathName = "Files in Base Path"; + } + break; + case SceneDataFileInfo::SortMode::RelativeToSceneFilePath: + pathName = fileData.getRelativePathToSceneFile(); + if (pathName.isEmpty()) { + pathName = "Files in Scene File Path"; + } + break; + } + + if (pathName != lastPathName) { + if (needListEndElementFlag) { + text.append("

"); + } + text.append("

" + + pathName + + ""); + text.append("
    "); + needListEndElementFlag = true; + lastPathName = pathName; + } + + if (fileData.isMissing()) { + missingText = "MISSING "; + } + + text.append("
  • " + + missingText + + fileData.getDataFileName() + + " (" + + fileData.getSceneIndicesAsString() + + ")"); + } + if (needListEndElementFlag) { + text.append("
"); + } + text.append(""); + + m_textEdit->clear(); + m_textEdit->setHtml(text); +} + diff --git a/src/GuiQt/SceneFileInformationDialog.h b/src/GuiQt/SceneFileInformationDialog.h new file mode 100644 index 000000000..ed0bb740e --- /dev/null +++ b/src/GuiQt/SceneFileInformationDialog.h @@ -0,0 +1,75 @@ +#ifndef __SCENE_FILE_INFORMATION_DIALOG_H__ +#define __SCENE_FILE_INFORMATION_DIALOG_H__ + +/*LICENSE_START*/ +/* + * Copyright (C) 2018 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + + + +#include + +#include "WuQDialogNonModal.h" + +class QComboBox; +class QTextEdit; + +namespace caret { + + class SceneFile; + + class SceneFileInformationDialog : public WuQDialogNonModal { + + Q_OBJECT + + public: + SceneFileInformationDialog(const SceneFile* sceneFile, + QWidget* parent); + + virtual ~SceneFileInformationDialog(); + + SceneFileInformationDialog(const SceneFileInformationDialog&) = delete; + + SceneFileInformationDialog& operator=(const SceneFileInformationDialog&) = delete; + + + // ADD_NEW_METHODS_HERE + + private slots: + void modeComboBoxActivated(int); + + private: + void displayFiles(int modeInteger); + + const SceneFile* m_sceneFile; + + QComboBox* m_modeComboBox; + + QTextEdit* m_textEdit; + + // ADD_NEW_MEMBERS_HERE + + }; + +#ifdef __SCENE_FILE_INFORMATION_DIALOG_DECLARE__ + // +#endif // __SCENE_FILE_INFORMATION_DIALOG_DECLARE__ + +} // namespace +#endif //__SCENE_FILE_INFORMATION_DIALOG_H__ -- GitLab From c04b9f31f4c3c9d5b8642f9c3c818b64bdd10d8e Mon Sep 17 00:00:00 2001 From: John Harwell Date: Fri, 21 Sep 2018 15:14:23 -0500 Subject: [PATCH 058/427] Added print method for scene dialogs list file info revision but disabled for now. --- src/Files/SceneDataFileInfo.cxx | 17 +++++++++++++++++ src/Files/SceneDataFileInfo.h | 2 ++ 2 files changed, 19 insertions(+) diff --git a/src/Files/SceneDataFileInfo.cxx b/src/Files/SceneDataFileInfo.cxx index 0e10f8ec9..e1618a783 100644 --- a/src/Files/SceneDataFileInfo.cxx +++ b/src/Files/SceneDataFileInfo.cxx @@ -294,4 +294,21 @@ SceneDataFileInfo::sort(std::vector& sceneDataFileInfo, } return (lhsPath < rhsPath); }); + + /*print (sceneDataFileInfo);*/ } + +/** + * Print the vector of file information. + * + * @param sceneDataFileInfo + * Information that is printed. + */ +void +SceneDataFileInfo::print(const std::vector& sceneDataFileInfo) +{ + for (const auto sdfi : sceneDataFileInfo) { + std::cout << sdfi.getAbsolutePathAndFileName() << std::endl; + } +} + diff --git a/src/Files/SceneDataFileInfo.h b/src/Files/SceneDataFileInfo.h index 1b3f95d51..6550fc949 100644 --- a/src/Files/SceneDataFileInfo.h +++ b/src/Files/SceneDataFileInfo.h @@ -84,6 +84,8 @@ namespace caret { static void sort(std::vector& sceneDataFileInfo, const SortMode sortMode); + static void print(const std::vector& sceneDataFileInfo); + private: void copyHelperSceneDataFileInfo(const SceneDataFileInfo& obj); -- GitLab From 75e5957589f6080c49926c33357feb638c1c9123 Mon Sep 17 00:00:00 2001 From: John Harwell Date: Fri, 21 Sep 2018 15:17:32 -0500 Subject: [PATCH 059/427] WB-636 Texture Based Annotations: Fixed bug with drawing tangent offset text annotations on a flat surface. Tangent offset annotations are sized as a percentage of a surface's height with is typically the extent of the Z-coordinates. However, flat surfaces are in the XY-plane with Z=0 and thus the Z-extent is zero. Now for tangent offset annotations on a flat surface, use the Y-extent from the flat surface. --- src/Brain/BrainOpenGLAnnotationDrawingFixedPipeline.cxx | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/Brain/BrainOpenGLAnnotationDrawingFixedPipeline.cxx b/src/Brain/BrainOpenGLAnnotationDrawingFixedPipeline.cxx index 21cfef0b0..9d74f04ea 100644 --- a/src/Brain/BrainOpenGLAnnotationDrawingFixedPipeline.cxx +++ b/src/Brain/BrainOpenGLAnnotationDrawingFixedPipeline.cxx @@ -1183,7 +1183,9 @@ BrainOpenGLAnnotationDrawingFixedPipeline::drawTwoDimAnnotationSurfaceTextureOff float normalXYZ[3]; getSurfaceNormalVector(surfaceDisplayed, vertexIndex, normalXYZ); const BoundingBox* boundingBox = surfaceDisplayed->getBoundingBox(); - const float surfaceExtentZ = boundingBox->getDifferenceZ(); + const float surfaceExtentZ = ((surfaceDisplayed->getSurfaceType() == SurfaceTypeEnum::FLAT) + ? boundingBox->getDifferenceY() + : boundingBox->getDifferenceZ()); /* * Need to restore model space -- GitLab From 066f9cebd6521e2573f1078a12ab8d0a2aed1b0a Mon Sep 17 00:00:00 2001 From: John Harwell Date: Tue, 25 Sep 2018 15:24:24 -0500 Subject: [PATCH 060/427] WB-826 Move scene file to top of zipped dataset or warn if not in base directory: To Scene File Information Dialog, add new option to list the data files using a hierarchical (tree) representation. At this time, this is working only for files shown in absolute paths. --- src/GuiQt/CMakeLists.txt | 5 + src/GuiQt/SceneDataFileTreeItem.cxx | 130 ++++++++++ src/GuiQt/SceneDataFileTreeItem.h | 79 ++++++ src/GuiQt/SceneDataFileTreeItemModel.cxx | 293 +++++++++++++++++++++++ src/GuiQt/SceneDataFileTreeItemModel.h | 80 +++++++ src/GuiQt/SceneFileInformationDialog.cxx | 82 ++++++- src/GuiQt/SceneFileInformationDialog.h | 13 + 7 files changed, 670 insertions(+), 12 deletions(-) create mode 100644 src/GuiQt/SceneDataFileTreeItem.cxx create mode 100644 src/GuiQt/SceneDataFileTreeItem.h create mode 100644 src/GuiQt/SceneDataFileTreeItemModel.cxx create mode 100644 src/GuiQt/SceneDataFileTreeItemModel.h diff --git a/src/GuiQt/CMakeLists.txt b/src/GuiQt/CMakeLists.txt index a5f817563..d8a646603 100644 --- a/src/GuiQt/CMakeLists.txt +++ b/src/GuiQt/CMakeLists.txt @@ -166,6 +166,7 @@ ELSE() SceneCreateReplaceDialog.h SceneDialog.h SceneFileInformationDialog.h + SceneDataFileTreeItemModel.h ScenePreviewDialog.h SceneReplaceAllDialog.h SceneShowOptionsDialog.h @@ -399,6 +400,8 @@ SceneBasePathWidget.h SceneCreateReplaceDialog.h SceneDialog.h SceneFileInformationDialog.h +SceneDataFileTreeItem.h +SceneDataFileTreeItemModel.h ScenePreviewDialog.h SceneReplaceAllDialog.h SceneShowOptionsDialog.h @@ -628,6 +631,8 @@ SceneBasePathWidget.cxx SceneCreateReplaceDialog.cxx SceneDialog.cxx SceneFileInformationDialog.cxx +SceneDataFileTreeItem.cxx +SceneDataFileTreeItemModel.cxx ScenePreviewDialog.cxx SceneReplaceAllDialog.cxx SceneShowOptionsDialog.cxx diff --git a/src/GuiQt/SceneDataFileTreeItem.cxx b/src/GuiQt/SceneDataFileTreeItem.cxx new file mode 100644 index 000000000..87de03694 --- /dev/null +++ b/src/GuiQt/SceneDataFileTreeItem.cxx @@ -0,0 +1,130 @@ + +/*LICENSE_START*/ +/* + * Copyright (C) 2018 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + +#define __SCENE_DATA_FILE_TREE_ITEM_DECLARE__ +#include "SceneDataFileTreeItem.h" +#undef __SCENE_DATA_FILE_TREE_ITEM_DECLARE__ + +#include "CaretAssert.h" +#include "FileInformation.h" + +using namespace caret; + + + +/** + * \class caret::SceneDataFileTreeItem + * \brief Item for insertion in a SceneDataFileTreeItemModel + * \ingroup GuiQt + */ + +/** + * Constructor. + * + * @param text + * Text for the model item + * @param absolutePathName + * Absolute path name including name of file (if a data file) + * @param itemType + * Item type indicating file or directory. + * Use getItemTypeDirectory() or getItemTypeFile(). + */ +SceneDataFileTreeItem::SceneDataFileTreeItem(const AString& text, + const AString& absolutePathName, + const int32_t itemType) +: QStandardItem(text), +m_absolutePathName(absolutePathName), +m_itemType(itemType) +{ + FileInformation fileInfo(absolutePathName); + if ( ! fileInfo.exists()) { + setText(text + + " "); + } + + setColumnCount(1); +} + +/** + * Destructor. + */ +SceneDataFileTreeItem::~SceneDataFileTreeItem() +{ +} + +/** + * Copy constructor. + * @param obj + * Object that is copied. + */ +SceneDataFileTreeItem::SceneDataFileTreeItem(const SceneDataFileTreeItem& obj) +: QStandardItem(obj) +{ + this->copyHelperSceneDataFileTreeItem(obj); +} + +/** + * Assignment operator. + * @param obj + * Data copied from obj to this. + * @return + * Reference to this object. + */ +SceneDataFileTreeItem& +SceneDataFileTreeItem::operator=(const SceneDataFileTreeItem& obj) +{ + if (this != &obj) { + QStandardItem::operator=(obj); + this->copyHelperSceneDataFileTreeItem(obj); + } + return *this; +} + +/** + * Helps with copying an object of this type. + * @param obj + * Object that is copied. + */ +void +SceneDataFileTreeItem::copyHelperSceneDataFileTreeItem(const SceneDataFileTreeItem& obj) +{ + m_absolutePathName = obj.m_absolutePathName; + m_itemType = obj.m_itemType; +} + +/** + * @return Full absolute path name + */ +AString +SceneDataFileTreeItem::getAbsolutePathName() const +{ + return m_absolutePathName; +} + +/** + * @return The type of this item. The type is used to distinguish custom items from the base class + */ +int +SceneDataFileTreeItem::type() const +{ + return m_itemType; +} + diff --git a/src/GuiQt/SceneDataFileTreeItem.h b/src/GuiQt/SceneDataFileTreeItem.h new file mode 100644 index 000000000..bd59b55e9 --- /dev/null +++ b/src/GuiQt/SceneDataFileTreeItem.h @@ -0,0 +1,79 @@ +#ifndef __SCENE_DATA_FILE_TREE_ITEM_H__ +#define __SCENE_DATA_FILE_TREE_ITEM_H__ + +/*LICENSE_START*/ +/* + * Copyright (C) 2018 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + + + +#include + +#include + +#include "AString.h" + +namespace caret { + + class SceneDataFileTreeItem : public QStandardItem { + + public: + /** + * @return ItemType for a directory + */ + static int32_t getItemTypeDirectory() { return (QStandardItem::UserType + 1); } + + /** + * @return ItemType for a data file + */ + static int32_t getItemTypeFile() { return (QStandardItem::UserType + 2); } + + SceneDataFileTreeItem(const AString& text, + const AString& absolutePathName, + const int32_t itemType); + + virtual ~SceneDataFileTreeItem(); + + SceneDataFileTreeItem(const SceneDataFileTreeItem&); + + SceneDataFileTreeItem& operator=(const SceneDataFileTreeItem&); + + AString getAbsolutePathName() const; + + virtual int type() const; + + // ADD_NEW_METHODS_HERE + + private: + void copyHelperSceneDataFileTreeItem(const SceneDataFileTreeItem& obj); + + AString m_absolutePathName; + + int32_t m_itemType; + + // ADD_NEW_MEMBERS_HERE + + }; + +#ifdef __SCENE_DATA_FILE_TREE_ITEM_DECLARE__ + // +#endif // __SCENE_DATA_FILE_TREE_ITEM_DECLARE__ + +} // namespace +#endif //__SCENE_DATA_FILE_TREE_ITEM_H__ diff --git a/src/GuiQt/SceneDataFileTreeItemModel.cxx b/src/GuiQt/SceneDataFileTreeItemModel.cxx new file mode 100644 index 000000000..f1b62e0af --- /dev/null +++ b/src/GuiQt/SceneDataFileTreeItemModel.cxx @@ -0,0 +1,293 @@ + +/*LICENSE_START*/ +/* + * Copyright (C) 2018 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + +#define __SCENE_DATA_FILE_TREE_ITEM_MODEL_DECLARE__ +#include "SceneDataFileTreeItemModel.h" +#undef __SCENE_DATA_FILE_TREE_ITEM_MODEL_DECLARE__ + +#include +#include + +#include "CaretAssert.h" +#include "CaretLogger.h" +#include "FileInformation.h" +#include "SceneDataFileTreeItem.h" + +using namespace caret; + +static bool debugFlag(false); + +/** + * \class caret::SceneDataFileTreeItemModel + * \brief Tree item model for hierarchical list of files in a scene file + * \ingroup GuiQt + */ + +/** + * Constructor. + * + * @param relativeToPath + * When the sortMode is a "relative to" mode, this contains that path that files are relative to. + * @param sceneDataFileInfo + * The scene file info. + * @param sortMode + * The sorting mode. + */ +SceneDataFileTreeItemModel::SceneDataFileTreeItemModel(const AString& /*relativeToPath */, + const std::vector& sceneDataFileInfo, + const SceneDataFileInfo::SortMode sortMode) +: QStandardItemModel() +{ + for (const auto dataFileInfo : sceneDataFileInfo) { + AString pathName; + AString pathAndFileName; + bool relativePathFlag = false; + switch (sortMode) { + case SceneDataFileInfo::SortMode::AbsolutePath: + pathName = dataFileInfo.getAbsolutePath(); + pathAndFileName = dataFileInfo.getAbsolutePathAndFileName(); + break; + case SceneDataFileInfo::SortMode::RelativeToBasePath: + pathName = dataFileInfo.getRelativePathToBasePath(); + pathAndFileName = (dataFileInfo.getRelativePathToBasePath() + + "/" + + dataFileInfo.getDataFileName()); + relativePathFlag = true; + break; + case SceneDataFileInfo::SortMode::RelativeToSceneFilePath: + pathName = dataFileInfo.getRelativePathToSceneFile(); + pathAndFileName = (dataFileInfo.getRelativePathToSceneFile() + + "/" + + dataFileInfo.getDataFileName()); + relativePathFlag = true; + break; + } + + + addFile(pathAndFileName, + dataFileInfo.getSceneIndicesAsString()); + } +} + +/** + * Destructor. + */ +SceneDataFileTreeItemModel::~SceneDataFileTreeItemModel() +{ +} + +/** + * Find or add all components in a directory path. + * @param absoluteDirName + * Full path of directory. + * @return + * Tree item containing directory with the given name. + */ +SceneDataFileTreeItem* +SceneDataFileTreeItemModel::addFindDirectoryPath(const AString& absoluteDirName) +{ + SceneDataFileTreeItem* directoryItemOut = findDirectory(absoluteDirName); + + if (directoryItemOut == NULL) { + AString parentDirName; + AString dirName; + + const AString httpsPrefix("https://"); + + AString rootPrefix("/"); + AString nameForSplitting(absoluteDirName); + if (absoluteDirName.startsWith(httpsPrefix)) { + nameForSplitting = absoluteDirName.mid(httpsPrefix.length()); + rootPrefix = httpsPrefix; + } + + QStringList components(nameForSplitting.split("/")); + + for (int32_t i = 0; i < components.length(); i++) { + if (i == 0) { + if (absoluteDirName.startsWith(rootPrefix)) { + dirName = rootPrefix; + addDirectory(dirName, + parentDirName); + } + } + + parentDirName = dirName; + + if ( ! dirName.endsWith('/')) { + dirName.append("/"); + } + dirName.append(components.at(i)); + addDirectory(dirName, + parentDirName); + } + + directoryItemOut = findDirectory(absoluteDirName); + CaretAssert(directoryItemOut); + } + + return directoryItemOut; +} + + +/** + * Add a directory. If directory already in tree, the existing item is returned. + * + * @param absoluteDirName + * Full path of directory. + * @param absoluteParentDirName + * Full path of parent directory. + * @return + * Tree item containing directory. + */ +SceneDataFileTreeItem* +SceneDataFileTreeItemModel::addDirectory(const AString& absoluteDirName, + const AString& absoluteParentDirName) +{ + SceneDataFileTreeItem* directoryItemOut = findDirectory(absoluteDirName); + + if (directoryItemOut == NULL) { + if (absoluteParentDirName.isEmpty()) { + directoryItemOut = new SceneDataFileTreeItem(absoluteDirName, + absoluteDirName, + SceneDataFileTreeItem::getItemTypeDirectory()); + invisibleRootItem()->appendRow(directoryItemOut); + m_directoryToTreeItemMap.insert(std::make_pair(absoluteDirName, + directoryItemOut)); + if (debugFlag) { + std::cout << "Added root directory: " << absoluteDirName << std::endl; + } + } + else { + SceneDataFileTreeItem* parentDirItem = findDirectory(absoluteParentDirName); + if (parentDirItem != NULL) { + FileInformation dirInfo(absoluteDirName); + const AString name = dirInfo.getFileName(); + directoryItemOut = new SceneDataFileTreeItem(name, + absoluteDirName, + SceneDataFileTreeItem::getItemTypeDirectory()); + parentDirItem->appendRow(directoryItemOut); + + m_directoryToTreeItemMap.insert(std::make_pair(absoluteDirName, + directoryItemOut)); + if (debugFlag) { + std::cout << "Added directory " << name << " to parent " << absoluteParentDirName << std::endl; + } + } + else { + CaretLogSevere("Unable to find parent directory named " + + absoluteParentDirName + + " for " + + absoluteDirName); + } + } + } + + return directoryItemOut; +} + +/** + * Find a directory with the given directory name. + * + * @param absoluteDirName + * Full path including directory. + * @return + * Tree item containing directory or NULL if not found. + */ +SceneDataFileTreeItem* +SceneDataFileTreeItemModel::findDirectory(const AString& absoluteDirName) +{ + SceneDataFileTreeItem* directoryItemOut = NULL; + + auto iter = m_directoryToTreeItemMap.find(absoluteDirName); + if (iter != m_directoryToTreeItemMap.end()) { + directoryItemOut = iter->second; + } + + return directoryItemOut; +} + +/** + * Add a file. If file already in tree, the existing item is returned. + * + * @param absoluteFilePathAndName + * Full path including directory and filename. + * @param sceneIndicesText + * Text containing indices of scenes using file. + * @return + * Tree item containing file. + */ +SceneDataFileTreeItem* +SceneDataFileTreeItemModel::addFile(const AString& absoluteFilePathAndName, + const AString& sceneIndicesText) +{ + SceneDataFileTreeItem* fileItemOut = findFile(absoluteFilePathAndName); + + if (fileItemOut == NULL) { + FileInformation fileInfo(absoluteFilePathAndName); + SceneDataFileTreeItem* directoryItem = addFindDirectoryPath(fileInfo.getAbsolutePath()); + if (directoryItem != NULL) { + AString itemText(fileInfo.getFileName()); + if ( ! sceneIndicesText.isEmpty()) { + itemText.append(" (" + + sceneIndicesText + + ")"); + } + fileItemOut = new SceneDataFileTreeItem(itemText, + absoluteFilePathAndName, + SceneDataFileTreeItem::getItemTypeFile()); + directoryItem->appendRow(fileItemOut); + m_fileNameToTreeItemMap.insert(std::make_pair(absoluteFilePathAndName, + fileItemOut)); + } + else { + CaretLogSevere("Failed to find directory named " + + fileInfo.getAbsolutePath() + + " for inserting file named " + + fileInfo.getFileName()); + } + } + + return fileItemOut; +} + +/** + * Find a file with the given filename. + * + * @param absoluteFilePathAndName + * Full path including directory and filename. + * @return + * Tree item containing file or NULL if not found. + */ +SceneDataFileTreeItem* +SceneDataFileTreeItemModel::findFile(const AString& absoluteFilePathAndName) +{ + SceneDataFileTreeItem* fileItemOut = NULL; + + auto iter = m_fileNameToTreeItemMap.find(absoluteFilePathAndName); + if (iter != m_fileNameToTreeItemMap.end()) { + fileItemOut = iter->second; + } + + return fileItemOut; +} + + diff --git a/src/GuiQt/SceneDataFileTreeItemModel.h b/src/GuiQt/SceneDataFileTreeItemModel.h new file mode 100644 index 000000000..488660045 --- /dev/null +++ b/src/GuiQt/SceneDataFileTreeItemModel.h @@ -0,0 +1,80 @@ +#ifndef __SCENE_DATA_FILE_TREE_ITEM_MODEL_H__ +#define __SCENE_DATA_FILE_TREE_ITEM_MODEL_H__ + +/*LICENSE_START*/ +/* + * Copyright (C) 2018 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + + +#include +#include + +#include + +#include "SceneDataFileInfo.h" + + +namespace caret { + + class SceneDataFileTreeItem; + + class SceneDataFileTreeItemModel : public QStandardItemModel { + + Q_OBJECT + + public: + SceneDataFileTreeItemModel(const AString& relativeToPath, + const std::vector& sceneDataFileInfo, + const SceneDataFileInfo::SortMode sortMode); + + virtual ~SceneDataFileTreeItemModel(); + + SceneDataFileTreeItemModel(const SceneDataFileTreeItemModel&) = delete; + + SceneDataFileTreeItemModel& operator=(const SceneDataFileTreeItemModel&) = delete; + + SceneDataFileTreeItem* addFindDirectoryPath(const AString& absoluteDirName); + + SceneDataFileTreeItem* addDirectory(const AString& absoluteDirName, + const AString& absoluteParentDirName); + + SceneDataFileTreeItem* findDirectory(const AString& absoluteDirName); + + SceneDataFileTreeItem* addFile(const AString& absoluteFilePathAndName, + const AString& sceneIndicesText); + + SceneDataFileTreeItem* findFile(const AString& absoluteFilePathAndName); + + // ADD_NEW_METHODS_HERE + + private: + std::map m_directoryToTreeItemMap; + + std::map m_fileNameToTreeItemMap; + + // ADD_NEW_MEMBERS_HERE + + }; + +#ifdef __SCENE_DATA_FILE_TREE_ITEM_MODEL_DECLARE__ + // +#endif // __SCENE_DATA_FILE_TREE_ITEM_MODEL_DECLARE__ + +} // namespace +#endif //__SCENE_DATA_FILE_TREE_ITEM_MODEL_H__ diff --git a/src/GuiQt/SceneFileInformationDialog.cxx b/src/GuiQt/SceneFileInformationDialog.cxx index ff0395678..a573d69cd 100644 --- a/src/GuiQt/SceneFileInformationDialog.cxx +++ b/src/GuiQt/SceneFileInformationDialog.cxx @@ -24,13 +24,19 @@ #undef __SCENE_FILE_INFORMATION_DIALOG_DECLARE__ #include +#include #include #include +#include +#include #include +#include #include #include "CaretAssert.h" #include "CursorDisplayScoped.h" +#include "FileInformation.h" +#include "SceneDataFileTreeItemModel.h" #include "SceneFile.h" #include "WuQMessageBox.h" @@ -62,32 +68,76 @@ m_sceneFile(sceneFile) setApplyButtonText(""); m_modeComboBox = new QComboBox(); - QLabel* modeLabel = new QLabel("Show Files"); - m_modeComboBox->addItem("Absolute Path", + QLabel* modeLabel = new QLabel("Show Files:"); + m_modeComboBox->addItem("With Absolute Path", static_cast(SceneDataFileInfo::SortMode::AbsolutePath)); m_modeComboBox->addItem("Relative to Base Path", static_cast(SceneDataFileInfo::SortMode::RelativeToBasePath)); m_modeComboBox->addItem("Relative to Scene File Path", static_cast(SceneDataFileInfo::SortMode::RelativeToSceneFilePath)); - + m_modeComboBox->setSizeAdjustPolicy(QComboBox::SizeAdjustPolicy::AdjustToContents); QObject::connect(m_modeComboBox, static_cast(&QComboBox::activated), this, &SceneFileInformationDialog::modeComboBoxActivated); - m_textEdit = new QTextEdit(); + AString basePathName; + std::vector missingDataFiles; + AString basePathErrorMessage; + const bool validBasePathFlag = sceneFile->findBaseDirectoryForDataFiles(basePathName, + missingDataFiles, + basePathErrorMessage); + if ( ! validBasePathFlag) { + basePathName = basePathErrorMessage; + } - QHBoxLayout* modeLayout = new QHBoxLayout(); - modeLayout->addWidget(modeLabel), - modeLayout->addWidget(m_modeComboBox); - modeLayout->addStretch(); + QLabel* basePathLabel = new QLabel("Base Path:"); + m_basePathLineEdit = new QLineEdit(); + m_basePathLineEdit->setReadOnly(true); + m_basePathLineEdit->setText(basePathName); + m_basePathLineEdit->home(true); + + FileInformation sceneFileInfo(sceneFile->getFileName()); + QLabel* sceneFileNameLabel = new QLabel("Scene File Name:"); + m_sceneFileNameLineEdit = new QLineEdit(); + m_sceneFileNameLineEdit->setReadOnly(true); + m_sceneFileNameLineEdit->setText(sceneFileInfo.getFileName()); + m_sceneFileNameLineEdit->home(true); + QLabel* sceneFilePathLabel = new QLabel("Scene File Path:"); + m_sceneFilePathLineEdit = new QLineEdit(); + m_sceneFilePathLineEdit->setReadOnly(true); + m_sceneFilePathLineEdit->setText(sceneFileInfo.getPathName()); + m_sceneFilePathLineEdit->home(true); + + m_textEdit = new QTextEdit(); + + m_sceneFileHierarchyTreeView = new QTreeView(); + m_sceneFileHierarchyTreeView->setHeaderHidden(true); + + QTabWidget* tabWidget = new QTabWidget(); + tabWidget->addTab(m_sceneFileHierarchyTreeView, "Hierarchy"); + tabWidget->addTab(m_textEdit, "List"); + + QGridLayout* namesLayout = new QGridLayout(); + namesLayout->setColumnStretch(0, 0); + namesLayout->setColumnStretch(1, 100); + namesLayout->addWidget(basePathLabel, 0, 0); + namesLayout->addWidget(m_basePathLineEdit, 0, 1); + namesLayout->addWidget(sceneFileNameLabel, 1, 0); + namesLayout->addWidget(m_sceneFileNameLineEdit, 1, 1); + namesLayout->addWidget(sceneFilePathLabel, 2, 0); + namesLayout->addWidget(m_sceneFilePathLineEdit, 2, 1); + namesLayout->addWidget(modeLabel, 3, 0); + namesLayout->addWidget(m_modeComboBox, 3, 1, Qt::AlignLeft); + QWidget* dialogWidget = new QWidget(); QVBoxLayout* dialogLayout = new QVBoxLayout(dialogWidget); - dialogLayout->addLayout(modeLayout, 0); - dialogLayout->addWidget(m_textEdit, 100); + dialogLayout->setSpacing(3); + dialogLayout->addLayout(namesLayout, 0); + dialogLayout->addWidget(tabWidget, 100); setCentralWidget(dialogWidget, WuQDialog::SCROLL_AREA_NEVER); - const SceneDataFileInfo::SortMode defaultSortMode(SceneDataFileInfo::SortMode::RelativeToSceneFilePath); + const SceneDataFileInfo::SortMode defaultSortMode(SceneDataFileInfo::SortMode::AbsolutePath); QSignalBlocker blocker(m_modeComboBox); m_modeComboBox->setCurrentIndex(static_cast(defaultSortMode)); displayFiles(static_cast(defaultSortMode)); @@ -133,7 +183,7 @@ SceneFileInformationDialog::displayFiles(int modeInteger) std::vector fileSceneInfo = m_sceneFile->getAllDataFileInfoFromAllScenes(); SceneDataFileInfo::sort(fileSceneInfo, - SceneDataFileInfo::SortMode::RelativeToSceneFilePath); + sortMode); if (fileSceneInfo.empty()) { WuQMessageBox::errorOk(this, @@ -141,6 +191,14 @@ SceneFileInformationDialog::displayFiles(int modeInteger) return; } + { + m_sceneFileHierarchyTreeModel.reset(new SceneDataFileTreeItemModel("", + fileSceneInfo, + sortMode)); + m_sceneFileHierarchyTreeView->setModel(m_sceneFileHierarchyTreeModel.get()); + m_sceneFileHierarchyTreeView->expandAll(); + } + const AString sceneFileName = m_sceneFile->getFileName(); AString text(""); diff --git a/src/GuiQt/SceneFileInformationDialog.h b/src/GuiQt/SceneFileInformationDialog.h index ed0bb740e..194bc0aed 100644 --- a/src/GuiQt/SceneFileInformationDialog.h +++ b/src/GuiQt/SceneFileInformationDialog.h @@ -28,10 +28,13 @@ #include "WuQDialogNonModal.h" class QComboBox; +class QLineEdit; class QTextEdit; +class QTreeView; namespace caret { + class SceneDataFileTreeItemModel; class SceneFile; class SceneFileInformationDialog : public WuQDialogNonModal { @@ -63,6 +66,16 @@ namespace caret { QTextEdit* m_textEdit; + QTreeView* m_sceneFileHierarchyTreeView; + + std::unique_ptr m_sceneFileHierarchyTreeModel; + + QLineEdit* m_basePathLineEdit; + + QLineEdit* m_sceneFileNameLineEdit; + + QLineEdit* m_sceneFilePathLineEdit; + // ADD_NEW_MEMBERS_HERE }; -- GitLab From b4f26d28694c8449f8b94d372f700bb139f05f9d Mon Sep 17 00:00:00 2001 From: John Harwell Date: Wed, 26 Sep 2018 12:06:03 -0500 Subject: [PATCH 061/427] WB-826 Move scene file to top of zipped dataset or warn if not in base directory: Improved layout of hierarchical list of files. --- src/GuiQt/SceneDataFileTreeItemModel.cxx | 148 ++++++++++++++++++++--- src/GuiQt/SceneDataFileTreeItemModel.h | 3 +- src/GuiQt/SceneFileInformationDialog.cxx | 99 +++++++-------- src/GuiQt/SceneFileInformationDialog.h | 10 +- 4 files changed, 176 insertions(+), 84 deletions(-) diff --git a/src/GuiQt/SceneDataFileTreeItemModel.cxx b/src/GuiQt/SceneDataFileTreeItemModel.cxx index f1b62e0af..d95ac2f78 100644 --- a/src/GuiQt/SceneDataFileTreeItemModel.cxx +++ b/src/GuiQt/SceneDataFileTreeItemModel.cxx @@ -44,18 +44,44 @@ static bool debugFlag(false); /** * Constructor. * - * @param relativeToPath - * When the sortMode is a "relative to" mode, this contains that path that files are relative to. + * @param sceneFilePathAndName + * Scene file path and name. + * @param baseDirectoryPath + * Path of the base directory. * @param sceneDataFileInfo * The scene file info. * @param sortMode * The sorting mode. */ -SceneDataFileTreeItemModel::SceneDataFileTreeItemModel(const AString& /*relativeToPath */, +SceneDataFileTreeItemModel::SceneDataFileTreeItemModel(const AString& sceneFilePathAndName, + const AString& baseDirectoryPath, const std::vector& sceneDataFileInfo, const SceneDataFileInfo::SortMode sortMode) : QStandardItemModel() { + if ( ! baseDirectoryPath.isEmpty()) { + /* + * Add a root element to the tree containing the base path + */ + addDirectory(baseDirectoryPath, + ""); + } + + if ( ! sceneFilePathAndName.isEmpty()) { + /* + * Add the scene file and highlight the name to make it stand out from other files + */ + SceneDataFileTreeItem* sceneFileItem = addFile(sceneFilePathAndName, + ""); + CaretAssert(sceneFileItem); + QFont font = sceneFileItem->font(); + font.setBold(true); + sceneFileItem->setFont(font); + } + + /* + * Add the data files + */ for (const auto dataFileInfo : sceneDataFileInfo) { AString pathName; AString pathAndFileName; @@ -81,7 +107,6 @@ SceneDataFileTreeItemModel::SceneDataFileTreeItemModel(const AString& /*relative break; } - addFile(pathAndFileName, dataFileInfo.getSceneIndicesAsString()); } @@ -107,9 +132,6 @@ SceneDataFileTreeItemModel::addFindDirectoryPath(const AString& absoluteDirName) SceneDataFileTreeItem* directoryItemOut = findDirectory(absoluteDirName); if (directoryItemOut == NULL) { - AString parentDirName; - AString dirName; - const AString httpsPrefix("https://"); AString rootPrefix("/"); @@ -120,26 +142,103 @@ SceneDataFileTreeItemModel::addFindDirectoryPath(const AString& absoluteDirName) } QStringList components(nameForSplitting.split("/")); + const int32_t componentCount = components.length(); - for (int32_t i = 0; i < components.length(); i++) { - if (i == 0) { - if (absoluteDirName.startsWith(rootPrefix)) { - dirName = rootPrefix; - addDirectory(dirName, - parentDirName); + const bool testFlag(true); + if(testFlag) { + std::vector parentDirectoryHierarchy; + + /* + * Create a vector containing all directory paths + * in the hierarchy as absolute paths. This is necessary + * to avoid duplicating a hierarchy that matches the + * base path. + */ + AString dirName; + AString parentDirName; + for (int32_t i = 0; i < componentCount; i++) { + if (i == 0) { + if (absoluteDirName.startsWith(rootPrefix)) { + dirName = rootPrefix; + parentDirectoryHierarchy.push_back(dirName); + } + } + + parentDirName = dirName; + + if ( ! dirName.endsWith('/')) { + dirName.append("/"); } + dirName.append(components.at(i)); + parentDirectoryHierarchy.push_back(dirName); } - parentDirName = dirName; + if (debugFlag) { + std::cout << "Parent directory hierarchy: " << std::endl; + for (const auto s : parentDirectoryHierarchy) { + std::cout << " " << s << std::endl; + } + } - if ( ! dirName.endsWith('/')) { - dirName.append("/"); + /* + * Start at the deepest directory path and work way up + * to find the deepest existing path. + */ + int32_t iStart(0); + const int32_t numDirs = static_cast(parentDirectoryHierarchy.size()); + for (int32_t iDir = (numDirs - 1); iDir >= 0; iDir--) { + CaretAssertVectorIndex(parentDirectoryHierarchy, iDir); + SceneDataFileTreeItem* dirItem = findDirectory(parentDirectoryHierarchy[iDir]); + if (dirItem != NULL) { + iStart = iDir + 1; + break; + } + } + if (iStart < numDirs) { + /* + * Create directories that are children of the deepest existing path + */ + for (int32_t iDir = iStart; iDir < numDirs; iDir++) { + AString parentDirName; + if (iDir > 0) { + CaretAssertVectorIndex(parentDirectoryHierarchy, iDir - 1); + parentDirName = parentDirectoryHierarchy[iDir - 1]; + } + CaretAssertVectorIndex(parentDirectoryHierarchy, iDir); + if (debugFlag) { + std::cout << "Creating directory " << parentDirectoryHierarchy[iDir] + << " with parent " << parentDirName << std::endl; + } + addDirectory(parentDirectoryHierarchy[iDir], + parentDirName); + } + } + } + else { + AString dirName; + AString parentDirName; + for (int32_t i = 0; i < componentCount; i++) { + if (i == 0) { + if (absoluteDirName.startsWith(rootPrefix)) { + dirName = rootPrefix; + addDirectory(dirName, + parentDirName); + } + } + + parentDirName = dirName; + + if ( ! dirName.endsWith('/')) { + dirName.append("/"); + } + dirName.append(components.at(i)); + addDirectory(dirName, + parentDirName); } - dirName.append(components.at(i)); - addDirectory(dirName, - parentDirName); } + + directoryItemOut = findDirectory(absoluteDirName); CaretAssert(directoryItemOut); } @@ -243,7 +342,16 @@ SceneDataFileTreeItemModel::addFile(const AString& absoluteFilePathAndName, if (fileItemOut == NULL) { FileInformation fileInfo(absoluteFilePathAndName); - SceneDataFileTreeItem* directoryItem = addFindDirectoryPath(fileInfo.getAbsolutePath()); + /* + * First look for parent directory, which may be the base directory. + */ + SceneDataFileTreeItem* directoryItem = findDirectory(fileInfo.getAbsolutePath()); + if (directoryItem == NULL) { + /* + * Start looking/adding directory from root down to file's parent + */ + directoryItem = addFindDirectoryPath(fileInfo.getAbsolutePath()); + } if (directoryItem != NULL) { AString itemText(fileInfo.getFileName()); if ( ! sceneIndicesText.isEmpty()) { diff --git a/src/GuiQt/SceneDataFileTreeItemModel.h b/src/GuiQt/SceneDataFileTreeItemModel.h index 488660045..dbdf251a8 100644 --- a/src/GuiQt/SceneDataFileTreeItemModel.h +++ b/src/GuiQt/SceneDataFileTreeItemModel.h @@ -39,7 +39,8 @@ namespace caret { Q_OBJECT public: - SceneDataFileTreeItemModel(const AString& relativeToPath, + SceneDataFileTreeItemModel(const AString& sceneFilePathAndName, + const AString& baseDirectoryPath, const std::vector& sceneDataFileInfo, const SceneDataFileInfo::SortMode sortMode); diff --git a/src/GuiQt/SceneFileInformationDialog.cxx b/src/GuiQt/SceneFileInformationDialog.cxx index a573d69cd..6716f2f28 100644 --- a/src/GuiQt/SceneFileInformationDialog.cxx +++ b/src/GuiQt/SceneFileInformationDialog.cxx @@ -23,7 +23,6 @@ #include "SceneFileInformationDialog.h" #undef __SCENE_FILE_INFORMATION_DIALOG_DECLARE__ -#include #include #include #include @@ -67,18 +66,6 @@ m_sceneFile(sceneFile) setDeleteWhenClosed(true); setApplyButtonText(""); - m_modeComboBox = new QComboBox(); - QLabel* modeLabel = new QLabel("Show Files:"); - m_modeComboBox->addItem("With Absolute Path", - static_cast(SceneDataFileInfo::SortMode::AbsolutePath)); - m_modeComboBox->addItem("Relative to Base Path", - static_cast(SceneDataFileInfo::SortMode::RelativeToBasePath)); - m_modeComboBox->addItem("Relative to Scene File Path", - static_cast(SceneDataFileInfo::SortMode::RelativeToSceneFilePath)); - m_modeComboBox->setSizeAdjustPolicy(QComboBox::SizeAdjustPolicy::AdjustToContents); - QObject::connect(m_modeComboBox, static_cast(&QComboBox::activated), - this, &SceneFileInformationDialog::modeComboBoxActivated); - AString basePathName; std::vector missingDataFiles; AString basePathErrorMessage; @@ -126,8 +113,6 @@ m_sceneFile(sceneFile) namesLayout->addWidget(m_sceneFileNameLineEdit, 1, 1); namesLayout->addWidget(sceneFilePathLabel, 2, 0); namesLayout->addWidget(m_sceneFilePathLineEdit, 2, 1); - namesLayout->addWidget(modeLabel, 3, 0); - namesLayout->addWidget(m_modeComboBox, 3, 1, Qt::AlignLeft); QWidget* dialogWidget = new QWidget(); QVBoxLayout* dialogLayout = new QVBoxLayout(dialogWidget); @@ -137,10 +122,8 @@ m_sceneFile(sceneFile) setCentralWidget(dialogWidget, WuQDialog::SCROLL_AREA_NEVER); - const SceneDataFileInfo::SortMode defaultSortMode(SceneDataFileInfo::SortMode::AbsolutePath); - QSignalBlocker blocker(m_modeComboBox); - m_modeComboBox->setCurrentIndex(static_cast(defaultSortMode)); - displayFiles(static_cast(defaultSortMode)); + displayFilesHierarchy(); + displayFilesList(); setSizeOfDialogWhenDisplayed(QSize(600, 800)); } @@ -153,32 +136,12 @@ SceneFileInformationDialog::~SceneFileInformationDialog() } /** - * Called when mode combo box is selected - */ -void -SceneFileInformationDialog::modeComboBoxActivated(int mode) -{ - CursorDisplayScoped cursor; - cursor.showWaitCursor(); - - displayFiles(mode); -} - -/** - * Called when mode combo box is selected + * Setup the list of files */ void -SceneFileInformationDialog::displayFiles(int modeInteger) +SceneFileInformationDialog::displayFilesList() { - const SceneDataFileInfo::SortMode sortMode = static_cast(modeInteger); - switch (sortMode) { - case SceneDataFileInfo::SortMode::AbsolutePath: - break; - case SceneDataFileInfo::SortMode::RelativeToBasePath: - break; - case SceneDataFileInfo::SortMode::RelativeToSceneFilePath: - break; - } + const SceneDataFileInfo::SortMode sortMode = SceneDataFileInfo::SortMode::RelativeToSceneFilePath; CaretAssert(m_sceneFile); std::vector fileSceneInfo = m_sceneFile->getAllDataFileInfoFromAllScenes(); @@ -191,30 +154,23 @@ SceneFileInformationDialog::displayFiles(int modeInteger) return; } - { - m_sceneFileHierarchyTreeModel.reset(new SceneDataFileTreeItemModel("", - fileSceneInfo, - sortMode)); - m_sceneFileHierarchyTreeView->setModel(m_sceneFileHierarchyTreeModel.get()); - m_sceneFileHierarchyTreeView->expandAll(); - } - - const AString sceneFileName = m_sceneFile->getFileName(); - AString text(""); - AString baseDirectoryName; std::vector missingFileNames; AString errorMessage; const bool validBasePathFlag = m_sceneFile->findBaseDirectoryForDataFiles(baseDirectoryName, - missingFileNames, - errorMessage); + missingFileNames, + errorMessage); + + const AString sceneFileName = m_sceneFile->getFileName(); + AString text(""); + text.appendWithNewLine("Automatic Base Path: " + (validBasePathFlag ? baseDirectoryName : ("INVALID: " + errorMessage)) + "

"); text.appendWithNewLine("Scene File: " + sceneFileName + "

"); - + bool needListEndElementFlag = false; AString lastPathName("bogus ##(*&$UI()#NFGK path name"); for (const auto& fileData : fileSceneInfo) { @@ -271,3 +227,34 @@ SceneFileInformationDialog::displayFiles(int modeInteger) m_textEdit->setHtml(text); } +/** + * Setup the hierarchy of files + */ +void +SceneFileInformationDialog::displayFilesHierarchy() +{ + AString baseDirectoryName; + std::vector missingFileNames; + AString errorMessage; + const bool validBasePathFlag = m_sceneFile->findBaseDirectoryForDataFiles(baseDirectoryName, + missingFileNames, + errorMessage); + if ( ! validBasePathFlag) { + return; + } + CaretAssert(m_sceneFile); + + std::vector fileSceneInfo = m_sceneFile->getAllDataFileInfoFromAllScenes(); + SceneDataFileInfo::sort(fileSceneInfo, + SceneDataFileInfo::SortMode::AbsolutePath); + + m_sceneFileHierarchyTreeModel.reset(new SceneDataFileTreeItemModel(m_sceneFile->getFileName(), + baseDirectoryName, + fileSceneInfo, + SceneDataFileInfo::SortMode::AbsolutePath)); + m_sceneFileHierarchyTreeView->setModel(m_sceneFileHierarchyTreeModel.get()); + m_sceneFileHierarchyTreeView->expandAll(); +} + + + diff --git a/src/GuiQt/SceneFileInformationDialog.h b/src/GuiQt/SceneFileInformationDialog.h index 194bc0aed..18234bb57 100644 --- a/src/GuiQt/SceneFileInformationDialog.h +++ b/src/GuiQt/SceneFileInformationDialog.h @@ -27,7 +27,6 @@ #include "WuQDialogNonModal.h" -class QComboBox; class QLineEdit; class QTextEdit; class QTreeView; @@ -54,15 +53,12 @@ namespace caret { // ADD_NEW_METHODS_HERE - private slots: - void modeComboBoxActivated(int); - private: - void displayFiles(int modeInteger); + void displayFilesList(); - const SceneFile* m_sceneFile; + void displayFilesHierarchy(); - QComboBox* m_modeComboBox; + const SceneFile* m_sceneFile; QTextEdit* m_textEdit; -- GitLab From 775e9a648ec8debd6ab91d9df6cd4eacbbcd1dd1 Mon Sep 17 00:00:00 2001 From: John Harwell Date: Wed, 26 Sep 2018 12:35:04 -0500 Subject: [PATCH 062/427] WB-826 Move scene file to top of zipped dataset or warn if not in base directory: Improved formatting of files list. --- src/GuiQt/SceneDataFileTreeItemModel.cxx | 137 +++++++++------------- src/GuiQt/SceneFileInformationDialog.cxx | 139 +++++++++++++++++++---- 2 files changed, 171 insertions(+), 105 deletions(-) diff --git a/src/GuiQt/SceneDataFileTreeItemModel.cxx b/src/GuiQt/SceneDataFileTreeItemModel.cxx index d95ac2f78..6ea1e4eb0 100644 --- a/src/GuiQt/SceneDataFileTreeItemModel.cxx +++ b/src/GuiQt/SceneDataFileTreeItemModel.cxx @@ -143,102 +143,75 @@ SceneDataFileTreeItemModel::addFindDirectoryPath(const AString& absoluteDirName) QStringList components(nameForSplitting.split("/")); const int32_t componentCount = components.length(); + + std::vector parentDirectoryHierarchy; - const bool testFlag(true); - if(testFlag) { - std::vector parentDirectoryHierarchy; - - /* - * Create a vector containing all directory paths - * in the hierarchy as absolute paths. This is necessary - * to avoid duplicating a hierarchy that matches the - * base path. - */ - AString dirName; - AString parentDirName; - for (int32_t i = 0; i < componentCount; i++) { - if (i == 0) { - if (absoluteDirName.startsWith(rootPrefix)) { - dirName = rootPrefix; - parentDirectoryHierarchy.push_back(dirName); - } - } - - parentDirName = dirName; - - if ( ! dirName.endsWith('/')) { - dirName.append("/"); + /* + * Create a vector containing all directory paths + * in the hierarchy as absolute paths. This is necessary + * to avoid duplicating a hierarchy that matches the + * base path. + */ + AString dirName; + AString parentDirName; + for (int32_t i = 0; i < componentCount; i++) { + if (i == 0) { + if (absoluteDirName.startsWith(rootPrefix)) { + dirName = rootPrefix; + parentDirectoryHierarchy.push_back(dirName); } - dirName.append(components.at(i)); - parentDirectoryHierarchy.push_back(dirName); } - if (debugFlag) { - std::cout << "Parent directory hierarchy: " << std::endl; - for (const auto s : parentDirectoryHierarchy) { - std::cout << " " << s << std::endl; - } - } + parentDirName = dirName; - /* - * Start at the deepest directory path and work way up - * to find the deepest existing path. - */ - int32_t iStart(0); - const int32_t numDirs = static_cast(parentDirectoryHierarchy.size()); - for (int32_t iDir = (numDirs - 1); iDir >= 0; iDir--) { - CaretAssertVectorIndex(parentDirectoryHierarchy, iDir); - SceneDataFileTreeItem* dirItem = findDirectory(parentDirectoryHierarchy[iDir]); - if (dirItem != NULL) { - iStart = iDir + 1; - break; - } + if ( ! dirName.endsWith('/')) { + dirName.append("/"); } - if (iStart < numDirs) { - /* - * Create directories that are children of the deepest existing path - */ - for (int32_t iDir = iStart; iDir < numDirs; iDir++) { - AString parentDirName; - if (iDir > 0) { - CaretAssertVectorIndex(parentDirectoryHierarchy, iDir - 1); - parentDirName = parentDirectoryHierarchy[iDir - 1]; - } - CaretAssertVectorIndex(parentDirectoryHierarchy, iDir); - if (debugFlag) { - std::cout << "Creating directory " << parentDirectoryHierarchy[iDir] - << " with parent " << parentDirName << std::endl; - } - addDirectory(parentDirectoryHierarchy[iDir], - parentDirName); - } + dirName.append(components.at(i)); + parentDirectoryHierarchy.push_back(dirName); + } + + if (debugFlag) { + std::cout << "Parent directory hierarchy: " << std::endl; + for (const auto s : parentDirectoryHierarchy) { + std::cout << " " << s << std::endl; } } - else { - AString dirName; - AString parentDirName; - for (int32_t i = 0; i < componentCount; i++) { - if (i == 0) { - if (absoluteDirName.startsWith(rootPrefix)) { - dirName = rootPrefix; - addDirectory(dirName, - parentDirName); - } + + /* + * Start at the deepest directory path and work way up + * to find the deepest existing path. + */ + int32_t iStart(0); + const int32_t numDirs = static_cast(parentDirectoryHierarchy.size()); + for (int32_t iDir = (numDirs - 1); iDir >= 0; iDir--) { + CaretAssertVectorIndex(parentDirectoryHierarchy, iDir); + SceneDataFileTreeItem* dirItem = findDirectory(parentDirectoryHierarchy[iDir]); + if (dirItem != NULL) { + iStart = iDir + 1; + break; + } + } + if (iStart < numDirs) { + /* + * Create directories that are children of the deepest existing path + */ + for (int32_t iDir = iStart; iDir < numDirs; iDir++) { + AString parentDirName; + if (iDir > 0) { + CaretAssertVectorIndex(parentDirectoryHierarchy, iDir - 1); + parentDirName = parentDirectoryHierarchy[iDir - 1]; } - - parentDirName = dirName; - - if ( ! dirName.endsWith('/')) { - dirName.append("/"); + CaretAssertVectorIndex(parentDirectoryHierarchy, iDir); + if (debugFlag) { + std::cout << "Creating directory " << parentDirectoryHierarchy[iDir] + << " with parent " << parentDirName << std::endl; } - dirName.append(components.at(i)); - addDirectory(dirName, + addDirectory(parentDirectoryHierarchy[iDir], parentDirName); } } - - directoryItemOut = findDirectory(absoluteDirName); CaretAssert(directoryItemOut); } diff --git a/src/GuiQt/SceneFileInformationDialog.cxx b/src/GuiQt/SceneFileInformationDialog.cxx index 6716f2f28..8b56979f1 100644 --- a/src/GuiQt/SceneFileInformationDialog.cxx +++ b/src/GuiQt/SceneFileInformationDialog.cxx @@ -135,6 +135,98 @@ SceneFileInformationDialog::~SceneFileInformationDialog() { } +///** +// * Setup the list of files +// */ +//void +//SceneFileInformationDialog::displayFilesList() +//{ +// const SceneDataFileInfo::SortMode sortMode = SceneDataFileInfo::SortMode::RelativeToSceneFilePath; +// CaretAssert(m_sceneFile); +// +// std::vector fileSceneInfo = m_sceneFile->getAllDataFileInfoFromAllScenes(); +// SceneDataFileInfo::sort(fileSceneInfo, +// sortMode); +// +// if (fileSceneInfo.empty()) { +// WuQMessageBox::errorOk(this, +// "Scene file is empty."); +// return; +// } +// +// AString baseDirectoryName; +// std::vector missingFileNames; +// AString errorMessage; +// const bool validBasePathFlag = m_sceneFile->findBaseDirectoryForDataFiles(baseDirectoryName, +// missingFileNames, +// errorMessage); +// +// const AString sceneFileName = m_sceneFile->getFileName(); +// AString text(""); +// +// text.appendWithNewLine("Automatic Base Path: " +// + (validBasePathFlag ? baseDirectoryName : ("INVALID: " + errorMessage)) +// + "

"); +// text.appendWithNewLine("Scene File: " +// + sceneFileName +// + "

"); +// +// bool needListEndElementFlag = false; +// AString lastPathName("bogus ##(*&$UI()#NFGK path name"); +// for (const auto& fileData : fileSceneInfo) { +// AString missingText; +// +// AString pathName; +// switch (sortMode) { +// case SceneDataFileInfo::SortMode::AbsolutePath: +// pathName = fileData.getAbsolutePath(); +// break; +// case SceneDataFileInfo::SortMode::RelativeToBasePath: +// pathName = fileData.getRelativePathToBasePath(); +// if (pathName.isEmpty()) { +// pathName = "Files in Base Path"; +// } +// break; +// case SceneDataFileInfo::SortMode::RelativeToSceneFilePath: +// pathName = fileData.getRelativePathToSceneFile(); +// if (pathName.isEmpty()) { +// pathName = "Files in Scene File Path"; +// } +// break; +// } +// +// if (pathName != lastPathName) { +// if (needListEndElementFlag) { +// text.append(""); +// } +// text.append("

" +// + pathName +// + ""); +// text.append("

    "); +// needListEndElementFlag = true; +// lastPathName = pathName; +// } +// +// if (fileData.isMissing()) { +// missingText = "MISSING "; +// } +// +// text.append("
  • " +// + missingText +// + fileData.getDataFileName() +// + " (" +// + fileData.getSceneIndicesAsString() +// + ")"); +// } +// if (needListEndElementFlag) { +// text.append("
"); +// } +// text.append(""); +// +// m_textEdit->clear(); +// m_textEdit->setHtml(text); +//} + /** * Setup the list of files */ @@ -171,8 +263,24 @@ SceneFileInformationDialog::displayFilesList() + sceneFileName + "

"); - bool needListEndElementFlag = false; - AString lastPathName("bogus ##(*&$UI()#NFGK path name"); + AString pathName; + switch (sortMode) { + case SceneDataFileInfo::SortMode::AbsolutePath: + pathName = "Absolute File Paths"; + break; + case SceneDataFileInfo::SortMode::RelativeToBasePath: + pathName = "Files in Base Path"; + break; + case SceneDataFileInfo::SortMode::RelativeToSceneFilePath: + pathName = "Data File paths relative to Scene File"; + break; + } + text.append("" + + pathName + + ""); + + text.append("

    "); + for (const auto& fileData : fileSceneInfo) { AString missingText; @@ -183,44 +291,29 @@ SceneFileInformationDialog::displayFilesList() break; case SceneDataFileInfo::SortMode::RelativeToBasePath: pathName = fileData.getRelativePathToBasePath(); - if (pathName.isEmpty()) { - pathName = "Files in Base Path"; - } break; case SceneDataFileInfo::SortMode::RelativeToSceneFilePath: pathName = fileData.getRelativePathToSceneFile(); - if (pathName.isEmpty()) { - pathName = "Files in Scene File Path"; - } break; } - - if (pathName != lastPathName) { - if (needListEndElementFlag) { - text.append("
"); - } - text.append("

" - + pathName - + ""); - text.append("
    "); - needListEndElementFlag = true; - lastPathName = pathName; + if (! pathName.isEmpty()) { + pathName.append("/ "); } if (fileData.isMissing()) { - missingText = "MISSING "; + missingText = " "; } text.append("
  • " + missingText + + pathName + fileData.getDataFileName() + " (" + fileData.getSceneIndicesAsString() + ")"); } - if (needListEndElementFlag) { - text.append("
"); - } + + text.append(""); text.append(""); m_textEdit->clear(); -- GitLab From 86aa45b58394c5d69513d514916d4986c6bb5fad Mon Sep 17 00:00:00 2001 From: John Harwell Date: Wed, 26 Sep 2018 15:04:32 -0500 Subject: [PATCH 063/427] WB-826 Move scene file to top of zipped dataset or warn if not in base directory: Fixing incorrect path for root directory on windows in tree model used for showing organization of a scene file's data files. --- src/GuiQt/SceneDataFileTreeItemModel.cxx | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/GuiQt/SceneDataFileTreeItemModel.cxx b/src/GuiQt/SceneDataFileTreeItemModel.cxx index 6ea1e4eb0..72c18295d 100644 --- a/src/GuiQt/SceneDataFileTreeItemModel.cxx +++ b/src/GuiQt/SceneDataFileTreeItemModel.cxx @@ -85,7 +85,6 @@ SceneDataFileTreeItemModel::SceneDataFileTreeItemModel(const AString& sceneFileP for (const auto dataFileInfo : sceneDataFileInfo) { AString pathName; AString pathAndFileName; - bool relativePathFlag = false; switch (sortMode) { case SceneDataFileInfo::SortMode::AbsolutePath: pathName = dataFileInfo.getAbsolutePath(); @@ -96,14 +95,12 @@ SceneDataFileTreeItemModel::SceneDataFileTreeItemModel(const AString& sceneFileP pathAndFileName = (dataFileInfo.getRelativePathToBasePath() + "/" + dataFileInfo.getDataFileName()); - relativePathFlag = true; break; case SceneDataFileInfo::SortMode::RelativeToSceneFilePath: pathName = dataFileInfo.getRelativePathToSceneFile(); pathAndFileName = (dataFileInfo.getRelativePathToSceneFile() + "/" + dataFileInfo.getDataFileName()); - relativePathFlag = true; break; } @@ -141,7 +138,8 @@ SceneDataFileTreeItemModel::addFindDirectoryPath(const AString& absoluteDirName) rootPrefix = httpsPrefix; } - QStringList components(nameForSplitting.split("/")); + QStringList components(nameForSplitting.split("/", + QString::SkipEmptyParts)); const int32_t componentCount = components.length(); std::vector parentDirectoryHierarchy; -- GitLab From 84e6a4b4e71c6906e0d6ce959da07db66d4ff6bd Mon Sep 17 00:00:00 2001 From: John Harwell Date: Wed, 26 Sep 2018 15:27:42 -0500 Subject: [PATCH 064/427] WB-826 Move scene file to top of zipped dataset or warn if not in base directory: Still trying to fix windows error for scene file hierarchy display. --- src/GuiQt/SceneDataFileTreeItemModel.cxx | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/GuiQt/SceneDataFileTreeItemModel.cxx b/src/GuiQt/SceneDataFileTreeItemModel.cxx index 72c18295d..4c7ec0e6c 100644 --- a/src/GuiQt/SceneDataFileTreeItemModel.cxx +++ b/src/GuiQt/SceneDataFileTreeItemModel.cxx @@ -162,9 +162,12 @@ SceneDataFileTreeItemModel::addFindDirectoryPath(const AString& absoluteDirName) parentDirName = dirName; - if ( ! dirName.endsWith('/')) { - dirName.append("/"); + if ( ! dirName.isEmpty()) { + if ( ! dirName.endsWith('/')) { + dirName.append("/"); + } } + dirName.append(components.at(i)); parentDirectoryHierarchy.push_back(dirName); } -- GitLab From 2ecac2f677b847df34bc75889e3fd14ecc5106ab Mon Sep 17 00:00:00 2001 From: John Harwell Date: Wed, 26 Sep 2018 16:00:23 -0500 Subject: [PATCH 065/427] WB-826 Move scene file to top of zipped dataset or warn if not in base directory: Change "List Files" button text to "List Files and Folders". --- src/GuiQt/SceneDialog.cxx | 4 ++-- src/GuiQt/SceneFileInformationDialog.cxx | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/GuiQt/SceneDialog.cxx b/src/GuiQt/SceneDialog.cxx index 8e96e6f5d..49eac3663 100644 --- a/src/GuiQt/SceneDialog.cxx +++ b/src/GuiQt/SceneDialog.cxx @@ -2145,9 +2145,9 @@ SceneDialog::createSceneFileWidget() /* * File structure buttons */ - m_showFileStructurePushButton = new QPushButton("List Files..."); + m_showFileStructurePushButton = new QPushButton("Show Files and Folders..."); WuQtUtilities::setWordWrappedToolTip(m_showFileStructurePushButton, - "In a dialog, show data files from all scenes with paths relative to the scene file"); + "In a dialog, show the organization files and folders contained in the Scene File"); QObject::connect(m_showFileStructurePushButton, &QPushButton::clicked, this, &SceneDialog::showFileStructure); diff --git a/src/GuiQt/SceneFileInformationDialog.cxx b/src/GuiQt/SceneFileInformationDialog.cxx index 8b56979f1..94b3f9383 100644 --- a/src/GuiQt/SceneFileInformationDialog.cxx +++ b/src/GuiQt/SceneFileInformationDialog.cxx @@ -59,7 +59,7 @@ using namespace caret; */ SceneFileInformationDialog::SceneFileInformationDialog(const SceneFile* sceneFile, QWidget* parent) -: WuQDialogNonModal("Scene File Information", +: WuQDialogNonModal("Files and Folders", parent), m_sceneFile(sceneFile) { -- GitLab From 039b51fc0eec966d00264c29b304c358abb3f264 Mon Sep 17 00:00:00 2001 From: John Harwell Date: Thu, 27 Sep 2018 09:41:48 -0500 Subject: [PATCH 066/427] Updated Workbench Help for Scene Dialog and Tool Tips --- .../Scenes_Window/Scenes_Window.html | 86 ++++++------ .../HelpFiles/Scenes_Window/Scenes_Window.png | Bin 328462 -> 247505 bytes .../ToolTips_icon.png | Bin 0 -> 3755 bytes .../Window_Elements_Hide-Show_Buttons.html | 122 ++++++++++-------- src/Resources/Help/help_resources.qrc | 1 + 5 files changed, 113 insertions(+), 96 deletions(-) create mode 100644 src/Resources/Help/HelpFiles/Workbench_Window/Window_Elements_Hide-Show_Buttons/ToolTips_icon.png diff --git a/src/Resources/Help/HelpFiles/Scenes_Window/Scenes_Window.html b/src/Resources/Help/HelpFiles/Scenes_Window/Scenes_Window.html index e06ebf8da..c888a5f16 100644 --- a/src/Resources/Help/HelpFiles/Scenes_Window/Scenes_Window.html +++ b/src/Resources/Help/HelpFiles/Scenes_Window/Scenes_Window.html @@ -23,6 +23,7 @@ show up in this selector.
  • New  Creates + a new Scene File.  A new Scene File is created with no scenes within it. Add any number of scenes with the Add button.
  • @@ -34,11 +35,12 @@
  • Zip  Creates a ZIP file containing the scene file and all data files it references.
  • -
  • List Files  Lists all data - files from all scenes within the scene file
    +
  • Show Files and Folders  Displays + a hierarchy or list of all data files from all scenes within + the scene file
  • -
  • Open  Displays a file selection - dialog for selecting and opening a scene file.
    +
  • Open  Displays a file + selection dialog for selecting and opening a scene file.
  • Save As   Displays a file selection dialog for saving the scene file with a new @@ -52,29 +54,32 @@


    Show Scene Group

    +

    +
      -
        -
      • Show loads the active (highlighted in blue) - scene.  Double clicking on a scene's - title/description will also show the scene.
      • -
      • Preview... opens a separate window with an enlarged - display of the highlighted scene's thumbnail along with - the name and description.
      • -
      • Options  Use background and foreground colors - from scene when a scene is created, the foreground and - background colors are added to the scene and will be - used when the scene is displayed. Note that the - background and foreground colors are adjusted using - the Preferences Dialog. When a scene is loaded that - contains background and foreground colors, they will - override the user's Preferences colors until a - different scene is loaded, a spec file is loaded, or - this checkbox is unchecked.
      • -
      -

      +
    • Show loads the active (highlighted in blue) + scene.  Double clicking on a scene's + title/description will also show the scene.
    • +
    • Preview... opens a separate window with an enlarged + display of the highlighted scene's thumbnail along with + the name and description.
    • +
    • Options  Use background and foreground colors from + scene when a scene is created, the foreground and + background colors are added to the scene and will be + used when the scene is displayed. Note that the + background and foreground colors are adjusted using the + Preferences Dialog. When a scene is loaded that contains + background and foreground colors, they will override the + user's Preferences colors until a different scene is + loaded, a spec file is loaded, or this checkbox is + unchecked.
    • +
      +
    + +

    Create Scene Group

    @@ -102,17 +107,20 @@
  • -

    Selected Scene Group -

      -
    • Move Up moves the selected - scene up one position in the list of scenes.
    • -
    • Move Down moves the selected scene - down one position in the list of scenes.
    • -
    • Delete Deletes the highlighted - scene from the scene file.
      -
    • -
    -

    +

    Selected Scene Group

    +
      + +
    • Move Up moves the selected + scene up one position in the list of scenes.
    • +
    • Move Down moves the selected scene + down one position in the list of scenes.
    • +
    • Delete Deletes the highlighted scene + from the scene file.
      +
    • +
      +
    + +

    Testing Group

    @@ -131,8 +139,8 @@

    -
    +
    diff --git a/src/Resources/Help/HelpFiles/Scenes_Window/Scenes_Window.png b/src/Resources/Help/HelpFiles/Scenes_Window/Scenes_Window.png index 0b3848d9bc3165d25e6dd7184c0b831e96810d3a..25edd6b6b811e09b8bc7c93b2fd9c762cbbb8b0c 100644 GIT binary patch literal 247505 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

    |yHwlJ?B;DXt~vnHE>4k2g2%PH&x;y88O< zgYndLq`vCE<$w!~Z@^&CU0DMD^j`v($GhUEJ?_23mc0FG9FLN}qogbyt5ibExGRE5+tZ zBwK1;n=dQhUHXam{QktBL4z0iTJ1r#jKV_33?rIpUw6!aL;`?0sgUgJ5!7j>(pQFz87-sPF_;U z?2}@1F{gQm{H{awK_-#45QYn;Aoiguf-*6Ge@&_oj!`6$NIQ+^UN>zz`k^I+9?kw~ z0v>FY&wt!FKc9YcGnpt91SQcwZ#t&0?XUR)k<|$Rn=e-$7DhbWN!rLl`oN6BfbYUST^%0tkBmZ z8){HA1qy=|Cl;A#ePWm~D!3KKE6Tmf_gH)*8>gzMhq$S%_h!d+#idp&$}gMwO$0oV z4Azw61v24^hh$#ItsAi>vjr9P{_hh~;3XNX&SiDR&c&r3rpqH?e3p=IpYM;(>&}Zf zYM)liHT}`mlbom2Wa;1*gT}mcHUH`4EsZU`o^O5~gjp`n`-ymJtv(@{nr%Lj5A2r$ zmV050TsHIhPg(6Yp^ruJnJrG*D3#PT`yOZJN*y5m;8dBcD(>|$=1TD>%FE3=Na9)= z-u1g{_K()*MGwX0pmL%RxLN6%TprCHf4A*P4d^#7kwqWw`SW61OR+lcXbpbW_T#uQ z!jk&g0$JzCw3at2>Pepy$Qy@;zdPlx$o!|5{8BW!l;%pRl@2G;OaCY|($=y|)|!mh zIUY{5s9bBRYXx#?+jWC-I_w3zoYy?6sBf)Rdf!uN!Q5wgKWztTs-Nzb&b-{8M$-AA zt^PgqzpRp{-UVDAYcQ~qD6u;b?)-kg$Yq$=`Fg?ic?xFJypIaVtn7Fx;jLzysLeY= z)AQ7)W8K__xi8aa%W$0Fc-g1(DNeQR(8$itmMuA+E#$iLSha36-|F&v`FOpI&&a5N z!D3~1F(#Sr+D2fZ9tl18&&d00l2>#V0Ju4CFr3fS&9?zS#a9DPG`kbR_nC74ed|n%UlwKds@9{pb@gQMg7l7fov)Vl# zPw+mUf^Koy1GlSVNbvoBft3MZp;rybiG}V5!m$qbZqI5bEoKX3oV&gi7KSVp=yiIZ zY_zw2Y<|A_a9+0EI`yNdv4>#xidB;cVNddXdV+^kzkaC^rMs5YSsiN)p^{i%ZFRrg z&C1=MH*Q)?WeLEp?H?RitTtH7>dk+zKbQAAkagG0q!4dhV^fIk7U+;A=gBN>NTGMD z6U1!sQp#CFD>&MJyF8nxW}~1Wp!_ktMOdDcoWtg#uI*u}|F}s~EXU<4q0hDs09~%9 zy1=Xy(C@q(q@$Uq5f)($c`W#YgtEWz)tb{->(#G(FJ)D7c2D#s1%*ecUQnpkIQ8D! z(>EhP6~lRYj+H>pG2%6E5+Gf|UY4YWqVS#wStcuI`+ae5dV8X#_XCP$jY6G4i2{pu zG4^}(in&{US*M~Lsi?-JG6L+rYv%N8omUsY-@J@D-G7AU*_g2N#@!-+wi!YuV2(H)qC4P zd--x4c0ZUi%#Wup8J)Cjx3)uIb)o6yM%zqaRyw~3{N@Y>=@ooBP^FMX@0Z*`4g*iMtc7Co`vmCozZk#W{qZjaDvRV~I zMZr5CO?3l!l-Um&qVYy=QIV0^FZDS@WH`PrBxed^>xH{aYPu9Ns z^Kp`S`aD?Atzm@rxk+VffoUBHODwD&6?Aj%_xRZLMi>EJlh0`vPW} z(=&B_lMHFj>E)$}b!DzJ_Ma*IBm|UOzLbb3z}!RfV4v6XMlPVBZ{SPW#P3v~$`Z;< zE8&O(^kV!_^dHxQc=z*adK@lbF4g)-TDz!8uV0;5>6RD22>BTwfMc3ncEj0D8av<1 znbzX;Bk#6V)O1LE!yhZG)|#dPtUIw4tYDp=8GJ&uYIn*8c@**oQLTr-KJpEnoi)fa zo-}Smhe{%_CtJ1)93MQZ8g~T^;im`W^sf#3lw4bywKHC9-k;aSEypnfoQm!pO;6bM z?RED2AvNn>nH3FfIq}QWQ8!a2B+fXR39iDxpRpx@-P<8bHM(9tL5QvOl%ref9x8jz zWyE5cbX1!@<$1zE&}vw!C1&wUk1=SaH;_tYJHWA9yu+HCgPFb6u9yw8XZN*{ExHax z!uMreVD~=T+E8b`xrB>X!c2wUEMcDCraN98{E->eUBv=k@jpRsIM*el&|j=NSL0pl z@9V!#1V}%>yV*uMii<&{D%_afe~aVgESYfWd^vGzDL;%q{6~uS^MQd%>ze%R4D_3D zqVl6e1qGnnZ7DPYeeXK)hvT&?37Yje&QIiA)T%SvE@FY61_o&Y8_SLWAp3q3!u5j= zTxS4@v81L}H0m9bUzR_W4+V;+)oFPf^~JOx>BUYq1#9bgy=a9Mo(A_t@5S_^2#l$T zTyF~eCE+>8h|FjOM{H<&Cha*fYf8=7!;$s08_DN_pUPngtw+MM1-4baLI~9{g;&o) zE$J!QSe}*xI{+mjrsB}Jp^>C;VI{lbV2w8=U`=^1U0Z)LYY%xmr>(V6MwwW#(;crv zh=QtpZDl-2;_kBzP2o~Pn=`iKIPW%wt}~tI$15SB^sv#zOf&E3SrpjuTuq8zG;e6O z)Lw0I_bMG80Sz0b-lC_X8!ueB6UVeizi+sXIxjv1!uD_6cuo=6Ap?KHH)rbp4 zP02)HL77J0b=_%?roYf?)<@Q=l&>&r@t|1huzchvnE=}PY93kvUjfH5^GFH`k78sP zR)x#B7n_bkH|Mw{Mk#a*g|I!+AQdg?*ke|9o;+1XYDFJM#~=uTeeC^K5qTjTY%(NP zlC7|I%)nI{1r4_eH7mKHDTEN3Yso(=Y}m4vJvFnSPH8jrR*hzR@0g zkiA4~`s9diV&Byw0BUkNCYk7Mvv@o-mbrY(L`LqY0I%_o^1b+H{%eimCxa4;g?ghR z%G&V4vyzUU(&3?tZn12@q*i+xm%K#nKvDPRhg$5x6Jum5w_I+{%5&B&@p5%p;6}z@ z*-1qMs55H55M#cV9M2OPm(nBKMPuu%_PjgwIV>3eDX7e}%m4l47r4c|#u2o3A(Mjo z%iZy=6D5>?R+01B(zN}PA9Xw=V@_ z1!UL-Y$xE!4+jrID?Uokvy*$!{{FB!@y6l};ceqbH~RJOLk!$T0izmktL5jlfhj>k zC|$>rLUYw>yqfMYw3<=9bcfYaXw#cGqY*OK4bgY<+x$Jw2V^!jwNoU`7ZjGp2$I#?xC8cHgp>^X#2Uk0 zncGRv2EocJ{|^;I=#rL{bf6F&Qv z0&UlfkA|yBUNXL(M2~AYt+>u@K`w}yd^OzyiQ&-r&C1wZSyXGCQVzH06x=cVc|q&Z z%>sX7D(%AP>hB|w=(%@s88e5=AyObMgiVwuag{V3O%oW$uhwpwcxQR4-Unv>%oOG} zffeQ&Wsiq&DmTFmCvDz!C38fshb5T;nneP=F}l|GDihe@GT0?xtes~rDmo_D(YE#E zB)Xwx5xZoYrBr9Un43QSGptUkCr`Q-3Z3$wNH~Q~D{;$8GO#w&vqDJsX5vkZfyptJ z#&CG_s?XEA=k99rpBDLnqXquJnQ5ff^%BkX1Q3({O@gtUD{J(%5TZw z)PyWgi@dK#pu%U%+*A)M7cGQy1fJQ77czz%ZV5|IEl<*Th=jPr3Mh$gJJFdS zmto8wn&k9M`O5nwX0329t(+lhIGoA)Bg7gPxpu$4BmDcQc6tcE@%$_O@Qwmj%mJeM z)meDFMBZ$?2F~5U^(WL$DV0oN(Igv&1_o*R&R+|NmRcAfzdZ&F+=b*j;K_&&dx+N` zK2f;)CsYt5DZ7Co4*}oK@7Gxt6uKU*x@qR{% zjw8!8MaEGwwD)3+YBvbB9*V09ZmcGf2ZI-q8rZZb0?+F%+7TBE(B#X=mxPU!$-B)B znA+iXF_y&?QYIoI!r6y0j_3f{IieeJ3d;Uw>GQg}c{hM%%f96)87{v&_G+p6!Xg=( zn)ep=)7Uw)A_N$YPp5e*QR|Kd67D;b3YGBk12gaWJseAx&apN#nP~6;@p^@8>}*_O zwJhEPC39LWJcF)K4^&*D19Q>-p%FRXpoI1dnP5B2Tm)p$qh)IP*>oRU9^IAub+m&+ zisP{uPQzsT%A{EDkMN3Zu06_8s;aV-i$W8g9GeO3j!-8pvl`tO068V2%9QrKx{|v= zPkPx3@z`{|QD8 z^hHW*3A$ndGak7-Z7+b8_|e@4Y8A5Ha#FzgWJzi?v2O7(TvN>qE2y)m2DSr)h=qt? zAem|T;@2jH(}28JjbmmHbQse)M*mQAaR{R^Mn4^cM+>S*ci=j;>@ud16(&lM-p+WFXAuLcXWoU-D8lh_~U&3uaIl*hRx=e(lDM1(jj z7dZ;MEJUX92jQXi2aXL`E|G!<==ECU9Of=gHMbxCC{+PNUBM>>jdH6$3Z6Fr!H*B* zc->(^EvTSTez2;cZMJ$t(sW@n1+HJ)&?YuuybOedlWU{tI7R2qW$gjUrU64oDJ-*# z>H|*!vExU&DX@R~0s{>sCi}(!r!|j00KVEbt<4K<9aVqF7qA53Auo5d*;q=; zDZh25_Qqsl({N#9Tgh%0)wjH;wi>S1dFO zsU~-?|64N=cG7SLk9~Kp-KY?9ERKK;WTCKFuvJW)PDQcs0O#Jru%NF$mAOqxi>5w0 zj_6R=6kdsdJ}?7}?ETzl(A-M41F5@D4hrFe!zaG5rgpry z-sr+;Jd;I2=oLd)6XKjt7wOW{GWe^3L=zHbk>tZ;P_&j4WM4YAu1~O_;jop3*32cb%X-)uCY?dP!i?@ekMm->)5nJy^d+K118%&&#Pr-Gs7pomT%sOnuT~LaoXz)UR{xz=jKI#a5^hW94zbeUusDa7n}Wqqzmx zH=mb^=-q0dMAS9B^|d;+fM5K=8dd_FMK> z_FD>8zMye*TTtY{$mpuzMcGq8aGo_!AR`z*4r$>c3Bi2raa17af%u0KpNa(IdSQf` zq5VNveNjg6%S)-NCs{^7jl5w2BsD&t9}y10nnUzL6V(V+Y<%8UZ}2$|0ApXotNC&* zwnI#K@HB==xS^J_n$fi8-)FI?ufSGaw69@grqzg9=-<=4dq1#&P5T7WfrFdTjvON> zfbDX9im9et3wi{U(mA1Po_WA5_HndQU+e7anY2S&w!3V{H$vEfVl{@QZ`N>5rp^&Dpee)_7BjXrX9gCwiE>czJiLW*uSWHZT$h1}YXS7z7 zFiO?lFK!CD}o+W85S4UB#3K z(=o%5o8uP8W89x*NO1VVvwXcy4`=N2*#;cfHS33--|c&e)e+Hi9kVmtiUGBcW%9!cn*CdwY=?e_ zEJ4GJE&nI((rtP=jLPaQRhMzR*SV4ME+5PU4~I*cy`Qe|Ulkqf%s!m<-Q-*wb>9zz z=#~_aPoG|vKWF_M&!`YzRl3|r-jgvg;G8u#V`}6chTU+5_NBo;rq^)F*w`+;q*Lg%?RX4p2p zeeUWdC!Q7-YoVAg0kUBOw)It%V~-!VLqRlRZhZ|kxVmm?6#o-N3^+XSbVHs7r~@Au7-rP9(xPYz)XzV z)>V}sm9pZKtL%8lhIGB0Y5`S>OSm*hqZpMPBYN5P&0nt*lo0ar>Ua(Ey^jA_8*zbU zP^0uNDxjH|k7x%!Z|rv^W8khnVH~|-U%+)N_v}V}UDhdrtIbfXDUSB|tR8LNTyY7n z9>qwZcTR3teXQB)?GKS@U|sEv8z>iK;T29{-ZI}uT@otp@q9PdEJH+8J&|v}tas{z zxW{rvQY}mn zf`L?WClOo5wu`L5`B{5@FGQ^q2*%ygb<)@{3e8|~R95<8?!F-U`Kcq4s;3ZHI$*y0=R-|H z3r1d~-D1VIIcLGhaD&ckRbcn6e}zzd|-;mR;<2zANZWzh9B8X zj>Gobtq4B{Ffzp6@Ea>X!1u?J&KHz`^HV4pu;t51tbQXT01O6EBrpf60uB#ARB-cc zj3yGJYfauJXKc6t^Blr8yj7%l-p03TJc zd3~SL*PCNPX4)_hf=ozEEc~my{%6om=}cmiQ5$7TWZMW-{HBmPPS!WtwKN)cslfI~ zix(h?pJsXYehg=vq1(H~D~Z>+qOd@$WD?r=heG9dcYDR%dEAG!Pc6p?HF8X&bSk9n zgPx`Qrx;fKf-dmu>JC8z$o5bNN`mGioX#y7hehEz9px*9W$_h5L(S$4Ov|LIgB5yI z$p^=RG>?)0i4{VGHP5&BvfLPUGRo1@!JW(a*v#B3LsB-8(GP@) zipfxLNjOi_yHVd0P_gyOVm;g{5i+Z6=tKA4IpG$ys~ly#loR6f;h#5mlJ8 z`3(t$Yq@sSbt~@$;c%?z96+d|XS3+xXxDAT&oEibR~k=EHaCDfBKeFUC&} z!NF4VLSDbp{RJEkj`2Qum9K#%=~7f#SeQ0Y#lFu3hbf=`k*V8G;l(#jI^1)s{&2m0 zcBmy)RtmxutIxJt%6iwdoRTgT+L0&eGvU$*6dq(Oofz+!nT%)$qwEXB)JvEf?TmtY zPfgFr3)+ogVdg4lu4EWVTN+GmRHMzMd`(G;Uv4i?QGQ8UT(LtuQ?Kye@cTyzMbkHE zu$~dwdI3)>$h_fVsw|&s?s%=YL47IH$!D{)&ar>dz3ec6lH(@afz`VR&w|b}gK>7D zZgYb1uruM|YPcF^f);Y%8YGS^D+p*VEu_nW4!RL|$ZkVnMt%_J8~-maY{wmY9CW*& z4FD3`re`fMP^oIULtKrPZTr0a6^10Z6~MU2fZW6v;5fqjb|qxWA`o~6z~U#6|Fs+0 z8~IB(nVc;R{9&O&=TuJ;nH&TW0R8Rw^YZg!ayXIkMDhp58(F63Om#WeCG_glp{Kns z-3zcY;(nOR4DSj|Q}q%!9nK)`Qu328QiDLb(NE%AXb!PY{ZrE~)f1go7v~$p39PQs zDybVGXF^+K4rJ!6$nByoY`X|TvCNpj7A3X{K)@Q(S|v;TRAgghk);kS372Y-RHiA5 zt8pMFOL-1SgMqF^qPK-U-2m=3^`5-Q8k1A;o_zH9XzjDK3!gH)gKU;)(Ah`5^dxMF zQ%0s1>Eo6{_r|1(5ejFkAH{%bHsUKTcEfuXrV8x*OBGL&*$=qr5wfD1v0O^~yYG50 zwdWqQQ?!~Kudt;8v;3(V?dNHj%nhuGbg65w)3C2rni8qaCN#9;sY~%eC#UjmoR3yA zqxQRaud^GYrf@h>f2dZP4N{QmJ^J5d197Mhj&VFti_THIf_6WTcfa%X%fXCgA_zP= z*VCMRZeg&%gf2Sb;mBk68l_!@1K(aK4~vF6&X2wv4N*Fm>6H<_HbZg_ zI0YEAs`DK!!F{vTdMz!TOkuH13woLFFTG1vcmC6MN@fB`^FgNg0Wl9|iY0E)YcD3C z4@@ZE;!7R@0A`(IVD$}Afl#oK{{aMYz?6MwC(X$ZSq~wWX@~})FaiXP%%+SD1d2}p zo8{Xgq)YC8LC-fwTmc9v;(!LnrvKsV_})kD@S+H8NWJunu|JZi5*tVqz<~dU6`=OE zG@~2G;<_*P@~u8>k#MWY04k12*8@o0kZF#7jujuWA2B|HAZkwn4@kZ2Ji%Oa0Ty&DLS$IaX>DOe5K0of``e)V9?Zv4s<({ zRvM&EaiaOk1VK!d^2I6qqdY;(W>%*gr=-AM$Zne29`nuYcH!igrZT0;2TL$>;>ts* zQ&L#+tPw`w9URNWL|kzqb&h@fa7R_!S6me^^9dD8G2^HM-CF>@*dXlu;`Wkx^9;Ne zZmCjWrm+IIo*Ix*B7OjS_nC`>XDfs3OH@$JXxv4R- zJF`hv1L{9?o~D=n(hDl4>A3?3w|7rMEgjd9<+KX}WydG>3mTk-43{{kg{G?B7sRh` z@j=e2kw#v+oK`ofEVDF`*_ySZiA_(V)KZV&?TxE6g*M*qSuQEBFuozd!FJfz{805_ zjzyZjh+6|>Q$S$=TToEYh@LWG<%-oUC|=s%#* zr%@xSfrZf5AN0YMY0mz*AFvmg=*5kF9UEib{R`CncAPCeKp4G4ymLwn2qlxcTk-=4 zd$5b({)ajRe&9R6cm(kF6A@gRQ7pP6PtMO62mw8pPiG!Es$fz*rqT6sqkjF(w8;B@ z?)*cVgymh6s{~!My09s>fDXf9PQHOj*}PKy-x%f>eWj3QYVQUY)0ie0?z6rElT&uG zEiV6&)u&kM;w?62rv;=8u}vXzty3kk{8b#U{HJURk2c)I?Ksybx8<*j6OYabFJ`4? zy#15@AnMK`c#_ID(L`<6@VMLHjj>0+EwRwcMbYuaoyQLQvxH8A?C}Mg>`<>!s5uR- z4^QSk&M8pe864NOZ6Kr)WJpL^Haj3_AS)m^pszh%50E)I2eqP~W8`f_?_?BnpmEN| z*3$0XZXGb-BDCCo4*=y04WA_)IG0^z533H$4}`pvvjBi8k|F=euUS?&$GJmdb=J>chlQb(gKchpOsY;%XHb4pYRW>%9@@BK#J(G|RC{=@{Bs0`Y(57q2*-t8Rs66VSUjIkF^ zT^B16w@yc+1(+Y1Qt?$Kof${u<6|2+d`{eQqtuk!!;7qDnwht8w>_RB-0`JQUWKP; zdU6U}GCD!C6QQ~GLkcDGlD_n5uE z-0WdkAi99z$^B1@^!Y)rK&7*G#%`c-pJSAibH=Ycc&j@85h^O3Po>PXtSA>Or|8B@Fc7aOJ;iCm|k@_x6?fh6UJ zV3sXauI$kY;s;`C>Z9{`p`IjfD6Q((8aZPPokP;snZiKfJxNicf7zG+#(15AmJTpvUc7FW7<4566#ZwiR8h8S+Py%Wn3& zMa*+(FefE~r8DeZA=-@Mj`hRQKF~6+n0IIv0$DFPindWb-OFKg@VjSEg${1DGH9C1 zqXn73XI%D!f#X0* z7&`!TRt%*N=SLz#friiofiE%ZH-z`7d{&QxXyun-f1}^J>Sw{{>vQ@OBk7&v*i`XG#hAz-!1how zv=pE@rrUeG{N>-|9#H}|GtoS4O=Ny!#xuzsp1CA4o@hJ)@<75HfKY^VC$VQGJt0#yO2zx5JaEQo5xz{~K|` zAMFy7JPSl$nd?krV{J`)ui`b;gYQI6h7#~X zQ*;dp6^?wjfediQ&XJ^%sK_-i`DB*}+HlEi07~|hIRv+G84z$3WkDEl729tLC|MqX zqvL4M&z?X@IC(=C8%XaU zU`oK8pVAB_45|o1bRVh)AY7Z4mkxQJXt!0fhgw|el0@d;&<$iIjFT>DDKdDf=* ziTu#W0DIK)>R0qR(m}47Z7J*0K^ma_$mqqZva#56%5HPu`_t|YnC*|Q$XD`yJM2c^ z4*#!5v<5O-=(YFPgMF{#xRsFd(p#7J&|CBGmhcA5UTT}C?ri(JVQrGHegmVBJu$z+ zY-?cd&T2eg-54=dO1+-OZPVF&+TSTWoSnxE!abkGw?w%eKUyz<va0E7$&V89;h8-{reH7w zU%<>6tRR5yTLACrdzu9g*q48^03@U7SWi;$H<@_A*Dwnx2{R!Q4wMX6&Zw;tgT$nR z@HH?ejvgA4heF^SeZD^-x(5^n@UU2R@e(b7CxF&wzZt7`k%%Cn73hcr)7Z9k``>Xi z*zRLuP`s1pGr6>W6|xD6wHxK;OK7h&W)Mo>u4K+v6?g{3zwxOWKiGd^weno-p-1;3UUraT=n|vSO~Cqs#D&c^PHXCF=j9CfGEK z6DtcVx3|6#9p?90IZnX&hXz65kJreePeYN$IfK4Vk_*V_Q3Mf?nS5;mKLSwU-x3nm zdyqF|qxRk}^_V^qVT1ufU=DDAM;;p-)dau;;6Z`la1P9ryij`isNJT(|x-0Z*WzVdiVaE zx}+6zt9$O+>}KvbX_#F>Gp$87TJmuGld0)#X6Ol30-T)o~MgUw( zftHpw!$+|PL}UQU_6NLNL05&#S#{tKiztgi!)$w8&)Y^*(}RUyu!M_~A;pN&8?J_PglI2(lg4zG$d4(lth=^3qd*ock> z2%YA3LM;Z6H-0Dh8*E0m?I;|g!sA*vo}yR3GL%FSvAP-^0a)@{TA;G?=!r@`s6_>* zP=G~To3arYHiEh5#?;S27ZCG~-A6{v!9Oy$eNEkt_}#oDbo}>fz$D|lp6r_IyOG3S zz3r=MD`NT$8Yx0L4{O_rnW`PVR)eD{&4auwE4_#?YBrq~{n5*qmfCra<(Ug8;&E}Y znQWfk$mbDXJD6nlIs(zQ7hT<^f=QKx%NVn+1rPV0-}N{Z8kBFF6ICk<&XtgI!53 z`{?(ggq(Jt?V(<9q|dIaFRZ9GQ5gWXs<#ZG;GtwBrlxqXr3(fee@BuKNf?ZVW&q8l z>Ol^n#pL=jm`7B1rHF#yArRmCG={eSaumq|%AV7^z(xGon*AWRlVm^^5Tn)$)tHmk z*VV-%&u_~e07?Tmd`#U;Vo*(X6b81ba>RD5Zd2UO*)M@;i>8rKis@bC*VtkB7x<(3 z!_c#5^al344rRWC`dG2GiC7g~4uhxF=bxo=(~>wM8_&b`*=2gFFzeGc#+J*4Tc!oe zzf93e++V+mF4YV|Dxxc4i{$m-)^bEfq81a6EaODiz(-hq(5J|{!%cPBzhb(~`1uI$ zt9MGfg!D7Ji?N<)P`!92!v4X$P1SZrZ%eR=K0WPqf4Ze=Y?ASB@%cWr4R7?loBha| z=XSbn9jILQicaB`G5Wb09kvCDp2@ov?cy5wC+8<@8S5Et5o z0$EjEmqkVbpTd~n*l_qgZH$^J^xgPs4?gwgOHdXM6u2TKh`=`)2z#9WAz9=N=U}U`L4iB~9|&3;vVD!cuq0Bj;v900>N^Jj zK@*yRstmNd!aVw`=Qr9hA|v!+`U`Phlp1T03Re||B%uwRLk{auMgAk4hlz~OB8C34 znA!&C60oD%^I6j9JVFSO*nyIxOZw`1v)u+t>5P^*7Q}|qaWSPo+zwYA9VL=<->Nbx95d~(v2r( zXmorE@%sKloVyy8a?1RkZ?eHfuo-;bFN*_>k8n2q~Uv59#s%4*% zmkiqKDP=SgENDsjDUx6!w6ElFG)Mt{fv>c??iDsMmK_h=EH@_5d1wI@?}?0@DR z{yU?~U{>X;8&IX<{n5%0TpDXhlF8<~?sR#W#wXZnlG=y;p*-a)J)u_qBP@^Q=-a>L zKSK>;<<|=6`1m_$>)Ox=mPuqnK#p^b_wu;d?$tBj`z0yREW<=Y=^AMj%IzOb;{xMn z8_fv;C_#|Ob5Mnk<>0II*m2T=Ymik2VekPQ7dOOhIDvrQc)(}v3w?%Z^bZNNk1pnM z)H!gjbNJ+Vp#tSGz+eXY3Lfp7j|8NLVvt{$=HhA6jlYV$b;BlEq3}@++Utsp+;Dr> zwV7;uI8@PXDO|sTAwGZe%|vV~Yy6+0L8r- z4GjFaarKOZ3kFE|YWHVwTg9MZk!?W1yJ{K&I0isR2w}teFT$O-losWNty5})s`Slh zJsMVPs@PS(M>PlXAJ6edWrcS5yPn4(;>2Vx_SqM#yRjJ=_1Y`FCetL^ zZ@#H0M=2>yPm)t~ zc7%s zVIC)@p*zBhThE)-|DC5m4C?{;DaVv2&oIOB`msfuYAef1jJJ6K!Gl!i}gf319{NUW>(sy$@s>Z?UM7-BHrSPr@nxz$t2N;RDB-r9zOgMS4AOG zS9GW;^}aN@Ts)Ti~p;!R>?|Ycndr-ZInBG8&sr^UQi&l=X*B6@jbG-#j>urjEvQjOh zd`QecT7k*?%`~`^a7pd`gzC87{t_*0>cF!lzRX-*qv)=Z(rb4~3_;$i7Kq>fN587` z?Pn<*Ni93KM=ox&w@UAZNr$#?_Vf2ss?U@>+&FSQS&7u1jol{~W0DJ!lCbSl5_`jT zQe4RV=M~7(GAin9&d#IA2Mn>nFwsirZ{9hF(cgt#w3KyEt)UXPGcOARY!j!VHvauA z)XyVb`(C38^YnH8^b*kM&^c&8q*hQqz6}U-8n99d1=;e}BV2mAZ7j#I2#8lc5C05C zLsxZ&B4^l91R%J6r`@Q4FYJea=8q)CJQ?>$$e1}X{?1!pV)2JnMLXgk#E^RDRSB8; z^>+ZY;361|!72+s4ybs)iWB7Q_$rZ|fD(!deWRNE_;4qrEOzq^jpH?&EuIHJ6o5e> zcohLCpaSm~k19u=o-rs+R&N1apAhcfRSBC$sjkY4~Dt9iv7>+r56l3wx= z5h z)GB*)i~bl7-}CtGdJP@Bu7v!-8Z6S1^6iMSo7f12r1ILy*D&E|qJtlmo}qoV1&q;d zr{hTmS$|`gD{FrLnkh4x2x#?8l(sMX&g%#D`ueqf;@{zlsy-{!397OkmYw`;4G}_p z_#zoA*c}-FKrSWALCXMs055|Vp~OH`EE0Gw2zw#d1N;D|iuw{qZGw`x`Wu-9Gkg8P z8bGWpVgXP++zU-q5jFuvP=Qw$4KN$K$BJ2yY_WD7J0Cnjb0ph}0#M8N#+M+n5o-7dX*WmJrdZIz7K?)%V2R1 zI`LryH*_ua6vtloSk7Quyt>Ybx+P;I(#449bbj*FaEGcW?>$`?Nhd5M-=1JPWA`O z@z0r7sC>B)S3mz)p&X-5j$F-0!>VPH^WAg|o0wm%m_Y-e^!y-lDloS^CDVrXf6R6@ z@?A0_G;zS#U1jO-ocPnAi}APBx71jj5S37W|2x@tE?qY?MOO`~2i^R(7-j9luXY-D zlymh5l!dd-suHpb9=0BJ6d%}1y#9wjPZ$G1g>k)DF{e@3VvL{hSI0PD(xuO3&85nv zD3~Uk|J5~$x9YI!=;!W%n1bGFw9AFu1WXrXiY7GsIQkW7!Icnt1}GH(M6o)94>EzY zn}1^*z+(Ss`ABw?!*lY*Tt$xnGG42SwwZsgN}mI@`*Hie1Lg%q@5TTUT>>%3C)f~_ z5MUt)T^#R?u}-kiq=;gx0U`bcp4M02QOmiX9*~FzP69k@b8p7%nHP7a8b6Q+**Fri z&Y;XbRcUXWQBc&-aHTp>OriihW3e--G$jDRfKR8WGiJ;XB5!w^F;u}AwPh&hZM>8b z)DDU*Yq{Rx`ZKO=7BpHVa)rCudbS5U>wh6YCMY}=VT?_9%U~_>}QJp z5dQ7BV7)k6keKHp(zTcPB*AV|>Ukhz_pyl4_{K+`Vsv7wS_(#bwwVFeUg}mG7V5vx z`JQa%P}P7M{`pNs);Rs~*V<(cu>7w1lKR5ejQk%0^rgR|6RgF=HNU61F;Z`<|M<6`MaLf>pilEBD&Dx!N5RIfE%y~IX8CifD);L50{&9d zUcacz*`8a7%&@>U56!Z4$CiGV6ql|WTA%@Vp8{DNlXOF&*JlNUZgNP>gsI$EmI0Ju zBXObP0E}OAjrW&pf2JFW<97s51VLWxJbynJ5rU^HgHe&)9;aq7x|5r4$nH?IvKL(C zu>mW17N9BsE{vbt7JoY!3F$ym7a4a3v;dU@gbQX2Q)BHx^go>7s=EA*Zoj5k9}H2t zL0JIM?FUoJO11;_UVrWY%orT^D9@l;V6`IGVcww3Rk4@S`5ZF=2*7hA#N0Cwyw9%0 zRJk!j8s-)<+L3SA4}md;e(HPy;+bl1c9Aa^18K#OAbkOlEJQ5VE*(N6C!r%xMcKB> zZ=g6nRo(pc1nVAKZ?8LIufV%FWt6k=nkE`woMzgtpv2IP#qbhjY|~WMrKMhiCa53( zVlZjWJ?+(YN=?#wNMf$$!bcHsX}V{pn2ta}eG(}h8*>;>%-?#o$`iZ+*}<{A##8ss zV7=EXJV!i=sT1T37WQeUr>rsapGQ9^J-PYsIuC1;o%ny(dx)*Dw2>!2(me}PRm!-4 znF(I+AL_hPsQmtP%sP1#i~qX8qvt4H2QCZT%b5Rn_kV6I=!u3Z@CkQf~` zg*}A_g)0Ow3d9DGhI6ca$`9l)ewoufh6aG5APC_=j|k%&%v0bARHbyz8?D z&U-+xfAE)Br0{hW!8HzdZ0MRWKgPvtDr5IVa>%O{iC)QP%Ud;uN$@!GXQWIhk`EyK z-X!OByjYb?tJR;hP3Wdd^}32ucU&ipWH`9$QyvJ8#5EJzGNHURW{PnsFmlhH}Ex*#NykgLmiCn4M(`55pziq|z+DfZ2ZGqU%7et9lp$mY6nfCo9)(AV3)vunU3 z{%iwif`$yJ%F14FQ3P=GdC+SIXZLW=HUVe=$Xsmbwrwz~!bQFeigxn_3nC=C#QA6gr@4S2}+LFCim@if+0>Sq` z#OB~RYoI^Yy*xXD5@RK3o!u|T6YZ`Xu1OJ&ScT?0j?sW`pBtVD03Zi7F*gOE_WXDu zzsW7)IFC+3>c`kwiTX|a%8C*liNJZlWgo~MBRA;JOyT{R688-t6lN&gEKZ5c<9w~w zm2&q=KYPRDCl*br#}7L?=y-uoEXM4c-kI9bS=vS@`&3af50GpYy24`hu2pIwg zD)V-~Q7dgiBLTbs;K&mXgR{Zwn3YjqzPLb;!@~T5I2o=Tu>AyVP^Of;>TZ!@>Kgd! zS-@FALg#vJRhXS z8A3lZZX_nsOOt$TIPo6CYX$%P5zY%&Ev@~*0ALF8T9ge1Ry41mPK3oVbiHP{Cf?Bsi|^n>fYA zl;vN^p=UG5zgO{R3MkKkRbQ0QXHuhmLJyraHC4w=aB`wM%&p)0nBlJ>+0H=yRRw3E z2B&#cGhr$N5~%sqK9yt_rrB*f+^2L-i9&l=_DKF4l7`LvpGTxg6G5ZRk@PO@|ND{& zvpTojmw%%ROUphhrSe{5tatdtinEtsMXh;%KYM%Jt8y{4;z>USkGNPZv#Z}e=h%@e z_xe*?YSZbHqwSVX<)ujB@r?G!fEOsZ7qIBbFObpAQ)q|zOR_R` zI0sS!h=#%PIL@m~4B#v9Egl};qe~@bg}F7=SvnMBEgvl&2l6wI6oD#*ZUT^o5)DYA zB0Vx>*LY8p>`|BETewn^(6O&Xdt>$%U=AR?0({}d1LXXZywQzDDk%F9tV6)M|9)PL z$A&!s;l}*@@}f5oKx+HWQ&P9k zaYG932{SGL*6f&XVX2EYymyh5L%md6Xkv$&?Zy0AoD8kv-^>}}9ErX||4@kie_6VnD;LAlzTb$C`}+vFhf*YGi`L|qyTARvUwy`j2NH`i5MEHPYgw}hG7y^qH76ai2)wcgFl} zuCOOVx`1)c{R>W3`4KP!0|g8PNZ!;%xu0`;pvC~@8o!=upTzvzzW#R}k=P%k2%mZd zeEx&5Jr^q(k(z#~Af82$GSnAogp z|DQ_X@=Fzd&Bt`Pmbye~QEyAwW56J!{n5Ls?_qwT#p!^1Pgb{gc_--?H3BNLC4{Pau@R*MJmgFrqn4Udc zLS1aU$z#h1DGMlt;EDO)b-|kmtV;xT?rF?x>NlTzxRL+k^D*CDMv{~J>31&DpO{#L z{bFvhk-us(QH9!(LJPAMf@L$1EZ0z=mT>Wk$>w4?$=Q(D24c)Q5IvpUl3~Sh*}T;^ zqLWTc-H?=Y>{V~?mr3d*H>9=h*%Pvl5 zU|X{)F*-R5BMv++NC^pdJA~q*d=NN;(uWS`;6sI@VN>i-zj{dlRGGnJ$fvI6eqJl0 zy0+8l@pno9-sdlRi{dQtr9kD&uMD1jsZO^T;U*l_fu=(yzjMmhN`O~DxkKcw0+(3a zSi!0>oXWP-$b3t|7j0ty4tD-imV*<_f+;+nlANmRYW5FRFN3g{@Mn46H{WgT>S=WG ziB6~pkQI8!dF}Fl-#?{U=dqhjh|Ip#i8kVbj4aY%1wA^vb#K-H@wZt*iE}5%hCJ@z z*#RfXP9z0#IIIC58r`cda1q-5BUoc)-Pu$06Nj3{gBw3rovd}#`2#wH=aUa7?Kg>W zw4>@PXHW7Yo9YgK$gTyaJ!ymMg}_ht4BF3I?_OeHxB;;0qfDbP^}AE8x>rFN&MPi$ zYMstQ0eqIK$Q}{u!t*DNXFabCO+yfGC*YGX2|IsiB zD2Q8>E2Q?M4`)JXp3^%%K9Wdcr%r@-Nr{zY%M_^&ssh?Rr%Zby_;cdViWTK*=`H@lAJ9JY z2ZrvwynA>a2@+|^9oQ}7-W>!|QWZS5r`BOSi4EIqQyI;v1HBA!JnS9MfUs5IHJKeS- zPt5{CZjCx;$#}DO@0gjuvPngD{y%;+6%{+i?iJKxXzx@f^1EyHA%;65h!ZhQ%G$?m`0A@N17CKQxN+u0 z@UQ1}M3d7fDCcxwixFg{S_*+@rP%4UMYa~}B>&`qeEyIZ;!*kc!h_nbGHrL@*l3TE zDsSA_Q{S*m)y~Mz@F?&ynv4fS`)a9?iKVH$yz7r$-Nwa54S(2PqmFSU6<+4XW25ta zUBvln&2-&t0siueKv~R#m=`wP`#`t$JVC8f5vI8xs460bby5OpDb;$MNprk8x=V+0 zbr2bgEw(Y)cY$d$bW-nkU7to}JuR8rF7{77NX+)qa}5zG5e2`C zFLr~LOGKiMlBHR-!1#+uOHA(0)BMVFzwR2Q*Ed~vVXVgc({R|TXG7)Eooix)$hZ9B zeVQ98eeRV-6>5#aPLf-ZXN+~QuuLBmI5AaWJAE+6MQ_s^N%$yT1xLXK1Gm!w2%r{p z<~h7P({0EUd0*8X{cta2h`YOao9xE9{W&uWdnk8nb;yk>Mbg6jH)R=r9z5qIXZBaH z5x4=hiv0oQBT(KR)9GV(!_2D{OE#Kr;$3DaHe52Zz)5ypg5wem_F*TC<|~|Ho8gz? z`>p5fA1^3Y7#0Ndk}<+Bq*o5=pg9HMa61$8R8Za+5D)mCke?B$`;mP-qfRc({>fBY z;K`JVE-QiU{nb5KFRa;Zl08rvqI@$jXgWAblAb7Bo^X`xEA=0|!t%fI3Pt7-XxX)& zhDe1`QkWF!k?qv!?91Byj^*zTIE)2Upnz_^m?SnoweWG#tWtizBd+5TVdH_9yWX+V zGov{6NHLTwggIX2>1Cqk$n)qgG&5!2a_NS}+{7L{`7g5L1B?OXY+8l9+4pB2^7^%gxtA+5 zFD~zXIoa-9`y_?T{qMLbf19?>;(jDn#AT#{)^k>)p{~}jP176Go z;nLmeE34J`t<_M)AU^{PqK<+is^qMJ17Cz0UG(so=~PrU)#%E2{cV(&gXX25AL4Dn zmm4X=3r!iDy*iuAxE1Vnk|`-asy*0UR#p+wSs~t&iFpl8+aE#_}f-5(j1+Q12Y1+SgH*m+R}A_4o$IB-)OlDj4WZ z&)&d|x3K-w?V?zCS))4W+rBAn04s0&(sdYo!guKP_RtV0(&* zR$%;caoj8^i&v4gB11$!`R~{#Qm*rf!Ln0ABJ(qFre<&uQ-RP2SUm=n)MoqviIMY+c>GrCy|aTO zp*SLb5rzD2rA;#2(!4{L27^034|0U|QKx(dU6P2!r!ygf#S?=Ih3m2ohQ0jDWv!%! z%q|&~vD+~V-hN|Y@oGj^o@(Pw`A08f$0|?ksYSKbq-7@y-PxH zt*lwV#rol(kj;Uzpp$6q&x?zXH1)UcA%c8&%28Bp14bCfP@oBa)Vx9aE1BKL*6g}w z*Ad2^K;ui9rIP6Q*qOPvD%PfqS~cGN@Zv(^S}KiSzUddvN5RrA7#Ad!gbbz520zfO zE?4%s_1koOJ&52mGMsKeEURoR4>aB|A58<9N#G@kZ-jj4a~mAn)in_D^sxMNfSHT& zscvuJ@&|P`YL%GV=mJf_IesbqL$GVVv&?PuY7v9WIiYH}{Br|)8yVx?T&pX3^Ys$xlPk;SO#Y%jz)YMGU#hXQIwd7^~uo|Us! zJ88Eho^Pq7`SkeU;S>6>Wu466WmCky90yiX*w5!H(nO2E8vlm)uzX|hr|Va$TW#3b>f!W>eQv4ySKzmU9_IcaBwu16);Fsg z9)K6N(!#?i9;gR8DvrQaavKF-zw8m&vxhoSm5ax+sM%CpjHmOB-N53Ct0%temc?QV zO|g1HT~1mKWQ@#9BlZ^SkZ>WlYmwsy@kgtGE z$TGp8ajRXfTdr1w9-ID9Zr0kv^h>=6b0-hMf;|0gQSVHmG@m4&%qu4Ic~vvU;+m#r zm9mD+tj`aRn8~5uW(ztU`Iq%(j^D%%KBW$BEWL|($#3JRZV#_#IY76L=mdC{!a^s) z)ijdoy^93rLrZ@c3fgJfe($7k^Kn2wFzEF>G{dRcpF^PfR>Jpnmoz*gnP;n&@yoGv z)30u#*ty7iJ+n&v_bd-|iAJ+#3YzCMKj;=d2c)Z&@VU?K?ax%ysuP!wMc5e~Q^(31 zmQw=xrG5(f0n2-zV;rL{erj$ydfnAYJf*Ns5hhM9FXI|xBz5*Bs}P^=3X4a%1zc&X z1x}3x7>iavC$VYS?{O6e&8iqyN$6^rPxflpADDThHxC^W^Ha?I=+&SH2do(k4vb-6 zr3JM398~sgISL!wl^s6Ap~aO36Cadx3g#q}El`#F36BJBoz4ycbRb0K{b99(;={Y4 zKdg6;R0GqRcQNIR?WyE50he-zcAh_KXG_}tWF8eyIypL~TEr}l5Z#e1*WZ_GVnGfE zRMd3-YBcV{`A{;0!rrBk@de(KYE&|F;1{VaY!h|j!PaofXkpCA2!gOjyHX1ANRBrHGVS{^3IYLOK7jglF2+?6MA zDd6R$R>6MW!PxLjqVT+2!`+{b1d!)W*bBLV8q_+Y6!_5yM$vL=X2Z5^5vAAeS}VJT z@rV4yLV+=3Id+y>zlnCIgDXz2gn&xq!%2(u!P{B`tj9^5;fg+h=ko%QL=2}f;xemL zJ(SeXcNCm{aPMC z)Rbk%6t_jTUKJ31OGtflrrciY*)H*l`(+&OG>rN0Vl1`DQjDQQ+Fw<3Vcn?d7F^wIXSe zUU=PpVahSzCi~pj&4KUCGS9`|v>%Ty>ytNWo-Ow^gM{330q-65j;4eu8~g^b#HjAY zeP%rqSsp&v-`$W7D457jykB&_*etbD*_?Paf$%Xa_4TmNy&oFm3RahM<91gXG;0?N zf_;;z)3WlzT(W&$PK-lRcE58XKT>DPY+QjD{p9U1D*Pv=`TZbO^i;vVbzza!&i_09 zZ07poCXY?oMYmv+)(-J&&SOL=-uvo=#>g8^eIcw5@h<7tyAwgqW{t3uu_-wQgtmKHBv>6W3-WPXb|Dk zx;!yj%_x6&pv5P{@m^@^4+20QNyP6n^)#|H3)j&yuzPAo4Q8+|xkd_kYakizB+eOv zin@eOq=>2?vinvWmWztQev+2Rr^z;zNW_B0WFVf)OOyyBP@NzX4?};++Cy-UB+sIz zwi<$pMHW&JFda0Unvf9p35pbaxZ3D;nRVo2d1)!)SD~w)FqC|eP4soDDVO>~D>q{8 z#?L;mVfLOydxZT+W|%Ja*}W!uxH+*@bI zU};15RIX=3Z~EPDlP>xQ(b>{(N;Db;lG*+_Zv}kj5(VWji0Q4b_U@86Z#obB(tWVg z3wI@XE5PC!4vPi|vqmcN_(P+QAE#hta;?mJKNrrY)Eua6*mOtU&e>+KRlKBjV4n*A zV0O9F*bv0c?-Eim`Dp87Qg%okU)Z|l%Ut=LS4S%}e&D`^6D^@DY5T86Yo%nY*u1`h zI*jiQJ54u70E;_MJC0kao6E{ZmwIX$(}H|UuSvcCMCszoh0|ubAJ1G?^u-^y`{0(N zdSheG59Y6iFdx=6CvLAa?(}41^kxTP@h5!c@|pFv4Mb8{Rjx;lR*vB7!N=e~qQ+U7 zy>ktNDRbXxC(N+ai$Bvq@H2_i860xHF;$rKX1%HiDwK^?9IgfpxYy=XS-H1cFdj*! zX0>n+5^zlL@`!#aP&w&zU?(phGEGmc?KiUjh>d7Yx>LMv5E=3`dCtk3O0vvVD>#w- zRRX3_awDFr-E6NX%e1(fx|=v?=7yBdhs^rSSzJMu)*AHug3|uTr0oMuNAmP9jD9{0 zr1{7C_y?*Q(yd>JTu(nZ%pMRW=sK1VLmeAgH2$y{ANWNXJ#6DB@$tS~>}d$^(JZD? z>hujQe5&35{MWx~C^@x5sXnP|i0e9X@J8m1hrbyYi{43Cbt|)Sm z-H}{Na;Hwt;hlDGZBp7tds@La%9E;Aw^kw2hPhxFxpqa^XYB5dvMAeR{5&^G&!xPJ z=7`>#&)>H_8YU#xp?^weSLoj^{~!mk*ND)e4m4Zk4z~;>qdzq42G$lSl9sc3IqHbe z_)oUnIk(s4B^&8oWEdGdCf2xin{(2TFYsdTeN`}hmz5^m$liWm-Cf%)92cMe)-k;EdxD{_*9v{8i>Jb5y9ay)d(%Ja+OAH?wdQ@o0hi)IN@Ydr z=sF>!f37wE40F(nG+t`^PpFFs#jbr3oajq1wAPDgcH_y2j(tub`ZGQCn18V`$2mZz zdUfAA6tPNGtc$AaTIJR8v;BDIyO3HyX55^S#w*vl-!`8H>n~0H^5c1=N*)wj{bXAl zhZMPs|BP}MeUbA_O13_VxGPX5w%MpW^SJN>xf4awu6#!6UW*sTmo2ut-2t*Oin8g0 zye>5^?l%gwvEF*4IvT7t0pI2ESOp zWrVZF`aAE^gIh_}vA+p&vgcqE70shhf7ah)5y;7L=8K_~XvVZmPPq zLjbIHM${`<5#%8Jhj-(V!hLIpMp2?V0=xQvR+%_MzgICWW4gb6aK`S=S3iT$7h8vV zeU*O{R9QZn5ivc({j&?yTHh*x#n#%m!%25p<3~Go?o%J3P1#yHJ-xqM*WWkWox!bT z|H6Vt{ra;2npv~CpsxK$LD*FcPP_1zSni4=Dy;sh?O_Mxt}~vWi^%0e7=4zuo~ul( zZ6dlSQ_lClkC+{eqMsCOkCgL2_c9fqL=?TNoVv^&8@mwh`?yp}=<9Alu74OltbgJ6 zqdJ;)CsuOhIb7C7LgW)xg|2}?2A!St)G^v;X|&1^^m}gx7FO2DXD19vTwOv_;0*Ki zvkuqX9vlk7@e~QJGChlccJ8VY^@2IAt^S59cH0_J^r=BHTeWGl$=kFH*iex!d&! zyWY%IY)zK=6#x|{4qMJl@t4>p-`~Z8U#;ycxmt?R)CCvf)y;V;oO z+lt7^D{(7PNCaT?UhWqcbQs3&iiJvMf4WKuG_cJ7s?IidGt8PU;gD``?&|p_@yMa{ zdUcnq76v(sbW3s|c^|=7G)dZ}aI7cUEZ^8xQRE8zYK7l9N7JXXH<|!VHT^<5* zobv?V8w@?<%i~ro6smXg95THRq}J0-#@A9RTuIn(G>i?l+(;@)N*_ctZ)PiEB*Ma8 z9Jo9CBrtv0ebxe;$`Hmv^WmPcFt4T7U{jM$1 zLxX%VtyO!WO*ZyrEwe7)ki$0c%Q8MgOf5euO^eu!$uOTc$n?KBk|zh|4HwmqZ06ub z%6=OY&)dFF@#^pUux%Lna=TV=U!ZA>7ZSf`qbYh&f5q!J=ScZAYxwwjFU^X#?9M3v zRMIs4^tSF%EfeNBch8c*YTwt;rc-oi>Y&{VO4qV({$Vqw2omLNF?cm@skxZ>^)yFh zgy{fvSUg~Nh@=;rtWsEL;E$bSXCjM;ntybMmXq#sv}=XgQO!ZTg0)7yV1Vtp)Ag?A zpB9>mugiipZhe$$ZgTK~e$JbuZk@P}m0zB3!Be~5#dqub0 z_jT9J`^CY{=#byXE&W#r4v+U3drFi(IcqE^_*$J_(E{PtXVtd9z09#e*dLx0n;4f{ z4ORZM2(g@FYE%x7bsf*zjAl(g0!yt`xbH?{Q z-}`>QbFTHr^ZfZd`&xVN75BRD1>cIqRB49P=_t5mhOj#8=}j$mx2jv}BNAcjB~pcj z$nOjA5#B%0CjzdOykCSR>>T(iW zui7jsrNDB?ZQiREZig`byULW7=YuX6Lc7($2K~4rLN6!mq<=1$Z`72gC3DPghTp5T znuSKK`lY3jO{}7ZG_xq!B~JEi?m{A$at##>rz&s2Q54lO`RYDvmN*Y>p)ERb+b!yP z?6;j)u)@5cCp4?R7nXm<#s|17okYLx39&10 zqw%UQK6nel1*ws=8B+_uh?$y)SAp$=z@8qlU`f&1dajuYY-G62+$$ z@sLnBXIZ6r=MChx>Qt8;d*l|B&0zC1U7vQrC#-?+=|WTP*c2%@+fjer0}}a;>27|@ zS-hH3t$5hQZsFB0idBiHHmqtHyu%!<;h$|RzvGu1d@^$32D&YqZd@pDFgre`4t$+y zrAX2|Z{4V?r&jPxNAR()*8 z2X4DyjZ(MzrwfOh)4C9ZBQrWt>e2Ji_mwktuhg2|%+)^Z06k4#$ep#h=M|B79&e42B6F$4cbH9Ez&fGpZ@(hBD)ciW0a`>h|walxkRzMS*f}Jr} zEc<(L`QpQzUREtK!D&?SCcQ+_RC+%5RoOj8%X=PGVQt6YrG<~?yxyYREX!Y1?k1<4 zW*(UY3DGh!oJ$Z?Kh|32i66Nh(4>~w9HHI%Vm##4CjI)!GNMiXP+e21?RC!7&4S5; z!qrw^@r_7L1Hh&Hii)HvYt7$={i6z&?hhXX@S(FX>j+_pje+J}8R_#RkRyZu%cCfRJcr z8x@kfu^|1LUzf0bb-c3#jZ2@Df^6W=;M5hPdD+w%oX4t=e$;&zZR4Xpi zG<~txa&7J%fqbp7Vt0?i7$MG&rqeXvv|fNJ~2$qU*omXRx(*X8+{o_y<)Jk`AZ zbU?m>%Ac<4taG<;p>f@XC55B&APn%FvFF3n3$tuIH$B`oo{$b%TYS73YX_sb2Cwo7 z4vv~DV!9>PI|&OWqoEGDQW4=XNtU#%)kpP_=38DmMY7GY9uvb~Jz`xKG&Sm-L#zkW zRC_N79Q=Vj2_3GN;1#QG+-ju%?tJ&SEqi(Ai;r`XlVsf{co{;tGU zGZX6__1DU}i#=wQR_}Vp@ac?S#ZGr7U5mHwz0U;Yln*2FxGGj_Lhjxz_Gf-;?*FvG zv{~M}H(xa=d{RX!x3=Q3IG{c~R`HBN?sZ1opH32f%z0X16bU*q6mHm>QXdjjM9}|a z!@>(#;k%6zUU31z@E)Jl0LCnfx7k zGJR0bKQO}LyiAK{A~#mh=*Gm~d{wrsvFbKqGx}+PZbF;noXdYoB(>t}DJJpB=W}6h zy!!W^jrN8*qCo9h?4GBObVAePLr$nCbTm%nzc76;aJQG`sXMIoU7KpZ3$boIfw+3r74KuXcwHn=Tkb{&KVTr)Lz6 zGAu)t8+>#=430Jgo40YqJA^bJ-^&X=ALZD>L(UnRcho3fanUzDPdgjpaq47~88qFW z=sxWyx5N;IvfDKI2++@D5@y#c`#y{VqvUWI*O-m|@HsgW<&VQBYgxJF=t4$Z;c!n; z7XwQ`zJ0;n)N*L(jktW7N2Kj~2H3j%)0}v{1yV3OEg$b^{g&b5MB|{(IVb$orX&=1 z#f=t}j32Y+({e;AeSEa6F29|;OR`C8tLe0Ny}ng*anlU>O>XF1)4&<pE9ZlOPFI)Yz28O8~F?w!!y&-Bo;_qT>NEzT^r+k1E6 zpH$j98N&G(PSp;DKHqzObviWu9ypQ6ZWs<%(Ehs0-SYFDRew$U*OostxaZofF1ZtR zF>KOYxmR(i@mq@B@7sb?%CGxxWf#~NvM@m(ziZiKZ!|3J%S=OVmKFq&KY5HK8@>pe zF$`&b5&xoeV=Uus!t}5z=v&)HvOP%6MZ1eXJ*zX!v>vP>Uas-Bz{D!Z`!EWe%q1Sd zNKjb}%JqPg6pmQJKb5_%C^2jk^jvh1M4Cj7H3YOpVWBgxA#RS;+r8NB2yh=vqnYmK z5s}4~S!cVmf{YZ>lfFaK5K+J{2Hx}bjozLf0R;l)MVvMjBB$?=3`#%>rtRARUhjFS#dx28#XC3R8_z;u%-fV%$5uxho)ny{5j}Kk@bm5NX z^G8S~yL5uKDdwJO9gF41LgriSvHca!Ka2BB%|4u@Y*Um#+H`8F9!e~+C%%~8D1C{> zTEU~NBor~)Z~H=bF2_?XBH5(lIYZY|H993vGIc8B;;sA3>b!fw`Nt1(Hvz2VLT%FH2eHVBVmi`k{9LZKC5fBQX|5AM^e{S0wKR@Za+E4%Vv>9My89fI&9F85N@dhy)9?{uzt$y5?TG)}ESubR5^MC)| z2AKJa&)CIS!dQ$oo3}u^wk^*_rXMmY3-iP65f;^jRLV-paXSB2-n!4UbB8Mhx>HFG ze~R!C!o^AgDClwmr*Pei`2DboThWU@kvd0!6+8q&z`%%lZaZY*`^pX zUZ42hb-!%yFs`CRD(R9Y*%Q**s`aTy!_xyi&QwL_{JMc`%og89Rw~Ll&-!o^V$uQ_ zT!~yPHepU0>TiZwe05srzKXAgd6%?3n^D2kDr=*a+q}0`zHDxoweDW{8Zo*?P}{No z+23T?w6$*+d-PC+%cG$23u}vqCb1_rH!1gxtK%Z+tkk2=g#8O;bu+JOOck3r12foi z-|ij`OmU7%H_evS!%x-EMw6r3s2fC-=JOA?W8cn&9x8#w1yz+4B?YRWp#A`R4=5O1 zt7a}@u-`wc$S~YH&xMUOu~dBt=PX#=YGMfY?J-FG zo;!ppl0yt4mg5fuNPhJf%iOaDV+aYKqu6FzV%I&jfaeXXj0F2+NavE~M&!!yBYG%5 z|9q+D$;nCPpihlm!}&uO&HW0n!{^2i!a(64Zro)?W)0Pmtt0VY{!2J{wlf_qmMv5njCThw)&An>`o45l{He`&? z=z{%9sBm-RQ$`AOorbwT)!vx}2M0$NtUeD(e_Soi6McM#KeSsBE=AnciYxjjA(YZ< z-%8KPMB~Y?W`qzA2Z)TLo8<|mranjbmN-xwyd#Cido$FJQdqOu`hg(S2;}eH`R04~ zIUR>9{T|w{?Eo^s^v!6gFpD}|Viz?Z9&1`9c>L8IHSMEANvJas`1dFNYsw@TeRv;N zAvcvQ*p2kSC}Q*{%zYkxLDo8!VX#=g67G>k=a#0Ovd^a|z&A$~4uR7fr2>d~idde( zI&wqd5${W5zd&zQ)Tgpdj5ip(3`d3t+n9SnP%v7*+rq-a(opZfoh{La#MPri zGUP##JS8!sEJv$WVW+&Ue+Z-pEYj^hUp0#61`?9v{mYg9x)Ts=UITATM~PC!Q(D{k*ucKAew>9r#ev00o%O_N|kkI~3dy z>UkPU`SelHXoXDYX8)AO+q7Qw+7t#?@bkcYN{c)Tt^Km^a@VLFCS-Z$qiK?K$v zK;DKG99c|H;8Wws!?{&Qn&UURXz0cQx(C>+Q{p+OfAP3jbi>SZ-I3QQDK=N1 zGYeT6UE(+wg^tm!(6t+>hB9DxEc#9hU9a~e1m&s5>vF$#q(jg##D$mS$Tp#mzqOpF zX}tjB)JUc%N(@1Q%;$X0tFs&Hd{5Gs%~5=o52~JX5JkQ#Qf|wHdla^0v9ttgYu-1O zNBbpTtq5 zqFo&e zOb95yO0FZZX2uQIY;v4cnD|82e~$ZGx#Y`Sel6a?k(b0bg~J8|W$DwX z!f1jJR`N%eS=!km-lyHfO6DUO{MSF1?_D*Cd5(gaW2gjYqC_=``zghS+Ch0kLVmmj zpkHIof8?mZY(O@5SVI3OrRT(OmPAD`GjV9@D@9qzc#5d1I!Y*2hYT5fMJ423k*?Kh z>8o0kAw9>jB`Tn)a_kjWV1l&lIjUfinspcUQqek-P5rVgte!{KPqmQ&WKDclx;&Ye%EsNy3t?`z7(P!O={QGYbfll6q80|Brg#6qSSHSr06Y@VyOYej1 zo7waD#RCJ|V?JQnM9!kv(3I`+YvG%T>hcsSzh%&k(a~ufgOfry3hPGkJKOmsX=iGj z=Q%%GXEiXz>(Fy~vNISjb3IOta!2z!;RkbevaP97waike ztesj}XVVJz~jqDN4yG zF}OT4bOg-ZiK5tbMyWRQSD`u|J=sOWmNk~1d(1t)rH8feBlav)Y!ymvS(kBjvCu=LUedt`DJ7Gp6iMM&J+WuFbn@c> zb8_o`*^1jhqKJdBpouwj7tOnWh4;TcJoN^R{~#!6(JJ9MQ7*E;W@0hsnfYSVw8!be ztFu7|+3SHZoSN^snR9KjR04N2UwZ~R&N-O?*Y3BK&dV-kzNwD9wb?de>h#PAOR+7L>1 z&JyzH`uIQ{_l#g_y;mm5Iz+*lQYw;|1Z^Nm7=KPJ29&dnx}D}&ND4O25AbfM=I5R z{U~r_S+0<-^^W%Wo$~(trw=UM=Ixi(1ojR-dJE$Gk2k5up1#y+)7%rHaFUIRY17o+ z#IfuD!jw3Fv0EOpXs>%J zPBY!(%Am`v6DH9#isT zS;>%+%VUtbp~M|6R{sKrfFyJA(O0x5VISfh^}{vjFC`}Wx_)tvYFAT30#5O8Xlr1i zFM0+1`c~&QP;^qRH}jW;_rzpL8@E#Y{MB4mAAa}s_p4^Ppgor6>r#B&9M$w;Mkh^%NSY5V;a}l~Ud*}V&U)EPrT{Qe#JlquylF@vVBNq|y zf{)-ou?3$80Aw>x+~%dQc;Eq*nDBbOIN?*4z3?MbsoA_+*5lO!NXPMvrD^>h^Be%SZt>zIe%iaq0+Kv&rC**aoLo1c@FqwHdDdSzG z!_a#p*5#@`A?xMxUpNP2ZK*=eNV4;&RV~LepUbxEfPCo!=Tq3{9beLSak2HMwCWwuY->(9?0m}m)jIW7t zK;Y&M4N^|n+kT|V)Q(cR<)wjo#|~ojF9!Jm7!v;5G~Gm17%F~ZViI%pt|%?wU1`zl zSUS!qn(YNopzIG55hH+S7?b3)U(Kd?w~(*;Tb1a9GL-b;dY}sWEIuGgrru7-KH#~3 zW8wWoMJoM12k$M1ueriDu_hO)YgFisOg{_21`$wS0m1y|;H)q|xRL1+jGUTe|E%)g zS))CUNVWWrU7pWE{EFj^krS4S0pCvztBp+v+Fm3G+D1Mbkx`ZF`m|*|`E0Ai=zEte zd8*MuPbwY}U4V;F?YVCQ?9)x1zg^C zIiEOOT1)R|8G<+XZa`r>M@7kX(i>~n`3!z@im*xjy=ajBY2euoEiv08yh9X`l5oqx z`yW1<1*howr}1{DGo3Zj*NYb(yNXuzCB_YDEq4L0D{B}>8$UkWI(*Z-_O_Iqh8|~- zXNo3z;^G>$5q0w1^c+FLlnKc?yFmo_v%nkhD}Z;vY*vl}9ZeQG6l`g*!zGR{jbHTihDPg=)|(advevG;oLx zkpRf65&{ir%tvrW_vE|Ncz%Djd@7S~(2b}o z{Z>Vp3U+nE@uFn*CS&`!gv_tgg_xfYpTAoEQdo_5>VS-T|6^1{JyV`y9%4JaaQ=-< zEnS#b4@N^-%JV@E-tVXYw@+?;2pmZh{3eM@yhias{XJno_5TF5f1e)U%e`ujE}mma#s4INdAse5l} z>L_|#3C3Q7T28*ia&5n^hR6}7t1O+YS=!vb6&?0HH@kMZq&i%ycjDgo{3Lp^uUn<5 zm|61VH2017rT^0MUFr2Hb@>C!b}pfjfb@>&QzcRpup$Nqm|OlQW zJV)=*|2nC7d5unc5{DQJ8&yS{#k<`IQ!# zze0Ewz{dVzq<`=m<5xa+bWGqY<7e7@bGtYnz=B-SL&WeD=>@;jT--n~Yy%RkKTJ9K zY8KxnUVwS+6X5#HV;c$1 z-Wd*4dUMNAY)lV*j!BN?Nd!Eu%r#;eu=jjEuqEl~rR_m;? z6Wm(_-mph1E?%}>IpA9{Q#wEO>UkYPo^jZIaKUmcNWLIw`Aut=TB@hO^WO9)4W-IW zv_SUOE8ao2SN^)u1Fa9%u_7Z`WxJkwdxlf{3@js=rI#mGTu8%UzWIJd2}lVj zB}T!sNJ$E?9kHNq=t8z%0KzNt_yhUaO@Ux~f;_zbVE5Qijs!-60PNK_uX-stND#(H zhUG6{)Lgn7fyRkRNlkhK@yT_NaP!^v#6T-#__aBqn#>HYgEprU*m{5?)N8??J!e}m ztK$&%xjqV6Iq)&Hn1}OTYjQ%`JZ+ScSHAk$a@ms2ocQhY%&vHDWX91?-t5x*<&*}f zW~y6}>8)FmRPj)Rv7$^^bxRtSW(A*y8|aYQ;ew_goblg41puU^z}Rw`ROp%V;drht zs(ogZNux7(GrH4e{h+zFs2&8LO)l(I$J%US%dm6X{vcR=Sz?c!Cl)ofF*r9MRQlfT z;C=mI*H6fKTWK*~p`7&f@l9vYHz|XC^?t2v%S++G({&t>#o zgzGocShW=ah$W+Y92?yRg$@`aj7Sn6oyrEU8%QhZ7b!l}(4&F{!2AjVpm$KBQ;Acd zXb*tfmB@f)E5|-UhZR3AvC}cMSej$87T_%yi_1F8cx4QOL&As4^AxZ8Ek;m?oi{ws zUn=w-M($wW;Zu8hdfJyIpk6|BXTa?4wl|(1+C?6R9|C5tKo{(L3IJ8Tr8JF7p$JHy zjr1p3U<)IeyT;UJf{7d%gM>8lZI7_sKz$VO6r~+#wADgL22o#^N3QS;&^t#l%tc(> z|9c2Tg0>Wg1sckmeA}68NBxbAf@ezACNhrZSI)-ogb$|Ch|J=Lly^l+qLmjq3Xtk? z?C4=`h*hBFXO^b4g%PbE1j?>U*a{eoU0tqNW$)#Q6U#MnrcJ>b`}20*KK7VNXaBq) zc>p7mm(ny7O0UTZK`_GK!F%b`Uo-pZzRjOw{=_*?>)Dyc^`RkM`0V-AZSeVmkV(ae z`xgJ*(Y0q`WyG1U>_UvNLT=J)t^D)Zi;JH{yK5FnQndV;Zg;GOrLnv&=6nGamgn+b&YUyxy832Z; zgb0qQvk0|#RtzOwU;h58J}dDRR2IR~2iS;lI)*C{UpMA~^Xx=DB7#S8SXHD-*`cNPnKQ(G= z7{+2`ET7I}<46j|xe7qzOj4cnV1LbqX4VtY^Ed;dh+x3z4tKUU9qH?B?45n^z`&U= z!oSP49YEkjQJ93o(y>)SlqClG<=Pt*L2JiIbEM zYkbtuo*rr{!<*=L*3Odl)c;A3!H}b1$XJzD+Wo!dt3qGaH<+&*IaST-E8TXdEkr-m z_kI8Pv&3|@Nay1B8Yo}l%C<=>{jd>Q6WY9~enwN2MRC#MGtl?)M@5t=@In zc@&4j$3w<0+~x87Hz!lGPpmh;r)g!(eu@1hX){N?)v3Z@^!$S( zdHY@V{&@U1;ZpI{ed|Q-a?ziG#7~UORl3==`(^KN!xnIZXwdJpmamg05l}?$k01QG zR8dcFk|01pkUhXjo|x!5+w*|GxD-kyzG&rLf*F7fhd@qYbwI>RCDJ}6KYTGX2nHC3 z>+KY}EL2}pnykjMTYTs8O*+gM!doEDc2!PlnsgfKypyMUJ|}L798TW~3;owOuZVD0)HqVD$wc3Yy>hv*?rSupJJ}cx_;a}K59YdC-Ph3KX;dy zU%Q2$*sd+F`ReC~Ded^HJ(k*iZo9l07CL+uAqh?ec`uohq(Y0V{@^;f<&B@eg|>E+ zg1;@OV9C@Tmed{;F%90AhBFkW>gRLHpPv~&XAl`SE~_aEkh-(|c&W4qj&``?)c9QI zs^$9q$H3BKCasgEpDcq<5-I!Ls+4Z$lw3s>Z_c6U2&@{OjCCP^)0;2?hM+xui9V^1 zp$MomjC^b-r&2ru6BjlY3UN@>-B}~g!?6$(yO0pH$f8H9V5CZ!R5ANv(F4&0-q%PA zEO5%82l-!bCoi+US=Vv-ZAZi;9k#krvx6@Bn3hHhwiqRTo}VEJMI{>KzO z0nK0RX>k@NbYIUt1ySFQF;fh7OJ&U%U`gu0cO#@-1!lk}{0qV}JTZ0Juq_sdrF6s| z`$njr4yx!g!VEs{68C!nDWEg1e!z<&bO3V&xdLp5$Q=(uNQJu2>#<(ntg_Sf0k{Z0~u+GUP`zE`B^Uf$Gc}7ziO@2rHi(C zQzn(J145n-zmOCJqGSBmig_{m*sR}(>ssUI-kE4L-Q?muS%6HCdQ4qj|GW2zu3`mz zo6Sk6OY-gefk1sR(10>tA`I@Fy{_nMmQZJ`8tJjfNd+o>9P@n<~&g)8XoY=5wwlziHy6qXJdS&Uhe#H zlE)K0|AdX9qL?adxkM%Z-N%o=cg)KF(Fw8lp!M7HyPf+Ows7TbHx+{#L;mQilK*c6 z2C5ALK+*f+R0W#?Y(N2m^f)Z3%QV50K>39a-*or2d-c4}rWKV#C7bWBcNN>E*`ymn z?ZrkQ4-kc``SIpupXiQ{p=MgRFcKr!R1%Hu6Q-FtoBk2F5I7G|0>|KjdTU(um{J z2#$ne8E{mjv-L3#gH85NH}<@(IACXLPaGuofL?^fyNVc{)oeVn0vN? zm-a8m28IarU2_Nkec8(4@(pRIjQ(0kJYJ9s167<@OU8|rPxy&(fR8ebzhedr2uxt4 z(WD%LLh;SBNpU12z09p4Pyi7B;JPv7$x)8qyBw7~TkUhv`^)umUG){fL+&yx1b_UZ z?ZKOu`aEZx7x*M7!OzoYND+5K`n#;a#v@ul=^6>;xT>LJA1w9{{7VO-s{4eEd3*4$=nO)QNM+*FDKy*nOc zb=$x_?of~w&U!ayaxAeU6G}h!dJ}l7+7pj2LwZKH=Y)t3!I7poP4r+I?Oe;DB&S?j z5~J4eDp>ad#05+~c}A=Gic6f4PT$dEkfok4a{Qw>GZ}G%m^0s22et@6o(gKIk8Zny zD^5!jM5+KqXBP4qCxqEb#L$td;P}I_)qoVn_>R~ghFBP405U@RVt~@Ftz4GAXIJGd z=bE!TfGlqGx*;+rwA`XLgji2+ioNbhsOW8n#ZPUzj_m)Pqfb6U-ykxU-4N0(qsx9X z$WT13ue90$q2o{=uX2Zl5dU9~VzUcMaahlBkw7yGfZ(RAI7H3#S6hZr>7o&JtAF{p z+RP~72kv3&W#y&0B06qlBI;`lvHz=(5zPf3#@}d9??R~&h3UQUkzppcwFFK4X52!s zF60pa2_@s3#=zqw$6|&MjE!LPyhW({W6VD*d>WEbhtuMgdR~9tm}&O8L)as+9AVil z$Os2cz0>4BY&c-SB`-Ecef*iJj>|m47#augt2WW7D=amo9H{k>Ab=wIxkrKF@tINQI}}iU%xR>f5=v3G$51aX&E^|=BHUpLy3?Kj(T{GK4ZEAMbQ?^d zsTeKKqx439wTj2Q3W^y8>XU~TMTv+>u=^TY|Kqs%TeIV<1p~P(dmUQA2%?p7Ng@a# z@WkAiuUBgs)t9GS{S*`9R)97sNcjd*kzXm{gt02FEbX*LSi=ec+^N#Z>}k;wp3xpj z$FuYW5F+rx!Wc0TuVrv0F=(Zl8KGImV4U2J(-;vV!{J5Yf>8J4KZ75~%h<8G1R~L$ z6#E|2>_;Dj9*+LG#WL#&LPoOtJ$1;6^p9sWF$c*qigA&>l8h4J1hM%Sc--E76zT|e z7he-PsM*4OxlaU9uJM1;5resPy8$Ztt4@7XzuC!QAzm%4O zT1*Z8sz&mKK1o_=5`*CwygB)&Bm8v=H5`8wfS|ZB08p_EG@x<}N)`)p1K0s9Y6K7@ zH4G`Wxx#^xVj1A6t3qZ?j)C0FKxGwH90{Q_`xSaj4zWxGx=iS7t2mlW{_8$07YFcG zKlX;7(+*?yYzjKT>_#Eg&gq^1lQG5~Ocg(K3DXh^!q^RIL7>CB(In!LJ)a6XEgt{s zPv^Z0i7p(nLM5x&a+mcp&pwFWDHT2+#4SZC0Mj^uuLtWHAe&G-EVMlq`@9>zPiAo% z$Zxu5KH2_55Y4~9pY8-yX{gH-Q%Vb$9DMFD&YNHh0rSr^WGmKm?bj)K?LS(B%RGo?c%+MQBO3wE?i>98R;X)}Wt;tC!gL}cuq>Pu3r3DKDDR4|WLcKOtldtk$ zk8BguQ|^t0jq?OOu2yG@fA7Di^6cxQ1>!!y0xg$4UlN4iD=q-**q@9B8CDbFr??1wKya7e zb&SbH6^K^|iRUWRD4%xV45;9&=P}>q>d2toJtuHd0OGKQB>z{SQxVwCAVi4)EkYA7rr<_>v zkJ3Le;Gej^KH9*@rC`Cu5Z*w8(j59sVTN}`K#ZeM&H;digMmRVeHeg1uCQmb;735J z{oGvtFfl-%l5QAwP^Z<}i$U%yJ_!5itkoAkqJU5f>bqd(+*}G&xrDBmF|I`1eoTIx zm+hR!yJ)mPJcW(hxLF^494hA3k?GzuO4?k6|EoO!%{7BqI#VCnPcug}ijHpVGafWf zvrt69*2_hN$}uO}2?$>9kC*q-$TLZkf%=3l^K1eU<02+62EB~12Q`ngeRGEXb|_~L zSCszg7Jluefns7)(UaQZ*5J!|iuaZaPk`v2<74iS?)VTyRQsXq69CW=Cl?kWLCwVk z#yb{&4N*_XSJXxt8RAxUmExq)aebVE=>p(8A<8PzuKbLsU4=n(42?y(2LZtIc!I&3 zfyU+1142EeMAjqo^Hu_M+0Nlzq`Odz?;@U{=q#3xO^wh3g)tlXlM<1#dK)t%7?mR1 zv)IKwQeGVg^1ArP5b6ic)1%9`Ctc$f_;a=;1Oq3q3u4{R)5c#LlRN!D$HZ>93eNGT zOUZ=?uCR+vb$R8~2HpN3_b=8PLf_U`%orVXw}`)29l02Ed3{OzzY0VD!mYi9pn~*u zE`^lz)h7UAZZ5_YT-qVe<27!!6b^|2@EB1I(FD=Kd}v5TG~{p?M^^fG2r0x5RW_6D zaKNTUyBc|RHU0(85qzZdy+#_&+Mt$%$$>!xi4xi<^a+D{OGHSeQ~qr%LHM)05HWsI zVIuJ$|6JyU92AD7tRc zJVdea2tz?Av^TD~z@OddA_&Ob5$3@}Xn>^53ua<)LMsi%pI}8rajbXwx}v_PxU~4? z-IE|E?5j6MvWt{83fZZ?L#o@vR+s^@A~j$B|CquB3=CUvNP87z$Hf?kP2k7^v8Usr zvvz`b(Y>SdgGJ62OHqfR0UjL(9Yrxw&jQcDd|~JTBAeso6(snEA>mNl>v5nsf{nXd z3*zE24BGIo>K1d+C}@ z&K5!Kl#C@-U!O&hZva9mz=Tv~t%ZFHbh55P5YKHmC> zF6t^UBR2FZHeMX{;pOwIm(0s_*uu4r;RUmcr{e$M@mh z)`pF7BmXs50D`Nh>sMt+z<&{c|9-Q#{0j$N=hFl31MhCvQVIR3Z9tuZ7<@tKv_W;K zRJC&>Jb4zOza+yy;QknZ+wq-75))P1ihhQtDkE~vxH<%IT*G~OcGbL5tO)SvBgvwN zqUowh=?Qh@KY+p4c>Os(JM;*tOx-TP6@<_wHf9t@-_e}|W8M4yYKtz53u?WBb!Bjy zl92bt2n6BihJ~~T{}B`G@8U}lWy?{-STDvWm*>h^zCW+0Gmbu9!O-)V2|V7bzgAC8Qo8hhDy{?Cj%-Mk4cV!28Ik`_~^U z+G`y+kPvgu{~%lbFGg#Q2;~u#MsD44Dclk%_P2hfn7LF{!8tL0F{S_~p>n9^UaG{H z6rc2*6meh?0G+zymz6teyNrzIh}ZPtrMAZcB+<~RCi%X7%?HV#2MBqiI_FgokhwmD zsK(#^P!`t&!j-ZxE(!|6$MN?<#k8T|N7o^tZ;zd#W(S&^n`Q1%U294Xp`&)p@)#y2 zrh~xoQ?ke1=&=F#{+P3ofzI~&(Wo?L9UYEIToKI1u7qGv`VemRnh6iut)0kcPe(Ii z#O9CX8Aj<8V+7s_RaPT}L+vTS9zZe^=sR6vFrx!$-6##t8_QaGLt8&MHkl_M+0407@E)*UDzHR@}WY{SdO)$Yv==k|ua@82S#@P3EohEbgX&CW((P4^(<8TV3 zYY3+sxF(B6xYkbb7$=pM&1VP2#?sie@q~r;k3L=!;^h#?u4$Vg6uLp63S3%&!TX*_ zrt!Z?m;Hsu+Pt!Wqjr>^Jw%o!4_e6i26kFjI{t%!7KeY3QH>0{pdWX+jks1*DZ6TG z(A#t64C{nS?SH=$UA&~hoGNdOu^MXI^e{j^X_-A_B%i0-z()pM)a6NXGJaD&p8E5N zd+6oBo}lkjCxY>~Sn44eEaDdNwlgR! z=cA}Twroy?(Sw?evf%ub^F`-7x6m@_UgdHb3IhI8e*@U3sYIOSVw)zJ3sZdMA(D@F zI{VY)ZS^vxn7!MfSF9H8xZh(3YQ3(xxLdYC1iwEH3cOg(?LGLe#4q=Vo5`*=ZoTtF zXBUke+`k}OU(3m4x!+)z>*;7U`je~GuGjv}=0eIt7}9i9&4G`Bom)**Moz`c>fn=+ zmRh1`{qNLGyPax;64SgdhQKQkmB6NYyjs;tX{-CoNZtG08%tN$FW26?0%u`oyVa_N zyIJF$yGvue0$O^9!ph4#RO(y8nvG@=)OW*n+&_<;ps!U@*Lz*Mg2(tDa{uO6p0lF6+1SM%fW{lde|2c>=(#j$SyjdYQ4OqUZ_XWBu; z*Wj&Ei~BT(3E}bX0Us~vI~s^RJ_=pFPbwyuMhj+*ro#&Ir2+3XN>zjk={WX3KlrIx zKvw|E8LL@QiSsrGbBAGS3QSyrNd6$iGvNA`IQ%(^K^|c=3w$C41R8z?ISMe?`XrPa zjD-<|wYE~La_dNtOGj7`;yRAzFgEjdMtMH|4reXM(<6+GoTbez;vVKhD!PFk0Dxk^ zzVRDbgFl3kHv|%oh>gj|_54Dh$RubIipL^h<}L6Xm&J!|rI1@kMlT6Ph~`ABq%%HwG(mr!-}yeRa1MfHv|G4b_m^Ss7@ zCp_+hNDAe%Atf2R=*HNXx@To8w=@|-c}1`V%?8;Un2&pn_dFmfC9@!rLQ3EkGZhmqi@n*JEGt&e)_u(>sAFD2_ zux3d1`cWbk^R!yEnU%J#KeD_}EoMfNTA?I*bv)rqDhUe4+n}p4L85Lq?7H*)^#Db2 zoB?N|d#8+M39l#b)88vA;i@c>Hir~S6tX{8k!PRkhIe*-x+eCnD*K0i&{;8-J-OH* z|IXO^osW2__(E;wF_Xj4li|a~H=#>f-!l67$zv}*IjWB+tAA&YHc_DZRjWJCU!9ne zurX6O;8v=$tGk+&V6nYHukb2Cl~I1IHGnc|(EBat3C#z+^yG<6&IAs4_}bppY@0@G z)udCc!P~5=(C#-;^j-sZNnD)=^^yu{{OWpEXQ`i(M^qK1%I-z##nvf~;>-_=B&co` zFGU2KvAk7AE7L}^HPgyCcW6RIRK*=~R})K579UrN`YxAbWH4`*4@`Z+3Eh>cucMGo zKg@oTH)A&S#H8?7F$J;Ni`FE!r!F8N8C2Qi@9e}rAi%pu0dDiC8^@pxMg6U~(kuB` zO%{l`fwqPPm-sEMhH24>f^sG6AZxgn@*(iCCil2tTp9~7=Gs`K__GpXLo`_^C=Q(| zUl&8NhZqNlWsId1Xr4GzuCvP}gGlQELo8#2j>EeA@3hU^=Wu}RScE;;OePr!Zmtgq zg@9hnHGKc$8(cC$cQG%f!OMUhBD) z1uJ|~scU^bMzqv~Rl3B;mwmy;XTrXDQX9;(KR5e#G)Ltw1_mBgr*adRG;H)=g{vF~ zQY2{0ttU#A*{Dn{v<-ZEyL(MCb%OlkVc!)POI>4bVdu22NxmQT$Sgfn<@LUaL}AN| z%Vw5jyN7t(sEJb(CwWi9+C}@|$(82DEj2pJ+VKu$yXaGz?jDos_vUAcI=2-Ta}rj0 zRo!e2}ob4qEgUq zxzIFLnCD!4Dg3dQ-uH5ZK`WVe$1|`MGF&b!x`U_7r1oFrb$KqW&idKW8=GlHlPaGJ z?Qm0iH@+h7v!!&_p8^?W+w*~-t|rG`59x^s(@iU(UH0eVn|BkZGOMm_C!uG$*jBg}k3s*? z8w;y0%5{NXrj_6DyVh7$&TNgwet(uoCqCI`dEFUJ|_Zt^`7E ztCR8fNo_CZ-8L?G#c5>%^>g?G1Y?=k;waXXBtW!9Fa8D7hr&aaNx{c*7a|YW0h*=@SzZr#H(FONmy@ z^uwbVrhbD-EKc53sCv6pBx!E;-JY4p`MeojQ4r!)^NT#(pgj(3E{zrZj)3L>Lel)o zNNzPHnU|8udgo}@&KXVkR02~(R_6&Sgzjs>k69=k_>v9EU&slZNK=B|rsz$OTk*NC z=+~CJY)}8xInlGRxR+NLWmO%Y*fu@jNAYBa8(&4!4yVpd$~)eaB&Yf*>QzX2=3BmC z)hSLj@)S53%Mw=OHnWH&yAOhGQn#@AzUBK`%rK%+E)2!fe!mCTIVmoXD%`Xxc3|m@ z&MQnaw=r;hEBdUt;!Un$%9E(+TNfyS>$c+DS-& zgkZrPLPC%L!QI{6H4wCEBtRpL1a}GU?rx1V?gaNn8+RI);D=|AD18lKl5_RBpU6~NL9`5%mig>M{bN-Qc3 zF=}@OB~t2ehOgWs(<{lo%S$iTOruT=b4Y$I=>prenTWa+RQ$@rwy7aBOUh_0>V8Tq zuS(O7|1pNftXx{7J~qn4dD&ftdQAm%`A-&r%sEjr#%-G_PS790PwTniTZ@G>B^A>x z4*{|@2MUW<;=O^24A;0;vissAM&G201;E8oYtg(8ZH7_uUU-32Y}Fz4wh59WGvbVl z3o06R0tZF(vFQ>c3ISGO{gLU#_xXXfP}Jmc9SX`hOc+Xa8oz^v=p%H>M3_ z7-s)IfVi!|=I3RnQ5c?!8w!bB(^W!zQ0Ck0eL?s+7oUu>kR39SV|9!gLR0^;V7a#W zqDF*=<|g!8r*#DbvQpK(#DW@!Mt11Z%DC*)VMn2g1Ms4J4~M5D(DAQCzgH8Kdw@yQ&pRK-rN6B++rl?b$PXO1` zR8My2C{atU(#1e>r|ej;ysY{Pz4SifabAVGyzfEN>Nxy@o6DVln^}f%E~{N?1(nms2(oH3lPQ&nqB$3itZFQUHzB;av5o8YMAB@EZ)J zIiVm`_zBQJ%}=?Mzn;*S=>*C zXMiN{{k>Aas=pHnL#G4M;?(K=WG`rDqL=LU5Q#CFa}-Xq=oMbfRoeYU;)(joAA@ci1!=@`U&5pv1(MJZMFi`CltYlNX^J1fmo zgXhn#;Zu23lGqsR{l#!W-jl>H?4wu1wBKV$0GC26&=UX|hueZ|1QFbTi~&9q=GGhe zzSa4F(%DX<0MhdN>B^X`i8l}Nd2u9IdEaT?I6_7{$Bm-U+TC3Uvcqvl)Z)uj#eM4p zxD=eJqK2*>EX&6Xj}MM;S^m(HD_hF`QixZCm{tk(GNnl57bB66(AKFJlc=XwY0p*eFD==KrkwY5TP3nGJZ$kEd#nkDD0;P=NMLZ)ITUDvmj6|WCO}DpO z6yRoes+yl#oW5N-K5(P}VfQR5!29BVx&(AB;44=$M}OUD12|xHoTJ&YsUcuUMsl{D z`nhHCnXdVpVsh=z5p{^X(n@?qU7icDWj+)p*s2-q2Jm4+Pvbq1rQQod$)L!WU@o?# z7#mQ&C9?3KC#e~2OF8)2s`ewCb}z`9Vq)C??9Wl!%Z*yRlT9qw3wy0xRu;@;$+zN~>#9uw#_uBD~^u*5sKvnOWW5ma~0csP8>oveCplxiU6E zjh=q|P$a(va-^to);nF(({)OGNy;UH_>~WL)(I#C%c$&}d;NkN@WYSj<^|#lhZaaD zZ>1z@!A=&JOWzDpB5&F(%SQ!}Wq8J{@5qpFHND6MJ1A*{S3iAx{(TmU`nY37+$nfV z-IpX&1HoQ;LLJM0voFHf9pfrmPV{j`_>#=2WCG?j zudW!xDp!TzxyyGLMc@V>-ReUV&*@idba^xMUfvB?6#FIq**K8KJ88(_ugBdiG8){U z#BWX6`KafUnS1{qIPhW%cmYhpU*XZOMvWk2V+ZJEOg2cq)8yOp*t!IzXLEsm6g^~* zMEX+>09{Mvmzftz=8;FI1r5F`&A0<*s!RXH0W48ECXdvQ_Xxz|m1L-iS$>S%6}+@G zNZ01Df{CepPzZbyt8Rli|H7~Z|M5S5J<`rPyI=HK{cw7!)+-lin^SLfEH$giU=F$Z zcD$xjfA^I)={)DeY)gu2%{kg?o)|aOak||iB0iQ~MywfToT-jq!OC_GBkl?ObRePG zDbFY~IKB_e#X}39coO?e8x90sXBEk1Jo0T_=jx*NN==TopZ zf2sd3HY9HIQm{(}<0YLe;@Y{seh})b0@PJ~+TmKjB_JcxRSQ|8MU@r3R-7?p7vHG>d9ecq>rC%$Ie02#TO(_% zG3GX5?^yzm1LKKX3Y1HY49l%z*}8bbcSqB|6B5@mvGdtH+(=DXcqw2Cc_^~V zsA4;BVJ1_{&O)ZC&WDB@H)<7HRt>W|C4ia)q24Rstf%j%vG`9x;DXs^re-4?IxU^! z=Q!rQxD7Gis#iO%K&abdB>jT`HRIxmFqhLBVp$?s4$_u+RQjuZceD1CMfn<)o%MBz zP%N>|j_(mE*( z8%Raw`ybV1y|n(^zXr8=+sJzZbNFpds!b zjG;(_$B-%L^OkwufcSb5R$&9s%8(13eU+*Od4K+TM>Nd}e$--6ecB4Kc~lo*b*Yhj zpS~UQA<)cvvOFDgQEq+CFs-#CxMX>=Ct8LwCgzgycEszBDdY$ptbR3M6$>)p=zKlC zvqC!|p$k++d>yg7tdg{Il@qIjpG8b;aRZBjaUH${rHPxT#7fse0G^>PfDd{C(65&~pAKFrDxV;z`$9V28+ z`<<6?HTzs^ZuMNhS8c%fi7{=N6Q@`zvB;;6cZ{~E(ZE4!ZT=_g zD|YGwuD~3Ck<@d6LCN%5VaWb!%lnu!%tDVlJ>l0MUj&qf_zeK;A3&Cw;}jJE8=2E z8sL)3R4>%u4lOeDFBSV`?c?lbzhe6gYAbD)h_`dD#@4d>9RXuIsPjskm76SLVLPUp zvXQDxe8)((C^f_w(ScBIv8^49-LNhjk}cP>Pc*biC@Whd2wB)m6)v^UWs#8j!`P9#jv}Su6t)}&u`*IeZGnTp`^(_|62J2#gnsvVs`nqX=uB){f z!J2Ygp}O6%kd(n4hC~0~GTC^m0sP0K&*W`j`qpWnQte$JidVUVvPj4Qbt=Kpk8!PgQ9~! zIMc?IOJ{2tF>~3%to5b&^W$0bV!WaDdJ0E%uV_y9srE72g53-!{3x8X^|pSb989-a zg8BFye8jJU>-B^li`}JWjRHqdqZ^WX7ySw6CNM-p z5RTxJ!@f1^O>-sLc*mlJrjbTNj z5|WYoA_H~URhhT$`S5$&eV~iBO=C>$n8p{j-U^E?@tF>*Y(vk6r3|ID51B_Uj#Dl9 zzn4=w3rdU+6wNSCN{jS+htSaPel%N%CA@@G<`Q~p>(|!o0>s*AkUw!*mCn7sYjvvN z3_r$ZQm%l(aEm2iqx6f_3gzw*${A~_{{zBEgn!W;kQSonKy*Qgi*@%zo}ce_Jfju9 z891vyAK7qF*04f!#1WdTr8&|IkNMc@m6bkfbrU$FTXwlPhx>^R9(Jos2iz zTy6%r0DFh8JkyBG;o{`IDfA5jK~Efe=n5XR)Mtik%G28=7zUL!@6awZ=cO<0^~*%< zfM4rS)Py($rUds)ioI8c@05MQ0CtcAbjQo@{Kcc;oJNw(Efw*37r> zTCt#g&&IDx>oIwjM#yo^1?ne<`gj^CA)KKl1<(G1STWhMDV|MjI}a}zk2rLj3OWr> zC$S*)Qo=V0Z}z#?&9wgh6L-ujklv&4*9yD1%c&p-j0lBZ4nh698b$m-{f;j63pR1 zk~#^T&W<>o%nv(%|MoFro@Ib#o?TkilSZ&ZC%4!JX?s2Kl!z?1z;%vm& z!1AfuSjO6 z)Foo0F6wytiDKG1_Lob=6b^B)D@ZI3!VT&%ZI@7&O%-K8ULGCW1y;0Mv;tuP@1!<$O@sS?aTXW-hhskLs(vVBV6w^ICUD|XsStJxm~_DbEF1(dbk3G; zR&GCx3mO|rwO&f57Erp0`u_Z)wA~8%(Cy0}DXID-1hzn;&~Pq)DCBsD8j6pHCzqR( zg~ueC{pwvX`h+C=FV&CZqPU+E=fC&JW9MRbMzKWZ}t)1XR3Yex`{?Z!?qGl0U^VqsHvQ?`a%I1@BDDEsojBcFG{vO&cn9Q!!z@pOdRW z?c_sVTF*|5$#Bmb8MaU7GwWXn)cv@VaX&xya~rfClh`Fkl_ml&PBkAD_1H*k?s)Z@ z&4$&}#TMJ{oT`dMB?b-ClGs%$Cyz$?1(MD6=15WyIfUxMMg3|QMSIr)0TQj3!_@WU zLoMwF%#$*Bqq#Mnnv8`$3nH{-V_%F$&Tl=wfD;14iE~rz9=BdoJ1Bn}rQA(@U;N#cH%-O6W?gz~u6ms7$qnf7#A<6D z+p)%SdRz|6!F3VbjjR=H)fX*IU+fiU0Uyvb@5$VYpfGgX>CgCN=#Gg*dv(U{XD{!;Cj+2*&6J5qE-8nV zSsvFD5W)QebC*6T@wbLj(TSiy-h<(Hu)=7fktLrtMoo3?MX!$-9^5Yel49v@n)ux# z*BWH+k^8kZ&MRB%*@6jfsf);JF6OlZb*lP+#GX^nmKR)peVqZ!|B`yn1R=>6`lcAZ>u~ zn7P8X-YIwdSowo@<$dxIaWTv6pSg>f`Vg5L*7!0Vk6EaN5TuHXkU3?X?Kxd@;m+p_ zMndw~2IQQklbGuF%Kv?haoR#VeJHs3G9Lq|wzUBz?lvk}(79m*)!11m8^$qD8D>jqM z@-rdkwsc6K74s){6^zo|bS2-_HOcM(X@%#smv7i)HS&GgwW1c6 zDJ{@0#+Q|4y{+QV&ejM(fNCz&H=K8rn-lHhb*wE&!uYu;8{djQNg)ht6rB4$FqcyVEm+Xq`RU*?DT>Zl&?S_ZSWr=3OW|GnZ zcsHV+azm(5=$z0+tYNwOXXWJch~6UvFw$P^T%CXr`gv+oQ?7> zL|%(Qit5MXm{Kl!E=eY-7{4osGAgJj-q$F6_x)Qe5kvl@&|*_Jl;^%nM!MGlGYC z=7ON!YK@s5xIl~LaxcivVkRxfU|lr5mz{36$IJjK-TCGE zh9?2o^c3C7x94}N`=Fh<@0KGA@mx|w7=TdPS?d&Ql=Ex8Q3PlQUbd}<&1*cVlN9Cx z?u{SW_EYLK(TtvQlYfXLE7ep-!S_WNWm#*^?_f3vHF^8tp^3^IH>aM##coo}sn{Wt z!RDK`ns4W!r;nmapxxrAf|Q1v66F_k8yERAVUsjJHJ6yq;avjlMvtwy4_?K@uizMv z0yX*PISc|SQQaT!^t_$qr{VRv>@+)mD5=9Pm%nKzisL-Q)a5RCu#LOcQzyH}s7d{X z$vJ2hMV2zuj2|a(!KZ1W7&Lon0Bqj4iqih8-Kl?ZQL@Ta&#s7Pst$G17-h&v{LL5# z&P=#{xJ`K?cu(-OQ?PIyP@eC(8w$5J;mgH{s4rH@Q`X=&f8ZUGDT$5jQ7l)ytFT#? z>~Ndjzxt~o<35{PJtIg?g-T?Y!GK6t@!vaze`~S2Z){W-d}Tbqw>d|-34z&+#I-U+ z*h>!(R&p7mNOyf?F`1m2BHLp`%5VMJC~B^(gx*CG6IX0=KrxKUl#1B0XLRT7q@JFr z)W{&ganxorUn(U6a9SSDRNs1>*Sar}7epmFIxl~+DzafFWhST?q$JR#t+)CnBKK?#WC6m&eiZXYr^??P38}DGPl^>U%}{sf1NH<9Gl%SJZ1eJ z5O+j{?^eqi4YoaR;)mLe=<%i*l*gQI3seQB{FD?5Ls^V5;#W+2orh*i58H|?+mQ7b z9{51gm4dvJXb3Wv9GyQH3`t>}cMSItjPuGThIHvJPCIyGLj(0gtx9qaUg^e2cs%Me zt<3d$xE-pCn^tb;r`2C^t5#}1v5v^(6@}f9bbIVz^RT|S!iCISn87p}|EOu`UI8Tz z>7E+tFZGFngf+12v!hg$P0F#kNY`vXNtx&T+N>dch4C`zmskhYDwaK}M?dz-AEMt} zq?z82H#4F=0fL%n703E(^a7Rl71`@x>Bc=$nY)UJwkg!OS6>K0Nh_ivZ{|F*kQte~Q|9@@H zB7IG&!hk}Uh{C`*)MmI+N?4>hNbbfKp=1n9WbD_e zMAQU;LDqMp)OM|wJCY-Wd_~&u3crgT0k zBJp%iX`dok=~|#(dwzZl(6_u}xiIHt%>pXEqCjGN%z!HandO97KWaBBxERcewSz@d zVyflm$Czh5g&qr4Q9v_dl}G%@lU{czPK)Q>cYV`VoBzF2P8h{jFE3UAO?ww}3wZlr z6;Gw+D@5gZZ{d-a$3EHZVmem|%#8!Ez{PzjHJM*XKc^=njD{Ab-tKktfOrB|gy&lI z?eoY&**_iQC0ZX(C0Z@09k3sj7^*BX&dZ&Y6%{Fm7FX`J;uViGC4Q0XbSiaawXh(XJh)8nt+p*1jSn83i7OJPd1 z6J69O0@XV73&lG$1VM5b?#9KS+v*^R%)MSBQ#pScCJ%vK#6pXG8E*uKb)#F^D4%Jx z;gYS*z+`=z5Ez*00-xvYzIDI!JM(Z0YB@6^3vcnN_d5OS32i=TqMFpmix?YG))I=V z1%xKFksW3Z1}qxEko-o+=Pfb|YsYp=i10ayz*qvDcJ3&}s+-9i}9x`+^$!ne4W+?8#i7{@R`ss&)6 zqZ)eHMh9&b6JsQENDk!xB#_anTbk)Dp8^VEYChl*Y33X|$n|O0DTFMB2lF0bklx8EzVi4&*c)YcUI1jhm%J|Tcw|EY{ZD=)|8coSbg8(3|vRV(LT{IRgkIAs^N2P>=Y;0<$QPVL1Vqr&N zws7WnR2kLsv$NR8+1uxiv3|hfdHFv(W(XA*aU;F0G*!$p;rAqK>Udq_a@i4VR6JEo z;@m$qxf{*`g>E(RUe~xrWIEJd^yoh5YSchYd|l;KeqCFrZ}CyM-OY)FU$ld-#&15S1`lYVOgMj)m%A_ty&K= zi(%!l*r(!Zm-p%(h4tI%2PH!7w?26A${9xsuwfU9*NhlJx_7aA#GR3ud~%NY7vc~P zP{H0kgW1!zB!|$0yz?QSyqbL4%7{LZepJI4_onAOG3@%Cfzt#Y6ybf8V2I{^N?)!g zJH6np3z?`WpX6)$Z*S#)qJQP*Ym-rKE#(5>VTY?#Zp;F{XVxFF$E*aL$D-0!2CWi{ zpxV~lt}&sjwnFs7Pr2IId#b=Yn3sXEJf+J5<9-7_p{na%K5mm@Uu=uhUj|dLgcj*P zIZ@A$hEd6)nJH>+)Ix8szfEMB1cE!pPoqG0@0C4kZ(z)IlZ)){E1Q+F3luOdEpWPZsXMS%KY-Ml#S37tLL zGhM2jGb%E6lfE+2EecYTGPYIFC2xvduOajL%(GW?Q@tdA5;;=<=~Ee*2#@ z)eF?QK1y%jRe7~Vk=V<9^I(`_E0DxNe|F5nOsilox-YXHK$lI&0M*^|lK*6`2 zlTP>kepF`F3RH`VxE+R9U)@dy;R@MjJyw2gnCgWI*`o7jV{i#%r9~{(IqoO|bE6bZ z8J}1mvL3CVCRsOcDc(V@=RsS;uFAzq%v$GRqq7oPnAJb0E&z-7n@H4Fi(G9*i_ojt z4*eDvyu#dKJ%^!{Lyag(?^**W{C|y4jOV$|UN_ZfZ}~9xBK0r!e(Mql6?0{?-aKa1>`hhlBE{@R<^ZSA>0-0`HKSoEU~Jn2v-SKQclqZ7 z**;c@*H&f0ZhG7m2-v zo$qVw;kC#zzVCY!0DnE3AZ~EU<_J~IE*igO|6(XVeI;)c`-e%goaE@!e&@t=#KJ09 ziVo!WgQNuSW4g%XGyFa4*!$^@_n9Bsdc46LAN!xW=U>k;i<9c)>2c-h92Eydy(c}O zdwCh6k~^hX3WD55XIVlhm9Ur4Yj7Qi%aQF8IVW)+RO^-oHv>K;tYqvh7Tw+=Z*iwU zC)jVy&zFcHw5|li`B5yhVqmU74-`(^- zM93;u(fgN&l7~{y$KVM0WlF6wej&5q{dit(*hV1Xn7H-dyIWb~^+%w@idUJnfz~LM zGSREiTuma;Bj|sZQ2$ry{td*oCx^(su!Fa_ef}JIf3{bcS@FCekazUhy+h+56D1*~}aiR4{3J2WdJHO!VF14M-r0Hxm$NgFm z1^Ljn!Q%grtXq`NT9>DTG|UDRZL%iBxQ7zacW2K*3 z5bjY(Z549={VnN}BK9ozQ@UBL<)Z!Mm|N8o^?!y)c_`K*)Tx9CSM?ffFQw<9MJ z%lq@pr;Fg%o8@*ZZT06?o&wMJ2JfTc10DXA6wpE}Ho*SjVEwL0VAON}@y6uEA1Y*$ zk0BnUV!TwD&wheeEvl}|+=U*%lXs#&DF>q2er%_if`hW2SHEdC&GZMPwjIX=U?dd$ zX=D87;m-iv+(<pLonv-20f96E5&th%ZHK9e^L zo^E07)^8sBKYW4J|NAiWv19n>b0YWm1N^$JupYQrqL9k@4_BUF^#xV8Sno}n=bh7Y zWf<6yw&f$?x~ogu{h3_3Uc)H0q|J3oQc}@#tgg5}T*3s7M_E3=0QjQ6uEV|F0{vTr z-%-X51oZE%hcVCgQ+Stk2Vox63^ARzaM`s#)ZbnoS3ccVKAm02OG}>wV|;giHYYe0 ztUFhrIYBWJMJ7UOQQi$`z?=-betY~mkMNKRxGN@Vgo?@gOsh`*HfFcpyMy(802bwS zky~Xm@X}#p>mYjl>8}0hybt+?%G5aA))h6`G0q|~dxylXWz9_x&@-f&RB1&`vnP;V z*3�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

    kt$1 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 z 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%0D9K_hwUkLI<-b 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∓bcZ}yLiJkOKY1Xo_P)OcqNlU_UdS6E6R*S!rH zTPh(T2--6(BCGG))bvn|VJT^h*)#PmBu!tKqUd)%qn6o&_PUX$TrFO<%b5xL&7Pj zU4^aNu*g3)7KxHUZ#8VtP?)%Xdv;W!by?Jqt5dn3pOxu&B(^UO>N`HabG;9L|A)1C z-L}d3rdh&bs-JkoC8T3{AlErKh&sAa4))N1`1mdER^=Z7*zW+*k=ya;Dzm0NC!0@t z&S%FFTejZIDM@lpG0use13%#gPLktZWT5iz!UC3Oh;L9kScgCVmm~avp<7dY5o+}X z1g3cYi*m_e;V2-ID~uh;WaRIKz`sEEr8zVtE?!+q@BRXizt#irLSV&+UK#$$BKlX(W{}Gr%~#QjbN_(fgOwe=e%a)5wVMa z%J-q}`VMuq&hhl)WX`>qrSmPM^@ZHj*gNrEOULB)ztEa6!sRPSC+?;!Wefs^eW-;i zt0+E&7SpqHQ|5JjNHM$Sh8vky4 zw&qMlLu1?xjGaR)l9ReN^e|?_Y%gj!)~AVFzWqY}GPXd_m%U!armFew$Jv(?5l_*8)Zuj)P+JA=m=R9na z5mwdKUf}j%YW=;lkj7P^G3E0z6-$xje(zM9oN$2lYC7NkKebH1>=Fl=jNZJJYz+Uk z3K=XU^jOUFw=H`8@1MM4e8bG2vWl&{_YunjXsu#h+!*}=h7t)Z3NytGbtQq(2o%cYq6+sJ=;a8Ct#=t}{EJO%~^jAR~Nmh9)RtRb1<=iaOm3emFHZ1#G;8;pc9OZVyF;laHWys}b3 z&}=-R4x!&K@f(?bp9#?z6OXc92eBft24s*bF7ea;^0|3(@M8pC?X+m>_r2DpoZCt z_2%#6AN9fNe~LT}C@H6eSGD12KEyDgck|CV8X5lET7dA$tJD4FaZ11C#pG*de*9ze zqrEsfc9yN~M>4u!bz&NcL>)lcro&T{JKa>Nnv$Z@l zi1_16v$|kKH)BeW1TcUErB2Su!$I)z!9KNQt6`gYQ@m}IxeD)IUuSklQYLXQdP*mq z)@THboti_GbU%Wk9c<@nWgx55EMPk`Em{HJ#GvrcdjHR-vScA9ArFxGjt-MFk@!Dh zw^X{wUO1(~RcFkuwMvMDD28;T!bbt>e4f_{agwU~Gi|6{p*HwbkRWkq^j_zhB^?+< z2$f}o{s2JsjdG*wKGL=E8B>4Jucwmx-Rw z#=(s6yNYE2!T8cYh7UYfWQly%;qkU{Lu{Bma26^2BFmpzO&E3Sn%Uok;{xmdQ`3_I&)@PnnBn#-jY+YZZ@<-?*V|xIstd)s^vyDbNebLm%JV zNVCBr;{F>%LhQm6~Joe(?cx_$Mj-#^tb#JHd9oW;}Cqd&d$C*eN0z> zA|9}C1fXO5J@VR0A>0CbJr-~C-b94B{ zNjp=keQVJq0pp>LTOTqyEQU$*98&``C4(p}o|IUfM!H>3rX6SSYHb~yE4|ekHG{@= z%)CI+Twwg<-rfO$RB!8- z5e3)EE0Gx1#j_pe%kkULqkC8=JJct%qz?kR>h}Vt~ULe7A~OllNoF3cRoU_U$|5R1`tniBQIk#s&vao*-EjEZX2Q8=X?ovH8c8@p=z`Sr(l1B#vga7RI`BRI7D2@TXiiMj^RT(;)vy|y= zk&$2j-!T_L*tWK|{(oZb+V=MI*ueluRY+D*QBi-!^df-*2(^M^b91nm6o-CQ=wjDx`O)r4g`9Ol)er0DhUPFk{qmaX_$yTJFjwDKtYE*G?D zDt-4mK5n#DKGahA~Mb?U7XH{#_>Wm%AWHneuZd&5@J6{ld}a d=0)!lyi(nAIQamX_%p~yMnX{>C~ElW{{W$xUU&ci literal 328462 zcmZ^~V|XA-(*PRVwr$%sHnweB8#~$9wry-|+qUi8J?Ff7?sxC}n(3)=On^J4-*u_@mDNiVKeIlZ2;o$h^)kGkK#wx74$rtksoaAPDAfyM#j zs3s6X-8&fb^NlcCO8~&s1%O=r$99V&wF3h`e{T;BcL1b!Z?R=%KR)|>vmSVSIMV?r zF!VWeI@?n9mBd-^M+{&Bn7M?Qr%NNMA`k;1R%aarY4CARIy87Y6_e8p;O_DR z^d@lPH~;|@G8Rsa9FP$>jf_|Zw8fDl5KRcJq-Bik5@v?p^err?%zL94^tbw@CEVCC z?-w$2W`a;oSDTRC;Qe$~gd%_c-iZ0eEu;L^W@b(-DTneNM4(1hZ`L$g@li+<1r3vK z1ZY?67lKU6x9c<^>lT}4gg4x87ehL_TkSM~IK53hq8~b0=dKdw%*VgUtjv`&{gh%U z0h#F7*&E3_%o%PF@03Xy29de{aG=ohisxpbLjxIa8H4U6xtyBLR}(#+wj39aal~9h zD^(sa{CtzR6udj<{CsI@GBDtdehCM?lCJxEqm)!52t-kWOo?p033Jn9AqZQ1krRmF5IG zdwbuMX$+vQ2I$XwY!yG^rmY7Kpkvcr%pHysXb7;@-iG9GWDU#v83-ss0FC0$Gy}lZ zi;xN=tq+X@2<;A}hXB$iz@iS=N`N>^3sxaWw*mkqfVcwa8VF?%fCJRDgTVo)*{flX zs13NeLv{iV|C;~ zF;7+jQ5i@@06dTD1pNfY8L=JgQSdpB&>Y1R+$UI(0aMj~(ZI(YaUw{^fS&_K7mmEw z!X7RY-Y#@}*Ybs814b{XW7pRm!51{P03aeLzo1c}n1XB!7d0qCaIj#Y)TLBN8M7Q~ zG0alfCC|11OtD5$Z$_O3c2XcCA9t4T#GVB`D?~SRE2LUrIX`1I>ty|e@B!nEJLIQg zf2uwXgS89_8DImJl0Rktg1(#)ozWX3acZnOO>zut@0x*6?d%H41=a=I1)nu?GwymQ z?oiCWJ%>pqX=@d+XhgdhkWVTOVXhm1M#20;`8S=gNja6U3)RI12^01Y`C5|pBppK3pA_FmVdl&<)VR>--AJMd-sIUt)(G3!-^6aoZo$7qc*fwe=OX38H_TSKX3o_0ayncZz)?er7joH|uARYuw8=!f4LmZY;s*%qYWL$<}EU z#A=8|ihar4ZUKY6jzNy4hPBNQAZslPLiR56hGvMiMvp>Qsmq}+#pc2~$zsER%T$o@ znqk9Y#pu-_;ifo|pB34T=EL5?D9^}m{WbH`a@VSEHh*$2tEi52xqj|026b?3+_Foh zVo}Qq*{b`|_uPw(B?BOXmJOF}+xpUausOQ9z_s1A&Q-+~SO(OT!3q1moOftz(zokV zbPU6Wj^)-{Y-85d0~4+?_wJb0Q?2)0?Hkw z6rBRb8WKqGB3DN+HW2pb{7*lfpE^jLg^r^Q2$69can%Z%Cu1{jGqDD?27?Ch{jmL{ zySRw;h~q?OM0}#2BFQ3fVzd#O*o(MTNVH+L(Z2^p1{MaQ=pVJ)y-r7%GE6|nujts- zvztEFppsB2si&$Ebw)Kjn9z{1F}3iyMFz!f z-RIZmkJ@b5Ec-RS)O`$>UEZ#HMcPC@U-w=iT(x%0o0!>zdrQiV-cK=Yuq=bM)$Vev248QyicX1!mNfC&d#z^&l1_;a$kd(h|Du5F{;eSQBaiTi?k^E2Sn$F2W*3tOFy!9CXPdhj#x-45TF zFV#Ku8REY5rfy@up4?Kt+>iaO%VNg$W+QG`VS0Y11U|H_zvfHg+sKjR8}m?O)oJqB zg`P$)6Za9T>(%jeJYFqd4Y>yTEAhMGEP6LX$*=C~3^Ni7+k@Jd`_XpY>+o5ePu=eX z10K)sj`4ASprmhlEXS&cp~u-L`s2;p@R7u}{v%_yrTN--8(+3aPBxswa4HH_fDm$c z82IK20wB>Gz&y;3Y;59fKh58i_Y)?dXI}%J7r7Y#$5Ol#;#aqX>LFlmC<*KfDD};jos*)D%$@!BU~MiW*v`>}fR&Dgj)8~| zihzKC$I;l7>xYQgzsUc5@e-LkJKJ;7)4RF3(YZ0x**TigGjeir(lap8GcnQr!Ju{W zuyrz+%5h)lC9If zruAon^nXF<8R;14|G&tbElmG^$o_)-L-zN){uz$vugEZj}3HAF0IOl+P0u*S#E z#K`maF#iYgUq=6jQvJV_9GvX`OZh*L|DpVg6E1m23zI)W`b!jie@6R1ZU4gOq5n%x z|0B157Ul2OKdZtA#Y6v}73PCl?E!`b01yC>6cJQ*2fWCF7En>?{_cUGAm|pA#VjLH zLT-$Qk%Ba)qP_2n(kxIoqy1f`j5XiJlyDj~v6$&CORS2Xg}qiNb0;oc+=f&Ji7E#b ztp011qAd4EJ^{|bx0cm))9SJP)NWq>f!(P=&9U9*lG$~`^xNg*3Z2<>W>rO5SwPX! z&-QT_d&F<;gYCBhJa9*sJ-w_C>Q6D8;0d`A2JZD*i3aqN0SMScy|Fqp`T}FTb4%mS z27V!@!$dt?mCi(I!1V&DV`5}|AQ^v120*f2kW_#_AM9{|i#aX4RIn;NxW@Ql#)46zxL5(}s zW7XfTwdH1MaksX$<=u=Rwq{cB8KgRE`XuUL2IQBH($9$oPJT}~^wngx?V9##NRqLM^31{)k${S?n)@n8W|6^(vV;Vmv0>lgnwhyP}`njf&DyQ^9*D{?gV2UvVI z*(F4RUNXB$%QDeAm$Le*etuNFJk0Buy;A+8_#HNrHFaZ?yfY{4K{=PG$1n2vM=lsI zu|=a_t6K%U8^&3lrl-rnVPKd7iF(rndz0UdM2|v2v*cA;!us>PLLL?MQkA}m(MoTn zjm42EoQtA)Yvi$}n63vAJYeolx=+mik>Ef4+3P^0zTd`tEJU|BiuGjgO`23Pr2$dJ zaz&7s$WyG&g(w1cRWc12b2h+{^)z~oG}k#6J3DnI65pMjs=AajHH*jV;JYZlsp(V_ z5i1xgr#)&agYi;8Qvpk`s25rBRnUzd=Ij(Z=fCJYj_tmc4ETo*7RT^W=ev zr?RV69m;KGk%80k4uJoU_Cf*9y0tKNR=zz6$vb^#f56VEXidB-XVR@}4_&CO4$NAM zvog$0U}l`Pl*HO-@W$4kAM~b~*OpaEC5TdP9lq43i*+wd-ihp>fQOdsppWpnQ|iV*7Q8{P*L3;B(1!BGwx;}wQbQ_q2yoOB3XJF z&K!!pU=|uZ7ZCd|EwX{se)~dk{rJ=|MmL8i+T`&hx(nAcJW$|o>}XbLaQFSq2F68uO)BEhxUJUvTL_qvdOnTv!` z&dX7dpC(uN+SrYfIN{I*dt_3*jA#0Mr8PoAl-Ri%_fIH;@~m>3;(|nB0|#g3 zptE3Qbs4}*%@5cO&*!;9Nx*g#MMPF@jL^*(+`;EnPJ5pV#aYe%TI9V6Ihk)1ISl_4 zdObFBOt3r&WU<^-y0r!~`SPDj0CW7O4}nxKv#T;V_Mb>P9dW*iWV&_+#;9krY^8l1 zJocy3oI1{i*Lr7$EP_(iToEF>=;Sh(C7ysD3iV9@-zI+>z8=Z}I2nQ~HS^ z9UtydD!=uOdf;8o3fTW*X#QETEUYkJC6<=93+XU;K< z#yRnWBJU%P2E8)<75R3Vf+&iL*wpZX42EoD5K@e^5v}){=_ZRb%|Z$j%lMlFGGhp+ z{>zOoV0#Z|ZD5I`_}=^D>HhKY@p8um0Zt@}2ZVVMKaOwUcDHM~XaVBbHqQtYD*$fi z^U_nAHT`C46J0J!V~7Zg{G6oYonyXOCF!$ZiXN^A&mcX`%g5D1@3KHnIk6l3f7HBn z3B=OM>L9TH>`dCZ(5u@y+uivL#9QZ-*e`=_d2ufz@crgZ%c54HJwqISV4|=H;IoPK zJn4}1WqpYK=J58O2vu?3bk{dsN>fcO+Ryd{zZI?xJEx?0&b+{$_x8h&7rX~=i|L@W zQi8|hK+m1of_cnjDkdz-1L_&Jfc2hf!tWm~&0U0`Fp|O*9Xw!vzK3P*-6&yy$ECEI zG7|6h7?>Wj!^>(=l2bK{Pz%W^ZdcZAkfso77M`|hlr)`9WL~Ea!IAoLTMrXFDVBSl zx$30LmRl-Ck#Jb!C%TNMQ`-2#IZwp^BY3-@LM4=cS`@gA&kdLIjY041dLoy@N6YTZ zXr>TRdYVf#xwwdWczAd+8(5rjwm8OQ$}d{UT?dB8mn8{1CtpM56pHwG@1?7hT@3*LBF2)J!3niZHgfkmXM5T7!ROkCK}z|6T>SjWcz#N5?**^c zc|XN{a>ftj)6q2idlPeH;uQz};|}w~+nZtx`g!;J>k;^>$B%(d?_|krlGf>Yxcb zD3^IW)P##(nkInd+YnzIS!)(DabWu^xx}S{S)iDy&&yVXI5Gb1(0f%#cFtyTZ^wOO z?ch-fjuYf-UFP*%PHo+JT;YCxc+Ft_rg>=NMv9HXBAXW)`bN^_{Q|Y~l+bus6)!D4 zk8afqDv+e8N7UgqwEB_^x*z)$Xz%lV=t3d1mG@2dnrZ9v4TkYqX34 zIJaH0EoAY7qubF_5oS02kgRY)M~1ts*o zM!6-o+#t@qwQ&d<$M7zf6x|@>=-*RPp|-&0P7Q0C3m^870fBEzDK^J+pL^`3Q<54j zG2QM4OG*-d5?=MEQ1~M29>M~I`*HEz(bNSRL_aFQQLaHB9v%{*Rz?G#OjYFvX8mr2@b}&b#VBxH2EVyDrGA5H6vx$vklBl;wYnrY&R>`wx4;waT!U6n1m+zA0Ly6 zsPN?9z#}WcjBR4d-D|x~!Fc3s!d=R;UeI+HV~w|7Pn6R}*KfCOimED(RCrhZRuwIv zm@q=aCrE5h_ATw6m^G)1NC+d?)T@Xkwnj}f_3}2_X*n#fwJ#SOjOc#5C;Ey7VOA4X zT14KUi`x}dQhx^m;p1F_g)nDG&zRO*CCD$l`Axc;r1pz(e-!fO^ddOIiM-a~5v<+m zxkRxgrj?roVHDG^e{0=qrv%vh=1`pb=2lJocH@BVWIYfM=*tI|t;lruR{SbBm@QmG zct{gJ9f)fudCJkuqD?S;#UMTNF0$ZSf7rwe-tBjKliy$|B;(5}FYs*4QbQyYuJ-z< z5w+3Y**{)I0I~M+yuy9mi1^dn0)HjJq#idBHV;M&G1>Q5)m)UNiqDxjN!&1rk_a~z zWFwX+N;{=Sd1n~%?3n3%KhcybeoB9EsO%_HXmMDoyyuJ6LeaHAEV8kAI3jyS%3#Xt z6Zpv@LJIgBwiHEyyfRrPCym(aKry}HJ-QctBs!RWi|yvl=`N+q49Se16>Rj0FnZ6x z_W0Hi`X&6jk47g0fZx_VB+OI$IE4}DDvjDJs*Fk>4%@p57yiG3v2-K~>MtZoE4Gsk zdrK9UJKT_!67quB7Ii*_@UH9b=!Z-o9eDPSz0+d^rV+Ied3;`fH9hHg?$8K`9zuiE zuLI(f6U*P+JzkJsbP5rS90L)pK-0%*v; z9`5kN)8>5xs_~xmm<^1L|K%&Rd4bB|=}FQ*n+=@!w=R}h6+<#1(m$CrQmA1m@3e4L ze07ScteOJ~VR&_tS(oWQ+Pzg&gH1Ts`JWCHJTz2Oj1tuh5@1YV+EGwY;;JO(5`l=A zeGU`|adpO~X5KLzcO4n<>9+9_h|X*u`wtC-6~zmJU3k3|T;uC>+uLSMdqxOhcyE8W zGg7H5DG@`rb#zD*D`BMDKERQm2H~|cS86~}4=&bK94RO!&UUpL~kB!+uIaAi!BHyGQ|RKWYCHVf3iZy;wE3 zP59o3t%*nK%|a@sgeb-{_M-7SPmO*%%MvEz z;HmmfO4(K0{LGi*C4JhQ5gGh`2o%(Fhjg_RvbzyjqX*}BiU(Ebdl7XvQw$<0Dih1j zF0nDLB!Dg{R21=5d)~ea_G5=LHKxVc%+h|5G+m6VHoYt!Y&H z^l(|&pKK0arHl9dMQj5*uNc9;$`kS_>1zYtp|`p|oHSlj0%DSrj^= ze@-z;VRWZ;DsLj9p5s*}%|KZK!(Noh5)#CKb*ki0j?|CQs3H@!f&p=k z6pDis!>fs|Q-)-A81peBstNu7J8fT1^S`Lbc)ux(ZTsHJjg>z=`b}HZ-2h0{fIFMZ zw|T;J0ap|mZDV+1jBJ;P>^a*5HZ}AoPCvn%ylha@J*^&V^Lf8)QFXxxPTW&1(-c5O zYffNwE;)ua~R#w*1($e0+NOVOV9oR?R zp{3HC^Zyq3H063bfA{Cy12<)X$X2NjQ?&j_6?A*D$g#A;9u2_yqlZJ|EYHF&#CVkUGiyX|H_@XA+IXWVHjda>M zJ3D*evHyOCGEllb(tf8ci;nJ1RGc6jhN&@tQo86-Zi)AG%UdI0@gbx`x4&4&ioPVE^3EV-RfE=XJZniNWWO z85@&&d3lj2n2}?%0MKo<7j(_$cDtg16|dODzC_(?B|}7j7a8y$dc8kB%oQ7s!6l3m z@`|Z3^HSDCTVAm0A)WK!GM6r8t&73{L{aq?5CFjTmo*;x4{IA?AeI*AE?CAww5tw# zO2i+=AY-*0X9kfH=mu|LY6M=5;J&r3`wfQgxTdqJSM(hG`+J1#C(_}3N&1H)ko&?1 zr<)xuEC9HN`!C6e2nBUNtMqBqxgEWExrydKUW#?!CsQV@fVBE5M@ekt9r?QF6fw zOHvn+A9G(nvVyv#36dcKs&Gyz#V2xL>kFyGBqighMY`K}h&xFk8rQV8#?MR1aYS_V z(3c?A5gMLjo*v334kuv4z9ttGLn=Xc86c)e>+HR(adD(3$wI3fdlC|p-qNCa1Zl&3 z1mT=FwuGF6A-j>1HIM{~(aO>}Wlj%jXeC5Ej970>yVxu;W`@kW6H8V}vkFV9xP@{L?v}4rEX0l7+N{Y zw*jA1Uk;t&q8sKfG~52(YPqyX2w+)EAoTO4xhyw9x8%{hnvr6}{%BZV^DsgN7?!W1 zx{M_1u3L48EAD=Iv`8IqT#s$QUTHkrey;2A*u^%hYw}gLXEjZ-&6)G zKpwjuT$A-<0>c~N6C+hB|H^-1cG;qCS2w&an`Vl)%!f zfYc;+gmJb@5!WYM68b06y-J4@ycP}S7)$z{`$QI;3wL%r-_$kgH5$JCe1~cM$q_ng ziG+!4U($K0thIC`<3s#j&`)I+eR7jvg16hyU1@mujKT&LSTtk0Hh97W)4YiWdI?1t zh+E+g*G&WxVXE!~ezHkP(b)C(y?an*guP|Rvf%VpHKk^FjgEA?&J^GDD9-apk_K=e z-bxlrxm;^}2W|zhjs&g?#~$oqHQHRK7XT(H+e$lEr?p9szYXXlDM1t$BdBupm84N@ zACd2h{F=dH;S0%2}9pxbcj9bg}h_c;k?{FSUTo?v=6-;nFa zugg_>H$$r&PIuo%yM{-HC(zlTHiLnF=m3><`&L7?pQ8|EJinqJJ!L!iIYTIc!4yw$ z_|}s_X+Ch@<}uF+=7x~iQwmE{_tge79BYCFc#aF z4;~8Y(}*OhB|PIP1}{A&Xv7G_S)H0&H7X$=?Pr&rSxde|{i+^gdFy$`y9 zxdqvv>x19pM+2ACrd1nZm~hWWm{scIoZWZI)0O-MN(KkDBx=E>Ich^(9acEvOFWb-*0L$gO&TXKxrv|gTt z^S1+K3GiPr|7Gfr_1+ssWJsU7+tR+nFT8QH`O5WSR*PLuxqrO9G|Cb$&Y@VnH0sna z^bY+)X8b!W)YC#kLK=GT=H@<(*_ZhA_gw&sbk!{Ikm+fVY1Nm{%zc_zzT;jUxz>w z(NV!IWl4*!p%sltDW!-8k;?`K7!1TWDkuB)Qkjpl$yqK`!I>u;1T|=kr@sk3BiOH? z$caCq&}gP~MY0Dm=wSqQy@hU!F-W~uI*O^{+sI-}VMPi1M4_&J70xM}v(j=<889#C8|@ysd;ZLdfV~Z{F9|-qBH=e^NQJ88CO1(* z<74_OcCQZd#{#X)qs%c}E5ShQY{-E&ymsrauA$p+S+_hRRUt7Jh-8Jj zkaswIX)j!j=0S7vBgWBDA&T%ElB_>pq6? zR)5aJv97Bc{BgQcTs!%01g*zPtf0Qyr=rKAcjCjq>4AtA^t70>re)9ILn{=0n(rVn z$!oz-#g!dc#K=2a5=;N~05hnWE>+Hnbyj3iTw}cz<-z;jyW9dV%N{L!H2c<$lfpWn zAmF)&7~Qrn6JpqP zwA5`&jKJ>4=cA$98^qABTQ^k@8o~b^CzgpfoOQMwIAz?!J?gv=jnfhQF!JT5gdGRr zM~^-*tJ${@#Y;hT8q-=N%~;HjDXM~QpXx{x+1-+dk)vO^ksMS2dRYiw$& zknuSEB$JxF4A|9ov(C=0I3=2z(5Q#fa1G=7Snv42Y8!oKO&xDbvwqSgq6Fri@9sy& zMiVPc5e-A9#VYu<1DECtG%VL6K|Gb-fZ`XFq`~4+nqV<06bolxGo+VGrc_ z?pl7<*814~N}Ezj8Y({h<(GH+QUf*SU2`=H@)u%*LU&a4rjdB9QxiR!3{;=q-6^!F z#WO5ks7iN5yw3@Q<9va|njHvU!f`oJM#*zONqt(2=`o=ld)Dwk*~znZxi`X(`~DVn zelsN1J+%>DXNc2rB1#V`Ch1_HsqwY)5re-nB>`y&^P!w{r1C1zOyr}G*jU{mz2~`( z(~%M6;jV4%p?mn&U-1-oGoil2R;TJ-ywL)dwI@phbXpQ!;UYXuL$}w$CbxJbB5gD8 zI|INi0yNvzy!O=jtQT~<(z&Wxi5D7y0x1|5l#5(K$$OU&Zs8SrSF=bXHr-L6DUDaP z*5T*0z>GhW=N_)fLh%)Ro~R2+m*vrQ(lSHrx@$yjP60c+y-Dl4%dWju@xC#rQ+lF} z+B^5bnQqL|P1`LOb85X0*ZJ2hiq*U!XP633eUanvYcYmZ`M_2q3r}+$0vz79;1|qt zc~Oym{i2AecG5H>#p@Ufy0i- zL(U$KY=0;?jYMEH;x$yW{{77}10MPV-Ee3n4**jDxdXQsxqsN`x)F6H$%lW+yAv_N zf{M}=W$#rVUO96u+@!(HP;d^;7@qk3Wbif>2S3VgzW8?s&#>fbw&x{VXoKEPAg|X> zRdcJ$bwGSU=xBTiF{rrxELlgZPca`mibEN4d}bZ?S2ECtgEvk{wpHvtH5rER3~U4| z)+|1vF(}y=F@BP~V9X2eeKCILdraYN3+D*0DqziqUtFR&%#Y-txU?QT>7%Rp!sBD% z4pIE!S*2r!{LyRT%6vxp-YsMSS%#UqgqjD6XWAFP>&|e= z@bqK|t*{H)NJJ6rDdYIe;dL!(Hxkd!Owtwc=2*wqg)5gfmMW7VJS88%Y~W)3=X!Po zE^3H%@&$uT-dR&uW#y6Z;?vCEz+$lYa z%dm62%_4}srd=!i@I)*S%I-ZS7UbBMbRw>vfn4`f4B}5)WV7A1wRl+@_@VILH1>7` zGG_JjwwKw;{^aZR#5)80PHAb*1uMF9*-RKN3NAg?sKajf{k!-X4%JNG5-QrYgk1k( zW{c&^3p<%aoY-c_ib9yaDq>5BJmS&d>6|HdiVNdd)z`l$Q3qbkGEq(rLarFPV$@$kTm*{y7Np z{w7Ks@aIvG{(S-ZDW7e@XdCYz0E$N4`AL8lhE&6n(MFJf{7V$gY^%2eB$?)6$NHy(4##Rmt9(EeLWNl~;e_Tpviz@9fZ40=LUaoNe<5Mll9Q~K!ct2jabEf zyfC~6V0BLd)C~R?u$FCpxZmBjFNVvmoRf|F_skC5-$QBDwMdn(9uPmwyXUTS=rKR( zXW2P3{V>oUGWj#53PNWr!gN}Y#qlmeH~q4sF&CBvuQbk-ws_EQ7O^28@P4A2h{wg( zgTZ^D8|iAZ428gi(np_kbstk0)bxsvHm=dSLMO{%2XfnD|AeIP4o^g--jT+A{Moo% zn=&yH~VP!>&0KGhfDz!HDe1b;A3oe3U7Gu9i49Oy8N``i7f1=_z$Gqhp0zIeR< z2F-dPA_%1O<8J&REoxNpT))GjC_aTJc|Xk`Sl@O6EO^P4qR+Hn*;Ic{)0zI&|-rfxzdvWF;#t#NQ-u$ov-#S~J}HiD?2k z9}AL-+bzbDsF}gsK?{qn>zRe*Lh$|YsMMQaMzSRYL4R{W-v$^-6CyTEfQP1NW$#(SX4Q_M3-S{hmceWH({amXa<^ z9Dod*EmLxHzFvR~VE#dI1p_x9h`?#VnTyVqafYOphY?w6*;rYI;PrRXNlT)i_n!JO z)2kMZu~`t$TPGtJ^-$KBjWpf z%N4?#0w|WB4-MGvG@v?*Yh1(es(RsA7(EK+6Y}O-zA3(EZGYqw>P7Me0xPprWNXXL zKb!b=NB(oRY`6-8mG0+Q;(h`UC+?A{;D!V@ZDYrm`ot&@ECPv9qsPubGl`4%z>mVh z);bJ~4~O8IbVPNBJ1d-zwV%0lN^!M&w24Vs1M$EewdRI^ zXkvr=1kpdd>r!@HSl#wL4KErk9+ZWh4+kDvqgkMK;J*dGS|68oQ(KfGi{GgQT>Ai!(#q7N9zNyx3jGf(-P)-n7D$RiT_9fHLqcm zie#|+tEFqJR~0j_;&OK%+XhgASqJNMMfSU)a46fu$W?8L1DnCj$By^|AQ)tdHAkD8wWuv(#S+c~cR@{A{_rm#2Rpusfb+2`ST1$Kt^( zba;D;N-FocF3bW0?y}*wQ$#x&q%LPp346X-xzB8Wp*Luw@6Z0M_f+Kj`$}oLOmDs} zIAIhY)?xyEWJz}y!(Ah;@Tr7{F%K-T=3$BVvWsl)tkms-=6Cdjom0i^_jJi4;hp+yd`s*`F?H2 zyvp7S`}T~{xNa*u5fXEDqZtxFlJ^6Xg??x!U)mmH= z5NX+vJ4~jZbK|%4Sn;KNU!dIu-Q!)tBfrXc1LqArHCId2dt7OkzD;Pq?w07nU%e@# z<7yK?B7W!%aA?LLcbqEh7kHSN`)+=^rH;%teO5@ET_v+v!6PU5vz>NXis{cTA&3Nh zu*YC!$+0^>;)WdibqW0}JKYHkxZBcSBLx}0^~C8C`>FeTXG<4mZfIX1jbWrwe)q~o z!h=i1byxIzHhytdU8BrDi$A2?b1KLEpB`=RfGbJ|cTS`dLg7U7Vhp1$0GCDerAN3K`#)r?{ zoYf~!HClM$*W?I2(SB|rgcloqywy=({xun|&&@=Li!uYEucCBZnqJfoti@4DofzMJ zB#Q-e)}dKV?%npe5`t%9ODni1g^WeBp1|it4=@>Vw#A$zQn8gYSR*Z0si|z2Jfk4-R?x4x3{|`gi`?SuxF6@k~y^={9q<*vfcN+i<$UE zrdsWt)J|rEz(bViNl{^*wPy8j-h=^v*0Wl*k%^t#Zh25#yjS(SsEbzktAgAX6DK4r z;DOsc4@zaY!CcA=&zrb-XStES|Kqi1NP<3Ht*9D$8W7_Vc_(8-zSd3;4;DCN$JIes z+bb9Su>W$uG>U?pZ7aKb;KVrMv(5|qRF}2u)h(u^L@?59Cp&zz*5uY0=5!PCIMy$MKRQcX$Z)nl zFxy-tL9u}W$_m(ZH${*)?)eG7mcfPmhwheu5>JUz_o%xqWi-)1$nQ@Py6niXA>DK! zO|%1R8T3}8sD}C$R`Vbh3hV){So`}f_;BNaq2Ld^u7;f99XYeQ{X>Nb>1fh^))kLk zF{O7R9xj+j^N{nEjwVDz#ew;6UC$}8P1E+-CA*2ei0rPimds|TeW6ld8^^jv%wc>p zcD)n>-a(0SM^+BukojE-gT#b;xFM?!i*oA|nwJ{%A=s`gLkBKj{^p(s?ssIKciIcf zZ*Gx^^azys{Q8we?mELKbB)IJFpkWTD}OU#Qv#wYKBa>`6w!0&EVKeUM-}>oy^{GeO^kY4!I)@4=My|#VMDIM8IEx@zR%1|cIigsQX?#X; z@mW!2h2Q!vTzyA|&w9v=z0qj+^UAur#d9pO4bWOyk?^Oe*jKm`4(*td$C&>7N7emr zxf(Auld*Hr*8Xi_(LGi^oSx6xLn z*;DH-FN;roQJ|Oj;)z`6DH9aL`<^^I%K3HAh&cSZw6dF<1fLokyZ8wG>$-w{Fo>m! z*=F4SPtkSy{b%HpP5+btcD~jqGd#yZVqxu@j)!r!nji%1{sl}@I}rIB?{u_w|0OX< zlZTku+)yJ*1~_@rjbQd;K8WW%93E5_K#d12Ep#3LTOV-)7j?d)85_^5C3eah{I9?W z8L2`Q_4B*Rlq!fe_UQAUgaJCYDqG+ZavlC*UZbfe`oAeXKNp{BEO%T|@OdngW)-6& zuBY=4?)`R@uy&nFqL+hCFJ-eL2%n<|z^}a{ z6diBySH5n080+l-J|>X&ZLzkBLo}fn=@ob~b9u?cTn2ryFxt%Dsy!cZBM={TtkPg{*1A#lY^YQX)UaqgjG1m=Xzi>b zep6a?GJdztl&-6EP-jiZ4n`X7*UoP&IY65;hq{A2qCA;mc9rttF{_s22-ljAfA739 zx{V+WJFyv#xv!-u(ep>Jg}>hW$p_NA6tjnHMciA(?z|f8P0U6#{9Jah!lBLF=YGHZ~JZDGv;L&A4Cy@@EOjrfk#JR7fxl=R_Bn2n&q{;t$=q*)-c-0~c!;o4)9Zt+EHio7d537b!7kFw*;62LccP6fvYl?e4x53aCPyv;fBqjLh)tS zG(NUWjQt4b28IVI6kly{=aPNxf+n-Aw)9FjVkv3}g64mJYv^SM&gq%74Yh3kA~<9n%>5!K%T zMn?3wz7%`W^cpzNn7CT0GrGC0@|x_&AME!% zTPF{8>l@yURID?P-?-FHjM8br_KU%$HQPTV7oDwWHNP*2v>T1SsO)=~47-OKF!(g+ z{?x3It!(($sj32{f}*Y*eNcg1CpIPw9xV5LGS%6EBK>qUmj`Tu=y7~@_{@yLrEU?gIu%dyCM)8s1=UZaRFtYe`8NJnCpd1qhFyeXxNq)I_Y0BO^CAq0QaqZ6 zRz)@%f*E=-J=@?^LG>4E*qvu)uH`>z$7w1f-io@b&=5^nV>YQSfLi zr~iNGddq;glC5hPhv0+&!9xfTAh^3ru*ThjJ2dVRg1fuB2X}V}+R(VWI|ToF=FZH0 z?!E8#qkmP`soG^{*IsMwLk$12#(YS+0Guo9)R(~Fle#V%2l>lBVjK}Awcb*XbN9^6 zAuTisoG-e;RNoLg#kE!3~Yh7xL; z(Zv`j4YZcAYnozP!>u4Sa8xAKoSD@PExEZp?uZ^I#5;YNW$zbxU)2!f+|!` zPwC>w#zg&T)A_tLh#Bo4H{EaWi0W020JCRe>zsN=MVy8^Un*uhL5+=4S67$in#so`r111RAr2&S#oPS*gTY2tDO*k(p*%rj8f0K z@g3={xM;Gd!ZcRjyc;)KB|uZS(7s9(XSRtV9auOXwSL31-l#@udk|35;z`HO z>nOo|uhu^qTA81#B@~wl=}msQ*{Y6Q1d{i2IG;3%sTaMTauP-TcSZ| z{wsZo=@EaK4~5+{g(TS0qm*>~R5H9pgjwXx+INK;GxcS}Tlz`M3#LQ*4 ztY-%$9);`m`6Bf5RMFdVqGohr?#VK)FPz>_O%0tt^1_MMKEfDGpm(b>;+7M0bz~C9 zn0tFFRXy)Y7it`-Ik-$Fem*y(CwI<&E3#W8m(jCOYpS53@fCnc3_!eRs$FmRo4aH- z3YTabYEj(r`)lJJr%u$_Y5{>CEW@Vhoz4ItC^X>vE%6=Ru>)51$zpw;tTOLZyNAp5 z>55h)9JeMR1!ri+WI*-D2kjp94eunwhOw_$d)*Q2UhnpV%kvzHi1#xx5Kmv(gDRif z#+q+Wr0qyX#j_pw)hJY!sO5jigB-BX)nEM=xh^*Q!-^{_cgIF5H$w2zxPx6v+}zyQ%sIsPIc@<64s{a+ z^@jJnb?iWE)8)cd5zSm;TU5|n3+=$a7Kk7r`G@chhzhM4lv`EB(BgCi9?+b;oq(1*`r>l;4^mTv`SX^%njPK~F&95aPDKL}h5D?OhI^$4B~lCa)I<{^ww)@$ag*tg>!T7MgSWba z-Eo>)GWZLle^CarHU3Z~r6sI!vpnxoh?4Q@K4)qc<^I9@c=OrOYVSS#c*489?ex6x z+|2i){gYHQr7zed!N1Y&e^v%v4oG}+GpQzf6u;%%I{Q*lVqKFyDxv5Zu6=c|xhXs- z2w_gGYb(Lm#dwaZb4goDU45Z=tmdUJ&HSeS4s12ic+klvHa$D6q>&t2d~OG!XXz6A zsVRiAfXAl^)Py{%da*j2zonrlVPn3}^!1cz#Gu`nyM6iK2y_*wo8CHvuMD>o;nQ40 zDt~~^Dm?95o#~&C(K5+X;9AwIeeZF1+Fwa-YBilo!ux1W6PYoERusxI?;*+ptpI%Q zlidE+8%0%rF*p6C zj0AGFQ`PXSc2qCi^6toRR4Zw@zWW;#ET-!>MkI~wWN7z)NkpJ2C*fcW9AasK#*fp{ zPT8nz&H>6>f{Xm|92Tak>lNORyTX7_tD>mUziClFln~Xu2gJ5AyA9rfS~6>3%_WEF zfS-@VrR)aQG&JKD>`% zi!CFQZ~?73SLr<_SmT+Q{nw0;s2HW;#qoi&_tgV?aS`Hk!`d;u!)&c2IZ_(B*LP}v zUW&-|^(mxtXc*I9JuScV)(e>4nFW@W*L8tFHh^GmBV6U_1z`MgxP?t}IS#zWhewQI zw=^a`K~qj{%(bFxkSIV*R7a(`P>`G!zX2kS1rwS>F zM3m$T&R!6Gi8u)1_wxLz`WbBUZbsT7Kl33R&8wz8Y?BF&_G0;{8I5I9)W2N|SMiCp zja3BSSCgG%t=g3hNL0NT%5c*y%LBt1`nM-^LoNss+Jf1N)+1B?+<&_Ryiq#r*@{t+ zjC*TL!tiSdrp5&RBcqA`*3SE#q~j<~%~?Z`w)7jtq!~a^xYT_3Td%jW#$VAB```$; z#VD)2NG7<%U4JLNHFq?^@VQV0H0s$<4hcP zYBf^M6v~vj5VP~is@T=aaBx@)V%0I%*OZa!ix3Y++h?~=C-V`!IC<5a zsi@sVOvd#W?)j*QKB68z+_xI;xh&`&c#&eI9lF*09yoH|Z%{QDv0PX^gZ*;yK7Kxu z^R%D$G%RHpMIOXhY*M#NJ`7*>tEg}@+G?LjJ|RgdFP zB0`ASN>G-G#%2P;Hu?N7!VIb=0hvI`qmzGi2gwoh%$uxLj5u6qt=g;TMi1<0PMqgV z>4H-&_!n+AxGy1fs* zXTVRs<>e~LOl9v6zB2*)9`M!ARdX`jmvrOrGB$!K>P-W#a}nQNNG`S!a|QIDr*V=p z$YLatyWIPPOje!n5^=?bIo%a}6W_OFRRIw~qNgH@>2nb`7C4A&gSq9NaOj8d=}Z0+ zPr@n$-#69CqSn7fxnWQrytHK@8w62aAF&H1|{5bIETC~ZLo>)Ui7ihz!{N$T$ z^?=HnS3vv|>z7=QF1QQ0|tG1QDTH0Z}S_ zx{nOtMoe7re+<)#PjNR98#uqAK|k&i5AWGl@Q3=g_EpUz5Vg1O zhm$%BmilcGs&f1exf{bz{lT>4_q=c}sD%DFdR7VnvV*sFC+>ve0?f$X?~XVw$xyEf zzAamP%QP2hhBvQ@`O1xkfAw)=ZVd4cJR6z7SLz|LvUHi zRz9uXyi9eWg)`Y~y@WENl&cO=9@4ABeWay5f2k4uz>V)f6)uXg){1py&z(>J3F?8X zB$wQ+U`bHW1-p`|AlyK^^S0!(YjV3)XvtEZu;XhZUhsw1uT*YmGXLjwu{>u>X7ib# z^fQsFZ>G2CGNi=5t1GtMU1W|C-${tY@6rGLdnvbQO3+i!fcV5xd z4E6o;J^(JS=5dIN4I1ivy3x~H$q`tN-i#3VgMJ$)<~2F=jd}cTHEW6qCCxb%&4y=< zDU57W;w0uMw*Hhy)wDB=+L0e2!WH!^Ro_hu@&TfgrAE<*Qw)~r;Jr$nWIp!y(BuOK zI})u};(JFCs6ZQ@t?G<}A&+NxxW_v&+pJY1Fvv?vj;*RoK#yF@=v3}ge?-48G<0&u zCAr&6K6KvP6fVZx7*cs9u$S0yD|o?RJN?UG(Go5!wyAz{W7+|Vf}bMur8wF(%d@=e zR;GN-xS2o_3Ke>eFX~`W;9a@tnrSd=9sTU!%;Oif=*AHOk1Lw~haVBwTO0pIbmMYA zKRoV&mGtt4sSdCEt6Zv}k#V;YW?%(Lm6+X7I=D>;4yyoV+<6-PEA708vqAAait~u~|SDBlS`G`V?BNNJsCS&3j8HRXhz z5ygcC7{ww_1qSnt4vd!6WwhM&E_3v=fmPCI4za%{4N+=;a`9*A2^2lt=s3^rh?MWr z1CRq8t#1$I=88#83KZUnEiIW*Kb}>6EespDH+Q-R#BSWYX3%ro17C>aG?t#SSa$G; z>vA}_TT1gKe_*gw#2tf^P$pq%dwAvuViW-q&|iWf)#E|gF+HQ8zrbsbYC;Rd|c&g`@#`yD;dNbAEvHcd-) zo`=W9Ct4E3krDYqA7mF>kr5EtJ`m^V;yp7we%Qb_uVvR?a`1I%c5O_|v4I>Mokq(tE;(WiVsXzZzmZrMcB=(mCkxX0HPoz1ahkKWc77LR9y56<&k)h2O<+{E9T&} zrIL15(Umh!N`sI5bU89R=goSU*Z59vuPKZ zI55I%OKvR#o6}T~>RgGLSJbc?gnFkb@t3}+0VK!;v{04FF1lJ{h6FM{P!KS-oHKQ zsB=BGjaINDGN!`VOfXkqXC^>Vm9D*tWCBr>TQAqJ;z$f5@p?1D0)oWJxF1n@E2a2u z+~N4DWKj~B9(pIqrcmlia#>}K4R?jo-*J0ph@2^3Cy~d7;Df9Y(U0J3qW*TS*(g6q zoR1R?NN{ltEz}}<%up2#9bTMpoQ>t1J7P=*;s+EL=c5rWT9#e&G~$V_u+jv?f12^YBjL`UxC<=;--+R)P`%p8*E_ye% z6hh7l9^LV-LM1VC>HX4_bAc*jFm%{=n01|e|2K}7cr3BR z`nP!d?ainXYu=4Em6A#a`JmxPd_BCkR3dHlLY!Au-8_ZmU5PBK$-F$F9jU2dI@SKD zol8dgyREJA4~}rTBIfE5%h4g#p8z}gblOXxbZI92VjhPTR&)vY7E2V+V|ce8F)gF9 zC($XENXWLhtd<{a?f3l7YIh?_M4$4bcPVC?X3lR_11BBTXVn{*qR&rE&)v94t|%q+ zGs!GJ#=1O1V+Y=_8qD|p9fopx)0As^2IZbCm)73%jbigyzdn+ozt=4hVJx+kKvnmW_io1JOXP`e&_#~YMpYPkm`;!(!{;q+6}1o_o9rtIP!9RSlH(Mg zIo+axW`Bbj_F@+BlDMqA8-Fo+*Dl^$C@#eSeJPVl-a3dC?F&I@Rt zr$f-%v+4cRa=Oz<^#vEj5H>oQ6WWrLCoE|Q_p5+CG`5*P^~tr^NA7S0ud|s`r3M-6 zUXH{1GhrptBS!*`Tffy6HL^AA{fa8upQ??ex!*HCw{$N(N)^dy$_v%3@1oiAoPBC@ zzfnK3Mb{`DQP5SOaN1v-U8*CQP7izjJ8+vSmz|vrl_#O)lAWsCa?eGVkWgG&5l|N7 zQq}>ejP>q4zqGB;Gx$zsL9RJPx{n2nPr(D9;Y>DD;-I#Xa+yt9&S z)16$v+I$L*>LHo+aC@;NXPGj-Va&e0!%#R1%!%;pi8w23cmMOHZq-?G@e;nJsd=td9C7vS z1<;OcWUL_6PKfs+Cv4VJE`9he-*PF&;fgg&np@2XDmBBYg{#2nNiCwR^7LlueW8;p zknZn^OoqZbKR?f|H$a>Fz?5zY5d0$q-wfvFXk30K{36}anxbS)Y`*ev!GSyow!M!~ z*^00u(Y9?S98G*i@Uope=vR9TXM=?HZ~iAhKUr-k(G1cjRC<)ishr#QBj#? z3;wO%H`{`9mDMK{@GIy4r`vzun6tUyAPxWjRf805QAN-GH}vHDhBXKROO569yZw#i z{5!9wbvmTT`U$S);os}yuV(#rG4;uRa@BGE9R&VEefBSbes%;lq$nawP&Ml>AL(xh z3=*Cvf`Pc$LAZPWN?P~tO1^LEfRLhw3+$nP!Rr5r{r~t+st6>_!S3W{+yC~1U9O8x zK%g^%!|(rL8>3(0TRR@F2S=qV`+uTa|K}Ny;nY4Hs=r|W`M(xIIx7leV>+I0ZlF3o z@Rx1>o(X*z>kKFeq6VJ$a5V8hrDh1w>@aE2VYPqagy9`F{s|>_eBh zK8rq3^`F7~yFo!Ms&z1!a9#5C^?B{pDZcOG|CsSdQp3Uz=i70B)psY`;_OjfQD7t@ zRx?cTNTLN^g7mcioQEe#B+rL~B7xXMJC;gz=uC9?`j8sl|7o%dO{QxEiMPX)W%HxR z6eL;UeMq=gD4ztajsA@W+yDF>3fl}Fx)+JJ*_19&Vococ^^?^?j2|&I?Ig7mMeb=z z!1Cp8@b>@CAcUor_m&Y1m&sih=BVvUN1G`@T4Ig)o&QQa%VRUzE1yV6NGEL>A*=w| z?#h>JxilYMCH{D0X0Jlk`5i z?knID$>@d2d$pKNOmgP$W-2czIPoG%8@lHLNcFA{@I&kI;oUXi75Dnr6UNe*s!U0L zKiuXV6at4>W!apxaXDK|$qke<_{gi-pFeyNulNUb`g`fGk08a9U)`*M@2*N11T6d4 zfQo01YF_QcF*%)Le#5FwH4mw}55F@TnMT`;tihzN!7x}g^b&GI!|-jYQ>XYllK(lelV4zPkMDcoWOS2%O!lG8Uqt!nix_-vcyaj> zNI#GM?M}m@qj&W?gZ#n>In!%aJBwd!wZ+r#s=1C&A;JaK0U@{xBdll(#wnDeJP|il zUnq=@h*!70BHjwe?%>b`$B4@&VuJGDr3O=uB>wpLHQc)J&ZlFdeIkP=j0t({I&;fm zkN?hX{DJ3AchAFTFMY7qsG_Q)d?>d5?pJI*B&GR=sOj*csq^q6{7sFI(OUYi*c+0m z4r{c!&6R%57T3rYzVMp6#y4mVg9ljh!6}rqsx3n#|IcD9%bDBQ@#x zC#ERv)?N8`W0kDvBzT%%;dYF;9E)*J@zj@`vq^#*0kmx+mgsNxh>OEm@N`r66h-$VAsxN z@r?Vjnmhv+Qz^6yG79uEt`CD+GsvQ$=`ep`l&V zX&hyI8`j)4_gdH+fn|WpoZfsqRXTY?TEza*Rw1DZuaYVVL28Xe&AwZ)Wr0YFN@>e1 z%jEhnAD{G8spE<2*FE&2XE(Ab#>NvG|GO0Mas|87a9)g}>wc!$AhFA{ZlQVo*2s;-?Ud*ZdetfL z3e?NZj94xiy4%DdWzlf2uM($!8lX16yDY*X!YZN7O0Sr`C}V5yWM!;t81QnHcav6A zBBm%ko3@r)&RY_gtm_k%4VOC?gpgwhn!YGNe3)PIM>XTI^&ya-U!KWlaH~c#D9*U0bYP<~1pSDfwKNDxh1 z*@8tmo-ALxF=_(q{#fUJ`B|)^S!RbONp&Z1MW&W&zq**Ceo1h>V{CMmoKqR=$eX*0 zNTYzS-X-A_qaDzzcQ5}KnW>o5bD!}f^^Ox!A+wQ{u=}6A_0Lw5RQXVKieIOmrYd3S zc$8MhUQ;EUhEQf9_|=I3kG9HZPc*CDye4%8zqrdoc(&VaE2+}}lUVJk&Rj8RA(gK< z!3M2O?5`niJ;QM|2x?b6wt&haDZ$ ziRQzni!T~lMGIS5%F_c}hu|0Q*-Db^SZj%c+FnW~<4&uQQ=AF~9fDH(d%=1Fqr~zE z(yAjg`cEBWvGw%swswRGtu66~KW?U7l@elSaEfr`-=-^v*E`au2h=j{hAmbllERbF&T2!KO$J3#jUc(mIe>lNP?dY4{8mD5gR?KM7U_M2v8_4iSl( zZFN*GJ|1gEu%}HBSuQFQc90almDW?4+@D=tnsM9ocuA$O)H_yOt>GwBNfH`2w73MG ztiCUUzPnQaLF3f#3DjQ)P-_XjNROd9Iv;Er#T4i6-m9=xyp&Q&-&H{?V=8{pXl>2) zP<2DOXYk?yXzG{Fu6gT;vnTwY7sQ{VyADuY zZ?Q_uYP;iOv*MieT|npCTrn%qpPIHX6D+;&GGA<&i;ji$8(?=BqV^hqR~bp>vGlnn zJ)^SROB$8w^GN^XCOro-!!}I?qDH+>PIq3);06oGi-d=%8RX2E4~-qLuvGyLP6E+x z%7ORjQ5O5nr(ZN4TQfcFOHY0F)J%spaR$|;E~RY~%WqzS-o1nqY;$PmzCg|byOs3_ zAm@dMHLsh0t{B0%OOj?ZHnGA>PS=QG_z0qw6{tN&hJP4UP-jV0KHySowzusG#o zWVNInD2p#n=4jW3NpD_80dK<7mb;)97ba?lN}5hjt;_2>PT1NE!rj@Lg!DS^az7>) za1~X6TiLEy!Un+CCI5;F! zRii-8n9YpBJ$NyN>`W}kX?LSugKM5xuA*1bIY*Pjuo|qm-D8}}V$hJXIUHMm>u#mY zCcE!cb$PN@JV+pJb`pH&_I z#G^x(55u=_?3VxGAwU*@V5EF@{zHdh;r?@8(ns=CnrXG74-rud*_~?i#Y$^Gn~tQ` za9Bf`02B0F64iq|k1& z%-JLAsWHi^(XghUDGv9~qGsD~7U}emNtHZ{t=EEByC5L7@{u6ZaCp%b;$an80Iu^f z4w+XW`v%m;dAj`j;}M1Bo>ZD<(z2^#z{2nqLkRcLT$Ne|w-bFaV08%?u&`6-TiLXU z3~1@hXU6*!67gTt8ZC|U+5X%gg#?3&RG>6(-0KCIyY`%Zo=J+|*oJP<3kF0__}Ke4 z)pMfVXe`d-vgB7%xm%S*I|M1WL7=3!>E^fcQ z{W@p1u=FjdQ(m27c`_VYRT+V&bCFXkLG)5KP{3$BbzZIM#Ni~F!^AsmPPJUoH?@|h z)ES&HV=D7p#j*!wHH>6;jbvVF$NxROQ82hLuLfs7AQ6V6)7CTRUAxz75P;k3q3+5I zT%KYvS6Knc!bi)*qyR}jHwQT(%KQ-_>Z{lKE4|t)_90Vt)Rx22vY95J;cU4E=<8(< z3InIU*X#3L8RT;a4?9VEtRA;k)~js`Q#j8R9SaI`1J!@Jp8uTNP1?xnNNw6udf)A7 zl#AE9mK&^>(wD>V7;H{2rsdW2yy`34E~Qo=dh?dsMN4?p%lSszELcWJPg-r7HV~*z zAU(&y#@^yoPs$E$<{oX^)lK1@jFS!g{{8z8=k%pI^EgCHcm{PA6(G|w`=Rx$16wnRPD>S% z=noW#M)<2zMM9;<=<6WU7sdSGp59)k=bO1S54jgvVjgE@2(g(cx=-QyMA*YT?Q+mT zpj`(gYrl9ebH5Vc?9i)Xd!9*p(T8**Tia+~)6lU9m_TDO9}uHG>6_kq*zP~wO?6A= zo%3{>85`qmJ!rA4X>M?Q)^(s~dohX|c)ie$I@LVVBkfG*1!z#0n_-CnPFwjTyU}3 z9%o!}Ns*lGENx-bZP7vA%xIlqA2zn*JPyUpbO5n}Tol>CDhghrSc4(e$@Fw_WT8)( z*$#T=olyhJ&;eGIrFHy$o0rSwrs}o*?Non7^Vr|JjJ^g2{HAT_yKd>RZ{WktQJLrc zCA#vuT=UUmi}MK$6#V}0hodUuqBrtz;B}}Z`F`+hkkrSq zj&fx};Pu!H$qYmakNi%)Kq`S~vXB&rp&>2ly*y$BNv4KaZ$qLsMinSMJUI;GOo@XRV#zkURZTlp<;qdP?ps%iX@k*}YN_yV=X6jb?7O z#T6 z8lN~DZdAr}>y)ta@X7}2m(35W@FJ~svuh>B9v=sU@v4DIRZ;W?1=gy6Z7;hw?<9#+ zbiV(xPV~`+PVby9kS+sc@$ZV?y1}VK;nVv;=A3k&e)y+B3Nt6WqIMdtKydPyb zefCi$^S&w6zA|MuIqb>prM7aRLPdH*an9LU*>>EUm?G0#!X5N#*|yL6b744Z>c?r@NUZ(ZfCt4#wK?Zm>Aq3#m65tDZ94VQ zOQ5)51Rsj0npXFsL*4#EM3T)DrT^}JrEe}j-kFs>|B5`*6<;~v6Fr-o(`5dV6n@i_ zcH9}V)ELjFPDF0m)%#Hn)nfOfXTV*lupL+D^aL?o=FKRxHh?RmoEQF*Mvo*aFau=w~xh zG)$e>?SiRXD|Bcm4lVaw%1g*--#fque@owwl_Wo5e*Da%fU5PlvoWn8n4ABEP=w-w znS_;uf>f$jwfc$*m#bxFR$N<&!T7SD7eyqPhvCax=ni9X{4~te0z}(O_}%2m>0yT9 zo5#V8opp7MPdH$QzW#DK%0P@5!}c8_8E~404d^0Ql3HjBdKoJD)*mey&^M-L3~OVO zoI+2e*z-hfK5qTKQ67IY#+Y}OH zm?vneWO7Y`q2|d73WPY*ZPB`+1bcm`sA;7y0B7)Qq$<_T*kwp67MGOAui38i>e8|d z#cFZ6<>%2LvmVp>>$7ikbxfsx{PY3}B0WbubMZB5C?0_+&TV<@jnDb7jt^0(9n4>M z!a*(ugKx*etg3K$WM-uJ-T_I^*wz(u<$8PAOV1Yf(woK|my4Yjo%ckYtWB;Zx$+|% z#LdHjRvGdo2@kwnLTDC=7j5I4XWcg}H=0%6x-RWW`93|(L;{?!U+%%D{3vKN`sSASu|}aE(9+JI+|QuiY&Ws{IEHI3Fn%@ z3OUl`x68KPLb91J7;B~$bGfJPPAk9s{e;m|o6!FPibj;~`;v^g|JDOw(eOGF3T=nqd?<{Rjd! zqGYH^C}biUqC!|ibk3iH*TO{n+CrC5GQNU^j5W}xgnj2GALvMp1Q8}-WuWjjVPga- z*M&8~NN-8;F8wk1C&3k385ln3u}8Ve0T@GuQGC0;ET^9zp!;pxZxU-u zHs|eks}6-B%+?%x0qSDo!Jr-?V^6tkt-<&+utfvQq)oxbRQtQNjspBrfI`n)jDua4r7Lai7_a4oPJe3SVqu`nVPN}`y6?LayUY7R-ZY+B# z(5_~j*6X@l_chU8CghFMuRQ<2j)#&;zLe}Fj~j40fzxcG6Haa6#aA)$apkygVQfF; z@V)A@(-1!h^EOjv&FAu^!Tt)%r7`Uwr{sICxec6k(VkV(tCDvV&J9fBkSj*FSVL!# zO*aZsJv6TkJIRbzAmA&EsB`f97zE{5(L{4tC!xg)%wUth@qk1g|$7Gk#i8AjQbo&CUC{vv|E<|6$(SME$_+89VT$MM7dz!c4}9 zV@1={zch)nG$9H+8U926hNVUIv!kFiiG}4Nt&_A;;9kE|<4nxk#UoH3iG@<{we1Lf zJ88Wjg#?C*HwcVntw~e8`ynEENzScPufzwPsjJgA(i~uxN=Hom!5YK zW;3}LCvvS=g-QLZZsR280eFBE5f!d{vTj_eJzxHqDtu4wp{}H zPj=JULa=n|KLxLL#~eOfGLP5~hY7oYFD`ZhrGwCkU`L4ZsFWjD@yQx+w&WhYtF7!_7#U?Hc{Dx-u7{YwEJjM&kO1Y+CAW% zuuvVPP5>UFJ#Uk)i>}jZWk(sLzQ-afE4Z?s#UyL!;xkVSg!oCD6erf2)Z_|V+=8dK zpyq&ECIp-)dgVdA1U$|0Bz20zk5zNwta62WC>`4JrtrlD6Dlhg-U4|$RVdv2#=-;ww; z{qd@~iNX;I5Y=Vj1+QB+H*gJ_f?`gg{^uUsK_r!OarwpOt%pHA;56}|c~Qs^Jgx|T zHvFnu@!7gVgY9UBUEe{wUN2l#ezuUtWmY5O_l57(mgt6O=IiQhWs1q-vd~u6;gA6C1{*n0#LeWeR6%d z#OBnd?^`5g{-T>=Ow6kgL~AdPVH#l(P$PoFNiE6a!&!Q`n2)m?6nD#_rxXqBGal!C zS~aLJTV$7ga~j5Le5#U#c~Ia2K71j_;eNcahYjz3xLZ-FbJ;;+!C5l9>5+_5oh`LI z?1MdAS*tS@ET+lFTQGtoU(#HWFxQ`u9~AqtVp_hNT;FXdL#QsGm2)HrFXJ;(qBUWV)YJQ*9aD*C+HxkPe+qbDA{mLTd*=+elSnVS`ttc z+K7^_lxXWP>F22&*dR$D1+^hW`I_`KL0S*h+Ipp>eyaw?eLqmHjx-}VHH0UKWh7|0 zz3Ah!U*4bzquB%l!!;Qux-v8TyJugAYf~xe3tthzSk9(^cp)h6DFXRzBJj+$0Sm3aP#msw=PLQ> zA`I6XK1^*pWeE!#u~Vev`Z!;t+fFy6j8Iy;kQcZ`CO3=Rtto1o>bS51gm75>r%=1Y zE%-qDu`Jpn$D-Yqwk+6ih?b9y5-RoLyEeuq2lV63sx4q1;~25TnfaK{J04!PT_4s- zULW4g(UA>Qr^S68rZ}W2Zw@!C)y0@8hosc;4Mi|dC#6b&?e1;?0~+7roUtaY4t)ZJ z7yYtl^Wx%>$=l?8CXf%|tYG#P4UDHCu)O@W5b9Nu7TCb#Yay&ZuYPl^#pa)uPF8{1 zG-s7x({YFXIOMLs%0gCBd~jyV);^_T4$cMN*2i4aDKFkFLRpT<`+@3w>n! z^Qk!}J-rJ&ipo`{!BiNs7EBXGmay z?Y7WR?K#R$wW_n_ZyfcC+~`Y}c+g|r$0qLpfiwrt-2)k6syhbLcaikn=5;ixwF}A3*Oy7|_jPYH{$_*X4Ri$c=lklx?-wZnO0uqjmAwk3S8rrU= zAHXQIW(nwllW;;hxZQRBJO^LeLSGofE?7HX5M1#3JhU?uDQ1td$OlZ*H;fx85u$ca zzT$-}vFKD2D_w9LDEm-0LrK4zG~#>He%PlFAP+$a#|vrBVQnQ_#-n_C#(_b%P*+dR z!=|TFV&EF|_g(eexL6;>a6w;(?SX3bIBgzmM)&~ZU0w#aPNP@C;O@PyW3KU{ci_kHq2z_Nh@#t z`L>yq9`O}l|Mt;_WTAYwYJZmM2eU0*6?D006&fx8*Q~{3vC)S~?C(y13gqchz4j^) zAHFFS0Gh>0?%V$HoAd0Ay}Isnhx5fFxPtcNi`f!V4nkK(%Nd(X#LDms`Z0W#$y>D2 zXQ?5F_DYWB>TR_mid7V<`Xi9~TB=~@{O-?a3r8XfCpVoVf@PD!_1kl+&^Y#3H>U(Q zt2rzs6OAgH!H%UvKHJRkOeP)3fhP7I#@)n?pDhNdq+4;ALzKtvoA?RrMYTU zvTUElIFeYf%{iz_Ub62kSiWdbp<9Cxv5zdCMAR&%1HbY=53PGTo#UqM4xcP^;?S9o z4_c1SNvL!`V{Ohm?_DJL?gzB%@ z>2TYP)l^YaJd){Mgyk9DN<*a9Fsgo|1yF{hF3zAG8c^cZ;JfV3`T29tZbOU=hQxO7 zDI6puq{*Qe@(f5C!qnEtC|Nz%u{O(P?nr1j8}7(QI8-Q0UnoqNb7-?JI85k1U}U{# zK0Izp;LtNP@GZ3Th>6JC!ep3_yjw&yTv8R9^+amOcgUYV+CpJ7ekAEiO@lq>@qTiJ zIR7Kf6$ns>ZBy(}sSG^VCIobrPzyidgUKP@0tz}hSjS~$XFoVm22@}>Z-HTa9-avC1hj--Q_!hjHUHxVk%S-t66#qC~?{X0tBi&6YlkJO# zQw94#z*XR`k4t*b_OBC+v4gkzV07Jr$a*ExSIzV0H13;bJ9CyCxd)&?@5>cpZTuoO zb(I_Q9e?^CX1 z6ig34#;|M+d}LdEfA?86E-;+g^6cZZV5nBtjf>0wN7h@vMH&6?zR%16Ll5040s_+A zqeyoN(u$OHHw-EvNC+t1-6$a-F(^{f-3(n4!capUK4@Z{la+MH{My+rC!dNn)Brw6?x{_iouiY5hut$CcDW6?%#de z9}~4w>rG#we=20l-JL$0;yX>d;Xw~uJPGjD%_P3_(8Ign{BLp%t69zLtX9Kg6eO>UT9x-;;<3a6CaXo^wsdl_0rIG)DF` zvFyZRd}>;xJ>}A!W&N;abXb)=XSpKK_OMNcT2TlS+8JA#9Vda#puW)=jj5e#$ zu~VX~E3a-6HQ?`Zakrh+*-DyUGI(bSwO60IQ{0%?yvYl&DOsjDn{i8{1S)5)!^7lJ zI4r;mB!C`5F(ZG4vmFggD6T@n5FjfhlLblSTXFd~W|F5Y+Ee6URwSN2spE9@K7uul z%neuI6?GEx5OeMmO z_Vex+?AyoaNApP_;3CWqtY{dG3nFmT>QeDWjEv};(aPuP<%agJa8{f!iM1-y#0@9re^Q38d-R%$(<9Sjt*F?`gEPG5 zZ|F;Qf>t8(%c`y3EC{u`^V4$VJ8dGM`Drkre^NKloz7rDx(yA%_gof0N$XNcO^ zDtrzmO1dt19gz9LqNC`#kVyKKcaP+t$y!&ML*V<44bXeH&4mPIBa1C2`jYAw$q^w} z6|?Xzr-0Q=F-MkIX&TF0I;c|Saht!to}tc2EphaRx%^Gd1C_zv!$;a_Uu4~JgInyaE^Bkx=GYL36e`8e8#?f)n)-6A!8N5aMqcd zWD%^$T|_mm!8o8KX+6j9_Knc~w)Ee@0;Z{`Ft~bQofJEDXL=*sWote;@;2+=u7T#? zF;j&UqO(m=wWJ8|q1q6m7iXfX!s9o#l&Vl_p85X0g|7^#-_z2h1Log#fUrRJ= z#+zHJk54|=U-q~BPAy+>L8~xPb6SO2xjCeqtgX}=Ixt%JqPf+o?GooStypg0gW1Q2 z&`8$%-lgIb7P?#TJ*=D?Bu>u`)A*5|z1+_KFxY4Hn<-46Buo|wKuL^XGk~6i?TEHa zHH7ZK>lwVreXX0=tnx)&xBJEYw!DLcj1zz)5ioIB32fVnXt`K3Bh#EStsA!&$OK?I zAPrQIE=L^s7N{>?{&wd3;A12n0?dy!7J835YPBbSG6jG4NR@EUFR1!41an5%%RNRg z-h@>|uPIAAFz@5w&o&i>++c_i1;YQrgz7tOZ2BMVNl7Z-<(<|$jNiZLx4zy(r(l&{ zR=R%PP46nTkPQYv*J zm2SEnT%n=$A)MaaeXrjfkfzSIsC^%JTRcG6xe$6Vu|siF1=rnbkKj@*4%uw7Dt1ed z^Zv$lo(w|0?YCs%6|sF`i*XY2-&f61#wHN18c+88n|w@pGCeSHHRJH1rI;t<@6ZVG ziP^&>G0%PRf_6^VS9VN8mXQPF&31LBarAc!!<+3%n^2wG+Pw$w`j{IgiL~Jz#Lk1jHF6_p=Y^s*hPZbkeCXY0>>`K1AG%~9hB_;Tb z#=KLTeYLvK0=LXGFQD^J@)nwXoGoBoHt_UYbLw}U&1s>Sobln{ihqF366V**)fEg5K`^cYtWA3B;p977!ClyEbR^LPzbX2B_U9z0`M*6&c`OZ& zAM|ybxClecTL3L^CO|0z5Vy)aTg7dWem_Rt6ts{I5l|ptkauYV6&BF)G4Zh?BF3YC z^w#Sf7s3$YKB4=g1(8YeL`BSxnC)NhGY10$<2lzXaOP3hiSTp6;H}-^c#sAWwy|?> zDLh-KN<`$b?p=_zLU&Av9dWuTW#x#QXn!7{Vt4}ZQd0tb4GBqhw!pR}M-T1WW{~8m zyQ@=94}%>H=?}>X3e?**%#o6rtYoceGf`&hq zj&m-sgSsA9Xc6?-zy3wvI({yBp7hEBlj73$AO}C=tKmC~x`t(BE@$c$eouQYj*;2R z5c`exfV}j^fD%uKt&Cq@itj=lN_@F@Zef-J{$68|kuo2W&b!mS+R%N&E*8y)vrkbr zyTYi)48FRD)9v%6R$1_0pbZ_n_f-umO#;{ zMnduBpgM>%f@y@vYm~~O05UGMc$NooVF*~o+#8o}R+gbR-h(;vmOMt)USi=d)i`gS zGJ_mt>SXyZA(5>(GG$PG=P(&G^d}dzv}*@q@Pb^;a;yX)afG)$no~VbP&jVqG`*0o z7?a6&mt)Y;S}w}+k9gROkHO)VMl8Yx0qip|r0d6|UoaV->`@$w5M%a2o`JLujyy5%+WBIWEu@_UacY z5+nd{r0Vs~$(eEHf^q>|fbhKd2o4?r{{_(|_lYQEJ{bbNMG_q;n3*Xduu=%ONC83t zmsDwXQss|(60vZyaYe3-p!;{^_ABeyax&=vlu|66Bucq0j1Qe>ZXefJO^)A{$4)mY zUr$YA9l)!_09Ro;5TrjCu`7g3A=HnHYkH}(+^@`T09QoZg^U^JrvQOPFmg+3?#CX% zj_|IUJZUMnpNxZVwi2&Ojq9L6 z5k$1o$C9TJ#7g1(+Vex@GQGVbW0U+Rq+GNz#qV z@wS7r@iF_m6HycRVaYvux7Xwc5LiJ=QF6K*EZ}Y65t3d9`Wm3mw7wPhuiYe^anFlr zLU5e|Dt@;8+iUUi2kXHfimuIP*WAP$#=6|49E6AHqyRlQy$N8?_mDjDEMItfoe5sk$%N)Z`<9p-av))vq(P7K6}R#Yx~?J3@!9xTr9 zu=&pM|3l~?=#{^;7gx91-78HpVamG-a$^4jueqq;aSOT=3vT3nacbhNKT%I3Y%5*C zw(K&I8pXToYY7Y9JmHut58|3WQTz0Q*5z9Ir{E2ZotQT9O_F7B9*aa>f&?3$eOv9F^_t%rRleE6!GsG7UE5hU5X3eX_Bl4Zq+S3n|w zX;Y{fR0*JjgWarahbf-semePk)Hk8Pz+na010pKW5fmLrxY7tRka~>QMbZ=t8fOW^ zDbDd=GD^I!K7m7`0EXsn=Ldi^3VH#-@<*wVQg5%*0xPL4)u!KvxR^)!K7{!|?g!E# zPLjR1jC)5TDJ=zDZYn=!MJb{RLRH>PIkCKpYl^Z*#6Z!Y0F9fjzJldmdZRc|#I_Qh z3ntD2O^q3w=5s6H&;1^B=}~eATn}YQOYh+~cX#u*@<%DiL#_(9lp(vSA+6do&R^!W zbRvlUw=Mj?;PTske5V|J&4!4xiMYCPgVDUS)|QrqEIJ{l@`#}qC@1uW!`Va4mI z50;LZ?M=+r;u0ED;+Bua`sgHfctrPI;vyY>V!ewc)h=Di3X9h9t?NU*6sGhl%;#y@ z8}Td|qXP54>{wa<>Q9h>ZGid-B0(}h0(`k2Ad!x%WYD7-YX&)=l-B_)aSRABuzCUT zBJuyJ{P91|XUN5ZlAyXA0#IR4hRCREu8fPq0%AD+!9#ZtG4j3jF^EqB`gql8MM#$~ z_m^ltgr!+)9^hTwsE3=UJtgqc*9NGO#Y-~d#9|(xFAk7e#UJ+{C#f32j#|47LBlQq z+tb}iPk_)Fq5s26@ASVAr2oCFN7IHZ0#11%^;z7a?Mi>Ty~nC-qIYJN;win2 zT*tR>xebqkoI6ZdArA@<21RqnuINV49PI7F&#MOOhi?z-(SIp*#8i@=eyr44~7#+ndDZP7GY3k-8GOK3Um;6Db~ zT5a8!t){j>luc1c$g~O!2}mNC{lSb9mT-L3Tejc44<(@2IzwN0O&PWUKU@S$SVLqo zcC8Md)iVYpgarHy#DEBIMUc+*IA*((_Awjxse+83vSmtV3h&}ufwbxgbcz?T5co|B z+GBbAJ(+gpdP*MxY?jo z@@3SgaB48hYoRmg5#5D8i+;mj9a7Q5kesxG9lV>8Zbv5`U)hz( zH0wZG$jz&95IGA8oSpwb8ERkKPNV_~=_SAD#DhpQDFwtiFrVHV%j{ysD|8tp*RvoN zYdSuby`S{8@1JAs_8I6LtWcAZt^kZT5HHdkAMpEnY(@khOZJ|G3sR6ykeyxryZRT2Tqq6rDjDWC7T;>Q?Ft{= zXDbo7>iNIN7^nhiX%P19^e5%XvKyng{8`46hGy-1MgH{1v}IoT;(&2^hUNVk**Sez z0S&f`E}uVU*Z zUe*p-G37$4Y3d=k2qxfnToMwW{%aF4BLWa;`fg)orOkxIx)dOyKy;q6^3u1v5usQZ z_fBUvk~|Bsx!t}1va4W9B)$LzHT6~(giJNYRB5T}WgA0AJCN4&9)aVX&xarw0VM$H zSS)1Qh31m1wT*ld5;GuYEBq~rTt)ffs#Rh=%j__IGg%BSqrEWQCme|a=4So}e3g4| z!ERj&)|pahxFdu4D&#l7dIOSJJpu=!X*b4(w>5LXYZ8fQ)|;onBWWQInP65~UK6lK z8?x^=5!^lNL`UCL2kyBUJ{RiDrRwlIn!I=6*H=@2T}+DW%a^AO8_GYc>MwoZt#Ro8 zTeN8=*}x=cVyIpiGWgf>^kXsqVr3$w)b>ix)6cU`vG4Zm-mHAwiPlyT@yFs8C=5&Y zrOyoz9s5#HSd}gwlw23`!)t!@4Ye7ortDaUgeQuvP1f;SKDB@G0M3L{kyO=Hx#1or z|bpfKDVXFxC}m3yDrH4 zaC$NJO-0mqr>och2fhv?FhsbkX~7f+jsL>JTf+xv4q+P!Yh-pM%X-RolGT{z?@tMD zn`@Xry(|JFJukuCQ`P2s@3>GfPNF6^^X!BK61zp3e;`t7re3Hqs1-p1AWm%%5CGJ= z;^VH4px=W--_6ON8X7b`JI1x zv+?eOS4ZO!wt!47RA%kVDp{g99Vm8PAr!Ql41Ji$dD#@l>OY#&N{!Ik{TZN8?6(T= zo7DF<|HfsBHvRjP)0s_2Y-3-LSU_G5WuG|xn?QArk-B2Q|LYQrd~Kcwbk)-Xh6j_i0~g(IKN-wAQHo ztAB)wKc-`6x}my3!Bc98T(Iu8Y`iI5SOi41D)9}8o?F)73|+qWqjX4JFI0=&%K30U zlVKDPb>%$`X6I1x*7jeD+#0YRl^84)F-uy$h*T?K@6*7qEBS9aEkPI5O?^Ex09z1S zC&u39$Lt4nK1E$pU>K*v07O`rEX0R_4i#0FdXGVSu>caUvuIsl0?0@LKS&oKiSthe zp$aD^0gd!#h#+2v<5Om~hLQHaDJvoen&ds0Uij_WG?@{gQ6u1s~TYn(c-eUDr4*B>ciBGgLy z{O(?>P)OoX88PO(oVGhlWXJOQNB2PVboNe-gX=Vj`PE_&Gl276a}wEJbvobZzdP+% zo8&Pz@VT6v@=e^L7*_dkjJ4s<@6cJ=tiiyfX+y6ym4ElV+=5k_3(pg=vaKwQ;F`Mr zKjh;#8UNJh2gfs%$1JLHMooEqQYoaBxO`&W&FTHGdIr1X{yUSh(BoSPSg9G~4n!DB zKE%IK?8xGO&7hshAKn&Xfek#$6hwXOlzQjI0#QK{feR6&Q=b3m&T(1T7!mLw25}TZ z1P;|vmNH|84`M%NP_laKfp~^|IHYm1F8cl6Rs#Au)KmC|L=5_KiLv^-41Z4Mo1w5- zKrA2{06$@43R@!y%gOxq*!d;<5J!K4|{T{bP`S*cRE^mY5w9nkLU^i)y#9Svo z+xAbi=9~(>J%J-_nS5^CRb;V7uwp3Y(^)RGHY!P(;W%G@Vdp!V-ED|CA0c{go??M17+ zo9NC}4E>wZrw#bTBj)Ctqz$;Vl_{%ZUur}59UT_L9FoNjwkx_ne-j@5sSzx@*~foW zUNLy=EZo-d%-K6tOx2|M_qMZ`y3p2fhH`%%tp33e!$tV#e|R|Hy{jx$+3Ssr*lg<0 zE(R~ROoxLGr0&%7N~m_bc7$xpe7`mFWgo-}t0+WzpJ}XAjw>hS%onsP6s!E1+Ue~; zz+DCQUEO@6uDD~i?p-kEs!QGdVMfk*|14>OaI|5tJ~O&`5OLBVTka6&PH6Plyg~~H zeAg&#qKi{augq_t(}-b<`SzFnf9Fsxa&!>?$kvEL5Sz-O-M(V$8rZ2Dk*Z;{olQ6Y z-4`Gq=YK^;bjs>uQaPgb)_;;65W#T}N5CpTo{fjZatGjJq|ox7G%zl}3q*#=cMLRL z>J6m|T4$DJSJy$2|7=<0S>Btn0xpNGCvgx_I4KMPeN?#AWQ2g}biKpWIuN>0JA6>S zk%Zd2#mSV)d%xV$JiZiu`kC7>v*DwCcMVGruDvufA=-MMaTv)r#Ei)=`eBh|>H!!c znPJDs%ZHZr$^>(G&pe49LC$(zH~K$oNYD;2vl@c9z*&(IK0$fl ziNb=9|Iv)yFWn5AyJ1)pX%fkhC?WHd<5Kxxtb{RQOzg;gQbHXFGp&D(!7_KWrOA{) zm!(Cksxndpecuye98um}l6n7S_;=ZEu^gQ|zb?-gSA(^lr2Tf`J@IU`h zH&>q2%;~tl8NVBc!%H#uJyH!bczP{%>)jJ>a8gD$ejG>??37H#spuH`Eco-Fz2I)k z+{y`+oeJpK*J2gI4&YrKPXti|;WlA~JfO9B}0e zLOmtTfiFp@I{gL1g}jp;Co+VV0`_>J2lmW5SjMZVpP(o(3XsysS;yp@VuMQcwAD`7^f4?CahtMrQ0;u>3vvowBT9gIL1?b&wR3El5TvPAtQVv!=_f}=4_PWj`l z{+>OHijJ71*87L7ckJJu0S^xzAE^c%Kl4QmhTD2U@~10btTjcg#||xsFkL9SvSi9V z;3ekJ653K#&HyR*w_J1ri|Cb|Z|1pK^Z5pOwzOHZZF-{`PYig3Z@xCVH1V0)Zc**0 z*%X_~G30w-kFgJ~+l1Fj(pkQV*zAy)aT<8wY%KQMpQK^K6K{8}G+J`baq7T-3LMes zP=)UuqEo9x+nQEJlo5HQNvp}ra_?;g-0ZUlIL^`hek`o>r55S`Ot@eIvHnLFMk78# zUXEjtlRgrdTmon9Nz({qwNM}M(z|nWRv(_wJvTdgS1|eQ^fgg%K$vZOPVUq5CZjR= z9QhhF*U-6?CF^vs=9q&*EjEw$)X(=rTk5to|2-^_C4jYBH}mz;phUFbSdM7-iD5s} zYaN994|kn#6|uDXic_INTD72x`WkD25s22e1rH6NI!WC_!p(5Wr!o zY_@J=V&((Deju7Ys1A!9B&hhlSQKj;(XhR3-{V}k2GT@SI*b<&wFm(?Iy;jUDJR{? zufINkBM|yvZ#)75PsH0GRfrb}bxMpBAO<$4co+}keuf>>q zDCN41OENZ$5O*%=n69xrln7l|0@)W>mO}gihjpl50skQ*^;ww9T}y!=Ld_&Z0tE$G z(U*`JCGgt}2@)ks0UP(>hfVf`w$DmE_jr8IC*dFVLxBdL)~M{e>jYb*TjG6c{C=6* zR$4GFm@CHln>}G@YRFOKx_0SUl)W))n~2Vy=wy^GqCcF5CoRvi1vZ5Ca7O2hYO0eJ zvMX5VB@rbVtnT?zu4z&T7o>!w-^GC7+NpK+Rq4}&r_G+(b$wE;ff_kG%!1hjT`Bgd@g1%E=#$+Y$dLUyFCp5w3IlI zH0x!vyZ6p;K*A||`Ps=M%ax~P(}SaZY?lVCGHUy=3UpZlTKs7~uj#&hGAiD=)-E1L zcf}vTK!#r4V!(ntPvHMe1cqYMyc6)LU#TfTgd<~SQjp+kGHGcrTT>XE1qE#tthIx~ zCP>|G0U|5Vky{Bpm;&*T{#OF?2=);O4@sbp-+ad`02R=|aoar5Q@!khJVRh3BLjy8 zu}PzHAA|YGcjoFH)&SO%HZG{j{Vm1NDzd>ILAK!@d#1w~LJ7{CX?^b{V=rz=fgsvj z;k#`SDcMcC1%XEy7{gwAJt5B}%u3}XLaiQ+GI!nmCmB&Sl5v419k!3~ktjz{28UbN zYfHOd*h?qGh)KMYDWC^674Vt$P(hI#f$5?kA5l`@5~zDVWTe$u zBm}wj`r**2#CWO(h{@&!G4SkSxw#E`5B5?|llH#sXBX%aJbD^17tVwI9;E}4kVsqK zlZs4H@v&^I|Kn}xuIYYK3Uw}KG=t{b(~k>-o)#}0f7IHJEH;C7nbUB}ZiT%%wwl&O z5+L&lf!H8C1OterfO#~{p#>=){N5TWQb3tNnA!sD{(Mm5P6U$0d?E@2e62`b6%u~y_koEmmH9#3mD%S3GYwzxp;OMrlviS3dyEetVnN+K#vejU*}RTU17K? zleo7%?Xp-md@`=uObkx8tCP(GihPJ5UjHyhYy0JI4mwllqIZYnaH0YT#S>;Uv)cXD zHFwQQf=_G%NE<$bUAe3A!?Y`Iz-u%6QPUT-dxI^V&$$OM&s>!jZ)b<@vzah3IVMdk zwed1Cp)BCWUw-D);*Vu}o6>op`llqBapRjLiJlkh-nW|BZOu0MT#jPQI}EGO{)07< zNAis?3|M!j|Q3C$RN(238EoIHnEuZ3O<~yV|OF&Cub^WL5lVI5&+>g?A z_*CFJy)9p}U(SvbrJsap>=46|&{PPHACZ+{Y$A+o3&aDwYw&4OCfqjffgO3|#hk#Q zKrb{Q&c;ZcCCmZgC8Yy;4-!Fe#{C+#eRE=pN4!o6ccJh)?fCN9cIipucc?$a* z$gbtc*55g~Ok_^fvdoi4fm&p65ZO_jAL| zN}2H>C>{L@q$&QrL1p3PDvFNmja>N(TlTS*J8y|kO=wZ#9c_NfM}=NlELm3Lc#Vwe zZc#N!{uA3#oV7p2vHHl2aQ|`!L6bc7SCk5~ODD5^Jfe~H$_Is+11Go!Ne8l%-#lTT zPh0Zu@t@aIFS>V$hpQCEZEY?8+oHlkj!k1J@GJTs&L8asK@|?&{WAF%pzZ=1sgEGq zJz?TOZnKw}#W6B!1LaMBe2Feza`qD7Rrq9Zx%kVNEgWRdU)W@^*$~qRAl*M1A4Udt zp%`(kYGJPdT?90tt6mG|qDib$8$xyON>c;4)g(G^c@%quN}+U;%61S61Xk9dOBMh@ zfUS=6*yV;8b)HDA3S^N;tjV~N>^_l^TaeI#a|=XXMUmsZUX=s!N*KWT-~{gzpd)=L zSVPp8ki`1H-H9^ddfTd*-);ROy-`uP&@Q(JVb&~ovZd1duNNPfxDEJE&Ht;w3x;^* zH506E4>-cf5bIAu3Khwb?)O1vVk^i8G{iDtVH_qqLcIlJDG2gX4#i}hLvs5i7gXZ> zIT-g*sc`?+(2FooFB@aQ;u`Ta!;k%hA<%TbU+_J z5%7#j_r6lLBhcefCb{eScTzxFCR=F# zs?6TjBl=wLFzTb`1yFD)1paH@eu zf*f*(Y(N4t)x{?OD0+;EzTPo4d%26tK(@fuI~22U#;MIp`Pi0;x^2n-bBCMm)+fU$ ze|#Fa2#o%}V%$+jC16e0T_d*6giW(0%=ewdFYU@H-aRMPwim@@6sH9|pqUZYf$||F z5U>&`9D+n}1BBF3JBYuJR4o7^W;wa{2mpcoUH}E9Lr7U9eK~bTf;s>o4GT`5LTpE2 z1dF`giH!5g=o>bLA*d5tvJt1dp3w$ zS4QKHN5OVvZDl;n_-0yD_G36K@fe8SL>g45N3=fo#y;{3I)}ib{)5K|VGo5H|1g*TvCahhO%OW+2$I^OuhIJ3@hxpE9jo=B5Q$i|HI zbd@Z&_V~#I?Z|@v|H5eUDN8i(-`fd+)p1w6=SHJ@6ZJO*yQAe1hZgPBMSPs!?co!0~RV@izMvKI{19LT8YfSti5{4uSYj1%k90ATETv?lm+` zYcuy(@Mz|7X-QkPygdmJXBV~>0K>Tj0Ea$utCckGzzN7!f`=PcnjSvO5z-qR1yJv9 zXsKdl122vt_vCeiM=ya<(l^kH{@;Ct7D$}ahcKo>sL$<3L`8RXF@lPZ8_-%b*6xG8 zJ=q4q{}J}yi!4BbA~{&<#44*;T9Nwvi^w*JaRz zMxW|{@uoKzQU>c;SIqgu?7t?V=L)AveaO7qANcM<=Fz)jgyM7=d-eZZwA(0fila=5 z$64)W7w_{s!6bvF7X%(^AA}>z&og)CM9z6M32HFP>7V%aX`M~3zs6ZGwQCxkZTP2U zh18Mw;bFkIR@@I!zpb*TH9)U{ct;3I&^swi)`bG%Urh?7jt-N71Y5<~A;@w;OElYS zIv~F46Nm&r%F4>xe%YZR{<`JCUf+H6bnVqASnW@oM-$kb1T-Ko;6eYdHt=JvoNrAj zOx5fzv^E}$BW(Q01V0V!BEo(!9FZ)~1{m?T9=#R*oVb%b!7R}IL4k8Fj$A!(2QsJ5 z4EkLkE0-4xXQDp3Qrh0Hy++*`^mkb}GtS!KkSg-QJhc`;(3kPsfP> z48a3Zn*GY^nNJz4LR64k>%ky5eP0n@-_WP7{hhk6%~=oG;HgqG|EIz3|Gk>AoIfJT zKe&f9>G-l_nDV}Tx2Pe#dy!{Hh!t?AG~6oM*Jq2TvGjD-laOGsO!Uq?$zSPC!OlGG zE;2pHKfOOd+DxoCP)NtlY8b^h13ZxMc|x2DJO==rD+DVX{JZWC2MQj*65+T$C_3~> z{(vzT@({2q#P$Ga`eTH(9_oM=YG2^#tWIGYgH+rg?wJ7DF4PHyGn6j+CycUKXN-9D zL?ehPSzQXA5K!y5oi6H_d1l5`B>d%RTeIcbylcn4S)mJ3pcZ{!QXzd5K|DdP3D`49 zxOG8p5!@Ka&B)v7Z8ZiF943$i28e>80OQMAR}rIQIK(cF@S`n@*|U*rlvuQ`-IdC_-uy1AYAM*J0VTB-B8nID-}Z?#8a=DGG7(LDOaK-*=^)yAw%s4O98CZK8$Q&BLz8S~n%<42uH3srWnZ z8(n8zZpq>tExzxaj%4EUnw^K2XD;TKyk#CcmESNLs}J-}X)FyKcn%3W-`=V^bj+vi za5yNTb783+xz(Ns{6{s&Xm>mue(%Py{}m}-UnvBcoiBLC?@reoBADL!qQaiYzVprP z^2WH0)1ZHh;2MCe`D#HdNbI;|0*|I3I6b^mM5!2`38SssT|WN3fMb6IL2= zkffJ(oIn)cL%rbz2H*ue9`%Q#FZ;m!IT&4Ib zlaROnqk|}+f+9-a4R07Hux!$$!e&jXIQ?DpNVlcsbXIrgyb!N}`)--Hwzm#;Ql(lK zpc(9%A0U5Mjp?c%uWZyiHquMAjC8iYjc9zTQUMj!33xTfO~Ii zcREr(a4!T8-0UH6SV(n%T?BD1PD%VXx9@)4pG;c3%GP9>;l2*Zz2?pN*y93eKl=h? zi(h_=AhC-Jf$L|QXUZ-}#`)vax)Zo>0G?APq|Dn&u5oD()Rsp(x}dw;)~ii7za5L; zj^&{F<>^qHs{-J6aMXQo%0^QJz_rDn2Js8{L3L~Wo|uaJ@ALboFt~Nrpmf1$HZp)Ke#fIE!a`-(CzbDZo{q-JGc8gGT3Du^YuSvMm-Hq`iXR~ zjI!KgTShzE9sCr7-_rA?FY7Rv`2!lwcoP=$t!0Z6{IR}=pIrp?_76T4eDL?(zH8lo z(EXKB9ewbZnzHWkk}8Q#-RHMb#0Doy4I60#4_q%Z4F3x>ypTRkXRhIfX&Ly{aWB=q zkY}{GOJ7PAoUJI*t-EAt*b|*8(VZ$2z8{sBTTd!&Wn{IABB~}6YNdr@k}HPyEk&P> zi!WA(65_W-o#%-h*15}~6^=aL=yRTH;vIY0PX%{`PLgQe)qlAath_lK*>9RslC5sd zDk)ux4Ii*$3G~n6U;4fKC+4&E91O?#Ag_+mw)_eE@`6~$qk}-(xBkNyq34&1eVZ$3 z)=;m%UaX%VZ*)|>+Wg?#Cj2ai;#NLf{P-?Tb?P>Li|b%*_@l6%+2;e764Z|hp7@O4 z_viM-K~QkJbNsQnp<;tcL`A^m#J7;!Hy@t!Uc!}QJILGu$tRt4EPpR_ZD|DR&3_r5 z5LtA&^D?6jy(*gxT$ zmq@3}RYr7%%w3sp3T(_gugd<{YC9l&r!2{;llM!hLetoq@?ZA9(EJX^35T6f?nvh8 zk)N@GEd>`Zgo_0Y2aSxy7Ywu%gX=vE@Eyvz+v6#q;&)rYuTyrLWAa*R>fh{3FXB^ldYnduu8E8_wUG^W}3<#Nbn!1#+4hazF_| z)Eju;{FUihNsC42o{xLLmt46AVQ5m4GlLNkdBaJm$#hUMxlz)+BF}s-yCi=OzPe#@ zcar!H-d2|fxxr~*#X-k#(|WAU7eD%vwJ`X`SWNDQJM?2sh}b10UB;O}75RP6dZURR z9Lpp-7xpCc35pq2$go82Bm(JOBfe6|$+&AaJyx!M=-_KsDONcAYF0xre(yf7A-~45 zCBNp`RO{4BFWA@dd-hTN3kkNsa2*rX- z{p+U)8GXcXt*|} zET^k%e>s`;G3x~HANQC*pZ!^v#vS`nP>LMsdr} zH&E)WzCXT_4jG#OLYA}a_kEJSsDPWvkyiknViuVS*8$9^}gW9VgFZd*sW21^`@A~zn8FDfywX+f7zisfg_z% z$goCcd3J)mqJHy-XAk#a+;SQ6)l$ySfpg~Nvaokk7^}XH{GP`4H-KBogMrl=x@oC- zWsGHeeGixFoXf7!!qr_==u7n4Rx$IhHqPKX>IsIzuyKnqQU7y?U+Sdk6V}CN?H8Ya z9`fCTRv{j@IS#X~mDS!6VI`+Gt1Ia*Z!H&vrJlg-&J<9e;zinZ$xlMva|@oJPB)jj z@J7=qCZ7v3YmWUHKG)fu4tW#GZ=RmPazytTR$2NuR!yj*#A{!XdyW)?N_ zWfHUAr8&eQWVAdvdJ$0|Dj$#?BJ^I|!CEIG)*AoIG0Vb2O(MlXo8`0PJYJKtKHW)S z>x@?acOqpG= z*dxR&YhedY?W;KGq8yWY`#fOnC-~T@T>+>MlVyp!1ui6f7UdUAhQ@Q&LAV}7TQl-U zzX1P#7s7988zosMuD1dmzY5Gl@BRuPPiPE6qV1vBsM zV;_3--SUyw2y67(bB|KuTUuoop^>`VYHMG<$vQMseVWy_ z+0YSq=wj-01tT`BIZKtl7i0X#y?$pz#0Uh8 zkxkN#EmX^BF0RqFpzs1$N_LRcbs~9zq0UB$OC~KAldt~R`LEbY403AKc7AulqOk#9+I1iN@D@0 z<-?V%nD*n9pwn_Bms zYUBiqXUYC;N`Eu_Q^h``w{D9PFZtjEZxY%n6VzbUTPiqLV3@SeIBg#tF(ZBX)O-4~ za*X3T^74%%%h4*MIwRo3gvI|x*GPG{+x~V;v)o~cZaUBW>-k6*b+*T0UcfZ9(f5Np z?H|PQh0)7f&(D+$4>1w8KBp$^MFVe&n9e}8*2Gk}SH6|?AwJ`6zT&+htwl-Swidf> zvKhLIHvR~?C|HYrRJpKegviJ$U`&H6Zhzmsjk--2*eS~`ifqf^bT(*j;#Q*(RUdTm ze=u`vzqr&G)9+tz`dx3d377r)#N3W3QCDjWv$MF^Gjs7m^^8^Ih`Cg6y^7oJ4hSIp zI6cpkQKnc$*8lhr{rpy|`YrQw?V^Uyhr9N(bUP~{(++y)IQ=QdUzw_B4Ll|;axAt&2-BgW2?$UZnLx?ttO_3$P=+%LX(NU z#hMh8!9#<#!qh9y)UB~c4M7g|blpBW;X(3q4{w{MhVjz9pP>zMLSQ@5F)`&{ReW^9 z*Z+XaaApU~3;7SJZ(5>xwbVZA|K4_aLq*=}v;C;?VKY{ZSvdV9>W+i%{ez$~g-h-V zTQ6(Jz>oR63luCmXB?BYSG!b}y!p>q`2L=cY{f7nu$Qag#!*xCFIza*`h%|}+RUxx)dS4~e_=?=5iw?SD9lLS_b)?qxm3C-rJ4pWiD-@kN|~HO+Kthf0h3M7 zs#NF&EplA0^j_xtQ83J!X?ymo3Z^ce-mEK7NEaV4xm`suXM6L4YAVO#CVQMZ&?!HS zJrlev-Q|eqOniL$d555$Yv=dK4T3ePUfk*9p^4m5U#Vu6`oz!IjUkbo;~K|;7L|)% zmTo_4N>?Wr0$I;T%0|`HpW}2t-~BRO*Dq)Na^Gaav@PPH#HGhgKYG?g>VaC-E`<@# zUJqJP%%auJtK!QH9nt9mq|E9gE)0AFTG; z6pXxXJRk^lZFZWyULUZY6TjZ*LT{aK2eiEz?%iDsbN@6vA!J#dY3m*yt4-^Fkge^w zJn)$-Ps;O4ok!4=-sW$fkf;}YP9Kculgf;Iqe{&wnyxuyZB1HzWT8LK7xIp}3M_g2 zV<@OiD8K0K z_ZbElkQfk^4h2EFOIk{4ly0OOqyU%>jd){;2^ZN(py7n`B zuf5jV_qsoU1h*Dx-aaHVG}o_X7_@R*Hs+bFpJujNS3L)8EjD#Y%yCo4QvTU+0Phg5@5-?)@d1 zex1d;ggY8DHQs`qo)z(Lj*2a3k+qw{A3;Nf#q3jZ!T=z9I_R6c=a1oBOxeWQr;*ar z%hLzpA-X#^i=Cvvi~RG2TLz2o7B`4@e>Sz%iGg_jkel7!H1U+(T90|Jpj0{9hG!O7 z;vt8*0gv~OmKPDk1p+_gM>k9Wm<_kxn*N6y#_oB-?9tl7J3wc(>o(MHlOSS!5M%7p zcBDzdGn2enar`P|uhy&Ls@9}!c;>sKvzglzbc+!pSVpk-7r-nfG{zx;;WEG83{G~h zEz7N&0r|Kjgq=IEQdz)K0kol`YiBTSVz}Mn! zFsUh<%y!zQP1ImtjSj`kmq*?b$~>C7Cg_4o2X&t;dT>oLOe2W0`vt8sROWuMH~k`+ z(JJ{x%FUjTm7|sAiRQz`Qy?AXN zeFuqwbx1X0jvD@&d(yn_$ zlGs{;d)Yeshg5SvdE5F5M7B7&kq*z*7iz7I z!4~toKQ124HsZ5_@5T8ly%S$0*VlQ4yt1^H{fBKa|DwXWMTOP*7v~3@IgyGUl1bIJ z&(7T2@82a~Wm`za?Nck?N?dJC_#-MuVuO1IVsOnnUuj=2l5oib$}&z_Odx|9o$Vzn-zs3=p~sm1Zg6BiJ=G!mBIn-)D1d6^dw2CqBhgb zsmdj?i%Ms8QnT9e>m8o9uVXu&>h3ZN*;dp%=J(%KL(89!a9QimE6h=6PMh37EqCJZ zNwPFl54vc7YE?aW*ulMbJ*Npt>aD983V(sf8skod++$z3cE0%EUI2BC{^&F`vR2(z zBhh?!KMcH+6H8@%-H_|+vii1>W+QLPeV{8WdSaS6?X`{K9Ntr;Sj07vZA!0pM9Ofi zKp8juK2EXvP1wQY*lQeaKDE?nLBY-%@w~QAyH<#OhtOR<)y~6rcG_Tj9YXfTr4+Ndt&SyB1q1dk2(N~*wx7mpFkjGQZio7IeBJWBN_QT?aTv|l%` z{9OHjdKI$tR~bupnH6BRkZ2*fOwVJ#Ao6YHT68P75SYxC(L=)T%I2@jEbRI=7`8%7 z_(iN*t?E>Da5x7=j(_)FQRrskz{dVv=IpJImWgcs-EV#LU@Y~gyFBAfKF%*TRxvC8Dk7YW(?qV{}4D02?ji-%5dXB1#K^KWgy`N?x z&F-veL|w-K8$^yX?;%6>+{wMJZ%I;QR^jkc)F^ZRoCBS!LRumq}`5? zo#qB~bK|A9&``Z%nVd%toA|hsp@gtnAAS7zd6xBFPbK|XBc1I__`~90*nV!vn~GjU z6k~3d($ThP8$1~2j>zok4*a?T_u%71Y?8ak>|=X}#*x#XN7k=*qbyB@gfCrKiqbr# ztS)~X$Q+pAam2NBa%4^?Nxl)7oiF}UZWF%GrJYg}|7-eF)(vw_7{etli0L8fHcjGom-`U3`d z8P%Byq+Z*n(E0Lg3iUoru9ZI$pLNNNW(<*y(X#UguIqU)_0ck|yuM^la5dl(JYAng zV_{A)D*W)aypHScv)yLZ%F|39CDG3=`R*B#4B}LdQ^}X5Nih;tq**FSeow!`7~%($ z@miXHOA*R@ZsERV^&WTQlcYXitis{}k-WdRQ2j<*m=e0#?_}5DfR3W&7D=!0!d{=7 zwZf-?A*FHj^;}HJla=~U4`cp<)U%M!FSWEn5){Pk-F9Soc5F*xhLiaT2!@|+CfIy- zFS|Lc{4C$eiGZ7)DSJD0PQJzYouvNDkGalrS;Bjv#?Q~P-|qbKKqQx8QhWQz(Q4bk z@AOp)w~iX!7LVIgp(j)FHtoXLWN68#rX9JUbpWpA0%@G}hxgLH97UJ`j2S`mjV%W*PnSnC^;w12L_qB+mZ;RJ_T_sDQ8&GXb0|sEGoEdDTkGeWUCYZqQj?4B5@@4b0Z<;dlUu&DeFfF1 z$U)uD|H|Xy>znht6m_y`O^0;Le4Nl{1;#Xe{&2rA4V|y?9|u%xFO*cJrzew^5Q}xU zDq%LC$#{31wWlQ^zjhSuhDTSM5Bnu>wD`}+lrCBs#oE3knFQHt_ri4^-$3spvd6jfZ8p!IB`XI_(K=>i{?6C{uT|9XKI%>TC2Vjz?0#P0p}`|G7eD+q z)J(T!#=Ml!usc;KAJ29{VpfL7@_M=jTV&IG=&K0hf#Bg69y;%Z=cXVT@41oXbs7T3hKbdBJw_5u;8#n&OAuIN%>}Q#iOpv0TiJQ7f&Vl@lW0VLQRBJRg9@R*(ZSoE zu8ZUg;k*5sR3s&||0<2=R1TlF!p;;kUjD}vtikMO=tGh`)+2Qb71UBpySQyb))CvG z!h^<18PWFQ0C8FsOItsVk_Iqs3+dsg<2*|VXSuU4bN6lLmY{TOJyC2o2$HHhFMf!F z24dWyES^zZ**OfSPIEtQ5Px1hyS2W0K*#>jSl#-=c43_B$Nd-PVvf2))z(_TEpZH*(td_&7nj5lON!8I=aNGihu_E{Ti$cqS-`VbBzom&9aa$8}duNI$ zzE%BW^rr%9Io-wTB-Y#4#D7We>LlQxv{dT-h2XuD+ER!59wqBMB~$dAm9Q4CD%5Dq zA&u$_!=R~s7lHOrfv;l72Sv@gRM|MQ_C6gnJ3F6^}KKFZN+(Em)kD{t?ODDp}G32 zaKM$BSV8zG*;ZU0#?IU|AzGK>8#ZliIKD#UeSh@8;Ca$~xzQRDw4kD3q5ZV6TcS&@ zM1V~=%?77$ts^M@)<{U|G8A$2&ska2*%;0OKl7?rB~VO??$8TW9vP&PE0 z4(s<~SvXg5CwsROGPy9zN$KqmN>X~4Q=BNz<=kUifP*QC2z$+@<``RZx^-VFw&9jq zpxra5)nU@Jx%KBy(i~kX4x-7iogr;UXl16{*o@dg*6&^h&CB|R-oTWI0lLh$nOQ#p zE@{fP$4_YUjLvMJo@~(*CYnV?Rk}%S(+En!~i&F|7qBmwo4Ci0#AHH*{rL07{8Z`$LG||l?Iv;(R zc9iD#DN%BiMps>`Sn}zyrJBccA}XxrCyp*gI2DtjY9CZDl1)n|T ze+i75K0}jH`LfYRrrz^5V>?ZGze?&4JP3xT%mdR+D7kljar zu_pfX0e{`@O#kzb!z(V>pZ&*CVM?ryVYAEl_1IG)TV>DuTEW@_bKCu<3MM%#;EV6H zMu$^A$Fr^BI zYt3Aj*E{zW8@C#^e4Tf$s&e>YM^1-Hw^0=$!WHxW+$g9nD!e9Np5Bm~Y@(fzWLR6h z@49M-=G)MLCe|fl65moycJ&1Bt`z;aP&3YwIzw{#oq>3Eq`ZjnZI7(* zX2G&hqc#2pe@hpa=38#>f0+iK#vfIScey6!>V4xpXAMFKnwMN7&Bmr_f<9)Pe`i;p zNK`S#p5rVfR$P#t%T=v6o`T8|_h+o?CcArQOLQeEo-j0MI>l}>YsQ5vJ2>VVnI2)y z?)J+vl=lyY%)eCE`!)7*|2Kyxu>DQnBg1{A-r0ApXgAp4MCI;@h;$Iiqt*@z+ zR-MB|_*&nuP4&zaD^nM>E5=W^|9p*bsaV%ts_nIo!sGBS#)kzVz$92$xSgebRg>vaalzkzG)<-7yQ z8c(0#8xKTV{@VUZN3&(xCoNHd7rgb-g@N~HHh#`j`ix}azw^rKQ#Cb8e%x z1V5o{dZ@fR9Hd#Uv_ zS~y?J2elq5Rz3On_r%8&%b|{OQ@@3+wa!a#U82U5c)*XB{w%vzh2^qwylI+^?oxG7 z)__ve$U=|}R>s}=Zi~c0T~)P<8Wg8SnCH;LwQGyM<#znHOAE)WqWATuvc=f1)%tlU ziabMspDzW6HI~<_nUq>$Uw#^Ee!5i>NlxfM>?m6HCBoDm&BkLP-`&j@8?@6lFPu}a zI_oKhz!k)MvhDp5-R@_z-I43A@hzjbjdSVb&X#0ui~j#K36c%(2Y0syDALSkAuO)^ zTbHGcitlgaI2%kdgy()wmYK=ed0(mrcjlexmu+i!DmopQDFp6oE+iID&vX@yJ}_7r z6dX@F4{|yAqerByXP(99D<2YD-iwAmd+{^Zl-=~-XkC<=_JYX1UPXaXb90>)&aUcE zt92z#u#1+R~cU0_T=LLfinEO_SE16q2D7;~dl0t0s zbamq;)srWV>R^d%T)pDV`$E_h-IdQV5E=H%@nUzoKIc%o-GJ_`dN>9>=z_WQgN_p_Gn+{iaPa%bq@GF`>A7-w$GBD z6K|tW>_tym`r2Dl?Q-M8Y;}7NK3~Bw6T>Vb-Y`j|0<#q|C&AWh5kmFK#@lL+pXEGC zM5SH6vPG%nK#5zQO~1xw?5Ba(;ytJ>@m=hLRnupyv)z;@O-F_?g-@)W@p5bC3hyYa z$&<0?$Be$eYS=+4U0RvMRk>{!B<6V0PW7ULcZ{ZdpYJT;_Lr>8bnx(pt2YFm_?;dI z%;dYo7Pi$bl3D(>7?{Z*nXSucB2K1~sLT4Puo)d_Kb&uZcrw;?e9k$v&`(S6Z7O7h zD;ruY`A6m9#k87t>IJlrTlv1W{51s{^+CKRX^@23p*V%E3P-EcPIGYau)0Xf_;bf+ ztGQ|K5OAH(f&JAi<5;hn*>e5HX%#087F0UMDdI{@kDPCPT`h~aq4TJ*X2lYa>Lms7HI!1mkeO*ZG z&|!|o%XW-Ghg$Eq5I&b(gmm)!R!H)W)|lx9Wh&|0%m{TI?R?5cz}ZSeh$c8VwDI`*598fCTRoiduaGW@7i!qr zrU6~AtWfd~*& znA39&XsXXzw)JEQs>4jIyONu$>iyY&U8}aCmUxPMf2~{ksQcnlm`3<6+F{;=qQWaY z?HYj^;qklXmT5i1Y;!pR-O8yw_&Z5;_y*WnC-0A@urS(ZjwjP{R!5KJ#Y@~nzAs>w zYS*(L^_xGs`c1s5wk{V1z(?JprFC0`0Vw#;oymYOu0}g;$3AX8m1@g~pPS~$x$Pl0 z72W*>-_Qlj1d&;^S7z7j@LP^pfi%OyN&rou%VRIf3lg@|wbDBB()nfNu zjEbaP)$K8+PdIvRa#2Mck&kxkq*YMf&C~ijwL?Wcoo)Hcvj!C6@bP#Rd1kK2O z_e4{hQ{(tX^PoGqmVs!OAUg%kw)X7BxL#y}5L)%~T3pKFxkFJfg0gGjh~7D)m3$+QkiMVo2uA!YkB$Hf^)Y>-#A-N#$#@p|o$kp$NukyZbYCsTxSP}J8E z&&@?*b$Zniz6BfhJBtQO*Lv(fknr$-veBO_F&&CKai5WE|Ky8-n@*2+9c&P^6oO5L zQQXQQal^Ys3K6|>gS4(i3cPsvvfx*FQy-`e3De>arnVM+9N--OEtw6&J6-dxR?7L1 z%43t-T&U>0JK?Z5GD3E{GL7{3h@4hTKhF;Qr>yAZ)pEA{?CAvE{hgV};aQ7Y%C2D5y2TZ%?24I}ePENHo7bYV7qF`;Y=%6Vmf%wGP zykq480IWaW9l2VrAgQSWtnubJCsH>Rz7PF=5&re`wDk+|7o8;zz#_^v3Ag3?5CNTn z%ID9q;4SkaB z|NH2eSxFB){=J!MrK5?C0%ZbX|K-Z3ayj^ew`h`MR&$=)ZwIIIE7h7S)5Vz%rtTn> z)-GxAPDjzXtt{Web>wTKC;J=5a*+QqWw#Kryw;aq_pgWAyw($+70yLcSXm+onRk4S z|L=9chCx|iVF+>sR=8}e9Wo*Ow6d{CM>Lo6wDsvMFhuD&dy(;BZ*^51fAEQz0dBZp z(rCk?$FCq+CbV_~IkA3-pf&@PNZ+o#e(kP2BzBXEzuTp@|C;?*Kq zZfgzykmuq5S=BY!^_nBB5$EjXCch6~J3k=0WgyGHzRCY?pkz`QV<3cc>m3DNa6RTA zK7ay)dx%cO;s>LLxJ{hEaB9D=pP(v|?JO3aP;66+z@vqBuVrB9VM=*YI7JzMbnvr# z7hM48X;u`j>`;&fFl??on*6n1vqDb@g1*8m_(ziC9t`~Pgp-Iyd6Uduf()ef4*A#e zgZZvQ=+XSXC5Pr#(^c|Ba|iV#5i(!ILB1&0c9cE^BqI0Ie~03~ZT5dJ#4HRvKz<{N z?5#i{G6@U?NH*#<)$tV-N9_lsl2xTLG3vq;X@3A-1~|pJKEhCHzr)0);$m=%!F*pI zLlW_r3H0bNVFcUJQrUu3CJRl@w)TL`h^?W*vu&_-E-@LOeRiIZ#TX5eQ0MuiYK0Cm z;j^|_YHMXsE8U*hfqmfeG{12x2Kq8}1^4cj$pw+r;53)HuFB#iP+-sZ3bXjNU)ZS{ zauT14$ZlfD-HnWlGSbi$ffra8ry{?7S_$Ykw0=tkmRPAIpjCntL=B(ijzVDQc&&gSHH>4k!%Z5#hK zKLx(rvF26x->gq$+CR} zddLNA{k@@o#1uX$pV#0ZAapVi=vmyfrw!LPDC= zZvW5Y-RQX7svJcM#24D=#D5p0 z?`eMO4hcLw5S3V7fd6wi1{LV|BMHd|06eYTU^|Hn_^9RppNBgD1;lqzxpGfX0ZWkSHPoOCO2jB)gYj{RQFn4Tzy*BRHhW2h+hk>Da63cLZr zIUy}HnS5~4X^M7DIt*6Ko>#YXgG}+Ftvx}Ur{Z(6X(Nv@kTTW_YP&A1RB^7Tb!y-_ z!SmpIZ1fs5gUL4hQ^#_{5yK%9+kuoN zJ|PdO^W`zS@i!lS`Q8SX%L`=x{j2O!JJ}K?{N<+e*Tt3q;+NvV`&;SeYL7B`a-2#J zbvug!tacCeX>CNhthKw^QCf|~)eYr_KfH#C8lc!~PVTAkg=Er#umR=)+)*+IXf#j@ zdV`TU1!1PT(Kv!Ei_SrQoN;MTXyju9kZiyn_z3XXYkgP_PkB?AXG8F zfVTq#7Nj;P`$}62u1g2ok)*sVsODs&XZH{rk~up!tGA$mOisdU2vKL+bFaLml?p)P|%1;E8X7n8$8%% zA{2~jH-~`;kptGuoi)WV*d!==`l0{pcKqMhzuyuyWNggm+u-A+^n|5fSpZp0RhOA6xQivon*Dqi?Xmde(JFXE zdMj;CMfD6Gc!@C*G!+~oY&G_0dfhOw2ZUi%@g4?H0hPg*!7VTX_-if@B~UY9;MWT2 zZT_|)(@;M$j*i0i4iXK-FQ=nQv%%h*19||ddBZy>)rA~&?{Gc(zU~k77-kv@kL}l> z*r6@ac0dl`g|nmz4p@Nn&tFDRW`_%d%K;b+X^@{CDHxw4XaN&}b0H**e=q^d$5@)m zL4xMHJrLNOEp28xy`tVO3M|(opn`0knr>T}ax*8KVD-1O0?f7Vjd(p_4%GbJ!~dJi z{;%zQz2J~7k&hsGwtqJ8!dU~MUC=2|X%*8n=h2aDSob}-yg2?k1?H#@IXjqbn=NM& zW?DZZ>kRm}L9aD`xM{urh6fJp^42z0?6`Ik=# zz(L#8I7QU!XvYrF8G}{w-Z$B3h*D^N`5E^}l5(C@V;SEv4nPL$28sOe&w`1= z*u0EIqy|_p^*kgb0yqQFfDb-1Z$K73!)TR?FN%maIXkD|bjd%j}GEICK?e^NY=WXziZCNS{0g5UW1pnTit2#ryJcQj|TGof$SdpZG9a&{27-P z)r0-zO=iD^Z%zfsht)rV#wYrns!Apzt#6A`^_O(UdC>$%jN6vZ70D3ivaU*TU%1WK z7pGA((M&lze37Jp`JiLBRMg`t+Hg1Ir0uip4$$`LDhQL(_r3~@V_w3iAt2==ijEZF zhUWZRC9z+zNCt?_GqU|QapAyk8Yl<`nW4rR@uP*_0@PdzFl^jD(co*Oy@mk%2R${Y z`|XHPlt$&1_Em00L>l>jk|AIUc&*){zmdQr+YE0#eHmF$- zP{>ezYtnrrp2E*XlMwH}Hi)pWwk|#U`ixoR|5xA%Lwpp;-aVgHxD#&v=xaOv!Bt0u zYT~>nvh#e_vGpaf{ebNWD0=d8j!?Bg?oPC=zO(k^A7s7y1$|Lsq1ygDt=3s^*_7GB zhy4BjM9`aNh^0j?H|I@Mg2VCJNdnd37yi<1gLV#?Qqk*z3`5@}2>W(YhD|MxE|^s~ za~0Vls~0jhPYn=ygPOe3u4qZn%pRw#dct6ypwBgqvY85m{^wO+_F@MHC~BkOz$xKKWjD}+j~uHZI^mK9aNuZE zV5RT0*dzcI%4Sf!80kkcSPZ9WXf0$}f@C?t`;1MfKqy}vtEtoil}bLHZ`6-O`daYX zaB#h>VR82wg$R6~`wvyry&85@>W{jOG9_3>1|zAAP#XfzI$_RsXuEBmAeuL^@x*a6 znfSgCI)Utt@due8sT$W|iBVi`7}smdB#+dbqVFLmnJ)M>s@r3%2-J5M#vT-vi+11t zNBtJhuf%i;dRVJ@GXFlvZ1XvfOI+?>XuybmbYzNEm)z9f*#oZ8%`DS?le6t(!Gg&h z%Bojlk(6$Jrnerv(5ThJm0hXFuo+fQ|%czO`yehJ#T7 zPq}Rnjd@=3M4mUwGKdDy*NG$4ng_oD(a1dtiHfE|L=9MWU_^4Cb2JyZJYamHXcsY& zQ&2dF$WQnHWgZ`Da|({;v7T9$qXNO=B^`r;LVXNnFhAz&Z;1oZ+%|}b6GCB!WmN)E zEK0x?RZe835dvQ(H~59@v%JoXiE_i0d^JpV;3VFmuY9yKbtBuK@t-j4TDI{P>lA#b z!5dUU0}u5FFhBsCpIIw;D#I!=7e=8yOLnWS$IgcEouD&jgACAt*OXlJ{w|m!;NKuQ zkKkz?fCA$G-OKGwAP;PU^G3qxWAhKNF&?y1LL2Q^bWb`1NzU-w4CjPJ%O5z{uUXKv zdJJ!*xPaY2YR4guBc`{a0xQ277EtcrP>Nzhivgxv$@Y+ z2f!1>sRjLjQF3VlxB$j7N~M?{4AajOpuYG_X#kFv)*E;M@~c5uD<-A?UreK82_`gi8D0R#PVsa*1d;RiieZ_H~)zqkP#}* zVOu>Pygss>9fFWvy$N9j(2I$L>A0T5x z7t9~Wu*jknYDyCT1S1$4Im115cMtJ@1%X5HBm_#pO{KaL0-qkpC`5B`ZzY9bANxjl z5dM$}WFy)NY1G#K-LbvxolOgJDdfpg{e3({=cLQ%KfvptmXTbL=?>O|ZB}x&K8?JF{MnKzM1}OE` zME7EQW;wi^RK&rcMmSYSm$Y3tH}|FGZXvq7N|nT=DO-2_;8gVMtexIi z`$D_^OC4h;;Zks{V|&lv&UwzQ%xCTTkLTz@lQoUc(#4t@r&H;j^>aNIW;Lp?E_XLS z&-T-N3E{N&RO%19*L0^ODN3stC>-k4zL*O0<~Et~5=uNSBauMe@NVMfbfIXd8fUfM z=hN~t`({;^V@a;d{_!E3XOJ}SiPyf82h}K7OQZsV5Gf2z;?U)9Wq1C%MDmvTR-(Jr zb#l~*LE}bmvP_vpseg0ecTqY?*Am(#kV{7IO`>x4+^vR416pIOXH!}Fd@XLoe1 zICSl+LMNxu#P7d3xGjEQ(5?v~>B{VTLam_eJn$xq7vn2gy1rfW39S3M_};mBj?}f? zA+X)?bO4gla0()orB!4<5jdKntiEwO>GlYBXCzuT?tu8wt&LdZgR$K{;-MQNX-Nu1 zFdP_Uo2-mfbZtoB$Vp%0KbvbfIFnLn5O=v_T-d=h7y7Yd9~%4?0HNXlek{|ALT0Oi z*&hV5rL)PVVK^uoy}_551j=v>EzVn{#UN!WEr1KM|67kS59V{@N0xp8Vre$i|B3du zn^4Sdf?aqm#w4I3_mz84t4DLvW$a2>14e8)2h9Is;(ItwG@8L0Hsxsf*P!08=tuXK zpy2qok(?(m!$bPu7LapK8mCisRCvtmJ;u??6K5R6$Xd?Du0h$`9oKn>;JG;bu z`J_^azFv8M$xT>!dzK8J)8NQ^i6>w?r4J>`-0>){NuR5$s%4Kb>W$kI1z+ZbgnVAR?zpuOW5r1ol=OZ@Q&QM#GM!5idL-~^ju!_P}bRy!YiAa5gybDp;WO z8mgYk=DeWjzklp4{7IJQBYe^t8@ol?o;kf?>-x?yzTK{|Fx>pt?2%AKyW(yx_B9)+ zOgZISk8}b&jl53Oi1347T^CDAl1H7QJT~WfC}akUe>jLs$7AE-7QCYP^C*W{aoJpr z%X<(sk7`0|dC-1^0 zr=3+%A_Q%%BEW%5dp-gJ=AFjnag#yTZg@FspjWA~dI0qffVW#f=ErFw2!P@K!lK{( zow&a6$>)@f6bSl<_sPDUB^}g`2#{Q%9aoH z$$rnTMMZ^Ra^`OC#aL9*ZOun*bCZMJpf1!FNz)#nQo$3Hd>$pd@?%^7=~mr5OI&@l z;-<7NE$z zWY8v71T-f)Bsj;dnNLU)?dj8|#PquTfQN%2E;hgE+S;FSa>_z)FRSVa7ev|X#el0V zr*cQ{sSiedi#Jk&%;9A#&Ie0XV_hb^3!YcRV;{HR0u>5JlMdGXX&_*RQ@K@J8ZDOE zZX(*MzgXT{U{J4GuhpnOYbyEH)?WyX{mX>WN1a!CSu<4=Ao!*c?@#WvDoI`P64JaI zS>6)Uy=pCP_t=U53<-iGuqJiYDVWP+z+1n{{NMxSjGr5PmNOezFu%yf5qhy$s*L9Z z;-?B-83`%4P`VT1yXVyL|ml+pjhhynCC@4lEJp;SIW@wTVOJKF_h9>f+z zOlQ?KG6s?mT<-4d7dtM`2~>;nz*sN9g})$SfJ2;!4C0Ys7!IJ^x6E0P)YhVYfSm!(%#`=_Ik+jG3NCH z*gn@#c>FapH;^jSq&|r`(LrV~-LmLmqL({09I75h5^)}=`BZ=$mOiJ$`lYJkQ1??= zQ?Md7~W)bQ5!eJSw#WtOv7Wu+Hjoxf!l|fPRBTz9MM3vAlt{Qz6 z4a6RT)EdFdNN4do4>Bl~dN)MkNGT5=_!(o$qUhk-{7TcaXn1N2U)fTXZlrA|Y^s^d z2iHbBTYa_kz?p^p{fQRG5kC%PZ1iHAlESu>#lSh#pIv%jzc$80b8%TsOh)rXg&wsj z<0S1v#ovF7|J>p@SoKcC2Xw4uTM!fG1(VGRjavd?Q3CgoI#P zhEtz3Td&aB*2ru7V%_oftD+L;^R7;qoc8SRlozzW)U>LAHf*o2)p&M(wT})of%5$Mw_P*-LvJ>`4L(M$IDFo>RG8y!75od)sQv z#P~dgn<;8p3dRIPNkT!%)(uYeC)3*Wg6dtJCWgabD|~wVPh_-0H-%^QWYuXADZ=eU zBv*dehV5boaTIagp!dT{*JrNX$fjYP4KXULBb-p}x3mzVK_&3H;nNqNj${sPWA6s~ zPa9Thm6sH?SIjIMn5aLF^%P`)t3 zuSeeQpdT^Fp7*re5N2(N(2c@%miS?PV}0?GUh=`mmky?o|jCNN@p9cKD`L_Uju+*Hn~@d_Zh z4^TYDXtReNY1s98$h!fGAU84{PpXkMYBM*#t{5ld2%QxIuMLXZzlKep7LGBAJ*x95 z;4Mj6>}&jsuW`??;6hZV{!n3iU}=rfAT|mIX=6%b11x`iF0(YGVxPqr2`N04%T|~n zNsTgXwN^*hVIotM9bNT{;5P4Ru;e}elpvU1yp_s5wGS5JHb6Xk_(v-iCf&U=`nm=@ zPNR)=;k56qrF80!lToM8DOESM|2pDgzLZ;P@c*LiJ>%j0y1wsYM(>g+(S}42L`(FZ zXwf^-C3^3o8xawmXwiw@+aMT>h~Dey1kuarooD{%{an|5Ug!0^eO}MU9M z>-SyE!o$+md2t7av-nZzyfUftDlxZ-H8`htnmh`EWBMKIjuSn}%+twNL)$l9Y_H@L zv>H?ng~_tAc?hP81BW$IXPYY)iB-yC>=noQxCZKY_r%Qi_{Q@aCe%ADnMzgt9u4l# zsqCEV{TQWsOjFpISmxk{v_T+lQU#r>bQ}mmtD+-s$RVa=d%_xn>!8gGiiQ-IAF+a%wS-*T`qYI!n!0w#jME1;??ja;;fT ztIUf{ymDsi6>7;-61E=--*U|0qWTaYYRB1 zck%zF%Qy1fn$02L6@Fhmt4&?_dQjN9H0YDj@s;)T=HKm?ta^_uIdsyyY@B!1_67|z z7VfM3^Lr&UlCk+%aa!Mw1;IKwjkN(l5|EdEt;H~1cao(^V%zZx-F}g{A4I~J=OQ)O zY7e6kxV@f4YQ2(3PaBx#*6i+PMqX%_DE?>Gn+Ll=)pX%OBW6${PvfFdaG`hx3 zRi1PuL~%9Wv+3bWHV4nP*aq9of6x-m)2n&INj5rY#8N6_y}%GxC|*GLXQ$cW?9fa) z(m$51Sk6p=8o@VG$AOq@!6qetP&yX$Git2|VS!xjl>cp533E0p;tH5V}wU?Ua zG{++6&7cZ6HaK4PH~U`eZWk=dd(n&rgI>**CIa5=>eFrMf4_EQ>><{Fw^2wxxHS`W zD_rFibLLnb6M*4E#YIyh2p{k;{4(BuE2Ny_qKZn)U4wg<>|F|$uW#d5%=EN zStfUZJ%S)uNKiS4&oThP+)AkAtdhhs9x%PyhNL7B#n=YELYDrd-~<%Mpg$%akfy?c zx^4fU{Tugc@9@lW%?woVGm-$7@ckZq7RPsEj0TuuGy#&EPkp;+k-*R%Kj(Dq1MbMK zGbPLdNlp6?X3j69tVP|!*=8kf5FfXa&@2nr+V@2%`CL^0@MSc4i){9KK5c^*# z@7L!{{Cz#0nT^ODj5H=kJx>2NTzec+!pehXCFW%EQ(}#U3~wbGdi{u7J^SX5vMs{L zlju6MSG;O41eG2l9cG+T=T-S8ndc8rOY}0`!6B8AW97WY^XJTx=?V0f;j-1yCR3*93nL`V`PyE8=1^vev4 ze0>S?-McS#eCA(KAkmVM-TAT8W^%)SayL~;#)~dh_DFR-@{6;~Fw(=+!dp4LN$a$U zE!oA~Y$UPgvatJ8SUq#3p)PxYtW}E#=_M_%n#3V(S4MiMLD7l|&{lX6#?b+i8EPR!}-~*G!76P9Ubf zn#N$W@knr-(2nsJ9P+AC`c3hdq72kPyGzqi`e#<6d9im&X3*QuJnhh3~ZkUI62_A)nB zlEKOaB(&|c`cMxTML&MoF?m^_l-4lrHBz+%u4`?m_g|Br8o2mEO>te;RYqL_mQfHI zqxzi-mA?;L4Si8wqF|JCSGLv&|7Lv*a`LzL>cFQ8jQ_~SY?G;jF2*yTMG+L}Xg4QY zJ|$jx4?R6&wt5~X(v+S9$BGv+T)5X__0(gBuU3J zfBg56%O6-~1T~4C5UgHeo11~VjSl_J7G=^C6n{&9!)4xEx$_bTJ~cwuoGBNdfv}af zQ}S}7z>jnG+G&{dCD89f2}uztHP+|PLvp7&rTE_W|c|K1$7ZT4kCwVLQw;*Vx`r!5A zKT7q7PxVW>v){8B$B%}^|i2QD8YQb$*pqZ&rH!;QB(0|`zOs?Ee6zz7B9AP{%UV2x4rElAKy(3KP zY<{)km+tf8W;fun+ShJ8B*!B;mk zbMDiXF@B2*5&u{Q#>g<0MOL^|7%}JV{t!`%bh4h(!9FF#>J?88Y5}=a>b^rcalf2c5ad`?+@7$GLAeverq|w>n}YS$ z>Yl67eSCg%tVX^dBaO^9qS@Nqa&QR@`!RXh#2t`aWVw?w;%`YG{fd}_p)~9Kpni$D z#7L~{rjw75Ownmix)fsVbiA2X3#C>h43YAju&uJjk@`{*ar=N_f{L_Wp@vja%gGo^JQYybJ9_+U&w zPW;b`yY5Qf_$H3@QvB9CD%aU=F3VB_)keLJV!IqzN4_aiXk~U<-Oh4;3E6F|i2k`q zg;Xwom{Sjk@3NG>;G>4?2U2oP|A;*m*m% z4}wO8>dn7ImT33a(NN_UW4>_Z76&3|Ve!>p%Cd!R z&dQOgrxM%LX^nxkXH|}SmtRtO+G|cl4`NEn;~Y50HuE6D&hT##ejM(=nmUOkzq?k; z`mpf`i`myhn5$rc!I#^sG-15*C7wJ7U-(;9$fk5cDYDB9otF;6qMS~WFa z{r$3_Jw0218H=tKt-|c_!7diZEi>v<;CuW?T#<3xTK`JF^2WlWT={o_=WX-8G9+*1 zy>!64!_6xNo= z#~)tMPEg7pum{%l3B$}<@F%_B4AYoI(=C>l%X4jQwGwIn98=*deF!`VdU+;ZUGH=e zmWc(XXE>wBgWG+*baeO@w2SyI0$Yus-IDzqEUT)F8{Y|fHF$mVR>(-pye>aQ=j!;j zWK-GQp+9zpVs0opK7=5gVF67Sn0)aVen!8Tr`^@;s$7o2Om*&9b-p1lrSG!@_U@oE zlt%J9uoCUsTc&cn8E)zS=oPgH5t9n_@l>o_p^PlnGFZJd%$oE$nf$}H5epCFx92hF z?fD}6u}OniG{@GxcZ#&&`-Wm-OShz{OJddcTL~2CDMQ3_c@TyxolVh(@_2p`aCxh6na99f}Y(MRyF`H z8|ORl+ZnsZ8T*;WRDZB#R6T|Qh;0pL8TbWoml<`;ZOstEc=-9Jf)I0#&B~*rqtTQA zlO%=cvk^=4Pm!A>ewZgdAc(JMaA*%^EUy1ij@XIhQ_NTYQ8$Ff&`I9H@Hp1x6i8uVP z#IyA3Sc`|rul~MlaIgFQFVS}W{@c-tH6&lnxasl8d0W#MGm*s(4;Ecv9OJ{2sGts` z-uMZ#Y*47{12(miidm|j$8;i+I}J0&KE6{sEpc^xW!Hvu{k5tG9@a%eF>TiF=ihOZ zuLW09)wR6a^vViFCMWG`AJ(0BGxnJMgvGgn5os^JD7sR?p!(7qW36sF^!gZNgo$rq2r4^ z$LA)>YYmf$R@-378o%OTwY+sGHcr`b1Z zow(kC%zn&>FRhnaQX!tkuIcmuni5`~HL3b2UfIsUx8`rZ2dza3D#0mYQy39GDI{PM zRq8hW!)mg@H@%gY_hb*pXacFEnau1|fBxBp-Th zsrj(uIXtr6h4i*ImS$nh15!uJ$lI09a`<9z;in|yDz)9aqjP@Zon|YW?G9D!3mf0* zLBE>nTyHjs3V6^WtAp^?sz+MgM>L3i&jiW)e$ezerHdM{Lk-v1mZ1-X% zQ$KRr%OYduVapM&LEPJO0bEfzW935O0(?VD<_LqXln$CjndZ}|L#z3YTZeS)DG)_# zQ-xS54X^dUv+g!DEnq0}=OG3UTkO59On0hb)7dK=i8IbwO9LGg1g#$Kv(&~c+1=AD z&ySxdUj@n+e(Fgu^n29uG4n63FnYu#_NEO>#t1X!RxBInyv}oUuJdwcGt7 zb}!x@eq%rrpAnKpOeeIr@#&~Oo{L2_h3@BBpg-|VvJ1mOpk83$`2NewZbl}{Or^V& zSd2*SW2x(oR8CO=u*%2^NCnWdF8gXVMy!SjWD|k&)F@)HNHKqA|9#_z>&!^xK(dV1 ziwL$$$NQw*NYP|3gD6%+)&41@7Gec62MLd6h6F$9dmS909(|_91q7dc!+wjk8yh+} zBj%={B- z`xMu>DjbI`8#+zO_v1Cgr_bSrWF?09u(#Wzaw#x?5hDW2qyS6hH!-yb<(-;9yJTAYl|n+yvg-p8c69PL>s z79;~DO*?#v<1pl174aNmI{WP;K@s4z>6pbOI!_Hd&Fz6E7M-fp*}QhMw4RM(W~(XQ5ey1Qq!&Bj&zL{L%n!ZG=bg*8>=pZ?m2i2yMzHs6mmJ^Z zuamd0UfiRJ2Xk3fFAFrg-bC1rEBj7M3f|1Q=@I{pPK6-{7wo0J-p}W)tKb+2m~S}Q zr{=lMH`TaSFUY^5c~+-e!%X5e0QWL*sMDF`*H)ox9V_V`3~$H~P7A$C@>x^xv{(B> z&FDY_&luTKnPb>sS60~35w5GhUGC<)NgC3&ut5$+KL+_ne^oNE_c~LvTe|X@m_0|b z+pE#y?cofMEWekCver}W=B=lZIER&ArlKXoUo_{Is~}lf)WN9UZz7#WzIsukDmV0O_ z%PW{U0yoN_ed8}J)53^m$mJhQj*>xv_NnpN0&c0jT64(zUqUVP6Oj{V`xhlCXL>b6 z-DH_h!4jdJe@iJ zbz6TnHRIv#OZ4hioK9Gu(cz=nn`?uYlUzp^-y-B%ON{9aKhWHKJ~ew)c!DR&`NOs7 zKG^uI(Q96u65Uh-T)%#XyRffzMc0Pwi$mn8nQ3QLOZm&^X!i}dsL>TwajRhCz`fRKi_aUiP=$lhtT@ylcK1MKw``VhhfPX@}1bWi*KIo|- zhR#nFv=BTN!yJbK4qzz+~uWb=uHz4S=r3-@B{j-uI^s@#i- zzD`>&l(;SKilINS@4~j1+A!55N06%JIuzk}0I(`L#1G>MJ`@VnW-aH7nF{WQKHd7- zFZKp7S&PW@LK*uV$`n2j9#5)ly(+kWE}iLJt*LT=%>$~k9j^wH)cp1$@1MCE1giMQWu2L4QAF_YT^ZzO%H3<$~oP1 z%YKvNuQ6HZ$ZFa2^9*bpSLU#_4qgin^1bI9vvGzIwf2WRBoDON(c0lF`Z3@d$DXl! z=M_T1bt#>psrhsVyjM+fK3&@`?xp5NPt)WCfhjc7=MkKhb*)s|bChlwPQE%g8-6*S z%5kp8GP{h)C)(snv#;3Nxvot2y+-N0?J%KyhG5R`bDh7v)o&FJ1s-9=`+=!cx(ANO zY}>j<60?-oeVuh1io(h?I#UT&s{7RkA={^STEq1bNS5)%@hVRK;S@HyGKijgkrS>SH)V(-He09Tyn;?_6)>#@JH7J&p?}ImDEDH)9BG^7M;6Yq+l-o zgVMVph95Tf`Togs=d#*MGS@%on=NMpd9~Lm9ZRPzLI$J#ViI@8<2(`;OeQOxmb=T} z78P(c`Dh>0(U?Dg>sG3h+LOxqEZl z7v1=K)y&F2lm%bJ&^UT~XLWak=99K+B$8cN-ka(64VeI`0N&IO@>ELPY12LWOls&S zU$lzv0ieI!i*fdN;t>u47eRD^f%{f26CfB@36kZv2HB(FaU?E5m`B*MZ{-?kA3^y= z%_MQ4_}1W;0KNh<~2Ld<0T1v7)<*s8%_JSJ7D=px+f>0wE=zKKY?zQ2zV#>5)6N1xf{`IvP zWc0DKuH}%rty)u~(#+&?Q7e(4+3m`Z|9&TN{f-P!F)tx-d8*2?G35mV zyX)o{cDrh^hkvT=NR)v**Hfhp(>)b;i?+jR8@d_pRT?W9Y+uj0tib8>xE8AUgUNO9 z+>JS9T$q7&Anti$$yFTFVw-bbqPO{O-s!Hfy>nFbl610|XXqEg+YF!bOXcZ!MUhUF z3U+bHaB_gj=~Y3A1lQ~R$u?wzu)jf0>twL5L+CbNQEQuaR((yr@3o;8(xjtN`vx+X z-*&ezEU+NtaYFT%pbYI7!?Ju!)qh`=?EBVz7)Z_3UE<1hd%gNSYaD;cggo%;G;+8z zNn(yM!oH(I!Vd@F);9^A>Bn8NUbnTcF>>4hAL5fMYs?9VEH;TU8^hc= zM({UN;4)bnN>-hjOA^nC8X$)f9pZWc)hXAA2R4TZ9O!vBZ7pg`d3c}dqU^t8aK9%( zLJhYu6F{XH9jC+WOwEIMB z7BI!;t&X5PhYIX6E@j_V;q}PsR%jACV82_nVRAA~h#G`RXnItKwSwReNl1!Ft_vhv znDJ7RuS;||`gGcx1GNwFkXz9MGFR&T9ru*0{+cCcW`Xy4^sF8yT<*UX@%r$cviY}2 zw)^8t7^-IlDyQ5;>lYp`koy=Kh%e^H_lX%)PemM2L9p>@ku`;mnVt=tMEsXRgxl`i zB`6trmW4z6Hd}#R;m4krFI&J%^isbp!&l{xMO;-O8Jk3yN74S)RGXOQp!bHWoje|IM7ATb;qmn6))I&qK^Tb*m{b~> zoiQ_kWnf)u+G1U6IY|2q2PL%#4qvEmtc#j8frkKWNYSyIs99+*s zF(`LpJ3Omcd*S7FEUaMi$$g8<+rIs%ieP*%Tu~qjObU>}Yi}j0pp^LBrp_dhfSy+h z6fk`OZ9>#`B&d$XF#|7v(9kb9<3d!stNs{*VckC5`~@IC@H;f!;`2{<&!cz1uGeEB z=LcRFRZLL1T^z?MO6Zfoqi4=Bq8P)EO{Hj$o;Adf*ZW4ZLkYGZD6+*S+B z#_u6?pJXLPA3#zHfcE#c9z z$dctZmgvGgi%8xgN8x*6!NJC$N~kV^U9Eu}Md}62h8AZc9vj@Q#({7FAqKD)BDaG~ zW++H-s)*;S)$*_;gybOvvJ8D4xSa92D&0PM5`nM`Sk#*+($Ar60ktx~y5CFM#(I<4Ji~ z{3*Zs`P>;HH0RtLkN1M4XEPT}d+Fb?(64|);TP4wG$V(^!x4&kIg=w65_sygvfmY9 zRj*jUyX?IgdH1cceBa4lx&QG~pwDBDY|Bqj089kqbm{)?yb1M-fG(To>ju_W5GZ6F z3m^Ly0UAN8dPqSd}yiD#X0x+)R z?{QU$y%m6Mms5eNJr{r4iVZ2N-5Ul1)&=1n3w_IROvU7}Wxg%<)+rx_J%Z zS%z}xX?xS(!&6dIw|OxMK`JM*^kW#dlEMz*lu346EhpdD>6J%GL~LG(R>7ZR7zEuR zR8|gG!9esr?fAw|2vGED{{HaaePbEzD!S5Qt)NFRo10M-(1Rgsc-%mu*Mp^iYX_XC0I9L0sA9W6 z6c6_aASEfkEVG*7W*R35Jo>xRxFVycn51>h%a?r6aESAK^~ts*edbToa@sQt9q8j_ zOh1r;^<%G_A{uT&%pW4c)zt(L?fp^y-y1F@UN}K=l0-yVzWYLjBHo(($eyfguia_Z z1pbwD*4pWyXs^Sn2a-nBA3_X}xX-XnDHUG@0H}8Me{B1=ZBSK-4hQqPovk4kL6o>f z;Afmam%%uXlxQrjo^yk}01qgYWKbv`gIUJ|(O5vb>iC%piUJG3`RPouxl9call&|i zL=1ej0|*gvt(Xj~(1)g2q#xuI;P1V?y@!smxF-+kIH7xh zY|y#DqE|jD}$_;u>n6(J5={S1AI3`#W}gg|(f ziLKusQ}ZW)grSlvU{=Xqx{vdJBtvh4qZGkhR7m1b;Cc6)kF2KwR%S5bL}`2As{Q1| zz2OOgbEKu0rJfX(Qm;kRbI1r*uBS8_(K(%HVh9?Vj_A2q_v}m*%N0UmOO?U8B6kw%N=&D zjCLq?S7a(3NZT!^nt5Kp(U`0G1U*IVg~L z5=^|gMAxg2`q(Q4NzMD7+ufkO;5)Yr*=C2`*U$(%B?I%4=aRTEAo4fp1Io)EiLofb zpY&ds1&I1cYBZzuB|{=e8bPeK204VfWXDi06J^)pku1KZ|L;`tUrXsB10o6X$Y-)` zCl*x}<%V4BEHpRF$&itP^iliOip(x3+?rM*T=T-wdWSjTcX$QdQ=eEv{V2RJf`kzy zPHdLB|E92ixqZMt>HKDP zqFiEC9jjr%GYvxm$~;~7h>Kwd|Bwv)#j6&DT>~OI@M`mmO}pwPQ!DVT2_ry+7Lx6u z9hdZ`=K+5VNJxjn47`Y(h2dx?we{@Gl192$ye07C>Nm*=?D3=q;y z1wg&7zn9=*f;sGUYS3^0L88DD5tAzhoR3~dsJxZe7P%RUTrCsc7^5)=ANghBcP1Ad z^milPSZG4Oynq;TXY+qXqh<1ywSSR5hXr%nc?|Bu@T4+5F34oABXx?KyVM4L1n3 zWh5r#oxlJ7e}3CPPt8_gU4Bxf!WSyg^}qiebi99uCHnuv9|uWE1?}zdS&iXlxR1%l zq066xdKG&(Jy$Dx1~40RQ=oB;ML7f_P&Kf~iE$cAi7Z_MW_$%LcAlV|ZIw+Z|5t3k zdtP;`Ok_VJvs%zn+6?K{a@;5Ixj570b1)3ok2WaExQ^d29=q{(4CfSd+P`~kc_^za zSlJvPCf6jvsMyG5zFpyVUksbzY&uBf@MWD@I*a*=^8Yt~{y!^(qSIZjYrI#DP!5*U z;Zi@)AxfvFG;*&V680>7IPN%_6&O(NlWE+%={!*_kuW5r^JlD2K+?q}jGDKo37!!+ z9DkSvF}^Ii%6e3NzTyU3O*!V>i*d5P$rm$QH)u^j>B-AilOZLm71`CR7b^Oaw#U2J z${h`lGZ9Ur#Eba<%kFmdZ1ddA%hS>^)PM#+NZ7d9#6unMH_FyJ{zZfUrI(}b^Q%=_Rb2pVioS~^Z6TG;umYuc+vb(2HjhMln6+6JaE;=+*E*_U4~9F zeP5il_)O7C#hLZHGtGEBFNkF;K~n|E%HlEF!Jo)W>uH;#{3_#RU~5-~dw4nrOVr5G zbAu8Un%*DLZJpSQ!j30Fya%O~tz#8N%`o3{NB`SXBu>rqtcHJ&EdQ*;@-n*`T=AZp zeP6*wcP1&Gb1bisvT;WQc`#ABW3@f0wc6RdulwQpi@xBI=)#ebiCAW{hSk2e>bbFh z#L)Fee}|^F!o@8A42?jC#U@If?Ue#1{mbTkYHtraqpKM@;k^|a_iH9M_oa=FQ?KXh zgM%pzHkZ#llu`d`p>h24ZD2FC5qx#aOiaME^MmeJt?Q`|~L+yH0 zIY+%WvI^aNqbDxjPLO1h6kjX9;=8$18r*gbKcXzyD)@bzC%|8Pj^|Xmgr&PPNppZa zn#?%STcpjO*3xK+t&F1-7#UKf*lCT6@VI8=vOc{Wt8M$K&nnUOE@VcaWb_PKBJZo= zEw!_m5{fAL0sQPOv*CPK9KF`;o z*Q7Wya;a#Cs?!I4JDECV{rN~BVv|;qZe@4kF5ko{!3f8&g5kOpe$;9Tn&O~i z*-DHuIGIU0O+626e$ID_Mcz$r7a9RKFxkCPqK99?W!)RFK2eCzQ7vN1p2qMb&+C`J zyAD#ucF0pfrqMX+bIoO2l#ADHkJz_Rerl6~6m*|F|3RxxBw5OJ?JWl%?LKniJbKX)hLe7c6OJ{7iwLHhU4& zv-!yy$0%VP`d0bbZMVC#{V&BEbL_*(YyWwiL*Y;GU`0#Z%eKTDn6$m6EjErgV6WTn z<008uJiN{LSa$LC%5wox&YPZ>mSs?(itV9W8Qq==Z|!^M->kf7pZ7ZDqm6!_U|k?K zw}@Ov_q5i4jT@QIx{(LHcihlR(q;`-g*|b(t$qRzxBDfw<5va*-))EQBNAM$SNkU= zu%Y~dxrH;=UuS$HG@9g;Wm}Fs|B7?VT1g5CNMv_Q#5uAJ9?&i%HtOr9{~Fx)xemGZ z2zD?Z7yowiaPHaq-UG^+mJH#syDz%H>}%3=t5Fq|VonFrM#r|mVZ*HZ(mCe+ERV{z zyCz}x%_`Nfwu=>l$x_|ne49o1rFi~Aat}+o^CxP4d+T9ZR-IhgsFT3E;d^ys&L?qm72yq1dt^oG&7y*icg zby{jQLIW5VkY&X0{C1l!Rw6uhtGjI{OSCK6Mj-V5CvkJ_XHar`s=$jsp*Syn|3`jW z`UF0J9}kJggz7zLaJyDXOqdo`JT9PHDzX&MUC9s$2|VzknM)!#BW%q$8)*B99rfGn zUg4`%_oStrnR#;8QOUY`P=4nwj3>Q(%XRq5j*RiOe08EtS7+zI@U|WQjB=-ff&SZp zV5e{VmS2`;J5%en8!U(Z=#h#k zQ#a8B=F)$lYtS9jqV@v8!^1=2%e8n9G$VTR7XjBqR}{@8np1+qh^8x#qs7VWi~rzF zIrM6NFRXT~ic8{jY_=ryI#wzjV4-4!)*t&$^=}bh0c;Q)uvY^b1mZ9Yb8&!Kk3MZd z>q$REZMNeAwr}2is%Y9~J2#}eBHH`q0Vcg@$Dv2-MIDF zP0F-2r3)NN7jleG9ZVAlEQK^ga5la5zdCV|L^gTY;k=Q=0XkFV8OW~%v8_P~Xk4q1 z6ZPVop)eE5DXmO%5^Stikc9nux*5eCzulD5v|_U}U7D)M9xqbd%kuuDP=ZxNL>%l_@D%;p zDcR>Al9oHqvZ{*86q>3=|IbZth+VtiGO3?<8nGkSsC14QiHZf4PKOlkmemf8b<+oy ziTR#wqTSccxKLopPZPZz#$y6b775+6=s10s?%SJ zcxtPB9Xt5Wxnh!U{c$HKzyBBlL_93F*^TQl=%Dt9{ZNUU_r(s=AhP|%mO0S*(%qQ= zl7E_HGl$2cqgFnPQQSY$=k708pbKP9|B#r1(t5V1*GE^5-TO_`H~lYS-@*+8l{v_L zygtn)+@<|^hP-kT45>8^6_Jdw9}e@0^X~uk5LUEmj&3Y9;twADyhWaCa_ZgWe4WRCEiRp#gOn9-B1DNWHi zr5EbuJoC5rUZ~y%4=SkYn%One>95b#}hVm(bo2?Iey>Lm~hU_n-Xth#@isxIF^L4h&+CPC3H) z40>0pTj_~B)ZyWKph}MFag_6#hIY-Kp@0Wy*QibVhF?}QRf*;dJElGv*9^i#gl~OK z$B>>@7M?2eN3(S+0+h0TI~K&O^sWf2rKP2n{`&PxzoGeqDv$(yQs9y4y`Dl`^lk^| z^B6^0+Zr4V{#6NFCVBV!RRjyEfej;q^f{(|@hi0+kCRG=y^V6=MCm7yv(Rq>63C)F6k+D@OOoo)qo@KhC z-~AN$A?a2n6iS?f;NeG*=Y9Dvpr4d&f!9+YR}${u{lA4JO(~(jELXrU?54_GGGFcE zWb$r*#r_BIuTx>Tb@VI-yl&6uXt@`KRR+C3n(V^r53qFE>dOe%j#8suQ=ix|h#q0t5 zC=ob}{s<{0xs0WEP@p%Dr)2mzUZ8yK5LisoS@6zi%qXj$hdL(E z(P^L;W@eUI^Wz2K8H8Y^@Qda1tpV9TI6w8_3(cz&B_8%% zozZpuFDi}o7b~jk%Tw%t=gN9Va1CeOGl$9m$}{A=1x*4kywq{hYcSBFc9xkpf=%q~ zPkkMiR)%hW>aXs`!-%T7aLN?^j2!-Q6&zvtf6sM)wg z(>+O6k6!O%$6}CpkuMQ` z-O0TIfAXwet?>1`f7PB$1Ibatc=INIU(i(rAtPAh2z#i^SLHtd#A-aTIN+4<9!bbg zvl2+A1lT#GFw`bsSK~66{ss}G84%E_sAwFrF4OS{>vL6CRXrHxGUw1>Gw_{+X-xofFA@?2y7gbX(4@eR}$O?ed+=F4PwjQ z`KHLpdO9(gbPHG~a~h@ZP4{TMU)hLH1vfut1c;~@_eWFdk|G@P45)U{RRzP?(idxAGBw|C1snEPgvQjItjs#> zqcW0{wBUOCH=j7=x9zJ)P{#^yH_I-*RDbbh^6^kjtP(4{^KoqxLusX*dK?q;6%uE^*c2&c9qkpYA#73X{UR*oS#M?@ zh$+~#!m zLT6Gi3Xq0k8`EZk4ju-(F@%AHM4&GLoKIdGSPVQ^WRjSKDUQ8}EHnOlgd&dg3LO|g z!S;5{xISo;m}ZE2*T_cGZaHdVqh1Tmn=yd2-cx3>@az7Op@TAS11}x`6jO_Tiou^i zGLj*z5ogf#pEK|JGmB$A;jA?MqCJO%%!dweeHS$X6p42rlvOCMYj0wCk+sMcpL^mFuv8HXo5YJ7A zW~Fy=3&_VQQ4fExJ4i1aKF5fjNcNTFPFu(ZZ2+8_kvKEV;@0PbpOg>%5Qv<1_54f( zL)*7sNHW)B$wz!2d;N|(A6VL5`dS1pFgT*bJw`=5e`+fhHht!gb)uVRJJ-!)7ymC7 zz}@CrEc%AId&PMxV6LV`YB%z(M!s|)-^=dT<$m&&j{nlgcEcyuGX8>v8%i--nT+wx z=gEPkDq{I%yUxZ_6mkyS&hrq6Y0@i%%2|?b=fz}3ppk|-CK6vwk5MmB1rg_qID(dATV?z-Q6MZ&GVk~e&;>s zU%0P*?R)QQuk~B4p0By=N?Na{n8OF>6YM6U{*M39x!x41os{${Ph3!qS;Bl@e_2K8 zDUE)J(s0N$nESniY3n(Wu&*jo>uYz{^GM0- zlsz>7wKnwo%#D{L{bYQSz#ou2|2N&7^103Gj)n5W&qi0BO3wf(uRCuKe>2FSHkQF9 z?B3b%?%*toB4#wByk@k*<5VH00xzG?S1UBR*1!2<5z_}A#(G<-M&%)C>)UWz*F!;F zvA+0m_j~fGDmCnqsmlql&ddv=JG(owM%%yC@s!d5vD>;2g64*;}Ebez5CRe068vAhFt?aO?H}@{67j-DSab#P0;Vd;zm)BMM z1`tE;&4<-52OnNR1WZ00D_06xCcWSDRHye$Q%IKAsQ68 z4Hw&u0-d1E0k~*Iq14B0{e`l%tw$|G!Ot(3|H3){llRmYN3%aqX)uPFzu-d7 z&4b6Qq021=s>F%_7f0a*HKks?6`Y#Y_RvLKcYlL(0Wm8m@={*?ydB} zow^i~{l_nX=<&t6-8IWB*F|m>ySkyGa&_R{3S2N{aQoNtSxmY$7nxN>hsm?U`BLR= zyR1mp2NhfGLTxv`Kba!ByP%fWk{zEl>_u1GqdoUycW~p~nH@M)Ydl%c%RAbwzuDX- z1R9z$CjR@%hkK?`zp=V%JHu;ynAm5%`Dta_Abr^-(H68P8#IBP@yJ zFVUYMMS~^Slx*}ZbEQ7BGS9*US=-i~^qAk!FxT{Z5GPJ!aC6SZZ3(!JIqUmsJo)pD z!g6(r_>?VuU)GyDwhskthjK)LEL}Jw_f3_tCYQX%y*45cY%SkDwli@|3 z>p=nq&ANhj6VRc^2in?6si5H9mJ)WM(*YKTA*m<^?<~dy47CD_}nO3ULIeI5FgX zAcB44xW7I=J_44***&`Xs0wAhprkMhY;IqP3#1HQ#&vI?JGoR~TZ_m3R$B=|92)V; zd!Hr#3Lq*{9M@J#b|`&&K?o&~FFCHEz<(g3kq>PEFjc>Qke7M-CWR4zOW=>15iiE$ z3p74y3P?)n<43wqGFs5)J%Waq>w^zME~vGt44c+O`T_+3H$ql79#E=R<_}FaE7xq&(cLyphOErT*A@cDj!-@>9ct zyZANo56@DHW9a^*-?rT^fS-=lOYLsW(b`pAdAr9N8Jc+v-61Reql%2S!QSs<@4sd6 zjeD-zozKaFcbMpgQsv7%8lx6Qm!wprRJglnA^n`!GsR7DHK&(RK6dT~kvfA}BZ%wq zL9v7*;7idxO!b!R@jJBEs}k2aZ4Wae^{Qd=!rt9LY%IR&4l zzC+j#-F~u}6b!Rnk}Qi0tsE5&abb%`h+UTL&XvizZPVawFRC4i>Q3@m$2xyH3nHG{ z%_@C#$&&ax#qj(%#QIxh>)XtF>u(-{IKLeXnyyz=`+oX+)YDO9c(nA5Guw^L)7W|( zD1@%P-Tc>-9wWF#`Rir5ftG%Mwog7&(C}qEeeU>mpHcmIn|)B4sKR@rS1m)?8ZsID z`J2nR-57eFd&Q(hxr(82SoZHEEw3IljWUxY^A{gq4~xFEf$OW+jXYEo?8>wZwU}#J z{|K^A^tIa9iN&NFPuMtG_50yx(>EzL_0uMq=SW3{=6ld?9F75bSW0*gW2kv;zDJU) z$EK!?D(jtfsLkAa%~2=nw+$344)!$FKUg_ii9EDCq&S>g+}TUC*Xinhq5>C}`(#W7f1J*7xHKt`M7Nh~0{OBmz=4eR0uxm3Eu-2m|Da92Y2 zy`KfkD(M?8JEw?OfC07ebtd2Do_kmI&u=A1x zQTLW;Wv}XBPlV<><2sIOxM|%tdr74>KtNl>f3!ei%U|(jtKZ?n-dTx%_tqMtF{%dA zG{2+bo+9QhS^~<7hubg8ES=Ds<47?eM{i7lpWiEDiI3HZd2HciwT(f9!RV*iCo`R>g!@N`j5g_MkNxOE4`++}b#`Q8w*~;##v%hf2!xFJqOJJf| zXCW1OL?|R$tXEM)`(K6Q!|S+gP5Y!|`n0g=zag~)t=fMh^uTCes4AkxS|fRhCh9Df zoa@WDjk)e_Xg24dr(dmfn$%wyc(r_OGh-@y%+UWJdJ-E$;Z^>D!t~EF;o&~}wAgoC zc1o|<7;^Uc7Q+Dxpt90POHNYS%D_C~+s? zMG^(na|kaNHyhnq`&O~~?jN)MYW`5?SUjTgf$(kVRr{{&Oa`50OHRNG1F?$pFjrAw z`UUkHe3}W%rqn=T>0Vibde2?rJ-+hzI5EHMh_8~8FS9&qJ)cb%{b3Y;@{kZ{3pKLO zE8e<%Wp5<#cV2yOb%Y^Ep}v0iYMSi)L@+giD4=@zf}cfDWRd&dgb{UkcjvMpBpkEk zD(AZpPV9l$OEgy5Czp{5a6ee)_GhWsbeI%f{_W7=m)CN0R@s3qibLbAc56LW^%Pk4 z-W~7suIbVqLPkpIc!gH}o#53o=h{$&CpWr8&cEI3yv&|CnCGc6T_`3E{JE{PbUPWT z(74+ce!RIGT=;_0H2%%+BSRy#hLK4eybtF5hT@K=ak4~ z$JdIFYjePUPRm?ZC#m`EPawxdiTo5pZ>pbLZcwYnG5N>US@)0U(+!Zol{xj5j@CDu z8p+vOQ>xuOlI+)Wtn*i8Mn{FIB|7Lc85bo#dB2N^p7qR+)wJ;blc<7vU~>za{^`1j zq#LGCy;Tz;f!W7)hr95`;r3Ncg&*sBP5fok1R!B0L#y49t@vnf)@ec;f?4wtZ2YC} z=SCMr>>dC@D9#W;ydNxY1R({Nn;tl2K?83=!NE=5W#kAlQqoj3QX7iJ^`+J9vuh6p zx=uWRFC1-@4jo)dCP)Yu%EQKDz-zVE;2d296-Gr~M|*pxvE)KTtI1F@dD=QuG^rv5 z1x3*W8<2Gc+kT!*rcmImb9|{aLTSKQv5ofw`{B^H5pQKqljz$@kcd@sO2i#dS-EU z<gg$4CNn;E}y;T92l!c&B!=Q(JY33 z&JU@}JT#e<|H$8SYN!}%pg{>puh50s6kA`( znJ4~oc)V35j0e~w zXSL8&TRflqtL;LPQjo*oHnY)oVfxLF!K{j_hmw}G)yc7FVL`6FYUAQ0H?85IZRL7SZUJPx~^j>8XKC0&KCQdb4kx-@yq20^pmwos!Ei88g{msJTMEdpe<CW9Zf(RUh~YHI}mvd(9@j4QxTtMc5a*8z9hW#pc{ZnP)<1AmU^1=DF?8^L+@wb zNlJ?@`&j!|8Mjlz+Aqcxi$*I!#-)5Vti)o$ik11yw}2FZ^y!!mI36PUa0gEhO#{wz zjZC}ms?$2y>;~Yf%K_5xv5w(ln%e8btzc?kWzVYUA%cs=T1gBo3_*5CieQ`pu;(T6 z+7SUn=_#5pEwG?WSNRyCgw>!0Ys>>6Zm=Gju~l1SVZrhacAFV~e8!3k^<-qnuh=V7 ziI)yxjQzgLfczcfl8 zUq@XbWQk@6YuYz2J$`#`Iu2+z>isqnyfRlsXW#2J^}FfMb$)i~?tdc}6amCg|^D}8Gi@j89UhMdAGjK4b=>L*R=iIJJx_# z<*i+qFL-L||7NghacMV!SnA}B3L=qMrfX$9^LGj zD31}pU!P(`F%fhcyQ1e4jvZ4T-_x)|cE@;Jb4nvqm7+7&sGAMj)aS1B<+EL6Z?|d| z`s?qb!3%{mNH-@t=hjMFWbyY;v4;v7oK6(pNtAXs=WIpKy7^zSR{UP639UCRcfi%O zeLp81DmcUDH~5Olpq)Xve7b#{fBmlE>h^V6vBd9!3aQjh@_-+Af+nKk`fS^dW7Q~Q z9}|P^+7CW=6N~o46%3KPi`18w$FwZVinXavzvesg13N4t9+%9+luec6pF8+&;nu4< z-6E@%orcNVdIWFZyNY#}YnD6KONs>C5lG}ztLSQ#cN41TmiJNqOP+JKORP6Y9cuEZ z>e~8(cfe|*<4pQJ_iWCmd)g*Wg|@1&LhWT0KZmMnS)Jf~nC|5R>xcEcNm9{(-i<9N*T4L~surxsJqd)ZiPw0KOgADO zVEq=eSbEI{NQs5O?T3Ox9gBB((&W5pqfgdixkS0}a(Kn%a7fvmuegm8G5t01KtU_u zsz1j;Pkxww-XGzu00jV`nC)!E8St5mKtUf$N~bc2jToaKTgR_PHaeNiu+F9=-gNQ5 z5f7+X1yz*doCGTaNSBrOe%E!QB&k|Li_W3n$(COoxgDdDcRh_o`G0{>DbQ6d*<7dP z`NT*C8d6Tf9nqKcq5Uy{D3x?x%c`qd9_-Ci27oW(r88L1MJ2!+zbzC>LRIYy$*fL^ z{K@A))Wmqw8!?0JEo*mXzE0`BMY;0U4-gJz>QC-3`W319Yu;lqGt2T!@f==jAHwHC6%H8b;-7;fh-CduHd8MC{nb~>^G zDVNdbjmJggG(3Za&NLz3I=XDf1M9Yr>7_Oz>asd6)}zrI(qE>JU+)>RN|QX4X|z!q zmqKXl^pfK}$ypKOSphWoa=N+Mleau`kur+FzV%N--*Ns{9Tj(y=>TBnd<5bw02(eB z&esa>c!eIs71(@3#g5*0}SOdKC0950EHvmqAO#2D$NE%PKG%Yd=0sv3E41RY2b_d|l zV<3O|_16*t-|R&gK{1wP_VD!7rRYBnB)s6nONfquKD#m(f=9iBD|=jNN+Fn$fFGyu zL^4XC+AIb%pNgs<@V1MKKDif`dWUja)xO84<%ef^T>x;?BXGnqJhrl-k30Q7C+0vw zH{&Fe0c%hoH`HJ-{}^3Uw3+k*^9=n{w9FG=pp3X>Tu^D9gFgZE7N{}qs??6v(?8_B znWEdWTLWO?%8Ti9^|7x?6UyO z{0%`=dInD%QWaGV9|W4_5S#i_O=2v3AKwXj;QNKt1ZL3$X#xtfgzx=jx2b9mT*;_* zg!k`1Ut<8eF|>{FJou`y_H;gE8T`{>neI7DIGJ~fA5XMTUw<}V%TOpuGdf=9GdGw+ z!waa}%?pj(`A4o~oXRV1z(ks+wUA>;p%qX!BPb{2_tBnd^_RY2wImS^pn0gh9Y}xr z%L5BKw%39WXyV!=XuKl`{G`dA%~tfiTuM7euNJ31-+Wl%tfmdk5}W(6bZB+BKgiL~ z33n!zuJ;3r`tsR7jLvaODU&={qR(yXc;!E-y`RRNW08KN!*QRD$gBB`l7fX?PH2HFxOKr`@cmFKhg%-NpIg4f6#kh+5E9=d z<&0GbLBT+#!d5hQ`VfvIEoM&4EmulLb@XI{Di|K$4sI%fMW z7CRp=tMDjAN+e2!mPPV1eb;?t7y*fGzeAh670TG8un6RF4tSZ8Z?rKyqlyB6BE}ms zVwzre7y+nfjhx>+T<+M2)T;t1K-DB_1?A=`N`fW*CNwVR_?t2zXTvF25NaN zHN%Z+{+F`J8~bBj6LleaIN;qFbuwM5piwKI52qTv@3$@AcPyW$>sWA2%k_3@ou6nH z4?5DC$qCp9Jq0O1C`nZDPkzBDkfzW$GMp9Dyb-73w=Xp-6N zWLG$gc=Rl!36MVv2}3bFZ7|%U6ay=!KzAWOfl|}YGz2e8j+DM&goXA?!-#LkZqq)# z51fo|sWwi$fwOaC_JEDKiwkuyf)TiPNn~911$a=DFJ$qJIEhxWLWmA+z7?=LxwnM$9qxVz2qJ z3PI?*bakNHGc-vXFGQA?y#=O*XGr^Is=-C(-+#sK4)iAycGK+FxYB2DFz?7Ndv(UY zS+&_1F)OV<#XTmSN>ge1hfd+$>#b%3Pf4`Bs ze4Y~Vsc!G+a(+ffXzW^dO>&g-Q}`v9R^nblMK6$_a`aiN1}_Wd2pk9e=v{wu^zYCq z$ZdZ`7?a>c1t^W%bV|f?W3@62&$20~s5gg=OU@@+-~e94}6Q!mGok zd7UVU@pgcD!sz6i1K0|F6L)ny37YeZCm1$85)AbD}TM#5=E)%MzdhM`>f@K%8gYe6lp9bO0^gWadixeHr@`7%#fb#+}j`9t?0u)b1dV4dn;5B?(`GHVrKRGcP=ZL+G<+=3Gz1ti#e0Xx-E>dRrcsyV=C~fcO9I(m=R4Bj+GwxSHm;!7GIP8rj@(yPW2HMOA`G6QUfbPH+1bf3f%r!Ns zx>aL9t2Hza&ztZ!)hJ^|q7*z*IeEB6YOpsI_L}wDhVEcnQccz$KnMxe+QWJ75^pyd z{0KS^*Q@KA5=V zE8?l%VZl0rIB51ZLUoKB4}=DAtvy-sJL(CgQ_}J2S$UJZ>|IUtaSgA$`TyI8?(rcw z-gs)oes_i~sG{K0J?+7(&yU?24{I6=2qf-Pe@O24b-0}S(5K!(1?%FJPO7}FiBDG{ z6r^P2fCFGj>Y4(!zQDf3lkI&?mr6Eu-goQ?x%agh{lwWRTO!9v(k zfHVL$Jfe#~)V0kJckwHi$x$A@Ggg~LAw=+H*ZRA>Py3avBf-GkV5#YJpaDAgz&j%f zvlJ-X<#ASENMB$EL&SR#<(a}&ml4>;%z#9G0NP1d`+qh?38i%l%2#TGM;S_ro7s0n zm>h66@o#VjO^#p!Ks7RtJQF|z2)~SiqB072?2GN`p9ur601Yw0`v@kRnrPnOSNu@M z1Ir9HXjmkBT7#kyK%lLvr=PR!%TAerz`&X1r3bciB;+i0UZqdx+as%Q;oC5?az=5te5m+`+rzuCj~H; z{f`LwSoI}Fa8y?h`~%AQ*CgA;Z^1?`^C}$Kj@SL{(|=ZVg(pV+S1bdxy}!M)$Dgq< zI%3><(I#v6bfZ`wPPwpH7Gfo-G1OtuP1penl0ZKy`a&_wCvOGEd&>>$v+IRXa>Mc- zPyn>wK3n94d@GnYC)EIvMJpISJ1>@13j^pJV4b$r56}7skO3b9Q1BszVlE9qBs#7s z{xTgbMmSm89}xiz=!LffA1mo0HBUvmnf zZaz$%E2>LmQvJl5kX$1T?FP0RlD@ z=O!UbHPOCbbbF!3P)cWoJWi#tLXH}eR_|?5wV_x#9Dv9;AVjHA)^9%hvnk-}KjCCI zTZFW;xYg#M3F9*S2S#Lie-gU0YG)x$Db6L-Z4d-6OTzyjCGy7DYe1vhQ)T=EvCdzQ z-qbyjILnfda^E4Gum1JX+t{ZqmiW;vq{YbG5rN6@wIc+~H9?{Ny-Y>yxZm^ew|rRl z)3D5n@jSUIM3Vu?00+futcaLGMkf|a~yyP-ZjL;f|Cw{QvsNRxGPvli)-bx&m;iRGYok+07_sWQcMkdb%Jin z<`S%q08o_*U9AUy!-KM4fkG!EFE&8bpqEN67}aoIC_19>03k^q#*b<;H92vw8Z^M~ z0Qe_gfD)9962`_IE~u1lxyN`16SG6ntPTxvR+3u2FkbPlF&kbQe?yuV`A({>~y||Lx#RM*7W0b8I|AH)E9I$9K9=w1-6D}5+9RUy4&{lQKJMueC%!5_xyb|iW^8e@K|3rHV<-oG ze@duk<^?K)ghC<_ly!VLWPndylH6!v0CA<7b=1QY4KMs-q%3&DyBp3f9*H{;{2YO% zidQ|=8T6#V1qpQ)=Y|469#9;3p?F>>Ka^gRGg$D52aZV(2g4FBlo~5u!|>_5wBF?5 zaFM~#N>_c>#?S$1v@hXdZ9q)Kr%Owv09;s<)d`SnHcX;tmE8G-3H@hLmu&GGk`)Gs zAeuA)cUf#Nma`Be#~kCJt6ScV#9Y}GYeObW z#z^wWxC9q}&Z@v?SYC3bHqi8*|Gnq09mG_}(YbUMCy-7lWnLz0S8RH{l#lm$sN*BH zYw>Q}f!9=><lOmG#4o!G;r8cv3hihR^vmb_PF?Wwvv$e^~wt19AUnfh>CSzfK^*pj`ss8&? zS>CZMV?(=`*5^5^m21XU$7Ru-az^n+D#Uzita{Z(w9zf3vY{WFnv8X!)91g@94km& z7|H=iniPF3)ryIB)j#4d8K`&ha2#=jOhyZjpR9ug56F;dS=$ph+H; z##Wirmf;d|3+bQp_V!e^Y5vg(1B0A}&$?r`IFlM3HE?y4e02hcvjgyF=fOt}n4}F;UZ_og}vNn+W*GKXA z!fxX>gIOvirwz($)UOv8 z0PC`i(0C%IAW(NQ+}u46Qbv~}B;H>4;!>Ilk$R^~QvF$w9^f=dlUWl?hn-d#!%TF{ z(tvKPF`v#0m%9MsMaxkZub%zsUtBbtc{LleY>j?U;I{JK&@>Ypcn=r81dx{z`vQIf zB<&==9lMH zj6jc+SywK1;XVF4e&?CtOh2Xg6R^DHse&7eSk#6#;aUTkMY^2co~gkH;_^R?<}I%p zXuqe#_OmPpO?=(mi~rwrniZrrY{qxkuSXCgpx8?UQ22(~z3rVjzjQ(FW9;KK;<<#b zTA>}--Q8Gbg|j|0dF<{bk6EWfmMSd+Eg|zF!zv>^;maQ`PwQ>tO6%!H0 z2jmJYK_h$^wH&Ty&SKQN33vOEU;%s1;`^FSNsuRa7xy0!7mi*90r`RVa1SW*_~@+? zAP64hE657eAH0Km1$++xCZJi%cf)9*D?a`R{E0g>F39FeG#XwSHa0!F<{Od9*9mmo zeX($f4bXFfYMR;A-go>;KML7LfV42;8}s?sKCAU+!}`^6KTw`nJYJyX9+ui_vqdfG z_&SG?Dg7J&VsmF91-}WvK_||1 zqa3t6f8OUQ#<;?Wh{C3>e`xZd`z{Vz0z7HQ}b1e6>45{ z1+o8hN$crbKP@8?-*S7M`+URp0FfZ5x6$}_$&7%UPNm^cSt3O{RpzLNtbPhm`X^F% zOY-}_P0hluxlsVvp3H}@ITaBL(>o(LNhzlKm3rsu&(TS3(xp{wW)muPvB4CD7~CIx z`)Lkt2B70Km*XLgM?%Mim)pM^tp_f|9nvz&{6UR$&p&=egY8eU`ntdQrm(DEdh{T} zxmg;iyAy+%bRb+RW2c->^|J+oc5-Q+<~lS=mnVwRQW(RxY)XF(bg56ZL zKF&!b0YzryM;V#OPx#>5v`DOz)v_)>@Xp&vz(rJlEOhRk5^V*I2&+w#6althodZo) zX7cN9LnZ9qtbAw&U6*AaQl*qY^I;_#U|sXhuhyev zt76%4VqF3H{Q{09F6YN7uFLgTe8~C*nDOsvaS8SOgOb!A0$2wGeEX_@?+tEz_b1#d z?x)36o^=P68F)#Q(6p-7{8UK3o+SPv+_)Egk$m!eWF)lSwJ&my*Q{Y|k!s#mih%rC zbmQwHHrk%{aB)bRn8C3WdAb=9oA2lIm=s}G4wtk_9zSG>oraabu~lavAWM=vcHM?3YF@9TAHbyoB!Ko{P!6K`8v(uizAlMR$Dgb6CKRe4WSG;fa z!CH(g#vlbm?OKV7`5=N{Vqzs4s%IE&@&-$jqvd;e zJ$Wy<(0t9wu3X~Jg7u&&+q*zGa28cng3}XqNn`yL8uDMQzj3PQZYRUxMsOEEzcQ- z+Xd4VWrd>zk^6oa(3Vty>#W@}eFUe>SToseNlt-MSlefJ?lt+mG>5tW_g=4#d3*i^ zK_%VzhKZ>bO)jR(CZ%Vg@5+tDIa?SWEYHEZ@y6Z0Un^JF-}r0DA3r^KZXegihhZDC z-zVKCzey5m+9q`QExEiWz9Y!=b{en6TJWK@zW#4Sg}Sfnj^&;)3C_$*uCJy7K^?ON zKS>>mA3JebB}6PdE5|!yG%rFEm%uwdEtMJU|2Y1CWYAkSgm)^>Ybm9BXKNOy-y?1RE`f8XhX(0>`EgZ^WVR!Rh`W0RHP;vh^o@9gYQMSdsm%2*iIu z6HNx>qOe2yUhZv93$DpB;31@~3Ie^i;ZpE;Kj1(DEyR{BkK)6544zU6D(~459?8Yg z=358MQ9M|uoKPEkg2{N*9ct5P>ca8;fWI=GI_9JWA7Uj#w@GidFkTi&7|XyCj0~Im z@Ch$u;SxJe+9|r1myp4<4C7VL)aj1Q;s%w2cj4fQ)z>U3$T#RR$SANZ`ZLl{t+Ns|h7_W9Uj>2ZbXc>7)yX>?`Sc+@auvW{ zc||q#(-;M{I>K0v%@76#`qPQ0yAcsLSE92ui05cz_CrR0hJc&k`I6o6!ADkY$NsFz zTwSFNFpc12Tu{9Y4uFEDuaOQmZJxP78wLdhgV7-9lvw;oDC7h!J^Tp`%RcW{T!?iY z*`bcfjb{L1$o+jW5E>aQQW@E_=tJK%m~2K0MMRB?EV_+KjLNn(2!9{QY%h zI{bwihF&!^>BoE=N41lvk}sY!i( z2wRBl>1Q$y+KQHU>1|>5t8?s_0j--!+$z+Q0al$_eNP$gjb3x{i7lktWYkPby4Cm= z#cV0->bTx`b~Ia53gK_j*u_}BdqZ|cOIb43eT?Yaesi?=HG9Zoy?~22ZxoAt6XY4<>e3LXfQ%Tt%p)wiY2?hx`uUMoUMm1Z;R#x~Q z%d}gO>Q+?R&2*IPCzVUOcP{$2C4x7s?Y|YV4No)~a@ia*wk zn@tA|EGq8}`gft+{Zl!=1D8c4?|PypPKOx~p+cLLyc}snH8FGldO0>@>w6`SVUJ%c z3)@1zUYyhN=#(C|(o_gE{EfhOY5hBOvP{9*JL}U>=+-Fsanr7V??9mgm|Ulydt8Y$ zAsaq_pWIYr7~4oLqp$w)Kp=ip#MjQOrA5o<>To(n%-4>3?J~2nUQmcDKY(mOW$b=y z!nXF|Vk(E&-P9!XLoGq-mWuiq6FyrzN4mY{X$8eqGqz&ZtU<|#lqW}BBkQ?A_7#_y z+ShNV){bSb+21aGVEm{wlbloVP4`;VTFuz6sNA<}f0Fhwb2&)*h1(aVmvR4U<|jE} zQ{o}D|6Ls{eu4sE-}5&6zJV_CD})J4zXdHdEkDGY5jO|+U!8*$0lu_BDF^OFJeuC~ z#>a{1Bj`bH;Jv0;+94o8kHB-b=jUV9o@SN>fqEz|O6Su;GZ)dZJUXq0{Xnl3PqhZx z%1(|*T0bwqH{gum5%nmtPfBRUq_aXs5QG9LWMgEI;bVThJZI`XicQJ+LitWi&T!Q) zdYgGKb|yDgZ=JXPK(%?y$46{%t6b*z+18ARVwFQnxoZk?JYhtN@p)$R8(Ak?rBr*` ze11zc-S({1vf2+0I?@}hfrHej_vN8szL%z@)}MbSGSum+S0eS7AUg`$H*3BgHBKkm z5#E=Sndb1Xb_v1?G|L1nW}u@b&WMQ*QK5LqN}}NW|`O? zXNzQY6J%Z(ED1B(k9BwR%pbQ3>*Q+Ey<2(nKU$?2*r%aX)YYC)Pfor-^Y2u){!Qpa zz%+nB-e5JqzvIOVI3mK@Mrs}4r)QqlAFdrE;`TQgAp^e+Mx-8lPo7%4l0e_-AS}#P zsOcY-vD5BdIJVpv{;jykNN(VN43)xlM5o_P?2x%U@5-)szo7Tx5MgG-h8spX<*pgN zDA8+S)=Ayp67`>?+7~X0)UoS}CQgy)Wq0kR9mkz`r!uqDSZo^`Acr*7T9YsGh~l~K zW!i~R%Q%ziea+%4+en4p_e*n#ld;ZTg>i}JW#PC7X}>Omh@xBIIgW>`e*IWliSAyV zO~YQ9KGtmnO!%eUcpD~v6Y(q6+Pf+B$P;F3^EH0FZNy&{q29p=@+q!^qrBgfN1qv^NGYX+}7fU$%8O3T=X z^QO6aJVyF`w(5#~B(0u$x2;;HPLs~{kmXbXrB>;R|87v>7JkH<^SvmoWQ#3BR(nmSp&Mzd&!m&p+&tk^cl6qH^D3`=zsU#C%tNR`UO;bq~DVrfV2=*j5XpHAb04)XBrm?X#H z!XI7dDfE2LelusMB&2-MUrOG`#8$;@%^3(^;P6{U)QIYCk7RqyS$=J9j$;ROn@Q}X zhx_c<=}A7FL`qR3e~HUcqv1C&g#Ph=^x zzFtq+p6NsceC06@aLn(*IRZ`jD)}al3hmwKP?54A;(w>>_1(ZlQkn6+D-OKRhlSX> zQZKE7Q^pEVPm*ihOMfosfA;FM3Wo%%8lJF5p=d_UvW__cSNab}UY%~vvfF5P318Mx zz~XJ9!EPW9yN=e@s_7TeMFOnnOFrA#j&Tf9QUBq-Lxx_mS)ZwUSDp_`NIJ&*i_coq z!S-tUY28i+>lrDkX|H!D`Y$0D(VOJyNTGn3NNz47;p5qw0)vZ)z`iUwc3t9EO2{Ze z#w>wD`DsQLF(mZ=bhxmm006+NgT_K0UybcU5T^p+=7>^_sc}Sq7kWpEVq~AtH!!Al z8aOuh2IS&3%7gd(le2Xx4NY zin=TUz55UQ0Tw#aW5$Re`3a=-4?_P=ktz-S8vY*eydtZoJLYXbbsI1WF@q8ec*t+# zd{e9lr~~4&E=su6Zgyc@ybuz--qxG(P6}^>l1xM4S*l*hi2vy3zBkbxiNsY{{D-?F zsz`VDw(0`72hGDjeTD_6x>gNbp*~kDcC2_l7#o4&Tkd7pRjN1ZThvUta$)`V!R)_! zGcPz`46shnN57rpchvC`XN?vJ;U=}{zEf=F;jCmsC(!%qN$DIf?R|Z;M1y7IyIVAZ z9VJGafc_8m11L$J1)@%~G6{O{AVb~Ai~-N#G4DV*SWREzDERt%QB1FU!wealKulpk zLq$c!4n7UwLx+F!cg$Q`6!_av++qABOM4)nIq1%n+(0Pu(Gm4z<~|Cmca)8 z7>zSB6iBYrnHMqTg(@b;hd0+;nZ5qXhWYyd;` z?B>>%=(RK)?G_+W0f$!zF|R*9BWK9`O1Gld?pq&F;E-*AL83^?9d5wlSK;FRrb2_= zc#UjXM2~~$w(P%I0GH5`|3GYNvsRa%DXB9rO4Z~6gQ3ErAI}e0etd_L9HNWY^qa-X zrZ#&(?;IMJwkp!#&$ltf5|{x)-$YSXiSKNoS6o6kw6GW@V@V54a2cj=$q!+x_1^9b z0lRN(VQ0vzzw+pxZV~@bJ?T~5i+_g0|G(GGe|E%ZcI(EwH?U_F zsxlsnm~qaz=uv4;5xXQ<7RV7mwh*%+Cb#o_4@YtnoFJ7Ke9QkFx|Saglt2#j2|z!& z0?=U`&#^_WJ4AbIPdES<`H{35G0i?`-v6OFhhphup;Nc@;@-gU7=LPDZeaY>i$cGx zt_B)TLZZ-~mr+5nUV<3IPz!EhIK_YCiyU5zHZ~247A+b}BZ^s?fYS~P^RFVL@5v_F ze;GT6|<2yo1WtBUhYbq#TmM_iZ5i z?*T+a)+7$aF5Q4qdnvwO9w8h1Y1C*eXg=v)@BMojKi=bHpO8tm|F^g9_z^ej@{vEo z8?Ars@jsmANZR_6htJ*aGS9cwmW>D>&SqlHC3xb{J4=ZX>u@e!>xB~`&vD!VZ*kXx zaVxZX%<~@qBYuYr0!6{wdb4@%?1F~pAh&UV)kZ(Ms#GT)3yg=@kfcTdn2AZ05ZtSl zn3O~_38WuD{{$caV9}HF5}*vN zY4^vSPw+kX$Us%l)1%7hfc)G6khPNqpHwFhi;0Qph|*zI8hcjeuc0~q+i_zA7nN%w zooHA6WtQ8Bi<>~u?U0Emj+0CvOrW07ri zyoLy60iFbkS6H+Ee^k8%Q(RrxVB3v`AdLj~kl+^FHAsScL*oQ@cW6QaBzSOlcXxMp z*Wfe~+-3TmnOpPSe{kwlon8Cv{j9b0B}fcIeVYQ_1$yr}{*^fFf(D43{z^rBf4MiN z1|Q7PEYqo@oF4Jz0ltyM62o#Z&G9Ks7z}$V}np;4Y{}IWT(=MOlsaG|StN zWT^;`)HBmNqKI=6>9b=vCH7&XqF@Zg^Um%rLl@L)xz<69(c5dV*# z@BhBl&Z&w3>E$D}>#&n!xpoB*&RB1TLp^4)(xmvK`ngK}j%03qPH8-<3K`0|&kKOw zu$(y1O7{ecqDu`VL)ii&R<#2#F<`{GbhNYrC6w@VB)gdTCnA(bDCT+9Q`H|@L*!NV z&4ICZ4RTx2Y^EYlXFbjEQR_8x%$q!~QcbTl*9EDpcL`q%fkX(>0jQcWR|KK}0)8k1 zOjfAWVK$)en*;A>Gpe_RJ0jNu2&k8x9Xliq$y~)`_T*TE=FctjwH9|*3)x$ znf72^!4z{_)2?ouDnpiQT)q0mx$ahaNo{V2;oUOaH1^TIZQB{BS)5CQUThkhKlZj< zUQ4OD%KSB$6VPOC!Sh!|%>$8tJJ(rU!opV=pGuuGu-aTxi<{!n=gtom{qnlB%b}Mt zB{fjSHhm}fwoa=**2>n^tluO2@MA$t!klK7Y+GYno=uUTs7d_=L4s3p?+%xq@CS(G zjG)vj9$La{L~;YHSlKRqxiRtW2=~5en&_Eb_=E*%({klnGBUL=X-3ic;ip44+v#bk z(CaNh(%C{ONZ#^gHU#2=izPU0P^XkFRC2x2KFuK73SQ7nE9RMcJ#;$=&z2Cb+af=V z<(w_^Hpp|k_yaFhU#{nAp%Wh3IultOqgR{voX>+u>*Q3V6B31p)AxR5)1G6O;FQm@!s2-@h zmFxcek-Of1|E#8Y3y)=#yu$jF(ZQY5X^-)r)D zAz~vibmHFv7*KAY7}&^UpP&P{y?_jB@SErNl#R*=DvegE6qSe=2poFNE>nwDCf5e9 z*9LZz8`$BhtY@;sgFipwoOONu)yf=#Km;IU{)!l49F26=xCeZzEi49Pn+->0y+C9+ zopxn*P`Bn$-eEAhA|6CYR@9xL*Ea7mS)2n0C$E2$6;hj zq7EvOFIs>=?wvFnZ;@Cn@eI+HmIeczVd9w-@XCCPbD2${RJfm*H!vAMMesGENOXWg zB0%xs?OE{4l<+QfLqeKYRhIWf=6HJtHI3^f0Dk@~di|aP>i6GRyugKC??aDbo0@Lv z77T}w(Bi8_Z2k&>x#zh+OHL?(Rb|N_av>jIZO(TxHZj#kJj=M=XO~&G3`Z}~$1V_> ziZzTSnv>%gik=^0`@q38VOYLZ<^K4x6O|H#dv$54m zuA!02AdA=B`g$$}L)m(mm-(ylzD;=5ZS=v9uIGg4LtIjWKjb^+->Axp-?HQDYir5h(+x3>0p;eEBApO0`K8TMf@v4Td4`9kE=uzPK*qPJy<$ywrR z^F;>p;Iymp_n0YvN=lcQ@V_E&!G>2vOsdO2XYG%~*-#*}g3~6|oC*)3#5Wi4Gn#lt zhS3hgq+_k#G~a9dlvo;uY#r_9I{LfCj%oD&fC-eezQX?5b2rj!SWL2@1Q+Tvl!fJ zwV*6d-g}d-QMw>>Bv~8t`=LZevw((PzUTM)#h%H$^zLwyh|HQ|c)ODYc+2F&{Jci! zwZ`kO>8>AQCsV(t@td~>l`f0x@I{zv%=d_MZZHwHlaE^083ZJ6oYW19KEztTn&&Za zG4vhSv?QkuZu*uC|JFKkhWV}f!Lkl5VZL>{ONDULrgHY`ld)+8=Ao!kr6oriX^CXX z^5fRLM&E;#^4%+S%qI3v;W$^J&mYMSZ93L3o4$U{Uyd&-&}`59!DcX1sIN`(np2+a zd{1X(4M^m;XDFleh*)RCIGna-RgP(>w^sjiH9)#-;UJz*(ctpS8-8hg8aQVb>T6|f z=6|xJR~}sBeQ0EAvAlW?o4)`2%(!c`R%aSU(a}(WdmynRK1P@@e-@*kRMv;f zKZE}_&VBKUad(8yu=jn)9I^7<-``(}p(xHIRa7ddJ4n@0p#Wuo+F)0DGr|WbvNvo8 z7|QR(oV~R#`<|B+fMAF))+^4|$?1BbtdViNS|B8n5`Z5%VdIfhDCmU}5)~{IU|t$Y z0f@#)+@d(FOwF=}A%Sr~O^E{`icq`dK%rmgZ;XrbUUtMuG9w5Pz_-wnW`v7R3E0F? z_Dy_#3-l`h^bfC7CaJEIC1{wI)q8kX#L2@`m@1nTCc6P(KMY>4!;i`>ujPH{3MXDl z0xh$@+3_{!3#$b#L@GUeJ~HoT(D>#~tfyDo~GQVtWgH=JM8#_@lI3 zK0te;Zc)``_`)t6cMo^=?#`7atT?zz$ZB0DRZv7EFjhcvy%4EpvTlD^w1R2CQuhXA zfQZx@)opL0M$E%(HouyF z*3VoQ`BK3HCaix@Jllx<8{?xqnvEqcIwh|9`vBF zFf(tTLbOx&2tysFxJ`A!KVY~GV{H3nID4`Gwim4|awOMfm`Ke+qYw7l-#T8+{W{%cP9 zl6Mr80l_z)1|nf|MX_H{K%i;arGBNc~Fn^KyU^l{>^rnwW%9S#w_jolp{0B5sbA0dx&KgZd}tOMN>0ou>Mj*(do=X-_7xkPwO-jTbmnx=1}>N%l%wLstrzSY z=|$1%K4iT^5!6+Jj2gkT{7MqZbrCbp(j&1T!!-ytO6py4#)|8|M^dWaTzAT zeQ(f-0Q-6`Pn+Zi2$QX@*w|gLzDx>qP&{>Jz`Z`kUWWgRX9DUZ0yEc#JN_^LaVCmv z6z}C#A>|pG;khr4VZa4|dP~_{4AmN+!%!->9lQe-r24~!v<0L?f(4t_=H~Wxu`(GW z2sc_H*h5JiNpM*^o}A2qQmnc{KpJKX@IN6N1SbILhW6*+S)2qJUEQSZkVgg%8a%+8 zD_m4z#H#}WCX##aHB}1bpR31H$h*_1P@Q&=wgI5@Q1k#dL1~xF@W0!c zxP)8f28yELw_{pUxua*h(=r;(3}N0P>vP0K1Qo%LELW9xMQh6|olO$n^}!||8u*4V zp<6m`(`A>mO&a@ip>%Nk!$i=Pc@hN^= zofeU}Sg#!T-C>UHMjM@v@`^!5x$PemrBq`{Ly4w(ZjObaS@oEzfI<#1gdy8@S8WS2&LA=9UBoZJ_0VOw zoH%A!Dg~9%a%F3$I zXZCSWZ}sA!{SF<^xmET8Ecv-n{FLKm0+zI-?erQU>j@&FX}O~YjsCK~789q%ge!WB zbui?aD>=}Wx?jg5x4mleaL@M7VZOcT)EE(Q?RpEmXX@m&awYX@MH0a~y7@>ng{$os z{(67y*KeyTEUk}Zh+}@uz8($5?m8~h^p9(~N$ZE*jq#ZBm_-(cx|p>BSQ!vAcs0iX zc3{z_CZjY*nqt!n`+jJ1CB{Nu!tAo}fK0;Y6;Q|cMa#kk=Ae+quU%xGnn$^K$?eK_ zXyVTvU7FoO?iG;0nUqB0xUJlxlWD&GhS#mHKY{BhOq~U+R&+~F>kK2Nj-#F~cN)XQ zj2sr=_s)qTx+{(wxq%M8xKcRmCm)-_TMJZKcP z=@A#yysti9z6#2ZPz)dbzCLereqBD9oSmc@bWKw~JH_Y|C_;eM!qZ+n)H6}}8tG?v zul)+KI>AP3tN`5}mL4ZbSzA4|;kL07&!l#Kzs~9PcTScd{QD%O7?bKS4 zmc4i0{&$wbJvYIUb#ASp)LF8=Cm+Ut3)J4Gxy3RqG!K!!r?C}a>=^_3+k3XA4O#}$ z6@T(qNb{f&V`;_nCi78f54XFNC)Z5SO`X6>hZlL7{>Erj7ANGs>fget_Q{L6-JHx( z!o-}ykQm9k_ z_Qb%!0xJ59Y9LOjI)JLg2O)M4WC)aL%vB|X_gBIF(D$7`oDET!@GJ*Oo1&vzi`1KnDu~wxQOlq;DgxMNgCiW8}0+(KD`W=6_FGdoPRe+xuCTp z?I)I8I~aL7h~A)WKa`{J27CnlR_>kVl;%`Oce1&H_#DG)8K7kt>4!2&BPbvp3}AB2 zdN3z8<{SWjV)hP9g%95VQeU1F|fFa$m)JaA+gy}VrOY<9VcTj$?Qxg{*{-f)8G;N z;;`KZJ7m#_cB=O~s~zKK(b#gYnB1g5i!S-pLV!|1^XFIsKI7;y{=4aVXoW2$F1KPl zVvo3B{zU$ksXH2az7t#Ht41=Dniu=+aH=k4)M@cc&1kaxVq-tbI@52iappk&OIFAE zbLj<+$A$u2is=V|H79wjyjBEzs|t2*wIzTCtMp!-^~CVEwX4J=}O!!Kfps@Fjj@-d*l`F5N*t_WpXDO}%*$D}QP7He*+( zb==G8)J?&l@$){CmMnF49Bi4HTN1v7Ovl;hNwFF`dNY(N}c8sZ6b-&nyz_R3EFy$&XJqiWA>-cfu-tT6x|nYTgujALlLkxN;~( zc0<@^3;UkAdLDX~@J+okU+MK4Pdu1-r9!36Exulo$t`FN*z0Dk2lf{&?lIa|-V{-| zfmaC2)`~yv^(i;37Z;TN^NAGEy)}ySBT!UHIM#60)Iw{GI<&in5hmxDT5c@NYeW{k z7E)VzXI&F|t5a^Qwu(yN#aiAb+}f0BQDP>Ew4|WlkYI|OoS@vo7yXU+FGZy$jrz91 zqL75efdk>d8j^I*2jf7IrI)Mpn+iAZkhpW9yVnl(Q$54;)>WiJoDPcYXU0@m zJFZ9bW4@#0*uC8dF6@J54p{1&%faY$=ltepm>BHPeG>M)uI~XN$_Toj49(9#^gwnn z(HEH)a0V36t`(eDu2rFrRXf;{4}J2vN_Dy1pKy0T#rBj%aD$3XBZ`C1p`cQwLFp!a z%})FUsc#<)s1U7jJ45pDM&4CBliwjIR?e>ZT#k)PyfFk;0%#sO1-=NCIv>>3Z|>_G z*C9DglAfV=8h-Nr`;#zr4y`Sl<|@z!>|(*TvrA$+&6BFec5{&S1l7Efpx6nO!ZSpQ zh{kaU506Z5R3rgpc+J0)RT32^91MDym^&5DX61#JSuo%6uaeDYrOIqg5n#nbc zWy75@-XgXCxI5!fNWKLw3C^wW1-9QGBLx;5K18#~U+@xIrq$c6TiJj1E5rd+sApi- z!{vHvb;i-hqs7Z}(mHe9xu%jmr^z=#Pvc>_;b{#zX<@s=Zg^GI?GSx#u~8+pDQ^@} zM;!EU_qAn7(XZ+fCG>L)r!D4!)_>;*b!Uc+d%8asxP;An4E9vvn71%F!B~P_rdT*>R@eBlq2E+u zRzf=eR z*_tnL`2DQ8pIg)y-*8)Gb!>{RIk+9Q@beo8Ai?ovbcl+2VSW3$B(qXDcJ* zcwtVtygdX>z6Ynj+HRWt4hoDz3qQ2kEC%hnJ94HmUu$p0(ynrjXswc-SyM9B7kAaJ zm1+dOoqZZ8rs7(>9?1O{u39a=l&s{z{jIrnV|ekWCy#x+&WxRN=~~d;4~-i~+dr-y zZ8O9TXQpe540up@hs@`R(&mdQF9+`g|>E*d3(lxF} z!ZMyF(|V8(t0by+=eYQ5{NOJ)q+dy*%X%4Z1(q0j;)wuXY0NiDV|z7kO8N8});E;; zwo6w9)w%<%y^}5?%LHx9=kRsfT608fy}aVQ`qFD{-g#?=uIro*^a${*3_7k=r;Iah zJkLfoU#PAY>2aCgNThi?64ouYNjjRR9?Dg=l|d@k+~z^fHQmqb)UgF+OydhbHeRK) zHi&sdxpOP#=3Z_>Bi`;DT_2wv%UjdzHYobu3}vtZp5jcF0Lf1Hpd42sPyavUo@&KUR!F*zrcF-M3;vK}#hS%o= zxiB3Q9O6}+49`7c)Hl77Jnb97Hp~FYC2#=%1Vujs$kOAD5MEHBW(Iu>b%vtcYQo+a z(ti9XwZ;&DdC`v<%^%xIdJmw`9l%BPP+@iL`lF++uCC|et|&ak?Mg?%0aQK?5Q4td zQd%|tZ)e+V#E{1dK#tM#+r&>2YkS)*0=Ujrbjv1t#~XCbH|0aUVR} z!Sm)D1;c}c-&T&lORz{~uL|ozfP>4=(~y4#M49e9osk@0&_~~+#(#!|p>udY5IYv~ z`ewsVJBF@fGM;+lM$82(asP<=qVq}lHxmAHUVK*ab~jLyofjhYco}^0Uw?MJdvP52 zWKk_8KRlerIm2aQ8SiGlR0tZ;w49R6`G&%_yOQ!S3L22nlg?oE9MF71pJ<~Z)SabwVQolNJ7bSvWA9p8^`rm4ABk7^^AtRak5-XrmUbJ0 z?V3KJ9(l`8?dodR$;kXDCAY=?4?g4@&{pl$eSTex$DaYL`I*JREP~cypJZ-Ud9N;6 zPimeYkAjem$%J!D3fbUAgZ*kL+y?qnkp@KC6g7?6gRg&9MwrCjT_6jD)?R`5B zR@lsZ9A~VEl}Q-IpScoeYxj2VlDd?k#R5($Y%~@D7-P-&*u$bBm;&MGp+Zn$XulUD z0^+kk7I?DIP~$c6;WgZAzI66S*oVm6Q+4EtgreY~4chy+av53X8_W4_bjHtSbbZGP?}F-jp5ESk#A|RtB7{{$COb2 zK$+skF?))YoO6vgbA4Y!ebiEu=G}1L0p2OsaGATfa&u!5wW)j2zix#I$n5P+B@i1h zO)unNy7|1D?eWzbCY@Ki;fz50^{{C_J%=o{EMnUnWSYpIVHm19mgrQlTpwXMdzNP9 zZ@mDi3wiX_X*ix^d9EYGTWBmAqhXu9?|E1tvkVo+Wqa})ZHsUIW|7P?TKA>JHvO^v zs&09CL->m3Ktn zqeD7Tb4%orUWeSa%`k*(82en>3P?t&?75DG9!W$i9;c}Y%aV6#=AWvbV^>e|&9a** z_k$>YIfJ^+PRCQl?X2(VCBM$?hKLTbVDe7)G_nu?8GuOD$-OscMBEEQ02_uQ{BdYQ z`tYU`WfBo;cnG8*f+Es`4T)AcqXwFLTrcQE91sel2?M>$Yk&&iBH6%e6#hk&6@Lpc zDFq-ERd|C0PGmC`x)Y*bEavA==8>&ci#Vu@fHy$w&bOUnSP0lsUZrtJ%uoQP6C%Ct zfIx^(Y~LpUe8KEsU|_HVDPInSEF)Z_)&htxQ0fVo!I|Fw+O#4?Um9Cvmj81o+*d-g zcoDolH~8#Fc~Mc?b_Y>J;kEVZtXSX~MsZj|mIIqrMFE&;QFN{nG6$jees#!HNn8ix z_OZcO2XtgJ<={U+F9blyc$18FtT;ulB!K}cOdEc|M3vx{MIK=F4cW&LF)#d}+X8P$kv*xHSN-!sR;XZuxLuoijnCz6er z#NvxxWLu)IhkTzWfb8tuF#I*L-0P&&@EUDiMA7AmCHrLJ#b;aBA;=Tgl0bLF zJEsnMkLFl)sf1@y$->Oz8=F8F|5v%=pVD{vbz`W>4T{YlhEQ&; zDeL^Avl@(l+b*03id*v5SQn9EdY_FRc zLGDql)_s4(C;xnGt1GE>c#z!^FPP=`7ur~dK&t`l%jsVd6r~$7E@|^LviV3Wje(LrniSTA z^=3HLuaz<$(dzy@xutxe?8Y*YNIPjVcjq&k{fh6$fD_ZI$;+~}8MNRmD@!=0pARbQ zuvt9II&H|`P^?$bDYAWE<*~FK=!@{}%3*iKnf}xxRaquE<;f>J%!BG!Nl5L3)--)W^)hTC@`NrMO zS!med$a*O3yk}0%Cc4b~DhA2B;bKbhx^0Fayt-by#_E^EKC`CV1ELNkjk&PYXM689 zDK@fA@wMuK7jrdtans0fO}A4l7Tr3Y(m+r`;yeo(r6sXcw26R!iGqE-Cn4M#%KsS zR&T|a0{x&UQQ7jPsynEL*ipak{ZnZWZx;&@7Z#yt%*L_kom6=Hx$s!n!s?SyC$jSP z49IzZ+||+{?_{PR5(SfFXUhcV`h1Ww4S{pVK$yz<_2rqI%JB)#tnnvyalD-bssNZS zRAh81i`c4x?FTRLURVyI%2i}*Ee$|X=K|0O6{obYHt^p9RPD&{yo zO|HctQ~m(yttu6F9>ch53vcGMgQ=sGHdL_+|BLsS8E>;#!4CitW-l?;OWbFnKGE0v ziBprCgP{6ulA*8F;FqYMgalU*UEQgHx&{3seH8VC4wz@v!%B)90n&kkkv-(vDXm4= zZG6zmIsA8;Mi&d2q2tt;wz=sFq~4jUA0_4oLtwMj<{8tfff z#98gwxAUv*B7Q~KBr^-QyGQzv4TzYEcJG~KPc87b{j@%H3$MT5-TJgB%co~;1IR_X zL@gu+$&_ee7jvkt&(bEK@S2G$S}E;HPOGJDX?U+9!i|ll|N1oZDbL;_r>?92g4ZS> zynQOpKDxro;?G&kS!(N*Bcqo|n;hqi?)h(pguoejtnWo!_VK`W;f?u@=FUA<0-24d zV46f;^M7P4EH(Fsqze{%Y^d5R%c1Et#s;(4&IvEo?B5>A^p?h$A!RFk%C>9GWvrAo zhIVgMpHP%>3n(#LH-I(1QWCf3>E;W}wIW*lzw&dg1nXSJyu62$Evzx6{6_u~X}rwO zr5j~3&$UWbSjBk0X^r(SkhtmjRkx-wRJuI0z;jDS=4Q1Lui)j{wKiVeZh9$dV`ha8 zxu4f@9;Skv{d}>zpJ-i3H|P^sBXF=<=^>eid6lpjciWq8C+CqYc}E4cHEXnp)Ze=V zPIEh+zO`MgA9=Ayr_t7EICL0t9l^$_h)=V2pE0!yI)wYoYo*ofNz~I5tC6-zYTj!)>?*7*l6x^R{98n!@SycyN z?8W=`wcj^F+UR|3jO{T=Z%WK`m)LKLcgCTsn%p2GljYaeq~ zl^?_6+sb3-{{8emPCqeNw4czAU0;SgRb1Z>h&5JGHC&=BeHSx(w^%e|xe`3}tq)0mi{>CIGhcaUxR?I_=q38F*)(#UA4V zZAXrbS#@`Jx11RTE&^Eqq5uLtz<2?|Q~`Irhs$*(fs~!)@O|3eb~J?;6=r}C5DoU7 zJ2XImJm>;S4wy|CO9$ivDOL+~$A^NOISDbNy1Rd&(_@de@-T-smR5hKccz&_GyMKY zi;ZcBGl_l=)Tja;AUHYT_k9luiw!6JxQV2mB|~7JrMpU9g{AK_^bu>755);;HY`SY z93AHsov|S5>4>?Rqqu6aofz(cRBCfD1~CiZdVq>X#%(O1C&(Gb2o1)FkvhqfU3Gps z`*IdF^l2d)op=(54UImOr%ynZ)Wcm%H>Rq-8jQ7@2d2#PS0>;9JZ#>CAaq323_7qMn*JaSzzABIQAxT4D=7mEI_Uw||tEVaDF zKAbs;azWZ8UB751e<}A~$eF+9=eKTZ?U+5`%q|=`mDIyAWM{f!pD1pXM?15A_L8%f zfBrT-t1~o~V~k|{#r%3)I%BAH5z<9kd@jSuS=sX)bKt-vSIwF&+1&^2H7C2Vkmg9K zky`}qi~oz$F`6J6upOL-_joB-=*Q@NRJ9mAO^r#>Oz(4_UPGrVYJEj=<+UcHwmr6a z{yrfEwpg?MvA6Ry$Kr%TQNJSbA%Ep8w#tsN(o;W&v%jc-SLdU-v(dc1O3*B!HsFTyzs)wC?LbKWe2f9?OR$)3~w zV6nCyXZLhSV#+~dl1JsLum=Rc3OeK2^L!th<#fVG`J)} z6%z){5E!p!Q-)!)k_yYW4$3Lkor_G(ZxrRiJ-nK@{2GJuf>-FRZQ|z@mcTiJ7HcO< z3h2%cMXx_zh|Eu(#pCEfK>0as>vZb+q4xfDX5Uz<>Qtvq8%+*5xu>W1cQI#Z zimZ=!XY9Fp`v0y@xN6ffnvZkjEw8E-1#2$bn-;PdPYZWf4%MFx4U^iI8|lj6ufOt| zhSf+}c8k(n8jnteS;y$IR{6tXvOp)gc~}DjwXJoJPw#ChlI%_hP7QwD_?(!tYiS+K zx`*I8%ejs}Yf0~$UrJbM?A)fb^X>ehx#WOYHhB?NqIUTmJTs?N-aaZa4ZtcVVGe4| zC{mPUKCJtMw2)RiZ4Swdn{l3oWe0RasSJG=ldC4MxA4(4f2YPqe0 z!6ok8?8}S32c=z1gJ`*ztHA`3Gkbo?*konJJ@PrI4aI_OxvU)?X%0LqtZ7diSo9?z zF=Ab9M?&dbPe$~>CW2o2^6tS?*>dsBj>?F$Kt*sa6a{6wQ4m3so>EJe>gN@>0xBT} zY&51Ogo6Zc5Isdfa_mG_F#KVYz*RPIOi(h|BZ23WR?D4Ex(0sB zaUhD|aSx8BIjleG7xnaUJB&J_~{kW_u8TLD(L7Q^#(V4|I z-Bc2Bg3g=jcLIvH#>%sw{M%)gJ+r%&=#M(ac4a4)l-8^L+g;6rXKY;tLa z(Pba_^Xa>T4IJ-dpHLC%g1vPWC5C;yw|4<0b}j#q3Z(>M4^X90A}84lnWQqfHp(K`5gyi#D#V_qEO#fo+;CEOfIhf zez(GVfQ2N~&nHS~C>#_#ui~SbJp@hjmlLDjsmQ@qWjG```6tPCR4rY zg}Cwy{kryU`#LM$+QPXlb=vZM{kV|elON|OCiwk6VL33w&>mW_@}xcqVDUW6gzOig zuCGx=xwgS~|At2~gv;T?1r#xWD8?2Lq=|%l)!G*kAP`SnDheeCv|okmx+@P*1MM#W z6d@zfjMoF=rx^>3(}1^PKWe$&0|o%(=%fn_p&mY?sV1AKA&)(jFUr<40F=>t3A0j_ zPTc!w3hhBcZ7ASIcb|xFi;Nr&^r2nfiXm<)({%{YJ8@9rW@h{K(y&h5FS$1a$pz@t5|IW|ZjTNaK0!<@)qVavBQ8xocqVU!X|8`6LkuvcJF-rRgf(7{; zkP64dH&Q%V;7xnK$z{$&Cx6f83D5y!dQxR;WtMwoMu6oV4UnmTti$5_S2U+;C>)tu zS7FS91JZ4UG}qLaj!=yqY1C0v4d$a5K)|nqc)4zw}*Yu?h}${3S6we&fCAy1xJ_VsixqTw4yFaxR(y z!bvlOv7mq>%1%V=%1_GX@KC5U;CEs{KNY3BGP3})EOZxz9jTls_Y(0E+RGRNB@T3> z3J@1P43ODEWaZ*G6Q=+>gRgNCi&+f0Nd;UA1G%ST2#A2c7Z3Pi5k~=hP*fmxckXCp znv@?Dy%&!sVN>FPlg`=%u9kak5`CSwi~xihrgYPq)q zs}nBYR!5=f0r@)L-N#UP(#N=E>$-rzCZ<<_Azs`ze2g}{44y0z@a*Cwr+|d?mC~as zL8cVR@Z|}{@eE2W;e3BG#c$mn4MN!g*H~vNvc*tjh2=o+*`FNrp-|*FXbIEqp9qnV zfV*0ByNUCmjSb zA;3L(=1Z&)dp^DYLnNPgYPmMBb(PiBTYSq+aZd zGU_gOyNd$W!J{mUJEI&0sL3#3_5PExzY8i77AcQB8X6jw1n}E(9N%b3k3i50tKl0e z8p45E;s7_`>?D4P%k+Hd=&!nHw_*lmR&*p_`z-Nd?!QcvSIInEOT=$`MsoaMY@jVw z{EMt!A(JyX8kBBfioqAP?V-=n^1;+jla`x+ zD_2V@XeZIhEONkSlTNHNpfYzGGEkYQwsZ8eSN)?_?C>i(=z{#vjp{o0xwG12>U4lkkz$K;yw7A@-T@~%Z44q%7Eybl|X9WI@KgQt0Bq^pXsz5 zjJ!de1@P^lJV3>E3dmf3uOK@n(dpPlg=^sHO$S>@QoU;eXira4fPV}TLi22)PQ!}} za5kmgiaCVxKWoVUu{1=8U?O3AT-6Oty;lJQd}2|dMC$)sc8jgtsfT?8D*pj*jO`%e zfYYHEC@)ZaQRPj<63o+|VrWqTNA!U{nYdu!;L|GSGytX3P!e$$7!N?oXamSs_8J+z z_t&Kk_%IA7Pv0acbRsv1^Xx?#6@37BfRms^rD*`1sP0d!!GOVNj6nsxDF#=`+tS2e z$oTZ;cGOPOnzfitIo{BC=Pd^;n<u-Np?~Rs@7Ai6VJ*gta zRwfBLDFt%b0KNczU*KA{KNS1fByE>uBd@nvhV()rz@*;ykdS+bLd;M>2dx|#?#%kX zh`os%BI?_KFglNar-E+k%OtO$I;J~VIX%#;||F|n26ZJLi*_m~roT;8Gf?|LJK^QPT8aP6ETKJ## z$>G;afJ!F*(MJOC&S&YZ?TFA8`bwBlbveiFu09~(F}sY)?tdsc1h^eCu?O$IJ2Hv| zI3iZ$P?4pP7_On_zp~exHxq0>HA_^o%j+%kzqzff#HqXh$b%yiol%Os-MqPm_!Zg!$@zc3=t~8v5rQ_ zC{+X;S}{!_VC%sDVMzGzljnO!(uolA>3hO~IA1e2AhV_mrH|kKF0={8T2rX(CLP0yovS_O&If3U(VJ64`6W2Ew zs;ICZbGU7Y>)=HPSLEh(|5snsMzQU`SYR!nIDv=wE0{`Kfq+nb^Ib@v@f#$H;snIt zmT_^<|LOneoGJqL%avAP|1gn(d%+&+WMO{dv3&%v9qAJn0@= zXuSXaG=2B`n;NKX=j#ddh@n$*(*$cqnPyS>4JKX(sFhaQpw6%~iX?r1{Qo^zM*+$i zw#mtl|D%=wGiA5KO*_6M0-tZIB&te9sSq642lvUeI%sVF0?+(zTQ>Nqc4=|%3u2zouyS6;(X5$*TgO@Su_8YR7jQg=F4x zPwCna`wMZ&<&)0q2Zeb<`L(BgYGbWsBlhv84njO(tD>$o?83|Q#cjwKiihEeN#EB! zZ`YIMxiYiE{md%HkF43fKm;($AAE_SK%sydX>;Cbh888qc;@Nz{vhwz5nlvNsZ2c2 zqz}W==>YoksGs+KFNwnO9JC1+Cqk=$5XmLo?Uo>IZSC<5qNGGfi2PmXw&D=)yDEYQ z{@_)b-tN$av<~Ui7jQ=yIo?zL zFS_3PFUof98Yc!Ax}>CL010W3h9QQOQc*%01`r96j-gv>2nk66iCcsrL^=dvknZjw zrMvmgz2EQieD=Nf`~CsfFXwfg>p0drj%%$+?lfIlAv+2>l67vKEXmf)E5%v@ zN^-`4{ng95EVsc9nk|MMYmBiwEULWu?6riDT_p|v?!`gXYYxOOHjyAKw>J7tTyWnn z>jUz+$_5UfZ+8=KZ@athqkczRo`3yNadP^jb>jW|?yZyk>^=R@9Q-!OPaMynMhaXM z08}N4xGnS#*YWB_5eu!N3AvGS_#o#}%hQK2%vc`d*-?LWvb9QEht22m4SkF^X&%_z z>(Cy8M;6tz&;*H!!K5{vK_~`8lUixN&Q3YprTaUwKp-vvdf~+c=@)@z-!@5a3#`wA zK-hJ-IYXq$_QMib;a>#~!us0SmsA)nXz+04FZa0K-UYZ37!}%Guk>PeaE;jgTTjmm z*4PMqMj$EeM1_tgG&6Wxqn-UOC)?v&D%g3-E<6~XHI=#6}WZEbjq z>`aWfz@ncFBGX3#RIx-syT|(h~2PAAs=u z5EWA_Z1R*gz|oPZ5{z2{#SY2C9jo*v6;k)FNs@d7OO=Z_z_gY>w6r(G) zcTW?vdY*6ScYYiCLtMNW;n39x-zi$ZbFM0H+$_B zEZmz&)_^I}qhM-23B$Kon7?W2R;7ioFqQ)FuXtbQ=;SYy%qy;D{C@~aNlhCy-xYU) z4yd*onOnZ$zKabLeZZqw26B7<-DFDA2ZJ_%e0czv9E09Iw_ z{AKNa%t_v!#uz31o=L$1_fzTBQeDxpp^GJg6oDk*K(1pvykxKdd3Q%?t&*EK>P6a_ z>nk{(BpkV3v2wp#~Yvy=h_ryrl|Kb=Q~a+Zl&Wr z-yQ^-G*y#egXVCPFhoNm7y`8{9vnOdvtifz@w*l`Nr<@zEey!li z4%&qMCp@k`KPHx1jN>FlmCodI2%W^{B;hh_R3BnVD+963EzDrd9qDi?AJV(`E1ofb z@CV;F-xGVv!>Z!%g}9>>d|f~_geK^ z>-7=&Bg1b?(8#uQsDhpRk3Wr{dqE@Rav^FPyAUcHeg~@35yW-E&hI`%gW%b z9R4`V(V|Iabxoftk1_r)MU}GH6wD(8`rIZDv4xM?W};|fud!GWYB?sj zPmqvGUmklSG559KANL>@KkZ1T5R5T2$4nZ27{5gOv6ANv*L#iGuRdI(b(b)@>f9dz z{ceF5{DE+|^Ys;buGcK!B{hldG;Y{tq?C^%=Knjvbz>`ks16LAXKnHD_DUS^yG~VZ zSVAarU<%@NSo#|NHQa%#yr8;U8d9{*WymOI{}4~GEuof!RKyopU?PrQg#^nv1(O&a zVL{ONBEz?j(o9w$4+eh<8r}ed*iJzZwFg1znV3_o8=2!Jw0%fYS>5;{9YdRv&#LcqW;Ghz4r}9 zPnTXq2;Avn2~p`tYx8*4{n5;9EH(r>+OiY{zB=RSj+}qWJ}gQfvU`)cfQgU&B@8Kf z`=v$vG#c4uxzMpavu^3da+|zW_r%w?j0xiosH=(1=os*f*>6t~5&UG5K^^yri5)1R z5j|F_mrWC~c2_BF>a+IoF%QpsL>L|!1pteo4A3(2Q2cq+AqTTw(#3j#7{w(xe}7*{GC~!^Zh;L1>neo0B6^MgLT-a`g?`%c zV-cZy`}@a{mK|9t)Nioq-;@3#`i=9blK5AiZiVLWM{){N6!hS1au%#oM(1P&kC1lO z)1Qy=$39c@WsLP28wYcqtn@-RHdEnss`DDUGI?I;g19=JOH{($Ly5OLi`2n`m2Y((0e8@n`-K4zEYN`2z+)6$$)BX;wcu zTJr+uM@8Aj>3lD@=x73mrNq5WZ6=AnTiw!$|Kbt(dJy9@V`Y$8Q?f$*SGG+8y-EB; zMb7K5U;Jlhe+tm}qE}W=32ZsVoW8jU$!=@jDgVURW)*qeVEM^cVx5{+>T1*c92NM+ z>iCn5uTLaDV6%V~di{zRWFGF8G?x)IBladUmk^TVgRE zR|(_Cb%IXTZP?$(e#uADv6TIaZBS-kR^pi*a^Oa~T-cj{35#LwY6=TGkC>w41Hnry z&Q83LDDoW;?m3E;m*RLZNIN8rF$0@c0SgH-U%y#q`_R-D{6h)MdVGT=j}=xEMB>3F zC-4zH$_+GP~ z1~YhyH{I3XyYGDAO=T>3&28-6C~XFBoKS{%87|gqx&!V3scjg8*QF_3R=)uT)U{&u z;%l{lTC*^Y@Kb)U4D{Rjua8dhj^k%8-X@ME${o|i#G7rmD6+m%3&d=-BqzWJDim$k4Uzo9s zSUUZBm45J>x1!FkliEP)QY(7GTHQIVD*k|^_^){h+p!K)bM|68H$ zJ*64xlh+tGf+Z`_-udqeHeM$$uNiGOb-P}tnY|Cb>1#KayZhuU*;v{F6g(=;>{2&y zzX{-oB*!Wt{3NOM*>92j+td7L% zYyM2?(>SKn!&1C5j%%tQ4*X$&LgbQSxHW!HK;90RQ=BZ+AAbQZjlEoUSvx*AmIM~ZVgmb}MzhWcPoT^%e({d;H7re7 zkpRZuXAXcZ6W>EkAB9JQZuf8-&Q}pwcx7O^<{o+dcrq6w6%iyf`<}WU`51P*u7h8? zatVSet$q)6f%89L_;o!1?T*Bg?E#2Qo}vmd00+*CEa(Vh^3R8 z6@l9}og6W)D5LN0z6*4D!Ft3+j%EiD9H>CBIEA_^E6ANXvNYG9yXHy{sgAV=mWROo z?>{q<$FofJ{{B6;p;AH~-1J`1_V|{Nn2lD6Huds;XP&Uh{FR5q%|{;)7|fwSu^d7| zg+q?5n1Kfqu~K9p3TJ!E9{@+{*SyxeF7)T0(EsaOZAppx7Zp6Bs3=RIHCAX}a#%Z- zC6ExIpj70S>=igD&sz`fAk9lRIa_zzld>asxY%{;xSIby2wDG?WLWD{PB9DTw5a%& z`awY@kL?*{3GgLg!U=G5fA_tsBU_GkaIbn0lKu4#Ir-@BlUxOy*w4jCvVr5H*!9A@ z?xpOmZaHE>0pp)FGuLU}Z#*)}&+6A^dzj4{D@|(TY+EG-uihaYwMNYrinhz_GFHFb z?<76n5tGFKLfQLp@&hlEGD%&-i?q;kvh0ys5s{Jl0T%#}r&7E1e5HwiD{nFI{xW@K<{@=SxFosdY+THZa;%q|uq1!euN9+BrTO-?fW1v=%v zDX^Xg=1*6AHXn=`tiU5hjWAS%!?xREz58CbGdgzgAb330rnJC|kCOSyBG@~FpjSTG zu!?#e1`0`CSg+CzNVX%R;G*Ytw93S-bqT|k+m>D}5u865OPfa{(@2pcIYzJT+(LHh zi_*-AE~f{XMhoJ#Zl`#^mXWVSy%}|AIf3%|hvn!5M!BqKZuC!`2uC~H#WLbvT3Jpc$OOcF#kmYc z%pKzj52eYTs|WjOirWMrbY&u5>qi@+06ILHuT9xC(eyDlUr4LWPy|$=2jC|yf}69U z5PrpkFL|lfuT!~=FMb4G|LA8>d`@M32hvebKK@N)veZv22^JOcImy8FZpZFAnJs;? z#vUld$#|G7Ce|fSyB=iH(4%jG(p??jvMvk#X1~9Z6O;MAzKPmwN1eWvEwaSqway zcKqwh|9>YQM z=n4nb0M1*Lpq$R-aa!w}}0{|hsiBgFP7frIMy>DQ*MS>t8+-5{wB| z0GW~9QU;Nn#ZlidhBkp@Zt2e6U4iZ0*Oqg>IX>GQS@&0BQ0;`Ckx(l3!G5_WQyQ^J z%PHLCPl_cektG$y-@s86!VKKXgRYagw>x&E7}H~BCCdC{T}Zu8jonw&feg@-4g z!NJ>ov); zlQ%=QB-8u;`+loQngaIj?Ib*E-aW$SQJ@{Z$Wx|^0$lUyzO>i>`a1syz!enw& zH9rVH%gatyd9p1fTw6heKXYgpd`W66r+7UgkWSlX2NJx{=X!G=(|Qq8xBkiD6JNU5 z3Jfa|2U{Y9d+1y8AY&?G^k1y}x3aNTb0>_e&`jAf>Xs`_8%jz^T-g>6;O_9c^+`*fyxBBjqBZGkh% zMBpKctep-==pN0U#6VdxnH^-rSQX9Xbte~uGVoX;aMa?bfk?_g;WJYfm*0G#7wnCZ zSR;ev;zl8vtyo>)+M61{h}h8%>ywL)s3r~v)t}4{q58II`RLhD~6uoJ*tz!uO)1I5@V0y%$ZF{Tu8CxI=S+M*I zOQz|aaq`L`PU|Qjo1r>5qe|5gYU1kicqOV6p_2R4v-hdZgC4ff-22B{oDu#(+EqHJXLHnVeFvI?XJUYq%h7$5i+#UTvR6b^T9_HiiWC&D*SgHOz%Q*4Ct$J7iyKZ zVT_kJ)U}uy>n*8|k`;w~-L{>;)K(BbpY%QZ`a`p*G>6?CH8W8eb%NHcR32IDOHI~)}q#SvQ)t-Y!pClV@mScp3dQJ z)v!==;DlpqM%&katxtL5>tvb1aH+Jm+DpbmhPv9c=RCG(?88HcfSI za0EK)ZwS%SUU5~v)D2kCtq9m+zwUX;xGC;607F;91o}zurz2p!n}_o^e?@-WfByPI zFRSv%k*+_=GF1L$-Fem;r|@Od@t1#|B2DmKq{5?M2ccqA2$snZzXC0iI8XlWFhK&1 z8+{z?9b5}qI6nd$ZjTfG7R4=C@ZNs|7DOirBGjCjP~m;4ec5d$Z3*g#M06@ z*ghVp9Qz5smT}r;uZgkphNzYB^=0@dX*c%TsCq3lBq&_ zRol%U^Lt-t6QmjsN91gY{$sGHcuF3mE9 zqfld~MO~YT1&sAwC`4&7dAq{Urdl6@oI~&r1yQj4&vv@bQCl!D(zh_0RmAs-epWw< zMe9WE2-R;B7RloKMlV7AAVKcnz&U3v7k!*&cE=4f6!$&+MJFLyJg9AbiG9uS9L0|V z$+^v0K(-c#us`!RyZ{sgi$!@kp z0x_{OZ`NN17A~;jAl@GJ5nS%|bKQRbx8n0RLvuF=JSN<+a!%%ulzE{1C&v87%EW8l zgY;6t1+W-$d!D*`$zPFDs`MB^fi17}sIHbW#wK-}f*+=oCxCUX!}vluz7og(9L2~M zuF!J=!c`AF^h&v}-KnoGBer6qFKC>tZuHVdL0N1YAug zge7&fa%e@-t$=!q<*hn(?qrNdd@q(GC6xQivt6uO&#`wcGz3>v1+*S;a1!?|x8tv# zcpOlcru6iklTtc|MUY<=y@PSsvtjt0ye{=E z6SOk}9E*A-(`97FY~<>N3fQX?_n1qQ<<=8!I96`*M{g2KTQ6aP`6$k*Pq^n@^{Q{x z;vSCgY}$}yd#gK9MTWs*f;Qh@Y=agz# z(?*duxh67x3`7y)0K>UV4yR0K$v!AzhBJ66P(>OngcIBcWJ`1v7*An)x>Wr{3H3Ou7>f@LV5i61#d zJE)_|V1M1`e7~w!zyS!%4@PW;0^?ZZ8WMTje$lQ@l^ujSOSWM#c*fma^a18Z2#mBu zY&u1%e;HIwean?n^z2dC*$kDj(&P->jAU%?`V&)@N!`LV?Ckhwn9G;n+5f&m*gLx4 z#l-0OJF*M%z`|H3GS88IRlD0hYI9k&JsN8_lD;XRSM8&}2?SF^D(T6$r$=L8^j+Rk zj(oPp(qE3|8S>xD|D6OWs94NHE!GmZ8#cQ4`>le1SS*4q&RO!vOL`S9K|esUxb}r) zl@uw)RF6yOqS&R;Q*daYKZ2pXmQ))-T}YJp;D+6F21Rqj&Z?gJ9+j16DzD;K5kFC$ z65UVZ;5nUi* z$hw?TuNeRG%WeZB-ooByo6kGztd0*nZG$sCm4O;6CpC;|nmUORwH?II(a8=F?QGeU&*i?ll+t&%e1DvRY`V$q>L@U0Kj$mShEr=aXOq4`JT)P4f?uH-Qofx z@S~`y3FixM?)tLFR8S$M74peQ_Ewf%T&dFz-8WGxySV zPqr8)b%F*&yn;9WHzCWk$*b}n3jYXs%VF7UQ{GmliclGHCVo0JxD=58V~1Z?Sh=I+M*f1 zl6nFJ{a8ILh=u1_8m5rRk= ze^^P9LYA?OzJk0exsx#Xu?!Ln({!H<+enD)}@TthM~*&8V9B*%x_r&Pzvy z_&BkMmZnL%@}0=fc1rle8FA{IGW`Y+TzJ7EJ(|{XEKRGWM|wn)&S++|yKCvH;Ge*j zHw%8IVL_+&;R&!RL25lc;-UvVO`?j-QSK8_Yp)^iByLEgEo5)S<6fFHrx0lKd`JC} zgA`3)oGmb!VPwsc6f6rIJ66C!49BpxO5UB9-#fudp+YDZ5EYOrf6W*!jswb~IqDQJ(hQ3Vj7nb1f-5j>DhyB%L1s(vmJ!0r+!hK65cTLUnc{Kvm`SsB zuK!tdrgH-iR0L&R(6X~CSXNV=%)G~Zpd?KL>A;It zipc8cmA{C%K+;{Z$MnPgd?lZE=}}@?*}e{2Elx=2-D@t}(qwIRv<vnQgU8`Y6L9Ghj#YK{DW>HQCa)q1_B&+6hN=!B0>05WSl}MqHPG41*cp z=sajZjsEzrhB*e)Fq`&$Q!oOxPMZ0N6%towzJTQCw96a?nSgb0{LXRgu_ac*NZvpX z0LR--Z`ks<7`G_kGBSW8bsZ{f>}Cf%LMTf`%hh&75UQ$w?yc=fU|t{;hmxdFU>7tg zfIxl729rxJ1x1ilnVNTU_h3qGA((wf+n9p)ijv6R)KB7t%*rowSJ)7&7M3wpqQeUK zpdA%{=vk_4Mk(BY5ONX;B4CBP++1|an>{tVAd&Ze{g*oDbQA>@@N3O08RH(!ojJy2 zz$YDaKT{ibG(DqberrDYG~5lQANMY5@1ud)G<={#c3=vwT6VAQkfvLU0oseBNfWl$ ztykj}pB{BscgH)4|Io;|LUxEg`66qOLHV-VvnquH3AZC~l+!x__rss4p)3aIFrxnay8`?eXP)+7H12i#m1QLJ1YpRsNcMcrjWPeT z*|S|aiAfyS;Ccv-U#D6#A5+N6>+JpYo)j?29^L|wo{cS?4$V#x5@=0<9cgj(Q4*>fT;eRTiz$o8T~>y{E-2U+@s(R(i}8@FP20;ym-cDZd>hPObHfza zGH|wV6z&TSUe1C-a=%2pt5eGgztbD&P*x)4*&$7-BJAQ%G6kweE47bpk@EGlq6g|3 z?W#pPSrwv?BlDy~UnX!X8GE$6|1*m|QUO81-ohpgd_WbAb)?PO882Ty`n3}Fb3Z>u z;O&8Bq*99%j6L=eTS7Tz5Pg`Tg&sdL;CP{bX}a%gV6Aqg!L?{3o#=3v)T4TZ3jXRa z$iOVLL47@y7ZRGQ37Nrj;OX?(#%@aW>{u7B&MQ z-;3Yp9Q0A-jnfuhdXS_Jg2|{D67K99)hg6Pq=~Y1B(vumRLYm=^TohGYx%%r^+$wH zbmUf|&}b^{TFDC?!Q9KYI!nJ;?N%?fxU|OFe@S}mc+_z*N^$XOFgcFhuh6x=X?g#~ zQJ^$D#>bYAm%9zMd9B{PUXoq}4 z&ow==9~<Da^j4)HZ}lMNB{$m zGI@_;`U4{g1{plCA_Xkigk2I9!8YZIVoC%H(QP(zX$JalN$xitKU}HD#6F5E%CIGM zl1x9W?2nSH+g%zG7!5T6>7cm|591^~>3caj$QTmqp-KpVij0V2eC4(c5<$0083`qq ztEn8M(&Nx&HF_q_P6He5Cp$ujb^@GHO!>WPx0?zIC(T<$k-sIV?b3vM7n-3(IRU-_ zV2eqK3;01OtD|FRnlKvdrr~`u*+px2l^g}`J2_0#?gG6;j?f81gD5=Ccn{?NlvRJ) zVXlg2`lNQzyO}5o8wDROWG~EOj_rLEqa3=#I?^w41CQ24g{0C_W%_HEme*l!S@ zaZ7XLtpJmY0@3$1MOvwNK6Lx<8{>wrad2@TXTcV4t4a@LaP_x+-t{`)@_$zvpHXfG z0AEto%}CL3C&V~Z_?N@G_x|EMpi?F;NyNquz#qX043>6XeR%jjp^tEzk{Y{F+!}8O z2wVwy2~w@{6j4R95iVXYoIhN#l6qXPVYsP`k4W!7UywcXzGjJIas6&1Up2*x@tF={ zDf~I2e08FG)Mj?Bp*;Q8#5PuBHVjoCfO(wm@3{q(t1yKERqK02AxoEY=MC!Q6+aU-t9zLk#R6Kx{7gGNWakmjrAN9DetIzfB7`mCJa^h z^Ie#555x6o0W~;Dajn{_v~(x)&RxavS-jCf^ zOI$kJGodfk>o;B#=8bm;d1-?@A4YT31b|Baac2B7oAm||oglY2`feBla~+ULf;Kqh5pPQaD` zf1<){JY@Ep+@J`GhlS*Xikxt9rKOzx?&s_mlbi~eO2i%FQb=X_tO7N5*^-yw#}zC( z5zGhTNR9uEKQN7iUDAFRk>Bo73<2EL&k3)wk?a)Efz_=(n|cy5K-rvh^2udjO;fQo zwI8)I-m9}xg$;p}o?bn6D;l7OJw6l?aI#|0y@Zmg0>2`7a}8srBptjy_w-)Qq=3^d6VVP|JxVzMJ|v!&I^=c-z%J z-IU3zjDY*rNLeTsNnaqd`C&!_U+2?>t#kMc7xdu^H56l4(NS>P?fQT8qF&0Fk~BWB zjjsCq8+8#Gcwtzb`DI>8w1bP{MpP-q6Y{24!@CAE=Wit;b$~NKjJ+%n7QWBjd;zm~T%NQyz2> z+N#DAaa9i%w^I&z*EJjoLbw7ZKC~f1FY45yHV4+?9XVq77mP$xZ%))2lcsKV z7hArb$cQ*M9d=#(Jw7h_rj`(CeLgP{4JBi!cDFA*bX?P=0%dQBNNBkC5mu>?%TIlh zJZMk!?|;4#lJam-UtN%S$J3p~I==rlB}dQdj`x`H{zXK~2T^7XpruNLuXIK0-IZ-U zk0}w;@!$iTxU9ObGTDU4p5p5m~C5nfS-|0!=QuUCDr6Z8RA zCqD5)5v_~=+whJTBb)t+i{^Q=(JLB~Ru# zEP;!W5rY{5_Ifje=O>|YL{ctEaqPY4)R&t<)KVWd2!q)}6WkAS1+Ut)gazH(CXYW2 z#zI^yVx%XxQO{A2*snW62L7*{vE0?!%l@^r^qnqPnK7krQu9&u4;yHe?>Wmbpg%!Y zq>$oM`=iYSD)rVez! zfehqN8qAIdC%+$4ZCK&VtC6G5h48ikK0w}gw-UZFJzY{q6y3;B^@gan_+aDPZ)Il2 ziPK*xaxPU;2L+p6I0lr1KR_ad943m^vfH^cv$Py$inaY}VM*eAXxmk*^MUX_Ex~}6 z<|(8p!|d1TVAmtG9T5veV1L$&y|>c8((4bKm#R_7)X&`vJ5}1BN97^EvXnf1A4tu9 zbexVlTA0ntuWrto{hbcB*~v@tnJ=?9u0x(?M4p|6t4xMOfIQr5fTIZTZA{CujaEz@ zTlOI2PbQon=k=!E@V*{ZlwAx^z@$ziBkh)`o%z|#k7(T(Wp*!Mx|?4ksi$uj^v_3O zw=0Rxzf{Ivqdg8+PKjxm*FZm1-r5bShFw3Agj_6*9r2O!9pOJPpAZyh|6cVm=0mec z=r=Fs#KybZM9W?UzXwVVkp```5WSJ=!!&)sI1?nuW`hE#n&0F=%$;x1oT^8!~vF z>3#ICmhOic&A!^a{>3!YXhwO}a_^X-vEk-scJLO|NO%vuhaz}*1j&mD`WAe z5$C-#GcymBZ0d6yv%UnM{E3Y1nF8~pxgFnNR#V%wMgsI$V$nv3ybQGZ8tVqI_{kfWDigw=1SG9rDQN`>Q9 zErq2b2eo|Ww6{SL^G?2CnjPnI*!!9G)V>z*E4bUhTE1fljm((Q?~-}B*pYWrmByJ; z1+!GItoc_}N>Dm|S^UE=33(b38Yg!KX!lQu&9v<${|Yt32M7Nn)Y|9mE%a&+S1yzM zMLYRJxN*iW8;Eym6K=@?Wy^_gysL%-m7?Kr@?h9ToUHd_xaYyNls`wB+}?PyWtOza zKX(l<44~z227vFD*)Gc#R0FaW_gD0sBfg^)Ilrn?1vLeR*MQ+4u#AV|f#}F(O1`5+ zD!cs*!mc=Ip(7Q05SKxAPGlwDC}2p+fm;NmC$mOY3Jv zs}gyJqmFBf%X=Te3Yc`FR%DWLuNcz`JDgxJfqCiPBrbxZ`Ci5 zBY4$HQKs`-Py97rnJJWjH3db$P`Jxba%c1{FxC&CewIv@(cV`FZgv^Y3C|{Li1pxh zUysj=qLQIe9`J5(YEr7Gejg=~tzIm>x+$K@2YZ$(-a`pL{XzYBq!@vj%_`*6rF^L) z_N#^46nlm@KV6WJSR-(JaCQxpW7-+{aB*n@2>G^IoFFQ*IijXNRHU?%8oVCr6Tz6B z9c?xPjqn*MO7K}Bm#5bCPe!e~jr~z#@ckbz0Qwh!|2b%s`PZQFsc8D(lF>mz4Pgo1 z#QB2XJL*{CDZCSnI)q{dS9=4>f^|4SQej#Xi{9_P%V))qha zr1LGx?a*=Gj!~o=KMbq=GhOg!;~`!7pBgM8QBMrZx2!iE5vEE(V_;=HA_nCLs62xR zs>*qy%9~b4w{7_ib+Lb(*!|`VO7_{)h@&*AJV#1cwT|E8CwT3@kDK!WsiLc}%6HQ?9k z;TFcM=O`LB&P~q=)_R3l3M_2dq>vr%tI^=5H^pk8_@$xHCrP^GBH?8yW$EPc0!Le` zj7K8hF{z19JP{f!^*|Q3B?X);N&-9ICTew-W-`E}Was1VQmD)6sBH!;iM=!G+aFZi<+Jhf?J-GW*+Hkg!cOus4#_NCdyy@GrMMmCjj zlTP4SYR8_1(#+5pLA}W8877Bl49OAqWAbIF2m5`>6N&FTM+|EjGXqN5p`Q~3dKFFC zm#5AB&knOVghRiHd&(RyirSHxWIDLvS^J(EM0R#$I9FMu-n~_0TK?$!aP1$pUL7 z37hXJ95z4#8NhuMiINJPi0Hbt^(TdU%*MSVYW%qx^Yo+a@?NaJGz;-c#$!4xg-1n^!cS%|rl4yIGGm zgPDKg%nqm+UoC5uw-ANIy{w9igDZ|%plChdt{|aPq;@}J6s=2g z`STpy zC_Gn?3!S_10M}Hi943ptcD(Vd(mnXQf^i#vp6-#2mO!9#1g!f3;4!Wjhbpb6d9XAU zbYVbde-4qzy~vFra*UZfWFm$41enqlF@_9f3JdCoD|nA_W!Ph`ABXF$@X`BS^`cPYGj&gOn4b99brAYMjkPLK`pm{M z4VnoR?!wkY6Yxa%Q$d}&kmM{~?HS*y(sx#3Ai!MlNC*Ed2?JR5 z?P|5v=M5;N06`!HJ^j*y; zI1Q&?;#0mvaE3GQDE~EHiAU*Pr{j0}D3X;dfIWb0a=WHF!K`r9^#$x;#6d9bOwBlV zekkDYVNK_Nz}LMku~gj~UDKPPy?Wu?nWfSSXrteiE1w_pdc4W%sT=*O)YGKxjO7CH zUy`1S7(<}`8-{n~Jt(b0=@xyeDFyw1)``v9f0f%YqA0wTkTniD^+dJ<+NR#N=_zQl zUwh5_R03af@@2|8MnRO$k)>=1Oe=|h7xl$rf!-B?FTM0@f?HNkka5AbYL@ zfThxs;3yRHG!UpYCd0?DMqDovuO5ArbKX>3Kf#qYCQiB*dAI$MLqWrLto@-;0X3mA z+=5Wg+VYp}-Da<|e-&%)Schuh+4i5EoUE+TtatQA>oS%5u{y0OY-@d zxqcSxvHihHw3qOweoF~ANxhzTm4kDYbgA$I$2WsguDce+@>=QSZk8mi{R}_9w#vUm z#yEN=Z5icvvr=bawtZHN|FHu7_xRyo0JtvR69h67;-}`s<%m)v0*qc6z9@BS+^seK zWKS8y$Tc;HWvu^{nGIx|Jk3~j%AO{RR>ptc+%`wwUf*_iyB`<`{ZwAR{wZA^`LSNO z%eAeq!N^uUGH+3_6#^r?=&>Wl30A)_Y>xfps~lpE0%RV|$X%n9I1rdtntbO)0vyE_ z&m>iKSq}1>a^iZfOd$M-l}9n2LRTg}NfV#yD@2n@R1(>r(F`9cN{{Re#Qab9{}J|< zQEh(R+HXtI;_gH_T=&qL_Yqx(+5a-}8b1G|<|Ir{da0yvl54{Eu(7~I zu9Qu8;Vdml0iO>^S#rQZrb3mR$xgYCvmNSM6{jqAFUq}SiZv#fOv1Ax-fP%Q)_8lo zPyFF9`Tg)ccpVn$e}S<~X1t1{UgC+F+GYun6A?L3Di#A&^AlJmlX0rDVk@F4$?e+X zE$w6nIfwLA#Z0g6L$84r06s&UU1_3NmA1rscpt8RK=G(_1j>OupY+7qbm1|N%RY5e ze6f)Ov9?;*-g+~lk83?tn*OmQNfUXQOih+}_6nOxMN%3^ai_iRK?+4*0Im$}%A*m# z=j)kB)pQWfkvFJsn71S`=sc!AsEql>ff8u7%YYC)zLDP?5EHxtwUJqCc)DP7aA-v# zz;e+eh0rOEjSBXDQT(C*HPiDibwHq<{7DHLz_&-~0~ran;tBs#+IKq7jhg4Hu;bO-?Fj z8eCs0e{VL=0xjGkDlci z+2X*O`27DW!ZRCLdR%4im`MMywu7GFy_1vEKrZwZIZ@1@;;fkqxI9RCti6y-8^z@5%QY^#$SNXwE3818Pj-{piRcqRwNk%D%We zHcapNWS17sK?e8bG?}EGS29SXI+OhS@)FFgvTCLs{i3rQ1VQ<6E5WUFTyeeDLh$q&R6%i7_q7~>H3b;twyx5L4o19x?p%gE)b>>Q}G^s+D6j<#QTAenam-R$xI?R176@tXK$_RWcS z*mC17_gz|EXpki(5^duw1nAVTnXi?a7vwqibXBQ_G<{^vnlxh7;3}rVik4wx@cy0g zrh4<_q?^=jh0rrT_kvQWV9}e~KaYZ*tyh?N;=xQ!!7GBs-Z>_YD$O@Qnl>8n? z@gypGGTw}hyevuYTc%7Mhmms>o=E67Hgac0&a*eg=IJElX#Q?}9ogC>>z_|O33m?4 z+uIQOt9%vfg!HP}ir)!Y;6;|rP7TPBPBCLm&aAOj1a2Znv{bD$%DbkZGF1^ z^-vk4*GgA1Ki_Yj`4HXC4zSKMqdJdPzWv>`?kx2>|lBiDu+FWpia+Uow4R+c7B7o=ey$$m(gWi7$ zX**0GhoBvgW2!M*mK(=otzWm!e{^re3EujEQRA;CURq}|XXIiAGd%~4Hl`tOn`_%G zPPRwDVh+Oxtl2IiD4&?j^Alx6|J{i<_#j8$wmOXt({}?l=PpePGsGHs`+kRY_-V*X z3U;Aa>DY^pPuVEL@SnKg=Mv=;893-A^2}l(%sii+(7F@0 z28L`gMkw80@#vQXCdUgdN$(Nd2tIV0m9W7MJzHh$uN8caqPu~w;WCar z@{D%Aq$n)sb% zcJt^S`7?4mh^dIh3Mi#?!g9QH;1~YM4E^t5+?`NYA9V_f zTWs_Zl5a80Y&>fBX_fct<*46hqSf^F6%kx5mT59wyN|M)+>(mJdgYZ^LB5jJl$END z^9jTER+w87Y#0`I}ohSLh~3#vuUOf(f@`jnr~0Tc~B_9-HsTi!u9VDlZ{TLT#_^$#HvYDZ-z2I0ikg?DHT@ zEb}SLTsnpmo`%@^AV63iIa0N(@#jCI_j(enYMxaye5)RDHwozk#+h~x@a%cqCllJZ z1h<iZ)*my&|?*$*qA$4nStpu$6IO)lH7_)H@`n%*gJkOMN2aJx{Of4T? z|4T&EQvN-c1sg|g*zJs6%X>VQ?a3oMLe%_EHkQrFZb9jS?NeQ;M6$C{X9eGQ`J-f| z0g6f6fG0$|MT7*K<-#l0nz`wbU9aS&_-pM7eQ&89MaY?&n4LGJrFXwUkKrFZSo)f@ zB?GjA77|?ov|F#5wCT&+94T2Zj|aFukJ?oa)!uh;VsiG~6iq~rmjcc@2|RWvB?B@A zS3Oa~nT$wfTvIb%)YD7wuArz^9U}eXf@B)l=c?Gv{UxhVq~AZYzAb5F@3Qs?Uv1?Z1wvMBNaG^=GX(d;CqDEEIBsXVS8vzcXbCyE=(887h+%JzV!11iyT*Kz zd%IRL06ui&Kd%=N&REPPH!p*M9j_l&l&XBx0}TaMAA&q3vtuLCoXJ(U^t9nd0i`&q*#q z!VTBD8Su^z=c&|Jc~xhB*TOU8lUM7=URYUuB9Mt-udYxnEh%ziS@@{@YTVdYqL>M` zo^&<6Q=J}o;TUuwICakh=I`H>RFlct?pRv#p3))~I9aR$i%(A4kNs@hoEUMvl?CSU zw{d1^GX7h6`Y67mpcI{u@+r2M`E>LGGf~{rzf$iPxANW_?tbwT(dZ4?E=BZ92eY|K z?G0zT59JBNw!L7PRc0@OFxe7kvbo4`a(y1KBwAvd5s`Il#&lN&$!|bsvqp3Hfn>Nf zFJHr79QXO(vGn&mQpqQWxk)~~lDR@ktc7#HW`)^<8ty^Wy{%*M!GY_k5{~y>LjuT~ z-T2oPLGB(faKz!MneN0g8zu-nQe|yC}4Mi-VLNX_3(*?Za#U9k4@po^yE96$H2XA_*_=f zu2aO|mfX}aJdt0OfF8b1*lx}NRK-?U&xUw&xU9r0g_8*cO-3+1$>^IBbo#N}>P2&V z#zE=fFXNrDQ*4?uVO=KVUT(;5i8GScPgv?kZf+Vm;CsXLl-fyVKzhtMP z#T6%URcX$>bQe-qhi(7VeXc0r6ERRbj{0-<<)4p8_^-h{7V`7SqTrQYgMEjLtF4H7 zVlq;*D^?HvFP~t*JC({yTn?La(tHX>nzFO=JC8Rhy{3_W;SJlX?c^RPaagKiUk`3} zLV_)3zhx4ve1rP_6Vb7?8jrWJ+pvC+A8lSgFXAhOnr=AQ+386Nvxvc%9x()a^I_F% zF?n|Ygg^LS@7tS$w3B@FmfX=l<4ep*@648bNu-)TRdENE^|j6Gm-Ax_ZHVqb#L%m* zQ*+)ZcEs$ejMT=`asQ^_WYtrQ%?)aZr&oJR-z*9_47KeVx(EPYLm~`eP zQ&yW{YR@GDw8@N8+3o+}6CCISOLXVg-?%g+~@q9j;Pmgf!L;(BC zgs3y4ayCQy4#u5aCC0`?M*=pi;&M$kPY#aorFpV&=1@qMxKa-tWY@D)TRJ=GN!6Zj z9sQ@R@9?BOMQ@ZNspY(lJ{A3Isrl%FD;l=!Q?_%k`^AAsZWATj$U=heeh*eRec_*ZJd2dqi!4*BPNDF z-{~JV*|pNJ4!9$0Lchfdo3AXYVO!#MxQcyc8gE18&R-6^H_#q;uG#pFkDzwwPou}c zu2%-$clQE&rv`QXYf;3BR#Ohcam(8D))fHLL%7wNRyEeMattk%y2na^#@4mC^>!i% z-(Ks!5SdjngiqNK=}!#!wf#23aJpqkmuKbwu5&}nuDWbOVBxY~$CtG24P7#K%%-xG#T*L+r-+W~rQ06u+}s_7U$Xa_=cn{maAbF!<9CgQBe=oE!8$ zga>)_Z$xTxzW07L+Tv9VRkR-+TFJ_ zYHCk@XgiG2uPcgNeC`=eer@u!?UGD#gF{0N&+;rH$(Iv10pE^-6wL;_dKTm7__c7P zHAt0`ovh$VIceJ*o~hWn!bO*-!i?XabVzxLgfdFBd256@Lum2!)|2Y~osD z`OhkW86~jWDeQN}aS1ezFP?n0(f$M2Kbt1jB_APYc6*SqVQxzDX~)&|bH??=*BX0h zruA>u=A{Dhb%*(;W^wuXrXLDc0uNLKL?mcFasaWw`R^}wRe%gU`Cw1@eF|EuE-t^1 z&F;^A>2Ed>H++K5gub&qUP@bXD|PN31)7a1d=>wp%18B!Idc*>gnug~!Ylt<@%`b@ zs4~gd&jbtt-{Xc>j#}OY6PBn*@*bZjpN!jIl7m+@Q5ak~ zJx!s)HE8VEg%ccDv-L$lEmP{=l3Q0je+}KNnrRg!s_9utezZfy zIu^g2aOJ7lu(QrB2D(5GZKQy54EI&~E4uvZ=I=H=Z{si8?Kx6c(=R;tW!vMR#~`~L zcdx-Cek0u8y#935ns@>qWB(GR=pVm3*Vf(qjYLZA=w?EW1#|(W#<5$!T5+mOhVfF1J%kMiHf%6{6>_^A9`^7huV8wpJK6DMkW ztsApkNSN3n$0!{&Vh044bOkqn$4=()HrnreSMQU1`TZ>ddQPpY9dmCQkdeQdh0ciw zk7&YhFaybKZwmz1{EqpeUvcxrqk=M+1#o7t-fyY|o4ov{Cg&zfEiaoTMnwLNgD3r@ zh{iT6Cw-hQ{4D=QSsPl97Sf?SD zw9VJ#s)Fg$M1Kh_VEYc&v{7cYB!B3L-Q6i@_a}?RE|e)e{5JhdVE;G|K3s0zQ1!Wu z0*_>cNFP1Gt|LivNM(kDyIkO^lfgl)Ytq8;YA`C^361D&fUAft>C?z8AL!I^^pAfD zMo&Z0h$5W?z(9tmA)cLvFyg3^6uquj(?|$+P^N} zunh}o*!(D01kMA;9+tEk?a?F2FX}>paB?|`YGN^bt7J_nvwONl6zKM!rL6nYLhg>A z6nfnk%mWA>$nO`%^M1SQKSyhOGYg#hL*nWw7vOh1EYap1UbUDk0Lsv^Il9^`!+C1b zWpAGi!*B(??HaMwmSDpKsh!b|Ws&u}JOiQE8xD(g`qlcWBsDG;tn(##+JctEw8mTr z=Vka3Vkk)@#78rQxB!)JO`-%Uf5M*!3Us}#3zu{@kkJ*RGdD5e0M?teLh>)>!~Ksq z@OqL!obxhLqs?2fTD~<(ZRFijP6EFV(^p8KGx6pNhgwa$udyzvIgZ8EE|2x&&%d@j zI)D4=qiIFS9FuZI!Z@2ll>&aUqC3Bw3Q-H_rN>4O_eUD=V#w={0un|7O}k%iATWt60G0Q+{ zNeZ-O20Iy_OyfVOEnw+hQ`En2|b1;zkUMb@-D!J1WL2EI$)}wQq_yQ+DIdwf^ ztsxSarIu%vt~B^En94De{Uz8+pMu%ZSh6a=?LM^VW_}&{kRGmnoqWQ-{Lwfaqq&FrL@mxT#WBd%vOT>US+io2%r zX9{M<%=d6-|1jIgrpg9F#NXEX_w-d?o1yvdeM`}h-wNNS?@?34@-I$z7EI*gg*2Z@ zoBiovBU-?o6FU+bollnGB+Mck=Mhe=^_*)>k&$ofFuSiwVwm@=q?^_*Ti}I7QVcSQ8UCz4kAMeS1CuQ6@n#{&u)!ubVcb z0M1c9nidv+P+n3tLkFt@As^ z6`I(4ab4QofToCJ!bn!7ds-(F{M3%CZ(GvC@5s4O@?+^%m&lKkh&?fTyyr+#TOV*J zeWUY}?wr#o7T<|`n3W^vys^~3hk-F21r$7GZK?b=xIQzEG2s-kL?2qE>M4?nTy~!` z8o^i7N@fRoo6ufo=(BzT!y3={4=V$6FZC*LbMH;=*m@M7WSl6-2orHe%9x`Wuy`6~ zjobx4rn@MjuvbOILLrQm1E1fk>|$f^@k8$o3QstrX%h)M>;pvf!;JrA{-o{U5EGFT zd8ztDLAbos*$mm$!p%Eik#A#_;UV7Qt*H3eZfrn_%vo2YC{g_QhW+ut=DjwXfi+%n zfX_*phz!3@vbs~km@VYy9q&9J=I!S{fWs3uMIh_~mUjsLD*%d)>zD+24#YX(jCwzV zbBGT|4eSnh3+1Jk3YA_47_l>VOxA@Vt3lhB-Swe!KLP_ zHhaAew5odtwq=^U1w4)8fbx0Du!7frJNC)&+AZFDG?69dYSQr$mGJ7V`ab%wX%NeL z=x%qci+7?<{?}Y*^t;(H)~6`p;_W|lzJLE03+<4+^hhb2NyoC}#nH5xAn(6WR{vD+ z9}r=cJ4)_K!OUKwh}m`h@D8!_OFli(4<-7~r~SWtxF$v{AIW#c^dskee6-#MPv!`6 z0!{wi?fhSu0>XRs5P0SVH#zu?PiyIa=dJ%a@-kLP6H|?;rmfG{e^bBzAA3Ow{k?;= zj9$Ce=pTNA|M~H|H2Bk71t_f4;MS>=U-@s0aU2#1lUR$97d{H^diY^C0b4nx5ZVaU*nH%K!6U zXu%;*e8ceK-MHyE(r4EG|L3bGBn3qgwI$N{ndL77{Hl{Xd>plZ7yVyvFHRgHetp}H zqaRGYdi%G2LuFReA_nSZyKxDOc7xpa5%<_%N~^hR`?cLyK6+=E1N7K`{P;U&zw6?v zULdZ&S-YiF2z_o+$&EBrTRZl`{RRKG`EoyAht#Kz&xtXsMNZ7j)wT}vYnxe%Zf}!} zTTkRP13h2OKd^UbX(@pD{jAe4sm>4P%7X+bf1ee;jxfIXLnwSV^0HV1G;6oAe^gK& z>Vdhr6!tq>a^Sz$bp10b_-`KJ3K4c7|7#JBBk*p-oK3&AMj9Y!XE|vx7iIGHx=&C_ z>T%=2jJO{17iXFF&ZqES94FIlTl&OKP9_uEwKJ3AodVcG^_{id3ClkJ_v1@q@ntSI zQw}ocDTE+47vmAam+2sz&V=Lmws^cLSlyyy&xwoQC-mSsA6M7}LP4_yo}4KW@M`M! z*oliRoctK-66Mp=!wQi;V)b|j-WWeLrSsN=YK%$=UcvqJ;`lc`J5hKrM_Nl7=*aXE zkFUguqOk8Q|67P<|5yU;QM}h!D_5k8G+z+t&NeOYR@x3Pkwk4kM!aGhMu z&To&Yu_SnXr7of+&_BX#Q%q7|$;Y|0&=F>`>{f{CaLZ15(UAIJ=FOij|HZ{frQsl< zHjp;^$49>Lg$Y0a$%;=(!`X4~ET-0g`i?4pn)Ze4U9 zIhEP8p*}hLW!+EuTXKiCzrfp?AM7EyzxM6_?-hLVoqe2z(OH(ej{k~#`O?4e>`S&A zl&;CtxqhPKou!RRKMG`{H@M*fnp1&|=hnyb*DXjUTvvA!zR4k{JPzQlqpLO@JWjJO zg4RVZbND4z8pS;~Y3vX7v2CH?fJIjxC&c5D#vh`=mT;*T8yLppxY_D0P1~ttyH#x! zFeiE;D0a^Iw>hi_%4%E!5jn~e0maNXjlHMOvH&?>g_%4d{82JElo~9=@q6Px7r?(% zYu`q$NMikt0WX4H?6qaqI+j0$8&QOixZ@aYDWbJ2WTH4~gtD3kO>D ziQpB%YaeITSYb)NA>h0pqJf&jEv12=}^8L@!-og zjbmJ;_M+diQGIBa+_v8m@4^(+IcP?8erJ$eg;^Ru&?Fu*B{wfV*T(!VwCSOUsb*;xXUPJ}NWE_0;&1&LOkVGP zs)$f4J64F!1JQa>twD z&h2cswomDA&B=E$_%etd%H`F;ed~YB6(=sN!$}XWl=|xvUfT^((ZaF1sD6hkmm>WZE*-CERXPxN9<{Kr}N8;v`hoBm@E@dx2Sff>b`RM#Us5RE4RXkE4}7!a(?yA7iAM;5F9HjoF3R z+7z}iBNnC06L`4=;{ejmvt-?7uL7@iMxkH&Sy)U-@2`hV7oQZcqWZ)*jJ~B0ZQfaz zJtw&7UbCnz2%yY<;9_ z?I*E6AG@|a_iMLpBoDj(8VZ4pLzx(nm>nLj#dDwsWXkp(+-JJ7@>Xy3rHJpMkH3uX zb;+E|xwc5;ArlWP2AtzJ-*YkH$1TQzm$39w8{oaTh1wiRWG00cS~T(rv|`W=Cpo9C z>&YVE#YeDi+YC6M>6`23B1hTfprnVEmnB>heKF^&W+T*JO1#4&>M*>UE`0D!Eyd=1 znQ$nWcxx-e(xQoXw<(hVK3}auWKnKObhIiSm}+upTp{5wl1uAFvu*Sy9k0-Db)|CU zBzg5{mP5)xAfZukB<9MchvwCzE8=J^v4t0}T3m+cn&$f^3XP!t0ce!}RcA8{(Vu6o zj=DNKr0$L)iYABPrnXC;B|PL>20E##$h-tuw}?2o4(Cb=(hQql{|ynm<1Fzv5)I+C z(R2DA%v41gyZb8Symi~Zp&B|>tRtwwm~rRawb0<#7mCC6_qmfQh?s7C>l`C>%~QL3 zy=HGk^iOlEDZRT6oD@6?Ag5#BZBq4zj>xz<;{r6r-8NHsm_m zKXCfd#BmV@kx<2C-M^v;Ya^b8vI_(ME|yt3TVO`^B#TsP+NTTIBW1;7GTXGEbmm76 zf=g_Hsp$7!SADT#XlO!}@N~|6warS){1$7DkqzcXfsA%okdd4}Fy63jaCt|TVpZbo zx4m$0hr|YdgM)~ogV^Ulz=vNr1+?YxbQ}9iwoKHfuz*Mk9$oCU_tFB+StrnGLohgS9x=nb4-uDJo7uE+ z@%ibNLJyYxTRX7C0Oey%V-@*^G!4mp<*V8>9OFQm`4B>=2A@WATiz3lTle!<#pk48M@dcT( z$0^b%-7VwxqZi>6UU?7|ncHzU+Xc7$PZ5N(t(t}z9c{n5J3tM-&w>%#Av9?u?acy58*(1+4(b-_S61v zEL*FQ#fzXmxCkEOnK_jUW4%iGYx`&K^R;Xji&v=;HhcP2bzT;q>C1<+sH+`DqTf&D z@$Kv|+FU%J`Ww2~=y7*}5NPofOUd6e>-visDdrl3ou;euo7+&NL#|Sm zwLl8XyG_!KYT6ZgM%qTp1;j@QL3ErB#8-1k>Szj44nxNw-J}IcWX_xrgO4!6fS}#^ zN|-vno@3I8Ptuv+@5I`wj>8$)q9ca8&$2aOI5?NH0DJ9VJcPq1Rfn9h}aoa6_y9$(h@ z;5*D-rTi)mV(sF%zdR(ZNU&}M8dc6G z^+#}N1Srt1153XF@Ox|~ISKOFHNI#AUrf5pSe8RYgGB)X3!5p#1kPpRcPJ;G5Ts?o zq9BW<5It-AO-dmwbvZ-t^L;-ky5PU`@q!*Fga<+de5Q(gmwre3ur@uh+)PW%4jH1y z2ewhJBO`bOJcyLAbzbzG650b5_ckD16FmBF?b$=hpLj+{+zma zZ_a-<_gZEI3jwc+1bbs?<6~hM*v$ooKciRP%@n~}{hqR&8N}rG%Gs|gV?!l>Sr&IV zP-ql0XW8#T3#7Cw^h< z>j+UC66V7tWg(BPbA{q9_VstrM)q_2InxTFJ~JFv?_}5)#M+K2JP`kEwxYp1STw)I zn;791E|}s?)iwEA?-}zgsArt^&$Y{z=~Gd0KF)jk?PCB&eb}tsNM^QN7FPhy%sdsS za(4-+YE&j-6_WWr=Fgp%jWOd({c!A{yzRDWXxbB*?LyIdbspCy8`!&XI{Pg{ELeD( z#9tV9*b*3s-e)dP-Ky0=?~!+3rxQmIe#w))?aee4OJ`{FF_G5|ioKGd5&nTPDK~33 z6ZvhL9?W|UWJGjO%4*=c>!Mb@bz5!{ifz$lWBt+droC5$Dm~xZ107o=eAJzV+})kU zz*EM?#;|*TDXornKEv2pp2>>58Av2tquQM`m)y}J=pFCJXPrTRIXXUaJzvJBWc*=4rbG2ibcfc#guK`8-Q?G|+mSE+mX2FRNP|jnsWZL2 zOLbWG@=5Zs)10&K&P1*iaru9eIEX~rl+<*`fcoWa6JkO6*_5Ee221dB0PW7|g#p}O zch)3Il61A=gGo8b62pNJx^Pv!>cOFK`7~N^yVQ80u(j1MK)m*5;){HdQc96CWVpST zvcF^w6!94&-3QITKs=r~L@pHiy`C%gJLrw+35ZB2|6h{AjG_g7Z||M^27bLiwME}}eo;mMxx&1CA$VZA{p;rFT%6rJbtm`?dD_B6k$-ED z3Y0OUgWl{3Xt*5N>^gloN@?c@?9J||`}q%bHxh|AfBpnQ_cw>jtu+jQ#-o?xnMrWf3!7ZW`f z*_AFD)%Vb{+hd9SxO5n=7igm%!xuCj&D?@o?M6Qne~Epf4{YLUW3vt1I70kEtMr`r z1DWt3-o0^4UZmzmoM5q0X;|vJEmyx7b5~=*x`p#8;rs2%C1bW5>-y|HMmg{pFM*$a zVD{9zR_GUSmKfV^&S5miL~V^h(`eOtv45`8X&iCqc&xQnSaNiRa74=v+F1@L&`_{h zQzt6TzrgP0dYYj?(0WfZ!`xtN{7jvLYTcUfwWdF51y|JS9z%8cdnssteSGp28wVSk zlK>l^{W@=0(;;~tGj^?=P7tpA`^Wj{Adl#==V-v{IcPLsM`q_zVAY-GORH-H{TWW| zT_oeikbz(kWwP8a_5{^tO8w%|&dq0%bfReXp8i#}))l3T%P4u8B02j>0##;_a0^YP z>Tg(R21csDm&tf)LUBEEe3fBBx$&ER67sQ0ZeaVz4-3eyzp=2jb*D)@t4aDWr|_sC zb~_+~MatyqW(a=;ae0VTC+XED*}EfQEB`=Tk*Xub(_D@%>7Bp!Ed;U0co~E@?%yRLhlozqz&TJ2|tFN zzCq5e$US)XO;h379X8ehTPXbJGXc87B6mqXcLND>Pe90zrHEJUgzl={4S-8ctG zwL&f3`LIF@$(hBeU{fNAc~azIKhuAu1~cm;%j+v2r(+oU2R}YmKj)M$SJ#?r9}zX* zldR;q(@G`G%f!=cJ~{fyjTp{4KPSm7+*$jfCYmH1Aj0i;=jh` zkVgMFK^7j_Yh%w%?fl4Rke|ooRGy z5&U62nwt$}UYup;2Cyh@*b0feMKv_6JyU!0yNh@rE@nsi&yW{!2*nNZZdh>8HQ7*r zZn(Qnmhfe308VZCa)4Ko8ml}Y7P7YBgv@nS+_%WUtW zhVh?}WQb`m1Tk+*V|)GO^e9$0l6Tui1)AtTZCLln%Lrb8ZCrIkW_V&E-cqCO3}tsN zAMRyzH!~(^a;;jsHS({Qlr%oQP}qLnvDhyQS$=)SPSbB^)4$iYQ-d1FqkCNE={#n+ z#Jl5TYT0G;r$|XcT&_z05D2jccn*}cni$MvcN?PFv7Lo^W1}+h58-2 zS4YzuL(qzJ-ukvA$GhjlW?@S^`8%E=bCBz_chYUt$iMy8$aF9ZEyG00!v2^S2XmGm zemh#TiF{y?i?QY0px#*~>N?mr)Du3Q66$)(R;Nx*v=Nj$MaAT(7^hfqbT|&m*Z@Awh_yt`)y8rAHECB?Bm_`bTdp z#wM=i+Tz`mu&|!3uXU-|+j9-qL?DZf%do@IB+D35hgMn_lSZ-Hua?hi>fpO-!tW4m2eRne1Et`Vh{i zn$YltWcaiqZd;pjbz&wcr=ggkWA*(HVgagb?{_ciI%oD2 zqQ2A9P`eONlNj4BlC9(sX&7@zQSg!*i&BXNi((1jb)mix(5kmzB>WOsY4D(8!1?;B z_ldCei*I3@?^K^ghFf?ug>(wB#C(M^J^n-mDNdRfQZb9n~lRwD7HajyFA zByWOlMka$p>tVW4vRY2nXNKh142Rh3{Y$!rO%uxWV+(p0#A_3U7#nZZT1dHl*g1G3OSH8 zwO7~Ce)anO&z*0xJ3|MjuVu};gLWj})kd=I>@?0UG)g}Ah)&(8GE#}=J$Z(-eANpc z)mixCvAantaRn_Zv7&j^dl5^`RRqNU%9L7~9Ya+m&0x{gG_&rr)i#nnGn0YbHq{m& zp`s@E0Isemau@tsTKwa2-D4+}_|8}_iuY3&d{r5=Y&(}O{#m|8Di#4)zw1u7!?T{G zbnAsq778y-vfED!@mba~a{-wItA3?#JTlg4&UX>SMSx`m(k)OA&`qSmpsOdT4if@i zNTRRn3U-3jR%yk0>v`(0CrOv;E%>^=DCw;7UIu$rzT-dX8f~RxRX-a8j`aMRzi(b+ z%r78Q`SqbbQ$B$PE%}LH2Srsi7F~ePh(Ndt7%NRW&py}DWGBcDTMTyGo*9ob^|71! za(3DzK<`15NrPi*)DW2fw~&s4(;$qc!|A#zLa}06x2`Dl@QM7JqX4@EV)@Iq9U;hs zpR`+9u5F~E(2!9w;Ek0|OLz%*O{WZ5iT>Oe&>jf&05oXxN`+GDXQC1AXS z|NP85C*?(6bzWn0Cn=I_Ce*||!`%Ix#-!1UY8q@;_L435TE!rEI8y34wQY(<<^EZU zK~x?edP;D`B0*2UQ0d3d?RtL-lRQ3WyLMH3(9IK&Q-;TocSu(pPlLPX}yeY|= zV-`|5eQ$bEu=+O;DoL+W5t~g#@!+cB@V{9AM_G9ZSxy$pN2HTVY%aVY*2|jGr3-I~ zT4AWwde8jRKLISw=Q?u8VjSa&cT#e-`&_VV;J1id8$S=xJWj%`3WF~nW2scUWVb%7 znno!P<#(9-9Vy4EXnT#fMVHUagO15nDrq#$>-teocrl&F`4OSU zL|c7YZyjvN_Zf;IuFepY+i0+9)1UiG462BcO6ro3e43B~F0pA2wQ>vEq)#^wL$&Zr zTv$;E9AC=P=(-p;X4vb8TdI~%d*jD)k0`#r5@~6=wtjjMnm+@rS>IaiwT5Yk{#;^r!`jV03i1C*isYZo`Id*0xv z%26HVz$ZxieWF@4-`(Rto)ZO#WU^Lc>W{jzKUj{@w3iO`om3QKcQgTr9BKBRq^@VS z2m1K23=#K#ytuKj{je}+FrY_d(CuJ8=GLcZtKi;!{%9*(*5#qP<5r>j-HWFJt+83L z+w0iAu-Z$+)0;gBzSWn6+4DP;Ok5Ys@GZL8?*;JUwKM8@W4D3sqcBp1*FN}D^Q`D` z+$x(vKh?8lqscp@7b+Ear9pL##UCn*{kmk>>Wf|+TQDK}kTz8DGG{ZN$P)=Fia zq0c5=D(3`wSwsF%SJSzZG^{BR&GOJPmPhHKN6Zf!jtUczBYbr5PS|@MxBlFubJ_D$o_cw1kNe;-5{cm1(~Tt2#c~m1cp{`O zO}?W(R!~&5UN?AP*{W;hkKg4OsR)!Hqv4p5jUUM@@GZz$dHv>xoKX$~!coM^t#YGk zDdO`FXhGGyu;P|d)tc-lqWymDM>R!-6Lflb%8@u{Th!5yy}JA(u;X*J2Gl<15q-e$CfUA*%7|^5dDeYc*3zP%(VHXK&ndb9?gQUfLGMbF&&K zI~el6mRFb7Rh(n%=^<7L2oN^%7dXWkCWP=l!z-srPyz6KD>B`&R@ttz@cEv%D0wDe zbQP4Wsuc;~fZK)9|1X$&2HIhvOVN;TMS6rslzB2U>9eaO`ef=4Mzhc>+erP8~ zxey`f^TcpfJ+9|qdgeH=zM=Z^}!EBrk`_3;eIH{{*CAmP8l4l9~ z;pks4O2{3?TVrzLo^9nx!V8i3ReAD{T}reuUc~l|#-Bt9! zTDb0)>5`cap44rB<=67)h8a;PG!y0I%p2FWDg3&kW`EVjtU=}{WPlEk?cH|lMfN5dXV>kj{m+DMz^P!Eiy$^c|0w>>Y@3JV&y*Rv9-r5 z9fI;tEfM3bxt13ycCjD4o*9F@2_N_IcYz#(Lwg}j|6qdDBOr%S73j^zJ*7~uwg z?yn@L3CGL-)4QOI^AvMwCzbQnoTk$MsE+SKJhziKRn20ZuyytdHtP9pWc%A4?x5YQ zRnIU^&W@n+>t(PVZkqM=m7YN4Qfx)!qgJjdmhC617_6#f;MRr)nNmTfcERwu~a*`lN+o+x# zJCMe-#p}~z(4ar+(p%FMBak;@fpz)QuNP1$ElW>ZhuTV4iR-W9mrj!Mk-6Dh(qs{B zxVH1dBkxme_oyNmiF#!_k`L@F9P?j!s7!4pR1574X9dLG!qU6Q4(Y`qMi13jAQ58# z!shytSDzCcC-vO@HTby&79x>4V|F>hIt>0TfwP;n<~1}alYax5$;7-){^U?0jHi-- zww-_Z;DG*!hXLa7H>jm-;_Dz$=H@v^LG5jPKFXDA1H8kdYlc=w_VAI-w5wL8mIUPa zd$7Q0Zf5xTDufZV8c3}n?gM2AUFp&SMpTG-X`Qk>TIRX#qau|s06RStwmt5oD+{k# zzdFXF+%i?aJTlK%ygf1r?UpX|C|G#xHS-WlPGHF;082kvQDU?ZF-7ZOJ@)($ChHlN z$;jQ?X)#mbyoJ|1Bnfh{+@J9vd%mi2CtB=3Xc*#Ok(+^OWy4lTB*Cm@>@{f*a3hXQ z)oxc=>-D-ab}Z9BhFcA(O1JlXBgg05CEQJxD|gAQd!d1aHQ1U>yGTsntzmpuSm3mJ)z<5X`B0tF# zA~$R0jbp)?35d(YCnPecJ=+O49gIPo81)z6~kT;_db02S-?>4E6)k% zcwsXgiD4+9i9S924_gd=*dO^BGjZAaNp@&+>qAdd#_1-xeHb3wb59`w^DC@cpBe1< zmM@E+8OUBdVGJHvjo-TjH7v9h+|BG^D;f{dPL&ZGhqOvG{|0^ah%eQ9hNYs}#et2h zjO|z5yt5|h_1(Ins3f>`85AH5-9V6|hSX`tZIXW$z@GHKj7i}P^B;di@9g5@{_Exo z;ZR5HHQHlCFCxQ}+h(HNukd0^a$I=MS?bgy$5$AJmpKfY4W4Rh5nY}1rLh^<1SL~; zz0k>tmHZ$_sm7^;kcru80dm{vSqb_%o?{*}#PY-lr@I0Z?nD6u(qaTe)5{wQiGE`E zfnk}^?*tnoc%U1V!KfkMR%VK#3p=C&ADb5^XL6mU37u8h)0`7^DNTz8qRe_?`>|1_ zYamjRcIAmwVd7W-&Ejy9zw4eeN;4y)wStm&vgMPX+hcZP><-t%&5RSGChMmX;7nMw zSju0D61J)FXB7b(#6_^Y%Dk8=EBa-*fbTj`@`nY*Qp|mJX8dD$btjv7uP)#$oV>njDsU~VxGwZuT__;v@z+UKn@i`Ll&eIwpwQB1ogLVr z2S|{F$!HfGUKetlB_U-!kAP;+M*v~~dzW*dhjl-sI$ z&In*_Nq_Y4)8f&g#W?wj1K4iJJp`-DjFCxp!qF{9Y{Vy6;>RCmFPXL7&YnhD_ zklY3XuXt3!1?v5a{HxH~|c&aK-1Jk>(#LX8o^RO%ell9=O=c&+o-1s&HFx z-|+lDT%BcC9l)}taW=4VclY4#?ry=I;1DdhZQPyUZX0(A?iSoFIKkb0GUu$j?yULN zKcK3+>g{@;8v4W?v;Uc#@%DVCHTv;v(v4*0zV#kE9Qc@S=FTh|B@~U6*&o<>{R-ln z=RiNazlhxJ@Av)Ll>IlD-9e8N+4qMRH8VgHDjw%7?06_vCq-z|l{q73=fx@kZ|QO0 zDAYG=D**<^#F_DIj>pZ*xOpoO27vE->@F|*o3u$YsAo*WKX5;#V_OAB6iU%(Hea8_Ko$59wcX9jEGw1W(ujTe*c#98Ll~V{dbT;6q-jYs)q)J zJ)j{r=_`x?@snCUvBKJ!PU(rbVI3hxFp6mzCc0iWVkkM_ctD?=lw4xpuK+Iv%|$@! zsicM)qUu^gB7vI*XS~l=?FaYTOjHar2g&T}?3cP+wHe>p9y=P-Htg%_!N&5cK><<6CAfQ~x7PgGC{`8nKk z*0in$tx&X4%J2ML`%w`(q6>knd=e(!&FkRMiaf4TGc2Bqk;CTiI~xbVZR(GF3|T4# zKaHr?Cy|fX6A>K$ehIIzamgSeH=x49%!y)?qr}Hlf9mV$Di7|tr~mcrR_fwlEAT;5 z7XPn>32y9|axq&;uI;H#a#~;}4HKNN z_SzhYrgmkv6f-zqab>L>QYr1ZjKFTEO8gxcSQDgFz@4|I!BY4fIF$-Q`VZft+~ z)?GrpX~R=`o@Ph_qN0dVAI>~e;Lhf2DqE(hyKJi~88-Z!I{vI7_Vn@i=Yr((H0dqz zE0|6x7*=ppN?TF>Q-{B65!V4Tm%A@Y+mT1h^-l(T;2WHDS zQyaEsM+5@L_-OeH{N9i^lDh*vtJu1J9gOBG^GY$>7+m`wHWF z&^U0`(?6Tr1fQ4cbFZY(A*%|0@3d_Gqq`obA(goPTzfGxTNLh`E_#17`|g=*VmqD5 zqe{JBguJZo{;3rDeQ$F4X3UZaOK9Ol9MdytlViFS;n$!hE%*cBi|XS?ud#T*!$?dl zADvz^%Jf?%1SL;6!<05OFITDHhv4ft_nX7W){3^}j#?rUHV%q_&Io(OuJ@X@^&Y0r zvGdK=SU?ui?O>JGHbN`$ed%{WY=@pT)=w{E>(2F;rQdP(Pcg8xmYFL4IoM^wA z-CZMe-y$Dhq)M8OsR)eoZd@c`zv%PKEC}L7&C_!P&oE_s24Y|N)PP!7G%J-d}>^UZjjZ`;Mi?Kp5nzD)GN8M(fFNP zv9T{cW1ddr;`Bi@HPQ|4PID1*N>Eb8_3@L%z`V{c7wTO7J+F=$!<1N$RaA3nCDV^( zwYhaAdpf@U!?<}LCbtN!n}M*_<7%@xQsHyH-r*&3$2X#-NkRsm=u4S`)-6vUtHkHu zf7Z{U9qCK>>qT`poMt0xdG~$u>vlhD`KLNsvMaqQ0 z%!zDc%d7~~#i6}}=Y@SsztH2=#Z(i*m7*^2&5(gEW7c4**@zV|W6FnC)$x3DZBReV zERSN)n!kvD6tkl3E9Fh0>5gYr*NG(Hxx3?(cR?RPu)$iwlGKMHi!MN9xF=eF*F&4S zeR;aIHcfbDdxRShZyx(uu~bW!@x5l{Xtp4opB`;;)-pxTb_bDhz&z*cpel{b(ZHi% zMvrt(7W?*HMYOAN#gQI=CSHFZt^pQkEbHMt~Okx5sUGGnQxpv+VYZ;XEp zT^YI+?iUO(kh;KGlA?_Uo%`gEM?$qjW?LsQo31k46GQfEuMU4) zAVkK_G&;;VoKQSP%5?rP-&qM!cynPj=XxtVI#f-TYIk)TIy2@}?b`*JCV0$+?kZns z8<++Z|4|v&oN8*V>Ayt8tiQUT)AVaVTD4P1>|~d-5V21wOAl!!&*7}nZ^KIOz^`n? z6dLIg{;r2>?j=;;gvP=1z0L7Wy!OPgu6!Wm7hnD?!4fs@?sd5uotIxVn4Nke5I*zu zDL$j`Sm5@{9|=Nme0EP`hX$eDE^9jy(mut8%7d*Al8mn#bwyk zHrLh@(~W}9)EfALnyThQKhuQ&6~RA;sY#YUbJD=39baCx%*5Y_|NPg_Mj%MKHP(9Q zV{vo}_YBChN5brA^H2E%3MKpgy-*Uv*HPiB&wz=Hb|Ac-56Fr@`;}^26EGmyUV*Fb zVfTP@ZJ!`zs0hy4izuB83w0M#aM2;s*| zivN98MsD~_mx^f&s3rROR&v_y!%Nx0?IkXrZfTw7J~gK3lrP+DM_zvQ5x!u(s3*4yVWgMi6*>=B7883` z{dVBt?Ij*-8S}Z3kvZ~G)$UEWwy|9Hgm5_Q({a1S+u!Ss0Dr{NdFyFb z8(Wz$_O*prqO?nDadug7$NtOSD&>;dX8Sqs(>#71@fJ-tsY~lOgQ8NFDk7!v>B8_e zSRM2_bcM!aG$r$qgMm{bpqmG@*R>wF0bXEsufqR!PQ>3A3d2NJoNS6(# z^U{x@L`Ow26ud^l=RAPb9xVDWoX!q+{teNod#{SdeT3QhtVZK7?AdDd6fisIR!^MCzJHijI=`>nLUt6Los%ts_wL zPTP$p^qKtBc;r7Q-Vlis3@*e~iF%*`FKw5Cqokua)4N?U6KPqRn)6irU#<7OK3Z8) zcEQR)Y@9kiv!yW3u>DlZnv%Ie!z6*q83@5A(r6*p))p82QfXL()GmTaNBtdN;fiaK zWeiq{t(Dh;sov%2vY9PZxfY!hrO&HwjQ3KL@kRn|o@QA~XQSs4_ zN6O*M-xfRc68^fAuN@3q6@3GjzME%{hXyNG1g-%AchNK&V97FO9nG;+xfIp;qPXk= zK3lCx)=DtHomGPBEo-*+Jha9sb~OCv*RE@Gn&}B^z(5jh=tj;$q^qjzblmE5Hr>2m zc;24iM@O%#%k{vYa0>A{PPJ5*@ygJ+dMe ziOZ!QJiGVM&XlIbXgM%HKuCvD$AUW6pZ^j{s!D&@R_iWN5VnT5>;ayiwwexA5VQ!-=uYAN4il>v=uW!nN-pzY)7mYT+ z*!pE&ER`>MamU$3kHv3H>od)vC;5U-)%Nt$e0KG*dtQANUIy-0R6G1VqcNBsB%Uv@ zo$RjIrI%xe!g)+QNF~&}<(u(DHMCzch3kFt;`AT7x3S|2UhB>U5&3Xjjy%hZZ?Bp@ z(|r(rEKz(;Cc}V|3zRc}yJjR~145DQ7l=W1`%K*n+APC`86+9|H4{?D-oy6iQ_ce{ zpXfS+uV46^FdiSq>uR^W#>0NBQ#lS%Ty|q{|LO?%GD3w~TG}{u==^Wy?u$DY?`?N< z<>_Mx%GK0P*ZH5*qKmdXgMAveeUUXn-pvkL>dg*3zO(q-(sPVT*=fTt-Lb4X|5-Cd z24*a-Y^y|i#ywvAKYssA+NUaGj^o$RSlz0T_AOo>9x^vog7&2v*_Fwx+;MX#u2K&S z6C0^4xi~i$#tNy<7pso!Mz71XMXo#~rq*R2(HKny9m#-zcgK|K(W6V%h>BD*40;Yo zmJZnXSd8i|4eSAWgWgn0HNuA5kY?mg66X$j zucUEppY_S!H~|wHf~!;mHVukz=uR#mo`lP(!1(fqXBA~|hTN4py|2ghXCrdVBd9Es%fz=3t} z?1^}A9-^%s#zM@GG}AXAy)s9O`&h#IU&)0LW=*G^2T4qagEK@d?ZNE@fjrh+AgXUx zBuhn|KtGxY6W~@N8|?PmDW%E^UW+f>wggW;!C>l;)XJ?86z0=`r6*~W8G(LCx^0`r z8tPV5-Hak%anbTeEp~dOQ%Z)#+nVs*R6~DLLr>Nlg-@@|(!W^r>;T_H8>XKdUbgaCa>=?%E^23Yp zdNZ_;R2~g|rn4n?o#Q}>KP%#kTDbg+@_2XBN5AOukLvoR!`a=vbL^bm^NVi;Z3%6= zamJ8rO|}mA!E3Qkk%V=(cfv8aqp;*^%0HFY7AxHRuAhhwMx*mWW^!mHY4LMoTCez2 z4O>;uDlME>zBC2zuaxrRcdRU@&>`8PiANi?;^!y*<-X>d)35y}pvRz}Z(K@Rmm|dNdqax-$jmW(U0Q_? zy+v23|J{9y%wU)IGRXE-j@m2IfWhrR6M2`XglvxigwWd#fc1><3w$l?&T+@B4^` z(eRD!Wf<`l0v@y~0A|ep$89I{SEnr0hnR1D(l0n`Q4MyOfSF7C5w>0tEeK70)1N)QlaXOptw&ZYN zGsxD_u~hp)xwy2{oAkjUAe@@4=@%LWg(~pW{hhGO`^i|O>~bP4DNywH@Z1y&DC|J5 z8IoPdd{Qm7w)q%wkF;4rJe>4p6sGM-J~_eb)V^ts(SWL-NJ2H|1-4>BPg7&ETu(cO z4uQBQfyBP3FOM%MAH;1L3xn)x_T9!2yc;DD2x@Y7gJG5Gg!BSf==srYB0(4#Aj(o+mK%w)S7s)Hra?#{I?Q_xM{K3kKW5K zCxh}bLR|c>EHJJ!1}+*%%ZO1hmn3B_3yc%M=ASlVU0^AKlxz5cb zjO<-8XINDp7#)rLnUyTafTZv8)9k@Gu4q>dG-P775?cw9=QA9}ovGYW^Ok#4w+U?T z)_i73)UEm8?=zkbrH{5d)DfbkuOKwIt*}Z9P}98lLq$t!vjsDremwlH!T}Pz8@cqd7KDeHYGi5;2-j-Q=bc(3N+G@mG z991mlkWvAlhI)mU6yctswAd3^poP?>JHNa6 zNv^o$ahA1yGx8RM8R7Gad?s)M>8|fKx@8bSIze=M&)Vm!t*RFB<6=18aTob9%V)T@ z(h_^v!I$LRtnRk9QrTEY7~zR(n+Z~^nU(HHfjPxndwae690P6bbmNyRPuIo$&q+tU znKV-qgi(aE;rQQ@XWMEPZbTHLr-kT78qf|c*6Srd!37o@JO~c$`vz>uzmaED4d*$d zOroVN4$Vcxbr#Vi*TN-cWyw@Yf>J7cOq@9-D6fNigo%EJ{7#?oB?>!-!R4HHLEaJR zE8;)|7vr3M%ws4LF!ZtIq;wCYSaWsE+NY6aUsGn{kBH(LLQg(Jzy-#KQWj#q{8Y`J zy9hh&OpQ~*0u;2n|h7bQQon|9Z$X^PC-;BNy{D`m1aA#ER+SAB5@Q*-aww&!WGn@Z9FWCFO$H3 z4g*Ft5Ez&=4}os8(n+~ui`CW^=>Q3BGK!p7RoYMJ!~^pkMCbgrm`YZdzv3=yqW~B5 zMq%A30vlkzumbI}?MTVK3c79{@J9w@^(0rqNVzJp2Z4ham+X>qa2{3YxjM>^N(9Fs z+3>I75X=;^M^t@P?5=s5_6L%{M?Y%9Su%M%=!IU`@_CkIqi#I!`*}n>F6v0a(5X1$E z7VZWKt>9UkQl3(jiZ0b=R#EK8nr;4KP7m=u37{7o&nLA-dN#S+$JA4RnC7A{3p;_< zhqBuO)lT7Gv>4_J)+H1W;}cYr`Bu0Qb_{sF93aFBqco?GAW+V*M$k+4*LJP59Gx6& zsb;k`m4LBdxorUsvef5l2P0Bf7w|{M*r%(9p@iul#c-0Rn2cLc^D-Rz4FN%WQy5hE zVSD|5&6>XY-Es%*H0{J>V?t`+og+g{z$lVRyhjonDmk{%YqxIq4#}csGx@s_DhtR~ zEF&_c>7_xy$t};yMgL^zz!=sHNqq3^Lx#~u2b6a}+YX;M>P{F4eGk8-6QW#7O-xVj z<+EUo8Mp%z>qgmDB*c~Z%u-boKg`KC4H*poqMC!#*hp6u4@;w*#8>2qpsKjU5bdzh zu;D}wYLJI_mB5^6u>?coaZ?l^kUJo-N^ust?fb{DTS&a3ylS`>jbg~3!<52!LMkaG zVDz=yOk0;;YxgEX@%0Fcx>WwZVs5~4FisOP%~$$*B3>+V{Xo@$#T7YflfNEf*k8;1WpmX237f_AwJ zAdOk%Y|ALD(*>2hRMQ*ZN;04jAA8>Q`||g2byF59jK5NKxrSY0COe#Iq68qj>Zq6l z9t6qRyuj|52Lzc0<1;Fc%s{JG_`gI03+7j0xmLL|q(MhviGWHfN=bOCac*KgafWgi zxkV9<491SsZ+L~Evx4oMu@Z}?ARQhobWK?7v+JWsRU?^K_6DWAuNv5HRQL>CSty1n zSB)VOhqzG@yQTm^eCXFYHE%iw>?=DDN}Us#X?AbXX&udTP6KnYAM_I1 zyiEk7&;zDF=cZ!B=8>0}-Q=28Z~S2{OU0t_2IXanxFQ)++vlCw3cU}dkk0?YQR)!J z_ae;l-t2U^ZC_2Y1r_uq;(Zhe%nIKLLVh8pVLy{r4~0%+nQ2#(x|F@vzn&m;53RRD|MUfzwSC9@u{cuOLgkcC^AJ(mh@Meeae0kNqkR8yO~fUJRCd${oKo zpITkz{Q3oB;dYRhDTpyVqmvX6^K5BTlGb3!xojLXXhtzpL_+2;P36{4;R(N};in>X zg^Pt1ACYc4qj69BWBQ%7lMwM(F3hRTI=E#G&^#u}@{MLzEh71&V12z(5-op$Q=G+F zLOF)cO_U!m^i+9U!n?dFeZ!Niofru~0qv#iz(saEX9%d3BVQq(c~T4?^-xEDa{dy8 zpyP%^UquYV1B2%x5>y&N#UE8bNjUe3=zV${PauwJ~ zyDE>65+h7{(hi-J4rnVRRz;rc*Wm+9?HK~T4w+3n*-R9*|03%VM+xpDO}@y8?JcHj zi?wZ3{o`j2#2^0&o^I_!Mw=ylND4jDVV`Q=B-H0za&q2=ECGHb|Pa>}QxL$O-y`Vrc1@D*Q zsIu&bXhZC;A^wRGJ3|~MTA5Cka~)sBE5UESZS+eMwr88nSQ((fQJHr{vuL#)@I}nUhK*ZwR(82t}*=D5p5xF95wj)XtidT*y?+Vsl1&&f%~v` ztlNoeYf7(kn6%X$3O9}a_5(q}$l*9`59nMEqVwwMDCG`Ra-2_7)WAnO~F&>V?D8s5xT^>tLsujD|} zW8e)ZQV2Yk7tph-DCb{+`juK!JhpFhtfn-a9zbrHmYMd$5gdxlrhV}l( zRa!VDyDi-TiHw&sUpR!)*bKV#W)>$statmwS9U-}enq9rasEC;>#c(c+k7y%9EY9V z4zVHogOp2Liq$$o$*fAaAuNUvOr{Th(~Rv4zmSYJ#{PmFLq8fCs5y)>A=?uYaP#N- z#SIRj6hE$=;EIxxcTN2DcLJjmyt-j(3&_zszMhRp(r?JCX=$;rz7q=a03A>#UC409 zsT}7EohR}F$NZs>Zn1NRGoBV7!v+l-$jsVc8zlCN90CqX{3T&WAv}L!7Ltc}<_U8n zJUl%5Tj#;y`osSh(2iv=;NCE`d0ck>6Uet?V$}TLJt!kxKM0i?g9zu~my5ss`XU1n z;a|zNwF5@#HAd=$P<6ajD02T8#|~awh@iY-)RNxzvKGXprQw*&v{5xg!|26J?QHoY zyuDx1XY-!eoQN)ik@{0BDe0>V_s3YW_J$Sah{C?M?RW3*pY0CxFf=UcB2Fs zyrYSy0e+k$ax*}}GJZf_M^=6FG#`M>{@DA?OIg^9(|MaE0u>A&sc7wENbaAJ6+LI`K?yH=|QJkmBUD|ZO3O=o14bIaByfnWj>-S!J9 zfX;Tui^*;|jC3KG?%2>B+VjIzq3#Cvz*EsWHSvEC6ov+|AWHlSZ#aC_WSto001mwo zO_v51Ob5h!fAS+)n?6+!Op8MR6n-ek(ksEKcVWpTG-#V@g`G4tHu(y~1%*J{NXEw^ zVQ0<+?Po+4y#mN#Lj#Se%>b@Jo?CL5mVGP-C={3#;EGWXwN!OweZHIktQ1YkWH@OU??nxm&$FyT9@08=&# zE?EM9T8s<);jDLD7=NwVqvPOh9zFn~NPG4Lt9tBLq;(R?afTzZlCD%!>?S~za!5eT z$SZ?2JRNvx&A#be1D)#iI6rEc(&P?L_P9!>ZzaYoTNKHGWqtGu1_Y9GiRMCMs)p28 zg+(=7p`Z%u%Hu71DFCzxqs#_^h##w_+eTr#37YxC;!_@|syL%1gWNNnMEA11*cJcZ z!ykj;)`HkM!d0kX-vbvk7Nt5m+AqJBS?x85VJ(>-7-xJCbeh>!) zSaJ`S#txwdbRp}z1{!~C@t8(uL{+c$o@fgfdw5TQM7Efgu1OzDRGaS%y>;PkHhl>= z)ZtBMj>501R1?njbQ5XLehn*iADGJKLWL=rcOEW(imqe^n}2^Nm>(|PxmZ=%xycHN z5YLhPE^#^itp@oig>J_A8E-%Z;TS>~O*Q`+=4&3Dzp!huCswuSTKjH|l? z>$lqU=CqHy{a?s=#w=W}gpN-gZjncIbRpI@l_O|~IMqqEsb8Tepg2q^9FHm1(q{dm z-F5kes2D`Re0;5Xl0%amh zf=A@KO|ozb1L>VbhW(Yp$@0*cRf#o!LKw)wZ}Jt1tI7kin@&5jouK$N0L4v@9Ow{; zC%FU_QsV~}l=mNQ%$QPvHU&!m*o)!A17arT^-bsr7(|JtE|^CM${H}KT75NY5Yo(0 znm=T4&s^4s^fG7AEi71FgI3s)&nK%agh~n@;CxQth)I+ zxRD%`>3YDLI{Wv^Sdl#V4eh7gsbN3^xXD>_g%iM!2CabnPiK8@m>pFXJEUtLC95d3 zefpxOlcfl>FpgE;?1`(Y$~vovC1>8ZSU^h-tifS-bw>g&vR!$E7U}9DP>Xfszmyj- zxA*UeE4Tsh#RZ6ut}>o+#SafG}EF^OFqdVLC>UCgPsY0mc^IF`ZNga$HhZ}~ZFrTss7y#}g<$kqV3^(Ues+_v*nI)gHe!n4?3+*7$e@j+e-AGEapFBp zd7lq=!BdZA(LE_60B>thlK-~J5X%yd>XU>Zs(JV8vv*pBfRn(85yt%)hdVD?)Idt7UzMAzxf&fKIKf{zI?Adv$#DNgQ&?)dR z66Vbk<8qzJ^`CEM>iJ|4=x1$TG_kQ4=nVP!O-+sZTvc3Msb`6_7?BH96EhlUJ+@s| ztd8uhWFeF{p(`kN$l5d+gYlpWiBWAU(Y=47n6C7cL~6cfO|=E)U}$ozv~dZeIXGX| z+fw&uCeM63%|EdTr?Y12t4MZ+{{!S!wv%*|v!@_)AZMfxsQ{3&qu*;Q&z4prGP4Kd zz*T~8&uHpYE1>~O_z7u{M$MK%S``V1Y(R%QU)!z)$*a-yFc>3tC>ox<6}fkOc`;?O z6?KUp<8W(B__LnJ82mCzCPx#jM6WCyzcHJ9hp1p|RC2MR9(3p{g1X7}XhX}J(G6b( z(1>`_a}~?`35w0PO)3Ul5I689$)^3QBR*43TJL5~Xr9~?CX?OV`}Nj=W5n#%F)1qg z)Lrh}`N21Ug^?@#6c|kl(^MCBS4X-W%|BGhHneq@#l#k9y7~x0jiZ!WnzzLgWN==s zi}T$LIe`JAvivc}z2!B(S+~Yos$5N2toBe*UFR>7J73R-$Kz+#wPC6EzqHiW zfyr1~5ux-EaIp=%8?qdlq`2x>^bTgEZDvgy*^l1RbP*eOqlJ97LCWuh4yUE}E1p+G z4SRs%A&UX{&o0D?3ptIyW^ZpP7sNGj&Hf;Yonjlr+;gF|Ry7Z6Xqrj=Eswy&{OAY3 zr+#AVEzD0(*dMh54{+S~1`(!wI2O#N)7{Fp{2`&3hXtdjZZ@+dfX|Lf%KG7&HR~MOJE>YDE_Icz=W?w38sf2YC|5woIr3HOggEBCX z+1E?}4oXxmlg0LQoT6+E5bj=Y7K_n|ErQw|f2dryK_9AMyVx-x+vp4p{@VY$wA)M^ zAT_VIJ!I|?8K^e-D>0!3*JC@J9_niM`?mp`&RaRg3&lHe=iQ9lcnGzQ8Y`8j59?DJ zqV316OMJvP4PF<7)VE`$;}0b+o`u^LEFdw}Livb<$XMUWmc7~8j`+UAT9g2kB_!oX z$W|U*ZwhXFpR0EEKRP19gTa=|jS_OVIJ7l>9uq@IoD7x)9Ju|E0-T@`8y}|2d1dhg zIeh45?U1PL2wb+zv_&;}4@=bl7-o%26m*5~oM)*buH?AN>dpFNp zT_Kuyjoq6W(S;nFvzpi60aC+wXgp#d%n#=D1>mM5VMrWD^<7H2lvPpWNecVZ{Btzv zqLpEva#Pq>iNztvaZu&gQ-m0LLhn%1XtCR0kz}z`4w^7*ntNJFi_NDK)|QGRC~HS! zc)nf#Rwl(w(YUF1X&sQF5Du`>33QBpa2Hi3RWu;OMBeS69LjJh2Gs`A1(|dpwlH%* z{i%P-R`!mOCm$JdW{?;9LCgE%9jwsnpKnP&(ExP3L(WDWEgW$XKm2LT2iJw zzc@F!cG?Gagl;E`ChVXkqUFWx&dvMMT&A7JuJ=iJQ=9Kyq_MH7f?gVgbbkG-i(=bv zj2AFqhW}0Vy#Q;^Pb%%EGd{}=!qa4_gjAb?lqi!9SXHpcC;b@Lue{O+eo~HW3d6Uq?A1ASM5f(qf-{aEzs^|N!%{OJ+DDTY{^_m3m00jNmuA@xss682BR zj&A(^K})g*A^7}P@u%fn@s7xnQeW}&?XTqU-{wO_2_uTb(J%?O;eW|HpR8nW?fOqE zWv+NWhf89Qd=Rc>*Lz31iHk zv-322R9**-p;q-1*J2qp8azomw?rtAFUtrD)U3z|8S6l8_m?bjMBABL7X@~Qrdt>9 zzK6BUo={40CQs1(gy{CeFc3!;c(4ed0`a<3N*^jPZpBzX9+=rGS5;+c3~m*@ft#Lv zVapjMSWlYWK7_QLCKLZP*0#vX`jpmm=6Vzz{(bB4ox$j1v!uHsYA7iLJ zO?IQib(CL3kQMtV)2y8)uDmo8lrT?u3=xj$$=)7>sYH9Qu(l-5t*M|&3KU7hK;d%W z8=x44Veck`Y-~!bf9GsY;AHicF~K^gq?BSc)X-3gg+!8&Uyx9u;camT1`(KfpJJ>I69>;AsLYcvjF);sX*gd`R46Fy_G*XM{XI357+N0 z)zW7Q_xD;b$4r_Mr7{{10=VXQaEs0TRr$76O{wSN4@=ZL9AgEQl)?7-k807u-&ViA zalnO;Q&i!nE@B(2|6BNyi9-w3UI!*Mb^a>+_59daYV}p%rQV^}yHmRX?^dJD#ARM` zDaj5=T>i1_g}vuM%=~IzI}hGHpig)KrO!DH;+E)#l;=T(UC+ZNeNgYUppSzs-Nyz3 z#wP3kumD_HGoqn)^`}WQ6mt97PqVB~5f7V*=)BKUF9d&Ijj!>MU{A#u{PO~XONn%}#?3OXPqk@$xR z-p(x$qISKqy#Bl&pHFVR^cnI?_V$=7-8Z?-TEBh@w#k(`O@~+e;mP<{WnzdJmT}P6 zhVN9h^O?;h@DIHU6!&eJzs>2MEC=F9(ly#65=+&91*EJy?G5j)Z>g2>8;y0>pYqE% z$H&y3vSmWaj%u<`lLiKmliK8z7(AtVb=-p&FiBazZ9Bme3V~m3)&{RuMf|lvHQzv9 zO9YBJIVc7Nml>=KXoM*X>-vYf#uul+n7PQ8kv;eZd_8mn6O6rREwWl>hm!D(A%?x~ z$e&mN4^8@6cUllOS1S(j$^x_b0h=66sc!QFqDtr0K1m5ACTxc+17+9>` zL=I?wTpC;rahhfxluQN};N10t+L;?dQKWDko}g?LWG-_v8LUUe`O9C9NaTHZQ_-1H zwaLoXtZ?JYkLBhu_Thcd94_!Kf{qdhs)M|7vfac|g5}%b3AARW0eB*b-e$-5v9+hccJ8h7^z{UTv9m9H?4m>#Gt^7vz}&%I`0^I5X5}wg=E=C! zo6kO+)|2ux)%O1+^Hr>zCF#xqOcIt)K zql>>dSJ0AM`M$EKqt5w@|75$rDR*?>Z}4TPZCCT7{n7kSRaXbO>5Csdl$X9h(l%Tt z|FJRbd%oJ(ORK>oL&v+t!NVQ@ty_TBY5jgm8Zd;?=J$>;b#_OWP2lm|c^Bw%ev#9{ z&}_hL@W~!!t%T1Sm*#O^VpdLaIVaG#zs;g%bpM2jrAf2pw_*L|ufR_-r5q2;W+*+EIGOz$fKK_vF=SpK z;#N?MOdF3=^PNcRH?K=MfkC85a0)xzkJj1WRJGJ~rs6STpl1wC_H!UE8C)>P+*ouP`!9T+} zRj+9FZ%qMW4FW;9$dsaIxOcB1z`iJU>zZ6sN~=$DUDB0z5#;rqea-eo1Nm2|OX^pn zVp}=~_-aeH*_uR6`Uu9^R8e?#DF$Ha)Z1~KtJ!B8XtYZ?wb0~Uuh!4h6<66WF}&&8 zv@X>z;+`UIyuV1-JOS&PuI>kuGwvdjwgp+}_O&Iw!^hvxye=M4e8og#m&lvh6yrXgulY>liC@?9|NQ$Gvu54^9?ImYNSDK)9<4CAJgws1pZYc zAlCS8L(~*!H%&;tI9%uZYH2X*1j~LScV~}#UQ@YN{MURpOVolRxQj7&U(a2>acLw~ z4+jm))S`#voTs#a4{GiUa=OY@N+E8%HX_izT&WjIv95Any7T$CZ^?pnKL=>pS3Q_X zk(PMVh4kZfho#3jW4KCexC)a+tIYXWY zwWcOPZ~aqgoG3{$43I1g1$u;}sHCWl`uiAR?5a$!6XiwjPsXrHC>et@J6~`orvgQTbd)UVYd^vO6n2(le$af@p#c4{KOoF)`9Lg_s2@SNj7Z5H0uGog+xDh1ft}uX^8Mhm&?r>c>_g4hOtQu}KnIqdJvQbQw za!|qKP&^8R_+P^1)R9w@4{l3KD1@s;p#sH!XwZdw2P-a6Dq%=L5>S(>VTF+|Q-jp{ zl0pIUq2Q=&`^D#WiJOR`T%;Yz~>-DaAt@OI9Ag{3%!@- zc!1|i0}14k@B>#%AJ{gOz6C!g%Jpc8M9kqh+(LWr*w_=^4~71yKcPuFr*u>qch}Qk zuKQ-=g#M=RF7c0|xgcE><%!5S)?>Ze8BO|GFk@sDr3RDMoz7k3&uJ1RI2Iu(jOpt} zqv@LSq3hM}d-6kUFG5SEJ0r>#R~z+iK<>GZVFP?-bQ8v1=0qjS0E&pOm1D(8YD*8p z5&@}SNi+pw{|yqz`J~#{Abbe(xS010o;kzM-sNodi7W>Xyd+H9X&V{ciu)!ROFG!Cagw5Ku3gTn0__TzIiVHHE)tVMG z4X@!=knzs_!*90)AiZAAjaywUOv6L$k-RPA8ZJ8uJ*=heXghK)CR!01f|TI(RDEs$ z74F?}-@fgrw6an+f<|_EJ5Z?*N?NYdruQkBNn5)920bA0Yhs}&P7L%~c+aG$SoY%lN&_t7s!8IFQ2 z>MRsYglvEAmXv$uXcGBE!EB;Z5rovgH>WT)m~LBwI!zfb*brjb8suMoz|ij_Qfd>l zeM{}4`BIi6w)hnY(`SLQ;ouAnS1v%|i0sYw|3VkDcBPaJe@A)NJ-I(;W+9<`FD zf5p8EyFKD^s7{*$O}|LL;$kL+RPHV9bBsAn@$A5gqN&iQUy>^{utV~z*>cL;rXHt; zHe>xg>*pU8<#L3;HIiKrP-4`Zt%q=BmFAR2QkPA|P+!Ni3$W61Vf9o9P*g_i@{`M6 zoL6q%v`bd2g#v}x$8eB2 zH-x@@PKCW?R!&?ecq_cv`f+PeZ53LZ%=7jhPy5IT@}245M;giLf$wg2YB3xgV`RG#LQx_lu-D@B zVMOr@!G;3{?yj_8b*2wA9#)Ilf#7&LFDRhW{=msJ=}0$_P@uUgV3ORkfeY%{)%bMVg(EkP~KyOIJGm2axn9#v`+$;~~hw z!WQ#w2sL&%+o51T2m4|@a8zpT)+}bc1M<>ait58BQAX)AW~^MJ9eNdsh1FP!pU^PK15p0Y=J0Je0>67grn>Nv_y ziI(^&_=mSbCuP%8%7~?Ta;8S*)VrYI#JT{~H7*%R_HoNYZY#*XT*jooXC>Q#hDb*+ z2%XxaWu{IAbv#f*PP_8YG-E-oic4l?(#hP(%4(%r2rlNh-fr?^mV^<98^Mk>gu)_t zHS`^u7h1BTY>IwxARgB9eBV$7y3EI0wqtq9$?p{jQ^ni%$<|0EtQ9?H%_JN`ePcXg zoHn?63^b1SZJCs^AFzl#Q2wK7uSsiFLF3fBePi+ph1`{yms&iHb-aO!Q2DX3M(Ku} zDOMPYHed-6(YbN~Pb}yv<2*xX-b6SSY$fhn5y=@v33AeCHv6eM(W_+dwyB#VsmDSGHD8#W>`K}e@vxrN(yIb!!nf5u7>u=;1Qe&%T>fIh>KkL`4Q0N`q7_K zNa984r24hmGtI3nt}1pU?ufYiaVSByvK00A#d9=dfAQJR@;VY8Gg4V( zvC3YYUqC8q-5naQB?QdPj?nk_@BEv@NtdOozBVtLtkpM)o=a~jl(Zxb0JEXRrJboW0r3l|_vEe22^QZjK9n3dJTVck_3~W#7K~$gvdaWptjgRNKD2o z+4di11ttCc1seObCQ+;c(veTEuO&=OO4!=-v$DJ<{bB?Abi*T?*&aV-$;TW~Q^(Vs ziDSmmc;83H$ARj0Wu-ABOO{m~LyT$UU|q=vRoF#nY0 zOBC}#mgspXKaxu&A-``rt%s^b*Twe2tz&~5qbARDMv<`9u;mGoJ1?86ZKC;y>Hj)w z>Qmb#sgoTTrTn*tlD4YB3{*s)@=N{Ntb{A-&HPp{KNnS^F8Rd-QK$ai2Tt~LQ-Go|~Y zua}!8$DGs=g3mJa0_k_9H#sxeF4c|4!i@8- zm@Vty5?$6_;iMV=^`XXxd6+TG9Vv?oUz0S^!WSiSgQlj zZ{)HXZ+t<&KX?-FP%=zap6E0qPoP3~;uCznqe?;a;eKoSPvH7QP%Llo3j!li zzcW_HoBE6OCB}RFTuq<$Aif7U< zbFjTsD(3$lL%Z%Ig({VaDJda;{xIZnIRzi6rKdY?VOj;~BO__+>1@nI!7)1Z40&~` zr#wAB|F!<~tG4^p_V<50RhBE5+3KvFw|(qUO(aLA=j8NoobCefp_Lvx?aHM`+(2ZJ zR8meTsmiNJ1eVGdGyW2eQVYN=94M(nCF&*%YFmR=J84?X<*SE$zI+*{@p8<kIu`&B{EHIETS(F!+lXT7ER1ICAmU#BW0QAKEqV@)Igm6vf=H!PD|&`B7? z#&}(+9h6G@1~6hnHJD!$;Kpz8#J1Txt@K!#-sk{gnEAlEroz4U{NW`dze1BHc6@p& z>b~RiW-mvsb=#lx$eyzy@G|8I(BYUkW=%`Y`-CM8^Oz|m<+OnbMuqBtnZ|N^RA?JJ zh7PP0Sgk(3&QNbIBkH$b(j&~5SKmyX{A>X!fm%tOFO26B3Y|@j z0j8|~ix@v8$Xx#lA6E$wcPt^p#tNx+kOD}FZY7Z47kem$3XwvT8!pe>#f*mjN{n!{ za#@fhY6G57{r&vkfupmlsHz;oBU?ZL)hM=E5j6P%Ce?>2<&kFcI6Dt7%5)SH(KDu~ zEFUrz71F9NWHcJqP(r@|%D}zJL;!A)=0ivj800B%dbNdo&P36>1gP5HCi* zeHoO_IWbjtGXzSPkm1hiP=szWkHo`Z9y zKS%nH$DI8cy8}bAh(sO4z{Kc+ga^Sa=~nrE3QZ>kl)P3F@ka<%s@6p=^Okw+p2`4B zxHAz)9c>pWb9LGUb)OW&8D}FAWfBC!PHtQ*!kUpx5qgEjkk!T(mBsA7CLNfny6=Y0 zTNJ!AiNmULq|@~*g~`;7A-PwmVtzHr<$_sp{TVhk3j$HsRK#(yJ99{z@1;CP3GR7K z7KHX=mL_?@gf(@$U0idVTg${xs^tEkkCTASY~uE( zeGyrg6Fxn@8Y(lZ16%$_sv(j%c+7HzHhgS=XG-rDidq!}6&?HVuo%Si0RK@+E*%Ek)Iq7ooXW&3=4{|9#=pimw+DrL5Zg5=7IPSY_1jge-WppMPH_R{B@HG_YedVb z?Ut7{Wr#)vXEbbgPFw_YBw?VQ@Ii@6X>5%cjQz?`JK$x{_4e{P#TmX=!PL%L&G?DU zbsLjD;f)*|V+UF0I2V#2i|{d)k@=H0Z5l`7jKacrcHj&K&?3u{A2EhmsT}_V!fz83 z7ChCELHj9k27oX|nV1frv2$fUapAcf;~7b!L0Ai8!k)Gd!NNknus@v0dD!rY^Dc_Q z$JK=n6v=;%rR(piMyXUXxfwyjXK99(;C2lL_3is5x4e{&&Y?=X`21mh6rRFOB)(p`;$DVivg}wS+CJWCufZ zvW51D-gFDcT?9QBT^xFGnj2Z`kCx$zFyyf?IcJ@2jShZ*E-xsg$@1{JUJeqQlp9+a zryt?$f*9t+CUtZ;EdE?GcKMgy9x5q;^gk!<-^ld_Lx~CP(kmE)EP@@G+WF4U-zu96 zhED`XX_xGlC`B&_yO?JWM+{v;sY0930oqj-tb=aGGjXs}gl}578cn;IjtA(*_)UqR z6wn@R8?(H*s*4kc4Dc{$G#lKRuneb>t=tPnNvIQPpfG$*Ta8&#<}(+qQ;oS?4P*K9WRzreBhWVNhf>~}hWAv)1U1;Zwsl{a$4!xg^;X!C=Qn8{UMYAFNDJrf=%C)LyiwjIBR60rs(O8q7)0~kL z-~7)F^1mOo*Nb=|FtES-pXcL&QD{8M1>|aEM|aklGM4Y#6d|nX$G38@@bwx@(YA%r zXE_YBjaZdLgSV{Kj zFiB%4?_^aJ(*v#l;|~S^?Rs zD{1&6dJXojk3f=ICm!+X(8Mee^UjzO^$e3ECo+WmV4`fn4zUpO9sY&*i~Dw54A(Nkku3F&`|&A9d} z0&#bhQb>??D*@+(TNU18RRyYiCG(OkxcH6RuNC`bo=cf2mU&Q+HF7Lg^{2&_p!IW8 zty9RoPPgvak#pgUHc1-r&5-4en1DSs^C{Aw6UCmP0gYEnj1N0oZa+G?QVfu>mgH0c zXjsyfVc?CShEmmT)E16KmQKbio1FGTk)_>)<%&;7mTccY$lZ)xMUV=((70I7Nmx)u zjJN7P3hQX*m%V`VR$?p@xMLnS>e~iCi74UQwpY*^9F^8K1Te^P*eM7;F;KXlrE;plEHD+n?Y z*MJwgc)DY-2bBYapyxR`%O&Y4X`Q-Fc-;>h za&^Oy5AxKc;@K)}Qye(Wex)ZVX`H4Fwx!3Nh8HIb=R9=tAS7%BfjC(Hm~-Lvm#7Dn zeavj0DXLqG7+JxrsG=(B)Ya2mVia7%jBHl*P@C10=czxcz=1$lxkf+Qfq2F@2^vR( zmT79S9?0(n-P(+{Jyst1I*P>g;#Z+H8Pjb<@k+w<-yCK2`4ZS^OpBMocx=kpsUplU z*^`o1V^S(fk9gt+Y+w}!3W{MZ)Ck?vrX7~H1(T~S4$n&T&8Qw)pHkOkOI+}Pisrq} zgDXp{ov4J7^5UhgKWqxJ%x?f1tfW>n|n#0@lbaYU07mx;^aflNQR6+m;1c2k1ctdD{IDnsf*Tz0`{wPy#zwsW+QN8wB%^50X%3X&bJ7NqlKzC=O>tCw8sS4r zFozvwd(Jx%mTc<8^K9VEdX==y9OiMaUC}-D&c)sNS@J|0tKm{}-M+WvGE7Szb z#m1lIups1%%EhToG|hD`5b-jK+QRr9XeF38yF$r;vvjmZwE4mb$ELuPun05(^GkHI zrnv>ppi;e35xq8_Hm?Q>DTne!L)=ks*@LJmqM{8{ z6+>T1`!z`#W>-h=YDio_v*?F43Eqz6i!eqFxxxs}T#+#?LO9uP+%Qdl`;9l&K2EhF z(~w9lsEti5l~~BR;2#aX$ihm;ne(lDX`UaFTgd zGAUy;QCj!R^i52(VzE^hoO44ijsjm~ChSq|{})u_{()+|t39`+MxDY$ZHbdlK^zHT zU}IxMUx>d4c^KVhF*VNxSV`tSweKn(&%Z1{VpMTG8sz}-oi?JlZg5&ubkYTxajJHH z(-WfQ(e_V;1_5`NFs`JD4!SUw^s_*Y3Oz8cPkRerHZ(vUY+LLjzC%P(RSLqpsMe@k zi6ik9fu$SK!>Q8*4mfHhn~fn%R-G-@(`X?xGGAUXmUIXLv-vwVN-TaiP%a%X&atGZ zU_B7_yD6HV6C!{Hq%aVA9X`cshY6?KDq~NMH?Eir5vTa5u2hUT!p$#BQbtQuiWvo% zfhv|%i*|e_kSMktz?9tnPA)tZ>?>H-@SHeQL1kfrLvk8{)prSyP@627_c_*W%Dp8P zqYeLM#;5}A+;ohjlOm;v!iB&_9(RoDk_jh->&p^To`G zlgbu#JFpo|I!Ui6>K%IpRz>{j6RtTm{zBIEz;tTM_`ovKepDd6_eaF?;w0QH@wdS96!lV{aNK2TpkkIEGk+exYSlYnPG@ttS ztCFDoM$7SYGj#zQJ%l>rI$YL4RC2_Njm$6la)#HiL_CtYIck&_+gm3ZD<{XP`H5~( zn0vmkJL75iWi4mbKrK^7YYa8w;#*_smSNLFJMn+pY3%H9Sf&q7v0e zWtmJ(!gm}Er8g@sGOmpHzc`6p5pm_IJvTO2xOuOeioO4BZBAwM`MxcY*|Kd zV=z#9UuaVL|4~Ey?@hW}EBFsoJ!4$x#0-3ii;<%3ph_ixXVB1AZtHs#^9W^uDvm`( z!N&>4#_!KksBi|oyObOt#g(YqgcDe+n`MnPMdbWk+pk=$NRckv0s}{(>DB4u{8_B? zYNQI4+mz6s?)>^;xGfbJ&ykkp&(J7|eY70JbR>vbCS~UaQ)TQr5j+YQ#0w+(h!xqq+zd!|w~v0-pT>*^Gk zgP!bQXwo6DDCd{dOs0*`%g*txXgy(yC77R0-zY{P=0^C&86(RU0a;S6C&i45V`wsz z_YA1M1cztE+XpW?Uuz9aV~xjyr?87oO|F#dOjX|c?U>{E^HR+i%4*^Dkqko>{|*h& z#EmlE5TP7op0c}@dUNOOKZw;TU8iU1Mbm!BPZ-z?6uDw<`lt;bWyHL)QB#x%Mk4r& z!Gdr_elj@d%94ZC9>wJVVVTKp0Dth7d$6Dx#fFRZxMzbuGb^OH$+ht8k;IxePh#Qt z(U36T772d}T^)Nk2ZCuhx~ZqSla*tGBDWp?ts{!iU)dl61vD{bcy)yvK@SkWOD&f( z{+;OJq4moWU{Ab9Yc`MDv2lu=r~xaNBDECsCf(iD|2@MBD%q0z!+r+#>0c@Mi_H)C{$d zfhz1J0`0PZAK)!IMTLuA4)^NHE|k_X4UM&%ZVnaHfpJ)x#nOvNHaY+S+^wD?>`PfxY$k}g>HJ;39ss4x! zEG(a#(yPs=LY4(RB|%)tkPntvPHQF#cR_$3L6zL|p2)c{TJAT*2*)gOBQ~f-R1{4* z_?#*|Yn67`bBj*qY0znjWFAr{a=uqEb292D1;sJTOL8y+FuaVQXCQ&Qj;A?yAxC4y zHJJ<7cy6==u##!wcuYItYiN&mv~qdk(#TgKq&~};I*M1J);~5kOM^C0N)Srz_UqIf zL=Z*Ixh!LX=dDl{q}kMwChII0&}$>h7g4yF;AGFbKy@4(^C^O2JyiMn+yg`r-@I=S zBk+4wpWE+AWv;TiG%pOM>VX+e+{lk2O!G7EQNebR+0Bg(MPywK^(w5n5VeIlk83if zB2NZj!uv&K2W<%VwX}0*x!dcBxWB=IRP#?4EJ~64Ex~V2K6ifLh{~WXV61;BW;&cU zz&;qBuN)W2odFYT*A};FTr3C6&K8?Dv8M^2dE54KWo3KPOW`>1JUA@aPi;1TcCI=Cua zPaH0e>)a^lEzQShz{~^Z{g(x1Dc5zbh3^Ro(HZk2aR~cKC3{J*4AfFuUioz7+Et zUKrCOVaDs4mIqMpMiVyf%TixOA9 z#}i)M38;uBI|O_&!p_7{@*5~%`|BjuY-Ui04aQ<>Fu6BN@UksjK%jmw5cF z0Jr7yP##V2!DLDf_ciNx@%`S;{#AmavbJV|^v)*(K{mfTSmU-M;rH9(1=2OV(E5P7 z*4t7_CjTWLobOS=d+n(xR-=ma=Zbx&r3{25(gr{m%+UI zD?DH88PYM}6Ffin%jXf*10OD-H%wd?zH8_9os%zS<-TBlJZtcVF`)0@opnP^blDrw z`v`CA?fwk)bw6tv(fj--^yb88vz_E-IE0=D+=bvBxF?|G;x2skem8q6wQ@jq6W?>@ zmMZix+mdt#cW@H!%cg&i{wKBor-kTKRy{VXTC?c>26MO951Tu??x-}waIFWC@uqfo zzkYA0tLWylz>fmR5YZ+ItZ;04T3iGK(lqJW0y{|HJ=ma|1_~EbMMZ^7iEANV-vgJx zYW{~+5Vfk1Bu|;ToW<0bXZPUZk2{zZqEVG#43My_(v>lfF^I9sE9szW!OV_0Tzl{7f`Udr*) z;FeX%c{K800P04TWhGS{$lHLlh;;PaI47sV8%9GM69N7+h04Ts*8Iw9iJ>8mK^Y_y zx(s4v0YZAY+1Q@MV9WUX8Md965oXDx4YkPMW%EslN+j%MP){$Hk}sz^L#uqi)s?a; zSf%a2HQ1RfQuEnVX&Uzc)Ur#MqM30V$sKgch8&6kD`wKfbMaq3IEMhE(CDl}UnMv- z>y5^~!#A`NBOQRj@rYPAxXA~Y>}a}lqDNL~@+W~vpAC+iDG}*ui52nO*g_9XRr*52 zEtg~_jTv0BZAs5%8-7XX-S!j)B?MT}8^&_n3DIKbEf$L~>Pj=2S1uU^JC9Uz7c`xFh;@xJAMt$^!F z^Dej`r2muSAd;mSk7#})Zf_1+miq~JRiG#SPzp`8GA*Iu0R0OXHYOG$7_B#JD(^rU-^*>dv&<#~WwQh^DbZ z6kJ;v9az6m6YNc5I_%-&*=Xm9@M=9CKD_*nC*U`Rl%NWywt`MA7A>gFocb4yIe&}M zTKc(iqat5Rq4SG0AGh8o+(Sf$&Yxje`>4&L#?Q|56F1gE<*eG4t%Y;=l4u_ zUl_cvWf#1_<-XfBL}-w~zILMZW_PyaIswcZB8T7DdX08yJ53wu1R zAEI;zfaGvhMt!3%-kfrb9_^~}S$-VW>EP<02TI>zoSVE$(S5$6bL1&~jw8fg)?8Pf z9?4&Y9Kf7!Y9wq)gb0E2y zujTEu_4sCf})Wk((v{ilzs>0bpQ4mV@RxN-l8mjTlAZM+tN(@(%jN{kLow7Jsf@GMYH8pih$6MV`F(r?f`ay^0u z3t@R=Y`cd1%6tX{GqY1`ZgGukmi>c9F=GPxBj{YNzKr@Z2Ge^X4?Yd!V4nBv`J;<) zeOyHT(Hm>czz|tZiw89@82|;@2L@^$a6mXB<;YEc8|urz%^`eZ0mVc^l2a*IKg}NV zD#*)}zUML69Ggy9lpbtiM>&ut!)o8m5~YE3Vwcg$IcoQUMejLfA)QWJ^9-pzQ=9t z7%^Orgg*Je216Kalum*$0t5z67qu!xQAvtLQJ{e-6(=k{a|SA)Uje9DXqDBfA-U+J&d^^lwl=7?vSjJ$^gUC^AS{pH!Ihae zS_*?wjUSdaB!?K_`&U_QgCAfsJKcl^zBj0`gp0vW?DCf93W1%6Y#`#$V5@TIElx;z zBk%Hv%W8SCyms`#7-d)4o%5$`^G`um7;qxN>yl|2EMb4Bw3^N4hI0_dOc?~G<3yo2Vg`$d1paG(pA35fTSdP^ zXW}a~rZ3q-$cQ^{CYj^31kGKe`KH>=+ZF#B%`sXdfycgUGGd(A(Sc*uzVK>S276=tbnlj%RMWqLc+TYF6yMjj+8oq< zX)wDSC#tJqa`o(OILXk&5P*=w>0sU)AO7Wx9x&JLF)UkmHu`{k;3ksA8og;l_~~x- zfhT8+2Nnoh|7J)&fnxhQ9phrOCd$-i#{E~@894z1rpj*j16p$xr{eMh!(7(W>h$Zv z{WY8sJQ17I;cC}nP?{-V(EQec!QPhEfjz4W7{6B6gzA=fx&- zW`2KQ!Bft}SJ3(umoM87S{E9KR{sx)5dG1!W})Rg7hu~L>PM*u>s;5?=Q*2mzQNqF zRjJhk{J2KGU-3u6_99_@qV6=x42$=*x&>>of`)ykj&K?wSa)7noOJiC{JRZ4LHkwo z@67CVAL+$_pO59M*-!-TIwD3-yvMo&JD8XFUuLbhPsj=~C^=lM_Mh6$Gf-%t^qRj> zoCSI7Ki6nee7p!V3*esFt23Y%6F+0NaRnHnUA453pw8R+2;FS{cLcTlFo}J;Lo@bl zpN23n4&57b5cVh70jm(+uN~b52X|+ppr!$v7EGa{2+>3VHOI%o)>LI2UF7i2JUn#% z)!;D^0c$^7H^*|s7sa)D4-l~5{W~3B5BEFo=VaDEk?57OyC>~G2mhRJ;xGqSelZ1E zRBGd$X9C+Z+)3B1=(W4;W(f#(7Iz*=`U2P&E$RnhZ(a)yNl^W9wPttHZR$UhFt$ng z4Nc`g-N-j_3tyc9!e@$@0;E7gRohH-IRxK^6KHBh@h^FrPJ3Z*nJkVw*gYHc`7dmF zhYm9^eD%AtA@gpIu{kLzE-~HlzMm&(L9wxLO)V`fCJv@ZV`80Bo!Vf*Y9X#HHLOE8 zLO6^4Te7ks4b&h=G~-L+FYDTy$Km0EBEk)vBk{7Q7nUaJAiyLpc<9{Fb=*8mGyyn| zYADMnK+(b;iPVwCUS_(mQ{`34l4+wl29POVV+#HI96hJ5x(uhX1!sm$A zl59aKpmteeZ~6>-Yy;a?qW8d<#W!uyJ!aBuKC^+mJ}}Pm9>f=?U`?&73oQfTmN-e1 zR+vcWO2o(3Fv*YHbHfx&Pimlc9(XX%vtlzVK44k9lU#I#~0P8j+H|2lHkkYn#oabz>xidO1rAT1uLPA zw@KXHfVuur9~jCEECT^wJfb-rW6uZF#I-YADt}I?-^4=(EyWc?&L!BJR4g8NijZdk z70;);MpuQwu_WaX5jc`~3h}GFt}Ij07QUu>w-ETI!@*LMv|(kZ2*Gu7!QM2D4FU=~ zCxL&5xb9pjADL>JW6<3QPlWojrF7|*HAf@rNLA&Rm01$&!SqXJg!3sVg(xyjva7dO zVk#8M5MQY0Qp!j6AWOXRslHu~;V6cDVSYG!1MT*p+5Ud;{-PHQJGJFEPQeNlE4#Jc zSNbg{3Gr$omabl?pRyXupvvlda#6(Pj}rYJlekGGc4)3c`9;mIoyB*oEOp3nXv!ML zhu8xYGJgC34Wk|lnmOuV9n#vFhKiA^HdZogiX&*^T^pMpLZjm^#+|7%k*b!FlKK*9 zN8Iz#XFHXvH={kW-kqP?(&s`j)8R?Y=UP!OBOF)hFsy8Yb)H5hk00r zx%WNFwbb-CYi_Rd?o^RqUvg=t2oROd#*$B|-7h2lOQ9UW#)_Xx1o0iJV>*S`qp?i_ zE#A-79Sw1yHpK8M_ScEmsE_*A20YppJbay~O5Iupno-S_#PzN=cGf5I~YL6t~ z2wXbR$-leUCsKc`#23wfJ!ETwA^aFK*n}DM&t%Zfkx>+ebC$7b&KSg)G4|38n>e~$ zyW>?sYB2Vc2bdd#bP`q`W*4xAn7l#^%rg4kVieE~!P1)Re-!z{oBmR^VJX+2IcNT>pI z;w&aKcb7R=O87VlP3|9*dqe<&pTTE4OS^=S0w^i0T@VGn`%XQb_&4*A>TCgTTsGW!Q0|MbMntD&h_%Z?YuytqcxF;gzD&x`Xa(i1QKBil8$!*#a)6=X{>@~BsDRm^H-ouXIAvzKSmtuA-v9QMrgaYI zv=29=7E}FNXj91YYxPMpKSEDZT(Bqa!FjtQc5nK=Nqkx&D$Gwl5o1R8lH z?hoYvIN{!F|CzYkZgL2#9kzg|73W;E#4PG^X7u`Ttlrd(a&CCr|E_OM_Oo4OP>U9z z*f02iL)c;Z-gCp3U)Jg+!F+q$%hc>~GTe2)Z=R9<`BRtj3I@pqu;S9285tzB7_$_t+6}#WeNu&JOAYEqBmXQRM(g^K75er);sAsrAvSH zrs7!p+=1_;*8#OMOd2!`zxQT@974V7g$155{aRiPI<*@3XDl*H1+w9 z$nQUDz$;br2qMB5l68{AD|c^~6kPHWkNVt$>&&D&orR6DybCqf`Dwr>TFEEYQt<7NZ< zu0?xl44)m_92Bg4&z!bM2`2doTozfjuXp12`qK4rw=mRscNuH@$s1;V$lvIN=k98O zCrnD+dCnD#=(I=yizQ`F$r9%x=%cJeQK0PmT|YZLNexL2@v=vR~(5ddz^o~ zUDRFdn+v|iuXFCY*6(v+4@q!v^g^KOk<#3E)8x^A8v4h_Qk1O-1P33m?q0Sq$+za5 zMD?Xx$%t&L&-f+W)RQK$t8^5iFr%qRL&r&c7U2!qDvIY)L;eLJ|8w}eB&xF4Y0Bhj z=$?{!8_Y%oGPef9az+_$OpeLJm${+$SG3v(N-m)w%qByGl6x(u5NBcXO{xLU5uaoUDEBKvRkjU(+IGXnpD5CMgw{JFdPulzm9F)LU~n&-ro5UF29SEmF*psJ>lE`fhvuuI?Ydpg*%~s8jnH1| zE6E&D!uu=)6Uw+zfx&p;x!nr>P9#|t@B&C!2=YuH|PqP=-ZazS*<=d2<}Fp7;?HHjb8>P9ZI06 ziwlrCf#ksKK9>u!iXN!XOE-hzZj{mrm!thv)0r)bfFD&hIKol{0NuB{U)0I^n8a0iYf~GHL)RCbN*oU*v>Dnr#!B<0=g+rgi{NCUaXQmgKJBGu=)@Cp+}PuuWIPd&_M-{ zCsGbAZQb2jxf1{9{+WR`4=4L(z5Bn8-WS5d-~A|mI`ISv0wc7{3bCu}eOFA@EOo>3)IaKdRLNUvb>qj=)Kv&S zcuq_WQ$U)YwD9VrR=qK22wvgcR~|G}1=KeL18(^SNAAk;j~(=EGnq>ca61h z@BVVxa-UU}Atn4d25+<=ABh~tG*4D5s0bnVr&s~(@rU=S785R~V56ZDflf~eXv?!Z z&wmO$kDm|@n%Mla@%xceOnZWblYg2~>4(en_J*L@oBZwpAMxnlDqTx9@B154P-y)y zxYqW}OqW5S1hrO>!*SdEbJYv1%yD0C`G=>g&6PO)zTVO3wf=D&^Og{DHkqxxTch;@ z-1p&M&!w5$#c{ERhpf*|4dy4T_P+bf=55iE!I;t4>~&cjT(%NL5^`Pz{-X}*;y&dh z<`^Rd?ietM}PWhFa(pm#X zmMy}*r#sL(f9Zs_9@xio+45*B%I?!kwX@&cTo+jQY^Iv^EsBH%z=~y{S4FUqg6Ys1 zI}@qmgfPVxL<(sTwEYo4Qabb(f5&K|Hl(ik7Ac{@xTFvgqg0t|L?yBn(DR_wN#ah# zqlvLaDJ?GY;+&YOOtRjJBxJsAA5%jux*v<=D!Mh>rin~YgL?zyj%H2MPtix>E-!>& z6X83j-urpWEDKF4K=Bq0mQlo9fZJID^Oqzn z@W*K+{6&p(ENCS)(-#0?&63DIlyIfJ5Ymfc)BRWPq^ZP=F|A|j$AS!l=~Zi9HedBf z)#ku4llqDA%z?;&e}BMxQ1PC%9l|(5AiQq@4!B_*lucjWD~#d=4roe;BLojF9;bW0 z--;3-!D`Si!`g(>YGkWa(pgV9lzWjo7RXF4+)L!XY5M~Enr|Y9 zu0w?Vk$&>Z4?tA4;I(NN$kI6;h(^Qb6s|k?S5F2p6Ze>rPNBWp#e7>#;L~!8>7_-h z@PmLcOQzcDiTYgrjnn(u9u&-o_vdn*gny`bk^?`~$REPTw@tnvbNbC?G&8vZmw2qwiPlw8>bL^Vi#ntNVd4?Q3T0@?z=npN>I?O6djmHrWWuJ?vnCd2fG6S6%TiC& z=XaXM4PUqg^)HglMc;6rr#}Svc3BQ`HkBME6ugz)Z+a%OVlx)?_w;_%a|V-rZtD|T*>_r$9zU^yY^376+;P4p!!#E6sbZ% zq&{W$7yib1=Q`!Zy4|A-xE43{j5G?f_oLJz0+D!vsw3Jy^g19zK!-A_S~kC$7U zAbj9Q=6x0R5#L{z)IGIC@Rd*2%1W&0hfXp7IoU~Leg3}B^&2lnq79jdQ8a-LzXJl> zuO-3i;Si`oaMuJv7^knkj-49jvD&NWsidep!xu<^3Jbu#VD^Ueq;Ui|!U4Op2PxVS~fYNLOgx zRbg!1)-|8|FR3b%2a3+y^Y=d8h3hdjI;MA@iOKP|KAWBA>jp4z?ikFS&2(d7<^!Pj z*zj|`2j1HfE2hD^C4BB+{02AI{N--i9!kz(xR)=~)))Wk#9uL=SozJ(-{3F`_FIu- zUr%3=J(<#7@D_~v+|2}$R|jo|*SM=({(tlL!X}n<14RjgeUT;g;s_1g?|jzVEMVpG zeZ~5#us-3R-(4qlpO;PXSoP?^ADyLPmKv@+=P(HbT#&*BNf+}aM_&m+p6iEOj@Fv?P?;q{3Z$$QjM__|NgPwO}Mxi_H75 zslq>mOZ>ZWg~SnHp}re3v~P^MmejUFQv) zBvQkY(kNMR$&z`+p)0GhgA>kTdAO-blnayEro63ot5nya3c}k(B&wxGaE=~2s2=4B z)-O{EWsIm&0{bEsUa+dl<>!A_MX;ZS2y93u6@$9DvxPFL3vltqOR&T)=8SHGLs6ve zb{fSI4d;~k_|Et|;HUb{Mn(S4khwE(ipJ+aYFcwZ6a^)5(6X6%1gjw>Mw6pZnTWoC zKr57qT{L7NYD|X35!8JJQnc)IXd(zYg_V`c7iSVb@xl3O9HF2r_eIG6SbaZbQkNb* z_R#uT5-nktQD|^yne+A{AoKgunV6e+nZRpeIp6QSV~8oI5};uZ7C|VXHc?puL{;~e zK#pxQH|N(~f;K-7=C+Gin=97?$fIqbRVB9gf9CwQoEyN~?jd2nXglJ_hfg%#_A{vs z$5AbD8@h{bHto26hyFahSPN5J`<4g%yRE|MRqwb$V_?PLQzAYy?nFnGJ1dyzH8AAx zT9V6-u3~Cv>UId2RI=gqWqI$Xh}MJ=bj&DFYOh4A*jg8Tv?0y;ju6t)IJ@G0Uy)|< zOv34XS)Vvl(IWlE0?;*S|ncYCIOUB@k5^3;U6ZaUbIuiwEKk>wS!~^*CBZtz2kIV+{){P?)4p(kCb^s>hwf|HlUsHDjOG23#g*Kb*mr#uTyBG*ZK7;D{?e^vQsnXl)My)qb(Kf8XK17e>9y@3L ztV61iQ=I%U#U`$iRjpd@y3Dp2JBLRHk8kZr$PP6D7fnlbUVVBuQ11o7%Z)pfRC4H= z9?#EWxpPF-IG8yY!!h!89 z#YXUgO2S88?+h=S5c-^s21;{_dpSf77AT%?9r-gwULc$|xcdxfXh;*iaP7XV(N&kn zFt)_w7=&JgY8p?B$JZS2EeX(V^%t%r1*|r_BMLa@7}g<&3h?_Uc@`yxWNzZstl{(& z!Z$@TVB8pV$?@f|*UlEe8=h|j729Z%RO|ykiI3WoyKfV2s571Az1~$66Stci??=34 zD-DrO=IIf%*(fsc>*V$>5~O=t^jpMeOct|VUFp9dI^Y}#p}>~azo0|h*>Ca$7jN5+we~IEJW1N0u2mF z84Lvih$3-V7-PS;6P+!V8Cjp%_?N#R@Oj(Srs5n3lMVSDkFL9#x+>PP#%2i>A@3`nmP(IkQ)X4I~>cp43ZJv?BeXvvhX3Ue51s+b+%{kF-*8hk;tzeNO$DJ7x#z&M~+ zzBa@>Clt=gsH};yc#tD*)^7`%emly`6Y?M7<REGBG6JW$UySV9{9H>4;mnl;r|iOX*)zqn>^Jm8tOBT$Vyby;%f0+r zI}7OhDhrGQ6+VM(U@L=ji*yKl!U&nU7>tHGf2DD{L4Ou!oc@+JgZ|FYi=3-NZU=Ro zZxkbib1)KH^&j*|h>L#8{MQrmYJU6Ujm%Iu1JD>FcvCoY>1m^X3a7fmWSDmkt{WEy z`~FHr??Rj*+88l`p0xspzU0BEx(gg9A$J^pZ}}~`W`h7a5)td2wfTZFk(cez_;6BF z)_i?8d6APP-j!Jyx7>VbPT|M7L+h3Kxf z(8l4Vcu2{bPL^nPn~Nd>gpSJ#$P2+j!K;;T66OX%>EP|t;KvBXtCD%VOrCug);aL= zz7sY?!mojl{M#q0lc_4IgP|joPl6UK1`!k#^d9#mXFe*2~Nf2=Kb4Ot>56Hp9J{aos3;`jt@6(iGL;kv)Yu-B8B_iitn5ghx-r zw%aIldQYA$C-fiEsRv-cUVH}$A2Z6Jy>L(HWC-ag4EfJ=-cDfFm*ngF2)z?)F{4Mp zlKt6o*TXcaV%7o4cM$JMFhvc6ntjoIlQ6meV!E4sf1KyoTkB z@uDu)?nW-(!gLEKSzx=obiUX9`cMOxpuHt$4L^G3khG44X}hYencVR7a0n)nQ-mje zjf||z;jt5tvzqTP*cKUe8Lm%^6C|aFB((MMpYmUD5B?`sx5zj-+;Kp5hPS`6+<_JI zSCEv}hUW85?a6(i-p>mD_-SmM+(bFRS)3Ymx;4?WZ{kh%_1paeulrU)grI9mqw7Yw zSuU(B2UZ{Ej)C5(!0p2NX^~5cuguq|EJ~q`vqAjY_?&X|2P0#`p`Zy8+gtFLSd*de zo#2wnjb{zenm_% zSQ`?TwmL64aT`n?La*osyY7K*9$$w}{{C#PZ#)fVe!;P+?@2Arns{6YiVdSZ$aVPN zdkk#v(ES`pUBF#b3!^IG_LHgOFiZi zEm1pkiP-iX-@?+OeL+g$#mVr`X!w!qg`H0yKCPt>AVeS|RFH9fm0Ep&*L%-As7Y?( za>>X?5!e|F+-JUv%+~rDUw02W-dO0hX`Ee>p9%O|6syet$+{d!N7iUO0++$HT1jt9 z8WM=ooeel6bks=A2d5u3#X8=*t#+fGz-8v zQp973?-gSn?3Y^mn6_IgycgW0)as_w(bpxKG^A1z=Ik%PPNZ-2=^BdE^e5Ohw*6HK#HsO1CaDHr>a=I~b0$nmn4pqN8#g9xXPEPZ5K zD6LIw-e!b#MvNw&(UJ|8dzDj<{&rnCz5jrA*Ph-+MN=*!s?OZZFPLk`3!kX^j%_U_ ztnqHw6Ta2<+?S*_IEYe&RyI6XwEG4>h(U`SJiD(E40G5;0e;91u&tr*O#PE)GAx}z zs=6pkMZ>U1A}UZ-&TqPsZR+=B;#%^ZulE4p9#G5y6B~1-&-qmTdVh6|tEZ$TijweV zs<<`1=TB+T;Bi-1750(3NJDw+8+<+#`J=?nPj>TmV?IZADX%%ME^tf{(Pne0cvpE3J$2Kj-1)GBca zeBt~@V6OF%^cn0H)3-1@X*}M{$QOfAN3Qjg>MFVV48xO^lpqnSyawn^#*|4hw5~GV znFW8-Z7pxwKd0NK+EYgtnx0~*K%|F_U;-QVVeLu;-zwUedzIc3~zsH;t z7Z~~l`L1j&InhZ}Ry#fY9Vn~;5L?VGmigQBb?9SQub*vK6 z0T&F57jxEHWx+R!B4Zj8>`cOF=+nhQQ35#OUNSz46Vpfw_f;=fmRXhGBKq@9T z(yxRl&aZ0->f2)Hl*Gq;Zs7j(|0ayBLb-!j(KDls*!(hjutE^0Q3sVKCNQl;)v}6; zQ3bpS0Kc-?&M?;W<(sA#P->OeP71V31BG)SfoXd8ZfbF212VO!gX(OQPZd(`g>otb z3CjV%JEjntvSG{t95!fmqT)kv>JWpje1xT_gL~e>HX3g<7p#Pa=}?Ai85E|h!(xPA zj`fk1DVQ2rJMeaS^L@!(f{r4lbHl9TOe>s;7))sE2sS^q`9wv_a4HR!FhXi8!tXU3 z3tn<)Ks2C{E}EniyZh3Hc$!R#cvpy7Bgk+wU}B|)wI%E$n;s%u1cK;XD;!`Rk+Oj@ zUANpgiR~W|d*0l-7+4?CD?c>!sb!+QFub-=@;#<}Gju_;QAhnHiZ^f+gqtklsDMTg z;PV~6kFP1T|4T{QSKKBI?eP^iWAj#^Tw6nhRa1Jahdxy8S$X6L#If#Ad7`>v&K=mp zd}YkDotlKU{^+p)ixaLy5Rg^YbU?!%lSIy*CA=iXfRTyd$%0MLC=E1Mdil+ff$VHu zFoRTzBMRbSP~}}TAWx?DD`NWki88&USCw@6i|7TpQP9*C2}`gc>XTVi+s=%C#Z|i` zwvPSWoNjG!**my{KSOL1CZ)$;GiR(QB|~89m76h+3-}1ZVK4Wru;<37_{5LIDlS8!5teRq?c}Y&ElR|D45T;U&=&>judW+bv`+WO}Rc`CySs1<=N#aXFz3s$=#&HIvZ*P~Y z5Oh6h;!d~)R`Hf0Szq-)>>0y?5Vt4k!R}8I4W$92TZpz3REf-6WIB(x#G;CuK(2@ddq&D%@ksl0#N|dW`C-5>57Bf63nG+$m|Tz6 zdYwcyx5wio`IxJtg6mbNPi2NoLe9JJr80I0;L`+37Nfj%Eq>cr-bj<>DN)1Ab$Hq>=c1 z0BK0vb!CWuy~XLiMWd4uP1<2yYc>NX_rO>4oqj7q^Qc5db7aNPPm$Ie`)$?sxUGya zp*b5>PqXMn=iB>tBi8iE6Ep2u zYj#N_CBkN@8Ww6`i6NWo2Sg&J+Z#IK0rVQsCrPF|Jsq%6?W>|JOYsPW3Ix!CF8%Y$ z6DbO+*v|I7G6N6XT|5QP=|nA#$vM9kJfjhvbZ=7}>9uxP7{UI9}! zk;a$xFNPAUSQhy|qJ&d_ExB<{urV*5ICc*9jLj7V>A2X=OaWjc^z>Y>XV}z2Ieew|0 zG9xa3u+327q&J-9>;rDt&v|fvM_?R@oNk+7ngB~*o!6R}%g*|-sO6$sKdN!5b|rks zq!2)rbkTap;X50hn)m0%{XvaZMcb2g+Gp{ z=A~s;A7LfE_vB5E>dV53N|;R^e`Am!7NE5% z?SL~u1>77690VVNn5nfCZ!97M+D7ObY|=grO&pjDP<>pwza$bmG_m^e*}^v+V8czr z10-k0)9j?K?JKB5$1KH+erD}==-jNp%uD@Kfkz7pLwTi|C46x5riL{NQcQDSqds;n zlVEM?#jc679f^&T)RRrq+L#qHqBlW?JyW5Vzpz}Hb8u3EbSR+?3v)Biv*_@|oppHR zHKWwD$8&e6l)ol=a`mL4-5-Cn_?okMlTjSee72*3Eqmhtr^gy>#oyLrUi(&gp6TLC z;+ht{r#M=-HbuW)Ifl@NpMUb*&N5_C)8qq_#B7~pJpFl+X?Dt~Ou9DwwvUuv=iZ9j z>b(}LVB;OB-BhwWHjvGRfPOVJ8gGQpM?1vhgdsp0Nar#RWX;3vwFv7m74 zE8cqno1r?!Bzu$BTwvubNQ=V{N-c zqf6eX5&IAKXo)k<@t=-ghHma zLJDuE_KtZat1v11NsL-0{5lmC(Hl{WC@5r%F?mEreaeeDYO8T#h#*v$f93tM28=l= z!yglc6?&7lgLs7G6X$Eg+<%huIC5W*&f|@tIML${3zqhZOk@QL?}}ePF@)fv;8Lpm za4JzCl4dGJ%0Hyf3aM19Sbr zaK_f*1)YV0$@>ZHd|z6T&iljRqGF$I+oKS&TFYV^(hrK{kcVulwmS|Zqn7VL8V;cl zb1pvC(dYl2xfz%NU=;Rv4Xu|KTkq*@$1TfT1ODcP?ZIGjns%FL*3MEA`vk6G?@xKVN+a!CH1eKoY>qRM{TDsN?a)(P0kU^*W1H(hZiU!kXl0O-@v@Cs$e+e9MvN`JFhP_`qM%FgapMe4MsBw|);U-?hdv2Q) zS0fu`zP?+K(E&xhucIbop(ps$lI-(d%leV2Z?H!Bz~OYB`zSn%biA4eeTa~6WF
    E>)*Yleki;HNx4mp#eQ6U@tOtgbE9SR&gj!+?ro&hD zJX?J;=~$OC?3wX7_B{)H{5R;$>GDjuy|6&CBu$vBYTM9@DrN=(ZX7+1KZv zOnIldJ9hQsA-U`-RY#h40&R?jftcFA9c0R8+DVN=8r!W4&=L73glcAJX+1v=jSF06 zgJTgxGqVKXk*;vDzXHRFBy=_DnK>eyLuvb`&ri4i*5kn`8xA&gmIMrJ;CZjVjp+by~*o~BqcC{`S5r_;LMgt6CLfIlDU79hudtb1Jvkl5gYVpDUpL5eLWX*6RGdK|kO-nBiiDmI;4qJ3zJoz{1{Bf5rrJ=_8sVqIE@D$?^nJZ+ zxEXgoUMj!1``d*Jf``!JGy_BeV?q`gmL-|HZTq^AvlKf)8!Uzd)1Wy12i@NBA0v2) zyzFh-dDu+9p1yK0`FT)zZ4SbWg#Bg~j$;=I0A zVY6)K16-A9sS>Ww8fFGnrM0Pmwb}XcY^X%NnrS#4v7gD!4AxeKL49<%nP`Gtg4`!w3jCe&;xVXSkizJll zL}Bj?(D2yV7WQQ1lZ$qe>=iyKWrb3DH4PyCTE3nfFi1wNd_DIfz zKPee44m7e8D#n}NmdMc^liCyG{SLRt5j>@A9HQ@T%hY+luuIL<0f-iJYhoOn>352o<_OZw0pi2iPGr;Q3`a_vIp?Y2#N?)${wJar!TR+hRbmgp$ z%{2J@z3Yx7>~ZA$^j4>s$0XeT^CTGa#QBZ#&#;uW@~@`dxo6h&JHi|;v^n3#xJaGY z?NsDX0>Kw7{hy!rv~v2jIJ{##ao_X;u)8_2wd#?3{vpWPuKMmxHhH%fyN7z})Iaj; zm7s3!{fb1pjl@bKHHj-g=UWCk+G`sqN}oDi$0?zzFx>=!a;txE8Z*?$j)bS0tyf}t zL7YA+gN&9T03MahAT1USOC~-jMC!fmmuR^*skST?{Z^n9mDA{7B_JS-#MV!k5D_nM z6~I~)5K_dZz`J)=XV6FHQuhZfYxU$|abBc~@QplGx?_ZeMot1s3Udd{nlW3PQWy>w z*2(gCU3yL#id_yo>a}{;8;S`b$SCnV?@>`W8eP8Bs$Z&E8gqhHls?SLlq)0xBSgVy zy_W)So`a_}6iRgrgTd-8Na8{#5k6X7b9&Y*gN%N@0QOEcU^z2g?1yUw!9;3iv0`z( zj7e`~RD`6O7)iPSsc~3L@neEdkO$gY5EE8zHLB)p1gaIDFo*i<5IgLl23pm0kcfP{ zk+Ni8E47GTQ1Hy3IErkMKw2q_)r<-%;NL7W#y)^21gNEPzSo|CQsk#H>tDZ%J`vec z;SqKCJ(haJiD*m8YxpRcAa)u6H2aE}jg^$79cg}(*tAIUw}1K+t?#HS%u@zpS-+(t zc(uRRq`rn~TA26~%aD)wh*(+3fut$#dO@#Wi zbM=MT{N9B~>r;>EyQ}un5xW)Jq=K~V`-Kg@BV~|wsepKeUHG$kDdFm$hyc{Ov0=Sp zo?#%uA^LIZL`!*zs;{jXLO~QW5QCVc3`fnWzlzMS7N&)GQ7KTWo^SDZL`pkBZi^{X zECg*KVD4U~{4~2p5wL^(j0Vj$FYnIWmu2b%bDtTNA)+UZxUP4SX@rDn; z(Vr(-9TAXb#|)fDzgNDqWS;v9_oLLGp_B+iG$aQ=v-T41LwKhPab7p7{j<_?tN| z@Y9*V3>MRj+77_`@UVbTsvP?GsG$)VBVp*IQs+$q3t_q!I*^7E8fQsr3c+GuSeL!J zLS$Lqq{d{BpBT7eZAGb6a#9*W(Liv2Vg!Wi5=bC4CKHNN^p)aex9(R{8<7lpL+MV= zjsnNg>I3=CJ4QS@l*AS=`0Wq_-)VTx^05Gd;KY^-s8S)*d`l#39kxGM;&nhGLCj)W ze0vNbGg+ytgAo>JCsKAbemQA^DIQA{{`WvE zQS+=svpV}kyj6j9bQ~6>m^(7+WKlNTO%oZXHIb*C!NR5uBy*YYFfxXvpeP^PQR;mX z0KvAYYOBOW$wg%IbSve>aAbbdZcI$z{=tcLD@?Wk7!k6K7d91l_=@TMnNFoGxrQO6 z*Edvmtx>pRJJ2TbFYD32Hfkpqgeaw zc~=$PGIHIL;RrC6J*{$7msw=Bv_QD3yuj`Nnh<3r+;2!6JoZfE->A@ zR)3?&g}f0{5{#mRYFx)fb`s!(&f1Im^|loLHoUL_&Zc=luVE2lQaj5VDdUbL~Qjt{en;;{}zV1qP!4&4qjCKDyrrTD_dqbU}; z&PITm5wA)VM+cLw1$WJea8+TQAND)wuVhZZWeg^gGHwIMzb0S{FR~*Oj)M3rgE%U# zqqN`zEAQfBHf2juTO@Nvn}nZ@#J0dDlgqMj1Vlm>6_aBVk%Z}s6GJ3oFJt(twKnS7 zfI7%q#$4p86xV+|a!dnG^{UTP)>oB}oF8mQ8D;rkTW)Dih5CzZ?>fG1e%xq%etgyP z_N*wqG;mdtAf4o*Y9S%LCfQ7&kHOk9{5S6?0!?FoKj%E!toSY{Ko0v2xv4MrY;~rb zn2l1^k_t9tzOu)tjD2kaK~Awo1ls$`>;u0tH~Hdp`;WpdejStX3DZD-`zkN&qeDIH zrDoaa!SBES4cULE_>~6$#4GVvWfzQ45|`G50K_~IgTZ8={W;tC3CLot3b~7(QKlZG z{6$3nU`SoWXP|rZ@lh{o0$LN_pC(yL@CjbgP0`{fh3>2af`Q^pqJ-1uajV?<5AZ~3 zw)HbD(M2QyRw|N@BV2ZDt9ykfZX8*axzdC7NP2D0ZtXQQSCP0xh^FsQKts;g7i}rf zE}1_<_WO2GiS!>O6q0*UN`E?PK5|uIRNHkKlo^HIx!F}A^Z{tC6Ut{7X$7>0#(7Q| zLNB;uktU!l(BB?#5HH0jmtkQ|?GPPI0VO}!chRH*NexxyLK*JKQzl-_p=9|(TIHqWA z&)JS%)k#?yeQcQLC}?cXnh>KWS?H#dV8O#Qgvg2%1ivlXGE5DLzY_fy5C01yBWSIK zWJLWsdfi^y(Ee^}6$l16GX|otk|cq>eFyHUv8%`ER0&TVJ^j&94`IDsT(a(%0W9uaC)g!n3=*L=wqNzPN!@tCrQvGSimCFX0msaU)FVTR~=4O(I@O zDvGHx^dg{G209Wry?q(aw9(#{yR%c2J6D`G9xogA%nDiXr;+& z5Og_mG`RvPE492oq-DZM?R+8!A5cC9TCdKqunhu7x4aErlexDuLPyLx5UQdg(eE>( zf}Q=))c$3cHNsd{s~(%!0YVoRj@`HavzSKv1`< z=miHA6dSgTQMMO?KCV5pu10SKOMrlAWLFZaMSBuuiM1@gi;V{v)l7YUFRrRIXBO|X z2K=N$Fj<>a3~dTrrGOvRmr}ZwSNwPJaoj^GuvERc8?Z|!FKoqN4HN}_@C_V4l^6Kt zF=eU;9h3Ef#HtDsD7v9jp~3SJ7!>D}*-h_Z1^b0w9ihckowe{{{a6*mIH07XYWiW+ zf_yC9;%bW{$@mrM8F=tUFswx*#US?*OvFQ`CDlYBaN6v;PY6`IegAiu{f{JjeIO+? zE*gS}C*jnllf~l_5dkq42R!V~TJ>?R!R=LY;dy105EGNSAYZ-kkcReON}RUiW589E z7G!aVAVNSO+X)Lq$m^$!7Dhn9!G}B;sRNqTy6tp)uPBoMlZS?^1dt+-_r*)k30o%v znc7>?h{jQHl93Uk=Wmtgme&71JI&v2oI4_J^7v+Vi`#a^#JHX~Cv#E=c`*5>`};AG z1$76$64ghupm=fdhgfY^R4|mT(7~EprmTtERcsgw*De6P(@>GBi=d`c>zgaYA#UTr z`^-GeTVXOH0&XB(*glF|ip>mU7)4srnUI4+gY#2pd7TpetGf6vG_BjiN;wlIR0Hq& zi^z7I#8gBl*$;kxm;NUqIinGfDdvStK1$?BrP40{DZ^ePnO zDIoshJ&Cn)JRKR7xzkk{)c9Gi-#XDt7=;Le&Kwik8vh5Nuv@+L{{O=T;EF-5Rr(!- z%02r32IqP}HyWT}tR*08x_lQ0@f#vAOI>pwGdUUK5`?D2Fa=oxKX$ zx>BK5Lol*mDw|e#l)~iJ#pej2zg~|lu1L%fWHIJo26Yj6#m~v7`hf+B6k1oc6f}3I zAk+#o%_ed7gQ#Du4NQ*_B{Ktg_%Tgzb{G@Rt7LY0P|p%il~T+3WoO)xm$bt)nbCQDCnw%_bggXJH}a&JMyt z?*bO%Ss@s};xuL#7Fo=;kLQd%!-s~9DW9A;Dmfo$O+ezQg|o`v0k>Nx*Rcn;6WSog&qGZ2^OWVnJ4cZa{`Mm6W9&5bOdm76BxsiXs<5Y%V0d|J>cjAbLFMmr;POgnt zc5-*8+Z%)GmzGz-(;p1z2tZM|h>K(fR3Tg&%*^=R;-bY)n<*){l>maY?Ge49b#-*7 zaWLW4YgHhw`q$^q8=3R?2E>^{Of{ME`Gt+k(xa-QHmF9tPS4KFRi}9yv=4OtClqGU z;)J&emc1aj^z6z+y`3SoDWu0_XHi{fVo#4U|bGB6P?jBQFSG?6ulv_*0By7SwxG;wVXnlB_nZW;dPpZjN${>KsE zb8jZ7h=qlgJYtF}+|3A0$ z-%FoW$cStpdNAt7u#?HRQ~U&|P>|S1ea`id&*KI>M?OUU-{RvxQ|qGazmK;V3>!Yf zfk%CnvRa~AsEHEFrA#B?{kE>GZPs~9!YNYeE1MybsR)+nGupFV;ejUav1lGxHBL31 zc!*z~Sj$%e2n{U9yaMSZLdxRIW{5bn;GMJJF6R+T&8ZFS;qX-Pws{NmzygsRNVyyTvV7|2{Q zIV)y%|9B{ty5bN>l}4D`Ny#HbN$gc^Q&@FL;UCO=w?8^nC~ zePm2asJxn7Pzm5)Yc|o}1fS|gpxdfNkj0h@{E2CU!%?#lCd+r)VIJR5g%7Lvj$6_; zpj7(<(^Xw@umUfYMEi_7#Bi}5g0JhGOztX;Nyo;bd$Zp8h~7cAx5kYLxq`ij$IYneMLuwyqP z3CwC_N z^I_+F@MC>?(5uw&4v&va=?6~|+70X7IKIIZr>xS{d*Y!E{K5sTTa8>iBlB_gi0sB! zykf@rxl@dtASL>x6cIBshfB3nB0kAg4}XkeS+?svpRr>KP|p?axRdKlaT06gi;!rG zULnDWWN@JT07UEb5#xe4%0X_znjeG9Vq4Sbx9a>3Xlh5FAaJQon{d>^M_}J+AGmy< ztcnrI_tU8ojuQX~^Y{eZD_KFH;^IMzyMwhS(18f%LasQMQ9K}~7r?E3Fl-AMprcv_ zLkgMu!abtl3CO}=!<9*>nZWXAdM{g<1{-4~Ka$`EdC*X$nU-%;E#|-4kO=H94BM61 zxI#A86iw)b^VkR+^@MjyAlE&?D19H<(D=d*ck8;iTy5EYdi`cgB@i+uu3c{gSB^3d zpVYuN?13F~weQE16p(Bdgu}f?s-hBn%^lHcK*uSegc%>&jNzYB=;twDq~yewcgSQC zyrMtn`b1)>VI8aCZ%0I)FpO;sZd-2hVEIXZRO@}iGcKqU^1geKMr7Z5fRR%4y%5D} z#M=~`g7QSk)eL#DQa7k`YO|M{S=&YDdc|KPiBT`Qlrpq`ST{xGNKC9KL^gB6JeW%& zpHGm4z*AHH#tbYgMhBmk-q;f5sal}k89ON4-xTE!_iWthIc=BcfN}fl8rw`8DFdcLd5vI zG2eJ2{_wk8#s8Wn)aZQT0XMp}w*7b^6QhNh;JX(7`>|*G2W99;+jsQDlA@K-tcQtM zn$!sZ@jz+BLNB^0j4rU58JOd?Y)EaG?cMb@OQ7t0r!D6}h4(h4Zp89-<#J|e-(MGb z$XjsQQh_!>9_-F*HADafG1W5^ocEwu&}vft*0v=JiaCr`)zsdMM$B{cBpBkU|A_QzHN5suF^ig$5L1>?>H*h-*JtMFkl^Q=lX^GSeoqWWsJzb8 z?(}M->gHjHTnbvp`Qng0AMB8YQj*KWjv0H0poLjz8^{4UHO{<&^^n6u51;Bji2^hn zELS&Ru?lQpUszcgAmG;Q_yJmKRXZEp*tqUk?pWw#`Dq$;J_wZt`|t;e$C7wJ5A!=2 z#BVAlFg`(B$~S#KQWA?0I_`?V zQ=mZQxSyp0{Ce?1H}jiE{}KHg_nQ@kxDu^3LR>L^3Dk#?aUtCNoK&GZb~{-^kfs=S zww?1j_q@;A`ppT$mH6cDTc60{{;7_TKl%AGaz71Rs)Wc*P##xIoD<{k^c7~;OfmHmz`=FJRRkoGs{lUOm2_`HLfi|aLoKIBgf zoat`afRKwg6-qfFVrWprTPbyLb>iZ>R@X7yy3+23)cCe&?TvZ*!~NRX)9m6Xw8Pxe zd~)#2(V(h0S;}{`FYq_`>9pY|V`uD+Bg=E?UDLw0i(GaG6sB{}^+L7h?!*3>Bd3Df z8nbInAj`R7MAEGwog_cjc~x(~+2Xqd3&Q4~5S0W5G8`%R6XwOYtnUav39sl$>9N~s z9v?>qj**%=3=t?4Y0dekTX7oCb)A&Lw-E#eL<0DVx3vD#wdvb1+ol~V0cIv?jBO{u zA=G4CfW6C{pil~Lm~!eT5*`Lb*m*3iL<093+2D3RFsp*wL_Y?(Z+nCL+ziKthuz|E zQ5n!=H^O3LqGITM;zKtB-ZwYBv3uF>u0@IH-=LBJ_;9515*MQ&i%u9)Nddn)H^qS> z=~&~#C+xkBE!`*>!i?aeA~ox_fbzZq`4dj2@HZqRq@gn9ljp~lhWPj!uHop+nATS1 zurQFsN)QPb8UEk8`T7#&OkBG^BbHD(5$FA45bYq7ASwnpTtcvZ*x`WzziGAYa}08W zoU%UuI%_QpDimo%BbjXYQzWSBlbY?pMsKBKOwf#npoCs}R9M9TDj}anyX){g9ho9N zRM*a!RchJ{ay=#fz)2*%N-Wh^EikPMT-TYkwqqLu5jK4s(_PZ2*Wt#Zt9)Y8s?3li zPXSAi9a6ru{qA&c*c7tKgF=|6=PKxHOA`B&*A|ZC7=<%eHOX zwr!hVb=kIU+qP|O&+ME%GjsMYynHt@^4^SyfXNRiAd&gFk$&9e*Ig{^Q&_UK(Z%%~ zxfE_FoYdc9p0E^T@Rp!Z@$n545I|iV6rcK7j;3_NwL3cNo}0CQNpU3XjMvK@clco9?*1ql=wb?@yCx;v45P>4ca0RyH zVhQ^5-YmA-%w03Gn&3Hb*u<|4E_mmSD)tt6C#56tD)mDXVu8va#hG=~k2wmdyZAVH2A^dO#KX zfi|O$%gjz>_i{_`k@c!My~AV@;u38zR1z7hs)s2-ttVuwopuC{Ns9I>I#jfNWbkQj zFS7wNPiQwAnA)mv?k_D_8e+N>WMB`DFL}!=zws!k)~T8lB9+6)R-GEPsaF)dlJ92* zyOC$QGNTN}CkKatUmX7a@=7%}d@z`Ef=EhG&VHQHu1CnAVt#vM53TGF?xB3Z#cho> zc_TMg?ATA8oUJ~fcfM_AOe1)*$|60qrP+Jw|QGN8W$ed$-g=_*qKur-RT6C zNH&1p@G|Od;cMS;JOJ>FP8n;xBoaC*TTn&Zij zn;M|fJU%>`ei&`KX_I2R7D{<}6RrsOoTBkc_9|cYx_$SVbn57?am?&@i{ltKJ~5EE zCi`$?6r#mWsUFagkdi;F@V`;EwYRul{a9_D(-Q@J%Q}s0PHHE+ceFHR*PHOpw63)} z;iTGYr?{8nkGd2_#(e`VaMy5q#m3YRT=UYzWJ#mMdJe}l(GonocKkJuTP#Vtywl#P z$?V$b*8?1h;3RPT#=!YHCw~udYaE!X_%WgH>qf!7ng7~4@(7(0J|6c;)|zp}z18k% ze`;B~;i=n#`CRuI8}{yD+^UF8uK0P7jCT8XU4be7Z#cviXKHcx0}9Z2y@ibSV8BC& z&h+2gyMu&@5dr=6myHx!G7Nk#*5J|ZU(~J%M!$t2d-G~YHX=p10)wwQhUn&1;IXeI z;;RDL16Sls@f*KVEbzKPuXv`{G^CTEGbqR;LQi$Sxt59~wg-$fwb+2X!o*XI{l1IR6P^L!RepOzxTzU}o zo0f#kfOu09wh+lS+h)kp9A|cWT1I)4y$IqCE{E*s7f1pwO7Y1V|B** z2&AxBq2c5$1S5IW?pHvEdraS>Vawlx;ljXneq3&+l%J~C(;UG`Pj~2Ixm+kz^t@~* z9IGQN*@yYtTk{rPnR1ZgT4k<)Z09M%b7CKK~MHp;6y_(dd{5 zcm}A<_L!#MS_R*iKh1KSL7`khf#4*i;7FhBJu2$IMFW3s=_629w*y&JT;72Dbp~4X z4s@kps{wX!x zeLGqD>q?tlJuj(pu3IKK)NXPz)mkUpCU-52JTOve$Ao5Q-2F#@`1burvB|lB7QKv< ztlMwr52{mdY9=mcQkP zl)5@L`$HupeJf_|QB)f*Oz1Uy5i8FG{dYniWfa>HBFx`1z*k3LHH21cp3-ZzFk!K{ zgUgkgm7{>*PseFfE!o|(uw%mCmLSmZc>UFIO0~Xs($-vi*Wt(Sg4UW1;B$7DBSZ?e z!@p=fw?SO1oowB%T3vKBQVf&MkbMDuh!8uTG0(?VwckAxg~a1gx_y&F2v9$NB*ogsgfRAl5vJ+7F755avd0+kk-p+sl*0vn-BTHG$Y_v@(#Re?dPGn`kt zJ(w>??HGi($JDs$J-|1?RsDTJZ=*RbuvW)rMj~6zsJI-#RTXALc0<4IhnZ7Fw2rH^ z#Fl>OaSiy~=}Kf04*-UYqE>Y#ifn8y+!3EbV)mu_G>Hh-{pUZNBwI-Xxvr z2A)P(p-z|Szwrz7DqGDmF{_<(|1J2oF8YReSl!NMl@uC%q{{S1%9t6fr^E$;bub)V zk{iouMj7^a!mW-JR=E8%q+T7QYXuRad@zk{`nra06^p#$!9-tSn#- ziy7n=qGK~WpP`S3fBA@2$ImseQ@glv(Wg326XlRjK9v~a)s5m@CWYPa^o|ri{fkxv z>lo2io(em;cCq@K^`e?vr{LmFVoJ-j*Zs%+a@RL~_E?UiPQrhik?F6O=~ZPcetGyxXaP>r zb$r?C8xD8teCdkp6ifBj0$$NeOe1qyAVb8)j_tUX2;&_CvT*@|g{R1VWOgb;t!-Qp z8vDCP$0CBO%Z9uo!xhc`{s^XR!pdG#pN66gTM|K}hbtN5Zi4uD*D0Lo?HUWgJElw| zI2V$Rj)5IUbIWCkjNnYQH%sZjdWNzH|AR_j1asVrjydVTkV+=Bgcc5OMYHS3Us4Q} z#02DN*=cAf_P4DL8x5_fw5oTbI1i1i3q8BA`_&>FFG;GMP~gZNS}eL$(ihb66@QBNM4>W= zzkesdPnB&??S*4*7{l^O1u%CFs>9*nrjNl}QQ)x`LUoCdXRVYO5^F8UUj>aVg9r=~ zWWf4|u$>W!=pdRwI$UrG!ld>2jFp|!PArRbS1X(LxJwWCBI%Kp2Yk|t73qD|3yM;j z-DwE+D&xgtK+do*L2p~R9VbhMVylNe!bNK&{;_DH}Z`IPmfLF@_5fGU<9f? zXsfs3WT3Ljt=ZQPhvH=SA+BWq_susF#Eu8d#QkQI@UY|I2~XJFGIpTWF5?_VXuN9{ z+W{#B__p%+$F{sZJj^_g7bJDl*kj!IRusoYz5HViH20?7+;{y!S$_5!4m1N12i{7> z1E}REgtV$3-e0aXdO_)Ib|lOF$oB-bHg^Z$nd!a3B=C9+nT2qhZ@ehiX~DjeH5R(Z z%0@5g7GWr&m6?|9WE|{Rqq;5Xj5>j+xzoVCgL2~gi-9WgTo5A)gzq}B~ z)WT`mf>0}!XVA0F+)r;-Hj|CQtG=0>jVQt)p5u>{6!&Ka8=h~@F9Q)i@nIA!tJ_GUAo`1v#N4LCgV{IgR{hDUOg`!Xq?S^s-~5T(1t zulcu5S-o~xi!9%>s_YGxfG_>75(3h!`&su#-pY@cp1m=;g@iUM2w#S4gJ?GPSw$^~ zUun_UuE=`#6he+9>?AuUipYfJdEl%u=+b~!c3xg4e3^Cwd*!Y;))4D}mS z^&SGUGpF0-4?ZDJ^brRfhYcVfY0kczTxwe;*Q?by2L!p$KGRTYi{O(|kkCV@*Kd@D zT8j@#Zl;(UvYrR;EZ1Lmss{&8Fr*np6xIC0@3g3DE&Ah>di2{9CGJU2(^GzMYb-Sw zC>cAKzs26O5xg^kQ-s&gy(_Pn*(TlntnlgNbO)F-w8M>7{O--T^f_xW?NEfjFBsgA zDt0&{R6r8WVF~4ShB!DGC(0y4FT{%U zcyJLsZWu&O2?`BI&>$NK^?qT&;)z)x8b4Q!Jzh&6o)H;U9Aa2Tb&5U9sTcsy3u{y= zG9Nq(o2)O)4V7KC$Pd$5U$1D%aBZJ`Rb zu34?Hfg1XkA7t_uHfXcP?9x$i&hAM05_X#HS+Dczgx742NMCcg`xgiaCpdZH z_Nzpk0c9zqd~Jv!jE%VIRBJE-PSMLesDJm|JfxZt>JI8`c5<26TbYOc=ZKrLx@|>E zW1mi{3>lg`CXUxKnY_J*{C+M^a==LY-}|!zHoN3{@}>z-*8me6q2Ep5HB@C2wAV6k zu*t;sa9e)%_1BMn72Orf3{_Ds%|xC!PXtI1dD8V+y_?>R9rJkPZ$UIZXbl)uiCntu z)3jXb-Um>DwVAlyHodrnNrIUbHmB=(op$BK0^F>$FC5V-ow!Qjm(E#d1_F+*2Z@X= z6i_=j^4~sq6DxedL%**F5>7rT!T`8kCMP@zC@~ky9$t2{yIgHMaA9qLkA>QwBiukI zTXG5`rEAwjL2v(c43zjkW;_8Ptdd7V>?&T0_i@pj&uYoR(!=JmfrFrGK>P`^fNmff z0sWpNQL3;Pf?P(!1@f;GSP$%2`;d(0%=`VX2#Zb(6!w(PLc2E*lALd*n4bQ>+Et)Fe(H`3)jKldgK}?&I97H z7R23D=B$V^yr)XPVQEbq@tz?*9*Twr|Ep`fv4vwtBSu*jI^iR7WT2yy0#+^@f|M^@ z0|>XIzh(TEap?iZ$=CEHBYt5WF=R>FJ0TF|MsduH`dW|Ou}|kN>!FlE6WCO~LaXf3 zgef`fZcEX3$ez%5Phcg5D|-edExhM|8Mfx5O9}0mCo+=m2bN!t?V792r%2-|y%-jZ z#O+akrtbGNm&uGgiJ(m>OLary+{L6_{^`QM79*ahhYw z#m^j990r0>wFW;8^!iNdWWV%K%ZEL~ytUfh;oel^@4yF4rh5~gIw*hcIs_vB+@bV$ zW$M07E*bu^M=y@M(tTx)?u={WJ!O-^cmk}?tlDc2c}Rp?+3y6f@<>)Azt9Hs!g4ds zbs?^j1U(w*dlV0cPZO>aqwKpJ%S06193OMd$H2I)BQ*2Ih%EX?C8v^qd-;@m@eB+S z^{~9Q`C@%+U3ODw6_~rx_tOIeve15#McEvf>mhKXh`R5t{Zc#Wq0PiWKiNK?RH{VonLuD_IwIBw}V~F$b}d z7#JOB$%_?HkW4#S1>0qc&VidS|8T=0#I%MHu?o)3uEBB%)JtN3VyFpgRqODp3|X+K z=nzRN{qy_(-(g6*V z-DfUwr>VCA4Oxe+K(SK6OTXS9qzt?gRVc22{LaLhE;xs!zJ5nMDcBbKC#j%C2ky;u zWapZ?L{^Yj(yCkvB^*SYLX*NFX3l5Wi#TIoS{$sBWi03sN#|$Iek0d}oVxF1d2-){ zsw3tgu!0AAC~@u??0Q0KT}^9*y8M~fdgR=lbQ_LmOT5mR_O=_i7$=ohm6EkT1NGI$Y4!5>RHF?_OIZs^2=x8WvtbUMl3O61UY6YZOSQNciPe;iYW8BuXSSr6BHp3kO8;g zp|>45H&ZjZq9Jzx>X3=5dnp8p>wlFo2hiuU^I;TmrR^;RI@Wy*BTwC(Uy|G*Lord) z0|pWT@xs?^`s-V!VXJWYri}e4fpimIA*Yf4g$pnZ+^C*Q0|(xBdpQ}1CQGulx_ z(9m8#HzmfT>FuU)J3V+2%M@SGG+FHoN261uY9Gwelr7<1v1JKPJkS6$BM@VXQc8@B zUn$i~2-_HBIyc%0BWcw%6>Jv37ZW!dv&4~4eB>@M- zsHCmvJCssXxq+;ntymr0g9lT{UEzt90Atb&AxVw*PonttPp7vABN%=cj5k z@=xz$j|xuxE)@q%GU}#Jx}Eg$QuhBcQQGuTXy5f0chUDqmGB9e*)lRKQfpR*|6x@I zocP^w+xF78haQ5_^^N3qj5vQ6YhT~Eqy=nu>=Nl=@3(b-ep$O;Q!(_z+3&DFw#k8G z@bQM@arp+=QCvV^Mdf@&zxY;@+`Nna=WBc3TZkzY-4$I zHr9$<$nGkrw(lh=_0Jip^<7RW?H5Lba7uZMQvTBI5IMBo9}7hy{|c?WDI!R>p#tWD zoIM!L-C7q(|2dHbhN)VXbR{JN6c-Z6`7^=1sPHh~LG{6COtIm?GjRf+-;eFTZ|{v& zdMXQzA!Cpj2X1@AoeM)Lh@#i!O1e<~Eow0gk|v~+f#yw_3M>C1Zp}x=BO|j1)Dzv^ zyMsaq=jrIhNl{TCZuYhEPmv(W*ChGtGLV)0-E;^b`1O2UymqY=9mx=f$&^C?5!Ck( zu+5PpPbL#$6U(111C5y&}U9x)!8zswqsy-sNv zY81L7wkz>7$W8}KTIfO81pX^Yi{Z^Hj+lrj1fx4-EIpFe0vOht!l@BKn@rYy)oE@m zJ(;+;{hBl&JoMr9c9ANZ(*d|dHiv%>5HqT)7a9HbBAH}px1Hzx=hY0kZtpmumROI_ z*6^ZnEW0|@@OzktvSdb0VvenHH<;CI50yR-HZ?lkGLsAuU)6S6Ou_@r(rUti*pK-8vKsfudujf2c9)AJwr`x;BDj$yr5WyH$=Q#6 z-yW3fsl&q>R42-w#B%Cn+^wJ{>ypN$ByndJ?BXu3IyXC{$jH~X1%YvF2E0$)V(Z`T zlkqHap-?mW7{3OlO{DBwvmyp>Bz^vLU0GIn6F424?H%zv=2_R>XOO7MmOh>v2(G+% z*OlnC5%2`uqxWF>)hHDV%alC7vQt-b&sOlM;A%!)VmXNeDS$Vqh@iajpIsulfRaz0 zY$a{EXels$2%y2+eUQm4Kr_;^f|o6zGwI<2NGyU*!&HVu)OnqF(Q^?I&ifW(vOk7Q zOnO12N>eAbKv5zj%2!tqZako($_A(2-e70aS@h5EL)e?0C?w5Hh%@5;ChUw0o?Yj# zr>0IwlyuY}(2@XVxi~~-ctd|YUO+9UKw2bj76xzf^hOxmH>C9@=UoklowyVx9dum{ z2~uZv2GQmz;@L)p`nkxxR%BjliR^fD1)?t`nfnU7d;?rSjV9i#mD$&M*67Ul8ukaf z)kuL8z}uBy`&za|JbnM4V@{ah)r9^K)o z4#NZ&I?oX7nbam|?jKm8MR}FLoX53ey1z~Jy6ZC-$Rnd>uT~CiBeQc|AE7@Z(L%PF zr7!>K{{yVUiymoq5})Xmr!cCC?Uei!@u^4Tb!V7;7Bcky+g9I$sqlBwZ12sWaw0{_ z65A{^DZxy{uQ~>tf=5+3ueCW*H&_FrdTOgK=Zs<3yIuR2BcoiO!RGR-rdp3Kry|49 zN4b>Szl{tHhQk%?n!~FxO=PcI6U=q)cR&7dB?80Z!FHVpXM&E!c7>qt9)^A-NA^;7 znhdtZ4*Aep5R0IG(3U^ib9a!=RDY4AiD9P@MSeQUd*d*Ts_>^7;hphqhnk)r#LL?e zkO<&~Y$U0^y*@X>i&F8v1`wxVxz!`}!ZpTV~>OB3g!rF{i8L>tz=; zMZyGWuF_S>yRGLL>vN@S^%#lPE|)MHIxcGKl)? zL3Z$2MA>M6>fuM)r!e=2IsERD1@_d|)`nlpiM8=upVXHZ32i`b4zwkJ$=07a+tei1 z(|f{JY~M_07Wva9;>DD-?+ud83|T%tUl=Z}`y`QtJd!u3AK~`p1@yqK3d`I^Wsmg( z4bpFlE8Q*vd4s+0`4QG?5xhm04e z?1fw(nnj3FC)kU2&agK8qO!_nzG^rT<@nzuf zQiO=0SnJ;epQKR%u|}* z*wQp7??6??vn{4bG4Gz_dj(33&a@lVC%$)9g0T_2yrgQBGC%{KOmMVZc}=5yEomp( zJ=3wK0`9%o5+;$35E7wKq(AO(6CQrdJSj7y9&lRUCXzcz+{Hfe)*qYx{Kk$b+@%aQ zWMjzpruuVHKQT^13{o=BmHAd;d?BAJn6|;1CUPjmHm>x|yeZo)@LkWaq%Yd_9Q4Q# zWa+C9q&}ru6^_-L(T|6KFRHJw4FhT~04O4}H^q_lY8-{daRuHNb+cpTZ>{VjMl_Sl zQJt`OEE3(K1^eu;+cORth9%~aDfQN0Be_UlTeLFATs~x;Z0yp7sLZQv0B%4{pK(#A zXKdSMl9!w7GewVY8mAkNE9-e(xZ`uIw1eGmQ*tlKl7v2?hSH~1% z$C&L*C;4-d`2}JlqSPcXs^4~u-y{u~agkm(PIPv2P_Ju!Rpyy((WY~@y+o4;JCyFB zrmg?welux|J0my)wZB@CeyQOHbGZHMBmD&#aI<(r+t=^n#NY?6Hkr=km}=yziO)u- z8(4M{$pDDLZm5NfYH%G8?m5nO?mCgaQM>t&e=%3FqjzYK+Tj4M)akF^G@>?hftUP~ zfH|CGQR~_9K}+)UI*iMM)Pk;0k3m{#()FSEqOzqGG?wHIjenlG_O!uhG>`p4m9ICu zBFnvnex09tO5CjmAO0eV?bym6*tz9?)T<$FWZiowcYD=hidQeYF0cKV{Na7(*S~wh zyOsMq>(j2d^Z{7C{(zDHS)grBaa)`ojurZWz6+JYeDpZNzwt#&`eOq181KKo-eeUF zt=B`UIzu7nDz_i%>1JbN11fl}k@T0T8;gq9m;Jiy#Q@o#p_N_59vVB+je`rxKw4l_ z2etsuD72a#cF)1VT%0Pbr~oYQsjfD-xzC*piVY&be=WMYRaP7K1)n_FFWPL~1~S6z zs=w^(BYT~cx5^hMzt2Pq$k#CsWk9Q4J`Kqqy8u{)<;%N9BVwjB2DUyXAm^^GvfR<~ zyZ^RIF7>aB3PeR0S|3E&nWgP6w0HU-rsapb2_uJtncKq*^}`qtr^kf9aVNZ%EbM9=rE8p# zOgo`TnUGIuOAHB2_*{h3bz`;E;g>`gfXEMAh_3bS2X%RUu4#wX*Y47Dqnj#%F7Zs= z&g~e%F8N0Kf#x&|1(wuP$W*35S%oQ9196tpHrVc4lGK%AY|q+OJ$R5QW_wTD&;8uQW+damMKXi2l103@8ECB9S4fGO@VXk@$9WDGym*N?r$V#8{m*1 zRG7IIv&YcL+kWY@n?}i6+sX2G);iQDoSEF(FUf$Zk0RNq>B`JLlZYc0z{j$w`RyOP<9wuA|a96#!$=; z7rScXOvPG{(+CBy_{D3L=^4yP)$Q0Dk~B&=%nSpR(VG9aRg1xy(xlHQ`kU9PH$6gS z;$T7$g@T52jy)NoZR&<+T31%Ly&L^O(r6ToYDH(Z_&0jaxnw{OK~(3Wx^DX`S01AA z8&{T|E_iH(hAWb41&63E*V_L4NOSuVZQPvYvQwGIZ&!Ns-(F=-B=yI^UQtl^aE%OSQ(y0e z-CU8!UUb^0^EI~Tm7D2R`>s>dI-pwgkuH=StaTx8rr* zvY~C;rl!Yala=dOkJAL*V%kFcoFRmH>H)`$zZO}`84-MM18fp)T8t`Wd z?Z1t zBBBpTjM%XR|01%C9_Oi`ME(j&SV(zXyNHM{8^{L|UpcA0hZS}LoteQ>c4>BbJd3IV zZk{H46V`~G{VoL0jFDPVII=`ckJ^B?MxU~A_lRer|fH}x~nwrE(r znxm(8)jhSTjdFssZ6!e)RI?J?E&4|M)DUA1d!L`u>EI~Eryv~ab-HVN2==;kH~_#O zNmk7Eqq*InWf^b=+=6>RB28PTEvA%Gj8CDPfi;L5W9q9EN~m?=^@nhE6`o+q-ukj@ z;!Rs#;yoH1r80s=`I!$B4{q|us&L&@kdqS}N1{37k3BqgXk#tt0@Kwn`isu!qzK~y z4*&R{uudMKmu0Q^IM#z2uAdkaqX*hJR^%D<)%q%P0;{9Wk-KnL^f)vd`3jy?#88I=XWA_= zp#rG)MkqR`thXCg)VW(oFChtV4yPZ8>ftlJ?!eX66qLS}dv4YJgV!aL-~E?JGyZx6 zBB@+E%N;o=^e1XbA%A|?)JSz|-MN-Fw)@o3XkkI_3t^}+oeO!;g)Inq&^h~9vOZBf zIldp;2`s=gPLYw_jE&mikou`EzZ$WMy47w=1Kc!epm){Lew#G!dco98{V}6$58W=j z*bnq+a98m4MBse`s=)u=ubEZhj{JR-m+x}3WB8E2vw}g}@(L37CfRzX=$;y-ph|6) zN~YSRqaHj~9{!R-+wuDPrhz-)Xy(i6Czh(-l>LY=!cQFlBm0?E2HISG)*nmf#^hCv z6>1~u9?hbMW#q#bR7RYYo_iqK%RGc!8vwL_Q4(J#vbQP5jG0P|?O~LCR`PIgtTy}8 zZ(}b9mLLsT5Knpur;F-Ev3KSN&IQbjIYs3j;p2;P`O=Wd;=F*@m~hjxPoFobSn4-6 z8=Ld<9B!8DH$ic$B1gjpyBV-M>xnwzr2I%s%D;`Bff$@G=y>)QNl#S1i2GRGRXH6ikOPn}nI15%9@VHi=#W_+JvGCRR!w=cYp;p7y50;w z^(^}QpkZKKW>q{F13f`gHF?2o>Qp=5&NX3i%XX(Oqr90Yuf&+HzENC@Fu&0hLQeZD zIU=HJ2~22AxO0Cr9dl}zrrwSzG$U3mfobXv$^7>_==Mr8IOzC9low|2TN`?T_XTj zE%-TzA1DNPR(&UYO->q)Ps=w$T0LN8pxsZHKh3rVE^)=aT5S}t;8T8)!nhqIY%2_QC55|>;wH zm4yC?1Yt%=iS*~?lO2RHpM{~TCPdE=$pj`yN)HU0gEp*5u-0mlWEqr8<L~<|D@u)u5T1<|`O18hIUPvPY}JPR7*BiObKl__5UQn0ft8 zThoE3bY`MULQFmNELVhDY~WTdn5WuDzjvi>U@@v=FcUJODrFxoPZ7A%MZg8Pn$zax z08Fci8Eq(;F=qCJyloRt3<1LPFe}1{@a^*%pQ_nwPb`~xgp#yGtPCuZ*F}k9`^)HS zfw)rMQI@fHD6Yz7+3T(NFN}PeVYK4g3{gf}zLWJku$rcS{w}7AdGN*!p6XZW^$&$X zq62WqYT?&18kE5n(*0aoO?fQcSPC*?tMsKs@{$ECV^0!5dCcvGt1l|uyK6D(X6>4Z zU!V2x9HWqb{requ|JkDIFNpDZki_VnyMFmuw`P<+@-zgljha9`vHcS}|LUTsV3~A&c{meLXCpq zgYs99+I62MC;4KjQb}TS)0(t^5gO$=rU?e9Kgx-%ZO-8w0i+2MiTYVs7R&DxrJmTZ zrr5tL`<7S563B>D*Nn_%GH?RhWCwU4>2h!gXzc!1l!c; zAGl+iorb2f%n-Y~@?`$Zg`Y3dzQn!7xTe4swBn)2#R9i_3EldpyA0bdcsJ4oV9ApEc(vCPa#d6bA(b zP6X|aSP_^&CwS-G2+98)(|gEC+5(%03B~Uh|3$03hRSfrgBRvhTvptA_S8tw(r(bu zs!z|wu;h$tgY##AYZ-h(140@tv`zCkL0v2)Z2WH~TA_5v`1~;S#~o9iM7!JYUc@ue z>}(v{47&4>8xyc#oWc&KXfkyBfeoZ=iG%ThE~N@crr5H8(;V!O&_D*F*Zw%I+~Kpx zS{|W)J}67B$(cc@t!ut>T|V)FF?0RKz$Mpn{uv#uSz&}ECrp~?P*i~XVzoJ;&{Bfy z1lI9R&eFa)#jkeTDRkTn?ig!;@wpAAj4ogEqjlhxxS(c!A40(&W&F6)CmIE+g$t}R z`O&^a!?>IY_^c$-_*aS~=20aiw61uQeVRqNy<|(%VBv&dt)hwwDu6%9(5*ukyhzMo zo3x;Wq8f`zLVkXx36=$~*k)@NsYJ)rcCS*=^0*a^vofk=Sz8Eu10TbbH+5FYFAc5} zf@X!OEub(ph#hH)%AR6u4vv_{v_6zcI?xFRv3FhJry7W>YRX+9M#uHH3 za-@NrnZ9^JEVzcp)jY9AAM|SyIB&?gz$t<@q%hw{(0S4;$Nzs>B~}FN3oxdB>d;i> zD4@4lm~tQ-vTPt{&fF4dodv}n(07KLYTd6Wsj<>K(UryUniTn9j9_rl_~va>uiMNY z>wSRCuyX}QdfD7VwHUTOptU#F^FEp)ktOsZsIwBTmB}HD;KfOe>MRnofn&^~8Dg{y zoazSWj`@d#N~B~E%-Wz@0PP4^$eEv*c=B}QskVv9^$4;H3A2YN4Dp0#7ETo638F5w zntE>kuv(dwZGE34H=Y6Ay!VKBWC0FF0eGBa(=lFlhk*uMj!rt6DgH*DpVMDAf zaz5A`a9muz0Nfmro$s z+)m&jYYox#h7#o|KGD&;h+k>X>{4=OYb}_Jv#kj$(K7+1X1ICHM2FExEDdRMY|S{K zsLYf;&U85jgJMo-oSA#5%O>_IZYZ+eFNlt1JKQhJE%%x67peaa?0-AI>e&APIc?ZP zMX4jpruC#72yoMo_w~9oigs`dmOG4XgvJLS{kO@FN4kJ*a@!YLNM}Y7u0G$(n__v; zmcOYcxYP|+E7lj(3il5oLkoJ{FjV?RcDS2#zDFqm4#M^nKDXv7hFmXtq*7fb1=bwN z4B=7v6toH=W+IM4{TX6W`V<^1N)i)YSyM1H-4F|YXkVJSy<+7tLk)Z{<)obW<*7Gt z(3n6zi=0t1Ds*%>B3U`DqDWDlB?fm`FG@tWf(IIr^nP;;-a;8uoVj0*O|@oM^^}fnVgle ziS&z`$diF{UJI?wjK+$#54zSHHSN{@70R*Ck;@8teG(C6YE|Lsmgk~WjnTC7u;uA z11cKuKM83c5Oy0;X*dnH4x?BQY>GG}otfuJwa5f3% zh_d`?P>Y=|&zQt@OQRkDfeA)C`)`F{YTUOZy4Y{Lrf^Kiq?7)Tn|C&WbUfg(D*>uNCNV*7#0spIJyH8I z5zYh>0Rs!Y(!f2(UowYG#~0E;R>bd~4wZJ#C>9S|K`c%nG;1}EGJ)kHXtVFI_F%%E zm`|DAyM|UnbBrz3ZAJEIDSdrq=egmkCv!N>Kwj+XsOrr6Ei&AncG;%3pNu_0egITk%qE6rh z{{R8_PqEKsN~5|}m%nBJ8Q|gatn~KXFV1vxSGdCfk+WI#$U6nEzpw{<#zjBS z2J;mMfixqH=}Jyu6HCOFG9QIH;A8yhCo*nmyw#&HIzbnY*P#UD$G-g+N=bSQY1se5 z0x$G`U7%)n(QbXsU#DZtNiiA|^C~aq2WWExFw%fGpq>k#WFP;jpg@!KsIIYp;?sn8 zj=DvXwRx+;6|X<7cQVU{Nxnwl&ii-E(rC>z0UA%GO))h%Q8p(~n~IbhJTWkE1}13& zZU~3JwkwMo)oVVTKrLESKU`Q}mLcE2n2ZxVk>*mZqP~arfT5t*myf9_6IEmawG}g* zejIhkHHuclxi)MDyF>#PWCn&5F@L32aNcMf{VY-=--y!dK>8AiRD({l)a1^cB~>93 z(56TwGlNMHL2V>WN18#(%Sjznz83BeAdNUQi>1z4uoTm~z|IC34)&qJ=keNVH~fm} zMmPO9N)Qgv%>IRs+|e{_nvSV2H-&~~h@OcjOvDXt)*hX4`rCo}&#;~IsS@yPy@l}c zH2<1eZ-k!fynaYm3d<0c`Uus&10G9aohegku{!oRCVbja?_7@qff~DZ(n=*!5`bDKZEt>ZMA`-b_oF&BE9O3@!2s#*O z#Ho1XnWnokvyUN`I`tN*#f-`);`!r}zpC9KDoMex++e}Pu<=DjOWz1p*b*zj!IG@v zh%K-M7>0bC?_5gZqdxgKwWk|EolGN5@~Qt#aRK?7;vyBjn82Jxy@D%pFhXY{pu!>J z8nMutvBuMilLZ1U13F9(JUYL1OewX)q6SMX^)*Uk$P#Fol`(_#{t2{)Atp<1=PtmL-b3V8K287Nf0=idAv4)ComwT6OKF}w%2`lmPokPc zH@Y-3uFemjFCC1Yrx1+pnoa6uWpO1QnqTV#jLdwCC1qF|5iE`18Of_xYfz;tz{@f% z<{Wivw{sj1c(2E?d+|RhOUpRRv7(kKW>!GD0m^bls`v01qUDd!x}x$sm-`{W;go;p za2k6aGp;?*zFV6yX2~Pwd1g6J_tsS)$&4)bXFKG(c$4fuTMVv)#}_M3E<_&C?o7Qt zv<9TK*l;7nZI$XTSN)s%ho(i*vYJ2a4?O@h9RIBy{6}}#PJ$FicF!pJ^0D%1O=Sd) zfae)g0DsW(Rlb0@kU4i?^FxDY%kHhIoCFjQtIZEHw!Wp!reB$1oAzKzXb!Y z5pO?!K#0#Oro(qa-)Pe;6L|1>fd|PKP%CB~GxR(mftD_Ybm*8fT8vUgyVsWAs@%8c zkY0hnnISrM2ZO9|F(jXf-d$>{hq)3?S@)ge2F(eLX|9z5L>Zu^cxX0#`$rHzsAD9> zAyJg)hzkQ%sDpl$dv>*>x2M$OCKt*l2SoayTIz;mmkt6%nc?K5mD@p~P(!JTL}PxP z$~cb~CbBZaMV0poo+z@l3*HI=G7Dtng(aO=WV_G1=46q3`pyI0uPuRqRg$igDwgjtPdSD zic7f^wVPyQoPp1_mHs(?!!BpH!mU|?LPPBfUqB@r zQPX;2PabZ}X?dgNP9#<)%JFXW0)($OLdoIwHc&UmPow%s7)9MW+rBc=9H@_o|Hsfz z3VZ|fA9w+Nir)a$!u&ELB$rBnyD)-eew=)^CY1F8aWhe9Ct6-zVa1K|R=0Y*8_n z&K{EGW|T0MibWjK+9)PWVkD3XA-=ii$HnKEUP@&X%H&6pm!vk|pkj2P9DBD3f_5TiOfa;OTamPMO*ryFDo#J+|2 zTy?SgJ}XaIII))W@*A#UoE92thNPWqE2EjD5pM?^&P*UnB^t&Y(vO5g;w`Z{u}5QW zX`)Co8^7bXyBinwDU+d?SF+EIDQ+i*OqX+~sa%_D8UrcfU9mvy+MI>-Er60hRSBZ0 zZ2j$T|E5??@j=7UH3S_-JmIRE$5Iy9EEICt-7Ql#j3cq z%Mz@U!#Qd@@Ss7tovb!u?{WfyaC=95(n6ZI8pSab=dy4}q8z|EVw{&~jEXGkScKGK zgxuiXk{g(0FkUMke#-8;LVQsd*cQdA9al7b!TQ__51k?nNg=$206}|3cYJH8w6_5p z{sh-v+8vZys9WOyeUkFOsAU@AKWb@`J!<2Nusp9lWD5QnpwP|;++|kQFK;u)Q?%zc zHNv=THyBJm(#=hB+y$11TJClYEPV-9p=e>5eIQ6 zFZST5S#<1fVIonKX@Eufv#^lDq*nAxf;C`zjkxoYf|g3T?=;!M?G1}uZhkCLf@Z0h zdM$o*MkxxM^4~W{qSBruv4(0ibaXXTG5b5P1I*`jTaPBHRC~fGINz?i0+~|G##x@C zioEFF(BkS158k)+~*-Mj9p<-*P&V-I{AMWmi1E`FKXWj7)+Ex4RJgo3=qL@`a21=P5fH>}L&EJ+}< z3)jDUaF$h5$;aT3z8xq;J`8DCEIM7sdyLQC8tsODk!imKXe4|2XV~t}Yyd-Ma3R*i z*XOrHCt*>ynEq}zvlU(#^Cgrhs$1%PQi!D2(WTO8EkO$}#fyVGa~ORYz@ zpf@UjipV;M4o$zRVw!?PH~2C zA(6kwDFBWvAnE%%lIVPT;J|#<->=i7wMqdrVFl(qBarO0jM{@l_b*YSUOT~*Tc>4v z0UYQ3ivgM)LmDWtinGcfe8^Q0(3ZP< zET4o4{@R@&U??}+WVT~V9hXF?6<~|q?EA%VeEX*)?=^Z^zjRkDx)H$o8&|;o$W!IW z;$^_9Zb!O+`;Tw|iFjewF_v5rRU>!~%DTSZJ2Ezf9P=7T`jL2S&9YHuI;QR7zG4*+ z2gaR-Qxg)MgiU(5f_SRPf5Oin34t%!O|ImAR6LWfgIB3kQ9`N;T>J&743s#=}5aa|fB4_h*;= zOdt>unmIim8k4xwx&qUnv_hI?Y)8iueZUeF&y!#cjR9H(L)j8PCNd@91`K?b<*Kkx zs6>VYiKwZ7_{{XRJl7{+9WIAc1*>Q--OR%XpKoygq0Jtq>tz;_W5%V=RwKO*V=|b^ zvavY%6%vlsB49ZxJ8GCQgI85`SkIV+b+@2VoQihDzT2K^%2%)>$sbKd%6xVUenw0id)uz{e+zudT-$-yg%HcCcL0QSLo&0u=Q+1e#LU&PZU_ zhtvTM4URj}o!L(3`_GRh`Ux4}WCb{>25Uc-$~n}_O0IrQRV6uw7AE&Xe2yv1{G4&} zCxE;{ywec~GY8z-BDYuwo&Y^wfxlfUZlwH_`!!!^nqRrjQmEDD7s!S3#m2?ZpmHK8 zvde!5k$^J9cv5?`GVO!?n$<(0`ixKeEm~6-m7egOI%)A#rK4M7yYsqle|T+hHrCtF z?$vamuzSo((g$EBKRPGZ; zYLL7TzH!33SR&xWiAW^HQ|3}*SjJ$aCImW!SQl6>8a07k+-XLGMwK-SO5*eyN=+0! zmuw7|wej`aWhdB$VmK)>Fr7XbuKpiw0`!$>0?w@)U- z@`Bq>EIZ=0j2%NgpPDjU5A=v+Oy z@}R&Ke&5`MZ#V9=CnYx2lEPAuUW^Eno2?@E;;|6aDwi^Cxy3u|t_pV6xY_}odBRQM z5%U%l69SSVk`)f>pX~!1fuVPVvIK*uqg^;|z=euJgStbJ=FsX1Jg3k8N+y|HO9`(O zK$W2Cox1AVlV+NyEj-ZEej4Zj2gg}X?J{$)uO$MeJxY^I8)Mkde;`X0{kLa=zfTdc z)W(N1-4c(F8&tThXm_GXBKh`iSzm@gX5?nk=5A5A|6*P7X%84hFG=L{)E zF0|APY|szL7`)ozy{Gc~r!$Nvn2m(DskS$M-}s;jY#5Ko^1pC9D+FP4OYvKix*{}fT+_@l^D}Gqtr6`^ z$Yna%=S-2l_fkIdPV_!tF5>fzqh?7gHONd)YzT5_!QhyqiabUPQ|s?^hI?~LwT?@Q z3PHS|DH)S~b1VwXN~lpV(QGV|nRQ|kAsHVO?(=f}JE3u36knES-bT(s9CGxiv_?y0 z#1L8YNM>WfDloNp-FUrm2QNyZO)Ol5G#Iw2Nnk`;qUQz@vBcKxgkcY@IbNx1S+Ku< zM-Q!xq727OLk3leHl_+%3I?%|4wcKIZI_+HBVk{C^QfSi9WXJqkW%KYs0OQc}LxgCU3P{_FkOt_D9nMIoDoBXc-`lA^Y^e|V zBXS#Egvgva6ka=|&dUvmEv{tJ4bXW)$Iw@6^TgW3m4{12(Db1d9?^FUlbeIi`-OL9 zTImYLIiV{%v&7squ1hj=wsrxP22eUdpJN~PccopE{de7SE9R$3ZtHKtHg_|;bcLZb zCSok=MGv7eA=P+y*{FR1jgD;RxIxSThJAaJx_=bhRaKuP3#_B z{N1TWgFk94YRkLe(E^2j3ND>8opdGmTw!&)xA+Y#oxF_rPV1J!mc5v@6J^s&cd94! z$rckRc~iKr@`_8%9*^kRrOH?VoM(wIG+!M_?FB|c+RxKOt!vIsGIQF0fVeMt@IU!$ zc|yW*A$9vqw4*7TLTC+p%o*?SpE53L9P~QMns@FhE@YK#6<^EimAW${jElGh=N->_ z&NZTLsc>zcwB3{SgiuQ5ngEkyks|~WS&9y|hu|h}6`?wBNT}SqZnB%<1o~i^W>sCX#A!l+yOFOks4+uYGDaqXc7J`Hf$vOItC*)p|40pVCdv_1Qik4=;0(Rp2XC>F?t7DvwwH530ZgJ;B# z8hjKaP*7$#OF-A{LDs}MNUSnMi%kgRT&_hX*fr0QWVA82bR%}7;7qEe%184^15i^u z(kN$jBx%*`A**sry1z^?NX^O{lN*K2lr`KxNa=;Wq~m`vpA@RyajENnYq`Ti<1y~T zGWkj6h*DyRw`33Ku&ZSC2trdy76-{DtFc(&W57{J8$Qxw+_`pC%}t0=#;8sS^wF7M z=F`8yy4-r4+)WrB_$^G(YXnh()I1_;0n*u+QS8yqw#k^<^rfZ!5UF?e@=A`qeJn$a z@728_2~~vf7I(E@>I+D=L%Zi&39r%zi}xwDjA%U$8x9`Pn_+U;$#Ecde)!tG;&v>*xO38)Eu#wkQ-=_d) z!_GRe@8@LQPHvqy#C-qm(Z^`+R+QJ(D|aZ2CU47e)XUU;!JB%OCmpO4pB}(|vr4P4 z(hPsV?dltTSc9MDzx}y@XW$?Drgn$6t|+Cu;G#nF;apo1*rSJcwQN9u{W+f%S@1iF z_R{#Ib(ioJaWRFcGDx5;wv-9g`s2&P{1Po4amS2s8^5 z;L8?clFYP&5uM0Mc=t_Tz)eSv4kJ*v6R*ZQK9o#zn`4joS0jCBB<~F%?156s6u5z9 z4qFtt0*<`_+0UVnBN91jIiLb3CQuM_AEBi56BU{k;}RJax_ufFA$5$6CM0yty(Z1V}K7{Y4E1etfoaBNI_rhPdk2yt|bG<2COkhr%xNz()42Vsevx9H)-Y|NAH&2 zX053Mq5>?dl2S8;8rqRNa@(CZA59TlQ61R4C}TFG#@fL>qsI+4k`FB|Zn!^7otPjl z6Y_eqXK~fXl-S!tgcS`fp#`?S>V!=w&~$Ggo_d)=qS9D*_CVWN1t7kidDbmFoPJfz z=Fh>+(Ka(~BVXIF~S6A(289G!j*QV{P3*1FXJc~=Ek0QgF_lf+)q9f z@wvHOzWyMOJkO6E#km%xWMt&|xC6^?be_U#s6g*2_RbY4aX!ba+Fu#`s>^P5&i*=P zC^XxI_uj>MZ^NUgll~tq4+o;gTkUn1!vziZzf^xbIsnC^rlZD4)}eJP&^#adL+r`z zdzoL1Ho_OERgYeuK50}#ReFj9z7e;Ga=H(-3_!rPz(Hab*{HoRm;DRqgVbiQNpWj+ ze$}!05pgfo!Chp+8U;@O;yAO!Y2T4!jAm`u&752)SF{%pK%WDx$`dyq7eng9jxsPS zH3xk$GOwjIduCoy;r_`juyo?h*0@rxd75h(NHXIIw2C@7XKGOI6+liE?SG=2$vB?e z!z$&Za7?^tYz!(b+iuZ~(w*nsqz}n3)7&2EFzgtv5iSkq0Bg6@hKpujkV*5TrUL=( zB`;f6;XgMx#IY&{om~dZDer^l`OE8OQcph0juGI7aOHQy^r1g(n^@~|a(dFc z{Ct-`6?bq-VgG#v@4FlnyZOBT{SnRMxdqh14#PtA-1yNPG#-(+srO1A8$75sEU_(xzcsaL{#2w@WSDT`n=r(;dRy1`h-{2u163uzcS$S z_GUCtC!civ-$WFE^iThLgG@0uYaSMbEJTw-XC%glPqP)!)A@vb0KvE7K^piKlr%$( zy~LN+E7=*C+!l8v+wzRH^X#iKOhF!b(u`+HWe(objt`p&n@%{Z{F}~xy0upnsXpr} zrkZ^yS~CperHk+v24W!D+duj+W1T$(hSwj9@kC0v#Vkyx{1T}RM@F2QdZo>VlE!Gp zdgALs=+P~KBtyf{e2_xE=@OE5quJ6`i@=yq%YVRZztj6My=CiN>H_*siRl;@7x+NN za#pQ_BqY#g2XjU3Ko%viZx}&^AAT)`g$L)OS=3xO_Z_?~V*qr|R_N1TkqPKsY*Tq! zVbp1kGKi>hjP7+4$%d)G`$zbcF;9JvN*u96p}i>YZAsZ>`&XV}9_C$CjaJJ7mUE{y z`f{v{%ya)iy237wOFP0W^N%_n?mIVZR_#`bP;Y$y{>UR?`fxznd?Q}Nwt;cmeS8kk zRe%}%+E0d;de!|p<==2vr{C~AzwGU%U=(N8Oy`x$TnSr$1NnQ{Xoj%U5TJ4CL&g&m z_T&wn`b0L`?Cr11%aS{QxL|av0o*{ z`&ko)xq^jy)Mba+E$g<|{eCsn^}HE`^J2am*av~Aw#SyV(>F%+-#YI$2+zxjk=6@WMCU~GmXmVyWz^FZ}e_i@CPr#0ZU|^r0)3Uynxq0rnR9p zV;sQe*G@SjV&g5vt><@$9C6z1kmZMfCLqu7po*B#FD*TB8IZLtBdkkV1xb?#VhLws z5@5lIV(X_MrU6BhQNH_^QI7csc)WxWwbPT!h|4-vd)e_GC*oGtBon%w4R&kv1QNM3 z{F#$llb?;Sq3Ns((uOmFe+1SyZI%Q*v7LA6|0 z0M(Ivjx){f@-P!Q5eTn$c4t$2CuJ|#%aX#c4SC8zICA6#GO0x+0NGfeAem};Fu|-2$?P^FAHOsceuXAh7VhUSs)@kG1j%&iLP z!;JJ_AN0BR(0}TOIWFEx?7ato)j17_PPJcrcP)n=TtkQS)D3SbH0RAjJQ4v@mDtjH zV+oJqv$7Wc%iJLhb9^TA`xiWX;)ch^Y-6t)SdrAXjCiDDSw}P}qC_F5pvzFVVgBZ% ziSj;!;XAk~YfaeI2RypE*MYcqN`3<;H4VK*?yn}9E%D7hH8DNhus&_);4Mw~+|ax| z2tLn#5t&y?B$I0@0^@Fa1ajRq4x*Krw7F4W z@t*-d&;SQ=L~I+sD8SzNq_->NILUIjN4ze^G2dxS;8^?EA{GsuZXB{>*{0BDaxTTt z9#Mv5?TNN~vc!C0^a8>;MgG?@UF-z?$INxFa+fDx8I6>3-n`HwN(4@+SQ7#iPc>Z% z7!*n4N@7Bf0`XCu>GBejW;OQ&4s|dp!dueZBiY$mK1N&tA19QVV`SzTw|_K#O?CCT zG!YU5iGkDr%YqmYh7#wt<|KT5A1JxZVnb+-!jNzn3xP=qUSt}1EI4(*+Yy|cp28Ds zh*mK#=cg(6c~pPu{2Uor0L^qey+ z@=pr&QYh<5g{uXCKPlVK-*5}*EBcOEHa-ijKU?{Rwl7MPOWTT-6i7zTYy<_w>SkOY z@ld;sNq6K3n`WO^I$H2MhW5!DG4FK(|6$V$0u%yu;%+U@5B9zL4a++PdBe>0R%#SF zY^B6WWTwwxg1)>XIyQHYl_{t5;y{o6VjzI1as6!+em4SS;}9I9?Byna!I7Z*G0y!) z`~60O!+Pil#}=adcKT)OxjO{s3D8r@zl4!e_dg`@W9UIr_;EsgeS-WXO)Kj(;VtOw zAD879u_`u%`vOet>dYQ+ z8?-O{27Pr*>T*5<6hn#^S8)|nOz&JgKxL1~8s;;PNB0;{jt&hA;4K7Ya+Xc%i%}tW z-K>Q7WY*-Thm(xpiy*NPy?t#sL!fzwZU+9n=xzR|5m-p*H_>0@5{Vi3J2JQ5>3T5P z&6bu|0A@RiuX4ZRhD{Ld2H~IcEI@O>S3HLI59>hAnfN=4deLi0Re5#e!4g5hd`V=u zcyeY2umQ+dLgcn@U(u_7ppAF=R{)xbTWf(k+mDamHa5_KH>+AmM@~u8I*fC(%|H^L z>4$X^9(LWaNmw0*o00>{K1k>Lbjs76&w9(c<@^b15FX#v{k8hnoHos|H{|uZ-FNh^ z0;*A%um7~)Zr`$+{%Hr4SNYpz`+b%B#VBLxsmxoBKzCB}4Y>CcJU*$!H64Lj&%TJe zc|8Kh)9u;Gn$YEqB5xbQ%Ld-Lp7wI(7Ha72qnGP^uc^Cg)L6!UG-L6QAMAt*Ub)}v z^+ngC{E?Y5j!|=Rc#YL2>u*J`P9=`0RM~*Og)b1lj`IBagDL5ul5Yk0?~3Gs{6BF7 z5I&4wq(MtTC@+g)tU9e?17Yk0!$=`#Ls6gRnU`W67Wgbv`>ad)>2ES8{^H-c(a}3P?wCg%kb|35YADjBf$pOb0d-X5);mBypmNKzEgdPRHUI)op^ys$s$z zQJh-WGR3sa3`A4-eVWG6A>kqHyfsApB~zfdxEul~~bS9hN}|w7~H3 z%1R@Ag=i{FQe|RKpLzpywcPeYB))+2z7O(teGoo1ZEkt4P6i0SeRFX5H{LftC4C-U zy-$lCBiwfC@ZYgrm$=OIX}>G;`F#X zsJsqK(@DsFsTJ7~t~mYU=C1B4D4dj8Y|nA01Oq?q#dOmtj(fQk%6pxk*De-Em6%H% zkvF_Wvk7^v=OJSA!NpSo&Q|S)WKj)x?|?RJTwJ5uRx;`s~Pf+;y3?tB5>p{4-%*E4|0aP7~NtmkkPg1 z`coN&)%etD7?|l#S*Q|@!?&SYMrcHRQVdJKw*RE)O3*1nNC2ECDvj$ zVJqcA4@)C;3$sOF)n2KZzhz2NDqa*)UBLsg}pqhyes_MW^emeXmsGcuu`F%jfa0KPgD- zBeRWk0Z9^`H`|)A+9qGyI~DZaVZYC!T2B-H(6mT{r5J91xK;)Lddq%8`$|6A!xhOa zfaa+Ij0S*t#x@;66)sXk5a4JNe{~y~N!n`s-${c2Hw54`ZJNg`p&eogrP4D*sWbxv zvGmOO1_A%3*r-dglX1Xc}+Sq@1)khj7m% z=QZJ3p119%>NmS($e5p0?a#3W(VN*~-a#+{Be?c>Q9miz2=fGT2KTA+=3Ytzbjzts zFTfF|Z+i`}fszX&DV6Gvk2-g%)`{zjKTe)XDH%RjTCXMn?XH%rA;Jlup$9R}h;-l( z)o?&3e6*X^)XNC44Hbwa6MuP#v(S6Oek%4Lzk62TcSkaSk1HJigEk{-HuoLwV^gx@aBviz# z8B|fgv(i;TG<|NnynBRadFOSH7LU_=f4_9SDW=vlpQx`gX_B&UeJW83obj8mzz3Qz z8h!TR>RVk&EPa99@O`n?%V^BEdLa-V!nqnW!(v6zx{A0y7CxwR0U65{L?L$CbpdSM z;3(hQP32cm1^UuED9%T?`N*T^ENLu+rnV#XzL?5|#wzgtQ~TumuVKRjRG3^A zuB2fbQtL@M5?}8Q5SH}^Og)LR88elW_pzV%_G`BHW{3`&Tr=@`<7P2kA3b-#IWRT_ zX~VWEzbtVWY1)C+)#9d6hjog1nn_&#qqD^!d*Q*o!-_93PU$P_cLDPzaML&J#0AVb zQAA@K?Pw_$Xt)0f-Ij?E;o!3iaNA8+y4&sX+Z~3snNO~X-dGOp#LIoT+%T4gK>-g% zJM_N^4E^84ZYzd$%JymGL@_qG}6F;IQ~g(k$RKPT>{Do?jhO$#~Ey#CsVA4d%6`h$2Q?au!ooes%#=@7Z0U=~Xmv1tXXh1udIEMnbgD0* zR9eVQEs+FBHn@{lYUx3L1Y7YYYrJm5;_}3wDWO>5BFOm*jTkDyx4+ehsL7JD>DJUk z$30PdcvMRoGl#4CIFmI&Tmedg-Zg>l0*f=OKkDC(4udwR#P1ag>|tpm6%{G&|7W%2 zndE2wk8JApR0X=TY)?V6#oc%?db1SK`vPPlfhh5KXQ2k4mu(E6ED`C@8hd#-ZVz?* zdgC7IK~{}%;Qb6Mu_!Aej;d>|dAs>LjIyZOv+^c&^5PXn@ZO^$ zF%>BbVSY8a;4%^gE7d0Rp9v*A1#23NNty!Cx_)7nR71KDQIxtrGBHy zGib=^o-LR*d%wYkNr|JGnKMHje;7p`ulE&ci?X|wQ`= znI;d0N0!A+)zwS3bT8kj{@;u8e{FTQqWy?_3=T(*APb8bbxRN`ftUx7|!RnDlXXs!WVM~<~RvlBtD7<_`=Ofw{6NeUR)d(8AR-U6l zXvuLgZl(;MfSaPiE0~Z1bt}CD+o5dK zDPCrFKr>@y`kbo6N#S(&vOM^17hgqEyOch8hR5xGU~g?XA=)9d2*9?y`iYAA!-Bd1 z3{M6YF5CW`$(-3>UlL5@541{aXauMfQJ$#`KhBWKOH4_d;rIWej6W0o96#u((_15= z2gQc9(X8|1KzLph+FhBjKyC4Rb1V;_R3#eMd5TfE+)Z}8+GwB6>fC8Zk@ zw_K9Q=Jst;GgXwwERZbu{5bUf-GzjD!W97&2}~&5SNG>9^j-4OvS{g1iheac7oY7t?pUfBH==1|FRqK>#L zj!aNS%EmV4FIp+!us{tZ7|8ot?iyWmB5c#esf&5!5s04lai*Ypwc8&xyv`LE9#B!R z3NvZCtwiEWxv)Ci4rH|*`hLHBuU8=!BYWWe8duNlP5bgicuRBO{{x@Uf1~StDJAfA z?$cRJa^^@lcKRROMfHDe0jvMgOc*?zHBz4Fgt?i@DL86(U1_|KkcOI+5{Zm92tD*J zD<4j;Ah#*}2iz1|Q5neMx^>coFqH*QY!;mWP^cv;sA^=0B2dg#Qe>S-Xmd7d2H^dc zfQ`Weo0ttg0^P*tV9ECVQ->6GOg+=2ZcUVdNLnaII%ohH*no>vO;DQttyClj**q*> zzFr&fy#kqyl?Z8iYq3gtu?lQWs_OGbevqz~Yj@Yc+6-k46s$%Vu?J4A0%1dr16ylq ztoc805ssG?odDDk&!&G=2JP;8mxyqP?%~ zpWT4+ib5G%owuJ6YV6+-VT}>|qih&F$ac%$l?7webcz^RisXH>|X=%(}L?iXZ-`W4J z8O`k?o{GPj{23wOE!SVO>DAE)^SJV>wBBHhWGMsgv@E<9ZAT|=)(iO@?pgKb3 ziwuG87#slKW99E97_Q*sJQ579&bbqu)9YfXJ&*H6Q-S&9s<=^o7#&-^%fO#^P z!+j^hf4K{C_q1ie@3vdd0t~ep{oIk)9cZnws0PF^+3ubQ11HzLpI)^T5ywcWxF0+d z9MT`+bfFLtBNiW>s??LKGe{qYY2DEf4L=@ar11dV|Bl4$pn;_~8Ua-2KFc+vSv0#5 z>ZLQ>Y2p&vxG`8LN7kfH(fIav{A*%VV=_b)GQf_F#RKF`kyV@2ONwy3#l}cBIpFu( z8MDKpyo;lWqHrm!*Vsj@D!VzpOu1szQ`TKnzJ8-XS(_0OZ@e!6KUBAp{&YL|or?`x zX-X@%MOrK4zI~4nDgQqdXfP2wdr5>x6-miM+kb8{)+x7qIR0IChD0V{rOxySMVKj& z@~HeKbu#uitPHm_+s2BhyONXCPKIe@LtJ z3JPceFRu2KnaUlSd02^jsuXU9Lfpp+C%RczVeb{s1_up;CyaCnr$CopNIAST}mvsqq z*pux~&97_`>y;N^eB4g7yq@Q#4<5$|1LOHp`+78nXc;1ou#h8#@z{PSZ=-R({#^D;$A{%K#4&=nlf_z#alBFF+l>?ps%=N}wwY`oiCvZe0B zkKBu60G};1uuISzZ>UM}Jgz`CJDHX*Z^oz_p%1_|kurz&tn}M^r;W%NeXsg2_TmGW zJWe3uX8fJW?Scw64zGXZdwOeKP|PLg{aeo@o&yOD2@Z-VTv%5UuA;cV&QdnuLwYU> zDouFN>ptwRrxB2lJfkJ4wp3jWDq>a5los--sd)-UA1eLtfxHAE~_W@biwDIrcPCb;C;li{ogm1M&vnTmMa?l&KbO%C=1vE zno0hSQB6&enFG;O83>7VNy(>q)RI5V=tczXl23_@1cZuJ6jcYp%(GtyJZTQkMpgQE z52P7ZuGh_`Joqf#rFVa0tMIF)!`J^!%OUw6$YZ+5Bltd)0A1U)jO2}GHZR)}IkM(* zeuUug+h{;`SrFCMQvb4FS$l_g`= zbnKb>@@yTws@=KY`(!=E8R`~TQ2P{koS%JY-Y>`CAD8xgmX=wwi)^pfP0Bv_Nh8YH z{)ygMP%(G2Xo#rGh{EousxNvwwIr|DQLyGh1Qr&AgajfY1P$)h z0KNmc*C1E!n3HWXybEREx#23AiO)ndU_)AWo~(#eL*37PakS+je`Ihh2OaJ zvSmBJlV(;J9)mXn$r$PW#JZNTugqZfw3F^Yyho=?3p@ITA>olXh8Kt+1?cjz+goIX z!6TR5pR>Je1@;sTTUxjLvU>D`Ve>$Tn2t^F)*$8;XP)?>Ceo(@MXc++i~;; z4TF3Bon7J2d~G_#reoaZ72Io!tFGF7*<-E|@~FvL(kONa-Ik;rPS`72$g8k_OSP!@ z{B-~qxe#bS1Z~cUrJ$P?a>p6JenKD2dlCE*%qB_y6vD0@mOqrkncR$qWJRA}{^qjL z`36Kt1Y`ntdvoU`Jel{G6&?A->5u^lb5E%M`cLxwi5!A5*3w`xA>|`sEMEh19>zDi zHcCHJT`_ipum4p$+lpcvR7MAKHRIyW-%&h`qSm3+W_!cM;Ou0~147s}AE-9%h7DOb z-AO-j5_-6mKMH}pC7+bz4?_0)a`9nx$jozc)XyNe^u#p0@JK_KoZf0Md9$UW zUI?N#>!rm8QAWE!(%`9^zhXS-%KYSG0kmfSr;hy*)0u&g0+hwsJVcXDxw#)m>CHua zc<8$MFb-L5Cv7rF09VeveEhK*faD+ebzC@}nru&e1VY(f<^u9HO{64ZX zCv9~$D@uKD&YOdeG2_;S*4148=8NpbbnuqGw;IN`1*7N_0476lVF8P)ohrlH01YFw zRRtXXd{(-oi$>~g>oaj&$(gOJ8S&bD(eFP_+*toGufYw+YTU;{s|gx}kx?hvw)Q+B zgaEZbGkM@?jyuTgSXGRjAGh>%Ue@3n1hSmsR0)J8nvrB>Xyk>WwZ4vOMUm##6mafS za!I}RaH#>XM}q1Jv2SuI9ol|(xUz)1sAbbn_!Oos6gwL`Dz#?E)?S(S^tYa5U7Z_b zv^|Ji?TM$&B6Y0e$xm4d_{r0#ye2Pr+)b;m;gjA|4AT9VBp6ds`7hG0e1uwlt4d9% zyH3kiGyk6D&T&cq{1Jbn-J9x5|5zG0RL9h7YS%L^;E)}?)2na5i49%gAYdK=wP@fF zIC@!rpI&Qg@4Y#D;2(b@Z|j`K0{C2c^WacMluw&|Z4$cIwJ=4yN8`|&E2M>&UTB?C zg4RN#VbRRW45$%dOwHfmVgQOe`%2E6CK4bjpco($4p)k0PiYPVt*}oJ0U!~O(0X}} z|DDn0QH-DUiM|hloQ&JZ&k14^PQ(9ZL=@O8W)A_^T%xe$9^xAf*-Ike0~NMR+WSh+ zLtT^(N?8B7!?Ln-F8+B>&)e#S5}DNGa5ZG_2!B*Bg_QF(pWS8p+Mym_bKm>+3jyx0g=mU?`A6_Z$P4b*dz|Fa%lD~Hc1cllO4&# zM=CbNAMt-*uZ(oS#K2`CR%Z!yNswoyVp{^omm6%jJinEn8Jb6aR!qRJiCabOD4aLN zY7@qf`Xfw(00XDU{GB8i(gg6sdnhzJ% zwe=`i3h2&b$+AJGJ5l?esj^l6H2qp#`EmAmBeIAEuqR`ZW+DzTEspvq(Mi=$)n->6 z5(nmgWj3M#9Bzh%hxoP4E%_m!yBPP!^BN*%v<`)yvu&c2N)((Mv$80?h(%i-CyqoY z)t|s8Ht*(zx#;c>u0~^Q5Y5@M`4PjYnQInz*b-WLaXKtLCyWIlsq!Zj(&S;c>6$S7 zk1HK$*`Y&RFoIj)lG*o7nyrICOmA`%?^BKdhs7k*C>uf;CfOTdVwXN|by}@%=qsA5 z5TYGw>Z>X8fK7kM%D8V;M?Bu;Fi-4lb$1uUzh>rr?=U$pjTJx1Kcoeo>mh`wT~3f+ zB5L#~m3Mlg?ssy*0$P!azkPykP6kYcR@R4WlMLK144Qta*mm-UQv~^gU}*jTov>R2 zNooFoQi`j>F(tzSnjd5SU6XRJDYWZkklDIg&{1T+YJGyQ>s&lYy);95lPGkC>xVw? z5?DeKTN37Eo-+cy?C*ZTx#&0Z8Sohir?milwy)e*))w@v#oD?;-vQeG#3mS#86Pzg zqCcqtnczkU@2y&Yj?0mQ|8Ftfu%RF#y|96(H4%`WN9Uq*9bbN!)y&Lrvc3<&+XL32 zrubP+C+E@KXju%}N4(7af`|Vtg7xmwPIgE|Rc%Yz7OTgwHl64TSXzQ7IL1LS{5csW zvC2tsvp|wt)+NH2)E!!k--Ofjwy$pyuXx1lQU`;*o*W6P&vVPsq3*Omazw`Xks4Za zXte>=z%_hfu|N}DYjIy(X(^hD5EitCegv9%k`I^`0R6b`unl9lldHlA<8rwmbRxEn z4Q-F9$175d-&^!kL^Sx?pY)u)TYW(A@T@eHn#&jAFmQtdgixyNa=8=+<57~Fv(aTv zl(7~_?S0%QyTVD26Z7-I0>+;Qy-Pf*pq1~lF6vl!F0lg^oaLzky5jn$#1bFjgJPmi z6tx>(2+W!2v!ry#qpsY)M#Q8U{Z2PE02?sN%Mbg01TPE^@H(Mx^S{)Te2#Rnd-ViZ zcK2b#7ZSlVwl_qYa<^jpC1ypX&23?yiaJ$d`Zs;L zdu)d!7V1Pv=HRxS1e1KCd%pViXobruDD0q&3;dy;xJi$t+0!FFl^`{GLp)?*-kB<@s!5N&B$@WmPk_M>ocsUl z#k&hs6x6rNn_^;9R2s+m&y`XO4Qs$4Z;5AFrQXx!!3zlKmkfDsL9!^}kV!kY8Z%S;gq?X-^FsdtNZ802pD=7TVm4-H-e^4Clz-d!U+k#Tic?&itT{{TOM%QWBFE>`O zyh1i$fuX=mpqIp(EamN}X92eSD`AYn!-=Jgtgs8!~blin${n-Mc~ ztSXE}hz6a%d)Y=^sJ=p)KXTnPE-}0G{3AY;USK0Gq{}MR1 zda=@?RDw2g05I7bhm^XQk}li8e;VJG{$-i|z^fCT`6X28rJaVf^^cE`j1^5<+Bf3Y zG5bd=d+0E0DAA19o0_0**lh+VUO&roVtN-;Q%MPbBg+55^xgmU^S9uqb6F(`=!E*R zz6>GG#wFcXr-MEd@6)eOqhHW*0@I}RfqD{ocYZR2H%B{y>h`(x=CrUxJ+hP;*D+U2Hbjp4sn{C~eh-MfBLdOuS>u0OBI(%Me zdF)s-6?v;0f3-jedi2cCTNgY)tIchNaLyxe$|5FjTK0SQ?}^940IQ& zBXJfd>C`iEqp#gDgVuE??U~oVK{Oca(26X8Hz_nfze729_6UkH%ii>7CIw!Xsu+I_ z>T$)=Q3{Xugpin$P)U(l_xBC-_(R|=-{`B;*r`YdL%A+F@YR#?pka|ZE~7aXbcb)s zow1TYFks{c*amS$ty~J~oM=h=F0>?WLbe5j&ac47t6Z1P99${CT;`*p|#h$Y5=5TUY$#qgAgB_ zCy`pc(}}S|(Sp~u{=|LPpSJt*KJHM`0py7s2~gmMKo2}4WiDDT2^T>3Z?+hXe4LI2 z|5)`PI+oPYzYl2L(lB3y62g{{?OHXjwacX&McrkL2 zdLnP5bqHA|33@Setneu=Lg*>F%_$Rb8T<>AW*(Tve~4wcDvbH(c*cDiUB?C>dXA$@ z(zhvP^*+1Ovrfo*S6#lXpwBeP(rB5Ty%Or4)F--c;&pm2bFQ-%H#sE9GW?`8MXxeU z2SZL0BY@{0lY>$NB6&5W1CBR;&^-{aW}4r!>QmAJXGS6MzrPtdiT&HdsYMThd}`iC z79VJ}p1i4im8rJ0q#Nu^I)FYC5Zua8;bYyt-(W|&Y2MY<{=!hkWq(feJQ2r`Z=%l+ zZn4>azAWp$V#YR^xf*4~dT-JLH~H}M>{ON;s0@q*HIH^h(8iGlTD(Rw;H+RvN(;u1 zW?Tk%aPuTBF3JXZj7Oa9k#p*TdP>E?-ZK$s!w4rWN1U_b&xWQVRb)EiEluXdo-+U6$MVZoKrR-^L0{M4C;Ri{F@C`fhFH+?r|q zVSizo3fW}okQ^S)B2a#JKw-)K&L?iU?_P=D-68eW%qyPO!$N&=detX7dJ771xH89z z))|Lj=hPYUaeV=rTyyI>plQ6WvP@7O)vrC9i#=pfsz5{hXp;Q2f(iUGmmvdtW9NNi z?`f^L{L03hPv$^RhN%#1W=>`-#K~1~OCWzp_%7IAjjVI+ztVJS5>h z4j$FRWxt0%+jW^DSl=I+wS?|SXfPXJr;5?FK#$z8*#blJqc5*FKrDfDb$?G*WX5k4 z(sF^mPbbsvSPZ5US_<4lFootb5Bb-`1a^-T{&|q8QT{!TBpMCDMHYvuf(b_W{bl7t z?4(m-Sy2c|m#uy2c}z+%V`G-}NXKmn^282j5|-#zd}lc!CY{yk$V@UUEfX{IA^vDw zi=@onJl(YGpC^3Hlc>=Tvg(S}Ku77a`2$5?BaT|SjXLqsI)ru)ZU`ouvI6Y8OCTh4 zH@DXi+@$eCqC4sCytMX7BDM_hpX-J<(U` zyR}WRwe73KZfonF9Nb~)+OkDLjF_xA4|_%BBEq#;;WbSzfYFihoCv8mf1Eb^_5fvQ zMAVQ|+16`=xxHI;4r0@{4KjUknOT%12CupiZIWHw84|_Tc6#(X>iu57F2vV538O!< zJD@K9X|~~h2Y=TyD7?g1&Ts9Dy-G>Em`tO~X*T1gf-_U7f&23MIwMq9R{Lr>Mexg2 z{FG6*oc9NpHM&vkx|#WbkcBTBPTQ$DYuuOLWj3TP(dzr1NXh1kSr72IGj}WQ%swmP z=HFKbH8wN~?Ef370I)1&2Ta^XJS7rHYuC*wBlu%7KZ*no=WzN4mbrN{nnviB`BwaC zWd@9DpGaIVHd!TwEoKOQvRDHWT^(#&5KE~U%g9Ml%1`TeBCb!{n9rBZrN}tl& z#`r#eh3JT`sMf5`H8bLi)FB|uB3#PaC(Zb93~0zIEz=hVO(f6NKaZKio*zus&qyPW z;nHHev(OdW`;M9aocsRfWKPx#os6a6e2mj-Ua?&(AO@pd=zLVE+wU9^v3InzX4l#U zDBj?p`Lie+T3S*WpIpGuI+E%2ih?df&<)sN)263gzvxXUKtNiAA0XiK?L~EavEbxBw<5JgX*%jUV-NNA!}rqY6fxmab{< zInLloy-MuBN2G;uua7ewc3v1t|8Xqi6TIB=kJT#;Iw0YLPMl}l;_4$k;A!&a{ z6XaeyD3nT0u#ZLlRqTq*dYPmgW-8DjZj(Xdm-+>oulD>WuWPU2&(C@{u#0-Va9&Nf zH%(XS&Jw;bWc-gW#TL8*rflg3r0XcaNP^cN^=6)fm<1PO!Ln+88xj}8;ww;}${Vyu zV#Jn}6Y6q3L znKF9P8sKwp7AZz4(Wqwri|ir%Q$_jL1#GYS-4Z)^VPc61X!m84a$2lV3m}yu$zVNR z%+kvRE(r|I*nXy)v01~_uTb0Miss{OY*-s3I;m<49sOK0Ew4d*nH>9VKG%0gO54GI zI}h(NBi=J90OJI!FH6pW`}4)j8L#_jl9^-VO(5kE^mgSSzWCFsI7&xgrKDRo#7k#m z*(h#L9?vUEHBvrrIu*$c*u4?zh7pl_jZ%&%9~{m1#Nq2ngY8~t%(`HDT3P}Nst!7B zFm21VZUe*24P0c>pajfAuN%(;gq+S%i$Yzy-w8k0N8yu0>I5Zm=7+9~V z02@&~m!0wJ9>inwa}^cxudS~y($SL!cp6t6DUoEYJTH|FmYTlW$3y;=NTraxcoYJ}gw;j9)u3>u^jsoz^+Kfb)`|K{YASs{CpchSL2 za0xPCmUBv4fAo74jNwH1+eua>b-<&WE$;kl{ZZdF$tX}u=#3lSCP}26-xvG2WMjlk zPYsP2`^t&xnf4g6ILsVUPLg#HkRs0Cv!njYXN15dhger~kfA&m!OxQPMO9QvOyM8I zu#lcmi4+EFk2?oPMBB1WCpQhlf)Ks05eV^$@H+Q|>r1-+OAT?u`azI-1$bVdYl3eH z^)CCWItm}G^-~1|>%E~M@eGRolbf^6?T^j zur?#EIHWqb78PT|OY6R&fgthdu%0y=iRI)DU{;dq#W)}*k{#ElCkB+WwP>7k)pnKd z?^3Zp@0)!yF4Y6XtpS^-;bFd<|DJFADh_-hs60$|8z?6!33`LP7z@M=YTXD&DzXB> zTlW>w(&a}4);G-I2IdH`ZBiU#IQKRISg}?7)9w8Ryei`&MY_&u6O+9mcp9P^c8!kv zVQ&*$NdDShMcokRS3AY@U_+#`H(W>JL(n4LggL$&)7XDlhw*P9hL9QC{w%DlvUXy0 z(7=RtW^zo5ES=E_u<-_w%U|hpM&HqEgcBQTd9l6*F+B>jLc3{X7vvF7nD%_u5 zk>@MR*l-`TeQE6<0)Q}2v3{eaIGO?+H+(J7zMe&nM&6=0(wKl`R#5Bj@Pr4C_`9V} zatBz3OxNhv&AUbBjLXEY`oMTk0tDI~u+wgvM$!3i9!%~V9EPS{pm;D_P-e*To9&o{ zmCnN(N)hMoe1V>0h*G+D0q_k>sgVv6?jZ=+(ua~zt3N>IpTvkRho`RJpd9DN_o@hm z?c?ry;{`_8r$st<9(W;O*7zrL#Hz`xc-~4i$%MB1?`?RZ*h(vuH*AE#n&k(5o^%92 z--tgeQ5=X5KYSF-tRflJ#6O_^lHUI0DW>r5s4ZO+WygYA09Y6&0l`}Oz8@+fMZ-F$ zzcfP^R0Mt|^F*t9A>9{@cUwS>X^(B-@Hypg-ko&Tsb!cDS~D7J zrky+=8*>45Y^{jJRcpg>06rT|NK=x@gOWLIK|!LEPgnnMh9%N#fURA4EBW)uo)a3HC|po7qxrZsUY|gWXkI+Opj{( znC}0`jsQSJ2-c756wFOgT7;022zGYa-Pg&#xQN3XWufo5qO~k!llqQ@y?m`niSmHU zE45fG`n{Y_<43?(y`IMo8pHmxZQYpM4f(2!_)mC9p zUXPG*!y4)Y1|OQSb+qY7=_L7ERU)KW7z%a;Yr3w^KpGnq8~q;TxpdrmovY^wdDZyd zMRPRV!B}IWcm?Wrg}Q2Szn9gFgUCM*3JjoQCjCN4R8T|BEGKDjGk4O0l{#B*jbTzqnykv z%7&kHI|)P(>3v><{rHaliy&VX&Zc~S@dK17K8cPEe10m1h2XBx*)q;~<=@posam~J zDcGms2>3|wI=HZ^3IB==6d-+lWI1)-(l&VKVX!UAi zhMYg1#KpKVfQFKLu+@uL7iYq#&`XBH{RZteZ4VofNti9(y4{I41nshO43HR2XIKQZ zTy~zOKzG|s+2i~Z*dtM_$f^-)e9f8uJ`Ly_WVr;lJlof9D+cS1Zkjw-b^_J z335?QkQiTDQ08+MELvJr!J!g4Ja^3qIwcp;wXOx?N3D6SN3!7>*N|W4btv>!NkNQE zxX3wP8ipuGdPYmneDvDg_D1(kpARCxtclM$O`&jln(~`ui$HxP5LnM|J34oZ2oh_a&oWUPp6gf`@WtCPZpPGO=8#uXn!IV z6bKf33yhp1qeA?d1d5D~i$a!c{K3d$@e2z(6&)=<-$4N~i^*=x>+B`;-)L{nG>LmOmMk8WQGi+t{6B z)uRI4A=?62)ayzU1ghg_tUF+HYIBwpM|Z7i7a2UVpZ*iyUu#&&FiqRD1zr5%zI=7< zY54}qaGE7@ARK0+jN8ZY#>oMyD^VZ4(}it1)g zx@N|bsm5<$)z>4Zz5D3jgMhzQC|f>0`|`xuXY&I}uarm7^=E8+BkUo|3MhizIOOK3 zOU`aP9x!k`EgyqGkxS#K!zDJ}C*%)rn+>%qxAV*#a0iXQqvBH%GJ@20Q#+{mZBVxr zvAioY^vu3Ri$x+Ew@^LWk_@O*sT@HI4u#xGsF^h)?}XgIV1i)|>z8uwJ%v@_h`JbT zj~OFpsl5j9wnp2KE-optQ8oswXO_N;+`(2a2h53Lu6-NLVj<>*+!AKBQl&G?D&l@F z6lE$!Ru5v9E>X%Fe{I=4+KZwyhqN?jWr?bFW>!Kz9-n1g78Yn0Rn_6^wXvWjeyPf; zV-aUb)*G?EM~(nO1*k2lAXfnP`?d$kj4LxV_)C+!o*q6#3M!|AUdn!>f^pfvd7r*! zAQ{G2tKRK!sI!^r8;yz!R0u?b&P+n`4Jd-PX9N61i zFKdkTf0P~zk;JcjE8SKR21|qX+$n&g{+x%1x37I*?a9{Gb!BCZ{uQZ>2=ZU5rSl%l zX2UPRmPZm8@G#wMlHGTtlW}e2v5_HA@nXt_$A;LM^um04=PDmmk23F>qdxr4TrV-e z&MNoQ>orI_>DelcOCz;Q@*079B4ot7YL7WD{LQ2@#(ISUZg&O|K>N!@J`{xykF#Ms z|3Imv_F&fzYpYA>zI=GNa+xSgAl4ypd0uBWv#KFGQ3n!$S^vumQiJTmS$q+K1B5yN z1hN`R9Tlm=pZTwe zjT15zLdV8Vl=yz9T9>H%_Qc+j5?!|SM5Q#+bofh{qySUiE^k%)=mp7+Nx$4bs?Up) zVzAWl49fy;Fqo)+s5rmq@o=sDkkpg#&vgG8*Y=tOzwma`ZHrWSP*F7Q? zHMN3*g2Cn}Ky28Ai{Wkw3%(lA_cce=G>7em+OS1HL=RCi3CNy3R6R{vQ?zNw-mz7I z?L)4C*>D};w8n^SKn?SBE-^O~baVtgXhDzjv_QO@2bDhrCneSy^lws_TaRoF_u}r0 zp8#OSV2AW4KP|9-aLNzZGyqP~zpTqTBrB9K?$r~}&|%uB>i^Iq&1vp|9m~uhv#qKv zGK83QcFySdlQ92lWEjMh@ep&0pE!TmU?#xp$|mWN!0FvF5kye4shBY(%u(;Oa#dLhoG_Z7E;Tq+Wp72X^~EDWnmSKVald-$4`Mb>mb6tU0jcKtP zl(#2TCXEAiZT-Sb)}0(2Fw@C5*f2UJ-N_{7jKHkVw);^J-+s@Jn7g%N{Q(sszXh$a zkZqhjhfoO6|6@jdG9-5Im%I9{5K}|0!pS*uf~4+arg#*&v+?>H5iota7wA`rq%S$roTuWOi!2d}hIaJ==fY z+;1#@dSwPB4F8n~|0m%BzJ};ux62?&{ND-whv!oQq+&aZ43eb(zFvR3fxapN0^)yf z<{t);l2%^L$AXuYm32r>@938f=42lkx>0kC5Jo2;GZ{P*}y@Rrs7opv7cjh^-V>6q}ZLdf9`k3=jCsH+_< zB^FtNz<~U^+3!X&i-ynFPHOR(Utu5(DH#^rN>pHRJHy%PHRk@k675)ocu9nrS($)Z zCt`kH`D}t#WKLK`X;CFIxYEV~zP?gC;9m45{D3IEsgiLY?I1Ox${g5ju#Wn zY(+kW!XZk_`NGybCt=G7vI!FGrrlECL%es3Y;Q}>czkSy)a+=4 zm(8%LUuoPY?5<*W^d`7`3R*)FG|7L>f)WA1@KCWN;Q+%kB>;6ScQpqu!i)pE;Eo4W z5@44;vhZW0hcAt$+14ujUy!A%N9B8(aEG?npBfTSsJI;tQ6Jm%0?&e!KHt+kiW{y~)MtC4R%umhDZs7+QR}X0o>hZ278QR4zBlA8; z2grBM-YRs9fkOuH?fkUMEliw-rj;%@ht%Q4HbB@I{ z+%a2=XOl&h?-#v%z@;ZB==s8;AxCYisDWMM2FT4b8o2=&bU30(;? z&cmT&Irqo$c5l;lSJf!!2yRF=Gw=d!#JCJGxPE*rZDeeCxs(G*7zW#1L=@JCwDku$4YWIi`O0@U-gW4`ie`7F2~57I%wK{&eS3TY)l>V~ z7V$H}`0sJ{qIdM3zBm@#%sw0rDu2SKT|8Jnl3V;XN$Q_h(4W6bapc3sG9rpAYgAf9 z@yg-gpjx(Aaptj zSv7VGiIu|VkwViJ>X{FpeJ|~!Ynp$&p)T=_h1RzirjpWw77;h>Y>|umQek#k9bx0u zDoZ((f!{)K@}Q`9Tl699JZ-YG+ybs@xfcjrzZ7q>^}a0cy`JQ*$PJzD?{gsLM~?3~ z{c{l#-X{db{Ji+-C)az)v}S6I*~zQshTSJFYLG1Kbky@(qy?WnwJ)&(A($~O121Fw$ zNJm5Yis&s+{V{KUpCB$xkmEhYCH3=9iWsu`QIn9^?dp(-YU!GE5MS94AH&sM+hyGK zB|qu3=jVR`55V*Arq)cC@cJ0C938tgG>y0q@1Kr9)a*Cuk`x_$?hB(8dl}s_vJ9qo zI#wVt5pDdHEgNv5$6y6n`#1<*_TstFN8}|PtkmB9Ui1){dg(X9Um2z$F540q(2A6> z9|tidAqt@4tGQ#>6%yf)Bc#t`wnf3SZOR!fKA5oV&B?3Va=?b7Q4N;T3`xW~!^onr zi5gNv8}3-nyTE_)#Pkn4TE)#h@4!#AJn9=+=bi?eV#U~5gSfe)QDe1jk%_B|Unx;=xhlE)b|Z}|yz3Mx zD*%-*)MI>EQ=oqqT>`obrRnHx)r6p+Hq@Kq$h+ePxEWJJj#$XDPHHHOol-pPrPdfi z3j-X-qj@fu3ygc)5h1BNd^j!L;Nm;S+FOYwN;PH_1x>(h_5Y*fovYb(%zmpz3~^y}5{B(YOhucDT8 z@j>(e^M1o*9_Cf|`|5)Lk*o{Kob{;gu#GMXI$ArTh?Wj}et19XoNdZvH+^1ZN9fy- z<_e#aVO&OcZSOYQRO2el?SeZZIO_jc!}1NvN5Oor4M$P7u_DgvGAMlkx<%3NW(f5$ zZkZChQ=gYHx&b&1W}>9B3q(t+Ox~A|IfAFAeU9d*Ca1b)q;pzbgOQ3tl$7y8?e{uW zcS5vty6&2#q}|)yEkjE|3VA!EDR86`2@o!hi!hC^v&_VO4VzpDaz1 z)!lYRa*T0)pZG_p0WEG^iU_)s;=w+Iyi>SNADrq6G){t^f^zFmv-ORcB7b*7n&_y6 zfbcw>eTtpr0Vb8s|9Ps>h{aj{CabT`~ls))G zJ&4vVKJcgsU?ltqV6E(VZ;N3O6Uik3C9SuOOy%kTX)@W-)CsY(vjYJOD?)KT7vlw4%;kkj4GwFG z#pYDc91HXg*DitoP^S?gyb7)0e|2-!`?8!ID@hicH##*WW9c0iUv!8&v~pshtfG?N z`U1w-6z?&?p2Ll8wDCz0&FrLDja^n=CjT&FnvjPf_MsZ4>cGP@3KECIeW_;`HmkO~ z*Xl6O>JvGdRz&C{;7V6bH5;hwF=Km(Jx_^|vv1gYpw-m9Fl> zlFp5U#A>*2PCM^qNjPr~=wbUlY&mRpR#6#$lB8X;{W;Epk|mFb4YeE~l{(3ZjEr;< zcnvd}U3Aa@ab!I0w;M(&J7&A_)ynWH&=YF4;o8m>Fk9lcqL;rd?aD1!S1T#VX3Rug zf)(z%-D?zHDl6f0Wn@a4JH$HHn`hL^x>1F6VlOfi(isxJsr~8|x=tJX#uN%tKjdXhFT&NjC9#>jczhLF-?MaGDX6Ild|#u&}Gq8&NMTxv<+zmD8biA3@C zY?=z8Y)}s2JE;gUP}L#DiSHp&Zxy2Jw7%?fUgmltw!6i;Ovsb59kW~7(g`aeo zPWcKYf(B#Z$~1N1C)O8q<7Iu@Fa3%VF%8CPpSLTjZQ6K?MibZ(kF2eovAw?^{SB*c zNbnM@WdL>>qLN^X;LZE;R$NlDcZ_nKDt62Y>|=F0*pdXuJAm`rvNbuO9@+NhJOf48 zIB7=x8@9X)1)qb3dIrWM^@Shb-J|>s=3|VZA@1WOES;j|pptuhk%b8Bgl^gnMohfv zgg1XvNDx$f4HO!SP17ZSifI1HsrzfQwF=X|wZiR94-}>t zVaJzVmzG$?3Q~4nqiNcH;+gs`S=i+TIaIoIs51}Q%*z}5Em*jc1Nbp&eDAI=oDQ`? z1JdoD%w%JpEhFT%)taKP(>2KlOyBh+a$me%gJroazumk6-YV=T(115`Khi0>7soAyTg%=zDa5^soG;G>(Vep&<$TwW#s$ znp4^R9?#@YhQHfJ0$1h8+*FCB;+X!BAl^*)~tPf z8QjFgL`lB$JrY~dv@;(~kt;jwD^n7F&#Q`znRQ+mVV=>rjXg0I^IZ|Elk?NUik18E z8{P5oE}U^IV3|@^4)QH(0iW^=N4eUMNBbmfV8!u~@;q33enG^dg`QQQXt=0Oulh zG465oWS#KiSWI-J2#|o+uDOTBS`xi*vfO>l`!T4{`1qLH%nplKU%<~1B{Kg(6YF5t z`$D@0X%C`1Sw$e%3!b*WFH*2DTan#EO-5_*bSU14rRHpift=w*WLI*BU|kqqc#_%I zMxlWaF8K`R{M95?}wvtZpwY7v`Rb%w1W6ifo$rjLtt;=j3 ztl%)H2?I+SJ>N*H5`J!NEfQ*dR(5ToN{)aWlh16c(>1gCnDWviw#`;aKB8ccnMSmK zg=nxUv;3A9E4Pfq^x^;-1ylOX6tAcXs03ZiB_)x`(4d-v>XNh(h2_)+~k=fzMKcyC)M{og-F6 z+`!=L_vA@5h_g54Dzpq{m#KSW`1&TbQ6-b@x2@-|T*XCrvkc;SbkZH<7jp}oS!t!3 zQhfVI1bQRtD@}xOqYpbn!ESAFlqMC4{_2D1gz_)-8uLkfBmMPPu)`+SzeJ5jZuNE} zH2m5aYznhP4CwGz`^43KgMHdFjlC1o{G`dyJ)sD+&STJM2ueclhCH0lannURjhuqt zt05&tV!&RZjnR!TYBGwq1%K!tFw$|R1Th5g`Nuth?3z{bfFnQV&-x(jk9wE(+<;|B z5L-Y`X%?VYYoUjH(Q^8k8^9-ty#Cb_3C~C2}BX9$+>gpl%8!^2h6!%m}hV%8&)xoxph35-SBxFJuh_@#xrrXYJ2_vZ?ZD6tQZt~iVLQjIgPRuH8#ih1#OILE-UJMXmIkO!%|g7BxpTFwAn;Sb=`87y zQOhsA_zK>BB2W7Q?{80^cR=;dU3nvRtZ<~E8!CRqZ#D^_VpWCCpF|%J1N4 z4&)BZO`hx^QFE_Z^zRf(fvx@eH26JzpnUWr{SDJ#fmKR;(gwy~-Z5p^_XGdM%Aft_ z7yz*Hj6uY|%7K9Rfh2?llznW^ZrPH}Hi*@OPsc13@GUO*--u;V6O+deYMyR40%`9m zo?-(4C9z&y*I&p@OX@F`{;REZQj?g?8?=o+!KWh6{q)KTBz8LFW@AxSR6hzJNwbpZ z=U{>)2F+QWZ8KZQ&{bCU=o8eid~{&5g$$};dW6i)rTKcD2v?d}e8YtOeL%6F#MIkY1S%r#m>_M`^QGe={I}u_$`H%RB_2>c5`{P zR{YBy1}o@mELrjMIi=UL;8jziF;&vE62*KS(brd&D^C)j{B~$S<{1!!@*Woc=LQ$E;$~&Iq=S=9PzX5pjh?nT0hodPK{OMHHPh>uF&;|RF zm@Vy(jL~J~V74vu9%Q};P&0|+?)@7mSiWA z=^=8;=qEf=SK=%PAawyGJU8uWWB(zT4X^3g?Yb9EQZ>d?rAaj-Ir@F%69!p6_jj^i z*x%0>4Q0U9gwMQOW=ZB+IMOoZhFPB?C;e8B81uZzm}kzI@qZp=ZiF^>km!2t2oYW3 zqoO=~mh{>bWhlIhFnzfm+@lcNZlrQ;JwIr-(fFn#bRUAh3(eJ5Gvy6zhfk}=sN2E9 zj9t1w9;&=Eao|Y3%0LM3BdfP#WTC?rH5gc5k1Nil2y9&fugNOQHb4uWHuT+WO7)LVG-wNvwsFot-+WK$SM>pQU=+=>+;#mA#)RADG$7dOr+i zc-$C0s25+OY$@N#PRPGc!u@Hf!=1W5H5&-U-|UPAF;UE-0n&IXC9)%1&^h^M-NZN! z$iu})=&I=2cxmmtGN9zp5au`e+Tf9brCS}=dw;rw%3$U!^+$L7x7rK%Q1`=69po3@ zUc38kLLD$x#$uZgRQ;x-kd-68sC$2pAw+$I~YY}rdQ9=-b*!=A&X}OrNw`Ie55|&rlyUN zIgkiZ3oS`S(BBNAB?nveG|DkzKUL7A`^iW|+l>}hYs=-KV#?KvC)poS_&KVua))zm1k zHgKcasc_;ZIk_5X7!n=I>(Z<$+ZTuPobuqiDtYPF3U54NLG#NNU;G-#Z9p&<7&_3p zv;~un^38X1(EGRP^be5$YNKOz=Db%Ln6kj&$nxya(aGS^VxAn2iNc>j_|tr-KV2W5 zoka#gqf{7z!|7BoDhwq_zZR{Xc#7vSBb}FD zr8#*6h>2W^2-k;3GR7U=FR%O`qB5`Q)Et>|tv(~%>RP|3qeO44LNu2&>GLaiP&p<4 z25F!)9CguwEk5jEQ{i~hoK#B=R2y=Nywu62K&NrBm97?Q(rLo=NCSW%aE#< zpbA@?A2XY%7j$pmO1RJKnlWw|8^35FV^BR);{s&itJkGRLfYcxj2K&t`YtcHPo%uY zzR*K5P;DOV;^oTYL3|Z&fCj7U{zbEFs`4jQ1>~HE^nGcpU zuSUbmNjr9Xz0K5UXs7hnO?kUJHw*>2A%f+xjLzs6<(Fgp?WoLChWQPS#u{+VlPa0J zmBa2WSez%vh)M7#tBL+eF_~%JTXL)1sqm@Le#v>Qm!rWc6ORG=Ha!Zhr~q@sYOj~# z4Iu=w`pIuIY zo-&u9yLU-HFq_?wd&2{9L5D>X#;59)VUq9XzJD5uLpOfHq_m& zd3cZBdA8o^RyQ_gWK?+=&neAS9+jM{!WHNOZj}adEKrpP@GtK@It($5PYEJ~8qpj) zHrYEZ2$h2fE*Emb1@E;tVthNkW}~79=-o$PUkr@QTHC20rBo%)*+2TFfu0|Kd5x;a zxpJ2D4}BU!ykK}!nbTE+7@hQ5qJ@%s2}$|fD?-2ak~D1zI&9!VK{UhN%CLC4?8Bvp z%~r=xJ-J#7q#;4gz##;J{Y&^2jD4UaeaN3W6YU*TyQ&+4>-P1y#Kp)=cD_c&Ew^5& z-1x$F2jarNhe)f~c(`3RZX^!UH3o~pE?;} z*%=nV04U*o9du;M`<*b>+7y~Mvijr^vVE8p8rc3nPDH_E4~j3R`qSH@|c+^UDN(I zmO?Vu5>D#As?8}{xJ5TsMOsBWSfi(~b29hvk+%I`tKxv}Bn1!Ym-I^~_^N;V@+%ZQ z#8c2azJ5)(n?Rd!$wTmtyUXO2K^*o9k670yKx98T%1SqzUcOXVe|Y~SOQVzs1VY5{ zVM4|c)JH>-|B}=2v}c5-L$j}bcU|?+UV4#5^P&z!r_-v8=o5iXuUg^nl|5&lMU$jn zP;(&D(5n$MQPky;Mq|}{i>AcYPG@Aj2vA_ z1>uHgGJp*&$l{%y=c{F$sdvSqsT0oqODt+7D{K`ij|m!eLZQcoYLxhEF#M+#qTgXe z>#h<+3`+!3ry(vn$7SBd0K!0c37=ZDpd->j)k+Z~j&B4j@(%T3y51_Ks9rWWwfB%3 z`is(~f^NDUdpC_*B8&va2O0Bs;mrr$(;D27;a4r>HzDo>Po)?O{R5w9{p1qV5? zv_&8_*bzSt2+Sq9T|kw!&I9g`JZ}ffc=L%K*0WRH7>vL5pl3+)0}4g7VDibbM6ukEO`i!h(?*w-Hsqx< zB)jhbKasq5M?LnK%$`4lq$M2LGT>07&h@b63Em{j_S^2iw#)&GSZ1mgvHeVTydtxQ$7; z^G%sO5<6hAZ^_1NNP(NZ&CddAef$=8(2&j&B3ougSBIS8M;WWUujSV<>_K>ypH9?m7{!LB-d=Mci|U)d!Lu?u)O$I}b=e<| zO1*u08j(n?Mm02+M5{Ci4ga(zRb+c{iz05S634pJ6(@wNxiNI z&_`VW>!uGf9%W=?BwH63Rqebs)%$Bw_AJ%R&=h3H>^bo1VnlG3bQ%TJm`(3;j&(Sp`}Yyw7u9@&r`kaPtKvlM3|>JpDKNXu1mCMU7t ztZN>8r*l%YM70|;PC6$drM z4_TVFMvm@mMAS%kA+}Y+zk5niebv9pt~oGd#Oks z^^Rk|k##19qC6uQ;w)B&Aj5=GrFj55g+M!srA#eNE%^x8PnTY`(Tp~Fai%ZFqd2%i zkWs5sQ4sNGiX3C;MtNS%qglM7W~OY!d;bqM@Fqaq1Y{VDwgM_v* zR_4NYbOMGws^)@Xmg1!78>hj6DXNF3XxcHeYbAl_rjro;3x@=~#@nxen@B|kq2Wkp zPGK?Rz4g0X)6_JTaDa|4bTm`iVpjXIF1=1(0ll*~k{$IDmqNi;XfuBlF6o%ai;=Ab zdcj4g>TOVaG%KHEc^~;_TRw~5A?a}}nK|@IB$YxCd_%bxB9#;K$%#J37QG)&!u!dI zGg*9JU$68^<o(@U|hX9L+( z1nqWN3xhrxqzQELZ%L zWQvxV7KHWTD-L;gpy31d`urh}9nwWj_OiKC;QV~ok)jt|tNo$-&agY=6_b`)_U=1+ zUOUgkW68IB?3vaTPXgh;7UlQy*>kQ+UmAn(Bneg#6io%9A$&ck%Gy|6Ggins854x8 zwQ|wC2iF`wMke>vyBh`yfQ{8PDp5mE)dzA77c~QN&I|ht9#-CaQC0R{vgLza@i)!l zdD5u)Wv4|JQOdu3+D&H$k6X0SLUEF-%6~*pap232oy!kV))E}JWr(8slszeJ2(`#q zyg$1UMG$OZ^TkFS*^W(@IOErXm0e52`i3OwRtr>3u8_*@#pE&=$Bp&woMaQbn4M)~ zt+j6fcB^trst_;qObCP^0@l^l;$lR~#1&uKoqdPvAMh$K9)i{KLFIDsN<4;6h?B7Q z+zb%vayzB=jroPyIh`5R`=jZTo#--AK+bQIP9Yf&j_(8?PWi6T<^H?9u=0U%ZTsLL z-lgOpvD<^eWXS-q(Eu7@%;-AGXSLxME6%j&C@rRHZ#_KShq{59aq<2N%RGD8uiNr> zNV~I>`mQ|&VDvV9zFi1mzp(xH?xzTT(iGm-LsDqv2eC5BKdY?E>&YIkUY&Wsr@+Vz z&zS58p8*yW`hX>GNr|mTn_l^L>V@kk5IPv)Sg-+<@M=UJR9hl4clgp0ojn#HK>Nlg zpXVir>VosH_pa_kG6I5KSo#u*%{|hep9IB^$A@nj5 zR>+p|dq%I%<~$2tn%K~#gn-j+jEJ$K(%im?2wnXs$r;9Fz4F~^c>FP;A~Rk%lExj; zv8e4B&2hABdD-D(8OcGm1Er3{2Xjh3-XHl@EdMhH7z2UrE3|SxH%u21f~@3nc>luK zH)h5ZwkoY}CMQJF;_H5yH9Ycm+fymbzMMp&S^eX@@XVAkz3(Xu7D%(j94}f|J)+XZ zZIr)vxyY#s1iCoIWCVBXLB+YhVU;7}3zgg^)8>qcpO9i*Te|M>8D=rxdG1fJC_g}_ zaC`9mY_TNU6rJt$3CiZ^pkE)YGT16ghJD;`23n!uwJqyi8Lt}*h_~>B)KNKR%;bel zZ@dJ9tL&R6tuxcf^lK>Pnkm(1+byCO*zHDCzcuEocadSvdUct54DK_N(WSqqjjyb) zd&2pC3>}Jz2=&?nV~l4ADC_dyF zZ_v{=F|D3!6GLaPS_J+JEUw>kgs#vavO@5Y(8!36dR$f*Y}Z~xM%UN6L1__>J8dh; zcRX%Km+QK|Q$kY~S<(R(ghI<=k&WsC-lGwT)7U0Emd4AoNS(308Z!&-oT-_Mfnf=a z=)mVLLFsW5n5L~4(+UCd$N=uQN>tpY7jNYML)SaDR{}NLqOqOs*tTt_BPQ@jGnAG^#)0-n{Gf)fM4(YOtg2Ux=Hq z;X+87GLZgG!azx@O3BxJ!@KIczMrExqqw)Plb%Fb)P9_H8af1CwVJ6dtVCI$Ha}XBWpD!5>Iu10-3vP)=b9$lZ~;ro2AeY-be_5s6|+AdXuw?NB`( zR(t_cpd-ks%c6rM*E#~}Y#MgbYtn!nEX%0~3oI7ntZ$T~%G(g7r@8*}I~4+>BxTHH zQNzR_i`>b|Ir@q(M_4ARZ2c<`2F;}c9gi{|6hAC`4~KAmUCF!01s#u4w&Gxw{ zJ;c2G1Qxyqx%181v0agn#&lIz_y7BZz?x8zA>v9Cez-$y?iRgj!2KCt6`(KrWOy3y`anf9{z} z%JXpB2E%#3haT{Uo113VyCb~nh?ad^PGKOeUn-iV)ZpX5capZhW~mZ}nsIl8S=2)k zTK0WSi;QAsKuxO=LYK`ge@7a0){E%q-GGr`nmSO>%pxS(7e=!ycM=OLdTww~bJ{=x z_U1P}D?J&EfW>4Y-lmH{fk&Xny&kcMc#`-w3&dl`5KWzh&oNz5K~z7*%{=tic7NP^kcJ{#Y2-@_P{TWL{gX19tl74C zy0<*^Dy^Yk(HA*v+uZS5q&x6LY-M41bg~$i2_s~9y77G;JX^!!v&f|$@@!_iZl3qw zf4j3>0mdD^gzh7)wPsU?S|1g*I_!GpVTrw_Q0wP;$sctjQzX+j*t|CXI!V9J> zTuL;%siOO_!X<~Y*u#;nOvnbM%n2q;KBl~J^xU-V*a3_qzHv{!RNF^icy?}|L*_Uh z>t+z-Ej~zg$JuZo;a+gqG}cw4SVzPT95D)T9{5WpP^Ol_;eqFwV$6+%^P%_p3i|H; zi-=~`jMQTm?v!bngKjKSOw$DcNFv_-wC65B8uMqp1=HdFq?9WmS$>B*OcWawYn~Co zHyavN4b9<1mbf5_@G@lYG}e}(@>8d_cFCC0liKdubaq{-AJ&57z_%?RrI*aw+l~Re zA}K(bG#$f05Sw_E}g z$4k3hI{h!v_*a9!395Y<;gvqv)!8sMu86DN*Oa!DW_Q=SoELN_v2Y;N_O+FxbiO{= z(A28?=W-={K<6k#XQ#bcxm+KHClhv`uYmBC};BA||@yRPt_{lN^)v1s^Z^r)U)I=G^ZQ->lpz?trN# zZ6%`AH4VOl-&+_gcnUnn8D_uytS26h``&Fk|2@T=O2oR%`RrSjJA~SD)M!NE>rWFx zfq5Yp`uoIO9Ss*$8Oea_)k96{W!1Lfs@;88R4$Zk8?&C=^?c<&7K`abbh@!U%Ik#^ z5GEjbKs>gOvOXmf3%v!9f9h)ik3V_NLtUR`SH@w$~70igaXy4w0|^Je1SM$JCk`jqk$+~ho*lk>6|7fa0t*3sQSKCTWX%C zjkRuJ0%F8kyHD(imJI$lm@fCCq(%9YKfcz|SIFZnG2NJk3NElc{u6_&xj7453g zZJSk^KAAxqO6>|t?9Ggd*fjcej5y}^So5{(`osGD>!<<8t+*aygn1>EtoxPQPLqTn zEn;Yt%vwgyWg9VOj zuAJj^XA?1#HPg|g7pT6PmRcG`ePV9Zl_4|ZeM zl(NFpu_5D#rJ=muo$45z*C zG9G85Z&)@5zDBY?cvKX3Tm80)PwLf9P9*kZlI7XvDZ2p%tDfEK)~4)MGAWXh%dEb% zYgfx_&+Jd;#tP0H6QKgK4tmpE*%7;aQ{wzL#X-ac)!zn>=@_+#|Bk9O7fwgc>PdQg zTDB?`7v}YHby_J`_8k_`>+LV(t)s$;=+^5^q#56vf#TjGsQFjqNlVG4fpp9K*)Xz4 z4J_3<-{@5Ub9Y*YaPvu2ASP@Z$T@<1FbJcyr30=`4BhViMw&&qpYTpwg&7?-{a)2Z zv0o0ysx=#JHJ5qO`l`ECU5Re%yW1#K6sIcz6w$m+uH{m?{) zC@H_czXKJx5_tHydo0nllExT^!n*VeqpRv zRuAe>aTa?CHL9X#N9hjbFr~sfAdAiu$g=SI{I4{3@A+nbbaa&ImDnA<2##dQPZQ-Z zr{c4`W__9K@QB6(>{qh^uwqB?p_rDneIP&m`1Q0el8LYTj(o*>>ppe=BZ-WZ(eaYv ze6>C>Z@@+pNmq}mVwd%zxl_^nocoj7R$}Bt(miuu7M*~TglnE^-dcL^z47ud_Noy2 zSN2AeAij;(axM6!iS2az+51)K^AP)Ly+>oA!ud*N_*@tuBLWXB&$bqi`CnUMCS%Bo z-n9O=nlnH)Z9n1rvErjm4uCWzC9m{|V-H=vR?EFp5p|bwK7`u19psN)0MuF11m@w1 z`x+2DmcB#XG@9)ku^9U?RNuMo3;JPYGZP4C-WRhNd< zCu+KrqUHERyEI;JS0>jk8DO1Ij+2{s3ohhkO1<+p zy`ZGqHih$J%30CGNm$!~SH83@b``B&6PfH|LcG&PRdlOpe;e;eY;9h6WuMbnoZ8OZ zNL;qG*NMDGvG*CTyH*aOP3#^=!ic))aR`nZ} zctEB*X7o^&Ojlm~nHDjjnmjf$pSY9QNqeVFea8P07O!k{_;&UjCG~mbCzpE0<{Wtv zHp41Vt&YiR;t?q<1QsedIf7%6mRkqBqo7;@)f-yXJip?J^7@wg`C-O2`@tW{7aKXA z@0;U+yevB#otAwry<-LK^pVsv)M9ECp)Ueeo@_+PGTQPQCR+HEZSK z{^cG1*o(gQ@;I&|`KhT8TapPt?8V6y<|K%4el z^HA^v+9G&lr7Vfdpt$9MNxHq~)kJ-J>WKzTy^3KP9tY6VK}ojO4j0_OhAq5daf2@W zI;MBXQ*g&aD(W(X;7=nsc1%R?M4@KyA8FynfpI(uz+Hot@<}b%8l*ThQ+DR{LEwS^VZiLF|<@D+u_^OtRyu;ofis zk2Et<%i8DlJQ_$J1vumfxQYF(tHS?p^tX9pxx7x6SKH>5+x_zK_C!iUbJ_xNVyex# zBMm3|E+^a3|5WtQHz*p1;My97K6v%~+q^j_ss=ojG%lMGhK*hy*I!H2F zX{EBt_cYXBtpOtZw9vSWUL1+NIvWT;XJVJvL}usEfv$KPqs)D$?f#H^wd{?H&@EKk zbJ2<|(7L;AGBdKMWj^T(ON7S}&$1@DpP1fZSNyGnh};tzoqDm*p`C&IZ};#0rrAH! zmtcO4-V3;%w|)1Ze*$SKe;12=;_V+Hvo_DeD%KJ<(FDqNLSraYr{>sbT2+!>Tz!XW zPA6C@0+Bv4vNtUvl(Ny5Z#p4gOtbAytY}xnoJf2Ac{yd?;D@-%b>L;bL~++FrPOpDji~2j1AB~E zuQVZJWW!{D6wd?z;=vr>)+f9>-9du^7s)7-Bucj+mK%l{W$LU zBUwk%gB(0hCAsl@1szF;SW$>vCS&JUCagakNTH8#CU>sk$w-x@J2*w7O-jeBch|4vk8YVqoy!#(avl<*Dl~f-JqUKb3p8c zVJElSi4D_6Qfdo<1lJ46y=NG}i#xj74d}=G3Lyu;RJ)Vl{W-nk!Fd08=zBl}vh`5s z!Cm3RYs1EPh#S8#&OW#KZ2JwsOS(dAnjU-DJaci?tnI%+g7sZFbPH6n?MbAZOgiq75s#MT{Zimmi>6iAuyU%b!Bd``T~^fyhtOv1D3s8R#LjMHaR z{&~F34XFnwSG!WR!l$#89yeRbp2jK#rV3Yb!{2Dfg+14B1GEAI?_$#)x$PLDwsvW` z`3{v4#Q_^DLW6cg8?|;z&v5K0SXjWAtg!P-RM{-r{l+Geo>R%h3EWw!f)rcNoRf&@ z>%$7EER9$|&TgU{L9--i{!HQtXPVjcKF?Q}D2(G(c*`Y0ROK?P?$#O|YFJJ)C{;DJ z$xv5eQw%eUDK&AuY&mOKs(0yf6j zgxC2*5x=TDrcWl1;yKYeAorgH4L8L*?3QfW?d@%dS2gCQm3i_(cC|4)0=W#R$+-KV zOZGAcd4EQg8_oXZG|#1@cr*5O84ph2Iyyv!ADB?1Tz@`@vJWeL8jvSoGnCNRywB+C z^8<0M0q#3zl=Rq~PiGwWUvZvt1K@sN-cUnSFQU45(NB^ik~k z?W}t_+!@TTX^6eC=up(pBiz;cFP&koyM3N=>Sy82Yu?w{%Sgh|rGZv)@D$#%30``p zS;WrymB08+HXyrn)Y7^_y6iH|5W@lk zNFB+Ee-%!JCr}ZQa0aFD|J2iEpA^vFe?a_!pzcp1`jx_=IRzmBqzp*hBzU^mFl*DR zh!UD_dbi*kXVsxMf&4)PH?N|nSnT{@`ICiJ0avZ#x^MU7E=25aGyp7b zlbHu~Q@~|Qs2aR!;l1jkwTQkln{DJXy1R2gB$S=RYRhSj;%=75O-xZm-dF~kJr(tUDws5^!Jj6ozw5uQWT`>p{N+%ntWt>-XDMJ<`X%ES`&hvT&bPX8 zOXag!{H5$mec{neKz|6)fE5>bX57K>TX1(t=>P6ZV~M$w_<4M`)=6BXqkw?7ntFIy z6F8Wk-Kg@e~GAsF3V@cyIdk160Llwb2Im@}t#Hl=E?J&?pSlCpcJ@%YFx@ zf+1T1S^+9zw@upHdv}{TYk4xqnb1WHY^jYwx653p>Xnp>O-HO4JYYxg>qUVStz2KR zk2Y=*3$lJb-l9KARdK8d3J&f22hca~|9^#lYRMlmKyUDAIn`><8U8Gk!}+cVKkU=< zPt#TsYF&h)C_Lb8=W?hAjwBs=X8twMdT;CJeSY&Liw*FFOXvXJFg*Y(0%>SyE2v$0 zXwW<5_+4Nce3Bdic=y#61|&|Jvm15E!gWXeS)OhG62dv*G3R+A<4>1I&XlFr*TOZ0 zVZ#+p05Qms$$dDp$%zq>_?Lfd` zG!FYXh_>d-qQZk@9xWBy8>7&2kM93(TKNZ&+U;ryj+BRZLljWkp}}gFKl%$ zP*0XZS23j_jDARxR+wQV%eD=rN|K`2}|94EK<7>(aze?r1S%1Z@s0M3=`$>LnD47&4RGni&s4U_&9rr?My5#{qj0!#)t z6%*RYXa)H->gMS0w8X@~r)|IOH1slVS1@vP6chd*?_;XU4<;`gtyeJK8~4M7v4{od z2Ezazq$LpQZs{6-g<1-Taen0y&xhR-4v6wY`Bu4rodBQQ9cX1jmhrMI&5@=8>6EJe z9z0Q$k%h`yEu+cdTWLK0jA*xQAHoaK_5GTB@F-^5+j0OGWMBgv+`*}T;$@juD!KzO(r^!NU@_mPHIaiN?(dvLK0tMvo`rKQVVqgG_z`Lj$v3lUd0%W@qb~{K zY}P|qthOh*d}|on(NH2Xw}^%{<8Vh#<+_`0WlLC?FrnZK+YOJ}pG$IlN`Bzbqls88 zucv}`{rzDFQCQ{&%}c1VYl!H6yX=I@8-pt$1Nl)zEnQ0I7qNBs3=%5uY~N|etarO5I+-D`hh0xcRLzmKnW>L>G}=Ps#Hp+7g?7N9x4x{SBdP{ zT#3?{Am%iI3as#-faCajk-p(4iT66 zY+gruJebKpIVm3um~rxD>Hq2P{}t~4?l{{3L=BYZpWUq;V^5~`s9zia!@gErJ#6L4 zZt!>-kzd2T(uV5LV4LjVgKZH=q@Jee-Ei(TK)=5yi|JB96DIP=5N|Q!D5Z`%?`%P? zo$$`L_xkB1(l4|IvGJ_wmFc?dLy~(0PGov&eBzMgO^Q9>KddaECn-(Ua8Ia)j5dF@ z^t=2s$2^yy)+!v?WzrDU1PI1z^MdIk#>Zm~5a^lADD-b%@T3;mi)we!xfZ=0P^#Ay z)Ty-m;mw8c$grY>&&iX-5YlXd&;=ImkHn&n=kHzTeOlcInH4l&PaHPoh#OWFDlOILjY8TbmiHA|tzjzb|55VjXt#F*Mr6yS$Vj z2p4PWu_Tdck6CM*kb9lXel}@^>_>BM1$E!^cs~6z63QB`aY%Q9Gzm_UXH&3WsnLPL z+jg4ahr)tOfQ)D;fCGb^ZXnYd`@g%pzQs;v<$sa$2Tv(6*$8CwIho6$cao}7yzVO@ z3;DMs^;Uvj%6|m?dUPpWAyM0ToN__s%wH-?5T|vVAS_ZF(YN1W{rSVpQej-MO`rov zvlhg~?hMAJH*2ux&nNdgT!Q$9?}|QZfq80HO+>W1#D_FhvSz?c0BW8#1d0h*^TXF7!RJ zJ(OEz)stJZ9GPT&n!5e`yiDVJad#P@Dp13*4i6Exjk!`DNM)4g z5}1HMr+3}pK>wKa4o?>b%PG$GrsZoO=-X?FsAet8IxQ(NLUMVtmd4LLUQBq%mDE5` zscs{l!75^T-V~g6vuGWKYa#DdLYDoS5I@9OmBtg2tJ?%fM^h-8FN{%Z* z$!$;oA@i0GV?=UC&3MU5fkis5!<(7|kKs?!u{w#sK&>XWudfc1y)y!ViMgXyFn{T7 zHRk^!IDLyed2qxl#?yxCY(^3A4u`Axa!la5D|wXaU}-{tjAbWUlCj>IuyKbFVE#7} z@{Kp!ohvqox>epPA~0`|djT_0qbB2eC7h#E@d&!0;E{y5MsYfgPYptWaEk%TAs2%N zSP=6AWjOm$j5FniRbaF0(q>F*kCYwHCNxVynA(){g=0C>Zrw z-huyRCWm5^sM^&}&s-3aW9)U|Q!zI}iV2Ee5$_OunQ~8uU?@jy>eecYKvLvFDSJ)| zGcJBFPp)WJAd$5-VFY2cJ22=2xpm90>Q_5 z{Kxd}!n#jPS^8<=L3*ZDZZf41>r8qr#eMt2fQI;4hH-nNb+od)MYR#VNb&-AcgEPy z=Oi;N=w`c|h05Eh$6@|=CIPaO$I{MHu!Do6BOE7E$o33qkE2O!qpF4o8YxQYLdn<~ zZ57>PW4WbersQ3BEgZa)#p}rqRod(EOXV=+l`6$z$F4WQ$iBT8HdnZUoTB87^z)Ti zQXR_3;XQ8Fg+q3`8R@9>jsbGY#KY$5{eiQWvh2P9HOHsv~Jm&ptI+)q$j<}== z0{#T`y|P%CvohiII-T~7QU*asF|v-Yq*_U&DXSTUb><|?#z7CKauXCaQ5o&i(1ioS z{@O7LwLQpfM**wp{x_MfFP8jWtgef@pKM{y7BPK6FJN_aVO-CEmC5RDX@QLN*cboA zEcWTmwF_3Ha#G*)D378Jw0s?(tBP>VId-GZdUvsAawwR_5tL-4ii7H`$IV4$&%`Vs zAv#-vY7}z_hc0kT2#6|F6>HR<&-%JPt5C((v=|kYBn(z>J8q$R@LSKUsS};vW6lka{ZS z$qDdo>!mQN2R4A1AX$;g{HZhL5!`sTGD;!0FOU>8ZJ?Kb{aq5o1lD957@${*5WiOp2Ek2bOfYO}?4fxf z3s5`x|A|(}NJ#MTQzHI2pVW?Xoh} z5i+WsWRl|2<;lTkO;h*`Hm}AoAkod8PJ8V)Z|)KeZ})p*ypHYvgCO5DVmFStC*kA8 z(jQEo+qk$Ih(3X?d^^c^%SbEd)O#|#Z1RlcJ*N6GdH3-{L!?}kYPfXHc%jnZHSX3Z zlw-MBxIv$$jU}{`$H5hPJl#vNddsPQ3*NmnDQEo~%FwPlCSe%J(x~=oN(rWyW64cP z&zOCDgalTP=^}rekG6a^Q|W0U&5+RLte7Jh3b#h{q5hiJ(0UvR`@XYCv0AAmVhQ>wjDD!8>o7Q`hTI^@9#pLlHZ}sd zstH;y&ZS0k3trl(C0@9D5V)Oel3nc7fu)$|ULXSYkb5;o1}?&M-T^k`R8T4wE~RCx#zA{#o< zs9Y4G^r{VV4D5aL>~waVBtao)C!RM^+0}quHIW@~rP_?!X=ct`pF1NfgQTfnNlmaR zj>EZhc(y}gjO&myxM|q3R{GR4TRRE&1d|WMZJv#rLel(w3BleMCPHME6ZPGT*>NX< zovRI5`ggDtfn2c{VzvwsS@C>=6WSzHXdpB&5l77y_(I}CKW1TEA&k4do;bjI%`XJ~ zzJ$QiYC-Geq%ZrB%iB~&8A*9>sia$>Q887->6@Y zqCT@FcZaG$Jd{o84bJlA%qRae{LeA~8?(O2%fimUx^Y+t<%HIJw;}T^0huMi1fXZs;fh0wylNjibt@7I^71^oo`%M($&53dUA@*AF??gdV?>s0Si&p^3 z2~#kf3A1-J@yeRHDlY9B?Kfd>Sld6^xSS+l6`>??LAqV@^;~R2?78+4O%!3*w>vaC z3g@Tr&}3TZ?k>w*@=hQ;OSXepBP9>WEEm|oKAOYh;*3Z0yrl3D*9DnlXFX?4&Si>T}ry(%|Bktk`dF zk`K+N9l(pRbE7)i?81$^4z|_Ef8zb;7R$W!jbgh>nw~ZXH-zw@PDwouqD&nm)EKK{ z)cOkdB1o(z2vPD2%cZ8RpR-^qDK%Lu?mbw9b@Egb$fjdNm)(+GBx2VmICfeS;ntV$ zGaIf0_&Qjy09T+kwFC-;Lc=9w$}t*D63jGZlT(iYHob?lGg-)?qAza>yfL+cv@1kF zd^U63N2hRJWr3FkxT6W*qdY?;fFpq1w(beqqa{g_-8!Cj?`9@C zZZ-2zz;p_%#bU(G6GSQ7%3i;hx4{xGZ&C9CO~cdWB4h!up4U{B#NC?v<3zlUtWIes zv8sx0aDZG2NGATVI1dx^V%zgeemXzWm|)kuZuiqYe)*ShAu_$2*$D@C+GPPnl2k~$ z9atu9(7&#PTL`Nf6CSq`(Tir1K^F72%y|*>q{6KzqsKM&bVr@LrrJk2E;c=#4Q@Yb z8SKID`mx+)W1z)tM9N$sFc zQ(j)4!`<}0tTiBh@dUsUgQpkKuY|9Z^FW}6h4(Wuynb-5^iE7)2dzEwGDRU)qW6fO z9d{J>{lL=t>YB3a{Y(tXl(?6ymX+gYM!P2^+CE8}8>k(<6zl0!HeO_ZVJ(WdI?4^l zUbg5A(SXTTo>JJCwE;6%yBGTBTWhVMDoZ{c|Jte=*)5syEz@5I3f$Vt z61|jv8j^0mR~CKt6m*c84aOO-_eZ`M_b|vwZ`6B3zgD42b&zZsCW^1=FFads+)|91 z4CeFoddIQ|d0tXZwSxbr|LtrP2bkR4JSXJ{OT!hE8q;B`-cys9RsU(YdSt$}z0%`m z$YzvytGU4M%|E29vwqaO8DyWBJ;u0%wXLPEyuYyQGw^KttEKj$z3g8bOSt~*Mnuq# z9@BxIecLXX2x<@^ZzERBM502Z$9U5gk+%}y+QlcbPMvZ!%*Wklf>JXcuDiuY34ovv ze!UmF?#0z%zIeC)o2Np&hj{esk^JZd8v}9dri8u@al+vSMEX=zAD-iJQ&YY#MNSGh z=Dqv_Z1i+j-kUa`)C}8UI`I$~?%ZER9a+Hm#Ya_IO_|DlvH{7-<7{^IQ{@Qi48z_I zTJnKeQn~+gjVjX#dwgNmc{1@^>yYKJ=A1T&*jau00T#Qw(_lDpUu83=v;WhIfI9z zO9mKJHh*8uu0T#s77uZmI&wx$jsm${;p$iQv6HKrZl`FhN-t$5;B|%4@p@vX^Uiom z&GyNeHD$BT|tSn17wbac6J30T`xiTAko1swui#x*zB4ccf zJ2+_wR-&G~_t)V*^v+JEpNpEzU@cnCaFEsk+M(!R-htadqK=>IoA9u*;t=g1hBJXdMbK*%f&t~o zH^&J0T(xND4D)|j5}#dD+FZgom(0Qxu!9)?EG?qlP-^xr+Vi8MIg_b*%o(anyBB^$ zv$V*K*}_n*U%CshXFMO)MKH5?`TmN$v;y2MmAufg1A|GwZ-(ZR{e@>bp%+C4Zibk5j=c8{Rg6(QwJij5SW< znFf~jIstA==w|v-e#%#33G)c@Vz0fB5K3w_-|*`)GnAJN__J%$Pa%?1GI_nbM-g(u zeSL35yo(H&A4ZPM98I~X6$N(yot|23E6{(W=SCl#dr#juo08l0p`uy@ss!*)P`SNx zMFCzZq`kpVw;16R7(O9Qc_viVnu}>Gz%3UN)ri<9e*6w=Vy|omK;l#1iO#Jh--w93 z)b&H*>cky}oL}ga;-xNgqo(98b64s)j9HmJr5Lj-&#+G{QrCvUODfk#F*HcWJ6(xt zIO#Z5TU}^>GhqKckZ#oiN6*a&EzR+S6ZED5jopOae7bFPKMfTeQocJfIQ`HO%(UgY zI1>15X7&s84k0?Ugp){%p>g@$+~Oo-JnGb?UN@h;^3CPA>6W^nxj6kTtrpDY``INf z!?%bsJpd~s!b+T9yYQPL61_j*P#OjZDaJjFeNZpR*&a!-t0pnDz=C9pfL~~`)eQ>( z8(}F(Pf1A?6NjfamiPKxa@^8^bWFD+4f><}sBtMc^ga^i_pvIEvhU~F7;TI27i|C= z_-PAj9ZJ+MptexF2>*nVC9;}<-0Iz;EES%g#k?BM-Hbr!6we|*(AtE?{!?9(GS+O< z{3~p$EJt$3{s<;q=iHd3flBdsNIM3h`>sE^?nE9<_XRSJ^iaJ#Ox5sA#8$)18Io+Q~&^?){gl<=(1Lb~< zgo;XKqd|ivZTju1*X~5PA+v;S=+6id1w{zYg*fI&v_$-Jn!{ z@DMM92yvAwA%xFQUy=iD{bA`WnI=$9x`p7j=&61)?r}RPLtp=7GNSTV&gl^i zRy0>b&qzw>qN^*{jgL8u*}pI4rSTO1`DT>Bj<;y!`Wz($RB2|U|5(THlUIVq;2u}` z{m0-xA9NUbRzA3EIcv-pc0*U1>5|YHnfc?Ty6xayiqR~}KNW4bEo&p_&UuVlpdQwU zC%XUb9aqwA)6P!LC0w?ALOnwWt$0gVW>nnyIm=Umke5y;Z5OIq~U@gr-2 zn&-QykA-ahR< zp&LxLTJ}U~^!A`o>)bv!kCaVzx<(YW0*vE}4R;>ayMSWk4inH>InA*N(?ZZ)t_%xVCTESYf!z@@#*t%3$*V35e+Nh;C4@{R=1fNa>$f?BTAJq!ZaTF`~QC9UCQi zwEY#+`1XYRp`2j5`lZOw#BX3>|O2E+0eH)-Cq9X_Z*Tz#TJt@9H z1Z#YLz&(Sglzn%xzji2>&2jrZDc?4V8epNruwH5!vQ{t)U1fOD>h`O%;GacA%AI^0R`3u8g({| zx{e*;miWTZpW^$$a$d|%ZL&FZ%i~xF^MCWAM4KhHt_i=uoJgZ}uzRDOX&bWGGrH0W zGE-5*Eu8fFrVD$WqN1BCFIKo`SP;n>de7v7w6Om-`||>zV65(-b|o3X{A4$x|Jr?m zYP_H$Xmv$D1^;=z%hKr5c=tKyyGC`T?2TwupB|iLhu+;j6Mlb<{Sub~kGmhCPh%oW zdvnd=b{K9OR7L-jx;-4(#OC@rnA!E^jxN}H#hCWqSe*pt-9V4J?|Vx!+?5dp4(+-Z zKS6sVc$M2Xq6t}HS6fh(`RUdf(ec?OKHDQA(0|#TI zVTh}}Vsi@7LW+D*!`=fPC*Ycc=LyWii1dVTrloTi&+h0 zz>O@sQFacm_!G!do5#GCn|d2a_fhLV{>756&dZL8I<{<&WBg5C50)+%FCe|}>eiD- zrgK%?3SwT1@`Z--Y{$iRWXm1A+t8?ghL1EPhvSx9`6KzK>i&xN z05qXDO7Ht2W3+&`$=5l8xL9AZTUe|f%)9rcio%K7@86*9v%i_Zv{9@XkG)sX1RnGL zs9e^!@Gf3pojG z%97!B@xk~aTsg$wy)rku5Vct4w+qUTKZDj25u@vFs1XRLJ|ARtv+G|n*~eRN$Q@?Q zme`d|DAdy{fa|T7(|qD^d#?WJEp|=OGzU(*vaz4&_hmvTDj(Ql>Q$Cin3b4k@vvd_3XtOEOlP$`mP~*8aGK{_%p3A%^;> z(F^i(eHBNaOn~8H&Sw9*6TBI1!b-q~kTka(H*foPsJ6DPQJNdDw$vj@4sb3;yQ4pz zbZAe+*}qP03yLSHz5KJnSK9Uhb=-Hpjx}6e$%2_VCHDy_Yp<0&6GdZRgGyJZOw&)N z9Tw)U9TtpS8#8xS9n|C?MQhwlFcmgvxZsh1b2gX(IyY;^;L0usf&~l*G>Oi~k@*J- z+!jRc5(K5&CHyD^4_JwjWbDqR*HyoV1bMQHY)yivDnI>ChsQl~zR_EX^Ym_}Sh;7` zJMy>n<}>%e%HmMub7;4L!W4jFkN@!Sqltsc7er(O!m0ac+gly!1z4(u{e3@RP~mo} zWiezeC!GvlKvn&f=^b+tyv>zq3!^2*y!7QZRufq&JNIzTb6^z(#gDFLN{LfTA= zRw<9BZxMM|J;z$$8vej_gx8St&B98jLC$}4>OVOCosFu1TOh#bnYJ`~U>qs}%YWh6 zpU`?O9JlucvE)yTV;26k&-+cU-@hQCzc+J|5}Yv&2yF6fr#^xmZaOtn>TbodkF;D< znVX=gMXg_B4aiG$y*3L?W%Q%;>>O~2pAy5?HwwB+;hxE(TQD`lmCjQI4?r? z*rHr^8L5eQvmoC0W&;vn{?ac;M%3t#;KdSkV`7Z0I~dFGjJ$=F%ilz&(izt6P-w|_ zVTwb8UyUG=qD5%guausMA*0Ea7XIF8KYY7nA^9zgPLiTk)DA6ayS z%Cn9U=4n5%QzA|Zbnn5RIYRaNiMbTrNJ$%w7U6Lk46;?_23IY7nS56@=Q;1`_H6@F z2jKvBPePmKFM0NHfSDbWAB-5~(C*LNSWRxm~Yx>heb`ZdT=SU8%UDh}Zr9L)JNPXBIX~JGO1x zwrz7_8xz}_aoATfZ)>Cm4$@Ocl?XU}^N=9*ApL3`|((>QV>b~ZJL*WO6@CB{gPQ)_mFuhCEI zlkBSU`F!*}#a8)fE}9SeVroIxMJ;(BI2j!BucIx3V>CgSBtI|y(&~1C;v~C347h~O zzX8PUcqunCr86~Dp>8!N0(-)dl_RKnAigQ-*mAsxyjgZQ5DGd4zVVTa3Z-{sjvOe7 zU4As}-Tf0nfd1MPD0N2)2u%pHhhd6`hE@ud59j_k>vhXsw=ez>+;%NV4vQ7xWjchu zFK6k9*XaP~?E#wYpe*L^-&*43CbkMYL2W#3&a}*EGq8%je3#3~Zu3?TYq|=XF^+W> zgGpx&kxEA4si;Rmx$P)qQWCCmo*5vO<@U42H%)m%G$0vC3NRQc_R;l-9GUF0oHrBl z|9!pv@tdqy0%OA_+3HCW|9SQi#D3)a@_a+7(dP+V{_0`YH9wb6TCdsv<{vwyQ`H}g zSFCY@31z%SPi}=vuR?KqE$)i#DZUygHV#);vT(@xV7`OgKHeA@_3%3vP%z?-&_o^D zwbOueWOH;z*7%cYE#wfNEKmyJqPo?1ZrJMmT%Z?qK8Jkeg_0%{;k~=!aV}sOW7G0E@l+3X z`LCI`U7Bpd>=tGR8RQQ(C?55MbCsIegwkBqA3%yuB_}l z)^gA{HaGAFBQ1rQx^B?^Pg z5)XJh#$4^8;kYRR(K?xD&{T_@12kh^4M{WxJC!UT7Ne+8ZbpP`ZvT%j@8|L_gc!WX z_Vwg(=D_>gbBIe0>(RSZq&qg++kdKD;7hCNJ$8ifo)m!l+LL|!1@FJY9j+Rc_A$V; z`wn_Dp-3?J^u6|HvoTvdi*c`wA~qX3O9r&0JCc)D^oHtu4F0j3kfFEdcWfoG_9=o@ zZ%{99-pEwG(G3aElkGgXI=Y3Dx}Hg^{@LDAGu?!W&(AqEGZuSl*_o08JeVmVpFa*L z!|(z#1LSl}gy!ngONlZM$x|IUvRfw^Yfi2=h7Tzq_8@xdw_eA8yPTX+d8apoPo#q) zj$KNxfaNn?Phg8}3#J7-P1vvS;!ybS5t`ku?GKs9Y1}Yw#Q1R!; zhTQKQ>d3$262}?F$?mnu`A>Fu1m{lr2+yE{r2jQ~9;cZ3v8Ew~9F0N8e4H}iNP~?Z zD6`j>0q$hdQ-#ZLq$;y)oiN*$a`9suLJ$QLc(n}di@aYdh*%q1HplPTB#=iU;SH0< z%P<^!{w*$X%tCM_PZhW`*K!Ki;!pbs;hMYi6zk_j-+<(+D$9RDB?oR?QNX|)Ej;VG zHDuI_{=-fcpg2gt?zDuaD^Hd^M~dm@9M86>?b7ZUIhj^onx8`k5f&~$60bzDW+?1t zXjkDB2U7*lF@i$>Dy1c7FZT@kg^t_;^Kh~PU5Z6jQcR{b4{`#+Eu3?TLUX>%-k%U4 z#Y9HMa&-E59^T(m+L!4Gvp_O3C-R66X!YFw_vd8D)9L7gUwd9|MfJVrohPL;07%fs zp}(ul$QyqJv3h{A?5<8vI2^}awEN95{&S&|AKZL)DMnTc4FT8uLD9avMnh;+!+IlU zN0F%1XJB#O>M{`l%$=v!G)J{#Gu@!Nem;nbHgC*ozLi|o7a6XlT`Yun@y+6YY^VQO z$bHt?c0^yC#rc+R&o{DVIkrg6_tLFoZ>O@p`$<8&ZkIZXT_8UNzD&}LOgf$AeWS>= zkzTmBKNv0tkoN>Yx(5_@5)tpA7}&+?kdwUPrIMrc0iAnO!xWGVs5xnsZ5|-}+*a0$ z{01S7VTik>5gLr#LSxAZOSmRaS$F~+tk+xy%^^PQW-ex%x@*ATN-?j#h)8Xn+D9fq zXv#xcjv|CA9Cj19XpWoZmW3CG5}5KS zs4vu30Pv66L8>)m1^k+_42Kg(JNi-p%au(ZH0PBSN3l#Ql3VA8Hf0?ac<7wryy?^> z>Izg?Q~Fg*GXdOKzX`c;ES;GjSWdob$G&B@q^UV%^`tW|QDI0%^&JfLD5 z3n;VYqHY3O=+K%bV!rZ?^h7OW=6q52Z`|dY=f&=&U2kfDtS`C`wGd!U8UIoKT_$>W znr{h5CC!o&F=W{kLfT z-$90M8u*8gt8uP4j9|`fKf(_8@PaiDTO;;o`A@;L>05YC`9u|l5LB4vJC6zBPoY6I3sw3PXK1Ib!TP3 z9&Vi`uP(0Vnp6;eTL4TDm<$icuKfLdP_P%S26tfWk7f47<#G{~)m*->kAJnS{VIGo zCo5tLbO0U*%@jH=3C#Sc8`{{McMLNEI&T5;(uQ?>6vt!m{fA1?5AD9yL_n+P%RFAe zc-FY;FskOO8g`egd+%Z!R5N53RCo$g!85i@A)9M4ol}5kE zqfaukw!*4S=`fRoi6Mlkm_f)m^WhoaNx|d_OA5u17wV+Su5cw7s@!+AVo^KPc8&l< zMxv5J=`F=6{nQuUMR%xG8Bxg5LWBo*+f>L>MlUeBYxAorSJ7L@yoMOf~=qE>##EQK{t?zM)9e5Y}h zhxM9oW0UK8rZ91~<;=>Np4PKrhdTYW#@BIDQ?I$$^l3TCs(Va?DylGnIQ5<=wz~tf zdo^{ogE8ANn?=pwEslLA$x3am=Z>x_>n~oel7{b{=yXTH&CN4`cn{L?vQ*)S^A}34}V@liyt;Zv+h_S1- zI3_mOB*adH`DxaF7}5CccD>k$r=1+x;Z=TMk1O@LfDe(kg<^kmpZf*|AOyELBq@2q zW0RIqJ%0UI_52lCeeL(}dQAw<_r}}fBe3LBCY-SwHPXyk0@rtJyAf|d|1vT$Gnb9v zf!((Ju&^JX#>dMDJerOg=k;K*a(_`<7o|_b*v$vI8x7&T+qJ!)zSzOUv1>NrNmS*j zcYAB}uk1GMNer*wCHm&R`=etVY4lt#mv-4y7ZN%X8HvH{Z+ja(TeX8N>q<`RC znD=}~0JqPnc7E!=s=sm<4@<8H&%hDc+7uilrLZW*Q+OO~0 zZB0r;a*qw{==xayBzpGDu;F{rBl8B~`1#2;Jnv8Y`@w35W;HP12>bXO1NMK}|JsaK zO^Bxd{?wuSm3%?>qxF9%r1MgCKYG!RzJuI9esVahLWs#7Ux%N&FJZ2fkiQuifJXbl zd_6)wm__#vWgaO2XCv<&g`W?eHVvuIl@x2+HvwT&k$G>Se{IHQt=VzCP;UaSwl)6C zf&oz#u+!Jypoy?;{>QEeFs;U&nC;|gQ*Xb0Qb8y@hVmomOitdA@ms~GKwGS zNmw;}ZXBqTUn{oxT61JCK_3)A!cF%-?nY?*O5zW;K&xiWrFSdAt6tO`?6IjD#fra( zHAlg?y+(7GMx320#VY)wE7#{XPH8b?Y&+mCo(GS2MFVFaNsHGSHDrx3qq&0)QZJhH z!KqvG2Hrn`#E~&r#&K(yckX@B=8IRX)Y5EEn$vvX8K+UPYGYt39Oc+2&}GePaqR{KiiDEOiYIMQ^LdRl}xVi z{$r~5X_Agf7=wwM@NG6DI9~)~>#|nWYc6#vfumQ{yh4_pZ1@{}_ zc1AwbqAXkNK9HiElmC8~G(J!1 z$&>wiz1UpHl0XW?ak%(bIc(p`N~&_&@q*mpU*x}653_gaf6w#shOmc#xp=9vp9da4 zEwlL<45oCt)Umv`*BTFs&7GeX`_#1I)(oN5zFhxq$W`jYk{&^EVt>-u*$Myi4_Qxyf2ztybE#yZ9T;GoCL()`I@%a5y3zc546Tzq(}X~b#z zB5QAgIab!QwVqHK_{f!7LW#E=B@2CYmN|s|FlRb=7!EGWF5Tv?fa_6)19cw-r~~)8 zoCki7s^YJe#-GZ5SFI5niNCw9?XfvgizUe?%-AI++&SX3*QI1U7%QiS)UkA_G zxvZ)Z#B&;y*ty66Mkb=V@|u@HUo$}zRMa|aZxRHDoYCz97Q87r$+CNAGZ|KW-wwl% zfAu~pSOH^MdQ!hqhcb`FHWpKU5$;BRkv~q(DCc~mH`jHTp zYv8@*9YM&7pB|4|=VHv1kTTI9X?N7T0f3NUscZW7XaT14F#{cP`Ad9Uh@UpS-5FiJ zy7K-MH2s`cnNA_7xYSoo8$SeDX?3c@OmFdfb6{|qbiU$!ayekU^ej_wg_pZusC^2& zG%&`J2aqEr$7TWNmxbBYQxY^PtvJY+lI^)ZBg9 z@)nlt$Y+AvpBSu%fzCa>cp4YzNEV3J@IqkfqHHVNh5M(e4HwwT_n;R0-m-4I`yS+qnE3>W#$sNh znb_2b=D7VDCSVOy4f;uK+NGE+cp8oA z?s&G);`hytHh^Fg7(AR+dFZ_4`owiCLs?qHb;$@8}W_0F! z0^t8oQb=X~uOvnFq}bRNnEtyPTGmQfXk&tfkA>kw;-&{VyF&C?vffaQD{Q?f zDdUi~7VLU8B)EQ+&1T$mW`epa5ErnG zJA=53Bl6eB1{WCxD#Y?+|8_}szv;k)d5fa1UP8n$;T8b$;ZTXP{*(b8%& z>I*j0Sn26|nRcIB$I9H0OxF_Kc~u6mxQs+R;9P!jnDLs z>gumw23rooNJ`3LVttS$NsLtN@K#|gbobV0az1g>)X)Yn&lcYRawXD^{2rI2#ok74b+E{arh5xvf8$DN8K3k{r0GdoeRg~1*G0{ z$CRJL`p~4rW%cGlxc+pEgVlT2c?G@VW;C%~cN1mxx6L*Fb#}LvB%-aXZH)(y6ZBDL z9m$9Bnv!2>ajTN9DALMPw#rvS8ZNnU7EkQ8t)oB3gOO^1{}bf~lJWQUUkBJB)(Y)F z2CQN3L!~Fxu;1E$W?oXi3~Fia=iZ83CJ@!;5ny2Yiiq8?hHsBHRbd_4e=K@E!;@!= zX{#A77i%fSO4N$>YhecuseY4PSVSXdE<9ofj zgQMaHTKdOg>oUaDi%A>lB2dmbLf1r;I>?!{Yu9RH_}IwMHXanwc@{+2L=Y(YZ8PeM zLT1wzd!+q>ObGcc$@5P*ff-q+sFvnKN=`?0oH6s$`e7cq%*pegEY410*UbQf-V%6c zqN==QEQtruNcd7!5uW%CYPys|7}+wp#i^!vPRV{H=Nw5F67rb@p~fUwX3nq_zUU*p zDFT?Lq%cusijQeC0@xExV+%eozrt3Tc;d$RoC~f|9SGF2JGr7m2WSY&oAx1a8rYNR zs23!}JD5H8EM~-`J*fp$4u5Chl3Ndbc4qQFnLQgAsqI0c)l()Y8gan*vA`S*3|duL%2HB4K72kN zYyAglYdeEbDHW*lL>t`5U1!ChgBRYK;Vl9g0Z z7(K7oCi+cc<%CC)l*G=qgy-JCEB-F%Nztq9=2$bsDvE{7ZKKI4*C>UAf!%l~oY7?m zP^`3ZuEVW|=pgB@bmYibU=sDd@$_^PlwV(w+PCNG)Z!@`?wi6U`$0&vzW z;7aa&VJ<~MHh2cLJfG?s43Wa4Aq$Ytj-eqr1;x6(;KFoGh9M2yVIYGRLm9~%7P95P zX8C7MT~?gg{x+IWX5x@Z&<`EGc!+tv8?P`KczD=wh-h zI)5pT_+g{tgvwPwN&{d9_f}Bts88WSr4#E0F_DXhDK~l5ZmGwupbK_ugnmj}6FX^`8BHTokN;i~2 z{;CD_X}tjk-pt04PRjk*5BV` zHa$tL--XTYHCt(l1Z$a|j=dThrqs{$#>LHz(L`1ytkHUYM(WT39O6Qtj}ragx$iDa z6GfSt*+ifDvK6v_xIN`Q11)^G{26>~3!@nYlUfK^##pKA7c`24##0I zV7cGU*#1qeMRc7H#K~TfE-nyVO0m=#=EvWidtSScUq!u|Wl$!13kWRy`NsN?g=9L! zzGKuvADU7bZsL9Hx`d%zitY(_1UsqoaF)^0IZCV8UvXD@T{+|GHk!_9`Q0qqm0>#f zlBRfOz#@AOTan|HF_3Q0xQrNC`Sux2;7#CVohJ&P=I(Lr*%gXY$Mly7*MP9Wh`Tqx zIZmwsA}7U$V!u?V`cKi}>V1d-b-5)t7~5kJw8|MNFu-N(JcPXfNrcSu*N@J{KNaXs z2$C~uit$LY6Ysf|p@}^3dOsO9;0d1#+va#O&_#y`uA9Pw!5wYUhhz`1LE467^1~dO zgVn_LC&x5NLLAMdJ2MlO%l4G};Uil2iNcu-)D@6`$o0g#p@X@Z{xSV9<;#z?P1!GK zuq-dmbx2!=*b0pycE|Pumx=_Nu9UH&8ehoVC(&jVC|#$HIhJQRGB{ATZFR<9^h(NO z!X(O%$Ws9aB#2TwgU(Z#M3S(T#4x3m!g~IhPzek~YwyQ{39hK@da-B4@h%180|&a> z%2aDN5)gn9moKxXQ>PL>JX6Gah~p z5!I!5F%5=WgXE@Jq~$d_YU_>S=(60j$5?1_U@-u*P&XJl4V_4uSxr7pw1i?!4K;X# zA`oi67_LUm&WMNIjC1C33CiBG+T5aEVmu1$@*HIGNjY4MDHZY}z1q+77-0Hj7W`iETb>mTlaW}N0>8T^EudiQmZP~yo>IlzB zFtz>Fg+rk8ynD0jtA(Bw5Za@Px!NYpNNoH{0nj0oq_%;);rbivQeR~O|AX5Z867j4 zb`t7MLzY^)6fQ9W{^k4x#Zhc-)S8Q%xcqS&f1%!JLDz4eOGQQ?5cV&bAYvD=z3(Xe=_ZiDvejdv}96Pw@ohA|eM-RiS~$?}wWZxh=zx^K6^og>(gvQ}LcY9jaz}&-IW3fLY+$q{|EV^`b^SxiMoxdQ#vQ-`9G=59qe@TZy5Ka6lV59Lm{ z`jGnYDwNLJiyw09bxdJGw{U7I?y#=?;7r;-KLD%B;Y7S{tLDOTq$>YV5L|*cy zi$?@0xpd_oSIkx{gNF>$>@N6b+SZOOO4eFF%Ksusa6Z##r4E;XKxTB#VmR43x=w6}>tBz&Y* zkhlsKIX<~`>tDa!m(&JRIPU%2-a5cm2qG4*;K{kZ<#AB$=~i*dt^QWc;0O69v~cl9 z;#*`fadOV9rcvX{1CqA%#j$U${#u^gUDkbD^o%^Qs!Lnf$SREPNqe+aX)8}_gmb0E z9R8+>N5cV7rE=zqcpOm!zuM+Lgi<%PyXt5mr_~aG%g_5;=C&s0STFp%{<0icd*C^3 zy#|<;Hge}Lo^*hF_JD9Oi^mw!rFyPJ}L7qDV+xS(RoZcZ>+fe&R*3U?q4{u%6;2rdqCO=&{&7k+~DTizdcYQ z$fCC1Jx86SiBkE$`CFmTG!{{PG?xWLuw6{#oAPEaXkbOq5nXne`9uuQgav!qMJX`( zRMW#uqtBa`kd91&x}1{iwubNd@Pe@un9JTkC>t=w(znruXhj^MFtu|RtJ?ZpMvv5i zZERqQh^1_pm5#yitk4*N`R+DkDhyr^$HCw#Dxf5alsedM$k4bvIlwh=0_HB7Izko! zD}Mxm6)iGr_vhz_%Q0_QIzd3=e+sztI^2j8^O(}2K7<$8sd{CvxUsRZa72s{Ct|Ub z$NYBt!5IQ#ccU1)mA3Hl>6jLp5fvOh5g5O{Q5DR;VW*KR@)p?`@2ZkT?}+nM$2<6J zuJ;tZF&rsvOA~T|LG^y0J*bB|RY1?i`;3}p#AqBbT&E~p3jNB=r6YstWn%vFwJ`RJ zzyvuS-U%39_jp9d2k&;&PDHBI-D77tVshF21YGd%OFpyy5%z5|{o;lHRX&@>%+TJo zU03ZPV{~twX}#{fqf>#f@8fz|-KNUdz?x!tT^hy7tNF}et4>RaC@4UNYMIvX5|3|n z^^5QQC6muP1!!}8eV41*7mZ*)#dv>ccy?WpL&Uz%F@9fhY-}uoXz-r3M=w+!8Q!Nx zSZ2ds!x69GSW$Vy$K`z{i7(~nohE&6;BsnNkkyAq!Y>^;BYJYX93BDfX6=W}50*WYE++DkD4{|q0l&r*4;=9i zQ~d^77t(p9!Dxn@+l%6coHMhTBC9im;!6FO+{o3y?jg=cVz}Qn7Bu@FnWDiBc1KI* zz`T;^7oQ`rOOa00G zpDuuhTS%@tf5wyqI|XJw!Hm{Ra6rwqfI#!8O6$}rJ(QY2E>B6Twk#bx7fNGvwkK&0 zJoz#Z>+v9VmRPm0%y$rZYK}dSkYQHaXBgtCoIhPIn&ox zI+w34Tg@uGnz2x#?LbYxmA$1@2Mk^vUf=Al6k}P4&KiRs^^wDIl=T+~sH2YsF$KM` zZx#ITO}R6cG4j;G8Iw@1xgod0q350qfbf;RKpS7DG&+yaEw2JMn%KqOf~$n8mo%*3 zdVvEIs}4IFe4zgAQL&E8w7ZXU)zNh$WH-|)M|6V+vEk{V@eh%cYKO~9gDoM6kLi8L zTiiDrD0I=(A=I}|%L7Y8u=S+@aKx-u^D|q}3BPKXz`iZXwC${B>&~g+Mr+E6Ws`iS zg?P%LD)6%{Et5n64EI0HE)gH_YXx;7aVQ9a-u zrJkva`)6El;$iC0W7Wi4r4-}|v!PW)B?C_!gU^v1x)eelM*ng$Y_=fmQ4m?6dW)AY zu9x!%xxVTI+3@5=#f#$PWSEqkGpPd0{aiY~K_Kz_gU6O))bEU7?D?;sHVVo-6+q#A5GWG)$;FPsQ>lm7Zwy8$jWmM%7_a7P~8OkJ1AGh613 z^Fv~9*f&%j3N$W@hgVw>C*RWHfFDF-TS$f;+DhbQ4!rjXW;4Zsg|%8l%ZN{hg+maX z=#yegIwYmMC)@JQ4r1c^GpI5%9F&C_O;`9H>l6M8yieY0{O~Gy+YifJ6c-yB14~eN z8!HB<9iwX*@6SR|8H$tK37>l?C=!S-VW1~45ve=$q$A163-X{O76Rc41gZydZd!iq zt|XUU*Hp1!H>vvNUb-l$a5Xo~uBu}n+B-s3wH7ZVv(6FftLU8AVX=cYaoP#N`8fBL zj?1VBAT(}jY_gC9S2*jqo5tUtVRFpuZv5SWN&9Cw<%&DhAeu1p#ZK4o zVtxfnzLVC_374U<{XBwskAw5R8#7(uqp)q+c^j9l4f z{*Rj9Zl)5H4}QOTNh5!!c1$yF3e|oJP<>hvKGrFj1?lPP9Kt7rN8mdhC%fz7M)g>Q zO1A@UQeqZc(|e7cxas-9R8r%O9RcPR#8bl?trbi%>4w7F&?F#3m{PI>*Gm_}D_2@R zv7J$Xw@!@ngns9Fh9tojV^?gJPpgMe9tc(R#xP$h-JX#9{q{Dyby<9xc?IPyfptYF zaz0~O@W@zgihWCZU~2c;6p%_F?~IE%ss0)cuR0aW#4$Hcs91M(rg-qLSS3M5osp zAg@)uO7r|No-~C2in3Snz8@=V5zJnlW`_H0B%ZfB=;7Rp+L5T#Y(|SAE~jco-AiUZ z&-3Cb)2||UHS~szICocdC@c)0xOO;gx1+Q+Orv%r)I%gz??9eCdZ6nUm(7jv?loxW zW{t6E@2}ur$uuh2O)*^8fD0gYv2*Ys3pJ6sfpbXYI1b2*V@hNepKNSbFATFEcYa?v zkTFlZuFN&yNPaa0<@rL+`Lh#<)^^8=*H731c&AEA3;8a2!=#4drxp4> z17}|}fJla8vwun?F}_je8l-Oz%%98UC;rsw|yW-pro_h3i4sxkdVbFwYEj z`?y+O~k##g!w;_NcE7d*4W^D!Hw}W5 zO2BJ|bTNUB?NGY^(V0u=QxeJ#H-o*XdMu-w9*$GRpdv4>&A9?00r$z~_r^G$&efb6 zf+qn&&u1J2E0LC#rlO};*OGulz$*h1{WHB@h4I9VhKP0$T>O2TTh~Gv#|fODP?Lgq zdI3Vg%0jgtASHhT**BeJ^ zVNMn0>_(F^ymCdzW~|}XVAiJ36=)JfjzH$01zXbDSYsMY4%^S=l%>u?0&E?f(n;Zo zHk4&#Jd>jyOqgWq#4ETCFtwLLPwpk*ATlTFK%|S@rlp!rcp|pTih*FlzhHVAb}5WEyVJbd~%HiXb-QB(w^K zq#2r1qg!Hr%8>$+h=)#hvRON2%0Jt|607m%Ijn1T@wQf>Go_QqUD zV=Pe@Sdo&ov&5alcN1njL(_~4k`mqzJRie2{hdS=K(ZRUG>PzCD>lniF@c=I)ES4ROUwQ3@)fF6{VRqCq zzOA@ag?24^e9&zRtXO0a3G%-|KUPf(QLalR)Ae{V^%>OC3j+zmkv>E)0h`|A+!*fq z05^os{gstgFB)VAexDeXpvCt2RI|N#)n(*^LNg874g$iEa1&KiygL_FHCb867p2~* z3U%JN3^n-i%zv6cfAeIkTh~_HH;kbx@vW#igu#WKg+8?vNO*BGdx$Iw#y(*go2puB zk+nX(v@RcxtngA3>5fKou#(iXo0E2YEtG0Wa&6-6sGPv!T?DQy)M|Rr#lJ26?<^e1OLjc9timk1vYgDC!Q$a)Dp49 z@%fmE9!;TecHbD{9TXTTJcL31o$v26iTzSn8I$Y+8?^$08f;VCi4V1cN@P`SHha)# z@a^DHvlPm672TlbLje>Q{!3&rT%k-3O=p93vT>~(n^tf{Ml@GPXp$dC<^rizSt*-2 z`Bw()$~YAP%!A5j=GVGPJNgusD48#xTHw&F8O-$TgJ=xh3wIA0NHZF_6U+%3{ECbB+U!bwJ;7Ju0Yqh&+YbD| z^TuhfUB5r(kUWY4So5hUGJkv~|*}Vl%7j|$miYn;75m-oy3IXq1Jc>5Tj->?9G$bLJ zu%vWyB^NZ*42t3M^fY3qr+Vu|wkYsV55oZinqWT67HhG<6S-&9e zw)k)9`aMlh9~_|hxg_D)*ZN_@=j(H&1}eO5Kv&RRh7Hy3YEu?0U^uA#0RmA2EXBjq z^Jmhl_8$KbLC|3G3GX=^^Ejtl^~l%H(AAQjB-6i;*<$oyD-Ei{z8_BOb^`3Jn>wM8 z(2+c;n2|B3VAYvZkB6sr?x$<+stl-WG}U%^gpI*JlUM~`HET%j<$w`7-4PtRJz;Wk zNSeazLJyG2OHGEmCFJC|V+ebS0(9ru)^ie>z3IRMl=3DWuR_jH8qiABe~@`vR5m?1_4Ei@f$3Kd9dfEB)w+W`D)+@U@a7EWPRi-#U5 z-x*W&M1&{>rzrl6gRewkXipq&`ds>5IU4@0!bZ#|k%GP!cb-9)^UJ<(bdDD7Pdn4# z@zo-7q+!$`S_zBWkdrI@9$_fD1UlFyY13m=-VFqMRk^If+%=O41ZGf}7l&&{mj$F; zT1P-sxhL9$BaE48m)sv;Wux*$kD^?6wkcu6`wK0Wl=~fZ#|2x`5!T1!(z^7^p(a=F zxJlOZHp;!*hXO5C#xY}ybp#N~mOk6|Z!h@2z)E8V0xV%I50q zCuQtyJU*s)M$c5Ur2JbSByUY{QcroV&?qwE%#TB}SG4aQqjC~6x1}XXK$PyF|FVbV z@3R++_9yoz!f7*ej3c|1VBMVfalTf)@_`X>;%rO{zz7?ituGRBuAvbdcrcUyDz%5S zTJZ$Tu#)TGI`iq%^`j?O?-hq@mb>@6n?rVwX$K#b-(W20BQV3hjOvOf>Ftdr_-sqm zsRZLyVTseNFP`V!ANxWA2ej?Ui#CImQ7td|keK~}J1c*Y$OSWHisGB8-U4yLg6>bp z(K`Dyx=$82VhvjbF>`x?X@uiFuu!teBkh?TN{PP5mc8>d{HN++3u<4R_db_mhis`2 zRuT+)5u91!tzArHW;+gW0<*%f^v3-)VL+l0Xd!(IOed>12L*o@{%M<8QkZpR6?h29 z;X=8bUH-wS0}XY@23Y!&2;IOD?Wb*eQ+3_&u7lS1yS!)CwjHON#p}MGzXiweZ`*O` zg>q;*xWvc)OB^Q@x)dkIkQfo?JMB`*=PFey*Z7|7B|o3;>az8}MzWmpOaKA|Jj!kE z7lhhit`VE>-A6!r)x9W@}H)e!5Lg)WkYVIQ)aMePuV z%sU$?&hqK?&HiVowwhjUBe$P)K7TXQW9-h%zq~iJL0eM# z`J~aoKcG8Gu^Bl*Xs{h7q19@TJ6Gas$Rqc~J!Az1Mu}WPWmC`jPARPSz-1nd(;%jx zQ6W)n%_`&|LB48k6AtAeD3P1ksE=zHZf8SN4ZYMOqOj*R$}P3!wi9k)>uk(qoFwWZ z(O6koJUI4Q-wFu$5Zp+d2I1NsT*ID!_4lwHJpeOI(HhDUxrYj5w^1cp$q-qii*h^t zp+cgIcrw9mWZ#6iI~xog$0nIK0rM3I$#HE${dNU(1x&+tPDB7vz9bB?XVT=KIE)Cd z`Hl|GT7ql>o8o3q-+YsCbylzdxLGE&MJ8F6qFC?Hi0Bp;c=}IemQvWQ%IsUrTO~M} z-pnN!ps@e}1q}@_34Mh5laLZmGWquMP+gG6P$1JjjyQgzT~K~+iTxJhLR54J%W+SK z%Ohi~r0eGXDpRd|)tr*s0|T>6;TGOdaUm6@%EL_^0nDB)@2}JG{B&6529%_wS72r?Ojid1Ovq4YssjFF}C?#;2jl@3Dp782HH#6twM6 z3oBB6EAKQ%wAR2Y=B*}GSPinx@TM(LP`i6%#K73cjHuR9HtNw$Hq3hAs+;yNjHu43 z5@W>~c~US^Hy^Om=C*r93emP%t%DQBs|ETIOd!Z6Z@+Vb5Dznm_Eq-*_bKj~no zMtb`tSB*NDKzw4abz(~`YWtctZO{mfj7+`cmiTyg;+t{nX?@tu$<8fL6$5EQl=05R zcIJb)a)5POAreu^=r+ajfJ4aPx!jHu(d0x^!wHqOpa{e;?Ex+(4r8e8hTx436S7h_GZ zw07TvWOU=urG`k_B6D%k+_%M3w#j28%8y1LwLMN6r?j&CaCTcK1p*soY62W~kCs39 z+3sBD_*VGlh2RsD6FxhE<`H?(GB<=SQwW(M?UrI8LRyo|C6SGnEpCS5tPMCd_|53) zjBG|PZOiErcTTY@S&>oa-84@5br+;&x}MWJfRoaJ`9-tQx>h3|^goFV?YyXgwUOH~ z8~0l?7_$!5ttKs~{v$Ecnk>Ue+`wP+I)iSU!{Lbss=RsQU-;kz@~jXMx;==BX)v)W zx}7&4fmphk^=P|^&076+?5EnrF}1E^!O1IB5^uYs|Drcce?{MbWg8z0(!zvOYp<^S z;u|*f#cB(vJR%nA`{;%`^mejGqsVZSb>R#vD{ua+Jap?7ftj4`4obS*7WIY8;aSqr z$yqnH@}3);Hz!q0fx&&=65YO|vz6j4H3xTRUZA&^@Cc(5)?E;%)5WpJ0{x^GzgI78 znsJN2A6%luJC3;?$kk7a4&D6)&8=CzCX+I+Sg*Qd!G?eD+jzapdB3dn7;9B@R{?6a zLN}$U4L$T*tM$U-Hz{wX$)nbP0ir-%zcmRgV7hQ_aXa$@O+AfsJL22zyc4A@g~c@vV+FqT<1up=IOs zBI6!G9eKl>$VSTfr8ft^kNErr;Zh;xZ2C~DXDF9}3HUjI)EL6HFBmFaiOGdielY2G zSLj~D{52qh!>foXZYD&uA&e8bj1`1gO?Zl6?6xN-mii2!i zyIx6O^hZjYqhl|!TRzTjm`N#})8IkX5OT&%D0Xruu@L$Blq8vJj{bEvC^n;;(^UkG z=?8nBk<$)6o3$|Y<9dYs>nMf)U8IT%;1jBuP#iTQZKbfV7Jqsv702H`Pid-poNIlH zqAea`%lMM@5Wt=L=~%YT5A{|x$oc3aWHZdTpz$zkbU}uT8(wpE!-QE|kbRx$uiuZF z$EPB&_fqUBbj6^6K1!Mb_@oSSNt)6I1pr>A(3`lpui(SQm@{>A2m;Ies)(;eErCK?ElshG@NxH(lVoO#z2e#*~jGmG^ zYig)Cg83I|t8HMfE8lmE9jRPpEE}`q*iG5~Cvq$GDd#k82%zs=UCOs)q@;Xz`5l#S zO)*x&=G8G#abt!oY*aTnTiL+po1M|q=O@8;(q`;SKd5j_80(Vfy(%jy!H@0AnUi3r z^o$;V$b(j18jeJJ4qVi*_!0@)|hiYVEvZP}BGC(>~Pja;9BY z4%`y%s&J->+{xz-efUmB{bR(Xje*K|bc+ep^PGPT=Tl3z8|PcD+wQ-ILZ#tZ`SxQ- z4Prw{nSK2nA_I8Drl#SR+RV+0A*_M7yB;=;Q;xOVfB*faR=56Kmx-7Dbq!aLT=&Z2 z*I*odCgGW1EylDVy)~=veb3yt7E38SXa8%nx&Hp|HhI{(WCcF8n~A@^_KdP=q2<6d zUswJI@+gw(hYx3a>c(_240hwY$BA%6WNy|FnpE-U!CksWRGPbDKuZq6lt>3{Aj7Ae zViU=qHgPTF{38@Cw0CGXdZzh7mY(iPvCWeR&l!6`>j) zWvmmKriYs)o*df%PXaMVKl&#C^V$!pbeA-FK84`vJn4wmOixcLpvqt>pydMTe%Z{g zC3E9U_-D_&-OF={&h+~Mb5l>CB~(OVUM250eGg%}dAwg`m+Hrye9Vx2YD!oNl?B$i z79jQ@tTbn-yctr?6FRaCl-$X=~3*)R}`mK!ZG8!!0(KM6_B#4qNFF3*6gkmM&=>GeI zdH(GhSsh0!U`SX#1r!MY5)dc%L4(|QF(Dd%f+{mJUj-}~0U!aE{vuBvj&WCX3kvWO zqX8&+7homviQUUDr{dt>(s7TOEfu1>Ahq>HjMW$Ni~JjNP4?I`*Kj2IYYd2VQv3^l zb}_}g@BKp&fv+tUVH6VF?x5dVOo|^(>h4F#a{UM|zy2crdg>Vk_(^(N9wc225XyPR zDU*lbK=NgbpD5zBuNy(C_!E#PM(RcbkMb@6P$+|5o+_*S0>aC z3GibWvO76A^SVjgmOPN;Y&?{76VG>zvn>G#ue&BcZ5fgJyz_%TzhieFQu)?xd1&_} zZYCKLDyhk9JFliMmR=M9v0y1?zjX?ED}&mcc;3s|jaQq^kMMMp^4=sxIrDT|h9c*M zV|wec2X3iuZY%DexaYR>W4BNcwl(RO-I?2puVX~y7`YLy?^3F9$MA;t+ioAmc>bHg zpO$WiKCD^at@rOU2HJ81vip|PRcX8b9-_OrOOwU9yjHe0WSqH`(Xh){?x>}#C%(67 zuEFWuJht-O@9}RTE?jp=Och92@gk;g-i?pQ68mufA6Cw$(o`Hd)R9v9y>on90k3_L zh!x}j0}?Uwxw&{Ipl$tRnlIDm+e+XEc)peI_QPua2W0N28r0I#8qMai$8Sl%;!iaN zy(m7j5Lw@nRk0=WwP*W5KOXUtv{bdh(MY@tLGc1-a~Isxk=~Od1K&(iqPmQVe-ojqx>+D(TMflNmz^b1AShOCT-%QJ)mA2m+J+ z3LI-7b*>(J>PK;LmCJC&a3;1K%1czl*Dxg+&RPiLt}^8c-mlS@6X9u7Z9a2$42PO) z3Fp)^g1rf1uYE{XR#w&_;+q263kZ~9&w=Skd9D$r_F_5w2#*|Cb~lTDsCKnRQ7M^d z&5CH-L|`Meq#6-=B7>?1(nM&Aob?l-DB>nyP}~H#Nym^&dlwL-0Y;(va&SwF$>+Upx4Cr5fFG5Zf`AsRbfAtfVIYGlN;kS zYiMLSofJj&vb(4WyAj~0QTrsEyo(V~DL_!-6+ML#k6cm*BDhBEdQ49Z+}$-`iO{u& zvGKpYMZP|$TlqhCcR6mJyULPU=wc2S#N^-He~*uz8tAEkpK1-*VBFUAxb7*8x&KQH zmmOU4;onyN21`r;(!Yg|fR*2qICr&*An=D%12tK>cdFLvDA3H#?q(3F4u^KFpge;p zth^FU%^t_`47t$So3?f8oz7$XbiUtP+v@WBh5tVn%G_Mo@kH`x4Z%qo>{~NnT~~@y zr+aYH%$X+v&FEsse*s05BFW2=@TQR9sfsRjq-lwg_UnXQvS087|78Cnh4!Bkh}kq# zswCe95Dwr2Rk4uL5S-2H1DDiqCZpUg~RlJk?`ab z!aaK8zc6jum^;D2yq0;YATYg1s-u8eQT0BJc^XCg$;_1#DS&FH6)4c3c_-12d8#9O zV;ak#o*BGCI{}L&%xN#?uadBpOs#b9O{jYh^EHmKR}&N-W1OTg3237KXNHnK*++ma zu)oEU0MS7y|3JP=PThD7q)#)3X=Mxoj=!W$j^_YmH`t-*tQ|@(_a<}_x@nscbq@mI zL?559hKh6~0X9PI5-Oii$>gq4z62D>C16OvqObxBuu>$4GNU2|Roul+%s2n}pGf=5 zUzB&TYhk#*{N^{vKDdUi|B0gOpP}j-e|Vo6gPd9q=02Wv@HM=xn=pc)wSk4Z@NomO zPBr73H_a(tdnVpR2sX3c;#u0iFqRJpH0`R(G5Pvw6kOVbO^-j0CIT}_Pc8v%8WmFV zCIGLpqLzA+RFx1?R-rVC;F-St`@*AdAN1|pSE&mrmz|xR0`U6u>7$%c6n}E{@84fx zofY<3IZQ0>J*KAy?)n;V@WY(Qi2v09P`-h4Kqw-^1Md3I(UZZR8tAEkA6f&J2#lDI z5U<;(JO0p=f9&v$a%8~7SPtU3Zj*QD3$X8{g~s*HthIkffx7pO3;Wk%^n**V_W4;x z_a(3FSJsE7QE1N>4!?z2hINyyotA9J*YKaax~j3G5IZTD+m;iWf#-!EH<=a)SPM8A%&g{AE$WNrp?e?mq1@5!o_Id$}z z@w^p*oSfpTw{(PegEgAQ)+7IPUtF%VB;4{K7`i|T7pYGKS(XGWg$m8C58tTTfUt_% z-N;%=Y8pvs(rB)d{#8N9RYIL}0*GWEsFpx4a4Z4YXB7X<{v(vM3j~ICq4fWEvQat{ ztdyw`Pe7wE{|N=M7Xc%!8Cwo>*oz&dk@+AyWs5!a>kT2@O5Qw}s{#VYFPWz%-p3f= zoVC)ic+QIYD*_>% zoY3EyY^X(jP|+v{&I7lb{hQpy)&qP$OVRjDREoeR2sv5_V7limE%oFNLF6 zTU#n1u&}UD0fa*FYh~pZ7gWO8#TgC+m%IM(2bBK(?+BbQ!3f4$>963k&(L!D8-<2? zy|EY0q0gue|_vJ6sJs~ z^m$S;Sh-Cx&YLq|mA0s>Z-Afk6eC4c%HCq8NAcw%oI7KNVm2T%vt|x>c4Vg=qSTL^ zz3VYOHPBN7JvGo%13fj+Qv*FU@Iz}r2nrRHN?lzo8`hLOUsr~sZ>}Q)CGez+0N!f+ z{TVOJ-@O3u9#4aRQ##g7o)51wu9mmYMs0ou(vKt}-DLxgpWVoX=^jjaawbyi4c?&-@t^ z!D1OnZ`KvSzTG@iM;fjbQBXeP7))SzfLP3dyCy>O8^udDBLox{6H2)gY?UxSmIPY0 zR1n@rZ;eVS@2e#HXFchK=Db%cF?lk?Py(-J{F64<3F<@>A?ay+s>2|HF)OFus3^!I zb1Mk#geg|e>j@0ZhePjSaIbZy5SRwSxsMq4IL0)ZRLRr$q=d9c=J5nGEP4a`GF&NX zj-D*{eM}>tsr4nyEu(!2YuK9bQ#2bkn+3wRuNA8MUWC~>sN z@=(%Jhl@>Aa$i~nuUW&9#P2vD%NBhyiZR0d3Y?}=A}FzPVKSvgH<;mkYj3g)d$8;x zswV@jOhW^KrDrp7^6kH|-Yu|c(+bu)+XDr=*tFC_*k7gm*4DPT{Iv%n=dOeWsn)qy zaydyqJG+3wU`|q>p^w6{szl!N*HZ&M zHPBN7JvGo%13fj+Qv*Ms1_VF-$8{J6MgzhCtxO4Ig6N!%MiK|I;u%?4Y;#ovcVXGBcwB zXD;%E;&!dN5jj=q)B#iq%kd?oxRClp80bLIAWl}(IWI+3WZxY$4z!saUa6(&!rKA?LusZV53q@OSNR!ch6 zc!I~tEJpw-f3<{ibv)BZ3S$k$%apd@Ai-o7-z6QPVma|&ps_=(1u7bC8EZ2y3)YT6 z$U@R1^>87<>j7qPC?m2Zu)S_!MHLEmr2E<68~RNrP!;OuFkS*MMLuW&pQ>61;hJp= zE?yppQVN)Hr2rNW9>8xV@Twz)Pcw2p(A->hl&lG6zQHF4^YF!IUs6$Y5)LLF!pYBy z@XhCyIC!80)2F**;lklqy!dfUo;nsMj~6Oo8yZ?kQL92_V->0i-t>BN6y(>yrNIF$ zHV(LEZACi^#a_9$x}v~o1d8n+VtvkHolhnJOvGFQ=&v{gWb$j+ou%f(zXekW&NMiT zqCDAO)!>_lhvA6rc+?DV!<1Fl2>+Eee#wux)ae_H?{fkbuJvpO(^zlTXlRo2h~~=W z&2yF0xTQcDsJL|LDp^A<;n3Fs`4sSLZB3c9#H>}LX3ZK6FG?D9 zzo!qzPM(TsBj;kspddInxsZA&qn9;VLOoz@(Hr%3W_*{HbZ1O<(gw_>GGbx%y_-#a zHox2A?L1$m=CZPg`ot(@^3IzYB^-)8(%Nr#*95>n28Dl!6s=yQzPvzwX>`691iZuA#ol z6-dhc5k4d{*}hM;ufHGjJ9jq*{g8C)N!iFRsx(%=q_cSQ(YAhJboo9H9s1Z!qqtrF zog=*Ey0~lFYv(fb*xa2pAPkio4qZYmkKL4)Q>bE9KHiO*O7_W3_?nLOB?U^xqh}6> zlani4NpKh)Nq|MNK-MJ*%|-9j2^eIlcvJoc1&&ee+h_rby`?QBt)JZk4^m027?0ni zNMj&1od|^ArvgB6uymh@CCi_Jx78J79iheUNO&-uhpQ7@{bq_6=5eid+h;%xA{$MtX&9{tOm5L3_GHjz~{FN4bAvw5bG zwdrY=#Tv9hi@us{oNdsevC$qC6%M#~k@^j*hG0h66g(Ifi5U^gsO14{>|#$`nkkwZPvcd@Q6{Jx|GoI7e@zJZ8{|9BMBtOdkrp=( zO&70V+@`<7%i%4YnBsV3JRUZ_W3LGE6{92>nYBR1}Za_{>1C=5Rt7~rp+yQXzHx#yg z2JuVq)M%ZnQdh;rR*c(>bz!dht@A-~i9PF&Wav9&^cAO(`rin6zHhdh_2txCcmzcR z(sDZWy>XK;Zg0l-jjr2p6`9F+FRQp)KX=lxSzmwB&2p%|4m3qO}TU5=Qhh^>b?AA z0xp*;oxs%k4m@}6Q|;>SNBqv+jzK>l-Sc}FVA8m~$~-O=Bh-Ev*rJ?*utV)WSEeKk8y?Z;7TdO}Jp5sE6o8~E5)v$FZ)q`fw z*;9Lwi=oAIgODr|aWX@IrE2Of?BhoHTBO$xfd5q5MIAEcp|!ON$KQV+FU=E@imB1# z=sN-~SK|1keB@uc#Ep60&wiwwbKLgMSEiot>Sq&NeOKr6uF0IMeIJF&)x_o~ip)h} zL@xpzS#W!NGZ5x2Wdu+51V@F0G1Y`fq9k4fwNw%;2_=xyDY+DvWxKFzHW36`3@4DA z4tTJqWWG}kXMHnBfI{CdXg7vHurJ#$-P4o!PT*%fmAe=F2?Spw#9~{vo<=|H3nM6L zv+fM3Y{Yek8Fv*gFQ$5lsq4+mQEyR(IxcqI3C~;!Gpc{?LPg>(aQNUJIFYDcNDPm zS+LA5IK|rFc|d`mEu=8Ecrmu4sB83N-MBI?k^fo9y2wWpTch1M&Jw;%Qiwn)$o_LwfLJm6@b*bl49zENV2w44k$~i)`a`RhtT3#29JGDVRZUl zTpoAXBVf55dWog^hkV4AjZ5q*cWvc7y#&L7;MLkta znC<2EjTWyB&-RMpJ_?Nm9QhrBoz!|$tnS#K1LnVzhxuK^*Rjl9c=?`IYW>?QH%h|g zCZ#RU$9`{PV^5uprE` z-;KU=xB2ci6D*Tn^rH%wiVUDi<#!5kqjVXVBQj_2Zn}w`lMnR_Ikk`3%|?AU)@i3C zZ_T1(AG=woZv5Zs_;|y*=pyYOyS;XiWRKNPz6NB)+h#1?HNh}Vu=JjSErG!N;Zf*& z_BROZRfhLo9F3*BSK?CZ0}Led^Je%h8I7~+9)VTfK|DKiGw|HI7(u7(wrKgp#V}11 zg#ni6FJkZ7(Ky2Q2Xl*H_h=Y?&*!E0t;Vpk>kw$4hnQ!dL}J+%#1f!R{lx+xaynj_ z_!XYHzaKI``ZLcHZboc&Edt#cP&3uv;EiZX(2t44L~j+qs!lwL!v)&R7k44^*Aq40 zo$u70xc29q*cQj*<)=vbxY>K>819%&4<+z6s!~MkJ6c}nAb*{Z#Dg@oRxYp2Noi|9 z!4+4W(O)82nA8{ARgp4duK>Wle6Qt%BJ{Lt1Yo2>5neU1QOmU7tC3LVJ3esXZ$6L! z$hruAeQQw6hJBu(j{=iG%z=LQ@eCe8-nHXgqSCM3&lrZ_v5|+KcHq0nCvC922iBJdqw?S+Du3@wdfX6d`@5HJ_tVEC z1ZSj<@u@!{q&xF3Q>cKzt1Q(;mc5wpQcr;BJY(ep_CzyX`iMqTe8YF zFr+Y-S}|QJp07NMYnAPRik$nCvu&Q97C3wMs-k37Q-dI91V!TIz<~o5>7gPGQ|!dQ zNDQ@YDN+Qhh&lN2|%YS-A^d{d!~moJX+c;YZ+3-IAXZ zGVW)sUM9_N4W7hrURi`EAAS&vrn;i+|H(N?kuck4X3Uv`-@g4ep}Cy-q>2l+M-g_@ zyC0>rmQr^mfp=X!Tc6)K1=xvHEO8gmD{WWmsFF%=i;9XARS;LNUL_UqDl#%IVb?D5 zyiXvTZ8O^m!J(;1j0(l#*b{A5`h0Aj8){%_)Fbm?EarN9!*`f34@WP?o|N1+za~%F zi8``NrSLl%jIb4jF2=!pet5Qp#HhvC9KQ!E7}qd~Yw>2BNIyzHhPXpl;2;IzXr?~@ z1U4+Do9{6AdNV%+1alz zI)%+ELRmg<0YNr=~lvTsTs@y3QklRi?Pi#=pb_GWuf+~Pmx_|=VJz7kE^xukWf zNcyfA*6WJ7h*ukdVpE?TkGax+l=jiNPbr=Zf2QC0rupu+e(G^5QElhGzTRX-kHxX` zxvJP{(*(OnS83~PQ}KH?D0MPS!khLJlkl1GQJA(Snd~rYFpOiKI)-ulNIin>8%*ld zw7$E|cU$B&nemQV}c=yo?A~1+;R=5u&kqZD@8K=n-qXY(T7-m^eaS7-HhqOz2M;`0B`}`Kl=x+ zOT|G)uv?91&}by|iNXQ`%>S4< z3tv?CM^yeXtPFg^;64feQMMAtK3Ggr4v!i>6N8U7EWnF1?;|^?mjVEP|1JR`&n=po zg3td!dSW1+Bl~K5V~GbB#L(&u9LTuVE?=g36cSL`k#wYT-KLtGy|+*0PQLYkz}u)z z3ky;1&`200i*pJLLE5^*hDx}%H{h+ z*%u~=RQ3Fo5kXZQA&9yFj2=Yl9jOpw@1Y{~%N)q}5(KiHIFU*;(w%mn5G>{r1Ww`Q zLAR+Ql!g9IvYWVavzNV1fWr{}x3XDhvUBmo7<1%LfMZ5LB}|r<+#zwrrF0iut<6>@ zeo=Zc%$#Cz+1Z*Yr_zS^VT7{23|+#!XYeV9u2D6-{_xKIh@SxghTpK5C)YzrEz2}wmVk3|()`n-i)^ALjGNfc94hxGD8 zIJEz5{NWEDpq^(dJ%;xtyXh}6_?{pF=3K@kfcX-^{S}s|0QLp8sBPu9U2zfDS1sTd zp+r+NJ)G|=#Pz|)*(OfXKR+mIr78Rz${Q(JHGMoTotcQHntmAn+H0^NFx8AJRXBK! z7=bgi5Jre*f;^$_4H!uALl^YQbFnK0^o({?1ms9L#R%n3RAiKtl#rFRP+?v8?$9B; z{kJ&m-n|cBet8-pA!KlNYEVG20Ao`VFlGdjb$g6w%4*IY!KTzK4EJ+G?!iA|#v&b@ zR>dIcKoYht1J=x&jLluJfa+^9$u5;;SWqiDLuOVoE*RL)YD$u^Y4sWm-Y_AC|c#VA0b5Qo~oCz|)gvW1IVWBpp7Cxb-23UNi&SPiV^=(-L%e zE;@?YGZtaDPZSalA4B3w4<1cOP(4|=W40~^k^bCJS)haNm*d$N$jY`N0-I`|K+>_} zIIwpklDDnKq+O>NhKt`9#$o9uASUsEl5cEVj&bXf*m8*voAR-H^*qd5Qv%)Ar5MD| zS(B5Bq_gTKM*h(an6^-dv0L}z@PU2Mjflm($t#i0KA#=`5*9_D#KwJxah&B}g;*?{ z_L{Oe(4j=gP5L9kmqsIGeH;!SJAjz*A}o&>hs0cMk)DN3x{LU&gMg^@SQHwDmt&$B&Y!yL;xKcROr1^0 z$;x4WR*m`{SHolLJ{*>O2Pa_R?+oYqsQ7&LG}iqH9TJZn!`_Y7ShIQ)l5fEBDQza} zq*_GX zyzc-GC+);Iw(W>7!j^GryOef(I4K4`37fF$-899UzJ_g2w-h_@ zw9-d*Muo9y{u(Jt7pcUarzc}gY!$XDd5S~j>gCwRfyKIgB8Imb#4jC(<(o2~i(~&x zibF_-l;K6Ct%|4ej8v3U@}Fj!DQF=zfn$kfSA9zJrQS`ybNBh~zJ79(UP8q3O&G_1 zaE$%naqNXImW6U^pPu$+ny%8;Q)97uO#yax(NDB6a_={OHrq8H{38()_cR8|P-`Xg zBFC`Gsa@1}_xWZ(qjB(=(y5P0-m*BGYkf>SS8yCjDd!5K3;$q0TQqqdwpA^|-lPNA z$3D9|dJ)D(spC?|e*N^l?l7&3TWQb64EH?r)IfJNV0SMPxIw-AUYRRQ3^&`i)7~iE zv-iDt`BNU4l5>N+T3h4rXaK1pis5Q|YApVmkfyj-RgrBpjzVG#*eu|gIJADcM4=a2 z_J4r!Hj%rDXAf(e@abzWBkq)_0s9gY8J)PlmS&Pp+G}{gTLXEHc=)%BOVoz(>|$+G zE7r}v*Cf;GWB-R=X8zJ&sI2e+06+jqL_t)#RC4cl?-FHwD&;lJm+3R?0+q6VD&k7T z-0WTT^`rcE!|d>*r1z7KR2Vty2%yM(M+kJ0z=b;*X1*9;PPGbTGb99qwJimC@O+x+ z2fRjFmOcN?%?4Az$w}Hd5G>h~?p4n{gi-}I$(cd2k!B%onLVt~9%M#*EBLUR4Vxba&G`dy-s7eBc@l5R+ zb0#uA{ixe7olvZv%eK#ycQfWf^!O1%P+}$lXrmVz8hL=1IZfj}jV;+bg}v2|Ii_L( zg2`HzSvjstwo_qBv>~Lk;Ln=Srw?43F2c94j%Nm|3EDiEi!=gX3)WbFN-=drIf2&s z<{;MMc~UF07-MhdMFi#8vc?F^>?w(qlWX2F_6x2b#uW1;t{@+JQmZ-| zQ3D|$NAv}{yR)aeIpBQSRh&vb4~HSP2pCJ%3w@mx^$<=x^ea+C#johb>p!9yMJ4sH zwX-3p&4+U@4puGJ@a%Pf3hh^;p_e-<8?s0Zynw(u7gEtG2pEa(XsTR;VCI*GJ_R4a zs-Y6SbNz9trI97SgvuH_lsJ+Gys#8ye)G`yo71os>SJHJ^*sZNZ(RwrzahNthY>CX zD7B=1!Cu3-d(;^P7@gIAAjx3x;F$>z`n8NLNgc zHi|#f_>GIsDn`5h0G5OX(0K%iH<0YU6DuRsd_M6w7qL5tqG&Qd*ugCXN)KD{2)voF zZ^UA(KN5>II}Tyt_&MxpH#V6^ceCX!z=7A7AV3yxe%uL+p(v!KC7dDL5EL>U*=|f@ zWH3U4eCUAyKo-trg(&2rYy7=4ar)S3T(ctuJO2ol6F)B7`D;w|=R%6T`-P3Oki0q$ z=hua^?}(X2(6n9&44*UR_^Sx@l7K#V zJa{#BRLe-g1?^-`l8IiJ66A#%3cK*}n?>8xC}LJ!6d%BfOqSAjRUD->$-@hz8|3^UyA(?hn5j2|pz(I>coV+;Z%?u~EOd z>V7Nr(S4bMZrGm5KIk|eaXa^6)Pv*UOjR_N-h&OdZu;9)p1t0!^n++aKk(7*LsNbr z*04?9*qElv&72$b6Vvd{K0$bdbMgafa5z0F=-LUrM2Olb5vv6 zuQNG4Xl%3HwP!D`H+szftr`%nqW>-<=LR)Mrlr}~$O-sJl`@s}ru)r}rNZrH^k+%+ zV;zEZm5$LrRcSeP%!JYLr}J5WoCGN)v!s>Gpsnu6ZO$c# z>4heCl*X!F1V$n^w1gn2Hz#E$*qRliu~kmc`4aG0k^N6}tlWEjs$9y@WwAuxZW|ibJ z4WcTEk+{x7di`2O(_jlbrJQY~0(RzZq*Ec+#I?w}E^M#&z%sbz~lTRS_s(C}*6f2}zroZW;X@=d+&l)Ml!mU>UBneAZ-fv?2Jb z2XYCxE>#raaw%0f5JYxmq_GPHOF)dK6VSuY9G-p-1c&wb=RebMzvET(fAD^)fN)p9 zk+6u$HGRhY*tz{16r8I-VcB^+KJVwSp%l`lR7d#myA2&c;9XmV`>N{k{DXmTh*?02 zo;oPL6_fV=+4~astg37Kl{p~^34}l(3?T#(h9HBW1Q|pFDr!L~YC#`Dd4)e!ETvMg z&zJVKP+CioJ|6Z_sC_`Oh(c9VKoExn1VkhZ2?PiUA%x8H^d~qfj&N_Ray>a;bZbW|PWB6U`fRxSivAe<<ya}Qvf$WDi zIbVlZ<^0YMAKatXhhu{ax2RE^1hvj4k2X{k8~oBa!HqWH=$=TF1(+Rz0rY{$aLdQl0&(s4@p z-0n!t07TEQgK+x-auRw5n4f!l`}h?zoEYMV*3H7>yAzG~@NnHG)dE;ON8zsFwE$4B zHalI!L3ikQh1ebY@!^qu%r|cA@S|orqf?{N-K+}zg9mWD#F`PBzlTk4BR!Z!i*0Il zJTsvOHOrmfiJ^0*HD8e?LRu`(h(8HY0|WqTonH{C%C0Nr2hPEj=d?VYBKbYL?!5UO zO~ucKhH}Aw-A(c^pE>rpG2yuO(Fk$pReU9pU_1(|2AiXRo?RlafVT=>4V}GSlW@;) zhxmSjxtYMJ&ao1BS;o}|Wfgf(uZqoV&MKt@` z{5)xx#QYv<``7O5^>)PRCcRs%H@_40`<1!1Hpk2&&3mKxtP3Sr9KSmOJ^IpbBhI83 z*Mh(X^V-ad_twX=LuBjmTTcP43AD*BTg+dD>pJwOSh=x>vDxxfl>bdEQ~lLwE!RXt zn)_lm5!Lc4r&yggHDtPYr4uC3|d4&~(ZP~1K4f$6tHP^Un+zlN9I@9hUxl8#$RN0%4(p)DBxVkXR z4F$#I09FshFc;dF>zIHmh2-az^=96I%(W*M&=Jh72q6@dy$0IKBaO&q% zR6RmCSwleTMqcG$QmQNI_%WP(^UzBm_cH0=x2KvuEz8y_EEHU7Tim?q9YenLcRjPItS=Mn~2?I7jf1D@L2O_biHQ^6~Xu! zVxOAyIs+*2*DGl$@Nj(GmU2H}i(MF=N0)L{B;)_nAoy25C$NJ6_cl_Hj zo`T+nw57S~WA3WQQIB}}#9F^Ui<_-lHAWecXCSx=0uDF0BK`|X&lc^jd z7|*V5+I3%mgDFcmF6}NmAr{9G1pumACGyF4z2VI zL>T+QE(2f|kcN#5V}R)~ctl+#FnWEpYDVHc8=J;btd`u!&(2Oq0Gm)8{}TF7KHw0C zk4>bd;Ms^|KvL)cr(1~nMOFxxuq2X?Pn#@gsh*}WGv9Dp_+1?Q{5QB1~NUpi@ zWCYg!lj!P%$78T)!+vazmZB{iwmlk(*NqNvjCuW;qrh-3RKw1WKk;lzUJ#B zGZ4e8p|i)|jkt69h%m=q@i}m)V!%Uokk2m_GE1+|-_%bJ5`>PgpHB9}OV$P?H`F0MsJW~PCWpiTAWccM90H^+<}7sje9%m!_m zHFS0HjewMT-?(CK2x>zotSie)txeg)yl(S1p<&_H`?rbKUM*b$a%BLyTj=S*8|-l-V~uRcqVQ$*M!lkt@VM+LBXCbnXf`c1Doy%egf;MzFo>OeWqsaS@-SrnU+InV3M;7XjqZb|8n$ z6!jFi5y(-$8Gwi3wn38_{s}pO>4q{GDN6Ao>pwKKH}1T97;e3Fu%SFgc~q0d+UfY6 zcy-&uc<6f}cs1j1h}v{3p1b>5{CwE&uqfoe@%z6LnDrh?hMZo;5sJdkw^|FidV z1m_0hzxw_kVuB~&h2UVUpkkX}PMT!2WzM;C@I0D~$_e)w08Z@}cvD-{-Yf!VQ0>f> z3?E#$G?dyvOAYR)L_Ng;6bN*8=D9|KL10z6AwHpxhVp9Y{D$2_$h8|!^YB~L zvT3yoc#CF^bNB+>=gVeHczz3# zIL?y~Z^JBAy3W}bvT>(AMSOAwXLuf7nKlihX1&CB+Q*|NQZ0BjyZn0Ij+k)Z=?T9^ zY!Vd=WhCHlznF+QbKXW7=Vr^!8@D|q`aMRaHu1=&DCXgvzr~a}b8v#I$CUj`FlE9t zDruqe2WIBwW-ryZ5g_ryh{q-Yt7`MfPQwQ;{f1(tyU`=e4l0_jM;S+>9KQp_q{Xpa zcxLi)!zYKF`;(mp;p$rvy5H#dmX%xl}}(R!Pj z9?vS+KK3gMi#SFTu<^%Ig2RoeRDDgGbFG)BluIWr!`=jq{dm0eQ;q#e7#UiBmbeDD zeLPjsOpe`^B0rmBXWK6gg+|$#S8MyUHn@qKXUG_1+%BK;yuD5t@mT$X$C`*~_cG!W zV8u*cj9tf)kd}N5FHr=1H$laWNn!R#0UXNfUqb>FPbG0qJ+)Ne%gnOjoHG4y?A|hc zIG-NncY*V1<<4Wqe2U$;#1J18#&L?jQ&{g*eWvkA40fy6Xn(OYTI}= zleF}$HRD+KZ#D(A$*i@*p+kpo?b@|(c4NNfwO9YBpVVLKx7J*%%aqnK_*V>EadAP|(}|otf8P4}Sl0DtC5r8%)BXut9TV$)uwW;q?LH7?`aGBgY8y&7$tL-wbW{hKB)^hy>w<+G*wNnxTx?P5I5f$waXx4akg2$N?6l)#~ zS3iGR+F2&>{ z<%r)q8$a5!4!INOWAa!#K$5uRnqA-L+z7zz?YofqgUNVof=Pj?*s%X6#=J|=GOcbQ ze7z3llbwFR<8vn0iF__vw+qulxXbgs^@vIODV4NLFr!1Xec0qC)%o%)=Imc%9dXM1 zx_rX1Y}0;3ccC@VLVi@PFBkdj0{=Q}7>t>lmJ!D<#n>I9ML1?J&_a4Me!1f`-n_eE zT1N2>#sgEsv6NiN_o!%M?=1Q`xcdEA8xU^4i}vxP%KFN1v{4O;@WVmJI)iwRQ6|WL`}lun8Se_v+E3h#~aS|8>n}~>>JLpd84s? z*;Gv9*fc&_QM0j#eSOGG*C>88j_iB^3#Tl`fCWhcVGN~^W}`W*M64v9IN$h{pfJgC~R`j$(Q8{y!;t^ zWM3~*dRAR*d-0&YILDq2&_t7HZ~GNV$n7$|tiTo<@0GarU|re1=%)$gN~R|9v2d?h z?NgaO%tRFQ{Mgzbie$7u9M3+_n4O=%jKEoLY&+_VbQ&K2$#=Y`6m3_X9GlceoX$uy zHUd|@4@5s41qtN#k3EMdzNVgfz8N``Y@gf*w%+h2Qr=rk;|7+6Ukk~ScvaLWQO$?@ z(I)p)X%3?{y8xmUZqp!Xdk^wxdGCujE*ULEJ3u}3Pj9|+Y+H!gubC3j+A1SOSm4dCMqM>r- z0}ml%cLk3X@x!O--KnBhkxF4t5gzWvG}YO-{wS?qq&ThL-b$c*Eym)AIKo`=k(=H# zgKi4+brKgrh)Rx_I}ziZOYNVIvomvz2s@WZq1(Q4^_Es(1-qw9?7DZABy}-*R9?o5 z6ZAS|C?I1>Clb9q+pFlb^!Vh5!vh5M^k_<75X;9O-%@n@7XC@v-9obg`Gkdp;J3{% zNT$sqq)+K>@;NL``Ch<7H@(mxvdoGJ2qZR}2{*N6OGPF`72VaMRzGRzAw-GZ)O>}8 zWHNT#I^i6U6sek1;e-yq2++9kfu5z4q<8oc&6~+VeUc|w&xP?}B_@-3Jln#)>|V@` zNX-4Kg=e<>eJ?rW&lky{SlqA70BLReV>|JV#0@Zfh9_~LnMKYmZwMe28a%e`hwvpF zD@c_<6_A&q_9B@Rml{JxI;BDKc+c^NE&+rc$@KWia;g9?IDNIsv#=(%!=BXLs-<1~ z9%2@cJF4T+Bq*RfqcBR~+tz zlYDYKllNt10rxhWnJJ@JLMt6PFlKAA>whJmuM*%uUs&vz5^&JFTYlgUw%=dL3(&yMIUI zTG_k)QnW6Xq}75a*4R@B3F6EQYj$qWvyE6$-H{-SO4xSsc@{{5M7=pvPp!}g@zo?0pLyCx&lYLz80God<%Amzzatw!rHM@yI>gjIQ_EZgU zfKjx)o%R}jNUIpp?XX8^0=dimtJ#^Ze$hv9c`^dB)Mi^QubQCzvEFVXCKlw2{GlTl1061j%Ez zV&Uzw%luV>N3}_mD;CYnivzSw2st%n@u7WcilHo=I=eO8_`o~WJ=%H~mNsfDSNTbU zi;U29>||9@i}9{mAn%>g5I3TRJg?NLRXG{md&fPngq!rEOX{5Ob64JAiPL{QrKq{XMBvO0FbgcR^+mKGR$k7 zE$}hz;r7IxNaoMh+D}#VgOP9Qic1Gfje>_5&wSC)udXtQAuB(TDU2I(^Z9S3!wPjJ zTIv1JnvH&G#j?I56`xMrNuK|83Y;akp{Y~FPLwFmYm$vPJ3u!^OM_9;w?`RZryA-F^H9&sJTyEO8|1}@29gEZ>UAb5*4M@rPM(}~vYREA^Hge5 z5SCMXF8v(L@rhMlivu%hya>JNTi1FK(UEeN$e0Ft3pTo4G1k=zY5VnHOtby_-IU_G z0W;f$JH#Hz>B!ecRKZ`KpRnrawN(0wJq3QEpir>tMAQCQ*6vkBF2hb4aL4;+P`iSG z!k9u~j_K2X$!bERKP_%w|G}hmK!=!N_-#|>=1oS&NjE)L2+jz@?uC<*qe$Wks{WW7 zG~Qu5vT}{LFAIY;^1GU+Gn;n=O)%DIw5ZHSa8sNV@~Wf49nT94pb^HNfU<S@W%4S;}_AY$a_hQgf69Ri`rp3x8r z)|xEnnJDlxO_h+z9Xd1lp?_fcpBjh+=;1&#giJ%WE<$N?n-wmjLVe5*zZDykjnMyf zY`jEE>{p3dRszLg2LEj0J)P~IXU|aEMo64< zk3L(=nai6tH(3Pw%x$zTXftcQPi=y%An_o@XduddxSsRSngJKpT7#OaGu-=z{mVm+ zNKJN_y6}gJci&hn=g4t88Yp37=D*+RUW*#NID7{vj2?;NJhH5RH zL8d-~Q4fconAbKEovnt{|B*;I;uHiaV>ACVtBJ#zusIYLCZsy3iCvf`=3DMO)450d z(9Lcs?1KPzhLm$fqpHgn!zo@Ek_9na*I5WVKZ2k7sD8EzV3|$f+F1{5SKO6O>llQH zb#B^eKTC^If1KbqA=x@038BA;x&2y>-Vo&{M;Dw{ga@HCX7J z%;RIbZ}nP#o2u<^MwHn}XtsU+ADrF@@x( zgi4+LJNvd0Xd%M9Y}s{wqyl)4H#nDW63pTO)%J$pmx-%3MUCsxU(*sJR+qA~Iq@UcJdP62s{;7ZK3K9pPE7MTGce-i?H+&jk1 z0w#l!65l9=Pvk;~O5qt*SX4oF)Y1F-4b$NMLPIhJ!_!IFN3#3tcvd8}N>wW|yNB~U zOF|#YB!MRLeWfB9Ms5UVHYVFlDS_D>);1YKPCFL{_tPv2?p8FYi!=RJ>+6n>T8cl3WzJRV z6LK9K7WQjLFpZFk&!P5T%sd0vm3eaocr2x&nO8-<${=DO@*tFDmf{>+HvMO~TS8j~I@5_ELD|_^2#+m&KjeuN zKp1&>@kd4)9z^^o_)`%-cKE#r4EaBi`C;UMN0>awS~F18&GI4-@}WCsi=iA3jy{BImKZk!(ZU2E26`a9#I^n|rHO_fA%K^SjLfKWRanmhN!(}VA?Hr@c*|$JB zm*jZjZ#;HH9t-4rXdgxeEEGc@LOlkZBBO@+dsiUOWR`$^!pqyPS*ZP(PJ|YCy4;xgtHr+8|8tu16}>iQ*CmG|J^(lu zI#c-sBI8qr#2LmG=mLm@O~o<$CX4?^4F4gJ2DXF|&@pdEDxw3w@`ieA|DMccQ}g|{ z4v&A}y}uA<(r=qL%H-CHse>FBFbKu;hb#a(e){)|!J|rSwTc`BZmD0KJLA$Om2djLr#g)8VCApC_uBqN*JZT7`eZJfh6bXusaR0WqFWa zVN}+H)Z`0jH7gZ^Bq?K&`HN)_K7H}L{>Z%-eLj6CoIk|%`}&VhRtYR7?AKx%DuGRXPEUDB{tW2-oP7;Lzt`w`Tn5unZa~{MA|ZrRce$o z9@20ri!uy8sy+mK=sZaIR;ay1xn1r~u|n4pG-gE8{$f)o_$)4rYWD>@?nHkrskXw^ zBRWXLsGek6H8&j*DF?iOh<4I;hG56?gm#WHex<83fA$zpQs(gW;r>zuy9u;%&VRvf z2&yk?@yUF6lI3jACTnpz@zz+Kwo5YzwxwGkBS=qvTE&qwXCEOB;3{YH#(IEe#&ym{ zOyh3qnDIJHWMqB6W=r(;Mn!s!f_y+jL1>Pw;!NMWQW5!Y!&z@M1WjlV*!OS@z)(Tq z0L;5-lzJune+GvCK*#HcQa`rk#=+Wk_E5lSO}*(YJW{kpM@SS@Ak`5TP zAxB)$6bba50kgkA_ISemui_Wzap1_vUce`j{OS+oq}WL3z80myl;4WBfPR~xIEa?y z8+7i-X~r-V4>n z8yak}?%Bl8cSJEVMD#MxX-A{#z13CPt^u9ZAO1Zy@RZ1ZAi+^P>QH{{6Qy7-<^^D@ zGDZFVslXDVa7V^4hXO1WzU+ynK5itOIVT-Y%x1oHzd==@>TOgc`Dw@OeXv%bNmNMOpP-HdtT40Eq zyDzinRD6mzl*C~L62*L@%jKsHxuTEyDrk;RZ100;#OiH-i{XF?DFX>plKV7b{+)&h zXK)|ezEM$DEU*zMIgF4Qz)KssEsYz9^vM&V(22$U@k+{TM)a8`NX}D9DD>M7@psET z(MP4a_$n6NRT%KQk!3po9cWq3CP}ZCDv-(H39vdiNQPWa#IjU1{(-2+9U)ihQP?&; zHjPXp%29$<_3dXtd*HrEi|%>x$B%DD;w3#p2*Tv2>HlTJ|3n02hz7~`DSOf9vsi+m z*FVg3Dnv`Fn$ni_Ib5?Zt_+{Ir}f7#5Z^Yt$>>QQ5CvLL3j_zj6N1Zt5?u!WF094P2uD12+db~F)d1cdl}y}kp;{J)l4FFgv*fKV)3 zfI+TX*mw{guUbw+qG6ictbnAAAs_J{2u>kpI{RoZc6p(G0(6vfF+Zw1^wM(E1cg{N z*ACH({H}weI9=tKYMwZ^C$j+7fF8has>CZy2FHXuEUehsc;|6PlYPk30teX!Q|@;p zmP=eOMDjAto|X@2JdR6h!H5dS4(1V7GCz2JVT2OQY`c=tAA;zD_r`AFk(h0d2)&zd z)Kqk&79Emu6e!>TQW6sQX;S($fmne!diEo&?YYU&bp>Bx$Aq3Ffvg^tzTS^x*6+uH z!(Dnpq2%&Rv8Ilk@3%vz5ZlmQxuQ!W_e#8ya|Pz8af+U5$jhTJA z8^E7jaH0eiH6Z?pJk8#xQIfaPU+RV}aF^;IM7rWQ*clslY1h(`P9@soB`a} zg-^SFlx#w!o+Cg1fW3#)c0%KIhOlI9*9F|vFnx}|fd{Ntg%ilJU=yQTu1`5y$@q5g za;l$vwp5z3Zg!>xFdIQ8g6RD_TW_uJ#csFT2_Obg=TVR|LW;-jCV_qG0&+*Pqh0O0 zz>C1Wq+0vutI1NWN!%!g+lv5_oHN-DkTNpK=_}ZP6A_e+VdlJ{;=ST8lj>w zn@eAVMdk8L#;=|{0y~fZlck#v?B%`c{;2czJ4u}p= zQBw`X_ft!>W{4@A#!}{(#okd)=09137THrV~kDZl?xbpQ}^~k&GGOcSQ>lK zF{f^{Rj{bAj>&i*0^3`2yje}4eMR?GCx|6{_rl)&bmVmLX~U97N~O!)fWWGByNk9o zl5YpDIOp1KDr-?cPgt&~DHvTOwTp@0VU8gpx+XHTYfmR?;in51vyAU7MzgPY-0gl6=2=o&8J`teo3eE z`Y@AlXV2Rn+UhFiUQq?laRF@u=}AyQk3j^Hu#e4s`^tBt_bz9C;Z!hQKNBrveb@h< zcm9#HW8AD#Aa9k9X}L2QbTC|LM%WqyYtdZ_)C|J(Dj||qLnY0XaBl7y1L>@E+nWWeuEEuK9JhmXs zZfXT2pt@F7*P7QcnX{0c5o@mC!52nJl8g5Pdb(aIx6*lv_yLb3@2ob6F3z z5Anir!UVO&UV5vh91iVUF;{q?)V$+V4zGlq9Gi>)^!GI95i^T1p%g4(fzwY}UM!GW zoKPe^q5MihlClS>EJjdrbU+>X# zD0ys?c4S(&rb%#p+$bP6D(=k4uSqKE=@T}M&o`K#9YT8&dtT86^Gtsu1i*UPHU8tK zcmGY?L4lqf@fq{=R)!Q2(MfHgY3Nt?{Ol&&@esrPnjcWm&=esRWa{i+8p^#sG~+@h z$|EZ8>x;R5H|?fUud|C@ak(4xidfl)YnuhUP~jvY5Qc1)2(q8KM`6+Xd`=D(?AW8 zG+u#uE(+8PDufx@H=K`ke;j7A6*n<_ztZfnCq zZaMoXI$|C+$bFx5l^aS2M%@e)KuI;^Q9BrD_b;LNg|z%!_xYm8xu+9d@1C|t;7NIS zly>k)gC}v2V@PR(akw5w@FQChxaTYRivg67X5>W9PhDU#KQ$Y7z{2Z94zEff!+*Kc zv)O=9lmgYsM_x|f+)Q2G>$8-fxLFu0qB6j~mswpPsrqBP8rgu5OqVHTJkNKzL-i!w(ZSIo1-lVbD+T8^%zx%*K zuN?1Emy5c$hvh$yoTV~81jsZ(>^{OI+Ee%qC5Ehr!!+NWE2B-i_z+?cfcSI;k=Q$J z*tTbWeTDtJBGVs#B7yI=-Q$M?-we|IA$|xuVu9b`p{E!~x4KaYKEFMkB*kqb9=j&^ z+^^%Iy;Le^)gBhfMSqU!^{5cl;7{F`Tc!Ijo|V2rlSRBy(wEZP|59CYxrmtYrE;EP zwzh3{7(59w1Z?0mX>F@!$zcCTwiY;`y{_`2`yVOYv!+6i3wAh^O5)0Khr3HB4Y}U9 z2yo8wTFyY8=WU}-T?x0%YA^J8#SWC??KQRi{b81~3(w2?@6BuO(_^167x??llI?ak zG@y8DZ|%5hVN>>0^(RpNE=!dQ^QyBo?dkfKs9zpOu)Hw=?vv>D;}#~_0j9SCW*boU zPQLyZ&^igFphMq(HqR_2kY?G_UkUDUfQ~sq6h=ky6>FH%u@2K+VJ2Oh)X`?q$8Y+{ zK#qLgt1{q)u}yXrCa?Oft$|;&Z&Z^mTs3fPYAITRXb<7vlQDtd0o`-~0tgXk;}>{S z=(7m~TMWG$1GqMw+3}Xr!AA#5I9XGrkIESxH4#M9WNK0NV`-MCYl-4B$_x5i?hapW zC5*AA^ShCjaVetd)0z9=4rHh}A~zF}m<%|kr@&6MQn^+7&azh(`+W zZf;om3K>GOHW@Gh=Z1wU4vE>;AmskiXi+eEFka<6pwusVG<>mgIl*=emAw!VV^*mZ>()ttvwE7f& z)X}A=aLFjHpehR*Vp?wxXHeOJ_m3&aOl!cK61zshZFxLCP_<&^wA~X_b(POs_7k!^ zYQ(bRBIe}Sga20R|L*$0!M3=FKf5iGSrA(Iu;w>OiE`W@zFl{DgE&n2B7TKO+6Un9 zN=H_2ALCjwxeTg5BTV+z_qQS^@4c2vs%7|n={SJw`FQC&xIY7p3#z3kHt2=tb@E8JM9x7^JiQb-+`n zDqpO!Y79;|064yv$XM(FLI9;UX~n2_asrykTn>HIdh}&Q95kZQpa6PM;cqxGh)9|k z_Q??_+NJdc?T_XETn3T5WdR}WUF@7A7z}FO8q`YtiYiEDAgXl3k>h{nZ}HsNvX6r` zCb1}BjGDiQmA6AmZaF7h{!#e_{eJrFOY+#G2nMA7nx9uG{5&}cI8&h!L8kSitgU$c zbF*HEF=^Z=V(Q6PEmfq{Y1EiRcn zVoGPZ|AT62OZ<=q`I!=-(?wyT=39YSW+8OgEx!{Fe+Ar^@S@&?=cc z=Z4&FO(*UE2x|dz&D-s3Y&)JPd=kr_cqNH-Ee8{N#0jTdI9-_sKKPeQT5rx}6U_~X zs7SJfKb&=Js(Eq;L|a2Wxt~21d!bnQJhZEdIEvXLve&l{MWS=$s=@7AgA6|Rt4Bb* znEX@WzS_}x6h-2RCepjm4U1wMY=6h6Qu)@kSz;OTyP83HPl6{Bgw%`=)9NDJ6||x6 zy)Y}c&V-6VxJeeN%muTZKhLMj?^~jPy9BZ)hqWveF{)B4-_aQ$uuHkc)q(BJ-g6jf zQ*=)wjdhuw9FE3A~$!nPv;a~^yHzsBQ-$F^i4cua3l-VlFGhP7bCkVDYgy#qDS z?fM0*k;~92<=rc_b#5`C(FJ};C~wS%G&KAI>nOjhIO1G=M7BepPE_Xd7nl*w5&#uV zB-qu>_@$86Qv_1>zXHWih~d6%4Zb;1o>s)G>*1|Vq)koA+m|~v80a^J$ene3x10N? zZ(GU6-uX7uMei_QG}uDi8z2jK+?~B8UUY4?+iF}`y*TZ_CciR>(>qrNvn5D)2g%L; z)R57guZ`e)Y1C{U$1A~HM5Y0_E>WJ&?Dj4>GC zNme#Uwm>RKf+DL` zybW>RK5^=$Yr$X!56Gv1_qQ*6b9}j-`xRv|@1#_LlAhAr-`T3-$>hGt-*JMdw7riE zvQqj60uh4f$Ci^treE=81Sw+vDnsc#D${~n_fd{#lw%BFWM*c@#No{5zosHy!qi0N zWw<&>+-wi`Cv#XVCQ*e;YH#BRR|WrQTAp9?^2-Hq`$t`i;IJm2uMcgL!nO+@*gKs-;u4o&|`m>m9dEcGSpY#Hx1(kz~ z8$||n8itC4)3bLad0ookel#3WnWhuku0v50u^89gKM0lQDK(fR+rH{_Jj1hACcMxj zueJmb>Hgkr+6Ofws!`oHVE&6UuB+y?z*7{f?>4|Ln56rI_p<-^=GVv$e(>m@fZ9_K z06TjTM9!~C^a}U|9T25;UQSS<7Z~jf5QT#>U6bqu6HuYfO0UANmnx@<%-u}$q*BgJ z72igUZ3S(S@bL<(V#Uw@)n?VXX>?e}ONFT~$*SzjfPKcix0LSIAiCCKV~i5@sk9~$ zA92a%UQ*Xnve7}LZj}GWbR-xPFA&(?j;XW`qoCbcju!$e4GnFbd_YA%At-3z!%MHC zBgX#H3vP_+4G#muc5NPv|Q15-@dL zEJK|I^&+7_$^9|?3GJJJ5a@mhpiZ2iRGb4S(6jpr+Mra4S`EW?!1eiQ|mUni(#O88_c<@W%`HZmoe?M43*A*mBRf$Or7az}3(`4eyuXKKQO zo#A+8T&I!pYE)@_Mt#40e3hmTL;@bO2E}!OM9=T9-Ar)AW6Dl#W+hEQAU=zXRemXN zs1kx||F2dlUp$8WcL~RFFyK}CV=?1#R% zA<{?-C>YajS?Q)XPTeZw_$wZeGARwH;qRAva0l@FOZd;eUA7}NZ+cXsh#!(Bb^l5} z#mgO4Yt48DI02$7DnUwbF|ky03PO8|1oCcw`~(L;5U5fXn~xd;ulm|p=YfsfGX!|7 zXqFg1dy?XN2R$`r>FHip8eaF13ZEA$9jgF$Mhvf{4bMSRbG&pQlKOh7e4USjh2ZYK4*EJGR&X=JYd;dO}dULU=GSn9h zy4ti9{U9ac$V=xl%PO$u8(3~V$5!E~!cPGqu3>vn&DC8A)hzTt@WF)u_P9dOGst8j?zNAIIYSBQJI zCpNIgJKt3L8I$g-9>Dm!kQiTeaMZEcXoF?1Yrk&B$@><~W#^^z&`5k&<(%t|JL&Tx zRlSiC0coJ4hiN{~_AZv#(!F%Q?o5@xGW2@kgAy+* zpUURNkoj|08hUsrMV!=rJO4wSf8$2WcTa;I&y+oiz!X`mkFuW-HVKtfCd~i^=zIXu~9j(=oD;uyAzro-ds^?Zfw=*}bF$alvBX>b~uS z22m|5tj5k3G%U0m%zaoUMI5C7=`<-LxvxJCuI4%+|Rsc=7OBv!Jr$0~73f%l;i0h_Bm>>tY1v zgmC7-l3DA-6mzrf&1Zb5^9_8G+K^6reiiE(yc`NedBqUbRx^HX^Roh^U6sR^{uvK@ z|AdVFyAgo^wI$+&FGVfvaIVMXej_F_z@we2ge1wGI}}vPPU>|?BICjzG!R=OdJgA# zy?YZ$xT%P5S+^fOJQjn>$SVjpkHox6rrqM@H_27~UW*j^`Q8Ae7do zX;@@&_NeAm(>+3kl)c4}4Ot>vy;u|bJve5TCt797)X*RpE%MiBlvsmVCx3fiyqrlV z-r4H8 zLNTw=4zUJHOM;1^N)anDEWDFp@3`kAXP`4hPtA^KQU1Y5%SU?s?#5XpR6RLq2X0n-mdj52_)%wlEC=o|%OAS(N$lYM z)m>Q3_B~lRdgwkVq*^_!ck2drfP`T~zXx z#bAF~xz3HOzIIx1J@8*@;eFwx49fQ9q+WF6e@Vn346X1dsIAQ{5}pk{F`M~R64lZr zZ#|PPb>Bpfz5VNgiFULZ6}Ln?(VW&iS*|l0Ts$PfK<%<)v*|2REMbnkHLO{&Br``jqedi#l?s&nL02Uxs<4 zaC3L^zGY15#JcAdB z1?a=H4qpMxi5y|F-SxVGhqpwHYMjrl&&*4NpPP4WVqRr_u$qRAbqU+eU6M`UadE9* zDlGrkU(y|xbn2}sP0;LDQ{beV0JYNq7 z)jnL-_7*?7^RJ9`Di~gRLt6eat)r|*K;I>*Y6)p|XSos9X|GA<^2`x!J> zCkOm`2BS(ZG|K?Pv(E61sV=t_a`e7PyNu+q)X94YD2ghQ)INj#<{A{K&GlcJR~0o4 zKJ%SFCv8acelq>#z<}r)L`SY2$rn=!pE0|J+YcWjuhvsn$AUz_3Htna!xfOw{KYCM zLxhfw{>N@L1RmQEOP^h^&~Nc(ZwP^i>alH<(SPx0^LoHee<*Id(JLbjgFp2K#4vcz zJ)J^@6W}+%tL>hyhi1Fj1D1@Yn!aB5+W;=}l*evcATY4p$s)JKgA0yong#_y)(ceE zzvKGa4(wa&H2lHl>=F+cKgmW>gzA)5gjMhsR0*B@b?4afFJr3dPB=JCd;TTMFM znuj*=yAO?Axh3V}a01jP}ZfB|_{>zfE>)LY3HB~ojxu_?@0NbRX#>#RL9@sQboS03Tc ze`N_fk-1}y=UMAHI5B9C{>9`!*43UDcZQ_rv_VhsE;H9!KJ;;|j_b6dVLuWNqE94A z-qab-eAKGrRTrb7blPQhe*8RC4Kd5?MC;ytL+m5wmx3bL4zRFh`)keqiT4T?CbcMI|UUW$$dOh~v?S~`c=~V5F6z#h`>ANQT_Oqjf+oylT6KCFN zT6oG6QB$wMYg?m}`nXI_|Dd3-um1Y*Yh22Oy64+o)xp1oww>57<|$|e+REK)TX9#t z(0GU%T@Lmyq1n;>_4$RvFu>fg;uLksxL8Bk#Lrrkzh?{4u!8!;r5jbA4VLBm$~Qk* z9iov>cTwBl9Q3fd_If2%D6_hq-zaB*$xr3mjz~|`c%jki}LnG`X$8B z9bUKIC0Ri;VB`4Kq_lf_68Ah+dVF9>ZWWHj;LVy*qX&-p?Z!W5(iuSMx4=j!jW#=0 z(s-{F`#N7G&PPXt_p5mP_?+8UtB+?>X{)VBX?qw@Sq>-q&sVm@e{tfJGz(d;sKHSC z+2GgMVtcxt3+8fUNgfq!vS7_6y9`?KJOl{mNUaCab|DYl339^cLvQp^tE=8Jx_G-G zdp33XZCGL&&5sRbNF@7XiZAb7u743fUTKEL;aAokFQAEOYuV$lDF<2dniwp7ek|8o zp)T@$pc~@j&N42YD=!a;o1)?{eeR?8Lixw6N8cl){?Oi8eH%C?jyULQd@=UG zA%?Y{{l31Tgf#af6lSk?cj?yd%G_ex?MKl9-0r*&yh;2EF>OEHP?oCANTW`WxRG6EPDqR zAYHM}eCQu|M1kqR8ai4DB zhlxzxHb(^KD_xLyB3L&aygK#9B`=1W<6Ps2rpcxbXop*34q7FR9X|fl@-=hH@&Za?i%7OmRgnq@sPSS-zN=L2^J*`;p0 z+oOp(3u^8ALAljSw!)hi(Lk$v?vKkK`1nY~1^vnBZr$0qaaXVAPXe9qQ#QjjC{(8@@>cmmG4QV|atIJ$Tki_%?|4yJdX{uSFAS{hXZb1JH} zeB^VyK$~L)&+I&IWv6uK4lJ|zg6HTu<1l5q7*18oVtIS~D2Y-x96z8K(g?go{k)o4 zwVfo0dD2m5GGxb)_e|fyvlW5I_S<06iqjuTqT+J|soX`K;im(cC{+es8uIwq)x>Si z0JB%mWLtlJJlAd>e&&I$)+A2Isq>?gntApjy;95g{B<-GF%CFKrnWzL+j{ZPb{Gnf(2xso`6{ z#uU--h*u&S!qstRkIKgTr0-3RXFQnW+Ucgt=R!{Rz%Ld2u)C~x&V8sUmofRfT-B39 z2vAcKrX6_Re#2SpvhB!gqwJoMzjOt9**Nw3lYF>CDvrtFIq>WzX_^2+v+Z z{xETI1wiD)KFeMqJ)V>wrYHo(?m0o2p$eNHEOGZ?PKfO6R54bMRfjvwozogK%1eO@ zcQzchP}VX_82NQgaCrN^Gc38c5(I|DWR6F2=IW+HZ$<6E472zO5A5J40P<27U)vu` ziC56Xa}oeYde3Fm`8>1Y)g;C>_koB5v;5vsp?XDGt-8I(pSD4|Eei@HfNrOVT%7(ej3 zQ-8;F&;u2R{Iu5pO*qY>VE;MUrzF96qHPP->cU+I&Z;4Z^yH*Ghm8zijIBoh*m)J( zO+vyjMIexQ8Wr7LNJ4_P+zDjvYvkE*)2CPaEC>h-D+j4Q{b1Hx{lY_|m;!DVhH7r< z-f9%qo)KQxC(BYBo8JEOD4B2iF1wZ3f|Z&$BTOiB&dm&eCzW2!T6xW0Z|+(;!Aos+QG|b>-Lo>1_Q6yvE=32GOa(M9Av7{?W&w@ zV|R(;YDSMBe<0=7|dKcDj4sj9ZO6+}EF;zAEAsM7q9lT*C^vAg?sVMU_SHf^lnCT^EvHn5&uV*eAy+qz9+^Ea)g`o2#Xc z65a!}btX3z=+P2VceqnYbXNrfL+h0bIRwbhF#&1KNgTZ{#T zXe!VD8Mqj$hKu)opl-BeHI#>J7;R6F^JO->C<%Nd#ZEH9eWEU+bCVz1bjJZ29s4#q zPliecH(oLA<)6(TH&IcQ&{lEp)m2zs{@SnRzRRS-m88Qq5YP#B#{imU0q3AG`J+qV zxq%L53ZQNLxn!aOkr*3qPhCg%s7(#02REF2=mcl8ZSy7b1Y7E~&S_my9ldNZAK~(c zTVgP7#`TKKlTVomXeF}b-n;QSbKTW?)0Oq&CM?qU0aZhF2BB;K?lTh{=JpyYSF+$R z?b2|+(8HaoilG+7Cn}}IEkXXWt)JicAyV2Fo-H^78#T+J9MLr0|lDbB!1BgN|7v^z27E~Pn>|wSY4P4knOH>IuO<;cPc%-G|v1;Ni1J*EnCh#I5E||Kfud0dcwQ)QrG27NCpQlE(~E zS#MGM>P>c-Uj)7hs@qkN>6gv4?~t|ISOL}G9iJ<0=vA#`y$cl+H}Y*P_tOmrR0TdU zQlvZVsEMJAMCG(cT&qoac2|X@^zBP1Z-z%jjB#V~V)ovnMa14?ob<8GQxbEaTZwbz z(5kOXr5V}w?99Fm26u9{oehr{JQCDndP~)4v_AXXdvb{4XHq_O)&o%~N3M_REGk+Z z1Gt9RelQ9fcO-6kl0mRqhsHj(hI^DHHZS;?!`C6opx-Qa02d5Uv@Vl{&bVtT*ep=G ztZ}<>36@=MSBh`&Kh^^I8u(xbaGmR6R`rAG9`@^54{Y)WHR#>|4DIpQlG_iHo6GG5 zbXaD7;*l^7O-YsqJze;ji4ymRdkR7JpM)CD132yhuOg}iI-~2-ELfd!g8Q4-;}5QH z=>{#8-_dd~tev_<#&zXu3T3)2U|pv}N8w+$S>Tc@gaKT6+GntSv4m=|s&^b_fF2xF zl?ov>;y~q8$I1+>s8j2R`OXx=-1I7 zMi~-6HDsM_thvl@wJHAWh*Zu|d#2b@^??`$>~?8t1@Oohd(SFt`5bs(t?gL}e$nzB zwPlYks5&{I@MhHWzKn!ZM_l{Zl&Qv?y@_WpkA*QYDs0cw%4R|+jUiw6T9VWP!8XdD z??9s7g$RFNBQ#lVOgPFY-6SdV6yqQadU!V2tTXeRb z&AlJq|B3>hwOzHYFTN!5ZHof8Kp*38lxKV3g?iuE;;l%{@HGC(nDx$86K9_( zrxhn{`*10BUco!g=)t1v5?N;6scW9ozK>}!tgL`{1d>ci+z zIN3rii{&75;>x_t0mP`ppR^Y-KZfYNsQl%V<|4lVTj;kXsj}7Xy7dPf%**$u^+|O` zqx>Z2ZivfzYP_rsoHq}h10Oj{3^0=YVO4m%5gNwyLsdaJSL;)m<-OMmn>B| zy($*&Wa7a9M~@EOOcpRM=~uxV`q4d(>M@sZ-habAvbHBn_vv%;(N&zt$<5y?FC)*x zb@kGkOGqxo;11P`<;;q!8IHA{-*kIxX!L!F?@7DTuNg0s9%q>Tgk4cpa%Pbj^pF6F zri~bRD}83fFzOq)EP7MzB6oOr6KSBbE?xl%Ig`Iob$a<(6Kek;6aP5EedP^&w3)df zvdvH^ryZ_^p?K;v*atwPVG^hR>c&PH2uiw8`uj-zwX${zAprIv9_>c^vj0d zLJQV=*jju1uzERUc7E7<-?wO!#!{1bWpkf#U=M9(3hzQ-1eLcIEyf}KXAUG}b{N8O z1HVp0?5%D7dlT0!C^UJ*jS>>l@+WLsM-wjYjMpz$`D}hVu0|7S~R zEeN&Y*gQ8ET*sVhVU$4-1WwJ+=oq#q(qZA@PsVByArcvK$vBkiP0G4iNk;&0_hAbT z+onQ^L|*x8AJ(80+M76{GK@{$KxVFmbUurwUp(n7FM}kuOHcF6+oC~o?tNoV;#4~# zf~Op#H3@tZ6i7*Ro)vUz_asCy?amaxQ^8A$;wVcVbl@3dvzK%%>+HMKllMxn{-jhO zru}Q9CgSySk$p2#4-5M1r*T@z{8M2A0T7puk1M8k?-P5PHO;;{WsR%5@FZYvV4xDGc7zdk6jj)RgH}z zKA=Xeupiq)bhy7jk}8jO0lx`E+6^*8V3$F>+5SxgF-J!UDNdBKlmbb>EgpZ)^xi&s zMcuvcy(o1NB>vFgi;zL-8c)YNN>syeyh}<$d8Q_r^p4_yML%wef|YL9RlC#;5BdFU zt)(eMJKtG->lsd*wVP+M>#zJ1bRZ9z)YID4*9eamddTwB5!qm9Z%s3|C7A)=P*k>Sp=N%CsVSXS57(1eriGGtJ0m-%ca>5U}`b<)@bXV40U}hyp84_HDqx< zd&{2sMVJt59$;wcjbQOIq}@vW#_Cw9ERG|<>^+m#;^hPt&r0((pN#u({|KB5%5tXYF??Ig#CV=)NcCH`J!B(EMvW z_;&g_Mw)7Rmgx8QZ{ll}!&DO%Ml&+wj6Hop0=I#tB( z0~aEtnY4FHoYiTz43x8q76Br@3#^d1fmalC+DGP?&6){mC||H|IrvyRD++irMfI#9 zZyU$Khu4_y1*(K*(*JF~4yx?lgluVh$d1ugPw(aMqp=g;nUW!oRw6_ah0I*?4b*O@3r2S-t3^nMh4&%`RHb&h}g&`wYWmmtPq2RV1^6 zEX3f}U1Xtv@p0`qv4@o4t#bcjP09$^gkVOHrg4`8ji-T>Xmoa6_qT|j^$q@X2*NVs zhtPvNc7=1#N3>^bPs#lP&CcjyAGzz3Yd`98X2cF{l2F^hV%^SCtvA~?cEW7UY&`CY zvPg*V&f9+X582{S`?x?7IPB-%NW`(z8aUP1cO~$4X{YpLqNz=ABgzSgD+fB}67M~T|4-@YAQilddl=34 zmKBSP^8r76<=CmNQJ5blmo7WcEI z`2K6&xsP)R7(|anHU2|m|I!|gb}*aUQu-L%KamjAVIgykiY)#)$_6*K^zH8k$ElI= z@i5o6i~D!QY34 zHMtJf8#6Q4$XxIWf|ZMl3;mfw=Z2o1{@`Lgr=YM94-5uJJUNc--OF82^8N>M(qr6u zJRM^M898~o2)2$uMihvzE(Ju3mWoo40UWdcZCe5d?4hBd=8Lt=P#AG*AN>F7IK${Z zm?rcL6tQ4sI#5Zx!=RkYNO}1WOW=xyY)@64$=$}hMmI_$X$*_W%frG3e{4DXjo1~= zy$a`Em;D6rtFR8ItFMs1f9F^bHM_LGyh=5N^>iQ%Ifk^anJ9m?c z3c3Q8qs$`FNYiksnLWTM^IY7T+(>3?e_2lN4UHQ_N4{f2^KVQ3ndj6w7zHG~JW$sP z2+q-t6;!yJ1$|Sj%kxokl6Tt6Y*q*?%T+ToSfjYTfrnDAd^nvk_SIpO&_BF7WQltO zLdW@(1bbk?o;uhd&&H~vyY`qfvQuoJ%yJgOR8_gXAtxrV9Ox3oP333YZ85IydV`V1vbk&PY73~yW)v8U5M?yeG}_vcthLs z8|r3g0=xmr(-OJj4P#Dxq8-aS7UQ%LKRA^oeF1nU_-5Umeo*Y+)CXnLqjP9vEMG_f zaZ=wujVTe*sJ;%?i6RBc@-BU-pV)hhMQ{zd4dew0;(Ti2>p0p^zAMM}XEa-+(xX%- zD9`jV{)Rxi9sCFx$N!^PJa>yEvuA;f!SpEZ{70JiHOG}2MIp{piS}JOKN>f2Y(mw? zG^AYj+5Yy)^GZ^;ZVaDNXdY}Y(KTUq*c|lY5hUx>B(k;P1|FuV@*>fA05rIJu1xsn z#`Yxbx|hh?R-F|xmlJ&;5!2a=KJdnP13P2d(sjwsl+d%-+6 z+g3*PAuikZBF5}JTADSTAyxN3wJ zc(VWN0OMW9LMy~%aJd%DR_!V&O!;>J31L-adGy(gCvmhKc^>U4sq^}?ul;7(T*CuP z>Pq}qjpoa^?-4p>y^qP)EYu1AbnmvHU;EHC*aB*J)*%VuJm=PzR{jR3>KyaF+@>Xx zF-0kY(F}5(k)-#<1(K30Tb8OPsJ$EJ^U_qX^gWrrWi<=D zuof`X-Km2Mvi_|MEV2!9Y;NuB?w#qnE0zjzQ880QgLwK6PPY3(x}c_lhWg(6ys0X7 zUO>NXYrDAA6kz>%R{`nrmn5_Uj5sXzdD4ELOKM?$KDJ(w#=)m`%Smor;6D~GRhC|Y zu&}UW-B`xRsLh35cq9R;GJlU2+$-gLRp<5YX{lf1&X`aDMrbLmW4QQ$;U7_2hm3%P zBo*wB5h>!Q3AUR~4#_GYs4sXf|Myfva=A$F5=QT6lkq%heZTndeHX62^FE0FhRWZ8 zigXBQy3q6~Ewu7kR?6S3(i z=X5OxXPwvav)SS|sJf~D4N-8FpqPe`h=|_(_m*aeBv-Y>F-__}BiD!Md&=hVAVHh| zYVlRP^ZftkB`=q{xhLEhWFW-kTp9`<9v<3b7P5b9_|A7jT!lge+KQ3OB(eGMUpUwD Ydfo&1=AY%F?%f?~%34a5&&@yl5A@8R%>V!Z diff --git a/src/Resources/Help/HelpFiles/Workbench_Window/Window_Elements_Hide-Show_Buttons/ToolTips_icon.png b/src/Resources/Help/HelpFiles/Workbench_Window/Window_Elements_Hide-Show_Buttons/ToolTips_icon.png new file mode 100644 index 0000000000000000000000000000000000000000..3d27fd70a65c8ab26ecfdea5d39355858a35ef00 GIT binary patch literal 3755 zcmZ`*2{hDS-~S=|9>@V=0aaFp6c90cSU zC!i(*yA%`k;zc6AJT5UYN)Z>!OxjD_hhQ)N0_@gPy!)ggw2ptmp_&WWv|6d*&W5eGyK;7gLNV`ER> zE%o%i1@c-KWec7V4#Ll5{tiCDKmcQj4&P>s_tlQlG|!c!E_UFlmX3|9f4p0s9-}z} zKQUuP!CIUpTxr<1It*PREY=0Ws{-wh6u5kef#C zeqBT6<2bOz)i-U57H$uem*0rB~s;LpnNNG?5~IW&dyEwnOefq>UJ5 zx_b(1sP0To9=!75VD3y>I@_95rTEQP(Y*(InbY_1?;}j&v6vDOLB!ms(UMSKWM%4*S%6B7 zah50`mdXdhdOg2nW$!78n-jC zrG|bV``5UWXAB}7N%t;;c?F{Vo*5W3ni89-kv~)1VBMg>FgB49)DNEVx-o~4hu<@i zU^kDNatIG%9E!7XP?e{#r4w#P`q1UmqY?)3?mt8pXzb$N;v<3>B1ltN5)wRdd{q678I{H*|tH9l`i>QW0X=DKhPI z4&nbApB32V*p}UfdotC5<`Y0YDJwqmF8P!rl&oTA8(JEQ8mJrYG%zl>DpOY8 zukQ-QJMC$lXos=4az}DUQlD|Ds;BX9Fmh3-U*hJ)8tcpJyYVl8A20|de$^tuF~J_> zXf3FT3CmuFGVw4jJ<<{I%1}X4>Z%R!XFW=JRFM&{W7lW;2KHA$^8+znlrRr({iDy1 zmTrqMhf^hA*6e@kSvqL~F?nJP9~^TYa-Nk`&Lv1@Ne)ZaJ1_QKb|yI6xLEY9^=0=z z$!y_0wn_n)Lf0X^>34)JL<6uRIbY z%y3|6v$Zp{U-kq)G%?64sV-5gHvJZ}rWeoGnU&rl;);>%shO+^s}ZmwuMMdcs-dmv ztd(<@L#j^mZc6ws```3e#Pncf{7Eoqe@+a~2IGEC$o-Je{ic2RC(ex+>vTR(s0hR33FI*EJ;Kgcp6+1I@UkB;&^2a z$_y#DmAAhs>n}4b?Ii({EW#dQy`(*0q18HpCPTP_!c1-2;Q}EoNm?cRZLpS_u(bp|a9g04pvFl3nuL@$9a9SXUI#6i^jl z7C@p;dRGF?`R$cT&*E@I%cAh(%L#L$}V~|&xcFEBj8HmWV4%|khdu+YaZ)wr|TzDw^=tW z@CTDivv%7{R{-)KWZP6h%r96)Xgn#2)wiG8sHeu##!SRS+QisgZhr39U(Jx5wvbj~ zY_%~kb~Kji;O)@uK;KE*DToI#zF{2X+vI~^57EljqSF>nvf`Wqy}m4-=>70rw^r9= z*8?cQx+(NipCs0WbZ{3Uhj>zZGDnrgB6@A)b*4?fWw7KGCQkM{JWBnP%ckg_m04di zUnlFpUGCj$+{qRLoU2M-^nQ%46fEm4+X=`EZr@}x!03wUg^ByHno=wZ9SVSSjxxD2 z!vun_3Q87>vJ`S)L$q~Rgs7D)kmUnbQ^JyrEQw?jT<-%)AKpLhlKEtO^mTulhO}#A zUx!8zE2x<}lAD!VXmO()o-0@=IJDU>JF=K}s@!{ReO+clWhGb)^2yb%maVU^(xbjFE*OJvgCT6Q%MGjT z^cy^vRQJ7W=b8ey4m51>1io*$H@JSU1+SUNnjcjsy)SpZ%!ej>B_Dil-(}cscst?Z z>UBR%*RX%DBFbjC_`&;*_w;Wqc3aKw+636s`=&aN+BZf9Z`pomQTlFZE?~ZI(PSoT z_R`Gj^-)b~{pab3>7P9$o8(D!VW+x#6D_fetQ1*FjeWMZy;haomHlpdW4cwU)m3qhYu@n8{rgsWwUz6&QPC&Y z;6}=YkrA63Blo@pm>h2nQNtj zwGz4@xTSc$?rmdl$@)G@wyj{dcE_vLA+U&0aynes+8w@*YFuavnqT>zrL?W|;WYX* zJh1cgA}2ytA}BTRbN6ZHF^b0-o)eUFK=JLxUe&_N8)0{&SCMi@Ey%Hey@j-16(&dQ%UXHI?sXY8w%f1!uh{h?xZ`5*#E%c0+=!E_6p1yd#U&&;#x&4+mG?Z~R1 zo9xLPoWa*3lnCDQp{oZs;E2c#HhS*JuQ0+&S82!SK%qyQL>ngj;mJ{$BSFCX%?WnA zzV36x5|2T#p}~E5$B_ptYP^)ay(D|P6hNjM;FgFI8W=iS$&I?Ja!M23wqi-I!c+%v zx!-7}$Y|9uUnP2)AV7O~Er(Q%jk?|3{QZgR^WHm_BO`pZ;OXa{?wcyh)V|t47-vpi zo?C9-k$G%lIG}diYiS1uA-dWgyS*DbTplUS4*saR%o8|3ijC`lcme=1G13%)K^W*O zI-$HE4$de?7f6to&-up|0F;9i&q*&Ai~~5x%hMaJ7^K4Y%R=#-z7RwCz`sl|9x8kY z17olz%FhKX1CfSE@WH9TV6d{E^IgSTTH61>&p8!7Hw?x{5ef|q41@$qK~R3KP?&;( z0#rg0Dk&*`ZXu2i_Qp5_iF>2@e>?eKKUywmCqJYQ28r?pU-)%!MEPS>`1mdY{d@h+ z(Qsr+nc|(HI%z;%TXc^m6e= zpQi?wl2iT_<-ZjF$@32&;t!zApTIv9e*iD?P&D#Gx}3M@q7iVIGW5S>|JW--FS_(c z*M5)Wm-Kula4Kc!zvp|-6yg;CfT&DYOWhPfxnlvVh?Ss`VOigxOiN>m53XG$5x(mXfa+#*{|^}H_krr6~tko0`o!jKqramA}M zi2!$J{O6)z-l{lij&C6?n=z#NFqpwaGj0Bgyx5g)>0ZA1I+YSzvzfY`43-`@bpA?gY?#`P>rtmu+tDJt*796qf+; z23^`>O8v6e!TL2qwaE&aDIPU^u2)>wvU>MbDIZK_LLJo^fR@y;bbP3;3A41b8E0~F zx#!-(kEI;`Md%4Gd`Xk}dX%%!JNTwg%BAqJz?&kFVLe|msN$Ead*M9G^6}CJe_2gh z1aB9&Gx)W3Kc#SPTaxn6b{Qb7BP9|6B2elLvYt0$)2~iMXV_U791F%b$A3oUE>DR> zccbz|*JTK76AjjoxiF7Nc`5&9D;Mp?4TGJp>@DWGhhz$QxQ{9Qjfjh - - - - Window Elements Hide/Show Buttons - - - - Window Elements Hide/Show - Buttons
    - The Hide/Show buttons for the
    Workbench Window - elements (Help - Window, + + + + Window Elements Hide/Show Buttons + + + + Window Elements Hide/Show + Buttons
    + The Hide/Show buttons for the
    Workbench Window + elements (Help + Window, Information - - Window, Scenes - Window, Toolbar, - Overlay - Toolbox, and
    , Scenes + Window, Toolbar, + Overlay + Toolbox, and Features - - Toolbox) are located in the upper right corner - of the Workbench Window. These buttons turn the display of these - elements on and off. The buttons are shaded gray (as in the images - below) to indicate that the element is on, and are unshaded when - the element is off. -

       Help - Window button
    -
    -
        Information - - - - - - - Window button
    -
    -     Identify Brainordinate button
    -
    -     Scenes Window button
    -
    -     Toolbar button
    -
    -     Overlay Toolbox button
    -
    -     Features Toolbox - button
    - - + + + + Toolbox) are located in the upper right corner + of the Workbench Window. These buttons turn the display of these + elements on and off. The buttons are shaded gray (as in the images + below) to indicate that the element is on, and are unshaded when + the element is off. +
    tooltips icon Tool Tips button  Enables + tool tips that display information about the brainordinate under + the mouse.  To show a tooltip, move the mouse over a surface + or volume and then 'rest' (do not move) the mouse for a couple of + second.  A small amount of text will appear that is similar + to an identification (mouse click) operation.
    +
    +    Help Window + button
    +
    +
        + + Information Window button
    +
    +     Identify + Brainordinate button
    +
    +     Scenes Window button
    +
    +     Toolbar button
    +
    +     Overlay Toolbox button
    +
    +     Features + Toolbox button
    + + diff --git a/src/Resources/Help/help_resources.qrc b/src/Resources/Help/help_resources.qrc index 08e855496..10eebe98a 100644 --- a/src/Resources/Help/help_resources.qrc +++ b/src/Resources/Help/help_resources.qrc @@ -240,6 +240,7 @@ ./HelpFiles/Workbench_Window/Window_Elements_Hide-Show_Buttons/Overlay_TB_icon.png ./HelpFiles/Workbench_Window/Window_Elements_Hide-Show_Buttons/Scenes_icon.png ./HelpFiles/Workbench_Window/Window_Elements_Hide-Show_Buttons/Toolbar_icon.png +./HelpFiles/Workbench_Window/Window_Elements_Hide-Show_Buttons/ToolTips_icon.png ./HelpFiles/Workbench_Window/Window_Elements_Hide-Show_Buttons/Window_Elements_Hide-Show_Buttons.html ./HelpFiles/Workbench_Window/Window_Elements_Hide-Show_Buttons/WindowForScene.tiff ./HelpFiles/Workbench_Window/Workbench_Window.html -- GitLab From 7bad1f9ca53b35ad171b53cc518ee31f09c3f8f6 Mon Sep 17 00:00:00 2001 From: John Harwell Date: Fri, 28 Sep 2018 12:23:31 -0500 Subject: [PATCH 067/427] Resolve WB-831 Problems controlling display of tab space annotations in features toolbox with tile tabs enabled: This problem has been resolved. The cause was that the tab index used for changing the display status was the tab index of the selected tab and not the tab index from the tab space annotation. Note that this problem only occurred when the "Group" in the Features Toolbox was set to "Tab". When "Group" was set to "Group A, B..." the existing functionality was and continues to operate correctly. --- src/Annotations/Annotation.cxx | 44 ++++++++++++++++++++++--- src/Annotations/Annotation.h | 3 ++ src/Annotations/AnnotationGroup.cxx | 50 +++++++++++++++++++++++------ src/Annotations/AnnotationGroup.h | 3 ++ 4 files changed, 87 insertions(+), 13 deletions(-) diff --git a/src/Annotations/Annotation.cxx b/src/Annotations/Annotation.cxx index db2611468..0524c5cf1 100644 --- a/src/Annotations/Annotation.cxx +++ b/src/Annotations/Annotation.cxx @@ -2299,8 +2299,11 @@ Annotation::isItemExpanded(const DisplayGroupEnum::Enum displayGroup, return m_displayGroupAndTabItemHelper->isExpandedInWindow(m_windowIndex); } + const int32_t itemTabIndex = updateDisplayGroupTabIndex(displayGroup, + tabIndex); + return m_displayGroupAndTabItemHelper->isExpanded(displayGroup, - tabIndex); + itemTabIndex); } /** @@ -2327,8 +2330,11 @@ Annotation::setItemExpanded(const DisplayGroupEnum::Enum displayGroup, status); } else { + const int32_t itemTabIndex = updateDisplayGroupTabIndex(displayGroup, + tabIndex); + m_displayGroupAndTabItemHelper->setExpanded(displayGroup, - tabIndex, + itemTabIndex, status); } @@ -2355,8 +2361,11 @@ Annotation::getItemDisplaySelected(const DisplayGroupEnum::Enum displayGroup, return m_displayGroupAndTabItemHelper->getSelectedInWindow(m_windowIndex); } + const int32_t itemTabIndex = updateDisplayGroupTabIndex(displayGroup, + tabIndex); + return m_displayGroupAndTabItemHelper->getSelected(displayGroup, - tabIndex); + itemTabIndex); } /* @@ -2389,12 +2398,39 @@ Annotation::setItemDisplaySelected(const DisplayGroupEnum::Enum displayGroup, status); } else { + const int32_t itemTabIndex = updateDisplayGroupTabIndex(displayGroup, + tabIndex); + m_displayGroupAndTabItemHelper->setSelected(displayGroup, - tabIndex, + itemTabIndex, status); } } +/** + * Update the tab index to correspond to the tab index used for this + * annotation if it is in tab annotation space. This functionality + * was added to resolve WB-831. + * + * @param displayGroup + * The display group. + * @param tabIndex + * Index of the tab. + */ +int32_t +Annotation::updateDisplayGroupTabIndex(const DisplayGroupEnum::Enum displayGroup, + const int32_t tabIndex) const +{ + int32_t tabIndexOut(tabIndex); + if (getCoordinateSpace() == AnnotationCoordinateSpaceEnum::TAB) { + if (displayGroup == DisplayGroupEnum::DISPLAY_GROUP_TAB) { + tabIndexOut = getTabIndex(); + } + } + return tabIndexOut; +} + + /** * Is this item selected for editing in the given window? * diff --git a/src/Annotations/Annotation.h b/src/Annotations/Annotation.h index 1d4eff719..aa5ca9e0b 100644 --- a/src/Annotations/Annotation.h +++ b/src/Annotations/Annotation.h @@ -381,6 +381,9 @@ namespace caret { void setUniqueKey(const int32_t uniqueKey); + int32_t updateDisplayGroupTabIndex(const DisplayGroupEnum::Enum displayGroup, + const int32_t tabIndex) const; + SceneClassAssistant* m_sceneAssistant; DisplayGroupAndTabItemHelper* m_displayGroupAndTabItemHelper; diff --git a/src/Annotations/AnnotationGroup.cxx b/src/Annotations/AnnotationGroup.cxx index 4f71c5bbf..38777fc4b 100644 --- a/src/Annotations/AnnotationGroup.cxx +++ b/src/Annotations/AnnotationGroup.cxx @@ -898,17 +898,19 @@ AnnotationGroup::isItemExpandable() const * * @param displayGroup * The display group. - * @param tabIndex + * @param tabIndexIn * Index of the tab. */ bool AnnotationGroup::isItemExpanded(const DisplayGroupEnum::Enum displayGroup, - const int32_t tabIndex) const + const int32_t tabIndexIn) const { if (m_coordinateSpace == AnnotationCoordinateSpaceEnum::WINDOW) { return m_displayGroupAndTabItemHelper->isExpandedInWindow(m_tabOrWindowIndex); } + const int32_t tabIndex = updateDisplayGroupTabIndex(displayGroup, + tabIndexIn); return m_displayGroupAndTabItemHelper->isExpanded(displayGroup, tabIndex); } @@ -918,14 +920,14 @@ AnnotationGroup::isItemExpanded(const DisplayGroupEnum::Enum displayGroup, * * @param displayGroup * The display group. - * @param tabIndex + * @param tabIndexIn * Index of the tab. * @param status * New expanded status. */ void AnnotationGroup::setItemExpanded(const DisplayGroupEnum::Enum displayGroup, - const int32_t tabIndex, + const int32_t tabIndexIn, const bool status) { if (m_coordinateSpace == AnnotationCoordinateSpaceEnum::WINDOW) { @@ -933,6 +935,8 @@ AnnotationGroup::setItemExpanded(const DisplayGroupEnum::Enum displayGroup, status); } else { + const int32_t tabIndex = updateDisplayGroupTabIndex(displayGroup, + tabIndexIn); m_displayGroupAndTabItemHelper->setExpanded(displayGroup, tabIndex, status); @@ -944,13 +948,16 @@ AnnotationGroup::setItemExpanded(const DisplayGroupEnum::Enum displayGroup, * * @param displayGroup * The display group. - * @param tabIndex + * @param tabIndexIn * Index of the tab. */ TriStateSelectionStatusEnum::Enum AnnotationGroup::getItemDisplaySelected(const DisplayGroupEnum::Enum displayGroup, - const int32_t tabIndex) const + const int32_t tabIndexIn) const { + const int32_t tabIndex = updateDisplayGroupTabIndex(displayGroup, + tabIndexIn); + TriStateSelectionStatusEnum::Enum status = TriStateSelectionStatusEnum::UNSELECTED; const int numChildren = getNumberOfAnnotations(); @@ -987,14 +994,14 @@ AnnotationGroup::getItemDisplaySelected(const DisplayGroupEnum::Enum displayGrou * * @param displayGroup * The display group. - * @param tabIndex + * @param tabIndexIn * Index of the tab. * @param status * New selection status. */ void AnnotationGroup::setItemDisplaySelected(const DisplayGroupEnum::Enum displayGroup, - const int32_t tabIndex, + const int32_t tabIndexIn, const TriStateSelectionStatusEnum::Enum status) { switch (status) { @@ -1008,18 +1015,43 @@ AnnotationGroup::setItemDisplaySelected(const DisplayGroupEnum::Enum displayGrou break; } + const int32_t tabIndex = updateDisplayGroupTabIndex(displayGroup, + tabIndexIn); + /* * Note: An annotation group's selection status is based * of the the group's annotations so we do not need to set * an explicit selection status for the group. */ - DisplayGroupAndTabItemInterface::setChildrenDisplaySelectedHelper(this, displayGroup, tabIndex, status); } +/** + * Update the tab index to correspond to the tab index used for this + * annotation group if it is in tab annotation space. This functionality + * was added to resolve WB-831. + * + * @param displayGroup + * The display group. + * @param tabIndex + * Index of the tab. + */ +int32_t +AnnotationGroup::updateDisplayGroupTabIndex(const DisplayGroupEnum::Enum displayGroup, + const int32_t tabIndex) const +{ + int32_t tabIndexOut(tabIndex); + if (getCoordinateSpace() == AnnotationCoordinateSpaceEnum::TAB) { + if (displayGroup == DisplayGroupEnum::DISPLAY_GROUP_TAB) { + tabIndexOut = getTabOrWindowIndex(); + } + } + return tabIndexOut; +} + /** * Is this item selected for editing in the given window? * diff --git a/src/Annotations/AnnotationGroup.h b/src/Annotations/AnnotationGroup.h index b90440221..767348950 100644 --- a/src/Annotations/AnnotationGroup.h +++ b/src/Annotations/AnnotationGroup.h @@ -162,6 +162,9 @@ namespace caret { void initializeInstance(); + int32_t updateDisplayGroupTabIndex(const DisplayGroupEnum::Enum displayGroup, + const int32_t tabIndex) const; + SceneClassAssistant* m_sceneAssistant; DisplayGroupAndTabItemHelper* m_displayGroupAndTabItemHelper; -- GitLab From 0a4126239eb8d74cefd9801232b9f37470bf46d8 Mon Sep 17 00:00:00 2001 From: John Harwell Date: Mon, 1 Oct 2018 14:29:45 -0500 Subject: [PATCH 068/427] WB-729 Tab Dragging: Dragging tabs is slow as there are graphics updates each time the 'dragged tab' is moved past another tab. A new implementation to improve performance has been added. When the user presses the mouse in the tab bar, graphics and user-interface updates are disabled until the mouse is released. This new functionality must be enabled by selecting "Tab Dragging (faster)" from the Develop Menu. --- src/Common/DeveloperFlagsEnum.cxx | 4 + src/Common/DeveloperFlagsEnum.h | 3 +- src/GuiQt/BrainBrowserWindow.cxx | 48 +++++++++++ src/GuiQt/BrainBrowserWindow.h | 4 + src/GuiQt/BrainBrowserWindowToolBar.cxx | 5 +- src/GuiQt/BrainBrowserWindowToolBar.h | 4 +- .../BrainBrowserWindowToolBarTabPopUpMenu.cxx | 1 + src/GuiQt/BrainOpenGLWidget.cxx | 24 ++++++ src/GuiQt/BrainOpenGLWidget.h | 7 +- src/GuiQt/CMakeLists.txt | 3 + src/GuiQt/WuQTabBar.cxx | 80 +++++++++++++++++++ src/GuiQt/WuQTabBar.h | 70 ++++++++++++++++ 12 files changed, 246 insertions(+), 7 deletions(-) create mode 100644 src/GuiQt/WuQTabBar.cxx create mode 100644 src/GuiQt/WuQTabBar.h diff --git a/src/Common/DeveloperFlagsEnum.cxx b/src/Common/DeveloperFlagsEnum.cxx index 4cae6e410..29f545f21 100644 --- a/src/Common/DeveloperFlagsEnum.cxx +++ b/src/Common/DeveloperFlagsEnum.cxx @@ -117,6 +117,10 @@ DeveloperFlagsEnum::initialize() "DEVELOPER_FLAG_FLIP_PALETTE_NOT_DATA", "Flip Palette Not Data", false)); + enumData.push_back(DeveloperFlagsEnum(DEVELOPER_FLAG_TAB_DRAGGING, + "DEVELOPER_FLAG_TAB_DRAGGING", + "Tab Dragging (faster)", + false)); } /** diff --git a/src/Common/DeveloperFlagsEnum.h b/src/Common/DeveloperFlagsEnum.h index 2a352066e..1b8e1109e 100644 --- a/src/Common/DeveloperFlagsEnum.h +++ b/src/Common/DeveloperFlagsEnum.h @@ -36,7 +36,8 @@ public: */ enum Enum { DEVELOPER_FLAG_UNUSED, - DEVELOPER_FLAG_FLIP_PALETTE_NOT_DATA + DEVELOPER_FLAG_FLIP_PALETTE_NOT_DATA, + DEVELOPER_FLAG_TAB_DRAGGING }; ~DeveloperFlagsEnum(); diff --git a/src/GuiQt/BrainBrowserWindow.cxx b/src/GuiQt/BrainBrowserWindow.cxx index d42c19a1c..255d56303 100644 --- a/src/GuiQt/BrainBrowserWindow.cxx +++ b/src/GuiQt/BrainBrowserWindow.cxx @@ -105,6 +105,7 @@ #include "WuQDataEntryDialog.h" #include "WuQDoubleSpinBox.h" #include "WuQMessageBox.h" +#include "WuQTabBar.h" #include "WuQtUtilities.h" #include "WuQTextEditorDialog.h" #include "VtkFileExporter.h" @@ -223,6 +224,10 @@ m_browserWindowIndex(browserWindowIndex) this); m_showToolBarAction = m_toolbar->toolBarToolButtonAction; addToolBar(m_toolbar); + QObject::connect(this->m_toolbar->tabBar, &WuQTabBar::mousePressedSignal, + this, &BrainBrowserWindow::tabBarMousePressedSlot); + QObject::connect(this->m_toolbar->tabBar, &WuQTabBar::mouseReleasedSignal, + this, &BrainBrowserWindow::tabBarMouseReleasedSlot); createActions(); @@ -476,6 +481,49 @@ BrainBrowserWindow::receiveEvent(Event* event) } } +/** + * Called when tab bar mouse pressed. + */ +void +BrainBrowserWindow::tabBarMousePressedSlot() +{ + if (DeveloperFlagsEnum::isFlag(DeveloperFlagsEnum::DEVELOPER_FLAG_TAB_DRAGGING)) { + /* + * Dragging tabs is slow because as the tab is moved, there are many graphics + * and user-interface updates. So, disable these updates during the time the + * mouse is pressed and released. + * + * Block user-interface updates and also disable graphics drawing. While there is + * an event for graphics updates, the windowing system may directly call the + * drawing code (BrainOpenGLWidget::paintGL()) so we simply disable graphics drawing. + */ + EventManager::get()->blockEvent(EventTypeEnum::EVENT_USER_INTERFACE_UPDATE, true); + BrainOpenGLWidget::setDrawingBlocked(true); + } +} + +/** + * Called when tab bar mouse released. + */ +void +BrainBrowserWindow::tabBarMouseReleasedSlot() +{ + if (DeveloperFlagsEnum::isFlag(DeveloperFlagsEnum::DEVELOPER_FLAG_TAB_DRAGGING)) { + /* + * Enable user-interface updates and graphics drawing. + */ + EventManager::get()->blockEvent(EventTypeEnum::EVENT_USER_INTERFACE_UPDATE, false); + BrainOpenGLWidget::setDrawingBlocked(false); + EventManager::get()->sendEvent(EventUserInterfaceUpdate().setWindowIndex(m_browserWindowIndex).getPointer()); + + /** + * Causes graphics and user-interface to update. + * A box is drawn around selected tab. + */ + m_toolbar->selectedTabChanged(m_toolbar->tabBar->currentIndex()); + } +} + /** * Resize the Dock Widgets (same as QMainWindow::resizeDocks in Qt 5.7 and later) * diff --git a/src/GuiQt/BrainBrowserWindow.h b/src/GuiQt/BrainBrowserWindow.h index fdf5c685a..405091a07 100644 --- a/src/GuiQt/BrainBrowserWindow.h +++ b/src/GuiQt/BrainBrowserWindow.h @@ -245,6 +245,10 @@ namespace caret { void aspectRatioDialogUpdateForTab(const double aspectRatio); void aspectRatioDialogUpdateForWindow(const double aspectRatio); + void tabBarMousePressedSlot(); + + void tabBarMouseReleasedSlot(); + private: /** Contains status of components such as enter/exit full screen */ struct WindowComponentStatus { diff --git a/src/GuiQt/BrainBrowserWindowToolBar.cxx b/src/GuiQt/BrainBrowserWindowToolBar.cxx index 417b8dd86..4f9f1a757 100644 --- a/src/GuiQt/BrainBrowserWindowToolBar.cxx +++ b/src/GuiQt/BrainBrowserWindowToolBar.cxx @@ -44,7 +44,6 @@ #include #include #include -#include #include #include #include @@ -124,6 +123,7 @@ #include "WuQDataEntryDialog.h" #include "WuQFactory.h" #include "WuQMessageBox.h" +#include "WuQTabBar.h" #include "WuQWidgetObjectGroup.h" #include "WuQtUtilities.h" @@ -181,7 +181,7 @@ BrainBrowserWindowToolBar::BrainBrowserWindowToolBar(const int32_t browserWindow /* * Create tab bar that displays models. */ - this->tabBar = new QTabBar(); + this->tabBar = new WuQTabBar(); if (WuQtUtilities::isSmallDisplay()) { this->tabBar->setStyleSheet("QTabBar::tab:selected {" " font: bold;" @@ -232,7 +232,6 @@ BrainBrowserWindowToolBar::BrainBrowserWindowToolBar(const int32_t browserWindow this, SLOT(tabCloseSelected(int))); QObject::connect(this->tabBar, SIGNAL(tabMoved(int,int)), this, SLOT(tabMoved(int,int))); - /* * Add context menu diff --git a/src/GuiQt/BrainBrowserWindowToolBar.h b/src/GuiQt/BrainBrowserWindowToolBar.h index d7bfe5410..76a0b15d0 100644 --- a/src/GuiQt/BrainBrowserWindowToolBar.h +++ b/src/GuiQt/BrainBrowserWindowToolBar.h @@ -45,7 +45,6 @@ class QLabel; class QMenu; class QRadioButton; class QSpinBox; -class QTabBar; class QToolButton; namespace caret { @@ -74,6 +73,7 @@ namespace caret { class Surface; class SurfaceSelectionViewController; class StructureSurfaceSelectionControl; + class WuQTabBar; class WuQWidgetObjectGroup; class BrainBrowserWindowToolBar : public QToolBar, public EventListenerInterface, public SceneableInterface { @@ -216,7 +216,7 @@ namespace caret { QHBoxLayout* toolbarWidgetLayout; QWidget* tabBarWidget; - QTabBar* tabBar; + WuQTabBar* tabBar; /** Widget displayed at bottom of toolbar for mouse input controls */ QWidget* userInputControlsWidget; diff --git a/src/GuiQt/BrainBrowserWindowToolBarTabPopUpMenu.cxx b/src/GuiQt/BrainBrowserWindowToolBarTabPopUpMenu.cxx index 5433697d3..f2df57ec9 100644 --- a/src/GuiQt/BrainBrowserWindowToolBarTabPopUpMenu.cxx +++ b/src/GuiQt/BrainBrowserWindowToolBarTabPopUpMenu.cxx @@ -25,6 +25,7 @@ #include "BrainBrowserWindowToolBar.h" #include "CaretAssert.h" +#include "WuQTabBar.h" using namespace caret; diff --git a/src/GuiQt/BrainOpenGLWidget.cxx b/src/GuiQt/BrainOpenGLWidget.cxx index b7c9b46f5..eb23e52a1 100644 --- a/src/GuiQt/BrainOpenGLWidget.cxx +++ b/src/GuiQt/BrainOpenGLWidget.cxx @@ -603,6 +603,26 @@ BrainOpenGLWidget::getDrawingWindowContent(const int32_t windowViewportIn[4], windowContent.setWindowViewport(windowViewportContent); } +/** + * @return True if drawing is blocked + */ +bool +BrainOpenGLWidget::isDrawingBlocked() +{ + return s_drawingBlockedFlag; +} + +/** + * Set drawing blocked status. + * + * @param blocked + * New blocked status. + */ +void +BrainOpenGLWidget::setDrawingBlocked(const bool blocked) +{ + s_drawingBlockedFlag = blocked; +} /** * Paints the graphics. @@ -610,6 +630,10 @@ BrainOpenGLWidget::getDrawingWindowContent(const int32_t windowViewportIn[4], void BrainOpenGLWidget::paintGL() { + if (isDrawingBlocked()) { + return; + } + #ifdef WORKBENCH_USE_QT5_QOPENGL_WIDGET if (m_contextShareGroupPointer == NULL) { m_contextShareGroupPointer = context()->shareGroup(); diff --git a/src/GuiQt/BrainOpenGLWidget.h b/src/GuiQt/BrainOpenGLWidget.h index ef7f45817..fe53fedf4 100644 --- a/src/GuiQt/BrainOpenGLWidget.h +++ b/src/GuiQt/BrainOpenGLWidget.h @@ -116,6 +116,10 @@ namespace caret { QImage performOffScreenImageCapture(const int32_t imageWidth, const int32_t imageHeight); + static bool isDrawingBlocked(); + + static void setDrawingBlocked(const bool blocked); + protected: virtual void initializeGL(); @@ -213,7 +217,7 @@ namespace caret { static BrainOpenGL* s_singletonOpenGL; - + static bool s_drawingBlockedFlag; }; #ifdef __BRAIN_OPENGL_WIDGET_DEFINE__ @@ -221,6 +225,7 @@ namespace caret { bool BrainOpenGLWidget::s_defaultGLFormatInitialized = false; std::set BrainOpenGLWidget::s_brainOpenGLWidgets; BrainOpenGL* BrainOpenGLWidget::s_singletonOpenGL = NULL; + bool BrainOpenGLWidget::s_drawingBlockedFlag = false; #endif // __BRAIN_OPENGL_WIDGET_DEFINE__ } // namespace diff --git a/src/GuiQt/CMakeLists.txt b/src/GuiQt/CMakeLists.txt index d8a646603..4d401491f 100644 --- a/src/GuiQt/CMakeLists.txt +++ b/src/GuiQt/CMakeLists.txt @@ -208,6 +208,7 @@ ELSE() WuQSpecialIncrementDoubleSpinBox.h WuQSpinBoxGroup.h WuQSpinBoxOddValue.h + WuQTabBar.h WuQTabWidget.h WuQTabWidgetWithSizeHint.h WuQTextEditorDialog.h @@ -453,6 +454,7 @@ WuQwtPlot.h WuQSpecialIncrementDoubleSpinBox.h WuQSpinBoxGroup.h WuQSpinBoxOddValue.h +WuQTabBar.h WuQTabWidget.h WuQTabWidgetWithSizeHint.h WuQTextEditorDialog.h @@ -684,6 +686,7 @@ WuQwtPlot.cxx WuQSpecialIncrementDoubleSpinBox.cxx WuQSpinBoxGroup.cxx WuQSpinBoxOddValue.cxx +WuQTabBar.cxx WuQTabWidget.cxx WuQTabWidgetWithSizeHint.cxx WuQTextEditorDialog.cxx diff --git a/src/GuiQt/WuQTabBar.cxx b/src/GuiQt/WuQTabBar.cxx new file mode 100644 index 000000000..6b1a2934d --- /dev/null +++ b/src/GuiQt/WuQTabBar.cxx @@ -0,0 +1,80 @@ + +/*LICENSE_START*/ +/* + * Copyright (C) 2018 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + +#define __WU_Q_TAB_BAR_DECLARE__ +#include "WuQTabBar.h" +#undef __WU_Q_TAB_BAR_DECLARE__ + +#include "CaretAssert.h" +using namespace caret; + + + +/** + * \class caret::WuQTabBar + * \brief Overrides and adds capabilities to QTabBar + * \ingroup GuiQt + */ + +/** + * Constructor. + */ +WuQTabBar::WuQTabBar(QWidget* parent) +: QTabBar(parent) +{ + +} + +/** + * Destructor. + */ +WuQTabBar::~WuQTabBar() +{ +} + +/** + * Overrides parent widget's method to emit mousePressedSignal. + * + * @param event + * The mouse event. + */ +void +WuQTabBar::mousePressEvent(QMouseEvent* event) +{ + emit mousePressedSignal(); + + QTabBar::mousePressEvent(event); +} + +/** + * Overrides parent widget's method to emit mouseReleaseSignal. + * + * @param event + * The mouse event. + */ +void +WuQTabBar::mouseReleaseEvent(QMouseEvent* event) +{ + emit mouseReleasedSignal(); + + QTabBar::mouseReleaseEvent(event); +} + diff --git a/src/GuiQt/WuQTabBar.h b/src/GuiQt/WuQTabBar.h new file mode 100644 index 000000000..84d294e2e --- /dev/null +++ b/src/GuiQt/WuQTabBar.h @@ -0,0 +1,70 @@ +#ifndef __WU_Q_TAB_BAR_H__ +#define __WU_Q_TAB_BAR_H__ + +/*LICENSE_START*/ +/* + * Copyright (C) 2018 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + + + +#include + +#include + + + +namespace caret { + + class WuQTabBar : public QTabBar { + + Q_OBJECT + + public: + WuQTabBar(QWidget* parent = 0); + + virtual ~WuQTabBar(); + + WuQTabBar(const WuQTabBar&) = delete; + + WuQTabBar& operator=(const WuQTabBar&) = delete; + + + // ADD_NEW_METHODS_HERE + + signals: + void mousePressedSignal(); + + void mouseReleasedSignal(); + + protected: + virtual void mousePressEvent(QMouseEvent* event) override; + + virtual void mouseReleaseEvent(QMouseEvent* event) override; + + private: + // ADD_NEW_MEMBERS_HERE + + }; + +#ifdef __WU_Q_TAB_BAR_DECLARE__ + // +#endif // __WU_Q_TAB_BAR_DECLARE__ + +} // namespace +#endif //__WU_Q_TAB_BAR_H__ -- GitLab From aa0111f7f345d72cd384869ba2e1a303c4f763cc Mon Sep 17 00:00:00 2001 From: Tim Coalson Date: Mon, 1 Oct 2018 20:03:50 -0500 Subject: [PATCH 069/427] color single-value ROIs as max palette color by default by treating equals case as max --- src/Palette/PaletteColorMapping.cxx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Palette/PaletteColorMapping.cxx b/src/Palette/PaletteColorMapping.cxx index de2a3a975..97ea9fc17 100644 --- a/src/Palette/PaletteColorMapping.cxx +++ b/src/Palette/PaletteColorMapping.cxx @@ -1963,7 +1963,7 @@ PaletteColorMapping::mapDataToPaletteNormalizedValues(const FastStatistics* stat { normalized = (scalar - mappingLeastPositive) / mappingPositiveDenominator + PALETTE_ZERO_COLOR_ZONE; } else { - if (scalar > mappingLeastPositive) + if (scalar >= mappingMostPositive) { normalized = 1.0f; } @@ -1979,7 +1979,7 @@ PaletteColorMapping::mapDataToPaletteNormalizedValues(const FastStatistics* stat { normalized = (scalar - mappingLeastNegative) / mappingNegativeDenominator - PALETTE_ZERO_COLOR_ZONE; } else { - if (scalar < mappingLeastNegative) + if (scalar <= mappingMostNegative) { normalized = -1.0f; } -- GitLab From 4b3935110b39ebd62f8dfedc4eefbc9d86c455fd Mon Sep 17 00:00:00 2001 From: John Harwell Date: Tue, 2 Oct 2018 09:40:16 -0500 Subject: [PATCH 070/427] WB-729 Tab Dragging: Allow graphics updates so that in tile tabs, the tab being dragged by the user is highlighted. User-interface updates are still blocked and performance is better. Must be enabled by selecting "Tab Dragging (faster)" from the Develop Menu. Also moved this tab dragging logic out of window and into toolbar. --- src/GuiQt/BrainBrowserWindow.cxx | 47 ------------------- src/GuiQt/BrainBrowserWindow.h | 4 -- src/GuiQt/BrainBrowserWindowToolBar.cxx | 60 +++++++++++++++++++++++++ src/GuiQt/BrainBrowserWindowToolBar.h | 2 + 4 files changed, 62 insertions(+), 51 deletions(-) diff --git a/src/GuiQt/BrainBrowserWindow.cxx b/src/GuiQt/BrainBrowserWindow.cxx index 255d56303..f4d5c0a22 100644 --- a/src/GuiQt/BrainBrowserWindow.cxx +++ b/src/GuiQt/BrainBrowserWindow.cxx @@ -224,10 +224,6 @@ m_browserWindowIndex(browserWindowIndex) this); m_showToolBarAction = m_toolbar->toolBarToolButtonAction; addToolBar(m_toolbar); - QObject::connect(this->m_toolbar->tabBar, &WuQTabBar::mousePressedSignal, - this, &BrainBrowserWindow::tabBarMousePressedSlot); - QObject::connect(this->m_toolbar->tabBar, &WuQTabBar::mouseReleasedSignal, - this, &BrainBrowserWindow::tabBarMouseReleasedSlot); createActions(); @@ -481,49 +477,6 @@ BrainBrowserWindow::receiveEvent(Event* event) } } -/** - * Called when tab bar mouse pressed. - */ -void -BrainBrowserWindow::tabBarMousePressedSlot() -{ - if (DeveloperFlagsEnum::isFlag(DeveloperFlagsEnum::DEVELOPER_FLAG_TAB_DRAGGING)) { - /* - * Dragging tabs is slow because as the tab is moved, there are many graphics - * and user-interface updates. So, disable these updates during the time the - * mouse is pressed and released. - * - * Block user-interface updates and also disable graphics drawing. While there is - * an event for graphics updates, the windowing system may directly call the - * drawing code (BrainOpenGLWidget::paintGL()) so we simply disable graphics drawing. - */ - EventManager::get()->blockEvent(EventTypeEnum::EVENT_USER_INTERFACE_UPDATE, true); - BrainOpenGLWidget::setDrawingBlocked(true); - } -} - -/** - * Called when tab bar mouse released. - */ -void -BrainBrowserWindow::tabBarMouseReleasedSlot() -{ - if (DeveloperFlagsEnum::isFlag(DeveloperFlagsEnum::DEVELOPER_FLAG_TAB_DRAGGING)) { - /* - * Enable user-interface updates and graphics drawing. - */ - EventManager::get()->blockEvent(EventTypeEnum::EVENT_USER_INTERFACE_UPDATE, false); - BrainOpenGLWidget::setDrawingBlocked(false); - EventManager::get()->sendEvent(EventUserInterfaceUpdate().setWindowIndex(m_browserWindowIndex).getPointer()); - - /** - * Causes graphics and user-interface to update. - * A box is drawn around selected tab. - */ - m_toolbar->selectedTabChanged(m_toolbar->tabBar->currentIndex()); - } -} - /** * Resize the Dock Widgets (same as QMainWindow::resizeDocks in Qt 5.7 and later) * diff --git a/src/GuiQt/BrainBrowserWindow.h b/src/GuiQt/BrainBrowserWindow.h index 405091a07..fdf5c685a 100644 --- a/src/GuiQt/BrainBrowserWindow.h +++ b/src/GuiQt/BrainBrowserWindow.h @@ -245,10 +245,6 @@ namespace caret { void aspectRatioDialogUpdateForTab(const double aspectRatio); void aspectRatioDialogUpdateForWindow(const double aspectRatio); - void tabBarMousePressedSlot(); - - void tabBarMouseReleasedSlot(); - private: /** Contains status of components such as enter/exit full screen */ struct WindowComponentStatus { diff --git a/src/GuiQt/BrainBrowserWindowToolBar.cxx b/src/GuiQt/BrainBrowserWindowToolBar.cxx index 4f9f1a757..f1a6ecaf2 100644 --- a/src/GuiQt/BrainBrowserWindowToolBar.cxx +++ b/src/GuiQt/BrainBrowserWindowToolBar.cxx @@ -67,6 +67,7 @@ #include "BrainBrowserWindowToolBarTab.h" #include "BrainBrowserWindowToolBarTabPopUpMenu.h" #include "BrainBrowserWindowToolBarVolumeMontage.h" +#include "BrainOpenGLWidget.h" #include "BrainStructure.h" #include "BrowserTabContent.h" #include "BrowserWindowContent.h" @@ -466,6 +467,11 @@ BrainBrowserWindowToolBar::BrainBrowserWindowToolBar(const int32_t browserWindow EventManager::get()->addEventListener(this, EventTypeEnum::EVENT_BROWSER_WINDOW_CREATE_TABS); EventManager::get()->addEventListener(this, EventTypeEnum::EVENT_BROWSER_WINDOW_TILE_TAB_OPERATION); EventManager::get()->addEventListener(this, EventTypeEnum::EVENT_USER_INTERFACE_UPDATE); + + QObject::connect(this->tabBar, &WuQTabBar::mousePressedSignal, + this, &BrainBrowserWindowToolBar::tabBarMousePressedSlot); + QObject::connect(this->tabBar, &WuQTabBar::mouseReleasedSignal, + this, &BrainBrowserWindowToolBar::tabBarMouseReleasedSlot); } /** @@ -1501,6 +1507,60 @@ BrainBrowserWindowToolBar::resetTabIndexForTileTabsHighlighting() EventManager::get()->sendEvent(EventGraphicsUpdateOneWindow(this->browserWindowIndex).getPointer()); } +/** + * Called when tab bar mouse pressed. + */ +void +BrainBrowserWindowToolBar::tabBarMousePressedSlot() +{ + if (DeveloperFlagsEnum::isFlag(DeveloperFlagsEnum::DEVELOPER_FLAG_TAB_DRAGGING)) { + /* + * Dragging tabs is slow because as the tab is moved, there are many graphics + * and user-interface updates. So, disable these updates during the time the + * mouse is pressed and released. + * + * Block user-interface updates and also disable graphics drawing. While there is + * an event for graphics updates, the windowing system may directly call the + * drawing code (BrainOpenGLWidget::paintGL()) so we simply disable graphics drawing. + */ + EventManager::get()->blockEvent(EventTypeEnum::EVENT_USER_INTERFACE_UPDATE, true); + EventManager::get()->blockEvent(EventTypeEnum::EVENT_GRAPHICS_UPDATE_ALL_WINDOWS, true); + + /* + * If drawing is blocked, the selected tab is NOT highlighted and the user + * may have trouble determining the location of the selected tab with + * tile tabs enabled. + */ + const bool blockDrawingFlag(false); + if (blockDrawingFlag) { + BrainOpenGLWidget::setDrawingBlocked(true); + } + } +} + +/** + * Called when tab bar mouse released. + */ +void +BrainBrowserWindowToolBar::tabBarMouseReleasedSlot() +{ + if (DeveloperFlagsEnum::isFlag(DeveloperFlagsEnum::DEVELOPER_FLAG_TAB_DRAGGING)) { + /* + * Enable user-interface updates and graphics drawing. + */ + EventManager::get()->blockEvent(EventTypeEnum::EVENT_USER_INTERFACE_UPDATE, false); + EventManager::get()->blockEvent(EventTypeEnum::EVENT_GRAPHICS_UPDATE_ALL_WINDOWS, false); + BrainOpenGLWidget::setDrawingBlocked(false); + EventManager::get()->sendEvent(EventUserInterfaceUpdate().setWindowIndex(this->browserWindowIndex).getPointer()); + + /** + * Causes graphics and user-interface to update. + * A box is drawn around selected tab. + */ + selectedTabChanged(tabBar->currentIndex()); + } +} + /** * Gets called when user closes a tab by clicking the tab's 'X'. * diff --git a/src/GuiQt/BrainBrowserWindowToolBar.h b/src/GuiQt/BrainBrowserWindowToolBar.h index 76a0b15d0..920e0d1dd 100644 --- a/src/GuiQt/BrainBrowserWindowToolBar.h +++ b/src/GuiQt/BrainBrowserWindowToolBar.h @@ -255,6 +255,8 @@ namespace caret { void tabMoved(int, int); void tabCloseSelected(int); void showTabMenu(const QPoint& pos); + void tabBarMousePressedSlot(); + void tabBarMouseReleasedSlot(); private: enum class InsertTabMode { -- GitLab From df9fd2189d121a84f003df436b8520d8023a20ce Mon Sep 17 00:00:00 2001 From: John Harwell Date: Wed, 3 Oct 2018 08:46:32 -0500 Subject: [PATCH 071/427] Resolve WB-729 Tab Dragging: As a tab is dragged, each time the tab moves one position a "tabMoved()" signal is emitted by the QTabBar. When this signal is received, the graphics are updated to draw a box around the selected tab and the user-interface is updated (overlay toolbox, features toolbox, toolbar, etc). Performing all of these updates causes tab dragging to be slow. While it is necessary to update graphics so that the box is drawn around the tab is displayed, the GUI updates can wait until tab dragging has completed. While there are no "tab dragging" signals, we can extend QTabBar() and override the mousePressEvent() and mouseReleaseEvent() methods. Now, when the mousePressEvent() is called, all GUI updates are blocked. When mouseReleaseEvent() is called, GUI updates are unblocked and the GUI is updated. Note the mousePressEvent() and mouseReleaseEvent() methods are called by Qt's event system. --- src/Common/DeveloperFlagsEnum.cxx | 4 -- src/Common/DeveloperFlagsEnum.h | 3 +- src/GuiQt/BrainBrowserWindowToolBar.cxx | 60 +++++++++---------------- src/GuiQt/BrainOpenGLWidget.cxx | 25 ----------- src/GuiQt/BrainOpenGLWidget.h | 7 --- 5 files changed, 23 insertions(+), 76 deletions(-) diff --git a/src/Common/DeveloperFlagsEnum.cxx b/src/Common/DeveloperFlagsEnum.cxx index 29f545f21..4cae6e410 100644 --- a/src/Common/DeveloperFlagsEnum.cxx +++ b/src/Common/DeveloperFlagsEnum.cxx @@ -117,10 +117,6 @@ DeveloperFlagsEnum::initialize() "DEVELOPER_FLAG_FLIP_PALETTE_NOT_DATA", "Flip Palette Not Data", false)); - enumData.push_back(DeveloperFlagsEnum(DEVELOPER_FLAG_TAB_DRAGGING, - "DEVELOPER_FLAG_TAB_DRAGGING", - "Tab Dragging (faster)", - false)); } /** diff --git a/src/Common/DeveloperFlagsEnum.h b/src/Common/DeveloperFlagsEnum.h index 1b8e1109e..2a352066e 100644 --- a/src/Common/DeveloperFlagsEnum.h +++ b/src/Common/DeveloperFlagsEnum.h @@ -36,8 +36,7 @@ public: */ enum Enum { DEVELOPER_FLAG_UNUSED, - DEVELOPER_FLAG_FLIP_PALETTE_NOT_DATA, - DEVELOPER_FLAG_TAB_DRAGGING + DEVELOPER_FLAG_FLIP_PALETTE_NOT_DATA }; ~DeveloperFlagsEnum(); diff --git a/src/GuiQt/BrainBrowserWindowToolBar.cxx b/src/GuiQt/BrainBrowserWindowToolBar.cxx index f1a6ecaf2..84a9673df 100644 --- a/src/GuiQt/BrainBrowserWindowToolBar.cxx +++ b/src/GuiQt/BrainBrowserWindowToolBar.cxx @@ -1513,29 +1513,15 @@ BrainBrowserWindowToolBar::resetTabIndexForTileTabsHighlighting() void BrainBrowserWindowToolBar::tabBarMousePressedSlot() { - if (DeveloperFlagsEnum::isFlag(DeveloperFlagsEnum::DEVELOPER_FLAG_TAB_DRAGGING)) { - /* - * Dragging tabs is slow because as the tab is moved, there are many graphics - * and user-interface updates. So, disable these updates during the time the - * mouse is pressed and released. - * - * Block user-interface updates and also disable graphics drawing. While there is - * an event for graphics updates, the windowing system may directly call the - * drawing code (BrainOpenGLWidget::paintGL()) so we simply disable graphics drawing. - */ - EventManager::get()->blockEvent(EventTypeEnum::EVENT_USER_INTERFACE_UPDATE, true); - EventManager::get()->blockEvent(EventTypeEnum::EVENT_GRAPHICS_UPDATE_ALL_WINDOWS, true); - - /* - * If drawing is blocked, the selected tab is NOT highlighted and the user - * may have trouble determining the location of the selected tab with - * tile tabs enabled. - */ - const bool blockDrawingFlag(false); - if (blockDrawingFlag) { - BrainOpenGLWidget::setDrawingBlocked(true); - } - } + /* + * Dragging tabs is slow because as the tab is moved, there are many graphics + * and user-interface updates each time the tab changes. So, disable these updates + * during the time the mouse is pressed and until it is released. Note that we + * block the "all windows graphics" update but not the individual window update. + * The individual window graphics update is needed to draw the box around the selected tab. + */ + EventManager::get()->blockEvent(EventTypeEnum::EVENT_USER_INTERFACE_UPDATE, true); + EventManager::get()->blockEvent(EventTypeEnum::EVENT_GRAPHICS_UPDATE_ALL_WINDOWS, true); } /** @@ -1544,21 +1530,19 @@ BrainBrowserWindowToolBar::tabBarMousePressedSlot() void BrainBrowserWindowToolBar::tabBarMouseReleasedSlot() { - if (DeveloperFlagsEnum::isFlag(DeveloperFlagsEnum::DEVELOPER_FLAG_TAB_DRAGGING)) { - /* - * Enable user-interface updates and graphics drawing. - */ - EventManager::get()->blockEvent(EventTypeEnum::EVENT_USER_INTERFACE_UPDATE, false); - EventManager::get()->blockEvent(EventTypeEnum::EVENT_GRAPHICS_UPDATE_ALL_WINDOWS, false); - BrainOpenGLWidget::setDrawingBlocked(false); - EventManager::get()->sendEvent(EventUserInterfaceUpdate().setWindowIndex(this->browserWindowIndex).getPointer()); - - /** - * Causes graphics and user-interface to update. - * A box is drawn around selected tab. - */ - selectedTabChanged(tabBar->currentIndex()); - } + /* + * Enable user-interface updates and graphics drawing since any tab + * dragging has finished. + */ + EventManager::get()->blockEvent(EventTypeEnum::EVENT_USER_INTERFACE_UPDATE, false); + EventManager::get()->blockEvent(EventTypeEnum::EVENT_GRAPHICS_UPDATE_ALL_WINDOWS, false); + EventManager::get()->sendEvent(EventUserInterfaceUpdate().setWindowIndex(this->browserWindowIndex).getPointer()); + + /** + * Causes graphics and user-interface to update. + * A box is drawn around selected tab. + */ + selectedTabChanged(tabBar->currentIndex()); } /** diff --git a/src/GuiQt/BrainOpenGLWidget.cxx b/src/GuiQt/BrainOpenGLWidget.cxx index eb23e52a1..2d783e130 100644 --- a/src/GuiQt/BrainOpenGLWidget.cxx +++ b/src/GuiQt/BrainOpenGLWidget.cxx @@ -603,37 +603,12 @@ BrainOpenGLWidget::getDrawingWindowContent(const int32_t windowViewportIn[4], windowContent.setWindowViewport(windowViewportContent); } -/** - * @return True if drawing is blocked - */ -bool -BrainOpenGLWidget::isDrawingBlocked() -{ - return s_drawingBlockedFlag; -} - -/** - * Set drawing blocked status. - * - * @param blocked - * New blocked status. - */ -void -BrainOpenGLWidget::setDrawingBlocked(const bool blocked) -{ - s_drawingBlockedFlag = blocked; -} - /** * Paints the graphics. */ void BrainOpenGLWidget::paintGL() { - if (isDrawingBlocked()) { - return; - } - #ifdef WORKBENCH_USE_QT5_QOPENGL_WIDGET if (m_contextShareGroupPointer == NULL) { m_contextShareGroupPointer = context()->shareGroup(); diff --git a/src/GuiQt/BrainOpenGLWidget.h b/src/GuiQt/BrainOpenGLWidget.h index fe53fedf4..730a449b5 100644 --- a/src/GuiQt/BrainOpenGLWidget.h +++ b/src/GuiQt/BrainOpenGLWidget.h @@ -116,10 +116,6 @@ namespace caret { QImage performOffScreenImageCapture(const int32_t imageWidth, const int32_t imageHeight); - static bool isDrawingBlocked(); - - static void setDrawingBlocked(const bool blocked); - protected: virtual void initializeGL(); @@ -216,8 +212,6 @@ namespace caret { static std::set s_brainOpenGLWidgets; static BrainOpenGL* s_singletonOpenGL; - - static bool s_drawingBlockedFlag; }; #ifdef __BRAIN_OPENGL_WIDGET_DEFINE__ @@ -225,7 +219,6 @@ namespace caret { bool BrainOpenGLWidget::s_defaultGLFormatInitialized = false; std::set BrainOpenGLWidget::s_brainOpenGLWidgets; BrainOpenGL* BrainOpenGLWidget::s_singletonOpenGL = NULL; - bool BrainOpenGLWidget::s_drawingBlockedFlag = false; #endif // __BRAIN_OPENGL_WIDGET_DEFINE__ } // namespace -- GitLab From 2bbb7b0f94e5a509941c1990bf41610e38c1cf87 Mon Sep 17 00:00:00 2001 From: Tim Coalson Date: Tue, 9 Oct 2018 19:19:24 -0500 Subject: [PATCH 072/427] add convenience option to output strain distortion as log2 --- src/Algorithms/AlgorithmSurfaceDistortion.cxx | 29 ++++++++++++++----- src/Algorithms/AlgorithmSurfaceDistortion.h | 3 +- 2 files changed, 24 insertions(+), 8 deletions(-) diff --git a/src/Algorithms/AlgorithmSurfaceDistortion.cxx b/src/Algorithms/AlgorithmSurfaceDistortion.cxx index 28140294d..6fbc716e2 100644 --- a/src/Algorithms/AlgorithmSurfaceDistortion.cxx +++ b/src/Algorithms/AlgorithmSurfaceDistortion.cxx @@ -61,7 +61,8 @@ OperationParameters* AlgorithmSurfaceDistortion::getParameters() ret->createOptionalParameter(6, "-edge-method", "calculate distortion of edge lengths rather than areas"); - ret->createOptionalParameter(7, "-local-affine-method", "calculate distortion by the local affines between triangles"); + OptionalParameter* strainOpt = ret->createOptionalParameter(7, "-local-affine-method", "calculate distortion by the local affines between triangles"); + strainOpt->createOptionalParameter(1, "-log2", "apply base-2 log transform"); ret->setHelpText( AString("This command, when not using -caret5-method, -edge-method, or -local-affine-method, is equivalent to using -surface-vertex-areas on each surface, ") + @@ -94,14 +95,22 @@ void AlgorithmSurfaceDistortion::useParameters(OperationParameters* myParams, Pr if (caret5method) ++methodCount; bool edgeMethod = myParams->getOptionalParameter(6)->m_present; if (edgeMethod) ++methodCount; - bool strainMethod = myParams->getOptionalParameter(7)->m_present; - if (strainMethod) ++methodCount; + bool strainMethod = false; + bool strainLog2 = false; + OptionalParameter* strainOpt = myParams->getOptionalParameter(7); + if (strainOpt->m_present) + { + strainMethod = true; + ++methodCount; + strainLog2 = strainOpt->getOptionalParameter(1)->m_present; + } if (methodCount > 1) throw AlgorithmException("you may not specify more than one of -caret5-method, -edge-method, or -local-affine-method"); - AlgorithmSurfaceDistortion(myProgObj, referenceSurf, distortedSurf, myMetricOut, smooth, caret5method, edgeMethod, strainMethod); + AlgorithmSurfaceDistortion(myProgObj, referenceSurf, distortedSurf, myMetricOut, smooth, caret5method, edgeMethod, strainMethod, strainLog2); } AlgorithmSurfaceDistortion::AlgorithmSurfaceDistortion(ProgressObject* myProgObj, const SurfaceFile* referenceSurf, const SurfaceFile* distortedSurf, - MetricFile* myMetricOut, const float& smooth, const bool& caret5method, const bool& edgeMethod, const bool& strainMethod) : AbstractAlgorithm(myProgObj) + MetricFile* myMetricOut, const float& smooth, const bool& caret5method, const bool& edgeMethod, + const bool& strainMethod, const bool& strainLog2) : AbstractAlgorithm(myProgObj) { int methodCount = 0; if (caret5method) ++methodCount; @@ -278,8 +287,14 @@ AlgorithmSurfaceDistortion::AlgorithmSurfaceDistortion(ProgressObject* myProgObj accumJ += thisJ;//not sure which areas to weight by, so for now do a straight average, relying on smoothness inherent in the measures accumR += thisR;//could maybe do some fancy interpolation of the affines in 3D sphere space } - myMetricOut->setValue(i, 0, accumJ / myTiles.size()); - myMetricOut->setValue(i, 1, accumR / myTiles.size()); + if (strainLog2) + { + myMetricOut->setValue(i, 0, log2(accumJ / myTiles.size())); + myMetricOut->setValue(i, 1, log2(accumR / myTiles.size())); + } else { + myMetricOut->setValue(i, 0, accumJ / myTiles.size()); + myMetricOut->setValue(i, 1, accumR / myTiles.size()); + } } } else { myMetricOut->setColumnName(0, "area distortion"); diff --git a/src/Algorithms/AlgorithmSurfaceDistortion.h b/src/Algorithms/AlgorithmSurfaceDistortion.h index e8c340a1e..c8e40d137 100644 --- a/src/Algorithms/AlgorithmSurfaceDistortion.h +++ b/src/Algorithms/AlgorithmSurfaceDistortion.h @@ -34,7 +34,8 @@ namespace caret { public: AlgorithmSurfaceDistortion(ProgressObject* myProgObj, const SurfaceFile* referenceSurf, const SurfaceFile* distortedSurf, MetricFile* myMetricOut, const float& smooth = -1.0f, - const bool& caret5method = false, const bool& edgeMethod = false, const bool& strainMethod = false); + const bool& caret5method = false, const bool& edgeMethod = false, + const bool& strainMethod = false, const bool& strainLog2 = false); static OperationParameters* getParameters(); static void useParameters(OperationParameters* myParams, ProgressObject* myProgObj); static AString getCommandSwitch(); -- GitLab From b24e9af4369fa91f627c88db0ea47d2824be23f3 Mon Sep 17 00:00:00 2001 From: John Harwell Date: Thu, 11 Oct 2018 11:12:50 -0500 Subject: [PATCH 073/427] Spec Files and Scene Files were not added to 'previous' files in a couple of instances and this has been fixed. Also they are added before the file is read (user might cancel or permission problem). --- src/Brain/Brain.cxx | 40 ++++++++++++++++++-------------- src/GuiQt/BrainBrowserWindow.cxx | 10 ++++++++ 2 files changed, 32 insertions(+), 18 deletions(-) diff --git a/src/Brain/Brain.cxx b/src/Brain/Brain.cxx index 2f4766406..05ae1bfd6 100644 --- a/src/Brain/Brain.cxx +++ b/src/Brain/Brain.cxx @@ -3346,6 +3346,14 @@ Brain::addReadOrReloadSceneFile(const FileModeAddReadReload fileMode, if (readFlag) { try { try { + /* + * Add to recent scene files + */ + if (FileInformation(filename).exists()) { + CaretPreferences* prefs = SessionManager::get()->getCaretPreferences(); + prefs->addToPreviousSceneFiles(filename); + } + sf->readFile(filename); } catch (const std::bad_alloc&) { @@ -3375,10 +3383,6 @@ Brain::addReadOrReloadSceneFile(const FileModeAddReadReload fileMode, m_sceneFiles.push_back(sf); } - -// CaretPreferences* prefs = SessionManager::get()->getCaretPreferences(); -// prefs->addToPreviousSceneFiles(sf->getFileName()); - return sf; } @@ -5484,6 +5488,20 @@ Brain::loadFilesSelectedInSpecFile(EventSpecFileReadDataFiles* readSpecFileDataF CaretPreferences* prefs = SessionManager::get()->getCaretPreferences(); prefs->setBackgroundAndForegroundColorsMode(BackgroundAndForegroundColorsModeEnum::USER_PREFERENCES); + const AString specFileName = sf->getFileName(); + if (DataFile::isFileOnNetwork(specFileName)) { + CaretPreferences* prefs = SessionManager::get()->getCaretPreferences(); + prefs->addToPreviousSpecFiles(specFileName); + } + else { + FileInformation specFileInfo(specFileName); + if (specFileInfo.exists() + && specFileInfo.isAbsolute()) { + CaretPreferences* prefs = SessionManager::get()->getCaretPreferences(); + prefs->addToPreviousSpecFiles(specFileName); + } + } + try { m_specFile->clear(); *m_specFile = *sf; @@ -5569,20 +5587,6 @@ Brain::loadFilesSelectedInSpecFile(EventSpecFileReadDataFiles* readSpecFileDataF m_specFile->clearModified(); - const AString specFileName = sf->getFileName(); - if (DataFile::isFileOnNetwork(specFileName)) { - CaretPreferences* prefs = SessionManager::get()->getCaretPreferences(); - prefs->addToPreviousSpecFiles(specFileName); - } - else { - FileInformation specFileInfo(specFileName); - if (specFileInfo.exists() - && specFileInfo.isAbsolute()) { - CaretPreferences* prefs = SessionManager::get()->getCaretPreferences(); - prefs->addToPreviousSpecFiles(specFileName); - } - } - if (errorMessage.isEmpty() == false) { readSpecFileDataFilesEvent->setErrorMessage(errorMessage); } diff --git a/src/GuiQt/BrainBrowserWindow.cxx b/src/GuiQt/BrainBrowserWindow.cxx index f4d5c0a22..a9c8c7dcb 100644 --- a/src/GuiQt/BrainBrowserWindow.cxx +++ b/src/GuiQt/BrainBrowserWindow.cxx @@ -2069,6 +2069,8 @@ BrainBrowserWindow::processRecentSpecFileMenuSelection(QAction* itemAction) SpecFile specFile; try { specFile.readFile(specFileName); + SessionManager::get()->getCaretPreferences()->addToPreviousSpecFiles(specFileName); + if (m_recentSpecFileMenu->title() == m_recentSpecFileMenuOpenConfirmTitle) { if (GuiManager::get()->processShowOpenSpecFileDialog(&specFile, @@ -3353,7 +3355,15 @@ BrainBrowserWindow::loadFiles(QWidget* parentForDialogs, if (specFileName.isEmpty() == false) { SpecFile specFile; try { + FileInformation fileInfo(specFileName); + if (fileInfo.isRelative()) { + specFileName = fileInfo.getAbsoluteFilePath(); + } + if (fileInfo.exists()) { + SessionManager::get()->getCaretPreferences()->addToPreviousSpecFiles(specFileName); + } specFile.readFile(specFileName); + } catch (const DataFileException& e) { errorMessages += e.whatString(); -- GitLab From 79c9b54607ec46eed312116bb0ce0e008087802b Mon Sep 17 00:00:00 2001 From: John Harwell Date: Thu, 11 Oct 2018 12:09:42 -0500 Subject: [PATCH 074/427] Implement WB-833 Border drawing default sphere size: When drawing a new border, use the 'Symbol Size' in Features Toolbox -> Borders -> Attributes so that the user can adjust the size of border spheres (points) while a border is being drawn. Features Toolbox -> Borders is now enabled when the window's 'Mode' is 'Border' so that the symbol size is adjustable prior to valid borders existing. In addition, the 'step' value for the symbol size spin box has been changed to 0.1 for finer control over the point size. --- src/Brain/BrainOpenGLFixedPipeline.cxx | 39 +++++++------------ src/GuiQt/BorderSelectionViewController.cxx | 9 ++--- .../BrainBrowserWindowOrientedToolBox.cxx | 29 +++++++++++++- 3 files changed, 45 insertions(+), 32 deletions(-) diff --git a/src/Brain/BrainOpenGLFixedPipeline.cxx b/src/Brain/BrainOpenGLFixedPipeline.cxx index 96e4c681e..a5f15599a 100644 --- a/src/Brain/BrainOpenGLFixedPipeline.cxx +++ b/src/Brain/BrainOpenGLFixedPipeline.cxx @@ -2670,32 +2670,21 @@ BrainOpenGLFixedPipeline::drawBorder(const BorderDrawInfo& borderDrawInfo) const int32_t numBorderPoints = borderDrawInfo.border->getNumberOfPoints(); const bool isHighlightEndPoints = borderDrawInfo.isHighlightEndPoints; - float pointDiameter = 2.0; - float lineWidth = 2.0; - BorderDrawingTypeEnum::Enum drawType = BorderDrawingTypeEnum::DRAW_AS_POINTS_SPHERES; - if (borderDrawInfo.borderFileIndex >= 0) { - const BrainStructure* bs = borderDrawInfo.surface->getBrainStructure(); - const Brain* brain = bs->getBrain(); - const DisplayPropertiesBorders* dpb = brain->getDisplayPropertiesBorders(); - const DisplayGroupEnum::Enum displayGroup = dpb->getDisplayGroupForTab(this->windowTabIndex); - pointDiameter = dpb->getPointSize(displayGroup, - this->windowTabIndex); - lineWidth = dpb->getLineWidth(displayGroup, - this->windowTabIndex); - drawType = dpb->getDrawingType(displayGroup, - this->windowTabIndex); - } - + CaretAssert(m_brain); + const DisplayPropertiesBorders* dpb = m_brain->getDisplayPropertiesBorders(); + const DisplayGroupEnum::Enum displayGroup = dpb->getDisplayGroupForTab(this->windowTabIndex); + const float pointDiameter = dpb->getPointSize(displayGroup, + this->windowTabIndex); + const float lineWidth = dpb->getLineWidth(displayGroup, + this->windowTabIndex); + BorderDrawingTypeEnum::Enum drawType = dpb->getDrawingType(displayGroup, + this->windowTabIndex); + + /* + * When a border is being drawn, always use spheres + */ if (borderDrawInfo.border == this->borderBeingDrawn) { - if (borderDrawInfo.surface != NULL) { - const BoundingBox* bb = borderDrawInfo.surface->getBoundingBox(); - const float maxSize = std::max(bb->getDifferenceX(), - std::max(bb->getDifferenceY(), bb->getDifferenceZ())); - if (maxSize > 0.0f) { - const float percentSize = 0.03f; - pointDiameter = maxSize * percentSize; - } - } + drawType = BorderDrawingTypeEnum::DRAW_AS_POINTS_SPHERES; } bool drawSphericalPoints = false; diff --git a/src/GuiQt/BorderSelectionViewController.cxx b/src/GuiQt/BorderSelectionViewController.cxx index d669b71ee..c656fd1a3 100644 --- a/src/GuiQt/BorderSelectionViewController.cxx +++ b/src/GuiQt/BorderSelectionViewController.cxx @@ -99,7 +99,6 @@ BorderSelectionViewController::BorderSelectionViewController(const int32_t brows m_tabWidget->setCurrentWidget(attributesWidget); QVBoxLayout* layout = new QVBoxLayout(this); - //WuQtUtilities::setLayoutSpacingAndMargins(layout, 2, 2); layout->addWidget(m_bordersDisplayCheckBox); layout->addWidget(WuQtUtilities::createHorizontalLineWidget()); layout->addLayout(groupLayout); @@ -171,17 +170,15 @@ BorderSelectionViewController::createAttributesWidget() QObject::connect(m_standardColorComboBox, SIGNAL(colorSelected(const CaretColorEnum::Enum)), this, SLOT(processAttributesChanges())); - float minLineWidth = 0; + float minLineWidth = 0.1; float maxLineWidth = 1000; - //BrainOpenGL::getMinMaxLineWidth(minLineWidth, - // maxLineWidth); QLabel* lineWidthLabel = new QLabel("Line Diameter"); m_lineWidthSpinBox = WuQFactory::newDoubleSpinBox(); m_lineWidthSpinBox->setFixedWidth(80); m_lineWidthSpinBox->setRange(minLineWidth, maxLineWidth); - m_lineWidthSpinBox->setSingleStep(1.0); + m_lineWidthSpinBox->setSingleStep(0.1); m_lineWidthSpinBox->setDecimals(1); m_lineWidthSpinBox->setSuffix("px"); m_lineWidthSpinBox->setToolTip("Adjust the width of borders drawn as lines.\n" @@ -199,7 +196,7 @@ BorderSelectionViewController::createAttributesWidget() m_pointSizeSpinBox->setFixedWidth(80); m_pointSizeSpinBox->setRange(minLineWidth, maxLineWidth); - m_pointSizeSpinBox->setSingleStep(1.0); + m_pointSizeSpinBox->setSingleStep(0.1); m_pointSizeSpinBox->setDecimals(1); m_pointSizeSpinBox->setToolTip("Adjust the size of borders drawn as points"); m_pointSizeSpinBox->setSuffix("mm"); diff --git a/src/GuiQt/BrainBrowserWindowOrientedToolBox.cxx b/src/GuiQt/BrainBrowserWindowOrientedToolBox.cxx index 9ffe7b335..b6dd00d8b 100644 --- a/src/GuiQt/BrainBrowserWindowOrientedToolBox.cxx +++ b/src/GuiQt/BrainBrowserWindowOrientedToolBox.cxx @@ -45,6 +45,7 @@ #include "CiftiConnectivityMatrixViewController.h" #include "DeveloperFlagsEnum.h" #include "EventBrowserWindowDrawingContent.h" +#include "EventGetOrSetUserInputModeProcessor.h" #include "EventManager.h" #include "EventUserInterfaceUpdate.h" #include "FiberOrientationSelectionViewController.h" @@ -251,7 +252,6 @@ BrainBrowserWindowOrientedToolBox::BrainBrowserWindowOrientedToolBox(const int32 if (orientation == Qt::Horizontal) { setMinimumHeight(200); setMaximumHeight(800); - //setSizeHintHeight(200); } else { if (isOverlayToolBox) { @@ -778,6 +778,33 @@ BrainBrowserWindowOrientedToolBox::receiveEvent(Event* event) } } + EventGetOrSetUserInputModeProcessor inputModeEvent(m_browserWindowIndex); + EventManager::get()->sendEvent(inputModeEvent.getPointer()); + const UserInputModeAbstract::UserInputMode inputMode = inputModeEvent.getUserInputMode(); + switch (inputMode) { + case UserInputModeAbstract::ANNOTATIONS: + break; + case UserInputModeAbstract::BORDERS: + /* + * Enable borders tab if the input mode is 'borders' so that user + * can edit border point size while drawing a border before any + * borders exist. + */ + haveBorders = true; + break; + case UserInputModeAbstract::FOCI: + break; + case UserInputModeAbstract::IMAGE: + break; + case UserInputModeAbstract::INVALID: + break; + case UserInputModeAbstract::VIEW: + break; + case UserInputModeAbstract::VOLUME_EDIT: + break; + } + + /* * Get the selected tab BEFORE enabling/disabling tabs. * Otherwise, the enabling/disabling of tabs may cause the selection -- GitLab From 4b139f7466dc2e5447fe7f522709fa8620d2d685 Mon Sep 17 00:00:00 2001 From: Tim Coalson Date: Fri, 19 Oct 2018 22:13:40 -0500 Subject: [PATCH 075/427] do a remove before writing a new file to improve symlink behavior, improve description of -volume-create arguments --- src/Common/CaretBinaryFile.cxx | 2 ++ src/Files/BorderFile.cxx | 1 + src/Files/VolumeFile.cxx | 32 +++++++++++++++++++----- src/Gifti/GiftiFile.cxx | 8 ++++-- src/Operations/OperationVolumeCreate.cxx | 6 ++--- 5 files changed, 38 insertions(+), 11 deletions(-) diff --git a/src/Common/CaretBinaryFile.cxx b/src/Common/CaretBinaryFile.cxx index 02f5edb47..d043f6023 100644 --- a/src/Common/CaretBinaryFile.cxx +++ b/src/Common/CaretBinaryFile.cxx @@ -174,6 +174,7 @@ void ZFileImpl::open(const QString& filename, const CaretBinaryFile::OpenMode& o mode = "rb"; break; case CaretBinaryFile::WRITE_TRUNCATE: + QFile::remove(filename);//attempt to remove file rather than truncating, to improve behavior with file symlinks mode = "wb";//you have to do "w+b" in order to ask it to not truncate, which zlib doesn't support anyway break; default: @@ -290,6 +291,7 @@ void QFileImpl::open(const QString& filename, const CaretBinaryFile::OpenMode& o if (opmode & CaretBinaryFile::WRITE) mode |= QIODevice::WriteOnly; if (opmode & CaretBinaryFile::TRUNCATE) mode |= QIODevice::Truncate;//expect QFile to recognize silliness like TRUNCATE by itself m_file.setFileName(filename); + if (mode & QIODevice::Truncate) m_file.remove();//attempt to delete the existing file rather than truncating, to improve behavior with file symlinks if (!m_file.open(mode)) { if (!m_file.exists()) diff --git a/src/Files/BorderFile.cxx b/src/Files/BorderFile.cxx index 13c0bb791..f59083792 100644 --- a/src/Files/BorderFile.cxx +++ b/src/Files/BorderFile.cxx @@ -1475,6 +1475,7 @@ void BorderFile::writeFile(const AString& filename, const int& version) checkFileWritability(filename); setFileName(filename); + QFile::remove(filename);//delete it if it exists, to play better with file symlinks switch (version) { diff --git a/src/Files/VolumeFile.cxx b/src/Files/VolumeFile.cxx index ab0654d3b..e0c5fcf91 100644 --- a/src/Files/VolumeFile.cxx +++ b/src/Files/VolumeFile.cxx @@ -406,8 +406,13 @@ float VolumeFile::interpolateValue(const float coordIn1, const float coordIn2, c int64_t ind3high = ind3low + 1; if (!indexValid(ind1low, ind2low, ind3low, brickIndex, component) || !indexValid(ind1high, ind2high, ind3high, brickIndex, component)) { - if (validOut != NULL) *validOut = false; - return INVALID_INTERP_VALUE;//check for valid coord before deconvolving the frame + if (validOut != NULL) *validOut = false;//check for valid coord before deconvolving the frame + if (getType() == SubvolumeAttributes::LABEL) + { + return getMapLabelTable(brickIndex)->getUnassignedLabelKey(); + } else { + return INVALID_INTERP_VALUE; + } } int64_t whichFrame = component * dimensions[3] + brickIndex; validateSpline(brickIndex, component); @@ -427,7 +432,12 @@ float VolumeFile::interpolateValue(const float coordIn1, const float coordIn2, c if (!indexValid(ind1low, ind2low, ind3low, brickIndex, component) || !indexValid(ind1high, ind2high, ind3high, brickIndex, component)) { if (validOut != NULL) *validOut = false; - return INVALID_INTERP_VALUE; + if (getType() == SubvolumeAttributes::LABEL) + { + return getMapLabelTable(brickIndex)->getUnassignedLabelKey(); + } else { + return INVALID_INTERP_VALUE; + } } float xhighWeight = index1 - ind1low; float xlowWeight = 1.0f - xhighWeight; @@ -458,13 +468,23 @@ float VolumeFile::interpolateValue(const float coordIn1, const float coordIn2, c return getValue(index1, index2, index3, brickIndex, component); } else { if (validOut != NULL) *validOut = false; - return INVALID_INTERP_VALUE; + if (getType() == SubvolumeAttributes::LABEL) + { + return getMapLabelTable(brickIndex)->getUnassignedLabelKey(); + } else { + return INVALID_INTERP_VALUE; + } } } break; } - if (validOut != NULL) *validOut = false; - return INVALID_INTERP_VALUE; + if (validOut != NULL) *validOut = false;//this shouldn't be reached unless the enum value is invalid + if (getType() == SubvolumeAttributes::LABEL) + { + return getMapLabelTable(brickIndex)->getUnassignedLabelKey(); + } else { + return INVALID_INTERP_VALUE; + } } void VolumeFile::validateSpline(const int64_t brickIndex, const int64_t component) const diff --git a/src/Gifti/GiftiFile.cxx b/src/Gifti/GiftiFile.cxx index 84a7e8f82..079a8dcde 100644 --- a/src/Gifti/GiftiFile.cxx +++ b/src/Gifti/GiftiFile.cxx @@ -37,6 +37,8 @@ #include "XmlSaxParser.h" +#include + using namespace caret; /** @@ -894,13 +896,15 @@ GiftiFile::writeFile(const AString& filename) try { this->setFileName(filename); - FileInformation fileInfo(filename); + QFile::remove(filename); + + /*FileInformation fileInfo(filename); if (fileInfo.exists()) { //if (GiftiDataArrayFile.isFileOverwriteAllowed() == false) { // throw new GiftiException( // "Overwriting of existing files is currently prohibited"); //} - } + }//*/ // // Create a GIFTI Data Array File Writer diff --git a/src/Operations/OperationVolumeCreate.cxx b/src/Operations/OperationVolumeCreate.cxx index 9e7039950..063316139 100644 --- a/src/Operations/OperationVolumeCreate.cxx +++ b/src/Operations/OperationVolumeCreate.cxx @@ -53,9 +53,9 @@ OperationParameters* OperationVolumeCreate::getParameters() plumbOpt->addDoubleParameter(2, "x-spacing", "change in x-coordinate from incrementing the relevant index"); plumbOpt->addDoubleParameter(3, "y-spacing", "change in y-coordinate from incrementing the relevant index"); plumbOpt->addDoubleParameter(4, "z-spacing", "change in z-coordinate from incrementing the relevant index"); - plumbOpt->addDoubleParameter(5, "x-offset", "the x-coordinate of the first voxel"); - plumbOpt->addDoubleParameter(6, "y-offset", "the y-coordinate of the first voxel"); - plumbOpt->addDoubleParameter(7, "z-offset", "the z-coordinate of the first voxel"); + plumbOpt->addDoubleParameter(5, "x-offset", "the x-coordinate of the center of the first voxel"); + plumbOpt->addDoubleParameter(6, "y-offset", "the y-coordinate of the center of the first voxel"); + plumbOpt->addDoubleParameter(7, "z-offset", "the z-coordinate of the center of the first voxel"); OptionalParameter* sformOpt = ret->createOptionalParameter(6, "-sform", "set via a nifti sform"); char axisNames[] = "xyz", indexNames[] = "ijk"; -- GitLab From 7a6ea6282fd12c840ea3361f53f9ffed1d0ca248 Mon Sep 17 00:00:00 2001 From: John Harwell Date: Wed, 10 Oct 2018 15:34:48 -0500 Subject: [PATCH 076/427] WB-668 Titling columns in tile tabs: Cleanup of TileTabsConfiguration and replace DOM XML parsing with QT's stream reader and stream writer. --- src/Common/TileTabsConfiguration.cxx | 467 ++++++++++++++++----------- src/Common/TileTabsConfiguration.h | 23 +- 2 files changed, 307 insertions(+), 183 deletions(-) diff --git a/src/Common/TileTabsConfiguration.cxx b/src/Common/TileTabsConfiguration.cxx index a88239e22..98aa8ec35 100644 --- a/src/Common/TileTabsConfiguration.cxx +++ b/src/Common/TileTabsConfiguration.cxx @@ -20,12 +20,14 @@ /*LICENSE_END*/ #include +#include #define __TILE_TABS_CONFIGURATION_DECLARE__ #include "TileTabsConfiguration.h" #undef __TILE_TABS_CONFIGURATION_DECLARE__ -#include +#include +#include #include "CaretAssert.h" #include "CaretLogger.h" @@ -535,54 +537,86 @@ TileTabsConfiguration::updateAutomaticConfigurationRowsAndColumns(const int32_t 1.0); } - /** * @return Encoded tile tabs configuration in XML */ AString TileTabsConfiguration::encodeInXML() const { - QDomDocument doc(s_rootTagName); - QDomElement root = doc.createElement(s_rootTagName); - doc.appendChild(root); - - QDomElement versionTag = doc.createElement(s_versionTagName); - versionTag.setAttribute(s_versionNumberAttributeName, - (int)1); - root.appendChild(versionTag); - - QDomElement nameTag = doc.createElement(s_nameTagName); - nameTag.appendChild(doc.createTextNode(m_name)); - root.appendChild(nameTag); - - QDomElement uniqueIdentifierTag = doc.createElement(s_uniqueIdentifierTagName); - uniqueIdentifierTag.appendChild(doc.createTextNode(m_uniqueIdentifier)); - root.appendChild(uniqueIdentifierTag); - - QDomElement rowStretchFactorsTag = doc.createElement(s_rowStretchFactorsTagName); - rowStretchFactorsTag.setAttribute(s_rowStretchFactorsTotalCountAttributeName, - static_cast(m_rowStretchFactors.size())); - rowStretchFactorsTag.setAttribute(s_rowStretchFactorsSelectedCountAttributeName, - static_cast(m_numberOfRows)); - rowStretchFactorsTag.appendChild(doc.createTextNode(AString::fromNumbers(m_rowStretchFactors, - " "))); - root.appendChild(rowStretchFactorsTag); - - QDomElement columnStretchFactorsTag = doc.createElement(s_columnStretchFactorsTagName); - columnStretchFactorsTag.setAttribute(s_columnStretchFactorsTotalCountAttributeName, - static_cast(m_columnStretchFactors.size())); - columnStretchFactorsTag.setAttribute(s_columnStretchFactorsSelectedCountAttributeName, - static_cast(m_numberOfColumns)); - columnStretchFactorsTag.appendChild(doc.createTextNode(AString::fromNumbers(m_columnStretchFactors, - " "))); - root.appendChild(columnStretchFactorsTag); - - const AString xmlString = doc.toString(); + return encodeInXMLWithStreamWriterVersionOne(); +} + +/** + * @return Encoded tile tabs configuration in XML with Stream Writer + */ +AString +TileTabsConfiguration::encodeInXMLWithStreamWriterVersionOne() const +{ + AString xmlString; + QXmlStreamWriter writer(&xmlString); + writer.setAutoFormatting(true); + + writer.writeStartElement(s_rootTagName); + + writer.writeStartElement(s_versionTagName); + writer.writeAttribute(s_versionNumberAttributeName, "1"); + writer.writeEndElement(); + + writer.writeTextElement(s_nameTagName, m_name); + writer.writeTextElement(s_uniqueIdentifierTagName, m_uniqueIdentifier); + + writer.writeStartElement(s_rowStretchFactorsTagName); + writer.writeAttribute(s_rowStretchFactorsSelectedCountAttributeName, AString::number(m_numberOfRows)); + writer.writeAttribute(s_rowStretchFactorsTotalCountAttributeName, AString::number(m_rowStretchFactors.size())); + writer.writeCharacters(AString::fromNumbers(m_rowStretchFactors, " ")); + writer.writeEndElement(); + + writer.writeStartElement(s_columnStretchFactorsTagName); + writer.writeAttribute(s_columnStretchFactorsSelectedCountAttributeName, AString::number(m_numberOfColumns)); + writer.writeAttribute(s_columnStretchFactorsTotalCountAttributeName, AString::number(m_columnStretchFactors.size())); + writer.writeCharacters(AString::fromNumbers(m_columnStretchFactors, " ")); + writer.writeEndElement(); + + writer.writeEndElement(); + return xmlString; } /** - * Decode the tile tabs configuration from XML. + * @return Encoded tile tabs configuration in XML with Stream Writer + */ +AString +TileTabsConfiguration::encodeInXMLWithStreamWriterVersionTwo() const +{ + AString xmlString; + QXmlStreamWriter writer(&xmlString); + writer.setAutoFormatting(true); + + writer.writeStartElement(s_rootTagName); + writer.writeAttribute(s_v2_versionAttributeName, "2"); + + writer.writeTextElement(s_nameTagName, m_name); + writer.writeTextElement(s_uniqueIdentifierTagName, m_uniqueIdentifier); + + writer.writeStartElement(s_rowStretchFactorsTagName); + writer.writeAttribute(s_rowStretchFactorsSelectedCountAttributeName, AString::number(m_numberOfRows)); + writer.writeAttribute(s_rowStretchFactorsTotalCountAttributeName, AString::number(m_rowStretchFactors.size())); + writer.writeCharacters(AString::fromNumbers(m_rowStretchFactors, " ")); + writer.writeEndElement(); + + writer.writeStartElement(s_columnStretchFactorsTagName); + writer.writeAttribute(s_columnStretchFactorsSelectedCountAttributeName, AString::number(m_numberOfColumns)); + writer.writeAttribute(s_columnStretchFactorsTotalCountAttributeName, AString::number(m_columnStretchFactors.size())); + writer.writeCharacters(AString::fromNumbers(m_columnStretchFactors, " ")); + writer.writeEndElement(); + + writer.writeEndElement(); + + return xmlString; +} + +/** + * Decode the tile tabs configuration from XML with stream reader. * * @param xmlString * String containing XML. @@ -590,186 +624,259 @@ TileTabsConfiguration::encodeInXML() const * True if configuration was successfully read from the XML, else false. */ bool -TileTabsConfiguration::decodeFromXML(const AString& xmlString) +TileTabsConfiguration::decodeFromXMLWithStreamReader(const AString& xmlString) { - setNumberOfRows(2); - setNumberOfColumns(2); - - try { - QDomDocument doc(s_rootTagName); - if (doc.setContent(xmlString) == false) { - throw CaretException("Error parsing DomDocument"); - } - - QDomNodeList nodeList = doc.elementsByTagName(s_versionTagName); - if (nodeList.isEmpty()) { - throw CaretException("Error finding version tag"); - } - QDomElement versionElement = nodeList.at(0).toElement(); - if (versionElement.isNull()) { - throw CaretException("Error finding version element"); - } - const AString versionNumberString = versionElement.attribute(s_versionNumberAttributeName, - ""); - if (versionNumberString.isEmpty()) { - throw CaretException("Error finding version number attribute"); - } - - const int versionNumber = versionNumberString.toInt(); - if (versionNumber == 1) { - parseVersionOneXML(doc); + QXmlStreamReader xml(xmlString); + + if (xml.readNextStartElement()) { + const QStringRef tagName(xml.name()); + if (tagName == s_rootTagName) { + QString versionNumberText("Unknown"); + const QXmlStreamAttributes atts = xml.attributes(); + if (atts.hasAttribute(s_v2_versionAttributeName)) { + versionNumberText = atts.value(s_v2_versionAttributeName).toString(); + } + else { + /* + * Version attribute was added to root element by version 2. + * So, if not present, must be version 1. + * Version 1 contained a version XML element. + */ + versionNumberText = "1"; + } + + if (versionNumberText == "1") { + decodeFromXMLWithStreamReaderVersionOne(xml); + } + else if (versionNumberText == "2") { + decodeFromXMLWithStreamReaderVersionTwo(xml); + } + else { + xml.raiseError("TileTabsConfiguration invalid version=" + + versionNumberText); + } } else { - throw CaretException("Invalid version number attribute " - + versionNumberString); + xml.raiseError("TileTabsConfiguration first element is " + + xml.name().toString() + + " but should be " + + s_rootTagName); } } - catch (const CaretException& e) { - CaretLogSevere("Error parsing tile tabs configuration XML:\n" - + e.whatString() - + "\n\n" - + xmlString); - return false; + + if (xml.hasError()) { + throw CaretException("Tile Tabs Configuration Read Error at line number=" + + AString::number(xml.lineNumber()) + + " column number=" + + AString::number(xml.columnNumber()) + + " description=" + + xml.errorString()); } + return true; } /** - * Parse XML for Version One. + * Decode Version One of the tile tabs configuration from XML with stream reader. * - * @param doc - * XML DOM document. + * @param xml + * Stream XML parser. + * @return + * True if configuration was successfully read from the XML, else false. */ -void -TileTabsConfiguration::parseVersionOneXML(QDomDocument& doc) +bool +TileTabsConfiguration::decodeFromXMLWithStreamReaderVersionOne(QXmlStreamReader& xml) { - QDomNodeList nameNodeList = doc.elementsByTagName(s_nameTagName); - if (nameNodeList.isEmpty()) { - throw CaretException("Error finding name tag"); - } - QDomElement nameElement = nameNodeList.at(0).toElement(); - if (nameElement.isNull()) { - throw CaretException("Error finding name element"); - } - m_name = nameElement.text(); - - QDomNodeList uniqueIdNodeList = doc.elementsByTagName(s_uniqueIdentifierTagName); - if (uniqueIdNodeList.isEmpty()) { - CaretLogWarning("Tile Tabs Configuration " - + m_name - + " is missing its unique identifier"); - m_uniqueIdentifier = SystemUtilities::createUniqueID(); - } - else { - QDomElement uniqueIdElement = uniqueIdNodeList.at(0).toElement(); - if (uniqueIdElement.isNull()) { - throw CaretException("Error finding unique identifier element"); + std::set invalidElements; + + AString name; + AString uniqueID; + std::vector rowStretchFactors; + std::vector columnStretchFactors; + int32_t numberOfRows(0); + int32_t numberOfColumns(0); + + QString message; + + while ( ! xml.atEnd()) { + xml.readNext(); + + if (xml.isStartElement()) { + const QStringRef tagName(xml.name()); + + if (tagName == s_versionTagName) { + /* ignore */ + } + else if (tagName == s_nameTagName) { + name = xml.readElementText(); + } + else if (tagName == s_uniqueIdentifierTagName) { + uniqueID = xml.readElementText(); + } + else if (tagName == s_rowStretchFactorsTagName) { + QXmlStreamAttributes atts = xml.attributes(); + if (atts.hasAttribute(s_rowStretchFactorsSelectedCountAttributeName)) { + numberOfRows = atts.value(s_rowStretchFactorsSelectedCountAttributeName).toInt(); + } + + AString::toNumbers(xml.readElementText(), rowStretchFactors); + } + else if (tagName == s_columnStretchFactorsTagName) { + QXmlStreamAttributes atts = xml.attributes(); + if (atts.hasAttribute(s_columnStretchFactorsSelectedCountAttributeName)) { + numberOfColumns = atts.value(s_columnStretchFactorsSelectedCountAttributeName).toInt(); + } + AString::toNumbers(xml.readElementText(), columnStretchFactors); + } + else { + invalidElements.insert(tagName.toString()); + xml.skipCurrentElement(); + } } - m_uniqueIdentifier = uniqueIdElement.text(); } - QDomNodeList rowNodeList = doc.elementsByTagName(s_rowStretchFactorsTagName); - if (rowNodeList.isEmpty()) { - throw CaretException("Error finding row stretch factors tag"); + if (name.isEmpty()) { + message.append(s_nameTagName + + " not found or invalid. "); } - QDomElement rowElement = rowNodeList.at(0).toElement(); - if (rowElement.isNull()) { - throw CaretException("Error finding row element"); + if (uniqueID.isEmpty()) { + message.append(s_uniqueIdentifierTagName + + " not found or invalid. "); } - - const AString numberOfRowsString = rowElement.attribute(s_rowStretchFactorsSelectedCountAttributeName, - ""); - if (numberOfRowsString.isEmpty()) { - throw CaretException("Error finding number of rows attribute"); - } - const int32_t selectedNumberOfRows = numberOfRowsString.toInt(); - if (selectedNumberOfRows <= 0) { - throw CaretException("Invalid number of rows attribute " - + numberOfRowsString); + if (rowStretchFactors.empty()) { + message.append(s_rowStretchFactorsTagName + + " not found or invalid. "); } - - const AString totalNumberOfRowsString = rowElement.attribute(s_rowStretchFactorsTotalCountAttributeName, - ""); - int32_t totalNumberOfRows = 0; - if (totalNumberOfRowsString.isEmpty()) { - CaretLogWarning("Total number of rows attribute is missing."); + if (columnStretchFactors.empty()) { + message.append(s_columnStretchFactorsTagName + + " not found or invalid. "); } - else { - totalNumberOfRows = totalNumberOfRowsString.toInt(); + if (numberOfRows <= 0) { + message.append(s_rowStretchFactorsTagName + + " attribute " + + s_rowStretchFactorsSelectedCountAttributeName + + " is missing or invalid." ); } - - const AString rowStretchFactorsText = rowElement.text(); - std::vector rowStretchFactors; - AString::toNumbers(rowStretchFactorsText, - rowStretchFactors); - if (static_cast(rowStretchFactors.size()) != totalNumberOfRows) { - throw CaretException("Stretch factor number of rows is " - + AString::number(totalNumberOfRows) - + " but have " - + AString::number(static_cast(rowStretchFactors.size())) - + " stretch factors."); + if (numberOfRows <= 0) { + message.append(s_columnStretchFactorsTagName + + " attribute " + + s_columnStretchFactorsSelectedCountAttributeName + + " is missing or invalid." ); } - - QDomNodeList columnNodeList = doc.elementsByTagName(s_columnStretchFactorsTagName); - if (columnNodeList.isEmpty()) { - throw CaretException("Error finding column stretch factors tag"); - } - QDomElement columnElement = columnNodeList.at(0).toElement(); - if (columnElement.isNull()) { - throw CaretException("Error finding column element"); + if ( ! invalidElements.empty()) { + AString msg("Invalid element(s) ignored: "); + for (const auto s : invalidElements) { + msg.append(s + " "); + } + CaretLogWarning(msg); } - const AString numberOfColumnsString = columnElement.attribute(s_columnStretchFactorsSelectedCountAttributeName, - ""); - if (numberOfColumnsString.isEmpty()) { - throw CaretException("Error finding number of columns attribute"); - } - const int32_t selectedNumberOfColumns = numberOfColumnsString.toInt(); - if (selectedNumberOfColumns <= 0) { - throw CaretException("Invalid number of columns attribute " - + numberOfColumnsString); + if (message.isEmpty()) { + m_name = name; + m_uniqueIdentifier = uniqueID; + + setRowStretchFactors(rowStretchFactors, + numberOfRows); + setColumnStretchFactors(columnStretchFactors, + numberOfColumns); + + return true; } + xml.raiseError(message); - const AString totalNumberOfColumnsString = columnElement.attribute(s_columnStretchFactorsTotalCountAttributeName, - ""); - int32_t totalNumberOfColumns = 0; - if (totalNumberOfColumnsString.isEmpty()) { - CaretLogWarning("Total number of columns attribute is missing."); - } - else { - totalNumberOfColumns = totalNumberOfColumnsString.toInt(); - } + return false; +} - const AString columnStretchFactorsText = columnElement.text(); - std::vector columnStretchFactors; - AString::toNumbers(columnStretchFactorsText, - columnStretchFactors); - if (static_cast(columnStretchFactors.size()) != totalNumberOfColumns) { - throw CaretException("Stretch factor number of columns is " - + AString::number(totalNumberOfColumns) - + " but have " - + AString::number(static_cast(columnStretchFactors.size())) - + " stretch factors."); - } +/** + * Set the number of rows. + * + * @param stretchFactors + * The stretch factors. + * @param numberOfRows + * Number of rows for the configuration. + */ +void +TileTabsConfiguration::setRowStretchFactors(const std::vector& stretchFactors, + const int32_t numberOfRows) +{ + setNumberOfRows(numberOfRows); - setNumberOfRows(selectedNumberOfRows); - setNumberOfColumns(selectedNumberOfColumns); - - const int32_t maxRowStretchFactors = std::min(totalNumberOfRows, + const int32_t maxRowStretchFactors = std::min(static_cast(stretchFactors.size()), static_cast(m_rowStretchFactors.size())); for (int32_t i = 0; i < maxRowStretchFactors; i++) { - m_rowStretchFactors[i] = rowStretchFactors[i]; + m_rowStretchFactors[i] = stretchFactors[i]; } - const int32_t maxColumnStretchFactors = std::min(totalNumberOfColumns, +} + +/** + * Set the number of columns. + * + * @param stretchFactors + * The stretch factors. + * @param numberOfColumns + * Number of columns for the configuration. + */ +void +TileTabsConfiguration::setColumnStretchFactors(const std::vector& stretchFactors, + const int32_t numberOfColumns) +{ + setNumberOfColumns(numberOfColumns); + + const int32_t maxColumnStretchFactors = std::min(static_cast(stretchFactors.size()), static_cast(m_columnStretchFactors.size())); for (int32_t i = 0; i < maxColumnStretchFactors; i++) { - m_columnStretchFactors[i] = columnStretchFactors[i]; + m_columnStretchFactors[i] = stretchFactors[i]; } } +/** + * Decode Version Two of the tile tabs configuration from XML with stream reader. + * + * @param xml + * Stream XML parser. + * @return + * True if configuration was successfully read from the XML, else false. + */ +bool +TileTabsConfiguration::decodeFromXMLWithStreamReaderVersionTwo(QXmlStreamReader& /*xml*/) +{ + return false; +} + +/** + * Decode the tile tabs configuration from XML using DOM + * + * @param xmlString + * String containing XML. + * @return + * True if configuration was successfully read from the XML, else false. + */ +bool +TileTabsConfiguration::decodeFromXML(const AString& xmlString) +{ + return decodeFromXMLWithStreamReader(xmlString); +} + +/** + * @return String version of an instance. + */ +AString +TileTabsConfiguration::toString() const +{ + AString s("Name: %1, Unique ID: %2, Number of Rows: %3, Number of Columns: %4, " + "Row Stretch Factors: %5, Columns Stretch Factors: %6"); + + const AString rs(AString::fromNumbers(m_rowStretchFactors, ", ")); + const AString cs(AString::fromNumbers(m_columnStretchFactors, ", ")); + + s = s.arg(m_name).arg(m_uniqueIdentifier).arg(m_numberOfRows).arg(m_numberOfColumns).arg(rs).arg(cs); + + return s; +} + /** * Compare two tile tabs configurations by name. * diff --git a/src/Common/TileTabsConfiguration.h b/src/Common/TileTabsConfiguration.h index 1b26b99b9..afc0c5c02 100644 --- a/src/Common/TileTabsConfiguration.h +++ b/src/Common/TileTabsConfiguration.h @@ -25,8 +25,7 @@ #include "CaretObject.h" #include "TileTabsConfigurationModeEnum.h" -class QDomDocument; - +class QXmlStreamReader; namespace caret { @@ -82,6 +81,8 @@ namespace caret { void updateAutomaticConfigurationRowsAndColumns(const int32_t numberOfTabs); + AString toString() const override; + static bool lessThanComparisonByName(const TileTabsConfiguration* ttc1, const TileTabsConfiguration* ttc2); @@ -102,9 +103,23 @@ namespace caret { private: + void setRowStretchFactors(const std::vector& stretchFactors, + const int32_t numberOfRows); + + void setColumnStretchFactors(const std::vector& stretchFactors, + const int32_t numberOfColumns); + void copyHelperTileTabsConfiguration(const TileTabsConfiguration& obj); - void parseVersionOneXML(QDomDocument& doc); + bool decodeFromXMLWithStreamReader(const AString& xmlString); + + bool decodeFromXMLWithStreamReaderVersionOne(QXmlStreamReader& xml); + + bool decodeFromXMLWithStreamReaderVersionTwo(QXmlStreamReader& xml); + + AString encodeInXMLWithStreamWriterVersionOne() const; + + AString encodeInXMLWithStreamWriterVersionTwo() const; void initialize(); @@ -124,6 +139,7 @@ namespace caret { std::vector m_columnStretchFactors; static const AString s_rootTagName; + static const AString s_v2_versionAttributeName; static const AString s_versionTagName; static const AString s_nameTagName; static const AString s_uniqueIdentifierTagName; @@ -139,6 +155,7 @@ namespace caret { #ifdef __TILE_TABS_CONFIGURATION_DECLARE__ const AString TileTabsConfiguration::s_rootTagName = "TileTabsConfiguration"; + const AString TileTabsConfiguration::s_v2_versionAttributeName = "Version"; const AString TileTabsConfiguration::s_versionTagName = "Version"; const AString TileTabsConfiguration::s_versionNumberAttributeName = "Number"; const AString TileTabsConfiguration::s_nameTagName = "Name"; -- GitLab From fecd050786e3d40dd64b283e48211d2086606264 Mon Sep 17 00:00:00 2001 From: John Harwell Date: Thu, 11 Oct 2018 09:36:56 -0500 Subject: [PATCH 077/427] WB-668 Titling columns in tile tabs: When streaming XML parser was added, it failed with a missing 'unique ID' when reading a tile tabs configuration and threw an exception that was not caught resulting in wb_view terminating. The parser has been updated to allow a missing unique ID and the exception was removed and now an error message is returned if parsing fails. --- src/Brain/BrowserWindowContent.cxx | 28 +++++++++++--- src/Common/CaretPreferences.cxx | 5 ++- src/Common/TileTabsConfiguration.cxx | 58 +++++++++++++++------------- src/Common/TileTabsConfiguration.h | 10 +++-- 4 files changed, 65 insertions(+), 36 deletions(-) diff --git a/src/Brain/BrowserWindowContent.cxx b/src/Brain/BrowserWindowContent.cxx index dd63f1734..314fc9bc0 100644 --- a/src/Brain/BrowserWindowContent.cxx +++ b/src/Brain/BrowserWindowContent.cxx @@ -486,9 +486,13 @@ BrowserWindowContent::restoreFromScene(const SceneAttributes* sceneAttributes, tileTabsConfig = sceneClass->getStringValue("m_tileTabsConfiguration"); } if ( ! tileTabsConfig.isEmpty()) { - const bool valid = m_customTileTabsConfiguration->decodeFromXML(tileTabsConfig); + AString errorMessage; + const bool valid = m_customTileTabsConfiguration->decodeFromXML(tileTabsConfig, + errorMessage); if ( ! valid) { - sceneAttributes->addToErrorMessage("Failed to decode custom tile tabs configuration from BrowserWindowContent: \"" + sceneAttributes->addToErrorMessage("Failed to decode custom tile tabs configuration with error \"" + + errorMessage + + "\" from BrowserWindowContent: \"" + tileTabsConfig + "\""); m_customTileTabsConfiguration.reset(); @@ -508,9 +512,13 @@ BrowserWindowContent::restoreFromScene(const SceneAttributes* sceneAttributes, */ const AString stringTileTabsConfig = sceneClass->getStringValue("m_sceneTileTabsConfiguration"); if ( ! stringTileTabsConfig.isEmpty()) { - const bool valid = m_customTileTabsConfiguration->decodeFromXML(stringTileTabsConfig); + AString errorMessage; + const bool valid = m_customTileTabsConfiguration->decodeFromXML(stringTileTabsConfig, + errorMessage); if ( ! valid) { - sceneAttributes->addToErrorMessage("Failed to decode custom tile tabs configuration from BrowserWindowContent: \"" + sceneAttributes->addToErrorMessage("Failed to decode custom tile tabs configuration with error \"" + + errorMessage + + "\" from BrowserWindowContent: \"" + stringTileTabsConfig + "\""); m_customTileTabsConfiguration.reset(); @@ -594,7 +602,17 @@ BrowserWindowContent::restoreFromOldBrainBrowserWindowScene(const SceneAttribute const AString tileTabsConfigString = browserClass->getStringValue("m_sceneTileTabsConfiguration"); if ( ! tileTabsConfigString.isEmpty()) { - m_customTileTabsConfiguration->decodeFromXML(tileTabsConfigString); + AString errorMessage; + const bool valid = m_customTileTabsConfiguration->decodeFromXML(tileTabsConfigString, + errorMessage); + if ( ! valid) { + sceneAttributes->addToErrorMessage("Failed to decode custom tile tabs configuration with error \"" + + errorMessage + + "\" from OLD BrowserWindowContent: \"" + + tileTabsConfigString + + "\""); + m_customTileTabsConfiguration.reset(); + } } const SceneClass* toolbarClass = browserClass->getClass("m_toolbar"); diff --git a/src/Common/CaretPreferences.cxx b/src/Common/CaretPreferences.cxx index 84cf1e457..f59d9bed5 100644 --- a/src/Common/CaretPreferences.cxx +++ b/src/Common/CaretPreferences.cxx @@ -394,10 +394,13 @@ CaretPreferences::readTileTabsConfigurations(const bool performSync) this->qSettings->setArrayIndex(i); const AString configString = this->qSettings->value(AString::number(i)).toString(); TileTabsConfiguration* ttc = new TileTabsConfiguration(); - if (ttc->decodeFromXML(configString)) { + AString errorMessage; + if (ttc->decodeFromXML(configString, + errorMessage)) { this->tileTabsConfigurations.push_back(ttc); } else { + CaretLogWarning(errorMessage); delete ttc; } } diff --git a/src/Common/TileTabsConfiguration.cxx b/src/Common/TileTabsConfiguration.cxx index 98aa8ec35..46cb63703 100644 --- a/src/Common/TileTabsConfiguration.cxx +++ b/src/Common/TileTabsConfiguration.cxx @@ -620,11 +620,14 @@ TileTabsConfiguration::encodeInXMLWithStreamWriterVersionTwo() const * * @param xmlString * String containing XML. + * @param errorMessageOut + * Will contain error information. * @return - * True if configuration was successfully read from the XML, else false. + * True if decoding is successful, else false. */ bool -TileTabsConfiguration::decodeFromXMLWithStreamReader(const AString& xmlString) +TileTabsConfiguration::decodeFromXMLWithStreamReader(const AString& xmlString, + AString& errorMessageOut) { QXmlStreamReader xml(xmlString); @@ -663,17 +666,20 @@ TileTabsConfiguration::decodeFromXMLWithStreamReader(const AString& xmlString) + s_rootTagName); } } + else { + xml.raiseError("TileTabsConfiguration failed to find start elemnent."); + } if (xml.hasError()) { - throw CaretException("Tile Tabs Configuration Read Error at line number=" - + AString::number(xml.lineNumber()) - + " column number=" - + AString::number(xml.columnNumber()) - + " description=" - + xml.errorString()); + errorMessageOut = ("Tile Tabs Configuration Read Error at line number=" + + AString::number(xml.lineNumber()) + + " column number=" + + AString::number(xml.columnNumber()) + + " description=" + + xml.errorString()); + return false; } - return true; } @@ -682,10 +688,8 @@ TileTabsConfiguration::decodeFromXMLWithStreamReader(const AString& xmlString) * * @param xml * Stream XML parser. - * @return - * True if configuration was successfully read from the XML, else false. */ -bool +void TileTabsConfiguration::decodeFromXMLWithStreamReaderVersionOne(QXmlStreamReader& xml) { std::set invalidElements; @@ -741,8 +745,7 @@ TileTabsConfiguration::decodeFromXMLWithStreamReaderVersionOne(QXmlStreamReader& + " not found or invalid. "); } if (uniqueID.isEmpty()) { - message.append(s_uniqueIdentifierTagName - + " not found or invalid. "); + uniqueID = SystemUtilities::createUniqueID(); } if (rowStretchFactors.empty()) { message.append(s_rowStretchFactorsTagName @@ -766,6 +769,9 @@ TileTabsConfiguration::decodeFromXMLWithStreamReaderVersionOne(QXmlStreamReader& } if ( ! invalidElements.empty()) { + /* + * If invalid elements were encountered, don't throw + */ AString msg("Invalid element(s) ignored: "); for (const auto s : invalidElements) { msg.append(s + " "); @@ -781,13 +787,10 @@ TileTabsConfiguration::decodeFromXMLWithStreamReaderVersionOne(QXmlStreamReader& numberOfRows); setColumnStretchFactors(columnStretchFactors, numberOfColumns); - - return true; } - - xml.raiseError(message); - - return false; + else { + xml.raiseError(message); + } } /** @@ -837,13 +840,10 @@ TileTabsConfiguration::setColumnStretchFactors(const std::vector& stretch * * @param xml * Stream XML parser. - * @return - * True if configuration was successfully read from the XML, else false. */ -bool +void TileTabsConfiguration::decodeFromXMLWithStreamReaderVersionTwo(QXmlStreamReader& /*xml*/) { - return false; } /** @@ -851,13 +851,19 @@ TileTabsConfiguration::decodeFromXMLWithStreamReaderVersionTwo(QXmlStreamReader& * * @param xmlString * String containing XML. + * @param errorMessageOut + * Contains error information if decoding fails. * @return * True if configuration was successfully read from the XML, else false. */ bool -TileTabsConfiguration::decodeFromXML(const AString& xmlString) +TileTabsConfiguration::decodeFromXML(const AString& xmlString, + AString& errorMessageOut) { - return decodeFromXMLWithStreamReader(xmlString); + errorMessageOut.clear(); + + return decodeFromXMLWithStreamReader(xmlString, + errorMessageOut); } /** diff --git a/src/Common/TileTabsConfiguration.h b/src/Common/TileTabsConfiguration.h index afc0c5c02..6ca9a0ba0 100644 --- a/src/Common/TileTabsConfiguration.h +++ b/src/Common/TileTabsConfiguration.h @@ -77,7 +77,8 @@ namespace caret { AString encodeInXML() const; - bool decodeFromXML(const AString& xmlString); + bool decodeFromXML(const AString& xmlString, + AString& errorMessageOut); void updateAutomaticConfigurationRowsAndColumns(const int32_t numberOfTabs); @@ -111,11 +112,12 @@ namespace caret { void copyHelperTileTabsConfiguration(const TileTabsConfiguration& obj); - bool decodeFromXMLWithStreamReader(const AString& xmlString); + bool decodeFromXMLWithStreamReader(const AString& xmlString, + AString& errorMessageOut); - bool decodeFromXMLWithStreamReaderVersionOne(QXmlStreamReader& xml); + void decodeFromXMLWithStreamReaderVersionOne(QXmlStreamReader& xml); - bool decodeFromXMLWithStreamReaderVersionTwo(QXmlStreamReader& xml); + void decodeFromXMLWithStreamReaderVersionTwo(QXmlStreamReader& xml); AString encodeInXMLWithStreamWriterVersionOne() const; -- GitLab From 64e786257c1838ef0e0d3d1e5189500a9f21b06b Mon Sep 17 00:00:00 2001 From: John Harwell Date: Fri, 12 Oct 2018 16:30:11 -0500 Subject: [PATCH 078/427] WB-668 Titling columns in tile tabs: Added version 2 of TileTabsConfiguration. It currently read/writes but has not been implemented. --- src/Brain/BrowserWindowContent.cxx | 24 +- src/Common/CMakeLists.txt | 6 + src/Common/CaretPreferences.cxx | 2 +- src/Common/CaretPreferences.h | 2 + src/Common/TileTabsConfiguration.cxx | 671 ++++++++++++------ src/Common/TileTabsConfiguration.h | 113 +-- .../TileTabsRowColumnContentTypeEnum.cxx | 373 ++++++++++ src/Common/TileTabsRowColumnContentTypeEnum.h | 104 +++ src/Common/TileTabsRowColumnElement.cxx | 206 ++++++ src/Common/TileTabsRowColumnElement.h | 88 +++ .../TileTabsRowColumnStretchTypeEnum.cxx | 373 ++++++++++ src/Common/TileTabsRowColumnStretchTypeEnum.h | 104 +++ src/GuiQt/TileTabsConfigurationDialog.cxx | 33 +- src/GuiQt/TileTabsConfigurationDialog.h | 2 + src/Operations/OperationShowScene.cxx | 6 - 15 files changed, 1793 insertions(+), 314 deletions(-) create mode 100644 src/Common/TileTabsRowColumnContentTypeEnum.cxx create mode 100644 src/Common/TileTabsRowColumnContentTypeEnum.h create mode 100644 src/Common/TileTabsRowColumnElement.cxx create mode 100644 src/Common/TileTabsRowColumnElement.h create mode 100644 src/Common/TileTabsRowColumnStretchTypeEnum.cxx create mode 100644 src/Common/TileTabsRowColumnStretchTypeEnum.h diff --git a/src/Brain/BrowserWindowContent.cxx b/src/Brain/BrowserWindowContent.cxx index 314fc9bc0..3cf7453f6 100644 --- a/src/Brain/BrowserWindowContent.cxx +++ b/src/Brain/BrowserWindowContent.cxx @@ -427,8 +427,15 @@ BrowserWindowContent::saveToScene(const SceneAttributes* sceneAttributes, sceneClass); if (m_tileTabsEnabled) { + sceneClass->addString("m_customTileTabsConfigurationLatest", + m_customTileTabsConfiguration->encodeInXML("")); + + /* + * Add a tile tabs version one so older versions of wb_view + * may still load the scene correctly + */ sceneClass->addString("m_customTileTabsConfiguration", - m_customTileTabsConfiguration->encodeInXML()); + m_customTileTabsConfiguration->encodeVersionInXML(1)); /* * Write the tile tabs configuration a second time using @@ -437,7 +444,7 @@ BrowserWindowContent::saveToScene(const SceneAttributes* sceneAttributes, * tile tabs correctly. */ sceneClass->addString("m_sceneTileTabsConfiguration", - m_customTileTabsConfiguration->encodeInXML()); + m_customTileTabsConfiguration->encodeVersionInXML(1)); } sceneClass->addChild(new SceneIntegerArray("m_sceneTabIndices", m_sceneTabIndices)); @@ -480,7 +487,18 @@ BrowserWindowContent::restoreFromScene(const SceneAttributes* sceneAttributes, sceneTabIndicesArray->integerVectorValues(m_sceneTabIndices); } - AString tileTabsConfig = sceneClass->getStringValue("m_customTileTabsConfiguration"); + /* + * Try restoring newest tile tabs configuration + */ + AString tileTabsConfig = sceneClass->getStringValue("m_customTileTabsConfigurationLatest"); + if ( ! tileTabsConfig.isEmpty()) { + /* Since latest was found, restore, but do not use, older configuration to prevent 'not found' warning */ + sceneClass->getStringValue("m_customTileTabsConfiguration"); + } + if (tileTabsConfig.isEmpty()) { + /* Try version one */ + tileTabsConfig = sceneClass->getStringValue("m_customTileTabsConfiguration"); + } if (tileTabsConfig.isEmpty()) { /* Restore an old name for custom configuration */ tileTabsConfig = sceneClass->getStringValue("m_tileTabsConfiguration"); diff --git a/src/Common/CMakeLists.txt b/src/Common/CMakeLists.txt index b828021da..a51e57ba8 100644 --- a/src/Common/CMakeLists.txt +++ b/src/Common/CMakeLists.txt @@ -147,6 +147,9 @@ StructureEnum.h SystemUtilities.h TileTabsConfiguration.h TileTabsConfigurationModeEnum.h +TileTabsRowColumnContentTypeEnum.h +TileTabsRowColumnElement.h +TileTabsRowColumnStretchTypeEnum.h TracksModificationInterface.h TriStateSelectionStatusEnum.h Vector3D.h @@ -243,6 +246,9 @@ StructureEnum.cxx SystemUtilities.cxx TileTabsConfiguration.cxx TileTabsConfigurationModeEnum.cxx +TileTabsRowColumnContentTypeEnum.cxx +TileTabsRowColumnElement.cxx +TileTabsRowColumnStretchTypeEnum.cxx TriStateSelectionStatusEnum.cxx Vector3D.cxx VectorOperation.cxx diff --git a/src/Common/CaretPreferences.cxx b/src/Common/CaretPreferences.cxx index f59d9bed5..7e72c90f4 100644 --- a/src/Common/CaretPreferences.cxx +++ b/src/Common/CaretPreferences.cxx @@ -362,7 +362,7 @@ CaretPreferences::writeTileTabsConfigurations() for (int32_t i = 0; i < numViews; i++) { this->qSettings->setArrayIndex(i); this->qSettings->setValue(AString::number(i), - this->tileTabsConfigurations[i]->encodeInXML()); + this->tileTabsConfigurations[i]->encodeVersionInXML(1)); } this->qSettings->endArray(); this->qSettings->sync(); diff --git a/src/Common/CaretPreferences.h b/src/Common/CaretPreferences.h index 3abf15d54..ef0ff71b7 100644 --- a/src/Common/CaretPreferences.h +++ b/src/Common/CaretPreferences.h @@ -338,6 +338,7 @@ namespace caret { static const AString NAME_SHOW_SURFACE_IDENTIFICATION_SYMBOLS; static const AString NAME_SHOW_VOLUME_IDENTIFICATION_SYMBOLS; static const AString NAME_TILE_TABS_CONFIGURATIONS; + static const AString NAME_TILE_TABS_CONFIGURATIONS_TWO; static const AString NAME_VOLUME_IDENTIFICATION_DEFAULTED_ON; static const AString NAME_YOKING_DEFAULT_ON; @@ -381,6 +382,7 @@ namespace caret { const AString CaretPreferences::NAME_SHOW_SURFACE_IDENTIFICATION_SYMBOLS = "showSurfaceIdentificationSymbols"; const AString CaretPreferences::NAME_SHOW_VOLUME_IDENTIFICATION_SYMBOLS = "showVolumeIdentificationSymbols"; const AString CaretPreferences::NAME_TILE_TABS_CONFIGURATIONS = "tileTabsConfigurations"; + const AString CaretPreferences::NAME_TILE_TABS_CONFIGURATIONS_TWO = "tileTabsConfigurationsTwo"; const AString CaretPreferences::NAME_VOLUME_IDENTIFICATION_DEFAULTED_ON = "volumeIdentificationDefaultedOn"; const AString CaretPreferences::NAME_YOKING_DEFAULT_ON = "yokingDefaultedOn"; #endif // __CARET_PREFERENCES_DECLARE__ diff --git a/src/Common/TileTabsConfiguration.cxx b/src/Common/TileTabsConfiguration.cxx index 46cb63703..57d4bec9c 100644 --- a/src/Common/TileTabsConfiguration.cxx +++ b/src/Common/TileTabsConfiguration.cxx @@ -70,7 +70,9 @@ TileTabsConfiguration::~TileTabsConfiguration() TileTabsConfiguration::TileTabsConfiguration(const TileTabsConfiguration& obj) : CaretObject(obj) { + const AString savedUniqueID = m_uniqueIdentifier; initialize(); + m_uniqueIdentifier = savedUniqueID; this->copyHelperTileTabsConfiguration(obj); } @@ -116,16 +118,10 @@ TileTabsConfiguration::newCopyWithNewUniqueIdentifier() const void TileTabsConfiguration::initialize() { - m_rowStretchFactors.resize(getMaximumNumberOfRows(), - 1.0); - m_columnStretchFactors.resize(getMaximumNumberOfColumns(), - 1.0); - m_numberOfColumns = 0; - m_numberOfRows = 0; - setNumberOfRows(2); setNumberOfColumns(2); - + + m_name.clear(); m_uniqueIdentifier = SystemUtilities::createUniqueID(); } @@ -141,11 +137,9 @@ TileTabsConfiguration::copyHelperTileTabsConfiguration(const TileTabsConfigurati return; } - m_numberOfColumns = obj.m_numberOfColumns; - m_numberOfRows = obj.m_numberOfRows; - m_rowStretchFactors = obj.m_rowStretchFactors; - m_columnStretchFactors = obj.m_columnStretchFactors; - m_name = obj.m_name; + m_name = obj.m_name; + m_columns = obj.m_columns; + m_rows = obj.m_rows; //DO NOT CHANGE THE UNIQUE IDENTIFIER: m_uniqueIdentifier } @@ -161,6 +155,54 @@ TileTabsConfiguration::copy(const TileTabsConfiguration& rhs) m_name = savedName; } +/** + * Get infoformation for the given elemnent in the columns. + * + * @param Information for element. + */ +TileTabsRowColumnElement* +TileTabsConfiguration::getColumn(const int32_t columnIndex) +{ + CaretAssertVectorIndex(m_columns, columnIndex); + return &m_columns[columnIndex]; +} + +/** + * Get infoformation for the given elemnent in the columns. + * + * @param Information for element. + */ +const TileTabsRowColumnElement* +TileTabsConfiguration::getColumn(const int32_t columnIndex) const +{ + CaretAssertVectorIndex(m_columns, columnIndex); + return &m_columns[columnIndex]; +} + +/** + * Get infoformation for the given elemnent in the rows. + * + * @param Information for element. + */ +TileTabsRowColumnElement* +TileTabsConfiguration::getRow(const int32_t rowIndex) +{ + CaretAssertVectorIndex(m_rows, rowIndex); + return &m_rows[rowIndex]; +} + +/** + * Get infoformation for the given elemnent in the rows. + * + * @param Information for element. + */ +const TileTabsRowColumnElement* +TileTabsConfiguration::getRow(const int32_t rowIndex) const +{ + CaretAssertVectorIndex(m_rows, rowIndex); + return &m_rows[rowIndex]; +} + /** * Get the row heights and column widths for this tile tabs configuration using the * given window width and height. @@ -234,12 +276,27 @@ TileTabsConfiguration::getRowHeightsAndColumnWidthsForWindowSize(const int32_t w */ float rowStretchTotal = 0.0; for (int32_t i = 0; i < numRows; i++) { - rowStretchTotal += getRowStretchFactor(i); + const TileTabsRowColumnElement* e = getRow(i); + switch (e->getStretchType()) { + case TileTabsRowColumnStretchTypeEnum::PERCENTAGE: + break; + case TileTabsRowColumnStretchTypeEnum::WEIGHT: + rowStretchTotal += e->getWeightStretch(); + break; + } } CaretAssert(rowStretchTotal > 0.0); for (int32_t i = 0; i < numRows; i++) { - const int32_t h = static_cast((getRowStretchFactor(i) / rowStretchTotal) - * windowHeight); + int32_t h = 0; + const TileTabsRowColumnElement* e = getRow(i); + switch (e->getStretchType()) { + case TileTabsRowColumnStretchTypeEnum::PERCENTAGE: + break; + case TileTabsRowColumnStretchTypeEnum::WEIGHT: + h = static_cast((e->getWeightStretch() / rowStretchTotal) + * windowHeight); + break; + } rowHeightsOut.push_back(h); } @@ -249,12 +306,27 @@ TileTabsConfiguration::getRowHeightsAndColumnWidthsForWindowSize(const int32_t w */ float columnStretchTotal = 0.0; for (int32_t i = 0; i < numCols; i++) { - columnStretchTotal += getColumnStretchFactor(i); + const TileTabsRowColumnElement* e = getColumn(i); + switch (e->getStretchType()) { + case TileTabsRowColumnStretchTypeEnum::PERCENTAGE: + break; + case TileTabsRowColumnStretchTypeEnum::WEIGHT: + columnStretchTotal += e->getWeightStretch(); + break; + } } CaretAssert(columnStretchTotal > 0.0); for (int32_t i = 0; i < numCols; i++) { - const int32_t w = static_cast((getColumnStretchFactor(i) / columnStretchTotal) - * windowWidth); + int32_t w = 0; + const TileTabsRowColumnElement* e = getColumn(i); + switch (e->getStretchType()) { + case TileTabsRowColumnStretchTypeEnum::PERCENTAGE: + break; + case TileTabsRowColumnStretchTypeEnum::WEIGHT: + w = static_cast((e->getWeightStretch() / columnStretchTotal) + * windowWidth); + break; + } columnWidthsOut.push_back(w); } } break; @@ -343,7 +415,7 @@ TileTabsConfiguration::setName(const AString& name) int32_t TileTabsConfiguration::getNumberOfRows() const { - return m_numberOfRows; + return m_rows.size(); } /** @@ -355,25 +427,7 @@ TileTabsConfiguration::getNumberOfRows() const void TileTabsConfiguration::setNumberOfRows(const int32_t numberOfRows) { - const int32_t oldNumerOfRows = m_numberOfRows; - - CaretAssert(numberOfRows >= 1); - m_numberOfRows = numberOfRows; - if (m_numberOfRows > getMaximumNumberOfRows()) { - CaretLogSevere("Requested number of rows is " - + AString::number(m_numberOfRows) - + " but maximum is " - + getMaximumNumberOfRows()); - m_numberOfRows = getMaximumNumberOfRows(); - } - - /* - * Stretch factors default to 1.0 - */ - for (int32_t iRow = oldNumerOfRows; iRow < m_numberOfRows; iRow++) { - CaretAssertVectorIndex(m_rowStretchFactors, iRow); - m_rowStretchFactors[iRow] = 1.0; - } + m_rows.resize(numberOfRows); } /** @@ -382,7 +436,7 @@ TileTabsConfiguration::setNumberOfRows(const int32_t numberOfRows) int32_t TileTabsConfiguration::getNumberOfColumns() const { - return m_numberOfColumns; + return m_columns.size(); } /** @@ -394,93 +448,7 @@ TileTabsConfiguration::getNumberOfColumns() const void TileTabsConfiguration::setNumberOfColumns(const int32_t numberOfColumns) { - const int32_t oldNumberOfColumns = m_numberOfColumns; - - CaretAssert(numberOfColumns >= 1); - - m_numberOfColumns = numberOfColumns; - if (m_numberOfColumns > getMaximumNumberOfColumns()) { - CaretLogSevere("Requested number of columns is " - + AString::number(m_numberOfColumns) - + " but maximum is " - + getMaximumNumberOfColumns()); - m_numberOfColumns = getMaximumNumberOfColumns(); - } - - /* - * Stretch factors default to 1.0 - */ - for (int32_t iCol = oldNumberOfColumns; iCol < m_numberOfColumns; iCol++) { - CaretAssertVectorIndex(m_columnStretchFactors, iCol); - m_columnStretchFactors[iCol] = 1.0; - } -} - -/** - * Get stretch factor for a column. - * - * @param columnIndex - * Index of the column. - * @return - * Stretch factor for the column. - */ -float -TileTabsConfiguration::getColumnStretchFactor(const int32_t columnIndex) const -{ - CaretAssertVectorIndex(m_columnStretchFactors, columnIndex); - - return m_columnStretchFactors[columnIndex]; -} - -/** - * Set stretch factor for a column. - * - * @param columnIndex - * Index of the column. - * @param stretchFactor - * Stretch factor for the column. - */ -void -TileTabsConfiguration::setColumnStretchFactor(const int32_t columnIndex, - const float stretchFactor) -{ - CaretAssertVectorIndex(m_columnStretchFactors, columnIndex); - - m_columnStretchFactors[columnIndex] = stretchFactor; -} - -/** - * Get stretch factor for a column. - * - * @param columnIndex - * Index of the column. - * @return - * Stretch factor for the column. - */ -float -TileTabsConfiguration::getRowStretchFactor(const int32_t rowIndex) const -{ - CaretAssertVectorIndex(m_rowStretchFactors, rowIndex); - - return m_rowStretchFactors[rowIndex]; -} - -/** - * Set stretch factor for a column. - * - * @param rowIndex - * Index of the row. - * @param stretchFactor - * Stretch factor for the column. - */ -void -TileTabsConfiguration::setRowStretchFactor(const int32_t rowIndex, - const float stretchFactor) -{ - CaretAssertVectorIndex(m_rowStretchFactors, rowIndex); - - m_rowStretchFactors[rowIndex] = stretchFactor; - + m_columns.resize(numberOfColumns); } /** @@ -528,24 +496,42 @@ TileTabsConfiguration::updateAutomaticConfigurationRowsAndColumns(const int32_t setNumberOfRows(numRows); setNumberOfColumns(numCols); - - std::fill(m_columnStretchFactors.begin(), - m_columnStretchFactors.end(), - 1.0); - std::fill(m_rowStretchFactors.begin(), - m_rowStretchFactors.end(), - 1.0); } /** * @return Encoded tile tabs configuration in XML */ AString -TileTabsConfiguration::encodeInXML() const +TileTabsConfiguration::encodeInXML(AString s) const { - return encodeInXMLWithStreamWriterVersionOne(); + return encodeVersionInXML(2); } +/** + * @return Encoded tile tabs configuration in XML + * using the give XML version of TileTabsConfiguration. + */ +AString +TileTabsConfiguration::encodeVersionInXML(const int32_t versionNumber) const +{ + AString s; + + switch (versionNumber) { + case 1: + s = encodeInXMLWithStreamWriterVersionOne(); + break; + case 2: + s = encodeInXMLWithStreamWriterVersionTwo(); + break; + default: + CaretAssertMessage(0, "Requested invalid version=" + AString::number(versionNumber)); + break; + } + + return s; +} + + /** * @return Encoded tile tabs configuration in XML with Stream Writer */ @@ -556,25 +542,35 @@ TileTabsConfiguration::encodeInXMLWithStreamWriterVersionOne() const QXmlStreamWriter writer(&xmlString); writer.setAutoFormatting(true); - writer.writeStartElement(s_rootTagName); + writer.writeStartElement(s_v1_rootTagName); - writer.writeStartElement(s_versionTagName); - writer.writeAttribute(s_versionNumberAttributeName, "1"); + writer.writeStartElement(s_v1_versionTagName); + writer.writeAttribute(s_v1_versionNumberAttributeName, "1"); writer.writeEndElement(); writer.writeTextElement(s_nameTagName, m_name); writer.writeTextElement(s_uniqueIdentifierTagName, m_uniqueIdentifier); - writer.writeStartElement(s_rowStretchFactorsTagName); - writer.writeAttribute(s_rowStretchFactorsSelectedCountAttributeName, AString::number(m_numberOfRows)); - writer.writeAttribute(s_rowStretchFactorsTotalCountAttributeName, AString::number(m_rowStretchFactors.size())); - writer.writeCharacters(AString::fromNumbers(m_rowStretchFactors, " ")); + const int32_t numberOfRows = getNumberOfRows(); + writer.writeStartElement(s_v1_rowStretchFactorsTagName); + writer.writeAttribute(s_v1_rowStretchFactorsSelectedCountAttributeName, AString::number(numberOfRows)); + writer.writeAttribute(s_v1_rowStretchFactorsTotalCountAttributeName, AString::number(numberOfRows)); + std::vector rowStretchFactors; + for (const auto e : m_rows) { + rowStretchFactors.push_back(e.getWeightStretch()); + } + writer.writeCharacters(AString::fromNumbers(rowStretchFactors, " ")); writer.writeEndElement(); - writer.writeStartElement(s_columnStretchFactorsTagName); - writer.writeAttribute(s_columnStretchFactorsSelectedCountAttributeName, AString::number(m_numberOfColumns)); - writer.writeAttribute(s_columnStretchFactorsTotalCountAttributeName, AString::number(m_columnStretchFactors.size())); - writer.writeCharacters(AString::fromNumbers(m_columnStretchFactors, " ")); + const int32_t numberOfColumns = getNumberOfColumns(); + writer.writeStartElement(s_v1_columnStretchFactorsTagName); + writer.writeAttribute(s_v1_columnStretchFactorsSelectedCountAttributeName, AString::number(numberOfColumns)); + writer.writeAttribute(s_v1_columnStretchFactorsTotalCountAttributeName, AString::number(numberOfColumns)); + std::vector columnStretchFactors; + for (const auto e : m_columns) { + columnStretchFactors.push_back(e.getWeightStretch()); + } + writer.writeCharacters(AString::fromNumbers(columnStretchFactors, " ")); writer.writeEndElement(); writer.writeEndElement(); @@ -592,29 +588,49 @@ TileTabsConfiguration::encodeInXMLWithStreamWriterVersionTwo() const QXmlStreamWriter writer(&xmlString); writer.setAutoFormatting(true); - writer.writeStartElement(s_rootTagName); + writer.writeStartElement(s_v2_rootTagName); writer.writeAttribute(s_v2_versionAttributeName, "2"); writer.writeTextElement(s_nameTagName, m_name); writer.writeTextElement(s_uniqueIdentifierTagName, m_uniqueIdentifier); - writer.writeStartElement(s_rowStretchFactorsTagName); - writer.writeAttribute(s_rowStretchFactorsSelectedCountAttributeName, AString::number(m_numberOfRows)); - writer.writeAttribute(s_rowStretchFactorsTotalCountAttributeName, AString::number(m_rowStretchFactors.size())); - writer.writeCharacters(AString::fromNumbers(m_rowStretchFactors, " ")); - writer.writeEndElement(); - - writer.writeStartElement(s_columnStretchFactorsTagName); - writer.writeAttribute(s_columnStretchFactorsSelectedCountAttributeName, AString::number(m_numberOfColumns)); - writer.writeAttribute(s_columnStretchFactorsTotalCountAttributeName, AString::number(m_columnStretchFactors.size())); - writer.writeCharacters(AString::fromNumbers(m_columnStretchFactors, " ")); - writer.writeEndElement(); + encodeRowColumnElement(writer, s_v2_columnsTagName, m_columns); + encodeRowColumnElement(writer, s_v2_rowsTagName, m_rows); writer.writeEndElement(); return xmlString; } +/** + * Encode a vector of elements into xml. + * + * @param writer + * The XML stream writer. + * @param tagName + * Tag name for enclosing the elements. + * @param elements + * Vector of elements added to XML. + */ +void +TileTabsConfiguration::encodeRowColumnElement(QXmlStreamWriter& writer, + const AString tagName, + const std::vector& elements) const +{ + writer.writeStartElement(tagName); + + for (const auto e : elements) { + writer.writeStartElement(s_v2_elementTagName); + writer.writeAttribute(s_v2_contentTypeAttributeName, TileTabsRowColumnContentTypeEnum::toName(e.getContentType())); + writer.writeAttribute(s_v2_stretchTypeAttributeName, TileTabsRowColumnStretchTypeEnum::toName(e.getStretchType())); + writer.writeAttribute(s_v2_percentStretchAttributeName, AString::number(e.getPercentStretch(), 'f', 2)); + writer.writeAttribute(s_v2_weightStretchAttributeName, AString::number(e.getWeightStretch(), 'f', 2)); + writer.writeEndElement(); + } + + writer.writeEndElement(); +} + /** * Decode the tile tabs configuration from XML with stream reader. * @@ -633,29 +649,27 @@ TileTabsConfiguration::decodeFromXMLWithStreamReader(const AString& xmlString, if (xml.readNextStartElement()) { const QStringRef tagName(xml.name()); - if (tagName == s_rootTagName) { + if (tagName == s_v1_rootTagName) { + decodeFromXMLWithStreamReaderVersionOne(xml); + } + else if (tagName == s_v2_rootTagName) { + /* + * Version 2 uses a different root tag than version 1. The reason is that + * the older code for decoding from XML will throw an exception if it + * encounters invalid elements or the version number is invalid. The problem + * is that the exception is not caught and wb_view will terminate. + */ QString versionNumberText("Unknown"); const QXmlStreamAttributes atts = xml.attributes(); if (atts.hasAttribute(s_v2_versionAttributeName)) { versionNumberText = atts.value(s_v2_versionAttributeName).toString(); } - else { - /* - * Version attribute was added to root element by version 2. - * So, if not present, must be version 1. - * Version 1 contained a version XML element. - */ - versionNumberText = "1"; - } - - if (versionNumberText == "1") { - decodeFromXMLWithStreamReaderVersionOne(xml); - } - else if (versionNumberText == "2") { + + if (versionNumberText == "2") { decodeFromXMLWithStreamReaderVersionTwo(xml); } else { - xml.raiseError("TileTabsConfiguration invalid version=" + xml.raiseError("TileTabsConfigurationT invalid version=" + versionNumberText); } } @@ -663,7 +677,9 @@ TileTabsConfiguration::decodeFromXMLWithStreamReader(const AString& xmlString, xml.raiseError("TileTabsConfiguration first element is " + xml.name().toString() + " but should be " - + s_rootTagName); + + s_v1_rootTagName + + " or " + + s_v2_rootTagName); } } else { @@ -680,6 +696,24 @@ TileTabsConfiguration::decodeFromXMLWithStreamReader(const AString& xmlString, return false; } + const bool debugFlag(false); + if (debugFlag) { + AString xmlText = encodeInXMLWithStreamWriterVersionTwo(); + std::cout << std::endl << "NEW: " << xmlText << std::endl << std::endl; + AString em; + TileTabsConfiguration temp; + QXmlStreamReader tempReader(xmlText); + tempReader.readNextStartElement(); + temp.decodeFromXMLWithStreamReaderVersionTwo(tempReader); + if (tempReader.hasError()) { + std::cout << "Decode error: " << tempReader.errorString() << std::endl; + } + else { + std::cout << "Decoded: " << temp.toString() << std::endl; + } + + std::cout << std::endl; + } return true; } @@ -709,7 +743,7 @@ TileTabsConfiguration::decodeFromXMLWithStreamReaderVersionOne(QXmlStreamReader& if (xml.isStartElement()) { const QStringRef tagName(xml.name()); - if (tagName == s_versionTagName) { + if (tagName == s_v1_versionTagName) { /* ignore */ } else if (tagName == s_nameTagName) { @@ -718,18 +752,18 @@ TileTabsConfiguration::decodeFromXMLWithStreamReaderVersionOne(QXmlStreamReader& else if (tagName == s_uniqueIdentifierTagName) { uniqueID = xml.readElementText(); } - else if (tagName == s_rowStretchFactorsTagName) { + else if (tagName == s_v1_rowStretchFactorsTagName) { QXmlStreamAttributes atts = xml.attributes(); - if (atts.hasAttribute(s_rowStretchFactorsSelectedCountAttributeName)) { - numberOfRows = atts.value(s_rowStretchFactorsSelectedCountAttributeName).toInt(); + if (atts.hasAttribute(s_v1_rowStretchFactorsSelectedCountAttributeName)) { + numberOfRows = atts.value(s_v1_rowStretchFactorsSelectedCountAttributeName).toInt(); } AString::toNumbers(xml.readElementText(), rowStretchFactors); } - else if (tagName == s_columnStretchFactorsTagName) { + else if (tagName == s_v1_columnStretchFactorsTagName) { QXmlStreamAttributes atts = xml.attributes(); - if (atts.hasAttribute(s_columnStretchFactorsSelectedCountAttributeName)) { - numberOfColumns = atts.value(s_columnStretchFactorsSelectedCountAttributeName).toInt(); + if (atts.hasAttribute(s_v1_columnStretchFactorsSelectedCountAttributeName)) { + numberOfColumns = atts.value(s_v1_columnStretchFactorsSelectedCountAttributeName).toInt(); } AString::toNumbers(xml.readElementText(), columnStretchFactors); } @@ -748,23 +782,23 @@ TileTabsConfiguration::decodeFromXMLWithStreamReaderVersionOne(QXmlStreamReader& uniqueID = SystemUtilities::createUniqueID(); } if (rowStretchFactors.empty()) { - message.append(s_rowStretchFactorsTagName + message.append(s_v1_rowStretchFactorsTagName + " not found or invalid. "); } if (columnStretchFactors.empty()) { - message.append(s_columnStretchFactorsTagName + message.append(s_v1_columnStretchFactorsTagName + " not found or invalid. "); } if (numberOfRows <= 0) { - message.append(s_rowStretchFactorsTagName + message.append(s_v1_rowStretchFactorsTagName + " attribute " - + s_rowStretchFactorsSelectedCountAttributeName + + s_v1_rowStretchFactorsSelectedCountAttributeName + " is missing or invalid." ); } if (numberOfRows <= 0) { - message.append(s_columnStretchFactorsTagName + message.append(s_v1_columnStretchFactorsTagName + " attribute " - + s_columnStretchFactorsSelectedCountAttributeName + + s_v1_columnStretchFactorsSelectedCountAttributeName + " is missing or invalid." ); } @@ -783,10 +817,28 @@ TileTabsConfiguration::decodeFromXMLWithStreamReaderVersionOne(QXmlStreamReader& m_name = name; m_uniqueIdentifier = uniqueID; - setRowStretchFactors(rowStretchFactors, - numberOfRows); - setColumnStretchFactors(columnStretchFactors, - numberOfColumns); + m_rows.clear(); + m_columns.clear(); + + for (int32_t i = 0; i < numberOfRows; i++) { + TileTabsRowColumnElement element; + CaretAssertVectorIndex(rowStretchFactors, i); + element.setWeightStretch(rowStretchFactors[i]); + element.setContentType(TileTabsRowColumnContentTypeEnum::TAB); + element.setStretchType(TileTabsRowColumnStretchTypeEnum::WEIGHT); + m_rows.push_back(element); + } + CaretAssert(numberOfRows == static_cast(m_rows.size())); + + for (int32_t i = 0; i < numberOfColumns; i++) { + TileTabsRowColumnElement element; + CaretAssertVectorIndex(columnStretchFactors, i); + element.setWeightStretch(columnStretchFactors[i]); + element.setContentType(TileTabsRowColumnContentTypeEnum::TAB); + element.setStretchType(TileTabsRowColumnStretchTypeEnum::WEIGHT); + m_columns.push_back(element); + } + CaretAssert(numberOfColumns == static_cast(m_columns.size())); } else { xml.raiseError(message); @@ -794,56 +846,201 @@ TileTabsConfiguration::decodeFromXMLWithStreamReaderVersionOne(QXmlStreamReader& } /** - * Set the number of rows. + * Decode Version Two of the tile tabs configuration from XML with stream reader. * - * @param stretchFactors - * The stretch factors. - * @param numberOfRows - * Number of rows for the configuration. + * @param xml + * Stream XML parser. */ void -TileTabsConfiguration::setRowStretchFactors(const std::vector& stretchFactors, - const int32_t numberOfRows) +TileTabsConfiguration::decodeFromXMLWithStreamReaderVersionTwo(QXmlStreamReader& xml) { - setNumberOfRows(numberOfRows); + m_rows.clear(); + m_columns.clear(); + m_uniqueIdentifier.clear(); - const int32_t maxRowStretchFactors = std::min(static_cast(stretchFactors.size()), - static_cast(m_rowStretchFactors.size())); - for (int32_t i = 0; i < maxRowStretchFactors; i++) { - m_rowStretchFactors[i] = stretchFactors[i]; - } -} + std::set invalidElements; + + AString name; + AString uniqueID; + + QString message; -/** - * Set the number of columns. - * - * @param stretchFactors - * The stretch factors. - * @param numberOfColumns - * Number of columns for the configuration. - */ -void -TileTabsConfiguration::setColumnStretchFactors(const std::vector& stretchFactors, - const int32_t numberOfColumns) -{ - setNumberOfColumns(numberOfColumns); + enum class ReadMode { + OTHER, + COLUMNS, + ROWS + }; + ReadMode readMode = ReadMode::OTHER; + + while ( ! xml.atEnd()) { + xml.readNext(); + + if (xml.isStartElement()) { + const QStringRef tagName(xml.name()); + + if (tagName == s_nameTagName) { + name = xml.readElementText(); + } + else if (tagName == s_uniqueIdentifierTagName) { + uniqueID = xml.readElementText(); + } + else if (tagName == s_v2_columnsTagName) { + readMode = ReadMode::COLUMNS; + } + else if (tagName == s_v2_rowsTagName) { + readMode = ReadMode::ROWS; + } + else if (tagName == s_v2_elementTagName) { + switch (readMode) { + case ReadMode::OTHER: + CaretAssert(0); + break; + case ReadMode::COLUMNS: + { + AString errorMessage; + TileTabsRowColumnElement e; + if (decodeRowColumnElement(xml, e, errorMessage)) { + m_columns.push_back(e); + } + else { + message.append(errorMessage); + } + } + break; + case ReadMode::ROWS: + { + AString errorMessage; + TileTabsRowColumnElement e; + if (decodeRowColumnElement(xml, e, errorMessage)) { + m_rows.push_back(e); + } + else { + message.append(errorMessage); + } + } + break; + } + } + else { + invalidElements.insert(tagName.toString()); + xml.skipCurrentElement(); + } + } + else if (xml.isEndElement()) { + const QStringRef tagName(xml.name()); + + if (tagName == s_v2_columnsTagName) { + readMode = ReadMode::OTHER; + } + else if (tagName == s_v2_rowsTagName) { + readMode = ReadMode::OTHER; + } + } + } + + if (name.isEmpty()) { + message.append(s_nameTagName + + " not found or invalid. "); + } + if (uniqueID.isEmpty()) { + uniqueID = SystemUtilities::createUniqueID(); + } - const int32_t maxColumnStretchFactors = std::min(static_cast(stretchFactors.size()), - static_cast(m_columnStretchFactors.size())); - for (int32_t i = 0; i < maxColumnStretchFactors; i++) { - m_columnStretchFactors[i] = stretchFactors[i]; + if ( ! invalidElements.empty()) { + /* + * If invalid elements were encountered, don't throw + */ + AString msg("Invalid element(s) ignored: "); + for (const auto s : invalidElements) { + msg.append(s + " "); + } + CaretLogWarning(msg); + } + + if (message.isEmpty()) { + m_name = name; + m_uniqueIdentifier = uniqueID; + } + else { + xml.raiseError(message); } } /** - * Decode Version Two of the tile tabs configuration from XML with stream reader. + * Decode elements in a row or column. * - * @param xml - * Stream XML parser. + * @param reader + * The XML stream reader. + * @param element + * row/column element that is read + * @param errorMessageOut + * Contains error information. + * @return + * True if read successfully, else false. */ -void -TileTabsConfiguration::decodeFromXMLWithStreamReaderVersionTwo(QXmlStreamReader& /*xml*/) +bool +TileTabsConfiguration::decodeRowColumnElement(QXmlStreamReader& reader, + TileTabsRowColumnElement& element, + AString& errorMessageOut) { + const QXmlStreamAttributes atts = reader.attributes(); + + errorMessageOut.clear(); + + if (atts.hasAttribute(s_v2_contentTypeAttributeName)) { + bool validFlag(false); + const AString s = atts.value(s_v2_contentTypeAttributeName).toString(); + element.setContentType(TileTabsRowColumnContentTypeEnum::fromName(s, &validFlag)); + if ( ! validFlag) { + errorMessageOut.append("Content type \"" + s + "\" is not valid. "); + } + } + else { + errorMessageOut.append("Content type is missing. "); + } + + if (atts.hasAttribute(s_v2_stretchTypeAttributeName)) { + bool validFlag(false); + const AString s = atts.value(s_v2_stretchTypeAttributeName).toString(); + element.setStretchType(TileTabsRowColumnStretchTypeEnum::fromName(s, &validFlag)); + if ( ! validFlag) { + errorMessageOut.append("Stretch type \"" + s + "\" is not valid. "); + } + } + else { + errorMessageOut.append("Stretch type is missing. "); + } + + if (atts.hasAttribute(s_v2_percentStretchAttributeName)) { + const float f = atts.value(s_v2_percentStretchAttributeName).toFloat(); + if ((f >= 0.0) && (f < 100.0)) { + element.setPercentStretch(f); + } + else { + errorMessageOut.append("Stretch percentage=" + AString::number(f) + " is invalid."); + } + } + else { + errorMessageOut.append("Stretch percentage is missing. "); + } + + if (atts.hasAttribute(s_v2_weightStretchAttributeName)) { + const float f = atts.value(s_v2_weightStretchAttributeName).toFloat(); + if ((f >= 0.0) && (f < 100.0)) { + element.setWeightStretch(f); + } + else { + errorMessageOut.append("Stretch weight=" + AString::number(f) + " is invalid."); + } + } + else { + errorMessageOut.append("Stretch weight is missing. "); + } + + if (errorMessageOut.isEmpty()) { + return true; + } + return false; } /** @@ -872,13 +1069,19 @@ TileTabsConfiguration::decodeFromXML(const AString& xmlString, AString TileTabsConfiguration::toString() const { - AString s("Name: %1, Unique ID: %2, Number of Rows: %3, Number of Columns: %4, " - "Row Stretch Factors: %5, Columns Stretch Factors: %6"); + AString s("Name: %1, Unique ID: %2\n"); + s = s.arg(m_name).arg(m_uniqueIdentifier); - const AString rs(AString::fromNumbers(m_rowStretchFactors, ", ")); - const AString cs(AString::fromNumbers(m_columnStretchFactors, ", ")); - - s = s.arg(m_name).arg(m_uniqueIdentifier).arg(m_numberOfRows).arg(m_numberOfColumns).arg(rs).arg(cs); + int32_t indx(0); + for (const auto item : m_columns) { + s.append(" Column " + AString::number(indx) + ": " + item.toString() + "\n"); + indx++; + } + indx = 0; + for (const auto item : m_rows) { + s.append(" Row " + AString::number(indx) + ": " + item.toString() + "\n"); + indx++; + } return s; } diff --git a/src/Common/TileTabsConfiguration.h b/src/Common/TileTabsConfiguration.h index 6ca9a0ba0..33c3e5278 100644 --- a/src/Common/TileTabsConfiguration.h +++ b/src/Common/TileTabsConfiguration.h @@ -24,8 +24,10 @@ #include "CaretException.h" #include "CaretObject.h" #include "TileTabsConfigurationModeEnum.h" +#include "TileTabsRowColumnElement.h" class QXmlStreamReader; +class QXmlStreamWriter; namespace caret { @@ -65,17 +67,17 @@ namespace caret { void setNumberOfColumns(const int32_t numberOfColumns); - float getColumnStretchFactor(const int32_t columnIndex) const; - - void setColumnStretchFactor(const int32_t columnIndex, - const float stretchFactor); + TileTabsRowColumnElement* getColumn(const int32_t columnIndex); + + const TileTabsRowColumnElement* getColumn(const int32_t columnIndex) const; + + TileTabsRowColumnElement* getRow(const int32_t rowIndex); - float getRowStretchFactor(const int32_t rowIndex) const; + const TileTabsRowColumnElement* getRow(const int32_t rowIndex) const; - void setRowStretchFactor(const int32_t rowIndex, - const float stretchFactor); + AString encodeInXML(AString s) const; - AString encodeInXML() const; + AString encodeVersionInXML(const int32_t versionNumber) const; bool decodeFromXML(const AString& xmlString, AString& errorMessageOut); @@ -87,16 +89,6 @@ namespace caret { static bool lessThanComparisonByName(const TileTabsConfiguration* ttc1, const TileTabsConfiguration* ttc2); - /** - * @return Maximum number of rows in a tile tabs configuration - */ - static inline int32_t getMaximumNumberOfRows() { return 20; } - - /** - * @return Maximum number of columns in a tile tabs configuration - */ - static inline int32_t getMaximumNumberOfColumns() { return 20; } - static void getRowsAndColumnsForNumberOfTabs(const int32_t numberOfTabs, int32_t& numberOfRowsOut, int32_t& numberOfColumnsOut); @@ -104,12 +96,6 @@ namespace caret { private: - void setRowStretchFactors(const std::vector& stretchFactors, - const int32_t numberOfRows); - - void setColumnStretchFactors(const std::vector& stretchFactors, - const int32_t numberOfColumns); - void copyHelperTileTabsConfiguration(const TileTabsConfiguration& obj); bool decodeFromXMLWithStreamReader(const AString& xmlString, @@ -123,6 +109,14 @@ namespace caret { AString encodeInXMLWithStreamWriterVersionTwo() const; + void encodeRowColumnElement(QXmlStreamWriter& writer, + const AString tagName, + const std::vector& elements) const; + + bool decodeRowColumnElement(QXmlStreamReader& reader, + TileTabsRowColumnElement& element, + AString& errorMessageOut); + void initialize(); // ADD_NEW_MEMBERS_HERE @@ -132,42 +126,59 @@ namespace caret { /** Unique identifier does not get copied */ AString m_uniqueIdentifier; - int32_t m_numberOfRows; - - int32_t m_numberOfColumns; - - std::vector m_rowStretchFactors; - std::vector m_columnStretchFactors; - static const AString s_rootTagName; - static const AString s_v2_versionAttributeName; - static const AString s_versionTagName; + std::vector m_columns; + + std::vector m_rows; + static const AString s_nameTagName; static const AString s_uniqueIdentifierTagName; - static const AString s_versionNumberAttributeName; - static const AString s_columnStretchFactorsTagName; - static const AString s_columnStretchFactorsSelectedCountAttributeName; - static const AString s_columnStretchFactorsTotalCountAttributeName; - static const AString s_rowStretchFactorsTagName; - static const AString s_rowStretchFactorsSelectedCountAttributeName; - static const AString s_rowStretchFactorsTotalCountAttributeName; - + + static const AString s_v1_rootTagName; + static const AString s_v1_versionTagName; + static const AString s_v1_versionNumberAttributeName; + static const AString s_v1_columnStretchFactorsTagName; + static const AString s_v1_columnStretchFactorsSelectedCountAttributeName; + static const AString s_v1_columnStretchFactorsTotalCountAttributeName; + static const AString s_v1_rowStretchFactorsTagName; + static const AString s_v1_rowStretchFactorsSelectedCountAttributeName; + static const AString s_v1_rowStretchFactorsTotalCountAttributeName; + + static const AString s_v2_rootTagName; + static const AString s_v2_versionAttributeName; + static const AString s_v2_columnsTagName; + static const AString s_v2_contentTypeAttributeName; + static const AString s_v2_elementTagName; + static const AString s_v2_percentStretchAttributeName; + static const AString s_v2_rowsTagName; + static const AString s_v2_stretchTypeAttributeName; + static const AString s_v2_weightStretchAttributeName; }; #ifdef __TILE_TABS_CONFIGURATION_DECLARE__ - const AString TileTabsConfiguration::s_rootTagName = "TileTabsConfiguration"; - const AString TileTabsConfiguration::s_v2_versionAttributeName = "Version"; - const AString TileTabsConfiguration::s_versionTagName = "Version"; - const AString TileTabsConfiguration::s_versionNumberAttributeName = "Number"; const AString TileTabsConfiguration::s_nameTagName = "Name"; const AString TileTabsConfiguration::s_uniqueIdentifierTagName = "UniqueIdentifier"; - const AString TileTabsConfiguration::s_columnStretchFactorsTagName = "ColumnStretchFactors"; - const AString TileTabsConfiguration::s_columnStretchFactorsSelectedCountAttributeName = "SelectedRowCount"; - const AString TileTabsConfiguration::s_columnStretchFactorsTotalCountAttributeName = "TotalRowCount"; - const AString TileTabsConfiguration::s_rowStretchFactorsTagName = "RowStretchFactors"; - const AString TileTabsConfiguration::s_rowStretchFactorsSelectedCountAttributeName = "SelectedColumnCount"; - const AString TileTabsConfiguration::s_rowStretchFactorsTotalCountAttributeName = "TotalColumnCount"; + + const AString TileTabsConfiguration::s_v1_rootTagName = "TileTabsConfiguration"; + const AString TileTabsConfiguration::s_v1_versionTagName = "Version"; + const AString TileTabsConfiguration::s_v1_versionNumberAttributeName = "Number"; + const AString TileTabsConfiguration::s_v1_columnStretchFactorsTagName = "ColumnStretchFactors"; + const AString TileTabsConfiguration::s_v1_columnStretchFactorsSelectedCountAttributeName = "SelectedRowCount"; + const AString TileTabsConfiguration::s_v1_columnStretchFactorsTotalCountAttributeName = "TotalRowCount"; + const AString TileTabsConfiguration::s_v1_rowStretchFactorsTagName = "RowStretchFactors"; + const AString TileTabsConfiguration::s_v1_rowStretchFactorsSelectedCountAttributeName = "SelectedColumnCount"; + const AString TileTabsConfiguration::s_v1_rowStretchFactorsTotalCountAttributeName = "TotalColumnCount"; + + const AString TileTabsConfiguration::s_v2_rootTagName = "TileTabsConfigurationTwo"; + const AString TileTabsConfiguration::s_v2_versionAttributeName = "Version"; + const AString TileTabsConfiguration::s_v2_columnsTagName = "Columns"; + const AString TileTabsConfiguration::s_v2_contentTypeAttributeName = "ContentType"; + const AString TileTabsConfiguration::s_v2_elementTagName = "Element"; + const AString TileTabsConfiguration::s_v2_percentStretchAttributeName = "PercentStretch"; + const AString TileTabsConfiguration::s_v2_rowsTagName = "Rows"; + const AString TileTabsConfiguration::s_v2_stretchTypeAttributeName = "StretchType"; + const AString TileTabsConfiguration::s_v2_weightStretchAttributeName = "WeightStretch"; #endif // __TILE_TABS_CONFIGURATION_DECLARE__ } // namespace diff --git a/src/Common/TileTabsRowColumnContentTypeEnum.cxx b/src/Common/TileTabsRowColumnContentTypeEnum.cxx new file mode 100644 index 000000000..61c91a1f7 --- /dev/null +++ b/src/Common/TileTabsRowColumnContentTypeEnum.cxx @@ -0,0 +1,373 @@ + +/*LICENSE_START*/ +/* + * Copyright (C) 2018 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + +#include +#define __TILE_TABS_ROW_COLUMN_CONTENT_TYPE_ENUM_DECLARE__ +#include "TileTabsRowColumnContentTypeEnum.h" +#undef __TILE_TABS_ROW_COLUMN_CONTENT_TYPE_ENUM_DECLARE__ + +#include "CaretAssert.h" + +using namespace caret; + + +/** + * \class caret::TileTabsRowColumnContentTypeEnum + * \brief Content type for a row or column in a tile tabs + * + * Using this enumerated type in the GUI with an EnumComboBoxTemplate + * + * Header File (.h) + * Forward declare the data type: + * class EnumComboBoxTemplate; + * + * Declare the member: + * EnumComboBoxTemplate* m_tileTabsRowColumnContentTypeEnumComboBox; + * + * Declare a slot that is called when user changes selection + * private slots: + * void tileTabsRowColumnContentTypeEnumComboBoxItemActivated(); + * + * Implementation File (.cxx) + * Include the header files + * #include "EnumComboBoxTemplate.h" + * #include "TileTabsRowColumnContentTypeEnum.h" + * + * Instatiate: + * m_tileTabsRowColumnContentTypeEnumComboBox = new EnumComboBoxTemplate(this); + * m_tileTabsRowColumnContentTypeEnumComboBox->setup(); + * + * Get notified when the user changes the selection: + * QObject::connect(m_tileTabsRowColumnContentTypeEnumComboBox, SIGNAL(itemActivated()), + * this, SLOT(tileTabsRowColumnContentTypeEnumComboBoxItemActivated())); + * + * Update the selection: + * m_tileTabsRowColumnContentTypeEnumComboBox->setSelectedItem(NEW_VALUE); + * + * Read the selection: + * const TileTabsRowColumnContentTypeEnum::Enum VARIABLE = m_tileTabsRowColumnContentTypeEnumComboBox->getSelectedItem(); + * + */ + +/** + * Constructor. + * + * @param enumValue + * An enumerated value. + * @param name + * Name of enumerated value. + * + * @param guiName + * User-friendly name for use in user-interface. + */ +TileTabsRowColumnContentTypeEnum::TileTabsRowColumnContentTypeEnum(const Enum enumValue, + const AString& name, + const AString& guiName) +{ + this->enumValue = enumValue; + this->integerCode = integerCodeCounter++; + this->name = name; + this->guiName = guiName; +} + +/** + * Destructor. + */ +TileTabsRowColumnContentTypeEnum::~TileTabsRowColumnContentTypeEnum() +{ +} + +/** + * Initialize the enumerated metadata. + */ +void +TileTabsRowColumnContentTypeEnum::initialize() +{ + if (initializedFlag) { + return; + } + initializedFlag = true; + + enumData.push_back(TileTabsRowColumnContentTypeEnum(SPACER, + "SPACER", + "Spacer")); + + enumData.push_back(TileTabsRowColumnContentTypeEnum(TAB, + "TAB", + "Tab")); + +} + +/** + * Find the data for and enumerated value. + * @param enumValue + * The enumerated value. + * @return Pointer to data for this enumerated type + * or NULL if no data for type or if type is invalid. + */ +const TileTabsRowColumnContentTypeEnum* +TileTabsRowColumnContentTypeEnum::findData(const Enum enumValue) +{ + if (initializedFlag == false) initialize(); + + size_t num = enumData.size(); + for (size_t i = 0; i < num; i++) { + const TileTabsRowColumnContentTypeEnum* d = &enumData[i]; + if (d->enumValue == enumValue) { + return d; + } + } + + return NULL; +} + +/** + * Get a string representation of the enumerated type. + * @param enumValue + * Enumerated value. + * @return + * String representing enumerated value. + */ +AString +TileTabsRowColumnContentTypeEnum::toName(Enum enumValue) { + if (initializedFlag == false) initialize(); + + const TileTabsRowColumnContentTypeEnum* enumInstance = findData(enumValue); + return enumInstance->name; +} + +/** + * Get an enumerated value corresponding to its name. + * @param name + * Name of enumerated value. + * @param isValidOut + * If not NULL, it is set indicating that a + * enum value exists for the input name. + * @return + * Enumerated value. + */ +TileTabsRowColumnContentTypeEnum::Enum +TileTabsRowColumnContentTypeEnum::fromName(const AString& name, bool* isValidOut) +{ + if (initializedFlag == false) initialize(); + + bool validFlag = false; + Enum enumValue = TileTabsRowColumnContentTypeEnum::enumData[0].enumValue; + + for (std::vector::iterator iter = enumData.begin(); + iter != enumData.end(); + iter++) { + const TileTabsRowColumnContentTypeEnum& d = *iter; + if (d.name == name) { + enumValue = d.enumValue; + validFlag = true; + break; + } + } + + if (isValidOut != 0) { + *isValidOut = validFlag; + } + else if (validFlag == false) { + CaretAssertMessage(0, AString("Name " + name + "failed to match enumerated value for type TileTabsRowColumnContentTypeEnum")); + } + return enumValue; +} + +/** + * Get a GUI string representation of the enumerated type. + * @param enumValue + * Enumerated value. + * @return + * String representing enumerated value. + */ +AString +TileTabsRowColumnContentTypeEnum::toGuiName(Enum enumValue) { + if (initializedFlag == false) initialize(); + + const TileTabsRowColumnContentTypeEnum* enumInstance = findData(enumValue); + return enumInstance->guiName; +} + +/** + * Get an enumerated value corresponding to its GUI name. + * @param s + * Name of enumerated value. + * @param isValidOut + * If not NULL, it is set indicating that a + * enum value exists for the input name. + * @return + * Enumerated value. + */ +TileTabsRowColumnContentTypeEnum::Enum +TileTabsRowColumnContentTypeEnum::fromGuiName(const AString& guiName, bool* isValidOut) +{ + if (initializedFlag == false) initialize(); + + bool validFlag = false; + Enum enumValue = TileTabsRowColumnContentTypeEnum::enumData[0].enumValue; + + for (std::vector::iterator iter = enumData.begin(); + iter != enumData.end(); + iter++) { + const TileTabsRowColumnContentTypeEnum& d = *iter; + if (d.guiName == guiName) { + enumValue = d.enumValue; + validFlag = true; + break; + } + } + + if (isValidOut != 0) { + *isValidOut = validFlag; + } + else if (validFlag == false) { + CaretAssertMessage(0, AString("guiName " + guiName + "failed to match enumerated value for type TileTabsRowColumnContentTypeEnum")); + } + return enumValue; +} + +/** + * Get the integer code for a data type. + * + * @return + * Integer code for data type. + */ +int32_t +TileTabsRowColumnContentTypeEnum::toIntegerCode(Enum enumValue) +{ + if (initializedFlag == false) initialize(); + const TileTabsRowColumnContentTypeEnum* enumInstance = findData(enumValue); + return enumInstance->integerCode; +} + +/** + * Find the data type corresponding to an integer code. + * + * @param integerCode + * Integer code for enum. + * @param isValidOut + * If not NULL, on exit isValidOut will indicate if + * integer code is valid. + * @return + * Enum for integer code. + */ +TileTabsRowColumnContentTypeEnum::Enum +TileTabsRowColumnContentTypeEnum::fromIntegerCode(const int32_t integerCode, bool* isValidOut) +{ + if (initializedFlag == false) initialize(); + + bool validFlag = false; + Enum enumValue = TileTabsRowColumnContentTypeEnum::enumData[0].enumValue; + + for (std::vector::iterator iter = enumData.begin(); + iter != enumData.end(); + iter++) { + const TileTabsRowColumnContentTypeEnum& enumInstance = *iter; + if (enumInstance.integerCode == integerCode) { + enumValue = enumInstance.enumValue; + validFlag = true; + break; + } + } + + if (isValidOut != 0) { + *isValidOut = validFlag; + } + else if (validFlag == false) { + CaretAssertMessage(0, AString("Integer code " + AString::number(integerCode) + "failed to match enumerated value for type TileTabsRowColumnContentTypeEnum")); + } + return enumValue; +} + +/** + * Get all of the enumerated type values. The values can be used + * as parameters to toXXX() methods to get associated metadata. + * + * @param allEnums + * A vector that is OUTPUT containing all of the enumerated values. + */ +void +TileTabsRowColumnContentTypeEnum::getAllEnums(std::vector& allEnums) +{ + if (initializedFlag == false) initialize(); + + allEnums.clear(); + + for (std::vector::iterator iter = enumData.begin(); + iter != enumData.end(); + iter++) { + allEnums.push_back(iter->enumValue); + } +} + +/** + * Get all of the names of the enumerated type values. + * + * @param allNames + * A vector that is OUTPUT containing all of the names of the enumerated values. + * @param isSorted + * If true, the names are sorted in alphabetical order. + */ +void +TileTabsRowColumnContentTypeEnum::getAllNames(std::vector& allNames, const bool isSorted) +{ + if (initializedFlag == false) initialize(); + + allNames.clear(); + + for (std::vector::iterator iter = enumData.begin(); + iter != enumData.end(); + iter++) { + allNames.push_back(TileTabsRowColumnContentTypeEnum::toName(iter->enumValue)); + } + + if (isSorted) { + std::sort(allNames.begin(), allNames.end()); + } +} + +/** + * Get all of the GUI names of the enumerated type values. + * + * @param allNames + * A vector that is OUTPUT containing all of the GUI names of the enumerated values. + * @param isSorted + * If true, the names are sorted in alphabetical order. + */ +void +TileTabsRowColumnContentTypeEnum::getAllGuiNames(std::vector& allGuiNames, const bool isSorted) +{ + if (initializedFlag == false) initialize(); + + allGuiNames.clear(); + + for (std::vector::iterator iter = enumData.begin(); + iter != enumData.end(); + iter++) { + allGuiNames.push_back(TileTabsRowColumnContentTypeEnum::toGuiName(iter->enumValue)); + } + + if (isSorted) { + std::sort(allGuiNames.begin(), allGuiNames.end()); + } +} + diff --git a/src/Common/TileTabsRowColumnContentTypeEnum.h b/src/Common/TileTabsRowColumnContentTypeEnum.h new file mode 100644 index 000000000..cd4224ef2 --- /dev/null +++ b/src/Common/TileTabsRowColumnContentTypeEnum.h @@ -0,0 +1,104 @@ +#ifndef __TILE_TABS_ROW_COLUMN_CONTENT_TYPE_ENUM_H__ +#define __TILE_TABS_ROW_COLUMN_CONTENT_TYPE_ENUM_H__ + +/*LICENSE_START*/ +/* + * Copyright (C) 2018 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + + +#include +#include +#include "AString.h" + +namespace caret { + +class TileTabsRowColumnContentTypeEnum { + +public: + /** + * Enumerated values. + */ + enum Enum { + /** Spacer */ + SPACER, + /** Tab */ + TAB + }; + + + ~TileTabsRowColumnContentTypeEnum(); + + static AString toName(Enum enumValue); + + static Enum fromName(const AString& name, bool* isValidOut); + + static AString toGuiName(Enum enumValue); + + static Enum fromGuiName(const AString& guiName, bool* isValidOut); + + static int32_t toIntegerCode(Enum enumValue); + + static Enum fromIntegerCode(const int32_t integerCode, bool* isValidOut); + + static void getAllEnums(std::vector& allEnums); + + static void getAllNames(std::vector& allNames, const bool isSorted); + + static void getAllGuiNames(std::vector& allGuiNames, const bool isSorted); + +private: + TileTabsRowColumnContentTypeEnum(const Enum enumValue, + const AString& name, + const AString& guiName); + + static const TileTabsRowColumnContentTypeEnum* findData(const Enum enumValue); + + /** Holds all instance of enum values and associated metadata */ + static std::vector enumData; + + /** Initialize instances that contain the enum values and metadata */ + static void initialize(); + + /** Indicates instance of enum values and metadata have been initialized */ + static bool initializedFlag; + + /** Auto generated integer codes */ + static int32_t integerCodeCounter; + + /** The enumerated type value for an instance */ + Enum enumValue; + + /** The integer code associated with an enumerated value */ + int32_t integerCode; + + /** The name, a text string that is identical to the enumerated value */ + AString name; + + /** A user-friendly name that is displayed in the GUI */ + AString guiName; +}; + +#ifdef __TILE_TABS_ROW_COLUMN_CONTENT_TYPE_ENUM_DECLARE__ +std::vector TileTabsRowColumnContentTypeEnum::enumData; +bool TileTabsRowColumnContentTypeEnum::initializedFlag = false; +int32_t TileTabsRowColumnContentTypeEnum::integerCodeCounter = 0; +#endif // __TILE_TABS_ROW_COLUMN_CONTENT_TYPE_ENUM_DECLARE__ + +} // namespace +#endif //__TILE_TABS_ROW_COLUMN_CONTENT_TYPE_ENUM_H__ diff --git a/src/Common/TileTabsRowColumnElement.cxx b/src/Common/TileTabsRowColumnElement.cxx new file mode 100644 index 000000000..7a761106a --- /dev/null +++ b/src/Common/TileTabsRowColumnElement.cxx @@ -0,0 +1,206 @@ + +/*LICENSE_START*/ +/* + * Copyright (C) 2018 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + +#define __TILE_TABS_ROW_COLUMN_ELEMENT_DECLARE__ +#include "TileTabsRowColumnElement.h" +#undef __TILE_TABS_ROW_COLUMN_ELEMENT_DECLARE__ + +#include "CaretAssert.h" +using namespace caret; + + + +/** + * \class caret::TileTabsRowColumnElement + * \brief Contents on an element in a Tile Tabs Configuration row or column. + * \ingroup Common + */ + +/** + * Constructor. + */ +TileTabsRowColumnElement::TileTabsRowColumnElement() +: CaretObject() +{ + clear(); +} + +/** + * Destructor. + */ +TileTabsRowColumnElement::~TileTabsRowColumnElement() +{ +} + +/** + * Copy constructor. + * @param obj + * Object that is copied. + */ +TileTabsRowColumnElement::TileTabsRowColumnElement(const TileTabsRowColumnElement& obj) +: CaretObject(obj) +{ + this->copyHelperTileTabsRowColumnElement(obj); +} + +/** + * Assignment operator. + * @param obj + * Data copied from obj to this. + * @return + * Reference to this object. + */ +TileTabsRowColumnElement& +TileTabsRowColumnElement::operator=(const TileTabsRowColumnElement& obj) +{ + if (this != &obj) { + CaretObject::operator=(obj); + this->copyHelperTileTabsRowColumnElement(obj); + } + return *this; +} + +/** + * Helps with copying an object of this type. + * @param obj + * Object that is copied. + */ +void +TileTabsRowColumnElement::copyHelperTileTabsRowColumnElement(const TileTabsRowColumnElement& obj) +{ + m_contentType = obj.m_contentType; + m_stretchType = obj.m_stretchType; + m_percentStretch = obj.m_percentStretch; + m_weightStretch = obj.m_weightStretch; +} + +/** + * Clear this instance. + */ +void +TileTabsRowColumnElement::clear() +{ + m_contentType = TileTabsRowColumnContentTypeEnum::TAB; + m_stretchType = TileTabsRowColumnStretchTypeEnum::WEIGHT; + m_percentStretch = 20.0; + m_weightStretch = 1.0; +} + +/** + * @return Content type (spacer or tab) + */ +TileTabsRowColumnContentTypeEnum::Enum +TileTabsRowColumnElement::getContentType() const +{ + return m_contentType; +} + +/** + * Set the content type (spacer or tab) + * + * @param contentType + * New value for content type. + */ +void +TileTabsRowColumnElement::setContentType(const TileTabsRowColumnContentTypeEnum::Enum contentType) +{ + m_contentType = contentType; +} + +/** + * @return The stretch type (percent or weight) + */ +TileTabsRowColumnStretchTypeEnum::Enum +TileTabsRowColumnElement::getStretchType() const +{ + return m_stretchType; +} + +/** + * Set the stretch type (percent or weight) + * + * @param stretchType + * New value for stretch type. + */ +void +TileTabsRowColumnElement::setStretchType(const TileTabsRowColumnStretchTypeEnum::Enum stretchType) +{ + m_stretchType = stretchType; +} + +/** + * @return The percent stretch value. + */ +float +TileTabsRowColumnElement::getPercentStretch() const +{ + return m_percentStretch; +} + +/** + * Set the percent stretch value. + * + * @param percentStretch + * New value for percent stretch. + */ +void +TileTabsRowColumnElement::setPercentStretch(const float percentStretch) +{ + m_percentStretch = percentStretch; +} + +/** + * @return The weight stretch value. + */ +float +TileTabsRowColumnElement::getWeightStretch() const +{ + return m_weightStretch; +} + +/** + * Set the weight stretch value. + * + * @param weightStretch + * New value for weight stretch. + */ +void +TileTabsRowColumnElement::setWeightStretch(const float weightStretch) +{ + m_weightStretch = weightStretch; +} + + +/** + * Get a description of this object's content. + * @return String describing this object's content. + */ +AString +TileTabsRowColumnElement::toString() const +{ + AString s("TileTabsRowColumnElement: "); + s.append("ContentType=" + TileTabsRowColumnContentTypeEnum::toGuiName(m_contentType)); + s.append(" StretchType=" + TileTabsRowColumnStretchTypeEnum::toGuiName(m_stretchType)); + s.append(" PercentStretch=" + AString::number(m_percentStretch)); + s.append(" WeightStretch=" + AString::number(m_weightStretch)); + return s; +} + diff --git a/src/Common/TileTabsRowColumnElement.h b/src/Common/TileTabsRowColumnElement.h new file mode 100644 index 000000000..9b7158bdb --- /dev/null +++ b/src/Common/TileTabsRowColumnElement.h @@ -0,0 +1,88 @@ +#ifndef __TILE_TABS_ROW_COLUMN_ELEMENT_H__ +#define __TILE_TABS_ROW_COLUMN_ELEMENT_H__ + +/*LICENSE_START*/ +/* + * Copyright (C) 2018 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + + + +#include + +#include "CaretObject.h" + +#include "TileTabsRowColumnContentTypeEnum.h" +#include "TileTabsRowColumnStretchTypeEnum.h" + +namespace caret { + + class TileTabsRowColumnElement : public CaretObject { + + public: + TileTabsRowColumnElement(); + + virtual ~TileTabsRowColumnElement(); + + TileTabsRowColumnElement(const TileTabsRowColumnElement& obj); + + TileTabsRowColumnElement& operator=(const TileTabsRowColumnElement& obj); + + void clear(); + + TileTabsRowColumnContentTypeEnum::Enum getContentType() const; + + void setContentType(const TileTabsRowColumnContentTypeEnum::Enum contentType); + + TileTabsRowColumnStretchTypeEnum::Enum getStretchType() const; + + void setStretchType(const TileTabsRowColumnStretchTypeEnum::Enum stretchType); + + float getPercentStretch() const; + + void setPercentStretch(const float percentStretch); + + float getWeightStretch() const; + + void setWeightStretch(const float weightStretch); + + // ADD_NEW_METHODS_HERE + + virtual AString toString() const; + + private: + void copyHelperTileTabsRowColumnElement(const TileTabsRowColumnElement& obj); + + TileTabsRowColumnContentTypeEnum::Enum m_contentType = TileTabsRowColumnContentTypeEnum::TAB; + + TileTabsRowColumnStretchTypeEnum::Enum m_stretchType = TileTabsRowColumnStretchTypeEnum::WEIGHT; + + float m_percentStretch = 20.0; + + float m_weightStretch = 1.0; + + // ADD_NEW_MEMBERS_HERE + + }; + +#ifdef __TILE_TABS_ROW_COLUMN_ELEMENT_DECLARE__ + // +#endif // __TILE_TABS_ROW_COLUMN_ELEMENT_DECLARE__ + +} // namespace +#endif //__TILE_TABS_ROW_COLUMN_ELEMENT_H__ diff --git a/src/Common/TileTabsRowColumnStretchTypeEnum.cxx b/src/Common/TileTabsRowColumnStretchTypeEnum.cxx new file mode 100644 index 000000000..2a1c53ef4 --- /dev/null +++ b/src/Common/TileTabsRowColumnStretchTypeEnum.cxx @@ -0,0 +1,373 @@ + +/*LICENSE_START*/ +/* + * Copyright (C) 2018 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + +#include +#define __TILE_TABS_ROW_COLUMN_STRETCH_TYPE_ENUM_DECLARE__ +#include "TileTabsRowColumnStretchTypeEnum.h" +#undef __TILE_TABS_ROW_COLUMN_STRETCH_TYPE_ENUM_DECLARE__ + +#include "CaretAssert.h" + +using namespace caret; + + +/** + * \class caret::TileTabsRowColumnStretchTypeEnum + * \brief Stretch type for tile tabs configuration. + * + * Using this enumerated type in the GUI with an EnumComboBoxTemplate + * + * Header File (.h) + * Forward declare the data type: + * class EnumComboBoxTemplate; + * + * Declare the member: + * EnumComboBoxTemplate* m_tileTabsRowColumnStretchTypeEnumComboBox; + * + * Declare a slot that is called when user changes selection + * private slots: + * void tileTabsRowColumnStretchTypeEnumComboBoxItemActivated(); + * + * Implementation File (.cxx) + * Include the header files + * #include "EnumComboBoxTemplate.h" + * #include "TileTabsRowColumnStretchTypeEnum.h" + * + * Instatiate: + * m_tileTabsRowColumnStretchTypeEnumComboBox = new EnumComboBoxTemplate(this); + * m_tileTabsRowColumnStretchTypeEnumComboBox->setup(); + * + * Get notified when the user changes the selection: + * QObject::connect(m_tileTabsRowColumnStretchTypeEnumComboBox, SIGNAL(itemActivated()), + * this, SLOT(tileTabsRowColumnStretchTypeEnumComboBoxItemActivated())); + * + * Update the selection: + * m_tileTabsRowColumnStretchTypeEnumComboBox->setSelectedItem(NEW_VALUE); + * + * Read the selection: + * const TileTabsRowColumnStretchTypeEnum::Enum VARIABLE = m_tileTabsRowColumnStretchTypeEnumComboBox->getSelectedItem(); + * + */ + +/** + * Constructor. + * + * @param enumValue + * An enumerated value. + * @param name + * Name of enumerated value. + * + * @param guiName + * User-friendly name for use in user-interface. + */ +TileTabsRowColumnStretchTypeEnum::TileTabsRowColumnStretchTypeEnum(const Enum enumValue, + const AString& name, + const AString& guiName) +{ + this->enumValue = enumValue; + this->integerCode = integerCodeCounter++; + this->name = name; + this->guiName = guiName; +} + +/** + * Destructor. + */ +TileTabsRowColumnStretchTypeEnum::~TileTabsRowColumnStretchTypeEnum() +{ +} + +/** + * Initialize the enumerated metadata. + */ +void +TileTabsRowColumnStretchTypeEnum::initialize() +{ + if (initializedFlag) { + return; + } + initializedFlag = true; + + enumData.push_back(TileTabsRowColumnStretchTypeEnum(PERCENTAGE, + "PERCENTAGE", + "Percentage")); + + enumData.push_back(TileTabsRowColumnStretchTypeEnum(WEIGHT, + "WEIGHT", + "Weight")); + +} + +/** + * Find the data for and enumerated value. + * @param enumValue + * The enumerated value. + * @return Pointer to data for this enumerated type + * or NULL if no data for type or if type is invalid. + */ +const TileTabsRowColumnStretchTypeEnum* +TileTabsRowColumnStretchTypeEnum::findData(const Enum enumValue) +{ + if (initializedFlag == false) initialize(); + + size_t num = enumData.size(); + for (size_t i = 0; i < num; i++) { + const TileTabsRowColumnStretchTypeEnum* d = &enumData[i]; + if (d->enumValue == enumValue) { + return d; + } + } + + return NULL; +} + +/** + * Get a string representation of the enumerated type. + * @param enumValue + * Enumerated value. + * @return + * String representing enumerated value. + */ +AString +TileTabsRowColumnStretchTypeEnum::toName(Enum enumValue) { + if (initializedFlag == false) initialize(); + + const TileTabsRowColumnStretchTypeEnum* enumInstance = findData(enumValue); + return enumInstance->name; +} + +/** + * Get an enumerated value corresponding to its name. + * @param name + * Name of enumerated value. + * @param isValidOut + * If not NULL, it is set indicating that a + * enum value exists for the input name. + * @return + * Enumerated value. + */ +TileTabsRowColumnStretchTypeEnum::Enum +TileTabsRowColumnStretchTypeEnum::fromName(const AString& name, bool* isValidOut) +{ + if (initializedFlag == false) initialize(); + + bool validFlag = false; + Enum enumValue = TileTabsRowColumnStretchTypeEnum::enumData[0].enumValue; + + for (std::vector::iterator iter = enumData.begin(); + iter != enumData.end(); + iter++) { + const TileTabsRowColumnStretchTypeEnum& d = *iter; + if (d.name == name) { + enumValue = d.enumValue; + validFlag = true; + break; + } + } + + if (isValidOut != 0) { + *isValidOut = validFlag; + } + else if (validFlag == false) { + CaretAssertMessage(0, AString("Name " + name + "failed to match enumerated value for type TileTabsRowColumnStretchTypeEnum")); + } + return enumValue; +} + +/** + * Get a GUI string representation of the enumerated type. + * @param enumValue + * Enumerated value. + * @return + * String representing enumerated value. + */ +AString +TileTabsRowColumnStretchTypeEnum::toGuiName(Enum enumValue) { + if (initializedFlag == false) initialize(); + + const TileTabsRowColumnStretchTypeEnum* enumInstance = findData(enumValue); + return enumInstance->guiName; +} + +/** + * Get an enumerated value corresponding to its GUI name. + * @param s + * Name of enumerated value. + * @param isValidOut + * If not NULL, it is set indicating that a + * enum value exists for the input name. + * @return + * Enumerated value. + */ +TileTabsRowColumnStretchTypeEnum::Enum +TileTabsRowColumnStretchTypeEnum::fromGuiName(const AString& guiName, bool* isValidOut) +{ + if (initializedFlag == false) initialize(); + + bool validFlag = false; + Enum enumValue = TileTabsRowColumnStretchTypeEnum::enumData[0].enumValue; + + for (std::vector::iterator iter = enumData.begin(); + iter != enumData.end(); + iter++) { + const TileTabsRowColumnStretchTypeEnum& d = *iter; + if (d.guiName == guiName) { + enumValue = d.enumValue; + validFlag = true; + break; + } + } + + if (isValidOut != 0) { + *isValidOut = validFlag; + } + else if (validFlag == false) { + CaretAssertMessage(0, AString("guiName " + guiName + "failed to match enumerated value for type TileTabsRowColumnStretchTypeEnum")); + } + return enumValue; +} + +/** + * Get the integer code for a data type. + * + * @return + * Integer code for data type. + */ +int32_t +TileTabsRowColumnStretchTypeEnum::toIntegerCode(Enum enumValue) +{ + if (initializedFlag == false) initialize(); + const TileTabsRowColumnStretchTypeEnum* enumInstance = findData(enumValue); + return enumInstance->integerCode; +} + +/** + * Find the data type corresponding to an integer code. + * + * @param integerCode + * Integer code for enum. + * @param isValidOut + * If not NULL, on exit isValidOut will indicate if + * integer code is valid. + * @return + * Enum for integer code. + */ +TileTabsRowColumnStretchTypeEnum::Enum +TileTabsRowColumnStretchTypeEnum::fromIntegerCode(const int32_t integerCode, bool* isValidOut) +{ + if (initializedFlag == false) initialize(); + + bool validFlag = false; + Enum enumValue = TileTabsRowColumnStretchTypeEnum::enumData[0].enumValue; + + for (std::vector::iterator iter = enumData.begin(); + iter != enumData.end(); + iter++) { + const TileTabsRowColumnStretchTypeEnum& enumInstance = *iter; + if (enumInstance.integerCode == integerCode) { + enumValue = enumInstance.enumValue; + validFlag = true; + break; + } + } + + if (isValidOut != 0) { + *isValidOut = validFlag; + } + else if (validFlag == false) { + CaretAssertMessage(0, AString("Integer code " + AString::number(integerCode) + "failed to match enumerated value for type TileTabsRowColumnStretchTypeEnum")); + } + return enumValue; +} + +/** + * Get all of the enumerated type values. The values can be used + * as parameters to toXXX() methods to get associated metadata. + * + * @param allEnums + * A vector that is OUTPUT containing all of the enumerated values. + */ +void +TileTabsRowColumnStretchTypeEnum::getAllEnums(std::vector& allEnums) +{ + if (initializedFlag == false) initialize(); + + allEnums.clear(); + + for (std::vector::iterator iter = enumData.begin(); + iter != enumData.end(); + iter++) { + allEnums.push_back(iter->enumValue); + } +} + +/** + * Get all of the names of the enumerated type values. + * + * @param allNames + * A vector that is OUTPUT containing all of the names of the enumerated values. + * @param isSorted + * If true, the names are sorted in alphabetical order. + */ +void +TileTabsRowColumnStretchTypeEnum::getAllNames(std::vector& allNames, const bool isSorted) +{ + if (initializedFlag == false) initialize(); + + allNames.clear(); + + for (std::vector::iterator iter = enumData.begin(); + iter != enumData.end(); + iter++) { + allNames.push_back(TileTabsRowColumnStretchTypeEnum::toName(iter->enumValue)); + } + + if (isSorted) { + std::sort(allNames.begin(), allNames.end()); + } +} + +/** + * Get all of the GUI names of the enumerated type values. + * + * @param allNames + * A vector that is OUTPUT containing all of the GUI names of the enumerated values. + * @param isSorted + * If true, the names are sorted in alphabetical order. + */ +void +TileTabsRowColumnStretchTypeEnum::getAllGuiNames(std::vector& allGuiNames, const bool isSorted) +{ + if (initializedFlag == false) initialize(); + + allGuiNames.clear(); + + for (std::vector::iterator iter = enumData.begin(); + iter != enumData.end(); + iter++) { + allGuiNames.push_back(TileTabsRowColumnStretchTypeEnum::toGuiName(iter->enumValue)); + } + + if (isSorted) { + std::sort(allGuiNames.begin(), allGuiNames.end()); + } +} + diff --git a/src/Common/TileTabsRowColumnStretchTypeEnum.h b/src/Common/TileTabsRowColumnStretchTypeEnum.h new file mode 100644 index 000000000..40fdcac57 --- /dev/null +++ b/src/Common/TileTabsRowColumnStretchTypeEnum.h @@ -0,0 +1,104 @@ +#ifndef __TILE_TABS_ROW_COLUMN_STRETCH_TYPE_ENUM_H__ +#define __TILE_TABS_ROW_COLUMN_STRETCH_TYPE_ENUM_H__ + +/*LICENSE_START*/ +/* + * Copyright (C) 2018 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + + +#include +#include +#include "AString.h" + +namespace caret { + +class TileTabsRowColumnStretchTypeEnum { + +public: + /** + * Enumerated values. + */ + enum Enum { + /** Percentage */ + PERCENTAGE, + /** Weight */ + WEIGHT + }; + + + ~TileTabsRowColumnStretchTypeEnum(); + + static AString toName(Enum enumValue); + + static Enum fromName(const AString& name, bool* isValidOut); + + static AString toGuiName(Enum enumValue); + + static Enum fromGuiName(const AString& guiName, bool* isValidOut); + + static int32_t toIntegerCode(Enum enumValue); + + static Enum fromIntegerCode(const int32_t integerCode, bool* isValidOut); + + static void getAllEnums(std::vector& allEnums); + + static void getAllNames(std::vector& allNames, const bool isSorted); + + static void getAllGuiNames(std::vector& allGuiNames, const bool isSorted); + +private: + TileTabsRowColumnStretchTypeEnum(const Enum enumValue, + const AString& name, + const AString& guiName); + + static const TileTabsRowColumnStretchTypeEnum* findData(const Enum enumValue); + + /** Holds all instance of enum values and associated metadata */ + static std::vector enumData; + + /** Initialize instances that contain the enum values and metadata */ + static void initialize(); + + /** Indicates instance of enum values and metadata have been initialized */ + static bool initializedFlag; + + /** Auto generated integer codes */ + static int32_t integerCodeCounter; + + /** The enumerated type value for an instance */ + Enum enumValue; + + /** The integer code associated with an enumerated value */ + int32_t integerCode; + + /** The name, a text string that is identical to the enumerated value */ + AString name; + + /** A user-friendly name that is displayed in the GUI */ + AString guiName; +}; + +#ifdef __TILE_TABS_ROW_COLUMN_STRETCH_TYPE_ENUM_DECLARE__ +std::vector TileTabsRowColumnStretchTypeEnum::enumData; +bool TileTabsRowColumnStretchTypeEnum::initializedFlag = false; +int32_t TileTabsRowColumnStretchTypeEnum::integerCodeCounter = 0; +#endif // __TILE_TABS_ROW_COLUMN_STRETCH_TYPE_ENUM_DECLARE__ + +} // namespace +#endif //__TILE_TABS_ROW_COLUMN_STRETCH_TYPE_ENUM_H__ diff --git a/src/GuiQt/TileTabsConfigurationDialog.cxx b/src/GuiQt/TileTabsConfigurationDialog.cxx index 707f5d534..d2400a1d2 100644 --- a/src/GuiQt/TileTabsConfigurationDialog.cxx +++ b/src/GuiQt/TileTabsConfigurationDialog.cxx @@ -354,9 +354,6 @@ TileTabsConfigurationDialog::createWorkbenchWindowWidget() QWidget* TileTabsConfigurationDialog::createCustomConfigurationWidget() { - const int32_t maximumNumberOfRows = TileTabsConfiguration::getMaximumNumberOfRows(); - const int32_t maximumNumberOfColumns = TileTabsConfiguration::getMaximumNumberOfColumns(); - const AString autoToolTip("Workbench adjusts the number of rows and columns so " "that all tabs are displayed"); m_automaticConfigurationRadioButton = new QRadioButton("Automatic Configuration"); @@ -375,7 +372,7 @@ TileTabsConfigurationDialog::createCustomConfigurationWidget() QLabel* dimensionsLabel = new QLabel("Dimensions"); QLabel* rowsLabel = new QLabel("Rows"); m_numberOfRowsSpinBox = WuQFactory::newSpinBoxWithMinMaxStepSignalInt(1, - maximumNumberOfRows, + s_maximumRowsColumns, 1, this, SLOT(configurationNumberOfRowsOrColumnsChanged())); @@ -383,7 +380,7 @@ TileTabsConfigurationDialog::createCustomConfigurationWidget() QLabel* columnsLabel = new QLabel("Columns"); m_numberOfColumnsSpinBox = WuQFactory::newSpinBoxWithMinMaxStepSignalInt(1, - maximumNumberOfColumns, + s_maximumRowsColumns, 1, this, SLOT(configurationNumberOfRowsOrColumnsChanged())); @@ -414,9 +411,8 @@ TileTabsConfigurationDialog::createCustomConfigurationWidget() { rowStretchFactorLayout->addWidget(new QLabel("Rows"), 0, 0, 1, 2, Qt::AlignHCenter); - for (int32_t i = 0; i < maximumNumberOfRows; i++) { + for (int32_t i = 0; i < s_maximumRowsColumns; i++) { AString labelSpace = ((i >= 10) ? "" : " "); - if (i < maximumNumberOfRows) { QLabel* numberLabel = new QLabel(labelSpace + AString::number(i + 1)); m_rowStretchFactorIndexLabels.push_back(numberLabel); @@ -437,10 +433,9 @@ TileTabsConfigurationDialog::createCustomConfigurationWidget() rowStretchFactorLayout->addWidget(numberLabel, layoutRow, 0, Qt::AlignRight); rowStretchFactorLayout->addWidget(spinBox, layoutRow, 1); rowStretchFactorLayout->addWidget(pctLabel, layoutRow, 2); - } } - rowStretchFactorLayout->setRowStretch(maximumNumberOfRows, 100); + rowStretchFactorLayout->setRowStretch(s_maximumRowsColumns, 100); } QWidget* columnStretchFactorWidget = new QWidget(); @@ -450,9 +445,8 @@ TileTabsConfigurationDialog::createCustomConfigurationWidget() { columnStretchFactorLayout->addWidget(new QLabel("Columns"), 0, 0, 1, 2, Qt::AlignHCenter); - for (int32_t i = 0; i < maximumNumberOfColumns; i++) { + for (int32_t i = 0; i < s_maximumRowsColumns; i++) { AString labelSpace = ((i >= 10) ? "" : " "); - if (i < maximumNumberOfColumns) { QLabel* numberLabel = new QLabel(labelSpace + AString::number(i + 1)); m_columnStretchFactorIndexLabels.push_back(numberLabel); @@ -473,10 +467,9 @@ TileTabsConfigurationDialog::createCustomConfigurationWidget() columnStretchFactorLayout->addWidget(numberLabel, layoutRow, 0, Qt::AlignRight); columnStretchFactorLayout->addWidget(spinBox, layoutRow, 1); columnStretchFactorLayout->addWidget(pctLabel, layoutRow, 2); - } } - columnStretchFactorLayout->setRowStretch(maximumNumberOfColumns, 100); + columnStretchFactorLayout->setRowStretch(s_maximumRowsColumns, 100); } QLabel* stretchFactorLabel = new QLabel("Stretch Factors"); @@ -708,7 +701,8 @@ TileTabsConfigurationDialog::updateStretchFactors() QLabel* pctLabel = m_columnStretchPercentageLabels[i]; if (i < numValidColumns) { QSignalBlocker blocker(sb); - sb->setValue(configuration->getColumnStretchFactor(i)); + const TileTabsRowColumnElement* e = configuration->getColumn(i); + sb->setValue(e->getWeightStretch()); } indexLabel->setVisible(i < numValidColumns); sb->setVisible(i < numValidColumns); @@ -730,7 +724,8 @@ TileTabsConfigurationDialog::updateStretchFactors() QLabel* pctLabel = m_rowStretchPercentageLabels[i]; if (i < numValidRows) { QSignalBlocker blocker(sb); - sb->setValue(configuration->getRowStretchFactor(i)); + const TileTabsRowColumnElement* e = configuration->getRow(i); + sb->setValue(e->getWeightStretch()); } indexLabel->setVisible(i < numValidRows); sb->setVisible(i < numValidRows); @@ -991,16 +986,16 @@ TileTabsConfigurationDialog::configurationStretchFactorWasChanged() const int32_t numColSpinBoxes = static_cast(m_columnStretchFactorSpinBoxes.size()); for (int32_t i = 0; i < numColSpinBoxes; i++) { if (m_columnStretchFactorSpinBoxes[i]->isEnabled()) { - configuration->setColumnStretchFactor(i, - m_columnStretchFactorSpinBoxes[i]->value()); + TileTabsRowColumnElement* e = configuration->getColumn(i); + e->setWeightStretch(m_columnStretchFactorSpinBoxes[i]->value()); } } const int32_t numRowSpinBoxes = static_cast(m_rowStretchFactorSpinBoxes.size()); for (int32_t i = 0; i < numRowSpinBoxes; i++) { if (m_rowStretchFactorSpinBoxes[i]->isEnabled()) { - configuration->setRowStretchFactor(i, - m_rowStretchFactorSpinBoxes[i]->value()); + TileTabsRowColumnElement* e = configuration->getRow(i); + e->setWeightStretch(m_rowStretchFactorSpinBoxes[i]->value()); } } diff --git a/src/GuiQt/TileTabsConfigurationDialog.h b/src/GuiQt/TileTabsConfigurationDialog.h index f06036c54..2beaf7906 100644 --- a/src/GuiQt/TileTabsConfigurationDialog.h +++ b/src/GuiQt/TileTabsConfigurationDialog.h @@ -166,6 +166,8 @@ namespace caret { * manager. */ CaretPreferences* m_caretPreferences; + + static const int32_t s_maximumRowsColumns = 50; }; #ifdef __TILE_TABS_CONFIGURATION_DIALOG_DECLARE__ diff --git a/src/Operations/OperationShowScene.cxx b/src/Operations/OperationShowScene.cxx index c459cfa87..d53509307 100644 --- a/src/Operations/OperationShowScene.cxx +++ b/src/Operations/OperationShowScene.cxx @@ -465,8 +465,6 @@ OperationShowScene::useParameters(OperationParameters* myParams, TileTabsConfiguration* tileTabsConfiguration = bwc->getSelectedTileTabsConfiguration(); CaretAssert(tileTabsConfiguration); - if ((tileTabsConfiguration->getMaximumNumberOfRows() > 0) - && (tileTabsConfiguration->getMaximumNumberOfColumns() > 0)) { const std::vector tabIndices = bwc->getSceneTabIndices(); if ( ! tabIndices.empty()) { @@ -534,10 +532,6 @@ OperationShowScene::useParameters(OperationParameters* myParams, } viewports.clear(); } - } - else { - throw OperationException("Tile tabs configuration is corrupted."); - } } else { CaretPointer brainOpenGL(createBrainOpenGL()); -- GitLab From 212859f17a3cc6100599d429d9475e4fb9eb8fe6 Mon Sep 17 00:00:00 2001 From: John Harwell Date: Tue, 16 Oct 2018 14:47:45 -0500 Subject: [PATCH 079/427] WB-668 Titling columns in tile tabs: Revised Tile Tabs Configuration GUI with new controls for editing the configuration. --- src/Common/CMakeLists.txt | 2 + src/Common/EventManager.cxx | 1 + ...EventTileTabsConfigurationModification.cxx | 108 +++ .../EventTileTabsConfigurationModification.h | 96 +++ src/Common/EventTypeEnum.cxx | 4 + src/Common/EventTypeEnum.h | 2 + src/Common/TileTabsConfiguration.cxx | 10 +- .../TileTabsRowColumnContentTypeEnum.cxx | 6 +- src/Common/TileTabsRowColumnContentTypeEnum.h | 4 +- .../TileTabsRowColumnStretchTypeEnum.cxx | 6 +- src/Common/TileTabsRowColumnStretchTypeEnum.h | 4 +- src/GuiQt/BrainBrowserWindow.cxx | 24 +- src/GuiQt/BrainBrowserWindow.h | 3 + src/GuiQt/TileTabsConfigurationDialog.cxx | 710 ++++++++++++------ src/GuiQt/TileTabsConfigurationDialog.h | 93 ++- src/Operations/OperationShowScene.cxx | 2 +- 16 files changed, 811 insertions(+), 264 deletions(-) create mode 100644 src/Common/EventTileTabsConfigurationModification.cxx create mode 100644 src/Common/EventTileTabsConfigurationModification.h diff --git a/src/Common/CMakeLists.txt b/src/Common/CMakeLists.txt index a51e57ba8..89ae2cfff 100644 --- a/src/Common/CMakeLists.txt +++ b/src/Common/CMakeLists.txt @@ -105,6 +105,7 @@ EventListenerInterface.h EventManager.h EventPaletteGetByName.h EventProgressUpdate.h +EventTileTabsConfigurationModification.h EventTypeEnum.h FastStatistics.h FileAdapter.h @@ -209,6 +210,7 @@ EventListenerInterface.cxx EventManager.cxx EventPaletteGetByName.cxx EventProgressUpdate.cxx +EventTileTabsConfigurationModification.cxx EventTypeEnum.cxx FastStatistics.cxx FileAdapter.cxx diff --git a/src/Common/EventManager.cxx b/src/Common/EventManager.cxx index 0ea65a89f..ebd80d88e 100644 --- a/src/Common/EventManager.cxx +++ b/src/Common/EventManager.cxx @@ -455,6 +455,7 @@ EventManager::sendSimpleEvent(const EventTypeEnum::Enum eventType) case EventTypeEnum::EVENT_SURFACE_COLORING_INVALIDATE: case EventTypeEnum::EVENT_SURFACES_GET: case EventTypeEnum::EVENT_SURFACE_STRUCTURES_VALID_GET: + case EventTypeEnum::EVENT_TILE_TABS_MODIFICATION: case EventTypeEnum::EVENT_TOOLBOX_SELECTION_DISPLAY: case EventTypeEnum::EVENT_USER_INTERFACE_UPDATE: case EventTypeEnum::EVENT_PROGRESS_UPDATE: diff --git a/src/Common/EventTileTabsConfigurationModification.cxx b/src/Common/EventTileTabsConfigurationModification.cxx new file mode 100644 index 000000000..28fc5f4e0 --- /dev/null +++ b/src/Common/EventTileTabsConfigurationModification.cxx @@ -0,0 +1,108 @@ + +/*LICENSE_START*/ +/* + * Copyright (C) 2018 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + +#define __EVENT_TILE_TABS_CONFIGURATION_MODIFICATION_DECLARE__ +#include "EventTileTabsConfigurationModification.h" +#undef __EVENT_TILE_TABS_CONFIGURATION_MODIFICATION_DECLARE__ + +#include "CaretAssert.h" +#include "EventTypeEnum.h" + +using namespace caret; + + + +/** + * \class caret::EventTileTabsConfigurationModification + * \brief Event for modifying a tile tabs configuration. + * \ingroup Common + */ + +/** + * Constructor. + */ +EventTileTabsConfigurationModification::EventTileTabsConfigurationModification(const int32_t rowColumnIndex, + const RowColumnType rowColumnType, + const Operation operation) +: Event(EventTypeEnum::EVENT_TILE_TABS_MODIFICATION), +m_rowColumnIndex(rowColumnIndex), +m_rowColumnType(rowColumnType), +m_operation(operation), +m_windowIndex(-1) +{ + +} + +/** + * Destructor. + */ +EventTileTabsConfigurationModification::~EventTileTabsConfigurationModification() +{ +} + +/** + * @return Index of the window. + */ +int32_t +EventTileTabsConfigurationModification::getWindowIndex() const +{ + return m_windowIndex; +} + +/** + * Set the window index. + * + * @param windowIndex + * Index of the window. + */ +void +EventTileTabsConfigurationModification::setWindowIndex(const int32_t windowIndex) +{ + m_windowIndex = windowIndex; +} + +/** + * @return The index of the row or column. + */ +int32_t +EventTileTabsConfigurationModification::getRowColumnIndex() const +{ + return m_rowColumnIndex; +} + +/** + * @return The type ROW or COLUMN + */ +EventTileTabsConfigurationModification::RowColumnType +EventTileTabsConfigurationModification::getRowColumnType() const +{ + return m_rowColumnType; +} + +/** + * @return The operation. + */ +EventTileTabsConfigurationModification::Operation +EventTileTabsConfigurationModification::getOperation() const +{ + return m_operation; +} + diff --git a/src/Common/EventTileTabsConfigurationModification.h b/src/Common/EventTileTabsConfigurationModification.h new file mode 100644 index 000000000..0ce15b07e --- /dev/null +++ b/src/Common/EventTileTabsConfigurationModification.h @@ -0,0 +1,96 @@ +#ifndef __EVENT_TILE_TABS_CONFIGURATION_MODIFICATION_H__ +#define __EVENT_TILE_TABS_CONFIGURATION_MODIFICATION_H__ + +/*LICENSE_START*/ +/* + * Copyright (C) 2018 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + + + +#include + +#include "Event.h" + + + +namespace caret { + + class EventTileTabsConfigurationModification : public Event { + + public: + /** + * Row or Column + */ + enum class RowColumnType { + COLUMN, + ROW + }; + + /** + * Operation + */ + enum class Operation { + DELETE, + DUPLICATE_AFTER, + DUPLICATE_BEFORE, + MOVE_AFTER, + MOVE_BEFORE + }; + + EventTileTabsConfigurationModification(const int32_t rowColumnIndex, + const RowColumnType rowColumnType, + const Operation operation); + + virtual ~EventTileTabsConfigurationModification(); + + EventTileTabsConfigurationModification(const EventTileTabsConfigurationModification&) = delete; + + EventTileTabsConfigurationModification& operator=(const EventTileTabsConfigurationModification&) = delete; + + int32_t getRowColumnIndex() const; + + RowColumnType getRowColumnType() const; + + Operation getOperation() const; + + int32_t getWindowIndex() const; + + void setWindowIndex(const int32_t windowIndex); + + // ADD_NEW_METHODS_HERE + + private: + const int32_t m_rowColumnIndex; + + const RowColumnType m_rowColumnType; + + const Operation m_operation; + + int32_t m_windowIndex = -1; + + // ADD_NEW_MEMBERS_HERE + + }; + +#ifdef __EVENT_TILE_TABS_CONFIGURATION_MODIFICATION_DECLARE__ + // +#endif // __EVENT_TILE_TABS_CONFIGURATION_MODIFICATION_DECLARE__ + +} // namespace +#endif //__EVENT_TILE_TABS_CONFIGURATION_MODIFICATION_H__ diff --git a/src/Common/EventTypeEnum.cxx b/src/Common/EventTypeEnum.cxx index cc4ff6720..8414db2bd 100644 --- a/src/Common/EventTypeEnum.cxx +++ b/src/Common/EventTypeEnum.cxx @@ -362,6 +362,10 @@ EventTypeEnum::initialize() "EVENT_SURFACE_STRUCTURES_VALID_GET", "GGet valid surface strucutures and their number of node")); + enumData.push_back(EventTypeEnum(EVENT_TILE_TABS_MODIFICATION, + "EVENT_TILE_TABS_MODIFICATION", + "Tile tabs modification")); + enumData.push_back(EventTypeEnum(EVENT_TOOLBOX_SELECTION_DISPLAY, "EVENT_TOOLBOX_SELECTION_DISPLAY", "Display or hide the selection toolbox")); diff --git a/src/Common/EventTypeEnum.h b/src/Common/EventTypeEnum.h index 702fd796d..ace16fa46 100644 --- a/src/Common/EventTypeEnum.h +++ b/src/Common/EventTypeEnum.h @@ -188,6 +188,8 @@ public: EVENT_SURFACES_GET, /** Get valid surface strucutures and their number of nodes */ EVENT_SURFACE_STRUCTURES_VALID_GET, + /** Tile tabs modification */ + EVENT_TILE_TABS_MODIFICATION, /** Display/Hide the selection toolbox */ EVENT_TOOLBOX_SELECTION_DISPLAY, /** Update the User-Interface */ diff --git a/src/Common/TileTabsConfiguration.cxx b/src/Common/TileTabsConfiguration.cxx index 57d4bec9c..88f19c168 100644 --- a/src/Common/TileTabsConfiguration.cxx +++ b/src/Common/TileTabsConfiguration.cxx @@ -278,7 +278,7 @@ TileTabsConfiguration::getRowHeightsAndColumnWidthsForWindowSize(const int32_t w for (int32_t i = 0; i < numRows; i++) { const TileTabsRowColumnElement* e = getRow(i); switch (e->getStretchType()) { - case TileTabsRowColumnStretchTypeEnum::PERCENTAGE: + case TileTabsRowColumnStretchTypeEnum::PERCENT: break; case TileTabsRowColumnStretchTypeEnum::WEIGHT: rowStretchTotal += e->getWeightStretch(); @@ -290,7 +290,7 @@ TileTabsConfiguration::getRowHeightsAndColumnWidthsForWindowSize(const int32_t w int32_t h = 0; const TileTabsRowColumnElement* e = getRow(i); switch (e->getStretchType()) { - case TileTabsRowColumnStretchTypeEnum::PERCENTAGE: + case TileTabsRowColumnStretchTypeEnum::PERCENT: break; case TileTabsRowColumnStretchTypeEnum::WEIGHT: h = static_cast((e->getWeightStretch() / rowStretchTotal) @@ -308,7 +308,7 @@ TileTabsConfiguration::getRowHeightsAndColumnWidthsForWindowSize(const int32_t w for (int32_t i = 0; i < numCols; i++) { const TileTabsRowColumnElement* e = getColumn(i); switch (e->getStretchType()) { - case TileTabsRowColumnStretchTypeEnum::PERCENTAGE: + case TileTabsRowColumnStretchTypeEnum::PERCENT: break; case TileTabsRowColumnStretchTypeEnum::WEIGHT: columnStretchTotal += e->getWeightStretch(); @@ -320,7 +320,7 @@ TileTabsConfiguration::getRowHeightsAndColumnWidthsForWindowSize(const int32_t w int32_t w = 0; const TileTabsRowColumnElement* e = getColumn(i); switch (e->getStretchType()) { - case TileTabsRowColumnStretchTypeEnum::PERCENTAGE: + case TileTabsRowColumnStretchTypeEnum::PERCENT: break; case TileTabsRowColumnStretchTypeEnum::WEIGHT: w = static_cast((e->getWeightStretch() / columnStretchTotal) @@ -669,7 +669,7 @@ TileTabsConfiguration::decodeFromXMLWithStreamReader(const AString& xmlString, decodeFromXMLWithStreamReaderVersionTwo(xml); } else { - xml.raiseError("TileTabsConfigurationT invalid version=" + xml.raiseError("TileTabsConfiguration invalid version=" + versionNumberText); } } diff --git a/src/Common/TileTabsRowColumnContentTypeEnum.cxx b/src/Common/TileTabsRowColumnContentTypeEnum.cxx index 61c91a1f7..5b8327f79 100644 --- a/src/Common/TileTabsRowColumnContentTypeEnum.cxx +++ b/src/Common/TileTabsRowColumnContentTypeEnum.cxx @@ -106,9 +106,9 @@ TileTabsRowColumnContentTypeEnum::initialize() } initializedFlag = true; - enumData.push_back(TileTabsRowColumnContentTypeEnum(SPACER, - "SPACER", - "Spacer")); + enumData.push_back(TileTabsRowColumnContentTypeEnum(SPACE, + "SPACE", + "Space")); enumData.push_back(TileTabsRowColumnContentTypeEnum(TAB, "TAB", diff --git a/src/Common/TileTabsRowColumnContentTypeEnum.h b/src/Common/TileTabsRowColumnContentTypeEnum.h index cd4224ef2..e1e25d3f5 100644 --- a/src/Common/TileTabsRowColumnContentTypeEnum.h +++ b/src/Common/TileTabsRowColumnContentTypeEnum.h @@ -35,8 +35,8 @@ public: * Enumerated values. */ enum Enum { - /** Spacer */ - SPACER, + /** Space */ + SPACE, /** Tab */ TAB }; diff --git a/src/Common/TileTabsRowColumnStretchTypeEnum.cxx b/src/Common/TileTabsRowColumnStretchTypeEnum.cxx index 2a1c53ef4..4e6b16162 100644 --- a/src/Common/TileTabsRowColumnStretchTypeEnum.cxx +++ b/src/Common/TileTabsRowColumnStretchTypeEnum.cxx @@ -106,9 +106,9 @@ TileTabsRowColumnStretchTypeEnum::initialize() } initializedFlag = true; - enumData.push_back(TileTabsRowColumnStretchTypeEnum(PERCENTAGE, - "PERCENTAGE", - "Percentage")); + enumData.push_back(TileTabsRowColumnStretchTypeEnum(PERCENT, + "PERCENT", + "Percent")); enumData.push_back(TileTabsRowColumnStretchTypeEnum(WEIGHT, "WEIGHT", diff --git a/src/Common/TileTabsRowColumnStretchTypeEnum.h b/src/Common/TileTabsRowColumnStretchTypeEnum.h index 40fdcac57..c99fb0cca 100644 --- a/src/Common/TileTabsRowColumnStretchTypeEnum.h +++ b/src/Common/TileTabsRowColumnStretchTypeEnum.h @@ -35,8 +35,8 @@ public: * Enumerated values. */ enum Enum { - /** Percentage */ - PERCENTAGE, + /** Percent */ + PERCENT, /** Weight */ WEIGHT }; diff --git a/src/GuiQt/BrainBrowserWindow.cxx b/src/GuiQt/BrainBrowserWindow.cxx index a9c8c7dcb..08adf6750 100644 --- a/src/GuiQt/BrainBrowserWindow.cxx +++ b/src/GuiQt/BrainBrowserWindow.cxx @@ -71,11 +71,12 @@ #include "EventMacDockMenuUpdate.h" #include "EventManager.h" #include "EventModelGetAll.h" +#include "EventGetOrSetUserInputModeProcessor.h" #include "EventGraphicsUpdateAllWindows.h" #include "EventGraphicsUpdateOneWindow.h" #include "EventSpecFileReadDataFiles.h" #include "EventSurfaceColoringInvalidate.h" -#include "EventGetOrSetUserInputModeProcessor.h" +#include "EventTileTabsConfigurationModification.h" #include "EventUserInterfaceUpdate.h" #include "FileInformation.h" #include "FociProjectionDialog.h" @@ -250,6 +251,7 @@ m_browserWindowIndex(browserWindowIndex) EventManager::get()->addEventListener(this, EventTypeEnum::EVENT_GET_VIEWPORT_SIZE); EventManager::get()->addEventListener(this, EventTypeEnum::EVENT_GRAPHICS_UPDATE_ALL_WINDOWS); EventManager::get()->addEventListener(this, EventTypeEnum::EVENT_GRAPHICS_UPDATE_ONE_WINDOW); + EventManager::get()->addEventListener(this, EventTypeEnum::EVENT_TILE_TABS_MODIFICATION); if (m_overlayHorizontalToolBox == m_overlayActiveToolBox) { /* @@ -460,6 +462,14 @@ BrainBrowserWindow::receiveEvent(Event* event) viewportSizeEvent->setViewportSize(viewport); } } + else if (event->getEventType() == EventTypeEnum::EVENT_TILE_TABS_MODIFICATION) { + EventTileTabsConfigurationModification* modEvent = dynamic_cast(event); + CaretAssert(modEvent); + + if (modEvent->getWindowIndex() == this->m_browserWindowIndex) { + modifyTileTabsConfiguration(modEvent); + } + } else if ((event->getEventType() == EventTypeEnum::EVENT_GRAPHICS_UPDATE_ONE_WINDOW) || (event->getEventType() == EventTypeEnum::EVENT_GRAPHICS_UPDATE_ALL_WINDOWS)) { /* @@ -2314,6 +2324,18 @@ BrainBrowserWindow::getTileTabsConfigurationLabelText(const TileTabsConfiguratio return textLabel; } +/** + * Modify the tile tabs configuration. + * + * @param modEvent + * Event with modification information. + */ +void +BrainBrowserWindow::modifyTileTabsConfiguration(EventTileTabsConfigurationModification* modEvent) +{ + CaretAssert(modEvent); +} + /** * Update the tile tabs configuration menu just before it is shown. */ diff --git a/src/GuiQt/BrainBrowserWindow.h b/src/GuiQt/BrainBrowserWindow.h index fdf5c685a..f902bf1ee 100644 --- a/src/GuiQt/BrainBrowserWindow.h +++ b/src/GuiQt/BrainBrowserWindow.h @@ -46,6 +46,7 @@ namespace caret { class BrainOpenGLWidget; class BrowserWindowContent; class BrowserTabContent; + class EventTileTabsConfigurationModification; class PlainTextStringBuilder; class SceneClassAssistant; class TileTabsConfiguration; @@ -328,6 +329,8 @@ namespace caret { void saveBrowserWindowContentForScene(); + void modifyTileTabsConfiguration(EventTileTabsConfigurationModification* modEvent); + /** Index of this window */ const int32_t m_browserWindowIndex; diff --git a/src/GuiQt/TileTabsConfigurationDialog.cxx b/src/GuiQt/TileTabsConfigurationDialog.cxx index d2400a1d2..8e6b21185 100644 --- a/src/GuiQt/TileTabsConfigurationDialog.cxx +++ b/src/GuiQt/TileTabsConfigurationDialog.cxx @@ -26,10 +26,12 @@ #include #include #include +#include #include #include #include #include +#include #include @@ -42,14 +44,17 @@ #include "BrowserWindowContent.h" #include "CaretAssert.h" #include "CaretPreferences.h" +#include "EnumComboBoxTemplate.h" #include "EventBrowserWindowGraphicsRedrawn.h" #include "EventGraphicsUpdateOneWindow.h" #include "EventManager.h" #include "GuiManager.h" #include "SessionManager.h" #include "TileTabsConfiguration.h" +#include "TileTabsRowColumnElement.h" #include "WuQDataEntryDialog.h" #include "WuQFactory.h" +#include "WuQGridLayoutGroup.h" #include "WuQListWidget.h" #include "WuQMessageBox.h" #include "WuQtUtilities.h" @@ -86,7 +91,7 @@ TileTabsConfigurationDialog::TileTabsConfigurationDialog(BrainBrowserWindow* par 0, Qt::AlignTop); configurationLayout->addWidget(createUserConfigurationSelectionWidget(), - 0, + 100, Qt::AlignTop); disableAutoDefaultForAllPushButtons(); @@ -100,8 +105,8 @@ TileTabsConfigurationDialog::TileTabsConfigurationDialog(BrainBrowserWindow* par setCentralWidget(dialogWidget, WuQDialog::SCROLL_AREA_NEVER); - resize(sizeHint().width(), - 400); + resize(750, + 500); } /** @@ -349,10 +354,158 @@ TileTabsConfigurationDialog::createWorkbenchWindowWidget() } /** - * @return Instance of workbench window widget. + * @return The rows/columns stretch layout */ QWidget* -TileTabsConfigurationDialog::createCustomConfigurationWidget() +TileTabsConfigurationDialog::createRowColumnStretchWidget() +{ + QGroupBox* rowGroupBox = new QGroupBox("Rows"); + rowGroupBox->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); + m_rowElementsGridLayout = new QGridLayout(rowGroupBox); + WuQtUtilities::setLayoutSpacingAndMargins(m_rowElementsGridLayout, 4, 2); + + QGroupBox* columnsGroupBox = new QGroupBox("Columns"); + m_columnElementsGridLayout = new QGridLayout(columnsGroupBox); + WuQtUtilities::setLayoutSpacingAndMargins(m_columnElementsGridLayout, 2, 2); + + QWidget* widget = new QWidget; + QVBoxLayout* layout = new QVBoxLayout(widget); + layout->setContentsMargins(0, 0, 0, 0); + layout->addWidget(rowGroupBox); + layout->addSpacing(6); + layout->addWidget(columnsGroupBox); + layout->addStretch(); + + return widget; +} + +/** + * update the row and column stretching widgets. + * + * @param configuration + * Current custom configuration. + */ +void +TileTabsConfigurationDialog::updateRowColumnStretchWidgets(TileTabsConfiguration* configuration) +{ + /* + * Update rows + */ + { + const int32_t numRows = configuration->getNumberOfRows(); + int32_t numRowElements = static_cast(m_rowElements.size()); + + /** + * Add elements as needed. + */ + const int32_t numToAdd = numRows - numRowElements; + for (int32_t iRow = 0; iRow < numToAdd; iRow++) { + addRowColumnStretchWidget(EventTileTabsConfigurationModification::RowColumnType::ROW, + m_rowElementsGridLayout, + m_rowElements); + } + + /* + * Update widgets with element content + */ + numRowElements = static_cast(m_rowElements.size()); + for (int32_t iRow = 0; iRow < numRowElements; iRow++) { + TileTabsRowColumnElement* element(NULL); + if (iRow < numRows) { + element = configuration->getRow(iRow); + } + + CaretAssertVectorIndex(m_rowElements, iRow); + m_rowElements[iRow]->updateContent(element); + } + + m_rowElementsGridLayout->setSizeConstraint(QLayout::SetMinAndMaxSize); + } + + /* + * Update columns + */ + { + const int32_t numColumns = configuration->getNumberOfColumns(); + int32_t numColumnElements = static_cast(m_columnElements.size()); + + /** + * Add elements as needed. + */ + const int32_t numToAdd = numColumns - numColumnElements; + for (int32_t iColumn = 0; iColumn < numToAdd; iColumn++) { + addRowColumnStretchWidget(EventTileTabsConfigurationModification::RowColumnType::COLUMN, + m_columnElementsGridLayout, + m_columnElements); + } + + /* + * Update widgets with element content + */ + numColumnElements = static_cast(m_columnElements.size()); + for (int32_t iColumn = 0; iColumn < numColumnElements; iColumn++) { + TileTabsRowColumnElement* element(NULL); + if (iColumn < numColumns) { + element = configuration->getColumn(iColumn); + } + + CaretAssertVectorIndex(m_columnElements, iColumn); + m_columnElements[iColumn]->updateContent(element); + } + + m_columnElementsGridLayout->setSizeConstraint(QLayout::SetMinAndMaxSize); + } + +// m_customConfigurationWidget->adjustSize(); +} + +/** + * Add a row/column stretch widget. + * + * @param rowColumnType + * The row or column type. + * @param gridLayout + * Grid layout for widgets. + * @param elementVector + * Container for row/column elements. + */ +void +TileTabsConfigurationDialog::addRowColumnStretchWidget(const EventTileTabsConfigurationModification::RowColumnType rowColumnType, + QGridLayout* gridLayout, + std::vector& elementVector) +{ + const int32_t index = static_cast(elementVector.size()); + if (index == 0) { + int32_t columnIndex(0); + int32_t row = gridLayout->rowCount(); + gridLayout->addWidget(new QLabel("Index"), row, columnIndex++, Qt::AlignRight); + gridLayout->addWidget(new QLabel(" "), row, columnIndex++); + gridLayout->addWidget(new QLabel("Content"), row, columnIndex++, Qt::AlignHCenter); + gridLayout->addWidget(new QLabel("Type"), row, columnIndex++, Qt::AlignHCenter); + gridLayout->addWidget(new QLabel("Stretch"), row, columnIndex++, Qt::AlignHCenter); + for (int32_t i = 0; i < columnIndex; i++) { + gridLayout->setColumnStretch(i, 0); + } + } + + TileTabElementWidgets* elementWidget = new TileTabElementWidgets(rowColumnType, + index, + gridLayout, + this); + QObject::connect(elementWidget, &TileTabElementWidgets::itemChanged, + this, &TileTabsConfigurationDialog::configurationStretchFactorWasChanged); + QObject::connect(elementWidget, &TileTabElementWidgets::modificationRequested, + this, &TileTabsConfigurationDialog::tileTabsModificationRequested); + + elementVector.push_back(elementWidget); +} + + +/** + * @return The active configuration widget. + */ +QWidget* +TileTabsConfigurationDialog::createActiveConfigurationWidget() { const AString autoToolTip("Workbench adjusts the number of rows and columns so " "that all tabs are displayed"); @@ -369,7 +522,6 @@ TileTabsConfigurationDialog::createCustomConfigurationWidget() QObject::connect(buttonGroup, static_cast(&QButtonGroup::buttonClicked), this, &TileTabsConfigurationDialog::automaticCustomButtonClicked); - QLabel* dimensionsLabel = new QLabel("Dimensions"); QLabel* rowsLabel = new QLabel("Rows"); m_numberOfRowsSpinBox = WuQFactory::newSpinBoxWithMinMaxStepSignalInt(1, s_maximumRowsColumns, @@ -386,149 +538,47 @@ TileTabsConfigurationDialog::createCustomConfigurationWidget() SLOT(configurationNumberOfRowsOrColumnsChanged())); m_numberOfColumnsSpinBox->setToolTip("Number of columns for the tab configuration"); - - QGridLayout* dimensionsLayout = new QGridLayout(); - dimensionsLayout->setContentsMargins(0, 0, 0, 0); - dimensionsLayout->setColumnStretch(0, 100); - dimensionsLayout->setColumnStretch(5, 100); - dimensionsLayout->addWidget(dimensionsLabel, 0, 1, 1, 4, Qt::AlignHCenter); - dimensionsLayout->addWidget(rowsLabel, 1, 1); - dimensionsLayout->addWidget(m_numberOfRowsSpinBox, 1, 2); - dimensionsLayout->addWidget(columnsLabel, 1, 3); - dimensionsLayout->addWidget(m_numberOfColumnsSpinBox, 1, 4); - - const float stretchFactorMinimumValue = 0.1; - const float stretchFactorMaximumValue = 10000000.0; - const float stretchFactorStep = 0.1; - const float stretchFactorDigitsRightOfDecimal = 2; - - const int32_t spinBoxWidth = 80; - - QWidget* rowStretchFactorWidget = new QWidget(); - QGridLayout* rowStretchFactorLayout = new QGridLayout(rowStretchFactorWidget); - rowStretchFactorLayout->setContentsMargins(0, 0, 0, 0); - rowStretchFactorLayout->setSpacing(2); - { - rowStretchFactorLayout->addWidget(new QLabel("Rows"), 0, 0, 1, 2, Qt::AlignHCenter); - - for (int32_t i = 0; i < s_maximumRowsColumns; i++) { - AString labelSpace = ((i >= 10) ? "" : " "); - QLabel* numberLabel = new QLabel(labelSpace + AString::number(i + 1)); - m_rowStretchFactorIndexLabels.push_back(numberLabel); - - QDoubleSpinBox* spinBox = WuQFactory::newDoubleSpinBoxWithMinMaxStepDecimalsSignalDouble(stretchFactorMinimumValue, - stretchFactorMaximumValue, - stretchFactorStep, - stretchFactorDigitsRightOfDecimal, - this, - SLOT(configurationStretchFactorWasChanged())); - spinBox->setFixedWidth(spinBoxWidth); - spinBox->setToolTip("Weight for row " + AString::number(i + 1)); - m_rowStretchFactorSpinBoxes.push_back(spinBox); - - QLabel* pctLabel = new QLabel("000%"); - m_rowStretchPercentageLabels.push_back(pctLabel); - - const int layoutRow = rowStretchFactorLayout->rowCount(); - rowStretchFactorLayout->addWidget(numberLabel, layoutRow, 0, Qt::AlignRight); - rowStretchFactorLayout->addWidget(spinBox, layoutRow, 1); - rowStretchFactorLayout->addWidget(pctLabel, layoutRow, 2); - } - - rowStretchFactorLayout->setRowStretch(s_maximumRowsColumns, 100); - } - - QWidget* columnStretchFactorWidget = new QWidget(); - QGridLayout* columnStretchFactorLayout = new QGridLayout(columnStretchFactorWidget); - columnStretchFactorLayout->setContentsMargins(0, 0, 0, 0); - columnStretchFactorLayout->setSpacing(2); - { - columnStretchFactorLayout->addWidget(new QLabel("Columns"), 0, 0, 1, 2, Qt::AlignHCenter); - - for (int32_t i = 0; i < s_maximumRowsColumns; i++) { - AString labelSpace = ((i >= 10) ? "" : " "); - QLabel* numberLabel = new QLabel(labelSpace + AString::number(i + 1)); - m_columnStretchFactorIndexLabels.push_back(numberLabel); - - QDoubleSpinBox* spinBox = WuQFactory::newDoubleSpinBoxWithMinMaxStepDecimalsSignalDouble(stretchFactorMinimumValue, - stretchFactorMaximumValue, - stretchFactorStep, - stretchFactorDigitsRightOfDecimal, - this, - SLOT(configurationStretchFactorWasChanged())); - spinBox->setFixedWidth(spinBoxWidth); - spinBox->setToolTip("Weight for column " + AString::number(i + 1)); - m_columnStretchFactorSpinBoxes.push_back(spinBox); - - QLabel* pctLabel = new QLabel("111%"); - m_columnStretchPercentageLabels.push_back(pctLabel); - - const int layoutRow = columnStretchFactorLayout->rowCount(); - columnStretchFactorLayout->addWidget(numberLabel, layoutRow, 0, Qt::AlignRight); - columnStretchFactorLayout->addWidget(spinBox, layoutRow, 1); - columnStretchFactorLayout->addWidget(pctLabel, layoutRow, 2); - } - - columnStretchFactorLayout->setRowStretch(s_maximumRowsColumns, 100); - } - - QLabel* stretchFactorLabel = new QLabel("Stretch Factors"); - - QWidget* stretchFactorWidget = new QWidget(); - stretchFactorWidget->setSizePolicy(stretchFactorWidget->sizePolicy().horizontalPolicy(), - QSizePolicy::Fixed); - QHBoxLayout* stretchFactorLayout = new QHBoxLayout(stretchFactorWidget); - stretchFactorLayout->setContentsMargins(0, 0, 0, 0); - stretchFactorLayout->addWidget(rowStretchFactorWidget, 0, Qt::AlignHCenter | Qt::AlignTop); - stretchFactorLayout->addWidget(WuQtUtilities::createVerticalLineWidget(), 0); - stretchFactorLayout->addWidget(columnStretchFactorWidget, 0, Qt::AlignHCenter | Qt::AlignTop); - - - m_customConfigurationWidget = new QWidget(); - QVBoxLayout* customConfigurationLayout = new QVBoxLayout(m_customConfigurationWidget); - customConfigurationLayout->addLayout(dimensionsLayout); - customConfigurationLayout->addWidget(stretchFactorLabel, - 0, - Qt::AlignHCenter); - customConfigurationLayout->addWidget(stretchFactorWidget, - 0, - Qt::AlignHCenter); - - return m_customConfigurationWidget; -} - -/** - * @return The active configuration widget. - */ -QWidget* -TileTabsConfigurationDialog::createActiveConfigurationWidget() -{ - + m_customConfigurationWidget = createRowColumnStretchWidget(); + m_customConfigurationWidget->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); QScrollArea* stretchFactorScrollArea = new QScrollArea(); - stretchFactorScrollArea->setWidget(createCustomConfigurationWidget()); + stretchFactorScrollArea->setWidget(m_customConfigurationWidget); + stretchFactorScrollArea->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); +// stretchFactorScrollArea->setSizeAdjustPolicy(QScrollArea::AdjustToContents); stretchFactorScrollArea->setWidgetResizable(true); - + QGroupBox* widget = new QGroupBox("Tile Tabs Configuration in Workbench Window"); QGridLayout* widgetLayout = new QGridLayout(widget); widgetLayout->setColumnStretch(0, 0); - widgetLayout->setColumnStretch(1, 100); + widgetLayout->setColumnStretch(1, 0); + widgetLayout->setColumnStretch(2, 0); + widgetLayout->setColumnStretch(3, 0); + widgetLayout->setColumnStretch(4, 0); + widgetLayout->setColumnStretch(5, 100); widgetLayout->setColumnMinimumWidth(0, 20); widgetLayout->addWidget(createWorkbenchWindowWidget(), - 0, 0, 1, 2, + 0, 0, 1, 6, Qt::AlignLeft); widgetLayout->addWidget(WuQtUtilities::createHorizontalLineWidget(), - 1, 0, 1, 2); + 1, 0, 1, 6); widgetLayout->addWidget(m_automaticConfigurationRadioButton, - 2, 0, 1, 2, + 2, 0, 1, 6, Qt::AlignLeft); widgetLayout->addWidget(m_customConfigurationRadioButton, - 3, 0, 1, 2, + 3, 0, 1, 1, Qt::AlignLeft); + widgetLayout->addWidget(rowsLabel, + 3, 1, 1, 1); + widgetLayout->addWidget(m_numberOfRowsSpinBox, + 3, 2, 1, 1); + widgetLayout->addWidget(columnsLabel, + 3, 3, 1, 1); + widgetLayout->addWidget(m_numberOfColumnsSpinBox, + 3, 4, 1, 1); widgetLayout->addWidget(stretchFactorScrollArea, - 4, 1, - Qt::AlignLeft); - widgetLayout->setRowStretch(widgetLayout->rowCount(), 100); + 4, 0, 1, 6); + + widget->setFixedWidth(widget->sizeHint().width()); return widget; } @@ -674,100 +724,21 @@ TileTabsConfigurationDialog::updateStretchFactors() true)); m_customConfigurationRadioButton->setText(browserWindow->getTileTabsConfigurationLabelText(TileTabsConfigurationModeEnum::CUSTOM, false)); - int32_t numValidRows = 0; - int32_t numValidColumns = 0; - const TileTabsConfiguration* configuration = getCustomTileTabsConfiguration(); if (configuration != NULL) { - numValidRows = configuration->getNumberOfRows(); - numValidColumns = configuration->getNumberOfColumns(); - } - - QSignalBlocker rowNumBlocker(m_numberOfRowsSpinBox); - m_numberOfRowsSpinBox->setValue(numValidRows); - - QSignalBlocker colNumBlocker(m_numberOfColumnsSpinBox); - m_numberOfColumnsSpinBox->setValue(numValidColumns); - - CaretAssert(m_columnStretchFactorIndexLabels.size() == m_columnStretchFactorSpinBoxes.size()); - CaretAssert(m_columnStretchPercentageLabels.size() == m_columnStretchFactorSpinBoxes.size()); - const int32_t numColSpinBoxes = static_cast(m_columnStretchFactorSpinBoxes.size()); - for (int32_t i = 0; i < numColSpinBoxes; i++) { - CaretAssertVectorIndex(m_columnStretchFactorSpinBoxes, i); - CaretAssertVectorIndex(m_columnStretchFactorIndexLabels, i); - CaretAssertVectorIndex(m_columnStretchPercentageLabels, i); - QDoubleSpinBox* sb = m_columnStretchFactorSpinBoxes[i]; - QLabel* indexLabel = m_columnStretchFactorIndexLabels[i]; - QLabel* pctLabel = m_columnStretchPercentageLabels[i]; - if (i < numValidColumns) { - QSignalBlocker blocker(sb); - const TileTabsRowColumnElement* e = configuration->getColumn(i); - sb->setValue(e->getWeightStretch()); - } - indexLabel->setVisible(i < numValidColumns); - sb->setVisible(i < numValidColumns); - pctLabel->setVisible(i < numValidColumns); - } - updatePercentageLabels(m_columnStretchFactorSpinBoxes, - m_columnStretchPercentageLabels, - numValidColumns); - - CaretAssert(m_rowStretchFactorIndexLabels.size() == m_rowStretchFactorSpinBoxes.size()); - CaretAssert(m_rowStretchPercentageLabels.size() == m_rowStretchFactorSpinBoxes.size()); - const int32_t numRowSpinBoxes = static_cast(m_rowStretchFactorSpinBoxes.size()); - for (int32_t i = 0; i < numRowSpinBoxes; i++) { - CaretAssertVectorIndex(m_rowStretchFactorIndexLabels, i); - CaretAssertVectorIndex(m_rowStretchFactorSpinBoxes, i); - CaretAssertVectorIndex(m_rowStretchPercentageLabels, i); - QDoubleSpinBox* sb = m_rowStretchFactorSpinBoxes[i]; - QLabel* indexLabel = m_rowStretchFactorIndexLabels[i]; - QLabel* pctLabel = m_rowStretchPercentageLabels[i]; - if (i < numValidRows) { - QSignalBlocker blocker(sb); - const TileTabsRowColumnElement* e = configuration->getRow(i); - sb->setValue(e->getWeightStretch()); - } - indexLabel->setVisible(i < numValidRows); - sb->setVisible(i < numValidRows); - pctLabel->setVisible(i < numValidRows); + updateRowColumnStretchWidgets(const_cast(configuration)); + QSignalBlocker rowBlocker(m_numberOfRowsSpinBox); + m_numberOfRowsSpinBox->setValue(configuration->getNumberOfRows()); + QSignalBlocker columnBlocker(m_numberOfColumnsSpinBox); + m_numberOfColumnsSpinBox->setValue(configuration->getNumberOfColumns()); } - updatePercentageLabels(m_rowStretchFactorSpinBoxes, - m_rowStretchPercentageLabels, - numValidRows); const bool editableFlag = ( ! m_automaticConfigurationRadioButton->isChecked()); - m_customConfigurationWidget->setFixedSize(m_customConfigurationWidget->sizeHint()); m_customConfigurationWidget->setEnabled(editableFlag); m_loadPushButton->setEnabled(editableFlag); } -/** - * Update the percentage labels. - */ -void -TileTabsConfigurationDialog::updatePercentageLabels(const std::vector& factorSpinBoxes, - std::vector& percentageLabels, - const int32_t validCount) -{ - float sum = 0.0; - - for (int32_t i = 0; i < validCount; i++) { - CaretAssertVectorIndex(factorSpinBoxes, i); - sum += factorSpinBoxes[i]->value(); - } - - if (sum > 0.0) { - for (int32_t i = 0; i < validCount; i++) { - CaretAssertVectorIndex(factorSpinBoxes, i); - const float pct = (factorSpinBoxes[i]->value() / sum) * 100.0; - CaretAssertVectorIndex(percentageLabels, i); - percentageLabels[i]->setText(QString::number(pct, 'f', 0) + "%"); - } - } -} - - /** * Select the tile tabs configuration with the given name. */ @@ -983,26 +954,31 @@ TileTabsConfigurationDialog::configurationStretchFactorWasChanged() return; } - const int32_t numColSpinBoxes = static_cast(m_columnStretchFactorSpinBoxes.size()); - for (int32_t i = 0; i < numColSpinBoxes; i++) { - if (m_columnStretchFactorSpinBoxes[i]->isEnabled()) { - TileTabsRowColumnElement* e = configuration->getColumn(i); - e->setWeightStretch(m_columnStretchFactorSpinBoxes[i]->value()); - } - } - - const int32_t numRowSpinBoxes = static_cast(m_rowStretchFactorSpinBoxes.size()); - for (int32_t i = 0; i < numRowSpinBoxes; i++) { - if (m_rowStretchFactorSpinBoxes[i]->isEnabled()) { - TileTabsRowColumnElement* e = configuration->getRow(i); - e->setWeightStretch(m_rowStretchFactorSpinBoxes[i]->value()); - } - } - updateStretchFactors(); updateGraphicsWindow(); } +/** + * Called when a tile tabs configuration modification is requested + * + * @param modification + * Modification that is requested. + */ +void +TileTabsConfigurationDialog::tileTabsModificationRequested(EventTileTabsConfigurationModification& modification) +{ + TileTabsConfiguration* configuration = getCustomTileTabsConfiguration(); + if (configuration != NULL) { + + modification.setWindowIndex(m_browserWindowComboBox->getSelectedBrowserWindowIndex()); + + EventManager::get()->sendEvent(modification.getPointer()); + + updateStretchFactors(); + + updateGraphicsWindow(); + } +} /** * Update the graphics for the selected window. @@ -1017,3 +993,269 @@ TileTabsConfigurationDialog::updateGraphicsWindow() } } +/** + * Constructor. + * + * @param index + * Index of the row/column + * @param gridLayout + * Gridlayout for widgets + * @param parent + * Parent QObject + */ +TileTabElementWidgets::TileTabElementWidgets(const EventTileTabsConfigurationModification::RowColumnType rowColumnType, + const int32_t index, + QGridLayout* gridLayout, + QObject* parent) +: QObject(parent), +m_rowColumnType(rowColumnType), +m_index(index), +m_element(NULL) +{ + m_indexLabel = new QLabel(QString::number(m_index + 1)); + m_indexLabel->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); + + /* + * Construction Tool Button + */ + QIcon constructionIcon; + const bool constructionIconValid = WuQtUtilities::loadIcon(":/LayersPanel/construction.png", + constructionIcon); + m_constructionAction = WuQtUtilities::createAction("M", + "Add/Move/Remove", + this); + if (constructionIconValid) { + m_constructionAction->setIcon(constructionIcon); + } + m_constructionToolButton = new QToolButton(); + QMenu* constructionMenu = createConstructionMenu(m_constructionToolButton); + QObject::connect(constructionMenu, &QMenu::aboutToShow, + this, &TileTabElementWidgets::constructionMenuAboutToShow); + QObject::connect(constructionMenu, &QMenu::triggered, + this, &TileTabElementWidgets::constructionMenuTriggered); + m_constructionAction->setMenu(constructionMenu); + m_constructionToolButton->setDefaultAction(m_constructionAction); + m_constructionToolButton->setPopupMode(QToolButton::InstantPopup); + m_constructionToolButton->setFixedWidth(m_constructionToolButton->sizeHint().width()); + + /* + * Content type combo box + */ + m_contentTypeComboBox = new EnumComboBoxTemplate(this); + m_contentTypeComboBox->setup(); + QObject::connect(m_contentTypeComboBox, &EnumComboBoxTemplate::itemActivated, + this, &TileTabElementWidgets::contentTypeActivated); + m_contentTypeComboBox->getComboBox()->setFixedWidth(m_contentTypeComboBox->getComboBox()->sizeHint().width()); + + /* + * Stretch type combo box + */ + m_stretchTypeComboBox = new EnumComboBoxTemplate(this); + m_stretchTypeComboBox->setup(); + QObject::connect(m_stretchTypeComboBox, &EnumComboBoxTemplate::itemActivated, + this, &TileTabElementWidgets::stretchTypeActivated); + m_stretchTypeComboBox->getComboBox()->setFixedWidth(m_stretchTypeComboBox->getComboBox()->sizeHint().width()); + + /* + * Stretch value spin box + */ + m_stretchValueSpinBox = new QDoubleSpinBox(); + m_stretchValueSpinBox->setKeyboardTracking(false); + m_stretchValueSpinBox->setRange(0.0, 1000.0); + m_stretchValueSpinBox->setDecimals(2); + m_stretchValueSpinBox->setSingleStep(0.1); + QObject::connect(m_stretchValueSpinBox, static_cast(&QDoubleSpinBox::valueChanged), + this, &TileTabElementWidgets::stretchValueChanged); + m_stretchValueSpinBox->setFixedWidth(m_stretchValueSpinBox->sizeHint().width()); + + m_gridLayoutGroup = new WuQGridLayoutGroup(gridLayout); + const int32_t rowIndex(gridLayout->rowCount()); + int32_t columnIndex(0); + m_gridLayoutGroup->addWidget(m_indexLabel, rowIndex, columnIndex++, Qt::AlignRight); + m_gridLayoutGroup->addWidget(m_constructionToolButton, rowIndex, columnIndex++); + m_gridLayoutGroup->addWidget(m_contentTypeComboBox->getWidget(), rowIndex, columnIndex++); + m_gridLayoutGroup->addWidget(m_stretchTypeComboBox->getWidget(), rowIndex, columnIndex++); + m_gridLayoutGroup->addWidget(m_stretchValueSpinBox, rowIndex, columnIndex++); +} + +/** + * Destructor. + */ +TileTabElementWidgets::~TileTabElementWidgets() +{ + +} + +/** + * @return The construction menu. + * + * @param toolButton + * The parent toolbutton. + */ +QMenu* +TileTabElementWidgets::createConstructionMenu(QToolButton* toolButton) +{ + const AString deleteText((m_rowColumnType == EventTileTabsConfigurationModification::RowColumnType::COLUMN) + ? "Delete this Column" + : "Delete this Row"); + + const AString duplicateAfterText((m_rowColumnType == EventTileTabsConfigurationModification::RowColumnType::COLUMN) + ? "Duplicate this Column to Right" + : "Duplicate this Row Below"); + + const AString duplicateBeforeText((m_rowColumnType == EventTileTabsConfigurationModification::RowColumnType::COLUMN) + ? "Duplicate this Column to Left" + : "Duplicate this Row Above"); + + const AString moveAfterText((m_rowColumnType == EventTileTabsConfigurationModification::RowColumnType::COLUMN) + ? "Move this Column to Right" + : "Move this Row Down"); + + const AString moveBeforeText((m_rowColumnType == EventTileTabsConfigurationModification::RowColumnType::COLUMN) + ? "Move this Column to Left" + : "Move this Row Up"); + + m_menuDeleteAction = new QAction(deleteText); + m_menuDeleteAction->setData(static_cast(EventTileTabsConfigurationModification::Operation::DELETE)); + + m_menuDuplicateAfterAction = new QAction(duplicateAfterText); + m_menuDuplicateAfterAction->setData(static_cast(EventTileTabsConfigurationModification::Operation::DUPLICATE_AFTER)); + + m_menuDuplicateBeforeAction = new QAction(duplicateBeforeText); + m_menuDuplicateBeforeAction->setData(static_cast(EventTileTabsConfigurationModification::Operation::DUPLICATE_BEFORE)); + + m_menuMoveAfterAction = new QAction(moveAfterText); + m_menuMoveAfterAction->setData(static_cast(EventTileTabsConfigurationModification::Operation::MOVE_AFTER)); + + m_menuMoveBeforeAction = new QAction(moveBeforeText); + m_menuMoveBeforeAction->setData(static_cast(EventTileTabsConfigurationModification::Operation::MOVE_BEFORE)); + + QMenu* menu = new QMenu(toolButton); + menu->addAction(m_menuDuplicateBeforeAction); + menu->addAction(m_menuDuplicateAfterAction); + menu->addSeparator(); + menu->addAction(m_menuMoveBeforeAction); + menu->addAction(m_menuMoveAfterAction); + menu->addSeparator(); + menu->addAction(m_menuDeleteAction); + + return menu; +} + +/** + * Update with the given row/column element. + */ +void +TileTabElementWidgets::updateContent(TileTabsRowColumnElement* element) +{ + m_element = element; + const bool showFlag(m_element != NULL); + + if (showFlag) { + m_contentTypeComboBox->setSelectedItem(element->getContentType()); + m_stretchTypeComboBox->setSelectedItem(element->getStretchType()); + QSignalBlocker valueBlocker(m_stretchValueSpinBox); + switch (m_element->getStretchType()) { + case TileTabsRowColumnStretchTypeEnum::PERCENT: + m_stretchValueSpinBox->setRange(0.0, 100.0); + m_stretchValueSpinBox->setValue(m_element->getPercentStretch()); + break; + case TileTabsRowColumnStretchTypeEnum::WEIGHT: + m_stretchValueSpinBox->setRange(0.0, 1000.0); + m_stretchValueSpinBox->setValue(m_element->getWeightStretch()); + break; + } + } + + m_gridLayoutGroup->setVisible(showFlag); +} + +/** + * Called when an item is selected from the construction menu + * + * @param action + * Action that was selected. + */ +void +TileTabElementWidgets::constructionMenuTriggered(QAction* action) +{ + if (action != NULL) { + const EventTileTabsConfigurationModification::Operation operation + = static_cast(action->data().toInt()); + + /* + * This switch is here so that it will cause a compilation error + * if the operations are changed. + */ + switch (operation) { + case EventTileTabsConfigurationModification::Operation::DELETE: + break; + case EventTileTabsConfigurationModification::Operation::DUPLICATE_AFTER: + break; + case EventTileTabsConfigurationModification::Operation::DUPLICATE_BEFORE: + break; + case EventTileTabsConfigurationModification::Operation::MOVE_AFTER: + break; + case EventTileTabsConfigurationModification::Operation::MOVE_BEFORE: + break; + } + + EventTileTabsConfigurationModification modification(m_index, + m_rowColumnType, + operation); + emit modificationRequested(modification); + } + +} + +/** + * Called when construction menu is about to show. + */ +void +TileTabElementWidgets::constructionMenuAboutToShow() +{ +} + + +/** + * Called when content type combo box changed. + */ +void +TileTabElementWidgets::contentTypeActivated() +{ + if (m_element != NULL) { + m_element->setContentType(m_contentTypeComboBox->getSelectedItem()); + emit itemChanged(); + } +} + +/** + * Called when stretch type combo box changed. + */ +void +TileTabElementWidgets::stretchTypeActivated() +{ + if (m_element != NULL) { + m_element->setStretchType(m_contentTypeComboBox->getSelectedItem()); + emit itemChanged(); + } +} + +/** + * Called when stretch value changed. + */ +void +TileTabElementWidgets::stretchValueChanged(double) +{ + if (m_element != NULL) { + switch (m_element->getStretchType()) { + case TileTabsRowColumnStretchTypeEnum::PERCENT: + m_element->setPercentStretch(m_stretchValueSpinBox->value()); + break; + case TileTabsRowColumnStretchTypeEnum::WEIGHT: + m_element->setWeightStretch(m_stretchValueSpinBox->value()); + break; + } + emit itemChanged(); + } +} diff --git a/src/GuiQt/TileTabsConfigurationDialog.h b/src/GuiQt/TileTabsConfigurationDialog.h index 2beaf7906..10cee7178 100644 --- a/src/GuiQt/TileTabsConfigurationDialog.h +++ b/src/GuiQt/TileTabsConfigurationDialog.h @@ -22,22 +22,31 @@ /*LICENSE_END*/ #include "EventListenerInterface.h" +#include "EventTileTabsConfigurationModification.h" +#include "TileTabsRowColumnContentTypeEnum.h" +#include "TileTabsRowColumnStretchTypeEnum.h" #include "WuQDialogNonModal.h" class QDoubleSpinBox; +class QGridLayout; class QLabel; class QLineEdit; class QListWidgetItem; class QPushButton; class QRadioButton; class QSpinBox; +class QToolButton; namespace caret { class BrainBrowserWindow; class BrainBrowserWindowComboBox; class BrowserWindowContent; class CaretPreferences; + class EnumComboBoxTemplate; class TileTabsConfiguration; + class TileTabElementWidgets; + class TileTabsRowColumnElement; + class WuQGridLayoutGroup; class WuQListWidget; class TileTabsConfigurationDialog : public WuQDialogNonModal, public EventListenerInterface { @@ -83,6 +92,9 @@ namespace caret { void automaticCustomButtonClicked(QAbstractButton*); + void tileTabsModificationRequested(EventTileTabsConfigurationModification& modification); + + protected: void focusGained(); @@ -105,7 +117,13 @@ namespace caret { QWidget* createActiveConfigurationWidget(); - QWidget* createCustomConfigurationWidget(); + QWidget* createRowColumnStretchWidget(); + + void updateRowColumnStretchWidgets(TileTabsConfiguration* configuration); + + void addRowColumnStretchWidget(const EventTileTabsConfigurationModification::RowColumnType rowColumnType, + QGridLayout* gridLayout, + std::vector& elementVector); void updateStretchFactors(); @@ -117,10 +135,6 @@ namespace caret { BrowserWindowContent* getBrowserWindowContent(); - void updatePercentageLabels(const std::vector& factorSpinBoxes, - std::vector& percentageLabels, - const int32_t validCount); - BrainBrowserWindowComboBox* m_browserWindowComboBox; QWidget* m_customConfigurationWidget; @@ -145,17 +159,13 @@ namespace caret { QSpinBox* m_numberOfColumnsSpinBox; - std::vector m_rowStretchFactorIndexLabels; - - std::vector m_rowStretchFactorSpinBoxes; + std::vector m_columnElements; - std::vector m_rowStretchPercentageLabels; + std::vector m_rowElements; - std::vector m_columnStretchFactorIndexLabels; + QGridLayout* m_rowElementsGridLayout = NULL; - std::vector m_columnStretchFactorSpinBoxes; - - std::vector m_columnStretchPercentageLabels; + QGridLayout* m_columnElementsGridLayout = NULL; /** Blocks reading of preferences since that may invalidate data pointers */ bool m_blockReadConfigurationsFromPreferences; @@ -170,6 +180,63 @@ namespace caret { static const int32_t s_maximumRowsColumns = 50; }; + + /** + * Contains widgets for one row or column of stretching. + */ + class TileTabElementWidgets : public QObject { + Q_OBJECT + + public: + TileTabElementWidgets(const EventTileTabsConfigurationModification::RowColumnType rowColumnType, + const int32_t index, + QGridLayout* gridLayout, + QObject* parent); + + virtual ~TileTabElementWidgets(); + + void updateContent(TileTabsRowColumnElement* element); + + signals: + void itemChanged(); + + void modificationRequested(EventTileTabsConfigurationModification& modification); + + private slots: + void constructionMenuAboutToShow(); + + void constructionMenuTriggered(QAction*); + + void contentTypeActivated(); + + void stretchTypeActivated(); + + void stretchValueChanged(double); + + private: + QMenu* createConstructionMenu(QToolButton* toolButton); + + const EventTileTabsConfigurationModification::RowColumnType m_rowColumnType; + const int32_t m_index; + TileTabsRowColumnElement* m_element; + + QLabel* m_indexLabel; + QAction* m_constructionAction; + QToolButton* m_constructionToolButton; + EnumComboBoxTemplate* m_contentTypeComboBox; + EnumComboBoxTemplate* m_stretchTypeComboBox; + QDoubleSpinBox* m_stretchValueSpinBox; + + QAction* m_menuDeleteAction; + QAction* m_menuDuplicateAfterAction; + QAction* m_menuDuplicateBeforeAction; + QAction* m_menuMoveAfterAction; + QAction* m_menuMoveBeforeAction; + + WuQGridLayoutGroup* m_gridLayoutGroup; + + }; + #ifdef __TILE_TABS_CONFIGURATION_DIALOG_DECLARE__ // const AString TileTabsConfigurationDialog::s_automaticConfigurationPrefix = "Automatic Configuration"; #endif // __TILE_TABS_CONFIGURATION_DIALOG_DECLARE__ diff --git a/src/Operations/OperationShowScene.cxx b/src/Operations/OperationShowScene.cxx index d53509307..d6efe141f 100644 --- a/src/Operations/OperationShowScene.cxx +++ b/src/Operations/OperationShowScene.cxx @@ -465,7 +465,7 @@ OperationShowScene::useParameters(OperationParameters* myParams, TileTabsConfiguration* tileTabsConfiguration = bwc->getSelectedTileTabsConfiguration(); CaretAssert(tileTabsConfiguration); - + const std::vector tabIndices = bwc->getSceneTabIndices(); if ( ! tabIndices.empty()) { std::vector allTabContent; -- GitLab From 431eddead60435357363160ef131963b27692907 Mon Sep 17 00:00:00 2001 From: John Harwell Date: Tue, 16 Oct 2018 16:01:25 -0500 Subject: [PATCH 080/427] WB-668 Titling columns in tile tabs: Implemented percentage width/heights for tile tabs. --- src/Brain/BrainOpenGLViewportContent.cxx | 4 +- src/Common/TileTabsConfiguration.cxx | 97 +++++++++++++++-------- src/GuiQt/TileTabsConfigurationDialog.cxx | 4 +- 3 files changed, 68 insertions(+), 37 deletions(-) diff --git a/src/Brain/BrainOpenGLViewportContent.cxx b/src/Brain/BrainOpenGLViewportContent.cxx index 699840d9e..94044e172 100644 --- a/src/Brain/BrainOpenGLViewportContent.cxx +++ b/src/Brain/BrainOpenGLViewportContent.cxx @@ -711,8 +711,8 @@ BrainOpenGLViewportContent::createViewportContentForTileTabs(std::vector= 0); - CaretAssert(windowExtraHeight >= 0); +// CaretAssert(windowExtraWidth >= 0); +// CaretAssert(windowExtraHeight >= 0); /* * Set the X and Y-coordinates for the tab viewports diff --git a/src/Common/TileTabsConfiguration.cxx b/src/Common/TileTabsConfiguration.cxx index 88f19c168..65b5e2fa8 100644 --- a/src/Common/TileTabsConfiguration.cxx +++ b/src/Common/TileTabsConfiguration.cxx @@ -274,27 +274,41 @@ TileTabsConfiguration::getRowHeightsAndColumnWidthsForWindowSize(const int32_t w /* * Determine height of each row */ + float rowPercentTotal = 0.0; float rowStretchTotal = 0.0; for (int32_t i = 0; i < numRows; i++) { const TileTabsRowColumnElement* e = getRow(i); switch (e->getStretchType()) { case TileTabsRowColumnStretchTypeEnum::PERCENT: + rowPercentTotal += (e->getPercentStretch() / 100.0); break; case TileTabsRowColumnStretchTypeEnum::WEIGHT: rowStretchTotal += e->getWeightStretch(); break; } } - CaretAssert(rowStretchTotal > 0.0); + + float windowWeightHeight = windowHeight; + if (rowPercentTotal > 0.0) { + if (rowPercentTotal >= 1.0) { + windowWeightHeight = 0.0; + } + else { + windowWeightHeight = (1.0 - rowPercentTotal) * windowHeight; + } + } for (int32_t i = 0; i < numRows; i++) { int32_t h = 0; const TileTabsRowColumnElement* e = getRow(i); switch (e->getStretchType()) { case TileTabsRowColumnStretchTypeEnum::PERCENT: + h = (e->getPercentStretch() / 100.0) * windowHeight; break; case TileTabsRowColumnStretchTypeEnum::WEIGHT: - h = static_cast((e->getWeightStretch() / rowStretchTotal) - * windowHeight); + if (rowStretchTotal > 0.0) { + h = static_cast((e->getWeightStretch() / rowStretchTotal) + * windowWeightHeight); + } break; } @@ -304,27 +318,42 @@ TileTabsConfiguration::getRowHeightsAndColumnWidthsForWindowSize(const int32_t w /* * Determine width of each column */ + float columnPercentTotal = 0.0; float columnStretchTotal = 0.0; for (int32_t i = 0; i < numCols; i++) { const TileTabsRowColumnElement* e = getColumn(i); switch (e->getStretchType()) { case TileTabsRowColumnStretchTypeEnum::PERCENT: + columnPercentTotal += (e->getPercentStretch() / 100.0); break; case TileTabsRowColumnStretchTypeEnum::WEIGHT: columnStretchTotal += e->getWeightStretch(); break; } } - CaretAssert(columnStretchTotal > 0.0); + + float windowWeightWidth = windowWidth; + if (columnPercentTotal > 0.0) { + if (columnPercentTotal >= 1.0) { + windowWeightWidth = 0.0; + } + else { + windowWeightWidth = (1.0 - columnPercentTotal) * windowWidth; + } + } + for (int32_t i = 0; i < numCols; i++) { int32_t w = 0; const TileTabsRowColumnElement* e = getColumn(i); switch (e->getStretchType()) { case TileTabsRowColumnStretchTypeEnum::PERCENT: + w = (e->getPercentStretch() / 100.0) * windowWidth; break; case TileTabsRowColumnStretchTypeEnum::WEIGHT: - w = static_cast((e->getWeightStretch() / columnStretchTotal) - * windowWidth); + if (columnStretchTotal > 0.0) { + w = static_cast((e->getWeightStretch() / columnStretchTotal) + * windowWeightWidth); + } break; } columnWidthsOut.push_back(w); @@ -334,34 +363,34 @@ TileTabsConfiguration::getRowHeightsAndColumnWidthsForWindowSize(const int32_t w if ((numRows == static_cast(rowHeightsOut.size())) && (numCols == static_cast(columnWidthsOut.size()))) { - /* - * Verify all rows fit within the window - */ - int32_t rowHeightsSum = 0; - for (int32_t i = 0; i < numRows; i++) { - rowHeightsSum += rowHeightsOut[i]; - } - if (rowHeightsSum > windowHeight) { - CaretLogSevere("PROGRAM ERROR: Tile Tabs total row heights exceed window height"); - rowHeightsOut[numRows - 1] -= (rowHeightsSum - windowHeight); - } - - /* - * Adjust width of last column so that it does not extend beyond viewport - */ - int32_t columnWidthsSum = 0; - for (int32_t i = 0; i < numCols; i++) { - columnWidthsSum += columnWidthsOut[i]; - } - if (columnWidthsSum > windowWidth) { - CaretLogSevere("PROGRAM ERROR: Tile Tabs total row heights exceed window height"); - columnWidthsOut[numCols - 1] = columnWidthsSum - windowWidth; - } - - CaretLogFiner("Tile Tabs Row Heights: " - + AString::fromNumbers(rowHeightsOut, ", ")); - CaretLogFiner("Tile Tabs Column Widths: " - + AString::fromNumbers(columnWidthsOut, ", ")); +// /* +// * Verify all rows fit within the window +// */ +// int32_t rowHeightsSum = 0; +// for (int32_t i = 0; i < numRows; i++) { +// rowHeightsSum += rowHeightsOut[i]; +// } +// if (rowHeightsSum > windowHeight) { +// CaretLogSevere("PROGRAM ERROR: Tile Tabs total row heights exceed window height"); +//// rowHeightsOut[numRows - 1] -= (rowHeightsSum - windowHeight); +// } +// +// /* +// * Adjust width of last column so that it does not extend beyond viewport +// */ +// int32_t columnWidthsSum = 0; +// for (int32_t i = 0; i < numCols; i++) { +// columnWidthsSum += columnWidthsOut[i]; +// } +// if (columnWidthsSum > windowWidth) { +// CaretLogSevere("PROGRAM ERROR: Tile Tabs total row heights exceed window height"); +//// columnWidthsOut[numCols - 1] = columnWidthsSum - windowWidth; +// } +// +// CaretLogFiner("Tile Tabs Row Heights: " +// + AString::fromNumbers(rowHeightsOut, ", ")); +// CaretLogFiner("Tile Tabs Column Widths: " +// + AString::fromNumbers(columnWidthsOut, ", ")); return true; } diff --git a/src/GuiQt/TileTabsConfigurationDialog.cxx b/src/GuiQt/TileTabsConfigurationDialog.cxx index 8e6b21185..cd4d1b53c 100644 --- a/src/GuiQt/TileTabsConfigurationDialog.cxx +++ b/src/GuiQt/TileTabsConfigurationDialog.cxx @@ -1158,10 +1158,12 @@ TileTabElementWidgets::updateContent(TileTabsRowColumnElement* element) switch (m_element->getStretchType()) { case TileTabsRowColumnStretchTypeEnum::PERCENT: m_stretchValueSpinBox->setRange(0.0, 100.0); + m_stretchValueSpinBox->setSingleStep(1.0); m_stretchValueSpinBox->setValue(m_element->getPercentStretch()); break; case TileTabsRowColumnStretchTypeEnum::WEIGHT: m_stretchValueSpinBox->setRange(0.0, 1000.0); + m_stretchValueSpinBox->setSingleStep(0.1); m_stretchValueSpinBox->setValue(m_element->getWeightStretch()); break; } @@ -1236,7 +1238,7 @@ void TileTabElementWidgets::stretchTypeActivated() { if (m_element != NULL) { - m_element->setStretchType(m_contentTypeComboBox->getSelectedItem()); + m_element->setStretchType(m_stretchTypeComboBox->getSelectedItem()); emit itemChanged(); } } -- GitLab From 9a7c856cdc364d970c32764fd78661066a420d16 Mon Sep 17 00:00:00 2001 From: John Harwell Date: Wed, 17 Oct 2018 10:24:57 -0500 Subject: [PATCH 081/427] WB-668 Titling columns in tile tabs: Do not draw a tab if it is off the window. Bug fixes for error messages caused by empty tabs. --- src/Brain/BrainOpenGLFixedPipeline.cxx | 51 ++++++++++++++--- src/Brain/BrainOpenGLViewportContent.cxx | 70 +++++++++++------------- 2 files changed, 74 insertions(+), 47 deletions(-) diff --git a/src/Brain/BrainOpenGLFixedPipeline.cxx b/src/Brain/BrainOpenGLFixedPipeline.cxx index a5f15599a..296e24663 100644 --- a/src/Brain/BrainOpenGLFixedPipeline.cxx +++ b/src/Brain/BrainOpenGLFixedPipeline.cxx @@ -587,10 +587,51 @@ BrainOpenGLFixedPipeline::drawModelsImplementation(const int32_t windowIndex, this->checkForOpenGLError(NULL, "At middle of drawModels()"); for (int32_t i = 0; i < static_cast(viewportContents.size()); i++) { + const BrainOpenGLViewportContent* vpContent = viewportContents[i]; + /* + * Don't draw if off-screen + */ + { + int32_t windowViewport[4]; + vpContent->getWindowViewport(windowViewport); + + int32_t tabViewport[4]; + vpContent->getTabViewportBeforeApplyingMargins(tabViewport); + + /* + * Test for tab offscreen to right (tabs flow left-to-right) + */ + if (tabViewport[0] > (windowViewport[0] + windowViewport[2])) { + continue; + } + + /* + * Test for tab offscreen to bottom (tabs flow top-to-bottom) + */ + const int32_t tabTop = tabViewport[1] + tabViewport[3]; + if (tabTop < 0) { + continue; + } + + if (tabViewport[2] <= 0) { + CaretLogSevere("Invalid TAB width=" + + AString::number(tabViewport[2]) + + " for index=" + + AString::number(i)); + continue; + } + if (tabViewport[3] <= 0) { + CaretLogSevere("Invalid TAB height=" + + AString::number(tabViewport[3]) + + " for index=" + + AString::number(i)); + continue; + } + } + /* * Viewport of window. */ - const BrainOpenGLViewportContent* vpContent = viewportContents[i]; setTabViewport(vpContent); glViewport(m_tabViewport[0], m_tabViewport[1], m_tabViewport[2], m_tabViewport[3]); @@ -3021,7 +3062,7 @@ BrainOpenGLFixedPipeline::drawBorder(const BorderDrawInfo& borderDrawInfo) } glPopAttrib(); -}//p-> +} /** @@ -3180,9 +3221,6 @@ BrainOpenGLFixedPipeline::drawSurfaceFoci(Surface* surface) colorLabel->getColor(rgbaFloat); focus->setClassRgba(rgbaFloat); } -// else { -// focus->setClassRgba(rgbaFloat); -// } } focus->getClassRgba(rgbaFloat); break; @@ -3199,9 +3237,6 @@ BrainOpenGLFixedPipeline::drawSurfaceFoci(Surface* surface) colorLabel->getColor(rgbaFloat); focus->setNameRgba(rgbaFloat); } -// else { -// focus->setNameRgba(rgbaFloat); -// } } focus->getNameRgba(rgbaFloat); break; diff --git a/src/Brain/BrainOpenGLViewportContent.cxx b/src/Brain/BrainOpenGLViewportContent.cxx index 94044e172..b249e874b 100644 --- a/src/Brain/BrainOpenGLViewportContent.cxx +++ b/src/Brain/BrainOpenGLViewportContent.cxx @@ -634,8 +634,10 @@ BrainOpenGLViewportContent::createViewportContentForTileTabs(std::vector tabSizeInfoVector; int32_t iTab = 0; for (int32_t iRowFromTop = 0; iRowFromTop < numRows; iRowFromTop++) { + CaretAssertVectorIndex(tabConfigRowHeights, iRowFromTop); const int32_t vpHeight = tabConfigRowHeights[iRowFromTop]; for (int32_t jCol = 0; jCol < numColumns; jCol++) { + CaretAssertVectorIndex(tabConfigColumnWidths, jCol); const int32_t vpWidth = tabConfigColumnWidths[jCol]; if (iTab < numberOfTabs) { CaretAssertVectorIndex(tabContents, @@ -681,46 +683,44 @@ BrainOpenGLViewportContent::createViewportContentForTileTabs(std::vector(tabSizeInfoVector.size()); + for (int32_t jCol = 0; jCol < numColumns; jCol++) { + CaretAssertVectorIndex(columnWidths, jCol); + if (columnWidths[jCol] <= 0) { + CaretAssertVectorIndex(tabConfigColumnWidths, jCol); + columnWidths[jCol] = tabConfigColumnWidths[jCol]; + } + } /* - * Now that we know the height of each row, and width of each column, - * we can get the total width and height of ALL tab viewports. + * Note: There may be more tabs than there are cells (rows * columns) + * so some tabs may not be displayed. */ - const int32_t allTabsHeight = std::accumulate(rowHeights.begin(), rowHeights.end(), 0); - const int32_t allTabsWidth = std::accumulate(columnWidths.begin(), columnWidths.end(), 0); + const int32_t numberOfDisplayedTabs = static_cast(tabSizeInfoVector.size()); - /* - * The total width/height of the tabs may be less than the size - * of the window viewport. We want to center the tabs inside of - * the window viewport so find any extra space in the window. - */ - const int32_t windowExtraWidth = windowWidth - allTabsWidth; - const int32_t windowExtraHeight = windowHeight - allTabsHeight; -// CaretAssert(windowExtraWidth >= 0); -// CaretAssert(windowExtraHeight >= 0); - /* * Set the X and Y-coordinates for the tab viewports * We start at the bottom row, left corner. */ - int32_t vpY = windowViewport[1] + (windowExtraHeight / 2); - for (int32_t iRow = (numRows - 1); iRow >= 0; iRow--) { - int32_t vpX = windowViewport[0] + (windowExtraWidth / 2); + int32_t vpY = windowHeight; + for (int32_t iRow = 0; iRow < numRows; iRow++) { + CaretAssertVectorIndex(rowHeights, iRow); + vpY -= rowHeights[iRow]; + int32_t vpX = windowViewport[0]; for (int32_t jCol = 0; jCol < numColumns; jCol++) { TileTabsViewportSizingInfo* tabSizePtr = NULL; for (int32_t iTab = 0; iTab < numberOfDisplayedTabs; iTab++) { @@ -735,14 +735,8 @@ BrainOpenGLViewportContent::createViewportContentForTileTabs(std::vectorm_width; - const int32_t tabExtraHeight = rowHeights[iRow] - tabSizePtr->m_height; - - const int32_t tabX = vpX + (tabExtraWidth / 2); - const int32_t tabY = vpY + (tabExtraHeight / 2); + const int32_t tabX = vpX; + const int32_t tabY = vpY; const int tabViewport[4] = { tabX, @@ -795,8 +789,6 @@ BrainOpenGLViewportContent::createViewportContentForTileTabs(std::vectorgetSurfaceMontageViewportsForTransformation(m_browserTabContent->getTabNumber(), montageViewports); - const int x = montageX; // + m_tabX; - const int y = montageY; // + m_tabY; + const int x = montageX; + const int y = montageY; for (std::vector::const_iterator iter = montageViewports.begin(); iter != montageViewports.end(); -- GitLab From 203c65094c36ec2597d2dd0ce42f03f62825ba7e Mon Sep 17 00:00:00 2001 From: John Harwell Date: Wed, 17 Oct 2018 12:26:31 -0500 Subject: [PATCH 082/427] WB-668 Titling columns in tile tabs: Corrections for adjustments of tab and window viewport positioning. --- src/Brain/BrainOpenGLViewportContent.cxx | 88 +++++++++++------------- 1 file changed, 41 insertions(+), 47 deletions(-) diff --git a/src/Brain/BrainOpenGLViewportContent.cxx b/src/Brain/BrainOpenGLViewportContent.cxx index b249e874b..e04fb66a7 100644 --- a/src/Brain/BrainOpenGLViewportContent.cxx +++ b/src/Brain/BrainOpenGLViewportContent.cxx @@ -589,6 +589,8 @@ BrainOpenGLViewportContent::createViewportContentForTileTabs(std::vector tabConfigRowHeights; - std::vector tabConfigColumnWidths; + std::vector rowHeights; + std::vector columnWidths; TileTabsConfiguration* tileTabsConfiguration = browserWindowContent->getSelectedTileTabsConfiguration(); tileTabsConfiguration->getRowHeightsAndColumnWidthsForWindowSize(windowWidth, windowHeight, numberOfTabs, browserWindowContent->getTileTabsConfigurationMode(), - tabConfigRowHeights, - tabConfigColumnWidths); + rowHeights, + columnWidths); - const int32_t numRows = static_cast(tabConfigRowHeights.size()); - const int32_t numColumns = static_cast(tabConfigColumnWidths.size()); + const int32_t numRows = static_cast(rowHeights.size()); + const int32_t numColumns = static_cast(columnWidths.size()); const int32_t numCells = numRows * numColumns; if (numCells <= 0) { return viewportContentsOut; @@ -616,15 +618,6 @@ BrainOpenGLViewportContent::createViewportContentForTileTabs(std::vector 0); CaretAssert(numColumns > 0); - /* - * Due to aspect ratios, the width or height - * of tab viewports may shrink so we will - * need to recompute the row heights and column - * widths. - */ - std::vector rowHeights(numRows, 0); - std::vector columnWidths(numColumns, 0); - const bool allTabsAspectLockedFlag = browserWindowContent->isAllTabsInWindowAspectRatioLocked(); /* @@ -634,11 +627,12 @@ BrainOpenGLViewportContent::createViewportContentForTileTabs(std::vector tabSizeInfoVector; int32_t iTab = 0; for (int32_t iRowFromTop = 0; iRowFromTop < numRows; iRowFromTop++) { - CaretAssertVectorIndex(tabConfigRowHeights, iRowFromTop); - const int32_t vpHeight = tabConfigRowHeights[iRowFromTop]; + CaretAssertVectorIndex(rowHeights, iRowFromTop); + + const int32_t vpHeight = rowHeights[iRowFromTop]; for (int32_t jCol = 0; jCol < numColumns; jCol++) { - CaretAssertVectorIndex(tabConfigColumnWidths, jCol); - const int32_t vpWidth = tabConfigColumnWidths[jCol]; + CaretAssertVectorIndex(columnWidths, jCol); + const int32_t vpWidth = columnWidths[jCol]; if (iTab < numberOfTabs) { CaretAssertVectorIndex(tabContents, iTab); @@ -675,37 +669,13 @@ BrainOpenGLViewportContent::createViewportContentForTileTabs(std::vectorm_width; + if (extraWidth > 1) { + const int32_t halfExtraWidth = extraWidth / 2; + tabX += halfExtraWidth; + } + + /* + * Adjust Tab's Y-coord so centered in tab + * when lock aspect causes tab's height to be smaller + * than the row's height + */ + CaretAssertVectorIndex(rowHeights, iRow); + const int32_t extraHeight = rowHeights[iRow] - tabSizePtr->m_height; + if (extraHeight > 1) { + const int32_t halfExtraHeight = extraHeight / 2; + tabY += halfExtraHeight; + } const int tabViewport[4] = { tabX, -- GitLab From 5a4555543e853b8088e4dc7e46443655b13ef806 Mon Sep 17 00:00:00 2001 From: John Harwell Date: Thu, 18 Oct 2018 14:31:55 -0500 Subject: [PATCH 083/427] WB-668 Titling columns in tile tabs: Added new "SpacerTabContent" that occupies tile tabs configuration spacer rows and columns. --- src/Brain/BrainOpenGLViewportContent.cxx | 183 ++++++++++++++++------- src/Brain/BrainOpenGLViewportContent.h | 11 +- src/Brain/BrowserTabContent.cxx | 9 +- src/Brain/BrowserTabContent.h | 7 +- src/Brain/BrowserWindowContent.cxx | 2 +- src/Brain/CMakeLists.txt | 6 + src/Brain/EventSpacerTabGet.cxx | 116 ++++++++++++++ src/Brain/EventSpacerTabGet.h | 76 ++++++++++ src/Brain/SessionManager.cxx | 53 ++++++- src/Brain/SessionManager.h | 7 + src/Brain/SpacerTabContent.cxx | 156 +++++++++++++++++++ src/Brain/SpacerTabContent.h | 84 +++++++++++ src/Brain/TabContentBase.cxx | 64 ++++++++ src/Brain/TabContentBase.h | 63 ++++++++ src/Common/EventManager.cxx | 1 + src/Common/EventTypeEnum.cxx | 4 + src/Common/EventTypeEnum.h | 2 + src/Common/TileTabsConfiguration.cxx | 2 +- src/Common/TileTabsConfiguration.h | 2 +- src/GuiQt/BrainOpenGLWidget.cxx | 1 + src/Operations/OperationShowScene.cxx | 1 + 21 files changed, 789 insertions(+), 61 deletions(-) create mode 100644 src/Brain/EventSpacerTabGet.cxx create mode 100644 src/Brain/EventSpacerTabGet.h create mode 100644 src/Brain/SpacerTabContent.cxx create mode 100644 src/Brain/SpacerTabContent.h create mode 100644 src/Brain/TabContentBase.cxx create mode 100644 src/Brain/TabContentBase.h diff --git a/src/Brain/BrainOpenGLViewportContent.cxx b/src/Brain/BrainOpenGLViewportContent.cxx index e04fb66a7..a74616c32 100644 --- a/src/Brain/BrainOpenGLViewportContent.cxx +++ b/src/Brain/BrainOpenGLViewportContent.cxx @@ -34,9 +34,11 @@ #include "CaretLogger.h" #include "EventBrowserWindowContent.h" #include "EventManager.h" +#include "EventSpacerTabGet.h" #include "GapsAndMargins.h" #include "MathFunctions.h" #include "ModelSurfaceMontage.h" +#include "SpacerTabContent.h" #include "SurfaceMontageConfigurationAbstract.h" #include "TileTabsConfiguration.h" @@ -68,18 +70,22 @@ using namespace caret; * Tile Tabs mode so user knows graphics region corresponding * to the tab that was recently selected). * @param browserTabContent - * Tab's content that is being drawn. + * Browser Tab content that is being drawn (if not NULL) + * @param spacerTabContent + * Spacer Tab content that is being drawn (if not NULL) */ BrainOpenGLViewportContent::BrainOpenGLViewportContent(const int windowViewport[4], const int tabViewport[4], const int modelViewport[4], const int windowIndex, const bool highlightTabFlag, - BrowserTabContent* browserTabContent) + BrowserTabContent* browserTabContent, + SpacerTabContent* spacerTabContent) : CaretObject(), m_windowIndex(windowIndex), m_highlightTab(highlightTabFlag), -m_browserTabContent(browserTabContent) +m_browserTabContent(browserTabContent), +m_spacerTabContent(spacerTabContent) { m_windowX = windowViewport[0]; m_windowY = windowViewport[1]; @@ -417,7 +423,7 @@ BrainOpenGLViewportContent::getWindowIndex() const } /** - * @return Pointer to tab content in viewport. + * @return Pointer to browser tab content in viewport (NULL if no browser tab in viewport) */ BrowserTabContent* BrainOpenGLViewportContent::getBrowserTabContent() const @@ -425,6 +431,15 @@ BrainOpenGLViewportContent::getBrowserTabContent() const return m_browserTabContent; } +/** + * @return Pointer to spacer tab content in viewport (NULL if no spacer tab in viewport) + */ +SpacerTabContent* +BrainOpenGLViewportContent::getSpacerTabContent() const +{ + return m_spacerTabContent; +} + /** * @return Index of browser tab or -1 if there is not browser tab for this viewport. */ @@ -551,7 +566,8 @@ BrainOpenGLViewportContent::createViewportForSingleTab(std::vectorgetRow(iRowFromTop)->getContentType(); + switch (rowContentType) { + case TileTabsRowColumnContentTypeEnum::SPACE: + spacerTabFlag = true; + break; + case TileTabsRowColumnContentTypeEnum::TAB: + break; + } + + const TileTabsRowColumnContentTypeEnum::Enum tabContentType = tileTabsConfiguration->getColumn(jCol)->getContentType(); + switch (tabContentType) { + case TileTabsRowColumnContentTypeEnum::SPACE: + spacerTabFlag = true; + break; + case TileTabsRowColumnContentTypeEnum::TAB: + break; + } + CaretAssertVectorIndex(columnWidths, jCol); const int32_t vpWidth = columnWidths[jCol]; - if (iTab < numberOfTabs) { + + if (spacerTabFlag) { + EventSpacerTabGet spacerTabEvent(windowIndex, iRowFromTop, jCol); + EventManager::get()->sendEvent(spacerTabEvent.getPointer()); + SpacerTabContent* spacerTabContent = spacerTabEvent.getSpacerTabContent(); + if (spacerTabContent != NULL) { + BrowserTabContent* invalidBrowserTab(NULL); + TileTabsViewportSizingInfo tsi(invalidBrowserTab, + spacerTabContent, + iRowFromTop, + jCol, + vpWidth, + vpHeight); + tabSizeInfoVector.push_back(tsi); + } + else { + AString msg("Failed to get SpacerTabContent for windowIndex=%1, row=%2, column=%3"); + msg = msg.arg(windowIndex).arg(iRowFromTop).arg(jCol); + CaretLogSevere(msg); + } + } + else if (iTab < numberOfTabs) { CaretAssertVectorIndex(tabContents, iTab); @@ -663,11 +722,13 @@ BrainOpenGLViewportContent::createViewportContentForTileTabs(std::vectorm_browserTabContent->getTabNumber(); + int32_t tabIndex = -1; + bool highlightTabFlag = false; + if (tabSizePtr->m_browserTabContent != NULL) { + tabIndex = tabSizePtr->m_browserTabContent->getTabNumber(); + highlightTabFlag = (highlightTabIndex == tabIndex); + } int modelViewport[4] = { 0, 0, 0, 0 }; createModelViewport(tabViewport, tabIndex, gapsAndMargins, modelViewport); - - //tabSizePtr->print(tabX, tabY); BrainOpenGLViewportContent* vpContent = new BrainOpenGLViewportContent(windowViewport, tabViewport, modelViewport, browserWindowContent->getWindowIndex(), - (highlightTabIndex ==tabIndex), - tabSizePtr->m_browserTabContent); + highlightTabFlag, + tabSizePtr->m_browserTabContent, + tabSizePtr->m_spacerTabContent); viewportContentsOut.push_back(vpContent); } else { @@ -776,6 +842,7 @@ BrainOpenGLViewportContent::createViewportContentForTileTabs(std::vectorgetWindowIndex(), false, + NULL, NULL); viewportContentsOut.push_back(vpContent); } @@ -816,33 +883,35 @@ BrainOpenGLViewportContent::createModelViewport(const int tabViewport[4], modelViewportOut[2] = tabViewport[2]; modelViewportOut[3] = tabViewport[3]; - if (gapsAndMargins != NULL) { - gapsAndMargins->getMarginsInPixelsForDrawing(tabIndex, - tabViewport[2], - tabViewport[3], - leftMargin, - rightMargin, - bottomMargin, - topMargin); - const int32_t marginHorizSize = (leftMargin + rightMargin); - const int32_t marginVertSize = (bottomMargin + topMargin); - if ((marginHorizSize < modelViewportOut[2]) - && (marginVertSize < modelViewportOut[3])) { - modelViewportOut[0] += leftMargin; - modelViewportOut[1] += bottomMargin; - modelViewportOut[2] -= marginHorizSize; - modelViewportOut[3] -= marginVertSize; - } - else { - CaretLogSevere("Margins are too big for tab " - + AString::number(tabIndex + 1) - + " viewport. Viewport (x,y,w,h)=" - + AString::fromNumbers(modelViewportOut, 4, ",") - + " margin (l,r,b,t)=" - + AString::number(leftMargin) + "," - + AString::number(rightMargin) + "," - + AString::number(bottomMargin) + "," - + AString::number(topMargin)); + if (tabIndex >= 0) { + if (gapsAndMargins != NULL) { + gapsAndMargins->getMarginsInPixelsForDrawing(tabIndex, + tabViewport[2], + tabViewport[3], + leftMargin, + rightMargin, + bottomMargin, + topMargin); + const int32_t marginHorizSize = (leftMargin + rightMargin); + const int32_t marginVertSize = (bottomMargin + topMargin); + if ((marginHorizSize < modelViewportOut[2]) + && (marginVertSize < modelViewportOut[3])) { + modelViewportOut[0] += leftMargin; + modelViewportOut[1] += bottomMargin; + modelViewportOut[2] -= marginHorizSize; + modelViewportOut[3] -= marginVertSize; + } + else { + CaretLogSevere("Margins are too big for tab " + + AString::number(tabIndex + 1) + + " viewport. Viewport (x,y,w,h)=" + + AString::fromNumbers(modelViewportOut, 4, ",") + + " margin (l,r,b,t)=" + + AString::number(leftMargin) + "," + + AString::number(rightMargin) + "," + + AString::number(bottomMargin) + "," + + AString::number(topMargin)); + } } } } @@ -924,11 +993,13 @@ BrainOpenGLViewportContent::getSurfaceMontageModelViewport(const int32_t montage * Initial height of the tab prior to application of aspect ratio. */ BrainOpenGLViewportContent::TileTabsViewportSizingInfo::TileTabsViewportSizingInfo(BrowserTabContent* browserTabContent, + SpacerTabContent* spacerTabContent, const int32_t rowIndexFromTop, const int32_t columnIndex, const float initialWidth, const float initialHeight) : m_browserTabContent(browserTabContent), +m_spacerTabContent(spacerTabContent), m_rowIndexFromTop(rowIndexFromTop), m_columnIndex(columnIndex), m_initialWidth(initialWidth), @@ -936,12 +1007,14 @@ m_initialHeight(initialHeight), m_width(initialWidth), m_height(initialHeight) { - if (browserTabContent->isAspectRatioLocked()) { - const float aspectRatio = browserTabContent->getAspectRatio(); - if (aspectRatio > 0.0) { - BrainOpenGLViewportContent::adjustWidthHeightForAspectRatio(aspectRatio, - m_width, - m_height); + if (m_browserTabContent != NULL) { + if (m_browserTabContent->isAspectRatioLocked()) { + const float aspectRatio = m_browserTabContent->getAspectRatio(); + if (aspectRatio > 0.0) { + BrainOpenGLViewportContent::adjustWidthHeightForAspectRatio(aspectRatio, + m_width, + m_height); + } } } } @@ -956,6 +1029,7 @@ BrainOpenGLViewportContent::TileTabsViewportSizingInfo::operator=(const TileTabs { if (this != &obj) { m_browserTabContent = obj.m_browserTabContent; + m_spacerTabContent = obj.m_spacerTabContent; m_rowIndexFromTop = obj.m_rowIndexFromTop; m_columnIndex = obj.m_columnIndex; m_initialWidth = obj.m_initialWidth; @@ -979,7 +1053,14 @@ void BrainOpenGLViewportContent::TileTabsViewportSizingInfo::print(const int32_t x, const int32_t y) { - const QString msg("Model: " + m_browserTabContent->getTabName() + AString name; + if (m_browserTabContent != NULL) { + name = m_browserTabContent->getTabName(); + } + else if (m_spacerTabContent != NULL) { + name = m_spacerTabContent->getTabName(); + } + const QString msg("Model: " + name + "\n row/col: " + QString::number(m_rowIndexFromTop) + ", " + QString::number(m_columnIndex) + "\n x/y: " + QString::number(x) + ", " + QString::number(y) + "\n width/height: " + QString::number(m_width) + ", " + QString::number(m_height)); diff --git a/src/Brain/BrainOpenGLViewportContent.h b/src/Brain/BrainOpenGLViewportContent.h index a0baf3d65..c8def2508 100644 --- a/src/Brain/BrainOpenGLViewportContent.h +++ b/src/Brain/BrainOpenGLViewportContent.h @@ -31,6 +31,7 @@ namespace caret { class BrowserTabContent; class BrowserWindowContent; class GapsAndMargins; + class SpacerTabContent; class TileTabsConfiguration; class BrainOpenGLViewportContent : public CaretObject { @@ -64,6 +65,8 @@ namespace caret { BrowserTabContent* getBrowserTabContent() const; + SpacerTabContent* getSpacerTabContent() const; + int32_t getTabIndex() const; bool isTabHighlighted() const; @@ -79,6 +82,7 @@ namespace caret { BrowserWindowContent* browserWindowContent, const GapsAndMargins* gapsAndMargins, const int32_t windowViewport[4], + const int32_t windowIndex, const int32_t highlightTabIndex); static BrainOpenGLViewportContent* createViewportForSingleTab(std::vector& allTabContents, @@ -104,6 +108,7 @@ namespace caret { class TileTabsViewportSizingInfo { public: TileTabsViewportSizingInfo(BrowserTabContent* browserTabContent, + SpacerTabContent* spacerTabContent, const int32_t rowIndexFromTop, const int32_t columnIndex, const float initialWidth, @@ -115,6 +120,7 @@ namespace caret { const int32_t y); BrowserTabContent* m_browserTabContent; + SpacerTabContent* m_spacerTabContent; int32_t m_rowIndexFromTop; int32_t m_columnIndex; @@ -132,7 +138,8 @@ namespace caret { const int modelViewport[4], const int windowIndex, const bool highlightTabFlag, - BrowserTabContent* browserTabContent); + BrowserTabContent* browserTabContent, + SpacerTabContent* spacerTabContent); void initializeMembersBrainOpenGLViewportContent(); @@ -194,6 +201,8 @@ namespace caret { BrowserTabContent* m_browserTabContent; + SpacerTabContent* m_spacerTabContent; + public: virtual AString toString() const; }; diff --git a/src/Brain/BrowserTabContent.cxx b/src/Brain/BrowserTabContent.cxx index 554163132..3c67fa22b 100644 --- a/src/Brain/BrowserTabContent.cxx +++ b/src/Brain/BrowserTabContent.cxx @@ -57,6 +57,7 @@ #include "EventIdentificationHighlightLocation.h" #include "EventModelGetAll.h" #include "EventManager.h" +#include "EventSpacerTabGet.h" #include "FociFile.h" #include "IdentificationManager.h" #include "LabelFile.h" @@ -100,7 +101,7 @@ using namespace caret; * Number for this tab. */ BrowserTabContent::BrowserTabContent(const int32_t tabNumber) -: CaretObject() +: TabContentBase() { isExecutingConstructor = true; @@ -228,6 +229,8 @@ BrowserTabContent::BrowserTabContent(const int32_t tabNumber) EventTypeEnum::EVENT_IDENTIFICATION_HIGHLIGHT_LOCATION); EventManager::get()->addEventListener(this, EventTypeEnum::EVENT_CARET_MAPPABLE_DATA_FILE_MAPS_VIEWED_IN_OVERLAYS); + EventManager::get()->addEventListener(this, + EventTypeEnum::EVENT_SPACER_TAB_GET); isExecutingConstructor = false; @@ -1399,6 +1402,10 @@ BrowserTabContent::receiveEvent(Event* event) } } } + else if (event->getEventType() == EventTypeEnum::EVENT_SPACER_TAB_GET) { + EventSpacerTabGet* tabEvent = dynamic_cast(event); + CaretAssert(tabEvent); + } } /** diff --git a/src/Brain/BrowserTabContent.h b/src/Brain/BrowserTabContent.h index 78f2cda17..7b1b9e693 100644 --- a/src/Brain/BrowserTabContent.h +++ b/src/Brain/BrowserTabContent.h @@ -31,6 +31,7 @@ #include "ProjectionViewTypeEnum.h" #include "SceneableInterface.h" #include "StructureEnum.h" +#include "TabContentBase.h" #include "VolumeSliceDrawingTypeEnum.h" #include "VolumeSliceInterpolationEdgeEffectsMaskingEnum.h" #include "VolumeSliceProjectionTypeEnum.h" @@ -70,7 +71,7 @@ namespace caret { class WholeBrainSurfaceSettings; /// Maintains content in a brower's tab - class BrowserTabContent : public CaretObject, public EventListenerInterface, public SceneableInterface { + class BrowserTabContent : public TabContentBase, public EventListenerInterface, public SceneableInterface { public: BrowserTabContent(const int32_t tabNumber); @@ -85,7 +86,7 @@ namespace caret { virtual AString toString() const; - AString getTabName() const; + AString getTabName() const override; AString getUserTabName() const; @@ -469,7 +470,7 @@ namespace caret { AString getDefaultName() const; - AString getTabNamePrefix() const; + AString getTabNamePrefix() const override; /** Number of this tab */ int32_t m_tabNumber; diff --git a/src/Brain/BrowserWindowContent.cxx b/src/Brain/BrowserWindowContent.cxx index 3cf7453f6..d2afa5c09 100644 --- a/src/Brain/BrowserWindowContent.cxx +++ b/src/Brain/BrowserWindowContent.cxx @@ -428,7 +428,7 @@ BrowserWindowContent::saveToScene(const SceneAttributes* sceneAttributes, if (m_tileTabsEnabled) { sceneClass->addString("m_customTileTabsConfigurationLatest", - m_customTileTabsConfiguration->encodeInXML("")); + m_customTileTabsConfiguration->encodeInXML()); /* * Add a tile tabs version one so older versions of wb_view diff --git a/src/Brain/CMakeLists.txt b/src/Brain/CMakeLists.txt index 385e4b1c2..704cbf46f 100644 --- a/src/Brain/CMakeLists.txt +++ b/src/Brain/CMakeLists.txt @@ -98,6 +98,7 @@ EventModelSurfaceGet.h EventNodeDataFilesGet.h EventNodeIdentificationColorsGetFromCharts.h EventOverlayValidate.h +EventSpacerTabGet.h EventSpecFileReadDataFiles.h EventSurfacesGet.h FeatureColoringTypeEnum.h @@ -153,6 +154,7 @@ SelectionItemVoxelEditing.h SelectionItemVoxelIdentificationSymbol.h SelectionManager.h SessionManager.h +SpacerTabContent.h Surface.h SurfaceDrawingTypeEnum.h SurfaceMontageConfigurationAbstract.h @@ -164,6 +166,7 @@ SurfaceMontageLayoutOrientationEnum.h SurfaceMontageViewport.h SurfaceNodeColoring.h SurfaceSelectionModel.h +TabContentBase.h ViewingTransformations.h ViewingTransformationsCerebellum.h ViewingTransformationsVolume.h @@ -247,6 +250,7 @@ EventModelSurfaceGet.cxx EventNodeDataFilesGet.cxx EventNodeIdentificationColorsGetFromCharts.cxx EventOverlayValidate.cxx +EventSpacerTabGet.cxx EventSpecFileReadDataFiles.cxx EventSurfacesGet.cxx FeatureColoringTypeEnum.cxx @@ -301,6 +305,7 @@ SelectionItemVoxelIdentificationSymbol.cxx SelectionItemVoxelEditing.cxx SelectionManager.cxx SessionManager.cxx +SpacerTabContent.cxx Surface.cxx SurfaceDrawingTypeEnum.cxx SurfaceMontageConfigurationAbstract.cxx @@ -312,6 +317,7 @@ SurfaceMontageLayoutOrientationEnum.cxx SurfaceMontageViewport.cxx SurfaceNodeColoring.cxx SurfaceSelectionModel.cxx +TabContentBase.cxx ViewingTransformations.cxx ViewingTransformationsCerebellum.cxx ViewingTransformationsVolume.cxx diff --git a/src/Brain/EventSpacerTabGet.cxx b/src/Brain/EventSpacerTabGet.cxx new file mode 100644 index 000000000..9081a16e7 --- /dev/null +++ b/src/Brain/EventSpacerTabGet.cxx @@ -0,0 +1,116 @@ + +/*LICENSE_START*/ +/* + * Copyright (C) 2018 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + +#define __EVENT_SPACER_TAB_GET_DECLARE__ +#include "EventSpacerTabGet.h" +#undef __EVENT_SPACER_TAB_GET_DECLARE__ + +#include "CaretAssert.h" +#include "EventTypeEnum.h" + +using namespace caret; + + + +/** + * \class caret::EventSpacerTabGet + * \brief Event for getting a spacer tag. + * \ingroup Brain + */ + +/** + * Constructor. + * + * @param windowIndex + * Index of the window + * @param rowIndex + * Index of the row + * @param columnIndex + * Index of the column + */ +EventSpacerTabGet::EventSpacerTabGet(const int32_t windowIndex, + const int32_t rowIndex, + const int32_t columnIndex) +: Event(EventTypeEnum::EVENT_SPACER_TAB_GET), +m_windowIndex(windowIndex), +m_rowIndex(rowIndex), +m_columnIndex(columnIndex), +m_spacerTabContent(NULL) +{ + +} + +/** + * Destructor. + */ +EventSpacerTabGet::~EventSpacerTabGet() +{ +} + +/** + * @return The window index. + */ +int32_t +EventSpacerTabGet::getWindowIndex() const +{ + return m_windowIndex; +} + +/** + * @return The row index. + */ +int32_t +EventSpacerTabGet::getRowIndex() const +{ + return m_rowIndex; +} + +/** + * @return The column index. + */ +int32_t +EventSpacerTabGet::getColumnIndex() const +{ + return m_columnIndex; +} + +/** + * @return The spacer tab. + */ +SpacerTabContent* +EventSpacerTabGet::getSpacerTabContent() +{ + return m_spacerTabContent; +} + +/** + * Set the spacer tab content. + * + * @param spacerTabContent + * New value for the spacer tab content. + */ +void +EventSpacerTabGet::setSpacerTabContent(SpacerTabContent* spacerTabContent) +{ + m_spacerTabContent = spacerTabContent; +} + + diff --git a/src/Brain/EventSpacerTabGet.h b/src/Brain/EventSpacerTabGet.h new file mode 100644 index 000000000..d75b52bef --- /dev/null +++ b/src/Brain/EventSpacerTabGet.h @@ -0,0 +1,76 @@ +#ifndef __EVENT_SPACER_TAB_GET_H__ +#define __EVENT_SPACER_TAB_GET_H__ + +/*LICENSE_START*/ +/* + * Copyright (C) 2018 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + + + +#include + +#include "Event.h" + + + +namespace caret { + + class SpacerTabContent; + + class EventSpacerTabGet : public Event { + + public: + EventSpacerTabGet(const int32_t windowIndex, + const int32_t rowIndex, + const int32_t columnIndex); + + virtual ~EventSpacerTabGet(); + + EventSpacerTabGet(const EventSpacerTabGet&) = delete; + + EventSpacerTabGet& operator=(const EventSpacerTabGet&) = delete; + + int32_t getWindowIndex() const; + + int32_t getRowIndex() const; + + int32_t getColumnIndex() const; + + SpacerTabContent* getSpacerTabContent(); + + void setSpacerTabContent(SpacerTabContent* spacerTabContent); + + // ADD_NEW_METHODS_HERE + + private: + const int32_t m_windowIndex; + const int32_t m_rowIndex; + const int32_t m_columnIndex; + + SpacerTabContent* m_spacerTabContent = NULL; + // ADD_NEW_MEMBERS_HERE + + }; + +#ifdef __EVENT_SPACER_TAB_GET_DECLARE__ + // +#endif // __EVENT_SPACER_TAB_GET_DECLARE__ + +} // namespace +#endif //__EVENT_SPACER_TAB_GET_H__ diff --git a/src/Brain/SessionManager.cxx b/src/Brain/SessionManager.cxx index 0c136efc2..72db1d9b2 100644 --- a/src/Brain/SessionManager.cxx +++ b/src/Brain/SessionManager.cxx @@ -50,6 +50,7 @@ #include "EventModelGetAll.h" #include "EventModelGetAllDisplayed.h" #include "EventProgressUpdate.h" +#include "EventSpacerTabGet.h" #include "ImageCaptureSettings.h" #include "LogManager.h" #include "MapYokingGroupEnum.h" @@ -99,6 +100,7 @@ SessionManager::SessionManager() EventManager::get()->addEventListener(this, EventTypeEnum::EVENT_MODEL_DELETE); EventManager::get()->addEventListener(this, EventTypeEnum::EVENT_MODEL_GET_ALL); EventManager::get()->addEventListener(this, EventTypeEnum::EVENT_MODEL_GET_ALL_DISPLAYED); + EventManager::get()->addEventListener(this, EventTypeEnum::EVENT_SPACER_TAB_GET); Brain* brain = new Brain(m_caretPreferences); m_brains.push_back(brain); @@ -121,6 +123,8 @@ SessionManager::~SessionManager() } } + clearSpacerTabs(); + std::for_each(m_browserWindowContent.begin(), m_browserWindowContent.end(), [](BrowserWindowContent* bwc) { if (bwc != NULL) delete bwc; } ); @@ -262,6 +266,18 @@ SessionManager::getBrain(const int32_t brainIndex) const return m_brains[brainIndex]; } +/** + * Clear all of the spacer tabs + */ +void +SessionManager::clearSpacerTabs() +{ + for (auto st : m_spacerTabsMap) { + delete st.second; + } + m_spacerTabsMap.clear(); +} + /** * Get a description of this object's content. * @return String describing this object's content. @@ -445,6 +461,34 @@ SessionManager::receiveEvent(Event* event) getDisplayedModelsEvent->setEventProcessed(); } + else if (event->getEventType() == EventTypeEnum::EVENT_SPACER_TAB_GET) { + EventSpacerTabGet* spacerTabEvent = dynamic_cast(event); + CaretAssert(spacerTabEvent); + + SpacerTabContent* spacerTabContent = NULL; + + SpacerTabContent::MapKey key(spacerTabEvent->getWindowIndex(), + spacerTabEvent->getRowIndex(), + spacerTabEvent->getColumnIndex()); + auto iter = m_spacerTabsMap.find(key); + if (iter != m_spacerTabsMap.end()) { + spacerTabContent = iter->second; + CaretLogFiner("Found Spacer Tab Content: " + + spacerTabContent->getTabName()); + } + else { + spacerTabContent = new SpacerTabContent(spacerTabEvent->getWindowIndex(), + spacerTabEvent->getRowIndex(), + spacerTabEvent->getColumnIndex()); + m_spacerTabsMap.insert(std::make_pair(key, + spacerTabContent)); + CaretLogFiner("Created Spacer Tab Content: " + + spacerTabContent->getTabName()); + } + + CaretAssert(spacerTabContent); + spacerTabEvent->setSpacerTabContent(spacerTabContent); + } } /** @@ -802,6 +846,11 @@ SessionManager::restoreFromScene(const SceneAttributes* sceneAttributes, } } + /* + * Remove all spacer tabs + */ + clearSpacerTabs(); + /* * Restore tabs */ @@ -940,6 +989,8 @@ SessionManager::resetBrains(const bool keepSceneFiles) } } + clearSpacerTabs(); + if (numBrains > 1) { m_brains.resize(1); } @@ -999,8 +1050,6 @@ SessionManager::getDataToolTipsManager() const return m_dataToolTipsManager.get(); } - - /** * @return Image capture settings for image capture dialog. */ diff --git a/src/Brain/SessionManager.h b/src/Brain/SessionManager.h index 219d9804d..9e791c276 100644 --- a/src/Brain/SessionManager.h +++ b/src/Brain/SessionManager.h @@ -22,6 +22,7 @@ /*LICENSE_END*/ #include +#include #include #include "ApplicationTypeEnum.h" @@ -29,6 +30,7 @@ #include "CaretObject.h" #include "EventListenerInterface.h" #include "SceneableInterface.h" +#include "SpacerTabContent.h" namespace caret { @@ -100,6 +102,8 @@ namespace caret { void resetBrains(const bool keepSceneFiles); + void clearSpacerTabs(); + /** The session manager */ static SessionManager* s_singletonSessionManager; @@ -130,6 +134,9 @@ namespace caret { /** Settings for image capture dialog */ ImageCaptureSettings* m_imageCaptureDialogSettings; + /** Map to spacer tabs where key is window index, row index, column index */ + std::map m_spacerTabsMap; + }; #ifdef __SESSION_MANAGER_DECLARE__ diff --git a/src/Brain/SpacerTabContent.cxx b/src/Brain/SpacerTabContent.cxx new file mode 100644 index 000000000..e06ada179 --- /dev/null +++ b/src/Brain/SpacerTabContent.cxx @@ -0,0 +1,156 @@ + +/*LICENSE_START*/ +/* + * Copyright (C) 2018 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + +#define __SPACER_TAB_CONTENT_DECLARE__ +#include "SpacerTabContent.h" +#undef __SPACER_TAB_CONTENT_DECLARE__ + +#include "CaretAssert.h" +using namespace caret; + + + +/** + * \class caret::SpacerTabContent + * \brief Content of a tile tabs 'spacer' + * \ingroup Brain + */ + +/** + * Constructor. + * + * @param windowIndex + * Index of the window. + * @param rowIndex + * Index of the row. + * @param columnIndex + * Index of the column. + */ +SpacerTabContent::SpacerTabContent(const int32_t windowIndex, + const int32_t rowIndex, + const int32_t columnIndex) +: TabContentBase(), +m_key(windowIndex, + rowIndex, + columnIndex) +{ +} + +/** + * Destructor. + */ +SpacerTabContent::~SpacerTabContent() +{ +} + +/** + * @return Name of the tab. + */ +AString +SpacerTabContent::getTabName() const +{ + AString s("Spacer " + + getTabNamePrefix()); + + return s; +} + +/** + * @return Prefix name of the tab. + */ +AString +SpacerTabContent::getTabNamePrefix() const +{ + AString s("%1, %2"); + s = s.arg(m_key.m_rowIndex).arg(m_key.m_columnIndex); + + return s; +} + +/** + * @return The Window index + */ +int32_t +SpacerTabContent::getWindowIndex() const +{ + return m_key.m_windowIndex; +} + +/** + * @return The row index + */ +int32_t +SpacerTabContent::getRowIndex() const +{ + return m_key.m_rowIndex; +} + +/** + * @return The column index + */ +int32_t +SpacerTabContent::getColumnIndex() const +{ + return m_key.m_columnIndex; +} + +/** + * Constructor. + * + * @param windowIndex + * Index of the window. + * @param rowIndex + * Index of the row. + * @param columnIndex + * Index of the column. + */ +SpacerTabContent::MapKey::MapKey(const int32_t windowIndex, + const int32_t rowIndex, + const int32_t columnIndex) +: m_windowIndex(windowIndex), +m_rowIndex(rowIndex), +m_columnIndex(columnIndex) +{ + CaretAssert(m_windowIndex >= 0); + CaretAssert(m_rowIndex >= 0); + CaretAssert(m_columnIndex >= 0); +} + +/** + * Comparison operator. + * + * @return True if 'this' is less than 'rhs', else false. + */ +bool +SpacerTabContent::MapKey::operator<(const MapKey& rhs) const +{ + if (m_windowIndex != rhs.m_windowIndex) { + return (m_windowIndex < rhs.m_windowIndex); + } + if (m_rowIndex != rhs.m_rowIndex) { + return (m_rowIndex < rhs.m_rowIndex); + } + if (m_columnIndex != rhs.m_columnIndex) { + return (m_columnIndex < rhs.m_columnIndex); + } + + return false; +} diff --git a/src/Brain/SpacerTabContent.h b/src/Brain/SpacerTabContent.h new file mode 100644 index 000000000..f84981182 --- /dev/null +++ b/src/Brain/SpacerTabContent.h @@ -0,0 +1,84 @@ +#ifndef __SPACER_TAB_CONTENT_H__ +#define __SPACER_TAB_CONTENT_H__ + +/*LICENSE_START*/ +/* + * Copyright (C) 2018 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + + + +#include + +#include "TabContentBase.h" + + + +namespace caret { + + class SpacerTabContent : public TabContentBase { + + public: + class MapKey { + public: + MapKey(const int32_t windowIndex, + const int32_t rowIndex, + const int32_t columnIndex); + + bool operator<(const MapKey& rhs) const; + + const int32_t m_windowIndex; + const int32_t m_rowIndex; + const int32_t m_columnIndex; + }; + + SpacerTabContent(const int32_t windowIndex, + const int32_t rowIndex, + const int32_t columnIndex); + + virtual ~SpacerTabContent(); + + SpacerTabContent(const SpacerTabContent&) = delete; + + SpacerTabContent& operator=(const SpacerTabContent&) = delete; + + virtual AString getTabName() const override; + + virtual AString getTabNamePrefix() const override; + + int32_t getWindowIndex() const; + + int32_t getRowIndex() const; + + int32_t getColumnIndex() const; + + // ADD_NEW_METHODS_HERE + + private: + MapKey m_key; + + // ADD_NEW_MEMBERS_HERE + + }; + +#ifdef __SPACER_TAB_CONTENT_DECLARE__ + // +#endif // __SPACER_TAB_CONTENT_DECLARE__ + +} // namespace +#endif //__SPACER_TAB_CONTENT_H__ diff --git a/src/Brain/TabContentBase.cxx b/src/Brain/TabContentBase.cxx new file mode 100644 index 000000000..0763f3e31 --- /dev/null +++ b/src/Brain/TabContentBase.cxx @@ -0,0 +1,64 @@ + +/*LICENSE_START*/ +/* + * Copyright (C) 2018 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + +#define __TAB_CONTENT_BASE_DECLARE__ +#include "TabContentBase.h" +#undef __TAB_CONTENT_BASE_DECLARE__ + +#include "CaretAssert.h" +using namespace caret; + + + +/** + * \class caret::TabContentBase + * \brief + * \ingroup Brain + * + * + */ + +/** + * Constructor. + */ +TabContentBase::TabContentBase() +: CaretObject() +{ + +} + +/** + * Destructor. + */ +TabContentBase::~TabContentBase() +{ +} + +/** + * Get a description of this object's content. + * @return String describing this object's content. + */ +AString +TabContentBase::toString() const +{ + return "TabContentBase"; +} + diff --git a/src/Brain/TabContentBase.h b/src/Brain/TabContentBase.h new file mode 100644 index 000000000..ae4f20379 --- /dev/null +++ b/src/Brain/TabContentBase.h @@ -0,0 +1,63 @@ +#ifndef __TAB_CONTENT_BASE_H__ +#define __TAB_CONTENT_BASE_H__ + +/*LICENSE_START*/ +/* + * Copyright (C) 2018 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + + + +#include + +#include "CaretObject.h" + + + +namespace caret { + + class TabContentBase : public CaretObject { + + public: + TabContentBase(); + + virtual ~TabContentBase(); + + TabContentBase(const TabContentBase&) = delete; + + TabContentBase& operator=(const TabContentBase&) = delete; + + virtual AString getTabName() const = 0; + + virtual AString getTabNamePrefix() const = 0; + + // ADD_NEW_METHODS_HERE + + virtual AString toString() const; + + private: + // ADD_NEW_MEMBERS_HERE + + }; + +#ifdef __TAB_CONTENT_BASE_DECLARE__ + // +#endif // __TAB_CONTENT_BASE_DECLARE__ + +} // namespace +#endif //__TAB_CONTENT_BASE_H__ diff --git a/src/Common/EventManager.cxx b/src/Common/EventManager.cxx index ebd80d88e..4507466d6 100644 --- a/src/Common/EventManager.cxx +++ b/src/Common/EventManager.cxx @@ -451,6 +451,7 @@ EventManager::sendSimpleEvent(const EventTypeEnum::Enum eventType) case EventTypeEnum::EVENT_PALETTE_COLOR_MAPPING_EDITOR_SHOW: case EventTypeEnum::EVENT_PALETTE_GET_BY_NAME: case EventTypeEnum::EVENT_SHOW_FILE_DATA_READ_WARNING_DIALOG: + case EventTypeEnum::EVENT_SPACER_TAB_GET: case EventTypeEnum::EVENT_SPEC_FILE_READ_DATA_FILES: case EventTypeEnum::EVENT_SURFACE_COLORING_INVALIDATE: case EventTypeEnum::EVENT_SURFACES_GET: diff --git a/src/Common/EventTypeEnum.cxx b/src/Common/EventTypeEnum.cxx index 8414db2bd..0b2efe1ab 100644 --- a/src/Common/EventTypeEnum.cxx +++ b/src/Common/EventTypeEnum.cxx @@ -346,6 +346,10 @@ EventTypeEnum::initialize() "EVENT_SHOW_FILE_DATA_READ_WARNING_DIALOG", "Show a dialog with warnings encountered reading data files")); + enumData.push_back(EventTypeEnum(EVENT_SPACER_TAB_GET, + "EVENT_SPACER_TAB_GET", + "Get a spacer tagb")); + enumData.push_back(EventTypeEnum(EVENT_SPEC_FILE_READ_DATA_FILES, "EVENT_SPEC_FILE_READ_DATA_FILES", "Read the selected data files in a spec file")); diff --git a/src/Common/EventTypeEnum.h b/src/Common/EventTypeEnum.h index ace16fa46..f4f62f816 100644 --- a/src/Common/EventTypeEnum.h +++ b/src/Common/EventTypeEnum.h @@ -180,6 +180,8 @@ public: EVENT_PALETTE_GET_BY_NAME, /** Show a dialog containing warnings encountered when reading data files */ EVENT_SHOW_FILE_DATA_READ_WARNING_DIALOG, + /** Get a spacer tab by tab number */ + EVENT_SPACER_TAB_GET, /** Read the selected files in a spec file */ EVENT_SPEC_FILE_READ_DATA_FILES, /** Invalidate surface coloring */ diff --git a/src/Common/TileTabsConfiguration.cxx b/src/Common/TileTabsConfiguration.cxx index 65b5e2fa8..a1d7f0ef9 100644 --- a/src/Common/TileTabsConfiguration.cxx +++ b/src/Common/TileTabsConfiguration.cxx @@ -531,7 +531,7 @@ TileTabsConfiguration::updateAutomaticConfigurationRowsAndColumns(const int32_t * @return Encoded tile tabs configuration in XML */ AString -TileTabsConfiguration::encodeInXML(AString s) const +TileTabsConfiguration::encodeInXML() const { return encodeVersionInXML(2); } diff --git a/src/Common/TileTabsConfiguration.h b/src/Common/TileTabsConfiguration.h index 33c3e5278..1b59458a1 100644 --- a/src/Common/TileTabsConfiguration.h +++ b/src/Common/TileTabsConfiguration.h @@ -75,7 +75,7 @@ namespace caret { const TileTabsRowColumnElement* getRow(const int32_t rowIndex) const; - AString encodeInXML(AString s) const; + AString encodeInXML() const; AString encodeVersionInXML(const int32_t versionNumber) const; diff --git a/src/GuiQt/BrainOpenGLWidget.cxx b/src/GuiQt/BrainOpenGLWidget.cxx index 2d783e130..64ce2b086 100644 --- a/src/GuiQt/BrainOpenGLWidget.cxx +++ b/src/GuiQt/BrainOpenGLWidget.cxx @@ -576,6 +576,7 @@ BrainOpenGLWidget::getDrawingWindowContent(const int32_t windowViewportIn[4], browserWindowContent, gapsAndMargins, windowViewport, + this->windowIndex, getModelEvent.getTabIndexForTileTabsHighlighting()); for (auto tabvp : tabViewportContent) { windowContent.addTabViewport(tabvp); diff --git a/src/Operations/OperationShowScene.cxx b/src/Operations/OperationShowScene.cxx index d6efe141f..bc29f0ee9 100644 --- a/src/Operations/OperationShowScene.cxx +++ b/src/Operations/OperationShowScene.cxx @@ -506,6 +506,7 @@ OperationShowScene::useParameters(OperationParameters* myParams, bwc, gapsAndMargins, windowViewport, + windowIndex, tabIndexToHighlight); std::vector constViewports(viewports.begin(), -- GitLab From 7caac14d46ef4511dd7b4a48bc26caeba3083b94 Mon Sep 17 00:00:00 2001 From: John Harwell Date: Thu, 18 Oct 2018 15:13:49 -0500 Subject: [PATCH 084/427] WB-668 Titling columns in tile tabs: Moved SpacerTabContent::MapKey into its own class named "SpacerTabIndex". --- src/Brain/SessionManager.cxx | 11 +- src/Brain/SessionManager.h | 5 +- src/Brain/SpacerTabContent.cxx | 42 ------ src/Brain/SpacerTabContent.h | 16 +- src/Scenes/CMakeLists.txt | 2 + src/Scenes/SpacerTabIndex.cxx | 268 +++++++++++++++++++++++++++++++++ src/Scenes/SpacerTabIndex.h | 105 +++++++++++++ 7 files changed, 386 insertions(+), 63 deletions(-) create mode 100644 src/Scenes/SpacerTabIndex.cxx create mode 100644 src/Scenes/SpacerTabIndex.h diff --git a/src/Brain/SessionManager.cxx b/src/Brain/SessionManager.cxx index 72db1d9b2..00db0f616 100644 --- a/src/Brain/SessionManager.cxx +++ b/src/Brain/SessionManager.cxx @@ -60,6 +60,7 @@ #include "SceneClass.h" #include "SceneClassArray.h" #include "ScenePrimitiveArray.h" +#include "SpacerTabContent.h" #include "VolumeSurfaceOutlineSetModel.h" @@ -467,10 +468,10 @@ SessionManager::receiveEvent(Event* event) SpacerTabContent* spacerTabContent = NULL; - SpacerTabContent::MapKey key(spacerTabEvent->getWindowIndex(), - spacerTabEvent->getRowIndex(), - spacerTabEvent->getColumnIndex()); - auto iter = m_spacerTabsMap.find(key); + SpacerTabIndex spacerTabIndex(spacerTabEvent->getWindowIndex(), + spacerTabEvent->getRowIndex(), + spacerTabEvent->getColumnIndex()); + auto iter = m_spacerTabsMap.find(spacerTabIndex); if (iter != m_spacerTabsMap.end()) { spacerTabContent = iter->second; CaretLogFiner("Found Spacer Tab Content: " @@ -480,7 +481,7 @@ SessionManager::receiveEvent(Event* event) spacerTabContent = new SpacerTabContent(spacerTabEvent->getWindowIndex(), spacerTabEvent->getRowIndex(), spacerTabEvent->getColumnIndex()); - m_spacerTabsMap.insert(std::make_pair(key, + m_spacerTabsMap.insert(std::make_pair(spacerTabIndex, spacerTabContent)); CaretLogFiner("Created Spacer Tab Content: " + spacerTabContent->getTabName()); diff --git a/src/Brain/SessionManager.h b/src/Brain/SessionManager.h index 9e791c276..5f031bbef 100644 --- a/src/Brain/SessionManager.h +++ b/src/Brain/SessionManager.h @@ -30,7 +30,7 @@ #include "CaretObject.h" #include "EventListenerInterface.h" #include "SceneableInterface.h" -#include "SpacerTabContent.h" +#include "SpacerTabIndex.h" namespace caret { @@ -43,6 +43,7 @@ namespace caret { class DataToolTipsManager; class ImageCaptureSettings; class Model; + class SpacerTabContent; /// Manages a Caret session which contains 'global' brain data. class SessionManager : public CaretObject, public EventListenerInterface, public SceneableInterface { @@ -135,7 +136,7 @@ namespace caret { ImageCaptureSettings* m_imageCaptureDialogSettings; /** Map to spacer tabs where key is window index, row index, column index */ - std::map m_spacerTabsMap; + std::map m_spacerTabsMap; }; diff --git a/src/Brain/SpacerTabContent.cxx b/src/Brain/SpacerTabContent.cxx index e06ada179..d227b7468 100644 --- a/src/Brain/SpacerTabContent.cxx +++ b/src/Brain/SpacerTabContent.cxx @@ -112,45 +112,3 @@ SpacerTabContent::getColumnIndex() const return m_key.m_columnIndex; } -/** - * Constructor. - * - * @param windowIndex - * Index of the window. - * @param rowIndex - * Index of the row. - * @param columnIndex - * Index of the column. - */ -SpacerTabContent::MapKey::MapKey(const int32_t windowIndex, - const int32_t rowIndex, - const int32_t columnIndex) -: m_windowIndex(windowIndex), -m_rowIndex(rowIndex), -m_columnIndex(columnIndex) -{ - CaretAssert(m_windowIndex >= 0); - CaretAssert(m_rowIndex >= 0); - CaretAssert(m_columnIndex >= 0); -} - -/** - * Comparison operator. - * - * @return True if 'this' is less than 'rhs', else false. - */ -bool -SpacerTabContent::MapKey::operator<(const MapKey& rhs) const -{ - if (m_windowIndex != rhs.m_windowIndex) { - return (m_windowIndex < rhs.m_windowIndex); - } - if (m_rowIndex != rhs.m_rowIndex) { - return (m_rowIndex < rhs.m_rowIndex); - } - if (m_columnIndex != rhs.m_columnIndex) { - return (m_columnIndex < rhs.m_columnIndex); - } - - return false; -} diff --git a/src/Brain/SpacerTabContent.h b/src/Brain/SpacerTabContent.h index f84981182..565d1b36e 100644 --- a/src/Brain/SpacerTabContent.h +++ b/src/Brain/SpacerTabContent.h @@ -25,6 +25,7 @@ #include +#include "SpacerTabIndex.h" #include "TabContentBase.h" @@ -34,19 +35,6 @@ namespace caret { class SpacerTabContent : public TabContentBase { public: - class MapKey { - public: - MapKey(const int32_t windowIndex, - const int32_t rowIndex, - const int32_t columnIndex); - - bool operator<(const MapKey& rhs) const; - - const int32_t m_windowIndex; - const int32_t m_rowIndex; - const int32_t m_columnIndex; - }; - SpacerTabContent(const int32_t windowIndex, const int32_t rowIndex, const int32_t columnIndex); @@ -70,7 +58,7 @@ namespace caret { // ADD_NEW_METHODS_HERE private: - MapKey m_key; + SpacerTabIndex m_key; // ADD_NEW_MEMBERS_HERE diff --git a/src/Scenes/CMakeLists.txt b/src/Scenes/CMakeLists.txt index ae9c80404..f6d5d8325 100644 --- a/src/Scenes/CMakeLists.txt +++ b/src/Scenes/CMakeLists.txt @@ -69,6 +69,7 @@ SceneWriterInterface.h SceneWriterXml.h SceneXmlElements.h SceneableInterface.h +SpacerTabIndex.h BackgroundAndForegroundColorsSceneHelper.cxx DisplayGroupAndTabItemHelper.cxx @@ -103,6 +104,7 @@ SceneTypeEnum.cxx SceneUnsignedByte.cxx SceneUnsignedByteArray.cxx SceneWriterXml.cxx +SpacerTabIndex.cxx ) TARGET_LINK_LIBRARIES(Scenes ${CARET_QT5_LINK}) diff --git a/src/Scenes/SpacerTabIndex.cxx b/src/Scenes/SpacerTabIndex.cxx new file mode 100644 index 000000000..0b6ba906c --- /dev/null +++ b/src/Scenes/SpacerTabIndex.cxx @@ -0,0 +1,268 @@ + +/*LICENSE_START*/ +/* + * Copyright (C) 2018 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + +#define __SPACER_TAB_INDEX_DECLARE__ +#include "SpacerTabIndex.h" +#undef __SPACER_TAB_INDEX_DECLARE__ + +#include "CaretAssert.h" +#include "SceneClass.h" +#include "SceneClassAssistant.h" + +using namespace caret; + + + +/** + * \class caret::SpacerTabIndex + * \brief Index of a spacer tab in a tile tabs configuration + * \ingroup Scenes + */ + +/** + * Constructor of invalid element. + */ +SpacerTabIndex::SpacerTabIndex() +: CaretObject(), +m_windowIndex(-1), +m_rowIndex(-1), +m_columnIndex(-1) +{ + m_sceneAssistant = std::unique_ptr(new SceneClassAssistant()); + m_sceneAssistant->add("m_windowIndex", + &m_windowIndex); + m_sceneAssistant->add("m_rowIndex", + &m_rowIndex); + m_sceneAssistant->add("m_columnIndex", + &m_columnIndex); +} + +/** + * Constructor with indices + */ +SpacerTabIndex::SpacerTabIndex(const int32_t windowIndex, + const int32_t rowIndex, + const int32_t columnIndex) +: CaretObject(), +m_windowIndex(windowIndex), +m_rowIndex(rowIndex), +m_columnIndex(columnIndex) +{ + m_sceneAssistant = std::unique_ptr(new SceneClassAssistant()); + m_sceneAssistant->add("m_windowIndex", + &m_windowIndex); + m_sceneAssistant->add("m_rowIndex", + &m_rowIndex); + m_sceneAssistant->add("m_columnIndex", + &m_columnIndex); +} + +/** + * Destructor. + */ +SpacerTabIndex::~SpacerTabIndex() +{ +} + +/** + * Copy constructor. + * @param obj + * Object that is copied. + */ +SpacerTabIndex::SpacerTabIndex(const SpacerTabIndex& obj) +: CaretObject(obj) +{ + this->copyHelperSpacerTabIndex(obj); +} + +/** + * Assignment operator. + * @param obj + * Data copied from obj to this. + * @return + * Reference to this object. + */ +SpacerTabIndex& +SpacerTabIndex::operator=(const SpacerTabIndex& obj) +{ + if (this != &obj) { + CaretObject::operator=(obj); + this->copyHelperSpacerTabIndex(obj); + } + return *this; +} + +/** + * Helps with copying an object of this type. + * @param obj + * Object that is copied. + */ +void +SpacerTabIndex::copyHelperSpacerTabIndex(const SpacerTabIndex& obj) +{ + m_windowIndex = obj.m_windowIndex; + m_rowIndex = obj.m_rowIndex; + m_columnIndex = obj.m_columnIndex; +} + +/** + * Equality operator. + * @param obj + * Instance compared to this for equality. + * @return + * True if this instance and 'obj' instance are considered equal. + */ +bool +SpacerTabIndex::operator==(const SpacerTabIndex& obj) const +{ + if (this == &obj) { + return true; + } + + if ((m_windowIndex == obj.m_windowIndex) + && (m_rowIndex == obj.m_rowIndex) + && (m_columnIndex == obj.m_columnIndex)) { + return true; + } + + return false; +} + +/** + * Comparison operator. + * + * @param rhs + * Other instance for comparison + * @return True if 'this' is less than 'rhs', else false. + */ +bool +SpacerTabIndex::operator<(const SpacerTabIndex& rhs) const +{ + if (m_windowIndex != rhs.m_windowIndex) { + return (m_windowIndex < rhs.m_windowIndex); + } + if (m_rowIndex != rhs.m_rowIndex) { + return (m_rowIndex < rhs.m_rowIndex); + } + if (m_columnIndex != rhs.m_columnIndex) { + return (m_columnIndex < rhs.m_columnIndex); + } + + return false; +} + +/** + * @return The window index + */ +int32_t +SpacerTabIndex::getWindowIndex() const +{ + return m_windowIndex; +} + +/** + * @return The row index + */ +int32_t +SpacerTabIndex::getRowIndex() const +{ + return m_rowIndex; +} + +/** + * @return The column index + */ +int32_t +SpacerTabIndex::getColumnIndex() const +{ + return m_columnIndex; +} + + +/** + * Get a description of this object's content. + * @return String describing this object's content. + */ +AString +SpacerTabIndex::toString() const +{ + AString s("SpacerTabIndex: windowIndex=%1, rowIndex=%2, columnIndex=%3"); + s = s.arg(m_windowIndex).arg(m_rowIndex).arg(m_columnIndex); + + return s; +} + +/** + * Save information specific to this type of model to the scene. + * + * @param sceneAttributes + * Attributes for the scene. Scenes may be of different types + * (full, generic, etc) and the attributes should be checked when + * saving the scene. + * + * @param instanceName + * Name of instance in the scene. + */ +SceneClass* +SpacerTabIndex::saveToScene(const SceneAttributes* sceneAttributes, + const AString& instanceName) +{ + SceneClass* sceneClass = new SceneClass(instanceName, + "SpacerTabIndex", + 1); + m_sceneAssistant->saveMembers(sceneAttributes, + sceneClass); + + // Uncomment if sub-classes must save to scene + //saveSubClassDataToScene(sceneAttributes, + // sceneClass); + + return sceneClass; +} + +/** + * Restore information specific to the type of model from the scene. + * + * @param sceneAttributes + * Attributes for the scene. Scenes may be of different types + * (full, generic, etc) and the attributes should be checked when + * restoring the scene. + * + * @param sceneClass + * sceneClass from which model specific information is obtained. + */ +void +SpacerTabIndex::restoreFromScene(const SceneAttributes* sceneAttributes, + const SceneClass* sceneClass) +{ + if (sceneClass == NULL) { + return; + } + + m_sceneAssistant->restoreMembers(sceneAttributes, + sceneClass); + + //Uncomment if sub-classes must restore from scene + //restoreSubClassDataFromScene(sceneAttributes, + // sceneClass); + +} + diff --git a/src/Scenes/SpacerTabIndex.h b/src/Scenes/SpacerTabIndex.h new file mode 100644 index 000000000..1a896b293 --- /dev/null +++ b/src/Scenes/SpacerTabIndex.h @@ -0,0 +1,105 @@ +#ifndef __SPACER_TAB_INDEX_H__ +#define __SPACER_TAB_INDEX_H__ + +/*LICENSE_START*/ +/* + * Copyright (C) 2018 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + + + +#include + +#include "CaretObject.h" + +#include "SceneableInterface.h" + + +namespace caret { + class SceneClassAssistant; + + class SpacerTabIndex : public CaretObject, public SceneableInterface { + + public: + SpacerTabIndex(); + + SpacerTabIndex(const int32_t windowIndex, + const int32_t rowIndex, + const int32_t columnIndex); + + virtual ~SpacerTabIndex(); + + SpacerTabIndex(const SpacerTabIndex& obj); + + SpacerTabIndex& operator=(const SpacerTabIndex& obj); + + bool operator==(const SpacerTabIndex& obj) const; + + bool operator<(const SpacerTabIndex& rhs) const; + + int32_t getWindowIndex() const; + + int32_t getRowIndex() const; + + int32_t getColumnIndex() const; + + // ADD_NEW_METHODS_HERE + + virtual AString toString() const; + + virtual SceneClass* saveToScene(const SceneAttributes* sceneAttributes, + const AString& instanceName); + + virtual void restoreFromScene(const SceneAttributes* sceneAttributes, + const SceneClass* sceneClass); + + + + + + +// If there will be sub-classes of this class that need to save +// and restore data from scenes, these pure virtual methods can +// be uncommented to force their implementation by sub-classes. +// protected: +// virtual void saveSubClassDataToScene(const SceneAttributes* sceneAttributes, +// SceneClass* sceneClass) = 0; +// +// virtual void restoreSubClassDataFromScene(const SceneAttributes* sceneAttributes, +// const SceneClass* sceneClass) = 0; + + private: + void copyHelperSpacerTabIndex(const SpacerTabIndex& obj); + + std::unique_ptr m_sceneAssistant; + + int32_t m_windowIndex; + int32_t m_rowIndex; + int32_t m_columnIndex; + + // ADD_NEW_MEMBERS_HERE + + friend class SpacerTabContent; + }; + +#ifdef __SPACER_TAB_INDEX_DECLARE__ + // +#endif // __SPACER_TAB_INDEX_DECLARE__ + +} // namespace +#endif //__SPACER_TAB_INDEX_H__ -- GitLab From 05f1368d93c96d122ad589aa2e43e8c37d868f18 Mon Sep 17 00:00:00 2001 From: John Harwell Date: Mon, 22 Oct 2018 12:38:25 -0500 Subject: [PATCH 085/427] WB-668 Titling columns in tile tabs: Spacer annotations now drawing, selectable in Features Toolbox, and many other updates. --- src/Annotations/Annotation.cxx | 155 +++++++++++++--- src/Annotations/Annotation.h | 7 + .../AnnotationCoordinateSpaceEnum.cxx | 8 + .../AnnotationCoordinateSpaceEnum.h | 2 + src/Annotations/AnnotationGroup.cxx | 31 +++- src/Annotations/AnnotationGroup.h | 8 +- .../AnnotationOneDimensionalShape.cxx | 35 +++- .../AnnotationOneDimensionalShape.h | 2 + src/Annotations/AnnotationRedoUndoCommand.cxx | 4 + .../AnnotationTwoDimensionalShape.cxx | 21 +++ .../AnnotationTwoDimensionalShape.h | 2 + src/Brain/AnnotationArrangerExecutor.cxx | 33 +++- src/Brain/AnnotationArrangerExecutor.h | 6 +- src/Brain/AnnotationManager.cxx | 3 + ...inOpenGLAnnotationDrawingFixedPipeline.cxx | 119 ++++++++++--- ...rainOpenGLAnnotationDrawingFixedPipeline.h | 4 + ...rainOpenGLChartTwoDrawingFixedPipeline.cxx | 2 + src/Brain/BrainOpenGLFixedPipeline.cxx | 77 +++++++- src/Brain/BrainOpenGLFixedPipeline.h | 2 + src/Brain/BrainOpenGLViewportContent.cxx | 3 + .../BrainOpenGLVolumeObliqueSliceDrawing.cxx | 2 + src/Brain/BrainOpenGLVolumeSliceDrawing.cxx | 2 + src/Brain/DisplayPropertiesAnnotation.cxx | 5 + src/Brain/FtglFontTextRenderer.cxx | 2 + src/Brain/SpacerTabContent.cxx | 9 + src/Brain/SpacerTabContent.h | 2 + src/Common/CMakeLists.txt | 2 + src/Common/EventGetViewportSize.cxx | 40 ++--- src/Common/EventGetViewportSize.h | 17 +- src/{Scenes => Common}/SpacerTabIndex.cxx | 166 +++++++++++------- src/{Scenes => Common}/SpacerTabIndex.h | 42 ++--- src/Files/AnnotationFile.cxx | 43 ++++- src/Files/AnnotationFile.h | 2 + src/Files/AnnotationFileXmlFormatBase.h | 4 + src/Files/AnnotationFileXmlReader.cxx | 25 +++ src/Files/AnnotationFileXmlWriter.cxx | 6 + .../AnnotationChangeCoordinateDialog.cxx | 3 + src/GuiQt/AnnotationCoordinateInformation.cxx | 99 ++++++++++- src/GuiQt/AnnotationCoordinateInformation.h | 13 +- .../AnnotationCoordinateSelectionWidget.cxx | 19 ++ src/GuiQt/AnnotationCoordinateSpaceWidget.cxx | 2 + src/GuiQt/AnnotationCoordinateWidget.cxx | 11 ++ src/GuiQt/AnnotationCreateDialog.cxx | 12 ++ src/GuiQt/AnnotationFontWidget.cxx | 2 + src/GuiQt/AnnotationInsertNewWidget.cxx | 35 ++-- src/GuiQt/AnnotationPasteDialog.cxx | 2 + src/GuiQt/AnnotationRotationWidget.cxx | 4 + src/GuiQt/BrainBrowserWindow.cxx | 16 ++ src/GuiQt/UserInputModeAnnotations.cxx | 18 ++ .../UserInputModeAnnotationsContextMenu.cxx | 2 + src/Scenes/CMakeLists.txt | 2 - src/Scenes/DisplayGroupAndTabItemHelper.cxx | 41 ++++- src/Scenes/DisplayGroupAndTabItemHelper.h | 7 + 53 files changed, 964 insertions(+), 217 deletions(-) rename src/{Scenes => Common}/SpacerTabIndex.cxx (63%) rename src/{Scenes => Common}/SpacerTabIndex.h (66%) diff --git a/src/Annotations/Annotation.cxx b/src/Annotations/Annotation.cxx index 0524c5cf1..1394a7752 100644 --- a/src/Annotations/Annotation.cxx +++ b/src/Annotations/Annotation.cxx @@ -125,6 +125,7 @@ Annotation::copyHelperAnnotation(const Annotation& obj) m_uniqueKey = -1; m_coordinateSpace = obj.m_coordinateSpace; m_tabIndex = obj.m_tabIndex; + m_spacerTabIndex = obj.m_spacerTabIndex; m_windowIndex = obj.m_windowIndex; m_viewportCoordinateSpaceViewport[0] = obj.m_viewportCoordinateSpaceViewport[0]; m_viewportCoordinateSpaceViewport[1] = obj.m_viewportCoordinateSpaceViewport[1]; @@ -385,6 +386,7 @@ Annotation::initializeAnnotationMembers() m_coordinateSpace = AnnotationCoordinateSpaceEnum::TAB; m_tabIndex = -1; + m_spacerTabIndex = SpacerTabIndex(); m_windowIndex = -1; m_viewportCoordinateSpaceViewport[0] = 0; m_viewportCoordinateSpaceViewport[1] = 0; @@ -538,8 +540,22 @@ Annotation::initializeAnnotationMembers() initializeProperties(); - if (m_coordinateSpace == AnnotationCoordinateSpaceEnum::VIEWPORT) { - setPropertiesForSpecializedUsage(PropertiesSpecializedUsage::VIEWPORT_ANNOTATION); + switch (m_coordinateSpace) { + case AnnotationCoordinateSpaceEnum::CHART: + break; + case AnnotationCoordinateSpaceEnum::SPACER: + break; + case AnnotationCoordinateSpaceEnum::STEREOTAXIC: + break; + case AnnotationCoordinateSpaceEnum::SURFACE: + break; + case AnnotationCoordinateSpaceEnum::TAB: + break; + case AnnotationCoordinateSpaceEnum::VIEWPORT: + setPropertiesForSpecializedUsage(PropertiesSpecializedUsage::VIEWPORT_ANNOTATION); + break; + case AnnotationCoordinateSpaceEnum::WINDOW: + break; } /* @@ -679,6 +695,8 @@ Annotation::isInSurfaceSpaceWithTangentOffset() const switch (m_coordinateSpace) { case AnnotationCoordinateSpaceEnum::CHART: break; + case AnnotationCoordinateSpaceEnum::SPACER: + break; case AnnotationCoordinateSpaceEnum::STEREOTAXIC: break; case AnnotationCoordinateSpaceEnum::SURFACE: @@ -1062,6 +1080,30 @@ Annotation::setTabIndex(const int32_t tabIndex) } } +/** + * @return Index of the spacer tab. + */ +SpacerTabIndex +Annotation::getSpacerTabIndex() const +{ + return m_spacerTabIndex; +} + +/** + * Set index of the spacer tab. + * + * @param spacerTabIndex + * Index of the spacer tab. + */ +void +Annotation::setSpacerTabIndex(const SpacerTabIndex& spacerTabIndex) +{ + if (spacerTabIndex != m_spacerTabIndex) { + m_spacerTabIndex = spacerTabIndex; + setModified(); + } +} + /** * @return The window index. Valid only for window coordinate space annotations. */ @@ -2295,8 +2337,22 @@ bool Annotation::isItemExpanded(const DisplayGroupEnum::Enum displayGroup, const int32_t tabIndex) const { - if (m_coordinateSpace == AnnotationCoordinateSpaceEnum::WINDOW) { - return m_displayGroupAndTabItemHelper->isExpandedInWindow(m_windowIndex); + switch (m_coordinateSpace) { + case AnnotationCoordinateSpaceEnum::CHART: + break; + case AnnotationCoordinateSpaceEnum::SPACER: + break; + case AnnotationCoordinateSpaceEnum::STEREOTAXIC: + break; + case AnnotationCoordinateSpaceEnum::SURFACE: + break; + case AnnotationCoordinateSpaceEnum::TAB: + break; + case AnnotationCoordinateSpaceEnum::VIEWPORT: + break; + case AnnotationCoordinateSpaceEnum::WINDOW: + return m_displayGroupAndTabItemHelper->isExpandedInWindow(m_windowIndex); + break; } const int32_t itemTabIndex = updateDisplayGroupTabIndex(displayGroup, @@ -2325,19 +2381,32 @@ Annotation::setItemExpanded(const DisplayGroupEnum::Enum displayGroup, const int32_t tabIndex, const bool status) { - if (m_coordinateSpace == AnnotationCoordinateSpaceEnum::WINDOW) { - m_displayGroupAndTabItemHelper->setExpandedInWindow(m_windowIndex, - status); + switch (m_coordinateSpace) { + case AnnotationCoordinateSpaceEnum::CHART: + break; + case AnnotationCoordinateSpaceEnum::SPACER: + break; + case AnnotationCoordinateSpaceEnum::STEREOTAXIC: + break; + case AnnotationCoordinateSpaceEnum::SURFACE: + break; + case AnnotationCoordinateSpaceEnum::TAB: + break; + case AnnotationCoordinateSpaceEnum::VIEWPORT: + break; + case AnnotationCoordinateSpaceEnum::WINDOW: + m_displayGroupAndTabItemHelper->setExpandedInWindow(m_windowIndex, + status); + return; + break; } - else { - const int32_t itemTabIndex = updateDisplayGroupTabIndex(displayGroup, + + const int32_t itemTabIndex = updateDisplayGroupTabIndex(displayGroup, tabIndex); - m_displayGroupAndTabItemHelper->setExpanded(displayGroup, - itemTabIndex, - status); - } - + m_displayGroupAndTabItemHelper->setExpanded(displayGroup, + itemTabIndex, + status); } /** @@ -2357,10 +2426,26 @@ Annotation::getItemDisplaySelected(const DisplayGroupEnum::Enum displayGroup, const int32_t tabIndex) const { if (testProperty(Annotation::Property::DISPLAY_GROUP)) { - if (m_coordinateSpace == AnnotationCoordinateSpaceEnum::WINDOW) { - return m_displayGroupAndTabItemHelper->getSelectedInWindow(m_windowIndex); + switch (m_coordinateSpace) { + case AnnotationCoordinateSpaceEnum::CHART: + break; + case AnnotationCoordinateSpaceEnum::SPACER: + return m_displayGroupAndTabItemHelper->getSelectedInSpacerTab(); + break; + case AnnotationCoordinateSpaceEnum::STEREOTAXIC: + break; + case AnnotationCoordinateSpaceEnum::SURFACE: + break; + case AnnotationCoordinateSpaceEnum::TAB: + break; + case AnnotationCoordinateSpaceEnum::VIEWPORT: + break; + case AnnotationCoordinateSpaceEnum::WINDOW: + return m_displayGroupAndTabItemHelper->getSelectedInWindow(m_windowIndex); + break; } + const int32_t itemTabIndex = updateDisplayGroupTabIndex(displayGroup, tabIndex); @@ -2393,18 +2478,34 @@ Annotation::setItemDisplaySelected(const DisplayGroupEnum::Enum displayGroup, const int32_t tabIndex, const TriStateSelectionStatusEnum::Enum status) { - if (m_coordinateSpace == AnnotationCoordinateSpaceEnum::WINDOW) { - m_displayGroupAndTabItemHelper->setSelectedInWindow(m_windowIndex, - status); - } - else { - const int32_t itemTabIndex = updateDisplayGroupTabIndex(displayGroup, - tabIndex); - - m_displayGroupAndTabItemHelper->setSelected(displayGroup, - itemTabIndex, - status); + switch (m_coordinateSpace) { + case AnnotationCoordinateSpaceEnum::CHART: + break; + case AnnotationCoordinateSpaceEnum::SPACER: + m_displayGroupAndTabItemHelper->setSelectedInSpacerTab(status); + return; + break; + case AnnotationCoordinateSpaceEnum::STEREOTAXIC: + break; + case AnnotationCoordinateSpaceEnum::SURFACE: + break; + case AnnotationCoordinateSpaceEnum::TAB: + break; + case AnnotationCoordinateSpaceEnum::VIEWPORT: + break; + case AnnotationCoordinateSpaceEnum::WINDOW: + m_displayGroupAndTabItemHelper->setSelectedInWindow(m_windowIndex, + status); + return; + break; } + + const int32_t itemTabIndex = updateDisplayGroupTabIndex(displayGroup, + tabIndex); + + m_displayGroupAndTabItemHelper->setSelected(displayGroup, + itemTabIndex, + status); } /** diff --git a/src/Annotations/Annotation.h b/src/Annotations/Annotation.h index aa5ca9e0b..1a3e7a20a 100644 --- a/src/Annotations/Annotation.h +++ b/src/Annotations/Annotation.h @@ -33,6 +33,7 @@ #include "CaretObjectTracksModification.h" #include "DisplayGroupAndTabItemInterface.h" #include "SceneableInterface.h" +#include "SpacerTabIndex.h" #include "StructureEnum.h" @@ -183,6 +184,10 @@ namespace caret { void setTabIndex(const int32_t tabIndex); + SpacerTabIndex getSpacerTabIndex() const; + + void setSpacerTabIndex(const SpacerTabIndex& spacerTabIndex); + int32_t getWindowIndex() const; void setWindowIndex(const int32_t windowIndex); @@ -390,6 +395,8 @@ namespace caret { AnnotationCoordinateSpaceEnum::Enum m_coordinateSpace; + SpacerTabIndex m_spacerTabIndex; + int32_t m_tabIndex; int32_t m_windowIndex; diff --git a/src/Annotations/AnnotationCoordinateSpaceEnum.cxx b/src/Annotations/AnnotationCoordinateSpaceEnum.cxx index 0c767caae..6520ee53e 100644 --- a/src/Annotations/AnnotationCoordinateSpaceEnum.cxx +++ b/src/Annotations/AnnotationCoordinateSpaceEnum.cxx @@ -115,6 +115,11 @@ AnnotationCoordinateSpaceEnum::initialize() "Chart", "Ch")); + enumData.push_back(AnnotationCoordinateSpaceEnum(SPACER, + "SPACER", + "Spacer", + "Sp")); + enumData.push_back(AnnotationCoordinateSpaceEnum(STEREOTAXIC, "STEREOTAXIC", "Stereotaxic", @@ -307,6 +312,9 @@ AnnotationCoordinateSpaceEnum::toToolTip(Enum enumValue) case CHART: text = "New annotation is drawn at a chart data XYZ coordinate"; break; + case SPACER: + text = "New annotation is drawn at an XY coordinate in the spacer"; + break; case STEREOTAXIC: text = "New annotation is drawn at a surface/volume XYZ coordinate"; break; diff --git a/src/Annotations/AnnotationCoordinateSpaceEnum.h b/src/Annotations/AnnotationCoordinateSpaceEnum.h index 3dc4a839f..1a2e35198 100644 --- a/src/Annotations/AnnotationCoordinateSpaceEnum.h +++ b/src/Annotations/AnnotationCoordinateSpaceEnum.h @@ -37,6 +37,8 @@ public: enum Enum { /** Chart space */ CHART, + /** Annotation in spacer */ + SPACER, /** Annotation in stereotaxic (3D) space */ STEREOTAXIC, /** Annotation on surface node */ diff --git a/src/Annotations/AnnotationGroup.cxx b/src/Annotations/AnnotationGroup.cxx index 38777fc4b..005373c05 100644 --- a/src/Annotations/AnnotationGroup.cxx +++ b/src/Annotations/AnnotationGroup.cxx @@ -57,12 +57,15 @@ using namespace caret; * Annotation coordinate space for the group. * @param tabOrWindowIndex * Index of tab or window for tab or window space. + * @param spacerTabIndex + * Index of a spacer tab. */ AnnotationGroup::AnnotationGroup(AnnotationFile* annotationFile, const AnnotationGroupTypeEnum::Enum groupType, const int32_t uniqueKey, const AnnotationCoordinateSpaceEnum::Enum coordinateSpace, - const int32_t tabOrWindowIndex) + const int32_t tabOrWindowIndex, + const SpacerTabIndex& spacerTabIndex) : CaretObjectTracksModification(), DisplayGroupAndTabItemInterface(), SceneableInterface() @@ -91,10 +94,14 @@ SceneableInterface() m_coordinateSpace = coordinateSpace; m_tabOrWindowIndex = tabOrWindowIndex; + m_spacerTabIndex = spacerTabIndex; switch (m_coordinateSpace) { case AnnotationCoordinateSpaceEnum::CHART: break; + case AnnotationCoordinateSpaceEnum::SPACER: + CaretAssert(m_spacerTabIndex.isValid()); + break; case AnnotationCoordinateSpaceEnum::STEREOTAXIC: break; case AnnotationCoordinateSpaceEnum::SURFACE: @@ -169,6 +176,7 @@ AnnotationGroup::copyHelperAnnotationGroup(const AnnotationGroup& obj) m_coordinateSpace = obj.m_coordinateSpace; m_name = obj.m_name; m_tabOrWindowIndex = obj.m_tabOrWindowIndex; + m_spacerTabIndex = obj.m_spacerTabIndex; *m_displayGroupAndTabItemHelper = *obj.m_displayGroupAndTabItemHelper; CaretAssertMessage(0, "What to do with annotations remove copy constructor/operator="); @@ -184,6 +192,7 @@ AnnotationGroup::initializeInstance() m_coordinateSpace = AnnotationCoordinateSpaceEnum::VIEWPORT; m_name = ""; m_tabOrWindowIndex = -1; + m_spacerTabIndex = SpacerTabIndex(); m_displayGroupAndTabItemHelper = new DisplayGroupAndTabItemHelper(); @@ -263,6 +272,10 @@ AnnotationGroup::getName() const switch (m_coordinateSpace) { case AnnotationCoordinateSpaceEnum::CHART: break; + case AnnotationCoordinateSpaceEnum::SPACER: + spaceName.append(" " + + m_spacerTabIndex.getWindowRowColumnGuiText()); + break; case AnnotationCoordinateSpaceEnum::STEREOTAXIC: break; case AnnotationCoordinateSpaceEnum::SURFACE: @@ -314,6 +327,15 @@ AnnotationGroup::getTabOrWindowIndex() const return m_tabOrWindowIndex; } +/** + * Index of a spacer tab. + */ +SpacerTabIndex +AnnotationGroup::getSpacerTabIndex() const +{ + return m_spacerTabIndex; +} + /** * @return Unique key displayed in annotation group name. */ @@ -425,6 +447,13 @@ AnnotationGroup::validateAddedAnnotation(const Annotation* annotation) switch (space) { case AnnotationCoordinateSpaceEnum::CHART: break; + case AnnotationCoordinateSpaceEnum::SPACER: + if (m_spacerTabIndex != annotation->getSpacerTabIndex()) { + CaretLogSevere("Attempting to add anntation with non-matching spacer tab index"); + CaretAssert(0); + return false; + } + break; case AnnotationCoordinateSpaceEnum::STEREOTAXIC: break; case AnnotationCoordinateSpaceEnum::SURFACE: diff --git a/src/Annotations/AnnotationGroup.h b/src/Annotations/AnnotationGroup.h index 767348950..87f8a2312 100644 --- a/src/Annotations/AnnotationGroup.h +++ b/src/Annotations/AnnotationGroup.h @@ -28,6 +28,7 @@ #include "CaretObjectTracksModification.h" #include "DisplayGroupAndTabItemInterface.h" #include "SceneableInterface.h" +#include "SpacerTabIndex.h" namespace caret { @@ -42,7 +43,8 @@ namespace caret { const AnnotationGroupTypeEnum::Enum groupType, const int32_t uniqueKey, const AnnotationCoordinateSpaceEnum::Enum coordinateSpace, - const int32_t tabOrWindowIndex); + const int32_t tabOrWindowIndex, + const SpacerTabIndex& spacerTabIndex); virtual ~AnnotationGroup(); @@ -62,6 +64,8 @@ namespace caret { int32_t getTabOrWindowIndex() const; + SpacerTabIndex getSpacerTabIndex() const; + int32_t getNumberOfAnnotations() const; Annotation* getAnnotation(const int32_t index); @@ -173,6 +177,8 @@ namespace caret { AnnotationCoordinateSpaceEnum::Enum m_coordinateSpace; + SpacerTabIndex m_spacerTabIndex; + int32_t m_tabOrWindowIndex; mutable AString m_name; diff --git a/src/Annotations/AnnotationOneDimensionalShape.cxx b/src/Annotations/AnnotationOneDimensionalShape.cxx index 871284e26..3a1d3a032 100644 --- a/src/Annotations/AnnotationOneDimensionalShape.cxx +++ b/src/Annotations/AnnotationOneDimensionalShape.cxx @@ -369,24 +369,26 @@ AnnotationOneDimensionalShape::setRotationAngle(const float viewportWidth, bool AnnotationOneDimensionalShape::isSizeHandleValid(const AnnotationSizingHandleTypeEnum::Enum sizingHandle) const { - bool chartFlag = false; - bool tabWindowFlag = false; + bool xyPlaneFlag = false; switch (getCoordinateSpace()) { case AnnotationCoordinateSpaceEnum::CHART: - chartFlag = true; + xyPlaneFlag = true; + break; + case AnnotationCoordinateSpaceEnum::SPACER: + xyPlaneFlag = true; break; case AnnotationCoordinateSpaceEnum::STEREOTAXIC: break; case AnnotationCoordinateSpaceEnum::SURFACE: break; case AnnotationCoordinateSpaceEnum::TAB: - tabWindowFlag = true; + xyPlaneFlag = true; break; case AnnotationCoordinateSpaceEnum::VIEWPORT: break; case AnnotationCoordinateSpaceEnum::WINDOW: - tabWindowFlag = true; + xyPlaneFlag = true; break; } @@ -416,13 +418,12 @@ AnnotationOneDimensionalShape::isSizeHandleValid(const AnnotationSizingHandleTyp validFlag = true; break; case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_NONE: - if (chartFlag - || tabWindowFlag) { + if (xyPlaneFlag) { validFlag = true; } break; case AnnotationSizingHandleTypeEnum::ANNOTATION_SIZING_HANDLE_ROTATION: - if (tabWindowFlag) { + if (xyPlaneFlag) { validFlag = true; } break; @@ -515,6 +516,21 @@ AnnotationOneDimensionalShape::applySpatialModificationSurfaceSpace(const Annota return validFlag; } +/** + * Apply a spatial modification to an annotation in spacer tab space. + * + * @param spatialModification + * Contains information about the spatial modification. + * @return + * True if the annotation was modified, else false. + */ +bool +AnnotationOneDimensionalShape::applySpatialModificationSpacerTabSpace(const AnnotationSpatialModification& spatialModification) +{ + return applySpatialModificationTabOrWindowSpace(spatialModification); +} + + /** * Apply a spatial modification to an annotation in tab or window space. * @@ -792,6 +808,9 @@ AnnotationOneDimensionalShape::applySpatialModification(const AnnotationSpatialM case AnnotationCoordinateSpaceEnum::CHART: return applySpatialModificationChartSpace(spatialModification); break; + case AnnotationCoordinateSpaceEnum::SPACER: + return applySpatialModificationSpacerTabSpace(spatialModification); + break; case AnnotationCoordinateSpaceEnum::STEREOTAXIC: return applySpatialModificationStereotaxicSpace(spatialModification); break; diff --git a/src/Annotations/AnnotationOneDimensionalShape.h b/src/Annotations/AnnotationOneDimensionalShape.h index e1f7ff879..8b40ed6f0 100644 --- a/src/Annotations/AnnotationOneDimensionalShape.h +++ b/src/Annotations/AnnotationOneDimensionalShape.h @@ -104,6 +104,8 @@ namespace caret { bool applySpatialModificationTabOrWindowSpace(const AnnotationSpatialModification& spatialModification); + bool applySpatialModificationSpacerTabSpace(const AnnotationSpatialModification& spatialModification); + CaretPointer m_sceneAssistant; CaretPointer m_startCoordinate; diff --git a/src/Annotations/AnnotationRedoUndoCommand.cxx b/src/Annotations/AnnotationRedoUndoCommand.cxx index df16def5d..c13e77b14 100644 --- a/src/Annotations/AnnotationRedoUndoCommand.cxx +++ b/src/Annotations/AnnotationRedoUndoCommand.cxx @@ -1418,6 +1418,8 @@ AnnotationRedoUndoCommand::setModeRotationAngle(const float newRotationAngle, switch (oneDimAnn->getCoordinateSpace()) { case AnnotationCoordinateSpaceEnum::CHART: break; + case AnnotationCoordinateSpaceEnum::SPACER: + break; case AnnotationCoordinateSpaceEnum::STEREOTAXIC: break; case AnnotationCoordinateSpaceEnum::SURFACE: @@ -1881,6 +1883,8 @@ AnnotationRedoUndoCommand::setModeTextFontPercentSize(const float newFontPercent switch (redoAnnotation->getCoordinateSpace()) { case AnnotationCoordinateSpaceEnum::CHART: break; + case AnnotationCoordinateSpaceEnum::SPACER: + break; case AnnotationCoordinateSpaceEnum::STEREOTAXIC: percentSize *= surfaceSpaceRowCount; break; diff --git a/src/Annotations/AnnotationTwoDimensionalShape.cxx b/src/Annotations/AnnotationTwoDimensionalShape.cxx index 2abe0ff46..53fd31615 100644 --- a/src/Annotations/AnnotationTwoDimensionalShape.cxx +++ b/src/Annotations/AnnotationTwoDimensionalShape.cxx @@ -699,6 +699,9 @@ AnnotationTwoDimensionalShape::applySpatialModificationSurfaceOrStereotaxicSpace case AnnotationCoordinateSpaceEnum::CHART: badSpaceFlag = true; break; + case AnnotationCoordinateSpaceEnum::SPACER: + badSpaceFlag = true; + break; case AnnotationCoordinateSpaceEnum::STEREOTAXIC: stereoSpaceFlag = true; break; @@ -925,6 +928,21 @@ AnnotationTwoDimensionalShape::applySpatialModificationSurfaceOrStereotaxicSpace return validFlag; } +/** + * Apply a spatial modification to an annotation in spacer tab space. + * + * @param spatialModification + * Contains information about the spatial modification. + * @return + * True if the annotation was modified, else false. + */ +bool +AnnotationTwoDimensionalShape::applySpatialModificationSpacerTabSpace(const AnnotationSpatialModification& spatialModification) +{ + return applySpatialModificationTabOrWindowSpace(spatialModification); +} + + /** * Apply a spatial modification to an annotation in tab or window space. * @@ -1337,6 +1355,9 @@ AnnotationTwoDimensionalShape::applySpatialModification(const AnnotationSpatialM case AnnotationCoordinateSpaceEnum::CHART: return applySpatialModificationChartSpace(spatialModification); break; + case AnnotationCoordinateSpaceEnum::SPACER: + return applySpatialModificationSpacerTabSpace(spatialModification); + break; case AnnotationCoordinateSpaceEnum::STEREOTAXIC: return applySpatialModificationSurfaceOrStereotaxicSpace(spatialModification, space); diff --git a/src/Annotations/AnnotationTwoDimensionalShape.h b/src/Annotations/AnnotationTwoDimensionalShape.h index b57f2f871..e49c98843 100644 --- a/src/Annotations/AnnotationTwoDimensionalShape.h +++ b/src/Annotations/AnnotationTwoDimensionalShape.h @@ -126,6 +126,8 @@ namespace caret { const float addX, const float addY); + bool applySpatialModificationSpacerTabSpace(const AnnotationSpatialModification& spatialModification); + bool applySpatialModificationSurfaceOrStereotaxicSpace(const AnnotationSpatialModification& spatialModification, const AnnotationCoordinateSpaceEnum::Enum coordinateSpace); diff --git a/src/Brain/AnnotationArrangerExecutor.cxx b/src/Brain/AnnotationArrangerExecutor.cxx index 01f2f3c91..0fb992f0e 100644 --- a/src/Brain/AnnotationArrangerExecutor.cxx +++ b/src/Brain/AnnotationArrangerExecutor.cxx @@ -616,6 +616,10 @@ AnnotationArrangerExecutor::setupAnnotationInfo(const AnnotationArrangerInputs& case AnnotationCoordinateSpaceEnum::CHART: CaretAssert(0); break; + case AnnotationCoordinateSpaceEnum::SPACER: + getSpacerTabViewport(annotation->getSpacerTabIndex(), + annViewport); + break; case AnnotationCoordinateSpaceEnum::STEREOTAXIC: CaretAssert(0); break; @@ -864,7 +868,9 @@ void AnnotationArrangerExecutor::getTabViewport(const int32_t tabIndex, int32_t tabViewportOut[4]) { - + /* + * Check cached viewports to avoid retrieving it a second time + */ std::map::iterator iter = m_tabViewports.find(tabIndex); if (iter != m_tabViewports.end()) { tabViewportOut[0] = iter->second.m_viewport[0]; @@ -893,6 +899,31 @@ AnnotationArrangerExecutor::getTabViewport(const int32_t tabIndex, } } +/** + * Get the viewport for the given spacer tab. Viewports are cached for efficiency. + * + * @param spacerTabIndex + * Index of the spacer tab. + * @param tabViewportOut + * Viewport of the tab. + * @throw CaretException + * If there is an error. + */ +void +AnnotationArrangerExecutor::getSpacerTabViewport(const SpacerTabIndex& spacerTabIndex, + int32_t tabViewportOut[4]) +{ + EventGetViewportSize vpEvent(spacerTabIndex); + EventManager::get()->sendEvent(vpEvent.getPointer()); + if (vpEvent.isViewportSizeValid()) { + vpEvent.getViewportSize(tabViewportOut); + } + else { + throw CaretException("Failed to get viewport size for spacer tab index " + + spacerTabIndex.toString()); + } +} + /** * Get a description of this object's content. * @return String describing this object's content. diff --git a/src/Brain/AnnotationArrangerExecutor.h b/src/Brain/AnnotationArrangerExecutor.h index c2ddc92e7..2d11250b4 100644 --- a/src/Brain/AnnotationArrangerExecutor.h +++ b/src/Brain/AnnotationArrangerExecutor.h @@ -27,8 +27,7 @@ #include "AnnotationDistributeEnum.h" #include "BoundingBox.h" #include "CaretObject.h" - - +#include "SpacerTabIndex.h" namespace caret { @@ -126,6 +125,9 @@ namespace caret { void getAnnotationsForArranging(const AnnotationArrangerInputs& arrangerInputs, std::vector& annotationsOut) const; + void getSpacerTabViewport(const SpacerTabIndex& spacerTabIndex, + int32_t tabViewportOut[4]); + void getTabViewport(const int32_t tabIndex, int32_t tabViewportOut[4]); diff --git a/src/Brain/AnnotationManager.cxx b/src/Brain/AnnotationManager.cxx index 46549ac34..d45af9e5a 100644 --- a/src/Brain/AnnotationManager.cxx +++ b/src/Brain/AnnotationManager.cxx @@ -932,6 +932,9 @@ AnnotationManager::getDisplayedAnnotationFiles(EventGetDisplayedDataFiles* displ case AnnotationCoordinateSpaceEnum::CHART: displayedFlag = true; break; + case AnnotationCoordinateSpaceEnum::SPACER: + displayedFlag = true; + break; case AnnotationCoordinateSpaceEnum::STEREOTAXIC: displayedFlag = true; break; diff --git a/src/Brain/BrainOpenGLAnnotationDrawingFixedPipeline.cxx b/src/Brain/BrainOpenGLAnnotationDrawingFixedPipeline.cxx index 9d74f04ea..4ab4949a1 100644 --- a/src/Brain/BrainOpenGLAnnotationDrawingFixedPipeline.cxx +++ b/src/Brain/BrainOpenGLAnnotationDrawingFixedPipeline.cxx @@ -179,6 +179,10 @@ BrainOpenGLAnnotationDrawingFixedPipeline::getAnnotationDrawingSpaceCoordinate(c modelXYZ[2] = annotationXYZ[2]; modelXYZValid = true; break; + case AnnotationCoordinateSpaceEnum::SPACER: + viewportToOpenGLWindowCoordinate(annotationXYZ, drawingSpaceXYZ); + drawingSpaceXYZValid = true; + break; case AnnotationCoordinateSpaceEnum::STEREOTAXIC: modelXYZ[0] = annotationXYZ[0]; modelXYZ[1] = annotationXYZ[1]; @@ -600,6 +604,9 @@ BrainOpenGLAnnotationDrawingFixedPipeline::drawAnnotationsInternal(const Annotat switch (drawingCoordinateSpace) { case AnnotationCoordinateSpaceEnum::CHART: break; + case AnnotationCoordinateSpaceEnum::SPACER: + haveDisplayGroupFlag = false; + break; case AnnotationCoordinateSpaceEnum::STEREOTAXIC: break; case AnnotationCoordinateSpaceEnum::SURFACE: @@ -714,6 +721,8 @@ BrainOpenGLAnnotationDrawingFixedPipeline::drawAnnotationsInternal(const Annotat switch (drawingCoordinateSpace) { case AnnotationCoordinateSpaceEnum::CHART: break; + case AnnotationCoordinateSpaceEnum::SPACER: + break; case AnnotationCoordinateSpaceEnum::STEREOTAXIC: break; case AnnotationCoordinateSpaceEnum::SURFACE: @@ -847,40 +856,90 @@ BrainOpenGLAnnotationDrawingFixedPipeline::drawAnnotationsInternal(const Annotat continue; } - /* - * Skip annotation in a different window - */ - if (annotationCoordinateSpace == AnnotationCoordinateSpaceEnum::WINDOW) { - const int32_t annotationWindowIndex = annotation->getWindowIndex(); - if ((annotationWindowIndex < 0) - || (annotationWindowIndex >= BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_WINDOWS)) { - CaretLogSevere("Annotation has invalid window index=" - + AString::number(annotationWindowIndex) - + " " - + annotation->toString()); - } - if (m_inputs->m_windowIndex != annotationWindowIndex) { - continue; + switch (annotationCoordinateSpace) { + case AnnotationCoordinateSpaceEnum::CHART: + break; + case AnnotationCoordinateSpaceEnum::SPACER: + { + const SpacerTabIndex spacerTabIndex = annotation->getSpacerTabIndex(); + CaretAssert(spacerTabIndex.isValid()); + if (m_inputs->m_spacerTabIndex != spacerTabIndex) { + continue; + } } - } - - /* - * Skip annotations in a different tab - */ - if (annotationCoordinateSpace == AnnotationCoordinateSpaceEnum::TAB) { - const int32_t annotationTabIndex = annotation->getTabIndex(); - if ((annotationTabIndex < 0) - || (annotationTabIndex >= BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS)) { - CaretLogSevere("Annotation has invalid tab index=" - + AString::number(annotationTabIndex) - + " " - + annotation->toString()); + break; + case AnnotationCoordinateSpaceEnum::STEREOTAXIC: + break; + case AnnotationCoordinateSpaceEnum::SURFACE: + break; + case AnnotationCoordinateSpaceEnum::TAB: + { + const int32_t annotationTabIndex = annotation->getTabIndex(); + if ((annotationTabIndex < 0) + || (annotationTabIndex >= BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS)) { + CaretLogSevere("Annotation has invalid tab index=" + + AString::number(annotationTabIndex) + + " " + + annotation->toString()); + } + if (m_inputs->m_tabIndex != annotationTabIndex) { + continue; + } } - if (m_inputs->m_tabIndex != annotationTabIndex) { - continue; + break; + case AnnotationCoordinateSpaceEnum::VIEWPORT: + break; + case AnnotationCoordinateSpaceEnum::WINDOW: + { + const int32_t annotationWindowIndex = annotation->getWindowIndex(); + if ((annotationWindowIndex < 0) + || (annotationWindowIndex >= BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_WINDOWS)) { + CaretLogSevere("Annotation has invalid window index=" + + AString::number(annotationWindowIndex) + + " " + + annotation->toString()); + } + if (m_inputs->m_windowIndex != annotationWindowIndex) { + continue; + } } + break; } +// /* +// * Skip annotation in a different window +// */ +// if (annotationCoordinateSpace == AnnotationCoordinateSpaceEnum::WINDOW) { +// const int32_t annotationWindowIndex = annotation->getWindowIndex(); +// if ((annotationWindowIndex < 0) +// || (annotationWindowIndex >= BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_WINDOWS)) { +// CaretLogSevere("Annotation has invalid window index=" +// + AString::number(annotationWindowIndex) +// + " " +// + annotation->toString()); +// } +// if (m_inputs->m_windowIndex != annotationWindowIndex) { +// continue; +// } +// } +// +// /* +// * Skip annotations in a different tab +// */ +// if (annotationCoordinateSpace == AnnotationCoordinateSpaceEnum::TAB) { +// const int32_t annotationTabIndex = annotation->getTabIndex(); +// if ((annotationTabIndex < 0) +// || (annotationTabIndex >= BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS)) { +// CaretLogSevere("Annotation has invalid tab index=" +// + AString::number(annotationTabIndex) +// + " " +// + annotation->toString()); +// } +// if (m_inputs->m_tabIndex != annotationTabIndex) { +// continue; +// } +// } + drawAnnotation(annotationFile, annotation, surfaceDisplayed); @@ -4626,6 +4685,8 @@ BrainOpenGLAnnotationDrawingFixedPipeline::isDrawnWithDepthTesting(const Annotat switch (annotation->getCoordinateSpace()) { case AnnotationCoordinateSpaceEnum::CHART: break; + case AnnotationCoordinateSpaceEnum::SPACER: + break; case AnnotationCoordinateSpaceEnum::STEREOTAXIC: testFlatSurfaceFlag = true; break; diff --git a/src/Brain/BrainOpenGLAnnotationDrawingFixedPipeline.h b/src/Brain/BrainOpenGLAnnotationDrawingFixedPipeline.h index 72e226d37..ba1ec79d6 100644 --- a/src/Brain/BrainOpenGLAnnotationDrawingFixedPipeline.h +++ b/src/Brain/BrainOpenGLAnnotationDrawingFixedPipeline.h @@ -32,6 +32,7 @@ #include "CaretObject.h" #include "CaretOpenGLInclude.h" #include "Plane.h" +#include "SpacerTabIndex.h" namespace caret { @@ -67,12 +68,14 @@ namespace caret { const float centerToEyeDistance, const int32_t windowIndex, const int32_t tabIndex, + const SpacerTabIndex &spacerTabIndex, const WindowDrawingMode windowDrawingMode) : m_brain(brain), m_drawingMode(drawingMode), m_centerToEyeDistance(centerToEyeDistance), m_windowIndex(windowIndex), m_tabIndex(tabIndex), + m_spacerTabIndex(spacerTabIndex), m_windowDrawingMode(windowDrawingMode) { } @@ -81,6 +84,7 @@ namespace caret { const float m_centerToEyeDistance; const int32_t m_windowIndex; const int32_t m_tabIndex; + const SpacerTabIndex m_spacerTabIndex; const WindowDrawingMode m_windowDrawingMode; }; diff --git a/src/Brain/BrainOpenGLChartTwoDrawingFixedPipeline.cxx b/src/Brain/BrainOpenGLChartTwoDrawingFixedPipeline.cxx index 96dfd563c..70bd290f1 100644 --- a/src/Brain/BrainOpenGLChartTwoDrawingFixedPipeline.cxx +++ b/src/Brain/BrainOpenGLChartTwoDrawingFixedPipeline.cxx @@ -177,6 +177,8 @@ BrainOpenGLChartTwoDrawingFixedPipeline::drawChartOverlaySet(Brain* brain, switch (cb->getCoordinateSpace()) { case AnnotationCoordinateSpaceEnum::CHART: break; + case AnnotationCoordinateSpaceEnum::SPACER: + break; case AnnotationCoordinateSpaceEnum::STEREOTAXIC: break; case AnnotationCoordinateSpaceEnum::SURFACE: diff --git a/src/Brain/BrainOpenGLFixedPipeline.cxx b/src/Brain/BrainOpenGLFixedPipeline.cxx index 296e24663..ab1a7a8b3 100644 --- a/src/Brain/BrainOpenGLFixedPipeline.cxx +++ b/src/Brain/BrainOpenGLFixedPipeline.cxx @@ -117,6 +117,7 @@ #include "SelectionItemSurfaceNodeIdentificationSymbol.h" #include "SelectionItemSurfaceTriangle.h" #include "SelectionItemVoxel.h" +#include "SpacerTabContent.h" #include "SurfaceMontageConfigurationCerebellar.h" #include "SurfaceMontageConfigurationCerebral.h" #include "SurfaceMontageConfigurationFlatMaps.h" @@ -288,7 +289,12 @@ BrainOpenGLFixedPipeline::selectModelImplementation(const int32_t windowIndex, * everything else. */ glClear(GL_DEPTH_BUFFER_BIT); - drawTabAnnotations(viewportContent); + if (viewportContent->getSpacerTabContent() != NULL) { + drawSpacerAnnotations(viewportContent); + } + else { + drawTabAnnotations(viewportContent); + } int windowViewport[4]; viewportContent->getWindowViewport(windowViewport); @@ -708,7 +714,12 @@ BrainOpenGLFixedPipeline::drawModelsImplementation(const int32_t windowIndex, */ updateForegroundAndBackgroundColors(vpContent); - drawTabAnnotations(vpContent); + if (vpContent->getSpacerTabContent() != NULL) { + drawSpacerAnnotations(vpContent); + } + else { + drawTabAnnotations(vpContent); + } } /* @@ -858,6 +869,7 @@ BrainOpenGLFixedPipeline::drawChartCoordinateSpaceAnnotations(const BrainOpenGLV BrainOpenGLFixedPipeline::s_gluLookAtCenterFromEyeOffsetDistance, m_windowIndex, this->windowTabIndex, + SpacerTabIndex(), BrainOpenGLAnnotationDrawingFixedPipeline::Inputs::WINDOW_DRAWING_NO); std::vector emptyColorBars; std::vector emptyViewportAnnotations; @@ -879,6 +891,63 @@ BrainOpenGLFixedPipeline::drawChartCoordinateSpaceAnnotations(const BrainOpenGLV glPopAttrib(); } +/** + * Draw the spacer tag annotations. + * + * @param tabContent + * Viewport content + */ +void +BrainOpenGLFixedPipeline::drawSpacerAnnotations(const BrainOpenGLViewportContent* tabContent) +{ + if (tabContent->getSpacerTabContent() == NULL) { + return; + } + + int tabViewport[4]; + tabContent->getModelViewport(tabViewport); + CaretAssertMessage(m_brain, "m_brain must NOT be NULL for drawing spacer tab annotations."); + glViewport(tabViewport[0], + tabViewport[1], + tabViewport[2], + tabViewport[3]); + glMatrixMode(GL_PROJECTION); + glPushMatrix(); + glOrtho(0.0, tabViewport[2], 0.0, tabViewport[3], -1.0, 1.0); + glMatrixMode(GL_MODELVIEW); + glPushMatrix(); + glLoadIdentity(); + + CaretAssert(m_windowIndex == tabContent->getWindowIndex()); + this->browserTabContent = NULL; + m_clippingPlaneGroup = NULL; //const_cast(tabContent->getBrowserTabContent()->getClippingPlaneGroup()); + + this->windowTabIndex = -1; + + SpacerTabIndex spacerTabIndex; + SpacerTabContent* spacerTabContent = tabContent->getSpacerTabContent(); + CaretAssert(spacerTabContent); + spacerTabIndex = spacerTabContent->getSpacerTabIndex(); + + BrainOpenGLAnnotationDrawingFixedPipeline::Inputs inputs(this->m_brain, + this->mode, + BrainOpenGLFixedPipeline::s_gluLookAtCenterFromEyeOffsetDistance, + m_windowIndex, + this->windowTabIndex, + spacerTabIndex, + BrainOpenGLAnnotationDrawingFixedPipeline::Inputs::WINDOW_DRAWING_NO); + m_annotationDrawing->drawAnnotations(&inputs, + AnnotationCoordinateSpaceEnum::SPACER, + m_annotationColorBarsForDrawing, + m_specialCaseGraphicsAnnotations, + annotationDrawingNullSurface, + annotationDrawingUnusedSurfaceScaling); + + glPopMatrix(); + glMatrixMode(GL_PROJECTION); + glPopMatrix(); + glMatrixMode(GL_MODELVIEW); +} /** * Draw the tab annotations. @@ -919,6 +988,7 @@ BrainOpenGLFixedPipeline::drawTabAnnotations(const BrainOpenGLViewportContent* t BrainOpenGLFixedPipeline::s_gluLookAtCenterFromEyeOffsetDistance, m_windowIndex, this->windowTabIndex, + SpacerTabIndex(), BrainOpenGLAnnotationDrawingFixedPipeline::Inputs::WINDOW_DRAWING_NO); m_annotationDrawing->drawAnnotations(&inputs, AnnotationCoordinateSpaceEnum::TAB, @@ -979,6 +1049,7 @@ BrainOpenGLFixedPipeline::drawWindowAnnotations(const int windowViewport[4]) BrainOpenGLFixedPipeline::s_gluLookAtCenterFromEyeOffsetDistance, m_windowIndex, this->windowTabIndex, + SpacerTabIndex(), windowDrawingMode); m_annotationDrawing->drawAnnotations(&inputs, @@ -1943,6 +2014,7 @@ BrainOpenGLFixedPipeline::drawSurface(Surface* surface, BrainOpenGLFixedPipeline::s_gluLookAtCenterFromEyeOffsetDistance, m_windowIndex, this->windowTabIndex, + SpacerTabIndex(), BrainOpenGLAnnotationDrawingFixedPipeline::Inputs::WINDOW_DRAWING_NO); std::vector emptyColorBars; std::vector emptyViewportAnnotations; @@ -1995,6 +2067,7 @@ BrainOpenGLFixedPipeline::drawSurface(Surface* surface, BrainOpenGLFixedPipeline::s_gluLookAtCenterFromEyeOffsetDistance, m_windowIndex, this->windowTabIndex, + SpacerTabIndex(), BrainOpenGLAnnotationDrawingFixedPipeline::Inputs::WINDOW_DRAWING_NO); std::vector emptyColorBars; std::vector emptyViewportAnnotations; diff --git a/src/Brain/BrainOpenGLFixedPipeline.h b/src/Brain/BrainOpenGLFixedPipeline.h index ecec741ff..668f97c9f 100644 --- a/src/Brain/BrainOpenGLFixedPipeline.h +++ b/src/Brain/BrainOpenGLFixedPipeline.h @@ -450,6 +450,8 @@ namespace caret { void drawWindowAnnotations(const int windowViewport[4]); + void drawSpacerAnnotations(const BrainOpenGLViewportContent* tabContent); + void drawTabAnnotations(const BrainOpenGLViewportContent* tabContent); void drawChartCoordinateSpaceAnnotations(const BrainOpenGLViewportContent* viewportContent); diff --git a/src/Brain/BrainOpenGLViewportContent.cxx b/src/Brain/BrainOpenGLViewportContent.cxx index a74616c32..11c870e5a 100644 --- a/src/Brain/BrainOpenGLViewportContent.cxx +++ b/src/Brain/BrainOpenGLViewportContent.cxx @@ -175,6 +175,8 @@ BrainOpenGLViewportContent::initializeMembersBrainOpenGLViewportContent() m_windowY = 0; m_windowWidth = 0; m_windowHeight = 0; + m_browserTabContent = NULL; + m_spacerTabContent = NULL; } /** @@ -206,6 +208,7 @@ BrainOpenGLViewportContent::copyHelperBrainOpenGLViewportContent(const BrainOpen m_windowHeight = obj.m_windowHeight; m_browserTabContent = obj.m_browserTabContent; + m_spacerTabContent = obj.m_spacerTabContent; } /** diff --git a/src/Brain/BrainOpenGLVolumeObliqueSliceDrawing.cxx b/src/Brain/BrainOpenGLVolumeObliqueSliceDrawing.cxx index 3f63c8fa8..528950a83 100644 --- a/src/Brain/BrainOpenGLVolumeObliqueSliceDrawing.cxx +++ b/src/Brain/BrainOpenGLVolumeObliqueSliceDrawing.cxx @@ -64,6 +64,7 @@ #include "SelectionItemVoxelEditing.h" #include "SelectionManager.h" #include "SessionManager.h" +#include "SpacerTabIndex.h" #include "Surface.h" #include "SurfacePlaneIntersectionToContour.h" #include "VolumeFile.h" @@ -849,6 +850,7 @@ BrainOpenGLVolumeObliqueSliceDrawing::drawVolumeSliceViewProjection(const BrainO BrainOpenGLFixedPipeline::s_gluLookAtCenterFromEyeOffsetDistance, m_fixedPipelineDrawing->m_windowIndex, m_fixedPipelineDrawing->windowTabIndex, + SpacerTabIndex(), BrainOpenGLAnnotationDrawingFixedPipeline::Inputs::WINDOW_DRAWING_NO); m_fixedPipelineDrawing->m_annotationDrawing->drawModelSpaceAnnotationsOnVolumeSlice(&inputs, slicePlane, diff --git a/src/Brain/BrainOpenGLVolumeSliceDrawing.cxx b/src/Brain/BrainOpenGLVolumeSliceDrawing.cxx index 080c28c0a..dc3b3fe8d 100644 --- a/src/Brain/BrainOpenGLVolumeSliceDrawing.cxx +++ b/src/Brain/BrainOpenGLVolumeSliceDrawing.cxx @@ -68,6 +68,7 @@ #include "SelectionItemVoxelIdentificationSymbol.h" #include "SelectionManager.h" #include "SessionManager.h" +#include "SpacerTabIndex.h" #include "SurfacePlaneIntersectionToContour.h" #include "Surface.h" #include "VolumeFile.h" @@ -891,6 +892,7 @@ BrainOpenGLVolumeSliceDrawing::drawVolumeSliceViewProjection(const AllSliceViewM BrainOpenGLFixedPipeline::s_gluLookAtCenterFromEyeOffsetDistance, m_fixedPipelineDrawing->m_windowIndex, m_fixedPipelineDrawing->windowTabIndex, + SpacerTabIndex(), BrainOpenGLAnnotationDrawingFixedPipeline::Inputs::WINDOW_DRAWING_NO); m_fixedPipelineDrawing->m_annotationDrawing->drawModelSpaceAnnotationsOnVolumeSlice(&inputs, slicePlane, diff --git a/src/Brain/DisplayPropertiesAnnotation.cxx b/src/Brain/DisplayPropertiesAnnotation.cxx index 71389ade5..a0161e89c 100644 --- a/src/Brain/DisplayPropertiesAnnotation.cxx +++ b/src/Brain/DisplayPropertiesAnnotation.cxx @@ -89,6 +89,8 @@ DisplayPropertiesAnnotation::updateForNewAnnotation(const Annotation* annotation switch (annotation->getCoordinateSpace()) { case AnnotationCoordinateSpaceEnum::CHART: break; + case AnnotationCoordinateSpaceEnum::SPACER: + break; case AnnotationCoordinateSpaceEnum::STEREOTAXIC: break; case AnnotationCoordinateSpaceEnum::SURFACE: @@ -415,6 +417,9 @@ DisplayPropertiesAnnotation::restoreVersionOne(const SceneClass* sceneClass) case AnnotationCoordinateSpaceEnum::CHART: CaretAssertMessage(0, "This should never happen as CHART SPACE was never available in a version one scene"); break; + case AnnotationCoordinateSpaceEnum::SPACER: + CaretAssertMessage(0, "This should never happen as SPACER TAB SPACE was never available in a version one scene"); + break; case AnnotationCoordinateSpaceEnum::STEREOTAXIC: if (stereoAnnDisplay != NULL) { for (int32_t iTab = 0; iTab < BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS; iTab++) { diff --git a/src/Brain/FtglFontTextRenderer.cxx b/src/Brain/FtglFontTextRenderer.cxx index f87570560..552992d9d 100644 --- a/src/Brain/FtglFontTextRenderer.cxx +++ b/src/Brain/FtglFontTextRenderer.cxx @@ -196,6 +196,8 @@ FtglFontTextRenderer::getFont(const AnnotationText& annotationText, switch (annotationText.getCoordinateSpace()) { case AnnotationCoordinateSpaceEnum::CHART: break; + case AnnotationCoordinateSpaceEnum::SPACER: + break; case AnnotationCoordinateSpaceEnum::STEREOTAXIC: break; case AnnotationCoordinateSpaceEnum::SURFACE: diff --git a/src/Brain/SpacerTabContent.cxx b/src/Brain/SpacerTabContent.cxx index d227b7468..1d7efb7ad 100644 --- a/src/Brain/SpacerTabContent.cxx +++ b/src/Brain/SpacerTabContent.cxx @@ -85,6 +85,15 @@ SpacerTabContent::getTabNamePrefix() const return s; } +/** + * @return The spacer tab index. + */ +SpacerTabIndex +SpacerTabContent::getSpacerTabIndex() const +{ + return m_key; +} + /** * @return The Window index */ diff --git a/src/Brain/SpacerTabContent.h b/src/Brain/SpacerTabContent.h index 565d1b36e..5f4faf8ba 100644 --- a/src/Brain/SpacerTabContent.h +++ b/src/Brain/SpacerTabContent.h @@ -49,6 +49,8 @@ namespace caret { virtual AString getTabNamePrefix() const override; + SpacerTabIndex getSpacerTabIndex() const; + int32_t getWindowIndex() const; int32_t getRowIndex() const; diff --git a/src/Common/CMakeLists.txt b/src/Common/CMakeLists.txt index 89ae2cfff..451bf351c 100644 --- a/src/Common/CMakeLists.txt +++ b/src/Common/CMakeLists.txt @@ -140,6 +140,7 @@ ProgressObject.h ProgressReportingInterface.h ReductionEnum.h ReductionOperation.h +SpacerTabIndex.h SpecFileDialogViewFilesTypeEnum.h SpeciesEnum.h StereotaxicSpaceEnum.h @@ -240,6 +241,7 @@ ProgramParametersException.cxx ProgressObject.cxx ReductionEnum.cxx ReductionOperation.cxx +SpacerTabIndex.cxx SpecFileDialogViewFilesTypeEnum.cxx SpeciesEnum.cxx StereotaxicSpaceEnum.cxx diff --git a/src/Common/EventGetViewportSize.cxx b/src/Common/EventGetViewportSize.cxx index 41908bfd4..5ed299117 100644 --- a/src/Common/EventGetViewportSize.cxx +++ b/src/Common/EventGetViewportSize.cxx @@ -37,31 +37,15 @@ using namespace caret; */ /** - * Constructor for finding a specific tab index. - * - * @param tabIndex - * Index of tab for which viewport size is requested. - */ -//EventGetViewportSize::EventGetViewportSize(const int32_t tabIndex) -//: Event(EventTypeEnum::EVENT_BROWSER_TAB_GET_VIEWPORT_SIZE), -//m_mode(MODE_TAB_INDEX), -//m_tabIndex(tabIndex), -//m_viewportValid(false) -//{ -// m_viewport[0] = 0; -// m_viewport[1] = 0; -// m_viewport[2] = 0; -// m_viewport[3] = 0; -//} - -/** - * Constructor for finding a surface or volume montage. + * Constructor for finding size of a viewport * * @param mode * The mode. + * @param index + * Index of a tab or window. */ EventGetViewportSize::EventGetViewportSize(const Mode mode, - const int32_t index) + const int32_t index) : Event(EventTypeEnum::EVENT_GET_VIEWPORT_SIZE), m_mode(mode), m_index(index), @@ -73,6 +57,22 @@ m_viewportValid(false) m_viewport[3] = 0; } +/** + * Constructor for finding size of a viewport + * + * @param spacerTabIndex + * Index of the spacer tab + */ +EventGetViewportSize::EventGetViewportSize(const SpacerTabIndex& spacerTabIndex) +: Event(EventTypeEnum::EVENT_GET_VIEWPORT_SIZE), +m_mode(MODE_SPACER_TAB_INDEX), +m_index(-1), +m_spacerTabIndex(spacerTabIndex), +m_viewportValid(false) +{ + +} + /** * Destructor. */ diff --git a/src/Common/EventGetViewportSize.h b/src/Common/EventGetViewportSize.h index 5587a2b8c..cd29f9431 100644 --- a/src/Common/EventGetViewportSize.h +++ b/src/Common/EventGetViewportSize.h @@ -23,8 +23,7 @@ #include "Event.h" - - +#include "SpacerTabIndex.h" namespace caret { @@ -32,16 +31,18 @@ namespace caret { public: enum Mode { + MODE_SPACER_TAB_INDEX, MODE_SURFACE_MONTAGE, MODE_TAB_BEFORE_MARGINS_INDEX, MODE_TAB_AFTER_MARGINS_INDEX, MODE_VOLUME_MONTAGE, MODE_WINDOW_INDEX, }; -// EventGetViewportSize(const int32_t tabIndex); EventGetViewportSize(const Mode mode, - const int32_t index); + const int32_t index); + + EventGetViewportSize(const SpacerTabIndex& spacerTabIndex); virtual ~EventGetViewportSize(); @@ -64,11 +65,13 @@ namespace caret { const Mode m_mode; - const int32_t m_index; + const int32_t m_index = -1; - int32_t m_viewport[4]; + SpacerTabIndex m_spacerTabIndex; + + bool m_viewportValid = false; - bool m_viewportValid; + int32_t m_viewport[4]; // ADD_NEW_MEMBERS_HERE diff --git a/src/Scenes/SpacerTabIndex.cxx b/src/Common/SpacerTabIndex.cxx similarity index 63% rename from src/Scenes/SpacerTabIndex.cxx rename to src/Common/SpacerTabIndex.cxx index 0b6ba906c..ea6d3de73 100644 --- a/src/Scenes/SpacerTabIndex.cxx +++ b/src/Common/SpacerTabIndex.cxx @@ -24,8 +24,6 @@ #undef __SPACER_TAB_INDEX_DECLARE__ #include "CaretAssert.h" -#include "SceneClass.h" -#include "SceneClassAssistant.h" using namespace caret; @@ -46,13 +44,6 @@ m_windowIndex(-1), m_rowIndex(-1), m_columnIndex(-1) { - m_sceneAssistant = std::unique_ptr(new SceneClassAssistant()); - m_sceneAssistant->add("m_windowIndex", - &m_windowIndex); - m_sceneAssistant->add("m_rowIndex", - &m_rowIndex); - m_sceneAssistant->add("m_columnIndex", - &m_columnIndex); } /** @@ -66,13 +57,6 @@ m_windowIndex(windowIndex), m_rowIndex(rowIndex), m_columnIndex(columnIndex) { - m_sceneAssistant = std::unique_ptr(new SceneClassAssistant()); - m_sceneAssistant->add("m_windowIndex", - &m_windowIndex); - m_sceneAssistant->add("m_rowIndex", - &m_rowIndex); - m_sceneAssistant->add("m_columnIndex", - &m_columnIndex); } /** @@ -123,6 +107,19 @@ SpacerTabIndex::copyHelperSpacerTabIndex(const SpacerTabIndex& obj) m_columnIndex = obj.m_columnIndex; } +/** + * Inequality operator. + * @param obj + * Instance compared to this for equality. + * @return + * True if this instance and 'obj' instance are considered NOT equal. + */ +bool +SpacerTabIndex::operator!=(const SpacerTabIndex& obj) const +{ + return ( ! (*this == obj)); +} + /** * Equality operator. * @param obj @@ -169,6 +166,22 @@ SpacerTabIndex::operator<(const SpacerTabIndex& rhs) const return false; } +/** + * @return True if the index is valid, else false. + */ +bool +SpacerTabIndex::isValid() const +{ + if ((m_windowIndex >= 0) + && (m_rowIndex >= 0) + && (m_columnIndex >= 0)) { + return true; + } + + return false; +} + + /** * @return The window index */ @@ -196,73 +209,94 @@ SpacerTabIndex::getColumnIndex() const return m_columnIndex; } +/** + * Reset to invalid indices. + */ +void +SpacerTabIndex::reset() +{ + m_windowIndex = -1; + m_rowIndex = -1; + m_columnIndex = -1; +} + /** - * Get a description of this object's content. - * @return String describing this object's content. + * @return Format for XML attribute (window index, row index, + * and column index separated by commas). + * NOTE: THESE INDICES START AT ZERO */ -AString -SpacerTabIndex::toString() const +AString +SpacerTabIndex::getXmlAttributeText() const { - AString s("SpacerTabIndex: windowIndex=%1, rowIndex=%2, columnIndex=%3"); + AString s("%1,%2,%3"); s = s.arg(m_windowIndex).arg(m_rowIndex).arg(m_columnIndex); - return s; } /** - * Save information specific to this type of model to the scene. - * - * @param sceneAttributes - * Attributes for the scene. Scenes may be of different types - * (full, generic, etc) and the attributes should be checked when - * saving the scene. - * - * @param instanceName - * Name of instance in the scene. + * Set from XML text in the form (w,r,c) where 'w' is the + * window index, 'r' is the row index, and 'c' is the + * column index. + * NOTE: THESE INDICES START AT ZERO */ -SceneClass* -SpacerTabIndex::saveToScene(const SceneAttributes* sceneAttributes, - const AString& instanceName) +void +SpacerTabIndex::setFromXmlAttributeText(const AString& text) { - SceneClass* sceneClass = new SceneClass(instanceName, - "SpacerTabIndex", - 1); - m_sceneAssistant->saveMembers(sceneAttributes, - sceneClass); + reset(); - // Uncomment if sub-classes must save to scene - //saveSubClassDataToScene(sceneAttributes, - // sceneClass); - - return sceneClass; + if ( ! text.isEmpty()) { + std::vector indices; + AString::toNumbers(text, indices); + if (indices.size() >= 3) { + m_windowIndex = indices[0]; + m_rowIndex = indices[1]; + m_columnIndex = indices[2]; + } + } } /** - * Restore information specific to the type of model from the scene. - * - * @param sceneAttributes - * Attributes for the scene. Scenes may be of different types - * (full, generic, etc) and the attributes should be checked when - * restoring the scene. - * - * @param sceneClass - * sceneClass from which model specific information is obtained. + * @return Row and column in text form (eg: "Row=2, Column=3") + * for use in the GUI and viewed by user. + * NOTE: THESE INDICES START AT ONE */ -void -SpacerTabIndex::restoreFromScene(const SceneAttributes* sceneAttributes, - const SceneClass* sceneClass) +AString +SpacerTabIndex::getRowColumnGuiText() const { - if (sceneClass == NULL) { - return; - } - - m_sceneAssistant->restoreMembers(sceneAttributes, - sceneClass); - - //Uncomment if sub-classes must restore from scene - //restoreSubClassDataFromScene(sceneAttributes, - // sceneClass); + AString s("Row=%1, Column=%2"); + s = s.arg(m_rowIndex+1).arg(m_columnIndex+1); + return s; +} + +/** + * @return Window, row, and column in text form (eg: "Window=1, Row=2, Column=3") + * for use in the GUI and viewed by user. + * NOTE: THESE INDICES START AT ONE + */ +AString +SpacerTabIndex::getWindowRowColumnGuiText() const +{ + AString s("Window=%1, "); + s = s.arg(m_windowIndex+1); + s.append(getRowColumnGuiText()); + return s; +} + + + +/** + * Get a description of this object's content. + * @return String describing this object's content. + * NOTE: THESE INDICES START AT ZERO + */ +AString +SpacerTabIndex::toString() const +{ + AString s("SpacerTabIndex: windowIndex=%1, rowIndex=%2, columnIndex=%3"); + s = s.arg(m_windowIndex).arg(m_rowIndex).arg(m_columnIndex); + + return s; } diff --git a/src/Scenes/SpacerTabIndex.h b/src/Common/SpacerTabIndex.h similarity index 66% rename from src/Scenes/SpacerTabIndex.h rename to src/Common/SpacerTabIndex.h index 1a896b293..8274db2a6 100644 --- a/src/Scenes/SpacerTabIndex.h +++ b/src/Common/SpacerTabIndex.h @@ -27,13 +27,10 @@ #include "CaretObject.h" -#include "SceneableInterface.h" - - namespace caret { class SceneClassAssistant; - class SpacerTabIndex : public CaretObject, public SceneableInterface { + class SpacerTabIndex : public CaretObject { public: SpacerTabIndex(); @@ -50,44 +47,35 @@ namespace caret { bool operator==(const SpacerTabIndex& obj) const; + bool operator!=(const SpacerTabIndex& obj) const; + bool operator<(const SpacerTabIndex& rhs) const; + bool isValid() const; + + void reset(); + int32_t getWindowIndex() const; int32_t getRowIndex() const; int32_t getColumnIndex() const; + AString getRowColumnGuiText() const; + + AString getWindowRowColumnGuiText() const; + + AString getXmlAttributeText() const; + + void setFromXmlAttributeText(const AString& text); + // ADD_NEW_METHODS_HERE virtual AString toString() const; - - virtual SceneClass* saveToScene(const SceneAttributes* sceneAttributes, - const AString& instanceName); - - virtual void restoreFromScene(const SceneAttributes* sceneAttributes, - const SceneClass* sceneClass); - - - - - - -// If there will be sub-classes of this class that need to save -// and restore data from scenes, these pure virtual methods can -// be uncommented to force their implementation by sub-classes. -// protected: -// virtual void saveSubClassDataToScene(const SceneAttributes* sceneAttributes, -// SceneClass* sceneClass) = 0; -// -// virtual void restoreSubClassDataFromScene(const SceneAttributes* sceneAttributes, -// const SceneClass* sceneClass) = 0; private: void copyHelperSpacerTabIndex(const SpacerTabIndex& obj); - std::unique_ptr m_sceneAssistant; - int32_t m_windowIndex; int32_t m_rowIndex; int32_t m_columnIndex; diff --git a/src/Files/AnnotationFile.cxx b/src/Files/AnnotationFile.cxx index 5660e4db3..b6884cfd9 100644 --- a/src/Files/AnnotationFile.cxx +++ b/src/Files/AnnotationFile.cxx @@ -237,10 +237,15 @@ AnnotationGroup* AnnotationFile::getSpaceAnnotationGroup(const Annotation* annotation) { const AnnotationCoordinateSpaceEnum::Enum annotationSpace = annotation->getCoordinateSpace(); + SpacerTabIndex annotationSpacerTabIndex; + int32_t annotationTabOrWindowIndex = -1; switch (annotationSpace) { case AnnotationCoordinateSpaceEnum::CHART: break; + case AnnotationCoordinateSpaceEnum::SPACER: + annotationSpacerTabIndex = annotation->getSpacerTabIndex(); + break; case AnnotationCoordinateSpaceEnum::STEREOTAXIC: break; case AnnotationCoordinateSpaceEnum::SURFACE: @@ -267,6 +272,11 @@ AnnotationFile::getSpaceAnnotationGroup(const Annotation* annotation) case AnnotationCoordinateSpaceEnum::SURFACE: return group; break; + case AnnotationCoordinateSpaceEnum::SPACER: + if (annotationSpacerTabIndex == group->getSpacerTabIndex()) { + return group; + } + break; case AnnotationCoordinateSpaceEnum::VIEWPORT: CaretAssert(0); break; @@ -290,6 +300,8 @@ AnnotationFile::getSpaceAnnotationGroup(const Annotation* annotation) CaretAssert((annotationTabOrWindowIndex >= 0) && (annotationTabOrWindowIndex < BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS)); break; + case AnnotationCoordinateSpaceEnum::SPACER: + break; case AnnotationCoordinateSpaceEnum::VIEWPORT: CaretAssert(0); break; @@ -303,7 +315,8 @@ AnnotationFile::getSpaceAnnotationGroup(const Annotation* annotation) AnnotationGroupTypeEnum::SPACE, generateUniqueKey(), annotationSpace, - annotationTabOrWindowIndex); + annotationTabOrWindowIndex, + annotationSpacerTabIndex); group->setItemParent(this); m_annotationGroups.push_back(QSharedPointer(group)); @@ -638,6 +651,7 @@ void AnnotationFile::addAnnotationGroupDuringFileReading(const AnnotationGroupTypeEnum::Enum groupType, const AnnotationCoordinateSpaceEnum::Enum coordinateSpace, const int32_t tabOrWindowIndex, + const SpacerTabIndex& spacerTabIndex, const int32_t uniqueKey, const std::vector& annotations) { @@ -654,6 +668,12 @@ AnnotationFile::addAnnotationGroupDuringFileReading(const AnnotationGroupTypeEnu switch (coordinateSpace) { case AnnotationCoordinateSpaceEnum::CHART: break; + case AnnotationCoordinateSpaceEnum::SPACER: + if ( ! spacerTabIndex.isValid()) { + throw DataFileException("Invalid spacer tab index for group while reading annotation file: " + + spacerTabIndex.toString()); + } + break; case AnnotationCoordinateSpaceEnum::STEREOTAXIC: break; case AnnotationCoordinateSpaceEnum::SURFACE: @@ -707,6 +727,15 @@ AnnotationFile::addAnnotationGroupDuringFileReading(const AnnotationGroupTypeEnu + AnnotationCoordinateSpaceEnum::toGuiName(coordinateSpace) + ". Only one space group for each space is allowed."); break; + case AnnotationCoordinateSpaceEnum::SPACER: + if (spacerTabIndex == group->getSpacerTabIndex()) { + throw DataFileException("There is more than one annotation space group with space " + + AnnotationCoordinateSpaceEnum::toGuiName(coordinateSpace) + + " for spacer tab " + + spacerTabIndex.toString() + + ". Only one space group for each space is allowed."); + } + break; case AnnotationCoordinateSpaceEnum::TAB: if (tabOrWindowIndex == group->getTabOrWindowIndex()) { throw DataFileException("There is more than one annotation space group with space " @@ -738,7 +767,8 @@ AnnotationFile::addAnnotationGroupDuringFileReading(const AnnotationGroupTypeEnu groupType, uniqueKey, coordinateSpace, - tabOrWindowIndex); + tabOrWindowIndex, + spacerTabIndex); for (std::vector::const_iterator annIter = annotations.begin(); annIter != annotations.end(); annIter++) { @@ -1093,7 +1123,8 @@ AnnotationFile::processGroupingAnnotations(EventAnnotationGrouping* groupingEven AnnotationGroupTypeEnum::USER, generateUniqueKey(), spaceGroup->getCoordinateSpace(), - spaceGroup->getTabOrWindowIndex()); + spaceGroup->getTabOrWindowIndex(), + spaceGroup->getSpacerTabIndex()); for (std::vector >::iterator annPtrIter = movedAnnotations.begin(); annPtrIter != movedAnnotations.end(); @@ -1246,7 +1277,8 @@ AnnotationFile::processRegroupingAnnotations(EventAnnotationGrouping* groupingEv AnnotationGroupTypeEnum::USER, reuseUniqueKeyOrGenerateNewUniqueKey(userGroupUniqueKey), spaceGroup->getCoordinateSpace(), - spaceGroup->getTabOrWindowIndex()); + spaceGroup->getTabOrWindowIndex(), + spaceGroup->getSpacerTabIndex()); bool allValidFlag = true; std::vector > movedAnnotations; @@ -1760,7 +1792,8 @@ AnnotationFile::appendContentFromDataFile(const DataFileContentCopyMoveParameter AnnotationGroupTypeEnum::USER, generateUniqueKey(), groupToCopy->getCoordinateSpace(), - groupToCopy->getTabOrWindowIndex()); + groupToCopy->getTabOrWindowIndex(), + groupToCopy->getSpacerTabIndex()); group->setItemParent(this); m_annotationGroups.push_back(QSharedPointer(group)); break; diff --git a/src/Files/AnnotationFile.h b/src/Files/AnnotationFile.h index 1bfde8e63..ac6c9ee41 100644 --- a/src/Files/AnnotationFile.h +++ b/src/Files/AnnotationFile.h @@ -40,6 +40,7 @@ namespace caret { class AnnotationGroup; class DisplayGroupAndTabItemHelper; class SceneClassAssistant; + class SpacerTabIndex; class AnnotationFile : public CaretDataFile, @@ -182,6 +183,7 @@ namespace caret { void addAnnotationGroupDuringFileReading(const AnnotationGroupTypeEnum::Enum groupType, const AnnotationCoordinateSpaceEnum::Enum coordinateSpace, const int32_t tabOrWindowIndex, + const SpacerTabIndex& spacerTabIndex, const int32_t uniqueKey, const std::vector& annotations); diff --git a/src/Files/AnnotationFileXmlFormatBase.h b/src/Files/AnnotationFileXmlFormatBase.h index 66e58272b..8d006e8d1 100644 --- a/src/Files/AnnotationFileXmlFormatBase.h +++ b/src/Files/AnnotationFileXmlFormatBase.h @@ -92,6 +92,8 @@ namespace caret { static const QString ATTRIBUTE_ROTATION_ANGLE; + static const QString ATTRIBUTE_SPACER_TAB_INDEX; + static const QString ATTRIBUTE_TAB_INDEX; static const QString ATTRIBUTE_TAB_OR_WINDOW_INDEX; @@ -212,6 +214,8 @@ namespace caret { const QString AnnotationFileXmlFormatBase::ATTRIBUTE_ROTATION_ANGLE = "rotationAngle"; + const QString AnnotationFileXmlFormatBase::ATTRIBUTE_SPACER_TAB_INDEX = "spacerTabIndex"; + const QString AnnotationFileXmlFormatBase::ATTRIBUTE_TAB_INDEX = "tabIndex"; const QString AnnotationFileXmlFormatBase::ATTRIBUTE_TAB_OR_WINDOW_INDEX = "tabOrWindowIndex"; diff --git a/src/Files/AnnotationFileXmlReader.cxx b/src/Files/AnnotationFileXmlReader.cxx index aab0fb224..edf601885 100644 --- a/src/Files/AnnotationFileXmlReader.cxx +++ b/src/Files/AnnotationFileXmlReader.cxx @@ -567,6 +567,18 @@ AnnotationFileXmlReader::readAnnotationAttributes(Annotation* annotation, annotation->setWindowIndex(m_streamHelper->getRequiredAttributeIntValue(attributes, annotationElementName, ATTRIBUTE_WINDOW_INDEX)); + /* + * Spacer Tab Index added as part of WB-668 + */ + const AString spacerTabText = m_streamHelper->getOptionalAttributeStringValue(attributes, + annotationElementName, + ATTRIBUTE_SPACER_TAB_INDEX, + ""); + SpacerTabIndex spacerTabIndex; + if ( ! spacerTabText.isEmpty()) { + spacerTabIndex.setFromXmlAttributeText(spacerTabText); + } + annotation->setSpacerTabIndex(spacerTabIndex); /* * Unique Key @@ -669,6 +681,18 @@ AnnotationFileXmlReader::readGroup(AnnotationFile* annotationFile) ELEMENT_GROUP, ATTRIBUTE_TAB_OR_WINDOW_INDEX); + /* + * Spacer Tab Index added as part of WB-668 + */ + const AString spacerTabText = m_streamHelper->getOptionalAttributeStringValue(attributes, + ELEMENT_GROUP, + ATTRIBUTE_SPACER_TAB_INDEX, + ""); + SpacerTabIndex spacerTabIndex; + if ( ! spacerTabText.isEmpty()) { + spacerTabIndex.setFromXmlAttributeText(spacerTabText); + } + const int32_t uniqueKey = m_streamHelper->getRequiredAttributeIntValue(attributes, ELEMENT_GROUP, ATTRIBUTE_UNIQUE_KEY); @@ -745,6 +769,7 @@ AnnotationFileXmlReader::readGroup(AnnotationFile* annotationFile) annotationFile->addAnnotationGroupDuringFileReading(groupType, coordSpace, tabOrWindowIndex, + spacerTabIndex, uniqueKey, annotations); } diff --git a/src/Files/AnnotationFileXmlWriter.cxx b/src/Files/AnnotationFileXmlWriter.cxx index 3b0edb917..6e2737a69 100644 --- a/src/Files/AnnotationFileXmlWriter.cxx +++ b/src/Files/AnnotationFileXmlWriter.cxx @@ -239,6 +239,9 @@ AnnotationFileXmlWriter::writeGroup(const AnnotationGroup* group) AnnotationGroupTypeEnum::toName(group->getGroupType())); m_stream->writeAttribute(ATTRIBUTE_TAB_OR_WINDOW_INDEX, QString::number(group->getTabOrWindowIndex())); + m_stream->writeAttribute(ATTRIBUTE_SPACER_TAB_INDEX, + group->getSpacerTabIndex().getXmlAttributeText()); + m_stream->writeAttribute(ATTRIBUTE_UNIQUE_KEY, QString::number(group->getUniqueKey())); @@ -500,6 +503,9 @@ AnnotationFileXmlWriter::getAnnotationPropertiesAsAttributes(const Annotation* a attributes.append(ATTRIBUTE_WINDOW_INDEX, QString::number(annotation->getWindowIndex())); + attributes.append(ATTRIBUTE_SPACER_TAB_INDEX, + annotation->getSpacerTabIndex().getXmlAttributeText()); + attributes.append(ATTRIBUTE_UNIQUE_KEY, QString::number(annotation->getUniqueKey())); } diff --git a/src/GuiQt/AnnotationChangeCoordinateDialog.cxx b/src/GuiQt/AnnotationChangeCoordinateDialog.cxx index aa6f28b1e..b54eee7c7 100644 --- a/src/GuiQt/AnnotationChangeCoordinateDialog.cxx +++ b/src/GuiQt/AnnotationChangeCoordinateDialog.cxx @@ -117,6 +117,9 @@ AnnotationChangeCoordinateDialog::createCurrentCoordinateWidget() switch (m_annotation->getCoordinateSpace()) { case AnnotationCoordinateSpaceEnum::CHART: break; + case AnnotationCoordinateSpaceEnum::SPACER: + spaceText += m_annotation->getSpacerTabIndex().getRowColumnGuiText(); + break; case AnnotationCoordinateSpaceEnum::STEREOTAXIC: break; case AnnotationCoordinateSpaceEnum::SURFACE: diff --git a/src/GuiQt/AnnotationCoordinateInformation.cxx b/src/GuiQt/AnnotationCoordinateInformation.cxx index ba8bbf869..83a4a03d6 100644 --- a/src/GuiQt/AnnotationCoordinateInformation.cxx +++ b/src/GuiQt/AnnotationCoordinateInformation.cxx @@ -37,6 +37,7 @@ #include "SelectionItemSurfaceNode.h" #include "SelectionItemVoxel.h" #include "SelectionManager.h" +#include "SpacerTabContent.h" #include "Surface.h" using namespace caret; @@ -72,11 +73,12 @@ AnnotationCoordinateInformation::~AnnotationCoordinateInformation() */ void AnnotationCoordinateInformation::reset() { - m_modelSpaceInfo = ModelSpaceInfo(); - m_tabSpaceInfo = TabWindowSpaceInfo(); - m_windowSpaceInfo = TabWindowSpaceInfo(); - m_chartSpaceInfo = ChartSpaceInfo(); + m_modelSpaceInfo = ModelSpaceInfo(); + m_tabSpaceInfo = TabWindowSpaceInfo(); + m_windowSpaceInfo = TabWindowSpaceInfo(); + m_chartSpaceInfo = ChartSpaceInfo(); m_surfaceSpaceInfo = SurfaceSpaceInfo(); + m_spacerTabSpaceInfo = SpacerTabSpaceInfo(); } bool @@ -88,6 +90,9 @@ AnnotationCoordinateInformation::isCoordinateSpaceValid(const AnnotationCoordina case AnnotationCoordinateSpaceEnum::CHART: validSpaceFlag = m_chartSpaceInfo.m_validFlag; break; + case AnnotationCoordinateSpaceEnum::SPACER: + validSpaceFlag = m_spacerTabSpaceInfo.m_spacerTabIndex.isValid(); + break; case AnnotationCoordinateSpaceEnum::STEREOTAXIC: validSpaceFlag = m_modelSpaceInfo.m_validFlag; break; @@ -140,6 +145,7 @@ AnnotationCoordinateInformation::getValidCoordinateSpaces(const AnnotationCoordi case AnnotationCoordinateSpaceEnum::VIEWPORT: break; case AnnotationCoordinateSpaceEnum::CHART: + case AnnotationCoordinateSpaceEnum::SPACER: case AnnotationCoordinateSpaceEnum::STEREOTAXIC: case AnnotationCoordinateSpaceEnum::SURFACE: case AnnotationCoordinateSpaceEnum::TAB: @@ -163,6 +169,12 @@ AnnotationCoordinateInformation::getValidCoordinateSpaces(const AnnotationCoordi if (coordInfoOne->m_tabSpaceInfo.m_index != coordInfoTwo->m_tabSpaceInfo.m_index) { addItFlag = false; } + break; + case AnnotationCoordinateSpaceEnum::SPACER: + if (coordInfoOne->m_spacerTabSpaceInfo.m_spacerTabIndex != coordInfoTwo->m_spacerTabSpaceInfo.m_spacerTabIndex) { + addItFlag = false; + } + break; case AnnotationCoordinateSpaceEnum::STEREOTAXIC: /* * Both coord info's must be in the SAME TAB @@ -170,6 +182,7 @@ AnnotationCoordinateInformation::getValidCoordinateSpaces(const AnnotationCoordi if (coordInfoOne->m_tabSpaceInfo.m_index != coordInfoTwo->m_tabSpaceInfo.m_index) { addItFlag = false; } + break; case AnnotationCoordinateSpaceEnum::SURFACE: /* * Both coord info's must be on same surface and @@ -361,6 +374,29 @@ AnnotationCoordinateInformation::createCoordinateInformationFromXY(BrainOpenGLWi } } + SpacerTabContent* spacerTabContent = viewportContent->getSpacerTabContent(); + if (spacerTabContent != NULL) { + int tabViewport[4]; + viewportContent->getModelViewport(tabViewport); + const float tabX = 100.0 * (windowX - tabViewport[0]) / static_cast(tabViewport[2]); + const float tabY = 100.0 * (windowY - tabViewport[1]) / static_cast(tabViewport[3]); + if ((tabX >= 0.0) + && (tabX < 100.0) + && (tabY >= 0.0) + && (tabY <= 100.0)) { + coordInfoOut.m_spacerTabSpaceInfo.m_xyz[0] = tabX; + coordInfoOut.m_spacerTabSpaceInfo.m_xyz[1] = tabY; + coordInfoOut.m_spacerTabSpaceInfo.m_xyz[2] = 0.0; + coordInfoOut.m_spacerTabSpaceInfo.m_pixelXYZ[0] = (windowX - tabViewport[0]); + coordInfoOut.m_spacerTabSpaceInfo.m_pixelXYZ[1] = (windowY - tabViewport[1]); + coordInfoOut.m_spacerTabSpaceInfo.m_pixelXYZ[2] = 0.0; + coordInfoOut.m_spacerTabSpaceInfo.m_spacerTabIndex = spacerTabContent->getSpacerTabIndex(); + coordInfoOut.m_spacerTabSpaceInfo.m_validFlag = spacerTabContent->getSpacerTabIndex().isValid(); + coordInfoOut.m_spacerTabSpaceInfo.m_width = tabViewport[2]; + coordInfoOut.m_spacerTabSpaceInfo.m_height = tabViewport[3]; + } + } + int windowViewport[4]; viewportContent->getWindowViewport(windowViewport); coordInfoOut.m_windowSpaceInfo.m_pixelXYZ[0] = windowX - windowViewport[0]; @@ -464,6 +500,40 @@ AnnotationCoordinateInformation::setOneDimAnnotationCoordinatesForSpace(Annotati } } break; + case AnnotationCoordinateSpaceEnum::SPACER: + if (coordInfoOne->m_spacerTabSpaceInfo.m_spacerTabIndex.isValid()) { + startCoordinate->setXYZ(coordInfoOne->m_spacerTabSpaceInfo.m_xyz); + annotation->setCoordinateSpace(AnnotationCoordinateSpaceEnum::SPACER); + annotation->setSpacerTabIndex(coordInfoOne->m_spacerTabSpaceInfo.m_spacerTabIndex); + + validCoordinateFlag = true; + + + if (coordInfoTwo != NULL) { + if (coordInfoTwo->m_spacerTabSpaceInfo.m_spacerTabIndex.isValid()) { + if (endCoordinate != NULL) { + endCoordinate->setXYZ(coordInfoTwo->m_spacerTabSpaceInfo.m_xyz); + } + } + } + else if (endCoordinate != NULL) { + double xyz[3] = { + coordInfoOne->m_spacerTabSpaceInfo.m_xyz[0], + coordInfoOne->m_spacerTabSpaceInfo.m_xyz[1], + coordInfoOne->m_spacerTabSpaceInfo.m_xyz[2] + }; + if (xyz[1] > 50.0) { + xyz[1] -= 25.0; + endCoordinate->setXYZ(xyz); + } + else { + xyz[1] += 25.0; + endCoordinate->setXYZ(coordInfoOne->m_spacerTabSpaceInfo.m_xyz); + startCoordinate->setXYZ(xyz); + } + } + } + break; case AnnotationCoordinateSpaceEnum::STEREOTAXIC: if (coordInfoOne->m_modelSpaceInfo.m_validFlag) { startCoordinate->setXYZ(coordInfoOne->m_modelSpaceInfo.m_xyz); @@ -633,6 +703,27 @@ AnnotationCoordinateInformation::setTwoDimAnnotationCoordinatesForSpace(Annotati } } break; + case AnnotationCoordinateSpaceEnum::SPACER: + if (coordInfoOne->m_spacerTabSpaceInfo.m_spacerTabIndex.isValid()) { + coordinate->setXYZ(coordInfoOne->m_spacerTabSpaceInfo.m_xyz); + annotation->setCoordinateSpace(AnnotationCoordinateSpaceEnum::SPACER); + annotation->setSpacerTabIndex(coordInfoOne->m_spacerTabSpaceInfo.m_spacerTabIndex); + + validCoordinateFlag = true; + + if (optionalCoordInfoTwo != NULL) { + if (optionalCoordInfoTwo->m_spacerTabSpaceInfo.m_spacerTabIndex.isValid() == coordInfoOne->m_spacerTabSpaceInfo.m_spacerTabIndex.isValid()) { + float centerXYZ[3] = { + (coordInfoOne->m_spacerTabSpaceInfo.m_xyz[0] + optionalCoordInfoTwo->m_spacerTabSpaceInfo.m_xyz[0]) / 2.0f, + (coordInfoOne->m_spacerTabSpaceInfo.m_xyz[1] + optionalCoordInfoTwo->m_spacerTabSpaceInfo.m_xyz[1]) / 2.0f, + (coordInfoOne->m_spacerTabSpaceInfo.m_xyz[2] + optionalCoordInfoTwo->m_spacerTabSpaceInfo.m_xyz[2]) / 2.0f + }; + coordinate->setXYZ(centerXYZ); + setWidthHeightWithTabCoordsFlag = true; + } + } + } + break; case AnnotationCoordinateSpaceEnum::STEREOTAXIC: if (coordInfoOne->m_modelSpaceInfo.m_validFlag) { coordinate->setXYZ(coordInfoOne->m_modelSpaceInfo.m_xyz); diff --git a/src/GuiQt/AnnotationCoordinateInformation.h b/src/GuiQt/AnnotationCoordinateInformation.h index 18782cda3..96daac169 100644 --- a/src/GuiQt/AnnotationCoordinateInformation.h +++ b/src/GuiQt/AnnotationCoordinateInformation.h @@ -23,6 +23,7 @@ #include "AnnotationCoordinateSpaceEnum.h" #include "AnnotationSurfaceOffsetVectorTypeEnum.h" +#include "SpacerTabIndex.h" #include "StructureEnum.h" class QLabel; @@ -87,7 +88,15 @@ namespace caret { float m_xyz[3] = { 0.0f, 0.0f, 0.0f }; float m_pixelXYZ[3] = { 0.0f, 0.0f, 0.0f }; int32_t m_index = -1; - + }; + + class SpacerTabSpaceInfo : public SpaceInfo { + public: + float m_width = 0.0f; + float m_height = 0.0f; + float m_xyz[3] = { 0.0f, 0.0f, 0.0f }; + float m_pixelXYZ[3] = { 0.0f, 0.0f, 0.0f }; + SpacerTabIndex m_spacerTabIndex; }; class ChartSpaceInfo : public SpaceInfo { @@ -111,6 +120,8 @@ namespace caret { TabWindowSpaceInfo m_windowSpaceInfo; + SpacerTabSpaceInfo m_spacerTabSpaceInfo; + ChartSpaceInfo m_chartSpaceInfo; SurfaceSpaceInfo m_surfaceSpaceInfo; diff --git a/src/GuiQt/AnnotationCoordinateSelectionWidget.cxx b/src/GuiQt/AnnotationCoordinateSelectionWidget.cxx index d4883287e..ea3541b8f 100644 --- a/src/GuiQt/AnnotationCoordinateSelectionWidget.cxx +++ b/src/GuiQt/AnnotationCoordinateSelectionWidget.cxx @@ -553,6 +553,9 @@ AnnotationCoordinateSelectionWidget::changeAnnotationCoordinate(Annotation* anno case AnnotationCoordinateSpaceEnum::CHART: oldViewportHeight = m_coordInfo.m_tabSpaceInfo.m_height; break; + case AnnotationCoordinateSpaceEnum::SPACER: + oldViewportHeight = m_coordInfo.m_spacerTabSpaceInfo.m_height; + break; case AnnotationCoordinateSpaceEnum::STEREOTAXIC: oldViewportHeight = m_coordInfo.m_tabSpaceInfo.m_height; break; @@ -588,6 +591,9 @@ AnnotationCoordinateSelectionWidget::changeAnnotationCoordinate(Annotation* anno case AnnotationCoordinateSpaceEnum::CHART: diffXyzValid = true; break; + case AnnotationCoordinateSpaceEnum::SPACER: + diffXyzValid = true; + break; case AnnotationCoordinateSpaceEnum::STEREOTAXIC: break; case AnnotationCoordinateSpaceEnum::SURFACE: @@ -620,6 +626,13 @@ AnnotationCoordinateSelectionWidget::changeAnnotationCoordinate(Annotation* anno newViewportHeight = m_coordInfo.m_tabSpaceInfo.m_height; } break; + case AnnotationCoordinateSpaceEnum::SPACER: + if (m_coordInfo.m_spacerTabSpaceInfo.m_spacerTabIndex.isValid()) { + coordinate->setXYZ(m_coordInfo.m_spacerTabSpaceInfo.m_xyz); + redoAnnotation->setCoordinateSpace(AnnotationCoordinateSpaceEnum::SPACER); + newViewportHeight = m_coordInfo.m_spacerTabSpaceInfo.m_height; + } + break; case AnnotationCoordinateSpaceEnum::STEREOTAXIC: if (m_coordInfo.m_modelSpaceInfo.m_validFlag) { coordinate->setXYZ(m_coordInfo.m_modelSpaceInfo.m_xyz); @@ -830,6 +843,10 @@ AnnotationCoordinateSelectionWidget::setWidthAndHeightForImage(AnnotationImage* vpWidth = m_coordInfo.m_tabSpaceInfo.m_width; vpHeight = m_coordInfo.m_tabSpaceInfo.m_height; break; + case AnnotationCoordinateSpaceEnum::SPACER: + vpWidth = m_coordInfo.m_spacerTabSpaceInfo.m_width; + vpHeight = m_coordInfo.m_spacerTabSpaceInfo.m_height; + break; case AnnotationCoordinateSpaceEnum::STEREOTAXIC: vpWidth = m_coordInfo.m_tabSpaceInfo.m_width; vpHeight = m_coordInfo.m_tabSpaceInfo.m_height; @@ -901,6 +918,8 @@ AnnotationCoordinateSelectionWidget::updateAnnotationDisplayProperties(const Ann switch (annotation->getCoordinateSpace()) { case AnnotationCoordinateSpaceEnum::CHART: break; + case AnnotationCoordinateSpaceEnum::SPACER: + break; case AnnotationCoordinateSpaceEnum::STEREOTAXIC: if (m_coordInfo.m_tabSpaceInfo.m_index >= 0) { } diff --git a/src/GuiQt/AnnotationCoordinateSpaceWidget.cxx b/src/GuiQt/AnnotationCoordinateSpaceWidget.cxx index 9d801cede..5d83631d9 100644 --- a/src/GuiQt/AnnotationCoordinateSpaceWidget.cxx +++ b/src/GuiQt/AnnotationCoordinateSpaceWidget.cxx @@ -119,6 +119,8 @@ AnnotationCoordinateSpaceWidget::updateContent(std::vector annotati switch (space) { case AnnotationCoordinateSpaceEnum::CHART: break; + case AnnotationCoordinateSpaceEnum::SPACER: + break; case AnnotationCoordinateSpaceEnum::STEREOTAXIC: break; case AnnotationCoordinateSpaceEnum::SURFACE: diff --git a/src/GuiQt/AnnotationCoordinateWidget.cxx b/src/GuiQt/AnnotationCoordinateWidget.cxx index b5f9aed03..4595898a1 100644 --- a/src/GuiQt/AnnotationCoordinateWidget.cxx +++ b/src/GuiQt/AnnotationCoordinateWidget.cxx @@ -401,6 +401,15 @@ AnnotationCoordinateWidget::updateContent(Annotation* annotation) } } break; + case AnnotationCoordinateSpaceEnum::SPACER: + xMin = percentageMinimum; + xMax = percentageMaximum; + yMin = percentageMinimum; + yMax = percentageMaximum; + zMin = zDepthMinimum; + zMax = zDepthMaximum; + suffix = "%"; + break; case AnnotationCoordinateSpaceEnum::STEREOTAXIC: xMin = coordinateMinimum; xMax = coordinateMaximum; @@ -561,6 +570,8 @@ AnnotationCoordinateWidget::valueChanged() switch (m_annotation->getCoordinateSpace()) { case AnnotationCoordinateSpaceEnum::CHART: break; + case AnnotationCoordinateSpaceEnum::SPACER: + break; case AnnotationCoordinateSpaceEnum::STEREOTAXIC: break; case AnnotationCoordinateSpaceEnum::SURFACE: diff --git a/src/GuiQt/AnnotationCreateDialog.cxx b/src/GuiQt/AnnotationCreateDialog.cxx index 51fc2ec2f..b269eb88a 100644 --- a/src/GuiQt/AnnotationCreateDialog.cxx +++ b/src/GuiQt/AnnotationCreateDialog.cxx @@ -280,6 +280,8 @@ AnnotationCreateDialog::createAnnotation(NewAnnotationInfo& newAnnotationInfo, case AnnotationCoordinateSpaceEnum::CHART: adjustTextPctSizeFlag = true; break; + case AnnotationCoordinateSpaceEnum::SPACER: + break; case AnnotationCoordinateSpaceEnum::STEREOTAXIC: adjustTextPctSizeFlag = true; break; @@ -334,6 +336,8 @@ AnnotationCreateDialog::createAnnotation(NewAnnotationInfo& newAnnotationInfo, case AnnotationCoordinateSpaceEnum::CHART: threeDimSpaceFlag = true; break; + case AnnotationCoordinateSpaceEnum::SPACER: + break; case AnnotationCoordinateSpaceEnum::STEREOTAXIC: threeDimSpaceFlag = true; break; @@ -977,6 +981,14 @@ AnnotationCreateDialog::NewAnnotationInfo::processTwoCoordInfo() } break; } + case AnnotationCoordinateSpaceEnum::SPACER: + { + int viewport[4]; + m_mouseEvent.getViewportContent()->getModelViewport(viewport); + viewportWidth = viewport[2]; + viewportHeight = viewport[3]; + } + break; case AnnotationCoordinateSpaceEnum::TAB: { int viewport[4]; diff --git a/src/GuiQt/AnnotationFontWidget.cxx b/src/GuiQt/AnnotationFontWidget.cxx index 14509e044..b89ff8b2c 100644 --- a/src/GuiQt/AnnotationFontWidget.cxx +++ b/src/GuiQt/AnnotationFontWidget.cxx @@ -393,6 +393,8 @@ AnnotationFontWidget::updateFontSizeControls() switch (ann->getCoordinateSpace()) { case AnnotationCoordinateSpaceEnum::CHART: break; + case AnnotationCoordinateSpaceEnum::SPACER: + break; case AnnotationCoordinateSpaceEnum::STEREOTAXIC: sizeValue /= surfaceMontageRowCount; break; diff --git a/src/GuiQt/AnnotationInsertNewWidget.cxx b/src/GuiQt/AnnotationInsertNewWidget.cxx index 2a60f4ee1..1e5d5e380 100644 --- a/src/GuiQt/AnnotationInsertNewWidget.cxx +++ b/src/GuiQt/AnnotationInsertNewWidget.cxx @@ -101,6 +101,8 @@ m_browserWindowIndex(browserWindowIndex) m_spaceActionGroup); QToolButton* tabSpaceToolButton = createSpaceToolButton(AnnotationCoordinateSpaceEnum::TAB, m_spaceActionGroup); + QToolButton* spacerTabSpaceToolButton = createSpaceToolButton(AnnotationCoordinateSpaceEnum::SPACER, + m_spaceActionGroup); QToolButton* stereotaxicSpaceToolButton = createSpaceToolButton(AnnotationCoordinateSpaceEnum::STEREOTAXIC, m_spaceActionGroup); QToolButton* surfaceSpaceToolButton = createSpaceToolButton(AnnotationCoordinateSpaceEnum::SURFACE, @@ -122,6 +124,7 @@ m_browserWindowIndex(browserWindowIndex) shapeTextToolButton->setMaximumSize(mw, mh); chartSpaceToolButton->setMaximumSize(mw, mh); + spacerTabSpaceToolButton->setMaximumSize(mw, mh); tabSpaceToolButton->setMaximumSize(mw, mh); stereotaxicSpaceToolButton->setMaximumSize(mw, mh); surfaceSpaceToolButton->setMaximumSize(mw, mh); @@ -148,7 +151,7 @@ m_browserWindowIndex(browserWindowIndex) QLabel* insertLabel = new QLabel("Insert New"); gridLayout->addWidget(insertLabel, - 0, 0, 1, 8, + 0, 0, 1, 9, Qt::AlignHCenter); gridLayout->addLayout(fileLayout, @@ -161,14 +164,16 @@ m_browserWindowIndex(browserWindowIndex) 1, 2, Qt::AlignLeft); gridLayout->addWidget(chartSpaceToolButton, 1, 3); - gridLayout->addWidget(stereotaxicSpaceToolButton, + gridLayout->addWidget(spacerTabSpaceToolButton, 1, 4); - gridLayout->addWidget(surfaceSpaceToolButton, + gridLayout->addWidget(stereotaxicSpaceToolButton, 1, 5); - gridLayout->addWidget(tabSpaceToolButton, + gridLayout->addWidget(surfaceSpaceToolButton, 1, 6); - gridLayout->addWidget(windowSpaceToolButton, + gridLayout->addWidget(tabSpaceToolButton, 1, 7); + gridLayout->addWidget(windowSpaceToolButton, + 1, 8); gridLayout->setRowMinimumHeight(2, 2); @@ -207,20 +212,22 @@ m_browserWindowIndex(browserWindowIndex) 1, 2, Qt::AlignLeft); gridLayout->addWidget(chartSpaceToolButton, 1, 3); - gridLayout->addWidget(stereotaxicSpaceToolButton, + gridLayout->addWidget(spacerTabSpaceToolButton, 1, 4); - gridLayout->addWidget(surfaceSpaceToolButton, + gridLayout->addWidget(stereotaxicSpaceToolButton, 1, 5); - gridLayout->addWidget(tabSpaceToolButton, + gridLayout->addWidget(surfaceSpaceToolButton, 1, 6); - gridLayout->addWidget(windowSpaceToolButton, + gridLayout->addWidget(tabSpaceToolButton, 1, 7); + gridLayout->addWidget(windowSpaceToolButton, + 1, 8); QSpacerItem* rowSpaceItem = new QSpacerItem(5, 5, QSizePolicy::Fixed, QSizePolicy::Fixed); gridLayout->addItem(rowSpaceItem, - 2, 3, 1, 6); + 2, 3, 1, 7); gridLayout->addWidget(typeLabel, 3, 2, Qt::AlignLeft); @@ -309,6 +316,7 @@ AnnotationInsertNewWidget::enableDisableSpaceActions() const ModelTypeEnum::Enum modelType = model->getModelType(); bool chartSpaceValidFlag = false; + const bool spacerSpaceValidFlag = window->isTileTabsSelected(); bool surfaceSpaceValidFlag = false; bool stereotaxicSpaceValidFlag = false; @@ -353,6 +361,9 @@ AnnotationInsertNewWidget::enableDisableSpaceActions() case AnnotationCoordinateSpaceEnum::CHART: enableSpaceFlag = chartSpaceValidFlag; break; + case AnnotationCoordinateSpaceEnum::SPACER: + enableSpaceFlag = spacerSpaceValidFlag; + break; case AnnotationCoordinateSpaceEnum::STEREOTAXIC: enableSpaceFlag = stereotaxicSpaceValidFlag; break; @@ -609,6 +620,8 @@ AnnotationInsertNewWidget::createSpaceToolButton(const AnnotationCoordinateSpace switch (annotationSpace) { case AnnotationCoordinateSpaceEnum::CHART: break; + case AnnotationCoordinateSpaceEnum::SPACER: + break; case AnnotationCoordinateSpaceEnum::STEREOTAXIC: break; case AnnotationCoordinateSpaceEnum::SURFACE: @@ -687,6 +700,8 @@ AnnotationInsertNewWidget::createSpacePixmap(const QWidget* widget, switch (annotationSpace) { case AnnotationCoordinateSpaceEnum::CHART: break; + case AnnotationCoordinateSpaceEnum::SPACER: + break; case AnnotationCoordinateSpaceEnum::STEREOTAXIC: break; case AnnotationCoordinateSpaceEnum::SURFACE: diff --git a/src/GuiQt/AnnotationPasteDialog.cxx b/src/GuiQt/AnnotationPasteDialog.cxx index ffe390c1a..e6d7e2db2 100644 --- a/src/GuiQt/AnnotationPasteDialog.cxx +++ b/src/GuiQt/AnnotationPasteDialog.cxx @@ -297,6 +297,8 @@ AnnotationPasteDialog::pasteOneDimensionalShape(AnnotationOneDimensionalShape* o switch (oneDimShape->getCoordinateSpace()) { case AnnotationCoordinateSpaceEnum::CHART: break; + case AnnotationCoordinateSpaceEnum::SPACER: + break; case AnnotationCoordinateSpaceEnum::STEREOTAXIC: break; case AnnotationCoordinateSpaceEnum::SURFACE: diff --git a/src/GuiQt/AnnotationRotationWidget.cxx b/src/GuiQt/AnnotationRotationWidget.cxx index de9a2363d..0e2d17bd4 100644 --- a/src/GuiQt/AnnotationRotationWidget.cxx +++ b/src/GuiQt/AnnotationRotationWidget.cxx @@ -112,6 +112,8 @@ AnnotationRotationWidget::getValidOneDimAnnotation(Annotation* annotation) switch (oneDimAnn->getCoordinateSpace()) { case AnnotationCoordinateSpaceEnum::CHART: break; + case AnnotationCoordinateSpaceEnum::SPACER: + break; case AnnotationCoordinateSpaceEnum::STEREOTAXIC: break; case AnnotationCoordinateSpaceEnum::SURFACE: @@ -171,6 +173,8 @@ AnnotationRotationWidget::updateContent(std::vector& annotations) switch (oneDimAnn->getCoordinateSpace()) { case AnnotationCoordinateSpaceEnum::CHART: break; + case AnnotationCoordinateSpaceEnum::SPACER: + break; case AnnotationCoordinateSpaceEnum::STEREOTAXIC: break; case AnnotationCoordinateSpaceEnum::SURFACE: diff --git a/src/GuiQt/BrainBrowserWindow.cxx b/src/GuiQt/BrainBrowserWindow.cxx index 08adf6750..f1d2303dc 100644 --- a/src/GuiQt/BrainBrowserWindow.cxx +++ b/src/GuiQt/BrainBrowserWindow.cxx @@ -96,6 +96,7 @@ #include "SceneFile.h" #include "SceneWindowGeometry.h" #include "SessionManager.h" +#include "SpacerTabContent.h" #include "SpecFile.h" #include "SpecFileManagementDialog.h" #include "StructureEnumComboBox.h" @@ -326,6 +327,21 @@ BrainBrowserWindow::receiveEvent(Event* event) bool notBestViewportValid = false; switch (viewportSizeEvent->getMode()) { + case EventGetViewportSize::MODE_SPACER_TAB_INDEX: + for (std::vector::iterator vpIter = allViewportContent.begin(); + vpIter != allViewportContent.end(); + vpIter++) { + const BrainOpenGLViewportContent* vpContent = *vpIter; + if (vpContent != NULL) { + SpacerTabContent* stc = vpContent->getSpacerTabContent(); + if (stc->getSpacerTabIndex() == vpContent->getSpacerTabContent()->getSpacerTabIndex()) { + vpContent->getTabViewportBeforeApplyingMargins(viewport); + viewportValid = true; + break; + } + } + } + break; case EventGetViewportSize::MODE_SURFACE_MONTAGE: if (viewportSizeEvent->getIndex() == m_browserWindowIndex) { /* diff --git a/src/GuiQt/UserInputModeAnnotations.cxx b/src/GuiQt/UserInputModeAnnotations.cxx index 02953ba50..607ced486 100644 --- a/src/GuiQt/UserInputModeAnnotations.cxx +++ b/src/GuiQt/UserInputModeAnnotations.cxx @@ -407,6 +407,10 @@ UserInputModeAnnotations::keyPressEvent(const KeyEvent& keyEvent) case AnnotationCoordinateSpaceEnum::CHART: changeCoordFlag = true; break; + case AnnotationCoordinateSpaceEnum::SPACER: + changeCoordFlag = true; + moveOnePixelFlag = true; + break; case AnnotationCoordinateSpaceEnum::STEREOTAXIC: changeCoordFlag = true; break; @@ -470,6 +474,8 @@ UserInputModeAnnotations::keyPressEvent(const KeyEvent& keyEvent) switch (selectedAnnotation->getCoordinateSpace()) { case AnnotationCoordinateSpaceEnum::CHART: break; + case AnnotationCoordinateSpaceEnum::SPACER: + break; case AnnotationCoordinateSpaceEnum::STEREOTAXIC: break; case AnnotationCoordinateSpaceEnum::SURFACE: @@ -620,6 +626,8 @@ UserInputModeAnnotations::mouseLeftDrag(const MouseEvent& mouseEvent) switch (draggingCoordinateSpace) { case AnnotationCoordinateSpaceEnum::CHART: break; + case AnnotationCoordinateSpaceEnum::SPACER: + break; case AnnotationCoordinateSpaceEnum::STEREOTAXIC: break; case AnnotationCoordinateSpaceEnum::SURFACE: @@ -674,6 +682,16 @@ UserInputModeAnnotations::mouseLeftDrag(const MouseEvent& mouseEvent) } } break; + case AnnotationCoordinateSpaceEnum::SPACER: + { + int viewport[4]; + vpContent->getTabViewportBeforeApplyingMargins(viewport); + spaceOriginX = viewport[0]; + spaceOriginY = viewport[1]; + spaceWidth = viewport[2]; + spaceHeight = viewport[3]; + } + break; case AnnotationCoordinateSpaceEnum::STEREOTAXIC: { /* diff --git a/src/GuiQt/UserInputModeAnnotationsContextMenu.cxx b/src/GuiQt/UserInputModeAnnotationsContextMenu.cxx index dc94e111b..8a6d89e61 100644 --- a/src/GuiQt/UserInputModeAnnotationsContextMenu.cxx +++ b/src/GuiQt/UserInputModeAnnotationsContextMenu.cxx @@ -115,6 +115,8 @@ m_newAnnotationCreatedByContextMenu(NULL) case AnnotationCoordinateSpaceEnum::CHART: threeDimCoordFlag = true; break; + case AnnotationCoordinateSpaceEnum::SPACER: + break; case AnnotationCoordinateSpaceEnum::STEREOTAXIC: threeDimCoordFlag = true; break; diff --git a/src/Scenes/CMakeLists.txt b/src/Scenes/CMakeLists.txt index f6d5d8325..ae9c80404 100644 --- a/src/Scenes/CMakeLists.txt +++ b/src/Scenes/CMakeLists.txt @@ -69,7 +69,6 @@ SceneWriterInterface.h SceneWriterXml.h SceneXmlElements.h SceneableInterface.h -SpacerTabIndex.h BackgroundAndForegroundColorsSceneHelper.cxx DisplayGroupAndTabItemHelper.cxx @@ -104,7 +103,6 @@ SceneTypeEnum.cxx SceneUnsignedByte.cxx SceneUnsignedByteArray.cxx SceneWriterXml.cxx -SpacerTabIndex.cxx ) TARGET_LINK_LIBRARIES(Scenes ${CARET_QT5_LINK}) diff --git a/src/Scenes/DisplayGroupAndTabItemHelper.cxx b/src/Scenes/DisplayGroupAndTabItemHelper.cxx index a17a8a24b..261c71b2b 100644 --- a/src/Scenes/DisplayGroupAndTabItemHelper.cxx +++ b/src/Scenes/DisplayGroupAndTabItemHelper.cxx @@ -121,13 +121,15 @@ DisplayGroupAndTabItemHelper::copyHelperDisplayGroupAndTabItemHelper(const Displ m_expandedStatusInDisplayGroup[i] = obj.m_expandedStatusInDisplayGroup[i]; } for (int32_t i = 0; i < BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS; i++) { - m_selectedInTab[i] = m_selectedInTab[i]; - m_expandedStatusInTab[i] = m_expandedStatusInTab[i]; + m_selectedInTab[i] = obj.m_selectedInTab[i]; + m_expandedStatusInTab[i] = obj.m_expandedStatusInTab[i]; } for (int32_t i = 0; i < BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_WINDOWS; i++) { - m_selectedInWindow[i] = m_selectedInWindow[i]; - m_expandedInWindow[i] = m_expandedInWindow[i]; + m_selectedInWindow[i] = obj.m_selectedInWindow[i]; + m_expandedInWindow[i] = obj.m_expandedInWindow[i]; } + + m_selectedInSpacerTab = obj.m_selectedInSpacerTab; } /** @@ -151,6 +153,8 @@ DisplayGroupAndTabItemHelper::clearPrivate() m_selectedInWindow[i] = TriStateSelectionStatusEnum::SELECTED; m_expandedInWindow[i] = defaultExpandStatus; } + + m_selectedInSpacerTab = TriStateSelectionStatusEnum::SELECTED; } /** @@ -191,6 +195,9 @@ DisplayGroupAndTabItemHelper::initializeNewInstance() m_expandedInWindow, BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_WINDOWS, m_expandedInWindow[0]); + + m_sceneAssistant->add("m_selectedInSpacerTab", + &m_selectedInSpacerTab); } /** @@ -315,6 +322,32 @@ DisplayGroupAndTabItemHelper::setExpandedInWindow(const int32_t windowIndex, } +/** + * Get the selected status of this item in a spacer tab. + * + * @param windowIndex + * Index of browser window in which item is controlled/viewed. + * @return + * The selection status. + */ +TriStateSelectionStatusEnum::Enum +DisplayGroupAndTabItemHelper::getSelectedInSpacerTab() const +{ + return m_selectedInSpacerTab; +} + +/** + * Set the selected status of this item in a spacer tab. + * + * @param status + * New selection status. +*/ +void +DisplayGroupAndTabItemHelper::setSelectedInSpacerTab(const TriStateSelectionStatusEnum::Enum status) +{ + m_selectedInSpacerTab = status; +} + /** * Is this item expanded to display its children in the * selection controls? diff --git a/src/Scenes/DisplayGroupAndTabItemHelper.h b/src/Scenes/DisplayGroupAndTabItemHelper.h index edcaf24ed..e2db6bf66 100644 --- a/src/Scenes/DisplayGroupAndTabItemHelper.h +++ b/src/Scenes/DisplayGroupAndTabItemHelper.h @@ -72,6 +72,10 @@ namespace caret { void setExpandedInWindow(const int32_t windowIndex, const bool status); + TriStateSelectionStatusEnum::Enum getSelectedInSpacerTab() const; + + void setSelectedInSpacerTab(const TriStateSelectionStatusEnum::Enum status); + // ADD_NEW_METHODS_HERE virtual SceneClass* saveToScene(const SceneAttributes* sceneAttributes, @@ -123,6 +127,9 @@ namespace caret { /** Expanded (collapsed) status in window */ bool m_expandedInWindow[BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_WINDOWS]; + /** Selected in Spacer Window/Row/Column */ + TriStateSelectionStatusEnum::Enum m_selectedInSpacerTab; + // ADD_NEW_MEMBERS_HERE }; -- GitLab From 4f818c3832ea5c712020debb3fa13b6fe1c0f2fd Mon Sep 17 00:00:00 2001 From: John Harwell Date: Mon, 22 Oct 2018 12:38:59 -0500 Subject: [PATCH 086/427] Corrected AString::toNumbers() for instances in which the number are separated by a character other than a space. --- src/Common/AString.cxx | 41 +++++++++++++++++++++++------------------ 1 file changed, 23 insertions(+), 18 deletions(-) diff --git a/src/Common/AString.cxx b/src/Common/AString.cxx index 531cd1fad..b892ddba1 100644 --- a/src/Common/AString.cxx +++ b/src/Common/AString.cxx @@ -460,19 +460,6 @@ AString::toNumbers(const AString& s, numbersOut.push_back(floatValue); } } - -// AString copy = s; -// QTextStream stream(©); -// -// AString numberString; -// bool valid = false; -// while (stream.atEnd() == false) { -// stream >> numberString; -// const float floatValue = numberString.toFloat(&valid); -// if (valid) { -// numbersOut.push_back(floatValue); -// } -// } } /** @@ -495,12 +482,30 @@ AString::toNumbers(const AString& s, AString copy = s; QTextStream stream(©); - AString numberString; - bool valid = false; + int intValue; while (stream.atEnd() == false) { - stream >> numberString; - const int32_t intValue = numberString.toInt(&valid); - if (valid) { + /* + * Try to read a int value from the current position + */ + stream >> intValue; + + /* + * If the text stream could not create an int from + * the current text position, the corrupt data flag + * will be set. + */ + if (stream.status() == QTextStream::ReadCorruptData) { + /* + * Reset the status of the string (to OK) and + * then read one character to remove the character + * from the stream since it was not the start + * of a number. + */ + stream.resetStatus(); + QChar oneChar; + stream >> oneChar; + } + else { numbersOut.push_back(intValue); } } -- GitLab From 884fb9da5178c4fa0a1283ae7442847d527fb121 Mon Sep 17 00:00:00 2001 From: John Harwell Date: Mon, 22 Oct 2018 14:53:21 -0500 Subject: [PATCH 087/427] WB-668 Titling columns in tile tabs: Enabled grouping for annotations located in 'spacer tabs'. --- src/Brain/AnnotationArrangerExecutor.cxx | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Brain/AnnotationArrangerExecutor.cxx b/src/Brain/AnnotationArrangerExecutor.cxx index 0fb992f0e..aead692c6 100644 --- a/src/Brain/AnnotationArrangerExecutor.cxx +++ b/src/Brain/AnnotationArrangerExecutor.cxx @@ -542,6 +542,7 @@ AnnotationArrangerExecutor::getAnnotationsForArranging(const AnnotationArrangerI annotationsOut.clear(); std::vector spaces; + spaces.push_back(AnnotationCoordinateSpaceEnum::SPACER); spaces.push_back(AnnotationCoordinateSpaceEnum::TAB); spaces.push_back(AnnotationCoordinateSpaceEnum::WINDOW); -- GitLab From 6e2a50b1c07163c29e3de031cd71e85b7829d1b3 Mon Sep 17 00:00:00 2001 From: John Harwell Date: Wed, 24 Oct 2018 15:38:04 -0500 Subject: [PATCH 088/427] WB-668 Titling columns in tile tabs: Starting to implement a modification operation (delete row) for the current tile tabs configuration --- src/Common/CMakeLists.txt | 2 - src/Common/TileTabsConfiguration.h | 2 - src/GuiQt/BrainBrowserWindow.cxx | 27 ++ src/GuiQt/BrainBrowserWindowToolBar.cxx | 125 ++++-- src/GuiQt/BrainBrowserWindowToolBar.h | 4 +- src/GuiQt/CMakeLists.txt | 4 + .../EventBrowserWindowTileTabOperation.cxx | 33 +- .../EventBrowserWindowTileTabOperation.h | 11 +- ...EventTileTabsConfigurationModification.cxx | 25 +- .../EventTileTabsConfigurationModification.h | 9 +- src/GuiQt/TileTabsConfigurationDialog.cxx | 20 +- src/GuiQt/TileTabsConfigurationDialog.h | 6 +- src/GuiQt/TileTabsConfigurationModifier.cxx | 406 ++++++++++++++++++ src/GuiQt/TileTabsConfigurationModifier.h | 126 ++++++ src/GuiQt/UserInputTileTabsContextMenu.cxx | 4 +- 15 files changed, 747 insertions(+), 57 deletions(-) rename src/{Common => GuiQt}/EventTileTabsConfigurationModification.cxx (78%) rename src/{Common => GuiQt}/EventTileTabsConfigurationModification.h (89%) create mode 100644 src/GuiQt/TileTabsConfigurationModifier.cxx create mode 100644 src/GuiQt/TileTabsConfigurationModifier.h diff --git a/src/Common/CMakeLists.txt b/src/Common/CMakeLists.txt index 451bf351c..57394b626 100644 --- a/src/Common/CMakeLists.txt +++ b/src/Common/CMakeLists.txt @@ -105,7 +105,6 @@ EventListenerInterface.h EventManager.h EventPaletteGetByName.h EventProgressUpdate.h -EventTileTabsConfigurationModification.h EventTypeEnum.h FastStatistics.h FileAdapter.h @@ -211,7 +210,6 @@ EventListenerInterface.cxx EventManager.cxx EventPaletteGetByName.cxx EventProgressUpdate.cxx -EventTileTabsConfigurationModification.cxx EventTypeEnum.cxx FastStatistics.cxx FileAdapter.cxx diff --git a/src/Common/TileTabsConfiguration.h b/src/Common/TileTabsConfiguration.h index 1b59458a1..c49813f56 100644 --- a/src/Common/TileTabsConfiguration.h +++ b/src/Common/TileTabsConfiguration.h @@ -126,8 +126,6 @@ namespace caret { /** Unique identifier does not get copied */ AString m_uniqueIdentifier; - std::vector m_columnStretchFactors; - std::vector m_columns; std::vector m_rows; diff --git a/src/GuiQt/BrainBrowserWindow.cxx b/src/GuiQt/BrainBrowserWindow.cxx index f1d2303dc..0c4f9b086 100644 --- a/src/GuiQt/BrainBrowserWindow.cxx +++ b/src/GuiQt/BrainBrowserWindow.cxx @@ -104,6 +104,7 @@ #include "SurfaceMontageConfigurationAbstract.h" #include "SurfaceSelectionViewController.h" #include "TileTabsConfiguration.h" +#include "TileTabsConfigurationModifier.h" #include "WuQDataEntryDialog.h" #include "WuQDoubleSpinBox.h" #include "WuQMessageBox.h" @@ -2350,6 +2351,32 @@ void BrainBrowserWindow::modifyTileTabsConfiguration(EventTileTabsConfigurationModification* modEvent) { CaretAssert(modEvent); + + if (modEvent->getWindowIndex() != m_browserWindowIndex) { + return; + } + + std::vector vpContent; + if (isTileTabsSelected()) { + vpContent = m_openGLWidget->getViewportContent(); + } + + TileTabsConfigurationModifier modifier(vpContent, + modEvent); + + AString errorMessage; + if (! modifier.run(errorMessage)) { + WuQMessageBox::errorOk(this, errorMessage); + } + + /* + * Update graphics and GUI + */ + EventManager::get()->sendEvent(EventSurfaceColoringInvalidate().getPointer()); + EventManager::get()->sendEvent(EventUserInterfaceUpdate().getPointer()); + EventManager::get()->sendEvent(EventGraphicsUpdateAllWindows().getPointer()); + + modEvent->setEventProcessed(); } /** diff --git a/src/GuiQt/BrainBrowserWindowToolBar.cxx b/src/GuiQt/BrainBrowserWindowToolBar.cxx index 84a9673df..4b85301f5 100644 --- a/src/GuiQt/BrainBrowserWindowToolBar.cxx +++ b/src/GuiQt/BrainBrowserWindowToolBar.cxx @@ -659,6 +659,79 @@ BrainBrowserWindowToolBar::addNewTabWithContent(BrowserTabContent* tabContent) -1); } +/** + * Replace the current browser tabs with the given browser tabs. Tabs will be added + * or removed as needed. + * + * @param browserTabs + * Browser tabs for this window. + */ +void +BrainBrowserWindowToolBar::replaceBrowserTabs(const std::vector& browserTabs) +{ + const int32_t numTabs = static_cast(browserTabs.size()); + if (numTabs <= 0) { + return; + } + + QSignalBlocker blocker(this->tabBar); + + int32_t selectedTabIndex = this->tabBar->currentIndex(); + + /* + * Remove BrowserTabContent from all tabs since tabs may be + * closed and closing the tab will try to delete the BrowserTabContent + * in the tab but this BrowserTabContent may still be valid. + */ + const int32_t beforeNumTabs = this->tabBar->count(); + for (int32_t iTab = 0; iTab < beforeNumTabs; iTab++) { + this->tabBar->setTabData(iTab, + qVariantFromValue((void*)NULL)); + } + + /* + * Add/remove tabs as needed + */ + if (beforeNumTabs < numTabs) { + const int32_t numToAdd = numTabs - beforeNumTabs; + for (int32_t i = 0; i < numToAdd; i++) { + this->tabBar->addTab(""); + } + } + else if (beforeNumTabs > numTabs) { + const int32_t numToRemove = beforeNumTabs - numTabs; + for (int32_t i = 0; i < numToRemove; i++) { + const int32_t lastTabIndex = this->tabBar->count() - 1; + this->tabBar->removeTab(lastTabIndex); + } + } + CaretAssert(this->tabBar->count() == numTabs); + + /* + * Set content and update name for each tab + */ + for (int32_t iTab = 0; iTab < numTabs; iTab++) { + CaretAssertVectorIndex(browserTabs, iTab); + BrowserTabContent* btc = browserTabs[iTab]; + CaretAssert(btc); + this->tabBar->setTabData(iTab, + qVariantFromValue((void*)btc)); + this->updateTabName(iTab); + } + + + const int32_t numOpenTabs = this->tabBar->count(); + this->tabBar->setTabsClosable(numOpenTabs > 1); + + if (selectedTabIndex >= numTabs) { + selectedTabIndex = numTabs - 1; + } + if (selectedTabIndex < 0) { + selectedTabIndex = 0; + } + this->tabBar->setCurrentIndex(selectedTabIndex); +} + /** * Adds a new tab. */ @@ -747,31 +820,6 @@ BrainBrowserWindowToolBar::insertTabContentPrivate(const InsertTabMode insertTab "NewTab"); } break; - case InsertTabMode::AT_TAB_CONTENTS_INDEX: - { - const int32_t tabContentIndex = browserTabContent->getTabNumber(); - - const int32_t numTabs = this->tabBar->count(); - if (numTabs <= 0) { - newTabIndex = this->tabBar->addTab("NewTab"); - } - else { - int insertIndex = 0; - for (int32_t i = 0; i < numTabs; i++) { - if (tabContentIndex > this->getTabContentFromTab(i)->getTabNumber()) { - insertIndex = i + 1; - } - } - if (insertIndex >= numTabs) { - newTabIndex = this->tabBar->addTab("NewTab"); - } - else { - this->tabBar->insertTab(insertIndex, "NewTab"); - newTabIndex = insertIndex; - } - } - } - break; } this->tabBar->setTabData(newTabIndex, @@ -4141,21 +4189,28 @@ BrainBrowserWindowToolBar::receiveEvent(Event* event) if (tileTabsEvent->getWindowIndex() == this->browserWindowIndex) { const int32_t browserTabIndex = tileTabsEvent->getBrowserTabIndex(); int32_t tabBarIndex = getTabBarIndexWithBrowserTabIndex(browserTabIndex); - if (tabBarIndex >= 0) { - const EventBrowserWindowTileTabOperation::Operation operation = tileTabsEvent->getOperation(); - switch (operation) { - case EventBrowserWindowTileTabOperation::OPERATION_NEW_TAB_AFTER: + const EventBrowserWindowTileTabOperation::Operation operation = tileTabsEvent->getOperation(); + switch (operation) { + case EventBrowserWindowTileTabOperation::OPERATION_NEW_TAB_AFTER: + if (tabBarIndex >= 0) { insertNewTabAtTabBarIndex(tabBarIndex + 1); - break; - case EventBrowserWindowTileTabOperation::OPERATION_NEW_TAB_BEFORE: + } + break; + case EventBrowserWindowTileTabOperation::OPERATION_NEW_TAB_BEFORE: + if (tabBarIndex >= 0) { insertNewTabAtTabBarIndex(tabBarIndex); - break; - case EventBrowserWindowTileTabOperation::OPERATION_SELECT_TAB: + } + break; + case EventBrowserWindowTileTabOperation::OPERATION_SELECT_TAB: + if (tabBarIndex >= 0) { m_tileTabsHighlightingTimerEnabledFlag = false; this->tabBar->setCurrentIndex(tabBarIndex); m_tileTabsHighlightingTimerEnabledFlag = true; - break; - } + } + break; + case EventBrowserWindowTileTabOperation::OPERATION_REPLACE_TABS: + replaceBrowserTabs(tileTabsEvent->getBrowserTabsForReplaceOperation()); + break; } tileTabsEvent->setEventProcessed(); diff --git a/src/GuiQt/BrainBrowserWindowToolBar.h b/src/GuiQt/BrainBrowserWindowToolBar.h index 920e0d1dd..5e0388cc3 100644 --- a/src/GuiQt/BrainBrowserWindowToolBar.h +++ b/src/GuiQt/BrainBrowserWindowToolBar.h @@ -261,8 +261,7 @@ namespace caret { private: enum class InsertTabMode { APPEND, - AT_TAB_BAR_INDEX, - AT_TAB_CONTENTS_INDEX + AT_TAB_BAR_INDEX }; bool allowAddingNewTab(); @@ -277,6 +276,7 @@ namespace caret { void insertNewTabAtTabBarIndex(int32_t tabBarIndex); void insertAndCloneTabContentAtTabBarIndex(const BrowserTabContent* tabContentToBeCloned, const int32_t tabBarIndex); + void replaceBrowserTabs(const std::vector& browserTabs); BrowserTabContent* createNewTab(AString& errorMessage); diff --git a/src/GuiQt/CMakeLists.txt b/src/GuiQt/CMakeLists.txt index 4d401491f..7c166d9c0 100644 --- a/src/GuiQt/CMakeLists.txt +++ b/src/GuiQt/CMakeLists.txt @@ -343,6 +343,7 @@ EventOperatingSystemRequestOpenDataFile.h EventOverlaySettingsEditorDialogRequest.h EventPaletteColorMappingEditorDialogRequest.h EventShowDataFileReadWarningsDialog.h +EventTileTabsConfigurationModification.h EventUpdateInformationWindows.h EventUpdateVolumeEditingToolBar.h EventUpdateYokedWindows.h @@ -415,6 +416,7 @@ SurfacePropertiesEditorDialog.h SurfaceSelectionViewController.h ThresholdingSetMapsDialog.h TileTabsConfigurationDialog.h +TileTabsConfigurationModifier.h UserInputModeAbstract.h UserInputModeAnnotations.h UserInputModeAnnotationsContextMenu.h @@ -575,6 +577,7 @@ EventOperatingSystemRequestOpenDataFile.cxx EventOverlaySettingsEditorDialogRequest.cxx EventPaletteColorMappingEditorDialogRequest.cxx EventShowDataFileReadWarningsDialog.cxx +EventTileTabsConfigurationModification.cxx EventUpdateInformationWindows.cxx EventUpdateVolumeEditingToolBar.cxx EventUpdateYokedWindows.cxx @@ -647,6 +650,7 @@ SurfacePropertiesEditorDialog.cxx SurfaceSelectionViewController.cxx ThresholdingSetMapsDialog.cxx TileTabsConfigurationDialog.cxx +TileTabsConfigurationModifier.cxx UserInputModeAbstract.cxx UserInputModeAnnotations.cxx UserInputModeAnnotationsContextMenu.cxx diff --git a/src/GuiQt/EventBrowserWindowTileTabOperation.cxx b/src/GuiQt/EventBrowserWindowTileTabOperation.cxx index 52a52281a..9cd4f3788 100644 --- a/src/GuiQt/EventBrowserWindowTileTabOperation.cxx +++ b/src/GuiQt/EventBrowserWindowTileTabOperation.cxx @@ -54,16 +54,30 @@ using namespace caret; EventBrowserWindowTileTabOperation::EventBrowserWindowTileTabOperation(const Operation operation, QWidget* parentWidget, const int32_t windowIndex, - const int32_t browserTabIndex) + const int32_t browserTabIndex, + const std::vector& browserTabsForReplaceOperation) : Event(EventTypeEnum::EVENT_BROWSER_WINDOW_TILE_TAB_OPERATION), m_operation(operation), m_parentWidget(parentWidget), m_windowIndex(windowIndex), -m_browserTabIndex(browserTabIndex) +m_browserTabIndex(browserTabIndex), +m_browserTabsForReplaceOperation(browserTabsForReplaceOperation) { CaretAssert(m_parentWidget); CaretAssert(m_windowIndex >= 0); - CaretAssert(m_browserTabIndex >= 0); + switch (m_operation) { + case OPERATION_NEW_TAB_AFTER: + CaretAssert(m_browserTabIndex >= 0); + break; + case OPERATION_NEW_TAB_BEFORE: + CaretAssert(m_browserTabIndex >= 0); + break; + case OPERATION_REPLACE_TABS: + break; + case OPERATION_SELECT_TAB: + CaretAssert(m_browserTabIndex >= 0); + break; + } } /** @@ -88,10 +102,12 @@ EventBrowserWindowTileTabOperation::selectTabInWindow(QWidget* parentWidget, const int32_t windowIndex, const int32_t browserTabIndex) { + std::vector emptyBrowserTabs; EventBrowserWindowTileTabOperation tabOperation(Operation::OPERATION_SELECT_TAB, parentWidget, windowIndex, - browserTabIndex); + browserTabIndex, + emptyBrowserTabs); EventManager::get()->sendEvent(tabOperation.getPointer()); @@ -128,4 +144,13 @@ EventBrowserWindowTileTabOperation::getBrowserTabIndex() const return m_browserTabIndex; } +/** + * @return Get the browser tabs for a replace tabs operation. + */ +const std::vector +EventBrowserWindowTileTabOperation::getBrowserTabsForReplaceOperation() const +{ + return m_browserTabsForReplaceOperation; +} + diff --git a/src/GuiQt/EventBrowserWindowTileTabOperation.h b/src/GuiQt/EventBrowserWindowTileTabOperation.h index 39540e96e..87a6aa0de 100644 --- a/src/GuiQt/EventBrowserWindowTileTabOperation.h +++ b/src/GuiQt/EventBrowserWindowTileTabOperation.h @@ -24,6 +24,7 @@ #include +#include #include "Event.h" @@ -31,6 +32,8 @@ class QWidget; namespace caret { + class BrowserTabContent; + class EventBrowserWindowTileTabOperation : public Event { public: @@ -40,13 +43,15 @@ namespace caret { enum Operation { OPERATION_NEW_TAB_AFTER, OPERATION_NEW_TAB_BEFORE, + OPERATION_REPLACE_TABS, OPERATION_SELECT_TAB }; EventBrowserWindowTileTabOperation(const Operation operation, QWidget* parentWidget, const int32_t windowIndex, - const int32_t browserTabIndex); + const int32_t browserTabIndex, + const std::vector& browserTabsForReplaceOperation); virtual ~EventBrowserWindowTileTabOperation(); @@ -56,6 +61,8 @@ namespace caret { int32_t getBrowserTabIndex() const; + const std::vector getBrowserTabsForReplaceOperation() const; + // ADD_NEW_METHODS_HERE private: @@ -71,6 +78,8 @@ namespace caret { const int32_t m_browserTabIndex; + const std::vector m_browserTabsForReplaceOperation; + // ADD_NEW_MEMBERS_HERE }; diff --git a/src/Common/EventTileTabsConfigurationModification.cxx b/src/GuiQt/EventTileTabsConfigurationModification.cxx similarity index 78% rename from src/Common/EventTileTabsConfigurationModification.cxx rename to src/GuiQt/EventTileTabsConfigurationModification.cxx index 28fc5f4e0..43aa47f2e 100644 --- a/src/Common/EventTileTabsConfigurationModification.cxx +++ b/src/GuiQt/EventTileTabsConfigurationModification.cxx @@ -33,16 +33,27 @@ using namespace caret; /** * \class caret::EventTileTabsConfigurationModification * \brief Event for modifying a tile tabs configuration. - * \ingroup Common + * \ingroup GuiQt */ /** * Constructor. + * + * @param tileTabsConfiguration + * Tile tabs configuration that will be modified + * @param rowColumnIndex + * Index of the row or column used for modification + * @param rowColumnType + * 'Row' or "Column' type + * @param operation + * Enumerated type with type of modification. */ -EventTileTabsConfigurationModification::EventTileTabsConfigurationModification(const int32_t rowColumnIndex, +EventTileTabsConfigurationModification::EventTileTabsConfigurationModification(TileTabsConfiguration* tileTabsConfiguration, + const int32_t rowColumnIndex, const RowColumnType rowColumnType, const Operation operation) : Event(EventTypeEnum::EVENT_TILE_TABS_MODIFICATION), +m_tileTabsConfiguration(tileTabsConfiguration), m_rowColumnIndex(rowColumnIndex), m_rowColumnType(rowColumnType), m_operation(operation), @@ -58,6 +69,16 @@ EventTileTabsConfigurationModification::~EventTileTabsConfigurationModification( { } +/** + * @return Tile tabs configuration that is modified. + */ +TileTabsConfiguration* +EventTileTabsConfigurationModification::getTileTabsConfiguration() +{ + return m_tileTabsConfiguration; +} + + /** * @return Index of the window. */ diff --git a/src/Common/EventTileTabsConfigurationModification.h b/src/GuiQt/EventTileTabsConfigurationModification.h similarity index 89% rename from src/Common/EventTileTabsConfigurationModification.h rename to src/GuiQt/EventTileTabsConfigurationModification.h index 0ce15b07e..2d54d9e05 100644 --- a/src/Common/EventTileTabsConfigurationModification.h +++ b/src/GuiQt/EventTileTabsConfigurationModification.h @@ -31,6 +31,8 @@ namespace caret { + class TileTabsConfiguration; + class EventTileTabsConfigurationModification : public Event { public: @@ -53,7 +55,8 @@ namespace caret { MOVE_BEFORE }; - EventTileTabsConfigurationModification(const int32_t rowColumnIndex, + EventTileTabsConfigurationModification(TileTabsConfiguration* tileTabsConfiguration, + const int32_t rowColumnIndex, const RowColumnType rowColumnType, const Operation operation); @@ -63,6 +66,8 @@ namespace caret { EventTileTabsConfigurationModification& operator=(const EventTileTabsConfigurationModification&) = delete; + TileTabsConfiguration* getTileTabsConfiguration(); + int32_t getRowColumnIndex() const; RowColumnType getRowColumnType() const; @@ -76,6 +81,8 @@ namespace caret { // ADD_NEW_METHODS_HERE private: + TileTabsConfiguration* m_tileTabsConfiguration; + const int32_t m_rowColumnIndex; const RowColumnType m_rowColumnType; diff --git a/src/GuiQt/TileTabsConfigurationDialog.cxx b/src/GuiQt/TileTabsConfigurationDialog.cxx index cd4d1b53c..71f8ab2aa 100644 --- a/src/GuiQt/TileTabsConfigurationDialog.cxx +++ b/src/GuiQt/TileTabsConfigurationDialog.cxx @@ -488,7 +488,8 @@ TileTabsConfigurationDialog::addRowColumnStretchWidget(const EventTileTabsConfig } } - TileTabElementWidgets* elementWidget = new TileTabElementWidgets(rowColumnType, + TileTabElementWidgets* elementWidget = new TileTabElementWidgets(this, + rowColumnType, index, gridLayout, this); @@ -996,6 +997,10 @@ TileTabsConfigurationDialog::updateGraphicsWindow() /** * Constructor. * + * @param tileTabsConfigurationDialog + * The tile tabs configuration dialog. + * @param rowColumnType + * 'Row' or 'Column' * @param index * Index of the row/column * @param gridLayout @@ -1003,11 +1008,13 @@ TileTabsConfigurationDialog::updateGraphicsWindow() * @param parent * Parent QObject */ -TileTabElementWidgets::TileTabElementWidgets(const EventTileTabsConfigurationModification::RowColumnType rowColumnType, +TileTabElementWidgets::TileTabElementWidgets(TileTabsConfigurationDialog* tileTabsConfigurationDialog, + const EventTileTabsConfigurationModification::RowColumnType rowColumnType, const int32_t index, QGridLayout* gridLayout, QObject* parent) : QObject(parent), +m_tileTabsConfigurationDialog(tileTabsConfigurationDialog), m_rowColumnType(rowColumnType), m_index(index), m_element(NULL) @@ -1201,10 +1208,11 @@ TileTabElementWidgets::constructionMenuTriggered(QAction* action) case EventTileTabsConfigurationModification::Operation::MOVE_BEFORE: break; } - - EventTileTabsConfigurationModification modification(m_index, - m_rowColumnType, - operation); + + EventTileTabsConfigurationModification modification(m_tileTabsConfigurationDialog->getCustomTileTabsConfiguration(), + m_index, + m_rowColumnType, + operation); emit modificationRequested(modification); } diff --git a/src/GuiQt/TileTabsConfigurationDialog.h b/src/GuiQt/TileTabsConfigurationDialog.h index 10cee7178..768d242aa 100644 --- a/src/GuiQt/TileTabsConfigurationDialog.h +++ b/src/GuiQt/TileTabsConfigurationDialog.h @@ -177,6 +177,8 @@ namespace caret { */ CaretPreferences* m_caretPreferences; + friend class TileTabElementWidgets; + static const int32_t s_maximumRowsColumns = 50; }; @@ -188,7 +190,8 @@ namespace caret { Q_OBJECT public: - TileTabElementWidgets(const EventTileTabsConfigurationModification::RowColumnType rowColumnType, + TileTabElementWidgets(TileTabsConfigurationDialog* tileTabsConfigurationDialog, + const EventTileTabsConfigurationModification::RowColumnType rowColumnType, const int32_t index, QGridLayout* gridLayout, QObject* parent); @@ -216,6 +219,7 @@ namespace caret { private: QMenu* createConstructionMenu(QToolButton* toolButton); + TileTabsConfigurationDialog* m_tileTabsConfigurationDialog; const EventTileTabsConfigurationModification::RowColumnType m_rowColumnType; const int32_t m_index; TileTabsRowColumnElement* m_element; diff --git a/src/GuiQt/TileTabsConfigurationModifier.cxx b/src/GuiQt/TileTabsConfigurationModifier.cxx new file mode 100644 index 000000000..79b9e7cee --- /dev/null +++ b/src/GuiQt/TileTabsConfigurationModifier.cxx @@ -0,0 +1,406 @@ + +/*LICENSE_START*/ +/* + * Copyright (C) 2018 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + +#include + +#define __TILE_TABS_CONFIGURATION_MODIFIER_DECLARE__ +#include "TileTabsConfigurationModifier.h" +#undef __TILE_TABS_CONFIGURATION_MODIFIER_DECLARE__ + +#include "BrainBrowserWindow.h" +#include "BrainOpenGLViewportContent.h" +#include "BrowserTabContent.h" +#include "CaretAssert.h" +#include "EventBrowserTabDelete.h" +#include "EventBrowserWindowTileTabOperation.h" +#include "EventGraphicsUpdateOneWindow.h" +#include "EventManager.h" +#include "EventUserInterfaceUpdate.h" +#include "EventTileTabsConfigurationModification.h" +#include "GuiManager.h" +#include "SpacerTabContent.h" +#include "TileTabsConfiguration.h" + +using namespace caret; + + + +/** + * \class caret::TileTabsConfigurationModifier + * \brief Modifies a tile tabs configuration. + * \ingroup GuiQt + */ + +/** + * Constructor. + * + * @param existingTabs + * All tabs in the window. + * @param modifyEvent + * Event describing modification. + */ +TileTabsConfigurationModifier::TileTabsConfigurationModifier(const std::vector& existingTabs, + EventTileTabsConfigurationModification* modifyEvent) +: CaretObject(), +m_existingTabs(existingTabs), +m_modifyEvent(modifyEvent) +{ + CaretAssert(modifyEvent); + m_currentTileTabsConfiguration = modifyEvent->getTileTabsConfiguration(); + CaretAssert(m_currentTileTabsConfiguration); +} + +/** + * Destructor. + */ +TileTabsConfigurationModifier::~TileTabsConfigurationModifier() +{ + for (auto rc : m_rowColumns) { + delete rc; + } + m_rowColumns.clear(); +} + +/** + * Get a description of this object's content. + * @return String describing this object's content. + */ +AString +TileTabsConfigurationModifier::toString() const +{ + return "TileTabsConfigurationModifier"; +} + +/** + * Run to perform the operation. + * + * @param errorMessageOut + * Contains error information if operation fails. + * @return + * True if successful, else false. + */ +bool +TileTabsConfigurationModifier::run(AString& errorMessageOut) +{ + errorMessageOut.clear(); + + loadRowColumnsFromTileTabsConfiguration(); + + bool validFlag = performModification(errorMessageOut); + + if (validFlag) { + validFlag = loadRowColumnsIntoTileTabsConfiguration(errorMessageOut); + } + + EventManager::get()->sendEvent(EventUserInterfaceUpdate().getPointer()); + EventManager::get()->sendEvent(EventGraphicsUpdateOneWindow(m_modifyEvent->getWindowIndex()).getPointer()); + + return validFlag; +} + +/** + * Load the rows or columns from the Tile Tabs Configuration + */ +void +TileTabsConfigurationModifier::loadRowColumnsFromTileTabsConfiguration() +{ + const int32_t numRows = m_currentTileTabsConfiguration->getNumberOfRows(); + const int32_t numColumns = m_currentTileTabsConfiguration->getNumberOfColumns(); + + switch (m_modifyEvent->getRowColumnType()) { + case EventTileTabsConfigurationModification::RowColumnType::COLUMN: + for (int32_t jCol = 0; jCol < numColumns; jCol++) { + m_rowColumns.push_back(new RowColumnContent(m_existingTabs, + m_modifyEvent->getTileTabsConfiguration(), + jCol, + false)); + } + break; + case EventTileTabsConfigurationModification::RowColumnType::ROW: + for (int32_t iRow = 0; iRow < numRows; iRow++) { + m_rowColumns.push_back(new RowColumnContent(m_existingTabs, + m_modifyEvent->getTileTabsConfiguration(), + iRow, + true)); + } + break; + } +} + +/** + * Perform the modification. + * + * @param errorMessageOut + * Contains error information if operation fails. + * @return + * True if successful, else false. + */ +bool +TileTabsConfigurationModifier::performModification(AString& errorMessageOut) +{ + + const int32_t rowColumnIndex = m_modifyEvent->getRowColumnIndex(); + + const int32_t numRowColumns = static_cast(m_rowColumns.size()); + switch (m_modifyEvent->getOperation()) { + case EventTileTabsConfigurationModification::Operation::DELETE: + if (numRowColumns <= 1) { + errorMessageOut = "Cannot delete ROWCOL when there is only one ROWCOL"; + } + else if ((rowColumnIndex >= 0) + && (rowColumnIndex < numRowColumns)) { + CaretAssertVectorIndex(m_rowColumns, rowColumnIndex); + RowColumnContent* deleteRowColumn = m_rowColumns[rowColumnIndex]; + CaretAssert(deleteRowColumn); + m_rowColumns.erase(m_rowColumns.begin() + rowColumnIndex); + CaretAssert(std::find(m_rowColumns.begin(), + m_rowColumns.end(), + deleteRowColumn) == m_rowColumns.end()); + delete deleteRowColumn; + + for (auto t : deleteRowColumn->m_tabElements) { + if (t->m_browserTabContent != NULL) { + m_browserTabsToDelete.push_back(t->m_browserTabContent); + } + } + } + else { + errorMessageOut = "Invalid ROWCOL index=RCINDEX"; + } + break; + case EventTileTabsConfigurationModification::Operation::DUPLICATE_AFTER: + break; + case EventTileTabsConfigurationModification::Operation::DUPLICATE_BEFORE: + break; + case EventTileTabsConfigurationModification::Operation::MOVE_AFTER: + break; + case EventTileTabsConfigurationModification::Operation::MOVE_BEFORE: + break; + } + + if ( ! errorMessageOut.isEmpty()) { + AString nameText; + switch (m_modifyEvent->getRowColumnType()) { + case EventTileTabsConfigurationModification::RowColumnType::COLUMN: + nameText = " Column "; + break; + case EventTileTabsConfigurationModification::RowColumnType::ROW: + nameText = " Row "; + break; + } + + errorMessageOut = errorMessageOut.replace("ROWCOL", nameText); + errorMessageOut = errorMessageOut.replace("RCINDEX", AString::number(rowColumnIndex + 1)); + } + + const bool validFlag = errorMessageOut.isEmpty(); + return validFlag; +} + +/** + * Load the modified rows/columns into the tile tabs configuration current in the browser window + * + * @param errorMessageOut + * Contains error information if operation fails. + * @return + * True if successful, else false. + */ +bool +TileTabsConfigurationModifier::loadRowColumnsIntoTileTabsConfiguration(AString& errorMessageOut) +{ + TileTabsConfiguration newConfiguration; + std::vector browserTabs; + + switch (m_modifyEvent->getRowColumnType()) { + case EventTileTabsConfigurationModification::RowColumnType::COLUMN: + { + + } + break; + case EventTileTabsConfigurationModification::RowColumnType::ROW: + { + const int32_t numRows = static_cast(m_rowColumns.size()); + const int32_t numColumns = static_cast(m_rowColumns[0]->m_tabElements.size()); + + newConfiguration.setNumberOfRows(numRows); + newConfiguration.setNumberOfColumns(numColumns); + + for (int32_t iRow = 0; iRow < numRows; iRow++) { + CaretAssertVectorIndex(m_rowColumns, iRow); + RowColumnContent* rowContent = m_rowColumns[iRow]; + for (int32_t jCol = 0; jCol < numColumns; jCol++) { + CaretAssertVectorIndex(rowContent->m_tabElements, jCol); + BrowserTabContent* btc = m_rowColumns[iRow]->m_tabElements[jCol]->m_browserTabContent; + if (btc != NULL) { + browserTabs.push_back(btc); + } + } + } + + for (int32_t iRow = 0; iRow < numRows; iRow++) { + TileTabsRowColumnElement* rce = newConfiguration.getRow(iRow); + CaretAssertVectorIndex(m_rowColumns, iRow); + CaretAssert(m_rowColumns[iRow]->m_stretching); + *rce = *m_rowColumns[iRow]->m_stretching; + } + } + break; + } + + /* + * Copy new tile tabs configuration into the current configuration + */ + *m_currentTileTabsConfiguration = newConfiguration; + + /* + * Update tabs in the window's toolbar + */ + QWidget* parentWindow(GuiManager::get()->getBrowserWindowByWindowIndex(m_modifyEvent->getWindowIndex())); + const int32_t invalidTabIndex(-1); + EventBrowserWindowTileTabOperation updateTabsEvent(EventBrowserWindowTileTabOperation::OPERATION_REPLACE_TABS, + parentWindow, + m_modifyEvent->getWindowIndex(), + invalidTabIndex, + browserTabs); + EventManager::get()->sendEvent(updateTabsEvent.getPointer()); + + if (updateTabsEvent.isError()) { + errorMessageOut.appendWithNewLine(updateTabsEvent.getErrorMessage()); + } + + if ( ! m_browserTabsToDelete.empty()) { + for (auto btc : m_browserTabsToDelete) { + EventBrowserTabDelete deleteEvent(btc, + btc->getTabNumber()); + EventManager::get()->sendEvent(deleteEvent.getPointer()); + if (deleteEvent.isError()) { + errorMessageOut.appendWithNewLine(deleteEvent.getErrorMessage()); + } + } + m_browserTabsToDelete.clear(); + } + + if (errorMessageOut.isEmpty()) { + return true; + } + + return false; +} + +/* + * Constructor for element in the row column matrix. + * + * @param rowIndex + * The row index + * @param columnIndex + * The column index + * @param browserTabContent + * Browser tab content at (rowIndex, columnIndex) in the current + * Tile Tabs Configuration + */ +TileTabsConfigurationModifier::Element::Element(const int32_t rowIndex, + const int32_t columnIndex, + BrowserTabContent* browserTabContent) +: +m_targetRowIndex(rowIndex), +m_targetColumnIndex(columnIndex), +m_sourceRowIndex(rowIndex), +m_sourceColumnIndex(columnIndex), +m_browserTabContent(browserTabContent) +{ + +} + + + +/* + * Constructor for content of a row/column element that + * contains the tabs and stretching for one row or one column + * + * @param existingTabs + * Existing browser tabs. + * @param tileTabsConfiguration + * The current tile tabs configuration in the browser window. + * @param rowColumnIndex + * Index of the row or column related to modification. + * @param rowFlag + * True if rows are being operated upon, false if operating on columns + */ +TileTabsConfigurationModifier::RowColumnContent::RowColumnContent(const std::vector& existingTabs, + TileTabsConfiguration* tileTabsConfiguration, + const int32_t rowColumnIndex, + const bool rowFlag) +{ + const int32_t numRows = tileTabsConfiguration->getNumberOfRows(); + const int32_t numColumns = tileTabsConfiguration->getNumberOfColumns(); + const int32_t numberOfTabs =static_cast(existingTabs.size()); + + if (rowFlag) { + for (int32_t jCol = 0; jCol < numColumns; jCol++) { + BrowserTabContent* browserTabContent(NULL); + const int32_t tabIndex = (rowColumnIndex * numColumns) + jCol; + if (tabIndex < numberOfTabs) { + CaretAssertVectorIndex(existingTabs, tabIndex); + browserTabContent = existingTabs[tabIndex]->getBrowserTabContent(); + } + + m_tabElements.push_back(new Element(rowColumnIndex, + jCol, + browserTabContent)); + } + + m_stretching = new TileTabsRowColumnElement(*tileTabsConfiguration->getRow(rowColumnIndex)); + } + else { + for (int32_t iRow = 0; iRow < numRows; iRow++) { + BrowserTabContent* browserTabContent(NULL); + SpacerTabContent* spacerTabContent(NULL); + const int32_t tabIndex = (iRow * numColumns) + rowColumnIndex; + if (tabIndex < numberOfTabs) { + CaretAssertVectorIndex(existingTabs, tabIndex); + browserTabContent = existingTabs[tabIndex]->getBrowserTabContent(); + spacerTabContent = existingTabs[tabIndex]->getSpacerTabContent(); + } + + m_tabElements.push_back(new Element(iRow, + rowColumnIndex, + browserTabContent)); + } + + m_stretching = new TileTabsRowColumnElement(*tileTabsConfiguration->getColumn(rowColumnIndex)); + } +} + +/** + * Destructor. + */ +TileTabsConfigurationModifier::RowColumnContent::~RowColumnContent() +{ + for (auto te : m_tabElements) { + delete te; + } + m_tabElements.clear(); + + delete m_stretching; + m_stretching = NULL; +} + + diff --git a/src/GuiQt/TileTabsConfigurationModifier.h b/src/GuiQt/TileTabsConfigurationModifier.h new file mode 100644 index 000000000..0b2648958 --- /dev/null +++ b/src/GuiQt/TileTabsConfigurationModifier.h @@ -0,0 +1,126 @@ +#ifndef __TILE_TABS_CONFIGURATION_MODIFIER_H__ +#define __TILE_TABS_CONFIGURATION_MODIFIER_H__ + +/*LICENSE_START*/ +/* + * Copyright (C) 2018 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + + + +#include +#include +#include "CaretObject.h" + + + +namespace caret { + + class BrainOpenGLViewportContent; + class BrowserTabContent; + class EventTileTabsConfigurationModification; + class SpacerTabContent; + class TileTabsConfiguration; + class TileTabsRowColumnElement; + + class TileTabsConfigurationModifier : public CaretObject { + + public: + TileTabsConfigurationModifier(const std::vector& existingTabs, + EventTileTabsConfigurationModification* modifyEvent); + + virtual ~TileTabsConfigurationModifier(); + + TileTabsConfigurationModifier(const TileTabsConfigurationModifier&) = delete; + + TileTabsConfigurationModifier& operator=(const TileTabsConfigurationModifier&) = delete; + + bool run(AString& errorMessageOut); + + + // ADD_NEW_METHODS_HERE + + virtual AString toString() const; + + class Element : public CaretObject { + public: + Element(const int32_t rowIndex, + const int32_t columnIndex, + BrowserTabContent* browserTabContent); + + int32_t m_targetRowIndex; + + int32_t m_targetColumnIndex; + + int32_t m_sourceRowIndex; + + int32_t m_sourceColumnIndex; + + BrowserTabContent* m_browserTabContent; + + }; + + /** + * Contains the tabs and stretching for one row or one column + */ + class RowColumnContent : public CaretObject { + public: + RowColumnContent(const std::vector& existingTabs, + TileTabsConfiguration* tileTabsConfiguration, + const int32_t rowColumnIndex, + const bool rowFlag); + + ~RowColumnContent(); + + std::vector m_tabElements; + + TileTabsRowColumnElement* m_stretching; + + }; + + private: + + void loadRowColumnsFromTileTabsConfiguration(); + + bool loadRowColumnsIntoTileTabsConfiguration(AString& errorMessageOut); + + bool performModification(AString& errorMessageOut); + + const std::vector& m_existingTabs; + + EventTileTabsConfigurationModification* m_modifyEvent; + + std::vector m_rowColumns; + + std::vector m_browserTabsToDelete; + + /** + * This is the current tile tabs configuration in the window so DO NOT delete it + */ + TileTabsConfiguration* m_currentTileTabsConfiguration = NULL; + + // ADD_NEW_MEMBERS_HERE + + }; + +#ifdef __TILE_TABS_CONFIGURATION_MODIFIER_DECLARE__ + // +#endif // __TILE_TABS_CONFIGURATION_MODIFIER_DECLARE__ + +} // namespace +#endif //__TILE_TABS_CONFIGURATION_MODIFIER_H__ diff --git a/src/GuiQt/UserInputTileTabsContextMenu.cxx b/src/GuiQt/UserInputTileTabsContextMenu.cxx index 051dfd164..2efb41af3 100644 --- a/src/GuiQt/UserInputTileTabsContextMenu.cxx +++ b/src/GuiQt/UserInputTileTabsContextMenu.cxx @@ -125,10 +125,12 @@ UserInputTileTabsContextMenu::actionTriggered(QAction* action) } if (validOperationFlag) { + std::vector emptyBrowserTabs; EventBrowserWindowTileTabOperation tileTabOperation(operation, m_parentWidget, m_windowIndex, - m_tabIndex); + m_tabIndex, + emptyBrowserTabs); EventManager::get()->sendEvent(tileTabOperation.getPointer()); } } -- GitLab From e8be195708e1462586f428a3516e113b2bdf8b95 Mon Sep 17 00:00:00 2001 From: John Harwell Date: Thu, 25 Oct 2018 11:56:56 -0500 Subject: [PATCH 089/427] WB-668 Titling columns in tile tabs: Implemented deletion of a column in a tile tabs configuration. --- src/GuiQt/TileTabsConfigurationModifier.cxx | 69 +++++++++++++++++++-- src/GuiQt/TileTabsConfigurationModifier.h | 4 ++ 2 files changed, 67 insertions(+), 6 deletions(-) diff --git a/src/GuiQt/TileTabsConfigurationModifier.cxx b/src/GuiQt/TileTabsConfigurationModifier.cxx index 79b9e7cee..45eb0dbad 100644 --- a/src/GuiQt/TileTabsConfigurationModifier.cxx +++ b/src/GuiQt/TileTabsConfigurationModifier.cxx @@ -41,7 +41,7 @@ using namespace caret; - +static bool debugFlag = false; /** * \class caret::TileTabsConfigurationModifier @@ -86,7 +86,14 @@ TileTabsConfigurationModifier::~TileTabsConfigurationModifier() AString TileTabsConfigurationModifier::toString() const { - return "TileTabsConfigurationModifier"; + AString s; + for (const auto rc : m_rowColumns) { + if ( ! s.isEmpty()) { + s.append("\n"); + } + s.append(rc->toString()); + } + return s; } /** @@ -103,8 +110,13 @@ TileTabsConfigurationModifier::run(AString& errorMessageOut) errorMessageOut.clear(); loadRowColumnsFromTileTabsConfiguration(); - + if (debugFlag) { + std::cout << "Loaded: " << toString() << std::endl; + } bool validFlag = performModification(errorMessageOut); + if (debugFlag) { + std::cout << "After Modification: " << toString() << std::endl << std::endl; + } if (validFlag) { validFlag = loadRowColumnsIntoTileTabsConfiguration(errorMessageOut); @@ -226,19 +238,43 @@ TileTabsConfigurationModifier::performModification(AString& errorMessageOut) bool TileTabsConfigurationModifier::loadRowColumnsIntoTileTabsConfiguration(AString& errorMessageOut) { - TileTabsConfiguration newConfiguration; + TileTabsConfiguration newConfiguration(*m_currentTileTabsConfiguration); std::vector browserTabs; switch (m_modifyEvent->getRowColumnType()) { case EventTileTabsConfigurationModification::RowColumnType::COLUMN: { + const int32_t numRows = static_cast(m_rowColumns[0]->m_tabElements.size()); + CaretAssert(numRows == m_currentTileTabsConfiguration->getNumberOfRows()); + const int32_t numColumns = static_cast(m_rowColumns.size()); + newConfiguration.setNumberOfRows(numRows); + newConfiguration.setNumberOfColumns(numColumns); + + for (int32_t iRow = 0; iRow < numRows; iRow++) { + for (int32_t jCol = 0; jCol < numColumns; jCol++) { + RowColumnContent* columnContent = m_rowColumns[jCol]; + CaretAssertVectorIndex(columnContent->m_tabElements, iRow); + BrowserTabContent* btc = columnContent->m_tabElements[iRow]->m_browserTabContent; + if (btc != NULL) { + browserTabs.push_back(btc); + } + } + } + + for (int32_t jCol = 0; jCol < numColumns; jCol++) { + TileTabsRowColumnElement* rce = newConfiguration.getColumn(jCol); + CaretAssertVectorIndex(m_rowColumns, jCol); + CaretAssert(m_rowColumns[jCol]->m_stretching); + *rce = *m_rowColumns[jCol]->m_stretching; + } } break; case EventTileTabsConfigurationModification::RowColumnType::ROW: { const int32_t numRows = static_cast(m_rowColumns.size()); const int32_t numColumns = static_cast(m_rowColumns[0]->m_tabElements.size()); + CaretAssert(numColumns == m_currentTileTabsConfiguration->getNumberOfColumns()); newConfiguration.setNumberOfRows(numRows); newConfiguration.setNumberOfColumns(numColumns); @@ -248,7 +284,7 @@ TileTabsConfigurationModifier::loadRowColumnsIntoTileTabsConfiguration(AString& RowColumnContent* rowContent = m_rowColumns[iRow]; for (int32_t jCol = 0; jCol < numColumns; jCol++) { CaretAssertVectorIndex(rowContent->m_tabElements, jCol); - BrowserTabContent* btc = m_rowColumns[iRow]->m_tabElements[jCol]->m_browserTabContent; + BrowserTabContent* btc = rowContent->m_tabElements[jCol]->m_browserTabContent; if (btc != NULL) { browserTabs.push_back(btc); } @@ -329,7 +365,16 @@ m_browserTabContent(browserTabContent) } - +/** + * @return String representation of object. + */ +AString +TileTabsConfigurationModifier::Element::toString() const +{ + AString s("(row=%1, column=%2)"); + s = s.arg(m_sourceRowIndex).arg(m_sourceColumnIndex); + return s; +} /* * Constructor for content of a row/column element that @@ -403,4 +448,16 @@ TileTabsConfigurationModifier::RowColumnContent::~RowColumnContent() m_stretching = NULL; } +/** + * @return String representation of object. + */ +AString +TileTabsConfigurationModifier::RowColumnContent::toString() const +{ + AString s; + for (const auto te : m_tabElements) { + s += (" " + te->toString()); + } + return s; +} diff --git a/src/GuiQt/TileTabsConfigurationModifier.h b/src/GuiQt/TileTabsConfigurationModifier.h index 0b2648958..b927918db 100644 --- a/src/GuiQt/TileTabsConfigurationModifier.h +++ b/src/GuiQt/TileTabsConfigurationModifier.h @@ -63,6 +63,8 @@ namespace caret { const int32_t columnIndex, BrowserTabContent* browserTabContent); + AString toString() const override; + int32_t m_targetRowIndex; int32_t m_targetColumnIndex; @@ -87,6 +89,8 @@ namespace caret { ~RowColumnContent(); + AString toString() const override; + std::vector m_tabElements; TileTabsRowColumnElement* m_stretching; -- GitLab From 20b6b3be62e4fa24afebe56de8157717f8f7ea2d Mon Sep 17 00:00:00 2001 From: John Harwell Date: Thu, 25 Oct 2018 12:44:28 -0500 Subject: [PATCH 090/427] WB-668 Titling columns in tile tabs: Implemented move row or column operations in Tile Tabs Configuration. --- src/GuiQt/TileTabsConfigurationModifier.cxx | 43 +++++++++++++++++++-- 1 file changed, 39 insertions(+), 4 deletions(-) diff --git a/src/GuiQt/TileTabsConfigurationModifier.cxx b/src/GuiQt/TileTabsConfigurationModifier.cxx index 45eb0dbad..7a52cfc4a 100644 --- a/src/GuiQt/TileTabsConfigurationModifier.cxx +++ b/src/GuiQt/TileTabsConfigurationModifier.cxx @@ -41,7 +41,7 @@ using namespace caret; -static bool debugFlag = false; +static bool debugFlag = true; /** * \class caret::TileTabsConfigurationModifier @@ -111,11 +111,11 @@ TileTabsConfigurationModifier::run(AString& errorMessageOut) loadRowColumnsFromTileTabsConfiguration(); if (debugFlag) { - std::cout << "Loaded: " << toString() << std::endl; + std::cout << "Loaded: " << toString() << std::endl << std::flush; } bool validFlag = performModification(errorMessageOut); if (debugFlag) { - std::cout << "After Modification: " << toString() << std::endl << std::endl; + std::cout << "After Modification: " << toString() << std::endl << std::endl << std::flush; } if (validFlag) { @@ -195,7 +195,7 @@ TileTabsConfigurationModifier::performModification(AString& errorMessageOut) } } else { - errorMessageOut = "Invalid ROWCOL index=RCINDEX"; + errorMessageOut = "Invalid ROWCOL index=RCINDEX when deleting"; } break; case EventTileTabsConfigurationModification::Operation::DUPLICATE_AFTER: @@ -203,8 +203,40 @@ TileTabsConfigurationModifier::performModification(AString& errorMessageOut) case EventTileTabsConfigurationModification::Operation::DUPLICATE_BEFORE: break; case EventTileTabsConfigurationModification::Operation::MOVE_AFTER: + if (numRowColumns <= 1) { + errorMessageOut = "Cannot move ROWCOL when there is only one ROWCOL"; + } + else if (rowColumnIndex == (numRowColumns - 1)) { + errorMessageOut = "Cannot move last ROWCOL after itself"; + } + else if ((rowColumnIndex >= 0) + && (rowColumnIndex < (numRowColumns - 1))) { + CaretAssertVectorIndex(m_rowColumns, rowColumnIndex); + CaretAssertVectorIndex(m_rowColumns, rowColumnIndex + 1); + std::swap(m_rowColumns[rowColumnIndex], + m_rowColumns[rowColumnIndex + 1]); + } + else { + errorMessageOut = "Invalid ROWCOL index=RCINDEX when moving"; + } break; case EventTileTabsConfigurationModification::Operation::MOVE_BEFORE: + if (numRowColumns <= 1) { + errorMessageOut = "Cannot move ROWCOL when there is only one ROWCOL"; + } + else if (rowColumnIndex == 0) { + errorMessageOut = "Cannot move last ROWCOL before itself"; + } + else if ((rowColumnIndex >= 1) + && (rowColumnIndex < numRowColumns)) { + CaretAssertVectorIndex(m_rowColumns, rowColumnIndex); + CaretAssertVectorIndex(m_rowColumns, rowColumnIndex - 1); + std::swap(m_rowColumns[rowColumnIndex], + m_rowColumns[rowColumnIndex - 1]); + } + else { + errorMessageOut = "Invalid ROWCOL index=RCINDEX when moving"; + } break; } @@ -219,6 +251,9 @@ TileTabsConfigurationModifier::performModification(AString& errorMessageOut) break; } + /* + * "substite names" + */ errorMessageOut = errorMessageOut.replace("ROWCOL", nameText); errorMessageOut = errorMessageOut.replace("RCINDEX", AString::number(rowColumnIndex + 1)); } -- GitLab From b048f9d04c729aee84743d4a86830fad6a9181cf Mon Sep 17 00:00:00 2001 From: John Harwell Date: Thu, 25 Oct 2018 14:40:43 -0500 Subject: [PATCH 091/427] WB-668 Titling columns in tile tabs: Implemented duplicate row or column operations in Tile Tabs Configuration. --- src/GuiQt/TileTabsConfigurationModifier.cxx | 77 ++++++++++++++++++++- src/GuiQt/TileTabsConfigurationModifier.h | 4 ++ 2 files changed, 80 insertions(+), 1 deletion(-) diff --git a/src/GuiQt/TileTabsConfigurationModifier.cxx b/src/GuiQt/TileTabsConfigurationModifier.cxx index 7a52cfc4a..9f54bef16 100644 --- a/src/GuiQt/TileTabsConfigurationModifier.cxx +++ b/src/GuiQt/TileTabsConfigurationModifier.cxx @@ -30,6 +30,7 @@ #include "BrowserTabContent.h" #include "CaretAssert.h" #include "EventBrowserTabDelete.h" +#include "EventBrowserTabNew.h" #include "EventBrowserWindowTileTabOperation.h" #include "EventGraphicsUpdateOneWindow.h" #include "EventManager.h" @@ -41,7 +42,7 @@ using namespace caret; -static bool debugFlag = true; +static bool debugFlag = false; /** * \class caret::TileTabsConfigurationModifier @@ -199,8 +200,34 @@ TileTabsConfigurationModifier::performModification(AString& errorMessageOut) } break; case EventTileTabsConfigurationModification::Operation::DUPLICATE_AFTER: + if ((rowColumnIndex >= 0) + && (rowColumnIndex < numRowColumns)) { + RowColumnContent* rowColumnCopy = m_rowColumns[rowColumnIndex]->clone(errorMessageOut); + if (rowColumnCopy != NULL) { + const int32_t insertOffset = (rowColumnIndex + 1); + m_rowColumns.insert(m_rowColumns.begin() + + insertOffset, + rowColumnCopy); + } + } + else { + errorMessageOut = "Invalid ROWCOL index=RCINDEX when duplicating"; + } break; case EventTileTabsConfigurationModification::Operation::DUPLICATE_BEFORE: + if ((rowColumnIndex >= 0) + && (rowColumnIndex < numRowColumns)) { + RowColumnContent* rowColumnCopy = m_rowColumns[rowColumnIndex]->clone(errorMessageOut); + if (rowColumnCopy != NULL) { + const int32_t insertOffset = rowColumnIndex; + m_rowColumns.insert(m_rowColumns.begin() + + insertOffset, + rowColumnCopy); + } + } + else { + errorMessageOut = "Invalid ROWCOL index=RCINDEX when duplicating"; + } break; case EventTileTabsConfigurationModification::Operation::MOVE_AFTER: if (numRowColumns <= 1) { @@ -483,6 +510,21 @@ TileTabsConfigurationModifier::RowColumnContent::~RowColumnContent() m_stretching = NULL; } +/** + * Copy constructor. + * + * @param obj + * Instance that is copied. + */ +TileTabsConfigurationModifier::RowColumnContent::RowColumnContent(const RowColumnContent& obj) +{ + for (auto te : obj.m_tabElements) { + m_tabElements.push_back(new Element(*te)); + } + + m_stretching = new TileTabsRowColumnElement(*obj.m_stretching); +} + /** * @return String representation of object. */ @@ -496,3 +538,36 @@ TileTabsConfigurationModifier::RowColumnContent::toString() const return s; } +/** + * Clone this Row/column content instance. + * + * @param errorMessageOut + * Output with error message. + * @return + * True if successful, else false + */ +TileTabsConfigurationModifier::RowColumnContent* +TileTabsConfigurationModifier::RowColumnContent::clone(AString& errorMessageOut) const +{ + RowColumnContent* cloned = new RowColumnContent(*this); + CaretAssert(cloned); + + for (auto te : cloned->m_tabElements) { + if (te->m_browserTabContent != NULL) { + EventBrowserTabNew newTabEvent; + EventManager::get()->sendEvent(newTabEvent.getPointer()); + if (newTabEvent.isError()) { + errorMessageOut.appendWithNewLine(newTabEvent.getErrorMessage()); + te->m_browserTabContent = NULL; + } + else { + BrowserTabContent* copyTab = te->m_browserTabContent; + te->m_browserTabContent = newTabEvent.getBrowserTab(); + te->m_browserTabContent->cloneBrowserTabContent(copyTab); + } + } + } + + return cloned; +} + diff --git a/src/GuiQt/TileTabsConfigurationModifier.h b/src/GuiQt/TileTabsConfigurationModifier.h index b927918db..e3482f59a 100644 --- a/src/GuiQt/TileTabsConfigurationModifier.h +++ b/src/GuiQt/TileTabsConfigurationModifier.h @@ -87,8 +87,12 @@ namespace caret { const int32_t rowColumnIndex, const bool rowFlag); + RowColumnContent(const RowColumnContent& obj); + ~RowColumnContent(); + RowColumnContent* clone(AString& errorMessageOut) const; + AString toString() const override; std::vector m_tabElements; -- GitLab From 15f820fd45805ab6521f69cad7a73e8ada47f85e Mon Sep 17 00:00:00 2001 From: John Harwell Date: Thu, 25 Oct 2018 16:14:36 -0500 Subject: [PATCH 092/427] WB-668 Titling columns in tile tabs: In Tile Tabs Configuration Dialog, disable some items in the Construction Menus for row/columns when the menu item is invalid (i.e.: Delete row when there is only one row). --- src/GuiQt/TileTabsConfigurationDialog.cxx | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/src/GuiQt/TileTabsConfigurationDialog.cxx b/src/GuiQt/TileTabsConfigurationDialog.cxx index 71f8ab2aa..ae143e773 100644 --- a/src/GuiQt/TileTabsConfigurationDialog.cxx +++ b/src/GuiQt/TileTabsConfigurationDialog.cxx @@ -1224,6 +1224,26 @@ TileTabElementWidgets::constructionMenuTriggered(QAction* action) void TileTabElementWidgets::constructionMenuAboutToShow() { + const TileTabsConfiguration* config = m_tileTabsConfigurationDialog->getCustomTileTabsConfiguration(); + if (config != NULL) { + int32_t numItems(-1); + switch (m_rowColumnType) { + case EventTileTabsConfigurationModification::RowColumnType::COLUMN: + numItems = config->getNumberOfColumns(); + break; + case EventTileTabsConfigurationModification::RowColumnType::ROW: + numItems = config->getNumberOfRows(); + break; + } + + m_menuDeleteAction->setEnabled(numItems > 1); + m_menuDuplicateAfterAction->setEnabled(numItems >= 1); + m_menuDuplicateBeforeAction->setEnabled(numItems >= 1); + m_menuMoveAfterAction->setEnabled((numItems > 1) + && (m_index < (numItems - 1))); + m_menuMoveBeforeAction->setEnabled((numItems > 1) + && (m_index > 0)); + } } -- GitLab From cfab01060c2b43b6dab612ffaa88be8582a8441e Mon Sep 17 00:00:00 2001 From: John Harwell Date: Fri, 26 Oct 2018 15:53:08 -0500 Subject: [PATCH 093/427] WB-668 Titling columns in tile tabs: When the tile tabs configuration is changed (add, more, delete row/column), copy, move, or delete annotations in spacer tab space. --- src/Common/CMakeLists.txt | 2 + ...EventTileTabsConfigurationModification.cxx | 2 +- .../EventTileTabsConfigurationModification.h | 0 src/Common/SpacerTabIndex.cxx | 24 ++ src/Common/SpacerTabIndex.h | 4 + src/Files/AnnotationFile.cxx | 213 ++++++++++++++++++ src/Files/AnnotationFile.h | 3 + src/GuiQt/BrainBrowserWindow.cxx | 1 + src/GuiQt/CMakeLists.txt | 2 - 9 files changed, 248 insertions(+), 3 deletions(-) rename src/{GuiQt => Common}/EventTileTabsConfigurationModification.cxx (99%) rename src/{GuiQt => Common}/EventTileTabsConfigurationModification.h (100%) diff --git a/src/Common/CMakeLists.txt b/src/Common/CMakeLists.txt index 57394b626..451bf351c 100644 --- a/src/Common/CMakeLists.txt +++ b/src/Common/CMakeLists.txt @@ -105,6 +105,7 @@ EventListenerInterface.h EventManager.h EventPaletteGetByName.h EventProgressUpdate.h +EventTileTabsConfigurationModification.h EventTypeEnum.h FastStatistics.h FileAdapter.h @@ -210,6 +211,7 @@ EventListenerInterface.cxx EventManager.cxx EventPaletteGetByName.cxx EventProgressUpdate.cxx +EventTileTabsConfigurationModification.cxx EventTypeEnum.cxx FastStatistics.cxx FileAdapter.cxx diff --git a/src/GuiQt/EventTileTabsConfigurationModification.cxx b/src/Common/EventTileTabsConfigurationModification.cxx similarity index 99% rename from src/GuiQt/EventTileTabsConfigurationModification.cxx rename to src/Common/EventTileTabsConfigurationModification.cxx index 43aa47f2e..4f4a12d68 100644 --- a/src/GuiQt/EventTileTabsConfigurationModification.cxx +++ b/src/Common/EventTileTabsConfigurationModification.cxx @@ -33,7 +33,7 @@ using namespace caret; /** * \class caret::EventTileTabsConfigurationModification * \brief Event for modifying a tile tabs configuration. - * \ingroup GuiQt + * \ingroup Common */ /** diff --git a/src/GuiQt/EventTileTabsConfigurationModification.h b/src/Common/EventTileTabsConfigurationModification.h similarity index 100% rename from src/GuiQt/EventTileTabsConfigurationModification.h rename to src/Common/EventTileTabsConfigurationModification.h diff --git a/src/Common/SpacerTabIndex.cxx b/src/Common/SpacerTabIndex.cxx index ea6d3de73..4b74acb3d 100644 --- a/src/Common/SpacerTabIndex.cxx +++ b/src/Common/SpacerTabIndex.cxx @@ -209,6 +209,30 @@ SpacerTabIndex::getColumnIndex() const return m_columnIndex; } +/** + * Set the row index. + * + * @param rowIndex + * New row index. + */ +void +SpacerTabIndex::setRowIndex(const int32_t rowIndex) +{ + m_rowIndex = rowIndex; +} + +/** + * Set the column index. + * + * @param columnIndex + * New column index. + */ +void +SpacerTabIndex::setColumnIndex(const int32_t columnIndex) + +{ + m_columnIndex = columnIndex; +} /** * Reset to invalid indices. */ diff --git a/src/Common/SpacerTabIndex.h b/src/Common/SpacerTabIndex.h index 8274db2a6..9c90318e8 100644 --- a/src/Common/SpacerTabIndex.h +++ b/src/Common/SpacerTabIndex.h @@ -69,6 +69,10 @@ namespace caret { void setFromXmlAttributeText(const AString& text); + void setRowIndex(const int32_t rowIndex); + + void setColumnIndex(const int32_t columnIndex); + // ADD_NEW_METHODS_HERE virtual AString toString() const; diff --git a/src/Files/AnnotationFile.cxx b/src/Files/AnnotationFile.cxx index b6884cfd9..ed6a250cb 100644 --- a/src/Files/AnnotationFile.cxx +++ b/src/Files/AnnotationFile.cxx @@ -45,6 +45,7 @@ #include "EventAnnotationTextSubstitutionInvalidate.h" #include "EventBrowserTabDelete.h" #include "EventManager.h" +#include "EventTileTabsConfigurationModification.h" #include "GiftiMetaData.h" #include "SceneClass.h" #include "SceneClassAssistant.h" @@ -222,6 +223,9 @@ AnnotationFile::initializeAnnotationFile() EventManager::get()->addEventListener(this, EventTypeEnum::EVENT_ANNOTATION_GROUPING); EventManager::get()->addEventListener(this, EventTypeEnum::EVENT_ANNOTATION_TEXT_SUBSTITUTION_INVALIDATE); EventManager::get()->addEventListener(this, EventTypeEnum::EVENT_BROWSER_TAB_DELETE); + + /* NEED THIS AFTER Tile Tabs have been modified */ + EventManager::get()->addProcessedEventListener(this, EventTypeEnum::EVENT_TILE_TABS_MODIFICATION); } /** @@ -477,6 +481,11 @@ AnnotationFile::receiveEvent(Event* event) textSubEvent->setEventProcessed(); } + else if (event->getEventType() == EventTypeEnum::EVENT_TILE_TABS_MODIFICATION) { + EventTileTabsConfigurationModification* modEvent = dynamic_cast(event); + CaretAssert(modEvent); + updateSpacerAnnotationsAfterTileTabsModification(modEvent); + } } /** @@ -2073,3 +2082,207 @@ AnnotationFile::isItemSelectedForEditingInWindow(const int32_t /*windowIndex*/) return false; } +/** + * Update spacer annotations after tile tabs configuration is modified. + * + * @param modEvent + * The tile tabs modify event. + */ +void +AnnotationFile::updateSpacerAnnotationsAfterTileTabsModification(const EventTileTabsConfigurationModification* modEvent) +{ + const int32_t rowColumnIndex = modEvent->getRowColumnIndex(); + const bool rowFlag = (modEvent->getRowColumnType() == EventTileTabsConfigurationModification::RowColumnType::ROW); + + int32_t deleteIndex(-1); + int32_t shiftStartIndex(-1); + int32_t duplicateFromIndex(-1); + int32_t duplicateToIndex(-1); + int32_t moveOneIndex(-1); + int32_t moveTwoIndex(-1); + + switch (modEvent->getOperation()) { + case EventTileTabsConfigurationModification::Operation::DELETE: + deleteIndex = rowColumnIndex; + shiftStartIndex = rowColumnIndex + 1; + break; + case EventTileTabsConfigurationModification::Operation::DUPLICATE_AFTER: + { + shiftStartIndex = rowColumnIndex + 1; + duplicateFromIndex = rowColumnIndex; + duplicateToIndex = rowColumnIndex + 1; + } + break; + case EventTileTabsConfigurationModification::Operation::DUPLICATE_BEFORE: + { + shiftStartIndex = rowColumnIndex; + duplicateFromIndex = rowColumnIndex + 1; + duplicateToIndex = rowColumnIndex; + } + break; + case EventTileTabsConfigurationModification::Operation::MOVE_AFTER: + moveOneIndex = rowColumnIndex; + moveTwoIndex = rowColumnIndex + 1; + break; + case EventTileTabsConfigurationModification::Operation::MOVE_BEFORE: + moveOneIndex = rowColumnIndex; + moveTwoIndex = rowColumnIndex - 1; + break; + } + + std::vector deleteAnnotations; + std::vector duplicatedAnnotations; + std::vector, Annotation*>> modifiedAnnotations; + + std::set processedAnnotations; + + const int32_t windowIndex = modEvent->getWindowIndex(); + for (auto group : m_annotationGroups) { + std::vector> removeAnnotationsFromGroup; + + if (group->getCoordinateSpace() == AnnotationCoordinateSpaceEnum::SPACER) { + const int32_t numAnn = group->getNumberOfAnnotations(); + for (int32_t iAnn = 0; iAnn < numAnn; iAnn++) { + Annotation* ann = group->getAnnotation(iAnn); + CaretAssert(ann); + SpacerTabIndex spacerTabIndex = ann->getSpacerTabIndex(); + + if (spacerTabIndex.getWindowIndex() == windowIndex) { + int32_t rowIndex = spacerTabIndex.getRowIndex(); + int32_t columnIndex = spacerTabIndex.getColumnIndex(); + + switch (modEvent->getOperation()) { + case EventTileTabsConfigurationModification::Operation::DELETE: + { + int32_t rcIndex = (rowFlag ? rowIndex : columnIndex); + if (rcIndex == deleteIndex) { + deleteAnnotations.push_back(ann); + } + + /* + * Shift annotations + */ + if (rcIndex >= shiftStartIndex) { + rcIndex = rcIndex - 1; + + if (rowFlag) { + rowIndex = rcIndex; + } + else { + columnIndex = rcIndex; + } + } + } + break; + case EventTileTabsConfigurationModification::Operation::DUPLICATE_AFTER: + case EventTileTabsConfigurationModification::Operation::DUPLICATE_BEFORE: + { + int32_t rcIndex = (rowFlag ? rowIndex : columnIndex); + /* + * Shift annotations + */ + if (rcIndex >= shiftStartIndex) { + rcIndex = rcIndex + 1; + } + + /* + * Then duplicate and yes, ok to use rowIndex even though it has changed + */ + if (rcIndex == duplicateFromIndex) { + Annotation* dupAnn = ann->clone(); + SpacerTabIndex dupAnnSTI = dupAnn->getSpacerTabIndex(); + if (rowFlag) { + dupAnnSTI.setRowIndex(duplicateToIndex); + } + else { + dupAnnSTI.setColumnIndex(duplicateToIndex); + } + dupAnn->setSpacerTabIndex(dupAnnSTI); + CaretLogFine("Copying from " + + AString::number(rcIndex) + " to " + AString::number(duplicateToIndex) + + " " + ann->toString()); + duplicatedAnnotations.push_back(dupAnn); + } + + if (rowFlag) { + rowIndex = rcIndex; + } + else { + columnIndex = rcIndex; + } + } + break; + case EventTileTabsConfigurationModification::Operation::MOVE_AFTER: + case EventTileTabsConfigurationModification::Operation::MOVE_BEFORE: + { + int32_t rcIndex = (rowFlag ? rowIndex : columnIndex); + int32_t newIndex(-1); + if (rcIndex == moveOneIndex) { + newIndex = moveTwoIndex; + } + else if (rcIndex == moveTwoIndex) { + newIndex = moveOneIndex; + } + if (newIndex >= 0) { + if (rowFlag) { + rowIndex = newIndex; + } + else { + columnIndex = newIndex; + } + } + } + break; + } + + if ((rowIndex != spacerTabIndex.getRowIndex()) + || (columnIndex != spacerTabIndex.getColumnIndex())) { + spacerTabIndex.setRowIndex(rowIndex); + spacerTabIndex.setColumnIndex(columnIndex); + ann->setSpacerTabIndex(spacerTabIndex); + CaretLogFine("Moved from " + + ann->getSpacerTabIndex().toString() + + " to " + spacerTabIndex.toString() + " " + ann->toString()); + modifiedAnnotations.push_back(std::make_pair(group, ann)); + } + } + } + } + + } + + /* + * Move modified annotations into correct annotation group. + */ + for (auto groupAndAnn : modifiedAnnotations) { + QSharedPointer group = groupAndAnn.first; + Annotation* ann = groupAndAnn.second; + + QSharedPointer annSharedPointer; + if (group->removeAnnotation(ann, + annSharedPointer)) { + addAnnotationPrivateSharedPointer(annSharedPointer, + ann->getUniqueKey()); + } + } + + /* + * Add duplicated anntotations. + */ + for (auto ann : duplicatedAnnotations) { + addAnnotationPrivate(ann, + generateUniqueKey()); + } + + /* + * Delete annotations + */ + for (auto ann : deleteAnnotations) { + const bool keepAnnotationForUndoRedoFlag(false); + removeAnnotationPrivate(ann, + keepAnnotationForUndoRedoFlag); + } +} + + + diff --git a/src/Files/AnnotationFile.h b/src/Files/AnnotationFile.h index ac6c9ee41..4495914b5 100644 --- a/src/Files/AnnotationFile.h +++ b/src/Files/AnnotationFile.h @@ -33,6 +33,7 @@ #include "DisplayGroupAndTabItemInterface.h" #include "EventAnnotationGrouping.h" #include "EventListenerInterface.h" +#include "EventTileTabsConfigurationModification.h" namespace caret { @@ -212,6 +213,8 @@ namespace caret { AnnotationGroup* getSpaceAnnotationGroup(const Annotation* annotation); + void updateSpacerAnnotationsAfterTileTabsModification(const EventTileTabsConfigurationModification* modEvent); + const AnnotationFileSubType m_fileSubType; SceneClassAssistant* m_sceneAssistant; diff --git a/src/GuiQt/BrainBrowserWindow.cxx b/src/GuiQt/BrainBrowserWindow.cxx index 0c4f9b086..d9c2c547a 100644 --- a/src/GuiQt/BrainBrowserWindow.cxx +++ b/src/GuiQt/BrainBrowserWindow.cxx @@ -2366,6 +2366,7 @@ BrainBrowserWindow::modifyTileTabsConfiguration(EventTileTabsConfigurationModifi AString errorMessage; if (! modifier.run(errorMessage)) { + modEvent->setErrorMessage(errorMessage); WuQMessageBox::errorOk(this, errorMessage); } diff --git a/src/GuiQt/CMakeLists.txt b/src/GuiQt/CMakeLists.txt index 7c166d9c0..fee2e32d8 100644 --- a/src/GuiQt/CMakeLists.txt +++ b/src/GuiQt/CMakeLists.txt @@ -343,7 +343,6 @@ EventOperatingSystemRequestOpenDataFile.h EventOverlaySettingsEditorDialogRequest.h EventPaletteColorMappingEditorDialogRequest.h EventShowDataFileReadWarningsDialog.h -EventTileTabsConfigurationModification.h EventUpdateInformationWindows.h EventUpdateVolumeEditingToolBar.h EventUpdateYokedWindows.h @@ -577,7 +576,6 @@ EventOperatingSystemRequestOpenDataFile.cxx EventOverlaySettingsEditorDialogRequest.cxx EventPaletteColorMappingEditorDialogRequest.cxx EventShowDataFileReadWarningsDialog.cxx -EventTileTabsConfigurationModification.cxx EventUpdateInformationWindows.cxx EventUpdateVolumeEditingToolBar.cxx EventUpdateYokedWindows.cxx -- GitLab From 979c1f5fe5f5f06dc24ee1e704bd257cdfdbfee4 Mon Sep 17 00:00:00 2001 From: John Harwell Date: Mon, 29 Oct 2018 10:41:18 -0500 Subject: [PATCH 094/427] WB-668 Titling columns in tile tabs: When a tab is duplicated, also duplicate tab space annotations in the tab. When a tab is deleted, also delete any annotations in tab space assigned to the tab. --- src/Brain/CMakeLists.txt | 2 - src/Brain/SessionManager.cxx | 39 ++++++- src/Common/CMakeLists.txt | 4 + src/{Brain => Common}/EventBrowserTabNew.cxx | 0 src/{Brain => Common}/EventBrowserTabNew.h | 0 src/Common/EventBrowserTabNewClone.cxx | 102 +++++++++++++++++++ src/Common/EventBrowserTabNewClone.h | 74 ++++++++++++++ src/Common/EventManager.cxx | 1 + src/Common/EventTypeEnum.cxx | 4 + src/Common/EventTypeEnum.h | 2 + src/Files/AnnotationFile.cxx | 61 ++++++++++- src/Files/AnnotationFile.h | 3 + src/GuiQt/BrainBrowserWindowToolBar.cxx | 19 ++-- src/GuiQt/TileTabsConfigurationModifier.cxx | 13 ++- 14 files changed, 298 insertions(+), 26 deletions(-) rename src/{Brain => Common}/EventBrowserTabNew.cxx (100%) rename src/{Brain => Common}/EventBrowserTabNew.h (100%) create mode 100644 src/Common/EventBrowserTabNewClone.cxx create mode 100644 src/Common/EventBrowserTabNewClone.h diff --git a/src/Brain/CMakeLists.txt b/src/Brain/CMakeLists.txt index 704cbf46f..2fcbc2b66 100644 --- a/src/Brain/CMakeLists.txt +++ b/src/Brain/CMakeLists.txt @@ -81,7 +81,6 @@ EventBrainStructureGetAll.h EventBrowserTabGet.h EventBrowserTabGetAll.h EventBrowserTabGetAllViewed.h -EventBrowserTabNew.h EventBrowserWindowContent.h EventChartOverlayValidate.h EventDataFileAdd.h @@ -233,7 +232,6 @@ EventBrainStructureGetAll.cxx EventBrowserTabGet.cxx EventBrowserTabGetAll.cxx EventBrowserTabGetAllViewed.cxx -EventBrowserTabNew.cxx EventBrowserWindowContent.cxx EventChartOverlayValidate.cxx EventDataFileAdd.cxx diff --git a/src/Brain/SessionManager.cxx b/src/Brain/SessionManager.cxx index 00db0f616..bd5567fdb 100644 --- a/src/Brain/SessionManager.cxx +++ b/src/Brain/SessionManager.cxx @@ -43,6 +43,7 @@ #include "EventBrowserTabGetAll.h" #include "EventBrowserTabIndicesGetAll.h" #include "EventBrowserTabNew.h" +#include "EventBrowserTabNewClone.h" #include "EventBrowserWindowContent.h" #include "EventCaretPreferencesGet.h" #include "EventModelAdd.h" @@ -95,6 +96,7 @@ SessionManager::SessionManager() EventManager::get()->addEventListener(this, EventTypeEnum::EVENT_BROWSER_TAB_GET_ALL); EventManager::get()->addEventListener(this, EventTypeEnum::EVENT_BROWSER_TAB_INDICES_GET_ALL); EventManager::get()->addEventListener(this, EventTypeEnum::EVENT_BROWSER_TAB_NEW); + EventManager::get()->addEventListener(this, EventTypeEnum::EVENT_BROWSER_TAB_NEW_CLONE); EventManager::get()->addEventListener(this, EventTypeEnum::EVENT_BROWSER_WINDOW_CONTENT); EventManager::get()->addEventListener(this, EventTypeEnum::EVENT_CARET_PREFERENCES_GET); EventManager::get()->addEventListener(this, EventTypeEnum::EVENT_MODEL_ADD); @@ -316,8 +318,41 @@ SessionManager::receiveEvent(Event* event) break; } } - if (createdTab == false) { - tabEvent->setErrorMessage("Workbench is exhausted. It cannot create any more tabs."); + if ( ! createdTab) { + tabEvent->setErrorMessage("Workbench is unable to create tabs, all tabs are in use."); + } + } + else if (event->getEventType() == EventTypeEnum::EVENT_BROWSER_TAB_NEW_CLONE) { + EventBrowserTabNewClone* cloneTabEvent = dynamic_cast(event); + CaretAssert(cloneTabEvent); + + cloneTabEvent->setEventProcessed(); + + const int32_t cloneTabIndex = cloneTabEvent->getIndexOfBrowserTabThatWasCloned(); + if ((cloneTabIndex < 0) + || (cloneTabIndex >= BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS)) { + cloneTabEvent->setErrorMessage("Invalid tab for cloning index=" + AString::number(cloneTabIndex)); + return; + } + + int32_t newTabIndex(-1); + for (int32_t i = 0; i < BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS; i++) { + if (m_browserTabs[i] == NULL) { + newTabIndex = i; + break; + } + } + if (newTabIndex >= 0) { + CaretAssertArrayIndex(m_browserTabs, BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS, cloneTabIndex); + CaretAssertArrayIndex(m_browserTabs, BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS, newTabIndex); + m_browserTabs[newTabIndex] = new BrowserTabContent(newTabIndex); + m_browserTabs[newTabIndex]->update(m_models); + m_browserTabs[newTabIndex]->cloneBrowserTabContent(m_browserTabs[cloneTabIndex]); + cloneTabEvent->setNewBrowserTab(m_browserTabs[newTabIndex], + newTabIndex); + } + else { + cloneTabEvent->setErrorMessage("Workbench is unable to create tabs, all tabs are in use."); } } else if (event->getEventType() == EventTypeEnum::EVENT_BROWSER_TAB_DELETE) { diff --git a/src/Common/CMakeLists.txt b/src/Common/CMakeLists.txt index 451bf351c..ac9de4108 100644 --- a/src/Common/CMakeLists.txt +++ b/src/Common/CMakeLists.txt @@ -99,6 +99,8 @@ Event.h EventAlertUser.h EventBrowserTabDelete.h EventBrowserTabIndicesGetAll.h +EventBrowserTabNew.h +EventBrowserTabNewClone.h EventCaretPreferencesGet.h EventGetViewportSize.h EventListenerInterface.h @@ -205,6 +207,8 @@ Event.cxx EventAlertUser.cxx EventBrowserTabDelete.cxx EventBrowserTabIndicesGetAll.cxx +EventBrowserTabNew.cxx +EventBrowserTabNewClone.cxx EventCaretPreferencesGet.cxx EventGetViewportSize.cxx EventListenerInterface.cxx diff --git a/src/Brain/EventBrowserTabNew.cxx b/src/Common/EventBrowserTabNew.cxx similarity index 100% rename from src/Brain/EventBrowserTabNew.cxx rename to src/Common/EventBrowserTabNew.cxx diff --git a/src/Brain/EventBrowserTabNew.h b/src/Common/EventBrowserTabNew.h similarity index 100% rename from src/Brain/EventBrowserTabNew.h rename to src/Common/EventBrowserTabNew.h diff --git a/src/Common/EventBrowserTabNewClone.cxx b/src/Common/EventBrowserTabNewClone.cxx new file mode 100644 index 000000000..1456c0264 --- /dev/null +++ b/src/Common/EventBrowserTabNewClone.cxx @@ -0,0 +1,102 @@ + +/*LICENSE_START*/ +/* + * Copyright (C) 2018 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + +#define __EVENT_BROWSER_TAB_NEW_CLONE_DECLARE__ +#include "EventBrowserTabNewClone.h" +#undef __EVENT_BROWSER_TAB_NEW_CLONE_DECLARE__ + +#include "CaretAssert.h" +#include "EventTypeEnum.h" + +using namespace caret; + + + +/** + * \class caret::EventBrowserTabNewClone + * \brief Get a browser tab that is cloned from another browser tab + * \ingroup Common + * + * Get a browser tab that is cloned from another browser tab + */ + +/** + * Constructor. + * + * @param indexOfBrowserTabThatWillBeCloned + * Index of browser tab that is cloned. + */ +EventBrowserTabNewClone::EventBrowserTabNewClone(const int32_t indexOfBrowserTabThatWillBeCloned) +: Event(EventTypeEnum::EVENT_BROWSER_TAB_NEW_CLONE), +m_indexOfBrowserTabThatWasClonded(indexOfBrowserTabThatWillBeCloned) +{ + +} + +/** + * Destructor. + */ +EventBrowserTabNewClone::~EventBrowserTabNewClone() +{ +} + +/** + * @return The new browser tab. + */ +BrowserTabContent* +EventBrowserTabNewClone::getNewBrowserTab() const +{ + return m_newBrowserTabContent; +} + +/** + * Set the new browser tab and its index. + * + * @param newBrowserTab + * The new browser tab. + * @param newBrowserTabIndex + * Index of the new browser tab. + */ +void +EventBrowserTabNewClone::setNewBrowserTab(BrowserTabContent* newBrowserTab, + const int32_t newBrowserTabIndex) +{ + m_newBrowserTabContent = newBrowserTab; + m_newBrowserTabIndex = newBrowserTabIndex; +} + +/** + * @return Index of the new browser index. + */ +int32_t +EventBrowserTabNewClone::getNewBrowserTabIndex() const +{ + return m_newBrowserTabIndex; +} + +/** + * @return Index of the browser tab that was cloned + */ +int32_t +EventBrowserTabNewClone::getIndexOfBrowserTabThatWasCloned() const +{ + return m_indexOfBrowserTabThatWasClonded; +} diff --git a/src/Common/EventBrowserTabNewClone.h b/src/Common/EventBrowserTabNewClone.h new file mode 100644 index 000000000..e04dcb228 --- /dev/null +++ b/src/Common/EventBrowserTabNewClone.h @@ -0,0 +1,74 @@ +#ifndef __EVENT_BROWSER_TAB_NEW_CLONE_H__ +#define __EVENT_BROWSER_TAB_NEW_CLONE_H__ + +/*LICENSE_START*/ +/* + * Copyright (C) 2018 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + + + +#include + +#include "Event.h" + + + +namespace caret { + + class BrowserTabContent; + + class EventBrowserTabNewClone : public Event { + + public: + EventBrowserTabNewClone(const int32_t indexOfBrowserTabThatWillBeCloned); + + virtual ~EventBrowserTabNewClone(); + + EventBrowserTabNewClone(const EventBrowserTabNewClone&) = delete; + + EventBrowserTabNewClone& operator=(const EventBrowserTabNewClone&) = delete; + + BrowserTabContent* getNewBrowserTab() const; + + void setNewBrowserTab(BrowserTabContent* newBrowserTab, + const int32_t newBrowserTabIndex); + + int32_t getNewBrowserTabIndex() const; + + int32_t getIndexOfBrowserTabThatWasCloned() const; + + // ADD_NEW_METHODS_HERE + + private: + // ADD_NEW_MEMBERS_HERE + + const int32_t m_indexOfBrowserTabThatWasClonded = -1; + + BrowserTabContent* m_newBrowserTabContent = NULL; + + int32_t m_newBrowserTabIndex = -1; + + }; + +#ifdef __EVENT_BROWSER_TAB_NEW_CLONE_DECLARE__ + // +#endif // __EVENT_BROWSER_TAB_NEW_CLONE_DECLARE__ + +} // namespace +#endif //__EVENT_BROWSER_TAB_NEW_CLONE_H__ diff --git a/src/Common/EventManager.cxx b/src/Common/EventManager.cxx index 4507466d6..806824721 100644 --- a/src/Common/EventManager.cxx +++ b/src/Common/EventManager.cxx @@ -400,6 +400,7 @@ EventManager::sendSimpleEvent(const EventTypeEnum::Enum eventType) case EventTypeEnum::EVENT_BROWSER_TAB_GET_ALL_VIEWED: case EventTypeEnum::EVENT_BROWSER_TAB_INDICES_GET_ALL: case EventTypeEnum::EVENT_BROWSER_TAB_NEW: + case EventTypeEnum::EVENT_BROWSER_TAB_NEW_CLONE: case EventTypeEnum::EVENT_BROWSER_WINDOW_CONTENT: case EventTypeEnum::EVENT_BROWSER_WINDOW_CREATE_TABS: case EventTypeEnum::EVENT_BROWSER_WINDOW_DRAWING_CONTENT_GET: diff --git a/src/Common/EventTypeEnum.cxx b/src/Common/EventTypeEnum.cxx index 0b2efe1ab..c7a6d5d0e 100644 --- a/src/Common/EventTypeEnum.cxx +++ b/src/Common/EventTypeEnum.cxx @@ -142,6 +142,10 @@ EventTypeEnum::initialize() "EVENT_BROWSER_TAB_NEW", "Create a browser tab")); + enumData.push_back(EventTypeEnum(EVENT_BROWSER_TAB_NEW_CLONE, + "EVENT_BROWSER_TAB_NEW_CLONE", + "Create a browser tab by cloning an existing browser tab")); + enumData.push_back(EventTypeEnum(EVENT_BROWSER_WINDOW_CONTENT, "EVENT_BROWSER_WINDOW_CONTENT", "Event for browser window content")); diff --git a/src/Common/EventTypeEnum.h b/src/Common/EventTypeEnum.h index f4f62f816..db11074ad 100644 --- a/src/Common/EventTypeEnum.h +++ b/src/Common/EventTypeEnum.h @@ -76,6 +76,8 @@ public: EVENT_BROWSER_TAB_GET_ALL_VIEWED, /** Create a new browser tab */ EVENT_BROWSER_TAB_NEW, + /** Create a new browser tab by cloning an existing browser tab */ + EVENT_BROWSER_TAB_NEW_CLONE, /** Event for browser window content */ EVENT_BROWSER_WINDOW_CONTENT, /** Get the content of a browser window */ diff --git a/src/Files/AnnotationFile.cxx b/src/Files/AnnotationFile.cxx index ed6a250cb..15022f5b9 100644 --- a/src/Files/AnnotationFile.cxx +++ b/src/Files/AnnotationFile.cxx @@ -44,6 +44,7 @@ #include "EventAnnotationGrouping.h" #include "EventAnnotationTextSubstitutionInvalidate.h" #include "EventBrowserTabDelete.h" +#include "EventBrowserTabNewClone.h" #include "EventManager.h" #include "EventTileTabsConfigurationModification.h" #include "GiftiMetaData.h" @@ -222,10 +223,11 @@ AnnotationFile::initializeAnnotationFile() EventManager::get()->addEventListener(this, EventTypeEnum::EVENT_ANNOTATION_GROUP_GET_WITH_KEY); EventManager::get()->addEventListener(this, EventTypeEnum::EVENT_ANNOTATION_GROUPING); EventManager::get()->addEventListener(this, EventTypeEnum::EVENT_ANNOTATION_TEXT_SUBSTITUTION_INVALIDATE); - EventManager::get()->addEventListener(this, EventTypeEnum::EVENT_BROWSER_TAB_DELETE); /* NEED THIS AFTER Tile Tabs have been modified */ EventManager::get()->addProcessedEventListener(this, EventTypeEnum::EVENT_TILE_TABS_MODIFICATION); + EventManager::get()->addProcessedEventListener(this, EventTypeEnum::EVENT_BROWSER_TAB_DELETE); + EventManager::get()->addProcessedEventListener(this, EventTypeEnum::EVENT_BROWSER_TAB_NEW_CLONE); } /** @@ -462,9 +464,16 @@ AnnotationFile::receiveEvent(Event* event) CaretAssert(deleteEvent); const int32_t tabIndex = deleteEvent->getBrowserTabIndex(); - if (removeAnnotationsInTab(tabIndex)) { - deleteEvent->setEventProcessed(); - } + removeAnnotationsInTab(tabIndex); + } + else if (event->getEventType() == EventTypeEnum::EVENT_BROWSER_TAB_NEW_CLONE) { + EventBrowserTabNewClone* cloneTabEvent = dynamic_cast(event); + CaretAssert(cloneTabEvent); + + const int32_t cloneToTabIndex = cloneTabEvent->getNewBrowserTabIndex(); + const int32_t cloneFromTabIndex = cloneTabEvent->getIndexOfBrowserTabThatWasCloned(); + cloneAnnotationsFromTabToTab(cloneFromTabIndex, + cloneToTabIndex); } else if (event->getEventType() == EventTypeEnum::EVENT_ANNOTATION_TEXT_SUBSTITUTION_INVALIDATE) { EventAnnotationTextSubstitutionInvalidate* textSubEvent = dynamic_cast(event); @@ -941,6 +950,50 @@ AnnotationFile::removeAnnotationPrivate(Annotation* annotation, return false; } +/** + * Clone annotations in 'fromTabIndex' to 'toTabIndex' + * + * @param fromTabIndex + * Clone annotations from this tab index + * @param toTabIndex + * Clone annotations into this tab index + * @return + * True if any annotations were cloned. + */ +bool +AnnotationFile::cloneAnnotationsFromTabToTab(const int32_t fromTabIndex, + const int32_t toTabIndex) +{ + /* + * Find annotation group(s) in tab space + * with the given tab index. + */ + bool annotationsWereClonedFlag(false); + for (AnnotationGroupIterator groupIter = m_annotationGroups.begin(); + groupIter != m_annotationGroups.end(); + groupIter++) { + QSharedPointer& group = *groupIter; + if (group->getCoordinateSpace() == AnnotationCoordinateSpaceEnum::TAB) { + if (group->getTabOrWindowIndex() == fromTabIndex) { + std::vector annotations; + group->getAllAnnotations(annotations); + + for (auto ann : annotations) { + CaretAssert(ann->getTabIndex() == fromTabIndex); + Annotation* newAnn = ann->clone(); + newAnn->setTabIndex(toTabIndex); + addAnnotationPrivate(newAnn, + generateUniqueKey()); + annotationsWereClonedFlag = true; + } + } + } + } + + return annotationsWereClonedFlag; +} + + /** * Remove all annotations in tab space in the given tab. * diff --git a/src/Files/AnnotationFile.h b/src/Files/AnnotationFile.h index 4495914b5..a478d78ed 100644 --- a/src/Files/AnnotationFile.h +++ b/src/Files/AnnotationFile.h @@ -203,6 +203,9 @@ namespace caret { bool removeAnnotationPrivate(Annotation* annotation, const bool keepAnnotationForUndoRedoFlag); + bool cloneAnnotationsFromTabToTab(const int32_t fromTabIndex, + const int32_t toTabIndex); + bool removeAnnotationsInTab(const int32_t tabIndex); int32_t generateUniqueKey(); diff --git a/src/GuiQt/BrainBrowserWindowToolBar.cxx b/src/GuiQt/BrainBrowserWindowToolBar.cxx index 4b85301f5..f5a7e3147 100644 --- a/src/GuiQt/BrainBrowserWindowToolBar.cxx +++ b/src/GuiQt/BrainBrowserWindowToolBar.cxx @@ -78,6 +78,7 @@ #include "CursorDisplayScoped.h" #include "DeveloperFlagsEnum.h" #include "DisplayPropertiesBorders.h" +#include "EventBrowserTabNewClone.h" #include "EventBrowserTabDelete.h" #include "EventBrowserTabGet.h" #include "EventBrowserTabGetAll.h" @@ -611,16 +612,19 @@ BrainBrowserWindowToolBar::insertAndCloneTabContentAtTabBarIndex(const BrowserTa CursorDisplayScoped cursor; cursor.showWaitCursor(); - AString errorMessage; - BrowserTabContent* tabContent = this->createNewTab(errorMessage); - if (tabContent == NULL) { + EventBrowserTabNewClone cloneTabEvent(browserTabContentToBeCloned->getTabNumber()); + EventManager::get()->sendEvent(cloneTabEvent.getPointer()); + if (cloneTabEvent.isError()) { cursor.restoreCursor(); QMessageBox::critical(this, "", - errorMessage); + cloneTabEvent.getErrorMessage()); return; } + BrowserTabContent* tabContent = cloneTabEvent.getNewBrowserTab(); + CaretAssert(tabContent); + if (tabBarIndex >= 0){ insertTabContentPrivate(InsertTabMode::AT_TAB_BAR_INDEX, tabContent, @@ -632,13 +636,6 @@ BrainBrowserWindowToolBar::insertAndCloneTabContentAtTabBarIndex(const BrowserTa -1); } - if (browserTabContentToBeCloned != NULL) { - /* - * New tab is clone of tab that was displayed when the new tab was created. - */ - tabContent->cloneBrowserTabContent(const_cast(browserTabContentToBeCloned)); - } - this->updateToolBar(); EventManager::get()->sendEvent(EventSurfaceColoringInvalidate().getPointer()); diff --git a/src/GuiQt/TileTabsConfigurationModifier.cxx b/src/GuiQt/TileTabsConfigurationModifier.cxx index 9f54bef16..995458df0 100644 --- a/src/GuiQt/TileTabsConfigurationModifier.cxx +++ b/src/GuiQt/TileTabsConfigurationModifier.cxx @@ -29,6 +29,7 @@ #include "BrainOpenGLViewportContent.h" #include "BrowserTabContent.h" #include "CaretAssert.h" +#include "EventBrowserTabNewClone.h" #include "EventBrowserTabDelete.h" #include "EventBrowserTabNew.h" #include "EventBrowserWindowTileTabOperation.h" @@ -554,16 +555,14 @@ TileTabsConfigurationModifier::RowColumnContent::clone(AString& errorMessageOut) for (auto te : cloned->m_tabElements) { if (te->m_browserTabContent != NULL) { - EventBrowserTabNew newTabEvent; - EventManager::get()->sendEvent(newTabEvent.getPointer()); - if (newTabEvent.isError()) { - errorMessageOut.appendWithNewLine(newTabEvent.getErrorMessage()); + EventBrowserTabNewClone cloneTabEvent(te->m_browserTabContent->getTabNumber()); + EventManager::get()->sendEvent(cloneTabEvent.getPointer()); + if (cloneTabEvent.isError()) { + errorMessageOut.appendWithNewLine(cloneTabEvent.getErrorMessage()); te->m_browserTabContent = NULL; } else { - BrowserTabContent* copyTab = te->m_browserTabContent; - te->m_browserTabContent = newTabEvent.getBrowserTab(); - te->m_browserTabContent->cloneBrowserTabContent(copyTab); + te->m_browserTabContent = cloneTabEvent.getNewBrowserTab(); } } } -- GitLab From 8592280f6e701a39eeb714c5caa1d28d2abc5fed Mon Sep 17 00:00:00 2001 From: John Harwell Date: Mon, 29 Oct 2018 15:20:34 -0500 Subject: [PATCH 095/427] WB-668 Titling columns in tile tabs: Changed Caret's Preferences to write the new version of TileTabsConfiguration. This fixes a bug on the Tile Tabs Configuration Dialog with incorrect configuration replace/save. --- src/Common/CaretPreferences.cxx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Common/CaretPreferences.cxx b/src/Common/CaretPreferences.cxx index 7e72c90f4..f59d9bed5 100644 --- a/src/Common/CaretPreferences.cxx +++ b/src/Common/CaretPreferences.cxx @@ -362,7 +362,7 @@ CaretPreferences::writeTileTabsConfigurations() for (int32_t i = 0; i < numViews; i++) { this->qSettings->setArrayIndex(i); this->qSettings->setValue(AString::number(i), - this->tileTabsConfigurations[i]->encodeVersionInXML(1)); + this->tileTabsConfigurations[i]->encodeInXML()); } this->qSettings->endArray(); this->qSettings->sync(); -- GitLab From e81e8f5496d101e1808623a20ca893ba626347a4 Mon Sep 17 00:00:00 2001 From: John Harwell Date: Mon, 29 Oct 2018 16:22:24 -0500 Subject: [PATCH 096/427] WB-668 Titling columns in tile tabs: Updated versions of AnnotationFile and SceneFile since adding new 'spacer' annotation space will cause errors in previous versions of wb_view. --- src/Files/AnnotationFileXmlFormatBase.h | 4 ++++ src/Files/AnnotationFileXmlReader.cxx | 6 +++++- src/Files/AnnotationFileXmlWriter.cxx | 2 +- src/Files/SceneFile.cxx | 2 ++ src/Files/SceneFile.h | 2 +- src/GuiQt/TileTabsConfigurationDialog.cxx | 6 +++++- 6 files changed, 18 insertions(+), 4 deletions(-) diff --git a/src/Files/AnnotationFileXmlFormatBase.h b/src/Files/AnnotationFileXmlFormatBase.h index 8d006e8d1..947e71b2e 100644 --- a/src/Files/AnnotationFileXmlFormatBase.h +++ b/src/Files/AnnotationFileXmlFormatBase.h @@ -164,6 +164,8 @@ namespace caret { static const int32_t XML_VERSION_TWO; + static const int32_t XML_VERSION_THREE; + // ADD_NEW_MEMBERS_HERE }; @@ -286,6 +288,8 @@ namespace caret { const int32_t AnnotationFileXmlFormatBase::XML_VERSION_TWO = 2; + const int32_t AnnotationFileXmlFormatBase::XML_VERSION_THREE = 3; + #endif // __ANNOTATION_FILE_XML_FORMAT_BASE_DECLARE__ } // namespace diff --git a/src/Files/AnnotationFileXmlReader.cxx b/src/Files/AnnotationFileXmlReader.cxx index edf601885..c9fd92ea2 100644 --- a/src/Files/AnnotationFileXmlReader.cxx +++ b/src/Files/AnnotationFileXmlReader.cxx @@ -133,7 +133,7 @@ AnnotationFileXmlReader::readFileFromString(const QString& fileInString, */ m_stream.grabNew(new QXmlStreamReader(fileInString)); - readFileContentFromXmlStreamReader("SceneFileName", + readFileContentFromXmlStreamReader("AnnotationsInSceneFile", annotationFile); if (m_stream->hasError()) { @@ -198,6 +198,10 @@ AnnotationFileXmlReader::readFileContentFromXmlStreamReader(const QString& filen else if (m_fileVersionNumber == XML_VERSION_TWO) { readVersionTwo(annotationFile); } + else if (m_fileVersionNumber == XML_VERSION_THREE) { + /* NOTE: version 3 added new coordinate space "SPACER " */ + readVersionTwo(annotationFile); + } else { m_streamHelper->throwDataFileException("File version number " + versionText.toString() diff --git a/src/Files/AnnotationFileXmlWriter.cxx b/src/Files/AnnotationFileXmlWriter.cxx index 6e2737a69..440b766e2 100644 --- a/src/Files/AnnotationFileXmlWriter.cxx +++ b/src/Files/AnnotationFileXmlWriter.cxx @@ -168,7 +168,7 @@ AnnotationFileXmlWriter::writeFileContentToXmlStreamWriter(const AnnotationFile* m_stream->writeStartElement(ELEMENT_ANNOTATION_FILE); m_stream->writeAttribute(ATTRIBUTE_VERSION, - AString::number(XML_VERSION_TWO)); + AString::number(XML_VERSION_THREE)); m_streamHelper->writeMetaData(annotationFile->getFileMetaData()); diff --git a/src/Files/SceneFile.cxx b/src/Files/SceneFile.cxx index f1b7541c8..9a587a3e6 100644 --- a/src/Files/SceneFile.cxx +++ b/src/Files/SceneFile.cxx @@ -648,6 +648,8 @@ SceneFile::writeFile(const AString& filename) try { // // Format the version string so that it ends with at most one zero + // Version 2 Added for New Annotation Coordinate Space "SPACER" since annotations + // may be saved to scene file. // const AString versionString = AString::number(SceneFile::getFileVersion(), 'f', diff --git a/src/Files/SceneFile.h b/src/Files/SceneFile.h index 84e4d76e0..506e9f22a 100644 --- a/src/Files/SceneFile.h +++ b/src/Files/SceneFile.h @@ -204,7 +204,7 @@ namespace caret { const AString SceneFile::XML_TAG_SCENE_FILE = "SceneFile"; const AString SceneFile::XML_ATTRIBUTE_VERSION = "Version"; const AString SceneFile::XML_TAG_SCENE_INFO_DIRECTORY_TAG = "SceneInfoDirectory"; - const float SceneFile::s_sceneFileVersion = 3.0; + const float SceneFile::s_sceneFileVersion = 4.0; #endif // __SCENE_FILE_DECLARE__ } // namespace diff --git a/src/GuiQt/TileTabsConfigurationDialog.cxx b/src/GuiQt/TileTabsConfigurationDialog.cxx index ae143e773..1ea27d0b6 100644 --- a/src/GuiQt/TileTabsConfigurationDialog.cxx +++ b/src/GuiQt/TileTabsConfigurationDialog.cxx @@ -265,13 +265,13 @@ TileTabsConfigurationDialog::getBrowserWindow() /** * @return The browser window content for the selected window index. + * May be NULL when no tabs are open. */ BrowserWindowContent* TileTabsConfigurationDialog::getBrowserWindowContent() { BrainBrowserWindow* bbw = getBrowserWindow(); BrowserWindowContent* bwc = bbw->getBrowerWindowContent(); - CaretAssert(bwc); return bwc; } @@ -662,6 +662,10 @@ void TileTabsConfigurationDialog::updateDialog() { BrowserWindowContent* browserWindowContent = getBrowserWindowContent(); + if (browserWindowContent == NULL) { + return; + } + switch (browserWindowContent->getTileTabsConfigurationMode()) { case TileTabsConfigurationModeEnum::AUTOMATIC: m_automaticConfigurationRadioButton->setChecked(true); -- GitLab From f5b5b2ec4272cfafaa3b182253ce40dca00cb265 Mon Sep 17 00:00:00 2001 From: John Harwell Date: Wed, 31 Oct 2018 09:42:08 -0500 Subject: [PATCH 097/427] Fixed crash that occurred if the window's custom tile tabs configuration failed to restore from a scene. If a tile tabs configuration is missing a name, create a generic name ("Config_)" for it. --- src/Brain/BrowserWindowContent.cxx | 6 +++--- src/Common/TileTabsConfiguration.cxx | 6 ++++-- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/src/Brain/BrowserWindowContent.cxx b/src/Brain/BrowserWindowContent.cxx index d2afa5c09..fe12a33f5 100644 --- a/src/Brain/BrowserWindowContent.cxx +++ b/src/Brain/BrowserWindowContent.cxx @@ -513,7 +513,7 @@ BrowserWindowContent::restoreFromScene(const SceneAttributes* sceneAttributes, + "\" from BrowserWindowContent: \"" + tileTabsConfig + "\""); - m_customTileTabsConfiguration.reset(); + m_customTileTabsConfiguration.reset(new TileTabsConfiguration()); } /* @@ -539,7 +539,7 @@ BrowserWindowContent::restoreFromScene(const SceneAttributes* sceneAttributes, + "\" from BrowserWindowContent: \"" + stringTileTabsConfig + "\""); - m_customTileTabsConfiguration.reset(); + m_customTileTabsConfiguration.reset(new TileTabsConfiguration()); } } } @@ -629,7 +629,7 @@ BrowserWindowContent::restoreFromOldBrainBrowserWindowScene(const SceneAttribute + "\" from OLD BrowserWindowContent: \"" + tileTabsConfigString + "\""); - m_customTileTabsConfiguration.reset(); + m_customTileTabsConfiguration.reset(new TileTabsConfiguration()); } } diff --git a/src/Common/TileTabsConfiguration.cxx b/src/Common/TileTabsConfiguration.cxx index a1d7f0ef9..67ea00aea 100644 --- a/src/Common/TileTabsConfiguration.cxx +++ b/src/Common/TileTabsConfiguration.cxx @@ -967,9 +967,11 @@ TileTabsConfiguration::decodeFromXMLWithStreamReaderVersionTwo(QXmlStreamReader& } } + static int32_t missingNameCounter = 1; if (name.isEmpty()) { - message.append(s_nameTagName - + " not found or invalid. "); + name = ("Config_" + + AString::number(missingNameCounter)); + missingNameCounter++; } if (uniqueID.isEmpty()) { uniqueID = SystemUtilities::createUniqueID(); -- GitLab From 4742aaa1a59db8e42474b6c92958234b0f69d527 Mon Sep 17 00:00:00 2001 From: John Harwell Date: Wed, 31 Oct 2018 09:45:43 -0500 Subject: [PATCH 098/427] Changed the scene version back to 3 since annotation changes do not alter the scene file. When writing an annotation file, test it for the presence of annotations in 'spacer' coordinate space, and set the files version to 2 when there are no 'spacer' space annotations. These changes will allow older versions of wb_view to load the scene file. However, if the scene contains annotations in 'spacer' space, the annotations will not restore correctly in older versions of wb_view (but otherwise the scene will still load). --- src/Files/AnnotationFile.cxx | 20 ++++++++++++++++++++ src/Files/AnnotationFile.h | 2 ++ src/Files/AnnotationFileXmlReader.cxx | 5 ++++- src/Files/AnnotationFileXmlWriter.cxx | 15 +++++++++++++-- src/Files/SceneFile.cxx | 2 +- src/Files/SceneFile.h | 2 +- 6 files changed, 41 insertions(+), 5 deletions(-) diff --git a/src/Files/AnnotationFile.cxx b/src/Files/AnnotationFile.cxx index 15022f5b9..92ccbcd61 100644 --- a/src/Files/AnnotationFile.cxx +++ b/src/Files/AnnotationFile.cxx @@ -1078,6 +1078,26 @@ AnnotationFile::getAllAnnotationGroups(std::vector& annotation } } +/** + * Does this file contain annotations in the given coordinate space? + * + * @param coordinateSpace + * The coordinate space. + * @return + * True if there are annotations in the given coordinate space, else false. + */ +bool +AnnotationFile::hasAnnotationsInCoordinateSpace(const AnnotationCoordinateSpaceEnum::Enum coordinateSpace) const +{ + for (const auto groupIter : m_annotationGroups) { + if (groupIter->getCoordinateSpace() == coordinateSpace) { + return true; + } + } + + return false; +} + /** * Group annotations. * diff --git a/src/Files/AnnotationFile.h b/src/Files/AnnotationFile.h index a478d78ed..be344a600 100644 --- a/src/Files/AnnotationFile.h +++ b/src/Files/AnnotationFile.h @@ -121,6 +121,8 @@ namespace caret { virtual DataFileContentCopyMoveInterface* newInstanceOfDataFile() const; + bool hasAnnotationsInCoordinateSpace(const AnnotationCoordinateSpaceEnum::Enum coordinateSpace) const; + // ADD_NEW_METHODS_HERE diff --git a/src/Files/AnnotationFileXmlReader.cxx b/src/Files/AnnotationFileXmlReader.cxx index c9fd92ea2..4788b9cb4 100644 --- a/src/Files/AnnotationFileXmlReader.cxx +++ b/src/Files/AnnotationFileXmlReader.cxx @@ -199,7 +199,10 @@ AnnotationFileXmlReader::readFileContentFromXmlStreamReader(const QString& filen readVersionTwo(annotationFile); } else if (m_fileVersionNumber == XML_VERSION_THREE) { - /* NOTE: version 3 added new coordinate space "SPACER " */ + /* + * NOTE: version 3 added new coordinate space "SPACER " + * and otherwise is the same as version 2 format + */ readVersionTwo(annotationFile); } else { diff --git a/src/Files/AnnotationFileXmlWriter.cxx b/src/Files/AnnotationFileXmlWriter.cxx index 440b766e2..e3c2812a5 100644 --- a/src/Files/AnnotationFileXmlWriter.cxx +++ b/src/Files/AnnotationFileXmlWriter.cxx @@ -167,8 +167,19 @@ AnnotationFileXmlWriter::writeFileContentToXmlStreamWriter(const AnnotationFile* m_stream->writeStartDocument(); m_stream->writeStartElement(ELEMENT_ANNOTATION_FILE); - m_stream->writeAttribute(ATTRIBUTE_VERSION, - AString::number(XML_VERSION_THREE)); + + /* + * To improve backward compatibility, only write version 3 if there + * are annotations in 'spacer' coordinate space. + */ + if (annotationFile->hasAnnotationsInCoordinateSpace(AnnotationCoordinateSpaceEnum::SPACER)) { + m_stream->writeAttribute(ATTRIBUTE_VERSION, + AString::number(XML_VERSION_THREE)); + } + else { + m_stream->writeAttribute(ATTRIBUTE_VERSION, + AString::number(XML_VERSION_TWO)); + } m_streamHelper->writeMetaData(annotationFile->getFileMetaData()); diff --git a/src/Files/SceneFile.cxx b/src/Files/SceneFile.cxx index 9a587a3e6..7a2483036 100644 --- a/src/Files/SceneFile.cxx +++ b/src/Files/SceneFile.cxx @@ -648,7 +648,7 @@ SceneFile::writeFile(const AString& filename) try { // // Format the version string so that it ends with at most one zero - // Version 2 Added for New Annotation Coordinate Space "SPACER" since annotations + // Version 4 Added for New Annotation Coordinate Space "SPACER" since annotations // may be saved to scene file. // const AString versionString = AString::number(SceneFile::getFileVersion(), diff --git a/src/Files/SceneFile.h b/src/Files/SceneFile.h index 506e9f22a..84e4d76e0 100644 --- a/src/Files/SceneFile.h +++ b/src/Files/SceneFile.h @@ -204,7 +204,7 @@ namespace caret { const AString SceneFile::XML_TAG_SCENE_FILE = "SceneFile"; const AString SceneFile::XML_ATTRIBUTE_VERSION = "Version"; const AString SceneFile::XML_TAG_SCENE_INFO_DIRECTORY_TAG = "SceneInfoDirectory"; - const float SceneFile::s_sceneFileVersion = 4.0; + const float SceneFile::s_sceneFileVersion = 3.0; #endif // __SCENE_FILE_DECLARE__ } // namespace -- GitLab From 4fc4508eef4dc8758e7f6110b8b63d37a949119b Mon Sep 17 00:00:00 2001 From: John Harwell Date: Thu, 1 Nov 2018 12:48:50 -0500 Subject: [PATCH 099/427] WB-668 Titling columns in tile tabs: Add tool tips to widgets in the Tile Tabs Configuration Dialog. In same dialog, display a percentage sign for rows/columns that use percentage stretching. --- src/GuiQt/TileTabsConfigurationDialog.cxx | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/src/GuiQt/TileTabsConfigurationDialog.cxx b/src/GuiQt/TileTabsConfigurationDialog.cxx index 1ea27d0b6..b7df4944e 100644 --- a/src/GuiQt/TileTabsConfigurationDialog.cxx +++ b/src/GuiQt/TileTabsConfigurationDialog.cxx @@ -1026,6 +1026,18 @@ m_element(NULL) m_indexLabel = new QLabel(QString::number(m_index + 1)); m_indexLabel->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); + const AString rowColText((rowColumnType == EventTileTabsConfigurationModification::RowColumnType::ROW) + ? "Row" + : "Column"); + const AString contructionToolTip(WuQtUtilities::createWordWrappedToolTipText("Delete, Duplicate, or Move " + + rowColText)); + const AString contentToolTip(WuQtUtilities::createWordWrappedToolTipText("Content of the " + + rowColText + + ": Spacer (empty space for Annotations) " + "or Tabs (Browser Tabs)")); + const AString typeToolTip(WuQtUtilities::createWordWrappedToolTipText("Type of Stretching: Percent or Weight")); + const AString stretchToolTip(WuQtUtilities::createWordWrappedToolTipText("Value of Stretching Percentage [0, 100] or Stretching Weight")); + /* * Construction Tool Button */ @@ -1048,6 +1060,7 @@ m_element(NULL) m_constructionToolButton->setDefaultAction(m_constructionAction); m_constructionToolButton->setPopupMode(QToolButton::InstantPopup); m_constructionToolButton->setFixedWidth(m_constructionToolButton->sizeHint().width()); + m_constructionToolButton->setToolTip(contructionToolTip); /* * Content type combo box @@ -1057,6 +1070,7 @@ m_element(NULL) QObject::connect(m_contentTypeComboBox, &EnumComboBoxTemplate::itemActivated, this, &TileTabElementWidgets::contentTypeActivated); m_contentTypeComboBox->getComboBox()->setFixedWidth(m_contentTypeComboBox->getComboBox()->sizeHint().width()); + m_contentTypeComboBox->getComboBox()->setToolTip(contentToolTip); /* * Stretch type combo box @@ -1066,6 +1080,7 @@ m_element(NULL) QObject::connect(m_stretchTypeComboBox, &EnumComboBoxTemplate::itemActivated, this, &TileTabElementWidgets::stretchTypeActivated); m_stretchTypeComboBox->getComboBox()->setFixedWidth(m_stretchTypeComboBox->getComboBox()->sizeHint().width()); + m_stretchTypeComboBox->getComboBox()->setToolTip(typeToolTip); /* * Stretch value spin box @@ -1078,6 +1093,7 @@ m_element(NULL) QObject::connect(m_stretchValueSpinBox, static_cast(&QDoubleSpinBox::valueChanged), this, &TileTabElementWidgets::stretchValueChanged); m_stretchValueSpinBox->setFixedWidth(m_stretchValueSpinBox->sizeHint().width()); + m_stretchValueSpinBox->setToolTip(stretchToolTip); m_gridLayoutGroup = new WuQGridLayoutGroup(gridLayout); const int32_t rowIndex(gridLayout->rowCount()); @@ -1171,11 +1187,13 @@ TileTabElementWidgets::updateContent(TileTabsRowColumnElement* element) m_stretchValueSpinBox->setRange(0.0, 100.0); m_stretchValueSpinBox->setSingleStep(1.0); m_stretchValueSpinBox->setValue(m_element->getPercentStretch()); + m_stretchValueSpinBox->setSuffix("%"); break; case TileTabsRowColumnStretchTypeEnum::WEIGHT: m_stretchValueSpinBox->setRange(0.0, 1000.0); m_stretchValueSpinBox->setSingleStep(0.1); m_stretchValueSpinBox->setValue(m_element->getWeightStretch()); + m_stretchValueSpinBox->setSuffix(""); break; } } -- GitLab From bc5c7d94d3e8c8a9c9d3985e3c3846b39bac65e3 Mon Sep 17 00:00:00 2001 From: John Harwell Date: Thu, 1 Nov 2018 15:04:15 -0500 Subject: [PATCH 100/427] WB-668 Titling Columns in Tile Tabs: Updated Help Documentation to reflect changes to GUI for new Tile Tabs Dialog updates. --- .../Annotations_1.3.fld/image008.png | Bin 116151 -> 132817 bytes .../Annotations/Annotations_1.3.html | 90 ++++---- .../Tile_Tabs_Configuration.html | 199 ++++++++++++++++-- .../Tile_Tabs_Configuration.png | Bin 61153 -> 103169 bytes 4 files changed, 229 insertions(+), 60 deletions(-) diff --git a/src/Resources/Help/HelpFiles/Annotations/Annotations_1.3.fld/image008.png b/src/Resources/Help/HelpFiles/Annotations/Annotations_1.3.fld/image008.png index 2f6075815c9df155afe458983bc65d94265e6d78..e13dbb33f62df8f54fb5e203bce765c3c16e6779 100644 GIT binary patch delta 132814 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

    @nO%gX}eH`3% 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

    W`+8N-t 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%0D0K0rWrncKkid~en!WWBC!`M{HDB0mF~Vk zp}{gT5{9+*t~|B+p$|K~TO)^oK1HFH-7!;TT{D#*ir`8dAjB6^K|v|^y3o09xv=Up zrD|W)LGtss7I(ive>FRnYUzTxu+A@csxBKj@S$i{BhBtr3w;^gYN(kNW(upGZdO{y-!7fI HNtx@f8dR7EUZrVKKbUjRR#bd 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 Eg1~7VsQ>@~ delta 116017 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�bS9AZ{lT&5p?cu!?pZ5aD`J>^*Ms0>S^pmJ|T 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

    style='font-size:28.0pt'>1.3.2

    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 to WB Annotations

    27 August 2018

    +style='font-size:24.0pt'>01 November 2018

     

    @@ -591,7 +591,7 @@ aspect ratio, padding is added to the horizontal or vertical sides of the window to limit the graphics region in a way that matches the locked aspect ratio.   For example, suppose one creates two annotations in tab coordinate space, a red oval and a fuchsia box as in Figure 3.  Now suppose one increases the width of the +style='font-size:14.0pt'>.  Now suppose one increases the width of the window while the aspect ratio is unlocked.  Notice that the annotations no longer overlay the original anatomical regions, Figure 4.  However, if one locks the aspect ratio and @@ -677,8 +677,8 @@ Figure from A multimodal parcellation of human cerebral cortex (Glasser et al) 

    -

    +

    Figure 7: Window with Annotation ToolBar

    @@ -711,6 +711,19 @@ annotation in chart data space will move.

     

    +

    Spacer Coordinate Space (Sp)

    + +

     

    + +

    When Tile Tabs is enabled, +rows and/or columns in the Tile Tabs Configuration may contain Spacers or Tabs +(Tabs is the default).  Spacers create an empty row (or column) in a Tile +Tabs Configuration so that one has regions for displaying annotations without +underlying brain models.  Each annotation in Spacer Coordinate Space is +assigned to a specific row and column and is visible only when Tile Tabs is +enabled.  The most common use for annotations in this space is as row and +column titles.

    +

    Stereotaxic Coordinate Space (St)

     

    @@ -852,10 +865,10 @@ and one or both may be used.

    of annotations that pictorially describe the mapping of numeric data into a color palette.  Color bars are not created like other annotation types but are enabled for display by pressing the color bar button for a row in the -Overlay ToolBoxÕs Layers tab.  Positioning of color bars is performed automatically -unless the user chooses to manually position them.  Color bars are limited -to Tab and Window spaces.  Editing of color bars is described at the end -of this document.

    +Overlay ToolBoxÕs Layers tab.  Positioning of color bars is performed +automatically unless the user chooses to manually position them.  Color +bars are limited to Tab and Window spaces.  Editing of color bars is +described at the end of this document.

     

    @@ -901,7 +914,7 @@ coordinate and the text alignment properties control the offset of the text relative to the coordinate.  Text characters are drawn in the Font Color.  The Line color, if enabled, draws a box around the text.  The File color, if enabled, draws a background behind the text.  The height of text is -specified as a percentage of the regionÕs height containing the text.  As +specified as a percentage of the regionÕs height containing the text.  As the region changes in height, a corresponding change will occur in the size of the text.

    @@ -1454,9 +1467,9 @@ Controls

     

    The Annotation Line Arrow -Tips section controls the addition of arrows to the end points of a line -annotation.  The top arrow button adds an arrow to the lineÕs start and -the bottom arrow button adds an arrow to the lineÕs end.

    +Tips section controls the addition of arrows to the end points of a line annotation.  +The top arrow button adds an arrow to the lineÕs start and the bottom arrow +button adds an arrow to the lineÕs end.

    Annotation Text Characters

    @@ -1811,8 +1824,8 @@ Selection Controls

    new annotations are placed in either ÒScene AnnotationsÓ or an Annotation File stored to disk.  Scene Annotations are not saved to an annotation file but instead are added to a scene when a a new scene is created.  Note that the -user will need to save the scene file.  Disk annotation files are saved and -opened like other data files.

    +user will need to save the scene file.  Disk annotation files are saved +and opened like other data files.

     

    @@ -1824,19 +1837,19 @@ the user must choose the Space from the top row of buttons (the selected space is highlighted).  Third, the user must click one of the type buttons in the bottom row to choose the type of annotation that is created.  Lastly, the user moves the mouse into the graphics region at which time the mouse -pointer becomes a small ÔplusÕ symbol.  The user must either click the -mouse in the graphics region to insert a default annotation for the type or drag -the mouse to create a rectangular region that bounds the new annotation.  -If the location of the new annotation is incompatible with the selected -annotation space, a dialog pops up that allows the user to choose a different -space or to cancel creation of the annotation. When entering a text annotation, -a dialog will always pop-up for entry of the text.

    +pointer becomes a small ÔplusÕ symbol.  The user must either click the mouse +in the graphics region to insert a default annotation for the type or drag the +mouse to create a rectangular region that bounds the new annotation.  If +the location of the new annotation is incompatible with the selected annotation +space, a dialog pops up that allows the user to choose a different space or to +cancel creation of the annotation. When entering a text annotation, a dialog +will always pop-up for entry of the text.

     

    BEWARE that when drawing -annotations in surface space, the annotation may move a short distance from where -it was drawn.  This is caused by the surface offset attribute of the +annotations in surface space, the annotation may move a short distance from +where it was drawn.  This is caused by the surface offset attribute of the surface coordinate.  While the annotation is attached to the surface vertex closest to the mouse click, the annotation is offset by a vector.  It is this offset vector that causes the annotation to move.  Without the @@ -2094,8 +2107,8 @@ Sensitive (Pop-up) Menu.

    á      -Select All Ð Selects all annotations -displayed in the window.

    +Select All Ð Selects all +annotations displayed in the window.


    @@ -2169,9 +2182,9 @@ deselected.  The selected annotation is highlighted in the graphics region.  The user may select multiple annotations in the features toolbox by holding down the CTRL (Apple) key or the Shift key.  If the CTRL key is held down while the mouse is clicked, the annotation under the mouse is added -to the selected annotations.  If the Shift key is held down while the -mouse is clicked, all annotation from the last selected annotation to the -annotations under the mouse are selected.

    +to the selected annotations.  If the Shift key is held down while the mouse +is clicked, all annotation from the last selected annotation to the annotations +under the mouse are selected.

     

    @@ -2207,8 +2220,8 @@ the annotation the annotation's ÔFill ColorÕ is enabled, the background of the icon is the fill color.  If the annotationÕs line color is enabled, the edges of the icon are drawn in the line color.  If the annotation is a text annotation, -a small square consisting of the text characterÕs color is in the center of -icon.

    +a small square consisting of the text characterÕs color is in the center of icon. +

     

    @@ -2354,10 +2367,11 @@ one group.  There is a group for each tab and a group for each window. To select an annotation with the mouse, the user simply clicks the mouse over an annotation.  The -annotation under the mouse is selected and any other annotations are deselected. - To select multiple annotations the user clicks the annotation with the -mouse while the Shift key is held down.  To deselect all annotations, the -user clicks the mouse in an empty region (not over an annotation).

    +annotation under the mouse is selected and any other annotations are +deselected.  To select multiple annotations the user clicks the annotation +with the mouse while the Shift key is held down.  To deselect all +annotations, the user clicks the mouse in an empty region (not over an +annotation).

     

    @@ -2439,8 +2453,8 @@ id="Picture 3" src="Annotations_1.3.fld/image032.png">

    -

    Figure 30: Overlay and Map Settings -Color Bar Controls

    +

    Figure 30: Overlay and Map +Settings Color Bar Controls

     

    @@ -2530,8 +2544,8 @@ background:transparent'>Glasser MF, Coalson TS, Robinson EC, et al. A multi-modal parcellation of human cerebral cortex. Nature. 2016;536(7615):171-178. -doi:10.1038/nature18933.

    +font-family:Arial;color:#303030;background:transparent'>. +2016;536(7615):171-178. doi:10.1038/nature18933.

     

    diff --git a/src/Resources/Help/HelpFiles/Menus/View_Menu/Tile_Tabs_Configuration/Tile_Tabs_Configuration.html b/src/Resources/Help/HelpFiles/Menus/View_Menu/Tile_Tabs_Configuration/Tile_Tabs_Configuration.html index 7243cf78d..3084e3793 100644 --- a/src/Resources/Help/HelpFiles/Menus/View_Menu/Tile_Tabs_Configuration/Tile_Tabs_Configuration.html +++ b/src/Resources/Help/HelpFiles/Menus/View_Menu/Tile_Tabs_Configuration/Tile_Tabs_Configuration.html @@ -20,6 +20,8 @@ + + Tabs
    in the Viewing @@ -40,40 +42,194 @@ + + Area

    +

    Tile Tabs Configuration in Workbench Window

    This section contains the tile tabs - configuration for the selected Workbench Window.  When a - scene is saved with Tile Tabs enabled, the selected configuration - is saved to the scene and is restored when the scene is displayed.
    + configuration for the selected Workbench Window. 
    If a Scene is saved, the + selected Configuration is saved to the Scene and is restored + when the Scene is displayed.
    +
    +
    Workbench Window  + Selects the window for control of the window's Tile Tabs + Configuration
    +
    +

    Layout of Tabs in a Tile Tabs Configuration

    +  A Tile Tabs Configuration is a grid-type + layout that displays all Tabs in the Window.  Regardless of + the Configuration Type, the first tab will be in the top left + corner with subsequent tabs appearing to the right and wrapping to + the next row when a row is filled. 
    +

    +

    Configuration Types
    +

      -
    • Workbench Window - Selects the Window for adjusting the - Tile Tabs Configuration
      -
    • Automatic - Configuration - When selected, Workbench will adjust - the number of rows and columns so that all tabs are displayed.
    • + Configuration - This is the default selection and + Workbench will adjust the number of rows and columns so that + all tabs are displayed.  Use Custom when Automatic does not produce + the desired layout of tabs.
    • Custom Configuration - - Allows the user to set the number of rows/columns and their - stretch factors.
      + Allows the user to customize the configuration of the Tile + Tabs layout including number of rows, number of columns, + heights of the rows, and widths of the columns.  If the + Custom Configuration contains insufficient rows and columns + for the Window's Tabs, some tabs will not be displayed.  + Conversely, there will be empty space at the bottom when the + Tile Tabs Configuration contains space for more tabs than are + currently in the Window.
      +
    • +
    +

    Custom Configuration Rows/Columns
    +

    +

    +
      +
    • Index - The index of the row or + column.
      +
    • +
    • Content - The content of the row + or column chosen from Space (empty space for used for + Annotations in "Spacer Coordinate Space") or Tab (the Window's + Browser Tabs).
      +
    • +
    • Type - The type of 'stretching' + chosen from Percent and Weight.
    • -
    • Dimensions (Rows / Columns) - - Number of Rows and Columns in the Custom Configuration
    • -
    • Stretch Factors - The Stretch - Factors function as weights and are used to allocate space - assigned to rows and columns.  For example, a row with a - stretch factor of 2 is double the height of a row with a - stretch factor of 1.  The percentage values indicate the - height (width) that a row (column) occupies in the graphics - region.
      +
    • Stretch - The stretching + value.  When the Type is Percent this value is a + percentage and when the Type is weight it is the weight for + this row/column.
      +
    • +
    +

    Explanation of Stretching

    +
    + The space allocated to a row is determined by a combination of the + Stretching Type and the Stretching Value.  (Note that row and + column stretching function identically) as described below.
    +

    Percentage Stretching Type

    + When a row's Type is set to Percentage, that row is allocated the + associated Percentage value of the window's height.  Thus, if + the row's Percentage Height is 20% and the Window is 1000 pixels in + height, the row will be 200 pixels in height.  The Percentage + value for each row should be in the range 0% to 100%.  If ALL + rows use a Percentage height, wb_view does not verify that the sum + of the row percentages is 100%.  Therefore, if ALL rows use a + Percentage height and the sum of the Percentage is less than 100%, + there will be empty space at the bottom of the window and if greater + than 100%, rows at the bottom may not visible.
    +
    +
    Example of Percentage Stretching
    +
      +
    • Row 1: 20%
    • +
    • Row 2: 50%
    • +
    • Row 3: 30%
    • +
    +

    Result for a Window 1000 pixels in height:
    +

    +
      +
    • Row 1 Height: 200 pixels (20% of 1000)
    • +
    • Row 2 Height: 500 pixels (50% of 1000)
    • +
    • Row 3 Height: 300 pixels (30% of 1000)
    +


    +

    +

    Weighted Stretching Type

    + When a row's Type is set to Weight, the Height of the row is + affected by Stretching Values of all Rows with the Stretching Type + set to Weight.  To determine the height of the a row, the + weights from all rows are summed and the row's weight is divided by + the sum.  This result (row's weight divided by sum) becomes the + percentage of the window's height allocated to the row.
    +
    +
    Example of Weighted Stretching
    +
      +
    • Row 1: 1.0
    • +
    • Row 2: 2.0
    • +
    • Row 3: 1.0
    • +
    +

    Result for a Window 1000 pixels in height (Note: sum of weights + is 4.0):
    +

    +
      +
    • Row 1 Height: 250 pixels   ((1.0 / 4.0) * 1000 = + 250)
      +
    • +
    • Row 2 Height: 500 pixels   ((2.0 / 4.0) * 1000 = 500)
    • +
    • Row 3 Height: 250 pixels   ((1.0 / 4.0) * 1000 = + 250)
    • +
    +
    +

    Combination of Percentage and Weighted Stretching

    + When both Percentage and Weighted Stretching are used, rows with + Percentage stretching are assigned their requested height percentage + and any remaining space is allocated to rows with weighted + Stretching.
    +
    +
    Example of Percentage and Weighted Stretching
    +
      +
    • Row 1: Percentage, 20%
    • +
    • Row 2: Percentage, 30%
    • +
    • Row 3: Weighted, 1.0
    • +
    • Row 4: Weighted, 2.0
    • +
    +

    Result for a Window 1000 pixels in height (Note: Sum of + Percentages is 50% and Sum of Weights is 3.0):
    +

    +
      +
    • Row 1 Height: 200 pixels (20% of 1000)
    • +
    • Row 2 Height: 300 pixels (30% of 1000)
    • +
    • Row 3 Height: 166 pixels ((1.0 / 3.0) * 50% * 1000)
    • +
    • Row 4 Height: 334 pixels ((2.0 / 3.0) * 50% * 1000)
    • +
    + Now Suppose a 5th row is added with a Weight of 1.0 (the Sum of + Weights is 4.0).  Notice that the Percentage Type rows remain + the same height and the that the Weighted Type rows shrink in height + to accommodate the new row:
    +
      +
    • Row 1 Height: 200 pixels (20% of 1000)
    • +
    • Row 2 Height: 300 pixels (30% of 1000)
    • +
    • Row 3 Height: 125 pixels ((1.0 / 4.0) * 50% * 1000)
    • +
    • Row 4 Height: 250 pixels ((2.0 / 4.0) * 50% * 1000)
    • +
    • Row 5 Height: 125 pixels ((1.0 / 4.0) * 50% * 1000)
      +
    • +
    +

    Summary of Percentage and Weighted Stretching

    +

    The advantage of using Percentage stretching is that the row will + be allocated the requested percentage of the window's + height.  The disadvantage of Percentage Stretching is that if + all rows use Percentage Stretching, and a row is added or removed, + the user will need to adjust the stretching percentages to ensure + all rows are visible (when a row is added) or to remove empty + space (when a row is removed).
    +

    +

    The advantage of Weighted stretching is that all of the vertical + space will be used and the available space is automatically + reallocated when a row is added or removed.  The disadvantage + is that if rows use different weights, calculations are required + to get the desired row heights.
    +

    +

    In some instances using both Percentage and Weighted Stretching + may be best.   One such instance is when the first row's + Content is set to Spacer and Annotation are added to the Spacer + Row for use as Column Titles.  In this case, the + recommendation is to use Percentage for this Row and Weight for + the Rows below containing the Brain Models.  As Rows for + Brain Models are added as removed, the Column Titles will remain + the same size and the Rows containing the Brain Models will occupy + all of the remaining vertical space.
    +

    User Configuration

    The User Configuration contains Tile Tabs Configurations that have been created by the user.  These @@ -88,7 +244,7 @@
  • Delete - Click this button to delete the selected User Configuration
  • -

    Copy and Load Push Buttons

    +

    Replace and Load Push Buttons

    Custom Configuration Rows/Columns
    @@ -99,9 +105,11 @@ column.
  • Content - The content of the row - or column chosen from Space (empty space for used for - Annotations in "Spacer Coordinate Space") or Tab (the Window's - Browser Tabs).
    + or column chosen from Space or Tab.  The content of + 'Space' is limited to annotations in "Spacer Coordinate Space" + or "Window Space" Annotations.  The content of 'Tab' is + limited to display of a browser tab along with annotations in + spaces other than "Spacer Coordinate Space."
  • Type - The type of 'stretching' chosen from Percent and Weight.
    @@ -116,15 +124,16 @@
    The space allocated to a row is determined by a combination of the Stretching Type and the Stretching Value.  (Note that row and - column stretching function identically) as described below.
    + column stretching function identically but along different axes) as + described below.

    Percentage Stretching Type

    When a row's Type is set to Percentage, that row is allocated the associated Percentage value of the window's height.  Thus, if the row's Percentage Height is 20% and the Window is 1000 pixels in - height, the row will be 200 pixels in height.  The Percentage - value for each row should be in the range 0% to 100%.  If ALL - rows use a Percentage height, wb_view does not verify that the sum - of the row percentages is 100%.  Therefore, if ALL rows use a + height, the row is 200 pixels in height.  The Percentage value + for each row should be in the range 0% to 100%.  If ALL rows + use a Percentage height, wb_view does not verify that the sum of the + row percentages is 100%.  Therefore, if ALL rows use a Percentage height and the sum of the Percentage is less than 100%, there will be empty space at the bottom of the window and if greater than 100%, rows at the bottom may not visible.
    @@ -195,8 +204,8 @@ Now Suppose a 5th row is added with a Weight of 1.0 (the Sum of Weights is 4.0).  Notice that the Percentage Type rows remain - the same height and the that the Weighted Type rows shrink in height - to accommodate the new row:
    + the same height and the Weighted Type rows shrink in height to + accommodate the new row:
    • Row 1 Height: 200 pixels (20% of 1000)
    • Row 2 Height: 300 pixels (30% of 1000)
    • @@ -246,11 +255,13 @@

    Replace and Load Push Buttons

      -
    • Replace - Replaces the selected - User Configuration with the content of the Custom +
    • Replace - Replaces (saves) the + selected User Configuration with the content of the Custom Configuration
    • Load - Copies the selected User - Configuration into the Custom Configuration
    • + Configuration into the Custom Configuration and is used for + the layout of the window
      +


    -- GitLab From fb87a0e47819449620a6ff1ec3ba463a5ea9a939 Mon Sep 17 00:00:00 2001 From: John Harwell Date: Wed, 7 Nov 2018 15:40:43 -0600 Subject: [PATCH 106/427] Fixed crash in Tile Tabs Configuration dialog when exiting wb_view. The dialog tried to update itself and needed a browser tab that was no longer available. While I was not able, to repeat this crash, some additional logic was added to prevent the crash using the assertion failure call stack. --- src/GuiQt/TileTabsConfigurationDialog.cxx | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/GuiQt/TileTabsConfigurationDialog.cxx b/src/GuiQt/TileTabsConfigurationDialog.cxx index 11cddb7ea..4003fc3d9 100644 --- a/src/GuiQt/TileTabsConfigurationDialog.cxx +++ b/src/GuiQt/TileTabsConfigurationDialog.cxx @@ -260,8 +260,10 @@ BrainBrowserWindow* TileTabsConfigurationDialog::getBrowserWindow() { m_browserWindowComboBox->updateComboBox(); + /* + * This can be NULL when wb_view is closing. + */ BrainBrowserWindow* bbw = m_browserWindowComboBox->getSelectedBrowserWindow(); - CaretAssert(bbw); return bbw; } @@ -274,8 +276,11 @@ TileTabsConfigurationDialog::getBrowserWindow() BrowserWindowContent* TileTabsConfigurationDialog::getBrowserWindowContent() { + BrowserWindowContent* bwc(NULL); BrainBrowserWindow* bbw = getBrowserWindow(); - BrowserWindowContent* bwc = bbw->getBrowerWindowContent(); + if (bbw != NULL) { + bwc = bbw->getBrowerWindowContent(); + } return bwc; } -- GitLab From 460f71a65945dc954ffe9d5fd166d23eff69b846 Mon Sep 17 00:00:00 2001 From: John Harwell Date: Thu, 8 Nov 2018 09:45:45 -0600 Subject: [PATCH 107/427] Additional updates to the Tile Tabs Configuration dialog documentation. --- .../Tile_Tabs_Configuration/Tile_Tabs_Configuration.html | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/Resources/Help/HelpFiles/Menus/View_Menu/Tile_Tabs_Configuration/Tile_Tabs_Configuration.html b/src/Resources/Help/HelpFiles/Menus/View_Menu/Tile_Tabs_Configuration/Tile_Tabs_Configuration.html index d16db91ad..8c70a2c11 100644 --- a/src/Resources/Help/HelpFiles/Menus/View_Menu/Tile_Tabs_Configuration/Tile_Tabs_Configuration.html +++ b/src/Resources/Help/HelpFiles/Menus/View_Menu/Tile_Tabs_Configuration/Tile_Tabs_Configuration.html @@ -23,6 +23,7 @@ + Tabs
    in the Viewing @@ -46,6 +47,7 @@ + Area
    @@ -63,6 +65,7 @@
    Workbench Window  + Selects the window for control of the window's Tile Tabs Configuration

    @@ -102,7 +105,10 @@

    • Index - The index of the row or - column.
      + column.
    • +
    • Construction Menu - Allows + the user to duplicate, move, or delete rows and columns.  +
    • Content - The content of the row or column chosen from Space or Tab.  The content of -- GitLab From f393311842f96ea104323bf19147e97751eba50b Mon Sep 17 00:00:00 2001 From: John Harwell Date: Thu, 8 Nov 2018 12:00:38 -0600 Subject: [PATCH 108/427] Fixed compilation warnings and a windows compilation error. --- src/Brain/BrowserTabContent.cxx | 8 -------- src/Brain/SessionManager.cxx | 1 + src/Common/EventTileTabsConfigurationModification.h | 2 +- src/Files/AnnotationFile.cxx | 4 ++-- src/GuiQt/TileTabsConfigurationDialog.cxx | 4 ++-- src/GuiQt/TileTabsConfigurationModifier.cxx | 5 ++--- 6 files changed, 8 insertions(+), 16 deletions(-) diff --git a/src/Brain/BrowserTabContent.cxx b/src/Brain/BrowserTabContent.cxx index 32e93fe81..aa7a56953 100644 --- a/src/Brain/BrowserTabContent.cxx +++ b/src/Brain/BrowserTabContent.cxx @@ -58,7 +58,6 @@ #include "EventIdentificationHighlightLocation.h" #include "EventModelGetAll.h" #include "EventManager.h" -#include "EventSpacerTabGet.h" #include "FociFile.h" #include "IdentificationManager.h" #include "LabelFile.h" @@ -230,9 +229,6 @@ BrowserTabContent::BrowserTabContent(const int32_t tabNumber) EventTypeEnum::EVENT_IDENTIFICATION_HIGHLIGHT_LOCATION); EventManager::get()->addEventListener(this, EventTypeEnum::EVENT_CARET_MAPPABLE_DATA_FILE_MAPS_VIEWED_IN_OVERLAYS); - EventManager::get()->addEventListener(this, - EventTypeEnum::EVENT_SPACER_TAB_GET); - isExecutingConstructor = false; @@ -1403,10 +1399,6 @@ BrowserTabContent::receiveEvent(Event* event) } } } - else if (event->getEventType() == EventTypeEnum::EVENT_SPACER_TAB_GET) { - EventSpacerTabGet* tabEvent = dynamic_cast(event); - CaretAssert(tabEvent); - } } /** diff --git a/src/Brain/SessionManager.cxx b/src/Brain/SessionManager.cxx index bd5567fdb..78b81eec0 100644 --- a/src/Brain/SessionManager.cxx +++ b/src/Brain/SessionManager.cxx @@ -524,6 +524,7 @@ SessionManager::receiveEvent(Event* event) CaretAssert(spacerTabContent); spacerTabEvent->setSpacerTabContent(spacerTabContent); + spacerTabEvent->setEventProcessed(); } } diff --git a/src/Common/EventTileTabsConfigurationModification.h b/src/Common/EventTileTabsConfigurationModification.h index 2d54d9e05..cf75b3044 100644 --- a/src/Common/EventTileTabsConfigurationModification.h +++ b/src/Common/EventTileTabsConfigurationModification.h @@ -48,7 +48,7 @@ namespace caret { * Operation */ enum class Operation { - DELETE, + DELETE_IT, /* DELETE is reserved word on Windows */ DUPLICATE_AFTER, DUPLICATE_BEFORE, MOVE_AFTER, diff --git a/src/Files/AnnotationFile.cxx b/src/Files/AnnotationFile.cxx index 92ccbcd61..f13005d41 100644 --- a/src/Files/AnnotationFile.cxx +++ b/src/Files/AnnotationFile.cxx @@ -2175,7 +2175,7 @@ AnnotationFile::updateSpacerAnnotationsAfterTileTabsModification(const EventTile int32_t moveTwoIndex(-1); switch (modEvent->getOperation()) { - case EventTileTabsConfigurationModification::Operation::DELETE: + case EventTileTabsConfigurationModification::Operation::DELETE_IT: deleteIndex = rowColumnIndex; shiftStartIndex = rowColumnIndex + 1; break; @@ -2225,7 +2225,7 @@ AnnotationFile::updateSpacerAnnotationsAfterTileTabsModification(const EventTile int32_t columnIndex = spacerTabIndex.getColumnIndex(); switch (modEvent->getOperation()) { - case EventTileTabsConfigurationModification::Operation::DELETE: + case EventTileTabsConfigurationModification::Operation::DELETE_IT: { int32_t rcIndex = (rowFlag ? rowIndex : columnIndex); if (rcIndex == deleteIndex) { diff --git a/src/GuiQt/TileTabsConfigurationDialog.cxx b/src/GuiQt/TileTabsConfigurationDialog.cxx index 4003fc3d9..f10d4024a 100644 --- a/src/GuiQt/TileTabsConfigurationDialog.cxx +++ b/src/GuiQt/TileTabsConfigurationDialog.cxx @@ -1164,7 +1164,7 @@ TileTabElementWidgets::createConstructionMenu(QToolButton* toolButton) : "Move this Row Up"); m_menuDeleteAction = new QAction(deleteText); - m_menuDeleteAction->setData(static_cast(EventTileTabsConfigurationModification::Operation::DELETE)); + m_menuDeleteAction->setData(static_cast(EventTileTabsConfigurationModification::Operation::DELETE_IT)); m_menuDuplicateAfterAction = new QAction(duplicateAfterText); m_menuDuplicateAfterAction->setData(static_cast(EventTileTabsConfigurationModification::Operation::DUPLICATE_AFTER)); @@ -1240,7 +1240,7 @@ TileTabElementWidgets::constructionMenuTriggered(QAction* action) * if the operations are changed. */ switch (operation) { - case EventTileTabsConfigurationModification::Operation::DELETE: + case EventTileTabsConfigurationModification::Operation::DELETE_IT: break; case EventTileTabsConfigurationModification::Operation::DUPLICATE_AFTER: break; diff --git a/src/GuiQt/TileTabsConfigurationModifier.cxx b/src/GuiQt/TileTabsConfigurationModifier.cxx index 995458df0..d631b61de 100644 --- a/src/GuiQt/TileTabsConfigurationModifier.cxx +++ b/src/GuiQt/TileTabsConfigurationModifier.cxx @@ -175,7 +175,7 @@ TileTabsConfigurationModifier::performModification(AString& errorMessageOut) const int32_t numRowColumns = static_cast(m_rowColumns.size()); switch (m_modifyEvent->getOperation()) { - case EventTileTabsConfigurationModification::Operation::DELETE: + case EventTileTabsConfigurationModification::Operation::DELETE_IT: if (numRowColumns <= 1) { errorMessageOut = "Cannot delete ROWCOL when there is only one ROWCOL"; } @@ -480,12 +480,10 @@ TileTabsConfigurationModifier::RowColumnContent::RowColumnContent(const std::vec else { for (int32_t iRow = 0; iRow < numRows; iRow++) { BrowserTabContent* browserTabContent(NULL); - SpacerTabContent* spacerTabContent(NULL); const int32_t tabIndex = (iRow * numColumns) + rowColumnIndex; if (tabIndex < numberOfTabs) { CaretAssertVectorIndex(existingTabs, tabIndex); browserTabContent = existingTabs[tabIndex]->getBrowserTabContent(); - spacerTabContent = existingTabs[tabIndex]->getSpacerTabContent(); } m_tabElements.push_back(new Element(iRow, @@ -518,6 +516,7 @@ TileTabsConfigurationModifier::RowColumnContent::~RowColumnContent() * Instance that is copied. */ TileTabsConfigurationModifier::RowColumnContent::RowColumnContent(const RowColumnContent& obj) +: CaretObject(obj) { for (auto te : obj.m_tabElements) { m_tabElements.push_back(new Element(*te)); -- GitLab From 472338ce10a71e9679ebeff17bd045e0300366b1 Mon Sep 17 00:00:00 2001 From: John Harwell Date: Mon, 12 Nov 2018 12:21:24 -0600 Subject: [PATCH 109/427] Resolve WB-432 Some Asymmetric Dense Connectomes are Not Listed: Code for loading data was incorrectly using the "Volume Map Information" from the 'mapping' row/column. This code has been corrected to use the "Volume Map Information" from the 'loading' row/column when loading data for a brainordinate. --- ...iftiMappableConnectivityMatrixDataFile.cxx | 4 +- src/Files/CiftiMappableDataFile.cxx | 271 +++++++++++++++--- src/Files/CiftiMappableDataFile.h | 24 +- 3 files changed, 260 insertions(+), 39 deletions(-) diff --git a/src/Files/CiftiMappableConnectivityMatrixDataFile.cxx b/src/Files/CiftiMappableConnectivityMatrixDataFile.cxx index fee8cbc5f..3cec18021 100644 --- a/src/Files/CiftiMappableConnectivityMatrixDataFile.cxx +++ b/src/Files/CiftiMappableConnectivityMatrixDataFile.cxx @@ -462,7 +462,7 @@ CiftiMappableConnectivityMatrixDataFile::getRowColumnIndexForVoxelAtCoordinateWh } int64_t ijk[3]; - enclosingVoxel(xyz[0], xyz[1], xyz[2], ijk[0], ijk[1], ijk[2]); + enclosingVoxelForDataLoading(xyz[0], xyz[1], xyz[2], ijk[0], ijk[1], ijk[2]); return getRowColumnIndexForVoxelIndexWhenLoading(ijk, rowIndexOut, columnIndexOut); @@ -504,7 +504,7 @@ CiftiMappableConnectivityMatrixDataFile::getRowColumnIndexForVoxelIndexWhenLoadi /* * Get the mapping type */ - if (indexValid(ijk[0], ijk[1], ijk[2])) { + if (indexValidForDataLoading(ijk[0], ijk[1], ijk[2])) { switch (rowMappingType) { case CIFTI_INDEX_TYPE_BRAIN_MODELS: rowOrColumnIndex = ciftiXML.getBrainModelsMap(ciftiDirection).getIndexForVoxel(ijk); diff --git a/src/Files/CiftiMappableDataFile.cxx b/src/Files/CiftiMappableDataFile.cxx index 1bb252a5d..2f8193f2a 100644 --- a/src/Files/CiftiMappableDataFile.cxx +++ b/src/Files/CiftiMappableDataFile.cxx @@ -81,7 +81,8 @@ CiftiMappableDataFile::CiftiMappableDataFile(const DataFileTypeEnum::Enum dataFi : CaretMappableDataFile(dataFileType) { m_ciftiFile.grabNew(NULL); - m_voxelIndicesToOffset.grabNew(NULL); + m_voxelIndicesToOffsetForDataReading.grabNew(NULL); + m_voxelIndicesToOffsetForDataMapping.grabNew(NULL); m_classNameHierarchy.grabNew(NULL); m_fileDataReadingType = FILE_READ_DATA_ALL; @@ -970,10 +971,10 @@ CiftiMappableDataFile::initializeAfterReading(const AString& filename) break; case DATA_ACCESS_FILE_ROWS_OR_XML_ALONG_COLUMN: if (ciftiXML.getMappingType(CiftiXML::ALONG_COLUMN) == CiftiMappingType::BRAIN_MODELS) { - m_voxelIndicesToOffset.grabNew(new SparseVolumeIndexer(ciftiXML.getBrainModelsMap(CiftiXML::ALONG_COLUMN))); + m_voxelIndicesToOffsetForDataMapping.grabNew(new SparseVolumeIndexer(ciftiXML.getBrainModelsMap(CiftiXML::ALONG_COLUMN))); } else if (ciftiXML.getMappingType(CiftiXML::ALONG_COLUMN) == CiftiMappingType::PARCELS) { - m_voxelIndicesToOffset.grabNew(new SparseVolumeIndexer(ciftiXML.getParcelsMap(CiftiXML::ALONG_COLUMN))); + m_voxelIndicesToOffsetForDataMapping.grabNew(new SparseVolumeIndexer(ciftiXML.getParcelsMap(CiftiXML::ALONG_COLUMN))); } else { CaretAssertMessage(0, "Invalid mapping type for mapping data to brainordinates"); @@ -983,10 +984,10 @@ CiftiMappableDataFile::initializeAfterReading(const AString& filename) break; case DATA_ACCESS_FILE_COLUMNS_OR_XML_ALONG_ROW: if (ciftiXML.getMappingType(CiftiXML::ALONG_ROW) == CiftiMappingType::BRAIN_MODELS) { - m_voxelIndicesToOffset.grabNew(new SparseVolumeIndexer(ciftiXML.getBrainModelsMap(CiftiXML::ALONG_ROW))); + m_voxelIndicesToOffsetForDataMapping.grabNew(new SparseVolumeIndexer(ciftiXML.getBrainModelsMap(CiftiXML::ALONG_ROW))); } else if (ciftiXML.getMappingType(CiftiXML::ALONG_ROW) == CiftiMappingType::PARCELS) { - m_voxelIndicesToOffset.grabNew(new SparseVolumeIndexer(ciftiXML.getParcelsMap(CiftiXML::ALONG_ROW))); + m_voxelIndicesToOffsetForDataMapping.grabNew(new SparseVolumeIndexer(ciftiXML.getParcelsMap(CiftiXML::ALONG_ROW))); } else { CaretAssertMessage(0, "Invalid mapping type for mapping data to brainordinates"); @@ -996,6 +997,38 @@ CiftiMappableDataFile::initializeAfterReading(const AString& filename) break; } + switch (m_dataReadingAccessMethod) { + case DATA_ACCESS_METHOD_INVALID: + CaretAssert(0); + break; + case DATA_ACCESS_NONE: + break; + case DATA_ACCESS_FILE_ROWS_OR_XML_ALONG_COLUMN: + if (ciftiXML.getMappingType(CiftiXML::ALONG_COLUMN) == CiftiMappingType::BRAIN_MODELS) { + m_voxelIndicesToOffsetForDataReading.grabNew(new SparseVolumeIndexer(ciftiXML.getBrainModelsMap(CiftiXML::ALONG_COLUMN))); + } + else if (ciftiXML.getMappingType(CiftiXML::ALONG_COLUMN) == CiftiMappingType::PARCELS) { + m_voxelIndicesToOffsetForDataReading.grabNew(new SparseVolumeIndexer(ciftiXML.getParcelsMap(CiftiXML::ALONG_COLUMN))); + } + else { + CaretAssertMessage(0, "Invalid mapping type for mapping data to brainordinates"); + throw DataFileException(filename, + "Invalid mapping type for mapping data to brainordinates"); + } + break; + case DATA_ACCESS_FILE_COLUMNS_OR_XML_ALONG_ROW: + if (ciftiXML.getMappingType(CiftiXML::ALONG_ROW) == CiftiMappingType::BRAIN_MODELS) { + m_voxelIndicesToOffsetForDataReading.grabNew(new SparseVolumeIndexer(ciftiXML.getBrainModelsMap(CiftiXML::ALONG_ROW))); + } + else if (ciftiXML.getMappingType(CiftiXML::ALONG_ROW) == CiftiMappingType::PARCELS) { + m_voxelIndicesToOffsetForDataReading.grabNew(new SparseVolumeIndexer(ciftiXML.getParcelsMap(CiftiXML::ALONG_ROW))); + } + else { + m_voxelIndicesToOffsetForDataReading.grabNew(new SparseVolumeIndexer()); + } + break; + } + /* * Special case for scalar data series that does NOT map to brainordinates */ @@ -1027,8 +1060,11 @@ CiftiMappableDataFile::initializeAfterReading(const AString& filename) /* * May not have mappings to voxels */ - if (m_voxelIndicesToOffset == NULL) { - m_voxelIndicesToOffset.grabNew(new SparseVolumeIndexer()); + if (m_voxelIndicesToOffsetForDataMapping == NULL) { + m_voxelIndicesToOffsetForDataMapping.grabNew(new SparseVolumeIndexer()); + } + if (m_voxelIndicesToOffsetForDataReading == NULL) { + m_voxelIndicesToOffsetForDataReading.grabNew(new SparseVolumeIndexer()); } int32_t numberOfMaps = 0; @@ -2682,6 +2718,96 @@ CiftiMappableDataFile::getDimensions(std::vector& dimsOut) const dimsOut[4] = dimComp; } +/** + * Get the dimensions of the volume for data loading + * + * @param dimOut1 + * First dimension (i) out. + * @param dimOut2 + * Second dimension (j) out. + * @param dimOut3 + * Third dimension (k) out. + * @param dimTimeOut + * Time dimensions out (number of maps) + * @param numComponentsOut + * Number of components per voxel. + */ +void +CiftiMappableDataFile::getDimensionsForDataLoading(int64_t& dimOut1, + int64_t& dimOut2, + int64_t& dimOut3, + int64_t& dimTimeOut, + int64_t& numComponentsOut) const +{ + CaretAssert(m_ciftiFile); + + dimOut1 = 0; + dimOut2 = 0; + dimOut3 = 0; + dimTimeOut = 0; + numComponentsOut = 0; + + if (m_dataReadingDirectionForCiftiXML != S_CIFTI_XML_ALONG_INVALID) { + switch (m_ciftiFile->getCiftiXML().getMappingType(m_dataReadingDirectionForCiftiXML)) + { + case CiftiMappingType::BRAIN_MODELS: + { + const CiftiBrainModelsMap& myDenseMap = m_ciftiFile->getCiftiXML().getBrainModelsMap(m_dataReadingDirectionForCiftiXML); + if (!myDenseMap.hasVolumeData()) return; + const VolumeSpace& mySpace = myDenseMap.getVolumeSpace(); + const int64_t* dims = mySpace.getDims(); + dimOut1 = dims[0]; + dimOut2 = dims[1]; + dimOut3 = dims[2]; + dimTimeOut = 1;//??? + numComponentsOut = 1; + break; + } + case CiftiMappingType::PARCELS: + { + const CiftiParcelsMap& myParcelMap = m_ciftiFile->getCiftiXML().getParcelsMap(m_dataReadingDirectionForCiftiXML); + if (!myParcelMap.hasVolumeData()) return; + const VolumeSpace& mySpace = myParcelMap.getVolumeSpace(); + const int64_t* dims = mySpace.getDims(); + dimOut1 = dims[0]; + dimOut2 = dims[1]; + dimOut3 = dims[2]; + dimTimeOut = 1;//??? + numComponentsOut = 1; + break; + } + default://nothing else has volume dimensions + break; + } + } +} + +/** + * Get the dimensions of the volume. + * + * @param dimsOut + * Will contain 5 elements: (0) X-dimension, (1) Y-dimension + * (2) Z-dimension, (3) time, (4) components. + */ +void +CiftiMappableDataFile::getDimensionsForDataLoading(std::vector& dimsOut) const +{ + dimsOut.resize(5); + + int64_t dimI, dimJ, dimK, dimTime, dimComp; + getDimensionsForDataLoading(dimI, + dimJ, + dimK, + dimTime, + dimComp); + + dimsOut[0] = dimI; + dimsOut[1] = dimJ; + dimsOut[2] = dimK; + dimsOut[3] = dimTime; + dimsOut[4] = dimComp; +} + /** * @return The number of componenents per voxel in the volume data. */ @@ -2723,8 +2849,8 @@ CiftiMappableDataFile::indexToSpace(const float& indexIn1, float& coordOut2, float& coordOut3) const { - CaretAssert(m_voxelIndicesToOffset); - m_voxelIndicesToOffset->indicesToCoordinate(indexIn1, + CaretAssert(m_voxelIndicesToOffsetForDataMapping); + m_voxelIndicesToOffsetForDataMapping->indicesToCoordinate(indexIn1, indexIn2, indexIn3, coordOut1, @@ -2750,8 +2876,8 @@ CiftiMappableDataFile::indexToSpace(const float& indexIn1, const float& indexIn3, float* coordOut) const { - CaretAssert(m_voxelIndicesToOffset); - m_voxelIndicesToOffset->indicesToCoordinate(indexIn1, + CaretAssert(m_voxelIndicesToOffsetForDataMapping); + m_voxelIndicesToOffsetForDataMapping->indicesToCoordinate(indexIn1, indexIn2, indexIn3, coordOut[0], @@ -2771,8 +2897,8 @@ void CiftiMappableDataFile::indexToSpace(const int64_t* indexIn, float* coordOut) const { - CaretAssert(m_voxelIndicesToOffset); - m_voxelIndicesToOffset->indicesToCoordinate(indexIn[0], + CaretAssert(m_voxelIndicesToOffsetForDataMapping); + m_voxelIndicesToOffsetForDataMapping->indicesToCoordinate(indexIn[0], indexIn[1], indexIn[2], coordOut[0], @@ -2805,8 +2931,8 @@ CiftiMappableDataFile::enclosingVoxel(const float& coordIn1, int64_t& indexOut2, int64_t& indexOut3) const { - CaretAssert(m_voxelIndicesToOffset); - m_voxelIndicesToOffset->coordinateToIndices(coordIn1, + CaretAssert(m_voxelIndicesToOffsetForDataMapping); + m_voxelIndicesToOffsetForDataMapping->coordinateToIndices(coordIn1, coordIn2, coordIn3, indexOut1, @@ -2814,6 +2940,41 @@ CiftiMappableDataFile::enclosingVoxel(const float& coordIn1, indexOut3); } +/** + * Use the method when LOADING data + * Convert a coordinate to indices. Note that output indices + * MAY NOT BE WITHIN THE VALID VOXEL DIMENSIONS. + * + * @param coordIn1 + * First (x) input coordinate. + * @param coordIn2 + * Second (y) input coordinate. + * @param coordIn3 + * Third (z) input coordinate. + * @param indexOut1 + * First output index (i). + * @param indexOut2 + * First output index (j). + * @param indexOut3 + * First output index (k). + */ +void +CiftiMappableDataFile::enclosingVoxelForDataLoading(const float& coordIn1, + const float& coordIn2, + const float& coordIn3, + int64_t& indexOut1, + int64_t& indexOut2, + int64_t& indexOut3) const +{ + CaretAssert(m_voxelIndicesToOffsetForDataReading); + m_voxelIndicesToOffsetForDataReading->coordinateToIndices(coordIn1, + coordIn2, + coordIn3, + indexOut1, + indexOut2, + indexOut3); +} + /** * Determine in the given voxel indices are valid (within the volume dimensions). * @@ -2852,10 +3013,48 @@ CiftiMappableDataFile::indexValid(const int64_t& indexIn1, return false; } +/** + * Determine in the given voxel indices are valid (within the volume dimensions). + * + * @param indexIn1 + * First dimension (i). + * @param indexIn2 + * Second dimension (j). + * @param indexIn3 + * Third dimension (k). + * @param coordOut1 + * Output first (x) coordinate. + * @param brickIndex + * Time/map index (default 0). + * @param component + * Voxel component (default 0). + */ +bool +CiftiMappableDataFile::indexValidForDataLoading(const int64_t& indexIn1, + const int64_t& indexIn2, + const int64_t& indexIn3, + const int64_t /*brickIndex*/, + const int64_t /*component*/) const +{ + std::vector volumeDimensions; + getDimensionsForDataLoading(volumeDimensions); + CaretAssertVectorIndex(volumeDimensions, 2); + if ((indexIn1 >= 0) + && (indexIn1 < volumeDimensions[0]) + && (indexIn2 >= 0) + && (indexIn2 < volumeDimensions[1]) + && (indexIn3 >= 0) + && (indexIn3 < volumeDimensions[2])) { + return true; + } + + return false; +} + const VolumeSpace& CiftiMappableDataFile::getVolumeSpace() const { - CaretAssert(m_voxelIndicesToOffset);//because this is where the other space functions get their volume space from, just roll with it for now - return m_voxelIndicesToOffset->getVolumeSpace(); + CaretAssert(m_voxelIndicesToOffsetForDataMapping);//because this is where the other space functions get their volume space from, just roll with it for now + return m_voxelIndicesToOffsetForDataMapping->getVolumeSpace(); } /** @@ -2867,7 +3066,7 @@ const VolumeSpace& CiftiMappableDataFile::getVolumeSpace() const void CiftiMappableDataFile::getVoxelSpaceBoundingBox(BoundingBox& boundingBoxOut) const { - CaretAssert(m_voxelIndicesToOffset); + CaretAssert(m_voxelIndicesToOffsetForDataMapping); boundingBoxOut.resetForUpdate(); @@ -2875,7 +3074,7 @@ CiftiMappableDataFile::getVoxelSpaceBoundingBox(BoundingBox& boundingBoxOut) con getDimensions(volumeDimensions); CaretAssertVectorIndex(volumeDimensions, 2); - if (m_voxelIndicesToOffset->isValid()) { + if (m_voxelIndicesToOffsetForDataMapping->isValid()) { float xyz[3]; indexToSpace(0, 0, @@ -2992,7 +3191,7 @@ CiftiMappableDataFile::getVoxelColorsForSliceInMap(const int32_t mapIndex, const uint8_t* mapRGBA = &m_mapContent[mapIndex]->m_rgba[0]; - CaretAssert(m_voxelIndicesToOffset); + CaretAssert(m_voxelIndicesToOffsetForDataMapping); /* * Data values are only needed when a label volume @@ -3021,7 +3220,7 @@ CiftiMappableDataFile::getVoxelColorsForSliceInMap(const int32_t mapIndex, case VolumeSliceViewPlaneEnum::AXIAL: for (int64_t j = 0; j < dimJ; j++) { for (int64_t i = 0; i < dimI; i++) { - const int64_t dataOffset = m_voxelIndicesToOffset->getOffsetForIndices(i, + const int64_t dataOffset = m_voxelIndicesToOffsetForDataMapping->getOffsetForIndices(i, j, sliceIndex); if (dataOffset >= 0) { @@ -3075,7 +3274,7 @@ CiftiMappableDataFile::getVoxelColorsForSliceInMap(const int32_t mapIndex, case VolumeSliceViewPlaneEnum::CORONAL: for (int64_t k = 0; k < dimK; k++) { for (int64_t i = 0; i < dimI; i++) { - const int64_t dataOffset = m_voxelIndicesToOffset->getOffsetForIndices(i, + const int64_t dataOffset = m_voxelIndicesToOffsetForDataMapping->getOffsetForIndices(i, sliceIndex, k); if (dataOffset >= 0) { @@ -3129,7 +3328,7 @@ CiftiMappableDataFile::getVoxelColorsForSliceInMap(const int32_t mapIndex, case VolumeSliceViewPlaneEnum::PARASAGITTAL: for (int64_t k = 0; k < dimK; k++) { for (int64_t j = 0; j < dimJ; j++) { - const int64_t dataOffset = m_voxelIndicesToOffset->getOffsetForIndices(sliceIndex, + const int64_t dataOffset = m_voxelIndicesToOffsetForDataMapping->getOffsetForIndices(sliceIndex, j, k); if (dataOffset >= 0) { @@ -3241,7 +3440,7 @@ CiftiMappableDataFile::getVoxelColorsForSliceInMap(const int32_t mapIndex, const uint8_t* mapRGBA = &m_mapContent[mapIndex]->m_rgba[0]; - CaretAssert(m_voxelIndicesToOffset); + CaretAssert(m_voxelIndicesToOffsetForDataMapping); /* * Data values are only needed when a label volume @@ -3270,7 +3469,7 @@ CiftiMappableDataFile::getVoxelColorsForSliceInMap(const int32_t mapIndex, for (int32_t iCol = 0; iCol < numberOfColumns; iCol++) { rgba[3] = 0; - const int64_t dataOffset = m_voxelIndicesToOffset->getOffsetForIndices(ijk[0], ijk[1], ijk[2]); + const int64_t dataOffset = m_voxelIndicesToOffsetForDataMapping->getOffsetForIndices(ijk[0], ijk[1], ijk[2]); if (dataOffset >= 0) { const int64_t dataOffset4 = dataOffset * 4; CaretAssert(dataOffset4 < mapRgbaCount); @@ -3457,7 +3656,7 @@ CiftiMappableDataFile::getVoxelColorsForSubSliceInMap(const int32_t mapIndex, const uint8_t* mapRGBA = &m_mapContent[mapIndex]->m_rgba[0]; - CaretAssert(m_voxelIndicesToOffset); + CaretAssert(m_voxelIndicesToOffsetForDataMapping); /* * Data values are only needed when a label volume @@ -3501,7 +3700,7 @@ CiftiMappableDataFile::getVoxelColorsForSubSliceInMap(const int32_t mapIndex, int64_t i = iStart; bool iLoopFlag = true; while (iLoopFlag) { - const int64_t dataOffset = m_voxelIndicesToOffset->getOffsetForIndices(i, + const int64_t dataOffset = m_voxelIndicesToOffsetForDataMapping->getOffsetForIndices(i, j, sliceIndex); if (dataOffset >= 0) { @@ -3581,7 +3780,7 @@ CiftiMappableDataFile::getVoxelColorsForSubSliceInMap(const int32_t mapIndex, bool iLoopFlag = true; while (iLoopFlag) { - const int64_t dataOffset = m_voxelIndicesToOffset->getOffsetForIndices(i, + const int64_t dataOffset = m_voxelIndicesToOffsetForDataMapping->getOffsetForIndices(i, sliceIndex, k); if (dataOffset >= 0) { @@ -3662,7 +3861,7 @@ CiftiMappableDataFile::getVoxelColorsForSubSliceInMap(const int32_t mapIndex, while (jLoopFlag) { - const int64_t dataOffset = m_voxelIndicesToOffset->getOffsetForIndices(sliceIndex, + const int64_t dataOffset = m_voxelIndicesToOffsetForDataMapping->getOffsetForIndices(sliceIndex, j, k); if (dataOffset >= 0) { @@ -3785,7 +3984,7 @@ CiftiMappableDataFile::getVoxelColorInMapForLabelData(const std::vector& const GiftiLabelTable* labelTable = getMapLabelTable(mapIndex); CaretAssert(labelTable); - const int64_t dataOffset = m_voxelIndicesToOffset->getOffsetForIndices(indexIn1, + const int64_t dataOffset = m_voxelIndicesToOffsetForDataMapping->getOffsetForIndices(indexIn1, indexIn2, indexIn3); if (dataOffset >= 0) { @@ -3849,7 +4048,7 @@ CiftiMappableDataFile::getVoxelColorInMap(const int64_t indexIn1, nonConstThis->updateScalarColoringForMap(mapIndex); } - CaretAssert(m_voxelIndicesToOffset); + CaretAssert(m_voxelIndicesToOffsetForDataMapping); const int64_t mapRgbaCount = m_mapContent[mapIndex]->m_rgba.size(); if (mapRgbaCount <= 0) { @@ -3857,7 +4056,7 @@ CiftiMappableDataFile::getVoxelColorInMap(const int64_t indexIn1, } const uint8_t* mapRGBA = &m_mapContent[mapIndex]->m_rgba[0]; - const int64_t dataOffset = m_voxelIndicesToOffset->getOffsetForIndices(indexIn1, + const int64_t dataOffset = m_voxelIndicesToOffsetForDataMapping->getOffsetForIndices(indexIn1, indexIn2, indexIn3); if (dataOffset >= 0) { @@ -5081,7 +5280,7 @@ CiftiMappableDataFile::getMapVolumeVoxelValue(const int32_t mapIndex, ijkOut[0] = ijk[0]; ijkOut[1] = ijk[1]; ijkOut[2] = ijk[2]; - const int64_t dataOffset = m_voxelIndicesToOffset->getOffsetForIndices(ijk[0], + const int64_t dataOffset = m_voxelIndicesToOffsetForDataMapping->getOffsetForIndices(ijk[0], ijk[1], ijk[2]); if (dataOffset >= 0) { @@ -5294,7 +5493,7 @@ CiftiMappableDataFile::getMapVolumeVoxelValues(const std::vector mapInd ijkOut[0] = ijk[0]; ijkOut[1] = ijk[1]; ijkOut[2] = ijk[2]; - const int64_t dataOffset = m_voxelIndicesToOffset->getOffsetForIndices(ijk[0], + const int64_t dataOffset = m_voxelIndicesToOffsetForDataMapping->getOffsetForIndices(ijk[0], ijk[1], ijk[2]); if (dataOffset >= 0) { @@ -5546,7 +5745,7 @@ CiftiMappableDataFile::getVoxelValue(const float coordinateX, voxelK, mapIndex, component)) { - const int64_t dataOffset = m_voxelIndicesToOffset->getOffsetForIndices(voxelI, + const int64_t dataOffset = m_voxelIndicesToOffsetForDataMapping->getOffsetForIndices(voxelI, voxelJ, voxelK); if (dataOffset >= 0) { @@ -5600,7 +5799,7 @@ CiftiMappableDataFile::getMapDataOffsetForVoxelAtCoordinate(const float coordina voxelK, mapIndex, 0)) { - dataOffset = m_voxelIndicesToOffset->getOffsetForIndices(voxelI, + dataOffset = m_voxelIndicesToOffsetForDataMapping->getOffsetForIndices(voxelI, voxelJ, voxelK); } diff --git a/src/Files/CiftiMappableDataFile.h b/src/Files/CiftiMappableDataFile.h index ed17338ee..2fcbe0f4e 100644 --- a/src/Files/CiftiMappableDataFile.h +++ b/src/Files/CiftiMappableDataFile.h @@ -261,6 +261,14 @@ namespace caret { virtual void getDimensions(std::vector& dimsOut) const override; + virtual void getDimensionsForDataLoading(int64_t& dimOut1, + int64_t& dimOut2, + int64_t& dimOut3, + int64_t& dimTimeOut, + int64_t& numComponents) const; + + virtual void getDimensionsForDataLoading(std::vector& dimsOut) const; + virtual void getMapDimensions(std::vector &dim) const; virtual const int64_t& getNumberOfComponents() const override; @@ -287,12 +295,25 @@ namespace caret { int64_t& indexOut2, int64_t& indexOut3) const override; + virtual void enclosingVoxelForDataLoading(const float& coordIn1, + const float& coordIn2, + const float& coordIn3, + int64_t& indexOut1, + int64_t& indexOut2, + int64_t& indexOut3) const; + virtual bool indexValid(const int64_t& indexIn1, const int64_t& indexIn2, const int64_t& indexIn3, const int64_t brickIndex = 0, const int64_t component = 0) const override; + virtual bool indexValidForDataLoading(const int64_t& indexIn1, + const int64_t& indexIn2, + const int64_t& indexIn3, + const int64_t brickIndex = 0, + const int64_t component = 0) const; + virtual const VolumeSpace& getVolumeSpace() const; virtual void getVoxelSpaceBoundingBox(BoundingBox& boundingBoxOut) const override; @@ -777,7 +798,8 @@ namespace caret { bool m_fileHistogramLimitedValuesIncludeZeroValues; /** Fast conversion of IJK to data offset */ - CaretPointer m_voxelIndicesToOffset; + CaretPointer m_voxelIndicesToOffsetForDataMapping; + CaretPointer m_voxelIndicesToOffsetForDataReading; /** Holds class and name hierarchy used for display selection */ mutable CaretPointer m_classNameHierarchy; -- GitLab From 355bcda92ed45fb4362795e7e34617d7a6938d89 Mon Sep 17 00:00:00 2001 From: John Harwell Date: Mon, 12 Nov 2018 15:57:18 -0600 Subject: [PATCH 110/427] WB-668 Titling columns in tile tabs: Do not show a 'Spacer' tool button in the annotation toolbar. Instead, use 'TAB' for annotations in both TAB and SPACER space and set appropriately based upon the location of where the annotation is created. --- src/GuiQt/AnnotationCoordinateSpaceWidget.cxx | 45 +++++++++++-------- src/GuiQt/AnnotationCreateDialog.cxx | 35 +++++++++++++-- src/GuiQt/AnnotationCreateDialog.h | 2 +- src/GuiQt/AnnotationInsertNewWidget.cxx | 38 ++++++++++------ 4 files changed, 84 insertions(+), 36 deletions(-) diff --git a/src/GuiQt/AnnotationCoordinateSpaceWidget.cxx b/src/GuiQt/AnnotationCoordinateSpaceWidget.cxx index 5d83631d9..88890fb5a 100644 --- a/src/GuiQt/AnnotationCoordinateSpaceWidget.cxx +++ b/src/GuiQt/AnnotationCoordinateSpaceWidget.cxx @@ -59,6 +59,7 @@ m_browserWindowIndex(browserWindowIndex) "Mouse dragging to move/resize\n" "annotations allowed in Tab or \n" "Window space only.\n" + " Ch : Chart\n" " St : Stereotaxic\n" " Sf : Surface\n" " T : Tab\n" @@ -94,7 +95,7 @@ AnnotationCoordinateSpaceWidget::updateContent(std::vector annotati CaretAssert(annotations[0]); AnnotationCoordinateSpaceEnum::Enum space = annotations[0]->getCoordinateSpace(); - std::set tabIndices; + std::set tabIndices; bool haveMultipleSpacesFlag = false; for (int32_t i = 0; i < static_cast(annotations.size()); i++) { CaretAssertVectorIndex(annotations, i); @@ -107,8 +108,27 @@ AnnotationCoordinateSpaceWidget::updateContent(std::vector annotati } if (annotations[i]->getCoordinateSpace() == AnnotationCoordinateSpaceEnum::TAB) { - tabIndices.insert(ann->getTabIndex()); + tabIndices.insert(AString::number(ann->getTabIndex() + 1)); } + + if (annotations[i]->getCoordinateSpace() == AnnotationCoordinateSpaceEnum::SPACER) { + const SpacerTabIndex sti = ann->getSpacerTabIndex(); + tabIndices.insert(AString::number(sti.getRowIndex() + 1) + + "," + + AString::number(sti.getColumnIndex() + 1)); + + } + } + + AString indicesString; + for (const auto s : tabIndices) { + if (indicesString.isEmpty()) { + indicesString.append(":"); + } + else { + indicesString.append(";"); + } + indicesString.append(s); } if (haveMultipleSpacesFlag) { @@ -120,28 +140,17 @@ AnnotationCoordinateSpaceWidget::updateContent(std::vector annotati case AnnotationCoordinateSpaceEnum::CHART: break; case AnnotationCoordinateSpaceEnum::SPACER: + { + text = AnnotationCoordinateSpaceEnum::toGuiAbbreviatedName(AnnotationCoordinateSpaceEnum::TAB); + text.append(indicesString); + } break; case AnnotationCoordinateSpaceEnum::STEREOTAXIC: break; case AnnotationCoordinateSpaceEnum::SURFACE: break; case AnnotationCoordinateSpaceEnum::TAB: - { - QString tabString; - for (std::set::iterator iter = tabIndices.begin(); - iter != tabIndices.end(); - iter++) { - if (tabString.isEmpty()) { - tabString.append(":"); - } - else { - tabString.append(","); - } - tabString.append(AString::number(*iter + 1)); - } - - text.append(tabString); - } + text.append(indicesString); break; case AnnotationCoordinateSpaceEnum::VIEWPORT: break; diff --git a/src/GuiQt/AnnotationCreateDialog.cxx b/src/GuiQt/AnnotationCreateDialog.cxx index b269eb88a..042c7e75c 100644 --- a/src/GuiQt/AnnotationCreateDialog.cxx +++ b/src/GuiQt/AnnotationCreateDialog.cxx @@ -159,7 +159,7 @@ AnnotationCreateDialog::newAnnotationFromSpaceTypeAndBounds(const MouseEvent& mo Annotation* AnnotationCreateDialog::newAnnotationFromSpaceTypeAndCoords(const Mode mode, const MouseEvent& mouseEvent, - const AnnotationCoordinateSpaceEnum::Enum annotationSpace, + const AnnotationCoordinateSpaceEnum::Enum annotationSpaceIn, const AnnotationTypeEnum::Enum annotationType, AnnotationFile* annotationFile) { @@ -173,7 +173,7 @@ AnnotationCreateDialog::newAnnotationFromSpaceTypeAndCoords(const Mode mode, } NewAnnotationInfo newInfo(mouseEvent, - annotationSpace, + annotationSpaceIn, annotationType, useBothFlag, annotationFile); @@ -212,7 +212,7 @@ AnnotationCreateDialog::newAnnotationFromSpaceTypeAndCoords(const Mode mode, } else { AString errorMessage; - Annotation* newAnn = createAnnotation(newInfo, annotationSpace, errorMessage); + Annotation* newAnn = createAnnotation(newInfo, newInfo.m_selectedSpace, /*annotationSpace,*/ errorMessage); if (newAnn != NULL) { DisplayPropertiesAnnotation* dpa = GuiManager::get()->getBrain()->getDisplayPropertiesAnnotation(); dpa->updateForNewAnnotation(newAnn); @@ -447,6 +447,13 @@ m_imageHeight(0) iter++) { const AnnotationCoordinateSpaceEnum::Enum space = *iter; QRadioButton* rb = new QRadioButton(AnnotationCoordinateSpaceEnum::toGuiName(space)); + if (space == AnnotationCoordinateSpaceEnum::SPACER) { + /* + * Spacer and Tab are presented as 'TAB' to the user. So show 'TAB' but + * use the integer code for 'SPACER'. + */ + rb->setText(AnnotationCoordinateSpaceEnum::toGuiName(AnnotationCoordinateSpaceEnum::TAB)); + } m_annotationSpaceButtonGroup->addButton(rb, AnnotationCoordinateSpaceEnum::toIntegerCode(space)); coordGroupLayout->addWidget(rb); @@ -894,10 +901,30 @@ m_annotationFile(annotationFile) NULL, m_validSpaces); } + + /* + * There is not a selection in the GUI for the user to choose 'SPACER' coordinate space. + * Instead the user uses 'TAB' space in the GUI. So, if TAB space is NOT valid, but + * SPACER space is valid, change the requested space to 'SPACER' + */ + if (m_selectedSpace == AnnotationCoordinateSpaceEnum::TAB) { + const bool haveTabFlag = (std::find(m_validSpaces.begin(), + m_validSpaces.end(), + AnnotationCoordinateSpaceEnum::TAB) != m_validSpaces.end()); + const bool haveSpacerFlag = (std::find(m_validSpaces.begin(), + m_validSpaces.end(), + AnnotationCoordinateSpaceEnum::SPACER) != m_validSpaces.end()); + + if (haveSpacerFlag) { + if ( ! haveTabFlag) { + m_selectedSpace = AnnotationCoordinateSpaceEnum::SPACER; + } + } + } } /** - * When the user drags to create an annotation, two points are + * When the user drags to create an annotation, two points are * used at opposite corners. For non-linear annotations, we * need a center, width, and height. So for these types, * convert the two points to one point with center, width, diff --git a/src/GuiQt/AnnotationCreateDialog.h b/src/GuiQt/AnnotationCreateDialog.h index e6d79952b..2aad84ba9 100644 --- a/src/GuiQt/AnnotationCreateDialog.h +++ b/src/GuiQt/AnnotationCreateDialog.h @@ -91,7 +91,7 @@ namespace caret { const MouseEvent& m_mouseEvent; - const AnnotationCoordinateSpaceEnum::Enum m_selectedSpace; + AnnotationCoordinateSpaceEnum::Enum m_selectedSpace; const AnnotationTypeEnum::Enum m_annotationType; diff --git a/src/GuiQt/AnnotationInsertNewWidget.cxx b/src/GuiQt/AnnotationInsertNewWidget.cxx index 1e5d5e380..3ebd25f49 100644 --- a/src/GuiQt/AnnotationInsertNewWidget.cxx +++ b/src/GuiQt/AnnotationInsertNewWidget.cxx @@ -101,8 +101,12 @@ m_browserWindowIndex(browserWindowIndex) m_spaceActionGroup); QToolButton* tabSpaceToolButton = createSpaceToolButton(AnnotationCoordinateSpaceEnum::TAB, m_spaceActionGroup); - QToolButton* spacerTabSpaceToolButton = createSpaceToolButton(AnnotationCoordinateSpaceEnum::SPACER, - m_spaceActionGroup); + const bool showSpacerToolButtonFlag(false); + QToolButton* spacerSpaceToolButton(NULL); + if (showSpacerToolButtonFlag) { + spacerSpaceToolButton = createSpaceToolButton(AnnotationCoordinateSpaceEnum::SPACER, + m_spaceActionGroup); + } QToolButton* stereotaxicSpaceToolButton = createSpaceToolButton(AnnotationCoordinateSpaceEnum::STEREOTAXIC, m_spaceActionGroup); QToolButton* surfaceSpaceToolButton = createSpaceToolButton(AnnotationCoordinateSpaceEnum::SURFACE, @@ -124,7 +128,9 @@ m_browserWindowIndex(browserWindowIndex) shapeTextToolButton->setMaximumSize(mw, mh); chartSpaceToolButton->setMaximumSize(mw, mh); - spacerTabSpaceToolButton->setMaximumSize(mw, mh); + if (spacerSpaceToolButton != NULL) { + spacerSpaceToolButton->setMaximumSize(mw, mh); + } tabSpaceToolButton->setMaximumSize(mw, mh); stereotaxicSpaceToolButton->setMaximumSize(mw, mh); surfaceSpaceToolButton->setMaximumSize(mw, mh); @@ -150,8 +156,11 @@ m_browserWindowIndex(browserWindowIndex) QLabel* insertLabel = new QLabel("Insert New"); + const int32_t topColumnCount((spacerSpaceToolButton != NULL) + ? 9 + : 8); gridLayout->addWidget(insertLabel, - 0, 0, 1, 9, + 0, 0, 1, topColumnCount, Qt::AlignHCenter); gridLayout->addLayout(fileLayout, @@ -160,20 +169,23 @@ m_browserWindowIndex(browserWindowIndex) gridLayout->setColumnMinimumWidth(1, 5); + int32_t topColumn(2); gridLayout->addWidget(spaceLabel, - 1, 2, Qt::AlignLeft); + 1, topColumn++, Qt::AlignLeft); gridLayout->addWidget(chartSpaceToolButton, - 1, 3); - gridLayout->addWidget(spacerTabSpaceToolButton, - 1, 4); + 1, topColumn++); + if (spacerSpaceToolButton != NULL) { + gridLayout->addWidget(spacerSpaceToolButton, + 1, topColumn++); + } gridLayout->addWidget(stereotaxicSpaceToolButton, - 1, 5); + 1, topColumn++); gridLayout->addWidget(surfaceSpaceToolButton, - 1, 6); + 1, topColumn++); gridLayout->addWidget(tabSpaceToolButton, - 1, 7); + 1, topColumn++); gridLayout->addWidget(windowSpaceToolButton, - 1, 8); + 1, topColumn++); gridLayout->setRowMinimumHeight(2, 2); @@ -212,7 +224,7 @@ m_browserWindowIndex(browserWindowIndex) 1, 2, Qt::AlignLeft); gridLayout->addWidget(chartSpaceToolButton, 1, 3); - gridLayout->addWidget(spacerTabSpaceToolButton, + gridLayout->addWidget(spacerSpaceToolButton, 1, 4); gridLayout->addWidget(stereotaxicSpaceToolButton, 1, 5); -- GitLab From a32b77892f73ce89c234c4534e0376aab498818c Mon Sep 17 00:00:00 2001 From: John Harwell Date: Tue, 13 Nov 2018 09:32:22 -0600 Subject: [PATCH 111/427] WB-668 Titling columns in tile tabs: Added insertion of a Spacer Row or Column to Construction Menus on Tile Tabs Configuration Dialog. --- .../EventTileTabsConfigurationModification.h | 2 + src/Files/AnnotationFile.cxx | 21 ++++++++ src/GuiQt/TileTabsConfigurationDialog.cxx | 21 +++++++- src/GuiQt/TileTabsConfigurationDialog.h | 2 + src/GuiQt/TileTabsConfigurationModifier.cxx | 53 +++++++++++++++++++ src/GuiQt/TileTabsConfigurationModifier.h | 4 ++ 6 files changed, 102 insertions(+), 1 deletion(-) diff --git a/src/Common/EventTileTabsConfigurationModification.h b/src/Common/EventTileTabsConfigurationModification.h index cf75b3044..202fa67c9 100644 --- a/src/Common/EventTileTabsConfigurationModification.h +++ b/src/Common/EventTileTabsConfigurationModification.h @@ -51,6 +51,8 @@ namespace caret { DELETE_IT, /* DELETE is reserved word on Windows */ DUPLICATE_AFTER, DUPLICATE_BEFORE, + INSERT_SPACER_BEFORE, + INSERT_SPACER_AFTER, MOVE_AFTER, MOVE_BEFORE }; diff --git a/src/Files/AnnotationFile.cxx b/src/Files/AnnotationFile.cxx index f13005d41..bd2350966 100644 --- a/src/Files/AnnotationFile.cxx +++ b/src/Files/AnnotationFile.cxx @@ -2193,6 +2193,12 @@ AnnotationFile::updateSpacerAnnotationsAfterTileTabsModification(const EventTile duplicateToIndex = rowColumnIndex; } break; + case EventTileTabsConfigurationModification::Operation::INSERT_SPACER_BEFORE: + shiftStartIndex = rowColumnIndex; + break; + case EventTileTabsConfigurationModification::Operation::INSERT_SPACER_AFTER: + shiftStartIndex = rowColumnIndex + 1; + break; case EventTileTabsConfigurationModification::Operation::MOVE_AFTER: moveOneIndex = rowColumnIndex; moveTwoIndex = rowColumnIndex + 1; @@ -2277,6 +2283,21 @@ AnnotationFile::updateSpacerAnnotationsAfterTileTabsModification(const EventTile duplicatedAnnotations.push_back(dupAnn); } + if (rowFlag) { + rowIndex = rcIndex; + } + else { + columnIndex = rcIndex; + } + } + break; + case EventTileTabsConfigurationModification::Operation::INSERT_SPACER_BEFORE: + case EventTileTabsConfigurationModification::Operation::INSERT_SPACER_AFTER: + { + int32_t rcIndex = (rowFlag ? rowIndex : columnIndex); + if (rcIndex >= shiftStartIndex) { + rcIndex = rcIndex + 1; + } if (rowFlag) { rowIndex = rcIndex; } diff --git a/src/GuiQt/TileTabsConfigurationDialog.cxx b/src/GuiQt/TileTabsConfigurationDialog.cxx index f10d4024a..a0362c753 100644 --- a/src/GuiQt/TileTabsConfigurationDialog.cxx +++ b/src/GuiQt/TileTabsConfigurationDialog.cxx @@ -1154,7 +1154,13 @@ TileTabElementWidgets::createConstructionMenu(QToolButton* toolButton) const AString duplicateBeforeText((m_rowColumnType == EventTileTabsConfigurationModification::RowColumnType::COLUMN) ? "Duplicate this Column to Left" : "Duplicate this Row Above"); - + + const AString insertSpacerAfterText((m_rowColumnType == EventTileTabsConfigurationModification::RowColumnType::COLUMN) + ? "Insert Spacer Column to Right" + : "Insert Spacer Row Below"); + const AString insertSpacerBeforeText((m_rowColumnType == EventTileTabsConfigurationModification::RowColumnType::COLUMN) + ? "Insert Spacer Column to Left" + : "Insert Spacer Row Above"); const AString moveAfterText((m_rowColumnType == EventTileTabsConfigurationModification::RowColumnType::COLUMN) ? "Move this Column to Right" : "Move this Row Down"); @@ -1172,6 +1178,12 @@ TileTabElementWidgets::createConstructionMenu(QToolButton* toolButton) m_menuDuplicateBeforeAction = new QAction(duplicateBeforeText); m_menuDuplicateBeforeAction->setData(static_cast(EventTileTabsConfigurationModification::Operation::DUPLICATE_BEFORE)); + m_insertSpacerAfterAction = new QAction(insertSpacerAfterText); + m_insertSpacerAfterAction->setData(static_cast(EventTileTabsConfigurationModification::Operation::INSERT_SPACER_AFTER)); + + m_insertSpacerBeforeAction = new QAction(insertSpacerBeforeText); + m_insertSpacerBeforeAction->setData(static_cast(EventTileTabsConfigurationModification::Operation::INSERT_SPACER_BEFORE)); + m_menuMoveAfterAction = new QAction(moveAfterText); m_menuMoveAfterAction->setData(static_cast(EventTileTabsConfigurationModification::Operation::MOVE_AFTER)); @@ -1182,6 +1194,9 @@ TileTabElementWidgets::createConstructionMenu(QToolButton* toolButton) menu->addAction(m_menuDuplicateBeforeAction); menu->addAction(m_menuDuplicateAfterAction); menu->addSeparator(); + menu->addAction(m_insertSpacerBeforeAction); + menu->addAction(m_insertSpacerAfterAction); + menu->addSeparator(); menu->addAction(m_menuMoveBeforeAction); menu->addAction(m_menuMoveAfterAction); menu->addSeparator(); @@ -1246,6 +1261,10 @@ TileTabElementWidgets::constructionMenuTriggered(QAction* action) break; case EventTileTabsConfigurationModification::Operation::DUPLICATE_BEFORE: break; + case EventTileTabsConfigurationModification::Operation::INSERT_SPACER_BEFORE: + break; + case EventTileTabsConfigurationModification::Operation::INSERT_SPACER_AFTER: + break; case EventTileTabsConfigurationModification::Operation::MOVE_AFTER: break; case EventTileTabsConfigurationModification::Operation::MOVE_BEFORE: diff --git a/src/GuiQt/TileTabsConfigurationDialog.h b/src/GuiQt/TileTabsConfigurationDialog.h index 15579798d..5c20a4deb 100644 --- a/src/GuiQt/TileTabsConfigurationDialog.h +++ b/src/GuiQt/TileTabsConfigurationDialog.h @@ -236,6 +236,8 @@ namespace caret { QAction* m_menuDeleteAction; QAction* m_menuDuplicateAfterAction; QAction* m_menuDuplicateBeforeAction; + QAction* m_insertSpacerAfterAction; + QAction* m_insertSpacerBeforeAction; QAction* m_menuMoveAfterAction; QAction* m_menuMoveBeforeAction; diff --git a/src/GuiQt/TileTabsConfigurationModifier.cxx b/src/GuiQt/TileTabsConfigurationModifier.cxx index d631b61de..53996dd27 100644 --- a/src/GuiQt/TileTabsConfigurationModifier.cxx +++ b/src/GuiQt/TileTabsConfigurationModifier.cxx @@ -230,6 +230,27 @@ TileTabsConfigurationModifier::performModification(AString& errorMessageOut) errorMessageOut = "Invalid ROWCOL index=RCINDEX when duplicating"; } break; + case EventTileTabsConfigurationModification::Operation::INSERT_SPACER_AFTER: + case EventTileTabsConfigurationModification::Operation::INSERT_SPACER_BEFORE: + if ((rowColumnIndex >= 0) + && (rowColumnIndex < numRowColumns)) { + CaretAssertVectorIndex(m_rowColumns, 0); + const int32_t numberOfElements = m_rowColumns[0]->m_tabElements.size(); + RowColumnContent* rowColumnSpacer = RowColumnContent::newInstanceContainingSpacers(numberOfElements); + if (rowColumnSpacer != NULL) { + int32_t insertOffset = rowColumnIndex; + if (m_modifyEvent->getOperation() == EventTileTabsConfigurationModification::Operation::INSERT_SPACER_AFTER) { + insertOffset++; + } + m_rowColumns.insert(m_rowColumns.begin() + + insertOffset, + rowColumnSpacer); + } + } + else { + errorMessageOut = "Invalid ROWCOL index=RCINDEX when insert spacer"; + } + break; case EventTileTabsConfigurationModification::Operation::MOVE_AFTER: if (numRowColumns <= 1) { errorMessageOut = "Cannot move ROWCOL when there is only one ROWCOL"; @@ -495,6 +516,38 @@ TileTabsConfigurationModifier::RowColumnContent::RowColumnContent(const std::vec } } +/* + * Constructor that creates the given number of elements. + * + * @param numberOfElements + * Number of elements for row/column. + */ +TileTabsConfigurationModifier::RowColumnContent::RowColumnContent(const int32_t numberOfElements) +{ + for (int32_t i = 0; i < numberOfElements; i++) { + m_tabElements.push_back(new Element(i, i, NULL)); + } + m_stretching = new TileTabsRowColumnElement(); +} + +/** + * @return New instance containing the given number of elements setup as spacers. + * + * @param numberOfElements + * Number of elements for row/column. + */ +TileTabsConfigurationModifier::RowColumnContent* +TileTabsConfigurationModifier::RowColumnContent::newInstanceContainingSpacers(const int32_t numberOfElements) +{ + RowColumnContent* content = new RowColumnContent(numberOfElements); + + content->m_stretching->setContentType(TileTabsRowColumnContentTypeEnum::SPACE); + content->m_stretching->setStretchType(TileTabsRowColumnStretchTypeEnum::WEIGHT); + content->m_stretching->setWeightStretch(1.0); + + return content; +} + /** * Destructor. */ diff --git a/src/GuiQt/TileTabsConfigurationModifier.h b/src/GuiQt/TileTabsConfigurationModifier.h index e3482f59a..5f01e846d 100644 --- a/src/GuiQt/TileTabsConfigurationModifier.h +++ b/src/GuiQt/TileTabsConfigurationModifier.h @@ -89,6 +89,8 @@ namespace caret { RowColumnContent(const RowColumnContent& obj); + static RowColumnContent* newInstanceContainingSpacers(const int32_t numberOfElements); + ~RowColumnContent(); RowColumnContent* clone(AString& errorMessageOut) const; @@ -99,6 +101,8 @@ namespace caret { TileTabsRowColumnElement* m_stretching; + private: + RowColumnContent(const int32_t numberOfElements); }; private: -- GitLab From dd688798a7437fc019d92101d5474bf2ec348920 Mon Sep 17 00:00:00 2001 From: John Harwell Date: Tue, 13 Nov 2018 11:03:29 -0600 Subject: [PATCH 112/427] Fixed crash when a spacer annotations is currently selected and the row/column containing the spacer annotation is deleted. --- src/GuiQt/AnnotationFontWidget.cxx | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/src/GuiQt/AnnotationFontWidget.cxx b/src/GuiQt/AnnotationFontWidget.cxx index b89ff8b2c..a343b9515 100644 --- a/src/GuiQt/AnnotationFontWidget.cxx +++ b/src/GuiQt/AnnotationFontWidget.cxx @@ -284,7 +284,21 @@ AnnotationFontWidget::receiveEvent(Event* event) if (m_browserWindowIndex == redrawEvent->getBrowserWindowIndex()) { if (isVisible()) { - updateFontSizeControls(); + AnnotationManager* annotationManager = GuiManager::get()->getBrain()->getAnnotationManager(); + std::vector selectedAnnotations = annotationManager->getAnnotationsSelectedForEditing(m_browserWindowIndex); + + if (selectedAnnotations.empty()) { + return; + } + + std::vector textAnnotations; + for (auto ann : selectedAnnotations) { + AnnotationFontAttributesInterface* textAnn = dynamic_cast(ann); + if (textAnn != NULL) { + textAnnotations.push_back(textAnn); + } + } + updateContent(textAnnotations); } } } -- GitLab From b72ae01f03da038597367f0d968436476674567e Mon Sep 17 00:00:00 2001 From: John Harwell Date: Tue, 13 Nov 2018 11:53:01 -0600 Subject: [PATCH 113/427] Corrected bug that failed to delete a tab annotation in a tab that was deleted. --- src/GuiQt/TileTabsConfigurationDialog.cxx | 2 ++ src/GuiQt/TileTabsConfigurationModifier.cxx | 7 +------ 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/src/GuiQt/TileTabsConfigurationDialog.cxx b/src/GuiQt/TileTabsConfigurationDialog.cxx index a0362c753..e953ec474 100644 --- a/src/GuiQt/TileTabsConfigurationDialog.cxx +++ b/src/GuiQt/TileTabsConfigurationDialog.cxx @@ -49,6 +49,7 @@ #include "EventGraphicsUpdateOneWindow.h" #include "EventHelpViewerDisplay.h" #include "EventManager.h" +#include "EventUserInterfaceUpdate.h" #include "GuiManager.h" #include "SessionManager.h" #include "TileTabsConfiguration.h" @@ -1003,6 +1004,7 @@ TileTabsConfigurationDialog::updateGraphicsWindow() const BrowserWindowContent* bwc = getBrowserWindowContent(); if (bwc->isTileTabsEnabled()) { const int32_t windowIndex = bwc->getWindowIndex(); + EventManager::get()->sendEvent(EventUserInterfaceUpdate().getPointer()); EventManager::get()->sendEvent(EventGraphicsUpdateOneWindow(windowIndex).getPointer()); } } diff --git a/src/GuiQt/TileTabsConfigurationModifier.cxx b/src/GuiQt/TileTabsConfigurationModifier.cxx index 53996dd27..3fe8be2a3 100644 --- a/src/GuiQt/TileTabsConfigurationModifier.cxx +++ b/src/GuiQt/TileTabsConfigurationModifier.cxx @@ -33,9 +33,7 @@ #include "EventBrowserTabDelete.h" #include "EventBrowserTabNew.h" #include "EventBrowserWindowTileTabOperation.h" -#include "EventGraphicsUpdateOneWindow.h" #include "EventManager.h" -#include "EventUserInterfaceUpdate.h" #include "EventTileTabsConfigurationModification.h" #include "GuiManager.h" #include "SpacerTabContent.h" @@ -124,9 +122,6 @@ TileTabsConfigurationModifier::run(AString& errorMessageOut) validFlag = loadRowColumnsIntoTileTabsConfiguration(errorMessageOut); } - EventManager::get()->sendEvent(EventUserInterfaceUpdate().getPointer()); - EventManager::get()->sendEvent(EventGraphicsUpdateOneWindow(m_modifyEvent->getWindowIndex()).getPointer()); - return validFlag; } @@ -188,13 +183,13 @@ TileTabsConfigurationModifier::performModification(AString& errorMessageOut) CaretAssert(std::find(m_rowColumns.begin(), m_rowColumns.end(), deleteRowColumn) == m_rowColumns.end()); - delete deleteRowColumn; for (auto t : deleteRowColumn->m_tabElements) { if (t->m_browserTabContent != NULL) { m_browserTabsToDelete.push_back(t->m_browserTabContent); } } + delete deleteRowColumn; } else { errorMessageOut = "Invalid ROWCOL index=RCINDEX when deleting"; -- GitLab From 1fd9011854d6e1b2c401f476946448fe66ec2a6a Mon Sep 17 00:00:00 2001 From: John Harwell Date: Tue, 13 Nov 2018 12:07:47 -0600 Subject: [PATCH 114/427] In Annotation Toolbar, the 'Spaces' buttons for insertion of new annotations were enabled based upon the content of the selected tab. This has been changed so that in Tile Tabs, the enabled 'Spaces' are based upon the content of all displayed tabs. Thus is the selected tab contains a volume, the user can now select Surface Space and insert the new annotation in a tab containing a surface. --- src/GuiQt/AnnotationInsertNewWidget.cxx | 78 ++++++++++++++----------- 1 file changed, 45 insertions(+), 33 deletions(-) diff --git a/src/GuiQt/AnnotationInsertNewWidget.cxx b/src/GuiQt/AnnotationInsertNewWidget.cxx index 3ebd25f49..b768fa26b 100644 --- a/src/GuiQt/AnnotationInsertNewWidget.cxx +++ b/src/GuiQt/AnnotationInsertNewWidget.cxx @@ -317,44 +317,56 @@ AnnotationInsertNewWidget::enableDisableSpaceActions() if (window == NULL) { return; } - BrowserTabContent* tabContent = window->getBrowserTabContent(); - if (tabContent == NULL) { - return; + + std::vector allTabContent; + if (window->isTileTabsSelected()) { + window->getAllTabContent(allTabContent); } - Model* model = tabContent->getModelForDisplay(); - if (model == NULL) { - return; + else { + BrowserTabContent* tabContent = window->getBrowserTabContent(); + if (tabContent != NULL) { + allTabContent.push_back(tabContent); + } } - const ModelTypeEnum::Enum modelType = model->getModelType(); + + const bool spacerSpaceValidFlag = window->isTileTabsSelected(); + const bool tabSpaceValidFlag = ( ! allTabContent.empty()); + const bool windowSpaceValidFlag = ( ! allTabContent.empty()); bool chartSpaceValidFlag = false; - const bool spacerSpaceValidFlag = window->isTileTabsSelected(); bool surfaceSpaceValidFlag = false; bool stereotaxicSpaceValidFlag = false; - switch (modelType) { - case ModelTypeEnum::MODEL_TYPE_CHART: - break; - case ModelTypeEnum::MODEL_TYPE_CHART_TWO: - chartSpaceValidFlag = true; - break; - case ModelTypeEnum::MODEL_TYPE_INVALID: - break; - case ModelTypeEnum::MODEL_TYPE_SURFACE: - stereotaxicSpaceValidFlag = true; - surfaceSpaceValidFlag = true; - break; - case ModelTypeEnum::MODEL_TYPE_SURFACE_MONTAGE: - stereotaxicSpaceValidFlag = true; - surfaceSpaceValidFlag = true; - break; - case ModelTypeEnum::MODEL_TYPE_VOLUME_SLICES: - stereotaxicSpaceValidFlag = true; - break; - case ModelTypeEnum::MODEL_TYPE_WHOLE_BRAIN: - stereotaxicSpaceValidFlag = true; - surfaceSpaceValidFlag = true; - break; + for (auto tabContent : allTabContent) { + Model* model = tabContent->getModelForDisplay(); + if (model == NULL) { + return; + } + const ModelTypeEnum::Enum modelType = model->getModelType(); + switch (modelType) { + case ModelTypeEnum::MODEL_TYPE_CHART: + break; + case ModelTypeEnum::MODEL_TYPE_CHART_TWO: + chartSpaceValidFlag = true; + break; + case ModelTypeEnum::MODEL_TYPE_INVALID: + break; + case ModelTypeEnum::MODEL_TYPE_SURFACE: + stereotaxicSpaceValidFlag = true; + surfaceSpaceValidFlag = true; + break; + case ModelTypeEnum::MODEL_TYPE_SURFACE_MONTAGE: + stereotaxicSpaceValidFlag = true; + surfaceSpaceValidFlag = true; + break; + case ModelTypeEnum::MODEL_TYPE_VOLUME_SLICES: + stereotaxicSpaceValidFlag = true; + break; + case ModelTypeEnum::MODEL_TYPE_WHOLE_BRAIN: + stereotaxicSpaceValidFlag = true; + surfaceSpaceValidFlag = true; + break; + } } QAction* selectedAction = m_spaceActionGroup->checkedAction(); @@ -384,12 +396,12 @@ AnnotationInsertNewWidget::enableDisableSpaceActions() break; case AnnotationCoordinateSpaceEnum::TAB: tabSpaceAction = action; - enableSpaceFlag = true; + enableSpaceFlag = tabSpaceValidFlag; break; case AnnotationCoordinateSpaceEnum::VIEWPORT: break; case AnnotationCoordinateSpaceEnum::WINDOW: - enableSpaceFlag = true; + enableSpaceFlag = windowSpaceValidFlag; break; } -- GitLab From c65bb9aaca3304c44691594973e763554ba569ce Mon Sep 17 00:00:00 2001 From: John Harwell Date: Tue, 13 Nov 2018 12:16:20 -0600 Subject: [PATCH 115/427] Updated Tile Tabs Help page with Tim's suggested text. --- .../Tile_Tabs_Configuration.html | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/src/Resources/Help/HelpFiles/Menus/View_Menu/Tile_Tabs_Configuration/Tile_Tabs_Configuration.html b/src/Resources/Help/HelpFiles/Menus/View_Menu/Tile_Tabs_Configuration/Tile_Tabs_Configuration.html index 8c70a2c11..fe88ce268 100644 --- a/src/Resources/Help/HelpFiles/Menus/View_Menu/Tile_Tabs_Configuration/Tile_Tabs_Configuration.html +++ b/src/Resources/Help/HelpFiles/Menus/View_Menu/Tile_Tabs_Configuration/Tile_Tabs_Configuration.html @@ -24,6 +24,7 @@ + Tabs in the Viewing @@ -48,6 +49,7 @@ + Area
      @@ -66,6 +68,7 @@
      Workbench Window  + Selects the window for control of the window's Tile Tabs Configuration

      @@ -137,12 +140,11 @@ associated Percentage value of the window's height.  Thus, if the row's Percentage Height is 20% and the Window is 1000 pixels in height, the row is 200 pixels in height.  The Percentage value - for each row should be in the range 0% to 100%.  If ALL rows - use a Percentage height, wb_view does not verify that the sum of the - row percentages is 100%.  Therefore, if ALL rows use a - Percentage height and the sum of the Percentage is less than 100%, - there will be empty space at the bottom of the window and if greater - than 100%, rows at the bottom may not visible.
      + for each row should be in the range 0% to 100%.  If the rows + that use Percentage sum to more than 100%, then part or all of the + last rows may not be displayed (likewise for columns).  If + there are no rows/columns that use Weight, then there may be blank + unused space, depending on the percentages used."

      Example of Percentage Stretching
        -- GitLab From 945548eba325a305e8833a7e67e37a7043422573 Mon Sep 17 00:00:00 2001 From: John Harwell Date: Tue, 13 Nov 2018 16:13:47 -0600 Subject: [PATCH 116/427] Recent change for Asymmetric Connectome files expected mapping to brainordinates for all CIFTI files. However, scalar data series does not map to brainordinates so this is now allowed. --- src/Files/CiftiMappableDataFile.cxx | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/Files/CiftiMappableDataFile.cxx b/src/Files/CiftiMappableDataFile.cxx index 2f8193f2a..450cb4b0c 100644 --- a/src/Files/CiftiMappableDataFile.cxx +++ b/src/Files/CiftiMappableDataFile.cxx @@ -1011,9 +1011,7 @@ CiftiMappableDataFile::initializeAfterReading(const AString& filename) m_voxelIndicesToOffsetForDataReading.grabNew(new SparseVolumeIndexer(ciftiXML.getParcelsMap(CiftiXML::ALONG_COLUMN))); } else { - CaretAssertMessage(0, "Invalid mapping type for mapping data to brainordinates"); - throw DataFileException(filename, - "Invalid mapping type for mapping data to brainordinates"); + m_voxelIndicesToOffsetForDataReading.grabNew(new SparseVolumeIndexer()); } break; case DATA_ACCESS_FILE_COLUMNS_OR_XML_ALONG_ROW: -- GitLab From 0e584fb07886c1b247582d4d21f707cacebddce9 Mon Sep 17 00:00:00 2001 From: John Harwell Date: Tue, 13 Nov 2018 16:51:56 -0600 Subject: [PATCH 117/427] Temporary bug fix for Tile Tabs Configuration Dialog, the construction menus were not re-enabled when switching from Automatic to Custom mode. For now, leave the custom configuration enabled regardless of mode. --- src/GuiQt/TileTabsConfigurationDialog.cxx | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/GuiQt/TileTabsConfigurationDialog.cxx b/src/GuiQt/TileTabsConfigurationDialog.cxx index e953ec474..4d1aeabe1 100644 --- a/src/GuiQt/TileTabsConfigurationDialog.cxx +++ b/src/GuiQt/TileTabsConfigurationDialog.cxx @@ -750,7 +750,9 @@ TileTabsConfigurationDialog::updateStretchFactors() const bool editableFlag = ( ! m_automaticConfigurationRadioButton->isChecked()); - m_customConfigurationWidget->setEnabled(editableFlag); + // This does not re-enable the construction menu + //m_customConfigurationWidget->setEnabled(editableFlag); + m_loadPushButton->setEnabled(editableFlag); } -- GitLab From ca11b95dff91b016f150b1b0942de05bb652f9db Mon Sep 17 00:00:00 2001 From: John Harwell Date: Mon, 19 Nov 2018 15:11:55 -0600 Subject: [PATCH 118/427] WB-834 Improvements to Animations vs Redesign: Implemented a prototype macro system for recording and replaying some GUI events. --- src/GuiQt/BrainBrowserWindow.cxx | 5 + src/GuiQt/BrainBrowserWindow.h | 2 + src/GuiQt/BrainBrowserWindowToolBar.cxx | 24 + .../BrainBrowserWindowToolBarSlicePlane.cxx | 13 + ...rainBrowserWindowToolBarSliceSelection.cxx | 34 +- src/GuiQt/BrainOpenGLWidget.cxx | 43 + src/GuiQt/BrainOpenGLWidget.h | 7 +- src/GuiQt/CMakeLists.txt | 3 + src/GuiQt/MacroPrototype.cxx | 1379 +++++++++++++++++ src/GuiQt/MacroPrototype.h | 373 +++++ src/GuiQt/MapYokingGroupComboBox.cxx | 29 + src/GuiQt/MapYokingGroupComboBox.h | 3 + src/GuiQt/OverlayViewController.cxx | 101 +- src/GuiQt/OverlayViewController.h | 3 +- src/GuiQt/WuQtUtilities.cxx | 61 +- src/GuiQt/WuQtUtilities.h | 14 + 16 files changed, 2056 insertions(+), 38 deletions(-) create mode 100644 src/GuiQt/MacroPrototype.cxx create mode 100644 src/GuiQt/MacroPrototype.h diff --git a/src/GuiQt/BrainBrowserWindow.cxx b/src/GuiQt/BrainBrowserWindow.cxx index e694c0a41..89c659516 100644 --- a/src/GuiQt/BrainBrowserWindow.cxx +++ b/src/GuiQt/BrainBrowserWindow.cxx @@ -84,6 +84,7 @@ #include "GapsAndMargins.h" #include "GuiManager.h" #include "LockAspectWarningDialog.h" +#include "MacroPrototype.h" #include "ModelSurface.h" #include "ModelSurfaceMontage.h" #include "ModelWholeBrain.h" @@ -1537,6 +1538,10 @@ BrainBrowserWindow::createMenus() m_developMenuAction = menubar->addMenu(developMenu); m_developMenuAction->setVisible(prefs->isDevelopMenuEnabled()); + QMenu* macroMenu = new MacroMenu(this); + m_macroMenuAction = menubar->addMenu(macroMenu); + m_macroMenuAction->setVisible(prefs->isDevelopMenuEnabled()); + menubar->addMenu(createMenuWindow()); menubar->addMenu(createMenuHelp()); diff --git a/src/GuiQt/BrainBrowserWindow.h b/src/GuiQt/BrainBrowserWindow.h index f902bf1ee..98510d1db 100644 --- a/src/GuiQt/BrainBrowserWindow.h +++ b/src/GuiQt/BrainBrowserWindow.h @@ -414,6 +414,8 @@ namespace caret { QAction* m_developerGraphicsTimingAction; QAction* m_developerExportVtkFileAction; + QAction* m_macroMenuAction; + QAction* m_overlayToolBoxAction; QAction* m_windowMenuLockWindowAspectRatioAction; diff --git a/src/GuiQt/BrainBrowserWindowToolBar.cxx b/src/GuiQt/BrainBrowserWindowToolBar.cxx index f5a7e3147..f26163690 100644 --- a/src/GuiQt/BrainBrowserWindowToolBar.cxx +++ b/src/GuiQt/BrainBrowserWindowToolBar.cxx @@ -2063,6 +2063,8 @@ BrainBrowserWindowToolBar::createOrientationWidget() else { this->orientationLeftOrLateralToolButtonAction->setIconText("L"); } + this->orientationLeftOrLateralToolButtonAction->setObjectName("ToolBar_Left_Or_LateralView"); + WuQObject::watchObjectForMacroRecording(this->orientationLeftOrLateralToolButtonAction); this->orientationRightOrMedialToolButtonAction = WuQtUtilities::createAction("R", "View from a RIGHT perspective", @@ -2075,6 +2077,8 @@ BrainBrowserWindowToolBar::createOrientationWidget() else { this->orientationRightOrMedialToolButtonAction->setIconText("R"); } + this->orientationRightOrMedialToolButtonAction->setObjectName("ToolBar_Right_Or_Medial_View"); + WuQObject::watchObjectForMacroRecording(this->orientationRightOrMedialToolButtonAction); this->orientationAnteriorToolButtonAction = WuQtUtilities::createAction("A", "View from an ANTERIOR perspective", @@ -2087,6 +2091,8 @@ BrainBrowserWindowToolBar::createOrientationWidget() else { this->orientationAnteriorToolButtonAction->setIconText("A"); } + this->orientationAnteriorToolButtonAction->setObjectName("ToolBar_Anterior_View"); + WuQObject::watchObjectForMacroRecording(this->orientationAnteriorToolButtonAction); this->orientationPosteriorToolButtonAction = WuQtUtilities::createAction("P", "View from a POSTERIOR perspective", @@ -2099,6 +2105,8 @@ BrainBrowserWindowToolBar::createOrientationWidget() else { this->orientationPosteriorToolButtonAction->setIconText("P"); } + this->orientationPosteriorToolButtonAction->setObjectName("ToolBar_Posterior_View"); + WuQObject::watchObjectForMacroRecording(this->orientationPosteriorToolButtonAction); this->orientationDorsalToolButtonAction = WuQtUtilities::createAction("D", "View from a DORSAL perspective", @@ -2111,6 +2119,8 @@ BrainBrowserWindowToolBar::createOrientationWidget() else { this->orientationDorsalToolButtonAction->setIconText("D"); } + this->orientationDorsalToolButtonAction->setObjectName("ToolBar_Dorsal_View"); + WuQObject::watchObjectForMacroRecording(this->orientationDorsalToolButtonAction); this->orientationVentralToolButtonAction = WuQtUtilities::createAction("V", "View from a VENTRAL perspective", @@ -2123,6 +2133,8 @@ BrainBrowserWindowToolBar::createOrientationWidget() else { this->orientationVentralToolButtonAction->setIconText("V"); } + this->orientationVentralToolButtonAction->setObjectName("ToolBar_Ventral_View"); + WuQObject::watchObjectForMacroRecording(this->orientationVentralToolButtonAction); this->orientationLateralMedialToolButtonAction = WuQtUtilities::createAction("LM", @@ -2130,24 +2142,32 @@ BrainBrowserWindowToolBar::createOrientationWidget() this, this, SLOT(orientationLateralMedialToolButtonTriggered(bool))); + this->orientationLateralMedialToolButtonAction->setObjectName("ToolBar_Lateral_Medial_View"); + WuQObject::watchObjectForMacroRecording(this->orientationLateralMedialToolButtonAction); this->orientationDorsalVentralToolButtonAction = WuQtUtilities::createAction("DV", "View from a Dorsal/Ventral perspective", this, this, SLOT(orientationDorsalVentralToolButtonTriggered(bool))); + this->orientationDorsalVentralToolButtonAction->setObjectName("ToolBar_Dorsal_Ventral_View"); + WuQObject::watchObjectForMacroRecording(this->orientationDorsalVentralToolButtonAction); this->orientationAnteriorPosteriorToolButtonAction = WuQtUtilities::createAction("AP", "View from a Anterior/Posterior perspective", this, this, SLOT(orientationAnteriorPosteriorToolButtonTriggered(bool))); + this->orientationAnteriorPosteriorToolButtonAction->setObjectName("ToolBar_Anterior_Posterior_View"); + WuQObject::watchObjectForMacroRecording(this->orientationAnteriorPosteriorToolButtonAction); this->orientationResetToolButtonAction = WuQtUtilities::createAction("R\nE\nS\nE\nT", "Reset the view to dorsal and remove any panning or zooming", this, this, SLOT(orientationResetToolButtonTriggered(bool))); + this->orientationResetToolButtonAction->setObjectName("ToolBar_Reset_View"); + WuQObject::watchObjectForMacroRecording(this->orientationResetToolButtonAction); this->orientationLeftOrLateralToolButton = new QToolButton(); this->orientationLeftOrLateralToolButton->setDefaultAction(this->orientationLeftOrLateralToolButtonAction); @@ -2156,6 +2176,7 @@ BrainBrowserWindowToolBar::createOrientationWidget() this->orientationRightOrMedialToolButton = new QToolButton(); this->orientationRightOrMedialToolButton->setDefaultAction(this->orientationRightOrMedialToolButtonAction); WuQtUtilities::setToolButtonStyleForQt5Mac(this->orientationRightOrMedialToolButton); + orientationRightOrMedialToolButtonAction->setParent(orientationRightOrMedialToolButton); this->orientationAnteriorToolButton = new QToolButton(); this->orientationAnteriorToolButton->setDefaultAction(this->orientationAnteriorToolButtonAction); @@ -2176,14 +2197,17 @@ BrainBrowserWindowToolBar::createOrientationWidget() this->orientationLateralMedialToolButton = new QToolButton(); this->orientationLateralMedialToolButton->setDefaultAction(this->orientationLateralMedialToolButtonAction); WuQtUtilities::setToolButtonStyleForQt5Mac(this->orientationLateralMedialToolButton); + orientationLateralMedialToolButtonAction->setParent(orientationLateralMedialToolButton); this->orientationDorsalVentralToolButton = new QToolButton(); this->orientationDorsalVentralToolButton->setDefaultAction(this->orientationDorsalVentralToolButtonAction); WuQtUtilities::setToolButtonStyleForQt5Mac(this->orientationDorsalVentralToolButton); + orientationDorsalVentralToolButtonAction->setParent(orientationDorsalVentralToolButton); this->orientationAnteriorPosteriorToolButton = new QToolButton(); this->orientationAnteriorPosteriorToolButton->setDefaultAction(this->orientationAnteriorPosteriorToolButtonAction); WuQtUtilities::setToolButtonStyleForQt5Mac(this->orientationAnteriorPosteriorToolButton); + orientationAnteriorPosteriorToolButtonAction->setParent(orientationAnteriorPosteriorToolButton); WuQtUtilities::matchWidgetWidths(this->orientationLateralMedialToolButton, this->orientationDorsalVentralToolButton, diff --git a/src/GuiQt/BrainBrowserWindowToolBarSlicePlane.cxx b/src/GuiQt/BrainBrowserWindowToolBarSlicePlane.cxx index 0561175cb..0d441ae7b 100644 --- a/src/GuiQt/BrainBrowserWindowToolBarSlicePlane.cxx +++ b/src/GuiQt/BrainBrowserWindowToolBarSlicePlane.cxx @@ -34,6 +34,7 @@ #include "BrowserTabContent.h" #include "CaretAssert.h" #include "CaretLogger.h" +#include "MacroPrototype.h" #include "WuQWidgetObjectGroup.h" #include "WuQtUtilities.h" @@ -77,6 +78,8 @@ m_parentToolBar(parentToolBar) if (parasagittalIconValid) { m_volumePlaneParasagittalToolButtonAction->setIcon(parasagittalIcon); } + m_volumePlaneParasagittalToolButtonAction->setObjectName("ToolBar_Volume_Parasagittal_Slice_View"); + WuQObject::watchObjectForMacroRecording(m_volumePlaneParasagittalToolButtonAction); m_volumePlaneCoronalToolButtonAction = WuQtUtilities::createAction(VolumeSliceViewPlaneEnum::toGuiNameAbbreviation(VolumeSliceViewPlaneEnum::CORONAL), "View the CORONAL slice", @@ -85,6 +88,8 @@ m_parentToolBar(parentToolBar) if (coronalIconValid) { m_volumePlaneCoronalToolButtonAction->setIcon(coronalIcon); } + m_volumePlaneCoronalToolButtonAction->setObjectName("ToolBar_Volume_Coronal_Slice_View"); + WuQObject::watchObjectForMacroRecording(m_volumePlaneCoronalToolButtonAction); m_volumePlaneAxialToolButtonAction = WuQtUtilities::createAction(VolumeSliceViewPlaneEnum::toGuiNameAbbreviation(VolumeSliceViewPlaneEnum::AXIAL), "View the AXIAL slice", @@ -93,6 +98,8 @@ m_parentToolBar(parentToolBar) if (axialIconValid) { m_volumePlaneAxialToolButtonAction->setIcon(axialIcon); } + m_volumePlaneAxialToolButtonAction->setObjectName("ToolBar_Volume_Axial_Slice_View"); + WuQObject::watchObjectForMacroRecording(m_volumePlaneAxialToolButtonAction); m_volumePlaneAllToolButtonAction = WuQtUtilities::createAction(VolumeSliceViewPlaneEnum::toGuiNameAbbreviation(VolumeSliceViewPlaneEnum::ALL), "View the PARASAGITTAL, CORONAL, and AXIAL slices\n" @@ -100,6 +107,8 @@ m_parentToolBar(parentToolBar) this); m_volumePlaneAllToolButtonAction->setCheckable(true); m_volumePlaneAllToolButtonAction->setMenu(createViewAllSlicesLayoutMenu()); + m_volumePlaneAllToolButtonAction->setObjectName("ToolBar_Volume_All_Slices_View"); + WuQObject::watchObjectForMacroRecording(m_volumePlaneAllToolButtonAction); m_volumePlaneActionGroup = new QActionGroup(this); @@ -122,18 +131,22 @@ m_parentToolBar(parentToolBar) QToolButton* volumePlaneParasagittalToolButton = new QToolButton(); volumePlaneParasagittalToolButton->setDefaultAction(m_volumePlaneParasagittalToolButtonAction); WuQtUtilities::setToolButtonStyleForQt5Mac(volumePlaneParasagittalToolButton); + m_volumePlaneParasagittalToolButtonAction->setParent(volumePlaneParasagittalToolButton); QToolButton* volumePlaneCoronalToolButton = new QToolButton(); volumePlaneCoronalToolButton->setDefaultAction(m_volumePlaneCoronalToolButtonAction); WuQtUtilities::setToolButtonStyleForQt5Mac(volumePlaneCoronalToolButton); + m_volumePlaneCoronalToolButtonAction->setParent(volumePlaneCoronalToolButton); QToolButton* volumePlaneAxialToolButton = new QToolButton(); volumePlaneAxialToolButton->setDefaultAction(m_volumePlaneAxialToolButtonAction); WuQtUtilities::setToolButtonStyleForQt5Mac(volumePlaneAxialToolButton); + m_volumePlaneAxialToolButtonAction->setParent(volumePlaneAxialToolButton); QToolButton* volumePlaneAllToolButton = new QToolButton(); volumePlaneAllToolButton->setDefaultAction(m_volumePlaneAllToolButtonAction); WuQtUtilities::setToolButtonStyleForQt5Mac(volumePlaneAllToolButton); + m_volumePlaneAllToolButtonAction->setParent(volumePlaneAllToolButton); QToolButton* volumePlaneResetToolButton = new QToolButton(); volumePlaneResetToolButton->setDefaultAction(m_volumePlaneResetToolButtonAction); diff --git a/src/GuiQt/BrainBrowserWindowToolBarSliceSelection.cxx b/src/GuiQt/BrainBrowserWindowToolBarSliceSelection.cxx index 91f2643c7..2d3cfd58a 100644 --- a/src/GuiQt/BrainBrowserWindowToolBarSliceSelection.cxx +++ b/src/GuiQt/BrainBrowserWindowToolBarSliceSelection.cxx @@ -43,6 +43,7 @@ #include "EventManager.h" #include "EventUpdateVolumeEditingToolBar.h" #include "GuiManager.h" +#include "MacroPrototype.h" #include "ModelVolume.h" #include "ModelWholeBrain.h" #include "VolumeFile.h" @@ -78,6 +79,9 @@ m_parentToolBar(parentToolBar) QToolButton* volumeIndicesOriginToolButton = new QToolButton; volumeIndicesOriginToolButton->setDefaultAction(volumeIndicesOriginToolButtonAction); WuQtUtilities::setToolButtonStyleForQt5Mac(volumeIndicesOriginToolButton); + volumeIndicesOriginToolButtonAction->setObjectName("ToolBar_Move_Volume_Slices_to_Origin"); + volumeIndicesOriginToolButtonAction->setParent(volumeIndicesOriginToolButton); + WuQObject::watchObjectForMacroRecording(volumeIndicesOriginToolButtonAction); QLabel* parasagittalLabel = new QLabel("P:"); QLabel* coronalLabel = new QLabel("C:"); @@ -109,21 +113,30 @@ m_parentToolBar(parentToolBar) m_volumeIndicesParasagittalSpinBox->setFixedWidth(sliceIndexSpinBoxWidth); WuQtUtilities::setToolTipAndStatusTip(m_volumeIndicesParasagittalSpinBox, "Change the selected PARASAGITTAL slice"); - QObject::connect(m_volumeIndicesParasagittalSpinBox, SIGNAL(valueChanged(int)), +// QObject::connect(m_volumeIndicesParasagittalSpinBox, SIGNAL(valueChanged(int)), +// this, SLOT(volumeIndicesParasagittalSpinBoxValueChanged(int))); + m_volumeIndicesParasagittalSpinBox->setObjectName("ToolBar_Volume_Parasagittal_Slice_Index"); + WuQObject::connect(m_volumeIndicesParasagittalSpinBox, SIGNAL(valueChanged(int)), this, SLOT(volumeIndicesParasagittalSpinBoxValueChanged(int))); m_volumeIndicesCoronalSpinBox = WuQFactory::newSpinBox(); m_volumeIndicesCoronalSpinBox->setFixedWidth(sliceIndexSpinBoxWidth); WuQtUtilities::setToolTipAndStatusTip(m_volumeIndicesCoronalSpinBox, "Change the selected CORONAL slice"); - QObject::connect(m_volumeIndicesCoronalSpinBox, SIGNAL(valueChanged(int)), +// QObject::connect(m_volumeIndicesCoronalSpinBox, SIGNAL(valueChanged(int)), +// this, SLOT(volumeIndicesCoronalSpinBoxValueChanged(int))); + m_volumeIndicesCoronalSpinBox->setObjectName("ToolBar_Volume_Coronal_Slice_Index"); + WuQObject::connect(m_volumeIndicesCoronalSpinBox, SIGNAL(valueChanged(int)), this, SLOT(volumeIndicesCoronalSpinBoxValueChanged(int))); m_volumeIndicesAxialSpinBox = WuQFactory::newSpinBox(); m_volumeIndicesAxialSpinBox->setFixedWidth(sliceIndexSpinBoxWidth); WuQtUtilities::setToolTipAndStatusTip(m_volumeIndicesAxialSpinBox, "Change the selected AXIAL slice"); - QObject::connect(m_volumeIndicesAxialSpinBox, SIGNAL(valueChanged(int)), +// QObject::connect(m_volumeIndicesAxialSpinBox, SIGNAL(valueChanged(int)), +// this, SLOT(volumeIndicesAxialSpinBoxValueChanged(int))); + m_volumeIndicesAxialSpinBox->setObjectName("ToolBar_Volume_Axial_Slice_Index"); + WuQObject::connect(m_volumeIndicesAxialSpinBox, SIGNAL(valueChanged(int)), this, SLOT(volumeIndicesAxialSpinBoxValueChanged(int))); m_volumeIndicesXcoordSpinBox = WuQFactory::newDoubleSpinBox(); @@ -131,7 +144,10 @@ m_parentToolBar(parentToolBar) m_volumeIndicesXcoordSpinBox->setFixedWidth(sliceCoordinateSpinBoxWidth); WuQtUtilities::setToolTipAndStatusTip(m_volumeIndicesXcoordSpinBox, "Adjust coordinate to select PARASAGITTAL slice"); - QObject::connect(m_volumeIndicesXcoordSpinBox, SIGNAL(valueChanged(double)), +// QObject::connect(m_volumeIndicesXcoordSpinBox, SIGNAL(valueChanged(double)), +// this, SLOT(volumeIndicesXcoordSpinBoxValueChanged(double))); + m_volumeIndicesXcoordSpinBox->setObjectName("ToolBar_Volume_Parasagittal_Coordinate"); + WuQObject::connect(m_volumeIndicesXcoordSpinBox, SIGNAL(valueChanged(double)), this, SLOT(volumeIndicesXcoordSpinBoxValueChanged(double))); m_volumeIndicesYcoordSpinBox = WuQFactory::newDoubleSpinBox(); @@ -139,7 +155,10 @@ m_parentToolBar(parentToolBar) m_volumeIndicesYcoordSpinBox->setFixedWidth(sliceCoordinateSpinBoxWidth); WuQtUtilities::setToolTipAndStatusTip(m_volumeIndicesYcoordSpinBox, "Adjust coordinate to select CORONAL slice"); - QObject::connect(m_volumeIndicesYcoordSpinBox, SIGNAL(valueChanged(double)), +// QObject::connect(m_volumeIndicesYcoordSpinBox, SIGNAL(valueChanged(double)), +// this, SLOT(volumeIndicesYcoordSpinBoxValueChanged(double))); + m_volumeIndicesYcoordSpinBox->setObjectName("ToolBar_Volume_Coronal_Coordinate"); + WuQObject::connect(m_volumeIndicesYcoordSpinBox, SIGNAL(valueChanged(double)), this, SLOT(volumeIndicesYcoordSpinBoxValueChanged(double))); m_volumeIndicesZcoordSpinBox = WuQFactory::newDoubleSpinBox(); @@ -147,7 +166,10 @@ m_parentToolBar(parentToolBar) m_volumeIndicesZcoordSpinBox->setFixedWidth(sliceCoordinateSpinBoxWidth); WuQtUtilities::setToolTipAndStatusTip(m_volumeIndicesZcoordSpinBox, "Adjust coordinate to select AXIAL slice"); - QObject::connect(m_volumeIndicesZcoordSpinBox, SIGNAL(valueChanged(double)), +// QObject::connect(m_volumeIndicesZcoordSpinBox, SIGNAL(valueChanged(double)), +// this, SLOT(volumeIndicesZcoordSpinBoxValueChanged(double))); + m_volumeIndicesZcoordSpinBox->setObjectName("ToolBar_Volume_Axial_Coordinate"); + WuQObject::connect(m_volumeIndicesZcoordSpinBox, SIGNAL(valueChanged(double)), this, SLOT(volumeIndicesZcoordSpinBoxValueChanged(double))); const AString idToolTipText = ("When selected: If there is an identification operation " diff --git a/src/GuiQt/BrainOpenGLWidget.cxx b/src/GuiQt/BrainOpenGLWidget.cxx index 64ce2b086..0950915bd 100644 --- a/src/GuiQt/BrainOpenGLWidget.cxx +++ b/src/GuiQt/BrainOpenGLWidget.cxx @@ -108,6 +108,8 @@ BrainOpenGLWidget::BrainOpenGLWidget(QWidget* parent, #endif windowIndex(windowIndex) { + setObjectName("m_openGLWidget"); + this->borderBeingDrawn = new Border(); m_mousePositionValid = false; @@ -863,6 +865,8 @@ BrainOpenGLWidget::keyReleaseEvent(QKeyEvent* e) void BrainOpenGLWidget::mousePressEvent(QMouseEvent* me) { + MacroManager::get()->recordMouseEvent(this, me); + Qt::MouseButton button = me->button(); Qt::KeyboardModifiers keyModifiers = me->modifiers(); Qt::MouseButtons mouseButtons = me->buttons(); @@ -948,6 +952,8 @@ BrainOpenGLWidget::mousePressEvent(QMouseEvent* me) void BrainOpenGLWidget::mouseReleaseEvent(QMouseEvent* me) { + MacroManager::get()->recordMouseEvent(this, me); + Qt::MouseButton button = me->button(); Qt::KeyboardModifiers keyModifiers = me->modifiers(); Qt::MouseButtons mouseButtons = me->buttons(); @@ -1043,6 +1049,8 @@ BrainOpenGLWidget::mouseReleaseEvent(QMouseEvent* me) void BrainOpenGLWidget::mouseDoubleClickEvent(QMouseEvent* me) { + MacroManager::get()->recordMouseEvent(this, me); + Qt::MouseButton button = me->button(); Qt::KeyboardModifiers keyModifiers = me->modifiers(); Qt::MouseButtons mouseButtons = me->buttons(); @@ -1385,6 +1393,8 @@ BrainOpenGLWidget::performProjection(const int x, void BrainOpenGLWidget::mouseMoveEvent(QMouseEvent* me) { + MacroManager::get()->recordMouseEvent(this, me); + /* * Tooltip will remain displayed for several seconds. * So if the user moves the mouse, remove the tooltip. @@ -1850,3 +1860,36 @@ BrainOpenGLWidget::initializeDefaultGLFormat() s_defaultGLFormatInitialized = true; } + +QSize +BrainOpenGLWidget::getSizeOfWidget() const +{ + return size(); +} + + +void +BrainOpenGLWidget::processMouseEvent(QMouseEvent* me) +{ + m_mousePositionValid = true; + switch (me->type()) { + case QEvent::MouseButtonDblClick: + mouseDoubleClickEvent(me); + break; + case QEvent::MouseButtonPress: + mousePressEvent(me); + break; + case QEvent::MouseButtonRelease: + mouseReleaseEvent(me); + break; + case QEvent::MouseMove: + mouseMoveEvent(me); + break; + default: + CaretAssert(0); + break; + } + m_mousePositionValid = false; +} + + diff --git a/src/GuiQt/BrainOpenGLWidget.h b/src/GuiQt/BrainOpenGLWidget.h index 730a449b5..bc0025e76 100644 --- a/src/GuiQt/BrainOpenGLWidget.h +++ b/src/GuiQt/BrainOpenGLWidget.h @@ -45,6 +45,7 @@ #include "BrainOpenGLWindowContent.h" #include "CaretPointer.h" #include "EventListenerInterface.h" +#include "MacroPrototype.h" class QMouseEvent; class QWidget; @@ -73,7 +74,7 @@ namespace caret { #ifdef WORKBENCH_USE_QT5_QOPENGL_WIDGET class BrainOpenGLWidget : public QOpenGLWidget, public EventListenerInterface { #else - class BrainOpenGLWidget : public QGLWidget, public EventListenerInterface { + class BrainOpenGLWidget : public QGLWidget, public EventListenerInterface, public MacroEventMouseInfoWidgetInterface { #endif Q_OBJECT @@ -116,6 +117,10 @@ namespace caret { QImage performOffScreenImageCapture(const int32_t imageWidth, const int32_t imageHeight); + virtual void processMouseEvent(QMouseEvent*) override; + + virtual QSize getSizeOfWidget() const override; + protected: virtual void initializeGL(); diff --git a/src/GuiQt/CMakeLists.txt b/src/GuiQt/CMakeLists.txt index fee2e32d8..d163b5445 100644 --- a/src/GuiQt/CMakeLists.txt +++ b/src/GuiQt/CMakeLists.txt @@ -138,6 +138,7 @@ ELSE() LockAspectWarningDialog.h MacApplication.h MacDockMenu.h + MacroPrototype.h MapSettingsChartTwoLineHistoryWidget.h MapSettingsColorBarPaletteOptionsWidget.h MapSettingsColorBarWidget.h @@ -371,6 +372,7 @@ LabelSelectionViewController.h LockAspectWarningDialog.h MacApplication.h MacDockMenu.h +MacroPrototype.h MapSettingsChartTwoLineHistoryWidget.h MapSettingsColorBarPaletteOptionsWidget.h MapSettingsColorBarWidget.h @@ -604,6 +606,7 @@ LabelSelectionViewController.cxx LockAspectWarningDialog.cxx MacApplication.cxx MacDockMenu.cxx +MacroPrototype.cxx MapSettingsChartTwoLineHistoryWidget.cxx MapSettingsColorBarPaletteOptionsWidget.cxx MapSettingsColorBarWidget.cxx diff --git a/src/GuiQt/MacroPrototype.cxx b/src/GuiQt/MacroPrototype.cxx new file mode 100644 index 000000000..2032a7f38 --- /dev/null +++ b/src/GuiQt/MacroPrototype.cxx @@ -0,0 +1,1379 @@ + +/*LICENSE_START*/ +/* + * Copyright (C) 2018 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + +#include + +#define __WU_Q_OBJECT_DECLARE__ +#include "MacroPrototype.h" +#undef __WU_Q_OBJECT_DECLARE__ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "CaretAssert.h" +#include "CaretLogger.h" +#include "EventUserInterfaceUpdate.h" +#include "EventManager.h" +#include "WuQMessageBox.h" +#include "WuQtUtilities.h" + +using namespace caret; + + + +/** + * \class caret::WuQObject + * \brief + * \ingroup GuiQt + * + * + */ + +/** + * Constructor. + */ +WuQObject::WuQObject() +: QObject() +{ + +} + +/** + * Destructor. + */ +WuQObject::~WuQObject() +{ +} + +QMetaObject::Connection +WuQObject::connect(QObject *sender, + const char *signal, + const QObject *receiver, + const char *method, + Qt::ConnectionType type) +{ + CaretAssert(sender); + const QString name = sender->objectName(); + if (name.isEmpty()) { + CaretLogWarning("Object name is empty, will be ignored for macros"); + } + else { + + auto existingWatcher = s_widgetWatchers.find(name); + if (existingWatcher != s_widgetWatchers.end()) { + CaretLogWarning("Widget named \"" + + name + + "\" has already been connected for macros"); + } + else { + AString errorMessage; + WidgetWatcher* widgetWatcher = WidgetWatcher::newInstance(sender, + signal, + errorMessage); + if (widgetWatcher != NULL) { + s_widgetWatchers.insert(std::make_pair(name, + widgetWatcher)); + } + else { + CaretLogWarning(errorMessage); + } + } + } + + QMetaObject::Connection connection = QObject::connect(sender, + signal, + receiver, + method, + type); + const bool validConnection(connection); + CaretAssert(validConnection); + + return connection; +} + +void +WuQObject::watchObjectForMacroRecording(QObject* sender) +{ + CaretAssert(sender); + + QAction* action = qobject_cast(sender); + if (action != NULL) { + watchObjectForMacroRecordingPrivate(action, + SIGNAL(triggered(bool))); + return; + } + + QComboBox* comboBox = qobject_cast(sender); + if (comboBox != NULL) { + watchObjectForMacroRecordingPrivate(comboBox, + SIGNAL(activated(int))); + return; + } + + CaretLogWarning("Widget type with name " + + sender->objectName() + + " of class " + + QString(sender->metaObject()->className()) + + " not supported in watchObjectForMacroRecording()"); +} + +void +WuQObject::watchObjectForMacroRecordingPrivate(QObject* sender, + const char* signal) +{ + CaretAssert(sender); + const QString name = sender->objectName(); + if (name.isEmpty()) { + CaretLogWarning("Object name is empty, will be ignored for macros"); + } + else { + + auto existingWatcher = s_widgetWatchers.find(name); + if (existingWatcher != s_widgetWatchers.end()) { + CaretLogWarning("Widget named \"" + + name + + "\" has already been connected for macros"); + } + else { + AString errorMessage; + WidgetWatcher* widgetWatcher = WidgetWatcher::newInstance(sender, + signal, + errorMessage); + if (widgetWatcher != NULL) { + s_widgetWatchers.insert(std::make_pair(name, + widgetWatcher)); + } + else { + CaretLogWarning(errorMessage); + } + } + } +} + +//QMetaObject::Connection +//WuQObject::connectFP(QObject *sender, +// QObjectBoolPointerToMemberFunction signal, +// QObject* receiver, +// QObjectBoolPointerToMemberFunction method, +// Qt::ConnectionType type) +//{ +//// QMetaObject::Connection connection; +// QMetaObject::Connection connection = QObject::connect(sender, +// signal, +// receiver, +// method, +// type); +// const bool validConnection(connection); +// CaretAssert(validConnection); +// +// return connection; +// +//} + +/* ========================================================================= */ + +/** + * @return Create and return the macro menu. + */ +MacroMenu::MacroMenu(QMainWindow* mainWindowParent) +: QMenu(mainWindowParent), +m_mainWindowParent(mainWindowParent) +{ + CaretAssert(m_mainWindowParent); + + m_macroEraseAction = WuQtUtilities::createAction("Erase", + "Erase the Macro", + this, + this, + SLOT(processMacroErase())); + + m_macroRecordAction = WuQtUtilities::createAction("Record", + "Record a Macro", + this, + this, + SLOT(processMacroRecord())); + m_macroRecordAction->setCheckable(true); + + m_macroRunAction = WuQtUtilities::createAction("Run", + "Run a Macro", + this, + this, + SLOT(processMacroRun())); + + m_macroLoadAction = WuQtUtilities::createAction("Load from File...", + "Load a Macro", + this, + this, + SLOT(processMacroLoad())); + + m_macroSaveAction = WuQtUtilities::createAction("Save to File...", + "Save a Macro", + this, + this, + SLOT(processMacroSave())); + + setTitle("Macro"); + QObject::connect(this, SIGNAL(aboutToShow()), + this, SLOT(macroMenuAboutToShow())); + addAction(m_macroEraseAction); + addAction(m_macroRecordAction); + addAction(m_macroRunAction); + addSeparator(); + addAction(m_macroLoadAction); + addAction(m_macroSaveAction); +} + +/** + * Called when macro menu is about to show. + */ +void +MacroMenu::macroMenuAboutToShow() +{ + bool enableEraseFlag(false); + bool enableRecordFlag(true); + bool enableRunFlag(true); + bool recordingFlag(false); + bool saveFlag(false); + bool loadFlag(false); + switch (MacroManager::get()->getMode()) { + case MacroManager::Mode::OFF: + enableEraseFlag = true; + saveFlag = true; + loadFlag = true; + break; + case MacroManager::Mode::RECORDING: + enableRunFlag = false; + recordingFlag = true; + break; + case MacroManager::Mode::RUNNING: + enableRecordFlag = false; + break; + } + + m_macroEraseAction->setEnabled(enableEraseFlag); + m_macroRecordAction->setEnabled(enableRecordFlag); + m_macroRecordAction->setChecked(recordingFlag); + m_macroRunAction->setEnabled(enableRunFlag); + m_macroLoadAction->setEnabled(loadFlag); + m_macroSaveAction->setEnabled(saveFlag); +} + +/* + * Called to erase the current macro + */ +void +MacroMenu::processMacroErase() +{ + MacroManager::get()->eraseMacro(); +} + +/** + * Called to record a macro + */ +void +MacroMenu::processMacroRecord() +{ + switch (MacroManager::get()->getMode()) { + case MacroManager::Mode::OFF: + MacroManager::get()->setMode(MacroManager::Mode::RECORDING); + break; + case MacroManager::Mode::RECORDING: + MacroManager::get()->setMode(MacroManager::Mode::OFF); + break; + case MacroManager::Mode::RUNNING: + break; + } +} + +/** + * Called to run a macro + */ +void +MacroMenu::processMacroRun() +{ + switch (MacroManager::get()->getMode()) { + case MacroManager::Mode::OFF: + { + AString errorMessage; + if ( ! MacroManager::get()->runMacro(m_mainWindowParent, + errorMessage)) { + WuQMessageBox::errorOk(m_mainWindowParent, errorMessage); + } + } + break; + case MacroManager::Mode::RECORDING: + break; + case MacroManager::Mode::RUNNING: + MacroManager::get()->setMode(MacroManager::Mode::OFF); + break; + } +} +/** + * Called to load a macro + */ +void +MacroMenu::processMacroLoad() +{ + const QString filename = QFileDialog::getOpenFileName(m_mainWindowParent, + "Open Macro File", + "", + "Macro File (*.wb_macro)"); + if ( ! filename.isNull()) { + MacroManager::get()->readMacroFromFile(filename); + } +} + +/** + * Called to save a macro + */ +void +MacroMenu::processMacroSave() +{ + const QString filename = QFileDialog::getSaveFileName(m_mainWindowParent, + "Save Macro File", + "", + "Macro File (*.wb_macro)"); + if ( ! filename.isNull()) { + MacroManager::get()->saveMacroToFile(filename); + } +} + +/* ========================================================================= */ + +MacroEventMouseInfo::MacroEventMouseInfo() +{ + +} + +MacroEventMouseInfo::MacroEventMouseInfo(QMouseEvent* me, + const int32_t widgetWidth, + const int32_t widgetHeight) +{ + m_type = me->type(); + m_localPos = me->localPos(); + m_button = me->button(); + m_buttons = me->buttons(); + m_modifiers = me->modifiers(); + m_widgetWidth = widgetWidth; + m_widgetHeight = widgetHeight; +} + +/* + * Widget may be a different size than it was when the event was recorded + */ +QPointF +MacroEventMouseInfo::getLocalPosRescaledToSize(const QSize& widgetSize) const +{ + QPointF pointOut = m_localPos; + + const int32_t newWidth = widgetSize.width(); + const int32_t newHeight = widgetSize.height(); + + if ((newWidth != m_widgetWidth) + || (newHeight != m_widgetHeight)) { + const float normalizedWidth = (static_cast(m_localPos.x()) + / static_cast(m_widgetWidth)); + const float normalizedHeight = (static_cast(m_localPos.y()) + / static_cast(m_widgetHeight)); + + pointOut.setX(newWidth * normalizedWidth); + pointOut.setY(newHeight * normalizedHeight); + } + + return pointOut; +} + +QString +MacroEventMouseInfo::toString() const +{ + const QString separator("*"); + QString s(QString::number((int)m_type) + + separator + + QString::number(m_localPos.x()) + + separator + + QString::number(m_localPos.y()) + + separator + + QString::number((int)m_button) + + separator + + QString::number((int)m_buttons) + + separator + + QString::number((int)m_modifiers) + + separator + + QString::number(m_widgetWidth) + + separator + + QString::number(m_widgetHeight)); + + return s; +} + +bool +MacroEventMouseInfo::fromString(const QString& s) +{ + const QString separator("*"); + QStringList components = s.split(separator); + if (components.size() == 8) { + m_type = static_cast(components[0].toInt()); + m_localPos.setX(components[1].toFloat()); + m_localPos.setY(components[2].toFloat()); + m_button = static_cast(components[3].toInt()); + m_buttons = static_cast(components[4].toInt()); + m_modifiers = static_cast(components[5].toInt()); + m_widgetWidth = components[6].toInt(); + m_widgetHeight = components[7].toInt(); + } + else { + CaretLogWarning("Invalid value string for mouse info: " + + s); + return false; + } + return true; +} + +/* ========================================================================= */ + +MacroEvent::MacroEvent(const WidgetWatcherType widgetType, + const QString& widgetName) +: m_widgetType(widgetType), +m_widgetName(widgetName) +{ + m_valueType = ValueType::BOOLEAN; + switch (m_widgetType) { + case ACTION: + m_valueType = ValueType::BOOLEAN; + break; + case CHECK_BOX: + m_valueType = ValueType::BOOLEAN; + break; + case COMBO_BOX: + m_valueType = ValueType::INTEGER; + break; + case DOUBLE_SPIN_BOX: + m_valueType = ValueType::DOUBLE; + break; + case INVALID: + break; + case MOUSE_EVENT: + CaretAssert(0); + break; + case SPIN_BOX: + m_valueType = ValueType::INTEGER; + break; + case TOOL_BUTTON: + m_valueType = ValueType::BOOLEAN; + break; + } +} + +MacroEvent::MacroEvent(QMouseEvent* me, + const QString& widgetName, + const int32_t widgetWidth, + const int32_t widgetHeight) +: m_widgetType(WidgetWatcherType::MOUSE_EVENT), +m_widgetName(widgetName) +{ + m_valueType = ValueType::MOUSE; + + m_mouseEventInfo = MacroEventMouseInfo(me, + widgetWidth, + widgetHeight); +} + +MacroEvent::MacroEvent() +{ + +} + +QString +MacroEvent::toString() const +{ + AString widgetTypeString("INVALID"); + switch (m_widgetType) { + case ACTION: + widgetTypeString = "ACTION"; + break; + case CHECK_BOX: + widgetTypeString = "CHECK_BOX"; + break; + case COMBO_BOX: + widgetTypeString = "COMBO_BOX"; + break; + case DOUBLE_SPIN_BOX: + widgetTypeString = "DOUBLE_SPIN_BOX"; + break; + case INVALID: + break; + case MOUSE_EVENT: + widgetTypeString = "MOUSE"; + break; + case SPIN_BOX: + widgetTypeString = "SPIN_BOX"; + break; + case TOOL_BUTTON: + widgetTypeString = "TOOL_BUTTON"; + break; + } + + AString valueString; + switch (m_valueType) { + case ValueType::BOOLEAN: + valueString = AString::fromBool(m_boolValue); + break; + case ValueType::INTEGER: + valueString = AString::number(m_intValue); + break; + case ValueType::DOUBLE: + valueString = AString::number(m_doubleValue); + break; + case ValueType::MOUSE: + valueString = m_mouseEventInfo.toString(); + break; + } + + const QString sep(":"); + QString s(m_widgetName + + sep + + widgetTypeString + + sep + + valueString); + + return s; +} + +bool +MacroEvent::fromString(const QString& s) +{ + bool validFlag = false; + const QString sep(":"); + QStringList sl(s.split(sep)); + + if (sl.length() == 3) { + validFlag = true; + const QString widgetName(sl.at(0)); + const QString widgetTypeName(sl.at(1)); + const AString valueString(sl.at(2)); + + WidgetWatcherType widgetType = WidgetWatcherType::INVALID; + if (widgetTypeName == "ACTION") { + widgetType = WidgetWatcherType::ACTION; + } + else if (widgetTypeName == "CHECK_BOX") { + widgetType = WidgetWatcherType::CHECK_BOX; + } + else if (widgetTypeName == "COMBO_BOX") { + widgetType = WidgetWatcherType::COMBO_BOX; + } + else if (widgetTypeName == "DOUBLE_SPIN_BOX") { + widgetType = WidgetWatcherType::DOUBLE_SPIN_BOX; + } + else if (widgetTypeName == "MOUSE") { + widgetType = WidgetWatcherType::MOUSE_EVENT; + } + else if (widgetTypeName == "SPIN_BOX") { + widgetType = WidgetWatcherType::SPIN_BOX; + } + else if (widgetTypeName == "TOOL_BUTTON") { + widgetType = WidgetWatcherType::TOOL_BUTTON; + } + else { + validFlag = false; + CaretLogSevere("Invalid widget type: " + widgetTypeName); + } + + if (validFlag) { + m_widgetName = widgetName; + m_widgetType = widgetType; + if (widgetType == WidgetWatcherType::MOUSE_EVENT) { + validFlag = m_mouseEventInfo.fromString(valueString); + } + else { + m_boolValue = valueString.toBool(); + m_intValue = valueString.toInt(); + m_doubleValue = valueString.toDouble(); + } + } + } + else { + CaretLogSevere("Invalid string for reading macro " + s); + } + + return validFlag; +} + +/* ========================================================================= */ + +MacroManager::MacroManager() +{ + +} + +MacroManager::~MacroManager() +{ + eraseMacro(); +} + +MacroManager* +MacroManager::get() +{ + if (s_macroManager == NULL) { + s_macroManager = new MacroManager(); + } + + CaretAssert(s_macroManager); + return s_macroManager; +} + +void +MacroManager::addActionTriggeredToMacro(const QString& name, + const bool checkedStatus) +{ + MacroEvent* macroEvent = new MacroEvent(WidgetWatcherType::ACTION, + name); + macroEvent->m_boolValue = checkedStatus; + m_macroEvents.push_back(macroEvent); +} + + +void +MacroManager::addCheckBoxClickedToMacro(const QString& name, + const bool checkedStatus) +{ + MacroEvent* macroEvent = new MacroEvent(WidgetWatcherType::CHECK_BOX, + name); + macroEvent->m_boolValue = checkedStatus; + m_macroEvents.push_back(macroEvent); +} + +void +MacroManager::addComboBoxActivatedToMacro(const QString& name, + const int index) +{ + MacroEvent* macroEvent = new MacroEvent(WidgetWatcherType::COMBO_BOX, + name); + macroEvent->m_intValue = index; + m_macroEvents.push_back(macroEvent); +} + +void +MacroManager::addSpinBoxValueChangedToMacro(const QString& name, + const int value) +{ + MacroEvent* macroEvent = new MacroEvent(WidgetWatcherType::SPIN_BOX, + name); + macroEvent->m_intValue = value; + m_macroEvents.push_back(macroEvent); +} + +void +MacroManager::addDoubleSpinBoxValueChangedToMacro(const QString& name, + const double value) +{ + MacroEvent* macroEvent = new MacroEvent(WidgetWatcherType::DOUBLE_SPIN_BOX, + name); + macroEvent->m_doubleValue = value; + m_macroEvents.push_back(macroEvent); +} + +void +MacroManager::addToolButtonClickedToMacro(const QString& name, + const bool checkedStatus) +{ + MacroEvent* macroEvent = new MacroEvent(WidgetWatcherType::TOOL_BUTTON, + name); + macroEvent->m_boolValue = checkedStatus; + m_macroEvents.push_back(macroEvent); +} + +void +MacroManager::recordMouseEvent(QWidget* widget, + QMouseEvent* me) +{ + CaretAssert(widget); + + if (isModeRecording()) { + const QString name(widget->objectName()); + if (name.isEmpty()) { + CaretLogWarning("recordMouseEvent() widget name is empty."); + return; + } + + MacroEvent* macroEvent = new MacroEvent(me, + name, + widget->width(), + widget->height()); + m_macroEvents.push_back(macroEvent); + } +} + +void +MacroManager::moveMouse(QWidget* widget, + const bool highlightFlag) +{ + CaretAssert(widget); + + const QPoint widgetCenter = widget->rect().center(); + const QPoint windowPoint = widget->mapToGlobal(widgetCenter); + + if (highlightFlag) { + const float radius = 15.0; + for (float angle = 0.0; angle < 6.28; angle += 0.314) { + const float x = windowPoint.x() + (std::cos(angle) * radius); + const float y = windowPoint.y() + (std::sin(angle) * radius); + QCursor::setPos(x, y); + SystemUtilities::sleepSeconds(0.025); + } + } + + QCursor::setPos(windowPoint); +} + + +bool +MacroManager::runMacro(QObject* window, + QString& errorMessageOut) +{ + errorMessageOut.clear(); + + switch (m_mode) { + case MacroManager::Mode::OFF: + break; + case MacroManager::Mode::RECORDING: + errorMessageOut = "Recording is on, turn off recording, then run"; + return false; + break; + case MacroManager::Mode::RUNNING: + CaretAssert(0); + break; + } + + m_mode = Mode::RUNNING; + + for (auto me : m_macroEvents) { + CaretAssert(me); + + const QString widgetName(me->m_widgetName); + QWidget* widgetToMoveMouse = window->findChild(widgetName); + + if (widgetToMoveMouse == NULL) { + QObject* object = window->findChild(widgetName); + if (object != NULL) { + QObject* parent = object->parent(); + if (parent != NULL) { + widgetToMoveMouse = qobject_cast(parent); + } + } + } + + const bool mouseEventFlag = (me->m_widgetType == WidgetWatcherType::MOUSE_EVENT); + if (widgetToMoveMouse != NULL) { + if ( ! mouseEventFlag) { + const bool highlightFlag = ( ! mouseEventFlag); + moveMouse(widgetToMoveMouse, + highlightFlag); + } + } + + switch (me->m_widgetType) { + case WidgetWatcherType::INVALID: + CaretAssert(0); + break; + case ACTION: + { + QAction* action = window->findChild(me->m_widgetName); + if (action != NULL) { + MacroActionEmitter actionUpdater(action); + actionUpdater.setActionChecked(me->m_boolValue); + } + else { + CaretLogWarning("ERROR: Unable to find action named " + + me->m_widgetName); + } + } + break; + case WidgetWatcherType::CHECK_BOX: + { + QCheckBox* checkBox = window->findChild(me->m_widgetName); + if (checkBox != NULL) { + checkBox->setChecked( ! me->m_boolValue); + checkBox->animateClick(100); // milliseconds + } + else { + CaretLogWarning("ERROR: Unable to find checkbox named " + + me->m_widgetName); + } + } + break; + case WidgetWatcherType::COMBO_BOX: + { + QComboBox* comboBox = window->findChild(me->m_widgetName); + if (comboBox != NULL) { + //comboBox->setCurrentIndex(me->m_intValue); + MacroComboBoxEmitter comboBoxUpdater(comboBox); + comboBoxUpdater.setComboBoxIndex(me->m_intValue); + } + else { + CaretLogWarning("ERROR: Unabe to find combo box named " + + me->m_widgetName); + } + } + break; + case WidgetWatcherType::DOUBLE_SPIN_BOX: + { + QList boxes = window->findChildren(me->m_widgetName); + QDoubleSpinBox* doubleSpinBox = window->findChild(me->m_widgetName); + if (doubleSpinBox != NULL) { + + MacroDoubleSpinBoxEmitter doubleSpinBoxUpdater(doubleSpinBox); + doubleSpinBoxUpdater.setDoubleSpinBoxValue(me->m_doubleValue); + } + else { + CaretLogWarning("ERROR: Unabe to find double spin box named " + + me->m_widgetName); + } + } + break; + case WidgetWatcherType::MOUSE_EVENT: + { + QWidget* widget = window->findChild(me->m_widgetName); + if (widget != NULL) { + MacroEventMouseInfoWidgetInterface* mouseInterface = dynamic_cast(widget); + if (mouseInterface != NULL) { + const QSize currentWidgetSize = mouseInterface->getSizeOfWidget(); + const MacroEventMouseInfo& memi = me->m_mouseEventInfo; + const QPointF adjustedLocalPos = memi.getLocalPosRescaledToSize(currentWidgetSize); + + QMouseEvent mouseEvent(memi.m_type, + adjustedLocalPos, //memi.m_localPos, + memi.m_button, + memi.m_buttons, + memi.m_modifiers); + mouseInterface->processMouseEvent(&mouseEvent); + +// std::cout << "Original x/y: " << memi.m_localPos.x() +// << ", " << memi.m_localPos.y() << std::endl; +// std::cout << " Updated x/y: " << adjustedLocalPos.x() +// << ", " << adjustedLocalPos.y() << std::endl; + +// QEvent* event = &mouseEvent; +// widget->event(event); +// switch (memi.m_type) { +// case QEvent::MouseButtonDblClick: +// break; +// case QEvent::MouseButtonPress: +// break; +// case QEvent::MouseButtonRelease: +// break; +// case QEvent::MouseMove: +// break; +// default: +// CaretAssert(0); +// break; +// } + } + else { + CaretLogWarning("ERROR: widget named " + + me->m_widgetName + + " does not cast to MacroEventMouseInfoWidgetInterface"); + } + } + else { + CaretLogWarning("ERROR: Unabe to widget named " + + me->m_widgetName); + } + } + break; + case WidgetWatcherType::SPIN_BOX: + { + QSpinBox* spinBox = window->findChild(me->m_widgetName); + if (spinBox != NULL) { + /* + * Calling QSpinBox::setValue() results in emission of + * the QSpinBox::valueChanged() signal. + */ + spinBox->setValue(me->m_intValue); + } + else { + CaretLogWarning("ERROR: Unabe to find spin box named " + + me->m_widgetName); + } + } + break; + case WidgetWatcherType::TOOL_BUTTON: + { + QToolButton* toolButton = window->findChild(me->m_widgetName); + if (toolButton != NULL) { + toolButton->setChecked( ! me->m_boolValue); + toolButton->animateClick(1000); // 1000 milliseconds + } + else { + CaretLogWarning("ERROR: Unable to find toolbutton named " + + me->m_widgetName); + } + } + break; + } + + QGuiApplication::processEvents(); + if ( ! mouseEventFlag) { + SystemUtilities::sleepSeconds(1); + } + QGuiApplication::processEvents(); + } + + m_mode = Mode::OFF; + + return (errorMessageOut.isEmpty()); +} + +void +MacroManager::eraseMacro() +{ + for (auto me : m_macroEvents) { + CaretAssert(me); + delete me; + } + m_macroEvents.clear(); +} + + +MacroManager::Mode +MacroManager::getMode() const +{ + return m_mode; +} + +bool +MacroManager::isModeRecording() const +{ + switch (m_mode) { + case Mode::OFF: + break; + case Mode::RECORDING: + return true; + break; + case Mode::RUNNING: + break; + } + + return false; +} + +void +MacroManager::setMode(const Mode mode) +{ + m_mode = mode; +} + +void +MacroManager::saveMacroToFile(const AString& filename) +{ + QFile file(filename); + if (file.open(QFile::WriteOnly)) { + QTextStream stream(&file); + for (auto me : m_macroEvents) { + stream << me->toString() << "\n"; + } + file.close(); + } + else { + CaretLogSevere("Unable to open for writing: " + + filename); + } +} + +void +MacroManager::readMacroFromFile(const AString& filename) +{ + eraseMacro(); + + QFile file(filename); + if (file.open(QFile::ReadOnly)) { + QTextStream stream(&file); + + QString fileText(stream.readAll()); + QStringListIterator lineIter(fileText.split("\n")); + while (lineIter.hasNext()) { + const QString text = lineIter.next().trimmed(); + if ( ! text.isEmpty()) { + MacroEvent* me = new MacroEvent(); + const bool valid = me->fromString(text); + if (valid) { + m_macroEvents.push_back(me); + } + else { + CaretLogWarning("Invalid line in macro file: " + + text); + delete me; + } + } + } + file.close(); + } + else { + CaretLogSevere("Unable to open for writing: " + + filename); + } +} + +/* ========================================================================= */ + +MacroActionEmitter::MacroActionEmitter(QAction* action) +: m_action(action) +{ + CaretAssert(m_action); + /* + * Need to cause emission of activated signale + */ + QObject::connect(this, SIGNAL(actionCheckedSignal(bool)), + m_action, SIGNAL(triggered(bool))); +} + +void +MacroActionEmitter::setActionChecked(bool checked) +{ +// QSignalBlocker blocker(m_action); + m_action->setChecked(checked); + emit actionCheckedSignal(checked); +} + + +MacroComboBoxEmitter::MacroComboBoxEmitter(QComboBox* comboBox) +: m_comboBox(comboBox) +{ + CaretAssert(comboBox); + + /* + * Need to cause emission of activated signale + */ + QObject::connect(this, SIGNAL(valueChangedSignal(int)), + m_comboBox, SIGNAL(activated(int))); +} + +void +MacroComboBoxEmitter::setComboBoxIndex(const int index) +{ + m_comboBox->showPopup(); + m_comboBox->setCurrentIndex(index); + m_comboBox->hidePopup(); + emit valueChangedSignal(index); +} + + +//MacroSpinBoxEmitter::MacroSpinBoxEmitter(QSpinBox* spinBox) +//: m_spinBox(spinBox) +//{ +// CaretAssert(spinBox); +// +//// /* +//// * Need to cause emission of activated signale +//// */ +//// QObject::connect(this, SIGNAL(valueChangedSignal(int)), +//// m_spinBox, SIGNAL(valueChanged(int))); +//} +// +//void +//MacroSpinBoxEmitter::setSpinBoxValue(const int value) +//{ +// /* +// * With a spin box, calling setValue() results in the valueChanged() +// * signal being emitted +// */ +// m_spinBox->setValue(value); +// +//} + +MacroDoubleSpinBoxEmitter::MacroDoubleSpinBoxEmitter(QDoubleSpinBox* doubleSpinBox) +: m_doubleSpinBox(doubleSpinBox) +{ + CaretAssert(doubleSpinBox); + + QObject::connect(this, SIGNAL(valueChangedSignal(double)), + m_doubleSpinBox, SIGNAL(valueChanged(double))); +} + +void +MacroDoubleSpinBoxEmitter::setDoubleSpinBoxValue(const double value) +{ + QSignalBlocker blocker(m_doubleSpinBox); + m_doubleSpinBox->setValue(value); + blocker.unblock(); + + emit valueChangedSignal(value); +} + +/* ========================================================================= */ + +WidgetWatcher* +WidgetWatcher::unsupportedMessage(QObject* object, + const QString& signalName, + QString& errorMessageOut) +{ + errorMessageOut = ("Unsupported signal \"" + + signalName + + " for named \"" + + object->objectName() + + "\""); + + return NULL; +} + +WidgetWatcher* +WidgetWatcher::newInstance(QObject* object, + const QString& signalName, + QString& errorMessageOut) +{ + CaretAssert(object); + errorMessageOut.clear(); + + QAction* action = qobject_cast(object); + if (action != NULL) { + if (signalName.contains("triggered(bool)")) { + WidgetWatcher* ww = new WidgetWatcher(WidgetWatcherType::ACTION, + action); + return ww; + } + else { + return unsupportedMessage(object, + signalName, + errorMessageOut); + } + } + + QCheckBox* checkBox = qobject_cast(object); + if (checkBox != NULL) { + if (signalName.contains("clicked(bool)")) { + WidgetWatcher* ww = new WidgetWatcher(WidgetWatcherType::CHECK_BOX, + checkBox); + return ww; + } + else { + errorMessageOut = ("Unsupported signal \"" + + signalName + + " for named \"" + + object->objectName() + + "\""); + return NULL; + } + } + + QComboBox* comboBox = qobject_cast(object); + if (comboBox != NULL) { + if (signalName.contains("activated(int)")) { + WidgetWatcher* ww = new WidgetWatcher(WidgetWatcherType::COMBO_BOX, + comboBox); + return ww; + } + else { + errorMessageOut = ("Unsupported signal \"" + + signalName + + " for named \"" + + object->objectName() + + "\""); + return NULL; + } + } + + QSpinBox* spinBox = qobject_cast(object); + if (spinBox != NULL) { + if (signalName.contains("valueChanged(int)")) { + WidgetWatcher* ww = new WidgetWatcher(WidgetWatcherType::SPIN_BOX, + spinBox); + return ww; + } + else { + errorMessageOut = ("Unsupported signal \"" + + signalName + + " for named \"" + + object->objectName() + + "\""); + return NULL; + } + } + + QDoubleSpinBox* doubleSpinBox = qobject_cast(object); + if (doubleSpinBox != NULL) { + if (signalName.contains("valueChanged(double)")) { + WidgetWatcher* ww = new WidgetWatcher(WidgetWatcherType::DOUBLE_SPIN_BOX, + doubleSpinBox); + return ww; + } + else { + errorMessageOut = ("Unsupported signal \"" + + signalName + + " for named \"" + + object->objectName() + + "\""); + return NULL; + } + } + + QToolButton* toolButton = qobject_cast(object); + if (toolButton != NULL) { + if (signalName.contains("clicked(bool)")) { + WidgetWatcher* ww = new WidgetWatcher(WidgetWatcherType::TOOL_BUTTON, + toolButton); + return ww; + } + else { + errorMessageOut = ("Unsupported signal \"" + + signalName + + " for named \"" + + object->objectName() + + "\""); + return NULL; + } + } + + errorMessageOut = ("Widget named \"" + + object->objectName() + + "\" of class \"" + + object->metaObject()->className() + + "\" is not supported for macros"); + + return NULL; +} + + +WidgetWatcher::WidgetWatcher(const WidgetWatcherType widgetType, + QObject* object) +: m_widgetType(widgetType) +{ + CaretAssert(object); + m_name = object->objectName(); + + switch (m_widgetType) { + case INVALID: + break; + case ACTION: + { + QAction* action = qobject_cast(object); + CaretAssert(action); + QObject::connect(action, &QAction::triggered, + this, &WidgetWatcher::actionTriggered); + } + break; + case CHECK_BOX: + { + QCheckBox* checkBox = qobject_cast(object); + CaretAssert(checkBox); + QObject::connect(checkBox, &QCheckBox::clicked, + this, &WidgetWatcher::checkBoxClicked); + } + break; + case COMBO_BOX: + { + QComboBox* comboBox = qobject_cast(object); + CaretAssert(comboBox); + QObject::connect(comboBox, static_cast(&QComboBox::activated), + this, &WidgetWatcher::comboBoxActivated); + } + break; + case DOUBLE_SPIN_BOX: + { + QDoubleSpinBox* spinBox = qobject_cast(object); + CaretAssert(spinBox); + QObject::connect(spinBox, static_cast(&QDoubleSpinBox::valueChanged), + this, &WidgetWatcher::doubleSpinBoxValueChanged); + } + break; + case MOUSE_EVENT: + break; + case SPIN_BOX: + { + QSpinBox* spinBox = qobject_cast(object); + CaretAssert(spinBox); + QObject::connect(spinBox, static_cast(&QSpinBox::valueChanged), + this, &WidgetWatcher::spinBoxValueChanged); + } + break; + case TOOL_BUTTON: + { + QToolButton* toolButton = qobject_cast(object); + CaretAssert(toolButton); + QObject::connect(toolButton, &QCheckBox::clicked, + this, &WidgetWatcher::toolButtonClicked); + } + break; + } +} + +void +WidgetWatcher::actionTriggered(bool checked) +{ + if (MacroManager::get()->isModeRecording()) { + MacroManager::get()->addActionTriggeredToMacro(m_name, + checked); + } +} + +void +WidgetWatcher::checkBoxClicked(bool checked) +{ + if (MacroManager::get()->isModeRecording()) { + MacroManager::get()->addCheckBoxClickedToMacro(m_name, + checked); + } +} + +void +WidgetWatcher::comboBoxActivated(int index) +{ + if (MacroManager::get()->isModeRecording()) { + MacroManager::get()->addComboBoxActivatedToMacro(m_name, + index); + } +} + +void +WidgetWatcher::spinBoxValueChanged(int value) +{ + if (MacroManager::get()->isModeRecording()) { + MacroManager::get()->addSpinBoxValueChangedToMacro(m_name, + value); + } +} + +void +WidgetWatcher::doubleSpinBoxValueChanged(double value) +{ + if (MacroManager::get()->isModeRecording()) { + MacroManager::get()->addDoubleSpinBoxValueChangedToMacro(m_name, + value); + } +} + +void +WidgetWatcher::toolButtonClicked(bool checked) +{ + if (MacroManager::get()->isModeRecording()) { + MacroManager::get()->addToolButtonClickedToMacro(m_name, + checked); + } +} + + diff --git a/src/GuiQt/MacroPrototype.h b/src/GuiQt/MacroPrototype.h new file mode 100644 index 000000000..45da17878 --- /dev/null +++ b/src/GuiQt/MacroPrototype.h @@ -0,0 +1,373 @@ +#ifndef __WU_Q_OBJECT_H__ +#define __WU_Q_OBJECT_H__ + +/*LICENSE_START*/ +/* + * Copyright (C) 2018 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + + +#include +#include + +#include +#include +#include + +#include "AString.h" + +class QDoubleSpinBox; +class QComboBox; +class QMainWindow; +class QSpinBox; + +namespace caret { + + class WidgetWatcher; + + enum WidgetWatcherType { + INVALID, + ACTION, + CHECK_BOX, + COMBO_BOX, + DOUBLE_SPIN_BOX, + MOUSE_EVENT, + SPIN_BOX, + TOOL_BUTTON, + }; + + /* ========================================================================= */ + + class WuQObject : public QObject { + + Q_OBJECT + + public: + WuQObject(const WuQObject&) = delete; + + WuQObject& operator=(const WuQObject&) = delete; + + static void watchObjectForMacroRecording(QObject* sender); + + static QMetaObject::Connection connect(QObject *sender, + const char *signal, + const QObject *receiver, + const char *method, + Qt::ConnectionType type = Qt::AutoConnection); + +// typedef void (QObject::*QObjectBoolPointerToMemberFunction)(bool); +// +// static QMetaObject::Connection connectFP(QObject *sender, +// QObjectBoolPointerToMemberFunction signal, +// QObject* receiver, +// QObjectBoolPointerToMemberFunction method, +// Qt::ConnectionType type = Qt::AutoConnection); + + // ADD_NEW_METHODS_HERE + + private: + WuQObject(); + + virtual ~WuQObject(); + + static void watchObjectForMacroRecordingPrivate(QObject* sender, + const char* signal); + +// static WidgetWatcher* createWidgetWatcher(QObject* object); + + // ADD_NEW_MEMBERS_HERE + + static std::map s_widgetWatchers; + }; + + /* ========================================================================= */ + + class MacroMenu : public QMenu { + Q_OBJECT + + public: + MacroMenu(QMainWindow* mainWindowParent); + + private slots: + void macroMenuAboutToShow(); + void processMacroRecord(); + void processMacroRun(); + void processMacroErase(); + void processMacroLoad(); + void processMacroSave(); + + private: + QMainWindow* m_mainWindowParent; + QAction* m_macroEraseAction; + QAction* m_macroRecordAction; + QAction* m_macroRunAction; + QAction* m_macroLoadAction; + QAction* m_macroSaveAction; + }; + + /* ========================================================================= */ + + class MacroEventMouseInfoWidgetInterface { + public: + MacroEventMouseInfoWidgetInterface() { } + + virtual QSize getSizeOfWidget() const = 0; + + virtual void processMouseEvent(QMouseEvent*) = 0; + }; + + /* ========================================================================= */ + + class MacroEventMouseInfo { + public: + MacroEventMouseInfo(); + + MacroEventMouseInfo(QMouseEvent* me, + const int32_t widgetWidth, + const int32_t widgetHeight); + + QPointF getLocalPosRescaledToSize(const QSize& widgetSize) const; + + QString toString() const; + + bool fromString(const QString& s); + + QEvent::Type m_type; + QPointF m_localPos; + Qt::MouseButton m_button; + Qt::MouseButtons m_buttons; + Qt::KeyboardModifiers m_modifiers; + int32_t m_widgetWidth; + int32_t m_widgetHeight; + }; + + /* ========================================================================= */ + + class MacroEvent { + public: + enum class ValueType { + BOOLEAN, + DOUBLE, + INTEGER, + MOUSE + }; + + MacroEvent(const WidgetWatcherType widgetType, + const QString& widgetName); + + MacroEvent(QMouseEvent* me, + const QString& widgetName, + const int32_t widgetWidth, + const int32_t widgetHeight); + + MacroEvent(); + + QString toString() const; + + bool fromString(const QString& s); + + WidgetWatcherType m_widgetType = WidgetWatcherType::INVALID; + QString m_widgetName; + ValueType m_valueType = ValueType::INTEGER; + + bool m_boolValue = false; + int m_intValue = 0; + double m_doubleValue = 0.0; + + MacroEventMouseInfo m_mouseEventInfo; + }; + + /* ========================================================================= */ + + class MacroActionEmitter : public QObject { + Q_OBJECT + + public: + MacroActionEmitter(QAction* action); + + void setActionChecked(bool checked); + + signals: + void actionCheckedSignal(bool); + + private: + QAction* m_action; + + }; + + /* ========================================================================= */ + + class MacroComboBoxEmitter : public QObject { + Q_OBJECT + + public: + MacroComboBoxEmitter(QComboBox* comboBox); + + void setComboBoxIndex(const int index); + + signals: + void valueChangedSignal(int); + + private: + QComboBox* m_comboBox; + + }; + + /* ========================================================================= */ + +// class MacroSpinBoxEmitter : public QObject { +// Q_OBJECT +// +// public: +// MacroSpinBoxEmitter(QSpinBox* spinBox); +// +// void setSpinBoxValue(const int value); +// +//// signals: +//// int valueChangedSignal(int); +// +// private: +// QSpinBox* m_spinBox; +// +// }; + + /* ========================================================================= */ + + class MacroDoubleSpinBoxEmitter : public QObject { + Q_OBJECT + + public: + MacroDoubleSpinBoxEmitter(QDoubleSpinBox* doubleSpinBox); + + void setDoubleSpinBoxValue(const double value); + + signals: + void valueChangedSignal(double); + + private: + QDoubleSpinBox* m_doubleSpinBox; + + }; + + /* ========================================================================= */ + + class MacroManager { + public: + enum class Mode { + OFF, + RECORDING, + RUNNING + }; + + static MacroManager* get(); + + MacroManager(); + + ~MacroManager(); + + void addActionTriggeredToMacro(const QString& name, + const bool checkedStatus); + + void addCheckBoxClickedToMacro(const QString& name, + const bool checkedStatus); + + void addComboBoxActivatedToMacro(const QString& name, + const int index); + + void addSpinBoxValueChangedToMacro(const QString& name, + const int value); + + void addDoubleSpinBoxValueChangedToMacro(const QString& name, + const double value); + + void addToolButtonClickedToMacro(const QString& name, + const bool checkedStatus); + + void recordMouseEvent(QWidget* widget, + QMouseEvent* me); + + bool isModeRecording() const; + + bool runMacro(QObject* window, + QString& errorMessageOut); + + Mode getMode() const; + + void eraseMacro(); + + void setMode(const Mode mode); + + void moveMouse(QWidget* widget, + const bool highlightFlag); + + void saveMacroToFile(const AString& filename); + + void readMacroFromFile(const AString& filename); + + std::vector m_macroEvents; + + static MacroManager* s_macroManager; + + Mode m_mode = Mode::OFF; + }; + + /* ========================================================================= */ + + class WidgetWatcher : public QObject { + + Q_OBJECT + + public: + static WidgetWatcher* newInstance(QObject* object, + const QString& signalName, + QString& errorMessageOut); + + WidgetWatcher(const WidgetWatcherType widgetType, + QObject* object); + + const WidgetWatcherType m_widgetType; + QString m_name; +// QObject* m_widget = NULL; + + public slots: + void actionTriggered(bool); + + void checkBoxClicked(bool); + + void comboBoxActivated(int); + + void spinBoxValueChanged(int); + + void doubleSpinBoxValueChanged(double); + + void toolButtonClicked(bool); + + private: + static WidgetWatcher* unsupportedMessage(QObject* object, + const QString& signalName, + QString& errorMessageOut); + + }; + +#ifdef __WU_Q_OBJECT_DECLARE__ + MacroManager* MacroManager::s_macroManager = NULL; + std::map WuQObject::s_widgetWatchers; +#endif // __WU_Q_OBJECT_DECLARE__ + +} // namespace +#endif //__WU_Q_OBJECT_H__ diff --git a/src/GuiQt/MapYokingGroupComboBox.cxx b/src/GuiQt/MapYokingGroupComboBox.cxx index 7806926aa..5fc13d810 100644 --- a/src/GuiQt/MapYokingGroupComboBox.cxx +++ b/src/GuiQt/MapYokingGroupComboBox.cxx @@ -32,6 +32,7 @@ #include "EnumComboBoxTemplate.h" #include "EventManager.h" #include "EventMapYokingValidation.h" +#include "MacroPrototype.h" #include "Overlay.h" #include "WuQMessageBox.h" #include "WuQtUtilities.h" @@ -48,6 +49,29 @@ using namespace caret; * Constructor. */ MapYokingGroupComboBox::MapYokingGroupComboBox(QObject* parent) +: MapYokingGroupComboBox(parent, + "") +{ +} +//: WuQWidget(parent) +//{ +// m_comboBox = new EnumComboBoxTemplate(this); +// m_comboBox->setup(); +// m_comboBox->getWidget()->setStatusTip("Synchronize selected map indices (and selection status for overlays)"); +// m_comboBox->getWidget()->setToolTip("Synchronize selected map indices (and selection status for overlays)"); +//#ifdef CARET_OS_MACOSX +// m_comboBox->getComboBox()->setFixedWidth(m_comboBox->getComboBox()->sizeHint().width() - 20); +//#endif // CARET_OS_MACOSX +// QObject::connect(m_comboBox, SIGNAL(itemActivated()), +// this, SLOT(comboBoxActivated())); +// WuQObject::watchObjectForMacroRecording(m_comboBox); +//} + +/** + * Constructor. + */ +MapYokingGroupComboBox::MapYokingGroupComboBox(QObject* parent, + const QString& objectName) : WuQWidget(parent) { m_comboBox = new EnumComboBoxTemplate(this); @@ -59,6 +83,11 @@ MapYokingGroupComboBox::MapYokingGroupComboBox(QObject* parent) #endif // CARET_OS_MACOSX QObject::connect(m_comboBox, SIGNAL(itemActivated()), this, SLOT(comboBoxActivated())); + if ( ! objectName.isEmpty()) { + QWidget* encapsulatedComboBox = m_comboBox->getWidget(); + encapsulatedComboBox->setObjectName(objectName); + WuQObject::watchObjectForMacroRecording(encapsulatedComboBox); + } } /** diff --git a/src/GuiQt/MapYokingGroupComboBox.h b/src/GuiQt/MapYokingGroupComboBox.h index 532fb9a24..ae1e91bae 100644 --- a/src/GuiQt/MapYokingGroupComboBox.h +++ b/src/GuiQt/MapYokingGroupComboBox.h @@ -39,6 +39,9 @@ namespace caret { public: MapYokingGroupComboBox(QObject* parent); + MapYokingGroupComboBox(QObject* parent, + const QString& objectName); + virtual ~MapYokingGroupComboBox(); virtual QWidget* getWidget(); diff --git a/src/GuiQt/OverlayViewController.cxx b/src/GuiQt/OverlayViewController.cxx index 0ee46f042..4af05d9ae 100644 --- a/src/GuiQt/OverlayViewController.cxx +++ b/src/GuiQt/OverlayViewController.cxx @@ -50,6 +50,7 @@ #include "FileInformation.h" #include "FilePathNamePrefixCompactor.h" #include "GuiManager.h" +#include "MacroPrototype.h" #include "MapYokingGroupComboBox.h" #include "Overlay.h" #include "UsernamePasswordWidget.h" @@ -97,23 +98,37 @@ OverlayViewController::OverlayViewController(const Qt::Orientation orientation, } const QComboBox::SizeAdjustPolicy comboSizePolicy = QComboBox::AdjustToContentsOnFirstShow; //QComboBox::AdjustToContents; + QString objectNamePrefix("Overlay_" + + AString::number(overlayIndex + 1) + + "_" + + ((orientation == Qt::Horizontal) + ? "H_" + : "V_")); /* * Enabled Check Box */ const QString checkboxText = ((orientation == Qt::Horizontal) ? " " : " "); this->enabledCheckBox = new QCheckBox(checkboxText); - QObject::connect(this->enabledCheckBox, SIGNAL(clicked(bool)), - this, SLOT(enabledCheckBoxClicked(bool))); + this->enabledCheckBox->setObjectName(objectNamePrefix + + "OnOff_CheckBox"); +// QObject::connect(this->enabledCheckBox, SIGNAL(clicked(bool)), +// this, SLOT(enabledCheckBoxClicked(bool))); + WuQObject::connect(this->enabledCheckBox, SIGNAL(clicked(bool)), + this, SLOT(enabledCheckBoxClicked(bool))); this->enabledCheckBox->setToolTip("Enables display of this overlay"); /* - * File Selection Check Box + * File Selection Combo Box */ this->fileComboBox = WuQFactory::newComboBox(); + this->fileComboBox->setObjectName(objectNamePrefix + + "File_Selection_ComboBox"); this->fileComboBox->setMinimumWidth(minComboBoxWidth); this->fileComboBox->setMaximumWidth(maxComboBoxWidth); - QObject::connect(this->fileComboBox, SIGNAL(activated(int)), - this, SLOT(fileComboBoxSelected(int))); +// QObject::connect(this->fileComboBox, SIGNAL(activated(int)), +// this, SLOT(fileComboBoxSelected(int))); + WuQObject::connect(this->fileComboBox, SIGNAL(activated(int)), + this, SLOT(fileComboBoxSelected(int))); this->fileComboBox->setToolTip("Selects file for this overlay"); this->fileComboBox->setSizeAdjustPolicy(comboSizePolicy); @@ -121,7 +136,11 @@ OverlayViewController::OverlayViewController(const Qt::Orientation orientation, * Map Index Spin Box */ m_mapIndexSpinBox = WuQFactory::newSpinBox(); - QObject::connect(m_mapIndexSpinBox, SIGNAL(valueChanged(int)), + this->m_mapIndexSpinBox->setObjectName(objectNamePrefix + + "Map_Index_SpinBox"); +// QObject::connect(m_mapIndexSpinBox, SIGNAL(valueChanged(int)), +// this, SLOT(mapIndexSpinBoxValueChanged(int))); + WuQObject::connect(m_mapIndexSpinBox, SIGNAL(valueChanged(int)), this, SLOT(mapIndexSpinBoxValueChanged(int))); m_mapIndexSpinBox->setToolTip("Select map by its index"); @@ -129,9 +148,13 @@ OverlayViewController::OverlayViewController(const Qt::Orientation orientation, * Map Name Combo Box */ this->mapNameComboBox = WuQFactory::newComboBox(); + this->mapNameComboBox->setObjectName(objectNamePrefix + + "Map_Selection_ComboBox"); this->mapNameComboBox->setMinimumWidth(minComboBoxWidth); this->mapNameComboBox->setMaximumWidth(maxComboBoxWidth); - QObject::connect(this->mapNameComboBox, SIGNAL(activated(int)), +// QObject::connect(this->mapNameComboBox, SIGNAL(activated(int)), +// this, SLOT(mapNameComboBoxSelected(int))); + WuQObject::connect(this->mapNameComboBox, SIGNAL(activated(int)), this, SLOT(mapNameComboBoxSelected(int))); this->mapNameComboBox->setToolTip("Select map by its name"); this->mapNameComboBox->setSizeAdjustPolicy(comboSizePolicy); @@ -140,32 +163,48 @@ OverlayViewController::OverlayViewController(const Qt::Orientation orientation, * Opacity double spin box */ this->opacityDoubleSpinBox = WuQFactory::newDoubleSpinBox(); + this->opacityDoubleSpinBox->setObjectName(objectNamePrefix + + "Opacity_SpinBox"); this->opacityDoubleSpinBox->setMinimum(0.0); this->opacityDoubleSpinBox->setMaximum(1.0); this->opacityDoubleSpinBox->setSingleStep(0.10); this->opacityDoubleSpinBox->setDecimals(1); this->opacityDoubleSpinBox->setFixedWidth(50); - QObject::connect(this->opacityDoubleSpinBox, SIGNAL(valueChanged(double)), +// QObject::connect(this->opacityDoubleSpinBox, SIGNAL(valueChanged(double)), +// this, SLOT(opacityDoubleSpinBoxValueChanged(double))); + WuQObject::connect(this->opacityDoubleSpinBox, SIGNAL(valueChanged(double)), this, SLOT(opacityDoubleSpinBoxValueChanged(double))); this->opacityDoubleSpinBox->setToolTip("Opacity (0.0=transparent, 1.0=opaque)"); /* * ColorBar Tool Button */ - QIcon colorBarIcon; - const bool colorBarIconValid = WuQtUtilities::loadIcon(":/LayersPanel/colorbar.png", - colorBarIcon); - this->colorBarAction = WuQtUtilities::createAction("CB", - "Display color bar for this overlay", - this, - this, - SLOT(colorBarActionTriggered(bool))); - this->colorBarAction->setCheckable(true); - if (colorBarIconValid) { - this->colorBarAction->setIcon(colorBarIcon); - } - QToolButton* colorBarToolButton = new QToolButton(); - colorBarToolButton->setDefaultAction(this->colorBarAction); + m_colorBarToolButton = WuQtUtilities::createToolButtonWithIcon("CB", + ":/LayersPanel/colorbar.png", + "Display color bar for this overlay", + (objectNamePrefix + + "Show_Color_Bar"), + this, + SLOT(colorBarActionTriggered(bool))); + m_colorBarToolButton->setCheckable(true); + +// /* +// * ColorBar Tool Button +// */ +// QIcon colorBarIcon; +// const bool colorBarIconValid = WuQtUtilities::loadIcon(":/LayersPanel/colorbar.png", +// colorBarIcon); +// this->colorBarAction = WuQtUtilities::createAction("CB", +// "Display color bar for this overlay", +// this, +// this, +// SLOT(colorBarActionTriggered(bool))); +// this->colorBarAction->setCheckable(true); +// if (colorBarIconValid) { +// this->colorBarAction->setIcon(colorBarIcon); +// } +// QToolButton* colorBarToolButton = new QToolButton(); +// colorBarToolButton->setDefaultAction(this->colorBarAction); /* * Settings Tool Button @@ -215,7 +254,9 @@ OverlayViewController::OverlayViewController(const Qt::Orientation orientation, /* * Yoking Group */ - m_mapYokingGroupComboBox = new MapYokingGroupComboBox(this); + m_mapYokingGroupComboBox = new MapYokingGroupComboBox(this, + (objectNamePrefix + + "Map_Yoking_Selection")); m_mapYokingGroupComboBox->getWidget()->setStatusTip("Synchronize enabled status and map indices)"); m_mapYokingGroupComboBox->getWidget()->setToolTip("Yoke to Overlay Mapped Files"); #ifdef CARET_OS_MACOSX @@ -237,7 +278,7 @@ OverlayViewController::OverlayViewController(const Qt::Orientation orientation, this->gridLayoutGroup->addWidget(settingsToolButton, row, 1, Qt::AlignHCenter); - this->gridLayoutGroup->addWidget(colorBarToolButton, + this->gridLayoutGroup->addWidget(m_colorBarToolButton, //colorBarToolButton, row, 2); this->gridLayoutGroup->addWidget(m_constructionToolButton, row, 3); @@ -268,7 +309,7 @@ OverlayViewController::OverlayViewController(const Qt::Orientation orientation, row, 0); this->gridLayoutGroup->addWidget(settingsToolButton, row, 1); - this->gridLayoutGroup->addWidget(colorBarToolButton, + this->gridLayoutGroup->addWidget(m_colorBarToolButton, //colorBarToolButton, row, 2); this->gridLayoutGroup->addWidget(m_constructionToolButton, row, 3); @@ -687,9 +728,10 @@ OverlayViewController::updateViewController(Overlay* overlay) m_mapYokingGroupComboBox->setMapYokingGroup(overlay->getMapYokingGroup()); - this->colorBarAction->blockSignals(true); - this->colorBarAction->setChecked(overlay->getColorBar()->isDisplayed()); - this->colorBarAction->blockSignals(false); + m_colorBarToolButton->setChecked(overlay->getColorBar()->isDisplayed()); +// this->colorBarAction->blockSignals(true); +// this->colorBarAction->setChecked(overlay->getColorBar()->isDisplayed()); +// this->colorBarAction->blockSignals(false); this->opacityDoubleSpinBox->blockSignals(true); this->opacityDoubleSpinBox->setValue(overlay->getOpacity()); @@ -758,7 +800,8 @@ OverlayViewController::updateViewController(Overlay* overlay) this->constructionAction->setEnabled(true); this->opacityDoubleSpinBox->setEnabled(haveOpacity); this->m_mapYokingGroupComboBox->getWidget()->setEnabled(haveYoking); - this->colorBarAction->setEnabled(dataIsMappedWithPalette); + //this->colorBarAction->setEnabled(dataIsMappedWithPalette); + this->m_colorBarToolButton->setEnabled(dataIsMappedWithPalette); this->settingsAction->setEnabled(true); } diff --git a/src/GuiQt/OverlayViewController.h b/src/GuiQt/OverlayViewController.h index d68b2c54f..70fb9c3ce 100644 --- a/src/GuiQt/OverlayViewController.h +++ b/src/GuiQt/OverlayViewController.h @@ -140,7 +140,8 @@ namespace caret { QAction* constructionAction; - QAction* colorBarAction; + QToolButton* m_colorBarToolButton; + //QAction* colorBarAction; QAction* settingsAction; diff --git a/src/GuiQt/WuQtUtilities.cxx b/src/GuiQt/WuQtUtilities.cxx index e4608c8de..c0900e59c 100644 --- a/src/GuiQt/WuQtUtilities.cxx +++ b/src/GuiQt/WuQtUtilities.cxx @@ -43,6 +43,7 @@ #include "CaretAssert.h" #include "CaretLogger.h" +#include "MacroPrototype.h" #include "WuQtUtilities.h" using namespace caret; @@ -215,12 +216,70 @@ WuQtUtilities::createPushButton(const QString& text, return pb; } +//QToolButton* +//WuQtUtilities::createToolButtonWithIcon(const QString& text, +// const QString& iconFileName, +// const QString& toolTip, +// const QString& widgetName, +// QObject* receiver, +// WuQObject::QObjectBoolPointerToMemberFunction method) +//{ +// QIcon icon; +// const bool iconValid = WuQtUtilities::loadIcon(iconFileName, +// icon); +// +// QToolButton* toolButton = new QToolButton(); +// if (iconValid) { +// toolButton->setIcon(icon); +// } +// else { +// toolButton->setText(text); +// } +// toolButton->setToolTip(toolTip); +// toolButton->setObjectName(widgetName); +// +// WuQObject::connectFP(toolButton, +// static_cast(&QToolButton::clicked), +// receiver, +// method); +// +// return toolButton; +//} + +QToolButton* +WuQtUtilities::createToolButtonWithIcon(const QString& text, + const QString& iconFileName, + const QString& toolTip, + const QString& widgetName, + QObject* receiver, + const char* method) +{ + QIcon icon; + const bool iconValid = WuQtUtilities::loadIcon(iconFileName, + icon); + + QToolButton* toolButton = new QToolButton(); + if (iconValid) { + toolButton->setIcon(icon); + } + else { + toolButton->setText(text); + } + toolButton->setToolTip(toolTip); + toolButton->setObjectName(widgetName); + + WuQObject::connect(toolButton, SIGNAL(clicked(bool)), + receiver, method); + + return toolButton; +} + /** * Create a horizontal line widget used as a separator. * * @return A horizontal line widget used as a separator. */ -QWidget* +QWidget* WuQtUtilities::createHorizontalLineWidget() { QFrame* frame = new QFrame(); diff --git a/src/GuiQt/WuQtUtilities.h b/src/GuiQt/WuQtUtilities.h index 791c3942e..d3fff8e21 100644 --- a/src/GuiQt/WuQtUtilities.h +++ b/src/GuiQt/WuQtUtilities.h @@ -30,6 +30,7 @@ #include #include "CaretColorEnum.h" +#include "MacroPrototype.h" class QAction; class QBoxLayout; @@ -65,6 +66,19 @@ namespace caret { TransparentBackground = 1 }; + static QToolButton* createToolButtonWithIcon(const QString& text, + const QString& iconFileName, + const QString& toolTip, + const QString& widgetName, + QObject* receiver, + const char* method); +// static QToolButton* createToolButtonWithIcon(const QString& text, +// const QString& iconFileName, +// const QString& toolTip, +// const QString& widgetName, +// QObject* receiver, +// WuQObject::QObjectBoolPointerToMemberFunction method); + static QAction* createAction(const QString& text, const QString& toolAndStatusTipText, const QKeySequence& shortcut, -- GitLab From 04fe485f41f28152a46b1d670d37a6736bcf1c29 Mon Sep 17 00:00:00 2001 From: John Harwell Date: Mon, 19 Nov 2018 15:36:43 -0600 Subject: [PATCH 119/427] Fix windows compilation error. It appears that "MOUSE_EVENT" clashes with a name in the Microsoft compiler. --- src/GuiQt/MacroPrototype.cxx | 16 ++++++++-------- src/GuiQt/MacroPrototype.h | 2 +- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/GuiQt/MacroPrototype.cxx b/src/GuiQt/MacroPrototype.cxx index 2032a7f38..2d89b4c99 100644 --- a/src/GuiQt/MacroPrototype.cxx +++ b/src/GuiQt/MacroPrototype.cxx @@ -478,7 +478,7 @@ m_widgetName(widgetName) break; case INVALID: break; - case MOUSE_EVENT: + case MOUSE_USER_EVENT: CaretAssert(0); break; case SPIN_BOX: @@ -494,7 +494,7 @@ MacroEvent::MacroEvent(QMouseEvent* me, const QString& widgetName, const int32_t widgetWidth, const int32_t widgetHeight) -: m_widgetType(WidgetWatcherType::MOUSE_EVENT), +: m_widgetType(WidgetWatcherType::MOUSE_USER_EVENT), m_widgetName(widgetName) { m_valueType = ValueType::MOUSE; @@ -528,7 +528,7 @@ MacroEvent::toString() const break; case INVALID: break; - case MOUSE_EVENT: + case MOUSE_USER_EVENT: widgetTypeString = "MOUSE"; break; case SPIN_BOX: @@ -592,7 +592,7 @@ MacroEvent::fromString(const QString& s) widgetType = WidgetWatcherType::DOUBLE_SPIN_BOX; } else if (widgetTypeName == "MOUSE") { - widgetType = WidgetWatcherType::MOUSE_EVENT; + widgetType = WidgetWatcherType::MOUSE_USER_EVENT; } else if (widgetTypeName == "SPIN_BOX") { widgetType = WidgetWatcherType::SPIN_BOX; @@ -608,7 +608,7 @@ MacroEvent::fromString(const QString& s) if (validFlag) { m_widgetName = widgetName; m_widgetType = widgetType; - if (widgetType == WidgetWatcherType::MOUSE_EVENT) { + if (widgetType == WidgetWatcherType::MOUSE_USER_EVENT) { validFlag = m_mouseEventInfo.fromString(valueString); } else { @@ -789,7 +789,7 @@ MacroManager::runMacro(QObject* window, } } - const bool mouseEventFlag = (me->m_widgetType == WidgetWatcherType::MOUSE_EVENT); + const bool mouseEventFlag = (me->m_widgetType == WidgetWatcherType::MOUSE_USER_EVENT); if (widgetToMoveMouse != NULL) { if ( ! mouseEventFlag) { const bool highlightFlag = ( ! mouseEventFlag); @@ -857,7 +857,7 @@ MacroManager::runMacro(QObject* window, } } break; - case WidgetWatcherType::MOUSE_EVENT: + case WidgetWatcherType::MOUSE_USER_EVENT: { QWidget* widget = window->findChild(me->m_widgetName); if (widget != NULL) { @@ -1301,7 +1301,7 @@ WidgetWatcher::WidgetWatcher(const WidgetWatcherType widgetType, this, &WidgetWatcher::doubleSpinBoxValueChanged); } break; - case MOUSE_EVENT: + case MOUSE_USER_EVENT: break; case SPIN_BOX: { diff --git a/src/GuiQt/MacroPrototype.h b/src/GuiQt/MacroPrototype.h index 45da17878..f31f481a0 100644 --- a/src/GuiQt/MacroPrototype.h +++ b/src/GuiQt/MacroPrototype.h @@ -46,7 +46,7 @@ namespace caret { CHECK_BOX, COMBO_BOX, DOUBLE_SPIN_BOX, - MOUSE_EVENT, + MOUSE_USER_EVENT, SPIN_BOX, TOOL_BUTTON, }; -- GitLab From 2122ee158d3b096234b978d683bb7c66dc967a52 Mon Sep 17 00:00:00 2001 From: John Harwell Date: Wed, 28 Nov 2018 08:48:51 -0600 Subject: [PATCH 120/427] Some tile tabs configurations (particularly from scenes) may have an empty name. Recent update to Tile Tabs Configurations mistakenly declared an empty name as an error for the older tile tabs configuration (v1). This has been corrected. --- src/Common/TileTabsConfiguration.cxx | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/Common/TileTabsConfiguration.cxx b/src/Common/TileTabsConfiguration.cxx index 67ea00aea..922c1c572 100644 --- a/src/Common/TileTabsConfiguration.cxx +++ b/src/Common/TileTabsConfiguration.cxx @@ -803,9 +803,11 @@ TileTabsConfiguration::decodeFromXMLWithStreamReaderVersionOne(QXmlStreamReader& } } + static int32_t missingNameCounter = 1; if (name.isEmpty()) { - message.append(s_nameTagName - + " not found or invalid. "); + name = ("Config_V1_" + + AString::number(missingNameCounter)); + missingNameCounter++; } if (uniqueID.isEmpty()) { uniqueID = SystemUtilities::createUniqueID(); -- GitLab From 22822e1abce97a3e1dd95e3b922a859170bf1227 Mon Sep 17 00:00:00 2001 From: Tim Coalson Date: Wed, 28 Nov 2018 22:10:50 -0600 Subject: [PATCH 121/427] fix mistaken loop placement that made palette "Apply to Files" take far longer than intended, update help text of -cifti-average-roi-correlation --- src/Algorithms/AlgorithmCiftiAverageROICorrelation.cxx | 2 +- src/GuiQt/CopyPaletteColorMappingToFilesDialog.cxx | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Algorithms/AlgorithmCiftiAverageROICorrelation.cxx b/src/Algorithms/AlgorithmCiftiAverageROICorrelation.cxx index d737a21f3..a19afed9e 100644 --- a/src/Algorithms/AlgorithmCiftiAverageROICorrelation.cxx +++ b/src/Algorithms/AlgorithmCiftiAverageROICorrelation.cxx @@ -80,7 +80,7 @@ OperationParameters* AlgorithmCiftiAverageROICorrelation::getParameters() ciftiOpt->addCiftiParameter(1, "cifti-in", "a cifti file to average across"); ret->setHelpText( - AString("Averages rows for each map of the ROI(s), takes the correlation of each ROI average to the rest of the rows in the same file, then averages the results across all files. ") + + AString("Averages rows for each map of the ROI(s), takes the correlation of each ROI average to the rest of the rows in the same file, applies the fisher small z transform, then averages the results across all files. ") + "ROIs are always treated as weighting functions, including negative values. " + "For efficiency, ensure that everything that is not intended to be used is zero in the ROI map. " + "If -cifti-roi is specified, -left-roi, -right-roi, -cerebellum-roi, and -vol-roi must not be specified. " + diff --git a/src/GuiQt/CopyPaletteColorMappingToFilesDialog.cxx b/src/GuiQt/CopyPaletteColorMappingToFilesDialog.cxx index 627b16b6f..546b5ca74 100644 --- a/src/GuiQt/CopyPaletteColorMappingToFilesDialog.cxx +++ b/src/GuiQt/CopyPaletteColorMappingToFilesDialog.cxx @@ -165,6 +165,7 @@ CopyPaletteColorMappingToFilesDialog::okButtonClicked() if (checkBox->isChecked()) { if (mapFile != m_selectedMapFile) { + ++checkedCount; mapFile->setPaletteNormalizationMode(m_selectedMapFile->getPaletteNormalizationMode()); const int32_t numMaps = mapFile->getNumberOfMaps(); @@ -172,9 +173,8 @@ CopyPaletteColorMappingToFilesDialog::okButtonClicked() PaletteColorMapping* pcm = mapFile->getMapPaletteColorMapping(iMap); pcm->copy(*m_selectedPaletteColorMapping, false); - mapFile->updateScalarColoringForAllMaps(); - ++checkedCount; } + mapFile->updateScalarColoringForAllMaps(); } else { sourceFileFlag = true; -- GitLab From dfc082f69d7f3f5457614b0cff18a44e5061e2aa Mon Sep 17 00:00:00 2001 From: Tim Coalson Date: Mon, 10 Dec 2018 18:53:42 -0600 Subject: [PATCH 122/427] synchronize/improve label import help --- src/Operations/OperationCiftiLabelImport.cxx | 5 ++--- src/Operations/OperationMetricLabelImport.cxx | 21 ++++++++++--------- src/Operations/OperationVolumeLabelImport.cxx | 21 ++++++++++--------- 3 files changed, 24 insertions(+), 23 deletions(-) diff --git a/src/Operations/OperationCiftiLabelImport.cxx b/src/Operations/OperationCiftiLabelImport.cxx index 44c013d4a..697c72b9d 100644 --- a/src/Operations/OperationCiftiLabelImport.cxx +++ b/src/Operations/OperationCiftiLabelImport.cxx @@ -67,13 +67,12 @@ OperationParameters* OperationCiftiLabelImport::getParameters() ret->setHelpText( AString("Creates a cifti label file from a cifti file with label-like values. ") + - "You may specify the empty string ('' will work on linux/mac) for , which will be treated as if it is an empty file. " + - "It is assumed that a value of 0 in the input file means \"unlabeled\", unless -unlabeled-value is specified. " + - "Do not specify the \"unlabeled\" label in the text file.\n\n" + + "You may specify the empty string (use \"\") for , which will be treated as if it is an empty file. " + "The label list file must have the following format (2 lines per label):\n\n" + "\n \n...\n\n" + "Label names are specified on a separate line from their value and color, in order to let label names contain spaces. " + "Whitespace is trimmed from both ends of the label name, but is kept if it is in the middle of a label. " + + "Do not specify the \"unlabeled\" key in the file, it is assumed that 0 means not labeled unless -unlabeled-value is specified. " + "The value of specifies what value in the imported file should be used as this label. " + "The values of , , and must be integers from 0 to 255, and will specify the color the label is drawn as " + "(alpha of 255 means fully opaque, which is probably what you want).\n\n" + diff --git a/src/Operations/OperationMetricLabelImport.cxx b/src/Operations/OperationMetricLabelImport.cxx index ffbfc27f4..322096910 100644 --- a/src/Operations/OperationMetricLabelImport.cxx +++ b/src/Operations/OperationMetricLabelImport.cxx @@ -68,17 +68,18 @@ OperationParameters* OperationMetricLabelImport::getParameters() ret->createOptionalParameter(7, "-drop-unused-labels", "remove any unused label values from the label table"); ret->setHelpText( - AString("Creates a new gifti label file from a metric file with label-like values. ") + - "You may specify the empty string ('' will work on linux/mac) for , which will be treated as if it is an empty file. " + - "The label list file must have lines of the following format:\n\n" + - "\n \n\n" + - "Do not specify the \"unlabeled\" key in the file, it is assumed that 0 means not labeled unless -unlabeled-value is specified. " + - "Label names must be on a separate line, but may contain spaces or other unusual characters (but not newline). " + + AString("Creates a gifti label file from a metric file with label-like values. ") + + "You may specify the empty string (use \"\") for , which will be treated as if it is an empty file. " + + "The label list file must have the following format (2 lines per label):\n\n" + + "\n \n...\n\n" + + "Label names are specified on a separate line from their value and color, in order to let label names contain spaces. " + "Whitespace is trimmed from both ends of the label name, but is kept if it is in the middle of a label. " + - "The values of red, green, blue and alpha must be integers from 0 to 255, and will specify the color the label is drawn as " + - "(alpha of 255 means opaque, which is probably what you want). " + - "By default, it will set new label names with names of LABEL_# for any values encountered that are not mentioned in the " + - "list file, specify -discard-others to instead set these voxels to the \"unlabeled\" key." + "Do not specify the \"unlabeled\" key in the file, it is assumed that 0 means not labeled unless -unlabeled-value is specified. " + + "The value of specifies what value in the imported file should be used as this label. " + + "The values of , , and must be integers from 0 to 255, and will specify the color the label is drawn as " + + "(alpha of 255 means fully opaque, which is probably what you want).\n\n" + + "By default, it will create new label names with names like LABEL_5 for any values encountered that are not mentioned in the " + + "list file, specify -discard-others to instead set these values to the \"unlabeled\" key." ); return ret; } diff --git a/src/Operations/OperationVolumeLabelImport.cxx b/src/Operations/OperationVolumeLabelImport.cxx index 8a8e21a4e..2bf1f582d 100644 --- a/src/Operations/OperationVolumeLabelImport.cxx +++ b/src/Operations/OperationVolumeLabelImport.cxx @@ -67,18 +67,19 @@ OperationParameters* OperationVolumeLabelImport::getParameters() ret->createOptionalParameter(7, "-drop-unused-labels", "remove any unused label values from the label table"); ret->setHelpText( - AString("Creates a new label volume from an integer-valued volume file. ") + + AString("Creates a label volume from an integer-valued volume file. ") + "The label name and color information is stored in the volume header in a nifti extension, with a similar format as in caret5, see -volume-help. " + - "You may specify the empty string ('' will work on linux/mac) for , which will be treated as if it is an empty file. " + - "The label list file must have pairs of lines of the following format:\n\n" + - "\n \n\n" + - "Do not specify the \"unlabeled\" key in the file, it is assumed that 0 means not labeled unless -unlabeled-value is specified. " + - "Label names must be on a separate line, but may contain spaces or other unusual characters (but not newline). " + + "You may specify the empty string (use \"\") for , which will be treated as if it is an empty file. " + + "The label list file must have the following format (2 lines per label):\n\n" + + "\n \n...\n\n" + + "Label names are specified on a separate line from their value and color, in order to let label names contain spaces. " + "Whitespace is trimmed from both ends of the label name, but is kept if it is in the middle of a label. " + - "The values of red, green, blue and alpha must be integers from 0 to 255, and will specify the color the label is drawn as " + - "(alpha of 255 means opaque, which is probably what you want). " + - "By default, it will set new label names with names of LABEL_# for any values encountered that are not mentioned in the " + - "list file, specify -discard-others to instead set these voxels to the \"unlabeled\" key." + "Do not specify the \"unlabeled\" key in the file, it is assumed that 0 means not labeled unless -unlabeled-value is specified. " + + "The value of specifies what value in the imported file should be used as this label. " + + "The values of , , and must be integers from 0 to 255, and will specify the color the label is drawn as " + + "(alpha of 255 means fully opaque, which is probably what you want).\n\n" + + "By default, it will create new label names with names like LABEL_5 for any values encountered that are not mentioned in the " + + "list file, specify -discard-others to instead set these values to the \"unlabeled\" key." ); return ret; } -- GitLab From be05246771e21087e4c2a6b846a5c1f221ec73a0 Mon Sep 17 00:00:00 2001 From: John Harwell Date: Mon, 26 Nov 2018 14:20:09 -0600 Subject: [PATCH 123/427] Related to WB-506: When drawing volume slices, allow the bottom layer's opacity to blend the bottom layer's volume slice with the background. This is needed for upcoming animation operations. --- src/Brain/BrainOpenGLFixedPipeline.cxx | 3 - .../BrainOpenGLVolumeObliqueSliceDrawing.cxx | 61 ++++++++++++++++--- .../BrainOpenGLVolumeObliqueSliceDrawing.h | 5 +- 3 files changed, 55 insertions(+), 14 deletions(-) diff --git a/src/Brain/BrainOpenGLFixedPipeline.cxx b/src/Brain/BrainOpenGLFixedPipeline.cxx index ab1a7a8b3..221941f83 100644 --- a/src/Brain/BrainOpenGLFixedPipeline.cxx +++ b/src/Brain/BrainOpenGLFixedPipeline.cxx @@ -3661,9 +3661,6 @@ BrainOpenGLFixedPipeline::setupVolumeDrawInfo(BrowserTabContent* browserTabConte VolumeMappableInterface* vf = dynamic_cast(mapFile); if (vf != NULL) { float opacity = overlay->getOpacity(); - if (volumeDrawInfoOut.empty()) { - opacity = 1.0; - } WholeBrainVoxelDrawingMode::Enum wholeBrainVoxelDrawingMode = overlay->getWholeBrainVoxelDrawingMode(); diff --git a/src/Brain/BrainOpenGLVolumeObliqueSliceDrawing.cxx b/src/Brain/BrainOpenGLVolumeObliqueSliceDrawing.cxx index 528950a83..609036316 100644 --- a/src/Brain/BrainOpenGLVolumeObliqueSliceDrawing.cxx +++ b/src/Brain/BrainOpenGLVolumeObliqueSliceDrawing.cxx @@ -2907,6 +2907,7 @@ BrainOpenGLVolumeObliqueSliceDrawing::drawObliqueSliceWithOutlines(const VolumeS const DisplayPropertiesLabels* displayPropertiesLabels = m_brain->getDisplayPropertiesLabels(); const DisplayGroupEnum::Enum displayGroup = displayPropertiesLabels->getDisplayGroupForTab(browserTabIndex); + bool haveAlphaBlendingFlag(false); std::vector slices; for (int32_t iVol = 0; iVol < numVolumes; iVol++) { const BrainOpenGLFixedPipeline::VolumeDrawInfo& vdi = m_volumeDrawInfo[iVol]; @@ -2918,6 +2919,8 @@ BrainOpenGLVolumeObliqueSliceDrawing::drawObliqueSliceWithOutlines(const VolumeS volumeEditDrawAllVoxelsFlag = true; } } + + const bool bottomLayerFlag(iVol == 0); ObliqueSlice* slice = new ObliqueSlice(m_fixedPipelineDrawing, volInter, m_volumeDrawInfo[iVol].opacity, @@ -2933,16 +2936,28 @@ BrainOpenGLVolumeObliqueSliceDrawing::drawObliqueSliceWithOutlines(const VolumeS m_obliqueSliceMaskingType, voxelEditingValue, volumeEditDrawAllVoxelsFlag, - m_identificationModeFlag); + m_identificationModeFlag, + bottomLayerFlag); slices.push_back(slice); + + if (m_volumeDrawInfo[iVol].opacity < 1.0) { + haveAlphaBlendingFlag = true; + } } const int32_t numSlices = static_cast(slices.size()); if (numSlices > 0) { - bool drawAllSlicesFlag = true; - const bool compositeFlag = true; - if (compositeFlag - && ( ! m_identificationModeFlag)) { + bool drawEachSliceFlag = true; + bool compositeFlag = true; + if (haveAlphaBlendingFlag + || m_identificationModeFlag) { + /* + * Do not composite slices if blending is used + * or if in identification mode + */ + compositeFlag = false; + } + if (compositeFlag) { CaretAssertVectorIndex(slices, 0); ObliqueSlice* underlaySlice = slices[0]; if (numSlices > 1) { @@ -2953,12 +2968,12 @@ BrainOpenGLVolumeObliqueSliceDrawing::drawObliqueSliceWithOutlines(const VolumeS } if (underlaySlice->compositeSlicesRGBA(overlaySlices)) { underlaySlice->draw(m_fixedPipelineDrawing); - drawAllSlicesFlag = false; + drawEachSliceFlag = false; } } } - if (drawAllSlicesFlag) { + if (drawEachSliceFlag) { for (auto s : slices) { s->draw(m_fixedPipelineDrawing); } @@ -3007,6 +3022,8 @@ BrainOpenGLVolumeObliqueSliceDrawing::drawObliqueSliceWithOutlines(const VolumeS * Draw all voxels when editing a volume. * @param identificationModeFlag * True if identification mode is active. + * @param bottomLayerFlag + * True if bottom layer. */ BrainOpenGLVolumeObliqueSliceDrawing::ObliqueSlice::ObliqueSlice(BrainOpenGLFixedPipeline* fixedPipelineDrawing, VolumeMappableInterface* volumeInterface, @@ -3023,7 +3040,8 @@ BrainOpenGLVolumeObliqueSliceDrawing::ObliqueSlice::ObliqueSlice(BrainOpenGLFixe const VolumeSliceInterpolationEdgeEffectsMaskingEnum::Enum maskingType, const float voxelEditingValue, const bool volumeEditingDrawAllVoxelsFlag, - const bool identificationModeFlag) + const bool identificationModeFlag, + const bool bottomLayerFlag) : m_volumeInterface(volumeInterface), m_opacity(opacity), @@ -3036,7 +3054,8 @@ m_displayPropertiesLabels(displayPropertiesLabels), m_displayGroup(displayGroup), m_identificationX(fixedPipelineDrawing->mouseX), m_identificationY(fixedPipelineDrawing->mouseY), -m_identificationModeFlag(identificationModeFlag) +m_identificationModeFlag(identificationModeFlag), +m_bottomLayerFlag(bottomLayerFlag) { CaretAssert(volumeInterface); CaretAssert(m_mapFile); @@ -3774,6 +3793,15 @@ BrainOpenGLVolumeObliqueSliceDrawing::ObliqueSlice::draw(BrainOpenGLFixedPipelin m_originXYZ[2] }; + uint8_t sliceAlpha = 255; + bool drawWithBlendingFlag(false); +// if (m_bottomLayerFlag) { + if (m_opacity < 1.0) { + sliceAlpha = static_cast(m_opacity * 255.0); + drawWithBlendingFlag = true; + } +// } + std::vector selectionIJK; for (int32_t iRow = 0; iRow < m_numberOfRows; iRow++) { float voxelXYZ[3] = { @@ -3804,7 +3832,14 @@ BrainOpenGLVolumeObliqueSliceDrawing::ObliqueSlice::draw(BrainOpenGLFixedPipelin nextXYZ[1] + m_bottomToTopStepXYZ[1], nextXYZ[2] + m_bottomToTopStepXYZ[2] }; - const uint8_t* rgba = &m_rgba[rgbaIndex]; + uint8_t* rgba = &m_rgba[rgbaIndex]; + if (drawWithBlendingFlag) { + if ((rgba[0] > 0) + || (rgba[1] > 0) + || (rgba[2] > 0)) { + rgba[3] = sliceAlpha; + } + } /* * Each voxel is drawn with two triangles because: @@ -3922,7 +3957,13 @@ BrainOpenGLVolumeObliqueSliceDrawing::ObliqueSlice::draw(BrainOpenGLFixedPipelin } } else { + glPushAttrib(GL_COLOR_BUFFER_BIT); + if (drawWithBlendingFlag) { + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + } GraphicsEngineDataOpenGL::draw(primitive.get()); + glPopAttrib(); } } } diff --git a/src/Brain/BrainOpenGLVolumeObliqueSliceDrawing.h b/src/Brain/BrainOpenGLVolumeObliqueSliceDrawing.h index bf4075b74..99dcdf8ba 100644 --- a/src/Brain/BrainOpenGLVolumeObliqueSliceDrawing.h +++ b/src/Brain/BrainOpenGLVolumeObliqueSliceDrawing.h @@ -90,7 +90,8 @@ namespace caret { const VolumeSliceInterpolationEdgeEffectsMaskingEnum::Enum maskingType, const float voxelEditingValue, const bool volumeEditingDrawAllVoxelsFlag, - const bool identificationModeFlag); + const bool identificationModeFlag, + const bool bottomLayerFlag); void assignRgba(const bool volumeEditingDrawAllVoxelsFlag); @@ -134,6 +135,8 @@ namespace caret { const bool m_identificationModeFlag; + const bool m_bottomLayerFlag; + CiftiMappableDataFile* m_ciftiMappableFile = NULL; VolumeFile* m_volumeFile = NULL; -- GitLab From 82bcb253100dceb5f2858d8e9c140cd670d4d874 Mon Sep 17 00:00:00 2001 From: John Harwell Date: Fri, 30 Nov 2018 16:32:41 -0600 Subject: [PATCH 124/427] WB-834: Mostly finished with reimplementation of the prototype macro system. --- src/Common/CMakeLists.txt | 10 + src/Common/CaretPreferences.cxx | 23 +- src/Common/CaretPreferences.h | 8 + src/Common/WuQMacro.cxx | 279 ++++++++++ src/Common/WuQMacro.h | 94 ++++ src/Common/WuQMacroCommand.cxx | 354 +++++++++++++ src/Common/WuQMacroCommand.h | 86 +++ src/Common/WuQMacroGroup.cxx | 222 ++++++++ src/Common/WuQMacroGroup.h | 85 +++ src/Common/WuQMacroModeEnum.cxx | 379 ++++++++++++++ src/Common/WuQMacroModeEnum.h | 106 ++++ src/Common/WuQMacroObjectTypeEnum.cxx | 428 +++++++++++++++ src/Common/WuQMacroObjectTypeEnum.h | 168 ++++++ src/GuiQt/BrainBrowserWindow.cxx | 9 +- src/GuiQt/BrainBrowserWindow.h | 2 - .../BrainBrowserWindowToolBarSlicePlane.cxx | 41 +- ...rainBrowserWindowToolBarSliceSelection.cxx | 81 ++- src/GuiQt/CMakeLists.txt | 21 + src/GuiQt/GuiManager.cxx | 35 +- src/GuiQt/MapYokingGroupComboBox.cxx | 4 +- src/GuiQt/OverlayViewController.cxx | 142 +++-- src/GuiQt/OverlayViewController.h | 4 +- src/GuiQt/WuQMacroCreateDialog.cxx | 185 +++++++ src/GuiQt/WuQMacroCreateDialog.h | 83 +++ src/GuiQt/WuQMacroDialog.cxx | 271 ++++++++++ src/GuiQt/WuQMacroDialog.h | 99 ++++ src/GuiQt/WuQMacroExecutor.cxx | 426 +++++++++++++++ src/GuiQt/WuQMacroExecutor.h | 75 +++ src/GuiQt/WuQMacroManager.cxx | 386 ++++++++++++++ src/GuiQt/WuQMacroManager.h | 110 ++++ src/GuiQt/WuQMacroMenu.cxx | 137 +++++ src/GuiQt/WuQMacroMenu.h | 70 +++ src/GuiQt/WuQMacroSignalEmitter.cxx | 436 ++++++++++++++++ src/GuiQt/WuQMacroSignalEmitter.h | 128 +++++ src/GuiQt/WuQMacroSignalWatcher.cxx | 494 ++++++++++++++++++ src/GuiQt/WuQMacroSignalWatcher.h | 110 ++++ src/GuiQt/WuQtUtilities.cxx | 1 - 37 files changed, 5486 insertions(+), 106 deletions(-) create mode 100644 src/Common/WuQMacro.cxx create mode 100644 src/Common/WuQMacro.h create mode 100644 src/Common/WuQMacroCommand.cxx create mode 100644 src/Common/WuQMacroCommand.h create mode 100644 src/Common/WuQMacroGroup.cxx create mode 100644 src/Common/WuQMacroGroup.h create mode 100644 src/Common/WuQMacroModeEnum.cxx create mode 100644 src/Common/WuQMacroModeEnum.h create mode 100644 src/Common/WuQMacroObjectTypeEnum.cxx create mode 100644 src/Common/WuQMacroObjectTypeEnum.h create mode 100644 src/GuiQt/WuQMacroCreateDialog.cxx create mode 100644 src/GuiQt/WuQMacroCreateDialog.h create mode 100644 src/GuiQt/WuQMacroDialog.cxx create mode 100644 src/GuiQt/WuQMacroDialog.h create mode 100644 src/GuiQt/WuQMacroExecutor.cxx create mode 100644 src/GuiQt/WuQMacroExecutor.h create mode 100644 src/GuiQt/WuQMacroManager.cxx create mode 100644 src/GuiQt/WuQMacroManager.h create mode 100644 src/GuiQt/WuQMacroMenu.cxx create mode 100644 src/GuiQt/WuQMacroMenu.h create mode 100644 src/GuiQt/WuQMacroSignalEmitter.cxx create mode 100644 src/GuiQt/WuQMacroSignalEmitter.h create mode 100644 src/GuiQt/WuQMacroSignalWatcher.cxx create mode 100644 src/GuiQt/WuQMacroSignalWatcher.h diff --git a/src/Common/CMakeLists.txt b/src/Common/CMakeLists.txt index ac9de4108..a4cef3d1c 100644 --- a/src/Common/CMakeLists.txt +++ b/src/Common/CMakeLists.txt @@ -160,6 +160,11 @@ Vector3D.h VectorOperation.h VoxelIJK.h WorkbenchSpecialVersionEnum.h +WuQMacro.h +WuQMacroCommand.h +WuQMacroGroup.h +WuQMacroModeEnum.h +WuQMacroObjectTypeEnum.h YokingGroupEnum.h ${MOC_SOURCE_FILES} @@ -261,6 +266,11 @@ TriStateSelectionStatusEnum.cxx Vector3D.cxx VectorOperation.cxx WorkbenchSpecialVersionEnum.cxx +WuQMacro.cxx +WuQMacroCommand.cxx +WuQMacroGroup.cxx +WuQMacroModeEnum.cxx +WuQMacroObjectTypeEnum.cxx YokingGroupEnum.cxx ) diff --git a/src/Common/CaretPreferences.cxx b/src/Common/CaretPreferences.cxx index f59d9bed5..dd93dd695 100644 --- a/src/Common/CaretPreferences.cxx +++ b/src/Common/CaretPreferences.cxx @@ -33,11 +33,10 @@ #include "CaretLogger.h" #include "ModelTransform.h" #include "TileTabsConfiguration.h" +#include "WuQMacroGroup.h" using namespace caret; - - /** * \class caret::CaretPreferences * \brief Preferences for use in Caret. @@ -53,6 +52,8 @@ using namespace caret; CaretPreferences::CaretPreferences() : CaretObject() { + m_macros.reset(new WuQMacroGroup("Preferences")); + this->qSettings = new QSettings("brainvis.wustl.edu", "Caret7"); this->readPreferences(); @@ -1326,6 +1327,24 @@ void CaretPreferences::setVolumeIdentificationDefaultedOn(const bool status) this->qSettings->sync(); } +/** + * @return Pointer to the macros. + */ +WuQMacroGroup* +CaretPreferences::getMacros() +{ + return m_macros.get(); +} + +/** + * @return Const pointer to the macros. + */ +const WuQMacroGroup* +CaretPreferences::getMacros() const +{ + return m_macros.get(); +} + /** * Read an unsigned byte array to the preferences. * diff --git a/src/Common/CaretPreferences.h b/src/Common/CaretPreferences.h index ef0ff71b7..d0399e722 100644 --- a/src/Common/CaretPreferences.h +++ b/src/Common/CaretPreferences.h @@ -21,6 +21,7 @@ */ /*LICENSE_END*/ +#include #include #include "BackgroundAndForegroundColors.h" @@ -38,6 +39,7 @@ namespace caret { class ModelTransform; class TileTabsConfiguration; + class WuQMacroGroup; class CaretPreferences : public CaretObject { @@ -194,6 +196,10 @@ namespace caret { void setDynamicConnectivityDefaultedOn(const bool defaultedOn); + WuQMacroGroup* getMacros(); + + const WuQMacroGroup* getMacros() const; + private: CaretPreferences(const CaretPreferences&); @@ -301,6 +307,8 @@ namespace caret { SpecFileDialogViewFilesTypeEnum::Enum manageFilesViewFileType; + std::unique_ptr m_macros; + static const AString NAME_ANIMATION_START_TIME; static const AString NAME_BALSA_USER_NAME; static const AString NAME_VOLUME_AXES_CROSSHAIRS; diff --git a/src/Common/WuQMacro.cxx b/src/Common/WuQMacro.cxx new file mode 100644 index 000000000..5a8a07582 --- /dev/null +++ b/src/Common/WuQMacro.cxx @@ -0,0 +1,279 @@ + +/*LICENSE_START*/ +/* + * Copyright (C) 2018 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + +#define __WU_Q_MACRO_DECLARE__ +#include "WuQMacro.h" +#undef __WU_Q_MACRO_DECLARE__ + +//#include +// +//#include +//#include + +#include "CaretAssert.h" +#include "WuQMacroCommand.h" +//#include "WuQMacroSignalEmitter.h" + +using namespace caret; + + + +/** + * \class caret::WuQMacro + * \brief Contains a sequence of WuQMacroCommand's + * \ingroup WuQMacro + * + */ + +/** + * Constructor. + */ +WuQMacro::WuQMacro() +: CaretObjectTracksModification() +{ + +} + +/** + * Destructor. + */ +WuQMacro::~WuQMacro() +{ + for (auto mc : m_macroCommands) { + delete mc; + } + m_macroCommands.clear(); +} + +void +WuQMacro::addMacroCommand(WuQMacroCommand* macroCommand) +{ + m_macroCommands.push_back(macroCommand); + setModified(); +} + +/** + * @return Name of macro + */ +QString +WuQMacro::getName() const +{ + return m_name; +} + +/** + * Set name of macro + * + * @param name + * New name + */ +void +WuQMacro::setName(const QString& name) +{ + if (m_name != name) { + m_name = name; + setModified(); + } +} + +/** + * @return Description of macro + */ +QString +WuQMacro::getDescription() const +{ + return m_description; +} + +/** + * Set description of macro + * + * @param description + * New description + */ +void +WuQMacro::setDescription(const QString& description) +{ + if (m_description != description) { + m_description = description; + setModified(); + } +} + +/** + * @return The number of macro commands in this macro + */ +int32_t +WuQMacro::getNumberOfMacroCommands() const +{ + return m_macroCommands.size(); +} + +/** + * @return + * The macro command at the given index + * @param index + * Index of the macro command + */ +const WuQMacroCommand* +WuQMacro::getMacroCommandAtIndex(const int32_t index) const +{ + CaretAssertVectorIndex(m_macroCommands, index); + return m_macroCommands[index]; +} + +/** + * @return + * The macro command at the given index + * @param index + * Index of the macro command + */ +WuQMacroCommand* +WuQMacro::getMacroCommandAtIndex(const int32_t index) +{ + CaretAssertVectorIndex(m_macroCommands, index); + return m_macroCommands[index]; +} + + +//void +//WuQMacro::moveMouse(QWidget* widget, +// const bool highlightFlag) +//{ +// CaretAssert(widget); +// +// const QPoint widgetCenter = widget->rect().center(); +// const QPoint windowPoint = widget->mapToGlobal(widgetCenter); +// +// if (highlightFlag) { +// const float radius = 15.0; +// for (float angle = 0.0; angle < 6.28; angle += 0.314) { +// const float x = windowPoint.x() + (std::cos(angle) * radius); +// const float y = windowPoint.y() + (std::sin(angle) * radius); +// QCursor::setPos(x, y); +// //SystemUtilities::sleepSeconds(0.025); +// } +// } +// +// QCursor::setPos(windowPoint); +//} +// +///** +// * Run the commands in this macro. +// * +// * @param window +// * The window from which macro was launched +// * @param stopOnErrorFlag +// * If true, stop running the commands if there is an error +// * @param errorMessageOut +// * Output containing any error messages +// * @return +// * True if the macro completed without errors, else false. +// */ +//bool +//WuQMacro::runMacro(QObject* window, +// const bool stopOnErrorFlag, +// QString& errorMessageOut) +//{ +// errorMessageOut.clear(); +// +// for (auto mc : m_macroCommands) { +// CaretAssert(mc); +// +// const QString objectName(mc->getObjectName()); +// QObject* object = window->findChild(objectName); +// if (object == NULL) { +// errorMessageOut.append("Unable to find object named " +// + objectName +// + "\n"); +// continue; +// } +// +// if (object->signalsBlocked()) { +// errorMessageOut.append("Object named " +// + objectName +// + " has signals blocked"); +// continue; +// } +// +// QWidget* widgetToMoveMouse = qobject_cast(object); +// +// if (widgetToMoveMouse == NULL) { +// QObject* object = window->findChild(objectName); +// if (object != NULL) { +// QObject* parent = object->parent(); +// if (parent != NULL) { +// widgetToMoveMouse = qobject_cast(parent); +// } +// } +// } +// +// const WuQMacroObjectTypeEnum::Enum objectType = mc->getObjectType(); +// +// const bool mouseEventFlag = (objectType == WuQMacroObjectTypeEnum::MOUSE_USER_EVENT); +// if (widgetToMoveMouse != NULL) { +// if ( ! mouseEventFlag) { +// const bool highlightFlag = ( ! mouseEventFlag); +// moveMouse(widgetToMoveMouse, +// highlightFlag); +// } +// } +// +// QString commandErrorMessage; +// if ( ! mc->runMacro(object, +// commandErrorMessage)) { +// errorMessageOut.append(commandErrorMessage + "\n"); +// if (stopOnErrorFlag) { +// return false; +// } +// } +// +// QGuiApplication::processEvents(); +// if ( ! mouseEventFlag) { +// SystemUtilities::sleepSeconds(1); +// } +// QGuiApplication::processEvents(); +// } +// +// return (errorMessageOut.isEmpty()); +// +// +//} + +/** + * Get a description of this object's content. + * @return String describing this object's content. + */ +AString +WuQMacro::toString() const +{ + QString s("WuQMacro\n"); + + s.append("Name=" + m_name + "\n"); + s.append("Description=" + m_description + "\n"); + + for (auto mc : m_macroCommands) { + s.append(mc->toString() + "\n"); + } + + return s; +} + diff --git a/src/Common/WuQMacro.h b/src/Common/WuQMacro.h new file mode 100644 index 000000000..870198e56 --- /dev/null +++ b/src/Common/WuQMacro.h @@ -0,0 +1,94 @@ +#ifndef __WU_Q_MACRO_H__ +#define __WU_Q_MACRO_H__ + +/*LICENSE_START*/ +/* + * Copyright (C) 2018 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + + + +#include + +#include "CaretObjectTracksModification.h" + +//class QObject; +//class QWidget; + +namespace caret { + + class WuQMacroCommand; + + class WuQMacro : public CaretObjectTracksModification { + + public: + WuQMacro(); + + virtual ~WuQMacro(); + + WuQMacro(const WuQMacro&) = delete; + + WuQMacro& operator=(const WuQMacro&) = delete; + + void addMacroCommand(WuQMacroCommand* macroCommand); + + int32_t getNumberOfMacroCommands() const; + + const WuQMacroCommand* getMacroCommandAtIndex(const int32_t index) const; + + WuQMacroCommand* getMacroCommandAtIndex(const int32_t index); + +// bool runMacro(QObject* window, +// const bool stopOnErrorFlag, +// QString& errorMessageOut); + + QString getName() const; + + void setName(const QString& name); + + QString getDescription() const; + + void setDescription(const QString& description); + + // ADD_NEW_METHODS_HERE + + virtual AString toString() const; + + private: +// void emitActionChecked(QAction* action, +// const bool checked); + +// void moveMouse(QWidget* widget, +// const bool highlightFlag); + + std::vector m_macroCommands; + + QString m_name; + + QString m_description; + + // ADD_NEW_MEMBERS_HERE + + }; + +#ifdef __WU_Q_MACRO_DECLARE__ + // +#endif // __WU_Q_MACRO_DECLARE__ + +} // namespace +#endif //__WU_Q_MACRO_H__ diff --git a/src/Common/WuQMacroCommand.cxx b/src/Common/WuQMacroCommand.cxx new file mode 100644 index 000000000..ca06b11e7 --- /dev/null +++ b/src/Common/WuQMacroCommand.cxx @@ -0,0 +1,354 @@ + +/*LICENSE_START*/ +/* + * Copyright (C) 2018 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + +#define __WU_Q_MACRO_COMMAND_DECLARE__ +#include "WuQMacroCommand.h" +#undef __WU_Q_MACRO_COMMAND_DECLARE__ + +//#include +//#include +//#include +//#include +//#include +//#include +//#include +//#include +//#include +//#include +//#include +//#include +//#include +//#include +//#include +//#include + +#include "CaretAssert.h" +//#include "WuQMacroSignalEmitter.h" + +using namespace caret; + +/** + * \class caret::WuQMacroCommand + * \brief Issues a QObject's signal so that its slots execute + * \ingroup WuQMacro + */ + +/** + * Constructor. + * + * @param objectType + * Type of object + * @param objectName + * Name of object + * @param objectToolTip + * Tooltip of object + * @param value + * Value for the command. + */ +WuQMacroCommand::WuQMacroCommand(const WuQMacroObjectTypeEnum::Enum objectType, + const QString objectName, + const QString objectToolTip, + const QVariant value) +: CaretObjectTracksModification(), +m_objectType(objectType), +m_objectName(objectName), +m_objectToolTip(objectToolTip), +m_value(value) +{ +} + +/** + * Destructor. + */ +WuQMacroCommand::~WuQMacroCommand() +{ +} + +/** + * @return The object' type + */ +WuQMacroObjectTypeEnum::Enum +WuQMacroCommand::getObjectType() const +{ + return m_objectType; +} + +/** + * @return The object's name + */ +QString +WuQMacroCommand::getObjectName() const +{ + return m_objectName; +} + +/** + * @return The object's tooltip + */ +QString +WuQMacroCommand::getObjectToolTip() const +{ + return m_objectToolTip; +} + +/** + * @return The object's value. + */ +QVariant +WuQMacroCommand::getObjectValue() const +{ + return m_value; +} + +///** +// * Run the command. +// * +// * @param object +// * The object that must be cast to a specific type +// * @param errorMessageOut +// * Output containing any error messages +// * @return +// * True if the macro completed without errors, else false. +// */ +//bool +//WuQMacroCommand::runMacro(QObject* object, +// QString& errorMessageOut) const +//{ +// errorMessageOut.clear(); +// +// bool notFoundFlag(false); +// switch (m_objectType) { +// case WuQMacroObjectTypeEnum::ACTION: +// { +// QAction* action = qobject_cast(object); +// if (action != NULL) { +// WuQMacroSignalEmitter signalEmitter; +// signalEmitter.emitQActionSignal(action, +// m_value.toBool()); +// } +// else { +// notFoundFlag = true; +// } +// } +// break; +// case WuQMacroObjectTypeEnum::CHECK_BOX: +// { +// QCheckBox* checkBox = qobject_cast(object); +// if (checkBox != NULL) { +// WuQMacroSignalEmitter signalEmitter; +// signalEmitter.emitQCheckBoxSignal(checkBox, +// m_value.toBool()); +// } +// else { +// notFoundFlag = true; +// } +// } +// break; +// case WuQMacroObjectTypeEnum::COMBO_BOX: +// { +// QComboBox* comboBox = qobject_cast(object); +// if (comboBox != NULL) { +// WuQMacroSignalEmitter signalEmitter; +// signalEmitter.emitQComboBoxSignal(comboBox, +// m_value.toInt()); +// } +// else { +// notFoundFlag = true; +// } +// } +// break; +// case WuQMacroObjectTypeEnum::DOUBLE_SPIN_BOX: +// { +// QDoubleSpinBox* doubleSpinBox = qobject_cast(object); +// if (doubleSpinBox != NULL) { +// WuQMacroSignalEmitter signalEmitter; +// signalEmitter.emitQDoubleSpinBoxSignal(doubleSpinBox, +// m_value.toDouble()); +// } +// else { +// notFoundFlag = true; +// } +// } +// break; +// case WuQMacroObjectTypeEnum::INVALID: +// CaretAssert(0); +// break; +// case WuQMacroObjectTypeEnum::LINE_EDIT: +// { +// QLineEdit* lineEdit = qobject_cast(object); +// if (lineEdit != NULL) { +// WuQMacroSignalEmitter signalEmitter; +// signalEmitter.emitQLineEditSignal(lineEdit, +// m_value.toString()); +// } +// else { +// notFoundFlag = true; +// } +// } +// break; +// case WuQMacroObjectTypeEnum::LIST_WIDGET: +// { +// QListWidget* listWidget = qobject_cast(object); +// if (listWidget != NULL) { +// WuQMacroSignalEmitter signalEmitter; +// signalEmitter.emitQListWidgetSignal(listWidget, +// m_value.toString()); +// } +// else { +// notFoundFlag = true; +// } +// } +// break; +// case WuQMacroObjectTypeEnum::MENU: +// { +// QMenu* menu = qobject_cast(object); +// if (menu != NULL) { +// WuQMacroSignalEmitter signalEmitter; +// signalEmitter.emitQMenuTriggered(menu, +// m_value.toString()); +// } +// else { +// notFoundFlag = true; +// } +// } +// break; +// case WuQMacroObjectTypeEnum::MOUSE_USER_EVENT: +// CaretAssertToDoFatal(); +// break; +// case WuQMacroObjectTypeEnum::PUSH_BUTTON: +// { +// QPushButton* pushButton = qobject_cast(object); +// if (pushButton != NULL) { +// WuQMacroSignalEmitter signalEmitter; +// signalEmitter.emitQPushButtonSignal(pushButton, +// m_value.toBool()); +// } +// else { +// notFoundFlag = true; +// } +// } +// break; +// case WuQMacroObjectTypeEnum::RADIO_BUTTON: +// { +// QRadioButton* radioButton = qobject_cast(object); +// if (radioButton != NULL) { +// WuQMacroSignalEmitter signalEmitter; +// signalEmitter.emitQRadioButtonSignal(radioButton, +// m_value.toBool()); +// } +// else { +// notFoundFlag = true; +// } +// } +// break; +// case WuQMacroObjectTypeEnum::SLIDER: +// { +// QSlider* slider = qobject_cast(object); +// if (slider != NULL) { +// WuQMacroSignalEmitter signalEmitter; +// signalEmitter.emitQSliderSignal(slider, +// m_value.toInt()); +// } +// else { +// notFoundFlag = true; +// } +// } +// break; +// case WuQMacroObjectTypeEnum::SPIN_BOX: +// { +// QSpinBox* spinBox = qobject_cast(object); +// if (spinBox != NULL) { +// WuQMacroSignalEmitter signalEmitter; +// signalEmitter.emitQSpinBoxSignal(spinBox, +// m_value.toInt()); +// } +// else { +// notFoundFlag = true; +// } +// } +// break; +// case WuQMacroObjectTypeEnum::TAB_BAR: +// { +// QTabBar* tabBar = qobject_cast(object); +// if (tabBar != NULL) { +// WuQMacroSignalEmitter signalEmitter; +// signalEmitter.emitQTabBarSignal(tabBar, +// m_value.toInt()); +// } +// else { +// notFoundFlag = true; +// } +// } +// break; +// case WuQMacroObjectTypeEnum::TAB_WIDGET: +// { +// QTabWidget* tabWidget = qobject_cast(object); +// if (tabWidget != NULL) { +// WuQMacroSignalEmitter signalEmitter; +// signalEmitter.emitQTabWidgetSignal(tabWidget, +// m_value.toInt()); +// } +// else { +// notFoundFlag = true; +// } +// } +// break; +// case WuQMacroObjectTypeEnum::TOOL_BUTTON: +// { +// QToolButton* toolButton = qobject_cast(object); +// if (toolButton != NULL) { +// WuQMacroSignalEmitter signalEmitter; +// signalEmitter.emitQToolButtonSignal(toolButton, +// m_value.toBool()); +// } +// else { +// notFoundFlag = true; +// } +// } +// break; +// } +// +// if (notFoundFlag) { +// errorMessageOut = ("ERROR: Unable to cast object named " +// + m_objectName +// + " to " +// + WuQMacroObjectTypeEnum::toGuiName(m_objectType)); +// return false; +// } +// +// return true; +//} + +/** + * Get a description of this object's content. + * @return String describing this object's content. + */ +AString +WuQMacroCommand::toString() const +{ + QString s("WuQMacroCommand name=%1, type=%2, tooltip=%3, value=%4"); + s = s.arg(m_objectName + ).arg(WuQMacroObjectTypeEnum::toGuiName(m_objectType) + ).arg(m_objectToolTip + ).arg(m_value.toString()); + return s; +} + diff --git a/src/Common/WuQMacroCommand.h b/src/Common/WuQMacroCommand.h new file mode 100644 index 000000000..b1f8e16c0 --- /dev/null +++ b/src/Common/WuQMacroCommand.h @@ -0,0 +1,86 @@ +#ifndef __WU_Q_MACRO_COMMAND_H__ +#define __WU_Q_MACRO_COMMAND_H__ + +/*LICENSE_START*/ +/* + * Copyright (C) 2018 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + + + +#include + +#include + +#include "CaretObjectTracksModification.h" +#include "WuQMacroObjectTypeEnum.h" + +class QObject; + +namespace caret { + + class WuQMacroCommand : public CaretObjectTracksModification { + + public: + + WuQMacroCommand(const WuQMacroObjectTypeEnum::Enum objectType, + const QString objectName, + const QString objectToolTip, + const QVariant value); + + virtual ~WuQMacroCommand(); + + WuQMacroCommand(const WuQMacroCommand&) = delete; + + WuQMacroCommand& operator=(const WuQMacroCommand&) = delete; + + WuQMacroObjectTypeEnum::Enum getObjectType() const; + + QString getObjectName() const; + + QString getObjectToolTip() const; + + QVariant getObjectValue() const; + +// bool runMacro(QObject* object, +// QString& errorMessageOut) const; + + // ADD_NEW_METHODS_HERE + + virtual AString toString() const; + + private: + + const WuQMacroObjectTypeEnum::Enum m_objectType; + + const QString m_objectName; + + const QString m_objectToolTip; + + const QVariant m_value; + + // ADD_NEW_MEMBERS_HERE + + }; + +#ifdef __WU_Q_MACRO_COMMAND_DECLARE__ + // +#endif // __WU_Q_MACRO_COMMAND_DECLARE__ + +} // namespace +#endif //__WU_Q_MACRO_COMMAND_H__ diff --git a/src/Common/WuQMacroGroup.cxx b/src/Common/WuQMacroGroup.cxx new file mode 100644 index 000000000..f0432e473 --- /dev/null +++ b/src/Common/WuQMacroGroup.cxx @@ -0,0 +1,222 @@ + +/*LICENSE_START*/ +/* + * Copyright (C) 2018 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + +#define __WU_Q_MACRO_GROUP_DECLARE__ +#include "WuQMacroGroup.h" +#undef __WU_Q_MACRO_GROUP_DECLARE__ + +#include "CaretAssert.h" +#include "WuQMacro.h" + +using namespace caret; + + + +/** + * \class caret::WuQMacroGroup + * \brief Contains a group of macros + * \ingroup WuQMacro + */ + +/** + * Constructor. + * + * @param groupName + * Name of group + */ +WuQMacroGroup::WuQMacroGroup(const QString& groupName) +: CaretObjectTracksModification(), +m_groupName(groupName) +{ + +} + +/** + * Destructor. + */ +WuQMacroGroup::~WuQMacroGroup() +{ + for (auto m : m_macros) { + delete m; + } + m_macros.clear(); +} + +/** + * @return Name of group + */ +QString +WuQMacroGroup::getGroupName() const +{ + return m_groupName; +} + +/** + * Set name of group + * + * @param groupName + * Name of group + */ +void +WuQMacroGroup::setGroupName(const QString& groupName) +{ + if (m_groupName != groupName) { + m_groupName = groupName; + setModified(); + } +} + +/** + * Add a macro + * + * @param macro + * Macro added to this group. This group will take ownership of the macro. + */ +void +WuQMacroGroup::addMacro(WuQMacro* macro) +{ + m_macros.push_back(macro); + setModified(); +} + +/** + * @return Number of macros in group + */ +int32_t +WuQMacroGroup::getNumberOfMacros() const +{ + return m_macros.size(); +} + +/** + * Get the macro with the given name + * + * @param name + * Name of the macro + * @return + * Pointer to macro with the name or NULL if not found + */ +WuQMacro* +WuQMacroGroup::getMacroByName(const QString& name) +{ + for (auto m : m_macros) { + if (m->getName() == name) { + return m; + } + } + return NULL; +} + +/** + * Get the macro with the given name + * + * @param name + * Name of the macro + * @return + * Pointer to macro with the name or NULL if not found + */ +const WuQMacro* +WuQMacroGroup::getMacroByName(const QString& name) const +{ + for (auto m : m_macros) { + if (m->getName() == name) { + return m; + } + } + return NULL; +} + +/** + * @return + * Macro at the given index + * @param index + * Index of the macro + */ +WuQMacro* +WuQMacroGroup::getMacroAtIndex(const int32_t index) +{ + CaretAssertVectorIndex(m_macros, index); + return m_macros[index]; +} + +/** + * @return + * Macro at the given index + * @param index + * Index of the macro + */ +const WuQMacro* +WuQMacroGroup::getMacroAtIndex(const int32_t index) const +{ + CaretAssertVectorIndex(m_macros, index); + return m_macros[index]; +} + +/** + * Delete the given macro from this group + * + * @param macro + * Macro for deletion + */ +void +WuQMacroGroup::deleteMacro(const WuQMacro* macro) +{ + const int32_t count = getNumberOfMacros(); + for (int32_t i = 0; i < count; i++) { + if (getMacroAtIndex(i) == macro) { + deleteMacroAtIndex(i); + break; + } + } +} + +/** + * Delete the macro at the given index + * + * @param index + * Index of macro for deletion + */ +void +WuQMacroGroup::deleteMacroAtIndex(const int32_t index) +{ + CaretAssertVectorIndex(m_macros, index); + WuQMacro* macro = m_macros[index]; + m_macros.erase(m_macros.begin() + index); + delete macro; + + setModified(); +} + + +/** + * Get a description of this object's content. + * @return String describing this object's content. + */ +AString +WuQMacroGroup::toString() const +{ + QString s("WuQMacroGroup name=" + m_groupName + "\n"); + for (auto m : m_macros) { + s.append(m->toString() + "\n"); + } + return s; +} + diff --git a/src/Common/WuQMacroGroup.h b/src/Common/WuQMacroGroup.h new file mode 100644 index 000000000..c387554c6 --- /dev/null +++ b/src/Common/WuQMacroGroup.h @@ -0,0 +1,85 @@ +#ifndef __WU_Q_MACRO_GROUP_H__ +#define __WU_Q_MACRO_GROUP_H__ + +/*LICENSE_START*/ +/* + * Copyright (C) 2018 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + + + +#include + +#include "CaretObjectTracksModification.h" + + + +namespace caret { + + class WuQMacro; + + class WuQMacroGroup : public CaretObjectTracksModification { + + public: + WuQMacroGroup(const QString& groupName); + + virtual ~WuQMacroGroup(); + + WuQMacroGroup(const WuQMacroGroup&) = delete; + + WuQMacroGroup& operator=(const WuQMacroGroup&) = delete; + + QString getGroupName() const; + + void setGroupName(const QString& groupName); + + void addMacro(WuQMacro* macro); + + int32_t getNumberOfMacros() const; + + WuQMacro* getMacroByName(const QString& name); + + const WuQMacro* getMacroByName(const QString& name) const; + + WuQMacro* getMacroAtIndex(const int32_t index); + + const WuQMacro* getMacroAtIndex(const int32_t index) const; + + void deleteMacro(const WuQMacro* macro); + + void deleteMacroAtIndex(const int32_t index); + + // ADD_NEW_METHODS_HERE + + virtual AString toString() const; + + private: + QString m_groupName; + + std::vector m_macros; + + // ADD_NEW_MEMBERS_HERE + + }; + +#ifdef __WU_Q_MACRO_GROUP_DECLARE__ + // +#endif // __WU_Q_MACRO_GROUP_DECLARE__ + +} // namespace +#endif //__WU_Q_MACRO_GROUP_H__ diff --git a/src/Common/WuQMacroModeEnum.cxx b/src/Common/WuQMacroModeEnum.cxx new file mode 100644 index 000000000..13019462f --- /dev/null +++ b/src/Common/WuQMacroModeEnum.cxx @@ -0,0 +1,379 @@ + +/*LICENSE_START*/ +/* + * Copyright (C) 2018 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + +#include +#define __WU_Q_MACRO_MODE_ENUM_DECLARE__ +#include "WuQMacroModeEnum.h" +#undef __WU_Q_MACRO_MODE_ENUM_DECLARE__ + +#include "CaretAssert.h" + +using namespace caret; + + +/** + * \class caret::WuQMacroModeEnum + * \brief + * + * + * + * Using this enumerated type in the GUI with an EnumComboBoxTemplate + * + * Header File (.h) + * Forward declare the data type: + * class EnumComboBoxTemplate; + * + * Declare the member: + * EnumComboBoxTemplate* m_wuQMacroModeEnumComboBox; + * + * Declare a slot that is called when user changes selection + * private slots: + * void wuQMacroModeEnumComboBoxItemActivated(); + * + * Implementation File (.cxx) + * Include the header files + * #include "EnumComboBoxTemplate.h" + * #include "WuQMacroModeEnum.h" + * + * Instatiate: + * m_wuQMacroModeEnumComboBox = new EnumComboBoxTemplate(this); + * m_wuQMacroModeEnumComboBox->setup(); + * + * Get notified when the user changes the selection: + * QObject::connect(m_wuQMacroModeEnumComboBox, SIGNAL(itemActivated()), + * this, SLOT(wuQMacroModeEnumComboBoxItemActivated())); + * + * Update the selection: + * m_wuQMacroModeEnumComboBox->setSelectedItem(NEW_VALUE); + * + * Read the selection: + * const WuQMacroModeEnum::Enum VARIABLE = m_wuQMacroModeEnumComboBox->getSelectedItem(); + * + */ + +/** + * Constructor. + * + * @param enumValue + * An enumerated value. + * @param name + * Name of enumerated value. + * + * @param guiName + * User-friendly name for use in user-interface. + */ +WuQMacroModeEnum::WuQMacroModeEnum(const Enum enumValue, + const AString& name, + const AString& guiName) +{ + this->enumValue = enumValue; + this->integerCode = integerCodeCounter++; + this->name = name; + this->guiName = guiName; +} + +/** + * Destructor. + */ +WuQMacroModeEnum::~WuQMacroModeEnum() +{ +} + +/** + * Initialize the enumerated metadata. + */ +void +WuQMacroModeEnum::initialize() +{ + if (initializedFlag) { + return; + } + initializedFlag = true; + + enumData.push_back(WuQMacroModeEnum(OFF, + "OFF", + "Off")); + + enumData.push_back(WuQMacroModeEnum(RECORDING, + "RECORDING", + "Recording")); + + enumData.push_back(WuQMacroModeEnum(RUNNING, + "RUNNING", + "Running")); + +} + +/** + * Find the data for and enumerated value. + * @param enumValue + * The enumerated value. + * @return Pointer to data for this enumerated type + * or NULL if no data for type or if type is invalid. + */ +const WuQMacroModeEnum* +WuQMacroModeEnum::findData(const Enum enumValue) +{ + if (initializedFlag == false) initialize(); + + size_t num = enumData.size(); + for (size_t i = 0; i < num; i++) { + const WuQMacroModeEnum* d = &enumData[i]; + if (d->enumValue == enumValue) { + return d; + } + } + + return NULL; +} + +/** + * Get a string representation of the enumerated type. + * @param enumValue + * Enumerated value. + * @return + * String representing enumerated value. + */ +AString +WuQMacroModeEnum::toName(Enum enumValue) { + if (initializedFlag == false) initialize(); + + const WuQMacroModeEnum* enumInstance = findData(enumValue); + return enumInstance->name; +} + +/** + * Get an enumerated value corresponding to its name. + * @param name + * Name of enumerated value. + * @param isValidOut + * If not NULL, it is set indicating that a + * enum value exists for the input name. + * @return + * Enumerated value. + */ +WuQMacroModeEnum::Enum +WuQMacroModeEnum::fromName(const AString& name, bool* isValidOut) +{ + if (initializedFlag == false) initialize(); + + bool validFlag = false; + Enum enumValue = WuQMacroModeEnum::enumData[0].enumValue; + + for (std::vector::iterator iter = enumData.begin(); + iter != enumData.end(); + iter++) { + const WuQMacroModeEnum& d = *iter; + if (d.name == name) { + enumValue = d.enumValue; + validFlag = true; + break; + } + } + + if (isValidOut != 0) { + *isValidOut = validFlag; + } + else if (validFlag == false) { + CaretAssertMessage(0, AString("Name " + name + "failed to match enumerated value for type WuQMacroModeEnum")); + } + return enumValue; +} + +/** + * Get a GUI string representation of the enumerated type. + * @param enumValue + * Enumerated value. + * @return + * String representing enumerated value. + */ +AString +WuQMacroModeEnum::toGuiName(Enum enumValue) { + if (initializedFlag == false) initialize(); + + const WuQMacroModeEnum* enumInstance = findData(enumValue); + return enumInstance->guiName; +} + +/** + * Get an enumerated value corresponding to its GUI name. + * @param s + * Name of enumerated value. + * @param isValidOut + * If not NULL, it is set indicating that a + * enum value exists for the input name. + * @return + * Enumerated value. + */ +WuQMacroModeEnum::Enum +WuQMacroModeEnum::fromGuiName(const AString& guiName, bool* isValidOut) +{ + if (initializedFlag == false) initialize(); + + bool validFlag = false; + Enum enumValue = WuQMacroModeEnum::enumData[0].enumValue; + + for (std::vector::iterator iter = enumData.begin(); + iter != enumData.end(); + iter++) { + const WuQMacroModeEnum& d = *iter; + if (d.guiName == guiName) { + enumValue = d.enumValue; + validFlag = true; + break; + } + } + + if (isValidOut != 0) { + *isValidOut = validFlag; + } + else if (validFlag == false) { + CaretAssertMessage(0, AString("guiName " + guiName + "failed to match enumerated value for type WuQMacroModeEnum")); + } + return enumValue; +} + +/** + * Get the integer code for a data type. + * + * @return + * Integer code for data type. + */ +int32_t +WuQMacroModeEnum::toIntegerCode(Enum enumValue) +{ + if (initializedFlag == false) initialize(); + const WuQMacroModeEnum* enumInstance = findData(enumValue); + return enumInstance->integerCode; +} + +/** + * Find the data type corresponding to an integer code. + * + * @param integerCode + * Integer code for enum. + * @param isValidOut + * If not NULL, on exit isValidOut will indicate if + * integer code is valid. + * @return + * Enum for integer code. + */ +WuQMacroModeEnum::Enum +WuQMacroModeEnum::fromIntegerCode(const int32_t integerCode, bool* isValidOut) +{ + if (initializedFlag == false) initialize(); + + bool validFlag = false; + Enum enumValue = WuQMacroModeEnum::enumData[0].enumValue; + + for (std::vector::iterator iter = enumData.begin(); + iter != enumData.end(); + iter++) { + const WuQMacroModeEnum& enumInstance = *iter; + if (enumInstance.integerCode == integerCode) { + enumValue = enumInstance.enumValue; + validFlag = true; + break; + } + } + + if (isValidOut != 0) { + *isValidOut = validFlag; + } + else if (validFlag == false) { + CaretAssertMessage(0, AString("Integer code " + AString::number(integerCode) + "failed to match enumerated value for type WuQMacroModeEnum")); + } + return enumValue; +} + +/** + * Get all of the enumerated type values. The values can be used + * as parameters to toXXX() methods to get associated metadata. + * + * @param allEnums + * A vector that is OUTPUT containing all of the enumerated values. + */ +void +WuQMacroModeEnum::getAllEnums(std::vector& allEnums) +{ + if (initializedFlag == false) initialize(); + + allEnums.clear(); + + for (std::vector::iterator iter = enumData.begin(); + iter != enumData.end(); + iter++) { + allEnums.push_back(iter->enumValue); + } +} + +/** + * Get all of the names of the enumerated type values. + * + * @param allNames + * A vector that is OUTPUT containing all of the names of the enumerated values. + * @param isSorted + * If true, the names are sorted in alphabetical order. + */ +void +WuQMacroModeEnum::getAllNames(std::vector& allNames, const bool isSorted) +{ + if (initializedFlag == false) initialize(); + + allNames.clear(); + + for (std::vector::iterator iter = enumData.begin(); + iter != enumData.end(); + iter++) { + allNames.push_back(WuQMacroModeEnum::toName(iter->enumValue)); + } + + if (isSorted) { + std::sort(allNames.begin(), allNames.end()); + } +} + +/** + * Get all of the GUI names of the enumerated type values. + * + * @param allNames + * A vector that is OUTPUT containing all of the GUI names of the enumerated values. + * @param isSorted + * If true, the names are sorted in alphabetical order. + */ +void +WuQMacroModeEnum::getAllGuiNames(std::vector& allGuiNames, const bool isSorted) +{ + if (initializedFlag == false) initialize(); + + allGuiNames.clear(); + + for (std::vector::iterator iter = enumData.begin(); + iter != enumData.end(); + iter++) { + allGuiNames.push_back(WuQMacroModeEnum::toGuiName(iter->enumValue)); + } + + if (isSorted) { + std::sort(allGuiNames.begin(), allGuiNames.end()); + } +} + diff --git a/src/Common/WuQMacroModeEnum.h b/src/Common/WuQMacroModeEnum.h new file mode 100644 index 000000000..4acc8714c --- /dev/null +++ b/src/Common/WuQMacroModeEnum.h @@ -0,0 +1,106 @@ +#ifndef __WU_Q_MACRO_MODE_ENUM_H__ +#define __WU_Q_MACRO_MODE_ENUM_H__ + +/*LICENSE_START*/ +/* + * Copyright (C) 2018 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + + +#include +#include +#include "AString.h" + +namespace caret { + +class WuQMacroModeEnum { + +public: + /** + * Enumerated values. + */ + enum Enum { + /** */ + OFF, + /** */ + RECORDING, + /** */ + RUNNING + }; + + + ~WuQMacroModeEnum(); + + static AString toName(Enum enumValue); + + static Enum fromName(const AString& name, bool* isValidOut); + + static AString toGuiName(Enum enumValue); + + static Enum fromGuiName(const AString& guiName, bool* isValidOut); + + static int32_t toIntegerCode(Enum enumValue); + + static Enum fromIntegerCode(const int32_t integerCode, bool* isValidOut); + + static void getAllEnums(std::vector& allEnums); + + static void getAllNames(std::vector& allNames, const bool isSorted); + + static void getAllGuiNames(std::vector& allGuiNames, const bool isSorted); + +private: + WuQMacroModeEnum(const Enum enumValue, + const AString& name, + const AString& guiName); + + static const WuQMacroModeEnum* findData(const Enum enumValue); + + /** Holds all instance of enum values and associated metadata */ + static std::vector enumData; + + /** Initialize instances that contain the enum values and metadata */ + static void initialize(); + + /** Indicates instance of enum values and metadata have been initialized */ + static bool initializedFlag; + + /** Auto generated integer codes */ + static int32_t integerCodeCounter; + + /** The enumerated type value for an instance */ + Enum enumValue; + + /** The integer code associated with an enumerated value */ + int32_t integerCode; + + /** The name, a text string that is identical to the enumerated value */ + AString name; + + /** A user-friendly name that is displayed in the GUI */ + AString guiName; +}; + +#ifdef __WU_Q_MACRO_MODE_ENUM_DECLARE__ +std::vector WuQMacroModeEnum::enumData; +bool WuQMacroModeEnum::initializedFlag = false; +int32_t WuQMacroModeEnum::integerCodeCounter = 0; +#endif // __WU_Q_MACRO_MODE_ENUM_DECLARE__ + +} // namespace +#endif //__WU_Q_MACRO_MODE_ENUM_H__ diff --git a/src/Common/WuQMacroObjectTypeEnum.cxx b/src/Common/WuQMacroObjectTypeEnum.cxx new file mode 100644 index 000000000..96e072b3b --- /dev/null +++ b/src/Common/WuQMacroObjectTypeEnum.cxx @@ -0,0 +1,428 @@ + +/*LICENSE_START*/ +/* + * Copyright (C) 2018 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + +#include +#define __WU_Q_MACRO_OBJECT_TYPE_ENUM_DECLARE__ +#include "WuQMacroObjectTypeEnum.h" +#undef __WU_Q_MACRO_OBJECT_TYPE_ENUM_DECLARE__ + +#include "CaretAssert.h" + +using namespace caret; + + +/** + * \class caret::WuQMacroObjectTypeEnum + * \brief Enumerated type for specific type of object in macro + * + * Using this enumerated type in the GUI with an EnumComboBoxTemplate + * + * Header File (.h) + * Forward declare the data type: + * class EnumComboBoxTemplate; + * + * Declare the member: + * EnumComboBoxTemplate* m_wuQMacroObjectTypeEnumComboBox; + * + * Declare a slot that is called when user changes selection + * private slots: + * void wuQMacroObjectTypeEnumComboBoxItemActivated(); + * + * Implementation File (.cxx) + * Include the header files + * #include "EnumComboBoxTemplate.h" + * #include "WuQMacroObjectTypeEnum.h" + * + * Instatiate: + * m_wuQMacroObjectTypeEnumComboBox = new EnumComboBoxTemplate(this); + * m_wuQMacroObjectTypeEnumComboBox->setup(); + * + * Get notified when the user changes the selection: + * QObject::connect(m_wuQMacroObjectTypeEnumComboBox, SIGNAL(itemActivated()), + * this, SLOT(wuQMacroObjectTypeEnumComboBoxItemActivated())); + * + * Update the selection: + * m_wuQMacroObjectTypeEnumComboBox->setSelectedItem(NEW_VALUE); + * + * Read the selection: + * const WuQMacroObjectTypeEnum::Enum VARIABLE = m_wuQMacroObjectTypeEnumComboBox->getSelectedItem(); + * + */ + +/** + * Constructor. + * + * @param enumValue + * An enumerated value. + * @param name + * Name of enumerated value. + * + * @param guiName + * User-friendly name for use in user-interface. + */ +WuQMacroObjectTypeEnum::WuQMacroObjectTypeEnum(const Enum enumValue, + const AString& name, + const AString& guiName) +{ + this->enumValue = enumValue; + this->integerCode = integerCodeCounter++; + this->name = name; + this->guiName = guiName; +} + +/** + * Destructor. + */ +WuQMacroObjectTypeEnum::~WuQMacroObjectTypeEnum() +{ +} + +/** + * Initialize the enumerated metadata. + */ +void +WuQMacroObjectTypeEnum::initialize() +{ + if (initializedFlag) { + return; + } + initializedFlag = true; + + enumData.push_back(WuQMacroObjectTypeEnum(INVALID, + "INVALID", + "Invalid")); + + enumData.push_back(WuQMacroObjectTypeEnum(ACTION, + "ACTION", + "QAction")); + + enumData.push_back(WuQMacroObjectTypeEnum(CHECK_BOX, + "CHECK_BOX", + "QCheckBox")); + + enumData.push_back(WuQMacroObjectTypeEnum(COMBO_BOX, + "COMBO_BOX", + "QComboBox")); + + enumData.push_back(WuQMacroObjectTypeEnum(DOUBLE_SPIN_BOX, + "DOUBLE_SPIN_BOX", + "QDoubleSpinBox")); + + enumData.push_back(WuQMacroObjectTypeEnum(LINE_EDIT, + "LINE_EDIT", + "QLineEdit")); + + enumData.push_back(WuQMacroObjectTypeEnum(LIST_WIDGET, + "LIST_WIDGET", + "QListWidget")); + + enumData.push_back(WuQMacroObjectTypeEnum(MENU, + "MENU", + "QMenu")); + + enumData.push_back(WuQMacroObjectTypeEnum(MOUSE_USER_EVENT, + "MOUSE_USER_EVENT", + "QMouseEvent")); + + enumData.push_back(WuQMacroObjectTypeEnum(PUSH_BUTTON, + "PUSH_BUTTON", + "QPushButton")); + + enumData.push_back(WuQMacroObjectTypeEnum(RADIO_BUTTON, + "RADIO_BUTTON", + "QRadioButton")); + + enumData.push_back(WuQMacroObjectTypeEnum(SLIDER, + "SLIDER", + "QSlider")); + + enumData.push_back(WuQMacroObjectTypeEnum(SPIN_BOX, + "SPIN_BOX", + "QSpinBox")); + + enumData.push_back(WuQMacroObjectTypeEnum(TAB_BAR, + "TAB_BAR", + "QTabBar")); + + enumData.push_back(WuQMacroObjectTypeEnum(TAB_WIDGET, + "TAB_WIDGET", + "QTabWidget")); + + enumData.push_back(WuQMacroObjectTypeEnum(TOOL_BUTTON, + "TOOL_BUTTON", + "QToolButton")); +} + +/** + * Find the data for and enumerated value. + * @param enumValue + * The enumerated value. + * @return Pointer to data for this enumerated type + * or NULL if no data for type or if type is invalid. + */ +const WuQMacroObjectTypeEnum* +WuQMacroObjectTypeEnum::findData(const Enum enumValue) +{ + if (initializedFlag == false) initialize(); + + size_t num = enumData.size(); + for (size_t i = 0; i < num; i++) { + const WuQMacroObjectTypeEnum* d = &enumData[i]; + if (d->enumValue == enumValue) { + return d; + } + } + + return NULL; +} + +/** + * Get a string representation of the enumerated type. + * @param enumValue + * Enumerated value. + * @return + * String representing enumerated value. + */ +AString +WuQMacroObjectTypeEnum::toName(Enum enumValue) { + if (initializedFlag == false) initialize(); + + const WuQMacroObjectTypeEnum* enumInstance = findData(enumValue); + return enumInstance->name; +} + +/** + * Get an enumerated value corresponding to its name. + * @param name + * Name of enumerated value. + * @param isValidOut + * If not NULL, it is set indicating that a + * enum value exists for the input name. + * @return + * Enumerated value. + */ +WuQMacroObjectTypeEnum::Enum +WuQMacroObjectTypeEnum::fromName(const AString& name, bool* isValidOut) +{ + if (initializedFlag == false) initialize(); + + bool validFlag = false; + Enum enumValue = WuQMacroObjectTypeEnum::enumData[0].enumValue; + + for (std::vector::iterator iter = enumData.begin(); + iter != enumData.end(); + iter++) { + const WuQMacroObjectTypeEnum& d = *iter; + if (d.name == name) { + enumValue = d.enumValue; + validFlag = true; + break; + } + } + + if (isValidOut != 0) { + *isValidOut = validFlag; + } + else if (validFlag == false) { + CaretAssertMessage(0, AString("Name " + name + "failed to match enumerated value for type WuQMacroObjectTypeEnum")); + } + return enumValue; +} + +/** + * Get a GUI string representation of the enumerated type. + * @param enumValue + * Enumerated value. + * @return + * String representing enumerated value. + */ +AString +WuQMacroObjectTypeEnum::toGuiName(Enum enumValue) { + if (initializedFlag == false) initialize(); + + const WuQMacroObjectTypeEnum* enumInstance = findData(enumValue); + return enumInstance->guiName; +} + +/** + * Get an enumerated value corresponding to its GUI name. + * @param s + * Name of enumerated value. + * @param isValidOut + * If not NULL, it is set indicating that a + * enum value exists for the input name. + * @return + * Enumerated value. + */ +WuQMacroObjectTypeEnum::Enum +WuQMacroObjectTypeEnum::fromGuiName(const AString& guiName, bool* isValidOut) +{ + if (initializedFlag == false) initialize(); + + bool validFlag = false; + Enum enumValue = WuQMacroObjectTypeEnum::enumData[0].enumValue; + + for (std::vector::iterator iter = enumData.begin(); + iter != enumData.end(); + iter++) { + const WuQMacroObjectTypeEnum& d = *iter; + if (d.guiName == guiName) { + enumValue = d.enumValue; + validFlag = true; + break; + } + } + + if (isValidOut != 0) { + *isValidOut = validFlag; + } + else if (validFlag == false) { + CaretAssertMessage(0, AString("guiName " + guiName + "failed to match enumerated value for type WuQMacroObjectTypeEnum")); + } + return enumValue; +} + +/** + * Get the integer code for a data type. + * + * @return + * Integer code for data type. + */ +int32_t +WuQMacroObjectTypeEnum::toIntegerCode(Enum enumValue) +{ + if (initializedFlag == false) initialize(); + const WuQMacroObjectTypeEnum* enumInstance = findData(enumValue); + return enumInstance->integerCode; +} + +/** + * Find the data type corresponding to an integer code. + * + * @param integerCode + * Integer code for enum. + * @param isValidOut + * If not NULL, on exit isValidOut will indicate if + * integer code is valid. + * @return + * Enum for integer code. + */ +WuQMacroObjectTypeEnum::Enum +WuQMacroObjectTypeEnum::fromIntegerCode(const int32_t integerCode, bool* isValidOut) +{ + if (initializedFlag == false) initialize(); + + bool validFlag = false; + Enum enumValue = WuQMacroObjectTypeEnum::enumData[0].enumValue; + + for (std::vector::iterator iter = enumData.begin(); + iter != enumData.end(); + iter++) { + const WuQMacroObjectTypeEnum& enumInstance = *iter; + if (enumInstance.integerCode == integerCode) { + enumValue = enumInstance.enumValue; + validFlag = true; + break; + } + } + + if (isValidOut != 0) { + *isValidOut = validFlag; + } + else if (validFlag == false) { + CaretAssertMessage(0, AString("Integer code " + AString::number(integerCode) + "failed to match enumerated value for type WuQMacroObjectTypeEnum")); + } + return enumValue; +} + +/** + * Get all of the enumerated type values. The values can be used + * as parameters to toXXX() methods to get associated metadata. + * + * @param allEnums + * A vector that is OUTPUT containing all of the enumerated values. + */ +void +WuQMacroObjectTypeEnum::getAllEnums(std::vector& allEnums) +{ + if (initializedFlag == false) initialize(); + + allEnums.clear(); + + for (std::vector::iterator iter = enumData.begin(); + iter != enumData.end(); + iter++) { + allEnums.push_back(iter->enumValue); + } +} + +/** + * Get all of the names of the enumerated type values. + * + * @param allNames + * A vector that is OUTPUT containing all of the names of the enumerated values. + * @param isSorted + * If true, the names are sorted in alphabetical order. + */ +void +WuQMacroObjectTypeEnum::getAllNames(std::vector& allNames, const bool isSorted) +{ + if (initializedFlag == false) initialize(); + + allNames.clear(); + + for (std::vector::iterator iter = enumData.begin(); + iter != enumData.end(); + iter++) { + allNames.push_back(WuQMacroObjectTypeEnum::toName(iter->enumValue)); + } + + if (isSorted) { + std::sort(allNames.begin(), allNames.end()); + } +} + +/** + * Get all of the GUI names of the enumerated type values. + * + * @param allNames + * A vector that is OUTPUT containing all of the GUI names of the enumerated values. + * @param isSorted + * If true, the names are sorted in alphabetical order. + */ +void +WuQMacroObjectTypeEnum::getAllGuiNames(std::vector& allGuiNames, const bool isSorted) +{ + if (initializedFlag == false) initialize(); + + allGuiNames.clear(); + + for (std::vector::iterator iter = enumData.begin(); + iter != enumData.end(); + iter++) { + allGuiNames.push_back(WuQMacroObjectTypeEnum::toGuiName(iter->enumValue)); + } + + if (isSorted) { + std::sort(allGuiNames.begin(), allGuiNames.end()); + } +} + diff --git a/src/Common/WuQMacroObjectTypeEnum.h b/src/Common/WuQMacroObjectTypeEnum.h new file mode 100644 index 000000000..054397ed2 --- /dev/null +++ b/src/Common/WuQMacroObjectTypeEnum.h @@ -0,0 +1,168 @@ +#ifndef __WU_Q_MACRO_OBJECT_TYPE_ENUM_H__ +#define __WU_Q_MACRO_OBJECT_TYPE_ENUM_H__ + +/*LICENSE_START*/ +/* + * Copyright (C) 2018 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + + +#include +#include +#include "AString.h" + +namespace caret { + +class WuQMacroObjectTypeEnum { + +public: + /** + * Enumerated values. + */ + enum Enum { + /** Invalid type */ + INVALID, + /** a QAction */ + ACTION, + /** a QCheckBox */ + CHECK_BOX, + /** a QComboBox */ + COMBO_BOX, + /** a QDoubleSpinBox */ + DOUBLE_SPIN_BOX, + /** a QLineEdit */ + LINE_EDIT, + /** a QListWidget */ + LIST_WIDGET, + /** a QMenu */ + MENU, + /** a Mouse Event */ + MOUSE_USER_EVENT, + /** a QPushButton */ + PUSH_BUTTON, + /** a QRadioButton */ + RADIO_BUTTON, + /** a QSlider */ + SLIDER, + /** a QSpinBox */ + SPIN_BOX, + /** a QTabBar */ + TAB_BAR, + /** a QTabWidget */ + TAB_WIDGET, + /** a QToolButton */ + TOOL_BUTTON + }; + +/* + switch (m_objectType) { + case WuQMacroObjectTypeEnum::ACTION: + break; + case WuQMacroObjectTypeEnum::CHECK_BOX: + break; + case WuQMacroObjectTypeEnum::COMBO_BOX: + break; + case WuQMacroObjectTypeEnum::DOUBLE_SPIN_BOX: + break; + case WuQMacroObjectTypeEnum::INVALID: + break; + case WuQMacroObjectTypeEnum::LINE_EDIT: + break; + case WuQMacroObjectTypeEnum::LIST_WIDGET: + break; + case WuQMacroObjectTypeEnum::MENU: + break; + case WuQMacroObjectTypeEnum::MOUSE_USER_EVENT: + break; + case WuQMacroObjectTypeEnum::PUSH_BUTTON: + break; + case WuQMacroObjectTypeEnum::RADIO_BUTTON: + break; + case WuQMacroObjectTypeEnum::SLIDER: + break; + case WuQMacroObjectTypeEnum::SPIN_BOX: + break; + case WuQMacroObjectTypeEnum::TAB_BAR: + break; + case WuQMacroObjectTypeEnum::TAB_WIDGET: + break; + case WuQMacroObjectTypeEnum::TOOL_BUTTON: + break; + } + */ + + ~WuQMacroObjectTypeEnum(); + + static AString toName(Enum enumValue); + + static Enum fromName(const AString& name, bool* isValidOut); + + static AString toGuiName(Enum enumValue); + + static Enum fromGuiName(const AString& guiName, bool* isValidOut); + + static int32_t toIntegerCode(Enum enumValue); + + static Enum fromIntegerCode(const int32_t integerCode, bool* isValidOut); + + static void getAllEnums(std::vector& allEnums); + + static void getAllNames(std::vector& allNames, const bool isSorted); + + static void getAllGuiNames(std::vector& allGuiNames, const bool isSorted); + +private: + WuQMacroObjectTypeEnum(const Enum enumValue, + const AString& name, + const AString& guiName); + + static const WuQMacroObjectTypeEnum* findData(const Enum enumValue); + + /** Holds all instance of enum values and associated metadata */ + static std::vector enumData; + + /** Initialize instances that contain the enum values and metadata */ + static void initialize(); + + /** Indicates instance of enum values and metadata have been initialized */ + static bool initializedFlag; + + /** Auto generated integer codes */ + static int32_t integerCodeCounter; + + /** The enumerated type value for an instance */ + Enum enumValue; + + /** The integer code associated with an enumerated value */ + int32_t integerCode; + + /** The name, a text string that is identical to the enumerated value */ + AString name; + + /** A user-friendly name that is displayed in the GUI */ + AString guiName; +}; + +#ifdef __WU_Q_MACRO_OBJECT_TYPE_ENUM_DECLARE__ +std::vector WuQMacroObjectTypeEnum::enumData; +bool WuQMacroObjectTypeEnum::initializedFlag = false; +int32_t WuQMacroObjectTypeEnum::integerCodeCounter = 0; +#endif // __WU_Q_MACRO_OBJECT_TYPE_ENUM_DECLARE__ + +} // namespace +#endif //__WU_Q_MACRO_OBJECT_TYPE_ENUM_H__ diff --git a/src/GuiQt/BrainBrowserWindow.cxx b/src/GuiQt/BrainBrowserWindow.cxx index 89c659516..2d917685d 100644 --- a/src/GuiQt/BrainBrowserWindow.cxx +++ b/src/GuiQt/BrainBrowserWindow.cxx @@ -84,7 +84,6 @@ #include "GapsAndMargins.h" #include "GuiManager.h" #include "LockAspectWarningDialog.h" -#include "MacroPrototype.h" #include "ModelSurface.h" #include "ModelSurfaceMontage.h" #include "ModelWholeBrain.h" @@ -109,6 +108,7 @@ #include "TileTabsConfigurationModifier.h" #include "WuQDataEntryDialog.h" #include "WuQDoubleSpinBox.h" +#include "WuQMacroMenu.h" #include "WuQMessageBox.h" #include "WuQTabBar.h" #include "WuQtUtilities.h" @@ -1534,14 +1534,13 @@ BrainBrowserWindow::createMenus() menubar->addMenu(connectMenu); } + menubar->addMenu(new WuQMacroMenu(this, + menubar)); + QMenu* developMenu = createMenuDevelop(); m_developMenuAction = menubar->addMenu(developMenu); m_developMenuAction->setVisible(prefs->isDevelopMenuEnabled()); - QMenu* macroMenu = new MacroMenu(this); - m_macroMenuAction = menubar->addMenu(macroMenu); - m_macroMenuAction->setVisible(prefs->isDevelopMenuEnabled()); - menubar->addMenu(createMenuWindow()); menubar->addMenu(createMenuHelp()); diff --git a/src/GuiQt/BrainBrowserWindow.h b/src/GuiQt/BrainBrowserWindow.h index 98510d1db..f902bf1ee 100644 --- a/src/GuiQt/BrainBrowserWindow.h +++ b/src/GuiQt/BrainBrowserWindow.h @@ -414,8 +414,6 @@ namespace caret { QAction* m_developerGraphicsTimingAction; QAction* m_developerExportVtkFileAction; - QAction* m_macroMenuAction; - QAction* m_overlayToolBoxAction; QAction* m_windowMenuLockWindowAspectRatioAction; diff --git a/src/GuiQt/BrainBrowserWindowToolBarSlicePlane.cxx b/src/GuiQt/BrainBrowserWindowToolBarSlicePlane.cxx index 0d441ae7b..35205c4d2 100644 --- a/src/GuiQt/BrainBrowserWindowToolBarSlicePlane.cxx +++ b/src/GuiQt/BrainBrowserWindowToolBarSlicePlane.cxx @@ -34,7 +34,7 @@ #include "BrowserTabContent.h" #include "CaretAssert.h" #include "CaretLogger.h" -#include "MacroPrototype.h" +#include "WuQMacroManager.h" #include "WuQWidgetObjectGroup.h" #include "WuQtUtilities.h" @@ -50,11 +50,20 @@ using namespace caret; /** * Constructor. + * + * @param parentToolBar + * The parent toolbar */ BrainBrowserWindowToolBarSlicePlane::BrainBrowserWindowToolBarSlicePlane(BrainBrowserWindowToolBar* parentToolBar) : BrainBrowserWindowToolBarComponent(parentToolBar), m_parentToolBar(parentToolBar) { + WuQMacroManager* macroManager = WuQMacroManager::instance(); + CaretAssert(macroManager); + const QString objectNamePrefix("Window_" + + QString::number(m_parentToolBar->browserWindowIndex + 1) + + ":ToolBar:SlicePlane:"); + QIcon parasagittalIcon; const bool parasagittalIconValid = WuQtUtilities::loadIcon(":/ToolBar/view-plane-parasagittal.png", @@ -78,8 +87,9 @@ m_parentToolBar(parentToolBar) if (parasagittalIconValid) { m_volumePlaneParasagittalToolButtonAction->setIcon(parasagittalIcon); } - m_volumePlaneParasagittalToolButtonAction->setObjectName("ToolBar_Volume_Parasagittal_Slice_View"); - WuQObject::watchObjectForMacroRecording(m_volumePlaneParasagittalToolButtonAction); + m_volumePlaneParasagittalToolButtonAction->setObjectName(objectNamePrefix + + "Parasagittal_Slice_View:Action"); + macroManager->addMacroSupportToObject(m_volumePlaneParasagittalToolButtonAction); m_volumePlaneCoronalToolButtonAction = WuQtUtilities::createAction(VolumeSliceViewPlaneEnum::toGuiNameAbbreviation(VolumeSliceViewPlaneEnum::CORONAL), "View the CORONAL slice", @@ -88,8 +98,9 @@ m_parentToolBar(parentToolBar) if (coronalIconValid) { m_volumePlaneCoronalToolButtonAction->setIcon(coronalIcon); } - m_volumePlaneCoronalToolButtonAction->setObjectName("ToolBar_Volume_Coronal_Slice_View"); - WuQObject::watchObjectForMacroRecording(m_volumePlaneCoronalToolButtonAction); + m_volumePlaneCoronalToolButtonAction->setObjectName(objectNamePrefix + + "Coronal_Slice_View:Action"); + macroManager->addMacroSupportToObject(m_volumePlaneCoronalToolButtonAction); m_volumePlaneAxialToolButtonAction = WuQtUtilities::createAction(VolumeSliceViewPlaneEnum::toGuiNameAbbreviation(VolumeSliceViewPlaneEnum::AXIAL), "View the AXIAL slice", @@ -98,7 +109,8 @@ m_parentToolBar(parentToolBar) if (axialIconValid) { m_volumePlaneAxialToolButtonAction->setIcon(axialIcon); } - m_volumePlaneAxialToolButtonAction->setObjectName("ToolBar_Volume_Axial_Slice_View"); + m_volumePlaneAxialToolButtonAction->setObjectName(objectNamePrefix + + "Axial_Slice_View:Action"); WuQObject::watchObjectForMacroRecording(m_volumePlaneAxialToolButtonAction); m_volumePlaneAllToolButtonAction = WuQtUtilities::createAction(VolumeSliceViewPlaneEnum::toGuiNameAbbreviation(VolumeSliceViewPlaneEnum::ALL), @@ -107,8 +119,9 @@ m_parentToolBar(parentToolBar) this); m_volumePlaneAllToolButtonAction->setCheckable(true); m_volumePlaneAllToolButtonAction->setMenu(createViewAllSlicesLayoutMenu()); - m_volumePlaneAllToolButtonAction->setObjectName("ToolBar_Volume_All_Slices_View"); - WuQObject::watchObjectForMacroRecording(m_volumePlaneAllToolButtonAction); + m_volumePlaneAllToolButtonAction->setObjectName(objectNamePrefix + + "All_Slices_View:Action"); + macroManager->addMacroSupportToObject(m_volumePlaneAllToolButtonAction); m_volumePlaneActionGroup = new QActionGroup(this); @@ -126,6 +139,9 @@ m_parentToolBar(parentToolBar) this, this, SLOT(volumePlaneResetToolButtonTriggered(bool))); + m_volumePlaneResetToolButtonAction->setObjectName(objectNamePrefix + + "Reset_View:Action"); + macroManager->addMacroSupportToObject(m_volumePlaneResetToolButtonAction); QToolButton* volumePlaneParasagittalToolButton = new QToolButton(); @@ -166,12 +182,17 @@ m_parentToolBar(parentToolBar) slicePlaneCustomToolButton->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Fixed); WuQtUtilities::setToolButtonStyleForQt5Mac(slicePlaneCustomToolButton); + macroManager->addMacroSupportToObject(m_parentToolBar->customViewAction); m_volumeAxisCrosshairsToolButtonAction = new QAction("", this); m_volumeAxisCrosshairsToolButtonAction->setCheckable(true); m_volumeAxisCrosshairsToolButtonAction->setToolTip("Show crosshairs on slice planes"); QObject::connect(m_volumeAxisCrosshairsToolButtonAction, &QAction::triggered, this, &BrainBrowserWindowToolBarSlicePlane::volumeAxisCrosshairsTriggered); + m_volumeAxisCrosshairsToolButtonAction->setObjectName(objectNamePrefix + + "Show_Volume_Slice_Crosshairs:Action"); + macroManager->addMacroSupportToObject(m_volumeAxisCrosshairsToolButtonAction); + QToolButton* volumeCrosshairsToolButton = new QToolButton(); QPixmap xhairPixmap = createCrosshairsIcon(volumeCrosshairsToolButton); volumeCrosshairsToolButton->setDefaultAction(m_volumeAxisCrosshairsToolButtonAction); @@ -184,6 +205,10 @@ m_parentToolBar(parentToolBar) m_volumeAxisCrosshairLabelsToolButtonAction->setToolTip("Show crosshair slice plane labels"); QObject::connect(m_volumeAxisCrosshairLabelsToolButtonAction, &QAction::triggered, this, &BrainBrowserWindowToolBarSlicePlane::volumeAxisCrosshairLabelsTriggered); + m_volumeAxisCrosshairLabelsToolButtonAction->setObjectName(objectNamePrefix + + "Show_Volume_Slice_Labels:Action"); + macroManager->addMacroSupportToObject(m_volumeAxisCrosshairLabelsToolButtonAction); + QToolButton* volumeCrosshairLabelsToolButton = new QToolButton(); volumeCrosshairLabelsToolButton->setDefaultAction(m_volumeAxisCrosshairLabelsToolButtonAction); QPixmap labelsPixmap = createCrosshairLabelsIcon(volumeCrosshairLabelsToolButton); diff --git a/src/GuiQt/BrainBrowserWindowToolBarSliceSelection.cxx b/src/GuiQt/BrainBrowserWindowToolBarSliceSelection.cxx index 2d3cfd58a..aed11f177 100644 --- a/src/GuiQt/BrainBrowserWindowToolBarSliceSelection.cxx +++ b/src/GuiQt/BrainBrowserWindowToolBarSliceSelection.cxx @@ -43,13 +43,13 @@ #include "EventManager.h" #include "EventUpdateVolumeEditingToolBar.h" #include "GuiManager.h" -#include "MacroPrototype.h" #include "ModelVolume.h" #include "ModelWholeBrain.h" #include "VolumeFile.h" #include "VolumeSliceInterpolationEdgeEffectsMaskingEnum.h" #include "VolumeSliceProjectionTypeEnum.h" #include "WuQFactory.h" +#include "WuQMacroManager.h" #include "WuQWidgetObjectGroup.h" #include "WuQtUtilities.h" @@ -70,6 +70,12 @@ BrainBrowserWindowToolBarSliceSelection::BrainBrowserWindowToolBarSliceSelection : BrainBrowserWindowToolBarComponent(parentToolBar), m_parentToolBar(parentToolBar) { + WuQMacroManager* macroManager = WuQMacroManager::instance(); + CaretAssert(macroManager); + const QString objectNamePrefix("Window_" + + QString::number(m_parentToolBar->browserWindowIndex + 1) + + ":ToolBar:SliceSelection:"); + QAction* volumeIndicesOriginToolButtonAction = WuQtUtilities::createAction("O\nR\nI\nG\nI\nN", "Set the slice indices to the origin, \n" "stereotaxic coordinate (0, 0, 0)", @@ -79,9 +85,10 @@ m_parentToolBar(parentToolBar) QToolButton* volumeIndicesOriginToolButton = new QToolButton; volumeIndicesOriginToolButton->setDefaultAction(volumeIndicesOriginToolButtonAction); WuQtUtilities::setToolButtonStyleForQt5Mac(volumeIndicesOriginToolButton); - volumeIndicesOriginToolButtonAction->setObjectName("ToolBar_Move_Volume_Slices_to_Origin"); + volumeIndicesOriginToolButtonAction->setObjectName(objectNamePrefix + + "Move_Volume_Slices_to_Origin:Action"); volumeIndicesOriginToolButtonAction->setParent(volumeIndicesOriginToolButton); - WuQObject::watchObjectForMacroRecording(volumeIndicesOriginToolButtonAction); + macroManager->addMacroSupportToObject(volumeIndicesOriginToolButtonAction); QLabel* parasagittalLabel = new QLabel("P:"); QLabel* coronalLabel = new QLabel("C:"); @@ -90,21 +97,30 @@ m_parentToolBar(parentToolBar) m_volumeIndicesParasagittalCheckBox = new QCheckBox(" "); WuQtUtilities::setToolTipAndStatusTip(m_volumeIndicesParasagittalCheckBox, "Enable/Disable display of PARASAGITTAL slice"); + m_volumeIndicesParasagittalCheckBox->setObjectName(objectNamePrefix + + "Enable_Parasagittal_Slice:CheckBox"); QObject::connect(m_volumeIndicesParasagittalCheckBox, SIGNAL(stateChanged(int)), this, SLOT(volumeIndicesParasagittalCheckBoxStateChanged(int))); + macroManager->addMacroSupportToObject(m_volumeIndicesParasagittalCheckBox); m_volumeIndicesCoronalCheckBox = new QCheckBox(" "); WuQtUtilities::setToolTipAndStatusTip(m_volumeIndicesCoronalCheckBox, "Enable/Disable display of CORONAL slice"); + m_volumeIndicesCoronalCheckBox->setObjectName(objectNamePrefix + + "Enable_Coronal_Slice:CheckBox"); QObject::connect(m_volumeIndicesCoronalCheckBox, SIGNAL(stateChanged(int)), this, SLOT(volumeIndicesCoronalCheckBoxStateChanged(int))); + macroManager->addMacroSupportToObject(m_volumeIndicesCoronalCheckBox); m_volumeIndicesAxialCheckBox = new QCheckBox(" "); WuQtUtilities::setToolTipAndStatusTip(m_volumeIndicesAxialCheckBox, "Enable/Disable display of AXIAL slice"); + m_volumeIndicesAxialCheckBox->setObjectName(objectNamePrefix + + "Enable_Axial_Slice:CheckBox"); QObject::connect(m_volumeIndicesAxialCheckBox, SIGNAL(stateChanged(int)), this, SLOT(volumeIndicesAxialCheckBoxStateChanged(int))); + macroManager->addMacroSupportToObject(m_volumeIndicesAxialCheckBox); const int sliceIndexSpinBoxWidth = 55; const int sliceCoordinateSpinBoxWidth = 60; @@ -113,64 +129,64 @@ m_parentToolBar(parentToolBar) m_volumeIndicesParasagittalSpinBox->setFixedWidth(sliceIndexSpinBoxWidth); WuQtUtilities::setToolTipAndStatusTip(m_volumeIndicesParasagittalSpinBox, "Change the selected PARASAGITTAL slice"); -// QObject::connect(m_volumeIndicesParasagittalSpinBox, SIGNAL(valueChanged(int)), -// this, SLOT(volumeIndicesParasagittalSpinBoxValueChanged(int))); - m_volumeIndicesParasagittalSpinBox->setObjectName("ToolBar_Volume_Parasagittal_Slice_Index"); - WuQObject::connect(m_volumeIndicesParasagittalSpinBox, SIGNAL(valueChanged(int)), + QObject::connect(m_volumeIndicesParasagittalSpinBox, SIGNAL(valueChanged(int)), this, SLOT(volumeIndicesParasagittalSpinBoxValueChanged(int))); + m_volumeIndicesParasagittalSpinBox->setObjectName(objectNamePrefix + + "Volume_Parasagittal_Slice_Index:SpinBox"); + macroManager->addMacroSupportToObject(m_volumeIndicesParasagittalSpinBox); m_volumeIndicesCoronalSpinBox = WuQFactory::newSpinBox(); m_volumeIndicesCoronalSpinBox->setFixedWidth(sliceIndexSpinBoxWidth); WuQtUtilities::setToolTipAndStatusTip(m_volumeIndicesCoronalSpinBox, "Change the selected CORONAL slice"); -// QObject::connect(m_volumeIndicesCoronalSpinBox, SIGNAL(valueChanged(int)), -// this, SLOT(volumeIndicesCoronalSpinBoxValueChanged(int))); - m_volumeIndicesCoronalSpinBox->setObjectName("ToolBar_Volume_Coronal_Slice_Index"); - WuQObject::connect(m_volumeIndicesCoronalSpinBox, SIGNAL(valueChanged(int)), + QObject::connect(m_volumeIndicesCoronalSpinBox, SIGNAL(valueChanged(int)), this, SLOT(volumeIndicesCoronalSpinBoxValueChanged(int))); + m_volumeIndicesCoronalSpinBox->setObjectName(objectNamePrefix + + "Volume_Coronal_Slice_Index:SpinBox"); + macroManager->addMacroSupportToObject(m_volumeIndicesCoronalSpinBox); m_volumeIndicesAxialSpinBox = WuQFactory::newSpinBox(); m_volumeIndicesAxialSpinBox->setFixedWidth(sliceIndexSpinBoxWidth); WuQtUtilities::setToolTipAndStatusTip(m_volumeIndicesAxialSpinBox, "Change the selected AXIAL slice"); -// QObject::connect(m_volumeIndicesAxialSpinBox, SIGNAL(valueChanged(int)), -// this, SLOT(volumeIndicesAxialSpinBoxValueChanged(int))); - m_volumeIndicesAxialSpinBox->setObjectName("ToolBar_Volume_Axial_Slice_Index"); - WuQObject::connect(m_volumeIndicesAxialSpinBox, SIGNAL(valueChanged(int)), + QObject::connect(m_volumeIndicesAxialSpinBox, SIGNAL(valueChanged(int)), this, SLOT(volumeIndicesAxialSpinBoxValueChanged(int))); + m_volumeIndicesAxialSpinBox->setObjectName(objectNamePrefix + + "Volume_Axial_Slice_Index:SpinBox"); + macroManager->addMacroSupportToObject(m_volumeIndicesAxialSpinBox); m_volumeIndicesXcoordSpinBox = WuQFactory::newDoubleSpinBox(); m_volumeIndicesXcoordSpinBox->setDecimals(1); m_volumeIndicesXcoordSpinBox->setFixedWidth(sliceCoordinateSpinBoxWidth); WuQtUtilities::setToolTipAndStatusTip(m_volumeIndicesXcoordSpinBox, "Adjust coordinate to select PARASAGITTAL slice"); -// QObject::connect(m_volumeIndicesXcoordSpinBox, SIGNAL(valueChanged(double)), -// this, SLOT(volumeIndicesXcoordSpinBoxValueChanged(double))); - m_volumeIndicesXcoordSpinBox->setObjectName("ToolBar_Volume_Parasagittal_Coordinate"); - WuQObject::connect(m_volumeIndicesXcoordSpinBox, SIGNAL(valueChanged(double)), + QObject::connect(m_volumeIndicesXcoordSpinBox, SIGNAL(valueChanged(double)), this, SLOT(volumeIndicesXcoordSpinBoxValueChanged(double))); + m_volumeIndicesXcoordSpinBox->setObjectName(objectNamePrefix + + "Volume_Parasagittal_Coordinate:DoubleSpinBox"); + macroManager->addMacroSupportToObject(m_volumeIndicesXcoordSpinBox); m_volumeIndicesYcoordSpinBox = WuQFactory::newDoubleSpinBox(); m_volumeIndicesYcoordSpinBox->setDecimals(1); m_volumeIndicesYcoordSpinBox->setFixedWidth(sliceCoordinateSpinBoxWidth); WuQtUtilities::setToolTipAndStatusTip(m_volumeIndicesYcoordSpinBox, "Adjust coordinate to select CORONAL slice"); -// QObject::connect(m_volumeIndicesYcoordSpinBox, SIGNAL(valueChanged(double)), -// this, SLOT(volumeIndicesYcoordSpinBoxValueChanged(double))); - m_volumeIndicesYcoordSpinBox->setObjectName("ToolBar_Volume_Coronal_Coordinate"); - WuQObject::connect(m_volumeIndicesYcoordSpinBox, SIGNAL(valueChanged(double)), + QObject::connect(m_volumeIndicesYcoordSpinBox, SIGNAL(valueChanged(double)), this, SLOT(volumeIndicesYcoordSpinBoxValueChanged(double))); + m_volumeIndicesYcoordSpinBox->setObjectName(objectNamePrefix + + "Volume_Coronal_Coordinate:DoubleSpinBox"); + macroManager->addMacroSupportToObject(m_volumeIndicesYcoordSpinBox); m_volumeIndicesZcoordSpinBox = WuQFactory::newDoubleSpinBox(); m_volumeIndicesZcoordSpinBox->setDecimals(1); m_volumeIndicesZcoordSpinBox->setFixedWidth(sliceCoordinateSpinBoxWidth); WuQtUtilities::setToolTipAndStatusTip(m_volumeIndicesZcoordSpinBox, "Adjust coordinate to select AXIAL slice"); -// QObject::connect(m_volumeIndicesZcoordSpinBox, SIGNAL(valueChanged(double)), -// this, SLOT(volumeIndicesZcoordSpinBoxValueChanged(double))); - m_volumeIndicesZcoordSpinBox->setObjectName("ToolBar_Volume_Axial_Coordinate"); - WuQObject::connect(m_volumeIndicesZcoordSpinBox, SIGNAL(valueChanged(double)), + QObject::connect(m_volumeIndicesZcoordSpinBox, SIGNAL(valueChanged(double)), this, SLOT(volumeIndicesZcoordSpinBoxValueChanged(double))); + m_volumeIndicesZcoordSpinBox->setObjectName(objectNamePrefix + + "Volume_Axial_Coordinate:DoubleSpinBox"); + macroManager->addMacroSupportToObject(m_volumeIndicesZcoordSpinBox); const AString idToolTipText = ("When selected: If there is an identification operation " "in ths tab or any other tab with the same yoking status " @@ -196,6 +212,9 @@ m_parentToolBar(parentToolBar) } volumeIDToolButton->setDefaultAction(m_volumeIdentificationUpdatesSlicesAction); WuQtUtilities::setToolButtonStyleForQt5Mac(volumeIDToolButton); + m_volumeIdentificationUpdatesSlicesAction->setObjectName(objectNamePrefix + + "Move_Slice_to_ID:Action"); + macroManager->addMacroSupportToObject(m_volumeIdentificationUpdatesSlicesAction); m_volumeSliceProjectionTypeEnumComboBox = new EnumComboBoxTemplate(this); m_volumeSliceProjectionTypeEnumComboBox->setup(); @@ -204,16 +223,24 @@ m_parentToolBar(parentToolBar) this, SLOT(volumeSliceProjectionTypeEnumComboBoxItemActivated())); WuQtUtilities::setToolTipAndStatusTip(m_volumeSliceProjectionTypeEnumComboBox->getWidget(), "Chooses viewing orientation (oblique or orthogonal)"); + m_volumeSliceProjectionTypeEnumComboBox->getComboBox()->setObjectName(objectNamePrefix + + "Orthogonal/Oblique:ComboBox"); + macroManager->addMacroSupportToObject(m_volumeSliceProjectionTypeEnumComboBox->getComboBox()); m_obliqueMaskingAction = new QAction("M", this); m_obliqueMaskingAction->setToolTip(VolumeSliceInterpolationEdgeEffectsMaskingEnum::getToolTip()); m_obliqueMaskingAction->setCheckable(true); QObject::connect(m_obliqueMaskingAction, &QAction::triggered, this, &BrainBrowserWindowToolBarSliceSelection::obliqueMaskingActionTriggered); + m_obliqueMaskingAction->setObjectName(objectNamePrefix + + "Oblique_Masking:Action"); + macroManager->addMacroSupportToObject(m_obliqueMaskingAction); + QToolButton* obliqueMaskingToolButton = new QToolButton(); obliqueMaskingToolButton->setDefaultAction(m_obliqueMaskingAction); WuQtUtilities::setToolButtonStyleForQt5Mac(obliqueMaskingToolButton); + QGridLayout* gridLayout = new QGridLayout(this); WuQtUtilities::setLayoutSpacingAndMargins(gridLayout, 0, 0); gridLayout->addWidget(m_volumeIndicesParasagittalCheckBox, 0, 0); diff --git a/src/GuiQt/CMakeLists.txt b/src/GuiQt/CMakeLists.txt index d163b5445..53e82d978 100644 --- a/src/GuiQt/CMakeLists.txt +++ b/src/GuiQt/CMakeLists.txt @@ -204,6 +204,13 @@ ELSE() WuQGroupBoxExclusiveWidget.h WuQImageLabel.h WuQListWidget.h + WuQMacroCreateDialog.h + WuQMacroDialog.h + WuQMacroExecutor.h + WuQMacroManager.h + WuQMacroMenu.h + WuQMacroSignalEmitter.h + WuQMacroSignalWatcher.h WuQMessageBox.h WuQwtPlot.h WuQSpecialIncrementDoubleSpinBox.h @@ -452,6 +459,13 @@ WuQGridLayoutGroup.h WuQGroupBoxExclusiveWidget.h WuQImageLabel.h WuQListWidget.h +WuQMacroCreateDialog.h +WuQMacroDialog.h +WuQMacroExecutor.h +WuQMacroManager.h +WuQMacroMenu.h +WuQMacroSignalEmitter.h +WuQMacroSignalWatcher.h WuQMessageBox.h WuQwtPlot.h WuQSpecialIncrementDoubleSpinBox.h @@ -686,6 +700,13 @@ WuQGridLayoutGroup.cxx WuQGroupBoxExclusiveWidget.cxx WuQImageLabel.cxx WuQListWidget.cxx +WuQMacroCreateDialog.cxx +WuQMacroDialog.cxx +WuQMacroExecutor.cxx +WuQMacroManager.cxx +WuQMacroMenu.cxx +WuQMacroSignalEmitter.cxx +WuQMacroSignalWatcher.cxx WuQMessageBox.cxx WuQwtPlot.cxx WuQSpecialIncrementDoubleSpinBox.cxx diff --git a/src/GuiQt/GuiManager.cxx b/src/GuiQt/GuiManager.cxx index f240c934c..113e2c76b 100644 --- a/src/GuiQt/GuiManager.cxx +++ b/src/GuiQt/GuiManager.cxx @@ -121,6 +121,7 @@ #include "Surface.h" #include "TileTabsConfigurationDialog.h" #include "VolumeMappableInterface.h" +#include "WuQMacroManager.h" #include "WuQMessageBox.h" #include "WuQtUtilities.h" @@ -1151,6 +1152,13 @@ GuiManager::processBringAllWindowsToFront() w->raise(); } } + + std::vector macroDialogs = WuQMacroManager::instance()->getNonModalDialogs(); + for (auto md : macroDialogs) { + if (md->isVisible()) { + md->raise(); + } + } } /** @@ -1639,8 +1647,15 @@ GuiManager::reparentNonModalDialogs(BrainBrowserWindow* closingBrainBrowserWindo ? NULL : *(validWindows.begin())); + std::vector allNonModalDialogs(this->nonModalDialogs.begin(), + this->nonModalDialogs.end()); + std::vector macroDialogs = WuQMacroManager::instance()->getNonModalDialogs(); + allNonModalDialogs.insert(allNonModalDialogs.end(), + macroDialogs.begin(), + macroDialogs.end()); + if (firstBrainBrowserWindow != NULL) { - for (auto dialog : this->nonModalDialogs) { + for (auto dialog : allNonModalDialogs) { QWidget* dialogParent = dialog->parentWidget(); if (validWindows.find(dialogParent) == validWindows.end()) { const bool wasVisible = dialog->isVisible(); @@ -1654,16 +1669,10 @@ GuiManager::reparentNonModalDialogs(BrainBrowserWindow* closingBrainBrowserWindo dialog->hide(); } } - - /* - * Update any dialogs that are WuQ non modal dialogs. - */ - WuQDialogNonModal* wuqNonModalDialog = dynamic_cast(dialog); - if (wuqNonModalDialog != NULL) { - wuqNonModalDialog->updateDialog(); - } } } + + updateNonModalDialogs(); } /** @@ -1672,7 +1681,13 @@ GuiManager::reparentNonModalDialogs(BrainBrowserWindow* closingBrainBrowserWindo void GuiManager::updateNonModalDialogs() { - reparentNonModalDialogs(NULL); + for (auto dialog : this->nonModalDialogs) { + WuQDialogNonModal* wuqNonModalDialog = dynamic_cast(dialog); + if (wuqNonModalDialog != NULL) { + wuqNonModalDialog->updateDialog(); + } + } + WuQMacroManager::instance()->updateNonModalDialogs(); } /** diff --git a/src/GuiQt/MapYokingGroupComboBox.cxx b/src/GuiQt/MapYokingGroupComboBox.cxx index 5fc13d810..9adeece77 100644 --- a/src/GuiQt/MapYokingGroupComboBox.cxx +++ b/src/GuiQt/MapYokingGroupComboBox.cxx @@ -32,8 +32,8 @@ #include "EnumComboBoxTemplate.h" #include "EventManager.h" #include "EventMapYokingValidation.h" -#include "MacroPrototype.h" #include "Overlay.h" +#include "WuQMacroManager.h" #include "WuQMessageBox.h" #include "WuQtUtilities.h" @@ -86,7 +86,7 @@ MapYokingGroupComboBox::MapYokingGroupComboBox(QObject* parent, if ( ! objectName.isEmpty()) { QWidget* encapsulatedComboBox = m_comboBox->getWidget(); encapsulatedComboBox->setObjectName(objectName); - WuQObject::watchObjectForMacroRecording(encapsulatedComboBox); + WuQMacroManager::instance()->addMacroSupportToObject(encapsulatedComboBox); } } diff --git a/src/GuiQt/OverlayViewController.cxx b/src/GuiQt/OverlayViewController.cxx index 4af05d9ae..007f350c3 100644 --- a/src/GuiQt/OverlayViewController.cxx +++ b/src/GuiQt/OverlayViewController.cxx @@ -50,11 +50,11 @@ #include "FileInformation.h" #include "FilePathNamePrefixCompactor.h" #include "GuiManager.h" -#include "MacroPrototype.h" #include "MapYokingGroupComboBox.h" #include "Overlay.h" #include "UsernamePasswordWidget.h" #include "WuQFactory.h" +#include "WuQMacroManager.h" #include "WuQMessageBox.h" #include "WuQtUtilities.h" #include "WuQGridLayoutGroup.h" @@ -98,83 +98,83 @@ OverlayViewController::OverlayViewController(const Qt::Orientation orientation, } const QComboBox::SizeAdjustPolicy comboSizePolicy = QComboBox::AdjustToContentsOnFirstShow; //QComboBox::AdjustToContents; - QString objectNamePrefix("Overlay_" + WuQMacroManager* macroManager = WuQMacroManager::instance(); + CaretAssert(macroManager); + QString objectNamePrefix("Window_" + + AString::number(browserWindowIndex + 1) + + ":Overlay_" + AString::number(overlayIndex + 1) + "_" + ((orientation == Qt::Horizontal) - ? "H_" - : "V_")); + ? "H" + : "V") + + ":"); /* * Enabled Check Box */ const QString checkboxText = ((orientation == Qt::Horizontal) ? " " : " "); this->enabledCheckBox = new QCheckBox(checkboxText); this->enabledCheckBox->setObjectName(objectNamePrefix - + "OnOff_CheckBox"); -// QObject::connect(this->enabledCheckBox, SIGNAL(clicked(bool)), -// this, SLOT(enabledCheckBoxClicked(bool))); - WuQObject::connect(this->enabledCheckBox, SIGNAL(clicked(bool)), - this, SLOT(enabledCheckBoxClicked(bool))); + + "OnOff:CheckBox"); + QObject::connect(this->enabledCheckBox, SIGNAL(clicked(bool)), + this, SLOT(enabledCheckBoxClicked(bool))); this->enabledCheckBox->setToolTip("Enables display of this overlay"); + macroManager->addMacroSupportToObject(this->enabledCheckBox); /* * File Selection Combo Box */ this->fileComboBox = WuQFactory::newComboBox(); this->fileComboBox->setObjectName(objectNamePrefix - + "File_Selection_ComboBox"); + + "File_Selection:ComboBox"); this->fileComboBox->setMinimumWidth(minComboBoxWidth); this->fileComboBox->setMaximumWidth(maxComboBoxWidth); -// QObject::connect(this->fileComboBox, SIGNAL(activated(int)), -// this, SLOT(fileComboBoxSelected(int))); - WuQObject::connect(this->fileComboBox, SIGNAL(activated(int)), - this, SLOT(fileComboBoxSelected(int))); + QObject::connect(this->fileComboBox, SIGNAL(activated(int)), + this, SLOT(fileComboBoxSelected(int))); this->fileComboBox->setToolTip("Selects file for this overlay"); this->fileComboBox->setSizeAdjustPolicy(comboSizePolicy); + macroManager->addMacroSupportToObject(this->fileComboBox); /* * Map Index Spin Box */ m_mapIndexSpinBox = WuQFactory::newSpinBox(); this->m_mapIndexSpinBox->setObjectName(objectNamePrefix - + "Map_Index_SpinBox"); -// QObject::connect(m_mapIndexSpinBox, SIGNAL(valueChanged(int)), -// this, SLOT(mapIndexSpinBoxValueChanged(int))); - WuQObject::connect(m_mapIndexSpinBox, SIGNAL(valueChanged(int)), + + "Map_Index:SpinBox"); + QObject::connect(m_mapIndexSpinBox, SIGNAL(valueChanged(int)), this, SLOT(mapIndexSpinBoxValueChanged(int))); m_mapIndexSpinBox->setToolTip("Select map by its index"); + macroManager->addMacroSupportToObject(m_mapIndexSpinBox); /* * Map Name Combo Box */ this->mapNameComboBox = WuQFactory::newComboBox(); this->mapNameComboBox->setObjectName(objectNamePrefix - + "Map_Selection_ComboBox"); + + "Map_Selection:ComboBox"); this->mapNameComboBox->setMinimumWidth(minComboBoxWidth); this->mapNameComboBox->setMaximumWidth(maxComboBoxWidth); -// QObject::connect(this->mapNameComboBox, SIGNAL(activated(int)), -// this, SLOT(mapNameComboBoxSelected(int))); - WuQObject::connect(this->mapNameComboBox, SIGNAL(activated(int)), + QObject::connect(this->mapNameComboBox, SIGNAL(activated(int)), this, SLOT(mapNameComboBoxSelected(int))); this->mapNameComboBox->setToolTip("Select map by its name"); this->mapNameComboBox->setSizeAdjustPolicy(comboSizePolicy); + macroManager->addMacroSupportToObject(this->mapNameComboBox); /* * Opacity double spin box */ this->opacityDoubleSpinBox = WuQFactory::newDoubleSpinBox(); this->opacityDoubleSpinBox->setObjectName(objectNamePrefix - + "Opacity_SpinBox"); + + "Opacity:SpinBox"); this->opacityDoubleSpinBox->setMinimum(0.0); this->opacityDoubleSpinBox->setMaximum(1.0); this->opacityDoubleSpinBox->setSingleStep(0.10); this->opacityDoubleSpinBox->setDecimals(1); this->opacityDoubleSpinBox->setFixedWidth(50); -// QObject::connect(this->opacityDoubleSpinBox, SIGNAL(valueChanged(double)), -// this, SLOT(opacityDoubleSpinBoxValueChanged(double))); - WuQObject::connect(this->opacityDoubleSpinBox, SIGNAL(valueChanged(double)), + QObject::connect(this->opacityDoubleSpinBox, SIGNAL(valueChanged(double)), this, SLOT(opacityDoubleSpinBoxValueChanged(double))); this->opacityDoubleSpinBox->setToolTip("Opacity (0.0=transparent, 1.0=opaque)"); + macroManager->addMacroSupportToObject(this->opacityDoubleSpinBox); /* * ColorBar Tool Button @@ -183,10 +183,11 @@ OverlayViewController::OverlayViewController(const Qt::Orientation orientation, ":/LayersPanel/colorbar.png", "Display color bar for this overlay", (objectNamePrefix - + "Show_Color_Bar"), + + "Show_Color_Bar:ToolButton"), this, SLOT(colorBarActionTriggered(bool))); m_colorBarToolButton->setCheckable(true); + macroManager->addMacroSupportToObject(m_colorBarToolButton); // /* // * ColorBar Tool Button @@ -218,14 +219,18 @@ OverlayViewController::OverlayViewController(const Qt::Orientation orientation, this, this, SLOT(settingsActionTriggered())); + this->settingsAction->setObjectName(objectNamePrefix + + "ShowSettingsDialog:ToolButton"); if (settingsIconValid) { this->settingsAction->setIcon(settingsIcon); } QToolButton* settingsToolButton = new QToolButton(); settingsToolButton->setDefaultAction(this->settingsAction); + macroManager->addMacroSupportToObject(this->settingsAction); /* * Construction Tool Button + * Note: macro support is on each action in menu in 'createConstructionMenu' */ QIcon constructionIcon; const bool constructionIconValid = WuQtUtilities::loadIcon(":/LayersPanel/construction.png", @@ -237,7 +242,9 @@ OverlayViewController::OverlayViewController(const Qt::Orientation orientation, this->constructionAction->setIcon(constructionIcon); } m_constructionToolButton = new QToolButton(); - QMenu* constructionMenu = createConstructionMenu(m_constructionToolButton); + QMenu* constructionMenu = createConstructionMenu(m_constructionToolButton, + (objectNamePrefix + + "ConstructionMenu:")); this->constructionAction->setMenu(constructionMenu); m_constructionToolButton->setDefaultAction(this->constructionAction); m_constructionToolButton->setPopupMode(QToolButton::InstantPopup); @@ -253,10 +260,11 @@ OverlayViewController::OverlayViewController(const Qt::Orientation orientation, /* * Yoking Group + * Note: macro support is in the class MapYokingGroupComboBox */ m_mapYokingGroupComboBox = new MapYokingGroupComboBox(this, (objectNamePrefix - + "Map_Yoking_Selection")); + + "Map_Yoking_Selection:ComboBox")); m_mapYokingGroupComboBox->getWidget()->setStatusTip("Synchronize enabled status and map indices)"); m_mapYokingGroupComboBox->getWidget()->setToolTip("Yoke to Overlay Mapped Files"); #ifdef CARET_OS_MACOSX @@ -848,53 +856,91 @@ OverlayViewController::updateGraphicsWindow() * Create the construction menu. * @param parent * Parent widget. + * @param menuActionNamePrefix + * Prefix for macros */ QMenu* -OverlayViewController::createConstructionMenu(QWidget* parent) +OverlayViewController::createConstructionMenu(QWidget* parent, + const AString& menuActionNamePrefix) { + WuQMacroManager* macroManager = WuQMacroManager::instance(); + CaretAssert(macroManager); + QMenu* menu = new QMenu(parent); QObject::connect(menu, SIGNAL(aboutToShow()), this, SLOT(menuConstructionAboutToShow())); - menu->addAction("Add Overlay Above", - this, - SLOT(menuAddOverlayAboveTriggered())); + QAction* addAboveAction = menu->addAction("Add Overlay Above", + this, + SLOT(menuAddOverlayAboveTriggered())); + addAboveAction->setObjectName(menuActionNamePrefix + + "Add_Overlay_Above:MenuAction"); + addAboveAction->setToolTip("Add an overlay above this overlay"); + macroManager->addMacroSupportToObject(addAboveAction); - menu->addAction("Add Overlay Below", - this, - SLOT(menuAddOverlayBelowTriggered())); + QAction* addBelowAction = menu->addAction("Add Overlay Below", + this, + SLOT(menuAddOverlayBelowTriggered())); + addBelowAction->setObjectName(menuActionNamePrefix + + "Add_Overlay_Below:MenuAction"); + addBelowAction->setToolTip("Add an overlay below this overlay"); + macroManager->addMacroSupportToObject(addBelowAction); menu->addSeparator(); - menu->addAction("Move Overlay Up", - this, - SLOT(menuMoveOverlayUpTriggered())); + QAction* moveUpAction = menu->addAction("Move Overlay Up", + this, + SLOT(menuMoveOverlayUpTriggered())); + moveUpAction->setObjectName(menuActionNamePrefix + + "Move_Overlay_Up:MenuAction"); + moveUpAction->setToolTip("Move this overlay up"); + macroManager->addMacroSupportToObject(moveUpAction); - menu->addAction("Move Overlay Down", - this, - SLOT(menuMoveOverlayDownTriggered())); + QAction* moveDownAction = menu->addAction("Move Overlay Down", + this, + SLOT(menuMoveOverlayDownTriggered())); + moveDownAction->setObjectName(menuActionNamePrefix + + "Move_Overlay_Down:MenuAction"); + moveDownAction->setToolTip("Move this overlay down"); + macroManager->addMacroSupportToObject(moveDownAction); menu->addSeparator(); - menu->addAction("Remove This Overlay", - this, - SLOT(menuRemoveOverlayTriggered())); + QAction* removeAction = menu->addAction("Remove This Overlay", + this, + SLOT(menuRemoveOverlayTriggered())); + removeAction->setObjectName(menuActionNamePrefix + + "Remove_Overlay:MenuAction"); + removeAction->setToolTip("Remove this overlay"); + macroManager->addMacroSupportToObject(removeAction); menu->addSeparator(); m_constructionReloadFileAction = menu->addAction("Reload Selected File", this, SLOT(menuReloadFileTriggered())); + m_constructionReloadFileAction->setObjectName(menuActionNamePrefix + + "Reload_Selected_File:MenuAction"); + m_constructionReloadFileAction->setToolTip("Reload file in this overlay"); + macroManager->addMacroSupportToObject(m_constructionReloadFileAction); menu->addSeparator(); m_copyPathAndFileNameToClipboardAction = menu->addAction("Copy Path and File Name to Clipboard", this, SLOT(menuCopyFileNameToClipBoard())); + m_copyPathAndFileNameToClipboardAction->setObjectName(menuActionNamePrefix + + "Copy_Path_And_File_Name_To_Clipboard:MenuAction"); + m_copyPathAndFileNameToClipboardAction->setToolTip("Copy path and file name of file in this overlay to clipboard"); + macroManager->addMacroSupportToObject(m_copyPathAndFileNameToClipboardAction); - menu->addAction("Copy Map Name to Clipboard", - this, - SLOT(menuCopyMapNameToClipBoard())); + QAction* copyMapNameAction = menu->addAction("Copy Map Name to Clipboard", + this, + SLOT(menuCopyMapNameToClipBoard())); + copyMapNameAction->setObjectName(menuActionNamePrefix + + "Copy_Map_Name_To_Clipboard:MenuAction"); + copyMapNameAction->setToolTip("Copy name of selected map to the clipboard"); + macroManager->addMacroSupportToObject(copyMapNameAction); return menu; diff --git a/src/GuiQt/OverlayViewController.h b/src/GuiQt/OverlayViewController.h index 70fb9c3ce..0954f4cd2 100644 --- a/src/GuiQt/OverlayViewController.h +++ b/src/GuiQt/OverlayViewController.h @@ -36,6 +36,7 @@ class QToolButton; namespace caret { + class AString; class MapYokingGroupComboBox; class Overlay; class WuQGridLayoutGroup; @@ -114,7 +115,8 @@ namespace caret { void updateGraphicsWindow(); - QMenu* createConstructionMenu(QWidget* parent); + QMenu* createConstructionMenu(QWidget* parent, + const AString& menuActionNamePrefix); void validateYokingSelection(); diff --git a/src/GuiQt/WuQMacroCreateDialog.cxx b/src/GuiQt/WuQMacroCreateDialog.cxx new file mode 100644 index 000000000..de45eece5 --- /dev/null +++ b/src/GuiQt/WuQMacroCreateDialog.cxx @@ -0,0 +1,185 @@ + +/*LICENSE_START*/ +/* + * Copyright (C) 2018 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + +#define __WU_Q_MACRO_CREATE_DIALOG_DECLARE__ +#include "WuQMacroCreateDialog.h" +#undef __WU_Q_MACRO_CREATE_DIALOG_DECLARE__ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "CaretAssert.h" +#include "CaretLogger.h" +#include "WuQMacro.h" +#include "WuQMacroGroup.h" +#include "WuQMacroManager.h" + +using namespace caret; + + + +/** + * \class caret::WuQMacroCreateDialog + * \brief Dialog for creating a new macro + * \ingroup WuQMacro + */ + +/** + * Constructor. + * + * @param parent + * The parent widget + */ +WuQMacroCreateDialog::WuQMacroCreateDialog(QWidget* parent) +: QDialog(parent) +{ + setWindowTitle("Record Macro"); + + m_macroGroups = WuQMacroManager::instance()->getMacroGroups(); + + QLabel* nameLabel = new QLabel("Macro name:"); + QLabel* descriptionLabel = new QLabel("Description:"); + QLabel* macroGroupLabel = new QLabel("Store macro in:"); + + m_macroNameLineEdit = new QLineEdit(); + m_macroNameLineEdit->setText(getDefaultMacroName()); + m_macroDescriptionTextEdit = new QPlainTextEdit(); + + m_macroGroupComboBox = new QComboBox(); + for (auto mg : m_macroGroups) { + m_macroGroupComboBox->addItem(mg->getGroupName()); + } + + QGridLayout* gridLayout = new QGridLayout(); + int row = 0; + gridLayout->addWidget(nameLabel, row, 0); + gridLayout->addWidget(m_macroNameLineEdit, row, 1); + row++; + gridLayout->addWidget(descriptionLabel, row, 0); + gridLayout->addWidget(m_macroDescriptionTextEdit, row, 1); + row++; + gridLayout->addWidget(macroGroupLabel, row, 0); + gridLayout->addWidget(m_macroGroupComboBox, row, 1); + row++; + + m_dialogButtonBox = new QDialogButtonBox(QDialogButtonBox::Ok + | QDialogButtonBox::Cancel); + QObject::connect(m_dialogButtonBox, &QDialogButtonBox::accepted, + this, &WuQMacroCreateDialog::accept); + QObject::connect(m_dialogButtonBox, &QDialogButtonBox::rejected, + this, &WuQMacroCreateDialog::reject); + + QVBoxLayout* dialogLayout = new QVBoxLayout(this); + dialogLayout->addLayout(gridLayout); + dialogLayout->addWidget(m_dialogButtonBox); +} + +/** + * Destructor. + */ +WuQMacroCreateDialog::~WuQMacroCreateDialog() +{ +} + +/** + * @return A default name for a macro + */ +QString +WuQMacroCreateDialog::getDefaultMacroName() const +{ + QString name(""); + bool foundFlag(false); + static int32_t minimumIndex = 1; + + for (int32_t i = minimumIndex; i < 10000; i++) { + name = ("Macro " + + QString::number(i)); + foundFlag = false; + for (auto mg : m_macroGroups) { + if (mg->getMacroByName(name) != NULL) { + foundFlag = true; + break; + } + } + + if ( ! foundFlag) { + /* + * If a default name was created, do not allow a "lower-numbered" + * default name. Start with same index since user may not use it. + */ + minimumIndex = i; + break; + } + } + + return name; +} + +/** + * Called when user clicks OK or Cancel + * + * @param r + * The dialog code (Accepted or Rejected) + */ +void +WuQMacroCreateDialog::done(int r) +{ + if (r == QDialog::Accepted) { + const QString name(m_macroNameLineEdit->text().trimmed()); + if (name.isEmpty()) { + QMessageBox::critical(this, + "Error", + "Name is missing", + QMessageBox::Ok, + QMessageBox::Ok); + return; + } + + m_macro = new WuQMacro(); + m_macro->setName(name); + m_macro->setDescription(m_macroDescriptionTextEdit->toPlainText()); + + const int32_t groupIndex = m_macroGroupComboBox->currentIndex(); + if (groupIndex >= 0) { + WuQMacroGroup* macroGroup = m_macroGroups[groupIndex]; + macroGroup->addMacro(m_macro); + } + } + + QDialog::done(r); +} + +/** + * @return Pointer to new macro. Macro has been added to a group. + * Do not delete the pointer. + */ +WuQMacro* +WuQMacroCreateDialog::getNewMacro() const +{ + return m_macro; +} + diff --git a/src/GuiQt/WuQMacroCreateDialog.h b/src/GuiQt/WuQMacroCreateDialog.h new file mode 100644 index 000000000..648641bc5 --- /dev/null +++ b/src/GuiQt/WuQMacroCreateDialog.h @@ -0,0 +1,83 @@ +#ifndef __WU_Q_MACRO_CREATE_DIALOG_H__ +#define __WU_Q_MACRO_CREATE_DIALOG_H__ + +/*LICENSE_START*/ +/* + * Copyright (C) 2018 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + + + +#include + +#include + +class QComboBox; +class QDialogButtonBox; +class QLineEdit; +class QPlainTextEdit; + + +namespace caret { + + class WuQMacro; + class WuQMacroGroup; + + class WuQMacroCreateDialog : public QDialog { + + Q_OBJECT + + public: + WuQMacroCreateDialog(QWidget* parent = 0); + + virtual ~WuQMacroCreateDialog(); + + WuQMacroCreateDialog(const WuQMacroCreateDialog&) = delete; + + WuQMacroCreateDialog& operator=(const WuQMacroCreateDialog&) = delete; + + WuQMacro* getNewMacro() const; + + // ADD_NEW_METHODS_HERE + + public slots: + virtual void done(int r) override; + + private: + QString getDefaultMacroName() const; + + std::vector m_macroGroups; + + QComboBox* m_macroGroupComboBox; + + QLineEdit* m_macroNameLineEdit; + + QPlainTextEdit* m_macroDescriptionTextEdit; + + QDialogButtonBox* m_dialogButtonBox; + + WuQMacro* m_macro = NULL; + + }; + +#ifdef __WU_Q_MACRO_CREATE_DIALOG_DECLARE__ + // +#endif // __WU_Q_MACRO_CREATE_DIALOG_DECLARE__ + +} // namespace +#endif //__WU_Q_MACRO_CREATE_DIALOG_H__ diff --git a/src/GuiQt/WuQMacroDialog.cxx b/src/GuiQt/WuQMacroDialog.cxx new file mode 100644 index 000000000..3a6542b32 --- /dev/null +++ b/src/GuiQt/WuQMacroDialog.cxx @@ -0,0 +1,271 @@ + +/*LICENSE_START*/ +/* + * Copyright (C) 2018 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + +#define __WU_Q_MACRO_DIALOG_DECLARE__ +#include "WuQMacroDialog.h" +#undef __WU_Q_MACRO_DIALOG_DECLARE__ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "CaretAssert.h" +#include "CaretLogger.h" +#include "WuQMacro.h" +#include "WuQMacroGroup.h" +#include "WuQMacroManager.h" + +using namespace caret; + +/** + * \class caret::WuQMacroDialog + * \brief Dialog for managing macros + * \ingroup WuQMacro + */ + +/** + * Constructor. + * + * @param defaultMacroName + * Default name for new macro + * @param parent + * The dialog's parent widget. + */ +WuQMacroDialog::WuQMacroDialog(QWidget* parent) +: QDialog(parent) +{ + setWindowTitle("Macros"); + this->setAttribute(Qt::WA_DeleteOnClose, false); + + m_macroGroups = WuQMacroManager::instance()->getMacroGroups(); + + QLabel* nameLabel = new QLabel("Macros:"); + QLabel* descriptionLabel = new QLabel("Description:"); + QLabel* macroGroupLabel = new QLabel("Macros in:"); + + m_macrosListWidget = new QListWidget(); + QObject::connect(m_macrosListWidget, &QListWidget::currentRowChanged, + this, &WuQMacroDialog::macrosListWidgetCurrentRowChanged); + + m_macroDescriptionTextEdit = new QPlainTextEdit(); + + m_macroGroupComboBox = new QComboBox(); + QObject::connect(m_macroGroupComboBox, static_cast(&QComboBox::activated), + this, &WuQMacroDialog::macroGroupBoxActivated); + + QGridLayout* gridLayout = new QGridLayout(); + int row = 0; + gridLayout->addWidget(macroGroupLabel, row, 0); + gridLayout->addWidget(m_macroGroupComboBox, row, 1); + row++; + gridLayout->addWidget(nameLabel, row, 0); + gridLayout->addWidget(m_macrosListWidget, row, 1); + row++; + gridLayout->addWidget(descriptionLabel, row, 0); + gridLayout->addWidget(m_macroDescriptionTextEdit, row, 1); + row++; + + m_dialogButtonBox = new QDialogButtonBox(QDialogButtonBox::Close); + m_editButton = m_dialogButtonBox->addButton("Edit...", QDialogButtonBox::ApplyRole); + m_runButton = m_dialogButtonBox->addButton("Run...", QDialogButtonBox::ApplyRole); + QObject::connect(m_dialogButtonBox, &QDialogButtonBox::rejected, + this, &WuQMacroDialog::close); + QObject::connect(m_dialogButtonBox, &QDialogButtonBox::clicked, + this, &WuQMacroDialog::buttonClicked); + + QVBoxLayout* dialogLayout = new QVBoxLayout(this); + dialogLayout->addLayout(gridLayout); + dialogLayout->addWidget(m_dialogButtonBox); + + updateDialogContents(); + + QPushButton* closeButton = m_dialogButtonBox->button(QDialogButtonBox::Close); + CaretAssert(closeButton); + closeButton->setDefault(true); +} + +/** + * Destructor. + */ +WuQMacroDialog::~WuQMacroDialog() +{ +} + +/** + * Called when a button in dialog is clicked + * + * @param button + * Button that was clicked. + */ +void +WuQMacroDialog::buttonClicked(QAbstractButton* button) +{ + if (button == m_editButton) { + editSelectedMacro(); + } + else if (button == m_runButton) { + runSelectedMacro(); + } +} + +/** + * Update content of the dialog + */ +void +WuQMacroDialog::updateDialogContents() +{ + QString selectedGroupName = m_macroGroupComboBox->currentText(); + + m_macroGroups = WuQMacroManager::instance()->getMacroGroups(); + + int32_t selectedIndex = 0; + int32_t groupCounter(0); + m_macroGroupComboBox->clear(); + for (auto mg : m_macroGroups) { + m_macroGroupComboBox->addItem(mg->getGroupName()); + if (selectedGroupName == mg->getGroupName()) { + selectedIndex = groupCounter; + } + groupCounter++; + } + + if ((selectedIndex >= 0) + && (selectedIndex < m_macroGroupComboBox->count())) { + m_macroGroupComboBox->setCurrentIndex(selectedIndex); + } + + macroGroupBoxActivated(selectedIndex); +} + +/** + * Called when macro group combo box selection is made + */ +void +WuQMacroDialog::macroGroupBoxActivated(int) +{ + QListWidgetItem* selectedItem = m_macrosListWidget->currentItem(); + QString selectedName = ((selectedItem != NULL) ? selectedItem->text() : ""); + + m_macrosListWidget->clear(); + + int32_t selectedIndex = 0; + WuQMacroGroup* selectedGroup = getSelectedMacroGroup(); + const int32_t numMacros = selectedGroup->getNumberOfMacros(); + for (int32_t i = 0; i < numMacros; i++) { + const QString macroName = selectedGroup->getMacroAtIndex(i)->getName(); + m_macrosListWidget->addItem(macroName); + if (selectedName == macroName) { + selectedIndex = i; + } + } + + if ((selectedIndex >= 0) + && (selectedIndex < numMacros)) { + m_macrosListWidget->setCurrentRow(selectedIndex); + } + macrosListWidgetCurrentRowChanged(selectedIndex); +} + +/** + * Called when item is selected in the macros list widget + */ +void +WuQMacroDialog::macrosListWidgetCurrentRowChanged(int rowIndex) +{ + m_macroDescriptionTextEdit->clear(); + + WuQMacroGroup* selectedGroup = getSelectedMacroGroup(); + if ((rowIndex >= 0) + && (rowIndex < selectedGroup->getNumberOfMacros())) { + WuQMacro* macro = selectedGroup->getMacroAtIndex(rowIndex); + CaretAssert(macro); + m_macroDescriptionTextEdit->setPlainText(macro->getDescription()); + } +} + +/** + * @return Pointer to selected macro group or NULL if none available. + */ +WuQMacroGroup* +WuQMacroDialog::getSelectedMacroGroup() +{ + const int32_t selectedGroupIndex = m_macroGroupComboBox->currentIndex(); + if ((selectedGroupIndex >= 0) + && (selectedGroupIndex < m_macroGroupComboBox->count())) { + CaretAssertVectorIndex(m_macroGroups, selectedGroupIndex); + WuQMacroGroup* group = m_macroGroups[selectedGroupIndex]; + return group; + } + return NULL; +} + +/** + * @return Pointer to selected macro + */ +WuQMacro* +WuQMacroDialog::getSelectedMacro() +{ + WuQMacroGroup* selectedGroup = getSelectedMacroGroup(); + if (selectedGroup != NULL) { + const int32_t selectedMacroIndex = m_macrosListWidget->currentRow(); + if ((selectedMacroIndex >= 0) + && (selectedMacroIndex < selectedGroup->getNumberOfMacros())) { + WuQMacro* macro = selectedGroup->getMacroAtIndex(selectedMacroIndex); + return macro; + } + } + return NULL; +} + +/** + * Edit the selected macro + */ +void +WuQMacroDialog::editSelectedMacro() +{ + WuQMacro* macro = getSelectedMacro(); + if (macro != NULL) { + std::cout << "Edit macro: " << macro->getName() << std::endl; + } +} + +/** + * Run the selected macro + */ +void +WuQMacroDialog::runSelectedMacro() +{ + WuQMacro* macro = getSelectedMacro(); + if (macro != NULL) { + std::cout << "Run macro: " << macro->getName() << std::endl; + std::cout << macro->toString() << std::endl; + + WuQMacroManager::instance()->runMacro(parentWidget(), + macro); + } +} + + diff --git a/src/GuiQt/WuQMacroDialog.h b/src/GuiQt/WuQMacroDialog.h new file mode 100644 index 000000000..a2475cd14 --- /dev/null +++ b/src/GuiQt/WuQMacroDialog.h @@ -0,0 +1,99 @@ +#ifndef __WU_Q_MACRO_DIALOG_H__ +#define __WU_Q_MACRO_DIALOG_H__ + +/*LICENSE_START*/ +/* + * Copyright (C) 2018 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + + + +#include + +#include + +class QAbstractButton; +class QComboBox; +class QDialogButtonBox; +class QListWidget; +class QPlainTextEdit; + +namespace caret { + + class WuQMacro; + class WuQMacroGroup; + + class WuQMacroDialog : public QDialog { + + Q_OBJECT + + public: + WuQMacroDialog(QWidget* parent = 0); + + virtual ~WuQMacroDialog(); + + WuQMacroDialog(const WuQMacroDialog&) = delete; + + WuQMacroDialog& operator=(const WuQMacroDialog&) = delete; + + void updateDialogContents(); + + // ADD_NEW_METHODS_HERE + + private slots: + void macroGroupBoxActivated(int); + + void macrosListWidgetCurrentRowChanged(int); + + void buttonClicked(QAbstractButton* button); + + void editSelectedMacro(); + + void runSelectedMacro(); + + private: + WuQMacroGroup* getSelectedMacroGroup(); + + WuQMacro* getSelectedMacro(); + + std::vector m_macroGroups; + + QComboBox* m_macroGroupComboBox; + + QListWidget* m_macrosListWidget; + + QPlainTextEdit* m_macroDescriptionTextEdit; + + QDialogButtonBox* m_dialogButtonBox; + + QAbstractButton* m_runButton; + + QAbstractButton* m_editButton; + + WuQMacro* m_macro = NULL; + + // ADD_NEW_MEMBERS_HERE + + }; + +#ifdef __WU_Q_MACRO_DIALOG_DECLARE__ + // +#endif // __WU_Q_MACRO_DIALOG_DECLARE__ + +} // namespace +#endif //__WU_Q_MACRO_DIALOG_H__ diff --git a/src/GuiQt/WuQMacroExecutor.cxx b/src/GuiQt/WuQMacroExecutor.cxx new file mode 100644 index 000000000..658e57b1e --- /dev/null +++ b/src/GuiQt/WuQMacroExecutor.cxx @@ -0,0 +1,426 @@ + +/*LICENSE_START*/ +/* + * Copyright (C) 2018 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + +#define __WU_Q_MACRO_EXECUTOR_DECLARE__ +#include "WuQMacroExecutor.h" +#undef __WU_Q_MACRO_EXECUTOR_DECLARE__ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "CaretAssert.h" +#include "WuQMacro.h" +#include "WuQMacroCommand.h" +#include "WuQMacroSignalEmitter.h" + +using namespace caret; + +/** + * \class caret::WuQMacroExecutor + * \brief Executes a macro + * \ingroup WuQMacro + */ + +/** + * Constructor. + */ +WuQMacroExecutor::WuQMacroExecutor() +: QObject() +{ + +} + +/** + * Destructor. + */ +WuQMacroExecutor::~WuQMacroExecutor() +{ +} + +/** + * Move the mouse around the given widget + * + * @param widget + * Widget to where mouse should be moved + * @param highlightFlag + * Highlights widget by having mouse circle around widget center + */ +void +WuQMacroExecutor::moveMouse(QWidget* widget, + const bool highlightFlag) const +{ + CaretAssert(widget); + + const QPoint widgetCenter = widget->rect().center(); + const QPoint windowPoint = widget->mapToGlobal(widgetCenter); + + if (highlightFlag) { + const float radius = 15.0; + for (float angle = 0.0; angle < 6.28; angle += 0.314) { + const float x = windowPoint.x() + (std::cos(angle) * radius); + const float y = windowPoint.y() + (std::sin(angle) * radius); + QCursor::setPos(x, y); + //SystemUtilities::sleepSeconds(0.025); + } + } + + QCursor::setPos(windowPoint); +} + +/** + * Run the commands in the given macro. + * + * @param macro + * Macro that is run + * @param window + * The window from which macro was launched + * @param stopOnErrorFlag + * If true, stop running the commands if there is an error + * @param errorMessageOut + * Output containing any error messages + * @return + * True if the macro completed without errors, else false. + */ +bool +WuQMacroExecutor::runMacro(const WuQMacro* macro, + QObject* window, + const bool stopOnErrorFlag, + QString& errorMessageOut) const +{ + errorMessageOut.clear(); + + const int32_t numberOfMacroCommands = macro->getNumberOfMacroCommands(); + for (int32_t i = 0; i < numberOfMacroCommands; i++) { + const WuQMacroCommand* mc = macro->getMacroCommandAtIndex(i); + CaretAssert(mc); + + const QString objectName(mc->getObjectName()); + QObject* object = window->findChild(objectName); + if (object == NULL) { + errorMessageOut.append("Unable to find object named " + + objectName + + "\n"); + if (stopOnErrorFlag) { + return false; + } + continue; + } + + if (object->signalsBlocked()) { + errorMessageOut.append("Object named " + + objectName + + " has signals blocked"); + if (stopOnErrorFlag) { + return false; + } + continue; + } + + QWidget* widgetToMoveMouse = qobject_cast(object); + + if (widgetToMoveMouse == NULL) { + QObject* object = window->findChild(objectName); + if (object != NULL) { + QObject* parent = object->parent(); + if (parent != NULL) { + widgetToMoveMouse = qobject_cast(parent); + } + } + } + + const WuQMacroObjectTypeEnum::Enum objectType = mc->getObjectType(); + + const bool mouseEventFlag = (objectType == WuQMacroObjectTypeEnum::MOUSE_USER_EVENT); + if (widgetToMoveMouse != NULL) { + if ( ! mouseEventFlag) { + const bool highlightFlag = ( ! mouseEventFlag); + moveMouse(widgetToMoveMouse, + highlightFlag); + } + } + + QString commandErrorMessage; + if ( ! runMacroCommand(mc, + object, + commandErrorMessage)) { + errorMessageOut.append(commandErrorMessage + "\n"); + if (stopOnErrorFlag) { + return false; + } + } + + QGuiApplication::processEvents(); + if ( ! mouseEventFlag) { + SystemUtilities::sleepSeconds(1); + } + QGuiApplication::processEvents(); + } + + return (errorMessageOut.isEmpty()); +} + +/** + * Run the commands in the given macro. + * + * @param macro + * Macro that is run + * @param window + * The window from which macro was launched + * @param stopOnErrorFlag + * If true, stop running the commands if there is an error + * @param errorMessageOut + * Output containing any error messages + * @return + * True if the macro completed without errors, else false. + */ +bool +WuQMacroExecutor::runMacroCommand(const WuQMacroCommand* macroCommand, + QObject* object, + QString& errorMessageOut) const +{ + errorMessageOut.clear(); + + CaretAssert(macroCommand); + CaretAssert(object); + + const WuQMacroObjectTypeEnum::Enum objectType = macroCommand->getObjectType(); + const QVariant objectValue = macroCommand->getObjectValue(); + + bool notFoundFlag(false); + switch (objectType) { + case WuQMacroObjectTypeEnum::ACTION: + { + QAction* action = qobject_cast(object); + if (action != NULL) { + WuQMacroSignalEmitter signalEmitter; + signalEmitter.emitQActionSignal(action, + objectValue.toBool()); + } + else { + notFoundFlag = true; + } + } + break; + case WuQMacroObjectTypeEnum::CHECK_BOX: + { + QCheckBox* checkBox = qobject_cast(object); + if (checkBox != NULL) { + WuQMacroSignalEmitter signalEmitter; + signalEmitter.emitQCheckBoxSignal(checkBox, + objectValue.toBool()); + } + else { + notFoundFlag = true; + } + } + break; + case WuQMacroObjectTypeEnum::COMBO_BOX: + { + QComboBox* comboBox = qobject_cast(object); + if (comboBox != NULL) { + WuQMacroSignalEmitter signalEmitter; + signalEmitter.emitQComboBoxSignal(comboBox, + objectValue.toInt()); + } + else { + notFoundFlag = true; + } + } + break; + case WuQMacroObjectTypeEnum::DOUBLE_SPIN_BOX: + { + QDoubleSpinBox* doubleSpinBox = qobject_cast(object); + if (doubleSpinBox != NULL) { + WuQMacroSignalEmitter signalEmitter; + signalEmitter.emitQDoubleSpinBoxSignal(doubleSpinBox, + objectValue.toDouble()); + } + else { + notFoundFlag = true; + } + } + break; + case WuQMacroObjectTypeEnum::INVALID: + CaretAssert(0); + break; + case WuQMacroObjectTypeEnum::LINE_EDIT: + { + QLineEdit* lineEdit = qobject_cast(object); + if (lineEdit != NULL) { + WuQMacroSignalEmitter signalEmitter; + signalEmitter.emitQLineEditSignal(lineEdit, + objectValue.toString()); + } + else { + notFoundFlag = true; + } + } + break; + case WuQMacroObjectTypeEnum::LIST_WIDGET: + { + QListWidget* listWidget = qobject_cast(object); + if (listWidget != NULL) { + WuQMacroSignalEmitter signalEmitter; + signalEmitter.emitQListWidgetSignal(listWidget, + objectValue.toString()); + } + else { + notFoundFlag = true; + } + } + break; + case WuQMacroObjectTypeEnum::MENU: + { + QMenu* menu = qobject_cast(object); + if (menu != NULL) { + WuQMacroSignalEmitter signalEmitter; + signalEmitter.emitQMenuTriggered(menu, + objectValue.toString()); + } + else { + notFoundFlag = true; + } + } + break; + case WuQMacroObjectTypeEnum::MOUSE_USER_EVENT: + CaretAssertToDoFatal(); + break; + case WuQMacroObjectTypeEnum::PUSH_BUTTON: + { + QPushButton* pushButton = qobject_cast(object); + if (pushButton != NULL) { + WuQMacroSignalEmitter signalEmitter; + signalEmitter.emitQPushButtonSignal(pushButton, + objectValue.toBool()); + } + else { + notFoundFlag = true; + } + } + break; + case WuQMacroObjectTypeEnum::RADIO_BUTTON: + { + QRadioButton* radioButton = qobject_cast(object); + if (radioButton != NULL) { + WuQMacroSignalEmitter signalEmitter; + signalEmitter.emitQRadioButtonSignal(radioButton, + objectValue.toBool()); + } + else { + notFoundFlag = true; + } + } + break; + case WuQMacroObjectTypeEnum::SLIDER: + { + QSlider* slider = qobject_cast(object); + if (slider != NULL) { + WuQMacroSignalEmitter signalEmitter; + signalEmitter.emitQSliderSignal(slider, + objectValue.toInt()); + } + else { + notFoundFlag = true; + } + } + break; + case WuQMacroObjectTypeEnum::SPIN_BOX: + { + QSpinBox* spinBox = qobject_cast(object); + if (spinBox != NULL) { + WuQMacroSignalEmitter signalEmitter; + signalEmitter.emitQSpinBoxSignal(spinBox, + objectValue.toInt()); + } + else { + notFoundFlag = true; + } + } + break; + case WuQMacroObjectTypeEnum::TAB_BAR: + { + QTabBar* tabBar = qobject_cast(object); + if (tabBar != NULL) { + WuQMacroSignalEmitter signalEmitter; + signalEmitter.emitQTabBarSignal(tabBar, + objectValue.toInt()); + } + else { + notFoundFlag = true; + } + } + break; + case WuQMacroObjectTypeEnum::TAB_WIDGET: + { + QTabWidget* tabWidget = qobject_cast(object); + if (tabWidget != NULL) { + WuQMacroSignalEmitter signalEmitter; + signalEmitter.emitQTabWidgetSignal(tabWidget, + objectValue.toInt()); + } + else { + notFoundFlag = true; + } + } + break; + case WuQMacroObjectTypeEnum::TOOL_BUTTON: + { + QToolButton* toolButton = qobject_cast(object); + if (toolButton != NULL) { + WuQMacroSignalEmitter signalEmitter; + signalEmitter.emitQToolButtonSignal(toolButton, + objectValue.toBool()); + } + else { + notFoundFlag = true; + } + } + break; + } + + if (notFoundFlag) { + errorMessageOut = ("ERROR: Unable to cast object named " + + object->objectName() + + " to " + + WuQMacroObjectTypeEnum::toGuiName(objectType)); + return false; + } + + return true; +} + + diff --git a/src/GuiQt/WuQMacroExecutor.h b/src/GuiQt/WuQMacroExecutor.h new file mode 100644 index 000000000..b95051617 --- /dev/null +++ b/src/GuiQt/WuQMacroExecutor.h @@ -0,0 +1,75 @@ +#ifndef __WU_Q_MACRO_EXECUTOR_H__ +#define __WU_Q_MACRO_EXECUTOR_H__ + +/*LICENSE_START*/ +/* + * Copyright (C) 2018 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + + + +#include + +#include + +class QWidget; + +namespace caret { + + class WuQMacro; + class WuQMacroCommand; + + class WuQMacroExecutor : public QObject { + + Q_OBJECT + + public: + WuQMacroExecutor(); + + virtual ~WuQMacroExecutor(); + + WuQMacroExecutor(const WuQMacroExecutor&) = delete; + + WuQMacroExecutor& operator=(const WuQMacroExecutor&) = delete; + + bool runMacro(const WuQMacro* macro, + QObject* window, + const bool stopOnErrorFlag, + QString& errorMessageOut) const; + + + // ADD_NEW_METHODS_HERE + + private: + void moveMouse(QWidget* widget, + const bool highlightFlag) const; + + bool runMacroCommand(const WuQMacroCommand* macroCommand, + QObject* object, + QString& errorMessageOut) const; + + // ADD_NEW_MEMBERS_HERE + + }; + +#ifdef __WU_Q_MACRO_EXECUTOR_DECLARE__ + // +#endif // __WU_Q_MACRO_EXECUTOR_DECLARE__ + +} // namespace +#endif //__WU_Q_MACRO_EXECUTOR_H__ diff --git a/src/GuiQt/WuQMacroManager.cxx b/src/GuiQt/WuQMacroManager.cxx new file mode 100644 index 000000000..df0cb46b0 --- /dev/null +++ b/src/GuiQt/WuQMacroManager.cxx @@ -0,0 +1,386 @@ + +/*LICENSE_START*/ +/* + * Copyright (C) 2018 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + +#define __WU_Q_MACRO_MANAGER_DECLARE__ +#include "WuQMacroManager.h" +#undef __WU_Q_MACRO_MANAGER_DECLARE__ + +#include +#include +#include +#include + +#include "CaretAssert.h" +#include "CaretLogger.h" +#include "CaretPreferences.h" +#include "SessionManager.h" +#include "WuQMacro.h" +#include "WuQMacroCreateDialog.h" +#include "WuQMacroDialog.h" +#include "WuQMacroExecutor.h" +#include "WuQMacroSignalWatcher.h" + +using namespace caret; + + + +/** + * \class caret::WuQMacroManager + * \brief Manages the macro system. + * \ingroup WuQMacro + */ + +/** + * Constructor. + * + * @param name + * Name for the macro manager. + */ +WuQMacroManager::WuQMacroManager(const QString& name, + QObject* parent) +: QObject(parent), +m_name(name) +{ + setObjectName(name); +} + +/** + * Destructor. + */ +WuQMacroManager::~WuQMacroManager() +{ + /* + * Do not delete the WuQMacroSignalWatcher instances in + * m_signalWatchers. This WuQMacroManager is set as the + * parent object of the WuQMacroSignalWatcher's so Qt + * will destroy them. + */ + m_signalWatchers.clear(); + + /* + * If an instance is being deleted it MUST be the + * singleton so make it NULL. + */ + if (s_singletonMacroManager != NULL) { + s_singletonMacroManager = NULL; + } +} + +///** +// * Create the singleton Macro Manager. This method MUST be called before +// * calling instance() and this method should only be called once. When +// * the given 'parent' is deleted, it will result in the singletone instance +// * being deleted. +// * +// * @param name +// * Name of macro manager +// * @parent +// * Parent of the macro manager. +// */ +//WuQMacroManager* +//WuQMacroManager::createMacroManagerSingleton(const QString& name, +// QObject* parent) +//{ +// if (s_singletonMacroManager == NULL) { +// s_singletonMacroManager = new WuQMacroManager(name, +// parent); +// } +// else { +// CaretLogSevere("WuQMacroManager::createMacroManagerSingleton() has already been " +// "called, this should never be called more than once."); +// } +// +// return s_singletonMacroManager; +//} + +/** + * @return The instance of the Macro Manager. Before calling this method, + * the createMacroManagerSingleton() must have been called to create the + * singleton Macro Manager. If the singleton is not valid this method + * will cause the application to abort. + */ +WuQMacroManager* +WuQMacroManager::instance() +{ + if (s_singletonMacroManager == NULL) { + /* + * 'qApp' is macro in QApplication that points to the QApplication instance + */ + s_singletonMacroManager = new WuQMacroManager("MacroManager", + qApp); +// CaretLogSevere("A macro manager has not been created with createMacroManagerSingleton(). " +// "That method MUST be called before this method."); +// std::abort(); + } + + return s_singletonMacroManager; +} + + +/** + * @return Name of this macro manager + */ +QString +WuQMacroManager::getName() const +{ + return m_name; +} + +/** + * @return The current macro mode. + */ +WuQMacroModeEnum::Enum +WuQMacroManager::getMode() const +{ + return m_mode; +} + +/** + * Set the macro mode + * + * @param mode + * New mode + */ +void +WuQMacroManager::setMode(const WuQMacroModeEnum::Enum mode) +{ + m_mode = mode; +} + +/** + * @return True if mode is RECORDING + */ +bool +WuQMacroManager::isModeRecording() const +{ + return (m_mode == WuQMacroModeEnum::RECORDING); +} + +/** + * Add macro support to the given object. When recording, + * The object's 'value changed' signal will be monitored so + * that the new value can be part of a macro command. + * + * @param object + * Object that is monitored. + */ +void +WuQMacroManager::addMacroSupportToObject(QObject* object) +{ + CaretAssert(object); + + const QString name = object->objectName(); + if (name.isEmpty()) { + CaretLogSevere("Object name is empty, will be ignored for macros"); + return; + } + + { + QString toolTipText; + QAction* action = qobject_cast(object); + if (action != NULL) { + toolTipText = action->toolTip(); + } + QWidget* widget = qobject_cast(object); + if (widget != NULL) { + toolTipText = widget->toolTip(); + } + if (toolTipText.isEmpty()) { + CaretLogWarning("Object named \"" + + name + + "\" is missing a tooltip"); + } + } + auto existingWatcher = m_signalWatchers.find(name); + if (existingWatcher != m_signalWatchers.end()) { + CaretLogSevere("Object named \"" + + name + + "\" has already been connected for macros"); + } + else { + AString errorMessage; + WuQMacroSignalWatcher* widgetWatcher = WuQMacroSignalWatcher::newInstance(this, + object, + errorMessage); + if (widgetWatcher != NULL) { + widgetWatcher->setParent(this); + m_signalWatchers.insert(std::make_pair(name, + widgetWatcher)); + } + else { + CaretLogWarning(errorMessage); + } + } +} + +/** + * Adds the given macro comand to the macro that is currently being + * recorded. If no macro is being recorded, no action is taken. + * + * @param macroCommand + * Command to add to the current macro + * @return + * True if recording is on and command was added to the macro. + * Ownership of command will be by the macro + * False if recording off in which case caller is responsible + * to delete the macro command + */ +bool +WuQMacroManager::addMacroCommandToRecording(WuQMacroCommand* macroCommand) +{ + CaretAssert(macroCommand); + + if (isModeRecording()) { + CaretAssert(m_macroBeingRecorded); + m_macroBeingRecorded->addMacroCommand(macroCommand); + return true; + } + + return false; +} + +/** + * @return Vector containing all Macro Groups. + */ +std::vector +WuQMacroManager::getMacroGroups() const +{ + std::vector macroGroups; + + CaretPreferences* preferences = SessionManager::get()->getCaretPreferences(); + CaretAssert(preferences); + macroGroups.push_back(preferences->getMacros()); + + return macroGroups; +} + +/** + * Start recording a new macro using the New Macro dialog. + * + * @param parent + * Parent for dialog. + */ +void +WuQMacroManager::startRecordingNewMacro(QWidget* parent) +{ + CaretAssert(m_mode == WuQMacroModeEnum::OFF); + + WuQMacroCreateDialog createMacroDialog(parent); + if (createMacroDialog.exec() == WuQMacroCreateDialog::Accepted) { + m_mode = WuQMacroModeEnum::RECORDING; + m_macroBeingRecorded = createMacroDialog.getNewMacro(); + CaretAssert(m_macroBeingRecorded); + } +} + +/** + * Stop recording the macro. + */ +void +WuQMacroManager::stopRecordingNewMacro() +{ + CaretAssert(m_mode == WuQMacroModeEnum::RECORDING); + m_mode = WuQMacroModeEnum::OFF; + m_macroBeingRecorded = NULL; + updateNonModalDialogs(); +} + +/** + * Show the macros dialog + * + * @param parent + * Parent for dialog + */ +void +WuQMacroManager::showMacrosDialog(QWidget* parent) +{ + if (m_macrosDialog == NULL) { + m_macrosDialog = new WuQMacroDialog(parent); + } + m_macrosDialog->updateDialogContents(); + m_macrosDialog->show(); + m_macrosDialog->raise(); + + // switch (macroManager->getMode()) { + // case WuQMacroModeEnum::OFF: + // break; + // case WuQMacroModeEnum::RECORDING: + // CaretAssert(0); + // break; + // case WuQMacroModeEnum::RUNNING: + // CaretAssert(0); + // break; + // } + +} + +/** + * @return Vector containing all non-modal dialogs used by Macro Manager. + * This may be useful if the parent window is closed but other parent + * windows are available. + */ +std::vector +WuQMacroManager::getNonModalDialogs() +{ + std::vector nonModalDialogs; + if (m_macrosDialog != NULL) { + nonModalDialogs.push_back(m_macrosDialog); + } + return nonModalDialogs; +} + + +/** + * Update non-modal dialogs in macro manager + */ +void +WuQMacroManager::updateNonModalDialogs() +{ + if (m_macrosDialog != NULL) { + m_macrosDialog->updateDialogContents(); + } +} + +void +WuQMacroManager::runMacro(QWidget* widget, + const WuQMacro* macro) +{ + CaretAssert(widget); + CaretAssert(macro); + + + QString errorMessage; + WuQMacroExecutor executor; + if ( ! executor.runMacro(macro, + widget, + true, + errorMessage)) { + QMessageBox::critical(widget, + "Run Macro Error", + errorMessage, + QMessageBox::Ok, + QMessageBox::NoButton); + } +} + + + + diff --git a/src/GuiQt/WuQMacroManager.h b/src/GuiQt/WuQMacroManager.h new file mode 100644 index 000000000..c103f5743 --- /dev/null +++ b/src/GuiQt/WuQMacroManager.h @@ -0,0 +1,110 @@ +#ifndef __WU_Q_MACRO_MANAGER_H__ +#define __WU_Q_MACRO_MANAGER_H__ + +/*LICENSE_START*/ +/* + * Copyright (C) 2018 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + + + +#include + +#include + +#include "WuQMacroModeEnum.h" + +class QWidget; + +namespace caret { + + class WuQMacro; + class WuQMacroCommand; + class WuQMacroDialog; + class WuQMacroGroup; + class WuQMacroSignalWatcher; + + class WuQMacroManager : public QObject { + Q_OBJECT + + public: +// static WuQMacroManager* createMacroManagerSingleton(const QString& name, +// QObject* parent); + static WuQMacroManager* instance(); + + virtual ~WuQMacroManager(); + + WuQMacroManager(const WuQMacroManager&) = delete; + + WuQMacroManager& operator=(const WuQMacroManager&) = delete; + + QString getName() const; + + void addMacroSupportToObject(QObject* object); + + bool addMacroCommandToRecording(WuQMacroCommand* macroCommand); + + WuQMacroModeEnum::Enum getMode() const; + + void setMode(const WuQMacroModeEnum::Enum mode); + + bool isModeRecording() const; + + std::vector getMacroGroups() const; + + void startRecordingNewMacro(QWidget* parent); + + void stopRecordingNewMacro(); + + void showMacrosDialog(QWidget* parent); + + std::vector getNonModalDialogs(); + + void updateNonModalDialogs(); + + void runMacro(QWidget* window, + const WuQMacro* macro); + + // ADD_NEW_METHODS_HERE + + private: + WuQMacroManager(const QString& name, + QObject* parent = NULL); + + const QString m_name; + + WuQMacroModeEnum::Enum m_mode = WuQMacroModeEnum::OFF; + + std::map m_signalWatchers; + + WuQMacro* m_macroBeingRecorded = NULL; + + WuQMacroDialog* m_macrosDialog = NULL; + + static WuQMacroManager* s_singletonMacroManager; + + // ADD_NEW_MEMBERS_HERE + + }; + +#ifdef __WU_Q_MACRO_MANAGER_DECLARE__ + WuQMacroManager* WuQMacroManager::s_singletonMacroManager = NULL; +#endif // __WU_Q_MACRO_MANAGER_DECLARE__ + +} // namespace +#endif //__WU_Q_MACRO_MANAGER_H__ diff --git a/src/GuiQt/WuQMacroMenu.cxx b/src/GuiQt/WuQMacroMenu.cxx new file mode 100644 index 000000000..0292307de --- /dev/null +++ b/src/GuiQt/WuQMacroMenu.cxx @@ -0,0 +1,137 @@ + +/*LICENSE_START*/ +/* + * Copyright (C) 2018 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + +#define __WU_Q_MACRO_MENU_DECLARE__ +#include "WuQMacroMenu.h" +#undef __WU_Q_MACRO_MENU_DECLARE__ + +#include "CaretAssert.h" +#include "WuQMacroManager.h" + +using namespace caret; + + + +/** + * \class caret::WuQMacroMenu + * \brief Menu for accessing and interacting with the Macro System. + * \ingroup WuQMacro + */ + +/** + * Constructor. + * + * @param windowParent + * Window used as parent for dialogs + * @param parent + * Parent of the menu + */ +WuQMacroMenu::WuQMacroMenu(QWidget* windowParent, + QWidget* parent) +: QMenu(parent), +m_windowParent(windowParent) +{ + CaretAssert(windowParent); + + setTitle("Macro New"); + + m_macroDialogAction = addAction("Macros..."); + QObject::connect(m_macroDialogAction, &QAction::triggered, + this, &WuQMacroMenu::macroDialogSelected); + + m_recordMacroAction = addAction("Record Macro..."); + QObject::connect(m_recordMacroAction, &QAction::triggered, + this, &WuQMacroMenu::macroRecordSelected); + + QObject::connect(this, &QMenu::aboutToShow, + this, &WuQMacroMenu::macroMenuAboutToShow); +} + +/** + * Destructor. + */ +WuQMacroMenu::~WuQMacroMenu() +{ +} + +/** + * Called when the macro menu is about to show. + */ +void +WuQMacroMenu::macroMenuAboutToShow() +{ + WuQMacroManager* macroManager = WuQMacroManager::instance(); + CaretAssert(macroManager); + + bool editValidFlag(false); + bool recordValidFlag(false); + QString recordText; + + switch (macroManager->getMode()) { + case WuQMacroModeEnum::OFF: + editValidFlag = true; + recordText = "Record Macro..."; + recordValidFlag = true; + break; + case WuQMacroModeEnum::RECORDING: + recordText = "Stop Recording"; + recordValidFlag = true; + break; + case WuQMacroModeEnum::RUNNING: + recordText = "Record Macro..."; + break; + } + + m_macroDialogAction->setEnabled(editValidFlag); + m_recordMacroAction->setEnabled(recordValidFlag); + m_recordMacroAction->setText(recordText); +} + +/** + * Called to start/stop macro recording. + */ +void +WuQMacroMenu::macroRecordSelected() +{ + WuQMacroManager* macroManager = WuQMacroManager::instance(); + CaretAssert(macroManager); + + switch (macroManager->getMode()) { + case WuQMacroModeEnum::OFF: + macroManager->startRecordingNewMacro(m_windowParent); + break; + case WuQMacroModeEnum::RECORDING: + macroManager->stopRecordingNewMacro(); + break; + case WuQMacroModeEnum::RUNNING: + CaretAssert(0); + break; + } +} + +/** + * Called to display the macro editor dialog. + */ +void +WuQMacroMenu::macroDialogSelected() +{ + WuQMacroManager::instance()->showMacrosDialog(m_windowParent); +} diff --git a/src/GuiQt/WuQMacroMenu.h b/src/GuiQt/WuQMacroMenu.h new file mode 100644 index 000000000..fd83dba37 --- /dev/null +++ b/src/GuiQt/WuQMacroMenu.h @@ -0,0 +1,70 @@ +#ifndef __WU_Q_MACRO_MENU_H__ +#define __WU_Q_MACRO_MENU_H__ + +/*LICENSE_START*/ +/* + * Copyright (C) 2018 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + + + +#include + +#include + +namespace caret { + + class WuQMacroMenu : public QMenu { + + Q_OBJECT + + public: + WuQMacroMenu(QWidget* windowParent, + QWidget* parent = 0); + + virtual ~WuQMacroMenu(); + + WuQMacroMenu(const WuQMacroMenu&) = delete; + + WuQMacroMenu& operator=(const WuQMacroMenu&) = delete; + + // ADD_NEW_METHODS_HERE + + private slots: + void macroMenuAboutToShow(); + + void macroRecordSelected(); + + void macroDialogSelected(); + + private: + // ADD_NEW_MEMBERS_HERE + + QWidget* m_windowParent; + + QAction* m_macroDialogAction; + + QAction* m_recordMacroAction; + }; + +#ifdef __WU_Q_MACRO_MENU_DECLARE__ + // +#endif // __WU_Q_MACRO_MENU_DECLARE__ + +} // namespace +#endif //__WU_Q_MACRO_MENU_H__ diff --git a/src/GuiQt/WuQMacroSignalEmitter.cxx b/src/GuiQt/WuQMacroSignalEmitter.cxx new file mode 100644 index 000000000..8f5530d39 --- /dev/null +++ b/src/GuiQt/WuQMacroSignalEmitter.cxx @@ -0,0 +1,436 @@ + +/*LICENSE_START*/ +/* + * Copyright (C) 2018 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + +#define __WU_Q_MACRO_SIGNAL_EMITTER_DECLARE__ +#include "WuQMacroSignalEmitter.h" +#undef __WU_Q_MACRO_SIGNAL_EMITTER_DECLARE__ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "CaretAssert.h" +#include "CaretLogger.h" + +using namespace caret; + +/** + * \class caret::WuQMacroSignalEmitter + * \brief Causes the emission of a signal from a QObject instance + * \ingroup WuQMacro + */ + +/** + * Constructor. + */ +WuQMacroSignalEmitter::WuQMacroSignalEmitter() +: QObject() +{ + +} + +/** + * Destructor. + */ +WuQMacroSignalEmitter::~WuQMacroSignalEmitter() +{ +} + +/** + * Update a QAction's checked status and cause emission + * of its triggered() signal + * + * @param action + * The QAction + * @param checked + * New checked status + */ +void +WuQMacroSignalEmitter::emitQActionSignal(QAction* action, + const bool checked) +{ + CaretAssert(action); + action->setChecked(checked); + + QObject::connect(this, &WuQMacroSignalEmitter::valueChangedSignalBool, + action, &QAction::triggered); + + emit valueChangedSignalBool(checked); + + QObject::disconnect(this, &WuQMacroSignalEmitter::valueChangedSignalBool, 0, 0); +} + +/** + * Update a QCheckBox's checked status and cause emission + * of its clicked() signal + * + * @param checkBox + * The QCheckBox + * @param checked + * New checked status + */ +void +WuQMacroSignalEmitter::emitQCheckBoxSignal(QCheckBox* checkBox, + const bool checked) +{ + CaretAssert(checkBox); + checkBox->setChecked(checkBox); + + QObject::connect(this, &WuQMacroSignalEmitter::valueChangedSignalBool, + checkBox, &QCheckBox::clicked); + + emit valueChangedSignalBool(checked); + + QObject::disconnect(this, &WuQMacroSignalEmitter::valueChangedSignalBool, 0, 0); +} + +/** + * Update a QComboBox's index and cause emission + * of its triggered() signal + * + * @param comboBox + * The QComboBox + * @param index + * New index + */ +void +WuQMacroSignalEmitter::emitQComboBoxSignal(QComboBox* comboBox, + const int32_t index) +{ + CaretAssert(comboBox); + comboBox->setCurrentIndex(index); + + QObject::connect(this, &WuQMacroSignalEmitter::valueChangedSignalInt, + comboBox, static_cast(&QComboBox::activated)); + + emit valueChangedSignalInt(index); + + QObject::disconnect(this, &WuQMacroSignalEmitter::valueChangedSignalInt, 0, 0); +} + +/** + * Update a QDoubleSpinBox value and cause emission + * of its valueChanged() signal + * + * @param doubleSpinBox + * The QDoubleSpinBox + * @param value + * New value + */ +void +WuQMacroSignalEmitter::emitQDoubleSpinBoxSignal(QDoubleSpinBox* doubleSpinBox, + const double value) +{ + CaretAssert(doubleSpinBox); + QSignalBlocker blocker(doubleSpinBox); + doubleSpinBox->setValue(value); + blocker.unblock(); + + QObject::connect(this, &WuQMacroSignalEmitter::valueChangedSignalDouble, + doubleSpinBox, static_cast(&QDoubleSpinBox::valueChanged)); + + emit valueChangedSignalDouble(value); + + QObject::disconnect(this, &WuQMacroSignalEmitter::valueChangedSignalDouble, 0, 0); + +} + +/** + * Update a QListWidget's item and cause emission + * of its itemActivated() signal + * + * @param lineEdit + * The QLineEdit + * @param text + * New text + */ +void +WuQMacroSignalEmitter::emitQListWidgetSignal(QListWidget* listWidget, + const QString& text) +{ + CaretAssert(listWidget); + + QList items = listWidget->findItems(text, + Qt::MatchExactly); + if (items.isEmpty()) { + CaretLogWarning("Unable to find QListWidgetItem with text \"" + + text + + "\" for " + + listWidget->objectName()); + return; + } + + QListWidgetItem* newItem = items.at(0); + listWidget->setCurrentItem(newItem); + + QObject::connect(this, &WuQMacroSignalEmitter::valueChangeSignalListWidgetItem, + listWidget, &QListWidget::itemActivated); + + emit valueChangeSignalListWidgetItem(newItem); + + QObject::disconnect(this, &WuQMacroSignalEmitter::valueChangeSignalListWidgetItem, 0, 0); +} + + +/** + * Update a QLineEdit's text and cause emission + * of its textChanged() signal + * + * @param lineEdit + * The QLineEdit + * @param text + * New text + */ +void +WuQMacroSignalEmitter::emitQLineEditSignal(QLineEdit* lineEdit, + const QString& text) +{ + CaretAssert(lineEdit); + lineEdit->setText(text); + + QObject::connect(this, &WuQMacroSignalEmitter::valueChangedSignalString, + lineEdit, &QLineEdit::textChanged); + + emit valueChangedSignalString(text); + + QObject::disconnect(this, &WuQMacroSignalEmitter::valueChangedSignalString, 0, 0); +} + +/** + * Update a QLineEdit's text and cause emission + * of its textChanged() signal + * + * @param lineEdit + * The QLineEdit + * @param text + * New text + */ +void +WuQMacroSignalEmitter::emitQMenuTriggered(QMenu* menu, + const QString& text) +{ + CaretAssert(menu); + + QList actions = menu->actions(); + if (actions.isEmpty()) { + CaretLogWarning("Unable to find QAction with text \"" + + text + + "\" for menu " + + menu->objectName()); + return; + } + + QObject::connect(this, &WuQMacroSignalEmitter::valueChangeSignalMenuAction, + menu, &QMenu::triggered); + + emit valueChangeSignalMenuAction(actions.at(0)); + + QObject::disconnect(this, &WuQMacroSignalEmitter::valueChangeSignalMenuAction, 0, 0); +} + + +/** + * Update a QPushButton's checked status and cause emission + * of its clicked() signal + * + * @param pushButton + * The QPushButton + * @param checked + * New checked status + */ +void +WuQMacroSignalEmitter::emitQPushButtonSignal(QPushButton* pushButton, + const bool checked) +{ + CaretAssert(pushButton); + pushButton->setChecked(checked); + + QObject::connect(this, &WuQMacroSignalEmitter::valueChangedSignalBool, + pushButton, &QPushButton::clicked); + + emit valueChangedSignalBool(checked); + + QObject::disconnect(this, &WuQMacroSignalEmitter::valueChangedSignalBool, 0, 0); +} + +/** + * Update a RadioButton's checked status and cause emission + * of its checked() signal + * + * @param radioButton + * The QRadioButton + * @param checked + * New checked status + */ +void +WuQMacroSignalEmitter::emitQRadioButtonSignal(QRadioButton* radioButton, + const bool checked) +{ + CaretAssert(radioButton); + radioButton->setChecked(checked); + + QObject::connect(this, &WuQMacroSignalEmitter::valueChangedSignalBool, + radioButton, &QRadioButton::clicked); + + emit valueChangedSignalBool(checked); + + QObject::disconnect(this, &WuQMacroSignalEmitter::valueChangedSignalBool, 0, 0); +} + +/** + * Update a QSlider's value and cause emission + * of its valueChanged signal + * + * @param slider + * The QSlider + * @param value + * New value + */ +void +WuQMacroSignalEmitter::emitQSliderSignal(QSlider* slider, + const int32_t value) +{ + CaretAssert(slider); + + QSignalBlocker blocker(slider); + slider->setValue(value); + blocker.unblock(); + + QObject::connect(this, &WuQMacroSignalEmitter::valueChangedSignalInt, + slider, &QSlider::valueChanged); + + emit valueChangedSignalInt(value); + + QObject::disconnect(this, &WuQMacroSignalEmitter::valueChangedSignalInt, 0, 0); +} + +/** + * Update a spin box's value and cause emission + * of its valueChanged signal + * + * @param spinBox + * The QSpinBox + * @param value + * New value + */ +void +WuQMacroSignalEmitter::emitQSpinBoxSignal(QSpinBox* spinBox, + const int32_t value) +{ + CaretAssert(spinBox); + + QSignalBlocker blocker(spinBox); + spinBox->setValue(value); + blocker.unblock(); + + QObject::connect(this, &WuQMacroSignalEmitter::valueChangedSignalInt, + spinBox, static_cast(&QSpinBox::valueChanged)); + + emit valueChangedSignalInt(value); + + QObject::disconnect(this, &WuQMacroSignalEmitter::valueChangedSignalInt, 0, 0); + +} + +/** + * Update a TabBar's index and cause emission + * of its currentChanged() signal + * + * @param tabBar + * The QTabBar + * @param index + * New index + */ +void +WuQMacroSignalEmitter::emitQTabBarSignal(QTabBar* tabBar, + const int32_t index) +{ + CaretAssert(tabBar); + + tabBar->setCurrentIndex(index); + + QObject::connect(this, &WuQMacroSignalEmitter::valueChangedSignalInt, + tabBar, &QTabBar::currentChanged); + + emit valueChangedSignalInt(index); + + QObject::disconnect(this, &WuQMacroSignalEmitter::valueChangedSignalInt, 0, 0); +} + +/** + * Update a TabWidget's index and cause emission + * of its currentChanged() signal + * + * @param tabWidget + * The QTabWidget + * @param index + * New index + */ +void +WuQMacroSignalEmitter::emitQTabWidgetSignal(QTabWidget* tabWidget, + const int32_t index) +{ + CaretAssert(tabWidget); + + tabWidget->setCurrentIndex(index); + + QObject::connect(this, &WuQMacroSignalEmitter::valueChangedSignalInt, + tabWidget, &QTabWidget::currentChanged); + + emit valueChangedSignalInt(index); + + QObject::disconnect(this, &WuQMacroSignalEmitter::valueChangedSignalInt, 0, 0); +} + +/** + * Update a QToolButton checked status and cause emission + * of its checked() signal + * + * @param toolButton + * The QToolButton + * @param checked + * New checked status + */ +void +WuQMacroSignalEmitter::emitQToolButtonSignal(QToolButton* toolButton, + const bool checked) +{ + CaretAssert(toolButton); + toolButton->setChecked(checked); + + QObject::connect(this, &WuQMacroSignalEmitter::valueChangedSignalBool, + toolButton, &QToolButton::clicked); + + emit valueChangedSignalBool(checked); + + QObject::disconnect(this, &WuQMacroSignalEmitter::valueChangedSignalBool, 0, 0); +} diff --git a/src/GuiQt/WuQMacroSignalEmitter.h b/src/GuiQt/WuQMacroSignalEmitter.h new file mode 100644 index 000000000..89a858f6f --- /dev/null +++ b/src/GuiQt/WuQMacroSignalEmitter.h @@ -0,0 +1,128 @@ +#ifndef __WU_Q_MACRO_SIGNAL_EMITTER_H__ +#define __WU_Q_MACRO_SIGNAL_EMITTER_H__ + +/*LICENSE_START*/ +/* + * Copyright (C) 2018 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + + + +#include + +#include + +class QAction; +class QCheckBox; +class QComboBox; +class QDoubleSpinBox; +class QLineEdit; +class QListWidget; +class QListWidgetItem; +class QMenu; +class QPushButton; +class QRadioButton; +class QSlider; +class QSpinBox; +class QTabBar; +class QTabWidget; +class QToolButton; + +namespace caret { + + class WuQMacroSignalEmitter : public QObject { + + Q_OBJECT + + public: + WuQMacroSignalEmitter(); + + virtual ~WuQMacroSignalEmitter(); + + WuQMacroSignalEmitter(const WuQMacroSignalEmitter&) = delete; + + WuQMacroSignalEmitter& operator=(const WuQMacroSignalEmitter&) = delete; + + void emitQActionSignal(QAction* action, + const bool checked); + + void emitQCheckBoxSignal(QCheckBox* checkBox, + const bool checked); + + void emitQComboBoxSignal(QComboBox* comboBox, + const int32_t index); + + void emitQDoubleSpinBoxSignal(QDoubleSpinBox* doubleSpinBox, + const double value); + + void emitQLineEditSignal(QLineEdit* lineEdit, + const QString& text); + + void emitQListWidgetSignal(QListWidget* listWidget, + const QString& text); + + void emitQMenuTriggered(QMenu* menu, + const QString& text); + + void emitQPushButtonSignal(QPushButton* pushButton, + const bool checked); + + void emitQRadioButtonSignal(QRadioButton* radioButton, + const bool checked); + + void emitQSliderSignal(QSlider* slider, + const int32_t value); + + void emitQSpinBoxSignal(QSpinBox* spinBox, + const int32_t value); + + void emitQTabBarSignal(QTabBar* tabBar, + const int32_t value); + + void emitQTabWidgetSignal(QTabWidget* tabWidget, + const int32_t value); + + void emitQToolButtonSignal(QToolButton* toolButton, + const bool checked); + + // ADD_NEW_METHODS_HERE + + signals: + void valueChangeSignalMenuAction(QAction*); + + void valueChangedSignalBool(bool); + + void valueChangedSignalInt(int); + + void valueChangedSignalDouble(double); + + void valueChangedSignalString(QString); + + void valueChangeSignalListWidgetItem(QListWidgetItem*); + + private: + // ADD_NEW_MEMBERS_HERE + + }; + +#ifdef __WU_Q_MACRO_SIGNAL_EMITTER_DECLARE__ + // +#endif // __WU_Q_MACRO_SIGNAL_EMITTER_DECLARE__ + +} // namespace +#endif //__WU_Q_MACRO_SIGNAL_EMITTER_H__ diff --git a/src/GuiQt/WuQMacroSignalWatcher.cxx b/src/GuiQt/WuQMacroSignalWatcher.cxx new file mode 100644 index 000000000..64d629ede --- /dev/null +++ b/src/GuiQt/WuQMacroSignalWatcher.cxx @@ -0,0 +1,494 @@ + +/*LICENSE_START*/ +/* + * Copyright (C) 2018 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + +#define __WU_Q_MACRO_SIGNAL_WATCHER_DECLARE__ +#include "WuQMacroSignalWatcher.h" +#undef __WU_Q_MACRO_SIGNAL_WATCHER_DECLARE__ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "CaretAssert.h" +#include "WuQMacroCommand.h" +#include "WuQMacroManager.h" + +using namespace caret; + +/** + * \class caret::WuQMacroSignalWatcher + * \brief Watches a QObject instance to observe its "value changed" signal + * \ingroup WuQMacro + */ + +/** + * Constructor. + * + * @param parentMacroManager + * Parent macro manager. + * @param object + * Object that is watched for a "value changed" signal + * @param objectType + * The type of the object. + */ +WuQMacroSignalWatcher::WuQMacroSignalWatcher(WuQMacroManager* parentMacroManager, + QObject* object, + const WuQMacroObjectTypeEnum::Enum objectType) +: QObject(), +m_parentMacroManager(parentMacroManager), +m_objectType(objectType), +m_objectName(object->objectName()) +{ + CaretAssert(m_parentMacroManager); + CaretAssert(object); + + QWidget* widget = qobject_cast(object); + if (widget != NULL) { + m_toolTipText = widget->toolTip(); + } + + switch (m_objectType) { + case WuQMacroObjectTypeEnum::ACTION: + { + QAction* action = qobject_cast(object); + CaretAssert(action); + QObject::connect(action, &QAction::triggered, + this, &WuQMacroSignalWatcher::actionTriggered); + m_toolTipText = action->toolTip(); + } + break; + case WuQMacroObjectTypeEnum::CHECK_BOX: + { + QCheckBox* checkBox = qobject_cast(object); + CaretAssert(checkBox); + QObject::connect(checkBox, &QCheckBox::clicked, + this, &WuQMacroSignalWatcher::checkBoxClicked); + } + break; + case WuQMacroObjectTypeEnum::COMBO_BOX: + { + QComboBox* comboBox = qobject_cast(object); + CaretAssert(comboBox); + QObject::connect(comboBox, static_cast(&QComboBox::activated), + this, &WuQMacroSignalWatcher::comboBoxActivated); + } + break; + case WuQMacroObjectTypeEnum::DOUBLE_SPIN_BOX: + { + QDoubleSpinBox* spinBox = qobject_cast(object); + CaretAssert(spinBox); + QObject::connect(spinBox, static_cast(&QDoubleSpinBox::valueChanged), + this, &WuQMacroSignalWatcher::doubleSpinBoxValueChanged); + } + break; + case WuQMacroObjectTypeEnum::INVALID: + CaretAssert(0); + break; + case WuQMacroObjectTypeEnum::LINE_EDIT: + { + QLineEdit* lineEdit = qobject_cast(object); + CaretAssert(lineEdit); + QObject::connect(lineEdit, &QLineEdit::textEdited, + this, &WuQMacroSignalWatcher::lineEditTextEdited); + } + break; + case WuQMacroObjectTypeEnum::LIST_WIDGET: + { + QListWidget* listWidget = qobject_cast(object); + CaretAssert(listWidget); + QObject::connect(listWidget, &QListWidget::itemActivated, + this, &WuQMacroSignalWatcher::listWidgetItemActivated); + } + break; + case WuQMacroObjectTypeEnum::MENU: + { + QMenu* menu = qobject_cast(object); + CaretAssert(menu); + QObject::connect(menu, &QMenu::triggered, + this, &WuQMacroSignalWatcher::menuTriggered); + } + break; + case WuQMacroObjectTypeEnum::MOUSE_USER_EVENT: + CaretAssertToDoFatal(); + break; + case WuQMacroObjectTypeEnum::PUSH_BUTTON: + { + QPushButton* pushButton = qobject_cast(object); + CaretAssert(pushButton); + QObject::connect(pushButton, &QPushButton::clicked, + this, &WuQMacroSignalWatcher::pushButtonClicked); + } + break; + case WuQMacroObjectTypeEnum::RADIO_BUTTON: + { + QRadioButton* radioButton = qobject_cast(object); + CaretAssert(radioButton); + QObject::connect(radioButton, &QRadioButton::clicked, + this, &WuQMacroSignalWatcher::radioButtonClicked); + } + break; + case WuQMacroObjectTypeEnum::SLIDER: + { + QSlider* slider = qobject_cast(object); + CaretAssert(slider); + QObject::connect(slider, &QSlider::valueChanged, + this, &WuQMacroSignalWatcher::sliderValueChanged); + } + break; + case WuQMacroObjectTypeEnum::SPIN_BOX: + { + QSpinBox* spinBox = qobject_cast(object); + CaretAssert(spinBox); + QObject::connect(spinBox, static_cast(&QSpinBox::valueChanged), + this, &WuQMacroSignalWatcher::spinBoxValueChanged); + } + break; + case WuQMacroObjectTypeEnum::TAB_BAR: + { + QTabBar* tabBar = qobject_cast(object); + CaretAssert(tabBar); + QObject::connect(tabBar, &QTabBar::currentChanged, + this, &WuQMacroSignalWatcher::tabBarCurrentChanged); + } + break; + case WuQMacroObjectTypeEnum::TAB_WIDGET: + { + QTabWidget* tabWidget = qobject_cast(object); + CaretAssert(tabWidget); + QObject::connect(tabWidget, &QTabWidget::currentChanged, + this, &WuQMacroSignalWatcher::tabWidgetCurrentChanged); + } + break; + case WuQMacroObjectTypeEnum::TOOL_BUTTON: + { + QToolButton* toolButton = qobject_cast(object); + CaretAssert(toolButton); + QObject::connect(toolButton, &QCheckBox::clicked, + this, &WuQMacroSignalWatcher::toolButtonClicked); + } + break; + } +} + +/** + * Destructor. + */ +WuQMacroSignalWatcher::~WuQMacroSignalWatcher() +{ +} + +/** + * Create an new instance of a widget signal watcher for + * the given object. + * + * @param parentMacroManager + * Parent macro manager. + * @param object + * Object that will have a widget signal watcher. + * @param errorMessageOut + * Output containing error information if failure. + * @return + * Pointer to widget watcher or NULL if there was an error. + */ +WuQMacroSignalWatcher* +WuQMacroSignalWatcher::newInstance(WuQMacroManager* parentMacroManager, + QObject* object, + QString& errorMessageOut) +{ + errorMessageOut.clear(); + + const QString objectClassName = object->metaObject()->className(); + + bool validFlag(false); + WuQMacroObjectTypeEnum::Enum objectType = WuQMacroObjectTypeEnum::fromGuiName(objectClassName, + &validFlag); + + if ((objectType == WuQMacroObjectTypeEnum::INVALID) + || ( ! validFlag)) { + errorMessageOut = ("Widget named \"" + + object->objectName() + + "\" of class \"" + + object->metaObject()->className() + + "\" is not supported for macros"); + return NULL; + } + + WuQMacroSignalWatcher* ww = new WuQMacroSignalWatcher(parentMacroManager, + object, + objectType); + return ww; + +// if (qobject_cast(object) != NULL) { +// objectType = WuQMacroObjectTypeEnum::ACTION; +// } +// else if (qobject_cast(object) != NULL) { +// objectType = WuQMacroObjectTypeEnum::CHECK_BOX; +// } +// else if (qobject_cast(object) != NULL) { +// objectType = WuQMacroObjectTypeEnum::CHECK_BOX; +// } +// else if (qobject_cast(object) != NULL) { +// objectType = WuQMacroObjectTypeEnum::CHECK_BOX; +// } +// else if (qobject_cast(object) != NULL) { +// objectType = WuQMacroObjectTypeEnum::CHECK_BOX; +// } +// else if (qobject_cast(object) != NULL) { +// objectType = WuQMacroObjectTypeEnum::CHECK_BOX; +// } +// else if (qobject_cast(object) != NULL) { +// objectType = WuQMacroObjectTypeEnum::CHECK_BOX; +// } +// else if (qobject_cast(object) != NULL) { +// objectType = WuQMacroObjectTypeEnum::CHECK_BOX; +// } +// else if (qobject_cast(object) != NULL) { +// objectType = WuQMacroObjectTypeEnum::CHECK_BOX; +// } +// else if (qobject_cast(object) != NULL) { +// objectType = WuQMacroObjectTypeEnum::CHECK_BOX; +// } +// else if (qobject_cast(object) != NULL) { +// objectType = WuQMacroObjectTypeEnum::CHECK_BOX; +// } +// else if (qobject_cast(object) != NULL) { +// objectType = WuQMacroObjectTypeEnum::CHECK_BOX; +// } +// else if (qobject_cast(object) != NULL) { +// objectType = WuQMacroObjectTypeEnum::CHECK_BOX; +// } +// else if (qobject_cast(object) != NULL) { +// objectType = WuQMacroObjectTypeEnum::CHECK_BOX; +// } +// else if (qobject_cast(object) != NULL) { +// objectType = WuQMacroObjectTypeEnum::CHECK_BOX; +// } +// else if (qobject_cast(object) != NULL) { +// objectType = WuQMacroObjectTypeEnum::CHECK_BOX; +// } +} + +/** + * If recording mode is enabled, create and send a macro command + * to the macro manager. + * + * @param value + * QVariant containing the data value. + */ +void +WuQMacroSignalWatcher::createAndSendMacroCommand(const QVariant value) +{ + if (m_parentMacroManager->isModeRecording()) { + WuQMacroCommand* mc = new WuQMacroCommand(m_objectType, + m_objectName, + m_toolTipText, + value); + if ( ! m_parentMacroManager->addMacroCommandToRecording(mc)) { + delete mc; + } + } +} + +/** + * Called when an action is triggered + * + * @param checked + * New checked status + */ +void +WuQMacroSignalWatcher::actionTriggered(bool checked) +{ + createAndSendMacroCommand(checked); +} + +/** + * Called when a check box is clicked + * + * @param checked + * New checked status + */ +void +WuQMacroSignalWatcher::checkBoxClicked(bool checked) +{ + createAndSendMacroCommand(checked); +} + +/** + * Called when a combo box is activated + * + * @param index + * Index of activated item + */ +void +WuQMacroSignalWatcher::comboBoxActivated(int index) +{ + createAndSendMacroCommand(index); +} + +/** + * Called when a spin box value is changed + * + * @param value + * New value in double spin box + */ +void +WuQMacroSignalWatcher::doubleSpinBoxValueChanged(double value) +{ + createAndSendMacroCommand(value); +} + +/** + * Called when a line edit has text edited + * + * @param text + * New value of text in the line edit + */ +void +WuQMacroSignalWatcher::lineEditTextEdited(const QString& text) +{ + createAndSendMacroCommand(text); +} + +/** + * Called when a list widget item is activated + * + * @param item + * List widget item that was selected + */ +void +WuQMacroSignalWatcher::listWidgetItemActivated(QListWidgetItem* item) +{ + const QString text((item != NULL) + ? item->text() + : ""); + createAndSendMacroCommand(text); +} + +/** + * Called when a menu has an item triggered + * + * @param action + * Menu action that was triggered + */ +void +WuQMacroSignalWatcher::menuTriggered(QAction* action) +{ + const QString text((action != NULL) + ? action->text() + : ""); + createAndSendMacroCommand(text); +} + +/** + * Called when a + * + * @param checked + * New checked status + */ +void +WuQMacroSignalWatcher::pushButtonClicked(bool checked) +{ + createAndSendMacroCommand(checked); +} + +/** + * Called when a radio button is clicked + * + * @param checked + * New checked status + */ +void +WuQMacroSignalWatcher::radioButtonClicked(bool checked) +{ + createAndSendMacroCommand(checked); +} + +/** + * Called when a slider value is changed + * + * @param value + * New value + */ +void +WuQMacroSignalWatcher::sliderValueChanged(int value) +{ + createAndSendMacroCommand(value); +} + +/** + * Called when a spin box value is changed + * + * @param value + * New value + */ +void +WuQMacroSignalWatcher::spinBoxValueChanged(int value) +{ + createAndSendMacroCommand(value); +} + +/** + * Called when a tab bar current tab is changed + * + * @param index + * Index of the new selected tab + */ +void +WuQMacroSignalWatcher::tabBarCurrentChanged(int index) +{ + createAndSendMacroCommand(index); +} + +/** + * Called when a tab widget current tab is changed + * + * @param index + * Index of the new selected tab + */ +void +WuQMacroSignalWatcher::tabWidgetCurrentChanged(int index) +{ + createAndSendMacroCommand(index); +} + +/** + * Called when a tool button is clicked + * + * @param checked + * New check status + */ +void +WuQMacroSignalWatcher::toolButtonClicked(bool checked) +{ + createAndSendMacroCommand(checked); +} + diff --git a/src/GuiQt/WuQMacroSignalWatcher.h b/src/GuiQt/WuQMacroSignalWatcher.h new file mode 100644 index 000000000..9eceade33 --- /dev/null +++ b/src/GuiQt/WuQMacroSignalWatcher.h @@ -0,0 +1,110 @@ +#ifndef __WU_Q_MACRO_SIGNAL_WATCHER_H__ +#define __WU_Q_MACRO_SIGNAL_WATCHER_H__ + +/*LICENSE_START*/ +/* + * Copyright (C) 2018 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + + + +#include + +#include + +#include "WuQMacroObjectTypeEnum.h" + +class QAction; +class QListWidgetItem; + +namespace caret { + + class WuQMacroManager; + + class WuQMacroSignalWatcher : public QObject { + + Q_OBJECT + + public: + static WuQMacroSignalWatcher* newInstance(WuQMacroManager* parentMacroManager, + QObject* object, + QString& errorMessageOut); + + virtual ~WuQMacroSignalWatcher(); + + WuQMacroSignalWatcher(const WuQMacroSignalWatcher&) = delete; + + WuQMacroSignalWatcher& operator=(const WuQMacroSignalWatcher&) = delete; + + private slots: + void actionTriggered(bool); + + void checkBoxClicked(bool); + + void comboBoxActivated(int); + + void doubleSpinBoxValueChanged(double); + + void lineEditTextEdited(const QString&); + + void listWidgetItemActivated(QListWidgetItem*); + + void menuTriggered(QAction* action); + + void pushButtonClicked(bool); + + void radioButtonClicked(bool); + + void sliderValueChanged(int); + + void spinBoxValueChanged(int); + + void tabBarCurrentChanged(int); + + void tabWidgetCurrentChanged(int); + + void toolButtonClicked(bool); + + + // ADD_NEW_METHODS_HERE + + private: + WuQMacroSignalWatcher(WuQMacroManager* parentMacroManager, + QObject* object, + const WuQMacroObjectTypeEnum::Enum objectType); + + WuQMacroManager* m_parentMacroManager; + + void createAndSendMacroCommand(const QVariant value); + + const WuQMacroObjectTypeEnum::Enum m_objectType; + + const QString m_objectName; + + QString m_toolTipText; + + // ADD_NEW_MEMBERS_HERE + + }; + +#ifdef __WU_Q_MACRO_SIGNAL_WATCHER_DECLARE__ + // +#endif // __WU_Q_MACRO_SIGNAL_WATCHER_DECLARE__ + +} // namespace +#endif //__WU_Q_MACRO_SIGNAL_WATCHER_H__ diff --git a/src/GuiQt/WuQtUtilities.cxx b/src/GuiQt/WuQtUtilities.cxx index c0900e59c..56a013c18 100644 --- a/src/GuiQt/WuQtUtilities.cxx +++ b/src/GuiQt/WuQtUtilities.cxx @@ -43,7 +43,6 @@ #include "CaretAssert.h" #include "CaretLogger.h" -#include "MacroPrototype.h" #include "WuQtUtilities.h" using namespace caret; -- GitLab From 34fbf86d76613b8e05ddfa56c211cfe6f2335033 Mon Sep 17 00:00:00 2001 From: John Harwell Date: Mon, 3 Dec 2018 15:00:18 -0600 Subject: [PATCH 125/427] WB-834: Finished removal of old macro system and added support for mouse operations. --- src/Common/CMakeLists.txt | 4 + src/Common/WuQMacroCommand.cxx | 48 +- src/Common/WuQMacroCommand.h | 14 +- src/Common/WuQMacroMouseEventInfo.cxx | 249 +++ src/Common/WuQMacroMouseEventInfo.h | 137 ++ src/Common/WuQMacroMouseEventTypeEnum.cxx | 380 +++++ src/Common/WuQMacroMouseEventTypeEnum.h | 108 ++ src/GuiQt/BrainBrowserWindowToolBar.cxx | 65 +- src/GuiQt/BrainBrowserWindowToolBar.h | 2 + .../BrainBrowserWindowToolBarSlicePlane.cxx | 17 +- ...rainBrowserWindowToolBarSliceSelection.cxx | 26 +- src/GuiQt/BrainOpenGLWidget.cxx | 32 +- src/GuiQt/BrainOpenGLWidget.h | 8 +- src/GuiQt/CMakeLists.txt | 4 +- src/GuiQt/MacroPrototype.cxx | 1379 ----------------- src/GuiQt/MacroPrototype.h | 373 ----- src/GuiQt/OverlayViewController.cxx | 32 +- src/GuiQt/WuQMacroExecutor.cxx | 58 +- src/GuiQt/WuQMacroManager.cxx | 110 +- src/GuiQt/WuQMacroManager.h | 6 + src/GuiQt/WuQMacroMenu.cxx | 18 +- src/GuiQt/WuQMacroMenu.h | 2 + src/GuiQt/WuQMacroMouseEventWidgetInterface.h | 51 + src/GuiQt/WuQMacroSignalWatcher.cxx | 13 + src/GuiQt/WuQMacroSignalWatcher.h | 3 +- src/GuiQt/WuQtUtilities.cxx | 57 +- src/GuiQt/WuQtUtilities.h | 8 - 27 files changed, 1313 insertions(+), 1891 deletions(-) create mode 100644 src/Common/WuQMacroMouseEventInfo.cxx create mode 100644 src/Common/WuQMacroMouseEventInfo.h create mode 100644 src/Common/WuQMacroMouseEventTypeEnum.cxx create mode 100644 src/Common/WuQMacroMouseEventTypeEnum.h delete mode 100644 src/GuiQt/MacroPrototype.cxx delete mode 100644 src/GuiQt/MacroPrototype.h create mode 100644 src/GuiQt/WuQMacroMouseEventWidgetInterface.h diff --git a/src/Common/CMakeLists.txt b/src/Common/CMakeLists.txt index a4cef3d1c..e643a8bcf 100644 --- a/src/Common/CMakeLists.txt +++ b/src/Common/CMakeLists.txt @@ -164,6 +164,8 @@ WuQMacro.h WuQMacroCommand.h WuQMacroGroup.h WuQMacroModeEnum.h +WuQMacroMouseEventInfo.h +WuQMacroMouseEventTypeEnum.h WuQMacroObjectTypeEnum.h YokingGroupEnum.h @@ -270,6 +272,8 @@ WuQMacro.cxx WuQMacroCommand.cxx WuQMacroGroup.cxx WuQMacroModeEnum.cxx +WuQMacroMouseEventInfo.cxx +WuQMacroMouseEventTypeEnum.cxx WuQMacroObjectTypeEnum.cxx YokingGroupEnum.cxx ) diff --git a/src/Common/WuQMacroCommand.cxx b/src/Common/WuQMacroCommand.cxx index ca06b11e7..94522db79 100644 --- a/src/Common/WuQMacroCommand.cxx +++ b/src/Common/WuQMacroCommand.cxx @@ -41,6 +41,8 @@ //#include #include "CaretAssert.h" +#include "WuQMacroMouseEventInfo.h" + //#include "WuQMacroSignalEmitter.h" using namespace caret; @@ -52,7 +54,7 @@ using namespace caret; */ /** - * Constructor. + * Constructor for a macro command * * @param objectType * Type of object @@ -64,22 +66,51 @@ using namespace caret; * Value for the command. */ WuQMacroCommand::WuQMacroCommand(const WuQMacroObjectTypeEnum::Enum objectType, - const QString objectName, - const QString objectToolTip, + const QString& objectName, + const QString& objectToolTip, const QVariant value) : CaretObjectTracksModification(), m_objectType(objectType), m_objectName(objectName), m_objectToolTip(objectToolTip), -m_value(value) +m_value(value), +m_macroMouseEvent(NULL) { } +/** + * Constructor for a macro command containing a mouse event + * + * @param objectType + * Type of object + * @param objectName + * Name of object + * @param objectToolTip + * Tooltip of object + * @param value + * Value for the command. + */ +WuQMacroCommand::WuQMacroCommand(const QString& objectName, + const QString& objectToolTip, + WuQMacroMouseEventInfo* mouseEventInfo) +: CaretObjectTracksModification(), +m_objectType(WuQMacroObjectTypeEnum::MOUSE_USER_EVENT), +m_objectName(objectName), +m_objectToolTip(objectToolTip), +m_value((int)0), +m_macroMouseEvent(mouseEventInfo) +{ + +} + /** * Destructor. */ WuQMacroCommand::~WuQMacroCommand() { + if (m_macroMouseEvent != NULL) { + delete m_macroMouseEvent; + } } /** @@ -118,6 +149,15 @@ WuQMacroCommand::getObjectValue() const return m_value; } +/** + * @return Point to mouse event information + */ +const WuQMacroMouseEventInfo* +WuQMacroCommand::getMouseEventInfo() const +{ + return m_macroMouseEvent; +} + ///** // * Run the command. // * diff --git a/src/Common/WuQMacroCommand.h b/src/Common/WuQMacroCommand.h index b1f8e16c0..bc5fca3fa 100644 --- a/src/Common/WuQMacroCommand.h +++ b/src/Common/WuQMacroCommand.h @@ -34,15 +34,21 @@ class QObject; namespace caret { + class WuQMacroMouseEventInfo; + class WuQMacroCommand : public CaretObjectTracksModification { public: WuQMacroCommand(const WuQMacroObjectTypeEnum::Enum objectType, - const QString objectName, - const QString objectToolTip, + const QString& objectName, + const QString& objectToolTip, const QVariant value); + WuQMacroCommand(const QString& objectName, + const QString& objectToolTip, + WuQMacroMouseEventInfo* mouseEventInfo); + virtual ~WuQMacroCommand(); WuQMacroCommand(const WuQMacroCommand&) = delete; @@ -57,6 +63,8 @@ namespace caret { QVariant getObjectValue() const; + const WuQMacroMouseEventInfo* getMouseEventInfo() const; + // bool runMacro(QObject* object, // QString& errorMessageOut) const; @@ -74,6 +82,8 @@ namespace caret { const QVariant m_value; + const WuQMacroMouseEventInfo* m_macroMouseEvent; + // ADD_NEW_MEMBERS_HERE }; diff --git a/src/Common/WuQMacroMouseEventInfo.cxx b/src/Common/WuQMacroMouseEventInfo.cxx new file mode 100644 index 000000000..d89d0a86b --- /dev/null +++ b/src/Common/WuQMacroMouseEventInfo.cxx @@ -0,0 +1,249 @@ + +/*LICENSE_START*/ +/* + * Copyright (C) 2018 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + +#define __WU_Q_MACRO_MOUSE_EVENT_INFO_DECLARE__ +#include "WuQMacroMouseEventInfo.h" +#undef __WU_Q_MACRO_MOUSE_EVENT_INFO_DECLARE__ + +#include "CaretAssert.h" +using namespace caret; + + + +/** + * \class caret::WuQMacroMouseEventInfo + * \brief Information about and related to a QMouseEvent + * \ingroup Common + */ + +/** + * Constructor. + * @param mouseEventType + * Type of mouse event + * @param localX + * Position of mouse relative to widget + * @param localY + * Position of mouse relative to widget + * @param windowX + * Position of mouse relative to window + * @param windowY + * Position of mouse relative to window + * @param screenX + * Position of mouse relative to screen + * @param screenY + * Position of mouse relative to screen + * @param mouseButton + * Button that caused the event + * @param mouseButtonsMask + * Mask with buttons down during mouse event + * @param keyboardModifiersMask + * Mask with any keys down during mouse event + * @param widgetWidth + * Width of widget where mouse event occurred + * @param widgetHeight + * Width of widget where mouse event occurred + */ +WuQMacroMouseEventInfo::WuQMacroMouseEventInfo(const WuQMacroMouseEventTypeEnum::Enum mouseEventType, + const int32_t localX, + const int32_t localY, + const int32_t windowX, + const int32_t windowY, + const int32_t screenX, + const int32_t screenY, + const uint32_t mouseButton, + const uint32_t mouseButtonsMask, + const uint32_t keyboardModifiersMask, + const int32_t widgetWidth, + const int32_t widgetHeight) +: CaretObject(), +m_mouseEventType(mouseEventType), +m_localX(localX), +m_localY(localY), +m_windowX(windowX), +m_windowY(windowY), +m_screenX(screenX), +m_screenY(screenY), +m_mouseButton(mouseButton), +m_mouseButtonsMask(mouseButtonsMask), +m_keyboardModifiersMask(keyboardModifiersMask), +m_widgetWidth(widgetWidth), +m_widgetHeight(widgetHeight) +{ + +} + +/** + * Destructor. + */ +WuQMacroMouseEventInfo::~WuQMacroMouseEventInfo() +{ +} + +/** + * Widget size may change so scale the x and y position to fit new widget size + * + * @param newWidth + * Current width of widget + * @param newHeight + * Current height of widget + * @param xLocalOut + * Output with adjusted local-X position + * @param yLocalOut + * Output with adjusted local-Y position + */ +void +WuQMacroMouseEventInfo::getLocalPositionRescaledToWidgetSize(const int32_t newWidth, + const int32_t newHeight, + int32_t& xLocalOut, + int32_t& yLocalOut) const +{ + xLocalOut = m_localX; + yLocalOut = m_localY; + + if ((newWidth != m_widgetWidth) + || (newHeight != m_widgetHeight)) { + const float normalizedWidth = (static_cast(m_localX) + / static_cast(m_widgetWidth)); + const float normalizedHeight = (static_cast(m_localY) + / static_cast(m_widgetHeight)); + + xLocalOut = (newWidth * normalizedWidth); + yLocalOut = (newHeight * normalizedHeight); + } +} + +/** + * @return Type of the mouse event + */ +WuQMacroMouseEventTypeEnum::Enum +WuQMacroMouseEventInfo::getMouseEventType() const +{ + return m_mouseEventType; +} + +/** + * @return Position of mouse relative to widget + */ +int32_t +WuQMacroMouseEventInfo::getLocalX() const +{ + return m_localX; +} + +/** + * @return Position of mouse relative to widget + */ +int32_t +WuQMacroMouseEventInfo::getLocalY() const +{ + return m_localY; +} + +/** + * @return Position of mouse relative to window + */ +int32_t +WuQMacroMouseEventInfo::getWindowX() const +{ + return m_windowX; +} + +/** + * @return Position of mouse relative to window + */ +int32_t +WuQMacroMouseEventInfo::getWindowY() const +{ + return m_windowY; +} + +/** + * @return Position of mouse relative to screen + */ +int32_t +WuQMacroMouseEventInfo::getScreenX() const +{ + return m_screenX; +} + +/** + * @return Position of mouse relative to screen + */ +int32_t +WuQMacroMouseEventInfo::getScreenY() const +{ + return m_screenY; +} + +/** + * @return Button that caused the event + */ +uint32_t +WuQMacroMouseEventInfo::getMouseButton() const +{ + return m_mouseButton; +} + +/** + * @return Mask with buttons down during mouse event + */ +uint32_t WuQMacroMouseEventInfo::getMouseButtonsMask() const +{ + return m_mouseButtonsMask; +} + +/** + * @@return Mask with any keys down during mouse event + */ +uint32_t +WuQMacroMouseEventInfo::getKeyboardModifiersMask() const +{ + return m_keyboardModifiersMask; +} + +/** + * @return Width of widget where mouse event occurred + */ +int32_t +WuQMacroMouseEventInfo::getWidgetWidth() const +{ + return m_widgetWidth; +} + +/** + * @return Width of widget where mouse event occurred + */ +int32_t +WuQMacroMouseEventInfo::getWidgetHeight() const +{ + return m_widgetHeight; +} + +/** + * Get a description of this object's content. + * @return String describing this object's content. + */ +AString +WuQMacroMouseEventInfo::toString() const +{ + return "WuQMacroMouseEventInfo"; +} + diff --git a/src/Common/WuQMacroMouseEventInfo.h b/src/Common/WuQMacroMouseEventInfo.h new file mode 100644 index 000000000..588ab91dd --- /dev/null +++ b/src/Common/WuQMacroMouseEventInfo.h @@ -0,0 +1,137 @@ +#ifndef __WU_Q_MACRO_MOUSE_EVENT_INFO_H__ +#define __WU_Q_MACRO_MOUSE_EVENT_INFO_H__ + +/*LICENSE_START*/ +/* + * Copyright (C) 2018 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + + + +#include + +#include "CaretObject.h" +#include "WuQMacroMouseEventTypeEnum.h" + + +namespace caret { + + class WuQMacroMouseEventInfo : public CaretObject { + + public: + WuQMacroMouseEventInfo(const WuQMacroMouseEventTypeEnum::Enum mouseEventType, + const int32_t localX, + const int32_t localY, + const int32_t windowX, + const int32_t windowY, + const int32_t screenX, + const int32_t screenY, + const uint32_t mouseButton, + const uint32_t mouseButtonsMask, + const uint32_t keyboardModifiersMask, + const int32_t widgetWidth, + const int32_t widgetHeight); + + virtual ~WuQMacroMouseEventInfo(); + + WuQMacroMouseEventInfo(const WuQMacroMouseEventInfo&) = delete; + + WuQMacroMouseEventInfo& operator=(const WuQMacroMouseEventInfo&) = delete; + + void getLocalPositionRescaledToWidgetSize(const int32_t widgetWidth, + const int32_t widgetHeight, + int32_t& xOut, + int32_t& yOut) const; + + WuQMacroMouseEventTypeEnum::Enum getMouseEventType() const; + + int32_t getLocalX() const; + + int32_t getLocalY() const; + + int32_t getWindowX() const; + + int32_t getWindowY() const; + + int32_t getScreenX() const; + + int32_t getScreenY() const; + + uint32_t getMouseButton() const; + + uint32_t getMouseButtonsMask() const; + + uint32_t getKeyboardModifiersMask() const; + + int32_t getWidgetWidth() const; + + int32_t getWidgetHeight() const; + + // ADD_NEW_METHODS_HERE + + virtual AString toString() const; + + private: + /** Type of mouse event */ + const WuQMacroMouseEventTypeEnum::Enum m_mouseEventType; + + /** Position of mouse relative to widget */ + const int32_t m_localX; + + /** Position of mouse relative to widget */ + const int32_t m_localY; + + /** Position of mouse relative to window */ + const int32_t m_windowX; + + /** Position of mouse relative to window */ + const int32_t m_windowY; + + /** Position of mouse relative to screen */ + const int32_t m_screenX; + + /** Position of mouse relative to screen */ + const int32_t m_screenY; + + /** Button that caused the event */ + const uint32_t m_mouseButton; + + /** Mask with buttons down during mouse event */ + const uint32_t m_mouseButtonsMask; + + /** Mask with any keys down during mouse event */ + const uint32_t m_keyboardModifiersMask; + + /** Width of widget where mouse event occurred */ + const int32_t m_widgetWidth; + + /** Width of widget where mouse event occurred */ + const int32_t m_widgetHeight; + + + // ADD_NEW_MEMBERS_HERE + + }; + +#ifdef __WU_Q_MACRO_MOUSE_EVENT_INFO_DECLARE__ + // +#endif // __WU_Q_MACRO_MOUSE_EVENT_INFO_DECLARE__ + +} // namespace +#endif //__WU_Q_MACRO_MOUSE_EVENT_INFO_H__ + diff --git a/src/Common/WuQMacroMouseEventTypeEnum.cxx b/src/Common/WuQMacroMouseEventTypeEnum.cxx new file mode 100644 index 000000000..10705e81a --- /dev/null +++ b/src/Common/WuQMacroMouseEventTypeEnum.cxx @@ -0,0 +1,380 @@ + +/*LICENSE_START*/ +/* + * Copyright (C) 2018 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + +#include +#define __WU_Q_MACRO_MOUSE_EVENT_TYPE_ENUM_DECLARE__ +#include "WuQMacroMouseEventTypeEnum.h" +#undef __WU_Q_MACRO_MOUSE_EVENT_TYPE_ENUM_DECLARE__ + +#include "CaretAssert.h" + +using namespace caret; + + +/** + * \class caret::WuQMacroMouseEventTypeEnum + * \brief Types of mouse events + * + * Using this enumerated type in the GUI with an EnumComboBoxTemplate + * + * Header File (.h) + * Forward declare the data type: + * class EnumComboBoxTemplate; + * + * Declare the member: + * EnumComboBoxTemplate* m_wuQMacroMouseEventTypeEnumComboBox; + * + * Declare a slot that is called when user changes selection + * private slots: + * void wuQMacroMouseEventTypeEnumComboBoxItemActivated(); + * + * Implementation File (.cxx) + * Include the header files + * #include "EnumComboBoxTemplate.h" + * #include "WuQMacroMouseEventTypeEnum.h" + * + * Instatiate: + * m_wuQMacroMouseEventTypeEnumComboBox = new EnumComboBoxTemplate(this); + * m_wuQMacroMouseEventTypeEnumComboBox->setup(); + * + * Get notified when the user changes the selection: + * QObject::connect(m_wuQMacroMouseEventTypeEnumComboBox, SIGNAL(itemActivated()), + * this, SLOT(wuQMacroMouseEventTypeEnumComboBoxItemActivated())); + * + * Update the selection: + * m_wuQMacroMouseEventTypeEnumComboBox->setSelectedItem(NEW_VALUE); + * + * Read the selection: + * const WuQMacroMouseEventTypeEnum::Enum VARIABLE = m_wuQMacroMouseEventTypeEnumComboBox->getSelectedItem(); + * + */ + +/** + * Constructor. + * + * @param enumValue + * An enumerated value. + * @param name + * Name of enumerated value. + * + * @param guiName + * User-friendly name for use in user-interface. + */ +WuQMacroMouseEventTypeEnum::WuQMacroMouseEventTypeEnum(const Enum enumValue, + const AString& name, + const AString& guiName) +{ + this->enumValue = enumValue; + this->integerCode = integerCodeCounter++; + this->name = name; + this->guiName = guiName; +} + +/** + * Destructor. + */ +WuQMacroMouseEventTypeEnum::~WuQMacroMouseEventTypeEnum() +{ +} + +/** + * Initialize the enumerated metadata. + */ +void +WuQMacroMouseEventTypeEnum::initialize() +{ + if (initializedFlag) { + return; + } + initializedFlag = true; + + enumData.push_back(WuQMacroMouseEventTypeEnum(BUTTON_PRESS, + "BUTTON_PRESS", + "Button-Press")); + + enumData.push_back(WuQMacroMouseEventTypeEnum(BUTTON_RELEASE, + "BUTTON_RELEASE", + "Button-Release")); + + enumData.push_back(WuQMacroMouseEventTypeEnum(DOUBLE_CLICK, + "DOUBLE_CLICK", + "Double-Click")); + + enumData.push_back(WuQMacroMouseEventTypeEnum(MOVE, + "MOVE", + "Move")); +} + +/** + * Find the data for and enumerated value. + * @param enumValue + * The enumerated value. + * @return Pointer to data for this enumerated type + * or NULL if no data for type or if type is invalid. + */ +const WuQMacroMouseEventTypeEnum* +WuQMacroMouseEventTypeEnum::findData(const Enum enumValue) +{ + if (initializedFlag == false) initialize(); + + size_t num = enumData.size(); + for (size_t i = 0; i < num; i++) { + const WuQMacroMouseEventTypeEnum* d = &enumData[i]; + if (d->enumValue == enumValue) { + return d; + } + } + + return NULL; +} + +/** + * Get a string representation of the enumerated type. + * @param enumValue + * Enumerated value. + * @return + * String representing enumerated value. + */ +AString +WuQMacroMouseEventTypeEnum::toName(Enum enumValue) { + if (initializedFlag == false) initialize(); + + const WuQMacroMouseEventTypeEnum* enumInstance = findData(enumValue); + return enumInstance->name; +} + +/** + * Get an enumerated value corresponding to its name. + * @param name + * Name of enumerated value. + * @param isValidOut + * If not NULL, it is set indicating that a + * enum value exists for the input name. + * @return + * Enumerated value. + */ +WuQMacroMouseEventTypeEnum::Enum +WuQMacroMouseEventTypeEnum::fromName(const AString& name, bool* isValidOut) +{ + if (initializedFlag == false) initialize(); + + bool validFlag = false; + Enum enumValue = WuQMacroMouseEventTypeEnum::enumData[0].enumValue; + + for (std::vector::iterator iter = enumData.begin(); + iter != enumData.end(); + iter++) { + const WuQMacroMouseEventTypeEnum& d = *iter; + if (d.name == name) { + enumValue = d.enumValue; + validFlag = true; + break; + } + } + + if (isValidOut != 0) { + *isValidOut = validFlag; + } + else if (validFlag == false) { + CaretAssertMessage(0, AString("Name " + name + "failed to match enumerated value for type WuQMacroMouseEventTypeEnum")); + } + return enumValue; +} + +/** + * Get a GUI string representation of the enumerated type. + * @param enumValue + * Enumerated value. + * @return + * String representing enumerated value. + */ +AString +WuQMacroMouseEventTypeEnum::toGuiName(Enum enumValue) { + if (initializedFlag == false) initialize(); + + const WuQMacroMouseEventTypeEnum* enumInstance = findData(enumValue); + return enumInstance->guiName; +} + +/** + * Get an enumerated value corresponding to its GUI name. + * @param s + * Name of enumerated value. + * @param isValidOut + * If not NULL, it is set indicating that a + * enum value exists for the input name. + * @return + * Enumerated value. + */ +WuQMacroMouseEventTypeEnum::Enum +WuQMacroMouseEventTypeEnum::fromGuiName(const AString& guiName, bool* isValidOut) +{ + if (initializedFlag == false) initialize(); + + bool validFlag = false; + Enum enumValue = WuQMacroMouseEventTypeEnum::enumData[0].enumValue; + + for (std::vector::iterator iter = enumData.begin(); + iter != enumData.end(); + iter++) { + const WuQMacroMouseEventTypeEnum& d = *iter; + if (d.guiName == guiName) { + enumValue = d.enumValue; + validFlag = true; + break; + } + } + + if (isValidOut != 0) { + *isValidOut = validFlag; + } + else if (validFlag == false) { + CaretAssertMessage(0, AString("guiName " + guiName + "failed to match enumerated value for type WuQMacroMouseEventTypeEnum")); + } + return enumValue; +} + +/** + * Get the integer code for a data type. + * + * @return + * Integer code for data type. + */ +int32_t +WuQMacroMouseEventTypeEnum::toIntegerCode(Enum enumValue) +{ + if (initializedFlag == false) initialize(); + const WuQMacroMouseEventTypeEnum* enumInstance = findData(enumValue); + return enumInstance->integerCode; +} + +/** + * Find the data type corresponding to an integer code. + * + * @param integerCode + * Integer code for enum. + * @param isValidOut + * If not NULL, on exit isValidOut will indicate if + * integer code is valid. + * @return + * Enum for integer code. + */ +WuQMacroMouseEventTypeEnum::Enum +WuQMacroMouseEventTypeEnum::fromIntegerCode(const int32_t integerCode, bool* isValidOut) +{ + if (initializedFlag == false) initialize(); + + bool validFlag = false; + Enum enumValue = WuQMacroMouseEventTypeEnum::enumData[0].enumValue; + + for (std::vector::iterator iter = enumData.begin(); + iter != enumData.end(); + iter++) { + const WuQMacroMouseEventTypeEnum& enumInstance = *iter; + if (enumInstance.integerCode == integerCode) { + enumValue = enumInstance.enumValue; + validFlag = true; + break; + } + } + + if (isValidOut != 0) { + *isValidOut = validFlag; + } + else if (validFlag == false) { + CaretAssertMessage(0, AString("Integer code " + AString::number(integerCode) + "failed to match enumerated value for type WuQMacroMouseEventTypeEnum")); + } + return enumValue; +} + +/** + * Get all of the enumerated type values. The values can be used + * as parameters to toXXX() methods to get associated metadata. + * + * @param allEnums + * A vector that is OUTPUT containing all of the enumerated values. + */ +void +WuQMacroMouseEventTypeEnum::getAllEnums(std::vector& allEnums) +{ + if (initializedFlag == false) initialize(); + + allEnums.clear(); + + for (std::vector::iterator iter = enumData.begin(); + iter != enumData.end(); + iter++) { + allEnums.push_back(iter->enumValue); + } +} + +/** + * Get all of the names of the enumerated type values. + * + * @param allNames + * A vector that is OUTPUT containing all of the names of the enumerated values. + * @param isSorted + * If true, the names are sorted in alphabetical order. + */ +void +WuQMacroMouseEventTypeEnum::getAllNames(std::vector& allNames, const bool isSorted) +{ + if (initializedFlag == false) initialize(); + + allNames.clear(); + + for (std::vector::iterator iter = enumData.begin(); + iter != enumData.end(); + iter++) { + allNames.push_back(WuQMacroMouseEventTypeEnum::toName(iter->enumValue)); + } + + if (isSorted) { + std::sort(allNames.begin(), allNames.end()); + } +} + +/** + * Get all of the GUI names of the enumerated type values. + * + * @param allNames + * A vector that is OUTPUT containing all of the GUI names of the enumerated values. + * @param isSorted + * If true, the names are sorted in alphabetical order. + */ +void +WuQMacroMouseEventTypeEnum::getAllGuiNames(std::vector& allGuiNames, const bool isSorted) +{ + if (initializedFlag == false) initialize(); + + allGuiNames.clear(); + + for (std::vector::iterator iter = enumData.begin(); + iter != enumData.end(); + iter++) { + allGuiNames.push_back(WuQMacroMouseEventTypeEnum::toGuiName(iter->enumValue)); + } + + if (isSorted) { + std::sort(allGuiNames.begin(), allGuiNames.end()); + } +} + diff --git a/src/Common/WuQMacroMouseEventTypeEnum.h b/src/Common/WuQMacroMouseEventTypeEnum.h new file mode 100644 index 000000000..d8f9ca5a5 --- /dev/null +++ b/src/Common/WuQMacroMouseEventTypeEnum.h @@ -0,0 +1,108 @@ +#ifndef __WU_Q_MACRO_MOUSE_EVENT_TYPE_ENUM_H__ +#define __WU_Q_MACRO_MOUSE_EVENT_TYPE_ENUM_H__ + +/*LICENSE_START*/ +/* + * Copyright (C) 2018 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + + +#include +#include +#include "AString.h" + +namespace caret { + +class WuQMacroMouseEventTypeEnum { + +public: + /** + * Enumerated values. + */ + enum Enum { + /** Mouse button is pressed */ + BUTTON_PRESS, + /** Mouse button is released */ + BUTTON_RELEASE, + /** Mouse is double-clicked */ + DOUBLE_CLICK, + /** Mouse is moved */ + MOVE + }; + + + ~WuQMacroMouseEventTypeEnum(); + + static AString toName(Enum enumValue); + + static Enum fromName(const AString& name, bool* isValidOut); + + static AString toGuiName(Enum enumValue); + + static Enum fromGuiName(const AString& guiName, bool* isValidOut); + + static int32_t toIntegerCode(Enum enumValue); + + static Enum fromIntegerCode(const int32_t integerCode, bool* isValidOut); + + static void getAllEnums(std::vector& allEnums); + + static void getAllNames(std::vector& allNames, const bool isSorted); + + static void getAllGuiNames(std::vector& allGuiNames, const bool isSorted); + +private: + WuQMacroMouseEventTypeEnum(const Enum enumValue, + const AString& name, + const AString& guiName); + + static const WuQMacroMouseEventTypeEnum* findData(const Enum enumValue); + + /** Holds all instance of enum values and associated metadata */ + static std::vector enumData; + + /** Initialize instances that contain the enum values and metadata */ + static void initialize(); + + /** Indicates instance of enum values and metadata have been initialized */ + static bool initializedFlag; + + /** Auto generated integer codes */ + static int32_t integerCodeCounter; + + /** The enumerated type value for an instance */ + Enum enumValue; + + /** The integer code associated with an enumerated value */ + int32_t integerCode; + + /** The name, a text string that is identical to the enumerated value */ + AString name; + + /** A user-friendly name that is displayed in the GUI */ + AString guiName; +}; + +#ifdef __WU_Q_MACRO_MOUSE_EVENT_TYPE_ENUM_DECLARE__ +std::vector WuQMacroMouseEventTypeEnum::enumData; +bool WuQMacroMouseEventTypeEnum::initializedFlag = false; +int32_t WuQMacroMouseEventTypeEnum::integerCodeCounter = 0; +#endif // __WU_Q_MACRO_MOUSE_EVENT_TYPE_ENUM_DECLARE__ + +} // namespace +#endif //__WU_Q_MACRO_MOUSE_EVENT_TYPE_ENUM_H__ diff --git a/src/GuiQt/BrainBrowserWindowToolBar.cxx b/src/GuiQt/BrainBrowserWindowToolBar.cxx index f26163690..0f4a50386 100644 --- a/src/GuiQt/BrainBrowserWindowToolBar.cxx +++ b/src/GuiQt/BrainBrowserWindowToolBar.cxx @@ -124,6 +124,7 @@ #include "VolumeSurfaceOutlineSetModel.h" #include "WuQDataEntryDialog.h" #include "WuQFactory.h" +#include "WuQMacroManager.h" #include "WuQMessageBox.h" #include "WuQTabBar.h" #include "WuQWidgetObjectGroup.h" @@ -177,8 +178,10 @@ BrainBrowserWindowToolBar::BrainBrowserWindowToolBar(const int32_t browserWindow /* * Needed for saving and restoring window state in main window */ - setObjectName("BrainBrowserWindowToolBar_" - + AString::number(browserWindowIndex)); + m_objectNamePrefix = ("Window_" + + QString::number(this->browserWindowIndex + 1) + + ":ToolBar"); + setObjectName(m_objectNamePrefix); /* * Create tab bar that displays models. @@ -256,6 +259,9 @@ BrainBrowserWindowToolBar::BrainBrowserWindowToolBar(const int32_t browserWindow this, this, SLOT(customViewActionTriggered())); + this->customViewAction->setObjectName(m_objectNamePrefix + + ":CustomView"); + WuQMacroManager::instance()->addMacroSupportToObject(this->customViewAction); /* * Actions at right side of toolbar @@ -2041,6 +2047,11 @@ BrainBrowserWindowToolBar::updateViewWidget(BrowserTabContent* browserTabContent QWidget* BrainBrowserWindowToolBar::createOrientationWidget() { + WuQMacroManager* macroManager = WuQMacroManager::instance(); + CaretAssert(macroManager); + const QString objectNamePrefix(m_objectNamePrefix + + ":Orientation:"); + this->viewOrientationLeftIcon = WuQtUtilities::loadIcon(":/ToolBar/view-left.png"); this->viewOrientationRightIcon = WuQtUtilities::loadIcon(":/ToolBar/view-right.png"); this->viewOrientationAnteriorIcon = WuQtUtilities::loadIcon(":/ToolBar/view-anterior.png"); @@ -2063,8 +2074,9 @@ BrainBrowserWindowToolBar::createOrientationWidget() else { this->orientationLeftOrLateralToolButtonAction->setIconText("L"); } - this->orientationLeftOrLateralToolButtonAction->setObjectName("ToolBar_Left_Or_LateralView"); - WuQObject::watchObjectForMacroRecording(this->orientationLeftOrLateralToolButtonAction); + this->orientationLeftOrLateralToolButtonAction->setObjectName(objectNamePrefix + + "Left_Or_LateralView"); + macroManager->addMacroSupportToObject(this->orientationLeftOrLateralToolButtonAction); this->orientationRightOrMedialToolButtonAction = WuQtUtilities::createAction("R", "View from a RIGHT perspective", @@ -2077,8 +2089,9 @@ BrainBrowserWindowToolBar::createOrientationWidget() else { this->orientationRightOrMedialToolButtonAction->setIconText("R"); } - this->orientationRightOrMedialToolButtonAction->setObjectName("ToolBar_Right_Or_Medial_View"); - WuQObject::watchObjectForMacroRecording(this->orientationRightOrMedialToolButtonAction); + this->orientationRightOrMedialToolButtonAction->setObjectName(objectNamePrefix + + "Right_Or_Medial_View"); + macroManager->addMacroSupportToObject(this->orientationRightOrMedialToolButtonAction); this->orientationAnteriorToolButtonAction = WuQtUtilities::createAction("A", "View from an ANTERIOR perspective", @@ -2091,8 +2104,9 @@ BrainBrowserWindowToolBar::createOrientationWidget() else { this->orientationAnteriorToolButtonAction->setIconText("A"); } - this->orientationAnteriorToolButtonAction->setObjectName("ToolBar_Anterior_View"); - WuQObject::watchObjectForMacroRecording(this->orientationAnteriorToolButtonAction); + this->orientationAnteriorToolButtonAction->setObjectName(objectNamePrefix + + "Anterior_View"); + macroManager->addMacroSupportToObject(this->orientationAnteriorToolButtonAction); this->orientationPosteriorToolButtonAction = WuQtUtilities::createAction("P", "View from a POSTERIOR perspective", @@ -2105,8 +2119,9 @@ BrainBrowserWindowToolBar::createOrientationWidget() else { this->orientationPosteriorToolButtonAction->setIconText("P"); } - this->orientationPosteriorToolButtonAction->setObjectName("ToolBar_Posterior_View"); - WuQObject::watchObjectForMacroRecording(this->orientationPosteriorToolButtonAction); + this->orientationPosteriorToolButtonAction->setObjectName(objectNamePrefix + + "Posterior_View"); + macroManager->addMacroSupportToObject(this->orientationPosteriorToolButtonAction); this->orientationDorsalToolButtonAction = WuQtUtilities::createAction("D", "View from a DORSAL perspective", @@ -2119,8 +2134,9 @@ BrainBrowserWindowToolBar::createOrientationWidget() else { this->orientationDorsalToolButtonAction->setIconText("D"); } - this->orientationDorsalToolButtonAction->setObjectName("ToolBar_Dorsal_View"); - WuQObject::watchObjectForMacroRecording(this->orientationDorsalToolButtonAction); + this->orientationDorsalToolButtonAction->setObjectName(objectNamePrefix + + "Dorsal_View"); + macroManager->addMacroSupportToObject(this->orientationDorsalToolButtonAction); this->orientationVentralToolButtonAction = WuQtUtilities::createAction("V", "View from a VENTRAL perspective", @@ -2133,8 +2149,9 @@ BrainBrowserWindowToolBar::createOrientationWidget() else { this->orientationVentralToolButtonAction->setIconText("V"); } - this->orientationVentralToolButtonAction->setObjectName("ToolBar_Ventral_View"); - WuQObject::watchObjectForMacroRecording(this->orientationVentralToolButtonAction); + this->orientationVentralToolButtonAction->setObjectName(objectNamePrefix + + "Ventral_View"); + macroManager->addMacroSupportToObject(this->orientationVentralToolButtonAction); this->orientationLateralMedialToolButtonAction = WuQtUtilities::createAction("LM", @@ -2142,32 +2159,36 @@ BrainBrowserWindowToolBar::createOrientationWidget() this, this, SLOT(orientationLateralMedialToolButtonTriggered(bool))); - this->orientationLateralMedialToolButtonAction->setObjectName("ToolBar_Lateral_Medial_View"); - WuQObject::watchObjectForMacroRecording(this->orientationLateralMedialToolButtonAction); + this->orientationLateralMedialToolButtonAction->setObjectName(objectNamePrefix + + "Lateral_Medial_View"); + macroManager->addMacroSupportToObject(this->orientationLateralMedialToolButtonAction); this->orientationDorsalVentralToolButtonAction = WuQtUtilities::createAction("DV", "View from a Dorsal/Ventral perspective", this, this, SLOT(orientationDorsalVentralToolButtonTriggered(bool))); - this->orientationDorsalVentralToolButtonAction->setObjectName("ToolBar_Dorsal_Ventral_View"); - WuQObject::watchObjectForMacroRecording(this->orientationDorsalVentralToolButtonAction); + this->orientationDorsalVentralToolButtonAction->setObjectName(objectNamePrefix + + "Dorsal_Ventral_View"); + macroManager->addMacroSupportToObject(this->orientationDorsalVentralToolButtonAction); this->orientationAnteriorPosteriorToolButtonAction = WuQtUtilities::createAction("AP", "View from a Anterior/Posterior perspective", this, this, SLOT(orientationAnteriorPosteriorToolButtonTriggered(bool))); - this->orientationAnteriorPosteriorToolButtonAction->setObjectName("ToolBar_Anterior_Posterior_View"); - WuQObject::watchObjectForMacroRecording(this->orientationAnteriorPosteriorToolButtonAction); + this->orientationAnteriorPosteriorToolButtonAction->setObjectName(objectNamePrefix + + "Anterior_Posterior_View"); + macroManager->addMacroSupportToObject(this->orientationAnteriorPosteriorToolButtonAction); this->orientationResetToolButtonAction = WuQtUtilities::createAction("R\nE\nS\nE\nT", "Reset the view to dorsal and remove any panning or zooming", this, this, SLOT(orientationResetToolButtonTriggered(bool))); - this->orientationResetToolButtonAction->setObjectName("ToolBar_Reset_View"); - WuQObject::watchObjectForMacroRecording(this->orientationResetToolButtonAction); + this->orientationResetToolButtonAction->setObjectName(objectNamePrefix + + "Reset_View"); + macroManager->addMacroSupportToObject(this->orientationResetToolButtonAction); this->orientationLeftOrLateralToolButton = new QToolButton(); this->orientationLeftOrLateralToolButton->setDefaultAction(this->orientationLeftOrLateralToolButtonAction); diff --git a/src/GuiQt/BrainBrowserWindowToolBar.h b/src/GuiQt/BrainBrowserWindowToolBar.h index 5e0388cc3..90bb44a48 100644 --- a/src/GuiQt/BrainBrowserWindowToolBar.h +++ b/src/GuiQt/BrainBrowserWindowToolBar.h @@ -437,6 +437,8 @@ namespace caret { QTimer* m_tileTabsHighlightingTimer = NULL; bool m_tileTabsHighlightingTimerEnabledFlag = true; + QString m_objectNamePrefix; + bool isContructorFinished; bool isDestructionInProgress; diff --git a/src/GuiQt/BrainBrowserWindowToolBarSlicePlane.cxx b/src/GuiQt/BrainBrowserWindowToolBarSlicePlane.cxx index 35205c4d2..73e9c528b 100644 --- a/src/GuiQt/BrainBrowserWindowToolBarSlicePlane.cxx +++ b/src/GuiQt/BrainBrowserWindowToolBarSlicePlane.cxx @@ -88,7 +88,7 @@ m_parentToolBar(parentToolBar) m_volumePlaneParasagittalToolButtonAction->setIcon(parasagittalIcon); } m_volumePlaneParasagittalToolButtonAction->setObjectName(objectNamePrefix - + "Parasagittal_Slice_View:Action"); + + "Parasagittal_Slice_View"); macroManager->addMacroSupportToObject(m_volumePlaneParasagittalToolButtonAction); m_volumePlaneCoronalToolButtonAction = WuQtUtilities::createAction(VolumeSliceViewPlaneEnum::toGuiNameAbbreviation(VolumeSliceViewPlaneEnum::CORONAL), @@ -99,7 +99,7 @@ m_parentToolBar(parentToolBar) m_volumePlaneCoronalToolButtonAction->setIcon(coronalIcon); } m_volumePlaneCoronalToolButtonAction->setObjectName(objectNamePrefix - + "Coronal_Slice_View:Action"); + + "Coronal_Slice_View"); macroManager->addMacroSupportToObject(m_volumePlaneCoronalToolButtonAction); m_volumePlaneAxialToolButtonAction = WuQtUtilities::createAction(VolumeSliceViewPlaneEnum::toGuiNameAbbreviation(VolumeSliceViewPlaneEnum::AXIAL), @@ -110,8 +110,8 @@ m_parentToolBar(parentToolBar) m_volumePlaneAxialToolButtonAction->setIcon(axialIcon); } m_volumePlaneAxialToolButtonAction->setObjectName(objectNamePrefix - + "Axial_Slice_View:Action"); - WuQObject::watchObjectForMacroRecording(m_volumePlaneAxialToolButtonAction); + + "Axial_Slice_View"); + macroManager->addMacroSupportToObject(m_volumePlaneAxialToolButtonAction); m_volumePlaneAllToolButtonAction = WuQtUtilities::createAction(VolumeSliceViewPlaneEnum::toGuiNameAbbreviation(VolumeSliceViewPlaneEnum::ALL), "View the PARASAGITTAL, CORONAL, and AXIAL slices\n" @@ -120,7 +120,7 @@ m_parentToolBar(parentToolBar) m_volumePlaneAllToolButtonAction->setCheckable(true); m_volumePlaneAllToolButtonAction->setMenu(createViewAllSlicesLayoutMenu()); m_volumePlaneAllToolButtonAction->setObjectName(objectNamePrefix - + "All_Slices_View:Action"); + + "All_Slices_View"); macroManager->addMacroSupportToObject(m_volumePlaneAllToolButtonAction); @@ -140,7 +140,7 @@ m_parentToolBar(parentToolBar) this, SLOT(volumePlaneResetToolButtonTriggered(bool))); m_volumePlaneResetToolButtonAction->setObjectName(objectNamePrefix - + "Reset_View:Action"); + + "Reset_View"); macroManager->addMacroSupportToObject(m_volumePlaneResetToolButtonAction); @@ -182,7 +182,6 @@ m_parentToolBar(parentToolBar) slicePlaneCustomToolButton->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Fixed); WuQtUtilities::setToolButtonStyleForQt5Mac(slicePlaneCustomToolButton); - macroManager->addMacroSupportToObject(m_parentToolBar->customViewAction); m_volumeAxisCrosshairsToolButtonAction = new QAction("", this); m_volumeAxisCrosshairsToolButtonAction->setCheckable(true); @@ -190,7 +189,7 @@ m_parentToolBar(parentToolBar) QObject::connect(m_volumeAxisCrosshairsToolButtonAction, &QAction::triggered, this, &BrainBrowserWindowToolBarSlicePlane::volumeAxisCrosshairsTriggered); m_volumeAxisCrosshairsToolButtonAction->setObjectName(objectNamePrefix - + "Show_Volume_Slice_Crosshairs:Action"); + + "Show_Volume_Slice_Crosshairs"); macroManager->addMacroSupportToObject(m_volumeAxisCrosshairsToolButtonAction); QToolButton* volumeCrosshairsToolButton = new QToolButton(); @@ -206,7 +205,7 @@ m_parentToolBar(parentToolBar) QObject::connect(m_volumeAxisCrosshairLabelsToolButtonAction, &QAction::triggered, this, &BrainBrowserWindowToolBarSlicePlane::volumeAxisCrosshairLabelsTriggered); m_volumeAxisCrosshairLabelsToolButtonAction->setObjectName(objectNamePrefix - + "Show_Volume_Slice_Labels:Action"); + + "Show_Volume_Slice_Labels"); macroManager->addMacroSupportToObject(m_volumeAxisCrosshairLabelsToolButtonAction); QToolButton* volumeCrosshairLabelsToolButton = new QToolButton(); diff --git a/src/GuiQt/BrainBrowserWindowToolBarSliceSelection.cxx b/src/GuiQt/BrainBrowserWindowToolBarSliceSelection.cxx index aed11f177..4125de972 100644 --- a/src/GuiQt/BrainBrowserWindowToolBarSliceSelection.cxx +++ b/src/GuiQt/BrainBrowserWindowToolBarSliceSelection.cxx @@ -86,7 +86,7 @@ m_parentToolBar(parentToolBar) volumeIndicesOriginToolButton->setDefaultAction(volumeIndicesOriginToolButtonAction); WuQtUtilities::setToolButtonStyleForQt5Mac(volumeIndicesOriginToolButton); volumeIndicesOriginToolButtonAction->setObjectName(objectNamePrefix - + "Move_Volume_Slices_to_Origin:Action"); + + "Move_Volume_Slices_to_Origin"); volumeIndicesOriginToolButtonAction->setParent(volumeIndicesOriginToolButton); macroManager->addMacroSupportToObject(volumeIndicesOriginToolButtonAction); @@ -98,7 +98,7 @@ m_parentToolBar(parentToolBar) WuQtUtilities::setToolTipAndStatusTip(m_volumeIndicesParasagittalCheckBox, "Enable/Disable display of PARASAGITTAL slice"); m_volumeIndicesParasagittalCheckBox->setObjectName(objectNamePrefix - + "Enable_Parasagittal_Slice:CheckBox"); + + "Enable_Parasagittal_Slice"); QObject::connect(m_volumeIndicesParasagittalCheckBox, SIGNAL(stateChanged(int)), this, SLOT(volumeIndicesParasagittalCheckBoxStateChanged(int))); macroManager->addMacroSupportToObject(m_volumeIndicesParasagittalCheckBox); @@ -107,7 +107,7 @@ m_parentToolBar(parentToolBar) WuQtUtilities::setToolTipAndStatusTip(m_volumeIndicesCoronalCheckBox, "Enable/Disable display of CORONAL slice"); m_volumeIndicesCoronalCheckBox->setObjectName(objectNamePrefix - + "Enable_Coronal_Slice:CheckBox"); + + "Enable_Coronal_Slice"); QObject::connect(m_volumeIndicesCoronalCheckBox, SIGNAL(stateChanged(int)), this, SLOT(volumeIndicesCoronalCheckBoxStateChanged(int))); macroManager->addMacroSupportToObject(m_volumeIndicesCoronalCheckBox); @@ -117,7 +117,7 @@ m_parentToolBar(parentToolBar) "Enable/Disable display of AXIAL slice"); m_volumeIndicesAxialCheckBox->setObjectName(objectNamePrefix - + "Enable_Axial_Slice:CheckBox"); + + "Enable_Axial_Slice"); QObject::connect(m_volumeIndicesAxialCheckBox, SIGNAL(stateChanged(int)), this, SLOT(volumeIndicesAxialCheckBoxStateChanged(int))); macroManager->addMacroSupportToObject(m_volumeIndicesAxialCheckBox); @@ -132,7 +132,7 @@ m_parentToolBar(parentToolBar) QObject::connect(m_volumeIndicesParasagittalSpinBox, SIGNAL(valueChanged(int)), this, SLOT(volumeIndicesParasagittalSpinBoxValueChanged(int))); m_volumeIndicesParasagittalSpinBox->setObjectName(objectNamePrefix - + "Volume_Parasagittal_Slice_Index:SpinBox"); + + "Volume_Parasagittal_Slice_Index"); macroManager->addMacroSupportToObject(m_volumeIndicesParasagittalSpinBox); m_volumeIndicesCoronalSpinBox = WuQFactory::newSpinBox(); @@ -142,7 +142,7 @@ m_parentToolBar(parentToolBar) QObject::connect(m_volumeIndicesCoronalSpinBox, SIGNAL(valueChanged(int)), this, SLOT(volumeIndicesCoronalSpinBoxValueChanged(int))); m_volumeIndicesCoronalSpinBox->setObjectName(objectNamePrefix - + "Volume_Coronal_Slice_Index:SpinBox"); + + "Volume_Coronal_Slice_Index"); macroManager->addMacroSupportToObject(m_volumeIndicesCoronalSpinBox); m_volumeIndicesAxialSpinBox = WuQFactory::newSpinBox(); @@ -152,7 +152,7 @@ m_parentToolBar(parentToolBar) QObject::connect(m_volumeIndicesAxialSpinBox, SIGNAL(valueChanged(int)), this, SLOT(volumeIndicesAxialSpinBoxValueChanged(int))); m_volumeIndicesAxialSpinBox->setObjectName(objectNamePrefix - + "Volume_Axial_Slice_Index:SpinBox"); + + "Volume_Axial_Slice_Index"); macroManager->addMacroSupportToObject(m_volumeIndicesAxialSpinBox); m_volumeIndicesXcoordSpinBox = WuQFactory::newDoubleSpinBox(); @@ -163,7 +163,7 @@ m_parentToolBar(parentToolBar) QObject::connect(m_volumeIndicesXcoordSpinBox, SIGNAL(valueChanged(double)), this, SLOT(volumeIndicesXcoordSpinBoxValueChanged(double))); m_volumeIndicesXcoordSpinBox->setObjectName(objectNamePrefix - + "Volume_Parasagittal_Coordinate:DoubleSpinBox"); + + "Volume_Parasagittal_Coordinate"); macroManager->addMacroSupportToObject(m_volumeIndicesXcoordSpinBox); m_volumeIndicesYcoordSpinBox = WuQFactory::newDoubleSpinBox(); @@ -174,7 +174,7 @@ m_parentToolBar(parentToolBar) QObject::connect(m_volumeIndicesYcoordSpinBox, SIGNAL(valueChanged(double)), this, SLOT(volumeIndicesYcoordSpinBoxValueChanged(double))); m_volumeIndicesYcoordSpinBox->setObjectName(objectNamePrefix - + "Volume_Coronal_Coordinate:DoubleSpinBox"); + + "Volume_Coronal_Coordinate"); macroManager->addMacroSupportToObject(m_volumeIndicesYcoordSpinBox); m_volumeIndicesZcoordSpinBox = WuQFactory::newDoubleSpinBox(); @@ -185,7 +185,7 @@ m_parentToolBar(parentToolBar) QObject::connect(m_volumeIndicesZcoordSpinBox, SIGNAL(valueChanged(double)), this, SLOT(volumeIndicesZcoordSpinBoxValueChanged(double))); m_volumeIndicesZcoordSpinBox->setObjectName(objectNamePrefix - + "Volume_Axial_Coordinate:DoubleSpinBox"); + + "Volume_Axial_Coordinate"); macroManager->addMacroSupportToObject(m_volumeIndicesZcoordSpinBox); const AString idToolTipText = ("When selected: If there is an identification operation " @@ -213,7 +213,7 @@ m_parentToolBar(parentToolBar) volumeIDToolButton->setDefaultAction(m_volumeIdentificationUpdatesSlicesAction); WuQtUtilities::setToolButtonStyleForQt5Mac(volumeIDToolButton); m_volumeIdentificationUpdatesSlicesAction->setObjectName(objectNamePrefix - + "Move_Slice_to_ID:Action"); + + "Move_Slice_to_ID"); macroManager->addMacroSupportToObject(m_volumeIdentificationUpdatesSlicesAction); m_volumeSliceProjectionTypeEnumComboBox = new EnumComboBoxTemplate(this); @@ -224,7 +224,7 @@ m_parentToolBar(parentToolBar) WuQtUtilities::setToolTipAndStatusTip(m_volumeSliceProjectionTypeEnumComboBox->getWidget(), "Chooses viewing orientation (oblique or orthogonal)"); m_volumeSliceProjectionTypeEnumComboBox->getComboBox()->setObjectName(objectNamePrefix - + "Orthogonal/Oblique:ComboBox"); + + "Orthogonal/Oblique"); macroManager->addMacroSupportToObject(m_volumeSliceProjectionTypeEnumComboBox->getComboBox()); m_obliqueMaskingAction = new QAction("M", this); @@ -233,7 +233,7 @@ m_parentToolBar(parentToolBar) QObject::connect(m_obliqueMaskingAction, &QAction::triggered, this, &BrainBrowserWindowToolBarSliceSelection::obliqueMaskingActionTriggered); m_obliqueMaskingAction->setObjectName(objectNamePrefix - + "Oblique_Masking:Action"); + + "Oblique_Masking"); macroManager->addMacroSupportToObject(m_obliqueMaskingAction); QToolButton* obliqueMaskingToolButton = new QToolButton(); diff --git a/src/GuiQt/BrainOpenGLWidget.cxx b/src/GuiQt/BrainOpenGLWidget.cxx index 0950915bd..fcf742568 100644 --- a/src/GuiQt/BrainOpenGLWidget.cxx +++ b/src/GuiQt/BrainOpenGLWidget.cxx @@ -82,6 +82,7 @@ #include "UserInputModeImage.h" #include "UserInputModeView.h" #include "UserInputModeVolumeEdit.h" +#include "WuQMacroManager.h" #include "WuQMessageBox.h" using namespace caret; @@ -108,7 +109,9 @@ BrainOpenGLWidget::BrainOpenGLWidget(QWidget* parent, #endif windowIndex(windowIndex) { - setObjectName("m_openGLWidget"); + setObjectName("Window_" + + AString::number(windowIndex + 1) + + ":OpenGLWidget"); this->borderBeingDrawn = new Border(); @@ -865,7 +868,8 @@ BrainOpenGLWidget::keyReleaseEvent(QKeyEvent* e) void BrainOpenGLWidget::mousePressEvent(QMouseEvent* me) { - MacroManager::get()->recordMouseEvent(this, me); + WuQMacroManager::instance()->addMouseEventToRecording(this, + me); Qt::MouseButton button = me->button(); Qt::KeyboardModifiers keyModifiers = me->modifiers(); @@ -952,7 +956,8 @@ BrainOpenGLWidget::mousePressEvent(QMouseEvent* me) void BrainOpenGLWidget::mouseReleaseEvent(QMouseEvent* me) { - MacroManager::get()->recordMouseEvent(this, me); + WuQMacroManager::instance()->addMouseEventToRecording(this, + me); Qt::MouseButton button = me->button(); Qt::KeyboardModifiers keyModifiers = me->modifiers(); @@ -1049,7 +1054,8 @@ BrainOpenGLWidget::mouseReleaseEvent(QMouseEvent* me) void BrainOpenGLWidget::mouseDoubleClickEvent(QMouseEvent* me) { - MacroManager::get()->recordMouseEvent(this, me); + WuQMacroManager::instance()->addMouseEventToRecording(this, + me); Qt::MouseButton button = me->button(); Qt::KeyboardModifiers keyModifiers = me->modifiers(); @@ -1393,7 +1399,8 @@ BrainOpenGLWidget::performProjection(const int x, void BrainOpenGLWidget::mouseMoveEvent(QMouseEvent* me) { - MacroManager::get()->recordMouseEvent(this, me); + WuQMacroManager::instance()->addMouseEventToRecording(this, + me); /* * Tooltip will remain displayed for several seconds. @@ -1861,15 +1868,14 @@ BrainOpenGLWidget::initializeDefaultGLFormat() s_defaultGLFormatInitialized = true; } -QSize -BrainOpenGLWidget::getSizeOfWidget() const -{ - return size(); -} - - +/** + * Process a mouse event from the macro system. + * + * @param me + * The mouse event + */ void -BrainOpenGLWidget::processMouseEvent(QMouseEvent* me) +BrainOpenGLWidget::processMouseEventFromMacro(QMouseEvent* me) { m_mousePositionValid = true; switch (me->type()) { diff --git a/src/GuiQt/BrainOpenGLWidget.h b/src/GuiQt/BrainOpenGLWidget.h index bc0025e76..fa4f3d0e2 100644 --- a/src/GuiQt/BrainOpenGLWidget.h +++ b/src/GuiQt/BrainOpenGLWidget.h @@ -45,7 +45,7 @@ #include "BrainOpenGLWindowContent.h" #include "CaretPointer.h" #include "EventListenerInterface.h" -#include "MacroPrototype.h" +#include "WuQMacroMouseEventWidgetInterface.h" class QMouseEvent; class QWidget; @@ -74,7 +74,7 @@ namespace caret { #ifdef WORKBENCH_USE_QT5_QOPENGL_WIDGET class BrainOpenGLWidget : public QOpenGLWidget, public EventListenerInterface { #else - class BrainOpenGLWidget : public QGLWidget, public EventListenerInterface, public MacroEventMouseInfoWidgetInterface { + class BrainOpenGLWidget : public QGLWidget, public EventListenerInterface, public WuQMacroMouseEventWidgetInterface { #endif Q_OBJECT @@ -117,9 +117,7 @@ namespace caret { QImage performOffScreenImageCapture(const int32_t imageWidth, const int32_t imageHeight); - virtual void processMouseEvent(QMouseEvent*) override; - - virtual QSize getSizeOfWidget() const override; + virtual void processMouseEventFromMacro(QMouseEvent* me) override; protected: virtual void initializeGL(); diff --git a/src/GuiQt/CMakeLists.txt b/src/GuiQt/CMakeLists.txt index 53e82d978..d59f0e73c 100644 --- a/src/GuiQt/CMakeLists.txt +++ b/src/GuiQt/CMakeLists.txt @@ -138,7 +138,6 @@ ELSE() LockAspectWarningDialog.h MacApplication.h MacDockMenu.h - MacroPrototype.h MapSettingsChartTwoLineHistoryWidget.h MapSettingsColorBarPaletteOptionsWidget.h MapSettingsColorBarWidget.h @@ -379,7 +378,6 @@ LabelSelectionViewController.h LockAspectWarningDialog.h MacApplication.h MacDockMenu.h -MacroPrototype.h MapSettingsChartTwoLineHistoryWidget.h MapSettingsColorBarPaletteOptionsWidget.h MapSettingsColorBarWidget.h @@ -464,6 +462,7 @@ WuQMacroDialog.h WuQMacroExecutor.h WuQMacroManager.h WuQMacroMenu.h +WuQMacroMouseEventWidgetInterface.h WuQMacroSignalEmitter.h WuQMacroSignalWatcher.h WuQMessageBox.h @@ -620,7 +619,6 @@ LabelSelectionViewController.cxx LockAspectWarningDialog.cxx MacApplication.cxx MacDockMenu.cxx -MacroPrototype.cxx MapSettingsChartTwoLineHistoryWidget.cxx MapSettingsColorBarPaletteOptionsWidget.cxx MapSettingsColorBarWidget.cxx diff --git a/src/GuiQt/MacroPrototype.cxx b/src/GuiQt/MacroPrototype.cxx deleted file mode 100644 index 2d89b4c99..000000000 --- a/src/GuiQt/MacroPrototype.cxx +++ /dev/null @@ -1,1379 +0,0 @@ - -/*LICENSE_START*/ -/* - * Copyright (C) 2018 Washington University School of Medicine - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ -/*LICENSE_END*/ - -#include - -#define __WU_Q_OBJECT_DECLARE__ -#include "MacroPrototype.h" -#undef __WU_Q_OBJECT_DECLARE__ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "CaretAssert.h" -#include "CaretLogger.h" -#include "EventUserInterfaceUpdate.h" -#include "EventManager.h" -#include "WuQMessageBox.h" -#include "WuQtUtilities.h" - -using namespace caret; - - - -/** - * \class caret::WuQObject - * \brief - * \ingroup GuiQt - * - * - */ - -/** - * Constructor. - */ -WuQObject::WuQObject() -: QObject() -{ - -} - -/** - * Destructor. - */ -WuQObject::~WuQObject() -{ -} - -QMetaObject::Connection -WuQObject::connect(QObject *sender, - const char *signal, - const QObject *receiver, - const char *method, - Qt::ConnectionType type) -{ - CaretAssert(sender); - const QString name = sender->objectName(); - if (name.isEmpty()) { - CaretLogWarning("Object name is empty, will be ignored for macros"); - } - else { - - auto existingWatcher = s_widgetWatchers.find(name); - if (existingWatcher != s_widgetWatchers.end()) { - CaretLogWarning("Widget named \"" - + name - + "\" has already been connected for macros"); - } - else { - AString errorMessage; - WidgetWatcher* widgetWatcher = WidgetWatcher::newInstance(sender, - signal, - errorMessage); - if (widgetWatcher != NULL) { - s_widgetWatchers.insert(std::make_pair(name, - widgetWatcher)); - } - else { - CaretLogWarning(errorMessage); - } - } - } - - QMetaObject::Connection connection = QObject::connect(sender, - signal, - receiver, - method, - type); - const bool validConnection(connection); - CaretAssert(validConnection); - - return connection; -} - -void -WuQObject::watchObjectForMacroRecording(QObject* sender) -{ - CaretAssert(sender); - - QAction* action = qobject_cast(sender); - if (action != NULL) { - watchObjectForMacroRecordingPrivate(action, - SIGNAL(triggered(bool))); - return; - } - - QComboBox* comboBox = qobject_cast(sender); - if (comboBox != NULL) { - watchObjectForMacroRecordingPrivate(comboBox, - SIGNAL(activated(int))); - return; - } - - CaretLogWarning("Widget type with name " - + sender->objectName() - + " of class " - + QString(sender->metaObject()->className()) - + " not supported in watchObjectForMacroRecording()"); -} - -void -WuQObject::watchObjectForMacroRecordingPrivate(QObject* sender, - const char* signal) -{ - CaretAssert(sender); - const QString name = sender->objectName(); - if (name.isEmpty()) { - CaretLogWarning("Object name is empty, will be ignored for macros"); - } - else { - - auto existingWatcher = s_widgetWatchers.find(name); - if (existingWatcher != s_widgetWatchers.end()) { - CaretLogWarning("Widget named \"" - + name - + "\" has already been connected for macros"); - } - else { - AString errorMessage; - WidgetWatcher* widgetWatcher = WidgetWatcher::newInstance(sender, - signal, - errorMessage); - if (widgetWatcher != NULL) { - s_widgetWatchers.insert(std::make_pair(name, - widgetWatcher)); - } - else { - CaretLogWarning(errorMessage); - } - } - } -} - -//QMetaObject::Connection -//WuQObject::connectFP(QObject *sender, -// QObjectBoolPointerToMemberFunction signal, -// QObject* receiver, -// QObjectBoolPointerToMemberFunction method, -// Qt::ConnectionType type) -//{ -//// QMetaObject::Connection connection; -// QMetaObject::Connection connection = QObject::connect(sender, -// signal, -// receiver, -// method, -// type); -// const bool validConnection(connection); -// CaretAssert(validConnection); -// -// return connection; -// -//} - -/* ========================================================================= */ - -/** - * @return Create and return the macro menu. - */ -MacroMenu::MacroMenu(QMainWindow* mainWindowParent) -: QMenu(mainWindowParent), -m_mainWindowParent(mainWindowParent) -{ - CaretAssert(m_mainWindowParent); - - m_macroEraseAction = WuQtUtilities::createAction("Erase", - "Erase the Macro", - this, - this, - SLOT(processMacroErase())); - - m_macroRecordAction = WuQtUtilities::createAction("Record", - "Record a Macro", - this, - this, - SLOT(processMacroRecord())); - m_macroRecordAction->setCheckable(true); - - m_macroRunAction = WuQtUtilities::createAction("Run", - "Run a Macro", - this, - this, - SLOT(processMacroRun())); - - m_macroLoadAction = WuQtUtilities::createAction("Load from File...", - "Load a Macro", - this, - this, - SLOT(processMacroLoad())); - - m_macroSaveAction = WuQtUtilities::createAction("Save to File...", - "Save a Macro", - this, - this, - SLOT(processMacroSave())); - - setTitle("Macro"); - QObject::connect(this, SIGNAL(aboutToShow()), - this, SLOT(macroMenuAboutToShow())); - addAction(m_macroEraseAction); - addAction(m_macroRecordAction); - addAction(m_macroRunAction); - addSeparator(); - addAction(m_macroLoadAction); - addAction(m_macroSaveAction); -} - -/** - * Called when macro menu is about to show. - */ -void -MacroMenu::macroMenuAboutToShow() -{ - bool enableEraseFlag(false); - bool enableRecordFlag(true); - bool enableRunFlag(true); - bool recordingFlag(false); - bool saveFlag(false); - bool loadFlag(false); - switch (MacroManager::get()->getMode()) { - case MacroManager::Mode::OFF: - enableEraseFlag = true; - saveFlag = true; - loadFlag = true; - break; - case MacroManager::Mode::RECORDING: - enableRunFlag = false; - recordingFlag = true; - break; - case MacroManager::Mode::RUNNING: - enableRecordFlag = false; - break; - } - - m_macroEraseAction->setEnabled(enableEraseFlag); - m_macroRecordAction->setEnabled(enableRecordFlag); - m_macroRecordAction->setChecked(recordingFlag); - m_macroRunAction->setEnabled(enableRunFlag); - m_macroLoadAction->setEnabled(loadFlag); - m_macroSaveAction->setEnabled(saveFlag); -} - -/* - * Called to erase the current macro - */ -void -MacroMenu::processMacroErase() -{ - MacroManager::get()->eraseMacro(); -} - -/** - * Called to record a macro - */ -void -MacroMenu::processMacroRecord() -{ - switch (MacroManager::get()->getMode()) { - case MacroManager::Mode::OFF: - MacroManager::get()->setMode(MacroManager::Mode::RECORDING); - break; - case MacroManager::Mode::RECORDING: - MacroManager::get()->setMode(MacroManager::Mode::OFF); - break; - case MacroManager::Mode::RUNNING: - break; - } -} - -/** - * Called to run a macro - */ -void -MacroMenu::processMacroRun() -{ - switch (MacroManager::get()->getMode()) { - case MacroManager::Mode::OFF: - { - AString errorMessage; - if ( ! MacroManager::get()->runMacro(m_mainWindowParent, - errorMessage)) { - WuQMessageBox::errorOk(m_mainWindowParent, errorMessage); - } - } - break; - case MacroManager::Mode::RECORDING: - break; - case MacroManager::Mode::RUNNING: - MacroManager::get()->setMode(MacroManager::Mode::OFF); - break; - } -} -/** - * Called to load a macro - */ -void -MacroMenu::processMacroLoad() -{ - const QString filename = QFileDialog::getOpenFileName(m_mainWindowParent, - "Open Macro File", - "", - "Macro File (*.wb_macro)"); - if ( ! filename.isNull()) { - MacroManager::get()->readMacroFromFile(filename); - } -} - -/** - * Called to save a macro - */ -void -MacroMenu::processMacroSave() -{ - const QString filename = QFileDialog::getSaveFileName(m_mainWindowParent, - "Save Macro File", - "", - "Macro File (*.wb_macro)"); - if ( ! filename.isNull()) { - MacroManager::get()->saveMacroToFile(filename); - } -} - -/* ========================================================================= */ - -MacroEventMouseInfo::MacroEventMouseInfo() -{ - -} - -MacroEventMouseInfo::MacroEventMouseInfo(QMouseEvent* me, - const int32_t widgetWidth, - const int32_t widgetHeight) -{ - m_type = me->type(); - m_localPos = me->localPos(); - m_button = me->button(); - m_buttons = me->buttons(); - m_modifiers = me->modifiers(); - m_widgetWidth = widgetWidth; - m_widgetHeight = widgetHeight; -} - -/* - * Widget may be a different size than it was when the event was recorded - */ -QPointF -MacroEventMouseInfo::getLocalPosRescaledToSize(const QSize& widgetSize) const -{ - QPointF pointOut = m_localPos; - - const int32_t newWidth = widgetSize.width(); - const int32_t newHeight = widgetSize.height(); - - if ((newWidth != m_widgetWidth) - || (newHeight != m_widgetHeight)) { - const float normalizedWidth = (static_cast(m_localPos.x()) - / static_cast(m_widgetWidth)); - const float normalizedHeight = (static_cast(m_localPos.y()) - / static_cast(m_widgetHeight)); - - pointOut.setX(newWidth * normalizedWidth); - pointOut.setY(newHeight * normalizedHeight); - } - - return pointOut; -} - -QString -MacroEventMouseInfo::toString() const -{ - const QString separator("*"); - QString s(QString::number((int)m_type) - + separator - + QString::number(m_localPos.x()) - + separator - + QString::number(m_localPos.y()) - + separator - + QString::number((int)m_button) - + separator - + QString::number((int)m_buttons) - + separator - + QString::number((int)m_modifiers) - + separator - + QString::number(m_widgetWidth) - + separator - + QString::number(m_widgetHeight)); - - return s; -} - -bool -MacroEventMouseInfo::fromString(const QString& s) -{ - const QString separator("*"); - QStringList components = s.split(separator); - if (components.size() == 8) { - m_type = static_cast(components[0].toInt()); - m_localPos.setX(components[1].toFloat()); - m_localPos.setY(components[2].toFloat()); - m_button = static_cast(components[3].toInt()); - m_buttons = static_cast(components[4].toInt()); - m_modifiers = static_cast(components[5].toInt()); - m_widgetWidth = components[6].toInt(); - m_widgetHeight = components[7].toInt(); - } - else { - CaretLogWarning("Invalid value string for mouse info: " - + s); - return false; - } - return true; -} - -/* ========================================================================= */ - -MacroEvent::MacroEvent(const WidgetWatcherType widgetType, - const QString& widgetName) -: m_widgetType(widgetType), -m_widgetName(widgetName) -{ - m_valueType = ValueType::BOOLEAN; - switch (m_widgetType) { - case ACTION: - m_valueType = ValueType::BOOLEAN; - break; - case CHECK_BOX: - m_valueType = ValueType::BOOLEAN; - break; - case COMBO_BOX: - m_valueType = ValueType::INTEGER; - break; - case DOUBLE_SPIN_BOX: - m_valueType = ValueType::DOUBLE; - break; - case INVALID: - break; - case MOUSE_USER_EVENT: - CaretAssert(0); - break; - case SPIN_BOX: - m_valueType = ValueType::INTEGER; - break; - case TOOL_BUTTON: - m_valueType = ValueType::BOOLEAN; - break; - } -} - -MacroEvent::MacroEvent(QMouseEvent* me, - const QString& widgetName, - const int32_t widgetWidth, - const int32_t widgetHeight) -: m_widgetType(WidgetWatcherType::MOUSE_USER_EVENT), -m_widgetName(widgetName) -{ - m_valueType = ValueType::MOUSE; - - m_mouseEventInfo = MacroEventMouseInfo(me, - widgetWidth, - widgetHeight); -} - -MacroEvent::MacroEvent() -{ - -} - -QString -MacroEvent::toString() const -{ - AString widgetTypeString("INVALID"); - switch (m_widgetType) { - case ACTION: - widgetTypeString = "ACTION"; - break; - case CHECK_BOX: - widgetTypeString = "CHECK_BOX"; - break; - case COMBO_BOX: - widgetTypeString = "COMBO_BOX"; - break; - case DOUBLE_SPIN_BOX: - widgetTypeString = "DOUBLE_SPIN_BOX"; - break; - case INVALID: - break; - case MOUSE_USER_EVENT: - widgetTypeString = "MOUSE"; - break; - case SPIN_BOX: - widgetTypeString = "SPIN_BOX"; - break; - case TOOL_BUTTON: - widgetTypeString = "TOOL_BUTTON"; - break; - } - - AString valueString; - switch (m_valueType) { - case ValueType::BOOLEAN: - valueString = AString::fromBool(m_boolValue); - break; - case ValueType::INTEGER: - valueString = AString::number(m_intValue); - break; - case ValueType::DOUBLE: - valueString = AString::number(m_doubleValue); - break; - case ValueType::MOUSE: - valueString = m_mouseEventInfo.toString(); - break; - } - - const QString sep(":"); - QString s(m_widgetName - + sep - + widgetTypeString - + sep - + valueString); - - return s; -} - -bool -MacroEvent::fromString(const QString& s) -{ - bool validFlag = false; - const QString sep(":"); - QStringList sl(s.split(sep)); - - if (sl.length() == 3) { - validFlag = true; - const QString widgetName(sl.at(0)); - const QString widgetTypeName(sl.at(1)); - const AString valueString(sl.at(2)); - - WidgetWatcherType widgetType = WidgetWatcherType::INVALID; - if (widgetTypeName == "ACTION") { - widgetType = WidgetWatcherType::ACTION; - } - else if (widgetTypeName == "CHECK_BOX") { - widgetType = WidgetWatcherType::CHECK_BOX; - } - else if (widgetTypeName == "COMBO_BOX") { - widgetType = WidgetWatcherType::COMBO_BOX; - } - else if (widgetTypeName == "DOUBLE_SPIN_BOX") { - widgetType = WidgetWatcherType::DOUBLE_SPIN_BOX; - } - else if (widgetTypeName == "MOUSE") { - widgetType = WidgetWatcherType::MOUSE_USER_EVENT; - } - else if (widgetTypeName == "SPIN_BOX") { - widgetType = WidgetWatcherType::SPIN_BOX; - } - else if (widgetTypeName == "TOOL_BUTTON") { - widgetType = WidgetWatcherType::TOOL_BUTTON; - } - else { - validFlag = false; - CaretLogSevere("Invalid widget type: " + widgetTypeName); - } - - if (validFlag) { - m_widgetName = widgetName; - m_widgetType = widgetType; - if (widgetType == WidgetWatcherType::MOUSE_USER_EVENT) { - validFlag = m_mouseEventInfo.fromString(valueString); - } - else { - m_boolValue = valueString.toBool(); - m_intValue = valueString.toInt(); - m_doubleValue = valueString.toDouble(); - } - } - } - else { - CaretLogSevere("Invalid string for reading macro " + s); - } - - return validFlag; -} - -/* ========================================================================= */ - -MacroManager::MacroManager() -{ - -} - -MacroManager::~MacroManager() -{ - eraseMacro(); -} - -MacroManager* -MacroManager::get() -{ - if (s_macroManager == NULL) { - s_macroManager = new MacroManager(); - } - - CaretAssert(s_macroManager); - return s_macroManager; -} - -void -MacroManager::addActionTriggeredToMacro(const QString& name, - const bool checkedStatus) -{ - MacroEvent* macroEvent = new MacroEvent(WidgetWatcherType::ACTION, - name); - macroEvent->m_boolValue = checkedStatus; - m_macroEvents.push_back(macroEvent); -} - - -void -MacroManager::addCheckBoxClickedToMacro(const QString& name, - const bool checkedStatus) -{ - MacroEvent* macroEvent = new MacroEvent(WidgetWatcherType::CHECK_BOX, - name); - macroEvent->m_boolValue = checkedStatus; - m_macroEvents.push_back(macroEvent); -} - -void -MacroManager::addComboBoxActivatedToMacro(const QString& name, - const int index) -{ - MacroEvent* macroEvent = new MacroEvent(WidgetWatcherType::COMBO_BOX, - name); - macroEvent->m_intValue = index; - m_macroEvents.push_back(macroEvent); -} - -void -MacroManager::addSpinBoxValueChangedToMacro(const QString& name, - const int value) -{ - MacroEvent* macroEvent = new MacroEvent(WidgetWatcherType::SPIN_BOX, - name); - macroEvent->m_intValue = value; - m_macroEvents.push_back(macroEvent); -} - -void -MacroManager::addDoubleSpinBoxValueChangedToMacro(const QString& name, - const double value) -{ - MacroEvent* macroEvent = new MacroEvent(WidgetWatcherType::DOUBLE_SPIN_BOX, - name); - macroEvent->m_doubleValue = value; - m_macroEvents.push_back(macroEvent); -} - -void -MacroManager::addToolButtonClickedToMacro(const QString& name, - const bool checkedStatus) -{ - MacroEvent* macroEvent = new MacroEvent(WidgetWatcherType::TOOL_BUTTON, - name); - macroEvent->m_boolValue = checkedStatus; - m_macroEvents.push_back(macroEvent); -} - -void -MacroManager::recordMouseEvent(QWidget* widget, - QMouseEvent* me) -{ - CaretAssert(widget); - - if (isModeRecording()) { - const QString name(widget->objectName()); - if (name.isEmpty()) { - CaretLogWarning("recordMouseEvent() widget name is empty."); - return; - } - - MacroEvent* macroEvent = new MacroEvent(me, - name, - widget->width(), - widget->height()); - m_macroEvents.push_back(macroEvent); - } -} - -void -MacroManager::moveMouse(QWidget* widget, - const bool highlightFlag) -{ - CaretAssert(widget); - - const QPoint widgetCenter = widget->rect().center(); - const QPoint windowPoint = widget->mapToGlobal(widgetCenter); - - if (highlightFlag) { - const float radius = 15.0; - for (float angle = 0.0; angle < 6.28; angle += 0.314) { - const float x = windowPoint.x() + (std::cos(angle) * radius); - const float y = windowPoint.y() + (std::sin(angle) * radius); - QCursor::setPos(x, y); - SystemUtilities::sleepSeconds(0.025); - } - } - - QCursor::setPos(windowPoint); -} - - -bool -MacroManager::runMacro(QObject* window, - QString& errorMessageOut) -{ - errorMessageOut.clear(); - - switch (m_mode) { - case MacroManager::Mode::OFF: - break; - case MacroManager::Mode::RECORDING: - errorMessageOut = "Recording is on, turn off recording, then run"; - return false; - break; - case MacroManager::Mode::RUNNING: - CaretAssert(0); - break; - } - - m_mode = Mode::RUNNING; - - for (auto me : m_macroEvents) { - CaretAssert(me); - - const QString widgetName(me->m_widgetName); - QWidget* widgetToMoveMouse = window->findChild(widgetName); - - if (widgetToMoveMouse == NULL) { - QObject* object = window->findChild(widgetName); - if (object != NULL) { - QObject* parent = object->parent(); - if (parent != NULL) { - widgetToMoveMouse = qobject_cast(parent); - } - } - } - - const bool mouseEventFlag = (me->m_widgetType == WidgetWatcherType::MOUSE_USER_EVENT); - if (widgetToMoveMouse != NULL) { - if ( ! mouseEventFlag) { - const bool highlightFlag = ( ! mouseEventFlag); - moveMouse(widgetToMoveMouse, - highlightFlag); - } - } - - switch (me->m_widgetType) { - case WidgetWatcherType::INVALID: - CaretAssert(0); - break; - case ACTION: - { - QAction* action = window->findChild(me->m_widgetName); - if (action != NULL) { - MacroActionEmitter actionUpdater(action); - actionUpdater.setActionChecked(me->m_boolValue); - } - else { - CaretLogWarning("ERROR: Unable to find action named " - + me->m_widgetName); - } - } - break; - case WidgetWatcherType::CHECK_BOX: - { - QCheckBox* checkBox = window->findChild(me->m_widgetName); - if (checkBox != NULL) { - checkBox->setChecked( ! me->m_boolValue); - checkBox->animateClick(100); // milliseconds - } - else { - CaretLogWarning("ERROR: Unable to find checkbox named " - + me->m_widgetName); - } - } - break; - case WidgetWatcherType::COMBO_BOX: - { - QComboBox* comboBox = window->findChild(me->m_widgetName); - if (comboBox != NULL) { - //comboBox->setCurrentIndex(me->m_intValue); - MacroComboBoxEmitter comboBoxUpdater(comboBox); - comboBoxUpdater.setComboBoxIndex(me->m_intValue); - } - else { - CaretLogWarning("ERROR: Unabe to find combo box named " - + me->m_widgetName); - } - } - break; - case WidgetWatcherType::DOUBLE_SPIN_BOX: - { - QList boxes = window->findChildren(me->m_widgetName); - QDoubleSpinBox* doubleSpinBox = window->findChild(me->m_widgetName); - if (doubleSpinBox != NULL) { - - MacroDoubleSpinBoxEmitter doubleSpinBoxUpdater(doubleSpinBox); - doubleSpinBoxUpdater.setDoubleSpinBoxValue(me->m_doubleValue); - } - else { - CaretLogWarning("ERROR: Unabe to find double spin box named " - + me->m_widgetName); - } - } - break; - case WidgetWatcherType::MOUSE_USER_EVENT: - { - QWidget* widget = window->findChild(me->m_widgetName); - if (widget != NULL) { - MacroEventMouseInfoWidgetInterface* mouseInterface = dynamic_cast(widget); - if (mouseInterface != NULL) { - const QSize currentWidgetSize = mouseInterface->getSizeOfWidget(); - const MacroEventMouseInfo& memi = me->m_mouseEventInfo; - const QPointF adjustedLocalPos = memi.getLocalPosRescaledToSize(currentWidgetSize); - - QMouseEvent mouseEvent(memi.m_type, - adjustedLocalPos, //memi.m_localPos, - memi.m_button, - memi.m_buttons, - memi.m_modifiers); - mouseInterface->processMouseEvent(&mouseEvent); - -// std::cout << "Original x/y: " << memi.m_localPos.x() -// << ", " << memi.m_localPos.y() << std::endl; -// std::cout << " Updated x/y: " << adjustedLocalPos.x() -// << ", " << adjustedLocalPos.y() << std::endl; - -// QEvent* event = &mouseEvent; -// widget->event(event); -// switch (memi.m_type) { -// case QEvent::MouseButtonDblClick: -// break; -// case QEvent::MouseButtonPress: -// break; -// case QEvent::MouseButtonRelease: -// break; -// case QEvent::MouseMove: -// break; -// default: -// CaretAssert(0); -// break; -// } - } - else { - CaretLogWarning("ERROR: widget named " - + me->m_widgetName - + " does not cast to MacroEventMouseInfoWidgetInterface"); - } - } - else { - CaretLogWarning("ERROR: Unabe to widget named " - + me->m_widgetName); - } - } - break; - case WidgetWatcherType::SPIN_BOX: - { - QSpinBox* spinBox = window->findChild(me->m_widgetName); - if (spinBox != NULL) { - /* - * Calling QSpinBox::setValue() results in emission of - * the QSpinBox::valueChanged() signal. - */ - spinBox->setValue(me->m_intValue); - } - else { - CaretLogWarning("ERROR: Unabe to find spin box named " - + me->m_widgetName); - } - } - break; - case WidgetWatcherType::TOOL_BUTTON: - { - QToolButton* toolButton = window->findChild(me->m_widgetName); - if (toolButton != NULL) { - toolButton->setChecked( ! me->m_boolValue); - toolButton->animateClick(1000); // 1000 milliseconds - } - else { - CaretLogWarning("ERROR: Unable to find toolbutton named " - + me->m_widgetName); - } - } - break; - } - - QGuiApplication::processEvents(); - if ( ! mouseEventFlag) { - SystemUtilities::sleepSeconds(1); - } - QGuiApplication::processEvents(); - } - - m_mode = Mode::OFF; - - return (errorMessageOut.isEmpty()); -} - -void -MacroManager::eraseMacro() -{ - for (auto me : m_macroEvents) { - CaretAssert(me); - delete me; - } - m_macroEvents.clear(); -} - - -MacroManager::Mode -MacroManager::getMode() const -{ - return m_mode; -} - -bool -MacroManager::isModeRecording() const -{ - switch (m_mode) { - case Mode::OFF: - break; - case Mode::RECORDING: - return true; - break; - case Mode::RUNNING: - break; - } - - return false; -} - -void -MacroManager::setMode(const Mode mode) -{ - m_mode = mode; -} - -void -MacroManager::saveMacroToFile(const AString& filename) -{ - QFile file(filename); - if (file.open(QFile::WriteOnly)) { - QTextStream stream(&file); - for (auto me : m_macroEvents) { - stream << me->toString() << "\n"; - } - file.close(); - } - else { - CaretLogSevere("Unable to open for writing: " - + filename); - } -} - -void -MacroManager::readMacroFromFile(const AString& filename) -{ - eraseMacro(); - - QFile file(filename); - if (file.open(QFile::ReadOnly)) { - QTextStream stream(&file); - - QString fileText(stream.readAll()); - QStringListIterator lineIter(fileText.split("\n")); - while (lineIter.hasNext()) { - const QString text = lineIter.next().trimmed(); - if ( ! text.isEmpty()) { - MacroEvent* me = new MacroEvent(); - const bool valid = me->fromString(text); - if (valid) { - m_macroEvents.push_back(me); - } - else { - CaretLogWarning("Invalid line in macro file: " - + text); - delete me; - } - } - } - file.close(); - } - else { - CaretLogSevere("Unable to open for writing: " - + filename); - } -} - -/* ========================================================================= */ - -MacroActionEmitter::MacroActionEmitter(QAction* action) -: m_action(action) -{ - CaretAssert(m_action); - /* - * Need to cause emission of activated signale - */ - QObject::connect(this, SIGNAL(actionCheckedSignal(bool)), - m_action, SIGNAL(triggered(bool))); -} - -void -MacroActionEmitter::setActionChecked(bool checked) -{ -// QSignalBlocker blocker(m_action); - m_action->setChecked(checked); - emit actionCheckedSignal(checked); -} - - -MacroComboBoxEmitter::MacroComboBoxEmitter(QComboBox* comboBox) -: m_comboBox(comboBox) -{ - CaretAssert(comboBox); - - /* - * Need to cause emission of activated signale - */ - QObject::connect(this, SIGNAL(valueChangedSignal(int)), - m_comboBox, SIGNAL(activated(int))); -} - -void -MacroComboBoxEmitter::setComboBoxIndex(const int index) -{ - m_comboBox->showPopup(); - m_comboBox->setCurrentIndex(index); - m_comboBox->hidePopup(); - emit valueChangedSignal(index); -} - - -//MacroSpinBoxEmitter::MacroSpinBoxEmitter(QSpinBox* spinBox) -//: m_spinBox(spinBox) -//{ -// CaretAssert(spinBox); -// -//// /* -//// * Need to cause emission of activated signale -//// */ -//// QObject::connect(this, SIGNAL(valueChangedSignal(int)), -//// m_spinBox, SIGNAL(valueChanged(int))); -//} -// -//void -//MacroSpinBoxEmitter::setSpinBoxValue(const int value) -//{ -// /* -// * With a spin box, calling setValue() results in the valueChanged() -// * signal being emitted -// */ -// m_spinBox->setValue(value); -// -//} - -MacroDoubleSpinBoxEmitter::MacroDoubleSpinBoxEmitter(QDoubleSpinBox* doubleSpinBox) -: m_doubleSpinBox(doubleSpinBox) -{ - CaretAssert(doubleSpinBox); - - QObject::connect(this, SIGNAL(valueChangedSignal(double)), - m_doubleSpinBox, SIGNAL(valueChanged(double))); -} - -void -MacroDoubleSpinBoxEmitter::setDoubleSpinBoxValue(const double value) -{ - QSignalBlocker blocker(m_doubleSpinBox); - m_doubleSpinBox->setValue(value); - blocker.unblock(); - - emit valueChangedSignal(value); -} - -/* ========================================================================= */ - -WidgetWatcher* -WidgetWatcher::unsupportedMessage(QObject* object, - const QString& signalName, - QString& errorMessageOut) -{ - errorMessageOut = ("Unsupported signal \"" - + signalName - + " for named \"" - + object->objectName() - + "\""); - - return NULL; -} - -WidgetWatcher* -WidgetWatcher::newInstance(QObject* object, - const QString& signalName, - QString& errorMessageOut) -{ - CaretAssert(object); - errorMessageOut.clear(); - - QAction* action = qobject_cast(object); - if (action != NULL) { - if (signalName.contains("triggered(bool)")) { - WidgetWatcher* ww = new WidgetWatcher(WidgetWatcherType::ACTION, - action); - return ww; - } - else { - return unsupportedMessage(object, - signalName, - errorMessageOut); - } - } - - QCheckBox* checkBox = qobject_cast(object); - if (checkBox != NULL) { - if (signalName.contains("clicked(bool)")) { - WidgetWatcher* ww = new WidgetWatcher(WidgetWatcherType::CHECK_BOX, - checkBox); - return ww; - } - else { - errorMessageOut = ("Unsupported signal \"" - + signalName - + " for named \"" - + object->objectName() - + "\""); - return NULL; - } - } - - QComboBox* comboBox = qobject_cast(object); - if (comboBox != NULL) { - if (signalName.contains("activated(int)")) { - WidgetWatcher* ww = new WidgetWatcher(WidgetWatcherType::COMBO_BOX, - comboBox); - return ww; - } - else { - errorMessageOut = ("Unsupported signal \"" - + signalName - + " for named \"" - + object->objectName() - + "\""); - return NULL; - } - } - - QSpinBox* spinBox = qobject_cast(object); - if (spinBox != NULL) { - if (signalName.contains("valueChanged(int)")) { - WidgetWatcher* ww = new WidgetWatcher(WidgetWatcherType::SPIN_BOX, - spinBox); - return ww; - } - else { - errorMessageOut = ("Unsupported signal \"" - + signalName - + " for named \"" - + object->objectName() - + "\""); - return NULL; - } - } - - QDoubleSpinBox* doubleSpinBox = qobject_cast(object); - if (doubleSpinBox != NULL) { - if (signalName.contains("valueChanged(double)")) { - WidgetWatcher* ww = new WidgetWatcher(WidgetWatcherType::DOUBLE_SPIN_BOX, - doubleSpinBox); - return ww; - } - else { - errorMessageOut = ("Unsupported signal \"" - + signalName - + " for named \"" - + object->objectName() - + "\""); - return NULL; - } - } - - QToolButton* toolButton = qobject_cast(object); - if (toolButton != NULL) { - if (signalName.contains("clicked(bool)")) { - WidgetWatcher* ww = new WidgetWatcher(WidgetWatcherType::TOOL_BUTTON, - toolButton); - return ww; - } - else { - errorMessageOut = ("Unsupported signal \"" - + signalName - + " for named \"" - + object->objectName() - + "\""); - return NULL; - } - } - - errorMessageOut = ("Widget named \"" - + object->objectName() - + "\" of class \"" - + object->metaObject()->className() - + "\" is not supported for macros"); - - return NULL; -} - - -WidgetWatcher::WidgetWatcher(const WidgetWatcherType widgetType, - QObject* object) -: m_widgetType(widgetType) -{ - CaretAssert(object); - m_name = object->objectName(); - - switch (m_widgetType) { - case INVALID: - break; - case ACTION: - { - QAction* action = qobject_cast(object); - CaretAssert(action); - QObject::connect(action, &QAction::triggered, - this, &WidgetWatcher::actionTriggered); - } - break; - case CHECK_BOX: - { - QCheckBox* checkBox = qobject_cast(object); - CaretAssert(checkBox); - QObject::connect(checkBox, &QCheckBox::clicked, - this, &WidgetWatcher::checkBoxClicked); - } - break; - case COMBO_BOX: - { - QComboBox* comboBox = qobject_cast(object); - CaretAssert(comboBox); - QObject::connect(comboBox, static_cast(&QComboBox::activated), - this, &WidgetWatcher::comboBoxActivated); - } - break; - case DOUBLE_SPIN_BOX: - { - QDoubleSpinBox* spinBox = qobject_cast(object); - CaretAssert(spinBox); - QObject::connect(spinBox, static_cast(&QDoubleSpinBox::valueChanged), - this, &WidgetWatcher::doubleSpinBoxValueChanged); - } - break; - case MOUSE_USER_EVENT: - break; - case SPIN_BOX: - { - QSpinBox* spinBox = qobject_cast(object); - CaretAssert(spinBox); - QObject::connect(spinBox, static_cast(&QSpinBox::valueChanged), - this, &WidgetWatcher::spinBoxValueChanged); - } - break; - case TOOL_BUTTON: - { - QToolButton* toolButton = qobject_cast(object); - CaretAssert(toolButton); - QObject::connect(toolButton, &QCheckBox::clicked, - this, &WidgetWatcher::toolButtonClicked); - } - break; - } -} - -void -WidgetWatcher::actionTriggered(bool checked) -{ - if (MacroManager::get()->isModeRecording()) { - MacroManager::get()->addActionTriggeredToMacro(m_name, - checked); - } -} - -void -WidgetWatcher::checkBoxClicked(bool checked) -{ - if (MacroManager::get()->isModeRecording()) { - MacroManager::get()->addCheckBoxClickedToMacro(m_name, - checked); - } -} - -void -WidgetWatcher::comboBoxActivated(int index) -{ - if (MacroManager::get()->isModeRecording()) { - MacroManager::get()->addComboBoxActivatedToMacro(m_name, - index); - } -} - -void -WidgetWatcher::spinBoxValueChanged(int value) -{ - if (MacroManager::get()->isModeRecording()) { - MacroManager::get()->addSpinBoxValueChangedToMacro(m_name, - value); - } -} - -void -WidgetWatcher::doubleSpinBoxValueChanged(double value) -{ - if (MacroManager::get()->isModeRecording()) { - MacroManager::get()->addDoubleSpinBoxValueChangedToMacro(m_name, - value); - } -} - -void -WidgetWatcher::toolButtonClicked(bool checked) -{ - if (MacroManager::get()->isModeRecording()) { - MacroManager::get()->addToolButtonClickedToMacro(m_name, - checked); - } -} - - diff --git a/src/GuiQt/MacroPrototype.h b/src/GuiQt/MacroPrototype.h deleted file mode 100644 index f31f481a0..000000000 --- a/src/GuiQt/MacroPrototype.h +++ /dev/null @@ -1,373 +0,0 @@ -#ifndef __WU_Q_OBJECT_H__ -#define __WU_Q_OBJECT_H__ - -/*LICENSE_START*/ -/* - * Copyright (C) 2018 Washington University School of Medicine - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ -/*LICENSE_END*/ - - -#include -#include - -#include -#include -#include - -#include "AString.h" - -class QDoubleSpinBox; -class QComboBox; -class QMainWindow; -class QSpinBox; - -namespace caret { - - class WidgetWatcher; - - enum WidgetWatcherType { - INVALID, - ACTION, - CHECK_BOX, - COMBO_BOX, - DOUBLE_SPIN_BOX, - MOUSE_USER_EVENT, - SPIN_BOX, - TOOL_BUTTON, - }; - - /* ========================================================================= */ - - class WuQObject : public QObject { - - Q_OBJECT - - public: - WuQObject(const WuQObject&) = delete; - - WuQObject& operator=(const WuQObject&) = delete; - - static void watchObjectForMacroRecording(QObject* sender); - - static QMetaObject::Connection connect(QObject *sender, - const char *signal, - const QObject *receiver, - const char *method, - Qt::ConnectionType type = Qt::AutoConnection); - -// typedef void (QObject::*QObjectBoolPointerToMemberFunction)(bool); -// -// static QMetaObject::Connection connectFP(QObject *sender, -// QObjectBoolPointerToMemberFunction signal, -// QObject* receiver, -// QObjectBoolPointerToMemberFunction method, -// Qt::ConnectionType type = Qt::AutoConnection); - - // ADD_NEW_METHODS_HERE - - private: - WuQObject(); - - virtual ~WuQObject(); - - static void watchObjectForMacroRecordingPrivate(QObject* sender, - const char* signal); - -// static WidgetWatcher* createWidgetWatcher(QObject* object); - - // ADD_NEW_MEMBERS_HERE - - static std::map s_widgetWatchers; - }; - - /* ========================================================================= */ - - class MacroMenu : public QMenu { - Q_OBJECT - - public: - MacroMenu(QMainWindow* mainWindowParent); - - private slots: - void macroMenuAboutToShow(); - void processMacroRecord(); - void processMacroRun(); - void processMacroErase(); - void processMacroLoad(); - void processMacroSave(); - - private: - QMainWindow* m_mainWindowParent; - QAction* m_macroEraseAction; - QAction* m_macroRecordAction; - QAction* m_macroRunAction; - QAction* m_macroLoadAction; - QAction* m_macroSaveAction; - }; - - /* ========================================================================= */ - - class MacroEventMouseInfoWidgetInterface { - public: - MacroEventMouseInfoWidgetInterface() { } - - virtual QSize getSizeOfWidget() const = 0; - - virtual void processMouseEvent(QMouseEvent*) = 0; - }; - - /* ========================================================================= */ - - class MacroEventMouseInfo { - public: - MacroEventMouseInfo(); - - MacroEventMouseInfo(QMouseEvent* me, - const int32_t widgetWidth, - const int32_t widgetHeight); - - QPointF getLocalPosRescaledToSize(const QSize& widgetSize) const; - - QString toString() const; - - bool fromString(const QString& s); - - QEvent::Type m_type; - QPointF m_localPos; - Qt::MouseButton m_button; - Qt::MouseButtons m_buttons; - Qt::KeyboardModifiers m_modifiers; - int32_t m_widgetWidth; - int32_t m_widgetHeight; - }; - - /* ========================================================================= */ - - class MacroEvent { - public: - enum class ValueType { - BOOLEAN, - DOUBLE, - INTEGER, - MOUSE - }; - - MacroEvent(const WidgetWatcherType widgetType, - const QString& widgetName); - - MacroEvent(QMouseEvent* me, - const QString& widgetName, - const int32_t widgetWidth, - const int32_t widgetHeight); - - MacroEvent(); - - QString toString() const; - - bool fromString(const QString& s); - - WidgetWatcherType m_widgetType = WidgetWatcherType::INVALID; - QString m_widgetName; - ValueType m_valueType = ValueType::INTEGER; - - bool m_boolValue = false; - int m_intValue = 0; - double m_doubleValue = 0.0; - - MacroEventMouseInfo m_mouseEventInfo; - }; - - /* ========================================================================= */ - - class MacroActionEmitter : public QObject { - Q_OBJECT - - public: - MacroActionEmitter(QAction* action); - - void setActionChecked(bool checked); - - signals: - void actionCheckedSignal(bool); - - private: - QAction* m_action; - - }; - - /* ========================================================================= */ - - class MacroComboBoxEmitter : public QObject { - Q_OBJECT - - public: - MacroComboBoxEmitter(QComboBox* comboBox); - - void setComboBoxIndex(const int index); - - signals: - void valueChangedSignal(int); - - private: - QComboBox* m_comboBox; - - }; - - /* ========================================================================= */ - -// class MacroSpinBoxEmitter : public QObject { -// Q_OBJECT -// -// public: -// MacroSpinBoxEmitter(QSpinBox* spinBox); -// -// void setSpinBoxValue(const int value); -// -//// signals: -//// int valueChangedSignal(int); -// -// private: -// QSpinBox* m_spinBox; -// -// }; - - /* ========================================================================= */ - - class MacroDoubleSpinBoxEmitter : public QObject { - Q_OBJECT - - public: - MacroDoubleSpinBoxEmitter(QDoubleSpinBox* doubleSpinBox); - - void setDoubleSpinBoxValue(const double value); - - signals: - void valueChangedSignal(double); - - private: - QDoubleSpinBox* m_doubleSpinBox; - - }; - - /* ========================================================================= */ - - class MacroManager { - public: - enum class Mode { - OFF, - RECORDING, - RUNNING - }; - - static MacroManager* get(); - - MacroManager(); - - ~MacroManager(); - - void addActionTriggeredToMacro(const QString& name, - const bool checkedStatus); - - void addCheckBoxClickedToMacro(const QString& name, - const bool checkedStatus); - - void addComboBoxActivatedToMacro(const QString& name, - const int index); - - void addSpinBoxValueChangedToMacro(const QString& name, - const int value); - - void addDoubleSpinBoxValueChangedToMacro(const QString& name, - const double value); - - void addToolButtonClickedToMacro(const QString& name, - const bool checkedStatus); - - void recordMouseEvent(QWidget* widget, - QMouseEvent* me); - - bool isModeRecording() const; - - bool runMacro(QObject* window, - QString& errorMessageOut); - - Mode getMode() const; - - void eraseMacro(); - - void setMode(const Mode mode); - - void moveMouse(QWidget* widget, - const bool highlightFlag); - - void saveMacroToFile(const AString& filename); - - void readMacroFromFile(const AString& filename); - - std::vector m_macroEvents; - - static MacroManager* s_macroManager; - - Mode m_mode = Mode::OFF; - }; - - /* ========================================================================= */ - - class WidgetWatcher : public QObject { - - Q_OBJECT - - public: - static WidgetWatcher* newInstance(QObject* object, - const QString& signalName, - QString& errorMessageOut); - - WidgetWatcher(const WidgetWatcherType widgetType, - QObject* object); - - const WidgetWatcherType m_widgetType; - QString m_name; -// QObject* m_widget = NULL; - - public slots: - void actionTriggered(bool); - - void checkBoxClicked(bool); - - void comboBoxActivated(int); - - void spinBoxValueChanged(int); - - void doubleSpinBoxValueChanged(double); - - void toolButtonClicked(bool); - - private: - static WidgetWatcher* unsupportedMessage(QObject* object, - const QString& signalName, - QString& errorMessageOut); - - }; - -#ifdef __WU_Q_OBJECT_DECLARE__ - MacroManager* MacroManager::s_macroManager = NULL; - std::map WuQObject::s_widgetWatchers; -#endif // __WU_Q_OBJECT_DECLARE__ - -} // namespace -#endif //__WU_Q_OBJECT_H__ diff --git a/src/GuiQt/OverlayViewController.cxx b/src/GuiQt/OverlayViewController.cxx index 007f350c3..959bfa4af 100644 --- a/src/GuiQt/OverlayViewController.cxx +++ b/src/GuiQt/OverlayViewController.cxx @@ -115,7 +115,7 @@ OverlayViewController::OverlayViewController(const Qt::Orientation orientation, const QString checkboxText = ((orientation == Qt::Horizontal) ? " " : " "); this->enabledCheckBox = new QCheckBox(checkboxText); this->enabledCheckBox->setObjectName(objectNamePrefix - + "OnOff:CheckBox"); + + "OnOff"); QObject::connect(this->enabledCheckBox, SIGNAL(clicked(bool)), this, SLOT(enabledCheckBoxClicked(bool))); this->enabledCheckBox->setToolTip("Enables display of this overlay"); @@ -126,7 +126,7 @@ OverlayViewController::OverlayViewController(const Qt::Orientation orientation, */ this->fileComboBox = WuQFactory::newComboBox(); this->fileComboBox->setObjectName(objectNamePrefix - + "File_Selection:ComboBox"); + + "File_Selection"); this->fileComboBox->setMinimumWidth(minComboBoxWidth); this->fileComboBox->setMaximumWidth(maxComboBoxWidth); QObject::connect(this->fileComboBox, SIGNAL(activated(int)), @@ -140,7 +140,7 @@ OverlayViewController::OverlayViewController(const Qt::Orientation orientation, */ m_mapIndexSpinBox = WuQFactory::newSpinBox(); this->m_mapIndexSpinBox->setObjectName(objectNamePrefix - + "Map_Index:SpinBox"); + + "Map_Index"); QObject::connect(m_mapIndexSpinBox, SIGNAL(valueChanged(int)), this, SLOT(mapIndexSpinBoxValueChanged(int))); m_mapIndexSpinBox->setToolTip("Select map by its index"); @@ -151,7 +151,7 @@ OverlayViewController::OverlayViewController(const Qt::Orientation orientation, */ this->mapNameComboBox = WuQFactory::newComboBox(); this->mapNameComboBox->setObjectName(objectNamePrefix - + "Map_Selection:ComboBox"); + + "Map_Selection"); this->mapNameComboBox->setMinimumWidth(minComboBoxWidth); this->mapNameComboBox->setMaximumWidth(maxComboBoxWidth); QObject::connect(this->mapNameComboBox, SIGNAL(activated(int)), @@ -165,7 +165,7 @@ OverlayViewController::OverlayViewController(const Qt::Orientation orientation, */ this->opacityDoubleSpinBox = WuQFactory::newDoubleSpinBox(); this->opacityDoubleSpinBox->setObjectName(objectNamePrefix - + "Opacity:SpinBox"); + + "Opacity"); this->opacityDoubleSpinBox->setMinimum(0.0); this->opacityDoubleSpinBox->setMaximum(1.0); this->opacityDoubleSpinBox->setSingleStep(0.10); @@ -182,11 +182,11 @@ OverlayViewController::OverlayViewController(const Qt::Orientation orientation, m_colorBarToolButton = WuQtUtilities::createToolButtonWithIcon("CB", ":/LayersPanel/colorbar.png", "Display color bar for this overlay", - (objectNamePrefix - + "Show_Color_Bar:ToolButton"), this, SLOT(colorBarActionTriggered(bool))); m_colorBarToolButton->setCheckable(true); + m_colorBarToolButton->setObjectName(objectNamePrefix + + "Show_Color_Bar"); macroManager->addMacroSupportToObject(m_colorBarToolButton); // /* @@ -220,7 +220,7 @@ OverlayViewController::OverlayViewController(const Qt::Orientation orientation, this, SLOT(settingsActionTriggered())); this->settingsAction->setObjectName(objectNamePrefix - + "ShowSettingsDialog:ToolButton"); + + "ShowSettingsDialog"); if (settingsIconValid) { this->settingsAction->setIcon(settingsIcon); } @@ -264,7 +264,7 @@ OverlayViewController::OverlayViewController(const Qt::Orientation orientation, */ m_mapYokingGroupComboBox = new MapYokingGroupComboBox(this, (objectNamePrefix - + "Map_Yoking_Selection:ComboBox")); + + "Map_Yoking_Selection")); m_mapYokingGroupComboBox->getWidget()->setStatusTip("Synchronize enabled status and map indices)"); m_mapYokingGroupComboBox->getWidget()->setToolTip("Yoke to Overlay Mapped Files"); #ifdef CARET_OS_MACOSX @@ -874,7 +874,7 @@ OverlayViewController::createConstructionMenu(QWidget* parent, this, SLOT(menuAddOverlayAboveTriggered())); addAboveAction->setObjectName(menuActionNamePrefix - + "Add_Overlay_Above:MenuAction"); + + "Add_Overlay_Above"); addAboveAction->setToolTip("Add an overlay above this overlay"); macroManager->addMacroSupportToObject(addAboveAction); @@ -882,7 +882,7 @@ OverlayViewController::createConstructionMenu(QWidget* parent, this, SLOT(menuAddOverlayBelowTriggered())); addBelowAction->setObjectName(menuActionNamePrefix - + "Add_Overlay_Below:MenuAction"); + + "Add_Overlay_Below"); addBelowAction->setToolTip("Add an overlay below this overlay"); macroManager->addMacroSupportToObject(addBelowAction); @@ -892,7 +892,7 @@ OverlayViewController::createConstructionMenu(QWidget* parent, this, SLOT(menuMoveOverlayUpTriggered())); moveUpAction->setObjectName(menuActionNamePrefix - + "Move_Overlay_Up:MenuAction"); + + "Move_Overlay_Up"); moveUpAction->setToolTip("Move this overlay up"); macroManager->addMacroSupportToObject(moveUpAction); @@ -900,7 +900,7 @@ OverlayViewController::createConstructionMenu(QWidget* parent, this, SLOT(menuMoveOverlayDownTriggered())); moveDownAction->setObjectName(menuActionNamePrefix - + "Move_Overlay_Down:MenuAction"); + + "Move_Overlay_Down"); moveDownAction->setToolTip("Move this overlay down"); macroManager->addMacroSupportToObject(moveDownAction); @@ -910,7 +910,7 @@ OverlayViewController::createConstructionMenu(QWidget* parent, this, SLOT(menuRemoveOverlayTriggered())); removeAction->setObjectName(menuActionNamePrefix - + "Remove_Overlay:MenuAction"); + + "Remove_Overlay"); removeAction->setToolTip("Remove this overlay"); macroManager->addMacroSupportToObject(removeAction); @@ -920,7 +920,7 @@ OverlayViewController::createConstructionMenu(QWidget* parent, this, SLOT(menuReloadFileTriggered())); m_constructionReloadFileAction->setObjectName(menuActionNamePrefix - + "Reload_Selected_File:MenuAction"); + + "Reload_Selected_File"); m_constructionReloadFileAction->setToolTip("Reload file in this overlay"); macroManager->addMacroSupportToObject(m_constructionReloadFileAction); @@ -938,7 +938,7 @@ OverlayViewController::createConstructionMenu(QWidget* parent, this, SLOT(menuCopyMapNameToClipBoard())); copyMapNameAction->setObjectName(menuActionNamePrefix - + "Copy_Map_Name_To_Clipboard:MenuAction"); + + "Copy_Map_Name_To_Clipboard"); copyMapNameAction->setToolTip("Copy name of selected map to the clipboard"); macroManager->addMacroSupportToObject(copyMapNameAction); diff --git a/src/GuiQt/WuQMacroExecutor.cxx b/src/GuiQt/WuQMacroExecutor.cxx index 658e57b1e..b890d86d1 100644 --- a/src/GuiQt/WuQMacroExecutor.cxx +++ b/src/GuiQt/WuQMacroExecutor.cxx @@ -34,6 +34,7 @@ #include #include #include +#include #include #include #include @@ -45,6 +46,8 @@ #include "CaretAssert.h" #include "WuQMacro.h" #include "WuQMacroCommand.h" +#include "WuQMacroMouseEventInfo.h" +#include "WuQMacroMouseEventWidgetInterface.h" #include "WuQMacroSignalEmitter.h" using namespace caret; @@ -317,7 +320,60 @@ WuQMacroExecutor::runMacroCommand(const WuQMacroCommand* macroCommand, } break; case WuQMacroObjectTypeEnum::MOUSE_USER_EVENT: - CaretAssertToDoFatal(); + { + QWidget* widget = qobject_cast(object); + if (widget != NULL) { + WuQMacroMouseEventWidgetInterface* mouseInterface = dynamic_cast(widget); + if (mouseInterface != NULL) { + const QSize currentWidgetSize = widget->size(); + const WuQMacroMouseEventInfo* mouseEventInfo = macroCommand->getMouseEventInfo(); + CaretAssert(mouseEventInfo); + + int32_t adjustedLocalX(0); + int32_t adjustedLocalY(0); + mouseEventInfo->getLocalPositionRescaledToWidgetSize(currentWidgetSize.width(), + currentWidgetSize.height(), + adjustedLocalX, + adjustedLocalY); + + QEvent::Type qtEventType = QEvent::None; + switch (mouseEventInfo->getMouseEventType()) { + case WuQMacroMouseEventTypeEnum::BUTTON_PRESS: + qtEventType = QEvent::MouseButtonPress; + break; + case WuQMacroMouseEventTypeEnum::BUTTON_RELEASE: + qtEventType = QEvent::MouseButtonRelease; + break; + case WuQMacroMouseEventTypeEnum::DOUBLE_CLICK: + qtEventType = QEvent::MouseButtonDblClick; + break; + case WuQMacroMouseEventTypeEnum::MOVE: + qtEventType = QEvent::MouseMove; + break; + } + + QMouseEvent qtMouseEvent(qtEventType, + QPointF(adjustedLocalX, + adjustedLocalY), + static_cast(mouseEventInfo->getMouseButton()), + static_cast(mouseEventInfo->getMouseButtonsMask()), + static_cast(mouseEventInfo->getKeyboardModifiersMask())); + mouseInterface->processMouseEventFromMacro(&qtMouseEvent); + } + else { + errorMessageOut = ("ERROR: Unable to cast object named " + + object->objectName() + + " to WuQMacroMouseEventWidgetInterface"); + return false; + } + } + else { + errorMessageOut = ("ERROR: Unable to cast object named " + + object->objectName() + + " to QWidget"); + return false; + } + } break; case WuQMacroObjectTypeEnum::PUSH_BUTTON: { diff --git a/src/GuiQt/WuQMacroManager.cxx b/src/GuiQt/WuQMacroManager.cxx index df0cb46b0..c832492f0 100644 --- a/src/GuiQt/WuQMacroManager.cxx +++ b/src/GuiQt/WuQMacroManager.cxx @@ -23,9 +23,12 @@ #include "WuQMacroManager.h" #undef __WU_Q_MACRO_MANAGER_DECLARE__ +#include + #include #include #include +#include #include #include "CaretAssert.h" @@ -33,9 +36,11 @@ #include "CaretPreferences.h" #include "SessionManager.h" #include "WuQMacro.h" +#include "WuQMacroCommand.h" #include "WuQMacroCreateDialog.h" #include "WuQMacroDialog.h" #include "WuQMacroExecutor.h" +#include "WuQMacroMouseEventInfo.h" #include "WuQMacroSignalWatcher.h" using namespace caret; @@ -189,7 +194,8 @@ WuQMacroManager::addMacroSupportToObject(QObject* object) const QString name = object->objectName(); if (name.isEmpty()) { - CaretLogSevere("Object name is empty, will be ignored for macros"); + CaretLogSevere("Object name is empty, will be ignored for macros\n" + + SystemUtilities::getBackTrace()); return; } @@ -213,7 +219,9 @@ WuQMacroManager::addMacroSupportToObject(QObject* object) if (existingWatcher != m_signalWatchers.end()) { CaretLogSevere("Object named \"" + name - + "\" has already been connected for macros"); + + "\" has already been connected for macros\n" + + SystemUtilities::getBackTrace() + + "\n"); } else { AString errorMessage; @@ -257,6 +265,75 @@ WuQMacroManager::addMacroCommandToRecording(WuQMacroCommand* macroCommand) return false; } +/** + * Adds a mouse event to the macro that is currently being + * recorded. If no macro is being recorded, no action is taken. + * + * @param widget + * Widget where mouse event occurred + * @param me + * The Qt Mouse Event + * @return + * True if the mouse event was recorded or false if there is an error. + */ +bool +WuQMacroManager::addMouseEventToRecording(QWidget* widget, + const QMouseEvent* me) +{ + CaretAssert(widget); + CaretAssert(me); + + if (isModeRecording()) { + const QString name(widget->objectName()); + if (name.isEmpty()) { + CaretLogSevere("Widget name is empty for recording of mouse event\n" + + SystemUtilities::getBackTrace()); + return false; + } + CaretAssert(m_macroBeingRecorded); + + WuQMacroMouseEventTypeEnum::Enum mouseEventType = WuQMacroMouseEventTypeEnum::MOVE; + switch (me->type()) { + case QEvent::MouseButtonPress: + mouseEventType = WuQMacroMouseEventTypeEnum::BUTTON_PRESS; + break; + case QEvent::MouseButtonRelease: + mouseEventType = WuQMacroMouseEventTypeEnum::BUTTON_RELEASE; + break; + case QEvent::MouseButtonDblClick: + mouseEventType = WuQMacroMouseEventTypeEnum::DOUBLE_CLICK; + break; + case QEvent::MouseMove: + mouseEventType = WuQMacroMouseEventTypeEnum::MOVE; + break; + default: + CaretAssertMessage(0, ("Unknown mouse event type integer cast=" + + QString::number(static_cast(me->type())))); + break; + } + WuQMacroMouseEventInfo* mouseInfo = new WuQMacroMouseEventInfo(mouseEventType, + me->localPos().x(), + me->localPos().y(), + me->windowPos().x(), + me->windowPos().y(), + me->screenPos().x(), + me->screenPos().y(), + static_cast(me->button()), + static_cast(me->buttons()), + static_cast(me->modifiers()), + widget->width(), + widget->height()); + + m_macroBeingRecorded->addMacroCommand(new WuQMacroCommand(name, + widget->toolTip(), + mouseInfo)); + return true; + } + + return false; +} + + /** * @return Vector containing all Macro Groups. */ @@ -381,6 +458,35 @@ WuQMacroManager::runMacro(QWidget* widget, } } +/** + * Print supported widgets to the terminal window. + */ +void +WuQMacroManager::printSupportedWidgetsToTerminal() +{ + std::set allList; + std::set duplicateList; + for (auto iter : m_signalWatchers) { + const QString s(iter.second->toString()); + + const auto existIter = allList.find(s); + if (existIter != allList.end()) { + duplicateList.insert(s); + } + else { + allList.insert(s); + } + } + + for (auto& iter : allList) { + std::cout << iter << std::endl; + } + + for (auto& iter : duplicateList) { + std::cout << "DUPLICTE: " << iter << std::endl; + } +} + diff --git a/src/GuiQt/WuQMacroManager.h b/src/GuiQt/WuQMacroManager.h index c103f5743..e0d310d4d 100644 --- a/src/GuiQt/WuQMacroManager.h +++ b/src/GuiQt/WuQMacroManager.h @@ -29,6 +29,7 @@ #include "WuQMacroModeEnum.h" +class QMouseEvent; class QWidget; namespace caret { @@ -59,6 +60,9 @@ namespace caret { bool addMacroCommandToRecording(WuQMacroCommand* macroCommand); + bool addMouseEventToRecording(QWidget* widget, + const QMouseEvent* me); + WuQMacroModeEnum::Enum getMode() const; void setMode(const WuQMacroModeEnum::Enum mode); @@ -80,6 +84,8 @@ namespace caret { void runMacro(QWidget* window, const WuQMacro* macro); + void printSupportedWidgetsToTerminal(); + // ADD_NEW_METHODS_HERE private: diff --git a/src/GuiQt/WuQMacroMenu.cxx b/src/GuiQt/WuQMacroMenu.cxx index 0292307de..639492a83 100644 --- a/src/GuiQt/WuQMacroMenu.cxx +++ b/src/GuiQt/WuQMacroMenu.cxx @@ -51,7 +51,7 @@ m_windowParent(windowParent) { CaretAssert(windowParent); - setTitle("Macro New"); + setTitle("Macro"); m_macroDialogAction = addAction("Macros..."); QObject::connect(m_macroDialogAction, &QAction::triggered, @@ -61,6 +61,11 @@ m_windowParent(windowParent) QObject::connect(m_recordMacroAction, &QAction::triggered, this, &WuQMacroMenu::macroRecordSelected); + addSeparator(); + QAction* printAction = addAction("Print Supported Widgets"); + QObject::connect(printAction, &QAction::triggered, + this, &WuQMacroMenu::macroPrintAllSelected); + QObject::connect(this, &QMenu::aboutToShow, this, &WuQMacroMenu::macroMenuAboutToShow); } @@ -135,3 +140,14 @@ WuQMacroMenu::macroDialogSelected() { WuQMacroManager::instance()->showMacrosDialog(m_windowParent); } + +/** + * Print all watched widgets to terminal + */ +void +WuQMacroMenu::macroPrintAllSelected() +{ + WuQMacroManager::instance()->printSupportedWidgetsToTerminal(); +} + + diff --git a/src/GuiQt/WuQMacroMenu.h b/src/GuiQt/WuQMacroMenu.h index fd83dba37..cee0acd1c 100644 --- a/src/GuiQt/WuQMacroMenu.h +++ b/src/GuiQt/WuQMacroMenu.h @@ -52,6 +52,8 @@ namespace caret { void macroDialogSelected(); + void macroPrintAllSelected(); + private: // ADD_NEW_MEMBERS_HERE diff --git a/src/GuiQt/WuQMacroMouseEventWidgetInterface.h b/src/GuiQt/WuQMacroMouseEventWidgetInterface.h new file mode 100644 index 000000000..4df88fb1b --- /dev/null +++ b/src/GuiQt/WuQMacroMouseEventWidgetInterface.h @@ -0,0 +1,51 @@ +#ifndef __WU_Q_MACRO_MOUSE_EVENT_WIDGET_INTERFACE_H__ +#define __WU_Q_MACRO_MOUSE_EVENT_WIDGET_INTERFACE_H__ + +/*LICENSE_START*/ +/* + * Copyright (C) 2018 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + +class QMouseEvent; + +namespace caret { + + class WuQMacroMouseEventWidgetInterface { + + public: + WuQMacroMouseEventWidgetInterface() { } + + virtual ~WuQMacroMouseEventWidgetInterface() { } + + WuQMacroMouseEventWidgetInterface(const WuQMacroMouseEventWidgetInterface&) = delete; + + WuQMacroMouseEventWidgetInterface& operator=(const WuQMacroMouseEventWidgetInterface&) = delete; + + virtual void processMouseEventFromMacro(QMouseEvent* me) = 0; + + private: + // ADD_NEW_MEMBERS_HERE + + }; + +#ifdef __WU_Q_MACRO_MOUSE_EVENT_WIDGET_INTERFACE_DECLARE__ + // +#endif // __WU_Q_MACRO_MOUSE_EVENT_WIDGET_INTERFACE_DECLARE__ + +} // namespace +#endif //__WU_Q_MACRO_MOUSE_EVENT_WIDGET_INTERFACE_H__ diff --git a/src/GuiQt/WuQMacroSignalWatcher.cxx b/src/GuiQt/WuQMacroSignalWatcher.cxx index 64d629ede..9c0a5bf18 100644 --- a/src/GuiQt/WuQMacroSignalWatcher.cxx +++ b/src/GuiQt/WuQMacroSignalWatcher.cxx @@ -492,3 +492,16 @@ WuQMacroSignalWatcher::toolButtonClicked(bool checked) createAndSendMacroCommand(checked); } +/** + * @return String containing description of this signal watcher + */ +QString +WuQMacroSignalWatcher::toString() const +{ + QString s(m_objectName + + " type=" + + WuQMacroObjectTypeEnum::toGuiName(m_objectType)); + return s; +} + + diff --git a/src/GuiQt/WuQMacroSignalWatcher.h b/src/GuiQt/WuQMacroSignalWatcher.h index 9eceade33..6a370c8a1 100644 --- a/src/GuiQt/WuQMacroSignalWatcher.h +++ b/src/GuiQt/WuQMacroSignalWatcher.h @@ -51,6 +51,8 @@ namespace caret { WuQMacroSignalWatcher& operator=(const WuQMacroSignalWatcher&) = delete; + QString toString() const; + private slots: void actionTriggered(bool); @@ -79,7 +81,6 @@ namespace caret { void tabWidgetCurrentChanged(int); void toolButtonClicked(bool); - // ADD_NEW_METHODS_HERE diff --git a/src/GuiQt/WuQtUtilities.cxx b/src/GuiQt/WuQtUtilities.cxx index 56a013c18..fb64061f8 100644 --- a/src/GuiQt/WuQtUtilities.cxx +++ b/src/GuiQt/WuQtUtilities.cxx @@ -168,12 +168,6 @@ WuQtUtilities::createAction(const QString& text, QAction* action = WuQtUtilities::createAction(text, toolAndStatusTipText, parent); -// QAction* action = new QAction(parent); -// action->setText(text); -// if (toolAndStatusTipText.isEmpty() == false) { -// action->setStatusTip(toolAndStatusTipText); -// action->setToolTip(toolAndStatusTipText); -// } QObject::connect(action, SIGNAL(triggered(bool)), receiver, @@ -215,41 +209,27 @@ WuQtUtilities::createPushButton(const QString& text, return pb; } -//QToolButton* -//WuQtUtilities::createToolButtonWithIcon(const QString& text, -// const QString& iconFileName, -// const QString& toolTip, -// const QString& widgetName, -// QObject* receiver, -// WuQObject::QObjectBoolPointerToMemberFunction method) -//{ -// QIcon icon; -// const bool iconValid = WuQtUtilities::loadIcon(iconFileName, -// icon); -// -// QToolButton* toolButton = new QToolButton(); -// if (iconValid) { -// toolButton->setIcon(icon); -// } -// else { -// toolButton->setText(text); -// } -// toolButton->setToolTip(toolTip); -// toolButton->setObjectName(widgetName); -// -// WuQObject::connectFP(toolButton, -// static_cast(&QToolButton::clicked), -// receiver, -// method); -// -// return toolButton; -//} - +/** + * Create a tool button with the specified text, icon, + * tooltip and slot. + * + * @param text + * Text for the action. + * @param iconFileName + * Name of file containing the icon. + * @param tooltip + * Tooltip for the button + * @param receiver + * Owner of method that is called when button is clicked. + * @param method + * method in receiver that is called when button is clicked. + * @return + * Toolbutton that was created. + */ QToolButton* WuQtUtilities::createToolButtonWithIcon(const QString& text, const QString& iconFileName, const QString& toolTip, - const QString& widgetName, QObject* receiver, const char* method) { @@ -265,9 +245,8 @@ WuQtUtilities::createToolButtonWithIcon(const QString& text, toolButton->setText(text); } toolButton->setToolTip(toolTip); - toolButton->setObjectName(widgetName); - WuQObject::connect(toolButton, SIGNAL(clicked(bool)), + QObject::connect(toolButton, SIGNAL(clicked(bool)), receiver, method); return toolButton; diff --git a/src/GuiQt/WuQtUtilities.h b/src/GuiQt/WuQtUtilities.h index d3fff8e21..13cdeb7a9 100644 --- a/src/GuiQt/WuQtUtilities.h +++ b/src/GuiQt/WuQtUtilities.h @@ -30,7 +30,6 @@ #include #include "CaretColorEnum.h" -#include "MacroPrototype.h" class QAction; class QBoxLayout; @@ -69,15 +68,8 @@ namespace caret { static QToolButton* createToolButtonWithIcon(const QString& text, const QString& iconFileName, const QString& toolTip, - const QString& widgetName, QObject* receiver, const char* method); -// static QToolButton* createToolButtonWithIcon(const QString& text, -// const QString& iconFileName, -// const QString& toolTip, -// const QString& widgetName, -// QObject* receiver, -// WuQObject::QObjectBoolPointerToMemberFunction method); static QAction* createAction(const QString& text, const QString& toolAndStatusTipText, -- GitLab From 93efdb37cea13345347fde12051ef1062e2f66e0 Mon Sep 17 00:00:00 2001 From: John Harwell Date: Thu, 6 Dec 2018 09:53:03 -0600 Subject: [PATCH 126/427] WB-834: Continuing macro system development including dialogs, execution, and read/write to preferences. --- src/Common/CMakeLists.txt | 8 + src/Common/CaretPreferences.cxx | 62 ++- src/Common/CaretPreferences.h | 6 + src/Common/WuQMacro.cxx | 220 +++++------ src/Common/WuQMacro.h | 29 +- src/Common/WuQMacroCommand.cxx | 345 ++++++----------- src/Common/WuQMacroCommand.h | 25 +- src/Common/WuQMacroDataValueTypeEnum.cxx | 387 +++++++++++++++++++ src/Common/WuQMacroDataValueTypeEnum.h | 110 ++++++ src/Common/WuQMacroGroup.cxx | 176 ++++++++- src/Common/WuQMacroGroup.h | 29 +- src/Common/WuQMacroGroupXmlFormatBase.cxx | 62 +++ src/Common/WuQMacroGroupXmlFormatBase.h | 122 ++++++ src/Common/WuQMacroGroupXmlReader.cxx | 441 ++++++++++++++++++++++ src/Common/WuQMacroGroupXmlReader.h | 88 +++++ src/Common/WuQMacroGroupXmlWriter.cxx | 225 +++++++++++ src/Common/WuQMacroGroupXmlWriter.h | 74 ++++ src/Common/WuQMacroMouseEventInfo.cxx | 98 +++-- src/Common/WuQMacroMouseEventInfo.h | 46 +-- src/GuiQt/CMakeLists.txt | 3 + src/GuiQt/WuQMacroAttributesDialog.cxx | 164 ++++++++ src/GuiQt/WuQMacroAttributesDialog.h | 81 ++++ src/GuiQt/WuQMacroCreateDialog.cxx | 10 +- src/GuiQt/WuQMacroCreateDialog.h | 2 + src/GuiQt/WuQMacroDialog.cxx | 157 ++++++-- src/GuiQt/WuQMacroDialog.h | 26 +- src/GuiQt/WuQMacroExecutor.cxx | 48 ++- src/GuiQt/WuQMacroExecutor.h | 23 +- src/GuiQt/WuQMacroManager.cxx | 183 ++++++--- src/GuiQt/WuQMacroManager.h | 14 +- src/GuiQt/WuQMacroMenu.cxx | 13 +- src/GuiQt/WuQMacroSignalWatcher.cxx | 49 --- 32 files changed, 2694 insertions(+), 632 deletions(-) create mode 100644 src/Common/WuQMacroDataValueTypeEnum.cxx create mode 100644 src/Common/WuQMacroDataValueTypeEnum.h create mode 100644 src/Common/WuQMacroGroupXmlFormatBase.cxx create mode 100644 src/Common/WuQMacroGroupXmlFormatBase.h create mode 100644 src/Common/WuQMacroGroupXmlReader.cxx create mode 100644 src/Common/WuQMacroGroupXmlReader.h create mode 100644 src/Common/WuQMacroGroupXmlWriter.cxx create mode 100644 src/Common/WuQMacroGroupXmlWriter.h create mode 100644 src/GuiQt/WuQMacroAttributesDialog.cxx create mode 100644 src/GuiQt/WuQMacroAttributesDialog.h diff --git a/src/Common/CMakeLists.txt b/src/Common/CMakeLists.txt index e643a8bcf..8489a6b48 100644 --- a/src/Common/CMakeLists.txt +++ b/src/Common/CMakeLists.txt @@ -162,7 +162,11 @@ VoxelIJK.h WorkbenchSpecialVersionEnum.h WuQMacro.h WuQMacroCommand.h +WuQMacroDataValueTypeEnum.h WuQMacroGroup.h +WuQMacroGroupXmlFormatBase.h +WuQMacroGroupXmlReader.h +WuQMacroGroupXmlWriter.h WuQMacroModeEnum.h WuQMacroMouseEventInfo.h WuQMacroMouseEventTypeEnum.h @@ -270,7 +274,11 @@ VectorOperation.cxx WorkbenchSpecialVersionEnum.cxx WuQMacro.cxx WuQMacroCommand.cxx +WuQMacroDataValueTypeEnum.cxx WuQMacroGroup.cxx +WuQMacroGroupXmlFormatBase.cxx +WuQMacroGroupXmlReader.cxx +WuQMacroGroupXmlWriter.cxx WuQMacroModeEnum.cxx WuQMacroMouseEventInfo.cxx WuQMacroMouseEventTypeEnum.cxx diff --git a/src/Common/CaretPreferences.cxx b/src/Common/CaretPreferences.cxx index dd93dd695..575703650 100644 --- a/src/Common/CaretPreferences.cxx +++ b/src/Common/CaretPreferences.cxx @@ -337,6 +337,64 @@ CaretPreferences::writeCustomViews() this->qSettings->sync(); } +/** + * Read macros from preferences + * + * @param performSync + * If true, synchronize preferences before reading macros + */ +void +CaretPreferences::readMacros(const bool performSync) +{ + if (performSync) { + this->qSettings->sync(); + } + + const QString macrosXmlString = this->getString(NAME_MACROS); + if (macrosXmlString.isEmpty()) { + m_macros->clear(); + } + else { + QString errorMessage; + QString warningMessage; + if ( ! m_macros->readXmlFromString(macrosXmlString, + errorMessage, + warningMessage)) { + CaretLogSevere("Reading macros from preferences: " + + errorMessage); + } + else if ( ! warningMessage.isEmpty()) { + CaretLogWarning(warningMessage); + } + } +} + +/** + * Write macros to preferences + */ +void +CaretPreferences::writeMacros() +{ + if ( ! m_macros->isModified()) { + return; + } + + QString macrosXmlString; + + if (m_macros->getNumberOfMacros() > 0) { + QString errorMessage; + if ( ! m_macros->writeXmlToString(macrosXmlString, + errorMessage)) { + CaretLogSevere("Writing macros to preferences: " + + errorMessage); + } + } + + this->setString(NAME_MACROS, + macrosXmlString); + this->qSettings->sync(); +} + /** * Remove all of the tile tabs configurations. @@ -1491,7 +1549,9 @@ CaretPreferences::readPreferences() this->readCustomViews(false); - this->readTileTabsConfigurations(); + this->readTileTabsConfigurations(false); + + this->readMacros(false); AString levelName = this->qSettings->value(NAME_LOGGING_LEVEL, LogLevelEnum::toName(LogLevelEnum::INFO)).toString(); diff --git a/src/Common/CaretPreferences.h b/src/Common/CaretPreferences.h index d0399e722..b8943e74a 100644 --- a/src/Common/CaretPreferences.h +++ b/src/Common/CaretPreferences.h @@ -200,6 +200,10 @@ namespace caret { const WuQMacroGroup* getMacros() const; + void readMacros(const bool performSync = true); + + void writeMacros(); + private: CaretPreferences(const CaretPreferences&); @@ -333,6 +337,7 @@ namespace caret { static const AString NAME_DYNAMIC_CONNECTIVITY_ON; static const AString NAME_IMAGE_CAPTURE_METHOD; static const AString NAME_LOGGING_LEVEL; + static const AString NAME_MACROS; static const AString NAME_MANAGE_FILES_VIEW_FILE_TYPE; static const AString NAME_OPENGL_DRAWING_METHOD; static const AString NAME_PREVIOUS_SCENE_FILES; @@ -377,6 +382,7 @@ namespace caret { const AString CaretPreferences::NAME_DYNAMIC_CONNECTIVITY_ON = "dynamicConnectivityDefaultedOn"; const AString CaretPreferences::NAME_IMAGE_CAPTURE_METHOD = "imageCaptureMethod"; const AString CaretPreferences::NAME_LOGGING_LEVEL = "loggingLevel"; + const AString CaretPreferences::NAME_MACROS = "macros"; const AString CaretPreferences::NAME_MANAGE_FILES_VIEW_FILE_TYPE = "manageFilesViewFileType"; const AString CaretPreferences::NAME_OPENGL_DRAWING_METHOD = "openGLDrawingMethod"; const AString CaretPreferences::NAME_PREVIOUS_SCENE_FILES = "previousSceneFiles"; diff --git a/src/Common/WuQMacro.cxx b/src/Common/WuQMacro.cxx index 5a8a07582..774b128ed 100644 --- a/src/Common/WuQMacro.cxx +++ b/src/Common/WuQMacro.cxx @@ -23,14 +23,8 @@ #include "WuQMacro.h" #undef __WU_Q_MACRO_DECLARE__ -//#include -// -//#include -//#include - #include "CaretAssert.h" #include "WuQMacroCommand.h" -//#include "WuQMacroSignalEmitter.h" using namespace caret; @@ -56,6 +50,15 @@ WuQMacro::WuQMacro() * Destructor. */ WuQMacro::~WuQMacro() +{ + clearCommands(); +} + +/** + * Clear (remove) all commands in this macro + */ +void +WuQMacro::clearCommands() { for (auto mc : m_macroCommands) { delete mc; @@ -63,6 +66,53 @@ WuQMacro::~WuQMacro() m_macroCommands.clear(); } +/** + * Copy constructor. + * @param obj + * Object that is copied. + */ +WuQMacro::WuQMacro(const WuQMacro& obj) +: CaretObjectTracksModification(obj) +{ + this->copyHelperWuQMacro(obj); +} + +/** + * Assignment operator. + * @param obj + * Data copied from obj to this. + * @return + * Reference to this object. + */ +WuQMacro& +WuQMacro::operator=(const WuQMacro& obj) +{ + if (this != &obj) { + CaretObjectTracksModification::operator=(obj); + this->copyHelperWuQMacro(obj); + } + return *this; +} + +/** + * Helps with copying an object of this type. + * @param obj + * Object that is copied. + */ +void +WuQMacro::copyHelperWuQMacro(const WuQMacro& obj) +{ + clearCommands(); + + for (auto mc : obj.m_macroCommands) { + addMacroCommand(new WuQMacroCommand(*mc)); + } + + m_name = obj.m_name; + m_description = obj.m_description; + m_functionKey = obj.m_functionKey; +} + void WuQMacro::addMacroCommand(WuQMacroCommand* macroCommand) { @@ -118,6 +168,30 @@ WuQMacro::setDescription(const QString& description) } } +/** + * @return The function key + */ +QString +WuQMacro::getFunctionKey() const +{ + return m_functionKey; +} + +/** + * Set the function key + * + * @param functionKey + * New function key + */ +void +WuQMacro::setFunctionKey(const QString& functionKey) +{ + if (m_functionKey != functionKey) { + m_functionKey = functionKey; + setModified(); + } +} + /** * @return The number of macro commands in this macro */ @@ -153,110 +227,37 @@ WuQMacro::getMacroCommandAtIndex(const int32_t index) return m_macroCommands[index]; } +/** + * @return True if this instance is modified + */ +bool +WuQMacro::isModified() const +{ + if (CaretObjectTracksModification::isModified()) { + return true; + } + + for (const auto mc : m_macroCommands) { + if (mc->isModified()) { + return true; + } + } + + return false; +} -//void -//WuQMacro::moveMouse(QWidget* widget, -// const bool highlightFlag) -//{ -// CaretAssert(widget); -// -// const QPoint widgetCenter = widget->rect().center(); -// const QPoint windowPoint = widget->mapToGlobal(widgetCenter); -// -// if (highlightFlag) { -// const float radius = 15.0; -// for (float angle = 0.0; angle < 6.28; angle += 0.314) { -// const float x = windowPoint.x() + (std::cos(angle) * radius); -// const float y = windowPoint.y() + (std::sin(angle) * radius); -// QCursor::setPos(x, y); -// //SystemUtilities::sleepSeconds(0.025); -// } -// } -// -// QCursor::setPos(windowPoint); -//} -// -///** -// * Run the commands in this macro. -// * -// * @param window -// * The window from which macro was launched -// * @param stopOnErrorFlag -// * If true, stop running the commands if there is an error -// * @param errorMessageOut -// * Output containing any error messages -// * @return -// * True if the macro completed without errors, else false. -// */ -//bool -//WuQMacro::runMacro(QObject* window, -// const bool stopOnErrorFlag, -// QString& errorMessageOut) -//{ -// errorMessageOut.clear(); -// -// for (auto mc : m_macroCommands) { -// CaretAssert(mc); -// -// const QString objectName(mc->getObjectName()); -// QObject* object = window->findChild(objectName); -// if (object == NULL) { -// errorMessageOut.append("Unable to find object named " -// + objectName -// + "\n"); -// continue; -// } -// -// if (object->signalsBlocked()) { -// errorMessageOut.append("Object named " -// + objectName -// + " has signals blocked"); -// continue; -// } -// -// QWidget* widgetToMoveMouse = qobject_cast(object); -// -// if (widgetToMoveMouse == NULL) { -// QObject* object = window->findChild(objectName); -// if (object != NULL) { -// QObject* parent = object->parent(); -// if (parent != NULL) { -// widgetToMoveMouse = qobject_cast(parent); -// } -// } -// } -// -// const WuQMacroObjectTypeEnum::Enum objectType = mc->getObjectType(); -// -// const bool mouseEventFlag = (objectType == WuQMacroObjectTypeEnum::MOUSE_USER_EVENT); -// if (widgetToMoveMouse != NULL) { -// if ( ! mouseEventFlag) { -// const bool highlightFlag = ( ! mouseEventFlag); -// moveMouse(widgetToMoveMouse, -// highlightFlag); -// } -// } -// -// QString commandErrorMessage; -// if ( ! mc->runMacro(object, -// commandErrorMessage)) { -// errorMessageOut.append(commandErrorMessage + "\n"); -// if (stopOnErrorFlag) { -// return false; -// } -// } -// -// QGuiApplication::processEvents(); -// if ( ! mouseEventFlag) { -// SystemUtilities::sleepSeconds(1); -// } -// QGuiApplication::processEvents(); -// } -// -// return (errorMessageOut.isEmpty()); -// -// -//} +/** + * Clear the modified status + */ +void +WuQMacro::clearModified() +{ + CaretObjectTracksModification::clearModified(); + + for (auto mc : m_macroCommands) { + mc->clearModified(); + } +} /** * Get a description of this object's content. @@ -269,6 +270,7 @@ WuQMacro::toString() const s.append("Name=" + m_name + "\n"); s.append("Description=" + m_description + "\n"); + s.append("Function Key=" + m_functionKey + "\n"); for (auto mc : m_macroCommands) { s.append(mc->toString() + "\n"); diff --git a/src/Common/WuQMacro.h b/src/Common/WuQMacro.h index 870198e56..f47e95c24 100644 --- a/src/Common/WuQMacro.h +++ b/src/Common/WuQMacro.h @@ -27,9 +27,6 @@ #include "CaretObjectTracksModification.h" -//class QObject; -//class QWidget; - namespace caret { class WuQMacroCommand; @@ -41,9 +38,9 @@ namespace caret { virtual ~WuQMacro(); - WuQMacro(const WuQMacro&) = delete; - - WuQMacro& operator=(const WuQMacro&) = delete; + WuQMacro(const WuQMacro& obj); + + WuQMacro& operator=(const WuQMacro& obj); void addMacroCommand(WuQMacroCommand* macroCommand); @@ -53,10 +50,6 @@ namespace caret { WuQMacroCommand* getMacroCommandAtIndex(const int32_t index); -// bool runMacro(QObject* window, -// const bool stopOnErrorFlag, -// QString& errorMessageOut); - QString getName() const; void setName(const QString& name); @@ -65,16 +58,22 @@ namespace caret { void setDescription(const QString& description); + QString getFunctionKey() const; + + void setFunctionKey(const QString& functionKey); + // ADD_NEW_METHODS_HERE virtual AString toString() const; + + virtual bool isModified() const override; + + virtual void clearModified() override; private: -// void emitActionChecked(QAction* action, -// const bool checked); + void copyHelperWuQMacro(const WuQMacro& obj); -// void moveMouse(QWidget* widget, -// const bool highlightFlag); + void clearCommands(); std::vector m_macroCommands; @@ -82,6 +81,8 @@ namespace caret { QString m_description; + QString m_functionKey; + // ADD_NEW_MEMBERS_HERE }; diff --git a/src/Common/WuQMacroCommand.cxx b/src/Common/WuQMacroCommand.cxx index 94522db79..62d722a2a 100644 --- a/src/Common/WuQMacroCommand.cxx +++ b/src/Common/WuQMacroCommand.cxx @@ -23,28 +23,9 @@ #include "WuQMacroCommand.h" #undef __WU_Q_MACRO_COMMAND_DECLARE__ -//#include -//#include -//#include -//#include -//#include -//#include -//#include -//#include -//#include -//#include -//#include -//#include -//#include -//#include -//#include -//#include - #include "CaretAssert.h" #include "WuQMacroMouseEventInfo.h" -//#include "WuQMacroSignalEmitter.h" - using namespace caret; /** @@ -76,6 +57,58 @@ m_objectToolTip(objectToolTip), m_value(value), m_macroMouseEvent(NULL) { + switch (m_objectType) { + case WuQMacroObjectTypeEnum::ACTION: + m_objectDataValueType = WuQMacroDataValueTypeEnum::BOOLEAN; + break; + case WuQMacroObjectTypeEnum::CHECK_BOX: + m_objectDataValueType = WuQMacroDataValueTypeEnum::BOOLEAN; + break; + case WuQMacroObjectTypeEnum::COMBO_BOX: + m_objectDataValueType = WuQMacroDataValueTypeEnum::INTEGER; + break; + case WuQMacroObjectTypeEnum::DOUBLE_SPIN_BOX: + m_objectDataValueType = WuQMacroDataValueTypeEnum::FLOAT; + break; + case WuQMacroObjectTypeEnum::INVALID: + break; + case WuQMacroObjectTypeEnum::LINE_EDIT: + m_objectDataValueType = WuQMacroDataValueTypeEnum::STRING; + break; + case WuQMacroObjectTypeEnum::LIST_WIDGET: + m_objectDataValueType = WuQMacroDataValueTypeEnum::STRING; + break; + case WuQMacroObjectTypeEnum::MENU: + m_objectDataValueType = WuQMacroDataValueTypeEnum::STRING; + break; + case WuQMacroObjectTypeEnum::MOUSE_USER_EVENT: + m_objectDataValueType = WuQMacroDataValueTypeEnum::MOUSE; + CaretAssertMessage(0, "Must use constructor for mouse event"); + break; + case WuQMacroObjectTypeEnum::PUSH_BUTTON: + m_objectDataValueType = WuQMacroDataValueTypeEnum::BOOLEAN; + break; + case WuQMacroObjectTypeEnum::RADIO_BUTTON: + m_objectDataValueType = WuQMacroDataValueTypeEnum::BOOLEAN; + break; + case WuQMacroObjectTypeEnum::SLIDER: + m_objectDataValueType = WuQMacroDataValueTypeEnum::INTEGER; + break; + case WuQMacroObjectTypeEnum::SPIN_BOX: + m_objectDataValueType = WuQMacroDataValueTypeEnum::INTEGER; + break; + case WuQMacroObjectTypeEnum::TAB_BAR: + m_objectDataValueType = WuQMacroDataValueTypeEnum::INTEGER; + break; + case WuQMacroObjectTypeEnum::TAB_WIDGET: + m_objectDataValueType = WuQMacroDataValueTypeEnum::INTEGER; + break; + case WuQMacroObjectTypeEnum::TOOL_BUTTON: + m_objectDataValueType = WuQMacroDataValueTypeEnum::BOOLEAN; + break; + } + + setModified(); } /** @@ -100,7 +133,9 @@ m_objectToolTip(objectToolTip), m_value((int)0), m_macroMouseEvent(mouseEventInfo) { + m_objectDataValueType = WuQMacroDataValueTypeEnum::MOUSE; + setModified(); } /** @@ -113,6 +148,50 @@ WuQMacroCommand::~WuQMacroCommand() } } +/** + * Copy constructor. + * @param obj + * Object that is copied. + */ +WuQMacroCommand::WuQMacroCommand(const WuQMacroCommand& obj) +: CaretObjectTracksModification(obj) +{ + this->copyHelperWuQMacroCommand(obj); +} + +/** + * Assignment operator. + * @param obj + * Data copied from obj to this. + * @return + * Reference to this object. + */ +WuQMacroCommand& +WuQMacroCommand::operator=(const WuQMacroCommand& obj) +{ + if (this != &obj) { + CaretObjectTracksModification::operator=(obj); + this->copyHelperWuQMacroCommand(obj); + } + return *this; +} + +/** + * Helps with copying an object of this type. + * @param obj + * Object that is copied. + */ +void +WuQMacroCommand::copyHelperWuQMacroCommand(const WuQMacroCommand& obj) +{ + m_objectType = obj.m_objectType; + m_objectName = obj.m_objectName; + m_objectToolTip = obj.m_objectToolTip; + m_value = obj.m_value; + m_macroMouseEvent = new WuQMacroMouseEventInfo(*obj.m_macroMouseEvent); + m_objectDataValueType = m_objectDataValueType; +} + /** * @return The object' type */ @@ -122,6 +201,15 @@ WuQMacroCommand::getObjectType() const return m_objectType; } +/** + * @return Type of data value for object + */ +WuQMacroDataValueTypeEnum::Enum +WuQMacroCommand::getObjectDataValueType() const +{ + return m_objectDataValueType; +} + /** * @return The object's name */ @@ -158,225 +246,6 @@ WuQMacroCommand::getMouseEventInfo() const return m_macroMouseEvent; } -///** -// * Run the command. -// * -// * @param object -// * The object that must be cast to a specific type -// * @param errorMessageOut -// * Output containing any error messages -// * @return -// * True if the macro completed without errors, else false. -// */ -//bool -//WuQMacroCommand::runMacro(QObject* object, -// QString& errorMessageOut) const -//{ -// errorMessageOut.clear(); -// -// bool notFoundFlag(false); -// switch (m_objectType) { -// case WuQMacroObjectTypeEnum::ACTION: -// { -// QAction* action = qobject_cast(object); -// if (action != NULL) { -// WuQMacroSignalEmitter signalEmitter; -// signalEmitter.emitQActionSignal(action, -// m_value.toBool()); -// } -// else { -// notFoundFlag = true; -// } -// } -// break; -// case WuQMacroObjectTypeEnum::CHECK_BOX: -// { -// QCheckBox* checkBox = qobject_cast(object); -// if (checkBox != NULL) { -// WuQMacroSignalEmitter signalEmitter; -// signalEmitter.emitQCheckBoxSignal(checkBox, -// m_value.toBool()); -// } -// else { -// notFoundFlag = true; -// } -// } -// break; -// case WuQMacroObjectTypeEnum::COMBO_BOX: -// { -// QComboBox* comboBox = qobject_cast(object); -// if (comboBox != NULL) { -// WuQMacroSignalEmitter signalEmitter; -// signalEmitter.emitQComboBoxSignal(comboBox, -// m_value.toInt()); -// } -// else { -// notFoundFlag = true; -// } -// } -// break; -// case WuQMacroObjectTypeEnum::DOUBLE_SPIN_BOX: -// { -// QDoubleSpinBox* doubleSpinBox = qobject_cast(object); -// if (doubleSpinBox != NULL) { -// WuQMacroSignalEmitter signalEmitter; -// signalEmitter.emitQDoubleSpinBoxSignal(doubleSpinBox, -// m_value.toDouble()); -// } -// else { -// notFoundFlag = true; -// } -// } -// break; -// case WuQMacroObjectTypeEnum::INVALID: -// CaretAssert(0); -// break; -// case WuQMacroObjectTypeEnum::LINE_EDIT: -// { -// QLineEdit* lineEdit = qobject_cast(object); -// if (lineEdit != NULL) { -// WuQMacroSignalEmitter signalEmitter; -// signalEmitter.emitQLineEditSignal(lineEdit, -// m_value.toString()); -// } -// else { -// notFoundFlag = true; -// } -// } -// break; -// case WuQMacroObjectTypeEnum::LIST_WIDGET: -// { -// QListWidget* listWidget = qobject_cast(object); -// if (listWidget != NULL) { -// WuQMacroSignalEmitter signalEmitter; -// signalEmitter.emitQListWidgetSignal(listWidget, -// m_value.toString()); -// } -// else { -// notFoundFlag = true; -// } -// } -// break; -// case WuQMacroObjectTypeEnum::MENU: -// { -// QMenu* menu = qobject_cast(object); -// if (menu != NULL) { -// WuQMacroSignalEmitter signalEmitter; -// signalEmitter.emitQMenuTriggered(menu, -// m_value.toString()); -// } -// else { -// notFoundFlag = true; -// } -// } -// break; -// case WuQMacroObjectTypeEnum::MOUSE_USER_EVENT: -// CaretAssertToDoFatal(); -// break; -// case WuQMacroObjectTypeEnum::PUSH_BUTTON: -// { -// QPushButton* pushButton = qobject_cast(object); -// if (pushButton != NULL) { -// WuQMacroSignalEmitter signalEmitter; -// signalEmitter.emitQPushButtonSignal(pushButton, -// m_value.toBool()); -// } -// else { -// notFoundFlag = true; -// } -// } -// break; -// case WuQMacroObjectTypeEnum::RADIO_BUTTON: -// { -// QRadioButton* radioButton = qobject_cast(object); -// if (radioButton != NULL) { -// WuQMacroSignalEmitter signalEmitter; -// signalEmitter.emitQRadioButtonSignal(radioButton, -// m_value.toBool()); -// } -// else { -// notFoundFlag = true; -// } -// } -// break; -// case WuQMacroObjectTypeEnum::SLIDER: -// { -// QSlider* slider = qobject_cast(object); -// if (slider != NULL) { -// WuQMacroSignalEmitter signalEmitter; -// signalEmitter.emitQSliderSignal(slider, -// m_value.toInt()); -// } -// else { -// notFoundFlag = true; -// } -// } -// break; -// case WuQMacroObjectTypeEnum::SPIN_BOX: -// { -// QSpinBox* spinBox = qobject_cast(object); -// if (spinBox != NULL) { -// WuQMacroSignalEmitter signalEmitter; -// signalEmitter.emitQSpinBoxSignal(spinBox, -// m_value.toInt()); -// } -// else { -// notFoundFlag = true; -// } -// } -// break; -// case WuQMacroObjectTypeEnum::TAB_BAR: -// { -// QTabBar* tabBar = qobject_cast(object); -// if (tabBar != NULL) { -// WuQMacroSignalEmitter signalEmitter; -// signalEmitter.emitQTabBarSignal(tabBar, -// m_value.toInt()); -// } -// else { -// notFoundFlag = true; -// } -// } -// break; -// case WuQMacroObjectTypeEnum::TAB_WIDGET: -// { -// QTabWidget* tabWidget = qobject_cast(object); -// if (tabWidget != NULL) { -// WuQMacroSignalEmitter signalEmitter; -// signalEmitter.emitQTabWidgetSignal(tabWidget, -// m_value.toInt()); -// } -// else { -// notFoundFlag = true; -// } -// } -// break; -// case WuQMacroObjectTypeEnum::TOOL_BUTTON: -// { -// QToolButton* toolButton = qobject_cast(object); -// if (toolButton != NULL) { -// WuQMacroSignalEmitter signalEmitter; -// signalEmitter.emitQToolButtonSignal(toolButton, -// m_value.toBool()); -// } -// else { -// notFoundFlag = true; -// } -// } -// break; -// } -// -// if (notFoundFlag) { -// errorMessageOut = ("ERROR: Unable to cast object named " -// + m_objectName -// + " to " -// + WuQMacroObjectTypeEnum::toGuiName(m_objectType)); -// return false; -// } -// -// return true; -//} - /** * Get a description of this object's content. * @return String describing this object's content. diff --git a/src/Common/WuQMacroCommand.h b/src/Common/WuQMacroCommand.h index bc5fca3fa..fe3f11ace 100644 --- a/src/Common/WuQMacroCommand.h +++ b/src/Common/WuQMacroCommand.h @@ -28,6 +28,7 @@ #include #include "CaretObjectTracksModification.h" +#include "WuQMacroDataValueTypeEnum.h" #include "WuQMacroObjectTypeEnum.h" class QObject; @@ -51,12 +52,14 @@ namespace caret { virtual ~WuQMacroCommand(); - WuQMacroCommand(const WuQMacroCommand&) = delete; - - WuQMacroCommand& operator=(const WuQMacroCommand&) = delete; + WuQMacroCommand(const WuQMacroCommand& obj); + + WuQMacroCommand& operator=(const WuQMacroCommand& obj); WuQMacroObjectTypeEnum::Enum getObjectType() const; + WuQMacroDataValueTypeEnum::Enum getObjectDataValueType() const; + QString getObjectName() const; QString getObjectToolTip() const; @@ -65,24 +68,24 @@ namespace caret { const WuQMacroMouseEventInfo* getMouseEventInfo() const; -// bool runMacro(QObject* object, -// QString& errorMessageOut) const; - // ADD_NEW_METHODS_HERE virtual AString toString() const; private: + void copyHelperWuQMacroCommand(const WuQMacroCommand& obj); + + WuQMacroObjectTypeEnum::Enum m_objectType; - const WuQMacroObjectTypeEnum::Enum m_objectType; + QString m_objectName; - const QString m_objectName; + QString m_objectToolTip; - const QString m_objectToolTip; + QVariant m_value; - const QVariant m_value; + WuQMacroMouseEventInfo* m_macroMouseEvent; - const WuQMacroMouseEventInfo* m_macroMouseEvent; + WuQMacroDataValueTypeEnum::Enum m_objectDataValueType; // ADD_NEW_MEMBERS_HERE diff --git a/src/Common/WuQMacroDataValueTypeEnum.cxx b/src/Common/WuQMacroDataValueTypeEnum.cxx new file mode 100644 index 000000000..612e1b197 --- /dev/null +++ b/src/Common/WuQMacroDataValueTypeEnum.cxx @@ -0,0 +1,387 @@ + +/*LICENSE_START*/ +/* + * Copyright (C) 2018 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + +#include +#define __WU_Q_MACRO_DATA_VALUE_TYPE_ENUM_DECLARE__ +#include "WuQMacroDataValueTypeEnum.h" +#undef __WU_Q_MACRO_DATA_VALUE_TYPE_ENUM_DECLARE__ + +#include "CaretAssert.h" + +using namespace caret; + + +/** + * \class caret::WuQMacroDataValueTypeEnum + * \brief + * + * + * + * Using this enumerated type in the GUI with an EnumComboBoxTemplate + * + * Header File (.h) + * Forward declare the data type: + * class EnumComboBoxTemplate; + * + * Declare the member: + * EnumComboBoxTemplate* m_wuQMacroDataValueTypeEnumComboBox; + * + * Declare a slot that is called when user changes selection + * private slots: + * void wuQMacroDataValueTypeEnumComboBoxItemActivated(); + * + * Implementation File (.cxx) + * Include the header files + * #include "EnumComboBoxTemplate.h" + * #include "WuQMacroDataValueTypeEnum.h" + * + * Instatiate: + * m_wuQMacroDataValueTypeEnumComboBox = new EnumComboBoxTemplate(this); + * m_wuQMacroDataValueTypeEnumComboBox->setup(); + * + * Get notified when the user changes the selection: + * QObject::connect(m_wuQMacroDataValueTypeEnumComboBox, SIGNAL(itemActivated()), + * this, SLOT(wuQMacroDataValueTypeEnumComboBoxItemActivated())); + * + * Update the selection: + * m_wuQMacroDataValueTypeEnumComboBox->setSelectedItem(NEW_VALUE); + * + * Read the selection: + * const WuQMacroDataValueTypeEnum::Enum VARIABLE = m_wuQMacroDataValueTypeEnumComboBox->getSelectedItem(); + * + */ + +/** + * Constructor. + * + * @param enumValue + * An enumerated value. + * @param name + * Name of enumerated value. + * + * @param guiName + * User-friendly name for use in user-interface. + */ +WuQMacroDataValueTypeEnum::WuQMacroDataValueTypeEnum(const Enum enumValue, + const AString& name, + const AString& guiName) +{ + this->enumValue = enumValue; + this->integerCode = integerCodeCounter++; + this->name = name; + this->guiName = guiName; +} + +/** + * Destructor. + */ +WuQMacroDataValueTypeEnum::~WuQMacroDataValueTypeEnum() +{ +} + +/** + * Initialize the enumerated metadata. + */ +void +WuQMacroDataValueTypeEnum::initialize() +{ + if (initializedFlag) { + return; + } + initializedFlag = true; + + enumData.push_back(WuQMacroDataValueTypeEnum(BOOLEAN, + "BOOLEAN", + "Boolean")); + + enumData.push_back(WuQMacroDataValueTypeEnum(FLOAT, + "FLOAT", + "Float")); + + enumData.push_back(WuQMacroDataValueTypeEnum(INTEGER, + "INTEGER", + "Integer")); + + enumData.push_back(WuQMacroDataValueTypeEnum(MOUSE, + "MOUSE", + "Mouse")); + + enumData.push_back(WuQMacroDataValueTypeEnum(STRING, + "STRING", + "String")); + +} + +/** + * Find the data for and enumerated value. + * @param enumValue + * The enumerated value. + * @return Pointer to data for this enumerated type + * or NULL if no data for type or if type is invalid. + */ +const WuQMacroDataValueTypeEnum* +WuQMacroDataValueTypeEnum::findData(const Enum enumValue) +{ + if (initializedFlag == false) initialize(); + + size_t num = enumData.size(); + for (size_t i = 0; i < num; i++) { + const WuQMacroDataValueTypeEnum* d = &enumData[i]; + if (d->enumValue == enumValue) { + return d; + } + } + + return NULL; +} + +/** + * Get a string representation of the enumerated type. + * @param enumValue + * Enumerated value. + * @return + * String representing enumerated value. + */ +AString +WuQMacroDataValueTypeEnum::toName(Enum enumValue) { + if (initializedFlag == false) initialize(); + + const WuQMacroDataValueTypeEnum* enumInstance = findData(enumValue); + return enumInstance->name; +} + +/** + * Get an enumerated value corresponding to its name. + * @param name + * Name of enumerated value. + * @param isValidOut + * If not NULL, it is set indicating that a + * enum value exists for the input name. + * @return + * Enumerated value. + */ +WuQMacroDataValueTypeEnum::Enum +WuQMacroDataValueTypeEnum::fromName(const AString& name, bool* isValidOut) +{ + if (initializedFlag == false) initialize(); + + bool validFlag = false; + Enum enumValue = WuQMacroDataValueTypeEnum::enumData[0].enumValue; + + for (std::vector::iterator iter = enumData.begin(); + iter != enumData.end(); + iter++) { + const WuQMacroDataValueTypeEnum& d = *iter; + if (d.name == name) { + enumValue = d.enumValue; + validFlag = true; + break; + } + } + + if (isValidOut != 0) { + *isValidOut = validFlag; + } + else if (validFlag == false) { + CaretAssertMessage(0, AString("Name " + name + "failed to match enumerated value for type WuQMacroDataValueTypeEnum")); + } + return enumValue; +} + +/** + * Get a GUI string representation of the enumerated type. + * @param enumValue + * Enumerated value. + * @return + * String representing enumerated value. + */ +AString +WuQMacroDataValueTypeEnum::toGuiName(Enum enumValue) { + if (initializedFlag == false) initialize(); + + const WuQMacroDataValueTypeEnum* enumInstance = findData(enumValue); + return enumInstance->guiName; +} + +/** + * Get an enumerated value corresponding to its GUI name. + * @param s + * Name of enumerated value. + * @param isValidOut + * If not NULL, it is set indicating that a + * enum value exists for the input name. + * @return + * Enumerated value. + */ +WuQMacroDataValueTypeEnum::Enum +WuQMacroDataValueTypeEnum::fromGuiName(const AString& guiName, bool* isValidOut) +{ + if (initializedFlag == false) initialize(); + + bool validFlag = false; + Enum enumValue = WuQMacroDataValueTypeEnum::enumData[0].enumValue; + + for (std::vector::iterator iter = enumData.begin(); + iter != enumData.end(); + iter++) { + const WuQMacroDataValueTypeEnum& d = *iter; + if (d.guiName == guiName) { + enumValue = d.enumValue; + validFlag = true; + break; + } + } + + if (isValidOut != 0) { + *isValidOut = validFlag; + } + else if (validFlag == false) { + CaretAssertMessage(0, AString("guiName " + guiName + "failed to match enumerated value for type WuQMacroDataValueTypeEnum")); + } + return enumValue; +} + +/** + * Get the integer code for a data type. + * + * @return + * Integer code for data type. + */ +int32_t +WuQMacroDataValueTypeEnum::toIntegerCode(Enum enumValue) +{ + if (initializedFlag == false) initialize(); + const WuQMacroDataValueTypeEnum* enumInstance = findData(enumValue); + return enumInstance->integerCode; +} + +/** + * Find the data type corresponding to an integer code. + * + * @param integerCode + * Integer code for enum. + * @param isValidOut + * If not NULL, on exit isValidOut will indicate if + * integer code is valid. + * @return + * Enum for integer code. + */ +WuQMacroDataValueTypeEnum::Enum +WuQMacroDataValueTypeEnum::fromIntegerCode(const int32_t integerCode, bool* isValidOut) +{ + if (initializedFlag == false) initialize(); + + bool validFlag = false; + Enum enumValue = WuQMacroDataValueTypeEnum::enumData[0].enumValue; + + for (std::vector::iterator iter = enumData.begin(); + iter != enumData.end(); + iter++) { + const WuQMacroDataValueTypeEnum& enumInstance = *iter; + if (enumInstance.integerCode == integerCode) { + enumValue = enumInstance.enumValue; + validFlag = true; + break; + } + } + + if (isValidOut != 0) { + *isValidOut = validFlag; + } + else if (validFlag == false) { + CaretAssertMessage(0, AString("Integer code " + AString::number(integerCode) + "failed to match enumerated value for type WuQMacroDataValueTypeEnum")); + } + return enumValue; +} + +/** + * Get all of the enumerated type values. The values can be used + * as parameters to toXXX() methods to get associated metadata. + * + * @param allEnums + * A vector that is OUTPUT containing all of the enumerated values. + */ +void +WuQMacroDataValueTypeEnum::getAllEnums(std::vector& allEnums) +{ + if (initializedFlag == false) initialize(); + + allEnums.clear(); + + for (std::vector::iterator iter = enumData.begin(); + iter != enumData.end(); + iter++) { + allEnums.push_back(iter->enumValue); + } +} + +/** + * Get all of the names of the enumerated type values. + * + * @param allNames + * A vector that is OUTPUT containing all of the names of the enumerated values. + * @param isSorted + * If true, the names are sorted in alphabetical order. + */ +void +WuQMacroDataValueTypeEnum::getAllNames(std::vector& allNames, const bool isSorted) +{ + if (initializedFlag == false) initialize(); + + allNames.clear(); + + for (std::vector::iterator iter = enumData.begin(); + iter != enumData.end(); + iter++) { + allNames.push_back(WuQMacroDataValueTypeEnum::toName(iter->enumValue)); + } + + if (isSorted) { + std::sort(allNames.begin(), allNames.end()); + } +} + +/** + * Get all of the GUI names of the enumerated type values. + * + * @param allNames + * A vector that is OUTPUT containing all of the GUI names of the enumerated values. + * @param isSorted + * If true, the names are sorted in alphabetical order. + */ +void +WuQMacroDataValueTypeEnum::getAllGuiNames(std::vector& allGuiNames, const bool isSorted) +{ + if (initializedFlag == false) initialize(); + + allGuiNames.clear(); + + for (std::vector::iterator iter = enumData.begin(); + iter != enumData.end(); + iter++) { + allGuiNames.push_back(WuQMacroDataValueTypeEnum::toGuiName(iter->enumValue)); + } + + if (isSorted) { + std::sort(allGuiNames.begin(), allGuiNames.end()); + } +} + diff --git a/src/Common/WuQMacroDataValueTypeEnum.h b/src/Common/WuQMacroDataValueTypeEnum.h new file mode 100644 index 000000000..aa5a06bf0 --- /dev/null +++ b/src/Common/WuQMacroDataValueTypeEnum.h @@ -0,0 +1,110 @@ +#ifndef __WU_Q_MACRO_DATA_VALUE_TYPE_ENUM_H__ +#define __WU_Q_MACRO_DATA_VALUE_TYPE_ENUM_H__ + +/*LICENSE_START*/ +/* + * Copyright (C) 2018 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + + +#include +#include +#include "AString.h" + +namespace caret { + +class WuQMacroDataValueTypeEnum { + +public: + /** + * Enumerated values. + */ + enum Enum { + /** */ + BOOLEAN, + /** */ + FLOAT, + /** */ + INTEGER, + /** */ + MOUSE, + /** */ + STRING + }; + + + ~WuQMacroDataValueTypeEnum(); + + static AString toName(Enum enumValue); + + static Enum fromName(const AString& name, bool* isValidOut); + + static AString toGuiName(Enum enumValue); + + static Enum fromGuiName(const AString& guiName, bool* isValidOut); + + static int32_t toIntegerCode(Enum enumValue); + + static Enum fromIntegerCode(const int32_t integerCode, bool* isValidOut); + + static void getAllEnums(std::vector& allEnums); + + static void getAllNames(std::vector& allNames, const bool isSorted); + + static void getAllGuiNames(std::vector& allGuiNames, const bool isSorted); + +private: + WuQMacroDataValueTypeEnum(const Enum enumValue, + const AString& name, + const AString& guiName); + + static const WuQMacroDataValueTypeEnum* findData(const Enum enumValue); + + /** Holds all instance of enum values and associated metadata */ + static std::vector enumData; + + /** Initialize instances that contain the enum values and metadata */ + static void initialize(); + + /** Indicates instance of enum values and metadata have been initialized */ + static bool initializedFlag; + + /** Auto generated integer codes */ + static int32_t integerCodeCounter; + + /** The enumerated type value for an instance */ + Enum enumValue; + + /** The integer code associated with an enumerated value */ + int32_t integerCode; + + /** The name, a text string that is identical to the enumerated value */ + AString name; + + /** A user-friendly name that is displayed in the GUI */ + AString guiName; +}; + +#ifdef __WU_Q_MACRO_DATA_VALUE_TYPE_ENUM_DECLARE__ +std::vector WuQMacroDataValueTypeEnum::enumData; +bool WuQMacroDataValueTypeEnum::initializedFlag = false; +int32_t WuQMacroDataValueTypeEnum::integerCodeCounter = 0; +#endif // __WU_Q_MACRO_DATA_VALUE_TYPE_ENUM_DECLARE__ + +} // namespace +#endif //__WU_Q_MACRO_DATA_VALUE_TYPE_ENUM_H__ diff --git a/src/Common/WuQMacroGroup.cxx b/src/Common/WuQMacroGroup.cxx index f0432e473..4e8e1a76b 100644 --- a/src/Common/WuQMacroGroup.cxx +++ b/src/Common/WuQMacroGroup.cxx @@ -25,6 +25,8 @@ #include "CaretAssert.h" #include "WuQMacro.h" +#include "WuQMacroGroupXmlReader.h" +#include "WuQMacroGroupXmlWriter.h" using namespace caret; @@ -39,12 +41,12 @@ using namespace caret; /** * Constructor. * - * @param groupName + * @param name * Name of group */ -WuQMacroGroup::WuQMacroGroup(const QString& groupName) +WuQMacroGroup::WuQMacroGroup(const QString& name) : CaretObjectTracksModification(), -m_groupName(groupName) +m_name(name) { } @@ -54,32 +56,91 @@ m_groupName(groupName) */ WuQMacroGroup::~WuQMacroGroup() { + clear(); +} + +/** + * Copy constructor. + * @param obj + * Object that is copied. + */ +WuQMacroGroup::WuQMacroGroup(const WuQMacroGroup& obj) +: CaretObjectTracksModification(obj) +{ + this->copyHelperWuQMacroGroup(obj); +} + +/** + * Assignment operator. + * @param obj + * Data copied from obj to this. + * @return + * Reference to this object. + */ +WuQMacroGroup& +WuQMacroGroup::operator=(const WuQMacroGroup& obj) +{ + if (this != &obj) { + CaretObjectTracksModification::operator=(obj); + this->copyHelperWuQMacroGroup(obj); + } + return *this; +} + +/** + * Helps with copying an object of this type. + * @param obj + * Object that is copied. + */ +void +WuQMacroGroup::copyHelperWuQMacroGroup(const WuQMacroGroup& obj) +{ + clear(); + + m_name = obj.m_name; + + for (auto m : obj.m_macros) { + addMacro(new WuQMacro(*m)); + } +} + +/** + * Clear (remove all macros) + */ +void +WuQMacroGroup::clear() +{ + if (m_macros.empty()) { + return; + } + for (auto m : m_macros) { delete m; } m_macros.clear(); + setModified(); } /** * @return Name of group */ QString -WuQMacroGroup::getGroupName() const +WuQMacroGroup::getName() const { - return m_groupName; + return m_name; } /** * Set name of group * - * @param groupName + * @param name * Name of group */ void -WuQMacroGroup::setGroupName(const QString& groupName) +WuQMacroGroup::setName(const QString& name) { - if (m_groupName != groupName) { - m_groupName = groupName; + if (m_name != name) { + m_name = name; setModified(); } } @@ -205,6 +266,101 @@ WuQMacroGroup::deleteMacroAtIndex(const int32_t index) setModified(); } +/** + * @return True if this instance is modified + */ +bool +WuQMacroGroup::isModified() const +{ + if (CaretObjectTracksModification::isModified()) { + return true; + } + + for (const auto m : m_macros) { + if (m->isModified()) { + return true; + } + } + + return false; +} + +/** + * Clear the modified status + */ +void +WuQMacroGroup::clearModified() +{ + CaretObjectTracksModification::clearModified(); + + for (auto m : m_macros) { + m->clearModified(); + } +} + +/** + * Read from a string containing XML. If successful, + * the modified status is cleared. + * + * @param xmlString + * String containing XML + * @param errorMessageOut + * Contains error information if reading fails + * @param nonFatalWarningMessageOut + * May contain non-fatal warnings when reading is successful + * @return + * True if successful, else false + */ +bool +WuQMacroGroup::readXmlFromString(const QString& xmlString, + QString& errorMessageOut, + QString& nonFatalWarningMessageOut) +{ + errorMessageOut.clear(); + nonFatalWarningMessageOut.clear(); + + WuQMacroGroupXmlReader reader; + reader.readFromString(xmlString, + this); + if (reader.hasError()) { + errorMessageOut = reader.getErrorMessage(); + return false; + } + else { + if (reader.hasWarnings()) { + nonFatalWarningMessageOut = reader.getWarningMessage(); + } + } + clearModified(); + + return true; +} + +/** + * Write to a string containing XML. If successful, + * the modified status is cleared. + * + * @param xmlString + * String to which XML is written + * @param errorMessageOut + * Contains error information if reading fails + * @return + * True if successful, else false + */ +bool +WuQMacroGroup::writeXmlToString(QString& xmlString, + QString& errorMessageOut) +{ + xmlString.clear(); + errorMessageOut.clear(); + + WuQMacroGroupXmlWriter writer; + writer.writeToString(this, + xmlString); + clearModified(); + + return true; +} /** * Get a description of this object's content. @@ -213,7 +369,7 @@ WuQMacroGroup::deleteMacroAtIndex(const int32_t index) AString WuQMacroGroup::toString() const { - QString s("WuQMacroGroup name=" + m_groupName + "\n"); + QString s("WuQMacroGroup name=" + m_name + "\n"); for (auto m : m_macros) { s.append(m->toString() + "\n"); } diff --git a/src/Common/WuQMacroGroup.h b/src/Common/WuQMacroGroup.h index c387554c6..d6e80ebf6 100644 --- a/src/Common/WuQMacroGroup.h +++ b/src/Common/WuQMacroGroup.h @@ -40,13 +40,15 @@ namespace caret { virtual ~WuQMacroGroup(); - WuQMacroGroup(const WuQMacroGroup&) = delete; - - WuQMacroGroup& operator=(const WuQMacroGroup&) = delete; - - QString getGroupName() const; + WuQMacroGroup(const WuQMacroGroup& obj); + + WuQMacroGroup& operator=(const WuQMacroGroup& obj); + + void clear(); + + QString getName() const; - void setGroupName(const QString& groupName); + void setName(const QString& name); void addMacro(WuQMacro* macro); @@ -64,12 +66,25 @@ namespace caret { void deleteMacroAtIndex(const int32_t index); + virtual bool isModified() const override; + + virtual void clearModified() override; + + bool readXmlFromString(const QString& xmlString, + QString& errorMessageOut, + QString& nonFatalWarningMessageOut); + + bool writeXmlToString(QString& xmlString, + QString& errorMessageOut); + // ADD_NEW_METHODS_HERE virtual AString toString() const; private: - QString m_groupName; + void copyHelperWuQMacroGroup(const WuQMacroGroup& obj); + + QString m_name; std::vector m_macros; diff --git a/src/Common/WuQMacroGroupXmlFormatBase.cxx b/src/Common/WuQMacroGroupXmlFormatBase.cxx new file mode 100644 index 000000000..2928dd57d --- /dev/null +++ b/src/Common/WuQMacroGroupXmlFormatBase.cxx @@ -0,0 +1,62 @@ + +/*LICENSE_START*/ +/* + * Copyright (C) 2018 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + +#define __WU_Q_MACRO_GROUP_XML_FORMAT_BASE_DECLARE__ +#include "WuQMacroGroupXmlFormatBase.h" +#undef __WU_Q_MACRO_GROUP_XML_FORMAT_BASE_DECLARE__ + +#include "CaretAssert.h" +using namespace caret; + + + +/** + * \class caret::WuQMacroGroupXmlFormatBase + * \brief Base class for reading/writing macro group in XML + * \ingroup Common + */ + +/** + * Constructor. + */ +WuQMacroGroupXmlFormatBase::WuQMacroGroupXmlFormatBase() +: CaretObject() +{ + +} + +/** + * Destructor. + */ +WuQMacroGroupXmlFormatBase::~WuQMacroGroupXmlFormatBase() +{ +} + +/** + * Get a description of this object's content. + * @return String describing this object's content. + */ +AString +WuQMacroGroupXmlFormatBase::toString() const +{ + return "WuQMacroGroupXmlFormatBase"; +} + diff --git a/src/Common/WuQMacroGroupXmlFormatBase.h b/src/Common/WuQMacroGroupXmlFormatBase.h new file mode 100644 index 000000000..23e7c7c77 --- /dev/null +++ b/src/Common/WuQMacroGroupXmlFormatBase.h @@ -0,0 +1,122 @@ +#ifndef __WU_Q_MACRO_GROUP_XML_FORMAT_BASE_H__ +#define __WU_Q_MACRO_GROUP_XML_FORMAT_BASE_H__ + +/*LICENSE_START*/ +/* + * Copyright (C) 2018 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + + + +#include + +#include "CaretObject.h" + + + +namespace caret { + + class WuQMacroGroupXmlFormatBase : public CaretObject { + + protected: + WuQMacroGroupXmlFormatBase(); + + public: + virtual ~WuQMacroGroupXmlFormatBase(); + + WuQMacroGroupXmlFormatBase(const WuQMacroGroupXmlFormatBase&) = delete; + + WuQMacroGroupXmlFormatBase& operator=(const WuQMacroGroupXmlFormatBase&) = delete; + + + // ADD_NEW_METHODS_HERE + + virtual AString toString() const; + + protected: + + static const QString ATTRIBUTE_NAME; + static const QString ATTRIBUTE_FUNCTION_KEY; + static const QString ATTRIBUTE_OBJECT_DATA_VALUE_TYPE; + static const QString ATTRIBUTE_OBJECT_TYPE; + static const QString ATTRIBUTE_OBJECT_VALUE; + static const QString ATTRIBUTE_VERSION; + + static const QString ATTRIBUTE_MOUSE_BUTTON; + static const QString ATTRIBUTE_MOUSE_BUTTONS_MASK; + static const QString ATTRIBUTE_MOUSE_EVENT_TYPE; + static const QString ATTRIBUTE_MOUSE_KEYBOARD_MODIFIERS_MASK; + static const QString ATTRIBUTE_MOUSE_LOCAL_X; + static const QString ATTRIBUTE_MOUSE_LOCAL_Y; + static const QString ATTRIBUTE_MOUSE_SCREEN_X; + static const QString ATTRIBUTE_MOUSE_SCREEN_Y; + static const QString ATTRIBUTE_MOUSE_WIDGET_WIDTH; + static const QString ATTRIBUTE_MOUSE_WIDGET_HEIGHT; + static const QString ATTRIBUTE_MOUSE_WINDOW_X; + static const QString ATTRIBUTE_MOUSE_WINDOW_Y; + + + + static const QString ELEMENT_DESCRIPTION; + static const QString ELEMENT_MACRO; + static const QString ELEMENT_MACRO_COMMAND; + static const QString ELEMENT_MACRO_GROUP; + static const QString ELEMENT_MOUSE_EVENT_INFO; + static const QString VALUE_BOOL_FALSE; + static const QString VALUE_BOOL_TRUE; + static const QString VALUE_VERSION_ONE; + + private: + // ADD_NEW_MEMBERS_HERE + + }; + +#ifdef __WU_Q_MACRO_GROUP_XML_FORMAT_BASE_DECLARE__ + const QString WuQMacroGroupXmlFormatBase::ATTRIBUTE_NAME = "Name"; + const QString WuQMacroGroupXmlFormatBase::ATTRIBUTE_FUNCTION_KEY = "FunctionKey"; + const QString WuQMacroGroupXmlFormatBase::ATTRIBUTE_OBJECT_DATA_VALUE_TYPE = "ValueType"; + const QString WuQMacroGroupXmlFormatBase::ATTRIBUTE_OBJECT_TYPE = "ObjectType"; + const QString WuQMacroGroupXmlFormatBase::ATTRIBUTE_OBJECT_VALUE = "Value"; + const QString WuQMacroGroupXmlFormatBase::ATTRIBUTE_VERSION = "Version"; + + const QString WuQMacroGroupXmlFormatBase::ATTRIBUTE_MOUSE_BUTTON = "MouseButton"; + const QString WuQMacroGroupXmlFormatBase::ATTRIBUTE_MOUSE_BUTTONS_MASK = "MouseButtonsMask"; + const QString WuQMacroGroupXmlFormatBase::ATTRIBUTE_MOUSE_EVENT_TYPE = "MouseEventType"; + const QString WuQMacroGroupXmlFormatBase::ATTRIBUTE_MOUSE_KEYBOARD_MODIFIERS_MASK = "KeyboardModifiersMask"; + const QString WuQMacroGroupXmlFormatBase::ATTRIBUTE_MOUSE_LOCAL_X = "LocalX"; + const QString WuQMacroGroupXmlFormatBase::ATTRIBUTE_MOUSE_LOCAL_Y = "LocalY"; + const QString WuQMacroGroupXmlFormatBase::ATTRIBUTE_MOUSE_SCREEN_X = "ScreenX"; + const QString WuQMacroGroupXmlFormatBase::ATTRIBUTE_MOUSE_SCREEN_Y = "ScreenY"; + const QString WuQMacroGroupXmlFormatBase::ATTRIBUTE_MOUSE_WIDGET_WIDTH = "WidgetWidth"; + const QString WuQMacroGroupXmlFormatBase::ATTRIBUTE_MOUSE_WIDGET_HEIGHT = "WidgetHeight"; + const QString WuQMacroGroupXmlFormatBase::ATTRIBUTE_MOUSE_WINDOW_X = "WINDOW_X"; + const QString WuQMacroGroupXmlFormatBase::ATTRIBUTE_MOUSE_WINDOW_Y = "WINDOW_Y"; + + const QString WuQMacroGroupXmlFormatBase::ELEMENT_DESCRIPTION = "Description"; + const QString WuQMacroGroupXmlFormatBase::ELEMENT_MACRO = "Macro"; + const QString WuQMacroGroupXmlFormatBase::ELEMENT_MACRO_COMMAND = "MacroCommand"; + const QString WuQMacroGroupXmlFormatBase::ELEMENT_MACRO_GROUP = "MacroGroup"; + const QString WuQMacroGroupXmlFormatBase::ELEMENT_MOUSE_EVENT_INFO = "MouseEventInfo"; + + const QString WuQMacroGroupXmlFormatBase::VALUE_BOOL_FALSE = "false"; + const QString WuQMacroGroupXmlFormatBase::VALUE_BOOL_TRUE = "true"; + const QString WuQMacroGroupXmlFormatBase::VALUE_VERSION_ONE = "1"; +#endif // __WU_Q_MACRO_GROUP_XML_FORMAT_BASE_DECLARE__ + +} // namespace +#endif //__WU_Q_MACRO_GROUP_XML_FORMAT_BASE_H__ diff --git a/src/Common/WuQMacroGroupXmlReader.cxx b/src/Common/WuQMacroGroupXmlReader.cxx new file mode 100644 index 000000000..302b2e60e --- /dev/null +++ b/src/Common/WuQMacroGroupXmlReader.cxx @@ -0,0 +1,441 @@ + +/*LICENSE_START*/ +/* + * Copyright (C) 2018 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + +#define __WU_Q_MACRO_GROUP_XML_READER_DECLARE__ +#include "WuQMacroGroupXmlReader.h" +#undef __WU_Q_MACRO_GROUP_XML_READER_DECLARE__ + +#include +#include + +#include "CaretAssert.h" +#include "WuQMacro.h" +#include "WuQMacroCommand.h" +#include "WuQMacroGroup.h" +#include "WuQMacroMouseEventInfo.h" + +using namespace caret; + + + +/** + * \class caret::WuQMacroGroupXmlReader + * \brief Reads macro group from XML format + * \ingroup Common + */ + +/** + * Constructor. + */ +WuQMacroGroupXmlReader::WuQMacroGroupXmlReader() +: WuQMacroGroupXmlFormatBase() +{ +} + +/** + * Destructor. + */ +WuQMacroGroupXmlReader::~WuQMacroGroupXmlReader() +{ +} + +/** + * Read XML from the given string into the given macro group + * + * @param xmlString + * The string containing XML + * @param macroGroup + * The macro group + */ +void +WuQMacroGroupXmlReader::readFromString(const QString& xmlString, + WuQMacroGroup* macroGroup) +{ + CaretAssert(macroGroup); + macroGroup->clear(); + + if (xmlString.isEmpty()) { + m_xmlStreamReader->raiseError("String that should contain XML is empty."); + return; + } + + m_xmlStreamReader.reset(new QXmlStreamReader(xmlString)); + + if (m_xmlStreamReader->atEnd()) { + m_xmlStreamReader->raiseError("At end when trying to start reading. Appears to have no XML content."); + } + else { + if (m_xmlStreamReader->readNextStartElement()) { + const QStringRef groupElement = m_xmlStreamReader->name(); + if (groupElement == ELEMENT_MACRO_GROUP) { + const QXmlStreamAttributes attributes = m_xmlStreamReader->attributes(); + const QStringRef name = attributes.value(ATTRIBUTE_NAME); + const QStringRef versionText = attributes.value(ATTRIBUTE_VERSION); + if (versionText.isEmpty()) { + m_xmlStreamReader->raiseError(ATTRIBUTE_VERSION + + " is missing from element " + + ELEMENT_MACRO_GROUP); + } + else if (versionText == VALUE_VERSION_ONE) { + macroGroup->setName(name.toString()); + readVersionOne(macroGroup); + } + else { + m_xmlStreamReader->raiseError(ATTRIBUTE_VERSION + + "=" + + versionText.toString() + + " is not supported by " + + ELEMENT_MACRO_GROUP + + ". Check for software update."); + } + } + else { + m_xmlStreamReader->raiseError("First XML element is \"" + + m_xmlStreamReader->name().toString() + + "\" but should be \"" + + ELEMENT_MACRO_GROUP + + "\""); + } + } + else { + m_xmlStreamReader->raiseError("No XML elements found"); + } + } + + if (m_xmlStreamReader->hasError()) { + macroGroup->clear(); + } +} + +/** + * Read version one of macro group + * + * @param macroGroup + * The macro group + */ +void +WuQMacroGroupXmlReader::readVersionOne(WuQMacroGroup* macroGroup) +{ + CaretAssert(macroGroup); + + WuQMacro* macro(NULL); + + while ( ! m_xmlStreamReader->atEnd()) { + m_xmlStreamReader->readNext(); + if (m_xmlStreamReader->isStartElement()) { + const QString elementName = m_xmlStreamReader->name().toString(); + + if (elementName == ELEMENT_MACRO) { + macro = readMacroVersionOne(); + if (macro != NULL) { + macroGroup->addMacro(macro); + } + } + else if (elementName == ELEMENT_DESCRIPTION) { + const QString text = m_xmlStreamReader->readElementText(); + if (macro != NULL) { + macro->setDescription(text); + } + } + else if (elementName == ELEMENT_MACRO_COMMAND) { + WuQMacroCommand* macroCommand = readMacroCommandVersionOne(); + if (macroCommand != NULL) { + if (macro != NULL) { + macro->addMacroCommand(macroCommand); + } + else { + delete macroCommand; + m_xmlStreamReader->skipCurrentElement(); + } + } + } + else if (elementName == ELEMENT_MOUSE_EVENT_INFO) { + CaretAssert(0); + } + else { + addToWarnings("Unexpected element=" + + elementName + + "\""); + m_xmlStreamReader->skipCurrentElement(); + } + } + + if (m_xmlStreamReader->hasError()) { + break; + } + } +} + +/** + * Read version one of macro + * + * @return The macro + */ +WuQMacro* +WuQMacroGroupXmlReader::readMacroVersionOne() +{ + WuQMacro* macro(NULL); + + const QXmlStreamAttributes attributes = m_xmlStreamReader->attributes(); + QString macroName = attributes.value(ATTRIBUTE_NAME).toString(); + const QStringRef functionKey = attributes.value(ATTRIBUTE_FUNCTION_KEY); + + if (macroName.isEmpty()) { + addToWarnings(ELEMENT_MACRO + + " is missing attribute or value is empty: " + + ATTRIBUTE_NAME); + static uint32_t missingCounter = 1; + macroName = ("Missing Name_" + + QString::number(missingCounter)); + } + + macro = new WuQMacro(); + macro->setName(macroName); + macro->setFunctionKey(functionKey.toString()); + + return macro; +} + +/** + * Read version one of macro command + * + * @Return The macro group + */ +WuQMacroCommand* +WuQMacroGroupXmlReader::readMacroCommandVersionOne() +{ + WuQMacroCommand* macroCommand(NULL); + + const QXmlStreamAttributes attributes = m_xmlStreamReader->attributes(); + const QStringRef objectName = attributes.value(ATTRIBUTE_NAME); + const QStringRef dataTypeString = attributes.value(ATTRIBUTE_OBJECT_DATA_VALUE_TYPE); + const QStringRef typeString = attributes.value(ATTRIBUTE_OBJECT_TYPE); + const QStringRef valueString = attributes.value(ATTRIBUTE_OBJECT_VALUE); + + QString es; + if (objectName.isEmpty()) es.append(ATTRIBUTE_NAME + " "); + if (dataTypeString.isEmpty()) es.append(ATTRIBUTE_OBJECT_DATA_VALUE_TYPE + " "); + if (typeString.isEmpty()) es.append(ATTRIBUTE_OBJECT_TYPE + " "); + if (valueString.isEmpty()) es.append(ATTRIBUTE_OBJECT_VALUE + " "); + if ( ! es.isEmpty()) { + addToWarnings(ELEMENT_MACRO_COMMAND + + " is missing attribute(s): " + + es); + return NULL; + } + + bool dataTypeValid(false); + WuQMacroDataValueTypeEnum::Enum dataValueType = WuQMacroDataValueTypeEnum::fromName(dataTypeString.toString(), + &dataTypeValid); + if ( ! dataTypeValid) { + es.append(dataTypeString.toString() + + " is not valid for attribute " + + ATTRIBUTE_OBJECT_DATA_VALUE_TYPE + + " "); + } + + bool objectTypeValid(false); + WuQMacroObjectTypeEnum::Enum objectType = WuQMacroObjectTypeEnum::fromName(typeString.toString(), + &objectTypeValid); + if ( ! objectTypeValid) { + es.append(typeString.toString() + + " is not valid for attribute " + + ATTRIBUTE_OBJECT_TYPE + + " "); + } + + if ( ! es.isEmpty()) { + addToWarnings(ELEMENT_MACRO_COMMAND + + " has invalid attributes: " + + es); + return NULL; + } + + if (objectType == WuQMacroObjectTypeEnum::MOUSE_USER_EVENT) { + WuQMacroMouseEventInfo* mouseEventInfo = readMacroMouseEventInfo(); + if (mouseEventInfo != NULL) { + macroCommand = new WuQMacroCommand(objectName.toString(), + "", + mouseEventInfo); + } + } + else { + QVariant value; + switch (dataValueType) { + case WuQMacroDataValueTypeEnum::BOOLEAN: + { + const bool boolValue = ((valueString == VALUE_BOOL_TRUE) ? true : false); + value.setValue(boolValue); + } + break; + case WuQMacroDataValueTypeEnum::FLOAT: + { + const double floatValue = valueString.toFloat(); + value.setValue(floatValue); + } + break; + case WuQMacroDataValueTypeEnum::INTEGER: + { + const int32_t intValue = valueString.toInt(); + value.setValue(intValue); + } + break; + case WuQMacroDataValueTypeEnum::MOUSE: + CaretAssertMessage(0, "Mouse is special case handled above"); + break; + case WuQMacroDataValueTypeEnum::STRING: + value.setValue(valueString.toString()); + break; + } + macroCommand = new WuQMacroCommand(objectType, + objectName.toString(), + "", + value); + } + + return macroCommand; +} + +/** + * @return Read and return the mouse event information + */ +WuQMacroMouseEventInfo* +WuQMacroGroupXmlReader::readMacroMouseEventInfo() +{ + if ( ! m_xmlStreamReader->readNextStartElement()) { + addToWarnings("Failed to read start element for element " + + ELEMENT_MOUSE_EVENT_INFO); + return NULL; + } + + const QXmlStreamAttributes attributes = m_xmlStreamReader->attributes(); + const QString mouseEventTypeString = attributes.value(ATTRIBUTE_MOUSE_EVENT_TYPE).toString(); + const QString xLocalString = attributes.value(ATTRIBUTE_MOUSE_LOCAL_X).toString(); + const QString yLocalString = attributes.value(ATTRIBUTE_MOUSE_LOCAL_Y).toString(); + const QString mouseButtonString = attributes.value(ATTRIBUTE_MOUSE_BUTTON).toString(); + const QString mouseButtonsMaskString = attributes.value(ATTRIBUTE_MOUSE_BUTTONS_MASK).toString(); + const QString keyboardModifiersMaskString = attributes.value(ATTRIBUTE_MOUSE_KEYBOARD_MODIFIERS_MASK).toString(); + const QString widgetWidthString = attributes.value(ATTRIBUTE_MOUSE_WIDGET_WIDTH).toString(); + const QString widgetHeightString = attributes.value(ATTRIBUTE_MOUSE_WIDGET_HEIGHT).toString(); + + QString es; + if (mouseEventTypeString.isEmpty()) es.append(ATTRIBUTE_MOUSE_EVENT_TYPE + " "); + if (xLocalString.isEmpty()) es.append(ATTRIBUTE_MOUSE_LOCAL_X + " "); + if (yLocalString.isEmpty()) es.append(ATTRIBUTE_MOUSE_LOCAL_Y + " "); + if (mouseButtonString.isEmpty()) es.append(ATTRIBUTE_MOUSE_BUTTON + " "); + if (mouseButtonsMaskString.isEmpty()) es.append(ATTRIBUTE_MOUSE_BUTTONS_MASK + " "); + if (keyboardModifiersMaskString.isEmpty()) es.append(ATTRIBUTE_MOUSE_KEYBOARD_MODIFIERS_MASK + " "); + if (widgetWidthString.isEmpty()) es.append(ATTRIBUTE_MOUSE_WIDGET_WIDTH + " "); + if (widgetHeightString.isEmpty()) es.append(ATTRIBUTE_MOUSE_WIDGET_HEIGHT + " "); + if ( ! es.isEmpty()) { + addToWarnings(ELEMENT_MOUSE_EVENT_INFO + + " is missing attribute(s): " + + es); + return NULL; + } + + bool validMouseEventTypeFlag(false); + const WuQMacroMouseEventTypeEnum::Enum mouseEventType = WuQMacroMouseEventTypeEnum::fromName(mouseEventTypeString, + &validMouseEventTypeFlag); + if ( ! validMouseEventTypeFlag) { + addToWarnings(mouseEventTypeString + + " is not valid for attribute " + + ATTRIBUTE_MOUSE_EVENT_TYPE); + return NULL; + } + + WuQMacroMouseEventInfo* mouseInfo = new WuQMacroMouseEventInfo(mouseEventType, + xLocalString.toInt(), + yLocalString.toInt(), + mouseButtonString.toUInt(), + mouseButtonsMaskString.toUInt(), + keyboardModifiersMaskString.toUInt(), + widgetWidthString.toInt(), + widgetHeightString.toInt()); + return mouseInfo; +} + +void +WuQMacroGroupXmlReader::addToWarnings(const QString& warning) +{ + if ( ! m_warningMessage.isEmpty()) { + m_warningMessage.append("\n"); + } + m_warningMessage.append("Line=" + + QString::number(m_xmlStreamReader->lineNumber()) + + ", Column=" + + QString::number(m_xmlStreamReader->columnNumber()) + + ": " + + warning); +} + + +/** + * @return True if there are non-fatal warnings + */ +bool +WuQMacroGroupXmlReader::hasWarnings() const +{ + return ( ! m_warningMessage.isEmpty()); +} + +/** + * @return The warning message + */ +QString +WuQMacroGroupXmlReader::getWarningMessage() const +{ + return m_warningMessage; +} + +/** + * @return True if there is an error + */ +bool +WuQMacroGroupXmlReader::hasError() const +{ + if (m_xmlStreamReader != NULL) { + return m_xmlStreamReader->hasError(); + } + return false; +} + +/** + * @return The error message + */ +QString +WuQMacroGroupXmlReader::getErrorMessage() const +{ + QString errorMessage; + + if (m_xmlStreamReader != NULL) { + errorMessage.append("Line Number=" + + QString::number(m_xmlStreamReader->lineNumber()) + + ", Column Number=" + + QString::number(m_xmlStreamReader->columnNumber()) + + ". "); + errorMessage.append(m_xmlStreamReader->errorString()); + } + + return errorMessage; +} + + diff --git a/src/Common/WuQMacroGroupXmlReader.h b/src/Common/WuQMacroGroupXmlReader.h new file mode 100644 index 000000000..da3f81a02 --- /dev/null +++ b/src/Common/WuQMacroGroupXmlReader.h @@ -0,0 +1,88 @@ +#ifndef __WU_Q_MACRO_GROUP_XML_READER_H__ +#define __WU_Q_MACRO_GROUP_XML_READER_H__ + +/*LICENSE_START*/ +/* + * Copyright (C) 2018 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + + + +#include + +#include "WuQMacroGroupXmlFormatBase.h" + +class QXmlStreamReader; + + +namespace caret { + + class WuQMacro; + class WuQMacroCommand; + class WuQMacroGroup; + class WuQMacroMouseEventInfo; + + class WuQMacroGroupXmlReader : public WuQMacroGroupXmlFormatBase { + + public: + WuQMacroGroupXmlReader(); + + virtual ~WuQMacroGroupXmlReader(); + + WuQMacroGroupXmlReader(const WuQMacroGroupXmlReader&) = delete; + + WuQMacroGroupXmlReader& operator=(const WuQMacroGroupXmlReader&) = delete; + + bool hasError() const; + + QString getErrorMessage() const; + + bool hasWarnings() const; + + QString getWarningMessage() const; + + void readFromString(const QString& xmlString, + WuQMacroGroup* macroGroup); + + // ADD_NEW_METHODS_HERE + + private: + WuQMacroMouseEventInfo* readMacroMouseEventInfo(); + + void readVersionOne(WuQMacroGroup* macroGroup); + + WuQMacro* readMacroVersionOne(); + + WuQMacroCommand* readMacroCommandVersionOne(); + + void addToWarnings(const QString& warning); + + std::unique_ptr m_xmlStreamReader; + + QString m_warningMessage; + + // ADD_NEW_MEMBERS_HERE + + }; + +#ifdef __WU_Q_MACRO_GROUP_XML_READER_DECLARE__ + // +#endif // __WU_Q_MACRO_GROUP_XML_READER_DECLARE__ + +} // namespace +#endif //__WU_Q_MACRO_GROUP_XML_READER_H__ diff --git a/src/Common/WuQMacroGroupXmlWriter.cxx b/src/Common/WuQMacroGroupXmlWriter.cxx new file mode 100644 index 000000000..ef969660a --- /dev/null +++ b/src/Common/WuQMacroGroupXmlWriter.cxx @@ -0,0 +1,225 @@ + +/*LICENSE_START*/ +/* + * Copyright (C) 2018 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + +#define __WU_Q_MACRO_GROUP_XML_WRITER_DECLARE__ +#include "WuQMacroGroupXmlWriter.h" +#undef __WU_Q_MACRO_GROUP_XML_WRITER_DECLARE__ + +#include + +#include "CaretAssert.h" +#include "WuQMacro.h" +#include "WuQMacroCommand.h" +#include "WuQMacroGroup.h" +#include "WuQMacroMouseEventInfo.h" + +using namespace caret; + + + +/** + * \class caret::WuQMacroGroupXmlWriter + * \brief Writes contents of macro group to XML format + * \ingroup Common + */ + +/** + * Constructor + */ +WuQMacroGroupXmlWriter::WuQMacroGroupXmlWriter() +: WuQMacroGroupXmlFormatBase() +{ +} + +/** + * Destructor. + */ +WuQMacroGroupXmlWriter::~WuQMacroGroupXmlWriter() +{ +} + +/** + * Constructor for writing the macro group to a text string + * + * @param macroGroup + * Macro group that is written to XML + * @param contentTextString + * Pointer to string to which XML is written + */ +void +WuQMacroGroupXmlWriter::writeToString(const WuQMacroGroup* macroGroup, + QString& contentTextString) +{ + CaretAssert(macroGroup); + contentTextString.clear(); + + m_xmlStreamWriter.reset(new QXmlStreamWriter(&contentTextString)); + m_xmlStreamWriter->setAutoFormatting(true); + m_xmlStreamWriter->writeStartElement(ELEMENT_MACRO_GROUP); + m_xmlStreamWriter->writeAttribute(ATTRIBUTE_NAME, macroGroup->getName()); + m_xmlStreamWriter->writeAttribute(ATTRIBUTE_VERSION, "1"); + + const int32_t numMacros = macroGroup->getNumberOfMacros(); + for (int32_t i = 0; i < numMacros; i++) { + const WuQMacro* macro = macroGroup->getMacroAtIndex(i); + CaretAssert(macro); + writeMacroToXML(macro); + } + m_xmlStreamWriter->writeEndElement(); + + m_xmlStreamWriter.reset(); +} + + +/** + * Write a macro to XML format + * + * @param macro + * The macro + */ +void +WuQMacroGroupXmlWriter::writeMacroToXML(const WuQMacro* macro) +{ + CaretAssert(macro); + + m_xmlStreamWriter->writeStartElement(ELEMENT_MACRO); + m_xmlStreamWriter->writeAttribute(ATTRIBUTE_NAME, + macro->getName()); + m_xmlStreamWriter->writeAttribute(ATTRIBUTE_FUNCTION_KEY, + macro->getFunctionKey()); + m_xmlStreamWriter->writeTextElement(ELEMENT_DESCRIPTION, + macro->getDescription()); + + const int32_t numCommands = macro->getNumberOfMacroCommands(); + for (int32_t i = 0; i < numCommands; i++) { + const WuQMacroCommand* macroCommand = macro->getMacroCommandAtIndex(i); + CaretAssert(macroCommand); + writeMacroCommandToXML(macroCommand); + } + + m_xmlStreamWriter->writeEndElement(); +} + +/** + * Write a macro command to XML format + * + * @param macroCommand + * The macro command + */ +void +WuQMacroGroupXmlWriter::writeMacroCommandToXML(const WuQMacroCommand* macroCommand) +{ + CaretAssert(macroCommand); + + m_xmlStreamWriter->writeStartElement(ELEMENT_MACRO_COMMAND); + m_xmlStreamWriter->writeAttribute(ATTRIBUTE_NAME, + macroCommand->getObjectName()); + m_xmlStreamWriter->writeAttribute(ATTRIBUTE_OBJECT_DATA_VALUE_TYPE, + WuQMacroDataValueTypeEnum::toName(macroCommand->getObjectDataValueType())); + m_xmlStreamWriter->writeAttribute(ATTRIBUTE_OBJECT_TYPE, + WuQMacroObjectTypeEnum::toName(macroCommand->getObjectType())); + const QVariant objectValue(macroCommand->getObjectValue()); + QString stringValue; + switch (macroCommand->getObjectDataValueType()) { + case WuQMacroDataValueTypeEnum::BOOLEAN: + stringValue = (objectValue.toBool() ? VALUE_BOOL_TRUE : VALUE_BOOL_FALSE); + break; + case WuQMacroDataValueTypeEnum::FLOAT: + stringValue = QString::number(objectValue.toFloat()); + break; + case WuQMacroDataValueTypeEnum::INTEGER: + stringValue = QString::number(objectValue.toInt()); + break; + case WuQMacroDataValueTypeEnum::MOUSE: + stringValue = "MouseEvent"; + break; + case WuQMacroDataValueTypeEnum::STRING: + stringValue = objectValue.toString(); + break; + } + m_xmlStreamWriter->writeAttribute(ATTRIBUTE_OBJECT_VALUE, + stringValue); + + + switch (macroCommand->getObjectType()) { + case WuQMacroObjectTypeEnum::ACTION: + break; + case WuQMacroObjectTypeEnum::CHECK_BOX: + break; + case WuQMacroObjectTypeEnum::COMBO_BOX: + break; + case WuQMacroObjectTypeEnum::DOUBLE_SPIN_BOX: + break; + case WuQMacroObjectTypeEnum::INVALID: + break; + case WuQMacroObjectTypeEnum::LINE_EDIT: + break; + case WuQMacroObjectTypeEnum::LIST_WIDGET: + break; + case WuQMacroObjectTypeEnum::MENU: + break; + case WuQMacroObjectTypeEnum::MOUSE_USER_EVENT: + writeMacroMouseEventInfo(macroCommand->getMouseEventInfo()); + break; + case WuQMacroObjectTypeEnum::PUSH_BUTTON: + break; + case WuQMacroObjectTypeEnum::RADIO_BUTTON: + break; + case WuQMacroObjectTypeEnum::SLIDER: + break; + case WuQMacroObjectTypeEnum::SPIN_BOX: + break; + case WuQMacroObjectTypeEnum::TAB_BAR: + break; + case WuQMacroObjectTypeEnum::TAB_WIDGET: + break; + case WuQMacroObjectTypeEnum::TOOL_BUTTON: + break; + } + + m_xmlStreamWriter->writeEndElement(); +} + +/** + * Write a macro mouse event info to XML format + * + * @param mouseEventInfo + * The macro mouse event info + */ +void +WuQMacroGroupXmlWriter::writeMacroMouseEventInfo(const WuQMacroMouseEventInfo* mouseEventInfo) +{ + CaretAssert(mouseEventInfo); + + m_xmlStreamWriter->writeStartElement(ELEMENT_MOUSE_EVENT_INFO); + + m_xmlStreamWriter->writeAttribute(ATTRIBUTE_MOUSE_BUTTON, QString::number(mouseEventInfo->getMouseButton())); + m_xmlStreamWriter->writeAttribute(ATTRIBUTE_MOUSE_BUTTONS_MASK, QString::number(mouseEventInfo->getMouseButtonsMask())); + m_xmlStreamWriter->writeAttribute(ATTRIBUTE_MOUSE_EVENT_TYPE, WuQMacroMouseEventTypeEnum::toName(mouseEventInfo->getMouseEventType())); + m_xmlStreamWriter->writeAttribute(ATTRIBUTE_MOUSE_KEYBOARD_MODIFIERS_MASK, QString::number(mouseEventInfo->getKeyboardModifiersMask())); + m_xmlStreamWriter->writeAttribute(ATTRIBUTE_MOUSE_LOCAL_X, QString::number(mouseEventInfo->getLocalX())); + m_xmlStreamWriter->writeAttribute(ATTRIBUTE_MOUSE_LOCAL_Y, QString::number(mouseEventInfo->getLocalY())); + m_xmlStreamWriter->writeAttribute(ATTRIBUTE_MOUSE_WIDGET_WIDTH, QString::number(mouseEventInfo->getWidgetWidth())); + m_xmlStreamWriter->writeAttribute(ATTRIBUTE_MOUSE_WIDGET_HEIGHT, QString::number(mouseEventInfo->getWidgetHeight())); + + m_xmlStreamWriter->writeEndElement(); +} + diff --git a/src/Common/WuQMacroGroupXmlWriter.h b/src/Common/WuQMacroGroupXmlWriter.h new file mode 100644 index 000000000..266f73a09 --- /dev/null +++ b/src/Common/WuQMacroGroupXmlWriter.h @@ -0,0 +1,74 @@ +#ifndef __WU_Q_MACRO_GROUP_XML_WRITER_H__ +#define __WU_Q_MACRO_GROUP_XML_WRITER_H__ + +/*LICENSE_START*/ +/* + * Copyright (C) 2018 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + + + +#include + +#include "WuQMacroGroupXmlFormatBase.h" + +class QXmlStreamWriter; + +namespace caret { + + class WuQMacro; + class WuQMacroCommand; + class WuQMacroGroup; + class WuQMacroMouseEventInfo; + + class WuQMacroGroupXmlWriter : public WuQMacroGroupXmlFormatBase { + + public: + WuQMacroGroupXmlWriter(); + + virtual ~WuQMacroGroupXmlWriter(); + + void writeToString(const WuQMacroGroup* macroGroup, + QString& contentTextString); + + WuQMacroGroupXmlWriter(const WuQMacroGroupXmlWriter&) = delete; + + WuQMacroGroupXmlWriter& operator=(const WuQMacroGroupXmlWriter&) = delete; + + + // ADD_NEW_METHODS_HERE + + private: + void writeMacroToXML(const WuQMacro* macro); + + void writeMacroCommandToXML(const WuQMacroCommand* macroCommand); + + void writeMacroMouseEventInfo(const WuQMacroMouseEventInfo* mouseEventInfo); + + std::unique_ptr m_xmlStreamWriter; + + // ADD_NEW_MEMBERS_HERE + + }; + +#ifdef __WU_Q_MACRO_GROUP_XML_WRITER_DECLARE__ + // +#endif // __WU_Q_MACRO_GROUP_XML_WRITER_DECLARE__ + +} // namespace +#endif //__WU_Q_MACRO_GROUP_XML_WRITER_H__ diff --git a/src/Common/WuQMacroMouseEventInfo.cxx b/src/Common/WuQMacroMouseEventInfo.cxx index d89d0a86b..a98981ce7 100644 --- a/src/Common/WuQMacroMouseEventInfo.cxx +++ b/src/Common/WuQMacroMouseEventInfo.cxx @@ -42,14 +42,6 @@ using namespace caret; * Position of mouse relative to widget * @param localY * Position of mouse relative to widget - * @param windowX - * Position of mouse relative to window - * @param windowY - * Position of mouse relative to window - * @param screenX - * Position of mouse relative to screen - * @param screenY - * Position of mouse relative to screen * @param mouseButton * Button that caused the event * @param mouseButtonsMask @@ -64,10 +56,6 @@ using namespace caret; WuQMacroMouseEventInfo::WuQMacroMouseEventInfo(const WuQMacroMouseEventTypeEnum::Enum mouseEventType, const int32_t localX, const int32_t localY, - const int32_t windowX, - const int32_t windowY, - const int32_t screenX, - const int32_t screenY, const uint32_t mouseButton, const uint32_t mouseButtonsMask, const uint32_t keyboardModifiersMask, @@ -77,10 +65,6 @@ WuQMacroMouseEventInfo::WuQMacroMouseEventInfo(const WuQMacroMouseEventTypeEnum: m_mouseEventType(mouseEventType), m_localX(localX), m_localY(localY), -m_windowX(windowX), -m_windowY(windowY), -m_screenX(screenX), -m_screenY(screenY), m_mouseButton(mouseButton), m_mouseButtonsMask(mouseButtonsMask), m_keyboardModifiersMask(keyboardModifiersMask), @@ -97,6 +81,52 @@ WuQMacroMouseEventInfo::~WuQMacroMouseEventInfo() { } +/** + * Copy constructor. + * @param obj + * Object that is copied. + */ +WuQMacroMouseEventInfo::WuQMacroMouseEventInfo(const WuQMacroMouseEventInfo& obj) +: CaretObject(obj) +{ + this->copyHelperWuQMacroMouseEventInfo(obj); +} + +/** + * Assignment operator. + * @param obj + * Data copied from obj to this. + * @return + * Reference to this object. + */ +WuQMacroMouseEventInfo& +WuQMacroMouseEventInfo::operator=(const WuQMacroMouseEventInfo& obj) +{ + if (this != &obj) { + CaretObject::operator=(obj); + this->copyHelperWuQMacroMouseEventInfo(obj); + } + return *this; +} + +/** + * Helps with copying an object of this type. + * @param obj + * Object that is copied. + */ +void +WuQMacroMouseEventInfo::copyHelperWuQMacroMouseEventInfo(const WuQMacroMouseEventInfo& obj) +{ + m_mouseEventType = obj.m_mouseEventType; + m_localX = obj.m_localX; + m_localY = obj.m_localY; + m_mouseButton = obj.m_mouseButton; + m_mouseButtonsMask = obj.m_mouseButtonsMask; + m_keyboardModifiersMask = obj.m_keyboardModifiersMask; + m_widgetWidth = obj.m_widgetWidth; + m_widgetHeight = obj.m_widgetHeight; +} + /** * Widget size may change so scale the x and y position to fit new widget size * @@ -157,42 +187,6 @@ WuQMacroMouseEventInfo::getLocalY() const return m_localY; } -/** - * @return Position of mouse relative to window - */ -int32_t -WuQMacroMouseEventInfo::getWindowX() const -{ - return m_windowX; -} - -/** - * @return Position of mouse relative to window - */ -int32_t -WuQMacroMouseEventInfo::getWindowY() const -{ - return m_windowY; -} - -/** - * @return Position of mouse relative to screen - */ -int32_t -WuQMacroMouseEventInfo::getScreenX() const -{ - return m_screenX; -} - -/** - * @return Position of mouse relative to screen - */ -int32_t -WuQMacroMouseEventInfo::getScreenY() const -{ - return m_screenY; -} - /** * @return Button that caused the event */ diff --git a/src/Common/WuQMacroMouseEventInfo.h b/src/Common/WuQMacroMouseEventInfo.h index 588ab91dd..9d9026e8b 100644 --- a/src/Common/WuQMacroMouseEventInfo.h +++ b/src/Common/WuQMacroMouseEventInfo.h @@ -37,10 +37,6 @@ namespace caret { WuQMacroMouseEventInfo(const WuQMacroMouseEventTypeEnum::Enum mouseEventType, const int32_t localX, const int32_t localY, - const int32_t windowX, - const int32_t windowY, - const int32_t screenX, - const int32_t screenY, const uint32_t mouseButton, const uint32_t mouseButtonsMask, const uint32_t keyboardModifiersMask, @@ -49,9 +45,9 @@ namespace caret { virtual ~WuQMacroMouseEventInfo(); - WuQMacroMouseEventInfo(const WuQMacroMouseEventInfo&) = delete; + WuQMacroMouseEventInfo(const WuQMacroMouseEventInfo&); - WuQMacroMouseEventInfo& operator=(const WuQMacroMouseEventInfo&) = delete; + WuQMacroMouseEventInfo& operator=(const WuQMacroMouseEventInfo&); void getLocalPositionRescaledToWidgetSize(const int32_t widgetWidth, const int32_t widgetHeight, @@ -64,14 +60,6 @@ namespace caret { int32_t getLocalY() const; - int32_t getWindowX() const; - - int32_t getWindowY() const; - - int32_t getScreenX() const; - - int32_t getScreenY() const; - uint32_t getMouseButton() const; uint32_t getMouseButtonsMask() const; @@ -87,41 +75,31 @@ namespace caret { virtual AString toString() const; private: + void copyHelperWuQMacroMouseEventInfo(const WuQMacroMouseEventInfo& obj); + /** Type of mouse event */ - const WuQMacroMouseEventTypeEnum::Enum m_mouseEventType; + WuQMacroMouseEventTypeEnum::Enum m_mouseEventType; /** Position of mouse relative to widget */ - const int32_t m_localX; + int32_t m_localX; /** Position of mouse relative to widget */ - const int32_t m_localY; - - /** Position of mouse relative to window */ - const int32_t m_windowX; - - /** Position of mouse relative to window */ - const int32_t m_windowY; - - /** Position of mouse relative to screen */ - const int32_t m_screenX; - - /** Position of mouse relative to screen */ - const int32_t m_screenY; + int32_t m_localY; /** Button that caused the event */ - const uint32_t m_mouseButton; + uint32_t m_mouseButton; /** Mask with buttons down during mouse event */ - const uint32_t m_mouseButtonsMask; + uint32_t m_mouseButtonsMask; /** Mask with any keys down during mouse event */ - const uint32_t m_keyboardModifiersMask; + uint32_t m_keyboardModifiersMask; /** Width of widget where mouse event occurred */ - const int32_t m_widgetWidth; + int32_t m_widgetWidth; /** Width of widget where mouse event occurred */ - const int32_t m_widgetHeight; + int32_t m_widgetHeight; // ADD_NEW_MEMBERS_HERE diff --git a/src/GuiQt/CMakeLists.txt b/src/GuiQt/CMakeLists.txt index d59f0e73c..3f98a4c43 100644 --- a/src/GuiQt/CMakeLists.txt +++ b/src/GuiQt/CMakeLists.txt @@ -203,6 +203,7 @@ ELSE() WuQGroupBoxExclusiveWidget.h WuQImageLabel.h WuQListWidget.h + WuQMacroAttributesDialog.h WuQMacroCreateDialog.h WuQMacroDialog.h WuQMacroExecutor.h @@ -457,6 +458,7 @@ WuQGridLayoutGroup.h WuQGroupBoxExclusiveWidget.h WuQImageLabel.h WuQListWidget.h +WuQMacroAttributesDialog.h WuQMacroCreateDialog.h WuQMacroDialog.h WuQMacroExecutor.h @@ -698,6 +700,7 @@ WuQGridLayoutGroup.cxx WuQGroupBoxExclusiveWidget.cxx WuQImageLabel.cxx WuQListWidget.cxx +WuQMacroAttributesDialog.cxx WuQMacroCreateDialog.cxx WuQMacroDialog.cxx WuQMacroExecutor.cxx diff --git a/src/GuiQt/WuQMacroAttributesDialog.cxx b/src/GuiQt/WuQMacroAttributesDialog.cxx new file mode 100644 index 000000000..f6007cb86 --- /dev/null +++ b/src/GuiQt/WuQMacroAttributesDialog.cxx @@ -0,0 +1,164 @@ + +/*LICENSE_START*/ +/* + * Copyright (C) 2018 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + +#define __WU_Q_MACRO_ATTRIBUTES_DIALOG_DECLARE__ +#include "WuQMacroAttributesDialog.h" +#undef __WU_Q_MACRO_ATTRIBUTES_DIALOG_DECLARE__ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "CaretAssert.h" +#include "CaretLogger.h" +#include "WuQMacro.h" +#include "WuQMacroGroup.h" +#include "WuQMacroManager.h" + +using namespace caret; + + + +/** + * \class caret::WuQMacroAttributesDialog + * \brief Dialog for creating a new macro + * \ingroup WuQMacro + */ + +/** + * Constructor. + * + * @param macro + * The macro being edited + * @param parent + * The parent widget + */ +WuQMacroAttributesDialog::WuQMacroAttributesDialog(WuQMacro* macro, + QWidget* parent) +: QDialog(parent), +m_macro(macro), +m_macroWasModifiedFlag(false) +{ + CaretAssert(m_macro); + + setWindowTitle("Macro Attributes"); + + QLabel* nameLabel = new QLabel("Macro name:"); + QLabel* functionKeyLabel = new QLabel("Function Key:"); + QLabel* descriptionLabel = new QLabel("Description:"); + + m_macroNameLineEdit = new QLineEdit(); + m_macroNameLineEdit->setText(m_macro->getName()); + m_macroFunctionKeyLineEdit = new QLineEdit(); + m_macroFunctionKeyLineEdit->setFixedWidth(40); + m_macroFunctionKeyLineEdit->setText(m_macro->getFunctionKey()); + m_macroDescriptionTextEdit = new QPlainTextEdit(); + m_macroDescriptionTextEdit->setFixedHeight(100); + m_macroDescriptionTextEdit->setPlainText(m_macro->getDescription()); + + QGridLayout* gridLayout = new QGridLayout(); + int row = 0; + gridLayout->addWidget(nameLabel, row, 0); + gridLayout->addWidget(m_macroNameLineEdit, row, 1); + row++; + gridLayout->addWidget(functionKeyLabel, row, 0); + gridLayout->addWidget(m_macroFunctionKeyLineEdit, row, 1); + row++; + gridLayout->addWidget(descriptionLabel, row, 0); + gridLayout->addWidget(m_macroDescriptionTextEdit, row, 1); + row++; + + m_dialogButtonBox = new QDialogButtonBox(QDialogButtonBox::Ok + | QDialogButtonBox::Cancel); + QObject::connect(m_dialogButtonBox, &QDialogButtonBox::accepted, + this, &WuQMacroAttributesDialog::accept); + QObject::connect(m_dialogButtonBox, &QDialogButtonBox::rejected, + this, &WuQMacroAttributesDialog::reject); + + QVBoxLayout* dialogLayout = new QVBoxLayout(this); + dialogLayout->addLayout(gridLayout); + dialogLayout->addWidget(m_dialogButtonBox); +} + +/** + * Destructor. + */ +WuQMacroAttributesDialog::~WuQMacroAttributesDialog() +{ +} + +/** + * Called when user clicks OK or Cancel + * + * @param r + * The dialog code (Accepted or Rejected) + */ +void +WuQMacroAttributesDialog::done(int r) +{ + if (r == QDialog::Accepted) { + const QString name(m_macroNameLineEdit->text().trimmed()); + if (name.isEmpty()) { + QMessageBox::critical(this, + "Error", + "Name is missing", + QMessageBox::Ok, + QMessageBox::Ok); + return; + } + + const QString newName = m_macroNameLineEdit->text().trimmed(); + if (m_macro->getName() != newName) { + m_macro->setName(newName); + m_macroWasModifiedFlag = true; + } + + const QString newFunctionKey = m_macroFunctionKeyLineEdit->text().trimmed(); + if (m_macro->getFunctionKey() != newFunctionKey) { + m_macro->setFunctionKey(newFunctionKey); + m_macroWasModifiedFlag = true; + } + + const QString newDescription = m_macroDescriptionTextEdit->toPlainText().trimmed(); + if (m_macro->getDescription() != newDescription) { + m_macro->setDescription(newDescription); + m_macroWasModifiedFlag = true; + } + } + + QDialog::done(r); +} + +/** + * @return True if the macro was modified and may need to be saved, + * else false + */ +bool +WuQMacroAttributesDialog::isMacroModified() const +{ + return m_macroWasModifiedFlag; +} + diff --git a/src/GuiQt/WuQMacroAttributesDialog.h b/src/GuiQt/WuQMacroAttributesDialog.h new file mode 100644 index 000000000..ae09d05e7 --- /dev/null +++ b/src/GuiQt/WuQMacroAttributesDialog.h @@ -0,0 +1,81 @@ +#ifndef __WU_Q_MACRO_ATTRIBUTES_DIALOG_H__ +#define __WU_Q_MACRO_ATTRIBUTES_DIALOG_H__ + +/*LICENSE_START*/ +/* + * Copyright (C) 2018 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + + + +#include + +#include + +class QDialogButtonBox; +class QLineEdit; +class QPlainTextEdit; + + +namespace caret { + + class WuQMacro; + class WuQMacroGroup; + + class WuQMacroAttributesDialog : public QDialog { + + Q_OBJECT + + public: + WuQMacroAttributesDialog(WuQMacro* macro, + QWidget* parent = 0); + + virtual ~WuQMacroAttributesDialog(); + + WuQMacroAttributesDialog(const WuQMacroAttributesDialog&) = delete; + + WuQMacroAttributesDialog& operator=(const WuQMacroAttributesDialog&) = delete; + + bool isMacroModified() const; + + // ADD_NEW_METHODS_HERE + + public slots: + virtual void done(int r) override; + + private: + WuQMacro* m_macro; + + bool m_macroWasModifiedFlag = false; + + QLineEdit* m_macroNameLineEdit; + + QLineEdit* m_macroFunctionKeyLineEdit; + + QPlainTextEdit* m_macroDescriptionTextEdit; + + QDialogButtonBox* m_dialogButtonBox; + + }; + +#ifdef __WU_Q_MACRO_ATTRIBUTES_DIALOG_DECLARE__ + // +#endif // __WU_Q_MACRO_ATTRIBUTES_DIALOG_DECLARE__ + +} // namespace +#endif //__WU_Q_MACRO_ATTRIBUTES_DIALOG_H__ diff --git a/src/GuiQt/WuQMacroCreateDialog.cxx b/src/GuiQt/WuQMacroCreateDialog.cxx index de45eece5..d5fa932e3 100644 --- a/src/GuiQt/WuQMacroCreateDialog.cxx +++ b/src/GuiQt/WuQMacroCreateDialog.cxx @@ -62,16 +62,20 @@ WuQMacroCreateDialog::WuQMacroCreateDialog(QWidget* parent) m_macroGroups = WuQMacroManager::instance()->getMacroGroups(); QLabel* nameLabel = new QLabel("Macro name:"); + QLabel* functionKeyLabel = new QLabel("Function Key"); QLabel* descriptionLabel = new QLabel("Description:"); QLabel* macroGroupLabel = new QLabel("Store macro in:"); m_macroNameLineEdit = new QLineEdit(); m_macroNameLineEdit->setText(getDefaultMacroName()); + m_macroFunctionKeyLineEdit = new QLineEdit(); + m_macroFunctionKeyLineEdit->setFixedWidth(40); m_macroDescriptionTextEdit = new QPlainTextEdit(); + m_macroDescriptionTextEdit->setFixedHeight(100); m_macroGroupComboBox = new QComboBox(); for (auto mg : m_macroGroups) { - m_macroGroupComboBox->addItem(mg->getGroupName()); + m_macroGroupComboBox->addItem(mg->getName()); } QGridLayout* gridLayout = new QGridLayout(); @@ -79,6 +83,9 @@ WuQMacroCreateDialog::WuQMacroCreateDialog(QWidget* parent) gridLayout->addWidget(nameLabel, row, 0); gridLayout->addWidget(m_macroNameLineEdit, row, 1); row++; + gridLayout->addWidget(functionKeyLabel, row, 0); + gridLayout->addWidget(m_macroFunctionKeyLineEdit, row, 1); + row++; gridLayout->addWidget(descriptionLabel, row, 0); gridLayout->addWidget(m_macroDescriptionTextEdit, row, 1); row++; @@ -161,6 +168,7 @@ WuQMacroCreateDialog::done(int r) m_macro = new WuQMacro(); m_macro->setName(name); + m_macro->setFunctionKey(m_macroFunctionKeyLineEdit->text().trimmed()); m_macro->setDescription(m_macroDescriptionTextEdit->toPlainText()); const int32_t groupIndex = m_macroGroupComboBox->currentIndex(); diff --git a/src/GuiQt/WuQMacroCreateDialog.h b/src/GuiQt/WuQMacroCreateDialog.h index 648641bc5..969346df6 100644 --- a/src/GuiQt/WuQMacroCreateDialog.h +++ b/src/GuiQt/WuQMacroCreateDialog.h @@ -67,6 +67,8 @@ namespace caret { QLineEdit* m_macroNameLineEdit; + QLineEdit* m_macroFunctionKeyLineEdit; + QPlainTextEdit* m_macroDescriptionTextEdit; QDialogButtonBox* m_dialogButtonBox; diff --git a/src/GuiQt/WuQMacroDialog.cxx b/src/GuiQt/WuQMacroDialog.cxx index 3a6542b32..558d39ed9 100644 --- a/src/GuiQt/WuQMacroDialog.cxx +++ b/src/GuiQt/WuQMacroDialog.cxx @@ -23,11 +23,14 @@ #include "WuQMacroDialog.h" #undef __WU_Q_MACRO_DIALOG_DECLARE__ +#include #include #include +#include #include #include #include +#include #include #include #include @@ -36,6 +39,7 @@ #include "CaretLogger.h" #include "WuQMacro.h" #include "WuQMacroGroup.h" +#include "WuQMacroExecutor.h" #include "WuQMacroManager.h" using namespace caret; @@ -62,42 +66,105 @@ WuQMacroDialog::WuQMacroDialog(QWidget* parent) m_macroGroups = WuQMacroManager::instance()->getMacroGroups(); - QLabel* nameLabel = new QLabel("Macros:"); + QLabel* macrosLabel = new QLabel("Macro:"); QLabel* descriptionLabel = new QLabel("Description:"); QLabel* macroGroupLabel = new QLabel("Macros in:"); + m_macroGroupComboBox = new QComboBox(); + QObject::connect(m_macroGroupComboBox, static_cast(&QComboBox::activated), + this, &WuQMacroDialog::macroGroupBoxActivated); + m_macrosListWidget = new QListWidget(); QObject::connect(m_macrosListWidget, &QListWidget::currentRowChanged, this, &WuQMacroDialog::macrosListWidgetCurrentRowChanged); - m_macroDescriptionTextEdit = new QPlainTextEdit(); + m_macroDescriptionLabel = new QLabel(""); + m_macroDescriptionLabel->setWordWrap(true); + m_macroDescriptionLabel->setMinimumHeight(40); + m_macroDescriptionLabel->setAlignment(Qt::AlignTop + | Qt::AlignLeft); - m_macroGroupComboBox = new QComboBox(); - QObject::connect(m_macroGroupComboBox, static_cast(&QComboBox::activated), - this, &WuQMacroDialog::macroGroupBoxActivated); + m_attributesPushButton = new QPushButton("Attributes..."); + m_attributesPushButton->setToolTip("Edit the selected macro's name, description, etc"); + QObject::connect(m_attributesPushButton, &QPushButton::clicked, + this, &WuQMacroDialog::attributesButtonClicked); + + m_deletePushButton = new QPushButton("Delete..."); + m_deletePushButton->setToolTip("Delete the selected macro"); + QObject::connect(m_deletePushButton, &QPushButton::clicked, + this, &WuQMacroDialog::deleteButtonClicked); + + m_editPushButton = new QPushButton("Edit..."); + m_editPushButton->setToolTip("Edit the steps (commands) in the selected macro"); + QObject::connect(m_editPushButton, &QPushButton::clicked, + this, &WuQMacroDialog::editButtonClicked); + QVBoxLayout* macroButtonsLayout = new QVBoxLayout(); + macroButtonsLayout->addWidget(m_attributesPushButton); + macroButtonsLayout->addWidget(m_editPushButton); + macroButtonsLayout->addSpacing(20); + macroButtonsLayout->addWidget(m_deletePushButton); + macroButtonsLayout->addStretch(); + + QLabel* runOptionsLabel = new QLabel("Run Macro Options: "); + m_runOptionLoopCheckBox = new QCheckBox("Loop"); + m_runOptionLoopCheckBox->setChecked(false); + m_runOptionLoopCheckBox->setEnabled(false); + m_runOptionMoveMouseCheckBox = new QCheckBox("Move mouse to to highlight controls"); + m_runOptionMoveMouseCheckBox->setChecked(true); + m_runOptionMoveMouseCheckBox->setToolTip("As macro runs, the mouse is moved to\n" + "highlight user-interface controls"); + QLabel* runOptionsDelayLabel = new QLabel("Delay (seconds) between commands"); + m_runOptionDelayBetweenCommandsSpinBox = new QDoubleSpinBox(); + m_runOptionDelayBetweenCommandsSpinBox->setMinimum(0.0); + m_runOptionDelayBetweenCommandsSpinBox->setMaximum(1000.0); + m_runOptionDelayBetweenCommandsSpinBox->setSingleStep(0.1); + m_runOptionDelayBetweenCommandsSpinBox->setDecimals(1); + m_runOptionDelayBetweenCommandsSpinBox->setValue(1.0); + m_runOptionDelayBetweenCommandsSpinBox->setToolTip("Pause for this amount of time"); + m_runOptionDelayBetweenCommandsSpinBox->setFixedWidth(80); + QGridLayout* runOptionsLayout = new QGridLayout(); + runOptionsLayout->setColumnMinimumWidth(0, 10); + runOptionsLayout->addWidget(runOptionsLabel, 0, 0, 1, 3); + runOptionsLayout->addWidget(m_runOptionLoopCheckBox, 1, 1, 1, 2); + runOptionsLayout->addWidget(m_runOptionMoveMouseCheckBox, 2, 1, 1, 2); + runOptionsLayout->addWidget(m_runOptionDelayBetweenCommandsSpinBox, 3, 1); + runOptionsLayout->addWidget(runOptionsDelayLabel, 3, 2); + + QFrame* horizontalLine = new QFrame(); + horizontalLine->setMidLineWidth(1); + horizontalLine->setLineWidth(1); + horizontalLine->setFrameStyle(QFrame::HLine | QFrame::Sunken); + QGridLayout* gridLayout = new QGridLayout(); int row = 0; gridLayout->addWidget(macroGroupLabel, row, 0); gridLayout->addWidget(m_macroGroupComboBox, row, 1); row++; - gridLayout->addWidget(nameLabel, row, 0); - gridLayout->addWidget(m_macrosListWidget, row, 1); + gridLayout->addWidget(macrosLabel, row, 0, (Qt::AlignTop + | Qt::AlignLeft)); + gridLayout->addWidget(m_macrosListWidget, row, 1, 2, 1); row++; - gridLayout->addWidget(descriptionLabel, row, 0); - gridLayout->addWidget(m_macroDescriptionTextEdit, row, 1); + gridLayout->addLayout(macroButtonsLayout, row, 0); row++; + for (int32_t iRow = 0; iRow < gridLayout->rowCount(); iRow++) { + gridLayout->setRowStretch(iRow, 0); + } + m_dialogButtonBox = new QDialogButtonBox(QDialogButtonBox::Close); - m_editButton = m_dialogButtonBox->addButton("Edit...", QDialogButtonBox::ApplyRole); m_runButton = m_dialogButtonBox->addButton("Run...", QDialogButtonBox::ApplyRole); QObject::connect(m_dialogButtonBox, &QDialogButtonBox::rejected, this, &WuQMacroDialog::close); QObject::connect(m_dialogButtonBox, &QDialogButtonBox::clicked, - this, &WuQMacroDialog::buttonClicked); + this, &WuQMacroDialog::buttonBoxButtonClicked); QVBoxLayout* dialogLayout = new QVBoxLayout(this); dialogLayout->addLayout(gridLayout); + dialogLayout->addWidget(descriptionLabel); + dialogLayout->addWidget(m_macroDescriptionLabel); + dialogLayout->addWidget(horizontalLine); + dialogLayout->addLayout(runOptionsLayout); dialogLayout->addWidget(m_dialogButtonBox); updateDialogContents(); @@ -121,12 +188,9 @@ WuQMacroDialog::~WuQMacroDialog() * Button that was clicked. */ void -WuQMacroDialog::buttonClicked(QAbstractButton* button) +WuQMacroDialog::buttonBoxButtonClicked(QAbstractButton* button) { - if (button == m_editButton) { - editSelectedMacro(); - } - else if (button == m_runButton) { + if (button == m_runButton) { runSelectedMacro(); } } @@ -145,8 +209,8 @@ WuQMacroDialog::updateDialogContents() int32_t groupCounter(0); m_macroGroupComboBox->clear(); for (auto mg : m_macroGroups) { - m_macroGroupComboBox->addItem(mg->getGroupName()); - if (selectedGroupName == mg->getGroupName()) { + m_macroGroupComboBox->addItem(mg->getName()); + if (selectedGroupName == mg->getName()) { selectedIndex = groupCounter; } groupCounter++; @@ -195,14 +259,14 @@ WuQMacroDialog::macroGroupBoxActivated(int) void WuQMacroDialog::macrosListWidgetCurrentRowChanged(int rowIndex) { - m_macroDescriptionTextEdit->clear(); + m_macroDescriptionLabel->clear(); WuQMacroGroup* selectedGroup = getSelectedMacroGroup(); if ((rowIndex >= 0) && (rowIndex < selectedGroup->getNumberOfMacros())) { WuQMacro* macro = selectedGroup->getMacroAtIndex(rowIndex); CaretAssert(macro); - m_macroDescriptionTextEdit->setPlainText(macro->getDescription()); + m_macroDescriptionLabel->setText(macro->getDescription()); } } @@ -260,12 +324,59 @@ WuQMacroDialog::runSelectedMacro() { WuQMacro* macro = getSelectedMacro(); if (macro != NULL) { - std::cout << "Run macro: " << macro->getName() << std::endl; - std::cout << macro->toString() << std::endl; - + WuQMacroExecutor::RunOptions runOptions(m_runOptionDelayBetweenCommandsSpinBox->value(), + m_runOptionMoveMouseCheckBox->isChecked(), + m_runOptionLoopCheckBox->isChecked()); WuQMacroManager::instance()->runMacro(parentWidget(), + runOptions, macro); } } +/** + * Called when the attributes button is clicked + */ +void +WuQMacroDialog::attributesButtonClicked() +{ + WuQMacro* macro = getSelectedMacro(); + if (macro != NULL) { + if (WuQMacroManager::instance()->editMacroAttributes(this, + macro)) { + updateDialogContents(); + } + } +} + +/** + * Called when the delete button is clicked + */ +void +WuQMacroDialog::deleteButtonClicked() +{ + WuQMacroGroup* macroGroup = getSelectedMacroGroup(); + WuQMacro* macro = getSelectedMacro(); + if ((macroGroup != NULL) + && (macro != NULL)) { + if (WuQMacroManager::instance()->deleteMacro(m_deletePushButton, macroGroup, macro)) { + updateDialogContents(); + } + } +} + +/** + * Called when the edit button is clicked + */ +void +WuQMacroDialog::editButtonClicked() +{ + WuQMacro* macro = getSelectedMacro(); + if (macro != NULL) { + if (WuQMacroManager::instance()->editMacroAttributes(m_editPushButton, macro)) { + updateDialogContents(); + } + } +} + + diff --git a/src/GuiQt/WuQMacroDialog.h b/src/GuiQt/WuQMacroDialog.h index a2475cd14..dc9221569 100644 --- a/src/GuiQt/WuQMacroDialog.h +++ b/src/GuiQt/WuQMacroDialog.h @@ -28,10 +28,12 @@ #include class QAbstractButton; +class QCheckBox; class QComboBox; class QDialogButtonBox; +class QDoubleSpinBox; +class QLabel; class QListWidget; -class QPlainTextEdit; namespace caret { @@ -60,10 +62,16 @@ namespace caret { void macrosListWidgetCurrentRowChanged(int); - void buttonClicked(QAbstractButton* button); + void buttonBoxButtonClicked(QAbstractButton* button); void editSelectedMacro(); + void attributesButtonClicked(); + + void deleteButtonClicked(); + + void editButtonClicked(); + void runSelectedMacro(); private: @@ -77,13 +85,23 @@ namespace caret { QListWidget* m_macrosListWidget; - QPlainTextEdit* m_macroDescriptionTextEdit; + QLabel* m_macroDescriptionLabel; QDialogButtonBox* m_dialogButtonBox; QAbstractButton* m_runButton; - QAbstractButton* m_editButton; + QPushButton* m_deletePushButton; + + QPushButton* m_editPushButton; + + QPushButton* m_attributesPushButton; + + QCheckBox* m_runOptionLoopCheckBox; + + QCheckBox* m_runOptionMoveMouseCheckBox; + + QDoubleSpinBox* m_runOptionDelayBetweenCommandsSpinBox; WuQMacro* m_macro = NULL; diff --git a/src/GuiQt/WuQMacroExecutor.cxx b/src/GuiQt/WuQMacroExecutor.cxx index b890d86d1..45279b887 100644 --- a/src/GuiQt/WuQMacroExecutor.cxx +++ b/src/GuiQt/WuQMacroExecutor.cxx @@ -97,7 +97,7 @@ WuQMacroExecutor::moveMouse(QWidget* widget, const float x = windowPoint.x() + (std::cos(angle) * radius); const float y = windowPoint.y() + (std::sin(angle) * radius); QCursor::setPos(x, y); - //SystemUtilities::sleepSeconds(0.025); + SystemUtilities::sleepSeconds(0.025); } } @@ -111,8 +111,8 @@ WuQMacroExecutor::moveMouse(QWidget* widget, * Macro that is run * @param window * The window from which macro was launched - * @param stopOnErrorFlag - * If true, stop running the commands if there is an error + * @param options + * Executor options * @param errorMessageOut * Output containing any error messages * @return @@ -121,7 +121,7 @@ WuQMacroExecutor::moveMouse(QWidget* widget, bool WuQMacroExecutor::runMacro(const WuQMacro* macro, QObject* window, - const bool stopOnErrorFlag, + const RunOptions& options, QString& errorMessageOut) const { errorMessageOut.clear(); @@ -137,7 +137,7 @@ WuQMacroExecutor::runMacro(const WuQMacro* macro, errorMessageOut.append("Unable to find object named " + objectName + "\n"); - if (stopOnErrorFlag) { + if (options.m_stopOnErrorFlag) { return false; } continue; @@ -147,7 +147,7 @@ WuQMacroExecutor::runMacro(const WuQMacro* macro, errorMessageOut.append("Object named " + objectName + " has signals blocked"); - if (stopOnErrorFlag) { + if (options.m_stopOnErrorFlag) { return false; } continue; @@ -166,13 +166,15 @@ WuQMacroExecutor::runMacro(const WuQMacro* macro, } const WuQMacroObjectTypeEnum::Enum objectType = mc->getObjectType(); - const bool mouseEventFlag = (objectType == WuQMacroObjectTypeEnum::MOUSE_USER_EVENT); - if (widgetToMoveMouse != NULL) { - if ( ! mouseEventFlag) { - const bool highlightFlag = ( ! mouseEventFlag); - moveMouse(widgetToMoveMouse, - highlightFlag); + + if (options.m_showMouseMovementFlag) { + if (widgetToMoveMouse != NULL) { + if ( ! mouseEventFlag) { + const bool highlightFlag = ( ! mouseEventFlag); + moveMouse(widgetToMoveMouse, + highlightFlag); + } } } @@ -181,15 +183,18 @@ WuQMacroExecutor::runMacro(const WuQMacro* macro, object, commandErrorMessage)) { errorMessageOut.append(commandErrorMessage + "\n"); - if (stopOnErrorFlag) { + if (options.m_stopOnErrorFlag) { return false; } } QGuiApplication::processEvents(); if ( ! mouseEventFlag) { - SystemUtilities::sleepSeconds(1); + if (options.m_secondsDelayBetweenCommands > 0.0) { + SystemUtilities::sleepSeconds(options.m_secondsDelayBetweenCommands); + } } + QGuiApplication::processEvents(); } @@ -221,6 +226,7 @@ WuQMacroExecutor::runMacroCommand(const WuQMacroCommand* macroCommand, CaretAssert(object); const WuQMacroObjectTypeEnum::Enum objectType = macroCommand->getObjectType(); + const WuQMacroDataValueTypeEnum::Enum dataValueType = macroCommand->getObjectDataValueType(); const QVariant objectValue = macroCommand->getObjectValue(); bool notFoundFlag(false); @@ -229,6 +235,7 @@ WuQMacroExecutor::runMacroCommand(const WuQMacroCommand* macroCommand, { QAction* action = qobject_cast(object); if (action != NULL) { + CaretAssert(dataValueType == WuQMacroDataValueTypeEnum::BOOLEAN); WuQMacroSignalEmitter signalEmitter; signalEmitter.emitQActionSignal(action, objectValue.toBool()); @@ -242,6 +249,7 @@ WuQMacroExecutor::runMacroCommand(const WuQMacroCommand* macroCommand, { QCheckBox* checkBox = qobject_cast(object); if (checkBox != NULL) { + CaretAssert(dataValueType == WuQMacroDataValueTypeEnum::BOOLEAN); WuQMacroSignalEmitter signalEmitter; signalEmitter.emitQCheckBoxSignal(checkBox, objectValue.toBool()); @@ -255,6 +263,7 @@ WuQMacroExecutor::runMacroCommand(const WuQMacroCommand* macroCommand, { QComboBox* comboBox = qobject_cast(object); if (comboBox != NULL) { + CaretAssert(dataValueType == WuQMacroDataValueTypeEnum::INTEGER); WuQMacroSignalEmitter signalEmitter; signalEmitter.emitQComboBoxSignal(comboBox, objectValue.toInt()); @@ -268,6 +277,7 @@ WuQMacroExecutor::runMacroCommand(const WuQMacroCommand* macroCommand, { QDoubleSpinBox* doubleSpinBox = qobject_cast(object); if (doubleSpinBox != NULL) { + CaretAssert(dataValueType == WuQMacroDataValueTypeEnum::FLOAT); WuQMacroSignalEmitter signalEmitter; signalEmitter.emitQDoubleSpinBoxSignal(doubleSpinBox, objectValue.toDouble()); @@ -284,6 +294,7 @@ WuQMacroExecutor::runMacroCommand(const WuQMacroCommand* macroCommand, { QLineEdit* lineEdit = qobject_cast(object); if (lineEdit != NULL) { + CaretAssert(dataValueType == WuQMacroDataValueTypeEnum::STRING); WuQMacroSignalEmitter signalEmitter; signalEmitter.emitQLineEditSignal(lineEdit, objectValue.toString()); @@ -297,6 +308,7 @@ WuQMacroExecutor::runMacroCommand(const WuQMacroCommand* macroCommand, { QListWidget* listWidget = qobject_cast(object); if (listWidget != NULL) { + CaretAssert(dataValueType == WuQMacroDataValueTypeEnum::STRING); WuQMacroSignalEmitter signalEmitter; signalEmitter.emitQListWidgetSignal(listWidget, objectValue.toString()); @@ -310,6 +322,7 @@ WuQMacroExecutor::runMacroCommand(const WuQMacroCommand* macroCommand, { QMenu* menu = qobject_cast(object); if (menu != NULL) { + CaretAssert(dataValueType == WuQMacroDataValueTypeEnum::STRING); WuQMacroSignalEmitter signalEmitter; signalEmitter.emitQMenuTriggered(menu, objectValue.toString()); @@ -379,6 +392,7 @@ WuQMacroExecutor::runMacroCommand(const WuQMacroCommand* macroCommand, { QPushButton* pushButton = qobject_cast(object); if (pushButton != NULL) { + CaretAssert(dataValueType == WuQMacroDataValueTypeEnum::BOOLEAN); WuQMacroSignalEmitter signalEmitter; signalEmitter.emitQPushButtonSignal(pushButton, objectValue.toBool()); @@ -392,6 +406,7 @@ WuQMacroExecutor::runMacroCommand(const WuQMacroCommand* macroCommand, { QRadioButton* radioButton = qobject_cast(object); if (radioButton != NULL) { + CaretAssert(dataValueType == WuQMacroDataValueTypeEnum::BOOLEAN); WuQMacroSignalEmitter signalEmitter; signalEmitter.emitQRadioButtonSignal(radioButton, objectValue.toBool()); @@ -405,6 +420,7 @@ WuQMacroExecutor::runMacroCommand(const WuQMacroCommand* macroCommand, { QSlider* slider = qobject_cast(object); if (slider != NULL) { + CaretAssert(dataValueType == WuQMacroDataValueTypeEnum::INTEGER); WuQMacroSignalEmitter signalEmitter; signalEmitter.emitQSliderSignal(slider, objectValue.toInt()); @@ -418,6 +434,7 @@ WuQMacroExecutor::runMacroCommand(const WuQMacroCommand* macroCommand, { QSpinBox* spinBox = qobject_cast(object); if (spinBox != NULL) { + CaretAssert(dataValueType == WuQMacroDataValueTypeEnum::INTEGER); WuQMacroSignalEmitter signalEmitter; signalEmitter.emitQSpinBoxSignal(spinBox, objectValue.toInt()); @@ -431,6 +448,7 @@ WuQMacroExecutor::runMacroCommand(const WuQMacroCommand* macroCommand, { QTabBar* tabBar = qobject_cast(object); if (tabBar != NULL) { + CaretAssert(dataValueType == WuQMacroDataValueTypeEnum::INTEGER); WuQMacroSignalEmitter signalEmitter; signalEmitter.emitQTabBarSignal(tabBar, objectValue.toInt()); @@ -444,6 +462,7 @@ WuQMacroExecutor::runMacroCommand(const WuQMacroCommand* macroCommand, { QTabWidget* tabWidget = qobject_cast(object); if (tabWidget != NULL) { + CaretAssert(dataValueType == WuQMacroDataValueTypeEnum::INTEGER); WuQMacroSignalEmitter signalEmitter; signalEmitter.emitQTabWidgetSignal(tabWidget, objectValue.toInt()); @@ -457,6 +476,7 @@ WuQMacroExecutor::runMacroCommand(const WuQMacroCommand* macroCommand, { QToolButton* toolButton = qobject_cast(object); if (toolButton != NULL) { + CaretAssert(dataValueType == WuQMacroDataValueTypeEnum::BOOLEAN); WuQMacroSignalEmitter signalEmitter; signalEmitter.emitQToolButtonSignal(toolButton, objectValue.toBool()); diff --git a/src/GuiQt/WuQMacroExecutor.h b/src/GuiQt/WuQMacroExecutor.h index b95051617..32c46b0f4 100644 --- a/src/GuiQt/WuQMacroExecutor.h +++ b/src/GuiQt/WuQMacroExecutor.h @@ -39,6 +39,27 @@ namespace caret { Q_OBJECT public: + class RunOptions { + public: + RunOptions(const float secondsDelayBetweenCommands, + const bool showMouseMovementFlag, + const bool loopFlag) + : m_secondsDelayBetweenCommands(secondsDelayBetweenCommands), + m_showMouseMovementFlag(showMouseMovementFlag), + m_stopOnErrorFlag(true), + m_loopFlag(loopFlag) + { + } + + const float m_secondsDelayBetweenCommands; + + const bool m_showMouseMovementFlag; + + const bool m_stopOnErrorFlag; + + const bool m_loopFlag; + }; + WuQMacroExecutor(); virtual ~WuQMacroExecutor(); @@ -49,7 +70,7 @@ namespace caret { bool runMacro(const WuQMacro* macro, QObject* window, - const bool stopOnErrorFlag, + const RunOptions& options, QString& errorMessageOut) const; diff --git a/src/GuiQt/WuQMacroManager.cxx b/src/GuiQt/WuQMacroManager.cxx index c832492f0..3f51a7ffb 100644 --- a/src/GuiQt/WuQMacroManager.cxx +++ b/src/GuiQt/WuQMacroManager.cxx @@ -40,13 +40,14 @@ #include "WuQMacroCreateDialog.h" #include "WuQMacroDialog.h" #include "WuQMacroExecutor.h" +#include "WuQMacroGroup.h" #include "WuQMacroMouseEventInfo.h" +#include "WuQMacroAttributesDialog.h" #include "WuQMacroSignalWatcher.h" using namespace caret; - /** * \class caret::WuQMacroManager * \brief Manages the macro system. @@ -89,33 +90,6 @@ WuQMacroManager::~WuQMacroManager() } } -///** -// * Create the singleton Macro Manager. This method MUST be called before -// * calling instance() and this method should only be called once. When -// * the given 'parent' is deleted, it will result in the singletone instance -// * being deleted. -// * -// * @param name -// * Name of macro manager -// * @parent -// * Parent of the macro manager. -// */ -//WuQMacroManager* -//WuQMacroManager::createMacroManagerSingleton(const QString& name, -// QObject* parent) -//{ -// if (s_singletonMacroManager == NULL) { -// s_singletonMacroManager = new WuQMacroManager(name, -// parent); -// } -// else { -// CaretLogSevere("WuQMacroManager::createMacroManagerSingleton() has already been " -// "called, this should never be called more than once."); -// } -// -// return s_singletonMacroManager; -//} - /** * @return The instance of the Macro Manager. Before calling this method, * the createMacroManagerSingleton() must have been called to create the @@ -131,9 +105,6 @@ WuQMacroManager::instance() */ s_singletonMacroManager = new WuQMacroManager("MacroManager", qApp); -// CaretLogSevere("A macro manager has not been created with createMacroManagerSingleton(). " -// "That method MUST be called before this method."); -// std::abort(); } return s_singletonMacroManager; @@ -292,6 +263,7 @@ WuQMacroManager::addMouseEventToRecording(QWidget* widget, } CaretAssert(m_macroBeingRecorded); + bool validMouseEventFlag(true); WuQMacroMouseEventTypeEnum::Enum mouseEventType = WuQMacroMouseEventTypeEnum::MOVE; switch (me->type()) { case QEvent::MouseButtonPress: @@ -305,29 +277,35 @@ WuQMacroManager::addMouseEventToRecording(QWidget* widget, break; case QEvent::MouseMove: mouseEventType = WuQMacroMouseEventTypeEnum::MOVE; + + /* + * Only track move events if a button is down + */ + if (me->button() == Qt::NoButton) { + validMouseEventFlag = false; + } break; default: CaretAssertMessage(0, ("Unknown mouse event type integer cast=" + QString::number(static_cast(me->type())))); break; } - WuQMacroMouseEventInfo* mouseInfo = new WuQMacroMouseEventInfo(mouseEventType, - me->localPos().x(), - me->localPos().y(), - me->windowPos().x(), - me->windowPos().y(), - me->screenPos().x(), - me->screenPos().y(), - static_cast(me->button()), - static_cast(me->buttons()), - static_cast(me->modifiers()), - widget->width(), - widget->height()); + if (validMouseEventFlag) { + WuQMacroMouseEventInfo* mouseInfo = new WuQMacroMouseEventInfo(mouseEventType, + me->localPos().x(), + me->localPos().y(), + static_cast(me->button()), + static_cast(me->buttons()), + static_cast(me->modifiers()), + widget->width(), + widget->height()); - m_macroBeingRecorded->addMacroCommand(new WuQMacroCommand(name, + m_macroBeingRecorded->addMacroCommand(new WuQMacroCommand(name, widget->toolTip(), mouseInfo)); - return true; + return true; + + } } return false; @@ -377,6 +355,11 @@ WuQMacroManager::stopRecordingNewMacro() CaretAssert(m_mode == WuQMacroModeEnum::RECORDING); m_mode = WuQMacroModeEnum::OFF; m_macroBeingRecorded = NULL; + + CaretPreferences* preferences = SessionManager::get()->getCaretPreferences(); + CaretAssert(preferences); + preferences->writeMacros(); + updateNonModalDialogs(); } @@ -395,18 +378,6 @@ WuQMacroManager::showMacrosDialog(QWidget* parent) m_macrosDialog->updateDialogContents(); m_macrosDialog->show(); m_macrosDialog->raise(); - - // switch (macroManager->getMode()) { - // case WuQMacroModeEnum::OFF: - // break; - // case WuQMacroModeEnum::RECORDING: - // CaretAssert(0); - // break; - // case WuQMacroModeEnum::RUNNING: - // CaretAssert(0); - // break; - // } - } /** @@ -436,8 +407,19 @@ WuQMacroManager::updateNonModalDialogs() } } +/** + * Run the given macro + * + * @param widget + * Widget used for parent of dialogs + * @param runOptions + * Options used when running the macro + * @param macro + * Macro that is run + */ void WuQMacroManager::runMacro(QWidget* widget, + const WuQMacroExecutor::RunOptions& runOptions, const WuQMacro* macro) { CaretAssert(widget); @@ -448,7 +430,7 @@ WuQMacroManager::runMacro(QWidget* widget, WuQMacroExecutor executor; if ( ! executor.runMacro(macro, widget, - true, + runOptions, errorMessage)) { QMessageBox::critical(widget, "Run Macro Error", @@ -458,6 +440,91 @@ WuQMacroManager::runMacro(QWidget* widget, } } +/** + * Edit a macros attributes (name, description) + * + * @param parent + * Parent widget for dialog + * @param macro + * Macro to edit + * @return + * True if macro was modified. + */ +bool +WuQMacroManager::editMacroAttributes(QWidget* parent, + WuQMacro* macro) +{ + if (macro != NULL) { + WuQMacroAttributesDialog attributesDialog(macro, + parent); + if (attributesDialog.exec() == WuQMacroDialog::Accepted) { + if (attributesDialog.isMacroModified()) { + CaretPreferences* preferences = SessionManager::get()->getCaretPreferences(); + CaretAssert(preferences); + preferences->writeMacros(); + return true; + } + } + } + + return false; +} + +/** + * Delete a macro + * + * @param parent + * Parent widget for dialog + * @param macroGroup + * Group containing macro for deletion + * @param macro + * Macro to delete + * @return + * True if macro was deleted. + */ +bool +WuQMacroManager::deleteMacro(QWidget* parent, + WuQMacroGroup* macroGroup, + WuQMacro* macro) +{ + if (QMessageBox::warning(parent, + "Warning", + ("Delete the macro " + macro->getName()), + QMessageBox::Ok | QMessageBox::Cancel, + QMessageBox::Ok) == QMessageBox::Ok) { + macroGroup->deleteMacro(macro); + CaretPreferences* preferences = SessionManager::get()->getCaretPreferences(); + CaretAssert(preferences); + preferences->writeMacros(); + return true; + } + + return false; +} + +/** + * Edit a macros commands + * + * @param parent + * Parent widget for dialog + * @param macro + * Macro to edit + * @return + * True if macro was modified. + */ +bool +WuQMacroManager::editMacroCommands(QWidget* parent, + WuQMacro* /*macro*/) +{ + QMessageBox::information(parent, + "Info", + "Edit button has not been implemented", + QMessageBox::Ok, + QMessageBox::Ok); + return false; +} + + /** * Print supported widgets to the terminal window. */ diff --git a/src/GuiQt/WuQMacroManager.h b/src/GuiQt/WuQMacroManager.h index e0d310d4d..020933fa5 100644 --- a/src/GuiQt/WuQMacroManager.h +++ b/src/GuiQt/WuQMacroManager.h @@ -27,6 +27,7 @@ #include +#include "WuQMacroExecutor.h" #include "WuQMacroModeEnum.h" class QMouseEvent; @@ -44,8 +45,6 @@ namespace caret { Q_OBJECT public: -// static WuQMacroManager* createMacroManagerSingleton(const QString& name, -// QObject* parent); static WuQMacroManager* instance(); virtual ~WuQMacroManager(); @@ -81,7 +80,18 @@ namespace caret { void updateNonModalDialogs(); + bool editMacroAttributes(QWidget* parent, + WuQMacro* macro); + + bool deleteMacro(QWidget* parent, + WuQMacroGroup* macroGroup, + WuQMacro* macro); + + bool editMacroCommands(QWidget* parent, + WuQMacro* macro); + void runMacro(QWidget* window, + const WuQMacroExecutor::RunOptions& runOptions, const WuQMacro* macro); void printSupportedWidgetsToTerminal(); diff --git a/src/GuiQt/WuQMacroMenu.cxx b/src/GuiQt/WuQMacroMenu.cxx index 639492a83..b32354525 100644 --- a/src/GuiQt/WuQMacroMenu.cxx +++ b/src/GuiQt/WuQMacroMenu.cxx @@ -61,10 +61,15 @@ m_windowParent(windowParent) QObject::connect(m_recordMacroAction, &QAction::triggered, this, &WuQMacroMenu::macroRecordSelected); - addSeparator(); - QAction* printAction = addAction("Print Supported Widgets"); - QObject::connect(printAction, &QAction::triggered, - this, &WuQMacroMenu::macroPrintAllSelected); + { + QMenu* developmentMenu = new QMenu("Development"); + QAction* printAction = developmentMenu->addAction("Development - Print Supported Widgets"); + QObject::connect(printAction, &QAction::triggered, + this, &WuQMacroMenu::macroPrintAllSelected); + + addSeparator(); + addMenu(developmentMenu); + } QObject::connect(this, &QMenu::aboutToShow, this, &WuQMacroMenu::macroMenuAboutToShow); diff --git a/src/GuiQt/WuQMacroSignalWatcher.cxx b/src/GuiQt/WuQMacroSignalWatcher.cxx index 9c0a5bf18..264c2d6f2 100644 --- a/src/GuiQt/WuQMacroSignalWatcher.cxx +++ b/src/GuiQt/WuQMacroSignalWatcher.cxx @@ -246,55 +246,6 @@ WuQMacroSignalWatcher::newInstance(WuQMacroManager* parentMacroManager, object, objectType); return ww; - -// if (qobject_cast(object) != NULL) { -// objectType = WuQMacroObjectTypeEnum::ACTION; -// } -// else if (qobject_cast(object) != NULL) { -// objectType = WuQMacroObjectTypeEnum::CHECK_BOX; -// } -// else if (qobject_cast(object) != NULL) { -// objectType = WuQMacroObjectTypeEnum::CHECK_BOX; -// } -// else if (qobject_cast(object) != NULL) { -// objectType = WuQMacroObjectTypeEnum::CHECK_BOX; -// } -// else if (qobject_cast(object) != NULL) { -// objectType = WuQMacroObjectTypeEnum::CHECK_BOX; -// } -// else if (qobject_cast(object) != NULL) { -// objectType = WuQMacroObjectTypeEnum::CHECK_BOX; -// } -// else if (qobject_cast(object) != NULL) { -// objectType = WuQMacroObjectTypeEnum::CHECK_BOX; -// } -// else if (qobject_cast(object) != NULL) { -// objectType = WuQMacroObjectTypeEnum::CHECK_BOX; -// } -// else if (qobject_cast(object) != NULL) { -// objectType = WuQMacroObjectTypeEnum::CHECK_BOX; -// } -// else if (qobject_cast(object) != NULL) { -// objectType = WuQMacroObjectTypeEnum::CHECK_BOX; -// } -// else if (qobject_cast(object) != NULL) { -// objectType = WuQMacroObjectTypeEnum::CHECK_BOX; -// } -// else if (qobject_cast(object) != NULL) { -// objectType = WuQMacroObjectTypeEnum::CHECK_BOX; -// } -// else if (qobject_cast(object) != NULL) { -// objectType = WuQMacroObjectTypeEnum::CHECK_BOX; -// } -// else if (qobject_cast(object) != NULL) { -// objectType = WuQMacroObjectTypeEnum::CHECK_BOX; -// } -// else if (qobject_cast(object) != NULL) { -// objectType = WuQMacroObjectTypeEnum::CHECK_BOX; -// } -// else if (qobject_cast(object) != NULL) { -// objectType = WuQMacroObjectTypeEnum::CHECK_BOX; -// } } /** -- GitLab From a16f6d05b0c53a08dc158e31be201cefd8665ac9 Mon Sep 17 00:00:00 2001 From: John Harwell Date: Thu, 6 Dec 2018 16:01:30 -0600 Subject: [PATCH 127/427] WB-834: Continuing macro system development, initial implementation of dialogs has been completed. --- src/Common/CMakeLists.txt | 2 + src/Common/WuQMacroCommand.cxx | 19 +- src/Common/WuQMacroFile.cxx | 332 +++++++++++++++++++++++++++++ src/Common/WuQMacroFile.h | 90 ++++++++ src/Common/WuQMacroGroup.cxx | 18 ++ src/Common/WuQMacroGroup.h | 2 + src/GuiQt/CMakeLists.txt | 3 + src/GuiQt/WuQMacroDialog.cxx | 168 ++++++++++----- src/GuiQt/WuQMacroDialog.h | 14 +- src/GuiQt/WuQMacroEditorDialog.cxx | 241 +++++++++++++++++++++ src/GuiQt/WuQMacroEditorDialog.h | 96 +++++++++ src/GuiQt/WuQMacroManager.cxx | 122 ++++++++++- src/GuiQt/WuQMacroManager.h | 7 + 13 files changed, 1054 insertions(+), 60 deletions(-) create mode 100644 src/Common/WuQMacroFile.cxx create mode 100644 src/Common/WuQMacroFile.h create mode 100644 src/GuiQt/WuQMacroEditorDialog.cxx create mode 100644 src/GuiQt/WuQMacroEditorDialog.h diff --git a/src/Common/CMakeLists.txt b/src/Common/CMakeLists.txt index 8489a6b48..f2697ad07 100644 --- a/src/Common/CMakeLists.txt +++ b/src/Common/CMakeLists.txt @@ -163,6 +163,7 @@ WorkbenchSpecialVersionEnum.h WuQMacro.h WuQMacroCommand.h WuQMacroDataValueTypeEnum.h +WuQMacroFile.h WuQMacroGroup.h WuQMacroGroupXmlFormatBase.h WuQMacroGroupXmlReader.h @@ -275,6 +276,7 @@ WorkbenchSpecialVersionEnum.cxx WuQMacro.cxx WuQMacroCommand.cxx WuQMacroDataValueTypeEnum.cxx +WuQMacroFile.cxx WuQMacroGroup.cxx WuQMacroGroupXmlFormatBase.cxx WuQMacroGroupXmlReader.cxx diff --git a/src/Common/WuQMacroCommand.cxx b/src/Common/WuQMacroCommand.cxx index 62d722a2a..912b03172 100644 --- a/src/Common/WuQMacroCommand.cxx +++ b/src/Common/WuQMacroCommand.cxx @@ -188,8 +188,23 @@ WuQMacroCommand::copyHelperWuQMacroCommand(const WuQMacroCommand& obj) m_objectName = obj.m_objectName; m_objectToolTip = obj.m_objectToolTip; m_value = obj.m_value; - m_macroMouseEvent = new WuQMacroMouseEventInfo(*obj.m_macroMouseEvent); - m_objectDataValueType = m_objectDataValueType; + m_macroMouseEvent = NULL; + m_objectDataValueType = obj.m_objectDataValueType; + + switch (m_objectDataValueType) { + case WuQMacroDataValueTypeEnum::BOOLEAN: + break; + case WuQMacroDataValueTypeEnum::FLOAT: + break; + case WuQMacroDataValueTypeEnum::INTEGER: + break; + case WuQMacroDataValueTypeEnum::MOUSE: + CaretAssert(obj.m_macroMouseEvent); + m_macroMouseEvent = new WuQMacroMouseEventInfo(*obj.m_macroMouseEvent); + break; + case WuQMacroDataValueTypeEnum::STRING: + break; + } } /** diff --git a/src/Common/WuQMacroFile.cxx b/src/Common/WuQMacroFile.cxx new file mode 100644 index 000000000..89a03637c --- /dev/null +++ b/src/Common/WuQMacroFile.cxx @@ -0,0 +1,332 @@ + +/*LICENSE_START*/ +/* + * Copyright (C) 2018 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + +#define __WU_Q_MACRO_FILE_DECLARE__ +#include "WuQMacroFile.h" +#undef __WU_Q_MACRO_FILE_DECLARE__ + +#include +#include +#include + +#include "CaretAssert.h" +#include "CaretLogger.h" +#include "DataFileException.h" +#include "WuQMacro.h" +#include "WuQMacroGroup.h" +#include "WuQMacroGroupXmlReader.h" +#include "WuQMacroGroupXmlWriter.h" + +using namespace caret; + + + +/** + * \class caret::WuQMacroFile + * \brief File for storing macros + * \ingroup Common + */ + +/** + * Constructor. + */ +WuQMacroFile::WuQMacroFile() +: DataFile() +{ + m_macroGroup.reset(new WuQMacroGroup("File")); +} + +/** + * Destructor. + */ +WuQMacroFile::~WuQMacroFile() +{ +} + +/** + * Copy constructor. + * @param obj + * Object that is copied. + */ +WuQMacroFile::WuQMacroFile(const WuQMacroFile& obj) +: DataFile(obj) +{ + this->copyHelperWuQMacroFile(obj); +} + +/** + * Assignment operator. + * @param obj + * Data copied from obj to this. + * @return + * Reference to this object. + */ +WuQMacroFile& +WuQMacroFile::operator=(const WuQMacroFile& obj) +{ + if (this != &obj) { + DataFile::operator=(obj); + this->copyHelperWuQMacroFile(obj); + } + return *this; +} + +/** + * Helps with copying an object of this type. + * @param obj + * Object that is copied. + */ +void +WuQMacroFile::copyHelperWuQMacroFile(const WuQMacroFile& obj) +{ + *m_macroGroup = *obj.m_macroGroup; + + setMacroGroupName(getFileName()); +} + +/** + * @return True if this file is empty (has no macros) + */ +bool +WuQMacroFile::isEmpty() const +{ + return (m_macroGroup->getNumberOfMacros() == 0); +} + +/** + * @return Macro group in this file + */ +WuQMacroGroup* +WuQMacroFile::getMacroGroup() +{ + return m_macroGroup.get(); +} + +/** + * @return Macro group in this file (const method) + */ +const WuQMacroGroup* +WuQMacroFile::getMacroGroup() const +{ + return m_macroGroup.get(); +} + + +/** + * @return File filter for macro file in a QFileDialog + */ +QString +WuQMacroFile::getFileDialogFilter() +{ + QString f("Macros (*" + + getFileExtension() + + ")"); + return f; +} + +/** + * @return Extension for a macro file + */ +QString +WuQMacroFile::getFileExtension() +{ + return ".wb_macro"; +} + +/** + * Set the status to unmodified. + */ +void +WuQMacroFile::clearModified() +{ + DataFile::clearModified(); + m_macroGroup->clearModified(); +} + +/** + * Is the object modified? + * @return true if modified, else false. + */ +bool +WuQMacroFile::isModified() const +{ + if (DataFile::isModified()) { + return true; + } + if (m_macroGroup->isModified()) { + return true; + } + return false; +} + +/** + * Clear the contents of the file. + */ +void +WuQMacroFile::clear() +{ + m_macroGroup->clear(); +} + +/** + * Append macros in the given group to this file + * + * @param macroGroup + * The macro group whose macros are appended to this macro group + */ +void +WuQMacroFile::appendMacroGroup(const WuQMacroGroup* macroGroup) +{ + const int32_t numMacros = macroGroup->getNumberOfMacros(); + for (int32_t i = 0; i < numMacros; i++) { + const WuQMacro* macro = macroGroup->getMacroAtIndex(i); + CaretAssert(macro); + addMacro(new WuQMacro(*macro)); + } + setModified(); +} + +/** + * Add a macro to this file. + * This file will take ownership of the macro. + * + * @param macro + * Macro to add to file + */ +void +WuQMacroFile::addMacro(WuQMacro* macro) +{ + CaretAssert(macro); + m_macroGroup->addMacro(macro); +} + +/** + * Set the macro group's name to the name of the file + * without the extension + */ +void +WuQMacroFile::setMacroGroupName(const QString& filename) +{ + if (filename.isEmpty()) { + return; + } + + QFileInfo fileInfo(filename); + QString macroGroupName(fileInfo.fileName()); + const int extIndex = macroGroupName.indexOf(getFileExtension()); + if (extIndex > 0) { + macroGroupName.resize(extIndex); + } + m_macroGroup->setName(macroGroupName); +} + + +/** + * Read the data file. + * + * @param filename + * Name of the data file. + * @throws DataFileException + * If the file was not successfully read. + */ +void +WuQMacroFile::readFile(const AString& filename) +{ + if (filename.isEmpty()) { + throw DataFileException("Filename is empty"); + } + + QFile file(filename); + if (file.open(QFile::ReadOnly)) { + setMacroGroupName(filename); + + QTextStream textStream(&file); + const QString fileContentString = textStream.readAll(); + + WuQMacroGroupXmlReader reader; + reader.readFromString(fileContentString, + m_macroGroup.get()); + file.close(); + if (reader.hasError()) { + throw DataFileException(reader.getErrorMessage()); + } + setFileName(filename); + clearModified(); + + if (reader.hasWarnings()) { + CaretLogWarning("When reading " + + filename + + ": " + + reader.getWarningMessage()); + } + } + else { + throw DataFileException("Unable to open file for writing: " + + filename); + } +} + +/** + * Write the data file. + * + * @param filename + * Name of the data file. + * @throws DataFileException + * If the file was not successfully written. + */ +void +WuQMacroFile::writeFile(const AString& filename) +{ + if (filename.isEmpty()) { + throw DataFileException("Filename is empty"); + } + + QFile file(filename); + if (file.open(QFile::WriteOnly)) { + setMacroGroupName(filename); + + /* + * Place the macro group into a string + */ + QString fileContentString; + WuQMacroGroupXmlWriter writer; + writer.writeToString(m_macroGroup.get(), + fileContentString); + + /* + * Write string containing macros and close file + */ + QTextStream textStream(&file); + textStream << fileContentString; + file.close(); + + setFileName(filename); + clearModified(); + } + else { + throw DataFileException("Unable to open file for writing: " + + filename); + } + + +} + + diff --git a/src/Common/WuQMacroFile.h b/src/Common/WuQMacroFile.h new file mode 100644 index 000000000..e87ee5084 --- /dev/null +++ b/src/Common/WuQMacroFile.h @@ -0,0 +1,90 @@ +#ifndef __WU_Q_MACRO_FILE_H__ +#define __WU_Q_MACRO_FILE_H__ + +/*LICENSE_START*/ +/* + * Copyright (C) 2018 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + + + +#include + +#include "DataFile.h" + + + +namespace caret { + + class WuQMacro; + class WuQMacroGroup; + + class WuQMacroFile : public DataFile { + + public: + WuQMacroFile(); + + virtual ~WuQMacroFile(); + + WuQMacroFile(const WuQMacroFile& obj); + + WuQMacroFile& operator=(const WuQMacroFile& obj); + + virtual bool isEmpty() const override; + + WuQMacroGroup* getMacroGroup(); + + const WuQMacroGroup* getMacroGroup() const; + + virtual void readFile(const AString& filename) override; + + virtual void writeFile(const AString& filename) override; + + static QString getFileDialogFilter(); + + static QString getFileExtension(); + + virtual void clearModified(); + + virtual bool isModified() const; + + virtual void clear(); + + void addMacro(WuQMacro* macro); + + void appendMacroGroup(const WuQMacroGroup* macroGroup); + + // ADD_NEW_METHODS_HERE + + private: + void copyHelperWuQMacroFile(const WuQMacroFile& obj); + + void setMacroGroupName(const QString& filename); + + std::unique_ptr m_macroGroup; + + // ADD_NEW_MEMBERS_HERE + + }; + +#ifdef __WU_Q_MACRO_FILE_DECLARE__ + // +#endif // __WU_Q_MACRO_FILE_DECLARE__ + +} // namespace +#endif //__WU_Q_MACRO_FILE_H__ diff --git a/src/Common/WuQMacroGroup.cxx b/src/Common/WuQMacroGroup.cxx index 4e8e1a76b..23d85efc2 100644 --- a/src/Common/WuQMacroGroup.cxx +++ b/src/Common/WuQMacroGroup.cxx @@ -158,6 +158,24 @@ WuQMacroGroup::addMacro(WuQMacro* macro) setModified(); } +/** + * Append macros in the given group to this group + * + * @param macroGroup + * The macro group whose macros are appended to this macro group + */ +void +WuQMacroGroup::appendMacroGroup(const WuQMacroGroup* macroGroup) +{ + const int32_t numMacros = macroGroup->getNumberOfMacros(); + for (int32_t i = 0; i < numMacros; i++) { + const WuQMacro* macro = macroGroup->getMacroAtIndex(i); + CaretAssert(macro); + addMacro(new WuQMacro(*macro)); + } + setModified(); +} + /** * @return Number of macros in group */ diff --git a/src/Common/WuQMacroGroup.h b/src/Common/WuQMacroGroup.h index d6e80ebf6..a52492fc3 100644 --- a/src/Common/WuQMacroGroup.h +++ b/src/Common/WuQMacroGroup.h @@ -52,6 +52,8 @@ namespace caret { void addMacro(WuQMacro* macro); + void appendMacroGroup(const WuQMacroGroup* macroGroup); + int32_t getNumberOfMacros() const; WuQMacro* getMacroByName(const QString& name); diff --git a/src/GuiQt/CMakeLists.txt b/src/GuiQt/CMakeLists.txt index 3f98a4c43..1378a1ae9 100644 --- a/src/GuiQt/CMakeLists.txt +++ b/src/GuiQt/CMakeLists.txt @@ -206,6 +206,7 @@ ELSE() WuQMacroAttributesDialog.h WuQMacroCreateDialog.h WuQMacroDialog.h + WuQMacroEditorDialog.h WuQMacroExecutor.h WuQMacroManager.h WuQMacroMenu.h @@ -461,6 +462,7 @@ WuQListWidget.h WuQMacroAttributesDialog.h WuQMacroCreateDialog.h WuQMacroDialog.h +WuQMacroEditorDialog.h WuQMacroExecutor.h WuQMacroManager.h WuQMacroMenu.h @@ -703,6 +705,7 @@ WuQListWidget.cxx WuQMacroAttributesDialog.cxx WuQMacroCreateDialog.cxx WuQMacroDialog.cxx +WuQMacroEditorDialog.cxx WuQMacroExecutor.cxx WuQMacroManager.cxx WuQMacroMenu.cxx diff --git a/src/GuiQt/WuQMacroDialog.cxx b/src/GuiQt/WuQMacroDialog.cxx index 558d39ed9..78a5faf18 100644 --- a/src/GuiQt/WuQMacroDialog.cxx +++ b/src/GuiQt/WuQMacroDialog.cxx @@ -67,7 +67,7 @@ WuQMacroDialog::WuQMacroDialog(QWidget* parent) m_macroGroups = WuQMacroManager::instance()->getMacroGroups(); QLabel* macrosLabel = new QLabel("Macro:"); - QLabel* descriptionLabel = new QLabel("Description:"); + QLabel* descriptionLabel = new QLabel("Macro Description:"); QLabel* macroGroupLabel = new QLabel("Macros in:"); m_macroGroupComboBox = new QComboBox(); @@ -84,53 +84,6 @@ WuQMacroDialog::WuQMacroDialog(QWidget* parent) m_macroDescriptionLabel->setAlignment(Qt::AlignTop | Qt::AlignLeft); - m_attributesPushButton = new QPushButton("Attributes..."); - m_attributesPushButton->setToolTip("Edit the selected macro's name, description, etc"); - QObject::connect(m_attributesPushButton, &QPushButton::clicked, - this, &WuQMacroDialog::attributesButtonClicked); - - m_deletePushButton = new QPushButton("Delete..."); - m_deletePushButton->setToolTip("Delete the selected macro"); - QObject::connect(m_deletePushButton, &QPushButton::clicked, - this, &WuQMacroDialog::deleteButtonClicked); - - m_editPushButton = new QPushButton("Edit..."); - m_editPushButton->setToolTip("Edit the steps (commands) in the selected macro"); - QObject::connect(m_editPushButton, &QPushButton::clicked, - this, &WuQMacroDialog::editButtonClicked); - - QVBoxLayout* macroButtonsLayout = new QVBoxLayout(); - macroButtonsLayout->addWidget(m_attributesPushButton); - macroButtonsLayout->addWidget(m_editPushButton); - macroButtonsLayout->addSpacing(20); - macroButtonsLayout->addWidget(m_deletePushButton); - macroButtonsLayout->addStretch(); - - QLabel* runOptionsLabel = new QLabel("Run Macro Options: "); - m_runOptionLoopCheckBox = new QCheckBox("Loop"); - m_runOptionLoopCheckBox->setChecked(false); - m_runOptionLoopCheckBox->setEnabled(false); - m_runOptionMoveMouseCheckBox = new QCheckBox("Move mouse to to highlight controls"); - m_runOptionMoveMouseCheckBox->setChecked(true); - m_runOptionMoveMouseCheckBox->setToolTip("As macro runs, the mouse is moved to\n" - "highlight user-interface controls"); - QLabel* runOptionsDelayLabel = new QLabel("Delay (seconds) between commands"); - m_runOptionDelayBetweenCommandsSpinBox = new QDoubleSpinBox(); - m_runOptionDelayBetweenCommandsSpinBox->setMinimum(0.0); - m_runOptionDelayBetweenCommandsSpinBox->setMaximum(1000.0); - m_runOptionDelayBetweenCommandsSpinBox->setSingleStep(0.1); - m_runOptionDelayBetweenCommandsSpinBox->setDecimals(1); - m_runOptionDelayBetweenCommandsSpinBox->setValue(1.0); - m_runOptionDelayBetweenCommandsSpinBox->setToolTip("Pause for this amount of time"); - m_runOptionDelayBetweenCommandsSpinBox->setFixedWidth(80); - QGridLayout* runOptionsLayout = new QGridLayout(); - runOptionsLayout->setColumnMinimumWidth(0, 10); - runOptionsLayout->addWidget(runOptionsLabel, 0, 0, 1, 3); - runOptionsLayout->addWidget(m_runOptionLoopCheckBox, 1, 1, 1, 2); - runOptionsLayout->addWidget(m_runOptionMoveMouseCheckBox, 2, 1, 1, 2); - runOptionsLayout->addWidget(m_runOptionDelayBetweenCommandsSpinBox, 3, 1); - runOptionsLayout->addWidget(runOptionsDelayLabel, 3, 2); - QFrame* horizontalLine = new QFrame(); horizontalLine->setMidLineWidth(1); horizontalLine->setLineWidth(1); @@ -145,7 +98,7 @@ WuQMacroDialog::WuQMacroDialog(QWidget* parent) | Qt::AlignLeft)); gridLayout->addWidget(m_macrosListWidget, row, 1, 2, 1); row++; - gridLayout->addLayout(macroButtonsLayout, row, 0); + gridLayout->addWidget(createMacroButtonsWidget(), row, 0); row++; for (int32_t iRow = 0; iRow < gridLayout->rowCount(); iRow++) { @@ -164,7 +117,7 @@ WuQMacroDialog::WuQMacroDialog(QWidget* parent) dialogLayout->addWidget(descriptionLabel); dialogLayout->addWidget(m_macroDescriptionLabel); dialogLayout->addWidget(horizontalLine); - dialogLayout->addLayout(runOptionsLayout); + dialogLayout->addWidget(createRunOptionsWidget()); dialogLayout->addWidget(m_dialogButtonBox); updateDialogContents(); @@ -181,6 +134,89 @@ WuQMacroDialog::~WuQMacroDialog() { } +/** + * @return New instance of widget containing macro buttons + */ +QWidget* +WuQMacroDialog::createMacroButtonsWidget() +{ + QWidget* widget = new QWidget(); + + m_attributesPushButton = new QPushButton("Attributes..."); + m_attributesPushButton->setToolTip("Edit the selected macro's name, description, etc"); + QObject::connect(m_attributesPushButton, &QPushButton::clicked, + this, &WuQMacroDialog::attributesButtonClicked); + + m_deletePushButton = new QPushButton("Delete..."); + m_deletePushButton->setToolTip("Delete the selected macro"); + QObject::connect(m_deletePushButton, &QPushButton::clicked, + this, &WuQMacroDialog::deleteButtonClicked); + + m_editPushButton = new QPushButton("Edit..."); + m_editPushButton->setToolTip("Edit the steps (commands) in the selected macro"); + QObject::connect(m_editPushButton, &QPushButton::clicked, + this, &WuQMacroDialog::editButtonClicked); + + m_importPushButton = new QPushButton("Import..."); + m_importPushButton->setToolTip("Import a macro"); + QObject::connect(m_importPushButton, &QPushButton::clicked, + this, &WuQMacroDialog::importButtonClicked); + + m_exportPushButton = new QPushButton("Export..."); + m_exportPushButton->setToolTip("Import a macro"); + QObject::connect(m_exportPushButton, &QPushButton::clicked, + this, &WuQMacroDialog::exportButtonClicked); + + QVBoxLayout* macroButtonsLayout = new QVBoxLayout(widget); + macroButtonsLayout->setSpacing(4); + macroButtonsLayout->addWidget(m_attributesPushButton); + macroButtonsLayout->addWidget(m_editPushButton); + macroButtonsLayout->addSpacing(15); + macroButtonsLayout->addWidget(m_deletePushButton); + macroButtonsLayout->addSpacing(15); + macroButtonsLayout->addWidget(m_importPushButton); + macroButtonsLayout->addWidget(m_exportPushButton); + macroButtonsLayout->addStretch(); + + return widget; +} + +/** + * @return New instance of widget containing macro run options + */ +QWidget* +WuQMacroDialog::createRunOptionsWidget() +{ + QWidget* widget = new QWidget(); + + QLabel* runOptionsLabel = new QLabel("Run Macro Options: "); + m_runOptionLoopCheckBox = new QCheckBox("Loop"); + m_runOptionLoopCheckBox->setChecked(false); + m_runOptionLoopCheckBox->setEnabled(false); + m_runOptionMoveMouseCheckBox = new QCheckBox("Move mouse to to highlight controls"); + m_runOptionMoveMouseCheckBox->setChecked(true); + m_runOptionMoveMouseCheckBox->setToolTip("As macro runs, the mouse is moved to\n" + "highlight user-interface controls"); + QLabel* runOptionsDelayLabel = new QLabel("Delay (seconds) between commands"); + m_runOptionDelayBetweenCommandsSpinBox = new QDoubleSpinBox(); + m_runOptionDelayBetweenCommandsSpinBox->setMinimum(0.0); + m_runOptionDelayBetweenCommandsSpinBox->setMaximum(1000.0); + m_runOptionDelayBetweenCommandsSpinBox->setSingleStep(0.1); + m_runOptionDelayBetweenCommandsSpinBox->setDecimals(1); + m_runOptionDelayBetweenCommandsSpinBox->setValue(1.0); + m_runOptionDelayBetweenCommandsSpinBox->setToolTip("Pause for this amount of time"); + m_runOptionDelayBetweenCommandsSpinBox->setFixedWidth(80); + QGridLayout* runOptionsLayout = new QGridLayout(widget); + runOptionsLayout->setColumnMinimumWidth(0, 10); + runOptionsLayout->addWidget(runOptionsLabel, 0, 0, 1, 3); + runOptionsLayout->addWidget(m_runOptionLoopCheckBox, 1, 1, 1, 2); + runOptionsLayout->addWidget(m_runOptionMoveMouseCheckBox, 2, 1, 1, 2); + runOptionsLayout->addWidget(m_runOptionDelayBetweenCommandsSpinBox, 3, 1); + runOptionsLayout->addWidget(runOptionsDelayLabel, 3, 2); + + return widget; +} + /** * Called when a button in dialog is clicked * @@ -372,11 +408,41 @@ WuQMacroDialog::editButtonClicked() { WuQMacro* macro = getSelectedMacro(); if (macro != NULL) { - if (WuQMacroManager::instance()->editMacroAttributes(m_editPushButton, macro)) { + if (WuQMacroManager::instance()->editMacroCommands(m_editPushButton, macro)) { + updateDialogContents(); + } + } +} + +/** + * Called when import button is clicked + */ +void +WuQMacroDialog::importButtonClicked() +{ + WuQMacroGroup* macroGroup = getSelectedMacroGroup(); + if (macroGroup != NULL) { + if (WuQMacroManager::instance()->importMacros(m_importPushButton, + macroGroup)) { updateDialogContents(); } } } +/** + * Called when export button is clicked + */ +void +WuQMacroDialog::exportButtonClicked() +{ + WuQMacroGroup* macroGroup = getSelectedMacroGroup(); + WuQMacro* macro = getSelectedMacro(); + + if (WuQMacroManager::instance()->exportMacros(m_importPushButton, + macroGroup, + macro)) { + updateDialogContents(); + } +} diff --git a/src/GuiQt/WuQMacroDialog.h b/src/GuiQt/WuQMacroDialog.h index dc9221569..327f3d2ce 100644 --- a/src/GuiQt/WuQMacroDialog.h +++ b/src/GuiQt/WuQMacroDialog.h @@ -72,9 +72,17 @@ namespace caret { void editButtonClicked(); - void runSelectedMacro(); + void importButtonClicked(); + + void exportButtonClicked(); private: + QWidget* createMacroButtonsWidget(); + + QWidget* createRunOptionsWidget(); + + void runSelectedMacro(); + WuQMacroGroup* getSelectedMacroGroup(); WuQMacro* getSelectedMacro(); @@ -97,6 +105,10 @@ namespace caret { QPushButton* m_attributesPushButton; + QPushButton* m_importPushButton; + + QPushButton* m_exportPushButton; + QCheckBox* m_runOptionLoopCheckBox; QCheckBox* m_runOptionMoveMouseCheckBox; diff --git a/src/GuiQt/WuQMacroEditorDialog.cxx b/src/GuiQt/WuQMacroEditorDialog.cxx new file mode 100644 index 000000000..81d683b04 --- /dev/null +++ b/src/GuiQt/WuQMacroEditorDialog.cxx @@ -0,0 +1,241 @@ + +/*LICENSE_START*/ +/* + * Copyright (C) 2018 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + +#define __WU_Q_MACRO_EDITOR_DIALOG_DECLARE__ +#include "WuQMacroEditorDialog.h" +#undef __WU_Q_MACRO_EDITOR_DIALOG_DECLARE__ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "CaretAssert.h" +#include "CaretLogger.h" +#include "WuQMacro.h" +#include "WuQMacroCommand.h" +#include "WuQMacroGroup.h" +#include "WuQMacroManager.h" + +using namespace caret; + + + +/** + * \class caret::WuQMacroEditorDialog + * \brief Dialog for creating a new macro + * \ingroup WuQMacro + */ + +/** + * Constructor. + * + * @param macro + * The macro being edited + * @param parent + * The parent widget + */ +WuQMacroEditorDialog::WuQMacroEditorDialog(WuQMacro* macro, + QWidget* parent) +: QDialog(parent), +m_macro(macro), +m_macroWasModifiedFlag(false) +{ + CaretAssert(m_macro); + + setWindowTitle("Macro Editor"); + + QLabel* nameLabel = new QLabel("Macro name:"); + QLabel* functionKeyLabel = new QLabel("Function Key:"); + QLabel* descriptionLabel = new QLabel("Description:"); + QLabel* commandsLabel = new QLabel("Commands:"); + + m_macroNameLineEdit = new QLineEdit(); + m_macroNameLineEdit->setText(m_macro->getName()); + m_macroFunctionKeyLineEdit = new QLineEdit(); + m_macroFunctionKeyLineEdit->setFixedWidth(40); + m_macroFunctionKeyLineEdit->setText(m_macro->getFunctionKey()); + m_macroDescriptionTextEdit = new QPlainTextEdit(); + m_macroDescriptionTextEdit->setFixedHeight(60); + m_macroDescriptionTextEdit->setPlainText(m_macro->getDescription()); + + m_tableWidget = createTableWidget(); + + QGridLayout* gridLayout = new QGridLayout(); + int row = 0; + gridLayout->addWidget(nameLabel, row, 0); + gridLayout->addWidget(m_macroNameLineEdit, row, 1); + row++; + gridLayout->addWidget(functionKeyLabel, row, 0); + gridLayout->addWidget(m_macroFunctionKeyLineEdit, row, 1); + row++; + gridLayout->addWidget(descriptionLabel, row, 0); + gridLayout->addWidget(m_macroDescriptionTextEdit, row, 1); + row++; + gridLayout->addWidget(commandsLabel, row, 0); + gridLayout->addWidget(m_tableWidget, row, 1); + row++; + + + m_dialogButtonBox = new QDialogButtonBox(QDialogButtonBox::Ok + | QDialogButtonBox::Cancel); + QObject::connect(m_dialogButtonBox, &QDialogButtonBox::accepted, + this, &WuQMacroEditorDialog::accept); + QObject::connect(m_dialogButtonBox, &QDialogButtonBox::rejected, + this, &WuQMacroEditorDialog::reject); + + QVBoxLayout* dialogLayout = new QVBoxLayout(this); + dialogLayout->addLayout(gridLayout); + dialogLayout->addWidget(m_dialogButtonBox); + + loadTableWidget(); +} + +/** + * Destructor. + */ +WuQMacroEditorDialog::~WuQMacroEditorDialog() +{ +} + +/** + * @return New instance of the table widget + */ +QTableWidget* +WuQMacroEditorDialog::createTableWidget() +{ + QTableWidget* tableWidget = new QTableWidget(); + + QStringList columnNames; + CaretAssert(COLUMN_TYPE == 0); + columnNames << "Class"; + CaretAssert(COLUMN_VALUE_TYPE == 1); + columnNames << "Value-Type"; + CaretAssert(COLUMN_NAME == 2); + columnNames << "Name"; + CaretAssert(COLUMN_VALUE == 3); + columnNames << "Value"; + CaretAssert(columnNames.size() == NUMBER_OF_COLUMNS); + + tableWidget->setHorizontalHeaderLabels(columnNames); + + return tableWidget; +} + +/** + * Load the table widget + */ +void +WuQMacroEditorDialog::loadTableWidget() +{ + CaretAssert(m_tableWidget); + + const int32_t numberOfCommands = m_macro->getNumberOfMacroCommands(); + m_tableWidget->setRowCount(numberOfCommands); + m_tableWidget->setColumnCount(NUMBER_OF_COLUMNS); + + for (int32_t i = 0; i < numberOfCommands; i++) { + const WuQMacroCommand* command = m_macro->getMacroCommandAtIndex(i); + QTableWidgetItem* classItem = new QTableWidgetItem(WuQMacroObjectTypeEnum::toGuiName(command->getObjectType())); + QTableWidgetItem* valueTypeItem = new QTableWidgetItem(WuQMacroDataValueTypeEnum::toGuiName(command->getObjectDataValueType())); + QTableWidgetItem* nameItem = new QTableWidgetItem(command->getObjectName()); + QTableWidgetItem* valueItem = new QTableWidgetItem(command->getObjectValue().toString()); + + switch (command->getObjectDataValueType()) { + case WuQMacroDataValueTypeEnum::BOOLEAN: + break; + case WuQMacroDataValueTypeEnum::FLOAT: + break; + case WuQMacroDataValueTypeEnum::INTEGER: + break; + case WuQMacroDataValueTypeEnum::MOUSE: + valueItem->setText("Mouse"); + break; + case WuQMacroDataValueTypeEnum::STRING: + break; + } + + m_tableWidget->setItem(i, COLUMN_TYPE, classItem); + m_tableWidget->setItem(i, COLUMN_VALUE_TYPE, valueTypeItem); + m_tableWidget->setItem(i, COLUMN_NAME, nameItem); + m_tableWidget->setItem(i, COLUMN_VALUE, valueItem); + } +} + + +/** + * Called when user clicks OK or Cancel + * + * @param r + * The dialog code (Accepted or Rejected) + */ +void +WuQMacroEditorDialog::done(int r) +{ + if (r == QDialog::Accepted) { + const QString name(m_macroNameLineEdit->text().trimmed()); + if (name.isEmpty()) { + QMessageBox::critical(this, + "Error", + "Name is missing", + QMessageBox::Ok, + QMessageBox::Ok); + return; + } + + const QString newName = m_macroNameLineEdit->text().trimmed(); + if (m_macro->getName() != newName) { + m_macro->setName(newName); + m_macroWasModifiedFlag = true; + } + + const QString newFunctionKey = m_macroFunctionKeyLineEdit->text().trimmed(); + if (m_macro->getFunctionKey() != newFunctionKey) { + m_macro->setFunctionKey(newFunctionKey); + m_macroWasModifiedFlag = true; + } + + const QString newDescription = m_macroDescriptionTextEdit->toPlainText().trimmed(); + if (m_macro->getDescription() != newDescription) { + m_macro->setDescription(newDescription); + m_macroWasModifiedFlag = true; + } + } + + QDialog::done(r); +} + +/** + * @return True if the macro was modified and may need to be saved, + * else false + */ +bool +WuQMacroEditorDialog::isMacroModified() const +{ + return m_macroWasModifiedFlag; +} + diff --git a/src/GuiQt/WuQMacroEditorDialog.h b/src/GuiQt/WuQMacroEditorDialog.h new file mode 100644 index 000000000..73ab1289d --- /dev/null +++ b/src/GuiQt/WuQMacroEditorDialog.h @@ -0,0 +1,96 @@ +#ifndef __WU_Q_MACRO_EDITOR_DIALOG_H__ +#define __WU_Q_MACRO_EDITOR_DIALOG_H__ + +/*LICENSE_START*/ +/* + * Copyright (C) 2018 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + + + +#include + +#include + +class QDialogButtonBox; +class QLineEdit; +class QPlainTextEdit; +class QTableWidget; + + +namespace caret { + + class WuQMacro; + class WuQMacroGroup; + + class WuQMacroEditorDialog : public QDialog { + + Q_OBJECT + + public: + WuQMacroEditorDialog(WuQMacro* macro, + QWidget* parent = 0); + + virtual ~WuQMacroEditorDialog(); + + WuQMacroEditorDialog(const WuQMacroEditorDialog&) = delete; + + WuQMacroEditorDialog& operator=(const WuQMacroEditorDialog&) = delete; + + bool isMacroModified() const; + + // ADD_NEW_METHODS_HERE + + public slots: + virtual void done(int r) override; + + private: + enum { + COLUMN_TYPE = 0, + COLUMN_VALUE_TYPE = 1, + COLUMN_NAME = 2, + COLUMN_VALUE = 3, + NUMBER_OF_COLUMNS = 4 + }; + + QTableWidget* createTableWidget(); + + void loadTableWidget(); + + WuQMacro* m_macro; + + bool m_macroWasModifiedFlag = false; + + QLineEdit* m_macroNameLineEdit; + + QLineEdit* m_macroFunctionKeyLineEdit; + + QPlainTextEdit* m_macroDescriptionTextEdit; + + QTableWidget* m_tableWidget; + + QDialogButtonBox* m_dialogButtonBox; + + }; + +#ifdef __WU_Q_MACRO_EDITOR_DIALOG_DECLARE__ + // +#endif // __WU_Q_MACRO_EDITOR_DIALOG_DECLARE__ + +} // namespace +#endif //__WU_Q_MACRO_EDITOR_DIALOG_H__ diff --git a/src/GuiQt/WuQMacroManager.cxx b/src/GuiQt/WuQMacroManager.cxx index 3f51a7ffb..0736b980b 100644 --- a/src/GuiQt/WuQMacroManager.cxx +++ b/src/GuiQt/WuQMacroManager.cxx @@ -33,16 +33,20 @@ #include "CaretAssert.h" #include "CaretLogger.h" +#include "CaretFileDialog.h" #include "CaretPreferences.h" +#include "DataFileException.h" #include "SessionManager.h" #include "WuQMacro.h" #include "WuQMacroCommand.h" #include "WuQMacroCreateDialog.h" #include "WuQMacroDialog.h" #include "WuQMacroExecutor.h" +#include "WuQMacroFile.h" #include "WuQMacroGroup.h" #include "WuQMacroMouseEventInfo.h" #include "WuQMacroAttributesDialog.h" +#include "WuQMacroEditorDialog.h" #include "WuQMacroSignalWatcher.h" using namespace caret; @@ -502,6 +506,104 @@ WuQMacroManager::deleteMacro(QWidget* parent, return false; } +/** + * Import macros from a file + * + * @param parent + * Parent widget for dialog + * @param macroGroup + * Group to which macros are appended + * @return + * True if macro(s) were successfully imported + */ +bool +WuQMacroManager::importMacros(QWidget* parent, + WuQMacroGroup* appendToMacroGroup) +{ + QString fileFilterString(WuQMacroFile::getFileDialogFilter()); + const QString filename = CaretFileDialog::getOpenFileNameDialog(parent, + "Import Macros", + "", + fileFilterString, + &fileFilterString); + if ( ! filename.isEmpty()) { + WuQMacroFile macroFile; + try { + macroFile.readFile(filename); + + const WuQMacroGroup* fileMacroGroup = macroFile.getMacroGroup(); + if (fileMacroGroup->getNumberOfMacros() > 0) { + appendToMacroGroup->appendMacroGroup(fileMacroGroup); + return true; + } + else { + throw DataFileException("File is empty, no macros to import"); + } + } + catch (const DataFileException& dfe) { + QMessageBox::critical(parent, + "File Error", + dfe.whatString(), + QMessageBox::Ok, + QMessageBox::Ok); + } + } + + return false; +} + +/** + * Export macro(s) + * + * @param parent + * Parent widget for dialog + * @param macroGroup + * Group for export (if non-NULL) + * @param macro + * Macro for export (if non-NULL) + * @return + * True if macro was successfully exported + */ +bool +WuQMacroManager::exportMacros(QWidget* parent, + WuQMacroGroup* macroGroup, + WuQMacro* macro) +{ + QString fileFilterString(WuQMacroFile::getFileDialogFilter()); + const QString filename = CaretFileDialog::getSaveFileNameDialog(parent, + "Export Macros", + "", + fileFilterString, + &fileFilterString); + if ( ! filename.isEmpty()) { + try { + WuQMacroFile macroFile; + + if (macroGroup != NULL) { + macroFile.appendMacroGroup(macroGroup); + } + else if (macro != NULL) { + macroFile.addMacro(new WuQMacro(*macro)); + } + else { + throw DataFileException("No macro group or macro for export"); + } + + macroFile.writeFile(filename); + return true; + } + catch (const DataFileException& dfe) { + QMessageBox::critical(parent, + "File Error", + dfe.whatString(), + QMessageBox::Ok, + QMessageBox::Ok); + } + } + + return false; +} + /** * Edit a macros commands * @@ -514,13 +616,21 @@ WuQMacroManager::deleteMacro(QWidget* parent, */ bool WuQMacroManager::editMacroCommands(QWidget* parent, - WuQMacro* /*macro*/) + WuQMacro* macro) { - QMessageBox::information(parent, - "Info", - "Edit button has not been implemented", - QMessageBox::Ok, - QMessageBox::Ok); + if (macro != NULL) { + WuQMacroEditorDialog editorDialog(macro, + parent); + if (editorDialog.exec() == WuQMacroDialog::Accepted) { + if (editorDialog.isMacroModified()) { + CaretPreferences* preferences = SessionManager::get()->getCaretPreferences(); + CaretAssert(preferences); + preferences->writeMacros(); + return true; + } + } + } + return false; } diff --git a/src/GuiQt/WuQMacroManager.h b/src/GuiQt/WuQMacroManager.h index 020933fa5..32f7cf1e8 100644 --- a/src/GuiQt/WuQMacroManager.h +++ b/src/GuiQt/WuQMacroManager.h @@ -90,6 +90,13 @@ namespace caret { bool editMacroCommands(QWidget* parent, WuQMacro* macro); + bool importMacros(QWidget* parent, + WuQMacroGroup* appendToMacroGroup); + + bool exportMacros(QWidget* parent, + WuQMacroGroup* macroGroup, + WuQMacro* macro); + void runMacro(QWidget* window, const WuQMacroExecutor::RunOptions& runOptions, const WuQMacro* macro); -- GitLab From 7c7905d7583732050edc12b3493ef2ffb4740076 Mon Sep 17 00:00:00 2001 From: John Harwell Date: Tue, 11 Dec 2018 16:54:14 -0600 Subject: [PATCH 128/427] WB-834: Added macro support to all widgets in the toolbar (but not the 'mode' sub-toolbars) and the Overlay and Features ToolBoxes. --- src/Common/WuQMacroCommand.cxx | 6 + src/Common/WuQMacroGroupXmlWriter.cxx | 4 + src/Common/WuQMacroObjectTypeEnum.cxx | 48 ++++- src/Common/WuQMacroObjectTypeEnum.h | 17 +- .../AnnotationSelectionViewController.cxx | 24 ++- src/GuiQt/AnnotationSelectionViewController.h | 1 + ...notationTextSubstitutionViewController.cxx | 10 +- ...AnnotationTextSubstitutionViewController.h | 3 +- src/GuiQt/BorderOptimizeDialog.cxx | 9 +- src/GuiQt/BorderOptimizeDialog.h | 2 + src/GuiQt/BorderSelectionViewController.cxx | 63 +++++- src/GuiQt/BorderSelectionViewController.h | 5 +- src/GuiQt/BrainBrowserWindow.cxx | 20 +- src/GuiQt/BrainBrowserWindow.h | 2 + .../BrainBrowserWindowOrientedToolBox.cxx | 52 ++++- src/GuiQt/BrainBrowserWindowOrientedToolBox.h | 7 +- src/GuiQt/BrainBrowserWindowToolBar.cxx | 188 ++++++++++++++---- src/GuiQt/BrainBrowserWindowToolBar.h | 1 + ...ainBrowserWindowToolBarChartAttributes.cxx | 42 +++- ...BrainBrowserWindowToolBarChartAttributes.h | 9 +- .../BrainBrowserWindowToolBarChartAxes.cxx | 28 ++- .../BrainBrowserWindowToolBarChartAxes.h | 3 +- ...BrowserWindowToolBarChartTwoAttributes.cxx | 38 +++- ...inBrowserWindowToolBarChartTwoAttributes.h | 9 +- .../BrainBrowserWindowToolBarChartTwoAxes.cxx | 68 ++++++- .../BrainBrowserWindowToolBarChartTwoAxes.h | 3 +- ...rowserWindowToolBarChartTwoOrientation.cxx | 21 +- ...nBrowserWindowToolBarChartTwoOrientation.h | 3 +- ...BrainBrowserWindowToolBarChartTwoTitle.cxx | 30 ++- .../BrainBrowserWindowToolBarChartTwoTitle.h | 3 +- .../BrainBrowserWindowToolBarChartTwoType.cxx | 15 +- .../BrainBrowserWindowToolBarChartTwoType.h | 3 +- .../BrainBrowserWindowToolBarChartType.cxx | 15 +- .../BrainBrowserWindowToolBarChartType.h | 3 +- .../BrainBrowserWindowToolBarClipping.cxx | 71 ++++--- src/GuiQt/BrainBrowserWindowToolBarClipping.h | 3 +- .../BrainBrowserWindowToolBarSlicePlane.cxx | 34 ++-- .../BrainBrowserWindowToolBarSlicePlane.h | 5 +- ...rainBrowserWindowToolBarSliceSelection.cxx | 35 ++-- .../BrainBrowserWindowToolBarSliceSelection.h | 3 +- ...rainBrowserWindowToolBarSurfaceMontage.cxx | 169 ++++++++++++---- .../BrainBrowserWindowToolBarSurfaceMontage.h | 12 +- src/GuiQt/BrainBrowserWindowToolBarTab.cxx | 21 +- src/GuiQt/BrainBrowserWindowToolBarTab.h | 5 +- ...BrainBrowserWindowToolBarVolumeMontage.cxx | 35 +++- .../BrainBrowserWindowToolBarVolumeMontage.h | 3 +- src/GuiQt/CaretColorEnumComboBox.cxx | 29 +++ src/GuiQt/CaretColorEnumComboBox.h | 5 + src/GuiQt/ChartHistoryViewController.cxx | 33 ++- src/GuiQt/ChartHistoryViewController.h | 3 + src/GuiQt/ChartSelectionViewController.cxx | 6 + src/GuiQt/ChartSelectionViewController.h | 1 + src/GuiQt/ChartToolBoxViewController.cxx | 28 ++- src/GuiQt/ChartToolBoxViewController.h | 1 + .../ChartTwoOverlaySetViewController.cxx | 16 +- src/GuiQt/ChartTwoOverlaySetViewController.h | 5 +- src/GuiQt/ChartTwoOverlayViewController.cxx | 148 +++++++++++--- src/GuiQt/ChartTwoOverlayViewController.h | 16 +- .../CiftiConnectivityMatrixViewController.cxx | 38 +++- .../CiftiConnectivityMatrixViewController.h | 4 +- src/GuiQt/DisplayGroupEnumComboBox.cxx | 38 ++++ src/GuiQt/DisplayGroupEnumComboBox.h | 3 + src/GuiQt/FociProjectionDialog.cxx | 11 +- src/GuiQt/FociProjectionDialog.h | 2 + src/GuiQt/FociSelectionViewController.cxx | 46 ++++- src/GuiQt/FociSelectionViewController.h | 5 +- .../GroupAndNameHierarchyViewController.cxx | 24 ++- .../GroupAndNameHierarchyViewController.h | 5 +- src/GuiQt/GuiManager.cxx | 23 +++ src/GuiQt/ImageSelectionViewController.cxx | 56 ++++-- src/GuiQt/ImageSelectionViewController.h | 3 + src/GuiQt/LabelSelectionViewController.cxx | 24 ++- src/GuiQt/LabelSelectionViewController.h | 5 +- src/GuiQt/OverlaySetViewController.cxx | 4 + src/GuiQt/OverlaySetViewController.h | 1 + src/GuiQt/OverlayViewController.cxx | 49 ++--- src/GuiQt/OverlayViewController.h | 1 + .../StructureSurfaceSelectionControl.cxx | 20 +- src/GuiQt/StructureSurfaceSelectionControl.h | 4 +- src/GuiQt/SurfaceSelectionViewController.cxx | 41 +++- src/GuiQt/SurfaceSelectionViewController.h | 12 +- .../VolumeSurfaceOutlineSetViewController.cxx | 8 +- .../VolumeSurfaceOutlineSetViewController.h | 1 + .../VolumeSurfaceOutlineViewController.cxx | 21 +- .../VolumeSurfaceOutlineViewController.h | 1 + src/GuiQt/WuQDataEntryDialog.cxx | 3 +- src/GuiQt/WuQMacroExecutor.cxx | 95 ++++++++- src/GuiQt/WuQMacroExecutor.h | 1 + src/GuiQt/WuQMacroManager.cxx | 156 ++++++++++++--- src/GuiQt/WuQMacroManager.h | 9 +- src/GuiQt/WuQMacroSignalEmitter.cxx | 158 ++++++++++++++- src/GuiQt/WuQMacroSignalEmitter.h | 15 +- src/GuiQt/WuQMacroSignalWatcher.cxx | 110 +++++++++- src/GuiQt/WuQMacroSignalWatcher.h | 13 +- src/GuiQt/WuQTabWidget.cxx | 10 + src/GuiQt/WuQTabWidget.h | 2 + 96 files changed, 2067 insertions(+), 397 deletions(-) diff --git a/src/Common/WuQMacroCommand.cxx b/src/Common/WuQMacroCommand.cxx index 912b03172..d6d7254d7 100644 --- a/src/Common/WuQMacroCommand.cxx +++ b/src/Common/WuQMacroCommand.cxx @@ -61,6 +61,12 @@ m_macroMouseEvent(NULL) case WuQMacroObjectTypeEnum::ACTION: m_objectDataValueType = WuQMacroDataValueTypeEnum::BOOLEAN; break; + case WuQMacroObjectTypeEnum::ACTION_GROUP: + m_objectDataValueType = WuQMacroDataValueTypeEnum::STRING; + break; + case WuQMacroObjectTypeEnum::BUTTON_GROUP: + m_objectDataValueType = WuQMacroDataValueTypeEnum::STRING; + break; case WuQMacroObjectTypeEnum::CHECK_BOX: m_objectDataValueType = WuQMacroDataValueTypeEnum::BOOLEAN; break; diff --git a/src/Common/WuQMacroGroupXmlWriter.cxx b/src/Common/WuQMacroGroupXmlWriter.cxx index ef969660a..3a6fdd231 100644 --- a/src/Common/WuQMacroGroupXmlWriter.cxx +++ b/src/Common/WuQMacroGroupXmlWriter.cxx @@ -162,6 +162,10 @@ WuQMacroGroupXmlWriter::writeMacroCommandToXML(const WuQMacroCommand* macroComma switch (macroCommand->getObjectType()) { case WuQMacroObjectTypeEnum::ACTION: break; + case WuQMacroObjectTypeEnum::ACTION_GROUP: + break; + case WuQMacroObjectTypeEnum::BUTTON_GROUP: + break; case WuQMacroObjectTypeEnum::CHECK_BOX: break; case WuQMacroObjectTypeEnum::COMBO_BOX: diff --git a/src/Common/WuQMacroObjectTypeEnum.cxx b/src/Common/WuQMacroObjectTypeEnum.cxx index 96e072b3b..f2070b9a3 100644 --- a/src/Common/WuQMacroObjectTypeEnum.cxx +++ b/src/Common/WuQMacroObjectTypeEnum.cxx @@ -114,11 +114,19 @@ WuQMacroObjectTypeEnum::initialize() "ACTION", "QAction")); - enumData.push_back(WuQMacroObjectTypeEnum(CHECK_BOX, - "CHECK_BOX", - "QCheckBox")); + enumData.push_back(WuQMacroObjectTypeEnum(ACTION_GROUP, + "ACTION_GROUP", + "QActionGroup")); - enumData.push_back(WuQMacroObjectTypeEnum(COMBO_BOX, + enumData.push_back(WuQMacroObjectTypeEnum(BUTTON_GROUP, + "BUTTON_GROUP", + "QButtonGroup")); + + enumData.push_back(WuQMacroObjectTypeEnum(CHECK_BOX, + "CHECK_BOX", + "QCheckBox")); + + enumData.push_back(WuQMacroObjectTypeEnum(COMBO_BOX, "COMBO_BOX", "QComboBox")); @@ -264,7 +272,7 @@ WuQMacroObjectTypeEnum::toGuiName(Enum enumValue) { /** * Get an enumerated value corresponding to its GUI name. - * @param s + * @param guiNameIn * Name of enumerated value. * @param isValidOut * If not NULL, it is set indicating that a @@ -273,10 +281,21 @@ WuQMacroObjectTypeEnum::toGuiName(Enum enumValue) { * Enumerated value. */ WuQMacroObjectTypeEnum::Enum -WuQMacroObjectTypeEnum::fromGuiName(const AString& guiName, bool* isValidOut) +WuQMacroObjectTypeEnum::fromGuiName(const AString& guiNameIn, bool* isValidOut) { if (initializedFlag == false) initialize(); + /* + * Some widgets are extended from Qt Widgets and end with the Qt Name + */ + AString guiName(guiNameIn); + for (auto alias : s_widgetClassNameAliases) { + if (alias.second == guiName) { + guiName = alias.first; + break; + } + } + bool validFlag = false; Enum enumValue = WuQMacroObjectTypeEnum::enumData[0].enumValue; @@ -426,3 +445,20 @@ WuQMacroObjectTypeEnum::getAllGuiNames(std::vector& allGuiNames, const } } +/** + * Add a widget alias for class names + * + * @param widgetName + * The name of a Qt widget class + * @param aliasWidgetName + * Name of class that is derived from Qt widget with name 'widgetName' + */ +void +WuQMacroObjectTypeEnum::addWidgetClassNameAlias(const QString& widgetName, + const QString& aliasWidgetName) +{ + s_widgetClassNameAliases.push_back(std::make_pair(widgetName, + aliasWidgetName)); +} + + diff --git a/src/Common/WuQMacroObjectTypeEnum.h b/src/Common/WuQMacroObjectTypeEnum.h index 054397ed2..3b4977830 100644 --- a/src/Common/WuQMacroObjectTypeEnum.h +++ b/src/Common/WuQMacroObjectTypeEnum.h @@ -39,6 +39,10 @@ public: INVALID, /** a QAction */ ACTION, + /** a QAction Group */ + ACTION_GROUP, + /** a QButtonGroup */ + BUTTON_GROUP, /** a QCheckBox */ CHECK_BOX, /** a QComboBox */ @@ -126,6 +130,9 @@ public: static void getAllGuiNames(std::vector& allGuiNames, const bool isSorted); + static void addWidgetClassNameAlias(const QString& widgetName, + const QString& aliasWidgetName); + private: WuQMacroObjectTypeEnum(const Enum enumValue, const AString& name, @@ -156,12 +163,16 @@ private: /** A user-friendly name that is displayed in the GUI */ AString guiName; + + /** Aliases for widget 'first' is QWidget, 'second' is alias */ + static std::vector> s_widgetClassNameAliases; }; #ifdef __WU_Q_MACRO_OBJECT_TYPE_ENUM_DECLARE__ -std::vector WuQMacroObjectTypeEnum::enumData; -bool WuQMacroObjectTypeEnum::initializedFlag = false; -int32_t WuQMacroObjectTypeEnum::integerCodeCounter = 0; + std::vector WuQMacroObjectTypeEnum::enumData; + bool WuQMacroObjectTypeEnum::initializedFlag = false; + int32_t WuQMacroObjectTypeEnum::integerCodeCounter = 0; + std::vector> WuQMacroObjectTypeEnum::s_widgetClassNameAliases; #endif // __WU_Q_MACRO_OBJECT_TYPE_ENUM_DECLARE__ } // namespace diff --git a/src/GuiQt/AnnotationSelectionViewController.cxx b/src/GuiQt/AnnotationSelectionViewController.cxx index 6334d6ce0..7bf38dc8a 100644 --- a/src/GuiQt/AnnotationSelectionViewController.cxx +++ b/src/GuiQt/AnnotationSelectionViewController.cxx @@ -41,6 +41,7 @@ #include "GuiManager.h" #include "SceneClass.h" #include "SceneClassAssistant.h" +#include "WuQMacroManager.h" #include "WuQtUtilities.h" using namespace caret; @@ -58,17 +59,26 @@ using namespace caret; * * @param browserWindowIndex * Index of the browser window. + * @param parentObjectName + * Name of parent object * @param parent * The parent widget. */ AnnotationSelectionViewController::AnnotationSelectionViewController(const int32_t browserWindowIndex, + const QString& parentObjectName, QWidget* parent) : QWidget(parent), m_browserWindowIndex(browserWindowIndex) { + const QString objectNamePrefix(parentObjectName + + ":AnnotationSelection"); + WuQMacroManager* macroManager = WuQMacroManager::instance(); + QLabel* groupLabel = new QLabel("Group"); - m_displayGroupComboBox = new DisplayGroupEnumComboBox(this); + m_displayGroupComboBox = new DisplayGroupEnumComboBox(this, + (objectNamePrefix + + ":DisplayGroup")); QObject::connect(m_displayGroupComboBox, SIGNAL(displayGroupSelected(const DisplayGroupEnum::Enum)), this, SLOT(displayGroupSelected(const DisplayGroupEnum::Enum))); @@ -84,20 +94,30 @@ m_browserWindowIndex(browserWindowIndex) m_displayAnnotationsCheckBox->setToolTip("Disables/enables display of annotations in all windows"); QObject::connect(m_displayAnnotationsCheckBox, SIGNAL(clicked(bool)), this, SLOT(checkBoxToggled())); + m_displayAnnotationsCheckBox->setObjectName(objectNamePrefix + + "DisplayAnnotatons"); + macroManager->addMacroSupportToObject(m_displayAnnotationsCheckBox); + m_displayTextAnnotationsCheckBox = new QCheckBox("Display Text Annotations"); m_displayTextAnnotationsCheckBox->setToolTip("Disables/enables display of text annotations in all windows"); QObject::connect(m_displayTextAnnotationsCheckBox, SIGNAL(clicked(bool)), this, SLOT(checkBoxToggled())); + m_displayTextAnnotationsCheckBox->setObjectName(objectNamePrefix + + "DisplayTextAnnotatons"); + macroManager->addMacroSupportToObject(m_displayTextAnnotationsCheckBox); m_displayWindowAnnotationInSingleTabViewsCheckBox = new QCheckBox("Show Window " + QString::number(m_browserWindowIndex + 1) + " Annotations in Single Tab View"); - const QString singTT(WuQtUtilities::createWordWrappedToolTipText("When checked, window annotations are always displayed." + const QString singTT(WuQtUtilities::createWordWrappedToolTipText("When checked, window annotations are always displayed.\n" "When unchecked, window annotations are only displayed when tile tabs is enabled.")); m_displayWindowAnnotationInSingleTabViewsCheckBox->setToolTip(singTT); QObject::connect(m_displayWindowAnnotationInSingleTabViewsCheckBox, SIGNAL(clicked(bool)), this, SLOT(checkBoxToggled())); + m_displayWindowAnnotationInSingleTabViewsCheckBox->setObjectName(objectNamePrefix + + "DisplayWindowAnnotatonsInSingleTabView"); + macroManager->addMacroSupportToObject(m_displayWindowAnnotationInSingleTabViewsCheckBox); m_sceneAssistant = new SceneClassAssistant(); diff --git a/src/GuiQt/AnnotationSelectionViewController.h b/src/GuiQt/AnnotationSelectionViewController.h index cf97c52e5..ea58c7202 100644 --- a/src/GuiQt/AnnotationSelectionViewController.h +++ b/src/GuiQt/AnnotationSelectionViewController.h @@ -41,6 +41,7 @@ namespace caret { public: AnnotationSelectionViewController(const int32_t browserWindowIndex, + const QString& parentObjectName, QWidget* parent); virtual ~AnnotationSelectionViewController(); diff --git a/src/GuiQt/AnnotationTextSubstitutionViewController.cxx b/src/GuiQt/AnnotationTextSubstitutionViewController.cxx index 3cc4e4010..17e8a05fb 100644 --- a/src/GuiQt/AnnotationTextSubstitutionViewController.cxx +++ b/src/GuiQt/AnnotationTextSubstitutionViewController.cxx @@ -49,6 +49,7 @@ #include "MapYokingGroupComboBox.h" #include "SceneClass.h" #include "SceneClassAssistant.h" +#include "WuQMacroManager.h" #include "WuQtUtilities.h" using namespace caret; @@ -66,11 +67,14 @@ using namespace caret; * * @param browserWindowIndex * Index of the browser window. + * @param parentObjectName + * Name of parent object for macros * @param parent * The parent widget. */ AnnotationTextSubstitutionViewController::AnnotationTextSubstitutionViewController(const int32_t browserWindowIndex, - QWidget* parent) + const QString& parentObjectName, + QWidget* parent) : QWidget(parent), m_browserWindowIndex(browserWindowIndex) { @@ -79,6 +83,10 @@ m_browserWindowIndex(browserWindowIndex) m_enableSubstitutionsCheckBox = new QCheckBox("Enable Substitutions"); QObject::connect(m_enableSubstitutionsCheckBox, &QCheckBox::clicked, this, &AnnotationTextSubstitutionViewController::enableCheckBoxClicked); + m_enableSubstitutionsCheckBox->setObjectName(parentObjectName + + ":Substitutions:Enable"); + m_enableSubstitutionsCheckBox->setToolTip("Enable Text Annotation Substitutions"); + WuQMacroManager::instance()->addMacroSupportToObject(m_enableSubstitutionsCheckBox); QButtonGroup* buttonGroup = new QButtonGroup(); diff --git a/src/GuiQt/AnnotationTextSubstitutionViewController.h b/src/GuiQt/AnnotationTextSubstitutionViewController.h index 5fdea8909..db48e9798 100644 --- a/src/GuiQt/AnnotationTextSubstitutionViewController.h +++ b/src/GuiQt/AnnotationTextSubstitutionViewController.h @@ -44,7 +44,8 @@ namespace caret { public: AnnotationTextSubstitutionViewController(const int32_t browserWindowIndex, - QWidget* parent); + const QString& parentObjectName, + QWidget* parent); virtual ~AnnotationTextSubstitutionViewController(); diff --git a/src/GuiQt/BorderOptimizeDialog.cxx b/src/GuiQt/BorderOptimizeDialog.cxx index 21a28ae93..363689f75 100644 --- a/src/GuiQt/BorderOptimizeDialog.cxx +++ b/src/GuiQt/BorderOptimizeDialog.cxx @@ -100,6 +100,7 @@ m_browserTabIndex(-1), m_upsamplingSurfaceSelectionModel(NULL), m_upsamplingSurfaceStructure(StructureEnum::INVALID) { + m_objectNamePrefix + "BorderOptimizeDialog"; m_optimizeDataFileTypes.push_back(DataFileTypeEnum::CONNECTIVITY_DENSE); m_optimizeDataFileTypes.push_back(DataFileTypeEnum::CONNECTIVITY_DENSE_SCALAR); m_optimizeDataFileTypes.push_back(DataFileTypeEnum::CONNECTIVITY_DENSE_TIME_SERIES); @@ -1015,7 +1016,9 @@ BorderOptimizeDialog::isKeepBoundaryBorderSelected() const QWidget* BorderOptimizeDialog::createSurfaceSelectionWidget() { - m_surfaceSelectionControl = new SurfaceSelectionViewController(this); + m_surfaceSelectionControl = new SurfaceSelectionViewController(this, + (m_objectNamePrefix + + ":SurfaceSelectionComboBox")); QObject::connect(m_surfaceSelectionControl, SIGNAL(surfaceSelected(Surface*)), this, SLOT(gradientComputatonSurfaceSelected(Surface*))); @@ -1035,7 +1038,9 @@ QWidget* BorderOptimizeDialog::createSphericalUpsamplingWidget() { QLabel* surfaceLabel = new QLabel("Current Sphere"); - m_upsamplingSurfaceSelectionControl = new SurfaceSelectionViewController(this); + m_upsamplingSurfaceSelectionControl = new SurfaceSelectionViewController(this, + (m_objectNamePrefix + + ":SphericalSurfaceSelectionComboBox")); QLabel* resolutionLabel = new QLabel("Upsampling Resolution"); m_upsamplingResolutionSpinBox = new QSpinBox(); diff --git a/src/GuiQt/BorderOptimizeDialog.h b/src/GuiQt/BorderOptimizeDialog.h index 70e8277c5..8369305c5 100644 --- a/src/GuiQt/BorderOptimizeDialog.h +++ b/src/GuiQt/BorderOptimizeDialog.h @@ -114,6 +114,8 @@ namespace caret { void preserveDialogSizeAndPositionWhenReOpened(); + QString m_objectNamePrefix; + QWidget* m_dialogWidget; Surface* m_surface; diff --git a/src/GuiQt/BorderSelectionViewController.cxx b/src/GuiQt/BorderSelectionViewController.cxx index c656fd1a3..4160c4a72 100644 --- a/src/GuiQt/BorderSelectionViewController.cxx +++ b/src/GuiQt/BorderSelectionViewController.cxx @@ -49,6 +49,7 @@ #include "SceneClass.h" #include "WuQDataEntryDialog.h" #include "WuQFactory.h" +#include "WuQMacroManager.h" #include "WuQTabWidget.h" #include "WuQtUtilities.h" @@ -66,13 +67,25 @@ using namespace caret; /** * Constructor. + * + * @param browserWindowIndex + * Index of browser window + * @param parentObjectName + * Name of parent object + * @param parent + * The parent object */ BorderSelectionViewController::BorderSelectionViewController(const int32_t browserWindowIndex, - QWidget* parent) -: QWidget(parent) + const QString& parentObjectName, + QWidget* parent) +: QWidget(parent), +m_objectNamePrefix(parentObjectName + + ":Borders") { m_browserWindowIndex = browserWindowIndex; + WuQMacroManager* macroManager = WuQMacroManager::instance(); + QLabel* groupLabel = new QLabel("Group"); m_bordersDisplayGroupComboBox = new DisplayGroupEnumComboBox(this); QObject::connect(m_bordersDisplayGroupComboBox, SIGNAL(displayGroupSelected(const DisplayGroupEnum::Enum)), @@ -86,6 +99,10 @@ BorderSelectionViewController::BorderSelectionViewController(const int32_t brows m_bordersDisplayCheckBox = new QCheckBox("Display Borders"); QObject::connect(m_bordersDisplayCheckBox, SIGNAL(clicked(bool)), this, SLOT(processAttributesChanges())); + m_bordersDisplayCheckBox->setObjectName(m_objectNamePrefix + + ":DisplayBorders"); + m_bordersDisplayCheckBox->setToolTip("Display Borders on Surfaces"); + macroManager->addMacroSupportToObject(m_bordersDisplayCheckBox); QWidget* attributesWidget = this->createAttributesWidget(); QWidget* selectionWidget = this->createSelectionWidget(); @@ -97,6 +114,9 @@ BorderSelectionViewController::BorderSelectionViewController(const int32_t brows m_tabWidget->addTab(selectionWidget, "Selection"); m_tabWidget->setCurrentWidget(attributesWidget); + m_tabWidget->getTabBar()->setObjectName(m_objectNamePrefix + + ":Tab"); + macroManager->addMacroSupportToObjectWithToolTip(m_tabWidget->getTabBar(), ""); QVBoxLayout* layout = new QVBoxLayout(this); layout->addWidget(m_bordersDisplayCheckBox); @@ -124,7 +144,10 @@ BorderSelectionViewController::~BorderSelectionViewController() QWidget* BorderSelectionViewController::createSelectionWidget() { - m_borderClassNameHierarchyViewController = new GroupAndNameHierarchyViewController(m_browserWindowIndex); + m_borderClassNameHierarchyViewController = new GroupAndNameHierarchyViewController(m_browserWindowIndex, + (m_objectNamePrefix + + ":Selection"), + this); return m_borderClassNameHierarchyViewController; } @@ -135,9 +158,15 @@ BorderSelectionViewController::createSelectionWidget() QWidget* BorderSelectionViewController::createAttributesWidget() { + WuQMacroManager* macroManager = WuQMacroManager::instance(); + m_bordersContralateralCheckBox = new QCheckBox("Contralateral"); QObject::connect(m_bordersContralateralCheckBox, SIGNAL(clicked(bool)), this, SLOT(processAttributesChanges())); + m_bordersContralateralCheckBox->setObjectName(m_objectNamePrefix + + ":Contralateral"); + m_bordersContralateralCheckBox->setToolTip("Show Contralateral Borders"); + macroManager->addMacroSupportToObject(m_bordersContralateralCheckBox); std::vector drawingTypeEnums; BorderDrawingTypeEnum::getAllEnums(drawingTypeEnums); @@ -155,6 +184,9 @@ BorderSelectionViewController::createAttributesWidget() "Polylines are slower but have unlimited width"); QObject::connect(m_drawTypeComboBox, SIGNAL(activated(int)), this, SLOT(processAttributesChanges())); + m_drawTypeComboBox->setObjectName(m_objectNamePrefix + + ":DrawingType"); + macroManager->addMacroSupportToObject(m_drawTypeComboBox); QLabel* coloringLabel = new QLabel("Coloring"); m_coloringTypeComboBox = new EnumComboBoxTemplate(this); @@ -163,9 +195,16 @@ BorderSelectionViewController::createAttributesWidget() m_coloringTypeComboBox->getWidget()->setToolTip("Select the coloring assignment for borders"); QObject::connect(m_coloringTypeComboBox, SIGNAL(itemActivated()), this, SLOT(processAttributesChanges())); + m_coloringTypeComboBox->getWidget()->setObjectName(m_objectNamePrefix + + ":ColoringType"); + macroManager->addMacroSupportToObject(m_coloringTypeComboBox->getWidget()); QLabel* standardColorLabel = new QLabel("Standard Color"); - m_standardColorComboBox = new CaretColorEnumComboBox(this); + m_standardColorComboBox = new CaretColorEnumComboBox("", + QIcon(), + (m_objectNamePrefix + + ":Color"), + this); m_standardColorComboBox->getWidget()->setToolTip("Select the standard color"); QObject::connect(m_standardColorComboBox, SIGNAL(colorSelected(const CaretColorEnum::Enum)), this, SLOT(processAttributesChanges())); @@ -190,6 +229,9 @@ BorderSelectionViewController::createAttributesWidget() "value of this control is changing"); QObject::connect(m_lineWidthSpinBox, SIGNAL(valueChanged(double)), this, SLOT(processAttributesChanges())); + m_lineWidthSpinBox->setObjectName(m_objectNamePrefix + + ":LineWidth"); + macroManager->addMacroSupportToObject(m_lineWidthSpinBox); QLabel* pointSizeLabel = new QLabel("Symbol Diameter"); m_pointSizeSpinBox = WuQFactory::newDoubleSpinBox(); @@ -202,10 +244,17 @@ BorderSelectionViewController::createAttributesWidget() m_pointSizeSpinBox->setSuffix("mm"); QObject::connect(m_pointSizeSpinBox, SIGNAL(valueChanged(double)), this, SLOT(processAttributesChanges())); + m_pointSizeSpinBox->setObjectName(m_objectNamePrefix + + ":PointSize"); + macroManager->addMacroSupportToObject(m_pointSizeSpinBox); m_enableUnstretchedLinesCheckBox = new QCheckBox("Unstretched Lines"); QObject::connect(m_enableUnstretchedLinesCheckBox, SIGNAL(clicked(bool)), this, SLOT(processAttributesChanges())); + m_enableUnstretchedLinesCheckBox->setToolTip("Disable lines that are invalid due to cuts in flat surface"); + m_enableUnstretchedLinesCheckBox->setObjectName(m_objectNamePrefix + + ":UnstretchedLines"); + macroManager->addMacroSupportToObject(m_enableUnstretchedLinesCheckBox); m_unstretchedLinesLengthSpinBox = WuQFactory::newDoubleSpinBox(); m_unstretchedLinesLengthSpinBox->setFixedWidth(80); @@ -218,6 +267,9 @@ BorderSelectionViewController::createAttributesWidget() m_unstretchedLinesLengthSpinBox->setSuffix("mm"); QObject::connect(m_unstretchedLinesLengthSpinBox, SIGNAL(valueChanged(double)), this, SLOT(processAttributesChanges())); + m_unstretchedLinesLengthSpinBox->setObjectName(m_objectNamePrefix + + ":UnstretchedLinesLength"); + macroManager->addMacroSupportToObject(m_unstretchedLinesLengthSpinBox); QLabel* aboveSurfaceLabel = new QLabel("Above Offset"); m_aboveSurfaceOffsetSpinBox =WuQFactory::newDoubleSpinBox(); @@ -230,6 +282,9 @@ BorderSelectionViewController::createAttributesWidget() "Use with caution.")); QObject::connect(m_aboveSurfaceOffsetSpinBox, SIGNAL(valueChanged(double)), this, SLOT(processAttributesChanges())); + m_aboveSurfaceOffsetSpinBox->setObjectName(m_objectNamePrefix + + ":AboveSurfaceOffset"); + macroManager->addMacroSupportToObject(m_aboveSurfaceOffsetSpinBox); QWidget* gridWidget = new QWidget(); QGridLayout* gridLayout = new QGridLayout(gridWidget); diff --git a/src/GuiQt/BorderSelectionViewController.h b/src/GuiQt/BorderSelectionViewController.h index 2ee174e8d..cc962597c 100644 --- a/src/GuiQt/BorderSelectionViewController.h +++ b/src/GuiQt/BorderSelectionViewController.h @@ -49,7 +49,8 @@ namespace caret { public: BorderSelectionViewController(const int32_t browserWindowIndex, - QWidget* parent = 0); + const QString& parentObjectName, + QWidget* parent = 0); virtual ~BorderSelectionViewController(); @@ -83,6 +84,8 @@ namespace caret { QWidget* createAttributesWidget(); + const QString m_objectNamePrefix; + int32_t m_browserWindowIndex; GroupAndNameHierarchyViewController* m_borderClassNameHierarchyViewController; diff --git a/src/GuiQt/BrainBrowserWindow.cxx b/src/GuiQt/BrainBrowserWindow.cxx index 2d917685d..1ed3fafaf 100644 --- a/src/GuiQt/BrainBrowserWindow.cxx +++ b/src/GuiQt/BrainBrowserWindow.cxx @@ -108,6 +108,7 @@ #include "TileTabsConfigurationModifier.h" #include "WuQDataEntryDialog.h" #include "WuQDoubleSpinBox.h" +#include "WuQMacroManager.h" #include "WuQMacroMenu.h" #include "WuQMessageBox.h" #include "WuQTabBar.h" @@ -140,6 +141,8 @@ m_browserWindowIndex(browserWindowIndex) { m_developMenuAction = NULL; + m_objectNamePrefix = QString("Window%1").arg((int)(browserWindowIndex + 1), 2, 10, QLatin1Char('0')); + std::unique_ptr bwc = EventBrowserWindowContent::newWindowContent(m_browserWindowIndex); EventManager::get()->sendEvent(bwc->getPointer()); @@ -179,6 +182,7 @@ m_browserWindowIndex(browserWindowIndex) new BrainBrowserWindowOrientedToolBox(m_browserWindowIndex, "Overlay ToolBox", BrainBrowserWindowOrientedToolBox::TOOL_BOX_OVERLAYS_VERTICAL, + m_objectNamePrefix, this); m_overlayVerticalToolBox->setAllowedAreas(Qt::LeftDockWidgetArea); @@ -186,6 +190,7 @@ m_browserWindowIndex(browserWindowIndex) new BrainBrowserWindowOrientedToolBox(m_browserWindowIndex, "Overlay ToolBox ", BrainBrowserWindowOrientedToolBox::TOOL_BOX_OVERLAYS_HORIZONTAL, + m_objectNamePrefix, this); m_overlayHorizontalToolBox->setAllowedAreas(Qt::BottomDockWidgetArea); @@ -209,6 +214,7 @@ m_browserWindowIndex(browserWindowIndex) new BrainBrowserWindowOrientedToolBox(m_browserWindowIndex, "Features ToolBox", BrainBrowserWindowOrientedToolBox::TOOL_BOX_FEATURES, + m_objectNamePrefix, this); m_featuresToolBox->setAllowedAreas(Qt::RightDockWidgetArea); addDockWidget(Qt::RightDockWidgetArea, m_featuresToolBox); @@ -226,7 +232,8 @@ m_browserWindowIndex(browserWindowIndex) m_overlayToolBoxAction, m_featuresToolBoxAction, m_toolBarLockWindowAndAllTabAspectRatioButton, - this); + m_objectNamePrefix, + this); m_showToolBarAction = m_toolbar->toolBarToolButtonAction; addToolBar(m_toolbar); @@ -816,6 +823,9 @@ BrainBrowserWindow::createActionsUsedByToolBar() else { m_overlayToolBoxAction->setIconText("OT"); } + m_overlayToolBoxAction->setObjectName(m_objectNamePrefix + + ":ToolBar:ShowOverlayToolBox"); + WuQMacroManager::instance()->addMacroSupportToObject(m_overlayToolBoxAction); /* * Note: The name of a dock widget becomes its @@ -834,8 +844,9 @@ BrainBrowserWindow::createActionsUsedByToolBar() else { m_featuresToolBoxAction->setIconText("LT"); } - - + m_featuresToolBoxAction->setObjectName(m_objectNamePrefix + + ":ToolBar:ShowFeaturesToolBox"); + WuQMacroManager::instance()->addMacroSupportToObject(m_featuresToolBoxAction); m_windowMenuLockWindowAspectRatioAction = new QAction(this); m_windowMenuLockWindowAspectRatioAction->setCheckable(true); @@ -871,6 +882,9 @@ BrainBrowserWindow::createActionsUsedByToolBar() m_toolBarLockWindowAndAllTabAspectRatioAction->setToolTip(aspectButtonToolTipText); QObject::connect(m_toolBarLockWindowAndAllTabAspectRatioAction, &QAction::triggered, this, &BrainBrowserWindow::processToolBarLockWindowAndAllTabAspectTriggered); + m_toolBarLockWindowAndAllTabAspectRatioAction->setObjectName(m_objectNamePrefix + + ":ToolBar:LockAspectRatio"); + WuQMacroManager::instance()->addMacroSupportToObject(m_toolBarLockWindowAndAllTabAspectRatioAction); /* * Button for locking aspect is passed to ToolBar's constructor diff --git a/src/GuiQt/BrainBrowserWindow.h b/src/GuiQt/BrainBrowserWindow.h index f902bf1ee..1ec2b7a81 100644 --- a/src/GuiQt/BrainBrowserWindow.h +++ b/src/GuiQt/BrainBrowserWindow.h @@ -464,6 +464,8 @@ namespace caret { static int32_t s_sceneFileFirstWindowY; static std::set s_brainBrowserWindows; + + QString m_objectNamePrefix; }; #ifdef __BRAIN_BROWSER_WINDOW_DECLARE__ std::set BrainBrowserWindow::s_brainBrowserWindows; diff --git a/src/GuiQt/BrainBrowserWindowOrientedToolBox.cxx b/src/GuiQt/BrainBrowserWindowOrientedToolBox.cxx index b6dd00d8b..4f4363bee 100644 --- a/src/GuiQt/BrainBrowserWindowOrientedToolBox.cxx +++ b/src/GuiQt/BrainBrowserWindowOrientedToolBox.cxx @@ -59,6 +59,7 @@ #include "SessionManager.h" #include "VolumeFile.h" #include "VolumeSurfaceOutlineSetViewController.h" +#include "WuQMacroManager.h" #include "WuQTabWidgetWithSizeHint.h" #include "WuQtUtilities.h" @@ -71,12 +72,17 @@ using namespace caret; * Index of browser window that contains this toolbox. * @param title * Title for the toolbox. - * @param location - * Locations allowed for this toolbox. + * @param toolBoxType + * Type of toolbox + * @param parentObjectName + * Name of parent for macro objets + * @param parent + * The parent widget */ BrainBrowserWindowOrientedToolBox::BrainBrowserWindowOrientedToolBox(const int32_t browserWindowIndex, const QString& title, const ToolBoxType toolBoxType, + const QString& parentObjectNamePrefix, QWidget* parent) : QDockWidget(parent) { @@ -87,8 +93,10 @@ BrainBrowserWindowOrientedToolBox::BrainBrowserWindowOrientedToolBox(const int32 m_toolBoxTitle = title; setWindowTitle(m_toolBoxTitle); + bool isFeaturesToolBox = false; bool isOverlayToolBox = false; + QString typeSuffix; Qt::Orientation orientation = Qt::Horizontal; AString toolboxTypeName = ""; switch (toolBoxType) { @@ -97,21 +105,30 @@ BrainBrowserWindowOrientedToolBox::BrainBrowserWindowOrientedToolBox(const int32 isFeaturesToolBox = true; toggleViewAction()->setText("Features Toolbox"); toolboxTypeName = "Features"; + typeSuffix = "Features"; break; case TOOL_BOX_OVERLAYS_HORIZONTAL: orientation = Qt::Horizontal; isOverlayToolBox = true; toolboxTypeName = "OverlayHorizontal"; + typeSuffix = "ToolBoxH"; break; case TOOL_BOX_OVERLAYS_VERTICAL: orientation = Qt::Vertical; isOverlayToolBox = true; toolboxTypeName = "OverlayVertical"; + typeSuffix = "ToolBoxV"; break; } + WuQMacroManager* macroManager = WuQMacroManager::instance(); + QString objectNamePrefix = (parentObjectNamePrefix + + ":" + + typeSuffix); + /* * Needed for saving and restoring window state in main window + * CHANGING THIS WILL BREAK SCENES !!! */ CaretAssert(toolboxTypeName.length() > 0); setObjectName("BrainBrowserWindowOrientedToolBox_" @@ -152,6 +169,9 @@ BrainBrowserWindowOrientedToolBox::BrainBrowserWindowOrientedToolBox(const int32 break; } #endif + m_tabWidget->setObjectName(objectNamePrefix + + ":Tab"); + macroManager->addMacroSupportToObjectWithToolTip(m_tabWidget, ""); m_annotationTabIndex = -1; m_borderTabIndex = -1; @@ -167,47 +187,57 @@ BrainBrowserWindowOrientedToolBox::BrainBrowserWindowOrientedToolBox(const int32 if (isOverlayToolBox) { m_overlaySetViewController = new OverlaySetViewController(orientation, - browserWindowIndex, - this); + browserWindowIndex, + objectNamePrefix, + this); m_overlayTabIndex = addToTabWidget(m_overlaySetViewController, "Layers"); } if (isOverlayToolBox) { m_chartOverlaySetViewController = new ChartTwoOverlaySetViewController(orientation, - browserWindowIndex, - this); + browserWindowIndex, + objectNamePrefix, + this); m_chartOverlayTabIndex = addToTabWidget(m_chartOverlaySetViewController, "Chart Layers"); } if (isOverlayToolBox) { m_chartToolBoxViewController = new ChartToolBoxViewController(orientation, browserWindowIndex, + objectNamePrefix, this); m_chartTabIndex = addToTabWidget(m_chartToolBoxViewController, "Charting"); } if (isOverlayToolBox) { - m_connectivityMatrixViewController = new CiftiConnectivityMatrixViewController(orientation, + m_connectivityMatrixViewController = new CiftiConnectivityMatrixViewController(objectNamePrefix, this); m_connectivityTabIndex = addToTabWidget(m_connectivityMatrixViewController, "Connectivity"); } if (isFeaturesToolBox) { m_annotationViewController = new AnnotationSelectionViewController(browserWindowIndex, + objectNamePrefix, this); m_annotationTextSubstitutionViewController = new AnnotationTextSubstitutionViewController(browserWindowIndex, + objectNamePrefix, this); m_annotationTabWidget = new QTabWidget(); m_annotationTabWidget->addTab(m_annotationViewController, "Annotations"); m_annotationTabWidget->addTab(m_annotationTextSubstitutionViewController, "Substitutions"); + m_annotationTabWidget->setObjectName(objectNamePrefix + + ":AnnotationTab"); + macroManager->addMacroSupportToObjectWithToolTip(m_annotationTabWidget, ""); + m_annotationTabIndex = addToTabWidget(m_annotationTabWidget, "Annot"); m_annotationTabWidget->setCurrentIndex(0); } if (isFeaturesToolBox) { m_borderSelectionViewController = new BorderSelectionViewController(browserWindowIndex, - this); + objectNamePrefix, + this); m_borderTabIndex = addToTabWidget(m_borderSelectionViewController, "Borders"); } @@ -221,6 +251,7 @@ BrainBrowserWindowOrientedToolBox::BrainBrowserWindowOrientedToolBox(const int32 if (isFeaturesToolBox) { m_fociSelectionViewController = new FociSelectionViewController(browserWindowIndex, + objectNamePrefix, this); m_fociTabIndex = addToTabWidget(m_fociSelectionViewController, "Foci"); @@ -228,6 +259,7 @@ BrainBrowserWindowOrientedToolBox::BrainBrowserWindowOrientedToolBox(const int32 if (isFeaturesToolBox) { m_imageSelectionViewController = new ImageSelectionViewController(browserWindowIndex, + objectNamePrefix, this); m_imageTabIndex = addToTabWidget(m_imageSelectionViewController, "Images"); @@ -235,6 +267,7 @@ BrainBrowserWindowOrientedToolBox::BrainBrowserWindowOrientedToolBox(const int32 if (isFeaturesToolBox) { m_labelSelectionViewController = new LabelSelectionViewController(browserWindowIndex, + objectNamePrefix, this); m_labelTabIndex = addToTabWidget(m_labelSelectionViewController, "Labels"); @@ -242,7 +275,8 @@ BrainBrowserWindowOrientedToolBox::BrainBrowserWindowOrientedToolBox(const int32 if (isOverlayToolBox) { m_volumeSurfaceOutlineSetViewController = new VolumeSurfaceOutlineSetViewController(orientation, - m_browserWindowIndex); + m_browserWindowIndex, + objectNamePrefix); m_volumeSurfaceOutlineTabIndex = addToTabWidget(m_volumeSurfaceOutlineSetViewController, "Vol/Surf Outline"); } diff --git a/src/GuiQt/BrainBrowserWindowOrientedToolBox.h b/src/GuiQt/BrainBrowserWindowOrientedToolBox.h index 726ab312c..b051b2b38 100644 --- a/src/GuiQt/BrainBrowserWindowOrientedToolBox.h +++ b/src/GuiQt/BrainBrowserWindowOrientedToolBox.h @@ -56,9 +56,10 @@ namespace caret { }; BrainBrowserWindowOrientedToolBox(const int32_t browserWindowIndex, - const QString& title, - const ToolBoxType toolBoxType, - QWidget* parent = 0); + const QString& title, + const ToolBoxType toolBoxType, + const QString& parentObjectName, + QWidget* parent = 0); ~BrainBrowserWindowOrientedToolBox(); diff --git a/src/GuiQt/BrainBrowserWindowToolBar.cxx b/src/GuiQt/BrainBrowserWindowToolBar.cxx index 0f4a50386..2d44a4bc2 100644 --- a/src/GuiQt/BrainBrowserWindowToolBar.cxx +++ b/src/GuiQt/BrainBrowserWindowToolBar.cxx @@ -147,6 +147,8 @@ using namespace caret; * Action to show layers tool box. * @param toolBarLockWindowAndAllTabAspectRatioButton * Button to lock window's aspect ratio and aspect ratio of all tabs. + * @param objectNamePrefix + * Prefix for object name * @param parent * Parent for this toolbar. */ @@ -155,6 +157,7 @@ BrainBrowserWindowToolBar::BrainBrowserWindowToolBar(const int32_t browserWindow QAction* overlayToolBoxAction, QAction* layersToolBoxAction, QToolButton* toolBarLockWindowAndAllTabAspectRatioButton, + const QString& objectNamePrefix, BrainBrowserWindow* parentBrainBrowserWindow) : QToolBar(parentBrainBrowserWindow) { @@ -178,8 +181,7 @@ BrainBrowserWindowToolBar::BrainBrowserWindowToolBar(const int32_t browserWindow /* * Needed for saving and restoring window state in main window */ - m_objectNamePrefix = ("Window_" - + QString::number(this->browserWindowIndex + 1) + m_objectNamePrefix = (objectNamePrefix + ":ToolBar"); setObjectName(m_objectNamePrefix); @@ -237,6 +239,11 @@ BrainBrowserWindowToolBar::BrainBrowserWindowToolBar(const int32_t browserWindow this, SLOT(tabCloseSelected(int))); QObject::connect(this->tabBar, SIGNAL(tabMoved(int,int)), this, SLOT(tabMoved(int,int))); + this->tabBar->setObjectName(m_objectNamePrefix + + ":Tab"); + + WuQMacroManager::instance()->addMacroSupportToObjectWithToolTip(qobject_cast(this->tabBar), + "Select Tab"); /* * Add context menu @@ -304,6 +311,9 @@ BrainBrowserWindowToolBar::BrainBrowserWindowToolBar(const int32_t browserWindow this->toolBarToolButtonAction->blockSignals(false); QToolButton* toolBarToolButton = new QToolButton(); toolBarToolButton->setDefaultAction(this->toolBarToolButtonAction); + toolBarToolButtonAction->setObjectName(m_objectNamePrefix + + ":ShowToolBar"); + WuQMacroManager::instance()->addMacroSupportToObject(toolBarToolButtonAction); /* * Toolbox control at right of the tab bar @@ -1926,12 +1936,45 @@ BrainBrowserWindowToolBar::updateToolBarComponents(BrowserTabContent* browserTab QWidget* BrainBrowserWindowToolBar::createViewWidget() { + WuQMacroManager* macroManager = WuQMacroManager::instance(); + const QString objectNamePrefix(m_objectNamePrefix + + ":ViewMode"); + this->viewModeChartOneRadioButton = new QRadioButton("Chart Old"); + this->viewModeChartOneRadioButton->setToolTip("Show Old Chart View"); + this->viewModeChartOneRadioButton->setObjectName(objectNamePrefix + + ":ChartOld"); + macroManager->addMacroSupportToObject(this->viewModeChartOneRadioButton); + this->viewModeChartTwoRadioButton = new QRadioButton("Chart"); + this->viewModeChartTwoRadioButton->setToolTip("Show Chart View"); + this->viewModeChartTwoRadioButton->setObjectName(objectNamePrefix + + ":Chart"); + macroManager->addMacroSupportToObject(this->viewModeChartTwoRadioButton); + this->viewModeSurfaceRadioButton = new QRadioButton("Surface"); + this->viewModeSurfaceRadioButton->setToolTip("Show Surace View"); + this->viewModeSurfaceRadioButton->setObjectName(objectNamePrefix + + ":Surface"); + macroManager->addMacroSupportToObject(this->viewModeSurfaceRadioButton); + this->viewModeSurfaceMontageRadioButton = new QRadioButton("Montage"); + this->viewModeSurfaceMontageRadioButton->setToolTip("Show Montage View"); + this->viewModeSurfaceMontageRadioButton->setObjectName(objectNamePrefix + + ":Montage"); + macroManager->addMacroSupportToObject(this->viewModeSurfaceMontageRadioButton); + this->viewModeVolumeRadioButton = new QRadioButton("Volume"); + this->viewModeVolumeRadioButton->setToolTip("Show Volume View"); + this->viewModeVolumeRadioButton->setObjectName(objectNamePrefix + + ":Volume"); + macroManager->addMacroSupportToObject(this->viewModeVolumeRadioButton); + this->viewModeWholeBrainRadioButton = new QRadioButton("All"); + this->viewModeWholeBrainRadioButton->setToolTip("Show All View"); + this->viewModeWholeBrainRadioButton->setObjectName(objectNamePrefix + + ":All"); + macroManager->addMacroSupportToObject(this->viewModeWholeBrainRadioButton); QWidget* widget = new QWidget(); QVBoxLayout* layout = new QVBoxLayout(widget); @@ -2075,7 +2118,7 @@ BrainBrowserWindowToolBar::createOrientationWidget() this->orientationLeftOrLateralToolButtonAction->setIconText("L"); } this->orientationLeftOrLateralToolButtonAction->setObjectName(objectNamePrefix - + "Left_Or_LateralView"); + + "LeftOrLateralView"); macroManager->addMacroSupportToObject(this->orientationLeftOrLateralToolButtonAction); this->orientationRightOrMedialToolButtonAction = WuQtUtilities::createAction("R", @@ -2090,7 +2133,7 @@ BrainBrowserWindowToolBar::createOrientationWidget() this->orientationRightOrMedialToolButtonAction->setIconText("R"); } this->orientationRightOrMedialToolButtonAction->setObjectName(objectNamePrefix - + "Right_Or_Medial_View"); + + "RightOrMedialView"); macroManager->addMacroSupportToObject(this->orientationRightOrMedialToolButtonAction); this->orientationAnteriorToolButtonAction = WuQtUtilities::createAction("A", @@ -2105,7 +2148,7 @@ BrainBrowserWindowToolBar::createOrientationWidget() this->orientationAnteriorToolButtonAction->setIconText("A"); } this->orientationAnteriorToolButtonAction->setObjectName(objectNamePrefix - + "Anterior_View"); + + "AnteriorView"); macroManager->addMacroSupportToObject(this->orientationAnteriorToolButtonAction); this->orientationPosteriorToolButtonAction = WuQtUtilities::createAction("P", @@ -2120,7 +2163,7 @@ BrainBrowserWindowToolBar::createOrientationWidget() this->orientationPosteriorToolButtonAction->setIconText("P"); } this->orientationPosteriorToolButtonAction->setObjectName(objectNamePrefix - + "Posterior_View"); + + "PosteriorView"); macroManager->addMacroSupportToObject(this->orientationPosteriorToolButtonAction); this->orientationDorsalToolButtonAction = WuQtUtilities::createAction("D", @@ -2135,7 +2178,7 @@ BrainBrowserWindowToolBar::createOrientationWidget() this->orientationDorsalToolButtonAction->setIconText("D"); } this->orientationDorsalToolButtonAction->setObjectName(objectNamePrefix - + "Dorsal_View"); + + "DorsalView"); macroManager->addMacroSupportToObject(this->orientationDorsalToolButtonAction); this->orientationVentralToolButtonAction = WuQtUtilities::createAction("V", @@ -2150,7 +2193,7 @@ BrainBrowserWindowToolBar::createOrientationWidget() this->orientationVentralToolButtonAction->setIconText("V"); } this->orientationVentralToolButtonAction->setObjectName(objectNamePrefix - + "Ventral_View"); + + "VentralView"); macroManager->addMacroSupportToObject(this->orientationVentralToolButtonAction); @@ -2160,7 +2203,7 @@ BrainBrowserWindowToolBar::createOrientationWidget() this, SLOT(orientationLateralMedialToolButtonTriggered(bool))); this->orientationLateralMedialToolButtonAction->setObjectName(objectNamePrefix - + "Lateral_Medial_View"); + + "LateralMedialView"); macroManager->addMacroSupportToObject(this->orientationLateralMedialToolButtonAction); this->orientationDorsalVentralToolButtonAction = WuQtUtilities::createAction("DV", @@ -2169,7 +2212,7 @@ BrainBrowserWindowToolBar::createOrientationWidget() this, SLOT(orientationDorsalVentralToolButtonTriggered(bool))); this->orientationDorsalVentralToolButtonAction->setObjectName(objectNamePrefix - + "Dorsal_Ventral_View"); + + "DorsalVentralView"); macroManager->addMacroSupportToObject(this->orientationDorsalVentralToolButtonAction); this->orientationAnteriorPosteriorToolButtonAction = WuQtUtilities::createAction("AP", @@ -2178,7 +2221,7 @@ BrainBrowserWindowToolBar::createOrientationWidget() this, SLOT(orientationAnteriorPosteriorToolButtonTriggered(bool))); this->orientationAnteriorPosteriorToolButtonAction->setObjectName(objectNamePrefix - + "Anterior_Posterior_View"); + + "AnteriorPosteriorView"); macroManager->addMacroSupportToObject(this->orientationAnteriorPosteriorToolButtonAction); this->orientationResetToolButtonAction = WuQtUtilities::createAction("R\nE\nS\nE\nT", @@ -2187,7 +2230,7 @@ BrainBrowserWindowToolBar::createOrientationWidget() this, SLOT(orientationResetToolButtonTriggered(bool))); this->orientationResetToolButtonAction->setObjectName(objectNamePrefix - + "Reset_View"); + + "ResetView"); macroManager->addMacroSupportToObject(this->orientationResetToolButtonAction); this->orientationLeftOrLateralToolButton = new QToolButton(); @@ -2449,12 +2492,18 @@ BrainBrowserWindowToolBar::updateOrientationWidget(BrowserTabContent* browserTab QWidget* BrainBrowserWindowToolBar::createWholeBrainSurfaceOptionsWidget() { + WuQMacroManager* macroManager = WuQMacroManager::instance(); + const QString objectNamePrefix(m_objectNamePrefix + + ":All:"); this->wholeBrainSurfaceTypeComboBox = WuQFactory::newComboBox(); WuQtUtilities::setToolTipAndStatusTip(this->wholeBrainSurfaceTypeComboBox, "Select the geometric type of surface for display"); QObject::connect(this->wholeBrainSurfaceTypeComboBox, SIGNAL(currentIndexChanged(int)), this, SLOT(wholeBrainSurfaceTypeComboBoxIndexChanged(int))); + this->wholeBrainSurfaceTypeComboBox->setObjectName(objectNamePrefix + + "SurfaceType"); + macroManager->addMacroSupportToObject(this->wholeBrainSurfaceTypeComboBox); /* * Left @@ -2464,15 +2513,21 @@ BrainBrowserWindowToolBar::createWholeBrainSurfaceOptionsWidget() "Enable/Disable display of the left cortical surface"); QObject::connect(this->wholeBrainSurfaceLeftCheckBox, SIGNAL(stateChanged(int)), this, SLOT(wholeBrainSurfaceLeftCheckBoxStateChanged(int))); + this->wholeBrainSurfaceLeftCheckBox->setObjectName(objectNamePrefix + + "EnableLeft"); + macroManager->addMacroSupportToObject(this->wholeBrainSurfaceLeftCheckBox); - QAction* leftSurfaceAction = WuQtUtilities::createAction("Left", + QToolButton* wholeBrainLeftSurfaceToolButton = new QToolButton(); + QAction* leftSurfaceAction = WuQtUtilities::createAction("Left", "Select the whole brain left surface", - this, + wholeBrainLeftSurfaceToolButton, this, SLOT(wholeBrainSurfaceLeftToolButtonTriggered(bool))); - QToolButton* wholeBrainLeftSurfaceToolButton = new QToolButton(); WuQtUtilities::setToolButtonStyleForQt5Mac(wholeBrainLeftSurfaceToolButton); wholeBrainLeftSurfaceToolButton->setDefaultAction(leftSurfaceAction); + leftSurfaceAction->setObjectName(objectNamePrefix + + "SelectLeft"); + macroManager->addMacroSupportToObject(leftSurfaceAction); /* * Right @@ -2482,15 +2537,21 @@ BrainBrowserWindowToolBar::createWholeBrainSurfaceOptionsWidget() "Enable/Disable display of the right cortical surface"); QObject::connect(this->wholeBrainSurfaceRightCheckBox, SIGNAL(stateChanged(int)), this, SLOT(wholeBrainSurfaceRightCheckBoxStateChanged(int))); + this->wholeBrainSurfaceRightCheckBox->setObjectName(objectNamePrefix + + "EnableRight"); + macroManager->addMacroSupportToObject(this->wholeBrainSurfaceRightCheckBox); - QAction* rightSurfaceAction = WuQtUtilities::createAction("Right", + QToolButton* wholeBrainRightSurfaceToolButton = new QToolButton(); + QAction* rightSurfaceAction = WuQtUtilities::createAction("Right", "Select the whole brain right surface", - this, + wholeBrainRightSurfaceToolButton, this, SLOT(wholeBrainSurfaceRightToolButtonTriggered(bool))); - QToolButton* wholeBrainRightSurfaceToolButton = new QToolButton(); WuQtUtilities::setToolButtonStyleForQt5Mac(wholeBrainRightSurfaceToolButton); wholeBrainRightSurfaceToolButton->setDefaultAction(rightSurfaceAction); + rightSurfaceAction->setObjectName(objectNamePrefix + + "SelectRight"); + macroManager->addMacroSupportToObject(rightSurfaceAction); /* * Cerebellum @@ -2500,15 +2561,21 @@ BrainBrowserWindowToolBar::createWholeBrainSurfaceOptionsWidget() "Enable/Disable display of the cerebellum surface"); QObject::connect(this->wholeBrainSurfaceCerebellumCheckBox, SIGNAL(stateChanged(int)), this, SLOT(wholeBrainSurfaceCerebellumCheckBoxStateChanged(int))); + this->wholeBrainSurfaceCerebellumCheckBox->setObjectName(objectNamePrefix + + "EnableCerebellum"); + macroManager->addMacroSupportToObject(this->wholeBrainSurfaceCerebellumCheckBox); - QAction* cerebellumSurfaceAction = WuQtUtilities::createAction("Cerebellum", + QToolButton* wholeBrainCerebellumSurfaceToolButton = new QToolButton(); + QAction* cerebellumSurfaceAction = WuQtUtilities::createAction("Cerebellum", "Select the whole brain cerebellum surface", - this, + wholeBrainCerebellumSurfaceToolButton, this, SLOT(wholeBrainSurfaceCerebellumToolButtonTriggered(bool))); - QToolButton* wholeBrainCerebellumSurfaceToolButton = new QToolButton(); WuQtUtilities::setToolButtonStyleForQt5Mac(wholeBrainCerebellumSurfaceToolButton); wholeBrainCerebellumSurfaceToolButton->setDefaultAction(cerebellumSurfaceAction); + cerebellumSurfaceAction->setObjectName(objectNamePrefix + + "SurfaceCerebellum"); + macroManager->addMacroSupportToObject(cerebellumSurfaceAction); /* * Left/Right separation @@ -2523,6 +2590,9 @@ BrainBrowserWindowToolBar::createWholeBrainSurfaceOptionsWidget() "Adjust the separation of the left and right cortical surfaces"); QObject::connect(this->wholeBrainSurfaceSeparationLeftRightSpinBox, SIGNAL(valueChanged(double)), this, SLOT(wholeBrainSurfaceSeparationLeftRightSpinBoxValueChanged(double))); + this->wholeBrainSurfaceSeparationLeftRightSpinBox->setObjectName(objectNamePrefix + + "LeftRightSeparation"); + macroManager->addMacroSupportToObject(this->wholeBrainSurfaceSeparationLeftRightSpinBox); /* * Cerebellum separation @@ -2536,6 +2606,9 @@ BrainBrowserWindowToolBar::createWholeBrainSurfaceOptionsWidget() "Adjust the separation of the cerebellum from the left and right cortical surfaces"); QObject::connect(this->wholeBrainSurfaceSeparationCerebellumSpinBox, SIGNAL(valueChanged(double)), this, SLOT(wholeBrainSurfaceSeparationCerebellumSpinBoxSelected(double))); + this->wholeBrainSurfaceSeparationCerebellumSpinBox->setObjectName(objectNamePrefix + + "CortexCerebellumSeparation"); + macroManager->addMacroSupportToObject(this->wholeBrainSurfaceSeparationCerebellumSpinBox); @@ -2659,7 +2732,8 @@ BrainBrowserWindowToolBar::updateWholeBrainSurfaceOptionsWidget(BrowserTabConten QWidget* BrainBrowserWindowToolBar::createVolumeIndicesWidget() { - m_sliceSelectionComponent = new BrainBrowserWindowToolBarSliceSelection(this); + m_sliceSelectionComponent = new BrainBrowserWindowToolBarSliceSelection(this, + m_objectNamePrefix); QWidget* w = this->createToolWidget("Slice Indices/Coords", m_sliceSelectionComponent, WIDGET_PLACEMENT_LEFT, @@ -2702,6 +2776,10 @@ BrainBrowserWindowToolBar::createModeWidget() QToolButton* inputModeAnnotationsToolButton = new QToolButton(); inputModeAnnotationsToolButton->setDefaultAction(this->modeInputModeAnnotationsAction); WuQtUtilities::setToolButtonStyleForQt5Mac(inputModeAnnotationsToolButton); + this->modeInputModeAnnotationsAction->setObjectName(m_objectNamePrefix + + ":Mode:Annotate"); + WuQMacroManager::instance()->addMacroSupportToObject(this->modeInputModeAnnotationsAction); + /* * Borders @@ -2713,6 +2791,9 @@ BrainBrowserWindowToolBar::createModeWidget() QToolButton* inputModeBordersToolButton = new QToolButton(); inputModeBordersToolButton->setDefaultAction(this->modeInputModeBordersAction); WuQtUtilities::setToolButtonStyleForQt5Mac(inputModeBordersToolButton); + this->modeInputModeBordersAction->setObjectName(m_objectNamePrefix + + ":Mode:Border"); + WuQMacroManager::instance()->addMacroSupportToObject(this->modeInputModeBordersAction); /* * Foci @@ -2724,6 +2805,9 @@ BrainBrowserWindowToolBar::createModeWidget() QToolButton* inputModeFociToolButton = new QToolButton(); inputModeFociToolButton->setDefaultAction(this->modeInputModeFociAction); WuQtUtilities::setToolButtonStyleForQt5Mac(inputModeFociToolButton); + this->modeInputModeFociAction->setObjectName(m_objectNamePrefix + + ":Mode:Foci"); + WuQMacroManager::instance()->addMacroSupportToObject(this->modeInputModeFociAction); /* * Image @@ -2739,6 +2823,9 @@ BrainBrowserWindowToolBar::createModeWidget() inputModeImageToolButton = new QToolButton(); inputModeImageToolButton->setDefaultAction(this->modeInputModeImageAction); WuQtUtilities::setToolButtonStyleForQt5Mac(inputModeImageToolButton); + this->modeInputModeImageAction->setObjectName(m_objectNamePrefix + + ":Mode:Image"); + WuQMacroManager::instance()->addMacroSupportToObject(this->modeInputModeImageAction); } /* @@ -2751,6 +2838,9 @@ BrainBrowserWindowToolBar::createModeWidget() QToolButton* inputModeVolumeEditButton = new QToolButton(); inputModeVolumeEditButton->setDefaultAction(this->modeInputVolumeEditAction); WuQtUtilities::setToolButtonStyleForQt5Mac(inputModeVolumeEditButton); + this->modeInputVolumeEditAction->setObjectName(m_objectNamePrefix + + ":Mode:Volume"); + WuQMacroManager::instance()->addMacroSupportToObject(this->modeInputVolumeEditAction); /* * View Mode @@ -2776,6 +2866,9 @@ BrainBrowserWindowToolBar::createModeWidget() QToolButton* inputModeViewToolButton = new QToolButton(); inputModeViewToolButton->setDefaultAction(this->modeInputModeViewAction); WuQtUtilities::setToolButtonStyleForQt5Mac(inputModeViewToolButton); + this->modeInputModeViewAction->setObjectName(m_objectNamePrefix + + ":Mode:View"); + WuQMacroManager::instance()->addMacroSupportToObject(this->modeInputModeViewAction); WuQtUtilities::matchWidgetWidths(inputModeAnnotationsToolButton, inputModeBordersToolButton, @@ -2824,6 +2917,10 @@ BrainBrowserWindowToolBar::createModeWidget() QObject::connect(this->modeInputModeActionGroup, SIGNAL(triggered(QAction*)), this, SLOT(modeInputModeActionTriggered(QAction*))); this->modeInputModeActionGroup->setExclusive(true); +// this->modeInputModeActionGroup->setObjectName(m_objectNamePrefix +// + ":Mode_Action_Group"); +// WuQMacroManager::instance()->addMacroSupportToObject(this->modeInputModeActionGroup, +// "Selects Mode for Mouse Operations"); QWidget* widget = new QWidget(); QVBoxLayout* layout = new QVBoxLayout(widget); @@ -3045,7 +3142,8 @@ BrainBrowserWindowToolBar::createTabOptionsWidget(QToolButton* toolBarLockWindow { m_tabOptionsComponent = new BrainBrowserWindowToolBarTab(this->browserWindowIndex, toolBarLockWindowAndAllTabAspectRatioButton, - this); + this, + m_objectNamePrefix); QWidget* w = this->createToolWidget("Tab", m_tabOptionsComponent, @@ -3081,7 +3179,8 @@ BrainBrowserWindowToolBar::updateTabOptionsWidget(BrowserTabContent* browserTabC QWidget* BrainBrowserWindowToolBar::createChartTypeWidget() { - m_chartTypeToolBarComponent = new BrainBrowserWindowToolBarChartType(this); + m_chartTypeToolBarComponent = new BrainBrowserWindowToolBarChartType(this, + m_objectNamePrefix); QWidget* w = this->createToolWidget("Chart Type (OLD)", m_chartTypeToolBarComponent, WIDGET_PLACEMENT_LEFT, @@ -3116,7 +3215,8 @@ BrainBrowserWindowToolBar::updateChartTypeWidget(BrowserTabContent* browserTabCo QWidget* BrainBrowserWindowToolBar::createChartTwoTitleWidget() { - m_chartTwoTitleToolBarComponent = new BrainBrowserWindowToolBarChartTwoTitle(this); + m_chartTwoTitleToolBarComponent = new BrainBrowserWindowToolBarChartTwoTitle(this, + m_objectNamePrefix); QWidget* w = this->createToolWidget("Chart Title", m_chartTwoTitleToolBarComponent, WIDGET_PLACEMENT_LEFT, @@ -3151,7 +3251,8 @@ BrainBrowserWindowToolBar::updateChartTwoTitleWidget(BrowserTabContent* browserT QWidget* BrainBrowserWindowToolBar::createChartTypeTwoWidget() { - m_chartTwoTypeToolBarComponent = new BrainBrowserWindowToolBarChartTwoType(this); + m_chartTwoTypeToolBarComponent = new BrainBrowserWindowToolBarChartTwoType(this, + m_objectNamePrefix); QWidget* w = this->createToolWidget("Chart Type", m_chartTwoTypeToolBarComponent, WIDGET_PLACEMENT_LEFT, @@ -3186,7 +3287,8 @@ BrainBrowserWindowToolBar::updateChartTypeTwoWidget(BrowserTabContent* browserTa QWidget* BrainBrowserWindowToolBar::createChartAxesWidget() { - m_chartAxisToolBarComponent = new BrainBrowserWindowToolBarChartAxes(this); + m_chartAxisToolBarComponent = new BrainBrowserWindowToolBarChartAxes(this, + m_objectNamePrefix); QWidget* w = this->createToolWidget("Chart Axes", m_chartAxisToolBarComponent, WIDGET_PLACEMENT_LEFT, @@ -3221,7 +3323,8 @@ BrainBrowserWindowToolBar::updateChartAxesWidget(BrowserTabContent* browserTabCo QWidget* BrainBrowserWindowToolBar::createChartAttributesWidget() { - m_chartAttributesToolBarComponent = new BrainBrowserWindowToolBarChartAttributes(this); + m_chartAttributesToolBarComponent = new BrainBrowserWindowToolBarChartAttributes(this, + m_objectNamePrefix); QWidget* w = this->createToolWidget("Chart Attributes", m_chartAttributesToolBarComponent, WIDGET_PLACEMENT_LEFT, @@ -3255,7 +3358,8 @@ BrainBrowserWindowToolBar::updateChartAttributesWidget(BrowserTabContent* browse QWidget* BrainBrowserWindowToolBar::createChartTwoOrientationWidget() { - m_chartTwoOrientationToolBarComponent = new BrainBrowserWindowToolBarChartTwoOrientation(this); + m_chartTwoOrientationToolBarComponent = new BrainBrowserWindowToolBarChartTwoOrientation(this, + m_objectNamePrefix); QWidget* w = this->createToolWidget("Chart
        Orientation", m_chartTwoOrientationToolBarComponent, WIDGET_PLACEMENT_LEFT, @@ -3289,7 +3393,8 @@ BrainBrowserWindowToolBar::updateChartTwoOrientationWidget(BrowserTabContent* br QWidget* BrainBrowserWindowToolBar::createChartTwoAttributesWidget() { - this->m_chartTwoAttributesToolBarComponent = new BrainBrowserWindowToolBarChartTwoAttributes(this); + this->m_chartTwoAttributesToolBarComponent = new BrainBrowserWindowToolBarChartTwoAttributes(this, + m_objectNamePrefix); QWidget* w = this->createToolWidget("Chart
        Attributes", this->m_chartTwoAttributesToolBarComponent, WIDGET_PLACEMENT_LEFT, @@ -3323,7 +3428,8 @@ BrainBrowserWindowToolBar::updateChartTwoAttributesWidget(BrowserTabContent* bro QWidget* BrainBrowserWindowToolBar::createChartTwoAxesWidget() { - this->m_chartTwoAxesToolBarComponent = new BrainBrowserWindowToolBarChartTwoAxes(this); + this->m_chartTwoAxesToolBarComponent = new BrainBrowserWindowToolBarChartTwoAxes(this, + m_objectNamePrefix); QWidget* w = this->createToolWidget("Chart Axes", this->m_chartTwoAxesToolBarComponent, WIDGET_PLACEMENT_LEFT, @@ -3358,7 +3464,13 @@ BrainBrowserWindowToolBar::createSingleSurfaceOptionsWidget() { QLabel* structureSurfaceLabel = new QLabel("Brain Structure and Surface: "); - this->surfaceSurfaceSelectionControl = new StructureSurfaceSelectionControl(false); + /* + * Note: Macro support is in StructureSurfaceSelectionControl + */ + this->surfaceSurfaceSelectionControl = new StructureSurfaceSelectionControl(false, + m_objectNamePrefix + + ":Surface", + this); QObject::connect(this->surfaceSurfaceSelectionControl, SIGNAL(selectionChanged(const StructureEnum::Enum, ModelSurface*)), @@ -3413,7 +3525,8 @@ BrainBrowserWindowToolBar::updateSingleSurfaceOptionsWidget(BrowserTabContent* b QWidget* BrainBrowserWindowToolBar::createSurfaceMontageOptionsWidget() { - m_surfaceMontageToolBarComponent = new BrainBrowserWindowToolBarSurfaceMontage(this); + m_surfaceMontageToolBarComponent = new BrainBrowserWindowToolBarSurfaceMontage(this, + m_objectNamePrefix); QWidget* w = this->createToolWidget("Montage Selection", m_surfaceMontageToolBarComponent, WIDGET_PLACEMENT_LEFT, @@ -3446,7 +3559,8 @@ QWidget* BrainBrowserWindowToolBar::createClippingOptionsWidget() { m_clippingToolBarComponent = new BrainBrowserWindowToolBarClipping(this->browserWindowIndex, - this); + this, + m_objectNamePrefix); QWidget* w = this->createToolWidget("Clipping", m_clippingToolBarComponent, WIDGET_PLACEMENT_LEFT, @@ -3480,7 +3594,8 @@ BrainBrowserWindowToolBar::updateClippingOptionsWidget(BrowserTabContent* browse QWidget* BrainBrowserWindowToolBar::createVolumeMontageWidget() { - m_volumeMontageComponent = new BrainBrowserWindowToolBarVolumeMontage(this); + m_volumeMontageComponent = new BrainBrowserWindowToolBarVolumeMontage(m_objectNamePrefix, + this); QWidget* w = this->createToolWidget("Montage", @@ -3516,7 +3631,8 @@ BrainBrowserWindowToolBar::updateVolumeMontageWidget(BrowserTabContent* browserT QWidget* BrainBrowserWindowToolBar::createVolumePlaneWidget() { - m_slicePlaneComponent = new BrainBrowserWindowToolBarSlicePlane(this); + m_slicePlaneComponent = new BrainBrowserWindowToolBarSlicePlane(m_objectNamePrefix, + this); QWidget* w = this->createToolWidget("Slice Plane", m_slicePlaneComponent, WIDGET_PLACEMENT_LEFT, diff --git a/src/GuiQt/BrainBrowserWindowToolBar.h b/src/GuiQt/BrainBrowserWindowToolBar.h index 90bb44a48..8229aab31 100644 --- a/src/GuiQt/BrainBrowserWindowToolBar.h +++ b/src/GuiQt/BrainBrowserWindowToolBar.h @@ -86,6 +86,7 @@ namespace caret { QAction* overlayToolBoxAction, QAction* layersToolBoxAction, QToolButton* toolBarLockWindowAndAllTabAspectRatioButton, + const QString& objectNamePrefix, BrainBrowserWindow* parentBrainBrowserWindow); ~BrainBrowserWindowToolBar(); diff --git a/src/GuiQt/BrainBrowserWindowToolBarChartAttributes.cxx b/src/GuiQt/BrainBrowserWindowToolBarChartAttributes.cxx index 425708ea2..0f711708d 100644 --- a/src/GuiQt/BrainBrowserWindowToolBarChartAttributes.cxx +++ b/src/GuiQt/BrainBrowserWindowToolBarChartAttributes.cxx @@ -47,6 +47,7 @@ #include "EventManager.h" #include "ModelChart.h" #include "WuQFactory.h" +#include "WuQMacroManager.h" #include "WuQWidgetObjectGroup.h" #include "WuQtUtilities.h" @@ -66,12 +67,18 @@ using namespace caret; * @param parentToolBar * The parent toolbar. */ -BrainBrowserWindowToolBarChartAttributes::BrainBrowserWindowToolBarChartAttributes(BrainBrowserWindowToolBar* parentToolBar) +BrainBrowserWindowToolBarChartAttributes::BrainBrowserWindowToolBarChartAttributes(BrainBrowserWindowToolBar* parentToolBar, + const QString& parentObjectName) : BrainBrowserWindowToolBarComponent(parentToolBar) { - m_cartesianChartAttributesWidget = new CartesianChartAttributesWidget(this); + const QString objectNamePrefix(parentObjectName + + ":ChartOneAttributes"); - m_matrixChartAttributesWidget = new MatrixChartAttributesWidget(this); + m_cartesianChartAttributesWidget = new CartesianChartAttributesWidget(this, + objectNamePrefix); + + m_matrixChartAttributesWidget = new MatrixChartAttributesWidget(this, + objectNamePrefix); m_stackedWidget = new QStackedWidget(); m_stackedWidget->addWidget(m_cartesianChartAttributesWidget); @@ -237,7 +244,8 @@ BrainBrowserWindowToolBarChartAttributes::updateGraphics() * @param brainBrowserWindowToolBarChartAttributes * The parent attributes widget. */ -CartesianChartAttributesWidget::CartesianChartAttributesWidget(BrainBrowserWindowToolBarChartAttributes* brainBrowserWindowToolBarChartAttributes) +CartesianChartAttributesWidget::CartesianChartAttributesWidget(BrainBrowserWindowToolBarChartAttributes* brainBrowserWindowToolBarChartAttributes, + const QString& parentObjectName) : QWidget(brainBrowserWindowToolBarChartAttributes) { m_brainBrowserWindowToolBarChartAttributes = brainBrowserWindowToolBarChartAttributes; @@ -250,6 +258,10 @@ CartesianChartAttributesWidget::CartesianChartAttributesWidget(BrainBrowserWindo this, SLOT(cartesianLineWidthValueChanged(double))); m_cartesianLineWidthDoubleSpinBox->setFixedWidth(65); + m_cartesianLineWidthDoubleSpinBox->setToolTip("Set line width"); + m_cartesianLineWidthDoubleSpinBox->setObjectName(parentObjectName + + ":LineWidth"); + WuQMacroManager::instance()->addMacroSupportToObject(m_cartesianLineWidthDoubleSpinBox); QGridLayout* gridLayout = new QGridLayout(this); WuQtUtilities::setLayoutSpacingAndMargins(gridLayout, 0, 0); @@ -319,7 +331,8 @@ CartesianChartAttributesWidget::cartesianLineWidthValueChanged(double value) * @param brainBrowserWindowToolBarChartAttributes * The parent attributes widget. */ -MatrixChartAttributesWidget::MatrixChartAttributesWidget(BrainBrowserWindowToolBarChartAttributes* brainBrowserWindowToolBarChartAttributes) +MatrixChartAttributesWidget::MatrixChartAttributesWidget(BrainBrowserWindowToolBarChartAttributes* brainBrowserWindowToolBarChartAttributes, + const QString& parentObjectName) : QWidget(brainBrowserWindowToolBarChartAttributes), EventListenerInterface() { @@ -333,6 +346,10 @@ EventListenerInterface() this, SLOT(cellWidthSpinBoxValueChanged(double))); m_cellWidthSpinBox->setKeyboardTracking(true); + m_cellWidthSpinBox->setToolTip("Set Cell Width"); + m_cellWidthSpinBox->setObjectName(parentObjectName + + ":Matrix:CellWidth"); + WuQMacroManager::instance()->addMacroSupportToObject(m_cellWidthSpinBox); QLabel* cellHeightLabel = new QLabel("Cell Height"); m_cellHeightSpinBox = WuQFactory::newDoubleSpinBoxWithMinMaxStepDecimalsSignalDouble(1.0, @@ -342,6 +359,10 @@ EventListenerInterface() this, SLOT(cellHeightSpinBoxValueChanged(double))); m_cellHeightSpinBox->setKeyboardTracking(true); + m_cellHeightSpinBox->setToolTip("Set Cell Height"); + m_cellHeightSpinBox->setObjectName(parentObjectName + + ":Matrix:CellHeight"); + WuQMacroManager::instance()->addMacroSupportToObject(m_cellHeightSpinBox); QAction* resetButtonAction = WuQtUtilities::createAction("Reset", "Reset panning (SHIFT-mouse),zooming (CTRL-mouse), and scale matrix to fit window", @@ -351,6 +372,9 @@ EventListenerInterface() QToolButton* resetToolButton = new QToolButton(); resetToolButton->setDefaultAction(resetButtonAction); WuQtUtilities::setToolButtonStyleForQt5Mac(resetToolButton); + resetToolButton->setObjectName(parentObjectName + + ":Matrix:ResetButton"); + WuQMacroManager::instance()->addMacroSupportToObject(resetToolButton); WuQtUtilities::matchWidgetWidths(m_cellHeightSpinBox, m_cellWidthSpinBox); @@ -358,10 +382,18 @@ EventListenerInterface() m_highlightSelectionCheckBox = new QCheckBox("Highlight Selection"); QObject::connect(m_highlightSelectionCheckBox, SIGNAL(clicked(bool)), this, SLOT(highlightSelectionCheckBoxClicked(bool))); + m_highlightSelectionCheckBox->setToolTip("Enable selected row/column highlight"); + m_highlightSelectionCheckBox->setObjectName(parentObjectName + + ":Matrix:EnableHighlight"); + WuQMacroManager::instance()->addMacroSupportToObject(m_highlightSelectionCheckBox); m_displayGridLinesCheckBox = new QCheckBox("Show Grid Outline"); QObject::connect(m_displayGridLinesCheckBox, SIGNAL(clicked(bool)), this, SLOT(displayGridLinesCheckBoxClicked(bool))); + m_displayGridLinesCheckBox->setToolTip("Show Grid Outline around matrix cells"); + m_displayGridLinesCheckBox->setObjectName(parentObjectName + + ":Matrix:EnableGridOutline"); + WuQMacroManager::instance()->addMacroSupportToObject(m_displayGridLinesCheckBox); m_manualWidgetsGroup = new WuQWidgetObjectGroup(this); m_manualWidgetsGroup->add(m_cellWidthSpinBox); diff --git a/src/GuiQt/BrainBrowserWindowToolBarChartAttributes.h b/src/GuiQt/BrainBrowserWindowToolBarChartAttributes.h index 9dc7b666f..df50cbe52 100644 --- a/src/GuiQt/BrainBrowserWindowToolBarChartAttributes.h +++ b/src/GuiQt/BrainBrowserWindowToolBarChartAttributes.h @@ -42,7 +42,8 @@ namespace caret { Q_OBJECT public: - BrainBrowserWindowToolBarChartAttributes(BrainBrowserWindowToolBar* parentToolBar); + BrainBrowserWindowToolBarChartAttributes(BrainBrowserWindowToolBar* parentToolBar, + const QString& parentObjectName); virtual ~BrainBrowserWindowToolBarChartAttributes(); @@ -81,7 +82,8 @@ namespace caret { Q_OBJECT public: - CartesianChartAttributesWidget(BrainBrowserWindowToolBarChartAttributes* brainBrowserWindowToolBarChartAttributes); + CartesianChartAttributesWidget(BrainBrowserWindowToolBarChartAttributes* brainBrowserWindowToolBarChartAttributes, + const QString& parentObjectName); ~CartesianChartAttributesWidget(); @@ -105,7 +107,8 @@ namespace caret { Q_OBJECT public: - MatrixChartAttributesWidget(BrainBrowserWindowToolBarChartAttributes* brainBrowserWindowToolBarChartAttributes); + MatrixChartAttributesWidget(BrainBrowserWindowToolBarChartAttributes* brainBrowserWindowToolBarChartAttributes, + const QString& parentObjectName); ~MatrixChartAttributesWidget(); diff --git a/src/GuiQt/BrainBrowserWindowToolBarChartAxes.cxx b/src/GuiQt/BrainBrowserWindowToolBarChartAxes.cxx index f8ed112f2..d6c12d5df 100644 --- a/src/GuiQt/BrainBrowserWindowToolBarChartAxes.cxx +++ b/src/GuiQt/BrainBrowserWindowToolBarChartAxes.cxx @@ -39,6 +39,7 @@ #include "ChartModelFrequencySeries.h" #include "ChartModelTimeSeries.h" #include "ModelChart.h" +#include "WuQMacroManager.h" #include "WuQWidgetObjectGroup.h" #include "WuQtUtilities.h" @@ -58,10 +59,15 @@ using namespace caret; * @param parentToolBar * parent toolbar. */ -BrainBrowserWindowToolBarChartAxes::BrainBrowserWindowToolBarChartAxes(BrainBrowserWindowToolBar* parentToolBar) +BrainBrowserWindowToolBarChartAxes::BrainBrowserWindowToolBarChartAxes(BrainBrowserWindowToolBar* parentToolBar, + const QString& parentObjectName) : BrainBrowserWindowToolBarComponent(parentToolBar), m_parentToolBar(parentToolBar) { + const QString objectNamePrefix(parentObjectName + + ":ChartOneAxes:"); + WuQMacroManager* macroManager = WuQMacroManager::instance(); + QGridLayout* gridLayout = new QGridLayout(this); WuQtUtilities::setLayoutSpacingAndMargins(gridLayout, 0, 0); gridLayout->addWidget(new QLabel("Axis"), @@ -91,6 +97,16 @@ m_parentToolBar(parentToolBar) QObject::connect(m_bottomAxisMaximumValueSpinBox, SIGNAL(valueChanged(double)), this, SLOT(bottomAxisValueChanged(double))); + m_bottomAxisAutoRangeScaleCheckBox->setObjectName(objectNamePrefix + + "EnableBottomAxis"); + macroManager->addMacroSupportToObject(m_bottomAxisAutoRangeScaleCheckBox); + m_bottomAxisMinimumValueSpinBox->setObjectName(objectNamePrefix + + "BottomAxisMinimumValue"); + macroManager->addMacroSupportToObject(m_bottomAxisMinimumValueSpinBox); + m_bottomAxisMaximumValueSpinBox->setObjectName(objectNamePrefix + + "BottomAxisMaximumValue"); + macroManager->addMacroSupportToObject(m_bottomAxisMaximumValueSpinBox); + createAxisWidgets(gridLayout, m_leftAxisLabel, m_leftAxisAutoRangeScaleCheckBox, @@ -105,6 +121,16 @@ m_parentToolBar(parentToolBar) QObject::connect(m_leftAxisMaximumValueSpinBox, SIGNAL(valueChanged(double)), this, SLOT(leftAxisValueChanged(double))); + m_leftAxisAutoRangeScaleCheckBox->setObjectName(objectNamePrefix + + "EnableLeftAxis"); + macroManager->addMacroSupportToObject(m_leftAxisAutoRangeScaleCheckBox); + m_leftAxisMinimumValueSpinBox->setObjectName(objectNamePrefix + + "LeftAxisMinimumValue"); + macroManager->addMacroSupportToObject(m_leftAxisMinimumValueSpinBox); + m_leftAxisMaximumValueSpinBox->setObjectName(objectNamePrefix + + "LeftAxisMaximumValue"); + macroManager->addMacroSupportToObject(m_leftAxisMaximumValueSpinBox); + // createAxisWidgets(gridLayout, // m_topAxisLabel, // m_topAxisAutoRangeScaleCheckBox, diff --git a/src/GuiQt/BrainBrowserWindowToolBarChartAxes.h b/src/GuiQt/BrainBrowserWindowToolBarChartAxes.h index 1e4635a2d..600db73d9 100644 --- a/src/GuiQt/BrainBrowserWindowToolBarChartAxes.h +++ b/src/GuiQt/BrainBrowserWindowToolBarChartAxes.h @@ -42,7 +42,8 @@ namespace caret { Q_OBJECT public: - BrainBrowserWindowToolBarChartAxes(BrainBrowserWindowToolBar* parentToolBar); + BrainBrowserWindowToolBarChartAxes(BrainBrowserWindowToolBar* parentToolBar, + const QString& parentObjectName); virtual ~BrainBrowserWindowToolBarChartAxes(); diff --git a/src/GuiQt/BrainBrowserWindowToolBarChartTwoAttributes.cxx b/src/GuiQt/BrainBrowserWindowToolBarChartTwoAttributes.cxx index 17054f71d..349312b23 100644 --- a/src/GuiQt/BrainBrowserWindowToolBarChartTwoAttributes.cxx +++ b/src/GuiQt/BrainBrowserWindowToolBarChartTwoAttributes.cxx @@ -44,6 +44,7 @@ #include "EventUserInterfaceUpdate.h" #include "ModelChartTwo.h" #include "WuQFactory.h" +#include "WuQMacroManager.h" #include "WuQWidgetObjectGroup.h" #include "WuQtUtilities.h" @@ -62,13 +63,21 @@ using namespace caret; * * @param parentToolBar * The parent toolbar. + * @param parentObjectName + * Name of parent for macros */ -BrainBrowserWindowToolBarChartTwoAttributes::BrainBrowserWindowToolBarChartTwoAttributes(BrainBrowserWindowToolBar* parentToolBar) +BrainBrowserWindowToolBarChartTwoAttributes::BrainBrowserWindowToolBarChartTwoAttributes(BrainBrowserWindowToolBar* parentToolBar, + const QString& parentObjectName) : BrainBrowserWindowToolBarComponent(parentToolBar) { - m_cartesianChartAttributesWidget = new CartesianChartTwoAttributesWidget(this); + const QString objectNamePrefix(parentObjectName + + ":ChartTwoAttributes"); - m_matrixChartTwoAttributesWidget = new MatrixChartTwoAttributesWidget(this); + m_cartesianChartAttributesWidget = new CartesianChartTwoAttributesWidget(this, + objectNamePrefix); + + m_matrixChartTwoAttributesWidget = new MatrixChartTwoAttributesWidget(this, + objectNamePrefix); m_stackedWidget = new QStackedWidget(); m_stackedWidget->addWidget(m_cartesianChartAttributesWidget); @@ -162,7 +171,8 @@ BrainBrowserWindowToolBarChartTwoAttributes::updateGraphics() * @param brainBrowserWindowToolBarChartAttributes * The parent attributes widget. */ -CartesianChartTwoAttributesWidget::CartesianChartTwoAttributesWidget(BrainBrowserWindowToolBarChartTwoAttributes* brainBrowserWindowToolBarChartAttributes) +CartesianChartTwoAttributesWidget::CartesianChartTwoAttributesWidget(BrainBrowserWindowToolBarChartTwoAttributes* brainBrowserWindowToolBarChartAttributes, + const QString& parentObjectName) : QWidget(brainBrowserWindowToolBarChartAttributes) { m_brainBrowserWindowToolBarChartAttributes = brainBrowserWindowToolBarChartAttributes; @@ -175,6 +185,11 @@ CartesianChartTwoAttributesWidget::CartesianChartTwoAttributesWidget(BrainBrowse this, SLOT(cartesianLineWidthValueChanged(double))); m_cartesianLineWidthDoubleSpinBox->setFixedWidth(65); + m_cartesianLineWidthDoubleSpinBox->setToolTip("Width of line"); + m_cartesianLineWidthDoubleSpinBox->setObjectName(parentObjectName + + ":LineWidth"); + WuQMacroManager::instance()->addMacroSupportToObject(m_cartesianLineWidthDoubleSpinBox); + QGridLayout* gridLayout = new QGridLayout(this); WuQtUtilities::setLayoutSpacingAndMargins(gridLayout, 0, 0); @@ -232,7 +247,8 @@ CartesianChartTwoAttributesWidget::cartesianLineWidthValueChanged(double /*value * @param brainBrowserWindowToolBarChartAttributes * The parent attributes widget. */ -MatrixChartTwoAttributesWidget::MatrixChartTwoAttributesWidget(BrainBrowserWindowToolBarChartTwoAttributes* brainBrowserWindowToolBarChartAttributes) +MatrixChartTwoAttributesWidget::MatrixChartTwoAttributesWidget(BrainBrowserWindowToolBarChartTwoAttributes* brainBrowserWindowToolBarChartAttributes, + const QString& parentObjectName) : QWidget(brainBrowserWindowToolBarChartAttributes), EventListenerInterface() { @@ -250,6 +266,9 @@ EventListenerInterface() m_cellWidthPercentageSpinBox->setToolTip("Percentage of tab width filled with matrix"); m_cellWidthPercentageSpinBox->setKeyboardTracking(false); m_cellWidthPercentageSpinBox->setSuffix("%"); + m_cellWidthPercentageSpinBox->setObjectName(parentObjectName + + ":CellWidth"); + WuQMacroManager::instance()->addMacroSupportToObject(m_cellWidthPercentageSpinBox); QLabel* cellHeightLabel = new QLabel("Cell Height"); m_cellHeightPercentageSpinBox = WuQFactory::newDoubleSpinBoxWithMinMaxStepDecimalsSignalDouble(minPercent, @@ -261,6 +280,9 @@ EventListenerInterface() m_cellHeightPercentageSpinBox->setToolTip("Percentage of tab height filled with matrix"); m_cellHeightPercentageSpinBox->setKeyboardTracking(false); m_cellHeightPercentageSpinBox->setSuffix("%"); + m_cellHeightPercentageSpinBox->setObjectName(parentObjectName + + ":CellHeight"); + WuQMacroManager::instance()->addMacroSupportToObject(m_cellHeightPercentageSpinBox); WuQtUtilities::matchWidgetWidths(m_cellHeightPercentageSpinBox, m_cellWidthPercentageSpinBox); @@ -269,11 +291,17 @@ EventListenerInterface() m_highlightSelectionCheckBox->setToolTip("Highlight selected row/column in the matrix"); QObject::connect(m_highlightSelectionCheckBox, SIGNAL(clicked(bool)), this, SLOT(valueChanged())); + m_highlightSelectionCheckBox->setObjectName(parentObjectName + + ":HighlightSelection"); + WuQMacroManager::instance()->addMacroSupportToObject(m_highlightSelectionCheckBox); m_displayGridLinesCheckBox = new QCheckBox("Show Grid Outline"); QObject::connect(m_displayGridLinesCheckBox, SIGNAL(clicked(bool)), this, SLOT(valueChanged())); m_displayGridLinesCheckBox->setToolTip("Outline cells in the matrix"); + m_displayGridLinesCheckBox->setObjectName(parentObjectName + + ":EnableGridOutline"); + WuQMacroManager::instance()->addMacroSupportToObject(m_displayGridLinesCheckBox); m_manualWidgetsGroup = new WuQWidgetObjectGroup(this); m_manualWidgetsGroup->add(m_cellWidthPercentageSpinBox); diff --git a/src/GuiQt/BrainBrowserWindowToolBarChartTwoAttributes.h b/src/GuiQt/BrainBrowserWindowToolBarChartTwoAttributes.h index 3855496a6..f1f446f02 100644 --- a/src/GuiQt/BrainBrowserWindowToolBarChartTwoAttributes.h +++ b/src/GuiQt/BrainBrowserWindowToolBarChartTwoAttributes.h @@ -41,7 +41,8 @@ namespace caret { Q_OBJECT public: - BrainBrowserWindowToolBarChartTwoAttributes(BrainBrowserWindowToolBar* parentToolBar); + BrainBrowserWindowToolBarChartTwoAttributes(BrainBrowserWindowToolBar* parentToolBar, + const QString& parentObjectName); virtual ~BrainBrowserWindowToolBarChartTwoAttributes(); @@ -80,7 +81,8 @@ namespace caret { Q_OBJECT public: - CartesianChartTwoAttributesWidget(BrainBrowserWindowToolBarChartTwoAttributes* brainBrowserWindowToolBarChartAttributes); + CartesianChartTwoAttributesWidget(BrainBrowserWindowToolBarChartTwoAttributes* brainBrowserWindowToolBarChartAttributes, + const QString& parentObjectName); ~CartesianChartTwoAttributesWidget(); @@ -104,7 +106,8 @@ namespace caret { Q_OBJECT public: - MatrixChartTwoAttributesWidget(BrainBrowserWindowToolBarChartTwoAttributes* brainBrowserWindowToolBarChartAttributes); + MatrixChartTwoAttributesWidget(BrainBrowserWindowToolBarChartTwoAttributes* brainBrowserWindowToolBarChartAttributes, + const QString& parentObjectName); ~MatrixChartTwoAttributesWidget(); diff --git a/src/GuiQt/BrainBrowserWindowToolBarChartTwoAxes.cxx b/src/GuiQt/BrainBrowserWindowToolBarChartTwoAxes.cxx index 24e5e9d2a..50c1d8f7e 100644 --- a/src/GuiQt/BrainBrowserWindowToolBarChartTwoAxes.cxx +++ b/src/GuiQt/BrainBrowserWindowToolBarChartTwoAxes.cxx @@ -55,6 +55,7 @@ #include "WuQDataEntryDialog.h" #include "WuQFactory.h" #include "WuQDoubleSpinBox.h" +#include "WuQMacroManager.h" #include "WuQWidgetObjectGroup.h" #include "WuQtUtilities.h" @@ -73,13 +74,20 @@ using namespace caret; * * @param parentToolBar * The parent toolbar. + * @param parentObjectName + * Name of parent object for macros */ -BrainBrowserWindowToolBarChartTwoAxes::BrainBrowserWindowToolBarChartTwoAxes(BrainBrowserWindowToolBar* parentToolBar) +BrainBrowserWindowToolBarChartTwoAxes::BrainBrowserWindowToolBarChartTwoAxes(BrainBrowserWindowToolBar* parentToolBar, + const QString& parentObjectName) : BrainBrowserWindowToolBarComponent(parentToolBar) { m_chartOverlaySet = NULL; m_chartAxis = NULL; + WuQMacroManager* macroManager = WuQMacroManager::instance(); + const QString objectNamePrefix(parentObjectName + + ":ChartAxes:"); + /* * 'Show' checkboxes */ @@ -87,26 +95,42 @@ BrainBrowserWindowToolBarChartTwoAxes::BrainBrowserWindowToolBarChartTwoAxes(Bra m_axisDisplayedByUserCheckBox->setToolTip("Show/hide the axis"); QObject::connect(m_axisDisplayedByUserCheckBox, &QCheckBox::clicked, this, &BrainBrowserWindowToolBarChartTwoAxes::valueChanged); + m_axisDisplayedByUserCheckBox->setObjectName(objectNamePrefix + + "ShowAxis"); + macroManager->addMacroSupportToObject(m_axisDisplayedByUserCheckBox); + m_showTickMarksCheckBox = new QCheckBox("Ticks"); QObject::connect(m_showTickMarksCheckBox, &QCheckBox::clicked, this, &BrainBrowserWindowToolBarChartTwoAxes::valueChangedBool); m_showTickMarksCheckBox->setToolTip("Show ticks along the axis"); + m_showTickMarksCheckBox->setObjectName(objectNamePrefix + + "ShowTicks"); + macroManager->addMacroSupportToObject(m_showTickMarksCheckBox); m_showLabelCheckBox = new QCheckBox("Label"); QObject::connect(m_showLabelCheckBox, &QCheckBox::clicked, this, &BrainBrowserWindowToolBarChartTwoAxes::valueChangedBool); m_showLabelCheckBox->setToolTip("Show label on axis"); + m_showLabelCheckBox->setObjectName(objectNamePrefix + + "ShowLabel"); + macroManager->addMacroSupportToObject(m_showLabelCheckBox); m_showNumericsCheckBox = new QCheckBox("Nums"); QObject::connect(m_showNumericsCheckBox, &QCheckBox::clicked, this, &BrainBrowserWindowToolBarChartTwoAxes::valueChangedBool); m_showNumericsCheckBox->setToolTip("Show numeric scale values on axis"); + m_showNumericsCheckBox->setObjectName(objectNamePrefix + + "ShowNumerics"); + macroManager->addMacroSupportToObject(m_showNumericsCheckBox); m_rotateNumericsCheckBox = new QCheckBox("Rotate"); QObject::connect(m_rotateNumericsCheckBox, &QCheckBox::clicked, this, &BrainBrowserWindowToolBarChartTwoAxes::valueChangedBool); m_rotateNumericsCheckBox->setToolTip("Rotate numeric scale values on axis"); + m_rotateNumericsCheckBox->setObjectName(objectNamePrefix + + "EnableNumericsRotate"); + macroManager->addMacroSupportToObject(m_rotateNumericsCheckBox); /* * Axes selection @@ -116,6 +140,9 @@ BrainBrowserWindowToolBarChartTwoAxes::BrainBrowserWindowToolBarChartTwoAxes(Bra QObject::connect(m_axisComboBox, &EnumComboBoxTemplate::itemActivated, this, &BrainBrowserWindowToolBarChartTwoAxes::axisChanged); m_axisComboBox->getWidget()->setToolTip("Choose axis for editing"); + m_axisComboBox->getWidget()->setObjectName(objectNamePrefix + + "ChooseAxis"); + macroManager->addMacroSupportToObject(m_axisComboBox->getComboBox()); /* * Controls for layer selection and label editing @@ -126,12 +153,18 @@ BrainBrowserWindowToolBarChartTwoAxes::BrainBrowserWindowToolBarChartTwoAxes(Bra this, &BrainBrowserWindowToolBarChartTwoAxes::axisLabelToolButtonClicked); WuQtUtilities::setToolButtonStyleForQt5Mac(m_axisLabelToolButton); m_axisLabelToolButton->setToolTip("Edit the axis name for the file in the selected overlay"); + m_axisLabelToolButton->setObjectName(objectNamePrefix + + "EditAxis"); + macroManager->addMacroSupportToObject(m_axisLabelToolButton); QLabel* axisLabelFromOverlayLabel = new QLabel("Label From File In"); m_axisLabelFromOverlayComboBox = new QComboBox(); m_axisLabelFromOverlayComboBox->setToolTip("Label for axis is from file in selected layer"); QObject::connect(m_axisLabelFromOverlayComboBox, static_cast(&QComboBox::activated), this, &BrainBrowserWindowToolBarChartTwoAxes::valueChangedInt); + m_axisLabelFromOverlayComboBox->setObjectName(objectNamePrefix + + "LabelFromOverlay"); + macroManager->addMacroSupportToObject(m_axisLabelFromOverlayComboBox); /* * Range controls @@ -147,6 +180,9 @@ BrainBrowserWindowToolBarChartTwoAxes::BrainBrowserWindowToolBarChartTwoAxes(Bra QObject::connect(m_autoUserRangeComboBox, &EnumComboBoxTemplate::itemActivated, this, &BrainBrowserWindowToolBarChartTwoAxes::valueChanged); m_autoUserRangeComboBox->getWidget()->setToolTip(rangeTooltip); + m_autoUserRangeComboBox->getWidget()->setObjectName(objectNamePrefix + + "RangeMode"); + macroManager->addMacroSupportToObject(m_autoUserRangeComboBox->getWidget()); m_userMinimumValueSpinBox = new WuQDoubleSpinBox(this); m_userMinimumValueSpinBox->setDecimalsModeAuto(); @@ -154,6 +190,9 @@ BrainBrowserWindowToolBarChartTwoAxes::BrainBrowserWindowToolBarChartTwoAxes(Bra QObject::connect(m_userMinimumValueSpinBox, static_cast(&WuQDoubleSpinBox::valueChanged), this, &BrainBrowserWindowToolBarChartTwoAxes::axisMinimumValueChanged); m_userMinimumValueSpinBox->setToolTip("Set user scaling axis minimum value"); + m_userMinimumValueSpinBox->getWidget()->setObjectName(objectNamePrefix + + "ScaleMinimum"); + macroManager->addMacroSupportToObject(m_userMinimumValueSpinBox->getWidget()); m_userMaximumValueSpinBox = new WuQDoubleSpinBox(this); m_userMaximumValueSpinBox->setDecimalsModeAuto(); @@ -161,6 +200,9 @@ BrainBrowserWindowToolBarChartTwoAxes::BrainBrowserWindowToolBarChartTwoAxes(Bra QObject::connect(m_userMaximumValueSpinBox, static_cast(&WuQDoubleSpinBox::valueChanged), this, &BrainBrowserWindowToolBarChartTwoAxes::axisMaximumValueChanged); m_userMaximumValueSpinBox->setToolTip("Set user scaling axis maximum value"); + m_userMaximumValueSpinBox->getWidget()->setObjectName(objectNamePrefix + + "ScaleMaximum"); + macroManager->addMacroSupportToObject(m_userMaximumValueSpinBox->getWidget()); /* * Format controls @@ -170,22 +212,34 @@ BrainBrowserWindowToolBarChartTwoAxes::BrainBrowserWindowToolBarChartTwoAxes(Bra QObject::connect(m_userNumericFormatComboBox, &EnumComboBoxTemplate::itemActivated, this, &BrainBrowserWindowToolBarChartTwoAxes::valueChanged); m_userNumericFormatComboBox->getWidget()->setToolTip("Choose format of axis scale numeric values"); + m_userNumericFormatComboBox->getWidget()->setObjectName(objectNamePrefix + + "Format"); + macroManager->addMacroSupportToObject(m_userNumericFormatComboBox->getWidget()); m_userDigitsRightOfDecimalSpinBox = WuQFactory::newSpinBoxWithMinMaxStep(0, 10, 1); QObject::connect(m_userDigitsRightOfDecimalSpinBox, static_cast(&QSpinBox::valueChanged), this, &BrainBrowserWindowToolBarChartTwoAxes::valueChangedInt); m_userDigitsRightOfDecimalSpinBox->setToolTip("Set digits right of decimal for\ndecimal or scientific format"); + m_userDigitsRightOfDecimalSpinBox->setObjectName(objectNamePrefix + + "DigitsRightOfDecimal"); + macroManager->addMacroSupportToObject(m_userDigitsRightOfDecimalSpinBox); m_numericSubdivisionsModeComboBox = new EnumComboBoxTemplate(this); m_numericSubdivisionsModeComboBox->setup(); QObject::connect(m_numericSubdivisionsModeComboBox, &EnumComboBoxTemplate::itemActivated, this, &BrainBrowserWindowToolBarChartTwoAxes::valueChanged); m_numericSubdivisionsModeComboBox->getWidget()->setToolTip("Numeric subdivisions mode"); + m_numericSubdivisionsModeComboBox->getWidget()->setObjectName(objectNamePrefix + + "NumericSubdivisionsMode"); + macroManager->addMacroSupportToObject(m_numericSubdivisionsModeComboBox->getWidget()); m_userSubdivisionsSpinBox = WuQFactory::newSpinBoxWithMinMaxStep(0, 100, 1); QObject::connect(m_userSubdivisionsSpinBox, static_cast(&QSpinBox::valueChanged), this, &BrainBrowserWindowToolBarChartTwoAxes::valueChangedInt); m_userSubdivisionsSpinBox->setToolTip("Set subdivisions on the axis when Auto is not checked"); + m_userSubdivisionsSpinBox->setObjectName(objectNamePrefix + + "NumberOfSubdivisions"); + macroManager->addMacroSupportToObject(m_userSubdivisionsSpinBox); /* * Size spin boxes @@ -195,24 +249,36 @@ BrainBrowserWindowToolBarChartTwoAxes::BrainBrowserWindowToolBarChartTwoAxes(Bra QObject::connect(m_labelSizeSpinBox, static_cast(&WuQDoubleSpinBox::valueChanged), this, &BrainBrowserWindowToolBarChartTwoAxes::valueChangedDouble); m_labelSizeSpinBox->setToolTip("Set height of label as percentage of tab height for selected axis"); + m_labelSizeSpinBox->getWidget()->setObjectName(objectNamePrefix + + "LabelHeight"); + macroManager->addMacroSupportToObject(m_labelSizeSpinBox->getWidget()); m_numericsSizeSpinBox = new WuQDoubleSpinBox(this); m_numericsSizeSpinBox->setRangePercentage(0.0, 100.0); QObject::connect(m_numericsSizeSpinBox, static_cast(&WuQDoubleSpinBox::valueChanged), this, &BrainBrowserWindowToolBarChartTwoAxes::valueChangedDouble); m_numericsSizeSpinBox->setToolTip("Set height of numeric values as percentage of tab height for selected axis"); + m_numericsSizeSpinBox->getWidget()->setObjectName(objectNamePrefix + + "NumericValueHeight"); + macroManager->addMacroSupportToObject(m_numericsSizeSpinBox->getWidget()); m_linesTicksSizeSpinBox = new WuQDoubleSpinBox(this); m_linesTicksSizeSpinBox->setRangePercentage(0.0, 100.0); QObject::connect(m_linesTicksSizeSpinBox, static_cast(&WuQDoubleSpinBox::valueChanged), this, &BrainBrowserWindowToolBarChartTwoAxes::axisLineThicknessChanged); m_linesTicksSizeSpinBox->setToolTip("Set thickness of axis lines as percentage of tab height for ALL axes"); + m_linesTicksSizeSpinBox->getWidget()->setObjectName(objectNamePrefix + + "TicksSize"); + macroManager->addMacroSupportToObject(m_linesTicksSizeSpinBox->getWidget()); m_paddingSizeSpinBox = new WuQDoubleSpinBox(this); m_paddingSizeSpinBox->setRangePercentage(0.0, 100.0); QObject::connect(m_paddingSizeSpinBox, static_cast(&WuQDoubleSpinBox::valueChanged), this, &BrainBrowserWindowToolBarChartTwoAxes::valueChangedDouble); m_paddingSizeSpinBox->setToolTip("Set padding (space between edge and labels) as percentage of tab height for selected axis"); + m_paddingSizeSpinBox->getWidget()->setObjectName(objectNamePrefix + + "PaddingSize"); + macroManager->addMacroSupportToObject(m_paddingSizeSpinBox->getWidget()); /* * Group widgets for blocking signals diff --git a/src/GuiQt/BrainBrowserWindowToolBarChartTwoAxes.h b/src/GuiQt/BrainBrowserWindowToolBarChartTwoAxes.h index df8db9b20..544348b0c 100644 --- a/src/GuiQt/BrainBrowserWindowToolBarChartTwoAxes.h +++ b/src/GuiQt/BrainBrowserWindowToolBarChartTwoAxes.h @@ -46,7 +46,8 @@ namespace caret { Q_OBJECT public: - BrainBrowserWindowToolBarChartTwoAxes(BrainBrowserWindowToolBar* parentToolBar); + BrainBrowserWindowToolBarChartTwoAxes(BrainBrowserWindowToolBar* parentToolBar, + const QString& parentObjectName); virtual ~BrainBrowserWindowToolBarChartTwoAxes(); diff --git a/src/GuiQt/BrainBrowserWindowToolBarChartTwoOrientation.cxx b/src/GuiQt/BrainBrowserWindowToolBarChartTwoOrientation.cxx index ea4313238..ae64a6646 100644 --- a/src/GuiQt/BrainBrowserWindowToolBarChartTwoOrientation.cxx +++ b/src/GuiQt/BrainBrowserWindowToolBarChartTwoOrientation.cxx @@ -33,6 +33,7 @@ #include "EventManager.h" #include "ModelChartTwo.h" #include "SessionManager.h" +#include "WuQMacroManager.h" #include "WuQtUtilities.h" using namespace caret; @@ -48,30 +49,40 @@ using namespace caret; /** * Constructor. */ -BrainBrowserWindowToolBarChartTwoOrientation::BrainBrowserWindowToolBarChartTwoOrientation(BrainBrowserWindowToolBar* parentToolBar) +BrainBrowserWindowToolBarChartTwoOrientation::BrainBrowserWindowToolBarChartTwoOrientation(BrainBrowserWindowToolBar* parentToolBar, + const QString& parentObjectName) : BrainBrowserWindowToolBarComponent(parentToolBar) { + const QString objectNamePrefix(parentObjectName + + ":ChartOrientation"); + + QToolButton* orientationResetToolButton = new QToolButton(); m_orientationResetToolButtonAction = WuQtUtilities::createAction("Reset", "Reset the view to remove any panning or zooming", - this, + orientationResetToolButton, this, SLOT(orientationResetToolButtonTriggered(bool))); + m_orientationResetToolButtonAction->setObjectName(objectNamePrefix + + ":ResetButton"); + WuQMacroManager::instance()->addMacroSupportToObject(m_orientationResetToolButtonAction); const QString customToolTip = ("Pressing the \"Custom\" button displays a dialog for creating and editing orientations.\n" "Note that custom orientations are stored in your Workbench's preferences and thus\n" "will be availble in any concurrent or future instances of Workbench."); + m_orientationCustomViewSelectToolButton = new QToolButton(); m_customViewAction = WuQtUtilities::createAction("Custom", customToolTip, - this, + m_orientationCustomViewSelectToolButton, this, SLOT(customViewActionTriggered())); + m_customViewAction->setObjectName(objectNamePrefix + + ":ShowCustomViewDialog"); + WuQMacroManager::instance()->addMacroSupportToObject(m_customViewAction); - QToolButton* orientationResetToolButton = new QToolButton(); orientationResetToolButton->setDefaultAction(m_orientationResetToolButtonAction); WuQtUtilities::setToolButtonStyleForQt5Mac(orientationResetToolButton); - m_orientationCustomViewSelectToolButton = new QToolButton(); m_orientationCustomViewSelectToolButton->setDefaultAction(m_customViewAction); m_orientationCustomViewSelectToolButton->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Fixed); diff --git a/src/GuiQt/BrainBrowserWindowToolBarChartTwoOrientation.h b/src/GuiQt/BrainBrowserWindowToolBarChartTwoOrientation.h index 6890f2700..34aa3877f 100644 --- a/src/GuiQt/BrainBrowserWindowToolBarChartTwoOrientation.h +++ b/src/GuiQt/BrainBrowserWindowToolBarChartTwoOrientation.h @@ -32,7 +32,8 @@ namespace caret { Q_OBJECT public: - BrainBrowserWindowToolBarChartTwoOrientation(BrainBrowserWindowToolBar* parentToolBar); + BrainBrowserWindowToolBarChartTwoOrientation(BrainBrowserWindowToolBar* parentToolBar, + const QString& parentObjectName); virtual ~BrainBrowserWindowToolBarChartTwoOrientation(); diff --git a/src/GuiQt/BrainBrowserWindowToolBarChartTwoTitle.cxx b/src/GuiQt/BrainBrowserWindowToolBarChartTwoTitle.cxx index 21d62455e..bfa2c97cc 100644 --- a/src/GuiQt/BrainBrowserWindowToolBarChartTwoTitle.cxx +++ b/src/GuiQt/BrainBrowserWindowToolBarChartTwoTitle.cxx @@ -42,6 +42,7 @@ #include "ModelChartTwo.h" #include "WuQDataEntryDialog.h" #include "WuQDoubleSpinBox.h" +#include "WuQMacroManager.h" #include "WuQtUtilities.h" using namespace caret; @@ -56,21 +57,36 @@ using namespace caret; /** * Constructor. + * + * @param parentToolBar + * The parent toolbar + * @param parentObjectName + * Name of parent object for macros */ -BrainBrowserWindowToolBarChartTwoTitle::BrainBrowserWindowToolBarChartTwoTitle(BrainBrowserWindowToolBar* parentToolBar) +BrainBrowserWindowToolBarChartTwoTitle::BrainBrowserWindowToolBarChartTwoTitle(BrainBrowserWindowToolBar* parentToolBar, + const QString& parentObjectName) : BrainBrowserWindowToolBarComponent(parentToolBar) { + WuQMacroManager* macroManager = WuQMacroManager::instance(); + const QString objectNamePrefix(parentObjectName + + ":ChartTitle:"); + m_showTitleCheckBox = new QCheckBox("Show Title"); m_showTitleCheckBox->setToolTip("Show the title at the top of the chart"); QObject::connect(m_showTitleCheckBox, &QCheckBox::clicked, this, &BrainBrowserWindowToolBarChartTwoTitle::showTitleCheckBoxClicked); + m_showTitleCheckBox->setObjectName(objectNamePrefix + + "Show"); + macroManager->addMacroSupportToObject(m_showTitleCheckBox); - QAction* editTitleAction = new QAction("Edit Title...", this); + QToolButton* editTitleToolButton = new QToolButton; + QAction* editTitleAction = new QAction("Edit Title...", editTitleToolButton); editTitleAction->setToolTip("Edit the chart title in a dialog"); QObject::connect(editTitleAction, &QAction::triggered, this, &BrainBrowserWindowToolBarChartTwoTitle::editTitleActionTriggered); - - QToolButton* editTitleToolButton = new QToolButton; + editTitleAction->setObjectName(objectNamePrefix + + "Edit"); + macroManager->addMacroSupportToObject(editTitleAction); WuQtUtilities::setToolButtonStyleForQt5Mac(editTitleToolButton); editTitleToolButton->setDefaultAction(editTitleAction); @@ -79,11 +95,17 @@ BrainBrowserWindowToolBarChartTwoTitle::BrainBrowserWindowToolBarChartTwoTitle(B QObject::connect(m_titleSizeSpinBox, static_cast(&WuQDoubleSpinBox::valueChanged), this, &BrainBrowserWindowToolBarChartTwoTitle::sizeSpinBoxValueChanged); m_titleSizeSpinBox->setToolTip("Set height of title as percentage of tab height"); + m_titleSizeSpinBox->getWidget()->setObjectName(objectNamePrefix + + "Height"); + macroManager->addMacroSupportToObject(m_titleSizeSpinBox->getWidget()); m_paddingSizeSpinBox = new WuQDoubleSpinBox(this); m_paddingSizeSpinBox->setRangePercentage(0.0, 100.0); QObject::connect(m_paddingSizeSpinBox, static_cast(&WuQDoubleSpinBox::valueChanged), this, &BrainBrowserWindowToolBarChartTwoTitle::sizeSpinBoxValueChanged); + m_paddingSizeSpinBox->getWidget()->setObjectName(objectNamePrefix + + "Padding"); + macroManager->addMacroSupportToObject(m_paddingSizeSpinBox->getWidget()); m_paddingSizeSpinBox->setToolTip("Set padding (space between edge and labels) as percentage of tab height"); QGridLayout* layout = new QGridLayout(this); diff --git a/src/GuiQt/BrainBrowserWindowToolBarChartTwoTitle.h b/src/GuiQt/BrainBrowserWindowToolBarChartTwoTitle.h index cf803400f..aa27d1895 100644 --- a/src/GuiQt/BrainBrowserWindowToolBarChartTwoTitle.h +++ b/src/GuiQt/BrainBrowserWindowToolBarChartTwoTitle.h @@ -35,7 +35,8 @@ namespace caret { Q_OBJECT public: - BrainBrowserWindowToolBarChartTwoTitle(BrainBrowserWindowToolBar* parentToolBar); + BrainBrowserWindowToolBarChartTwoTitle(BrainBrowserWindowToolBar* parentToolBar, + const QString& parentObjectName); virtual ~BrainBrowserWindowToolBarChartTwoTitle(); diff --git a/src/GuiQt/BrainBrowserWindowToolBarChartTwoType.cxx b/src/GuiQt/BrainBrowserWindowToolBarChartTwoType.cxx index a1cdab23e..08ee5fbde 100644 --- a/src/GuiQt/BrainBrowserWindowToolBarChartTwoType.cxx +++ b/src/GuiQt/BrainBrowserWindowToolBarChartTwoType.cxx @@ -32,6 +32,7 @@ #include "CaretAssert.h" #include "EnumComboBoxTemplate.h" #include "ModelChartTwo.h" +#include "WuQMacroManager.h" #include "WuQtUtilities.h" using namespace caret; @@ -49,8 +50,11 @@ using namespace caret; * * @param parentToolBar * parent toolbar. + * @param parentObjectName + * Name of parent object for macros */ -BrainBrowserWindowToolBarChartTwoType::BrainBrowserWindowToolBarChartTwoType(BrainBrowserWindowToolBar* parentToolBar) +BrainBrowserWindowToolBarChartTwoType::BrainBrowserWindowToolBarChartTwoType(BrainBrowserWindowToolBar* parentToolBar, + const QString& parentObjectName) : BrainBrowserWindowToolBarComponent(parentToolBar), m_parentToolBar(parentToolBar) { @@ -69,6 +73,15 @@ m_parentToolBar(parentToolBar) QRadioButton* rb = new QRadioButton(ChartTwoDataTypeEnum::toGuiName(ct)); m_chartTypeButtonGroup->addButton(rb, m_chartTypeRadioButtons.size()); + rb->setToolTip("Set chart to " + + rb->text()); + + QString chartTypeName = rb->text(); + chartTypeName = chartTypeName.replace(" ", ""); + rb->setObjectName(parentObjectName + + ":ChartType:" + + chartTypeName); + WuQMacroManager::instance()->addMacroSupportToObject(rb); radioButtonLayout->addWidget(rb); diff --git a/src/GuiQt/BrainBrowserWindowToolBarChartTwoType.h b/src/GuiQt/BrainBrowserWindowToolBarChartTwoType.h index b784137f6..c7c7e9a89 100644 --- a/src/GuiQt/BrainBrowserWindowToolBarChartTwoType.h +++ b/src/GuiQt/BrainBrowserWindowToolBarChartTwoType.h @@ -36,7 +36,8 @@ namespace caret { Q_OBJECT public: - BrainBrowserWindowToolBarChartTwoType(BrainBrowserWindowToolBar* parentToolBar); + BrainBrowserWindowToolBarChartTwoType(BrainBrowserWindowToolBar* parentToolBar, + const QString& parentObjectName); virtual ~BrainBrowserWindowToolBarChartTwoType(); diff --git a/src/GuiQt/BrainBrowserWindowToolBarChartType.cxx b/src/GuiQt/BrainBrowserWindowToolBarChartType.cxx index b45ecc59b..a27488fbd 100644 --- a/src/GuiQt/BrainBrowserWindowToolBarChartType.cxx +++ b/src/GuiQt/BrainBrowserWindowToolBarChartType.cxx @@ -33,6 +33,7 @@ #include "ChartModel.h" #include "EnumComboBoxTemplate.h" #include "ModelChart.h" +#include "WuQMacroManager.h" #include "WuQtUtilities.h" using namespace caret; @@ -51,10 +52,12 @@ using namespace caret; * @param parentToolBar * parent toolbar. */ -BrainBrowserWindowToolBarChartType::BrainBrowserWindowToolBarChartType(BrainBrowserWindowToolBar* parentToolBar) +BrainBrowserWindowToolBarChartType::BrainBrowserWindowToolBarChartType(BrainBrowserWindowToolBar* parentToolBar, + const QString& parentObjectName) : BrainBrowserWindowToolBarComponent(parentToolBar), m_parentToolBar(parentToolBar) { + m_chartTypeButtonGroup = new QButtonGroup(this); QVBoxLayout* radioButtonLayout = new QVBoxLayout(this); @@ -74,6 +77,16 @@ m_parentToolBar(parentToolBar) m_chartTypeButtonGroup->addButton(rb, m_chartTypeRadioButtons.size()); + rb->setToolTip("Set chart to " + + rb->text()); + + QString chartTypeName = rb->text(); + chartTypeName = chartTypeName.replace(" ", ""); + rb->setObjectName(parentObjectName + + ":ChartOneType:" + + chartTypeName); + WuQMacroManager::instance()->addMacroSupportToObject(rb); + radioButtonLayout->addWidget(rb); m_chartTypeRadioButtons.push_back(std::make_pair(ct, rb)); diff --git a/src/GuiQt/BrainBrowserWindowToolBarChartType.h b/src/GuiQt/BrainBrowserWindowToolBarChartType.h index f468511be..47f0d93e5 100644 --- a/src/GuiQt/BrainBrowserWindowToolBarChartType.h +++ b/src/GuiQt/BrainBrowserWindowToolBarChartType.h @@ -36,7 +36,8 @@ namespace caret { Q_OBJECT public: - BrainBrowserWindowToolBarChartType(BrainBrowserWindowToolBar* parentToolBar); + BrainBrowserWindowToolBarChartType(BrainBrowserWindowToolBar* parentToolBar, + const QString& parentObjectName); virtual ~BrainBrowserWindowToolBarChartType(); diff --git a/src/GuiQt/BrainBrowserWindowToolBarClipping.cxx b/src/GuiQt/BrainBrowserWindowToolBarClipping.cxx index 9c3beeb22..2b7ca97ea 100644 --- a/src/GuiQt/BrainBrowserWindowToolBarClipping.cxx +++ b/src/GuiQt/BrainBrowserWindowToolBarClipping.cxx @@ -33,6 +33,7 @@ #include "BrowserTabContent.h" #include "CaretAssert.h" #include "GuiManager.h" +#include "WuQMacroManager.h" #include "WuQtUtilities.h" using namespace caret; @@ -47,45 +48,86 @@ using namespace caret; /** * Constructor. + * + * @param browserWindowIndex + * Index of window + * @param parentToolBar + * The parent toolbar + * @param parentObjectNamePrefix + * Name of parent object */ BrainBrowserWindowToolBarClipping::BrainBrowserWindowToolBarClipping(const int32_t browserWindowIndex, - BrainBrowserWindowToolBar* parentToolBar) + BrainBrowserWindowToolBar* parentToolBar, + const QString& parentObjectNamePrefix) : BrainBrowserWindowToolBarComponent(parentToolBar), m_browserWindowIndex(browserWindowIndex), m_parentToolBar(parentToolBar) { + WuQMacroManager* macroManager = WuQMacroManager::instance(); + CaretAssert(macroManager); + + const QString objectNamePrefix(parentObjectNamePrefix + + ":Clipping"); + m_xClippingEnabledCheckBox = new QCheckBox("X"); QObject::connect(m_xClippingEnabledCheckBox, SIGNAL(clicked(bool)), this, SLOT(clippingCheckBoxCheckStatusChanged())); + m_xClippingEnabledCheckBox->setToolTip("Enable X clipping plane"); + m_xClippingEnabledCheckBox->setObjectName(objectNamePrefix + + ":EnableX"); + macroManager->addMacroSupportToObject(m_xClippingEnabledCheckBox); m_yClippingEnabledCheckBox = new QCheckBox("Y"); QObject::connect(m_yClippingEnabledCheckBox, SIGNAL(clicked(bool)), this, SLOT(clippingCheckBoxCheckStatusChanged())); + m_yClippingEnabledCheckBox->setToolTip("Enable Y clipping plane"); + m_yClippingEnabledCheckBox->setObjectName(objectNamePrefix + + ":EnableY"); + macroManager->addMacroSupportToObject(m_yClippingEnabledCheckBox); m_zClippingEnabledCheckBox = new QCheckBox("Z"); QObject::connect(m_zClippingEnabledCheckBox, SIGNAL(clicked(bool)), this, SLOT(clippingCheckBoxCheckStatusChanged())); + m_zClippingEnabledCheckBox->setToolTip("Enable Z clipping plane"); + m_zClippingEnabledCheckBox->setObjectName(objectNamePrefix + + ":EnableZ"); + macroManager->addMacroSupportToObject(m_zClippingEnabledCheckBox); m_surfaceClippingEnabledCheckBox = new QCheckBox("Surface"); QObject::connect(m_surfaceClippingEnabledCheckBox, SIGNAL(clicked(bool)), this, SLOT(clippingCheckBoxCheckStatusChanged())); + m_surfaceClippingEnabledCheckBox->setToolTip("Enable Clipping of Surface"); + m_surfaceClippingEnabledCheckBox->setObjectName(objectNamePrefix + + ":EnableSurface"); + macroManager->addMacroSupportToObject(m_surfaceClippingEnabledCheckBox); m_volumeClippingEnabledCheckBox = new QCheckBox("Volume"); QObject::connect(m_volumeClippingEnabledCheckBox, SIGNAL(clicked(bool)), this, SLOT(clippingCheckBoxCheckStatusChanged())); + m_volumeClippingEnabledCheckBox->setToolTip("Enable Clipping of Volume Slices"); + m_volumeClippingEnabledCheckBox->setObjectName(objectNamePrefix + + ":EnableVolume"); + macroManager->addMacroSupportToObject(m_volumeClippingEnabledCheckBox); m_featuresClippingEnabledCheckBox = new QCheckBox("Features"); QObject::connect(m_featuresClippingEnabledCheckBox, SIGNAL(clicked(bool)), this, SLOT(clippingCheckBoxCheckStatusChanged())); + m_featuresClippingEnabledCheckBox->setToolTip("Enable Clipping of Features"); + m_featuresClippingEnabledCheckBox->setObjectName(objectNamePrefix + + ":EnableFeatures"); + macroManager->addMacroSupportToObject(m_featuresClippingEnabledCheckBox); QToolButton* setupToolButton = new QToolButton(); setupToolButton->setText("Setup"); QObject::connect(setupToolButton, SIGNAL(clicked()), this, SLOT(setupClippingPushButtonClicked())); WuQtUtilities::setToolButtonStyleForQt5Mac(setupToolButton); + setupToolButton->setToolTip("Display Clipping Planes Setup Dialog"); + setupToolButton->setObjectName(objectNamePrefix + + ":ShowSetupDialog"); + macroManager->addMacroSupportToObject(setupToolButton); QGridLayout* gridLayout = new QGridLayout(this); -// WuQtUtilities::setLayoutSpacingAndMargins(gridLayout, 2, 0); gridLayout->setHorizontalSpacing(6); gridLayout->setVerticalSpacing(4); gridLayout->setContentsMargins(1, 1, 1, 1); @@ -101,31 +143,6 @@ m_parentToolBar(parentToolBar) gridLayout->addWidget(m_volumeClippingEnabledCheckBox, rowIndex, 0, 1, 3); rowIndex++; gridLayout->addWidget(setupToolButton, rowIndex, 0, 1, 3, Qt::AlignHCenter); - -// /* -// * Layout: -// * Column 1: Clipping X, Y, Z -// * Column 2: Nothing but used as space -// * Column 3: Type of data clipped. -// */ -// QGridLayout* checkboxGridLayout = new QGridLayout(); -// WuQtUtilities::setLayoutSpacingAndMargins(checkboxGridLayout, 4, 0); -// checkboxGridLayout->setColumnMinimumWidth(1, 15); -// checkboxGridLayout->setColumnStretch(0, 0); -// checkboxGridLayout->setColumnStretch(0, 1); -// checkboxGridLayout->setColumnStretch(0, 2); -// checkboxGridLayout->addWidget(m_xClippingEnabledCheckBox, 0, 0); -// checkboxGridLayout->addWidget(m_yClippingEnabledCheckBox, 1, 0); -// checkboxGridLayout->addWidget(m_zClippingEnabledCheckBox, 2, 0); -// checkboxGridLayout->addWidget(m_surfaceClippingEnabledCheckBox, 0, 2); -// checkboxGridLayout->addWidget(m_volumeClippingEnabledCheckBox, 1, 2); -// checkboxGridLayout->addWidget(m_featuresClippingEnabledCheckBox, 2, 2); -// -// QVBoxLayout* layout = new QVBoxLayout(this); -// WuQtUtilities::setLayoutSpacingAndMargins(layout, 0, 0); -// layout->addLayout(checkboxGridLayout); -// layout->addWidget(setupToolButton, 0, Qt::AlignHCenter); -// layout->addStretch(); } /** diff --git a/src/GuiQt/BrainBrowserWindowToolBarClipping.h b/src/GuiQt/BrainBrowserWindowToolBarClipping.h index 23b631c56..a76376fca 100644 --- a/src/GuiQt/BrainBrowserWindowToolBarClipping.h +++ b/src/GuiQt/BrainBrowserWindowToolBarClipping.h @@ -35,7 +35,8 @@ namespace caret { public: BrainBrowserWindowToolBarClipping(const int32_t browserWindowIndex, - BrainBrowserWindowToolBar* parentToolBar); + BrainBrowserWindowToolBar* parentToolBar, + const QString& objectNamePrefix); virtual ~BrainBrowserWindowToolBarClipping(); diff --git a/src/GuiQt/BrainBrowserWindowToolBarSlicePlane.cxx b/src/GuiQt/BrainBrowserWindowToolBarSlicePlane.cxx index 73e9c528b..1db6e3ad9 100644 --- a/src/GuiQt/BrainBrowserWindowToolBarSlicePlane.cxx +++ b/src/GuiQt/BrainBrowserWindowToolBarSlicePlane.cxx @@ -54,15 +54,15 @@ using namespace caret; * @param parentToolBar * The parent toolbar */ -BrainBrowserWindowToolBarSlicePlane::BrainBrowserWindowToolBarSlicePlane(BrainBrowserWindowToolBar* parentToolBar) +BrainBrowserWindowToolBarSlicePlane::BrainBrowserWindowToolBarSlicePlane(const QString& parentObjectName, + BrainBrowserWindowToolBar* parentToolBar) : BrainBrowserWindowToolBarComponent(parentToolBar), m_parentToolBar(parentToolBar) { WuQMacroManager* macroManager = WuQMacroManager::instance(); CaretAssert(macroManager); - const QString objectNamePrefix("Window_" - + QString::number(m_parentToolBar->browserWindowIndex + 1) - + ":ToolBar:SlicePlane:"); + const QString objectNamePrefix(parentObjectName + + ":SlicePlane:"); QIcon parasagittalIcon; const bool parasagittalIconValid = @@ -88,7 +88,7 @@ m_parentToolBar(parentToolBar) m_volumePlaneParasagittalToolButtonAction->setIcon(parasagittalIcon); } m_volumePlaneParasagittalToolButtonAction->setObjectName(objectNamePrefix - + "Parasagittal_Slice_View"); + + "ParasagittalSliceView"); macroManager->addMacroSupportToObject(m_volumePlaneParasagittalToolButtonAction); m_volumePlaneCoronalToolButtonAction = WuQtUtilities::createAction(VolumeSliceViewPlaneEnum::toGuiNameAbbreviation(VolumeSliceViewPlaneEnum::CORONAL), @@ -99,7 +99,7 @@ m_parentToolBar(parentToolBar) m_volumePlaneCoronalToolButtonAction->setIcon(coronalIcon); } m_volumePlaneCoronalToolButtonAction->setObjectName(objectNamePrefix - + "Coronal_Slice_View"); + + "CoronalSliceView"); macroManager->addMacroSupportToObject(m_volumePlaneCoronalToolButtonAction); m_volumePlaneAxialToolButtonAction = WuQtUtilities::createAction(VolumeSliceViewPlaneEnum::toGuiNameAbbreviation(VolumeSliceViewPlaneEnum::AXIAL), @@ -110,7 +110,7 @@ m_parentToolBar(parentToolBar) m_volumePlaneAxialToolButtonAction->setIcon(axialIcon); } m_volumePlaneAxialToolButtonAction->setObjectName(objectNamePrefix - + "Axial_Slice_View"); + + "AxialSliceView"); macroManager->addMacroSupportToObject(m_volumePlaneAxialToolButtonAction); m_volumePlaneAllToolButtonAction = WuQtUtilities::createAction(VolumeSliceViewPlaneEnum::toGuiNameAbbreviation(VolumeSliceViewPlaneEnum::ALL), @@ -118,9 +118,9 @@ m_parentToolBar(parentToolBar) "Press arrow to display menu for layout selection", this); m_volumePlaneAllToolButtonAction->setCheckable(true); - m_volumePlaneAllToolButtonAction->setMenu(createViewAllSlicesLayoutMenu()); + m_volumePlaneAllToolButtonAction->setMenu(createViewAllSlicesLayoutMenu(objectNamePrefix)); m_volumePlaneAllToolButtonAction->setObjectName(objectNamePrefix - + "All_Slices_View"); + + "AllSlicesView"); macroManager->addMacroSupportToObject(m_volumePlaneAllToolButtonAction); @@ -140,7 +140,7 @@ m_parentToolBar(parentToolBar) this, SLOT(volumePlaneResetToolButtonTriggered(bool))); m_volumePlaneResetToolButtonAction->setObjectName(objectNamePrefix - + "Reset_View"); + + "ResetView"); macroManager->addMacroSupportToObject(m_volumePlaneResetToolButtonAction); @@ -189,7 +189,7 @@ m_parentToolBar(parentToolBar) QObject::connect(m_volumeAxisCrosshairsToolButtonAction, &QAction::triggered, this, &BrainBrowserWindowToolBarSlicePlane::volumeAxisCrosshairsTriggered); m_volumeAxisCrosshairsToolButtonAction->setObjectName(objectNamePrefix - + "Show_Volume_Slice_Crosshairs"); + + "ShowVolumeSliceCrosshairs"); macroManager->addMacroSupportToObject(m_volumeAxisCrosshairsToolButtonAction); QToolButton* volumeCrosshairsToolButton = new QToolButton(); @@ -205,7 +205,7 @@ m_parentToolBar(parentToolBar) QObject::connect(m_volumeAxisCrosshairLabelsToolButtonAction, &QAction::triggered, this, &BrainBrowserWindowToolBarSlicePlane::volumeAxisCrosshairLabelsTriggered); m_volumeAxisCrosshairLabelsToolButtonAction->setObjectName(objectNamePrefix - + "Show_Volume_Slice_Labels"); + + "ShowVolumeSliceLabels"); macroManager->addMacroSupportToObject(m_volumeAxisCrosshairLabelsToolButtonAction); QToolButton* volumeCrosshairLabelsToolButton = new QToolButton(); @@ -280,14 +280,19 @@ BrainBrowserWindowToolBarSlicePlane::updateContent(BrowserTabContent* browserTab /** * @return A new instance of the view all slices layout menu. + * @param objectNamePrefix + * Prefix for object names in macro system */ QMenu* -BrainBrowserWindowToolBarSlicePlane::createViewAllSlicesLayoutMenu() +BrainBrowserWindowToolBarSlicePlane::createViewAllSlicesLayoutMenu(const QString& objectNamePrefix) { std::vector allLayouts; VolumeSliceViewAllPlanesLayoutEnum::getAllEnums(allLayouts); - QMenu* menu = new QMenu(); + QMenu* menu = new QMenu(this); + menu->setObjectName(objectNamePrefix + + "LayoutMenu"); + menu->setToolTip("Selects layout of volume slices (column, grid, row)"); QActionGroup* actionGroup = new QActionGroup(this); for (auto layout : allLayouts) { @@ -301,6 +306,7 @@ BrainBrowserWindowToolBarSlicePlane::createViewAllSlicesLayoutMenu() QObject::connect(menu, &QMenu::triggered, this, &BrainBrowserWindowToolBarSlicePlane::viewAllSliceLayoutMenuTriggered); + WuQMacroManager::instance()->addMacroSupportToObject(menu); return menu; } diff --git a/src/GuiQt/BrainBrowserWindowToolBarSlicePlane.h b/src/GuiQt/BrainBrowserWindowToolBarSlicePlane.h index f22ed5f14..524e719ad 100644 --- a/src/GuiQt/BrainBrowserWindowToolBarSlicePlane.h +++ b/src/GuiQt/BrainBrowserWindowToolBarSlicePlane.h @@ -36,7 +36,8 @@ namespace caret { Q_OBJECT public: - BrainBrowserWindowToolBarSlicePlane(BrainBrowserWindowToolBar* parentToolBar); + BrainBrowserWindowToolBarSlicePlane(const QString& parentObjectName, + BrainBrowserWindowToolBar* parentToolBar); virtual ~BrainBrowserWindowToolBarSlicePlane(); @@ -58,7 +59,7 @@ namespace caret { BrainBrowserWindowToolBarSlicePlane& operator=(const BrainBrowserWindowToolBarSlicePlane&); - QMenu* createViewAllSlicesLayoutMenu(); + QMenu* createViewAllSlicesLayoutMenu(const QString& objectNamePrefix); void updateViewAllSlicesLayoutMenu(BrowserTabContent* browserTabContent); diff --git a/src/GuiQt/BrainBrowserWindowToolBarSliceSelection.cxx b/src/GuiQt/BrainBrowserWindowToolBarSliceSelection.cxx index 4125de972..8626dea4b 100644 --- a/src/GuiQt/BrainBrowserWindowToolBarSliceSelection.cxx +++ b/src/GuiQt/BrainBrowserWindowToolBarSliceSelection.cxx @@ -65,15 +65,20 @@ using namespace caret; /** * Constructor. + * + * @param parentToolBar + * The parent toolbar. + * @param parentObjectName + * Name of parent object. */ -BrainBrowserWindowToolBarSliceSelection::BrainBrowserWindowToolBarSliceSelection(BrainBrowserWindowToolBar* parentToolBar) +BrainBrowserWindowToolBarSliceSelection::BrainBrowserWindowToolBarSliceSelection(BrainBrowserWindowToolBar* parentToolBar, + const QString parentObjectName) : BrainBrowserWindowToolBarComponent(parentToolBar), m_parentToolBar(parentToolBar) { WuQMacroManager* macroManager = WuQMacroManager::instance(); CaretAssert(macroManager); - const QString objectNamePrefix("Window_" - + QString::number(m_parentToolBar->browserWindowIndex + 1) + const QString objectNamePrefix(parentObjectName + ":ToolBar:SliceSelection:"); QAction* volumeIndicesOriginToolButtonAction = WuQtUtilities::createAction("O\nR\nI\nG\nI\nN", @@ -86,7 +91,7 @@ m_parentToolBar(parentToolBar) volumeIndicesOriginToolButton->setDefaultAction(volumeIndicesOriginToolButtonAction); WuQtUtilities::setToolButtonStyleForQt5Mac(volumeIndicesOriginToolButton); volumeIndicesOriginToolButtonAction->setObjectName(objectNamePrefix - + "Move_Volume_Slices_to_Origin"); + + "MoveVolumeSlicesToOrigin"); volumeIndicesOriginToolButtonAction->setParent(volumeIndicesOriginToolButton); macroManager->addMacroSupportToObject(volumeIndicesOriginToolButtonAction); @@ -98,7 +103,7 @@ m_parentToolBar(parentToolBar) WuQtUtilities::setToolTipAndStatusTip(m_volumeIndicesParasagittalCheckBox, "Enable/Disable display of PARASAGITTAL slice"); m_volumeIndicesParasagittalCheckBox->setObjectName(objectNamePrefix - + "Enable_Parasagittal_Slice"); + + "EnableParasagittalSlice"); QObject::connect(m_volumeIndicesParasagittalCheckBox, SIGNAL(stateChanged(int)), this, SLOT(volumeIndicesParasagittalCheckBoxStateChanged(int))); macroManager->addMacroSupportToObject(m_volumeIndicesParasagittalCheckBox); @@ -107,7 +112,7 @@ m_parentToolBar(parentToolBar) WuQtUtilities::setToolTipAndStatusTip(m_volumeIndicesCoronalCheckBox, "Enable/Disable display of CORONAL slice"); m_volumeIndicesCoronalCheckBox->setObjectName(objectNamePrefix - + "Enable_Coronal_Slice"); + + "EnableCoronalSlice"); QObject::connect(m_volumeIndicesCoronalCheckBox, SIGNAL(stateChanged(int)), this, SLOT(volumeIndicesCoronalCheckBoxStateChanged(int))); macroManager->addMacroSupportToObject(m_volumeIndicesCoronalCheckBox); @@ -117,7 +122,7 @@ m_parentToolBar(parentToolBar) "Enable/Disable display of AXIAL slice"); m_volumeIndicesAxialCheckBox->setObjectName(objectNamePrefix - + "Enable_Axial_Slice"); + + "EnableAxialSlice"); QObject::connect(m_volumeIndicesAxialCheckBox, SIGNAL(stateChanged(int)), this, SLOT(volumeIndicesAxialCheckBoxStateChanged(int))); macroManager->addMacroSupportToObject(m_volumeIndicesAxialCheckBox); @@ -132,7 +137,7 @@ m_parentToolBar(parentToolBar) QObject::connect(m_volumeIndicesParasagittalSpinBox, SIGNAL(valueChanged(int)), this, SLOT(volumeIndicesParasagittalSpinBoxValueChanged(int))); m_volumeIndicesParasagittalSpinBox->setObjectName(objectNamePrefix - + "Volume_Parasagittal_Slice_Index"); + + "VolumeParasagittalSliceIndex"); macroManager->addMacroSupportToObject(m_volumeIndicesParasagittalSpinBox); m_volumeIndicesCoronalSpinBox = WuQFactory::newSpinBox(); @@ -142,7 +147,7 @@ m_parentToolBar(parentToolBar) QObject::connect(m_volumeIndicesCoronalSpinBox, SIGNAL(valueChanged(int)), this, SLOT(volumeIndicesCoronalSpinBoxValueChanged(int))); m_volumeIndicesCoronalSpinBox->setObjectName(objectNamePrefix - + "Volume_Coronal_Slice_Index"); + + "VolumeCoronalSliceIndex"); macroManager->addMacroSupportToObject(m_volumeIndicesCoronalSpinBox); m_volumeIndicesAxialSpinBox = WuQFactory::newSpinBox(); @@ -152,7 +157,7 @@ m_parentToolBar(parentToolBar) QObject::connect(m_volumeIndicesAxialSpinBox, SIGNAL(valueChanged(int)), this, SLOT(volumeIndicesAxialSpinBoxValueChanged(int))); m_volumeIndicesAxialSpinBox->setObjectName(objectNamePrefix - + "Volume_Axial_Slice_Index"); + + "VolumeAxialSliceIndex"); macroManager->addMacroSupportToObject(m_volumeIndicesAxialSpinBox); m_volumeIndicesXcoordSpinBox = WuQFactory::newDoubleSpinBox(); @@ -163,7 +168,7 @@ m_parentToolBar(parentToolBar) QObject::connect(m_volumeIndicesXcoordSpinBox, SIGNAL(valueChanged(double)), this, SLOT(volumeIndicesXcoordSpinBoxValueChanged(double))); m_volumeIndicesXcoordSpinBox->setObjectName(objectNamePrefix - + "Volume_Parasagittal_Coordinate"); + + "VolumeParasagittalCoordinate"); macroManager->addMacroSupportToObject(m_volumeIndicesXcoordSpinBox); m_volumeIndicesYcoordSpinBox = WuQFactory::newDoubleSpinBox(); @@ -174,7 +179,7 @@ m_parentToolBar(parentToolBar) QObject::connect(m_volumeIndicesYcoordSpinBox, SIGNAL(valueChanged(double)), this, SLOT(volumeIndicesYcoordSpinBoxValueChanged(double))); m_volumeIndicesYcoordSpinBox->setObjectName(objectNamePrefix - + "Volume_Coronal_Coordinate"); + + "VolumeCoronalCoordinate"); macroManager->addMacroSupportToObject(m_volumeIndicesYcoordSpinBox); m_volumeIndicesZcoordSpinBox = WuQFactory::newDoubleSpinBox(); @@ -185,7 +190,7 @@ m_parentToolBar(parentToolBar) QObject::connect(m_volumeIndicesZcoordSpinBox, SIGNAL(valueChanged(double)), this, SLOT(volumeIndicesZcoordSpinBoxValueChanged(double))); m_volumeIndicesZcoordSpinBox->setObjectName(objectNamePrefix - + "Volume_Axial_Coordinate"); + + "VolumeAxialCoordinate"); macroManager->addMacroSupportToObject(m_volumeIndicesZcoordSpinBox); const AString idToolTipText = ("When selected: If there is an identification operation " @@ -213,7 +218,7 @@ m_parentToolBar(parentToolBar) volumeIDToolButton->setDefaultAction(m_volumeIdentificationUpdatesSlicesAction); WuQtUtilities::setToolButtonStyleForQt5Mac(volumeIDToolButton); m_volumeIdentificationUpdatesSlicesAction->setObjectName(objectNamePrefix - + "Move_Slice_to_ID"); + + "MoveSliceToID"); macroManager->addMacroSupportToObject(m_volumeIdentificationUpdatesSlicesAction); m_volumeSliceProjectionTypeEnumComboBox = new EnumComboBoxTemplate(this); @@ -233,7 +238,7 @@ m_parentToolBar(parentToolBar) QObject::connect(m_obliqueMaskingAction, &QAction::triggered, this, &BrainBrowserWindowToolBarSliceSelection::obliqueMaskingActionTriggered); m_obliqueMaskingAction->setObjectName(objectNamePrefix - + "Oblique_Masking"); + + "ObliqueMasking"); macroManager->addMacroSupportToObject(m_obliqueMaskingAction); QToolButton* obliqueMaskingToolButton = new QToolButton(); diff --git a/src/GuiQt/BrainBrowserWindowToolBarSliceSelection.h b/src/GuiQt/BrainBrowserWindowToolBarSliceSelection.h index 1274f65b6..1e489a4cc 100644 --- a/src/GuiQt/BrainBrowserWindowToolBarSliceSelection.h +++ b/src/GuiQt/BrainBrowserWindowToolBarSliceSelection.h @@ -39,7 +39,8 @@ namespace caret { Q_OBJECT public: - BrainBrowserWindowToolBarSliceSelection(BrainBrowserWindowToolBar* parentToolBar); + BrainBrowserWindowToolBarSliceSelection(BrainBrowserWindowToolBar* parentToolBar, + const QString parentObjectName); virtual ~BrainBrowserWindowToolBarSliceSelection(); diff --git a/src/GuiQt/BrainBrowserWindowToolBarSurfaceMontage.cxx b/src/GuiQt/BrainBrowserWindowToolBarSurfaceMontage.cxx index 032d72f1d..cb40b2c44 100644 --- a/src/GuiQt/BrainBrowserWindowToolBarSurfaceMontage.cxx +++ b/src/GuiQt/BrainBrowserWindowToolBarSurfaceMontage.cxx @@ -41,6 +41,7 @@ #include "SurfaceMontageLayoutOrientationEnum.h" #include "SurfaceSelectionModel.h" #include "SurfaceSelectionViewController.h" +#include "WuQMacroManager.h" #include "WuQtUtilities.h" #include "WuQWidgetObjectGroup.h" @@ -59,8 +60,11 @@ using namespace caret; * * @param parentToolBar * parent toolbar. + * @param objectNamePrefix + * Prefix name for naming objects */ -BrainBrowserWindowToolBarSurfaceMontage::BrainBrowserWindowToolBarSurfaceMontage(BrainBrowserWindowToolBar* parentToolBar) +BrainBrowserWindowToolBarSurfaceMontage::BrainBrowserWindowToolBarSurfaceMontage(BrainBrowserWindowToolBar* parentToolBar, + const QString& objectNamePrefix) : BrainBrowserWindowToolBarComponent(parentToolBar), m_parentToolBar(parentToolBar) { @@ -74,6 +78,9 @@ m_parentToolBar(parentToolBar) " Cerebellar Cortex\n" " Cerebral Cortex\n" " Flat Maps")); + m_surfaceMontageConfigurationTypeEnumComboBox->getComboBox()->setObjectName(objectNamePrefix + + ":SurfaceMontageConfiguration"); + WuQMacroManager::instance()->addMacroSupportToObject(m_surfaceMontageConfigurationTypeEnumComboBox->getComboBox()); m_surfaceMontageLayoutOrientationEnumComboBox = new EnumComboBoxTemplate(this); m_surfaceMontageLayoutOrientationEnumComboBox->setup(); @@ -84,12 +91,18 @@ m_parentToolBar(parentToolBar) ("Selects Surface Layout:\n" " Landscape (Layout left-to-right)\n" " Portrait (Layout top-to-bottom)")); + m_surfaceMontageLayoutOrientationEnumComboBox->getComboBox()->setObjectName(objectNamePrefix + + ":SurfaceMontageOrientation"); + WuQMacroManager::instance()->addMacroSupportToObject(m_surfaceMontageLayoutOrientationEnumComboBox->getComboBox()); - m_cerebellarComponent = new SurfaceMontageCerebellarComponent(this); + m_cerebellarComponent = new SurfaceMontageCerebellarComponent(this, + objectNamePrefix); - m_cerebralComponent = new SurfaceMontageCerebralComponent(this); + m_cerebralComponent = new SurfaceMontageCerebralComponent(this, + objectNamePrefix); - m_flatMapsComponent = new SurfaceMontageFlatMapsComponent(this); + m_flatMapsComponent = new SurfaceMontageFlatMapsComponent(this, + objectNamePrefix); QHBoxLayout* configOrientationLayout = new QHBoxLayout(); WuQtUtilities::setLayoutSpacingAndMargins(configOrientationLayout, 2, 0); @@ -221,49 +234,89 @@ BrainBrowserWindowToolBarSurfaceMontage::updateContent(BrowserTabContent* browse * * @param parentToolBar * parent toolbar. + * @param objectNamePrefix + * Prefix name for naming objects */ -SurfaceMontageCerebralComponent::SurfaceMontageCerebralComponent(BrainBrowserWindowToolBarSurfaceMontage* parentToolBarMontage) +SurfaceMontageCerebralComponent::SurfaceMontageCerebralComponent(BrainBrowserWindowToolBarSurfaceMontage* parentToolBarMontage, + const QString& parentObjectNamePrefix) : QWidget(parentToolBarMontage) { + const QString objectNamePrefix(parentObjectNamePrefix + + ":SurfaceMontage"); + m_parentToolBarMontage = parentToolBarMontage; + WuQMacroManager* macroManager = WuQMacroManager::instance(); + m_leftCheckBox = new QCheckBox("Left"); QObject::connect(m_leftCheckBox, SIGNAL(toggled(bool)), this, SLOT(checkBoxSelected(bool))); + m_leftCheckBox->setObjectName(objectNamePrefix + + ":EnableLeft"); + m_leftCheckBox->setToolTip("Enable Left Surfaces"); + macroManager->addMacroSupportToObject(m_leftCheckBox); m_rightCheckBox = new QCheckBox("Right"); QObject::connect(m_rightCheckBox, SIGNAL(toggled(bool)), this, SLOT(checkBoxSelected(bool))); + m_rightCheckBox->setObjectName(objectNamePrefix + + ":EnableRight"); + m_rightCheckBox->setToolTip("Enable Right Surface"); + macroManager->addMacroSupportToObject(m_rightCheckBox); m_lateralCheckBox = new QCheckBox("Lateral"); QObject::connect(m_lateralCheckBox, SIGNAL(toggled(bool)), this, SLOT(checkBoxSelected(bool))); + m_lateralCheckBox->setObjectName(objectNamePrefix + + ":EnableLateralView"); + m_lateralCheckBox->setToolTip("Enable Lateral View"); + macroManager->addMacroSupportToObject(m_lateralCheckBox); m_medialCheckBox = new QCheckBox("Medial"); QObject::connect(m_medialCheckBox, SIGNAL(toggled(bool)), this, SLOT(checkBoxSelected(bool))); + m_medialCheckBox->setObjectName(objectNamePrefix + + ":EnableMedialView"); + m_medialCheckBox->setToolTip("Enable Medial View"); + macroManager->addMacroSupportToObject(m_medialCheckBox); m_surfaceMontageFirstSurfaceCheckBox = new QCheckBox(" "); QObject::connect(m_surfaceMontageFirstSurfaceCheckBox, SIGNAL(toggled(bool)), this, SLOT(checkBoxSelected(bool))); + m_surfaceMontageFirstSurfaceCheckBox->setObjectName(objectNamePrefix + + ":EnableFirstRowSurfaces"); + m_surfaceMontageFirstSurfaceCheckBox->setToolTip("Enable First Surfaces"); + macroManager->addMacroSupportToObject(m_surfaceMontageFirstSurfaceCheckBox); m_surfaceMontageSecondSurfaceCheckBox = new QCheckBox(" "); QObject::connect(m_surfaceMontageSecondSurfaceCheckBox, SIGNAL(toggled(bool)), this, SLOT(checkBoxSelected(bool))); - - m_leftSurfaceViewController = new SurfaceSelectionViewController(this); + m_surfaceMontageSecondSurfaceCheckBox->setObjectName(objectNamePrefix + + ":EnableSecondRowSurfaces"); + m_surfaceMontageSecondSurfaceCheckBox->setToolTip("Enable Second Surfaces"); + macroManager->addMacroSupportToObject(m_surfaceMontageSecondSurfaceCheckBox); + + m_leftSurfaceViewController = new SurfaceSelectionViewController(this, + objectNamePrefix + + ":SurfaceLeftTop"); QObject::connect(m_leftSurfaceViewController, SIGNAL(surfaceSelected(Surface*)), this, SLOT(leftSurfaceSelected(Surface*))); - m_leftSecondSurfaceViewController = new SurfaceSelectionViewController(this); + m_leftSecondSurfaceViewController = new SurfaceSelectionViewController(this, + objectNamePrefix + + ":SurfaceLeftBottom"); QObject::connect(m_leftSecondSurfaceViewController, SIGNAL(surfaceSelected(Surface*)), this, SLOT(leftSecondSurfaceSelected(Surface*))); - m_rightSurfaceViewController = new SurfaceSelectionViewController(this); + m_rightSurfaceViewController = new SurfaceSelectionViewController(this, + objectNamePrefix + + ":SurfaceRightTop"); QObject::connect(m_rightSurfaceViewController, SIGNAL(surfaceSelected(Surface*)), this, SLOT(rightSurfaceSelected(Surface*))); - m_rightSecondSurfaceViewController = new SurfaceSelectionViewController(this); + m_rightSecondSurfaceViewController = new SurfaceSelectionViewController(this, + objectNamePrefix + + ":SurfaceRightBottom"); QObject::connect(m_rightSecondSurfaceViewController, SIGNAL(surfaceSelected(Surface*)), this, SLOT(rightSecondSurfaceSelected(Surface*))); @@ -468,41 +521,77 @@ SurfaceMontageCerebralComponent::checkBoxSelected(bool /*status*/) * * @param parentToolBar * parent toolbar. + * @param objectNamePrefix + * Prefix name for naming objects */ -SurfaceMontageCerebellarComponent::SurfaceMontageCerebellarComponent(BrainBrowserWindowToolBarSurfaceMontage* parentToolBarMontage) +SurfaceMontageCerebellarComponent::SurfaceMontageCerebellarComponent(BrainBrowserWindowToolBarSurfaceMontage* parentToolBarMontage, + const QString& parentObjectNamePrefix) : QWidget(parentToolBarMontage) { + const QString objectNamePrefix(parentObjectNamePrefix + + ":SurfaceMontageCerebellum"); + m_parentToolBarMontage = parentToolBarMontage; + WuQMacroManager* macroManager = WuQMacroManager::instance(); m_dorsalCheckBox = new QCheckBox("Dorsal"); QObject::connect(m_dorsalCheckBox, SIGNAL(toggled(bool)), this, SLOT(checkBoxSelected(bool))); + m_dorsalCheckBox->setObjectName(objectNamePrefix + + ":EnableDorsalView"); + m_dorsalCheckBox->setToolTip("Enable Dorsal View"); + macroManager->addMacroSupportToObject(m_dorsalCheckBox); m_ventralCheckBox = new QCheckBox("Ventral"); QObject::connect(m_ventralCheckBox, SIGNAL(toggled(bool)), this, SLOT(checkBoxSelected(bool))); + m_ventralCheckBox->setObjectName(objectNamePrefix + + ":EnableVentralView"); + m_ventralCheckBox->setToolTip("Enable Ventral View"); + macroManager->addMacroSupportToObject(m_ventralCheckBox); m_anteriorCheckBox = new QCheckBox("Anterior"); QObject::connect(m_anteriorCheckBox, SIGNAL(toggled(bool)), this, SLOT(checkBoxSelected(bool))); + m_anteriorCheckBox->setObjectName(objectNamePrefix + + ":EnableAnteriorView"); + m_anteriorCheckBox->setToolTip("Enable Anterior View"); + macroManager->addMacroSupportToObject(m_anteriorCheckBox); m_posteriorCheckBox = new QCheckBox("Posterior"); QObject::connect(m_posteriorCheckBox, SIGNAL(toggled(bool)), this, SLOT(checkBoxSelected(bool))); + m_posteriorCheckBox->setObjectName(objectNamePrefix + + ":EnablePosteriorView"); + m_posteriorCheckBox->setToolTip("Enable Posterior View"); + macroManager->addMacroSupportToObject(m_posteriorCheckBox); m_firstSurfaceCheckBox = new QCheckBox(" "); QObject::connect(m_firstSurfaceCheckBox, SIGNAL(toggled(bool)), this, SLOT(checkBoxSelected(bool))); + m_firstSurfaceCheckBox->setObjectName(objectNamePrefix + + ":EnableFirstSurface"); + m_firstSurfaceCheckBox->setToolTip("Enable First Cerebellar Surface"); + macroManager->addMacroSupportToObject(m_firstSurfaceCheckBox); m_secondSurfaceCheckBox = new QCheckBox(" "); QObject::connect(m_secondSurfaceCheckBox, SIGNAL(toggled(bool)), this, SLOT(checkBoxSelected(bool))); - - m_firstSurfaceViewController = new SurfaceSelectionViewController(this); + m_secondSurfaceCheckBox->setObjectName(objectNamePrefix + + ":EnableSecondSurface"); + m_secondSurfaceCheckBox->setToolTip("Enable Second Cerebellar Surface"); + macroManager->addMacroSupportToObject(m_secondSurfaceCheckBox); + + m_firstSurfaceViewController = new SurfaceSelectionViewController(this, + objectNamePrefix + + ":SurfaceFirst"); QObject::connect(m_firstSurfaceViewController, SIGNAL(surfaceSelected(Surface*)), this, SLOT(firstSurfaceSelected(Surface*))); - m_secondSurfaceViewController = new SurfaceSelectionViewController(this); + m_secondSurfaceViewController = new SurfaceSelectionViewController(this, + objectNamePrefix + + ":SecondSurface"); + QObject::connect(m_secondSurfaceViewController, SIGNAL(surfaceSelected(Surface*)), this, SLOT(secondSurfaceSelected(Surface*))); @@ -669,48 +758,62 @@ SurfaceMontageCerebellarComponent::checkBoxSelected(bool /*status*/) * * @param parentToolBar * parent toolbar. + * @param objectNamePrefix + * Prefix name for naming objects */ -SurfaceMontageFlatMapsComponent::SurfaceMontageFlatMapsComponent(BrainBrowserWindowToolBarSurfaceMontage* parentToolBarMontage) +SurfaceMontageFlatMapsComponent::SurfaceMontageFlatMapsComponent(BrainBrowserWindowToolBarSurfaceMontage* parentToolBarMontage, + const QString& parentObjectNamePrefix) : QWidget(parentToolBarMontage) { + const QString objectNamePrefix(parentObjectNamePrefix + + ":SurfaceMontageFlat"); + m_parentToolBarMontage = parentToolBarMontage; + WuQMacroManager* macroManager = WuQMacroManager::instance(); + m_leftSurfaceCheckBox = new QCheckBox("Left"); QObject::connect(m_leftSurfaceCheckBox, SIGNAL(toggled(bool)), this, SLOT(checkBoxSelected(bool))); + m_leftSurfaceCheckBox->setObjectName(objectNamePrefix + + ":EnableLeft"); + m_leftSurfaceCheckBox->setToolTip("Enable Left Flat Surface"); + macroManager->addMacroSupportToObject(m_leftSurfaceCheckBox); m_rightSurfaceCheckBox = new QCheckBox("Right"); QObject::connect(m_rightSurfaceCheckBox, SIGNAL(toggled(bool)), this, SLOT(checkBoxSelected(bool))); + m_rightSurfaceCheckBox->setObjectName(objectNamePrefix + + ":EnableRight"); + m_rightSurfaceCheckBox->setToolTip("Enable Right Flat Surface"); + macroManager->addMacroSupportToObject(m_rightSurfaceCheckBox); m_cerebellumSurfaceCheckBox = new QCheckBox("Cerebellum "); QObject::connect(m_cerebellumSurfaceCheckBox, SIGNAL(toggled(bool)), this, SLOT(checkBoxSelected(bool))); - - m_leftSurfaceViewController = new SurfaceSelectionViewController(this); + m_cerebellumSurfaceCheckBox->setObjectName(objectNamePrefix + + ":EnableCerebellum"); + m_cerebellumSurfaceCheckBox->setToolTip("Enable Cerebellum Flat Surface"); + macroManager->addMacroSupportToObject(m_cerebellumSurfaceCheckBox); + + m_leftSurfaceViewController = new SurfaceSelectionViewController(this, + objectNamePrefix + + ":LeftFlatSurface"); QObject::connect(m_leftSurfaceViewController, SIGNAL(surfaceSelected(Surface*)), this, SLOT(leftSurfaceSelected(Surface*))); - m_rightSurfaceViewController = new SurfaceSelectionViewController(this); + m_rightSurfaceViewController = new SurfaceSelectionViewController(this, + objectNamePrefix + + ":RightFlatSurface"); QObject::connect(m_rightSurfaceViewController, SIGNAL(surfaceSelected(Surface*)), this, SLOT(rightSurfaceSelected(Surface*))); - m_cerebellumSurfaceViewController = new SurfaceSelectionViewController(this); + m_cerebellumSurfaceViewController = new SurfaceSelectionViewController(this, + objectNamePrefix + + ":CerebellumSurface"); QObject::connect(m_cerebellumSurfaceViewController, SIGNAL(surfaceSelected(Surface*)), this, SLOT(cerebellumSurfaceSelected(Surface*))); -// QHBoxLayout* checkBoxLayout = new QHBoxLayout(); -// WuQtUtilities::setLayoutSpacingAndMargins(checkBoxLayout, 2, 0); -// checkBoxLayout->addStretch(); -// checkBoxLayout->addWidget(m_leftSurfaceCheckBox); -// checkBoxLayout->addSpacing(5); -// checkBoxLayout->addStretch(); -// checkBoxLayout->addWidget(m_rightSurfaceCheckBox); -// checkBoxLayout->addSpacing(5); -// checkBoxLayout->addStretch(); -// checkBoxLayout->addWidget(m_cerebellumSurfaceCheckBox); -// checkBoxLayout->addStretch(); - int32_t columnIndex = 0; const int32_t COLUMN_CHECKBOX = columnIndex++; const int32_t COLUMN_SELECTION = columnIndex++; @@ -729,10 +832,6 @@ SurfaceMontageFlatMapsComponent::SurfaceMontageFlatMapsComponent(BrainBrowserWin layout->addWidget(m_cerebellumSurfaceCheckBox, row, COLUMN_CHECKBOX); layout->addWidget(m_cerebellumSurfaceViewController->getWidget(), row, COLUMN_SELECTION); row = layout->rowCount(); -// layout->addLayout(checkBoxLayout, -// row, COLUMN_CHECKBOX, -// 1, 2); -// row = layout->rowCount(); m_widgetGroup = new WuQWidgetObjectGroup(this); m_widgetGroup->add(m_leftSurfaceViewController->getWidget()); diff --git a/src/GuiQt/BrainBrowserWindowToolBarSurfaceMontage.h b/src/GuiQt/BrainBrowserWindowToolBarSurfaceMontage.h index 04f92d97b..59e28f2eb 100644 --- a/src/GuiQt/BrainBrowserWindowToolBarSurfaceMontage.h +++ b/src/GuiQt/BrainBrowserWindowToolBarSurfaceMontage.h @@ -41,7 +41,8 @@ namespace caret { Q_OBJECT public: - BrainBrowserWindowToolBarSurfaceMontage(BrainBrowserWindowToolBar* parentToolBar); + BrainBrowserWindowToolBarSurfaceMontage(BrainBrowserWindowToolBar* parentToolBar, + const QString& parentObjectNamePrefix); virtual ~BrainBrowserWindowToolBarSurfaceMontage(); @@ -84,7 +85,8 @@ namespace caret { Q_OBJECT public: - SurfaceMontageCerebralComponent(BrainBrowserWindowToolBarSurfaceMontage* parentToolBarMontage); + SurfaceMontageCerebralComponent(BrainBrowserWindowToolBarSurfaceMontage* parentToolBarMontage, + const QString& parentObjectNamePrefix); ~SurfaceMontageCerebralComponent(); @@ -121,7 +123,8 @@ namespace caret { Q_OBJECT public: - SurfaceMontageCerebellarComponent(BrainBrowserWindowToolBarSurfaceMontage* parentToolBarMontage); + SurfaceMontageCerebellarComponent(BrainBrowserWindowToolBarSurfaceMontage* parentToolBarMontage, + const QString& parentObjectNamePrefix); ~SurfaceMontageCerebellarComponent(); @@ -154,7 +157,8 @@ namespace caret { Q_OBJECT public: - SurfaceMontageFlatMapsComponent(BrainBrowserWindowToolBarSurfaceMontage* parentToolBarMontage); + SurfaceMontageFlatMapsComponent(BrainBrowserWindowToolBarSurfaceMontage* parentToolBarMontage, + const QString& parentObjectNamePrefix); ~SurfaceMontageFlatMapsComponent(); diff --git a/src/GuiQt/BrainBrowserWindowToolBarTab.cxx b/src/GuiQt/BrainBrowserWindowToolBarTab.cxx index d632cf147..2d70ece69 100644 --- a/src/GuiQt/BrainBrowserWindowToolBarTab.cxx +++ b/src/GuiQt/BrainBrowserWindowToolBarTab.cxx @@ -41,6 +41,7 @@ #include "WuQWidgetObjectGroup.h" #include "YokingGroupEnum.h" #include "WuQDataEntryDialog.h" +#include "WuQMacroManager.h" #include "WuQtUtilities.h" using namespace caret; @@ -60,10 +61,13 @@ using namespace caret; * Button for locking window and all tab aspect ratio. * @param parentToolBar * Parent toolbar. + * @param objectNamePrefix + Prefix for naming objects */ BrainBrowserWindowToolBarTab::BrainBrowserWindowToolBarTab(const int32_t browserWindowIndex, QToolButton* toolBarLockWindowAndAllTabAspectRatioButton, - BrainBrowserWindowToolBar* parentToolBar) + BrainBrowserWindowToolBar* parentToolBar, + const QString& objectNamePrefix) : BrainBrowserWindowToolBarComponent(parentToolBar), m_browserWindowIndex(browserWindowIndex), m_parentToolBar(parentToolBar), @@ -76,6 +80,10 @@ m_lockWindowAndAllTabAspectButton(toolBarLockWindowAndAllTabAspectRatioButton) "Models yoked to a group are displayed in the same view.\n" "Surface Yoking is applied to Surface, Surface Montage\n" "and Whole Brain. Volume Yoking is applied to Volumes.")); + QComboBox* encapComboBox = m_yokingGroupComboBox->getComboBox(); + encapComboBox->setObjectName(objectNamePrefix + + ":Tab:YokingGroup"); + WuQMacroManager::instance()->addMacroSupportToObject(encapComboBox); m_yokeToLabel = new QLabel("Yoking:"); QObject::connect(m_yokingGroupComboBox, SIGNAL(itemActivated()), @@ -86,8 +94,11 @@ m_lockWindowAndAllTabAspectButton(toolBarLockWindowAndAllTabAspectRatioButton) "may be helpful to turn shading off."); m_lightingEnabledCheckBox = new QCheckBox("Shading"); m_lightingEnabledCheckBox->setToolTip(lightToolTip); - QObject::connect(m_lightingEnabledCheckBox, &QCheckBox::toggled, - this, &BrainBrowserWindowToolBarTab::lightingEnabledCheckBoxToggled); + QObject::connect(m_lightingEnabledCheckBox, &QCheckBox::clicked, + this, &BrainBrowserWindowToolBarTab::lightingEnabledCheckBoxChecked); + m_lightingEnabledCheckBox->setObjectName(objectNamePrefix + + ":Tab:EnableShading"); + WuQMacroManager::instance()->addMacroSupportToObject(m_lightingEnabledCheckBox); QVBoxLayout* layout = new QVBoxLayout(this); WuQtUtilities::setLayoutSpacingAndMargins(layout, 4, 0); @@ -158,13 +169,13 @@ BrainBrowserWindowToolBarTab::updateContent(BrowserTabContent* browserTabContent } /** - * Called when lighting checkbox is toggled by user + * Called when lighting checkbox is checked by user * * @param checked * New status of lighting. */ void -BrainBrowserWindowToolBarTab::lightingEnabledCheckBoxToggled(bool checked) +BrainBrowserWindowToolBarTab::lightingEnabledCheckBoxChecked(bool checked) { BrowserTabContent* browserTabContent = this->getTabContentFromSelectedTab(); if (browserTabContent == NULL) { diff --git a/src/GuiQt/BrainBrowserWindowToolBarTab.h b/src/GuiQt/BrainBrowserWindowToolBarTab.h index 4b9595537..dbed671b0 100644 --- a/src/GuiQt/BrainBrowserWindowToolBarTab.h +++ b/src/GuiQt/BrainBrowserWindowToolBarTab.h @@ -39,7 +39,8 @@ namespace caret { public: BrainBrowserWindowToolBarTab(const int32_t browserWindowIndex, QToolButton* toolBarLockWindowAndAllTabAspectRatioButton, - BrainBrowserWindowToolBar* parentToolBar); + BrainBrowserWindowToolBar* parentToolBar, + const QString& objectNamePrefix); virtual ~BrainBrowserWindowToolBarTab(); @@ -51,7 +52,7 @@ namespace caret { private slots: void yokeToGroupComboBoxIndexChanged(); - void lightingEnabledCheckBoxToggled(bool checked); + void lightingEnabledCheckBoxChecked(bool checked); private: BrainBrowserWindowToolBarTab(const BrainBrowserWindowToolBarTab&); diff --git a/src/GuiQt/BrainBrowserWindowToolBarVolumeMontage.cxx b/src/GuiQt/BrainBrowserWindowToolBarVolumeMontage.cxx index 23cd40bb1..232cdba96 100644 --- a/src/GuiQt/BrainBrowserWindowToolBarVolumeMontage.cxx +++ b/src/GuiQt/BrainBrowserWindowToolBarVolumeMontage.cxx @@ -33,6 +33,7 @@ #include "BrowserTabContent.h" #include "CaretAssert.h" #include "WuQFactory.h" +#include "WuQMacroManager.h" #include "WuQWidgetObjectGroup.h" #include "WuQtUtilities.h" @@ -48,11 +49,19 @@ using namespace caret; /** * Constructor. + * + * @param parentObjectName + * Name of parent for macros + * @param parentToolBar + * The parent toolbar */ -BrainBrowserWindowToolBarVolumeMontage::BrainBrowserWindowToolBarVolumeMontage(BrainBrowserWindowToolBar* parentToolBar) +BrainBrowserWindowToolBarVolumeMontage::BrainBrowserWindowToolBarVolumeMontage(const QString& parentObjectName, + BrainBrowserWindowToolBar* parentToolBar) : BrainBrowserWindowToolBarComponent(parentToolBar), m_parentToolBar(parentToolBar) { + const QString objectNamePrefix(parentObjectName + + ":VolumeSliceMontage:"); const int spinBoxWidth = 48; @@ -64,6 +73,10 @@ m_parentToolBar(parentToolBar) m_montageRowsSpinBox->setToolTip(rowsLabel->toolTip()); QObject::connect(m_montageRowsSpinBox, SIGNAL(valueChanged(int)), this, SLOT(montageRowsSpinBoxValueChanged(int))); + m_montageRowsSpinBox->setObjectName(objectNamePrefix + + "Rows"); + WuQMacroManager::instance()->addMacroSupportToObject(m_montageRowsSpinBox); + QLabel* columnsLabel = new QLabel("Cols:"); columnsLabel->setToolTip("Select the number of columns in montage of volume slices"); @@ -73,6 +86,9 @@ m_parentToolBar(parentToolBar) m_montageColumnsSpinBox->setToolTip(columnsLabel->toolTip()); QObject::connect(m_montageColumnsSpinBox, SIGNAL(valueChanged(int)), this, SLOT(montageColumnsSpinBoxValueChanged(int))); + m_montageColumnsSpinBox->setObjectName(objectNamePrefix + + "Columns"); + WuQMacroManager::instance()->addMacroSupportToObject(m_montageColumnsSpinBox); QLabel* spacingLabel = new QLabel("Step:"); spacingLabel->setToolTip("Select the number of slices stepped (incremented) between displayed montage slices"); @@ -82,6 +98,9 @@ m_parentToolBar(parentToolBar) m_montageSpacingSpinBox->setToolTip(spacingLabel->toolTip()); QObject::connect(m_montageSpacingSpinBox, SIGNAL(valueChanged(int)), this, SLOT(montageSpacingSpinBoxValueChanged(int))); + m_montageSpacingSpinBox->setObjectName(objectNamePrefix + + "Step"); + WuQMacroManager::instance()->addMacroSupportToObject(m_montageSpacingSpinBox); m_showSliceCoordinateAction = new QAction("XYZ", this); m_showSliceCoordinateAction->setText("XYZ"); @@ -89,6 +108,9 @@ m_parentToolBar(parentToolBar) m_showSliceCoordinateAction->setToolTip("Show coordinates on slices"); QObject::connect(m_showSliceCoordinateAction, &QAction::triggered, this, &BrainBrowserWindowToolBarVolumeMontage::showSliceCoordinateToolButtonClicked); + m_showSliceCoordinateAction->setObjectName(objectNamePrefix + + "ShowCoordinateOnSlice"); + WuQMacroManager::instance()->addMacroSupportToObject(m_showSliceCoordinateAction); QToolButton* showSliceCoordToolButton = new QToolButton; showSliceCoordToolButton->setDefaultAction(m_showSliceCoordinateAction); @@ -102,17 +124,23 @@ m_parentToolBar(parentToolBar) m_sliceCoordinatePrecisionSpinBox->setToolTip(decimalsLabel->toolTip()); QObject::connect(m_sliceCoordinatePrecisionSpinBox, static_cast(&QSpinBox::valueChanged), this, &BrainBrowserWindowToolBarVolumeMontage::slicePrecisionSpinBoxValueChanged); + m_sliceCoordinatePrecisionSpinBox->setObjectName(objectNamePrefix + + "Precision"); + WuQMacroManager::instance()->addMacroSupportToObject(m_sliceCoordinatePrecisionSpinBox); + QToolButton* montageEnabledToolButton = new QToolButton(); m_montageEnabledAction = WuQtUtilities::createAction("On", "View a montage of parallel slices", - this, + montageEnabledToolButton, this, SLOT(montageEnabledActionToggled(bool))); m_montageEnabledAction->setCheckable(true); - QToolButton* montageEnabledToolButton = new QToolButton(); montageEnabledToolButton->setDefaultAction(m_montageEnabledAction); WuQtUtilities::setToolButtonStyleForQt5Mac(montageEnabledToolButton); + m_montageEnabledAction->setObjectName(objectNamePrefix + + "Enable"); + WuQMacroManager::instance()->addMacroSupportToObject(m_montageEnabledAction); QGridLayout* gridLayout = new QGridLayout(this); WuQtUtilities::setLayoutSpacingAndMargins(gridLayout, 0, 0); @@ -127,7 +155,6 @@ m_parentToolBar(parentToolBar) gridLayout->addWidget(m_sliceCoordinatePrecisionSpinBox, 3, 1); gridLayout->addWidget(showSliceCoordToolButton, 4, 0); gridLayout->addWidget(montageEnabledToolButton, 4, 1); -// gridLayout->addWidget(montageEnabledToolButton, 4, 0, 1, 2, Qt::AlignHCenter); m_volumeMontageWidgetGroup = new WuQWidgetObjectGroup(this); m_volumeMontageWidgetGroup->add(m_montageRowsSpinBox); diff --git a/src/GuiQt/BrainBrowserWindowToolBarVolumeMontage.h b/src/GuiQt/BrainBrowserWindowToolBarVolumeMontage.h index d0ce2fcc1..985e26138 100644 --- a/src/GuiQt/BrainBrowserWindowToolBarVolumeMontage.h +++ b/src/GuiQt/BrainBrowserWindowToolBarVolumeMontage.h @@ -35,7 +35,8 @@ namespace caret { Q_OBJECT public: - BrainBrowserWindowToolBarVolumeMontage(BrainBrowserWindowToolBar* parentToolBar); + BrainBrowserWindowToolBarVolumeMontage(const QString& parentObjectName, + BrainBrowserWindowToolBar* parentToolBar); virtual ~BrainBrowserWindowToolBarVolumeMontage(); diff --git a/src/GuiQt/CaretColorEnumComboBox.cxx b/src/GuiQt/CaretColorEnumComboBox.cxx index e59e78dc2..2354a9712 100644 --- a/src/GuiQt/CaretColorEnumComboBox.cxx +++ b/src/GuiQt/CaretColorEnumComboBox.cxx @@ -26,6 +26,7 @@ #include #include "CaretAssert.h" +#include "WuQMacroManager.h" #include "WuQtUtilities.h" using namespace caret; @@ -91,6 +92,34 @@ CaretColorEnumComboBox::CaretColorEnumComboBox(const AString& customColorSelecti &customColorSelectionIcon); } +/** + * Constructor. + * + * @param customColorSelectionName + * If not empty, CaretColorEnum::CUSTOM is added to the combo with this name as the text. + * @param customColorSelectionIcon + * Icon for custom color. + * @param objectNameForMacros + * If not empty, name is used to set this combo box for macro support + * @param parent + * Parent object. + */ +CaretColorEnumComboBox::CaretColorEnumComboBox(const AString& customColorSelectionName, + const QIcon& customColorSelectionIcon, + const QString& objectNameForMacros, + QObject* parent) +: WuQWidget(parent) +{ + initializeCaretColorComboBox(customColorSelectionName, + &customColorSelectionIcon); + + if ( ! objectNameForMacros.isEmpty()) { + this->colorComboBox->setObjectName(objectNameForMacros); + this->colorComboBox->setToolTip("Select Color"); + WuQMacroManager::instance()->addMacroSupportToObject(this->colorComboBox); + } +} + /** * Destructor. */ diff --git a/src/GuiQt/CaretColorEnumComboBox.h b/src/GuiQt/CaretColorEnumComboBox.h index 4e991eeee..637febbaa 100644 --- a/src/GuiQt/CaretColorEnumComboBox.h +++ b/src/GuiQt/CaretColorEnumComboBox.h @@ -43,6 +43,11 @@ namespace caret { const QIcon& customColorSelectionIcon, QObject* parent); + CaretColorEnumComboBox(const AString& customColorSelectionName, + const QIcon& customColorSelectionIcon, + const QString& objectNameForMacros, + QObject* parent); + virtual ~CaretColorEnumComboBox(); CaretColorEnum::Enum getSelectedColor(); diff --git a/src/GuiQt/ChartHistoryViewController.cxx b/src/GuiQt/ChartHistoryViewController.cxx index 89d42473b..3d3a3e4c8 100644 --- a/src/GuiQt/ChartHistoryViewController.cxx +++ b/src/GuiQt/ChartHistoryViewController.cxx @@ -48,6 +48,7 @@ #include "EventGraphicsUpdateOneWindow.h" #include "GuiManager.h" #include "ModelChart.h" +#include "WuQMacroManager.h" #include "WuQtUtilities.h" using namespace caret; @@ -65,11 +66,15 @@ using namespace caret; */ ChartHistoryViewController::ChartHistoryViewController(const Qt::Orientation orientation, const int32_t browserWindowIndex, + const QString& parentObjectName, QWidget* parent) : QWidget(parent), m_orientation(orientation), -m_browserWindowIndex(browserWindowIndex) +m_browserWindowIndex(browserWindowIndex), +m_objectNamePrefix(parentObjectName + + ":History") { + WuQMacroManager* macroManager = WuQMacroManager::instance(); m_averageCheckBox = new QCheckBox("Show Average"); WuQtUtilities::setWordWrappedToolTip(m_averageCheckBox, "Display an average of the displayed chart data. " @@ -79,6 +84,9 @@ m_browserWindowIndex(browserWindowIndex) "chart."); QObject::connect(m_averageCheckBox, SIGNAL(clicked(bool)), this, SLOT(averageCheckBoxClicked(bool))); + m_averageCheckBox->setObjectName(m_objectNamePrefix + + ":ShowAverage"); + macroManager->addMacroSupportToObject(m_averageCheckBox); QPushButton* clearPushButton = new QPushButton("Clear"); clearPushButton->setFixedWidth(clearPushButton->sizeHint().width() + 20); @@ -86,6 +94,10 @@ m_browserWindowIndex(browserWindowIndex) this, SLOT(clearPushButtonClicked())); WuQtUtilities::setWordWrappedToolTip(clearPushButton, "Remove all charts of the selected type in this tab"); + clearPushButton->setObjectName(m_objectNamePrefix + + ":ClearButton"); + macroManager->addMacroSupportToObject(clearPushButton); + QLabel* maximumDisplayedLabel = new QLabel("Show last "); m_maximumDisplayedSpinBox = new QSpinBox(); @@ -93,6 +105,11 @@ m_browserWindowIndex(browserWindowIndex) m_maximumDisplayedSpinBox->setMaximum(1000); QObject::connect(m_maximumDisplayedSpinBox, SIGNAL(valueChanged(int)), this, SLOT(maximumDisplayedSpinBoxValueChanged(int))); + m_maximumDisplayedSpinBox->setToolTip("Show Last Lines"); + m_maximumDisplayedSpinBox->setObjectName(m_objectNamePrefix + + ":ShowLastCount"); + macroManager->addMacroSupportToObject(m_maximumDisplayedSpinBox); + QHBoxLayout* maxDisplayedLayout = new QHBoxLayout(); maxDisplayedLayout->addWidget(maximumDisplayedLabel); maxDisplayedLayout->addWidget(m_maximumDisplayedSpinBox); @@ -283,6 +300,7 @@ ChartHistoryViewController::updateHistoryViewController() break; } + WuQMacroManager* macroManager = WuQMacroManager::instance(); const std::vector chartDataVector = chartModel->getAllChartDatas(); const int32_t numData = static_cast(chartDataVector.size()); @@ -291,6 +309,8 @@ ChartHistoryViewController::updateHistoryViewController() numWidgetRows); for (int32_t i = 0; i < maxItems; i++) { if (i >= static_cast(m_chartDataCheckBoxes.size())) { + const QString numberString(QString("%1").arg((int)i+1, 2, 10, QLatin1Char('0'))); + /* * Checkbox */ @@ -299,6 +319,12 @@ ChartHistoryViewController::updateHistoryViewController() m_chartDataCheckBoxesSignalMapper, SLOT(map())); m_chartDataCheckBoxesSignalMapper->setMapping(checkBox, i); m_chartDataCheckBoxes.push_back(checkBox); + checkBox->setToolTip("Show Chart Line"); + checkBox->setObjectName(m_objectNamePrefix + + ":ShowChartLine" + + numberString); + macroManager->addMacroSupportToObject(checkBox); + /* * Construction Tool Button @@ -326,6 +352,11 @@ ChartHistoryViewController::updateHistoryViewController() m_chartDataColorComboBoxesSignalMapper, SLOT(map())); m_chartDataColorComboBoxesSignalMapper->setMapping(colorComboBox, i); m_chartDataColorComboBoxes.push_back(colorComboBox); + colorComboBox->getWidget()->setToolTip("Select Color"); + colorComboBox->getWidget()->setObjectName(m_objectNamePrefix + + ":Color" + + numberString); + macroManager->addMacroSupportToObject(colorComboBox->getWidget()); /* * Label diff --git a/src/GuiQt/ChartHistoryViewController.h b/src/GuiQt/ChartHistoryViewController.h index 75fbc9570..e4d81f4ab 100644 --- a/src/GuiQt/ChartHistoryViewController.h +++ b/src/GuiQt/ChartHistoryViewController.h @@ -48,6 +48,7 @@ namespace caret { public: ChartHistoryViewController(const Qt::Orientation orientation, const int32_t browserWindowIndex, + const QString& parentObjectName, QWidget* parent); virtual ~ChartHistoryViewController(); @@ -90,6 +91,8 @@ namespace caret { const int32_t m_browserWindowIndex; + const QString m_objectNamePrefix; + QCheckBox* m_averageCheckBox; QSpinBox* m_maximumDisplayedSpinBox; diff --git a/src/GuiQt/ChartSelectionViewController.cxx b/src/GuiQt/ChartSelectionViewController.cxx index c60622986..4a6fc2789 100644 --- a/src/GuiQt/ChartSelectionViewController.cxx +++ b/src/GuiQt/ChartSelectionViewController.cxx @@ -37,6 +37,7 @@ #include "EventUserInterfaceUpdate.h" #include "GuiManager.h" #include "ModelChart.h" +#include "WuQMacroManager.h" #include "WuQtUtilities.h" using namespace caret; @@ -52,10 +53,15 @@ using namespace caret; */ ChartSelectionViewController::ChartSelectionViewController(const Qt::Orientation orientation, const int32_t browserWindowIndex, + const QString& parentObjectName, QWidget* parent) : QWidget(parent), m_browserWindowIndex(browserWindowIndex) + { + const QString objectNamePrefix(parentObjectName + + ":History"); + m_mode = MODE_INVALID; m_brainordinateChartWidget = new ChartLinesSelectionViewController(orientation, diff --git a/src/GuiQt/ChartSelectionViewController.h b/src/GuiQt/ChartSelectionViewController.h index fb2fe096a..437c6beb8 100644 --- a/src/GuiQt/ChartSelectionViewController.h +++ b/src/GuiQt/ChartSelectionViewController.h @@ -41,6 +41,7 @@ namespace caret { public: ChartSelectionViewController(const Qt::Orientation orientation, const int32_t browserWindowIndex, + const QString& parentObjectName, QWidget* parent); virtual ~ChartSelectionViewController(); diff --git a/src/GuiQt/ChartToolBoxViewController.cxx b/src/GuiQt/ChartToolBoxViewController.cxx index f345685bb..298cba03c 100644 --- a/src/GuiQt/ChartToolBoxViewController.cxx +++ b/src/GuiQt/ChartToolBoxViewController.cxx @@ -42,6 +42,7 @@ #include "ModelChart.h" #include "SceneClass.h" #include "SceneClassAssistant.h" +#include "WuQMacroManager.h" using namespace caret; @@ -56,28 +57,47 @@ using namespace caret; /** * Constructor. + * + * @param orientation + * Orientation of toolbox + * @param browserWindowIndex + * Index of browser window + * @param parentObjectName + * Name of parent object for macros + * @param parent + * Parent widget */ ChartToolBoxViewController::ChartToolBoxViewController(const Qt::Orientation orientation, const int32_t browserWindowIndex, + const QString& parentObjectName, QWidget* parent) : QWidget(parent), EventListenerInterface(), m_browserWindowIndex(browserWindowIndex) { + const QString objectNamePrefix(parentObjectName + + ":Chart"); + /* * Data series widgets */ m_chartSelectionViewController = new ChartSelectionViewController(orientation, - browserWindowIndex, - NULL); + browserWindowIndex, + objectNamePrefix, + NULL); m_chartHistoryViewController = new ChartHistoryViewController(orientation, - browserWindowIndex, - NULL); + browserWindowIndex, + objectNamePrefix, + NULL); m_tabWidget = new QTabWidget(); m_tabWidget->addTab(m_chartSelectionViewController, "Loading"); m_tabWidget->addTab(m_chartHistoryViewController, "History"); + m_tabWidget->setObjectName(objectNamePrefix + + ":Tab"); + WuQMacroManager::instance()->addMacroSupportToObjectWithToolTip(m_tabWidget, ""); + QVBoxLayout* layout = new QVBoxLayout(this); layout->addWidget(m_tabWidget); diff --git a/src/GuiQt/ChartToolBoxViewController.h b/src/GuiQt/ChartToolBoxViewController.h index debc5c366..adb061344 100644 --- a/src/GuiQt/ChartToolBoxViewController.h +++ b/src/GuiQt/ChartToolBoxViewController.h @@ -43,6 +43,7 @@ namespace caret { public: ChartToolBoxViewController(const Qt::Orientation orientation, const int32_t browserWindowIndex, + const QString& parentObjectName, QWidget* parent); virtual ~ChartToolBoxViewController(); diff --git a/src/GuiQt/ChartTwoOverlaySetViewController.cxx b/src/GuiQt/ChartTwoOverlaySetViewController.cxx index 687430db7..5db1dbc90 100644 --- a/src/GuiQt/ChartTwoOverlaySetViewController.cxx +++ b/src/GuiQt/ChartTwoOverlaySetViewController.cxx @@ -63,12 +63,15 @@ using namespace caret; * Orientation for layout * @param browserWindowIndex * Index of browser window that contains this view controller. + * @param parentObjectName + * Name of parent object for macros * @param parent * Parent widget. */ ChartTwoOverlaySetViewController::ChartTwoOverlaySetViewController(const Qt::Orientation orientation, - const int32_t browserWindowIndex, - QWidget* parent) + const int32_t browserWindowIndex, + const QString& parentObjectName, + QWidget* parent) : QWidget(parent), m_browserWindowIndex(browserWindowIndex) { @@ -77,10 +80,11 @@ m_browserWindowIndex(browserWindowIndex) WuQtUtilities::setLayoutSpacingAndMargins(gridLayout, 4, 2); for (int32_t i = 0; i < BrainConstants::MAXIMUM_NUMBER_OF_OVERLAYS; i++) { - ChartTwoOverlayViewController* ovc = new ChartTwoOverlayViewController(orientation, - m_browserWindowIndex, - i, - this); + ChartTwoOverlayViewController* ovc = new ChartTwoOverlayViewController(orientation, + m_browserWindowIndex, + i, + parentObjectName, + this); m_chartOverlayViewControllers.push_back(ovc); m_chartOverlayGridLayoutGroups.push_back(new WuQGridLayoutGroup(gridLayout, this)); diff --git a/src/GuiQt/ChartTwoOverlaySetViewController.h b/src/GuiQt/ChartTwoOverlaySetViewController.h index f637eae69..de18b713f 100644 --- a/src/GuiQt/ChartTwoOverlaySetViewController.h +++ b/src/GuiQt/ChartTwoOverlaySetViewController.h @@ -40,8 +40,9 @@ namespace caret { public: ChartTwoOverlaySetViewController(const Qt::Orientation orientation, - const int32_t browserWindowIndex, - QWidget* parent = 0); + const int32_t browserWindowIndex, + const QString& parentObjectName, + QWidget* parent = 0); virtual ~ChartTwoOverlaySetViewController(); diff --git a/src/GuiQt/ChartTwoOverlayViewController.cxx b/src/GuiQt/ChartTwoOverlayViewController.cxx index db16ad6dd..41797ddfb 100644 --- a/src/GuiQt/ChartTwoOverlayViewController.cxx +++ b/src/GuiQt/ChartTwoOverlayViewController.cxx @@ -59,6 +59,7 @@ using namespace caret; #include "MapYokingGroupComboBox.h" #include "UsernamePasswordWidget.h" #include "WuQFactory.h" +#include "WuQMacroManager.h" #include "WuQMessageBox.h" #include "WuQtUtilities.h" @@ -77,13 +78,16 @@ using namespace caret; * Index of browser window in which this view controller resides. * @param chartOverlayIndex * Index of this overlay view controller. + * @param parentObjectName + * Name of parent object for macros * @param parent * The parent widget. */ ChartTwoOverlayViewController::ChartTwoOverlayViewController(const Qt::Orientation orientation, - const int32_t browserWindowIndex, - const int32_t chartOverlayIndex, - QObject* parent) + const int32_t browserWindowIndex, + const int32_t chartOverlayIndex, + const QString& parentObjectName, + QObject* parent) : QObject(parent), m_browserWindowIndex(browserWindowIndex), m_chartOverlayIndex(chartOverlayIndex), @@ -97,14 +101,23 @@ m_chartOverlay(NULL) } const QComboBox::SizeAdjustPolicy comboSizePolicy = QComboBox::AdjustToContentsOnFirstShow; //QComboBox::AdjustToContents; + WuQMacroManager* macroManager = WuQMacroManager::instance(); + CaretAssert(macroManager); + QString objectNamePrefix = QString(parentObjectName + + ":ChartOverlay%1" + + ":").arg((int)(chartOverlayIndex + 1), 2, 10, QLatin1Char('0')); + /* * Enabled Check Box */ const QString enabledCheckboxText = ((orientation == Qt::Horizontal) ? " " : "On"); m_enabledCheckBox = new QCheckBox(enabledCheckboxText); + m_enabledCheckBox->setObjectName(objectNamePrefix + + "OnOff"); QObject::connect(m_enabledCheckBox, SIGNAL(clicked(bool)), this, SLOT(enabledCheckBoxClicked(bool))); m_enabledCheckBox->setToolTip("Display line charts from the selected file"); + macroManager->addMacroSupportToObject(m_enabledCheckBox); /* * Line Series Enabled Check Box @@ -114,6 +127,9 @@ m_chartOverlay(NULL) QObject::connect(m_lineSeriesLoadingEnabledCheckBox, &QCheckBox::clicked, this, &ChartTwoOverlayViewController::lineSeriesLoadingEnabledCheckBoxClicked); m_lineSeriesLoadingEnabledCheckBox->setToolTip("Enable loading of line charts for the selected file"); + m_lineSeriesLoadingEnabledCheckBox->setObjectName(objectNamePrefix + + "EnableLoading"); + macroManager->addMacroSupportToObject(m_lineSeriesLoadingEnabledCheckBox); /* * Settings Tool Button @@ -122,15 +138,18 @@ m_chartOverlay(NULL) const bool settingsIconValid = WuQtUtilities::loadIcon(":/LayersPanel/wrench.png", settingsIcon); + m_settingsToolButton = new QToolButton(); m_settingsAction = WuQtUtilities::createAction("S", "Edit settings for this chart", - this, + m_settingsToolButton, this, SLOT(settingsActionTriggered())); if (settingsIconValid) { m_settingsAction->setIcon(settingsIcon); } - m_settingsToolButton = new QToolButton(); + m_settingsAction->setObjectName(objectNamePrefix + + "ShowSettingsDialog"); + macroManager->addMacroSupportToObject(m_settingsAction); m_settingsToolButton->setDefaultAction(m_settingsAction); /* @@ -139,20 +158,24 @@ m_chartOverlay(NULL) QIcon colorBarIcon; const bool colorBarIconValid = WuQtUtilities::loadIcon(":/LayersPanel/colorbar.png", colorBarIcon); + m_colorBarToolButton = new QToolButton(); m_colorBarAction = WuQtUtilities::createAction("CB", "Display color bar for this overlay", - this, + m_colorBarToolButton, this, SLOT(colorBarActionTriggered(bool))); m_colorBarAction->setCheckable(true); if (colorBarIconValid) { m_colorBarAction->setIcon(colorBarIcon); } - m_colorBarToolButton = new QToolButton(); + m_colorBarAction->setObjectName(objectNamePrefix + + "ShowColorBar"); + macroManager->addMacroSupportToObject(m_colorBarAction); m_colorBarToolButton->setDefaultAction(m_colorBarAction); /* * Construction Tool Button + * Note: macro support is on each action in menu in 'createConstructionMenu' */ QIcon constructionIcon; const bool constructionIconValid = WuQtUtilities::loadIcon(":/LayersPanel/construction.png", @@ -164,19 +187,24 @@ m_chartOverlay(NULL) m_constructionAction->setIcon(constructionIcon); } m_constructionToolButton = new QToolButton(); - QMenu* constructionMenu = createConstructionMenu(m_constructionToolButton); + QMenu* constructionMenu = createConstructionMenu(m_constructionToolButton, + (objectNamePrefix + + "ConstructionMenu:")); m_constructionAction->setMenu(constructionMenu); m_constructionToolButton->setDefaultAction(m_constructionAction); m_constructionToolButton->setPopupMode(QToolButton::InstantPopup); /* * Matrix triangular view mode button + * Note: macro support is on each action in menu in createMatrixTriangularViewModeMenu */ + m_matrixTriangularViewModeToolButton = new QToolButton(); m_matrixTriangularViewModeAction = WuQtUtilities::createAction("M", "Select a triangular view of the matrix", - this); - m_matrixTriangularViewModeToolButton = new QToolButton(); - QMenu* matrixTriangularViewModeMenu = createMatrixTriangularViewModeMenu(m_matrixTriangularViewModeToolButton); + m_matrixTriangularViewModeToolButton); + QMenu* matrixTriangularViewModeMenu = createMatrixTriangularViewModeMenu(m_matrixTriangularViewModeToolButton, + (objectNamePrefix + + "TriangularViewMenu:")); m_matrixTriangularViewModeAction->setMenu(matrixTriangularViewModeMenu); m_matrixTriangularViewModeToolButton->setDefaultAction(m_matrixTriangularViewModeAction); m_matrixTriangularViewModeToolButton->setPopupMode(QToolButton::InstantPopup); @@ -184,12 +212,15 @@ m_chartOverlay(NULL) /* * Axis location button + * Note: macro support is on each action in menu in createMatrixTriangularViewModeMenu */ + m_axisLocationToolButton = new QToolButton(); m_axisLocationAction = WuQtUtilities::createAction("A", "Select location of vertical axis for the selected file", - this); - m_axisLocationToolButton = new QToolButton(); - QMenu* axisLocationMenu = createAxisLocationMenu(m_axisLocationToolButton); + m_axisLocationToolButton); + QMenu* axisLocationMenu = createAxisLocationMenu(m_axisLocationToolButton, + (objectNamePrefix + + "VerticalAxisLocationMenu:")); m_axisLocationAction->setMenu(axisLocationMenu); m_axisLocationToolButton->setDefaultAction(m_axisLocationAction); m_axisLocationToolButton->setPopupMode(QToolButton::InstantPopup); @@ -204,6 +235,9 @@ m_chartOverlay(NULL) this, SLOT(fileComboBoxSelected(int))); m_mapFileComboBox->setToolTip("Selects file for this overlay"); m_mapFileComboBox->setSizeAdjustPolicy(comboSizePolicy); + m_mapFileComboBox->setObjectName(objectNamePrefix + + "FileSelection"); + macroManager->addMacroSupportToObject(m_mapFileComboBox); /* * Yoking Group @@ -234,6 +268,9 @@ m_chartOverlay(NULL) } QObject::connect(m_allMapsCheckBox, SIGNAL(clicked(bool)), this, SLOT(allMapsCheckBoxClicked(bool))); + m_allMapsCheckBox->setObjectName(objectNamePrefix + + "AllMaps"); + macroManager->addMacroSupportToObject(m_allMapsCheckBox); /* * Map/Row/Column Index Spin Box @@ -246,6 +283,10 @@ m_chartOverlay(NULL) m_mapRowOrColumnIndexSpinBox->setFixedSize(m_mapRowOrColumnIndexSpinBox->sizeHint()); m_mapRowOrColumnIndexSpinBox->setRange(1, 1); m_mapRowOrColumnIndexSpinBox->setValue(1); + m_mapRowOrColumnIndexSpinBox->setObjectName(objectNamePrefix + + "MapIndex"); + macroManager->addMacroSupportToObject(m_mapRowOrColumnIndexSpinBox); + /* * Map/Row/Column Name Combo Box @@ -257,6 +298,9 @@ m_chartOverlay(NULL) this, SLOT(mapRowOrColumnNameComboBoxSelected(int))); m_mapRowOrColumnNameComboBox->setToolTip("Select map/row/column by its name"); m_mapRowOrColumnNameComboBox->setSizeAdjustPolicy(comboSizePolicy); + m_mapRowOrColumnNameComboBox->setObjectName(objectNamePrefix + + "MapSelection"); + macroManager->addMacroSupportToObject(m_mapRowOrColumnNameComboBox); } /** @@ -932,9 +976,12 @@ ChartTwoOverlayViewController::updateGraphicsWindow() * Create the matrix triangular view mode menu. * @param parent * Parent widget. + * @param parentObjectName + * Name of parent object for macros */ QMenu* -ChartTwoOverlayViewController::createMatrixTriangularViewModeMenu(QWidget* parent) +ChartTwoOverlayViewController::createMatrixTriangularViewModeMenu(QWidget* parent, + const QString& parentObjectName) { std::vector allViewModes; ChartTwoMatrixTriangularViewingModeEnum::getAllEnums(allViewModes); @@ -954,6 +1001,12 @@ ChartTwoOverlayViewController::createMatrixTriangularViewModeMenu(QWidget* paren action->setIcon(pixmap); actionGroup->addAction(action); + QString objName = (parentObjectName + + ChartTwoMatrixTriangularViewingModeEnum::toGuiName(viewMode)); + objName = objName.replace(" ", ""); + action->setObjectName(objName); + WuQMacroManager::instance()->addMacroSupportToObject(action); + m_matrixViewMenuData.push_back(std::make_tuple(viewMode, action, pixmap)); } @@ -994,9 +1047,12 @@ ChartTwoOverlayViewController::menuMatrixTriangularViewModeTriggered(QAction* ac * Create the axis location menu. * @param parent * Parent widget. + * @param parentObjectName + * Name of parent object for macros */ QMenu* -ChartTwoOverlayViewController::createAxisLocationMenu(QWidget* widget) +ChartTwoOverlayViewController::createAxisLocationMenu(QWidget* widget, + const QString& parentObjectName) { std::vector axisLocations; axisLocations.push_back(ChartAxisLocationEnum::CHART_AXIS_LOCATION_LEFT); @@ -1017,6 +1073,12 @@ ChartTwoOverlayViewController::createAxisLocationMenu(QWidget* widget) action->setIcon(pixmap); actionGroup->addAction(action); + QString objName = (parentObjectName + + ChartAxisLocationEnum::toGuiName(axis)); + objName = objName.replace(" ", ""); + action->setObjectName(objName); + WuQMacroManager::instance()->addMacroSupportToObject(action); + m_axisLocationMenuData.push_back(std::make_tuple(axis, action, pixmap)); } @@ -1048,53 +1110,91 @@ ChartTwoOverlayViewController::menuAxisLocationTriggered(QAction* action) * Create the construction menu. * @param parent * Parent widget. + * @param parentObjectName + * Name of parent object for macros */ QMenu* -ChartTwoOverlayViewController::createConstructionMenu(QWidget* parent) +ChartTwoOverlayViewController::createConstructionMenu(QWidget* parent, + const QString& menuActionNamePrefix) { + WuQMacroManager* macroManager = WuQMacroManager::instance(); + CaretAssert(macroManager); + QMenu* menu = new QMenu(parent); QObject::connect(menu, SIGNAL(aboutToShow()), this, SLOT(menuConstructionAboutToShow())); - menu->addAction("Add Overlay Above", + QAction* addAboveAction = menu->addAction("Add Overlay Above", this, SLOT(menuAddOverlayAboveTriggered())); + addAboveAction->setObjectName(menuActionNamePrefix + + "AddOverlayAbove"); + addAboveAction->setToolTip("Add an overlay above this overlay"); + macroManager->addMacroSupportToObject(addAboveAction); - menu->addAction("Add Overlay Below", + QAction* addBelowAction = menu->addAction("Add Overlay Below", this, SLOT(menuAddOverlayBelowTriggered())); + addBelowAction->setObjectName(menuActionNamePrefix + + "AddOverlayBelow"); + addBelowAction->setToolTip("Add an overlay below this overlay"); + macroManager->addMacroSupportToObject(addBelowAction); menu->addSeparator(); - menu->addAction("Move Overlay Up", + QAction* moveUpAction = menu->addAction("Move Overlay Up", this, SLOT(menuMoveOverlayUpTriggered())); + moveUpAction->setObjectName(menuActionNamePrefix + + "MoveOverlayUp"); + moveUpAction->setToolTip("Move this overlay up"); + macroManager->addMacroSupportToObject(moveUpAction); - menu->addAction("Move Overlay Down", + QAction* moveDownAction = menu->addAction("Move Overlay Down", this, SLOT(menuMoveOverlayDownTriggered())); + moveDownAction->setObjectName(menuActionNamePrefix + + "MoveOverlayDown"); + moveDownAction->setToolTip("Move this overlay down"); + macroManager->addMacroSupportToObject(moveDownAction); menu->addSeparator(); - menu->addAction("Remove This Overlay", + QAction* removeAction = menu->addAction("Remove This Overlay", this, SLOT(menuRemoveOverlayTriggered())); + removeAction->setObjectName(menuActionNamePrefix + + "RemoveOverlay"); + removeAction->setToolTip("Remove this overlay"); + macroManager->addMacroSupportToObject(removeAction); menu->addSeparator(); m_constructionReloadFileAction = menu->addAction("Reload Selected File", this, SLOT(menuReloadFileTriggered())); + m_constructionReloadFileAction->setObjectName(menuActionNamePrefix + + "ReloadSelectedFile"); + m_constructionReloadFileAction->setToolTip("Reload file in this overlay"); + macroManager->addMacroSupportToObject(m_constructionReloadFileAction); menu->addSeparator(); - menu->addAction("Copy Path and File Name to Clipboard", + QAction* copyPathFileNameAction = menu->addAction("Copy Path and File Name to Clipboard", this, SLOT(menuCopyFileNameToClipBoard())); + copyPathFileNameAction->setObjectName(menuActionNamePrefix + + "CopyPathAndFileNameToClipboard"); + copyPathFileNameAction->setToolTip("Copy path and file name of file in this overlay to clipboard"); + macroManager->addMacroSupportToObject(copyPathFileNameAction); - menu->addAction("Copy Map Name to Clipboard", + QAction* copyMapNameAction = menu->addAction("Copy Map Name to Clipboard", this, SLOT(menuCopyMapNameToClipBoard())); + copyMapNameAction->setObjectName(menuActionNamePrefix + + "CopyMapNameToClipboard"); + copyMapNameAction->setToolTip("Copy name of selected map to the clipboard"); + macroManager->addMacroSupportToObject(copyMapNameAction); return menu; diff --git a/src/GuiQt/ChartTwoOverlayViewController.h b/src/GuiQt/ChartTwoOverlayViewController.h index 0be90da06..2f20ab52a 100644 --- a/src/GuiQt/ChartTwoOverlayViewController.h +++ b/src/GuiQt/ChartTwoOverlayViewController.h @@ -47,9 +47,10 @@ namespace caret { public: ChartTwoOverlayViewController(const Qt::Orientation orientation, - const int32_t browserWindowIndex, - const int32_t chartOverlayIndex, - QObject* parent); + const int32_t browserWindowIndex, + const int32_t chartOverlayIndex, + const QString& parentObjectName, + QObject* parent); virtual ~ChartTwoOverlayViewController(); @@ -119,11 +120,14 @@ namespace caret { void updateGraphicsWindow(); - QMenu* createConstructionMenu(QWidget* parent); + QMenu* createConstructionMenu(QWidget* parent, + const QString& parentObjectName); - QMenu* createMatrixTriangularViewModeMenu(QWidget* widget); + QMenu* createMatrixTriangularViewModeMenu(QWidget* widget, + const QString& parentObjectName); - QMenu* createAxisLocationMenu(QWidget* widget); + QMenu* createAxisLocationMenu(QWidget* widget, + const QString& parentObjectName); void validateYokingSelection(); diff --git a/src/GuiQt/CiftiConnectivityMatrixViewController.cxx b/src/GuiQt/CiftiConnectivityMatrixViewController.cxx index 1780d2ad4..377af42be 100644 --- a/src/GuiQt/CiftiConnectivityMatrixViewController.cxx +++ b/src/GuiQt/CiftiConnectivityMatrixViewController.cxx @@ -49,6 +49,7 @@ #include "FiberTrajectoryMapProperties.h" #include "FilePathNamePrefixCompactor.h" #include "GuiManager.h" +#include "WuQMacroManager.h" #include "WuQMessageBox.h" #include "WuQtUtilities.h" @@ -63,10 +64,17 @@ static const char* FILE_POINTER_PROPERTY_NAME = "filePointer"; */ /** * Constructor. + * + * @param parentObjectName + * Name of parent object for macros + * @param parent + * The parent widget */ -CiftiConnectivityMatrixViewController::CiftiConnectivityMatrixViewController(const Qt::Orientation /*orientation*/, +CiftiConnectivityMatrixViewController::CiftiConnectivityMatrixViewController(const QString& parentObjectName, QWidget* parent) -: QWidget(parent) +: QWidget(parent), +m_objectNamePrefix(parentObjectName + + ":Connectivity") { m_gridLayout = new QGridLayout(); WuQtUtilities::setLayoutSpacingAndMargins(m_gridLayout, 2, 2); @@ -147,16 +155,11 @@ CiftiConnectivityMatrixViewController::updateViewController() matrixIter++) { files.push_back(*matrixIter); } - + + WuQMacroManager* macroManager = WuQMacroManager::instance(); const int32_t numFiles = static_cast(files.size()); -// std::vector displayNames; -// FilePathNamePrefixCompactor::removeMatchingPathPrefixFromCaretDataFiles(files, -// displayNames); -// -// CaretAssert(files.size() == displayNames.size()); - for (int32_t i = 0; i < numFiles; i++) { QCheckBox* checkBox = NULL; QCheckBox* layerCheckBox = NULL; @@ -172,10 +175,17 @@ CiftiConnectivityMatrixViewController::updateViewController() comboBox = m_fiberOrientationFileComboBoxes[i]; } else { + + const QString objectNamePrefix(m_objectNamePrefix + + QString("%1").arg((int)i+1, 2, 10, QLatin1Char('0')) + + ":"); checkBox = new QCheckBox(""); checkBox->setToolTip("When selected, load data during\n" "an identification operation"); m_fileEnableCheckBoxes.push_back(checkBox); + checkBox->setObjectName(objectNamePrefix + + "Enable"); + macroManager->addMacroSupportToObject(checkBox); const AString dynToolTip("This option is enabled only for .dynconn.nii (dynamic connectivity) files. " "When checked, this dynamic connectivity file will appear in the Overlay Layers' File selection combo box. " @@ -187,6 +197,9 @@ CiftiConnectivityMatrixViewController::updateViewController() layerCheckBox = new QCheckBox(""); WuQtUtilities::setWordWrappedToolTip(layerCheckBox, dynToolTip); m_layerCheckBoxes.push_back(layerCheckBox); + layerCheckBox->setObjectName(objectNamePrefix + + "EnableLayer"); + macroManager->addMacroSupportToObject(layerCheckBox); lineEdit = new QLineEdit(); lineEdit->setReadOnly(true); @@ -196,9 +209,16 @@ CiftiConnectivityMatrixViewController::updateViewController() copyToolButton->setText("Copy"); copyToolButton->setToolTip("Copy loaded row data to a new CIFTI Scalar File"); m_fileCopyToolButtons.push_back(copyToolButton); + copyToolButton->setObjectName(objectNamePrefix + + "CopyButton"); + macroManager->addMacroSupportToObject(copyToolButton); comboBox = new QComboBox(); m_fiberOrientationFileComboBoxes.push_back(comboBox); + comboBox->setToolTip("Select Fiber Orientation File"); + comboBox->setObjectName(objectNamePrefix + + "FiberOrientationFile"); + macroManager->addMacroSupportToObject(comboBox); QObject::connect(copyToolButton, SIGNAL(clicked()), m_signalMapperFileCopyToolButton, SLOT(map())); diff --git a/src/GuiQt/CiftiConnectivityMatrixViewController.h b/src/GuiQt/CiftiConnectivityMatrixViewController.h index 89f53378f..59d7e1152 100644 --- a/src/GuiQt/CiftiConnectivityMatrixViewController.h +++ b/src/GuiQt/CiftiConnectivityMatrixViewController.h @@ -44,7 +44,7 @@ namespace caret { Q_OBJECT public: - CiftiConnectivityMatrixViewController(const Qt::Orientation orientation, + CiftiConnectivityMatrixViewController(const QString& parentObjectName, QWidget* parent); virtual ~CiftiConnectivityMatrixViewController(); @@ -77,6 +77,8 @@ namespace caret { CiftiMappableConnectivityMatrixDataFile* &ciftiMatrixFileOut, CiftiFiberTrajectoryFile* &ciftiTrajFileOut); + const QString m_objectNamePrefix; + std::vector m_fileEnableCheckBoxes; std::vector m_layerCheckBoxes; diff --git a/src/GuiQt/DisplayGroupEnumComboBox.cxx b/src/GuiQt/DisplayGroupEnumComboBox.cxx index 4d12ab471..3a3ca2f28 100644 --- a/src/GuiQt/DisplayGroupEnumComboBox.cxx +++ b/src/GuiQt/DisplayGroupEnumComboBox.cxx @@ -25,6 +25,8 @@ #include "DisplayGroupEnumComboBox.h" #undef __DISPLAY_GROUP_ENUM_COMBO_BOX_DECLARE__ +#include "WuQMacroManager.h" + using namespace caret; @@ -46,6 +48,36 @@ using namespace caret; * Parent object. */ DisplayGroupEnumComboBox::DisplayGroupEnumComboBox(QObject* parent) +: DisplayGroupEnumComboBox(parent, + "") +{ + +} +//: WuQWidget(parent) +//{ +// std::vector allDisplayGroups; +// DisplayGroupEnum::getAllEnums(allDisplayGroups); +// const int32_t numStructures = static_cast(allDisplayGroups.size()); +// +// this->displayGroupComboBox = new QComboBox(); +// for (int32_t i = 0; i < numStructures; i++) { +// this->displayGroupComboBox->addItem(DisplayGroupEnum::toGuiName(allDisplayGroups[i])); +// this->displayGroupComboBox->setItemData(i, DisplayGroupEnum::toIntegerCode(allDisplayGroups[i])); +// } +// +// QObject::connect(this->displayGroupComboBox, SIGNAL(activated(int)), +// this, SLOT(displayGroupComboBoxSelection(int))); +//} + +/** + * Constructor. + * @param Parent + * Parent object. + * @param objectNameForMacros + * Name of object for macros + */ +DisplayGroupEnumComboBox::DisplayGroupEnumComboBox(QObject* parent, + const QString& objectNameForMacros) : WuQWidget(parent) { std::vector allDisplayGroups; @@ -60,6 +92,12 @@ DisplayGroupEnumComboBox::DisplayGroupEnumComboBox(QObject* parent) QObject::connect(this->displayGroupComboBox, SIGNAL(activated(int)), this, SLOT(displayGroupComboBoxSelection(int))); + + if ( ! objectNameForMacros.isEmpty()) { + this->displayGroupComboBox->setToolTip("Select Display Group"); + this->displayGroupComboBox->setObjectName(objectNameForMacros); + WuQMacroManager::instance()->addMacroSupportToObject(this->displayGroupComboBox); + } } /** diff --git a/src/GuiQt/DisplayGroupEnumComboBox.h b/src/GuiQt/DisplayGroupEnumComboBox.h index d779b9a51..fdc19d056 100644 --- a/src/GuiQt/DisplayGroupEnumComboBox.h +++ b/src/GuiQt/DisplayGroupEnumComboBox.h @@ -34,6 +34,9 @@ namespace caret { public: DisplayGroupEnumComboBox(QObject* parent); + DisplayGroupEnumComboBox(QObject* parent, + const QString& objectNameForMacros); + virtual ~DisplayGroupEnumComboBox(); DisplayGroupEnum::Enum getSelectedDisplayGroup() const; diff --git a/src/GuiQt/FociProjectionDialog.cxx b/src/GuiQt/FociProjectionDialog.cxx index dde8958a3..16d09d6a0 100644 --- a/src/GuiQt/FociProjectionDialog.cxx +++ b/src/GuiQt/FociProjectionDialog.cxx @@ -61,6 +61,8 @@ FociProjectionDialog::FociProjectionDialog(QWidget* parent) : WuQDialogModal("Project Foci", parent) { + m_objectNamePrefix = "FociProjectDialog"; + QWidget* surfaceWidget = NULL; //createSurfaceSelectionWidget(); QWidget* fociFileWidget = createFociFileSelectionWidget(); @@ -205,7 +207,8 @@ FociProjectionDialog::createSurfaceSelectionWidget() if (leftBrainStructure != NULL) { m_leftSurfaceCheckBox = new QCheckBox("Left: "); m_leftSurfaceCheckBox->setChecked(true); - m_leftSurfaceViewController = new SurfaceSelectionViewController(this, leftBrainStructure); + m_leftSurfaceViewController = new SurfaceSelectionViewController(this, leftBrainStructure, + (m_objectNamePrefix + ":LeftSurface")); m_leftSurfaceViewController->updateControl(); } @@ -214,7 +217,8 @@ FociProjectionDialog::createSurfaceSelectionWidget() if (rightBrainStructure != NULL) { m_rightSurfaceCheckBox = new QCheckBox("Right: "); m_rightSurfaceCheckBox->setChecked(true); - m_rightSurfaceViewController = new SurfaceSelectionViewController(this, rightBrainStructure); + m_rightSurfaceViewController = new SurfaceSelectionViewController(this, rightBrainStructure, + (m_objectNamePrefix + ":RightSurface")); m_rightSurfaceViewController->updateControl(); } @@ -224,7 +228,8 @@ FociProjectionDialog::createSurfaceSelectionWidget() m_cerebellumSurfaceCheckBox = new QCheckBox("Cerebellum: "); m_cerebellumSurfaceCheckBox->setChecked(true); m_cerebellumSurfaceViewController = new SurfaceSelectionViewController(this, - cerebellumBrainStructure); + cerebellumBrainStructure, + (m_objectNamePrefix + ":CerebellumSurface")); m_cerebellumSurfaceViewController->updateControl(); } diff --git a/src/GuiQt/FociProjectionDialog.h b/src/GuiQt/FociProjectionDialog.h index 1b9564933..e4f36308d 100644 --- a/src/GuiQt/FociProjectionDialog.h +++ b/src/GuiQt/FociProjectionDialog.h @@ -57,6 +57,8 @@ namespace caret { QWidget* createOptionsWidget(); + QString m_objectNamePrefix; + QCheckBox* m_leftSurfaceCheckBox; SurfaceSelectionViewController* m_leftSurfaceViewController; diff --git a/src/GuiQt/FociSelectionViewController.cxx b/src/GuiQt/FociSelectionViewController.cxx index 2667e418e..8f55263bc 100644 --- a/src/GuiQt/FociSelectionViewController.cxx +++ b/src/GuiQt/FociSelectionViewController.cxx @@ -50,6 +50,7 @@ #include "SceneClass.h" #include "WuQDataEntryDialog.h" #include "WuQFactory.h" +#include "WuQMacroManager.h" #include "WuQTabWidget.h" #include "WuQTrueFalseComboBox.h" #include "WuQtUtilities.h" @@ -69,10 +70,20 @@ using namespace caret; /** * Constructor. + * + * @param browserWindowIndex + * Index of browser window + * @param parentObjectName + * Name of parent object + * @param parent + * The parent object */ FociSelectionViewController::FociSelectionViewController(const int32_t browserWindowIndex, - QWidget* parent) -: QWidget(parent) + const QString& parentObjectName, + QWidget* parent) +: QWidget(parent), +m_objectNamePrefix(parentObjectName + + ":Foci") { m_browserWindowIndex = browserWindowIndex; @@ -90,6 +101,9 @@ FociSelectionViewController::FociSelectionViewController(const int32_t browserWi m_fociDisplayCheckBox->setToolTip("Enable the display of foci"); QObject::connect(m_fociDisplayCheckBox, SIGNAL(clicked(bool)), this, SLOT(processAttributesChanges())); + m_fociDisplayCheckBox->setObjectName(m_objectNamePrefix + + ":DisplayFoci"); + WuQMacroManager::instance()->addMacroSupportToObject(m_fociDisplayCheckBox); QWidget* attributesWidget = this->createAttributesWidget(); QWidget* selectionWidget = this->createSelectionWidget(); @@ -129,7 +143,10 @@ FociSelectionViewController::~FociSelectionViewController() QWidget* FociSelectionViewController::createSelectionWidget() { - m_fociClassNameHierarchyViewController = new GroupAndNameHierarchyViewController(m_browserWindowIndex); + m_fociClassNameHierarchyViewController = new GroupAndNameHierarchyViewController(m_browserWindowIndex, + (m_objectNamePrefix + + ":Selection"), + this); return m_fociClassNameHierarchyViewController; } @@ -140,15 +157,23 @@ FociSelectionViewController::createSelectionWidget() QWidget* FociSelectionViewController::createAttributesWidget() { + WuQMacroManager* macroManager = WuQMacroManager::instance(); + m_fociContralateralCheckBox = new QCheckBox("Contralateral"); m_fociContralateralCheckBox->setToolTip("Enable display of foci from contralateral brain structure"); QObject::connect(m_fociContralateralCheckBox, SIGNAL(clicked(bool)), this, SLOT(processAttributesChanges())); + m_fociContralateralCheckBox->setObjectName(m_objectNamePrefix + + ":Contralateral"); + macroManager->addMacroSupportToObject(m_fociContralateralCheckBox); m_pasteOntoSurfaceCheckBox = new QCheckBox("Paste Onto Surface"); m_pasteOntoSurfaceCheckBox->setToolTip("Place the foci onto the surface"); QObject::connect(m_pasteOntoSurfaceCheckBox, SIGNAL(clicked(bool)), this, SLOT(processAttributesChanges())); + m_pasteOntoSurfaceCheckBox->setObjectName(m_objectNamePrefix + + ":PasteOntoSurface"); + macroManager->addMacroSupportToObject(m_pasteOntoSurfaceCheckBox); QLabel* coloringLabel = new QLabel("Coloring"); m_coloringTypeComboBox = new EnumComboBoxTemplate(this); @@ -157,9 +182,16 @@ FociSelectionViewController::createAttributesWidget() m_coloringTypeComboBox->getWidget()->setToolTip("Select the coloring assignment for foci"); QObject::connect(m_coloringTypeComboBox, SIGNAL(itemActivated()), this, SLOT(processAttributesChanges())); + m_coloringTypeComboBox->getComboBox()->setObjectName(m_objectNamePrefix + + ":ColorType"); + macroManager->addMacroSupportToObject(m_coloringTypeComboBox->getComboBox()); QLabel* standardColorLabel = new QLabel("Standard Color"); - m_standardColorComboBox = new CaretColorEnumComboBox(this); + m_standardColorComboBox = new CaretColorEnumComboBox("", + QIcon(), + (m_objectNamePrefix + + ":Color"), + this); m_standardColorComboBox->getWidget()->setToolTip("Select the standard color"); QObject::connect(m_standardColorComboBox, SIGNAL(colorSelected(const CaretColorEnum::Enum)), this, SLOT(processAttributesChanges())); @@ -178,6 +210,9 @@ FociSelectionViewController::createAttributesWidget() m_drawTypeComboBox->setToolTip("Select the drawing style of foci"); QObject::connect(m_drawTypeComboBox, SIGNAL(activated(int)), this, SLOT(processAttributesChanges())); + m_drawTypeComboBox->setObjectName(m_objectNamePrefix + + ":DrawingStyle"); + macroManager->addMacroSupportToObject(m_drawTypeComboBox); float minLineWidth = 0; float maxLineWidth = 1000; @@ -195,6 +230,9 @@ FociSelectionViewController::createAttributesWidget() m_sizeSpinBox->setSuffix("mm"); QObject::connect(m_sizeSpinBox, SIGNAL(valueChanged(double)), this, SLOT(processAttributesChanges())); + m_sizeSpinBox->setObjectName(m_objectNamePrefix + + ":Diameter"); + macroManager->addMacroSupportToObject(m_sizeSpinBox); QWidget* gridWidget = new QWidget(); diff --git a/src/GuiQt/FociSelectionViewController.h b/src/GuiQt/FociSelectionViewController.h index d6700292b..974be5177 100644 --- a/src/GuiQt/FociSelectionViewController.h +++ b/src/GuiQt/FociSelectionViewController.h @@ -48,7 +48,8 @@ namespace caret { public: FociSelectionViewController(const int32_t browserWindowIndex, - QWidget* parent = 0); + const QString& parentObjectName, + QWidget* parent = 0); virtual ~FociSelectionViewController(); @@ -82,6 +83,8 @@ namespace caret { QWidget* createAttributesWidget(); + const QString m_objectNamePrefix; + int32_t m_browserWindowIndex; GroupAndNameHierarchyViewController* m_fociClassNameHierarchyViewController; diff --git a/src/GuiQt/GroupAndNameHierarchyViewController.cxx b/src/GuiQt/GroupAndNameHierarchyViewController.cxx index 0239246aa..f1e6cb576 100644 --- a/src/GuiQt/GroupAndNameHierarchyViewController.cxx +++ b/src/GuiQt/GroupAndNameHierarchyViewController.cxx @@ -49,6 +49,7 @@ #include "GiftiLabelTable.h" #include "LabelFile.h" #include "VolumeFile.h" +#include "WuQMacroManager.h" #include "WuQTreeWidget.h" #include "WuQtUtilities.h" @@ -65,10 +66,16 @@ using namespace caret; /** * Constructor. + * + * @param browserWindowIndex + * Index of browser window + * @param objectNameForMacros + * Name of this object for macros * @param parent * Parent widget. */ GroupAndNameHierarchyViewController::GroupAndNameHierarchyViewController(const int32_t browserWindowIndex, + const QString& objectNameForMacros, QWidget* parent) : QWidget(parent) { @@ -78,7 +85,7 @@ GroupAndNameHierarchyViewController::GroupAndNameHierarchyViewController(const i m_previousBrowserTabIndex = -1; m_browserWindowIndex = browserWindowIndex; - QWidget* allOnOffWidget = createAllOnOffControls(); + QWidget* allOnOffWidget = createAllOnOffControls(objectNameForMacros); m_modelTreeWidgetLayout = new QVBoxLayout(); WuQtUtilities::setLayoutSpacingAndMargins(m_modelTreeWidgetLayout, 0, 0); @@ -176,19 +183,32 @@ GroupAndNameHierarchyViewController::updateGraphics() /** * Create buttons for all on and off + * + * @param objectNameForMacros + * Name of this object for macros */ QWidget* -GroupAndNameHierarchyViewController::createAllOnOffControls() +GroupAndNameHierarchyViewController::createAllOnOffControls(const QString& objectNameForMacros) { + WuQMacroManager* macroManager = WuQMacroManager::instance(); + QLabel* allLabel = new QLabel("All: "); QPushButton* onPushButton = new QPushButton("On"); + onPushButton->setToolTip("Turn all on"); + onPushButton->setObjectName(objectNameForMacros + + ":AllOn"); QObject::connect(onPushButton, SIGNAL(clicked()), this, SLOT(allOnPushButtonClicked())); + macroManager->addMacroSupportToObject(onPushButton); QPushButton* offPushButton = new QPushButton("Off"); + offPushButton->setToolTip("Turn all of"); + offPushButton->setObjectName(objectNameForMacros + + ":AllOff"); QObject::connect(offPushButton, SIGNAL(clicked()), this, SLOT(allOffPushButtonClicked())); + macroManager->addMacroSupportToObject(offPushButton); QWidget* w = new QWidget(); QHBoxLayout* layout = new QHBoxLayout(w); diff --git a/src/GuiQt/GroupAndNameHierarchyViewController.h b/src/GuiQt/GroupAndNameHierarchyViewController.h index 9e0b7466f..bbc4290bd 100644 --- a/src/GuiQt/GroupAndNameHierarchyViewController.h +++ b/src/GuiQt/GroupAndNameHierarchyViewController.h @@ -49,7 +49,8 @@ namespace caret { public: GroupAndNameHierarchyViewController(const int32_t browserWindowIndex, - QWidget* parent = 0); + const QString& objectNameForMacros, + QWidget* parent); virtual ~GroupAndNameHierarchyViewController(); @@ -95,7 +96,7 @@ namespace caret { void createTreeWidget(); - QWidget* createAllOnOffControls(); + QWidget* createAllOnOffControls(const QString& objectNameForMacros); void setAllSelected(bool selected); diff --git a/src/GuiQt/GuiManager.cxx b/src/GuiQt/GuiManager.cxx index 113e2c76b..1fbff4486 100644 --- a/src/GuiQt/GuiManager.cxx +++ b/src/GuiQt/GuiManager.cxx @@ -122,6 +122,7 @@ #include "TileTabsConfigurationDialog.h" #include "VolumeMappableInterface.h" #include "WuQMacroManager.h" +#include "WuQMacroObjectTypeEnum.h" #include "WuQMessageBox.h" #include "WuQtUtilities.h" @@ -186,6 +187,17 @@ GuiManager::initializeGuiManager() this->cursorManager = new CursorManager(); + /* + * When running macro commands, some object may be child + * of GuiManager and not found when searching a window + * for children objects + */ + WuQMacroObjectTypeEnum::addWidgetClassNameAlias("QTabWidget", + "caret::WuQTabWidgetWithSizeHint"); + WuQMacroObjectTypeEnum::addWidgetClassNameAlias("QTabBar", + "caret::WuQTabBar"); + WuQMacroManager::instance()->addParentObject(this); + /* * Windows vector never changes size */ @@ -221,6 +233,8 @@ GuiManager::initializeGuiManager() this->showHideInfoWindowSelected(m_informationDisplayDialogEnabledAction->isChecked()); m_informationDisplayDialogEnabledAction->setIconText("Info"); m_informationDisplayDialogEnabledAction->blockSignals(false); + m_informationDisplayDialogEnabledAction->setObjectName("ToolBar:ShowInformationWindow"); + WuQMacroManager::instance()->addMacroSupportToObject(m_informationDisplayDialogEnabledAction); /* * Identify brainordinate window @@ -247,6 +261,8 @@ GuiManager::initializeGuiManager() m_identifyBrainordinateDialogEnabledAction->setCheckable(true); m_identifyBrainordinateDialogEnabledAction->setChecked(false); m_identifyBrainordinateDialogEnabledAction->blockSignals(false); + m_identifyBrainordinateDialogEnabledAction->setObjectName("ToolBar:ShowIdentifyBrainordinateWindow"); + WuQMacroManager::instance()->addMacroSupportToObject(m_identifyBrainordinateDialogEnabledAction); /* * Scene dialog action @@ -270,6 +286,8 @@ GuiManager::initializeGuiManager() m_sceneDialogDisplayAction->setCheckable(true); m_sceneDialogDisplayAction->setChecked(false); m_sceneDialogDisplayAction->blockSignals(false); + m_sceneDialogDisplayAction->setObjectName("ToolBar:ShowScenesWindow"); + WuQMacroManager::instance()->addMacroSupportToObject(m_sceneDialogDisplayAction); /* * Help dialog action @@ -294,6 +312,8 @@ GuiManager::initializeGuiManager() m_helpViewerDialogDisplayAction->setCheckable(true); m_helpViewerDialogDisplayAction->setChecked(false); m_helpViewerDialogDisplayAction->blockSignals(false); + m_helpViewerDialogDisplayAction->setObjectName("ToolBar:ShowHelpWindow"); + WuQMacroManager::instance()->addMacroSupportToObject(m_helpViewerDialogDisplayAction); /* * Data tooltip action is created when requested by a toolbar @@ -1911,6 +1931,9 @@ GuiManager::getDataToolTipsAction(QWidget* buttonWidget) m_dataToolTipsEnabledAction->setIconVisibleInMenu(false); m_dataToolTipsEnabledAction->setCheckable(true); m_dataToolTipsEnabledAction->setChecked(SessionManager::get()->getDataToolTipsManager()->isEnabled()); + + m_dataToolTipsEnabledAction->setObjectName("ToolBar:DataToolTipsEnabled"); + WuQMacroManager::instance()->addMacroSupportToObject(m_dataToolTipsEnabledAction); } return m_dataToolTipsEnabledAction; diff --git a/src/GuiQt/ImageSelectionViewController.cxx b/src/GuiQt/ImageSelectionViewController.cxx index 4d7a20d26..881eadd56 100644 --- a/src/GuiQt/ImageSelectionViewController.cxx +++ b/src/GuiQt/ImageSelectionViewController.cxx @@ -46,6 +46,7 @@ #include "ImageFile.h" #include "SceneClass.h" #include "SceneClassAssistant.h" +#include "WuQMacroManager.h" #include "WuQSpinBoxGroup.h" #include "WuQtUtilities.h" #include "WuQTabWidget.h" @@ -64,16 +65,23 @@ static const int COLUMN_RADIO_BUTTON = 0; * Constructor. */ ImageSelectionViewController::ImageSelectionViewController(const int32_t browserWindowIndex, + const QString& parentObjectName, QWidget* parent) : QWidget(parent), -m_browserWindowIndex(browserWindowIndex) +m_browserWindowIndex(browserWindowIndex), +m_objectNamePrefix(parentObjectName + + ":Image") { setWindowTitle("Images"); + WuQMacroManager* macroManager = WuQMacroManager::instance(); + m_sceneAssistant = new SceneClassAssistant(); QLabel* groupLabel = new QLabel("Group"); - m_imagesDisplayGroupComboBox = new DisplayGroupEnumComboBox(this); + m_imagesDisplayGroupComboBox = new DisplayGroupEnumComboBox(this, + (m_objectNamePrefix + + ":DisplayGroup")); QObject::connect(m_imagesDisplayGroupComboBox, SIGNAL(displayGroupSelected(const DisplayGroupEnum::Enum)), this, SLOT(imageDisplayGroupSelected(const DisplayGroupEnum::Enum))); @@ -85,10 +93,18 @@ m_browserWindowIndex(browserWindowIndex) m_imageDisplayCheckBox = new QCheckBox("Display Image"); QObject::connect(m_imageDisplayCheckBox, SIGNAL(clicked(bool)), this, SLOT(processAttributesChanges())); + m_imageDisplayCheckBox->setToolTip("Display Image"); + m_imageDisplayCheckBox->setObjectName(m_objectNamePrefix + + ":Display"); + macroManager->addMacroSupportToObject(m_imageDisplayCheckBox); m_controlPointsDisplayCheckBox = new QCheckBox("Display Control Points"); QObject::connect(m_controlPointsDisplayCheckBox, SIGNAL(clicked(bool)), this, SLOT(processAttributesChanges())); + m_controlPointsDisplayCheckBox->setToolTip("Display Control Points"); + m_controlPointsDisplayCheckBox->setObjectName(m_objectNamePrefix + + ":DisplayControlPoints"); + macroManager->addMacroSupportToObject(m_controlPointsDisplayCheckBox); QWidget* attributesWidget = this->createAttributesWidget(); QWidget* selectionWidget = this->createSelectionWidget(); @@ -100,6 +116,9 @@ m_browserWindowIndex(browserWindowIndex) m_tabWidget->addTab(selectionWidget, "Selection"); m_tabWidget->setCurrentWidget(attributesWidget); + m_tabWidget->getTabBar()->setObjectName(m_objectNamePrefix + + ":Tab"); + macroManager->addMacroSupportToObjectWithToolTip(m_tabWidget->getTabBar(), ""); QVBoxLayout* layout = new QVBoxLayout(this); // WuQtUtilities::setLayoutSpacingAndMargins(layout, 2, 2); @@ -189,12 +208,19 @@ ImageSelectionViewController::createSelectionWidget() QWidget* ImageSelectionViewController::createAttributesWidget() { + WuQMacroManager* macroManager = WuQMacroManager::instance(); + const QString objectNamePrefix(m_objectNamePrefix + + ":Attributes"); + QLabel* depthLabel = new QLabel("Depth"); m_depthComboBox = new EnumComboBoxTemplate(this); m_depthComboBox->setup(); m_depthComboBox->getWidget()->setToolTip("Set the depth position of the image3"); QObject::connect(m_depthComboBox, SIGNAL(itemActivated()), this, SLOT(processAttributesChanges())); + m_depthComboBox->getComboBox()->setObjectName(objectNamePrefix + + ":DepthPosition"); + macroManager->addMacroSupportToObject(m_depthComboBox->getComboBox()); const float threshMin = -1.0; const float threshMax = 100000.0; @@ -209,6 +235,9 @@ ImageSelectionViewController::createAttributesWidget() m_thresholdMinimumSpinBox->setToolTip("Do not display image pixels containing a color component less than this value"); QObject::connect(m_thresholdMinimumSpinBox, SIGNAL(valueChanged(double)), this, SLOT(processAttributesChanges())); + m_thresholdMinimumSpinBox->setObjectName(objectNamePrefix + + "MinimumThreshold"); + macroManager->addMacroSupportToObject(m_thresholdMinimumSpinBox); QLabel* thresholdMaximumLabel = new QLabel("Maximum Threshold"); m_thresholdMaximumSpinBox = WuQFactory::newDoubleSpinBox(); @@ -220,6 +249,9 @@ ImageSelectionViewController::createAttributesWidget() m_thresholdMaximumSpinBox->setToolTip("Do not display image pixels containing a color component greater than this value"); QObject::connect(m_thresholdMaximumSpinBox, SIGNAL(valueChanged(double)), this, SLOT(processAttributesChanges())); + m_thresholdMaximumSpinBox->setObjectName(objectNamePrefix + + "MaximumThreshold"); + macroManager->addMacroSupportToObject(m_thresholdMaximumSpinBox); const float minOpacity = 0.0; const float maxOpacity = 1.0; @@ -233,6 +265,9 @@ ImageSelectionViewController::createAttributesWidget() m_opacitySpinBox->setToolTip("Opacity for image"); QObject::connect(m_opacitySpinBox, SIGNAL(valueChanged(double)), this, SLOT(processAttributesChanges())); + m_opacitySpinBox->setObjectName(objectNamePrefix + + ":Opacity"); + macroManager->addMacroSupportToObject(m_opacitySpinBox); QWidget* gridWidget = new QWidget(); @@ -251,17 +286,6 @@ ImageSelectionViewController::createAttributesWidget() gridLayout->addWidget(opacityLabel, row, 0); gridLayout->addWidget(m_opacitySpinBox, row, 1); row++; -// gridLayout->addWidget(pointSizeLabel, row, 0); -// gridLayout->addWidget(m_pointSizeSpinBox, row, 1); -// row++; -// gridLayout->addWidget(m_enableUnstretchedLinesCheckBox, row, 0); -// gridLayout->addWidget(m_unstretchedLinesLengthSpinBox, row, 1); -// row++; -// gridLayout->addWidget(aboveSurfaceLabel, row, 0); -// gridLayout->addWidget(m_aboveSurfaceOffsetSpinBox, row, 1); -// -// gridWidget->setSizePolicy(QSizePolicy::Fixed, -// QSizePolicy::Fixed); QWidget* widget = new QWidget(); QVBoxLayout* layout = new QVBoxLayout(widget); @@ -417,6 +441,12 @@ ImageSelectionViewController::updateImageViewController() m_imageRadioButtonGroup->addButton(rb, buttonID); + + const QString buttonName(m_objectNamePrefix + + ":Selection:Image" + + QString("%1").arg((int)i+1, 2, 10, QLatin1Char('0'))); + rb->setObjectName(buttonName); + WuQMacroManager::instance()->addMacroSupportToObjectWithToolTip(rb, ""); } numRadioButtons = static_cast(m_imageRadioButtons.size()); diff --git a/src/GuiQt/ImageSelectionViewController.h b/src/GuiQt/ImageSelectionViewController.h index fcf9a9747..ac31e19da 100644 --- a/src/GuiQt/ImageSelectionViewController.h +++ b/src/GuiQt/ImageSelectionViewController.h @@ -49,6 +49,7 @@ namespace caret { public: ImageSelectionViewController(const int32_t browserWindowIndex, + const QString& parentObjectName, QWidget* parent = 0); virtual ~ImageSelectionViewController(); @@ -97,6 +98,8 @@ namespace caret { const int32_t m_browserWindowIndex; + const QString m_objectNamePrefix; + WuQTabWidget* m_tabWidget; SceneClassAssistant* m_sceneAssistant; diff --git a/src/GuiQt/LabelSelectionViewController.cxx b/src/GuiQt/LabelSelectionViewController.cxx index b294bba3b..0505c6765 100644 --- a/src/GuiQt/LabelSelectionViewController.cxx +++ b/src/GuiQt/LabelSelectionViewController.cxx @@ -49,6 +49,7 @@ #include "SceneClass.h" #include "VolumeFile.h" #include "WuQDataEntryDialog.h" +#include "WuQMacroManager.h" #include "WuQTabWidget.h" #include "WuQtUtilities.h" @@ -67,15 +68,27 @@ using namespace caret; /** * Constructor. + * + * @param browserWindowIndex + * Index of browser window + * @param parentObjectName + * Name of parent object + * @param parent + * The parent object */ LabelSelectionViewController::LabelSelectionViewController(const int32_t browserWindowIndex, - QWidget* parent) -: QWidget(parent) + const QString& parentObjectName, + QWidget* parent) +: QWidget(parent), +m_objectNamePrefix(parentObjectName + + ":Label") { m_browserWindowIndex = browserWindowIndex; QLabel* groupLabel = new QLabel("Group"); - m_labelsDisplayGroupComboBox = new DisplayGroupEnumComboBox(this); + m_labelsDisplayGroupComboBox = new DisplayGroupEnumComboBox(this, + (m_objectNamePrefix + + ":DisplayGroup")); QObject::connect(m_labelsDisplayGroupComboBox, SIGNAL(displayGroupSelected(const DisplayGroupEnum::Enum)), this, SLOT(labelDisplayGroupSelected(const DisplayGroupEnum::Enum))); @@ -110,7 +123,10 @@ LabelSelectionViewController::~LabelSelectionViewController() QWidget* LabelSelectionViewController::createSelectionWidget() { - m_labelClassNameHierarchyViewController = new GroupAndNameHierarchyViewController(m_browserWindowIndex); + m_labelClassNameHierarchyViewController = new GroupAndNameHierarchyViewController(m_browserWindowIndex, + (m_objectNamePrefix + + ":Selection"), + this); return m_labelClassNameHierarchyViewController; } diff --git a/src/GuiQt/LabelSelectionViewController.h b/src/GuiQt/LabelSelectionViewController.h index e15632ba9..59141d8b3 100644 --- a/src/GuiQt/LabelSelectionViewController.h +++ b/src/GuiQt/LabelSelectionViewController.h @@ -43,7 +43,8 @@ namespace caret { public: LabelSelectionViewController(const int32_t browserWindowIndex, - QWidget* parent = 0); + const QString& parentObjectName, + QWidget* parent = 0); virtual ~LabelSelectionViewController(); @@ -73,6 +74,8 @@ namespace caret { QWidget* createSelectionWidget(); + const QString m_objectNamePrefix; + int32_t m_browserWindowIndex; GroupAndNameHierarchyViewController* m_labelClassNameHierarchyViewController; diff --git a/src/GuiQt/OverlaySetViewController.cxx b/src/GuiQt/OverlaySetViewController.cxx index fb090d8ee..1ec1c0829 100644 --- a/src/GuiQt/OverlaySetViewController.cxx +++ b/src/GuiQt/OverlaySetViewController.cxx @@ -60,11 +60,14 @@ using namespace caret; * Orientation for layout * @param browserWindowIndex * Index of browser window that contains this view controller. + * @param parentObjectName + * Name of parent * @param parent * Parent widget. */ OverlaySetViewController::OverlaySetViewController(const Qt::Orientation orientation, const int32_t browserWindowIndex, + const QString& parentObjectName, QWidget* parent) : QWidget(parent) { @@ -114,6 +117,7 @@ OverlaySetViewController::OverlaySetViewController(const Qt::Orientation orienta gridLayout, browserWindowIndex, i, + parentObjectName, this); this->overlayViewControllers.push_back(ovc); diff --git a/src/GuiQt/OverlaySetViewController.h b/src/GuiQt/OverlaySetViewController.h index a7299b574..94c5da1e4 100644 --- a/src/GuiQt/OverlaySetViewController.h +++ b/src/GuiQt/OverlaySetViewController.h @@ -44,6 +44,7 @@ namespace caret { public: OverlaySetViewController(const Qt::Orientation orientation, const int32_t browserWindowIndex, + const QString& parentObjectName, QWidget* parent = 0); virtual ~OverlaySetViewController(); diff --git a/src/GuiQt/OverlayViewController.cxx b/src/GuiQt/OverlayViewController.cxx index 959bfa4af..6398c55c5 100644 --- a/src/GuiQt/OverlayViewController.cxx +++ b/src/GuiQt/OverlayViewController.cxx @@ -71,10 +71,16 @@ using namespace caret; /** * Constructor. * + * @param orientation + * Orientation of overlay (horizontal/vertical) + * @param gridLayout + * Layout for widegets * @param browserWindowIndex * Index of browser window in which this view controller resides. - * @param showTopHorizontalLine - * If true, display a horizontal line above the controls. + * @param overlayIndex + * Index of the overlay + * @param parentObjectName + * Name of parent object for macros * @param parent * The parent widget. */ @@ -82,6 +88,7 @@ OverlayViewController::OverlayViewController(const Qt::Orientation orientation, QGridLayout* gridLayout, const int32_t browserWindowIndex, const int32_t overlayIndex, + const QString& parentObjectName, QObject* parent) : QObject(parent), browserWindowIndex(browserWindowIndex), @@ -100,15 +107,9 @@ OverlayViewController::OverlayViewController(const Qt::Orientation orientation, WuQMacroManager* macroManager = WuQMacroManager::instance(); CaretAssert(macroManager); - QString objectNamePrefix("Window_" - + AString::number(browserWindowIndex + 1) - + ":Overlay_" - + AString::number(overlayIndex + 1) - + "_" - + ((orientation == Qt::Horizontal) - ? "H" - : "V") - + ":"); + QString objectNamePrefix = QString(parentObjectName + + ":Overlay%1" + + ":").arg((int)(overlayIndex + 1), 2, 10, QLatin1Char('0')); /* * Enabled Check Box */ @@ -126,7 +127,7 @@ OverlayViewController::OverlayViewController(const Qt::Orientation orientation, */ this->fileComboBox = WuQFactory::newComboBox(); this->fileComboBox->setObjectName(objectNamePrefix - + "File_Selection"); + + "FileSelection"); this->fileComboBox->setMinimumWidth(minComboBoxWidth); this->fileComboBox->setMaximumWidth(maxComboBoxWidth); QObject::connect(this->fileComboBox, SIGNAL(activated(int)), @@ -140,7 +141,7 @@ OverlayViewController::OverlayViewController(const Qt::Orientation orientation, */ m_mapIndexSpinBox = WuQFactory::newSpinBox(); this->m_mapIndexSpinBox->setObjectName(objectNamePrefix - + "Map_Index"); + + "MapIndex"); QObject::connect(m_mapIndexSpinBox, SIGNAL(valueChanged(int)), this, SLOT(mapIndexSpinBoxValueChanged(int))); m_mapIndexSpinBox->setToolTip("Select map by its index"); @@ -151,7 +152,7 @@ OverlayViewController::OverlayViewController(const Qt::Orientation orientation, */ this->mapNameComboBox = WuQFactory::newComboBox(); this->mapNameComboBox->setObjectName(objectNamePrefix - + "Map_Selection"); + + "MapSelection"); this->mapNameComboBox->setMinimumWidth(minComboBoxWidth); this->mapNameComboBox->setMaximumWidth(maxComboBoxWidth); QObject::connect(this->mapNameComboBox, SIGNAL(activated(int)), @@ -186,7 +187,7 @@ OverlayViewController::OverlayViewController(const Qt::Orientation orientation, SLOT(colorBarActionTriggered(bool))); m_colorBarToolButton->setCheckable(true); m_colorBarToolButton->setObjectName(objectNamePrefix - + "Show_Color_Bar"); + + "ShowColorBar"); macroManager->addMacroSupportToObject(m_colorBarToolButton); // /* @@ -264,7 +265,7 @@ OverlayViewController::OverlayViewController(const Qt::Orientation orientation, */ m_mapYokingGroupComboBox = new MapYokingGroupComboBox(this, (objectNamePrefix - + "Map_Yoking_Selection")); + + "MapYokingSelection")); m_mapYokingGroupComboBox->getWidget()->setStatusTip("Synchronize enabled status and map indices)"); m_mapYokingGroupComboBox->getWidget()->setToolTip("Yoke to Overlay Mapped Files"); #ifdef CARET_OS_MACOSX @@ -874,7 +875,7 @@ OverlayViewController::createConstructionMenu(QWidget* parent, this, SLOT(menuAddOverlayAboveTriggered())); addAboveAction->setObjectName(menuActionNamePrefix - + "Add_Overlay_Above"); + + "AddOverlayAbove"); addAboveAction->setToolTip("Add an overlay above this overlay"); macroManager->addMacroSupportToObject(addAboveAction); @@ -882,7 +883,7 @@ OverlayViewController::createConstructionMenu(QWidget* parent, this, SLOT(menuAddOverlayBelowTriggered())); addBelowAction->setObjectName(menuActionNamePrefix - + "Add_Overlay_Below"); + + "AddOverlayBelow"); addBelowAction->setToolTip("Add an overlay below this overlay"); macroManager->addMacroSupportToObject(addBelowAction); @@ -892,7 +893,7 @@ OverlayViewController::createConstructionMenu(QWidget* parent, this, SLOT(menuMoveOverlayUpTriggered())); moveUpAction->setObjectName(menuActionNamePrefix - + "Move_Overlay_Up"); + + "MoveOverlayUp"); moveUpAction->setToolTip("Move this overlay up"); macroManager->addMacroSupportToObject(moveUpAction); @@ -900,7 +901,7 @@ OverlayViewController::createConstructionMenu(QWidget* parent, this, SLOT(menuMoveOverlayDownTriggered())); moveDownAction->setObjectName(menuActionNamePrefix - + "Move_Overlay_Down"); + + "MoveOverlayDown"); moveDownAction->setToolTip("Move this overlay down"); macroManager->addMacroSupportToObject(moveDownAction); @@ -910,7 +911,7 @@ OverlayViewController::createConstructionMenu(QWidget* parent, this, SLOT(menuRemoveOverlayTriggered())); removeAction->setObjectName(menuActionNamePrefix - + "Remove_Overlay"); + + "RemoveOverlay"); removeAction->setToolTip("Remove this overlay"); macroManager->addMacroSupportToObject(removeAction); @@ -920,7 +921,7 @@ OverlayViewController::createConstructionMenu(QWidget* parent, this, SLOT(menuReloadFileTriggered())); m_constructionReloadFileAction->setObjectName(menuActionNamePrefix - + "Reload_Selected_File"); + + "ReloadSelectedFile"); m_constructionReloadFileAction->setToolTip("Reload file in this overlay"); macroManager->addMacroSupportToObject(m_constructionReloadFileAction); @@ -930,7 +931,7 @@ OverlayViewController::createConstructionMenu(QWidget* parent, this, SLOT(menuCopyFileNameToClipBoard())); m_copyPathAndFileNameToClipboardAction->setObjectName(menuActionNamePrefix - + "Copy_Path_And_File_Name_To_Clipboard:MenuAction"); + + "CopyPathAndFileNameToClipboard"); m_copyPathAndFileNameToClipboardAction->setToolTip("Copy path and file name of file in this overlay to clipboard"); macroManager->addMacroSupportToObject(m_copyPathAndFileNameToClipboardAction); @@ -938,7 +939,7 @@ OverlayViewController::createConstructionMenu(QWidget* parent, this, SLOT(menuCopyMapNameToClipBoard())); copyMapNameAction->setObjectName(menuActionNamePrefix - + "Copy_Map_Name_To_Clipboard"); + + "CopyMapNameToClipboard"); copyMapNameAction->setToolTip("Copy name of selected map to the clipboard"); macroManager->addMacroSupportToObject(copyMapNameAction); diff --git a/src/GuiQt/OverlayViewController.h b/src/GuiQt/OverlayViewController.h index 0954f4cd2..c0fb1d76a 100644 --- a/src/GuiQt/OverlayViewController.h +++ b/src/GuiQt/OverlayViewController.h @@ -50,6 +50,7 @@ namespace caret { QGridLayout* gridLayout, const int32_t browserWindowIndex, const int32_t overlayIndex, + const QString& parentObjectName, QObject* parent); virtual ~OverlayViewController(); diff --git a/src/GuiQt/StructureSurfaceSelectionControl.cxx b/src/GuiQt/StructureSurfaceSelectionControl.cxx index c3f9674eb..76f338e45 100644 --- a/src/GuiQt/StructureSurfaceSelectionControl.cxx +++ b/src/GuiQt/StructureSurfaceSelectionControl.cxx @@ -30,6 +30,7 @@ #include "EventModelGetAll.h" #include "WuQFactory.h" #include "GuiManager.h" +#include "WuQMacroManager.h" #include "WuQtUtilities.h" #define __STRUCTURE_SURFACE_SELECTION_CONTROL_DECLARE__ @@ -50,9 +51,16 @@ using namespace caret; /** * Constructor. + * + * @param showLabels + * Show labels on controls + * @param objectNamePrefix + * Object name prefix used for macros */ -StructureSurfaceSelectionControl::StructureSurfaceSelectionControl(const bool showLabels) -: QWidget() +StructureSurfaceSelectionControl::StructureSurfaceSelectionControl(const bool showLabels, + const QString& objectNamePrefix, + QWidget* parent) +: QWidget(parent) { this->surfaceControllerSelector = NULL; @@ -60,11 +68,19 @@ StructureSurfaceSelectionControl::StructureSurfaceSelectionControl(const bool sh this->structureSelectionComboBox->setSizeAdjustPolicy(QComboBox::AdjustToContents); QObject::connect(this->structureSelectionComboBox, SIGNAL(currentIndexChanged(int)), this, SLOT(structureSelected(int))); + this->structureSelectionComboBox->setToolTip("Selects Structure of Surface"); + this->structureSelectionComboBox->setObjectName(objectNamePrefix + + ":StructureSelection"); + WuQMacroManager::instance()->addMacroSupportToObject(this->structureSelectionComboBox); this->surfaceControllerSelectionComboBox = WuQFactory::newComboBox(); this->surfaceControllerSelectionComboBox->setSizeAdjustPolicy(QComboBox::AdjustToContents); QObject::connect(this->surfaceControllerSelectionComboBox, SIGNAL(currentIndexChanged(int)), this, SLOT(surfaceControllerSelected(int))); + this->surfaceControllerSelectionComboBox->setToolTip("Selects Surface"); + this->surfaceControllerSelectionComboBox->setObjectName(objectNamePrefix + + ":SurfaceSelection"); + WuQMacroManager::instance()->addMacroSupportToObject(this->surfaceControllerSelectionComboBox); QGridLayout* layout = new QGridLayout(this); layout->setColumnStretch(0, 0); diff --git a/src/GuiQt/StructureSurfaceSelectionControl.h b/src/GuiQt/StructureSurfaceSelectionControl.h index c878fee00..a98123f57 100644 --- a/src/GuiQt/StructureSurfaceSelectionControl.h +++ b/src/GuiQt/StructureSurfaceSelectionControl.h @@ -39,7 +39,9 @@ namespace caret { Q_OBJECT public: - StructureSurfaceSelectionControl(const bool showLabels); + StructureSurfaceSelectionControl(const bool showLabels, + const QString& objectNamePrefix, + QWidget* parent); virtual ~StructureSurfaceSelectionControl(); diff --git a/src/GuiQt/SurfaceSelectionViewController.cxx b/src/GuiQt/SurfaceSelectionViewController.cxx index f8b52c432..61e98e3c3 100644 --- a/src/GuiQt/SurfaceSelectionViewController.cxx +++ b/src/GuiQt/SurfaceSelectionViewController.cxx @@ -32,6 +32,7 @@ #include "SurfaceSelectionModel.h" #include "WuQEventBlockingFilter.h" #include "WuQFactory.h" +#include "WuQMacroManager.h" using namespace caret; @@ -50,13 +51,17 @@ using namespace caret; * The parent. * @param surfaceSelection * Surface selection that is controlled through this control. + * @param objectName + * Name for combo box */ SurfaceSelectionViewController::SurfaceSelectionViewController(QObject* parent, - SurfaceSelectionModel* surfaceSelectionModel) + SurfaceSelectionModel* surfaceSelectionModel, + const QString& objectName) : WuQWidget(parent) { this->initializeControl(MODE_SELECTION_MODEL_STATIC, - surfaceSelectionModel); + surfaceSelectionModel, + objectName); } /** @@ -65,9 +70,12 @@ SurfaceSelectionViewController::SurfaceSelectionViewController(QObject* parent, * The parent. * @param brainStructure * Allows selection of any surface with the specified brain structure. + * @param objectName + * Name for combo box */ SurfaceSelectionViewController::SurfaceSelectionViewController(QObject* parent, - BrainStructure* brainStructure) + BrainStructure* brainStructure, + const QString& objectName) : WuQWidget(parent) { std::vector allSurfaceTypes; @@ -76,7 +84,8 @@ SurfaceSelectionViewController::SurfaceSelectionViewController(QObject* parent, SurfaceSelectionModel* ss = new SurfaceSelectionModel(brainStructure->getStructure(), allSurfaceTypes); this->initializeControl(MODE_BRAIN_STRUCTURE, - ss); + ss, + objectName); } /** @@ -108,26 +117,42 @@ SurfaceSelectionViewController::~SurfaceSelectionViewController() * * @param parent * The parent. + * @param objectName + * Name for combo box */ -SurfaceSelectionViewController::SurfaceSelectionViewController(QObject* parent) +SurfaceSelectionViewController::SurfaceSelectionViewController(QObject* parent, + const QString& objectName) : WuQWidget(parent) { this->initializeControl(MODE_SELECTION_MODEL_DYNAMIC, - NULL); + NULL, + objectName); } /** - * Help initialize an instance. + * @param mode + * The mode. + * @param surfaceSelectionModel + * Model for surface selection + * @param objectName + * Name for combo box + */ void SurfaceSelectionViewController::initializeControl(const Mode mode, - SurfaceSelectionModel* surfaceSelectionModel) + SurfaceSelectionModel* surfaceSelectionModel, + const QString& objectName) { this->mode = mode; this->surfaceComboBox = WuQFactory::newComboBox(); this->surfaceComboBox->setSizeAdjustPolicy(QComboBox::AdjustToContents); QObject::connect(this->surfaceComboBox, SIGNAL(currentIndexChanged(int)), this, SLOT(comboBoxCurrentIndexChanged(int))); + + this->surfaceComboBox->setToolTip("Selects a surface in a combo box"); + this->surfaceComboBox->setObjectName(objectName); + WuQMacroManager::instance()->addMacroSupportToObject(this->surfaceComboBox); + //#ifdef CARET_OS_MACOSX // /* diff --git a/src/GuiQt/SurfaceSelectionViewController.h b/src/GuiQt/SurfaceSelectionViewController.h index 6254b129b..6802692fb 100644 --- a/src/GuiQt/SurfaceSelectionViewController.h +++ b/src/GuiQt/SurfaceSelectionViewController.h @@ -36,12 +36,15 @@ namespace caret { public: SurfaceSelectionViewController(QObject* parent, - SurfaceSelectionModel* surfaceSelectionModel); + SurfaceSelectionModel* surfaceSelectionModel, + const QString& objectName); SurfaceSelectionViewController(QObject* parent, - BrainStructure* brainStructure); + BrainStructure* brainStructure, + const QString& objectName); - SurfaceSelectionViewController(QObject* parent); + SurfaceSelectionViewController(QObject* parent, + const QString& objectName); virtual ~SurfaceSelectionViewController(); @@ -78,7 +81,8 @@ namespace caret { }; void initializeControl(const Mode mode, - SurfaceSelectionModel* surfaceSelectionModel); + SurfaceSelectionModel* surfaceSelectionModel, + const QString& objectName); Mode mode; diff --git a/src/GuiQt/VolumeSurfaceOutlineSetViewController.cxx b/src/GuiQt/VolumeSurfaceOutlineSetViewController.cxx index 0479a4571..c783b2e13 100644 --- a/src/GuiQt/VolumeSurfaceOutlineSetViewController.cxx +++ b/src/GuiQt/VolumeSurfaceOutlineSetViewController.cxx @@ -53,11 +53,14 @@ using namespace caret; * Orientation for layout * @param browserWindowIndex * Index of browser window that contains this view controller. + * @param parentObjectNamePrefix + * Name of parent object for macros * @param parent * Parent widget. */ VolumeSurfaceOutlineSetViewController::VolumeSurfaceOutlineSetViewController(const Qt::Orientation orientation, const int32_t browserWindowIndex, + const QString& parentObjectNamePrefix, QWidget* parent) : QWidget(parent) { @@ -91,8 +94,11 @@ VolumeSurfaceOutlineSetViewController::VolumeSurfaceOutlineSetViewController(con } for (int32_t i = 0; i < BrainConstants::MAXIMUM_NUMBER_OF_VOLUME_SURFACE_OUTLINES; i++) { + const QString name = QString(parentObjectNamePrefix + + ":VolumeSurfaceOutline%1").arg((int)(i + 1), 2, 10, QLatin1Char('0')); VolumeSurfaceOutlineViewController* ovc = new VolumeSurfaceOutlineViewController(orientation, - gridLayout); + gridLayout, + name); this->outlineViewControllers.push_back(ovc); } diff --git a/src/GuiQt/VolumeSurfaceOutlineSetViewController.h b/src/GuiQt/VolumeSurfaceOutlineSetViewController.h index 380b227dd..56d642734 100644 --- a/src/GuiQt/VolumeSurfaceOutlineSetViewController.h +++ b/src/GuiQt/VolumeSurfaceOutlineSetViewController.h @@ -42,6 +42,7 @@ namespace caret { public: VolumeSurfaceOutlineSetViewController(const Qt::Orientation orientation, const int32_t browserWindowIndex, + const QString& parentObjectNamePrefix, QWidget* parent = 0); virtual ~VolumeSurfaceOutlineSetViewController(); diff --git a/src/GuiQt/VolumeSurfaceOutlineViewController.cxx b/src/GuiQt/VolumeSurfaceOutlineViewController.cxx index cd54c9d8e..67f31c8ab 100644 --- a/src/GuiQt/VolumeSurfaceOutlineViewController.cxx +++ b/src/GuiQt/VolumeSurfaceOutlineViewController.cxx @@ -40,6 +40,7 @@ #include "WuQDoubleSpinBox.h" #include "WuQFactory.h" #include "WuQGridLayoutGroup.h" +#include "WuQMacroManager.h" #include "WuQtUtilities.h" using namespace caret; @@ -56,18 +57,25 @@ using namespace caret; */ VolumeSurfaceOutlineViewController::VolumeSurfaceOutlineViewController(const Qt::Orientation orientation, QGridLayout* gridLayout, + const QString& objectNamePrefix, QObject* parent) : QObject(parent) { + WuQMacroManager* macroManager = WuQMacroManager::instance(); + this->outlineModel = NULL; this->enabledCheckBox = new QCheckBox(" "); QObject::connect(this->enabledCheckBox, SIGNAL(stateChanged(int)), this, SLOT(enabledCheckBoxStateChanged(int))); this->enabledCheckBox->setToolTip("Enables display of this volume surface outline"); + this->enabledCheckBox->setObjectName(objectNamePrefix + + ":Enable"); + macroManager->addMacroSupportToObject(this->enabledCheckBox); - - this->surfaceSelectionViewController = new SurfaceSelectionViewController(this); + this->surfaceSelectionViewController = new SurfaceSelectionViewController(this, + (objectNamePrefix + + ":Surface")); QObject::connect(this->surfaceSelectionViewController, SIGNAL(surfaceSelected(Surface*)), this, SLOT(surfaceSelected(Surface*))); this->surfaceSelectionViewController->getWidget()->setToolTip("Select surface drawn as outline over volume slices"); @@ -78,13 +86,20 @@ VolumeSurfaceOutlineViewController::VolumeSurfaceOutlineViewController(const Qt: this->colorOrTabSelectionControl->getWidget()->setToolTip("Select coloring for surface outline.\n" "If tab, coloring assigned to selected surface\n" "in the selected tab is used.\n"); + this->colorOrTabSelectionControl->getWidget()->setObjectName(objectNamePrefix + + ":ColorSource"); + macroManager->addMacroSupportToObject(this->colorOrTabSelectionControl->getWidget()); + this->thicknessSpinBox = new WuQDoubleSpinBox(this); this->thicknessSpinBox->setRange(0.0, 100.0); this->thicknessSpinBox->setSingleStep(0.10); this->thicknessSpinBox->setSuffix("%"); QObject::connect(this->thicknessSpinBox, static_cast(&WuQDoubleSpinBox::valueChanged), this, &VolumeSurfaceOutlineViewController::thicknessSpinBoxValueChanged); - this->thicknessSpinBox->setToolTip("Thickness of surface outline as percentage of viewport height"); + this->thicknessSpinBox->getWidget()->setToolTip("Thickness of surface outline as percentage of viewport height"); + this->thicknessSpinBox->getWidget()->setObjectName(objectNamePrefix + + ":Thickness"); + macroManager->addMacroSupportToObject(this->thicknessSpinBox->getWidget()); if (orientation == Qt::Horizontal) { diff --git a/src/GuiQt/VolumeSurfaceOutlineViewController.h b/src/GuiQt/VolumeSurfaceOutlineViewController.h index 74cb33885..675e0c3dd 100644 --- a/src/GuiQt/VolumeSurfaceOutlineViewController.h +++ b/src/GuiQt/VolumeSurfaceOutlineViewController.h @@ -43,6 +43,7 @@ namespace caret { public: VolumeSurfaceOutlineViewController(const Qt::Orientation orientation, QGridLayout* gridLayout, + const QString& objectNamePrefix, QObject* parent = 0); virtual ~VolumeSurfaceOutlineViewController(); diff --git a/src/GuiQt/WuQDataEntryDialog.cxx b/src/GuiQt/WuQDataEntryDialog.cxx index 22bcbd9bb..b3c993588 100644 --- a/src/GuiQt/WuQDataEntryDialog.cxx +++ b/src/GuiQt/WuQDataEntryDialog.cxx @@ -587,7 +587,8 @@ WuQDataEntryDialog::addSurfaceSelectionViewController(const QString& labelText, { SurfaceSelectionViewController* surfaceSelectionViewController = new SurfaceSelectionViewController(this, - brainStructure); + brainStructure, + "DataEntryDialogSurfaceComboBox"); this->addWidget(labelText, surfaceSelectionViewController->getWidget()); diff --git a/src/GuiQt/WuQMacroExecutor.cxx b/src/GuiQt/WuQMacroExecutor.cxx index 45279b887..e69e2184c 100644 --- a/src/GuiQt/WuQMacroExecutor.cxx +++ b/src/GuiQt/WuQMacroExecutor.cxx @@ -26,7 +26,9 @@ #include #include +#include #include +#include #include #include #include @@ -44,6 +46,7 @@ #include #include "CaretAssert.h" +#include "CaretLogger.h" #include "WuQMacro.h" #include "WuQMacroCommand.h" #include "WuQMacroMouseEventInfo.h" @@ -110,7 +113,11 @@ WuQMacroExecutor::moveMouse(QWidget* widget, * @param macro * Macro that is run * @param window - * The window from which macro was launched + * The window from which macro was launched and is searched for + * objects contained in the macro commands + * @param + * Additional objects that are searched for objects contained + * in the macro commands * @param options * Executor options * @param errorMessageOut @@ -121,6 +128,7 @@ WuQMacroExecutor::moveMouse(QWidget* widget, bool WuQMacroExecutor::runMacro(const WuQMacro* macro, QObject* window, + std::vector& otherObjectParents, const RunOptions& options, QString& errorMessageOut) const { @@ -134,13 +142,54 @@ WuQMacroExecutor::runMacro(const WuQMacro* macro, const QString objectName(mc->getObjectName()); QObject* object = window->findChild(objectName); if (object == NULL) { - errorMessageOut.append("Unable to find object named " - + objectName - + "\n"); - if (options.m_stopOnErrorFlag) { - return false; + object = qApp->findChild(objectName); + if (object != NULL) { + CaretLogWarning("Found " + + objectName + + " in the app"); + } + else { + for (auto other : otherObjectParents) { + object = other->findChild(objectName); + if (object != NULL) { + break; + } + } + const bool extraFindFlag(false); + if (extraFindFlag) { + static bool firstTime = true; + if (firstTime) { + QWidgetList topLevelWs = QApplication::topLevelWidgets(); + std::cout << "Top level widget count: " << topLevelWs.size() << std::endl; + for (QObject* w : topLevelWs) { + if (w->objectName() == objectName) { + std::cout << objectName << " is a top level widget" << std::endl; + break; + } + object = w->findChild(objectName); + if (object != NULL) { + QString parentName; + QObject* parent = object->parent(); + if (parent != NULL) { + parentName = parent->objectName(); + } + std::cout << "Found object in top level widget: \"" << parentName << "\"" << std::endl; + break; + } + } + } + } + + if (object == NULL) { + errorMessageOut.append("Unable to find object named " + + objectName + + "\n"); + if (options.m_stopOnErrorFlag) { + return false; + } + continue; + } } - continue; } if (object->signalsBlocked()) { @@ -245,6 +294,34 @@ WuQMacroExecutor::runMacroCommand(const WuQMacroCommand* macroCommand, } } break; + case WuQMacroObjectTypeEnum::ACTION_GROUP: + { + QActionGroup* actionGroup = qobject_cast(object); + if (actionGroup != NULL) { + CaretAssert(dataValueType == WuQMacroDataValueTypeEnum::STRING); + WuQMacroSignalEmitter signalEmitter; + signalEmitter.emitActionGroupSignal(actionGroup, + objectValue.toString()); + } + else { + notFoundFlag = true; + } + } + break; + case WuQMacroObjectTypeEnum::BUTTON_GROUP: + { + QButtonGroup* buttonGroup = qobject_cast(object); + if (buttonGroup != NULL) { + CaretAssert(dataValueType == WuQMacroDataValueTypeEnum::STRING); + WuQMacroSignalEmitter signalEmitter; + signalEmitter.emitQButtonGroupSignal(buttonGroup, + objectValue.toString()); + } + else { + notFoundFlag = true; + } + } + break; case WuQMacroObjectTypeEnum::CHECK_BOX: { QCheckBox* checkBox = qobject_cast(object); @@ -324,8 +401,8 @@ WuQMacroExecutor::runMacroCommand(const WuQMacroCommand* macroCommand, if (menu != NULL) { CaretAssert(dataValueType == WuQMacroDataValueTypeEnum::STRING); WuQMacroSignalEmitter signalEmitter; - signalEmitter.emitQMenuTriggered(menu, - objectValue.toString()); + signalEmitter.emitQMenuSignal(menu, + objectValue.toString()); } else { notFoundFlag = true; diff --git a/src/GuiQt/WuQMacroExecutor.h b/src/GuiQt/WuQMacroExecutor.h index 32c46b0f4..1df096471 100644 --- a/src/GuiQt/WuQMacroExecutor.h +++ b/src/GuiQt/WuQMacroExecutor.h @@ -70,6 +70,7 @@ namespace caret { bool runMacro(const WuQMacro* macro, QObject* window, + std::vector& otherObjectParents, const RunOptions& options, QString& errorMessageOut) const; diff --git a/src/GuiQt/WuQMacroManager.cxx b/src/GuiQt/WuQMacroManager.cxx index 0736b980b..f0344cd66 100644 --- a/src/GuiQt/WuQMacroManager.cxx +++ b/src/GuiQt/WuQMacroManager.cxx @@ -161,9 +161,15 @@ WuQMacroManager::isModeRecording() const * * @param object * Object that is monitored. + * @param toolTipTextOverride + * Override of object's tooltip. This is primarily used when + * an object of a particular class does not support a tooltip + * such as a QButtonGroup. This can also be empty to avoid + * the "no tooltip" message. */ -void -WuQMacroManager::addMacroSupportToObject(QObject* object) +bool +WuQMacroManager::addMacroSupportToObjectWithToolTip(QObject* object, + const QString& toolTipOverride) { CaretAssert(object); @@ -171,10 +177,56 @@ WuQMacroManager::addMacroSupportToObject(QObject* object) if (name.isEmpty()) { CaretLogSevere("Object name is empty, will be ignored for macros\n" + SystemUtilities::getBackTrace()); - return; + return false; + } + + auto existingWatcher = m_signalWatchers.find(name); + if (existingWatcher != m_signalWatchers.end()) { + CaretLogSevere("Object named \"" + + name + + "\" has already been connected for macros\n" + + SystemUtilities::getBackTrace() + + "\n"); + return false; } - { + AString errorMessage; + WuQMacroSignalWatcher* widgetWatcher = WuQMacroSignalWatcher::newInstance(this, + object, + toolTipOverride, + errorMessage); + if (widgetWatcher != NULL) { + widgetWatcher->setParent(this); + m_signalWatchers.insert(std::make_pair(name, + widgetWatcher)); + return true; + } + else { + CaretLogWarning(errorMessage); + return false; + } +} + +/** + * Add macro support to the given object. When recording, + * The object's 'value changed' signal will be monitored so + * that the new value can be part of a macro command. + * + * @param object + * Object that is monitored. + * @param toolTipTextOverride + * Override of object's tooltip. This is primarily used when + * an object of a particular class does not support a tooltip + * such as a QButtonGroup + */ +bool +WuQMacroManager::addMacroSupportToObject(QObject* object) +{ + CaretAssert(object); + + const bool resultFlag = addMacroSupportToObjectWithToolTip(object, + ""); + if (resultFlag) { QString toolTipText; QAction* action = qobject_cast(object); if (action != NULL) { @@ -184,34 +236,67 @@ WuQMacroManager::addMacroSupportToObject(QObject* object) if (widget != NULL) { toolTipText = widget->toolTip(); } + if (toolTipText.isEmpty()) { CaretLogWarning("Object named \"" - + name + + object->objectName() + "\" is missing a tooltip"); } } - auto existingWatcher = m_signalWatchers.find(name); - if (existingWatcher != m_signalWatchers.end()) { - CaretLogSevere("Object named \"" - + name - + "\" has already been connected for macros\n" - + SystemUtilities::getBackTrace() - + "\n"); - } - else { - AString errorMessage; - WuQMacroSignalWatcher* widgetWatcher = WuQMacroSignalWatcher::newInstance(this, - object, - errorMessage); - if (widgetWatcher != NULL) { - widgetWatcher->setParent(this); - m_signalWatchers.insert(std::make_pair(name, - widgetWatcher)); - } - else { - CaretLogWarning(errorMessage); - } - } + + return resultFlag; + +// const QString name = object->objectName(); +// if (name.isEmpty()) { +// CaretLogSevere("Object name is empty, will be ignored for macros\n" +// + SystemUtilities::getBackTrace()); +// return false; +// } +// +// +// { +// QString toolTipText; +// if (toolTipTextOverride.isEmpty()) { +// QAction* action = qobject_cast(object); +// if (action != NULL) { +// toolTipText = action->toolTip(); +// } +// QWidget* widget = qobject_cast(object); +// if (widget != NULL) { +// toolTipText = widget->toolTip(); +// } +// if (toolTipText.isEmpty()) { +// CaretLogWarning("Object named \"" +// + name +// + "\" is missing a tooltip"); +// } +// } +// else { +// toolTipText = toolTipTextOverride; +// } +// } +// auto existingWatcher = m_signalWatchers.find(name); +// if (existingWatcher != m_signalWatchers.end()) { +// CaretLogSevere("Object named \"" +// + name +// + "\" has already been connected for macros\n" +// + SystemUtilities::getBackTrace() +// + "\n"); +// } +// else { +// AString errorMessage; +// WuQMacroSignalWatcher* widgetWatcher = WuQMacroSignalWatcher::newInstance(this, +// object, +// errorMessage); +// if (widgetWatcher != NULL) { +// widgetWatcher->setParent(this); +// m_signalWatchers.insert(std::make_pair(name, +// widgetWatcher)); +// } +// else { +// CaretLogWarning(errorMessage); +// } +// } } /** @@ -434,6 +519,7 @@ WuQMacroManager::runMacro(QWidget* widget, WuQMacroExecutor executor; if ( ! executor.runMacro(macro, widget, + m_parentObjects, runOptions, errorMessage)) { QMessageBox::critical(widget, @@ -444,6 +530,22 @@ WuQMacroManager::runMacro(QWidget* widget, } } +/** + * Add a parent object that will be searched during macro + * execution to find objects by name that are contained + * in a macro command + * + * @param parentObject + * Object used to find objects + */ +void +WuQMacroManager::addParentObject(QObject* parentObject) +{ + CaretAssert(parentObject); + m_parentObjects.push_back(parentObject); +} + + /** * Edit a macros attributes (name, description) * diff --git a/src/GuiQt/WuQMacroManager.h b/src/GuiQt/WuQMacroManager.h index 32f7cf1e8..363b1202f 100644 --- a/src/GuiQt/WuQMacroManager.h +++ b/src/GuiQt/WuQMacroManager.h @@ -55,7 +55,10 @@ namespace caret { QString getName() const; - void addMacroSupportToObject(QObject* object); + bool addMacroSupportToObject(QObject* object); + + bool addMacroSupportToObjectWithToolTip(QObject* object, + const QString& toolTipOverride); bool addMacroCommandToRecording(WuQMacroCommand* macroCommand); @@ -103,12 +106,16 @@ namespace caret { void printSupportedWidgetsToTerminal(); + void addParentObject(QObject* parentObject); + // ADD_NEW_METHODS_HERE private: WuQMacroManager(const QString& name, QObject* parent = NULL); + std::vector m_parentObjects; + const QString m_name; WuQMacroModeEnum::Enum m_mode = WuQMacroModeEnum::OFF; diff --git a/src/GuiQt/WuQMacroSignalEmitter.cxx b/src/GuiQt/WuQMacroSignalEmitter.cxx index 8f5530d39..bda4800fe 100644 --- a/src/GuiQt/WuQMacroSignalEmitter.cxx +++ b/src/GuiQt/WuQMacroSignalEmitter.cxx @@ -24,6 +24,8 @@ #undef __WU_Q_MACRO_SIGNAL_EMITTER_DECLARE__ #include +#include +#include #include #include #include @@ -89,6 +91,111 @@ WuQMacroSignalEmitter::emitQActionSignal(QAction* action, QObject::disconnect(this, &WuQMacroSignalEmitter::valueChangedSignalBool, 0, 0); } +/** + * Update a QActionGroup's selected action and cause emission + * of its triggered() signal + * + * @param actionGroup + * The QActionGroup + * @param text + * Text of selected action + */ +void +WuQMacroSignalEmitter::emitActionGroupSignal(QActionGroup* actionGroup, + const QString& text) +{ + CaretAssert(actionGroup); + + QList actions = actionGroup->actions(); + if (actions.isEmpty()) { + CaretLogWarning("Menu " + + actionGroup->objectName() + + " does not contain any actions when trying to select menu item with text: " + + text); + return; + } + + QAction* actionSelected = NULL; + const int32_t numActions = actions.size(); + for (int32_t i = 0; i < numActions; i++) { + if (actions.at(i)->text() == text) { + actionSelected = actions.at(i); + } + } + + if (actionSelected == NULL) { + CaretLogWarning("Unable to find QAction with text \"" + + text + + "\" for menu " + + actionGroup->objectName()); + return; + } + + QSignalBlocker blocker(actionGroup); + actionSelected->trigger(); + blocker.unblock(); + + + QObject::connect(this, &WuQMacroSignalEmitter::valueChangedSignalActionGroupAction, + actionGroup, &QActionGroup::triggered); + + emit valueChangeSignalMenuAction(actionSelected); + + QObject::disconnect(this, &WuQMacroSignalEmitter::valueChangedSignalActionGroupAction, 0, 0); +} + +/** + * Update a QButtonGroup and cause emission + * of its buttonClicked() signal + * + * @param buttonGroup + * The QButtonGroup + * @param text + * Text of button + */ +void +WuQMacroSignalEmitter::emitQButtonGroupSignal(QButtonGroup* buttonGroup, + const QString& text) +{ + CaretAssert(buttonGroup); + + QAbstractButton* buttonSelected = NULL; + + QList allButtons = buttonGroup->buttons(); + if (allButtons.isEmpty()) { + CaretLogWarning("ButtonGroup " + + buttonGroup->objectName() + + " does not contain any buttons when trying to select button item with text: " + + text); + return; + } + + for (auto b : allButtons) { + if (b->text() == text) { + buttonSelected = b; + break; + } + } + if (buttonSelected == NULL) { + CaretLogWarning("Unable to find QAbstactButton with text \"" + + text + + "\" for button group " + + buttonGroup->objectName()); + return; + } + + QSignalBlocker blocker(buttonGroup); + buttonSelected->click(); + blocker.unblock(); + + QObject::connect(this, &WuQMacroSignalEmitter::valueChangedSignalAbstractButton, + buttonGroup, static_cast(&QButtonGroup::buttonClicked)); + + emit valueChangedSignalAbstractButton(buttonSelected); + + QObject::disconnect(this, &WuQMacroSignalEmitter::valueChangedSignalBool, 0, 0); +} + /** * Update a QCheckBox's checked status and cause emission * of its clicked() signal @@ -103,7 +210,7 @@ WuQMacroSignalEmitter::emitQCheckBoxSignal(QCheckBox* checkBox, const bool checked) { CaretAssert(checkBox); - checkBox->setChecked(checkBox); + checkBox->setChecked(checked); QObject::connect(this, &WuQMacroSignalEmitter::valueChangedSignalBool, checkBox, &QCheckBox::clicked); @@ -226,24 +333,39 @@ WuQMacroSignalEmitter::emitQLineEditSignal(QLineEdit* lineEdit, } /** - * Update a QLineEdit's text and cause emission - * of its textChanged() signal + * Select an item from a menu using the item's text * * @param lineEdit * The QLineEdit - * @param text - * New text + * @param menuActionText + * Text of menu item for selection */ void -WuQMacroSignalEmitter::emitQMenuTriggered(QMenu* menu, - const QString& text) +WuQMacroSignalEmitter::emitQMenuSignal(QMenu* menu, + const QString& menuActionText) { CaretAssert(menu); QList actions = menu->actions(); if (actions.isEmpty()) { + CaretLogWarning("Menu " + + menu->objectName() + + " does not contain any actions when trying to select menu item with text: " + + menuActionText); + return; + } + + QAction* actionSelected = NULL; + const int32_t numActions = actions.size(); + for (int32_t i = 0; i < numActions; i++) { + if (actions.at(i)->text() == menuActionText) { + actionSelected = actions.at(i); + } + } + + if (actionSelected == NULL) { CaretLogWarning("Unable to find QAction with text \"" - + text + + menuActionText + "\" for menu " + menu->objectName()); return; @@ -252,7 +374,7 @@ WuQMacroSignalEmitter::emitQMenuTriggered(QMenu* menu, QObject::connect(this, &WuQMacroSignalEmitter::valueChangeSignalMenuAction, menu, &QMenu::triggered); - emit valueChangeSignalMenuAction(actions.at(0)); + emit valueChangeSignalMenuAction(actionSelected); QObject::disconnect(this, &WuQMacroSignalEmitter::valueChangeSignalMenuAction, 0, 0); } @@ -296,12 +418,26 @@ WuQMacroSignalEmitter::emitQRadioButtonSignal(QRadioButton* radioButton, const bool checked) { CaretAssert(radioButton); - radioButton->setChecked(checked); QObject::connect(this, &WuQMacroSignalEmitter::valueChangedSignalBool, radioButton, &QRadioButton::clicked); - emit valueChangedSignalBool(checked); + const bool useClickFlag(true); + if (useClickFlag) { + QSignalBlocker blocker(radioButton); + radioButton->setChecked( !checked); + blocker.unblock(); + radioButton->click(); + } + else { + /* + * This does not work for a radio button in a button group + * as either click() or animateClick() is needed for the + * button group to emit a signal + */ + radioButton->setChecked(checked); + emit valueChangedSignalBool(checked); + } QObject::disconnect(this, &WuQMacroSignalEmitter::valueChangedSignalBool, 0, 0); } diff --git a/src/GuiQt/WuQMacroSignalEmitter.h b/src/GuiQt/WuQMacroSignalEmitter.h index 89a858f6f..ed185720c 100644 --- a/src/GuiQt/WuQMacroSignalEmitter.h +++ b/src/GuiQt/WuQMacroSignalEmitter.h @@ -28,6 +28,9 @@ #include class QAction; +class QActionGroup; +class QAbstractButton; +class QButtonGroup; class QCheckBox; class QComboBox; class QDoubleSpinBox; @@ -61,9 +64,15 @@ namespace caret { void emitQActionSignal(QAction* action, const bool checked); + void emitActionGroupSignal(QActionGroup* actionGroup, + const QString& text); + void emitQCheckBoxSignal(QCheckBox* checkBox, const bool checked); + void emitQButtonGroupSignal(QButtonGroup* buttonGroup, + const QString& text); + void emitQComboBoxSignal(QComboBox* comboBox, const int32_t index); @@ -76,7 +85,7 @@ namespace caret { void emitQListWidgetSignal(QListWidget* listWidget, const QString& text); - void emitQMenuTriggered(QMenu* menu, + void emitQMenuSignal(QMenu* menu, const QString& text); void emitQPushButtonSignal(QPushButton* pushButton, @@ -103,10 +112,14 @@ namespace caret { // ADD_NEW_METHODS_HERE signals: + void valueChangedSignalActionGroupAction(QAction*); + void valueChangeSignalMenuAction(QAction*); void valueChangedSignalBool(bool); + void valueChangedSignalAbstractButton(QAbstractButton* button); + void valueChangedSignalInt(int); void valueChangedSignalDouble(double); diff --git a/src/GuiQt/WuQMacroSignalWatcher.cxx b/src/GuiQt/WuQMacroSignalWatcher.cxx index 264c2d6f2..1ce900273 100644 --- a/src/GuiQt/WuQMacroSignalWatcher.cxx +++ b/src/GuiQt/WuQMacroSignalWatcher.cxx @@ -24,6 +24,8 @@ #undef __WU_Q_MACRO_SIGNAL_WATCHER_DECLARE__ #include +#include +#include #include #include #include @@ -39,6 +41,7 @@ #include #include "CaretAssert.h" +#include "CaretLogger.h" #include "WuQMacroCommand.h" #include "WuQMacroManager.h" @@ -59,10 +62,14 @@ using namespace caret; * Object that is watched for a "value changed" signal * @param objectType * The type of the object. + * @param toolTipTextOverride + * Used to override tool tip or for when object does not + * support a tool tip. */ WuQMacroSignalWatcher::WuQMacroSignalWatcher(WuQMacroManager* parentMacroManager, QObject* object, - const WuQMacroObjectTypeEnum::Enum objectType) + const WuQMacroObjectTypeEnum::Enum objectType, + const QString& toolTipTextOverride) : QObject(), m_parentMacroManager(parentMacroManager), m_objectType(objectType), @@ -86,6 +93,22 @@ m_objectName(object->objectName()) m_toolTipText = action->toolTip(); } break; + case WuQMacroObjectTypeEnum::ACTION_GROUP: + { + QActionGroup* actionGroup = qobject_cast(object); + CaretAssert(actionGroup); + QObject::connect(actionGroup, &QActionGroup::triggered, + this, &WuQMacroSignalWatcher::actionGroupTriggered); + } + break; + case WuQMacroObjectTypeEnum::BUTTON_GROUP: + { + QButtonGroup* buttonGroup = qobject_cast(object); + CaretAssert(buttonGroup); + QObject::connect(buttonGroup, static_cast(&QButtonGroup::buttonClicked), + this, &WuQMacroSignalWatcher::buttonGroupButtonClicked); + } + break; case WuQMacroObjectTypeEnum::CHECK_BOX: { QCheckBox* checkBox = qobject_cast(object); @@ -197,6 +220,18 @@ m_objectName(object->objectName()) } break; } + + /* + * Override the tool tip text + */ + if ( ! toolTipTextOverride.isEmpty()) { + m_toolTipText = toolTipTextOverride; + } + + QObject::connect(object, &QObject::destroyed, + this, &WuQMacroSignalWatcher::objectWasDestroyed); + QObject::connect(object, &QObject::objectNameChanged, + this, &WuQMacroSignalWatcher::objectNameWasChanged); } /** @@ -206,6 +241,38 @@ WuQMacroSignalWatcher::~WuQMacroSignalWatcher() { } +/** + * Called if the object whose signal is being monitored is destroyed + * + * @obj + * Pointer to object that was destroyed + */ +void +WuQMacroSignalWatcher::objectWasDestroyed(QObject* /*obj*/) +{ + /* + * Log object destroyed only when NOT debug + */ +#ifndef NDEBUG + CaretLogWarning("Object was destroyed: " + + m_objectName); +#endif +} + +/** + * Called if the object whose signal is being monitored + * has its name changed + * + * @name + * New name + */ +void +WuQMacroSignalWatcher::objectNameWasChanged(const QString& name) +{ + std::cout << "Object name changed from " + << m_objectName << " to " << name << std::endl; +} + /** * Create an new instance of a widget signal watcher for * the given object. @@ -214,6 +281,9 @@ WuQMacroSignalWatcher::~WuQMacroSignalWatcher() * Parent macro manager. * @param object * Object that will have a widget signal watcher. + * @param toolTipTextOverride + * Used to override tool tip or for when object does not + * support a tool tip. * @param errorMessageOut * Output containing error information if failure. * @return @@ -222,11 +292,12 @@ WuQMacroSignalWatcher::~WuQMacroSignalWatcher() WuQMacroSignalWatcher* WuQMacroSignalWatcher::newInstance(WuQMacroManager* parentMacroManager, QObject* object, + const QString& toolTipTextOverride, QString& errorMessageOut) { errorMessageOut.clear(); - const QString objectClassName = object->metaObject()->className(); + QString objectClassName = object->metaObject()->className(); bool validFlag(false); WuQMacroObjectTypeEnum::Enum objectType = WuQMacroObjectTypeEnum::fromGuiName(objectClassName, @@ -244,7 +315,8 @@ WuQMacroSignalWatcher::newInstance(WuQMacroManager* parentMacroManager, WuQMacroSignalWatcher* ww = new WuQMacroSignalWatcher(parentMacroManager, object, - objectType); + objectType, + toolTipTextOverride); return ww; } @@ -269,6 +341,22 @@ WuQMacroSignalWatcher::createAndSendMacroCommand(const QVariant value) } } +/** + * Called when a action group has an item triggered + * + * @param action + * ActionGroup action that was triggered + */ +void +WuQMacroSignalWatcher::actionGroupTriggered(QAction* action) +{ + const QString text((action != NULL) + ? action->text() + : ""); + createAndSendMacroCommand(text); +} + + /** * Called when an action is triggered * @@ -281,6 +369,22 @@ WuQMacroSignalWatcher::actionTriggered(bool checked) createAndSendMacroCommand(checked); } +/** + * Called when a button group button is clicked + * + * @param button + * Button that was clicked + */ +void +WuQMacroSignalWatcher::buttonGroupButtonClicked(QAbstractButton* button) +{ + const QString text((button != NULL) + ? button->text() + : ""); + createAndSendMacroCommand(text); +} + + /** * Called when a check box is clicked * diff --git a/src/GuiQt/WuQMacroSignalWatcher.h b/src/GuiQt/WuQMacroSignalWatcher.h index 6a370c8a1..6e99bde7e 100644 --- a/src/GuiQt/WuQMacroSignalWatcher.h +++ b/src/GuiQt/WuQMacroSignalWatcher.h @@ -29,6 +29,7 @@ #include "WuQMacroObjectTypeEnum.h" +class QAbstractButton; class QAction; class QListWidgetItem; @@ -43,6 +44,7 @@ namespace caret { public: static WuQMacroSignalWatcher* newInstance(WuQMacroManager* parentMacroManager, QObject* object, + const QString& toolTipTextOverride, QString& errorMessageOut); virtual ~WuQMacroSignalWatcher(); @@ -56,6 +58,10 @@ namespace caret { private slots: void actionTriggered(bool); + void actionGroupTriggered(QAction* action); + + void buttonGroupButtonClicked(QAbstractButton* button); + void checkBoxClicked(bool); void comboBoxActivated(int); @@ -82,12 +88,17 @@ namespace caret { void toolButtonClicked(bool); + void objectWasDestroyed(QObject* obj); + + void objectNameWasChanged(const QString& name); + // ADD_NEW_METHODS_HERE private: WuQMacroSignalWatcher(WuQMacroManager* parentMacroManager, QObject* object, - const WuQMacroObjectTypeEnum::Enum objectType); + const WuQMacroObjectTypeEnum::Enum objectType, + const QString& toolTipTextOverride); WuQMacroManager* m_parentMacroManager; diff --git a/src/GuiQt/WuQTabWidget.cxx b/src/GuiQt/WuQTabWidget.cxx index 6f5c69f38..246169e2a 100644 --- a/src/GuiQt/WuQTabWidget.cxx +++ b/src/GuiQt/WuQTabWidget.cxx @@ -116,6 +116,16 @@ WuQTabWidget::getWidget() return m_widget; } +/** + * @return The embedded tab bar + */ +QTabBar* +WuQTabWidget::getTabBar() const +{ + return m_tabBar; +} + + /** * Adds a tab with the given page and label to the tab widget, and returns * the index of the tab in the tab bar. If the tab's label contains an diff --git a/src/GuiQt/WuQTabWidget.h b/src/GuiQt/WuQTabWidget.h index f20d001df..4d88f6069 100644 --- a/src/GuiQt/WuQTabWidget.h +++ b/src/GuiQt/WuQTabWidget.h @@ -55,6 +55,8 @@ namespace caret { QWidget* currentWidget() const; + QTabBar* getTabBar() const; + virtual SceneClass* saveToScene(const SceneAttributes* sceneAttributes, const AString& instanceName); -- GitLab From 9ea7036cf315f282bb1ef12759f8ceb2fb5a2f41 Mon Sep 17 00:00:00 2001 From: John Harwell Date: Thu, 13 Dec 2018 15:41:07 -0600 Subject: [PATCH 129/427] WB-834: Continuing macro development including better support for widgets, such as combo box, whose order of items may be different between the time the macro is created and executed. --- src/Common/CMakeLists.txt | 4 +- ...TypeEnum.cxx => WuQMacroClassTypeEnum.cxx} | 140 +++++----- ...jectTypeEnum.h => WuQMacroClassTypeEnum.h} | 60 ++--- src/Common/WuQMacroCommand.cxx | 173 ++++++------ src/Common/WuQMacroCommand.h | 37 +-- src/Common/WuQMacroGroupXmlFormatBase.h | 18 +- src/Common/WuQMacroGroupXmlReader.cxx | 82 ++++-- src/Common/WuQMacroGroupXmlWriter.cxx | 116 +++++--- src/GuiQt/GuiManager.cxx | 6 +- src/GuiQt/WuQMacroDialog.cxx | 21 +- src/GuiQt/WuQMacroEditorDialog.cxx | 29 +- src/GuiQt/WuQMacroEditorDialog.h | 3 +- src/GuiQt/WuQMacroExecutor.cxx | 249 ++++++++++++++---- src/GuiQt/WuQMacroManager.cxx | 43 ++- src/GuiQt/WuQMacroManager.h | 2 + src/GuiQt/WuQMacroSignalWatcher.cxx | 213 ++++++++++----- src/GuiQt/WuQMacroSignalWatcher.h | 13 +- 17 files changed, 801 insertions(+), 408 deletions(-) rename src/Common/{WuQMacroObjectTypeEnum.cxx => WuQMacroClassTypeEnum.cxx} (68%) rename src/Common/{WuQMacroObjectTypeEnum.h => WuQMacroClassTypeEnum.h} (72%) diff --git a/src/Common/CMakeLists.txt b/src/Common/CMakeLists.txt index f2697ad07..b175aa8ce 100644 --- a/src/Common/CMakeLists.txt +++ b/src/Common/CMakeLists.txt @@ -161,6 +161,7 @@ VectorOperation.h VoxelIJK.h WorkbenchSpecialVersionEnum.h WuQMacro.h +WuQMacroClassTypeEnum.h WuQMacroCommand.h WuQMacroDataValueTypeEnum.h WuQMacroFile.h @@ -171,7 +172,6 @@ WuQMacroGroupXmlWriter.h WuQMacroModeEnum.h WuQMacroMouseEventInfo.h WuQMacroMouseEventTypeEnum.h -WuQMacroObjectTypeEnum.h YokingGroupEnum.h ${MOC_SOURCE_FILES} @@ -274,6 +274,7 @@ Vector3D.cxx VectorOperation.cxx WorkbenchSpecialVersionEnum.cxx WuQMacro.cxx +WuQMacroClassTypeEnum.cxx WuQMacroCommand.cxx WuQMacroDataValueTypeEnum.cxx WuQMacroFile.cxx @@ -284,7 +285,6 @@ WuQMacroGroupXmlWriter.cxx WuQMacroModeEnum.cxx WuQMacroMouseEventInfo.cxx WuQMacroMouseEventTypeEnum.cxx -WuQMacroObjectTypeEnum.cxx YokingGroupEnum.cxx ) diff --git a/src/Common/WuQMacroObjectTypeEnum.cxx b/src/Common/WuQMacroClassTypeEnum.cxx similarity index 68% rename from src/Common/WuQMacroObjectTypeEnum.cxx rename to src/Common/WuQMacroClassTypeEnum.cxx index f2070b9a3..fc2ceea6c 100644 --- a/src/Common/WuQMacroObjectTypeEnum.cxx +++ b/src/Common/WuQMacroClassTypeEnum.cxx @@ -20,9 +20,9 @@ /*LICENSE_END*/ #include -#define __WU_Q_MACRO_OBJECT_TYPE_ENUM_DECLARE__ -#include "WuQMacroObjectTypeEnum.h" -#undef __WU_Q_MACRO_OBJECT_TYPE_ENUM_DECLARE__ +#define __WU_Q_MACRO_CLASS_TYPE_ENUM_DECLARE__ +#include "WuQMacroClassTypeEnum.h" +#undef __WU_Q_MACRO_CLASS_TYPE_ENUM_DECLARE__ #include "CaretAssert.h" @@ -30,7 +30,7 @@ using namespace caret; /** - * \class caret::WuQMacroObjectTypeEnum + * \class caret::WuQMacroClassTypeEnum * \brief Enumerated type for specific type of object in macro * * Using this enumerated type in the GUI with an EnumComboBoxTemplate @@ -40,30 +40,30 @@ using namespace caret; * class EnumComboBoxTemplate; * * Declare the member: - * EnumComboBoxTemplate* m_wuQMacroObjectTypeEnumComboBox; + * EnumComboBoxTemplate* m_WuQMacroClassTypeEnumComboBox; * * Declare a slot that is called when user changes selection * private slots: - * void wuQMacroObjectTypeEnumComboBoxItemActivated(); + * void WuQMacroClassTypeEnumComboBoxItemActivated(); * * Implementation File (.cxx) * Include the header files * #include "EnumComboBoxTemplate.h" - * #include "WuQMacroObjectTypeEnum.h" + * #include "WuQMacroClassTypeEnum.h" * * Instatiate: - * m_wuQMacroObjectTypeEnumComboBox = new EnumComboBoxTemplate(this); - * m_wuQMacroObjectTypeEnumComboBox->setup(); + * m_WuQMacroClassTypeEnumComboBox = new EnumComboBoxTemplate(this); + * m_WuQMacroClassTypeEnumComboBox->setup(); * * Get notified when the user changes the selection: - * QObject::connect(m_wuQMacroObjectTypeEnumComboBox, SIGNAL(itemActivated()), - * this, SLOT(wuQMacroObjectTypeEnumComboBoxItemActivated())); + * QObject::connect(m_WuQMacroClassTypeEnumComboBox, SIGNAL(itemActivated()), + * this, SLOT(WuQMacroClassTypeEnumComboBoxItemActivated())); * * Update the selection: - * m_wuQMacroObjectTypeEnumComboBox->setSelectedItem(NEW_VALUE); + * m_WuQMacroClassTypeEnumComboBox->setSelectedItem(NEW_VALUE); * * Read the selection: - * const WuQMacroObjectTypeEnum::Enum VARIABLE = m_wuQMacroObjectTypeEnumComboBox->getSelectedItem(); + * const WuQMacroClassTypeEnum::Enum VARIABLE = m_WuQMacroClassTypeEnumComboBox->getSelectedItem(); * */ @@ -78,7 +78,7 @@ using namespace caret; * @param guiName * User-friendly name for use in user-interface. */ -WuQMacroObjectTypeEnum::WuQMacroObjectTypeEnum(const Enum enumValue, +WuQMacroClassTypeEnum::WuQMacroClassTypeEnum(const Enum enumValue, const AString& name, const AString& guiName) { @@ -91,7 +91,7 @@ WuQMacroObjectTypeEnum::WuQMacroObjectTypeEnum(const Enum enumValue, /** * Destructor. */ -WuQMacroObjectTypeEnum::~WuQMacroObjectTypeEnum() +WuQMacroClassTypeEnum::~WuQMacroClassTypeEnum() { } @@ -99,82 +99,82 @@ WuQMacroObjectTypeEnum::~WuQMacroObjectTypeEnum() * Initialize the enumerated metadata. */ void -WuQMacroObjectTypeEnum::initialize() +WuQMacroClassTypeEnum::initialize() { if (initializedFlag) { return; } initializedFlag = true; - enumData.push_back(WuQMacroObjectTypeEnum(INVALID, + enumData.push_back(WuQMacroClassTypeEnum(INVALID, "INVALID", "Invalid")); - enumData.push_back(WuQMacroObjectTypeEnum(ACTION, + enumData.push_back(WuQMacroClassTypeEnum(ACTION, "ACTION", "QAction")); - enumData.push_back(WuQMacroObjectTypeEnum(ACTION_GROUP, + enumData.push_back(WuQMacroClassTypeEnum(ACTION_GROUP, "ACTION_GROUP", "QActionGroup")); - enumData.push_back(WuQMacroObjectTypeEnum(BUTTON_GROUP, + enumData.push_back(WuQMacroClassTypeEnum(BUTTON_GROUP, "BUTTON_GROUP", "QButtonGroup")); - enumData.push_back(WuQMacroObjectTypeEnum(CHECK_BOX, + enumData.push_back(WuQMacroClassTypeEnum(CHECK_BOX, "CHECK_BOX", "QCheckBox")); - enumData.push_back(WuQMacroObjectTypeEnum(COMBO_BOX, + enumData.push_back(WuQMacroClassTypeEnum(COMBO_BOX, "COMBO_BOX", "QComboBox")); - enumData.push_back(WuQMacroObjectTypeEnum(DOUBLE_SPIN_BOX, + enumData.push_back(WuQMacroClassTypeEnum(DOUBLE_SPIN_BOX, "DOUBLE_SPIN_BOX", "QDoubleSpinBox")); - enumData.push_back(WuQMacroObjectTypeEnum(LINE_EDIT, + enumData.push_back(WuQMacroClassTypeEnum(LINE_EDIT, "LINE_EDIT", "QLineEdit")); - enumData.push_back(WuQMacroObjectTypeEnum(LIST_WIDGET, + enumData.push_back(WuQMacroClassTypeEnum(LIST_WIDGET, "LIST_WIDGET", "QListWidget")); - enumData.push_back(WuQMacroObjectTypeEnum(MENU, + enumData.push_back(WuQMacroClassTypeEnum(MENU, "MENU", "QMenu")); - enumData.push_back(WuQMacroObjectTypeEnum(MOUSE_USER_EVENT, + enumData.push_back(WuQMacroClassTypeEnum(MOUSE_USER_EVENT, "MOUSE_USER_EVENT", "QMouseEvent")); - enumData.push_back(WuQMacroObjectTypeEnum(PUSH_BUTTON, + enumData.push_back(WuQMacroClassTypeEnum(PUSH_BUTTON, "PUSH_BUTTON", "QPushButton")); - enumData.push_back(WuQMacroObjectTypeEnum(RADIO_BUTTON, + enumData.push_back(WuQMacroClassTypeEnum(RADIO_BUTTON, "RADIO_BUTTON", "QRadioButton")); - enumData.push_back(WuQMacroObjectTypeEnum(SLIDER, + enumData.push_back(WuQMacroClassTypeEnum(SLIDER, "SLIDER", "QSlider")); - enumData.push_back(WuQMacroObjectTypeEnum(SPIN_BOX, + enumData.push_back(WuQMacroClassTypeEnum(SPIN_BOX, "SPIN_BOX", "QSpinBox")); - enumData.push_back(WuQMacroObjectTypeEnum(TAB_BAR, + enumData.push_back(WuQMacroClassTypeEnum(TAB_BAR, "TAB_BAR", "QTabBar")); - enumData.push_back(WuQMacroObjectTypeEnum(TAB_WIDGET, + enumData.push_back(WuQMacroClassTypeEnum(TAB_WIDGET, "TAB_WIDGET", "QTabWidget")); - enumData.push_back(WuQMacroObjectTypeEnum(TOOL_BUTTON, + enumData.push_back(WuQMacroClassTypeEnum(TOOL_BUTTON, "TOOL_BUTTON", "QToolButton")); } @@ -186,14 +186,14 @@ WuQMacroObjectTypeEnum::initialize() * @return Pointer to data for this enumerated type * or NULL if no data for type or if type is invalid. */ -const WuQMacroObjectTypeEnum* -WuQMacroObjectTypeEnum::findData(const Enum enumValue) +const WuQMacroClassTypeEnum* +WuQMacroClassTypeEnum::findData(const Enum enumValue) { if (initializedFlag == false) initialize(); size_t num = enumData.size(); for (size_t i = 0; i < num; i++) { - const WuQMacroObjectTypeEnum* d = &enumData[i]; + const WuQMacroClassTypeEnum* d = &enumData[i]; if (d->enumValue == enumValue) { return d; } @@ -210,10 +210,10 @@ WuQMacroObjectTypeEnum::findData(const Enum enumValue) * String representing enumerated value. */ AString -WuQMacroObjectTypeEnum::toName(Enum enumValue) { +WuQMacroClassTypeEnum::toName(Enum enumValue) { if (initializedFlag == false) initialize(); - const WuQMacroObjectTypeEnum* enumInstance = findData(enumValue); + const WuQMacroClassTypeEnum* enumInstance = findData(enumValue); return enumInstance->name; } @@ -227,18 +227,18 @@ WuQMacroObjectTypeEnum::toName(Enum enumValue) { * @return * Enumerated value. */ -WuQMacroObjectTypeEnum::Enum -WuQMacroObjectTypeEnum::fromName(const AString& name, bool* isValidOut) +WuQMacroClassTypeEnum::Enum +WuQMacroClassTypeEnum::fromName(const AString& name, bool* isValidOut) { if (initializedFlag == false) initialize(); bool validFlag = false; - Enum enumValue = WuQMacroObjectTypeEnum::enumData[0].enumValue; + Enum enumValue = WuQMacroClassTypeEnum::enumData[0].enumValue; - for (std::vector::iterator iter = enumData.begin(); + for (std::vector::iterator iter = enumData.begin(); iter != enumData.end(); iter++) { - const WuQMacroObjectTypeEnum& d = *iter; + const WuQMacroClassTypeEnum& d = *iter; if (d.name == name) { enumValue = d.enumValue; validFlag = true; @@ -250,7 +250,7 @@ WuQMacroObjectTypeEnum::fromName(const AString& name, bool* isValidOut) *isValidOut = validFlag; } else if (validFlag == false) { - CaretAssertMessage(0, AString("Name " + name + "failed to match enumerated value for type WuQMacroObjectTypeEnum")); + CaretAssertMessage(0, AString("Name " + name + "failed to match enumerated value for type WuQMacroClassTypeEnum")); } return enumValue; } @@ -263,10 +263,10 @@ WuQMacroObjectTypeEnum::fromName(const AString& name, bool* isValidOut) * String representing enumerated value. */ AString -WuQMacroObjectTypeEnum::toGuiName(Enum enumValue) { +WuQMacroClassTypeEnum::toGuiName(Enum enumValue) { if (initializedFlag == false) initialize(); - const WuQMacroObjectTypeEnum* enumInstance = findData(enumValue); + const WuQMacroClassTypeEnum* enumInstance = findData(enumValue); return enumInstance->guiName; } @@ -280,8 +280,8 @@ WuQMacroObjectTypeEnum::toGuiName(Enum enumValue) { * @return * Enumerated value. */ -WuQMacroObjectTypeEnum::Enum -WuQMacroObjectTypeEnum::fromGuiName(const AString& guiNameIn, bool* isValidOut) +WuQMacroClassTypeEnum::Enum +WuQMacroClassTypeEnum::fromGuiName(const AString& guiNameIn, bool* isValidOut) { if (initializedFlag == false) initialize(); @@ -297,12 +297,12 @@ WuQMacroObjectTypeEnum::fromGuiName(const AString& guiNameIn, bool* isValidOut) } bool validFlag = false; - Enum enumValue = WuQMacroObjectTypeEnum::enumData[0].enumValue; + Enum enumValue = WuQMacroClassTypeEnum::enumData[0].enumValue; - for (std::vector::iterator iter = enumData.begin(); + for (std::vector::iterator iter = enumData.begin(); iter != enumData.end(); iter++) { - const WuQMacroObjectTypeEnum& d = *iter; + const WuQMacroClassTypeEnum& d = *iter; if (d.guiName == guiName) { enumValue = d.enumValue; validFlag = true; @@ -314,7 +314,7 @@ WuQMacroObjectTypeEnum::fromGuiName(const AString& guiNameIn, bool* isValidOut) *isValidOut = validFlag; } else if (validFlag == false) { - CaretAssertMessage(0, AString("guiName " + guiName + "failed to match enumerated value for type WuQMacroObjectTypeEnum")); + CaretAssertMessage(0, AString("guiName " + guiName + "failed to match enumerated value for type WuQMacroClassTypeEnum")); } return enumValue; } @@ -326,10 +326,10 @@ WuQMacroObjectTypeEnum::fromGuiName(const AString& guiNameIn, bool* isValidOut) * Integer code for data type. */ int32_t -WuQMacroObjectTypeEnum::toIntegerCode(Enum enumValue) +WuQMacroClassTypeEnum::toIntegerCode(Enum enumValue) { if (initializedFlag == false) initialize(); - const WuQMacroObjectTypeEnum* enumInstance = findData(enumValue); + const WuQMacroClassTypeEnum* enumInstance = findData(enumValue); return enumInstance->integerCode; } @@ -344,18 +344,18 @@ WuQMacroObjectTypeEnum::toIntegerCode(Enum enumValue) * @return * Enum for integer code. */ -WuQMacroObjectTypeEnum::Enum -WuQMacroObjectTypeEnum::fromIntegerCode(const int32_t integerCode, bool* isValidOut) +WuQMacroClassTypeEnum::Enum +WuQMacroClassTypeEnum::fromIntegerCode(const int32_t integerCode, bool* isValidOut) { if (initializedFlag == false) initialize(); bool validFlag = false; - Enum enumValue = WuQMacroObjectTypeEnum::enumData[0].enumValue; + Enum enumValue = WuQMacroClassTypeEnum::enumData[0].enumValue; - for (std::vector::iterator iter = enumData.begin(); + for (std::vector::iterator iter = enumData.begin(); iter != enumData.end(); iter++) { - const WuQMacroObjectTypeEnum& enumInstance = *iter; + const WuQMacroClassTypeEnum& enumInstance = *iter; if (enumInstance.integerCode == integerCode) { enumValue = enumInstance.enumValue; validFlag = true; @@ -367,7 +367,7 @@ WuQMacroObjectTypeEnum::fromIntegerCode(const int32_t integerCode, bool* isValid *isValidOut = validFlag; } else if (validFlag == false) { - CaretAssertMessage(0, AString("Integer code " + AString::number(integerCode) + "failed to match enumerated value for type WuQMacroObjectTypeEnum")); + CaretAssertMessage(0, AString("Integer code " + AString::number(integerCode) + "failed to match enumerated value for type WuQMacroClassTypeEnum")); } return enumValue; } @@ -380,13 +380,13 @@ WuQMacroObjectTypeEnum::fromIntegerCode(const int32_t integerCode, bool* isValid * A vector that is OUTPUT containing all of the enumerated values. */ void -WuQMacroObjectTypeEnum::getAllEnums(std::vector& allEnums) +WuQMacroClassTypeEnum::getAllEnums(std::vector& allEnums) { if (initializedFlag == false) initialize(); allEnums.clear(); - for (std::vector::iterator iter = enumData.begin(); + for (std::vector::iterator iter = enumData.begin(); iter != enumData.end(); iter++) { allEnums.push_back(iter->enumValue); @@ -402,16 +402,16 @@ WuQMacroObjectTypeEnum::getAllEnums(std::vector& a * If true, the names are sorted in alphabetical order. */ void -WuQMacroObjectTypeEnum::getAllNames(std::vector& allNames, const bool isSorted) +WuQMacroClassTypeEnum::getAllNames(std::vector& allNames, const bool isSorted) { if (initializedFlag == false) initialize(); allNames.clear(); - for (std::vector::iterator iter = enumData.begin(); + for (std::vector::iterator iter = enumData.begin(); iter != enumData.end(); iter++) { - allNames.push_back(WuQMacroObjectTypeEnum::toName(iter->enumValue)); + allNames.push_back(WuQMacroClassTypeEnum::toName(iter->enumValue)); } if (isSorted) { @@ -428,16 +428,16 @@ WuQMacroObjectTypeEnum::getAllNames(std::vector& allNames, const bool i * If true, the names are sorted in alphabetical order. */ void -WuQMacroObjectTypeEnum::getAllGuiNames(std::vector& allGuiNames, const bool isSorted) +WuQMacroClassTypeEnum::getAllGuiNames(std::vector& allGuiNames, const bool isSorted) { if (initializedFlag == false) initialize(); allGuiNames.clear(); - for (std::vector::iterator iter = enumData.begin(); + for (std::vector::iterator iter = enumData.begin(); iter != enumData.end(); iter++) { - allGuiNames.push_back(WuQMacroObjectTypeEnum::toGuiName(iter->enumValue)); + allGuiNames.push_back(WuQMacroClassTypeEnum::toGuiName(iter->enumValue)); } if (isSorted) { @@ -454,7 +454,7 @@ WuQMacroObjectTypeEnum::getAllGuiNames(std::vector& allGuiNames, const * Name of class that is derived from Qt widget with name 'widgetName' */ void -WuQMacroObjectTypeEnum::addWidgetClassNameAlias(const QString& widgetName, +WuQMacroClassTypeEnum::addWidgetClassNameAlias(const QString& widgetName, const QString& aliasWidgetName) { s_widgetClassNameAliases.push_back(std::make_pair(widgetName, diff --git a/src/Common/WuQMacroObjectTypeEnum.h b/src/Common/WuQMacroClassTypeEnum.h similarity index 72% rename from src/Common/WuQMacroObjectTypeEnum.h rename to src/Common/WuQMacroClassTypeEnum.h index 3b4977830..79b1a7bbd 100644 --- a/src/Common/WuQMacroObjectTypeEnum.h +++ b/src/Common/WuQMacroClassTypeEnum.h @@ -1,5 +1,5 @@ -#ifndef __WU_Q_MACRO_OBJECT_TYPE_ENUM_H__ -#define __WU_Q_MACRO_OBJECT_TYPE_ENUM_H__ +#ifndef __WU_Q_MACRO_CLASS_TYPE_ENUM_H__ +#define __WU_Q_MACRO_CLASS_TYPE_ENUM_H__ /*LICENSE_START*/ /* @@ -28,7 +28,7 @@ namespace caret { -class WuQMacroObjectTypeEnum { +class WuQMacroClassTypeEnum { public: /** @@ -75,42 +75,42 @@ public: /* switch (m_objectType) { - case WuQMacroObjectTypeEnum::ACTION: + case WuQMacroClassTypeEnum::ACTION: break; - case WuQMacroObjectTypeEnum::CHECK_BOX: + case WuQMacroClassTypeEnum::CHECK_BOX: break; - case WuQMacroObjectTypeEnum::COMBO_BOX: + case WuQMacroClassTypeEnum::COMBO_BOX: break; - case WuQMacroObjectTypeEnum::DOUBLE_SPIN_BOX: + case WuQMacroClassTypeEnum::DOUBLE_SPIN_BOX: break; - case WuQMacroObjectTypeEnum::INVALID: + case WuQMacroClassTypeEnum::INVALID: break; - case WuQMacroObjectTypeEnum::LINE_EDIT: + case WuQMacroClassTypeEnum::LINE_EDIT: break; - case WuQMacroObjectTypeEnum::LIST_WIDGET: + case WuQMacroClassTypeEnum::LIST_WIDGET: break; - case WuQMacroObjectTypeEnum::MENU: + case WuQMacroClassTypeEnum::MENU: break; - case WuQMacroObjectTypeEnum::MOUSE_USER_EVENT: + case WuQMacroClassTypeEnum::MOUSE_USER_EVENT: break; - case WuQMacroObjectTypeEnum::PUSH_BUTTON: + case WuQMacroClassTypeEnum::PUSH_BUTTON: break; - case WuQMacroObjectTypeEnum::RADIO_BUTTON: + case WuQMacroClassTypeEnum::RADIO_BUTTON: break; - case WuQMacroObjectTypeEnum::SLIDER: + case WuQMacroClassTypeEnum::SLIDER: break; - case WuQMacroObjectTypeEnum::SPIN_BOX: + case WuQMacroClassTypeEnum::SPIN_BOX: break; - case WuQMacroObjectTypeEnum::TAB_BAR: + case WuQMacroClassTypeEnum::TAB_BAR: break; - case WuQMacroObjectTypeEnum::TAB_WIDGET: + case WuQMacroClassTypeEnum::TAB_WIDGET: break; - case WuQMacroObjectTypeEnum::TOOL_BUTTON: + case WuQMacroClassTypeEnum::TOOL_BUTTON: break; } */ - ~WuQMacroObjectTypeEnum(); + ~WuQMacroClassTypeEnum(); static AString toName(Enum enumValue); @@ -134,14 +134,14 @@ public: const QString& aliasWidgetName); private: - WuQMacroObjectTypeEnum(const Enum enumValue, + WuQMacroClassTypeEnum(const Enum enumValue, const AString& name, const AString& guiName); - static const WuQMacroObjectTypeEnum* findData(const Enum enumValue); + static const WuQMacroClassTypeEnum* findData(const Enum enumValue); /** Holds all instance of enum values and associated metadata */ - static std::vector enumData; + static std::vector enumData; /** Initialize instances that contain the enum values and metadata */ static void initialize(); @@ -168,12 +168,12 @@ private: static std::vector> s_widgetClassNameAliases; }; -#ifdef __WU_Q_MACRO_OBJECT_TYPE_ENUM_DECLARE__ - std::vector WuQMacroObjectTypeEnum::enumData; - bool WuQMacroObjectTypeEnum::initializedFlag = false; - int32_t WuQMacroObjectTypeEnum::integerCodeCounter = 0; - std::vector> WuQMacroObjectTypeEnum::s_widgetClassNameAliases; -#endif // __WU_Q_MACRO_OBJECT_TYPE_ENUM_DECLARE__ +#ifdef __WU_Q_MACRO_CLASS_TYPE_ENUM_DECLARE__ + std::vector WuQMacroClassTypeEnum::enumData; + bool WuQMacroClassTypeEnum::initializedFlag = false; + int32_t WuQMacroClassTypeEnum::integerCodeCounter = 0; + std::vector> WuQMacroClassTypeEnum::s_widgetClassNameAliases; +#endif // __WU_Q_MACRO_CLASS_TYPE_ENUM_DECLARE__ } // namespace -#endif //__WU_Q_MACRO_OBJECT_TYPE_ENUM_H__ +#endif //__WU_Q_MACRO_CLASS_TYPE_ENUM_H__ diff --git a/src/Common/WuQMacroCommand.cxx b/src/Common/WuQMacroCommand.cxx index d6d7254d7..421e4411f 100644 --- a/src/Common/WuQMacroCommand.cxx +++ b/src/Common/WuQMacroCommand.cxx @@ -37,80 +37,90 @@ using namespace caret; /** * Constructor for a macro command * - * @param objectType - * Type of object + * @param classType + * Type of object class * @param objectName * Name of object - * @param objectToolTip - * Tooltip of object - * @param value - * Value for the command. + * @param dataValue + * Data value for the command. + * @param dataValueTwo + * Second Data value for the command. */ -WuQMacroCommand::WuQMacroCommand(const WuQMacroObjectTypeEnum::Enum objectType, +WuQMacroCommand::WuQMacroCommand(const WuQMacroClassTypeEnum::Enum classType, const QString& objectName, - const QString& objectToolTip, - const QVariant value) + const QVariant dataValue, + const QVariant dataValueTwo) : CaretObjectTracksModification(), -m_objectType(objectType), +m_classType(classType), m_objectName(objectName), -m_objectToolTip(objectToolTip), -m_value(value), +m_dataValue(dataValue), +m_dataValueTwo(dataValueTwo), m_macroMouseEvent(NULL) { - switch (m_objectType) { - case WuQMacroObjectTypeEnum::ACTION: - m_objectDataValueType = WuQMacroDataValueTypeEnum::BOOLEAN; + /* + * Second data value is used by only some commands + */ + m_dataTypeTwo = WuQMacroDataValueTypeEnum::STRING; + + switch (m_classType) { + case WuQMacroClassTypeEnum::ACTION: + m_dataType = WuQMacroDataValueTypeEnum::BOOLEAN; break; - case WuQMacroObjectTypeEnum::ACTION_GROUP: - m_objectDataValueType = WuQMacroDataValueTypeEnum::STRING; + case WuQMacroClassTypeEnum::ACTION_GROUP: + m_dataType = WuQMacroDataValueTypeEnum::INTEGER; + m_dataTypeTwo = WuQMacroDataValueTypeEnum::STRING; break; - case WuQMacroObjectTypeEnum::BUTTON_GROUP: - m_objectDataValueType = WuQMacroDataValueTypeEnum::STRING; + case WuQMacroClassTypeEnum::BUTTON_GROUP: + m_dataType = WuQMacroDataValueTypeEnum::INTEGER; + m_dataTypeTwo = WuQMacroDataValueTypeEnum::STRING; break; - case WuQMacroObjectTypeEnum::CHECK_BOX: - m_objectDataValueType = WuQMacroDataValueTypeEnum::BOOLEAN; + case WuQMacroClassTypeEnum::CHECK_BOX: + m_dataType = WuQMacroDataValueTypeEnum::BOOLEAN; break; - case WuQMacroObjectTypeEnum::COMBO_BOX: - m_objectDataValueType = WuQMacroDataValueTypeEnum::INTEGER; + case WuQMacroClassTypeEnum::COMBO_BOX: + m_dataType = WuQMacroDataValueTypeEnum::INTEGER; + m_dataTypeTwo = WuQMacroDataValueTypeEnum::STRING; break; - case WuQMacroObjectTypeEnum::DOUBLE_SPIN_BOX: - m_objectDataValueType = WuQMacroDataValueTypeEnum::FLOAT; + case WuQMacroClassTypeEnum::DOUBLE_SPIN_BOX: + m_dataType = WuQMacroDataValueTypeEnum::FLOAT; break; - case WuQMacroObjectTypeEnum::INVALID: + case WuQMacroClassTypeEnum::INVALID: break; - case WuQMacroObjectTypeEnum::LINE_EDIT: - m_objectDataValueType = WuQMacroDataValueTypeEnum::STRING; + case WuQMacroClassTypeEnum::LINE_EDIT: + m_dataType = WuQMacroDataValueTypeEnum::STRING; break; - case WuQMacroObjectTypeEnum::LIST_WIDGET: - m_objectDataValueType = WuQMacroDataValueTypeEnum::STRING; + case WuQMacroClassTypeEnum::LIST_WIDGET: + m_dataType = WuQMacroDataValueTypeEnum::INTEGER; + m_dataTypeTwo = WuQMacroDataValueTypeEnum::STRING; break; - case WuQMacroObjectTypeEnum::MENU: - m_objectDataValueType = WuQMacroDataValueTypeEnum::STRING; + case WuQMacroClassTypeEnum::MENU: + m_dataType = WuQMacroDataValueTypeEnum::INTEGER; + m_dataTypeTwo = WuQMacroDataValueTypeEnum::STRING; break; - case WuQMacroObjectTypeEnum::MOUSE_USER_EVENT: - m_objectDataValueType = WuQMacroDataValueTypeEnum::MOUSE; + case WuQMacroClassTypeEnum::MOUSE_USER_EVENT: + m_dataType = WuQMacroDataValueTypeEnum::MOUSE; CaretAssertMessage(0, "Must use constructor for mouse event"); break; - case WuQMacroObjectTypeEnum::PUSH_BUTTON: - m_objectDataValueType = WuQMacroDataValueTypeEnum::BOOLEAN; + case WuQMacroClassTypeEnum::PUSH_BUTTON: + m_dataType = WuQMacroDataValueTypeEnum::BOOLEAN; break; - case WuQMacroObjectTypeEnum::RADIO_BUTTON: - m_objectDataValueType = WuQMacroDataValueTypeEnum::BOOLEAN; + case WuQMacroClassTypeEnum::RADIO_BUTTON: + m_dataType = WuQMacroDataValueTypeEnum::BOOLEAN; break; - case WuQMacroObjectTypeEnum::SLIDER: - m_objectDataValueType = WuQMacroDataValueTypeEnum::INTEGER; + case WuQMacroClassTypeEnum::SLIDER: + m_dataType = WuQMacroDataValueTypeEnum::INTEGER; break; - case WuQMacroObjectTypeEnum::SPIN_BOX: - m_objectDataValueType = WuQMacroDataValueTypeEnum::INTEGER; + case WuQMacroClassTypeEnum::SPIN_BOX: + m_dataType = WuQMacroDataValueTypeEnum::INTEGER; break; - case WuQMacroObjectTypeEnum::TAB_BAR: - m_objectDataValueType = WuQMacroDataValueTypeEnum::INTEGER; + case WuQMacroClassTypeEnum::TAB_BAR: + m_dataType = WuQMacroDataValueTypeEnum::INTEGER; break; - case WuQMacroObjectTypeEnum::TAB_WIDGET: - m_objectDataValueType = WuQMacroDataValueTypeEnum::INTEGER; + case WuQMacroClassTypeEnum::TAB_WIDGET: + m_dataType = WuQMacroDataValueTypeEnum::INTEGER; break; - case WuQMacroObjectTypeEnum::TOOL_BUTTON: - m_objectDataValueType = WuQMacroDataValueTypeEnum::BOOLEAN; + case WuQMacroClassTypeEnum::TOOL_BUTTON: + m_dataType = WuQMacroDataValueTypeEnum::BOOLEAN; break; } @@ -124,22 +134,19 @@ m_macroMouseEvent(NULL) * Type of object * @param objectName * Name of object - * @param objectToolTip - * Tooltip of object * @param value * Value for the command. */ WuQMacroCommand::WuQMacroCommand(const QString& objectName, - const QString& objectToolTip, WuQMacroMouseEventInfo* mouseEventInfo) : CaretObjectTracksModification(), -m_objectType(WuQMacroObjectTypeEnum::MOUSE_USER_EVENT), +m_classType(WuQMacroClassTypeEnum::MOUSE_USER_EVENT), m_objectName(objectName), -m_objectToolTip(objectToolTip), -m_value((int)0), +m_dataValue((int)0), +m_dataValueTwo(""), m_macroMouseEvent(mouseEventInfo) { - m_objectDataValueType = WuQMacroDataValueTypeEnum::MOUSE; + m_dataType = WuQMacroDataValueTypeEnum::MOUSE; setModified(); } @@ -190,14 +197,15 @@ WuQMacroCommand::operator=(const WuQMacroCommand& obj) void WuQMacroCommand::copyHelperWuQMacroCommand(const WuQMacroCommand& obj) { - m_objectType = obj.m_objectType; - m_objectName = obj.m_objectName; - m_objectToolTip = obj.m_objectToolTip; - m_value = obj.m_value; + m_classType = obj.m_classType; + m_objectName = obj.m_objectName; + m_dataValue = obj.m_dataValue; + m_dataType = obj.m_dataType; + m_dataTypeTwo = obj.m_dataTypeTwo; + m_dataValueTwo = obj.m_dataValueTwo; m_macroMouseEvent = NULL; - m_objectDataValueType = obj.m_objectDataValueType; - switch (m_objectDataValueType) { + switch (m_dataType) { case WuQMacroDataValueTypeEnum::BOOLEAN: break; case WuQMacroDataValueTypeEnum::FLOAT: @@ -214,21 +222,30 @@ WuQMacroCommand::copyHelperWuQMacroCommand(const WuQMacroCommand& obj) } /** - * @return The object' type + * @return The object' class type */ -WuQMacroObjectTypeEnum::Enum -WuQMacroCommand::getObjectType() const +WuQMacroClassTypeEnum::Enum +WuQMacroCommand::getClassType() const { - return m_objectType; + return m_classType; } /** * @return Type of data value for object */ WuQMacroDataValueTypeEnum::Enum -WuQMacroCommand::getObjectDataValueType() const +WuQMacroCommand::getDataType() const { - return m_objectDataValueType; + return m_dataType; +} + +/** + * @return Type of second data value for object + */ +WuQMacroDataValueTypeEnum::Enum +WuQMacroCommand::getDataTypeTwo() const +{ + return m_dataTypeTwo; } /** @@ -241,21 +258,21 @@ WuQMacroCommand::getObjectName() const } /** - * @return The object's tooltip + * @return The object's data value. */ -QString -WuQMacroCommand::getObjectToolTip() const +QVariant +WuQMacroCommand::getDataValue() const { - return m_objectToolTip; + return m_dataValue; } /** - * @return The object's value. + * @return The object's second data value. */ QVariant -WuQMacroCommand::getObjectValue() const +WuQMacroCommand::getDataValueTwo() const { - return m_value; + return m_dataValueTwo; } /** @@ -274,11 +291,11 @@ WuQMacroCommand::getMouseEventInfo() const AString WuQMacroCommand::toString() const { - QString s("WuQMacroCommand name=%1, type=%2, tooltip=%3, value=%4"); + QString s("WuQMacroCommand name=%1, type=%2, value=%3, valueTwo=%4"); s = s.arg(m_objectName - ).arg(WuQMacroObjectTypeEnum::toGuiName(m_objectType) - ).arg(m_objectToolTip - ).arg(m_value.toString()); + ).arg(WuQMacroClassTypeEnum::toGuiName(m_classType) + ).arg(m_dataValue.toString() + ).arg(m_dataValueTwo.toString()); return s; } diff --git a/src/Common/WuQMacroCommand.h b/src/Common/WuQMacroCommand.h index fe3f11ace..8d19a6c1b 100644 --- a/src/Common/WuQMacroCommand.h +++ b/src/Common/WuQMacroCommand.h @@ -29,7 +29,7 @@ #include "CaretObjectTracksModification.h" #include "WuQMacroDataValueTypeEnum.h" -#include "WuQMacroObjectTypeEnum.h" +#include "WuQMacroClassTypeEnum.h" class QObject; @@ -41,13 +41,12 @@ namespace caret { public: - WuQMacroCommand(const WuQMacroObjectTypeEnum::Enum objectType, - const QString& objectName, - const QString& objectToolTip, - const QVariant value); + WuQMacroCommand(const WuQMacroClassTypeEnum::Enum objectClassType, + const QString& objectClassName, + const QVariant dataValue, + const QVariant dataValueTwo); - WuQMacroCommand(const QString& objectName, - const QString& objectToolTip, + WuQMacroCommand(const QString& objectClassName, WuQMacroMouseEventInfo* mouseEventInfo); virtual ~WuQMacroCommand(); @@ -56,16 +55,18 @@ namespace caret { WuQMacroCommand& operator=(const WuQMacroCommand& obj); - WuQMacroObjectTypeEnum::Enum getObjectType() const; - - WuQMacroDataValueTypeEnum::Enum getObjectDataValueType() const; + WuQMacroClassTypeEnum::Enum getClassType() const; QString getObjectName() const; - QString getObjectToolTip() const; + WuQMacroDataValueTypeEnum::Enum getDataType() const; - QVariant getObjectValue() const; + QVariant getDataValue() const; + WuQMacroDataValueTypeEnum::Enum getDataTypeTwo() const; + + QVariant getDataValueTwo() const; + const WuQMacroMouseEventInfo* getMouseEventInfo() const; // ADD_NEW_METHODS_HERE @@ -75,17 +76,19 @@ namespace caret { private: void copyHelperWuQMacroCommand(const WuQMacroCommand& obj); - WuQMacroObjectTypeEnum::Enum m_objectType; + WuQMacroClassTypeEnum::Enum m_classType; QString m_objectName; - QString m_objectToolTip; + QVariant m_dataValue; - QVariant m_value; + WuQMacroDataValueTypeEnum::Enum m_dataType; - WuQMacroMouseEventInfo* m_macroMouseEvent; + QVariant m_dataValueTwo; + + WuQMacroDataValueTypeEnum::Enum m_dataTypeTwo; - WuQMacroDataValueTypeEnum::Enum m_objectDataValueType; + WuQMacroMouseEventInfo* m_macroMouseEvent; // ADD_NEW_MEMBERS_HERE diff --git a/src/Common/WuQMacroGroupXmlFormatBase.h b/src/Common/WuQMacroGroupXmlFormatBase.h index 23e7c7c77..c6d5afe64 100644 --- a/src/Common/WuQMacroGroupXmlFormatBase.h +++ b/src/Common/WuQMacroGroupXmlFormatBase.h @@ -51,10 +51,12 @@ namespace caret { protected: static const QString ATTRIBUTE_NAME; + static const QString ATTRIBUTE_OBJECT_CLASS; + static const QString ATTRIBUTE_OBJECT_DATA_TYPE; + static const QString ATTRIBUTE_OBJECT_DATA_TYPE_TWO; static const QString ATTRIBUTE_FUNCTION_KEY; - static const QString ATTRIBUTE_OBJECT_DATA_VALUE_TYPE; - static const QString ATTRIBUTE_OBJECT_TYPE; static const QString ATTRIBUTE_OBJECT_VALUE; + static const QString ATTRIBUTE_OBJECT_VALUE_TWO; static const QString ATTRIBUTE_VERSION; static const QString ATTRIBUTE_MOUSE_BUTTON; @@ -88,10 +90,12 @@ namespace caret { #ifdef __WU_Q_MACRO_GROUP_XML_FORMAT_BASE_DECLARE__ const QString WuQMacroGroupXmlFormatBase::ATTRIBUTE_NAME = "Name"; + const QString WuQMacroGroupXmlFormatBase::ATTRIBUTE_OBJECT_CLASS = "ObjectClass"; + const QString WuQMacroGroupXmlFormatBase::ATTRIBUTE_OBJECT_DATA_TYPE = "DataType"; + const QString WuQMacroGroupXmlFormatBase::ATTRIBUTE_OBJECT_DATA_TYPE_TWO = "DataTypeTwo"; const QString WuQMacroGroupXmlFormatBase::ATTRIBUTE_FUNCTION_KEY = "FunctionKey"; - const QString WuQMacroGroupXmlFormatBase::ATTRIBUTE_OBJECT_DATA_VALUE_TYPE = "ValueType"; - const QString WuQMacroGroupXmlFormatBase::ATTRIBUTE_OBJECT_TYPE = "ObjectType"; - const QString WuQMacroGroupXmlFormatBase::ATTRIBUTE_OBJECT_VALUE = "Value"; + const QString WuQMacroGroupXmlFormatBase::ATTRIBUTE_OBJECT_VALUE = "DataValue"; + const QString WuQMacroGroupXmlFormatBase::ATTRIBUTE_OBJECT_VALUE_TWO = "DataValueTwo"; const QString WuQMacroGroupXmlFormatBase::ATTRIBUTE_VERSION = "Version"; const QString WuQMacroGroupXmlFormatBase::ATTRIBUTE_MOUSE_BUTTON = "MouseButton"; @@ -104,8 +108,8 @@ namespace caret { const QString WuQMacroGroupXmlFormatBase::ATTRIBUTE_MOUSE_SCREEN_Y = "ScreenY"; const QString WuQMacroGroupXmlFormatBase::ATTRIBUTE_MOUSE_WIDGET_WIDTH = "WidgetWidth"; const QString WuQMacroGroupXmlFormatBase::ATTRIBUTE_MOUSE_WIDGET_HEIGHT = "WidgetHeight"; - const QString WuQMacroGroupXmlFormatBase::ATTRIBUTE_MOUSE_WINDOW_X = "WINDOW_X"; - const QString WuQMacroGroupXmlFormatBase::ATTRIBUTE_MOUSE_WINDOW_Y = "WINDOW_Y"; + const QString WuQMacroGroupXmlFormatBase::ATTRIBUTE_MOUSE_WINDOW_X = "WindowX"; + const QString WuQMacroGroupXmlFormatBase::ATTRIBUTE_MOUSE_WINDOW_Y = "WindowY"; const QString WuQMacroGroupXmlFormatBase::ELEMENT_DESCRIPTION = "Description"; const QString WuQMacroGroupXmlFormatBase::ELEMENT_MACRO = "Macro"; diff --git a/src/Common/WuQMacroGroupXmlReader.cxx b/src/Common/WuQMacroGroupXmlReader.cxx index 302b2e60e..0424b35be 100644 --- a/src/Common/WuQMacroGroupXmlReader.cxx +++ b/src/Common/WuQMacroGroupXmlReader.cxx @@ -226,15 +226,18 @@ WuQMacroGroupXmlReader::readMacroCommandVersionOne() const QXmlStreamAttributes attributes = m_xmlStreamReader->attributes(); const QStringRef objectName = attributes.value(ATTRIBUTE_NAME); - const QStringRef dataTypeString = attributes.value(ATTRIBUTE_OBJECT_DATA_VALUE_TYPE); - const QStringRef typeString = attributes.value(ATTRIBUTE_OBJECT_TYPE); + const QStringRef dataTypeString = attributes.value(ATTRIBUTE_OBJECT_DATA_TYPE); + const QStringRef classString = attributes.value(ATTRIBUTE_OBJECT_CLASS); const QStringRef valueString = attributes.value(ATTRIBUTE_OBJECT_VALUE); + const QStringRef dataTypeTwoString = attributes.value(ATTRIBUTE_OBJECT_DATA_TYPE_TWO); + const QStringRef valueTwoString = attributes.value(ATTRIBUTE_OBJECT_VALUE_TWO); QString es; if (objectName.isEmpty()) es.append(ATTRIBUTE_NAME + " "); - if (dataTypeString.isEmpty()) es.append(ATTRIBUTE_OBJECT_DATA_VALUE_TYPE + " "); - if (typeString.isEmpty()) es.append(ATTRIBUTE_OBJECT_TYPE + " "); - if (valueString.isEmpty()) es.append(ATTRIBUTE_OBJECT_VALUE + " "); + if (dataTypeString.isEmpty()) es.append(ATTRIBUTE_OBJECT_DATA_TYPE + " "); + if (dataTypeTwoString.isEmpty()) es.append(ATTRIBUTE_OBJECT_DATA_TYPE + " "); + if (classString.isEmpty()) es.append(ATTRIBUTE_OBJECT_CLASS + " "); +// empty could be valid for line edit if (valueString.isEmpty()) es.append(ATTRIBUTE_OBJECT_VALUE + " "); if ( ! es.isEmpty()) { addToWarnings(ELEMENT_MACRO_COMMAND + " is missing attribute(s): " @@ -242,26 +245,41 @@ WuQMacroGroupXmlReader::readMacroCommandVersionOne() return NULL; } + + bool objectTypeValid(false); + WuQMacroClassTypeEnum::Enum objectClass = WuQMacroClassTypeEnum::fromName(classString.toString(), + &objectTypeValid); + if (! objectTypeValid) { + es.append(classString.toString() + + " is not valid for attribute " + + ATTRIBUTE_OBJECT_CLASS + + " "); + return NULL; + } + bool dataTypeValid(false); WuQMacroDataValueTypeEnum::Enum dataValueType = WuQMacroDataValueTypeEnum::fromName(dataTypeString.toString(), &dataTypeValid); - if ( ! dataTypeValid) { + if (! dataTypeValid) { es.append(dataTypeString.toString() + " is not valid for attribute " - + ATTRIBUTE_OBJECT_DATA_VALUE_TYPE + + ATTRIBUTE_OBJECT_DATA_TYPE + " "); + return NULL; } - bool objectTypeValid(false); - WuQMacroObjectTypeEnum::Enum objectType = WuQMacroObjectTypeEnum::fromName(typeString.toString(), - &objectTypeValid); - if ( ! objectTypeValid) { - es.append(typeString.toString() + bool dataTypeTwoValid(false); + WuQMacroDataValueTypeEnum::Enum dataValueTypeTwo = WuQMacroDataValueTypeEnum::fromName(dataTypeTwoString.toString(), + &dataTypeTwoValid); + if (! dataTypeTwoValid) { + es.append(dataTypeTwoString.toString() + " is not valid for attribute " - + ATTRIBUTE_OBJECT_TYPE + + ATTRIBUTE_OBJECT_DATA_TYPE_TWO + " "); + return NULL; } + if ( ! es.isEmpty()) { addToWarnings(ELEMENT_MACRO_COMMAND + " has invalid attributes: " @@ -269,11 +287,10 @@ WuQMacroGroupXmlReader::readMacroCommandVersionOne() return NULL; } - if (objectType == WuQMacroObjectTypeEnum::MOUSE_USER_EVENT) { + if (objectClass == WuQMacroClassTypeEnum::MOUSE_USER_EVENT) { WuQMacroMouseEventInfo* mouseEventInfo = readMacroMouseEventInfo(); if (mouseEventInfo != NULL) { macroCommand = new WuQMacroCommand(objectName.toString(), - "", mouseEventInfo); } } @@ -305,10 +322,39 @@ WuQMacroGroupXmlReader::readMacroCommandVersionOne() value.setValue(valueString.toString()); break; } - macroCommand = new WuQMacroCommand(objectType, + + QVariant valueTwo; + switch (dataValueTypeTwo) { + case WuQMacroDataValueTypeEnum::BOOLEAN: + { + const bool boolValue = ((valueTwoString == VALUE_BOOL_TRUE) ? true : false); + valueTwo.setValue(boolValue); + } + break; + case WuQMacroDataValueTypeEnum::FLOAT: + { + const double floatValue = valueTwoString.toFloat(); + valueTwo.setValue(floatValue); + } + break; + case WuQMacroDataValueTypeEnum::INTEGER: + { + const int32_t intValue = valueTwoString.toInt(); + valueTwo.setValue(intValue); + } + break; + case WuQMacroDataValueTypeEnum::MOUSE: + CaretAssertMessage(0, "Mouse is special case handled above"); + break; + case WuQMacroDataValueTypeEnum::STRING: + valueTwo.setValue(valueTwoString.toString()); + break; + } + + macroCommand = new WuQMacroCommand(objectClass, objectName.toString(), - "", - value); + value, + valueTwo); } return macroCommand; diff --git a/src/Common/WuQMacroGroupXmlWriter.cxx b/src/Common/WuQMacroGroupXmlWriter.cxx index 3a6fdd231..e03b353a5 100644 --- a/src/Common/WuQMacroGroupXmlWriter.cxx +++ b/src/Common/WuQMacroGroupXmlWriter.cxx @@ -132,70 +132,100 @@ WuQMacroGroupXmlWriter::writeMacroCommandToXML(const WuQMacroCommand* macroComma m_xmlStreamWriter->writeStartElement(ELEMENT_MACRO_COMMAND); m_xmlStreamWriter->writeAttribute(ATTRIBUTE_NAME, macroCommand->getObjectName()); - m_xmlStreamWriter->writeAttribute(ATTRIBUTE_OBJECT_DATA_VALUE_TYPE, - WuQMacroDataValueTypeEnum::toName(macroCommand->getObjectDataValueType())); - m_xmlStreamWriter->writeAttribute(ATTRIBUTE_OBJECT_TYPE, - WuQMacroObjectTypeEnum::toName(macroCommand->getObjectType())); - const QVariant objectValue(macroCommand->getObjectValue()); - QString stringValue; - switch (macroCommand->getObjectDataValueType()) { - case WuQMacroDataValueTypeEnum::BOOLEAN: - stringValue = (objectValue.toBool() ? VALUE_BOOL_TRUE : VALUE_BOOL_FALSE); - break; - case WuQMacroDataValueTypeEnum::FLOAT: - stringValue = QString::number(objectValue.toFloat()); - break; - case WuQMacroDataValueTypeEnum::INTEGER: - stringValue = QString::number(objectValue.toInt()); - break; - case WuQMacroDataValueTypeEnum::MOUSE: - stringValue = "MouseEvent"; - break; - case WuQMacroDataValueTypeEnum::STRING: - stringValue = objectValue.toString(); - break; + m_xmlStreamWriter->writeAttribute(ATTRIBUTE_OBJECT_CLASS, + WuQMacroClassTypeEnum::toName(macroCommand->getClassType())); + { + m_xmlStreamWriter->writeAttribute(ATTRIBUTE_OBJECT_DATA_TYPE, + WuQMacroDataValueTypeEnum::toName(macroCommand->getDataType())); + + const QVariant dataValue(macroCommand->getDataValue()); + QString stringValue; + switch (macroCommand->getDataType()) { + case WuQMacroDataValueTypeEnum::BOOLEAN: + stringValue = (dataValue.toBool() ? VALUE_BOOL_TRUE : VALUE_BOOL_FALSE); + break; + case WuQMacroDataValueTypeEnum::FLOAT: + stringValue = QString::number(dataValue.toFloat()); + break; + case WuQMacroDataValueTypeEnum::INTEGER: + stringValue = QString::number(dataValue.toInt()); + break; + case WuQMacroDataValueTypeEnum::MOUSE: + stringValue = "MouseEvent"; + break; + case WuQMacroDataValueTypeEnum::STRING: + stringValue = dataValue.toString(); + break; + } + m_xmlStreamWriter->writeAttribute(ATTRIBUTE_OBJECT_VALUE, + stringValue); + } + + { + m_xmlStreamWriter->writeAttribute(ATTRIBUTE_OBJECT_DATA_TYPE_TWO, + WuQMacroDataValueTypeEnum::toName(macroCommand->getDataTypeTwo())); + + const QVariant dataValueTwo(macroCommand->getDataValueTwo()); + QString stringValue; + switch (macroCommand->getDataTypeTwo()) { + case WuQMacroDataValueTypeEnum::BOOLEAN: + stringValue = (dataValueTwo.toBool() ? VALUE_BOOL_TRUE : VALUE_BOOL_FALSE); + break; + case WuQMacroDataValueTypeEnum::FLOAT: + stringValue = QString::number(dataValueTwo.toFloat()); + break; + case WuQMacroDataValueTypeEnum::INTEGER: + stringValue = QString::number(dataValueTwo.toInt()); + break; + case WuQMacroDataValueTypeEnum::MOUSE: + stringValue = "MouseEvent"; + break; + case WuQMacroDataValueTypeEnum::STRING: + stringValue = dataValueTwo.toString(); + break; + } + m_xmlStreamWriter->writeAttribute(ATTRIBUTE_OBJECT_VALUE_TWO, + stringValue); } - m_xmlStreamWriter->writeAttribute(ATTRIBUTE_OBJECT_VALUE, - stringValue); - switch (macroCommand->getObjectType()) { - case WuQMacroObjectTypeEnum::ACTION: + switch (macroCommand->getClassType()) { + case WuQMacroClassTypeEnum::ACTION: break; - case WuQMacroObjectTypeEnum::ACTION_GROUP: + case WuQMacroClassTypeEnum::ACTION_GROUP: break; - case WuQMacroObjectTypeEnum::BUTTON_GROUP: + case WuQMacroClassTypeEnum::BUTTON_GROUP: break; - case WuQMacroObjectTypeEnum::CHECK_BOX: + case WuQMacroClassTypeEnum::CHECK_BOX: break; - case WuQMacroObjectTypeEnum::COMBO_BOX: + case WuQMacroClassTypeEnum::COMBO_BOX: break; - case WuQMacroObjectTypeEnum::DOUBLE_SPIN_BOX: + case WuQMacroClassTypeEnum::DOUBLE_SPIN_BOX: break; - case WuQMacroObjectTypeEnum::INVALID: + case WuQMacroClassTypeEnum::INVALID: break; - case WuQMacroObjectTypeEnum::LINE_EDIT: + case WuQMacroClassTypeEnum::LINE_EDIT: break; - case WuQMacroObjectTypeEnum::LIST_WIDGET: + case WuQMacroClassTypeEnum::LIST_WIDGET: break; - case WuQMacroObjectTypeEnum::MENU: + case WuQMacroClassTypeEnum::MENU: break; - case WuQMacroObjectTypeEnum::MOUSE_USER_EVENT: + case WuQMacroClassTypeEnum::MOUSE_USER_EVENT: writeMacroMouseEventInfo(macroCommand->getMouseEventInfo()); break; - case WuQMacroObjectTypeEnum::PUSH_BUTTON: + case WuQMacroClassTypeEnum::PUSH_BUTTON: break; - case WuQMacroObjectTypeEnum::RADIO_BUTTON: + case WuQMacroClassTypeEnum::RADIO_BUTTON: break; - case WuQMacroObjectTypeEnum::SLIDER: + case WuQMacroClassTypeEnum::SLIDER: break; - case WuQMacroObjectTypeEnum::SPIN_BOX: + case WuQMacroClassTypeEnum::SPIN_BOX: break; - case WuQMacroObjectTypeEnum::TAB_BAR: + case WuQMacroClassTypeEnum::TAB_BAR: break; - case WuQMacroObjectTypeEnum::TAB_WIDGET: + case WuQMacroClassTypeEnum::TAB_WIDGET: break; - case WuQMacroObjectTypeEnum::TOOL_BUTTON: + case WuQMacroClassTypeEnum::TOOL_BUTTON: break; } diff --git a/src/GuiQt/GuiManager.cxx b/src/GuiQt/GuiManager.cxx index 1fbff4486..599f41628 100644 --- a/src/GuiQt/GuiManager.cxx +++ b/src/GuiQt/GuiManager.cxx @@ -122,7 +122,7 @@ #include "TileTabsConfigurationDialog.h" #include "VolumeMappableInterface.h" #include "WuQMacroManager.h" -#include "WuQMacroObjectTypeEnum.h" +#include "WuQMacroClassTypeEnum.h" #include "WuQMessageBox.h" #include "WuQtUtilities.h" @@ -192,9 +192,9 @@ GuiManager::initializeGuiManager() * of GuiManager and not found when searching a window * for children objects */ - WuQMacroObjectTypeEnum::addWidgetClassNameAlias("QTabWidget", + WuQMacroClassTypeEnum::addWidgetClassNameAlias("QTabWidget", "caret::WuQTabWidgetWithSizeHint"); - WuQMacroObjectTypeEnum::addWidgetClassNameAlias("QTabBar", + WuQMacroClassTypeEnum::addWidgetClassNameAlias("QTabBar", "caret::WuQTabBar"); WuQMacroManager::instance()->addParentObject(this); diff --git a/src/GuiQt/WuQMacroDialog.cxx b/src/GuiQt/WuQMacroDialog.cxx index 78a5faf18..df4ab6e4f 100644 --- a/src/GuiQt/WuQMacroDialog.cxx +++ b/src/GuiQt/WuQMacroDialog.cxx @@ -266,8 +266,13 @@ WuQMacroDialog::updateDialogContents() void WuQMacroDialog::macroGroupBoxActivated(int) { + const QString emptySuffix(" (Empty)"); QListWidgetItem* selectedItem = m_macrosListWidget->currentItem(); QString selectedName = ((selectedItem != NULL) ? selectedItem->text() : ""); + const int emptyIndex = selectedName.indexOf(emptySuffix); + if (emptyIndex >= 0) { + selectedName = selectedName.left(emptyIndex); + } m_macrosListWidget->clear(); @@ -275,11 +280,14 @@ WuQMacroDialog::macroGroupBoxActivated(int) WuQMacroGroup* selectedGroup = getSelectedMacroGroup(); const int32_t numMacros = selectedGroup->getNumberOfMacros(); for (int32_t i = 0; i < numMacros; i++) { - const QString macroName = selectedGroup->getMacroAtIndex(i)->getName(); - m_macrosListWidget->addItem(macroName); + QString macroName = selectedGroup->getMacroAtIndex(i)->getName(); if (selectedName == macroName) { selectedIndex = i; } + if (selectedGroup->getMacroAtIndex(i)->getNumberOfMacroCommands() <= 0) { + macroName.append(emptySuffix); + } + m_macrosListWidget->addItem(macroName); } if ((selectedIndex >= 0) @@ -360,6 +368,15 @@ WuQMacroDialog::runSelectedMacro() { WuQMacro* macro = getSelectedMacro(); if (macro != NULL) { + if (macro->getNumberOfMacroCommands() <= 0) { + QMessageBox::critical(m_runButton, + "Error", + "Macro does not contain any commands", + QMessageBox::Ok, + QMessageBox::NoButton); + return; + } + WuQMacroExecutor::RunOptions runOptions(m_runOptionDelayBetweenCommandsSpinBox->value(), m_runOptionMoveMouseCheckBox->isChecked(), m_runOptionLoopCheckBox->isChecked()); diff --git a/src/GuiQt/WuQMacroEditorDialog.cxx b/src/GuiQt/WuQMacroEditorDialog.cxx index 81d683b04..9d108a98c 100644 --- a/src/GuiQt/WuQMacroEditorDialog.cxx +++ b/src/GuiQt/WuQMacroEditorDialog.cxx @@ -139,6 +139,8 @@ WuQMacroEditorDialog::createTableWidget() columnNames << "Name"; CaretAssert(COLUMN_VALUE == 3); columnNames << "Value"; + CaretAssert(COLUMN_TOOLTIP == 4); + columnNames << "ToolTip"; CaretAssert(columnNames.size() == NUMBER_OF_COLUMNS); tableWidget->setHorizontalHeaderLabels(columnNames); @@ -160,12 +162,15 @@ WuQMacroEditorDialog::loadTableWidget() for (int32_t i = 0; i < numberOfCommands; i++) { const WuQMacroCommand* command = m_macro->getMacroCommandAtIndex(i); - QTableWidgetItem* classItem = new QTableWidgetItem(WuQMacroObjectTypeEnum::toGuiName(command->getObjectType())); - QTableWidgetItem* valueTypeItem = new QTableWidgetItem(WuQMacroDataValueTypeEnum::toGuiName(command->getObjectDataValueType())); + QTableWidgetItem* classItem = new QTableWidgetItem(WuQMacroClassTypeEnum::toGuiName(command->getClassType())); + QTableWidgetItem* valueTypeItem = new QTableWidgetItem(WuQMacroDataValueTypeEnum::toGuiName(command->getDataType())); QTableWidgetItem* nameItem = new QTableWidgetItem(command->getObjectName()); - QTableWidgetItem* valueItem = new QTableWidgetItem(command->getObjectValue().toString()); + QTableWidgetItem* valueItem = new QTableWidgetItem(command->getDataValue().toString()); - switch (command->getObjectDataValueType()) { + const QString toolTip = WuQMacroManager::instance()->getToolTipForObjectName(command->getObjectName()); + QTableWidgetItem* toolTipItem = new QTableWidgetItem(toolTip); + + switch (command->getDataType()) { case WuQMacroDataValueTypeEnum::BOOLEAN: break; case WuQMacroDataValueTypeEnum::FLOAT: @@ -183,7 +188,23 @@ WuQMacroEditorDialog::loadTableWidget() m_tableWidget->setItem(i, COLUMN_VALUE_TYPE, valueTypeItem); m_tableWidget->setItem(i, COLUMN_NAME, nameItem); m_tableWidget->setItem(i, COLUMN_VALUE, valueItem); + m_tableWidget->setItem(i, COLUMN_TOOLTIP, toolTipItem); } + + QStringList columnNames; + CaretAssert(COLUMN_TYPE == 0); + columnNames << "Class"; + CaretAssert(COLUMN_VALUE_TYPE == 1); + columnNames << "Value-Type"; + CaretAssert(COLUMN_NAME == 2); + columnNames << "Name"; + CaretAssert(COLUMN_VALUE == 3); + columnNames << "Value"; + CaretAssert(COLUMN_TOOLTIP == 4); + columnNames << "ToolTip"; + CaretAssert(columnNames.size() == NUMBER_OF_COLUMNS); + + m_tableWidget->setHorizontalHeaderLabels(columnNames); } diff --git a/src/GuiQt/WuQMacroEditorDialog.h b/src/GuiQt/WuQMacroEditorDialog.h index 73ab1289d..6c28a23a2 100644 --- a/src/GuiQt/WuQMacroEditorDialog.h +++ b/src/GuiQt/WuQMacroEditorDialog.h @@ -65,7 +65,8 @@ namespace caret { COLUMN_VALUE_TYPE = 1, COLUMN_NAME = 2, COLUMN_VALUE = 3, - NUMBER_OF_COLUMNS = 4 + COLUMN_TOOLTIP = 4, + NUMBER_OF_COLUMNS = 5 }; QTableWidget* createTableWidget(); diff --git a/src/GuiQt/WuQMacroExecutor.cxx b/src/GuiQt/WuQMacroExecutor.cxx index e69e2184c..25b602503 100644 --- a/src/GuiQt/WuQMacroExecutor.cxx +++ b/src/GuiQt/WuQMacroExecutor.cxx @@ -214,8 +214,8 @@ WuQMacroExecutor::runMacro(const WuQMacro* macro, } } - const WuQMacroObjectTypeEnum::Enum objectType = mc->getObjectType(); - const bool mouseEventFlag = (objectType == WuQMacroObjectTypeEnum::MOUSE_USER_EVENT); + const WuQMacroClassTypeEnum::Enum classType = mc->getClassType(); + const bool mouseEventFlag = (classType == WuQMacroClassTypeEnum::MOUSE_USER_EVENT); if (options.m_showMouseMovementFlag) { if (widgetToMoveMouse != NULL) { @@ -274,142 +274,285 @@ WuQMacroExecutor::runMacroCommand(const WuQMacroCommand* macroCommand, CaretAssert(macroCommand); CaretAssert(object); - const WuQMacroObjectTypeEnum::Enum objectType = macroCommand->getObjectType(); - const WuQMacroDataValueTypeEnum::Enum dataValueType = macroCommand->getObjectDataValueType(); - const QVariant objectValue = macroCommand->getObjectValue(); + const WuQMacroClassTypeEnum::Enum classType = macroCommand->getClassType(); + const WuQMacroDataValueTypeEnum::Enum dataValueType = macroCommand->getDataType(); + const QVariant dataValue = macroCommand->getDataValue(); + const WuQMacroDataValueTypeEnum::Enum dataValueTypeTwo = macroCommand->getDataTypeTwo(); + const QVariant dataValueTwo = macroCommand->getDataValueTwo(); + QString valueNotFoundErrorMessage; bool notFoundFlag(false); - switch (objectType) { - case WuQMacroObjectTypeEnum::ACTION: + switch (classType) { + case WuQMacroClassTypeEnum::ACTION: { QAction* action = qobject_cast(object); if (action != NULL) { CaretAssert(dataValueType == WuQMacroDataValueTypeEnum::BOOLEAN); WuQMacroSignalEmitter signalEmitter; signalEmitter.emitQActionSignal(action, - objectValue.toBool()); + dataValue.toBool()); } else { notFoundFlag = true; } } break; - case WuQMacroObjectTypeEnum::ACTION_GROUP: + case WuQMacroClassTypeEnum::ACTION_GROUP: { QActionGroup* actionGroup = qobject_cast(object); if (actionGroup != NULL) { - CaretAssert(dataValueType == WuQMacroDataValueTypeEnum::STRING); + CaretAssert(dataValueType == WuQMacroDataValueTypeEnum::INTEGER); + CaretAssert(dataValueTypeTwo == WuQMacroDataValueTypeEnum::STRING); + + QAction* textAction(NULL); + QAction* indexAction(NULL); + QList actions = actionGroup->actions(); + for (int32_t i = 0; i < actions.size(); i++) { + QAction* actionAtIndex = actions.at(i); + if (actionAtIndex->text() == dataValueTwo.toString()) { + textAction = actionAtIndex; + } + if (dataValue.toInt() == i) { + indexAction = actionAtIndex; + } + } + WuQMacroSignalEmitter signalEmitter; - signalEmitter.emitActionGroupSignal(actionGroup, - objectValue.toString()); + if (textAction != NULL) { + signalEmitter.emitActionGroupSignal(actionGroup, + textAction->text()); + } + else if (indexAction != NULL) { + signalEmitter.emitActionGroupSignal(actionGroup, + indexAction->text()); + } + else { + valueNotFoundErrorMessage = ("For QActionGroup \"" + + object->objectName() + + "\", unable to find action with text \"" + + dataValueTwo.toString() + + "\" or index=" + + dataValue.toInt()); + } } else { notFoundFlag = true; } } break; - case WuQMacroObjectTypeEnum::BUTTON_GROUP: + case WuQMacroClassTypeEnum::BUTTON_GROUP: { QButtonGroup* buttonGroup = qobject_cast(object); if (buttonGroup != NULL) { - CaretAssert(dataValueType == WuQMacroDataValueTypeEnum::STRING); + CaretAssert(dataValueType == WuQMacroDataValueTypeEnum::INTEGER); + CaretAssert(dataValueTypeTwo == WuQMacroDataValueTypeEnum::STRING); + + QAbstractButton* textButton(NULL); + QAbstractButton* indexButton(NULL); + QList buttons = buttonGroup->buttons(); + for (int32_t i = 0; i < buttons.size(); i++) { + QAbstractButton* buttonAtIndex = buttons.at(i); + if (buttonAtIndex->text() == dataValueTwo.toString()) { + textButton = buttonAtIndex; + } + if (dataValue.toInt() == i) { + indexButton = buttonAtIndex; + } + } + WuQMacroSignalEmitter signalEmitter; - signalEmitter.emitQButtonGroupSignal(buttonGroup, - objectValue.toString()); + if (textButton != NULL) { + signalEmitter.emitQButtonGroupSignal(buttonGroup, + textButton->text()); + } + else if (indexButton != NULL) { + signalEmitter.emitQButtonGroupSignal(buttonGroup, + indexButton->text()); + } + else { + valueNotFoundErrorMessage = ("For QButtonGroup \"" + + object->objectName() + + "\", unable to find button with text \"" + + dataValueTwo.toString() + + "\" or index=" + + dataValue.toInt()); + } } else { notFoundFlag = true; } } break; - case WuQMacroObjectTypeEnum::CHECK_BOX: + case WuQMacroClassTypeEnum::CHECK_BOX: { QCheckBox* checkBox = qobject_cast(object); if (checkBox != NULL) { CaretAssert(dataValueType == WuQMacroDataValueTypeEnum::BOOLEAN); WuQMacroSignalEmitter signalEmitter; signalEmitter.emitQCheckBoxSignal(checkBox, - objectValue.toBool()); + dataValue.toBool()); } else { notFoundFlag = true; } } break; - case WuQMacroObjectTypeEnum::COMBO_BOX: + case WuQMacroClassTypeEnum::COMBO_BOX: { QComboBox* comboBox = qobject_cast(object); if (comboBox != NULL) { - CaretAssert(dataValueType == WuQMacroDataValueTypeEnum::INTEGER); + CaretAssert(dataValueType == WuQMacroDataValueTypeEnum::INTEGER); + CaretAssert(dataValueTypeTwo == WuQMacroDataValueTypeEnum::STRING); + int textIndex(-1); + int indexIndex(-1); + for (int32_t i = 0; i < comboBox->count(); i++) { + if (comboBox->itemText(i) == dataValueTwo.toString()) { + textIndex = i; + } + else if (i == dataValue.toInt()) { + indexIndex = i; + } + } WuQMacroSignalEmitter signalEmitter; - signalEmitter.emitQComboBoxSignal(comboBox, - objectValue.toInt()); + if (textIndex >= 0) { + signalEmitter.emitQComboBoxSignal(comboBox, + textIndex); + } + else if (indexIndex >= 0) { + signalEmitter.emitQComboBoxSignal(comboBox, + indexIndex); + } + else { + valueNotFoundErrorMessage = ("For ComboBox \"" + + object->objectName() + + "\", unable to find item with text \"" + + dataValueTwo.toString() + + "\" or index=" + + dataValue.toInt()); + } } else { notFoundFlag = true; } } break; - case WuQMacroObjectTypeEnum::DOUBLE_SPIN_BOX: + case WuQMacroClassTypeEnum::DOUBLE_SPIN_BOX: { QDoubleSpinBox* doubleSpinBox = qobject_cast(object); if (doubleSpinBox != NULL) { CaretAssert(dataValueType == WuQMacroDataValueTypeEnum::FLOAT); WuQMacroSignalEmitter signalEmitter; signalEmitter.emitQDoubleSpinBoxSignal(doubleSpinBox, - objectValue.toDouble()); + dataValue.toDouble()); } else { notFoundFlag = true; } } break; - case WuQMacroObjectTypeEnum::INVALID: + case WuQMacroClassTypeEnum::INVALID: CaretAssert(0); break; - case WuQMacroObjectTypeEnum::LINE_EDIT: + case WuQMacroClassTypeEnum::LINE_EDIT: { QLineEdit* lineEdit = qobject_cast(object); if (lineEdit != NULL) { CaretAssert(dataValueType == WuQMacroDataValueTypeEnum::STRING); WuQMacroSignalEmitter signalEmitter; signalEmitter.emitQLineEditSignal(lineEdit, - objectValue.toString()); + dataValue.toString()); } else { notFoundFlag = true; } } break; - case WuQMacroObjectTypeEnum::LIST_WIDGET: + case WuQMacroClassTypeEnum::LIST_WIDGET: { QListWidget* listWidget = qobject_cast(object); if (listWidget != NULL) { - CaretAssert(dataValueType == WuQMacroDataValueTypeEnum::STRING); + CaretAssert(dataValueType == WuQMacroDataValueTypeEnum::INTEGER); + CaretAssert(dataValueTypeTwo == WuQMacroDataValueTypeEnum::STRING); + + QListWidgetItem* textItem(NULL); + QListWidgetItem* indexItem(NULL); + for (int32_t i = 0; i < listWidget->count(); i++) { + QListWidgetItem* itemAtIndex = listWidget->item(i); + if (itemAtIndex->text() == dataValueTwo.toString()) { + textItem = itemAtIndex; + } + if (dataValue.toInt() == i) { + indexItem = itemAtIndex; + } + } + WuQMacroSignalEmitter signalEmitter; - signalEmitter.emitQListWidgetSignal(listWidget, - objectValue.toString()); + if (textItem != NULL) { + signalEmitter.emitQListWidgetSignal(listWidget, + textItem->text()); + } + else if (indexItem != NULL) { + signalEmitter.emitQListWidgetSignal(listWidget, + indexItem->text()); + } + else { + valueNotFoundErrorMessage = ("For QListWidget \"" + + object->objectName() + + "\", unable to find item with text \"" + + dataValueTwo.toString() + + "\" or index=" + + dataValue.toInt()); + } } else { notFoundFlag = true; } } break; - case WuQMacroObjectTypeEnum::MENU: + case WuQMacroClassTypeEnum::MENU: { QMenu* menu = qobject_cast(object); if (menu != NULL) { - CaretAssert(dataValueType == WuQMacroDataValueTypeEnum::STRING); + CaretAssert(dataValueType == WuQMacroDataValueTypeEnum::INTEGER); + CaretAssert(dataValueTypeTwo == WuQMacroDataValueTypeEnum::STRING); + + QAction* textAction(NULL); + QAction* indexAction(NULL); + QList actions = menu->actions(); + for (int32_t i = 0; i < actions.size(); i++) { + QAction* actionAtIndex = actions.at(i); + if (actionAtIndex->text() == dataValueTwo.toString()) { + textAction = actionAtIndex; + } + if (dataValue.toInt() == i) { + indexAction = actionAtIndex; + } + } + WuQMacroSignalEmitter signalEmitter; - signalEmitter.emitQMenuSignal(menu, - objectValue.toString()); + if (textAction != NULL) { + signalEmitter.emitQMenuSignal(menu, + textAction->text()); + } + else if (indexAction != NULL) { + signalEmitter.emitQMenuSignal(menu, + indexAction->text()); + } + else { + valueNotFoundErrorMessage = ("For QMenu \"" + + object->objectName() + + "\", unable to find action with text \"" + + dataValueTwo.toString() + + "\" or index=" + + dataValue.toInt()); + } } else { notFoundFlag = true; } } break; - case WuQMacroObjectTypeEnum::MOUSE_USER_EVENT: + case WuQMacroClassTypeEnum::MOUSE_USER_EVENT: { QWidget* widget = qobject_cast(object); if (widget != NULL) { @@ -465,98 +608,98 @@ WuQMacroExecutor::runMacroCommand(const WuQMacroCommand* macroCommand, } } break; - case WuQMacroObjectTypeEnum::PUSH_BUTTON: + case WuQMacroClassTypeEnum::PUSH_BUTTON: { QPushButton* pushButton = qobject_cast(object); if (pushButton != NULL) { CaretAssert(dataValueType == WuQMacroDataValueTypeEnum::BOOLEAN); WuQMacroSignalEmitter signalEmitter; signalEmitter.emitQPushButtonSignal(pushButton, - objectValue.toBool()); + dataValue.toBool()); } else { notFoundFlag = true; } } break; - case WuQMacroObjectTypeEnum::RADIO_BUTTON: + case WuQMacroClassTypeEnum::RADIO_BUTTON: { QRadioButton* radioButton = qobject_cast(object); if (radioButton != NULL) { CaretAssert(dataValueType == WuQMacroDataValueTypeEnum::BOOLEAN); WuQMacroSignalEmitter signalEmitter; signalEmitter.emitQRadioButtonSignal(radioButton, - objectValue.toBool()); + dataValue.toBool()); } else { notFoundFlag = true; } } break; - case WuQMacroObjectTypeEnum::SLIDER: + case WuQMacroClassTypeEnum::SLIDER: { QSlider* slider = qobject_cast(object); if (slider != NULL) { CaretAssert(dataValueType == WuQMacroDataValueTypeEnum::INTEGER); WuQMacroSignalEmitter signalEmitter; signalEmitter.emitQSliderSignal(slider, - objectValue.toInt()); + dataValue.toInt()); } else { notFoundFlag = true; } } break; - case WuQMacroObjectTypeEnum::SPIN_BOX: + case WuQMacroClassTypeEnum::SPIN_BOX: { QSpinBox* spinBox = qobject_cast(object); if (spinBox != NULL) { CaretAssert(dataValueType == WuQMacroDataValueTypeEnum::INTEGER); WuQMacroSignalEmitter signalEmitter; signalEmitter.emitQSpinBoxSignal(spinBox, - objectValue.toInt()); + dataValue.toInt()); } else { notFoundFlag = true; } } break; - case WuQMacroObjectTypeEnum::TAB_BAR: + case WuQMacroClassTypeEnum::TAB_BAR: { QTabBar* tabBar = qobject_cast(object); if (tabBar != NULL) { CaretAssert(dataValueType == WuQMacroDataValueTypeEnum::INTEGER); WuQMacroSignalEmitter signalEmitter; signalEmitter.emitQTabBarSignal(tabBar, - objectValue.toInt()); + dataValue.toInt()); } else { notFoundFlag = true; } } break; - case WuQMacroObjectTypeEnum::TAB_WIDGET: + case WuQMacroClassTypeEnum::TAB_WIDGET: { QTabWidget* tabWidget = qobject_cast(object); if (tabWidget != NULL) { CaretAssert(dataValueType == WuQMacroDataValueTypeEnum::INTEGER); WuQMacroSignalEmitter signalEmitter; signalEmitter.emitQTabWidgetSignal(tabWidget, - objectValue.toInt()); + dataValue.toInt()); } else { notFoundFlag = true; } } break; - case WuQMacroObjectTypeEnum::TOOL_BUTTON: + case WuQMacroClassTypeEnum::TOOL_BUTTON: { QToolButton* toolButton = qobject_cast(object); if (toolButton != NULL) { CaretAssert(dataValueType == WuQMacroDataValueTypeEnum::BOOLEAN); WuQMacroSignalEmitter signalEmitter; signalEmitter.emitQToolButtonSignal(toolButton, - objectValue.toBool()); + dataValue.toBool()); } else { notFoundFlag = true; @@ -569,7 +712,11 @@ WuQMacroExecutor::runMacroCommand(const WuQMacroCommand* macroCommand, errorMessageOut = ("ERROR: Unable to cast object named " + object->objectName() + " to " - + WuQMacroObjectTypeEnum::toGuiName(objectType)); + + WuQMacroClassTypeEnum::toGuiName(classType)); + return false; + } + else if ( ! valueNotFoundErrorMessage.isEmpty()) { + errorMessageOut = valueNotFoundErrorMessage; return false; } diff --git a/src/GuiQt/WuQMacroManager.cxx b/src/GuiQt/WuQMacroManager.cxx index f0344cd66..8ede58f88 100644 --- a/src/GuiQt/WuQMacroManager.cxx +++ b/src/GuiQt/WuQMacroManager.cxx @@ -224,18 +224,19 @@ WuQMacroManager::addMacroSupportToObject(QObject* object) { CaretAssert(object); + QString toolTipText; + QAction* action = qobject_cast(object); + if (action != NULL) { + toolTipText = action->toolTip(); + } + QWidget* widget = qobject_cast(object); + if (widget != NULL) { + toolTipText = widget->toolTip(); + } + const bool resultFlag = addMacroSupportToObjectWithToolTip(object, - ""); + toolTipText); if (resultFlag) { - QString toolTipText; - QAction* action = qobject_cast(object); - if (action != NULL) { - toolTipText = action->toolTip(); - } - QWidget* widget = qobject_cast(object); - if (widget != NULL) { - toolTipText = widget->toolTip(); - } if (toolTipText.isEmpty()) { CaretLogWarning("Object named \"" @@ -390,7 +391,6 @@ WuQMacroManager::addMouseEventToRecording(QWidget* widget, widget->height()); m_macroBeingRecorded->addMacroCommand(new WuQMacroCommand(name, - widget->toolTip(), mouseInfo)); return true; @@ -766,6 +766,27 @@ WuQMacroManager::printSupportedWidgetsToTerminal() } } +/** + * Get the tooltip for the object with the given name + * + * @param objectName + * Name of object + * @return + * Tooltip for object or empty if not found. + */ +QString +WuQMacroManager::getToolTipForObjectName(const QString& objectName) const +{ + QString tooltip; + + const auto existingWatcher = m_signalWatchers.find(objectName); + if (existingWatcher != m_signalWatchers.end()) { + tooltip = existingWatcher->second->getToolTip(); + } + + return tooltip; +} + diff --git a/src/GuiQt/WuQMacroManager.h b/src/GuiQt/WuQMacroManager.h index 363b1202f..f5a7bf40a 100644 --- a/src/GuiQt/WuQMacroManager.h +++ b/src/GuiQt/WuQMacroManager.h @@ -108,6 +108,8 @@ namespace caret { void addParentObject(QObject* parentObject); + QString getToolTipForObjectName(const QString& objectName) const; + // ADD_NEW_METHODS_HERE private: diff --git a/src/GuiQt/WuQMacroSignalWatcher.cxx b/src/GuiQt/WuQMacroSignalWatcher.cxx index 1ce900273..517cd0d00 100644 --- a/src/GuiQt/WuQMacroSignalWatcher.cxx +++ b/src/GuiQt/WuQMacroSignalWatcher.cxx @@ -68,152 +68,153 @@ using namespace caret; */ WuQMacroSignalWatcher::WuQMacroSignalWatcher(WuQMacroManager* parentMacroManager, QObject* object, - const WuQMacroObjectTypeEnum::Enum objectType, + const WuQMacroClassTypeEnum::Enum objectType, const QString& toolTipTextOverride) : QObject(), m_parentMacroManager(parentMacroManager), +m_object(object), m_objectType(objectType), m_objectName(object->objectName()) { CaretAssert(m_parentMacroManager); - CaretAssert(object); + CaretAssert(m_object); - QWidget* widget = qobject_cast(object); + QWidget* widget = qobject_cast(m_object); if (widget != NULL) { m_toolTipText = widget->toolTip(); } switch (m_objectType) { - case WuQMacroObjectTypeEnum::ACTION: + case WuQMacroClassTypeEnum::ACTION: { - QAction* action = qobject_cast(object); + QAction* action = qobject_cast(m_object); CaretAssert(action); QObject::connect(action, &QAction::triggered, this, &WuQMacroSignalWatcher::actionTriggered); m_toolTipText = action->toolTip(); } break; - case WuQMacroObjectTypeEnum::ACTION_GROUP: + case WuQMacroClassTypeEnum::ACTION_GROUP: { - QActionGroup* actionGroup = qobject_cast(object); + QActionGroup* actionGroup = qobject_cast(m_object); CaretAssert(actionGroup); QObject::connect(actionGroup, &QActionGroup::triggered, this, &WuQMacroSignalWatcher::actionGroupTriggered); } break; - case WuQMacroObjectTypeEnum::BUTTON_GROUP: + case WuQMacroClassTypeEnum::BUTTON_GROUP: { - QButtonGroup* buttonGroup = qobject_cast(object); + QButtonGroup* buttonGroup = qobject_cast(m_object); CaretAssert(buttonGroup); QObject::connect(buttonGroup, static_cast(&QButtonGroup::buttonClicked), this, &WuQMacroSignalWatcher::buttonGroupButtonClicked); } break; - case WuQMacroObjectTypeEnum::CHECK_BOX: + case WuQMacroClassTypeEnum::CHECK_BOX: { - QCheckBox* checkBox = qobject_cast(object); + QCheckBox* checkBox = qobject_cast(m_object); CaretAssert(checkBox); QObject::connect(checkBox, &QCheckBox::clicked, this, &WuQMacroSignalWatcher::checkBoxClicked); } break; - case WuQMacroObjectTypeEnum::COMBO_BOX: + case WuQMacroClassTypeEnum::COMBO_BOX: { - QComboBox* comboBox = qobject_cast(object); + QComboBox* comboBox = qobject_cast(m_object); CaretAssert(comboBox); QObject::connect(comboBox, static_cast(&QComboBox::activated), this, &WuQMacroSignalWatcher::comboBoxActivated); } break; - case WuQMacroObjectTypeEnum::DOUBLE_SPIN_BOX: + case WuQMacroClassTypeEnum::DOUBLE_SPIN_BOX: { - QDoubleSpinBox* spinBox = qobject_cast(object); + QDoubleSpinBox* spinBox = qobject_cast(m_object); CaretAssert(spinBox); QObject::connect(spinBox, static_cast(&QDoubleSpinBox::valueChanged), this, &WuQMacroSignalWatcher::doubleSpinBoxValueChanged); } break; - case WuQMacroObjectTypeEnum::INVALID: + case WuQMacroClassTypeEnum::INVALID: CaretAssert(0); break; - case WuQMacroObjectTypeEnum::LINE_EDIT: + case WuQMacroClassTypeEnum::LINE_EDIT: { - QLineEdit* lineEdit = qobject_cast(object); + QLineEdit* lineEdit = qobject_cast(m_object); CaretAssert(lineEdit); QObject::connect(lineEdit, &QLineEdit::textEdited, this, &WuQMacroSignalWatcher::lineEditTextEdited); } break; - case WuQMacroObjectTypeEnum::LIST_WIDGET: + case WuQMacroClassTypeEnum::LIST_WIDGET: { - QListWidget* listWidget = qobject_cast(object); + QListWidget* listWidget = qobject_cast(m_object); CaretAssert(listWidget); QObject::connect(listWidget, &QListWidget::itemActivated, this, &WuQMacroSignalWatcher::listWidgetItemActivated); } break; - case WuQMacroObjectTypeEnum::MENU: + case WuQMacroClassTypeEnum::MENU: { - QMenu* menu = qobject_cast(object); + QMenu* menu = qobject_cast(m_object); CaretAssert(menu); QObject::connect(menu, &QMenu::triggered, this, &WuQMacroSignalWatcher::menuTriggered); } break; - case WuQMacroObjectTypeEnum::MOUSE_USER_EVENT: + case WuQMacroClassTypeEnum::MOUSE_USER_EVENT: CaretAssertToDoFatal(); break; - case WuQMacroObjectTypeEnum::PUSH_BUTTON: + case WuQMacroClassTypeEnum::PUSH_BUTTON: { - QPushButton* pushButton = qobject_cast(object); + QPushButton* pushButton = qobject_cast(m_object); CaretAssert(pushButton); QObject::connect(pushButton, &QPushButton::clicked, this, &WuQMacroSignalWatcher::pushButtonClicked); } break; - case WuQMacroObjectTypeEnum::RADIO_BUTTON: + case WuQMacroClassTypeEnum::RADIO_BUTTON: { - QRadioButton* radioButton = qobject_cast(object); + QRadioButton* radioButton = qobject_cast(m_object); CaretAssert(radioButton); QObject::connect(radioButton, &QRadioButton::clicked, this, &WuQMacroSignalWatcher::radioButtonClicked); } break; - case WuQMacroObjectTypeEnum::SLIDER: + case WuQMacroClassTypeEnum::SLIDER: { - QSlider* slider = qobject_cast(object); + QSlider* slider = qobject_cast(m_object); CaretAssert(slider); QObject::connect(slider, &QSlider::valueChanged, this, &WuQMacroSignalWatcher::sliderValueChanged); } break; - case WuQMacroObjectTypeEnum::SPIN_BOX: + case WuQMacroClassTypeEnum::SPIN_BOX: { - QSpinBox* spinBox = qobject_cast(object); + QSpinBox* spinBox = qobject_cast(m_object); CaretAssert(spinBox); QObject::connect(spinBox, static_cast(&QSpinBox::valueChanged), this, &WuQMacroSignalWatcher::spinBoxValueChanged); } break; - case WuQMacroObjectTypeEnum::TAB_BAR: + case WuQMacroClassTypeEnum::TAB_BAR: { - QTabBar* tabBar = qobject_cast(object); + QTabBar* tabBar = qobject_cast(m_object); CaretAssert(tabBar); - QObject::connect(tabBar, &QTabBar::currentChanged, + QObject::connect(tabBar, &QTabBar::tabBarClicked, //&QTabBar::currentChanged, this, &WuQMacroSignalWatcher::tabBarCurrentChanged); } break; - case WuQMacroObjectTypeEnum::TAB_WIDGET: + case WuQMacroClassTypeEnum::TAB_WIDGET: { - QTabWidget* tabWidget = qobject_cast(object); + QTabWidget* tabWidget = qobject_cast(m_object); CaretAssert(tabWidget); - QObject::connect(tabWidget, &QTabWidget::currentChanged, + QObject::connect(tabWidget, &QTabWidget::tabBarClicked, //&QTabWidget::currentChanged, this, &WuQMacroSignalWatcher::tabWidgetCurrentChanged); } break; - case WuQMacroObjectTypeEnum::TOOL_BUTTON: + case WuQMacroClassTypeEnum::TOOL_BUTTON: { - QToolButton* toolButton = qobject_cast(object); + QToolButton* toolButton = qobject_cast(m_object); CaretAssert(toolButton); QObject::connect(toolButton, &QCheckBox::clicked, this, &WuQMacroSignalWatcher::toolButtonClicked); @@ -228,9 +229,9 @@ m_objectName(object->objectName()) m_toolTipText = toolTipTextOverride; } - QObject::connect(object, &QObject::destroyed, + QObject::connect(m_object, &QObject::destroyed, this, &WuQMacroSignalWatcher::objectWasDestroyed); - QObject::connect(object, &QObject::objectNameChanged, + QObject::connect(m_object, &QObject::objectNameChanged, this, &WuQMacroSignalWatcher::objectNameWasChanged); } @@ -300,10 +301,10 @@ WuQMacroSignalWatcher::newInstance(WuQMacroManager* parentMacroManager, QString objectClassName = object->metaObject()->className(); bool validFlag(false); - WuQMacroObjectTypeEnum::Enum objectType = WuQMacroObjectTypeEnum::fromGuiName(objectClassName, + WuQMacroClassTypeEnum::Enum objectType = WuQMacroClassTypeEnum::fromGuiName(objectClassName, &validFlag); - if ((objectType == WuQMacroObjectTypeEnum::INVALID) + if ((objectType == WuQMacroClassTypeEnum::INVALID) || ( ! validFlag)) { errorMessageOut = ("Widget named \"" + object->objectName() @@ -320,21 +321,33 @@ WuQMacroSignalWatcher::newInstance(WuQMacroManager* parentMacroManager, return ww; } +/** + * @return Tooltip for this signal watcher + */ +QString +WuQMacroSignalWatcher::getToolTip() const +{ + return m_toolTipText; +} + /** * If recording mode is enabled, create and send a macro command * to the macro manager. * * @param value * QVariant containing the data value. + * @param valueTwo + * QVariant containing the second data value. */ void -WuQMacroSignalWatcher::createAndSendMacroCommand(const QVariant value) +WuQMacroSignalWatcher::createAndSendMacroCommand(const QVariant& value, + const QVariant& valueTwo) { if (m_parentMacroManager->isModeRecording()) { WuQMacroCommand* mc = new WuQMacroCommand(m_objectType, m_objectName, - m_toolTipText, - value); + value, + valueTwo); if ( ! m_parentMacroManager->addMacroCommandToRecording(mc)) { delete mc; } @@ -350,10 +363,23 @@ WuQMacroSignalWatcher::createAndSendMacroCommand(const QVariant value) void WuQMacroSignalWatcher::actionGroupTriggered(QAction* action) { - const QString text((action != NULL) - ? action->text() - : ""); - createAndSendMacroCommand(text); + QActionGroup* actionGroup = qobject_cast(m_object); + CaretAssert(actionGroup); + + int actionIndex(-1); + QList actionList = actionGroup->actions(); + for (int32_t i = 0; i < actionList.size(); i++) { + if (actionList.at(i) == action) { + actionIndex = i; + break; + } + } + + const QString actionText((action != NULL) + ? action->text() + : ""); + createAndSendMacroCommand(actionIndex, + actionText); } @@ -366,7 +392,8 @@ WuQMacroSignalWatcher::actionGroupTriggered(QAction* action) void WuQMacroSignalWatcher::actionTriggered(bool checked) { - createAndSendMacroCommand(checked); + createAndSendMacroCommand(checked, + ""); } /** @@ -378,10 +405,23 @@ WuQMacroSignalWatcher::actionTriggered(bool checked) void WuQMacroSignalWatcher::buttonGroupButtonClicked(QAbstractButton* button) { - const QString text((button != NULL) + QButtonGroup* buttonGroup = qobject_cast(m_object); + CaretAssert(buttonGroup); + + int buttonIndex(-1); + QList buttonList = buttonGroup->buttons(); + for (int32_t i = 0; i < buttonList.size(); i++) { + if (buttonList.at(i) == button) { + buttonIndex = i; + break; + } + } + + const QString buttonText((button != NULL) ? button->text() : ""); - createAndSendMacroCommand(text); + createAndSendMacroCommand(buttonIndex, + buttonText); } @@ -394,7 +434,8 @@ WuQMacroSignalWatcher::buttonGroupButtonClicked(QAbstractButton* button) void WuQMacroSignalWatcher::checkBoxClicked(bool checked) { - createAndSendMacroCommand(checked); + createAndSendMacroCommand(checked, + ""); } /** @@ -406,7 +447,17 @@ WuQMacroSignalWatcher::checkBoxClicked(bool checked) void WuQMacroSignalWatcher::comboBoxActivated(int index) { - createAndSendMacroCommand(index); + QComboBox* comboBox = qobject_cast(m_object); + CaretAssert(comboBox); + + QString text; + if ((index >= 0) + && (index < comboBox->count())) { + text = comboBox->itemText(index); + } + + createAndSendMacroCommand(index, + text); } /** @@ -418,7 +469,8 @@ WuQMacroSignalWatcher::comboBoxActivated(int index) void WuQMacroSignalWatcher::doubleSpinBoxValueChanged(double value) { - createAndSendMacroCommand(value); + createAndSendMacroCommand(value, + ""); } /** @@ -430,7 +482,8 @@ WuQMacroSignalWatcher::doubleSpinBoxValueChanged(double value) void WuQMacroSignalWatcher::lineEditTextEdited(const QString& text) { - createAndSendMacroCommand(text); + createAndSendMacroCommand(text, + ""); } /** @@ -442,10 +495,16 @@ WuQMacroSignalWatcher::lineEditTextEdited(const QString& text) void WuQMacroSignalWatcher::listWidgetItemActivated(QListWidgetItem* item) { + QListWidget* listWidget = qobject_cast(m_object); + CaretAssert(listWidget); + + const int rowIndex = listWidget->row(item); + const QString text((item != NULL) ? item->text() : ""); - createAndSendMacroCommand(text); + createAndSendMacroCommand(rowIndex, + text); } /** @@ -457,10 +516,23 @@ WuQMacroSignalWatcher::listWidgetItemActivated(QListWidgetItem* item) void WuQMacroSignalWatcher::menuTriggered(QAction* action) { + QMenu* menu = qobject_cast(m_object); + CaretAssert(menu); + + int actionIndex(-1); + QList actionList = menu->actions(); + for (int32_t i = 0; i < actionList.size(); i++) { + if (actionList.at(i) == action) { + actionIndex = i; + break; + } + } + const QString text((action != NULL) ? action->text() : ""); - createAndSendMacroCommand(text); + createAndSendMacroCommand(actionIndex, + text); } /** @@ -472,7 +544,8 @@ WuQMacroSignalWatcher::menuTriggered(QAction* action) void WuQMacroSignalWatcher::pushButtonClicked(bool checked) { - createAndSendMacroCommand(checked); + createAndSendMacroCommand(checked, + ""); } /** @@ -484,7 +557,8 @@ WuQMacroSignalWatcher::pushButtonClicked(bool checked) void WuQMacroSignalWatcher::radioButtonClicked(bool checked) { - createAndSendMacroCommand(checked); + createAndSendMacroCommand(checked, + ""); } /** @@ -496,7 +570,8 @@ WuQMacroSignalWatcher::radioButtonClicked(bool checked) void WuQMacroSignalWatcher::sliderValueChanged(int value) { - createAndSendMacroCommand(value); + createAndSendMacroCommand(value, + ""); } /** @@ -508,7 +583,8 @@ WuQMacroSignalWatcher::sliderValueChanged(int value) void WuQMacroSignalWatcher::spinBoxValueChanged(int value) { - createAndSendMacroCommand(value); + createAndSendMacroCommand(value, + ""); } /** @@ -520,7 +596,8 @@ WuQMacroSignalWatcher::spinBoxValueChanged(int value) void WuQMacroSignalWatcher::tabBarCurrentChanged(int index) { - createAndSendMacroCommand(index); + createAndSendMacroCommand(index, + ""); } /** @@ -532,7 +609,8 @@ WuQMacroSignalWatcher::tabBarCurrentChanged(int index) void WuQMacroSignalWatcher::tabWidgetCurrentChanged(int index) { - createAndSendMacroCommand(index); + createAndSendMacroCommand(index, + ""); } /** @@ -544,7 +622,8 @@ WuQMacroSignalWatcher::tabWidgetCurrentChanged(int index) void WuQMacroSignalWatcher::toolButtonClicked(bool checked) { - createAndSendMacroCommand(checked); + createAndSendMacroCommand(checked, + ""); } /** @@ -555,7 +634,7 @@ WuQMacroSignalWatcher::toString() const { QString s(m_objectName + " type=" - + WuQMacroObjectTypeEnum::toGuiName(m_objectType)); + + WuQMacroClassTypeEnum::toGuiName(m_objectType)); return s; } diff --git a/src/GuiQt/WuQMacroSignalWatcher.h b/src/GuiQt/WuQMacroSignalWatcher.h index 6e99bde7e..bf41f9598 100644 --- a/src/GuiQt/WuQMacroSignalWatcher.h +++ b/src/GuiQt/WuQMacroSignalWatcher.h @@ -27,7 +27,7 @@ #include -#include "WuQMacroObjectTypeEnum.h" +#include "WuQMacroClassTypeEnum.h" class QAbstractButton; class QAction; @@ -55,6 +55,8 @@ namespace caret { QString toString() const; + QString getToolTip() const; + private slots: void actionTriggered(bool); @@ -97,14 +99,17 @@ namespace caret { private: WuQMacroSignalWatcher(WuQMacroManager* parentMacroManager, QObject* object, - const WuQMacroObjectTypeEnum::Enum objectType, + const WuQMacroClassTypeEnum::Enum objectType, const QString& toolTipTextOverride); WuQMacroManager* m_parentMacroManager; - void createAndSendMacroCommand(const QVariant value); + void createAndSendMacroCommand(const QVariant& value, + const QVariant& valueTwo); + + QObject* m_object; - const WuQMacroObjectTypeEnum::Enum m_objectType; + const WuQMacroClassTypeEnum::Enum m_objectType; const QString m_objectName; -- GitLab From 6ded11b11bb4c61363b04c97ca0d620f89d13ee9 Mon Sep 17 00:00:00 2001 From: John Harwell Date: Fri, 14 Dec 2018 15:04:03 -0600 Subject: [PATCH 130/427] WB-834: Improvements with mouse highlighting for widgets where center of widget is not correct location for the mouse. --- src/GuiQt/WuQMacroEditorDialog.cxx | 52 +++-- src/GuiQt/WuQMacroEditorDialog.h | 10 +- src/GuiQt/WuQMacroExecutor.cxx | 285 +++++++++++++++++----------- src/GuiQt/WuQMacroExecutor.h | 30 ++- src/GuiQt/WuQMacroManager.cxx | 14 ++ src/GuiQt/WuQMacroManager.h | 2 + src/GuiQt/WuQMacroMenu.cxx | 14 +- src/GuiQt/WuQMacroMenu.h | 2 + src/GuiQt/WuQMacroSignalWatcher.cxx | 8 +- 9 files changed, 276 insertions(+), 141 deletions(-) diff --git a/src/GuiQt/WuQMacroEditorDialog.cxx b/src/GuiQt/WuQMacroEditorDialog.cxx index 9d108a98c..488589778 100644 --- a/src/GuiQt/WuQMacroEditorDialog.cxx +++ b/src/GuiQt/WuQMacroEditorDialog.cxx @@ -130,20 +130,24 @@ WuQMacroEditorDialog::createTableWidget() { QTableWidget* tableWidget = new QTableWidget(); - QStringList columnNames; - CaretAssert(COLUMN_TYPE == 0); - columnNames << "Class"; - CaretAssert(COLUMN_VALUE_TYPE == 1); - columnNames << "Value-Type"; - CaretAssert(COLUMN_NAME == 2); - columnNames << "Name"; - CaretAssert(COLUMN_VALUE == 3); - columnNames << "Value"; - CaretAssert(COLUMN_TOOLTIP == 4); - columnNames << "ToolTip"; - CaretAssert(columnNames.size() == NUMBER_OF_COLUMNS); - - tableWidget->setHorizontalHeaderLabels(columnNames); +// QStringList columnNames; +// CaretAssert(COLUMN_TYPE == 0); +// columnNames << "Class"; +// CaretAssert(COLUMN_NAME == 1); +// columnNames << "Name"; +// CaretAssert(COLUMN_VALUE_TYPE == 2); +// columnNames << "Value-Type"; +// CaretAssert(COLUMN_VALUE == 3); +// columnNames << "Value"; +// CaretAssert(COLUMN_VALUE_TWO_TYPE == 4); +// columnNames << "Value-Two-Type"; +// CaretAssert(COLUMN_VALUE_TWO == 5); +// columnNames << "Value-Two"; +// CaretAssert(COLUMN_TOOLTIP == 6); +// columnNames << "ToolTip"; +// CaretAssert(columnNames.size() == NUMBER_OF_COLUMNS); +// +// tableWidget->setHorizontalHeaderLabels(columnNames); return tableWidget; } @@ -163,9 +167,11 @@ WuQMacroEditorDialog::loadTableWidget() for (int32_t i = 0; i < numberOfCommands; i++) { const WuQMacroCommand* command = m_macro->getMacroCommandAtIndex(i); QTableWidgetItem* classItem = new QTableWidgetItem(WuQMacroClassTypeEnum::toGuiName(command->getClassType())); - QTableWidgetItem* valueTypeItem = new QTableWidgetItem(WuQMacroDataValueTypeEnum::toGuiName(command->getDataType())); QTableWidgetItem* nameItem = new QTableWidgetItem(command->getObjectName()); + QTableWidgetItem* valueTypeItem = new QTableWidgetItem(WuQMacroDataValueTypeEnum::toGuiName(command->getDataType())); QTableWidgetItem* valueItem = new QTableWidgetItem(command->getDataValue().toString()); + QTableWidgetItem* valueTwoTypeItem = new QTableWidgetItem(WuQMacroDataValueTypeEnum::toGuiName(command->getDataTypeTwo())); + QTableWidgetItem* valueTwoItem = new QTableWidgetItem(command->getDataValueTwo().toString()); const QString toolTip = WuQMacroManager::instance()->getToolTipForObjectName(command->getObjectName()); QTableWidgetItem* toolTipItem = new QTableWidgetItem(toolTip); @@ -185,22 +191,28 @@ WuQMacroEditorDialog::loadTableWidget() } m_tableWidget->setItem(i, COLUMN_TYPE, classItem); - m_tableWidget->setItem(i, COLUMN_VALUE_TYPE, valueTypeItem); m_tableWidget->setItem(i, COLUMN_NAME, nameItem); + m_tableWidget->setItem(i, COLUMN_VALUE_TYPE, valueTypeItem); m_tableWidget->setItem(i, COLUMN_VALUE, valueItem); + m_tableWidget->setItem(i, COLUMN_VALUE_TWO_TYPE, valueTwoTypeItem); + m_tableWidget->setItem(i, COLUMN_VALUE_TWO, valueTwoItem); m_tableWidget->setItem(i, COLUMN_TOOLTIP, toolTipItem); } QStringList columnNames; CaretAssert(COLUMN_TYPE == 0); columnNames << "Class"; - CaretAssert(COLUMN_VALUE_TYPE == 1); - columnNames << "Value-Type"; - CaretAssert(COLUMN_NAME == 2); + CaretAssert(COLUMN_NAME == 1); columnNames << "Name"; + CaretAssert(COLUMN_VALUE_TYPE == 2); + columnNames << "Value-Type"; CaretAssert(COLUMN_VALUE == 3); columnNames << "Value"; - CaretAssert(COLUMN_TOOLTIP == 4); + CaretAssert(COLUMN_VALUE_TWO_TYPE == 4); + columnNames << "Value-Two-Type"; + CaretAssert(COLUMN_VALUE_TWO == 5); + columnNames << "Value-Two"; + CaretAssert(COLUMN_TOOLTIP == 6); columnNames << "ToolTip"; CaretAssert(columnNames.size() == NUMBER_OF_COLUMNS); diff --git a/src/GuiQt/WuQMacroEditorDialog.h b/src/GuiQt/WuQMacroEditorDialog.h index 6c28a23a2..bbad9bf88 100644 --- a/src/GuiQt/WuQMacroEditorDialog.h +++ b/src/GuiQt/WuQMacroEditorDialog.h @@ -62,11 +62,13 @@ namespace caret { private: enum { COLUMN_TYPE = 0, - COLUMN_VALUE_TYPE = 1, - COLUMN_NAME = 2, + COLUMN_NAME = 1, + COLUMN_VALUE_TYPE = 2, COLUMN_VALUE = 3, - COLUMN_TOOLTIP = 4, - NUMBER_OF_COLUMNS = 5 + COLUMN_VALUE_TWO_TYPE = 4, + COLUMN_VALUE_TWO = 5, + COLUMN_TOOLTIP = 6, + NUMBER_OF_COLUMNS = 7 }; QTableWidget* createTableWidget(); diff --git a/src/GuiQt/WuQMacroExecutor.cxx b/src/GuiQt/WuQMacroExecutor.cxx index 25b602503..9d51e6d8a 100644 --- a/src/GuiQt/WuQMacroExecutor.cxx +++ b/src/GuiQt/WuQMacroExecutor.cxx @@ -77,23 +77,83 @@ WuQMacroExecutor::~WuQMacroExecutor() { } +/** + * Move the mouse to the tab bar's tab with the given tab index + * + * @param tabBar + * The tab bar + * @param tabIndex + * Index of the tab + */ +void +WuQMacroExecutor::moveMouseToTabBarTab(QTabBar* tabBar, + const int32_t tabIndex) const +{ + QRect tabRect = tabBar->tabRect(tabIndex); + if (tabRect.isNull()) { + moveMouseToWidget(tabBar); + } + else { + moveMouseToWidget(tabBar, + &tabRect); + } +} + /** * Move the mouse around the given widget * - * @param widget - * Widget to where mouse should be moved - * @param highlightFlag - * Highlights widget by having mouse circle around widget center + * @param moveToObject + * Object to where mouse should be moved + * @param objectRect + * Optional, if not NULL, rectangle of object used for positioning mouse */ void -WuQMacroExecutor::moveMouse(QWidget* widget, - const bool highlightFlag) const +WuQMacroExecutor::moveMouseToWidget(QObject* moveToObject, + const QRect* objectRect) const { - CaretAssert(widget); + if ( ! m_runOptions.m_showMouseMovementFlag) { + return; + } + CaretAssert(moveToObject); + const QString objectName = moveToObject->objectName(); + + /* + * Object may not be a widget so find ancestor + * that is a widget + */ + bool usingParentFlag(false); + QWidget* moveToWidget(NULL); + while ((moveToWidget == NULL) + && (moveToObject != NULL)) { + moveToWidget = qobject_cast(moveToObject); + if (moveToWidget == NULL) { + moveToObject = moveToObject->parent(); + usingParentFlag = true; + } + } + + if (moveToWidget == NULL) { + return; + + } + + if (usingParentFlag) { + std::cout << "For mouse, using parent " << moveToWidget->objectName() + << " for object " << objectName << std::endl; + } - const QPoint widgetCenter = widget->rect().center(); - const QPoint windowPoint = widget->mapToGlobal(widgetCenter); + CaretAssert(moveToWidget); + const QRect widgetRect = ((objectRect != NULL) + ? *objectRect + : moveToWidget->rect()); + const QPoint widgetCenter = widgetRect.center(); + const QPoint windowPoint = moveToWidget->mapToGlobal(widgetCenter); + + QCursor::setPos(windowPoint); + SystemUtilities::sleepSeconds(0.025); + + const bool highlightFlag(true); if (highlightFlag) { const float radius = 15.0; for (float angle = 0.0; angle < 6.28; angle += 0.314) { @@ -107,15 +167,37 @@ WuQMacroExecutor::moveMouse(QWidget* widget, QCursor::setPos(windowPoint); } +/** + * Find an object, by name, in the parent objects + * + * @param objectName + * Name of object + * @return + * Pointer to object with name or NULL if not found + */ +QObject* +WuQMacroExecutor::findObjectByName(const QString& objectName) const +{ + QObject* object(NULL); + + for (auto po : m_parentObjects) { + object = po->findChild(objectName); + if (object != NULL) { + break; + } + } + + return object; +} + /** * Run the commands in the given macro. * * @param macro * Macro that is run - * @param window - * The window from which macro was launched and is searched for - * objects contained in the macro commands - * @param + * @param topLevelObject + * Top level object for finding children objects + * @param * Additional objects that are searched for objects contained * in the macro commands * @param options @@ -127,11 +209,18 @@ WuQMacroExecutor::moveMouse(QWidget* widget, */ bool WuQMacroExecutor::runMacro(const WuQMacro* macro, - QObject* window, + QObject* topLevelObject, std::vector& otherObjectParents, const RunOptions& options, QString& errorMessageOut) const { + m_parentObjects.clear(); + m_parentObjects.push_back(topLevelObject); + m_parentObjects.insert(m_parentObjects.end(), + otherObjectParents.begin(), otherObjectParents.end()); + + m_runOptions = options; + errorMessageOut.clear(); const int32_t numberOfMacroCommands = macro->getNumberOfMacroCommands(); @@ -140,107 +229,41 @@ WuQMacroExecutor::runMacro(const WuQMacro* macro, CaretAssert(mc); const QString objectName(mc->getObjectName()); - QObject* object = window->findChild(objectName); + QObject* object = findObjectByName(objectName); if (object == NULL) { - object = qApp->findChild(objectName); - if (object != NULL) { - CaretLogWarning("Found " - + objectName - + " in the app"); - } - else { - for (auto other : otherObjectParents) { - object = other->findChild(objectName); - if (object != NULL) { - break; - } - } - const bool extraFindFlag(false); - if (extraFindFlag) { - static bool firstTime = true; - if (firstTime) { - QWidgetList topLevelWs = QApplication::topLevelWidgets(); - std::cout << "Top level widget count: " << topLevelWs.size() << std::endl; - for (QObject* w : topLevelWs) { - if (w->objectName() == objectName) { - std::cout << objectName << " is a top level widget" << std::endl; - break; - } - object = w->findChild(objectName); - if (object != NULL) { - QString parentName; - QObject* parent = object->parent(); - if (parent != NULL) { - parentName = parent->objectName(); - } - std::cout << "Found object in top level widget: \"" << parentName << "\"" << std::endl; - break; - } - } - } - } - - if (object == NULL) { - errorMessageOut.append("Unable to find object named " - + objectName - + "\n"); - if (options.m_stopOnErrorFlag) { - return false; - } - continue; - } + errorMessageOut.append("Unable to find object named " + + objectName + + "\n"); + if (m_runOptions.m_stopOnErrorFlag) { + return false; } + continue; } - + if (object->signalsBlocked()) { errorMessageOut.append("Object named " + objectName + " has signals blocked"); - if (options.m_stopOnErrorFlag) { + if (m_runOptions.m_stopOnErrorFlag) { return false; } continue; } - QWidget* widgetToMoveMouse = qobject_cast(object); - - if (widgetToMoveMouse == NULL) { - QObject* object = window->findChild(objectName); - if (object != NULL) { - QObject* parent = object->parent(); - if (parent != NULL) { - widgetToMoveMouse = qobject_cast(parent); - } - } - } - - const WuQMacroClassTypeEnum::Enum classType = mc->getClassType(); - const bool mouseEventFlag = (classType == WuQMacroClassTypeEnum::MOUSE_USER_EVENT); - - if (options.m_showMouseMovementFlag) { - if (widgetToMoveMouse != NULL) { - if ( ! mouseEventFlag) { - const bool highlightFlag = ( ! mouseEventFlag); - moveMouse(widgetToMoveMouse, - highlightFlag); - } - } - } - QString commandErrorMessage; if ( ! runMacroCommand(mc, object, commandErrorMessage)) { errorMessageOut.append(commandErrorMessage + "\n"); - if (options.m_stopOnErrorFlag) { + if (m_runOptions.m_stopOnErrorFlag) { return false; } } QGuiApplication::processEvents(); - if ( ! mouseEventFlag) { - if (options.m_secondsDelayBetweenCommands > 0.0) { - SystemUtilities::sleepSeconds(options.m_secondsDelayBetweenCommands); + if (mc->getClassType() != WuQMacroClassTypeEnum::MOUSE_USER_EVENT) { + if (m_runOptions.m_secondsDelayBetweenCommands > 0.0) { + SystemUtilities::sleepSeconds(m_runOptions.m_secondsDelayBetweenCommands); } } @@ -280,13 +303,14 @@ WuQMacroExecutor::runMacroCommand(const WuQMacroCommand* macroCommand, const WuQMacroDataValueTypeEnum::Enum dataValueTypeTwo = macroCommand->getDataTypeTwo(); const QVariant dataValueTwo = macroCommand->getDataValueTwo(); - QString valueNotFoundErrorMessage; + QString objectErrorMessage; bool notFoundFlag(false); switch (classType) { case WuQMacroClassTypeEnum::ACTION: { QAction* action = qobject_cast(object); if (action != NULL) { + moveMouseToWidget(action); CaretAssert(dataValueType == WuQMacroDataValueTypeEnum::BOOLEAN); WuQMacroSignalEmitter signalEmitter; signalEmitter.emitQActionSignal(action, @@ -301,6 +325,7 @@ WuQMacroExecutor::runMacroCommand(const WuQMacroCommand* macroCommand, { QActionGroup* actionGroup = qobject_cast(object); if (actionGroup != NULL) { + moveMouseToWidget(actionGroup); CaretAssert(dataValueType == WuQMacroDataValueTypeEnum::INTEGER); CaretAssert(dataValueTypeTwo == WuQMacroDataValueTypeEnum::STRING); @@ -327,7 +352,7 @@ WuQMacroExecutor::runMacroCommand(const WuQMacroCommand* macroCommand, indexAction->text()); } else { - valueNotFoundErrorMessage = ("For QActionGroup \"" + objectErrorMessage = ("For QActionGroup \"" + object->objectName() + "\", unable to find action with text \"" + dataValueTwo.toString() @@ -362,15 +387,17 @@ WuQMacroExecutor::runMacroCommand(const WuQMacroCommand* macroCommand, WuQMacroSignalEmitter signalEmitter; if (textButton != NULL) { + moveMouseToWidget(textButton); signalEmitter.emitQButtonGroupSignal(buttonGroup, textButton->text()); } else if (indexButton != NULL) { + moveMouseToWidget(indexButton); signalEmitter.emitQButtonGroupSignal(buttonGroup, indexButton->text()); } else { - valueNotFoundErrorMessage = ("For QButtonGroup \"" + objectErrorMessage = ("For QButtonGroup \"" + object->objectName() + "\", unable to find button with text \"" + dataValueTwo.toString() @@ -387,6 +414,7 @@ WuQMacroExecutor::runMacroCommand(const WuQMacroCommand* macroCommand, { QCheckBox* checkBox = qobject_cast(object); if (checkBox != NULL) { + moveMouseToWidget(checkBox); CaretAssert(dataValueType == WuQMacroDataValueTypeEnum::BOOLEAN); WuQMacroSignalEmitter signalEmitter; signalEmitter.emitQCheckBoxSignal(checkBox, @@ -413,6 +441,7 @@ WuQMacroExecutor::runMacroCommand(const WuQMacroCommand* macroCommand, indexIndex = i; } } + moveMouseToWidget(comboBox); WuQMacroSignalEmitter signalEmitter; if (textIndex >= 0) { signalEmitter.emitQComboBoxSignal(comboBox, @@ -423,7 +452,7 @@ WuQMacroExecutor::runMacroCommand(const WuQMacroCommand* macroCommand, indexIndex); } else { - valueNotFoundErrorMessage = ("For ComboBox \"" + objectErrorMessage = ("For ComboBox \"" + object->objectName() + "\", unable to find item with text \"" + dataValueTwo.toString() @@ -441,6 +470,7 @@ WuQMacroExecutor::runMacroCommand(const WuQMacroCommand* macroCommand, QDoubleSpinBox* doubleSpinBox = qobject_cast(object); if (doubleSpinBox != NULL) { CaretAssert(dataValueType == WuQMacroDataValueTypeEnum::FLOAT); + moveMouseToWidget(doubleSpinBox); WuQMacroSignalEmitter signalEmitter; signalEmitter.emitQDoubleSpinBoxSignal(doubleSpinBox, dataValue.toDouble()); @@ -457,6 +487,7 @@ WuQMacroExecutor::runMacroCommand(const WuQMacroCommand* macroCommand, { QLineEdit* lineEdit = qobject_cast(object); if (lineEdit != NULL) { + moveMouseToWidget(lineEdit); CaretAssert(dataValueType == WuQMacroDataValueTypeEnum::STRING); WuQMacroSignalEmitter signalEmitter; signalEmitter.emitQLineEditSignal(lineEdit, @@ -485,6 +516,8 @@ WuQMacroExecutor::runMacroCommand(const WuQMacroCommand* macroCommand, indexItem = itemAtIndex; } } + + moveMouseToWidget(listWidget); WuQMacroSignalEmitter signalEmitter; if (textItem != NULL) { @@ -492,11 +525,12 @@ WuQMacroExecutor::runMacroCommand(const WuQMacroCommand* macroCommand, textItem->text()); } else if (indexItem != NULL) { + signalEmitter.emitQListWidgetSignal(listWidget, indexItem->text()); } else { - valueNotFoundErrorMessage = ("For QListWidget \"" + objectErrorMessage = ("For QListWidget \"" + object->objectName() + "\", unable to find item with text \"" + dataValueTwo.toString() @@ -529,6 +563,8 @@ WuQMacroExecutor::runMacroCommand(const WuQMacroCommand* macroCommand, } } + moveMouseToWidget(menu); + WuQMacroSignalEmitter signalEmitter; if (textAction != NULL) { signalEmitter.emitQMenuSignal(menu, @@ -539,7 +575,7 @@ WuQMacroExecutor::runMacroCommand(const WuQMacroCommand* macroCommand, indexAction->text()); } else { - valueNotFoundErrorMessage = ("For QMenu \"" + objectErrorMessage = ("For QMenu \"" + object->objectName() + "\", unable to find action with text \"" + dataValueTwo.toString() @@ -612,6 +648,7 @@ WuQMacroExecutor::runMacroCommand(const WuQMacroCommand* macroCommand, { QPushButton* pushButton = qobject_cast(object); if (pushButton != NULL) { + moveMouseToWidget(pushButton); CaretAssert(dataValueType == WuQMacroDataValueTypeEnum::BOOLEAN); WuQMacroSignalEmitter signalEmitter; signalEmitter.emitQPushButtonSignal(pushButton, @@ -626,6 +663,7 @@ WuQMacroExecutor::runMacroCommand(const WuQMacroCommand* macroCommand, { QRadioButton* radioButton = qobject_cast(object); if (radioButton != NULL) { + moveMouseToWidget(radioButton); CaretAssert(dataValueType == WuQMacroDataValueTypeEnum::BOOLEAN); WuQMacroSignalEmitter signalEmitter; signalEmitter.emitQRadioButtonSignal(radioButton, @@ -640,6 +678,7 @@ WuQMacroExecutor::runMacroCommand(const WuQMacroCommand* macroCommand, { QSlider* slider = qobject_cast(object); if (slider != NULL) { + moveMouseToWidget(slider); CaretAssert(dataValueType == WuQMacroDataValueTypeEnum::INTEGER); WuQMacroSignalEmitter signalEmitter; signalEmitter.emitQSliderSignal(slider, @@ -654,6 +693,7 @@ WuQMacroExecutor::runMacroCommand(const WuQMacroCommand* macroCommand, { QSpinBox* spinBox = qobject_cast(object); if (spinBox != NULL) { + moveMouseToWidget(spinBox); CaretAssert(dataValueType == WuQMacroDataValueTypeEnum::INTEGER); WuQMacroSignalEmitter signalEmitter; signalEmitter.emitQSpinBoxSignal(spinBox, @@ -669,9 +709,23 @@ WuQMacroExecutor::runMacroCommand(const WuQMacroCommand* macroCommand, QTabBar* tabBar = qobject_cast(object); if (tabBar != NULL) { CaretAssert(dataValueType == WuQMacroDataValueTypeEnum::INTEGER); - WuQMacroSignalEmitter signalEmitter; - signalEmitter.emitQTabBarSignal(tabBar, - dataValue.toInt()); + const int32_t tabIndex = dataValue.toInt(); + if (tabBar->isTabEnabled(tabIndex)) { + moveMouseToTabBarTab(tabBar, tabIndex); + WuQMacroSignalEmitter signalEmitter; + signalEmitter.emitQTabBarSignal(tabBar, + tabIndex); + } + else { + objectErrorMessage = ("QTabWidget \"" + + object->objectName() + + "\", tab \"" + + QString::number(tabIndex + 1) + + "\" with text \"" + + tabBar->tabText(tabIndex) + + "\" is disabled"); + } + } else { notFoundFlag = true; @@ -683,9 +737,25 @@ WuQMacroExecutor::runMacroCommand(const WuQMacroCommand* macroCommand, QTabWidget* tabWidget = qobject_cast(object); if (tabWidget != NULL) { CaretAssert(dataValueType == WuQMacroDataValueTypeEnum::INTEGER); - WuQMacroSignalEmitter signalEmitter; - signalEmitter.emitQTabWidgetSignal(tabWidget, - dataValue.toInt()); + QTabBar* tabBar = tabWidget->tabBar(); + CaretAssert(tabBar); + const int32_t tabIndex = dataValue.toInt(); + if (tabWidget->isTabEnabled(tabIndex)) { + moveMouseToTabBarTab(tabBar, + tabIndex); + WuQMacroSignalEmitter signalEmitter; + signalEmitter.emitQTabWidgetSignal(tabWidget, + tabIndex); + } + else { + objectErrorMessage = ("QTabWidget \"" + + object->objectName() + + "\", tab \"" + + QString::number(tabIndex + 1) + + "\" with text \"" + + tabWidget->tabText(tabIndex) + + "\" is disabled"); + } } else { notFoundFlag = true; @@ -696,6 +766,7 @@ WuQMacroExecutor::runMacroCommand(const WuQMacroCommand* macroCommand, { QToolButton* toolButton = qobject_cast(object); if (toolButton != NULL) { + moveMouseToWidget(toolButton); CaretAssert(dataValueType == WuQMacroDataValueTypeEnum::BOOLEAN); WuQMacroSignalEmitter signalEmitter; signalEmitter.emitQToolButtonSignal(toolButton, @@ -715,8 +786,8 @@ WuQMacroExecutor::runMacroCommand(const WuQMacroCommand* macroCommand, + WuQMacroClassTypeEnum::toGuiName(classType)); return false; } - else if ( ! valueNotFoundErrorMessage.isEmpty()) { - errorMessageOut = valueNotFoundErrorMessage; + else if ( ! objectErrorMessage.isEmpty()) { + errorMessageOut = objectErrorMessage; return false; } diff --git a/src/GuiQt/WuQMacroExecutor.h b/src/GuiQt/WuQMacroExecutor.h index 1df096471..1f4ddbb2b 100644 --- a/src/GuiQt/WuQMacroExecutor.h +++ b/src/GuiQt/WuQMacroExecutor.h @@ -27,6 +27,7 @@ #include +class QTabBar; class QWidget; namespace caret { @@ -41,6 +42,14 @@ namespace caret { public: class RunOptions { public: + RunOptions() + : m_secondsDelayBetweenCommands(1.0), + m_showMouseMovementFlag(true), + m_stopOnErrorFlag(true), + m_loopFlag(false) + { + + } RunOptions(const float secondsDelayBetweenCommands, const bool showMouseMovementFlag, const bool loopFlag) @@ -51,13 +60,13 @@ namespace caret { { } - const float m_secondsDelayBetweenCommands; + float m_secondsDelayBetweenCommands; - const bool m_showMouseMovementFlag; + bool m_showMouseMovementFlag; - const bool m_stopOnErrorFlag; + bool m_stopOnErrorFlag; - const bool m_loopFlag; + bool m_loopFlag; }; WuQMacroExecutor(); @@ -78,13 +87,22 @@ namespace caret { // ADD_NEW_METHODS_HERE private: - void moveMouse(QWidget* widget, - const bool highlightFlag) const; + void moveMouseToTabBarTab(QTabBar* tabBar, + const int32_t tabIndex) const; + + void moveMouseToWidget(QObject* moveToObject, + const QRect* objectRect = NULL) const; bool runMacroCommand(const WuQMacroCommand* macroCommand, QObject* object, QString& errorMessageOut) const; + QObject* findObjectByName(const QString& objectName) const; + + mutable RunOptions m_runOptions; + + mutable std::vector m_parentObjects; + // ADD_NEW_MEMBERS_HERE }; diff --git a/src/GuiQt/WuQMacroManager.cxx b/src/GuiQt/WuQMacroManager.cxx index 8ede58f88..e5939f7a7 100644 --- a/src/GuiQt/WuQMacroManager.cxx +++ b/src/GuiQt/WuQMacroManager.cxx @@ -766,6 +766,20 @@ WuQMacroManager::printSupportedWidgetsToTerminal() } } +/** + * Print top level widgets + */ +void +WuQMacroManager::printToLevelWidgetsToTerminal() +{ + std::cout << "Top Level Widgets: " << std::endl; + QWidgetList widgetList = qApp->topLevelWidgets(); + foreach (QWidget* widget, widgetList) { + std::cout << " " << widget->objectName() + << ", " << widget->metaObject()->className() << std::endl; + } + std::cout << std::endl; +} /** * Get the tooltip for the object with the given name * diff --git a/src/GuiQt/WuQMacroManager.h b/src/GuiQt/WuQMacroManager.h index f5a7bf40a..650d98573 100644 --- a/src/GuiQt/WuQMacroManager.h +++ b/src/GuiQt/WuQMacroManager.h @@ -106,6 +106,8 @@ namespace caret { void printSupportedWidgetsToTerminal(); + void printToLevelWidgetsToTerminal(); + void addParentObject(QObject* parentObject); QString getToolTipForObjectName(const QString& objectName) const; diff --git a/src/GuiQt/WuQMacroMenu.cxx b/src/GuiQt/WuQMacroMenu.cxx index b32354525..471cc8f21 100644 --- a/src/GuiQt/WuQMacroMenu.cxx +++ b/src/GuiQt/WuQMacroMenu.cxx @@ -63,9 +63,12 @@ m_windowParent(windowParent) { QMenu* developmentMenu = new QMenu("Development"); - QAction* printAction = developmentMenu->addAction("Development - Print Supported Widgets"); + QAction* printAction = developmentMenu->addAction("Print Supported Widgets"); QObject::connect(printAction, &QAction::triggered, this, &WuQMacroMenu::macroPrintAllSelected); + QAction* printTopLevelWidgetsAction = developmentMenu->addAction("Print Top-Level Widgets"); + QObject::connect(printTopLevelWidgetsAction, &QAction::triggered, + this, &WuQMacroMenu::macroPrintTopLevelWidgets); addSeparator(); addMenu(developmentMenu); @@ -155,4 +158,11 @@ WuQMacroMenu::macroPrintAllSelected() WuQMacroManager::instance()->printSupportedWidgetsToTerminal(); } - +/** + * Print all top level widgets name and class name + */ +void +WuQMacroMenu::macroPrintTopLevelWidgets() +{ + WuQMacroManager::instance()->printToLevelWidgetsToTerminal(); +} \ No newline at end of file diff --git a/src/GuiQt/WuQMacroMenu.h b/src/GuiQt/WuQMacroMenu.h index cee0acd1c..dc536612a 100644 --- a/src/GuiQt/WuQMacroMenu.h +++ b/src/GuiQt/WuQMacroMenu.h @@ -54,6 +54,8 @@ namespace caret { void macroPrintAllSelected(); + void macroPrintTopLevelWidgets(); + private: // ADD_NEW_MEMBERS_HERE diff --git a/src/GuiQt/WuQMacroSignalWatcher.cxx b/src/GuiQt/WuQMacroSignalWatcher.cxx index 517cd0d00..8fb5624b7 100644 --- a/src/GuiQt/WuQMacroSignalWatcher.cxx +++ b/src/GuiQt/WuQMacroSignalWatcher.cxx @@ -255,8 +255,12 @@ WuQMacroSignalWatcher::objectWasDestroyed(QObject* /*obj*/) * Log object destroyed only when NOT debug */ #ifndef NDEBUG - CaretLogWarning("Object was destroyed: " - + m_objectName); + /* disable as need way to disallow this while a window is closing or application exiting */ + const bool allowMessageFlag(false); + if (allowMessageFlag) { + CaretLogWarning("Object was destroyed: " + + m_objectName); + } #endif } -- GitLab From d58419aa24cd0ebfa6a5f8670a4bae3ccb2368f7 Mon Sep 17 00:00:00 2001 From: John Harwell Date: Mon, 17 Dec 2018 16:46:32 -0600 Subject: [PATCH 131/427] Fix compilation error when QOpenGLWidget is used used (instead of QGLWidget). Fixed bug when QOpenGLWidget is used: if QSurfaceFormat::setSamples() is called with a value greater than zero, an OpenGL Invalid Value error occurs when glReadPixels() is called. So, setSamples(0) is used. --- src/GuiQt/BrainOpenGLWidget.cxx | 7 ++++++- src/GuiQt/BrainOpenGLWidget.h | 2 +- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/src/GuiQt/BrainOpenGLWidget.cxx b/src/GuiQt/BrainOpenGLWidget.cxx index fcf742568..81229da03 100644 --- a/src/GuiQt/BrainOpenGLWidget.cxx +++ b/src/GuiQt/BrainOpenGLWidget.cxx @@ -1835,7 +1835,12 @@ BrainOpenGLWidget::initializeDefaultGLFormat() glfmt.setProfile(QSurfaceFormat::CompatibilityProfile); glfmt.setRedBufferSize(8); glfmt.setRenderableType(QSurfaceFormat::OpenGL); - glfmt.setSamples(6); + /* + * Values greater than zero for setSamples() cause an OpenGL error in + * glReadPixels(). + * QtBug-43127 + */ + glfmt.setSamples(0); //6); glfmt.setStereo(false); glfmt.setSwapBehavior(QSurfaceFormat::DoubleBuffer); diff --git a/src/GuiQt/BrainOpenGLWidget.h b/src/GuiQt/BrainOpenGLWidget.h index fa4f3d0e2..daf1ac5be 100644 --- a/src/GuiQt/BrainOpenGLWidget.h +++ b/src/GuiQt/BrainOpenGLWidget.h @@ -72,7 +72,7 @@ namespace caret { class VolumeFile; #ifdef WORKBENCH_USE_QT5_QOPENGL_WIDGET - class BrainOpenGLWidget : public QOpenGLWidget, public EventListenerInterface { + class BrainOpenGLWidget : public QOpenGLWidget, public EventListenerInterface, public WuQMacroMouseEventWidgetInterface { #else class BrainOpenGLWidget : public QGLWidget, public EventListenerInterface, public WuQMacroMouseEventWidgetInterface { #endif -- GitLab From b008993baaceb9f0c86336b6c66319237e65ab02 Mon Sep 17 00:00:00 2001 From: John Harwell Date: Tue, 18 Dec 2018 10:06:07 -0600 Subject: [PATCH 132/427] WB-834: Fix some macro issues including a crash and other problems with recording mouse movement and disable function key on macro dialogs until function key is implemented. --- src/Common/WuQMacroCommand.cxx | 3 ++- src/GuiQt/WuQMacroAttributesDialog.cxx | 5 +++++ src/GuiQt/WuQMacroCreateDialog.cxx | 5 +++++ src/GuiQt/WuQMacroEditorDialog.cxx | 5 +++++ src/GuiQt/WuQMacroManager.cxx | 3 ++- 5 files changed, 19 insertions(+), 2 deletions(-) diff --git a/src/Common/WuQMacroCommand.cxx b/src/Common/WuQMacroCommand.cxx index 421e4411f..bdbb4055f 100644 --- a/src/Common/WuQMacroCommand.cxx +++ b/src/Common/WuQMacroCommand.cxx @@ -146,7 +146,8 @@ m_dataValue((int)0), m_dataValueTwo(""), m_macroMouseEvent(mouseEventInfo) { - m_dataType = WuQMacroDataValueTypeEnum::MOUSE; + m_dataType = WuQMacroDataValueTypeEnum::MOUSE; + m_dataTypeTwo = WuQMacroDataValueTypeEnum::STRING; setModified(); } diff --git a/src/GuiQt/WuQMacroAttributesDialog.cxx b/src/GuiQt/WuQMacroAttributesDialog.cxx index f6007cb86..2a57ec146 100644 --- a/src/GuiQt/WuQMacroAttributesDialog.cxx +++ b/src/GuiQt/WuQMacroAttributesDialog.cxx @@ -79,6 +79,11 @@ m_macroWasModifiedFlag(false) m_macroDescriptionTextEdit->setFixedHeight(100); m_macroDescriptionTextEdit->setPlainText(m_macro->getDescription()); + /* + * DISABLE FUNCTION KEY UNTIL IMPLEMENTED + */ + m_macroFunctionKeyLineEdit->setEnabled(false); + QGridLayout* gridLayout = new QGridLayout(); int row = 0; gridLayout->addWidget(nameLabel, row, 0); diff --git a/src/GuiQt/WuQMacroCreateDialog.cxx b/src/GuiQt/WuQMacroCreateDialog.cxx index d5fa932e3..3daac49c8 100644 --- a/src/GuiQt/WuQMacroCreateDialog.cxx +++ b/src/GuiQt/WuQMacroCreateDialog.cxx @@ -73,6 +73,11 @@ WuQMacroCreateDialog::WuQMacroCreateDialog(QWidget* parent) m_macroDescriptionTextEdit = new QPlainTextEdit(); m_macroDescriptionTextEdit->setFixedHeight(100); + /* + * DISABLE FUNCTION KEY UNTIL IMPLEMENTED + */ + m_macroFunctionKeyLineEdit->setEnabled(false); + m_macroGroupComboBox = new QComboBox(); for (auto mg : m_macroGroups) { m_macroGroupComboBox->addItem(mg->getName()); diff --git a/src/GuiQt/WuQMacroEditorDialog.cxx b/src/GuiQt/WuQMacroEditorDialog.cxx index 488589778..5b23da031 100644 --- a/src/GuiQt/WuQMacroEditorDialog.cxx +++ b/src/GuiQt/WuQMacroEditorDialog.cxx @@ -83,6 +83,11 @@ m_macroWasModifiedFlag(false) m_macroDescriptionTextEdit->setFixedHeight(60); m_macroDescriptionTextEdit->setPlainText(m_macro->getDescription()); + /* + * DISABLE FUNCTION KEY UNTIL IMPLEMENTED + */ + m_macroFunctionKeyLineEdit->setEnabled(false); + m_tableWidget = createTableWidget(); QGridLayout* gridLayout = new QGridLayout(); diff --git a/src/GuiQt/WuQMacroManager.cxx b/src/GuiQt/WuQMacroManager.cxx index e5939f7a7..adc86a410 100644 --- a/src/GuiQt/WuQMacroManager.cxx +++ b/src/GuiQt/WuQMacroManager.cxx @@ -370,8 +370,9 @@ WuQMacroManager::addMouseEventToRecording(QWidget* widget, /* * Only track move events if a button is down + * Note: Use "buttons()" mask, not button() */ - if (me->button() == Qt::NoButton) { + if (me->buttons() == Qt::NoButton) { validMouseEventFlag = false; } break; -- GitLab From 2b5ab1574428e316093e758c6c2eb3eaf58e3266 Mon Sep 17 00:00:00 2001 From: John Harwell Date: Tue, 18 Dec 2018 16:43:40 -0600 Subject: [PATCH 133/427] WB-834: When playing a macro that has recorded mouse movement (such as surface rotation), show the mouse movement while playing the macro. --- src/GuiQt/WuQMacroExecutor.cxx | 86 ++++++++++++++++++++++++++++++---- src/GuiQt/WuQMacroExecutor.h | 15 +++++- 2 files changed, 91 insertions(+), 10 deletions(-) diff --git a/src/GuiQt/WuQMacroExecutor.cxx b/src/GuiQt/WuQMacroExecutor.cxx index 9d51e6d8a..0504d9cdd 100644 --- a/src/GuiQt/WuQMacroExecutor.cxx +++ b/src/GuiQt/WuQMacroExecutor.cxx @@ -94,8 +94,11 @@ WuQMacroExecutor::moveMouseToTabBarTab(QTabBar* tabBar, moveMouseToWidget(tabBar); } else { - moveMouseToWidget(tabBar, - &tabRect); + moveMouseToWidgetImplementation(tabBar, + -1, + -1, + &tabRect, + true); } } @@ -104,12 +107,72 @@ WuQMacroExecutor::moveMouseToTabBarTab(QTabBar* tabBar, * * @param moveToObject * Object to where mouse should be moved + * @param highlightFlag + * If true, highlight mouse location by moving mouse in + * a circular orientation + */ +void +WuQMacroExecutor::moveMouseToWidget(QObject* moveToObject, + const bool highlightFlag) const +{ + moveMouseToWidgetImplementation(moveToObject, + -1, + -1, + NULL, + highlightFlag); +} + +/** + * Move the mouse to and around the given widget + * at the given X/Y + * + * @param moveToObject + * Object to where mouse should be moved + * @param x + * Move to this X in widget + * @param y + * Move to this Y in widget + * @param highlightFlag + * If true, highlight mouse location by moving mouse in + * a circular orientation + */ +void +WuQMacroExecutor::moveMouseToWidgetXY(QObject* moveToObject, + const int x, + const int y, + const bool highlightFlag) const +{ + moveMouseToWidgetImplementation(moveToObject, + x, + y, + NULL, + highlightFlag); +} + + +/** + * Move the mouse around the given widget. If the given X/Y are + * non-negative, mouse is moved to that location instead of center + * of widget + * + * @param moveToObject + * Object to where mouse should be moved + * @param x + * Move to this X in widget + * @param y + * Move to this Y in widget * @param objectRect * Optional, if not NULL, rectangle of object used for positioning mouse + * @param highlightFlag + * If true, highlight mouse location by moving mouse in + * a circular orientation */ void -WuQMacroExecutor::moveMouseToWidget(QObject* moveToObject, - const QRect* objectRect) const +WuQMacroExecutor::moveMouseToWidgetImplementation(QObject* moveToObject, + const int x, + const int y, + const QRect* objectRect, + const bool highlightFlag) const { if ( ! m_runOptions.m_showMouseMovementFlag) { return; @@ -147,13 +210,15 @@ WuQMacroExecutor::moveMouseToWidget(QObject* moveToObject, const QRect widgetRect = ((objectRect != NULL) ? *objectRect : moveToWidget->rect()); - const QPoint widgetCenter = widgetRect.center(); - const QPoint windowPoint = moveToWidget->mapToGlobal(widgetCenter); - + QPoint widgetPoint = widgetRect.center(); + if ((x >= 0) && (y >= 0)) { + widgetPoint.setX(x); + widgetPoint.setY(y); + } + const QPoint windowPoint = moveToWidget->mapToGlobal(widgetPoint); QCursor::setPos(windowPoint); SystemUtilities::sleepSeconds(0.025); - const bool highlightFlag(true); if (highlightFlag) { const float radius = 15.0; for (float angle = 0.0; angle < 6.28; angle += 0.314) { @@ -621,6 +686,11 @@ WuQMacroExecutor::runMacroCommand(const WuQMacroCommand* macroCommand, break; } + moveMouseToWidgetXY(widget, + adjustedLocalX, + adjustedLocalY, + false); + QMouseEvent qtMouseEvent(qtEventType, QPointF(adjustedLocalX, adjustedLocalY), diff --git a/src/GuiQt/WuQMacroExecutor.h b/src/GuiQt/WuQMacroExecutor.h index 1f4ddbb2b..55ceda07d 100644 --- a/src/GuiQt/WuQMacroExecutor.h +++ b/src/GuiQt/WuQMacroExecutor.h @@ -89,9 +89,20 @@ namespace caret { private: void moveMouseToTabBarTab(QTabBar* tabBar, const int32_t tabIndex) const; - + void moveMouseToWidget(QObject* moveToObject, - const QRect* objectRect = NULL) const; + const bool highlightFlag = true) const; + + void moveMouseToWidgetXY(QObject* moveToObject, + const int x, + const int y, + const bool highlightFlag = true) const; + + void moveMouseToWidgetImplementation(QObject* moveToObject, + const int x, + const int y, + const QRect* objectRect = NULL, + const bool hightlightFlag = false) const; bool runMacroCommand(const WuQMacroCommand* macroCommand, QObject* object, -- GitLab From 0b3b3e7500472fb9e12d7792e7ceb00e125b1eed Mon Sep 17 00:00:00 2001 From: John Harwell Date: Wed, 19 Dec 2018 15:48:06 -0600 Subject: [PATCH 134/427] Added an error message to CaretHttpResponse so that it can include network error information such as "connection closed". Display this error information when encountered during BALSA file uploading. --- src/Common/CaretHttpManager.cxx | 21 ++++++++++- src/Common/CaretHttpManager.h | 1 + src/GuiQt/BalsaDatabaseManager.cxx | 57 ++++++++++++++++++++++++------ src/GuiQt/BalsaDatabaseManager.h | 7 ++-- 4 files changed, 72 insertions(+), 14 deletions(-) diff --git a/src/Common/CaretHttpManager.cxx b/src/Common/CaretHttpManager.cxx index d45d7ad98..696ad5c02 100644 --- a/src/Common/CaretHttpManager.cxx +++ b/src/Common/CaretHttpManager.cxx @@ -136,14 +136,25 @@ logHeadersFromReply(const QNetworkReply& reply, { AString infoText; infoText.appendWithNewLine("Reply " + caretHttpRequestToName(caretHttpRequest) + " URL (" + QString::number(caretHttpResponse.m_responseCode) + ") Header: "); + bool errorFlag(false); if ( ! caretHttpResponse.m_responseCodeValid) { infoText.appendWithNewLine("RESPONSE CODE IS NOT VALID."); + errorFlag = true; } const QNetworkReply::NetworkError networkErrorCode = reply.error(); if (networkErrorCode != QNetworkReply::NoError) { infoText.appendWithNewLine("Network Error Code (See QNetworkReply::NetworkError for description): " - + QString::number(static_cast(networkErrorCode))); + + QString::number(static_cast(networkErrorCode)) + + "\nDescription: " + + reply.errorString()); + errorFlag = true; } + + if (errorFlag) { + CaretLogWarning(infoText); + return; + } + QList readHeaderList = reply.rawHeaderList(); const int numItems = readHeaderList.size(); if (numItems > 0) { @@ -354,6 +365,7 @@ void CaretHttpManager::httpRequestPrivate(const CaretHttpRequest &request, Caret response.m_responseCode = -1; response.m_responseCodeValid = false; response.m_headers.clear(); + response.m_errorMessage.clear(); const QVariant responseCodeVariant = myReply->attribute(QNetworkRequest::HttpStatusCodeAttribute); if ( ! responseCodeVariant.isNull()) { response.m_responseCode = myReply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(); @@ -384,6 +396,13 @@ void CaretHttpManager::httpRequestPrivate(const CaretHttpRequest &request, Caret request, response); } + if (response.m_responseCode != 200) { + AString s("QNetworkReply::NetworkError Code=" + + AString::number((int)myReply->error()) + + ", Error=" + + myReply->errorString()); + response.m_errorMessage = s; + } QByteArray myBody = myReply->readAll(); int64_t mySize = myBody.size(); diff --git a/src/Common/CaretHttpManager.h b/src/Common/CaretHttpManager.h index 89585f130..bd7688132 100644 --- a/src/Common/CaretHttpManager.h +++ b/src/Common/CaretHttpManager.h @@ -76,6 +76,7 @@ namespace caret { bool m_responseCodeValid; QUrl m_redirectionUrl; bool m_redirectionUrlValid; + AString m_errorMessage; std::map m_headers; // map so that newer values replace older values }; diff --git a/src/GuiQt/BalsaDatabaseManager.cxx b/src/GuiQt/BalsaDatabaseManager.cxx index 957d3494a..1cd25b696 100644 --- a/src/GuiQt/BalsaDatabaseManager.cxx +++ b/src/GuiQt/BalsaDatabaseManager.cxx @@ -177,6 +177,9 @@ BalsaDatabaseManager::login(const AString& databaseURL, errorMessageOut = ("Login has failed.\n" "HTTP Code: " + AString::number(loginResponse.m_responseCode) + " Content: " + responseContent); + if (loginResponse.m_responseCode < 0) { + errorMessageOut.appendWithNewLine(loginResponse.m_errorMessage); + } logout(); return false; @@ -303,9 +306,10 @@ BalsaDatabaseManager::uploadFileWithCaretHttpManager(const AString& uploadURL, return verifyUploadFileResponse(uploadResponse.m_headers, - responseContentOut, - uploadResponse.m_responseCode, - errorMessageOut); + responseContentOut, + uploadResponse.m_responseCode, + uploadResponse.m_errorMessage, + errorMessageOut); } /** @@ -317,6 +321,8 @@ BalsaDatabaseManager::uploadFileWithCaretHttpManager(const AString& uploadURL, * Content from the response. * @param responseHttpCode * HTTP code from response. + * @param responseErrorMessage + * * @param errorMessageOut * Contains description of error. * @return @@ -324,9 +330,10 @@ BalsaDatabaseManager::uploadFileWithCaretHttpManager(const AString& uploadURL, */ bool BalsaDatabaseManager::verifyUploadFileResponse(const std::map& responseHeaders, - const AString& responseContent, - const int32_t responseHttpCode, - AString& errorMessageOut) const + const AString& responseContent, + const int32_t responseHttpCode, + const AString& responseErrorMessage, + AString& errorMessageOut) const { if (responseHttpCode != 200) { if (responseHttpCode == 403) { @@ -336,7 +343,20 @@ BalsaDatabaseManager::verifyUploadFileResponse(const std::map& } else { errorMessageOut = ("Upload failed. Http Code=" - + AString::number(responseHttpCode)); + + AString::number(responseHttpCode) + + ".\n" + + responseErrorMessage); +// if (responseHttpCode < 0) { +// const QString s(responseContent.isEmpty() +// ? "Response content is empty !!!" +// : ("Response content:\n" +// + responseContent)); +// CaretLogSevere("Invalid http response=" +// + AString::number(responseHttpCode) +// + "\n" +// + s +// + "\n"); +// } } return false; } @@ -619,8 +639,10 @@ BalsaDatabaseManager::processUploadedFile(SceneFile* sceneFile, if (uploadResponse.m_responseCode != 200) { errorMessageOut = ("Process Upload failed code: " - + QString::number(uploadResponse.m_responseCode) - + "\n"); + + QString::number(uploadResponse.m_responseCode)); + if (uploadResponse.m_responseCode < 0) { + errorMessageOut.appendWithNewLine(uploadResponse.m_errorMessage); + } return false; } @@ -879,6 +901,9 @@ BalsaDatabaseManager::getSceneIDs(const int32_t numberOfSceneIDs, errorMessageOut = ("Requesting Scene IDs failed with HTTP code=" + AString::number(response.m_responseCode) + ". This error may be caused by failure to agree to data use terms."); + if (response.m_responseCode < 0) { + errorMessageOut.appendWithNewLine(response.m_errorMessage); + } return false; } @@ -978,6 +1003,9 @@ BalsaDatabaseManager::requestStudyID(const AString& databaseURL, errorMessageOut = ("Requesting study ID failed with HTTP code=" + AString::number(studyResponse.m_responseCode) + ". This error may be caused by failure to agree to data use terms."); + if (studyResponse.m_responseCode < 0) { + errorMessageOut.appendWithNewLine(studyResponse.m_errorMessage); + } return false; } @@ -1104,12 +1132,15 @@ BalsaDatabaseManager::getUserRoles(BalsaUserRoles& userRolesOut, CaretHttpManager::httpRequest(caretRequest, studyResponse); if (m_debugFlag) { - std::cout << "Request rolese response Code: " << studyResponse.m_responseCode << std::endl; + std::cout << "Request roles response Code: " << studyResponse.m_responseCode << std::endl; } if (studyResponse.m_responseCode != 200) { errorMessageOut = ("Requesting roles failed with HTTP code=" + AString::number(studyResponse.m_responseCode)); + if (studyResponse.m_responseCode < 0) { + errorMessageOut.appendWithNewLine(studyResponse.m_errorMessage); + } return false; } @@ -1213,6 +1244,9 @@ BalsaDatabaseManager::getStudyExtractDirectoryPrefix(const AString& studyID, if (studyResponse.m_responseCode != 200) { errorMessageOut = ("Requesting extract directory failed with HTTP code=" + AString::number(studyResponse.m_responseCode)); + if (studyResponse.m_responseCode < 0) { + errorMessageOut.appendWithNewLine(studyResponse.m_errorMessage); + } return false; } @@ -1291,6 +1325,9 @@ BalsaDatabaseManager::getAllStudyInformation(std::vector& errorMessageOut = ("Requesting all study information failed with HTTP code=" + AString::number(idResponse.m_responseCode) + ". This error may be caused by failure to agree to data use terms."); + if (idResponse.m_responseCode < 0) { + errorMessageOut.appendWithNewLine(idResponse.m_errorMessage); + } return false; } diff --git a/src/GuiQt/BalsaDatabaseManager.h b/src/GuiQt/BalsaDatabaseManager.h index f94cc98c2..a664a8635 100644 --- a/src/GuiQt/BalsaDatabaseManager.h +++ b/src/GuiQt/BalsaDatabaseManager.h @@ -140,9 +140,10 @@ namespace caret { AString& errorMessageOut); bool verifyUploadFileResponse(const std::map& responseHeaders, - const AString& responseContent, - const int32_t responseHttpCode, - AString& errorMessageOut) const; + const AString& responseContent, + const int32_t responseHttpCode, + const AString& responseErrorMessage, + AString& errorMessageOut) const; AString getHeaderValue(const CaretHttpResponse& httpResponse, const AString& headerName) const; -- GitLab From 9328c311361e800fc0be6682c45092d2d8045e0f Mon Sep 17 00:00:00 2001 From: John Harwell Date: Wed, 19 Dec 2018 16:05:11 -0600 Subject: [PATCH 135/427] Include the URL in the error message when a HTTP request fails. --- src/Common/CaretHttpManager.cxx | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/Common/CaretHttpManager.cxx b/src/Common/CaretHttpManager.cxx index 696ad5c02..6772a7b46 100644 --- a/src/Common/CaretHttpManager.cxx +++ b/src/Common/CaretHttpManager.cxx @@ -400,7 +400,9 @@ void CaretHttpManager::httpRequestPrivate(const CaretHttpRequest &request, Caret AString s("QNetworkReply::NetworkError Code=" + AString::number((int)myReply->error()) + ", Error=" - + myReply->errorString()); + + myReply->errorString() + + "\nURL=" + + myUrl.toString(QUrl::None)); response.m_errorMessage = s; } -- GitLab From 4ee7e9a4bbc56a866c547f6d56141b831180b1c6 Mon Sep 17 00:00:00 2001 From: Tim Coalson Date: Fri, 28 Dec 2018 17:46:36 -0600 Subject: [PATCH 136/427] add L2 norm to reduction types --- src/Common/ReductionEnum.cxx | 1 + src/Common/ReductionEnum.h | 1 + src/Common/ReductionOperation.cxx | 14 ++++++++++++++ 3 files changed, 16 insertions(+) diff --git a/src/Common/ReductionEnum.cxx b/src/Common/ReductionEnum.cxx index ad3442b62..f753ccfb4 100644 --- a/src/Common/ReductionEnum.cxx +++ b/src/Common/ReductionEnum.cxx @@ -81,6 +81,7 @@ ReductionEnum::initialize() enumData.push_back(ReductionEnum(VARIANCE, "VARIANCE", "the variance of the data")); enumData.push_back(ReductionEnum(TSNR, "TSNR", "mean divided by sample standard deviation (N-1 denominator)")); enumData.push_back(ReductionEnum(COV, "COV", "sample standard deviation (N-1 denominator) divided by mean")); + enumData.push_back(ReductionEnum(L2NORM, "L2NORM", "square root of sum of squares")); enumData.push_back(ReductionEnum(MEDIAN, "MEDIAN", "the median of the data")); enumData.push_back(ReductionEnum(MODE, "MODE", "the mode of the data")); enumData.push_back(ReductionEnum(COUNT_NONZERO, "COUNT_NONZERO", "the number of nonzero elements in the data")); diff --git a/src/Common/ReductionEnum.h b/src/Common/ReductionEnum.h index a4b8f895b..e7fe0f9d7 100644 --- a/src/Common/ReductionEnum.h +++ b/src/Common/ReductionEnum.h @@ -52,6 +52,7 @@ public: VARIANCE, TSNR, COV, + L2NORM, PRODUCT, MEDIAN, MODE, diff --git a/src/Common/ReductionOperation.cxx b/src/Common/ReductionOperation.cxx index 6fe6784b2..ce98a2256 100644 --- a/src/Common/ReductionOperation.cxx +++ b/src/Common/ReductionOperation.cxx @@ -83,6 +83,12 @@ float ReductionOperation::reduce(const float* data, const int64_t& numElems, con } } } + case ReductionEnum::L2NORM: + { + double sum = 0.0; + for (int64_t i = 0; i < numElems; ++i) sum += data[i] * data[i]; + return sqrt(sum); + } case ReductionEnum::PRODUCT: { double prod = 1.0; @@ -183,6 +189,7 @@ float ReductionOperation::reduce(const float* data, const int64_t& numElems, con return count; } } + CaretAssertMessage(false, "unhandled reduction type"); return 0.0f; } @@ -387,6 +394,12 @@ float ReductionOperation::reduceWeighted(const float* data, const float* weights return 0.0f; } } + case ReductionEnum::L2NORM: + { + double sum = 0.0; + for (int64_t i = 0; i < numElems; ++i) sum += weights[i] * data[i] * data[i]; + return sqrt(sum);//if all weights are 1, this should match unweighted, so don't divide by sum of weights + } case ReductionEnum::MEDIAN: { vector toSort; @@ -446,6 +459,7 @@ float ReductionOperation::reduceWeighted(const float* data, const float* weights return bestval; } } + CaretAssertMessage(false, "unhandled reduction type"); return 0.0f; } -- GitLab From 34ae69344b8b9d08b3ade05ddf6e08f7c5fb9144 Mon Sep 17 00:00:00 2001 From: Tim Coalson Date: Fri, 4 Jan 2019 15:54:53 -0600 Subject: [PATCH 137/427] update some cifti documentation/examples --- src/Cifti/QUICKSTART | 2 +- src/Cifti/examples/datatype.cxx | 63 +++++++++++++++++++++++++++++++++ src/Cifti/examples/rewrite.cxx | 2 +- 3 files changed, 65 insertions(+), 2 deletions(-) create mode 100644 src/Cifti/examples/datatype.cxx diff --git a/src/Cifti/QUICKSTART b/src/Cifti/QUICKSTART index 49e4c3f71..a3e3e54f7 100644 --- a/src/Cifti/QUICKSTART +++ b/src/Cifti/QUICKSTART @@ -1,6 +1,6 @@ The entry point for reading and writing Cifti data is the object CiftiFile. To get a handle to a Cifti File, use the following syntax: CiftiFile cf("testciftifile.dtseries.nii"), cf2;//starts on-disk reading, and makes an uninitialized second object -cf2.setWritingFile(testoutcifti.dtseries.nii");//prepare on-disk writing mode +cf2.setWritingFile("testoutcifti.dtseries.nii");//prepare on-disk writing mode //cf.convertToInMemory();//if you want to read entire file into memory now CiftiFile gives access to the file in two parts, the Cifti XML, and the Cifti Matrix data. To access the data use the following functions: diff --git a/src/Cifti/examples/datatype.cxx b/src/Cifti/examples/datatype.cxx new file mode 100644 index 000000000..b9c1124b9 --- /dev/null +++ b/src/Cifti/examples/datatype.cxx @@ -0,0 +1,63 @@ +/*LICENSE_START*/ +/* + * Copyright (C) 2017 Washington University School of Medicine + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +/*LICENSE_END*/ + +#include "CaretException.h" +#include "CiftiFile.h" + +#include +#include + +using namespace std; +using namespace cifti; + +/**\file datatype.cxx +This program reads a Cifti file from argv[1], and writes it out to argv[2] using 8-bit unsigned integer and data scaling. +It uses a single CiftiFile object to do this, for simplicity - to see how to do something similar with two objects, +which is more relevant for how you would do processing on cifti files, see rewrite.cxx. + +\include datatype.cxx +*/ + +int main(int argc, char** argv) +{ + if (argc < 3) + { + cout << "usage: " << argv[0] << " " << endl; + cout << " rewrite the input cifti file to the output filename, using uint8 and data scaling." << endl; + return 1; + } + try + { + CiftiFile inputFile(argv[1]);//on-disk reading by default + inputFile.setWritingDataTypeAndScaling(NIFTI_TYPE_UINT8, -1.0, 6.0);//tells it to use this datatype to best represent this specified range of values [-1.0, 6.0] whenever this instance is written + inputFile.writeFile(argv[2]);//if the output filename is the same as the input filename, CiftiFile actually detects this and reads the input into memory first + //otherwise, it will read and write one row at a time, using very little memory + //inputFile.setWritingDataTypeNoScaling(NIFTI_TYPE_FLOAT32);//this is how you would revert back to writing as float32 without rescaling + } catch (CaretException& e) { + cerr << "Caught CaretException: " + e.whatString() << endl; + return 1; + } + return 0; +} diff --git a/src/Cifti/examples/rewrite.cxx b/src/Cifti/examples/rewrite.cxx index 55340f367..df0e637ed 100644 --- a/src/Cifti/examples/rewrite.cxx +++ b/src/Cifti/examples/rewrite.cxx @@ -63,7 +63,7 @@ int main(int argc, char** argv) } outputFile.writeFile(argv[2]);//because we called setWritingFile with this filename (and default cifti version), this will return immediately //NOTE: if you call writeFile with a different writing version (takes its default from CiftiVersion constructor) than setWritingFile, it will rewrite the entire file after reading it into memory - } catch (CiftiException& e) { + } catch (CaretException& e) { cerr << "Caught CaretException: " + e.whatString() << endl; return 1; } -- GitLab From 763d0a023cf6274029ed8a8e394367ea15b9b25e Mon Sep 17 00:00:00 2001 From: John Harwell Date: Thu, 24 Jan 2019 09:21:21 -0600 Subject: [PATCH 138/427] Resolve WB-839 Tile tabs configuration lost if scene saved without tile tabs enabled: The custom configuration was only saved to scenes if tile tabs was enabled. Now the custom configuration is always saved to the scene. --- src/Brain/BrowserWindowContent.cxx | 37 +++++++++++++++--------------- 1 file changed, 18 insertions(+), 19 deletions(-) diff --git a/src/Brain/BrowserWindowContent.cxx b/src/Brain/BrowserWindowContent.cxx index fe12a33f5..3463f7c53 100644 --- a/src/Brain/BrowserWindowContent.cxx +++ b/src/Brain/BrowserWindowContent.cxx @@ -426,26 +426,25 @@ BrowserWindowContent::saveToScene(const SceneAttributes* sceneAttributes, m_sceneAssistant->saveMembers(sceneAttributes, sceneClass); - if (m_tileTabsEnabled) { - sceneClass->addString("m_customTileTabsConfigurationLatest", - m_customTileTabsConfiguration->encodeInXML()); + sceneClass->addString("m_customTileTabsConfigurationLatest", + m_customTileTabsConfiguration->encodeInXML()); + + /* + * Add a tile tabs version one so older versions of wb_view + * may still load the scene correctly + */ + sceneClass->addString("m_customTileTabsConfiguration", + m_customTileTabsConfiguration->encodeVersionInXML(1)); + + /* + * Write the tile tabs configuration a second time using + * the old name 'm_sceneTileTabsConfiguration'. This will + * allow the previous version of Workbench to display + * tile tabs correctly. + */ + sceneClass->addString("m_sceneTileTabsConfiguration", + m_customTileTabsConfiguration->encodeVersionInXML(1)); - /* - * Add a tile tabs version one so older versions of wb_view - * may still load the scene correctly - */ - sceneClass->addString("m_customTileTabsConfiguration", - m_customTileTabsConfiguration->encodeVersionInXML(1)); - - /* - * Write the tile tabs configuration a second time using - * the old name 'm_sceneTileTabsConfiguration'. This will - * allow the previous version of Workbench to display - * tile tabs correctly. - */ - sceneClass->addString("m_sceneTileTabsConfiguration", - m_customTileTabsConfiguration->encodeVersionInXML(1)); - } sceneClass->addChild(new SceneIntegerArray("m_sceneTabIndices", m_sceneTabIndices)); -- GitLab From 37fada6286c3002cb27357a6b37f6b79587dc5d5 Mon Sep 17 00:00:00 2001 From: John Harwell Date: Fri, 25 Jan 2019 15:08:48 -0600 Subject: [PATCH 139/427] The message "setNativeLocks failed: "Function not implemented" was being emitted on the cluster when wb_command is run. It is produced by Qt's file locking software, probably related to Preferences. There was one instance that caused Preferences to "sync" (write to preferences) when the preferences were being initialized and this has been fixed. In addition all of the "set" methods in preferences were updated to immediately return if the new value matched the cached preferences value. --- src/Common/BackgroundAndForegroundColors.cxx | 59 +++++++++++++ src/Common/BackgroundAndForegroundColors.h | 2 + src/Common/CaretPreferences.cxx | 92 +++++++++++++++++++- 3 files changed, 150 insertions(+), 3 deletions(-) diff --git a/src/Common/BackgroundAndForegroundColors.cxx b/src/Common/BackgroundAndForegroundColors.cxx index 062196b00..30eb6ff63 100644 --- a/src/Common/BackgroundAndForegroundColors.cxx +++ b/src/Common/BackgroundAndForegroundColors.cxx @@ -78,6 +78,65 @@ BackgroundAndForegroundColors::operator=(const BackgroundAndForegroundColors& ob return *this; } +/** + * Equality operator + * + * @param obj + * Compare 'this' to 'obj' for equality + * @return + * True if 'this' is equal to 'obj', else false. + */ +bool +BackgroundAndForegroundColors::operator==(const BackgroundAndForegroundColors& obj) const +{ + bool equalFlag(true); + + for (int32_t i = 0; i < 3; i++) { + if (m_colorForegroundAll[i] != obj.m_colorForegroundAll[i]) { + equalFlag = false; + break; + } + if (m_colorBackgroundAll[i] !=obj.m_colorBackgroundAll[i]) { + equalFlag = false; + break; + } + if (m_colorForegroundChart[i] != obj.m_colorForegroundChart[i]) { + equalFlag = false; + break; + } + if (m_colorBackgroundChart[i] != obj.m_colorBackgroundChart[i]) { + equalFlag = false; + break; + } + if (m_colorForegroundSurface[i] != obj.m_colorForegroundSurface[i]) { + equalFlag = false; + break; + } + if (m_colorBackgroundSurface[i] != obj.m_colorBackgroundSurface[i]) { + equalFlag = false; + break; + } + if (m_colorForegroundVolume[i] != obj.m_colorForegroundVolume[i]) { + equalFlag = false; + break; + } + if (m_colorBackgroundVolume[i] != obj.m_colorBackgroundVolume[i]) { + equalFlag = false; + break; + } + if (m_colorChartMatrixGridLines[i] != obj.m_colorChartMatrixGridLines[i]) { + equalFlag = false; + break; + } + if (m_colorChartHistogramThreshold[i] != obj.m_colorChartHistogramThreshold[i]) { + equalFlag = false; + break; + } + } + + return equalFlag; +} + /** * Helps with copying an object of this type. * @param obj diff --git a/src/Common/BackgroundAndForegroundColors.h b/src/Common/BackgroundAndForegroundColors.h index a772d6ad2..f77ac1d95 100644 --- a/src/Common/BackgroundAndForegroundColors.h +++ b/src/Common/BackgroundAndForegroundColors.h @@ -38,6 +38,8 @@ namespace caret { BackgroundAndForegroundColors& operator=(const BackgroundAndForegroundColors& obj); + bool operator==(const BackgroundAndForegroundColors& obj) const; + void reset(); // ADD_NEW_METHODS_HERE diff --git a/src/Common/CaretPreferences.cxx b/src/Common/CaretPreferences.cxx index 575703650..8acb470cc 100644 --- a/src/Common/CaretPreferences.cxx +++ b/src/Common/CaretPreferences.cxx @@ -637,6 +637,10 @@ CaretPreferences::getUserBackgroundAndForegroundColors() void CaretPreferences::setUserBackgroundAndForegroundColors(const BackgroundAndForegroundColors& colors) { + if (this->userColors == colors) { + return; + } + /* * "in memory" colors */ @@ -912,6 +916,10 @@ CaretPreferences::getLoggingLevel() const void CaretPreferences::setLoggingLevel(const LogLevelEnum::Enum loggingLevel) { + if (this->loggingLevel == loggingLevel) { + return; + } + this->loggingLevel = loggingLevel; const AString name = LogLevelEnum::toName(this->loggingLevel); @@ -945,6 +953,10 @@ CaretPreferences::getOpenDrawingMethod() const void CaretPreferences::setOpenGLDrawingMethod(const OpenGLDrawingMethodEnum::Enum openGLDrawingMethod) { + if (this->openGLDrawingMethod == openGLDrawingMethod) { + return; + } + this->openGLDrawingMethod = openGLDrawingMethod; this->setString(NAME_OPENGL_DRAWING_METHOD, OpenGLDrawingMethodEnum::toName(this->openGLDrawingMethod)); @@ -968,6 +980,10 @@ CaretPreferences::getManageFilesViewFileType() const void CaretPreferences::setManageFilesViewFileType(const SpecFileDialogViewFilesTypeEnum::Enum manageFilesViewFileType) { + if (this->manageFilesViewFileType == manageFilesViewFileType) { + return; + } + this->manageFilesViewFileType = manageFilesViewFileType; this->setString(NAME_MANAGE_FILES_VIEW_FILE_TYPE, SpecFileDialogViewFilesTypeEnum::toName(this->manageFilesViewFileType)); @@ -991,6 +1007,10 @@ CaretPreferences::isShowSurfaceIdentificationSymbols() const void CaretPreferences::setShowSurfaceIdentificationSymbols(const bool showSymbols) { + if (this->showSurfaceIdentificationSymbols == showSymbols) { + return; + } + this->showSurfaceIdentificationSymbols = showSymbols; this->setBoolean(NAME_SHOW_SURFACE_IDENTIFICATION_SYMBOLS, this->showSurfaceIdentificationSymbols); @@ -1014,6 +1034,10 @@ CaretPreferences::isShowVolumeIdentificationSymbols() const void CaretPreferences::setShowVolumeIdentificationSymbols(const bool showSymbols) { + if (this->showVolumeIdentificationSymbols == showSymbols) { + return; + } + this->showVolumeIdentificationSymbols = showSymbols; this->setBoolean(NAME_SHOW_VOLUME_IDENTIFICATION_SYMBOLS, this->showVolumeIdentificationSymbols); @@ -1037,6 +1061,10 @@ CaretPreferences::isDynamicConnectivityDefaultedOn() const void CaretPreferences::setDynamicConnectivityDefaultedOn(const bool defaultedOn) { + if (this->dynamicConnectivityDefaultedOn == defaultedOn) { + return; + } + this->dynamicConnectivityDefaultedOn = defaultedOn; this->setBoolean(NAME_DYNAMIC_CONNECTIVITY_ON, defaultedOn); @@ -1061,6 +1089,10 @@ CaretPreferences::getImageCaptureMethod() const void CaretPreferences::setImageCaptureMethod(const ImageCaptureMethodEnum::Enum imageCaptureMethod) { + if (this->imageCaptureMethod == imageCaptureMethod) { + return; + } + this->imageCaptureMethod = imageCaptureMethod; this->setString(NAME_IMAGE_CAPTURE_METHOD, ImageCaptureMethodEnum::toName(this->imageCaptureMethod)); @@ -1085,6 +1117,10 @@ CaretPreferences::isRemoteFilePasswordSaved() void CaretPreferences::setRemoteFilePasswordSaved(const bool saveRemotePasswordToPreferences) { + if (this->remoteFileLoginSaved == saveRemotePasswordToPreferences) { + return; + } + this->remoteFileLoginSaved = saveRemotePasswordToPreferences; this->setBoolean(NAME_REMOTE_FILE_LOGIN_SAVED, this->remoteFileLoginSaved); @@ -1120,6 +1156,11 @@ void CaretPreferences::setRemoteFileUserNameAndPassword(const AString& userName, const AString& password) { + if ((this->remoteFileUserName == userName) + && (this->remoteFilePassword == password)) { + return; + } + this->remoteFileUserName = userName; this->remoteFilePassword = password; @@ -1148,6 +1189,10 @@ CaretPreferences::getBalsaUserName() const void CaretPreferences::setBalsaUserName(const AString& userName) { + if (this->balsaUserName == userName) { + return; + } + this->balsaUserName = userName; this->setString(NAME_BALSA_USER_NAME, userName); @@ -1170,8 +1215,12 @@ CaretPreferences::isVolumeAxesCrosshairsDisplayed() const void CaretPreferences::setVolumeAxesCrosshairsDisplayed(const bool displayed) { + if (this->displayVolumeAxesCrosshairs == displayed) { + return; + } + this->displayVolumeAxesCrosshairs = displayed; - this->setBoolean(CaretPreferences::NAME_VOLUME_AXES_CROSSHAIRS, + this->setBoolean(CaretPreferences::NAME_VOLUME_AXES_CROSSHAIRS, this->displayVolumeAxesCrosshairs); this->qSettings->sync(); } @@ -1193,6 +1242,10 @@ CaretPreferences::isVolumeAxesLabelsDisplayed() const void CaretPreferences::setVolumeAxesLabelsDisplayed(const bool displayed) { + if (this->displayVolumeAxesLabels == displayed) { + return; + } + this->displayVolumeAxesLabels = displayed; this->setBoolean(CaretPreferences::NAME_VOLUME_AXES_LABELS, this->displayVolumeAxesLabels); @@ -1217,6 +1270,10 @@ CaretPreferences::isVolumeMontageAxesCoordinatesDisplayed() const void CaretPreferences::setVolumeMontageAxesCoordinatesDisplayed(const bool displayed) { + if (this->displayVolumeAxesCoordinates == displayed) { + return; + } + this->displayVolumeAxesCoordinates = displayed; this->setBoolean(CaretPreferences::NAME_VOLUME_AXES_COORDINATE, this->displayVolumeAxesCoordinates); @@ -1241,6 +1298,10 @@ CaretPreferences::getVolumeMontageGap() const void CaretPreferences::setVolumeMontageGap(const int32_t volumeMontageGap) { + if (this->volumeMontageGap == volumeMontageGap) { + return; + } + this->volumeMontageGap = volumeMontageGap; this->setInteger(CaretPreferences::NAME_VOLUME_MONTAGE_GAP, this->volumeMontageGap); @@ -1265,6 +1326,10 @@ CaretPreferences::getVolumeMontageCoordinatePrecision() const void CaretPreferences::setVolumeMontageCoordinatePrecision(const int32_t volumeMontageCoordinatePrecision) { + if (this->volumeMontageCoordinatePrecision == volumeMontageCoordinatePrecision) { + return; + } + this->volumeMontageCoordinatePrecision = volumeMontageCoordinatePrecision; this->setInteger(CaretPreferences::NAME_VOLUME_MONTAGE_COORDINATE_PRECISION, this->volumeMontageCoordinatePrecision); @@ -1288,8 +1353,12 @@ CaretPreferences::isSplashScreenEnabled() const void CaretPreferences::setSplashScreenEnabled(const bool enabled) { + if (this->splashScreenEnabled == enabled) { + return; + } + this->splashScreenEnabled = enabled; - this->setBoolean(CaretPreferences::NAME_SPLASH_SCREEN, + this->setBoolean(CaretPreferences::NAME_SPLASH_SCREEN, this->splashScreenEnabled); this->qSettings->sync(); } @@ -1311,6 +1380,10 @@ CaretPreferences::isDevelopMenuEnabled() const void CaretPreferences::setDevelopMenuEnabled(const bool enabled) { + if (this->developMenuEnabled == enabled) { + return; + } + this->developMenuEnabled = enabled; this->setBoolean(CaretPreferences::NAME_DEVELOP_MENU, this->developMenuEnabled); @@ -1334,6 +1407,10 @@ CaretPreferences::isShowDataToolTipsEnabled() const void CaretPreferences::setShowDataToolTipsEnabled(const bool enabled) { + if (this->dataToolTipsEnabled == enabled) { + return; + } + this->dataToolTipsEnabled = enabled; this->setBoolean(CaretPreferences::NAME_DATA_TOOL_TIPS, this->dataToolTipsEnabled); @@ -1357,6 +1434,10 @@ bool CaretPreferences::isYokingDefaultedOn() const */ void CaretPreferences::setYokingDefaultedOn(const bool status) { + if (this->yokingDefaultedOn == status) { + return; + } + this->yokingDefaultedOn = status; this->setBoolean(CaretPreferences::NAME_YOKING_DEFAULT_ON, this->yokingDefaultedOn); @@ -1379,6 +1460,10 @@ bool CaretPreferences::isVolumeIdentificationDefaultedOn() const */ void CaretPreferences::setVolumeIdentificationDefaultedOn(const bool status) { + if (this->volumeIdentificationDefaultedOn == status) { + return; + } + this->volumeIdentificationDefaultedOn = status; this->setBoolean(CaretPreferences::NAME_VOLUME_IDENTIFICATION_DEFAULTED_ON, this->volumeIdentificationDefaultedOn); @@ -1560,7 +1645,8 @@ CaretPreferences::readPreferences() if (valid == false) { logLevel = LogLevelEnum::INFO; } - this->setLoggingLevel(logLevel); + /* Do not call setLoggingLevel() as it will cause preferences to sync */ + this->loggingLevel = logLevel; ImageCaptureMethodEnum::Enum defaultCaptureType = ImageCaptureMethodEnum::IMAGE_CAPTURE_WITH_RENDER_PIXMAP; AString imageCaptureMethodName = this->qSettings->value(NAME_IMAGE_CAPTURE_METHOD, -- GitLab From b63e5a033d2dec499e1f898624226f765ee43ba3 Mon Sep 17 00:00:00 2001 From: John Harwell Date: Tue, 29 Jan 2019 09:04:43 -0600 Subject: [PATCH 140/427] The last update to try and fix the 'setNativeLocks failed' problem took out a call to CaretPreferences::setLoggingLevel() and replaced it with setting the cached logging level in preference. However, the setLoggingLevel() method also updated the logger's logging level. The logger's logging level is now also updated during the initialization of preferences and the 'resync' of preferences is still avoided to try and prevent the setNativeLocks error. --- src/Common/CaretPreferences.cxx | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Common/CaretPreferences.cxx b/src/Common/CaretPreferences.cxx index 8acb470cc..0ef58000f 100644 --- a/src/Common/CaretPreferences.cxx +++ b/src/Common/CaretPreferences.cxx @@ -1647,6 +1647,7 @@ CaretPreferences::readPreferences() } /* Do not call setLoggingLevel() as it will cause preferences to sync */ this->loggingLevel = logLevel; + CaretLogger::getLogger()->setLevel(this->loggingLevel); ImageCaptureMethodEnum::Enum defaultCaptureType = ImageCaptureMethodEnum::IMAGE_CAPTURE_WITH_RENDER_PIXMAP; AString imageCaptureMethodName = this->qSettings->value(NAME_IMAGE_CAPTURE_METHOD, -- GitLab From 9e6d3696321aacfcbb94ce0441f418cde4f034ea Mon Sep 17 00:00:00 2001 From: David G Ellis Date: Thu, 7 Mar 2019 14:09:35 -0600 Subject: [PATCH 141/427] DOC: Changes flag explanation to "right". Changes documentation for "-right-metric" to "metric for right surface". It had said "left surface" previously. --- src/Algorithms/AlgorithmCiftiCreateDenseScalar.cxx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Algorithms/AlgorithmCiftiCreateDenseScalar.cxx b/src/Algorithms/AlgorithmCiftiCreateDenseScalar.cxx index 44ae4aedf..da037df2c 100644 --- a/src/Algorithms/AlgorithmCiftiCreateDenseScalar.cxx +++ b/src/Algorithms/AlgorithmCiftiCreateDenseScalar.cxx @@ -63,7 +63,7 @@ OperationParameters* AlgorithmCiftiCreateDenseScalar::getParameters() OptionalParameter* leftRoiOpt = leftMetricOpt->createOptionalParameter(2, "-roi-left", "roi of vertices to use from left surface"); leftRoiOpt->addMetricParameter(1, "roi-metric", "the ROI as a metric file"); - OptionalParameter* rightMetricOpt = ret->createOptionalParameter(4, "-right-metric", "metric for left surface"); + OptionalParameter* rightMetricOpt = ret->createOptionalParameter(4, "-right-metric", "metric for right surface"); rightMetricOpt->addMetricParameter(1, "metric", "the metric file"); OptionalParameter* rightRoiOpt = rightMetricOpt->createOptionalParameter(2, "-roi-right", "roi of vertices to use from right surface"); rightRoiOpt->addMetricParameter(1, "roi-metric", "the ROI as a metric file"); -- GitLab From b466f9427fbc5f0a48d846eed3914a763b283777 Mon Sep 17 00:00:00 2001 From: Tim Coalson Date: Tue, 12 Mar 2019 16:30:39 -0500 Subject: [PATCH 142/427] simplify anisotropic volume distortion formula, add -log2 option --- src/Algorithms/AlgorithmVolumeDistortion.cxx | 32 ++++++++++++-------- src/Algorithms/AlgorithmVolumeDistortion.h | 2 +- 2 files changed, 21 insertions(+), 13 deletions(-) diff --git a/src/Algorithms/AlgorithmVolumeDistortion.cxx b/src/Algorithms/AlgorithmVolumeDistortion.cxx index 7b1e5673d..4f8eab55a 100644 --- a/src/Algorithms/AlgorithmVolumeDistortion.cxx +++ b/src/Algorithms/AlgorithmVolumeDistortion.cxx @@ -56,6 +56,8 @@ OperationParameters* AlgorithmVolumeDistortion::getParameters() ret->createOptionalParameter(4, "-circular", "use the circle-based formula for the anisotropic measure"); + ret->createOptionalParameter(5, "-log2", "apply base-2 log transform"); + ret->setHelpText( AString("Calculates isotropic and anisotropic distortions in the volume warpfield. ") + "At each voxel, the gradient of the absolute warpfield is computed to obtain the local affine transforms for each voxel (jacobian matrices), and strain tensors are derived from them. " + @@ -74,6 +76,7 @@ void AlgorithmVolumeDistortion::useParameters(OperationParameters* myParams, Pro OptionalParameter* fnirtOpt = myParams->getOptionalParameter(3); Method myMethod = ELONGATION; if (myParams->getOptionalParameter(4)->m_present) myMethod = CIRCULAR; + bool dolog2 = myParams->getOptionalParameter(5)->m_present; WarpfieldFile myWarp; if (fnirtOpt->m_present) { @@ -81,10 +84,10 @@ void AlgorithmVolumeDistortion::useParameters(OperationParameters* myParams, Pro } else { myWarp.readWorld(warpName); } - AlgorithmVolumeDistortion(myProgObj, myWarp, distortionOut, myMethod); + AlgorithmVolumeDistortion(myProgObj, myWarp, distortionOut, myMethod, dolog2); } -AlgorithmVolumeDistortion::AlgorithmVolumeDistortion(ProgressObject* myProgObj, const WarpfieldFile& myWarp, VolumeFile* distortionOut, Method myMethod) : AbstractAlgorithm(myProgObj) +AlgorithmVolumeDistortion::AlgorithmVolumeDistortion(ProgressObject* myProgObj, const WarpfieldFile& myWarp, VolumeFile* distortionOut, const Method myMethod, const bool dolog2) : AbstractAlgorithm(myProgObj) { LevelProgress myProgress(myProgObj); switch (myMethod) @@ -159,19 +162,24 @@ AlgorithmVolumeDistortion::AlgorithmVolumeDistortion(ProgressObject* myProgObj, break; case CIRCULAR: { - const float ASPHER_THRESH = 1.00001f; - if (shapes[2] > ASPHER_THRESH) - {//asphericity formula is somewhat unstable when near-spherical (shapes[0] will always be near 1 when shapes[2] is, so a reciprocal reformulation won't help much) - anisotropic = exp(-2.0f * log(shapes[0]) / cos(atan(sqrt(3.0f) * (-log(shapes[0]) / log(shapes[2]) - 1) / (log(shapes[0]) / log(shapes[2]) - 1)) + PI / 6.0f));//deceptively complicated - } else {//when unstable, reshape the elongation ratio to approximate it - anisotropic = pow(shapes[2] / shapes[0], 1.24f); - }//explanation time: per the help info, put the shape ratios into log space, so that geometry can be useful - //now consider an equilateral triangle centered at the origin, and these log-space shape values as the signed distance from the x=0 line + //new solution found by coincidence: square and sum the x coordinates of the vertices an equilateral triangle centered on the origin, you get (3r^2)/2, where r is the distance from the origin to a vertex + float logshapes[3]; + for (int a = 0; a < 3; ++a) + { + logshapes[a] = log(shapes[a]); + } + anisotropic = exp(sqrt(8.0f / 3.0f * (logshapes[0] * logshapes[0] + logshapes[1] * logshapes[1] + logshapes[2] * logshapes[2]))); + break;//explanation time: per the help info, put the shape ratios into log space, so that geometry can be useful + //now consider an equilateral triangle centered at the origin, and these log-space shape values as the x coordinates of the vertices //now, find the distance from the origin to any vertex, and multiply by 2 to get the circumscribing circle's diameter - //this solution relied on the the most-horizontal edge of the triangle being split by x=0 at the same ratio as the magnitudes of the two extreme log-shape values (don't forget the negative sign) - break; + //the old solution relied on the the most-horizontal edge of the triangle being split by the y-axis at the same ratio as the magnitudes of the two extreme log-shape values (don't forget the negative sign) } } + if (dolog2) + { + isotropic = log2(isotropic); + anisotropic = log2(anisotropic); + } distortionOut->setValue(isotropic, i, j, k, 0); distortionOut->setValue(anisotropic, i, j, k, 1); } diff --git a/src/Algorithms/AlgorithmVolumeDistortion.h b/src/Algorithms/AlgorithmVolumeDistortion.h index 4dbb75e93..4b7d3acba 100644 --- a/src/Algorithms/AlgorithmVolumeDistortion.h +++ b/src/Algorithms/AlgorithmVolumeDistortion.h @@ -39,7 +39,7 @@ namespace caret { ELONGATION, CIRCULAR }; - AlgorithmVolumeDistortion(ProgressObject* myProgObj, const WarpfieldFile& myWarp, VolumeFile* distortionOut, Method myMethod = ELONGATION); + AlgorithmVolumeDistortion(ProgressObject* myProgObj, const WarpfieldFile& myWarp, VolumeFile* distortionOut, const Method myMethod = ELONGATION, const bool dolog2 = false); static OperationParameters* getParameters(); static void useParameters(OperationParameters* myParams, ProgressObject* myProgObj); static AString getCommandSwitch(); -- GitLab From 1e08c3c99b179b85c2e262cef111d8d473b86da4 Mon Sep 17 00:00:00 2001 From: Tim Coalson Date: Thu, 11 Apr 2019 16:08:39 -0500 Subject: [PATCH 143/427] change cifti parcellate default to keep empty labels and error when data is missing where a parcel exists, and also to allow the weights file to not completely match the data --- src/Algorithms/AlgorithmCiftiParcellate.cxx | 137 ++++++++++++-------- src/Algorithms/AlgorithmCiftiParcellate.h | 8 +- 2 files changed, 88 insertions(+), 57 deletions(-) diff --git a/src/Algorithms/AlgorithmCiftiParcellate.cxx b/src/Algorithms/AlgorithmCiftiParcellate.cxx index 66c2eedfe..82b2bfdb8 100644 --- a/src/Algorithms/AlgorithmCiftiParcellate.cxx +++ b/src/Algorithms/AlgorithmCiftiParcellate.cxx @@ -82,21 +82,24 @@ OperationParameters* AlgorithmCiftiParcellate::getParameters() ret->createOptionalParameter(9, "-only-numeric", "exclude non-numeric values"); - OptionalParameter* emptyOpt = ret->createOptionalParameter(10, "-include-empty", "create parcels for labels that have no vertices or voxels"); - OptionalParameter* emptyValOpt = emptyOpt->createOptionalParameter(1, "-fill-value", "specify value to use in empty parcels (default 0)"); + OptionalParameter* emptyValOpt = ret->createOptionalParameter(11, "-fill-value", "specify value to use in empty parcels (default 0)"); emptyValOpt->addDoubleParameter(1, "value", "the value to fill empty parcels with"); - OptionalParameter* emptyRoiOpt = emptyOpt->createOptionalParameter(2, "-nonempty-mask-out", "output a matching pscalar file that has 0s in empty parcels, and 1s elsewhere"); + OptionalParameter* emptyRoiOpt = ret->createOptionalParameter(12, "-nonempty-mask-out", "output a matching pscalar file that has 0s in empty parcels, and 1s elsewhere"); emptyRoiOpt->addCiftiOutputParameter(1, "mask-out", "the output mask file"); + ret->createOptionalParameter(13, "-legacy-mode", "use the old behavior, parcels are defined by the intersection between labels and valid data, and empty parcels are discarded"); + + ret->createOptionalParameter(10, "-include-empty", "deprecated: now the default behavior"); + ret->setHelpText( - AString("Each non-empty label (other than the unlabeled key) in the cifti label file will be treated as a parcel, and all rows or columns within the parcel are averaged together to form the output ") + - "row or column. " + - "If -include-empty is specified, empty labels will be treated as parcels with no elements, and filled with a constant value. " + + AString("Each label (other than the unlabeled key) in the cifti label file will be treated as a parcel, and all rows or columns of data within the parcel ") + + "are averaged together to form the parcel's output row or column. " + + "If -legacy-mode is specified, parcels will be defined as the overlap between a label and the data, with no errors for missing data vertices or voxels, and empty parcels discarded. " + CiftiXML::directionFromStringExplanation() + " " + "For dtseries or dscalar, use COLUMN. " + "If you are parcellating a dconn in both directions, parcellating by ROW first will use much less memory.\n\n" + "The parameter to the -method option must be one of the following:\n\n" + ReductionOperation::getHelpInfo() + - "\nThe -*-weights options are mutually exclusive and may only be used with MEAN, SUM, STDEV, SAMPSTDEV, VARIANCE, MEDIAN, or MODE." + "\nThe -*-weights options are mutually exclusive and may only be used with MEAN (default), SUM, STDEV, SAMPSTDEV, VARIANCE, MEDIAN, or MODE (default for label data)." ); return ret; } @@ -138,23 +141,20 @@ void AlgorithmCiftiParcellate::useParameters(OperationParameters* myParams, Prog excludeHigh = (float)excludeOpt->getDouble(2); if (!(excludeLow > 0.0f && excludeHigh > 0.0f)) throw AlgorithmException("exclusion sigmas must be positive"); } - OptionalParameter* emptyOpt = myParams->getOptionalParameter(10); - bool includeEmpty = emptyOpt->m_present; + /*OptionalParameter* emptyOpt = */myParams->getOptionalParameter(10);//deprecated, but we need to "get" it to avoid a debug warning of an ignored option float emptyFillValue = 0.0f; + OptionalParameter* emptyValOpt = myParams->getOptionalParameter(11); + if (emptyValOpt->m_present) + { + emptyFillValue = emptyValOpt->getDouble(1); + } CiftiFile* emptyMaskOut = NULL; - if (includeEmpty) + OptionalParameter* emptyRoiOpt = myParams->getOptionalParameter(12); + if (emptyRoiOpt->m_present) { - OptionalParameter* emptyValOpt = emptyOpt->getOptionalParameter(1); - if (emptyValOpt->m_present) - { - emptyFillValue = emptyValOpt->getDouble(1); - } - OptionalParameter* emptyRoiOpt = emptyOpt->getOptionalParameter(2); - if (emptyRoiOpt->m_present) - { - emptyMaskOut = emptyRoiOpt->getOutputCifti(1); - } + emptyMaskOut = emptyRoiOpt->getOutputCifti(1); } + bool legacyMode = myParams->getOptionalParameter(13)->m_present; OptionalParameter* spatialWeightOpt = myParams->getOptionalParameter(5); OptionalParameter* ciftiWeightOpt = myParams->getOptionalParameter(6); if (spatialWeightOpt->m_present && ciftiWeightOpt->m_present) @@ -221,7 +221,7 @@ void AlgorithmCiftiParcellate::useParameters(OperationParameters* myParams, Prog AlgorithmCiftiParcellate(myProgObj, myCiftiIn, myCiftiLabel, direction, myCiftiOut, leftWeights, rightWeights, cerebWeights, method, excludeLow, excludeHigh, onlyNumeric, - includeEmpty, emptyFillValue, emptyMaskOut); + legacyMode, emptyFillValue, emptyMaskOut); return; } if (ciftiWeightOpt->m_present) @@ -229,17 +229,17 @@ void AlgorithmCiftiParcellate::useParameters(OperationParameters* myParams, Prog AlgorithmCiftiParcellate(myProgObj, myCiftiIn, myCiftiLabel, direction, myCiftiOut, ciftiWeightOpt->getCifti(1), method, excludeLow, excludeHigh, onlyNumeric, - includeEmpty, emptyFillValue, emptyMaskOut); + legacyMode, emptyFillValue, emptyMaskOut); return; } AlgorithmCiftiParcellate(myProgObj, myCiftiIn, myCiftiLabel, direction, myCiftiOut, method, excludeLow, excludeHigh, onlyNumeric, - includeEmpty, emptyFillValue, emptyMaskOut); + legacyMode, emptyFillValue, emptyMaskOut); } AlgorithmCiftiParcellate::AlgorithmCiftiParcellate(ProgressObject* myProgObj, const CiftiFile* myCiftiIn, const CiftiFile* myCiftiLabel, const int& direction, CiftiFile* myCiftiOut, const ReductionEnum::Enum& method, const float& excludeLow, const float& excludeHigh, const bool& onlyNumeric, - const bool& includeEmpty, const float& emptyFillVal, CiftiFile* emptyMaskOut) : AbstractAlgorithm(myProgObj) + const bool& legacyMode, const float& emptyFillVal, CiftiFile* emptyMaskOut) : AbstractAlgorithm(myProgObj) { LevelProgress myProgress(myProgObj); CaretAssert(direction >= 0); @@ -268,7 +268,7 @@ AlgorithmCiftiParcellate::AlgorithmCiftiParcellate(ProgressObject* myProgObj, co } vector indexToParcel; CiftiXML myOutXML = myInputXML; - CiftiParcelsMap outParcelMap = parcellateMapping(myCiftiLabel, inputDense, indexToParcel, includeEmpty); + CiftiParcelsMap outParcelMap = parcellateMapping(myCiftiLabel, inputDense, indexToParcel, legacyMode); int numParcels = outParcelMap.getLength(); if (numParcels < 1) { @@ -677,7 +677,7 @@ namespace AlgorithmCiftiParcellate::AlgorithmCiftiParcellate(ProgressObject* myProgObj, const CiftiFile* myCiftiIn, const CiftiFile* myCiftiLabel, const int& direction, CiftiFile* myCiftiOut, const MetricFile* leftWeights, const MetricFile* rightWeights, const MetricFile* cerebWeights, const ReductionEnum::Enum& method, const float& excludeLow, const float& excludeHigh, const bool& onlyNumeric, - const bool& includeEmpty, const float& emptyFillVal, CiftiFile* emptyMaskOut): AbstractAlgorithm(myProgObj) + const bool& legacyMode, const float& emptyFillVal, CiftiFile* emptyMaskOut): AbstractAlgorithm(myProgObj) { LevelProgress myProgress(myProgObj); CaretAssert(direction >= 0); @@ -735,7 +735,7 @@ AlgorithmCiftiParcellate::AlgorithmCiftiParcellate(ProgressObject* myProgObj, co } vector indexToParcel; CiftiXML myOutXML = myInputXML; - CiftiParcelsMap outParcelMap = parcellateMapping(myCiftiLabel, inputDense, indexToParcel, includeEmpty); + CiftiParcelsMap outParcelMap = parcellateMapping(myCiftiLabel, inputDense, indexToParcel, legacyMode); int numParcels = outParcelMap.getLength(); if (numParcels < 1) { @@ -778,7 +778,7 @@ AlgorithmCiftiParcellate::AlgorithmCiftiParcellate(ProgressObject* myProgObj, co AlgorithmCiftiParcellate::AlgorithmCiftiParcellate(ProgressObject* myProgObj, const CiftiFile* myCiftiIn, const CiftiFile* myCiftiLabel, const int& direction, CiftiFile* myCiftiOut, const CiftiFile* ciftiWeights, const ReductionEnum::Enum& method, const float& excludeLow, const float& excludeHigh, const bool& onlyNumeric, - const bool& includeEmpty, const float& emptyFillVal, CiftiFile* emptyMaskOut): AbstractAlgorithm(myProgObj) + const bool& legacyMode, const float& emptyFillVal, CiftiFile* emptyMaskOut): AbstractAlgorithm(myProgObj) { LevelProgress myProgress(myProgObj); CaretAssert(direction >= 0); @@ -803,20 +803,32 @@ AlgorithmCiftiParcellate::AlgorithmCiftiParcellate(ProgressObject* myProgObj, co } const CiftiBrainModelsMap& inputDense = myInputXML.getBrainModelsMap(direction); const CiftiBrainModelsMap& labelDense = myLabelXML.getBrainModelsMap(CiftiXML::ALONG_COLUMN); - if (!weightsXML.getMap(CiftiXML::ALONG_COLUMN)->approximateMatch(inputDense)) + const CiftiBrainModelsMap& weightsDense = weightsXML.getBrainModelsMap(CiftiXML::ALONG_COLUMN); + vector surfModels = labelDense.getSurfaceStructureList(); + for (int i = 0; i < int(surfModels.size()); ++i) { - throw AlgorithmException("cifti weight file does not match brain models mapping of input file"); + if (weightsDense.hasSurfaceData(surfModels[i])) + { + if (labelDense.getSurfaceNumberOfNodes(surfModels[i]) != weightsDense.getSurfaceNumberOfNodes(surfModels[i])) + { + throw AlgorithmException("cifti weight file has wrong number of vertices for surface structure " + StructureEnum::toName(surfModels[i])); + } + } } - if (inputDense.hasVolumeData()) + if (labelDense.hasVolumeData()) {//don't check volume space if direction doesn't have volume data - if (labelDense.hasVolumeData() && !inputDense.getVolumeSpace().matches(labelDense.getVolumeSpace())) + if (inputDense.hasVolumeData() && !labelDense.getVolumeSpace().matches(inputDense.getVolumeSpace())) { - throw AlgorithmException("input cifti files must have the same volume space"); + throw AlgorithmException("input cifti file has a different volume space"); + } + if (weightsDense.hasVolumeData() && !labelDense.getVolumeSpace().matches(weightsDense.getVolumeSpace())) + { + throw AlgorithmException("cifti weight file has a different volume space"); } } vector indexToParcel; CiftiXML myOutXML = myInputXML; - CiftiParcelsMap outParcelMap = parcellateMapping(myCiftiLabel, inputDense, indexToParcel, includeEmpty); + CiftiParcelsMap outParcelMap = parcellateMapping(myCiftiLabel, inputDense, indexToParcel, legacyMode); int numParcels = outParcelMap.getLength(); if (numParcels < 1) { @@ -832,13 +844,28 @@ AlgorithmCiftiParcellate::AlgorithmCiftiParcellate(ProgressObject* myProgObj, co int parcel = indexToParcel[j]; if (parcel != -1) { - parcelWeights[parcel].push_back(weightCol[j]);//we already tested that the dense mappings matched + int weightIndex = -1; + CiftiBrainModelsMap::IndexInfo myInfo = inputDense.getInfoForIndex(j); + switch (myInfo.m_type) + { + case CiftiBrainModelsMap::SURFACE: + weightIndex = weightsDense.getIndexForNode(myInfo.m_surfaceNode, myInfo.m_structure); + break; + case CiftiBrainModelsMap::VOXELS: + weightIndex = weightsDense.getIndexForVoxel(myInfo.m_ijk); + break; + } + if (weightIndex < 0) + { + throw AlgorithmException("cifti weights file does not contain all necessary vertices and voxels"); + } + parcelWeights[parcel].push_back(weightCol[weightIndex]); } } doWeightedParcellation(myCiftiIn, direction, myCiftiOut, indexToParcel, parcelWeights, method, excludeLow, excludeHigh, onlyNumeric, emptyFillVal, emptyMaskOut); } -CiftiParcelsMap AlgorithmCiftiParcellate::parcellateMapping(const CiftiFile* myCiftiLabel, const CiftiBrainModelsMap& toParcellate, vector& indexToParcelOut, const bool& includeEmpty) +CiftiParcelsMap AlgorithmCiftiParcellate::parcellateMapping(const CiftiFile* myCiftiLabel, const CiftiBrainModelsMap& toParcellate, vector& indexToParcelOut, const bool& legacyMode) { const CiftiXML& myLabelXML = myCiftiLabel->getCiftiXML(); if (myLabelXML.getNumberOfDimensions() != 2 || @@ -850,11 +877,11 @@ CiftiParcelsMap AlgorithmCiftiParcellate::parcellateMapping(const CiftiFile* myC const CiftiLabelsMap& myLabelsMap = myLabelXML.getLabelsMap(CiftiXML::ALONG_ROW); const CiftiBrainModelsMap& labelDenseMap = myLabelXML.getBrainModelsMap(CiftiXML::ALONG_COLUMN); CiftiParcelsMap ret; - if (toParcellate.hasVolumeData() && labelDenseMap.hasVolumeData()) - { + if (labelDenseMap.hasVolumeData() && toParcellate.hasVolumeData()) + {//if only one has voxel data, don't error until we know it is used if(!toParcellate.getVolumeSpace().matches(labelDenseMap.getVolumeSpace())) { - throw AlgorithmException("AlgorithmCiftiParcellate::parcellateMapping requires matching volume space between dlabel and dense mapping to parcellate"); + throw AlgorithmException("data file to parcellate has a different voxel space than the dlabel file"); } ret.setVolumeSpace(toParcellate.getVolumeSpace()); } @@ -864,9 +891,8 @@ CiftiParcelsMap AlgorithmCiftiParcellate::parcellateMapping(const CiftiFile* myC myCiftiLabel->getColumn(labelData.data(), 0); indexToParcelOut.clear(); indexToParcelOut.resize(toParcellate.getLength(), -1); - vector surfList = toParcellate.getSurfaceStructureList(); - if (includeEmpty) - {//if we include empty, then the dlabel file by itself determines the entire parcel map, ignoring the data map + if (!legacyMode) + {//by default, the parcel definitions are based only on the dlabel file const vector labelSurfList = labelDenseMap.getSurfaceStructureList(); const set allLabelKeys = myLabelTable->getKeys(); map keyToParcel; @@ -885,11 +911,11 @@ CiftiParcelsMap AlgorithmCiftiParcellate::parcellateMapping(const CiftiFile* myC for (int i = 0; i < (int)labelSurfList.size(); ++i) { StructureEnum::Enum myStruct = labelSurfList[i]; - if (labelDenseMap.hasSurfaceData(myStruct) && toParcellate.hasSurfaceData(myStruct)) - {//if they don't match in vertex count but one is empty, don't error? + if (toParcellate.hasSurfaceData(myStruct)) + {//if a surface is missing from the data, don't error until we know it is used by the parcellation if (labelDenseMap.getSurfaceNumberOfNodes(myStruct) != toParcellate.getSurfaceNumberOfNodes(myStruct)) - { - throw AlgorithmException("mismatch in number of surface vertices between input and dlabel for structure " + StructureEnum::toName(myStruct)); + {//if both have a surface, it must match + throw AlgorithmException("mismatch in number of surface vertices between data and dlabel for structure " + StructureEnum::toName(myStruct)); } } ret.addSurface(labelDenseMap.getSurfaceNumberOfNodes(myStruct), myStruct); @@ -903,10 +929,12 @@ CiftiParcelsMap AlgorithmCiftiParcellate::parcellateMapping(const CiftiFile* myC int32_t whichParcel = found->second; parcelList[whichParcel].m_surfaceNodes[myStruct].insert(labelSurfMap[j].m_surfaceNode); int64_t dataIndex = toParcellate.getIndexForNode(labelSurfMap[j].m_surfaceNode, myStruct); - if (dataIndex != -1) + if (dataIndex < 0) { - indexToParcelOut[dataIndex] = whichParcel; + throw AlgorithmException("data file is missing vertex " + AString::number(labelSurfMap[j].m_surfaceNode) + " in structure " + + StructureEnum::toName(myStruct) + ", which is used by label '" + parcelList[whichParcel].m_name + "'"); } + indexToParcelOut[dataIndex] = whichParcel; } } } @@ -920,23 +948,26 @@ CiftiParcelsMap AlgorithmCiftiParcellate::parcellateMapping(const CiftiFile* myC int32_t whichParcel = found->second; parcelList[whichParcel].m_voxelIndices.insert(labelVolMap[i].m_ijk); int64_t dataIndex = toParcellate.getIndexForVoxel(labelVolMap[i].m_ijk); - if (dataIndex != -1) + if (dataIndex < 0) { - indexToParcelOut[dataIndex] = whichParcel; + throw AlgorithmException("data file is missing voxel (" + AString::fromNumbers(labelVolMap[i].m_ijk, 3, ", ") + "), which is used by label '" + + parcelList[whichParcel].m_name + "'"); } + indexToParcelOut[dataIndex] = whichParcel; } } for (int i = 0; i < (int)parcelList.size(); ++i) { ret.addParcel(parcelList[i]); } - } else { + } else {//legacy mode: parcels are defined by overlap between labels and the data ROI, any parcels that don't overlap any data are discarded + vector surfList = toParcellate.getSurfaceStructureList(); map > usedKeys;//the keys from the label table that actually overlap with data in the input file for (int i = 0; i < (int)surfList.size(); ++i) { StructureEnum::Enum myStruct = surfList[i]; - if (labelDenseMap.hasSurfaceData(myStruct) && toParcellate.hasSurfaceData(myStruct)) - { + if (labelDenseMap.hasSurfaceData(myStruct)) + {//if a surface is missing from the label file, don't error if (labelDenseMap.getSurfaceNumberOfNodes(myStruct) != toParcellate.getSurfaceNumberOfNodes(myStruct)) { throw AlgorithmException("mismatch in number of surface vertices between input and dlabel for structure " + StructureEnum::toName(myStruct)); diff --git a/src/Algorithms/AlgorithmCiftiParcellate.h b/src/Algorithms/AlgorithmCiftiParcellate.h index 5d2771ae6..17d8e8740 100644 --- a/src/Algorithms/AlgorithmCiftiParcellate.h +++ b/src/Algorithms/AlgorithmCiftiParcellate.h @@ -39,17 +39,17 @@ namespace caret { AlgorithmCiftiParcellate(ProgressObject* myProgObj, const CiftiFile* myCiftiIn, const CiftiFile* myCiftiLabel, const int& direction, CiftiFile* myCiftiOut, const ReductionEnum::Enum& method = ReductionEnum::MEAN, const float& excludeLow = -1.0f, const float& excludeHigh = -1.0f, const bool& onlyNumeric = false, - const bool& includeEmpty = false, const float& emptyFillVal = 0.0f, CiftiFile* emptyMaskOut = NULL); + const bool& legacyMode = false, const float& emptyFillVal = 0.0f, CiftiFile* emptyMaskOut = NULL); AlgorithmCiftiParcellate(ProgressObject* myProgObj, const CiftiFile* myCiftiIn, const CiftiFile* myCiftiLabel, const int& direction, CiftiFile* myCiftiOut, const MetricFile* leftWeights, const MetricFile* rightWeights = NULL, const MetricFile* cerebWeights = NULL, const ReductionEnum::Enum& method = ReductionEnum::MEAN, const float& excludeLow = -1.0f, const float& excludeHigh = -1.0f, const bool& onlyNumeric = false, - const bool& includeEmpty = false, const float& emptyFillVal = 0.0f, CiftiFile* emptyMaskOut = NULL); + const bool& legacyMode = false, const float& emptyFillVal = 0.0f, CiftiFile* emptyMaskOut = NULL); AlgorithmCiftiParcellate(ProgressObject* myProgObj, const CiftiFile* myCiftiIn, const CiftiFile* myCiftiLabel, const int& direction, CiftiFile* myCiftiOut, const CiftiFile* ciftiWeights, const ReductionEnum::Enum& method = ReductionEnum::MEAN, const float& excludeLow = -1.0f, const float& excludeHigh = -1.0f, const bool& onlyNumeric = false, - const bool& includeEmpty = false, const float& emptyFillVal = 0.0f, CiftiFile* emptyMaskOut = NULL); - static CiftiParcelsMap parcellateMapping(const CiftiFile* myCiftiLabel, const CiftiBrainModelsMap& toParcellate, std::vector& indexToParcelOut, const bool& includeEmpty = false); + const bool& legacyMode = false, const float& emptyFillVal = 0.0f, CiftiFile* emptyMaskOut = NULL); + static CiftiParcelsMap parcellateMapping(const CiftiFile* myCiftiLabel, const CiftiBrainModelsMap& toParcellate, std::vector& indexToParcelOut, const bool& legacyMode = false); static OperationParameters* getParameters(); static void useParameters(OperationParameters* myParams, ProgressObject* myProgObj); static AString getCommandSwitch(); -- GitLab From 646b0c61d5576e57d543f2661db701f00770882e Mon Sep 17 00:00:00 2001 From: Tim Coalson Date: Thu, 11 Apr 2019 16:53:16 -0500 Subject: [PATCH 144/427] remove deprecated sudo line from travis config --- .travis.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 5b3a0fea7..d05ad88b9 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,6 +1,5 @@ language: cpp dist: trusty -sudo: required notifications: email: false -- GitLab From 737f999d741b22515aabe9b1dcd57daa9251a79b Mon Sep 17 00:00:00 2001 From: Tim Coalson Date: Thu, 11 Apr 2019 17:17:27 -0500 Subject: [PATCH 145/427] man -zip-*-file commands remove the zip file first, to behave better with symlinks --- src/Operations/OperationZipSceneFile.cxx | 1 + src/Operations/OperationZipSpecFile.cxx | 1 + 2 files changed, 2 insertions(+) diff --git a/src/Operations/OperationZipSceneFile.cxx b/src/Operations/OperationZipSceneFile.cxx index f0d1891d7..37f6a69b6 100644 --- a/src/Operations/OperationZipSceneFile.cxx +++ b/src/Operations/OperationZipSceneFile.cxx @@ -215,6 +215,7 @@ void OperationZipSceneFile::createZipFile(const AString& sceneFileName, EventManager::get()->sendEvent(progressEvent.getPointer()); QFile zipFileObject(zipFileName); + zipFileObject.remove();//delete it if it exists, to play better with file symlinks QuaZip zipFile(&zipFileObject); if (!zipFile.open(QuaZip::mdCreate)) { diff --git a/src/Operations/OperationZipSpecFile.cxx b/src/Operations/OperationZipSpecFile.cxx index d507d5998..bba5abe31 100644 --- a/src/Operations/OperationZipSpecFile.cxx +++ b/src/Operations/OperationZipSpecFile.cxx @@ -163,6 +163,7 @@ void OperationZipSpecFile::useParameters(OperationParameters* myParams, Progress * Create the ZIP file */ QFile zipFileObject(zipFileName); + zipFileObject.remove();//delete it if it exists, to play better with file symlinks QuaZip zipFile(&zipFileObject); if (zipFile.open(QuaZip::mdCreate) == false) { throw OperationException("Unable to open ZIP File \"" -- GitLab From fac212b75555fe6a6d4d537c90a04d503a544d35 Mon Sep 17 00:00:00 2001 From: John Harwell Date: Mon, 22 Apr 2019 15:12:07 -0500 Subject: [PATCH 146/427] Fixed compilation warnings produced by optimizing compiler on Mac. --- src/Charting/ChartTwoLineSeriesHistory.cxx | 2 +- src/Files/CiftiMappableConnectivityMatrixDataFile.cxx | 8 ++++---- src/GuiQt/AnnotationLineArrowTipsWidget.cxx | 4 ---- src/GuiQt/BrainOpenGLWidget.cxx | 2 +- src/GuiQt/CaretFileDialog.cxx | 4 ++-- src/GuiQt/WuQtUtilities.cxx | 2 +- 6 files changed, 9 insertions(+), 13 deletions(-) diff --git a/src/Charting/ChartTwoLineSeriesHistory.cxx b/src/Charting/ChartTwoLineSeriesHistory.cxx index 7ea103834..4ec766e75 100644 --- a/src/Charting/ChartTwoLineSeriesHistory.cxx +++ b/src/Charting/ChartTwoLineSeriesHistory.cxx @@ -159,7 +159,7 @@ ChartTwoLineSeriesHistory::validateDefaultColor() { std::vector allEnums; CaretColorEnum::getColorAndOptionalEnums(allEnums, (CaretColorEnum::ColorOptions::OPTION_INCLUDE_CUSTOM_COLOR - || CaretColorEnum::CaretColorEnum::OPTION_INCLUDE_NONE_COLOR)); + | CaretColorEnum::CaretColorEnum::OPTION_INCLUDE_NONE_COLOR)); if (std::find(allEnums.begin(), allEnums.end(), m_defaultColor) == allEnums.end()) { diff --git a/src/Files/CiftiMappableConnectivityMatrixDataFile.cxx b/src/Files/CiftiMappableConnectivityMatrixDataFile.cxx index 3cec18021..f4067e507 100644 --- a/src/Files/CiftiMappableConnectivityMatrixDataFile.cxx +++ b/src/Files/CiftiMappableConnectivityMatrixDataFile.cxx @@ -266,9 +266,9 @@ CiftiMappableConnectivityMatrixDataFile::getRowColumnIndexForNodeWhenLoading(con case CiftiMappingType::PARCELS: rowOrColumnIndex = ciftiXML.getParcelsMap(ciftiDirection).getIndexForNode(nodeIndex, structure); break; - case CIFTI_INDEX_TYPE_SCALARS: + case CiftiMappingType::SCALARS: break; - case CIFTI_INDEX_TYPE_TIME_POINTS: + case CiftiMappingType::SERIES: break; default: CaretAssert(0); @@ -506,10 +506,10 @@ CiftiMappableConnectivityMatrixDataFile::getRowColumnIndexForVoxelIndexWhenLoadi */ if (indexValidForDataLoading(ijk[0], ijk[1], ijk[2])) { switch (rowMappingType) { - case CIFTI_INDEX_TYPE_BRAIN_MODELS: + case CiftiMappingType::BRAIN_MODELS: rowOrColumnIndex = ciftiXML.getBrainModelsMap(ciftiDirection).getIndexForVoxel(ijk); break; - case CIFTI_INDEX_TYPE_PARCELS: + case CiftiMappingType::PARCELS: rowOrColumnIndex = ciftiXML.getParcelsMap(ciftiDirection).getIndexForVoxel(ijk); break; default: diff --git a/src/GuiQt/AnnotationLineArrowTipsWidget.cxx b/src/GuiQt/AnnotationLineArrowTipsWidget.cxx index 081af4789..820ee8fb4 100644 --- a/src/GuiQt/AnnotationLineArrowTipsWidget.cxx +++ b/src/GuiQt/AnnotationLineArrowTipsWidget.cxx @@ -60,10 +60,6 @@ m_browserWindowIndex(browserWindowIndex) { QLabel* label = new QLabel("Line"); - const QSize toolButtonSize(24, 24); - - - QToolButton* endArrowToolButton = new QToolButton(); m_endArrowAction = new QAction(this); m_endArrowAction->setCheckable(true); diff --git a/src/GuiQt/BrainOpenGLWidget.cxx b/src/GuiQt/BrainOpenGLWidget.cxx index 81229da03..81159fdad 100644 --- a/src/GuiQt/BrainOpenGLWidget.cxx +++ b/src/GuiQt/BrainOpenGLWidget.cxx @@ -389,7 +389,7 @@ BrainOpenGLWidget::getOpenGLInformation() #endif // BRAIN_OPENGL_INFO_SUPPORTS_DISPLAY_LISTS info += "\n"; - return info; + return std::move(info); } /** diff --git a/src/GuiQt/CaretFileDialog.cxx b/src/GuiQt/CaretFileDialog.cxx index fb51fee68..68709f39f 100644 --- a/src/GuiQt/CaretFileDialog.cxx +++ b/src/GuiQt/CaretFileDialog.cxx @@ -403,7 +403,7 @@ CaretFileDialog::getSaveFileNameDialog(const DataFileTypeEnum::Enum dataFileType if (selectedFiles.size() > 0) { AString filename = DataFileTypeEnum::addFileExtensionIfMissing(selectedFiles[0], dataFileType); - return filename; + return std::move(filename); } } @@ -470,7 +470,7 @@ CaretFileDialog::getChooseFileNameDialog(const DataFileTypeEnum::Enum dataFileTy if (selectedFiles.size() > 0) { AString filename = DataFileTypeEnum::addFileExtensionIfMissing(selectedFiles[0], dataFileType); - return filename; + return std::move(filename); } } return QString(); diff --git a/src/GuiQt/WuQtUtilities.cxx b/src/GuiQt/WuQtUtilities.cxx index fb64061f8..d653978ce 100644 --- a/src/GuiQt/WuQtUtilities.cxx +++ b/src/GuiQt/WuQtUtilities.cxx @@ -680,7 +680,7 @@ WuQtUtilities::estimateTableWidgetSize(QTableWidget* tableWidget) if (item != NULL) { int itemWidth = 0; int itemHeight = 0; - if (item->flags() && Qt::ItemIsUserCheckable) { + if (item->flags() & Qt::ItemIsUserCheckable) { itemWidth += 12; } -- GitLab From 7f7c1327dd5b274051ddf007304e81b193cc5c1d Mon Sep 17 00:00:00 2001 From: John Harwell Date: Tue, 23 Apr 2019 09:38:34 -0500 Subject: [PATCH 147/427] Resolve WB-849 dynconn error display using subcortical seed: Loading dynconn from voxel data probably never was tested. Data from a dynconn file (actually a dense data-series) is loaded by column in contrast to all other 'dense-like' files that load data by row. Testing for validity of a voxel index and conversion of a coordinate to a voxel index was attempting to use the CIFTI series-map (ALONG_ROW) which is invalid since a series-map never contains voxel data. This has been corrected to use the brain models map in the dynconn file for voxel validity and conversion from coordinates to voxel indices. --- .../CiftiMappableConnectivityMatrixDataFile.cxx | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/src/Files/CiftiMappableConnectivityMatrixDataFile.cxx b/src/Files/CiftiMappableConnectivityMatrixDataFile.cxx index f4067e507..0b6a2dd90 100644 --- a/src/Files/CiftiMappableConnectivityMatrixDataFile.cxx +++ b/src/Files/CiftiMappableConnectivityMatrixDataFile.cxx @@ -462,7 +462,12 @@ CiftiMappableConnectivityMatrixDataFile::getRowColumnIndexForVoxelAtCoordinateWh } int64_t ijk[3]; - enclosingVoxelForDataLoading(xyz[0], xyz[1], xyz[2], ijk[0], ijk[1], ijk[2]); + if (getDataFileType() == DataFileTypeEnum::CONNECTIVITY_DENSE_DYNAMIC) { + enclosingVoxel(xyz[0], xyz[1], xyz[2], ijk[0], ijk[1], ijk[2]); + } + else { + enclosingVoxelForDataLoading(xyz[0], xyz[1], xyz[2], ijk[0], ijk[1], ijk[2]); + } return getRowColumnIndexForVoxelIndexWhenLoading(ijk, rowIndexOut, columnIndexOut); @@ -504,7 +509,10 @@ CiftiMappableConnectivityMatrixDataFile::getRowColumnIndexForVoxelIndexWhenLoadi /* * Get the mapping type */ - if (indexValidForDataLoading(ijk[0], ijk[1], ijk[2])) { + const bool indexValidFlag = ((getDataFileType() == DataFileTypeEnum::CONNECTIVITY_DENSE_DYNAMIC) + ? indexValid(ijk[0], ijk[1], ijk[2]) + : indexValidForDataLoading(ijk[0], ijk[1], ijk[2])); + if (indexValidFlag) { switch (rowMappingType) { case CiftiMappingType::BRAIN_MODELS: rowOrColumnIndex = ciftiXML.getBrainModelsMap(ciftiDirection).getIndexForVoxel(ijk); -- GitLab From daaeecc6383fd3b6d139fb16649f7f81f6a3f777 Mon Sep 17 00:00:00 2001 From: Tim Coalson Date: Tue, 23 Apr 2019 19:16:53 -0500 Subject: [PATCH 148/427] fix wrong enum value in case --- src/Common/CaretMathExpression.cxx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Common/CaretMathExpression.cxx b/src/Common/CaretMathExpression.cxx index 80f189617..52299f401 100644 --- a/src/Common/CaretMathExpression.cxx +++ b/src/Common/CaretMathExpression.cxx @@ -372,7 +372,7 @@ double CaretMathExpression::MathNode::eval(const vector& values) const } break; } - case INVALID: + case MathFunctionEnum::INVALID: CaretAssertMessage(0, "MathNode is type FUNC but INVALID function"); throw CaretException("parsing problem in CaretMathExpression"); } -- GitLab From 52d5cdb675d44400494eb038c27079b1825fa88a Mon Sep 17 00:00:00 2001 From: Tim Coalson Date: Fri, 26 Apr 2019 16:30:03 -0500 Subject: [PATCH 149/427] improve dilation defaults, make -cifti-resample set the background of the volume to be resampled to the average of the edge voxels after dilation (to reduce the necessity of dilation for preventing edge ringing) --- src/Algorithms/AlgorithmCiftiDilate.cxx | 24 +- src/Algorithms/AlgorithmCiftiDilate.h | 2 +- src/Algorithms/AlgorithmCiftiResample.cxx | 569 +++++++++++----- src/Algorithms/AlgorithmCiftiResample.h | 22 +- src/Algorithms/AlgorithmMetricDilate.cxx | 80 ++- src/Algorithms/AlgorithmMetricDilate.h | 6 +- .../AlgorithmMetricFalseCorrelation.cxx | 6 +- src/Algorithms/AlgorithmVolumeDilate.cxx | 630 ++++++++---------- src/Algorithms/AlgorithmVolumeDilate.h | 9 +- src/Common/CaretPointLocator.cxx | 18 +- src/Common/CaretPointLocator.h | 8 +- src/Common/VoxelIJK.h | 3 +- src/Files/VolumePaddingHelper.cxx | 11 +- src/Files/VolumePaddingHelper.h | 5 +- src/FilesBase/VolumeSpace.h | 16 + .../OperationCiftiResampleDconnMemory.cxx | 24 +- 16 files changed, 804 insertions(+), 629 deletions(-) diff --git a/src/Algorithms/AlgorithmCiftiDilate.cxx b/src/Algorithms/AlgorithmCiftiDilate.cxx index 55e360542..b90be480f 100644 --- a/src/Algorithms/AlgorithmCiftiDilate.cxx +++ b/src/Algorithms/AlgorithmCiftiDilate.cxx @@ -80,6 +80,8 @@ OperationParameters* AlgorithmCiftiDilate::getParameters() ret->createOptionalParameter(11, "-merged-volume", "treat volume components as if they were a single component"); + ret->createOptionalParameter(12, "-legacy-mode", "use the math from v1.3.2 and earlier for weighted dilation"); + ret->setHelpText( AString("For all data values designated as bad, if they neighbor a good value or are within the specified distance of a good value in the same kind of model, ") + "replace the value with a distance weighted average of nearby good values, otherwise set the value to zero. " + @@ -149,13 +151,14 @@ void AlgorithmCiftiDilate::useParameters(OperationParameters* myParams, Progress } bool nearest = myParams->getOptionalParameter(10)->m_present; bool mergedVolume = myParams->getOptionalParameter(11)->m_present; - AlgorithmCiftiDilate(myProgObj, myCifti, myDir, surfDist, volDist, myCiftiOut, myLeftSurf, myRightSurf, myCerebSurf, myLeftAreas, myRightAreas, myCerebAreas, myRoi, nearest, mergedVolume); + bool legacyMode = myParams->getOptionalParameter(12)->m_present; + AlgorithmCiftiDilate(myProgObj, myCifti, myDir, surfDist, volDist, myCiftiOut, myLeftSurf, myRightSurf, myCerebSurf, myLeftAreas, myRightAreas, myCerebAreas, myRoi, nearest, mergedVolume, legacyMode); } AlgorithmCiftiDilate::AlgorithmCiftiDilate(ProgressObject* myProgObj, const CiftiFile* myCifti, const int& myDir, const float& surfDist, const float& volDist, CiftiFile* myCiftiOut, const SurfaceFile* myLeftSurf, const SurfaceFile* myRightSurf, const SurfaceFile* myCerebSurf, const MetricFile* myLeftAreas, const MetricFile* myRightAreas, const MetricFile* myCerebAreas, - const CiftiFile* myBadRoi, const bool& nearest, const bool& mergedVolume) : AbstractAlgorithm(myProgObj) + const CiftiFile* myBadRoi, const bool& nearest, const bool& mergedVolume, const bool legacyMode) : AbstractAlgorithm(myProgObj) { LevelProgress myProgress(myProgObj); CiftiXMLOld myXML = myCifti->getCiftiXMLOld(); @@ -166,6 +169,12 @@ AlgorithmCiftiDilate::AlgorithmCiftiDilate(ProgressObject* myProgObj, const Cift { throw AlgorithmException("specified direction does not contain brainordinates"); } + float volExponent = 7.0f, surfExponent = 6.0f; + if (legacyMode) + { + volExponent = 2.0f; + surfExponent = 2.0f; + } for (int whichStruct = 0; whichStruct < (int)surfaceList.size(); ++whichStruct) {//sanity check surfaces const SurfaceFile* mySurf = NULL; @@ -245,7 +254,7 @@ AlgorithmCiftiDilate::AlgorithmCiftiDilate(ProgressObject* myProgObj, const Cift AlgorithmMetricDilate::Method myMethod = AlgorithmMetricDilate::WEIGHTED; if (nearest) myMethod = AlgorithmMetricDilate::NEAREST; AlgorithmCiftiSeparate(NULL, myCifti, myDir, surfaceList[whichStruct], &myMetric, &dataRoiMetric); - AlgorithmMetricDilate(NULL, &myMetric, mySurf, surfDist, &myMetricOut, badRoiPtr, &dataRoiMetric, -1, myMethod, 2.0f, myCorrAreas); + AlgorithmMetricDilate(NULL, &myMetric, mySurf, surfDist, &myMetricOut, badRoiPtr, &dataRoiMetric, -1, myMethod, surfExponent, myCorrAreas, legacyMode); AlgorithmCiftiReplaceStructure(NULL, myCiftiOut, myDir, surfaceList[whichStruct], &myMetricOut); } } @@ -253,8 +262,9 @@ AlgorithmCiftiDilate::AlgorithmCiftiDilate(ProgressObject* myProgObj, const Cift { if (myXML.hasVolumeData(myDir)) { - VolumeFile myVol, roiVol, myVolOut; - VolumeFile* roiPtr = NULL; + VolumeFile myVol, roiVol, myVolOut, dataRoi, junkVol; + VolumeFile* roiPtr = NULL, *dataRoiPtr = &dataRoi; + if (legacyMode) dataRoiPtr = NULL; int64_t offset[3]; AlgorithmVolumeDilate::Method myMethod = AlgorithmVolumeDilate::WEIGHTED; if (nearest) @@ -266,8 +276,8 @@ AlgorithmCiftiDilate::AlgorithmCiftiDilate(ProgressObject* myProgObj, const Cift AlgorithmCiftiSeparate(NULL, myBadRoi, CiftiXMLOld::ALONG_COLUMN, &roiVol, offset, NULL, true); roiPtr = &roiVol; } - AlgorithmCiftiSeparate(NULL, myCifti, myDir, &myVol, offset, NULL, true); - AlgorithmVolumeDilate(NULL, &myVol, volDist, myMethod, &myVolOut, roiPtr); + AlgorithmCiftiSeparate(NULL, myCifti, myDir, &myVol, offset, &dataRoi, true); + AlgorithmVolumeDilate(NULL, &myVol, volDist, myMethod, &myVolOut, roiPtr, dataRoiPtr, -1, volExponent, legacyMode); AlgorithmCiftiReplaceStructure(NULL, myCiftiOut, myDir, &myVolOut, true); } } else { diff --git a/src/Algorithms/AlgorithmCiftiDilate.h b/src/Algorithms/AlgorithmCiftiDilate.h index ecd21fd6b..2414c63ad 100644 --- a/src/Algorithms/AlgorithmCiftiDilate.h +++ b/src/Algorithms/AlgorithmCiftiDilate.h @@ -35,7 +35,7 @@ namespace caret { AlgorithmCiftiDilate(ProgressObject* myProgObj, const CiftiFile* myCifti, const int& myDir, const float& surfDist, const float& volDist, CiftiFile* myCiftiOut, const SurfaceFile* myLeftSurf = NULL, const SurfaceFile* myRightSurf = NULL, const SurfaceFile* myCerebSurf = NULL, const MetricFile* myLeftAreas = NULL, const MetricFile* myRightAreas = NULL, const MetricFile* myCerebAreas = NULL, - const CiftiFile* myRoi = NULL, const bool& nearest = false, const bool& mergedVolume = false); + const CiftiFile* myRoi = NULL, const bool& nearest = false, const bool& mergedVolume = false, const bool legacyMode = false); static OperationParameters* getParameters(); static void useParameters(OperationParameters* myParams, ProgressObject* myProgObj); static AString getCommandSwitch(); diff --git a/src/Algorithms/AlgorithmCiftiResample.cxx b/src/Algorithms/AlgorithmCiftiResample.cxx index 410ff77c7..04a0322a0 100644 --- a/src/Algorithms/AlgorithmCiftiResample.cxx +++ b/src/Algorithms/AlgorithmCiftiResample.cxx @@ -75,60 +75,62 @@ OperationParameters* AlgorithmCiftiResample::getParameters() ret->createOptionalParameter(8, "-surface-largest", "use largest weight instead of weighted average or popularity when doing surface resampling"); OptionalParameter* volDilateOpt = ret->createOptionalParameter(9, "-volume-predilate", "dilate the volume components before resampling"); - volDilateOpt->addDoubleParameter(1, "dilate-mm", "distance, in mm, to dilate"); - volDilateOpt->createOptionalParameter(2, "-nearest", "use nearest value dilation"); - OptionalParameter* volDilateWeightedOpt = volDilateOpt->createOptionalParameter(3, "-weighted", "use weighted dilation (default)"); - OptionalParameter* volDilateExpOpt = volDilateWeightedOpt->createOptionalParameter(1, "-exponent", "specify exponent in weighting function"); - volDilateExpOpt->addDoubleParameter(1, "exponent", "exponent 'n' to use in (1 / (distance ^ n)) as the weighting function (default 2)"); + volDilateOpt->addDoubleParameter(1, "dilate-mm", "distance, in mm, to dilate"); + volDilateOpt->createOptionalParameter(2, "-nearest", "use nearest value dilation"); + OptionalParameter* volDilateWeightedOpt = volDilateOpt->createOptionalParameter(3, "-weighted", "use weighted dilation (default)"); + OptionalParameter* volDilateExpOpt = volDilateWeightedOpt->createOptionalParameter(1, "-exponent", "specify exponent in weighting function"); + volDilateExpOpt->addDoubleParameter(1, "exponent", "exponent 'n' to use in (1 / (distance ^ n)) as the weighting function (default 7)"); + volDilateWeightedOpt->createOptionalParameter(2, "-legacy-cutoff", "use v1.3.2 logic for the kernel cutoff"); OptionalParameter* surfDilateOpt = ret->createOptionalParameter(10, "-surface-postdilate", "dilate the surface components after resampling"); - surfDilateOpt->addDoubleParameter(1, "dilate-mm", "distance, in mm, to dilate"); - surfDilateOpt->createOptionalParameter(2, "-nearest", "use nearest value dilation"); - surfDilateOpt->createOptionalParameter(3, "-linear", "use linear dilation"); - OptionalParameter* surfDilateWeightedOpt = surfDilateOpt->createOptionalParameter(4, "-weighted", "use weighted dilation (default for non-label data)"); - OptionalParameter* surfDilateExpOpt = surfDilateWeightedOpt->createOptionalParameter(1, "-exponent", "specify exponent in weighting function"); - surfDilateExpOpt->addDoubleParameter(1, "exponent", "exponent 'n' to use in (area / (distance ^ n)) as the weighting function (default 2)"); + surfDilateOpt->addDoubleParameter(1, "dilate-mm", "distance, in mm, to dilate"); + surfDilateOpt->createOptionalParameter(2, "-nearest", "use nearest value dilation"); + surfDilateOpt->createOptionalParameter(3, "-linear", "use linear dilation"); + OptionalParameter* surfDilateWeightedOpt = surfDilateOpt->createOptionalParameter(4, "-weighted", "use weighted dilation (default for non-label data)"); + OptionalParameter* surfDilateExpOpt = surfDilateWeightedOpt->createOptionalParameter(1, "-exponent", "specify exponent in weighting function"); + surfDilateExpOpt->addDoubleParameter(1, "exponent", "exponent 'n' to use in (area / (distance ^ n)) as the weighting function (default 6)"); + surfDilateWeightedOpt->createOptionalParameter(2, "-legacy-cutoff", "use v1.3.2 logic for the kernel cutoff"); OptionalParameter* affineOpt = ret->createOptionalParameter(11, "-affine", "use an affine transformation on the volume components"); - affineOpt->addStringParameter(1, "affine-file", "the affine file to use"); - OptionalParameter* flirtOpt = affineOpt->createOptionalParameter(2, "-flirt", "MUST be used if affine is a flirt affine"); - flirtOpt->addStringParameter(1, "source-volume", "the source volume used when generating the affine"); - flirtOpt->addStringParameter(2, "target-volume", "the target volume used when generating the affine"); + affineOpt->addStringParameter(1, "affine-file", "the affine file to use"); + OptionalParameter* flirtOpt = affineOpt->createOptionalParameter(2, "-flirt", "MUST be used if affine is a flirt affine"); + flirtOpt->addStringParameter(1, "source-volume", "the source volume used when generating the affine"); + flirtOpt->addStringParameter(2, "target-volume", "the target volume used when generating the affine"); OptionalParameter* warpfieldOpt = ret->createOptionalParameter(12, "-warpfield", "use a warpfield on the volume components"); - warpfieldOpt->addStringParameter(1, "warpfield", "the warpfield to use"); - OptionalParameter* fnirtOpt = warpfieldOpt->createOptionalParameter(2, "-fnirt", "MUST be used if using a fnirt warpfield"); - fnirtOpt->addStringParameter(1, "source-volume", "the source volume used when generating the warpfield"); + warpfieldOpt->addStringParameter(1, "warpfield", "the warpfield to use"); + OptionalParameter* fnirtOpt = warpfieldOpt->createOptionalParameter(2, "-fnirt", "MUST be used if using a fnirt warpfield"); + fnirtOpt->addStringParameter(1, "source-volume", "the source volume used when generating the warpfield"); OptionalParameter* leftSpheresOpt = ret->createOptionalParameter(13, "-left-spheres", "specify spheres for left surface resampling"); - leftSpheresOpt->addSurfaceParameter(1, "current-sphere", "a sphere with the same mesh as the current left surface"); - leftSpheresOpt->addSurfaceParameter(2, "new-sphere", "a sphere with the new left mesh that is in register with the current sphere"); - OptionalParameter* leftAreaSurfsOpt = leftSpheresOpt->createOptionalParameter(3, "-left-area-surfs", "specify left surfaces to do vertex area correction based on"); - leftAreaSurfsOpt->addSurfaceParameter(1, "current-area", "a relevant left anatomical surface with current mesh"); - leftAreaSurfsOpt->addSurfaceParameter(2, "new-area", "a relevant left anatomical surface with new mesh"); - OptionalParameter* leftAreaMetricsOpt = leftSpheresOpt->createOptionalParameter(4, "-left-area-metrics", "specify left vertex area metrics to do area correction based on"); - leftAreaMetricsOpt->addMetricParameter(1, "current-area", "a metric file with vertex areas for the current mesh"); - leftAreaMetricsOpt->addMetricParameter(2, "new-area", "a metric file with vertex areas for the new mesh"); + leftSpheresOpt->addSurfaceParameter(1, "current-sphere", "a sphere with the same mesh as the current left surface"); + leftSpheresOpt->addSurfaceParameter(2, "new-sphere", "a sphere with the new left mesh that is in register with the current sphere"); + OptionalParameter* leftAreaSurfsOpt = leftSpheresOpt->createOptionalParameter(3, "-left-area-surfs", "specify left surfaces to do vertex area correction based on"); + leftAreaSurfsOpt->addSurfaceParameter(1, "current-area", "a relevant left anatomical surface with current mesh"); + leftAreaSurfsOpt->addSurfaceParameter(2, "new-area", "a relevant left anatomical surface with new mesh"); + OptionalParameter* leftAreaMetricsOpt = leftSpheresOpt->createOptionalParameter(4, "-left-area-metrics", "specify left vertex area metrics to do area correction based on"); + leftAreaMetricsOpt->addMetricParameter(1, "current-area", "a metric file with vertex areas for the current mesh"); + leftAreaMetricsOpt->addMetricParameter(2, "new-area", "a metric file with vertex areas for the new mesh"); OptionalParameter* rightSpheresOpt = ret->createOptionalParameter(14, "-right-spheres", "specify spheres for right surface resampling"); - rightSpheresOpt->addSurfaceParameter(1, "current-sphere", "a sphere with the same mesh as the current right surface"); - rightSpheresOpt->addSurfaceParameter(2, "new-sphere", "a sphere with the new right mesh that is in register with the current sphere"); - OptionalParameter* rightAreaSurfsOpt = rightSpheresOpt->createOptionalParameter(3, "-right-area-surfs", "specify right surfaces to do vertex area correction based on"); - rightAreaSurfsOpt->addSurfaceParameter(1, "current-area", "a relevant right anatomical surface with current mesh"); - rightAreaSurfsOpt->addSurfaceParameter(2, "new-area", "a relevant right anatomical surface with new mesh"); - OptionalParameter* rightAreaMetricsOpt = rightSpheresOpt->createOptionalParameter(4, "-right-area-metrics", "specify right vertex area metrics to do area correction based on"); - rightAreaMetricsOpt->addMetricParameter(1, "current-area", "a metric file with vertex areas for the current mesh"); - rightAreaMetricsOpt->addMetricParameter(2, "new-area", "a metric file with vertex areas for the new mesh"); + rightSpheresOpt->addSurfaceParameter(1, "current-sphere", "a sphere with the same mesh as the current right surface"); + rightSpheresOpt->addSurfaceParameter(2, "new-sphere", "a sphere with the new right mesh that is in register with the current sphere"); + OptionalParameter* rightAreaSurfsOpt = rightSpheresOpt->createOptionalParameter(3, "-right-area-surfs", "specify right surfaces to do vertex area correction based on"); + rightAreaSurfsOpt->addSurfaceParameter(1, "current-area", "a relevant right anatomical surface with current mesh"); + rightAreaSurfsOpt->addSurfaceParameter(2, "new-area", "a relevant right anatomical surface with new mesh"); + OptionalParameter* rightAreaMetricsOpt = rightSpheresOpt->createOptionalParameter(4, "-right-area-metrics", "specify right vertex area metrics to do area correction based on"); + rightAreaMetricsOpt->addMetricParameter(1, "current-area", "a metric file with vertex areas for the current mesh"); + rightAreaMetricsOpt->addMetricParameter(2, "new-area", "a metric file with vertex areas for the new mesh"); OptionalParameter* cerebSpheresOpt = ret->createOptionalParameter(15, "-cerebellum-spheres", "specify spheres for cerebellum surface resampling"); - cerebSpheresOpt->addSurfaceParameter(1, "current-sphere", "a sphere with the same mesh as the current cerebellum surface"); - cerebSpheresOpt->addSurfaceParameter(2, "new-sphere", "a sphere with the new cerebellum mesh that is in register with the current sphere"); - OptionalParameter* cerebAreaSurfsOpt = cerebSpheresOpt->createOptionalParameter(3, "-cerebellum-area-surfs", "specify cerebellum surfaces to do vertex area correction based on"); - cerebAreaSurfsOpt->addSurfaceParameter(1, "current-area", "a relevant cerebellum anatomical surface with current mesh"); - cerebAreaSurfsOpt->addSurfaceParameter(2, "new-area", "a relevant cerebellum anatomical surface with new mesh"); - OptionalParameter* cerebAreaMetricsOpt = cerebSpheresOpt->createOptionalParameter(4, "-cerebellum-area-metrics", "specify cerebellum vertex area metrics to do area correction based on"); - cerebAreaMetricsOpt->addMetricParameter(1, "current-area", "a metric file with vertex areas for the current mesh"); - cerebAreaMetricsOpt->addMetricParameter(2, "new-area", "a metric file with vertex areas for the new mesh"); + cerebSpheresOpt->addSurfaceParameter(1, "current-sphere", "a sphere with the same mesh as the current cerebellum surface"); + cerebSpheresOpt->addSurfaceParameter(2, "new-sphere", "a sphere with the new cerebellum mesh that is in register with the current sphere"); + OptionalParameter* cerebAreaSurfsOpt = cerebSpheresOpt->createOptionalParameter(3, "-cerebellum-area-surfs", "specify cerebellum surfaces to do vertex area correction based on"); + cerebAreaSurfsOpt->addSurfaceParameter(1, "current-area", "a relevant cerebellum anatomical surface with current mesh"); + cerebAreaSurfsOpt->addSurfaceParameter(2, "new-area", "a relevant cerebellum anatomical surface with new mesh"); + OptionalParameter* cerebAreaMetricsOpt = cerebSpheresOpt->createOptionalParameter(4, "-cerebellum-area-metrics", "specify cerebellum vertex area metrics to do area correction based on"); + cerebAreaMetricsOpt->addMetricParameter(1, "current-area", "a metric file with vertex areas for the current mesh"); + cerebAreaMetricsOpt->addMetricParameter(2, "new-area", "a metric file with vertex areas for the new mesh"); AString myHelpText = AString("Resample cifti data to a different brainordinate space. Use COLUMN for the direction to resample dscalar, dlabel, or dtseries. ") + @@ -216,9 +218,10 @@ void AlgorithmCiftiResample::useParameters(OperationParameters* myParams, Progre } } AlgorithmVolumeDilate::Method volDilateMethod = AlgorithmVolumeDilate::WEIGHTED; - float volDilateExponent = 2.0f; - AlgorithmMetricDilate::Method surfDilateMethod = AlgorithmMetricDilate::WEIGHTED;//label dilate doesn't support multiple methods - what to do there, share the enum between them? - float surfDilateExponent = 2.0f;//label dilate currently only supports nearest, so in order to accept a default in the algorithm, it currently ignores this on label data, no warning + float volDilateExponent = 7.0f; + AlgorithmMetricDilate::Method surfDilateMethod = AlgorithmMetricDilate::WEIGHTED;//label dilate doesn't support multiple methods + float surfDilateExponent = 6.0f;//label dilate currently only supports nearest, so in order to accept a default in the algorithm, it currently ignores this on label data, no warning + bool surfLegacyCutoff = false, volLegacyCutoff = false; OptionalParameter* volDilateOpt = myParams->getOptionalParameter(9);//but it does check the options on the command line for sanity if (volDilateOpt->m_present) { @@ -241,6 +244,7 @@ void AlgorithmCiftiResample::useParameters(OperationParameters* myParams, Progre { volDilateExponent = (float)volDilateExpOpt->getDouble(1); } + volLegacyCutoff = volDilateWeightedOpt->getOptionalParameter(2)->m_present; } } OptionalParameter* surfDilateOpt = myParams->getOptionalParameter(10); @@ -274,6 +278,7 @@ void AlgorithmCiftiResample::useParameters(OperationParameters* myParams, Progre surfDilateExponent = (float)surfDilateExpOpt->getDouble(1); } } + surfLegacyCutoff = surfDilateWeightedOpt->getOptionalParameter(2)->m_present; } OptionalParameter* affineOpt = myParams->getOptionalParameter(11); OptionalParameter* warpfieldOpt = myParams->getOptionalParameter(12); @@ -408,13 +413,13 @@ void AlgorithmCiftiResample::useParameters(OperationParameters* myParams, Progre curLeftSphere, newLeftSphere, curLeftAreas, newLeftAreas, curRightSphere, newRightSphere, curRightAreas, newRightAreas, curCerebSphere, newCerebSphere, curCerebAreas, newCerebAreas, - volDilateMethod, volDilateExponent, surfDilateMethod, surfDilateExponent); + volDilateMethod, volDilateExponent, surfDilateMethod, surfDilateExponent, volLegacyCutoff, surfLegacyCutoff); } else {//rely on AffineFile() being the identity transform for if neither option is specified AlgorithmCiftiResample(myProgObj, myCiftiIn, direction, myTemplate, templateDir, mySurfMethod, myVolMethod, myCiftiOut, surfLargest, voldilatemm, surfdilatemm, myAffine.getMatrix(), curLeftSphere, newLeftSphere, curLeftAreas, newLeftAreas, curRightSphere, newRightSphere, curRightAreas, newRightAreas, curCerebSphere, newCerebSphere, curCerebAreas, newCerebAreas, - volDilateMethod, volDilateExponent, surfDilateMethod, surfDilateExponent); + volDilateMethod, volDilateExponent, surfDilateMethod, surfDilateExponent, volLegacyCutoff, surfLegacyCutoff); } } @@ -505,19 +510,19 @@ namespace const SurfaceFile* curSphere, *newSphere; MetricFile tempMetric1, tempMetric2, surfDilateRoi; LabelFile tempLabel1, tempLabel2; - CaretPointer tempVol1, tempVol2, tempVol3, volDilateRoi; + CaretPointer inputVol, tempVol2, tempVol3, volDilateRoi; vector inSurfMap, outSurfMap; vector inVolMap, outVolMap; vector floatScratch1, floatScratch2; vector intScratch1, intScratch2; - vector inOffset; - int64_t refDims[3], refOffset[3]; + int64_t refDims[3], refOffset[3], inOffset[3]; vector > refSform; bool copyMode; + vector edgeVoxelList, outsideRoiVoxels, insideResampledRoiVoxels; }; void setupRowResampling(map& surfCache, map& volCache, const CiftiFile* myCiftiIn, CiftiFile* myCiftiOut, - const SurfaceResamplingMethodEnum::Enum& mySurfMethod, const float& voldilatemm, + const SurfaceResamplingMethodEnum::Enum& mySurfMethod, const float& voldilatemm, const FloatMatrix* affine, const VolumeFile* warpfield, const SurfaceFile* curLeftSphere, const SurfaceFile* newLeftSphere, const MetricFile* curLeftAreas, const MetricFile* newLeftAreas, const SurfaceFile* curRightSphere, const SurfaceFile* newRightSphere, const MetricFile* curRightAreas, const MetricFile* newRightAreas, const SurfaceFile* curCerebSphere, const SurfaceFile* newCerebSphere, const MetricFile* curCerebAreas, const MetricFile* newCerebAreas) @@ -604,38 +609,136 @@ namespace myCache.outVolMap = outModels.getVolumeStructureMap(volList[i]); vector > sform; vector inDims(3); - myCache.inOffset.resize(3); myCache.floatScratch1.resize(inDims[0] * inDims[1] * inDims[2]); - AlgorithmCiftiSeparate::getCroppedVolSpace(myCiftiIn, CiftiXML::ALONG_ROW, volList[i], inDims.data(), sform, myCache.inOffset.data()); + AlgorithmCiftiSeparate::getCroppedVolSpace(myCiftiIn, CiftiXML::ALONG_ROW, volList[i], inDims.data(), sform, myCache.inOffset); AlgorithmCiftiSeparate::getCroppedVolSpace(myCiftiOut, CiftiXML::ALONG_ROW, volList[i], myCache.refDims, myCache.refSform, myCache.refOffset); if (labelMode) { - myCache.tempVol1.grabNew(new VolumeFile(inDims, sform, 1, SubvolumeAttributes::LABEL)); + myCache.inputVol.grabNew(new VolumeFile(inDims, sform, 1, SubvolumeAttributes::LABEL)); } else { - myCache.tempVol1.grabNew(new VolumeFile(inDims, sform)); - myCache.tempVol1->setValueAllVoxels(0.0f); + myCache.inputVol.grabNew(new VolumeFile(inDims, sform)); + myCache.inputVol->setValueAllVoxels(0.0f); } - myCache.tempVol2.grabNew(new VolumeFile(inDims, sform));//temporarily use to make the dilation roi, if needed + myCache.tempVol2.grabNew(new VolumeFile(inDims, sform));//make the dilation roi, to figure out edge voxels and in case we do dilation + myCache.tempVol2->setValueAllVoxels(0.0f); + for (int64_t j = 0; j < (int64_t)myCache.inVolMap.size(); ++j) + { + myCache.tempVol2->setValue(1.0f, myCache.inVolMap[j].m_ijk[0] - myCache.inOffset[0], + myCache.inVolMap[j].m_ijk[1] - myCache.inOffset[1], + myCache.inVolMap[j].m_ijk[2] - myCache.inOffset[2]); + } + int neighbors[] = { 0, 0, -1, + 0, -1, 0, + -1, 0, 0, + 1, 0, 0, + 0, 1, 0, + 0, 0, 1 }; + VolumeFile resampledRoi; if (voldilatemm > 0.0f) { - myCache.volPadding = VolumePaddingHelper::padMM(myCache.tempVol1, voldilatemm); + VolumeFile ROIinvTemp(inDims, sform); + ROIinvTemp.setValueAllVoxels(1.0f);//make the ordinary ROI so that we can dilate it + for (int64_t j = 0; j < (int64_t)myCache.inVolMap.size(); ++j) + { + ROIinvTemp.setValue(0.0f, myCache.inVolMap[j].m_ijk[0] - myCache.inOffset[0], + myCache.inVolMap[j].m_ijk[1] - myCache.inOffset[1], + myCache.inVolMap[j].m_ijk[2] - myCache.inOffset[2]); + } + myCache.volPadding = VolumePaddingHelper::padMM(myCache.inputVol, voldilatemm); myCache.volDilateRoi.grabNew(new VolumeFile()); - myCache.tempVol3.grabNew(new VolumeFile()); - myCache.tempVol2->setValueAllVoxels(1.0f); - for (int j = 0; j < (int)myCache.inVolMap.size(); ++j) + myCache.volPadding.doPadding(&ROIinvTemp, myCache.volDilateRoi, 1.0f);//this is the correct final volDilateRoi, we don't need the unpadded form anymore + ROIinvTemp.clear(); + myCache.tempVol3.grabNew(new VolumeFile());//this is only used in processing when dilating + myCache.volPadding.doPadding(myCache.tempVol2, myCache.tempVol3); + AlgorithmVolumeDilate(NULL, myCache.tempVol3, voldilatemm, AlgorithmVolumeDilate::NEAREST, myCache.tempVol2, myCache.volDilateRoi); + vector paddedDims = myCache.tempVol2->getDimensions(); + for (int64_t k = 0; k < paddedDims[2]; ++k) + { + for (int64_t j = 0; j < paddedDims[1]; ++j) + { + for (int64_t i = 0; i < paddedDims[0]; ++i) + { + if (myCache.tempVol2->getValue(i, j, k) > 0.0f) + { + for (int n = 0; n < 6; ++n) + { + VoxelIJK thisVoxel = VoxelIJK(i + neighbors[j * 3 + 0], + j + neighbors[j * 3 + 1], + k + neighbors[j * 3 + 2]); + if (myCache.tempVol2->indexValid(thisVoxel.m_ijk) && + myCache.tempVol2->getValue(thisVoxel.m_ijk) == 0.0f)//trick: if it is merely touching an FOV edge, cubic won't cause ringing there, so ignore that voxel + { + myCache.edgeVoxelList.push_back(VoxelIJK(i, j, k)); + break; + } + } + } else { + myCache.outsideRoiVoxels.push_back(VoxelIJK(i, j, k)); + } + } + } + } + if (warpfield != NULL) + { + AlgorithmVolumeWarpfieldResample(NULL, myCache.tempVol2, warpfield, myCache.refDims, myCache.refSform, VolumeFile::TRILINEAR, &resampledRoi); + } else { + AlgorithmVolumeAffineResample(NULL, myCache.tempVol2, *affine, myCache.refDims, myCache.refSform, VolumeFile::TRILINEAR, &resampledRoi); + } + } else { + for (int64_t k = 0; k < inDims[2]; ++k)//we have to loop through all voxels anyway to find the ones outside the ROI { - myCache.tempVol2->setValue(0.0f, myCache.inVolMap[j].m_ijk[0] - myCache.inOffset[0], - myCache.inVolMap[j].m_ijk[1] - myCache.inOffset[1], - myCache.inVolMap[j].m_ijk[2] - myCache.inOffset[2]); + for (int64_t j = 0; j < inDims[1]; ++j) + { + for (int64_t i = 0; i < inDims[0]; ++i) + { + if (myCache.tempVol2->getValue(i, j, k) > 0.0f) + { + for (int k = 0; k < 6; ++k) + { + VoxelIJK thisVoxel = VoxelIJK(myCache.inVolMap[j].m_ijk[0] - myCache.inOffset[0] + neighbors[k * 3 + 0], + myCache.inVolMap[j].m_ijk[1] - myCache.inOffset[1] + neighbors[k * 3 + 1], + myCache.inVolMap[j].m_ijk[2] - myCache.inOffset[2] + neighbors[k * 3 + 2]); + if (myCache.tempVol2->indexValid(thisVoxel.m_ijk) && + myCache.tempVol2->getValue(thisVoxel.m_ijk) == 0.0f) + { + myCache.edgeVoxelList.push_back(VoxelIJK(myCache.inVolMap[j].m_ijk[0] - myCache.inOffset[0], + myCache.inVolMap[j].m_ijk[1] - myCache.inOffset[1], + myCache.inVolMap[j].m_ijk[2] - myCache.inOffset[2])); + break; + } + } + } else { + myCache.outsideRoiVoxels.push_back(VoxelIJK(i, j, k)); + } + } + } + } + if (warpfield != NULL) + { + AlgorithmVolumeWarpfieldResample(NULL, myCache.tempVol2, warpfield, myCache.refDims, myCache.refSform, VolumeFile::TRILINEAR, &resampledRoi); + } else { + AlgorithmVolumeAffineResample(NULL, myCache.tempVol2, *affine, myCache.refDims, myCache.refSform, VolumeFile::TRILINEAR, &resampledRoi); + } + } + for (int64_t k = 0; k < myCache.refDims[2]; ++k) + { + for (int64_t j = 0; j < myCache.refDims[1]; ++j) + { + for (int64_t i = 0; i < myCache.refDims[0]; ++i) + { + if (resampledRoi.getValue(i, j, k) > 0.5f) + { + myCache.insideResampledRoiVoxels.push_back(VoxelIJK(i, j, k)); + } + } } - myCache.volPadding.doPadding(myCache.tempVol2, myCache.volDilateRoi, 1.0f); } } } void processRowSurface(ResampleCache& myCache, const vector& inRow, vector& outRow, const CiftiXML& myInputXML, const float& surfdilatemm, const bool& surfLargest, const int& unassignedLabelKey, const int64_t& row, - const AlgorithmMetricDilate::Method& surfDilateMethod, const float& surfDilateExponent) + const AlgorithmMetricDilate::Method& surfDilateMethod, const float& surfDilateExponent, const bool surfLegacyCutoff) { bool labelMode = (myInputXML.getMappingType(CiftiXML::ALONG_COLUMN) == CiftiMappingType::LABELS); int inMapSize = (int)myCache.inSurfMap.size(), outMapSize = (int)myCache.outSurfMap.size(); @@ -691,7 +794,7 @@ namespace MetricFile* toUse = &(myCache.tempMetric1); if (surfdilatemm > 0.0f) { - AlgorithmMetricDilate(NULL, toUse, myCache.newSphere, surfdilatemm, &(myCache.tempMetric2), &(myCache.surfDilateRoi), NULL, 0, surfDilateMethod, surfDilateExponent); + AlgorithmMetricDilate(NULL, toUse, myCache.newSphere, surfdilatemm, &(myCache.tempMetric2), &(myCache.surfDilateRoi), NULL, 0, surfDilateMethod, surfDilateExponent, NULL, surfLegacyCutoff); toUse = &(myCache.tempMetric2); } const float* outData = toUse->getValuePointerForColumn(0); @@ -702,6 +805,70 @@ namespace } } } + + void processRowVolume(ResampleCache& myCache, const vector& inRow, vector& outRow, const CiftiXML& myInputXML, + const float& voldilatemm, const AlgorithmVolumeDilate::Method& volDilateMethod, const float& volDilateExponent, const int& unassignedLabelKey, + const VolumeFile* warpfield, const FloatMatrix* affine, const VolumeFile::InterpType& myVolMethod, const bool volLegacyCutoff) + { + bool labelMode = (myInputXML.getMappingType(CiftiXML::ALONG_COLUMN) == CiftiMappingType::LABELS); + bool doEdgeAdjust = (myVolMethod == VolumeFile::VolumeFile::CUBIC);//if they ask for cubic on label data, let strange things happen (there will be warnings from resample) + if (labelMode)//gets initialized to 0 when not using labels + { + myCache.inputVol->setValueAllVoxels(unassignedLabelKey); + } + int inMapSize = (int)myCache.inVolMap.size(), outMapSize = (int)myCache.outVolMap.size(); + for (int j = 0; j < inMapSize; ++j) + { + myCache.inputVol->setValue(inRow[myCache.inVolMap[j].m_ciftiIndex], myCache.inVolMap[j].m_ijk[0] - myCache.inOffset[0], + myCache.inVolMap[j].m_ijk[1] - myCache.inOffset[1], + myCache.inVolMap[j].m_ijk[2] - myCache.inOffset[2]); + } + VolumeFile* toResample = myCache.inputVol; + if (voldilatemm > 0.0f) + {//inputVol is kept at original dimensions, don't overwrite it + myCache.volPadding.doPadding(myCache.inputVol, myCache.tempVol2); + AlgorithmVolumeDilate(NULL, myCache.tempVol2, voldilatemm, volDilateMethod, myCache.tempVol3, myCache.volDilateRoi, NULL, -1, volDilateExponent, volLegacyCutoff); + toResample = myCache.tempVol3; + } + if (doEdgeAdjust) + { + double edgeSum = 0.0;//calculate the average edge value so we can use it to reduce interpolation edge effects (particularly cubic) + for (int64_t j = 0; j < (int64_t)myCache.edgeVoxelList.size(); ++j) + { + edgeSum += toResample->getValue(myCache.edgeVoxelList[j]); + } + float edgeAverage = 0.0f; + if (!myCache.edgeVoxelList.empty()) + { + edgeAverage = edgeSum / myCache.edgeVoxelList.size(); + } + for (int j = 0; j < int(myCache.outsideRoiVoxels.size()); ++j) + { + toResample->setValue(edgeAverage, myCache.outsideRoiVoxels[j]); + } + }//toResample is never 2, so we can use 2 + if (warpfield != NULL) + { + AlgorithmVolumeWarpfieldResample(NULL, toResample, warpfield, myCache.refDims, myCache.refSform, myVolMethod, myCache.tempVol2); + } else { + AlgorithmVolumeAffineResample(NULL, toResample, *affine, myCache.refDims, myCache.refSform, myVolMethod, myCache.tempVol2); + } + if (doEdgeAdjust) + { + vector tempFrame(myCache.refDims[0] * myCache.refDims[1] * myCache.refDims[2], 0.0f);//for consistency with COLUMN, copy out only the selected voxels + for (int j = 0; j < int(myCache.insideResampledRoiVoxels.size()); ++j) + { + tempFrame[myCache.tempVol2->getIndex(myCache.insideResampledRoiVoxels[j])] = myCache.tempVol2->getValue(myCache.insideResampledRoiVoxels[j]); + } + myCache.tempVol2->setFrame(tempFrame.data()); + } + for (int j = 0; j < outMapSize; ++j) + { + outRow[myCache.outVolMap[j].m_ciftiIndex] = myCache.tempVol2->getValue(myCache.outVolMap[j].m_ijk[0] - myCache.refOffset[0], + myCache.outVolMap[j].m_ijk[1] - myCache.refOffset[1], + myCache.outVolMap[j].m_ijk[2] - myCache.refOffset[2]); + } + } } AlgorithmCiftiResample::AlgorithmCiftiResample(ProgressObject* myProgObj, const CiftiFile* myCiftiIn, const int& direction, const CiftiFile* myTemplate, const int& templateDir, @@ -712,7 +879,8 @@ AlgorithmCiftiResample::AlgorithmCiftiResample(ProgressObject* myProgObj, const const SurfaceFile* curRightSphere, const SurfaceFile* newRightSphere, const MetricFile* curRightAreas, const MetricFile* newRightAreas, const SurfaceFile* curCerebSphere, const SurfaceFile* newCerebSphere, const MetricFile* curCerebAreas, const MetricFile* newCerebAreas, const AlgorithmVolumeDilate::Method& volDilateMethod, const float& volDilateExponent, - const AlgorithmMetricDilate::Method& surfDilateMethod, const float& surfDilateExponent) : AbstractAlgorithm(myProgObj) + const AlgorithmMetricDilate::Method& surfDilateMethod, const float& surfDilateExponent, + const bool volLegacyCutoff, const bool surfLegacyCutoff) : AbstractAlgorithm(myProgObj) { LevelProgress myProgress(myProgObj); pair myError = checkForErrors(myCiftiIn, direction, myTemplate, templateDir, mySurfMethod, @@ -723,7 +891,6 @@ AlgorithmCiftiResample::AlgorithmCiftiResample(ProgressObject* myProgObj, const const CiftiXML& myInputXML = myCiftiIn->getCiftiXML(); CiftiXML myOutXML = myInputXML; myOutXML.setMap(direction, *(myTemplate->getCiftiXML().getMap(templateDir))); - bool labelMode = (myInputXML.getMappingType(CiftiXML::ALONG_COLUMN) == CiftiMappingType::LABELS); const CiftiBrainModelsMap& outModels = myOutXML.getBrainModelsMap(direction); vector surfList = outModels.getSurfaceStructureList(), volList = outModels.getVolumeStructureList(); myCiftiOut->setCiftiXML(myOutXML); @@ -757,17 +924,18 @@ AlgorithmCiftiResample::AlgorithmCiftiResample(ProgressObject* myProgObj, const throw AlgorithmException("unsupported surface structure: " + StructureEnum::toGuiName(surfList[i])); break; } - processSurfaceComponent(myCiftiIn, direction, surfList[i], mySurfMethod, myCiftiOut, surfLargest, surfdilatemm, curSphere, newSphere, curAreas, newAreas, surfDilateMethod, surfDilateExponent); + processSurfaceComponent(myCiftiIn, direction, surfList[i], mySurfMethod, myCiftiOut, surfLargest, surfdilatemm, curSphere, newSphere, curAreas, newAreas, surfDilateMethod, surfDilateExponent, surfLegacyCutoff); } for (int i = 0; i < (int)volList.size(); ++i) { - processVolumeWarpfield(myCiftiIn, direction, volList[i], myVolMethod, myCiftiOut, voldilatemm, warpfield, volDilateMethod, volDilateExponent); + processVolume(myCiftiIn, direction, volList[i], myVolMethod, myCiftiOut, voldilatemm, warpfield, NULL, volDilateMethod, volDilateExponent, volLegacyCutoff); } } else {//avoid cifti separate/replace with ALONG_ROW + bool labelMode = (myInputXML.getMappingType(CiftiXML::ALONG_COLUMN) == CiftiMappingType::LABELS); vector surfList = outModels.getSurfaceStructureList(), volList = outModels.getVolumeStructureList(); int numSurfStructs = (int)surfList.size(), numVolStructs = (int)volList.size(); vector unassignedLabelKey; - if (myInputXML.getMappingType(CiftiXML::ALONG_COLUMN) == CiftiMappingType::LABELS) + if (labelMode) { const CiftiLabelsMap& myLabelMap = myInputXML.getLabelsMap(CiftiXML::ALONG_COLUMN); unassignedLabelKey.resize(myLabelMap.getLength()); @@ -777,7 +945,7 @@ AlgorithmCiftiResample::AlgorithmCiftiResample(ProgressObject* myProgObj, const } } map surfCache, volCache;//could make them different types, but whatever - two variables in case of structure overlap in surface and volume, as some members may get used by both - setupRowResampling(surfCache, volCache, myCiftiIn, myCiftiOut, mySurfMethod, voldilatemm, + setupRowResampling(surfCache, volCache, myCiftiIn, myCiftiOut, mySurfMethod, voldilatemm, NULL, warpfield, curLeftSphere, newLeftSphere, curLeftAreas, newLeftAreas, curRightSphere, newRightSphere, curRightAreas, newRightAreas, curCerebSphere, newCerebSphere, curCerebAreas, newCerebAreas); @@ -790,38 +958,13 @@ AlgorithmCiftiResample::AlgorithmCiftiResample(ProgressObject* myProgObj, const { map::iterator iter = surfCache.find(surfList[i]); CaretAssert(iter != surfCache.end()); - processRowSurface(iter->second, inRow, outRow, myInputXML, surfdilatemm, surfLargest, unassignedLabelKey[row], row, surfDilateMethod, surfDilateExponent); + processRowSurface(iter->second, inRow, outRow, myInputXML, surfdilatemm, surfLargest, unassignedLabelKey[row], row, surfDilateMethod, surfDilateExponent, surfLegacyCutoff); } for (int i = 0; i < numVolStructs; ++i) { map::iterator iter = volCache.find(volList[i]); CaretAssert(iter != volCache.end()); - ResampleCache& myCache = iter->second; - if (labelMode)//gets initialized to 0 when not using labels - { - myCache.tempVol1->setValueAllVoxels(unassignedLabelKey[row]); - } - int inMapSize = (int)myCache.inVolMap.size(), outMapSize = (int)myCache.outVolMap.size(); - for (int j = 0; j < inMapSize; ++j) - { - myCache.tempVol1->setValue(inRow[myCache.inVolMap[j].m_ciftiIndex], myCache.inVolMap[j].m_ijk[0] - myCache.inOffset[0], - myCache.inVolMap[j].m_ijk[1] - myCache.inOffset[1], - myCache.inVolMap[j].m_ijk[2] - myCache.inOffset[2]); - } - const VolumeFile* toResample = myCache.tempVol1; - if (voldilatemm > 0.0f) - { - myCache.volPadding.doPadding(myCache.tempVol1, myCache.tempVol2); - AlgorithmVolumeDilate(NULL, myCache.tempVol2, voldilatemm, volDilateMethod, myCache.tempVol3, myCache.volDilateRoi, NULL, -1, volDilateExponent); - toResample = myCache.tempVol3; - } - AlgorithmVolumeWarpfieldResample(NULL, toResample, warpfield, myCache.refDims, myCache.refSform, myVolMethod, myCache.tempVol2); - for (int j = 0; j < outMapSize; ++j) - { - outRow[myCache.outVolMap[j].m_ciftiIndex] = myCache.tempVol2->getValue(myCache.outVolMap[j].m_ijk[0] - myCache.refOffset[0], - myCache.outVolMap[j].m_ijk[1] - myCache.refOffset[1], - myCache.outVolMap[j].m_ijk[2] - myCache.refOffset[2]); - } + processRowVolume(iter->second, inRow, outRow, myInputXML, voldilatemm, volDilateMethod, volDilateExponent, unassignedLabelKey[row], warpfield, NULL, myVolMethod, volLegacyCutoff); } myCiftiOut->setRow(outRow.data(), row); } @@ -836,7 +979,8 @@ AlgorithmCiftiResample::AlgorithmCiftiResample(ProgressObject* myProgObj, const const SurfaceFile* curRightSphere, const SurfaceFile* newRightSphere, const MetricFile* curRightAreas, const MetricFile* newRightAreas, const SurfaceFile* curCerebSphere, const SurfaceFile* newCerebSphere, const MetricFile* curCerebAreas, const MetricFile* newCerebAreas, const AlgorithmVolumeDilate::Method& volDilateMethod, const float& volDilateExponent, - const AlgorithmMetricDilate::Method& surfDilateMethod, const float& surfDilateExponent) : AbstractAlgorithm(myProgObj) + const AlgorithmMetricDilate::Method& surfDilateMethod, const float& surfDilateExponent, + const bool volLegacyCutoff, const bool surfLegacyCutoff) : AbstractAlgorithm(myProgObj) { LevelProgress myProgress(myProgObj); pair myError = checkForErrors(myCiftiIn, direction, myTemplate, templateDir, mySurfMethod, @@ -880,17 +1024,18 @@ AlgorithmCiftiResample::AlgorithmCiftiResample(ProgressObject* myProgObj, const throw AlgorithmException("unsupported surface structure: " + StructureEnum::toGuiName(surfList[i])); break; } - processSurfaceComponent(myCiftiIn, direction, surfList[i], mySurfMethod, myCiftiOut, surfLargest, surfdilatemm, curSphere, newSphere, curAreas, newAreas, surfDilateMethod, surfDilateExponent); + processSurfaceComponent(myCiftiIn, direction, surfList[i], mySurfMethod, myCiftiOut, surfLargest, surfdilatemm, curSphere, newSphere, curAreas, newAreas, surfDilateMethod, surfDilateExponent, surfLegacyCutoff); } for (int i = 0; i < (int)volList.size(); ++i) { - processVolumeAffine(myCiftiIn, direction, volList[i], myVolMethod, myCiftiOut, voldilatemm, affine, volDilateMethod, volDilateExponent); + processVolume(myCiftiIn, direction, volList[i], myVolMethod, myCiftiOut, voldilatemm, NULL, &affine, volDilateMethod, volDilateExponent, volLegacyCutoff); } } else {//avoid cifti separate/replace with ALONG_ROW + bool labelMode = (myInputXML.getMappingType(CiftiXML::ALONG_COLUMN) == CiftiMappingType::LABELS); vector surfList = outModels.getSurfaceStructureList(), volList = outModels.getVolumeStructureList(); int numSurfStructs = (int)surfList.size(), numVolStructs = (int)volList.size(); vector unassignedLabelKey; - if (myInputXML.getMappingType(CiftiXML::ALONG_COLUMN) == CiftiMappingType::LABELS) + if (labelMode) { const CiftiLabelsMap& myLabelMap = myInputXML.getLabelsMap(CiftiXML::ALONG_COLUMN); unassignedLabelKey.resize(myLabelMap.getLength()); @@ -900,7 +1045,7 @@ AlgorithmCiftiResample::AlgorithmCiftiResample(ProgressObject* myProgObj, const } } map surfCache, volCache;//could make them different types, but whatever - two variables in case of structure overlap in surface and volume, as some members may get used by both - setupRowResampling(surfCache, volCache, myCiftiIn, myCiftiOut, mySurfMethod, voldilatemm, + setupRowResampling(surfCache, volCache, myCiftiIn, myCiftiOut, mySurfMethod, voldilatemm, &affine, NULL, curLeftSphere, newLeftSphere, curLeftAreas, newLeftAreas, curRightSphere, newRightSphere, curRightAreas, newRightAreas, curCerebSphere, newCerebSphere, curCerebAreas, newCerebAreas); @@ -913,34 +1058,13 @@ AlgorithmCiftiResample::AlgorithmCiftiResample(ProgressObject* myProgObj, const { map::iterator iter = surfCache.find(surfList[i]); CaretAssert(iter != surfCache.end()); - processRowSurface(iter->second, inRow, outRow, myInputXML, surfdilatemm, surfLargest, unassignedLabelKey[row], row, surfDilateMethod, surfDilateExponent); + processRowSurface(iter->second, inRow, outRow, myInputXML, surfdilatemm, surfLargest, unassignedLabelKey[row], row, surfDilateMethod, surfDilateExponent, surfLegacyCutoff); } for (int i = 0; i < numVolStructs; ++i) { map::iterator iter = volCache.find(volList[i]); CaretAssert(iter != volCache.end()); - ResampleCache& myCache = iter->second; - int inMapSize = (int)myCache.inVolMap.size(), outMapSize = (int)myCache.outVolMap.size(); - for (int j = 0; j < inMapSize; ++j) - { - myCache.tempVol1->setValue(inRow[myCache.inVolMap[j].m_ciftiIndex], myCache.inVolMap[j].m_ijk[0] - myCache.inOffset[0], - myCache.inVolMap[j].m_ijk[1] - myCache.inOffset[1], - myCache.inVolMap[j].m_ijk[2] - myCache.inOffset[2]); - } - const VolumeFile* toResample = myCache.tempVol1; - if (voldilatemm > 0.0f) - { - myCache.volPadding.doPadding(myCache.tempVol1, myCache.tempVol2); - AlgorithmVolumeDilate(NULL, myCache.tempVol2, voldilatemm, volDilateMethod, myCache.tempVol3, myCache.volDilateRoi, NULL, -1, volDilateExponent); - toResample = myCache.tempVol3; - } - AlgorithmVolumeAffineResample(NULL, toResample, affine, myCache.refDims, myCache.refSform, myVolMethod, myCache.tempVol2); - for (int j = 0; j < outMapSize; ++j) - { - outRow[myCache.outVolMap[j].m_ciftiIndex] = myCache.tempVol2->getValue(myCache.outVolMap[j].m_ijk[0] - myCache.refOffset[0], - myCache.outVolMap[j].m_ijk[1] - myCache.refOffset[1], - myCache.outVolMap[j].m_ijk[2] - myCache.refOffset[2]); - } + processRowVolume(iter->second, inRow, outRow, myInputXML, voldilatemm, volDilateMethod, volDilateExponent, unassignedLabelKey[row], NULL, &affine, myVolMethod, volLegacyCutoff); } myCiftiOut->setRow(outRow.data(), row); } @@ -950,7 +1074,7 @@ AlgorithmCiftiResample::AlgorithmCiftiResample(ProgressObject* myProgObj, const void AlgorithmCiftiResample::processSurfaceComponent(const CiftiFile* myCiftiIn, const int& direction, const StructureEnum::Enum& myStruct, const SurfaceResamplingMethodEnum::Enum& mySurfMethod, CiftiFile* myCiftiOut, const bool& surfLargest, const float& surfdilatemm, const SurfaceFile* curSphere, const SurfaceFile* newSphere, const MetricFile* curAreas, const MetricFile* newAreas, - const AlgorithmMetricDilate::Method& surfDilateMethod, const float& surfDilateExponent) + const AlgorithmMetricDilate::Method& surfDilateMethod, const float& surfDilateExponent, const bool surfLegacyCutoff) { const CiftiXML& myInputXML = myCiftiIn->getCiftiXML(); if (myInputXML.getMappingType(1 - direction) == CiftiMappingType::LABELS) @@ -997,7 +1121,7 @@ void AlgorithmCiftiResample::processSurfaceComponent(const CiftiFile* myCiftiIn, float tempf = (resampleROI.getValue(j, 0) > 0.0f) ? 0.0f : 1.0f;//make an inverse ROI invertResampleROI.setValue(j, 0, tempf); } - AlgorithmMetricDilate(NULL, &newMetric, newSphere, surfdilatemm, &newDilate, &invertResampleROI, NULL, -1, surfDilateMethod, surfDilateExponent);//we could get the data roi from the template cifti and use it here + AlgorithmMetricDilate(NULL, &newMetric, newSphere, surfdilatemm, &newDilate, &invertResampleROI, NULL, -1, surfDilateMethod, surfDilateExponent, NULL, surfLegacyCutoff);//we could get the data roi from the template cifti and use it here newMetric.clear(); newUse = &newDilate; } @@ -1008,83 +1132,158 @@ void AlgorithmCiftiResample::processSurfaceComponent(const CiftiFile* myCiftiIn, } } -void AlgorithmCiftiResample::processVolumeWarpfield(const CiftiFile* myCiftiIn, const int& direction, const StructureEnum::Enum& myStruct, const VolumeFile::InterpType& myVolMethod, - CiftiFile* myCiftiOut, const float& voldilatemm, const VolumeFile* warpfield, - const AlgorithmVolumeDilate::Method& volDilateMethod, const float& volDilateExponent) +void AlgorithmCiftiResample::processVolume(const CiftiFile* myCiftiIn, const int& direction, const StructureEnum::Enum& myStruct, const VolumeFile::InterpType& myVolMethod, + CiftiFile* myCiftiOut, const float& voldilatemm, const VolumeFile* warpfield, const FloatMatrix* affine, + const AlgorithmVolumeDilate::Method& volDilateMethod, const float& volDilateExponent, const bool volLegacyCutoff) { - VolumeFile origData, origROI, origDilate, *origProcess; + VolumeFile origData, origDilate, origROI, ROIDilate, *origProcess, *ROIProcess; origProcess = &origData; + ROIProcess = &origROI; int64_t offset[3]; + const int neighbors[] = { 0, 0, -1, + 0, -1, 0, + -1, 0, 0, + 1, 0, 0, + 0, 1, 0, + 0, 0, 1 }; + const bool doEdgeAdjust = (myVolMethod == VolumeFile::CUBIC);//if someone asks for cubic resampling of label data, allow strange things to happen (there will be a warning from resample) + vector edgeVoxelList; AlgorithmCiftiSeparate(NULL, myCiftiIn, direction, myStruct, &origData, offset, &origROI, true); + vector origDims = origData.getDimensions(); if (voldilatemm > 0.0f) { - VolumeFile invertROI; - invertROI.reinitialize(origROI.getOriginalDimensions(), origROI.getSform()); - vector dims; - origROI.getDimensions(dims); - int64_t frameSize = dims[0] * dims[1] * dims[2]; - const float* roiFrame = origROI.getFrame(); - vector invertFrame(frameSize); - for (int64_t j = 0; j < frameSize; ++j) + vector spatialDims = origDims;//make the bad voxel roi by inverting + spatialDims.resize(3); + int64_t framesize = origDims[0] * origDims[1] * origDims[2]; + vector scratchframe(framesize); + const float* roiframe = origROI.getFrame(); + for (int64_t i = 0; i < framesize; ++i) { - invertFrame[j] = (roiFrame[j] > 0.0f) ? 0.0f : 1.0f; + scratchframe[i] = (roiframe[i] > 0.0f ? 0.0f : 1.0f); } - invertROI.setFrame(invertFrame.data()); + VolumeFile origPad, invertROI, invertROIPad; + invertROI.reinitialize(spatialDims, origData.getSform()); + invertROI.setFrame(scratchframe.data()); VolumePaddingHelper mypadding = VolumePaddingHelper::padMM(&origData, voldilatemm); - VolumeFile origPad, invertROIPad; mypadding.doPadding(&origData, &origPad); - origData.clear();//delete data we no longer need to keep memory use down + origData.clear();//free data copies we no longer need mypadding.doPadding(&invertROI, &invertROIPad, 1.0f);//pad with ones since this is an inverted ROI - AlgorithmVolumeDilate(NULL, &origPad, voldilatemm, volDilateMethod, &origDilate, &invertROIPad, NULL, -1, volDilateExponent); + AlgorithmVolumeDilate(NULL, &origPad, voldilatemm, volDilateMethod, &origDilate, &invertROIPad, NULL, -1, volDilateExponent, volLegacyCutoff); origPad.clear();//ditto origProcess = &origDilate; + if (doEdgeAdjust) + {//we need to use the dilated ROI + VolumeFile ROIPad; + mypadding.doPadding(&origROI, &ROIPad); + AlgorithmVolumeDilate(NULL, &ROIPad, voldilatemm, AlgorithmVolumeDilate::NEAREST, &ROIDilate, &invertROIPad); + ROIProcess = &ROIDilate; + vector paddedDims = ROIPad.getDimensions(); + for (int64_t k = 0; k < paddedDims[2]; ++k) + { + for (int64_t j = 0; j < paddedDims[1]; ++j) + { + for (int64_t i = 0; i < paddedDims[0]; ++i) + { + if (ROIDilate.getValue(i, j, k) > 0.0f) + { + for (int n = 0; n < 6; ++n) + { + VoxelIJK thisVoxel = VoxelIJK(i + neighbors[j * 3 + 0], + j + neighbors[j * 3 + 1], + k + neighbors[j * 3 + 2]); + if (ROIDilate.indexValid(thisVoxel.m_ijk) && + ROIDilate.getValue(thisVoxel.m_ijk) == 0.0f)//trick: if it is merely touching an FOV edge, cubic won't cause ringing there, so ignore that voxel + { + edgeVoxelList.push_back(VoxelIJK(i, j, k)); + break; + } + } + } + } + } + } + } + } else {//if we don't dilate, we can use cifti to find the used voxels + if (doEdgeAdjust) + { + vector myMap = myCiftiIn->getCiftiXML().getBrainModelsMap(direction).getVolumeStructureMap(myStruct); + for (int64_t i = 0; i < (int64_t)myMap.size(); ++i) + { + for (int j = 0; j < 6; ++j) + { + VoxelIJK thisVoxel = VoxelIJK(myMap[i].m_ijk[0] - offset[0] + neighbors[j * 3 + 0], + myMap[i].m_ijk[1] - offset[1] + neighbors[j * 3 + 1], + myMap[i].m_ijk[2] - offset[2] + neighbors[j * 3 + 2]); + if (origROI.indexValid(thisVoxel.m_ijk) && + origROI.getValue(thisVoxel.m_ijk) == 0.0f)//trick: if it is merely touching an FOV edge, cubic won't cause ringing there, so ignore that voxel + { + edgeVoxelList.push_back(VoxelIJK(myMap[i].m_ijk[0] - offset[0], + myMap[i].m_ijk[1] - offset[1], + myMap[i].m_ijk[2] - offset[2])); + break; + } + } + } + } + } + if (doEdgeAdjust) + { + vector currentDims = origProcess->getDimensions(); + int64_t framesize = currentDims[0] * currentDims[1] * currentDims[2]; + vector scratchframe(framesize); + const float* ROIFrame = ROIProcess->getFrame(); + for (int64_t b = 0; b < origDims[3]; ++b) + {//input is cifti, no RGB or complex types + double edgesum = 0.0; + for (int64_t i = 0; i < (int64_t)edgeVoxelList.size(); ++i) + { + edgesum += origProcess->getValue(edgeVoxelList[i].m_ijk, b); + } + float edgeMean = edgesum / edgeVoxelList.size(); + const float* dataFrame = origProcess->getFrame(b); + for (int64_t i = 0; i < framesize; ++i) + { + scratchframe[i] = (ROIFrame[i] > 0.0f ? dataFrame[i] : edgeMean);//trick: set the background to the edge mean so we don't need to readjust data values after resampling + } + origProcess->setFrame(scratchframe.data(), b); + } } VolumeFile newVolume; int64_t refdims[3], refoffset[3]; vector > refsform; AlgorithmCiftiSeparate::getCroppedVolSpace(myCiftiOut, direction, myStruct, refdims, refsform, refoffset); - AlgorithmVolumeWarpfieldResample(NULL, origProcess, warpfield, refdims, refsform, myVolMethod, &newVolume); - origProcess->clear();//ditto - AlgorithmCiftiReplaceStructure(NULL, myCiftiOut, direction, myStruct, &newVolume, true); -} - -void AlgorithmCiftiResample::processVolumeAffine(const CiftiFile* myCiftiIn, const int& direction, const StructureEnum::Enum& myStruct, const VolumeFile::InterpType& myVolMethod, - CiftiFile* myCiftiOut, const float& voldilatemm, const FloatMatrix& affine, - const AlgorithmVolumeDilate::Method& volDilateMethod, const float& volDilateExponent) -{ - VolumeFile origData, origROI, origDilate, *origProcess; - origProcess = &origData; - int64_t offset[3]; - AlgorithmCiftiSeparate(NULL, myCiftiIn, direction, myStruct, &origData, offset, &origROI, true); - if (voldilatemm > 0.0f) + if (warpfield != NULL) { - VolumeFile invertROI; - invertROI.reinitialize(origROI.getOriginalDimensions(), origROI.getSform()); - vector dims; - origROI.getDimensions(dims); - int64_t frameSize = dims[0] * dims[1] * dims[2]; - const float* roiFrame = origROI.getFrame(); - vector invertFrame(frameSize); - for (int64_t j = 0; j < frameSize; ++j) + AlgorithmVolumeWarpfieldResample(NULL, origProcess, warpfield, refdims, refsform, myVolMethod, &newVolume); + } else { + AlgorithmVolumeAffineResample(NULL, origProcess, *affine, refdims, refsform, myVolMethod, &newVolume); + } + origProcess->clear(); + if (doEdgeAdjust) + {//to make it obvious when the dilation didn't cover the output ROI, mask the result with the (dilated) input ROI + VolumeFile newROI;//trilinear masked at 0.5 should give somewhat better downsampling behavior than enclosing + if (warpfield != NULL) { - invertFrame[j] = (roiFrame[j] > 0.0f) ? 0.0f : 1.0f; + AlgorithmVolumeWarpfieldResample(NULL, ROIProcess, warpfield, refdims, refsform, VolumeFile::TRILINEAR, &newROI); + } else { + AlgorithmVolumeAffineResample(NULL, ROIProcess, *affine, refdims, refsform, VolumeFile::TRILINEAR, &newROI); + } + const float* roiFrame = newROI.getFrame(); + int64_t framesize = refdims[0] * refdims[1] * refdims[2]; + vector scratchframe(framesize, 0.0f); + for (int64_t b = 0; b < origDims[3]; ++b) + { + const float* dataFrame = newVolume.getFrame(b); + for (int64_t i = 0; i < framesize; ++i) + { + if (roiFrame[i] > 0.5f) + { + scratchframe[i] = dataFrame[i]; + } + } + newVolume.setFrame(scratchframe.data(), b); } - invertROI.setFrame(invertFrame.data()); - VolumePaddingHelper mypadding = VolumePaddingHelper::padMM(&origData, voldilatemm); - VolumeFile origPad, invertROIPad; - mypadding.doPadding(&origData, &origPad); - origData.clear();//delete data we no longer need to keep memory use down - mypadding.doPadding(&invertROI, &invertROIPad, 1.0f);//pad with ones since this is an inverted ROI - AlgorithmVolumeDilate(NULL, &origPad, voldilatemm, volDilateMethod, &origDilate, &invertROIPad, NULL, -1, volDilateExponent); - origPad.clear();//ditto - origProcess = &origDilate; } - VolumeFile newVolume; - int64_t refdims[3], refoffset[3]; - vector > refsform; - AlgorithmCiftiSeparate::getCroppedVolSpace(myCiftiOut, direction, myStruct, refdims, refsform, refoffset); - AlgorithmVolumeAffineResample(NULL, origProcess, affine, refdims, refsform, myVolMethod, &newVolume); - origProcess->clear();//ditto AlgorithmCiftiReplaceStructure(NULL, myCiftiOut, direction, myStruct, &newVolume, true); } diff --git a/src/Algorithms/AlgorithmCiftiResample.h b/src/Algorithms/AlgorithmCiftiResample.h index a08d379b3..7b2aaeded 100644 --- a/src/Algorithms/AlgorithmCiftiResample.h +++ b/src/Algorithms/AlgorithmCiftiResample.h @@ -38,13 +38,11 @@ namespace caret { AlgorithmCiftiResample(); void processSurfaceComponent(const CiftiFile* myCiftiIn, const int& direction, const StructureEnum::Enum& myStruct, const SurfaceResamplingMethodEnum::Enum& mySurfMethod, CiftiFile* myCiftiOut, const bool& surfLargest, const float& surfdilatemm, const SurfaceFile* curSphere, const SurfaceFile* newSphere, - const MetricFile* curAreas, const MetricFile* newAreas, const AlgorithmMetricDilate::Method& surfDilateMethod, const float& surfDilateExponent); - void processVolumeWarpfield(const CiftiFile* myCiftiIn, const int& direction, const StructureEnum::Enum& myStruct, const VolumeFile::InterpType& myVolMethod, - CiftiFile* myCiftiOut, const float& voldilatemm, const VolumeFile* warpfield, - const AlgorithmVolumeDilate::Method& volDilateMethod, const float& volDilateExponent); - void processVolumeAffine(const CiftiFile* myCiftiIn, const int& direction, const StructureEnum::Enum& myStruct, const VolumeFile::InterpType& myVolMethod, - CiftiFile* myCiftiOut, const float& voldilatemm, const FloatMatrix& affine, - const AlgorithmVolumeDilate::Method& volDilateMethod, const float& volDilateExponent); + const MetricFile* curAreas, const MetricFile* newAreas, + const AlgorithmMetricDilate::Method& surfDilateMethod, const float& surfDilateExponent, const bool surfLegacyCutoff); + void processVolume(const CiftiFile* myCiftiIn, const int& direction, const StructureEnum::Enum& myStruct, const VolumeFile::InterpType& myVolMethod, + CiftiFile* myCiftiOut, const float& voldilatemm, const VolumeFile* warpfield, const FloatMatrix* affine, + const AlgorithmVolumeDilate::Method& volDilateMethod, const float& volDilateExponent, const bool volLegacyCutoff); protected: static float getSubAlgorithmWeight(); static float getAlgorithmInternalWeight(); @@ -63,8 +61,9 @@ namespace caret { const SurfaceFile* curLeftSphere, const SurfaceFile* newLeftSphere, const MetricFile* curLeftAreas, const MetricFile* newLeftAreas, const SurfaceFile* curRightSphere, const SurfaceFile* newRightSphere, const MetricFile* curRightAreas, const MetricFile* newRightAreas, const SurfaceFile* curCerebSphere, const SurfaceFile* newCerebSphere, const MetricFile* curCerebAreas, const MetricFile* newCerebAreas, - const AlgorithmVolumeDilate::Method& volDilateMethod = AlgorithmVolumeDilate::WEIGHTED, const float& volDilateExponent = 2.0f, - const AlgorithmMetricDilate::Method& surfDilateMethod = AlgorithmMetricDilate::WEIGHTED, const float& surfDilateExponent = 2.0f); + const AlgorithmVolumeDilate::Method& volDilateMethod = AlgorithmVolumeDilate::WEIGHTED, const float& volDilateExponent = 7.0f, + const AlgorithmMetricDilate::Method& surfDilateMethod = AlgorithmMetricDilate::WEIGHTED, const float& surfDilateExponent = 6.0f, + const bool volLegacyCutoff = false, const bool surfLegacyCutoff = false); AlgorithmCiftiResample(ProgressObject* myProgObj, const CiftiFile* myCiftiIn, const int& direction, const CiftiFile* myTemplate, const int& templateDir, const SurfaceResamplingMethodEnum::Enum& mySurfMethod, const VolumeFile::InterpType& myVolMethod, CiftiFile* myCiftiOut, @@ -73,8 +72,9 @@ namespace caret { const SurfaceFile* curLeftSphere, const SurfaceFile* newLeftSphere, const MetricFile* curLeftAreas, const MetricFile* newLeftAreas, const SurfaceFile* curRightSphere, const SurfaceFile* newRightSphere, const MetricFile* curRightAreas, const MetricFile* newRightAreas, const SurfaceFile* curCerebSphere, const SurfaceFile* newCerebSphere, const MetricFile* curCerebAreas, const MetricFile* newCerebAreas, - const AlgorithmVolumeDilate::Method& volDilateMethod = AlgorithmVolumeDilate::WEIGHTED, const float& volDilateExponent = 2.0f, - const AlgorithmMetricDilate::Method& surfDilateMethod = AlgorithmMetricDilate::WEIGHTED, const float& surfDilateExponent = 2.0f); + const AlgorithmVolumeDilate::Method& volDilateMethod = AlgorithmVolumeDilate::WEIGHTED, const float& volDilateExponent = 7.0f, + const AlgorithmMetricDilate::Method& surfDilateMethod = AlgorithmMetricDilate::WEIGHTED, const float& surfDilateExponent = 6.0f, + const bool volLegacyCutoff = false, const bool surfLegacyCutoff = false); static OperationParameters* getParameters(); static void useParameters(OperationParameters* myParams, ProgressObject* myProgObj); diff --git a/src/Algorithms/AlgorithmMetricDilate.cxx b/src/Algorithms/AlgorithmMetricDilate.cxx index cbc3dfc25..4d7d3e8bb 100644 --- a/src/Algorithms/AlgorithmMetricDilate.cxx +++ b/src/Algorithms/AlgorithmMetricDilate.cxx @@ -23,6 +23,7 @@ #include "CaretAssert.h" #include "CaretOMP.h" +#include "FastStatistics.h" #include "GeodesicHelper.h" #include "MetricFile.h" #include "PaletteColorMapping.h" @@ -71,10 +72,12 @@ OperationParameters* AlgorithmMetricDilate::getParameters() ret->createOptionalParameter(10, "-linear", "fill in values with linear interpolation along strongest gradient"); OptionalParameter* exponentOpt = ret->createOptionalParameter(8, "-exponent", "use a different exponent in the weighting function"); - exponentOpt->addDoubleParameter(1, "exponent", "exponent 'n' to use in (area / (distance ^ n)) as the weighting function (default 2)"); + exponentOpt->addDoubleParameter(1, "exponent", "exponent 'n' to use in (area / (distance ^ n)) as the weighting function (default 6)"); OptionalParameter* corrAreaOpt = ret->createOptionalParameter(11, "-corrected-areas", "vertex areas to use instead of computing them from the surface"); corrAreaOpt->addMetricParameter(1, "area-metric", "the corrected vertex areas, as a metric"); + + ret->createOptionalParameter(12, "-legacy-cutoff", "use the old method of choosing how many vertices to use when calulating the dilated value with weighted method"); ret->setHelpText( AString("For all metric vertices that are designated as bad, if they neighbor a non-bad vertex with data or are within the specified distance of such a vertex, ") + @@ -84,8 +87,8 @@ OperationParameters* AlgorithmMetricDilate::getParameters() "If -bad-vertex-roi is specified, only vertices with a positive value in the ROI are bad. " + "If it is not specified, only vertices that have data, with a value of zero, are bad. " + "If -data-roi is not specified, all vertices are assumed to have data.\n\n" + - - "Note that the -corrected-areas option uses an approximate correction for the change in distances along a group average surface." + "Note that the -corrected-areas option uses an approximate correction for the change in distances along a group average surface.\n\n" + + "To get the behavior of version 1.3.2 or earlier, use '-legacy-cutoff -exponent 2'." ); return ret; } @@ -131,7 +134,7 @@ void AlgorithmMetricDilate::useParameters(OperationParameters* myParams, Progres methodSpecified = true; myMethod = LINEAR; } - float exponent = 2.0f; + float exponent = 6.0f; OptionalParameter* exponentOpt = myParams->getOptionalParameter(8); if (exponentOpt->m_present) { @@ -143,12 +146,13 @@ void AlgorithmMetricDilate::useParameters(OperationParameters* myParams, Progres { corrAreas = corrAreaOpt->getMetric(1); } - AlgorithmMetricDilate(myProgObj, myMetric, mySurf, distance, myMetricOut, badNodeRoi, dataRoi, columnNum, myMethod, exponent, corrAreas); + bool legacyCutoff = myParams->getOptionalParameter(12)->m_present; + AlgorithmMetricDilate(myProgObj, myMetric, mySurf, distance, myMetricOut, badNodeRoi, dataRoi, columnNum, myMethod, exponent, corrAreas, legacyCutoff); } AlgorithmMetricDilate::AlgorithmMetricDilate(ProgressObject* myProgObj, const MetricFile* myMetric, const SurfaceFile* mySurf, const float& distance, MetricFile* myMetricOut, const MetricFile* badNodeRoi, const MetricFile* dataRoi, const int& columnNum, - const Method& myMethod, const float& exponent, const MetricFile* corrAreas) : AbstractAlgorithm(myProgObj) + const Method& myMethod, const float& exponent, const MetricFile* corrAreas, const bool legacyCutoff) : AbstractAlgorithm(myProgObj) { LevelProgress myProgress(myProgObj); int numNodes = mySurf->getNumberOfNodes(); @@ -196,7 +200,7 @@ AlgorithmMetricDilate::AlgorithmMetricDilate(ProgressObject* myProgObj, const Me { precomputeNearest(myNearest, mySurf, badNodeRoi, dataRoi, corrAreas, distance); } else { - precomputeStencils(myStencils, mySurf, myAreas, badNodeRoi, dataRoi, corrAreas, distance, exponent); + precomputeStencils(myStencils, mySurf, myAreas, badNodeRoi, dataRoi, corrAreas, distance, exponent, legacyCutoff); } } if (columnNum == -1) @@ -209,7 +213,7 @@ AlgorithmMetricDilate::AlgorithmMetricDilate(ProgressObject* myProgObj, const Me myMetricOut->setColumnName(thisCol, myMetric->getColumnName(thisCol)); if (badNodeRoi == NULL) { - processColumn(colScratch.data(), myInputData, mySurf, myAreas, badNodeRoi, dataRoi, corrAreas, distance, nearest, linear, exponent); + processColumn(colScratch.data(), myInputData, mySurf, myAreas, badNodeRoi, dataRoi, corrAreas, distance, nearest, linear, exponent, legacyCutoff); } else { switch (myMethod) { @@ -220,7 +224,7 @@ AlgorithmMetricDilate::AlgorithmMetricDilate(ProgressObject* myProgObj, const Me processColumn(colScratch.data(), numNodes, myInputData, myStencils); break; case LINEAR: - processColumn(colScratch.data(), myInputData, mySurf, myAreas, badNodeRoi, dataRoi, corrAreas, distance, nearest, linear, exponent); + processColumn(colScratch.data(), myInputData, mySurf, myAreas, badNodeRoi, dataRoi, corrAreas, distance, nearest, linear, exponent, legacyCutoff); break; } } @@ -233,7 +237,7 @@ AlgorithmMetricDilate::AlgorithmMetricDilate(ProgressObject* myProgObj, const Me myMetricOut->setColumnName(0, myMetric->getColumnName(columnNum)); if (badNodeRoi == NULL) { - processColumn(colScratch.data(), myInputData, mySurf, myAreas, badNodeRoi, dataRoi, corrAreas, distance, nearest, linear, exponent); + processColumn(colScratch.data(), myInputData, mySurf, myAreas, badNodeRoi, dataRoi, corrAreas, distance, nearest, linear, exponent, legacyCutoff); } else { switch (myMethod) { @@ -244,7 +248,7 @@ AlgorithmMetricDilate::AlgorithmMetricDilate(ProgressObject* myProgObj, const Me processColumn(colScratch.data(), numNodes, myInputData, myStencils); break; case LINEAR: - processColumn(colScratch.data(), myInputData, mySurf, myAreas, badNodeRoi, dataRoi, corrAreas, distance, nearest, linear, exponent); + processColumn(colScratch.data(), myInputData, mySurf, myAreas, badNodeRoi, dataRoi, corrAreas, distance, nearest, linear, exponent, legacyCutoff); break; } } @@ -302,16 +306,19 @@ void AlgorithmMetricDilate::processColumn(float* colScratch, const int& numNodes void AlgorithmMetricDilate::processColumn(float* colScratch, const float* myInputData, const SurfaceFile* mySurf, const float* myAreas, const MetricFile* badNodeRoi, const MetricFile* dataRoi, const MetricFile* corrAreas, - const float& distance, const bool& nearest, const bool& linear, const float& exponent) + const float& distance, const bool& nearest, const bool& linear, const float& exponent, const bool legacyCutoff) { - float cutoffRatio = 1.5f, test = pow(10.0f, 1.0f / exponent);//find what cutoff ratio corresponds to a tenth of weight, but don't use more than a 1.5 * nearest cutoff - if (test > 1.0f && test < cutoffRatio)//if it is less than 1, the exponent is weird, so simply ignore it and use default - { + FastStatistics spacingStats; + mySurf->getNodesSpacingStatistics(spacingStats);//use mean spacing to help set minimum stencil distance, since native surfaces might have a minimum of 0 + float cutoffBase = max(2.0f * distance, 2.0f * spacingStats.getMean()), cutoffRatio = max(1.1f, pow(49.0f, 1.0f / (exponent - 2.0f)));//find what ratio from closest vertex corresponds to having 98% of total weight accounted for on a plane, assuming non-adverse ROI + float legacyCutoffRatio = 1.5f, test = pow(10.0f, 1.0f / exponent);//old logic: find what cutoff ratio corresponds to a tenth of weight, but don't use more than a 1.5 * nearest cutoff + if (test > 1.0f && test < legacyCutoffRatio)//if it is less than 1, the exponent is weird, so simply ignore it and use default + {//this generally cut off early, causing the result to behave like a higher exponent was used if (test > 1.1f) { - cutoffRatio = test; + legacyCutoffRatio = test; } else { - cutoffRatio = 1.1f; + legacyCutoffRatio = 1.1f; } } int numNodes = mySurf->getNumberOfNodes(); @@ -497,7 +504,17 @@ void AlgorithmMetricDilate::processColumn(float* colScratch, const float* myInpu colScratch[i] = myInputData[node1] + (myInputData[node2] - myInputData[node1]) * usableDists[bestj] / (usableDists[bestj] + usableDists[bestk]); } } else { - myGeoHelp->getNodesToGeoDist(i, closestDist * cutoffRatio, nodeList, distList);//NOTE: guaranteed to find at least the closest node + float cutoffDist = cutoffBase; + if (legacyCutoff) + { + cutoffDist = closestDist * legacyCutoffRatio; + } else { + if (exponent > 2.0f && cutoffRatio < 100.0f && cutoffRatio > 1.0f)//if the ratio is sane, use it, but never exceed cutoffBase + { + cutoffDist = max(min(cutoffRatio * closestDist, cutoffDist), cutoffBase * 0.25f);//but small kernels are rather cheap anyway, so have a minimum size just in case + } + } + myGeoHelp->getNodesToGeoDist(i, cutoffDist, nodeList, distList); int numInRange = (int)nodeList.size(); float totalWeight = 0.0f, weightedSum = 0.0f; for (int j = 0; j < numInRange; ++j) @@ -535,18 +552,21 @@ void AlgorithmMetricDilate::processColumn(float* colScratch, const float* myInpu void AlgorithmMetricDilate::precomputeStencils(vector >& myStencils, const SurfaceFile* mySurf, const float* myAreas, const MetricFile* badNodeRoi, const MetricFile* dataRoi, const MetricFile* corrAreas, - const float& distance, const float& exponent) + const float& distance, const float& exponent, const bool legacyCutoff) { CaretAssert(badNodeRoi != NULL);//because it should never be called if we don't know exactly what nodes we are replacing const float* badNodeData = badNodeRoi->getValuePointerForColumn(0); - float cutoffRatio = 1.5f, test = pow(10.0f, 1.0f / exponent);//find what cutoff ratio corresponds to a tenth of weight, but don't use more than a 1.5 * nearest cutoff - if (test > 1.0f && test < cutoffRatio)//if it is less than 1, the exponent is weird, so simply ignore it and use default - { + FastStatistics spacingStats; + mySurf->getNodesSpacingStatistics(spacingStats);//use mean spacing to help set minimum stencil distance, since native surfaces might have a minimum of 0 + float cutoffBase = max(2.0f * distance, 2.0f * spacingStats.getMean()), cutoffRatio = max(1.1f, pow(49.0f, 1.0f / (exponent - 2.0f)));//find what ratio from closest vertex corresponds to having 98% of total weight accounted for on a plane, assuming non-adverse ROI + float legacyCutoffRatio = 1.5f, test = pow(10.0f, 1.0f / exponent);//old logic: find what cutoff ratio corresponds to a tenth of weight, but don't use more than a 1.5 * nearest cutoff + if (test > 1.0f && test < legacyCutoffRatio)//if it is less than 1, the exponent is weird, so simply ignore it and use default + {//this generally cut off early, causing the result to behave like a higher exponent was used if (test > 1.1f) { - cutoffRatio = test; + legacyCutoffRatio = test; } else { - cutoffRatio = 1.1f; + legacyCutoffRatio = 1.1f; } } int numNodes = mySurf->getNumberOfNodes(); @@ -637,7 +657,17 @@ void AlgorithmMetricDilate::precomputeStencils(vector >& { vector nodeList; vector distList; - myGeoHelp->getNodesToGeoDist(i, closestDist * cutoffRatio, nodeList, distList); + float cutoffDist = cutoffBase; + if (legacyCutoff) + { + cutoffDist = closestDist * legacyCutoffRatio; + } else { + if (exponent > 2.0f && cutoffRatio < 100.0f && cutoffRatio > 1.0f)//if the ratio is sane, use it, but never exceed cutoffBase + { + cutoffDist = max(min(cutoffRatio * closestDist, cutoffDist), cutoffBase * 0.25f);//but small kernels are rather cheap anyway, so have a minimum size just in case + } + } + myGeoHelp->getNodesToGeoDist(i, cutoffDist, nodeList, distList); int numInRange = (int)nodeList.size(); myElem.m_weightsum = 0.0f; for (int j = 0; j < numInRange; ++j) diff --git a/src/Algorithms/AlgorithmMetricDilate.h b/src/Algorithms/AlgorithmMetricDilate.h index 8130fed6f..153b1bc56 100644 --- a/src/Algorithms/AlgorithmMetricDilate.h +++ b/src/Algorithms/AlgorithmMetricDilate.h @@ -35,14 +35,14 @@ namespace caret { AlgorithmMetricDilate(); void precomputeStencils(std::vector >& myStencils, const SurfaceFile* mySurf, const float* myAreas, const MetricFile* badNodeRoi, const MetricFile* dataRoi, const MetricFile* corrAreas, - const float& distance, const float& exponent); + const float& distance, const float& exponent, const bool legacyCutoff); void precomputeNearest(std::vector >& myNearest, const SurfaceFile* mySurf, const MetricFile* badNodeRoi, const MetricFile* dataRoi, const MetricFile* corrAreas, const float& distance); void processColumn(float* colScratch, const int& numNodes, const float* myInputData, std::vector > myNearest); void processColumn(float* colScratch, const int& numNodes, const float* myInputData, std::vector > myStencils); void processColumn(float* colScratch, const float* myInputData, const SurfaceFile* mySurf, const float* myAreas, const MetricFile* badNodeRoi, const MetricFile* dataRoi, const MetricFile* corrAreas, - const float& distance, const bool& nearest, const bool& linear, const float& exponent); + const float& distance, const bool& nearest, const bool& linear, const float& exponent, const bool legacyCutoff); protected: static float getSubAlgorithmWeight(); static float getAlgorithmInternalWeight(); @@ -55,7 +55,7 @@ namespace caret { }; AlgorithmMetricDilate(ProgressObject* myProgObj, const MetricFile* myMetric, const SurfaceFile* mySurf, const float& distance, MetricFile* myMetricOut, const MetricFile* badNodeRoi = NULL, const MetricFile* dataRoi = NULL, const int& columnNum = -1, - const Method& myMethod = WEIGHTED, const float& exponent = 2.0f, const MetricFile* corrAreas = NULL); + const Method& myMethod = WEIGHTED, const float& exponent = 6.0f, const MetricFile* corrAreas = NULL, const bool legacyCutoff = false); static OperationParameters* getParameters(); static void useParameters(OperationParameters* myParams, ProgressObject* myProgObj); static AString getCommandSwitch(); diff --git a/src/Algorithms/AlgorithmMetricFalseCorrelation.cxx b/src/Algorithms/AlgorithmMetricFalseCorrelation.cxx index 3c793baed..4f44fdfa0 100644 --- a/src/Algorithms/AlgorithmMetricFalseCorrelation.cxx +++ b/src/Algorithms/AlgorithmMetricFalseCorrelation.cxx @@ -141,11 +141,11 @@ AlgorithmMetricFalseCorrelation::AlgorithmMetricFalseCorrelation(ProgressObject* { AString rawDumpString;//build the entire string for a single node, then write it in one call within #pragma omp critical Vector3D myCoord = mySurf->getCoordinate(n); - set inRange = myLocator->pointsInRange(myCoord, max3D); + vector inRange = myLocator->pointsInRange(myCoord, max3D); int numInterested = (int)inRange.size(); vector interested(numInterested); int counter = 0; - for (set::iterator iter = inRange.begin(); iter != inRange.end(); ++iter) + for (vector::iterator iter = inRange.begin(); iter != inRange.end(); ++iter) { interested[counter] = iter->index; ++counter; @@ -153,7 +153,7 @@ AlgorithmMetricFalseCorrelation::AlgorithmMetricFalseCorrelation(ProgressObject* vector geoDists; myGeo->getGeoToTheseNodes(n, interested, geoDists); counter = 0; - for (set::iterator iter = inRange.begin(); iter != inRange.end(); ++iter) + for (vector::iterator iter = inRange.begin(); iter != inRange.end(); ++iter) { if (roiCol == NULL || (roiCol[iter->index] > 0.0f)) { diff --git a/src/Algorithms/AlgorithmVolumeDilate.cxx b/src/Algorithms/AlgorithmVolumeDilate.cxx index 55fc9e4e1..cfc05ad70 100644 --- a/src/Algorithms/AlgorithmVolumeDilate.cxx +++ b/src/Algorithms/AlgorithmVolumeDilate.cxx @@ -24,11 +24,13 @@ #include "CaretHeap.h" #include "CaretLogger.h" #include "CaretOMP.h" +#include "CaretPointLocator.h" #include "FloatMatrix.h" #include "Vector3D.h" #include "VolumeFile.h" #include "VoxelIJK.h" +#include #include #include @@ -57,7 +59,7 @@ OperationParameters* AlgorithmVolumeDilate::getParameters() ret->addVolumeOutputParameter(4, "volume-out", "the output volume"); OptionalParameter* exponentOpt = ret->createOptionalParameter(8, "-exponent", "use a different exponent in the weighting function"); - exponentOpt->addDoubleParameter(1, "exponent", "exponent 'n' to use in (1 / (distance ^ n)) as the weighting function (default 2)"); + exponentOpt->addDoubleParameter(1, "exponent", "exponent 'n' to use in (1 / (distance ^ n)) as the weighting function (default 7)"); OptionalParameter* badRoiOpt = ret->createOptionalParameter(5, "-bad-voxel-roi", "specify an roi of voxels to overwrite, rather than voxels with value zero"); badRoiOpt->addVolumeParameter(1, "roi-volume", "volume file, positive values denote voxels to have their values replaced"); @@ -68,12 +70,15 @@ OperationParameters* AlgorithmVolumeDilate::getParameters() OptionalParameter* subvolSelect = ret->createOptionalParameter(6, "-subvolume", "select a single subvolume to dilate"); subvolSelect->addStringParameter(1, "subvol", "the subvolume number or name"); + ret->createOptionalParameter(9, "-legacy-cutoff", "use the old method of excluding voxels further than the dilation distance when calculating the dilated value"); + ret->setHelpText( AString("For all voxels that are designated as bad, if they neighbor a non-bad voxel with data or are within the specified distance of such a voxel, ") + "replace the value in the bad voxel with a value calculated from nearby non-bad voxels that have data, otherwise set the value to zero. " + "No matter how small is, dilation will always use at least the face neighbor voxels.\n\n" + "By default, voxels that have data with the value 0 are bad, specify -bad-voxel-roi to only count voxels as bad if they are selected by the roi. " + "If -data-roi is not specified, all voxels are assumed to have data.\n\n" + + "To get the behavior of version 1.3.2 or earlier, use '-legacy-cutoff -exponent 2'.\n\n" + "Valid values for are:\n\n" + "NEAREST - use the value from the nearest good voxel\n" + "WEIGHTED - use a weighted average based on distance" @@ -97,7 +102,7 @@ void AlgorithmVolumeDilate::useParameters(OperationParameters* myParams, Progres } VolumeFile* volOut = myParams->getOutputVolume(4); OptionalParameter* exponentOpt = myParams->getOptionalParameter(8); - float exponent = 2.0f; + float exponent = 7.0f; if (exponentOpt->m_present) { exponent = (float)exponentOpt->getDouble(1); @@ -121,445 +126,350 @@ void AlgorithmVolumeDilate::useParameters(OperationParameters* myParams, Progres subvol = volIn->getMapIndexFromNameOrNumber(subvolSelect->getString(1)); if (subvol < 0) throw AlgorithmException("invalid subvolume specified"); } - AlgorithmVolumeDilate(myProgObj, volIn, distance, myMethod, volOut, badRoi, dataRoi, subvol, exponent); + bool legacyCutoff = myParams->getOptionalParameter(9)->m_present; + AlgorithmVolumeDilate(myProgObj, volIn, distance, myMethod, volOut, badRoi, dataRoi, subvol, exponent, legacyCutoff); } -AlgorithmVolumeDilate::AlgorithmVolumeDilate(ProgressObject* myProgObj, const VolumeFile* volIn, const float& distance, const Method& myMethod, - VolumeFile* volOut, const VolumeFile* badRoi, const VolumeFile* dataRoi, const int& subvol, const float& exponent) : AbstractAlgorithm(myProgObj) +namespace { - LevelProgress myProgress(myProgObj); - vector myDims; - volIn->getDimensions(myDims); - if (subvol < -1 || subvol >= myDims[3]) - { - throw AlgorithmException("invalid subvolume specified"); - } - if (distance < 0.0f) - { - throw AlgorithmException("distance cannot be negative"); - } - if (badRoi != NULL && !volIn->matchesVolumeSpace(badRoi)) - { - throw AlgorithmException("bad voxel roi volume space does not match input volume"); - } - if (dataRoi != NULL && !volIn->matchesVolumeSpace(dataRoi)) - { - throw AlgorithmException("data roi volume space does not match input volume"); - } - bool isLabelData = false; - if (volIn->getType() == SubvolumeAttributes::LABEL) - { - isLabelData = true; - } - vector > volSpace = volIn->getSform(); - Vector3D ivec, jvec, kvec, origin, ijorth, jkorth, kiorth; - FloatMatrix(volSpace).getAffineVectors(ivec, jvec, kvec, origin); - ijorth = ivec.cross(jvec).normal();//find the bounding box that encloses a sphere of radius kernBox - jkorth = jvec.cross(kvec).normal(); - kiorth = kvec.cross(ivec).normal(); - int irange = (int)floor(abs(distance / ivec.dot(jkorth))); - int jrange = (int)floor(abs(distance / jvec.dot(kiorth))); - int krange = (int)floor(abs(distance / kvec.dot(ijorth))); - if (irange < 1) irange = 1;//don't underflow - if (jrange < 1) jrange = 1; - if (krange < 1) krange = 1; - Vector3D kscratch, jscratch, iscratch; - vector stencil; - vector stenWeights; - for (int k = -krange; k <= krange; ++k) - { - kscratch = kvec * k; - for (int j = -jrange; j <= jrange; ++j) + + inline bool copyVoxel(const bool labelMode, const int32_t unlabeledKey, const int64_t i, const int64_t j, const int64_t k, const VolumeFile* volIn, const int& insubvol, const int& component, + const VolumeFile* badRoi, const VolumeFile* dataRoi) + {//copy all voxels that are not to be replaced + if (badRoi == NULL) { - jscratch = kscratch + jvec * j; - for (int i = -irange; i <= irange; ++i) + if (labelMode) { - if (k == 0 && j == 0 && i == 0) continue; - iscratch = jscratch + ivec * i; - float tempf = iscratch.length(); - if (tempf <= distance || abs(i) + abs(j) + abs(k) == 1) - { - stencil.push_back(i); - stencil.push_back(j); - stencil.push_back(k); - switch (myMethod) - { - case NEAREST: - stenWeights.push_back(tempf); - break; - case WEIGHTED: - if (tempf == 0.0f) throw AlgorithmException("volume space is degenerate, aborting"); - stenWeights.push_back(1.0f / pow(tempf, exponent)); - break; - } - } + return (dataRoi != NULL && !(dataRoi->getValue(i, j, k) > 0.0f)) || floor(0.5f + volIn->getValue(i, j, k, insubvol, component)) != unlabeledKey; + } else { + return (dataRoi != NULL && !(dataRoi->getValue(i, j, k) > 0.0f)) || volIn->getValue(i, j, k, insubvol, component) != 0.0f; } + } else { + return !(badRoi->getValue(i, j, k) > 0.0f);//in case some clown uses NaNs instead of 0s in an roi } } - if (myMethod == NEAREST) - {//sort the stencil by distance, so we can stop early - CaretSimpleMinHeap myHeap; - int stencilSize = (int)stenWeights.size(); - myHeap.reserve(stencilSize); - for (int i = 0; i < stencilSize; ++i) - { - myHeap.push(VoxelIJK(stencil.data() + i * 3), stenWeights[i]); - } - stencil.clear(); - stenWeights.clear(); - while (!myHeap.isEmpty()) - { - float tempf; - VoxelIJK myTriple = myHeap.pop(&tempf); - stenWeights.push_back(tempf); - stencil.push_back(myTriple.m_ijk[0]); - stencil.push_back(myTriple.m_ijk[1]); - stencil.push_back(myTriple.m_ijk[2]); - } - } - if (subvol == -1) - { - volOut->reinitialize(volIn->getOriginalDimensions(), volIn->getSform(), volIn->getNumberOfComponents(), volIn->getType()); - for (int i = 0; i < myDims[3]; ++i) + + inline bool voxelUsable(const bool labelMode, const int32_t unlabeledKey, const int64_t i, const int64_t j, const int64_t k, const VolumeFile* volIn, const int& insubvol, const int& component, + const VolumeFile* badRoi, const VolumeFile* dataRoi) + {//only voxels that are inside the data ROI and not to be replaced + if (badRoi == NULL) { - if (volIn->getType() == SubvolumeAttributes::LABEL) + if (labelMode) { - *(volOut->getMapLabelTable(i)) = *(volIn->getMapLabelTable(i)); + return (dataRoi == NULL || dataRoi->getValue(i, j, k) > 0.0f) && floor(0.5f + volIn->getValue(i, j, k, insubvol, component)) != unlabeledKey; } else { - *(volOut->getMapPaletteColorMapping(i)) = *(volIn->getMapPaletteColorMapping(i)); + return (dataRoi == NULL || dataRoi->getValue(i, j, k) > 0.0f) && volIn->getValue(i, j, k, insubvol, component) != 0.0f; } - volOut->setMapName(i, volIn->getMapName(i) + " dilate " + AString::number(distance)); - } - } else { - vector outDims = myDims; - outDims.resize(3); - volOut->reinitialize(outDims, volIn->getSform(), volIn->getNumberOfComponents(), volIn->getType()); - if (volIn->getType() == SubvolumeAttributes::LABEL) - { - *(volOut->getMapLabelTable(0)) = *(volIn->getMapLabelTable(subvol)); } else { - *(volOut->getMapPaletteColorMapping(0)) = *(volIn->getMapPaletteColorMapping(subvol)); + return (dataRoi == NULL || dataRoi->getValue(i, j, k) > 0.0f) && !(badRoi->getValue(i, j, k) > 0.0f); } - volOut->setMapName(0, volIn->getMapName(subvol) + " dilate " + AString::number(distance)); } - if (subvol == -1) - { - for (int s = 0; s < myDims[3]; ++s) + + void dilateFrame(const bool labelMode, const VolumeFile* volIn, const int& insubvol, const int& component, VolumeFile* volOut, const int& outsubvol, const VolumeFile* badRoi, + const VolumeFile* dataRoi, const float& distance, const AlgorithmVolumeDilate::Method& myMethod, const float& exponent, const bool& legacyCutoff) + {//copy data that is outside the dataROI, replace values where badROI is > 0 (with zero if nothing else), if no badROI, pretend badROI is (data == 0 && dataROI > 0) + int neighbors[18] = {0, 0, -1, + 0, -1, 0, + -1, 0, 0, + 1, 0, 0, + 0, 1, 0, + 0, 0, 1};//special behavior: when distance is 0, it still dilates by 1 voxel + Vector3D voxStep[3], origin; + const VolumeSpace& myVolSpace = volIn->getVolumeSpace(); + myVolSpace.getSpacingVectors(voxStep[0], voxStep[1], voxStep[2], origin); + //if the distance is within 1% of excluding a neighbor, we need to additionally check the neighbors + bool checkNeighbors = distance <= voxStep[0].length() * 1.01f || distance <= voxStep[1].length() * 1.01f || distance <= voxStep[2].length() * 1.01f; + //the single-voxel rule means we can't just base the maximum search distance on the dilation distance + float cutoffBase = max(2.0f * distance, 2.0f * min(min(voxStep[0].length(), voxStep[1].length()), voxStep[2].length())); + vector myDims = volIn->getDimensions(); + int32_t unlabeledKey = 0; + if (labelMode) unlabeledKey = volIn->getMapLabelTable(insubvol)->getUnassignedLabelKey(); + vector validPoints; + vector validIndices; + for (int64_t k = 0; k < myDims[2]; ++k) { - for (int c = 0; c < myDims[4]; ++c) + for (int64_t j = 0; j < myDims[1]; ++j) { - if (isLabelData) + for (int64_t i = 0; i < myDims[0]; ++i) { - dilateFrameLabel(volIn, s, c, volOut, s, badRoi, dataRoi, myMethod, stencil, stenWeights); - } else { - dilateFrame(volIn, s, c, volOut, s, badRoi, dataRoi, myMethod, stencil, stenWeights); + if (voxelUsable(labelMode, unlabeledKey, i, j, k, volIn, insubvol, component, badRoi, dataRoi)) + { + VoxelIJK tempVoxel(i, j, k); + Vector3D tempCoord = myVolSpace.indexToSpace(tempVoxel); + validPoints.push_back(tempCoord[0]); + validPoints.push_back(tempCoord[1]); + validPoints.push_back(tempCoord[2]); + validIndices.push_back(tempVoxel); + } } } } - } else { - for (int c = 0; c < myDims[4]; ++c) - { - if (isLabelData) - { - dilateFrameLabel(volIn, subvol, c, volOut, 0, badRoi, dataRoi, myMethod, stencil, stenWeights); - } else { - dilateFrame(volIn, subvol, c, volOut, 0, badRoi, dataRoi, myMethod, stencil, stenWeights); - } - } - } -} - -void AlgorithmVolumeDilate::dilateFrame(const VolumeFile* volIn, const int& insubvol, const int& component, VolumeFile* volOut, const int& outsubvol, - const VolumeFile* badRoi, const VolumeFile* dataRoi, const Method& myMethod, const vector& stencil, const vector& stenWeights) -{ - vector myDims; - volIn->getDimensions(myDims); - int stensize = (int)stenWeights.size(); + CaretPointLocator locator(validPoints); #pragma omp CARET_PARFOR schedule(dynamic) - for (int k = 0; k < myDims[2]; ++k) - { - for (int j = 0; j < myDims[1]; ++j) + for (int64_t k = 0; k < myDims[2]; ++k) { - for (int i = 0; i < myDims[0]; ++i) + for (int64_t j = 0; j < myDims[1]; ++j) { - bool copy = true; - if (badRoi == NULL) + for (int64_t i = 0; i < myDims[0]; ++i) { - copy = volIn->getValue(i, j, k, insubvol, component) != 0.0f || (dataRoi != NULL && !(dataRoi->getValue(i, j, k) > 0.0f)); - } else { - copy = !(badRoi->getValue(i, j, k) > 0.0f);//in case some clown uses NaNs as bad in an roi - } - if (copy) - { - volOut->setValue(volIn->getValue(i, j, k, insubvol, component), i, j, k, outsubvol, component); - } else { - Vector3D voxcoord; - volIn->indexToSpace(i, j, k, voxcoord); - switch (myMethod) + if (copyVoxel(labelMode, unlabeledKey, i, j, k, volIn, insubvol, component, badRoi, dataRoi)) { - case NEAREST: + if (labelMode) + { + volOut->setValue(floor(0.5f + volIn->getValue(i, j, k, insubvol, component)), i, j, k, outsubvol, component); + } else { + volOut->setValue(volIn->getValue(i, j, k, insubvol, component), i, j, k, outsubvol, component); + } + } else { + Vector3D voxcoord = myVolSpace.indexToSpace(i, j, k); + switch (myMethod) { - int best = -1; - if (badRoi == NULL) + case AlgorithmVolumeDilate::NEAREST: { - for (int stenind = 0; stenind < stensize; ++stenind) + int64_t index = locator.closestPointLimited(voxcoord, distance); + if (index < 0) { - int base = stenind * 3; - int64_t tempindex[3]; - tempindex[0] = stencil[base] + i; - tempindex[1] = stencil[base + 1] + j; - tempindex[2] = stencil[base + 2] + k; - if (volIn->indexValid(tempindex)) + if (checkNeighbors) { - if ((dataRoi == NULL || dataRoi->getValue(tempindex) > 0.0f) && volIn->getValue(tempindex, insubvol, component) != 0.0f) + float bestDist = -1.0f; + float bestVal = 0.0f; + for (int n = 0; n < 6; ++n) { - best = stenind; - break; + int neighbase = n * 3; + int64_t neighVox[3] = {i + neighbors[neighbase], j + neighbors[neighbase + 1], k + neighbors[neighbase + 2]}; + if (myVolSpace.indexValid(neighVox) && voxelUsable(labelMode, unlabeledKey, neighVox[0], neighVox[1], neighVox[2], volIn, insubvol, component, badRoi, dataRoi)) + { + float tempdist = (myVolSpace.indexToSpace(neighbors + neighbase) - myVolSpace.indexToSpace(0, 0, 0)).length();//slightly hacky, but won't have inconsistencies from different rounding per voxel + if (tempdist < bestDist || bestDist == -1.0f) + { + bestDist = tempdist; + bestVal = volIn->getValue(neighVox, insubvol, component); + } + } } - } - } - } else { - for (int stenind = 0; stenind < stensize; ++stenind) - { - int base = stenind * 3; - int64_t tempindex[3]; - tempindex[0] = stencil[base] + i; - tempindex[1] = stencil[base + 1] + j; - tempindex[2] = stencil[base + 2] + k; - if (volIn->indexValid(tempindex)) - { - if ((dataRoi == NULL || dataRoi->getValue(tempindex) > 0.0f) && !(badRoi->getValue(tempindex) > 0.0f)) + if (labelMode) { - best = stenind; - break; + volOut->setValue(floor(0.5f + bestVal), i, j, k, outsubvol, component); + } else { + volOut->setValue(bestVal, i, j, k, outsubvol, component); } + } else { + volOut->setValue(0.0f, i, j, k, outsubvol, component); + } + } else { + if (labelMode) + { + volOut->setValue(floor(0.5f + volIn->getValue(validIndices[index], insubvol, component)), i, j, k, outsubvol, component); + } else { + volOut->setValue(volIn->getValue(validIndices[index], insubvol, component), i, j, k, outsubvol, component); } } + break; } - if (best == -1) + case AlgorithmVolumeDilate::WEIGHTED: { - volOut->setValue(0.0f, i, j, k, outsubvol, component); - } else { - int base = best * 3; - int64_t tempindex[3]; - tempindex[0] = stencil[base] + i; - tempindex[1] = stencil[base + 1] + j; - tempindex[2] = stencil[base + 2] + k; - volOut->setValue(volIn->getValue(tempindex, insubvol, component), i, j, k, outsubvol, component); - } - break; - } - case WEIGHTED: - { - double sum = 0.0, weightsum = 0.0; - if (badRoi == NULL) - { - for (int stenind = 0; stenind < stensize; ++stenind) + vector inRange; + if (legacyCutoff) { - int base = stenind * 3; - int64_t tempindex[3]; - tempindex[0] = stencil[base] + i; - tempindex[1] = stencil[base + 1] + j; - tempindex[2] = stencil[base + 2] + k; - if (volIn->indexValid(tempindex)) + inRange = locator.pointsInRange(voxcoord, distance);//immediate neighbor special case is handled below + } else { + float closeDist = -1.0f; + LocatorInfo myInfo; + int64_t index = locator.closestPointLimited(voxcoord, distance, &myInfo);//only need the distance + bool found = false; + if (index >= 0) { - float tempf = volIn->getValue(tempindex, insubvol, component); - if ((dataRoi == NULL || dataRoi->getValue(tempindex) > 0.0f) && tempf != 0.0f) - { - float weight = stenWeights[stenind]; - sum += weight * tempf; - weightsum += weight; + found = true; + closeDist = (myInfo.coords - voxcoord).length(); + } else { + if (checkNeighbors) + {//always dilate to neighbor voxels, regardless + for (int n = 0; n < 6; ++n) + { + int neighbase = n * 3; + int64_t neighVox[3] = {i + neighbors[neighbase], j + neighbors[neighbase + 1], k + neighbors[neighbase + 2]}; + if (myVolSpace.indexValid(neighVox) && voxelUsable(labelMode, unlabeledKey, neighVox[0], neighVox[1], neighVox[2], volIn, insubvol, component, badRoi, dataRoi)) + { + float tempdist = (myVolSpace.indexToSpace(neighbors + neighbase) - myVolSpace.indexToSpace(0, 0, 0)).length();//slightly hacky, but won't have inconsistencies from different rounding per voxel + if (tempdist < closeDist || !found) + { + found = true; + closeDist = tempdist; + } + } + } } } - } - } else { - for (int stenind = 0; stenind < stensize; ++stenind) - { - int base = stenind * 3; - int64_t tempindex[3]; - tempindex[0] = stencil[base] + i; - tempindex[1] = stencil[base + 1] + j; - tempindex[2] = stencil[base + 2] + k; - if (volIn->indexValid(tempindex)) + if (found) { - if ((dataRoi == NULL || dataRoi->getValue(tempindex) > 0.0f) && !(badRoi->getValue(tempindex) > 0.0f)) + //find what cutoff corresponds to 98% of the total weight being found compared to an infinite kernel + //to do this, assume a non-adversarial situation, where farther parts have at most equal angular area to closer ones + //49 = 98/(100-98) + float cutoffRatio = max(1.1f, pow(49.0f, 1.0f / (exponent - 3.0f))), cutoffDist = cutoffBase;//find what cutoff ratio corresponds to a hudredth of weight + if (exponent > 3.0f && cutoffRatio < 100.0f && cutoffRatio > 1.0f)//if the ratio is sane, use it, but never exceed cutoffBase { - float tempf = volIn->getValue(tempindex, insubvol, component); - float weight = stenWeights[stenind]; - sum += weight * tempf; - weightsum += weight; + cutoffDist = max(min(cutoffRatio * closeDist, cutoffDist), cutoffBase * 0.25f);//but small kernels are rather cheap anyway, so have a minimum size just in case } + inRange = locator.pointsInRange(voxcoord, cutoffDist); } } - } - if (weightsum != 0.0) - { - volOut->setValue(sum / weightsum, i, j, k, outsubvol, component); - } else { - volOut->setValue(0.0f, i, j, k, outsubvol, component); - } - break; - } - } - } - } - } - } -} - -void AlgorithmVolumeDilate::dilateFrameLabel(const VolumeFile* volIn, const int& insubvol, const int& component, VolumeFile* volOut, const int& outsubvol, - const VolumeFile* badRoi, const VolumeFile* dataRoi, const Method& myMethod, const vector& stencil, const vector& stenWeights) -{ - vector myDims; - volIn->getDimensions(myDims); - int stensize = (int)stenWeights.size(); - int32_t unlabeledKey = volIn->getMapLabelTable(insubvol)->getUnassignedLabelKey(); -#pragma omp CARET_PARFOR schedule(dynamic) - for (int k = 0; k < myDims[2]; ++k) - { - for (int j = 0; j < myDims[1]; ++j) - { - for (int i = 0; i < myDims[0]; ++i) - { - bool copy = true; - int32_t keyIn = floor(volIn->getValue(i, j, k, insubvol, component) + 0.5f);//fix non-integers - if (badRoi == NULL) - { - copy = keyIn != unlabeledKey || (dataRoi != NULL && !(dataRoi->getValue(i, j, k) > 0.0f)); - } else { - copy = !(badRoi->getValue(i, j, k) > 0.0f);//in case some clown uses NaNs as bad in an roi - } - if (copy) - { - volOut->setValue(keyIn, i, j, k, outsubvol, component); - } else { - Vector3D voxcoord; - volIn->indexToSpace(i, j, k, voxcoord); - switch (myMethod) - { - case NEAREST: - { - int best = -1; - if (badRoi == NULL) - { - for (int stenind = 0; stenind < stensize; ++stenind) - { - int base = stenind * 3; - int64_t tempindex[3]; - tempindex[0] = stencil[base] + i; - tempindex[1] = stencil[base + 1] + j; - tempindex[2] = stencil[base + 2] + k; - if (volIn->indexValid(tempindex)) + map labelSums; + double sum = 0.0, weightsum = 0.0; + if (legacyCutoff && checkNeighbors) + {//add valid neighbors only if they aren't already in the list, and the non-legacy mode is already handled above... + set voxelsToUse;//but looking up the neighbors' indices in validIndices is work we don't need to do, so copy the list and add to it + for (auto thisInfo : inRange) { - if ((dataRoi == NULL || dataRoi->getValue(tempindex) > 0.0f) && volIn->getValue(tempindex, insubvol, component) != 0.0f) - { - best = stenind; - break; - } + voxelsToUse.insert(validIndices[thisInfo.index]); } - } - } else { - for (int stenind = 0; stenind < stensize; ++stenind) - { - int base = stenind * 3; - int64_t tempindex[3]; - tempindex[0] = stencil[base] + i; - tempindex[1] = stencil[base + 1] + j; - tempindex[2] = stencil[base + 2] + k; - if (volIn->indexValid(tempindex)) + for (int n = 0; n < 6; ++n) { - if ((dataRoi == NULL || dataRoi->getValue(tempindex) > 0.0f) && !(badRoi->getValue(tempindex) > 0.0f)) + int neighbase = n * 3; + int64_t neighVox[3] = {i + neighbors[neighbase], j + neighbors[neighbase + 1], k + neighbors[neighbase + 2]}; + if (myVolSpace.indexValid(neighVox) && voxelUsable(labelMode, unlabeledKey, neighVox[0], neighVox[1], neighVox[2], volIn, insubvol, component, badRoi, dataRoi)) { - best = stenind; - break; + voxelsToUse.insert(neighVox);//set eliminates duplicates } } - } - } - if (best == -1) - { - volOut->setValue(unlabeledKey, i, j, k, outsubvol, component); - } else { - int base = best * 3; - int64_t tempindex[3]; - tempindex[0] = stencil[base] + i; - tempindex[1] = stencil[base + 1] + j; - tempindex[2] = stencil[base + 2] + k; - volOut->setValue((int32_t)floor(volIn->getValue(tempindex, insubvol, component) + 0.5f), i, j, k, outsubvol, component);//fix non-integers - }//yes, the cast is undefined for silly things like NaN that shouldn't be in a label file, but so are the other paths - we just want consistency between behavior of old and new values - break; - } - case WEIGHTED: - { - map labelSums; - if (badRoi == NULL) - { - for (int stenind = 0; stenind < stensize; ++stenind) - { - int base = stenind * 3; - int64_t tempindex[3]; - tempindex[0] = stencil[base] + i; - tempindex[1] = stencil[base + 1] + j; - tempindex[2] = stencil[base + 2] + k; - if (volIn->indexValid(tempindex)) + for (auto thisVoxel : voxelsToUse)//unfortunately, this means we need to write a copy of the loop for the common case not to do unneeded work { - int32_t tempKey = floor(volIn->getValue(tempindex, insubvol, component) + 0.5f);//fix non-integers - if ((dataRoi == NULL || dataRoi->getValue(tempindex) > 0.0f) && tempKey != unlabeledKey) + float thisdist = (myVolSpace.indexToSpace(thisVoxel) - voxcoord).length(); + float weight = 1.0f / pow(thisdist, exponent); + if (labelMode) { - float weight = stenWeights[stenind]; - map::iterator iter = labelSums.find(tempKey); + int32_t thisKey = int32_t(floor(0.5f + volIn->getValue(thisVoxel, insubvol, component))); + map::iterator iter = labelSums.find(thisKey); if (iter == labelSums.end()) { - labelSums[tempKey] = weight; + labelSums[thisKey] = weight; } else { iter->second += weight; } + } else { + sum += weight * volIn->getValue(thisVoxel, insubvol, component); + weightsum += weight; } } - } - } else { - for (int stenind = 0; stenind < stensize; ++stenind) - { - int base = stenind * 3; - int64_t tempindex[3]; - tempindex[0] = stencil[base] + i; - tempindex[1] = stencil[base + 1] + j; - tempindex[2] = stencil[base + 2] + k; - if (volIn->indexValid(tempindex)) + } else { + for (auto thisInfo : inRange) { - if ((dataRoi == NULL || dataRoi->getValue(tempindex) > 0.0f) && !(badRoi->getValue(tempindex) > 0.0f)) + float thisdist = (thisInfo.coords - voxcoord).length(); + float weight = 1.0f / pow(thisdist, exponent); + if (labelMode) { - int32_t tempKey = floor(volIn->getValue(tempindex, insubvol, component) + 0.5f);//fix non-integers - float weight = stenWeights[stenind]; - map::iterator iter = labelSums.find(tempKey); + int32_t thisKey = int32_t(floor(0.5f + volIn->getValue(validIndices[thisInfo.index], insubvol, component))); + map::iterator iter = labelSums.find(thisKey); if (iter == labelSums.end()) { - labelSums[tempKey] = weight; + labelSums[thisKey] = weight; } else { iter->second += weight; } + } else { + sum += weight * volIn->getValue(validIndices[thisInfo.index], insubvol, component); + weightsum += weight; } } } - } - int32_t outVal = unlabeledKey; - float bestSum = -1.0f;//weights should all be positive, so should the sums - for (map::iterator iter = labelSums.begin(); iter != labelSums.end(); ++iter) - { - if (iter->second > bestSum) + if (labelMode) { - outVal = iter->first; - bestSum = iter->second; + float bestWeight = -1.0f;//all weights should be positive, so their sums should too + int32_t bestKey = unlabeledKey; + for (auto iter : labelSums) + { + if (iter.second > bestWeight) + { + bestWeight = iter.second; + bestKey = iter.first; + } + } + volOut->setValue(bestKey, i, j, k, outsubvol, component); + } else { + if (weightsum > 0.0) + { + volOut->setValue(sum / weightsum, i, j, k, outsubvol, component); + } else { + volOut->setValue(0.0f, i, j, k, outsubvol, component); + } } + break; } - volOut->setValue(outVal, i, j, k, outsubvol, component); - break; } } } } } } + +} + +AlgorithmVolumeDilate::AlgorithmVolumeDilate(ProgressObject* myProgObj, const VolumeFile* volIn, const float& distance, const Method& myMethod, VolumeFile* volOut, + const VolumeFile* badRoi, const VolumeFile* dataRoi, const int& subvol, const float& exponent, const bool legacyCutoff) : AbstractAlgorithm(myProgObj) +{ + LevelProgress myProgress(myProgObj); + vector myDims; + volIn->getDimensions(myDims); + if (subvol < -1 || subvol >= myDims[3]) + { + throw AlgorithmException("invalid subvolume specified"); + } + if (distance < 0.0f) + { + throw AlgorithmException("distance cannot be negative"); + } + if (badRoi != NULL && !volIn->matchesVolumeSpace(badRoi)) + { + throw AlgorithmException("bad voxel roi volume space does not match input volume"); + } + if (dataRoi != NULL && !volIn->matchesVolumeSpace(dataRoi)) + { + throw AlgorithmException("data roi volume space does not match input volume"); + } + bool isLabelData = false; + if (volIn->getType() == SubvolumeAttributes::LABEL) + { + isLabelData = true; + } + if (subvol == -1) + { + volOut->reinitialize(volIn->getOriginalDimensions(), volIn->getSform(), volIn->getNumberOfComponents(), volIn->getType()); + for (int i = 0; i < myDims[3]; ++i) + { + if (volIn->getType() == SubvolumeAttributes::LABEL) + { + *(volOut->getMapLabelTable(i)) = *(volIn->getMapLabelTable(i)); + } else { + *(volOut->getMapPaletteColorMapping(i)) = *(volIn->getMapPaletteColorMapping(i)); + } + volOut->setMapName(i, volIn->getMapName(i) + " dilate " + AString::number(distance)); + } + } else { + vector outDims = myDims; + outDims.resize(3); + volOut->reinitialize(outDims, volIn->getSform(), volIn->getNumberOfComponents(), volIn->getType()); + if (volIn->getType() == SubvolumeAttributes::LABEL) + { + *(volOut->getMapLabelTable(0)) = *(volIn->getMapLabelTable(subvol)); + } else { + *(volOut->getMapPaletteColorMapping(0)) = *(volIn->getMapPaletteColorMapping(subvol)); + } + volOut->setMapName(0, volIn->getMapName(subvol) + " dilate " + AString::number(distance)); + } + if (subvol == -1) + { + for (int s = 0; s < myDims[3]; ++s) + { + for (int c = 0; c < myDims[4]; ++c) + { + dilateFrame(isLabelData, volIn, s, c, volOut, s, badRoi, dataRoi, distance, myMethod, exponent, legacyCutoff); + } + } + } else { + for (int c = 0; c < myDims[4]; ++c) + { + dilateFrame(isLabelData, volIn, subvol, c, volOut, 0, badRoi, dataRoi, distance, myMethod, exponent, legacyCutoff); + } + } } float AlgorithmVolumeDilate::getAlgorithmInternalWeight() diff --git a/src/Algorithms/AlgorithmVolumeDilate.h b/src/Algorithms/AlgorithmVolumeDilate.h index ad3452d97..7a9b7231f 100644 --- a/src/Algorithms/AlgorithmVolumeDilate.h +++ b/src/Algorithms/AlgorithmVolumeDilate.h @@ -37,17 +37,12 @@ namespace caret { NEAREST, WEIGHTED }; - AlgorithmVolumeDilate(ProgressObject* myProgObj, const VolumeFile* volIn, const float& distance, const Method& myMethod, - VolumeFile* volOut, const VolumeFile* badRoi = NULL, const VolumeFile* dataRoi = NULL, const int& subvol = -1, const float& exponent = 2.0f); + AlgorithmVolumeDilate(ProgressObject* myProgObj, const VolumeFile* volIn, const float& distance, const Method& myMethod, VolumeFile* volOut, + const VolumeFile* badRoi = NULL, const VolumeFile* dataRoi = NULL, const int& subvol = -1, const float& exponent = 7.0f, const bool legacyCutoff = false); static OperationParameters* getParameters(); static void useParameters(OperationParameters* myParams, ProgressObject* myProgObj); static AString getCommandSwitch(); static AString getShortDescription(); - private: - void dilateFrame(const VolumeFile* volIn, const int& insubvol, const int& component, VolumeFile* volOut, const int& outsubvol, const VolumeFile* badRoi, - const VolumeFile* dataRoi, const Method& myMethod, const std::vector& stencil, const std::vector& stenWeights); - void dilateFrameLabel(const VolumeFile* volIn, const int& insubvol, const int& component, VolumeFile* volOut, const int& outsubvol, const VolumeFile* badRoi, - const VolumeFile* dataRoi, const Method& myMethod, const std::vector& stencil, const std::vector& stenWeights); }; typedef TemplateAutoOperation AutoAlgorithmVolumeDilate; diff --git a/src/Common/CaretPointLocator.cxx b/src/Common/CaretPointLocator.cxx index c34022c0f..2d0074d53 100644 --- a/src/Common/CaretPointLocator.cxx +++ b/src/Common/CaretPointLocator.cxx @@ -196,15 +196,15 @@ int64_t CaretPointLocator::closestPoint(const float target[3], LocatorInfo* info int64_t CaretPointLocator::closestPointLimited(const float target[3], const float& maxDist, LocatorInfo* infoOut) const { + if (infoOut != NULL) + { + infoOut->whichSet = -1; + infoOut->index = -1; + } if (m_tree == NULL) return -1; float curDist2 = m_tree->distSquaredToPoint(target), maxDist2 = maxDist * maxDist; if (curDist2 > maxDist2) { - if (infoOut != NULL) - { - infoOut->whichSet = -1; - infoOut->index = -1; - } return -1; } CaretSimpleMinHeap >*, float> myHeap; @@ -264,9 +264,9 @@ int64_t CaretPointLocator::closestPointLimited(const float target[3], const floa return bestIndex; } -set CaretPointLocator::pointsInRange(const float target[3], const float& maxDist) const -{ - set ret; +vector CaretPointLocator::pointsInRange(const float target[3], const float& maxDist) const +{//each point occurs in only once in the tree, so we can use a vector + vector ret; if (m_tree == NULL) return ret; float curDist2 = m_tree->distSquaredToPoint(target), maxDist2 = maxDist * maxDist; if (curDist2 > maxDist2) return ret; @@ -285,7 +285,7 @@ set CaretPointLocator::pointsInRange(const float target[3], const f float tempf = MathFunctions::distanceSquared3D(myVecRef[i].m_point, target); if (tempf <= maxDist2) { - ret.insert(LocatorInfo(myVecRef[i].m_index, myVecRef[i].m_mySet, myVecRef[i].m_point)); + ret.push_back(LocatorInfo(myVecRef[i].m_index, myVecRef[i].m_mySet, myVecRef[i].m_point)); } } } else { diff --git a/src/Common/CaretPointLocator.h b/src/Common/CaretPointLocator.h index 055891628..cc0cb3ad9 100644 --- a/src/Common/CaretPointLocator.h +++ b/src/Common/CaretPointLocator.h @@ -36,11 +36,12 @@ namespace caret { int64_t index; int32_t whichSet; Vector3D coords; + LocatorInfo() { index = -1; whichSet = -1; } LocatorInfo(const int64_t& indexIn, const int32_t& whichSetIn, const Vector3D& coordsIn) : index(indexIn), whichSet(whichSetIn), coords(coordsIn) { } bool operator==(const LocatorInfo& rhs) const { return (index == rhs.index) && (whichSet == rhs.whichSet); }//ignore coords bool operator<(const LocatorInfo& rhs) const { - if (whichSet == rhs.whichSet)//expect multi-set usage with pointsInRange to be rare, but still separate by whichSet + if (whichSet == rhs.whichSet)//expect multi-set usage with pointsInRange to be rare, but still separate by whichSet if sorted { return index < rhs.index; } else { @@ -77,14 +78,17 @@ namespace caret { CaretPointLocator(const float minBounds[3], const float maxBounds[3]); ///make a point locator with the bounding box of this point set, and use this point set as set #0 CaretPointLocator(const float* coordsIn, const int64_t numCoords); + ///convenience constructor for vectors + CaretPointLocator(const std::vector coordsIn) : CaretPointLocator(coordsIn.data(), coordsIn.size() / 3) { } ///add a point set, SAVE THE RETURN VALUE because it is how you identify which point set found points belong to int32_t addPointSet(const float* coordsIn, const int64_t numCoords); + int32_t addPointSet(const std::vector coordsIn) { return addPointSet(coordsIn.data(), coordsIn.size() / 3); } ///remove a point set by its set number void removePointSet(const int32_t whichSet); ///returns the index of the closest point, and optionally which point set and the coords int64_t closestPoint(const float target[3], LocatorInfo* infoOut = NULL) const; int64_t closestPointLimited(const float target[3], const float& maxDist, LocatorInfo* infoOut = NULL) const; - std::set pointsInRange(const float target[3], const float& maxDist) const; + std::vector pointsInRange(const float target[3], const float& maxDist) const; bool anyInRange(const float target[3], const float& maxDist) const; }; } diff --git a/src/Common/VoxelIJK.h b/src/Common/VoxelIJK.h index 1ee3c34b7..9d7c8d349 100644 --- a/src/Common/VoxelIJK.h +++ b/src/Common/VoxelIJK.h @@ -36,7 +36,7 @@ namespace caret { m_ijk[1] = ijk[1]; m_ijk[2] = ijk[2]; } - bool operator<(const VoxelIJK& rhs) const//so it kan be the key of a map + bool operator<(const VoxelIJK& rhs) const//so it can be the key of a map { if (m_ijk[2] < rhs.m_ijk[2]) return true;//compare such that when sorted, m_ijk[0] moves fastest if (m_ijk[2] > rhs.m_ijk[2]) return false; @@ -51,6 +51,7 @@ namespace caret { m_ijk[2] == rhs.m_ijk[2]); } bool operator!=(const VoxelIJK& rhs) const { return !((*this) == rhs); } + inline operator int64_t*() { return m_ijk; } }; } diff --git a/src/Files/VolumePaddingHelper.cxx b/src/Files/VolumePaddingHelper.cxx index 5cdeecda5..4022ad229 100644 --- a/src/Files/VolumePaddingHelper.cxx +++ b/src/Files/VolumePaddingHelper.cxx @@ -70,14 +70,14 @@ VolumePaddingHelper VolumePaddingHelper::padMM(const VolumeFile* orig, const flo return padVoxels(orig, ipad, jpad, kpad); } -void VolumePaddingHelper::doPadding(const VolumeFile* orig, VolumeFile* padded, const float& padval) +void VolumePaddingHelper::doPadding(const VolumeFile* orig, VolumeFile* padded, const float& padval) const { CaretAssert(padded != orig); + bool labelMode = (orig->getType() == SubvolumeAttributes::LABEL); if (!orig->matchesVolumeSpace(m_origDims.data(), m_origSform)) throw CaretException("attempted to pad a volume that doesn't match the one initialized with"); vector newdims = m_paddedDims, curdims = orig->getOriginalDimensions(); while (newdims.size() < curdims.size()) newdims.push_back(curdims[newdims.size()]);//add the nonspatial dimensions from orig padded->reinitialize(newdims, m_paddedSform, orig->getNumberOfComponents(), orig->getType()); - vector padframe(m_paddedDims[0] * m_paddedDims[1] * m_paddedDims[2], padval); vector loopdims; orig->getDimensions(loopdims); for (int c = 0; c < loopdims[4]; ++c) @@ -86,7 +86,7 @@ void VolumePaddingHelper::doPadding(const VolumeFile* orig, VolumeFile* padded, { if (c == 0) { - if (orig->getType() == SubvolumeAttributes::LABEL) + if (labelMode) { *(padded->getMapLabelTable(s)) = *(orig->getMapLabelTable(s)); } else { @@ -94,6 +94,9 @@ void VolumePaddingHelper::doPadding(const VolumeFile* orig, VolumeFile* padded, } padded->setMapName(s, orig->getMapName(s)); } + float mypadval = padval; + if (labelMode) mypadval = orig->getMapLabelTable(s)->getUnassignedLabelKey(); + vector padframe(m_paddedDims[0] * m_paddedDims[1] * m_paddedDims[2], mypadval); int64_t ijk[3], inIndex = 0;//we scan the frame linearly, so we can do this const float* inFrame = orig->getFrame(s, c); for (ijk[2] = 0; ijk[2] < m_origDims[2]; ++ijk[2]) @@ -113,7 +116,7 @@ void VolumePaddingHelper::doPadding(const VolumeFile* orig, VolumeFile* padded, } } -void VolumePaddingHelper::undoPadding(const VolumeFile* padded, VolumeFile* orig) +void VolumePaddingHelper::undoPadding(const VolumeFile* padded, VolumeFile* orig) const { CaretAssert(orig != padded); if (!padded->matchesVolumeSpace(m_paddedDims.data(), m_paddedSform)) throw CaretException("attempted to unpad a volume that doesn't match padding"); diff --git a/src/Files/VolumePaddingHelper.h b/src/Files/VolumePaddingHelper.h index 3104913f9..b4d3542b5 100644 --- a/src/Files/VolumePaddingHelper.h +++ b/src/Files/VolumePaddingHelper.h @@ -37,8 +37,9 @@ namespace caret { VolumePaddingHelper() { } static VolumePaddingHelper padMM(const VolumeFile* orig, const float& mmpad); static VolumePaddingHelper padVoxels(const VolumeFile* orig, const int& ipad, const int& jpad, const int& kpad); - void doPadding(const VolumeFile* orig, VolumeFile* padded, const float& padval = 0.0f); - void undoPadding(const VolumeFile* padded, VolumeFile* orig); + void getPadding(int& ipad, int& jpad, int& kpad) const { ipad = m_ipad; jpad = m_jpad; kpad = m_kpad; } + void doPadding(const VolumeFile* orig, VolumeFile* padded, const float& padval = 0.0f) const; + void undoPadding(const VolumeFile* padded, VolumeFile* orig) const; }; } diff --git a/src/FilesBase/VolumeSpace.h b/src/FilesBase/VolumeSpace.h index 0afc6be39..40b512cd3 100644 --- a/src/FilesBase/VolumeSpace.h +++ b/src/FilesBase/VolumeSpace.h @@ -24,6 +24,7 @@ #include "CaretAssert.h" #include "Vector3D.h" +#include "VoxelIJK.h" #include #include @@ -86,6 +87,21 @@ namespace caret inline void indexToSpace(const T* indexIn, float& coordOut1, float& coordOut2, float& coordOut3) const { indexToSpace(indexIn[0], indexIn[1], indexIn[2], coordOut1, coordOut2, coordOut3); } + ///output a Vector3D for three indices + template + inline Vector3D indexToSpace(const T& indexIn1, const T& indexIn2, const T& indexIn3) const + { Vector3D coords; indexToSpace(indexIn1, indexIn2, indexIn3, coords); return coords; } + + ///output a Vector3D for an index triplet + template + inline Vector3D indexToSpace(const T* indexIn) const + { Vector3D coords; indexToSpace(indexIn, coords); return coords; } + + ///convenience methods to use VoxelIJK without .m_ijk + inline Vector3D indexToSpace(const VoxelIJK indexIn) const { return indexToSpace(indexIn.m_ijk); } + inline void indexToSpace(const VoxelIJK indexIn, float* coordOut) const { indexToSpace(indexIn.m_ijk, coordOut); } + inline void indexToSpace(const VoxelIJK indexIn, float& coordOut1, float& coordOut2, float& coordOut3) const { indexToSpace(indexIn.m_ijk, coordOut1, coordOut2, coordOut3); } + ///returns three coordinates of three indices template void indexToSpace(const T& indexIn1, const T& indexIn2, const T& indexIn3, float& coordOut1, float& coordOut2, float& coordOut3) const; diff --git a/src/Operations/OperationCiftiResampleDconnMemory.cxx b/src/Operations/OperationCiftiResampleDconnMemory.cxx index 2cc2b2fb3..7272b0750 100644 --- a/src/Operations/OperationCiftiResampleDconnMemory.cxx +++ b/src/Operations/OperationCiftiResampleDconnMemory.cxx @@ -61,17 +61,19 @@ OperationParameters* OperationCiftiResampleDconnMemory::getParameters() OptionalParameter* volDilateOpt = ret->createOptionalParameter(9, "-volume-predilate", "dilate the volume components before resampling"); volDilateOpt->addDoubleParameter(1, "dilate-mm", "distance, in mm, to dilate"); volDilateOpt->createOptionalParameter(2, "-nearest", "use nearest value dilation"); - OptionalParameter* volDilateWeightedOpt = volDilateOpt->createOptionalParameter(3, "-weighted", "use weighted dilation"); + OptionalParameter* volDilateWeightedOpt = volDilateOpt->createOptionalParameter(3, "-weighted", "use weighted dilation (default)"); OptionalParameter* volDilateExpOpt = volDilateWeightedOpt->createOptionalParameter(1, "-exponent", "specify exponent in weighting function"); - volDilateExpOpt->addDoubleParameter(1, "exponent", "exponent 'n' to use in (1 / (distance ^ n)) as the weighting function (default 2)"); + volDilateExpOpt->addDoubleParameter(1, "exponent", "exponent 'n' to use in (1 / (distance ^ n)) as the weighting function (default 7)"); + volDilateWeightedOpt->createOptionalParameter(2, "-legacy-cutoff", "use v1.3.2 logic for the kernel cutoff"); OptionalParameter* surfDilateOpt = ret->createOptionalParameter(10, "-surface-postdilate", "dilate the surface components after resampling"); surfDilateOpt->addDoubleParameter(1, "dilate-mm", "distance, in mm, to dilate"); surfDilateOpt->createOptionalParameter(2, "-nearest", "use nearest value dilation"); surfDilateOpt->createOptionalParameter(3, "-linear", "use linear dilation"); - OptionalParameter* surfDilateWeightedOpt = surfDilateOpt->createOptionalParameter(4, "-weighted", "use weighted dilation"); + OptionalParameter* surfDilateWeightedOpt = surfDilateOpt->createOptionalParameter(4, "-weighted", "use weighted dilation (default)"); OptionalParameter* surfDilateExpOpt = surfDilateWeightedOpt->createOptionalParameter(1, "-exponent", "specify exponent in weighting function"); - surfDilateExpOpt->addDoubleParameter(1, "exponent", "exponent 'n' to use in (area / (distance ^ n)) as the weighting function (default 2)"); + surfDilateExpOpt->addDoubleParameter(1, "exponent", "exponent 'n' to use in (area / (distance ^ n)) as the weighting function (default 6)"); + surfDilateWeightedOpt->createOptionalParameter(2, "-legacy-cutoff", "use v1.3.2 logic for the kernel cutoff"); OptionalParameter* affineOpt = ret->createOptionalParameter(11, "-affine", "use an affine transformation on the volume components"); affineOpt->addStringParameter(1, "affine-file", "the affine file to use"); @@ -122,6 +124,7 @@ OperationParameters* OperationCiftiResampleDconnMemory::getParameters() "If spheres are not specified for a surface structure which exists in the cifti files, its data is copied without resampling or dilation. " + "Dilation is done with the 'nearest' method, and is done on for surface data. " + "Volume components are padded before dilation so that dilation doesn't run into the edge of the component bounding box.\n\n" + + "To get the v1.3.2 and earlier behavior of weighted dilation, specify exponent of 2 for surface and volume, and -legacy-cutoff for both surface and volume.\n\n" + "The argument must be one of the following:\n\n" + "CUBIC\nENCLOSING_VOXEL\nTRILINEAR\n\n" + "The argument must be one of the following:\n\n"; @@ -186,8 +189,9 @@ void OperationCiftiResampleDconnMemory::useParameters(OperationParameters* myPar } AlgorithmVolumeDilate::Method volDilateMethod = AlgorithmVolumeDilate::WEIGHTED; float volDilateExponent = 2.0f; - AlgorithmMetricDilate::Method surfDilateMethod = AlgorithmMetricDilate::WEIGHTED;//label dilate doesn't support multiple methods - what to do there, share the enum between them? + AlgorithmMetricDilate::Method surfDilateMethod = AlgorithmMetricDilate::WEIGHTED;//label dilate doesn't support multiple methods float surfDilateExponent = 2.0f;//label dilate currently only supports nearest, so in order to accept a default in the algorithm, it currently ignores this on label data, no warning + bool surfLegacyCutoff = false, volLegacyCutoff = false; OptionalParameter* volDilateOpt = myParams->getOptionalParameter(9); if (volDilateOpt->m_present) { @@ -210,6 +214,7 @@ void OperationCiftiResampleDconnMemory::useParameters(OperationParameters* myPar { volDilateExponent = (float)volDilateExpOpt->getDouble(1); } + volLegacyCutoff = volDilateWeightedOpt->getOptionalParameter(2)->m_present; } } OptionalParameter* surfDilateOpt = myParams->getOptionalParameter(10); @@ -242,6 +247,7 @@ void OperationCiftiResampleDconnMemory::useParameters(OperationParameters* myPar { surfDilateExponent = (float)surfDilateExpOpt->getDouble(1); } + surfLegacyCutoff = surfDilateWeightedOpt->getOptionalParameter(2)->m_present; } } OptionalParameter* affineOpt = myParams->getOptionalParameter(11); @@ -404,22 +410,22 @@ void OperationCiftiResampleDconnMemory::useParameters(OperationParameters* myPar curLeftSphere, newLeftSphere, curLeftAreas, newLeftAreas, curRightSphere, newRightSphere, curRightAreas, newRightAreas, curCerebSphere, newCerebSphere, curCerebAreas, newCerebAreas, - volDilateMethod, volDilateExponent, surfDilateMethod, surfDilateExponent); + volDilateMethod, volDilateExponent, surfDilateMethod, surfDilateExponent, volLegacyCutoff, surfLegacyCutoff); AlgorithmCiftiResample(myProgObj, &tempCifti, CiftiXML::ALONG_ROW, myTemplate, templateDir, mySurfMethod, myVolMethod, myCiftiOut, surfLargest, voldilatemm, surfdilatemm, myWarpfield.getWarpfield(), curLeftSphere, newLeftSphere, curLeftAreas, newLeftAreas, curRightSphere, newRightSphere, curRightAreas, newRightAreas, curCerebSphere, newCerebSphere, curCerebAreas, newCerebAreas, - volDilateMethod, volDilateExponent, surfDilateMethod, surfDilateExponent); + volDilateMethod, volDilateExponent, surfDilateMethod, surfDilateExponent, volLegacyCutoff, surfLegacyCutoff); } else {//rely on AffineFile() being the identity transform for if neither option is specified AlgorithmCiftiResample(myProgObj, myCiftiIn, CiftiXML::ALONG_COLUMN, myTemplate, templateDir, mySurfMethod, myVolMethod, &tempCifti, surfLargest, voldilatemm, surfdilatemm, myAffine.getMatrix(), curLeftSphere, newLeftSphere, curLeftAreas, newLeftAreas, curRightSphere, newRightSphere, curRightAreas, newRightAreas, curCerebSphere, newCerebSphere, curCerebAreas, newCerebAreas, - volDilateMethod, volDilateExponent, surfDilateMethod, surfDilateExponent); + volDilateMethod, volDilateExponent, surfDilateMethod, surfDilateExponent, volLegacyCutoff, surfLegacyCutoff); AlgorithmCiftiResample(myProgObj, &tempCifti, CiftiXML::ALONG_ROW, myTemplate, templateDir, mySurfMethod, myVolMethod, myCiftiOut, surfLargest, voldilatemm, surfdilatemm, myAffine.getMatrix(), curLeftSphere, newLeftSphere, curLeftAreas, newLeftAreas, curRightSphere, newRightSphere, curRightAreas, newRightAreas, curCerebSphere, newCerebSphere, curCerebAreas, newCerebAreas, - volDilateMethod, volDilateExponent, surfDilateMethod, surfDilateExponent); + volDilateMethod, volDilateExponent, surfDilateMethod, surfDilateExponent, volLegacyCutoff, surfLegacyCutoff); } } -- GitLab From 5692d1fb38926f29b042dc33749b283fa54c0e92 Mon Sep 17 00:00:00 2001 From: John Harwell Date: Thu, 20 Dec 2018 12:01:57 -0600 Subject: [PATCH 150/427] Fix mouse movement problem with Qt 5.12. BrainOpenGLWidget::mouseMoveEvent() tested QMouseEvent::button() to verify it was equal to Qt::NoButton which, according to the documentation is always the case for mouse move events. However, in Qt 5.12, this is not true so this test of QMouseEvent::button() == Qt::NoButton has been removed. Other code uses QMouseEvent::buttons() [note plural] to find the button that is actually pressed. --- src/GuiQt/BrainOpenGLWidget.cxx | 129 +++++++++++++++----------------- 1 file changed, 62 insertions(+), 67 deletions(-) diff --git a/src/GuiQt/BrainOpenGLWidget.cxx b/src/GuiQt/BrainOpenGLWidget.cxx index 81159fdad..9510e73ff 100644 --- a/src/GuiQt/BrainOpenGLWidget.cxx +++ b/src/GuiQt/BrainOpenGLWidget.cxx @@ -1420,92 +1420,87 @@ BrainOpenGLWidget::mouseMoveEvent(QMouseEvent* me) const int mouseX = me->x(); const int mouseY = this->windowHeight[this->windowIndex] - me->y(); - if (button == Qt::NoButton) { + if (mouseButtons == Qt::LeftButton) { + this->mouseMovementMinimumX = std::min(this->mouseMovementMinimumX, mouseX); + this->mouseMovementMaximumX = std::max(this->mouseMovementMaximumX, mouseX); + this->mouseMovementMinimumY = std::min(this->mouseMovementMinimumY, mouseY); + this->mouseMovementMaximumY = std::max(this->mouseMovementMaximumY, mouseY); - if (mouseButtons == Qt::LeftButton) { - - this->mouseMovementMinimumX = std::min(this->mouseMovementMinimumX, mouseX); - this->mouseMovementMaximumX = std::max(this->mouseMovementMaximumX, mouseX); - this->mouseMovementMinimumY = std::min(this->mouseMovementMinimumY, mouseY); - this->mouseMovementMaximumY = std::max(this->mouseMovementMaximumY, mouseY); - - const int dx = mouseX - this->lastMouseX; - const int dy = mouseY - this->lastMouseY; - const int absDX = (dx >= 0) ? dx : -dx; - const int absDY = (dy >= 0) ? dy : -dy; - - if ((absDX > 0) - || (absDY > 0)) { - /* - * Use location of mouse press so that the model - * being manipulated does not change if mouse moves - * out of its viewport without releasing the mouse - * button. - */ - const BrainOpenGLViewportContent* viewportContent = this->getViewportContentAtXY(this->mousePressX, - this->mousePressY); - if (viewportContent != NULL) { - MouseEvent mouseEvent(viewportContent, - this, - this->windowIndex, - mouseX, - mouseY, - dx, - dy, - this->mousePressX, - this->mousePressY, - this->mouseNewDraggingStartedFlag); - - if (keyModifiers == Qt::NoModifier) { - this->selectedUserInputProcessor->mouseLeftDrag(mouseEvent); - } - else if (keyModifiers == Qt::ControlModifier) { - this->selectedUserInputProcessor->mouseLeftDragWithCtrl(mouseEvent); - } - else if (keyModifiers == Qt::ShiftModifier) { - this->selectedUserInputProcessor->mouseLeftDragWithShift(mouseEvent); - } - else if (keyModifiers == Qt::AltModifier) { - this->selectedUserInputProcessor->mouseLeftDragWithAlt(mouseEvent); - } - else if (keyModifiers == (Qt::ShiftModifier - | Qt::ControlModifier)) { - this->selectedUserInputProcessor->mouseLeftDragWithCtrlShift(mouseEvent); - } - - this->mouseNewDraggingStartedFlag = false; - } - } - - this->lastMouseX = mouseX; - this->lastMouseY = mouseY; - } - else if (mouseButtons == Qt::NoButton) { - const BrainOpenGLViewportContent* viewportContent = this->getViewportContentAtXY(mouseX, - mouseY); + const int dx = mouseX - this->lastMouseX; + const int dy = mouseY - this->lastMouseY; + const int absDX = (dx >= 0) ? dx : -dx; + const int absDY = (dy >= 0) ? dy : -dy; + + if ((absDX > 0) + || (absDY > 0)) { + /* + * Use location of mouse press so that the model + * being manipulated does not change if mouse moves + * out of its viewport without releasing the mouse + * button. + */ + const BrainOpenGLViewportContent* viewportContent = this->getViewportContentAtXY(this->mousePressX, + this->mousePressY); if (viewportContent != NULL) { MouseEvent mouseEvent(viewportContent, this, this->windowIndex, mouseX, mouseY, - 0, - 0, + dx, + dy, this->mousePressX, this->mousePressY, this->mouseNewDraggingStartedFlag); if (keyModifiers == Qt::NoModifier) { - this->selectedUserInputProcessor->mouseMove(mouseEvent); + this->selectedUserInputProcessor->mouseLeftDrag(mouseEvent); + } + else if (keyModifiers == Qt::ControlModifier) { + this->selectedUserInputProcessor->mouseLeftDragWithCtrl(mouseEvent); } else if (keyModifiers == Qt::ShiftModifier) { - this->selectedUserInputProcessor->mouseMoveWithShift(mouseEvent); + this->selectedUserInputProcessor->mouseLeftDragWithShift(mouseEvent); + } + else if (keyModifiers == Qt::AltModifier) { + this->selectedUserInputProcessor->mouseLeftDragWithAlt(mouseEvent); + } + else if (keyModifiers == (Qt::ShiftModifier + | Qt::ControlModifier)) { + this->selectedUserInputProcessor->mouseLeftDragWithCtrlShift(mouseEvent); } + + this->mouseNewDraggingStartedFlag = false; } + } + + this->lastMouseX = mouseX; + this->lastMouseY = mouseY; + } + else if (mouseButtons == Qt::NoButton) { + const BrainOpenGLViewportContent* viewportContent = this->getViewportContentAtXY(mouseX, + mouseY); + if (viewportContent != NULL) { + MouseEvent mouseEvent(viewportContent, + this, + this->windowIndex, + mouseX, + mouseY, + 0, + 0, + this->mousePressX, + this->mousePressY, + this->mouseNewDraggingStartedFlag); + if (keyModifiers == Qt::NoModifier) { + this->selectedUserInputProcessor->mouseMove(mouseEvent); + } + else if (keyModifiers == Qt::ShiftModifier) { + this->selectedUserInputProcessor->mouseMoveWithShift(mouseEvent); + } } } - + const BrainOpenGLViewportContent* viewportContent = this->getViewportContentAtXY(mouseX, mouseY); if (viewportContent != NULL) { -- GitLab From 5b62c29f15bada0a7ba4177464fd316279f43a83 Mon Sep 17 00:00:00 2001 From: John Harwell Date: Thu, 20 Dec 2018 12:43:40 -0600 Subject: [PATCH 151/427] Fix a couple of compilation warnings produced by compiler on MacOS Mojave. --- src/GuiQt/AnnotationLineArrowTipsWidget.cxx | 2 +- src/GuiQt/BrainOpenGLWidget.cxx | 11 +++++++++++ 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/src/GuiQt/AnnotationLineArrowTipsWidget.cxx b/src/GuiQt/AnnotationLineArrowTipsWidget.cxx index 820ee8fb4..8a9a4a252 100644 --- a/src/GuiQt/AnnotationLineArrowTipsWidget.cxx +++ b/src/GuiQt/AnnotationLineArrowTipsWidget.cxx @@ -59,7 +59,7 @@ AnnotationLineArrowTipsWidget::AnnotationLineArrowTipsWidget(const int32_t brows m_browserWindowIndex(browserWindowIndex) { QLabel* label = new QLabel("Line"); - + QToolButton* endArrowToolButton = new QToolButton(); m_endArrowAction = new QAction(this); m_endArrowAction->setCheckable(true); diff --git a/src/GuiQt/BrainOpenGLWidget.cxx b/src/GuiQt/BrainOpenGLWidget.cxx index 9510e73ff..e97b78672 100644 --- a/src/GuiQt/BrainOpenGLWidget.cxx +++ b/src/GuiQt/BrainOpenGLWidget.cxx @@ -97,9 +97,20 @@ using namespace caret; * * @param * The parent widget. + * @param shareWidget + * Widget used for sharing OpenGL contexts when the deprecated + * QGLWidget is used. Note that with QOpenGLWidget, + * sharing is enabled by calling QCoreApplication::setAttribute(Qt::AA_ShareOpenGLContexts) + * in desktop.cxx + * @param windowIndex + * Index of this window */ BrainOpenGLWidget::BrainOpenGLWidget(QWidget* parent, +#ifdef WORKBENCH_USE_QT5_QOPENGL_WIDGET + const BrainOpenGLWidget* /*shareWidget*/, +#else const BrainOpenGLWidget* shareWidget, +#endif const int32_t windowIndex) #ifdef WORKBENCH_USE_QT5_QOPENGL_WIDGET : QOpenGLWidget(parent), -- GitLab From d2e3cc8d20c4d15d4e04fa1c633c28121e6f2f41 Mon Sep 17 00:00:00 2001 From: John Harwell Date: Fri, 21 Dec 2018 15:17:19 -0600 Subject: [PATCH 152/427] WB-834: Implemented use of shortcut key to run a macro. --- src/Common/CMakeLists.txt | 2 + src/Common/WuQMacro.cxx | 24 +- src/Common/WuQMacro.h | 11 +- src/Common/WuQMacroGroup.cxx | 38 ++ src/Common/WuQMacroGroup.h | 7 +- src/Common/WuQMacroGroupXmlFormatBase.h | 4 +- src/Common/WuQMacroGroupXmlReader.cxx | 23 +- src/Common/WuQMacroGroupXmlWriter.cxx | 5 +- src/Common/WuQMacroShortCutKeyEnum.cxx | 549 ++++++++++++++++++++++ src/Common/WuQMacroShortCutKeyEnum.h | 176 +++++++ src/Common/createMacroShortCutKeyEnum.sh | 42 ++ src/GuiQt/BrainBrowserWindow.cxx | 10 + src/GuiQt/BrainOpenGLWidget.cxx | 10 +- src/GuiQt/CMakeLists.txt | 5 + src/GuiQt/UserInputModeAbstract.h | 6 +- src/GuiQt/UserInputModeAnnotations.cxx | 14 +- src/GuiQt/UserInputModeAnnotations.h | 2 +- src/GuiQt/WuQMacroAttributesDialog.cxx | 29 +- src/GuiQt/WuQMacroAttributesDialog.h | 3 +- src/GuiQt/WuQMacroCreateDialog.cxx | 44 +- src/GuiQt/WuQMacroCreateDialog.h | 9 +- src/GuiQt/WuQMacroDialog.cxx | 61 ++- src/GuiQt/WuQMacroDialog.h | 6 + src/GuiQt/WuQMacroEditorDialog.cxx | 33 +- src/GuiQt/WuQMacroEditorDialog.h | 5 +- src/GuiQt/WuQMacroExecutor.cxx | 27 +- src/GuiQt/WuQMacroExecutor.h | 35 +- src/GuiQt/WuQMacroExecutorOptions.cxx | 187 ++++++++ src/GuiQt/WuQMacroExecutorOptions.h | 85 ++++ src/GuiQt/WuQMacroManager.cxx | 108 ++++- src/GuiQt/WuQMacroManager.h | 17 +- src/GuiQt/WuQMacroShortCutKeyComboBox.cxx | 132 ++++++ src/GuiQt/WuQMacroShortCutKeyComboBox.h | 71 +++ 33 files changed, 1644 insertions(+), 136 deletions(-) create mode 100644 src/Common/WuQMacroShortCutKeyEnum.cxx create mode 100644 src/Common/WuQMacroShortCutKeyEnum.h create mode 100755 src/Common/createMacroShortCutKeyEnum.sh create mode 100644 src/GuiQt/WuQMacroExecutorOptions.cxx create mode 100644 src/GuiQt/WuQMacroExecutorOptions.h create mode 100644 src/GuiQt/WuQMacroShortCutKeyComboBox.cxx create mode 100644 src/GuiQt/WuQMacroShortCutKeyComboBox.h diff --git a/src/Common/CMakeLists.txt b/src/Common/CMakeLists.txt index b175aa8ce..89d13da89 100644 --- a/src/Common/CMakeLists.txt +++ b/src/Common/CMakeLists.txt @@ -172,6 +172,7 @@ WuQMacroGroupXmlWriter.h WuQMacroModeEnum.h WuQMacroMouseEventInfo.h WuQMacroMouseEventTypeEnum.h +WuQMacroShortCutKeyEnum.h YokingGroupEnum.h ${MOC_SOURCE_FILES} @@ -285,6 +286,7 @@ WuQMacroGroupXmlWriter.cxx WuQMacroModeEnum.cxx WuQMacroMouseEventInfo.cxx WuQMacroMouseEventTypeEnum.cxx +WuQMacroShortCutKeyEnum.cxx YokingGroupEnum.cxx ) diff --git a/src/Common/WuQMacro.cxx b/src/Common/WuQMacro.cxx index 774b128ed..cda10953d 100644 --- a/src/Common/WuQMacro.cxx +++ b/src/Common/WuQMacro.cxx @@ -110,7 +110,7 @@ WuQMacro::copyHelperWuQMacro(const WuQMacro& obj) m_name = obj.m_name; m_description = obj.m_description; - m_functionKey = obj.m_functionKey; + m_shortCutKey = obj.m_shortCutKey; } void @@ -169,25 +169,25 @@ WuQMacro::setDescription(const QString& description) } /** - * @return The function key + * @return The short cut key */ -QString -WuQMacro::getFunctionKey() const +WuQMacroShortCutKeyEnum::Enum +WuQMacro::getShortCutKey() const { - return m_functionKey; + return m_shortCutKey; } /** - * Set the function key + * Set the short cut key * - * @param functionKey - * New function key + * @param shortCutKey + * New short cut key */ void -WuQMacro::setFunctionKey(const QString& functionKey) +WuQMacro::setShortCutKey(const WuQMacroShortCutKeyEnum::Enum shortCutKey) { - if (m_functionKey != functionKey) { - m_functionKey = functionKey; + if (m_shortCutKey != shortCutKey) { + m_shortCutKey = shortCutKey; setModified(); } } @@ -270,7 +270,7 @@ WuQMacro::toString() const s.append("Name=" + m_name + "\n"); s.append("Description=" + m_description + "\n"); - s.append("Function Key=" + m_functionKey + "\n"); + s.append("ShortCutKey=" + WuQMacroShortCutKeyEnum::toGuiName(m_shortCutKey) + "\n"); for (auto mc : m_macroCommands) { s.append(mc->toString() + "\n"); diff --git a/src/Common/WuQMacro.h b/src/Common/WuQMacro.h index f47e95c24..64f393910 100644 --- a/src/Common/WuQMacro.h +++ b/src/Common/WuQMacro.h @@ -26,6 +26,7 @@ #include #include "CaretObjectTracksModification.h" +#include "WuQMacroShortCutKeyEnum.h" namespace caret { @@ -58,18 +59,18 @@ namespace caret { void setDescription(const QString& description); - QString getFunctionKey() const; + WuQMacroShortCutKeyEnum::Enum getShortCutKey() const; - void setFunctionKey(const QString& functionKey); + void setShortCutKey(const WuQMacroShortCutKeyEnum::Enum shortCutKey); - // ADD_NEW_METHODS_HERE - virtual AString toString() const; virtual bool isModified() const override; virtual void clearModified() override; + // ADD_NEW_METHODS_HERE + private: void copyHelperWuQMacro(const WuQMacro& obj); @@ -81,7 +82,7 @@ namespace caret { QString m_description; - QString m_functionKey; + WuQMacroShortCutKeyEnum::Enum m_shortCutKey = WuQMacroShortCutKeyEnum::Key_None; // ADD_NEW_MEMBERS_HERE diff --git a/src/Common/WuQMacroGroup.cxx b/src/Common/WuQMacroGroup.cxx index 23d85efc2..471da57ce 100644 --- a/src/Common/WuQMacroGroup.cxx +++ b/src/Common/WuQMacroGroup.cxx @@ -223,6 +223,44 @@ WuQMacroGroup::getMacroByName(const QString& name) const return NULL; } +/** + * Get the macro with the given short cut key + * + * @param name + * Name of the macro + * @return + * Pointer to macro with the short cut key or NULL if not found + */ +WuQMacro* +WuQMacroGroup::getMacroWithShortCutKey(const WuQMacroShortCutKeyEnum::Enum shortCutKey) +{ + for (auto m : m_macros) { + if (m->getShortCutKey() == shortCutKey) { + return m; + } + } + return NULL; +} + +/** + * Get the macro with the given short cut key + * + * @param name + * Name of the macro + * @return + * Pointer to macro with the short cut key or NULL if not found + */ +const WuQMacro* +WuQMacroGroup::getMacroWithShortCutKey(const WuQMacroShortCutKeyEnum::Enum shortCutKey) const +{ + for (auto m : m_macros) { + if (m->getShortCutKey() == shortCutKey) { + return m; + } + } + return NULL; +} + /** * @return * Macro at the given index diff --git a/src/Common/WuQMacroGroup.h b/src/Common/WuQMacroGroup.h index a52492fc3..3bff9f58b 100644 --- a/src/Common/WuQMacroGroup.h +++ b/src/Common/WuQMacroGroup.h @@ -26,8 +26,7 @@ #include #include "CaretObjectTracksModification.h" - - +#include "WuQMacroShortCutKeyEnum.h" namespace caret { @@ -64,6 +63,10 @@ namespace caret { const WuQMacro* getMacroAtIndex(const int32_t index) const; + WuQMacro* getMacroWithShortCutKey(const WuQMacroShortCutKeyEnum::Enum shortCutKey); + + const WuQMacro* getMacroWithShortCutKey(const WuQMacroShortCutKeyEnum::Enum shortCutKey) const; + void deleteMacro(const WuQMacro* macro); void deleteMacroAtIndex(const int32_t index); diff --git a/src/Common/WuQMacroGroupXmlFormatBase.h b/src/Common/WuQMacroGroupXmlFormatBase.h index c6d5afe64..aa16eec11 100644 --- a/src/Common/WuQMacroGroupXmlFormatBase.h +++ b/src/Common/WuQMacroGroupXmlFormatBase.h @@ -54,9 +54,9 @@ namespace caret { static const QString ATTRIBUTE_OBJECT_CLASS; static const QString ATTRIBUTE_OBJECT_DATA_TYPE; static const QString ATTRIBUTE_OBJECT_DATA_TYPE_TWO; - static const QString ATTRIBUTE_FUNCTION_KEY; static const QString ATTRIBUTE_OBJECT_VALUE; static const QString ATTRIBUTE_OBJECT_VALUE_TWO; + static const QString ATTRIBUTE_SHORT_CUT_KEY; static const QString ATTRIBUTE_VERSION; static const QString ATTRIBUTE_MOUSE_BUTTON; @@ -93,9 +93,9 @@ namespace caret { const QString WuQMacroGroupXmlFormatBase::ATTRIBUTE_OBJECT_CLASS = "ObjectClass"; const QString WuQMacroGroupXmlFormatBase::ATTRIBUTE_OBJECT_DATA_TYPE = "DataType"; const QString WuQMacroGroupXmlFormatBase::ATTRIBUTE_OBJECT_DATA_TYPE_TWO = "DataTypeTwo"; - const QString WuQMacroGroupXmlFormatBase::ATTRIBUTE_FUNCTION_KEY = "FunctionKey"; const QString WuQMacroGroupXmlFormatBase::ATTRIBUTE_OBJECT_VALUE = "DataValue"; const QString WuQMacroGroupXmlFormatBase::ATTRIBUTE_OBJECT_VALUE_TWO = "DataValueTwo"; + const QString WuQMacroGroupXmlFormatBase::ATTRIBUTE_SHORT_CUT_KEY = "ShortCutKey"; const QString WuQMacroGroupXmlFormatBase::ATTRIBUTE_VERSION = "Version"; const QString WuQMacroGroupXmlFormatBase::ATTRIBUTE_MOUSE_BUTTON = "MouseButton"; diff --git a/src/Common/WuQMacroGroupXmlReader.cxx b/src/Common/WuQMacroGroupXmlReader.cxx index 0424b35be..5ee38d1ed 100644 --- a/src/Common/WuQMacroGroupXmlReader.cxx +++ b/src/Common/WuQMacroGroupXmlReader.cxx @@ -196,7 +196,26 @@ WuQMacroGroupXmlReader::readMacroVersionOne() const QXmlStreamAttributes attributes = m_xmlStreamReader->attributes(); QString macroName = attributes.value(ATTRIBUTE_NAME).toString(); - const QStringRef functionKey = attributes.value(ATTRIBUTE_FUNCTION_KEY); + QString shortCutKeyString = attributes.value(ATTRIBUTE_SHORT_CUT_KEY).toString(); + if (shortCutKeyString.isEmpty()) { + shortCutKeyString = WuQMacroShortCutKeyEnum::toName(WuQMacroShortCutKeyEnum::Key_None); + addToWarnings(ELEMENT_MACRO + + " is missing attribute or value is empty: " + + ATTRIBUTE_SHORT_CUT_KEY); + } + + bool validShortCutKey(false); + WuQMacroShortCutKeyEnum::Enum shortCutKey = WuQMacroShortCutKeyEnum::fromName(shortCutKeyString, + &validShortCutKey); + if ( ! validShortCutKey) { + shortCutKey = WuQMacroShortCutKeyEnum::Key_None; + addToWarnings(ELEMENT_MACRO + + " attribute " + + ATTRIBUTE_SHORT_CUT_KEY + + " has invalid value " + + shortCutKeyString); + } + if (macroName.isEmpty()) { addToWarnings(ELEMENT_MACRO @@ -209,7 +228,7 @@ WuQMacroGroupXmlReader::readMacroVersionOne() macro = new WuQMacro(); macro->setName(macroName); - macro->setFunctionKey(functionKey.toString()); + macro->setShortCutKey(shortCutKey); return macro; } diff --git a/src/Common/WuQMacroGroupXmlWriter.cxx b/src/Common/WuQMacroGroupXmlWriter.cxx index e03b353a5..cb9da5ed3 100644 --- a/src/Common/WuQMacroGroupXmlWriter.cxx +++ b/src/Common/WuQMacroGroupXmlWriter.cxx @@ -103,8 +103,9 @@ WuQMacroGroupXmlWriter::writeMacroToXML(const WuQMacro* macro) m_xmlStreamWriter->writeStartElement(ELEMENT_MACRO); m_xmlStreamWriter->writeAttribute(ATTRIBUTE_NAME, macro->getName()); - m_xmlStreamWriter->writeAttribute(ATTRIBUTE_FUNCTION_KEY, - macro->getFunctionKey()); + const QString shortCutText = WuQMacroShortCutKeyEnum::toName(macro->getShortCutKey()); + m_xmlStreamWriter->writeAttribute(ATTRIBUTE_SHORT_CUT_KEY, + shortCutText); m_xmlStreamWriter->writeTextElement(ELEMENT_DESCRIPTION, macro->getDescription()); diff --git a/src/Common/WuQMacroShortCutKeyEnum.cxx b/src/Common/WuQMacroShortCutKeyEnum.cxx new file mode 100644 index 000000000..ea1955de0 --- /dev/null +++ b/src/Common/WuQMacroShortCutKeyEnum.cxx @@ -0,0 +1,549 @@ + +/*LICENSE_START*/ +/* + * Copyright (C) 2018 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + +#include +#define __WU_Q_MACRO_SHORT_CUT_KEY_ENUM_DECLARE__ +#include "WuQMacroShortCutKeyEnum.h" +#undef __WU_Q_MACRO_SHORT_CUT_KEY_ENUM_DECLARE__ + +#include "CaretAssert.h" + +using namespace caret; + + +/** + * \class caret::WuQMacroShortCutKeyEnum + * \brief Shortcut keys for running macros + * + * Using this enumerated type in the GUI with an EnumComboBoxTemplate + * + * Header File (.h) + * Forward declare the data type: + * class EnumComboBoxTemplate; + * + * Declare the member: + * EnumComboBoxTemplate* m_wuQMacroShortCutKeyEnumComboBox; + * + * Declare a slot that is called when user changes selection + * private slots: + * void wuQMacroShortCutKeyEnumComboBoxItemActivated(); + * + * Implementation File (.cxx) + * Include the header files + * #include "EnumComboBoxTemplate.h" + * #include "WuQMacroShortCutKeyEnum.h" + * + * Instatiate: + * m_wuQMacroShortCutKeyEnumComboBox = new EnumComboBoxTemplate(this); + * m_wuQMacroShortCutKeyEnumComboBox->setup(); + * + * Get notified when the user changes the selection: + * QObject::connect(m_wuQMacroShortCutKeyEnumComboBox, SIGNAL(itemActivated()), + * this, SLOT(wuQMacroShortCutKeyEnumComboBoxItemActivated())); + * + * Update the selection: + * m_wuQMacroShortCutKeyEnumComboBox->setSelectedItem(NEW_VALUE); + * + * Read the selection: + * const WuQMacroShortCutKeyEnum::Enum VARIABLE = m_wuQMacroShortCutKeyEnumComboBox->getSelectedItem(); + * + */ + +/** + * Constructor. + * + * @param enumValue + * An enumerated value. + * @param name + * Name of enumerated value. + * + * @param guiName + * User-friendly name for use in user-interface. + */ +WuQMacroShortCutKeyEnum::WuQMacroShortCutKeyEnum(const Enum enumValue, + const AString& name, + const AString& guiName) +{ + this->enumValue = enumValue; + this->integerCode = integerCodeCounter++; + this->name = name; + this->guiName = guiName; +} + +/** + * Destructor. + */ +WuQMacroShortCutKeyEnum::~WuQMacroShortCutKeyEnum() +{ +} + +/** + * Initialize the enumerated metadata. + */ +void +WuQMacroShortCutKeyEnum::initialize() +{ + if (initializedFlag) { + return; + } + initializedFlag = true; + + enumData.push_back(WuQMacroShortCutKeyEnum(Key_None, + "Key_None", + "None")); + + enumData.push_back(WuQMacroShortCutKeyEnum(Key_A, + "Key_A", + "A")); + + enumData.push_back(WuQMacroShortCutKeyEnum(Key_B, + "Key_B", + "B")); + + enumData.push_back(WuQMacroShortCutKeyEnum(Key_C, + "Key_C", + "C")); + + enumData.push_back(WuQMacroShortCutKeyEnum(Key_D, + "Key_D", + "D")); + + enumData.push_back(WuQMacroShortCutKeyEnum(Key_E, + "Key_E", + "E")); + + enumData.push_back(WuQMacroShortCutKeyEnum(Key_F, + "Key_F", + "F")); + + enumData.push_back(WuQMacroShortCutKeyEnum(Key_G, + "Key_G", + "G")); + + enumData.push_back(WuQMacroShortCutKeyEnum(Key_H, + "Key_H", + "H")); + + enumData.push_back(WuQMacroShortCutKeyEnum(Key_I, + "Key_I", + "I")); + + enumData.push_back(WuQMacroShortCutKeyEnum(Key_J, + "Key_J", + "J")); + + enumData.push_back(WuQMacroShortCutKeyEnum(Key_K, + "Key_K", + "K")); + + enumData.push_back(WuQMacroShortCutKeyEnum(Key_L, + "Key_L", + "L")); + + enumData.push_back(WuQMacroShortCutKeyEnum(Key_M, + "Key_M", + "M")); + + enumData.push_back(WuQMacroShortCutKeyEnum(Key_N, + "Key_N", + "N")); + + enumData.push_back(WuQMacroShortCutKeyEnum(Key_O, + "Key_O", + "O")); + + enumData.push_back(WuQMacroShortCutKeyEnum(Key_P, + "Key_P", + "P")); + + enumData.push_back(WuQMacroShortCutKeyEnum(Key_Q, + "Key_Q", + "Q")); + + enumData.push_back(WuQMacroShortCutKeyEnum(Key_R, + "Key_R", + "R")); + + enumData.push_back(WuQMacroShortCutKeyEnum(Key_S, + "Key_S", + "S")); + + enumData.push_back(WuQMacroShortCutKeyEnum(Key_T, + "Key_T", + "T")); + + enumData.push_back(WuQMacroShortCutKeyEnum(Key_U, + "Key_U", + "U")); + + enumData.push_back(WuQMacroShortCutKeyEnum(Key_V, + "Key_V", + "V")); + + enumData.push_back(WuQMacroShortCutKeyEnum(Key_W, + "Key_W", + "W")); + + enumData.push_back(WuQMacroShortCutKeyEnum(Key_X, + "Key_X", + "X")); + + enumData.push_back(WuQMacroShortCutKeyEnum(Key_Y, + "Key_Y", + "Y")); + + enumData.push_back(WuQMacroShortCutKeyEnum(Key_Z, + "Key_Z", + "Z")); + + enumData.push_back(WuQMacroShortCutKeyEnum(Key_0, + "Key_0", + "0")); + + enumData.push_back(WuQMacroShortCutKeyEnum(Key_1, + "Key_1", + "1")); + + enumData.push_back(WuQMacroShortCutKeyEnum(Key_2, + "Key_2", + "2")); + + enumData.push_back(WuQMacroShortCutKeyEnum(Key_3, + "Key_3", + "3")); + + enumData.push_back(WuQMacroShortCutKeyEnum(Key_4, + "Key_4", + "4")); + + enumData.push_back(WuQMacroShortCutKeyEnum(Key_5, + "Key_5", + "5")); + + enumData.push_back(WuQMacroShortCutKeyEnum(Key_6, + "Key_6", + "6")); + + enumData.push_back(WuQMacroShortCutKeyEnum(Key_7, + "Key_7", + "7")); + + enumData.push_back(WuQMacroShortCutKeyEnum(Key_8, + "Key_8", + "8")); + + enumData.push_back(WuQMacroShortCutKeyEnum(Key_9, + "Key_9", + "9")); + +} + +/** + * Find the data for and enumerated value. + * @param enumValue + * The enumerated value. + * @return Pointer to data for this enumerated type + * or NULL if no data for type or if type is invalid. + */ +const WuQMacroShortCutKeyEnum* +WuQMacroShortCutKeyEnum::findData(const Enum enumValue) +{ + if (initializedFlag == false) initialize(); + + size_t num = enumData.size(); + for (size_t i = 0; i < num; i++) { + const WuQMacroShortCutKeyEnum* d = &enumData[i]; + if (d->enumValue == enumValue) { + return d; + } + } + + return NULL; +} + +/** + * Get a string representation of the enumerated type. + * @param enumValue + * Enumerated value. + * @return + * String representing enumerated value. + */ +AString +WuQMacroShortCutKeyEnum::toName(Enum enumValue) { + if (initializedFlag == false) initialize(); + + const WuQMacroShortCutKeyEnum* enumInstance = findData(enumValue); + return enumInstance->name; +} + +/** + * Get an enumerated value corresponding to its name. + * @param name + * Name of enumerated value. + * @param isValidOut + * If not NULL, it is set indicating that a + * enum value exists for the input name. + * @return + * Enumerated value. + */ +WuQMacroShortCutKeyEnum::Enum +WuQMacroShortCutKeyEnum::fromName(const AString& name, bool* isValidOut) +{ + if (initializedFlag == false) initialize(); + + bool validFlag = false; + Enum enumValue = WuQMacroShortCutKeyEnum::enumData[0].enumValue; + + for (std::vector::iterator iter = enumData.begin(); + iter != enumData.end(); + iter++) { + const WuQMacroShortCutKeyEnum& d = *iter; + if (d.name == name) { + enumValue = d.enumValue; + validFlag = true; + break; + } + } + + if (isValidOut != 0) { + *isValidOut = validFlag; + } + else if (validFlag == false) { + CaretAssertMessage(0, AString("Name " + name + "failed to match enumerated value for type WuQMacroShortCutKeyEnum")); + } + return enumValue; +} + +/** + * Get a GUI string representation of the enumerated type. + * @param enumValue + * Enumerated value. + * @return + * String representing enumerated value. + */ +AString +WuQMacroShortCutKeyEnum::toGuiName(Enum enumValue) { + if (initializedFlag == false) initialize(); + + const WuQMacroShortCutKeyEnum* enumInstance = findData(enumValue); + return enumInstance->guiName; +} + +/** + * Get an enumerated value corresponding to its GUI name. + * @param s + * Name of enumerated value. + * @param isValidOut + * If not NULL, it is set indicating that a + * enum value exists for the input name. + * @return + * Enumerated value. + */ +WuQMacroShortCutKeyEnum::Enum +WuQMacroShortCutKeyEnum::fromGuiName(const AString& guiName, bool* isValidOut) +{ + if (initializedFlag == false) initialize(); + + bool validFlag = false; + Enum enumValue = WuQMacroShortCutKeyEnum::enumData[0].enumValue; + + for (std::vector::iterator iter = enumData.begin(); + iter != enumData.end(); + iter++) { + const WuQMacroShortCutKeyEnum& d = *iter; + if (d.guiName == guiName) { + enumValue = d.enumValue; + validFlag = true; + break; + } + } + + if (isValidOut != 0) { + *isValidOut = validFlag; + } + else if (validFlag == false) { + CaretAssertMessage(0, AString("guiName " + guiName + "failed to match enumerated value for type WuQMacroShortCutKeyEnum")); + } + return enumValue; +} + +/** + * Get the integer code for a data type. + * + * @return + * Integer code for data type. + */ +int32_t +WuQMacroShortCutKeyEnum::toIntegerCode(Enum enumValue) +{ + if (initializedFlag == false) initialize(); + const WuQMacroShortCutKeyEnum* enumInstance = findData(enumValue); + return enumInstance->integerCode; +} + +/** + * Find the data type corresponding to an integer code. + * + * @param integerCode + * Integer code for enum. + * @param isValidOut + * If not NULL, on exit isValidOut will indicate if + * integer code is valid. + * @return + * Enum for integer code. + */ +WuQMacroShortCutKeyEnum::Enum +WuQMacroShortCutKeyEnum::fromIntegerCode(const int32_t integerCode, bool* isValidOut) +{ + if (initializedFlag == false) initialize(); + + bool validFlag = false; + Enum enumValue = WuQMacroShortCutKeyEnum::enumData[0].enumValue; + + for (std::vector::iterator iter = enumData.begin(); + iter != enumData.end(); + iter++) { + const WuQMacroShortCutKeyEnum& enumInstance = *iter; + if (enumInstance.integerCode == integerCode) { + enumValue = enumInstance.enumValue; + validFlag = true; + break; + } + } + + if (isValidOut != 0) { + *isValidOut = validFlag; + } + else if (validFlag == false) { + CaretAssertMessage(0, AString("Integer code " + AString::number(integerCode) + "failed to match enumerated value for type WuQMacroShortCutKeyEnum")); + } + return enumValue; +} + +/** + * Get all of the enumerated type values. The values can be used + * as parameters to toXXX() methods to get associated metadata. + * + * @param allEnums + * A vector that is OUTPUT containing all of the enumerated values. + */ +void +WuQMacroShortCutKeyEnum::getAllEnums(std::vector& allEnums) +{ + if (initializedFlag == false) initialize(); + + allEnums.clear(); + + for (std::vector::iterator iter = enumData.begin(); + iter != enumData.end(); + iter++) { + allEnums.push_back(iter->enumValue); + } +} + +/** + * Get all of the names of the enumerated type values. + * + * @param allNames + * A vector that is OUTPUT containing all of the names of the enumerated values. + * @param isSorted + * If true, the names are sorted in alphabetical order. + */ +void +WuQMacroShortCutKeyEnum::getAllNames(std::vector& allNames, const bool isSorted) +{ + if (initializedFlag == false) initialize(); + + allNames.clear(); + + for (std::vector::iterator iter = enumData.begin(); + iter != enumData.end(); + iter++) { + allNames.push_back(WuQMacroShortCutKeyEnum::toName(iter->enumValue)); + } + + if (isSorted) { + std::sort(allNames.begin(), allNames.end()); + } +} + +/** + * Get all of the GUI names of the enumerated type values. + * + * @param allNames + * A vector that is OUTPUT containing all of the GUI names of the enumerated values. + * @param isSorted + * If true, the names are sorted in alphabetical order. + */ +void +WuQMacroShortCutKeyEnum::getAllGuiNames(std::vector& allGuiNames, const bool isSorted) +{ + if (initializedFlag == false) initialize(); + + allGuiNames.clear(); + + for (std::vector::iterator iter = enumData.begin(); + iter != enumData.end(); + iter++) { + allGuiNames.push_back(WuQMacroShortCutKeyEnum::toGuiName(iter->enumValue)); + } + + if (isSorted) { + std::sort(allGuiNames.begin(), allGuiNames.end()); + } +} + +/** + * Find the data type corresponding to a Qt::Key enum value. + * If not valid, the none key (Key_None) is returned. + * + * @param qtKeyEnum + * The Qt::Key enum value + * @return + * Enum for integer code. + */ +WuQMacroShortCutKeyEnum::Enum +WuQMacroShortCutKeyEnum::fromQtKeyEnum(const int32_t qtKeyEnum) +{ + if (initializedFlag == false) initialize(); + + int32_t shortCutKeyInteger = -1; + if ((qtKeyEnum >= Qt::Key_A) + && (qtKeyEnum <= Qt::Key_Z)) { + shortCutKeyInteger = (WuQMacroShortCutKeyEnum::toIntegerCode(Key_A) + + (qtKeyEnum - (int)Qt::Key_A)); + } + else if ((qtKeyEnum >= Qt::Key_0) + && (qtKeyEnum <= Qt::Key_9)) { + shortCutKeyInteger = (WuQMacroShortCutKeyEnum::toIntegerCode(Key_0) + + (qtKeyEnum - (int)Qt::Key_0)); + } + else { + shortCutKeyInteger = WuQMacroShortCutKeyEnum::toIntegerCode(Key_None); + } + + bool validFlag = false; + const Enum enumValue = WuQMacroShortCutKeyEnum::fromIntegerCode(shortCutKeyInteger, + &validFlag); + CaretAssert(validFlag); + + return enumValue; +} diff --git a/src/Common/WuQMacroShortCutKeyEnum.h b/src/Common/WuQMacroShortCutKeyEnum.h new file mode 100644 index 000000000..47e65e18a --- /dev/null +++ b/src/Common/WuQMacroShortCutKeyEnum.h @@ -0,0 +1,176 @@ +#ifndef __WU_Q_MACRO_SHORT_CUT_KEY_ENUM_H__ +#define __WU_Q_MACRO_SHORT_CUT_KEY_ENUM_H__ + +/*LICENSE_START*/ +/* + * Copyright (C) 2018 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + + +#include +#include +#include "AString.h" + +namespace caret { + +class WuQMacroShortCutKeyEnum { + +public: + /** + * Enumerated values. + */ + enum Enum { + /** No Key */ + Key_None, + /** */ + Key_A, + /** */ + Key_B, + /** */ + Key_C, + /** */ + Key_D, + /** */ + Key_E, + /** */ + Key_F, + /** */ + Key_G, + /** */ + Key_H, + /** */ + Key_I, + /** */ + Key_J, + /** */ + Key_K, + /** */ + Key_L, + /** */ + Key_M, + /** */ + Key_N, + /** */ + Key_O, + /** */ + Key_P, + /** */ + Key_Q, + /** */ + Key_R, + /** */ + Key_S, + /** */ + Key_T, + /** */ + Key_U, + /** */ + Key_V, + /** */ + Key_W, + /** */ + Key_X, + /** */ + Key_Y, + /** */ + Key_Z, + /** */ + Key_0, + /** */ + Key_1, + /** */ + Key_2, + /** */ + Key_3, + /** */ + Key_4, + /** */ + Key_5, + /** */ + Key_6, + /** */ + Key_7, + /** */ + Key_8, + /** */ + Key_9 + }; + + + ~WuQMacroShortCutKeyEnum(); + + static AString toName(Enum enumValue); + + static Enum fromName(const AString& name, bool* isValidOut); + + static AString toGuiName(Enum enumValue); + + static Enum fromGuiName(const AString& guiName, bool* isValidOut); + + static int32_t toIntegerCode(Enum enumValue); + + static Enum fromIntegerCode(const int32_t integerCode, bool* isValidOut); + + static void getAllEnums(std::vector& allEnums); + + static void getAllNames(std::vector& allNames, const bool isSorted); + + static void getAllGuiNames(std::vector& allGuiNames, const bool isSorted); + + static Enum fromQtKeyEnum(const int32_t qtKeyEnum); + +private: + WuQMacroShortCutKeyEnum(const Enum enumValue, + const AString& name, + const AString& guiName); + + static const WuQMacroShortCutKeyEnum* findData(const Enum enumValue); + + /** Holds all instance of enum values and associated metadata */ + static std::vector enumData; + + /** Initialize instances that contain the enum values and metadata */ + static void initialize(); + + /** Indicates instance of enum values and metadata have been initialized */ + static bool initializedFlag; + + /** Auto generated integer codes */ + static int32_t integerCodeCounter; + + /** The enumerated type value for an instance */ + Enum enumValue; + + /** The integer code associated with an enumerated value */ + int32_t integerCode; + + /** The name, a text string that is identical to the enumerated value */ + AString name; + + /** A user-friendly name that is displayed in the GUI */ + AString guiName; +}; + +#ifdef __WU_Q_MACRO_SHORT_CUT_KEY_ENUM_DECLARE__ +std::vector WuQMacroShortCutKeyEnum::enumData; +bool WuQMacroShortCutKeyEnum::initializedFlag = false; +int32_t WuQMacroShortCutKeyEnum::integerCodeCounter = 0; +#endif // __WU_Q_MACRO_SHORT_CUT_KEY_ENUM_DECLARE__ + +} // namespace +#endif //__WU_Q_MACRO_SHORT_CUT_KEY_ENUM_H__ diff --git a/src/Common/createMacroShortCutKeyEnum.sh b/src/Common/createMacroShortCutKeyEnum.sh new file mode 100755 index 000000000..6eac9380d --- /dev/null +++ b/src/Common/createMacroShortCutKeyEnum.sh @@ -0,0 +1,42 @@ +#!/bin/sh + +/mnt/myelin/distribution/caret7_distribution/workbench/bin_macosx64/wb_command \ + -class-create-enum WuQMacroShortCutKeyEnum 37 true \ + Key_None \ + Key_A \ + Key_B \ + Key_C \ + Key_D \ + Key_E \ + Key_F \ + Key_G \ + Key_H \ + Key_I \ + Key_J \ + Key_K \ + Key_L \ + Key_M \ + Key_N \ + Key_O \ + Key_P \ + Key_Q \ + Key_R \ + Key_S \ + Key_T \ + Key_U \ + Key_V \ + Key_W \ + Key_X \ + Key_Y \ + Key_Z \ + Key_0 \ + Key_1 \ + Key_2 \ + Key_3 \ + Key_4 \ + Key_5 \ + Key_6 \ + Key_7 \ + Key_8 \ + Key_9 + diff --git a/src/GuiQt/BrainBrowserWindow.cxx b/src/GuiQt/BrainBrowserWindow.cxx index 1ed3fafaf..f667a3ead 100644 --- a/src/GuiQt/BrainBrowserWindow.cxx +++ b/src/GuiQt/BrainBrowserWindow.cxx @@ -288,6 +288,11 @@ m_browserWindowIndex(browserWindowIndex) gapsAndMargins->setSurfaceMontageVerticalGapForWindow(m_browserWindowIndex, 0.0f); gapsAndMargins->setVolumeMontageHorizontalGapForWindow(m_browserWindowIndex, 0.0f); gapsAndMargins->setVolumeMontageVerticalGapForWindow(m_browserWindowIndex, 0.0f); + + /* + * Allows keyboard events + */ + setFocusPolicy(Qt::StrongFocus); } /** @@ -779,6 +784,11 @@ BrainBrowserWindow::keyPressEvent(QKeyEvent* event) } } + if ( ! keyEventWasProcessed) { + keyEventWasProcessed = WuQMacroManager::instance()->runMacroWithShortCutKeyEvent(this, + event); + } + /* * According to the documentation, if the key event was not acted upon, * pass it on the base class implementation. diff --git a/src/GuiQt/BrainOpenGLWidget.cxx b/src/GuiQt/BrainOpenGLWidget.cxx index e97b78672..8f9b6a8aa 100644 --- a/src/GuiQt/BrainOpenGLWidget.cxx +++ b/src/GuiQt/BrainOpenGLWidget.cxx @@ -851,11 +851,19 @@ BrainOpenGLWidget::keyPressEvent(QKeyEvent* e) m_newKeyPressStartedFlag, shiftKeyDownFlag); - this->selectedUserInputProcessor->keyPressEvent(keyEvent); + const bool keyWasProcessedFlag = this->selectedUserInputProcessor->keyPressEvent(keyEvent); e->accept(); m_newKeyPressStartedFlag = false; + + if ( ! keyWasProcessedFlag) { +#ifdef WORKBENCH_USE_QT5_QOPENGL_WIDGET + QOpenGLWidget::keyPressEvent(e); +#else + QGLWidget::keyPressEvent(e); +#endif + } } /** diff --git a/src/GuiQt/CMakeLists.txt b/src/GuiQt/CMakeLists.txt index 1378a1ae9..4397f3c2a 100644 --- a/src/GuiQt/CMakeLists.txt +++ b/src/GuiQt/CMakeLists.txt @@ -210,6 +210,7 @@ ELSE() WuQMacroExecutor.h WuQMacroManager.h WuQMacroMenu.h + WuQMacroShortCutKeyComboBox.h WuQMacroSignalEmitter.h WuQMacroSignalWatcher.h WuQMessageBox.h @@ -464,9 +465,11 @@ WuQMacroCreateDialog.h WuQMacroDialog.h WuQMacroEditorDialog.h WuQMacroExecutor.h +WuQMacroExecutorOptions.h WuQMacroManager.h WuQMacroMenu.h WuQMacroMouseEventWidgetInterface.h +WuQMacroShortCutKeyComboBox.h WuQMacroSignalEmitter.h WuQMacroSignalWatcher.h WuQMessageBox.h @@ -707,8 +710,10 @@ WuQMacroCreateDialog.cxx WuQMacroDialog.cxx WuQMacroEditorDialog.cxx WuQMacroExecutor.cxx +WuQMacroExecutorOptions.cxx WuQMacroManager.cxx WuQMacroMenu.cxx +WuQMacroShortCutKeyComboBox.cxx WuQMacroSignalEmitter.cxx WuQMacroSignalWatcher.cxx WuQMessageBox.cxx diff --git a/src/GuiQt/UserInputModeAbstract.h b/src/GuiQt/UserInputModeAbstract.h index 967be3440..41796be61 100644 --- a/src/GuiQt/UserInputModeAbstract.h +++ b/src/GuiQt/UserInputModeAbstract.h @@ -96,8 +96,12 @@ namespace caret { * * @param keyEvent * Key event information. + * @return + * True if the input process recognized the key event + * and the key event SHOULD NOT be propagated to parent + * widgets */ - virtual void keyPressEvent(const KeyEvent& /*keyEvent*/) { } + virtual bool keyPressEvent(const KeyEvent& /*keyEvent*/) { return false; } /** * Process a mouse left double-click event. diff --git a/src/GuiQt/UserInputModeAnnotations.cxx b/src/GuiQt/UserInputModeAnnotations.cxx index 607ced486..08c83d03c 100644 --- a/src/GuiQt/UserInputModeAnnotations.cxx +++ b/src/GuiQt/UserInputModeAnnotations.cxx @@ -337,10 +337,16 @@ UserInputModeAnnotations::deleteSelectedAnnotations() * * @param keyEvent * Key event information. + * @return + * True if the input process recognized the key event + * and the key event SHOULD NOT be propagated to parent + * widgets */ -void +bool UserInputModeAnnotations::keyPressEvent(const KeyEvent& keyEvent) { + bool keyWasProcessedFlag(false); + const int32_t keyCode = keyEvent.getKeyCode(); switch (keyCode) { case Qt::Key_Backspace: @@ -357,6 +363,7 @@ UserInputModeAnnotations::keyPressEvent(const KeyEvent& keyEvent) break; case MODE_SELECT: deleteSelectedAnnotations(); + keyWasProcessedFlag = true; break; case MODE_SET_COORDINATE_ONE: break; @@ -391,6 +398,7 @@ UserInputModeAnnotations::keyPressEvent(const KeyEvent& keyEvent) if (selectModeFlag) { setMode(MODE_SELECT); EventManager::get()->sendEvent(EventGraphicsUpdateAllWindows().getPointer()); + keyWasProcessedFlag = true; } } break; @@ -429,6 +437,8 @@ UserInputModeAnnotations::keyPressEvent(const KeyEvent& keyEvent) } if (changeCoordFlag) { + keyWasProcessedFlag = true; + float distanceX = 1.0; float distanceY = 1.0; if (moveOnePixelFlag) { @@ -548,6 +558,8 @@ UserInputModeAnnotations::keyPressEvent(const KeyEvent& keyEvent) } break; } + + return keyWasProcessedFlag; } /** diff --git a/src/GuiQt/UserInputModeAnnotations.h b/src/GuiQt/UserInputModeAnnotations.h index e4b673703..2aa73142a 100644 --- a/src/GuiQt/UserInputModeAnnotations.h +++ b/src/GuiQt/UserInputModeAnnotations.h @@ -80,7 +80,7 @@ namespace caret { Mode getMode() const; - virtual void keyPressEvent(const KeyEvent& /*keyEvent*/); + virtual bool keyPressEvent(const KeyEvent& /*keyEvent*/) override; virtual void mouseLeftDoubleClick(const MouseEvent& mouseEvent); diff --git a/src/GuiQt/WuQMacroAttributesDialog.cxx b/src/GuiQt/WuQMacroAttributesDialog.cxx index 2a57ec146..1199ed76b 100644 --- a/src/GuiQt/WuQMacroAttributesDialog.cxx +++ b/src/GuiQt/WuQMacroAttributesDialog.cxx @@ -37,6 +37,7 @@ #include "WuQMacro.h" #include "WuQMacroGroup.h" #include "WuQMacroManager.h" +#include "WuQMacroShortCutKeyComboBox.h" using namespace caret; @@ -67,33 +68,29 @@ m_macroWasModifiedFlag(false) setWindowTitle("Macro Attributes"); QLabel* nameLabel = new QLabel("Macro name:"); - QLabel* functionKeyLabel = new QLabel("Function Key:"); + QLabel* shortCutKeyLabel = new QLabel("Short Cut Key:"); + QLabel* shortCutKeyMaskLabel = new QLabel(WuQMacroManager::getShortCutKeysMask()); QLabel* descriptionLabel = new QLabel("Description:"); m_macroNameLineEdit = new QLineEdit(); m_macroNameLineEdit->setText(m_macro->getName()); - m_macroFunctionKeyLineEdit = new QLineEdit(); - m_macroFunctionKeyLineEdit->setFixedWidth(40); - m_macroFunctionKeyLineEdit->setText(m_macro->getFunctionKey()); + m_macroShortCutKeyComboBox = new WuQMacroShortCutKeyComboBox(this); + m_macroShortCutKeyComboBox->setSelectedShortCutKey(m_macro->getShortCutKey()); m_macroDescriptionTextEdit = new QPlainTextEdit(); m_macroDescriptionTextEdit->setFixedHeight(100); m_macroDescriptionTextEdit->setPlainText(m_macro->getDescription()); - /* - * DISABLE FUNCTION KEY UNTIL IMPLEMENTED - */ - m_macroFunctionKeyLineEdit->setEnabled(false); - QGridLayout* gridLayout = new QGridLayout(); int row = 0; gridLayout->addWidget(nameLabel, row, 0); - gridLayout->addWidget(m_macroNameLineEdit, row, 1); + gridLayout->addWidget(m_macroNameLineEdit, row, 1, 1, 2); row++; - gridLayout->addWidget(functionKeyLabel, row, 0); - gridLayout->addWidget(m_macroFunctionKeyLineEdit, row, 1); + gridLayout->addWidget(shortCutKeyLabel, row, 0); + gridLayout->addWidget(shortCutKeyMaskLabel, row, 1); + gridLayout->addWidget(m_macroShortCutKeyComboBox->getWidget(), row, 2, Qt::AlignLeft); row++; gridLayout->addWidget(descriptionLabel, row, 0); - gridLayout->addWidget(m_macroDescriptionTextEdit, row, 1); + gridLayout->addWidget(m_macroDescriptionTextEdit, row, 1, 1, 2); row++; m_dialogButtonBox = new QDialogButtonBox(QDialogButtonBox::Ok @@ -141,9 +138,9 @@ WuQMacroAttributesDialog::done(int r) m_macroWasModifiedFlag = true; } - const QString newFunctionKey = m_macroFunctionKeyLineEdit->text().trimmed(); - if (m_macro->getFunctionKey() != newFunctionKey) { - m_macro->setFunctionKey(newFunctionKey); + const WuQMacroShortCutKeyEnum::Enum newShortCutKey = m_macroShortCutKeyComboBox->getSelectedShortCutKey(); + if (m_macro->getShortCutKey() != newShortCutKey) { + m_macro->setShortCutKey(newShortCutKey); m_macroWasModifiedFlag = true; } diff --git a/src/GuiQt/WuQMacroAttributesDialog.h b/src/GuiQt/WuQMacroAttributesDialog.h index ae09d05e7..d9368428f 100644 --- a/src/GuiQt/WuQMacroAttributesDialog.h +++ b/src/GuiQt/WuQMacroAttributesDialog.h @@ -36,6 +36,7 @@ namespace caret { class WuQMacro; class WuQMacroGroup; + class WuQMacroShortCutKeyComboBox; class WuQMacroAttributesDialog : public QDialog { @@ -65,7 +66,7 @@ namespace caret { QLineEdit* m_macroNameLineEdit; - QLineEdit* m_macroFunctionKeyLineEdit; + WuQMacroShortCutKeyComboBox* m_macroShortCutKeyComboBox; QPlainTextEdit* m_macroDescriptionTextEdit; diff --git a/src/GuiQt/WuQMacroCreateDialog.cxx b/src/GuiQt/WuQMacroCreateDialog.cxx index 3daac49c8..ff8deeea2 100644 --- a/src/GuiQt/WuQMacroCreateDialog.cxx +++ b/src/GuiQt/WuQMacroCreateDialog.cxx @@ -37,6 +37,7 @@ #include "WuQMacro.h" #include "WuQMacroGroup.h" #include "WuQMacroManager.h" +#include "WuQMacroShortCutKeyComboBox.h" using namespace caret; @@ -62,22 +63,17 @@ WuQMacroCreateDialog::WuQMacroCreateDialog(QWidget* parent) m_macroGroups = WuQMacroManager::instance()->getMacroGroups(); QLabel* nameLabel = new QLabel("Macro name:"); - QLabel* functionKeyLabel = new QLabel("Function Key"); + QLabel* shortCutKeyLabel = new QLabel("Short Cut Key:"); + QLabel* shortCutKeyMaskLabel = new QLabel(WuQMacroManager::getShortCutKeysMask()); QLabel* descriptionLabel = new QLabel("Description:"); QLabel* macroGroupLabel = new QLabel("Store macro in:"); m_macroNameLineEdit = new QLineEdit(); m_macroNameLineEdit->setText(getDefaultMacroName()); - m_macroFunctionKeyLineEdit = new QLineEdit(); - m_macroFunctionKeyLineEdit->setFixedWidth(40); + m_macroShortCutKeyComboBox = new WuQMacroShortCutKeyComboBox(this); m_macroDescriptionTextEdit = new QPlainTextEdit(); m_macroDescriptionTextEdit->setFixedHeight(100); - /* - * DISABLE FUNCTION KEY UNTIL IMPLEMENTED - */ - m_macroFunctionKeyLineEdit->setEnabled(false); - m_macroGroupComboBox = new QComboBox(); for (auto mg : m_macroGroups) { m_macroGroupComboBox->addItem(mg->getName()); @@ -86,16 +82,17 @@ WuQMacroCreateDialog::WuQMacroCreateDialog(QWidget* parent) QGridLayout* gridLayout = new QGridLayout(); int row = 0; gridLayout->addWidget(nameLabel, row, 0); - gridLayout->addWidget(m_macroNameLineEdit, row, 1); + gridLayout->addWidget(m_macroNameLineEdit, row, 1, 1, 2); row++; - gridLayout->addWidget(functionKeyLabel, row, 0); - gridLayout->addWidget(m_macroFunctionKeyLineEdit, row, 1); + gridLayout->addWidget(shortCutKeyLabel, row, 0); + gridLayout->addWidget(shortCutKeyMaskLabel, row, 1); + gridLayout->addWidget(m_macroShortCutKeyComboBox->getWidget(), row, 2, Qt::AlignLeft); row++; gridLayout->addWidget(descriptionLabel, row, 0); - gridLayout->addWidget(m_macroDescriptionTextEdit, row, 1); + gridLayout->addWidget(m_macroDescriptionTextEdit, row, 1, 1, 2); row++; gridLayout->addWidget(macroGroupLabel, row, 0); - gridLayout->addWidget(m_macroGroupComboBox, row, 1); + gridLayout->addWidget(m_macroGroupComboBox, row, 1, 1, 2); row++; m_dialogButtonBox = new QDialogButtonBox(QDialogButtonBox::Ok @@ -173,7 +170,7 @@ WuQMacroCreateDialog::done(int r) m_macro = new WuQMacro(); m_macro->setName(name); - m_macro->setFunctionKey(m_macroFunctionKeyLineEdit->text().trimmed()); + m_macro->setShortCutKey(m_macroShortCutKeyComboBox->getSelectedShortCutKey()); m_macro->setDescription(m_macroDescriptionTextEdit->toPlainText()); const int32_t groupIndex = m_macroGroupComboBox->currentIndex(); @@ -196,3 +193,22 @@ WuQMacroCreateDialog::getNewMacro() const return m_macro; } +/** + * @return Create a combo box with valid function keys. + * Method is static so that other classes may use it. + */ +QComboBox* +WuQMacroCreateDialog::createFunctionKeyComboBox() +{ + QComboBox* comboBox = new QComboBox(); + comboBox->addItem(" "); + + const char firstChar = 'A'; + const char lastChar = 'Z'; + for (char ch = firstChar; ch <= lastChar; ch++) { + comboBox->addItem(QString(ch)); + } + + return comboBox; +} + diff --git a/src/GuiQt/WuQMacroCreateDialog.h b/src/GuiQt/WuQMacroCreateDialog.h index 969346df6..2a9d1b6cb 100644 --- a/src/GuiQt/WuQMacroCreateDialog.h +++ b/src/GuiQt/WuQMacroCreateDialog.h @@ -37,7 +37,8 @@ namespace caret { class WuQMacro; class WuQMacroGroup; - + class WuQMacroShortCutKeyComboBox; + class WuQMacroCreateDialog : public QDialog { Q_OBJECT @@ -55,6 +56,8 @@ namespace caret { // ADD_NEW_METHODS_HERE + static QComboBox* createFunctionKeyComboBox(); + public slots: virtual void done(int r) override; @@ -67,8 +70,8 @@ namespace caret { QLineEdit* m_macroNameLineEdit; - QLineEdit* m_macroFunctionKeyLineEdit; - + WuQMacroShortCutKeyComboBox* m_macroShortCutKeyComboBox; + QPlainTextEdit* m_macroDescriptionTextEdit; QDialogButtonBox* m_dialogButtonBox; diff --git a/src/GuiQt/WuQMacroDialog.cxx b/src/GuiQt/WuQMacroDialog.cxx index df4ab6e4f..8cc1673b2 100644 --- a/src/GuiQt/WuQMacroDialog.cxx +++ b/src/GuiQt/WuQMacroDialog.cxx @@ -193,10 +193,16 @@ WuQMacroDialog::createRunOptionsWidget() m_runOptionLoopCheckBox = new QCheckBox("Loop"); m_runOptionLoopCheckBox->setChecked(false); m_runOptionLoopCheckBox->setEnabled(false); + QObject::connect(m_runOptionLoopCheckBox, &QCheckBox::clicked, + this, &WuQMacroDialog::runOptionLoopCheckBoxClicked); + m_runOptionMoveMouseCheckBox = new QCheckBox("Move mouse to to highlight controls"); m_runOptionMoveMouseCheckBox->setChecked(true); m_runOptionMoveMouseCheckBox->setToolTip("As macro runs, the mouse is moved to\n" "highlight user-interface controls"); + QObject::connect(m_runOptionMoveMouseCheckBox, &QCheckBox::clicked, + this, &WuQMacroDialog::runOptionMoveMouseCheckBoxClicked); + QLabel* runOptionsDelayLabel = new QLabel("Delay (seconds) between commands"); m_runOptionDelayBetweenCommandsSpinBox = new QDoubleSpinBox(); m_runOptionDelayBetweenCommandsSpinBox->setMinimum(0.0); @@ -206,6 +212,9 @@ WuQMacroDialog::createRunOptionsWidget() m_runOptionDelayBetweenCommandsSpinBox->setValue(1.0); m_runOptionDelayBetweenCommandsSpinBox->setToolTip("Pause for this amount of time"); m_runOptionDelayBetweenCommandsSpinBox->setFixedWidth(80); + QObject::connect(m_runOptionDelayBetweenCommandsSpinBox, static_cast(&QDoubleSpinBox::valueChanged), + this, &WuQMacroDialog::runOptionDelaySpinBoxValueChanged); + QGridLayout* runOptionsLayout = new QGridLayout(widget); runOptionsLayout->setColumnMinimumWidth(0, 10); runOptionsLayout->addWidget(runOptionsLabel, 0, 0, 1, 3); @@ -217,6 +226,48 @@ WuQMacroDialog::createRunOptionsWidget() return widget; } +/** + * Called when run options delay between commands spin box value changed + * + * @param value + * New value. + */ +void +WuQMacroDialog::runOptionDelaySpinBoxValueChanged(float value) +{ + WuQMacroExecutorOptions* options = WuQMacroManager::instance()->getExecutorOptions(); + CaretAssert(options); + options->setSecondsDelayBetweenCommands(value); +} + +/** + * Called when run options move mouse checkbox is changed + * + * @param value + * New value. + */ +void +WuQMacroDialog::runOptionMoveMouseCheckBoxClicked(bool checked) +{ + WuQMacroExecutorOptions* options = WuQMacroManager::instance()->getExecutorOptions(); + CaretAssert(options); + options->setShowMouseMovement(checked); +} + +/** + * Called when run options loop checkbox is changed + * + * @param value + * New value. + */ +void +WuQMacroDialog::runOptionLoopCheckBoxClicked(bool checked) +{ + WuQMacroExecutorOptions* options = WuQMacroManager::instance()->getExecutorOptions(); + CaretAssert(options); + options->setLooping(checked); +} + /** * Called when a button in dialog is clicked * @@ -257,6 +308,12 @@ WuQMacroDialog::updateDialogContents() m_macroGroupComboBox->setCurrentIndex(selectedIndex); } + const WuQMacroExecutorOptions* runOptions = WuQMacroManager::instance()->getExecutorOptions(); + CaretAssert(runOptions); + QSignalBlocker delaySpinBoxBlocker(m_runOptionDelayBetweenCommandsSpinBox); + m_runOptionDelayBetweenCommandsSpinBox->setValue(runOptions->getSecondsDelayBetweenCommands()); + m_runOptionMoveMouseCheckBox->setChecked(runOptions->isShowMouseMovement()); + m_runOptionLoopCheckBox->setChecked(runOptions->isLooping()); macroGroupBoxActivated(selectedIndex); } @@ -377,11 +434,7 @@ WuQMacroDialog::runSelectedMacro() return; } - WuQMacroExecutor::RunOptions runOptions(m_runOptionDelayBetweenCommandsSpinBox->value(), - m_runOptionMoveMouseCheckBox->isChecked(), - m_runOptionLoopCheckBox->isChecked()); WuQMacroManager::instance()->runMacro(parentWidget(), - runOptions, macro); } } diff --git a/src/GuiQt/WuQMacroDialog.h b/src/GuiQt/WuQMacroDialog.h index 327f3d2ce..d4382f063 100644 --- a/src/GuiQt/WuQMacroDialog.h +++ b/src/GuiQt/WuQMacroDialog.h @@ -76,6 +76,12 @@ namespace caret { void exportButtonClicked(); + void runOptionDelaySpinBoxValueChanged(float); + + void runOptionMoveMouseCheckBoxClicked(bool); + + void runOptionLoopCheckBoxClicked(bool); + private: QWidget* createMacroButtonsWidget(); diff --git a/src/GuiQt/WuQMacroEditorDialog.cxx b/src/GuiQt/WuQMacroEditorDialog.cxx index 5b23da031..fee02d52b 100644 --- a/src/GuiQt/WuQMacroEditorDialog.cxx +++ b/src/GuiQt/WuQMacroEditorDialog.cxx @@ -40,6 +40,7 @@ #include "WuQMacroCommand.h" #include "WuQMacroGroup.h" #include "WuQMacroManager.h" +#include "WuQMacroShortCutKeyComboBox.h" using namespace caret; @@ -70,39 +71,35 @@ m_macroWasModifiedFlag(false) setWindowTitle("Macro Editor"); QLabel* nameLabel = new QLabel("Macro name:"); - QLabel* functionKeyLabel = new QLabel("Function Key:"); + QLabel* shortCutKeyLabel = new QLabel("Short Cut Key:"); + QLabel* shortCutKeyMaskLabel = new QLabel(WuQMacroManager::getShortCutKeysMask()); QLabel* descriptionLabel = new QLabel("Description:"); QLabel* commandsLabel = new QLabel("Commands:"); m_macroNameLineEdit = new QLineEdit(); m_macroNameLineEdit->setText(m_macro->getName()); - m_macroFunctionKeyLineEdit = new QLineEdit(); - m_macroFunctionKeyLineEdit->setFixedWidth(40); - m_macroFunctionKeyLineEdit->setText(m_macro->getFunctionKey()); + m_macroShortCutKeyComboBox = new WuQMacroShortCutKeyComboBox(this); + m_macroShortCutKeyComboBox->setSelectedShortCutKey(m_macro->getShortCutKey()); m_macroDescriptionTextEdit = new QPlainTextEdit(); m_macroDescriptionTextEdit->setFixedHeight(60); m_macroDescriptionTextEdit->setPlainText(m_macro->getDescription()); - /* - * DISABLE FUNCTION KEY UNTIL IMPLEMENTED - */ - m_macroFunctionKeyLineEdit->setEnabled(false); - m_tableWidget = createTableWidget(); QGridLayout* gridLayout = new QGridLayout(); int row = 0; gridLayout->addWidget(nameLabel, row, 0); - gridLayout->addWidget(m_macroNameLineEdit, row, 1); + gridLayout->addWidget(m_macroNameLineEdit, row, 1, 1, 2); row++; - gridLayout->addWidget(functionKeyLabel, row, 0); - gridLayout->addWidget(m_macroFunctionKeyLineEdit, row, 1); + gridLayout->addWidget(shortCutKeyLabel, row, 0); + gridLayout->addWidget(shortCutKeyMaskLabel, row, 1); + gridLayout->addWidget(m_macroShortCutKeyComboBox->getWidget(), row, 2, Qt::AlignLeft); row++; gridLayout->addWidget(descriptionLabel, row, 0); - gridLayout->addWidget(m_macroDescriptionTextEdit, row, 1); + gridLayout->addWidget(m_macroDescriptionTextEdit, row, 1, 1, 2); row++; gridLayout->addWidget(commandsLabel, row, 0); - gridLayout->addWidget(m_tableWidget, row, 1); + gridLayout->addWidget(m_tableWidget, row, 1, 1, 2); row++; @@ -251,12 +248,12 @@ WuQMacroEditorDialog::done(int r) m_macroWasModifiedFlag = true; } - const QString newFunctionKey = m_macroFunctionKeyLineEdit->text().trimmed(); - if (m_macro->getFunctionKey() != newFunctionKey) { - m_macro->setFunctionKey(newFunctionKey); + const WuQMacroShortCutKeyEnum::Enum newShortCutKey = m_macroShortCutKeyComboBox->getSelectedShortCutKey(); + if (m_macro->getShortCutKey() != newShortCutKey) { + m_macro->setShortCutKey(newShortCutKey); m_macroWasModifiedFlag = true; } - + const QString newDescription = m_macroDescriptionTextEdit->toPlainText().trimmed(); if (m_macro->getDescription() != newDescription) { m_macro->setDescription(newDescription); diff --git a/src/GuiQt/WuQMacroEditorDialog.h b/src/GuiQt/WuQMacroEditorDialog.h index bbad9bf88..320e2af6f 100644 --- a/src/GuiQt/WuQMacroEditorDialog.h +++ b/src/GuiQt/WuQMacroEditorDialog.h @@ -37,7 +37,8 @@ namespace caret { class WuQMacro; class WuQMacroGroup; - + class WuQMacroShortCutKeyComboBox; + class WuQMacroEditorDialog : public QDialog { Q_OBJECT @@ -81,7 +82,7 @@ namespace caret { QLineEdit* m_macroNameLineEdit; - QLineEdit* m_macroFunctionKeyLineEdit; + WuQMacroShortCutKeyComboBox* m_macroShortCutKeyComboBox; QPlainTextEdit* m_macroDescriptionTextEdit; diff --git a/src/GuiQt/WuQMacroExecutor.cxx b/src/GuiQt/WuQMacroExecutor.cxx index 0504d9cdd..30acd2b29 100644 --- a/src/GuiQt/WuQMacroExecutor.cxx +++ b/src/GuiQt/WuQMacroExecutor.cxx @@ -174,7 +174,7 @@ WuQMacroExecutor::moveMouseToWidgetImplementation(QObject* moveToObject, const QRect* objectRect, const bool highlightFlag) const { - if ( ! m_runOptions.m_showMouseMovementFlag) { + if ( ! m_runOptions.isShowMouseMovement()) { return; } CaretAssert(moveToObject); @@ -262,10 +262,10 @@ WuQMacroExecutor::findObjectByName(const QString& objectName) const * Macro that is run * @param topLevelObject * Top level object for finding children objects - * @param + * @param otherObjectParents * Additional objects that are searched for objects contained * in the macro commands - * @param options + * @param executorOptions * Executor options * @param errorMessageOut * Output containing any error messages @@ -274,17 +274,20 @@ WuQMacroExecutor::findObjectByName(const QString& objectName) const */ bool WuQMacroExecutor::runMacro(const WuQMacro* macro, - QObject* topLevelObject, + QObject* window, std::vector& otherObjectParents, - const RunOptions& options, + const WuQMacroExecutorOptions* executorOptions, QString& errorMessageOut) const { + CaretAssert(macro); + CaretAssert(executorOptions); + m_parentObjects.clear(); - m_parentObjects.push_back(topLevelObject); + m_parentObjects.push_back(window); m_parentObjects.insert(m_parentObjects.end(), otherObjectParents.begin(), otherObjectParents.end()); - m_runOptions = options; + m_runOptions = *executorOptions; errorMessageOut.clear(); @@ -299,7 +302,7 @@ WuQMacroExecutor::runMacro(const WuQMacro* macro, errorMessageOut.append("Unable to find object named " + objectName + "\n"); - if (m_runOptions.m_stopOnErrorFlag) { + if (m_runOptions.isStopOnError()) { return false; } continue; @@ -309,7 +312,7 @@ WuQMacroExecutor::runMacro(const WuQMacro* macro, errorMessageOut.append("Object named " + objectName + " has signals blocked"); - if (m_runOptions.m_stopOnErrorFlag) { + if (m_runOptions.isStopOnError()) { return false; } continue; @@ -320,15 +323,15 @@ WuQMacroExecutor::runMacro(const WuQMacro* macro, object, commandErrorMessage)) { errorMessageOut.append(commandErrorMessage + "\n"); - if (m_runOptions.m_stopOnErrorFlag) { + if (m_runOptions.isStopOnError()) { return false; } } QGuiApplication::processEvents(); if (mc->getClassType() != WuQMacroClassTypeEnum::MOUSE_USER_EVENT) { - if (m_runOptions.m_secondsDelayBetweenCommands > 0.0) { - SystemUtilities::sleepSeconds(m_runOptions.m_secondsDelayBetweenCommands); + if (m_runOptions.getSecondsDelayBetweenCommands() > 0.0) { + SystemUtilities::sleepSeconds(m_runOptions.getSecondsDelayBetweenCommands()); } } diff --git a/src/GuiQt/WuQMacroExecutor.h b/src/GuiQt/WuQMacroExecutor.h index 55ceda07d..f94775b6a 100644 --- a/src/GuiQt/WuQMacroExecutor.h +++ b/src/GuiQt/WuQMacroExecutor.h @@ -27,6 +27,7 @@ #include +#include "WuQMacroExecutorOptions.h" class QTabBar; class QWidget; @@ -34,41 +35,13 @@ namespace caret { class WuQMacro; class WuQMacroCommand; + class WuQMacroExecutorOptions; class WuQMacroExecutor : public QObject { Q_OBJECT public: - class RunOptions { - public: - RunOptions() - : m_secondsDelayBetweenCommands(1.0), - m_showMouseMovementFlag(true), - m_stopOnErrorFlag(true), - m_loopFlag(false) - { - - } - RunOptions(const float secondsDelayBetweenCommands, - const bool showMouseMovementFlag, - const bool loopFlag) - : m_secondsDelayBetweenCommands(secondsDelayBetweenCommands), - m_showMouseMovementFlag(showMouseMovementFlag), - m_stopOnErrorFlag(true), - m_loopFlag(loopFlag) - { - } - - float m_secondsDelayBetweenCommands; - - bool m_showMouseMovementFlag; - - bool m_stopOnErrorFlag; - - bool m_loopFlag; - }; - WuQMacroExecutor(); virtual ~WuQMacroExecutor(); @@ -80,7 +53,7 @@ namespace caret { bool runMacro(const WuQMacro* macro, QObject* window, std::vector& otherObjectParents, - const RunOptions& options, + const WuQMacroExecutorOptions* executorOptions, QString& errorMessageOut) const; @@ -110,7 +83,7 @@ namespace caret { QObject* findObjectByName(const QString& objectName) const; - mutable RunOptions m_runOptions; + mutable WuQMacroExecutorOptions m_runOptions; mutable std::vector m_parentObjects; diff --git a/src/GuiQt/WuQMacroExecutorOptions.cxx b/src/GuiQt/WuQMacroExecutorOptions.cxx new file mode 100644 index 000000000..f19e8d16f --- /dev/null +++ b/src/GuiQt/WuQMacroExecutorOptions.cxx @@ -0,0 +1,187 @@ + +/*LICENSE_START*/ +/* + * Copyright (C) 2018 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + +#define __WU_Q_MACRO_EXECUTOR_OPTIONS_DECLARE__ +#include "WuQMacroExecutorOptions.h" +#undef __WU_Q_MACRO_EXECUTOR_OPTIONS_DECLARE__ + +#include "CaretAssert.h" +using namespace caret; + + + +/** + * \class caret::WuQMacroExecutorOptions + * \brief Options for execution of macros + * \ingroup GuiQt + */ + +/** + * Constructor. + */ +WuQMacroExecutorOptions::WuQMacroExecutorOptions() +: CaretObject() +{ + /* members initialized in header file */ +} + +/** + * Destructor. + */ +WuQMacroExecutorOptions::~WuQMacroExecutorOptions() +{ +} + +/** + * Copy constructor. + * @param obj + * Object that is copied. + */ +WuQMacroExecutorOptions::WuQMacroExecutorOptions(const WuQMacroExecutorOptions& obj) +: CaretObject(obj) +{ + this->copyHelperWuQMacroExecutorOptions(obj); +} + +/** + * Assignment operator. + * @param obj + * Data copied from obj to this. + * @return + * Reference to this object. + */ +WuQMacroExecutorOptions& +WuQMacroExecutorOptions::operator=(const WuQMacroExecutorOptions& obj) +{ + if (this != &obj) { + CaretObject::operator=(obj); + this->copyHelperWuQMacroExecutorOptions(obj); + } + return *this; +} + +/** + * Helps with copying an object of this type. + * @param obj + * Object that is copied. + */ +void +WuQMacroExecutorOptions::copyHelperWuQMacroExecutorOptions(const WuQMacroExecutorOptions& obj) +{ + m_secondsDelayBetweenCommands = obj.m_secondsDelayBetweenCommands; + m_loopingFlag = obj.m_loopingFlag; + m_stopOnErrorFlag = obj.m_stopOnErrorFlag; + m_showMouseMovementFlag = obj.m_showMouseMovementFlag; +} + +/** + * Get a description of this object's content. + * @return String describing this object's content. + */ +AString +WuQMacroExecutorOptions::toString() const +{ + return "WuQMacroExecutorOptions"; +} + +/** + * @return Seconds delay between macro commands + */ +float +WuQMacroExecutorOptions::getSecondsDelayBetweenCommands() const +{ + return m_secondsDelayBetweenCommands; +} + +/** + * Set seconds delay between macro commands + * + * @parm seconds + * Number of seconds + */ +void +WuQMacroExecutorOptions::setSecondsDelayBetweenCommands(const float seconds) +{ + m_secondsDelayBetweenCommands = seconds; +} + +/** + * @return Show mouse movement while running macro + */ +bool +WuQMacroExecutorOptions::isShowMouseMovement() const +{ + return m_showMouseMovementFlag; +} + +/** + * Set show mouse movement activity that was recorded in the macro + * + * @param status + * New status + */ +void +WuQMacroExecutorOptions::setShowMouseMovement(const bool status) +{ + m_showMouseMovementFlag = status; +} + +/** + * @return True if exector should stop macro if an error occurs + */ +bool +WuQMacroExecutorOptions::isStopOnError() const +{ + return m_stopOnErrorFlag; +} + +/** + * Stop runningh of macro if there is an error while running the macro + * + * @param status + * New status + */ +void +WuQMacroExecutorOptions::setStopOnError(const bool status) +{ + m_stopOnErrorFlag = status; +} + +/** + * @return True if looping is on. + */ +bool +WuQMacroExecutorOptions::isLooping() const +{ + return m_loopingFlag; +} + +/** + * Set looping on/off + * + * @param status + * New status + */ +void +WuQMacroExecutorOptions::setLooping(const bool status) +{ + m_loopingFlag = status; +} diff --git a/src/GuiQt/WuQMacroExecutorOptions.h b/src/GuiQt/WuQMacroExecutorOptions.h new file mode 100644 index 000000000..948783165 --- /dev/null +++ b/src/GuiQt/WuQMacroExecutorOptions.h @@ -0,0 +1,85 @@ +#ifndef __WU_Q_MACRO_EXECUTOR_OPTIONS_H__ +#define __WU_Q_MACRO_EXECUTOR_OPTIONS_H__ + +/*LICENSE_START*/ +/* + * Copyright (C) 2018 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + + + +#include + +#include "CaretObject.h" + + + +namespace caret { + + class WuQMacroExecutorOptions : public CaretObject { + + public: + WuQMacroExecutorOptions(); + + virtual ~WuQMacroExecutorOptions(); + + WuQMacroExecutorOptions(const WuQMacroExecutorOptions& obj); + + WuQMacroExecutorOptions& operator=(const WuQMacroExecutorOptions& obj); + + float getSecondsDelayBetweenCommands() const; + + void setSecondsDelayBetweenCommands(const float seconds); + + bool isShowMouseMovement() const; + + void setShowMouseMovement(const bool status); + + bool isStopOnError() const; + + void setStopOnError(const bool status); + + bool isLooping() const; + + void setLooping(const bool status); + + // ADD_NEW_METHODS_HERE + + virtual AString toString() const; + + private: + void copyHelperWuQMacroExecutorOptions(const WuQMacroExecutorOptions& obj); + + float m_secondsDelayBetweenCommands = 1.0f; + + bool m_showMouseMovementFlag = true; + + bool m_stopOnErrorFlag = true; + + bool m_loopingFlag = false; + + // ADD_NEW_MEMBERS_HERE + + }; + +#ifdef __WU_Q_MACRO_EXECUTOR_OPTIONS_DECLARE__ + // +#endif // __WU_Q_MACRO_EXECUTOR_OPTIONS_DECLARE__ + +} // namespace +#endif //__WU_Q_MACRO_EXECUTOR_OPTIONS_H__ diff --git a/src/GuiQt/WuQMacroManager.cxx b/src/GuiQt/WuQMacroManager.cxx index adc86a410..7750c53fc 100644 --- a/src/GuiQt/WuQMacroManager.cxx +++ b/src/GuiQt/WuQMacroManager.cxx @@ -27,6 +27,7 @@ #include #include +#include #include #include #include @@ -47,6 +48,7 @@ #include "WuQMacroMouseEventInfo.h" #include "WuQMacroAttributesDialog.h" #include "WuQMacroEditorDialog.h" +#include "WuQMacroExecutorOptions.h" #include "WuQMacroSignalWatcher.h" using namespace caret; @@ -70,6 +72,7 @@ WuQMacroManager::WuQMacroManager(const QString& name, m_name(name) { setObjectName(name); + m_executorOptions.reset(new WuQMacroExecutorOptions()); } /** @@ -502,14 +505,11 @@ WuQMacroManager::updateNonModalDialogs() * * @param widget * Widget used for parent of dialogs - * @param runOptions - * Options used when running the macro * @param macro * Macro that is run */ void WuQMacroManager::runMacro(QWidget* widget, - const WuQMacroExecutor::RunOptions& runOptions, const WuQMacro* macro) { CaretAssert(widget); @@ -521,7 +521,7 @@ WuQMacroManager::runMacro(QWidget* widget, if ( ! executor.runMacro(macro, widget, m_parentObjects, - runOptions, + m_executorOptions.get(), errorMessage)) { QMessageBox::critical(widget, "Run Macro Error", @@ -559,7 +559,7 @@ WuQMacroManager::addParentObject(QObject* parentObject) */ bool WuQMacroManager::editMacroAttributes(QWidget* parent, - WuQMacro* macro) + WuQMacro* macro) { if (macro != NULL) { WuQMacroAttributesDialog attributesDialog(macro, @@ -802,6 +802,104 @@ WuQMacroManager::getToolTipForObjectName(const QString& objectName) const return tooltip; } +/** + * @return Pointer to the executor's run options (const method) + */ +const WuQMacroExecutorOptions* +WuQMacroManager::getExecutorOptions() const +{ + return m_executorOptions.get(); +} + +/** + * @return Pointer to the executor's run options + */ +WuQMacroExecutorOptions* +WuQMacroManager::getExecutorOptions() +{ + return m_executorOptions.get(); +} + +/** + * Process a key press event for macro shortcut + * + * @param keyEvent + * Key event information. + * @return + * True if the input process recognized the key event + * and the key event SHOULD NOT be propagated to parent + * widgets + */ +bool +WuQMacroManager::runMacroWithShortCutKeyEvent(QWidget* window, + const QKeyEvent* keyEvent) +{ + CaretAssert(keyEvent); + + /* + * On Mac, SHIFT, CONTROL, COMMANDS + * On Linux/Windows: SHIFT CTRL META + */ + Qt::KeyboardModifiers mods; +#ifdef CARET_OS_MACOSX + mods.setFlag(Qt::ShiftModifier); + mods.setFlag(Qt::MetaModifier); + mods.setFlag(Qt::ControlModifier); +#else + mods.setFlag(Qt::ShiftModifier); + mods.setFlag(Qt::ControlModifier); + mods.setFlag(Qt::AltModifier); +#endif + if (keyEvent->modifiers() == mods) { + const int qtKeyCode = keyEvent->key(); + const WuQMacroShortCutKeyEnum::Enum shortCutKey = WuQMacroShortCutKeyEnum::fromQtKeyEnum(qtKeyCode); + if (shortCutKey != WuQMacroShortCutKeyEnum::Key_None) { + const WuQMacro* macro = getMacroWithShortCutKey(shortCutKey); + if (macro != NULL) { + runMacro(window, + macro); + return true; + } + } + } + + return false; +} + +/** + * @return the macro with the given short cut key or NULL if not found + * + * @param shortCutKey + * The short cut key + */ +WuQMacro* +WuQMacroManager::getMacroWithShortCutKey(const WuQMacroShortCutKeyEnum::Enum shortCutKey) const +{ + WuQMacro* macro(NULL); + + const auto macroGroups = getMacroGroups(); + for (const auto mg : macroGroups) { + macro = mg->getMacroWithShortCutKey(shortCutKey); + if (macro != NULL) { + break; + } + } + + return macro; +} + +QString +WuQMacroManager::getShortCutKeysMask() +{ + QString mask; +#ifdef CARET_OS_MACOSX + mask = "shift/control/command"; +#else + mask = "shift/ctrl/alt"; +#endif + + return mask; +} diff --git a/src/GuiQt/WuQMacroManager.h b/src/GuiQt/WuQMacroManager.h index 650d98573..cced1d4d8 100644 --- a/src/GuiQt/WuQMacroManager.h +++ b/src/GuiQt/WuQMacroManager.h @@ -29,7 +29,9 @@ #include "WuQMacroExecutor.h" #include "WuQMacroModeEnum.h" +#include "WuQMacroShortCutKeyEnum.h" +class QKeyEvent; class QMouseEvent; class QWidget; @@ -38,6 +40,7 @@ namespace caret { class WuQMacro; class WuQMacroCommand; class WuQMacroDialog; + class WuQMacroExecutorOptions; class WuQMacroGroup; class WuQMacroSignalWatcher; @@ -81,6 +84,10 @@ namespace caret { std::vector getNonModalDialogs(); + WuQMacroExecutorOptions* getExecutorOptions(); + + const WuQMacroExecutorOptions* getExecutorOptions() const; + void updateNonModalDialogs(); bool editMacroAttributes(QWidget* parent, @@ -101,9 +108,11 @@ namespace caret { WuQMacro* macro); void runMacro(QWidget* window, - const WuQMacroExecutor::RunOptions& runOptions, const WuQMacro* macro); + bool runMacroWithShortCutKeyEvent(QWidget* window, + const QKeyEvent* keyEvent); + void printSupportedWidgetsToTerminal(); void printToLevelWidgetsToTerminal(); @@ -112,6 +121,10 @@ namespace caret { QString getToolTipForObjectName(const QString& objectName) const; + WuQMacro* getMacroWithShortCutKey(const WuQMacroShortCutKeyEnum::Enum shortCutKey) const; + + static QString getShortCutKeysMask(); + // ADD_NEW_METHODS_HERE private: @@ -132,6 +145,8 @@ namespace caret { static WuQMacroManager* s_singletonMacroManager; + std::unique_ptr m_executorOptions; + // ADD_NEW_MEMBERS_HERE }; diff --git a/src/GuiQt/WuQMacroShortCutKeyComboBox.cxx b/src/GuiQt/WuQMacroShortCutKeyComboBox.cxx new file mode 100644 index 000000000..748f111c7 --- /dev/null +++ b/src/GuiQt/WuQMacroShortCutKeyComboBox.cxx @@ -0,0 +1,132 @@ + +/*LICENSE_START*/ +/* + * Copyright (C) 2014 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + +#define __WUQ_MACRO_SHORT_CUT_COMBOBOX_DECLARE__ +#include "WuQMacroShortCutKeyComboBox.h" +#undef __WUQ_MACRO_SHORT_CUT_COMBOBOX_DECLARE__ + +using namespace caret; + +/** + * \class caret::WuQMacroShortCutKeyComboBox + * \brief Control for selection of a macro short cut key. + * \ingroup GuiQt + */ + +/** + * Constructor. + * @param parent + * The parent. + */ +WuQMacroShortCutKeyComboBox::WuQMacroShortCutKeyComboBox(QObject* parent) +: WuQWidget(parent) +{ + std::vector allShortCutKeys; + WuQMacroShortCutKeyEnum::getAllEnums(allShortCutKeys); + const int32_t numShortCutKeys = static_cast(allShortCutKeys.size()); + + m_shortCutKeyComboBox = new QComboBox(); + for (int32_t i = 0; i < numShortCutKeys; i++) { + m_shortCutKeyComboBox->addItem(WuQMacroShortCutKeyEnum::toGuiName(allShortCutKeys[i])); + m_shortCutKeyComboBox->setItemData(i, WuQMacroShortCutKeyEnum::toIntegerCode(allShortCutKeys[i])); + } + + QObject::connect(m_shortCutKeyComboBox, SIGNAL(activated(int)), + this, SLOT(shortCutKeyComboBoxSelection(int))); + +} + +/** + * Destructor. + */ +WuQMacroShortCutKeyComboBox::~WuQMacroShortCutKeyComboBox() +{ + +} + +/** + * Called to set the selected short cut key. + * @param shortCutKey + * New value for short cut key. + */ +void +WuQMacroShortCutKeyComboBox::setSelectedShortCutKey(const WuQMacroShortCutKeyEnum::Enum shortCutKey) +{ + const int32_t shortCutIntegerCode = WuQMacroShortCutKeyEnum::toIntegerCode(shortCutKey); + + const int numShortCutKeys = m_shortCutKeyComboBox->count(); + for (int32_t i = 0; i < numShortCutKeys; i++) { + if (shortCutIntegerCode == m_shortCutKeyComboBox->itemData(i).toInt()) { + if (this->signalsBlocked()) { + m_shortCutKeyComboBox->blockSignals(true); + } + + m_shortCutKeyComboBox->setCurrentIndex(i); + + if (this->signalsBlocked()) { + m_shortCutKeyComboBox->blockSignals(false); + } + break; + } + } +} + +/** + * @return The selected short cut key. + */ +WuQMacroShortCutKeyEnum::Enum +WuQMacroShortCutKeyComboBox::getSelectedShortCutKey() const +{ + WuQMacroShortCutKeyEnum::Enum shortCutKey = WuQMacroShortCutKeyEnum::Key_None; + const int32_t indx = m_shortCutKeyComboBox->currentIndex(); + if (indx >= 0) { + const int32_t integerCode = m_shortCutKeyComboBox->itemData(indx).toInt(); + shortCutKey = WuQMacroShortCutKeyEnum::fromIntegerCode(integerCode, NULL); + } + return shortCutKey; +} + +/** + * @return The widget for this control. + */ +QWidget* +WuQMacroShortCutKeyComboBox::getWidget() +{ + return m_shortCutKeyComboBox; +} + +/** + * Called when a short cut key is selected + * @param indx + * Index of selection. + */ +void +WuQMacroShortCutKeyComboBox::shortCutKeyComboBoxSelection(int indx) +{ + if (this->signalsBlocked() == false) { + if ((indx >= 0) && + (indx < m_shortCutKeyComboBox->count())) { + const int32_t integerCode = m_shortCutKeyComboBox->itemData(indx).toInt(); + WuQMacroShortCutKeyEnum::Enum shortCutKey = WuQMacroShortCutKeyEnum::fromIntegerCode(integerCode, NULL); + emit shortCutKeySelected(shortCutKey); + } + } +} diff --git a/src/GuiQt/WuQMacroShortCutKeyComboBox.h b/src/GuiQt/WuQMacroShortCutKeyComboBox.h new file mode 100644 index 000000000..00776b894 --- /dev/null +++ b/src/GuiQt/WuQMacroShortCutKeyComboBox.h @@ -0,0 +1,71 @@ +#ifndef __WUQ_MACRO_SHORT_CUT_COMBOBOX__H_ +#define __WUQ_MACRO_SHORT_CUT_COMBOBOX__H_ + +/*LICENSE_START*/ +/* + * Copyright (C) 2014 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + +#include + +#include "WuQMacroShortCutKeyEnum.h" +#include "WuQWidget.h" + +namespace caret { + + class Surface; + + class WuQMacroShortCutKeyComboBox : public WuQWidget { + + Q_OBJECT + + public: + WuQMacroShortCutKeyComboBox(QObject* parent); + + virtual ~WuQMacroShortCutKeyComboBox(); + + WuQMacroShortCutKeyEnum::Enum getSelectedShortCutKey() const; + + QWidget* getWidget(); + + public slots: + void setSelectedShortCutKey(const WuQMacroShortCutKeyEnum::Enum shortCutKey); + + signals: + void shortCutKeySelected(const WuQMacroShortCutKeyEnum::Enum); + + private slots: + void shortCutKeyComboBoxSelection(int); + + private: + WuQMacroShortCutKeyComboBox(const WuQMacroShortCutKeyComboBox&); + + WuQMacroShortCutKeyComboBox& operator=(const WuQMacroShortCutKeyComboBox&); + + QComboBox* m_shortCutKeyComboBox; + + public: + private: + }; + +#ifdef __WUQ_MACRO_SHORT_CUT_COMBOBOX_DECLARE__ + // +#endif // __WUQ_MACRO_SHORT_CUT_COMBOBOX_DECLARE__ + +} // namespace +#endif //__WUQ_MACRO_SHORT_CUT_COMBOBOX__H_ -- GitLab From 02fba57fd7a4d58156a609e34120e6b901df6dc7 Mon Sep 17 00:00:00 2001 From: John Harwell Date: Wed, 26 Dec 2018 09:46:53 -0600 Subject: [PATCH 153/427] WB-834: When running a macro and a widget is not visible, do not move the mouse as it will move to an incorrect location, possibly outside of the window. --- src/GuiQt/WuQMacroExecutor.cxx | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/GuiQt/WuQMacroExecutor.cxx b/src/GuiQt/WuQMacroExecutor.cxx index 30acd2b29..9e9c0a9f2 100644 --- a/src/GuiQt/WuQMacroExecutor.cxx +++ b/src/GuiQt/WuQMacroExecutor.cxx @@ -197,7 +197,14 @@ WuQMacroExecutor::moveMouseToWidgetImplementation(QObject* moveToObject, if (moveToWidget == NULL) { return; - + } + + /* + * Test visibility of widget. + * Note: Cannot use isHidden(), see Qt documentation. + */ + if ( ! moveToWidget->isVisible()) { + return; } if (usingParentFlag) { @@ -215,6 +222,7 @@ WuQMacroExecutor::moveMouseToWidgetImplementation(QObject* moveToObject, widgetPoint.setX(x); widgetPoint.setY(y); } + const QPoint windowPoint = moveToWidget->mapToGlobal(widgetPoint); QCursor::setPos(windowPoint); SystemUtilities::sleepSeconds(0.025); -- GitLab From 161a771ea252d51a4edbb42736d56760b57fca17 Mon Sep 17 00:00:00 2001 From: John Harwell Date: Wed, 26 Dec 2018 12:34:21 -0600 Subject: [PATCH 154/427] WB-834: Add unique identifier to macro and macro group since names may not be unique. --- src/Common/WuQMacro.cxx | 34 +++++++++- src/Common/WuQMacro.h | 6 ++ src/Common/WuQMacroGroup.cxx | 78 +++++++++++++++++++++-- src/Common/WuQMacroGroup.h | 10 +++ src/Common/WuQMacroGroupXmlFormatBase.h | 2 + src/Common/WuQMacroGroupXmlReader.cxx | 14 +++++ src/Common/WuQMacroGroupXmlWriter.cxx | 4 ++ src/GuiQt/WuQMacroDialog.cxx | 82 +++++++++++++------------ src/GuiQt/WuQMacroDialog.h | 4 -- 9 files changed, 184 insertions(+), 50 deletions(-) diff --git a/src/Common/WuQMacro.cxx b/src/Common/WuQMacro.cxx index cda10953d..3a32638b3 100644 --- a/src/Common/WuQMacro.cxx +++ b/src/Common/WuQMacro.cxx @@ -23,6 +23,8 @@ #include "WuQMacro.h" #undef __WU_Q_MACRO_DECLARE__ +#include + #include "CaretAssert.h" #include "WuQMacroCommand.h" @@ -43,7 +45,7 @@ using namespace caret; WuQMacro::WuQMacro() : CaretObjectTracksModification() { - + m_uniqueIdentifier = QUuid::createUuid().toString(); } /** @@ -74,6 +76,8 @@ WuQMacro::clearCommands() WuQMacro::WuQMacro(const WuQMacro& obj) : CaretObjectTracksModification(obj) { + m_uniqueIdentifier = QUuid::createUuid().toString(); + this->copyHelperWuQMacro(obj); } @@ -102,6 +106,8 @@ WuQMacro::operator=(const WuQMacro& obj) void WuQMacro::copyHelperWuQMacro(const WuQMacro& obj) { + /* Note: unique identifier is NOT copied */ + clearCommands(); for (auto mc : obj.m_macroCommands) { @@ -120,6 +126,32 @@ WuQMacro::addMacroCommand(WuQMacroCommand* macroCommand) setModified(); } +/** + * @return The unique identifier + */ +QString +WuQMacro::getUniqueIdentifier() const +{ + return m_uniqueIdentifier; +} + +/** + * Set unique identifier of macro + * + * @param uniqueIdentifier + * New unique identifier + */void +WuQMacro::setUniqueIdentifier(const QString& uniqueIdentifier) +{ + if (uniqueIdentifier.isEmpty()) { + return; + } + if (m_uniqueIdentifier != uniqueIdentifier) { + m_uniqueIdentifier = uniqueIdentifier; + setModified(); + } +} + /** * @return Name of macro */ diff --git a/src/Common/WuQMacro.h b/src/Common/WuQMacro.h index 64f393910..13b822b76 100644 --- a/src/Common/WuQMacro.h +++ b/src/Common/WuQMacro.h @@ -51,6 +51,10 @@ namespace caret { WuQMacroCommand* getMacroCommandAtIndex(const int32_t index); + QString getUniqueIdentifier() const; + + void setUniqueIdentifier(const QString& uniqueIdentifier); + QString getName() const; void setName(const QString& name); @@ -78,6 +82,8 @@ namespace caret { std::vector m_macroCommands; + QString m_uniqueIdentifier; + QString m_name; QString m_description; diff --git a/src/Common/WuQMacroGroup.cxx b/src/Common/WuQMacroGroup.cxx index 471da57ce..e8cba4e36 100644 --- a/src/Common/WuQMacroGroup.cxx +++ b/src/Common/WuQMacroGroup.cxx @@ -23,6 +23,8 @@ #include "WuQMacroGroup.h" #undef __WU_Q_MACRO_GROUP_DECLARE__ +#include + #include "CaretAssert.h" #include "WuQMacro.h" #include "WuQMacroGroupXmlReader.h" @@ -48,7 +50,7 @@ WuQMacroGroup::WuQMacroGroup(const QString& name) : CaretObjectTracksModification(), m_name(name) { - + m_uniqueIdentifier = QUuid::createUuid().toString(); } /** @@ -67,6 +69,8 @@ WuQMacroGroup::~WuQMacroGroup() WuQMacroGroup::WuQMacroGroup(const WuQMacroGroup& obj) : CaretObjectTracksModification(obj) { + m_uniqueIdentifier = QUuid::createUuid().toString(); + this->copyHelperWuQMacroGroup(obj); } @@ -95,6 +99,8 @@ WuQMacroGroup::operator=(const WuQMacroGroup& obj) void WuQMacroGroup::copyHelperWuQMacroGroup(const WuQMacroGroup& obj) { + /* Note: unique identifier is NOT copied */ + clear(); m_name = obj.m_name; @@ -110,10 +116,8 @@ WuQMacroGroup::copyHelperWuQMacroGroup(const WuQMacroGroup& obj) void WuQMacroGroup::clear() { - if (m_macros.empty()) { - return; - } - + /* Note: Do not clear unique identifier */ + for (auto m : m_macros) { delete m; } @@ -145,6 +149,32 @@ WuQMacroGroup::setName(const QString& name) } } +/** + * @return The unique identifier + */ +QString +WuQMacroGroup::getUniqueIdentifier() const +{ + return m_uniqueIdentifier; +} + +/** + * Set unique identifier of macro + * + * @param uniqueIdentifier + * New unique identifier + */void +WuQMacroGroup::setUniqueIdentifier(const QString& uniqueIdentifier) +{ + if (uniqueIdentifier.isEmpty()) { + return; + } + if (m_uniqueIdentifier != uniqueIdentifier) { + m_uniqueIdentifier = uniqueIdentifier; + setModified(); + } +} + /** * Add a macro * @@ -223,6 +253,44 @@ WuQMacroGroup::getMacroByName(const QString& name) const return NULL; } +/** + * Get the macro with the given unique identifier + * + * @param uniqueIdentifier + * Unique identifier of the macro + * @return + * Pointer to macro with the unique identifier or NULL if not found + */ +WuQMacro* +WuQMacroGroup::getMacroWithUniqueIdentifier(const QString& uniqueIdentifier) +{ + for (auto m : m_macros) { + if (m->getUniqueIdentifier() == uniqueIdentifier) { + return m; + } + } + return NULL; +} + +/** + * Get the macro with the given unique identifier (const method) + * + * @param uniqueIdentifier + * Unique identifier of the macro + * @return + * Pointer to macro with the unique identifier or NULL if not found + */ +const WuQMacro* +WuQMacroGroup::getMacroWithUniqueIdentifier(const QString& uniqueIdentifier) const +{ + for (auto m : m_macros) { + if (m->getUniqueIdentifier() == uniqueIdentifier) { + return m; + } + } + return NULL; +} + /** * Get the macro with the given short cut key * diff --git a/src/Common/WuQMacroGroup.h b/src/Common/WuQMacroGroup.h index 3bff9f58b..43ec9d7b5 100644 --- a/src/Common/WuQMacroGroup.h +++ b/src/Common/WuQMacroGroup.h @@ -49,6 +49,10 @@ namespace caret { void setName(const QString& name); + QString getUniqueIdentifier() const; + + void setUniqueIdentifier(const QString& uniqueIdentifier); + void addMacro(WuQMacro* macro); void appendMacroGroup(const WuQMacroGroup* macroGroup); @@ -63,6 +67,10 @@ namespace caret { const WuQMacro* getMacroAtIndex(const int32_t index) const; + WuQMacro* getMacroWithUniqueIdentifier(const QString& uniqueIdentifier); + + const WuQMacro* getMacroWithUniqueIdentifier(const QString& uniqueIdentifier) const; + WuQMacro* getMacroWithShortCutKey(const WuQMacroShortCutKeyEnum::Enum shortCutKey); const WuQMacro* getMacroWithShortCutKey(const WuQMacroShortCutKeyEnum::Enum shortCutKey) const; @@ -89,6 +97,8 @@ namespace caret { private: void copyHelperWuQMacroGroup(const WuQMacroGroup& obj); + QString m_uniqueIdentifier; + QString m_name; std::vector m_macros; diff --git a/src/Common/WuQMacroGroupXmlFormatBase.h b/src/Common/WuQMacroGroupXmlFormatBase.h index aa16eec11..c9b3e4696 100644 --- a/src/Common/WuQMacroGroupXmlFormatBase.h +++ b/src/Common/WuQMacroGroupXmlFormatBase.h @@ -57,6 +57,7 @@ namespace caret { static const QString ATTRIBUTE_OBJECT_VALUE; static const QString ATTRIBUTE_OBJECT_VALUE_TWO; static const QString ATTRIBUTE_SHORT_CUT_KEY; + static const QString ATTRIBUTE_UNIQUE_IDENTIFIER; static const QString ATTRIBUTE_VERSION; static const QString ATTRIBUTE_MOUSE_BUTTON; @@ -96,6 +97,7 @@ namespace caret { const QString WuQMacroGroupXmlFormatBase::ATTRIBUTE_OBJECT_VALUE = "DataValue"; const QString WuQMacroGroupXmlFormatBase::ATTRIBUTE_OBJECT_VALUE_TWO = "DataValueTwo"; const QString WuQMacroGroupXmlFormatBase::ATTRIBUTE_SHORT_CUT_KEY = "ShortCutKey"; + const QString WuQMacroGroupXmlFormatBase::ATTRIBUTE_UNIQUE_IDENTIFIER = "UniqueIdentifier"; const QString WuQMacroGroupXmlFormatBase::ATTRIBUTE_VERSION = "Version"; const QString WuQMacroGroupXmlFormatBase::ATTRIBUTE_MOUSE_BUTTON = "MouseButton"; diff --git a/src/Common/WuQMacroGroupXmlReader.cxx b/src/Common/WuQMacroGroupXmlReader.cxx index 5ee38d1ed..ca94defd5 100644 --- a/src/Common/WuQMacroGroupXmlReader.cxx +++ b/src/Common/WuQMacroGroupXmlReader.cxx @@ -89,6 +89,12 @@ WuQMacroGroupXmlReader::readFromString(const QString& xmlString, const QXmlStreamAttributes attributes = m_xmlStreamReader->attributes(); const QStringRef name = attributes.value(ATTRIBUTE_NAME); const QStringRef versionText = attributes.value(ATTRIBUTE_VERSION); + QString uniqueIdentifier = attributes.value(ATTRIBUTE_UNIQUE_IDENTIFIER).toString(); + if (uniqueIdentifier.isEmpty()) { + addToWarnings(ELEMENT_MACRO_GROUP + + " is missing attribute or value is empty: " + + ATTRIBUTE_UNIQUE_IDENTIFIER); + } if (versionText.isEmpty()) { m_xmlStreamReader->raiseError(ATTRIBUTE_VERSION + " is missing from element " @@ -96,6 +102,7 @@ WuQMacroGroupXmlReader::readFromString(const QString& xmlString, } else if (versionText == VALUE_VERSION_ONE) { macroGroup->setName(name.toString()); + macroGroup->setUniqueIdentifier(uniqueIdentifier); readVersionOne(macroGroup); } else { @@ -203,6 +210,12 @@ WuQMacroGroupXmlReader::readMacroVersionOne() + " is missing attribute or value is empty: " + ATTRIBUTE_SHORT_CUT_KEY); } + QString uniqueIdentifier = attributes.value(ATTRIBUTE_UNIQUE_IDENTIFIER).toString(); + if (uniqueIdentifier.isEmpty()) { + addToWarnings(ELEMENT_MACRO + + " is missing attribute or value is empty: " + + ATTRIBUTE_UNIQUE_IDENTIFIER); + } bool validShortCutKey(false); WuQMacroShortCutKeyEnum::Enum shortCutKey = WuQMacroShortCutKeyEnum::fromName(shortCutKeyString, @@ -229,6 +242,7 @@ WuQMacroGroupXmlReader::readMacroVersionOne() macro = new WuQMacro(); macro->setName(macroName); macro->setShortCutKey(shortCutKey); + macro->setUniqueIdentifier(uniqueIdentifier); return macro; } diff --git a/src/Common/WuQMacroGroupXmlWriter.cxx b/src/Common/WuQMacroGroupXmlWriter.cxx index cb9da5ed3..4e3c4c4bc 100644 --- a/src/Common/WuQMacroGroupXmlWriter.cxx +++ b/src/Common/WuQMacroGroupXmlWriter.cxx @@ -76,6 +76,8 @@ WuQMacroGroupXmlWriter::writeToString(const WuQMacroGroup* macroGroup, m_xmlStreamWriter->writeStartElement(ELEMENT_MACRO_GROUP); m_xmlStreamWriter->writeAttribute(ATTRIBUTE_NAME, macroGroup->getName()); m_xmlStreamWriter->writeAttribute(ATTRIBUTE_VERSION, "1"); + m_xmlStreamWriter->writeAttribute(ATTRIBUTE_UNIQUE_IDENTIFIER, + macroGroup->getUniqueIdentifier()); const int32_t numMacros = macroGroup->getNumberOfMacros(); for (int32_t i = 0; i < numMacros; i++) { @@ -106,6 +108,8 @@ WuQMacroGroupXmlWriter::writeMacroToXML(const WuQMacro* macro) const QString shortCutText = WuQMacroShortCutKeyEnum::toName(macro->getShortCutKey()); m_xmlStreamWriter->writeAttribute(ATTRIBUTE_SHORT_CUT_KEY, shortCutText); + m_xmlStreamWriter->writeAttribute(ATTRIBUTE_UNIQUE_IDENTIFIER, + macro->getUniqueIdentifier()); m_xmlStreamWriter->writeTextElement(ELEMENT_DESCRIPTION, macro->getDescription()); diff --git a/src/GuiQt/WuQMacroDialog.cxx b/src/GuiQt/WuQMacroDialog.cxx index 8cc1673b2..34d5a6bc9 100644 --- a/src/GuiQt/WuQMacroDialog.cxx +++ b/src/GuiQt/WuQMacroDialog.cxx @@ -106,7 +106,7 @@ WuQMacroDialog::WuQMacroDialog(QWidget* parent) } m_dialogButtonBox = new QDialogButtonBox(QDialogButtonBox::Close); - m_runButton = m_dialogButtonBox->addButton("Run...", QDialogButtonBox::ApplyRole); + m_runButton = m_dialogButtonBox->addButton("Run", QDialogButtonBox::ApplyRole); QObject::connect(m_dialogButtonBox, &QDialogButtonBox::rejected, this, &WuQMacroDialog::close); QObject::connect(m_dialogButtonBox, &QDialogButtonBox::clicked, @@ -288,21 +288,26 @@ WuQMacroDialog::buttonBoxButtonClicked(QAbstractButton* button) void WuQMacroDialog::updateDialogContents() { - QString selectedGroupName = m_macroGroupComboBox->currentText(); - + QString selectedUniqueIdentifer; + const QVariant dataSelected = m_macroGroupComboBox->currentData(); + if (dataSelected.isValid()) { + if (dataSelected.type() == QVariant::String) { + selectedUniqueIdentifer = dataSelected.toString(); + } + } + m_macroGroups = WuQMacroManager::instance()->getMacroGroups(); - int32_t selectedIndex = 0; - int32_t groupCounter(0); m_macroGroupComboBox->clear(); for (auto mg : m_macroGroups) { - m_macroGroupComboBox->addItem(mg->getName()); - if (selectedGroupName == mg->getName()) { - selectedIndex = groupCounter; - } - groupCounter++; + m_macroGroupComboBox->addItem(mg->getName(), + mg->getUniqueIdentifier()); } + int32_t selectedIndex = m_macroGroupComboBox->findData(selectedUniqueIdentifer); + if (selectedIndex < 0) { + selectedIndex = 0; + } if ((selectedIndex >= 0) && (selectedIndex < m_macroGroupComboBox->count())) { m_macroGroupComboBox->setCurrentIndex(selectedIndex); @@ -314,6 +319,7 @@ WuQMacroDialog::updateDialogContents() m_runOptionDelayBetweenCommandsSpinBox->setValue(runOptions->getSecondsDelayBetweenCommands()); m_runOptionMoveMouseCheckBox->setChecked(runOptions->isShowMouseMovement()); m_runOptionLoopCheckBox->setChecked(runOptions->isLooping()); + macroGroupBoxActivated(selectedIndex); } @@ -325,32 +331,40 @@ WuQMacroDialog::macroGroupBoxActivated(int) { const QString emptySuffix(" (Empty)"); QListWidgetItem* selectedItem = m_macrosListWidget->currentItem(); - QString selectedName = ((selectedItem != NULL) ? selectedItem->text() : ""); - const int emptyIndex = selectedName.indexOf(emptySuffix); - if (emptyIndex >= 0) { - selectedName = selectedName.left(emptyIndex); + QString selectedUniqueIdentifier; + if (selectedItem != NULL) { + selectedUniqueIdentifier = selectedItem->data(Qt::UserRole).toString(); } m_macrosListWidget->clear(); int32_t selectedIndex = 0; WuQMacroGroup* selectedGroup = getSelectedMacroGroup(); - const int32_t numMacros = selectedGroup->getNumberOfMacros(); - for (int32_t i = 0; i < numMacros; i++) { - QString macroName = selectedGroup->getMacroAtIndex(i)->getName(); - if (selectedName == macroName) { - selectedIndex = i; + if (selectedGroup != NULL) { + const int32_t numMacros = selectedGroup->getNumberOfMacros(); + for (int32_t i = 0; i < numMacros; i++) { + const WuQMacro* macro = selectedGroup->getMacroAtIndex(i); + if (macro->getUniqueIdentifier() == selectedUniqueIdentifier) { + selectedIndex = i; + } + + QString macroName = macro->getName(); + if (selectedGroup->getMacroAtIndex(i)->getNumberOfMacroCommands() <= 0) { + macroName.append(emptySuffix); + } + + QListWidgetItem* item = new QListWidgetItem(macroName); + item->setData(Qt::UserRole, + macro->getUniqueIdentifier()); + m_macrosListWidget->addItem(item); } - if (selectedGroup->getMacroAtIndex(i)->getNumberOfMacroCommands() <= 0) { - macroName.append(emptySuffix); + + if ((selectedIndex >= 0) + && (selectedIndex < numMacros)) { + m_macrosListWidget->setCurrentRow(selectedIndex); } - m_macrosListWidget->addItem(macroName); - } - - if ((selectedIndex >= 0) - && (selectedIndex < numMacros)) { - m_macrosListWidget->setCurrentRow(selectedIndex); } + macrosListWidgetCurrentRowChanged(selectedIndex); } @@ -405,18 +419,6 @@ WuQMacroDialog::getSelectedMacro() return NULL; } -/** - * Edit the selected macro - */ -void -WuQMacroDialog::editSelectedMacro() -{ - WuQMacro* macro = getSelectedMacro(); - if (macro != NULL) { - std::cout << "Edit macro: " << macro->getName() << std::endl; - } -} - /** * Run the selected macro */ @@ -508,7 +510,7 @@ WuQMacroDialog::exportButtonClicked() WuQMacroGroup* macroGroup = getSelectedMacroGroup(); WuQMacro* macro = getSelectedMacro(); - if (WuQMacroManager::instance()->exportMacros(m_importPushButton, + if (WuQMacroManager::instance()->exportMacros(m_exportPushButton, macroGroup, macro)) { updateDialogContents(); diff --git a/src/GuiQt/WuQMacroDialog.h b/src/GuiQt/WuQMacroDialog.h index d4382f063..9c204745b 100644 --- a/src/GuiQt/WuQMacroDialog.h +++ b/src/GuiQt/WuQMacroDialog.h @@ -64,8 +64,6 @@ namespace caret { void buttonBoxButtonClicked(QAbstractButton* button); - void editSelectedMacro(); - void attributesButtonClicked(); void deleteButtonClicked(); @@ -121,8 +119,6 @@ namespace caret { QDoubleSpinBox* m_runOptionDelayBetweenCommandsSpinBox; - WuQMacro* m_macro = NULL; - // ADD_NEW_MEMBERS_HERE }; -- GitLab From 348292d7b7bcb62da769e3ee6abe49e415f6c5bd Mon Sep 17 00:00:00 2001 From: John Harwell Date: Thu, 27 Dec 2018 09:38:44 -0600 Subject: [PATCH 155/427] WB-834: Macros can now be stored in scene files. Small revision to Macro Manager to be less dependent upon Workbench specific code. --- src/Common/WuQMacroGroup.cxx | 19 ++++ src/Common/WuQMacroGroup.h | 2 + src/Files/SceneFile.cxx | 61 +++++++++++++ src/Files/SceneFile.h | 10 +++ src/Files/SceneFileSaxReader.cxx | 22 ++++- src/Files/SceneFileSaxReader.h | 2 + src/GuiQt/CMakeLists.txt | 3 + src/GuiQt/GuiMacroHelper.cxx | 134 ++++++++++++++++++++++++++++ src/GuiQt/GuiMacroHelper.h | 65 ++++++++++++++ src/GuiQt/GuiManager.cxx | 2 + src/GuiQt/WuQMacroCreateDialog.cxx | 15 +++- src/GuiQt/WuQMacroCreateDialog.h | 3 +- src/GuiQt/WuQMacroHelperInterface.h | 80 +++++++++++++++++ src/GuiQt/WuQMacroManager.cxx | 49 +++++----- src/GuiQt/WuQMacroManager.h | 5 ++ src/Scenes/SceneXmlElements.h | 5 ++ 16 files changed, 452 insertions(+), 25 deletions(-) create mode 100644 src/GuiQt/GuiMacroHelper.cxx create mode 100644 src/GuiQt/GuiMacroHelper.h create mode 100644 src/GuiQt/WuQMacroHelperInterface.h diff --git a/src/Common/WuQMacroGroup.cxx b/src/Common/WuQMacroGroup.cxx index e8cba4e36..26f528c3c 100644 --- a/src/Common/WuQMacroGroup.cxx +++ b/src/Common/WuQMacroGroup.cxx @@ -355,6 +355,25 @@ WuQMacroGroup::getMacroAtIndex(const int32_t index) const return m_macros[index]; } +/** + * @return True if this macro group contains the given macro + * + * @param macro + * Macro for inclusion testing + */ +bool +WuQMacroGroup::containsMacro(const WuQMacro* macro) +{ + const int32_t count = getNumberOfMacros(); + for (int32_t i = 0; i < count; i++) { + if (getMacroAtIndex(i) == macro) { + return true; + } + } + + return false; +} + /** * Delete the given macro from this group * diff --git a/src/Common/WuQMacroGroup.h b/src/Common/WuQMacroGroup.h index 43ec9d7b5..358a2f3d4 100644 --- a/src/Common/WuQMacroGroup.h +++ b/src/Common/WuQMacroGroup.h @@ -75,6 +75,8 @@ namespace caret { const WuQMacro* getMacroWithShortCutKey(const WuQMacroShortCutKeyEnum::Enum shortCutKey) const; + bool containsMacro(const WuQMacro* macro); + void deleteMacro(const WuQMacro* macro); void deleteMacroAtIndex(const int32_t index); diff --git a/src/Files/SceneFile.cxx b/src/Files/SceneFile.cxx index 7a2483036..08ef66f90 100644 --- a/src/Files/SceneFile.cxx +++ b/src/Files/SceneFile.cxx @@ -48,7 +48,10 @@ #include "SceneWriterXml.h" #include "SpecFile.h" #include "SystemUtilities.h" +#include "WuQMacroGroup.h" +#include "WuQMacroGroupXmlWriter.h" #include "XmlSaxParser.h" +#include "XmlUtilities.h" #include "XmlWriter.h" using namespace caret; @@ -72,6 +75,11 @@ SceneFile::SceneFile() m_balsaExtractToDirectoryName = ""; m_basePathType = SceneFileBasePathTypeEnum::AUTOMATIC; m_metadata = new GiftiMetaData(); + + static int counter = 1; + const AString macroGroupName("SceneFile_" + + AString::number(counter)); + m_macroGroup.reset(new WuQMacroGroup(macroGroupName)); } /** @@ -98,6 +106,7 @@ SceneFile::clear() CaretDataFile::clear(); m_metadata->clear(); + m_macroGroup->clear(); m_balsaStudyID = ""; m_balsaStudyTitle = ""; @@ -570,6 +579,20 @@ void SceneFile::setBasePathType(const SceneFileBasePathTypeEnum::Enum basePathTy } } +/** + * Set the name of the file. + * + * @param filename + * New name of file + */ +void +SceneFile::setFileName(const AString& filename) +{ + CaretDataFile::setFileName(filename); + + FileInformation fileInfo(filename); + m_macroGroup->setName(fileInfo.getFileName()); +} /** * Read the scene file. @@ -752,6 +775,21 @@ SceneFile::writeFile(const AString& filename) } xmlWriter.writeEndElement(); + /* + * Write macro group + */ + if (m_macroGroup->getNumberOfMacros() > 0) { + QString textString; + WuQMacroGroupXmlWriter macroWriter; + macroWriter.writeToString(m_macroGroup.get(), + textString); + textString = XmlUtilities::encodeXmlSpecialCharacters(textString); + xmlWriter.writeElementCharacters(SceneXmlElements::SCENE_FILE_MACRO_GROUP, + textString); +// xmlWriter.writeElementCData(SceneXmlElements::SCENE_FILE_MACRO_GROUP, +// textString); + } + // // Write scenes // @@ -1263,6 +1301,9 @@ SceneFile::isModified() const return true; } } + if (m_macroGroup->isModified()) { + return true; + } return false; } @@ -1277,4 +1318,24 @@ SceneFile::clearModified() for (auto scene : m_scenes) { scene->clearModified(); } + m_macroGroup->clearModified(); +} + +/** + * @return The macro group + */ +WuQMacroGroup* +SceneFile::getMacroGroup() +{ + return m_macroGroup.get(); } + +/** + * @return The macro group (const method) + */ +const WuQMacroGroup* +SceneFile::getMacroGroup() const +{ + return m_macroGroup.get(); +} + diff --git a/src/Files/SceneFile.h b/src/Files/SceneFile.h index 84e4d76e0..a9a7c4e8b 100644 --- a/src/Files/SceneFile.h +++ b/src/Files/SceneFile.h @@ -21,6 +21,7 @@ */ /*LICENSE_END*/ +#include #include #include "CaretDataFile.h" @@ -30,6 +31,7 @@ namespace caret { class Scene; + class WuQMacroGroup; class SceneFile : public CaretDataFile { @@ -49,6 +51,8 @@ namespace caret { void clear(); + virtual void setFileName(const AString& filename) override; + void readFile(const AString& filename); void writeFile(const AString& filename); @@ -155,6 +159,10 @@ namespace caret { virtual void clearModified() override; + WuQMacroGroup* getMacroGroup(); + + const WuQMacroGroup* getMacroGroup() const; + // ADD_NEW_METHODS_HERE /** Version of file */ @@ -194,6 +202,8 @@ namespace caret { /** The "extract to" directory name */ AString m_balsaExtractToDirectoryName; + std::unique_ptr m_macroGroup; + // ADD_NEW_MEMBERS_HERE /** Version of this SceneFile */ diff --git a/src/Files/SceneFileSaxReader.cxx b/src/Files/SceneFileSaxReader.cxx index ed8f8a27d..171f6f59d 100644 --- a/src/Files/SceneFileSaxReader.cxx +++ b/src/Files/SceneFileSaxReader.cxx @@ -32,10 +32,10 @@ #include "SceneInfoSaxReader.h" #include "ScenePathName.h" #include "SceneXmlElements.h" - #include "XmlAttributes.h" #include "XmlException.h" #include "XmlUtilities.h" +#include "WuQMacroGroupXmlReader.h" using namespace caret; @@ -121,6 +121,8 @@ SceneFileSaxReader::startElement(const AString& namespaceURI, throw e; } break; + case STATE_SCENE_FILE_MACRO_GROUP: + break; case STATE_SCENE_INFO_DIRECTORY: if (qName == SceneXmlElements::SCENE_INFO_TAG) { m_state = STATE_SCENE_INFO; @@ -200,6 +202,10 @@ SceneFileSaxReader::startElement(const AString& namespaceURI, m_scene); m_sceneSaxReader->startElement(namespaceURI, localName, qName, attributes); } + else if (qName == SceneXmlElements::SCENE_FILE_MACRO_GROUP) { + m_state = STATE_SCENE_FILE_MACRO_GROUP; + break; + } else { const AString msg = XmlUtilities::createInvalidChildElementMessage(SceneXmlElements::SCENE_TAG, qName); @@ -244,6 +250,20 @@ SceneFileSaxReader::endElement(const AString& namespaceURI, m_sceneSaxReader = NULL; } break; + case STATE_SCENE_FILE_MACRO_GROUP: + { + WuQMacroGroup* macroGroup = m_sceneFile->getMacroGroup(); + WuQMacroGroupXmlReader reader; + reader.readFromString(m_elementText, + macroGroup); + if (reader.hasError()) { + CaretLogSevere(reader.getErrorMessage()); + } + else if (reader.hasWarnings()) { + CaretLogWarning(reader.getWarningMessage()); + } + } + break; case STATE_SCENE_INFO_DIRECTORY: break; case STATE_SCENE_INFO_BALSA_STUDY_ID: diff --git a/src/Files/SceneFileSaxReader.h b/src/Files/SceneFileSaxReader.h index 531e1f5f9..e548c225b 100644 --- a/src/Files/SceneFileSaxReader.h +++ b/src/Files/SceneFileSaxReader.h @@ -78,6 +78,8 @@ namespace caret { STATE_NONE, /// processing SceneFile tag STATE_SCENE_FILE, + /// processing scene file macro group + STATE_SCENE_FILE_MACRO_GROUP, /// processing MetaData tag STATE_METADATA, /// processing SceneInfoDirectory tag diff --git a/src/GuiQt/CMakeLists.txt b/src/GuiQt/CMakeLists.txt index 4397f3c2a..43e70d069 100644 --- a/src/GuiQt/CMakeLists.txt +++ b/src/GuiQt/CMakeLists.txt @@ -367,6 +367,7 @@ GiftiLabelTableEditor.h GiftiLabelTableSelectionComboBox.h GroupAndNameHierarchyTreeWidgetItem.h GroupAndNameHierarchyViewController.h +GuiMacroHelper.h GuiManager.h HelpViewerDialog.h HyperLinkTextBrowser.h @@ -466,6 +467,7 @@ WuQMacroDialog.h WuQMacroEditorDialog.h WuQMacroExecutor.h WuQMacroExecutorOptions.h +WuQMacroHelperInterface.h WuQMacroManager.h WuQMacroMenu.h WuQMacroMouseEventWidgetInterface.h @@ -612,6 +614,7 @@ GiftiLabelTableEditor.cxx GiftiLabelTableSelectionComboBox.cxx GroupAndNameHierarchyTreeWidgetItem.cxx GroupAndNameHierarchyViewController.cxx +GuiMacroHelper.cxx GuiManager.cxx HelpViewerDialog.cxx HyperLinkTextBrowser.cxx diff --git a/src/GuiQt/GuiMacroHelper.cxx b/src/GuiQt/GuiMacroHelper.cxx new file mode 100644 index 000000000..ca664c0b8 --- /dev/null +++ b/src/GuiQt/GuiMacroHelper.cxx @@ -0,0 +1,134 @@ + +/*LICENSE_START*/ +/* + * Copyright (C) 2018 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + +#define __GUI_MACRO_HELPER_DECLARE__ +#include "GuiMacroHelper.h" +#undef __GUI_MACRO_HELPER_DECLARE__ + +#include "Brain.h" +#include "CaretAssert.h" +#include "CaretPreferences.h" +#include "EventManager.h" +#include "EventUserInterfaceUpdate.h" +#include "GuiManager.h" +#include "SceneFile.h" +#include "SessionManager.h" +#include "WuQMacroGroup.h" + +using namespace caret; + + + +/** + * \class caret::GuiMacroHelper + * \brief Implementation of WuQMacroHelperInterface that provides macro groups + * \ingroup GuiQt + */ + +/** + * Constructor. + */ +GuiMacroHelper::GuiMacroHelper() +: CaretObject(), +WuQMacroHelperInterface() +{ + +} + +/** + * Destructor. + */ +GuiMacroHelper::~GuiMacroHelper() +{ +} + +/** + * Get a description of this object's content. + * @return String describing this object's content. + */ +AString +GuiMacroHelper::toString() const +{ + return "GuiMacroHelper"; +} + +/** + * @return All available macro groups + */ +std::vector +GuiMacroHelper::getMacroGroups() +{ + std::vector macroGroups; + + CaretPreferences* preferences = SessionManager::get()->getCaretPreferences(); + CaretAssert(preferences); + macroGroups.push_back(preferences->getMacros()); + + const int32_t numSceneFiles = GuiManager::get()->getBrain()->getNumberOfSceneFiles(); + for (int32_t i = 0; i < numSceneFiles; i++) { + macroGroups.push_back(GuiManager::get()->getBrain()->getSceneFile(i)->getMacroGroup()); + } + + return macroGroups; +} + +/** + * Is called when the given macro is modified + * + * @param macro + * Macro that is modified + */ +void +GuiMacroHelper::macroWasModified(WuQMacro* macro) +{ + /* + * Need to write to preferences if macro is from preferences + */ + CaretPreferences* preferences = SessionManager::get()->getCaretPreferences(); + CaretAssert(preferences); + WuQMacroGroup* prefMacroGroup = preferences->getMacros(); + if (prefMacroGroup->containsMacro(macro)) { + preferences->writeMacros(); + } + + EventManager::get()->sendEvent(EventUserInterfaceUpdate().getPointer()); +} + +/** + * Is called when the given macro group is modified + * + * @param macroGroup + * Macro Group that is modified + */ +void +GuiMacroHelper::macroGroupWasModified(WuQMacroGroup* macroGroup) +{ + /** + * Need to write to preferences if macro group is from preferences + */ + CaretPreferences* preferences = SessionManager::get()->getCaretPreferences(); + CaretAssert(preferences); + if (macroGroup == preferences->getMacros()) { + preferences->writeMacros(); + } + + EventManager::get()->sendEvent(EventUserInterfaceUpdate().getPointer()); +} diff --git a/src/GuiQt/GuiMacroHelper.h b/src/GuiQt/GuiMacroHelper.h new file mode 100644 index 000000000..d86c37a03 --- /dev/null +++ b/src/GuiQt/GuiMacroHelper.h @@ -0,0 +1,65 @@ +#ifndef __GUI_MACRO_HELPER_H__ +#define __GUI_MACRO_HELPER_H__ + +/*LICENSE_START*/ +/* + * Copyright (C) 2018 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + + + +#include + +#include "CaretObject.h" +#include "WuQMacroHelperInterface.h" + + +namespace caret { + + class GuiMacroHelper : public CaretObject, public WuQMacroHelperInterface { + + public: + GuiMacroHelper(); + + virtual ~GuiMacroHelper(); + + GuiMacroHelper(const GuiMacroHelper&) = delete; + + GuiMacroHelper& operator=(const GuiMacroHelper&) = delete; + + virtual std::vector getMacroGroups(); + + virtual void macroWasModified(WuQMacro* macro); + + virtual void macroGroupWasModified(WuQMacroGroup* macroGroup); + + // ADD_NEW_METHODS_HERE + + virtual AString toString() const; + + private: + // ADD_NEW_MEMBERS_HERE + + }; + +#ifdef __GUI_MACRO_HELPER_DECLARE__ + // +#endif // __GUI_MACRO_HELPER_DECLARE__ + +} // namespace +#endif //__GUI_MACRO_HELPER_H__ diff --git a/src/GuiQt/GuiManager.cxx b/src/GuiQt/GuiManager.cxx index 599f41628..bc0e4aa18 100644 --- a/src/GuiQt/GuiManager.cxx +++ b/src/GuiQt/GuiManager.cxx @@ -86,6 +86,7 @@ #include "EventUserInterfaceUpdate.h" #include "FociPropertiesEditorDialog.h" #include "GapsAndMarginsDialog.h" +#include "GuiMacroHelper.h" #include "HelpViewerDialog.h" #include "IdentifiedItemNode.h" #include "IdentifiedItemVoxel.h" @@ -197,6 +198,7 @@ GuiManager::initializeGuiManager() WuQMacroClassTypeEnum::addWidgetClassNameAlias("QTabBar", "caret::WuQTabBar"); WuQMacroManager::instance()->addParentObject(this); + WuQMacroManager::instance()->setMacroHelper(new GuiMacroHelper()); /* * Windows vector never changes size diff --git a/src/GuiQt/WuQMacroCreateDialog.cxx b/src/GuiQt/WuQMacroCreateDialog.cxx index ff8deeea2..a60257f88 100644 --- a/src/GuiQt/WuQMacroCreateDialog.cxx +++ b/src/GuiQt/WuQMacroCreateDialog.cxx @@ -74,13 +74,24 @@ WuQMacroCreateDialog::WuQMacroCreateDialog(QWidget* parent) m_macroDescriptionTextEdit = new QPlainTextEdit(); m_macroDescriptionTextEdit->setFixedHeight(100); + int32_t selectedMacroGroupIndex(0); m_macroGroupComboBox = new QComboBox(); for (auto mg : m_macroGroups) { + if (mg->getUniqueIdentifier() == s_lastSelectedMacroGroupIdentifier) { + selectedMacroGroupIndex = m_macroGroupComboBox->count(); + } m_macroGroupComboBox->addItem(mg->getName()); } + if ((selectedMacroGroupIndex >= 0) + && (selectedMacroGroupIndex < m_macroGroupComboBox->count())) { + m_macroGroupComboBox->setCurrentIndex(selectedMacroGroupIndex); + } QGridLayout* gridLayout = new QGridLayout(); int row = 0; + gridLayout->addWidget(macroGroupLabel, row, 0); + gridLayout->addWidget(m_macroGroupComboBox, row, 1, 1, 2); + row++; gridLayout->addWidget(nameLabel, row, 0); gridLayout->addWidget(m_macroNameLineEdit, row, 1, 1, 2); row++; @@ -91,9 +102,6 @@ WuQMacroCreateDialog::WuQMacroCreateDialog(QWidget* parent) gridLayout->addWidget(descriptionLabel, row, 0); gridLayout->addWidget(m_macroDescriptionTextEdit, row, 1, 1, 2); row++; - gridLayout->addWidget(macroGroupLabel, row, 0); - gridLayout->addWidget(m_macroGroupComboBox, row, 1, 1, 2); - row++; m_dialogButtonBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel); @@ -177,6 +185,7 @@ WuQMacroCreateDialog::done(int r) if (groupIndex >= 0) { WuQMacroGroup* macroGroup = m_macroGroups[groupIndex]; macroGroup->addMacro(m_macro); + s_lastSelectedMacroGroupIdentifier = macroGroup->getUniqueIdentifier(); } } diff --git a/src/GuiQt/WuQMacroCreateDialog.h b/src/GuiQt/WuQMacroCreateDialog.h index 2a9d1b6cb..231ce91b1 100644 --- a/src/GuiQt/WuQMacroCreateDialog.h +++ b/src/GuiQt/WuQMacroCreateDialog.h @@ -78,10 +78,11 @@ namespace caret { WuQMacro* m_macro = NULL; + static QString s_lastSelectedMacroGroupIdentifier; }; #ifdef __WU_Q_MACRO_CREATE_DIALOG_DECLARE__ - // + QString WuQMacroCreateDialog::s_lastSelectedMacroGroupIdentifier = ""; #endif // __WU_Q_MACRO_CREATE_DIALOG_DECLARE__ } // namespace diff --git a/src/GuiQt/WuQMacroHelperInterface.h b/src/GuiQt/WuQMacroHelperInterface.h new file mode 100644 index 000000000..d8071a710 --- /dev/null +++ b/src/GuiQt/WuQMacroHelperInterface.h @@ -0,0 +1,80 @@ +#ifndef __WU_Q_MACRO_HELPER_INTERFACE_H__ +#define __WU_Q_MACRO_HELPER_INTERFACE_H__ + +/*LICENSE_START*/ +/* + * Copyright (C) 2018 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + +/** + * \class caret::WuQMacroHelperInterface + * \brief Interface that provides Macro Groups and is used by WuQMacroManager + * \ingroup GuiQt + */ + +#include + +namespace caret { + class WuQMacro; + class WuQMacroGroup; + + class WuQMacroHelperInterface { + + public: + WuQMacroHelperInterface() { } + + virtual ~WuQMacroHelperInterface() { } + + WuQMacroHelperInterface(const WuQMacroHelperInterface&) = delete; + + WuQMacroHelperInterface& operator=(const WuQMacroHelperInterface&) = delete; + + /** + * @return All available macro groups + */ + virtual std::vector getMacroGroups() = 0; + + /** + * Is called when the given macro is modified + * + * @param macro + * Macro that is modified + */ + virtual void macroWasModified(WuQMacro* macro) = 0; + + /** + * Is called when the given macro group is modified + * + * @param macroGroup + * Macro Group that is modified + */ + virtual void macroGroupWasModified(WuQMacroGroup* macroGroup) = 0; + + // ADD_NEW_METHODS_HERE + + private: + // ADD_NEW_MEMBERS_HERE + + }; + +#ifdef __WU_Q_MACRO_HELPER_INTERFACE_DECLARE__ + // +#endif // __WU_Q_MACRO_HELPER_INTERFACE_DECLARE__ + +} // namespace +#endif //__WU_Q_MACRO_HELPER_INTERFACE_H__ diff --git a/src/GuiQt/WuQMacroManager.cxx b/src/GuiQt/WuQMacroManager.cxx index 7750c53fc..a9a58d7f5 100644 --- a/src/GuiQt/WuQMacroManager.cxx +++ b/src/GuiQt/WuQMacroManager.cxx @@ -35,9 +35,7 @@ #include "CaretAssert.h" #include "CaretLogger.h" #include "CaretFileDialog.h" -#include "CaretPreferences.h" #include "DataFileException.h" -#include "SessionManager.h" #include "WuQMacro.h" #include "WuQMacroCommand.h" #include "WuQMacroCreateDialog.h" @@ -47,6 +45,7 @@ #include "WuQMacroGroup.h" #include "WuQMacroMouseEventInfo.h" #include "WuQMacroAttributesDialog.h" +#include "WuQMacroHelperInterface.h" #include "WuQMacroEditorDialog.h" #include "WuQMacroExecutorOptions.h" #include "WuQMacroSignalWatcher.h" @@ -117,6 +116,18 @@ WuQMacroManager::instance() return s_singletonMacroManager; } +/** + * Set the macro helper that provides the macro groups + * + * @param macroHelper + * The macro helper. Ownership will be taken of macro helper + * and it will be destoryed when this instance is destroyed. + */ +void +WuQMacroManager::setMacroHelper(WuQMacroHelperInterface* macroHelper) +{ + m_macroHelper.reset(macroHelper); +} /** * @return Name of this macro manager @@ -412,11 +423,9 @@ std::vector WuQMacroManager::getMacroGroups() const { std::vector macroGroups; - - CaretPreferences* preferences = SessionManager::get()->getCaretPreferences(); - CaretAssert(preferences); - macroGroups.push_back(preferences->getMacros()); - + if (m_macroHelper) { + macroGroups = m_macroHelper->getMacroGroups(); + } return macroGroups; } @@ -447,11 +456,11 @@ WuQMacroManager::stopRecordingNewMacro() { CaretAssert(m_mode == WuQMacroModeEnum::RECORDING); m_mode = WuQMacroModeEnum::OFF; - m_macroBeingRecorded = NULL; - CaretPreferences* preferences = SessionManager::get()->getCaretPreferences(); - CaretAssert(preferences); - preferences->writeMacros(); + if (m_macroHelper) { + m_macroHelper->macroWasModified(m_macroBeingRecorded); + } + m_macroBeingRecorded = NULL; updateNonModalDialogs(); } @@ -566,9 +575,9 @@ WuQMacroManager::editMacroAttributes(QWidget* parent, parent); if (attributesDialog.exec() == WuQMacroDialog::Accepted) { if (attributesDialog.isMacroModified()) { - CaretPreferences* preferences = SessionManager::get()->getCaretPreferences(); - CaretAssert(preferences); - preferences->writeMacros(); + if (m_macroHelper) { + m_macroHelper->macroWasModified(macro); + } return true; } } @@ -600,9 +609,9 @@ WuQMacroManager::deleteMacro(QWidget* parent, QMessageBox::Ok | QMessageBox::Cancel, QMessageBox::Ok) == QMessageBox::Ok) { macroGroup->deleteMacro(macro); - CaretPreferences* preferences = SessionManager::get()->getCaretPreferences(); - CaretAssert(preferences); - preferences->writeMacros(); + if (m_macroHelper) { + m_macroHelper->macroGroupWasModified(macroGroup); + } return true; } @@ -726,9 +735,9 @@ WuQMacroManager::editMacroCommands(QWidget* parent, parent); if (editorDialog.exec() == WuQMacroDialog::Accepted) { if (editorDialog.isMacroModified()) { - CaretPreferences* preferences = SessionManager::get()->getCaretPreferences(); - CaretAssert(preferences); - preferences->writeMacros(); + if (m_macroHelper) { + m_macroHelper->macroWasModified(macro); + } return true; } } diff --git a/src/GuiQt/WuQMacroManager.h b/src/GuiQt/WuQMacroManager.h index cced1d4d8..145377f49 100644 --- a/src/GuiQt/WuQMacroManager.h +++ b/src/GuiQt/WuQMacroManager.h @@ -42,6 +42,7 @@ namespace caret { class WuQMacroDialog; class WuQMacroExecutorOptions; class WuQMacroGroup; + class WuQMacroHelperInterface; class WuQMacroSignalWatcher; class WuQMacroManager : public QObject { @@ -56,6 +57,8 @@ namespace caret { WuQMacroManager& operator=(const WuQMacroManager&) = delete; + void setMacroHelper(WuQMacroHelperInterface* macroHelper); + QString getName() const; bool addMacroSupportToObject(QObject* object); @@ -147,6 +150,8 @@ namespace caret { std::unique_ptr m_executorOptions; + std::unique_ptr m_macroHelper; + // ADD_NEW_MEMBERS_HERE }; diff --git a/src/Scenes/SceneXmlElements.h b/src/Scenes/SceneXmlElements.h index 5ea4d3423..cded5149a 100644 --- a/src/Scenes/SceneXmlElements.h +++ b/src/Scenes/SceneXmlElements.h @@ -177,6 +177,11 @@ namespace caret { */ static const AString SCENE_INFO_BASE_PATH_TYPE = "BasePathType"; + /** + * XML Tag for Macros in Scene File + */ + static const AString SCENE_FILE_MACRO_GROUP = "SceneFileMacroGroup"; + } // namespace SceneXmlElements } // namespace caret -- GitLab From ab90639594de5e68460ed88821502ed97964d548 Mon Sep 17 00:00:00 2001 From: John Harwell Date: Thu, 27 Dec 2018 12:00:17 -0600 Subject: [PATCH 156/427] WB-834: Moved execution of each command type to its own method in WuQMacroExecutor. For QTabBar and QTabWidget, try first to select the tab by the tab's name but if that fails, use the tab index. --- src/Files/SceneFile.cxx | 2 - src/GuiQt/WuQMacroExecutor.cxx | 1289 ++++++++++++++++++--------- src/GuiQt/WuQMacroExecutor.h | 89 +- src/GuiQt/WuQMacroSignalWatcher.cxx | 14 +- 4 files changed, 942 insertions(+), 452 deletions(-) diff --git a/src/Files/SceneFile.cxx b/src/Files/SceneFile.cxx index 08ef66f90..5752c9e9a 100644 --- a/src/Files/SceneFile.cxx +++ b/src/Files/SceneFile.cxx @@ -786,8 +786,6 @@ SceneFile::writeFile(const AString& filename) textString = XmlUtilities::encodeXmlSpecialCharacters(textString); xmlWriter.writeElementCharacters(SceneXmlElements::SCENE_FILE_MACRO_GROUP, textString); -// xmlWriter.writeElementCData(SceneXmlElements::SCENE_FILE_MACRO_GROUP, -// textString); } // diff --git a/src/GuiQt/WuQMacroExecutor.cxx b/src/GuiQt/WuQMacroExecutor.cxx index 9e9c0a9f2..0c4cc5770 100644 --- a/src/GuiQt/WuQMacroExecutor.cxx +++ b/src/GuiQt/WuQMacroExecutor.cxx @@ -352,8 +352,8 @@ WuQMacroExecutor::runMacro(const WuQMacro* macro, /** * Run the commands in the given macro. * - * @param macro - * Macro that is run + * @param macroCommand + * Macro command that is run * @param window * The window from which macro was launched * @param stopOnErrorFlag @@ -374,489 +374,65 @@ WuQMacroExecutor::runMacroCommand(const WuQMacroCommand* macroCommand, CaretAssert(object); const WuQMacroClassTypeEnum::Enum classType = macroCommand->getClassType(); - const WuQMacroDataValueTypeEnum::Enum dataValueType = macroCommand->getDataType(); const QVariant dataValue = macroCommand->getDataValue(); - const WuQMacroDataValueTypeEnum::Enum dataValueTypeTwo = macroCommand->getDataTypeTwo(); const QVariant dataValueTwo = macroCommand->getDataValueTwo(); QString objectErrorMessage; bool notFoundFlag(false); switch (classType) { case WuQMacroClassTypeEnum::ACTION: - { - QAction* action = qobject_cast(object); - if (action != NULL) { - moveMouseToWidget(action); - CaretAssert(dataValueType == WuQMacroDataValueTypeEnum::BOOLEAN); - WuQMacroSignalEmitter signalEmitter; - signalEmitter.emitQActionSignal(action, - dataValue.toBool()); - } - else { - notFoundFlag = true; - } - } + runActionCommand(macroCommand, object, objectErrorMessage, notFoundFlag); break; case WuQMacroClassTypeEnum::ACTION_GROUP: - { - QActionGroup* actionGroup = qobject_cast(object); - if (actionGroup != NULL) { - moveMouseToWidget(actionGroup); - CaretAssert(dataValueType == WuQMacroDataValueTypeEnum::INTEGER); - CaretAssert(dataValueTypeTwo == WuQMacroDataValueTypeEnum::STRING); - - QAction* textAction(NULL); - QAction* indexAction(NULL); - QList actions = actionGroup->actions(); - for (int32_t i = 0; i < actions.size(); i++) { - QAction* actionAtIndex = actions.at(i); - if (actionAtIndex->text() == dataValueTwo.toString()) { - textAction = actionAtIndex; - } - if (dataValue.toInt() == i) { - indexAction = actionAtIndex; - } - } - - WuQMacroSignalEmitter signalEmitter; - if (textAction != NULL) { - signalEmitter.emitActionGroupSignal(actionGroup, - textAction->text()); - } - else if (indexAction != NULL) { - signalEmitter.emitActionGroupSignal(actionGroup, - indexAction->text()); - } - else { - objectErrorMessage = ("For QActionGroup \"" - + object->objectName() - + "\", unable to find action with text \"" - + dataValueTwo.toString() - + "\" or index=" - + dataValue.toInt()); - } - } - else { - notFoundFlag = true; - } - } + runActionGroupCommand(macroCommand, object, objectErrorMessage, notFoundFlag); break; case WuQMacroClassTypeEnum::BUTTON_GROUP: - { - QButtonGroup* buttonGroup = qobject_cast(object); - if (buttonGroup != NULL) { - CaretAssert(dataValueType == WuQMacroDataValueTypeEnum::INTEGER); - CaretAssert(dataValueTypeTwo == WuQMacroDataValueTypeEnum::STRING); - - QAbstractButton* textButton(NULL); - QAbstractButton* indexButton(NULL); - QList buttons = buttonGroup->buttons(); - for (int32_t i = 0; i < buttons.size(); i++) { - QAbstractButton* buttonAtIndex = buttons.at(i); - if (buttonAtIndex->text() == dataValueTwo.toString()) { - textButton = buttonAtIndex; - } - if (dataValue.toInt() == i) { - indexButton = buttonAtIndex; - } - } - - WuQMacroSignalEmitter signalEmitter; - if (textButton != NULL) { - moveMouseToWidget(textButton); - signalEmitter.emitQButtonGroupSignal(buttonGroup, - textButton->text()); - } - else if (indexButton != NULL) { - moveMouseToWidget(indexButton); - signalEmitter.emitQButtonGroupSignal(buttonGroup, - indexButton->text()); - } - else { - objectErrorMessage = ("For QButtonGroup \"" - + object->objectName() - + "\", unable to find button with text \"" - + dataValueTwo.toString() - + "\" or index=" - + dataValue.toInt()); - } - } - else { - notFoundFlag = true; - } - } + runButtonGroupCommand(macroCommand, object, objectErrorMessage, notFoundFlag); break; case WuQMacroClassTypeEnum::CHECK_BOX: - { - QCheckBox* checkBox = qobject_cast(object); - if (checkBox != NULL) { - moveMouseToWidget(checkBox); - CaretAssert(dataValueType == WuQMacroDataValueTypeEnum::BOOLEAN); - WuQMacroSignalEmitter signalEmitter; - signalEmitter.emitQCheckBoxSignal(checkBox, - dataValue.toBool()); - } - else { - notFoundFlag = true; - } - } + runCheckBoxCommand(macroCommand, object, objectErrorMessage, notFoundFlag); break; case WuQMacroClassTypeEnum::COMBO_BOX: - { - QComboBox* comboBox = qobject_cast(object); - if (comboBox != NULL) { - CaretAssert(dataValueType == WuQMacroDataValueTypeEnum::INTEGER); - CaretAssert(dataValueTypeTwo == WuQMacroDataValueTypeEnum::STRING); - int textIndex(-1); - int indexIndex(-1); - for (int32_t i = 0; i < comboBox->count(); i++) { - if (comboBox->itemText(i) == dataValueTwo.toString()) { - textIndex = i; - } - else if (i == dataValue.toInt()) { - indexIndex = i; - } - } - moveMouseToWidget(comboBox); - WuQMacroSignalEmitter signalEmitter; - if (textIndex >= 0) { - signalEmitter.emitQComboBoxSignal(comboBox, - textIndex); - } - else if (indexIndex >= 0) { - signalEmitter.emitQComboBoxSignal(comboBox, - indexIndex); - } - else { - objectErrorMessage = ("For ComboBox \"" - + object->objectName() - + "\", unable to find item with text \"" - + dataValueTwo.toString() - + "\" or index=" - + dataValue.toInt()); - } - } - else { - notFoundFlag = true; - } - } + runComboBoxCommand(macroCommand, object, errorMessageOut, notFoundFlag); break; case WuQMacroClassTypeEnum::DOUBLE_SPIN_BOX: - { - QDoubleSpinBox* doubleSpinBox = qobject_cast(object); - if (doubleSpinBox != NULL) { - CaretAssert(dataValueType == WuQMacroDataValueTypeEnum::FLOAT); - moveMouseToWidget(doubleSpinBox); - WuQMacroSignalEmitter signalEmitter; - signalEmitter.emitQDoubleSpinBoxSignal(doubleSpinBox, - dataValue.toDouble()); - } - else { - notFoundFlag = true; - } - } + runDoubleSpinBoxCommand(macroCommand, object, errorMessageOut, notFoundFlag); break; case WuQMacroClassTypeEnum::INVALID: CaretAssert(0); break; case WuQMacroClassTypeEnum::LINE_EDIT: - { - QLineEdit* lineEdit = qobject_cast(object); - if (lineEdit != NULL) { - moveMouseToWidget(lineEdit); - CaretAssert(dataValueType == WuQMacroDataValueTypeEnum::STRING); - WuQMacroSignalEmitter signalEmitter; - signalEmitter.emitQLineEditSignal(lineEdit, - dataValue.toString()); - } - else { - notFoundFlag = true; - } - } + runLineEditCommand(macroCommand, object, errorMessageOut, notFoundFlag); break; case WuQMacroClassTypeEnum::LIST_WIDGET: - { - QListWidget* listWidget = qobject_cast(object); - if (listWidget != NULL) { - CaretAssert(dataValueType == WuQMacroDataValueTypeEnum::INTEGER); - CaretAssert(dataValueTypeTwo == WuQMacroDataValueTypeEnum::STRING); - - QListWidgetItem* textItem(NULL); - QListWidgetItem* indexItem(NULL); - for (int32_t i = 0; i < listWidget->count(); i++) { - QListWidgetItem* itemAtIndex = listWidget->item(i); - if (itemAtIndex->text() == dataValueTwo.toString()) { - textItem = itemAtIndex; - } - if (dataValue.toInt() == i) { - indexItem = itemAtIndex; - } - } - - moveMouseToWidget(listWidget); - - WuQMacroSignalEmitter signalEmitter; - if (textItem != NULL) { - signalEmitter.emitQListWidgetSignal(listWidget, - textItem->text()); - } - else if (indexItem != NULL) { - - signalEmitter.emitQListWidgetSignal(listWidget, - indexItem->text()); - } - else { - objectErrorMessage = ("For QListWidget \"" - + object->objectName() - + "\", unable to find item with text \"" - + dataValueTwo.toString() - + "\" or index=" - + dataValue.toInt()); - } - } - else { - notFoundFlag = true; - } - } - break; + runListWidgetCommand(macroCommand, object, errorMessageOut, notFoundFlag); + break; case WuQMacroClassTypeEnum::MENU: - { - QMenu* menu = qobject_cast(object); - if (menu != NULL) { - CaretAssert(dataValueType == WuQMacroDataValueTypeEnum::INTEGER); - CaretAssert(dataValueTypeTwo == WuQMacroDataValueTypeEnum::STRING); - - QAction* textAction(NULL); - QAction* indexAction(NULL); - QList actions = menu->actions(); - for (int32_t i = 0; i < actions.size(); i++) { - QAction* actionAtIndex = actions.at(i); - if (actionAtIndex->text() == dataValueTwo.toString()) { - textAction = actionAtIndex; - } - if (dataValue.toInt() == i) { - indexAction = actionAtIndex; - } - } - - moveMouseToWidget(menu); - - WuQMacroSignalEmitter signalEmitter; - if (textAction != NULL) { - signalEmitter.emitQMenuSignal(menu, - textAction->text()); - } - else if (indexAction != NULL) { - signalEmitter.emitQMenuSignal(menu, - indexAction->text()); - } - else { - objectErrorMessage = ("For QMenu \"" - + object->objectName() - + "\", unable to find action with text \"" - + dataValueTwo.toString() - + "\" or index=" - + dataValue.toInt()); - } - } - else { - notFoundFlag = true; - } - } + runMenuCommand(macroCommand, object, errorMessageOut, notFoundFlag); break; case WuQMacroClassTypeEnum::MOUSE_USER_EVENT: - { - QWidget* widget = qobject_cast(object); - if (widget != NULL) { - WuQMacroMouseEventWidgetInterface* mouseInterface = dynamic_cast(widget); - if (mouseInterface != NULL) { - const QSize currentWidgetSize = widget->size(); - const WuQMacroMouseEventInfo* mouseEventInfo = macroCommand->getMouseEventInfo(); - CaretAssert(mouseEventInfo); - - int32_t adjustedLocalX(0); - int32_t adjustedLocalY(0); - mouseEventInfo->getLocalPositionRescaledToWidgetSize(currentWidgetSize.width(), - currentWidgetSize.height(), - adjustedLocalX, - adjustedLocalY); - - QEvent::Type qtEventType = QEvent::None; - switch (mouseEventInfo->getMouseEventType()) { - case WuQMacroMouseEventTypeEnum::BUTTON_PRESS: - qtEventType = QEvent::MouseButtonPress; - break; - case WuQMacroMouseEventTypeEnum::BUTTON_RELEASE: - qtEventType = QEvent::MouseButtonRelease; - break; - case WuQMacroMouseEventTypeEnum::DOUBLE_CLICK: - qtEventType = QEvent::MouseButtonDblClick; - break; - case WuQMacroMouseEventTypeEnum::MOVE: - qtEventType = QEvent::MouseMove; - break; - } - - moveMouseToWidgetXY(widget, - adjustedLocalX, - adjustedLocalY, - false); - - QMouseEvent qtMouseEvent(qtEventType, - QPointF(adjustedLocalX, - adjustedLocalY), - static_cast(mouseEventInfo->getMouseButton()), - static_cast(mouseEventInfo->getMouseButtonsMask()), - static_cast(mouseEventInfo->getKeyboardModifiersMask())); - mouseInterface->processMouseEventFromMacro(&qtMouseEvent); - } - else { - errorMessageOut = ("ERROR: Unable to cast object named " - + object->objectName() - + " to WuQMacroMouseEventWidgetInterface"); - return false; - } - } - else { - errorMessageOut = ("ERROR: Unable to cast object named " - + object->objectName() - + " to QWidget"); - return false; - } - } + return runMouseCommand(macroCommand, object, errorMessageOut, notFoundFlag); break; case WuQMacroClassTypeEnum::PUSH_BUTTON: - { - QPushButton* pushButton = qobject_cast(object); - if (pushButton != NULL) { - moveMouseToWidget(pushButton); - CaretAssert(dataValueType == WuQMacroDataValueTypeEnum::BOOLEAN); - WuQMacroSignalEmitter signalEmitter; - signalEmitter.emitQPushButtonSignal(pushButton, - dataValue.toBool()); - } - else { - notFoundFlag = true; - } - } + runPushButtonCommand(macroCommand, object, errorMessageOut, notFoundFlag); break; case WuQMacroClassTypeEnum::RADIO_BUTTON: - { - QRadioButton* radioButton = qobject_cast(object); - if (radioButton != NULL) { - moveMouseToWidget(radioButton); - CaretAssert(dataValueType == WuQMacroDataValueTypeEnum::BOOLEAN); - WuQMacroSignalEmitter signalEmitter; - signalEmitter.emitQRadioButtonSignal(radioButton, - dataValue.toBool()); - } - else { - notFoundFlag = true; - } - } + runRadioButtonCommand(macroCommand, object, errorMessageOut, notFoundFlag); break; case WuQMacroClassTypeEnum::SLIDER: - { - QSlider* slider = qobject_cast(object); - if (slider != NULL) { - moveMouseToWidget(slider); - CaretAssert(dataValueType == WuQMacroDataValueTypeEnum::INTEGER); - WuQMacroSignalEmitter signalEmitter; - signalEmitter.emitQSliderSignal(slider, - dataValue.toInt()); - } - else { - notFoundFlag = true; - } - } + runSliderCommand(macroCommand, object, errorMessageOut, notFoundFlag); break; case WuQMacroClassTypeEnum::SPIN_BOX: - { - QSpinBox* spinBox = qobject_cast(object); - if (spinBox != NULL) { - moveMouseToWidget(spinBox); - CaretAssert(dataValueType == WuQMacroDataValueTypeEnum::INTEGER); - WuQMacroSignalEmitter signalEmitter; - signalEmitter.emitQSpinBoxSignal(spinBox, - dataValue.toInt()); - } - else { - notFoundFlag = true; - } - } + runSpinBoxCommand(macroCommand, object, errorMessageOut, notFoundFlag); break; case WuQMacroClassTypeEnum::TAB_BAR: - { - QTabBar* tabBar = qobject_cast(object); - if (tabBar != NULL) { - CaretAssert(dataValueType == WuQMacroDataValueTypeEnum::INTEGER); - const int32_t tabIndex = dataValue.toInt(); - if (tabBar->isTabEnabled(tabIndex)) { - moveMouseToTabBarTab(tabBar, tabIndex); - WuQMacroSignalEmitter signalEmitter; - signalEmitter.emitQTabBarSignal(tabBar, - tabIndex); - } - else { - objectErrorMessage = ("QTabWidget \"" - + object->objectName() - + "\", tab \"" - + QString::number(tabIndex + 1) - + "\" with text \"" - + tabBar->tabText(tabIndex) - + "\" is disabled"); - } - - } - else { - notFoundFlag = true; - } - } + runTabBarCommand(macroCommand, object, errorMessageOut, notFoundFlag); break; case WuQMacroClassTypeEnum::TAB_WIDGET: - { - QTabWidget* tabWidget = qobject_cast(object); - if (tabWidget != NULL) { - CaretAssert(dataValueType == WuQMacroDataValueTypeEnum::INTEGER); - QTabBar* tabBar = tabWidget->tabBar(); - CaretAssert(tabBar); - const int32_t tabIndex = dataValue.toInt(); - if (tabWidget->isTabEnabled(tabIndex)) { - moveMouseToTabBarTab(tabBar, - tabIndex); - WuQMacroSignalEmitter signalEmitter; - signalEmitter.emitQTabWidgetSignal(tabWidget, - tabIndex); - } - else { - objectErrorMessage = ("QTabWidget \"" - + object->objectName() - + "\", tab \"" - + QString::number(tabIndex + 1) - + "\" with text \"" - + tabWidget->tabText(tabIndex) - + "\" is disabled"); - } - } - else { - notFoundFlag = true; - } - } + runTabWidgetCommand(macroCommand, object, errorMessageOut, notFoundFlag); break; case WuQMacroClassTypeEnum::TOOL_BUTTON: - { - QToolButton* toolButton = qobject_cast(object); - if (toolButton != NULL) { - moveMouseToWidget(toolButton); - CaretAssert(dataValueType == WuQMacroDataValueTypeEnum::BOOLEAN); - WuQMacroSignalEmitter signalEmitter; - signalEmitter.emitQToolButtonSignal(toolButton, - dataValue.toBool()); - } - else { - notFoundFlag = true; - } - } + runToolButtonCommand(macroCommand, object, errorMessageOut, notFoundFlag); break; } @@ -875,4 +451,829 @@ WuQMacroExecutor::runMacroCommand(const WuQMacroCommand* macroCommand, return true; } +/** + * Run a selection command + * + * @param macroCommand + * Macro command that is run + * @param object + * The object that is cast to specific object/widget + * @param errorMessageOut + * Error message from execution + * @param castFailureFlagOut + * Set to true if unable to cast object to widget type + */ +void +WuQMacroExecutor::runActionCommand(const WuQMacroCommand* macroCommand, + QObject* object, + QString& /*errorMessageOut*/, + bool& castFailureFlagOut) const +{ + QAction* action = qobject_cast(object); + if (action != NULL) { + moveMouseToWidget(action); + CaretAssert(macroCommand->getDataType() == WuQMacroDataValueTypeEnum::BOOLEAN); + WuQMacroSignalEmitter signalEmitter; + signalEmitter.emitQActionSignal(action, + macroCommand->getDataValue().toBool()); + } + else { + castFailureFlagOut = true; + } +} + +/** + * Run a selection command + * + * @param macroCommand + * Macro command that is run + * @param object + * The object that is cast to specific object/widget + * @param errorMessageOut + * Error message from execution + * @param castFailureFlagOut + * Set to true if unable to cast object to widget type + */ +void +WuQMacroExecutor::runActionGroupCommand(const WuQMacroCommand* macroCommand, + QObject* object, + QString& errorMessageOut, + bool& castFailureFlagOut) const +{ + QActionGroup* actionGroup = qobject_cast(object); + if (actionGroup != NULL) { + moveMouseToWidget(actionGroup); + CaretAssert(macroCommand->getDataType() == WuQMacroDataValueTypeEnum::INTEGER); + CaretAssert(macroCommand->getDataTypeTwo() == WuQMacroDataValueTypeEnum::STRING); + + QAction* textAction(NULL); + QAction* indexAction(NULL); + QList actions = actionGroup->actions(); + for (int32_t i = 0; i < actions.size(); i++) { + QAction* actionAtIndex = actions.at(i); + if (actionAtIndex->text() == macroCommand->getDataValueTwo().toString()) { + textAction = actionAtIndex; + } + if (macroCommand->getDataValue().toInt() == i) { + indexAction = actionAtIndex; + } + } + + WuQMacroSignalEmitter signalEmitter; + if (textAction != NULL) { + signalEmitter.emitActionGroupSignal(actionGroup, + textAction->text()); + } + else if (indexAction != NULL) { + signalEmitter.emitActionGroupSignal(actionGroup, + indexAction->text()); + } + else { + errorMessageOut = ("For QActionGroup \"" + + object->objectName() + + "\", unable to find action with text \"" + + macroCommand->getDataValueTwo().toString() + + "\" or index=" + + macroCommand->getDataValue().toInt()); + } + } + else { + castFailureFlagOut = true; + } +} + +/** + * Run a selection command + * + * @param macroCommand + * Macro command that is run + * @param object + * The object that is cast to specific object/widget + * @param errorMessageOut + * Error message from execution + * @param castFailureFlagOut + * Set to true if unable to cast object to widget type + */ +void +WuQMacroExecutor::runButtonGroupCommand(const WuQMacroCommand* macroCommand, + QObject* object, + QString& errorMessageOut, + bool& castFailureFlagOut) const +{ + QButtonGroup* buttonGroup = qobject_cast(object); + if (buttonGroup != NULL) { + CaretAssert(macroCommand->getDataType() == WuQMacroDataValueTypeEnum::INTEGER); + CaretAssert(macroCommand->getDataTypeTwo() == WuQMacroDataValueTypeEnum::STRING); + const QVariant dataValue = macroCommand->getDataValue(); + const QVariant dataValueTwo = macroCommand->getDataValueTwo(); + + QAbstractButton* textButton(NULL); + QAbstractButton* indexButton(NULL); + QList buttons = buttonGroup->buttons(); + for (int32_t i = 0; i < buttons.size(); i++) { + QAbstractButton* buttonAtIndex = buttons.at(i); + if (buttonAtIndex->text() == dataValueTwo.toString()) { + textButton = buttonAtIndex; + } + if (dataValue.toInt() == i) { + indexButton = buttonAtIndex; + } + } + + WuQMacroSignalEmitter signalEmitter; + if (textButton != NULL) { + moveMouseToWidget(textButton); + signalEmitter.emitQButtonGroupSignal(buttonGroup, + textButton->text()); + } + else if (indexButton != NULL) { + moveMouseToWidget(indexButton); + signalEmitter.emitQButtonGroupSignal(buttonGroup, + indexButton->text()); + } + else { + errorMessageOut = ("For QButtonGroup \"" + + object->objectName() + + "\", unable to find button with text \"" + + dataValueTwo.toString() + + "\" or index=" + + dataValue.toInt()); + } + } + else { + castFailureFlagOut = true; + } +} + +/** + * Run a selection command + * + * @param macroCommand + * Macro command that is run + * @param object + * The object that is cast to specific object/widget + * @param errorMessageOut + * Error message from execution + * @param castFailureFlagOut + * Set to true if unable to cast object to widget type + */ +void +WuQMacroExecutor::runCheckBoxCommand(const WuQMacroCommand* macroCommand, + QObject* object, + QString& /*errorMessageOut*/, + bool& castFailureFlagOut) const +{ + QCheckBox* checkBox = qobject_cast(object); + if (checkBox != NULL) { + moveMouseToWidget(checkBox); + CaretAssert(macroCommand->getDataType() == WuQMacroDataValueTypeEnum::BOOLEAN); + WuQMacroSignalEmitter signalEmitter; + signalEmitter.emitQCheckBoxSignal(checkBox, + macroCommand->getDataValue().toBool()); + } + else { + castFailureFlagOut = true; + } +} + + +/** + * Run a selection command + * + * @param macroCommand + * Macro command that is run + * @param object + * The object that is cast to specific object/widget + * @param errorMessageOut + * Error message from execution + * @param castFailureFlagOut + * Set to true if unable to cast object to widget type + */ +void +WuQMacroExecutor::runComboBoxCommand(const WuQMacroCommand* macroCommand, + QObject* object, + QString& errorMessageOut, + bool& castFailureFlagOut) const +{ + QComboBox* comboBox = qobject_cast(object); + if (comboBox != NULL) { + const QVariant dataValue = macroCommand->getDataValue(); + const QVariant dataValueTwo = macroCommand->getDataValueTwo(); + CaretAssert(macroCommand->getDataType() == WuQMacroDataValueTypeEnum::INTEGER); + CaretAssert(macroCommand->getDataTypeTwo() == WuQMacroDataValueTypeEnum::STRING); + int textIndex(-1); + int indexIndex(-1); + for (int32_t i = 0; i < comboBox->count(); i++) { + if (comboBox->itemText(i) == dataValueTwo.toString()) { + textIndex = i; + } + else if (i == dataValue.toInt()) { + indexIndex = i; + } + } + moveMouseToWidget(comboBox); + WuQMacroSignalEmitter signalEmitter; + if (textIndex >= 0) { + signalEmitter.emitQComboBoxSignal(comboBox, + textIndex); + } + else if (indexIndex >= 0) { + signalEmitter.emitQComboBoxSignal(comboBox, + indexIndex); + } + else { + errorMessageOut = ("For ComboBox \"" + + object->objectName() + + "\", unable to find item with text \"" + + dataValueTwo.toString() + + "\" or index=" + + dataValue.toInt()); + } + } + else { + castFailureFlagOut = true; + } +} + +/** + * Run a selection command + * + * @param macroCommand + * Macro command that is run + * @param object + * The object that is cast to specific object/widget + * @param errorMessageOut + * Error message from execution + * @param castFailureFlagOut + * Set to true if unable to cast object to widget type + */ +void +WuQMacroExecutor::runDoubleSpinBoxCommand(const WuQMacroCommand* macroCommand, + QObject* object, + QString& /*errorMessageOut*/, + bool& castFailureFlagOut) const +{ + QDoubleSpinBox* doubleSpinBox = qobject_cast(object); + if (doubleSpinBox != NULL) { + const QVariant dataValue = macroCommand->getDataValue(); + const WuQMacroDataValueTypeEnum::Enum dataValueType = macroCommand->getDataType(); + + CaretAssert(dataValueType == WuQMacroDataValueTypeEnum::FLOAT); + moveMouseToWidget(doubleSpinBox); + WuQMacroSignalEmitter signalEmitter; + signalEmitter.emitQDoubleSpinBoxSignal(doubleSpinBox, + dataValue.toDouble()); + } + else { + castFailureFlagOut = true; + } +} + +/** + * Run a selection command + * + * @param macroCommand + * Macro command that is run + * @param object + * The object that is cast to specific object/widget + * @param errorMessageOut + * Error message from execution + * @param castFailureFlagOut + * Set to true if unable to cast object to widget type + */ +void +WuQMacroExecutor::runLineEditCommand(const WuQMacroCommand* macroCommand, + QObject* object, + QString& /*errorMessageOut*/, + bool& castFailureFlagOut) const +{ + QLineEdit* lineEdit = qobject_cast(object); + if (lineEdit != NULL) { + const QVariant dataValue = macroCommand->getDataValue(); + const WuQMacroDataValueTypeEnum::Enum dataValueType = macroCommand->getDataType(); + + moveMouseToWidget(lineEdit); + CaretAssert(dataValueType == WuQMacroDataValueTypeEnum::STRING); + WuQMacroSignalEmitter signalEmitter; + signalEmitter.emitQLineEditSignal(lineEdit, + dataValue.toString()); + } + else { + castFailureFlagOut = true; + } +} + + +/** + * Run a selection command + * + * @param macroCommand + * Macro command that is run + * @param object + * The object that is cast to specific object/widget + * @param errorMessageOut + * Error message from execution + * @param castFailureFlagOut + * Set to true if unable to cast object to widget type + */ +void +WuQMacroExecutor::runListWidgetCommand(const WuQMacroCommand* macroCommand, + QObject* object, + QString& errorMessageOut, + bool& castFailureFlagOut) const +{ + QListWidget* listWidget = qobject_cast(object); + if (listWidget != NULL) { + const QVariant dataValue = macroCommand->getDataValue(); + const WuQMacroDataValueTypeEnum::Enum dataValueType = macroCommand->getDataType(); + const QVariant dataValueTwo = macroCommand->getDataValueTwo(); + const WuQMacroDataValueTypeEnum::Enum dataValueTypeTwo = macroCommand->getDataTypeTwo(); + + CaretAssert(dataValueType == WuQMacroDataValueTypeEnum::INTEGER); + CaretAssert(dataValueTypeTwo == WuQMacroDataValueTypeEnum::STRING); + + QListWidgetItem* textItem(NULL); + QListWidgetItem* indexItem(NULL); + for (int32_t i = 0; i < listWidget->count(); i++) { + QListWidgetItem* itemAtIndex = listWidget->item(i); + if (itemAtIndex->text() == dataValueTwo.toString()) { + textItem = itemAtIndex; + } + if (dataValue.toInt() == i) { + indexItem = itemAtIndex; + } + } + + moveMouseToWidget(listWidget); + + WuQMacroSignalEmitter signalEmitter; + if (textItem != NULL) { + signalEmitter.emitQListWidgetSignal(listWidget, + textItem->text()); + } + else if (indexItem != NULL) { + + signalEmitter.emitQListWidgetSignal(listWidget, + indexItem->text()); + } + else { + errorMessageOut = ("For QListWidget \"" + + object->objectName() + + "\", unable to find item with text \"" + + dataValueTwo.toString() + + "\" or index=" + + dataValue.toInt()); + } + } + else { + castFailureFlagOut = true; + } +} + +/** + * Run a selection command + * + * @param macroCommand + * Macro command that is run + * @param object + * The object that is cast to specific object/widget + * @param errorMessageOut + * Error message from execution + * @param castFailureFlagOut + * Set to true if unable to cast object to widget type + */ +void +WuQMacroExecutor::runMenuCommand(const WuQMacroCommand* macroCommand, + QObject* object, + QString& errorMessageOut, + bool& castFailureFlagOut) const +{ + QMenu* menu = qobject_cast(object); + if (menu != NULL) { + const QVariant dataValue = macroCommand->getDataValue(); + const WuQMacroDataValueTypeEnum::Enum dataValueType = macroCommand->getDataType(); + const QVariant dataValueTwo = macroCommand->getDataValueTwo(); + const WuQMacroDataValueTypeEnum::Enum dataValueTypeTwo = macroCommand->getDataTypeTwo(); + + CaretAssert(dataValueType == WuQMacroDataValueTypeEnum::INTEGER); + CaretAssert(dataValueTypeTwo == WuQMacroDataValueTypeEnum::STRING); + + QAction* textAction(NULL); + QAction* indexAction(NULL); + QList actions = menu->actions(); + for (int32_t i = 0; i < actions.size(); i++) { + QAction* actionAtIndex = actions.at(i); + if (actionAtIndex->text() == dataValueTwo.toString()) { + textAction = actionAtIndex; + } + if (dataValue.toInt() == i) { + indexAction = actionAtIndex; + } + } + + moveMouseToWidget(menu); + + WuQMacroSignalEmitter signalEmitter; + if (textAction != NULL) { + signalEmitter.emitQMenuSignal(menu, + textAction->text()); + } + else if (indexAction != NULL) { + signalEmitter.emitQMenuSignal(menu, + indexAction->text()); + } + else { + errorMessageOut = ("For QMenu \"" + + object->objectName() + + "\", unable to find action with text \"" + + dataValueTwo.toString() + + "\" or index=" + + dataValue.toInt()); + } + } + else { + castFailureFlagOut = true; + } +} + +/** + * Run a selection command + * + * @param macroCommand + * Macro command that is run + * @param object + * The object that is cast to specific object/widget + * @param errorMessageOut + * Error message from execution + * @param castFailureFlagOut + * Set to true if unable to cast object to widget type + * @return + * True if mouse command was successful + */ +bool +WuQMacroExecutor::runMouseCommand(const WuQMacroCommand* macroCommand, + QObject* object, + QString& errorMessageOut, + bool& /*castFailureFlagOut*/) const +{ + QWidget* widget = qobject_cast(object); + if (widget != NULL) { + WuQMacroMouseEventWidgetInterface* mouseInterface = dynamic_cast(widget); + if (mouseInterface != NULL) { + const QSize currentWidgetSize = widget->size(); + const WuQMacroMouseEventInfo* mouseEventInfo = macroCommand->getMouseEventInfo(); + CaretAssert(mouseEventInfo); + + int32_t adjustedLocalX(0); + int32_t adjustedLocalY(0); + mouseEventInfo->getLocalPositionRescaledToWidgetSize(currentWidgetSize.width(), + currentWidgetSize.height(), + adjustedLocalX, + adjustedLocalY); + + QEvent::Type qtEventType = QEvent::None; + switch (mouseEventInfo->getMouseEventType()) { + case WuQMacroMouseEventTypeEnum::BUTTON_PRESS: + qtEventType = QEvent::MouseButtonPress; + break; + case WuQMacroMouseEventTypeEnum::BUTTON_RELEASE: + qtEventType = QEvent::MouseButtonRelease; + break; + case WuQMacroMouseEventTypeEnum::DOUBLE_CLICK: + qtEventType = QEvent::MouseButtonDblClick; + break; + case WuQMacroMouseEventTypeEnum::MOVE: + qtEventType = QEvent::MouseMove; + break; + } + + moveMouseToWidgetXY(widget, + adjustedLocalX, + adjustedLocalY, + false); + + QMouseEvent qtMouseEvent(qtEventType, + QPointF(adjustedLocalX, + adjustedLocalY), + static_cast(mouseEventInfo->getMouseButton()), + static_cast(mouseEventInfo->getMouseButtonsMask()), + static_cast(mouseEventInfo->getKeyboardModifiersMask())); + mouseInterface->processMouseEventFromMacro(&qtMouseEvent); + } + else { + errorMessageOut = ("ERROR: Unable to cast object named " + + object->objectName() + + " to WuQMacroMouseEventWidgetInterface"); + return false; + } + } + else { + errorMessageOut = ("ERROR: Unable to cast object named " + + object->objectName() + + " to QWidget"); + return false; + } + + return true; +} + +/** + * Run a selection command + * + * @param macroCommand + * Macro command that is run + * @param object + * The object that is cast to specific object/widget + * @param errorMessageOut + * Error message from execution + * @param castFailureFlagOut + * Set to true if unable to cast object to widget type + */ +void +WuQMacroExecutor::runPushButtonCommand(const WuQMacroCommand* macroCommand, + QObject* object, + QString& /*errorMessageOut*/, + bool& castFailureFlagOut) const +{ + QPushButton* pushButton = qobject_cast(object); + if (pushButton != NULL) { + const QVariant dataValue = macroCommand->getDataValue(); + const WuQMacroDataValueTypeEnum::Enum dataValueType = macroCommand->getDataType(); + + moveMouseToWidget(pushButton); + CaretAssert(dataValueType == WuQMacroDataValueTypeEnum::BOOLEAN); + WuQMacroSignalEmitter signalEmitter; + signalEmitter.emitQPushButtonSignal(pushButton, + dataValue.toBool()); + } + else { + castFailureFlagOut = true; + } +} + +/** + * Run a selection command + * + * @param macroCommand + * Macro command that is run + * @param object + * The object that is cast to specific object/widget + * @param errorMessageOut + * Error message from execution + * @param castFailureFlagOut + * Set to true if unable to cast object to widget type + */ +void +WuQMacroExecutor::runRadioButtonCommand(const WuQMacroCommand* macroCommand, + QObject* object, + QString& /*errorMessageOut*/, + bool& castFailureFlagOut) const +{ + QRadioButton* radioButton = qobject_cast(object); + if (radioButton != NULL) { + const QVariant dataValue = macroCommand->getDataValue(); + const WuQMacroDataValueTypeEnum::Enum dataValueType = macroCommand->getDataType(); + + moveMouseToWidget(radioButton); + CaretAssert(dataValueType == WuQMacroDataValueTypeEnum::BOOLEAN); + WuQMacroSignalEmitter signalEmitter; + signalEmitter.emitQRadioButtonSignal(radioButton, + dataValue.toBool()); + } + else { + castFailureFlagOut = true; + } +} + +/** + * Run a selection command + * + * @param macroCommand + * Macro command that is run + * @param object + * The object that is cast to specific object/widget + * @param errorMessageOut + * Error message from execution + * @param castFailureFlagOut + * Set to true if unable to cast object to widget type + */ +void +WuQMacroExecutor::runSliderCommand(const WuQMacroCommand* macroCommand, + QObject* object, + QString& /*errorMessageOut*/, + bool& castFailureFlagOut) const +{ + QSlider* slider = qobject_cast(object); + if (slider != NULL) { + const QVariant dataValue = macroCommand->getDataValue(); + const WuQMacroDataValueTypeEnum::Enum dataValueType = macroCommand->getDataType(); + + moveMouseToWidget(slider); + CaretAssert(dataValueType == WuQMacroDataValueTypeEnum::INTEGER); + WuQMacroSignalEmitter signalEmitter; + signalEmitter.emitQSliderSignal(slider, + dataValue.toInt()); + } + else { + castFailureFlagOut = true; + } +} + +/** + * Run a selection command + * + * @param macroCommand + * Macro command that is run + * @param object + * The object that is cast to specific object/widget + * @param errorMessageOut + * Error message from execution + * @param castFailureFlagOut + * Set to true if unable to cast object to widget type + */ +void +WuQMacroExecutor::runSpinBoxCommand(const WuQMacroCommand* macroCommand, + QObject* object, + QString& /*errorMessageOut*/, + bool& castFailureFlagOut) const +{ + QSpinBox* spinBox = qobject_cast(object); + if (spinBox != NULL) { + const QVariant dataValue = macroCommand->getDataValue(); + const WuQMacroDataValueTypeEnum::Enum dataValueType = macroCommand->getDataType(); + + moveMouseToWidget(spinBox); + CaretAssert(dataValueType == WuQMacroDataValueTypeEnum::INTEGER); + WuQMacroSignalEmitter signalEmitter; + signalEmitter.emitQSpinBoxSignal(spinBox, + dataValue.toInt()); + } + else { + castFailureFlagOut = true; + } +} + +/** + * Run a selection command + * + * @param macroCommand + * Macro command that is run + * @param object + * The object that is cast to specific object/widget + * @param errorMessageOut + * Error message from execution + * @param castFailureFlagOut + * Set to true if unable to cast object to widget type + */ +void +WuQMacroExecutor::runTabBarCommand(const WuQMacroCommand* macroCommand, + QObject* object, + QString& errorMessageOut, + bool& castFailureFlagOut) const +{ + QTabBar* tabBar = qobject_cast(object); + if (tabBar != NULL) { + const QVariant dataValue = macroCommand->getDataValue(); + const WuQMacroDataValueTypeEnum::Enum dataValueType = macroCommand->getDataType(); + const QVariant dataValueTwo = macroCommand->getDataValueTwo(); + const WuQMacroDataValueTypeEnum::Enum dataValueTypeTwo = macroCommand->getDataTypeTwo(); + + CaretAssert(dataValueType == WuQMacroDataValueTypeEnum::INTEGER); + int32_t tabIndex = dataValue.toInt(); + if (dataValueTypeTwo == WuQMacroDataValueTypeEnum::STRING) { + /* + * Allow tab name to override tab index + */ + const QString tabName = dataValueTwo.toString(); + if ( ! tabName.isEmpty()) { + for (int32_t i = 0; i < tabBar->count(); i++) { + if (tabName == tabBar->tabText(i)) { + tabIndex = i; + break; + } + } + } + } + if (tabBar->isTabEnabled(tabIndex)) { + moveMouseToTabBarTab(tabBar, tabIndex); + WuQMacroSignalEmitter signalEmitter; + signalEmitter.emitQTabBarSignal(tabBar, + tabIndex); + } + else { + errorMessageOut = ("QTabWidget \"" + + object->objectName() + + "\", tab \"" + + QString::number(tabIndex + 1) + + "\" with text \"" + + tabBar->tabText(tabIndex) + + "\" is disabled"); + } + + } + else { + castFailureFlagOut = true; + } +} + +/** + * Run a selection command + * + * @param macroCommand + * Macro command that is run + * @param object + * The object that is cast to specific object/widget + * @param errorMessageOut + * Error message from execution + * @param castFailureFlagOut + * Set to true if unable to cast object to widget type + */ +void +WuQMacroExecutor::runTabWidgetCommand(const WuQMacroCommand* macroCommand, + QObject* object, + QString& errorMessageOut, + bool& castFailureFlagOut) const +{ + QTabWidget* tabWidget = qobject_cast(object); + if (tabWidget != NULL) { + const QVariant dataValue = macroCommand->getDataValue(); + const WuQMacroDataValueTypeEnum::Enum dataValueType = macroCommand->getDataType(); + const QVariant dataValueTwo = macroCommand->getDataValueTwo(); + const WuQMacroDataValueTypeEnum::Enum dataValueTypeTwo = macroCommand->getDataTypeTwo(); + + CaretAssert(dataValueType == WuQMacroDataValueTypeEnum::INTEGER); + QTabBar* tabBar = tabWidget->tabBar(); + CaretAssert(tabBar); + int32_t tabIndex = dataValue.toInt(); + if (dataValueTypeTwo == WuQMacroDataValueTypeEnum::STRING) { + /* + * Allow tab name to override tab index + */ + const QString tabName = dataValueTwo.toString(); + if ( ! tabName.isEmpty()) { + for (int32_t i = 0; i < tabWidget->count(); i++) { + if (tabName == tabWidget->tabText(i)) { + tabIndex = i; + break; + } + } + } + } + + if (tabWidget->isTabEnabled(tabIndex)) { + moveMouseToTabBarTab(tabBar, + tabIndex); + WuQMacroSignalEmitter signalEmitter; + signalEmitter.emitQTabWidgetSignal(tabWidget, + tabIndex); + } + else { + errorMessageOut = ("QTabWidget \"" + + object->objectName() + + "\", tab \"" + + QString::number(tabIndex + 1) + + "\" with text \"" + + tabWidget->tabText(tabIndex) + + "\" is disabled"); + } + } + else { + castFailureFlagOut = true; + } +} + +/** + * Run a selection command + * + * @param macroCommand + * Macro command that is run + * @param object + * The object that is cast to specific object/widget + * @param errorMessageOut + * Error message from execution + * @param castFailureFlagOut + * Set to true if unable to cast object to widget type + */ +void +WuQMacroExecutor::runToolButtonCommand(const WuQMacroCommand* macroCommand, + QObject* object, + QString& /*errorMessageOut*/, + bool& castFailureFlagOut) const +{ + QToolButton* toolButton = qobject_cast(object); + if (toolButton != NULL) { + const QVariant dataValue = macroCommand->getDataValue(); + const WuQMacroDataValueTypeEnum::Enum dataValueType = macroCommand->getDataType(); + const QVariant dataValueTwo = macroCommand->getDataValueTwo(); + + moveMouseToWidget(toolButton); + CaretAssert(dataValueType == WuQMacroDataValueTypeEnum::BOOLEAN); + WuQMacroSignalEmitter signalEmitter; + signalEmitter.emitQToolButtonSignal(toolButton, + dataValue.toBool()); + } + else { + castFailureFlagOut = true; + } +} diff --git a/src/GuiQt/WuQMacroExecutor.h b/src/GuiQt/WuQMacroExecutor.h index f94775b6a..0688debb2 100644 --- a/src/GuiQt/WuQMacroExecutor.h +++ b/src/GuiQt/WuQMacroExecutor.h @@ -83,17 +83,102 @@ namespace caret { QObject* findObjectByName(const QString& objectName) const; + void runActionCommand(const WuQMacroCommand* macroCommand, + QObject* object, + QString& errorMessageOut, + bool& castFailureFlagOut) const; + + void runActionGroupCommand(const WuQMacroCommand* macroCommand, + QObject* object, + QString& errorMessageOut, + bool& castFailureFlagOut) const; + + void runButtonGroupCommand(const WuQMacroCommand* macroCommand, + QObject* object, + QString& errorMessageOut, + bool& castFailureFlagOut) const; + + void runCheckBoxCommand(const WuQMacroCommand* macroCommand, + QObject* object, + QString& errorMessageOut, + bool& castFailureFlagOut) const; + + void runComboBoxCommand(const WuQMacroCommand* macroCommand, + QObject* object, + QString& errorMessageOut, + bool& castFailureFlagOut) const; + + void runDoubleSpinBoxCommand(const WuQMacroCommand* macroCommand, + QObject* object, + QString& errorMessageOut, + bool& castFailureFlagOut) const; + + void runLineEditCommand(const WuQMacroCommand* macroCommand, + QObject* object, + QString& errorMessageOut, + bool& castFailureFlagOut) const; + + void runListWidgetCommand(const WuQMacroCommand* macroCommand, + QObject* object, + QString& errorMessageOut, + bool& castFailureFlagOut) const; + + void runMenuCommand(const WuQMacroCommand* macroCommand, + QObject* object, + QString& errorMessageOut, + bool& castFailureFlagOut) const; + + bool runMouseCommand(const WuQMacroCommand* macroCommand, + QObject* object, + QString& errorMessageOut, + bool& castFailureFlagOut) const; + + void runPushButtonCommand(const WuQMacroCommand* macroCommand, + QObject* object, + QString& errorMessageOut, + bool& castFailureFlagOut) const; + + void runRadioButtonCommand(const WuQMacroCommand* macroCommand, + QObject* object, + QString& errorMessageOut, + bool& castFailureFlagOut) const; + + void runSliderCommand(const WuQMacroCommand* macroCommand, + QObject* object, + QString& errorMessageOut, + bool& castFailureFlagOut) const; + + void runSpinBoxCommand(const WuQMacroCommand* macroCommand, + QObject* object, + QString& errorMessageOut, + bool& castFailureFlagOut) const; + + void runTabBarCommand(const WuQMacroCommand* macroCommand, + QObject* object, + QString& errorMessageOut, + bool& castFailureFlagOut) const; + + void runTabWidgetCommand(const WuQMacroCommand* macroCommand, + QObject* object, + QString& errorMessageOut, + bool& castFailureFlagOut) const; + + void runToolButtonCommand(const WuQMacroCommand* macroCommand, + QObject* object, + QString& errorMessageOut, + bool& castFailureFlagOut) const; + mutable WuQMacroExecutorOptions m_runOptions; mutable std::vector m_parentObjects; // ADD_NEW_MEMBERS_HERE - + }; #ifdef __WU_Q_MACRO_EXECUTOR_DECLARE__ // #endif // __WU_Q_MACRO_EXECUTOR_DECLARE__ - + } // namespace #endif //__WU_Q_MACRO_EXECUTOR_H__ diff --git a/src/GuiQt/WuQMacroSignalWatcher.cxx b/src/GuiQt/WuQMacroSignalWatcher.cxx index 8fb5624b7..42f746ccc 100644 --- a/src/GuiQt/WuQMacroSignalWatcher.cxx +++ b/src/GuiQt/WuQMacroSignalWatcher.cxx @@ -200,7 +200,7 @@ m_objectName(object->objectName()) { QTabBar* tabBar = qobject_cast(m_object); CaretAssert(tabBar); - QObject::connect(tabBar, &QTabBar::tabBarClicked, //&QTabBar::currentChanged, + QObject::connect(tabBar, &QTabBar::tabBarClicked, this, &WuQMacroSignalWatcher::tabBarCurrentChanged); } break; @@ -208,7 +208,7 @@ m_objectName(object->objectName()) { QTabWidget* tabWidget = qobject_cast(m_object); CaretAssert(tabWidget); - QObject::connect(tabWidget, &QTabWidget::tabBarClicked, //&QTabWidget::currentChanged, + QObject::connect(tabWidget, &QTabWidget::tabBarClicked, this, &WuQMacroSignalWatcher::tabWidgetCurrentChanged); } break; @@ -600,8 +600,11 @@ WuQMacroSignalWatcher::spinBoxValueChanged(int value) void WuQMacroSignalWatcher::tabBarCurrentChanged(int index) { + QTabBar* tabBar = qobject_cast(m_object); + CaretAssert(tabBar); + const QString tabText = tabBar->tabText(index); createAndSendMacroCommand(index, - ""); + tabText); } /** @@ -613,8 +616,11 @@ WuQMacroSignalWatcher::tabBarCurrentChanged(int index) void WuQMacroSignalWatcher::tabWidgetCurrentChanged(int index) { + QTabWidget* tabWidget = qobject_cast(m_object); + CaretAssert(tabWidget); + const QString tabText = tabWidget->tabText(index); createAndSendMacroCommand(index, - ""); + tabText); } /** -- GitLab From 18188c56ad7f4e1f48089eddd8829021917a0a3c Mon Sep 17 00:00:00 2001 From: John Harwell Date: Wed, 2 Jan 2019 12:18:09 -0600 Subject: [PATCH 157/427] Display more information when there is an error received from BALSA database. --- src/Common/SystemUtilities.cxx | 5 +++ src/GuiQt/BalsaDatabaseManager.cxx | 39 +++++++------------ .../BalsaDatabaseUploadSceneFileDialog.cxx | 11 ++++-- 3 files changed, 28 insertions(+), 27 deletions(-) diff --git a/src/Common/SystemUtilities.cxx b/src/Common/SystemUtilities.cxx index 8f1fda4f1..69369ef56 100644 --- a/src/Common/SystemUtilities.cxx +++ b/src/Common/SystemUtilities.cxx @@ -672,6 +672,11 @@ SystemUtilities::getWorkbenchHome() AString SystemUtilities::getLocalHostName() { + /* + * NOTE: THIS FUNCTION DOES NOT SEEM TO WORK ON MACOS 10.14 MOJAVE. + * THE LOCALHOST NAME IS .LOCAL WHERE IS THE COMPUTER'NAME + * AND QHostInfo::fromName() DOES NOT MAKE IT FULLY QUALIFIED + */ QString hostName; QHostInfo hostInfo = QHostInfo::fromName(QHostInfo::localHostName()); diff --git a/src/GuiQt/BalsaDatabaseManager.cxx b/src/GuiQt/BalsaDatabaseManager.cxx index 1cd25b696..897be6ab4 100644 --- a/src/GuiQt/BalsaDatabaseManager.cxx +++ b/src/GuiQt/BalsaDatabaseManager.cxx @@ -639,10 +639,9 @@ BalsaDatabaseManager::processUploadedFile(SceneFile* sceneFile, if (uploadResponse.m_responseCode != 200) { errorMessageOut = ("Process Upload failed code: " - + QString::number(uploadResponse.m_responseCode)); - if (uploadResponse.m_responseCode < 0) { - errorMessageOut.appendWithNewLine(uploadResponse.m_errorMessage); - } + + QString::number(uploadResponse.m_responseCode) + + ". "); + errorMessageOut.appendWithNewLine(uploadResponse.m_errorMessage); return false; } @@ -900,10 +899,8 @@ BalsaDatabaseManager::getSceneIDs(const int32_t numberOfSceneIDs, if (response.m_responseCode != 200) { errorMessageOut = ("Requesting Scene IDs failed with HTTP code=" + AString::number(response.m_responseCode) - + ". This error may be caused by failure to agree to data use terms."); - if (response.m_responseCode < 0) { - errorMessageOut.appendWithNewLine(response.m_errorMessage); - } + + ". This error may be caused by failure to agree to data use terms. "); + errorMessageOut.appendWithNewLine(response.m_errorMessage); return false; } @@ -1002,10 +999,8 @@ BalsaDatabaseManager::requestStudyID(const AString& databaseURL, if (studyResponse.m_responseCode != 200) { errorMessageOut = ("Requesting study ID failed with HTTP code=" + AString::number(studyResponse.m_responseCode) - + ". This error may be caused by failure to agree to data use terms."); - if (studyResponse.m_responseCode < 0) { - errorMessageOut.appendWithNewLine(studyResponse.m_errorMessage); - } + + ". This error may be caused by failure to agree to data use terms. "); + errorMessageOut.appendWithNewLine(studyResponse.m_errorMessage); return false; } @@ -1137,10 +1132,9 @@ BalsaDatabaseManager::getUserRoles(BalsaUserRoles& userRolesOut, if (studyResponse.m_responseCode != 200) { errorMessageOut = ("Requesting roles failed with HTTP code=" - + AString::number(studyResponse.m_responseCode)); - if (studyResponse.m_responseCode < 0) { - errorMessageOut.appendWithNewLine(studyResponse.m_errorMessage); - } + + AString::number(studyResponse.m_responseCode) + + ". "); + errorMessageOut.appendWithNewLine(studyResponse.m_errorMessage); return false; } @@ -1243,10 +1237,9 @@ BalsaDatabaseManager::getStudyExtractDirectoryPrefix(const AString& studyID, if (studyResponse.m_responseCode != 200) { errorMessageOut = ("Requesting extract directory failed with HTTP code=" - + AString::number(studyResponse.m_responseCode)); - if (studyResponse.m_responseCode < 0) { - errorMessageOut.appendWithNewLine(studyResponse.m_errorMessage); - } + + AString::number(studyResponse.m_responseCode) + + ". "); + errorMessageOut.appendWithNewLine(studyResponse.m_errorMessage); return false; } @@ -1324,10 +1317,8 @@ BalsaDatabaseManager::getAllStudyInformation(std::vector& if (idResponse.m_responseCode != 200) { errorMessageOut = ("Requesting all study information failed with HTTP code=" + AString::number(idResponse.m_responseCode) - + ". This error may be caused by failure to agree to data use terms."); - if (idResponse.m_responseCode < 0) { - errorMessageOut.appendWithNewLine(idResponse.m_errorMessage); - } + + ". This error may be caused by failure to agree to data use terms. "); + errorMessageOut.appendWithNewLine(idResponse.m_errorMessage); return false; } diff --git a/src/GuiQt/BalsaDatabaseUploadSceneFileDialog.cxx b/src/GuiQt/BalsaDatabaseUploadSceneFileDialog.cxx index 40b27c5a8..4241e0758 100644 --- a/src/GuiQt/BalsaDatabaseUploadSceneFileDialog.cxx +++ b/src/GuiQt/BalsaDatabaseUploadSceneFileDialog.cxx @@ -447,9 +447,14 @@ BalsaDatabaseUploadSceneFileDialog::createUploadTab() QWidget* BalsaDatabaseUploadSceneFileDialog::createBalsaDatabaseSelectionWidget() { - QString hostName = SystemUtilities::getLocalHostName(); - const bool isWustlDomainFlag = hostName.endsWith(".wustl.edu"); - + bool isWustlDomainFlag = false; + /* DOES NOT WORK ON MACOS 10.14, See note in: SystemUtilities::getLocalHostName() + QString hostName = SystemUtilities::getLocalHostName(); + bool isWustlDomainFlag = hostName.endsWith(".wustl.edu"); + */ +#ifndef NDEBUG + isWustlDomainFlag = true; +#endif /* * Database selection */ -- GitLab From 8393321ceef4a777821ed933ad854add320a9f84 Mon Sep 17 00:00:00 2001 From: John Harwell Date: Thu, 3 Jan 2019 09:51:54 -0600 Subject: [PATCH 158/427] Improve error message if upload of study fails with permission error. Indicate that the cause may be a study that has been submitted for curation. --- src/GuiQt/BalsaDatabaseManager.cxx | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/GuiQt/BalsaDatabaseManager.cxx b/src/GuiQt/BalsaDatabaseManager.cxx index 897be6ab4..027739931 100644 --- a/src/GuiQt/BalsaDatabaseManager.cxx +++ b/src/GuiQt/BalsaDatabaseManager.cxx @@ -337,9 +337,15 @@ BalsaDatabaseManager::verifyUploadFileResponse(const std::map& { if (responseHttpCode != 200) { if (responseHttpCode == 403) { - errorMessageOut = ("Upload failed. Http Code=" + errorMessageOut = ("Upload failed. (Http Code=" + AString::number(responseHttpCode) - + ". You may not have ownership/permission to edit the study."); + + ").\n\n" + "Either you do now have ownership/permission to edit the study or " + "the study has been submitted for curation.\n\n" + "Use your web browser to login to BALSA to view the study and " + "check its permissions. If the the study has been submitted for " + "curation, there will be a 'return for revision' option on the study. " + "Selection of 'return for 'revision' will allow you to upload your data."); } else { errorMessageOut = ("Upload failed. Http Code=" -- GitLab From 97f7568a94dc4541c88dfbe9959e16a136e4de62 Mon Sep 17 00:00:00 2001 From: John Harwell Date: Thu, 3 Jan 2019 14:33:46 -0600 Subject: [PATCH 159/427] WB-834: Removed macros from scene file and moved them into each scene. Track the "current scene" and highlight current scene on the Scene Dialog. --- src/Brain/Brain.cxx | 42 +++++++++++ src/Brain/Brain.h | 3 + src/Brain/CMakeLists.txt | 2 + src/Brain/EventSceneActive.cxx | 91 ++++++++++++++++++++++ src/Brain/EventSceneActive.h | 78 +++++++++++++++++++ src/Common/EventManager.cxx | 1 + src/Common/EventTypeEnum.cxx | 4 + src/Common/EventTypeEnum.h | 2 + src/Common/WuQMacroGroup.cxx | 9 ++- src/Common/WuQMacroGroupXmlReader.cxx | 2 + src/Files/SceneFile.cxx | 45 ----------- src/Files/SceneFile.h | 7 -- src/Files/SceneFileSaxReader.cxx | 20 ----- src/Files/SceneFileSaxReader.h | 2 - src/GuiQt/CMakeLists.txt | 24 +++--- src/GuiQt/GuiMacroHelper.cxx | 37 ++++++--- src/GuiQt/GuiMacroHelper.h | 12 +-- src/GuiQt/GuiManager.cxx | 2 +- src/GuiQt/SceneDialog.cxx | 105 ++++++++++++++++++++++++-- src/GuiQt/SceneDialog.h | 5 +- src/GuiQt/WuQMacroHelperInterface.h | 9 ++- src/GuiQt/WuQMacroManager.cxx | 7 +- src/GuiQt/WuQMacroManager.h | 7 +- src/Scenes/SceneInfo.cxx | 75 +++++++++++++++++- src/Scenes/SceneInfo.h | 12 +++ src/Scenes/SceneInfoSaxReader.cxx | 22 +++++- src/Scenes/SceneInfoSaxReader.h | 2 + src/Scenes/SceneXmlElements.h | 4 +- 28 files changed, 507 insertions(+), 124 deletions(-) create mode 100644 src/Brain/EventSceneActive.cxx create mode 100644 src/Brain/EventSceneActive.h diff --git a/src/Brain/Brain.cxx b/src/Brain/Brain.cxx index 05ae1bfd6..cd27e5406 100644 --- a/src/Brain/Brain.cxx +++ b/src/Brain/Brain.cxx @@ -82,6 +82,7 @@ #include "EventModelGetAllDisplayed.h" #include "EventPaletteGetByName.h" #include "EventProgressUpdate.h" +#include "EventSceneActive.h" #include "EventSpecFileReadDataFiles.h" #include "EventManager.h" #include "FiberOrientationSamplesLoader.h" @@ -104,6 +105,7 @@ #include "OverlaySet.h" #include "PaletteFile.h" #include "RgbaFile.h" +#include "Scene.h" #include "SceneAttributes.h" #include "SceneClass.h" #include "SceneClassArray.h" @@ -200,6 +202,8 @@ Brain::Brain(const CaretPreferences* caretPreferences) EventTypeEnum::EVENT_SPEC_FILE_READ_DATA_FILES); EventManager::get()->addEventListener(this, EventTypeEnum::EVENT_PALETTE_GET_BY_NAME); + EventManager::get()->addEventListener(this, + EventTypeEnum::EVENT_SCENE_ACTIVE); m_isSpecFileBeingRead = false; @@ -491,6 +495,7 @@ Brain::resetBrain(const ResetBrainKeepSceneFiles keepSceneFiles, const ResetBrainKeepSpecFile keepSpecFile) { m_isSpecFileBeingRead = false; + m_activeScene = NULL; /* * Clear the counters used to prevent duplicate file names. @@ -6133,6 +6138,43 @@ Brain::receiveEvent(Event* event) } } } + else if (event->getEventType() == EventTypeEnum::EVENT_SCENE_ACTIVE) { + EventSceneActive* sceneEvent = dynamic_cast(event); + CaretAssert(sceneEvent); + + switch (sceneEvent->getMode()) { + case EventSceneActive::MODE_GET: + { + if (m_activeScene != NULL) { + bool sceneFoundFlag = false; + + for (auto sf : m_sceneFiles) { + const int32_t numScenes = sf->getNumberOfScenes(); + for (int32_t i = 0; i < numScenes; i++) { + if (sf->getSceneAtIndex(i) == m_activeScene) { + sceneFoundFlag = true; + break; + } + } + if (sceneFoundFlag) { + break; + } + } + + if ( ! sceneFoundFlag) { + m_activeScene = NULL; + } + } + + sceneEvent->setScene(m_activeScene); + sceneEvent->setEventProcessed(); + } + break; + case EventSceneActive::MODE_SET: + m_activeScene = sceneEvent->getScene(); + break; + } + } } /** diff --git a/src/Brain/Brain.h b/src/Brain/Brain.h index 946ac5277..2e6e29941 100644 --- a/src/Brain/Brain.h +++ b/src/Brain/Brain.h @@ -94,6 +94,7 @@ namespace caret { class PaletteFile; class RgbaFile; class SceneClassAssistant; + class Scene; class SceneFile; class SelectionManager; class SpecFile; @@ -868,6 +869,8 @@ namespace caret { std::map m_duplicateFileNameCounter; GapsAndMargins* m_gapsAndMargins; + + Scene* m_activeScene = NULL; }; } // namespace diff --git a/src/Brain/CMakeLists.txt b/src/Brain/CMakeLists.txt index 7f0615464..4ffaab51b 100644 --- a/src/Brain/CMakeLists.txt +++ b/src/Brain/CMakeLists.txt @@ -98,6 +98,7 @@ EventModelSurfaceGet.h EventNodeDataFilesGet.h EventNodeIdentificationColorsGetFromCharts.h EventOverlayValidate.h +EventSceneActive.h EventSpacerTabGet.h EventSpecFileReadDataFiles.h EventSurfacesGet.h @@ -250,6 +251,7 @@ EventModelSurfaceGet.cxx EventNodeDataFilesGet.cxx EventNodeIdentificationColorsGetFromCharts.cxx EventOverlayValidate.cxx +EventSceneActive.cxx EventSpacerTabGet.cxx EventSpecFileReadDataFiles.cxx EventSurfacesGet.cxx diff --git a/src/Brain/EventSceneActive.cxx b/src/Brain/EventSceneActive.cxx new file mode 100644 index 000000000..16472b7e9 --- /dev/null +++ b/src/Brain/EventSceneActive.cxx @@ -0,0 +1,91 @@ + +/*LICENSE_START*/ +/* + * Copyright (C) 2019 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + +#define __EVENT_SCENE_ACTIVE_DECLARE__ +#include "EventSceneActive.h" +#undef __EVENT_SCENE_ACTIVE_DECLARE__ + +#include "CaretAssert.h" +#include "EventTypeEnum.h" + +using namespace caret; + + + +/** + * \class caret::EventSceneActive + * \brief Event to get/set the active scene + * \ingroup Brain + * + * Event to get/set the active scene. + * The "active scene" is the scene that was last loaded + * or created. If a spec file is loaded or the scene file + * containing the scene is closed, the "active scene" + * becomes invalid. + */ + +/** + * Constructor. + * + * @param mode + * The mode of this event + */ +EventSceneActive::EventSceneActive(const Mode mode) +: Event(EventTypeEnum::EVENT_SCENE_ACTIVE), +m_mode(mode) +{ + +} + +/** + * Destructor. + */ +EventSceneActive::~EventSceneActive() +{ +} + +/** + * @return The mode. + */ +EventSceneActive::Mode +EventSceneActive::getMode() const +{ + return m_mode; +} +/** + * @return The scene + */ +Scene* +EventSceneActive::getScene() const +{ + return m_scene; +} + +/** + * Set the scene + * New value for active scene. + */ +void +EventSceneActive::setScene(Scene* scene) +{ + m_scene = scene; +} + diff --git a/src/Brain/EventSceneActive.h b/src/Brain/EventSceneActive.h new file mode 100644 index 000000000..5532f0ae8 --- /dev/null +++ b/src/Brain/EventSceneActive.h @@ -0,0 +1,78 @@ +#ifndef __EVENT_SCENE_ACTIVE_H__ +#define __EVENT_SCENE_ACTIVE_H__ + +/*LICENSE_START*/ +/* + * Copyright (C) 2019 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + + + +#include + +#include "Event.h" + + + +namespace caret { + + class Scene; + + class EventSceneActive : public Event { + + public: + /** The mode of the event */ + enum Mode { + /** Get the active scene */ + MODE_GET, + /** Set the active scene */ + MODE_SET + }; + + EventSceneActive(const Mode mode); + + virtual ~EventSceneActive(); + + Mode getMode() const; + + Scene* getScene() const; + + void setScene(Scene* scene); + + EventSceneActive(const EventSceneActive&) = delete; + + EventSceneActive& operator=(const EventSceneActive&) = delete; + + + // ADD_NEW_METHODS_HERE + + private: + const Mode m_mode; + + Scene* m_scene = NULL; + + // ADD_NEW_MEMBERS_HERE + + }; + +#ifdef __EVENT_SCENE_ACTIVE_DECLARE__ + // +#endif // __EVENT_SCENE_ACTIVE_DECLARE__ + +} // namespace +#endif //__EVENT_SCENE_ACTIVE_H__ diff --git a/src/Common/EventManager.cxx b/src/Common/EventManager.cxx index 92a756656..098156ccf 100644 --- a/src/Common/EventManager.cxx +++ b/src/Common/EventManager.cxx @@ -452,6 +452,7 @@ EventManager::sendSimpleEvent(const EventTypeEnum::Enum eventType) case EventTypeEnum::EVENT_OVERLAY_VALIDATE: case EventTypeEnum::EVENT_PALETTE_COLOR_MAPPING_EDITOR_SHOW: case EventTypeEnum::EVENT_PALETTE_GET_BY_NAME: + case EventTypeEnum::EVENT_SCENE_ACTIVE: case EventTypeEnum::EVENT_SHOW_FILE_DATA_READ_WARNING_DIALOG: case EventTypeEnum::EVENT_SPACER_TAB_GET: case EventTypeEnum::EVENT_SPEC_FILE_READ_DATA_FILES: diff --git a/src/Common/EventTypeEnum.cxx b/src/Common/EventTypeEnum.cxx index 07097a582..d5fff5a1d 100644 --- a/src/Common/EventTypeEnum.cxx +++ b/src/Common/EventTypeEnum.cxx @@ -350,6 +350,10 @@ EventTypeEnum::initialize() "EVENT_PALETTE_GET_BY_NAME", "Read the selected files in a spec file")); + enumData.push_back(EventTypeEnum(EVENT_SCENE_ACTIVE, + "EVENT_SCENE_ACTIVE", + "Get/Set the active scene")); + enumData.push_back(EventTypeEnum(EVENT_SHOW_FILE_DATA_READ_WARNING_DIALOG, "EVENT_SHOW_FILE_DATA_READ_WARNING_DIALOG", "Show a dialog with warnings encountered reading data files")); diff --git a/src/Common/EventTypeEnum.h b/src/Common/EventTypeEnum.h index d3a6c0daa..2fd301fbd 100644 --- a/src/Common/EventTypeEnum.h +++ b/src/Common/EventTypeEnum.h @@ -182,6 +182,8 @@ public: EVENT_PALETTE_COLOR_MAPPING_EDITOR_SHOW, /** Get a palette by name from a palette file */ EVENT_PALETTE_GET_BY_NAME, + /** Get the active scene */ + EVENT_SCENE_ACTIVE, /** Show a dialog containing warnings encountered when reading data files */ EVENT_SHOW_FILE_DATA_READ_WARNING_DIALOG, /** Get a spacer tab by tab number */ diff --git a/src/Common/WuQMacroGroup.cxx b/src/Common/WuQMacroGroup.cxx index 26f528c3c..3b26dc475 100644 --- a/src/Common/WuQMacroGroup.cxx +++ b/src/Common/WuQMacroGroup.cxx @@ -118,11 +118,18 @@ WuQMacroGroup::clear() { /* Note: Do not clear unique identifier */ + bool hadMacrosFlag = ( ! m_macros.empty()); for (auto m : m_macros) { delete m; } m_macros.clear(); - setModified(); + + if (hadMacrosFlag) { + setModified(); + } + else { + clearModified(); + } } /** diff --git a/src/Common/WuQMacroGroupXmlReader.cxx b/src/Common/WuQMacroGroupXmlReader.cxx index ca94defd5..2b6db7709 100644 --- a/src/Common/WuQMacroGroupXmlReader.cxx +++ b/src/Common/WuQMacroGroupXmlReader.cxx @@ -130,6 +130,8 @@ WuQMacroGroupXmlReader::readFromString(const QString& xmlString, if (m_xmlStreamReader->hasError()) { macroGroup->clear(); } + + macroGroup->clearModified(); } /** diff --git a/src/Files/SceneFile.cxx b/src/Files/SceneFile.cxx index 5752c9e9a..31864d136 100644 --- a/src/Files/SceneFile.cxx +++ b/src/Files/SceneFile.cxx @@ -48,8 +48,6 @@ #include "SceneWriterXml.h" #include "SpecFile.h" #include "SystemUtilities.h" -#include "WuQMacroGroup.h" -#include "WuQMacroGroupXmlWriter.h" #include "XmlSaxParser.h" #include "XmlUtilities.h" #include "XmlWriter.h" @@ -75,11 +73,6 @@ SceneFile::SceneFile() m_balsaExtractToDirectoryName = ""; m_basePathType = SceneFileBasePathTypeEnum::AUTOMATIC; m_metadata = new GiftiMetaData(); - - static int counter = 1; - const AString macroGroupName("SceneFile_" - + AString::number(counter)); - m_macroGroup.reset(new WuQMacroGroup(macroGroupName)); } /** @@ -106,7 +99,6 @@ SceneFile::clear() CaretDataFile::clear(); m_metadata->clear(); - m_macroGroup->clear(); m_balsaStudyID = ""; m_balsaStudyTitle = ""; @@ -589,9 +581,6 @@ void SceneFile::setFileName(const AString& filename) { CaretDataFile::setFileName(filename); - - FileInformation fileInfo(filename); - m_macroGroup->setName(fileInfo.getFileName()); } /** @@ -775,19 +764,6 @@ SceneFile::writeFile(const AString& filename) } xmlWriter.writeEndElement(); - /* - * Write macro group - */ - if (m_macroGroup->getNumberOfMacros() > 0) { - QString textString; - WuQMacroGroupXmlWriter macroWriter; - macroWriter.writeToString(m_macroGroup.get(), - textString); - textString = XmlUtilities::encodeXmlSpecialCharacters(textString); - xmlWriter.writeElementCharacters(SceneXmlElements::SCENE_FILE_MACRO_GROUP, - textString); - } - // // Write scenes // @@ -1299,9 +1275,6 @@ SceneFile::isModified() const return true; } } - if (m_macroGroup->isModified()) { - return true; - } return false; } @@ -1316,24 +1289,6 @@ SceneFile::clearModified() for (auto scene : m_scenes) { scene->clearModified(); } - m_macroGroup->clearModified(); } -/** - * @return The macro group - */ -WuQMacroGroup* -SceneFile::getMacroGroup() -{ - return m_macroGroup.get(); -} - -/** - * @return The macro group (const method) - */ -const WuQMacroGroup* -SceneFile::getMacroGroup() const -{ - return m_macroGroup.get(); -} diff --git a/src/Files/SceneFile.h b/src/Files/SceneFile.h index a9a7c4e8b..cf95b1f65 100644 --- a/src/Files/SceneFile.h +++ b/src/Files/SceneFile.h @@ -31,7 +31,6 @@ namespace caret { class Scene; - class WuQMacroGroup; class SceneFile : public CaretDataFile { @@ -159,10 +158,6 @@ namespace caret { virtual void clearModified() override; - WuQMacroGroup* getMacroGroup(); - - const WuQMacroGroup* getMacroGroup() const; - // ADD_NEW_METHODS_HERE /** Version of file */ @@ -202,8 +197,6 @@ namespace caret { /** The "extract to" directory name */ AString m_balsaExtractToDirectoryName; - std::unique_ptr m_macroGroup; - // ADD_NEW_MEMBERS_HERE /** Version of this SceneFile */ diff --git a/src/Files/SceneFileSaxReader.cxx b/src/Files/SceneFileSaxReader.cxx index 171f6f59d..f17ffc264 100644 --- a/src/Files/SceneFileSaxReader.cxx +++ b/src/Files/SceneFileSaxReader.cxx @@ -121,8 +121,6 @@ SceneFileSaxReader::startElement(const AString& namespaceURI, throw e; } break; - case STATE_SCENE_FILE_MACRO_GROUP: - break; case STATE_SCENE_INFO_DIRECTORY: if (qName == SceneXmlElements::SCENE_INFO_TAG) { m_state = STATE_SCENE_INFO; @@ -202,10 +200,6 @@ SceneFileSaxReader::startElement(const AString& namespaceURI, m_scene); m_sceneSaxReader->startElement(namespaceURI, localName, qName, attributes); } - else if (qName == SceneXmlElements::SCENE_FILE_MACRO_GROUP) { - m_state = STATE_SCENE_FILE_MACRO_GROUP; - break; - } else { const AString msg = XmlUtilities::createInvalidChildElementMessage(SceneXmlElements::SCENE_TAG, qName); @@ -250,20 +244,6 @@ SceneFileSaxReader::endElement(const AString& namespaceURI, m_sceneSaxReader = NULL; } break; - case STATE_SCENE_FILE_MACRO_GROUP: - { - WuQMacroGroup* macroGroup = m_sceneFile->getMacroGroup(); - WuQMacroGroupXmlReader reader; - reader.readFromString(m_elementText, - macroGroup); - if (reader.hasError()) { - CaretLogSevere(reader.getErrorMessage()); - } - else if (reader.hasWarnings()) { - CaretLogWarning(reader.getWarningMessage()); - } - } - break; case STATE_SCENE_INFO_DIRECTORY: break; case STATE_SCENE_INFO_BALSA_STUDY_ID: diff --git a/src/Files/SceneFileSaxReader.h b/src/Files/SceneFileSaxReader.h index e548c225b..531e1f5f9 100644 --- a/src/Files/SceneFileSaxReader.h +++ b/src/Files/SceneFileSaxReader.h @@ -78,8 +78,6 @@ namespace caret { STATE_NONE, /// processing SceneFile tag STATE_SCENE_FILE, - /// processing scene file macro group - STATE_SCENE_FILE_MACRO_GROUP, /// processing MetaData tag STATE_METADATA, /// processing SceneInfoDirectory tag diff --git a/src/GuiQt/CMakeLists.txt b/src/GuiQt/CMakeLists.txt index 43e70d069..7e727525f 100644 --- a/src/GuiQt/CMakeLists.txt +++ b/src/GuiQt/CMakeLists.txt @@ -110,7 +110,7 @@ ELSE() CiftiParcelSelectionComboBox.h ClippingPlanesDialog.h ColorEditorWidget.h - CopyPaletteColorMappingToFilesDialog.h + CopyPaletteColorMappingToFilesDialog.h CustomViewDialog.h DataFileContentCopyMoveDialog.h DisplayGroupAndTabItemViewController.h @@ -125,6 +125,7 @@ ELSE() GiftiLabelTableEditor.h GiftiLabelTableSelectionComboBox.h GroupAndNameHierarchyViewController.h + GuiMacroHelper.h GuiManager.h HelpViewerDialog.h HyperLinkTextBrowser.h @@ -135,11 +136,11 @@ ELSE() InformationDisplayDialog.h InformationDisplayPropertiesDialog.h LabelSelectionViewController.h - LockAspectWarningDialog.h + LockAspectWarningDialog.h MacApplication.h MacDockMenu.h MapSettingsChartTwoLineHistoryWidget.h - MapSettingsColorBarPaletteOptionsWidget.h + MapSettingsColorBarPaletteOptionsWidget.h MapSettingsColorBarWidget.h MapSettingsFiberTrajectoryWidget.h MapSettingsLabelsWidget.h @@ -150,7 +151,7 @@ ELSE() MetaDataEditorDialog.h MetaDataEditorWidget.h MovieDialog.h - OffScreenOpenGLRenderer.h + OffScreenOpenGLRenderer.h OverlaySetViewController.h OverlaySettingsEditorDialog.h OverlayViewController.h @@ -165,8 +166,8 @@ ELSE() SceneBasePathWidget.h SceneCreateReplaceDialog.h SceneDialog.h - SceneFileInformationDialog.h - SceneDataFileTreeItemModel.h + SceneFileInformationDialog.h + SceneDataFileTreeItemModel.h ScenePreviewDialog.h SceneReplaceAllDialog.h SceneShowOptionsDialog.h @@ -176,7 +177,7 @@ ELSE() StructureSurfaceSelectionControl.h SurfacePropertiesEditorDialog.h SurfaceSelectionViewController.h - ThresholdingSetMapsDialog.h + ThresholdingSetMapsDialog.h TileTabsConfigurationDialog.h UserInputModeAnnotationsContextMenu.h UserInputModeAnnotationsWidget.h @@ -208,9 +209,10 @@ ELSE() WuQMacroDialog.h WuQMacroEditorDialog.h WuQMacroExecutor.h + WuQMacroHelperInterface.h WuQMacroManager.h WuQMacroMenu.h - WuQMacroShortCutKeyComboBox.h + WuQMacroShortCutKeyComboBox.h WuQMacroSignalEmitter.h WuQMacroSignalWatcher.h WuQMessageBox.h @@ -218,10 +220,10 @@ ELSE() WuQSpecialIncrementDoubleSpinBox.h WuQSpinBoxGroup.h WuQSpinBoxOddValue.h - WuQTabBar.h + WuQTabBar.h WuQTabWidget.h - WuQTabWidgetWithSizeHint.h - WuQTextEditorDialog.h + WuQTabWidgetWithSizeHint.h + WuQTextEditorDialog.h WuQTimedMessageDisplay.h WuQTreeWidget.h WuQTrueFalseComboBox.h diff --git a/src/GuiQt/GuiMacroHelper.cxx b/src/GuiQt/GuiMacroHelper.cxx index ca664c0b8..cce9dd873 100644 --- a/src/GuiQt/GuiMacroHelper.cxx +++ b/src/GuiQt/GuiMacroHelper.cxx @@ -27,9 +27,12 @@ #include "CaretAssert.h" #include "CaretPreferences.h" #include "EventManager.h" +#include "EventSceneActive.h" #include "EventUserInterfaceUpdate.h" #include "GuiManager.h" +#include "Scene.h" #include "SceneFile.h" +#include "SceneInfo.h" #include "SessionManager.h" #include "WuQMacroGroup.h" @@ -46,11 +49,10 @@ using namespace caret; /** * Constructor. */ -GuiMacroHelper::GuiMacroHelper() -: CaretObject(), -WuQMacroHelperInterface() +GuiMacroHelper::GuiMacroHelper(QObject* parent) +: WuQMacroHelperInterface(parent) { - + EventManager::get()->addEventListener(this, EventTypeEnum::EVENT_USER_INTERFACE_UPDATE); } /** @@ -58,16 +60,23 @@ WuQMacroHelperInterface() */ GuiMacroHelper::~GuiMacroHelper() { + EventManager::get()->removeAllEventsFromListener(this); } /** - * Get a description of this object's content. - * @return String describing this object's content. + * Receive an event + * + * @param event + * The event. */ -AString -GuiMacroHelper::toString() const +void +GuiMacroHelper::receiveEvent(Event* event) { - return "GuiMacroHelper"; + CaretAssert(event); + if (event->getEventType() == EventTypeEnum::EVENT_USER_INTERFACE_UPDATE) { + event->setEventProcessed(); + emit requestDialogsUpdate(); + } } /** @@ -82,9 +91,13 @@ GuiMacroHelper::getMacroGroups() CaretAssert(preferences); macroGroups.push_back(preferences->getMacros()); - const int32_t numSceneFiles = GuiManager::get()->getBrain()->getNumberOfSceneFiles(); - for (int32_t i = 0; i < numSceneFiles; i++) { - macroGroups.push_back(GuiManager::get()->getBrain()->getSceneFile(i)->getMacroGroup()); + EventSceneActive activeSceneEvent(EventSceneActive::MODE_GET); + EventManager::get()->sendEvent(activeSceneEvent.getPointer()); + if ( ! activeSceneEvent.isError()) { + Scene* activeScene = activeSceneEvent.getScene(); + if (activeScene != NULL) { + macroGroups.push_back(activeScene->getSceneInfo()->getMacroGroup()); + } } return macroGroups; diff --git a/src/GuiQt/GuiMacroHelper.h b/src/GuiQt/GuiMacroHelper.h index d86c37a03..b8d08d6c4 100644 --- a/src/GuiQt/GuiMacroHelper.h +++ b/src/GuiQt/GuiMacroHelper.h @@ -22,19 +22,19 @@ /*LICENSE_END*/ - #include -#include "CaretObject.h" +#include "EventListenerInterface.h" #include "WuQMacroHelperInterface.h" namespace caret { - class GuiMacroHelper : public CaretObject, public WuQMacroHelperInterface { + class GuiMacroHelper : public WuQMacroHelperInterface, public EventListenerInterface { + Q_OBJECT public: - GuiMacroHelper(); + GuiMacroHelper(QObject* parent); virtual ~GuiMacroHelper(); @@ -42,6 +42,8 @@ namespace caret { GuiMacroHelper& operator=(const GuiMacroHelper&) = delete; + void receiveEvent(Event* event) override; + virtual std::vector getMacroGroups(); virtual void macroWasModified(WuQMacro* macro); @@ -49,8 +51,6 @@ namespace caret { virtual void macroGroupWasModified(WuQMacroGroup* macroGroup); // ADD_NEW_METHODS_HERE - - virtual AString toString() const; private: // ADD_NEW_MEMBERS_HERE diff --git a/src/GuiQt/GuiManager.cxx b/src/GuiQt/GuiManager.cxx index bc0e4aa18..f04f4337a 100644 --- a/src/GuiQt/GuiManager.cxx +++ b/src/GuiQt/GuiManager.cxx @@ -198,7 +198,7 @@ GuiManager::initializeGuiManager() WuQMacroClassTypeEnum::addWidgetClassNameAlias("QTabBar", "caret::WuQTabBar"); WuQMacroManager::instance()->addParentObject(this); - WuQMacroManager::instance()->setMacroHelper(new GuiMacroHelper()); + WuQMacroManager::instance()->setMacroHelper(new GuiMacroHelper(this)); /* * Windows vector never changes size diff --git a/src/GuiQt/SceneDialog.cxx b/src/GuiQt/SceneDialog.cxx index 7fd83e7df..af26fd541 100644 --- a/src/GuiQt/SceneDialog.cxx +++ b/src/GuiQt/SceneDialog.cxx @@ -71,6 +71,7 @@ #include "EventHelpViewerDisplay.h" #include "EventUserInterfaceUpdate.h" #include "EventShowDataFileReadWarningsDialog.h" +#include "EventSceneActive.h" #include "FileInformation.h" #include "GuiManager.h" #include "ImageFile.h" @@ -417,13 +418,18 @@ SceneDialog::loadScenesIntoDialog(Scene* selectedSceneIn) selectedScene = getSelectedScene(); } + EventSceneActive activeSceneEvent(EventSceneActive::MODE_GET); + EventManager::get()->sendEvent(activeSceneEvent.getPointer()); + const Scene* activeScene = activeSceneEvent.getScene(); + for (std::vector::iterator iter = m_sceneClassInfoWidgets.begin(); iter != m_sceneClassInfoWidgets.end(); iter++) { SceneClassInfoWidget* sciw = *iter; sciw->blockSignals(true); sciw->updateContent(NULL, - -1); + -1, + false); } int32_t numberOfValidSceneInfoWidgets = 0; @@ -455,8 +461,10 @@ SceneDialog::loadScenesIntoDialog(Scene* selectedSceneIn) sciw = m_sceneClassInfoWidgets[i]; } + const bool activeSceneFlag(scene == activeScene); sciw->updateContent(scene, - i); + i, + activeSceneFlag); sciw->setBackgroundForSelected(i == 1); @@ -1109,6 +1117,12 @@ SceneDialog::sceneFileSelected(int /*index*/) updateSceneFileModifiedStatusLabel(); } +//Modified status for both scenes and _LIBCPP_POP_MACROS +//In Scene info panel indicate if scene is the active scene +//Add macro group to Scene +//Remove macro group from scene FILE +//List active scene file on macros Dialog + /** * Called when add new scene button clicked. */ @@ -1130,13 +1144,22 @@ SceneDialog::addNewSceneButtonClicked() sceneFile); if (newScene != NULL) { s_informUserAboutScenesOnExitFlag = false; + + /* + * Set the active scene + */ + EventSceneActive activeSceneEvent(EventSceneActive::MODE_SET); + activeSceneEvent.setScene(newScene); + EventManager::get()->sendEvent(activeSceneEvent.getPointer()); } loadScenesIntoDialog(newScene); } - updateSceneFileModifiedStatusLabel(); + + /* Ensures macros dialog gets updated with active scene */ + EventManager::get()->sendEvent(EventUserInterfaceUpdate().getPointer()); } /** @@ -1163,6 +1186,13 @@ SceneDialog::insertSceneButtonClicked() scene); if (newScene != NULL) { s_informUserAboutScenesOnExitFlag = false; + + /* + * Set the active scene + */ + EventSceneActive activeSceneEvent(EventSceneActive::MODE_SET); + activeSceneEvent.setScene(newScene); + EventManager::get()->sendEvent(activeSceneEvent.getPointer()); } loadScenesIntoDialog(newScene); @@ -1170,6 +1200,9 @@ SceneDialog::insertSceneButtonClicked() } updateSceneFileModifiedStatusLabel(); + + /* Ensures macros dialog gets updated with active scene */ + EventManager::get()->sendEvent(EventUserInterfaceUpdate().getPointer()); } /** @@ -1346,6 +1379,13 @@ SceneDialog::replaceAllScenesPushButtonClicked() newScene->setDescription(origScene->getDescription()); newScene->setBalsaSceneID(origScene->getBalsaSceneID()); + /* + * Set the active scene + */ + EventSceneActive activeSceneEvent(EventSceneActive::MODE_SET); + activeSceneEvent.setScene(newScene); + EventManager::get()->sendEvent(activeSceneEvent.getPointer()); + /* * Process options */ @@ -1392,7 +1432,10 @@ SceneDialog::replaceAllScenesPushButtonClicked() getSelectedSceneFile()->replaceScene(newScene, origScene); EventManager::get()->sendEvent(EventGraphicsUpdateAllWindows().getPointer()); - + + /* Ensures macros dialog gets updated with active scene */ + EventManager::get()->sendEvent(EventUserInterfaceUpdate().getPointer()); + loadScenesIntoDialog(newScene); const QImage* newImage = getQImageFromSceneInfo(newScene->getSceneInfo()); if (newImage != NULL) { @@ -1411,6 +1454,7 @@ SceneDialog::replaceAllScenesPushButtonClicked() } } + progressDialog.close(); CaretAssert(sceneNames.size() == sceneErrors.size()); @@ -1617,8 +1661,18 @@ SceneDialog::testScenesPushButtonClicked() origScene, false, errorMessage); + /* + * Set the active scene + */ + EventSceneActive activeSceneEvent(EventSceneActive::MODE_SET); + activeSceneEvent.setScene(origScene); + EventManager::get()->sendEvent(activeSceneEvent.getPointer()); + EventManager::get()->sendEvent(EventGraphicsUpdateAllWindows().getPointer()); - + + /* Ensures macros dialog gets updated with active scene */ + EventManager::get()->sendEvent(EventUserInterfaceUpdate().getPointer()); + /* * Always generate an image, even if the scene fails and failure may * be a very minor error. @@ -1811,6 +1865,13 @@ SceneDialog::replaceSceneButtonClicked() scene); if (newScene != NULL) { s_informUserAboutScenesOnExitFlag = false; + + /* + * Set the active scene + */ + EventSceneActive activeSceneEvent(EventSceneActive::MODE_SET); + activeSceneEvent.setScene(newScene); + EventManager::get()->sendEvent(activeSceneEvent.getPointer()); } loadScenesIntoDialog(newScene); @@ -1818,6 +1879,9 @@ SceneDialog::replaceSceneButtonClicked() updateSceneFileModifiedStatusLabel(); } } + + /* Ensures macros dialog gets updated with active scene */ + EventManager::get()->sendEvent(EventUserInterfaceUpdate().getPointer()); } /** @@ -2336,6 +2400,9 @@ SceneDialog::deleteSceneButtonClicked() SceneFile* sceneFile = getSelectedSceneFile(); sceneFile->removeScene(scene); updateDialog(); + + /* Ensures macros dialog gets updated with active scene */ + EventManager::get()->sendEvent(EventUserInterfaceUpdate().getPointer()); } } } @@ -2389,6 +2456,9 @@ SceneDialog::showSceneButtonClicked() scene, false); } + + /* Ensures macros dialog gets updated with active scene */ + EventManager::get()->sendEvent(EventUserInterfaceUpdate().getPointer()); } /** @@ -2534,6 +2604,9 @@ SceneDialog::displayScene(SceneFile* sceneFile, loadSceneFileComboBox(sceneFile); loadScenesIntoDialog(scene); + /* Ensures macros dialog gets updated with active scene */ + EventManager::get()->sendEvent(EventUserInterfaceUpdate().getPointer()); + return isSuccessful; } @@ -2679,6 +2752,13 @@ SceneDialog::displayScenePrivateWithErrorMessage(SceneFile* sceneFile, CaretPreferences* prefs = SessionManager::get()->getCaretPreferences(); prefs->addToPreviousSceneFiles(sceneFile->getFileName()); } + + /* + * Set the active scene + */ + EventSceneActive activeSceneEvent(EventSceneActive::MODE_SET); + activeSceneEvent.setScene(scene); + EventManager::get()->sendEvent(activeSceneEvent.getPointer()); } else { errorMessageOut = sceneErrorMessage; @@ -2899,6 +2979,7 @@ SceneClassInfoWidget::SceneClassInfoWidget() m_defaultBackgroundRole = backgroundRole(); m_defaultAutoFillBackgroundStatus = autoFillBackground(); + m_activeSceneLabel = new QLabel(); m_nameLabel = new QLabel(); m_nameLabel->setWordWrap(true); @@ -2914,6 +2995,7 @@ SceneClassInfoWidget::SceneClassInfoWidget() QVBoxLayout* rightLayout = new QVBoxLayout(m_rightSideWidget); rightLayout->setContentsMargins(0, 0, 0, 0); rightLayout->setSpacing(3); + rightLayout->addWidget(m_activeSceneLabel); rightLayout->addWidget(m_nameLabel,1); rightLayout->addWidget(m_sceneIdLabel); rightLayout->addWidget(m_descriptionLabel, 100); @@ -2968,10 +3050,13 @@ SceneClassInfoWidget::setBackgroundForSelected(const bool selected) * Scene for display. * @param sceneIndex * Index of the scene. + * @param activeSceneFlag + * True if this is the active scene */ void SceneClassInfoWidget::updateContent(Scene* scene, - const int32_t sceneIndex) + const int32_t sceneIndex, + const bool activeSceneFlag) { m_scene = scene; m_sceneIndex = sceneIndex; @@ -2989,6 +3074,14 @@ SceneClassInfoWidget::updateContent(Scene* scene, descriptionText, numLinesToDisplay); + if (activeSceneFlag) { + m_activeSceneLabel->setText("Current Scene"); + m_activeSceneLabel->setVisible(true); + } + else { + m_activeSceneLabel->setText(""); + m_activeSceneLabel->setVisible(false); + } m_nameLabel->setText(nameText); m_sceneIdLabel->setText(sceneIdText); m_descriptionLabel->setText(descriptionText); diff --git a/src/GuiQt/SceneDialog.h b/src/GuiQt/SceneDialog.h index 8338b19f8..1df6c24d3 100644 --- a/src/GuiQt/SceneDialog.h +++ b/src/GuiQt/SceneDialog.h @@ -274,7 +274,8 @@ namespace caret { ~SceneClassInfoWidget(); void updateContent(Scene* scene, - const int32_t sceneIndex); + const int32_t sceneIndex, + const bool activeSceneFlag); void setBackgroundForSelected(const bool selected); @@ -317,6 +318,8 @@ namespace caret { QLabel* m_previewImageLabel; + QLabel* m_activeSceneLabel; + QLabel* m_nameLabel; QLabel* m_sceneIdLabel; diff --git a/src/GuiQt/WuQMacroHelperInterface.h b/src/GuiQt/WuQMacroHelperInterface.h index d8071a710..5feb7cff6 100644 --- a/src/GuiQt/WuQMacroHelperInterface.h +++ b/src/GuiQt/WuQMacroHelperInterface.h @@ -27,16 +27,18 @@ * \ingroup GuiQt */ +#include #include namespace caret { class WuQMacro; class WuQMacroGroup; - class WuQMacroHelperInterface { + class WuQMacroHelperInterface : public QObject { + Q_OBJECT public: - WuQMacroHelperInterface() { } + WuQMacroHelperInterface(QObject* parent) : QObject(parent) { } virtual ~WuQMacroHelperInterface() { } @@ -67,6 +69,9 @@ namespace caret { // ADD_NEW_METHODS_HERE + signals: + void requestDialogsUpdate(); + private: // ADD_NEW_MEMBERS_HERE diff --git a/src/GuiQt/WuQMacroManager.cxx b/src/GuiQt/WuQMacroManager.cxx index a9a58d7f5..4330311b1 100644 --- a/src/GuiQt/WuQMacroManager.cxx +++ b/src/GuiQt/WuQMacroManager.cxx @@ -126,7 +126,12 @@ WuQMacroManager::instance() void WuQMacroManager::setMacroHelper(WuQMacroHelperInterface* macroHelper) { - m_macroHelper.reset(macroHelper); + m_macroHelper = macroHelper; + + if (macroHelper != NULL) { + QObject::connect(macroHelper, &WuQMacroHelperInterface::requestDialogsUpdate, + this, &WuQMacroManager::updateNonModalDialogs); + } } /** diff --git a/src/GuiQt/WuQMacroManager.h b/src/GuiQt/WuQMacroManager.h index 145377f49..c41fa0343 100644 --- a/src/GuiQt/WuQMacroManager.h +++ b/src/GuiQt/WuQMacroManager.h @@ -91,8 +91,6 @@ namespace caret { const WuQMacroExecutorOptions* getExecutorOptions() const; - void updateNonModalDialogs(); - bool editMacroAttributes(QWidget* parent, WuQMacro* macro); @@ -130,6 +128,9 @@ namespace caret { // ADD_NEW_METHODS_HERE + public slots: + void updateNonModalDialogs(); + private: WuQMacroManager(const QString& name, QObject* parent = NULL); @@ -150,7 +151,7 @@ namespace caret { std::unique_ptr m_executorOptions; - std::unique_ptr m_macroHelper; + WuQMacroHelperInterface* m_macroHelper; // ADD_NEW_MEMBERS_HERE diff --git a/src/Scenes/SceneInfo.cxx b/src/Scenes/SceneInfo.cxx index a5e3cfbe4..b1f397e74 100644 --- a/src/Scenes/SceneInfo.cxx +++ b/src/Scenes/SceneInfo.cxx @@ -26,11 +26,15 @@ #include "CaretAssert.h" #include "CaretLogger.h" #include "SceneXmlElements.h" +#include "WuQMacroGroup.h" +#include "WuQMacroGroupXmlWriter.h" #include "XmlAttributes.h" +#include "XmlUtilities.h" #include "XmlWriter.h" using namespace caret; + /** * \class caret::SceneInfo @@ -44,7 +48,11 @@ using namespace caret; SceneInfo::SceneInfo() : CaretObjectTracksModification() { - + static int counter = 1; + const AString macroGroupName("SceneFile_" + + AString::number(counter)); + m_macroGroup.reset(new WuQMacroGroup(macroGroupName)); + m_macroGroup->clearModified(); } SceneInfo::SceneInfo(const SceneInfo& rhs) : CaretObjectTracksModification() @@ -63,6 +71,34 @@ SceneInfo::~SceneInfo() { } +/** + * @return True if this scene info is modified + */ +bool +SceneInfo::isModified() const +{ + if (CaretObjectTracksModification::isModified()) { + return true; + } + + if (m_macroGroup->isModified()) { + return true; + } + + return false; +} + +/** + * @return Is this instance modified? + */ +void +SceneInfo::clearModified() +{ + CaretObjectTracksModification::clearModified(); + + m_macroGroup->clearModified(); +} + /** * @return name of scene */ @@ -82,6 +118,8 @@ SceneInfo::setName(const AString& sceneName) { if (sceneName != m_sceneName) { m_sceneName = sceneName; + m_macroGroup->setName("Scene: " + + m_sceneName); setModified(); } } @@ -140,8 +178,8 @@ void SceneInfo::getImageBytes(QByteArray& imageBytesOut, AString& imageFormatOut) const { - imageBytesOut = m_imageBytes; - imageFormatOut = m_imageFormat; + imageBytesOut = m_imageBytes; + imageFormatOut = m_imageFormat; } /** @@ -184,6 +222,24 @@ SceneInfo::setBalsaSceneID(const AString& balsaSceneID) } } +/** + * @return The macro group + */ +WuQMacroGroup* +SceneInfo::getMacroGroup() +{ + return m_macroGroup.get(); +} + +/** + * @return The macro group (const method) + */ +const WuQMacroGroup* +SceneInfo::getMacroGroup() const +{ + return m_macroGroup.get(); +} + /** * Write the scene info element. * @@ -212,6 +268,19 @@ SceneInfo::writeSceneInfo(XmlWriter& xmlWriter, xmlWriter.writeElementCData(SceneXmlElements::SCENE_INFO_DESCRIPTION_TAG, m_sceneDescription); + /* + * Write macro group + */ + if (m_macroGroup->getNumberOfMacros() > 0) { + QString textString; + WuQMacroGroupXmlWriter macroWriter; + macroWriter.writeToString(m_macroGroup.get(), + textString); + textString = XmlUtilities::encodeXmlSpecialCharacters(textString); + xmlWriter.writeElementCharacters(SceneXmlElements::SCENE_INFO_MACRO_GROUP, + textString); + } + writeSceneInfoImage(xmlWriter, SceneXmlElements::SCENE_INFO_IMAGE_TAG, m_imageBytes, diff --git a/src/Scenes/SceneInfo.h b/src/Scenes/SceneInfo.h index b4358c77e..abffe5a1d 100644 --- a/src/Scenes/SceneInfo.h +++ b/src/Scenes/SceneInfo.h @@ -27,6 +27,8 @@ namespace caret { + class WuQMacroGroup; + class XmlWriter; class SceneInfo : public CaretObjectTracksModification { @@ -38,6 +40,10 @@ namespace caret { virtual ~SceneInfo(); + bool isModified() const override; + + void clearModified() override; + AString getName() const; void setName(const AString& sceneName); @@ -70,6 +76,10 @@ namespace caret { const QByteArray& imageBytes, const AString& imageFormat) const; + WuQMacroGroup* getMacroGroup(); + + const WuQMacroGroup* getMacroGroup() const; + private: SceneInfo& operator=(const SceneInfo&); @@ -88,6 +98,8 @@ namespace caret { /** format of thumbnail image (eg: jpg, ppm, etc.) */ AString m_imageFormat; + std::unique_ptr m_macroGroup; + // ADD_NEW_MEMBERS_HERE }; diff --git a/src/Scenes/SceneInfoSaxReader.cxx b/src/Scenes/SceneInfoSaxReader.cxx index 7f5d35adf..6ca9f8823 100644 --- a/src/Scenes/SceneInfoSaxReader.cxx +++ b/src/Scenes/SceneInfoSaxReader.cxx @@ -24,7 +24,7 @@ #include "SceneInfo.h" #include "SceneInfoSaxReader.h" #include "SceneXmlElements.h" - +#include "WuQMacroGroupXmlReader.h" #include "XmlAttributes.h" #include "XmlException.h" #include "XmlUtilities.h" @@ -104,6 +104,10 @@ SceneInfoSaxReader::startElement(const AString& /* namespaceURI */, else if (qName == SceneXmlElements::SCENE_INFO_BALSA_SCENE_ID_TAG) { m_state = STATE_SCENE_INFO_BALSA_ID; } + else if (qName == SceneXmlElements::SCENE_INFO_MACRO_GROUP) { + m_state = STATE_SCENE_INFO_MACRO_GROUP; + break; + } else { const AString msg = XmlUtilities::createInvalidChildElementMessage(SceneXmlElements::SCENE_INFO_TAG, qName); @@ -112,6 +116,8 @@ SceneInfoSaxReader::startElement(const AString& /* namespaceURI */, m_state = STATE_SCENE_INFO_UNRECOGNIZED; } break; + case STATE_SCENE_INFO_MACRO_GROUP: + break; case STATE_SCENE_INFO_NAME: break; case STATE_SCENE_INFO_DESCRIPTION: @@ -170,6 +176,20 @@ SceneInfoSaxReader::endElement(const AString& /* namspaceURI */, break; case STATE_SCENE_INFO_UNRECOGNIZED: break; + case STATE_SCENE_INFO_MACRO_GROUP: + { + WuQMacroGroup* macroGroup = m_sceneInfo->getMacroGroup(); + WuQMacroGroupXmlReader reader; + reader.readFromString(m_elementText, + macroGroup); + if (reader.hasError()) { + CaretLogSevere(reader.getErrorMessage()); + } + else if (reader.hasWarnings()) { + CaretLogWarning(reader.getWarningMessage()); + } + } + break; } /* diff --git a/src/Scenes/SceneInfoSaxReader.h b/src/Scenes/SceneInfoSaxReader.h index aae2479c9..4a14f0b2a 100644 --- a/src/Scenes/SceneInfoSaxReader.h +++ b/src/Scenes/SceneInfoSaxReader.h @@ -81,6 +81,8 @@ namespace caret { STATE_SCENE_INFO_DESCRIPTION, /// processing Scene Info thumbnail tag STATE_SCENE_INFO_IMAGE_THUMBNAIL, + /// processing Scene Info macro group tag + STATE_SCENE_INFO_MACRO_GROUP, /// process an unrecognized element in Scene Info STATE_SCENE_INFO_UNRECOGNIZED }; diff --git a/src/Scenes/SceneXmlElements.h b/src/Scenes/SceneXmlElements.h index cded5149a..3ae40f593 100644 --- a/src/Scenes/SceneXmlElements.h +++ b/src/Scenes/SceneXmlElements.h @@ -178,9 +178,9 @@ namespace caret { static const AString SCENE_INFO_BASE_PATH_TYPE = "BasePathType"; /** - * XML Tag for Macros in Scene File + * XML Tag for Macros in Scene Info */ - static const AString SCENE_FILE_MACRO_GROUP = "SceneFileMacroGroup"; + static const AString SCENE_INFO_MACRO_GROUP = "SceneFileMacroGroup"; } // namespace SceneXmlElements -- GitLab From fe43da7c9e5226f06086b518115eb2c212252cf4 Mon Sep 17 00:00:00 2001 From: John Harwell Date: Thu, 3 Jan 2019 15:13:08 -0600 Subject: [PATCH 160/427] WB-834: On Macros Dialog, default to last macro group (which is usually scene) since it will be most common use case. --- src/GuiQt/WuQMacroCreateDialog.cxx | 5 ++++- src/GuiQt/WuQMacroDialog.cxx | 2 +- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/src/GuiQt/WuQMacroCreateDialog.cxx b/src/GuiQt/WuQMacroCreateDialog.cxx index a60257f88..fdfd9d6c8 100644 --- a/src/GuiQt/WuQMacroCreateDialog.cxx +++ b/src/GuiQt/WuQMacroCreateDialog.cxx @@ -74,7 +74,7 @@ WuQMacroCreateDialog::WuQMacroCreateDialog(QWidget* parent) m_macroDescriptionTextEdit = new QPlainTextEdit(); m_macroDescriptionTextEdit->setFixedHeight(100); - int32_t selectedMacroGroupIndex(0); + int32_t selectedMacroGroupIndex(-1); m_macroGroupComboBox = new QComboBox(); for (auto mg : m_macroGroups) { if (mg->getUniqueIdentifier() == s_lastSelectedMacroGroupIdentifier) { @@ -82,6 +82,9 @@ WuQMacroCreateDialog::WuQMacroCreateDialog(QWidget* parent) } m_macroGroupComboBox->addItem(mg->getName()); } + if (selectedMacroGroupIndex < 0) { + selectedMacroGroupIndex = m_macroGroupComboBox->count() - 1; + } if ((selectedMacroGroupIndex >= 0) && (selectedMacroGroupIndex < m_macroGroupComboBox->count())) { m_macroGroupComboBox->setCurrentIndex(selectedMacroGroupIndex); diff --git a/src/GuiQt/WuQMacroDialog.cxx b/src/GuiQt/WuQMacroDialog.cxx index 34d5a6bc9..4c251bb98 100644 --- a/src/GuiQt/WuQMacroDialog.cxx +++ b/src/GuiQt/WuQMacroDialog.cxx @@ -306,7 +306,7 @@ WuQMacroDialog::updateDialogContents() int32_t selectedIndex = m_macroGroupComboBox->findData(selectedUniqueIdentifer); if (selectedIndex < 0) { - selectedIndex = 0; + selectedIndex = m_macroGroupComboBox->count() - 1; } if ((selectedIndex >= 0) && (selectedIndex < m_macroGroupComboBox->count())) { -- GitLab From 8a36ce4ebf31c7a1f37f73501e877f8b63fdbd49 Mon Sep 17 00:00:00 2001 From: John Harwell Date: Fri, 4 Jan 2019 16:32:29 -0600 Subject: [PATCH 161/427] WB-834: Revising Macro Dialog to show both a macro and its commands so it looks more like PowerPoint Animation GUI. --- src/Common/CMakeLists.txt | 3 + src/Common/WuQMacro.cxx | 106 +++++++--- src/Common/WuQMacro.h | 17 +- src/Common/WuQMacroCommand.cxx | 140 +++++++++++-- src/Common/WuQMacroCommand.h | 15 +- src/Common/WuQMacroFile.cxx | 80 ++++---- src/Common/WuQMacroFile.h | 8 +- src/Common/WuQMacroGroup.cxx | 189 ++++++++---------- src/Common/WuQMacroGroup.h | 20 +- src/Common/WuQMacroStandardItemTypes.cxx | 71 +++++++ src/Common/WuQMacroStandardItemTypes.h | 63 ++++++ src/GuiQt/CMakeLists.txt | 20 +- src/GuiQt/WuQMacroDialog.cxx | 241 ++++++++++++++++------- src/GuiQt/WuQMacroDialog.h | 26 ++- src/Scenes/CMakeLists.txt | 1 + 15 files changed, 706 insertions(+), 294 deletions(-) create mode 100644 src/Common/WuQMacroStandardItemTypes.cxx create mode 100644 src/Common/WuQMacroStandardItemTypes.h diff --git a/src/Common/CMakeLists.txt b/src/Common/CMakeLists.txt index 89d13da89..c2fa2f61a 100644 --- a/src/Common/CMakeLists.txt +++ b/src/Common/CMakeLists.txt @@ -14,6 +14,7 @@ SET(QT_USE_QTNETWORK TRUE) if(Qt5_FOUND) include_directories(${Qt5Core_INCLUDE_DIRS}) + include_directories(${Qt5Gui_INCLUDE_DIRS}) include_directories(${Qt5Network_INCLUDE_DIRS}) include_directories(${Qt5Xml_INCLUDE_DIRS}) endif() @@ -173,6 +174,7 @@ WuQMacroModeEnum.h WuQMacroMouseEventInfo.h WuQMacroMouseEventTypeEnum.h WuQMacroShortCutKeyEnum.h +WuQMacroStandardItemTypes.h YokingGroupEnum.h ${MOC_SOURCE_FILES} @@ -287,6 +289,7 @@ WuQMacroModeEnum.cxx WuQMacroMouseEventInfo.cxx WuQMacroMouseEventTypeEnum.cxx WuQMacroShortCutKeyEnum.cxx +WuQMacroStandardItemTypes.cxx YokingGroupEnum.cxx ) diff --git a/src/Common/WuQMacro.cxx b/src/Common/WuQMacro.cxx index 3a32638b3..a21ed74a4 100644 --- a/src/Common/WuQMacro.cxx +++ b/src/Common/WuQMacro.cxx @@ -27,6 +27,7 @@ #include "CaretAssert.h" #include "WuQMacroCommand.h" +#include "WuQMacroStandardItemTypes.h" using namespace caret; @@ -37,14 +38,18 @@ using namespace caret; * \brief Contains a sequence of WuQMacroCommand's * \ingroup WuQMacro * + * Subclasses QStandardItem so that instance can be in a tree widget */ /** * Constructor. */ WuQMacro::WuQMacro() -: CaretObjectTracksModification() +: QStandardItem(), +TracksModificationInterface() { + setFlags(Qt::ItemIsEnabled + | Qt::ItemIsSelectable); m_uniqueIdentifier = QUuid::createUuid().toString(); } @@ -62,10 +67,9 @@ WuQMacro::~WuQMacro() void WuQMacro::clearCommands() { - for (auto mc : m_macroCommands) { - delete mc; - } - m_macroCommands.clear(); + const int32_t numItems = rowCount(); + removeRows(0, numItems); + CaretAssert(rowCount() == 0); } /** @@ -74,8 +78,11 @@ WuQMacro::clearCommands() * Object that is copied. */ WuQMacro::WuQMacro(const WuQMacro& obj) -: CaretObjectTracksModification(obj) +: QStandardItem(obj), +TracksModificationInterface() { + setFlags(Qt::ItemIsEnabled + | Qt::ItemIsSelectable); m_uniqueIdentifier = QUuid::createUuid().toString(); this->copyHelperWuQMacro(obj); @@ -92,7 +99,7 @@ WuQMacro& WuQMacro::operator=(const WuQMacro& obj) { if (this != &obj) { - CaretObjectTracksModification::operator=(obj); + QStandardItem::operator=(obj); this->copyHelperWuQMacro(obj); } return *this; @@ -110,19 +117,39 @@ WuQMacro::copyHelperWuQMacro(const WuQMacro& obj) clearCommands(); - for (auto mc : obj.m_macroCommands) { - addMacroCommand(new WuQMacroCommand(*mc)); + const int32_t numRows = obj.rowCount(); + for (int32_t i = 0; i < numRows; i++) { + const QStandardItem* item = obj.child(i); + CaretAssert(item); + const WuQMacroCommand* command = dynamic_cast(item); + CaretAssert(command); + WuQMacroCommand* commandCopy = new WuQMacroCommand(*command); + CaretAssert(commandCopy); + addMacroCommand(commandCopy); } - m_name = obj.m_name; + setText(obj.text()); m_description = obj.m_description; m_shortCutKey = obj.m_shortCutKey; } +/** + * @return The type of this item for Qt + */ +int +WuQMacro::type() const +{ + /* + * This must be different than the type returned by of macro + subclasses of QStandardItem + */ + return WuQMacroStandardItemTypes::typeWuQMacro(); +} + void WuQMacro::addMacroCommand(WuQMacroCommand* macroCommand) { - m_macroCommands.push_back(macroCommand); + appendRow(macroCommand); setModified(); } @@ -158,7 +185,7 @@ WuQMacro::setUniqueIdentifier(const QString& uniqueIdentifier) QString WuQMacro::getName() const { - return m_name; + return text(); } /** @@ -170,8 +197,8 @@ WuQMacro::getName() const void WuQMacro::setName(const QString& name) { - if (m_name != name) { - m_name = name; + if (name != text()) { + setText(name); setModified(); } } @@ -230,7 +257,7 @@ WuQMacro::setShortCutKey(const WuQMacroShortCutKeyEnum::Enum shortCutKey) int32_t WuQMacro::getNumberOfMacroCommands() const { - return m_macroCommands.size(); + return rowCount(); } /** @@ -242,8 +269,13 @@ WuQMacro::getNumberOfMacroCommands() const const WuQMacroCommand* WuQMacro::getMacroCommandAtIndex(const int32_t index) const { - CaretAssertVectorIndex(m_macroCommands, index); - return m_macroCommands[index]; + CaretAssert((index >= 0) + && (index < rowCount())); + QStandardItem* item = child(index); + CaretAssert(item); + WuQMacroCommand* command = dynamic_cast(item); + CaretAssert(command); + return command; } /** @@ -255,8 +287,13 @@ WuQMacro::getMacroCommandAtIndex(const int32_t index) const WuQMacroCommand* WuQMacro::getMacroCommandAtIndex(const int32_t index) { - CaretAssertVectorIndex(m_macroCommands, index); - return m_macroCommands[index]; + CaretAssert((index >= 0) + && (index < rowCount())); + QStandardItem* item = child(index); + CaretAssert(item); + WuQMacroCommand* command = dynamic_cast(item); + CaretAssert(command); + return command; } /** @@ -265,12 +302,13 @@ WuQMacro::getMacroCommandAtIndex(const int32_t index) bool WuQMacro::isModified() const { - if (CaretObjectTracksModification::isModified()) { + if (m_modifiedStatusFlag) { return true; } - for (const auto mc : m_macroCommands) { - if (mc->isModified()) { + const int32_t numItems = getNumberOfMacroCommands(); + for (int32_t i = 0; i < numItems; i++) { + if (getMacroCommandAtIndex(i)->isModified()) { return true; } } @@ -284,13 +322,23 @@ WuQMacro::isModified() const void WuQMacro::clearModified() { - CaretObjectTracksModification::clearModified(); + m_modifiedStatusFlag = false; - for (auto mc : m_macroCommands) { - mc->clearModified(); + const int32_t numItems = getNumberOfMacroCommands(); + for (int32_t i = 0; i < numItems; i++) { + getMacroCommandAtIndex(i)->clearModified(); } } +/** + * Set the modification status to modified + */ +void +WuQMacro::setModified() +{ + m_modifiedStatusFlag = true; +} + /** * Get a description of this object's content. * @return String describing this object's content. @@ -300,12 +348,14 @@ WuQMacro::toString() const { QString s("WuQMacro\n"); - s.append("Name=" + m_name + "\n"); + s.append("Name=" + text() + "\n"); s.append("Description=" + m_description + "\n"); s.append("ShortCutKey=" + WuQMacroShortCutKeyEnum::toGuiName(m_shortCutKey) + "\n"); + s.append("UUID=" + m_uniqueIdentifier + "\n"); - for (auto mc : m_macroCommands) { - s.append(mc->toString() + "\n"); + const int32_t numItems = getNumberOfMacroCommands(); + for (int32_t i = 0; i < numItems; i++) { + s.append(getMacroCommandAtIndex(i)->toString()); } return s; diff --git a/src/Common/WuQMacro.h b/src/Common/WuQMacro.h index 13b822b76..13cde6521 100644 --- a/src/Common/WuQMacro.h +++ b/src/Common/WuQMacro.h @@ -22,17 +22,18 @@ /*LICENSE_END*/ - #include -#include "CaretObjectTracksModification.h" +#include + +#include "TracksModificationInterface.h" #include "WuQMacroShortCutKeyEnum.h" namespace caret { class WuQMacroCommand; - class WuQMacro : public CaretObjectTracksModification { + class WuQMacro : public QStandardItem, public TracksModificationInterface { public: WuQMacro(); @@ -73,6 +74,10 @@ namespace caret { virtual void clearModified() override; + virtual void setModified() override; + + virtual int type() const override; + // ADD_NEW_METHODS_HERE private: @@ -80,16 +85,14 @@ namespace caret { void clearCommands(); - std::vector m_macroCommands; - QString m_uniqueIdentifier; - QString m_name; - QString m_description; WuQMacroShortCutKeyEnum::Enum m_shortCutKey = WuQMacroShortCutKeyEnum::Key_None; + bool m_modifiedStatusFlag = false; + // ADD_NEW_MEMBERS_HERE }; diff --git a/src/Common/WuQMacroCommand.cxx b/src/Common/WuQMacroCommand.cxx index bdbb4055f..1fe5f1627 100644 --- a/src/Common/WuQMacroCommand.cxx +++ b/src/Common/WuQMacroCommand.cxx @@ -25,6 +25,7 @@ #include "CaretAssert.h" #include "WuQMacroMouseEventInfo.h" +#include "WuQMacroStandardItemTypes.h" using namespace caret; @@ -50,80 +51,144 @@ WuQMacroCommand::WuQMacroCommand(const WuQMacroClassTypeEnum::Enum classType, const QString& objectName, const QVariant dataValue, const QVariant dataValueTwo) -: CaretObjectTracksModification(), +: QStandardItem(), +TracksModificationInterface(), m_classType(classType), m_objectName(objectName), m_dataValue(dataValue), m_dataValueTwo(dataValueTwo), m_macroMouseEvent(NULL) { + setFlags(Qt::ItemIsEnabled + | Qt::ItemIsSelectable); + /* * Second data value is used by only some commands */ m_dataTypeTwo = WuQMacroDataValueTypeEnum::STRING; + QString title = "Unknown"; + switch (m_classType) { case WuQMacroClassTypeEnum::ACTION: m_dataType = WuQMacroDataValueTypeEnum::BOOLEAN; + title = ("Turn " + + QString((m_dataValue.toBool() ? "On" : "Off"))); break; case WuQMacroClassTypeEnum::ACTION_GROUP: m_dataType = WuQMacroDataValueTypeEnum::INTEGER; m_dataTypeTwo = WuQMacroDataValueTypeEnum::STRING; + title = ("Select Name " + + m_dataValueTwo.toString() + + " else " + + " index " + + QString::number(m_dataValue.toInt())); break; case WuQMacroClassTypeEnum::BUTTON_GROUP: m_dataType = WuQMacroDataValueTypeEnum::INTEGER; m_dataTypeTwo = WuQMacroDataValueTypeEnum::STRING; + title = ("Select Name " + + m_dataValueTwo.toString() + + " else " + + " index " + + QString::number(m_dataValue.toInt())); break; case WuQMacroClassTypeEnum::CHECK_BOX: m_dataType = WuQMacroDataValueTypeEnum::BOOLEAN; + title = ("Turn " + + QString((m_dataValue.toBool() ? "On" : "Off"))); break; case WuQMacroClassTypeEnum::COMBO_BOX: m_dataType = WuQMacroDataValueTypeEnum::INTEGER; m_dataTypeTwo = WuQMacroDataValueTypeEnum::STRING; + title = ("Select Name " + + m_dataValueTwo.toString() + + " else " + + " index " + + QString::number(m_dataValue.toInt())); break; case WuQMacroClassTypeEnum::DOUBLE_SPIN_BOX: m_dataType = WuQMacroDataValueTypeEnum::FLOAT; + title = ("Set to value " + + QString::number(m_dataValue.toFloat())); break; case WuQMacroClassTypeEnum::INVALID: + title = ""; break; case WuQMacroClassTypeEnum::LINE_EDIT: m_dataType = WuQMacroDataValueTypeEnum::STRING; + title = ("Set to text " + + m_dataValue.toString()); break; case WuQMacroClassTypeEnum::LIST_WIDGET: m_dataType = WuQMacroDataValueTypeEnum::INTEGER; m_dataTypeTwo = WuQMacroDataValueTypeEnum::STRING; + title = ("Select Name " + + m_dataValueTwo.toString() + + " else " + + " index " + + QString::number(m_dataValue.toInt())); break; case WuQMacroClassTypeEnum::MENU: m_dataType = WuQMacroDataValueTypeEnum::INTEGER; m_dataTypeTwo = WuQMacroDataValueTypeEnum::STRING; + title = ("Select Name " + + m_dataValueTwo.toString() + + " else " + + " index " + + QString::number(m_dataValue.toInt())); break; case WuQMacroClassTypeEnum::MOUSE_USER_EVENT: m_dataType = WuQMacroDataValueTypeEnum::MOUSE; + title = ""; CaretAssertMessage(0, "Must use constructor for mouse event"); break; case WuQMacroClassTypeEnum::PUSH_BUTTON: m_dataType = WuQMacroDataValueTypeEnum::BOOLEAN; + title = ("Click Button"); break; case WuQMacroClassTypeEnum::RADIO_BUTTON: m_dataType = WuQMacroDataValueTypeEnum::BOOLEAN; + title = ("Turn " + + QString((m_dataValue.toBool() ? "On" : "Off"))); break; case WuQMacroClassTypeEnum::SLIDER: m_dataType = WuQMacroDataValueTypeEnum::INTEGER; + title = ("Move to " + + AString::number(m_dataValue.toInt())); break; case WuQMacroClassTypeEnum::SPIN_BOX: m_dataType = WuQMacroDataValueTypeEnum::INTEGER; + title = ("Set to " + + AString::number(m_dataValue.toInt())); break; case WuQMacroClassTypeEnum::TAB_BAR: m_dataType = WuQMacroDataValueTypeEnum::INTEGER; + m_dataTypeTwo = WuQMacroDataValueTypeEnum::STRING; + title = ("Select Name " + + m_dataValueTwo.toString() + + " else " + + " index " + + QString::number(m_dataValue.toInt())); break; case WuQMacroClassTypeEnum::TAB_WIDGET: m_dataType = WuQMacroDataValueTypeEnum::INTEGER; + m_dataTypeTwo = WuQMacroDataValueTypeEnum::STRING; + title = ("Select Name " + + m_dataValueTwo.toString() + + " else " + + " index " + + QString::number(m_dataValue.toInt())); break; case WuQMacroClassTypeEnum::TOOL_BUTTON: m_dataType = WuQMacroDataValueTypeEnum::BOOLEAN; + title = ("Click Button"); break; } + setText(title + + " " + + m_objectName); setModified(); } @@ -139,16 +204,20 @@ m_macroMouseEvent(NULL) */ WuQMacroCommand::WuQMacroCommand(const QString& objectName, WuQMacroMouseEventInfo* mouseEventInfo) -: CaretObjectTracksModification(), +: QStandardItem(), +TracksModificationInterface(), m_classType(WuQMacroClassTypeEnum::MOUSE_USER_EVENT), m_objectName(objectName), m_dataValue((int)0), m_dataValueTwo(""), m_macroMouseEvent(mouseEventInfo) { + setFlags(Qt::ItemIsEnabled + | Qt::ItemIsSelectable); m_dataType = WuQMacroDataValueTypeEnum::MOUSE; m_dataTypeTwo = WuQMacroDataValueTypeEnum::STRING; - + setText("Mouse"); + setModified(); } @@ -168,8 +237,11 @@ WuQMacroCommand::~WuQMacroCommand() * Object that is copied. */ WuQMacroCommand::WuQMacroCommand(const WuQMacroCommand& obj) -: CaretObjectTracksModification(obj) +: QStandardItem(obj), +TracksModificationInterface() { + setFlags(Qt::ItemIsEnabled + | Qt::ItemIsSelectable); this->copyHelperWuQMacroCommand(obj); } @@ -184,12 +256,25 @@ WuQMacroCommand& WuQMacroCommand::operator=(const WuQMacroCommand& obj) { if (this != &obj) { - CaretObjectTracksModification::operator=(obj); + QStandardItem::operator=(obj); this->copyHelperWuQMacroCommand(obj); } return *this; } +/** + * @return The type of this item for Qt + */ +int +WuQMacroCommand::type() const +{ + /* + * This must be different than the type returned by of macro + subclasses of QStandardItem + */ + return WuQMacroStandardItemTypes::typeWuQMacroCommand(); +} + /** * Helps with copying an object of this type. * @param obj @@ -198,7 +283,7 @@ WuQMacroCommand::operator=(const WuQMacroCommand& obj) void WuQMacroCommand::copyHelperWuQMacroCommand(const WuQMacroCommand& obj) { - m_classType = obj.m_classType; + m_classType = obj.m_classType; m_objectName = obj.m_objectName; m_dataValue = obj.m_dataValue; m_dataType = obj.m_dataType; @@ -220,6 +305,7 @@ WuQMacroCommand::copyHelperWuQMacroCommand(const WuQMacroCommand& obj) case WuQMacroDataValueTypeEnum::STRING: break; } + setText(obj.text()); } /** @@ -285,6 +371,37 @@ WuQMacroCommand::getMouseEventInfo() const return m_macroMouseEvent; } +/** + * @return True if this instance is modified + */ +bool +WuQMacroCommand::isModified() const +{ + if (m_modifiedStatusFlag) { + return true; + } + + return false; +} + +/** + * Clear the modified status + */ +void +WuQMacroCommand::clearModified() +{ + m_modifiedStatusFlag = false; +} + +/** + * Set the modification status to modified + */ +void +WuQMacroCommand::setModified() +{ + m_modifiedStatusFlag = true; +} + /** * Get a description of this object's content. * @return String describing this object's content. @@ -292,11 +409,12 @@ WuQMacroCommand::getMouseEventInfo() const AString WuQMacroCommand::toString() const { - QString s("WuQMacroCommand name=%1, type=%2, value=%3, valueTwo=%4"); - s = s.arg(m_objectName - ).arg(WuQMacroClassTypeEnum::toGuiName(m_classType) - ).arg(m_dataValue.toString() - ).arg(m_dataValueTwo.toString()); + QString s("WuQMacroCommand text=%1 name=%2, type=%3, value=%4, valueTwo=%5"); + s = s.arg(text() + ).arg(m_objectName + ).arg(WuQMacroClassTypeEnum::toGuiName(m_classType) + ).arg(m_dataValue.toString() + ).arg(m_dataValueTwo.toString()); return s; } diff --git a/src/Common/WuQMacroCommand.h b/src/Common/WuQMacroCommand.h index 8d19a6c1b..240c072d3 100644 --- a/src/Common/WuQMacroCommand.h +++ b/src/Common/WuQMacroCommand.h @@ -25,9 +25,10 @@ #include +#include #include -#include "CaretObjectTracksModification.h" +#include "TracksModificationInterface.h" #include "WuQMacroDataValueTypeEnum.h" #include "WuQMacroClassTypeEnum.h" @@ -37,7 +38,7 @@ namespace caret { class WuQMacroMouseEventInfo; - class WuQMacroCommand : public CaretObjectTracksModification { + class WuQMacroCommand : public QStandardItem, public TracksModificationInterface { public: @@ -71,8 +72,16 @@ namespace caret { // ADD_NEW_METHODS_HERE + virtual bool isModified() const override; + + virtual void clearModified() override; + + virtual void setModified() override; + virtual AString toString() const; + virtual int type() const override; + private: void copyHelperWuQMacroCommand(const WuQMacroCommand& obj); @@ -90,6 +99,8 @@ namespace caret { WuQMacroMouseEventInfo* m_macroMouseEvent; + bool m_modifiedStatusFlag = false; + // ADD_NEW_MEMBERS_HERE }; diff --git a/src/Common/WuQMacroFile.cxx b/src/Common/WuQMacroFile.cxx index 89a03637c..145e9d713 100644 --- a/src/Common/WuQMacroFile.cxx +++ b/src/Common/WuQMacroFile.cxx @@ -61,46 +61,46 @@ WuQMacroFile::~WuQMacroFile() { } -/** - * Copy constructor. - * @param obj - * Object that is copied. - */ -WuQMacroFile::WuQMacroFile(const WuQMacroFile& obj) -: DataFile(obj) -{ - this->copyHelperWuQMacroFile(obj); -} - -/** - * Assignment operator. - * @param obj - * Data copied from obj to this. - * @return - * Reference to this object. - */ -WuQMacroFile& -WuQMacroFile::operator=(const WuQMacroFile& obj) -{ - if (this != &obj) { - DataFile::operator=(obj); - this->copyHelperWuQMacroFile(obj); - } - return *this; -} - -/** - * Helps with copying an object of this type. - * @param obj - * Object that is copied. - */ -void -WuQMacroFile::copyHelperWuQMacroFile(const WuQMacroFile& obj) -{ - *m_macroGroup = *obj.m_macroGroup; - - setMacroGroupName(getFileName()); -} +///** +// * Copy constructor. +// * @param obj +// * Object that is copied. +// */ +//WuQMacroFile::WuQMacroFile(const WuQMacroFile& obj) +//: DataFile(obj) +//{ +// this->copyHelperWuQMacroFile(obj); +//} +// +///** +// * Assignment operator. +// * @param obj +// * Data copied from obj to this. +// * @return +// * Reference to this object. +// */ +//WuQMacroFile& +//WuQMacroFile::operator=(const WuQMacroFile& obj) +//{ +// if (this != &obj) { +// DataFile::operator=(obj); +// this->copyHelperWuQMacroFile(obj); +// } +// return *this; +//} +// +///** +// * Helps with copying an object of this type. +// * @param obj +// * Object that is copied. +// */ +//void +//WuQMacroFile::copyHelperWuQMacroFile(const WuQMacroFile& obj) +//{ +// *m_macroGroup = *obj.m_macroGroup; +// +// setMacroGroupName(getFileName()); +//} /** * @return True if this file is empty (has no macros) diff --git a/src/Common/WuQMacroFile.h b/src/Common/WuQMacroFile.h index e87ee5084..34ad04a19 100644 --- a/src/Common/WuQMacroFile.h +++ b/src/Common/WuQMacroFile.h @@ -41,9 +41,9 @@ namespace caret { virtual ~WuQMacroFile(); - WuQMacroFile(const WuQMacroFile& obj); - - WuQMacroFile& operator=(const WuQMacroFile& obj); +// WuQMacroFile(const WuQMacroFile& obj); +// +// WuQMacroFile& operator=(const WuQMacroFile& obj); virtual bool isEmpty() const override; @@ -72,7 +72,7 @@ namespace caret { // ADD_NEW_METHODS_HERE private: - void copyHelperWuQMacroFile(const WuQMacroFile& obj); +// void copyHelperWuQMacroFile(const WuQMacroFile& obj); void setMacroGroupName(const QString& filename); diff --git a/src/Common/WuQMacroGroup.cxx b/src/Common/WuQMacroGroup.cxx index 3b26dc475..a52729d1b 100644 --- a/src/Common/WuQMacroGroup.cxx +++ b/src/Common/WuQMacroGroup.cxx @@ -47,7 +47,7 @@ using namespace caret; * Name of group */ WuQMacroGroup::WuQMacroGroup(const QString& name) -: CaretObjectTracksModification(), +: QStandardItemModel(), TracksModificationInterface(), m_name(name) { m_uniqueIdentifier = QUuid::createUuid().toString(); @@ -58,78 +58,16 @@ m_name(name) */ WuQMacroGroup::~WuQMacroGroup() { - clear(); -} - -/** - * Copy constructor. - * @param obj - * Object that is copied. - */ -WuQMacroGroup::WuQMacroGroup(const WuQMacroGroup& obj) -: CaretObjectTracksModification(obj) -{ - m_uniqueIdentifier = QUuid::createUuid().toString(); - - this->copyHelperWuQMacroGroup(obj); -} - -/** - * Assignment operator. - * @param obj - * Data copied from obj to this. - * @return - * Reference to this object. - */ -WuQMacroGroup& -WuQMacroGroup::operator=(const WuQMacroGroup& obj) -{ - if (this != &obj) { - CaretObjectTracksModification::operator=(obj); - this->copyHelperWuQMacroGroup(obj); - } - return *this; -} - -/** - * Helps with copying an object of this type. - * @param obj - * Object that is copied. - */ -void -WuQMacroGroup::copyHelperWuQMacroGroup(const WuQMacroGroup& obj) -{ - /* Note: unique identifier is NOT copied */ - - clear(); - - m_name = obj.m_name; - - for (auto m : obj.m_macros) { - addMacro(new WuQMacro(*m)); - } + clearPrivate(); } /** * Clear (remove all macros) */ void -WuQMacroGroup::clear() +WuQMacroGroup::clearPrivate() { /* Note: Do not clear unique identifier */ - - bool hadMacrosFlag = ( ! m_macros.empty()); - for (auto m : m_macros) { - delete m; - } - m_macros.clear(); - - if (hadMacrosFlag) { - setModified(); - } - else { - clearModified(); - } } /** @@ -191,7 +129,7 @@ WuQMacroGroup::setUniqueIdentifier(const QString& uniqueIdentifier) void WuQMacroGroup::addMacro(WuQMacro* macro) { - m_macros.push_back(macro); + appendRow(macro); setModified(); } @@ -208,7 +146,7 @@ WuQMacroGroup::appendMacroGroup(const WuQMacroGroup* macroGroup) for (int32_t i = 0; i < numMacros; i++) { const WuQMacro* macro = macroGroup->getMacroAtIndex(i); CaretAssert(macro); - addMacro(new WuQMacro(*macro)); + appendRow(new WuQMacro(*macro)); } setModified(); } @@ -219,7 +157,7 @@ WuQMacroGroup::appendMacroGroup(const WuQMacroGroup* macroGroup) int32_t WuQMacroGroup::getNumberOfMacros() const { - return m_macros.size(); + return rowCount(); } /** @@ -233,11 +171,14 @@ WuQMacroGroup::getNumberOfMacros() const WuQMacro* WuQMacroGroup::getMacroByName(const QString& name) { - for (auto m : m_macros) { - if (m->getName() == name) { - return m; + const int32_t numItems = rowCount(); + for (int32_t i = 0; i < numItems; i++) { + WuQMacro* macro = getMacroAtIndex(i); + if (macro->getName() == name) { + return macro; } } + return NULL; } @@ -252,11 +193,14 @@ WuQMacroGroup::getMacroByName(const QString& name) const WuQMacro* WuQMacroGroup::getMacroByName(const QString& name) const { - for (auto m : m_macros) { - if (m->getName() == name) { - return m; + const int32_t numItems = rowCount(); + for (int32_t i = 0; i < numItems; i++) { + const WuQMacro* macro = getMacroAtIndex(i); + if (macro->getName() == name) { + return macro; } } + return NULL; } @@ -271,11 +215,14 @@ WuQMacroGroup::getMacroByName(const QString& name) const WuQMacro* WuQMacroGroup::getMacroWithUniqueIdentifier(const QString& uniqueIdentifier) { - for (auto m : m_macros) { - if (m->getUniqueIdentifier() == uniqueIdentifier) { - return m; + const int32_t numItems = rowCount(); + for (int32_t i = 0; i < numItems; i++) { + WuQMacro* macro = getMacroAtIndex(i); + if (macro->getUniqueIdentifier() == uniqueIdentifier) { + return macro; } } + return NULL; } @@ -290,11 +237,14 @@ WuQMacroGroup::getMacroWithUniqueIdentifier(const QString& uniqueIdentifier) const WuQMacro* WuQMacroGroup::getMacroWithUniqueIdentifier(const QString& uniqueIdentifier) const { - for (auto m : m_macros) { - if (m->getUniqueIdentifier() == uniqueIdentifier) { - return m; + const int32_t numItems = rowCount(); + for (int32_t i = 0; i < numItems; i++) { + const WuQMacro* macro = getMacroAtIndex(i); + if (macro->getUniqueIdentifier() == uniqueIdentifier) { + return macro; } } + return NULL; } @@ -309,11 +259,14 @@ WuQMacroGroup::getMacroWithUniqueIdentifier(const QString& uniqueIdentifier) con WuQMacro* WuQMacroGroup::getMacroWithShortCutKey(const WuQMacroShortCutKeyEnum::Enum shortCutKey) { - for (auto m : m_macros) { - if (m->getShortCutKey() == shortCutKey) { - return m; + const int32_t numItems = rowCount(); + for (int32_t i = 0; i < numItems; i++) { + WuQMacro* macro = getMacroAtIndex(i); + if (macro->getShortCutKey() == shortCutKey) { + return macro; } } + return NULL; } @@ -328,11 +281,14 @@ WuQMacroGroup::getMacroWithShortCutKey(const WuQMacroShortCutKeyEnum::Enum short const WuQMacro* WuQMacroGroup::getMacroWithShortCutKey(const WuQMacroShortCutKeyEnum::Enum shortCutKey) const { - for (auto m : m_macros) { - if (m->getShortCutKey() == shortCutKey) { - return m; + const int32_t numItems = rowCount(); + for (int32_t i = 0; i < numItems; i++) { + const WuQMacro* macro = getMacroAtIndex(i); + if (macro->getShortCutKey() == shortCutKey) { + return macro; } } + return NULL; } @@ -345,8 +301,13 @@ WuQMacroGroup::getMacroWithShortCutKey(const WuQMacroShortCutKeyEnum::Enum short WuQMacro* WuQMacroGroup::getMacroAtIndex(const int32_t index) { - CaretAssertVectorIndex(m_macros, index); - return m_macros[index]; + CaretAssert((index >= 0) + && (index < rowCount())); + QStandardItem* standardItem = item(index); + CaretAssert(standardItem); + WuQMacro* macro = dynamic_cast(standardItem); + CaretAssert(macro); + return macro; } /** @@ -358,8 +319,14 @@ WuQMacroGroup::getMacroAtIndex(const int32_t index) const WuQMacro* WuQMacroGroup::getMacroAtIndex(const int32_t index) const { - CaretAssertVectorIndex(m_macros, index); - return m_macros[index]; + CaretAssert((index >= 0) + && (index < rowCount())); + QStandardItem* standardItem = item(index); + CaretAssert(standardItem); + WuQMacro* macro = dynamic_cast(standardItem); + CaretAssert(macro); + return macro; + } /** @@ -408,10 +375,10 @@ WuQMacroGroup::deleteMacro(const WuQMacro* macro) void WuQMacroGroup::deleteMacroAtIndex(const int32_t index) { - CaretAssertVectorIndex(m_macros, index); - WuQMacro* macro = m_macros[index]; - m_macros.erase(m_macros.begin() + index); - delete macro; + CaretAssert((index >= 0) + && (index < rowCount())); + QStandardItem* item = takeItem(index); + delete item; setModified(); } @@ -422,16 +389,18 @@ WuQMacroGroup::deleteMacroAtIndex(const int32_t index) bool WuQMacroGroup::isModified() const { - if (CaretObjectTracksModification::isModified()) { + if (m_modifiedStatusFlag) { return true; } - for (const auto m : m_macros) { - if (m->isModified()) { - return true; + + const int32_t count = getNumberOfMacros(); + for (int32_t i = 0; i < count; i++) { + if (getMacroAtIndex(i)->isModified()) { + break; } } - + return false; } @@ -441,13 +410,24 @@ WuQMacroGroup::isModified() const void WuQMacroGroup::clearModified() { - CaretObjectTracksModification::clearModified(); + m_modifiedStatusFlag = false; - for (auto m : m_macros) { - m->clearModified(); + const int32_t count = getNumberOfMacros(); + for (int32_t i = 0; i < count; i++) { + getMacroAtIndex(i)->clearModified(); } } +/** + * Set the modification status to modified + */ +void +WuQMacroGroup::setModified() +{ + m_modifiedStatusFlag = true; +} + + /** * Read from a string containing XML. If successful, * the modified status is cleared. @@ -520,8 +500,9 @@ AString WuQMacroGroup::toString() const { QString s("WuQMacroGroup name=" + m_name + "\n"); - for (auto m : m_macros) { - s.append(m->toString() + "\n"); + const int32_t count = getNumberOfMacros(); + for (int32_t i = 0; i < count; i++) { + s.append(getMacroAtIndex(i)->toString() + "\n"); } return s; } diff --git a/src/Common/WuQMacroGroup.h b/src/Common/WuQMacroGroup.h index 358a2f3d4..4a55692ac 100644 --- a/src/Common/WuQMacroGroup.h +++ b/src/Common/WuQMacroGroup.h @@ -19,32 +19,28 @@ * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ -/*LICENSE_END*/ - +/*LICENSE_END*/ #include +#include + #include "CaretObjectTracksModification.h" +#include "TracksModificationInterface.h" #include "WuQMacroShortCutKeyEnum.h" namespace caret { class WuQMacro; - class WuQMacroGroup : public CaretObjectTracksModification { + class WuQMacroGroup : public QStandardItemModel, public TracksModificationInterface { public: WuQMacroGroup(const QString& groupName); virtual ~WuQMacroGroup(); - WuQMacroGroup(const WuQMacroGroup& obj); - - WuQMacroGroup& operator=(const WuQMacroGroup& obj); - - void clear(); - QString getName() const; void setName(const QString& name); @@ -85,6 +81,8 @@ namespace caret { virtual void clearModified() override; + virtual void setModified() override; + bool readXmlFromString(const QString& xmlString, QString& errorMessageOut, QString& nonFatalWarningMessageOut); @@ -97,13 +95,13 @@ namespace caret { virtual AString toString() const; private: - void copyHelperWuQMacroGroup(const WuQMacroGroup& obj); + void clearPrivate(); QString m_uniqueIdentifier; QString m_name; - std::vector m_macros; + bool m_modifiedStatusFlag = false; // ADD_NEW_MEMBERS_HERE diff --git a/src/Common/WuQMacroStandardItemTypes.cxx b/src/Common/WuQMacroStandardItemTypes.cxx new file mode 100644 index 000000000..cbe72673a --- /dev/null +++ b/src/Common/WuQMacroStandardItemTypes.cxx @@ -0,0 +1,71 @@ + +/*LICENSE_START*/ +/* + * Copyright (C) 2019 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + +#define __WU_Q_MACRO_STANDARD_ITEM_TYPES_DECLARE__ +#include "WuQMacroStandardItemTypes.h" +#undef __WU_Q_MACRO_STANDARD_ITEM_TYPES_DECLARE__ + +#include + +#include "CaretAssert.h" +using namespace caret; + + + +/** + * \class caret::WuQMacroStandardItemTypes + * \brief Standard Items Types for Macro Classes that subclass QStandardItem + * \ingroup Common + */ + +/** + * Constructor. + */ +WuQMacroStandardItemTypes::WuQMacroStandardItemTypes() +{ +} + +/** + * Destructor. + */ +WuQMacroStandardItemTypes::~WuQMacroStandardItemTypes() +{ +} + +/** + * @return Standard item type for WuQMacro + */ +int +WuQMacroStandardItemTypes::typeWuQMacro() +{ + return (QStandardItem::UserType + 1); +} + +/** + * @return Standard item type for WuQMacroCommand + */ +int +WuQMacroStandardItemTypes::typeWuQMacroCommand() +{ + return (QStandardItem::UserType + 2); +} + + diff --git a/src/Common/WuQMacroStandardItemTypes.h b/src/Common/WuQMacroStandardItemTypes.h new file mode 100644 index 000000000..459b5373f --- /dev/null +++ b/src/Common/WuQMacroStandardItemTypes.h @@ -0,0 +1,63 @@ +#ifndef __WU_Q_MACRO_STANDARD_ITEM_TYPES_H__ +#define __WU_Q_MACRO_STANDARD_ITEM_TYPES_H__ + +/*LICENSE_START*/ +/* + * Copyright (C) 2019 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + + + +#include + + + + + +namespace caret { + + class WuQMacroStandardItemTypes { + + public: + static int typeWuQMacro(); + + static int typeWuQMacroCommand(); + + private: + WuQMacroStandardItemTypes(); + + virtual ~WuQMacroStandardItemTypes(); + + WuQMacroStandardItemTypes(const WuQMacroStandardItemTypes&) = delete; + + WuQMacroStandardItemTypes& operator=(const WuQMacroStandardItemTypes&) = delete; + + + // ADD_NEW_METHODS_HERE + + private: + // ADD_NEW_MEMBERS_HERE + + }; + +#ifdef __WU_Q_MACRO_STANDARD_ITEM_TYPES_DECLARE__ + // +#endif // __WU_Q_MACRO_STANDARD_ITEM_TYPES_DECLARE__ + +} // namespace +#endif //__WU_Q_MACRO_STANDARD_ITEM_TYPES_H__ diff --git a/src/GuiQt/CMakeLists.txt b/src/GuiQt/CMakeLists.txt index 7e727525f..f09b52106 100644 --- a/src/GuiQt/CMakeLists.txt +++ b/src/GuiQt/CMakeLists.txt @@ -110,7 +110,7 @@ ELSE() CiftiParcelSelectionComboBox.h ClippingPlanesDialog.h ColorEditorWidget.h - CopyPaletteColorMappingToFilesDialog.h + CopyPaletteColorMappingToFilesDialog.h CustomViewDialog.h DataFileContentCopyMoveDialog.h DisplayGroupAndTabItemViewController.h @@ -140,7 +140,7 @@ ELSE() MacApplication.h MacDockMenu.h MapSettingsChartTwoLineHistoryWidget.h - MapSettingsColorBarPaletteOptionsWidget.h + MapSettingsColorBarPaletteOptionsWidget.h MapSettingsColorBarWidget.h MapSettingsFiberTrajectoryWidget.h MapSettingsLabelsWidget.h @@ -151,7 +151,7 @@ ELSE() MetaDataEditorDialog.h MetaDataEditorWidget.h MovieDialog.h - OffScreenOpenGLRenderer.h + OffScreenOpenGLRenderer.h OverlaySetViewController.h OverlaySettingsEditorDialog.h OverlayViewController.h @@ -166,8 +166,8 @@ ELSE() SceneBasePathWidget.h SceneCreateReplaceDialog.h SceneDialog.h - SceneFileInformationDialog.h - SceneDataFileTreeItemModel.h + SceneFileInformationDialog.h + SceneDataFileTreeItemModel.h ScenePreviewDialog.h SceneReplaceAllDialog.h SceneShowOptionsDialog.h @@ -177,7 +177,7 @@ ELSE() StructureSurfaceSelectionControl.h SurfacePropertiesEditorDialog.h SurfaceSelectionViewController.h - ThresholdingSetMapsDialog.h + ThresholdingSetMapsDialog.h TileTabsConfigurationDialog.h UserInputModeAnnotationsContextMenu.h UserInputModeAnnotationsWidget.h @@ -212,7 +212,7 @@ ELSE() WuQMacroHelperInterface.h WuQMacroManager.h WuQMacroMenu.h - WuQMacroShortCutKeyComboBox.h + WuQMacroShortCutKeyComboBox.h WuQMacroSignalEmitter.h WuQMacroSignalWatcher.h WuQMessageBox.h @@ -220,10 +220,10 @@ ELSE() WuQSpecialIncrementDoubleSpinBox.h WuQSpinBoxGroup.h WuQSpinBoxOddValue.h - WuQTabBar.h + WuQTabBar.h WuQTabWidget.h - WuQTabWidgetWithSizeHint.h - WuQTextEditorDialog.h + WuQTabWidgetWithSizeHint.h + WuQTextEditorDialog.h WuQTimedMessageDisplay.h WuQTreeWidget.h WuQTrueFalseComboBox.h diff --git a/src/GuiQt/WuQMacroDialog.cxx b/src/GuiQt/WuQMacroDialog.cxx index 4c251bb98..4e9dd2018 100644 --- a/src/GuiQt/WuQMacroDialog.cxx +++ b/src/GuiQt/WuQMacroDialog.cxx @@ -29,18 +29,21 @@ #include #include #include -#include #include #include #include +#include +#include #include #include "CaretAssert.h" #include "CaretLogger.h" #include "WuQMacro.h" +#include "WuQMacroCommand.h" #include "WuQMacroGroup.h" #include "WuQMacroExecutor.h" #include "WuQMacroManager.h" +#include "WuQMacroStandardItemTypes.h" using namespace caret; @@ -67,28 +70,17 @@ WuQMacroDialog::WuQMacroDialog(QWidget* parent) m_macroGroups = WuQMacroManager::instance()->getMacroGroups(); QLabel* macrosLabel = new QLabel("Macro:"); - QLabel* descriptionLabel = new QLabel("Macro Description:"); QLabel* macroGroupLabel = new QLabel("Macros in:"); m_macroGroupComboBox = new QComboBox(); QObject::connect(m_macroGroupComboBox, static_cast(&QComboBox::activated), - this, &WuQMacroDialog::macroGroupBoxActivated); - - m_macrosListWidget = new QListWidget(); - QObject::connect(m_macrosListWidget, &QListWidget::currentRowChanged, - this, &WuQMacroDialog::macrosListWidgetCurrentRowChanged); + this, &WuQMacroDialog::macroGroupComboBoxActivated); - m_macroDescriptionLabel = new QLabel(""); - m_macroDescriptionLabel->setWordWrap(true); - m_macroDescriptionLabel->setMinimumHeight(40); - m_macroDescriptionLabel->setAlignment(Qt::AlignTop - | Qt::AlignLeft); + m_treeView = new QTreeView(); + m_treeView->setHeaderHidden(true); + QObject::connect(m_treeView, &QTreeView::clicked, + this, &WuQMacroDialog::treeViewItemClicked); - QFrame* horizontalLine = new QFrame(); - horizontalLine->setMidLineWidth(1); - horizontalLine->setLineWidth(1); - horizontalLine->setFrameStyle(QFrame::HLine | QFrame::Sunken); - QGridLayout* gridLayout = new QGridLayout(); int row = 0; gridLayout->addWidget(macroGroupLabel, row, 0); @@ -96,7 +88,7 @@ WuQMacroDialog::WuQMacroDialog(QWidget* parent) row++; gridLayout->addWidget(macrosLabel, row, 0, (Qt::AlignTop | Qt::AlignLeft)); - gridLayout->addWidget(m_macrosListWidget, row, 1, 2, 1); + gridLayout->addWidget(m_treeView, row, 1, 2, 1); row++; gridLayout->addWidget(createMacroButtonsWidget(), row, 0); row++; @@ -105,6 +97,13 @@ WuQMacroDialog::WuQMacroDialog(QWidget* parent) gridLayout->setRowStretch(iRow, 0); } + m_macroWidget = createMacroWidget(); + m_commandWidget = createCommandWidget(); + m_stackedWidget = new QStackedWidget(); + m_stackedWidget->addWidget(m_macroWidget); + m_stackedWidget->addWidget(m_commandWidget); + m_stackedWidget->setCurrentWidget(m_macroWidget); + m_dialogButtonBox = new QDialogButtonBox(QDialogButtonBox::Close); m_runButton = m_dialogButtonBox->addButton("Run", QDialogButtonBox::ApplyRole); QObject::connect(m_dialogButtonBox, &QDialogButtonBox::rejected, @@ -114,10 +113,7 @@ WuQMacroDialog::WuQMacroDialog(QWidget* parent) QVBoxLayout* dialogLayout = new QVBoxLayout(this); dialogLayout->addLayout(gridLayout); - dialogLayout->addWidget(descriptionLabel); - dialogLayout->addWidget(m_macroDescriptionLabel); - dialogLayout->addWidget(horizontalLine); - dialogLayout->addWidget(createRunOptionsWidget()); + dialogLayout->addWidget(m_stackedWidget); dialogLayout->addWidget(m_dialogButtonBox); updateDialogContents(); @@ -181,6 +177,35 @@ WuQMacroDialog::createMacroButtonsWidget() return widget; } +/** + * @return The widget displayed when a macro is selected + */ +QWidget* +WuQMacroDialog::createMacroWidget() +{ + QLabel* descriptionLabel = new QLabel("Macro Description:"); + m_macroDescriptionLabel = new QLabel(""); + m_macroDescriptionLabel->setWordWrap(true); + m_macroDescriptionLabel->setMinimumHeight(40); + m_macroDescriptionLabel->setAlignment(Qt::AlignTop + | Qt::AlignLeft); + + QFrame* horizontalLine = new QFrame(); + horizontalLine->setMidLineWidth(1); + horizontalLine->setLineWidth(1); + horizontalLine->setFrameStyle(QFrame::HLine | QFrame::Sunken); + + QWidget* widget = new QWidget(); + QVBoxLayout* dialogLayout = new QVBoxLayout(widget); + dialogLayout->addWidget(descriptionLabel); + dialogLayout->addWidget(m_macroDescriptionLabel); + dialogLayout->addWidget(horizontalLine); + dialogLayout->addWidget(createRunOptionsWidget()); + + return widget; +} + + /** * @return New instance of widget containing macro run options */ @@ -282,6 +307,24 @@ WuQMacroDialog::buttonBoxButtonClicked(QAbstractButton* button) } } +/** + * @return The widget displayed when a commnand is selected + */ +QWidget* +WuQMacroDialog::createCommandWidget() +{ + QLabel* titleLabel = new QLabel("Title"); + m_commandTitleLabel = new QLabel(); + + QWidget* widget = new QWidget(); + QGridLayout* layout = new QGridLayout(widget); + int row = 0; + layout->addWidget(titleLabel, row, 0); + layout->addWidget(m_commandTitleLabel, row, 1); + + return widget; +} + /** * Update content of the dialog */ @@ -320,69 +363,96 @@ WuQMacroDialog::updateDialogContents() m_runOptionMoveMouseCheckBox->setChecked(runOptions->isShowMouseMovement()); m_runOptionLoopCheckBox->setChecked(runOptions->isLooping()); - macroGroupBoxActivated(selectedIndex); + macroGroupComboBoxActivated(selectedIndex); } /** - * Called when macro group combo box selection is made + * Called when an item in the tree view is clicked */ void -WuQMacroDialog::macroGroupBoxActivated(int) +WuQMacroDialog::treeViewItemClicked(const QModelIndex& modelIndex) { - const QString emptySuffix(" (Empty)"); - QListWidgetItem* selectedItem = m_macrosListWidget->currentItem(); - QString selectedUniqueIdentifier; - if (selectedItem != NULL) { - selectedUniqueIdentifier = selectedItem->data(Qt::UserRole).toString(); + + QStandardItemModel* selectedModel = NULL; + if (modelIndex.isValid()) { + const QAbstractItemModel* abstractModel = modelIndex.model(); + if (abstractModel != NULL) { + const QStandardItemModel* constModel = qobject_cast(abstractModel); + if (constModel != NULL) { + selectedModel = const_cast(constModel); + } + } } - m_macrosListWidget->clear(); + WuQMacro* macro(NULL); + WuQMacroCommand* macroCommand(NULL); + + if (selectedModel != NULL) { + QStandardItem* selectedItem = selectedModel->itemFromIndex(modelIndex); + if (selectedItem->type() == WuQMacroStandardItemTypes::typeWuQMacro()) { + macro = dynamic_cast(selectedItem); + CaretAssert(macro); + m_stackedWidget->setCurrentWidget(m_macroWidget); + } + else if (selectedItem->type() == WuQMacroStandardItemTypes::typeWuQMacroCommand()) { + macroCommand = dynamic_cast(selectedItem); + CaretAssert(macroCommand); + m_stackedWidget->setCurrentWidget(m_commandWidget); + } + } + + updateMacroWidget(macro); + updateCommandWidget(macroCommand); +} - int32_t selectedIndex = 0; +/** + * Called when macro group combo box selection is made + */ +void +WuQMacroDialog::macroGroupComboBoxActivated(int) +{ WuQMacroGroup* selectedGroup = getSelectedMacroGroup(); + if (selectedGroup != NULL) { - const int32_t numMacros = selectedGroup->getNumberOfMacros(); - for (int32_t i = 0; i < numMacros; i++) { - const WuQMacro* macro = selectedGroup->getMacroAtIndex(i); - if (macro->getUniqueIdentifier() == selectedUniqueIdentifier) { - selectedIndex = i; - } - - QString macroName = macro->getName(); - if (selectedGroup->getMacroAtIndex(i)->getNumberOfMacroCommands() <= 0) { - macroName.append(emptySuffix); - } - - QListWidgetItem* item = new QListWidgetItem(macroName); - item->setData(Qt::UserRole, - macro->getUniqueIdentifier()); - m_macrosListWidget->addItem(item); - } - - if ((selectedIndex >= 0) - && (selectedIndex < numMacros)) { - m_macrosListWidget->setCurrentRow(selectedIndex); - } + m_treeView->setModel(selectedGroup); + treeViewItemClicked(m_treeView->currentIndex()); + } + else { + m_treeView->setModel(new QStandardItemModel()); } +} - macrosListWidgetCurrentRowChanged(selectedIndex); +/** + * Update the macro widget with the given macro + * + * @param macro + * The macro (may be NULL) + */ +void +WuQMacroDialog::updateMacroWidget(WuQMacro* macro) +{ + QString text; + if (macro != NULL) { + text = macro->getDescription(); + } + + m_macroDescriptionLabel->setText(text); } /** - * Called when item is selected in the macros list widget + * Update the command widget with the given macommandcro + * + * @param command + * The command (may be NULL) */ void -WuQMacroDialog::macrosListWidgetCurrentRowChanged(int rowIndex) +WuQMacroDialog::updateCommandWidget(WuQMacroCommand* command) { - m_macroDescriptionLabel->clear(); - - WuQMacroGroup* selectedGroup = getSelectedMacroGroup(); - if ((rowIndex >= 0) - && (rowIndex < selectedGroup->getNumberOfMacros())) { - WuQMacro* macro = selectedGroup->getMacroAtIndex(rowIndex); - CaretAssert(macro); - m_macroDescriptionLabel->setText(macro->getDescription()); + QString text; + if (command != NULL) { + text = command->text(); } + m_commandTitleLabel->setText(text); } /** @@ -407,16 +477,41 @@ WuQMacroDialog::getSelectedMacroGroup() WuQMacro* WuQMacroDialog::getSelectedMacro() { - WuQMacroGroup* selectedGroup = getSelectedMacroGroup(); - if (selectedGroup != NULL) { - const int32_t selectedMacroIndex = m_macrosListWidget->currentRow(); - if ((selectedMacroIndex >= 0) - && (selectedMacroIndex < selectedGroup->getNumberOfMacros())) { - WuQMacro* macro = selectedGroup->getMacroAtIndex(selectedMacroIndex); - return macro; + WuQMacro* macro(NULL); + + QModelIndex modelIndex = m_treeView->currentIndex(); + if (modelIndex.isValid()) { + QStandardItemModel* selectedModel = NULL; + if (modelIndex.isValid()) { + const QAbstractItemModel* abstractModel = modelIndex.model(); + if (abstractModel != NULL) { + const QStandardItemModel* constModel = qobject_cast(abstractModel); + if (constModel != NULL) { + selectedModel = const_cast(constModel); + } + } + } + + + if (selectedModel != NULL) { + QStandardItem* selectedItem = selectedModel->itemFromIndex(modelIndex); + + if (selectedItem->type() == WuQMacroStandardItemTypes::typeWuQMacroCommand()) { + /* + * Parent should be WuQMacro + */ + selectedItem = selectedItem->parent(); + CaretAssert(selectedItem); + } + + if (selectedItem->type() == WuQMacroStandardItemTypes::typeWuQMacro()) { + macro = dynamic_cast(selectedItem); + CaretAssert(macro); + } } } - return NULL; + + return macro; } /** diff --git a/src/GuiQt/WuQMacroDialog.h b/src/GuiQt/WuQMacroDialog.h index 9c204745b..d0acfe671 100644 --- a/src/GuiQt/WuQMacroDialog.h +++ b/src/GuiQt/WuQMacroDialog.h @@ -33,11 +33,13 @@ class QComboBox; class QDialogButtonBox; class QDoubleSpinBox; class QLabel; -class QListWidget; +class QStackedWidget; +class QTreeView; namespace caret { class WuQMacro; + class WuQMacroCommand; class WuQMacroGroup; class WuQMacroDialog : public QDialog { @@ -58,9 +60,9 @@ namespace caret { // ADD_NEW_METHODS_HERE private slots: - void macroGroupBoxActivated(int); + void treeViewItemClicked(const QModelIndex& modelIndex); - void macrosListWidgetCurrentRowChanged(int); + void macroGroupComboBoxActivated(int); void buttonBoxButtonClicked(QAbstractButton* button); @@ -85,6 +87,14 @@ namespace caret { QWidget* createRunOptionsWidget(); + QWidget* createMacroWidget(); + + QWidget* createCommandWidget(); + + void updateMacroWidget(WuQMacro* macro); + + void updateCommandWidget(WuQMacroCommand* command); + void runSelectedMacro(); WuQMacroGroup* getSelectedMacroGroup(); @@ -95,10 +105,16 @@ namespace caret { QComboBox* m_macroGroupComboBox; - QListWidget* m_macrosListWidget; + QTreeView* m_treeView; QLabel* m_macroDescriptionLabel; + QWidget* m_macroWidget; + + QWidget* m_commandWidget; + + QStackedWidget* m_stackedWidget; + QDialogButtonBox* m_dialogButtonBox; QAbstractButton* m_runButton; @@ -119,6 +135,8 @@ namespace caret { QDoubleSpinBox* m_runOptionDelayBetweenCommandsSpinBox; + QLabel* m_commandTitleLabel; + // ADD_NEW_MEMBERS_HERE }; diff --git a/src/Scenes/CMakeLists.txt b/src/Scenes/CMakeLists.txt index ae9c80404..e4e0235fa 100644 --- a/src/Scenes/CMakeLists.txt +++ b/src/Scenes/CMakeLists.txt @@ -20,6 +20,7 @@ PROJECT (Scenes) SET(CARET_QT_LINK_MODULES "") if(Qt5_FOUND) include_directories(${Qt5Core_INCLUDE_DIRS}) + include_directories(${Qt5Gui_INCLUDE_DIRS}) #include_directories(${Qt5Network_INCLUDE_DIRS}) include_directories(${Qt5Xml_INCLUDE_DIRS}) SET(CARET_QT_LINK_MODULES "Qt5::Core") -- GitLab From ea81453964af32e9f779921067ad600c860a87f4 Mon Sep 17 00:00:00 2001 From: John Harwell Date: Mon, 7 Jan 2019 15:40:55 -0600 Subject: [PATCH 162/427] WB-834: Continuing revision of the Macro Dialog and also of the Macro XML format for macro storage. --- src/Common/CMakeLists.txt | 4 +- src/Common/CaretPreferences.cxx | 2 +- src/Common/WuQMacro.cxx | 6 +- src/Common/WuQMacroCommand.cxx | 77 +++- src/Common/WuQMacroCommand.h | 14 +- src/Common/WuQMacroGroup.cxx | 8 +- src/Common/WuQMacroGroup.h | 2 +- src/Common/WuQMacroGroupXmlFormatBase.h | 4 +- src/Common/WuQMacroGroupXmlReader.cxx | 191 +++++++++- src/Common/WuQMacroGroupXmlReader.h | 2 + src/Common/WuQMacroGroupXmlWriter.cxx | 5 + src/Common/WuQMacroStandardItemTypeEnum.cxx | 387 ++++++++++++++++++++ src/Common/WuQMacroStandardItemTypeEnum.h | 101 +++++ src/Common/WuQMacroStandardItemTypes.cxx | 71 ---- src/Common/WuQMacroStandardItemTypes.h | 63 ---- src/Files/SceneFileSaxReader.cxx | 1 - src/GuiQt/CMakeLists.txt | 3 - src/GuiQt/WuQMacroDialog.cxx | 103 +++--- src/GuiQt/WuQMacroDialog.h | 6 +- src/GuiQt/WuQMacroEditorDialog.cxx | 276 -------------- src/GuiQt/WuQMacroEditorDialog.h | 100 ----- src/GuiQt/WuQMacroManager.cxx | 32 -- src/GuiQt/WuQMacroManager.h | 3 - src/GuiQt/WuQMacroSignalWatcher.cxx | 1 + 24 files changed, 832 insertions(+), 630 deletions(-) create mode 100644 src/Common/WuQMacroStandardItemTypeEnum.cxx create mode 100644 src/Common/WuQMacroStandardItemTypeEnum.h delete mode 100644 src/Common/WuQMacroStandardItemTypes.cxx delete mode 100644 src/Common/WuQMacroStandardItemTypes.h delete mode 100644 src/GuiQt/WuQMacroEditorDialog.cxx delete mode 100644 src/GuiQt/WuQMacroEditorDialog.h diff --git a/src/Common/CMakeLists.txt b/src/Common/CMakeLists.txt index c2fa2f61a..a1e3ce675 100644 --- a/src/Common/CMakeLists.txt +++ b/src/Common/CMakeLists.txt @@ -174,7 +174,7 @@ WuQMacroModeEnum.h WuQMacroMouseEventInfo.h WuQMacroMouseEventTypeEnum.h WuQMacroShortCutKeyEnum.h -WuQMacroStandardItemTypes.h +WuQMacroStandardItemTypeEnum.h YokingGroupEnum.h ${MOC_SOURCE_FILES} @@ -289,7 +289,7 @@ WuQMacroModeEnum.cxx WuQMacroMouseEventInfo.cxx WuQMacroMouseEventTypeEnum.cxx WuQMacroShortCutKeyEnum.cxx -WuQMacroStandardItemTypes.cxx +WuQMacroStandardItemTypeEnum.cxx YokingGroupEnum.cxx ) diff --git a/src/Common/CaretPreferences.cxx b/src/Common/CaretPreferences.cxx index 0ef58000f..1a834b48c 100644 --- a/src/Common/CaretPreferences.cxx +++ b/src/Common/CaretPreferences.cxx @@ -357,7 +357,7 @@ CaretPreferences::readMacros(const bool performSync) else { QString errorMessage; QString warningMessage; - if ( ! m_macros->readXmlFromString(macrosXmlString, + if ( ! m_macros->readXmlFromStringOld(macrosXmlString, errorMessage, warningMessage)) { CaretLogSevere("Reading macros from preferences: " diff --git a/src/Common/WuQMacro.cxx b/src/Common/WuQMacro.cxx index a21ed74a4..0e5675d2b 100644 --- a/src/Common/WuQMacro.cxx +++ b/src/Common/WuQMacro.cxx @@ -27,7 +27,7 @@ #include "CaretAssert.h" #include "WuQMacroCommand.h" -#include "WuQMacroStandardItemTypes.h" +#include "WuQMacroStandardItemTypeEnum.h" using namespace caret; @@ -141,9 +141,9 @@ WuQMacro::type() const { /* * This must be different than the type returned by of macro - subclasses of QStandardItem + * subclasses of QStandardItem */ - return WuQMacroStandardItemTypes::typeWuQMacro(); + return WuQMacroStandardItemTypeEnum::toIntegerCode(WuQMacroStandardItemTypeEnum::MACRO); } void diff --git a/src/Common/WuQMacroCommand.cxx b/src/Common/WuQMacroCommand.cxx index 1fe5f1627..915814523 100644 --- a/src/Common/WuQMacroCommand.cxx +++ b/src/Common/WuQMacroCommand.cxx @@ -25,7 +25,7 @@ #include "CaretAssert.h" #include "WuQMacroMouseEventInfo.h" -#include "WuQMacroStandardItemTypes.h" +#include "WuQMacroStandardItemTypeEnum.h" using namespace caret; @@ -35,6 +35,24 @@ using namespace caret; * \ingroup WuQMacro */ +/** + * Constructor for a macro command, usually a mouse command + * + * @param classType + * Type of object class + * @param objectName + * Name of object + */ +WuQMacroCommand::WuQMacroCommand(const WuQMacroClassTypeEnum::Enum classType, + const QString& objectName) +: WuQMacroCommand(classType, + objectName, + "", + "", + "") +{ +} + /** * Constructor for a macro command * @@ -42,6 +60,8 @@ using namespace caret; * Type of object class * @param objectName * Name of object + * @param objectToolTip + * ToolTip for object * @param dataValue * Data value for the command. * @param dataValueTwo @@ -49,6 +69,7 @@ using namespace caret; */ WuQMacroCommand::WuQMacroCommand(const WuQMacroClassTypeEnum::Enum classType, const QString& objectName, + const QString& objectToolTip, const QVariant dataValue, const QVariant dataValueTwo) : QStandardItem(), @@ -61,6 +82,7 @@ m_macroMouseEvent(NULL) { setFlags(Qt::ItemIsEnabled | Qt::ItemIsSelectable); + setToolTip(objectToolTip); /* * Second data value is used by only some commands @@ -140,8 +162,7 @@ m_macroMouseEvent(NULL) break; case WuQMacroClassTypeEnum::MOUSE_USER_EVENT: m_dataType = WuQMacroDataValueTypeEnum::MOUSE; - title = ""; - CaretAssertMessage(0, "Must use constructor for mouse event"); + title = "MouseEvent"; break; case WuQMacroClassTypeEnum::PUSH_BUTTON: m_dataType = WuQMacroDataValueTypeEnum::BOOLEAN; @@ -270,9 +291,9 @@ WuQMacroCommand::type() const { /* * This must be different than the type returned by of macro - subclasses of QStandardItem + * subclasses of QStandardItem */ - return WuQMacroStandardItemTypes::typeWuQMacroCommand(); + return WuQMacroStandardItemTypeEnum::toIntegerCode(WuQMacroStandardItemTypeEnum::MACRO_COMMAND); } /** @@ -299,8 +320,9 @@ WuQMacroCommand::copyHelperWuQMacroCommand(const WuQMacroCommand& obj) case WuQMacroDataValueTypeEnum::INTEGER: break; case WuQMacroDataValueTypeEnum::MOUSE: - CaretAssert(obj.m_macroMouseEvent); - m_macroMouseEvent = new WuQMacroMouseEventInfo(*obj.m_macroMouseEvent); + if (obj.m_macroMouseEvent != NULL) { + m_macroMouseEvent = new WuQMacroMouseEventInfo(*obj.m_macroMouseEvent); + } break; case WuQMacroDataValueTypeEnum::STRING: break; @@ -344,6 +366,30 @@ WuQMacroCommand::getObjectName() const return m_objectName; } +/** + * @return ToolTip for the object + */ +QString +WuQMacroCommand::getObjectToolTip() const +{ + return toolTip(); +} + +/** + * Set the object's tooltip + * + * @param objectToolTip + * New value for object tooltip + */ +void +WuQMacroCommand::setObjectToolTip(const QString& objectToolTip) +{ + if (toolTip() != objectToolTip) { + setToolTip(objectToolTip); + setModified(); + } +} + /** * @return The object's data value. */ @@ -371,6 +417,23 @@ WuQMacroCommand::getMouseEventInfo() const return m_macroMouseEvent; } +/** + * Set the mouse event info + * + * @param mouseEventInfo + * The new mouse event info + */ +void +WuQMacroCommand::setMouseEventInfo(WuQMacroMouseEventInfo* mouseEventInfo) +{ + if (m_macroMouseEvent != NULL) { + delete m_macroMouseEvent; + m_macroMouseEvent = NULL; + } + + m_macroMouseEvent = mouseEventInfo; +} + /** * @return True if this instance is modified */ diff --git a/src/Common/WuQMacroCommand.h b/src/Common/WuQMacroCommand.h index 240c072d3..7020b87b5 100644 --- a/src/Common/WuQMacroCommand.h +++ b/src/Common/WuQMacroCommand.h @@ -43,11 +43,15 @@ namespace caret { public: WuQMacroCommand(const WuQMacroClassTypeEnum::Enum objectClassType, - const QString& objectClassName, + const QString& objectName, + const QString& objectToolTip, const QVariant dataValue, const QVariant dataValueTwo); - WuQMacroCommand(const QString& objectClassName, + WuQMacroCommand(const WuQMacroClassTypeEnum::Enum objectClassType, + const QString& objectName); + + WuQMacroCommand(const QString& objectName, WuQMacroMouseEventInfo* mouseEventInfo); virtual ~WuQMacroCommand(); @@ -60,6 +64,10 @@ namespace caret { QString getObjectName() const; + QString getObjectToolTip() const; + + void setObjectToolTip(const QString& objectToolTip); + WuQMacroDataValueTypeEnum::Enum getDataType() const; QVariant getDataValue() const; @@ -70,6 +78,8 @@ namespace caret { const WuQMacroMouseEventInfo* getMouseEventInfo() const; + void setMouseEventInfo(WuQMacroMouseEventInfo* mouseEventInfo); + // ADD_NEW_METHODS_HERE virtual bool isModified() const override; diff --git a/src/Common/WuQMacroGroup.cxx b/src/Common/WuQMacroGroup.cxx index a52729d1b..0bf524ad7 100644 --- a/src/Common/WuQMacroGroup.cxx +++ b/src/Common/WuQMacroGroup.cxx @@ -375,9 +375,15 @@ WuQMacroGroup::deleteMacro(const WuQMacro* macro) void WuQMacroGroup::deleteMacroAtIndex(const int32_t index) { + /* + * Note that 'takeItem' removes the item and the + * row becomes NULL so also need to remove the row. + */ CaretAssert((index >= 0) && (index < rowCount())); QStandardItem* item = takeItem(index); + CaretAssert(item); + removeRow(index); delete item; setModified(); @@ -442,7 +448,7 @@ WuQMacroGroup::setModified() * True if successful, else false */ bool -WuQMacroGroup::readXmlFromString(const QString& xmlString, +WuQMacroGroup::readXmlFromStringOld(const QString& xmlString, QString& errorMessageOut, QString& nonFatalWarningMessageOut) { diff --git a/src/Common/WuQMacroGroup.h b/src/Common/WuQMacroGroup.h index 4a55692ac..4951568bd 100644 --- a/src/Common/WuQMacroGroup.h +++ b/src/Common/WuQMacroGroup.h @@ -83,7 +83,7 @@ namespace caret { virtual void setModified() override; - bool readXmlFromString(const QString& xmlString, + bool readXmlFromStringOld(const QString& xmlString, QString& errorMessageOut, QString& nonFatalWarningMessageOut); diff --git a/src/Common/WuQMacroGroupXmlFormatBase.h b/src/Common/WuQMacroGroupXmlFormatBase.h index c9b3e4696..147020e9d 100644 --- a/src/Common/WuQMacroGroupXmlFormatBase.h +++ b/src/Common/WuQMacroGroupXmlFormatBase.h @@ -80,6 +80,7 @@ namespace caret { static const QString ELEMENT_MACRO_COMMAND; static const QString ELEMENT_MACRO_GROUP; static const QString ELEMENT_MOUSE_EVENT_INFO; + static const QString ELEMENT_TOOL_TIP; static const QString VALUE_BOOL_FALSE; static const QString VALUE_BOOL_TRUE; static const QString VALUE_VERSION_ONE; @@ -118,7 +119,8 @@ namespace caret { const QString WuQMacroGroupXmlFormatBase::ELEMENT_MACRO_COMMAND = "MacroCommand"; const QString WuQMacroGroupXmlFormatBase::ELEMENT_MACRO_GROUP = "MacroGroup"; const QString WuQMacroGroupXmlFormatBase::ELEMENT_MOUSE_EVENT_INFO = "MouseEventInfo"; - + const QString WuQMacroGroupXmlFormatBase::ELEMENT_TOOL_TIP = "ToolTip"; + const QString WuQMacroGroupXmlFormatBase::VALUE_BOOL_FALSE = "false"; const QString WuQMacroGroupXmlFormatBase::VALUE_BOOL_TRUE = "true"; const QString WuQMacroGroupXmlFormatBase::VALUE_VERSION_ONE = "1"; diff --git a/src/Common/WuQMacroGroupXmlReader.cxx b/src/Common/WuQMacroGroupXmlReader.cxx index 2b6db7709..6d4c4b3d6 100644 --- a/src/Common/WuQMacroGroupXmlReader.cxx +++ b/src/Common/WuQMacroGroupXmlReader.cxx @@ -146,6 +146,7 @@ WuQMacroGroupXmlReader::readVersionOne(WuQMacroGroup* macroGroup) CaretAssert(macroGroup); WuQMacro* macro(NULL); + WuQMacroCommand* macroCommand(NULL); while ( ! m_xmlStreamReader->atEnd()) { m_xmlStreamReader->readNext(); @@ -157,6 +158,7 @@ WuQMacroGroupXmlReader::readVersionOne(WuQMacroGroup* macroGroup) if (macro != NULL) { macroGroup->addMacro(macro); } + macroCommand = NULL; } else if (elementName == ELEMENT_DESCRIPTION) { const QString text = m_xmlStreamReader->readElementText(); @@ -165,7 +167,7 @@ WuQMacroGroupXmlReader::readVersionOne(WuQMacroGroup* macroGroup) } } else if (elementName == ELEMENT_MACRO_COMMAND) { - WuQMacroCommand* macroCommand = readMacroCommandVersionOne(); + macroCommand = readMacroCommandAttributesVersionOne(); if (macroCommand != NULL) { if (macro != NULL) { macro->addMacroCommand(macroCommand); @@ -177,7 +179,14 @@ WuQMacroGroupXmlReader::readVersionOne(WuQMacroGroup* macroGroup) } } else if (elementName == ELEMENT_MOUSE_EVENT_INFO) { - CaretAssert(0); + WuQMacroMouseEventInfo* mouseEventInfo = readMacroMouseEventInfo(); + macroCommand->setMouseEventInfo(mouseEventInfo); + } + else if (elementName == ELEMENT_TOOL_TIP) { + const QString text = m_xmlStreamReader->readElementText(); + if (macroCommand != NULL) { + macroCommand->setToolTip(text); + } } else { addToWarnings("Unexpected element=" @@ -250,12 +259,12 @@ WuQMacroGroupXmlReader::readMacroVersionOne() } /** - * Read version one of macro command + * Read version one of macro command attributes * * @Return The macro group */ WuQMacroCommand* -WuQMacroGroupXmlReader::readMacroCommandVersionOne() +WuQMacroGroupXmlReader::readMacroCommandAttributesVersionOne() { WuQMacroCommand* macroCommand(NULL); @@ -272,7 +281,7 @@ WuQMacroGroupXmlReader::readMacroCommandVersionOne() if (dataTypeString.isEmpty()) es.append(ATTRIBUTE_OBJECT_DATA_TYPE + " "); if (dataTypeTwoString.isEmpty()) es.append(ATTRIBUTE_OBJECT_DATA_TYPE + " "); if (classString.isEmpty()) es.append(ATTRIBUTE_OBJECT_CLASS + " "); -// empty could be valid for line edit if (valueString.isEmpty()) es.append(ATTRIBUTE_OBJECT_VALUE + " "); + // empty could be valid for line edit if (valueString.isEmpty()) es.append(ATTRIBUTE_OBJECT_VALUE + " "); if ( ! es.isEmpty()) { addToWarnings(ELEMENT_MACRO_COMMAND + " is missing attribute(s): " @@ -283,7 +292,7 @@ WuQMacroGroupXmlReader::readMacroCommandVersionOne() bool objectTypeValid(false); WuQMacroClassTypeEnum::Enum objectClass = WuQMacroClassTypeEnum::fromName(classString.toString(), - &objectTypeValid); + &objectTypeValid); if (! objectTypeValid) { es.append(classString.toString() + " is not valid for attribute " @@ -323,11 +332,8 @@ WuQMacroGroupXmlReader::readMacroCommandVersionOne() } if (objectClass == WuQMacroClassTypeEnum::MOUSE_USER_EVENT) { - WuQMacroMouseEventInfo* mouseEventInfo = readMacroMouseEventInfo(); - if (mouseEventInfo != NULL) { - macroCommand = new WuQMacroCommand(objectName.toString(), - mouseEventInfo); - } + macroCommand = new WuQMacroCommand(WuQMacroClassTypeEnum::MOUSE_USER_EVENT, + objectName.toString()); } else { QVariant value; @@ -379,15 +385,16 @@ WuQMacroGroupXmlReader::readMacroCommandVersionOne() } break; case WuQMacroDataValueTypeEnum::MOUSE: - CaretAssertMessage(0, "Mouse is special case handled above"); break; case WuQMacroDataValueTypeEnum::STRING: valueTwo.setValue(valueTwoString.toString()); break; } + QString tooltip; macroCommand = new WuQMacroCommand(objectClass, objectName.toString(), + tooltip, value, valueTwo); } @@ -395,17 +402,167 @@ WuQMacroGroupXmlReader::readMacroCommandVersionOne() return macroCommand; } + +///** +// * Read version one of macro command +// * +// * @Return The macro group +// */ +//WuQMacroCommand* +//WuQMacroGroupXmlReader::readMacroCommandVersionOne() +//{ +// WuQMacroCommand* macroCommand(NULL); +// +// const QXmlStreamAttributes attributes = m_xmlStreamReader->attributes(); +// const QStringRef objectName = attributes.value(ATTRIBUTE_NAME); +// const QStringRef dataTypeString = attributes.value(ATTRIBUTE_OBJECT_DATA_TYPE); +// const QStringRef classString = attributes.value(ATTRIBUTE_OBJECT_CLASS); +// const QStringRef valueString = attributes.value(ATTRIBUTE_OBJECT_VALUE); +// const QStringRef dataTypeTwoString = attributes.value(ATTRIBUTE_OBJECT_DATA_TYPE_TWO); +// const QStringRef valueTwoString = attributes.value(ATTRIBUTE_OBJECT_VALUE_TWO); +// +// QString es; +// if (objectName.isEmpty()) es.append(ATTRIBUTE_NAME + " "); +// if (dataTypeString.isEmpty()) es.append(ATTRIBUTE_OBJECT_DATA_TYPE + " "); +// if (dataTypeTwoString.isEmpty()) es.append(ATTRIBUTE_OBJECT_DATA_TYPE + " "); +// if (classString.isEmpty()) es.append(ATTRIBUTE_OBJECT_CLASS + " "); +//// empty could be valid for line edit if (valueString.isEmpty()) es.append(ATTRIBUTE_OBJECT_VALUE + " "); +// if ( ! es.isEmpty()) { +// addToWarnings(ELEMENT_MACRO_COMMAND +// + " is missing attribute(s): " +// + es); +// return NULL; +// } +// +// +// bool objectTypeValid(false); +// WuQMacroClassTypeEnum::Enum objectClass = WuQMacroClassTypeEnum::fromName(classString.toString(), +// &objectTypeValid); +// if (! objectTypeValid) { +// es.append(classString.toString() +// + " is not valid for attribute " +// + ATTRIBUTE_OBJECT_CLASS +// + " "); +// return NULL; +// } +// +// bool dataTypeValid(false); +// WuQMacroDataValueTypeEnum::Enum dataValueType = WuQMacroDataValueTypeEnum::fromName(dataTypeString.toString(), +// &dataTypeValid); +// if (! dataTypeValid) { +// es.append(dataTypeString.toString() +// + " is not valid for attribute " +// + ATTRIBUTE_OBJECT_DATA_TYPE +// + " "); +// return NULL; +// } +// +// bool dataTypeTwoValid(false); +// WuQMacroDataValueTypeEnum::Enum dataValueTypeTwo = WuQMacroDataValueTypeEnum::fromName(dataTypeTwoString.toString(), +// &dataTypeTwoValid); +// if (! dataTypeTwoValid) { +// es.append(dataTypeTwoString.toString() +// + " is not valid for attribute " +// + ATTRIBUTE_OBJECT_DATA_TYPE_TWO +// + " "); +// return NULL; +// } +// +// +// if ( ! es.isEmpty()) { +// addToWarnings(ELEMENT_MACRO_COMMAND +// + " has invalid attributes: " +// + es); +// return NULL; +// } +// +// if (objectClass == WuQMacroClassTypeEnum::MOUSE_USER_EVENT) { +// WuQMacroMouseEventInfo* mouseEventInfo = readMacroMouseEventInfo(); +// if (mouseEventInfo != NULL) { +// macroCommand = new WuQMacroCommand(objectName.toString(), +// mouseEventInfo); +// } +// } +// else { +// QVariant value; +// switch (dataValueType) { +// case WuQMacroDataValueTypeEnum::BOOLEAN: +// { +// const bool boolValue = ((valueString == VALUE_BOOL_TRUE) ? true : false); +// value.setValue(boolValue); +// } +// break; +// case WuQMacroDataValueTypeEnum::FLOAT: +// { +// const double floatValue = valueString.toFloat(); +// value.setValue(floatValue); +// } +// break; +// case WuQMacroDataValueTypeEnum::INTEGER: +// { +// const int32_t intValue = valueString.toInt(); +// value.setValue(intValue); +// } +// break; +// case WuQMacroDataValueTypeEnum::MOUSE: +// CaretAssertMessage(0, "Mouse is special case handled above"); +// break; +// case WuQMacroDataValueTypeEnum::STRING: +// value.setValue(valueString.toString()); +// break; +// } +// +// QVariant valueTwo; +// switch (dataValueTypeTwo) { +// case WuQMacroDataValueTypeEnum::BOOLEAN: +// { +// const bool boolValue = ((valueTwoString == VALUE_BOOL_TRUE) ? true : false); +// valueTwo.setValue(boolValue); +// } +// break; +// case WuQMacroDataValueTypeEnum::FLOAT: +// { +// const double floatValue = valueTwoString.toFloat(); +// valueTwo.setValue(floatValue); +// } +// break; +// case WuQMacroDataValueTypeEnum::INTEGER: +// { +// const int32_t intValue = valueTwoString.toInt(); +// valueTwo.setValue(intValue); +// } +// break; +// case WuQMacroDataValueTypeEnum::MOUSE: +// CaretAssertMessage(0, "Mouse is special case handled above"); +// break; +// case WuQMacroDataValueTypeEnum::STRING: +// valueTwo.setValue(valueTwoString.toString()); +// break; +// } +// +// QString tooltip; +// CaretAssertToDoWarning(); +// macroCommand = new WuQMacroCommand(objectClass, +// objectName.toString(), +// tooltip, +// value, +// valueTwo); +// } +// +// return macroCommand; +//} + /** * @return Read and return the mouse event information */ WuQMacroMouseEventInfo* WuQMacroGroupXmlReader::readMacroMouseEventInfo() { - if ( ! m_xmlStreamReader->readNextStartElement()) { - addToWarnings("Failed to read start element for element " - + ELEMENT_MOUSE_EVENT_INFO); - return NULL; - } +// if ( ! m_xmlStreamReader->readNextStartElement()) { +// addToWarnings("Failed to read start element for element " +// + ELEMENT_MOUSE_EVENT_INFO); +// return NULL; +// } const QXmlStreamAttributes attributes = m_xmlStreamReader->attributes(); const QString mouseEventTypeString = attributes.value(ATTRIBUTE_MOUSE_EVENT_TYPE).toString(); diff --git a/src/Common/WuQMacroGroupXmlReader.h b/src/Common/WuQMacroGroupXmlReader.h index da3f81a02..5a0434a60 100644 --- a/src/Common/WuQMacroGroupXmlReader.h +++ b/src/Common/WuQMacroGroupXmlReader.h @@ -68,6 +68,8 @@ namespace caret { WuQMacro* readMacroVersionOne(); + WuQMacroCommand* readMacroCommandAttributesVersionOne(); + WuQMacroCommand* readMacroCommandVersionOne(); void addToWarnings(const QString& warning); diff --git a/src/Common/WuQMacroGroupXmlWriter.cxx b/src/Common/WuQMacroGroupXmlWriter.cxx index 4e3c4c4bc..41ad115d3 100644 --- a/src/Common/WuQMacroGroupXmlWriter.cxx +++ b/src/Common/WuQMacroGroupXmlWriter.cxx @@ -234,6 +234,11 @@ WuQMacroGroupXmlWriter::writeMacroCommandToXML(const WuQMacroCommand* macroComma break; } + const QString toolTip = macroCommand->getObjectToolTip(); + if (toolTip != NULL) { + m_xmlStreamWriter->writeTextElement(ELEMENT_TOOL_TIP, + toolTip); + } m_xmlStreamWriter->writeEndElement(); } diff --git a/src/Common/WuQMacroStandardItemTypeEnum.cxx b/src/Common/WuQMacroStandardItemTypeEnum.cxx new file mode 100644 index 000000000..60b274fba --- /dev/null +++ b/src/Common/WuQMacroStandardItemTypeEnum.cxx @@ -0,0 +1,387 @@ + +/*LICENSE_START*/ +/* + * Copyright (C) 2019 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + +#include +#define __WU_Q_MACRO_STANDARD_ITEM_TYPE_ENUM_DECLARE__ +#include "WuQMacroStandardItemTypeEnum.h" +#undef __WU_Q_MACRO_STANDARD_ITEM_TYPE_ENUM_DECLARE__ + +#include + +#include "CaretAssert.h" + +using namespace caret; + + +/** + * \class caret::WuQMacroStandardItemTypeEnum + * \brief Enumerated type for macro items in a QStandardItemModel + * + * Macros and macro command are displayed in the GUI using a + * QStandardItem. The QStandardItem documentation recommends + * overriding the type() method and returning a value greater + * than or equal to UserType. This enumerated type provides + * these values. + * + * Using this enumerated type in the GUI with an EnumComboBoxTemplate + * + * Header File (.h) + * Forward declare the data type: + * class EnumComboBoxTemplate; + * + * Declare the member: + * EnumComboBoxTemplate* m_wuQMacroStandardItemTypeEnumComboBox; + * + * Declare a slot that is called when user changes selection + * private slots: + * void wuQMacroStandardItemTypeEnumComboBoxItemActivated(); + * + * Implementation File (.cxx) + * Include the header files + * #include "EnumComboBoxTemplate.h" + * #include "WuQMacroStandardItemTypeEnum.h" + * + * Instatiate: + * m_wuQMacroStandardItemTypeEnumComboBox = new EnumComboBoxTemplate(this); + * m_wuQMacroStandardItemTypeEnumComboBox->setup(); + * + * Get notified when the user changes the selection: + * QObject::connect(m_wuQMacroStandardItemTypeEnumComboBox, SIGNAL(itemActivated()), + * this, SLOT(wuQMacroStandardItemTypeEnumComboBoxItemActivated())); + * + * Update the selection: + * m_wuQMacroStandardItemTypeEnumComboBox->setSelectedItem(NEW_VALUE); + * + * Read the selection: + * const WuQMacroStandardItemTypeEnum::Enum VARIABLE = m_wuQMacroStandardItemTypeEnumComboBox->getSelectedItem(); + * + */ + +/** + * Constructor. + * + * @param enumValue + * An enumerated value. + * @param integerCode + * Integer code for this enumerated value. + * + * @param name + * Name of enumerated value. + * + * @param guiName + * User-friendly name for use in user-interface. + */ +WuQMacroStandardItemTypeEnum::WuQMacroStandardItemTypeEnum(const Enum enumValue, + const int32_t integerCode, + const AString& name, + const AString& guiName) +{ + this->enumValue = enumValue; + this->integerCode = integerCode; + this->name = name; + this->guiName = guiName; +} + +/** + * Destructor. + */ +WuQMacroStandardItemTypeEnum::~WuQMacroStandardItemTypeEnum() +{ +} + +/** + * Initialize the enumerated metadata. + */ +void +WuQMacroStandardItemTypeEnum::initialize() +{ + if (initializedFlag) { + return; + } + initializedFlag = true; + + enumData.push_back(WuQMacroStandardItemTypeEnum(MACRO, + QStandardItem::UserType + 1, + "MACRO", + "Macro")); + + enumData.push_back(WuQMacroStandardItemTypeEnum(MACRO_COMMAND, + QStandardItem::UserType + 2, + "MACRO_COMMAND", + "Macro Command")); + +} + +/** + * Find the data for and enumerated value. + * @param enumValue + * The enumerated value. + * @return Pointer to data for this enumerated type + * or NULL if no data for type or if type is invalid. + */ +const WuQMacroStandardItemTypeEnum* +WuQMacroStandardItemTypeEnum::findData(const Enum enumValue) +{ + if (initializedFlag == false) initialize(); + + size_t num = enumData.size(); + for (size_t i = 0; i < num; i++) { + const WuQMacroStandardItemTypeEnum* d = &enumData[i]; + if (d->enumValue == enumValue) { + return d; + } + } + + return NULL; +} + +/** + * Get a string representation of the enumerated type. + * @param enumValue + * Enumerated value. + * @return + * String representing enumerated value. + */ +AString +WuQMacroStandardItemTypeEnum::toName(Enum enumValue) { + if (initializedFlag == false) initialize(); + + const WuQMacroStandardItemTypeEnum* enumInstance = findData(enumValue); + return enumInstance->name; +} + +/** + * Get an enumerated value corresponding to its name. + * @param name + * Name of enumerated value. + * @param isValidOut + * If not NULL, it is set indicating that a + * enum value exists for the input name. + * @return + * Enumerated value. + */ +WuQMacroStandardItemTypeEnum::Enum +WuQMacroStandardItemTypeEnum::fromName(const AString& name, bool* isValidOut) +{ + if (initializedFlag == false) initialize(); + + bool validFlag = false; + Enum enumValue = WuQMacroStandardItemTypeEnum::enumData[0].enumValue; + + for (std::vector::iterator iter = enumData.begin(); + iter != enumData.end(); + iter++) { + const WuQMacroStandardItemTypeEnum& d = *iter; + if (d.name == name) { + enumValue = d.enumValue; + validFlag = true; + break; + } + } + + if (isValidOut != 0) { + *isValidOut = validFlag; + } + else if (validFlag == false) { + CaretAssertMessage(0, AString("Name " + name + "failed to match enumerated value for type WuQMacroStandardItemTypeEnum")); + } + return enumValue; +} + +/** + * Get a GUI string representation of the enumerated type. + * @param enumValue + * Enumerated value. + * @return + * String representing enumerated value. + */ +AString +WuQMacroStandardItemTypeEnum::toGuiName(Enum enumValue) { + if (initializedFlag == false) initialize(); + + const WuQMacroStandardItemTypeEnum* enumInstance = findData(enumValue); + return enumInstance->guiName; +} + +/** + * Get an enumerated value corresponding to its GUI name. + * @param s + * Name of enumerated value. + * @param isValidOut + * If not NULL, it is set indicating that a + * enum value exists for the input name. + * @return + * Enumerated value. + */ +WuQMacroStandardItemTypeEnum::Enum +WuQMacroStandardItemTypeEnum::fromGuiName(const AString& guiName, bool* isValidOut) +{ + if (initializedFlag == false) initialize(); + + bool validFlag = false; + Enum enumValue = WuQMacroStandardItemTypeEnum::enumData[0].enumValue; + + for (std::vector::iterator iter = enumData.begin(); + iter != enumData.end(); + iter++) { + const WuQMacroStandardItemTypeEnum& d = *iter; + if (d.guiName == guiName) { + enumValue = d.enumValue; + validFlag = true; + break; + } + } + + if (isValidOut != 0) { + *isValidOut = validFlag; + } + else if (validFlag == false) { + CaretAssertMessage(0, AString("guiName " + guiName + "failed to match enumerated value for type WuQMacroStandardItemTypeEnum")); + } + return enumValue; +} + +/** + * Get the integer code for a data type. + * + * @return + * Integer code for data type. + */ +int32_t +WuQMacroStandardItemTypeEnum::toIntegerCode(Enum enumValue) +{ + if (initializedFlag == false) initialize(); + const WuQMacroStandardItemTypeEnum* enumInstance = findData(enumValue); + return enumInstance->integerCode; +} + +/** + * Find the data type corresponding to an integer code. + * + * @param integerCode + * Integer code for enum. + * @param isValidOut + * If not NULL, on exit isValidOut will indicate if + * integer code is valid. + * @return + * Enum for integer code. + */ +WuQMacroStandardItemTypeEnum::Enum +WuQMacroStandardItemTypeEnum::fromIntegerCode(const int32_t integerCode, bool* isValidOut) +{ + if (initializedFlag == false) initialize(); + + bool validFlag = false; + Enum enumValue = WuQMacroStandardItemTypeEnum::enumData[0].enumValue; + + for (std::vector::iterator iter = enumData.begin(); + iter != enumData.end(); + iter++) { + const WuQMacroStandardItemTypeEnum& enumInstance = *iter; + if (enumInstance.integerCode == integerCode) { + enumValue = enumInstance.enumValue; + validFlag = true; + break; + } + } + + if (isValidOut != 0) { + *isValidOut = validFlag; + } + else if (validFlag == false) { + CaretAssertMessage(0, AString("Integer code " + AString::number(integerCode) + "failed to match enumerated value for type WuQMacroStandardItemTypeEnum")); + } + return enumValue; +} + +/** + * Get all of the enumerated type values. The values can be used + * as parameters to toXXX() methods to get associated metadata. + * + * @param allEnums + * A vector that is OUTPUT containing all of the enumerated values. + */ +void +WuQMacroStandardItemTypeEnum::getAllEnums(std::vector& allEnums) +{ + if (initializedFlag == false) initialize(); + + allEnums.clear(); + + for (std::vector::iterator iter = enumData.begin(); + iter != enumData.end(); + iter++) { + allEnums.push_back(iter->enumValue); + } +} + +/** + * Get all of the names of the enumerated type values. + * + * @param allNames + * A vector that is OUTPUT containing all of the names of the enumerated values. + * @param isSorted + * If true, the names are sorted in alphabetical order. + */ +void +WuQMacroStandardItemTypeEnum::getAllNames(std::vector& allNames, const bool isSorted) +{ + if (initializedFlag == false) initialize(); + + allNames.clear(); + + for (std::vector::iterator iter = enumData.begin(); + iter != enumData.end(); + iter++) { + allNames.push_back(WuQMacroStandardItemTypeEnum::toName(iter->enumValue)); + } + + if (isSorted) { + std::sort(allNames.begin(), allNames.end()); + } +} + +/** + * Get all of the GUI names of the enumerated type values. + * + * @param allNames + * A vector that is OUTPUT containing all of the GUI names of the enumerated values. + * @param isSorted + * If true, the names are sorted in alphabetical order. + */ +void +WuQMacroStandardItemTypeEnum::getAllGuiNames(std::vector& allGuiNames, const bool isSorted) +{ + if (initializedFlag == false) initialize(); + + allGuiNames.clear(); + + for (std::vector::iterator iter = enumData.begin(); + iter != enumData.end(); + iter++) { + allGuiNames.push_back(WuQMacroStandardItemTypeEnum::toGuiName(iter->enumValue)); + } + + if (isSorted) { + std::sort(allGuiNames.begin(), allGuiNames.end()); + } +} + diff --git a/src/Common/WuQMacroStandardItemTypeEnum.h b/src/Common/WuQMacroStandardItemTypeEnum.h new file mode 100644 index 000000000..de7f6399e --- /dev/null +++ b/src/Common/WuQMacroStandardItemTypeEnum.h @@ -0,0 +1,101 @@ +#ifndef __WU_Q_MACRO_STANDARD_ITEM_TYPE_ENUM_H__ +#define __WU_Q_MACRO_STANDARD_ITEM_TYPE_ENUM_H__ + +/*LICENSE_START*/ +/* + * Copyright (C) 2019 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + + +#include +#include +#include "AString.h" + +namespace caret { + +class WuQMacroStandardItemTypeEnum { + +public: + /** + * Enumerated values. + */ + enum Enum { + /** Type for a macro */ + MACRO, + /** Type for a macro command */ + MACRO_COMMAND + }; + + + ~WuQMacroStandardItemTypeEnum(); + + static AString toName(Enum enumValue); + + static Enum fromName(const AString& name, bool* isValidOut); + + static AString toGuiName(Enum enumValue); + + static Enum fromGuiName(const AString& guiName, bool* isValidOut); + + static int32_t toIntegerCode(Enum enumValue); + + static Enum fromIntegerCode(const int32_t integerCode, bool* isValidOut); + + static void getAllEnums(std::vector& allEnums); + + static void getAllNames(std::vector& allNames, const bool isSorted); + + static void getAllGuiNames(std::vector& allGuiNames, const bool isSorted); + +private: + WuQMacroStandardItemTypeEnum(const Enum enumValue, + const int32_t integerCode, + const AString& name, + const AString& guiName); + + static const WuQMacroStandardItemTypeEnum* findData(const Enum enumValue); + + /** Holds all instance of enum values and associated metadata */ + static std::vector enumData; + + /** Initialize instances that contain the enum values and metadata */ + static void initialize(); + + /** Indicates instance of enum values and metadata have been initialized */ + static bool initializedFlag; + + /** The enumerated type value for an instance */ + Enum enumValue; + + /** The integer code associated with an enumerated value */ + int32_t integerCode; + + /** The name, a text string that is identical to the enumerated value */ + AString name; + + /** A user-friendly name that is displayed in the GUI */ + AString guiName; +}; + +#ifdef __WU_Q_MACRO_STANDARD_ITEM_TYPE_ENUM_DECLARE__ +std::vector WuQMacroStandardItemTypeEnum::enumData; +bool WuQMacroStandardItemTypeEnum::initializedFlag = false; +#endif // __WU_Q_MACRO_STANDARD_ITEM_TYPE_ENUM_DECLARE__ + +} // namespace +#endif //__WU_Q_MACRO_STANDARD_ITEM_TYPE_ENUM_H__ diff --git a/src/Common/WuQMacroStandardItemTypes.cxx b/src/Common/WuQMacroStandardItemTypes.cxx deleted file mode 100644 index cbe72673a..000000000 --- a/src/Common/WuQMacroStandardItemTypes.cxx +++ /dev/null @@ -1,71 +0,0 @@ - -/*LICENSE_START*/ -/* - * Copyright (C) 2019 Washington University School of Medicine - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ -/*LICENSE_END*/ - -#define __WU_Q_MACRO_STANDARD_ITEM_TYPES_DECLARE__ -#include "WuQMacroStandardItemTypes.h" -#undef __WU_Q_MACRO_STANDARD_ITEM_TYPES_DECLARE__ - -#include - -#include "CaretAssert.h" -using namespace caret; - - - -/** - * \class caret::WuQMacroStandardItemTypes - * \brief Standard Items Types for Macro Classes that subclass QStandardItem - * \ingroup Common - */ - -/** - * Constructor. - */ -WuQMacroStandardItemTypes::WuQMacroStandardItemTypes() -{ -} - -/** - * Destructor. - */ -WuQMacroStandardItemTypes::~WuQMacroStandardItemTypes() -{ -} - -/** - * @return Standard item type for WuQMacro - */ -int -WuQMacroStandardItemTypes::typeWuQMacro() -{ - return (QStandardItem::UserType + 1); -} - -/** - * @return Standard item type for WuQMacroCommand - */ -int -WuQMacroStandardItemTypes::typeWuQMacroCommand() -{ - return (QStandardItem::UserType + 2); -} - - diff --git a/src/Common/WuQMacroStandardItemTypes.h b/src/Common/WuQMacroStandardItemTypes.h deleted file mode 100644 index 459b5373f..000000000 --- a/src/Common/WuQMacroStandardItemTypes.h +++ /dev/null @@ -1,63 +0,0 @@ -#ifndef __WU_Q_MACRO_STANDARD_ITEM_TYPES_H__ -#define __WU_Q_MACRO_STANDARD_ITEM_TYPES_H__ - -/*LICENSE_START*/ -/* - * Copyright (C) 2019 Washington University School of Medicine - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ -/*LICENSE_END*/ - - - -#include - - - - - -namespace caret { - - class WuQMacroStandardItemTypes { - - public: - static int typeWuQMacro(); - - static int typeWuQMacroCommand(); - - private: - WuQMacroStandardItemTypes(); - - virtual ~WuQMacroStandardItemTypes(); - - WuQMacroStandardItemTypes(const WuQMacroStandardItemTypes&) = delete; - - WuQMacroStandardItemTypes& operator=(const WuQMacroStandardItemTypes&) = delete; - - - // ADD_NEW_METHODS_HERE - - private: - // ADD_NEW_MEMBERS_HERE - - }; - -#ifdef __WU_Q_MACRO_STANDARD_ITEM_TYPES_DECLARE__ - // -#endif // __WU_Q_MACRO_STANDARD_ITEM_TYPES_DECLARE__ - -} // namespace -#endif //__WU_Q_MACRO_STANDARD_ITEM_TYPES_H__ diff --git a/src/Files/SceneFileSaxReader.cxx b/src/Files/SceneFileSaxReader.cxx index f17ffc264..6d09e2a33 100644 --- a/src/Files/SceneFileSaxReader.cxx +++ b/src/Files/SceneFileSaxReader.cxx @@ -35,7 +35,6 @@ #include "XmlAttributes.h" #include "XmlException.h" #include "XmlUtilities.h" -#include "WuQMacroGroupXmlReader.h" using namespace caret; diff --git a/src/GuiQt/CMakeLists.txt b/src/GuiQt/CMakeLists.txt index f09b52106..5a6b925d3 100644 --- a/src/GuiQt/CMakeLists.txt +++ b/src/GuiQt/CMakeLists.txt @@ -207,7 +207,6 @@ ELSE() WuQMacroAttributesDialog.h WuQMacroCreateDialog.h WuQMacroDialog.h - WuQMacroEditorDialog.h WuQMacroExecutor.h WuQMacroHelperInterface.h WuQMacroManager.h @@ -466,7 +465,6 @@ WuQListWidget.h WuQMacroAttributesDialog.h WuQMacroCreateDialog.h WuQMacroDialog.h -WuQMacroEditorDialog.h WuQMacroExecutor.h WuQMacroExecutorOptions.h WuQMacroHelperInterface.h @@ -713,7 +711,6 @@ WuQListWidget.cxx WuQMacroAttributesDialog.cxx WuQMacroCreateDialog.cxx WuQMacroDialog.cxx -WuQMacroEditorDialog.cxx WuQMacroExecutor.cxx WuQMacroExecutorOptions.cxx WuQMacroManager.cxx diff --git a/src/GuiQt/WuQMacroDialog.cxx b/src/GuiQt/WuQMacroDialog.cxx index 4e9dd2018..64f3e2267 100644 --- a/src/GuiQt/WuQMacroDialog.cxx +++ b/src/GuiQt/WuQMacroDialog.cxx @@ -43,7 +43,7 @@ #include "WuQMacroGroup.h" #include "WuQMacroExecutor.h" #include "WuQMacroManager.h" -#include "WuQMacroStandardItemTypes.h" +#include "WuQMacroStandardItemTypeEnum.h" using namespace caret; @@ -148,11 +148,6 @@ WuQMacroDialog::createMacroButtonsWidget() QObject::connect(m_deletePushButton, &QPushButton::clicked, this, &WuQMacroDialog::deleteButtonClicked); - m_editPushButton = new QPushButton("Edit..."); - m_editPushButton->setToolTip("Edit the steps (commands) in the selected macro"); - QObject::connect(m_editPushButton, &QPushButton::clicked, - this, &WuQMacroDialog::editButtonClicked); - m_importPushButton = new QPushButton("Import..."); m_importPushButton->setToolTip("Import a macro"); QObject::connect(m_importPushButton, &QPushButton::clicked, @@ -166,7 +161,6 @@ WuQMacroDialog::createMacroButtonsWidget() QVBoxLayout* macroButtonsLayout = new QVBoxLayout(widget); macroButtonsLayout->setSpacing(4); macroButtonsLayout->addWidget(m_attributesPushButton); - macroButtonsLayout->addWidget(m_editPushButton); macroButtonsLayout->addSpacing(15); macroButtonsLayout->addWidget(m_deletePushButton); macroButtonsLayout->addSpacing(15); @@ -316,11 +310,22 @@ WuQMacroDialog::createCommandWidget() QLabel* titleLabel = new QLabel("Title"); m_commandTitleLabel = new QLabel(); - QWidget* widget = new QWidget(); - QGridLayout* layout = new QGridLayout(widget); + QLabel* toolTipLabel = new QLabel("ToolTip"); + m_commandToolTip = new QLabel(); + m_commandToolTip->setWordWrap(true); + + QGridLayout* layout = new QGridLayout(); int row = 0; layout->addWidget(titleLabel, row, 0); layout->addWidget(m_commandTitleLabel, row, 1); + row++; + layout->addWidget(toolTipLabel, row, 0); + layout->addWidget(m_commandToolTip, row, 1); + + QWidget* widget = new QWidget(); + QVBoxLayout* widgetLayout = new QVBoxLayout(widget); + widgetLayout->addLayout(layout); + widgetLayout->addStretch(); return widget; } @@ -389,15 +394,26 @@ WuQMacroDialog::treeViewItemClicked(const QModelIndex& modelIndex) if (selectedModel != NULL) { QStandardItem* selectedItem = selectedModel->itemFromIndex(modelIndex); - if (selectedItem->type() == WuQMacroStandardItemTypes::typeWuQMacro()) { - macro = dynamic_cast(selectedItem); - CaretAssert(macro); - m_stackedWidget->setCurrentWidget(m_macroWidget); + bool validFlag(false); + const WuQMacroStandardItemTypeEnum::Enum itemType = WuQMacroStandardItemTypeEnum::fromIntegerCode(selectedItem->type(), + &validFlag); + if (validFlag) { + switch (itemType) { + case WuQMacroStandardItemTypeEnum::MACRO: + macro = dynamic_cast(selectedItem); + CaretAssert(macro); + m_stackedWidget->setCurrentWidget(m_macroWidget); + break; + case WuQMacroStandardItemTypeEnum::MACRO_COMMAND: + macroCommand = dynamic_cast(selectedItem); + CaretAssert(macroCommand); + m_stackedWidget->setCurrentWidget(m_commandWidget); + break; + } } - else if (selectedItem->type() == WuQMacroStandardItemTypes::typeWuQMacroCommand()) { - macroCommand = dynamic_cast(selectedItem); - CaretAssert(macroCommand); - m_stackedWidget->setCurrentWidget(m_commandWidget); + else { + CaretAssertMessage(0, + ("Invalid StandardItemModel type=" + AString::number(selectedItem->type()))); } } @@ -449,10 +465,13 @@ void WuQMacroDialog::updateCommandWidget(WuQMacroCommand* command) { QString text; + QString toolTip; if (command != NULL) { - text = command->text(); + text = command->text(); + toolTip = command->getObjectToolTip(); } m_commandTitleLabel->setText(text); + m_commandToolTip->setText(toolTip); } /** @@ -496,17 +515,31 @@ WuQMacroDialog::getSelectedMacro() if (selectedModel != NULL) { QStandardItem* selectedItem = selectedModel->itemFromIndex(modelIndex); - if (selectedItem->type() == WuQMacroStandardItemTypes::typeWuQMacroCommand()) { - /* - * Parent should be WuQMacro - */ - selectedItem = selectedItem->parent(); - CaretAssert(selectedItem); + bool validFlag(false); + const WuQMacroStandardItemTypeEnum::Enum itemType = WuQMacroStandardItemTypeEnum::fromIntegerCode(selectedItem->type(), + &validFlag); + if (validFlag) { + switch (itemType) { + case WuQMacroStandardItemTypeEnum::MACRO_COMMAND: + { + /* + * Parent should be WuQMacro + */ + QStandardItem* selectedItemParent = selectedItem->parent(); + CaretAssert(selectedItemParent); + macro = dynamic_cast(selectedItemParent); + CaretAssert(macro); + } + break; + case WuQMacroStandardItemTypeEnum::MACRO: + macro = dynamic_cast(selectedItem); + CaretAssert(macro); + break; + } } - - if (selectedItem->type() == WuQMacroStandardItemTypes::typeWuQMacro()) { - macro = dynamic_cast(selectedItem); - CaretAssert(macro); + else { + CaretAssertMessage(0, + ("Invalid StandardItemModel type=" + AString::number(selectedItem->type()))); } } } @@ -567,20 +600,6 @@ WuQMacroDialog::deleteButtonClicked() } } -/** - * Called when the edit button is clicked - */ -void -WuQMacroDialog::editButtonClicked() -{ - WuQMacro* macro = getSelectedMacro(); - if (macro != NULL) { - if (WuQMacroManager::instance()->editMacroCommands(m_editPushButton, macro)) { - updateDialogContents(); - } - } -} - /** * Called when import button is clicked */ diff --git a/src/GuiQt/WuQMacroDialog.h b/src/GuiQt/WuQMacroDialog.h index d0acfe671..1dcce19ad 100644 --- a/src/GuiQt/WuQMacroDialog.h +++ b/src/GuiQt/WuQMacroDialog.h @@ -70,8 +70,6 @@ namespace caret { void deleteButtonClicked(); - void editButtonClicked(); - void importButtonClicked(); void exportButtonClicked(); @@ -121,8 +119,6 @@ namespace caret { QPushButton* m_deletePushButton; - QPushButton* m_editPushButton; - QPushButton* m_attributesPushButton; QPushButton* m_importPushButton; @@ -137,6 +133,8 @@ namespace caret { QLabel* m_commandTitleLabel; + QLabel* m_commandToolTip; + // ADD_NEW_MEMBERS_HERE }; diff --git a/src/GuiQt/WuQMacroEditorDialog.cxx b/src/GuiQt/WuQMacroEditorDialog.cxx deleted file mode 100644 index fee02d52b..000000000 --- a/src/GuiQt/WuQMacroEditorDialog.cxx +++ /dev/null @@ -1,276 +0,0 @@ - -/*LICENSE_START*/ -/* - * Copyright (C) 2018 Washington University School of Medicine - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ -/*LICENSE_END*/ - -#define __WU_Q_MACRO_EDITOR_DIALOG_DECLARE__ -#include "WuQMacroEditorDialog.h" -#undef __WU_Q_MACRO_EDITOR_DIALOG_DECLARE__ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "CaretAssert.h" -#include "CaretLogger.h" -#include "WuQMacro.h" -#include "WuQMacroCommand.h" -#include "WuQMacroGroup.h" -#include "WuQMacroManager.h" -#include "WuQMacroShortCutKeyComboBox.h" - -using namespace caret; - - - -/** - * \class caret::WuQMacroEditorDialog - * \brief Dialog for creating a new macro - * \ingroup WuQMacro - */ - -/** - * Constructor. - * - * @param macro - * The macro being edited - * @param parent - * The parent widget - */ -WuQMacroEditorDialog::WuQMacroEditorDialog(WuQMacro* macro, - QWidget* parent) -: QDialog(parent), -m_macro(macro), -m_macroWasModifiedFlag(false) -{ - CaretAssert(m_macro); - - setWindowTitle("Macro Editor"); - - QLabel* nameLabel = new QLabel("Macro name:"); - QLabel* shortCutKeyLabel = new QLabel("Short Cut Key:"); - QLabel* shortCutKeyMaskLabel = new QLabel(WuQMacroManager::getShortCutKeysMask()); - QLabel* descriptionLabel = new QLabel("Description:"); - QLabel* commandsLabel = new QLabel("Commands:"); - - m_macroNameLineEdit = new QLineEdit(); - m_macroNameLineEdit->setText(m_macro->getName()); - m_macroShortCutKeyComboBox = new WuQMacroShortCutKeyComboBox(this); - m_macroShortCutKeyComboBox->setSelectedShortCutKey(m_macro->getShortCutKey()); - m_macroDescriptionTextEdit = new QPlainTextEdit(); - m_macroDescriptionTextEdit->setFixedHeight(60); - m_macroDescriptionTextEdit->setPlainText(m_macro->getDescription()); - - m_tableWidget = createTableWidget(); - - QGridLayout* gridLayout = new QGridLayout(); - int row = 0; - gridLayout->addWidget(nameLabel, row, 0); - gridLayout->addWidget(m_macroNameLineEdit, row, 1, 1, 2); - row++; - gridLayout->addWidget(shortCutKeyLabel, row, 0); - gridLayout->addWidget(shortCutKeyMaskLabel, row, 1); - gridLayout->addWidget(m_macroShortCutKeyComboBox->getWidget(), row, 2, Qt::AlignLeft); - row++; - gridLayout->addWidget(descriptionLabel, row, 0); - gridLayout->addWidget(m_macroDescriptionTextEdit, row, 1, 1, 2); - row++; - gridLayout->addWidget(commandsLabel, row, 0); - gridLayout->addWidget(m_tableWidget, row, 1, 1, 2); - row++; - - - m_dialogButtonBox = new QDialogButtonBox(QDialogButtonBox::Ok - | QDialogButtonBox::Cancel); - QObject::connect(m_dialogButtonBox, &QDialogButtonBox::accepted, - this, &WuQMacroEditorDialog::accept); - QObject::connect(m_dialogButtonBox, &QDialogButtonBox::rejected, - this, &WuQMacroEditorDialog::reject); - - QVBoxLayout* dialogLayout = new QVBoxLayout(this); - dialogLayout->addLayout(gridLayout); - dialogLayout->addWidget(m_dialogButtonBox); - - loadTableWidget(); -} - -/** - * Destructor. - */ -WuQMacroEditorDialog::~WuQMacroEditorDialog() -{ -} - -/** - * @return New instance of the table widget - */ -QTableWidget* -WuQMacroEditorDialog::createTableWidget() -{ - QTableWidget* tableWidget = new QTableWidget(); - -// QStringList columnNames; -// CaretAssert(COLUMN_TYPE == 0); -// columnNames << "Class"; -// CaretAssert(COLUMN_NAME == 1); -// columnNames << "Name"; -// CaretAssert(COLUMN_VALUE_TYPE == 2); -// columnNames << "Value-Type"; -// CaretAssert(COLUMN_VALUE == 3); -// columnNames << "Value"; -// CaretAssert(COLUMN_VALUE_TWO_TYPE == 4); -// columnNames << "Value-Two-Type"; -// CaretAssert(COLUMN_VALUE_TWO == 5); -// columnNames << "Value-Two"; -// CaretAssert(COLUMN_TOOLTIP == 6); -// columnNames << "ToolTip"; -// CaretAssert(columnNames.size() == NUMBER_OF_COLUMNS); -// -// tableWidget->setHorizontalHeaderLabels(columnNames); - - return tableWidget; -} - -/** - * Load the table widget - */ -void -WuQMacroEditorDialog::loadTableWidget() -{ - CaretAssert(m_tableWidget); - - const int32_t numberOfCommands = m_macro->getNumberOfMacroCommands(); - m_tableWidget->setRowCount(numberOfCommands); - m_tableWidget->setColumnCount(NUMBER_OF_COLUMNS); - - for (int32_t i = 0; i < numberOfCommands; i++) { - const WuQMacroCommand* command = m_macro->getMacroCommandAtIndex(i); - QTableWidgetItem* classItem = new QTableWidgetItem(WuQMacroClassTypeEnum::toGuiName(command->getClassType())); - QTableWidgetItem* nameItem = new QTableWidgetItem(command->getObjectName()); - QTableWidgetItem* valueTypeItem = new QTableWidgetItem(WuQMacroDataValueTypeEnum::toGuiName(command->getDataType())); - QTableWidgetItem* valueItem = new QTableWidgetItem(command->getDataValue().toString()); - QTableWidgetItem* valueTwoTypeItem = new QTableWidgetItem(WuQMacroDataValueTypeEnum::toGuiName(command->getDataTypeTwo())); - QTableWidgetItem* valueTwoItem = new QTableWidgetItem(command->getDataValueTwo().toString()); - - const QString toolTip = WuQMacroManager::instance()->getToolTipForObjectName(command->getObjectName()); - QTableWidgetItem* toolTipItem = new QTableWidgetItem(toolTip); - - switch (command->getDataType()) { - case WuQMacroDataValueTypeEnum::BOOLEAN: - break; - case WuQMacroDataValueTypeEnum::FLOAT: - break; - case WuQMacroDataValueTypeEnum::INTEGER: - break; - case WuQMacroDataValueTypeEnum::MOUSE: - valueItem->setText("Mouse"); - break; - case WuQMacroDataValueTypeEnum::STRING: - break; - } - - m_tableWidget->setItem(i, COLUMN_TYPE, classItem); - m_tableWidget->setItem(i, COLUMN_NAME, nameItem); - m_tableWidget->setItem(i, COLUMN_VALUE_TYPE, valueTypeItem); - m_tableWidget->setItem(i, COLUMN_VALUE, valueItem); - m_tableWidget->setItem(i, COLUMN_VALUE_TWO_TYPE, valueTwoTypeItem); - m_tableWidget->setItem(i, COLUMN_VALUE_TWO, valueTwoItem); - m_tableWidget->setItem(i, COLUMN_TOOLTIP, toolTipItem); - } - - QStringList columnNames; - CaretAssert(COLUMN_TYPE == 0); - columnNames << "Class"; - CaretAssert(COLUMN_NAME == 1); - columnNames << "Name"; - CaretAssert(COLUMN_VALUE_TYPE == 2); - columnNames << "Value-Type"; - CaretAssert(COLUMN_VALUE == 3); - columnNames << "Value"; - CaretAssert(COLUMN_VALUE_TWO_TYPE == 4); - columnNames << "Value-Two-Type"; - CaretAssert(COLUMN_VALUE_TWO == 5); - columnNames << "Value-Two"; - CaretAssert(COLUMN_TOOLTIP == 6); - columnNames << "ToolTip"; - CaretAssert(columnNames.size() == NUMBER_OF_COLUMNS); - - m_tableWidget->setHorizontalHeaderLabels(columnNames); -} - - -/** - * Called when user clicks OK or Cancel - * - * @param r - * The dialog code (Accepted or Rejected) - */ -void -WuQMacroEditorDialog::done(int r) -{ - if (r == QDialog::Accepted) { - const QString name(m_macroNameLineEdit->text().trimmed()); - if (name.isEmpty()) { - QMessageBox::critical(this, - "Error", - "Name is missing", - QMessageBox::Ok, - QMessageBox::Ok); - return; - } - - const QString newName = m_macroNameLineEdit->text().trimmed(); - if (m_macro->getName() != newName) { - m_macro->setName(newName); - m_macroWasModifiedFlag = true; - } - - const WuQMacroShortCutKeyEnum::Enum newShortCutKey = m_macroShortCutKeyComboBox->getSelectedShortCutKey(); - if (m_macro->getShortCutKey() != newShortCutKey) { - m_macro->setShortCutKey(newShortCutKey); - m_macroWasModifiedFlag = true; - } - - const QString newDescription = m_macroDescriptionTextEdit->toPlainText().trimmed(); - if (m_macro->getDescription() != newDescription) { - m_macro->setDescription(newDescription); - m_macroWasModifiedFlag = true; - } - } - - QDialog::done(r); -} - -/** - * @return True if the macro was modified and may need to be saved, - * else false - */ -bool -WuQMacroEditorDialog::isMacroModified() const -{ - return m_macroWasModifiedFlag; -} - diff --git a/src/GuiQt/WuQMacroEditorDialog.h b/src/GuiQt/WuQMacroEditorDialog.h deleted file mode 100644 index 320e2af6f..000000000 --- a/src/GuiQt/WuQMacroEditorDialog.h +++ /dev/null @@ -1,100 +0,0 @@ -#ifndef __WU_Q_MACRO_EDITOR_DIALOG_H__ -#define __WU_Q_MACRO_EDITOR_DIALOG_H__ - -/*LICENSE_START*/ -/* - * Copyright (C) 2018 Washington University School of Medicine - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ -/*LICENSE_END*/ - - - -#include - -#include - -class QDialogButtonBox; -class QLineEdit; -class QPlainTextEdit; -class QTableWidget; - - -namespace caret { - - class WuQMacro; - class WuQMacroGroup; - class WuQMacroShortCutKeyComboBox; - - class WuQMacroEditorDialog : public QDialog { - - Q_OBJECT - - public: - WuQMacroEditorDialog(WuQMacro* macro, - QWidget* parent = 0); - - virtual ~WuQMacroEditorDialog(); - - WuQMacroEditorDialog(const WuQMacroEditorDialog&) = delete; - - WuQMacroEditorDialog& operator=(const WuQMacroEditorDialog&) = delete; - - bool isMacroModified() const; - - // ADD_NEW_METHODS_HERE - - public slots: - virtual void done(int r) override; - - private: - enum { - COLUMN_TYPE = 0, - COLUMN_NAME = 1, - COLUMN_VALUE_TYPE = 2, - COLUMN_VALUE = 3, - COLUMN_VALUE_TWO_TYPE = 4, - COLUMN_VALUE_TWO = 5, - COLUMN_TOOLTIP = 6, - NUMBER_OF_COLUMNS = 7 - }; - - QTableWidget* createTableWidget(); - - void loadTableWidget(); - - WuQMacro* m_macro; - - bool m_macroWasModifiedFlag = false; - - QLineEdit* m_macroNameLineEdit; - - WuQMacroShortCutKeyComboBox* m_macroShortCutKeyComboBox; - - QPlainTextEdit* m_macroDescriptionTextEdit; - - QTableWidget* m_tableWidget; - - QDialogButtonBox* m_dialogButtonBox; - - }; - -#ifdef __WU_Q_MACRO_EDITOR_DIALOG_DECLARE__ - // -#endif // __WU_Q_MACRO_EDITOR_DIALOG_DECLARE__ - -} // namespace -#endif //__WU_Q_MACRO_EDITOR_DIALOG_H__ diff --git a/src/GuiQt/WuQMacroManager.cxx b/src/GuiQt/WuQMacroManager.cxx index 4330311b1..2404f5003 100644 --- a/src/GuiQt/WuQMacroManager.cxx +++ b/src/GuiQt/WuQMacroManager.cxx @@ -46,7 +46,6 @@ #include "WuQMacroMouseEventInfo.h" #include "WuQMacroAttributesDialog.h" #include "WuQMacroHelperInterface.h" -#include "WuQMacroEditorDialog.h" #include "WuQMacroExecutorOptions.h" #include "WuQMacroSignalWatcher.h" @@ -721,37 +720,6 @@ WuQMacroManager::exportMacros(QWidget* parent, return false; } -/** - * Edit a macros commands - * - * @param parent - * Parent widget for dialog - * @param macro - * Macro to edit - * @return - * True if macro was modified. - */ -bool -WuQMacroManager::editMacroCommands(QWidget* parent, - WuQMacro* macro) -{ - if (macro != NULL) { - WuQMacroEditorDialog editorDialog(macro, - parent); - if (editorDialog.exec() == WuQMacroDialog::Accepted) { - if (editorDialog.isMacroModified()) { - if (m_macroHelper) { - m_macroHelper->macroWasModified(macro); - } - return true; - } - } - } - - return false; -} - - /** * Print supported widgets to the terminal window. */ diff --git a/src/GuiQt/WuQMacroManager.h b/src/GuiQt/WuQMacroManager.h index c41fa0343..9686b915f 100644 --- a/src/GuiQt/WuQMacroManager.h +++ b/src/GuiQt/WuQMacroManager.h @@ -98,9 +98,6 @@ namespace caret { WuQMacroGroup* macroGroup, WuQMacro* macro); - bool editMacroCommands(QWidget* parent, - WuQMacro* macro); - bool importMacros(QWidget* parent, WuQMacroGroup* appendToMacroGroup); diff --git a/src/GuiQt/WuQMacroSignalWatcher.cxx b/src/GuiQt/WuQMacroSignalWatcher.cxx index 42f746ccc..1126ce574 100644 --- a/src/GuiQt/WuQMacroSignalWatcher.cxx +++ b/src/GuiQt/WuQMacroSignalWatcher.cxx @@ -350,6 +350,7 @@ WuQMacroSignalWatcher::createAndSendMacroCommand(const QVariant& value, if (m_parentMacroManager->isModeRecording()) { WuQMacroCommand* mc = new WuQMacroCommand(m_objectType, m_objectName, + m_toolTipText, value, valueTwo); if ( ! m_parentMacroManager->addMacroCommandToRecording(mc)) { -- GitLab From 998874bb026c8ec47c66c187a1e4428c0d164d78 Mon Sep 17 00:00:00 2001 From: John Harwell Date: Tue, 8 Jan 2019 12:44:32 -0600 Subject: [PATCH 163/427] WB-834, Macros: For recording of mouse movement, each change of coordinate was in a separate 'macro command' resulting in a long list on the macros dialog. So, consolidate mouse movement into one command containing all mouse movement coordinates. Replaces many commands with just one mouse coordinate. --- src/Common/WuQMacro.cxx | 43 +++++++++++++-- src/Common/WuQMacro.h | 2 +- src/Common/WuQMacroCommand.cxx | 64 ++++++++++++++++++++++ src/Common/WuQMacroCommand.h | 4 ++ src/Common/WuQMacroGroupXmlReader.cxx | 77 +++++++++++++++++++-------- src/Common/WuQMacroGroupXmlWriter.cxx | 13 ++++- src/Common/WuQMacroMouseEventInfo.cxx | 71 ++++++++++++++++-------- src/Common/WuQMacroMouseEventInfo.h | 20 +++---- src/GuiQt/WuQMacroExecutor.cxx | 74 +++++++++++++------------ src/GuiQt/WuQMacroManager.cxx | 8 +-- 10 files changed, 279 insertions(+), 97 deletions(-) diff --git a/src/Common/WuQMacro.cxx b/src/Common/WuQMacro.cxx index 0e5675d2b..acfeb0dbf 100644 --- a/src/Common/WuQMacro.cxx +++ b/src/Common/WuQMacro.cxx @@ -27,6 +27,7 @@ #include "CaretAssert.h" #include "WuQMacroCommand.h" +#include "WuQMacroMouseEventInfo.h" #include "WuQMacroStandardItemTypeEnum.h" using namespace caret; @@ -125,7 +126,7 @@ WuQMacro::copyHelperWuQMacro(const WuQMacro& obj) CaretAssert(command); WuQMacroCommand* commandCopy = new WuQMacroCommand(*command); CaretAssert(commandCopy); - addMacroCommand(commandCopy); + appendMacroCommand(commandCopy); } setText(obj.text()); @@ -146,10 +147,46 @@ WuQMacro::type() const return WuQMacroStandardItemTypeEnum::toIntegerCode(WuQMacroStandardItemTypeEnum::MACRO); } +/** + * Append a macro command to this macro + * + * @param macroCommand + * The macro command. + */ void -WuQMacro::addMacroCommand(WuQMacroCommand* macroCommand) +WuQMacro::appendMacroCommand(WuQMacroCommand* macroCommand) { - appendRow(macroCommand); + CaretAssert(macroCommand); + const int32_t numCommands = getNumberOfMacroCommands(); + if (numCommands > 0) { + WuQMacroCommand* lastCommand = getMacroCommandAtIndex(numCommands - 1); + /* + * 'Compact' macro commands for mouse events + */ + if (lastCommand->isMouseEventMatch(macroCommand)) { + WuQMacroMouseEventInfo* lastMouseInfo = lastCommand->getMouseEventInfo(); + CaretAssert(lastMouseInfo); + + const WuQMacroMouseEventInfo* mouseInfo = macroCommand->getMouseEventInfo(); + CaretAssert(mouseInfo); + const int32_t numXY = mouseInfo->getNumberOfLocalXY(); + for (int32_t i = 0; i < numXY; i++) { + lastMouseInfo->addLocalXY(mouseInfo->getLocalX(i), + mouseInfo->getLocalY(i)); + } + + /* + * No longer needed since mouse x/y appended to last command + */ + delete macroCommand; + macroCommand = NULL; + } + } + + if (macroCommand != NULL) { + appendRow(macroCommand); + } + setModified(); } diff --git a/src/Common/WuQMacro.h b/src/Common/WuQMacro.h index 13cde6521..5ecd7eb2e 100644 --- a/src/Common/WuQMacro.h +++ b/src/Common/WuQMacro.h @@ -44,7 +44,7 @@ namespace caret { WuQMacro& operator=(const WuQMacro& obj); - void addMacroCommand(WuQMacroCommand* macroCommand); + void appendMacroCommand(WuQMacroCommand* macroCommand); int32_t getNumberOfMacroCommands() const; diff --git a/src/Common/WuQMacroCommand.cxx b/src/Common/WuQMacroCommand.cxx index 915814523..3a3674b73 100644 --- a/src/Common/WuQMacroCommand.cxx +++ b/src/Common/WuQMacroCommand.cxx @@ -417,6 +417,15 @@ WuQMacroCommand::getMouseEventInfo() const return m_macroMouseEvent; } +/** + * @return Point to mouse event information + */ +WuQMacroMouseEventInfo* +WuQMacroCommand::getMouseEventInfo() +{ + return m_macroMouseEvent; +} + /** * Set the mouse event info * @@ -432,8 +441,63 @@ WuQMacroCommand::setMouseEventInfo(WuQMacroMouseEventInfo* mouseEventInfo) } m_macroMouseEvent = mouseEventInfo; + + if (m_macroMouseEvent != NULL) { + QString title; + + switch (m_macroMouseEvent->getMouseEventType()) { + case WuQMacroMouseEventTypeEnum::BUTTON_PRESS: + title = "Mouse Press "; + break; + case WuQMacroMouseEventTypeEnum::BUTTON_RELEASE: + title = "Mouse Release "; + break; + case WuQMacroMouseEventTypeEnum::DOUBLE_CLICK: + title = "Mouse Double Click "; + break; + case WuQMacroMouseEventTypeEnum::MOVE: + title = "Mouse Move "; + break; + } + + title.append(m_objectName); + setText(title); + } +} + +/** + * @return True if this command the same mouse event type as the given command. + * Must be mouse move events only. + * + * @param command + * The other command + */ +bool +WuQMacroCommand::isMouseEventMatch(const WuQMacroCommand* command) const +{ + if (getClassType() == WuQMacroClassTypeEnum::MOUSE_USER_EVENT) { + if (command->getClassType() == WuQMacroClassTypeEnum::MOUSE_USER_EVENT) { + const WuQMacroMouseEventInfo* myMouse = getMouseEventInfo(); + CaretAssert(myMouse); + const WuQMacroMouseEventInfo* otherMouse = command->getMouseEventInfo(); + CaretAssert(otherMouse); + if ((myMouse->getMouseEventType() == WuQMacroMouseEventTypeEnum::MOVE) + && (myMouse->getMouseEventType() == WuQMacroMouseEventTypeEnum::MOVE)) { + if ((myMouse->getMouseButton() == otherMouse->getMouseButton()) + && (myMouse->getMouseButtonsMask() == otherMouse->getMouseButtonsMask()) + && (myMouse->getKeyboardModifiersMask() == otherMouse->getKeyboardModifiersMask()) + && (myMouse->getWidgetWidth() == otherMouse->getWidgetWidth()) + && (myMouse->getWidgetHeight() == otherMouse->getWidgetHeight())) { + return true; + } + } + } + } + + return false; } + /** * @return True if this instance is modified */ diff --git a/src/Common/WuQMacroCommand.h b/src/Common/WuQMacroCommand.h index 7020b87b5..871eb0448 100644 --- a/src/Common/WuQMacroCommand.h +++ b/src/Common/WuQMacroCommand.h @@ -76,10 +76,14 @@ namespace caret { QVariant getDataValueTwo() const; + WuQMacroMouseEventInfo* getMouseEventInfo(); + const WuQMacroMouseEventInfo* getMouseEventInfo() const; void setMouseEventInfo(WuQMacroMouseEventInfo* mouseEventInfo); + bool isMouseEventMatch(const WuQMacroCommand* command) const; + // ADD_NEW_METHODS_HERE virtual bool isModified() const override; diff --git a/src/Common/WuQMacroGroupXmlReader.cxx b/src/Common/WuQMacroGroupXmlReader.cxx index 6d4c4b3d6..7fa00443b 100644 --- a/src/Common/WuQMacroGroupXmlReader.cxx +++ b/src/Common/WuQMacroGroupXmlReader.cxx @@ -25,6 +25,7 @@ #include #include +#include #include "CaretAssert.h" #include "WuQMacro.h" @@ -155,10 +156,6 @@ WuQMacroGroupXmlReader::readVersionOne(WuQMacroGroup* macroGroup) if (elementName == ELEMENT_MACRO) { macro = readMacroVersionOne(); - if (macro != NULL) { - macroGroup->addMacro(macro); - } - macroCommand = NULL; } else if (elementName == ELEMENT_DESCRIPTION) { const QString text = m_xmlStreamReader->readElementText(); @@ -168,20 +165,29 @@ WuQMacroGroupXmlReader::readVersionOne(WuQMacroGroup* macroGroup) } else if (elementName == ELEMENT_MACRO_COMMAND) { macroCommand = readMacroCommandAttributesVersionOne(); - if (macroCommand != NULL) { - if (macro != NULL) { - macro->addMacroCommand(macroCommand); + } + else if (elementName == ELEMENT_MOUSE_EVENT_INFO) { + WuQMacroMouseEventInfo* mouseEventInfo = readMacroMouseEventInfo(); + if (mouseEventInfo != NULL) { + if (macroCommand != NULL) { + macroCommand->setMouseEventInfo(mouseEventInfo); } else { + addToWarnings("Read WuQMacroMouseEventInfo but macroCommand is NULL"); + delete mouseEventInfo; + mouseEventInfo = NULL; + } + } + else { + if (macroCommand != NULL) { + /* + * Command is invalid + */ delete macroCommand; - m_xmlStreamReader->skipCurrentElement(); + macroCommand = NULL; } } } - else if (elementName == ELEMENT_MOUSE_EVENT_INFO) { - WuQMacroMouseEventInfo* mouseEventInfo = readMacroMouseEventInfo(); - macroCommand->setMouseEventInfo(mouseEventInfo); - } else if (elementName == ELEMENT_TOOL_TIP) { const QString text = m_xmlStreamReader->readElementText(); if (macroCommand != NULL) { @@ -195,6 +201,22 @@ WuQMacroGroupXmlReader::readVersionOne(WuQMacroGroup* macroGroup) m_xmlStreamReader->skipCurrentElement(); } } + else if (m_xmlStreamReader->isEndElement()) { + const QString elementName = m_xmlStreamReader->name().toString(); + if (elementName == ELEMENT_MACRO) { + if (macro != NULL) { + macroGroup->addMacro(macro); + } + } + else if (elementName == ELEMENT_MACRO_COMMAND) { + if (macro != NULL) { + if (macroCommand != NULL) { + macro->appendMacroCommand(macroCommand); + } + } + macroCommand = NULL; + } + } if (m_xmlStreamReader->hasError()) { break; @@ -558,12 +580,6 @@ WuQMacroGroupXmlReader::readMacroCommandAttributesVersionOne() WuQMacroMouseEventInfo* WuQMacroGroupXmlReader::readMacroMouseEventInfo() { -// if ( ! m_xmlStreamReader->readNextStartElement()) { -// addToWarnings("Failed to read start element for element " -// + ELEMENT_MOUSE_EVENT_INFO); -// return NULL; -// } - const QXmlStreamAttributes attributes = m_xmlStreamReader->attributes(); const QString mouseEventTypeString = attributes.value(ATTRIBUTE_MOUSE_EVENT_TYPE).toString(); const QString xLocalString = attributes.value(ATTRIBUTE_MOUSE_LOCAL_X).toString(); @@ -576,8 +592,6 @@ WuQMacroGroupXmlReader::readMacroMouseEventInfo() QString es; if (mouseEventTypeString.isEmpty()) es.append(ATTRIBUTE_MOUSE_EVENT_TYPE + " "); - if (xLocalString.isEmpty()) es.append(ATTRIBUTE_MOUSE_LOCAL_X + " "); - if (yLocalString.isEmpty()) es.append(ATTRIBUTE_MOUSE_LOCAL_Y + " "); if (mouseButtonString.isEmpty()) es.append(ATTRIBUTE_MOUSE_BUTTON + " "); if (mouseButtonsMaskString.isEmpty()) es.append(ATTRIBUTE_MOUSE_BUTTONS_MASK + " "); if (keyboardModifiersMaskString.isEmpty()) es.append(ATTRIBUTE_MOUSE_KEYBOARD_MODIFIERS_MASK + " "); @@ -585,7 +599,7 @@ WuQMacroGroupXmlReader::readMacroMouseEventInfo() if (widgetHeightString.isEmpty()) es.append(ATTRIBUTE_MOUSE_WIDGET_HEIGHT + " "); if ( ! es.isEmpty()) { addToWarnings(ELEMENT_MOUSE_EVENT_INFO - + " is missing attribute(s): " + + " is missing required attribute(s): " + es); return NULL; } @@ -601,13 +615,30 @@ WuQMacroGroupXmlReader::readMacroMouseEventInfo() } WuQMacroMouseEventInfo* mouseInfo = new WuQMacroMouseEventInfo(mouseEventType, - xLocalString.toInt(), - yLocalString.toInt(), mouseButtonString.toUInt(), mouseButtonsMaskString.toUInt(), keyboardModifiersMaskString.toUInt(), widgetWidthString.toInt(), widgetHeightString.toInt()); + if (( ! xLocalString.isEmpty()) + && ( ! yLocalString.isEmpty())) { + mouseInfo->addLocalXY(xLocalString.toInt(), + yLocalString.toInt()); + } + + QString xyString = m_xmlStreamReader->readElementText(); + if ( ! xyString.isEmpty()) { + QTextStream stream(&xyString); + while ( ! stream.atEnd()) { + int32_t x, y; + stream >> x; + if ( ! stream.atEnd()) { + stream >> y; + mouseInfo->addLocalXY(x, y); + } + } + } + return mouseInfo; } diff --git a/src/Common/WuQMacroGroupXmlWriter.cxx b/src/Common/WuQMacroGroupXmlWriter.cxx index 41ad115d3..693823f8c 100644 --- a/src/Common/WuQMacroGroupXmlWriter.cxx +++ b/src/Common/WuQMacroGroupXmlWriter.cxx @@ -23,6 +23,7 @@ #include "WuQMacroGroupXmlWriter.h" #undef __WU_Q_MACRO_GROUP_XML_WRITER_DECLARE__ +#include #include #include "CaretAssert.h" @@ -259,11 +260,19 @@ WuQMacroGroupXmlWriter::writeMacroMouseEventInfo(const WuQMacroMouseEventInfo* m m_xmlStreamWriter->writeAttribute(ATTRIBUTE_MOUSE_BUTTONS_MASK, QString::number(mouseEventInfo->getMouseButtonsMask())); m_xmlStreamWriter->writeAttribute(ATTRIBUTE_MOUSE_EVENT_TYPE, WuQMacroMouseEventTypeEnum::toName(mouseEventInfo->getMouseEventType())); m_xmlStreamWriter->writeAttribute(ATTRIBUTE_MOUSE_KEYBOARD_MODIFIERS_MASK, QString::number(mouseEventInfo->getKeyboardModifiersMask())); - m_xmlStreamWriter->writeAttribute(ATTRIBUTE_MOUSE_LOCAL_X, QString::number(mouseEventInfo->getLocalX())); - m_xmlStreamWriter->writeAttribute(ATTRIBUTE_MOUSE_LOCAL_Y, QString::number(mouseEventInfo->getLocalY())); m_xmlStreamWriter->writeAttribute(ATTRIBUTE_MOUSE_WIDGET_WIDTH, QString::number(mouseEventInfo->getWidgetWidth())); m_xmlStreamWriter->writeAttribute(ATTRIBUTE_MOUSE_WIDGET_HEIGHT, QString::number(mouseEventInfo->getWidgetHeight())); + QString xyString; + QTextStream textStream(&xyString); + const int32_t numXY = mouseEventInfo->getNumberOfLocalXY(); + for (int32_t i = 0; i < numXY; i++) { + textStream << mouseEventInfo->getLocalX(i) << " " + << mouseEventInfo->getLocalY(i) << " "; + } + + m_xmlStreamWriter->writeCharacters(xyString); + m_xmlStreamWriter->writeEndElement(); } diff --git a/src/Common/WuQMacroMouseEventInfo.cxx b/src/Common/WuQMacroMouseEventInfo.cxx index a98981ce7..99e021a53 100644 --- a/src/Common/WuQMacroMouseEventInfo.cxx +++ b/src/Common/WuQMacroMouseEventInfo.cxx @@ -38,10 +38,6 @@ using namespace caret; * Constructor. * @param mouseEventType * Type of mouse event - * @param localX - * Position of mouse relative to widget - * @param localY - * Position of mouse relative to widget * @param mouseButton * Button that caused the event * @param mouseButtonsMask @@ -54,8 +50,6 @@ using namespace caret; * Width of widget where mouse event occurred */ WuQMacroMouseEventInfo::WuQMacroMouseEventInfo(const WuQMacroMouseEventTypeEnum::Enum mouseEventType, - const int32_t localX, - const int32_t localY, const uint32_t mouseButton, const uint32_t mouseButtonsMask, const uint32_t keyboardModifiersMask, @@ -63,15 +57,12 @@ WuQMacroMouseEventInfo::WuQMacroMouseEventInfo(const WuQMacroMouseEventTypeEnum: const int32_t widgetHeight) : CaretObject(), m_mouseEventType(mouseEventType), -m_localX(localX), -m_localY(localY), m_mouseButton(mouseButton), m_mouseButtonsMask(mouseButtonsMask), m_keyboardModifiersMask(keyboardModifiersMask), m_widgetWidth(widgetWidth), m_widgetHeight(widgetHeight) { - } /** @@ -118,8 +109,7 @@ void WuQMacroMouseEventInfo::copyHelperWuQMacroMouseEventInfo(const WuQMacroMouseEventInfo& obj) { m_mouseEventType = obj.m_mouseEventType; - m_localX = obj.m_localX; - m_localY = obj.m_localY; + m_localXY = obj.m_localXY; m_mouseButton = obj.m_mouseButton; m_mouseButtonsMask = obj.m_mouseButtonsMask; m_keyboardModifiersMask = obj.m_keyboardModifiersMask; @@ -134,6 +124,10 @@ WuQMacroMouseEventInfo::copyHelperWuQMacroMouseEventInfo(const WuQMacroMouseEven * Current width of widget * @param newHeight * Current height of widget + * @param localX + * Local X position + * @param localY + * Local Y position * @param xLocalOut * Output with adjusted local-X position * @param yLocalOut @@ -142,17 +136,19 @@ WuQMacroMouseEventInfo::copyHelperWuQMacroMouseEventInfo(const WuQMacroMouseEven void WuQMacroMouseEventInfo::getLocalPositionRescaledToWidgetSize(const int32_t newWidth, const int32_t newHeight, + const int32_t localX, + const int32_t localY, int32_t& xLocalOut, int32_t& yLocalOut) const { - xLocalOut = m_localX; - yLocalOut = m_localY; + xLocalOut = localX; + yLocalOut = localY; if ((newWidth != m_widgetWidth) || (newHeight != m_widgetHeight)) { - const float normalizedWidth = (static_cast(m_localX) + const float normalizedWidth = (static_cast(localX) / static_cast(m_widgetWidth)); - const float normalizedHeight = (static_cast(m_localY) + const float normalizedHeight = (static_cast(localY) / static_cast(m_widgetHeight)); xLocalOut = (newWidth * normalizedWidth); @@ -170,21 +166,54 @@ WuQMacroMouseEventInfo::getMouseEventType() const } /** - * @return Position of mouse relative to widget + * Append local mouse X/Y coordinates + * + * @param localX + * The local X-coordinate + * @param localY + * The local Y-coordinate */ +void +WuQMacroMouseEventInfo::addLocalXY(const int32_t localX, + const int32_t localY) +{ + m_localXY.push_back(localX); + m_localXY.push_back(localY); +} + int32_t -WuQMacroMouseEventInfo::getLocalX() const +WuQMacroMouseEventInfo::getNumberOfLocalXY() const { - return m_localX; + const int32_t num = (m_localXY.size() / 2); + return num; +} + +/** + * @return X-coordinate of mouse relative to widget at the given index + * + * @param index + * Index of the coordinate + */ +int32_t +WuQMacroMouseEventInfo::getLocalX(const int32_t index) const +{ + const int32_t offset = (index * 2); + CaretAssertVectorIndex(m_localXY, offset); + return m_localXY[offset]; } /** - * @return Position of mouse relative to widget + * @return Y-coordinate of mouse relative to widget at the given index + * + * @param index + * Index of the coordinate */ int32_t -WuQMacroMouseEventInfo::getLocalY() const +WuQMacroMouseEventInfo::getLocalY(const int32_t index) const { - return m_localY; + const int32_t offset = (index * 2) + 1; + CaretAssertVectorIndex(m_localXY, offset); + return m_localXY[offset]; } /** diff --git a/src/Common/WuQMacroMouseEventInfo.h b/src/Common/WuQMacroMouseEventInfo.h index 9d9026e8b..d381c9e0b 100644 --- a/src/Common/WuQMacroMouseEventInfo.h +++ b/src/Common/WuQMacroMouseEventInfo.h @@ -35,8 +35,6 @@ namespace caret { public: WuQMacroMouseEventInfo(const WuQMacroMouseEventTypeEnum::Enum mouseEventType, - const int32_t localX, - const int32_t localY, const uint32_t mouseButton, const uint32_t mouseButtonsMask, const uint32_t keyboardModifiersMask, @@ -51,14 +49,21 @@ namespace caret { void getLocalPositionRescaledToWidgetSize(const int32_t widgetWidth, const int32_t widgetHeight, + const int32_t localX, + const int32_t localY, int32_t& xOut, int32_t& yOut) const; WuQMacroMouseEventTypeEnum::Enum getMouseEventType() const; - int32_t getLocalX() const; + void addLocalXY(const int32_t localX, + const int32_t localY); - int32_t getLocalY() const; + int32_t getNumberOfLocalXY() const; + + int32_t getLocalX(const int32_t index) const; + + int32_t getLocalY(const int32_t index) const; uint32_t getMouseButton() const; @@ -80,11 +85,8 @@ namespace caret { /** Type of mouse event */ WuQMacroMouseEventTypeEnum::Enum m_mouseEventType; - /** Position of mouse relative to widget */ - int32_t m_localX; - - /** Position of mouse relative to widget */ - int32_t m_localY; + /** Positions of mouse relative to widget */ + std::vector m_localXY; /** Button that caused the event */ uint32_t m_mouseButton; diff --git a/src/GuiQt/WuQMacroExecutor.cxx b/src/GuiQt/WuQMacroExecutor.cxx index 0c4cc5770..8e51652a7 100644 --- a/src/GuiQt/WuQMacroExecutor.cxx +++ b/src/GuiQt/WuQMacroExecutor.cxx @@ -924,41 +924,47 @@ WuQMacroExecutor::runMouseCommand(const WuQMacroCommand* macroCommand, const WuQMacroMouseEventInfo* mouseEventInfo = macroCommand->getMouseEventInfo(); CaretAssert(mouseEventInfo); - int32_t adjustedLocalX(0); - int32_t adjustedLocalY(0); - mouseEventInfo->getLocalPositionRescaledToWidgetSize(currentWidgetSize.width(), - currentWidgetSize.height(), - adjustedLocalX, - adjustedLocalY); - - QEvent::Type qtEventType = QEvent::None; - switch (mouseEventInfo->getMouseEventType()) { - case WuQMacroMouseEventTypeEnum::BUTTON_PRESS: - qtEventType = QEvent::MouseButtonPress; - break; - case WuQMacroMouseEventTypeEnum::BUTTON_RELEASE: - qtEventType = QEvent::MouseButtonRelease; - break; - case WuQMacroMouseEventTypeEnum::DOUBLE_CLICK: - qtEventType = QEvent::MouseButtonDblClick; - break; - case WuQMacroMouseEventTypeEnum::MOVE: - qtEventType = QEvent::MouseMove; - break; + const int32_t numXY = mouseEventInfo->getNumberOfLocalXY(); + for (int32_t i = 0; i < numXY; i++) { + int32_t adjustedLocalX(0); + int32_t adjustedLocalY(0); + mouseEventInfo->getLocalPositionRescaledToWidgetSize(currentWidgetSize.width(), + currentWidgetSize.height(), + mouseEventInfo->getLocalX(i), + mouseEventInfo->getLocalY(i), + adjustedLocalX, + adjustedLocalY); + + QEvent::Type qtEventType = QEvent::None; + switch (mouseEventInfo->getMouseEventType()) { + case WuQMacroMouseEventTypeEnum::BUTTON_PRESS: + qtEventType = QEvent::MouseButtonPress; + break; + case WuQMacroMouseEventTypeEnum::BUTTON_RELEASE: + qtEventType = QEvent::MouseButtonRelease; + break; + case WuQMacroMouseEventTypeEnum::DOUBLE_CLICK: + qtEventType = QEvent::MouseButtonDblClick; + break; + case WuQMacroMouseEventTypeEnum::MOVE: + qtEventType = QEvent::MouseMove; + break; + } + + moveMouseToWidgetXY(widget, + adjustedLocalX, + adjustedLocalY, + false); + + QMouseEvent qtMouseEvent(qtEventType, + QPointF(adjustedLocalX, + adjustedLocalY), + static_cast(mouseEventInfo->getMouseButton()), + static_cast(mouseEventInfo->getMouseButtonsMask()), + static_cast(mouseEventInfo->getKeyboardModifiersMask())); + mouseInterface->processMouseEventFromMacro(&qtMouseEvent); + QGuiApplication::processEvents(); } - - moveMouseToWidgetXY(widget, - adjustedLocalX, - adjustedLocalY, - false); - - QMouseEvent qtMouseEvent(qtEventType, - QPointF(adjustedLocalX, - adjustedLocalY), - static_cast(mouseEventInfo->getMouseButton()), - static_cast(mouseEventInfo->getMouseButtonsMask()), - static_cast(mouseEventInfo->getKeyboardModifiersMask())); - mouseInterface->processMouseEventFromMacro(&qtMouseEvent); } else { errorMessageOut = ("ERROR: Unable to cast object named " diff --git a/src/GuiQt/WuQMacroManager.cxx b/src/GuiQt/WuQMacroManager.cxx index 2404f5003..62b05f54f 100644 --- a/src/GuiQt/WuQMacroManager.cxx +++ b/src/GuiQt/WuQMacroManager.cxx @@ -337,7 +337,7 @@ WuQMacroManager::addMacroCommandToRecording(WuQMacroCommand* macroCommand) if (isModeRecording()) { CaretAssert(m_macroBeingRecorded); - m_macroBeingRecorded->addMacroCommand(macroCommand); + m_macroBeingRecorded->appendMacroCommand(macroCommand); return true; } @@ -401,15 +401,15 @@ WuQMacroManager::addMouseEventToRecording(QWidget* widget, } if (validMouseEventFlag) { WuQMacroMouseEventInfo* mouseInfo = new WuQMacroMouseEventInfo(mouseEventType, - me->localPos().x(), - me->localPos().y(), static_cast(me->button()), static_cast(me->buttons()), static_cast(me->modifiers()), widget->width(), widget->height()); + mouseInfo->addLocalXY(me->localPos().x(), + me->localPos().y()); - m_macroBeingRecorded->addMacroCommand(new WuQMacroCommand(name, + m_macroBeingRecorded->appendMacroCommand(new WuQMacroCommand(name, mouseInfo)); return true; -- GitLab From f1524993e9f02c5ddd8a15fef7e9857107a111ef Mon Sep 17 00:00:00 2001 From: John Harwell Date: Tue, 8 Jan 2019 14:55:07 -0600 Subject: [PATCH 164/427] WB-834 Macros: Do no allow running of a macro if a macro is currently being recorded. Display 'Recording' in red in ToolBar:Tab section while recording a macro. Cleanup of some macro files. --- src/Common/WuQMacroFile.cxx | 43 ------ src/Common/WuQMacroFile.h | 6 - src/Common/WuQMacroGroupXmlReader.cxx | 151 --------------------- src/GuiQt/BrainBrowserWindowToolBarTab.cxx | 11 ++ src/GuiQt/BrainBrowserWindowToolBarTab.h | 2 + src/GuiQt/WuQMacroDialog.cxx | 9 ++ src/GuiQt/WuQMacroManager.cxx | 56 +------- 7 files changed, 26 insertions(+), 252 deletions(-) diff --git a/src/Common/WuQMacroFile.cxx b/src/Common/WuQMacroFile.cxx index 145e9d713..061358263 100644 --- a/src/Common/WuQMacroFile.cxx +++ b/src/Common/WuQMacroFile.cxx @@ -61,47 +61,6 @@ WuQMacroFile::~WuQMacroFile() { } -///** -// * Copy constructor. -// * @param obj -// * Object that is copied. -// */ -//WuQMacroFile::WuQMacroFile(const WuQMacroFile& obj) -//: DataFile(obj) -//{ -// this->copyHelperWuQMacroFile(obj); -//} -// -///** -// * Assignment operator. -// * @param obj -// * Data copied from obj to this. -// * @return -// * Reference to this object. -// */ -//WuQMacroFile& -//WuQMacroFile::operator=(const WuQMacroFile& obj) -//{ -// if (this != &obj) { -// DataFile::operator=(obj); -// this->copyHelperWuQMacroFile(obj); -// } -// return *this; -//} -// -///** -// * Helps with copying an object of this type. -// * @param obj -// * Object that is copied. -// */ -//void -//WuQMacroFile::copyHelperWuQMacroFile(const WuQMacroFile& obj) -//{ -// *m_macroGroup = *obj.m_macroGroup; -// -// setMacroGroupName(getFileName()); -//} - /** * @return True if this file is empty (has no macros) */ @@ -325,8 +284,6 @@ WuQMacroFile::writeFile(const AString& filename) throw DataFileException("Unable to open file for writing: " + filename); } - - } diff --git a/src/Common/WuQMacroFile.h b/src/Common/WuQMacroFile.h index 34ad04a19..ca04944d5 100644 --- a/src/Common/WuQMacroFile.h +++ b/src/Common/WuQMacroFile.h @@ -41,10 +41,6 @@ namespace caret { virtual ~WuQMacroFile(); -// WuQMacroFile(const WuQMacroFile& obj); -// -// WuQMacroFile& operator=(const WuQMacroFile& obj); - virtual bool isEmpty() const override; WuQMacroGroup* getMacroGroup(); @@ -72,8 +68,6 @@ namespace caret { // ADD_NEW_METHODS_HERE private: -// void copyHelperWuQMacroFile(const WuQMacroFile& obj); - void setMacroGroupName(const QString& filename); std::unique_ptr m_macroGroup; diff --git a/src/Common/WuQMacroGroupXmlReader.cxx b/src/Common/WuQMacroGroupXmlReader.cxx index 7fa00443b..c10f43776 100644 --- a/src/Common/WuQMacroGroupXmlReader.cxx +++ b/src/Common/WuQMacroGroupXmlReader.cxx @@ -303,7 +303,6 @@ WuQMacroGroupXmlReader::readMacroCommandAttributesVersionOne() if (dataTypeString.isEmpty()) es.append(ATTRIBUTE_OBJECT_DATA_TYPE + " "); if (dataTypeTwoString.isEmpty()) es.append(ATTRIBUTE_OBJECT_DATA_TYPE + " "); if (classString.isEmpty()) es.append(ATTRIBUTE_OBJECT_CLASS + " "); - // empty could be valid for line edit if (valueString.isEmpty()) es.append(ATTRIBUTE_OBJECT_VALUE + " "); if ( ! es.isEmpty()) { addToWarnings(ELEMENT_MACRO_COMMAND + " is missing attribute(s): " @@ -424,156 +423,6 @@ WuQMacroGroupXmlReader::readMacroCommandAttributesVersionOne() return macroCommand; } - -///** -// * Read version one of macro command -// * -// * @Return The macro group -// */ -//WuQMacroCommand* -//WuQMacroGroupXmlReader::readMacroCommandVersionOne() -//{ -// WuQMacroCommand* macroCommand(NULL); -// -// const QXmlStreamAttributes attributes = m_xmlStreamReader->attributes(); -// const QStringRef objectName = attributes.value(ATTRIBUTE_NAME); -// const QStringRef dataTypeString = attributes.value(ATTRIBUTE_OBJECT_DATA_TYPE); -// const QStringRef classString = attributes.value(ATTRIBUTE_OBJECT_CLASS); -// const QStringRef valueString = attributes.value(ATTRIBUTE_OBJECT_VALUE); -// const QStringRef dataTypeTwoString = attributes.value(ATTRIBUTE_OBJECT_DATA_TYPE_TWO); -// const QStringRef valueTwoString = attributes.value(ATTRIBUTE_OBJECT_VALUE_TWO); -// -// QString es; -// if (objectName.isEmpty()) es.append(ATTRIBUTE_NAME + " "); -// if (dataTypeString.isEmpty()) es.append(ATTRIBUTE_OBJECT_DATA_TYPE + " "); -// if (dataTypeTwoString.isEmpty()) es.append(ATTRIBUTE_OBJECT_DATA_TYPE + " "); -// if (classString.isEmpty()) es.append(ATTRIBUTE_OBJECT_CLASS + " "); -//// empty could be valid for line edit if (valueString.isEmpty()) es.append(ATTRIBUTE_OBJECT_VALUE + " "); -// if ( ! es.isEmpty()) { -// addToWarnings(ELEMENT_MACRO_COMMAND -// + " is missing attribute(s): " -// + es); -// return NULL; -// } -// -// -// bool objectTypeValid(false); -// WuQMacroClassTypeEnum::Enum objectClass = WuQMacroClassTypeEnum::fromName(classString.toString(), -// &objectTypeValid); -// if (! objectTypeValid) { -// es.append(classString.toString() -// + " is not valid for attribute " -// + ATTRIBUTE_OBJECT_CLASS -// + " "); -// return NULL; -// } -// -// bool dataTypeValid(false); -// WuQMacroDataValueTypeEnum::Enum dataValueType = WuQMacroDataValueTypeEnum::fromName(dataTypeString.toString(), -// &dataTypeValid); -// if (! dataTypeValid) { -// es.append(dataTypeString.toString() -// + " is not valid for attribute " -// + ATTRIBUTE_OBJECT_DATA_TYPE -// + " "); -// return NULL; -// } -// -// bool dataTypeTwoValid(false); -// WuQMacroDataValueTypeEnum::Enum dataValueTypeTwo = WuQMacroDataValueTypeEnum::fromName(dataTypeTwoString.toString(), -// &dataTypeTwoValid); -// if (! dataTypeTwoValid) { -// es.append(dataTypeTwoString.toString() -// + " is not valid for attribute " -// + ATTRIBUTE_OBJECT_DATA_TYPE_TWO -// + " "); -// return NULL; -// } -// -// -// if ( ! es.isEmpty()) { -// addToWarnings(ELEMENT_MACRO_COMMAND -// + " has invalid attributes: " -// + es); -// return NULL; -// } -// -// if (objectClass == WuQMacroClassTypeEnum::MOUSE_USER_EVENT) { -// WuQMacroMouseEventInfo* mouseEventInfo = readMacroMouseEventInfo(); -// if (mouseEventInfo != NULL) { -// macroCommand = new WuQMacroCommand(objectName.toString(), -// mouseEventInfo); -// } -// } -// else { -// QVariant value; -// switch (dataValueType) { -// case WuQMacroDataValueTypeEnum::BOOLEAN: -// { -// const bool boolValue = ((valueString == VALUE_BOOL_TRUE) ? true : false); -// value.setValue(boolValue); -// } -// break; -// case WuQMacroDataValueTypeEnum::FLOAT: -// { -// const double floatValue = valueString.toFloat(); -// value.setValue(floatValue); -// } -// break; -// case WuQMacroDataValueTypeEnum::INTEGER: -// { -// const int32_t intValue = valueString.toInt(); -// value.setValue(intValue); -// } -// break; -// case WuQMacroDataValueTypeEnum::MOUSE: -// CaretAssertMessage(0, "Mouse is special case handled above"); -// break; -// case WuQMacroDataValueTypeEnum::STRING: -// value.setValue(valueString.toString()); -// break; -// } -// -// QVariant valueTwo; -// switch (dataValueTypeTwo) { -// case WuQMacroDataValueTypeEnum::BOOLEAN: -// { -// const bool boolValue = ((valueTwoString == VALUE_BOOL_TRUE) ? true : false); -// valueTwo.setValue(boolValue); -// } -// break; -// case WuQMacroDataValueTypeEnum::FLOAT: -// { -// const double floatValue = valueTwoString.toFloat(); -// valueTwo.setValue(floatValue); -// } -// break; -// case WuQMacroDataValueTypeEnum::INTEGER: -// { -// const int32_t intValue = valueTwoString.toInt(); -// valueTwo.setValue(intValue); -// } -// break; -// case WuQMacroDataValueTypeEnum::MOUSE: -// CaretAssertMessage(0, "Mouse is special case handled above"); -// break; -// case WuQMacroDataValueTypeEnum::STRING: -// valueTwo.setValue(valueTwoString.toString()); -// break; -// } -// -// QString tooltip; -// CaretAssertToDoWarning(); -// macroCommand = new WuQMacroCommand(objectClass, -// objectName.toString(), -// tooltip, -// value, -// valueTwo); -// } -// -// return macroCommand; -//} - /** * @return Read and return the mouse event information */ diff --git a/src/GuiQt/BrainBrowserWindowToolBarTab.cxx b/src/GuiQt/BrainBrowserWindowToolBarTab.cxx index 2d70ece69..1ca249b3c 100644 --- a/src/GuiQt/BrainBrowserWindowToolBarTab.cxx +++ b/src/GuiQt/BrainBrowserWindowToolBarTab.cxx @@ -100,16 +100,20 @@ m_lockWindowAndAllTabAspectButton(toolBarLockWindowAndAllTabAspectRatioButton) + ":Tab:EnableShading"); WuQMacroManager::instance()->addMacroSupportToObject(m_lightingEnabledCheckBox); + m_macroRecordingLabel = new QLabel(""); + QVBoxLayout* layout = new QVBoxLayout(this); WuQtUtilities::setLayoutSpacingAndMargins(layout, 4, 0); layout->addWidget(m_yokeToLabel); layout->addWidget(m_yokingGroupComboBox->getWidget()); layout->addWidget(m_lockWindowAndAllTabAspectButton); layout->addWidget(m_lightingEnabledCheckBox); + layout->addWidget(m_macroRecordingLabel); addToWidgetGroup(m_yokeToLabel); addToWidgetGroup(m_yokingGroupComboBox->getWidget()); addToWidgetGroup(m_lightingEnabledCheckBox); + addToWidgetGroup(m_macroRecordingLabel); } /** @@ -165,6 +169,13 @@ BrainBrowserWindowToolBarTab::updateContent(BrowserTabContent* browserTabContent m_lightingEnabledCheckBox->setChecked(browserTabContent->isLightingEnabled()); + if (WuQMacroManager::instance()->isModeRecording()) { + m_macroRecordingLabel->setText("Recording"); + } + else { + m_macroRecordingLabel->setText(""); + } + blockAllSignals(false); } diff --git a/src/GuiQt/BrainBrowserWindowToolBarTab.h b/src/GuiQt/BrainBrowserWindowToolBarTab.h index dbed671b0..9d5c2f37c 100644 --- a/src/GuiQt/BrainBrowserWindowToolBarTab.h +++ b/src/GuiQt/BrainBrowserWindowToolBarTab.h @@ -71,6 +71,8 @@ namespace caret { QCheckBox* m_lightingEnabledCheckBox; + QLabel* m_macroRecordingLabel; + // ADD_NEW_MEMBERS_HERE }; diff --git a/src/GuiQt/WuQMacroDialog.cxx b/src/GuiQt/WuQMacroDialog.cxx index 64f3e2267..4a4ac0dcb 100644 --- a/src/GuiQt/WuQMacroDialog.cxx +++ b/src/GuiQt/WuQMacroDialog.cxx @@ -553,6 +553,15 @@ WuQMacroDialog::getSelectedMacro() void WuQMacroDialog::runSelectedMacro() { + if (WuQMacroManager::instance()->isModeRecording()) { + QMessageBox::critical(m_runButton, + "Error", + "A macro is being recorded. Finish recording of macro.", + QMessageBox::Ok, + QMessageBox::NoButton); + return; + } + WuQMacro* macro = getSelectedMacro(); if (macro != NULL) { if (macro->getNumberOfMacroCommands() <= 0) { diff --git a/src/GuiQt/WuQMacroManager.cxx b/src/GuiQt/WuQMacroManager.cxx index 62b05f54f..c42e4d339 100644 --- a/src/GuiQt/WuQMacroManager.cxx +++ b/src/GuiQt/WuQMacroManager.cxx @@ -36,6 +36,8 @@ #include "CaretLogger.h" #include "CaretFileDialog.h" #include "DataFileException.h" +#include "EventManager.h" +#include "EventUserInterfaceUpdate.h" #include "WuQMacro.h" #include "WuQMacroCommand.h" #include "WuQMacroCreateDialog.h" @@ -264,58 +266,6 @@ WuQMacroManager::addMacroSupportToObject(QObject* object) } return resultFlag; - -// const QString name = object->objectName(); -// if (name.isEmpty()) { -// CaretLogSevere("Object name is empty, will be ignored for macros\n" -// + SystemUtilities::getBackTrace()); -// return false; -// } -// -// -// { -// QString toolTipText; -// if (toolTipTextOverride.isEmpty()) { -// QAction* action = qobject_cast(object); -// if (action != NULL) { -// toolTipText = action->toolTip(); -// } -// QWidget* widget = qobject_cast(object); -// if (widget != NULL) { -// toolTipText = widget->toolTip(); -// } -// if (toolTipText.isEmpty()) { -// CaretLogWarning("Object named \"" -// + name -// + "\" is missing a tooltip"); -// } -// } -// else { -// toolTipText = toolTipTextOverride; -// } -// } -// auto existingWatcher = m_signalWatchers.find(name); -// if (existingWatcher != m_signalWatchers.end()) { -// CaretLogSevere("Object named \"" -// + name -// + "\" has already been connected for macros\n" -// + SystemUtilities::getBackTrace() -// + "\n"); -// } -// else { -// AString errorMessage; -// WuQMacroSignalWatcher* widgetWatcher = WuQMacroSignalWatcher::newInstance(this, -// object, -// errorMessage); -// if (widgetWatcher != NULL) { -// widgetWatcher->setParent(this); -// m_signalWatchers.insert(std::make_pair(name, -// widgetWatcher)); -// } -// else { -// CaretLogWarning(errorMessage); -// } -// } } /** @@ -449,6 +399,8 @@ WuQMacroManager::startRecordingNewMacro(QWidget* parent) m_mode = WuQMacroModeEnum::RECORDING; m_macroBeingRecorded = createMacroDialog.getNewMacro(); CaretAssert(m_macroBeingRecorded); + + EventManager::get()->sendEvent(EventUserInterfaceUpdate().getPointer()); } } -- GitLab From 1df4ff0503eb4a5f0fb5c3a6d561614206dc3589 Mon Sep 17 00:00:00 2001 From: John Harwell Date: Wed, 9 Jan 2019 14:43:43 -0600 Subject: [PATCH 165/427] WB-834 Macros: Display more information for a macro's command and allow editing in the Macro Dialog. --- src/Common/WuQMacroCommand.cxx | 109 +++-- src/Common/WuQMacroCommand.h | 8 + src/Common/WuQMacroDataValueTypeEnum.cxx | 16 +- src/Common/WuQMacroDataValueTypeEnum.h | 12 +- src/Common/WuQMacroGroupXmlReader.cxx | 6 + src/Common/WuQMacroGroupXmlWriter.cxx | 4 + src/Common/WuQMacroStandardItemTypeEnum.cxx | 25 +- src/Common/WuQMacroStandardItemTypeEnum.h | 2 + src/GuiQt/WuQMacroDialog.cxx | 429 +++++++++++++++++--- src/GuiQt/WuQMacroDialog.h | 30 ++ src/GuiQt/WuQMacroExecutor.cxx | 91 ++--- src/GuiQt/WuQMacroManager.cxx | 19 +- src/GuiQt/WuQMacroManager.h | 2 + src/GuiQt/WuQMacroSignalWatcher.cxx | 28 +- 14 files changed, 619 insertions(+), 162 deletions(-) diff --git a/src/Common/WuQMacroCommand.cxx b/src/Common/WuQMacroCommand.cxx index 3a3674b73..1a8956311 100644 --- a/src/Common/WuQMacroCommand.cxx +++ b/src/Common/WuQMacroCommand.cxx @@ -87,7 +87,8 @@ m_macroMouseEvent(NULL) /* * Second data value is used by only some commands */ - m_dataTypeTwo = WuQMacroDataValueTypeEnum::STRING; + m_dataType = WuQMacroDataValueTypeEnum::INVALID; + m_dataTypeTwo = WuQMacroDataValueTypeEnum::INVALID; QString title = "Unknown"; @@ -98,22 +99,24 @@ m_macroMouseEvent(NULL) + QString((m_dataValue.toBool() ? "On" : "Off"))); break; case WuQMacroClassTypeEnum::ACTION_GROUP: - m_dataType = WuQMacroDataValueTypeEnum::INTEGER; - m_dataTypeTwo = WuQMacroDataValueTypeEnum::STRING; + m_dataType = WuQMacroDataValueTypeEnum::STRING; + m_dataTypeTwo = WuQMacroDataValueTypeEnum::INTEGER; + m_dataValueTwoValidFlag = true; title = ("Select Name " - + m_dataValueTwo.toString() + + m_dataValue.toString() + " else " + " index " - + QString::number(m_dataValue.toInt())); + + QString::number(m_dataValueTwo.toInt())); break; case WuQMacroClassTypeEnum::BUTTON_GROUP: - m_dataType = WuQMacroDataValueTypeEnum::INTEGER; - m_dataTypeTwo = WuQMacroDataValueTypeEnum::STRING; + m_dataType = WuQMacroDataValueTypeEnum::STRING; + m_dataTypeTwo = WuQMacroDataValueTypeEnum::INTEGER; + m_dataValueTwoValidFlag = true; title = ("Select Name " - + m_dataValueTwo.toString() + + m_dataValue.toString() + " else " + " index " - + QString::number(m_dataValue.toInt())); + + QString::number(m_dataValueTwo.toInt())); break; case WuQMacroClassTypeEnum::CHECK_BOX: m_dataType = WuQMacroDataValueTypeEnum::BOOLEAN; @@ -121,13 +124,14 @@ m_macroMouseEvent(NULL) + QString((m_dataValue.toBool() ? "On" : "Off"))); break; case WuQMacroClassTypeEnum::COMBO_BOX: - m_dataType = WuQMacroDataValueTypeEnum::INTEGER; - m_dataTypeTwo = WuQMacroDataValueTypeEnum::STRING; + m_dataType = WuQMacroDataValueTypeEnum::STRING; + m_dataTypeTwo = WuQMacroDataValueTypeEnum::INTEGER; + m_dataValueTwoValidFlag = true; title = ("Select Name " - + m_dataValueTwo.toString() + + m_dataValue.toString() + " else " + " index " - + QString::number(m_dataValue.toInt())); + + QString::number(m_dataValueTwo.toInt())); break; case WuQMacroClassTypeEnum::DOUBLE_SPIN_BOX: m_dataType = WuQMacroDataValueTypeEnum::FLOAT; @@ -143,22 +147,24 @@ m_macroMouseEvent(NULL) + m_dataValue.toString()); break; case WuQMacroClassTypeEnum::LIST_WIDGET: - m_dataType = WuQMacroDataValueTypeEnum::INTEGER; - m_dataTypeTwo = WuQMacroDataValueTypeEnum::STRING; + m_dataType = WuQMacroDataValueTypeEnum::STRING; + m_dataTypeTwo = WuQMacroDataValueTypeEnum::INTEGER; + m_dataValueTwoValidFlag = true; title = ("Select Name " - + m_dataValueTwo.toString() + + m_dataValue.toString() + " else " + " index " - + QString::number(m_dataValue.toInt())); + + QString::number(m_dataValueTwo.toInt())); break; case WuQMacroClassTypeEnum::MENU: - m_dataType = WuQMacroDataValueTypeEnum::INTEGER; - m_dataTypeTwo = WuQMacroDataValueTypeEnum::STRING; + m_dataType = WuQMacroDataValueTypeEnum::STRING; + m_dataTypeTwo = WuQMacroDataValueTypeEnum::INTEGER; + m_dataValueTwoValidFlag = true; title = ("Select Name " - + m_dataValueTwo.toString() + + m_dataValue.toString() + " else " + " index " - + QString::number(m_dataValue.toInt())); + + QString::number(m_dataValueTwo.toInt())); break; case WuQMacroClassTypeEnum::MOUSE_USER_EVENT: m_dataType = WuQMacroDataValueTypeEnum::MOUSE; @@ -184,22 +190,24 @@ m_macroMouseEvent(NULL) + AString::number(m_dataValue.toInt())); break; case WuQMacroClassTypeEnum::TAB_BAR: - m_dataType = WuQMacroDataValueTypeEnum::INTEGER; - m_dataTypeTwo = WuQMacroDataValueTypeEnum::STRING; + m_dataType = WuQMacroDataValueTypeEnum::STRING; + m_dataTypeTwo = WuQMacroDataValueTypeEnum::INTEGER; + m_dataValueTwoValidFlag = true; title = ("Select Name " - + m_dataValueTwo.toString() + + m_dataValue.toString() + " else " + " index " - + QString::number(m_dataValue.toInt())); + + QString::number(m_dataValueTwo.toInt())); break; case WuQMacroClassTypeEnum::TAB_WIDGET: - m_dataType = WuQMacroDataValueTypeEnum::INTEGER; - m_dataTypeTwo = WuQMacroDataValueTypeEnum::STRING; + m_dataType = WuQMacroDataValueTypeEnum::STRING; + m_dataTypeTwo = WuQMacroDataValueTypeEnum::INTEGER; + m_dataValueTwoValidFlag = true; title = ("Select Name " - + m_dataValueTwo.toString() + + m_dataValue.toString() + " else " + " index " - + QString::number(m_dataValue.toInt())); + + QString::number(m_dataValueTwo.toInt())); break; case WuQMacroClassTypeEnum::TOOL_BUTTON: m_dataType = WuQMacroDataValueTypeEnum::BOOLEAN; @@ -207,6 +215,8 @@ m_macroMouseEvent(NULL) break; } + CaretAssert(m_dataType != WuQMacroDataValueTypeEnum::INVALID); + setText(title + " " + m_objectName); @@ -313,6 +323,8 @@ WuQMacroCommand::copyHelperWuQMacroCommand(const WuQMacroCommand& obj) m_macroMouseEvent = NULL; switch (m_dataType) { + case WuQMacroDataValueTypeEnum::INVALID: + break; case WuQMacroDataValueTypeEnum::BOOLEAN: break; case WuQMacroDataValueTypeEnum::FLOAT: @@ -399,6 +411,21 @@ WuQMacroCommand::getDataValue() const return m_dataValue; } +/** + * Set the data value + * + * @param dataValue + * New data value + */ +void +WuQMacroCommand::setDataValue(const QVariant& dataValue) +{ + if (m_dataValue != dataValue) { + m_dataValue = dataValue; + setModified(); + } +} + /** * @return The object's second data value. */ @@ -408,6 +435,30 @@ WuQMacroCommand::getDataValueTwo() const return m_dataValueTwo; } +/** + * Set the data value two + * + * @param dataValue + * New data value + */ +void +WuQMacroCommand::setDataValueTwo(const QVariant& dataValue) +{ + if (m_dataValueTwo != dataValue) { + m_dataValueTwo = dataValue; + setModified(); + } +} + +/** + * @return true if data value two is valid + */ +bool +WuQMacroCommand::isDataValueTwoValid() const +{ + return m_dataValueTwoValidFlag; +} + /** * @return Point to mouse event information */ diff --git a/src/Common/WuQMacroCommand.h b/src/Common/WuQMacroCommand.h index 871eb0448..22fcda64a 100644 --- a/src/Common/WuQMacroCommand.h +++ b/src/Common/WuQMacroCommand.h @@ -72,10 +72,16 @@ namespace caret { QVariant getDataValue() const; + void setDataValue(const QVariant& dataValue); + WuQMacroDataValueTypeEnum::Enum getDataTypeTwo() const; QVariant getDataValueTwo() const; + void setDataValueTwo(const QVariant& dataValue); + + bool isDataValueTwoValid() const; + WuQMacroMouseEventInfo* getMouseEventInfo(); const WuQMacroMouseEventInfo* getMouseEventInfo() const; @@ -115,6 +121,8 @@ namespace caret { bool m_modifiedStatusFlag = false; + bool m_dataValueTwoValidFlag = false; + // ADD_NEW_MEMBERS_HERE }; diff --git a/src/Common/WuQMacroDataValueTypeEnum.cxx b/src/Common/WuQMacroDataValueTypeEnum.cxx index 612e1b197..2774d60d3 100644 --- a/src/Common/WuQMacroDataValueTypeEnum.cxx +++ b/src/Common/WuQMacroDataValueTypeEnum.cxx @@ -31,9 +31,7 @@ using namespace caret; /** * \class caret::WuQMacroDataValueTypeEnum - * \brief - * - * + * \brief Enumerated type for macro data values * * Using this enumerated type in the GUI with an EnumComboBoxTemplate * @@ -108,11 +106,15 @@ WuQMacroDataValueTypeEnum::initialize() } initializedFlag = true; - enumData.push_back(WuQMacroDataValueTypeEnum(BOOLEAN, - "BOOLEAN", - "Boolean")); + enumData.push_back(WuQMacroDataValueTypeEnum(INVALID, + "INVALID", + "Invalid")); + + enumData.push_back(WuQMacroDataValueTypeEnum(BOOLEAN, + "BOOLEAN", + "Boolean")); - enumData.push_back(WuQMacroDataValueTypeEnum(FLOAT, + enumData.push_back(WuQMacroDataValueTypeEnum(FLOAT, "FLOAT", "Float")); diff --git a/src/Common/WuQMacroDataValueTypeEnum.h b/src/Common/WuQMacroDataValueTypeEnum.h index aa5a06bf0..9fe956a8f 100644 --- a/src/Common/WuQMacroDataValueTypeEnum.h +++ b/src/Common/WuQMacroDataValueTypeEnum.h @@ -35,15 +35,17 @@ public: * Enumerated values. */ enum Enum { - /** */ + /** Invalid */ + INVALID, + /** Boolean */ BOOLEAN, - /** */ + /** Float */ FLOAT, - /** */ + /** Integer */ INTEGER, - /** */ + /** Mouse */ MOUSE, - /** */ + /** String */ STRING }; diff --git a/src/Common/WuQMacroGroupXmlReader.cxx b/src/Common/WuQMacroGroupXmlReader.cxx index c10f43776..99e458469 100644 --- a/src/Common/WuQMacroGroupXmlReader.cxx +++ b/src/Common/WuQMacroGroupXmlReader.cxx @@ -359,6 +359,9 @@ WuQMacroGroupXmlReader::readMacroCommandAttributesVersionOne() else { QVariant value; switch (dataValueType) { + case WuQMacroDataValueTypeEnum::INVALID: + value.setValue(QString("")); + break; case WuQMacroDataValueTypeEnum::BOOLEAN: { const bool boolValue = ((valueString == VALUE_BOOL_TRUE) ? true : false); @@ -387,6 +390,9 @@ WuQMacroGroupXmlReader::readMacroCommandAttributesVersionOne() QVariant valueTwo; switch (dataValueTypeTwo) { + case WuQMacroDataValueTypeEnum::INVALID: + valueTwo.setValue(QString("")); + break; case WuQMacroDataValueTypeEnum::BOOLEAN: { const bool boolValue = ((valueTwoString == VALUE_BOOL_TRUE) ? true : false); diff --git a/src/Common/WuQMacroGroupXmlWriter.cxx b/src/Common/WuQMacroGroupXmlWriter.cxx index 693823f8c..7347c8f66 100644 --- a/src/Common/WuQMacroGroupXmlWriter.cxx +++ b/src/Common/WuQMacroGroupXmlWriter.cxx @@ -147,6 +147,8 @@ WuQMacroGroupXmlWriter::writeMacroCommandToXML(const WuQMacroCommand* macroComma const QVariant dataValue(macroCommand->getDataValue()); QString stringValue; switch (macroCommand->getDataType()) { + case WuQMacroDataValueTypeEnum::INVALID: + break; case WuQMacroDataValueTypeEnum::BOOLEAN: stringValue = (dataValue.toBool() ? VALUE_BOOL_TRUE : VALUE_BOOL_FALSE); break; @@ -174,6 +176,8 @@ WuQMacroGroupXmlWriter::writeMacroCommandToXML(const WuQMacroCommand* macroComma const QVariant dataValueTwo(macroCommand->getDataValueTwo()); QString stringValue; switch (macroCommand->getDataTypeTwo()) { + case WuQMacroDataValueTypeEnum::INVALID: + break; case WuQMacroDataValueTypeEnum::BOOLEAN: stringValue = (dataValueTwo.toBool() ? VALUE_BOOL_TRUE : VALUE_BOOL_FALSE); break; diff --git a/src/Common/WuQMacroStandardItemTypeEnum.cxx b/src/Common/WuQMacroStandardItemTypeEnum.cxx index 60b274fba..4003763b0 100644 --- a/src/Common/WuQMacroStandardItemTypeEnum.cxx +++ b/src/Common/WuQMacroStandardItemTypeEnum.cxx @@ -118,16 +118,25 @@ WuQMacroStandardItemTypeEnum::initialize() } initializedFlag = true; - enumData.push_back(WuQMacroStandardItemTypeEnum(MACRO, - QStandardItem::UserType + 1, - "MACRO", - "Macro")); + int32_t typeOffset = QStandardItem::UserType + 1; + const int32_t invalidType(typeOffset++); + const int32_t macroType(typeOffset++); + const int32_t macroCommandType(typeOffset++); - enumData.push_back(WuQMacroStandardItemTypeEnum(MACRO_COMMAND, - QStandardItem::UserType + 2, - "MACRO_COMMAND", - "Macro Command")); + enumData.push_back(WuQMacroStandardItemTypeEnum(INVALID, + invalidType, + "INVALID", + "Invalid")); + enumData.push_back(WuQMacroStandardItemTypeEnum(MACRO, + macroType, + "MACRO", + "Macro")); + + enumData.push_back(WuQMacroStandardItemTypeEnum(MACRO_COMMAND, + macroCommandType, + "MACRO_COMMAND", + "Macro Command")); } /** diff --git a/src/Common/WuQMacroStandardItemTypeEnum.h b/src/Common/WuQMacroStandardItemTypeEnum.h index de7f6399e..120b8f539 100644 --- a/src/Common/WuQMacroStandardItemTypeEnum.h +++ b/src/Common/WuQMacroStandardItemTypeEnum.h @@ -35,6 +35,8 @@ public: * Enumerated values. */ enum Enum { + /** Type that is invalid */ + INVALID, /** Type for a macro */ MACRO, /** Type for a macro command */ diff --git a/src/GuiQt/WuQMacroDialog.cxx b/src/GuiQt/WuQMacroDialog.cxx index 4a4ac0dcb..96f9669ce 100644 --- a/src/GuiQt/WuQMacroDialog.cxx +++ b/src/GuiQt/WuQMacroDialog.cxx @@ -28,7 +28,9 @@ #include #include #include +#include #include +#include #include #include #include @@ -171,6 +173,28 @@ WuQMacroDialog::createMacroButtonsWidget() return widget; } +/** + * Update buttons based upon selected item(s) + */ +void +WuQMacroDialog::updateButtons() +{ + bool macroSelected(false); + WuQMacroStandardItemTypeEnum::Enum itemType = getSelectedItemType(); + switch (itemType) { + case WuQMacroStandardItemTypeEnum::INVALID: + break; + case WuQMacroStandardItemTypeEnum::MACRO: + macroSelected = true; + break; + case WuQMacroStandardItemTypeEnum::MACRO_COMMAND: + break; + } + + m_attributesPushButton->setEnabled(macroSelected); + m_deletePushButton->setEnabled(macroSelected); +} + /** * @return The widget displayed when a macro is selected */ @@ -310,17 +334,56 @@ WuQMacroDialog::createCommandWidget() QLabel* titleLabel = new QLabel("Title"); m_commandTitleLabel = new QLabel(); + QLabel* nameLabel = new QLabel("Name"); + m_commandNameLabel = new QLabel(); + + QLabel* typeLabel = new QLabel("Type"); + m_commandTypeLabel = new QLabel(); + + QLabel* valueOneLabel = new QLabel("Value"); + m_commandValueOnePushButton = new QPushButton("Set..."); + m_commandValueOnePushButton->setSizePolicy(QSizePolicy::Fixed, + m_commandValueOnePushButton->sizePolicy().verticalPolicy()); + QObject::connect(m_commandValueOnePushButton, &QPushButton::clicked, + [=] { setMacroCommandValue(ValueIndex::ONE); }); + m_commandValueOneLabel = new QLabel(); + + QLabel* valueTwoLabel = new QLabel("Alt Value"); + m_commandValueTwoPushButton = new QPushButton("Set..."); + m_commandValueTwoPushButton->setSizePolicy(QSizePolicy::Fixed, + m_commandValueTwoPushButton->sizePolicy().verticalPolicy()); + QObject::connect(m_commandValueTwoPushButton, &QPushButton::clicked, + [=] { setMacroCommandValue(ValueIndex::TWO); }); + m_commandValueTwoLabel = new QLabel(); + QLabel* toolTipLabel = new QLabel("ToolTip"); m_commandToolTip = new QLabel(); m_commandToolTip->setWordWrap(true); QGridLayout* layout = new QGridLayout(); + layout->setColumnStretch(0, 0); + layout->setColumnStretch(1, 0); + layout->setColumnStretch(2, 100); int row = 0; layout->addWidget(titleLabel, row, 0); - layout->addWidget(m_commandTitleLabel, row, 1); + layout->addWidget(m_commandTitleLabel, row, 1, 1, 2); + row++; + layout->addWidget(nameLabel, row, 0); + layout->addWidget(m_commandNameLabel, row, 1, 1, 2); + row++; + layout->addWidget(typeLabel, row, 0); + layout->addWidget(m_commandTypeLabel, row, 1, 1, 2); + row++; + layout->addWidget(valueOneLabel, row, 0); + layout->addWidget(m_commandValueOnePushButton, row, 1); + layout->addWidget(m_commandValueOneLabel, row, 2); + row++; + layout->addWidget(valueTwoLabel, row, 0); + layout->addWidget(m_commandValueTwoPushButton, row, 1); + layout->addWidget(m_commandValueTwoLabel, row, 2); row++; layout->addWidget(toolTipLabel, row, 0); - layout->addWidget(m_commandToolTip, row, 1); + layout->addWidget(m_commandToolTip, row, 1, 1, 2); QWidget* widget = new QWidget(); QVBoxLayout* widgetLayout = new QVBoxLayout(widget); @@ -369,6 +432,7 @@ WuQMacroDialog::updateDialogContents() m_runOptionLoopCheckBox->setChecked(runOptions->isLooping()); macroGroupComboBoxActivated(selectedIndex); + updateButtons(); } /** @@ -377,7 +441,6 @@ WuQMacroDialog::updateDialogContents() void WuQMacroDialog::treeViewItemClicked(const QModelIndex& modelIndex) { - QStandardItemModel* selectedModel = NULL; if (modelIndex.isValid()) { const QAbstractItemModel* abstractModel = modelIndex.model(); @@ -399,6 +462,9 @@ WuQMacroDialog::treeViewItemClicked(const QModelIndex& modelIndex) &validFlag); if (validFlag) { switch (itemType) { + case WuQMacroStandardItemTypeEnum::INVALID: + CaretAssertMessage(0, "Type should never be invalid"); + break; case WuQMacroStandardItemTypeEnum::MACRO: macro = dynamic_cast(selectedItem); CaretAssert(macro); @@ -419,6 +485,7 @@ WuQMacroDialog::treeViewItemClicked(const QModelIndex& modelIndex) updateMacroWidget(macro); updateCommandWidget(macroCommand); + updateButtons(); } /** @@ -464,13 +531,91 @@ WuQMacroDialog::updateMacroWidget(WuQMacro* macro) void WuQMacroDialog::updateCommandWidget(WuQMacroCommand* command) { - QString text; + bool validOneFlag = false; + QString newDataValueOne; + if (command != NULL) { + newDataValueOne = command->getDataValue().toString(); + + switch (command->getDataType()) { + case WuQMacroDataValueTypeEnum::INVALID: + break; + case WuQMacroDataValueTypeEnum::BOOLEAN: + if (command->getDataValue().toBool()) { + newDataValueOne = "On"; + } + else { + newDataValueOne = "Off"; + } + validOneFlag = true; + break; + case WuQMacroDataValueTypeEnum::FLOAT: + validOneFlag = true; + break; + case WuQMacroDataValueTypeEnum::INTEGER: + validOneFlag = true; + break; + case WuQMacroDataValueTypeEnum::MOUSE: + break; + case WuQMacroDataValueTypeEnum::STRING: + validOneFlag = true; + break; + } + } + + bool validTwoFlag = false; + QString newDataValueTwo; + if (command != NULL) { + newDataValueTwo = command->getDataValueTwo().toString(); + + switch (command->getDataTypeTwo()) { + case WuQMacroDataValueTypeEnum::INVALID: + break; + case WuQMacroDataValueTypeEnum::BOOLEAN: + if (command->getDataValueTwo().toBool()) { + newDataValueTwo = "On"; + } + else { + newDataValueTwo = "Off"; + } + validTwoFlag = true; + break; + case WuQMacroDataValueTypeEnum::FLOAT: + validTwoFlag = true; + break; + case WuQMacroDataValueTypeEnum::INTEGER: + validTwoFlag = true; + break; + case WuQMacroDataValueTypeEnum::MOUSE: + break; + case WuQMacroDataValueTypeEnum::STRING: + validTwoFlag = true; + break; + } + } + + QString title; + QString name; + QString type; + QString newDataTypeTwo; QString toolTip; if (command != NULL) { - text = command->text(); - toolTip = command->getObjectToolTip(); + title = command->text(); + name = command->getObjectName(); + type = WuQMacroClassTypeEnum::toGuiName(command->getClassType()); + newDataTypeTwo = WuQMacroDataValueTypeEnum::toGuiName(command->getDataTypeTwo()); + toolTip = command->getObjectToolTip(); + } + m_commandTitleLabel->setText(title); + m_commandNameLabel->setText(name); + m_commandTypeLabel->setText(type); + m_commandValueOneLabel->setText(newDataValueOne); + m_commandValueOnePushButton->setEnabled(validOneFlag); + m_commandValueTwoLabel->setText(""); + m_commandValueTwoPushButton->setEnabled(false); + if (validTwoFlag) { + m_commandValueTwoLabel->setText(newDataValueTwo); + m_commandValueTwoPushButton->setEnabled(true); } - m_commandTitleLabel->setText(text); m_commandToolTip->setText(toolTip); } @@ -491,60 +636,121 @@ WuQMacroDialog::getSelectedMacroGroup() } /** - * @return Pointer to selected macro + * @return Pointer to selected macro. If a macro command is selected, its parent + * macro is returned (NULL if no macro is selected) */ WuQMacro* WuQMacroDialog::getSelectedMacro() { WuQMacro* macro(NULL); - QModelIndex modelIndex = m_treeView->currentIndex(); - if (modelIndex.isValid()) { - QStandardItemModel* selectedModel = NULL; - if (modelIndex.isValid()) { - const QAbstractItemModel* abstractModel = modelIndex.model(); - if (abstractModel != NULL) { - const QStandardItemModel* constModel = qobject_cast(abstractModel); - if (constModel != NULL) { - selectedModel = const_cast(constModel); + QStandardItem* selectedItem = getSelectedItem(); + if (selectedItem != NULL) { + bool validFlag(false); + const WuQMacroStandardItemTypeEnum::Enum itemType = WuQMacroStandardItemTypeEnum::fromIntegerCode(selectedItem->type(), + &validFlag); + if (validFlag) { + switch (itemType) { + case WuQMacroStandardItemTypeEnum::INVALID: + CaretAssertMessage(0, "Type should never be invalid"); + break; + case WuQMacroStandardItemTypeEnum::MACRO_COMMAND: + { + /* + * Parent should be WuQMacro + */ + QStandardItem* selectedItemParent = selectedItem->parent(); + CaretAssert(selectedItemParent); + macro = dynamic_cast(selectedItemParent); + CaretAssert(macro); } + break; + case WuQMacroStandardItemTypeEnum::MACRO: + macro = dynamic_cast(selectedItem); + CaretAssert(macro); + break; } } - - - if (selectedModel != NULL) { - QStandardItem* selectedItem = selectedModel->itemFromIndex(modelIndex); - - bool validFlag(false); - const WuQMacroStandardItemTypeEnum::Enum itemType = WuQMacroStandardItemTypeEnum::fromIntegerCode(selectedItem->type(), - &validFlag); - if (validFlag) { - switch (itemType) { - case WuQMacroStandardItemTypeEnum::MACRO_COMMAND: - { - /* - * Parent should be WuQMacro - */ - QStandardItem* selectedItemParent = selectedItem->parent(); - CaretAssert(selectedItemParent); - macro = dynamic_cast(selectedItemParent); - CaretAssert(macro); - } - break; - case WuQMacroStandardItemTypeEnum::MACRO: - macro = dynamic_cast(selectedItem); - CaretAssert(macro); - break; - } + else { + CaretAssertMessage(0, + ("Invalid StandardItemModel type=" + AString::number(selectedItem->type()))); + } + } + + return macro; +} + +/** + * @return Pointer to selected macro command (NULL if no macro command is selected) + */ +WuQMacroCommand* +WuQMacroDialog::getSelectedMacroCommand() +{ + WuQMacroCommand* macroCommand(NULL); + + QStandardItem* selectedItem = getSelectedItem(); + if (selectedItem != NULL) { + bool validFlag(false); + const WuQMacroStandardItemTypeEnum::Enum itemType = WuQMacroStandardItemTypeEnum::fromIntegerCode(selectedItem->type(), + &validFlag); + if (validFlag) { + switch (itemType) { + case WuQMacroStandardItemTypeEnum::INVALID: + CaretAssertMessage(0, "Type should never be invalid"); + break; + case WuQMacroStandardItemTypeEnum::MACRO_COMMAND: + macroCommand = dynamic_cast(selectedItem); + CaretAssert(macroCommand); + break; + case WuQMacroStandardItemTypeEnum::MACRO: + break; } - else { - CaretAssertMessage(0, - ("Invalid StandardItemModel type=" + AString::number(selectedItem->type()))); + } + else { + CaretAssertMessage(0, + ("Invalid StandardItemModel type=" + AString::number(selectedItem->type()))); + } + } + + return macroCommand; +} + +/** + * @return The selected item (NULL if invalid) + */ +QStandardItem* +WuQMacroDialog::getSelectedItem() const +{ + QModelIndex modelIndex = m_treeView->currentIndex(); + if (modelIndex.isValid()) { + QAbstractItemModel* abstractModel = m_treeView->model(); + if (abstractModel != NULL) { + QStandardItemModel* model = qobject_cast(abstractModel); + if (model != NULL) { + QStandardItem* item = model->itemFromIndex(modelIndex); + return item; } } } - return macro; + return NULL; +} + +/** + * @return The selected item type + */ +WuQMacroStandardItemTypeEnum::Enum +WuQMacroDialog::getSelectedItemType() const +{ + WuQMacroStandardItemTypeEnum::Enum itemType = WuQMacroStandardItemTypeEnum::INVALID; + QStandardItem* item = getSelectedItem(); + if (item != NULL) { + bool validFlag(false); + itemType = WuQMacroStandardItemTypeEnum::fromIntegerCode(item->type(), + &validFlag); + } + + return itemType; } /** @@ -640,4 +846,133 @@ WuQMacroDialog::exportButtonClicked() } } +/** + * Set the command value for the given value index + * + * @param valueIndex + * Value one or two + */ +void +WuQMacroDialog::setMacroCommandValue(const ValueIndex valueIndex) +{ + WuQMacroCommand* macroCommand = getSelectedMacroCommand(); + if (macroCommand == NULL) { + return; + } + + WuQMacroDataValueTypeEnum::Enum dataType = WuQMacroDataValueTypeEnum::INVALID; + QVariant dataValue; + QWidget* parentWidget(NULL); + switch (valueIndex) { + case ValueIndex::ONE: + dataType = macroCommand->getDataType(); + dataValue = macroCommand->getDataValue(); + parentWidget = m_commandValueOnePushButton; + break; + case ValueIndex::TWO: + dataType = macroCommand->getDataTypeTwo(); + dataValue = macroCommand->getDataValueTwo(); + parentWidget = m_commandValueTwoPushButton; + break; + } + + bool validFlag(false); + switch (dataType) { + case WuQMacroDataValueTypeEnum::BOOLEAN: + { + QStringList items; + items.push_back("On"); + items.push_back("Off"); + + bool ok(false); + int defaultIndex = (dataValue.toBool() ? 0 : 1); + const QString text = QInputDialog::getItem(parentWidget, + "New Status", + "New Status", + items, + defaultIndex, + false, + &ok); + if (ok + && ( ! text.isEmpty())) { + bool result(text == items.at(0)); + dataValue.setValue(result); + validFlag = true; + } + } + break; + case WuQMacroDataValueTypeEnum::FLOAT: + { + bool ok(false); + const float f = QInputDialog::getDouble(parentWidget, + "New Value", + "New Value", + dataValue.toDouble(), + -2147483647, + 2147483647, + 3, + &ok); + if (ok) { + dataValue.setValue(f); + validFlag = true; + } + } + break; + case WuQMacroDataValueTypeEnum::INTEGER: + { + bool ok(false); + const int i = QInputDialog::getInt(parentWidget, + "New Value", + "New Value", + dataValue.toInt(), + -2147483647, + 2147483647, + 1, + &ok); + if (ok) { + dataValue.setValue(i); + validFlag = true; + } + } + break; + case WuQMacroDataValueTypeEnum::INVALID: + break; + case WuQMacroDataValueTypeEnum::MOUSE: + break; + case WuQMacroDataValueTypeEnum::STRING: + { + bool ok(false); + const QString text = QInputDialog::getText(parentWidget, + "New Text", + "New Text", + QLineEdit::Normal, + dataValue.toString(), + &ok); + if (ok) { + dataValue.setValue(text); + validFlag = true; + } + } + break; + } + + if (validFlag) { + switch (valueIndex) { + case ValueIndex::ONE: + macroCommand->setDataValue(dataValue); + break; + case ValueIndex::TWO: + macroCommand->setDataValueTwo(dataValue); + break; + } + + WuQMacro* macro = getSelectedMacro(); + if (macro != NULL) { + WuQMacroManager::instance()->macroWasModified(macro); + } + } + + updateCommandWidget(macroCommand); +} + diff --git a/src/GuiQt/WuQMacroDialog.h b/src/GuiQt/WuQMacroDialog.h index 1dcce19ad..e0e5e763a 100644 --- a/src/GuiQt/WuQMacroDialog.h +++ b/src/GuiQt/WuQMacroDialog.h @@ -27,6 +27,8 @@ #include +#include "WuQMacroStandardItemTypeEnum.h" + class QAbstractButton; class QCheckBox; class QComboBox; @@ -34,6 +36,7 @@ class QDialogButtonBox; class QDoubleSpinBox; class QLabel; class QStackedWidget; +class QStandardItem; class QTreeView; namespace caret { @@ -81,6 +84,11 @@ namespace caret { void runOptionLoopCheckBoxClicked(bool); private: + enum class ValueIndex { + ONE, + TWO + }; + QWidget* createMacroButtonsWidget(); QWidget* createRunOptionsWidget(); @@ -95,10 +103,20 @@ namespace caret { void runSelectedMacro(); + void updateButtons(); + + void setMacroCommandValue(const ValueIndex valueIndex); + WuQMacroGroup* getSelectedMacroGroup(); WuQMacro* getSelectedMacro(); + WuQMacroCommand* getSelectedMacroCommand(); + + WuQMacroStandardItemTypeEnum::Enum getSelectedItemType() const; + + QStandardItem* getSelectedItem() const; + std::vector m_macroGroups; QComboBox* m_macroGroupComboBox; @@ -133,6 +151,18 @@ namespace caret { QLabel* m_commandTitleLabel; + QLabel* m_commandTypeLabel; + + QLabel* m_commandNameLabel; + + QPushButton* m_commandValueOnePushButton; + + QLabel* m_commandValueOneLabel; + + QPushButton* m_commandValueTwoPushButton; + + QLabel* m_commandValueTwoLabel; + QLabel* m_commandToolTip; // ADD_NEW_MEMBERS_HERE diff --git a/src/GuiQt/WuQMacroExecutor.cxx b/src/GuiQt/WuQMacroExecutor.cxx index 8e51652a7..cc2aac7eb 100644 --- a/src/GuiQt/WuQMacroExecutor.cxx +++ b/src/GuiQt/WuQMacroExecutor.cxx @@ -207,13 +207,7 @@ WuQMacroExecutor::moveMouseToWidgetImplementation(QObject* moveToObject, return; } - if (usingParentFlag) { - std::cout << "For mouse, using parent " << moveToWidget->objectName() - << " for object " << objectName << std::endl; - } - - CaretAssert(moveToWidget); - + CaretAssert(moveToWidget); const QRect widgetRect = ((objectRect != NULL) ? *objectRect : moveToWidget->rect()); @@ -503,18 +497,18 @@ WuQMacroExecutor::runActionGroupCommand(const WuQMacroCommand* macroCommand, QActionGroup* actionGroup = qobject_cast(object); if (actionGroup != NULL) { moveMouseToWidget(actionGroup); - CaretAssert(macroCommand->getDataType() == WuQMacroDataValueTypeEnum::INTEGER); - CaretAssert(macroCommand->getDataTypeTwo() == WuQMacroDataValueTypeEnum::STRING); - + CaretAssert(macroCommand->getDataType() == WuQMacroDataValueTypeEnum::STRING); + CaretAssert(macroCommand->getDataTypeTwo() == WuQMacroDataValueTypeEnum::INTEGER); + QAction* textAction(NULL); QAction* indexAction(NULL); QList actions = actionGroup->actions(); for (int32_t i = 0; i < actions.size(); i++) { QAction* actionAtIndex = actions.at(i); - if (actionAtIndex->text() == macroCommand->getDataValueTwo().toString()) { + if (actionAtIndex->text() == macroCommand->getDataValue().toString()) { textAction = actionAtIndex; } - if (macroCommand->getDataValue().toInt() == i) { + if (macroCommand->getDataValueTwo().toInt() == i) { indexAction = actionAtIndex; } } @@ -532,9 +526,9 @@ WuQMacroExecutor::runActionGroupCommand(const WuQMacroCommand* macroCommand, errorMessageOut = ("For QActionGroup \"" + object->objectName() + "\", unable to find action with text \"" - + macroCommand->getDataValueTwo().toString() + + macroCommand->getDataValue().toString() + "\" or index=" - + macroCommand->getDataValue().toInt()); + + macroCommand->getDataValueTwo().toInt()); } } else { @@ -562,9 +556,9 @@ WuQMacroExecutor::runButtonGroupCommand(const WuQMacroCommand* macroCommand, { QButtonGroup* buttonGroup = qobject_cast(object); if (buttonGroup != NULL) { - CaretAssert(macroCommand->getDataType() == WuQMacroDataValueTypeEnum::INTEGER); - CaretAssert(macroCommand->getDataTypeTwo() == WuQMacroDataValueTypeEnum::STRING); - const QVariant dataValue = macroCommand->getDataValue(); + CaretAssert(macroCommand->getDataType() == WuQMacroDataValueTypeEnum::STRING); + CaretAssert(macroCommand->getDataTypeTwo() == WuQMacroDataValueTypeEnum::INTEGER); + const QVariant dataValue = macroCommand->getDataValue(); const QVariant dataValueTwo = macroCommand->getDataValueTwo(); QAbstractButton* textButton(NULL); @@ -572,10 +566,10 @@ WuQMacroExecutor::runButtonGroupCommand(const WuQMacroCommand* macroCommand, QList buttons = buttonGroup->buttons(); for (int32_t i = 0; i < buttons.size(); i++) { QAbstractButton* buttonAtIndex = buttons.at(i); - if (buttonAtIndex->text() == dataValueTwo.toString()) { + if (buttonAtIndex->text() == dataValue.toString()) { textButton = buttonAtIndex; } - if (dataValue.toInt() == i) { + if (dataValueTwo.toInt() == i) { indexButton = buttonAtIndex; } } @@ -595,9 +589,9 @@ WuQMacroExecutor::runButtonGroupCommand(const WuQMacroCommand* macroCommand, errorMessageOut = ("For QButtonGroup \"" + object->objectName() + "\", unable to find button with text \"" - + dataValueTwo.toString() + + dataValue.toString() + "\" or index=" - + dataValue.toInt()); + + dataValueTwo.toInt()); } } else { @@ -657,17 +651,17 @@ WuQMacroExecutor::runComboBoxCommand(const WuQMacroCommand* macroCommand, { QComboBox* comboBox = qobject_cast(object); if (comboBox != NULL) { - const QVariant dataValue = macroCommand->getDataValue(); + const QVariant dataValue = macroCommand->getDataValue(); const QVariant dataValueTwo = macroCommand->getDataValueTwo(); - CaretAssert(macroCommand->getDataType() == WuQMacroDataValueTypeEnum::INTEGER); - CaretAssert(macroCommand->getDataTypeTwo() == WuQMacroDataValueTypeEnum::STRING); + CaretAssert(macroCommand->getDataType() == WuQMacroDataValueTypeEnum::STRING); + CaretAssert(macroCommand->getDataTypeTwo() == WuQMacroDataValueTypeEnum::INTEGER); int textIndex(-1); int indexIndex(-1); for (int32_t i = 0; i < comboBox->count(); i++) { - if (comboBox->itemText(i) == dataValueTwo.toString()) { + if (comboBox->itemText(i) == dataValue.toString()) { textIndex = i; } - else if (i == dataValue.toInt()) { + else if (i == dataValueTwo.toInt()) { indexIndex = i; } } @@ -685,9 +679,9 @@ WuQMacroExecutor::runComboBoxCommand(const WuQMacroCommand* macroCommand, errorMessageOut = ("For ComboBox \"" + object->objectName() + "\", unable to find item with text \"" - + dataValueTwo.toString() + + dataValue.toString() + "\" or index=" - + dataValue.toInt()); + + dataValueTwo.toInt()); } } else { @@ -789,17 +783,17 @@ WuQMacroExecutor::runListWidgetCommand(const WuQMacroCommand* macroCommand, const QVariant dataValueTwo = macroCommand->getDataValueTwo(); const WuQMacroDataValueTypeEnum::Enum dataValueTypeTwo = macroCommand->getDataTypeTwo(); - CaretAssert(dataValueType == WuQMacroDataValueTypeEnum::INTEGER); - CaretAssert(dataValueTypeTwo == WuQMacroDataValueTypeEnum::STRING); + CaretAssert(dataValueType == WuQMacroDataValueTypeEnum::STRING); + CaretAssert(dataValueTypeTwo == WuQMacroDataValueTypeEnum::INTEGER); QListWidgetItem* textItem(NULL); QListWidgetItem* indexItem(NULL); for (int32_t i = 0; i < listWidget->count(); i++) { QListWidgetItem* itemAtIndex = listWidget->item(i); - if (itemAtIndex->text() == dataValueTwo.toString()) { + if (itemAtIndex->text() == dataValue.toString()) { textItem = itemAtIndex; } - if (dataValue.toInt() == i) { + if (dataValueTwo.toInt() == i) { indexItem = itemAtIndex; } } @@ -812,7 +806,6 @@ WuQMacroExecutor::runListWidgetCommand(const WuQMacroCommand* macroCommand, textItem->text()); } else if (indexItem != NULL) { - signalEmitter.emitQListWidgetSignal(listWidget, indexItem->text()); } @@ -820,9 +813,9 @@ WuQMacroExecutor::runListWidgetCommand(const WuQMacroCommand* macroCommand, errorMessageOut = ("For QListWidget \"" + object->objectName() + "\", unable to find item with text \"" - + dataValueTwo.toString() + + dataValue.toString() + "\" or index=" - + dataValue.toInt()); + + dataValueTwo.toInt()); } } else { @@ -855,18 +848,18 @@ WuQMacroExecutor::runMenuCommand(const WuQMacroCommand* macroCommand, const QVariant dataValueTwo = macroCommand->getDataValueTwo(); const WuQMacroDataValueTypeEnum::Enum dataValueTypeTwo = macroCommand->getDataTypeTwo(); - CaretAssert(dataValueType == WuQMacroDataValueTypeEnum::INTEGER); - CaretAssert(dataValueTypeTwo == WuQMacroDataValueTypeEnum::STRING); + CaretAssert(dataValueType == WuQMacroDataValueTypeEnum::STRING); + CaretAssert(dataValueTypeTwo == WuQMacroDataValueTypeEnum::INTEGER); QAction* textAction(NULL); QAction* indexAction(NULL); QList actions = menu->actions(); for (int32_t i = 0; i < actions.size(); i++) { QAction* actionAtIndex = actions.at(i); - if (actionAtIndex->text() == dataValueTwo.toString()) { + if (actionAtIndex->text() == dataValue.toString()) { textAction = actionAtIndex; } - if (dataValue.toInt() == i) { + if (dataValueTwo.toInt() == i) { indexAction = actionAtIndex; } } @@ -886,9 +879,9 @@ WuQMacroExecutor::runMenuCommand(const WuQMacroCommand* macroCommand, errorMessageOut = ("For QMenu \"" + object->objectName() + "\", unable to find action with text \"" - + dataValueTwo.toString() + + dataValue.toString() + "\" or index=" - + dataValue.toInt()); + + dataValueTwo.toInt()); } } else { @@ -1144,13 +1137,13 @@ WuQMacroExecutor::runTabBarCommand(const WuQMacroCommand* macroCommand, const QVariant dataValueTwo = macroCommand->getDataValueTwo(); const WuQMacroDataValueTypeEnum::Enum dataValueTypeTwo = macroCommand->getDataTypeTwo(); - CaretAssert(dataValueType == WuQMacroDataValueTypeEnum::INTEGER); - int32_t tabIndex = dataValue.toInt(); - if (dataValueTypeTwo == WuQMacroDataValueTypeEnum::STRING) { + CaretAssert(dataValueTypeTwo == WuQMacroDataValueTypeEnum::INTEGER); + int32_t tabIndex = dataValueTwo.toInt(); + if (dataValueType == WuQMacroDataValueTypeEnum::STRING) { /* * Allow tab name to override tab index */ - const QString tabName = dataValueTwo.toString(); + const QString tabName = dataValue.toString(); if ( ! tabName.isEmpty()) { for (int32_t i = 0; i < tabBar->count(); i++) { if (tabName == tabBar->tabText(i)) { @@ -1207,15 +1200,15 @@ WuQMacroExecutor::runTabWidgetCommand(const WuQMacroCommand* macroCommand, const QVariant dataValueTwo = macroCommand->getDataValueTwo(); const WuQMacroDataValueTypeEnum::Enum dataValueTypeTwo = macroCommand->getDataTypeTwo(); - CaretAssert(dataValueType == WuQMacroDataValueTypeEnum::INTEGER); + CaretAssert(dataValueTypeTwo == WuQMacroDataValueTypeEnum::INTEGER); QTabBar* tabBar = tabWidget->tabBar(); CaretAssert(tabBar); - int32_t tabIndex = dataValue.toInt(); - if (dataValueTypeTwo == WuQMacroDataValueTypeEnum::STRING) { + int32_t tabIndex = dataValueTwo.toInt(); + if (dataValueType == WuQMacroDataValueTypeEnum::STRING) { /* * Allow tab name to override tab index */ - const QString tabName = dataValueTwo.toString(); + const QString tabName = dataValue.toString(); if ( ! tabName.isEmpty()) { for (int32_t i = 0; i < tabWidget->count(); i++) { if (tabName == tabWidget->tabText(i)) { diff --git a/src/GuiQt/WuQMacroManager.cxx b/src/GuiQt/WuQMacroManager.cxx index c42e4d339..a5855c509 100644 --- a/src/GuiQt/WuQMacroManager.cxx +++ b/src/GuiQt/WuQMacroManager.cxx @@ -531,9 +531,7 @@ WuQMacroManager::editMacroAttributes(QWidget* parent, parent); if (attributesDialog.exec() == WuQMacroDialog::Accepted) { if (attributesDialog.isMacroModified()) { - if (m_macroHelper) { - m_macroHelper->macroWasModified(macro); - } + macroWasModified(macro); return true; } } @@ -542,6 +540,21 @@ WuQMacroManager::editMacroAttributes(QWidget* parent, return false; } +/** + * Can be called to indicate that a macro was modified + * + * @param macro + * Macro that was modified + */ +void +WuQMacroManager::macroWasModified(WuQMacro* macro) +{ + if (m_macroHelper) { + m_macroHelper->macroWasModified(macro); + } +} + + /** * Delete a macro * diff --git a/src/GuiQt/WuQMacroManager.h b/src/GuiQt/WuQMacroManager.h index 9686b915f..30e1bf5cd 100644 --- a/src/GuiQt/WuQMacroManager.h +++ b/src/GuiQt/WuQMacroManager.h @@ -123,6 +123,8 @@ namespace caret { static QString getShortCutKeysMask(); + void macroWasModified(WuQMacro* macro); + // ADD_NEW_METHODS_HERE public slots: diff --git a/src/GuiQt/WuQMacroSignalWatcher.cxx b/src/GuiQt/WuQMacroSignalWatcher.cxx index 1126ce574..9fac6ab5e 100644 --- a/src/GuiQt/WuQMacroSignalWatcher.cxx +++ b/src/GuiQt/WuQMacroSignalWatcher.cxx @@ -383,8 +383,8 @@ WuQMacroSignalWatcher::actionGroupTriggered(QAction* action) const QString actionText((action != NULL) ? action->text() : ""); - createAndSendMacroCommand(actionIndex, - actionText); + createAndSendMacroCommand(actionText, + actionIndex); } @@ -425,8 +425,8 @@ WuQMacroSignalWatcher::buttonGroupButtonClicked(QAbstractButton* button) const QString buttonText((button != NULL) ? button->text() : ""); - createAndSendMacroCommand(buttonIndex, - buttonText); + createAndSendMacroCommand(buttonText, + buttonIndex); } @@ -461,8 +461,8 @@ WuQMacroSignalWatcher::comboBoxActivated(int index) text = comboBox->itemText(index); } - createAndSendMacroCommand(index, - text); + createAndSendMacroCommand(text, + index); } /** @@ -508,8 +508,8 @@ WuQMacroSignalWatcher::listWidgetItemActivated(QListWidgetItem* item) const QString text((item != NULL) ? item->text() : ""); - createAndSendMacroCommand(rowIndex, - text); + createAndSendMacroCommand(text, + rowIndex); } /** @@ -536,8 +536,8 @@ WuQMacroSignalWatcher::menuTriggered(QAction* action) const QString text((action != NULL) ? action->text() : ""); - createAndSendMacroCommand(actionIndex, - text); + createAndSendMacroCommand(text, + actionIndex); } /** @@ -604,8 +604,8 @@ WuQMacroSignalWatcher::tabBarCurrentChanged(int index) QTabBar* tabBar = qobject_cast(m_object); CaretAssert(tabBar); const QString tabText = tabBar->tabText(index); - createAndSendMacroCommand(index, - tabText); + createAndSendMacroCommand(tabText, + index); } /** @@ -620,8 +620,8 @@ WuQMacroSignalWatcher::tabWidgetCurrentChanged(int index) QTabWidget* tabWidget = qobject_cast(m_object); CaretAssert(tabWidget); const QString tabText = tabWidget->tabText(index); - createAndSendMacroCommand(index, - tabText); + createAndSendMacroCommand(tabText, + index); } /** -- GitLab From 6e8460099bc1c9adbc33d42ed8de8d1969a7e6e0 Mon Sep 17 00:00:00 2001 From: John Harwell Date: Wed, 9 Jan 2019 16:29:54 -0600 Subject: [PATCH 166/427] WB-834, Macros: Moved macro editing (name/shortcut/description) to the macro dialog and eliminated the separate dialog for macro editing. --- src/Common/WuQMacroCommand.cxx | 169 ++++++++++++++++--------- src/Common/WuQMacroCommand.h | 4 +- src/Common/WuQMacroGroup.cxx | 1 + src/GuiQt/CMakeLists.txt | 3 - src/GuiQt/WuQMacroAttributesDialog.cxx | 166 ------------------------ src/GuiQt/WuQMacroAttributesDialog.h | 82 ------------ src/GuiQt/WuQMacroDialog.cxx | 161 +++++++++++++++++------ src/GuiQt/WuQMacroDialog.h | 16 ++- src/GuiQt/WuQMacroManager.cxx | 30 ----- src/GuiQt/WuQMacroManager.h | 3 - 10 files changed, 251 insertions(+), 384 deletions(-) delete mode 100644 src/GuiQt/WuQMacroAttributesDialog.cxx delete mode 100644 src/GuiQt/WuQMacroAttributesDialog.h diff --git a/src/Common/WuQMacroCommand.cxx b/src/Common/WuQMacroCommand.cxx index 1a8956311..d2409c868 100644 --- a/src/Common/WuQMacroCommand.cxx +++ b/src/Common/WuQMacroCommand.cxx @@ -90,136 +90,79 @@ m_macroMouseEvent(NULL) m_dataType = WuQMacroDataValueTypeEnum::INVALID; m_dataTypeTwo = WuQMacroDataValueTypeEnum::INVALID; - QString title = "Unknown"; - switch (m_classType) { case WuQMacroClassTypeEnum::ACTION: m_dataType = WuQMacroDataValueTypeEnum::BOOLEAN; - title = ("Turn " - + QString((m_dataValue.toBool() ? "On" : "Off"))); break; case WuQMacroClassTypeEnum::ACTION_GROUP: m_dataType = WuQMacroDataValueTypeEnum::STRING; m_dataTypeTwo = WuQMacroDataValueTypeEnum::INTEGER; m_dataValueTwoValidFlag = true; - title = ("Select Name " - + m_dataValue.toString() - + " else " - + " index " - + QString::number(m_dataValueTwo.toInt())); break; case WuQMacroClassTypeEnum::BUTTON_GROUP: m_dataType = WuQMacroDataValueTypeEnum::STRING; m_dataTypeTwo = WuQMacroDataValueTypeEnum::INTEGER; m_dataValueTwoValidFlag = true; - title = ("Select Name " - + m_dataValue.toString() - + " else " - + " index " - + QString::number(m_dataValueTwo.toInt())); break; case WuQMacroClassTypeEnum::CHECK_BOX: m_dataType = WuQMacroDataValueTypeEnum::BOOLEAN; - title = ("Turn " - + QString((m_dataValue.toBool() ? "On" : "Off"))); break; case WuQMacroClassTypeEnum::COMBO_BOX: m_dataType = WuQMacroDataValueTypeEnum::STRING; m_dataTypeTwo = WuQMacroDataValueTypeEnum::INTEGER; m_dataValueTwoValidFlag = true; - title = ("Select Name " - + m_dataValue.toString() - + " else " - + " index " - + QString::number(m_dataValueTwo.toInt())); break; case WuQMacroClassTypeEnum::DOUBLE_SPIN_BOX: m_dataType = WuQMacroDataValueTypeEnum::FLOAT; - title = ("Set to value " - + QString::number(m_dataValue.toFloat())); break; case WuQMacroClassTypeEnum::INVALID: - title = ""; break; case WuQMacroClassTypeEnum::LINE_EDIT: m_dataType = WuQMacroDataValueTypeEnum::STRING; - title = ("Set to text " - + m_dataValue.toString()); break; case WuQMacroClassTypeEnum::LIST_WIDGET: m_dataType = WuQMacroDataValueTypeEnum::STRING; m_dataTypeTwo = WuQMacroDataValueTypeEnum::INTEGER; m_dataValueTwoValidFlag = true; - title = ("Select Name " - + m_dataValue.toString() - + " else " - + " index " - + QString::number(m_dataValueTwo.toInt())); break; case WuQMacroClassTypeEnum::MENU: m_dataType = WuQMacroDataValueTypeEnum::STRING; m_dataTypeTwo = WuQMacroDataValueTypeEnum::INTEGER; m_dataValueTwoValidFlag = true; - title = ("Select Name " - + m_dataValue.toString() - + " else " - + " index " - + QString::number(m_dataValueTwo.toInt())); break; case WuQMacroClassTypeEnum::MOUSE_USER_EVENT: m_dataType = WuQMacroDataValueTypeEnum::MOUSE; - title = "MouseEvent"; break; case WuQMacroClassTypeEnum::PUSH_BUTTON: m_dataType = WuQMacroDataValueTypeEnum::BOOLEAN; - title = ("Click Button"); break; case WuQMacroClassTypeEnum::RADIO_BUTTON: m_dataType = WuQMacroDataValueTypeEnum::BOOLEAN; - title = ("Turn " - + QString((m_dataValue.toBool() ? "On" : "Off"))); break; case WuQMacroClassTypeEnum::SLIDER: m_dataType = WuQMacroDataValueTypeEnum::INTEGER; - title = ("Move to " - + AString::number(m_dataValue.toInt())); break; case WuQMacroClassTypeEnum::SPIN_BOX: m_dataType = WuQMacroDataValueTypeEnum::INTEGER; - title = ("Set to " - + AString::number(m_dataValue.toInt())); break; case WuQMacroClassTypeEnum::TAB_BAR: m_dataType = WuQMacroDataValueTypeEnum::STRING; m_dataTypeTwo = WuQMacroDataValueTypeEnum::INTEGER; m_dataValueTwoValidFlag = true; - title = ("Select Name " - + m_dataValue.toString() - + " else " - + " index " - + QString::number(m_dataValueTwo.toInt())); break; case WuQMacroClassTypeEnum::TAB_WIDGET: m_dataType = WuQMacroDataValueTypeEnum::STRING; m_dataTypeTwo = WuQMacroDataValueTypeEnum::INTEGER; m_dataValueTwoValidFlag = true; - title = ("Select Name " - + m_dataValue.toString() - + " else " - + " index " - + QString::number(m_dataValueTwo.toInt())); break; case WuQMacroClassTypeEnum::TOOL_BUTTON: m_dataType = WuQMacroDataValueTypeEnum::BOOLEAN; - title = ("Click Button"); break; } CaretAssert(m_dataType != WuQMacroDataValueTypeEnum::INVALID); - - setText(title - + " " - + m_objectName); + + updateTitle(); setModified(); } @@ -340,6 +283,112 @@ WuQMacroCommand::copyHelperWuQMacroCommand(const WuQMacroCommand& obj) break; } setText(obj.text()); + updateTitle(); +} + +/** + * Update the title for this command + */ +void +WuQMacroCommand::updateTitle() +{ + QString title = "Unknown"; + + switch (m_classType) { + case WuQMacroClassTypeEnum::ACTION: + title = ("Turn " + + QString((m_dataValue.toBool() ? "On" : "Off"))); + break; + case WuQMacroClassTypeEnum::ACTION_GROUP: + title = ("Select Name " + + m_dataValue.toString() + + " else " + + " index " + + QString::number(m_dataValueTwo.toInt())); + break; + case WuQMacroClassTypeEnum::BUTTON_GROUP: + title = ("Select Name " + + m_dataValue.toString() + + " else " + + " index " + + QString::number(m_dataValueTwo.toInt())); + break; + case WuQMacroClassTypeEnum::CHECK_BOX: + title = ("Turn " + + QString((m_dataValue.toBool() ? "On" : "Off"))); + break; + case WuQMacroClassTypeEnum::COMBO_BOX: + title = ("Select Name " + + m_dataValue.toString() + + " else " + + " index " + + QString::number(m_dataValueTwo.toInt())); + break; + case WuQMacroClassTypeEnum::DOUBLE_SPIN_BOX: + title = ("Set to value " + + QString::number(m_dataValue.toFloat())); + break; + case WuQMacroClassTypeEnum::INVALID: + title = ""; + break; + case WuQMacroClassTypeEnum::LINE_EDIT: + title = ("Set to text " + + m_dataValue.toString()); + break; + case WuQMacroClassTypeEnum::LIST_WIDGET: + title = ("Select Name " + + m_dataValue.toString() + + " else " + + " index " + + QString::number(m_dataValueTwo.toInt())); + break; + case WuQMacroClassTypeEnum::MENU: + title = ("Select Name " + + m_dataValue.toString() + + " else " + + " index " + + QString::number(m_dataValueTwo.toInt())); + break; + case WuQMacroClassTypeEnum::MOUSE_USER_EVENT: + title = "MouseEvent"; + break; + case WuQMacroClassTypeEnum::PUSH_BUTTON: + title = ("Click Button"); + break; + case WuQMacroClassTypeEnum::RADIO_BUTTON: + title = ("Turn " + + QString((m_dataValue.toBool() ? "On" : "Off"))); + break; + case WuQMacroClassTypeEnum::SLIDER: + title = ("Move to " + + AString::number(m_dataValue.toInt())); + break; + case WuQMacroClassTypeEnum::SPIN_BOX: + title = ("Set to " + + AString::number(m_dataValue.toInt())); + break; + case WuQMacroClassTypeEnum::TAB_BAR: + title = ("Select Name " + + m_dataValue.toString() + + " else " + + " index " + + QString::number(m_dataValueTwo.toInt())); + break; + case WuQMacroClassTypeEnum::TAB_WIDGET: + title = ("Select Name " + + m_dataValue.toString() + + " else " + + " index " + + QString::number(m_dataValueTwo.toInt())); + break; + case WuQMacroClassTypeEnum::TOOL_BUTTON: + title = ("Click Button"); + break; + } + + setText(title + + " " + + m_objectName); } /** @@ -422,6 +471,7 @@ WuQMacroCommand::setDataValue(const QVariant& dataValue) { if (m_dataValue != dataValue) { m_dataValue = dataValue; + updateTitle(); setModified(); } } @@ -446,6 +496,7 @@ WuQMacroCommand::setDataValueTwo(const QVariant& dataValue) { if (m_dataValueTwo != dataValue) { m_dataValueTwo = dataValue; + updateTitle(); setModified(); } } diff --git a/src/Common/WuQMacroCommand.h b/src/Common/WuQMacroCommand.h index 22fcda64a..cd897a4a4 100644 --- a/src/Common/WuQMacroCommand.h +++ b/src/Common/WuQMacroCommand.h @@ -104,7 +104,9 @@ namespace caret { private: void copyHelperWuQMacroCommand(const WuQMacroCommand& obj); - + + void updateTitle(); + WuQMacroClassTypeEnum::Enum m_classType; QString m_objectName; diff --git a/src/Common/WuQMacroGroup.cxx b/src/Common/WuQMacroGroup.cxx index 0bf524ad7..bf93a2930 100644 --- a/src/Common/WuQMacroGroup.cxx +++ b/src/Common/WuQMacroGroup.cxx @@ -403,6 +403,7 @@ WuQMacroGroup::isModified() const const int32_t count = getNumberOfMacros(); for (int32_t i = 0; i < count; i++) { if (getMacroAtIndex(i)->isModified()) { + return true; break; } } diff --git a/src/GuiQt/CMakeLists.txt b/src/GuiQt/CMakeLists.txt index 5a6b925d3..8048feb58 100644 --- a/src/GuiQt/CMakeLists.txt +++ b/src/GuiQt/CMakeLists.txt @@ -204,7 +204,6 @@ ELSE() WuQGroupBoxExclusiveWidget.h WuQImageLabel.h WuQListWidget.h - WuQMacroAttributesDialog.h WuQMacroCreateDialog.h WuQMacroDialog.h WuQMacroExecutor.h @@ -462,7 +461,6 @@ WuQGridLayoutGroup.h WuQGroupBoxExclusiveWidget.h WuQImageLabel.h WuQListWidget.h -WuQMacroAttributesDialog.h WuQMacroCreateDialog.h WuQMacroDialog.h WuQMacroExecutor.h @@ -708,7 +706,6 @@ WuQGridLayoutGroup.cxx WuQGroupBoxExclusiveWidget.cxx WuQImageLabel.cxx WuQListWidget.cxx -WuQMacroAttributesDialog.cxx WuQMacroCreateDialog.cxx WuQMacroDialog.cxx WuQMacroExecutor.cxx diff --git a/src/GuiQt/WuQMacroAttributesDialog.cxx b/src/GuiQt/WuQMacroAttributesDialog.cxx deleted file mode 100644 index 1199ed76b..000000000 --- a/src/GuiQt/WuQMacroAttributesDialog.cxx +++ /dev/null @@ -1,166 +0,0 @@ - -/*LICENSE_START*/ -/* - * Copyright (C) 2018 Washington University School of Medicine - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ -/*LICENSE_END*/ - -#define __WU_Q_MACRO_ATTRIBUTES_DIALOG_DECLARE__ -#include "WuQMacroAttributesDialog.h" -#undef __WU_Q_MACRO_ATTRIBUTES_DIALOG_DECLARE__ - -#include -#include -#include -#include -#include -#include -#include -#include - -#include "CaretAssert.h" -#include "CaretLogger.h" -#include "WuQMacro.h" -#include "WuQMacroGroup.h" -#include "WuQMacroManager.h" -#include "WuQMacroShortCutKeyComboBox.h" - -using namespace caret; - - - -/** - * \class caret::WuQMacroAttributesDialog - * \brief Dialog for creating a new macro - * \ingroup WuQMacro - */ - -/** - * Constructor. - * - * @param macro - * The macro being edited - * @param parent - * The parent widget - */ -WuQMacroAttributesDialog::WuQMacroAttributesDialog(WuQMacro* macro, - QWidget* parent) -: QDialog(parent), -m_macro(macro), -m_macroWasModifiedFlag(false) -{ - CaretAssert(m_macro); - - setWindowTitle("Macro Attributes"); - - QLabel* nameLabel = new QLabel("Macro name:"); - QLabel* shortCutKeyLabel = new QLabel("Short Cut Key:"); - QLabel* shortCutKeyMaskLabel = new QLabel(WuQMacroManager::getShortCutKeysMask()); - QLabel* descriptionLabel = new QLabel("Description:"); - - m_macroNameLineEdit = new QLineEdit(); - m_macroNameLineEdit->setText(m_macro->getName()); - m_macroShortCutKeyComboBox = new WuQMacroShortCutKeyComboBox(this); - m_macroShortCutKeyComboBox->setSelectedShortCutKey(m_macro->getShortCutKey()); - m_macroDescriptionTextEdit = new QPlainTextEdit(); - m_macroDescriptionTextEdit->setFixedHeight(100); - m_macroDescriptionTextEdit->setPlainText(m_macro->getDescription()); - - QGridLayout* gridLayout = new QGridLayout(); - int row = 0; - gridLayout->addWidget(nameLabel, row, 0); - gridLayout->addWidget(m_macroNameLineEdit, row, 1, 1, 2); - row++; - gridLayout->addWidget(shortCutKeyLabel, row, 0); - gridLayout->addWidget(shortCutKeyMaskLabel, row, 1); - gridLayout->addWidget(m_macroShortCutKeyComboBox->getWidget(), row, 2, Qt::AlignLeft); - row++; - gridLayout->addWidget(descriptionLabel, row, 0); - gridLayout->addWidget(m_macroDescriptionTextEdit, row, 1, 1, 2); - row++; - - m_dialogButtonBox = new QDialogButtonBox(QDialogButtonBox::Ok - | QDialogButtonBox::Cancel); - QObject::connect(m_dialogButtonBox, &QDialogButtonBox::accepted, - this, &WuQMacroAttributesDialog::accept); - QObject::connect(m_dialogButtonBox, &QDialogButtonBox::rejected, - this, &WuQMacroAttributesDialog::reject); - - QVBoxLayout* dialogLayout = new QVBoxLayout(this); - dialogLayout->addLayout(gridLayout); - dialogLayout->addWidget(m_dialogButtonBox); -} - -/** - * Destructor. - */ -WuQMacroAttributesDialog::~WuQMacroAttributesDialog() -{ -} - -/** - * Called when user clicks OK or Cancel - * - * @param r - * The dialog code (Accepted or Rejected) - */ -void -WuQMacroAttributesDialog::done(int r) -{ - if (r == QDialog::Accepted) { - const QString name(m_macroNameLineEdit->text().trimmed()); - if (name.isEmpty()) { - QMessageBox::critical(this, - "Error", - "Name is missing", - QMessageBox::Ok, - QMessageBox::Ok); - return; - } - - const QString newName = m_macroNameLineEdit->text().trimmed(); - if (m_macro->getName() != newName) { - m_macro->setName(newName); - m_macroWasModifiedFlag = true; - } - - const WuQMacroShortCutKeyEnum::Enum newShortCutKey = m_macroShortCutKeyComboBox->getSelectedShortCutKey(); - if (m_macro->getShortCutKey() != newShortCutKey) { - m_macro->setShortCutKey(newShortCutKey); - m_macroWasModifiedFlag = true; - } - - const QString newDescription = m_macroDescriptionTextEdit->toPlainText().trimmed(); - if (m_macro->getDescription() != newDescription) { - m_macro->setDescription(newDescription); - m_macroWasModifiedFlag = true; - } - } - - QDialog::done(r); -} - -/** - * @return True if the macro was modified and may need to be saved, - * else false - */ -bool -WuQMacroAttributesDialog::isMacroModified() const -{ - return m_macroWasModifiedFlag; -} - diff --git a/src/GuiQt/WuQMacroAttributesDialog.h b/src/GuiQt/WuQMacroAttributesDialog.h deleted file mode 100644 index d9368428f..000000000 --- a/src/GuiQt/WuQMacroAttributesDialog.h +++ /dev/null @@ -1,82 +0,0 @@ -#ifndef __WU_Q_MACRO_ATTRIBUTES_DIALOG_H__ -#define __WU_Q_MACRO_ATTRIBUTES_DIALOG_H__ - -/*LICENSE_START*/ -/* - * Copyright (C) 2018 Washington University School of Medicine - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ -/*LICENSE_END*/ - - - -#include - -#include - -class QDialogButtonBox; -class QLineEdit; -class QPlainTextEdit; - - -namespace caret { - - class WuQMacro; - class WuQMacroGroup; - class WuQMacroShortCutKeyComboBox; - - class WuQMacroAttributesDialog : public QDialog { - - Q_OBJECT - - public: - WuQMacroAttributesDialog(WuQMacro* macro, - QWidget* parent = 0); - - virtual ~WuQMacroAttributesDialog(); - - WuQMacroAttributesDialog(const WuQMacroAttributesDialog&) = delete; - - WuQMacroAttributesDialog& operator=(const WuQMacroAttributesDialog&) = delete; - - bool isMacroModified() const; - - // ADD_NEW_METHODS_HERE - - public slots: - virtual void done(int r) override; - - private: - WuQMacro* m_macro; - - bool m_macroWasModifiedFlag = false; - - QLineEdit* m_macroNameLineEdit; - - WuQMacroShortCutKeyComboBox* m_macroShortCutKeyComboBox; - - QPlainTextEdit* m_macroDescriptionTextEdit; - - QDialogButtonBox* m_dialogButtonBox; - - }; - -#ifdef __WU_Q_MACRO_ATTRIBUTES_DIALOG_DECLARE__ - // -#endif // __WU_Q_MACRO_ATTRIBUTES_DIALOG_DECLARE__ - -} // namespace -#endif //__WU_Q_MACRO_ATTRIBUTES_DIALOG_H__ diff --git a/src/GuiQt/WuQMacroDialog.cxx b/src/GuiQt/WuQMacroDialog.cxx index 96f9669ce..c2aa80317 100644 --- a/src/GuiQt/WuQMacroDialog.cxx +++ b/src/GuiQt/WuQMacroDialog.cxx @@ -45,6 +45,7 @@ #include "WuQMacroGroup.h" #include "WuQMacroExecutor.h" #include "WuQMacroManager.h" +#include "WuQMacroShortCutKeyComboBox.h" #include "WuQMacroStandardItemTypeEnum.h" using namespace caret; @@ -140,11 +141,6 @@ WuQMacroDialog::createMacroButtonsWidget() { QWidget* widget = new QWidget(); - m_attributesPushButton = new QPushButton("Attributes..."); - m_attributesPushButton->setToolTip("Edit the selected macro's name, description, etc"); - QObject::connect(m_attributesPushButton, &QPushButton::clicked, - this, &WuQMacroDialog::attributesButtonClicked); - m_deletePushButton = new QPushButton("Delete..."); m_deletePushButton->setToolTip("Delete the selected macro"); QObject::connect(m_deletePushButton, &QPushButton::clicked, @@ -162,8 +158,6 @@ WuQMacroDialog::createMacroButtonsWidget() QVBoxLayout* macroButtonsLayout = new QVBoxLayout(widget); macroButtonsLayout->setSpacing(4); - macroButtonsLayout->addWidget(m_attributesPushButton); - macroButtonsLayout->addSpacing(15); macroButtonsLayout->addWidget(m_deletePushButton); macroButtonsLayout->addSpacing(15); macroButtonsLayout->addWidget(m_importPushButton); @@ -191,7 +185,6 @@ WuQMacroDialog::updateButtons() break; } - m_attributesPushButton->setEnabled(macroSelected); m_deletePushButton->setEnabled(macroSelected); } @@ -201,28 +194,133 @@ WuQMacroDialog::updateButtons() QWidget* WuQMacroDialog::createMacroWidget() { - QLabel* descriptionLabel = new QLabel("Macro Description:"); + QLabel* nameLabel = new QLabel("Name:"); + m_macroNameLabel = new QLabel(); + QPushButton* nameEditPushButton = new QPushButton("Edit..."); + nameEditPushButton->setSizePolicy(QSizePolicy::Fixed, + nameEditPushButton->sizePolicy().verticalPolicy()); + QObject::connect(nameEditPushButton, &QPushButton::clicked, + this, &WuQMacroDialog::macroNameEditButtonClicked); + + QLabel* shortCutKeyLabel = new QLabel("Short Cut Key:"); + QLabel* shortCutKeyMaskLabel = new QLabel(WuQMacroManager::getShortCutKeysMask()); + m_macroShortCutKeyComboBox = new WuQMacroShortCutKeyComboBox(this); + QObject::connect(m_macroShortCutKeyComboBox, &WuQMacroShortCutKeyComboBox::shortCutKeySelected, + this, &WuQMacroDialog::macroShortCutKeySelected); + + QLabel* descriptionLabel = new QLabel("Description:"); m_macroDescriptionLabel = new QLabel(""); m_macroDescriptionLabel->setWordWrap(true); m_macroDescriptionLabel->setMinimumHeight(40); m_macroDescriptionLabel->setAlignment(Qt::AlignTop | Qt::AlignLeft); + QPushButton* descriptionEditPushButton = new QPushButton("Edit..."); + descriptionEditPushButton->setSizePolicy(QSizePolicy::Fixed, + descriptionEditPushButton->sizePolicy().verticalPolicy()); + QObject::connect(descriptionEditPushButton, &QPushButton::clicked, + this, &WuQMacroDialog::macroDescriptionEditButtonClicked); + QFrame* horizontalLine = new QFrame(); horizontalLine->setMidLineWidth(1); horizontalLine->setLineWidth(1); horizontalLine->setFrameStyle(QFrame::HLine | QFrame::Sunken); + QGridLayout* gridLayout = new QGridLayout(); + gridLayout->setColumnStretch(0, 0); + gridLayout->setColumnStretch(1, 0); + gridLayout->setColumnStretch(2, 0); + gridLayout->setColumnStretch(3, 100); + int row = 0; + gridLayout->addWidget(nameLabel, row, 0); + gridLayout->addWidget(nameEditPushButton, row, 1); + gridLayout->addWidget(m_macroNameLabel, row, 2, 1, 2); + row++; + gridLayout->addWidget(shortCutKeyLabel, row, 0, 1, 2); + gridLayout->addWidget(shortCutKeyMaskLabel, row, 2); + gridLayout->addWidget(m_macroShortCutKeyComboBox->getWidget(), row, 3, Qt::AlignLeft); + row++; + gridLayout->addWidget(descriptionLabel, row, 0, 1, 2); + gridLayout->addWidget(m_macroDescriptionLabel, row, 2, 2, 2); + row++; + gridLayout->addWidget(descriptionEditPushButton, row, 0, 1, 2); + row++; + QWidget* widget = new QWidget(); - QVBoxLayout* dialogLayout = new QVBoxLayout(widget); - dialogLayout->addWidget(descriptionLabel); - dialogLayout->addWidget(m_macroDescriptionLabel); - dialogLayout->addWidget(horizontalLine); - dialogLayout->addWidget(createRunOptionsWidget()); + QVBoxLayout* layout = new QVBoxLayout(widget); + layout->addLayout(gridLayout); + layout->addWidget(horizontalLine); + layout->addWidget(createRunOptionsWidget()); + layout->addStretch(); return widget; } +/** + * Called to edit macro name + */ +void +WuQMacroDialog::macroNameEditButtonClicked() +{ + WuQMacro* macro = getSelectedMacro(); + if (macro != NULL) { + bool ok(false); + const QString text = QInputDialog::getText(this, + "New Macro Name", + "Name", + QLineEdit::Normal, + macro->getName(), + &ok); + if (ok) { + if ( ! text.isEmpty()) { + macro->setName(text); + WuQMacroManager::instance()->macroWasModified(macro); + updateMacroWidget(macro); + } + } + } +} + +/** + * Called when macro short cut key is selected + * + * @param shortCutKey + * Shortcut key that was selected + */ +void +WuQMacroDialog::macroShortCutKeySelected(const WuQMacroShortCutKeyEnum::Enum shortCutKey) +{ + WuQMacro* macro = getSelectedMacro(); + if (macro != NULL) { + macro->setShortCutKey(shortCutKey); + WuQMacroManager::instance()->macroWasModified(macro); + updateMacroWidget(macro); + } +} + +/** + * Called to edit macro description + */ +void +WuQMacroDialog::macroDescriptionEditButtonClicked() +{ + WuQMacro* macro = getSelectedMacro(); + if (macro != NULL) { + bool ok(false); + const QString text = QInputDialog::getMultiLineText(this, + "New Macro Description", + "Description", + macro->getDescription(), + &ok); + if (ok) { + if ( ! text.isEmpty()) { + macro->setDescription(text); + WuQMacroManager::instance()->macroWasModified(macro); + updateMacroWidget(macro); + } + } + } +} /** * @return New instance of widget containing macro run options @@ -331,16 +429,16 @@ WuQMacroDialog::buttonBoxButtonClicked(QAbstractButton* button) QWidget* WuQMacroDialog::createCommandWidget() { - QLabel* titleLabel = new QLabel("Title"); + QLabel* titleLabel = new QLabel("Title:"); m_commandTitleLabel = new QLabel(); - QLabel* nameLabel = new QLabel("Name"); + QLabel* nameLabel = new QLabel("Name:"); m_commandNameLabel = new QLabel(); - QLabel* typeLabel = new QLabel("Type"); + QLabel* typeLabel = new QLabel("Type:"); m_commandTypeLabel = new QLabel(); - QLabel* valueOneLabel = new QLabel("Value"); + QLabel* valueOneLabel = new QLabel("Value:"); m_commandValueOnePushButton = new QPushButton("Set..."); m_commandValueOnePushButton->setSizePolicy(QSizePolicy::Fixed, m_commandValueOnePushButton->sizePolicy().verticalPolicy()); @@ -348,7 +446,7 @@ WuQMacroDialog::createCommandWidget() [=] { setMacroCommandValue(ValueIndex::ONE); }); m_commandValueOneLabel = new QLabel(); - QLabel* valueTwoLabel = new QLabel("Alt Value"); + QLabel* valueTwoLabel = new QLabel("Alt Value:"); m_commandValueTwoPushButton = new QPushButton("Set..."); m_commandValueTwoPushButton->setSizePolicy(QSizePolicy::Fixed, m_commandValueTwoPushButton->sizePolicy().verticalPolicy()); @@ -356,7 +454,7 @@ WuQMacroDialog::createCommandWidget() [=] { setMacroCommandValue(ValueIndex::TWO); }); m_commandValueTwoLabel = new QLabel(); - QLabel* toolTipLabel = new QLabel("ToolTip"); + QLabel* toolTipLabel = new QLabel("ToolTip:"); m_commandToolTip = new QLabel(); m_commandToolTip->setWordWrap(true); @@ -514,11 +612,17 @@ WuQMacroDialog::macroGroupComboBoxActivated(int) void WuQMacroDialog::updateMacroWidget(WuQMacro* macro) { + QString name; + WuQMacroShortCutKeyEnum::Enum shortCutKey = WuQMacroShortCutKeyEnum::Key_None; QString text; if (macro != NULL) { + name = macro->getName(); text = macro->getDescription(); + shortCutKey = macro->getShortCutKey(); } + m_macroNameLabel->setText(name); + m_macroShortCutKeyComboBox->setSelectedShortCutKey(shortCutKey); m_macroDescriptionLabel->setText(text); } @@ -784,21 +888,6 @@ WuQMacroDialog::runSelectedMacro() } } -/** - * Called when the attributes button is clicked - */ -void -WuQMacroDialog::attributesButtonClicked() -{ - WuQMacro* macro = getSelectedMacro(); - if (macro != NULL) { - if (WuQMacroManager::instance()->editMacroAttributes(this, - macro)) { - updateDialogContents(); - } - } -} - /** * Called when the delete button is clicked */ @@ -972,7 +1061,7 @@ WuQMacroDialog::setMacroCommandValue(const ValueIndex valueIndex) } } - updateCommandWidget(macroCommand); + updateDialogContents(); } diff --git a/src/GuiQt/WuQMacroDialog.h b/src/GuiQt/WuQMacroDialog.h index e0e5e763a..afc4bb0fa 100644 --- a/src/GuiQt/WuQMacroDialog.h +++ b/src/GuiQt/WuQMacroDialog.h @@ -27,6 +27,7 @@ #include +#include "WuQMacroShortCutKeyEnum.h" #include "WuQMacroStandardItemTypeEnum.h" class QAbstractButton; @@ -44,6 +45,7 @@ namespace caret { class WuQMacro; class WuQMacroCommand; class WuQMacroGroup; + class WuQMacroShortCutKeyComboBox; class WuQMacroDialog : public QDialog { @@ -69,14 +71,18 @@ namespace caret { void buttonBoxButtonClicked(QAbstractButton* button); - void attributesButtonClicked(); - void deleteButtonClicked(); void importButtonClicked(); void exportButtonClicked(); + void macroNameEditButtonClicked(); + + void macroDescriptionEditButtonClicked(); + + void macroShortCutKeySelected(const WuQMacroShortCutKeyEnum::Enum); + void runOptionDelaySpinBoxValueChanged(float); void runOptionMoveMouseCheckBoxClicked(bool); @@ -123,6 +129,10 @@ namespace caret { QTreeView* m_treeView; + QLabel* m_macroNameLabel; + + WuQMacroShortCutKeyComboBox* m_macroShortCutKeyComboBox; + QLabel* m_macroDescriptionLabel; QWidget* m_macroWidget; @@ -137,8 +147,6 @@ namespace caret { QPushButton* m_deletePushButton; - QPushButton* m_attributesPushButton; - QPushButton* m_importPushButton; QPushButton* m_exportPushButton; diff --git a/src/GuiQt/WuQMacroManager.cxx b/src/GuiQt/WuQMacroManager.cxx index a5855c509..e467c97dc 100644 --- a/src/GuiQt/WuQMacroManager.cxx +++ b/src/GuiQt/WuQMacroManager.cxx @@ -46,7 +46,6 @@ #include "WuQMacroFile.h" #include "WuQMacroGroup.h" #include "WuQMacroMouseEventInfo.h" -#include "WuQMacroAttributesDialog.h" #include "WuQMacroHelperInterface.h" #include "WuQMacroExecutorOptions.h" #include "WuQMacroSignalWatcher.h" @@ -511,35 +510,6 @@ WuQMacroManager::addParentObject(QObject* parentObject) m_parentObjects.push_back(parentObject); } - -/** - * Edit a macros attributes (name, description) - * - * @param parent - * Parent widget for dialog - * @param macro - * Macro to edit - * @return - * True if macro was modified. - */ -bool -WuQMacroManager::editMacroAttributes(QWidget* parent, - WuQMacro* macro) -{ - if (macro != NULL) { - WuQMacroAttributesDialog attributesDialog(macro, - parent); - if (attributesDialog.exec() == WuQMacroDialog::Accepted) { - if (attributesDialog.isMacroModified()) { - macroWasModified(macro); - return true; - } - } - } - - return false; -} - /** * Can be called to indicate that a macro was modified * diff --git a/src/GuiQt/WuQMacroManager.h b/src/GuiQt/WuQMacroManager.h index 30e1bf5cd..fd8d76777 100644 --- a/src/GuiQt/WuQMacroManager.h +++ b/src/GuiQt/WuQMacroManager.h @@ -91,9 +91,6 @@ namespace caret { const WuQMacroExecutorOptions* getExecutorOptions() const; - bool editMacroAttributes(QWidget* parent, - WuQMacro* macro); - bool deleteMacro(QWidget* parent, WuQMacroGroup* macroGroup, WuQMacro* macro); -- GitLab From 6bfddc3285b5a6e318108388fe7ebe7c4bc86e3e Mon Sep 17 00:00:00 2001 From: John Harwell Date: Thu, 10 Jan 2019 15:12:23 -0600 Subject: [PATCH 167/427] WB-834, Macros: Add a descriptive, user-friendly name to all widgets that support macro commands. Display this user-friendly name in the macro dialog. --- src/Common/WuQMacroCommand.cxx | 55 ++++++++-- src/Common/WuQMacroCommand.h | 9 +- src/Common/WuQMacroGroupXmlFormatBase.h | 2 + src/Common/WuQMacroGroupXmlReader.cxx | 5 +- src/Common/WuQMacroGroupXmlWriter.cxx | 2 + .../AnnotationSelectionViewController.cxx | 12 ++- ...notationTextSubstitutionViewController.cxx | 3 +- src/GuiQt/BorderOptimizeDialog.cxx | 6 +- src/GuiQt/BorderSelectionViewController.cxx | 33 ++++-- src/GuiQt/BrainBrowserWindow.cxx | 9 +- .../BrainBrowserWindowOrientedToolBox.cxx | 9 +- src/GuiQt/BrainBrowserWindowToolBar.cxx | 101 ++++++++++++------ ...ainBrowserWindowToolBarChartAttributes.cxx | 18 ++-- .../BrainBrowserWindowToolBarChartAxes.cxx | 18 ++-- ...BrowserWindowToolBarChartTwoAttributes.cxx | 15 ++- .../BrainBrowserWindowToolBarChartTwoAxes.cxx | 57 ++++++---- ...rowserWindowToolBarChartTwoOrientation.cxx | 6 +- ...BrainBrowserWindowToolBarChartTwoTitle.cxx | 12 ++- .../BrainBrowserWindowToolBarChartTwoType.cxx | 3 +- .../BrainBrowserWindowToolBarChartType.cxx | 3 +- .../BrainBrowserWindowToolBarClipping.cxx | 21 ++-- .../BrainBrowserWindowToolBarSlicePlane.cxx | 31 ++++-- ...rainBrowserWindowToolBarSliceSelection.cxx | 40 ++++--- ...rainBrowserWindowToolBarSurfaceMontage.cxx | 78 +++++++++----- src/GuiQt/BrainBrowserWindowToolBarTab.cxx | 6 +- ...BrainBrowserWindowToolBarVolumeMontage.cxx | 18 ++-- src/GuiQt/BrainOpenGLWidget.cxx | 4 + src/GuiQt/CaretColorEnumComboBox.cxx | 6 +- src/GuiQt/CaretColorEnumComboBox.h | 1 + src/GuiQt/ChartHistoryViewController.cxx | 15 ++- src/GuiQt/ChartToolBoxViewController.cxx | 4 +- src/GuiQt/ChartTwoOverlayViewController.cxx | 78 +++++++++----- src/GuiQt/ChartTwoOverlayViewController.h | 9 +- .../CiftiConnectivityMatrixViewController.cxx | 14 ++- src/GuiQt/DisplayGroupEnumComboBox.cxx | 9 +- src/GuiQt/DisplayGroupEnumComboBox.h | 3 +- src/GuiQt/FociProjectionDialog.cxx | 9 +- src/GuiQt/FociSelectionViewController.cxx | 29 +++-- .../GroupAndNameHierarchyViewController.cxx | 17 ++- .../GroupAndNameHierarchyViewController.h | 4 +- src/GuiQt/GuiManager.cxx | 15 ++- src/GuiQt/ImageSelectionViewController.cxx | 31 ++++-- src/GuiQt/LabelSelectionViewController.cxx | 4 +- src/GuiQt/MapYokingGroupComboBox.cxx | 16 ++- src/GuiQt/MapYokingGroupComboBox.h | 3 +- src/GuiQt/OverlayViewController.cxx | 53 ++++++--- src/GuiQt/OverlayViewController.h | 1 + .../StructureSurfaceSelectionControl.cxx | 11 +- src/GuiQt/StructureSurfaceSelectionControl.h | 1 + src/GuiQt/SurfaceSelectionViewController.cxx | 31 ++++-- src/GuiQt/SurfaceSelectionViewController.h | 14 ++- .../VolumeSurfaceOutlineSetViewController.cxx | 12 ++- .../VolumeSurfaceOutlineSetViewController.h | 1 + .../VolumeSurfaceOutlineViewController.cxx | 22 +++- .../VolumeSurfaceOutlineViewController.h | 1 + src/GuiQt/WuQDataEntryDialog.cxx | 3 +- src/GuiQt/WuQMacroManager.cxx | 53 +++++++-- src/GuiQt/WuQMacroManager.h | 7 +- src/GuiQt/WuQMacroSignalWatcher.cxx | 9 ++ src/GuiQt/WuQMacroSignalWatcher.h | 4 + 60 files changed, 772 insertions(+), 294 deletions(-) diff --git a/src/Common/WuQMacroCommand.cxx b/src/Common/WuQMacroCommand.cxx index d2409c868..aa35c11dd 100644 --- a/src/Common/WuQMacroCommand.cxx +++ b/src/Common/WuQMacroCommand.cxx @@ -24,6 +24,7 @@ #undef __WU_Q_MACRO_COMMAND_DECLARE__ #include "CaretAssert.h" +#include "CaretLogger.h" #include "WuQMacroMouseEventInfo.h" #include "WuQMacroStandardItemTypeEnum.h" @@ -42,15 +43,23 @@ using namespace caret; * Type of object class * @param objectName * Name of object + * @param objectDescriptiveName + * Descriptive name of macro command */ WuQMacroCommand::WuQMacroCommand(const WuQMacroClassTypeEnum::Enum classType, - const QString& objectName) + const QString& objectName, + const QString& objectDescriptiveName) : WuQMacroCommand(classType, objectName, + objectDescriptiveName, "", "", "") { + if (objectDescriptiveName.isEmpty()) { + CaretLogWarning("Empty descriptive name for " + + objectName); + } } /** @@ -60,6 +69,8 @@ WuQMacroCommand::WuQMacroCommand(const WuQMacroClassTypeEnum::Enum classType, * Type of object class * @param objectName * Name of object + * @param objectDescriptiveName + * Descriptive name of macro command * @param objectToolTip * ToolTip for object * @param dataValue @@ -69,6 +80,7 @@ WuQMacroCommand::WuQMacroCommand(const WuQMacroClassTypeEnum::Enum classType, */ WuQMacroCommand::WuQMacroCommand(const WuQMacroClassTypeEnum::Enum classType, const QString& objectName, + const QString& objectDescriptiveName, const QString& objectToolTip, const QVariant dataValue, const QVariant dataValueTwo) @@ -76,10 +88,16 @@ WuQMacroCommand::WuQMacroCommand(const WuQMacroClassTypeEnum::Enum classType, TracksModificationInterface(), m_classType(classType), m_objectName(objectName), +m_descriptiveName(objectDescriptiveName), m_dataValue(dataValue), m_dataValueTwo(dataValueTwo), m_macroMouseEvent(NULL) { + if (objectDescriptiveName.isEmpty()) { + CaretLogWarning("Empty descriptive name for " + + objectName); + } + setFlags(Qt::ItemIsEnabled | Qt::ItemIsSelectable); setToolTip(objectToolTip); @@ -169,23 +187,30 @@ m_macroMouseEvent(NULL) /** * Constructor for a macro command containing a mouse event * - * @param objectType - * Type of object * @param objectName * Name of object - * @param value - * Value for the command. + * @param objectDescriptiveName + * Descriptive name of macro command + * @param mouseEventInfo + * Information about mouse event. */ WuQMacroCommand::WuQMacroCommand(const QString& objectName, + const QString& objectDescriptiveName, WuQMacroMouseEventInfo* mouseEventInfo) : QStandardItem(), TracksModificationInterface(), m_classType(WuQMacroClassTypeEnum::MOUSE_USER_EVENT), m_objectName(objectName), +m_descriptiveName(objectDescriptiveName), m_dataValue((int)0), m_dataValueTwo(""), m_macroMouseEvent(mouseEventInfo) { + if (objectDescriptiveName.isEmpty()) { + CaretLogWarning("Empty descriptive name for " + + objectName); + } + setFlags(Qt::ItemIsEnabled | Qt::ItemIsSelectable); m_dataType = WuQMacroDataValueTypeEnum::MOUSE; @@ -386,9 +411,14 @@ WuQMacroCommand::updateTitle() break; } - setText(title - + " " - + m_objectName); + if ( ! m_descriptiveName.isEmpty()) { + setText(m_descriptiveName); + } + else { + setText(title + + " " + + m_objectName); + } } /** @@ -427,6 +457,15 @@ WuQMacroCommand::getObjectName() const return m_objectName; } +/** + * @return The object's descriptive name + */ +QString +WuQMacroCommand::getDescriptiveName() const +{ + return m_descriptiveName; +} + /** * @return ToolTip for the object */ diff --git a/src/Common/WuQMacroCommand.h b/src/Common/WuQMacroCommand.h index cd897a4a4..fe07e77ac 100644 --- a/src/Common/WuQMacroCommand.h +++ b/src/Common/WuQMacroCommand.h @@ -44,14 +44,17 @@ namespace caret { WuQMacroCommand(const WuQMacroClassTypeEnum::Enum objectClassType, const QString& objectName, + const QString& objectDescriptiveName, const QString& objectToolTip, const QVariant dataValue, const QVariant dataValueTwo); WuQMacroCommand(const WuQMacroClassTypeEnum::Enum objectClassType, - const QString& objectName); + const QString& objectName, + const QString& objectDescriptiveName); WuQMacroCommand(const QString& objectName, + const QString& objectDescriptiveName, WuQMacroMouseEventInfo* mouseEventInfo); virtual ~WuQMacroCommand(); @@ -64,6 +67,8 @@ namespace caret { QString getObjectName() const; + QString getDescriptiveName() const; + QString getObjectToolTip() const; void setObjectToolTip(const QString& objectToolTip); @@ -111,6 +116,8 @@ namespace caret { QString m_objectName; + QString m_descriptiveName; + QVariant m_dataValue; WuQMacroDataValueTypeEnum::Enum m_dataType; diff --git a/src/Common/WuQMacroGroupXmlFormatBase.h b/src/Common/WuQMacroGroupXmlFormatBase.h index 147020e9d..750dad83c 100644 --- a/src/Common/WuQMacroGroupXmlFormatBase.h +++ b/src/Common/WuQMacroGroupXmlFormatBase.h @@ -54,6 +54,7 @@ namespace caret { static const QString ATTRIBUTE_OBJECT_CLASS; static const QString ATTRIBUTE_OBJECT_DATA_TYPE; static const QString ATTRIBUTE_OBJECT_DATA_TYPE_TWO; + static const QString ATTRIBUTE_OBJECT_DESCRIPTIVE_NAME; static const QString ATTRIBUTE_OBJECT_VALUE; static const QString ATTRIBUTE_OBJECT_VALUE_TWO; static const QString ATTRIBUTE_SHORT_CUT_KEY; @@ -95,6 +96,7 @@ namespace caret { const QString WuQMacroGroupXmlFormatBase::ATTRIBUTE_OBJECT_CLASS = "ObjectClass"; const QString WuQMacroGroupXmlFormatBase::ATTRIBUTE_OBJECT_DATA_TYPE = "DataType"; const QString WuQMacroGroupXmlFormatBase::ATTRIBUTE_OBJECT_DATA_TYPE_TWO = "DataTypeTwo"; + const QString WuQMacroGroupXmlFormatBase::ATTRIBUTE_OBJECT_DESCRIPTIVE_NAME = "DescriptiveName"; const QString WuQMacroGroupXmlFormatBase::ATTRIBUTE_OBJECT_VALUE = "DataValue"; const QString WuQMacroGroupXmlFormatBase::ATTRIBUTE_OBJECT_VALUE_TWO = "DataValueTwo"; const QString WuQMacroGroupXmlFormatBase::ATTRIBUTE_SHORT_CUT_KEY = "ShortCutKey"; diff --git a/src/Common/WuQMacroGroupXmlReader.cxx b/src/Common/WuQMacroGroupXmlReader.cxx index 99e458469..0648b7974 100644 --- a/src/Common/WuQMacroGroupXmlReader.cxx +++ b/src/Common/WuQMacroGroupXmlReader.cxx @@ -293,6 +293,7 @@ WuQMacroGroupXmlReader::readMacroCommandAttributesVersionOne() const QXmlStreamAttributes attributes = m_xmlStreamReader->attributes(); const QStringRef objectName = attributes.value(ATTRIBUTE_NAME); const QStringRef dataTypeString = attributes.value(ATTRIBUTE_OBJECT_DATA_TYPE); + const QStringRef descriptiveNameString = attributes.value(ATTRIBUTE_OBJECT_DESCRIPTIVE_NAME); const QStringRef classString = attributes.value(ATTRIBUTE_OBJECT_CLASS); const QStringRef valueString = attributes.value(ATTRIBUTE_OBJECT_VALUE); const QStringRef dataTypeTwoString = attributes.value(ATTRIBUTE_OBJECT_DATA_TYPE_TWO); @@ -354,7 +355,8 @@ WuQMacroGroupXmlReader::readMacroCommandAttributesVersionOne() if (objectClass == WuQMacroClassTypeEnum::MOUSE_USER_EVENT) { macroCommand = new WuQMacroCommand(WuQMacroClassTypeEnum::MOUSE_USER_EVENT, - objectName.toString()); + objectName.toString(), + descriptiveNameString.toString()); } else { QVariant value; @@ -421,6 +423,7 @@ WuQMacroGroupXmlReader::readMacroCommandAttributesVersionOne() QString tooltip; macroCommand = new WuQMacroCommand(objectClass, objectName.toString(), + descriptiveNameString.toString(), tooltip, value, valueTwo); diff --git a/src/Common/WuQMacroGroupXmlWriter.cxx b/src/Common/WuQMacroGroupXmlWriter.cxx index 7347c8f66..b60bc0387 100644 --- a/src/Common/WuQMacroGroupXmlWriter.cxx +++ b/src/Common/WuQMacroGroupXmlWriter.cxx @@ -140,6 +140,8 @@ WuQMacroGroupXmlWriter::writeMacroCommandToXML(const WuQMacroCommand* macroComma macroCommand->getObjectName()); m_xmlStreamWriter->writeAttribute(ATTRIBUTE_OBJECT_CLASS, WuQMacroClassTypeEnum::toName(macroCommand->getClassType())); + m_xmlStreamWriter->writeAttribute(ATTRIBUTE_OBJECT_DESCRIPTIVE_NAME, + macroCommand->getDescriptiveName()); { m_xmlStreamWriter->writeAttribute(ATTRIBUTE_OBJECT_DATA_TYPE, WuQMacroDataValueTypeEnum::toName(macroCommand->getDataType())); diff --git a/src/GuiQt/AnnotationSelectionViewController.cxx b/src/GuiQt/AnnotationSelectionViewController.cxx index 7bf38dc8a..0bd1a7a20 100644 --- a/src/GuiQt/AnnotationSelectionViewController.cxx +++ b/src/GuiQt/AnnotationSelectionViewController.cxx @@ -78,7 +78,8 @@ m_browserWindowIndex(browserWindowIndex) m_displayGroupComboBox = new DisplayGroupEnumComboBox(this, (objectNamePrefix - + ":DisplayGroup")); + + ":DisplayGroup"), + "annotations"); QObject::connect(m_displayGroupComboBox, SIGNAL(displayGroupSelected(const DisplayGroupEnum::Enum)), this, SLOT(displayGroupSelected(const DisplayGroupEnum::Enum))); @@ -96,7 +97,8 @@ m_browserWindowIndex(browserWindowIndex) this, SLOT(checkBoxToggled())); m_displayAnnotationsCheckBox->setObjectName(objectNamePrefix + "DisplayAnnotatons"); - macroManager->addMacroSupportToObject(m_displayAnnotationsCheckBox); + macroManager->addMacroSupportToObject(m_displayAnnotationsCheckBox, + "Enable display of annotations"); m_displayTextAnnotationsCheckBox = new QCheckBox("Display Text Annotations"); @@ -105,7 +107,8 @@ m_browserWindowIndex(browserWindowIndex) this, SLOT(checkBoxToggled())); m_displayTextAnnotationsCheckBox->setObjectName(objectNamePrefix + "DisplayTextAnnotatons"); - macroManager->addMacroSupportToObject(m_displayTextAnnotationsCheckBox); + macroManager->addMacroSupportToObject(m_displayTextAnnotationsCheckBox, + "Enable display of text annotations"); m_displayWindowAnnotationInSingleTabViewsCheckBox = new QCheckBox("Show Window " + QString::number(m_browserWindowIndex + 1) @@ -117,7 +120,8 @@ m_browserWindowIndex(browserWindowIndex) this, SLOT(checkBoxToggled())); m_displayWindowAnnotationInSingleTabViewsCheckBox->setObjectName(objectNamePrefix + "DisplayWindowAnnotatonsInSingleTabView"); - macroManager->addMacroSupportToObject(m_displayWindowAnnotationInSingleTabViewsCheckBox); + macroManager->addMacroSupportToObject(m_displayWindowAnnotationInSingleTabViewsCheckBox, + "Enable display window annotations in single tab view"); m_sceneAssistant = new SceneClassAssistant(); diff --git a/src/GuiQt/AnnotationTextSubstitutionViewController.cxx b/src/GuiQt/AnnotationTextSubstitutionViewController.cxx index 17e8a05fb..0a3c71c31 100644 --- a/src/GuiQt/AnnotationTextSubstitutionViewController.cxx +++ b/src/GuiQt/AnnotationTextSubstitutionViewController.cxx @@ -86,7 +86,8 @@ m_browserWindowIndex(browserWindowIndex) m_enableSubstitutionsCheckBox->setObjectName(parentObjectName + ":Substitutions:Enable"); m_enableSubstitutionsCheckBox->setToolTip("Enable Text Annotation Substitutions"); - WuQMacroManager::instance()->addMacroSupportToObject(m_enableSubstitutionsCheckBox); + WuQMacroManager::instance()->addMacroSupportToObject(m_enableSubstitutionsCheckBox, + "Enable annotation subsitutions"); QButtonGroup* buttonGroup = new QButtonGroup(); diff --git a/src/GuiQt/BorderOptimizeDialog.cxx b/src/GuiQt/BorderOptimizeDialog.cxx index 363689f75..88ca9d4a5 100644 --- a/src/GuiQt/BorderOptimizeDialog.cxx +++ b/src/GuiQt/BorderOptimizeDialog.cxx @@ -1018,7 +1018,8 @@ BorderOptimizeDialog::createSurfaceSelectionWidget() { m_surfaceSelectionControl = new SurfaceSelectionViewController(this, (m_objectNamePrefix - + ":SurfaceSelectionComboBox")); + + ":SurfaceSelectionComboBox"), + "border sampling"); QObject::connect(m_surfaceSelectionControl, SIGNAL(surfaceSelected(Surface*)), this, SLOT(gradientComputatonSurfaceSelected(Surface*))); @@ -1040,7 +1041,8 @@ BorderOptimizeDialog::createSphericalUpsamplingWidget() QLabel* surfaceLabel = new QLabel("Current Sphere"); m_upsamplingSurfaceSelectionControl = new SurfaceSelectionViewController(this, (m_objectNamePrefix - + ":SphericalSurfaceSelectionComboBox")); + + ":UpSamplingSurfaceSelection"), + "border upsampling"); QLabel* resolutionLabel = new QLabel("Upsampling Resolution"); m_upsamplingResolutionSpinBox = new QSpinBox(); diff --git a/src/GuiQt/BorderSelectionViewController.cxx b/src/GuiQt/BorderSelectionViewController.cxx index 4160c4a72..862614eed 100644 --- a/src/GuiQt/BorderSelectionViewController.cxx +++ b/src/GuiQt/BorderSelectionViewController.cxx @@ -102,7 +102,8 @@ m_objectNamePrefix(parentObjectName m_bordersDisplayCheckBox->setObjectName(m_objectNamePrefix + ":DisplayBorders"); m_bordersDisplayCheckBox->setToolTip("Display Borders on Surfaces"); - macroManager->addMacroSupportToObject(m_bordersDisplayCheckBox); + macroManager->addMacroSupportToObject(m_bordersDisplayCheckBox, + "Enable borders display"); QWidget* attributesWidget = this->createAttributesWidget(); QWidget* selectionWidget = this->createSelectionWidget(); @@ -116,7 +117,9 @@ m_objectNamePrefix(parentObjectName m_tabWidget->setCurrentWidget(attributesWidget); m_tabWidget->getTabBar()->setObjectName(m_objectNamePrefix + ":Tab"); - macroManager->addMacroSupportToObjectWithToolTip(m_tabWidget->getTabBar(), ""); + macroManager->addMacroSupportToObjectWithToolTip(m_tabWidget->getTabBar(), + "Features ToolBox Borders Tab", + "Select tab"); QVBoxLayout* layout = new QVBoxLayout(this); layout->addWidget(m_bordersDisplayCheckBox); @@ -147,6 +150,7 @@ BorderSelectionViewController::createSelectionWidget() m_borderClassNameHierarchyViewController = new GroupAndNameHierarchyViewController(m_browserWindowIndex, (m_objectNamePrefix + ":Selection"), + "borders", this); return m_borderClassNameHierarchyViewController; @@ -166,7 +170,8 @@ BorderSelectionViewController::createAttributesWidget() m_bordersContralateralCheckBox->setObjectName(m_objectNamePrefix + ":Contralateral"); m_bordersContralateralCheckBox->setToolTip("Show Contralateral Borders"); - macroManager->addMacroSupportToObject(m_bordersContralateralCheckBox); + macroManager->addMacroSupportToObject(m_bordersContralateralCheckBox, + "Enable contralateral borders display"); std::vector drawingTypeEnums; BorderDrawingTypeEnum::getAllEnums(drawingTypeEnums); @@ -186,7 +191,8 @@ BorderSelectionViewController::createAttributesWidget() this, SLOT(processAttributesChanges())); m_drawTypeComboBox->setObjectName(m_objectNamePrefix + ":DrawingType"); - macroManager->addMacroSupportToObject(m_drawTypeComboBox); + macroManager->addMacroSupportToObject(m_drawTypeComboBox, + "Select border drawing style"); QLabel* coloringLabel = new QLabel("Coloring"); m_coloringTypeComboBox = new EnumComboBoxTemplate(this); @@ -197,13 +203,15 @@ BorderSelectionViewController::createAttributesWidget() this, SLOT(processAttributesChanges())); m_coloringTypeComboBox->getWidget()->setObjectName(m_objectNamePrefix + ":ColoringType"); - macroManager->addMacroSupportToObject(m_coloringTypeComboBox->getWidget()); + macroManager->addMacroSupportToObject(m_coloringTypeComboBox->getWidget(), + "Select border coloring type"); QLabel* standardColorLabel = new QLabel("Standard Color"); m_standardColorComboBox = new CaretColorEnumComboBox("", QIcon(), (m_objectNamePrefix + ":Color"), + "Set border standard color", this); m_standardColorComboBox->getWidget()->setToolTip("Select the standard color"); QObject::connect(m_standardColorComboBox, SIGNAL(colorSelected(const CaretColorEnum::Enum)), @@ -231,7 +239,8 @@ BorderSelectionViewController::createAttributesWidget() this, SLOT(processAttributesChanges())); m_lineWidthSpinBox->setObjectName(m_objectNamePrefix + ":LineWidth"); - macroManager->addMacroSupportToObject(m_lineWidthSpinBox); + macroManager->addMacroSupportToObject(m_lineWidthSpinBox, + "Set border line width"); QLabel* pointSizeLabel = new QLabel("Symbol Diameter"); m_pointSizeSpinBox = WuQFactory::newDoubleSpinBox(); @@ -246,7 +255,8 @@ BorderSelectionViewController::createAttributesWidget() this, SLOT(processAttributesChanges())); m_pointSizeSpinBox->setObjectName(m_objectNamePrefix + ":PointSize"); - macroManager->addMacroSupportToObject(m_pointSizeSpinBox); + macroManager->addMacroSupportToObject(m_pointSizeSpinBox, + "Set border point size"); m_enableUnstretchedLinesCheckBox = new QCheckBox("Unstretched Lines"); QObject::connect(m_enableUnstretchedLinesCheckBox, SIGNAL(clicked(bool)), @@ -254,7 +264,8 @@ BorderSelectionViewController::createAttributesWidget() m_enableUnstretchedLinesCheckBox->setToolTip("Disable lines that are invalid due to cuts in flat surface"); m_enableUnstretchedLinesCheckBox->setObjectName(m_objectNamePrefix + ":UnstretchedLines"); - macroManager->addMacroSupportToObject(m_enableUnstretchedLinesCheckBox); + macroManager->addMacroSupportToObject(m_enableUnstretchedLinesCheckBox, + "Enable border unstretched lines"); m_unstretchedLinesLengthSpinBox = WuQFactory::newDoubleSpinBox(); m_unstretchedLinesLengthSpinBox->setFixedWidth(80); @@ -269,7 +280,8 @@ BorderSelectionViewController::createAttributesWidget() this, SLOT(processAttributesChanges())); m_unstretchedLinesLengthSpinBox->setObjectName(m_objectNamePrefix + ":UnstretchedLinesLength"); - macroManager->addMacroSupportToObject(m_unstretchedLinesLengthSpinBox); + macroManager->addMacroSupportToObject(m_unstretchedLinesLengthSpinBox, + "Set border unstretched lines lenght"); QLabel* aboveSurfaceLabel = new QLabel("Above Offset"); m_aboveSurfaceOffsetSpinBox =WuQFactory::newDoubleSpinBox(); @@ -284,7 +296,8 @@ BorderSelectionViewController::createAttributesWidget() this, SLOT(processAttributesChanges())); m_aboveSurfaceOffsetSpinBox->setObjectName(m_objectNamePrefix + ":AboveSurfaceOffset"); - macroManager->addMacroSupportToObject(m_aboveSurfaceOffsetSpinBox); + macroManager->addMacroSupportToObject(m_aboveSurfaceOffsetSpinBox, + "Set border above surface offset"); QWidget* gridWidget = new QWidget(); QGridLayout* gridLayout = new QGridLayout(gridWidget); diff --git a/src/GuiQt/BrainBrowserWindow.cxx b/src/GuiQt/BrainBrowserWindow.cxx index f667a3ead..c748c0ba2 100644 --- a/src/GuiQt/BrainBrowserWindow.cxx +++ b/src/GuiQt/BrainBrowserWindow.cxx @@ -835,7 +835,8 @@ BrainBrowserWindow::createActionsUsedByToolBar() } m_overlayToolBoxAction->setObjectName(m_objectNamePrefix + ":ToolBar:ShowOverlayToolBox"); - WuQMacroManager::instance()->addMacroSupportToObject(m_overlayToolBoxAction); + WuQMacroManager::instance()->addMacroSupportToObject(m_overlayToolBoxAction, + ("Show overlay toolbox in window " + QString::number(m_browserWindowIndex + 1))); /* * Note: The name of a dock widget becomes its @@ -856,7 +857,8 @@ BrainBrowserWindow::createActionsUsedByToolBar() } m_featuresToolBoxAction->setObjectName(m_objectNamePrefix + ":ToolBar:ShowFeaturesToolBox"); - WuQMacroManager::instance()->addMacroSupportToObject(m_featuresToolBoxAction); + WuQMacroManager::instance()->addMacroSupportToObject(m_featuresToolBoxAction, + ("Show features toolbox in window " + QString::number(m_browserWindowIndex + 1))); m_windowMenuLockWindowAspectRatioAction = new QAction(this); m_windowMenuLockWindowAspectRatioAction->setCheckable(true); @@ -894,7 +896,8 @@ BrainBrowserWindow::createActionsUsedByToolBar() this, &BrainBrowserWindow::processToolBarLockWindowAndAllTabAspectTriggered); m_toolBarLockWindowAndAllTabAspectRatioAction->setObjectName(m_objectNamePrefix + ":ToolBar:LockAspectRatio"); - WuQMacroManager::instance()->addMacroSupportToObject(m_toolBarLockWindowAndAllTabAspectRatioAction); + WuQMacroManager::instance()->addMacroSupportToObject(m_toolBarLockWindowAndAllTabAspectRatioAction, + ("Lock aspect ratio in window " + QString::number(m_browserWindowIndex + 1))); /* * Button for locking aspect is passed to ToolBar's constructor diff --git a/src/GuiQt/BrainBrowserWindowOrientedToolBox.cxx b/src/GuiQt/BrainBrowserWindowOrientedToolBox.cxx index 4f4363bee..70acb9d02 100644 --- a/src/GuiQt/BrainBrowserWindowOrientedToolBox.cxx +++ b/src/GuiQt/BrainBrowserWindowOrientedToolBox.cxx @@ -171,7 +171,7 @@ BrainBrowserWindowOrientedToolBox::BrainBrowserWindowOrientedToolBox(const int32 #endif m_tabWidget->setObjectName(objectNamePrefix + ":Tab"); - macroManager->addMacroSupportToObjectWithToolTip(m_tabWidget, ""); + macroManager->addMacroSupportToObjectWithToolTip(m_tabWidget, "Toolbox tab", ""); m_annotationTabIndex = -1; m_borderTabIndex = -1; @@ -228,7 +228,9 @@ BrainBrowserWindowOrientedToolBox::BrainBrowserWindowOrientedToolBox(const int32 m_annotationTabWidget->addTab(m_annotationTextSubstitutionViewController, "Substitutions"); m_annotationTabWidget->setObjectName(objectNamePrefix + ":AnnotationTab"); - macroManager->addMacroSupportToObjectWithToolTip(m_annotationTabWidget, ""); + macroManager->addMacroSupportToObjectWithToolTip(m_annotationTabWidget, + "Features ToolBox Annotation Tab", + ""); m_annotationTabIndex = addToTabWidget(m_annotationTabWidget, "Annot"); @@ -276,7 +278,8 @@ BrainBrowserWindowOrientedToolBox::BrainBrowserWindowOrientedToolBox(const int32 if (isOverlayToolBox) { m_volumeSurfaceOutlineSetViewController = new VolumeSurfaceOutlineSetViewController(orientation, m_browserWindowIndex, - objectNamePrefix); + objectNamePrefix, + "toolbox"); m_volumeSurfaceOutlineTabIndex = addToTabWidget(m_volumeSurfaceOutlineSetViewController, "Vol/Surf Outline"); } diff --git a/src/GuiQt/BrainBrowserWindowToolBar.cxx b/src/GuiQt/BrainBrowserWindowToolBar.cxx index 2d44a4bc2..0a5c3c0ae 100644 --- a/src/GuiQt/BrainBrowserWindowToolBar.cxx +++ b/src/GuiQt/BrainBrowserWindowToolBar.cxx @@ -243,6 +243,7 @@ BrainBrowserWindowToolBar::BrainBrowserWindowToolBar(const int32_t browserWindow + ":Tab"); WuQMacroManager::instance()->addMacroSupportToObjectWithToolTip(qobject_cast(this->tabBar), + "ToolBar Tab", "Select Tab"); /* @@ -268,7 +269,8 @@ BrainBrowserWindowToolBar::BrainBrowserWindowToolBar(const int32_t browserWindow SLOT(customViewActionTriggered())); this->customViewAction->setObjectName(m_objectNamePrefix + ":CustomView"); - WuQMacroManager::instance()->addMacroSupportToObject(this->customViewAction); + WuQMacroManager::instance()->addMacroSupportToObject(this->customViewAction, + "Display custom view dialog"); /* * Actions at right side of toolbar @@ -313,7 +315,8 @@ BrainBrowserWindowToolBar::BrainBrowserWindowToolBar(const int32_t browserWindow toolBarToolButton->setDefaultAction(this->toolBarToolButtonAction); toolBarToolButtonAction->setObjectName(m_objectNamePrefix + ":ShowToolBar"); - WuQMacroManager::instance()->addMacroSupportToObject(toolBarToolButtonAction); + WuQMacroManager::instance()->addMacroSupportToObject(toolBarToolButtonAction, + "Show toolbar"); /* * Toolbox control at right of the tab bar @@ -1944,37 +1947,43 @@ BrainBrowserWindowToolBar::createViewWidget() this->viewModeChartOneRadioButton->setToolTip("Show Old Chart View"); this->viewModeChartOneRadioButton->setObjectName(objectNamePrefix + ":ChartOld"); - macroManager->addMacroSupportToObject(this->viewModeChartOneRadioButton); + macroManager->addMacroSupportToObject(this->viewModeChartOneRadioButton, + "Select Chart Old View"); this->viewModeChartTwoRadioButton = new QRadioButton("Chart"); this->viewModeChartTwoRadioButton->setToolTip("Show Chart View"); this->viewModeChartTwoRadioButton->setObjectName(objectNamePrefix + ":Chart"); - macroManager->addMacroSupportToObject(this->viewModeChartTwoRadioButton); + macroManager->addMacroSupportToObject(this->viewModeChartTwoRadioButton, + "Select Chart View"); this->viewModeSurfaceRadioButton = new QRadioButton("Surface"); this->viewModeSurfaceRadioButton->setToolTip("Show Surace View"); this->viewModeSurfaceRadioButton->setObjectName(objectNamePrefix + ":Surface"); - macroManager->addMacroSupportToObject(this->viewModeSurfaceRadioButton); + macroManager->addMacroSupportToObject(this->viewModeSurfaceRadioButton, + "Select surface view"); this->viewModeSurfaceMontageRadioButton = new QRadioButton("Montage"); this->viewModeSurfaceMontageRadioButton->setToolTip("Show Montage View"); this->viewModeSurfaceMontageRadioButton->setObjectName(objectNamePrefix + ":Montage"); - macroManager->addMacroSupportToObject(this->viewModeSurfaceMontageRadioButton); + macroManager->addMacroSupportToObject(this->viewModeSurfaceMontageRadioButton, + "Select surface montage view"); this->viewModeVolumeRadioButton = new QRadioButton("Volume"); this->viewModeVolumeRadioButton->setToolTip("Show Volume View"); this->viewModeVolumeRadioButton->setObjectName(objectNamePrefix + ":Volume"); - macroManager->addMacroSupportToObject(this->viewModeVolumeRadioButton); + macroManager->addMacroSupportToObject(this->viewModeVolumeRadioButton, + "Select volume view"); this->viewModeWholeBrainRadioButton = new QRadioButton("All"); this->viewModeWholeBrainRadioButton->setToolTip("Show All View"); this->viewModeWholeBrainRadioButton->setObjectName(objectNamePrefix + ":All"); - macroManager->addMacroSupportToObject(this->viewModeWholeBrainRadioButton); + macroManager->addMacroSupportToObject(this->viewModeWholeBrainRadioButton, + "Select all view"); QWidget* widget = new QWidget(); QVBoxLayout* layout = new QVBoxLayout(widget); @@ -2119,7 +2128,8 @@ BrainBrowserWindowToolBar::createOrientationWidget() } this->orientationLeftOrLateralToolButtonAction->setObjectName(objectNamePrefix + "LeftOrLateralView"); - macroManager->addMacroSupportToObject(this->orientationLeftOrLateralToolButtonAction); + macroManager->addMacroSupportToObject(this->orientationLeftOrLateralToolButtonAction, + "Select left or lateral orientation"); this->orientationRightOrMedialToolButtonAction = WuQtUtilities::createAction("R", "View from a RIGHT perspective", @@ -2134,7 +2144,8 @@ BrainBrowserWindowToolBar::createOrientationWidget() } this->orientationRightOrMedialToolButtonAction->setObjectName(objectNamePrefix + "RightOrMedialView"); - macroManager->addMacroSupportToObject(this->orientationRightOrMedialToolButtonAction); + macroManager->addMacroSupportToObject(this->orientationRightOrMedialToolButtonAction, + "Select right or medial orientation"); this->orientationAnteriorToolButtonAction = WuQtUtilities::createAction("A", "View from an ANTERIOR perspective", @@ -2149,7 +2160,8 @@ BrainBrowserWindowToolBar::createOrientationWidget() } this->orientationAnteriorToolButtonAction->setObjectName(objectNamePrefix + "AnteriorView"); - macroManager->addMacroSupportToObject(this->orientationAnteriorToolButtonAction); + macroManager->addMacroSupportToObject(this->orientationAnteriorToolButtonAction, + "Select anterior orientation"); this->orientationPosteriorToolButtonAction = WuQtUtilities::createAction("P", "View from a POSTERIOR perspective", @@ -2164,7 +2176,8 @@ BrainBrowserWindowToolBar::createOrientationWidget() } this->orientationPosteriorToolButtonAction->setObjectName(objectNamePrefix + "PosteriorView"); - macroManager->addMacroSupportToObject(this->orientationPosteriorToolButtonAction); + macroManager->addMacroSupportToObject(this->orientationPosteriorToolButtonAction, + "Select posterior orientation"); this->orientationDorsalToolButtonAction = WuQtUtilities::createAction("D", "View from a DORSAL perspective", @@ -2179,7 +2192,8 @@ BrainBrowserWindowToolBar::createOrientationWidget() } this->orientationDorsalToolButtonAction->setObjectName(objectNamePrefix + "DorsalView"); - macroManager->addMacroSupportToObject(this->orientationDorsalToolButtonAction); + macroManager->addMacroSupportToObject(this->orientationDorsalToolButtonAction, + "Select dorsal orientation"); this->orientationVentralToolButtonAction = WuQtUtilities::createAction("V", "View from a VENTRAL perspective", @@ -2194,7 +2208,8 @@ BrainBrowserWindowToolBar::createOrientationWidget() } this->orientationVentralToolButtonAction->setObjectName(objectNamePrefix + "VentralView"); - macroManager->addMacroSupportToObject(this->orientationVentralToolButtonAction); + macroManager->addMacroSupportToObject(this->orientationVentralToolButtonAction, + "Select ventral orientation"); this->orientationLateralMedialToolButtonAction = WuQtUtilities::createAction("LM", @@ -2204,7 +2219,8 @@ BrainBrowserWindowToolBar::createOrientationWidget() SLOT(orientationLateralMedialToolButtonTriggered(bool))); this->orientationLateralMedialToolButtonAction->setObjectName(objectNamePrefix + "LateralMedialView"); - macroManager->addMacroSupportToObject(this->orientationLateralMedialToolButtonAction); + macroManager->addMacroSupportToObject(this->orientationLateralMedialToolButtonAction, + "Select lateral/medial orientation"); this->orientationDorsalVentralToolButtonAction = WuQtUtilities::createAction("DV", "View from a Dorsal/Ventral perspective", @@ -2213,7 +2229,8 @@ BrainBrowserWindowToolBar::createOrientationWidget() SLOT(orientationDorsalVentralToolButtonTriggered(bool))); this->orientationDorsalVentralToolButtonAction->setObjectName(objectNamePrefix + "DorsalVentralView"); - macroManager->addMacroSupportToObject(this->orientationDorsalVentralToolButtonAction); + macroManager->addMacroSupportToObject(this->orientationDorsalVentralToolButtonAction, + "Select dorsal/ventral orientation"); this->orientationAnteriorPosteriorToolButtonAction = WuQtUtilities::createAction("AP", "View from a Anterior/Posterior perspective", @@ -2222,7 +2239,8 @@ BrainBrowserWindowToolBar::createOrientationWidget() SLOT(orientationAnteriorPosteriorToolButtonTriggered(bool))); this->orientationAnteriorPosteriorToolButtonAction->setObjectName(objectNamePrefix + "AnteriorPosteriorView"); - macroManager->addMacroSupportToObject(this->orientationAnteriorPosteriorToolButtonAction); + macroManager->addMacroSupportToObject(this->orientationAnteriorPosteriorToolButtonAction, + "Select anterior/posterior orientation"); this->orientationResetToolButtonAction = WuQtUtilities::createAction("R\nE\nS\nE\nT", "Reset the view to dorsal and remove any panning or zooming", @@ -2231,7 +2249,8 @@ BrainBrowserWindowToolBar::createOrientationWidget() SLOT(orientationResetToolButtonTriggered(bool))); this->orientationResetToolButtonAction->setObjectName(objectNamePrefix + "ResetView"); - macroManager->addMacroSupportToObject(this->orientationResetToolButtonAction); + macroManager->addMacroSupportToObject(this->orientationResetToolButtonAction, + "Reset to default orientation"); this->orientationLeftOrLateralToolButton = new QToolButton(); this->orientationLeftOrLateralToolButton->setDefaultAction(this->orientationLeftOrLateralToolButtonAction); @@ -2503,7 +2522,8 @@ BrainBrowserWindowToolBar::createWholeBrainSurfaceOptionsWidget() this, SLOT(wholeBrainSurfaceTypeComboBoxIndexChanged(int))); this->wholeBrainSurfaceTypeComboBox->setObjectName(objectNamePrefix + "SurfaceType"); - macroManager->addMacroSupportToObject(this->wholeBrainSurfaceTypeComboBox); + macroManager->addMacroSupportToObject(this->wholeBrainSurfaceTypeComboBox, + "Select all view surface type"); /* * Left @@ -2515,7 +2535,8 @@ BrainBrowserWindowToolBar::createWholeBrainSurfaceOptionsWidget() this, SLOT(wholeBrainSurfaceLeftCheckBoxStateChanged(int))); this->wholeBrainSurfaceLeftCheckBox->setObjectName(objectNamePrefix + "EnableLeft"); - macroManager->addMacroSupportToObject(this->wholeBrainSurfaceLeftCheckBox); + macroManager->addMacroSupportToObject(this->wholeBrainSurfaceLeftCheckBox, + "Enable all view left surface"); QToolButton* wholeBrainLeftSurfaceToolButton = new QToolButton(); QAction* leftSurfaceAction = WuQtUtilities::createAction("Left", @@ -2527,7 +2548,8 @@ BrainBrowserWindowToolBar::createWholeBrainSurfaceOptionsWidget() wholeBrainLeftSurfaceToolButton->setDefaultAction(leftSurfaceAction); leftSurfaceAction->setObjectName(objectNamePrefix + "SelectLeft"); - macroManager->addMacroSupportToObject(leftSurfaceAction); + macroManager->addMacroSupportToObject(leftSurfaceAction, + "Select all view left surface"); /* * Right @@ -2539,7 +2561,8 @@ BrainBrowserWindowToolBar::createWholeBrainSurfaceOptionsWidget() this, SLOT(wholeBrainSurfaceRightCheckBoxStateChanged(int))); this->wholeBrainSurfaceRightCheckBox->setObjectName(objectNamePrefix + "EnableRight"); - macroManager->addMacroSupportToObject(this->wholeBrainSurfaceRightCheckBox); + macroManager->addMacroSupportToObject(this->wholeBrainSurfaceRightCheckBox, + "Enable all view right surface"); QToolButton* wholeBrainRightSurfaceToolButton = new QToolButton(); QAction* rightSurfaceAction = WuQtUtilities::createAction("Right", @@ -2551,7 +2574,8 @@ BrainBrowserWindowToolBar::createWholeBrainSurfaceOptionsWidget() wholeBrainRightSurfaceToolButton->setDefaultAction(rightSurfaceAction); rightSurfaceAction->setObjectName(objectNamePrefix + "SelectRight"); - macroManager->addMacroSupportToObject(rightSurfaceAction); + macroManager->addMacroSupportToObject(rightSurfaceAction, + "Select all view right surface"); /* * Cerebellum @@ -2563,7 +2587,8 @@ BrainBrowserWindowToolBar::createWholeBrainSurfaceOptionsWidget() this, SLOT(wholeBrainSurfaceCerebellumCheckBoxStateChanged(int))); this->wholeBrainSurfaceCerebellumCheckBox->setObjectName(objectNamePrefix + "EnableCerebellum"); - macroManager->addMacroSupportToObject(this->wholeBrainSurfaceCerebellumCheckBox); + macroManager->addMacroSupportToObject(this->wholeBrainSurfaceCerebellumCheckBox, + "Enable all view cerebellum"); QToolButton* wholeBrainCerebellumSurfaceToolButton = new QToolButton(); QAction* cerebellumSurfaceAction = WuQtUtilities::createAction("Cerebellum", @@ -2575,7 +2600,8 @@ BrainBrowserWindowToolBar::createWholeBrainSurfaceOptionsWidget() wholeBrainCerebellumSurfaceToolButton->setDefaultAction(cerebellumSurfaceAction); cerebellumSurfaceAction->setObjectName(objectNamePrefix + "SurfaceCerebellum"); - macroManager->addMacroSupportToObject(cerebellumSurfaceAction); + macroManager->addMacroSupportToObject(cerebellumSurfaceAction, + "Select all view cerebellum surface"); /* * Left/Right separation @@ -2592,7 +2618,8 @@ BrainBrowserWindowToolBar::createWholeBrainSurfaceOptionsWidget() this, SLOT(wholeBrainSurfaceSeparationLeftRightSpinBoxValueChanged(double))); this->wholeBrainSurfaceSeparationLeftRightSpinBox->setObjectName(objectNamePrefix + "LeftRightSeparation"); - macroManager->addMacroSupportToObject(this->wholeBrainSurfaceSeparationLeftRightSpinBox); + macroManager->addMacroSupportToObject(this->wholeBrainSurfaceSeparationLeftRightSpinBox, + "Set all view left/right separation"); /* * Cerebellum separation @@ -2608,7 +2635,8 @@ BrainBrowserWindowToolBar::createWholeBrainSurfaceOptionsWidget() this, SLOT(wholeBrainSurfaceSeparationCerebellumSpinBoxSelected(double))); this->wholeBrainSurfaceSeparationCerebellumSpinBox->setObjectName(objectNamePrefix + "CortexCerebellumSeparation"); - macroManager->addMacroSupportToObject(this->wholeBrainSurfaceSeparationCerebellumSpinBox); + macroManager->addMacroSupportToObject(this->wholeBrainSurfaceSeparationCerebellumSpinBox, + "Set all view cerebral/cerebellum separation"); @@ -2778,7 +2806,8 @@ BrainBrowserWindowToolBar::createModeWidget() WuQtUtilities::setToolButtonStyleForQt5Mac(inputModeAnnotationsToolButton); this->modeInputModeAnnotationsAction->setObjectName(m_objectNamePrefix + ":Mode:Annotate"); - WuQMacroManager::instance()->addMacroSupportToObject(this->modeInputModeAnnotationsAction); + WuQMacroManager::instance()->addMacroSupportToObject(this->modeInputModeAnnotationsAction, + "Select annotate mode"); /* @@ -2793,7 +2822,8 @@ BrainBrowserWindowToolBar::createModeWidget() WuQtUtilities::setToolButtonStyleForQt5Mac(inputModeBordersToolButton); this->modeInputModeBordersAction->setObjectName(m_objectNamePrefix + ":Mode:Border"); - WuQMacroManager::instance()->addMacroSupportToObject(this->modeInputModeBordersAction); + WuQMacroManager::instance()->addMacroSupportToObject(this->modeInputModeBordersAction, + "Select border mode"); /* * Foci @@ -2807,7 +2837,8 @@ BrainBrowserWindowToolBar::createModeWidget() WuQtUtilities::setToolButtonStyleForQt5Mac(inputModeFociToolButton); this->modeInputModeFociAction->setObjectName(m_objectNamePrefix + ":Mode:Foci"); - WuQMacroManager::instance()->addMacroSupportToObject(this->modeInputModeFociAction); + WuQMacroManager::instance()->addMacroSupportToObject(this->modeInputModeFociAction, + "Select foci mode"); /* * Image @@ -2825,7 +2856,8 @@ BrainBrowserWindowToolBar::createModeWidget() WuQtUtilities::setToolButtonStyleForQt5Mac(inputModeImageToolButton); this->modeInputModeImageAction->setObjectName(m_objectNamePrefix + ":Mode:Image"); - WuQMacroManager::instance()->addMacroSupportToObject(this->modeInputModeImageAction); + WuQMacroManager::instance()->addMacroSupportToObject(this->modeInputModeImageAction, + "Select image mode"); } /* @@ -2840,7 +2872,8 @@ BrainBrowserWindowToolBar::createModeWidget() WuQtUtilities::setToolButtonStyleForQt5Mac(inputModeVolumeEditButton); this->modeInputVolumeEditAction->setObjectName(m_objectNamePrefix + ":Mode:Volume"); - WuQMacroManager::instance()->addMacroSupportToObject(this->modeInputVolumeEditAction); + WuQMacroManager::instance()->addMacroSupportToObject(this->modeInputVolumeEditAction, + "Select volume mode"); /* * View Mode @@ -2868,7 +2901,8 @@ BrainBrowserWindowToolBar::createModeWidget() WuQtUtilities::setToolButtonStyleForQt5Mac(inputModeViewToolButton); this->modeInputModeViewAction->setObjectName(m_objectNamePrefix + ":Mode:View"); - WuQMacroManager::instance()->addMacroSupportToObject(this->modeInputModeViewAction); + WuQMacroManager::instance()->addMacroSupportToObject(this->modeInputModeViewAction, + "Select view mode"); WuQtUtilities::matchWidgetWidths(inputModeAnnotationsToolButton, inputModeBordersToolButton, @@ -3470,6 +3504,7 @@ BrainBrowserWindowToolBar::createSingleSurfaceOptionsWidget() this->surfaceSurfaceSelectionControl = new StructureSurfaceSelectionControl(false, m_objectNamePrefix + ":Surface", + "surface view", this); QObject::connect(this->surfaceSurfaceSelectionControl, SIGNAL(selectionChanged(const StructureEnum::Enum, diff --git a/src/GuiQt/BrainBrowserWindowToolBarChartAttributes.cxx b/src/GuiQt/BrainBrowserWindowToolBarChartAttributes.cxx index 0f711708d..39d20a330 100644 --- a/src/GuiQt/BrainBrowserWindowToolBarChartAttributes.cxx +++ b/src/GuiQt/BrainBrowserWindowToolBarChartAttributes.cxx @@ -261,7 +261,8 @@ CartesianChartAttributesWidget::CartesianChartAttributesWidget(BrainBrowserWindo m_cartesianLineWidthDoubleSpinBox->setToolTip("Set line width"); m_cartesianLineWidthDoubleSpinBox->setObjectName(parentObjectName + ":LineWidth"); - WuQMacroManager::instance()->addMacroSupportToObject(m_cartesianLineWidthDoubleSpinBox); + WuQMacroManager::instance()->addMacroSupportToObject(m_cartesianLineWidthDoubleSpinBox, + "Set chart line width"); QGridLayout* gridLayout = new QGridLayout(this); WuQtUtilities::setLayoutSpacingAndMargins(gridLayout, 0, 0); @@ -349,7 +350,8 @@ EventListenerInterface() m_cellWidthSpinBox->setToolTip("Set Cell Width"); m_cellWidthSpinBox->setObjectName(parentObjectName + ":Matrix:CellWidth"); - WuQMacroManager::instance()->addMacroSupportToObject(m_cellWidthSpinBox); + WuQMacroManager::instance()->addMacroSupportToObject(m_cellWidthSpinBox, + "Set matrix chart cell width"); QLabel* cellHeightLabel = new QLabel("Cell Height"); m_cellHeightSpinBox = WuQFactory::newDoubleSpinBoxWithMinMaxStepDecimalsSignalDouble(1.0, @@ -362,7 +364,8 @@ EventListenerInterface() m_cellHeightSpinBox->setToolTip("Set Cell Height"); m_cellHeightSpinBox->setObjectName(parentObjectName + ":Matrix:CellHeight"); - WuQMacroManager::instance()->addMacroSupportToObject(m_cellHeightSpinBox); + WuQMacroManager::instance()->addMacroSupportToObject(m_cellHeightSpinBox, + "Set matrix chart cell height"); QAction* resetButtonAction = WuQtUtilities::createAction("Reset", "Reset panning (SHIFT-mouse),zooming (CTRL-mouse), and scale matrix to fit window", @@ -374,7 +377,8 @@ EventListenerInterface() WuQtUtilities::setToolButtonStyleForQt5Mac(resetToolButton); resetToolButton->setObjectName(parentObjectName + ":Matrix:ResetButton"); - WuQMacroManager::instance()->addMacroSupportToObject(resetToolButton); + WuQMacroManager::instance()->addMacroSupportToObject(resetToolButton, + "Reset chart matrix scaling"); WuQtUtilities::matchWidgetWidths(m_cellHeightSpinBox, m_cellWidthSpinBox); @@ -385,7 +389,8 @@ EventListenerInterface() m_highlightSelectionCheckBox->setToolTip("Enable selected row/column highlight"); m_highlightSelectionCheckBox->setObjectName(parentObjectName + ":Matrix:EnableHighlight"); - WuQMacroManager::instance()->addMacroSupportToObject(m_highlightSelectionCheckBox); + WuQMacroManager::instance()->addMacroSupportToObject(m_highlightSelectionCheckBox, + "Enable chart matrix selected row height"); m_displayGridLinesCheckBox = new QCheckBox("Show Grid Outline"); QObject::connect(m_displayGridLinesCheckBox, SIGNAL(clicked(bool)), @@ -393,7 +398,8 @@ EventListenerInterface() m_displayGridLinesCheckBox->setToolTip("Show Grid Outline around matrix cells"); m_displayGridLinesCheckBox->setObjectName(parentObjectName + ":Matrix:EnableGridOutline"); - WuQMacroManager::instance()->addMacroSupportToObject(m_displayGridLinesCheckBox); + WuQMacroManager::instance()->addMacroSupportToObject(m_displayGridLinesCheckBox, + "Enable chart matrix grid outline"); m_manualWidgetsGroup = new WuQWidgetObjectGroup(this); m_manualWidgetsGroup->add(m_cellWidthSpinBox); diff --git a/src/GuiQt/BrainBrowserWindowToolBarChartAxes.cxx b/src/GuiQt/BrainBrowserWindowToolBarChartAxes.cxx index d6c12d5df..766b4ce90 100644 --- a/src/GuiQt/BrainBrowserWindowToolBarChartAxes.cxx +++ b/src/GuiQt/BrainBrowserWindowToolBarChartAxes.cxx @@ -99,13 +99,16 @@ m_parentToolBar(parentToolBar) m_bottomAxisAutoRangeScaleCheckBox->setObjectName(objectNamePrefix + "EnableBottomAxis"); - macroManager->addMacroSupportToObject(m_bottomAxisAutoRangeScaleCheckBox); + macroManager->addMacroSupportToObject(m_bottomAxisAutoRangeScaleCheckBox, + "Enable chart bottom axis auto scale"); m_bottomAxisMinimumValueSpinBox->setObjectName(objectNamePrefix + "BottomAxisMinimumValue"); - macroManager->addMacroSupportToObject(m_bottomAxisMinimumValueSpinBox); + macroManager->addMacroSupportToObject(m_bottomAxisMinimumValueSpinBox, + "Set chart bottom axis minimum"); m_bottomAxisMaximumValueSpinBox->setObjectName(objectNamePrefix + "BottomAxisMaximumValue"); - macroManager->addMacroSupportToObject(m_bottomAxisMaximumValueSpinBox); + macroManager->addMacroSupportToObject(m_bottomAxisMaximumValueSpinBox, + "Set chart bottom axis maximum"); createAxisWidgets(gridLayout, m_leftAxisLabel, @@ -123,13 +126,16 @@ m_parentToolBar(parentToolBar) m_leftAxisAutoRangeScaleCheckBox->setObjectName(objectNamePrefix + "EnableLeftAxis"); - macroManager->addMacroSupportToObject(m_leftAxisAutoRangeScaleCheckBox); + macroManager->addMacroSupportToObject(m_leftAxisAutoRangeScaleCheckBox, + "Enable chart left axis auto scale"); m_leftAxisMinimumValueSpinBox->setObjectName(objectNamePrefix + "LeftAxisMinimumValue"); - macroManager->addMacroSupportToObject(m_leftAxisMinimumValueSpinBox); + macroManager->addMacroSupportToObject(m_leftAxisMinimumValueSpinBox, + "Set chart left axis minimum value"); m_leftAxisMaximumValueSpinBox->setObjectName(objectNamePrefix + "LeftAxisMaximumValue"); - macroManager->addMacroSupportToObject(m_leftAxisMaximumValueSpinBox); + macroManager->addMacroSupportToObject(m_leftAxisMaximumValueSpinBox, + "Set chart left axis maximum value"); // createAxisWidgets(gridLayout, // m_topAxisLabel, diff --git a/src/GuiQt/BrainBrowserWindowToolBarChartTwoAttributes.cxx b/src/GuiQt/BrainBrowserWindowToolBarChartTwoAttributes.cxx index 349312b23..b31006b28 100644 --- a/src/GuiQt/BrainBrowserWindowToolBarChartTwoAttributes.cxx +++ b/src/GuiQt/BrainBrowserWindowToolBarChartTwoAttributes.cxx @@ -188,7 +188,8 @@ CartesianChartTwoAttributesWidget::CartesianChartTwoAttributesWidget(BrainBrowse m_cartesianLineWidthDoubleSpinBox->setToolTip("Width of line"); m_cartesianLineWidthDoubleSpinBox->setObjectName(parentObjectName + ":LineWidth"); - WuQMacroManager::instance()->addMacroSupportToObject(m_cartesianLineWidthDoubleSpinBox); + WuQMacroManager::instance()->addMacroSupportToObject(m_cartesianLineWidthDoubleSpinBox, + "Set cartesian chart line width"); QGridLayout* gridLayout = new QGridLayout(this); @@ -268,7 +269,8 @@ EventListenerInterface() m_cellWidthPercentageSpinBox->setSuffix("%"); m_cellWidthPercentageSpinBox->setObjectName(parentObjectName + ":CellWidth"); - WuQMacroManager::instance()->addMacroSupportToObject(m_cellWidthPercentageSpinBox); + WuQMacroManager::instance()->addMacroSupportToObject(m_cellWidthPercentageSpinBox, + "Set matrix chart cell width"); QLabel* cellHeightLabel = new QLabel("Cell Height"); m_cellHeightPercentageSpinBox = WuQFactory::newDoubleSpinBoxWithMinMaxStepDecimalsSignalDouble(minPercent, @@ -282,7 +284,8 @@ EventListenerInterface() m_cellHeightPercentageSpinBox->setSuffix("%"); m_cellHeightPercentageSpinBox->setObjectName(parentObjectName + ":CellHeight"); - WuQMacroManager::instance()->addMacroSupportToObject(m_cellHeightPercentageSpinBox); + WuQMacroManager::instance()->addMacroSupportToObject(m_cellHeightPercentageSpinBox, + "Set matrix chart cell height"); WuQtUtilities::matchWidgetWidths(m_cellHeightPercentageSpinBox, m_cellWidthPercentageSpinBox); @@ -293,7 +296,8 @@ EventListenerInterface() this, SLOT(valueChanged())); m_highlightSelectionCheckBox->setObjectName(parentObjectName + ":HighlightSelection"); - WuQMacroManager::instance()->addMacroSupportToObject(m_highlightSelectionCheckBox); + WuQMacroManager::instance()->addMacroSupportToObject(m_highlightSelectionCheckBox, + "Enable outline of selected chart matrix row/column"); m_displayGridLinesCheckBox = new QCheckBox("Show Grid Outline"); QObject::connect(m_displayGridLinesCheckBox, SIGNAL(clicked(bool)), @@ -301,7 +305,8 @@ EventListenerInterface() m_displayGridLinesCheckBox->setToolTip("Outline cells in the matrix"); m_displayGridLinesCheckBox->setObjectName(parentObjectName + ":EnableGridOutline"); - WuQMacroManager::instance()->addMacroSupportToObject(m_displayGridLinesCheckBox); + WuQMacroManager::instance()->addMacroSupportToObject(m_displayGridLinesCheckBox, + "Enable matrix chart grid outline"); m_manualWidgetsGroup = new WuQWidgetObjectGroup(this); m_manualWidgetsGroup->add(m_cellWidthPercentageSpinBox); diff --git a/src/GuiQt/BrainBrowserWindowToolBarChartTwoAxes.cxx b/src/GuiQt/BrainBrowserWindowToolBarChartTwoAxes.cxx index 50c1d8f7e..b7818b108 100644 --- a/src/GuiQt/BrainBrowserWindowToolBarChartTwoAxes.cxx +++ b/src/GuiQt/BrainBrowserWindowToolBarChartTwoAxes.cxx @@ -97,7 +97,8 @@ BrainBrowserWindowToolBarChartTwoAxes::BrainBrowserWindowToolBarChartTwoAxes(Bra this, &BrainBrowserWindowToolBarChartTwoAxes::valueChanged); m_axisDisplayedByUserCheckBox->setObjectName(objectNamePrefix + "ShowAxis"); - macroManager->addMacroSupportToObject(m_axisDisplayedByUserCheckBox); + macroManager->addMacroSupportToObject(m_axisDisplayedByUserCheckBox, + "Enable chart axis"); m_showTickMarksCheckBox = new QCheckBox("Ticks"); @@ -106,7 +107,8 @@ BrainBrowserWindowToolBarChartTwoAxes::BrainBrowserWindowToolBarChartTwoAxes(Bra m_showTickMarksCheckBox->setToolTip("Show ticks along the axis"); m_showTickMarksCheckBox->setObjectName(objectNamePrefix + "ShowTicks"); - macroManager->addMacroSupportToObject(m_showTickMarksCheckBox); + macroManager->addMacroSupportToObject(m_showTickMarksCheckBox, + "Enable chart axis ticks"); m_showLabelCheckBox = new QCheckBox("Label"); QObject::connect(m_showLabelCheckBox, &QCheckBox::clicked, @@ -114,7 +116,8 @@ BrainBrowserWindowToolBarChartTwoAxes::BrainBrowserWindowToolBarChartTwoAxes(Bra m_showLabelCheckBox->setToolTip("Show label on axis"); m_showLabelCheckBox->setObjectName(objectNamePrefix + "ShowLabel"); - macroManager->addMacroSupportToObject(m_showLabelCheckBox); + macroManager->addMacroSupportToObject(m_showLabelCheckBox, + "Enable chart axis label"); m_showNumericsCheckBox = new QCheckBox("Nums"); QObject::connect(m_showNumericsCheckBox, &QCheckBox::clicked, @@ -122,7 +125,8 @@ BrainBrowserWindowToolBarChartTwoAxes::BrainBrowserWindowToolBarChartTwoAxes(Bra m_showNumericsCheckBox->setToolTip("Show numeric scale values on axis"); m_showNumericsCheckBox->setObjectName(objectNamePrefix + "ShowNumerics"); - macroManager->addMacroSupportToObject(m_showNumericsCheckBox); + macroManager->addMacroSupportToObject(m_showNumericsCheckBox, + "Enable chart axis numerics"); m_rotateNumericsCheckBox = new QCheckBox("Rotate"); QObject::connect(m_rotateNumericsCheckBox, &QCheckBox::clicked, @@ -130,7 +134,8 @@ BrainBrowserWindowToolBarChartTwoAxes::BrainBrowserWindowToolBarChartTwoAxes(Bra m_rotateNumericsCheckBox->setToolTip("Rotate numeric scale values on axis"); m_rotateNumericsCheckBox->setObjectName(objectNamePrefix + "EnableNumericsRotate"); - macroManager->addMacroSupportToObject(m_rotateNumericsCheckBox); + macroManager->addMacroSupportToObject(m_rotateNumericsCheckBox, + "Enable rotation of chart axis numerics"); /* * Axes selection @@ -142,7 +147,8 @@ BrainBrowserWindowToolBarChartTwoAxes::BrainBrowserWindowToolBarChartTwoAxes(Bra m_axisComboBox->getWidget()->setToolTip("Choose axis for editing"); m_axisComboBox->getWidget()->setObjectName(objectNamePrefix + "ChooseAxis"); - macroManager->addMacroSupportToObject(m_axisComboBox->getComboBox()); + macroManager->addMacroSupportToObject(m_axisComboBox->getComboBox(), + "Select chart axis"); /* * Controls for layer selection and label editing @@ -155,7 +161,8 @@ BrainBrowserWindowToolBarChartTwoAxes::BrainBrowserWindowToolBarChartTwoAxes(Bra m_axisLabelToolButton->setToolTip("Edit the axis name for the file in the selected overlay"); m_axisLabelToolButton->setObjectName(objectNamePrefix + "EditAxis"); - macroManager->addMacroSupportToObject(m_axisLabelToolButton); + macroManager->addMacroSupportToObject(m_axisLabelToolButton, + "Edit chart axis label"); QLabel* axisLabelFromOverlayLabel = new QLabel("Label From File In"); m_axisLabelFromOverlayComboBox = new QComboBox(); @@ -164,7 +171,8 @@ BrainBrowserWindowToolBarChartTwoAxes::BrainBrowserWindowToolBarChartTwoAxes(Bra this, &BrainBrowserWindowToolBarChartTwoAxes::valueChangedInt); m_axisLabelFromOverlayComboBox->setObjectName(objectNamePrefix + "LabelFromOverlay"); - macroManager->addMacroSupportToObject(m_axisLabelFromOverlayComboBox); + macroManager->addMacroSupportToObject(m_axisLabelFromOverlayComboBox, + "Select chart axis overlay source"); /* * Range controls @@ -182,7 +190,8 @@ BrainBrowserWindowToolBarChartTwoAxes::BrainBrowserWindowToolBarChartTwoAxes(Bra m_autoUserRangeComboBox->getWidget()->setToolTip(rangeTooltip); m_autoUserRangeComboBox->getWidget()->setObjectName(objectNamePrefix + "RangeMode"); - macroManager->addMacroSupportToObject(m_autoUserRangeComboBox->getWidget()); + macroManager->addMacroSupportToObject(m_autoUserRangeComboBox->getWidget(), + "Select chart axis range mode"); m_userMinimumValueSpinBox = new WuQDoubleSpinBox(this); m_userMinimumValueSpinBox->setDecimalsModeAuto(); @@ -192,7 +201,8 @@ BrainBrowserWindowToolBarChartTwoAxes::BrainBrowserWindowToolBarChartTwoAxes(Bra m_userMinimumValueSpinBox->setToolTip("Set user scaling axis minimum value"); m_userMinimumValueSpinBox->getWidget()->setObjectName(objectNamePrefix + "ScaleMinimum"); - macroManager->addMacroSupportToObject(m_userMinimumValueSpinBox->getWidget()); + macroManager->addMacroSupportToObject(m_userMinimumValueSpinBox->getWidget(), + "Set chart axis minimum"); m_userMaximumValueSpinBox = new WuQDoubleSpinBox(this); m_userMaximumValueSpinBox->setDecimalsModeAuto(); @@ -202,7 +212,8 @@ BrainBrowserWindowToolBarChartTwoAxes::BrainBrowserWindowToolBarChartTwoAxes(Bra m_userMaximumValueSpinBox->setToolTip("Set user scaling axis maximum value"); m_userMaximumValueSpinBox->getWidget()->setObjectName(objectNamePrefix + "ScaleMaximum"); - macroManager->addMacroSupportToObject(m_userMaximumValueSpinBox->getWidget()); + macroManager->addMacroSupportToObject(m_userMaximumValueSpinBox->getWidget(), + "See chart axis maximum"); /* * Format controls @@ -214,7 +225,8 @@ BrainBrowserWindowToolBarChartTwoAxes::BrainBrowserWindowToolBarChartTwoAxes(Bra m_userNumericFormatComboBox->getWidget()->setToolTip("Choose format of axis scale numeric values"); m_userNumericFormatComboBox->getWidget()->setObjectName(objectNamePrefix + "Format"); - macroManager->addMacroSupportToObject(m_userNumericFormatComboBox->getWidget()); + macroManager->addMacroSupportToObject(m_userNumericFormatComboBox->getWidget(), + "Select chart axis numeric format"); m_userDigitsRightOfDecimalSpinBox = WuQFactory::newSpinBoxWithMinMaxStep(0, 10, 1); QObject::connect(m_userDigitsRightOfDecimalSpinBox, static_cast(&QSpinBox::valueChanged), @@ -222,7 +234,8 @@ BrainBrowserWindowToolBarChartTwoAxes::BrainBrowserWindowToolBarChartTwoAxes(Bra m_userDigitsRightOfDecimalSpinBox->setToolTip("Set digits right of decimal for\ndecimal or scientific format"); m_userDigitsRightOfDecimalSpinBox->setObjectName(objectNamePrefix + "DigitsRightOfDecimal"); - macroManager->addMacroSupportToObject(m_userDigitsRightOfDecimalSpinBox); + macroManager->addMacroSupportToObject(m_userDigitsRightOfDecimalSpinBox, + "Set chart axis digits right of decimal"); m_numericSubdivisionsModeComboBox = new EnumComboBoxTemplate(this); m_numericSubdivisionsModeComboBox->setup(); @@ -231,7 +244,8 @@ BrainBrowserWindowToolBarChartTwoAxes::BrainBrowserWindowToolBarChartTwoAxes(Bra m_numericSubdivisionsModeComboBox->getWidget()->setToolTip("Numeric subdivisions mode"); m_numericSubdivisionsModeComboBox->getWidget()->setObjectName(objectNamePrefix + "NumericSubdivisionsMode"); - macroManager->addMacroSupportToObject(m_numericSubdivisionsModeComboBox->getWidget()); + macroManager->addMacroSupportToObject(m_numericSubdivisionsModeComboBox->getWidget(), + "Set chart axis numeric subdivisions mode"); m_userSubdivisionsSpinBox = WuQFactory::newSpinBoxWithMinMaxStep(0, 100, 1); QObject::connect(m_userSubdivisionsSpinBox, static_cast(&QSpinBox::valueChanged), @@ -239,7 +253,8 @@ BrainBrowserWindowToolBarChartTwoAxes::BrainBrowserWindowToolBarChartTwoAxes(Bra m_userSubdivisionsSpinBox->setToolTip("Set subdivisions on the axis when Auto is not checked"); m_userSubdivisionsSpinBox->setObjectName(objectNamePrefix + "NumberOfSubdivisions"); - macroManager->addMacroSupportToObject(m_userSubdivisionsSpinBox); + macroManager->addMacroSupportToObject(m_userSubdivisionsSpinBox, + "Set chart axis number of subivisions"); /* * Size spin boxes @@ -251,7 +266,8 @@ BrainBrowserWindowToolBarChartTwoAxes::BrainBrowserWindowToolBarChartTwoAxes(Bra m_labelSizeSpinBox->setToolTip("Set height of label as percentage of tab height for selected axis"); m_labelSizeSpinBox->getWidget()->setObjectName(objectNamePrefix + "LabelHeight"); - macroManager->addMacroSupportToObject(m_labelSizeSpinBox->getWidget()); + macroManager->addMacroSupportToObject(m_labelSizeSpinBox->getWidget(), + "Set chart axis label height"); m_numericsSizeSpinBox = new WuQDoubleSpinBox(this); m_numericsSizeSpinBox->setRangePercentage(0.0, 100.0); @@ -260,7 +276,8 @@ BrainBrowserWindowToolBarChartTwoAxes::BrainBrowserWindowToolBarChartTwoAxes(Bra m_numericsSizeSpinBox->setToolTip("Set height of numeric values as percentage of tab height for selected axis"); m_numericsSizeSpinBox->getWidget()->setObjectName(objectNamePrefix + "NumericValueHeight"); - macroManager->addMacroSupportToObject(m_numericsSizeSpinBox->getWidget()); + macroManager->addMacroSupportToObject(m_numericsSizeSpinBox->getWidget(), + "Set chart axis numerics height"); m_linesTicksSizeSpinBox = new WuQDoubleSpinBox(this); m_linesTicksSizeSpinBox->setRangePercentage(0.0, 100.0); @@ -269,7 +286,8 @@ BrainBrowserWindowToolBarChartTwoAxes::BrainBrowserWindowToolBarChartTwoAxes(Bra m_linesTicksSizeSpinBox->setToolTip("Set thickness of axis lines as percentage of tab height for ALL axes"); m_linesTicksSizeSpinBox->getWidget()->setObjectName(objectNamePrefix + "TicksSize"); - macroManager->addMacroSupportToObject(m_linesTicksSizeSpinBox->getWidget()); + macroManager->addMacroSupportToObject(m_linesTicksSizeSpinBox->getWidget(), + "Set chart axis ticks height"); m_paddingSizeSpinBox = new WuQDoubleSpinBox(this); m_paddingSizeSpinBox->setRangePercentage(0.0, 100.0); @@ -278,7 +296,8 @@ BrainBrowserWindowToolBarChartTwoAxes::BrainBrowserWindowToolBarChartTwoAxes(Bra m_paddingSizeSpinBox->setToolTip("Set padding (space between edge and labels) as percentage of tab height for selected axis"); m_paddingSizeSpinBox->getWidget()->setObjectName(objectNamePrefix + "PaddingSize"); - macroManager->addMacroSupportToObject(m_paddingSizeSpinBox->getWidget()); + macroManager->addMacroSupportToObject(m_paddingSizeSpinBox->getWidget(), + "Set chart axis padding height"); /* * Group widgets for blocking signals diff --git a/src/GuiQt/BrainBrowserWindowToolBarChartTwoOrientation.cxx b/src/GuiQt/BrainBrowserWindowToolBarChartTwoOrientation.cxx index ae64a6646..319936bfc 100644 --- a/src/GuiQt/BrainBrowserWindowToolBarChartTwoOrientation.cxx +++ b/src/GuiQt/BrainBrowserWindowToolBarChartTwoOrientation.cxx @@ -64,7 +64,8 @@ BrainBrowserWindowToolBarChartTwoOrientation::BrainBrowserWindowToolBarChartTwoO SLOT(orientationResetToolButtonTriggered(bool))); m_orientationResetToolButtonAction->setObjectName(objectNamePrefix + ":ResetButton"); - WuQMacroManager::instance()->addMacroSupportToObject(m_orientationResetToolButtonAction); + WuQMacroManager::instance()->addMacroSupportToObject(m_orientationResetToolButtonAction, + "Reset chart panning and zooming"); const QString customToolTip = ("Pressing the \"Custom\" button displays a dialog for creating and editing orientations.\n" "Note that custom orientations are stored in your Workbench's preferences and thus\n" @@ -77,7 +78,8 @@ BrainBrowserWindowToolBarChartTwoOrientation::BrainBrowserWindowToolBarChartTwoO SLOT(customViewActionTriggered())); m_customViewAction->setObjectName(objectNamePrefix + ":ShowCustomViewDialog"); - WuQMacroManager::instance()->addMacroSupportToObject(m_customViewAction); + WuQMacroManager::instance()->addMacroSupportToObject(m_customViewAction, + "Display chart custom view dialog"); orientationResetToolButton->setDefaultAction(m_orientationResetToolButtonAction); diff --git a/src/GuiQt/BrainBrowserWindowToolBarChartTwoTitle.cxx b/src/GuiQt/BrainBrowserWindowToolBarChartTwoTitle.cxx index bfa2c97cc..88e04dba1 100644 --- a/src/GuiQt/BrainBrowserWindowToolBarChartTwoTitle.cxx +++ b/src/GuiQt/BrainBrowserWindowToolBarChartTwoTitle.cxx @@ -77,7 +77,8 @@ BrainBrowserWindowToolBarChartTwoTitle::BrainBrowserWindowToolBarChartTwoTitle(B this, &BrainBrowserWindowToolBarChartTwoTitle::showTitleCheckBoxClicked); m_showTitleCheckBox->setObjectName(objectNamePrefix + "Show"); - macroManager->addMacroSupportToObject(m_showTitleCheckBox); + macroManager->addMacroSupportToObject(m_showTitleCheckBox, + "Show chart title"); QToolButton* editTitleToolButton = new QToolButton; QAction* editTitleAction = new QAction("Edit Title...", editTitleToolButton); @@ -86,7 +87,8 @@ BrainBrowserWindowToolBarChartTwoTitle::BrainBrowserWindowToolBarChartTwoTitle(B this, &BrainBrowserWindowToolBarChartTwoTitle::editTitleActionTriggered); editTitleAction->setObjectName(objectNamePrefix + "Edit"); - macroManager->addMacroSupportToObject(editTitleAction); + macroManager->addMacroSupportToObject(editTitleAction, + "Display dialog to edit chart title"); WuQtUtilities::setToolButtonStyleForQt5Mac(editTitleToolButton); editTitleToolButton->setDefaultAction(editTitleAction); @@ -97,7 +99,8 @@ BrainBrowserWindowToolBarChartTwoTitle::BrainBrowserWindowToolBarChartTwoTitle(B m_titleSizeSpinBox->setToolTip("Set height of title as percentage of tab height"); m_titleSizeSpinBox->getWidget()->setObjectName(objectNamePrefix + "Height"); - macroManager->addMacroSupportToObject(m_titleSizeSpinBox->getWidget()); + macroManager->addMacroSupportToObject(m_titleSizeSpinBox->getWidget(), + "Set chart title height"); m_paddingSizeSpinBox = new WuQDoubleSpinBox(this); m_paddingSizeSpinBox->setRangePercentage(0.0, 100.0); @@ -105,7 +108,8 @@ BrainBrowserWindowToolBarChartTwoTitle::BrainBrowserWindowToolBarChartTwoTitle(B this, &BrainBrowserWindowToolBarChartTwoTitle::sizeSpinBoxValueChanged); m_paddingSizeSpinBox->getWidget()->setObjectName(objectNamePrefix + "Padding"); - macroManager->addMacroSupportToObject(m_paddingSizeSpinBox->getWidget()); + macroManager->addMacroSupportToObject(m_paddingSizeSpinBox->getWidget(), + "Set chart padding"); m_paddingSizeSpinBox->setToolTip("Set padding (space between edge and labels) as percentage of tab height"); QGridLayout* layout = new QGridLayout(this); diff --git a/src/GuiQt/BrainBrowserWindowToolBarChartTwoType.cxx b/src/GuiQt/BrainBrowserWindowToolBarChartTwoType.cxx index 08ee5fbde..a19c7b368 100644 --- a/src/GuiQt/BrainBrowserWindowToolBarChartTwoType.cxx +++ b/src/GuiQt/BrainBrowserWindowToolBarChartTwoType.cxx @@ -81,7 +81,8 @@ m_parentToolBar(parentToolBar) rb->setObjectName(parentObjectName + ":ChartType:" + chartTypeName); - WuQMacroManager::instance()->addMacroSupportToObject(rb); + WuQMacroManager::instance()->addMacroSupportToObject(rb, + "Select " + chartTypeName + " chart"); radioButtonLayout->addWidget(rb); diff --git a/src/GuiQt/BrainBrowserWindowToolBarChartType.cxx b/src/GuiQt/BrainBrowserWindowToolBarChartType.cxx index a27488fbd..f2ba10012 100644 --- a/src/GuiQt/BrainBrowserWindowToolBarChartType.cxx +++ b/src/GuiQt/BrainBrowserWindowToolBarChartType.cxx @@ -85,7 +85,8 @@ m_parentToolBar(parentToolBar) rb->setObjectName(parentObjectName + ":ChartOneType:" + chartTypeName); - WuQMacroManager::instance()->addMacroSupportToObject(rb); + WuQMacroManager::instance()->addMacroSupportToObject(rb, + "Select " + chartTypeName + " chart"); radioButtonLayout->addWidget(rb); diff --git a/src/GuiQt/BrainBrowserWindowToolBarClipping.cxx b/src/GuiQt/BrainBrowserWindowToolBarClipping.cxx index 2b7ca97ea..fa3535807 100644 --- a/src/GuiQt/BrainBrowserWindowToolBarClipping.cxx +++ b/src/GuiQt/BrainBrowserWindowToolBarClipping.cxx @@ -75,7 +75,8 @@ m_parentToolBar(parentToolBar) m_xClippingEnabledCheckBox->setToolTip("Enable X clipping plane"); m_xClippingEnabledCheckBox->setObjectName(objectNamePrefix + ":EnableX"); - macroManager->addMacroSupportToObject(m_xClippingEnabledCheckBox); + macroManager->addMacroSupportToObject(m_xClippingEnabledCheckBox, + "Enable X clipping plane"); m_yClippingEnabledCheckBox = new QCheckBox("Y"); QObject::connect(m_yClippingEnabledCheckBox, SIGNAL(clicked(bool)), @@ -83,7 +84,8 @@ m_parentToolBar(parentToolBar) m_yClippingEnabledCheckBox->setToolTip("Enable Y clipping plane"); m_yClippingEnabledCheckBox->setObjectName(objectNamePrefix + ":EnableY"); - macroManager->addMacroSupportToObject(m_yClippingEnabledCheckBox); + macroManager->addMacroSupportToObject(m_yClippingEnabledCheckBox, + "Enable Y clipping plane"); m_zClippingEnabledCheckBox = new QCheckBox("Z"); QObject::connect(m_zClippingEnabledCheckBox, SIGNAL(clicked(bool)), @@ -91,7 +93,8 @@ m_parentToolBar(parentToolBar) m_zClippingEnabledCheckBox->setToolTip("Enable Z clipping plane"); m_zClippingEnabledCheckBox->setObjectName(objectNamePrefix + ":EnableZ"); - macroManager->addMacroSupportToObject(m_zClippingEnabledCheckBox); + macroManager->addMacroSupportToObject(m_zClippingEnabledCheckBox, + "Enable Z clipping"); m_surfaceClippingEnabledCheckBox = new QCheckBox("Surface"); QObject::connect(m_surfaceClippingEnabledCheckBox, SIGNAL(clicked(bool)), @@ -99,7 +102,8 @@ m_parentToolBar(parentToolBar) m_surfaceClippingEnabledCheckBox->setToolTip("Enable Clipping of Surface"); m_surfaceClippingEnabledCheckBox->setObjectName(objectNamePrefix + ":EnableSurface"); - macroManager->addMacroSupportToObject(m_surfaceClippingEnabledCheckBox); + macroManager->addMacroSupportToObject(m_surfaceClippingEnabledCheckBox, + "Enable surface clipping"); m_volumeClippingEnabledCheckBox = new QCheckBox("Volume"); QObject::connect(m_volumeClippingEnabledCheckBox, SIGNAL(clicked(bool)), @@ -107,7 +111,8 @@ m_parentToolBar(parentToolBar) m_volumeClippingEnabledCheckBox->setToolTip("Enable Clipping of Volume Slices"); m_volumeClippingEnabledCheckBox->setObjectName(objectNamePrefix + ":EnableVolume"); - macroManager->addMacroSupportToObject(m_volumeClippingEnabledCheckBox); + macroManager->addMacroSupportToObject(m_volumeClippingEnabledCheckBox, + "Enable volume clipping"); m_featuresClippingEnabledCheckBox = new QCheckBox("Features"); QObject::connect(m_featuresClippingEnabledCheckBox, SIGNAL(clicked(bool)), @@ -115,7 +120,8 @@ m_parentToolBar(parentToolBar) m_featuresClippingEnabledCheckBox->setToolTip("Enable Clipping of Features"); m_featuresClippingEnabledCheckBox->setObjectName(objectNamePrefix + ":EnableFeatures"); - macroManager->addMacroSupportToObject(m_featuresClippingEnabledCheckBox); + macroManager->addMacroSupportToObject(m_featuresClippingEnabledCheckBox, + "Enable features clipping"); QToolButton* setupToolButton = new QToolButton(); setupToolButton->setText("Setup"); @@ -125,7 +131,8 @@ m_parentToolBar(parentToolBar) setupToolButton->setToolTip("Display Clipping Planes Setup Dialog"); setupToolButton->setObjectName(objectNamePrefix + ":ShowSetupDialog"); - macroManager->addMacroSupportToObject(setupToolButton); + macroManager->addMacroSupportToObject(setupToolButton, + "Display clipping planes dialog"); QGridLayout* gridLayout = new QGridLayout(this); gridLayout->setHorizontalSpacing(6); diff --git a/src/GuiQt/BrainBrowserWindowToolBarSlicePlane.cxx b/src/GuiQt/BrainBrowserWindowToolBarSlicePlane.cxx index 1db6e3ad9..da4c2b551 100644 --- a/src/GuiQt/BrainBrowserWindowToolBarSlicePlane.cxx +++ b/src/GuiQt/BrainBrowserWindowToolBarSlicePlane.cxx @@ -89,7 +89,8 @@ m_parentToolBar(parentToolBar) } m_volumePlaneParasagittalToolButtonAction->setObjectName(objectNamePrefix + "ParasagittalSliceView"); - macroManager->addMacroSupportToObject(m_volumePlaneParasagittalToolButtonAction); + macroManager->addMacroSupportToObject(m_volumePlaneParasagittalToolButtonAction, + "Select parasagittal slice view"); m_volumePlaneCoronalToolButtonAction = WuQtUtilities::createAction(VolumeSliceViewPlaneEnum::toGuiNameAbbreviation(VolumeSliceViewPlaneEnum::CORONAL), "View the CORONAL slice", @@ -100,7 +101,8 @@ m_parentToolBar(parentToolBar) } m_volumePlaneCoronalToolButtonAction->setObjectName(objectNamePrefix + "CoronalSliceView"); - macroManager->addMacroSupportToObject(m_volumePlaneCoronalToolButtonAction); + macroManager->addMacroSupportToObject(m_volumePlaneCoronalToolButtonAction, + "Select coronal slice view"); m_volumePlaneAxialToolButtonAction = WuQtUtilities::createAction(VolumeSliceViewPlaneEnum::toGuiNameAbbreviation(VolumeSliceViewPlaneEnum::AXIAL), "View the AXIAL slice", @@ -111,7 +113,8 @@ m_parentToolBar(parentToolBar) } m_volumePlaneAxialToolButtonAction->setObjectName(objectNamePrefix + "AxialSliceView"); - macroManager->addMacroSupportToObject(m_volumePlaneAxialToolButtonAction); + macroManager->addMacroSupportToObject(m_volumePlaneAxialToolButtonAction, + "Select axial slice view"); m_volumePlaneAllToolButtonAction = WuQtUtilities::createAction(VolumeSliceViewPlaneEnum::toGuiNameAbbreviation(VolumeSliceViewPlaneEnum::ALL), "View the PARASAGITTAL, CORONAL, and AXIAL slices\n" @@ -121,7 +124,8 @@ m_parentToolBar(parentToolBar) m_volumePlaneAllToolButtonAction->setMenu(createViewAllSlicesLayoutMenu(objectNamePrefix)); m_volumePlaneAllToolButtonAction->setObjectName(objectNamePrefix + "AllSlicesView"); - macroManager->addMacroSupportToObject(m_volumePlaneAllToolButtonAction); + macroManager->addMacroSupportToObject(m_volumePlaneAllToolButtonAction, + "Select all planes view"); m_volumePlaneActionGroup = new QActionGroup(this); @@ -141,7 +145,8 @@ m_parentToolBar(parentToolBar) SLOT(volumePlaneResetToolButtonTriggered(bool))); m_volumePlaneResetToolButtonAction->setObjectName(objectNamePrefix + "ResetView"); - macroManager->addMacroSupportToObject(m_volumePlaneResetToolButtonAction); + macroManager->addMacroSupportToObject(m_volumePlaneResetToolButtonAction, + "Reset slice view pan/rotate/zoom"); QToolButton* volumePlaneParasagittalToolButton = new QToolButton(); @@ -190,7 +195,8 @@ m_parentToolBar(parentToolBar) this, &BrainBrowserWindowToolBarSlicePlane::volumeAxisCrosshairsTriggered); m_volumeAxisCrosshairsToolButtonAction->setObjectName(objectNamePrefix + "ShowVolumeSliceCrosshairs"); - macroManager->addMacroSupportToObject(m_volumeAxisCrosshairsToolButtonAction); + macroManager->addMacroSupportToObject(m_volumeAxisCrosshairsToolButtonAction, + "Show slice axis crosshairs"); QToolButton* volumeCrosshairsToolButton = new QToolButton(); QPixmap xhairPixmap = createCrosshairsIcon(volumeCrosshairsToolButton); @@ -206,7 +212,8 @@ m_parentToolBar(parentToolBar) this, &BrainBrowserWindowToolBarSlicePlane::volumeAxisCrosshairLabelsTriggered); m_volumeAxisCrosshairLabelsToolButtonAction->setObjectName(objectNamePrefix + "ShowVolumeSliceLabels"); - macroManager->addMacroSupportToObject(m_volumeAxisCrosshairLabelsToolButtonAction); + macroManager->addMacroSupportToObject(m_volumeAxisCrosshairLabelsToolButtonAction, + "Show slice axis labels"); QToolButton* volumeCrosshairLabelsToolButton = new QToolButton(); volumeCrosshairLabelsToolButton->setDefaultAction(m_volumeAxisCrosshairLabelsToolButtonAction); @@ -299,14 +306,20 @@ BrainBrowserWindowToolBarSlicePlane::createViewAllSlicesLayoutMenu(const QString QAction* action = menu->addAction(VolumeSliceViewAllPlanesLayoutEnum::toGuiName(layout)); action->setData((int)VolumeSliceViewAllPlanesLayoutEnum::toIntegerCode(layout)); action->setCheckable(true); + action->setObjectName(objectNamePrefix + + ":Layout:" + + VolumeSliceViewAllPlanesLayoutEnum::toName(layout)); m_viewAllSliceLayoutMenuActions.push_back(action); - actionGroup->addAction(action); + + WuQMacroManager::instance()->addMacroSupportToObject(action, + "Select " + + VolumeSliceViewAllPlanesLayoutEnum::toGuiName(layout) + + " volume slice layout"); } QObject::connect(menu, &QMenu::triggered, this, &BrainBrowserWindowToolBarSlicePlane::viewAllSliceLayoutMenuTriggered); - WuQMacroManager::instance()->addMacroSupportToObject(menu); return menu; } diff --git a/src/GuiQt/BrainBrowserWindowToolBarSliceSelection.cxx b/src/GuiQt/BrainBrowserWindowToolBarSliceSelection.cxx index 8626dea4b..21ffaeaa0 100644 --- a/src/GuiQt/BrainBrowserWindowToolBarSliceSelection.cxx +++ b/src/GuiQt/BrainBrowserWindowToolBarSliceSelection.cxx @@ -93,7 +93,8 @@ m_parentToolBar(parentToolBar) volumeIndicesOriginToolButtonAction->setObjectName(objectNamePrefix + "MoveVolumeSlicesToOrigin"); volumeIndicesOriginToolButtonAction->setParent(volumeIndicesOriginToolButton); - macroManager->addMacroSupportToObject(volumeIndicesOriginToolButtonAction); + macroManager->addMacroSupportToObject(volumeIndicesOriginToolButtonAction, + "Set volume slices to origin"); QLabel* parasagittalLabel = new QLabel("P:"); QLabel* coronalLabel = new QLabel("C:"); @@ -106,7 +107,8 @@ m_parentToolBar(parentToolBar) + "EnableParasagittalSlice"); QObject::connect(m_volumeIndicesParasagittalCheckBox, SIGNAL(stateChanged(int)), this, SLOT(volumeIndicesParasagittalCheckBoxStateChanged(int))); - macroManager->addMacroSupportToObject(m_volumeIndicesParasagittalCheckBox); + macroManager->addMacroSupportToObject(m_volumeIndicesParasagittalCheckBox, + "Enable parasagittal volume slice"); m_volumeIndicesCoronalCheckBox = new QCheckBox(" "); WuQtUtilities::setToolTipAndStatusTip(m_volumeIndicesCoronalCheckBox, @@ -115,7 +117,8 @@ m_parentToolBar(parentToolBar) + "EnableCoronalSlice"); QObject::connect(m_volumeIndicesCoronalCheckBox, SIGNAL(stateChanged(int)), this, SLOT(volumeIndicesCoronalCheckBoxStateChanged(int))); - macroManager->addMacroSupportToObject(m_volumeIndicesCoronalCheckBox); + macroManager->addMacroSupportToObject(m_volumeIndicesCoronalCheckBox, + "Enable coronal volume slice"); m_volumeIndicesAxialCheckBox = new QCheckBox(" "); WuQtUtilities::setToolTipAndStatusTip(m_volumeIndicesAxialCheckBox, @@ -125,7 +128,8 @@ m_parentToolBar(parentToolBar) + "EnableAxialSlice"); QObject::connect(m_volumeIndicesAxialCheckBox, SIGNAL(stateChanged(int)), this, SLOT(volumeIndicesAxialCheckBoxStateChanged(int))); - macroManager->addMacroSupportToObject(m_volumeIndicesAxialCheckBox); + macroManager->addMacroSupportToObject(m_volumeIndicesAxialCheckBox, + "Enable axial volume slice"); const int sliceIndexSpinBoxWidth = 55; const int sliceCoordinateSpinBoxWidth = 60; @@ -138,7 +142,8 @@ m_parentToolBar(parentToolBar) this, SLOT(volumeIndicesParasagittalSpinBoxValueChanged(int))); m_volumeIndicesParasagittalSpinBox->setObjectName(objectNamePrefix + "VolumeParasagittalSliceIndex"); - macroManager->addMacroSupportToObject(m_volumeIndicesParasagittalSpinBox); + macroManager->addMacroSupportToObject(m_volumeIndicesParasagittalSpinBox, + "Set parasagittal volume slice index"); m_volumeIndicesCoronalSpinBox = WuQFactory::newSpinBox(); m_volumeIndicesCoronalSpinBox->setFixedWidth(sliceIndexSpinBoxWidth); @@ -148,7 +153,8 @@ m_parentToolBar(parentToolBar) this, SLOT(volumeIndicesCoronalSpinBoxValueChanged(int))); m_volumeIndicesCoronalSpinBox->setObjectName(objectNamePrefix + "VolumeCoronalSliceIndex"); - macroManager->addMacroSupportToObject(m_volumeIndicesCoronalSpinBox); + macroManager->addMacroSupportToObject(m_volumeIndicesCoronalSpinBox, + "Set coronal volume slice index"); m_volumeIndicesAxialSpinBox = WuQFactory::newSpinBox(); m_volumeIndicesAxialSpinBox->setFixedWidth(sliceIndexSpinBoxWidth); @@ -158,7 +164,8 @@ m_parentToolBar(parentToolBar) this, SLOT(volumeIndicesAxialSpinBoxValueChanged(int))); m_volumeIndicesAxialSpinBox->setObjectName(objectNamePrefix + "VolumeAxialSliceIndex"); - macroManager->addMacroSupportToObject(m_volumeIndicesAxialSpinBox); + macroManager->addMacroSupportToObject(m_volumeIndicesAxialSpinBox, + "Set axial volume slice index"); m_volumeIndicesXcoordSpinBox = WuQFactory::newDoubleSpinBox(); m_volumeIndicesXcoordSpinBox->setDecimals(1); @@ -169,7 +176,8 @@ m_parentToolBar(parentToolBar) this, SLOT(volumeIndicesXcoordSpinBoxValueChanged(double))); m_volumeIndicesXcoordSpinBox->setObjectName(objectNamePrefix + "VolumeParasagittalCoordinate"); - macroManager->addMacroSupportToObject(m_volumeIndicesXcoordSpinBox); + macroManager->addMacroSupportToObject(m_volumeIndicesXcoordSpinBox, + "Set parasagittal volume slice coordinate"); m_volumeIndicesYcoordSpinBox = WuQFactory::newDoubleSpinBox(); m_volumeIndicesYcoordSpinBox->setDecimals(1); @@ -180,7 +188,8 @@ m_parentToolBar(parentToolBar) this, SLOT(volumeIndicesYcoordSpinBoxValueChanged(double))); m_volumeIndicesYcoordSpinBox->setObjectName(objectNamePrefix + "VolumeCoronalCoordinate"); - macroManager->addMacroSupportToObject(m_volumeIndicesYcoordSpinBox); + macroManager->addMacroSupportToObject(m_volumeIndicesYcoordSpinBox, + "Set coronal volume slice coordinate"); m_volumeIndicesZcoordSpinBox = WuQFactory::newDoubleSpinBox(); m_volumeIndicesZcoordSpinBox->setDecimals(1); @@ -191,7 +200,8 @@ m_parentToolBar(parentToolBar) this, SLOT(volumeIndicesZcoordSpinBoxValueChanged(double))); m_volumeIndicesZcoordSpinBox->setObjectName(objectNamePrefix + "VolumeAxialCoordinate"); - macroManager->addMacroSupportToObject(m_volumeIndicesZcoordSpinBox); + macroManager->addMacroSupportToObject(m_volumeIndicesZcoordSpinBox, + "Set axial volume slice coordinate"); const AString idToolTipText = ("When selected: If there is an identification operation " "in ths tab or any other tab with the same yoking status " @@ -219,7 +229,8 @@ m_parentToolBar(parentToolBar) WuQtUtilities::setToolButtonStyleForQt5Mac(volumeIDToolButton); m_volumeIdentificationUpdatesSlicesAction->setObjectName(objectNamePrefix + "MoveSliceToID"); - macroManager->addMacroSupportToObject(m_volumeIdentificationUpdatesSlicesAction); + macroManager->addMacroSupportToObject(m_volumeIdentificationUpdatesSlicesAction, + "Enable move volume slice to ID location"); m_volumeSliceProjectionTypeEnumComboBox = new EnumComboBoxTemplate(this); m_volumeSliceProjectionTypeEnumComboBox->setup(); @@ -230,7 +241,8 @@ m_parentToolBar(parentToolBar) "Chooses viewing orientation (oblique or orthogonal)"); m_volumeSliceProjectionTypeEnumComboBox->getComboBox()->setObjectName(objectNamePrefix + "Orthogonal/Oblique"); - macroManager->addMacroSupportToObject(m_volumeSliceProjectionTypeEnumComboBox->getComboBox()); + macroManager->addMacroSupportToObject(m_volumeSliceProjectionTypeEnumComboBox->getComboBox(), + "Select volume slice projection type"); m_obliqueMaskingAction = new QAction("M", this); m_obliqueMaskingAction->setToolTip(VolumeSliceInterpolationEdgeEffectsMaskingEnum::getToolTip()); @@ -239,7 +251,6 @@ m_parentToolBar(parentToolBar) this, &BrainBrowserWindowToolBarSliceSelection::obliqueMaskingActionTriggered); m_obliqueMaskingAction->setObjectName(objectNamePrefix + "ObliqueMasking"); - macroManager->addMacroSupportToObject(m_obliqueMaskingAction); QToolButton* obliqueMaskingToolButton = new QToolButton(); obliqueMaskingToolButton->setDefaultAction(m_obliqueMaskingAction); @@ -827,6 +838,9 @@ BrainBrowserWindowToolBarSliceSelection::obliqueMaskingActionTriggered(bool) if (maskEnum == browserTabContent->getVolumeSliceInterpolationEdgeEffectsMaskingType()) { selectedAction = action; } + WuQMacroManager::instance()->addMacroSupportToObject(action, + "Select " + action->text() + " oblique sampling"); + obliqueMaskingMenu.addAction(action); } if (selectedAction != NULL) { diff --git a/src/GuiQt/BrainBrowserWindowToolBarSurfaceMontage.cxx b/src/GuiQt/BrainBrowserWindowToolBarSurfaceMontage.cxx index cb40b2c44..c04f57c2d 100644 --- a/src/GuiQt/BrainBrowserWindowToolBarSurfaceMontage.cxx +++ b/src/GuiQt/BrainBrowserWindowToolBarSurfaceMontage.cxx @@ -80,7 +80,8 @@ m_parentToolBar(parentToolBar) " Flat Maps")); m_surfaceMontageConfigurationTypeEnumComboBox->getComboBox()->setObjectName(objectNamePrefix + ":SurfaceMontageConfiguration"); - WuQMacroManager::instance()->addMacroSupportToObject(m_surfaceMontageConfigurationTypeEnumComboBox->getComboBox()); + WuQMacroManager::instance()->addMacroSupportToObject(m_surfaceMontageConfigurationTypeEnumComboBox->getComboBox(), + "Select surface montage configuration"); m_surfaceMontageLayoutOrientationEnumComboBox = new EnumComboBoxTemplate(this); m_surfaceMontageLayoutOrientationEnumComboBox->setup(); @@ -93,7 +94,8 @@ m_parentToolBar(parentToolBar) " Portrait (Layout top-to-bottom)")); m_surfaceMontageLayoutOrientationEnumComboBox->getComboBox()->setObjectName(objectNamePrefix + ":SurfaceMontageOrientation"); - WuQMacroManager::instance()->addMacroSupportToObject(m_surfaceMontageLayoutOrientationEnumComboBox->getComboBox()); + WuQMacroManager::instance()->addMacroSupportToObject(m_surfaceMontageLayoutOrientationEnumComboBox->getComboBox(), + "Select surface montage layout"); m_cerebellarComponent = new SurfaceMontageCerebellarComponent(this, objectNamePrefix); @@ -254,7 +256,8 @@ SurfaceMontageCerebralComponent::SurfaceMontageCerebralComponent(BrainBrowserWin m_leftCheckBox->setObjectName(objectNamePrefix + ":EnableLeft"); m_leftCheckBox->setToolTip("Enable Left Surfaces"); - macroManager->addMacroSupportToObject(m_leftCheckBox); + macroManager->addMacroSupportToObject(m_leftCheckBox, + "Enable left surface in cerebral montage"); m_rightCheckBox = new QCheckBox("Right"); QObject::connect(m_rightCheckBox, SIGNAL(toggled(bool)), @@ -262,7 +265,8 @@ SurfaceMontageCerebralComponent::SurfaceMontageCerebralComponent(BrainBrowserWin m_rightCheckBox->setObjectName(objectNamePrefix + ":EnableRight"); m_rightCheckBox->setToolTip("Enable Right Surface"); - macroManager->addMacroSupportToObject(m_rightCheckBox); + macroManager->addMacroSupportToObject(m_rightCheckBox, + "Enable right surface in cerebral montage"); m_lateralCheckBox = new QCheckBox("Lateral"); QObject::connect(m_lateralCheckBox, SIGNAL(toggled(bool)), @@ -270,7 +274,8 @@ SurfaceMontageCerebralComponent::SurfaceMontageCerebralComponent(BrainBrowserWin m_lateralCheckBox->setObjectName(objectNamePrefix + ":EnableLateralView"); m_lateralCheckBox->setToolTip("Enable Lateral View"); - macroManager->addMacroSupportToObject(m_lateralCheckBox); + macroManager->addMacroSupportToObject(m_lateralCheckBox, + "Enable lateral view in cerebral montage"); m_medialCheckBox = new QCheckBox("Medial"); QObject::connect(m_medialCheckBox, SIGNAL(toggled(bool)), @@ -278,7 +283,8 @@ SurfaceMontageCerebralComponent::SurfaceMontageCerebralComponent(BrainBrowserWin m_medialCheckBox->setObjectName(objectNamePrefix + ":EnableMedialView"); m_medialCheckBox->setToolTip("Enable Medial View"); - macroManager->addMacroSupportToObject(m_medialCheckBox); + macroManager->addMacroSupportToObject(m_medialCheckBox, + "Enable medial view in cerebral montage"); m_surfaceMontageFirstSurfaceCheckBox = new QCheckBox(" "); QObject::connect(m_surfaceMontageFirstSurfaceCheckBox, SIGNAL(toggled(bool)), @@ -286,7 +292,8 @@ SurfaceMontageCerebralComponent::SurfaceMontageCerebralComponent(BrainBrowserWin m_surfaceMontageFirstSurfaceCheckBox->setObjectName(objectNamePrefix + ":EnableFirstRowSurfaces"); m_surfaceMontageFirstSurfaceCheckBox->setToolTip("Enable First Surfaces"); - macroManager->addMacroSupportToObject(m_surfaceMontageFirstSurfaceCheckBox); + macroManager->addMacroSupportToObject(m_surfaceMontageFirstSurfaceCheckBox, + "Enable first surface row in cerebral montage"); m_surfaceMontageSecondSurfaceCheckBox = new QCheckBox(" "); QObject::connect(m_surfaceMontageSecondSurfaceCheckBox, SIGNAL(toggled(bool)), @@ -294,29 +301,34 @@ SurfaceMontageCerebralComponent::SurfaceMontageCerebralComponent(BrainBrowserWin m_surfaceMontageSecondSurfaceCheckBox->setObjectName(objectNamePrefix + ":EnableSecondRowSurfaces"); m_surfaceMontageSecondSurfaceCheckBox->setToolTip("Enable Second Surfaces"); - macroManager->addMacroSupportToObject(m_surfaceMontageSecondSurfaceCheckBox); + macroManager->addMacroSupportToObject(m_surfaceMontageSecondSurfaceCheckBox, + "Enable second row in cerebral montage"); m_leftSurfaceViewController = new SurfaceSelectionViewController(this, objectNamePrefix - + ":SurfaceLeftTop"); + + ":SurfaceLeftTop", + "cerebral montage left top"); QObject::connect(m_leftSurfaceViewController, SIGNAL(surfaceSelected(Surface*)), this, SLOT(leftSurfaceSelected(Surface*))); m_leftSecondSurfaceViewController = new SurfaceSelectionViewController(this, objectNamePrefix - + ":SurfaceLeftBottom"); + + ":SurfaceLeftBottom", + "cerebral montage left bottom"); QObject::connect(m_leftSecondSurfaceViewController, SIGNAL(surfaceSelected(Surface*)), this, SLOT(leftSecondSurfaceSelected(Surface*))); m_rightSurfaceViewController = new SurfaceSelectionViewController(this, objectNamePrefix - + ":SurfaceRightTop"); + + ":SurfaceRightTop", + "cerebral montage right top"); QObject::connect(m_rightSurfaceViewController, SIGNAL(surfaceSelected(Surface*)), this, SLOT(rightSurfaceSelected(Surface*))); m_rightSecondSurfaceViewController = new SurfaceSelectionViewController(this, objectNamePrefix - + ":SurfaceRightBottom"); + + ":SurfaceRightBottom", + "cerebral montage right bottom"); QObject::connect(m_rightSecondSurfaceViewController, SIGNAL(surfaceSelected(Surface*)), this, SLOT(rightSecondSurfaceSelected(Surface*))); @@ -540,7 +552,8 @@ SurfaceMontageCerebellarComponent::SurfaceMontageCerebellarComponent(BrainBrowse m_dorsalCheckBox->setObjectName(objectNamePrefix + ":EnableDorsalView"); m_dorsalCheckBox->setToolTip("Enable Dorsal View"); - macroManager->addMacroSupportToObject(m_dorsalCheckBox); + macroManager->addMacroSupportToObject(m_dorsalCheckBox, + "Enable dorsal view in cerebellar montage"); m_ventralCheckBox = new QCheckBox("Ventral"); QObject::connect(m_ventralCheckBox, SIGNAL(toggled(bool)), @@ -548,7 +561,8 @@ SurfaceMontageCerebellarComponent::SurfaceMontageCerebellarComponent(BrainBrowse m_ventralCheckBox->setObjectName(objectNamePrefix + ":EnableVentralView"); m_ventralCheckBox->setToolTip("Enable Ventral View"); - macroManager->addMacroSupportToObject(m_ventralCheckBox); + macroManager->addMacroSupportToObject(m_ventralCheckBox, + "Enable ventral view in cerebellar montage"); m_anteriorCheckBox = new QCheckBox("Anterior"); QObject::connect(m_anteriorCheckBox, SIGNAL(toggled(bool)), @@ -556,7 +570,8 @@ SurfaceMontageCerebellarComponent::SurfaceMontageCerebellarComponent(BrainBrowse m_anteriorCheckBox->setObjectName(objectNamePrefix + ":EnableAnteriorView"); m_anteriorCheckBox->setToolTip("Enable Anterior View"); - macroManager->addMacroSupportToObject(m_anteriorCheckBox); + macroManager->addMacroSupportToObject(m_anteriorCheckBox, + "Enable anterior view in cerebellar montage"); m_posteriorCheckBox = new QCheckBox("Posterior"); QObject::connect(m_posteriorCheckBox, SIGNAL(toggled(bool)), @@ -564,7 +579,8 @@ SurfaceMontageCerebellarComponent::SurfaceMontageCerebellarComponent(BrainBrowse m_posteriorCheckBox->setObjectName(objectNamePrefix + ":EnablePosteriorView"); m_posteriorCheckBox->setToolTip("Enable Posterior View"); - macroManager->addMacroSupportToObject(m_posteriorCheckBox); + macroManager->addMacroSupportToObject(m_posteriorCheckBox, + "Enable posterior view in cerebellar montage"); m_firstSurfaceCheckBox = new QCheckBox(" "); QObject::connect(m_firstSurfaceCheckBox, SIGNAL(toggled(bool)), @@ -572,7 +588,8 @@ SurfaceMontageCerebellarComponent::SurfaceMontageCerebellarComponent(BrainBrowse m_firstSurfaceCheckBox->setObjectName(objectNamePrefix + ":EnableFirstSurface"); m_firstSurfaceCheckBox->setToolTip("Enable First Cerebellar Surface"); - macroManager->addMacroSupportToObject(m_firstSurfaceCheckBox); + macroManager->addMacroSupportToObject(m_firstSurfaceCheckBox, + "Enable first in cerebellar montage"); m_secondSurfaceCheckBox = new QCheckBox(" "); QObject::connect(m_secondSurfaceCheckBox, SIGNAL(toggled(bool)), @@ -580,17 +597,20 @@ SurfaceMontageCerebellarComponent::SurfaceMontageCerebellarComponent(BrainBrowse m_secondSurfaceCheckBox->setObjectName(objectNamePrefix + ":EnableSecondSurface"); m_secondSurfaceCheckBox->setToolTip("Enable Second Cerebellar Surface"); - macroManager->addMacroSupportToObject(m_secondSurfaceCheckBox); + macroManager->addMacroSupportToObject(m_secondSurfaceCheckBox, + "Enable second surface in cerebellar montage"); m_firstSurfaceViewController = new SurfaceSelectionViewController(this, objectNamePrefix - + ":SurfaceFirst"); + + ":SurfaceFirst", + "cerebellar monage first"); QObject::connect(m_firstSurfaceViewController, SIGNAL(surfaceSelected(Surface*)), this, SLOT(firstSurfaceSelected(Surface*))); m_secondSurfaceViewController = new SurfaceSelectionViewController(this, objectNamePrefix - + ":SecondSurface"); + + ":SecondSurface", + "cerebellar montage second"); QObject::connect(m_secondSurfaceViewController, SIGNAL(surfaceSelected(Surface*)), this, SLOT(secondSurfaceSelected(Surface*))); @@ -778,7 +798,8 @@ SurfaceMontageFlatMapsComponent::SurfaceMontageFlatMapsComponent(BrainBrowserWin m_leftSurfaceCheckBox->setObjectName(objectNamePrefix + ":EnableLeft"); m_leftSurfaceCheckBox->setToolTip("Enable Left Flat Surface"); - macroManager->addMacroSupportToObject(m_leftSurfaceCheckBox); + macroManager->addMacroSupportToObject(m_leftSurfaceCheckBox, + "Enable left surface in flat montage"); m_rightSurfaceCheckBox = new QCheckBox("Right"); QObject::connect(m_rightSurfaceCheckBox, SIGNAL(toggled(bool)), @@ -786,7 +807,8 @@ SurfaceMontageFlatMapsComponent::SurfaceMontageFlatMapsComponent(BrainBrowserWin m_rightSurfaceCheckBox->setObjectName(objectNamePrefix + ":EnableRight"); m_rightSurfaceCheckBox->setToolTip("Enable Right Flat Surface"); - macroManager->addMacroSupportToObject(m_rightSurfaceCheckBox); + macroManager->addMacroSupportToObject(m_rightSurfaceCheckBox, + "Enable right surface in flat montage"); m_cerebellumSurfaceCheckBox = new QCheckBox("Cerebellum "); QObject::connect(m_cerebellumSurfaceCheckBox, SIGNAL(toggled(bool)), @@ -794,23 +816,27 @@ SurfaceMontageFlatMapsComponent::SurfaceMontageFlatMapsComponent(BrainBrowserWin m_cerebellumSurfaceCheckBox->setObjectName(objectNamePrefix + ":EnableCerebellum"); m_cerebellumSurfaceCheckBox->setToolTip("Enable Cerebellum Flat Surface"); - macroManager->addMacroSupportToObject(m_cerebellumSurfaceCheckBox); + macroManager->addMacroSupportToObject(m_cerebellumSurfaceCheckBox, + "Enable cerebellar surface in flat montage"); m_leftSurfaceViewController = new SurfaceSelectionViewController(this, objectNamePrefix - + ":LeftFlatSurface"); + + ":LeftFlatSurface", + "montage flat left"); QObject::connect(m_leftSurfaceViewController, SIGNAL(surfaceSelected(Surface*)), this, SLOT(leftSurfaceSelected(Surface*))); m_rightSurfaceViewController = new SurfaceSelectionViewController(this, objectNamePrefix - + ":RightFlatSurface"); + + ":RightFlatSurface", + "montage flat right"); QObject::connect(m_rightSurfaceViewController, SIGNAL(surfaceSelected(Surface*)), this, SLOT(rightSurfaceSelected(Surface*))); m_cerebellumSurfaceViewController = new SurfaceSelectionViewController(this, objectNamePrefix - + ":CerebellumSurface"); + + ":CerebellumSurface", + "montage flat cerebellum"); QObject::connect(m_cerebellumSurfaceViewController, SIGNAL(surfaceSelected(Surface*)), this, SLOT(cerebellumSurfaceSelected(Surface*))); diff --git a/src/GuiQt/BrainBrowserWindowToolBarTab.cxx b/src/GuiQt/BrainBrowserWindowToolBarTab.cxx index 1ca249b3c..2f574fa26 100644 --- a/src/GuiQt/BrainBrowserWindowToolBarTab.cxx +++ b/src/GuiQt/BrainBrowserWindowToolBarTab.cxx @@ -83,7 +83,8 @@ m_lockWindowAndAllTabAspectButton(toolBarLockWindowAndAllTabAspectRatioButton) QComboBox* encapComboBox = m_yokingGroupComboBox->getComboBox(); encapComboBox->setObjectName(objectNamePrefix + ":Tab:YokingGroup"); - WuQMacroManager::instance()->addMacroSupportToObject(encapComboBox); + WuQMacroManager::instance()->addMacroSupportToObject(encapComboBox, + "Select yoking group"); m_yokeToLabel = new QLabel("Yoking:"); QObject::connect(m_yokingGroupComboBox, SIGNAL(itemActivated()), @@ -98,7 +99,8 @@ m_lockWindowAndAllTabAspectButton(toolBarLockWindowAndAllTabAspectRatioButton) this, &BrainBrowserWindowToolBarTab::lightingEnabledCheckBoxChecked); m_lightingEnabledCheckBox->setObjectName(objectNamePrefix + ":Tab:EnableShading"); - WuQMacroManager::instance()->addMacroSupportToObject(m_lightingEnabledCheckBox); + WuQMacroManager::instance()->addMacroSupportToObject(m_lightingEnabledCheckBox, + "Enable shading"); m_macroRecordingLabel = new QLabel(""); diff --git a/src/GuiQt/BrainBrowserWindowToolBarVolumeMontage.cxx b/src/GuiQt/BrainBrowserWindowToolBarVolumeMontage.cxx index 232cdba96..b291d0506 100644 --- a/src/GuiQt/BrainBrowserWindowToolBarVolumeMontage.cxx +++ b/src/GuiQt/BrainBrowserWindowToolBarVolumeMontage.cxx @@ -75,7 +75,8 @@ m_parentToolBar(parentToolBar) this, SLOT(montageRowsSpinBoxValueChanged(int))); m_montageRowsSpinBox->setObjectName(objectNamePrefix + "Rows"); - WuQMacroManager::instance()->addMacroSupportToObject(m_montageRowsSpinBox); + WuQMacroManager::instance()->addMacroSupportToObject(m_montageRowsSpinBox, + "Set volume montage rows"); QLabel* columnsLabel = new QLabel("Cols:"); @@ -88,7 +89,8 @@ m_parentToolBar(parentToolBar) this, SLOT(montageColumnsSpinBoxValueChanged(int))); m_montageColumnsSpinBox->setObjectName(objectNamePrefix + "Columns"); - WuQMacroManager::instance()->addMacroSupportToObject(m_montageColumnsSpinBox); + WuQMacroManager::instance()->addMacroSupportToObject(m_montageColumnsSpinBox, + "Set volume montage columns"); QLabel* spacingLabel = new QLabel("Step:"); spacingLabel->setToolTip("Select the number of slices stepped (incremented) between displayed montage slices"); @@ -100,7 +102,8 @@ m_parentToolBar(parentToolBar) this, SLOT(montageSpacingSpinBoxValueChanged(int))); m_montageSpacingSpinBox->setObjectName(objectNamePrefix + "Step"); - WuQMacroManager::instance()->addMacroSupportToObject(m_montageSpacingSpinBox); + WuQMacroManager::instance()->addMacroSupportToObject(m_montageSpacingSpinBox, + "Set volume montage spacing"); m_showSliceCoordinateAction = new QAction("XYZ", this); m_showSliceCoordinateAction->setText("XYZ"); @@ -110,7 +113,8 @@ m_parentToolBar(parentToolBar) this, &BrainBrowserWindowToolBarVolumeMontage::showSliceCoordinateToolButtonClicked); m_showSliceCoordinateAction->setObjectName(objectNamePrefix + "ShowCoordinateOnSlice"); - WuQMacroManager::instance()->addMacroSupportToObject(m_showSliceCoordinateAction); + WuQMacroManager::instance()->addMacroSupportToObject(m_showSliceCoordinateAction, + "Enable coordinates in volume montage"); QToolButton* showSliceCoordToolButton = new QToolButton; showSliceCoordToolButton->setDefaultAction(m_showSliceCoordinateAction); @@ -126,7 +130,8 @@ m_parentToolBar(parentToolBar) this, &BrainBrowserWindowToolBarVolumeMontage::slicePrecisionSpinBoxValueChanged); m_sliceCoordinatePrecisionSpinBox->setObjectName(objectNamePrefix + "Precision"); - WuQMacroManager::instance()->addMacroSupportToObject(m_sliceCoordinatePrecisionSpinBox); + WuQMacroManager::instance()->addMacroSupportToObject(m_sliceCoordinatePrecisionSpinBox, + "Set volume montage coordinate precision"); QToolButton* montageEnabledToolButton = new QToolButton(); @@ -140,7 +145,8 @@ m_parentToolBar(parentToolBar) WuQtUtilities::setToolButtonStyleForQt5Mac(montageEnabledToolButton); m_montageEnabledAction->setObjectName(objectNamePrefix + "Enable"); - WuQMacroManager::instance()->addMacroSupportToObject(m_montageEnabledAction); + WuQMacroManager::instance()->addMacroSupportToObject(m_montageEnabledAction, + "Enable volume slice montage"); QGridLayout* gridLayout = new QGridLayout(this); WuQtUtilities::setLayoutSpacingAndMargins(gridLayout, 0, 0); diff --git a/src/GuiQt/BrainOpenGLWidget.cxx b/src/GuiQt/BrainOpenGLWidget.cxx index 8f9b6a8aa..220eae68c 100644 --- a/src/GuiQt/BrainOpenGLWidget.cxx +++ b/src/GuiQt/BrainOpenGLWidget.cxx @@ -888,6 +888,7 @@ void BrainOpenGLWidget::mousePressEvent(QMouseEvent* me) { WuQMacroManager::instance()->addMouseEventToRecording(this, + "Mouse Press in Window" + AString::number(this->windowIndex + 1), me); Qt::MouseButton button = me->button(); @@ -976,6 +977,7 @@ void BrainOpenGLWidget::mouseReleaseEvent(QMouseEvent* me) { WuQMacroManager::instance()->addMouseEventToRecording(this, + "Mouse Release in Window" + AString::number(this->windowIndex + 1), me); Qt::MouseButton button = me->button(); @@ -1074,6 +1076,7 @@ void BrainOpenGLWidget::mouseDoubleClickEvent(QMouseEvent* me) { WuQMacroManager::instance()->addMouseEventToRecording(this, + "Mouse Double Click in Window" + AString::number(this->windowIndex + 1), me); Qt::MouseButton button = me->button(); @@ -1419,6 +1422,7 @@ void BrainOpenGLWidget::mouseMoveEvent(QMouseEvent* me) { WuQMacroManager::instance()->addMouseEventToRecording(this, + "Mouse Move in Window" + AString::number(this->windowIndex + 1), me); /* diff --git a/src/GuiQt/CaretColorEnumComboBox.cxx b/src/GuiQt/CaretColorEnumComboBox.cxx index 2354a9712..0a0559949 100644 --- a/src/GuiQt/CaretColorEnumComboBox.cxx +++ b/src/GuiQt/CaretColorEnumComboBox.cxx @@ -101,12 +101,15 @@ CaretColorEnumComboBox::CaretColorEnumComboBox(const AString& customColorSelecti * Icon for custom color. * @param objectNameForMacros * If not empty, name is used to set this combo box for macro support + * @param objectDescriptiveNameForMacros + * Descriptive name for macros * @param parent * Parent object. */ CaretColorEnumComboBox::CaretColorEnumComboBox(const AString& customColorSelectionName, const QIcon& customColorSelectionIcon, const QString& objectNameForMacros, + const QString& objectDescriptiveNameForMacros, QObject* parent) : WuQWidget(parent) { @@ -116,7 +119,8 @@ CaretColorEnumComboBox::CaretColorEnumComboBox(const AString& customColorSelecti if ( ! objectNameForMacros.isEmpty()) { this->colorComboBox->setObjectName(objectNameForMacros); this->colorComboBox->setToolTip("Select Color"); - WuQMacroManager::instance()->addMacroSupportToObject(this->colorComboBox); + WuQMacroManager::instance()->addMacroSupportToObject(this->colorComboBox, + objectDescriptiveNameForMacros); } } diff --git a/src/GuiQt/CaretColorEnumComboBox.h b/src/GuiQt/CaretColorEnumComboBox.h index 637febbaa..5882fab42 100644 --- a/src/GuiQt/CaretColorEnumComboBox.h +++ b/src/GuiQt/CaretColorEnumComboBox.h @@ -46,6 +46,7 @@ namespace caret { CaretColorEnumComboBox(const AString& customColorSelectionName, const QIcon& customColorSelectionIcon, const QString& objectNameForMacros, + const QString& objectDescriptiveNameForMacros, QObject* parent); virtual ~CaretColorEnumComboBox(); diff --git a/src/GuiQt/ChartHistoryViewController.cxx b/src/GuiQt/ChartHistoryViewController.cxx index 3d3a3e4c8..997293d8e 100644 --- a/src/GuiQt/ChartHistoryViewController.cxx +++ b/src/GuiQt/ChartHistoryViewController.cxx @@ -86,7 +86,8 @@ m_objectNamePrefix(parentObjectName this, SLOT(averageCheckBoxClicked(bool))); m_averageCheckBox->setObjectName(m_objectNamePrefix + ":ShowAverage"); - macroManager->addMacroSupportToObject(m_averageCheckBox); + macroManager->addMacroSupportToObject(m_averageCheckBox, + "Enable linew chart history average"); QPushButton* clearPushButton = new QPushButton("Clear"); clearPushButton->setFixedWidth(clearPushButton->sizeHint().width() + 20); @@ -96,7 +97,8 @@ m_objectNamePrefix(parentObjectName "Remove all charts of the selected type in this tab"); clearPushButton->setObjectName(m_objectNamePrefix + ":ClearButton"); - macroManager->addMacroSupportToObject(clearPushButton); + macroManager->addMacroSupportToObject(clearPushButton, + "Remove line charts in tab"); QLabel* maximumDisplayedLabel = new QLabel("Show last "); @@ -108,7 +110,8 @@ m_objectNamePrefix(parentObjectName m_maximumDisplayedSpinBox->setToolTip("Show Last Lines"); m_maximumDisplayedSpinBox->setObjectName(m_objectNamePrefix + ":ShowLastCount"); - macroManager->addMacroSupportToObject(m_maximumDisplayedSpinBox); + macroManager->addMacroSupportToObject(m_maximumDisplayedSpinBox, + "Set maximum line charts"); QHBoxLayout* maxDisplayedLayout = new QHBoxLayout(); maxDisplayedLayout->addWidget(maximumDisplayedLabel); @@ -323,7 +326,8 @@ ChartHistoryViewController::updateHistoryViewController() checkBox->setObjectName(m_objectNamePrefix + ":ShowChartLine" + numberString); - macroManager->addMacroSupportToObject(checkBox); + macroManager->addMacroSupportToObject(checkBox, + "Show line chart " + numberString); /* @@ -356,7 +360,8 @@ ChartHistoryViewController::updateHistoryViewController() colorComboBox->getWidget()->setObjectName(m_objectNamePrefix + ":Color" + numberString); - macroManager->addMacroSupportToObject(colorComboBox->getWidget()); + macroManager->addMacroSupportToObject(colorComboBox->getWidget(), + "Set line chart " + numberString + " color"); /* * Label diff --git a/src/GuiQt/ChartToolBoxViewController.cxx b/src/GuiQt/ChartToolBoxViewController.cxx index 298cba03c..3f844dae1 100644 --- a/src/GuiQt/ChartToolBoxViewController.cxx +++ b/src/GuiQt/ChartToolBoxViewController.cxx @@ -96,7 +96,9 @@ m_browserWindowIndex(browserWindowIndex) "History"); m_tabWidget->setObjectName(objectNamePrefix + ":Tab"); - WuQMacroManager::instance()->addMacroSupportToObjectWithToolTip(m_tabWidget, ""); + WuQMacroManager::instance()->addMacroSupportToObjectWithToolTip(m_tabWidget, + "Overlay ToolBox Chart Tab", + ""); QVBoxLayout* layout = new QVBoxLayout(this); diff --git a/src/GuiQt/ChartTwoOverlayViewController.cxx b/src/GuiQt/ChartTwoOverlayViewController.cxx index 41797ddfb..ed18907e9 100644 --- a/src/GuiQt/ChartTwoOverlayViewController.cxx +++ b/src/GuiQt/ChartTwoOverlayViewController.cxx @@ -106,6 +106,8 @@ m_chartOverlay(NULL) QString objectNamePrefix = QString(parentObjectName + ":ChartOverlay%1" + ":").arg((int)(chartOverlayIndex + 1), 2, 10, QLatin1Char('0')); + QString descriptivePrefix = QString("chart overlay " + + QString::number(chartOverlayIndex + 1)); /* * Enabled Check Box @@ -117,7 +119,8 @@ m_chartOverlay(NULL) QObject::connect(m_enabledCheckBox, SIGNAL(clicked(bool)), this, SLOT(enabledCheckBoxClicked(bool))); m_enabledCheckBox->setToolTip("Display line charts from the selected file"); - macroManager->addMacroSupportToObject(m_enabledCheckBox); + macroManager->addMacroSupportToObject(m_enabledCheckBox, + "Enable line chart display for " + descriptivePrefix); /* * Line Series Enabled Check Box @@ -129,7 +132,8 @@ m_chartOverlay(NULL) m_lineSeriesLoadingEnabledCheckBox->setToolTip("Enable loading of line charts for the selected file"); m_lineSeriesLoadingEnabledCheckBox->setObjectName(objectNamePrefix + "EnableLoading"); - macroManager->addMacroSupportToObject(m_lineSeriesLoadingEnabledCheckBox); + macroManager->addMacroSupportToObject(m_lineSeriesLoadingEnabledCheckBox, + "Enable line chart loading for " + descriptivePrefix); /* * Settings Tool Button @@ -149,7 +153,8 @@ m_chartOverlay(NULL) } m_settingsAction->setObjectName(objectNamePrefix + "ShowSettingsDialog"); - macroManager->addMacroSupportToObject(m_settingsAction); + macroManager->addMacroSupportToObject(m_settingsAction, + "Show settings dialog for " + descriptivePrefix); m_settingsToolButton->setDefaultAction(m_settingsAction); /* @@ -170,7 +175,8 @@ m_chartOverlay(NULL) } m_colorBarAction->setObjectName(objectNamePrefix + "ShowColorBar"); - macroManager->addMacroSupportToObject(m_colorBarAction); + macroManager->addMacroSupportToObject(m_colorBarAction, + "Enable color bar for " + descriptivePrefix); m_colorBarToolButton->setDefaultAction(m_colorBarAction); /* @@ -189,7 +195,8 @@ m_chartOverlay(NULL) m_constructionToolButton = new QToolButton(); QMenu* constructionMenu = createConstructionMenu(m_constructionToolButton, (objectNamePrefix - + "ConstructionMenu:")); + + "ConstructionMenu:"), + descriptivePrefix); m_constructionAction->setMenu(constructionMenu); m_constructionToolButton->setDefaultAction(m_constructionAction); m_constructionToolButton->setPopupMode(QToolButton::InstantPopup); @@ -204,7 +211,8 @@ m_chartOverlay(NULL) m_matrixTriangularViewModeToolButton); QMenu* matrixTriangularViewModeMenu = createMatrixTriangularViewModeMenu(m_matrixTriangularViewModeToolButton, (objectNamePrefix - + "TriangularViewMenu:")); + + "TriangularViewMenu:"), + descriptivePrefix); m_matrixTriangularViewModeAction->setMenu(matrixTriangularViewModeMenu); m_matrixTriangularViewModeToolButton->setDefaultAction(m_matrixTriangularViewModeAction); m_matrixTriangularViewModeToolButton->setPopupMode(QToolButton::InstantPopup); @@ -220,7 +228,8 @@ m_chartOverlay(NULL) m_axisLocationToolButton); QMenu* axisLocationMenu = createAxisLocationMenu(m_axisLocationToolButton, (objectNamePrefix - + "VerticalAxisLocationMenu:")); + + "VerticalAxisLocationMenu:"), + descriptivePrefix); m_axisLocationAction->setMenu(axisLocationMenu); m_axisLocationToolButton->setDefaultAction(m_axisLocationAction); m_axisLocationToolButton->setPopupMode(QToolButton::InstantPopup); @@ -237,7 +246,8 @@ m_chartOverlay(NULL) m_mapFileComboBox->setSizeAdjustPolicy(comboSizePolicy); m_mapFileComboBox->setObjectName(objectNamePrefix + "FileSelection"); - macroManager->addMacroSupportToObject(m_mapFileComboBox); + macroManager->addMacroSupportToObject(m_mapFileComboBox, + "Select file in " + descriptivePrefix); /* * Yoking Group @@ -270,7 +280,8 @@ m_chartOverlay(NULL) this, SLOT(allMapsCheckBoxClicked(bool))); m_allMapsCheckBox->setObjectName(objectNamePrefix + "AllMaps"); - macroManager->addMacroSupportToObject(m_allMapsCheckBox); + macroManager->addMacroSupportToObject(m_allMapsCheckBox, + "Enable all maps in " + descriptivePrefix); /* * Map/Row/Column Index Spin Box @@ -285,7 +296,8 @@ m_chartOverlay(NULL) m_mapRowOrColumnIndexSpinBox->setValue(1); m_mapRowOrColumnIndexSpinBox->setObjectName(objectNamePrefix + "MapIndex"); - macroManager->addMacroSupportToObject(m_mapRowOrColumnIndexSpinBox); + macroManager->addMacroSupportToObject(m_mapRowOrColumnIndexSpinBox, + "Select map by index in " + descriptivePrefix); /* @@ -300,7 +312,8 @@ m_chartOverlay(NULL) m_mapRowOrColumnNameComboBox->setSizeAdjustPolicy(comboSizePolicy); m_mapRowOrColumnNameComboBox->setObjectName(objectNamePrefix + "MapSelection"); - macroManager->addMacroSupportToObject(m_mapRowOrColumnNameComboBox); + macroManager->addMacroSupportToObject(m_mapRowOrColumnNameComboBox, + "Select map name in " + descriptivePrefix); } /** @@ -978,10 +991,13 @@ ChartTwoOverlayViewController::updateGraphicsWindow() * Parent widget. * @param parentObjectName * Name of parent object for macros + * @param descriptivePrefix + * Descriptive prefix for macros */ QMenu* ChartTwoOverlayViewController::createMatrixTriangularViewModeMenu(QWidget* parent, - const QString& parentObjectName) + const QString& parentObjectName, + const QString& descriptivePrefix) { std::vector allViewModes; ChartTwoMatrixTriangularViewingModeEnum::getAllEnums(allViewModes); @@ -1005,7 +1021,8 @@ ChartTwoOverlayViewController::createMatrixTriangularViewModeMenu(QWidget* paren + ChartTwoMatrixTriangularViewingModeEnum::toGuiName(viewMode)); objName = objName.replace(" ", ""); action->setObjectName(objName); - WuQMacroManager::instance()->addMacroSupportToObject(action); + WuQMacroManager::instance()->addMacroSupportToObject(action, + "Set triangular view in " + descriptivePrefix); m_matrixViewMenuData.push_back(std::make_tuple(viewMode, action, pixmap)); } @@ -1052,7 +1069,8 @@ ChartTwoOverlayViewController::menuMatrixTriangularViewModeTriggered(QAction* ac */ QMenu* ChartTwoOverlayViewController::createAxisLocationMenu(QWidget* widget, - const QString& parentObjectName) + const QString& parentObjectName, + const QString& descriptivePrefix) { std::vector axisLocations; axisLocations.push_back(ChartAxisLocationEnum::CHART_AXIS_LOCATION_LEFT); @@ -1077,7 +1095,8 @@ ChartTwoOverlayViewController::createAxisLocationMenu(QWidget* widget, + ChartAxisLocationEnum::toGuiName(axis)); objName = objName.replace(" ", ""); action->setObjectName(objName); - WuQMacroManager::instance()->addMacroSupportToObject(action); + WuQMacroManager::instance()->addMacroSupportToObject(action, + "Select chart axis location for " + descriptivePrefix); m_axisLocationMenuData.push_back(std::make_tuple(axis, action, pixmap)); } @@ -1112,10 +1131,13 @@ ChartTwoOverlayViewController::menuAxisLocationTriggered(QAction* action) * Parent widget. * @param parentObjectName * Name of parent object for macros + * @param descriptivePrefix + * Descriptive name for macros */ QMenu* ChartTwoOverlayViewController::createConstructionMenu(QWidget* parent, - const QString& menuActionNamePrefix) + const QString& menuActionNamePrefix, + const QString& descriptivePrefix) { WuQMacroManager* macroManager = WuQMacroManager::instance(); CaretAssert(macroManager); @@ -1130,7 +1152,8 @@ ChartTwoOverlayViewController::createConstructionMenu(QWidget* parent, addAboveAction->setObjectName(menuActionNamePrefix + "AddOverlayAbove"); addAboveAction->setToolTip("Add an overlay above this overlay"); - macroManager->addMacroSupportToObject(addAboveAction); + macroManager->addMacroSupportToObject(addAboveAction, + "Add overlay above " + descriptivePrefix); QAction* addBelowAction = menu->addAction("Add Overlay Below", this, @@ -1138,7 +1161,8 @@ ChartTwoOverlayViewController::createConstructionMenu(QWidget* parent, addBelowAction->setObjectName(menuActionNamePrefix + "AddOverlayBelow"); addBelowAction->setToolTip("Add an overlay below this overlay"); - macroManager->addMacroSupportToObject(addBelowAction); + macroManager->addMacroSupportToObject(addBelowAction, + "Add overlay below " + descriptivePrefix); menu->addSeparator(); @@ -1148,7 +1172,8 @@ ChartTwoOverlayViewController::createConstructionMenu(QWidget* parent, moveUpAction->setObjectName(menuActionNamePrefix + "MoveOverlayUp"); moveUpAction->setToolTip("Move this overlay up"); - macroManager->addMacroSupportToObject(moveUpAction); + macroManager->addMacroSupportToObject(moveUpAction, + "Move " + descriptivePrefix + " up"); QAction* moveDownAction = menu->addAction("Move Overlay Down", this, @@ -1156,7 +1181,8 @@ ChartTwoOverlayViewController::createConstructionMenu(QWidget* parent, moveDownAction->setObjectName(menuActionNamePrefix + "MoveOverlayDown"); moveDownAction->setToolTip("Move this overlay down"); - macroManager->addMacroSupportToObject(moveDownAction); + macroManager->addMacroSupportToObject(moveDownAction, + "Move " + descriptivePrefix + " down"); menu->addSeparator(); @@ -1166,7 +1192,8 @@ ChartTwoOverlayViewController::createConstructionMenu(QWidget* parent, removeAction->setObjectName(menuActionNamePrefix + "RemoveOverlay"); removeAction->setToolTip("Remove this overlay"); - macroManager->addMacroSupportToObject(removeAction); + macroManager->addMacroSupportToObject(removeAction, + "Remove " + descriptivePrefix + " overlay"); menu->addSeparator(); @@ -1176,7 +1203,8 @@ ChartTwoOverlayViewController::createConstructionMenu(QWidget* parent, m_constructionReloadFileAction->setObjectName(menuActionNamePrefix + "ReloadSelectedFile"); m_constructionReloadFileAction->setToolTip("Reload file in this overlay"); - macroManager->addMacroSupportToObject(m_constructionReloadFileAction); + macroManager->addMacroSupportToObject(m_constructionReloadFileAction, + "Reload file in " + descriptivePrefix); menu->addSeparator(); @@ -1186,7 +1214,8 @@ ChartTwoOverlayViewController::createConstructionMenu(QWidget* parent, copyPathFileNameAction->setObjectName(menuActionNamePrefix + "CopyPathAndFileNameToClipboard"); copyPathFileNameAction->setToolTip("Copy path and file name of file in this overlay to clipboard"); - macroManager->addMacroSupportToObject(copyPathFileNameAction); + macroManager->addMacroSupportToObject(copyPathFileNameAction, + "Copy path and name to clipboard from " + descriptivePrefix); QAction* copyMapNameAction = menu->addAction("Copy Map Name to Clipboard", this, @@ -1194,7 +1223,8 @@ ChartTwoOverlayViewController::createConstructionMenu(QWidget* parent, copyMapNameAction->setObjectName(menuActionNamePrefix + "CopyMapNameToClipboard"); copyMapNameAction->setToolTip("Copy name of selected map to the clipboard"); - macroManager->addMacroSupportToObject(copyMapNameAction); + macroManager->addMacroSupportToObject(copyMapNameAction, + "Copy map name to clipboard from " + descriptivePrefix); return menu; diff --git a/src/GuiQt/ChartTwoOverlayViewController.h b/src/GuiQt/ChartTwoOverlayViewController.h index 2f20ab52a..039273bd2 100644 --- a/src/GuiQt/ChartTwoOverlayViewController.h +++ b/src/GuiQt/ChartTwoOverlayViewController.h @@ -121,13 +121,16 @@ namespace caret { void updateGraphicsWindow(); QMenu* createConstructionMenu(QWidget* parent, - const QString& parentObjectName); + const QString& parentObjectName, + const QString& descriptivePrefix); QMenu* createMatrixTriangularViewModeMenu(QWidget* widget, - const QString& parentObjectName); + const QString& parentObjectName, + const QString& descriptivePrefix); QMenu* createAxisLocationMenu(QWidget* widget, - const QString& parentObjectName); + const QString& parentObjectName, + const QString& descriptivePrefix); void validateYokingSelection(); diff --git a/src/GuiQt/CiftiConnectivityMatrixViewController.cxx b/src/GuiQt/CiftiConnectivityMatrixViewController.cxx index 377af42be..4969b7dca 100644 --- a/src/GuiQt/CiftiConnectivityMatrixViewController.cxx +++ b/src/GuiQt/CiftiConnectivityMatrixViewController.cxx @@ -179,13 +179,16 @@ CiftiConnectivityMatrixViewController::updateViewController() const QString objectNamePrefix(m_objectNamePrefix + QString("%1").arg((int)i+1, 2, 10, QLatin1Char('0')) + ":"); + const QString descriptivePrefix("connectivity file " + QString::number(i+1)); + checkBox = new QCheckBox(""); checkBox->setToolTip("When selected, load data during\n" "an identification operation"); m_fileEnableCheckBoxes.push_back(checkBox); checkBox->setObjectName(objectNamePrefix + "Enable"); - macroManager->addMacroSupportToObject(checkBox); + macroManager->addMacroSupportToObject(checkBox, + "Enable " + descriptivePrefix); const AString dynToolTip("This option is enabled only for .dynconn.nii (dynamic connectivity) files. " "When checked, this dynamic connectivity file will appear in the Overlay Layers' File selection combo box. " @@ -199,7 +202,8 @@ CiftiConnectivityMatrixViewController::updateViewController() m_layerCheckBoxes.push_back(layerCheckBox); layerCheckBox->setObjectName(objectNamePrefix + "EnableLayer"); - macroManager->addMacroSupportToObject(layerCheckBox); + macroManager->addMacroSupportToObject(layerCheckBox, + "Enable dynamic connectivity for " + descriptivePrefix); lineEdit = new QLineEdit(); lineEdit->setReadOnly(true); @@ -211,14 +215,16 @@ CiftiConnectivityMatrixViewController::updateViewController() m_fileCopyToolButtons.push_back(copyToolButton); copyToolButton->setObjectName(objectNamePrefix + "CopyButton"); - macroManager->addMacroSupportToObject(copyToolButton); + macroManager->addMacroSupportToObject(copyToolButton, + "Copy load row to new CIFTI scalar file for " + descriptivePrefix); comboBox = new QComboBox(); m_fiberOrientationFileComboBoxes.push_back(comboBox); comboBox->setToolTip("Select Fiber Orientation File"); comboBox->setObjectName(objectNamePrefix + "FiberOrientationFile"); - macroManager->addMacroSupportToObject(comboBox); + macroManager->addMacroSupportToObject(comboBox, + "Select fiber orientation for " + descriptivePrefix); QObject::connect(copyToolButton, SIGNAL(clicked()), m_signalMapperFileCopyToolButton, SLOT(map())); diff --git a/src/GuiQt/DisplayGroupEnumComboBox.cxx b/src/GuiQt/DisplayGroupEnumComboBox.cxx index 3a3ca2f28..07ea86299 100644 --- a/src/GuiQt/DisplayGroupEnumComboBox.cxx +++ b/src/GuiQt/DisplayGroupEnumComboBox.cxx @@ -49,6 +49,7 @@ using namespace caret; */ DisplayGroupEnumComboBox::DisplayGroupEnumComboBox(QObject* parent) : DisplayGroupEnumComboBox(parent, + "", "") { @@ -75,9 +76,12 @@ DisplayGroupEnumComboBox::DisplayGroupEnumComboBox(QObject* parent) * Parent object. * @param objectNameForMacros * Name of object for macros + * @param descriptiveNameForMacros + * Descriptive name for macros */ DisplayGroupEnumComboBox::DisplayGroupEnumComboBox(QObject* parent, - const QString& objectNameForMacros) + const QString& objectNameForMacros, + const QString& descriptiveNameForMacros) : WuQWidget(parent) { std::vector allDisplayGroups; @@ -96,7 +100,8 @@ DisplayGroupEnumComboBox::DisplayGroupEnumComboBox(QObject* parent, if ( ! objectNameForMacros.isEmpty()) { this->displayGroupComboBox->setToolTip("Select Display Group"); this->displayGroupComboBox->setObjectName(objectNameForMacros); - WuQMacroManager::instance()->addMacroSupportToObject(this->displayGroupComboBox); + WuQMacroManager::instance()->addMacroSupportToObject(this->displayGroupComboBox, + "Select display group for " + descriptiveNameForMacros); } } diff --git a/src/GuiQt/DisplayGroupEnumComboBox.h b/src/GuiQt/DisplayGroupEnumComboBox.h index fdc19d056..6141fb56a 100644 --- a/src/GuiQt/DisplayGroupEnumComboBox.h +++ b/src/GuiQt/DisplayGroupEnumComboBox.h @@ -35,7 +35,8 @@ namespace caret { DisplayGroupEnumComboBox(QObject* parent); DisplayGroupEnumComboBox(QObject* parent, - const QString& objectNameForMacros); + const QString& objectNameForMacros, + const QString& descriptiveNameForMacros); virtual ~DisplayGroupEnumComboBox(); diff --git a/src/GuiQt/FociProjectionDialog.cxx b/src/GuiQt/FociProjectionDialog.cxx index 16d09d6a0..ac79f9081 100644 --- a/src/GuiQt/FociProjectionDialog.cxx +++ b/src/GuiQt/FociProjectionDialog.cxx @@ -208,7 +208,8 @@ FociProjectionDialog::createSurfaceSelectionWidget() m_leftSurfaceCheckBox = new QCheckBox("Left: "); m_leftSurfaceCheckBox->setChecked(true); m_leftSurfaceViewController = new SurfaceSelectionViewController(this, leftBrainStructure, - (m_objectNamePrefix + ":LeftSurface")); + (m_objectNamePrefix + ":LeftSurface"), + "foci projection left"); m_leftSurfaceViewController->updateControl(); } @@ -218,7 +219,8 @@ FociProjectionDialog::createSurfaceSelectionWidget() m_rightSurfaceCheckBox = new QCheckBox("Right: "); m_rightSurfaceCheckBox->setChecked(true); m_rightSurfaceViewController = new SurfaceSelectionViewController(this, rightBrainStructure, - (m_objectNamePrefix + ":RightSurface")); + (m_objectNamePrefix + ":RightSurface"), + "foci projection right"); m_rightSurfaceViewController->updateControl(); } @@ -229,7 +231,8 @@ FociProjectionDialog::createSurfaceSelectionWidget() m_cerebellumSurfaceCheckBox->setChecked(true); m_cerebellumSurfaceViewController = new SurfaceSelectionViewController(this, cerebellumBrainStructure, - (m_objectNamePrefix + ":CerebellumSurface")); + (m_objectNamePrefix + ":CerebellumSurface"), + "foci projection cerebellum"); m_cerebellumSurfaceViewController->updateControl(); } diff --git a/src/GuiQt/FociSelectionViewController.cxx b/src/GuiQt/FociSelectionViewController.cxx index 8f55263bc..e5888c340 100644 --- a/src/GuiQt/FociSelectionViewController.cxx +++ b/src/GuiQt/FociSelectionViewController.cxx @@ -103,7 +103,8 @@ m_objectNamePrefix(parentObjectName this, SLOT(processAttributesChanges())); m_fociDisplayCheckBox->setObjectName(m_objectNamePrefix + ":DisplayFoci"); - WuQMacroManager::instance()->addMacroSupportToObject(m_fociDisplayCheckBox); + WuQMacroManager::instance()->addMacroSupportToObject(m_fociDisplayCheckBox, + "Enable foci display"); QWidget* attributesWidget = this->createAttributesWidget(); QWidget* selectionWidget = this->createSelectionWidget(); @@ -115,6 +116,11 @@ m_objectNamePrefix(parentObjectName m_tabWidget->addTab(selectionWidget, "Selection"); m_tabWidget->setCurrentWidget(attributesWidget); + m_tabWidget->getTabBar()->setToolTip("Select foci tab"); + m_tabWidget->getTabBar()->setObjectName(m_objectNamePrefix + + ":Tab"); + WuQMacroManager::instance()->addMacroSupportToObject(m_tabWidget->getTabBar(), + "Select features toolbox foci tab"); QVBoxLayout* layout = new QVBoxLayout(this); //WuQtUtilities::setLayoutSpacingAndMargins(layout, 2, 2); @@ -139,13 +145,16 @@ FociSelectionViewController::~FociSelectionViewController() FociSelectionViewController::allFociSelectionViewControllers.erase(this); } - +/** + * @return New instance of foci selection widget + */ QWidget* FociSelectionViewController::createSelectionWidget() { m_fociClassNameHierarchyViewController = new GroupAndNameHierarchyViewController(m_browserWindowIndex, (m_objectNamePrefix + ":Selection"), + "foci", this); return m_fociClassNameHierarchyViewController; @@ -165,7 +174,8 @@ FociSelectionViewController::createAttributesWidget() this, SLOT(processAttributesChanges())); m_fociContralateralCheckBox->setObjectName(m_objectNamePrefix + ":Contralateral"); - macroManager->addMacroSupportToObject(m_fociContralateralCheckBox); + macroManager->addMacroSupportToObject(m_fociContralateralCheckBox, + "Enable contralateral foci"); m_pasteOntoSurfaceCheckBox = new QCheckBox("Paste Onto Surface"); m_pasteOntoSurfaceCheckBox->setToolTip("Place the foci onto the surface"); @@ -173,7 +183,8 @@ FociSelectionViewController::createAttributesWidget() this, SLOT(processAttributesChanges())); m_pasteOntoSurfaceCheckBox->setObjectName(m_objectNamePrefix + ":PasteOntoSurface"); - macroManager->addMacroSupportToObject(m_pasteOntoSurfaceCheckBox); + macroManager->addMacroSupportToObject(m_pasteOntoSurfaceCheckBox, + "Enable paste foci onto surface"); QLabel* coloringLabel = new QLabel("Coloring"); m_coloringTypeComboBox = new EnumComboBoxTemplate(this); @@ -184,13 +195,15 @@ FociSelectionViewController::createAttributesWidget() this, SLOT(processAttributesChanges())); m_coloringTypeComboBox->getComboBox()->setObjectName(m_objectNamePrefix + ":ColorType"); - macroManager->addMacroSupportToObject(m_coloringTypeComboBox->getComboBox()); + macroManager->addMacroSupportToObject(m_coloringTypeComboBox->getComboBox(), + "Select foci color type"); QLabel* standardColorLabel = new QLabel("Standard Color"); m_standardColorComboBox = new CaretColorEnumComboBox("", QIcon(), (m_objectNamePrefix + ":Color"), + "Select foci standard color", this); m_standardColorComboBox->getWidget()->setToolTip("Select the standard color"); QObject::connect(m_standardColorComboBox, SIGNAL(colorSelected(const CaretColorEnum::Enum)), @@ -212,7 +225,8 @@ FociSelectionViewController::createAttributesWidget() this, SLOT(processAttributesChanges())); m_drawTypeComboBox->setObjectName(m_objectNamePrefix + ":DrawingStyle"); - macroManager->addMacroSupportToObject(m_drawTypeComboBox); + macroManager->addMacroSupportToObject(m_drawTypeComboBox, + "Select foci drawing style"); float minLineWidth = 0; float maxLineWidth = 1000; @@ -232,7 +246,8 @@ FociSelectionViewController::createAttributesWidget() this, SLOT(processAttributesChanges())); m_sizeSpinBox->setObjectName(m_objectNamePrefix + ":Diameter"); - macroManager->addMacroSupportToObject(m_sizeSpinBox); + macroManager->addMacroSupportToObject(m_sizeSpinBox, + "Set foci size"); QWidget* gridWidget = new QWidget(); diff --git a/src/GuiQt/GroupAndNameHierarchyViewController.cxx b/src/GuiQt/GroupAndNameHierarchyViewController.cxx index f1e6cb576..d84daa230 100644 --- a/src/GuiQt/GroupAndNameHierarchyViewController.cxx +++ b/src/GuiQt/GroupAndNameHierarchyViewController.cxx @@ -71,11 +71,14 @@ using namespace caret; * Index of browser window * @param objectNameForMacros * Name of this object for macros + * @param descriptiveNameForMacros + * Descriptive name for macros * @param parent * Parent widget. */ GroupAndNameHierarchyViewController::GroupAndNameHierarchyViewController(const int32_t browserWindowIndex, const QString& objectNameForMacros, + const QString& descriptiveNameForMacros, QWidget* parent) : QWidget(parent) { @@ -85,7 +88,8 @@ GroupAndNameHierarchyViewController::GroupAndNameHierarchyViewController(const i m_previousBrowserTabIndex = -1; m_browserWindowIndex = browserWindowIndex; - QWidget* allOnOffWidget = createAllOnOffControls(objectNameForMacros); + QWidget* allOnOffWidget = createAllOnOffControls(objectNameForMacros, + descriptiveNameForMacros); m_modelTreeWidgetLayout = new QVBoxLayout(); WuQtUtilities::setLayoutSpacingAndMargins(m_modelTreeWidgetLayout, 0, 0); @@ -186,9 +190,12 @@ GroupAndNameHierarchyViewController::updateGraphics() * * @param objectNameForMacros * Name of this object for macros + * @param descriptiveNameForMacros + * Descriptive name for macros */ QWidget* -GroupAndNameHierarchyViewController::createAllOnOffControls(const QString& objectNameForMacros) +GroupAndNameHierarchyViewController::createAllOnOffControls(const QString& objectNameForMacros, + const QString& descriptiveNameForMacros) { WuQMacroManager* macroManager = WuQMacroManager::instance(); @@ -200,7 +207,8 @@ GroupAndNameHierarchyViewController::createAllOnOffControls(const QString& objec + ":AllOn"); QObject::connect(onPushButton, SIGNAL(clicked()), this, SLOT(allOnPushButtonClicked())); - macroManager->addMacroSupportToObject(onPushButton); + macroManager->addMacroSupportToObject(onPushButton, + "Turn on all in " + descriptiveNameForMacros + " selection"); QPushButton* offPushButton = new QPushButton("Off"); offPushButton->setToolTip("Turn all of"); @@ -208,7 +216,8 @@ GroupAndNameHierarchyViewController::createAllOnOffControls(const QString& objec + ":AllOff"); QObject::connect(offPushButton, SIGNAL(clicked()), this, SLOT(allOffPushButtonClicked())); - macroManager->addMacroSupportToObject(offPushButton); + macroManager->addMacroSupportToObject(offPushButton, + "Turn off all in " + descriptiveNameForMacros + " selection"); QWidget* w = new QWidget(); QHBoxLayout* layout = new QHBoxLayout(w); diff --git a/src/GuiQt/GroupAndNameHierarchyViewController.h b/src/GuiQt/GroupAndNameHierarchyViewController.h index bbc4290bd..6e5cb8528 100644 --- a/src/GuiQt/GroupAndNameHierarchyViewController.h +++ b/src/GuiQt/GroupAndNameHierarchyViewController.h @@ -50,6 +50,7 @@ namespace caret { public: GroupAndNameHierarchyViewController(const int32_t browserWindowIndex, const QString& objectNameForMacros, + const QString& descriptiveNameForMacros, QWidget* parent); virtual ~GroupAndNameHierarchyViewController(); @@ -96,7 +97,8 @@ namespace caret { void createTreeWidget(); - QWidget* createAllOnOffControls(const QString& objectNameForMacros); + QWidget* createAllOnOffControls(const QString& objectNameForMacros, + const QString& descriptiveNameForMacros); void setAllSelected(bool selected); diff --git a/src/GuiQt/GuiManager.cxx b/src/GuiQt/GuiManager.cxx index f04f4337a..1484bd8da 100644 --- a/src/GuiQt/GuiManager.cxx +++ b/src/GuiQt/GuiManager.cxx @@ -236,7 +236,8 @@ GuiManager::initializeGuiManager() m_informationDisplayDialogEnabledAction->setIconText("Info"); m_informationDisplayDialogEnabledAction->blockSignals(false); m_informationDisplayDialogEnabledAction->setObjectName("ToolBar:ShowInformationWindow"); - WuQMacroManager::instance()->addMacroSupportToObject(m_informationDisplayDialogEnabledAction); + WuQMacroManager::instance()->addMacroSupportToObject(m_informationDisplayDialogEnabledAction, + "Display information window"); /* * Identify brainordinate window @@ -264,7 +265,8 @@ GuiManager::initializeGuiManager() m_identifyBrainordinateDialogEnabledAction->setChecked(false); m_identifyBrainordinateDialogEnabledAction->blockSignals(false); m_identifyBrainordinateDialogEnabledAction->setObjectName("ToolBar:ShowIdentifyBrainordinateWindow"); - WuQMacroManager::instance()->addMacroSupportToObject(m_identifyBrainordinateDialogEnabledAction); + WuQMacroManager::instance()->addMacroSupportToObject(m_identifyBrainordinateDialogEnabledAction, + "Display Identify Brainordinate Window"); /* * Scene dialog action @@ -289,7 +291,8 @@ GuiManager::initializeGuiManager() m_sceneDialogDisplayAction->setChecked(false); m_sceneDialogDisplayAction->blockSignals(false); m_sceneDialogDisplayAction->setObjectName("ToolBar:ShowScenesWindow"); - WuQMacroManager::instance()->addMacroSupportToObject(m_sceneDialogDisplayAction); + WuQMacroManager::instance()->addMacroSupportToObject(m_sceneDialogDisplayAction, + "Display Scene Dialog"); /* * Help dialog action @@ -315,7 +318,8 @@ GuiManager::initializeGuiManager() m_helpViewerDialogDisplayAction->setChecked(false); m_helpViewerDialogDisplayAction->blockSignals(false); m_helpViewerDialogDisplayAction->setObjectName("ToolBar:ShowHelpWindow"); - WuQMacroManager::instance()->addMacroSupportToObject(m_helpViewerDialogDisplayAction); + WuQMacroManager::instance()->addMacroSupportToObject(m_helpViewerDialogDisplayAction, + "Display Help Dialog"); /* * Data tooltip action is created when requested by a toolbar @@ -1935,7 +1939,8 @@ GuiManager::getDataToolTipsAction(QWidget* buttonWidget) m_dataToolTipsEnabledAction->setChecked(SessionManager::get()->getDataToolTipsManager()->isEnabled()); m_dataToolTipsEnabledAction->setObjectName("ToolBar:DataToolTipsEnabled"); - WuQMacroManager::instance()->addMacroSupportToObject(m_dataToolTipsEnabledAction); + WuQMacroManager::instance()->addMacroSupportToObject(m_dataToolTipsEnabledAction, + "Enable data tool tips"); } return m_dataToolTipsEnabledAction; diff --git a/src/GuiQt/ImageSelectionViewController.cxx b/src/GuiQt/ImageSelectionViewController.cxx index 881eadd56..53661617c 100644 --- a/src/GuiQt/ImageSelectionViewController.cxx +++ b/src/GuiQt/ImageSelectionViewController.cxx @@ -81,7 +81,8 @@ m_objectNamePrefix(parentObjectName QLabel* groupLabel = new QLabel("Group"); m_imagesDisplayGroupComboBox = new DisplayGroupEnumComboBox(this, (m_objectNamePrefix - + ":DisplayGroup")); + + ":DisplayGroup"), + "images"); QObject::connect(m_imagesDisplayGroupComboBox, SIGNAL(displayGroupSelected(const DisplayGroupEnum::Enum)), this, SLOT(imageDisplayGroupSelected(const DisplayGroupEnum::Enum))); @@ -96,7 +97,8 @@ m_objectNamePrefix(parentObjectName m_imageDisplayCheckBox->setToolTip("Display Image"); m_imageDisplayCheckBox->setObjectName(m_objectNamePrefix + ":Display"); - macroManager->addMacroSupportToObject(m_imageDisplayCheckBox); + macroManager->addMacroSupportToObject(m_imageDisplayCheckBox, + "Enable display of image"); m_controlPointsDisplayCheckBox = new QCheckBox("Display Control Points"); QObject::connect(m_controlPointsDisplayCheckBox, SIGNAL(clicked(bool)), @@ -104,7 +106,8 @@ m_objectNamePrefix(parentObjectName m_controlPointsDisplayCheckBox->setToolTip("Display Control Points"); m_controlPointsDisplayCheckBox->setObjectName(m_objectNamePrefix + ":DisplayControlPoints"); - macroManager->addMacroSupportToObject(m_controlPointsDisplayCheckBox); + macroManager->addMacroSupportToObject(m_controlPointsDisplayCheckBox, + "Display image control points"); QWidget* attributesWidget = this->createAttributesWidget(); QWidget* selectionWidget = this->createSelectionWidget(); @@ -118,7 +121,9 @@ m_objectNamePrefix(parentObjectName m_tabWidget->setCurrentWidget(attributesWidget); m_tabWidget->getTabBar()->setObjectName(m_objectNamePrefix + ":Tab"); - macroManager->addMacroSupportToObjectWithToolTip(m_tabWidget->getTabBar(), ""); + macroManager->addMacroSupportToObjectWithToolTip(m_tabWidget->getTabBar(), + "Select features toolbox image tab", + "Features ToolBox Image Tab"); QVBoxLayout* layout = new QVBoxLayout(this); // WuQtUtilities::setLayoutSpacingAndMargins(layout, 2, 2); @@ -220,7 +225,8 @@ ImageSelectionViewController::createAttributesWidget() this, SLOT(processAttributesChanges())); m_depthComboBox->getComboBox()->setObjectName(objectNamePrefix + ":DepthPosition"); - macroManager->addMacroSupportToObject(m_depthComboBox->getComboBox()); + macroManager->addMacroSupportToObject(m_depthComboBox->getComboBox(), + "Set image depth"); const float threshMin = -1.0; const float threshMax = 100000.0; @@ -237,7 +243,8 @@ ImageSelectionViewController::createAttributesWidget() this, SLOT(processAttributesChanges())); m_thresholdMinimumSpinBox->setObjectName(objectNamePrefix + "MinimumThreshold"); - macroManager->addMacroSupportToObject(m_thresholdMinimumSpinBox); + macroManager->addMacroSupportToObject(m_thresholdMinimumSpinBox, + "Set image threshold minimum"); QLabel* thresholdMaximumLabel = new QLabel("Maximum Threshold"); m_thresholdMaximumSpinBox = WuQFactory::newDoubleSpinBox(); @@ -251,7 +258,8 @@ ImageSelectionViewController::createAttributesWidget() this, SLOT(processAttributesChanges())); m_thresholdMaximumSpinBox->setObjectName(objectNamePrefix + "MaximumThreshold"); - macroManager->addMacroSupportToObject(m_thresholdMaximumSpinBox); + macroManager->addMacroSupportToObject(m_thresholdMaximumSpinBox, + "Set image threshold maximum"); const float minOpacity = 0.0; const float maxOpacity = 1.0; @@ -267,7 +275,8 @@ ImageSelectionViewController::createAttributesWidget() this, SLOT(processAttributesChanges())); m_opacitySpinBox->setObjectName(objectNamePrefix + ":Opacity"); - macroManager->addMacroSupportToObject(m_opacitySpinBox); + macroManager->addMacroSupportToObject(m_opacitySpinBox, + "Set image opacity"); QWidget* gridWidget = new QWidget(); @@ -446,7 +455,11 @@ ImageSelectionViewController::updateImageViewController() + ":Selection:Image" + QString("%1").arg((int)i+1, 2, 10, QLatin1Char('0'))); rb->setObjectName(buttonName); - WuQMacroManager::instance()->addMacroSupportToObjectWithToolTip(rb, ""); + const QString descriptiveName("Select Image" + + QString("%1").arg(i + 1)); + WuQMacroManager::instance()->addMacroSupportToObjectWithToolTip(rb, + descriptiveName, + ""); } numRadioButtons = static_cast(m_imageRadioButtons.size()); diff --git a/src/GuiQt/LabelSelectionViewController.cxx b/src/GuiQt/LabelSelectionViewController.cxx index 0505c6765..02cd98890 100644 --- a/src/GuiQt/LabelSelectionViewController.cxx +++ b/src/GuiQt/LabelSelectionViewController.cxx @@ -88,7 +88,8 @@ m_objectNamePrefix(parentObjectName QLabel* groupLabel = new QLabel("Group"); m_labelsDisplayGroupComboBox = new DisplayGroupEnumComboBox(this, (m_objectNamePrefix - + ":DisplayGroup")); + + ":DisplayGroup"), + "labels"); QObject::connect(m_labelsDisplayGroupComboBox, SIGNAL(displayGroupSelected(const DisplayGroupEnum::Enum)), this, SLOT(labelDisplayGroupSelected(const DisplayGroupEnum::Enum))); @@ -126,6 +127,7 @@ LabelSelectionViewController::createSelectionWidget() m_labelClassNameHierarchyViewController = new GroupAndNameHierarchyViewController(m_browserWindowIndex, (m_objectNamePrefix + ":Selection"), + "labels", this); return m_labelClassNameHierarchyViewController; diff --git a/src/GuiQt/MapYokingGroupComboBox.cxx b/src/GuiQt/MapYokingGroupComboBox.cxx index 9adeece77..17c8757a0 100644 --- a/src/GuiQt/MapYokingGroupComboBox.cxx +++ b/src/GuiQt/MapYokingGroupComboBox.cxx @@ -50,7 +50,8 @@ using namespace caret; */ MapYokingGroupComboBox::MapYokingGroupComboBox(QObject* parent) : MapYokingGroupComboBox(parent, - "") + "", + "") { } //: WuQWidget(parent) @@ -69,9 +70,17 @@ MapYokingGroupComboBox::MapYokingGroupComboBox(QObject* parent) /** * Constructor. + * + * @param parent + * Parent of this combo box + * @param objectName + * Object name for macros + * @param descriptiveName + Descriptive name for macros */ MapYokingGroupComboBox::MapYokingGroupComboBox(QObject* parent, - const QString& objectName) + const QString& objectName, + const QString& descriptiveName) : WuQWidget(parent) { m_comboBox = new EnumComboBoxTemplate(this); @@ -86,7 +95,8 @@ MapYokingGroupComboBox::MapYokingGroupComboBox(QObject* parent, if ( ! objectName.isEmpty()) { QWidget* encapsulatedComboBox = m_comboBox->getWidget(); encapsulatedComboBox->setObjectName(objectName); - WuQMacroManager::instance()->addMacroSupportToObject(encapsulatedComboBox); + WuQMacroManager::instance()->addMacroSupportToObject(encapsulatedComboBox, + "Select map yoking for " + descriptiveName); } } diff --git a/src/GuiQt/MapYokingGroupComboBox.h b/src/GuiQt/MapYokingGroupComboBox.h index ae1e91bae..787a95848 100644 --- a/src/GuiQt/MapYokingGroupComboBox.h +++ b/src/GuiQt/MapYokingGroupComboBox.h @@ -40,7 +40,8 @@ namespace caret { MapYokingGroupComboBox(QObject* parent); MapYokingGroupComboBox(QObject* parent, - const QString& objectName); + const QString& objectName, + const QString& descriptiveName); virtual ~MapYokingGroupComboBox(); diff --git a/src/GuiQt/OverlayViewController.cxx b/src/GuiQt/OverlayViewController.cxx index 6398c55c5..53950b557 100644 --- a/src/GuiQt/OverlayViewController.cxx +++ b/src/GuiQt/OverlayViewController.cxx @@ -110,6 +110,7 @@ OverlayViewController::OverlayViewController(const Qt::Orientation orientation, QString objectNamePrefix = QString(parentObjectName + ":Overlay%1" + ":").arg((int)(overlayIndex + 1), 2, 10, QLatin1Char('0')); + const QString descriptivePrefix = QString("overlay %1").arg(overlayIndex + 1); /* * Enabled Check Box */ @@ -120,7 +121,8 @@ OverlayViewController::OverlayViewController(const Qt::Orientation orientation, QObject::connect(this->enabledCheckBox, SIGNAL(clicked(bool)), this, SLOT(enabledCheckBoxClicked(bool))); this->enabledCheckBox->setToolTip("Enables display of this overlay"); - macroManager->addMacroSupportToObject(this->enabledCheckBox); + macroManager->addMacroSupportToObject(this->enabledCheckBox, + "Enable " + descriptivePrefix); /* * File Selection Combo Box @@ -134,7 +136,8 @@ OverlayViewController::OverlayViewController(const Qt::Orientation orientation, this, SLOT(fileComboBoxSelected(int))); this->fileComboBox->setToolTip("Selects file for this overlay"); this->fileComboBox->setSizeAdjustPolicy(comboSizePolicy); - macroManager->addMacroSupportToObject(this->fileComboBox); + macroManager->addMacroSupportToObject(this->fileComboBox, + ("Select file in " + descriptivePrefix)); /* * Map Index Spin Box @@ -145,7 +148,8 @@ OverlayViewController::OverlayViewController(const Qt::Orientation orientation, QObject::connect(m_mapIndexSpinBox, SIGNAL(valueChanged(int)), this, SLOT(mapIndexSpinBoxValueChanged(int))); m_mapIndexSpinBox->setToolTip("Select map by its index"); - macroManager->addMacroSupportToObject(m_mapIndexSpinBox); + macroManager->addMacroSupportToObject(m_mapIndexSpinBox, + ("Select " + descriptivePrefix + " map index")); /* * Map Name Combo Box @@ -159,7 +163,8 @@ OverlayViewController::OverlayViewController(const Qt::Orientation orientation, this, SLOT(mapNameComboBoxSelected(int))); this->mapNameComboBox->setToolTip("Select map by its name"); this->mapNameComboBox->setSizeAdjustPolicy(comboSizePolicy); - macroManager->addMacroSupportToObject(this->mapNameComboBox); + macroManager->addMacroSupportToObject(this->mapNameComboBox, + ("Select " + descriptivePrefix + " map name")); /* * Opacity double spin box @@ -175,7 +180,8 @@ OverlayViewController::OverlayViewController(const Qt::Orientation orientation, QObject::connect(this->opacityDoubleSpinBox, SIGNAL(valueChanged(double)), this, SLOT(opacityDoubleSpinBoxValueChanged(double))); this->opacityDoubleSpinBox->setToolTip("Opacity (0.0=transparent, 1.0=opaque)"); - macroManager->addMacroSupportToObject(this->opacityDoubleSpinBox); + macroManager->addMacroSupportToObject(this->opacityDoubleSpinBox, + ("Set " + descriptivePrefix + " opacity")); /* * ColorBar Tool Button @@ -188,7 +194,8 @@ OverlayViewController::OverlayViewController(const Qt::Orientation orientation, m_colorBarToolButton->setCheckable(true); m_colorBarToolButton->setObjectName(objectNamePrefix + "ShowColorBar"); - macroManager->addMacroSupportToObject(m_colorBarToolButton); + macroManager->addMacroSupportToObject(m_colorBarToolButton, + ("Enable " + descriptivePrefix + " colorbar")); // /* // * ColorBar Tool Button @@ -227,7 +234,8 @@ OverlayViewController::OverlayViewController(const Qt::Orientation orientation, } QToolButton* settingsToolButton = new QToolButton(); settingsToolButton->setDefaultAction(this->settingsAction); - macroManager->addMacroSupportToObject(this->settingsAction); + macroManager->addMacroSupportToObject(this->settingsAction, + ("Display " + descriptivePrefix + " palette settings")); /* * Construction Tool Button @@ -244,6 +252,7 @@ OverlayViewController::OverlayViewController(const Qt::Orientation orientation, } m_constructionToolButton = new QToolButton(); QMenu* constructionMenu = createConstructionMenu(m_constructionToolButton, + descriptivePrefix, (objectNamePrefix + "ConstructionMenu:")); this->constructionAction->setMenu(constructionMenu); @@ -265,7 +274,8 @@ OverlayViewController::OverlayViewController(const Qt::Orientation orientation, */ m_mapYokingGroupComboBox = new MapYokingGroupComboBox(this, (objectNamePrefix - + "MapYokingSelection")); + + "MapYokingSelection"), + descriptivePrefix); m_mapYokingGroupComboBox->getWidget()->setStatusTip("Synchronize enabled status and map indices)"); m_mapYokingGroupComboBox->getWidget()->setToolTip("Yoke to Overlay Mapped Files"); #ifdef CARET_OS_MACOSX @@ -857,11 +867,14 @@ OverlayViewController::updateGraphicsWindow() * Create the construction menu. * @param parent * Parent widget. + * @param descriptivePrefix + * Descriptive prefix * @param menuActionNamePrefix * Prefix for macros */ QMenu* OverlayViewController::createConstructionMenu(QWidget* parent, + const AString& descriptivePrefix, const AString& menuActionNamePrefix) { WuQMacroManager* macroManager = WuQMacroManager::instance(); @@ -877,7 +890,8 @@ OverlayViewController::createConstructionMenu(QWidget* parent, addAboveAction->setObjectName(menuActionNamePrefix + "AddOverlayAbove"); addAboveAction->setToolTip("Add an overlay above this overlay"); - macroManager->addMacroSupportToObject(addAboveAction); + macroManager->addMacroSupportToObject(addAboveAction, + ("Add overlay above " + descriptivePrefix)); QAction* addBelowAction = menu->addAction("Add Overlay Below", this, @@ -885,7 +899,8 @@ OverlayViewController::createConstructionMenu(QWidget* parent, addBelowAction->setObjectName(menuActionNamePrefix + "AddOverlayBelow"); addBelowAction->setToolTip("Add an overlay below this overlay"); - macroManager->addMacroSupportToObject(addBelowAction); + macroManager->addMacroSupportToObject(addBelowAction, + ("Add overlay below " + descriptivePrefix)); menu->addSeparator(); @@ -895,7 +910,8 @@ OverlayViewController::createConstructionMenu(QWidget* parent, moveUpAction->setObjectName(menuActionNamePrefix + "MoveOverlayUp"); moveUpAction->setToolTip("Move this overlay up"); - macroManager->addMacroSupportToObject(moveUpAction); + macroManager->addMacroSupportToObject(moveUpAction, + ("Move " + descriptivePrefix + " up")); QAction* moveDownAction = menu->addAction("Move Overlay Down", this, @@ -903,7 +919,8 @@ OverlayViewController::createConstructionMenu(QWidget* parent, moveDownAction->setObjectName(menuActionNamePrefix + "MoveOverlayDown"); moveDownAction->setToolTip("Move this overlay down"); - macroManager->addMacroSupportToObject(moveDownAction); + macroManager->addMacroSupportToObject(moveDownAction, + ("Move " + descriptivePrefix + " down")); menu->addSeparator(); @@ -913,7 +930,8 @@ OverlayViewController::createConstructionMenu(QWidget* parent, removeAction->setObjectName(menuActionNamePrefix + "RemoveOverlay"); removeAction->setToolTip("Remove this overlay"); - macroManager->addMacroSupportToObject(removeAction); + macroManager->addMacroSupportToObject(removeAction, + ("Remove " + descriptivePrefix)); menu->addSeparator(); @@ -923,7 +941,8 @@ OverlayViewController::createConstructionMenu(QWidget* parent, m_constructionReloadFileAction->setObjectName(menuActionNamePrefix + "ReloadSelectedFile"); m_constructionReloadFileAction->setToolTip("Reload file in this overlay"); - macroManager->addMacroSupportToObject(m_constructionReloadFileAction); + macroManager->addMacroSupportToObject(m_constructionReloadFileAction, + ("Reload file in " + descriptivePrefix)); menu->addSeparator(); @@ -933,7 +952,8 @@ OverlayViewController::createConstructionMenu(QWidget* parent, m_copyPathAndFileNameToClipboardAction->setObjectName(menuActionNamePrefix + "CopyPathAndFileNameToClipboard"); m_copyPathAndFileNameToClipboardAction->setToolTip("Copy path and file name of file in this overlay to clipboard"); - macroManager->addMacroSupportToObject(m_copyPathAndFileNameToClipboardAction); + macroManager->addMacroSupportToObject(m_copyPathAndFileNameToClipboardAction, + ("Copy path and filename from " + descriptivePrefix + " to clipboard")); QAction* copyMapNameAction = menu->addAction("Copy Map Name to Clipboard", this, @@ -941,7 +961,8 @@ OverlayViewController::createConstructionMenu(QWidget* parent, copyMapNameAction->setObjectName(menuActionNamePrefix + "CopyMapNameToClipboard"); copyMapNameAction->setToolTip("Copy name of selected map to the clipboard"); - macroManager->addMacroSupportToObject(copyMapNameAction); + macroManager->addMacroSupportToObject(copyMapNameAction, + ("Copy map namne in " + descriptivePrefix + " to clipboard")); return menu; diff --git a/src/GuiQt/OverlayViewController.h b/src/GuiQt/OverlayViewController.h index c0fb1d76a..cdfda6d27 100644 --- a/src/GuiQt/OverlayViewController.h +++ b/src/GuiQt/OverlayViewController.h @@ -117,6 +117,7 @@ namespace caret { void updateGraphicsWindow(); QMenu* createConstructionMenu(QWidget* parent, + const AString& descriptivePrefix, const AString& menuActionNamePrefix); void validateYokingSelection(); diff --git a/src/GuiQt/StructureSurfaceSelectionControl.cxx b/src/GuiQt/StructureSurfaceSelectionControl.cxx index 76f338e45..2c8f2700d 100644 --- a/src/GuiQt/StructureSurfaceSelectionControl.cxx +++ b/src/GuiQt/StructureSurfaceSelectionControl.cxx @@ -56,9 +56,14 @@ using namespace caret; * Show labels on controls * @param objectNamePrefix * Object name prefix used for macros + * @param descriptivePrefix + * Descriptive prefix for macros + * @param parent + * Parent widget for controls */ StructureSurfaceSelectionControl::StructureSurfaceSelectionControl(const bool showLabels, const QString& objectNamePrefix, + const QString& descriptivePrefix, QWidget* parent) : QWidget(parent) { @@ -71,7 +76,8 @@ StructureSurfaceSelectionControl::StructureSurfaceSelectionControl(const bool sh this->structureSelectionComboBox->setToolTip("Selects Structure of Surface"); this->structureSelectionComboBox->setObjectName(objectNamePrefix + ":StructureSelection"); - WuQMacroManager::instance()->addMacroSupportToObject(this->structureSelectionComboBox); + WuQMacroManager::instance()->addMacroSupportToObject(this->structureSelectionComboBox, + "Select structure for " + descriptivePrefix); this->surfaceControllerSelectionComboBox = WuQFactory::newComboBox(); this->surfaceControllerSelectionComboBox->setSizeAdjustPolicy(QComboBox::AdjustToContents); @@ -80,7 +86,8 @@ StructureSurfaceSelectionControl::StructureSurfaceSelectionControl(const bool sh this->surfaceControllerSelectionComboBox->setToolTip("Selects Surface"); this->surfaceControllerSelectionComboBox->setObjectName(objectNamePrefix + ":SurfaceSelection"); - WuQMacroManager::instance()->addMacroSupportToObject(this->surfaceControllerSelectionComboBox); + WuQMacroManager::instance()->addMacroSupportToObject(this->surfaceControllerSelectionComboBox, + "Select surface for " + descriptivePrefix); QGridLayout* layout = new QGridLayout(this); layout->setColumnStretch(0, 0); diff --git a/src/GuiQt/StructureSurfaceSelectionControl.h b/src/GuiQt/StructureSurfaceSelectionControl.h index a98123f57..9cb75fd91 100644 --- a/src/GuiQt/StructureSurfaceSelectionControl.h +++ b/src/GuiQt/StructureSurfaceSelectionControl.h @@ -41,6 +41,7 @@ namespace caret { public: StructureSurfaceSelectionControl(const bool showLabels, const QString& objectNamePrefix, + const QString& descriptivePrefix, QWidget* parent); virtual ~StructureSurfaceSelectionControl(); diff --git a/src/GuiQt/SurfaceSelectionViewController.cxx b/src/GuiQt/SurfaceSelectionViewController.cxx index 61e98e3c3..e80935c33 100644 --- a/src/GuiQt/SurfaceSelectionViewController.cxx +++ b/src/GuiQt/SurfaceSelectionViewController.cxx @@ -53,15 +53,19 @@ using namespace caret; * Surface selection that is controlled through this control. * @param objectName * Name for combo box + * @param descriptiveName + * Descriptive name for macros */ SurfaceSelectionViewController::SurfaceSelectionViewController(QObject* parent, SurfaceSelectionModel* surfaceSelectionModel, - const QString& objectName) + const QString& objectName, + const QString& descriptiveName) : WuQWidget(parent) { this->initializeControl(MODE_SELECTION_MODEL_STATIC, surfaceSelectionModel, - objectName); + objectName, + descriptiveName); } /** @@ -72,10 +76,13 @@ SurfaceSelectionViewController::SurfaceSelectionViewController(QObject* parent, * Allows selection of any surface with the specified brain structure. * @param objectName * Name for combo box + * @param descriptiveName + * Descriptive name for macros */ SurfaceSelectionViewController::SurfaceSelectionViewController(QObject* parent, BrainStructure* brainStructure, - const QString& objectName) + const QString& objectName, + const QString& descriptiveName) : WuQWidget(parent) { std::vector allSurfaceTypes; @@ -85,7 +92,8 @@ SurfaceSelectionViewController::SurfaceSelectionViewController(QObject* parent, allSurfaceTypes); this->initializeControl(MODE_BRAIN_STRUCTURE, ss, - objectName); + objectName, + descriptiveName); } /** @@ -121,12 +129,14 @@ SurfaceSelectionViewController::~SurfaceSelectionViewController() * Name for combo box */ SurfaceSelectionViewController::SurfaceSelectionViewController(QObject* parent, - const QString& objectName) + const QString& objectName, + const QString& descriptiveName) : WuQWidget(parent) { this->initializeControl(MODE_SELECTION_MODEL_DYNAMIC, NULL, - objectName); + objectName, + descriptiveName); } /** @@ -136,12 +146,14 @@ SurfaceSelectionViewController::SurfaceSelectionViewController(QObject* parent, * Model for surface selection * @param objectName * Name for combo box - + * @param descriptiveName + * Descriptive name for macros */ void SurfaceSelectionViewController::initializeControl(const Mode mode, SurfaceSelectionModel* surfaceSelectionModel, - const QString& objectName) + const QString& objectName, + const QString& descriptiveName) { this->mode = mode; this->surfaceComboBox = WuQFactory::newComboBox(); @@ -151,7 +163,8 @@ SurfaceSelectionViewController::initializeControl(const Mode mode, this->surfaceComboBox->setToolTip("Selects a surface in a combo box"); this->surfaceComboBox->setObjectName(objectName); - WuQMacroManager::instance()->addMacroSupportToObject(this->surfaceComboBox); + WuQMacroManager::instance()->addMacroSupportToObject(this->surfaceComboBox, + "Select surface for " + descriptiveName); //#ifdef CARET_OS_MACOSX diff --git a/src/GuiQt/SurfaceSelectionViewController.h b/src/GuiQt/SurfaceSelectionViewController.h index 6802692fb..01b526812 100644 --- a/src/GuiQt/SurfaceSelectionViewController.h +++ b/src/GuiQt/SurfaceSelectionViewController.h @@ -36,15 +36,18 @@ namespace caret { public: SurfaceSelectionViewController(QObject* parent, - SurfaceSelectionModel* surfaceSelectionModel, - const QString& objectName); + SurfaceSelectionModel* surfaceSelectionModel, + const QString& objectName, + const QString& descriptiveName); SurfaceSelectionViewController(QObject* parent, BrainStructure* brainStructure, - const QString& objectName); + const QString& objectName, + const QString& descriptiveName); SurfaceSelectionViewController(QObject* parent, - const QString& objectName); + const QString& objectName, + const QString& descriptiveName); virtual ~SurfaceSelectionViewController(); @@ -82,7 +85,8 @@ namespace caret { void initializeControl(const Mode mode, SurfaceSelectionModel* surfaceSelectionModel, - const QString& objectName); + const QString& objectName, + const QString& descriptiveName); Mode mode; diff --git a/src/GuiQt/VolumeSurfaceOutlineSetViewController.cxx b/src/GuiQt/VolumeSurfaceOutlineSetViewController.cxx index c783b2e13..0b6a1c46d 100644 --- a/src/GuiQt/VolumeSurfaceOutlineSetViewController.cxx +++ b/src/GuiQt/VolumeSurfaceOutlineSetViewController.cxx @@ -36,6 +36,7 @@ #include "VolumeSurfaceOutlineSetModel.h" #include "VolumeSurfaceOutlineViewController.h" #include "WuQFactory.h" +#include "WuQMacroManager.h" #include "WuQtUtilities.h" using namespace caret; @@ -55,12 +56,15 @@ using namespace caret; * Index of browser window that contains this view controller. * @param parentObjectNamePrefix * Name of parent object for macros + * @param descriptivePrefix + * Descriptive prefix for macros * @param parent * Parent widget. */ VolumeSurfaceOutlineSetViewController::VolumeSurfaceOutlineSetViewController(const Qt::Orientation orientation, const int32_t browserWindowIndex, const QString& parentObjectNamePrefix, + const QString& descriptivePrefix, QWidget* parent) : QWidget(parent) { @@ -98,7 +102,8 @@ VolumeSurfaceOutlineSetViewController::VolumeSurfaceOutlineSetViewController(con + ":VolumeSurfaceOutline%1").arg((int)(i + 1), 2, 10, QLatin1Char('0')); VolumeSurfaceOutlineViewController* ovc = new VolumeSurfaceOutlineViewController(orientation, gridLayout, - name); + name, + descriptivePrefix + " " + QString::number(i + 1)); this->outlineViewControllers.push_back(ovc); } @@ -109,6 +114,11 @@ VolumeSurfaceOutlineSetViewController::VolumeSurfaceOutlineSetViewController(con this->outlineCountSpinBox->setSingleStep(1); QObject::connect(this->outlineCountSpinBox, SIGNAL(valueChanged(int)), this, SLOT(outlineCountSpinBoxValueChanged(int))); + this->outlineCountSpinBox->setObjectName(parentObjectNamePrefix + + ":VolumeSurfaceOutlineNumberOfOutlines"); + this->outlineCountSpinBox->setToolTip("Number of volume surface outlines"); + WuQMacroManager::instance()->addMacroSupportToObject(this->outlineCountSpinBox, + "Set number of displayed volume/surface outlines for " + descriptivePrefix); QHBoxLayout* overlayCountLayout = new QHBoxLayout(); overlayCountLayout->addWidget(outlineCountLabel); diff --git a/src/GuiQt/VolumeSurfaceOutlineSetViewController.h b/src/GuiQt/VolumeSurfaceOutlineSetViewController.h index 56d642734..d1ea1fa9e 100644 --- a/src/GuiQt/VolumeSurfaceOutlineSetViewController.h +++ b/src/GuiQt/VolumeSurfaceOutlineSetViewController.h @@ -43,6 +43,7 @@ namespace caret { VolumeSurfaceOutlineSetViewController(const Qt::Orientation orientation, const int32_t browserWindowIndex, const QString& parentObjectNamePrefix, + const QString& descriptivePrefix, QWidget* parent = 0); virtual ~VolumeSurfaceOutlineSetViewController(); diff --git a/src/GuiQt/VolumeSurfaceOutlineViewController.cxx b/src/GuiQt/VolumeSurfaceOutlineViewController.cxx index 67f31c8ab..dc84196e4 100644 --- a/src/GuiQt/VolumeSurfaceOutlineViewController.cxx +++ b/src/GuiQt/VolumeSurfaceOutlineViewController.cxx @@ -54,10 +54,20 @@ using namespace caret; */ /** * Constructor. + * + * @param orientation + * Orientation for controller + * @param gridLayout + * Layout for widgets + * @param objectNamePrefix + * Object name prefix for macros + * @param descriptivePrefix + * Descriptive name prefix for macros */ VolumeSurfaceOutlineViewController::VolumeSurfaceOutlineViewController(const Qt::Orientation orientation, QGridLayout* gridLayout, const QString& objectNamePrefix, + const QString& descriptivePrefix, QObject* parent) : QObject(parent) { @@ -71,11 +81,13 @@ VolumeSurfaceOutlineViewController::VolumeSurfaceOutlineViewController(const Qt: this->enabledCheckBox->setToolTip("Enables display of this volume surface outline"); this->enabledCheckBox->setObjectName(objectNamePrefix + ":Enable"); - macroManager->addMacroSupportToObject(this->enabledCheckBox); + macroManager->addMacroSupportToObject(this->enabledCheckBox, + "Enable volume surface outline for " + descriptivePrefix); this->surfaceSelectionViewController = new SurfaceSelectionViewController(this, (objectNamePrefix - + ":Surface")); + + ":Surface"), + "Select volume surface outline surface for " + descriptivePrefix); QObject::connect(this->surfaceSelectionViewController, SIGNAL(surfaceSelected(Surface*)), this, SLOT(surfaceSelected(Surface*))); this->surfaceSelectionViewController->getWidget()->setToolTip("Select surface drawn as outline over volume slices"); @@ -88,7 +100,8 @@ VolumeSurfaceOutlineViewController::VolumeSurfaceOutlineViewController(const Qt: "in the selected tab is used.\n"); this->colorOrTabSelectionControl->getWidget()->setObjectName(objectNamePrefix + ":ColorSource"); - macroManager->addMacroSupportToObject(this->colorOrTabSelectionControl->getWidget()); + macroManager->addMacroSupportToObject(this->colorOrTabSelectionControl->getWidget(), + "Set surface outline color for " + descriptivePrefix); this->thicknessSpinBox = new WuQDoubleSpinBox(this); this->thicknessSpinBox->setRange(0.0, 100.0); @@ -99,7 +112,8 @@ VolumeSurfaceOutlineViewController::VolumeSurfaceOutlineViewController(const Qt: this->thicknessSpinBox->getWidget()->setToolTip("Thickness of surface outline as percentage of viewport height"); this->thicknessSpinBox->getWidget()->setObjectName(objectNamePrefix + ":Thickness"); - macroManager->addMacroSupportToObject(this->thicknessSpinBox->getWidget()); + macroManager->addMacroSupportToObject(this->thicknessSpinBox->getWidget(), + "Set thickness for volume surface outline for " + descriptivePrefix); if (orientation == Qt::Horizontal) { diff --git a/src/GuiQt/VolumeSurfaceOutlineViewController.h b/src/GuiQt/VolumeSurfaceOutlineViewController.h index 675e0c3dd..2bd18af03 100644 --- a/src/GuiQt/VolumeSurfaceOutlineViewController.h +++ b/src/GuiQt/VolumeSurfaceOutlineViewController.h @@ -44,6 +44,7 @@ namespace caret { VolumeSurfaceOutlineViewController(const Qt::Orientation orientation, QGridLayout* gridLayout, const QString& objectNamePrefix, + const QString& descriptivePrefix, QObject* parent = 0); virtual ~VolumeSurfaceOutlineViewController(); diff --git a/src/GuiQt/WuQDataEntryDialog.cxx b/src/GuiQt/WuQDataEntryDialog.cxx index b3c993588..714fd7e16 100644 --- a/src/GuiQt/WuQDataEntryDialog.cxx +++ b/src/GuiQt/WuQDataEntryDialog.cxx @@ -588,7 +588,8 @@ WuQDataEntryDialog::addSurfaceSelectionViewController(const QString& labelText, SurfaceSelectionViewController* surfaceSelectionViewController = new SurfaceSelectionViewController(this, brainStructure, - "DataEntryDialogSurfaceComboBox"); + "DataEntryDialogSurfaceComboBox", + "Data Entry"); this->addWidget(labelText, surfaceSelectionViewController->getWidget()); diff --git a/src/GuiQt/WuQMacroManager.cxx b/src/GuiQt/WuQMacroManager.cxx index e467c97dc..fee6d28f6 100644 --- a/src/GuiQt/WuQMacroManager.cxx +++ b/src/GuiQt/WuQMacroManager.cxx @@ -180,6 +180,8 @@ WuQMacroManager::isModeRecording() const * * @param object * Object that is monitored. + * @param descriptiveName + * Descriptive name for user * @param toolTipTextOverride * Override of object's tooltip. This is primarily used when * an object of a particular class does not support a tooltip @@ -188,7 +190,8 @@ WuQMacroManager::isModeRecording() const */ bool WuQMacroManager::addMacroSupportToObjectWithToolTip(QObject* object, - const QString& toolTipOverride) + const QString& descriptiveName, + const QString& toolTipOverride) { CaretAssert(object); @@ -198,6 +201,12 @@ WuQMacroManager::addMacroSupportToObjectWithToolTip(QObject* object, + SystemUtilities::getBackTrace()); return false; } + if (descriptiveName.isEmpty()) { + CaretLogSevere("Descriptive name is empty for " + + name + + "\n" + + SystemUtilities::getBackTrace()); + } auto existingWatcher = m_signalWatchers.find(name); if (existingWatcher != m_signalWatchers.end()) { @@ -212,6 +221,7 @@ WuQMacroManager::addMacroSupportToObjectWithToolTip(QObject* object, AString errorMessage; WuQMacroSignalWatcher* widgetWatcher = WuQMacroSignalWatcher::newInstance(this, object, + descriptiveName, toolTipOverride, errorMessage); if (widgetWatcher != NULL) { @@ -226,6 +236,25 @@ WuQMacroManager::addMacroSupportToObjectWithToolTip(QObject* object, } } +///** +// * Add macro support to the given object. When recording, +// * The object's 'value changed' signal will be monitored so +// * that the new value can be part of a macro command. +// * +// * @param object +// * Object that is monitored. +// * @param toolTipTextOverride +// * Override of object's tooltip. This is primarily used when +// * an object of a particular class does not support a tooltip +// * such as a QButtonGroup +// */ +//bool +//WuQMacroManager::addMacroSupportToObject(QObject* object) +//{ +// return addMacroSupportToObject(object, +// "IN WORK"); +//} + /** * Add macro support to the given object. When recording, * The object's 'value changed' signal will be monitored so @@ -233,13 +262,12 @@ WuQMacroManager::addMacroSupportToObjectWithToolTip(QObject* object, * * @param object * Object that is monitored. - * @param toolTipTextOverride - * Override of object's tooltip. This is primarily used when - * an object of a particular class does not support a tooltip - * such as a QButtonGroup + * @param descriptiveName + * Descriptive name for user */ bool -WuQMacroManager::addMacroSupportToObject(QObject* object) +WuQMacroManager::addMacroSupportToObject(QObject* object, + const QString& descriptiveName) { CaretAssert(object); @@ -254,6 +282,7 @@ WuQMacroManager::addMacroSupportToObject(QObject* object) } const bool resultFlag = addMacroSupportToObjectWithToolTip(object, + descriptiveName, toolTipText); if (resultFlag) { @@ -299,6 +328,8 @@ WuQMacroManager::addMacroCommandToRecording(WuQMacroCommand* macroCommand) * * @param widget * Widget where mouse event occurred + * @param descriptiveName + * Descriptive name for user * @param me * The Qt Mouse Event * @return @@ -306,6 +337,7 @@ WuQMacroManager::addMacroCommandToRecording(WuQMacroCommand* macroCommand) */ bool WuQMacroManager::addMouseEventToRecording(QWidget* widget, + const QString& descriptiveName, const QMouseEvent* me) { CaretAssert(widget); @@ -318,6 +350,12 @@ WuQMacroManager::addMouseEventToRecording(QWidget* widget, + SystemUtilities::getBackTrace()); return false; } + if (descriptiveName.isEmpty()) { + CaretLogSevere("Descriptive name is empty for " + + name + + "\n" + + SystemUtilities::getBackTrace()); + } CaretAssert(m_macroBeingRecorded); bool validMouseEventFlag(true); @@ -359,7 +397,8 @@ WuQMacroManager::addMouseEventToRecording(QWidget* widget, me->localPos().y()); m_macroBeingRecorded->appendMacroCommand(new WuQMacroCommand(name, - mouseInfo)); + descriptiveName, + mouseInfo)); return true; } diff --git a/src/GuiQt/WuQMacroManager.h b/src/GuiQt/WuQMacroManager.h index fd8d76777..07fba10b0 100644 --- a/src/GuiQt/WuQMacroManager.h +++ b/src/GuiQt/WuQMacroManager.h @@ -61,14 +61,19 @@ namespace caret { QString getName() const; - bool addMacroSupportToObject(QObject* object); +// bool addMacroSupportToObject(QObject* object); +// + bool addMacroSupportToObject(QObject* object, + const QString& descriptiveName); bool addMacroSupportToObjectWithToolTip(QObject* object, + const QString& descriptiveName, const QString& toolTipOverride); bool addMacroCommandToRecording(WuQMacroCommand* macroCommand); bool addMouseEventToRecording(QWidget* widget, + const QString& descriptiveName, const QMouseEvent* me); WuQMacroModeEnum::Enum getMode() const; diff --git a/src/GuiQt/WuQMacroSignalWatcher.cxx b/src/GuiQt/WuQMacroSignalWatcher.cxx index 9fac6ab5e..71493569c 100644 --- a/src/GuiQt/WuQMacroSignalWatcher.cxx +++ b/src/GuiQt/WuQMacroSignalWatcher.cxx @@ -62,6 +62,8 @@ using namespace caret; * Object that is watched for a "value changed" signal * @param objectType * The type of the object. + * @param descriptiveName + * Descriptive name shown to user in macro command * @param toolTipTextOverride * Used to override tool tip or for when object does not * support a tool tip. @@ -69,11 +71,13 @@ using namespace caret; WuQMacroSignalWatcher::WuQMacroSignalWatcher(WuQMacroManager* parentMacroManager, QObject* object, const WuQMacroClassTypeEnum::Enum objectType, + const QString& descriptiveName, const QString& toolTipTextOverride) : QObject(), m_parentMacroManager(parentMacroManager), m_object(object), m_objectType(objectType), +m_descriptiveName(descriptiveName), m_objectName(object->objectName()) { CaretAssert(m_parentMacroManager); @@ -286,6 +290,8 @@ WuQMacroSignalWatcher::objectNameWasChanged(const QString& name) * Parent macro manager. * @param object * Object that will have a widget signal watcher. + * @param descriptiveName + * Descriptive name shown to user in macro command * @param toolTipTextOverride * Used to override tool tip or for when object does not * support a tool tip. @@ -297,6 +303,7 @@ WuQMacroSignalWatcher::objectNameWasChanged(const QString& name) WuQMacroSignalWatcher* WuQMacroSignalWatcher::newInstance(WuQMacroManager* parentMacroManager, QObject* object, + const QString& descriptiveName, const QString& toolTipTextOverride, QString& errorMessageOut) { @@ -321,6 +328,7 @@ WuQMacroSignalWatcher::newInstance(WuQMacroManager* parentMacroManager, WuQMacroSignalWatcher* ww = new WuQMacroSignalWatcher(parentMacroManager, object, objectType, + descriptiveName, toolTipTextOverride); return ww; } @@ -350,6 +358,7 @@ WuQMacroSignalWatcher::createAndSendMacroCommand(const QVariant& value, if (m_parentMacroManager->isModeRecording()) { WuQMacroCommand* mc = new WuQMacroCommand(m_objectType, m_objectName, + m_descriptiveName, m_toolTipText, value, valueTwo); diff --git a/src/GuiQt/WuQMacroSignalWatcher.h b/src/GuiQt/WuQMacroSignalWatcher.h index bf41f9598..30479b1b4 100644 --- a/src/GuiQt/WuQMacroSignalWatcher.h +++ b/src/GuiQt/WuQMacroSignalWatcher.h @@ -44,6 +44,7 @@ namespace caret { public: static WuQMacroSignalWatcher* newInstance(WuQMacroManager* parentMacroManager, QObject* object, + const QString& descriptiveName, const QString& toolTipTextOverride, QString& errorMessageOut); @@ -100,6 +101,7 @@ namespace caret { WuQMacroSignalWatcher(WuQMacroManager* parentMacroManager, QObject* object, const WuQMacroClassTypeEnum::Enum objectType, + const QString& descriptiveName, const QString& toolTipTextOverride); WuQMacroManager* m_parentMacroManager; @@ -111,6 +113,8 @@ namespace caret { const WuQMacroClassTypeEnum::Enum m_objectType; + const QString m_descriptiveName; + const QString m_objectName; QString m_toolTipText; -- GitLab From 0b155c959fa6b7fb51c70db8d08934ba52888778 Mon Sep 17 00:00:00 2001 From: John Harwell Date: Fri, 11 Jan 2019 16:44:15 -0600 Subject: [PATCH 168/427] WB-834 Macros: Implemented deleting and moving of macros and macro commands. --- src/Common/WuQMacro.cxx | 111 +++++++ src/Common/WuQMacro.h | 13 + src/Common/WuQMacroGroup.cxx | 104 ++++++- src/Common/WuQMacroGroup.h | 9 + src/GuiQt/WuQMacroDialog.cxx | 556 ++++++++++++++++++++++++++++------- src/GuiQt/WuQMacroDialog.h | 67 +++-- 6 files changed, 726 insertions(+), 134 deletions(-) diff --git a/src/Common/WuQMacro.cxx b/src/Common/WuQMacro.cxx index acfeb0dbf..3832b97f8 100644 --- a/src/Common/WuQMacro.cxx +++ b/src/Common/WuQMacro.cxx @@ -190,6 +190,27 @@ WuQMacro::appendMacroCommand(WuQMacroCommand* macroCommand) setModified(); } +/** + * Insert the given macro command at the given index + * + * @param index + * Index of where to insert the macro command + * @param macroCommand + * Macro command to insert. + */ +void +WuQMacro::insertMacroCommandAtIndex(const int32_t index, + WuQMacroCommand* macroCommand) +{ + CaretAssert((index >= 0) + && (index <= getNumberOfMacroCommands())); + CaretAssert(macroCommand); + + insertRow(index, + macroCommand); + setModified(); +} + /** * @return The unique identifier */ @@ -333,6 +354,96 @@ WuQMacro::getMacroCommandAtIndex(const int32_t index) return command; } +/** + * @return + * Index of the given macro command + * @param macroCommand + * The macro command + */ +int32_t +WuQMacro::getIndexOfMacroCommand(const WuQMacroCommand* macroCommand) const +{ + CaretAssert(macroCommand); + + const int32_t num = getNumberOfMacroCommands(); + for (int32_t i = 0; i < num; i++) { + if (getMacroCommandAtIndex(i) == macroCommand) { + return i; + break; + } + } + return -1; +} + +/** + * Delete macro command at the given index + * + * @param index + * Index of macro command + */ +void +WuQMacro::deleteMacroCommandAtIndex(const int32_t index) +{ + CaretAssert((index >= 0) + && (index < getNumberOfMacroCommands())); + removeRow(index); + setModified(); +} + +/** + * Delete the given macro command + + * @param macroCommand + */ +void +WuQMacro::deleteMacroCommand(WuQMacroCommand* macroCommand) +{ + const int32_t index = getIndexOfMacroCommand(macroCommand); + if (index >= 0) { + deleteMacroCommandAtIndex(index); + } +} + +/** + * Move the given macro command down one position + * + * @param macroCommand + */ +void +WuQMacro::moveMacroCommandDown(WuQMacroCommand* macroCommand) +{ + const int32_t index = getIndexOfMacroCommand(macroCommand); + if ((index >= 0) + && (index < getNumberOfMacroCommands() - 1)) { + /* Note that take child removes item but does not remove row */ + QStandardItem* item = takeChild(index); + removeRow(index); + CaretAssert(item); + insertMacroCommandAtIndex(index + 1, + dynamic_cast(item)); + } +} + +/** + * Move the given macro command up one position + * + * @param macroCommand + */ +void +WuQMacro::moveMacroCommandUp(WuQMacroCommand* macroCommand) +{ + const int32_t index = getIndexOfMacroCommand(macroCommand); + if ((index > 0) + && (index < getNumberOfMacroCommands())) { + /* Note that take child removes item but does not remove row */ + QStandardItem* item = takeChild(index); + removeRow(index); + CaretAssert(item); + insertMacroCommandAtIndex(index - 1, + dynamic_cast(item)); + } +} + /** * @return True if this instance is modified */ diff --git a/src/Common/WuQMacro.h b/src/Common/WuQMacro.h index 5ecd7eb2e..75dc08e1f 100644 --- a/src/Common/WuQMacro.h +++ b/src/Common/WuQMacro.h @@ -46,12 +46,25 @@ namespace caret { void appendMacroCommand(WuQMacroCommand* macroCommand); + void insertMacroCommandAtIndex(const int32_t index, + WuQMacroCommand* macroCommand); + int32_t getNumberOfMacroCommands() const; const WuQMacroCommand* getMacroCommandAtIndex(const int32_t index) const; WuQMacroCommand* getMacroCommandAtIndex(const int32_t index); + int32_t getIndexOfMacroCommand(const WuQMacroCommand* macroCommand) const; + + void deleteMacroCommandAtIndex(const int32_t index); + + void deleteMacroCommand(WuQMacroCommand* macroCommand); + + void moveMacroCommandDown(WuQMacroCommand* macroCommand); + + void moveMacroCommandUp(WuQMacroCommand* macroCommand); + QString getUniqueIdentifier() const; void setUniqueIdentifier(const QString& uniqueIdentifier); diff --git a/src/Common/WuQMacroGroup.cxx b/src/Common/WuQMacroGroup.cxx index bf93a2930..d6fd2f752 100644 --- a/src/Common/WuQMacroGroup.cxx +++ b/src/Common/WuQMacroGroup.cxx @@ -133,6 +133,27 @@ WuQMacroGroup::addMacro(WuQMacro* macro) setModified(); } +/** + * Insert the given macro at the given index + * + * @param index + * Index of where to insert the macro + * @param macro + * Macro to insert. + */ +void +WuQMacroGroup::insertMacroAtIndex(const int32_t index, + WuQMacro* macro) +{ + CaretAssert((index >= 0) + && (index <= getNumberOfMacros())); + CaretAssert(macro); + + insertRow(index, + macro); + setModified(); +} + /** * Append macros in the given group to this group * @@ -330,22 +351,42 @@ WuQMacroGroup::getMacroAtIndex(const int32_t index) const } /** - * @return True if this macro group contains the given macro + * Get the index of the given macro * * @param macro - * Macro for inclusion testing + * The macro for index + * @return + * Index of macro or -1 if not found */ -bool -WuQMacroGroup::containsMacro(const WuQMacro* macro) +int32_t +WuQMacroGroup::getIndexOfMacro(const WuQMacro* macro) { const int32_t count = getNumberOfMacros(); for (int32_t i = 0; i < count; i++) { if (getMacroAtIndex(i) == macro) { - return true; + return i; } } - - return false; + + return -1; +} + +/** + * @return True if this macro group contains the given macro + * + * @param macro + * Macro for inclusion testing + */ +bool +WuQMacroGroup::containsMacro(const WuQMacro* macro) +{ + const int32_t index = getIndexOfMacro(macro); + if (index >= 0) { + return true; + } + else { + return false; + } } /** @@ -357,12 +398,9 @@ WuQMacroGroup::containsMacro(const WuQMacro* macro) void WuQMacroGroup::deleteMacro(const WuQMacro* macro) { - const int32_t count = getNumberOfMacros(); - for (int32_t i = 0; i < count; i++) { - if (getMacroAtIndex(i) == macro) { - deleteMacroAtIndex(i); - break; - } + const int32_t index = getIndexOfMacro(macro); + if (index >= 0) { + deleteMacroAtIndex(index); } } @@ -389,6 +427,46 @@ WuQMacroGroup::deleteMacroAtIndex(const int32_t index) setModified(); } +/** + * Move the given macro down one position + * + * @param macro + */ +void +WuQMacroGroup::moveMacroDown(WuQMacro* macro) +{ + const int32_t index = getIndexOfMacro(macro); + if ((index >= 0) + && (index < getNumberOfMacros() - 1)) { + /* Note that take item removes item but does not remove row */ + QStandardItem* item = takeItem(index); + removeRow(index); + CaretAssert(item); + insertMacroAtIndex(index + 1, + dynamic_cast(item)); + } +} + +/** + * Move the given macro up one position + * + * @param macro + */ +void +WuQMacroGroup::moveMacroUp(WuQMacro* macro) +{ + const int32_t index = getIndexOfMacro(macro); + if ((index > 0) + && (index < getNumberOfMacros())) { + /* Note that take child removes item but does not remove row */ + QStandardItem* item = takeItem(index); + removeRow(index); + CaretAssert(item); + insertMacroAtIndex(index - 1, + dynamic_cast(item)); + } +} + /** * @return True if this instance is modified */ diff --git a/src/Common/WuQMacroGroup.h b/src/Common/WuQMacroGroup.h index 4951568bd..026028c03 100644 --- a/src/Common/WuQMacroGroup.h +++ b/src/Common/WuQMacroGroup.h @@ -51,6 +51,9 @@ namespace caret { void addMacro(WuQMacro* macro); + void insertMacroAtIndex(const int32_t index, + WuQMacro* macro); + void appendMacroGroup(const WuQMacroGroup* macroGroup); int32_t getNumberOfMacros() const; @@ -63,6 +66,8 @@ namespace caret { const WuQMacro* getMacroAtIndex(const int32_t index) const; + int32_t getIndexOfMacro(const WuQMacro* macro); + WuQMacro* getMacroWithUniqueIdentifier(const QString& uniqueIdentifier); const WuQMacro* getMacroWithUniqueIdentifier(const QString& uniqueIdentifier) const; @@ -77,6 +82,10 @@ namespace caret { void deleteMacroAtIndex(const int32_t index); + void moveMacroDown(WuQMacro* macro); + + void moveMacroUp(WuQMacro* macro); + virtual bool isModified() const override; virtual void clearModified() override; diff --git a/src/GuiQt/WuQMacroDialog.cxx b/src/GuiQt/WuQMacroDialog.cxx index c2aa80317..30f7c6ac1 100644 --- a/src/GuiQt/WuQMacroDialog.cxx +++ b/src/GuiQt/WuQMacroDialog.cxx @@ -23,6 +23,7 @@ #include "WuQMacroDialog.h" #undef __WU_Q_MACRO_DIALOG_DECLARE__ +#include #include #include #include @@ -31,11 +32,14 @@ #include #include #include +#include #include +#include #include #include #include #include +#include #include #include "CaretAssert.h" @@ -47,6 +51,7 @@ #include "WuQMacroManager.h" #include "WuQMacroShortCutKeyComboBox.h" #include "WuQMacroStandardItemTypeEnum.h" +#include "WuQtUtilities.h" using namespace caret; @@ -72,43 +77,49 @@ WuQMacroDialog::WuQMacroDialog(QWidget* parent) m_macroGroups = WuQMacroManager::instance()->getMacroGroups(); - QLabel* macrosLabel = new QLabel("Macro:"); +// QLabel* macrosLabel = new QLabel("Macro:"); QLabel* macroGroupLabel = new QLabel("Macros in:"); m_macroGroupComboBox = new QComboBox(); QObject::connect(m_macroGroupComboBox, static_cast(&QComboBox::activated), this, &WuQMacroDialog::macroGroupComboBoxActivated); - m_treeView = new QTreeView(); - m_treeView->setHeaderHidden(true); - QObject::connect(m_treeView, &QTreeView::clicked, - this, &WuQMacroDialog::treeViewItemClicked); + m_macroGroupToolButton = new QToolButton(); + m_macroGroupToolButton->setText("..."); + m_macroGroupToolButton->setToolTip("Export and import macro groups"); + QObject::connect(m_macroGroupToolButton, &QToolButton::clicked, + this, &WuQMacroDialog::macroGroupToolButtonClicked); QGridLayout* gridLayout = new QGridLayout(); + gridLayout->setContentsMargins(0, 0, 0, 0); + gridLayout->setRowStretch(0, 0); + gridLayout->setRowStretch(1, 0); + gridLayout->setRowStretch(2, 100); int row = 0; gridLayout->addWidget(macroGroupLabel, row, 0); gridLayout->addWidget(m_macroGroupComboBox, row, 1); + gridLayout->addWidget(m_macroGroupToolButton, row, 2); row++; - gridLayout->addWidget(macrosLabel, row, 0, (Qt::AlignTop - | Qt::AlignLeft)); - gridLayout->addWidget(m_treeView, row, 1, 2, 1); + gridLayout->addWidget(createHorizontalLine(), row, 0, 1, 3); row++; - gridLayout->addWidget(createMacroButtonsWidget(), row, 0); + gridLayout->addWidget(createMacroRunAndEditingToolButtons(), row, 0, 1, 3); +// gridLayout->addWidget(macrosLabel, row, 0, (Qt::AlignTop| Qt::AlignLeft)); + row++; + gridLayout->addWidget(createMacroAndCommandSelectionWidget(), row, 0, 1, 3); row++; for (int32_t iRow = 0; iRow < gridLayout->rowCount(); iRow++) { gridLayout->setRowStretch(iRow, 0); } - m_macroWidget = createMacroWidget(); - m_commandWidget = createCommandWidget(); + m_macroWidget = createMacroDisplayWidget(); + m_commandWidget = createCommandDisplayWidget(); m_stackedWidget = new QStackedWidget(); m_stackedWidget->addWidget(m_macroWidget); m_stackedWidget->addWidget(m_commandWidget); m_stackedWidget->setCurrentWidget(m_macroWidget); m_dialogButtonBox = new QDialogButtonBox(QDialogButtonBox::Close); - m_runButton = m_dialogButtonBox->addButton("Run", QDialogButtonBox::ApplyRole); QObject::connect(m_dialogButtonBox, &QDialogButtonBox::rejected, this, &WuQMacroDialog::close); QObject::connect(m_dialogButtonBox, &QDialogButtonBox::clicked, @@ -133,66 +144,84 @@ WuQMacroDialog::~WuQMacroDialog() { } -/** - * @return New instance of widget containing macro buttons - */ QWidget* -WuQMacroDialog::createMacroButtonsWidget() +WuQMacroDialog::createMacroRunAndEditingToolButtons() { - QWidget* widget = new QWidget(); + m_runMacroToolButton = new QToolButton(); + m_runMacroToolButton->setToolButtonStyle(Qt::ToolButtonTextBesideIcon); + m_runMacroToolButton->setText("Run"); + QPixmap runPixmap = createEditingToolButtonPixmap(m_runMacroToolButton, + EditButton::RUN); + m_runMacroToolButton->setIcon(runPixmap); + m_runMacroToolButton->setToolTip("Run the selected macro"); + QObject::connect(m_runMacroToolButton, &QToolButton::clicked, + this, &WuQMacroDialog::runMacroToolButtonClicked); + + m_editingMoveUpToolButton = new QToolButton(); + QPixmap moveUpPixmap = createEditingToolButtonPixmap(m_editingMoveUpToolButton, + EditButton::MOVE_UP); + m_editingMoveUpToolButton->setIcon(moveUpPixmap); + m_editingMoveUpToolButton->setToolTip("Move selected macro/command up"); + QObject::connect(m_editingMoveUpToolButton, &QToolButton::clicked, + this, &WuQMacroDialog::editingMoveUpToolButtonClicked); + + m_editingMoveDownToolButton = new QToolButton(); + QPixmap moveDownPixmap = createEditingToolButtonPixmap(m_editingMoveDownToolButton, + EditButton::MOVE_DOWN); + m_editingMoveDownToolButton->setIcon(moveDownPixmap); + m_editingMoveDownToolButton->setToolTip("Move selected macro/command down"); + QObject::connect(m_editingMoveDownToolButton, &QToolButton::clicked, + this, &WuQMacroDialog::editingMoveDownToolButtonClicked); - m_deletePushButton = new QPushButton("Delete..."); - m_deletePushButton->setToolTip("Delete the selected macro"); - QObject::connect(m_deletePushButton, &QPushButton::clicked, - this, &WuQMacroDialog::deleteButtonClicked); - - m_importPushButton = new QPushButton("Import..."); - m_importPushButton->setToolTip("Import a macro"); - QObject::connect(m_importPushButton, &QPushButton::clicked, - this, &WuQMacroDialog::importButtonClicked); - - m_exportPushButton = new QPushButton("Export..."); - m_exportPushButton->setToolTip("Import a macro"); - QObject::connect(m_exportPushButton, &QPushButton::clicked, - this, &WuQMacroDialog::exportButtonClicked); - - QVBoxLayout* macroButtonsLayout = new QVBoxLayout(widget); - macroButtonsLayout->setSpacing(4); - macroButtonsLayout->addWidget(m_deletePushButton); - macroButtonsLayout->addSpacing(15); - macroButtonsLayout->addWidget(m_importPushButton); - macroButtonsLayout->addWidget(m_exportPushButton); - macroButtonsLayout->addStretch(); + m_editingDeleteToolButton = new QToolButton(); + QPixmap deletePixmap = createEditingToolButtonPixmap(m_editingDeleteToolButton, + EditButton::DELETER); + m_editingDeleteToolButton->setIcon(deletePixmap); + m_editingDeleteToolButton->setToolTip("Delete the selected macro/command"); + QObject::connect(m_editingDeleteToolButton, &QToolButton::clicked, + this, &WuQMacroDialog::editingDeleteToolButtonClicked); + + m_editingInsertToolButton = new QToolButton(); + QPixmap insertPixmap = createEditingToolButtonPixmap(m_editingInsertToolButton, + EditButton::INSERTER); + m_editingInsertToolButton->setIcon(insertPixmap); + m_editingInsertToolButton->setToolTip("Insert a macro command"); + QObject::connect(m_editingInsertToolButton, &QToolButton::clicked, + this, &WuQMacroDialog::editingInsertToolButtonClicked); + + QWidget* widget = new QWidget(); + QHBoxLayout* toolButtonLayout = new QHBoxLayout(widget); + toolButtonLayout->setContentsMargins(0, 0, 0, 0); + toolButtonLayout->addWidget(m_runMacroToolButton); + toolButtonLayout->addStretch(); + toolButtonLayout->addWidget(m_editingMoveUpToolButton); + toolButtonLayout->addWidget(m_editingMoveDownToolButton); + toolButtonLayout->addWidget(m_editingInsertToolButton); + toolButtonLayout->addSpacing(15); + toolButtonLayout->addWidget(m_editingDeleteToolButton); return widget; } /** - * Update buttons based upon selected item(s) + * @return the macro and command selection widget */ -void -WuQMacroDialog::updateButtons() +QWidget* +WuQMacroDialog::createMacroAndCommandSelectionWidget() { - bool macroSelected(false); - WuQMacroStandardItemTypeEnum::Enum itemType = getSelectedItemType(); - switch (itemType) { - case WuQMacroStandardItemTypeEnum::INVALID: - break; - case WuQMacroStandardItemTypeEnum::MACRO: - macroSelected = true; - break; - case WuQMacroStandardItemTypeEnum::MACRO_COMMAND: - break; - } - - m_deletePushButton->setEnabled(macroSelected); + m_treeView = new QTreeView(); + m_treeView->setHeaderHidden(true); + QObject::connect(m_treeView, &QTreeView::clicked, + this, &WuQMacroDialog::treeViewItemClicked); + + return m_treeView; } /** * @return The widget displayed when a macro is selected */ QWidget* -WuQMacroDialog::createMacroWidget() +WuQMacroDialog::createMacroDisplayWidget() { QLabel* nameLabel = new QLabel("Name:"); m_macroNameLabel = new QLabel(); @@ -221,12 +250,8 @@ WuQMacroDialog::createMacroWidget() QObject::connect(descriptionEditPushButton, &QPushButton::clicked, this, &WuQMacroDialog::macroDescriptionEditButtonClicked); - QFrame* horizontalLine = new QFrame(); - horizontalLine->setMidLineWidth(1); - horizontalLine->setLineWidth(1); - horizontalLine->setFrameStyle(QFrame::HLine | QFrame::Sunken); - QGridLayout* gridLayout = new QGridLayout(); + gridLayout->setContentsMargins(0, 0, 0, 0); gridLayout->setColumnStretch(0, 0); gridLayout->setColumnStretch(1, 0); gridLayout->setColumnStretch(2, 0); @@ -246,10 +271,22 @@ WuQMacroDialog::createMacroWidget() gridLayout->addWidget(descriptionEditPushButton, row, 0, 1, 2); row++; + QHBoxLayout* titleLayout = new QHBoxLayout(); + titleLayout->setContentsMargins(0, 0, 0, 0); + titleLayout->addWidget(new QLabel("Macro ")); + titleLayout->addWidget(createHorizontalLine(), 100); + + QHBoxLayout* runOptionsTitleLayout = new QHBoxLayout(); + runOptionsTitleLayout->setContentsMargins(0, 0, 0, 0); + runOptionsTitleLayout->setContentsMargins(0, 0, 0, 0); + runOptionsTitleLayout->addWidget(new QLabel("Run Macro Options ")); + runOptionsTitleLayout->addWidget(createHorizontalLine(), 100); + QWidget* widget = new QWidget(); QVBoxLayout* layout = new QVBoxLayout(widget); + layout->addLayout(titleLayout); layout->addLayout(gridLayout); - layout->addWidget(horizontalLine); + layout->addLayout(runOptionsTitleLayout); layout->addWidget(createRunOptionsWidget()); layout->addStretch(); @@ -330,7 +367,6 @@ WuQMacroDialog::createRunOptionsWidget() { QWidget* widget = new QWidget(); - QLabel* runOptionsLabel = new QLabel("Run Macro Options: "); m_runOptionLoopCheckBox = new QCheckBox("Loop"); m_runOptionLoopCheckBox->setChecked(false); m_runOptionLoopCheckBox->setEnabled(false); @@ -358,7 +394,6 @@ WuQMacroDialog::createRunOptionsWidget() QGridLayout* runOptionsLayout = new QGridLayout(widget); runOptionsLayout->setColumnMinimumWidth(0, 10); - runOptionsLayout->addWidget(runOptionsLabel, 0, 0, 1, 3); runOptionsLayout->addWidget(m_runOptionLoopCheckBox, 1, 1, 1, 2); runOptionsLayout->addWidget(m_runOptionMoveMouseCheckBox, 2, 1, 1, 2); runOptionsLayout->addWidget(m_runOptionDelayBetweenCommandsSpinBox, 3, 1); @@ -416,18 +451,15 @@ WuQMacroDialog::runOptionLoopCheckBoxClicked(bool checked) * Button that was clicked. */ void -WuQMacroDialog::buttonBoxButtonClicked(QAbstractButton* button) +WuQMacroDialog::buttonBoxButtonClicked(QAbstractButton* /*button*/) { - if (button == m_runButton) { - runSelectedMacro(); - } } /** * @return The widget displayed when a commnand is selected */ QWidget* -WuQMacroDialog::createCommandWidget() +WuQMacroDialog::createCommandDisplayWidget() { QLabel* titleLabel = new QLabel("Title:"); m_commandTitleLabel = new QLabel(); @@ -459,6 +491,7 @@ WuQMacroDialog::createCommandWidget() m_commandToolTip->setWordWrap(true); QGridLayout* layout = new QGridLayout(); + layout->setContentsMargins(0, 0, 0, 0); layout->setColumnStretch(0, 0); layout->setColumnStretch(1, 0); layout->setColumnStretch(2, 100); @@ -466,12 +499,6 @@ WuQMacroDialog::createCommandWidget() layout->addWidget(titleLabel, row, 0); layout->addWidget(m_commandTitleLabel, row, 1, 1, 2); row++; - layout->addWidget(nameLabel, row, 0); - layout->addWidget(m_commandNameLabel, row, 1, 1, 2); - row++; - layout->addWidget(typeLabel, row, 0); - layout->addWidget(m_commandTypeLabel, row, 1, 1, 2); - row++; layout->addWidget(valueOneLabel, row, 0); layout->addWidget(m_commandValueOnePushButton, row, 1); layout->addWidget(m_commandValueOneLabel, row, 2); @@ -480,11 +507,24 @@ WuQMacroDialog::createCommandWidget() layout->addWidget(m_commandValueTwoPushButton, row, 1); layout->addWidget(m_commandValueTwoLabel, row, 2); row++; + layout->addWidget(nameLabel, row, 0); + layout->addWidget(m_commandNameLabel, row, 1, 1, 2); + row++; + layout->addWidget(typeLabel, row, 0); + layout->addWidget(m_commandTypeLabel, row, 1, 1, 2); + row++; layout->addWidget(toolTipLabel, row, 0); layout->addWidget(m_commandToolTip, row, 1, 1, 2); + row++; + + QHBoxLayout* titleLayout = new QHBoxLayout(); + titleLayout->setContentsMargins(0, 0, 0, 0); + titleLayout->addWidget(new QLabel("Command")); + titleLayout->addWidget(createHorizontalLine(), 100); QWidget* widget = new QWidget(); QVBoxLayout* widgetLayout = new QVBoxLayout(widget); + widgetLayout->addLayout(titleLayout); widgetLayout->addLayout(layout); widgetLayout->addStretch(); @@ -530,7 +570,6 @@ WuQMacroDialog::updateDialogContents() m_runOptionLoopCheckBox->setChecked(runOptions->isLooping()); macroGroupComboBoxActivated(selectedIndex); - updateButtons(); } /** @@ -583,7 +622,7 @@ WuQMacroDialog::treeViewItemClicked(const QModelIndex& modelIndex) updateMacroWidget(macro); updateCommandWidget(macroCommand); - updateButtons(); + updateEditingToolButtons(); } /** @@ -595,7 +634,24 @@ WuQMacroDialog::macroGroupComboBoxActivated(int) WuQMacroGroup* selectedGroup = getSelectedMacroGroup(); if (selectedGroup != NULL) { + QModelIndex selectedIndex; + if (m_treeView->model() != selectedGroup) { + /* + * Model has changed, select first macro + */ + if (selectedGroup->getNumberOfMacros() > 0) { + selectedIndex = selectedGroup->indexFromItem(selectedGroup->getMacroAtIndex(0)); + } + } + else { + selectedIndex = m_treeView->currentIndex(); + } + m_treeView->setModel(selectedGroup); + + if (selectedIndex.isValid()) { + m_treeView->setCurrentIndex(selectedIndex); + } treeViewItemClicked(m_treeView->currentIndex()); } else { @@ -858,13 +914,13 @@ WuQMacroDialog::getSelectedItemType() const } /** - * Run the selected macro + * Called when run button is clicked */ void -WuQMacroDialog::runSelectedMacro() +WuQMacroDialog::runMacroToolButtonClicked() { if (WuQMacroManager::instance()->isModeRecording()) { - QMessageBox::critical(m_runButton, + QMessageBox::critical(m_runMacroToolButton, "Error", "A macro is being recorded. Finish recording of macro.", QMessageBox::Ok, @@ -875,7 +931,7 @@ WuQMacroDialog::runSelectedMacro() WuQMacro* macro = getSelectedMacro(); if (macro != NULL) { if (macro->getNumberOfMacroCommands() <= 0) { - QMessageBox::critical(m_runButton, + QMessageBox::critical(m_runMacroToolButton, "Error", "Macro does not contain any commands", QMessageBox::Ok, @@ -889,30 +945,14 @@ WuQMacroDialog::runSelectedMacro() } /** - * Called when the delete button is clicked - */ -void -WuQMacroDialog::deleteButtonClicked() -{ - WuQMacroGroup* macroGroup = getSelectedMacroGroup(); - WuQMacro* macro = getSelectedMacro(); - if ((macroGroup != NULL) - && (macro != NULL)) { - if (WuQMacroManager::instance()->deleteMacro(m_deletePushButton, macroGroup, macro)) { - updateDialogContents(); - } - } -} - -/** - * Called when import button is clicked + * Called when import item is selected */ void -WuQMacroDialog::importButtonClicked() +WuQMacroDialog::importMacroGroupActionTriggered() { WuQMacroGroup* macroGroup = getSelectedMacroGroup(); if (macroGroup != NULL) { - if (WuQMacroManager::instance()->importMacros(m_importPushButton, + if (WuQMacroManager::instance()->importMacros(m_macroGroupToolButton, macroGroup)) { updateDialogContents(); } @@ -920,15 +960,15 @@ WuQMacroDialog::importButtonClicked() } /** - * Called when export button is clicked + * Called when export item is selected */ void -WuQMacroDialog::exportButtonClicked() +WuQMacroDialog::exportMacroGroupActionTriggered() { WuQMacroGroup* macroGroup = getSelectedMacroGroup(); WuQMacro* macro = getSelectedMacro(); - if (WuQMacroManager::instance()->exportMacros(m_exportPushButton, + if (WuQMacroManager::instance()->exportMacros(m_macroGroupToolButton, macroGroup, macro)) { updateDialogContents(); @@ -1064,4 +1104,314 @@ WuQMacroDialog::setMacroCommandValue(const ValueIndex valueIndex) updateDialogContents(); } +/** + * @return a horizontal line + */ +QWidget* +WuQMacroDialog::createHorizontalLine() const +{ + QFrame* horizontalLine = new QFrame(); + horizontalLine->setMidLineWidth(1); + horizontalLine->setLineWidth(1); + horizontalLine->setFrameStyle(QFrame::HLine | QFrame::Sunken); + return horizontalLine; +} + +/** + * Called when macro group tool button is clicked + */ +void +WuQMacroDialog::macroGroupToolButtonClicked() +{ + QMenu* menu = new QMenu(this); + + QAction* importAction = menu->addAction("Import..."); + QObject::connect(importAction, &QAction::triggered, + this, &WuQMacroDialog::importMacroGroupActionTriggered); + + QAction* exportAction = menu->addAction("Export..."); + QObject::connect(exportAction, &QAction::triggered, + this, &WuQMacroDialog::exportMacroGroupActionTriggered); + + menu->exec(mapToGlobal(m_macroGroupToolButton->pos())); + + delete menu; +} + +/** + * @return Instance of macro group menu + */ +QMenu* +WuQMacroDialog::createMacroGroupMenu() +{ + QMenu* menu = new QMenu(this); + + QAction* importAction = menu->addAction("Import..."); + QObject::connect(importAction, &QAction::triggered, + this, &WuQMacroDialog::importMacroGroupActionTriggered); + + QAction* exportAction = menu->addAction("Export..."); + QObject::connect(exportAction, &QAction::triggered, + this, &WuQMacroDialog::exportMacroGroupActionTriggered); + + return menu; +} + +/** + * Called when editing move up button clicked + */ +void +WuQMacroDialog::editingMoveUpToolButtonClicked() +{ + WuQMacroGroup* macroGroup = getSelectedMacroGroup(); + WuQMacro* macro = getSelectedMacro(); + WuQMacroCommand* command = getSelectedMacroCommand(); + + switch (getSelectedItemType()) { + case WuQMacroStandardItemTypeEnum::INVALID: + break; + case WuQMacroStandardItemTypeEnum::MACRO: + if (macroGroup != NULL) { + if (macro != NULL) { + macroGroup->moveMacroUp(macro); +// updateMacroWidget(macro); + m_treeView->setCurrentIndex(macro->index()); + treeViewItemClicked(macro->index()); + } + } + break; + case WuQMacroStandardItemTypeEnum::MACRO_COMMAND: + if ((macro != NULL) + && (command != NULL)) { + macro->moveMacroCommandUp(command); +// updateCommandWidget(command); + m_treeView->setCurrentIndex(command->index()); + treeViewItemClicked(command->index()); + } + break; + } +} + +/** + * Called when editing move down button clicked + */ +void +WuQMacroDialog::editingMoveDownToolButtonClicked() +{ + WuQMacroGroup* macroGroup = getSelectedMacroGroup(); + WuQMacro* macro = getSelectedMacro(); + WuQMacroCommand* command = getSelectedMacroCommand(); + + switch (getSelectedItemType()) { + case WuQMacroStandardItemTypeEnum::INVALID: + break; + case WuQMacroStandardItemTypeEnum::MACRO: + if (macroGroup != NULL) { + if (macro != NULL) { + macroGroup->moveMacroDown(macro); +// updateMacroWidget(macro); + m_treeView->setCurrentIndex(macro->index()); + treeViewItemClicked(macro->index()); + } + } + break; + case WuQMacroStandardItemTypeEnum::MACRO_COMMAND: + if ((macro != NULL) + && (command != NULL)) { + macro->moveMacroCommandDown(command); +// updateCommandWidget(command); + m_treeView->setCurrentIndex(command->index()); + treeViewItemClicked(command->index()); + } + break; + } +} + +/** + * Called when editing delete button clicked + */ +void +WuQMacroDialog::editingDeleteToolButtonClicked() +{ + WuQMacroGroup* macroGroup = getSelectedMacroGroup(); + WuQMacro* macro = getSelectedMacro(); + WuQMacroCommand* command = getSelectedMacroCommand(); + + switch (getSelectedItemType()) { + case WuQMacroStandardItemTypeEnum::INVALID: + break; + case WuQMacroStandardItemTypeEnum::MACRO: + if ((macroGroup != NULL) + && (macro != NULL)) { + int32_t macroIndex = macroGroup->getIndexOfMacro(macro); + if (WuQMacroManager::instance()->deleteMacro(m_editingDeleteToolButton, + macroGroup, + macro)) { + updateDialogContents(); + + if (macroIndex < macroGroup->getNumberOfMacros()) { + macroIndex = macroGroup->getNumberOfMacros() - 1; + } + if ((macroIndex >= 0) + && (macroIndex < macroGroup->getNumberOfMacros())) { + QModelIndex modelIndex = macroGroup->getMacroAtIndex(macroIndex)->index(); + m_treeView->setCurrentIndex(modelIndex); + treeViewItemClicked(modelIndex); + } + } + } + break; + case WuQMacroStandardItemTypeEnum::MACRO_COMMAND: + if ((macro != NULL) + && (command != NULL)) { + macro->deleteMacroCommand(command); + updateDialogContents(); + } + break; + } +} + +/** + * Called when editing insert button clicked + */ +void +WuQMacroDialog::editingInsertToolButtonClicked() +{ + std::cout << "Inserrt button clicked" << std::endl; +} + +/** + * Update editing buttons after item selected + * in macro/command tree view + */ +void +WuQMacroDialog::updateEditingToolButtons() +{ + bool runValid(false); + bool insertValid(false); + bool deleteValid(false); + bool moveUpValid(false); + bool moveDownValid(false); + + WuQMacroGroup* macroGroup = getSelectedMacroGroup(); + WuQMacro* macro = getSelectedMacro(); + WuQMacroCommand* command = getSelectedMacroCommand(); + + switch (getSelectedItemType()) { + case WuQMacroStandardItemTypeEnum::INVALID: + break; + case WuQMacroStandardItemTypeEnum::MACRO: + if (macroGroup != NULL) { + if (macro != NULL) { + const int32_t macroIndex = macroGroup->getIndexOfMacro(macro); + deleteValid = true; + moveUpValid = (macroIndex > 0); + moveDownValid = (macroIndex < (macroGroup->getNumberOfMacros() - 1)); + runValid = true; + } + } + break; + case WuQMacroStandardItemTypeEnum::MACRO_COMMAND: + { + if ((macro != NULL) + && (command != NULL)) { + const int32_t commandIndex = macro->getIndexOfMacroCommand(command); + deleteValid = true; + moveUpValid = (commandIndex > 0); + moveDownValid = (commandIndex < (macro->getNumberOfMacroCommands() - 1)); + } + } + break; + } + + m_runMacroToolButton->setEnabled(runValid); + m_editingDeleteToolButton->setEnabled(deleteValid); + m_editingInsertToolButton->setEnabled(insertValid); + m_editingMoveDownToolButton->setEnabled(moveDownValid); + m_editingMoveUpToolButton->setEnabled(moveUpValid); +} + +/** + * Create a pixmap for the given editing tool button + * + * @param editButton + * The edit button identifier + * @return + * Pixmap for the given button + */ +QPixmap +WuQMacroDialog::createEditingToolButtonPixmap(const QWidget* widget, + const EditButton editButton) +{ + CaretAssert(widget); + const qreal pixmapSize = 22.0; + const qreal maxValue = pixmapSize / 2.0 - 1.0; + const qreal arrowTip = maxValue * (2.0 / 3.0); + + uint32_t pixmapOptions(static_cast(WuQtUtilities::PixMapCreationOptions::TransparentBackground)); + switch (editButton) { + case EditButton::DELETER: + break; + case EditButton::INSERTER: + break; + case EditButton::MOVE_DOWN: + break; + case EditButton::MOVE_UP: + break; + case EditButton::RUN: + /* allow background */ + pixmapOptions = 0; + break; + } + + QPixmap pixmap(static_cast(pixmapSize), + static_cast(pixmapSize)); + QSharedPointer painter = WuQtUtilities::createPixmapWidgetPainterOriginCenter(widget, + pixmap, + pixmapOptions); + QPen pen(painter->pen()); + pen.setWidth(3); + painter->setPen(pen); + + switch (editButton) { + case EditButton::DELETER: + pen.setColor(Qt::red); + painter->setPen(pen); + painter->drawLine(QPointF(-maxValue, maxValue), QPointF(maxValue, -maxValue)); + painter->drawLine(QPointF(-maxValue, -maxValue), QPointF(maxValue, maxValue)); + break; + case EditButton::INSERTER: + painter->drawLine(QPointF(0, maxValue), QPointF(0, -maxValue)); + painter->drawLine(QPointF(-maxValue, 0), QPointF(maxValue, 0)); + break; + case EditButton::MOVE_DOWN: + painter->drawLine(QPointF(0, maxValue), QPointF(0, -maxValue)); + painter->drawLine(QPointF(0, -maxValue), QPointF(arrowTip, -maxValue + arrowTip)); + painter->drawLine(QPointF(0, -maxValue), QPointF(-arrowTip, -maxValue + arrowTip)); + break; + case EditButton::MOVE_UP: + painter->drawLine(QPointF(0, maxValue), QPointF(0, -maxValue)); + painter->drawLine(QPointF(0, maxValue), QPointF(arrowTip, maxValue - arrowTip)); + painter->drawLine(QPointF(0, maxValue), QPointF(-arrowTip, maxValue - arrowTip)); + break; + case EditButton::RUN: + { + painter->setPen(Qt::green); + painter->setBrush(Qt::green); + const qreal p = pixmapSize / 3; + const QPointF points[3] = { + QPointF(-p, -maxValue), + QPointF( p, 0.0), + QPointF(-p, maxValue) + }; + painter->drawConvexPolygon(points, 3); + } + break; + } + + return pixmap; +} + + + diff --git a/src/GuiQt/WuQMacroDialog.h b/src/GuiQt/WuQMacroDialog.h index afc4bb0fa..e064d24b6 100644 --- a/src/GuiQt/WuQMacroDialog.h +++ b/src/GuiQt/WuQMacroDialog.h @@ -36,9 +36,11 @@ class QComboBox; class QDialogButtonBox; class QDoubleSpinBox; class QLabel; +class QMenu; class QStackedWidget; class QStandardItem; class QTreeView; +class QToolButton; namespace caret { @@ -71,11 +73,11 @@ namespace caret { void buttonBoxButtonClicked(QAbstractButton* button); - void deleteButtonClicked(); + void importMacroGroupActionTriggered(); - void importButtonClicked(); + void exportMacroGroupActionTriggered(); - void exportButtonClicked(); + void macroGroupToolButtonClicked(); void macroNameEditButtonClicked(); @@ -89,28 +91,42 @@ namespace caret { void runOptionLoopCheckBoxClicked(bool); + void editingMoveUpToolButtonClicked(); + + void editingMoveDownToolButtonClicked(); + + void editingDeleteToolButtonClicked(); + + void editingInsertToolButtonClicked(); + + void runMacroToolButtonClicked(); + private: enum class ValueIndex { ONE, TWO }; - QWidget* createMacroButtonsWidget(); + enum class EditButton { + DELETER, /* "DELETE" does not compile on an operating system */ + INSERTER, + MOVE_DOWN, + MOVE_UP, + RUN + }; + + QWidget* createMacroAndCommandSelectionWidget(); QWidget* createRunOptionsWidget(); - QWidget* createMacroWidget(); + QWidget* createMacroDisplayWidget(); - QWidget* createCommandWidget(); + QWidget* createCommandDisplayWidget(); void updateMacroWidget(WuQMacro* macro); void updateCommandWidget(WuQMacroCommand* command); - void runSelectedMacro(); - - void updateButtons(); - void setMacroCommandValue(const ValueIndex valueIndex); WuQMacroGroup* getSelectedMacroGroup(); @@ -123,10 +139,23 @@ namespace caret { QStandardItem* getSelectedItem() const; + QWidget* createHorizontalLine() const; + + QMenu* createMacroGroupMenu(); + + QWidget* createMacroRunAndEditingToolButtons(); + + QPixmap createEditingToolButtonPixmap(const QWidget* widget, + const EditButton editButton); + + void updateEditingToolButtons(); + std::vector m_macroGroups; QComboBox* m_macroGroupComboBox; + QToolButton* m_macroGroupToolButton; + QTreeView* m_treeView; QLabel* m_macroNameLabel; @@ -143,14 +172,6 @@ namespace caret { QDialogButtonBox* m_dialogButtonBox; - QAbstractButton* m_runButton; - - QPushButton* m_deletePushButton; - - QPushButton* m_importPushButton; - - QPushButton* m_exportPushButton; - QCheckBox* m_runOptionLoopCheckBox; QCheckBox* m_runOptionMoveMouseCheckBox; @@ -173,6 +194,16 @@ namespace caret { QLabel* m_commandToolTip; + QToolButton* m_runMacroToolButton; + + QToolButton* m_editingMoveUpToolButton; + + QToolButton* m_editingMoveDownToolButton; + + QToolButton* m_editingDeleteToolButton; + + QToolButton* m_editingInsertToolButton; + // ADD_NEW_MEMBERS_HERE }; -- GitLab From 665e653ce668ca769b4c4c43fe70aa936b015683 Mon Sep 17 00:00:00 2001 From: John Harwell Date: Mon, 14 Jan 2019 09:48:01 -0600 Subject: [PATCH 169/427] Use more descriptive names when updating a macro command's value. Fix for macro selection on macro dialog when a macro is removed. --- src/Common/WuQMacroCommand.cxx | 43 ++++++++++++++++++++++++++++++++++ src/Common/WuQMacroCommand.h | 8 +++++++ src/GuiQt/WuQMacroDialog.cxx | 25 ++++++++++++++------ src/GuiQt/WuQMacroManager.cxx | 42 ++++++++++++++++++++++++++++++++- src/GuiQt/WuQMacroManager.h | 5 ++++ 5 files changed, 115 insertions(+), 8 deletions(-) diff --git a/src/Common/WuQMacroCommand.cxx b/src/Common/WuQMacroCommand.cxx index aa35c11dd..e0c31a180 100644 --- a/src/Common/WuQMacroCommand.cxx +++ b/src/Common/WuQMacroCommand.cxx @@ -111,41 +111,55 @@ m_macroMouseEvent(NULL) switch (m_classType) { case WuQMacroClassTypeEnum::ACTION: m_dataType = WuQMacroDataValueTypeEnum::BOOLEAN; + m_dataValueUpdateLabelText = "New Status"; break; case WuQMacroClassTypeEnum::ACTION_GROUP: m_dataType = WuQMacroDataValueTypeEnum::STRING; m_dataTypeTwo = WuQMacroDataValueTypeEnum::INTEGER; + m_dataValueUpdateLabelText = "Select name"; + m_dataValueTwoUpdateLabelText = "Select by index"; m_dataValueTwoValidFlag = true; break; case WuQMacroClassTypeEnum::BUTTON_GROUP: m_dataType = WuQMacroDataValueTypeEnum::STRING; m_dataTypeTwo = WuQMacroDataValueTypeEnum::INTEGER; + m_dataValueUpdateLabelText = "Select button with name"; + m_dataValueTwoUpdateLabelText = "Select button at index"; m_dataValueTwoValidFlag = true; break; case WuQMacroClassTypeEnum::CHECK_BOX: m_dataType = WuQMacroDataValueTypeEnum::BOOLEAN; + m_dataValueUpdateLabelText = "Check On/Off"; break; case WuQMacroClassTypeEnum::COMBO_BOX: m_dataType = WuQMacroDataValueTypeEnum::STRING; + m_dataValueUpdateLabelText = "Select item with name"; m_dataTypeTwo = WuQMacroDataValueTypeEnum::INTEGER; + m_dataValueTwoUpdateLabelText = "Select item at index"; m_dataValueTwoValidFlag = true; break; case WuQMacroClassTypeEnum::DOUBLE_SPIN_BOX: + m_dataValueUpdateLabelText = "New Value"; m_dataType = WuQMacroDataValueTypeEnum::FLOAT; break; case WuQMacroClassTypeEnum::INVALID: break; case WuQMacroClassTypeEnum::LINE_EDIT: m_dataType = WuQMacroDataValueTypeEnum::STRING; + m_dataValueUpdateLabelText = "New Text"; break; case WuQMacroClassTypeEnum::LIST_WIDGET: m_dataType = WuQMacroDataValueTypeEnum::STRING; + m_dataValueUpdateLabelText = "Select item with name"; m_dataTypeTwo = WuQMacroDataValueTypeEnum::INTEGER; + m_dataValueTwoUpdateLabelText = "Select item at index"; m_dataValueTwoValidFlag = true; break; case WuQMacroClassTypeEnum::MENU: m_dataType = WuQMacroDataValueTypeEnum::STRING; + m_dataValueUpdateLabelText = "Select item with name"; m_dataTypeTwo = WuQMacroDataValueTypeEnum::INTEGER; + m_dataValueTwoUpdateLabelText = "Select menu item at index"; m_dataValueTwoValidFlag = true; break; case WuQMacroClassTypeEnum::MOUSE_USER_EVENT: @@ -153,28 +167,37 @@ m_macroMouseEvent(NULL) break; case WuQMacroClassTypeEnum::PUSH_BUTTON: m_dataType = WuQMacroDataValueTypeEnum::BOOLEAN; + m_dataValueUpdateLabelText = "Select button"; break; case WuQMacroClassTypeEnum::RADIO_BUTTON: m_dataType = WuQMacroDataValueTypeEnum::BOOLEAN; + m_dataValueUpdateLabelText = "Select button"; break; case WuQMacroClassTypeEnum::SLIDER: m_dataType = WuQMacroDataValueTypeEnum::INTEGER; + m_dataValueUpdateLabelText = "Move slider to"; break; case WuQMacroClassTypeEnum::SPIN_BOX: m_dataType = WuQMacroDataValueTypeEnum::INTEGER; + m_dataValueUpdateLabelText = "Enter value"; break; case WuQMacroClassTypeEnum::TAB_BAR: m_dataType = WuQMacroDataValueTypeEnum::STRING; m_dataTypeTwo = WuQMacroDataValueTypeEnum::INTEGER; m_dataValueTwoValidFlag = true; + m_dataValueUpdateLabelText = "Select tab with name"; + m_dataValueTwoUpdateLabelText = "Select tab at index"; break; case WuQMacroClassTypeEnum::TAB_WIDGET: m_dataType = WuQMacroDataValueTypeEnum::STRING; m_dataTypeTwo = WuQMacroDataValueTypeEnum::INTEGER; m_dataValueTwoValidFlag = true; + m_dataValueUpdateLabelText = "Select tab with name"; + m_dataValueTwoUpdateLabelText = "Select tab at index"; break; case WuQMacroClassTypeEnum::TOOL_BUTTON: m_dataType = WuQMacroDataValueTypeEnum::BOOLEAN; + m_dataValueUpdateLabelText = "Select button"; break; } @@ -285,9 +308,11 @@ WuQMacroCommand::copyHelperWuQMacroCommand(const WuQMacroCommand& obj) m_classType = obj.m_classType; m_objectName = obj.m_objectName; m_dataValue = obj.m_dataValue; + m_dataValueUpdateLabelText = obj.m_dataValueUpdateLabelText; m_dataType = obj.m_dataType; m_dataTypeTwo = obj.m_dataTypeTwo; m_dataValueTwo = obj.m_dataValueTwo; + m_dataValueTwoUpdateLabelText = obj.m_dataValueTwoUpdateLabelText; m_macroMouseEvent = NULL; switch (m_dataType) { @@ -515,6 +540,15 @@ WuQMacroCommand::setDataValue(const QVariant& dataValue) } } +/** + * @return Text used in dialog for updates the data value + */ +QString +WuQMacroCommand::getDataValueUpdateLabelText() const +{ + return m_dataValueUpdateLabelText; +} + /** * @return The object's second data value. */ @@ -549,6 +583,15 @@ WuQMacroCommand::isDataValueTwoValid() const return m_dataValueTwoValidFlag; } +/** + * @return Text used in dialog for updates the data value + */ +QString +WuQMacroCommand::getDataValueTwoUpdateLabelText() const +{ + return m_dataValueTwoUpdateLabelText; +} + /** * @return Point to mouse event information */ diff --git a/src/Common/WuQMacroCommand.h b/src/Common/WuQMacroCommand.h index fe07e77ac..42861533a 100644 --- a/src/Common/WuQMacroCommand.h +++ b/src/Common/WuQMacroCommand.h @@ -79,6 +79,8 @@ namespace caret { void setDataValue(const QVariant& dataValue); + QString getDataValueUpdateLabelText() const; + WuQMacroDataValueTypeEnum::Enum getDataTypeTwo() const; QVariant getDataValueTwo() const; @@ -87,6 +89,8 @@ namespace caret { bool isDataValueTwoValid() const; + QString getDataValueTwoUpdateLabelText() const; + WuQMacroMouseEventInfo* getMouseEventInfo(); const WuQMacroMouseEventInfo* getMouseEventInfo() const; @@ -122,10 +126,14 @@ namespace caret { WuQMacroDataValueTypeEnum::Enum m_dataType; + QString m_dataValueUpdateLabelText; + QVariant m_dataValueTwo; WuQMacroDataValueTypeEnum::Enum m_dataTypeTwo; + QString m_dataValueTwoUpdateLabelText; + WuQMacroMouseEventInfo* m_macroMouseEvent; bool m_modifiedStatusFlag = false; diff --git a/src/GuiQt/WuQMacroDialog.cxx b/src/GuiQt/WuQMacroDialog.cxx index 30f7c6ac1..25782a735 100644 --- a/src/GuiQt/WuQMacroDialog.cxx +++ b/src/GuiQt/WuQMacroDialog.cxx @@ -989,18 +989,23 @@ WuQMacroDialog::setMacroCommandValue(const ValueIndex valueIndex) return; } + const WuQMacroClassTypeEnum::Enum classType = macroCommand->getClassType(); + WuQMacroDataValueTypeEnum::Enum dataType = WuQMacroDataValueTypeEnum::INVALID; QVariant dataValue; QWidget* parentWidget(NULL); + QString dataValueUpdateText; switch (valueIndex) { case ValueIndex::ONE: dataType = macroCommand->getDataType(); dataValue = macroCommand->getDataValue(); + dataValueUpdateText = macroCommand->getDataValueUpdateLabelText(); parentWidget = m_commandValueOnePushButton; break; case ValueIndex::TWO: dataType = macroCommand->getDataTypeTwo(); dataValue = macroCommand->getDataValueTwo(); + dataValueUpdateText = macroCommand->getDataValueTwoUpdateLabelText(); parentWidget = m_commandValueTwoPushButton; break; } @@ -1017,7 +1022,7 @@ WuQMacroDialog::setMacroCommandValue(const ValueIndex valueIndex) int defaultIndex = (dataValue.toBool() ? 0 : 1); const QString text = QInputDialog::getItem(parentWidget, "New Status", - "New Status", + dataValueUpdateText, items, defaultIndex, false, @@ -1035,7 +1040,7 @@ WuQMacroDialog::setMacroCommandValue(const ValueIndex valueIndex) bool ok(false); const float f = QInputDialog::getDouble(parentWidget, "New Value", - "New Value", + dataValueUpdateText, dataValue.toDouble(), -2147483647, 2147483647, @@ -1052,7 +1057,7 @@ WuQMacroDialog::setMacroCommandValue(const ValueIndex valueIndex) bool ok(false); const int i = QInputDialog::getInt(parentWidget, "New Value", - "New Value", + dataValueUpdateText, dataValue.toInt(), -2147483647, 2147483647, @@ -1073,7 +1078,7 @@ WuQMacroDialog::setMacroCommandValue(const ValueIndex valueIndex) bool ok(false); const QString text = QInputDialog::getText(parentWidget, "New Text", - "New Text", + dataValueUpdateText, QLineEdit::Normal, dataValue.toString(), &ok); @@ -1247,9 +1252,11 @@ WuQMacroDialog::editingDeleteToolButtonClicked() if (WuQMacroManager::instance()->deleteMacro(m_editingDeleteToolButton, macroGroup, macro)) { + updateDialogContents(); - if (macroIndex < macroGroup->getNumberOfMacros()) { + CaretAssert(macroIndex >= 0); + if (macroIndex >= macroGroup->getNumberOfMacros()) { macroIndex = macroGroup->getNumberOfMacros() - 1; } if ((macroIndex >= 0) @@ -1264,8 +1271,12 @@ WuQMacroDialog::editingDeleteToolButtonClicked() case WuQMacroStandardItemTypeEnum::MACRO_COMMAND: if ((macro != NULL) && (command != NULL)) { - macro->deleteMacroCommand(command); - updateDialogContents(); + if (WuQMacroManager::instance()->deleteMacroCommand(m_editingDeleteToolButton, + macroGroup, + macro, + command)) { + updateDialogContents(); + } } break; } diff --git a/src/GuiQt/WuQMacroManager.cxx b/src/GuiQt/WuQMacroManager.cxx index fee6d28f6..4735e607c 100644 --- a/src/GuiQt/WuQMacroManager.cxx +++ b/src/GuiQt/WuQMacroManager.cxx @@ -581,9 +581,11 @@ WuQMacroManager::deleteMacro(QWidget* parent, WuQMacroGroup* macroGroup, WuQMacro* macro) { + CaretAssert(macroGroup); + CaretAssert(macro); if (QMessageBox::warning(parent, "Warning", - ("Delete the macro " + macro->getName()), + ("Delete the macro: " + macro->getName()), QMessageBox::Ok | QMessageBox::Cancel, QMessageBox::Ok) == QMessageBox::Ok) { macroGroup->deleteMacro(macro); @@ -596,6 +598,44 @@ WuQMacroManager::deleteMacro(QWidget* parent, return false; } +/** + * Delete a macro command + * + * @param parent + * Parent widget for dialog + * @param macroGroup + * Group containing macro + * @param macro + * Macro containing command to be deleted + * @param macroCommand + * Macro command for deletion + * @return + * True if macro was deleted. + */ +bool +WuQMacroManager::deleteMacroCommand(QWidget* parent, + WuQMacroGroup* macroGroup, + WuQMacro* macro, + WuQMacroCommand* macroCommand) +{ + CaretAssert(macroGroup); + CaretAssert(macro); + CaretAssert(macroCommand); + if (QMessageBox::warning(parent, + "Warning", + ("Delete the macro command: " + macroCommand->getDescriptiveName()), + QMessageBox::Ok | QMessageBox::Cancel, + QMessageBox::Ok) == QMessageBox::Ok) { + macro->deleteMacroCommand(macroCommand); + if (m_macroHelper) { + m_macroHelper->macroGroupWasModified(macroGroup); + } + return true; + } + + return false; +} + /** * Import macros from a file * diff --git a/src/GuiQt/WuQMacroManager.h b/src/GuiQt/WuQMacroManager.h index 07fba10b0..1a82bd3f4 100644 --- a/src/GuiQt/WuQMacroManager.h +++ b/src/GuiQt/WuQMacroManager.h @@ -100,6 +100,11 @@ namespace caret { WuQMacroGroup* macroGroup, WuQMacro* macro); + bool deleteMacroCommand(QWidget* parent, + WuQMacroGroup* macroGroup, + WuQMacro* macro, + WuQMacroCommand* macroCommand); + bool importMacros(QWidget* parent, WuQMacroGroup* appendToMacroGroup); -- GitLab From 7ea195fdaabe112bb28771770d297f96e1d59a7e Mon Sep 17 00:00:00 2001 From: John Harwell Date: Mon, 14 Jan 2019 14:39:41 -0600 Subject: [PATCH 170/427] WB-834 Macros: Updates for QAction, QPushButton, and QToolButton. Each of these widget types may have a checkable status and the checkable status is affected by membership in an exclusive group. Also fixed some parent widget issues for orientation buttons in toolbar so that when macro is run, mouse is over the button containing the action. --- src/Common/WuQMacroClassTypeEnum.cxx | 14 +- src/Common/WuQMacroClassTypeEnum.h | 14 +- src/Common/WuQMacroCommand.cxx | 36 ++++- src/Common/WuQMacroDataValueTypeEnum.cxx | 6 +- src/Common/WuQMacroDataValueTypeEnum.h | 2 + src/Common/WuQMacroGroupXmlReader.cxx | 6 + src/Common/WuQMacroGroupXmlWriter.cxx | 12 ++ src/GuiQt/BrainBrowserWindowToolBar.cxx | 32 +++-- src/GuiQt/WuQMacroDialog.cxx | 10 +- src/GuiQt/WuQMacroExecutor.cxx | 150 +++++++++++++++++--- src/GuiQt/WuQMacroExecutor.h | 15 ++ src/GuiQt/WuQMacroSignalWatcher.cxx | 168 +++++++++++++++++++++++ 12 files changed, 420 insertions(+), 45 deletions(-) diff --git a/src/Common/WuQMacroClassTypeEnum.cxx b/src/Common/WuQMacroClassTypeEnum.cxx index fc2ceea6c..3bb4c4855 100644 --- a/src/Common/WuQMacroClassTypeEnum.cxx +++ b/src/Common/WuQMacroClassTypeEnum.cxx @@ -114,6 +114,10 @@ WuQMacroClassTypeEnum::initialize() "ACTION", "QAction")); + enumData.push_back(WuQMacroClassTypeEnum(ACTION_CHECKABLE, + "ACTION_CHECKABLE", + "QActionCheckable")); + enumData.push_back(WuQMacroClassTypeEnum(ACTION_GROUP, "ACTION_GROUP", "QActionGroup")); @@ -154,7 +158,11 @@ WuQMacroClassTypeEnum::initialize() "PUSH_BUTTON", "QPushButton")); - enumData.push_back(WuQMacroClassTypeEnum(RADIO_BUTTON, + enumData.push_back(WuQMacroClassTypeEnum(PUSH_BUTTON_CHECKABLE, + "PUSH_BUTTON_CHECKABLE", + "QPushButtonCheckable")); + + enumData.push_back(WuQMacroClassTypeEnum(RADIO_BUTTON, "RADIO_BUTTON", "QRadioButton")); @@ -177,6 +185,10 @@ WuQMacroClassTypeEnum::initialize() enumData.push_back(WuQMacroClassTypeEnum(TOOL_BUTTON, "TOOL_BUTTON", "QToolButton")); + + enumData.push_back(WuQMacroClassTypeEnum(TOOL_BUTTON_CHECKABLE, + "TOOL_BUTTON_CHECKABLE", + "QToolButtonCheckable")); } /** diff --git a/src/Common/WuQMacroClassTypeEnum.h b/src/Common/WuQMacroClassTypeEnum.h index 79b1a7bbd..eb81511ff 100644 --- a/src/Common/WuQMacroClassTypeEnum.h +++ b/src/Common/WuQMacroClassTypeEnum.h @@ -37,8 +37,10 @@ public: enum Enum { /** Invalid type */ INVALID, - /** a QAction */ + /** a QAction (not checkable) */ ACTION, + /** a Checkable QAction */ + ACTION_CHECKABLE, /** a QAction Group */ ACTION_GROUP, /** a QButtonGroup */ @@ -57,8 +59,10 @@ public: MENU, /** a Mouse Event */ MOUSE_USER_EVENT, - /** a QPushButton */ + /** a QPushButton(not checkable) */ PUSH_BUTTON, + /** a Checkable QPushButton */ + PUSH_BUTTON_CHECKABLE, /** a QRadioButton */ RADIO_BUTTON, /** a QSlider */ @@ -69,8 +73,10 @@ public: TAB_BAR, /** a QTabWidget */ TAB_WIDGET, - /** a QToolButton */ - TOOL_BUTTON + /** a QToolButton (not checkable) */ + TOOL_BUTTON, + /** a Checkable QToolButton */ + TOOL_BUTTON_CHECKABLE }; /* diff --git a/src/Common/WuQMacroCommand.cxx b/src/Common/WuQMacroCommand.cxx index e0c31a180..ee5845033 100644 --- a/src/Common/WuQMacroCommand.cxx +++ b/src/Common/WuQMacroCommand.cxx @@ -110,8 +110,12 @@ m_macroMouseEvent(NULL) switch (m_classType) { case WuQMacroClassTypeEnum::ACTION: + m_dataType = WuQMacroDataValueTypeEnum::NONE; + m_dataValueUpdateLabelText = "Click/Select"; + break; + case WuQMacroClassTypeEnum::ACTION_CHECKABLE: m_dataType = WuQMacroDataValueTypeEnum::BOOLEAN; - m_dataValueUpdateLabelText = "New Status"; + m_dataValueUpdateLabelText = "On/Off"; break; case WuQMacroClassTypeEnum::ACTION_GROUP: m_dataType = WuQMacroDataValueTypeEnum::STRING; @@ -166,11 +170,16 @@ m_macroMouseEvent(NULL) m_dataType = WuQMacroDataValueTypeEnum::MOUSE; break; case WuQMacroClassTypeEnum::PUSH_BUTTON: + m_dataType = WuQMacroDataValueTypeEnum::NONE; + m_dataValueUpdateLabelText = "Click button"; + break; + case WuQMacroClassTypeEnum::PUSH_BUTTON_CHECKABLE: m_dataType = WuQMacroDataValueTypeEnum::BOOLEAN; - m_dataValueUpdateLabelText = "Select button"; + m_dataValueUpdateLabelText = "On/Off"; break; case WuQMacroClassTypeEnum::RADIO_BUTTON: - m_dataType = WuQMacroDataValueTypeEnum::BOOLEAN; + /* Note: Radio buttons cannot be "unselected" */ + m_dataType = WuQMacroDataValueTypeEnum::NONE; m_dataValueUpdateLabelText = "Select button"; break; case WuQMacroClassTypeEnum::SLIDER: @@ -196,9 +205,13 @@ m_macroMouseEvent(NULL) m_dataValueTwoUpdateLabelText = "Select tab at index"; break; case WuQMacroClassTypeEnum::TOOL_BUTTON: - m_dataType = WuQMacroDataValueTypeEnum::BOOLEAN; + m_dataType = WuQMacroDataValueTypeEnum::NONE; m_dataValueUpdateLabelText = "Select button"; break; + case WuQMacroClassTypeEnum::TOOL_BUTTON_CHECKABLE: + m_dataType = WuQMacroDataValueTypeEnum::BOOLEAN; + m_dataValueUpdateLabelText = "On/Off"; + break; } CaretAssert(m_dataType != WuQMacroDataValueTypeEnum::INVALID); @@ -329,6 +342,8 @@ WuQMacroCommand::copyHelperWuQMacroCommand(const WuQMacroCommand& obj) m_macroMouseEvent = new WuQMacroMouseEventInfo(*obj.m_macroMouseEvent); } break; + case WuQMacroDataValueTypeEnum::NONE: + break; case WuQMacroDataValueTypeEnum::STRING: break; } @@ -349,6 +364,10 @@ WuQMacroCommand::updateTitle() title = ("Turn " + QString((m_dataValue.toBool() ? "On" : "Off"))); break; + case WuQMacroClassTypeEnum::ACTION_CHECKABLE: + title = ("Turn " + + QString((m_dataValue.toBool() ? "On" : "Off"))); + break; case WuQMacroClassTypeEnum::ACTION_GROUP: title = ("Select Name " + m_dataValue.toString() @@ -405,10 +424,13 @@ WuQMacroCommand::updateTitle() case WuQMacroClassTypeEnum::PUSH_BUTTON: title = ("Click Button"); break; - case WuQMacroClassTypeEnum::RADIO_BUTTON: + case WuQMacroClassTypeEnum::PUSH_BUTTON_CHECKABLE: title = ("Turn " + QString((m_dataValue.toBool() ? "On" : "Off"))); break; + case WuQMacroClassTypeEnum::RADIO_BUTTON: + title = "Click Button"; + break; case WuQMacroClassTypeEnum::SLIDER: title = ("Move to " + AString::number(m_dataValue.toInt())); @@ -434,6 +456,10 @@ WuQMacroCommand::updateTitle() case WuQMacroClassTypeEnum::TOOL_BUTTON: title = ("Click Button"); break; + case WuQMacroClassTypeEnum::TOOL_BUTTON_CHECKABLE: + title = ("Turn " + + QString((m_dataValue.toBool() ? "On" : "Off"))); + break; } if ( ! m_descriptiveName.isEmpty()) { diff --git a/src/Common/WuQMacroDataValueTypeEnum.cxx b/src/Common/WuQMacroDataValueTypeEnum.cxx index 2774d60d3..c7790efe5 100644 --- a/src/Common/WuQMacroDataValueTypeEnum.cxx +++ b/src/Common/WuQMacroDataValueTypeEnum.cxx @@ -126,7 +126,11 @@ WuQMacroDataValueTypeEnum::initialize() "MOUSE", "Mouse")); - enumData.push_back(WuQMacroDataValueTypeEnum(STRING, + enumData.push_back(WuQMacroDataValueTypeEnum(NONE, + "NONE", + "None")); + + enumData.push_back(WuQMacroDataValueTypeEnum(STRING, "STRING", "String")); diff --git a/src/Common/WuQMacroDataValueTypeEnum.h b/src/Common/WuQMacroDataValueTypeEnum.h index 9fe956a8f..f6be45165 100644 --- a/src/Common/WuQMacroDataValueTypeEnum.h +++ b/src/Common/WuQMacroDataValueTypeEnum.h @@ -45,6 +45,8 @@ public: INTEGER, /** Mouse */ MOUSE, + /** None (no data value associated with object */ + NONE, /** String */ STRING }; diff --git a/src/Common/WuQMacroGroupXmlReader.cxx b/src/Common/WuQMacroGroupXmlReader.cxx index 0648b7974..7b6da017c 100644 --- a/src/Common/WuQMacroGroupXmlReader.cxx +++ b/src/Common/WuQMacroGroupXmlReader.cxx @@ -385,6 +385,9 @@ WuQMacroGroupXmlReader::readMacroCommandAttributesVersionOne() case WuQMacroDataValueTypeEnum::MOUSE: CaretAssertMessage(0, "Mouse is special case handled above"); break; + case WuQMacroDataValueTypeEnum::NONE: + value.setValue(QString()); + break; case WuQMacroDataValueTypeEnum::STRING: value.setValue(valueString.toString()); break; @@ -415,6 +418,9 @@ WuQMacroGroupXmlReader::readMacroCommandAttributesVersionOne() break; case WuQMacroDataValueTypeEnum::MOUSE: break; + case WuQMacroDataValueTypeEnum::NONE: + value.setValue(QString()); + break; case WuQMacroDataValueTypeEnum::STRING: valueTwo.setValue(valueTwoString.toString()); break; diff --git a/src/Common/WuQMacroGroupXmlWriter.cxx b/src/Common/WuQMacroGroupXmlWriter.cxx index b60bc0387..0a5de5360 100644 --- a/src/Common/WuQMacroGroupXmlWriter.cxx +++ b/src/Common/WuQMacroGroupXmlWriter.cxx @@ -163,6 +163,9 @@ WuQMacroGroupXmlWriter::writeMacroCommandToXML(const WuQMacroCommand* macroComma case WuQMacroDataValueTypeEnum::MOUSE: stringValue = "MouseEvent"; break; + case WuQMacroDataValueTypeEnum::NONE: + stringValue = ""; + break; case WuQMacroDataValueTypeEnum::STRING: stringValue = dataValue.toString(); break; @@ -192,6 +195,9 @@ WuQMacroGroupXmlWriter::writeMacroCommandToXML(const WuQMacroCommand* macroComma case WuQMacroDataValueTypeEnum::MOUSE: stringValue = "MouseEvent"; break; + case WuQMacroDataValueTypeEnum::NONE: + stringValue = ""; + break; case WuQMacroDataValueTypeEnum::STRING: stringValue = dataValueTwo.toString(); break; @@ -204,6 +210,8 @@ WuQMacroGroupXmlWriter::writeMacroCommandToXML(const WuQMacroCommand* macroComma switch (macroCommand->getClassType()) { case WuQMacroClassTypeEnum::ACTION: break; + case WuQMacroClassTypeEnum::ACTION_CHECKABLE: + break; case WuQMacroClassTypeEnum::ACTION_GROUP: break; case WuQMacroClassTypeEnum::BUTTON_GROUP: @@ -227,6 +235,8 @@ WuQMacroGroupXmlWriter::writeMacroCommandToXML(const WuQMacroCommand* macroComma break; case WuQMacroClassTypeEnum::PUSH_BUTTON: break; + case WuQMacroClassTypeEnum::PUSH_BUTTON_CHECKABLE: + break; case WuQMacroClassTypeEnum::RADIO_BUTTON: break; case WuQMacroClassTypeEnum::SLIDER: @@ -239,6 +249,8 @@ WuQMacroGroupXmlWriter::writeMacroCommandToXML(const WuQMacroCommand* macroComma break; case WuQMacroClassTypeEnum::TOOL_BUTTON: break; + case WuQMacroClassTypeEnum::TOOL_BUTTON_CHECKABLE: + break; } const QString toolTip = macroCommand->getObjectToolTip(); diff --git a/src/GuiQt/BrainBrowserWindowToolBar.cxx b/src/GuiQt/BrainBrowserWindowToolBar.cxx index 0a5c3c0ae..db7cf3fde 100644 --- a/src/GuiQt/BrainBrowserWindowToolBar.cxx +++ b/src/GuiQt/BrainBrowserWindowToolBar.cxx @@ -2255,6 +2255,7 @@ BrainBrowserWindowToolBar::createOrientationWidget() this->orientationLeftOrLateralToolButton = new QToolButton(); this->orientationLeftOrLateralToolButton->setDefaultAction(this->orientationLeftOrLateralToolButtonAction); WuQtUtilities::setToolButtonStyleForQt5Mac(this->orientationLeftOrLateralToolButton); + this->orientationLeftOrLateralToolButtonAction->setParent(this->orientationLeftOrLateralToolButton); this->orientationRightOrMedialToolButton = new QToolButton(); this->orientationRightOrMedialToolButton->setDefaultAction(this->orientationRightOrMedialToolButtonAction); @@ -2264,18 +2265,22 @@ BrainBrowserWindowToolBar::createOrientationWidget() this->orientationAnteriorToolButton = new QToolButton(); this->orientationAnteriorToolButton->setDefaultAction(this->orientationAnteriorToolButtonAction); WuQtUtilities::setToolButtonStyleForQt5Mac(this->orientationAnteriorToolButton); + this->orientationAnteriorToolButtonAction->setParent(this->orientationAnteriorToolButton); this->orientationPosteriorToolButton = new QToolButton(); this->orientationPosteriorToolButton->setDefaultAction(this->orientationPosteriorToolButtonAction); WuQtUtilities::setToolButtonStyleForQt5Mac(this->orientationPosteriorToolButton); + this->orientationPosteriorToolButtonAction->setParent(this->orientationPosteriorToolButton); this->orientationDorsalToolButton = new QToolButton(); this->orientationDorsalToolButton->setDefaultAction(this->orientationDorsalToolButtonAction); WuQtUtilities::setToolButtonStyleForQt5Mac(this->orientationDorsalToolButton); + this->orientationDorsalToolButtonAction->setParent(this->orientationDorsalToolButton); this->orientationVentralToolButton = new QToolButton(); this->orientationVentralToolButton->setDefaultAction(this->orientationVentralToolButtonAction); WuQtUtilities::setToolButtonStyleForQt5Mac(this->orientationVentralToolButton); + this->orientationVentralToolButtonAction->setParent(this->orientationVentralToolButton); this->orientationLateralMedialToolButton = new QToolButton(); this->orientationLateralMedialToolButton->setDefaultAction(this->orientationLateralMedialToolButtonAction); @@ -2806,8 +2811,6 @@ BrainBrowserWindowToolBar::createModeWidget() WuQtUtilities::setToolButtonStyleForQt5Mac(inputModeAnnotationsToolButton); this->modeInputModeAnnotationsAction->setObjectName(m_objectNamePrefix + ":Mode:Annotate"); - WuQMacroManager::instance()->addMacroSupportToObject(this->modeInputModeAnnotationsAction, - "Select annotate mode"); /* @@ -2822,8 +2825,6 @@ BrainBrowserWindowToolBar::createModeWidget() WuQtUtilities::setToolButtonStyleForQt5Mac(inputModeBordersToolButton); this->modeInputModeBordersAction->setObjectName(m_objectNamePrefix + ":Mode:Border"); - WuQMacroManager::instance()->addMacroSupportToObject(this->modeInputModeBordersAction, - "Select border mode"); /* * Foci @@ -2837,8 +2838,6 @@ BrainBrowserWindowToolBar::createModeWidget() WuQtUtilities::setToolButtonStyleForQt5Mac(inputModeFociToolButton); this->modeInputModeFociAction->setObjectName(m_objectNamePrefix + ":Mode:Foci"); - WuQMacroManager::instance()->addMacroSupportToObject(this->modeInputModeFociAction, - "Select foci mode"); /* * Image @@ -2856,8 +2855,6 @@ BrainBrowserWindowToolBar::createModeWidget() WuQtUtilities::setToolButtonStyleForQt5Mac(inputModeImageToolButton); this->modeInputModeImageAction->setObjectName(m_objectNamePrefix + ":Mode:Image"); - WuQMacroManager::instance()->addMacroSupportToObject(this->modeInputModeImageAction, - "Select image mode"); } /* @@ -2872,8 +2869,6 @@ BrainBrowserWindowToolBar::createModeWidget() WuQtUtilities::setToolButtonStyleForQt5Mac(inputModeVolumeEditButton); this->modeInputVolumeEditAction->setObjectName(m_objectNamePrefix + ":Mode:Volume"); - WuQMacroManager::instance()->addMacroSupportToObject(this->modeInputVolumeEditAction, - "Select volume mode"); /* * View Mode @@ -2901,8 +2896,6 @@ BrainBrowserWindowToolBar::createModeWidget() WuQtUtilities::setToolButtonStyleForQt5Mac(inputModeViewToolButton); this->modeInputModeViewAction->setObjectName(m_objectNamePrefix + ":Mode:View"); - WuQMacroManager::instance()->addMacroSupportToObject(this->modeInputModeViewAction, - "Select view mode"); WuQtUtilities::matchWidgetWidths(inputModeAnnotationsToolButton, inputModeBordersToolButton, @@ -2951,6 +2944,21 @@ BrainBrowserWindowToolBar::createModeWidget() QObject::connect(this->modeInputModeActionGroup, SIGNAL(triggered(QAction*)), this, SLOT(modeInputModeActionTriggered(QAction*))); this->modeInputModeActionGroup->setExclusive(true); + + WuQMacroManager::instance()->addMacroSupportToObject(this->modeInputModeAnnotationsAction, + "Select annotate mode"); + WuQMacroManager::instance()->addMacroSupportToObject(this->modeInputModeBordersAction, + "Select border mode"); + WuQMacroManager::instance()->addMacroSupportToObject(this->modeInputModeFociAction, + "Select foci mode"); + if (modeInputModeImageAction != NULL) { + WuQMacroManager::instance()->addMacroSupportToObject(this->modeInputModeImageAction, + "Select image mode"); + } + WuQMacroManager::instance()->addMacroSupportToObject(this->modeInputModeViewAction, + "Select view mode"); + WuQMacroManager::instance()->addMacroSupportToObject(this->modeInputVolumeEditAction, + "Select volume mode"); // this->modeInputModeActionGroup->setObjectName(m_objectNamePrefix // + ":Mode_Action_Group"); // WuQMacroManager::instance()->addMacroSupportToObject(this->modeInputModeActionGroup, diff --git a/src/GuiQt/WuQMacroDialog.cxx b/src/GuiQt/WuQMacroDialog.cxx index 25782a735..5321f721e 100644 --- a/src/GuiQt/WuQMacroDialog.cxx +++ b/src/GuiQt/WuQMacroDialog.cxx @@ -716,6 +716,9 @@ WuQMacroDialog::updateCommandWidget(WuQMacroCommand* command) break; case WuQMacroDataValueTypeEnum::MOUSE: break; + case WuQMacroDataValueTypeEnum::NONE: + newDataValueOne = ""; + break; case WuQMacroDataValueTypeEnum::STRING: validOneFlag = true; break; @@ -747,6 +750,9 @@ WuQMacroDialog::updateCommandWidget(WuQMacroCommand* command) break; case WuQMacroDataValueTypeEnum::MOUSE: break; + case WuQMacroDataValueTypeEnum::NONE: + newDataValueTwo = ""; + break; case WuQMacroDataValueTypeEnum::STRING: validTwoFlag = true; break; @@ -989,8 +995,6 @@ WuQMacroDialog::setMacroCommandValue(const ValueIndex valueIndex) return; } - const WuQMacroClassTypeEnum::Enum classType = macroCommand->getClassType(); - WuQMacroDataValueTypeEnum::Enum dataType = WuQMacroDataValueTypeEnum::INVALID; QVariant dataValue; QWidget* parentWidget(NULL); @@ -1073,6 +1077,8 @@ WuQMacroDialog::setMacroCommandValue(const ValueIndex valueIndex) break; case WuQMacroDataValueTypeEnum::MOUSE: break; + case WuQMacroDataValueTypeEnum::NONE: + break; case WuQMacroDataValueTypeEnum::STRING: { bool ok(false); diff --git a/src/GuiQt/WuQMacroExecutor.cxx b/src/GuiQt/WuQMacroExecutor.cxx index cc2aac7eb..d0b1cf9bb 100644 --- a/src/GuiQt/WuQMacroExecutor.cxx +++ b/src/GuiQt/WuQMacroExecutor.cxx @@ -377,6 +377,9 @@ WuQMacroExecutor::runMacroCommand(const WuQMacroCommand* macroCommand, case WuQMacroClassTypeEnum::ACTION: runActionCommand(macroCommand, object, objectErrorMessage, notFoundFlag); break; + case WuQMacroClassTypeEnum::ACTION_CHECKABLE: + runActionCheckableCommand(macroCommand, object, objectErrorMessage, notFoundFlag); + break; case WuQMacroClassTypeEnum::ACTION_GROUP: runActionGroupCommand(macroCommand, object, objectErrorMessage, notFoundFlag); break; @@ -410,6 +413,9 @@ WuQMacroExecutor::runMacroCommand(const WuQMacroCommand* macroCommand, case WuQMacroClassTypeEnum::PUSH_BUTTON: runPushButtonCommand(macroCommand, object, errorMessageOut, notFoundFlag); break; + case WuQMacroClassTypeEnum::PUSH_BUTTON_CHECKABLE: + runPushButtonCheckableCommand(macroCommand, object, errorMessageOut, notFoundFlag); + break; case WuQMacroClassTypeEnum::RADIO_BUTTON: runRadioButtonCommand(macroCommand, object, errorMessageOut, notFoundFlag); break; @@ -428,6 +434,9 @@ WuQMacroExecutor::runMacroCommand(const WuQMacroCommand* macroCommand, case WuQMacroClassTypeEnum::TOOL_BUTTON: runToolButtonCommand(macroCommand, object, errorMessageOut, notFoundFlag); break; + case WuQMacroClassTypeEnum::TOOL_BUTTON_CHECKABLE: + runToolButtonCheckableCommand(macroCommand, object, errorMessageOut, notFoundFlag); + break; } if (notFoundFlag) { @@ -446,7 +455,7 @@ WuQMacroExecutor::runMacroCommand(const WuQMacroCommand* macroCommand, } /** - * Run a selection command + * Run a QAction selection command * * @param macroCommand * Macro command that is run @@ -462,6 +471,37 @@ WuQMacroExecutor::runActionCommand(const WuQMacroCommand* macroCommand, QObject* object, QString& /*errorMessageOut*/, bool& castFailureFlagOut) const +{ + QAction* action = qobject_cast(object); + if (action != NULL) { + moveMouseToWidget(action); + CaretAssert(macroCommand->getDataType() == WuQMacroDataValueTypeEnum::NONE); + WuQMacroSignalEmitter signalEmitter; + signalEmitter.emitQActionSignal(action, + true); + } + else { + castFailureFlagOut = true; + } +} + +/** + * Run a checkable QAction selection command + * + * @param macroCommand + * Macro command that is run + * @param object + * The object that is cast to specific object/widget + * @param errorMessageOut + * Error message from execution + * @param castFailureFlagOut + * Set to true if unable to cast object to widget type + */ +void +WuQMacroExecutor::runActionCheckableCommand(const WuQMacroCommand* macroCommand, + QObject* object, + QString& /*errorMessageOut*/, + bool& castFailureFlagOut) const { QAction* action = qobject_cast(object); if (action != NULL) { @@ -476,8 +516,9 @@ WuQMacroExecutor::runActionCommand(const WuQMacroCommand* macroCommand, } } + /** - * Run a selection command + * Run a action group selection command * * @param macroCommand * Macro command that is run @@ -537,7 +578,7 @@ WuQMacroExecutor::runActionGroupCommand(const WuQMacroCommand* macroCommand, } /** - * Run a selection command + * Run a button group selection command * * @param macroCommand * Macro command that is run @@ -600,7 +641,7 @@ WuQMacroExecutor::runButtonGroupCommand(const WuQMacroCommand* macroCommand, } /** - * Run a selection command + * Run a check box selection command * * @param macroCommand * Macro command that is run @@ -632,7 +673,7 @@ WuQMacroExecutor::runCheckBoxCommand(const WuQMacroCommand* macroCommand, /** - * Run a selection command + * Run a combo box selection command * * @param macroCommand * Macro command that is run @@ -690,7 +731,7 @@ WuQMacroExecutor::runComboBoxCommand(const WuQMacroCommand* macroCommand, } /** - * Run a selection command + * Run a double spin box selection command * * @param macroCommand * Macro command that is run @@ -724,7 +765,7 @@ WuQMacroExecutor::runDoubleSpinBoxCommand(const WuQMacroCommand* macroCommand, } /** - * Run a selection command + * Run a line edit selection command * * @param macroCommand * Macro command that is run @@ -759,7 +800,7 @@ WuQMacroExecutor::runLineEditCommand(const WuQMacroCommand* macroCommand, /** - * Run a selection command + * Run a list widget selection command * * @param macroCommand * Macro command that is run @@ -824,7 +865,7 @@ WuQMacroExecutor::runListWidgetCommand(const WuQMacroCommand* macroCommand, } /** - * Run a selection command + * Run a menu selection selection command * * @param macroCommand * Macro command that is run @@ -890,7 +931,7 @@ WuQMacroExecutor::runMenuCommand(const WuQMacroCommand* macroCommand, } /** - * Run a selection command + * Run a mouse command * * @param macroCommand * Macro command that is run @@ -977,7 +1018,7 @@ WuQMacroExecutor::runMouseCommand(const WuQMacroCommand* macroCommand, } /** - * Run a selection command + * Run a pushbutton selection command * * @param macroCommand * Macro command that is run @@ -999,6 +1040,40 @@ WuQMacroExecutor::runPushButtonCommand(const WuQMacroCommand* macroCommand, const QVariant dataValue = macroCommand->getDataValue(); const WuQMacroDataValueTypeEnum::Enum dataValueType = macroCommand->getDataType(); + moveMouseToWidget(pushButton); + CaretAssert(dataValueType == WuQMacroDataValueTypeEnum::NONE); + WuQMacroSignalEmitter signalEmitter; + signalEmitter.emitQPushButtonSignal(pushButton, + true); + } + else { + castFailureFlagOut = true; + } +} + +/** + * Run a checkable pushbutton selection command + * + * @param macroCommand + * Macro command that is run + * @param object + * The object that is cast to specific object/widget + * @param errorMessageOut + * Error message from execution + * @param castFailureFlagOut + * Set to true if unable to cast object to widget type + */ +void +WuQMacroExecutor::runPushButtonCheckableCommand(const WuQMacroCommand* macroCommand, + QObject* object, + QString& /*errorMessageOut*/, + bool& castFailureFlagOut) const +{ + QPushButton* pushButton = qobject_cast(object); + if (pushButton != NULL) { + const QVariant dataValue = macroCommand->getDataValue(); + const WuQMacroDataValueTypeEnum::Enum dataValueType = macroCommand->getDataType(); + moveMouseToWidget(pushButton); CaretAssert(dataValueType == WuQMacroDataValueTypeEnum::BOOLEAN); WuQMacroSignalEmitter signalEmitter; @@ -1011,7 +1086,7 @@ WuQMacroExecutor::runPushButtonCommand(const WuQMacroCommand* macroCommand, } /** - * Run a selection command + * Run a radio button selection command * * @param macroCommand * Macro command that is run @@ -1034,10 +1109,11 @@ WuQMacroExecutor::runRadioButtonCommand(const WuQMacroCommand* macroCommand, const WuQMacroDataValueTypeEnum::Enum dataValueType = macroCommand->getDataType(); moveMouseToWidget(radioButton); - CaretAssert(dataValueType == WuQMacroDataValueTypeEnum::BOOLEAN); + CaretAssert(dataValueType == WuQMacroDataValueTypeEnum::NONE); WuQMacroSignalEmitter signalEmitter; + /* Note: Radio buttons are always exclusive so always use a true value */ signalEmitter.emitQRadioButtonSignal(radioButton, - dataValue.toBool()); + true); } else { castFailureFlagOut = true; @@ -1045,7 +1121,7 @@ WuQMacroExecutor::runRadioButtonCommand(const WuQMacroCommand* macroCommand, } /** - * Run a selection command + * Run a slider selection command * * @param macroCommand * Macro command that is run @@ -1079,7 +1155,7 @@ WuQMacroExecutor::runSliderCommand(const WuQMacroCommand* macroCommand, } /** - * Run a selection command + * Run a spin box selection command * * @param macroCommand * Macro command that is run @@ -1113,7 +1189,7 @@ WuQMacroExecutor::runSpinBoxCommand(const WuQMacroCommand* macroCommand, } /** - * Run a selection command + * Run a tab bar selection command * * @param macroCommand * Macro command that is run @@ -1176,7 +1252,7 @@ WuQMacroExecutor::runTabBarCommand(const WuQMacroCommand* macroCommand, } /** - * Run a selection command + * Run a tab widget selection command * * @param macroCommand * Macro command that is run @@ -1242,7 +1318,7 @@ WuQMacroExecutor::runTabWidgetCommand(const WuQMacroCommand* macroCommand, } /** - * Run a selection command + * Run a toolbutton selection command * * @param macroCommand * Macro command that is run @@ -1265,6 +1341,41 @@ WuQMacroExecutor::runToolButtonCommand(const WuQMacroCommand* macroCommand, const WuQMacroDataValueTypeEnum::Enum dataValueType = macroCommand->getDataType(); const QVariant dataValueTwo = macroCommand->getDataValueTwo(); + moveMouseToWidget(toolButton); + CaretAssert(dataValueType == WuQMacroDataValueTypeEnum::NONE); + WuQMacroSignalEmitter signalEmitter; + signalEmitter.emitQToolButtonSignal(toolButton, + true); + } + else { + castFailureFlagOut = true; + } +} + +/** + * Run a checkable toolbutton selection command + * + * @param macroCommand + * Macro command that is run + * @param object + * The object that is cast to specific object/widget + * @param errorMessageOut + * Error message from execution + * @param castFailureFlagOut + * Set to true if unable to cast object to widget type + */ +void +WuQMacroExecutor::runToolButtonCheckableCommand(const WuQMacroCommand* macroCommand, + QObject* object, + QString& /*errorMessageOut*/, + bool& castFailureFlagOut) const +{ + QToolButton* toolButton = qobject_cast(object); + if (toolButton != NULL) { + const QVariant dataValue = macroCommand->getDataValue(); + const WuQMacroDataValueTypeEnum::Enum dataValueType = macroCommand->getDataType(); + const QVariant dataValueTwo = macroCommand->getDataValueTwo(); + moveMouseToWidget(toolButton); CaretAssert(dataValueType == WuQMacroDataValueTypeEnum::BOOLEAN); WuQMacroSignalEmitter signalEmitter; @@ -1275,4 +1386,3 @@ WuQMacroExecutor::runToolButtonCommand(const WuQMacroCommand* macroCommand, castFailureFlagOut = true; } } - diff --git a/src/GuiQt/WuQMacroExecutor.h b/src/GuiQt/WuQMacroExecutor.h index 0688debb2..a23d93075 100644 --- a/src/GuiQt/WuQMacroExecutor.h +++ b/src/GuiQt/WuQMacroExecutor.h @@ -88,6 +88,11 @@ namespace caret { QString& errorMessageOut, bool& castFailureFlagOut) const; + void runActionCheckableCommand(const WuQMacroCommand* macroCommand, + QObject* object, + QString& errorMessageOut, + bool& castFailureFlagOut) const; + void runActionGroupCommand(const WuQMacroCommand* macroCommand, QObject* object, QString& errorMessageOut, @@ -138,6 +143,11 @@ namespace caret { QString& errorMessageOut, bool& castFailureFlagOut) const; + void runPushButtonCheckableCommand(const WuQMacroCommand* macroCommand, + QObject* object, + QString& errorMessageOut, + bool& castFailureFlagOut) const; + void runRadioButtonCommand(const WuQMacroCommand* macroCommand, QObject* object, QString& errorMessageOut, @@ -168,6 +178,11 @@ namespace caret { QString& errorMessageOut, bool& castFailureFlagOut) const; + void runToolButtonCheckableCommand(const WuQMacroCommand* macroCommand, + QObject* object, + QString& errorMessageOut, + bool& castFailureFlagOut) const; + mutable WuQMacroExecutorOptions m_runOptions; mutable std::vector m_parentObjects; diff --git a/src/GuiQt/WuQMacroSignalWatcher.cxx b/src/GuiQt/WuQMacroSignalWatcher.cxx index 71493569c..483f786ba 100644 --- a/src/GuiQt/WuQMacroSignalWatcher.cxx +++ b/src/GuiQt/WuQMacroSignalWatcher.cxx @@ -90,6 +90,15 @@ m_objectName(object->objectName()) switch (m_objectType) { case WuQMacroClassTypeEnum::ACTION: + { + QAction* action = qobject_cast(m_object); + CaretAssert(action); + QObject::connect(action, &QAction::triggered, + this, &WuQMacroSignalWatcher::actionTriggered); + m_toolTipText = action->toolTip(); + } + break; + case WuQMacroClassTypeEnum::ACTION_CHECKABLE: { QAction* action = qobject_cast(m_object); CaretAssert(action); @@ -169,6 +178,14 @@ m_objectName(object->objectName()) CaretAssertToDoFatal(); break; case WuQMacroClassTypeEnum::PUSH_BUTTON: + { + QPushButton* pushButton = qobject_cast(m_object); + CaretAssert(pushButton); + QObject::connect(pushButton, &QPushButton::clicked, + this, &WuQMacroSignalWatcher::pushButtonClicked); + } + break; + case WuQMacroClassTypeEnum::PUSH_BUTTON_CHECKABLE: { QPushButton* pushButton = qobject_cast(m_object); CaretAssert(pushButton); @@ -224,6 +241,14 @@ m_objectName(object->objectName()) this, &WuQMacroSignalWatcher::toolButtonClicked); } break; + case WuQMacroClassTypeEnum::TOOL_BUTTON_CHECKABLE: + { + QToolButton* toolButton = qobject_cast(m_object); + CaretAssert(toolButton); + QObject::connect(toolButton, &QCheckBox::clicked, + this, &WuQMacroSignalWatcher::toolButtonClicked); + } + break; } /* @@ -315,6 +340,149 @@ WuQMacroSignalWatcher::newInstance(WuQMacroManager* parentMacroManager, WuQMacroClassTypeEnum::Enum objectType = WuQMacroClassTypeEnum::fromGuiName(objectClassName, &validFlag); + /* + * Some Qt Widgets may have a 'checkable' state + * and the 'checkable' and 'non-checkable' states + * must be handled differently. + */ + switch (objectType) { + case WuQMacroClassTypeEnum::ACTION: + { + /* + * Actions may have a 'checkable' status enabled + * Actions may also be in a QActionGroup and the + * QActionGroup may have an 'exclusive' status. + * + * NOTE: For this logic to work, the actions must have + * macro support added after the actions are placed + * in an exclusive action group + */ + QAction* action = qobject_cast(object); + CaretAssert(action); + if (action->isCheckable()) { + /* + * Probably checkable + */ + objectType = WuQMacroClassTypeEnum::ACTION_CHECKABLE; + + const QActionGroup* actionGroup = action->actionGroup(); + if (actionGroup != NULL) { + if (actionGroup->isExclusive()) { + /* + * In an exclusive group, actions CANNOT be + * uchecked so treat as non-checkable action + */ + objectType = WuQMacroClassTypeEnum::ACTION; + } + } + } + } + break; + case WuQMacroClassTypeEnum::ACTION_CHECKABLE: + CaretAssertMessage(0, "ACTION_CHECKABLE is created by ACTION above"); + break; + case WuQMacroClassTypeEnum::ACTION_GROUP: + break; + case WuQMacroClassTypeEnum::BUTTON_GROUP: + break; + case WuQMacroClassTypeEnum::CHECK_BOX: + break; + case WuQMacroClassTypeEnum::COMBO_BOX: + break; + case WuQMacroClassTypeEnum::DOUBLE_SPIN_BOX: + break; + case WuQMacroClassTypeEnum::INVALID: + break; + case WuQMacroClassTypeEnum::LINE_EDIT: + break; + case WuQMacroClassTypeEnum::LIST_WIDGET: + break; + case WuQMacroClassTypeEnum::MENU: + break; + case WuQMacroClassTypeEnum::MOUSE_USER_EVENT: + break; + case WuQMacroClassTypeEnum::PUSH_BUTTON: + { + /* + * Buttons may have a 'checkable' status enabled + * Buttons may also be in a QButtonGroup and the + * QButtonGroup may have an 'exclusive' status. + * + * NOTE: For this logic to work, the buttons must have + * macro support added after the buttons are placed + * in an exclusive button group + */ + QAbstractButton* button = qobject_cast(object); + CaretAssert(button); + if (button->isCheckable()) { + /* + * Probably checkable + */ + objectType = WuQMacroClassTypeEnum::PUSH_BUTTON_CHECKABLE; + + const QButtonGroup* buttonGroup = button->group(); + if (buttonGroup != NULL) { + if (buttonGroup->exclusive()) { + /* + * In an exclusive group, buttons CANNOT be + * uchecked so treat as non-checkable button + */ + objectType = WuQMacroClassTypeEnum::PUSH_BUTTON; + } + } + } + } + break; + case WuQMacroClassTypeEnum::PUSH_BUTTON_CHECKABLE: + CaretAssertMessage(0, "PUSH_BUTTON_CHECKABLE is created by PUSH_BUTTON case above"); + break; + case WuQMacroClassTypeEnum::RADIO_BUTTON: + break; + case WuQMacroClassTypeEnum::SLIDER: + break; + case WuQMacroClassTypeEnum::SPIN_BOX: + break; + case WuQMacroClassTypeEnum::TAB_BAR: + break; + case WuQMacroClassTypeEnum::TAB_WIDGET: + break; + case WuQMacroClassTypeEnum::TOOL_BUTTON: + { + /* + * Buttons may have a 'checkable' status enabled + * Buttons may also be in a QButtonGroup and the + * QButtonGroup may have an 'exclusive' status. + * + * NOTE: For this logic to work, the buttons must have + * macro support added after the buttons are placed + * in an exclusive button group + */ + QAbstractButton* button = qobject_cast(object); + CaretAssert(button); + if (button->isCheckable()) { + /* + * Probably checkable + */ + objectType = WuQMacroClassTypeEnum::TOOL_BUTTON_CHECKABLE; + + const QButtonGroup* buttonGroup = button->group(); + if (buttonGroup != NULL) { + if (buttonGroup->exclusive()) { + /* + * In an exclusive group, buttons CANNOT be + * uchecked so treat as non-checkable button + */ + objectType = WuQMacroClassTypeEnum::TOOL_BUTTON; + } + } + } + } + break; + case WuQMacroClassTypeEnum::TOOL_BUTTON_CHECKABLE: + CaretAssertMessage(0, "TOOL_BUTTON_CHECKABLE is created by TOOL_BUTTON case above"); + break; + } + if ((objectType == WuQMacroClassTypeEnum::INVALID) || ( ! validFlag)) { errorMessageOut = ("Widget named \"" -- GitLab From 21769364a18b7b586ee4cfa1ee58398c631017ba Mon Sep 17 00:00:00 2001 From: John Harwell Date: Tue, 15 Jan 2019 09:33:35 -0600 Subject: [PATCH 171/427] WB-834 Macros: Renamed classes for reading/writing Macros to indicate that they are XML stream reader/writers. --- src/Common/CMakeLists.txt | 12 +-- src/Common/WuQMacroFile.cxx | 8 +- src/Common/WuQMacroGroup.cxx | 8 +- ...ase.cxx => WuQMacroGroupXmlStreamBase.cxx} | 16 ++-- ...matBase.h => WuQMacroGroupXmlStreamBase.h} | 84 +++++++++---------- ...r.cxx => WuQMacroGroupXmlStreamReader.cxx} | 34 ++++---- ...eader.h => WuQMacroGroupXmlStreamReader.h} | 22 ++--- ...r.cxx => WuQMacroGroupXmlStreamWriter.cxx} | 22 ++--- ...riter.h => WuQMacroGroupXmlStreamWriter.h} | 22 ++--- src/Scenes/SceneInfo.cxx | 4 +- src/Scenes/SceneInfoSaxReader.cxx | 4 +- 11 files changed, 118 insertions(+), 118 deletions(-) rename src/Common/{WuQMacroGroupXmlFormatBase.cxx => WuQMacroGroupXmlStreamBase.cxx} (75%) rename src/Common/{WuQMacroGroupXmlFormatBase.h => WuQMacroGroupXmlStreamBase.h} (58%) rename src/Common/{WuQMacroGroupXmlReader.cxx => WuQMacroGroupXmlStreamReader.cxx} (95%) rename src/Common/{WuQMacroGroupXmlReader.h => WuQMacroGroupXmlStreamReader.h} (75%) rename src/Common/{WuQMacroGroupXmlWriter.cxx => WuQMacroGroupXmlStreamWriter.cxx} (93%) rename src/Common/{WuQMacroGroupXmlWriter.h => WuQMacroGroupXmlStreamWriter.h} (71%) diff --git a/src/Common/CMakeLists.txt b/src/Common/CMakeLists.txt index a1e3ce675..cfd35acd6 100644 --- a/src/Common/CMakeLists.txt +++ b/src/Common/CMakeLists.txt @@ -167,9 +167,9 @@ WuQMacroCommand.h WuQMacroDataValueTypeEnum.h WuQMacroFile.h WuQMacroGroup.h -WuQMacroGroupXmlFormatBase.h -WuQMacroGroupXmlReader.h -WuQMacroGroupXmlWriter.h +WuQMacroGroupXmlStreamBase.h +WuQMacroGroupXmlStreamReader.h +WuQMacroGroupXmlStreamWriter.h WuQMacroModeEnum.h WuQMacroMouseEventInfo.h WuQMacroMouseEventTypeEnum.h @@ -282,9 +282,9 @@ WuQMacroCommand.cxx WuQMacroDataValueTypeEnum.cxx WuQMacroFile.cxx WuQMacroGroup.cxx -WuQMacroGroupXmlFormatBase.cxx -WuQMacroGroupXmlReader.cxx -WuQMacroGroupXmlWriter.cxx +WuQMacroGroupXmlStreamBase.cxx +WuQMacroGroupXmlStreamReader.cxx +WuQMacroGroupXmlStreamWriter.cxx WuQMacroModeEnum.cxx WuQMacroMouseEventInfo.cxx WuQMacroMouseEventTypeEnum.cxx diff --git a/src/Common/WuQMacroFile.cxx b/src/Common/WuQMacroFile.cxx index 061358263..957bffffa 100644 --- a/src/Common/WuQMacroFile.cxx +++ b/src/Common/WuQMacroFile.cxx @@ -32,8 +32,8 @@ #include "DataFileException.h" #include "WuQMacro.h" #include "WuQMacroGroup.h" -#include "WuQMacroGroupXmlReader.h" -#include "WuQMacroGroupXmlWriter.h" +#include "WuQMacroGroupXmlStreamReader.h" +#include "WuQMacroGroupXmlStreamWriter.h" using namespace caret; @@ -220,7 +220,7 @@ WuQMacroFile::readFile(const AString& filename) QTextStream textStream(&file); const QString fileContentString = textStream.readAll(); - WuQMacroGroupXmlReader reader; + WuQMacroGroupXmlStreamReader reader; reader.readFromString(fileContentString, m_macroGroup.get()); file.close(); @@ -266,7 +266,7 @@ WuQMacroFile::writeFile(const AString& filename) * Place the macro group into a string */ QString fileContentString; - WuQMacroGroupXmlWriter writer; + WuQMacroGroupXmlStreamWriter writer; writer.writeToString(m_macroGroup.get(), fileContentString); diff --git a/src/Common/WuQMacroGroup.cxx b/src/Common/WuQMacroGroup.cxx index d6fd2f752..810cfd27a 100644 --- a/src/Common/WuQMacroGroup.cxx +++ b/src/Common/WuQMacroGroup.cxx @@ -27,8 +27,8 @@ #include "CaretAssert.h" #include "WuQMacro.h" -#include "WuQMacroGroupXmlReader.h" -#include "WuQMacroGroupXmlWriter.h" +#include "WuQMacroGroupXmlStreamReader.h" +#include "WuQMacroGroupXmlStreamWriter.h" using namespace caret; @@ -534,7 +534,7 @@ WuQMacroGroup::readXmlFromStringOld(const QString& xmlString, errorMessageOut.clear(); nonFatalWarningMessageOut.clear(); - WuQMacroGroupXmlReader reader; + WuQMacroGroupXmlStreamReader reader; reader.readFromString(xmlString, this); if (reader.hasError()) { @@ -569,7 +569,7 @@ WuQMacroGroup::writeXmlToString(QString& xmlString, xmlString.clear(); errorMessageOut.clear(); - WuQMacroGroupXmlWriter writer; + WuQMacroGroupXmlStreamWriter writer; writer.writeToString(this, xmlString); clearModified(); diff --git a/src/Common/WuQMacroGroupXmlFormatBase.cxx b/src/Common/WuQMacroGroupXmlStreamBase.cxx similarity index 75% rename from src/Common/WuQMacroGroupXmlFormatBase.cxx rename to src/Common/WuQMacroGroupXmlStreamBase.cxx index 2928dd57d..8da464f07 100644 --- a/src/Common/WuQMacroGroupXmlFormatBase.cxx +++ b/src/Common/WuQMacroGroupXmlStreamBase.cxx @@ -19,9 +19,9 @@ */ /*LICENSE_END*/ -#define __WU_Q_MACRO_GROUP_XML_FORMAT_BASE_DECLARE__ -#include "WuQMacroGroupXmlFormatBase.h" -#undef __WU_Q_MACRO_GROUP_XML_FORMAT_BASE_DECLARE__ +#define __WU_Q_MACRO_GROUP_XML_STREAM_BASE_DECLARE__ +#include "WuQMacroGroupXmlStreamBase.h" +#undef __WU_Q_MACRO_GROUP_XML_STREAM_BASE_DECLARE__ #include "CaretAssert.h" using namespace caret; @@ -29,7 +29,7 @@ using namespace caret; /** - * \class caret::WuQMacroGroupXmlFormatBase + * \class caret::WuQMacroGroupXmlStreamBase * \brief Base class for reading/writing macro group in XML * \ingroup Common */ @@ -37,7 +37,7 @@ using namespace caret; /** * Constructor. */ -WuQMacroGroupXmlFormatBase::WuQMacroGroupXmlFormatBase() +WuQMacroGroupXmlStreamBase::WuQMacroGroupXmlStreamBase() : CaretObject() { @@ -46,7 +46,7 @@ WuQMacroGroupXmlFormatBase::WuQMacroGroupXmlFormatBase() /** * Destructor. */ -WuQMacroGroupXmlFormatBase::~WuQMacroGroupXmlFormatBase() +WuQMacroGroupXmlStreamBase::~WuQMacroGroupXmlStreamBase() { } @@ -55,8 +55,8 @@ WuQMacroGroupXmlFormatBase::~WuQMacroGroupXmlFormatBase() * @return String describing this object's content. */ AString -WuQMacroGroupXmlFormatBase::toString() const +WuQMacroGroupXmlStreamBase::toString() const { - return "WuQMacroGroupXmlFormatBase"; + return "WuQMacroGroupXmlStreamBase"; } diff --git a/src/Common/WuQMacroGroupXmlFormatBase.h b/src/Common/WuQMacroGroupXmlStreamBase.h similarity index 58% rename from src/Common/WuQMacroGroupXmlFormatBase.h rename to src/Common/WuQMacroGroupXmlStreamBase.h index 750dad83c..5f88f06c1 100644 --- a/src/Common/WuQMacroGroupXmlFormatBase.h +++ b/src/Common/WuQMacroGroupXmlStreamBase.h @@ -1,5 +1,5 @@ -#ifndef __WU_Q_MACRO_GROUP_XML_FORMAT_BASE_H__ -#define __WU_Q_MACRO_GROUP_XML_FORMAT_BASE_H__ +#ifndef __WU_Q_MACRO_GROUP_XML_STREAM_BASE_H__ +#define __WU_Q_MACRO_GROUP_XML_STREAM_BASE_H__ /*LICENSE_START*/ /* @@ -31,17 +31,17 @@ namespace caret { - class WuQMacroGroupXmlFormatBase : public CaretObject { + class WuQMacroGroupXmlStreamBase : public CaretObject { protected: - WuQMacroGroupXmlFormatBase(); + WuQMacroGroupXmlStreamBase(); public: - virtual ~WuQMacroGroupXmlFormatBase(); + virtual ~WuQMacroGroupXmlStreamBase(); - WuQMacroGroupXmlFormatBase(const WuQMacroGroupXmlFormatBase&) = delete; + WuQMacroGroupXmlStreamBase(const WuQMacroGroupXmlStreamBase&) = delete; - WuQMacroGroupXmlFormatBase& operator=(const WuQMacroGroupXmlFormatBase&) = delete; + WuQMacroGroupXmlStreamBase& operator=(const WuQMacroGroupXmlStreamBase&) = delete; // ADD_NEW_METHODS_HERE @@ -91,42 +91,42 @@ namespace caret { }; -#ifdef __WU_Q_MACRO_GROUP_XML_FORMAT_BASE_DECLARE__ - const QString WuQMacroGroupXmlFormatBase::ATTRIBUTE_NAME = "Name"; - const QString WuQMacroGroupXmlFormatBase::ATTRIBUTE_OBJECT_CLASS = "ObjectClass"; - const QString WuQMacroGroupXmlFormatBase::ATTRIBUTE_OBJECT_DATA_TYPE = "DataType"; - const QString WuQMacroGroupXmlFormatBase::ATTRIBUTE_OBJECT_DATA_TYPE_TWO = "DataTypeTwo"; - const QString WuQMacroGroupXmlFormatBase::ATTRIBUTE_OBJECT_DESCRIPTIVE_NAME = "DescriptiveName"; - const QString WuQMacroGroupXmlFormatBase::ATTRIBUTE_OBJECT_VALUE = "DataValue"; - const QString WuQMacroGroupXmlFormatBase::ATTRIBUTE_OBJECT_VALUE_TWO = "DataValueTwo"; - const QString WuQMacroGroupXmlFormatBase::ATTRIBUTE_SHORT_CUT_KEY = "ShortCutKey"; - const QString WuQMacroGroupXmlFormatBase::ATTRIBUTE_UNIQUE_IDENTIFIER = "UniqueIdentifier"; - const QString WuQMacroGroupXmlFormatBase::ATTRIBUTE_VERSION = "Version"; +#ifdef __WU_Q_MACRO_GROUP_XML_STREAM_BASE_DECLARE__ + const QString WuQMacroGroupXmlStreamBase::ATTRIBUTE_NAME = "Name"; + const QString WuQMacroGroupXmlStreamBase::ATTRIBUTE_OBJECT_CLASS = "ObjectClass"; + const QString WuQMacroGroupXmlStreamBase::ATTRIBUTE_OBJECT_DATA_TYPE = "DataType"; + const QString WuQMacroGroupXmlStreamBase::ATTRIBUTE_OBJECT_DATA_TYPE_TWO = "DataTypeTwo"; + const QString WuQMacroGroupXmlStreamBase::ATTRIBUTE_OBJECT_DESCRIPTIVE_NAME = "DescriptiveName"; + const QString WuQMacroGroupXmlStreamBase::ATTRIBUTE_OBJECT_VALUE = "DataValue"; + const QString WuQMacroGroupXmlStreamBase::ATTRIBUTE_OBJECT_VALUE_TWO = "DataValueTwo"; + const QString WuQMacroGroupXmlStreamBase::ATTRIBUTE_SHORT_CUT_KEY = "ShortCutKey"; + const QString WuQMacroGroupXmlStreamBase::ATTRIBUTE_UNIQUE_IDENTIFIER = "UniqueIdentifier"; + const QString WuQMacroGroupXmlStreamBase::ATTRIBUTE_VERSION = "Version"; - const QString WuQMacroGroupXmlFormatBase::ATTRIBUTE_MOUSE_BUTTON = "MouseButton"; - const QString WuQMacroGroupXmlFormatBase::ATTRIBUTE_MOUSE_BUTTONS_MASK = "MouseButtonsMask"; - const QString WuQMacroGroupXmlFormatBase::ATTRIBUTE_MOUSE_EVENT_TYPE = "MouseEventType"; - const QString WuQMacroGroupXmlFormatBase::ATTRIBUTE_MOUSE_KEYBOARD_MODIFIERS_MASK = "KeyboardModifiersMask"; - const QString WuQMacroGroupXmlFormatBase::ATTRIBUTE_MOUSE_LOCAL_X = "LocalX"; - const QString WuQMacroGroupXmlFormatBase::ATTRIBUTE_MOUSE_LOCAL_Y = "LocalY"; - const QString WuQMacroGroupXmlFormatBase::ATTRIBUTE_MOUSE_SCREEN_X = "ScreenX"; - const QString WuQMacroGroupXmlFormatBase::ATTRIBUTE_MOUSE_SCREEN_Y = "ScreenY"; - const QString WuQMacroGroupXmlFormatBase::ATTRIBUTE_MOUSE_WIDGET_WIDTH = "WidgetWidth"; - const QString WuQMacroGroupXmlFormatBase::ATTRIBUTE_MOUSE_WIDGET_HEIGHT = "WidgetHeight"; - const QString WuQMacroGroupXmlFormatBase::ATTRIBUTE_MOUSE_WINDOW_X = "WindowX"; - const QString WuQMacroGroupXmlFormatBase::ATTRIBUTE_MOUSE_WINDOW_Y = "WindowY"; + const QString WuQMacroGroupXmlStreamBase::ATTRIBUTE_MOUSE_BUTTON = "MouseButton"; + const QString WuQMacroGroupXmlStreamBase::ATTRIBUTE_MOUSE_BUTTONS_MASK = "MouseButtonsMask"; + const QString WuQMacroGroupXmlStreamBase::ATTRIBUTE_MOUSE_EVENT_TYPE = "MouseEventType"; + const QString WuQMacroGroupXmlStreamBase::ATTRIBUTE_MOUSE_KEYBOARD_MODIFIERS_MASK = "KeyboardModifiersMask"; + const QString WuQMacroGroupXmlStreamBase::ATTRIBUTE_MOUSE_LOCAL_X = "LocalX"; + const QString WuQMacroGroupXmlStreamBase::ATTRIBUTE_MOUSE_LOCAL_Y = "LocalY"; + const QString WuQMacroGroupXmlStreamBase::ATTRIBUTE_MOUSE_SCREEN_X = "ScreenX"; + const QString WuQMacroGroupXmlStreamBase::ATTRIBUTE_MOUSE_SCREEN_Y = "ScreenY"; + const QString WuQMacroGroupXmlStreamBase::ATTRIBUTE_MOUSE_WIDGET_WIDTH = "WidgetWidth"; + const QString WuQMacroGroupXmlStreamBase::ATTRIBUTE_MOUSE_WIDGET_HEIGHT = "WidgetHeight"; + const QString WuQMacroGroupXmlStreamBase::ATTRIBUTE_MOUSE_WINDOW_X = "WindowX"; + const QString WuQMacroGroupXmlStreamBase::ATTRIBUTE_MOUSE_WINDOW_Y = "WindowY"; - const QString WuQMacroGroupXmlFormatBase::ELEMENT_DESCRIPTION = "Description"; - const QString WuQMacroGroupXmlFormatBase::ELEMENT_MACRO = "Macro"; - const QString WuQMacroGroupXmlFormatBase::ELEMENT_MACRO_COMMAND = "MacroCommand"; - const QString WuQMacroGroupXmlFormatBase::ELEMENT_MACRO_GROUP = "MacroGroup"; - const QString WuQMacroGroupXmlFormatBase::ELEMENT_MOUSE_EVENT_INFO = "MouseEventInfo"; - const QString WuQMacroGroupXmlFormatBase::ELEMENT_TOOL_TIP = "ToolTip"; - - const QString WuQMacroGroupXmlFormatBase::VALUE_BOOL_FALSE = "false"; - const QString WuQMacroGroupXmlFormatBase::VALUE_BOOL_TRUE = "true"; - const QString WuQMacroGroupXmlFormatBase::VALUE_VERSION_ONE = "1"; -#endif // __WU_Q_MACRO_GROUP_XML_FORMAT_BASE_DECLARE__ + const QString WuQMacroGroupXmlStreamBase::ELEMENT_DESCRIPTION = "Description"; + const QString WuQMacroGroupXmlStreamBase::ELEMENT_MACRO = "Macro"; + const QString WuQMacroGroupXmlStreamBase::ELEMENT_MACRO_COMMAND = "MacroCommand"; + const QString WuQMacroGroupXmlStreamBase::ELEMENT_MACRO_GROUP = "MacroGroup"; + const QString WuQMacroGroupXmlStreamBase::ELEMENT_MOUSE_EVENT_INFO = "MouseEventInfo"; + const QString WuQMacroGroupXmlStreamBase::ELEMENT_TOOL_TIP = "ToolTip"; + + const QString WuQMacroGroupXmlStreamBase::VALUE_BOOL_FALSE = "false"; + const QString WuQMacroGroupXmlStreamBase::VALUE_BOOL_TRUE = "true"; + const QString WuQMacroGroupXmlStreamBase::VALUE_VERSION_ONE = "1"; +#endif // __WU_Q_MACRO_GROUP_XML_STREAM_BASE_DECLARE__ } // namespace -#endif //__WU_Q_MACRO_GROUP_XML_FORMAT_BASE_H__ +#endif //__WU_Q_MACRO_GROUP_XML_STREAM_BASE_H__ diff --git a/src/Common/WuQMacroGroupXmlReader.cxx b/src/Common/WuQMacroGroupXmlStreamReader.cxx similarity index 95% rename from src/Common/WuQMacroGroupXmlReader.cxx rename to src/Common/WuQMacroGroupXmlStreamReader.cxx index 7b6da017c..b2c7a1028 100644 --- a/src/Common/WuQMacroGroupXmlReader.cxx +++ b/src/Common/WuQMacroGroupXmlStreamReader.cxx @@ -19,9 +19,9 @@ */ /*LICENSE_END*/ -#define __WU_Q_MACRO_GROUP_XML_READER_DECLARE__ -#include "WuQMacroGroupXmlReader.h" -#undef __WU_Q_MACRO_GROUP_XML_READER_DECLARE__ +#define __WU_Q_MACRO_GROUP_XML_STREAM_READER_DECLARE__ +#include "WuQMacroGroupXmlStreamReader.h" +#undef __WU_Q_MACRO_GROUP_XML_STREAM_READER_DECLARE__ #include #include @@ -38,7 +38,7 @@ using namespace caret; /** - * \class caret::WuQMacroGroupXmlReader + * \class caret::WuQMacroGroupXmlStreamReader * \brief Reads macro group from XML format * \ingroup Common */ @@ -46,15 +46,15 @@ using namespace caret; /** * Constructor. */ -WuQMacroGroupXmlReader::WuQMacroGroupXmlReader() -: WuQMacroGroupXmlFormatBase() +WuQMacroGroupXmlStreamReader::WuQMacroGroupXmlStreamReader() +: WuQMacroGroupXmlStreamBase() { } /** * Destructor. */ -WuQMacroGroupXmlReader::~WuQMacroGroupXmlReader() +WuQMacroGroupXmlStreamReader::~WuQMacroGroupXmlStreamReader() { } @@ -67,7 +67,7 @@ WuQMacroGroupXmlReader::~WuQMacroGroupXmlReader() * The macro group */ void -WuQMacroGroupXmlReader::readFromString(const QString& xmlString, +WuQMacroGroupXmlStreamReader::readFromString(const QString& xmlString, WuQMacroGroup* macroGroup) { CaretAssert(macroGroup); @@ -142,7 +142,7 @@ WuQMacroGroupXmlReader::readFromString(const QString& xmlString, * The macro group */ void -WuQMacroGroupXmlReader::readVersionOne(WuQMacroGroup* macroGroup) +WuQMacroGroupXmlStreamReader::readVersionOne(WuQMacroGroup* macroGroup) { CaretAssert(macroGroup); @@ -230,7 +230,7 @@ WuQMacroGroupXmlReader::readVersionOne(WuQMacroGroup* macroGroup) * @return The macro */ WuQMacro* -WuQMacroGroupXmlReader::readMacroVersionOne() +WuQMacroGroupXmlStreamReader::readMacroVersionOne() { WuQMacro* macro(NULL); @@ -286,7 +286,7 @@ WuQMacroGroupXmlReader::readMacroVersionOne() * @Return The macro group */ WuQMacroCommand* -WuQMacroGroupXmlReader::readMacroCommandAttributesVersionOne() +WuQMacroGroupXmlStreamReader::readMacroCommandAttributesVersionOne() { WuQMacroCommand* macroCommand(NULL); @@ -442,7 +442,7 @@ WuQMacroGroupXmlReader::readMacroCommandAttributesVersionOne() * @return Read and return the mouse event information */ WuQMacroMouseEventInfo* -WuQMacroGroupXmlReader::readMacroMouseEventInfo() +WuQMacroGroupXmlStreamReader::readMacroMouseEventInfo() { const QXmlStreamAttributes attributes = m_xmlStreamReader->attributes(); const QString mouseEventTypeString = attributes.value(ATTRIBUTE_MOUSE_EVENT_TYPE).toString(); @@ -507,7 +507,7 @@ WuQMacroGroupXmlReader::readMacroMouseEventInfo() } void -WuQMacroGroupXmlReader::addToWarnings(const QString& warning) +WuQMacroGroupXmlStreamReader::addToWarnings(const QString& warning) { if ( ! m_warningMessage.isEmpty()) { m_warningMessage.append("\n"); @@ -525,7 +525,7 @@ WuQMacroGroupXmlReader::addToWarnings(const QString& warning) * @return True if there are non-fatal warnings */ bool -WuQMacroGroupXmlReader::hasWarnings() const +WuQMacroGroupXmlStreamReader::hasWarnings() const { return ( ! m_warningMessage.isEmpty()); } @@ -534,7 +534,7 @@ WuQMacroGroupXmlReader::hasWarnings() const * @return The warning message */ QString -WuQMacroGroupXmlReader::getWarningMessage() const +WuQMacroGroupXmlStreamReader::getWarningMessage() const { return m_warningMessage; } @@ -543,7 +543,7 @@ WuQMacroGroupXmlReader::getWarningMessage() const * @return True if there is an error */ bool -WuQMacroGroupXmlReader::hasError() const +WuQMacroGroupXmlStreamReader::hasError() const { if (m_xmlStreamReader != NULL) { return m_xmlStreamReader->hasError(); @@ -555,7 +555,7 @@ WuQMacroGroupXmlReader::hasError() const * @return The error message */ QString -WuQMacroGroupXmlReader::getErrorMessage() const +WuQMacroGroupXmlStreamReader::getErrorMessage() const { QString errorMessage; diff --git a/src/Common/WuQMacroGroupXmlReader.h b/src/Common/WuQMacroGroupXmlStreamReader.h similarity index 75% rename from src/Common/WuQMacroGroupXmlReader.h rename to src/Common/WuQMacroGroupXmlStreamReader.h index 5a0434a60..ba0698f8e 100644 --- a/src/Common/WuQMacroGroupXmlReader.h +++ b/src/Common/WuQMacroGroupXmlStreamReader.h @@ -1,5 +1,5 @@ -#ifndef __WU_Q_MACRO_GROUP_XML_READER_H__ -#define __WU_Q_MACRO_GROUP_XML_READER_H__ +#ifndef __WU_Q_MACRO_GROUP_XML_STREAM_READER_H__ +#define __WU_Q_MACRO_GROUP_XML_STREAM_READER_H__ /*LICENSE_START*/ /* @@ -25,7 +25,7 @@ #include -#include "WuQMacroGroupXmlFormatBase.h" +#include "WuQMacroGroupXmlStreamBase.h" class QXmlStreamReader; @@ -37,16 +37,16 @@ namespace caret { class WuQMacroGroup; class WuQMacroMouseEventInfo; - class WuQMacroGroupXmlReader : public WuQMacroGroupXmlFormatBase { + class WuQMacroGroupXmlStreamReader : public WuQMacroGroupXmlStreamBase { public: - WuQMacroGroupXmlReader(); + WuQMacroGroupXmlStreamReader(); - virtual ~WuQMacroGroupXmlReader(); + virtual ~WuQMacroGroupXmlStreamReader(); - WuQMacroGroupXmlReader(const WuQMacroGroupXmlReader&) = delete; + WuQMacroGroupXmlStreamReader(const WuQMacroGroupXmlStreamReader&) = delete; - WuQMacroGroupXmlReader& operator=(const WuQMacroGroupXmlReader&) = delete; + WuQMacroGroupXmlStreamReader& operator=(const WuQMacroGroupXmlStreamReader&) = delete; bool hasError() const; @@ -82,9 +82,9 @@ namespace caret { }; -#ifdef __WU_Q_MACRO_GROUP_XML_READER_DECLARE__ +#ifdef __WU_Q_MACRO_GROUP_XML_STREAM_READER_DECLARE__ // -#endif // __WU_Q_MACRO_GROUP_XML_READER_DECLARE__ +#endif // __WU_Q_MACRO_GROUP_XML_STREAM_READER_DECLARE__ } // namespace -#endif //__WU_Q_MACRO_GROUP_XML_READER_H__ +#endif //__WU_Q_MACRO_GROUP_XML_STREAM_READER_H__ diff --git a/src/Common/WuQMacroGroupXmlWriter.cxx b/src/Common/WuQMacroGroupXmlStreamWriter.cxx similarity index 93% rename from src/Common/WuQMacroGroupXmlWriter.cxx rename to src/Common/WuQMacroGroupXmlStreamWriter.cxx index 0a5de5360..7a1011a76 100644 --- a/src/Common/WuQMacroGroupXmlWriter.cxx +++ b/src/Common/WuQMacroGroupXmlStreamWriter.cxx @@ -19,9 +19,9 @@ */ /*LICENSE_END*/ -#define __WU_Q_MACRO_GROUP_XML_WRITER_DECLARE__ -#include "WuQMacroGroupXmlWriter.h" -#undef __WU_Q_MACRO_GROUP_XML_WRITER_DECLARE__ +#define __WU_Q_MACRO_GROUP_XML_STREAM_WRITER_DECLARE__ +#include "WuQMacroGroupXmlStreamWriter.h" +#undef __WU_Q_MACRO_GROUP_XML_STREAM_WRITER_DECLARE__ #include #include @@ -37,7 +37,7 @@ using namespace caret; /** - * \class caret::WuQMacroGroupXmlWriter + * \class caret::WuQMacroGroupXmlStreamWriter * \brief Writes contents of macro group to XML format * \ingroup Common */ @@ -45,15 +45,15 @@ using namespace caret; /** * Constructor */ -WuQMacroGroupXmlWriter::WuQMacroGroupXmlWriter() -: WuQMacroGroupXmlFormatBase() +WuQMacroGroupXmlStreamWriter::WuQMacroGroupXmlStreamWriter() +: WuQMacroGroupXmlStreamBase() { } /** * Destructor. */ -WuQMacroGroupXmlWriter::~WuQMacroGroupXmlWriter() +WuQMacroGroupXmlStreamWriter::~WuQMacroGroupXmlStreamWriter() { } @@ -66,7 +66,7 @@ WuQMacroGroupXmlWriter::~WuQMacroGroupXmlWriter() * Pointer to string to which XML is written */ void -WuQMacroGroupXmlWriter::writeToString(const WuQMacroGroup* macroGroup, +WuQMacroGroupXmlStreamWriter::writeToString(const WuQMacroGroup* macroGroup, QString& contentTextString) { CaretAssert(macroGroup); @@ -99,7 +99,7 @@ WuQMacroGroupXmlWriter::writeToString(const WuQMacroGroup* macroGroup, * The macro */ void -WuQMacroGroupXmlWriter::writeMacroToXML(const WuQMacro* macro) +WuQMacroGroupXmlStreamWriter::writeMacroToXML(const WuQMacro* macro) { CaretAssert(macro); @@ -131,7 +131,7 @@ WuQMacroGroupXmlWriter::writeMacroToXML(const WuQMacro* macro) * The macro command */ void -WuQMacroGroupXmlWriter::writeMacroCommandToXML(const WuQMacroCommand* macroCommand) +WuQMacroGroupXmlStreamWriter::writeMacroCommandToXML(const WuQMacroCommand* macroCommand) { CaretAssert(macroCommand); @@ -268,7 +268,7 @@ WuQMacroGroupXmlWriter::writeMacroCommandToXML(const WuQMacroCommand* macroComma * The macro mouse event info */ void -WuQMacroGroupXmlWriter::writeMacroMouseEventInfo(const WuQMacroMouseEventInfo* mouseEventInfo) +WuQMacroGroupXmlStreamWriter::writeMacroMouseEventInfo(const WuQMacroMouseEventInfo* mouseEventInfo) { CaretAssert(mouseEventInfo); diff --git a/src/Common/WuQMacroGroupXmlWriter.h b/src/Common/WuQMacroGroupXmlStreamWriter.h similarity index 71% rename from src/Common/WuQMacroGroupXmlWriter.h rename to src/Common/WuQMacroGroupXmlStreamWriter.h index 266f73a09..c79b2821d 100644 --- a/src/Common/WuQMacroGroupXmlWriter.h +++ b/src/Common/WuQMacroGroupXmlStreamWriter.h @@ -1,5 +1,5 @@ -#ifndef __WU_Q_MACRO_GROUP_XML_WRITER_H__ -#define __WU_Q_MACRO_GROUP_XML_WRITER_H__ +#ifndef __WU_Q_MACRO_GROUP_XML_STREAM_WRITER_H__ +#define __WU_Q_MACRO_GROUP_XML_STREAM_WRITER_H__ /*LICENSE_START*/ /* @@ -25,7 +25,7 @@ #include -#include "WuQMacroGroupXmlFormatBase.h" +#include "WuQMacroGroupXmlStreamBase.h" class QXmlStreamWriter; @@ -36,19 +36,19 @@ namespace caret { class WuQMacroGroup; class WuQMacroMouseEventInfo; - class WuQMacroGroupXmlWriter : public WuQMacroGroupXmlFormatBase { + class WuQMacroGroupXmlStreamWriter : public WuQMacroGroupXmlStreamBase { public: - WuQMacroGroupXmlWriter(); + WuQMacroGroupXmlStreamWriter(); - virtual ~WuQMacroGroupXmlWriter(); + virtual ~WuQMacroGroupXmlStreamWriter(); void writeToString(const WuQMacroGroup* macroGroup, QString& contentTextString); - WuQMacroGroupXmlWriter(const WuQMacroGroupXmlWriter&) = delete; + WuQMacroGroupXmlStreamWriter(const WuQMacroGroupXmlStreamWriter&) = delete; - WuQMacroGroupXmlWriter& operator=(const WuQMacroGroupXmlWriter&) = delete; + WuQMacroGroupXmlStreamWriter& operator=(const WuQMacroGroupXmlStreamWriter&) = delete; // ADD_NEW_METHODS_HERE @@ -66,9 +66,9 @@ namespace caret { }; -#ifdef __WU_Q_MACRO_GROUP_XML_WRITER_DECLARE__ +#ifdef __WU_Q_MACRO_GROUP_XML_STREAM_WRITER_DECLARE__ // -#endif // __WU_Q_MACRO_GROUP_XML_WRITER_DECLARE__ +#endif // __WU_Q_MACRO_GROUP_XML_STREAM_WRITER_DECLARE__ } // namespace -#endif //__WU_Q_MACRO_GROUP_XML_WRITER_H__ +#endif //__WU_Q_MACRO_GROUP_XML_STREAM_WRITER_H__ diff --git a/src/Scenes/SceneInfo.cxx b/src/Scenes/SceneInfo.cxx index b1f397e74..e3347042d 100644 --- a/src/Scenes/SceneInfo.cxx +++ b/src/Scenes/SceneInfo.cxx @@ -27,7 +27,7 @@ #include "CaretLogger.h" #include "SceneXmlElements.h" #include "WuQMacroGroup.h" -#include "WuQMacroGroupXmlWriter.h" +#include "WuQMacroGroupXmlStreamWriter.h" #include "XmlAttributes.h" #include "XmlUtilities.h" #include "XmlWriter.h" @@ -273,7 +273,7 @@ SceneInfo::writeSceneInfo(XmlWriter& xmlWriter, */ if (m_macroGroup->getNumberOfMacros() > 0) { QString textString; - WuQMacroGroupXmlWriter macroWriter; + WuQMacroGroupXmlStreamWriter macroWriter; macroWriter.writeToString(m_macroGroup.get(), textString); textString = XmlUtilities::encodeXmlSpecialCharacters(textString); diff --git a/src/Scenes/SceneInfoSaxReader.cxx b/src/Scenes/SceneInfoSaxReader.cxx index 6ca9f8823..dbc11fd3a 100644 --- a/src/Scenes/SceneInfoSaxReader.cxx +++ b/src/Scenes/SceneInfoSaxReader.cxx @@ -24,7 +24,7 @@ #include "SceneInfo.h" #include "SceneInfoSaxReader.h" #include "SceneXmlElements.h" -#include "WuQMacroGroupXmlReader.h" +#include "WuQMacroGroupXmlStreamReader.h" #include "XmlAttributes.h" #include "XmlException.h" #include "XmlUtilities.h" @@ -179,7 +179,7 @@ SceneInfoSaxReader::endElement(const AString& /* namspaceURI */, case STATE_SCENE_INFO_MACRO_GROUP: { WuQMacroGroup* macroGroup = m_sceneInfo->getMacroGroup(); - WuQMacroGroupXmlReader reader; + WuQMacroGroupXmlStreamReader reader; reader.readFromString(m_elementText, macroGroup); if (reader.hasError()) { -- GitLab From bb590dbaea7f7d32a36139b16a92aa731c2d4960 Mon Sep 17 00:00:00 2001 From: John Harwell Date: Tue, 15 Jan 2019 11:28:18 -0600 Subject: [PATCH 172/427] WB-834 Macros: Added delay to each macro command. Improvements of macro dialog including text edits for a macro's description and a macro command's tooltip. --- src/Common/WuQMacroCommand.cxx | 24 +++ src/Common/WuQMacroCommand.h | 6 + src/Common/WuQMacroGroupXmlStreamBase.h | 2 + src/Common/WuQMacroGroupXmlStreamReader.cxx | 10 ++ src/Common/WuQMacroGroupXmlStreamWriter.cxx | 2 + src/GuiQt/WuQMacroDialog.cxx | 158 +++++++++++--------- src/GuiQt/WuQMacroDialog.h | 15 +- src/GuiQt/WuQMacroExecutor.cxx | 4 +- src/GuiQt/WuQMacroExecutorOptions.cxx | 22 --- src/GuiQt/WuQMacroExecutorOptions.h | 6 - 10 files changed, 138 insertions(+), 111 deletions(-) diff --git a/src/Common/WuQMacroCommand.cxx b/src/Common/WuQMacroCommand.cxx index ee5845033..8e84f85a0 100644 --- a/src/Common/WuQMacroCommand.cxx +++ b/src/Common/WuQMacroCommand.cxx @@ -327,6 +327,7 @@ WuQMacroCommand::copyHelperWuQMacroCommand(const WuQMacroCommand& obj) m_dataValueTwo = obj.m_dataValueTwo; m_dataValueTwoUpdateLabelText = obj.m_dataValueTwoUpdateLabelText; m_macroMouseEvent = NULL; + m_delayInSeconds = obj.m_delayInSeconds; switch (m_dataType) { case WuQMacroDataValueTypeEnum::INVALID: @@ -707,6 +708,29 @@ WuQMacroCommand::isMouseEventMatch(const WuQMacroCommand* command) const return false; } +/** + * @return Delay in seconds + */ +float +WuQMacroCommand::getDelayInSeconds() const +{ + return m_delayInSeconds; +} + +/** + * Set delay in seconds + * + * @param seconds + * New delay value + */ +void +WuQMacroCommand::setDelayInSeconds(const float seconds) +{ + if (seconds != m_delayInSeconds) { + m_delayInSeconds = seconds; + setModified(); + } +} /** * @return True if this instance is modified diff --git a/src/Common/WuQMacroCommand.h b/src/Common/WuQMacroCommand.h index 42861533a..b8060045b 100644 --- a/src/Common/WuQMacroCommand.h +++ b/src/Common/WuQMacroCommand.h @@ -99,6 +99,10 @@ namespace caret { bool isMouseEventMatch(const WuQMacroCommand* command) const; + float getDelayInSeconds() const; + + void setDelayInSeconds(const float seconds); + // ADD_NEW_METHODS_HERE virtual bool isModified() const override; @@ -136,6 +140,8 @@ namespace caret { WuQMacroMouseEventInfo* m_macroMouseEvent; + float m_delayInSeconds = 1.0f; + bool m_modifiedStatusFlag = false; bool m_dataValueTwoValidFlag = false; diff --git a/src/Common/WuQMacroGroupXmlStreamBase.h b/src/Common/WuQMacroGroupXmlStreamBase.h index 5f88f06c1..4ac290488 100644 --- a/src/Common/WuQMacroGroupXmlStreamBase.h +++ b/src/Common/WuQMacroGroupXmlStreamBase.h @@ -57,6 +57,7 @@ namespace caret { static const QString ATTRIBUTE_OBJECT_DESCRIPTIVE_NAME; static const QString ATTRIBUTE_OBJECT_VALUE; static const QString ATTRIBUTE_OBJECT_VALUE_TWO; + static const QString ATTRIBUTE_DELAY; static const QString ATTRIBUTE_SHORT_CUT_KEY; static const QString ATTRIBUTE_UNIQUE_IDENTIFIER; static const QString ATTRIBUTE_VERSION; @@ -99,6 +100,7 @@ namespace caret { const QString WuQMacroGroupXmlStreamBase::ATTRIBUTE_OBJECT_DESCRIPTIVE_NAME = "DescriptiveName"; const QString WuQMacroGroupXmlStreamBase::ATTRIBUTE_OBJECT_VALUE = "DataValue"; const QString WuQMacroGroupXmlStreamBase::ATTRIBUTE_OBJECT_VALUE_TWO = "DataValueTwo"; + const QString WuQMacroGroupXmlStreamBase::ATTRIBUTE_DELAY = "Delay"; const QString WuQMacroGroupXmlStreamBase::ATTRIBUTE_SHORT_CUT_KEY = "ShortCutKey"; const QString WuQMacroGroupXmlStreamBase::ATTRIBUTE_UNIQUE_IDENTIFIER = "UniqueIdentifier"; const QString WuQMacroGroupXmlStreamBase::ATTRIBUTE_VERSION = "Version"; diff --git a/src/Common/WuQMacroGroupXmlStreamReader.cxx b/src/Common/WuQMacroGroupXmlStreamReader.cxx index b2c7a1028..60c3eaae5 100644 --- a/src/Common/WuQMacroGroupXmlStreamReader.cxx +++ b/src/Common/WuQMacroGroupXmlStreamReader.cxx @@ -293,6 +293,7 @@ WuQMacroGroupXmlStreamReader::readMacroCommandAttributesVersionOne() const QXmlStreamAttributes attributes = m_xmlStreamReader->attributes(); const QStringRef objectName = attributes.value(ATTRIBUTE_NAME); const QStringRef dataTypeString = attributes.value(ATTRIBUTE_OBJECT_DATA_TYPE); + const QStringRef delayString = attributes.value(ATTRIBUTE_DELAY); const QStringRef descriptiveNameString = attributes.value(ATTRIBUTE_OBJECT_DESCRIPTIVE_NAME); const QStringRef classString = attributes.value(ATTRIBUTE_OBJECT_CLASS); const QStringRef valueString = attributes.value(ATTRIBUTE_OBJECT_VALUE); @@ -433,6 +434,15 @@ WuQMacroGroupXmlStreamReader::readMacroCommandAttributesVersionOne() tooltip, value, valueTwo); + + if ( ! delayString.isEmpty()) { + bool valid(false); + float delayValue = delayString.toFloat(&valid); + if ( ! valid) { + delayValue = 1.0; + } + macroCommand->setDelayInSeconds(delayValue); + } } return macroCommand; diff --git a/src/Common/WuQMacroGroupXmlStreamWriter.cxx b/src/Common/WuQMacroGroupXmlStreamWriter.cxx index 7a1011a76..f1252ee15 100644 --- a/src/Common/WuQMacroGroupXmlStreamWriter.cxx +++ b/src/Common/WuQMacroGroupXmlStreamWriter.cxx @@ -142,6 +142,8 @@ WuQMacroGroupXmlStreamWriter::writeMacroCommandToXML(const WuQMacroCommand* macr WuQMacroClassTypeEnum::toName(macroCommand->getClassType())); m_xmlStreamWriter->writeAttribute(ATTRIBUTE_OBJECT_DESCRIPTIVE_NAME, macroCommand->getDescriptiveName()); + m_xmlStreamWriter->writeAttribute(ATTRIBUTE_DELAY, + QString::number(macroCommand->getDelayInSeconds(), 'f', 2)); { m_xmlStreamWriter->writeAttribute(ATTRIBUTE_OBJECT_DATA_TYPE, WuQMacroDataValueTypeEnum::toName(macroCommand->getDataType())); diff --git a/src/GuiQt/WuQMacroDialog.cxx b/src/GuiQt/WuQMacroDialog.cxx index 5321f721e..b48db497d 100644 --- a/src/GuiQt/WuQMacroDialog.cxx +++ b/src/GuiQt/WuQMacroDialog.cxx @@ -134,7 +134,17 @@ WuQMacroDialog::WuQMacroDialog(QWidget* parent) QPushButton* closeButton = m_dialogButtonBox->button(QDialogButtonBox::Close); CaretAssert(closeButton); - closeButton->setDefault(true); + + /* + * Disable auto default for all push buttons + */ + QList allChildPushButtons = findChildren(QRegExp(".*")); + QListIterator allChildPushButtonsIterator(allChildPushButtons); + while (allChildPushButtonsIterator.hasNext()) { + QPushButton* pushButton = allChildPushButtonsIterator.next(); + pushButton->setAutoDefault(false); + pushButton->setDefault(false); + } } /** @@ -223,9 +233,8 @@ WuQMacroDialog::createMacroAndCommandSelectionWidget() QWidget* WuQMacroDialog::createMacroDisplayWidget() { - QLabel* nameLabel = new QLabel("Name:"); m_macroNameLabel = new QLabel(); - QPushButton* nameEditPushButton = new QPushButton("Edit..."); + QPushButton* nameEditPushButton = new QPushButton("Name..."); nameEditPushButton->setSizePolicy(QSizePolicy::Fixed, nameEditPushButton->sizePolicy().verticalPolicy()); QObject::connect(nameEditPushButton, &QPushButton::clicked, @@ -237,14 +246,11 @@ WuQMacroDialog::createMacroDisplayWidget() QObject::connect(m_macroShortCutKeyComboBox, &WuQMacroShortCutKeyComboBox::shortCutKeySelected, this, &WuQMacroDialog::macroShortCutKeySelected); - QLabel* descriptionLabel = new QLabel("Description:"); - m_macroDescriptionLabel = new QLabel(""); - m_macroDescriptionLabel->setWordWrap(true); - m_macroDescriptionLabel->setMinimumHeight(40); - m_macroDescriptionLabel->setAlignment(Qt::AlignTop - | Qt::AlignLeft); + m_macroDescriptionTextEdit = new QPlainTextEdit(); + m_macroDescriptionTextEdit->setReadOnly(true); + m_macroDescriptionTextEdit->setFixedHeight(100); - QPushButton* descriptionEditPushButton = new QPushButton("Edit..."); + QPushButton* descriptionEditPushButton = new QPushButton("Description..."); descriptionEditPushButton->setSizePolicy(QSizePolicy::Fixed, descriptionEditPushButton->sizePolicy().verticalPolicy()); QObject::connect(descriptionEditPushButton, &QPushButton::clicked, @@ -254,21 +260,17 @@ WuQMacroDialog::createMacroDisplayWidget() gridLayout->setContentsMargins(0, 0, 0, 0); gridLayout->setColumnStretch(0, 0); gridLayout->setColumnStretch(1, 0); - gridLayout->setColumnStretch(2, 0); - gridLayout->setColumnStretch(3, 100); + gridLayout->setColumnStretch(2, 100); int row = 0; - gridLayout->addWidget(nameLabel, row, 0); - gridLayout->addWidget(nameEditPushButton, row, 1); - gridLayout->addWidget(m_macroNameLabel, row, 2, 1, 2); - row++; - gridLayout->addWidget(shortCutKeyLabel, row, 0, 1, 2); - gridLayout->addWidget(shortCutKeyMaskLabel, row, 2); - gridLayout->addWidget(m_macroShortCutKeyComboBox->getWidget(), row, 3, Qt::AlignLeft); + gridLayout->addWidget(nameEditPushButton, row, 0); + gridLayout->addWidget(m_macroNameLabel, row, 1, 1, 2, Qt::AlignLeft); row++; - gridLayout->addWidget(descriptionLabel, row, 0, 1, 2); - gridLayout->addWidget(m_macroDescriptionLabel, row, 2, 2, 2); + gridLayout->addWidget(shortCutKeyLabel, row, 0); + gridLayout->addWidget(shortCutKeyMaskLabel, row, 1); + gridLayout->addWidget(m_macroShortCutKeyComboBox->getWidget(), row, 2, Qt::AlignLeft); row++; - gridLayout->addWidget(descriptionEditPushButton, row, 0, 1, 2); + gridLayout->addWidget(descriptionEditPushButton, row, 0, Qt::AlignTop); + gridLayout->addWidget(m_macroDescriptionTextEdit, row, 1, 1, 2); row++; QHBoxLayout* titleLayout = new QHBoxLayout(); @@ -380,42 +382,16 @@ WuQMacroDialog::createRunOptionsWidget() QObject::connect(m_runOptionMoveMouseCheckBox, &QCheckBox::clicked, this, &WuQMacroDialog::runOptionMoveMouseCheckBoxClicked); - QLabel* runOptionsDelayLabel = new QLabel("Delay (seconds) between commands"); - m_runOptionDelayBetweenCommandsSpinBox = new QDoubleSpinBox(); - m_runOptionDelayBetweenCommandsSpinBox->setMinimum(0.0); - m_runOptionDelayBetweenCommandsSpinBox->setMaximum(1000.0); - m_runOptionDelayBetweenCommandsSpinBox->setSingleStep(0.1); - m_runOptionDelayBetweenCommandsSpinBox->setDecimals(1); - m_runOptionDelayBetweenCommandsSpinBox->setValue(1.0); - m_runOptionDelayBetweenCommandsSpinBox->setToolTip("Pause for this amount of time"); - m_runOptionDelayBetweenCommandsSpinBox->setFixedWidth(80); - QObject::connect(m_runOptionDelayBetweenCommandsSpinBox, static_cast(&QDoubleSpinBox::valueChanged), - this, &WuQMacroDialog::runOptionDelaySpinBoxValueChanged); - QGridLayout* runOptionsLayout = new QGridLayout(widget); - runOptionsLayout->setColumnMinimumWidth(0, 10); - runOptionsLayout->addWidget(m_runOptionLoopCheckBox, 1, 1, 1, 2); - runOptionsLayout->addWidget(m_runOptionMoveMouseCheckBox, 2, 1, 1, 2); - runOptionsLayout->addWidget(m_runOptionDelayBetweenCommandsSpinBox, 3, 1); - runOptionsLayout->addWidget(runOptionsDelayLabel, 3, 2); + int row = 0; + runOptionsLayout->addWidget(m_runOptionLoopCheckBox, row, 0); + row++; + runOptionsLayout->addWidget(m_runOptionMoveMouseCheckBox, row, 0); + row++; return widget; } -/** - * Called when run options delay between commands spin box value changed - * - * @param value - * New value. - */ -void -WuQMacroDialog::runOptionDelaySpinBoxValueChanged(float value) -{ - WuQMacroExecutorOptions* options = WuQMacroManager::instance()->getExecutorOptions(); - CaretAssert(options); - options->setSecondsDelayBetweenCommands(value); -} - /** * Called when run options move mouse checkbox is changed * @@ -470,25 +446,36 @@ WuQMacroDialog::createCommandDisplayWidget() QLabel* typeLabel = new QLabel("Type:"); m_commandTypeLabel = new QLabel(); - QLabel* valueOneLabel = new QLabel("Value:"); - m_commandValueOnePushButton = new QPushButton("Set..."); + m_commandValueOnePushButton = new QPushButton("Value..."); m_commandValueOnePushButton->setSizePolicy(QSizePolicy::Fixed, m_commandValueOnePushButton->sizePolicy().verticalPolicy()); QObject::connect(m_commandValueOnePushButton, &QPushButton::clicked, [=] { setMacroCommandValue(ValueIndex::ONE); }); m_commandValueOneLabel = new QLabel(); - QLabel* valueTwoLabel = new QLabel("Alt Value:"); - m_commandValueTwoPushButton = new QPushButton("Set..."); + m_commandValueTwoPushButton = new QPushButton("Alt Value..."); m_commandValueTwoPushButton->setSizePolicy(QSizePolicy::Fixed, m_commandValueTwoPushButton->sizePolicy().verticalPolicy()); QObject::connect(m_commandValueTwoPushButton, &QPushButton::clicked, [=] { setMacroCommandValue(ValueIndex::TWO); }); m_commandValueTwoLabel = new QLabel(); + QLabel* delayLabel = new QLabel("Delay:"); + m_commandDelaySpinBox = new QDoubleSpinBox(); + m_commandDelaySpinBox->setMinimum(0.0); + m_commandDelaySpinBox->setMaximum(1000.0); + m_commandDelaySpinBox->setSingleStep(1.0); + m_commandDelaySpinBox->setDecimals(1); + m_commandDelaySpinBox->setToolTip("Delay, in seconds, after running command"); + m_commandDelaySpinBox->setFixedWidth(150); + m_commandDelaySpinBox->setSuffix(" seconds"); + QObject::connect(m_commandDelaySpinBox, static_cast(&QDoubleSpinBox::valueChanged), + this, &WuQMacroDialog::macroCommandDelaySpinBoxValueChanged); + QLabel* toolTipLabel = new QLabel("ToolTip:"); - m_commandToolTip = new QLabel(); - m_commandToolTip->setWordWrap(true); + m_commandToolTipTextEdit = new QPlainTextEdit(); + m_commandToolTipTextEdit->setReadOnly(true); + m_commandToolTipTextEdit->setMaximumHeight(100); QGridLayout* layout = new QGridLayout(); layout->setContentsMargins(0, 0, 0, 0); @@ -497,24 +484,25 @@ WuQMacroDialog::createCommandDisplayWidget() layout->setColumnStretch(2, 100); int row = 0; layout->addWidget(titleLabel, row, 0); - layout->addWidget(m_commandTitleLabel, row, 1, 1, 2); + layout->addWidget(m_commandTitleLabel, row, 1); row++; - layout->addWidget(valueOneLabel, row, 0); - layout->addWidget(m_commandValueOnePushButton, row, 1); - layout->addWidget(m_commandValueOneLabel, row, 2); + layout->addWidget(m_commandValueOnePushButton, row, 0); + layout->addWidget(m_commandValueOneLabel, row, 1); row++; - layout->addWidget(valueTwoLabel, row, 0); - layout->addWidget(m_commandValueTwoPushButton, row, 1); - layout->addWidget(m_commandValueTwoLabel, row, 2); + layout->addWidget(m_commandValueTwoPushButton, row, 0); + layout->addWidget(m_commandValueTwoLabel, row, 1); row++; layout->addWidget(nameLabel, row, 0); - layout->addWidget(m_commandNameLabel, row, 1, 1, 2); + layout->addWidget(m_commandNameLabel, row, 1); row++; layout->addWidget(typeLabel, row, 0); - layout->addWidget(m_commandTypeLabel, row, 1, 1, 2); + layout->addWidget(m_commandTypeLabel, row, 1); + row++; + layout->addWidget(delayLabel, row, 0); + layout->addWidget(m_commandDelaySpinBox, row, 1); row++; - layout->addWidget(toolTipLabel, row, 0); - layout->addWidget(m_commandToolTip, row, 1, 1, 2); + layout->addWidget(toolTipLabel, row, 0, (Qt::AlignLeft | Qt::AlignTop)); + layout->addWidget(m_commandToolTipTextEdit, row, 1); row++; QHBoxLayout* titleLayout = new QHBoxLayout(); @@ -564,8 +552,6 @@ WuQMacroDialog::updateDialogContents() const WuQMacroExecutorOptions* runOptions = WuQMacroManager::instance()->getExecutorOptions(); CaretAssert(runOptions); - QSignalBlocker delaySpinBoxBlocker(m_runOptionDelayBetweenCommandsSpinBox); - m_runOptionDelayBetweenCommandsSpinBox->setValue(runOptions->getSecondsDelayBetweenCommands()); m_runOptionMoveMouseCheckBox->setChecked(runOptions->isShowMouseMovement()); m_runOptionLoopCheckBox->setChecked(runOptions->isLooping()); @@ -679,7 +665,7 @@ WuQMacroDialog::updateMacroWidget(WuQMacro* macro) m_macroNameLabel->setText(name); m_macroShortCutKeyComboBox->setSelectedShortCutKey(shortCutKey); - m_macroDescriptionLabel->setText(text); + m_macroDescriptionTextEdit->setPlainText(text); } /** @@ -764,12 +750,14 @@ WuQMacroDialog::updateCommandWidget(WuQMacroCommand* command) QString type; QString newDataTypeTwo; QString toolTip; + float delay(0.0f); if (command != NULL) { title = command->text(); name = command->getObjectName(); type = WuQMacroClassTypeEnum::toGuiName(command->getClassType()); newDataTypeTwo = WuQMacroDataValueTypeEnum::toGuiName(command->getDataTypeTwo()); toolTip = command->getObjectToolTip(); + delay = command->getDelayInSeconds(); } m_commandTitleLabel->setText(title); m_commandNameLabel->setText(name); @@ -782,7 +770,10 @@ WuQMacroDialog::updateCommandWidget(WuQMacroCommand* command) m_commandValueTwoLabel->setText(newDataValueTwo); m_commandValueTwoPushButton->setEnabled(true); } - m_commandToolTip->setText(toolTip); + QSignalBlocker delayBlocker(m_commandDelaySpinBox); + m_commandDelaySpinBox->setValue(delay); + + m_commandToolTipTextEdit->setPlainText(toolTip); } /** @@ -981,6 +972,25 @@ WuQMacroDialog::exportMacroGroupActionTriggered() } } +/** + * Called when macro commands delay value is changed + * + * @param value + * New value + */ +void +WuQMacroDialog::macroCommandDelaySpinBoxValueChanged(double value) +{ + WuQMacroCommand* command = getSelectedMacroCommand(); + CaretAssert(command); + command->setDelayInSeconds(value); + + WuQMacro* macro = getSelectedMacro(); + if (macro != NULL) { + WuQMacroManager::instance()->macroWasModified(macro); + } +} + /** * Set the command value for the given value index * diff --git a/src/GuiQt/WuQMacroDialog.h b/src/GuiQt/WuQMacroDialog.h index e064d24b6..5530033fd 100644 --- a/src/GuiQt/WuQMacroDialog.h +++ b/src/GuiQt/WuQMacroDialog.h @@ -37,8 +37,9 @@ class QDialogButtonBox; class QDoubleSpinBox; class QLabel; class QMenu; +class QPlainTextEdit; class QStackedWidget; -class QStandardItem; +class QStandardItem; class QTreeView; class QToolButton; @@ -85,8 +86,6 @@ namespace caret { void macroShortCutKeySelected(const WuQMacroShortCutKeyEnum::Enum); - void runOptionDelaySpinBoxValueChanged(float); - void runOptionMoveMouseCheckBoxClicked(bool); void runOptionLoopCheckBoxClicked(bool); @@ -101,6 +100,8 @@ namespace caret { void runMacroToolButtonClicked(); + void macroCommandDelaySpinBoxValueChanged(double); + private: enum class ValueIndex { ONE, @@ -162,7 +163,7 @@ namespace caret { WuQMacroShortCutKeyComboBox* m_macroShortCutKeyComboBox; - QLabel* m_macroDescriptionLabel; + QPlainTextEdit* m_macroDescriptionTextEdit; QWidget* m_macroWidget; @@ -176,8 +177,6 @@ namespace caret { QCheckBox* m_runOptionMoveMouseCheckBox; - QDoubleSpinBox* m_runOptionDelayBetweenCommandsSpinBox; - QLabel* m_commandTitleLabel; QLabel* m_commandTypeLabel; @@ -192,7 +191,9 @@ namespace caret { QLabel* m_commandValueTwoLabel; - QLabel* m_commandToolTip; + QDoubleSpinBox* m_commandDelaySpinBox; + + QPlainTextEdit* m_commandToolTipTextEdit; QToolButton* m_runMacroToolButton; diff --git a/src/GuiQt/WuQMacroExecutor.cxx b/src/GuiQt/WuQMacroExecutor.cxx index d0b1cf9bb..2f950652f 100644 --- a/src/GuiQt/WuQMacroExecutor.cxx +++ b/src/GuiQt/WuQMacroExecutor.cxx @@ -332,8 +332,8 @@ WuQMacroExecutor::runMacro(const WuQMacro* macro, QGuiApplication::processEvents(); if (mc->getClassType() != WuQMacroClassTypeEnum::MOUSE_USER_EVENT) { - if (m_runOptions.getSecondsDelayBetweenCommands() > 0.0) { - SystemUtilities::sleepSeconds(m_runOptions.getSecondsDelayBetweenCommands()); + if (mc->getDelayInSeconds() > 0.0) { + SystemUtilities::sleepSeconds(mc->getDelayInSeconds()); } } diff --git a/src/GuiQt/WuQMacroExecutorOptions.cxx b/src/GuiQt/WuQMacroExecutorOptions.cxx index f19e8d16f..09704f5b8 100644 --- a/src/GuiQt/WuQMacroExecutorOptions.cxx +++ b/src/GuiQt/WuQMacroExecutorOptions.cxx @@ -86,7 +86,6 @@ WuQMacroExecutorOptions::operator=(const WuQMacroExecutorOptions& obj) void WuQMacroExecutorOptions::copyHelperWuQMacroExecutorOptions(const WuQMacroExecutorOptions& obj) { - m_secondsDelayBetweenCommands = obj.m_secondsDelayBetweenCommands; m_loopingFlag = obj.m_loopingFlag; m_stopOnErrorFlag = obj.m_stopOnErrorFlag; m_showMouseMovementFlag = obj.m_showMouseMovementFlag; @@ -102,27 +101,6 @@ WuQMacroExecutorOptions::toString() const return "WuQMacroExecutorOptions"; } -/** - * @return Seconds delay between macro commands - */ -float -WuQMacroExecutorOptions::getSecondsDelayBetweenCommands() const -{ - return m_secondsDelayBetweenCommands; -} - -/** - * Set seconds delay between macro commands - * - * @parm seconds - * Number of seconds - */ -void -WuQMacroExecutorOptions::setSecondsDelayBetweenCommands(const float seconds) -{ - m_secondsDelayBetweenCommands = seconds; -} - /** * @return Show mouse movement while running macro */ diff --git a/src/GuiQt/WuQMacroExecutorOptions.h b/src/GuiQt/WuQMacroExecutorOptions.h index 948783165..8f18fff6c 100644 --- a/src/GuiQt/WuQMacroExecutorOptions.h +++ b/src/GuiQt/WuQMacroExecutorOptions.h @@ -42,10 +42,6 @@ namespace caret { WuQMacroExecutorOptions& operator=(const WuQMacroExecutorOptions& obj); - float getSecondsDelayBetweenCommands() const; - - void setSecondsDelayBetweenCommands(const float seconds); - bool isShowMouseMovement() const; void setShowMouseMovement(const bool status); @@ -65,8 +61,6 @@ namespace caret { private: void copyHelperWuQMacroExecutorOptions(const WuQMacroExecutorOptions& obj); - float m_secondsDelayBetweenCommands = 1.0f; - bool m_showMouseMovementFlag = true; bool m_stopOnErrorFlag = true; -- GitLab From 4a20550cace3c3bf7d10e47b2202fd199312bbf0 Mon Sep 17 00:00:00 2001 From: John Harwell Date: Wed, 16 Jan 2019 16:17:26 -0600 Subject: [PATCH 173/427] WB-834 Macros: Added a Stream XML writer for the Scene File that includes writing macros. The existing Scene File xml writer uses some older Caret classes for XML writing that were created prior to Qt's XML stream writer. In addition, some of the scene object classes were modified to add an additional enumerated type that indicates the type of scene object (array, map, or single). By using this enumerated type, the new scene file xml stream writer does not use any dynamic_cast's unlike the existing scene file writer that uses many dynamic casts. Classes for reading a scene file using an XML stream reader have been created but do not contain any code. implementing these classes is next. --- src/Common/WuQMacroGroupXmlStreamWriter.cxx | 44 +- src/Common/WuQMacroGroupXmlStreamWriter.h | 7 +- src/Files/CMakeLists.txt | 6 + src/Files/SceneFile.cxx | 12 + src/Files/SceneFileXmlStreamBase.cxx | 62 ++ src/Files/SceneFileXmlStreamBase.h | 89 +++ src/Files/SceneFileXmlStreamReader.cxx | 52 ++ src/Files/SceneFileXmlStreamReader.h | 58 ++ src/Files/SceneFileXmlStreamWriter.cxx | 222 ++++++++ src/Files/SceneFileXmlStreamWriter.h | 68 +++ src/Scenes/CMakeLists.txt | 14 + src/Scenes/SceneClass.cxx | 18 +- src/Scenes/SceneClass.h | 2 + src/Scenes/SceneClassArray.cxx | 12 + src/Scenes/SceneClassArray.h | 2 + src/Scenes/SceneEnumeratedType.cxx | 13 + src/Scenes/SceneEnumeratedType.h | 2 + src/Scenes/SceneEnumeratedTypeArray.cxx | 11 + src/Scenes/SceneEnumeratedTypeArray.h | 2 + src/Scenes/SceneInfoXmlStreamBase.cxx | 62 ++ src/Scenes/SceneInfoXmlStreamBase.h | 81 +++ src/Scenes/SceneInfoXmlStreamReader.cxx | 52 ++ src/Scenes/SceneInfoXmlStreamReader.h | 58 ++ src/Scenes/SceneInfoXmlStreamWriter.cxx | 125 ++++ src/Scenes/SceneInfoXmlStreamWriter.h | 68 +++ src/Scenes/SceneObject.cxx | 88 ++- src/Scenes/SceneObject.h | 26 + src/Scenes/SceneObjectArray.cxx | 61 ++ src/Scenes/SceneObjectArray.h | 14 + src/Scenes/SceneObjectContainerTypeEnum.cxx | 377 +++++++++++++ src/Scenes/SceneObjectContainerTypeEnum.h | 106 ++++ src/Scenes/SceneObjectMapIntegerKey.cxx | 15 +- src/Scenes/SceneObjectMapIntegerKey.h | 2 + src/Scenes/ScenePathName.cxx | 18 +- src/Scenes/ScenePathName.h | 2 + src/Scenes/ScenePathNameArray.cxx | 12 + src/Scenes/ScenePathNameArray.h | 2 + src/Scenes/ScenePrimitive.cxx | 16 +- src/Scenes/ScenePrimitive.h | 2 + src/Scenes/ScenePrimitiveArray.cxx | 12 + src/Scenes/ScenePrimitiveArray.h | 2 + src/Scenes/SceneXmlStreamBase.cxx | 62 ++ src/Scenes/SceneXmlStreamBase.h | 125 ++++ src/Scenes/SceneXmlStreamReader.cxx | 52 ++ src/Scenes/SceneXmlStreamReader.h | 58 ++ src/Scenes/SceneXmlStreamWriter.cxx | 595 ++++++++++++++++++++ src/Scenes/SceneXmlStreamWriter.h | 103 ++++ 47 files changed, 2875 insertions(+), 17 deletions(-) create mode 100644 src/Files/SceneFileXmlStreamBase.cxx create mode 100644 src/Files/SceneFileXmlStreamBase.h create mode 100644 src/Files/SceneFileXmlStreamReader.cxx create mode 100644 src/Files/SceneFileXmlStreamReader.h create mode 100644 src/Files/SceneFileXmlStreamWriter.cxx create mode 100644 src/Files/SceneFileXmlStreamWriter.h create mode 100644 src/Scenes/SceneInfoXmlStreamBase.cxx create mode 100644 src/Scenes/SceneInfoXmlStreamBase.h create mode 100644 src/Scenes/SceneInfoXmlStreamReader.cxx create mode 100644 src/Scenes/SceneInfoXmlStreamReader.h create mode 100644 src/Scenes/SceneInfoXmlStreamWriter.cxx create mode 100644 src/Scenes/SceneInfoXmlStreamWriter.h create mode 100644 src/Scenes/SceneObjectContainerTypeEnum.cxx create mode 100644 src/Scenes/SceneObjectContainerTypeEnum.h create mode 100644 src/Scenes/SceneXmlStreamBase.cxx create mode 100644 src/Scenes/SceneXmlStreamBase.h create mode 100644 src/Scenes/SceneXmlStreamReader.cxx create mode 100644 src/Scenes/SceneXmlStreamReader.h create mode 100644 src/Scenes/SceneXmlStreamWriter.cxx create mode 100644 src/Scenes/SceneXmlStreamWriter.h diff --git a/src/Common/WuQMacroGroupXmlStreamWriter.cxx b/src/Common/WuQMacroGroupXmlStreamWriter.cxx index f1252ee15..37f851eb3 100644 --- a/src/Common/WuQMacroGroupXmlStreamWriter.cxx +++ b/src/Common/WuQMacroGroupXmlStreamWriter.cxx @@ -58,22 +58,27 @@ WuQMacroGroupXmlStreamWriter::~WuQMacroGroupXmlStreamWriter() } /** - * Constructor for writing the macro group to a text string + * Wite the given macro group to the given xml writer * + * @param xmlWriter + * The XML stream writer * @param macroGroup * Macro group that is written to XML - * @param contentTextString - * Pointer to string to which XML is written */ void -WuQMacroGroupXmlStreamWriter::writeToString(const WuQMacroGroup* macroGroup, - QString& contentTextString) +WuQMacroGroupXmlStreamWriter::writeXml(QXmlStreamWriter* xmlWriter, + const WuQMacroGroup* macroGroup) { + CaretAssert(xmlWriter); CaretAssert(macroGroup); - contentTextString.clear(); - m_xmlStreamWriter.reset(new QXmlStreamWriter(&contentTextString)); - m_xmlStreamWriter->setAutoFormatting(true); + if (macroGroup->getNumberOfMacros() <= 0) { + xmlWriter->writeEmptyElement(ELEMENT_MACRO_GROUP); + return; + } + + m_xmlStreamWriter = xmlWriter; + m_xmlStreamWriter->writeStartElement(ELEMENT_MACRO_GROUP); m_xmlStreamWriter->writeAttribute(ATTRIBUTE_NAME, macroGroup->getName()); m_xmlStreamWriter->writeAttribute(ATTRIBUTE_VERSION, "1"); @@ -88,7 +93,28 @@ WuQMacroGroupXmlStreamWriter::writeToString(const WuQMacroGroup* macroGroup, } m_xmlStreamWriter->writeEndElement(); - m_xmlStreamWriter.reset(); + m_xmlStreamWriter = NULL; +} + +/** + * Write the macro group to a text string + * + * @param macroGroup + * Macro group that is written to XML + * @param contentTextString + * Pointer to string to which XML is written + */ +void +WuQMacroGroupXmlStreamWriter::writeToString(const WuQMacroGroup* macroGroup, + QString& contentTextString) +{ + CaretAssert(macroGroup); + contentTextString.clear(); + + std::unique_ptr xmlWriter(new QXmlStreamWriter(&contentTextString)); + xmlWriter->setAutoFormatting(true); + writeXml(xmlWriter.get(), + macroGroup); } diff --git a/src/Common/WuQMacroGroupXmlStreamWriter.h b/src/Common/WuQMacroGroupXmlStreamWriter.h index c79b2821d..48ea6ebd5 100644 --- a/src/Common/WuQMacroGroupXmlStreamWriter.h +++ b/src/Common/WuQMacroGroupXmlStreamWriter.h @@ -42,7 +42,10 @@ namespace caret { WuQMacroGroupXmlStreamWriter(); virtual ~WuQMacroGroupXmlStreamWriter(); - + + void writeXml(QXmlStreamWriter* xmlStreamWriter, + const WuQMacroGroup* macroGroup); + void writeToString(const WuQMacroGroup* macroGroup, QString& contentTextString); @@ -60,7 +63,7 @@ namespace caret { void writeMacroMouseEventInfo(const WuQMacroMouseEventInfo* mouseEventInfo); - std::unique_ptr m_xmlStreamWriter; + QXmlStreamWriter* m_xmlStreamWriter; // ADD_NEW_MEMBERS_HERE diff --git a/src/Files/CMakeLists.txt b/src/Files/CMakeLists.txt index 9305f06e8..d94a498ab 100755 --- a/src/Files/CMakeLists.txt +++ b/src/Files/CMakeLists.txt @@ -119,6 +119,9 @@ RibbonMappingHelper.h SceneDataFileInfo.h SceneFile.h SceneFileSaxReader.h +SceneFileXmlStreamBase.h +SceneFileXmlStreamReader.h +SceneFileXmlStreamWriter.h SignedDistanceHelper.h SparseVolumeIndexer.h SpecFile.h @@ -248,6 +251,9 @@ RibbonMappingHelper.cxx SceneDataFileInfo.cxx SceneFile.cxx SceneFileSaxReader.cxx +SceneFileXmlStreamBase.cxx +SceneFileXmlStreamReader.cxx +SceneFileXmlStreamWriter.cxx SignedDistanceHelper.cxx SparseVolumeIndexer.cxx SpecFile.cxx diff --git a/src/Files/SceneFile.cxx b/src/Files/SceneFile.cxx index 31864d136..51b3eec03 100644 --- a/src/Files/SceneFile.cxx +++ b/src/Files/SceneFile.cxx @@ -45,6 +45,7 @@ #include "SceneInfo.h" #include "ScenePathName.h" #include "SceneXmlElements.h" +#include "SceneFileXmlStreamWriter.h" #include "SceneWriterXml.h" #include "SpecFile.h" #include "SystemUtilities.h" @@ -787,6 +788,17 @@ SceneFile::writeFile(const AString& filename) catch (const XmlException& e) { throw DataFileException(e); } + + try { + CaretAssertToDoWarning(); + SceneFileXmlStreamWriter xmlStreamWriter; + xmlStreamWriter.writeFile(this); + } + catch (const DataFileException& e) { + const QString msg("Xml Stream Writer failed: " + + e.whatString()); + throw DataFileException(msg); + } } /** diff --git a/src/Files/SceneFileXmlStreamBase.cxx b/src/Files/SceneFileXmlStreamBase.cxx new file mode 100644 index 000000000..9b0a191f8 --- /dev/null +++ b/src/Files/SceneFileXmlStreamBase.cxx @@ -0,0 +1,62 @@ + +/*LICENSE_START*/ +/* + * Copyright (C) 2019 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + +#define __SCENE_FILE_XML_STREAM_BASE_DECLARE__ +#include "SceneFileXmlStreamBase.h" +#undef __SCENE_FILE_XML_STREAM_BASE_DECLARE__ + +#include "CaretAssert.h" +using namespace caret; + + + +/** + * \class caret::SceneFileXmlStreamBase + * \brief Base class for Scene File XML Stream Reader and Writer + * \ingroup Files + */ + +/** + * Constructor. + */ +SceneFileXmlStreamBase::SceneFileXmlStreamBase() +: CaretObject() +{ + +} + +/** + * Destructor. + */ +SceneFileXmlStreamBase::~SceneFileXmlStreamBase() +{ +} + +/** + * Get a description of this object's content. + * @return String describing this object's content. + */ +AString +SceneFileXmlStreamBase::toString() const +{ + return "SceneFileXmlStreamBase"; +} + diff --git a/src/Files/SceneFileXmlStreamBase.h b/src/Files/SceneFileXmlStreamBase.h new file mode 100644 index 000000000..9b7543759 --- /dev/null +++ b/src/Files/SceneFileXmlStreamBase.h @@ -0,0 +1,89 @@ +#ifndef __SCENE_FILE_XML_STREAM_BASE_H__ +#define __SCENE_FILE_XML_STREAM_BASE_H__ + +/*LICENSE_START*/ +/* + * Copyright (C) 2019 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + + + +#include + +#include "CaretObject.h" + + + +namespace caret { + + class SceneFileXmlStreamBase : public CaretObject { + + public: + SceneFileXmlStreamBase(); + + virtual ~SceneFileXmlStreamBase(); + + SceneFileXmlStreamBase(const SceneFileXmlStreamBase&) = delete; + + SceneFileXmlStreamBase& operator=(const SceneFileXmlStreamBase&) = delete; + + static const QString ELEMENT_SCENE_FILE; + + static const QString ATTRIBUTE_SCENE_FILE_VERSION; + + static const QString ELEMENT_SCENE_FILE_INFO_DIRECTORY; + + static const QString ELEMENT_SCENE_FILE_BALSA_STUDY_ID; + + static const QString ELEMENT_SCENE_FILE_BALSA_STUDY_TITLE; + + static const QString ELEMENT_SCENE_FILE_BALSA_BASE_DIRECTORY; + + static const QString ELEMENT_SCENE_FILE_BALSA_EXTRACT_TO_DIRECTORY; + + static const QString ELEMENT_SCENE_FILE_BALSA_BASE_PATH_TYPE; + + // ADD_NEW_METHODS_HERE + + virtual AString toString() const; + + private: + // ADD_NEW_MEMBERS_HERE + + }; + +#ifdef __SCENE_FILE_XML_STREAM_BASE_DECLARE__ + const QString SceneFileXmlStreamBase::ELEMENT_SCENE_FILE = "SceneFile"; + + const QString SceneFileXmlStreamBase::ATTRIBUTE_SCENE_FILE_VERSION = "Version"; + + const QString SceneFileXmlStreamBase::ELEMENT_SCENE_FILE_INFO_DIRECTORY = "SceneInfoDirectory"; + + const QString SceneFileXmlStreamBase::ELEMENT_SCENE_FILE_BALSA_STUDY_ID = "BalsaStudyID"; + + const QString SceneFileXmlStreamBase::ELEMENT_SCENE_FILE_BALSA_STUDY_TITLE = "BalsaStudyTitle"; + + const QString SceneFileXmlStreamBase::ELEMENT_SCENE_FILE_BALSA_BASE_DIRECTORY = "BalsaBaseDirectory"; + + const QString SceneFileXmlStreamBase::ELEMENT_SCENE_FILE_BALSA_EXTRACT_TO_DIRECTORY = "BalsaExtractToDirectory"; + + const QString SceneFileXmlStreamBase::ELEMENT_SCENE_FILE_BALSA_BASE_PATH_TYPE = "BasePathType"; +#endif // __SCENE_FILE_XML_STREAM_BASE_DECLARE__ + +} // namespace +#endif //__SCENE_FILE_XML_STREAM_BASE_H__ diff --git a/src/Files/SceneFileXmlStreamReader.cxx b/src/Files/SceneFileXmlStreamReader.cxx new file mode 100644 index 000000000..117fa80eb --- /dev/null +++ b/src/Files/SceneFileXmlStreamReader.cxx @@ -0,0 +1,52 @@ + +/*LICENSE_START*/ +/* + * Copyright (C) 2019 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + +#define __SCENE_FILE_XML_STREAM_READER_DECLARE__ +#include "SceneFileXmlStreamReader.h" +#undef __SCENE_FILE_XML_STREAM_READER_DECLARE__ + +#include "CaretAssert.h" +using namespace caret; + + + +/** + * \class caret::SceneFileXmlStreamReader + * \brief XML Stream Writer for Scene File + * \ingroup Files + */ + +/** + * Constructor. + */ +SceneFileXmlStreamReader::SceneFileXmlStreamReader() +: SceneFileXmlStreamBase() +{ + +} + +/** + * Destructor. + */ +SceneFileXmlStreamReader::~SceneFileXmlStreamReader() +{ +} + diff --git a/src/Files/SceneFileXmlStreamReader.h b/src/Files/SceneFileXmlStreamReader.h new file mode 100644 index 000000000..21d9fc783 --- /dev/null +++ b/src/Files/SceneFileXmlStreamReader.h @@ -0,0 +1,58 @@ +#ifndef __SCENE_FILE_XML_STREAM_READER_H__ +#define __SCENE_FILE_XML_STREAM_READER_H__ + +/*LICENSE_START*/ +/* + * Copyright (C) 2019 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + + + +#include + +#include "SceneFileXmlStreamBase.h" + + + +namespace caret { + + class SceneFileXmlStreamReader : public SceneFileXmlStreamBase { + + public: + SceneFileXmlStreamReader(); + + virtual ~SceneFileXmlStreamReader(); + + SceneFileXmlStreamReader(const SceneFileXmlStreamReader&) = delete; + + SceneFileXmlStreamReader& operator=(const SceneFileXmlStreamReader&) = delete; + + + // ADD_NEW_METHODS_HERE + + private: + // ADD_NEW_MEMBERS_HERE + + }; + +#ifdef __SCENE_FILE_XML_STREAM_READER_DECLARE__ + // +#endif // __SCENE_FILE_XML_STREAM_READER_DECLARE__ + +} // namespace +#endif //__SCENE_FILE_XML_STREAM_READER_H__ diff --git a/src/Files/SceneFileXmlStreamWriter.cxx b/src/Files/SceneFileXmlStreamWriter.cxx new file mode 100644 index 000000000..f5a379bc9 --- /dev/null +++ b/src/Files/SceneFileXmlStreamWriter.cxx @@ -0,0 +1,222 @@ + +/*LICENSE_START*/ +/* + * Copyright (C) 2019 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + +#define __SCENE_FILE_XML_STREAM_WRITER_DECLARE__ +#include "SceneFileXmlStreamWriter.h" +#undef __SCENE_FILE_XML_STREAM_WRITER_DECLARE__ + +#include +#include + +#include "CaretAssert.h" +#include "DataFileException.h" +#include "FileInformation.h" +#include "GiftiMetaData.h" +#include "Scene.h" +#include "SceneFile.h" +#include "SceneInfoXmlStreamWriter.h" +#include "ScenePathName.h" +#include "SceneXmlStreamWriter.h" + +using namespace caret; + + + +/** + * \class caret::SceneFileXmlStreamWriter + * \brief XML Stream reader for Scene File + * \ingroup Files + */ + +/** + * Constructor. + */ +SceneFileXmlStreamWriter::SceneFileXmlStreamWriter() +: SceneFileXmlStreamBase() +{ + +} + +/** + * Destructor. + */ +SceneFileXmlStreamWriter::~SceneFileXmlStreamWriter() +{ +} + +/** + * Write the given Scene File in XML format. Name of the file + * is obtained from the file. + * + * @param sceneFile + * Scene File written in XML format. + * @throws + * DataFileException if there is an error writing the file. + */ +void +SceneFileXmlStreamWriter::writeFile(const SceneFile* sceneFile) +{ + CaretAssert(sceneFile); + + const QString filename = "TestSceneXmlStream.scene"; //sceneFile->getFileName(); + if (filename.isEmpty()) { + throw DataFileException("Name for writing annotation file is empty."); + } + + QFile file(filename); + if ( ! file.open(QFile::WriteOnly)) { + throw DataFileException(filename, + "Error opening for writing: " + + file.errorString()); + } + + QXmlStreamWriter xmlWriter(&file); + writeFileContentToXmlStreamWriter(xmlWriter, + sceneFile, + filename); + + file.close(); + + if (xmlWriter.hasError()) { + throw DataFileException(filename, + ("Unknown error writing file " + + sceneFile->getFileNameNoPath())); + } +} + +/** + * Write the scene files content to the XML stream. + * + * @param xmlWriter + * The XML stream writer + * @param sceneFile + * The scene file + * @param sceneFileName + * Name of the file. + */ +void +SceneFileXmlStreamWriter::writeFileContentToXmlStreamWriter(QXmlStreamWriter& xmlWriter, + const SceneFile* sceneFile, + const AString& sceneFileName) +{ + CaretAssert(sceneFile); + + const AString versionString = AString::number(SceneFile::getFileVersion(), + 'f', + 1); + + xmlWriter.setAutoFormatting(true); + + xmlWriter.writeStartDocument(); + + xmlWriter.writeStartElement(ELEMENT_SCENE_FILE); + xmlWriter.writeAttribute(ATTRIBUTE_SCENE_FILE_VERSION, + versionString); + + const GiftiMetaData* metaData = sceneFile->getFileMetaData(); + if ( ! metaData->isEmpty()) { + metaData->writeCiftiXML1(xmlWriter); + } + + writeSceneInfoDirectory(xmlWriter, + sceneFile, + sceneFileName); + + const int32_t numberOfScenes = sceneFile->getNumberOfScenes(); + for (int32_t sceneIndex = 0; sceneIndex < numberOfScenes; sceneIndex++) { + SceneXmlStreamWriter sceneXmlWriter; + sceneXmlWriter.writeXML(&xmlWriter, + sceneFile->getSceneAtIndex(sceneIndex), + sceneIndex, + sceneFileName); + } + + xmlWriter.writeEndElement(); + + xmlWriter.writeEndDocument(); +} + +/** + * Write the scene info directory to the XML stream + * + * @param xmlWriter + * The XML stream writer + * @param sceneFile + * The scene file + * @param sceneFileName + * Name of the file. + */ +void +SceneFileXmlStreamWriter::writeSceneInfoDirectory(QXmlStreamWriter& xmlWriter, + const SceneFile* sceneFile, + const AString& sceneFileName) +{ + xmlWriter.writeStartElement(ELEMENT_SCENE_FILE_INFO_DIRECTORY); + + xmlWriter.writeTextElement(ELEMENT_SCENE_FILE_BALSA_STUDY_ID, + sceneFile->getBalsaStudyID()); + xmlWriter.writeTextElement(ELEMENT_SCENE_FILE_BALSA_STUDY_TITLE, + sceneFile->getBalsaStudyTitle()); + + AString relativeBasePath(""); + switch (sceneFile->getBasePathType()) { + case SceneFileBasePathTypeEnum::AUTOMATIC: + break; + case SceneFileBasePathTypeEnum::CUSTOM: + { + /* + * Write base path as a path RELATIVE to the scene file + * but only when base path type is CUSTOM + * Note: we do not use FileInformation::getCanonicalFilePath() + * because it returns an empty string if the file DOES NOT exist + * and this may occur since the file may be new and has not + * been closed. + */ + if ( ! sceneFile->getBalsaCustomBaseDirectory().isEmpty()) { + const AString baseDirAbsPath = FileInformation(sceneFile->getBalsaCustomBaseDirectory()).getAbsoluteFilePath(); + const AString sceneFileAbsPath = FileInformation(sceneFileName).getAbsoluteFilePath(); + ScenePathName basePathName("basePathName", + baseDirAbsPath); + + relativeBasePath = basePathName.getRelativePathToSceneFile(sceneFileAbsPath); + } + } + break; + } + xmlWriter.writeTextElement(ELEMENT_SCENE_FILE_BALSA_BASE_DIRECTORY, + relativeBasePath); + + + xmlWriter.writeTextElement(ELEMENT_SCENE_FILE_BALSA_EXTRACT_TO_DIRECTORY, + sceneFile->getBalsaExtractToDirectoryName()); + xmlWriter.writeTextElement(ELEMENT_SCENE_FILE_BALSA_BASE_PATH_TYPE, + SceneFileBasePathTypeEnum::toName(sceneFile->getBasePathType())); + + const int32_t numberOfScenes = sceneFile->getNumberOfScenes(); + for (int32_t sceneIndex = 0; sceneIndex < numberOfScenes; sceneIndex++) { + SceneInfoXmlStreamWriter infoXmlWriter; + infoXmlWriter.writeXML(&xmlWriter, + sceneFile->getSceneAtIndex(sceneIndex)->getSceneInfo(), + sceneIndex); + } + + xmlWriter.writeEndElement(); +} diff --git a/src/Files/SceneFileXmlStreamWriter.h b/src/Files/SceneFileXmlStreamWriter.h new file mode 100644 index 000000000..73a123af4 --- /dev/null +++ b/src/Files/SceneFileXmlStreamWriter.h @@ -0,0 +1,68 @@ +#ifndef __SCENE_FILE_XML_STREAM_WRITER_H__ +#define __SCENE_FILE_XML_STREAM_WRITER_H__ + +/*LICENSE_START*/ +/* + * Copyright (C) 2019 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + + + +#include + +#include "SceneFileXmlStreamBase.h" + +class QXmlStreamWriter; + +namespace caret { + class SceneFile; + + class SceneFileXmlStreamWriter : public SceneFileXmlStreamBase { + + public: + SceneFileXmlStreamWriter(); + + virtual ~SceneFileXmlStreamWriter(); + + SceneFileXmlStreamWriter(const SceneFileXmlStreamWriter&) = delete; + + SceneFileXmlStreamWriter& operator=(const SceneFileXmlStreamWriter&) = delete; + + void writeFile(const SceneFile* sceneFile); + + // ADD_NEW_METHODS_HERE + + private: + void writeFileContentToXmlStreamWriter(QXmlStreamWriter& xmlWriter, + const SceneFile* sceneFile, + const AString& sceneFileName); + + void writeSceneInfoDirectory(QXmlStreamWriter& xmlWriter, + const SceneFile* sceneFile, + const AString& sceneFileName); + + // ADD_NEW_MEMBERS_HERE + + }; + +#ifdef __SCENE_FILE_XML_STREAM_WRITER_DECLARE__ + // +#endif // __SCENE_FILE_XML_STREAM_WRITER_DECLARE__ + +} // namespace +#endif //__SCENE_FILE_XML_STREAM_WRITER_H__ diff --git a/src/Scenes/CMakeLists.txt b/src/Scenes/CMakeLists.txt index e4e0235fa..abc7defab 100644 --- a/src/Scenes/CMakeLists.txt +++ b/src/Scenes/CMakeLists.txt @@ -50,10 +50,14 @@ SceneFloat.h SceneFloatArray.h SceneInfo.h SceneInfoSaxReader.h +SceneInfoXmlStreamBase.h +SceneInfoXmlStreamReader.h +SceneInfoXmlStreamWriter.h SceneInteger.h SceneIntegerArray.h SceneObject.h SceneObjectArray.h +SceneObjectContainerTypeEnum.h SceneObjectDataTypeEnum.h SceneObjectMapIntegerKey.h ScenePathName.h @@ -69,6 +73,9 @@ SceneUnsignedByteArray.h SceneWriterInterface.h SceneWriterXml.h SceneXmlElements.h +SceneXmlStreamBase.h +SceneXmlStreamReader.h +SceneXmlStreamWriter.h SceneableInterface.h BackgroundAndForegroundColorsSceneHelper.cxx @@ -87,10 +94,14 @@ SceneFloat.cxx SceneFloatArray.cxx SceneInfo.cxx SceneInfoSaxReader.cxx +SceneInfoXmlStreamBase.cxx +SceneInfoXmlStreamReader.cxx +SceneInfoXmlStreamWriter.cxx SceneInteger.cxx SceneIntegerArray.cxx SceneObject.cxx SceneObjectArray.cxx +SceneObjectContainerTypeEnum.cxx SceneObjectDataTypeEnum.cxx SceneObjectMapIntegerKey.cxx ScenePathName.cxx @@ -104,6 +115,9 @@ SceneTypeEnum.cxx SceneUnsignedByte.cxx SceneUnsignedByteArray.cxx SceneWriterXml.cxx +SceneXmlStreamBase.cxx +SceneXmlStreamReader.cxx +SceneXmlStreamWriter.cxx ) TARGET_LINK_LIBRARIES(Scenes ${CARET_QT5_LINK}) diff --git a/src/Scenes/SceneClass.cxx b/src/Scenes/SceneClass.cxx index 36a60414e..1525cbb66 100644 --- a/src/Scenes/SceneClass.cxx +++ b/src/Scenes/SceneClass.cxx @@ -70,6 +70,7 @@ SceneClass::SceneClass(const AString& name, const AString& className, const int32_t versionNumber) : SceneObject(name, + SceneObjectContainerTypeEnum::SINGLE, SceneObjectDataTypeEnum::SCENE_CLASS), m_className(className), m_versionNumber(versionNumber) @@ -77,7 +78,10 @@ SceneClass::SceneClass(const AString& name, } -SceneClass::SceneClass(const SceneClass& rhs): SceneObject(rhs.getName(), SceneObjectDataTypeEnum::SCENE_CLASS), +SceneClass::SceneClass(const SceneClass& rhs) +: SceneObject(rhs.getName(), + SceneObjectContainerTypeEnum::SINGLE, + SceneObjectDataTypeEnum::SCENE_CLASS), m_className(rhs.m_className), m_versionNumber(rhs.m_versionNumber) { @@ -105,6 +109,18 @@ SceneClass::~SceneClass() m_childObjects.clear(); } +/** + * Cast an instance of SceneObject to a SceneClass. + * Is used to avoid dynamic casting and overridden by the class. + * + * @return Valid pointer (non-NULL) this is SceneClass + */ +const SceneClass* +SceneClass::castToSceneClass() const +{ + return this; +} + /** * @return Name of the class (NOT the instance). */ diff --git a/src/Scenes/SceneClass.h b/src/Scenes/SceneClass.h index 758cd4c5a..6f79ece93 100644 --- a/src/Scenes/SceneClass.h +++ b/src/Scenes/SceneClass.h @@ -46,6 +46,8 @@ namespace caret { virtual ~SceneClass(); + virtual const SceneClass* castToSceneClass() const; + AString getClassName() const; int32_t getVersionNumber() const; diff --git a/src/Scenes/SceneClassArray.cxx b/src/Scenes/SceneClassArray.cxx index 6af530ff8..051601456 100644 --- a/src/Scenes/SceneClassArray.cxx +++ b/src/Scenes/SceneClassArray.cxx @@ -116,6 +116,18 @@ SceneClassArray::~SceneClassArray() m_values.clear(); } +/** + * Cast an instance of SceneObjectArray to a SceneClassArray. + * Is used to avoid dynamic casting and overridden by the class. + * + * @return Valid pointer (non-NULL) this is SceneClass + */ +const SceneClassArray* +SceneClassArray::castToSceneClassArray() const +{ + return this; +} + /** * @return All descendant SceneClasses (children, grandchildren, etc.) of this instance. */ diff --git a/src/Scenes/SceneClassArray.h b/src/Scenes/SceneClassArray.h index 3ee4bb0ea..c98a5b80d 100644 --- a/src/Scenes/SceneClassArray.h +++ b/src/Scenes/SceneClassArray.h @@ -45,6 +45,8 @@ namespace caret { virtual ~SceneClassArray(); + virtual const SceneClassArray* castToSceneClassArray() const; + void setClassAtIndex(const int32_t arrayIndex, SceneClass* sceneClass); diff --git a/src/Scenes/SceneEnumeratedType.cxx b/src/Scenes/SceneEnumeratedType.cxx index 47aff4439..b1571ace5 100644 --- a/src/Scenes/SceneEnumeratedType.cxx +++ b/src/Scenes/SceneEnumeratedType.cxx @@ -41,6 +41,7 @@ using namespace caret; SceneEnumeratedType::SceneEnumeratedType(const AString& name, const AString& enumeratedValueAsString) : SceneObject(name, + SceneObjectContainerTypeEnum::SINGLE, SceneObjectDataTypeEnum::SCENE_ENUMERATED_TYPE) { m_enumeratedValueAsString = enumeratedValueAsString; @@ -54,6 +55,18 @@ SceneEnumeratedType::~SceneEnumeratedType() } +/** + * Cast an instance of SceneObject to a SceneEnumeratedType. + * Is used to avoid dynamic casting and overridden by the class. + * + * @return Valid pointer (non-NULL) this is SceneEnumeratedType + */ +const SceneEnumeratedType* +SceneEnumeratedType::castToSceneEnumeratedType() const +{ + return this; +} + /** * @param enumeratedValueAsString * New value. diff --git a/src/Scenes/SceneEnumeratedType.h b/src/Scenes/SceneEnumeratedType.h index f1602b085..78063952b 100644 --- a/src/Scenes/SceneEnumeratedType.h +++ b/src/Scenes/SceneEnumeratedType.h @@ -34,6 +34,8 @@ namespace caret { virtual ~SceneEnumeratedType(); + virtual const SceneEnumeratedType* castToSceneEnumeratedType() const; + void setValue(const AString& enumeratedValueAsString); AString stringValue() const; diff --git a/src/Scenes/SceneEnumeratedTypeArray.cxx b/src/Scenes/SceneEnumeratedTypeArray.cxx index 6fc829deb..4bbf06924 100644 --- a/src/Scenes/SceneEnumeratedTypeArray.cxx +++ b/src/Scenes/SceneEnumeratedTypeArray.cxx @@ -109,6 +109,17 @@ SceneEnumeratedTypeArray::~SceneEnumeratedTypeArray() } +/** + * Cast an instance of SceneObjectArray to a SceneEnumeratedTypeArray. + * Is used to avoid dynamic casting and overridden by the class. + * + * @return Valid pointer (non-NULL) this is SceneClass + */ +const SceneEnumeratedTypeArray* +SceneEnumeratedTypeArray::castToSceneEnumeratedTypeArray() const +{ + return this; +} /** * Set a value. diff --git a/src/Scenes/SceneEnumeratedTypeArray.h b/src/Scenes/SceneEnumeratedTypeArray.h index b088cccf8..95c2fd70d 100644 --- a/src/Scenes/SceneEnumeratedTypeArray.h +++ b/src/Scenes/SceneEnumeratedTypeArray.h @@ -43,6 +43,8 @@ namespace caret { virtual ~SceneEnumeratedTypeArray(); + virtual const SceneEnumeratedTypeArray* castToSceneEnumeratedTypeArray() const; + void setValue(const int32_t arrayIndex, const AString enumeratedValueAsString); diff --git a/src/Scenes/SceneInfoXmlStreamBase.cxx b/src/Scenes/SceneInfoXmlStreamBase.cxx new file mode 100644 index 000000000..4f587d99c --- /dev/null +++ b/src/Scenes/SceneInfoXmlStreamBase.cxx @@ -0,0 +1,62 @@ + +/*LICENSE_START*/ +/* + * Copyright (C) 2019 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + +#define __SCENE_INFO_XML_STREAM_BASE_DECLARE__ +#include "SceneInfoXmlStreamBase.h" +#undef __SCENE_INFO_XML_STREAM_BASE_DECLARE__ + +#include "CaretAssert.h" +using namespace caret; + + + +/** + * \class caret::SceneInfoXmlStreamBase + * \brief Base class for SceneInfo XML Stream Reader and Writer + * \ingroup Scenes + */ + +/** + * Constructor. + */ +SceneInfoXmlStreamBase::SceneInfoXmlStreamBase() +: CaretObject() +{ + +} + +/** + * Destructor. + */ +SceneInfoXmlStreamBase::~SceneInfoXmlStreamBase() +{ +} + +/** + * Get a description of this object's content. + * @return String describing this object's content. + */ +AString +SceneInfoXmlStreamBase::toString() const +{ + return "SceneInfoXmlStreamBase"; +} + diff --git a/src/Scenes/SceneInfoXmlStreamBase.h b/src/Scenes/SceneInfoXmlStreamBase.h new file mode 100644 index 000000000..055d16df2 --- /dev/null +++ b/src/Scenes/SceneInfoXmlStreamBase.h @@ -0,0 +1,81 @@ +#ifndef __SCENE_INFO_XML_STREAM_BASE_H__ +#define __SCENE_INFO_XML_STREAM_BASE_H__ + +/*LICENSE_START*/ +/* + * Copyright (C) 2019 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + + + +#include + +#include "CaretObject.h" + + + +namespace caret { + + class SceneInfoXmlStreamBase : public CaretObject { + + public: + SceneInfoXmlStreamBase(); + + virtual ~SceneInfoXmlStreamBase(); + + SceneInfoXmlStreamBase(const SceneInfoXmlStreamBase&) = delete; + + SceneInfoXmlStreamBase& operator=(const SceneInfoXmlStreamBase&) = delete; + + static const AString ELEMENT_BALSA_SCENE_ID; + static const AString ELEMENT_DESCRIPTION; + static const AString ELEMENT_IMAGE; + static const AString ELEMENT_NAME; + static const AString ELEMENT_SCENE_INFO; + + static const AString ATTRIBUTE_ENCODING; + static const AString ATTRIBUTE_FORMAT; + static const AString ATTRIBUTE_INDEX; + + static const AString VALUE_ENCODING_BASE64; + + // ADD_NEW_METHODS_HERE + + virtual AString toString() const; + + private: + // ADD_NEW_MEMBERS_HERE + + }; + +#ifdef __SCENE_INFO_XML_STREAM_BASE_DECLARE__ + const AString SceneInfoXmlStreamBase::ELEMENT_BALSA_SCENE_ID = "BalsaSceneID"; + const AString SceneInfoXmlStreamBase::ELEMENT_DESCRIPTION = "Description"; + const AString SceneInfoXmlStreamBase::ELEMENT_IMAGE = "Image"; + const AString SceneInfoXmlStreamBase::ELEMENT_NAME = "Name"; + const AString SceneInfoXmlStreamBase::ELEMENT_SCENE_INFO = "SceneInfo"; + + const AString SceneInfoXmlStreamBase::ATTRIBUTE_ENCODING = "Encoding"; + const AString SceneInfoXmlStreamBase::ATTRIBUTE_FORMAT = "Format"; + const AString SceneInfoXmlStreamBase::ATTRIBUTE_INDEX = "Index"; + + const AString SceneInfoXmlStreamBase::VALUE_ENCODING_BASE64 = "Base64"; +#endif // __SCENE_INFO_XML_STREAM_BASE_DECLARE__ + +} // namespace +#endif //__SCENE_INFO_XML_STREAM_BASE_H__ diff --git a/src/Scenes/SceneInfoXmlStreamReader.cxx b/src/Scenes/SceneInfoXmlStreamReader.cxx new file mode 100644 index 000000000..9a2deba27 --- /dev/null +++ b/src/Scenes/SceneInfoXmlStreamReader.cxx @@ -0,0 +1,52 @@ + +/*LICENSE_START*/ +/* + * Copyright (C) 2019 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + +#define __SCENE_INFO_XML_STREAM_READER_DECLARE__ +#include "SceneInfoXmlStreamReader.h" +#undef __SCENE_INFO_XML_STREAM_READER_DECLARE__ + +#include "CaretAssert.h" +using namespace caret; + + + +/** + * \class caret::SceneInfoXmlStreamReader + * \brief XML Stream Reader for SceneInfo + * \ingroup Scenes + */ + +/** + * Constructor. + */ +SceneInfoXmlStreamReader::SceneInfoXmlStreamReader() +: SceneInfoXmlStreamBase() +{ + +} + +/** + * Destructor. + */ +SceneInfoXmlStreamReader::~SceneInfoXmlStreamReader() +{ +} + diff --git a/src/Scenes/SceneInfoXmlStreamReader.h b/src/Scenes/SceneInfoXmlStreamReader.h new file mode 100644 index 000000000..ba6e7973b --- /dev/null +++ b/src/Scenes/SceneInfoXmlStreamReader.h @@ -0,0 +1,58 @@ +#ifndef __SCENE_INFO_XML_STREAM_READER_H__ +#define __SCENE_INFO_XML_STREAM_READER_H__ + +/*LICENSE_START*/ +/* + * Copyright (C) 2019 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + + + +#include + +#include "SceneInfoXmlStreamBase.h" + + + +namespace caret { + + class SceneInfoXmlStreamReader : public SceneInfoXmlStreamBase { + + public: + SceneInfoXmlStreamReader(); + + virtual ~SceneInfoXmlStreamReader(); + + SceneInfoXmlStreamReader(const SceneInfoXmlStreamReader&) = delete; + + SceneInfoXmlStreamReader& operator=(const SceneInfoXmlStreamReader&) = delete; + + + // ADD_NEW_METHODS_HERE + + private: + // ADD_NEW_MEMBERS_HERE + + }; + +#ifdef __SCENE_INFO_XML_STREAM_READER_DECLARE__ + // +#endif // __SCENE_INFO_XML_STREAM_READER_DECLARE__ + +} // namespace +#endif //__SCENE_INFO_XML_STREAM_READER_H__ diff --git a/src/Scenes/SceneInfoXmlStreamWriter.cxx b/src/Scenes/SceneInfoXmlStreamWriter.cxx new file mode 100644 index 000000000..ec36528c6 --- /dev/null +++ b/src/Scenes/SceneInfoXmlStreamWriter.cxx @@ -0,0 +1,125 @@ + +/*LICENSE_START*/ +/* + * Copyright (C) 2019 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + +#define __SCENE_INFO_XML_STREAM_WRITER_DECLARE__ +#include "SceneInfoXmlStreamWriter.h" +#undef __SCENE_INFO_XML_STREAM_WRITER_DECLARE__ + +#include + +#include "CaretAssert.h" +#include "SceneInfo.h" +#include "WuQMacroGroupXmlStreamWriter.h" + +using namespace caret; + +/** + * \class caret::SceneInfoXmlStreamWriter + * \brief XML stream writer for SceneInfo + * \ingroup Scenes + */ + +/** + * Constructor. + */ +SceneInfoXmlStreamWriter::SceneInfoXmlStreamWriter() +: SceneInfoXmlStreamBase() +{ + +} + +/** + * Destructor. + */ +SceneInfoXmlStreamWriter::~SceneInfoXmlStreamWriter() +{ +} + +/** + * Write the given SceneInfo to the given xml stream writer + * + * @param xmlWriter + * The XML stream writer + * @param sceneInfo + * The scene info + * @param sceneInfoIndex + * Index of scene associated with the sceneInfo + */ +void +SceneInfoXmlStreamWriter::writeXML(QXmlStreamWriter* xmlWriter, + const SceneInfo* sceneInfo, + const int32_t sceneInfoIndex) +{ + CaretAssert(xmlWriter); + CaretAssert(sceneInfo); + CaretAssert(sceneInfoIndex >= 0); + + m_xmlWriter = xmlWriter; + + m_xmlWriter->writeStartElement(ELEMENT_SCENE_INFO); + m_xmlWriter->writeAttribute(ATTRIBUTE_INDEX, + QString::number(sceneInfoIndex)); + + m_xmlWriter->writeTextElement(ELEMENT_NAME, + sceneInfo->getName()); + + m_xmlWriter->writeTextElement(ELEMENT_BALSA_SCENE_ID, + sceneInfo->getBalsaSceneID()); + + m_xmlWriter->writeTextElement(ELEMENT_DESCRIPTION, + sceneInfo->getDescription()); + + WuQMacroGroupXmlStreamWriter macroGroupXmlWriter; + macroGroupXmlWriter.writeXml(xmlWriter, + sceneInfo->getMacroGroup()); + + writeImageElement(sceneInfo); + + m_xmlWriter->writeEndElement(); + + m_xmlWriter = NULL; +} + +/** + * Write the image element + * + * @param sceneInfo + * The scene information + */ +void +SceneInfoXmlStreamWriter::writeImageElement(const SceneInfo* sceneInfo) +{ + QByteArray imageBytes; + AString imageFormat; + sceneInfo->getImageBytes(imageBytes, + imageFormat); + if (! imageBytes.isEmpty()) { + const QByteArray base64ByteArray(imageBytes.toBase64()); + QString base64String = QString::fromLatin1(base64ByteArray.constData(), + base64ByteArray.size()); + m_xmlWriter->writeStartElement(ELEMENT_IMAGE); + m_xmlWriter->writeAttribute(ATTRIBUTE_ENCODING, VALUE_ENCODING_BASE64); + m_xmlWriter->writeAttribute(ATTRIBUTE_FORMAT, imageFormat); + m_xmlWriter->writeCharacters(base64String); + m_xmlWriter->writeEndElement(); + } +} + diff --git a/src/Scenes/SceneInfoXmlStreamWriter.h b/src/Scenes/SceneInfoXmlStreamWriter.h new file mode 100644 index 000000000..be089568f --- /dev/null +++ b/src/Scenes/SceneInfoXmlStreamWriter.h @@ -0,0 +1,68 @@ +#ifndef __SCENE_INFO_XML_STREAM_WRITER_H__ +#define __SCENE_INFO_XML_STREAM_WRITER_H__ + +/*LICENSE_START*/ +/* + * Copyright (C) 2019 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + + + +#include + +#include "SceneInfoXmlStreamBase.h" + +class QXmlStreamWriter; + +namespace caret { + + class SceneInfo; + + class SceneInfoXmlStreamWriter : public SceneInfoXmlStreamBase { + + public: + SceneInfoXmlStreamWriter(); + + virtual ~SceneInfoXmlStreamWriter(); + + void writeXML(QXmlStreamWriter* xmlWriter, + const SceneInfo* sceneInfo, + const int32_t sceneInfoIndex); + + SceneInfoXmlStreamWriter(const SceneInfoXmlStreamWriter&) = delete; + + SceneInfoXmlStreamWriter& operator=(const SceneInfoXmlStreamWriter&) = delete; + + + // ADD_NEW_METHODS_HERE + + private: + void writeImageElement(const SceneInfo* sceneInfo); + + QXmlStreamWriter* m_xmlWriter = NULL; + + // ADD_NEW_MEMBERS_HERE + + }; + +#ifdef __SCENE_INFO_XML_STREAM_WRITER_DECLARE__ + // +#endif // __SCENE_INFO_XML_STREAM_WRITER_DECLARE__ + +} // namespace +#endif //__SCENE_INFO_XML_STREAM_WRITER_H__ diff --git a/src/Scenes/SceneObject.cxx b/src/Scenes/SceneObject.cxx index f1b085d56..57389a952 100644 --- a/src/Scenes/SceneObject.cxx +++ b/src/Scenes/SceneObject.cxx @@ -42,15 +42,18 @@ using namespace caret; * Constructor. * @param name * Name of the item. + * @param containerType + * Type of container of object (array, map, single). * @param dataType * Data type of the primitive. */ SceneObject::SceneObject(const QString& name, + const SceneObjectContainerTypeEnum::Enum containerType, const SceneObjectDataTypeEnum::Enum dataType) #ifdef CARET_SCENE_DEBUG -: CaretObject(), m_name(name), m_dataType(dataType), m_restoredFlag(false) +: CaretObject(), m_name(name), m_containerType(containerType), m_dataType(dataType), m_restoredFlag(false) #else // CARET_SCENE_DEBUG -: m_name(name), m_dataType(dataType), m_restoredFlag(false) +: m_name(name), m_containerType(containerType), m_dataType(dataType), m_restoredFlag(false) #endif // CARET_SCENE_DEBUG { CaretAssert(name.isEmpty() == false); @@ -73,6 +76,15 @@ SceneObject::getName() const return m_name; } +/** + * @return Type of container of object (array, map, single) + */ +SceneObjectContainerTypeEnum::Enum +SceneObject::getContainerType() const +{ + return m_containerType; +} + /** * @return Data type of the object. */ @@ -203,3 +215,75 @@ SceneObject::toString() const + m_name); return objectInfo; } + +/** + * Cast an instance of SceneObject to a SceneClass. + * Is used to avoid dynamic casting and overridden by the class. + * + * @return Valid pointer (non-NULL) this is SceneClass + */ +const SceneClass* +SceneObject::castToSceneClass() const +{ + return NULL; +} + +/** + * Cast an instance of SceneObject to a SceneEnumeratedType. + * Is used to avoid dynamic casting and overridden by the class. + * + * @return Valid pointer (non-NULL) this is SceneEnumeratedType + */ +const SceneEnumeratedType* +SceneObject::castToSceneEnumeratedType() const +{ + return NULL; +} + +/** + * Cast an instance of SceneObject to a SceneObjectArray. + * Is used to avoid dynamic casting and overridden by the class. + * + * @return Valid pointer (non-NULL) this is SceneObjectArray + */ +const SceneObjectArray* +SceneObject::castToSceneObjectArray() const +{ + return NULL; +} + +/** + * Cast an instance of SceneObject to a SceneObjectMapIntegerKey. + * Is used to avoid dynamic casting and overridden by the class. + * + * @return Valid pointer (non-NULL) this is SceneObjectMapIntegerKey + */ +const SceneObjectMapIntegerKey* +SceneObject::castToSceneObjectMapIntegerKey() const +{ + return NULL; +} + +/** + * Cast an instance of SceneObject to a ScenePathName. + * Is used to avoid dynamic casting and overridden by the class. + * + * @return Valid pointer (non-NULL) this is ScenePathName + */ +const ScenePathName* +SceneObject::castToScenePathName() const +{ + return NULL; +} + +/** + * Cast an instance of SceneObject to a ScenePrimitive. + * Is used to avoid dynamic casting and overridden by the class. + * + * @return Valid pointer (non-NULL) this is ScenePrimitive + */ +const ScenePrimitive* +SceneObject::castToScenePrimitive() const +{ + return NULL; +} diff --git a/src/Scenes/SceneObject.h b/src/Scenes/SceneObject.h index bd4b00709..f3a33bc1f 100644 --- a/src/Scenes/SceneObject.h +++ b/src/Scenes/SceneObject.h @@ -23,10 +23,18 @@ #include "CaretObject.h" +#include "SceneObjectContainerTypeEnum.h" #include "SceneObjectDataTypeEnum.h" namespace caret { + class SceneClass; + class SceneEnumeratedType; + class SceneObjectArray; + class SceneObjectMapIntegerKey; + class ScenePathName; + class ScenePrimitive; + #ifdef CARET_SCENE_DEBUG class SceneObject : public CaretObject { #else // CARET_SCENE_DEBUG @@ -38,6 +46,8 @@ namespace caret { QString getName() const; + SceneObjectContainerTypeEnum::Enum getContainerType() const; + SceneObjectDataTypeEnum::Enum getDataType() const; bool isRestored() const; @@ -55,8 +65,21 @@ namespace caret { /// Should be overridden by any sub-classes that have children virtual std::vector getDescendants() const; + virtual const SceneClass* castToSceneClass() const; + + virtual const SceneEnumeratedType* castToSceneEnumeratedType() const; + + virtual const SceneObjectArray* castToSceneObjectArray() const; + + virtual const SceneObjectMapIntegerKey* castToSceneObjectMapIntegerKey() const; + + virtual const ScenePathName* castToScenePathName() const; + + virtual const ScenePrimitive* castToScenePrimitive() const; + protected: SceneObject(const QString& name, + const SceneObjectContainerTypeEnum::Enum containerType, const SceneObjectDataTypeEnum::Enum dataType); private: @@ -78,6 +101,9 @@ namespace caret { /** Name of the item*/ const QString m_name; + /** Container type of object */ + const SceneObjectContainerTypeEnum::Enum m_containerType; + /** Type of object */ const SceneObjectDataTypeEnum::Enum m_dataType; diff --git a/src/Scenes/SceneObjectArray.cxx b/src/Scenes/SceneObjectArray.cxx index 7ef2a8d0f..d51df2a35 100644 --- a/src/Scenes/SceneObjectArray.cxx +++ b/src/Scenes/SceneObjectArray.cxx @@ -47,6 +47,7 @@ using namespace caret; SceneObjectArray::SceneObjectArray(const QString& name, const SceneObjectDataTypeEnum::Enum dataType) : SceneObject(name, + SceneObjectContainerTypeEnum::ARRAY, dataType) { @@ -60,3 +61,63 @@ SceneObjectArray::~SceneObjectArray() } +/** + * Cast an instance of SceneObject to a SceneObjectArray. + * Is used to avoid dynamic casting and overridden by the class. + * + * @return Valid pointer (non-NULL) this is SceneObjectArray + */ +const SceneObjectArray* +SceneObjectArray::castToSceneObjectArray() const +{ + return this; +} + + +/** + * Cast an instance of SceneObjectArray to a SceneClassArray. + * Is used to avoid dynamic casting and overridden by the class. + * + * @return Valid pointer (non-NULL) this is SceneClass + */ +const SceneClassArray* +SceneObjectArray::castToSceneClassArray() const +{ + return NULL; +} + +/** + * Cast an instance of SceneObjectArray to a SceneEnumeratedTypeArray. + * Is used to avoid dynamic casting and overridden by the class. + * + * @return Valid pointer (non-NULL) this is SceneClass + */ +const SceneEnumeratedTypeArray* +SceneObjectArray::castToSceneEnumeratedTypeArray() const +{ + return NULL; +} + +/** + * Cast an instance of SceneObjectArray to a ScenePathNameArray. + * Is used to avoid dynamic casting and overridden by the class. + * + * @return Valid pointer (non-NULL) this is SceneClass + */ +const ScenePathNameArray* +SceneObjectArray::castToScenePathNameArray() const +{ + return NULL; +} + +/** + * Cast an instance of SceneObjectArray to a ScenePrimitiveArray. + * Is used to avoid dynamic casting and overridden by the class. + * + * @return Valid pointer (non-NULL) this is SceneClass + */ +const ScenePrimitiveArray* +SceneObjectArray::castToScenePrimitiveArray() const +{ + return NULL; +} diff --git a/src/Scenes/SceneObjectArray.h b/src/Scenes/SceneObjectArray.h index 206e0d549..7057eed24 100644 --- a/src/Scenes/SceneObjectArray.h +++ b/src/Scenes/SceneObjectArray.h @@ -25,6 +25,10 @@ #include "SceneObject.h" namespace caret { + class SceneClassArray; + class SceneEnumeratedTypeArray; + class ScenePathNameArray; + class ScenePrimitiveArray; class SceneObjectArray : public SceneObject { @@ -34,6 +38,16 @@ namespace caret { virtual ~SceneObjectArray(); + virtual const SceneObjectArray* castToSceneObjectArray() const; + + virtual const SceneClassArray* castToSceneClassArray() const; + + virtual const SceneEnumeratedTypeArray* castToSceneEnumeratedTypeArray() const; + + virtual const ScenePathNameArray* castToScenePathNameArray() const; + + virtual const ScenePrimitiveArray* castToScenePrimitiveArray() const; + private: SceneObjectArray(const SceneObjectArray&); diff --git a/src/Scenes/SceneObjectContainerTypeEnum.cxx b/src/Scenes/SceneObjectContainerTypeEnum.cxx new file mode 100644 index 000000000..b183638cc --- /dev/null +++ b/src/Scenes/SceneObjectContainerTypeEnum.cxx @@ -0,0 +1,377 @@ + +/*LICENSE_START*/ +/* + * Copyright (C) 2019 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + +#include +#define __SCENE_OBJECT_CONTAINER_TYPE_ENUM_DECLARE__ +#include "SceneObjectContainerTypeEnum.h" +#undef __SCENE_OBJECT_CONTAINER_TYPE_ENUM_DECLARE__ + +#include "CaretAssert.h" + +using namespace caret; + + +/** + * \class caret::SceneObjectContainerTypeEnum + * \brief Type of container for a scene object class instance + * + * Using this enumerated type in the GUI with an EnumComboBoxTemplate + * + * Header File (.h) + * Forward declare the data type: + * class EnumComboBoxTemplate; + * + * Declare the member: + * EnumComboBoxTemplate* m_sceneObjectContainerTypeEnumComboBox; + * + * Declare a slot that is called when user changes selection + * private slots: + * void sceneObjectContainerTypeEnumComboBoxItemActivated(); + * + * Implementation File (.cxx) + * Include the header files + * #include "EnumComboBoxTemplate.h" + * #include "SceneObjectContainerTypeEnum.h" + * + * Instatiate: + * m_sceneObjectContainerTypeEnumComboBox = new EnumComboBoxTemplate(this); + * m_sceneObjectContainerTypeEnumComboBox->setup(); + * + * Get notified when the user changes the selection: + * QObject::connect(m_sceneObjectContainerTypeEnumComboBox, SIGNAL(itemActivated()), + * this, SLOT(sceneObjectContainerTypeEnumComboBoxItemActivated())); + * + * Update the selection: + * m_sceneObjectContainerTypeEnumComboBox->setSelectedItem(NEW_VALUE); + * + * Read the selection: + * const SceneObjectContainerTypeEnum::Enum VARIABLE = m_sceneObjectContainerTypeEnumComboBox->getSelectedItem(); + * + */ + +/** + * Constructor. + * + * @param enumValue + * An enumerated value. + * @param name + * Name of enumerated value. + * + * @param guiName + * User-friendly name for use in user-interface. + */ +SceneObjectContainerTypeEnum::SceneObjectContainerTypeEnum(const Enum enumValue, + const AString& name, + const AString& guiName) +{ + this->enumValue = enumValue; + this->integerCode = integerCodeCounter++; + this->name = name; + this->guiName = guiName; +} + +/** + * Destructor. + */ +SceneObjectContainerTypeEnum::~SceneObjectContainerTypeEnum() +{ +} + +/** + * Initialize the enumerated metadata. + */ +void +SceneObjectContainerTypeEnum::initialize() +{ + if (initializedFlag) { + return; + } + initializedFlag = true; + + enumData.push_back(SceneObjectContainerTypeEnum(ARRAY, + "ARRAY", + "Array")); + + enumData.push_back(SceneObjectContainerTypeEnum(MAP, + "MAP", + "Map")); + + enumData.push_back(SceneObjectContainerTypeEnum(SINGLE, + "SINGLE", + "Single")); + +} + +/** + * Find the data for and enumerated value. + * @param enumValue + * The enumerated value. + * @return Pointer to data for this enumerated type + * or NULL if no data for type or if type is invalid. + */ +const SceneObjectContainerTypeEnum* +SceneObjectContainerTypeEnum::findData(const Enum enumValue) +{ + if (initializedFlag == false) initialize(); + + size_t num = enumData.size(); + for (size_t i = 0; i < num; i++) { + const SceneObjectContainerTypeEnum* d = &enumData[i]; + if (d->enumValue == enumValue) { + return d; + } + } + + return NULL; +} + +/** + * Get a string representation of the enumerated type. + * @param enumValue + * Enumerated value. + * @return + * String representing enumerated value. + */ +AString +SceneObjectContainerTypeEnum::toName(Enum enumValue) { + if (initializedFlag == false) initialize(); + + const SceneObjectContainerTypeEnum* enumInstance = findData(enumValue); + return enumInstance->name; +} + +/** + * Get an enumerated value corresponding to its name. + * @param name + * Name of enumerated value. + * @param isValidOut + * If not NULL, it is set indicating that a + * enum value exists for the input name. + * @return + * Enumerated value. + */ +SceneObjectContainerTypeEnum::Enum +SceneObjectContainerTypeEnum::fromName(const AString& name, bool* isValidOut) +{ + if (initializedFlag == false) initialize(); + + bool validFlag = false; + Enum enumValue = SceneObjectContainerTypeEnum::enumData[0].enumValue; + + for (std::vector::iterator iter = enumData.begin(); + iter != enumData.end(); + iter++) { + const SceneObjectContainerTypeEnum& d = *iter; + if (d.name == name) { + enumValue = d.enumValue; + validFlag = true; + break; + } + } + + if (isValidOut != 0) { + *isValidOut = validFlag; + } + else if (validFlag == false) { + CaretAssertMessage(0, AString("Name " + name + "failed to match enumerated value for type SceneObjectContainerTypeEnum")); + } + return enumValue; +} + +/** + * Get a GUI string representation of the enumerated type. + * @param enumValue + * Enumerated value. + * @return + * String representing enumerated value. + */ +AString +SceneObjectContainerTypeEnum::toGuiName(Enum enumValue) { + if (initializedFlag == false) initialize(); + + const SceneObjectContainerTypeEnum* enumInstance = findData(enumValue); + return enumInstance->guiName; +} + +/** + * Get an enumerated value corresponding to its GUI name. + * @param s + * Name of enumerated value. + * @param isValidOut + * If not NULL, it is set indicating that a + * enum value exists for the input name. + * @return + * Enumerated value. + */ +SceneObjectContainerTypeEnum::Enum +SceneObjectContainerTypeEnum::fromGuiName(const AString& guiName, bool* isValidOut) +{ + if (initializedFlag == false) initialize(); + + bool validFlag = false; + Enum enumValue = SceneObjectContainerTypeEnum::enumData[0].enumValue; + + for (std::vector::iterator iter = enumData.begin(); + iter != enumData.end(); + iter++) { + const SceneObjectContainerTypeEnum& d = *iter; + if (d.guiName == guiName) { + enumValue = d.enumValue; + validFlag = true; + break; + } + } + + if (isValidOut != 0) { + *isValidOut = validFlag; + } + else if (validFlag == false) { + CaretAssertMessage(0, AString("guiName " + guiName + "failed to match enumerated value for type SceneObjectContainerTypeEnum")); + } + return enumValue; +} + +/** + * Get the integer code for a data type. + * + * @return + * Integer code for data type. + */ +int32_t +SceneObjectContainerTypeEnum::toIntegerCode(Enum enumValue) +{ + if (initializedFlag == false) initialize(); + const SceneObjectContainerTypeEnum* enumInstance = findData(enumValue); + return enumInstance->integerCode; +} + +/** + * Find the data type corresponding to an integer code. + * + * @param integerCode + * Integer code for enum. + * @param isValidOut + * If not NULL, on exit isValidOut will indicate if + * integer code is valid. + * @return + * Enum for integer code. + */ +SceneObjectContainerTypeEnum::Enum +SceneObjectContainerTypeEnum::fromIntegerCode(const int32_t integerCode, bool* isValidOut) +{ + if (initializedFlag == false) initialize(); + + bool validFlag = false; + Enum enumValue = SceneObjectContainerTypeEnum::enumData[0].enumValue; + + for (std::vector::iterator iter = enumData.begin(); + iter != enumData.end(); + iter++) { + const SceneObjectContainerTypeEnum& enumInstance = *iter; + if (enumInstance.integerCode == integerCode) { + enumValue = enumInstance.enumValue; + validFlag = true; + break; + } + } + + if (isValidOut != 0) { + *isValidOut = validFlag; + } + else if (validFlag == false) { + CaretAssertMessage(0, AString("Integer code " + AString::number(integerCode) + "failed to match enumerated value for type SceneObjectContainerTypeEnum")); + } + return enumValue; +} + +/** + * Get all of the enumerated type values. The values can be used + * as parameters to toXXX() methods to get associated metadata. + * + * @param allEnums + * A vector that is OUTPUT containing all of the enumerated values. + */ +void +SceneObjectContainerTypeEnum::getAllEnums(std::vector& allEnums) +{ + if (initializedFlag == false) initialize(); + + allEnums.clear(); + + for (std::vector::iterator iter = enumData.begin(); + iter != enumData.end(); + iter++) { + allEnums.push_back(iter->enumValue); + } +} + +/** + * Get all of the names of the enumerated type values. + * + * @param allNames + * A vector that is OUTPUT containing all of the names of the enumerated values. + * @param isSorted + * If true, the names are sorted in alphabetical order. + */ +void +SceneObjectContainerTypeEnum::getAllNames(std::vector& allNames, const bool isSorted) +{ + if (initializedFlag == false) initialize(); + + allNames.clear(); + + for (std::vector::iterator iter = enumData.begin(); + iter != enumData.end(); + iter++) { + allNames.push_back(SceneObjectContainerTypeEnum::toName(iter->enumValue)); + } + + if (isSorted) { + std::sort(allNames.begin(), allNames.end()); + } +} + +/** + * Get all of the GUI names of the enumerated type values. + * + * @param allNames + * A vector that is OUTPUT containing all of the GUI names of the enumerated values. + * @param isSorted + * If true, the names are sorted in alphabetical order. + */ +void +SceneObjectContainerTypeEnum::getAllGuiNames(std::vector& allGuiNames, const bool isSorted) +{ + if (initializedFlag == false) initialize(); + + allGuiNames.clear(); + + for (std::vector::iterator iter = enumData.begin(); + iter != enumData.end(); + iter++) { + allGuiNames.push_back(SceneObjectContainerTypeEnum::toGuiName(iter->enumValue)); + } + + if (isSorted) { + std::sort(allGuiNames.begin(), allGuiNames.end()); + } +} + diff --git a/src/Scenes/SceneObjectContainerTypeEnum.h b/src/Scenes/SceneObjectContainerTypeEnum.h new file mode 100644 index 000000000..e8e85948e --- /dev/null +++ b/src/Scenes/SceneObjectContainerTypeEnum.h @@ -0,0 +1,106 @@ +#ifndef __SCENE_OBJECT_CONTAINER_TYPE_ENUM_H__ +#define __SCENE_OBJECT_CONTAINER_TYPE_ENUM_H__ + +/*LICENSE_START*/ +/* + * Copyright (C) 2019 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + + +#include +#include +#include "AString.h" + +namespace caret { + +class SceneObjectContainerTypeEnum { + +public: + /** + * Enumerated values. + */ + enum Enum { + /** Array */ + ARRAY, + /** Map */ + MAP, + /** Single (one value) */ + SINGLE + }; + + + ~SceneObjectContainerTypeEnum(); + + static AString toName(Enum enumValue); + + static Enum fromName(const AString& name, bool* isValidOut); + + static AString toGuiName(Enum enumValue); + + static Enum fromGuiName(const AString& guiName, bool* isValidOut); + + static int32_t toIntegerCode(Enum enumValue); + + static Enum fromIntegerCode(const int32_t integerCode, bool* isValidOut); + + static void getAllEnums(std::vector& allEnums); + + static void getAllNames(std::vector& allNames, const bool isSorted); + + static void getAllGuiNames(std::vector& allGuiNames, const bool isSorted); + +private: + SceneObjectContainerTypeEnum(const Enum enumValue, + const AString& name, + const AString& guiName); + + static const SceneObjectContainerTypeEnum* findData(const Enum enumValue); + + /** Holds all instance of enum values and associated metadata */ + static std::vector enumData; + + /** Initialize instances that contain the enum values and metadata */ + static void initialize(); + + /** Indicates instance of enum values and metadata have been initialized */ + static bool initializedFlag; + + /** Auto generated integer codes */ + static int32_t integerCodeCounter; + + /** The enumerated type value for an instance */ + Enum enumValue; + + /** The integer code associated with an enumerated value */ + int32_t integerCode; + + /** The name, a text string that is identical to the enumerated value */ + AString name; + + /** A user-friendly name that is displayed in the GUI */ + AString guiName; +}; + +#ifdef __SCENE_OBJECT_CONTAINER_TYPE_ENUM_DECLARE__ +std::vector SceneObjectContainerTypeEnum::enumData; +bool SceneObjectContainerTypeEnum::initializedFlag = false; +int32_t SceneObjectContainerTypeEnum::integerCodeCounter = 0; +#endif // __SCENE_OBJECT_CONTAINER_TYPE_ENUM_DECLARE__ + +} // namespace +#endif //__SCENE_OBJECT_CONTAINER_TYPE_ENUM_H__ diff --git a/src/Scenes/SceneObjectMapIntegerKey.cxx b/src/Scenes/SceneObjectMapIntegerKey.cxx index 362a43936..21151659f 100644 --- a/src/Scenes/SceneObjectMapIntegerKey.cxx +++ b/src/Scenes/SceneObjectMapIntegerKey.cxx @@ -60,7 +60,8 @@ using namespace caret; */ SceneObjectMapIntegerKey::SceneObjectMapIntegerKey(const QString& name, const SceneObjectDataTypeEnum::Enum valueDataType) -: SceneObject(name, +: SceneObject(name, + SceneObjectContainerTypeEnum::MAP, valueDataType) { @@ -78,6 +79,18 @@ SceneObjectMapIntegerKey::~SceneObjectMapIntegerKey() } } +/** + * Cast an instance of SceneObject to a SceneObjectMapIntegerKey. + * Is used to avoid dynamic casting and overridden by the class. + * + * @return Valid pointer (non-NULL) this is SceneObjectMapIntegerKey + */ +const SceneObjectMapIntegerKey* +SceneObjectMapIntegerKey::castToSceneObjectMapIntegerKey() const +{ + return this; +} + /** * @return True if this map's content is empty, else false. */ diff --git a/src/Scenes/SceneObjectMapIntegerKey.h b/src/Scenes/SceneObjectMapIntegerKey.h index 9c430d16e..70f0886f7 100644 --- a/src/Scenes/SceneObjectMapIntegerKey.h +++ b/src/Scenes/SceneObjectMapIntegerKey.h @@ -37,6 +37,8 @@ namespace caret { SceneObjectMapIntegerKey(const QString& name, const SceneObjectDataTypeEnum::Enum valueDataType); + virtual const SceneObjectMapIntegerKey* castToSceneObjectMapIntegerKey() const; + virtual std::vector getDescendants() const; private: diff --git a/src/Scenes/ScenePathName.cxx b/src/Scenes/ScenePathName.cxx index aa1627b1f..d9c09822d 100644 --- a/src/Scenes/ScenePathName.cxx +++ b/src/Scenes/ScenePathName.cxx @@ -62,12 +62,16 @@ using namespace caret; ScenePathName::ScenePathName(const AString& name, const AString& value) : SceneObject(name, + SceneObjectContainerTypeEnum::SINGLE, SceneObjectDataTypeEnum::SCENE_PATH_NAME) { setValue(value); } -ScenePathName::ScenePathName(const ScenePathName& rhs): SceneObject(rhs.getName(), SceneObjectDataTypeEnum::SCENE_PATH_NAME) +ScenePathName::ScenePathName(const ScenePathName& rhs) +: SceneObject(rhs.getName(), + SceneObjectContainerTypeEnum::SINGLE, + SceneObjectDataTypeEnum::SCENE_PATH_NAME) { m_value = rhs.m_value; } @@ -85,6 +89,18 @@ ScenePathName::~ScenePathName() } +/** + * Cast an instance of SceneObject to a ScenePathName. + * Is used to avoid dynamic casting and overridden by the class. + * + * @return Valid pointer (non-NULL) this is ScenePathName + */ +const ScenePathName* +ScenePathName::castToScenePathName() const +{ + return this; +} + /** * Set the value. * @param value diff --git a/src/Scenes/ScenePathName.h b/src/Scenes/ScenePathName.h index 3bd205c44..d03e9f7e6 100644 --- a/src/Scenes/ScenePathName.h +++ b/src/Scenes/ScenePathName.h @@ -36,6 +36,8 @@ namespace caret { virtual ~ScenePathName(); + virtual const ScenePathName* castToScenePathName() const; + void setValue(const AString& value); virtual AString stringValue() const; diff --git a/src/Scenes/ScenePathNameArray.cxx b/src/Scenes/ScenePathNameArray.cxx index 4a53446f5..fc2d565d3 100644 --- a/src/Scenes/ScenePathNameArray.cxx +++ b/src/Scenes/ScenePathNameArray.cxx @@ -122,6 +122,18 @@ ScenePathNameArray::~ScenePathNameArray() m_values.clear(); } +/** + * Cast an instance of SceneObjectArray to a ScenePathNameArray. + * Is used to avoid dynamic casting and overridden by the class. + * + * @return Valid pointer (non-NULL) this is SceneClass + */ +const ScenePathNameArray* +ScenePathNameArray::castToScenePathNameArray() const +{ + return this; +} + /** * @return All descendant SceneClasses (children, grandchildren, etc.) of this instance. */ diff --git a/src/Scenes/ScenePathNameArray.h b/src/Scenes/ScenePathNameArray.h index dcc6d16ac..52a9c7a50 100644 --- a/src/Scenes/ScenePathNameArray.h +++ b/src/Scenes/ScenePathNameArray.h @@ -45,6 +45,8 @@ namespace caret { virtual ~ScenePathNameArray(); + virtual const ScenePathNameArray* castToScenePathNameArray() const; + void setScenePathNameAtIndex(const int32_t arrayIndex, const AString& sceneFileName, const AString& pathNameValue); diff --git a/src/Scenes/ScenePrimitive.cxx b/src/Scenes/ScenePrimitive.cxx index 70b707b98..550a561b7 100644 --- a/src/Scenes/ScenePrimitive.cxx +++ b/src/Scenes/ScenePrimitive.cxx @@ -47,7 +47,9 @@ using namespace caret; */ ScenePrimitive::ScenePrimitive(const QString& name, const SceneObjectDataTypeEnum::Enum dataType) -: SceneObject(name, dataType) +: SceneObject(name, + SceneObjectContainerTypeEnum::SINGLE, + dataType) { } @@ -60,6 +62,18 @@ ScenePrimitive::~ScenePrimitive() } +/** + * Cast an instance of SceneObject to a ScenePrimitive. + * Is used to avoid dynamic casting and overridden by the class. + * + * @return Valid pointer (non-NULL) this is ScenePrimitive + */ +const ScenePrimitive* +ScenePrimitive::castToScenePrimitive() const +{ + return this; +} + /** * Get a description of this object's content. * @return String describing this object's content. diff --git a/src/Scenes/ScenePrimitive.h b/src/Scenes/ScenePrimitive.h index e64c158df..ee0d4a0e5 100644 --- a/src/Scenes/ScenePrimitive.h +++ b/src/Scenes/ScenePrimitive.h @@ -31,6 +31,8 @@ namespace caret { public: virtual ~ScenePrimitive(); + virtual const ScenePrimitive* castToScenePrimitive() const; + protected: ScenePrimitive(const QString& name, const SceneObjectDataTypeEnum::Enum dataType); diff --git a/src/Scenes/ScenePrimitiveArray.cxx b/src/Scenes/ScenePrimitiveArray.cxx index c8959fb0a..f2a1dfd92 100644 --- a/src/Scenes/ScenePrimitiveArray.cxx +++ b/src/Scenes/ScenePrimitiveArray.cxx @@ -61,6 +61,18 @@ ScenePrimitiveArray::~ScenePrimitiveArray() } +/** + * Cast an instance of SceneObjectArray to a ScenePrimitiveArray. + * Is used to avoid dynamic casting and overridden by the class. + * + * @return Valid pointer (non-NULL) this is SceneClass + */ +const ScenePrimitiveArray* +ScenePrimitiveArray::castToScenePrimitiveArray() const +{ + return this; +} + /** * Load the array with boolean values. * @param valuesOut diff --git a/src/Scenes/ScenePrimitiveArray.h b/src/Scenes/ScenePrimitiveArray.h index 2424815ef..4d25fa219 100644 --- a/src/Scenes/ScenePrimitiveArray.h +++ b/src/Scenes/ScenePrimitiveArray.h @@ -31,6 +31,8 @@ namespace caret { public: virtual ~ScenePrimitiveArray(); + virtual const ScenePrimitiveArray* castToScenePrimitiveArray() const; + protected: ScenePrimitiveArray(const QString& name, const SceneObjectDataTypeEnum::Enum dataType); diff --git a/src/Scenes/SceneXmlStreamBase.cxx b/src/Scenes/SceneXmlStreamBase.cxx new file mode 100644 index 000000000..2df1811ee --- /dev/null +++ b/src/Scenes/SceneXmlStreamBase.cxx @@ -0,0 +1,62 @@ + +/*LICENSE_START*/ +/* + * Copyright (C) 2019 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + +#define __SCENE_XML_STREAM_BASE_DECLARE__ +#include "SceneXmlStreamBase.h" +#undef __SCENE_XML_STREAM_BASE_DECLARE__ + +#include "CaretAssert.h" +using namespace caret; + + + +/** + * \class caret::SceneXmlStreamBase + * \brief Base class for Scene XML Stream Reader and Writer + * \ingroup Scenes + */ + +/** + * Constructor. + */ +SceneXmlStreamBase::SceneXmlStreamBase() +: CaretObject() +{ + +} + +/** + * Destructor. + */ +SceneXmlStreamBase::~SceneXmlStreamBase() +{ +} + +/** + * Get a description of this object's content. + * @return String describing this object's content. + */ +AString +SceneXmlStreamBase::toString() const +{ + return "SceneXmlStreamBase"; +} + diff --git a/src/Scenes/SceneXmlStreamBase.h b/src/Scenes/SceneXmlStreamBase.h new file mode 100644 index 000000000..3619ed544 --- /dev/null +++ b/src/Scenes/SceneXmlStreamBase.h @@ -0,0 +1,125 @@ +#ifndef __SCENE_XML_STREAM_BASE_H__ +#define __SCENE_XML_STREAM_BASE_H__ + +/*LICENSE_START*/ +/* + * Copyright (C) 2019 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + + + +#include + +#include "CaretObject.h" + + + +namespace caret { + + class SceneXmlStreamBase : public CaretObject { + + public: + SceneXmlStreamBase(); + + virtual ~SceneXmlStreamBase(); + + SceneXmlStreamBase(const SceneXmlStreamBase&) = delete; + + SceneXmlStreamBase& operator=(const SceneXmlStreamBase&) = delete; + + static const AString ELEMENT_SCENE; + static const AString ELEMENT_SCENE_DESCRIPTION; + static const AString ELEMENT_SCENE_NAME; + + static const AString ATTRIBUTE_SCENE_INDEX; + static const AString ATTRIBUTE_SCENE_TYPE; + + static const AString ELEMENT_OBJECT; + + static const AString ATTRIBUTE_OBJECT_CLASS; + static const AString ATTRIBUTE_OBJECT_NAME; + static const AString ATTRIBUTE_OBJECT_TYPE; + static const AString ATTRIBUTE_OBJECT_VERSION; + + + static const AString ELEMENT_OBJECT_ARRAY; + + static const AString ATTRIBUTE_OBJECT_ARRAY_NAME; + static const AString ATTRIBUTE_OBJECT_ARRAY_LENGTH; + static const AString ATTRIBUTE_OBJECT_ARRAY_TYPE; + + static const AString ELEMENT_OBJECT_ARRAY_ELEMENT; + + static const AString ATTRIBUTE_OBJECT_ARRAY_ELEMENT_INDEX; + + static const AString ELEMENT_OBJECT_MAP; + + static const AString ATTRIBUTE_OBJECT_MAP_NAME; + static const AString ATTRIBUTE_OBJECT_MAP_TYPE; + + static const AString ELEMENT_OBJECT_MAP_VALUE; + + static const AString ATTRIBUTE_OBJECT_MAP_VALUE_KEY; + + // ADD_NEW_METHODS_HERE + + virtual AString toString() const; + + private: + // ADD_NEW_MEMBERS_HERE + + }; + +#ifdef __SCENE_XML_STREAM_BASE_DECLARE__ + const AString SceneXmlStreamBase::ELEMENT_SCENE = "Scene"; + const AString SceneXmlStreamBase::ELEMENT_SCENE_DESCRIPTION = "Description"; + const AString SceneXmlStreamBase::ELEMENT_SCENE_NAME = "Name"; + + const AString SceneXmlStreamBase::ATTRIBUTE_SCENE_INDEX = "Index"; + const AString SceneXmlStreamBase::ATTRIBUTE_SCENE_TYPE = "Type"; + + const AString SceneXmlStreamBase::ELEMENT_OBJECT = "Object"; + + const AString SceneXmlStreamBase::ATTRIBUTE_OBJECT_CLASS = "Class"; + const AString SceneXmlStreamBase::ATTRIBUTE_OBJECT_NAME = "Name"; + const AString SceneXmlStreamBase::ATTRIBUTE_OBJECT_TYPE = "Type"; + const AString SceneXmlStreamBase::ATTRIBUTE_OBJECT_VERSION = "Version"; + + + const AString SceneXmlStreamBase::ELEMENT_OBJECT_ARRAY = "ObjectArray"; + + const AString SceneXmlStreamBase::ATTRIBUTE_OBJECT_ARRAY_NAME = "Name"; + const AString SceneXmlStreamBase::ATTRIBUTE_OBJECT_ARRAY_LENGTH = "Length"; + const AString SceneXmlStreamBase::ATTRIBUTE_OBJECT_ARRAY_TYPE = "Type"; + + const AString SceneXmlStreamBase::ELEMENT_OBJECT_ARRAY_ELEMENT = "Element"; + + const AString SceneXmlStreamBase::ATTRIBUTE_OBJECT_ARRAY_ELEMENT_INDEX = "Index"; + + const AString SceneXmlStreamBase::ELEMENT_OBJECT_MAP = "ObjectMap"; + + const AString SceneXmlStreamBase::ATTRIBUTE_OBJECT_MAP_NAME = "Name"; + const AString SceneXmlStreamBase::ATTRIBUTE_OBJECT_MAP_TYPE = "Type"; + + const AString SceneXmlStreamBase::ELEMENT_OBJECT_MAP_VALUE = "Value"; + + const AString SceneXmlStreamBase::ATTRIBUTE_OBJECT_MAP_VALUE_KEY = "Key"; +#endif // __SCENE_XML_STREAM_BASE_DECLARE__ + +} // namespace +#endif //__SCENE_XML_STREAM_BASE_H__ diff --git a/src/Scenes/SceneXmlStreamReader.cxx b/src/Scenes/SceneXmlStreamReader.cxx new file mode 100644 index 000000000..941fd7c2a --- /dev/null +++ b/src/Scenes/SceneXmlStreamReader.cxx @@ -0,0 +1,52 @@ + +/*LICENSE_START*/ +/* + * Copyright (C) 2019 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + +#define __SCENE_XML_STREAM_READER_DECLARE__ +#include "SceneXmlStreamReader.h" +#undef __SCENE_XML_STREAM_READER_DECLARE__ + +#include "CaretAssert.h" +using namespace caret; + + + +/** + * \class caret::SceneXmlStreamReader + * \brief XML stream reader for Scene + * \ingroup Scenes + */ + +/** + * Constructor. + */ +SceneXmlStreamReader::SceneXmlStreamReader() +: SceneXmlStreamBase() +{ + +} + +/** + * Destructor. + */ +SceneXmlStreamReader::~SceneXmlStreamReader() +{ +} + diff --git a/src/Scenes/SceneXmlStreamReader.h b/src/Scenes/SceneXmlStreamReader.h new file mode 100644 index 000000000..ccf969631 --- /dev/null +++ b/src/Scenes/SceneXmlStreamReader.h @@ -0,0 +1,58 @@ +#ifndef __SCENE_XML_STREAM_READER_H__ +#define __SCENE_XML_STREAM_READER_H__ + +/*LICENSE_START*/ +/* + * Copyright (C) 2019 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + + + +#include + +#include "SceneXmlStreamBase.h" + + + +namespace caret { + + class SceneXmlStreamReader : public SceneXmlStreamBase { + + public: + SceneXmlStreamReader(); + + virtual ~SceneXmlStreamReader(); + + SceneXmlStreamReader(const SceneXmlStreamReader&) = delete; + + SceneXmlStreamReader& operator=(const SceneXmlStreamReader&) = delete; + + + // ADD_NEW_METHODS_HERE + + private: + // ADD_NEW_MEMBERS_HERE + + }; + +#ifdef __SCENE_XML_STREAM_READER_DECLARE__ + // +#endif // __SCENE_XML_STREAM_READER_DECLARE__ + +} // namespace +#endif //__SCENE_XML_STREAM_READER_H__ diff --git a/src/Scenes/SceneXmlStreamWriter.cxx b/src/Scenes/SceneXmlStreamWriter.cxx new file mode 100644 index 000000000..eb5441ecb --- /dev/null +++ b/src/Scenes/SceneXmlStreamWriter.cxx @@ -0,0 +1,595 @@ + +/*LICENSE_START*/ +/* + * Copyright (C) 2019 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + +#define __SCENE_XML_STREAM_WRITER_DECLARE__ +#include "SceneXmlStreamWriter.h" +#undef __SCENE_XML_STREAM_WRITER_DECLARE__ + +#include + +#include "CaretAssert.h" +#include "FileInformation.h" +#include "Scene.h" +#include "SceneAttributes.h" +#include "SceneClass.h" +#include "SceneClassArray.h" +#include "SceneEnumeratedType.h" +#include "SceneEnumeratedTypeArray.h" +#include "SceneInfo.h" +#include "SceneObjectMapIntegerKey.h" +#include "ScenePathName.h" +#include "ScenePathNameArray.h" +#include "ScenePrimitive.h" +#include "ScenePrimitiveArray.h" +#include "SceneXmlElements.h" + +using namespace caret; + + + +/** + * \class caret::SceneXmlStreamWriter + * \brief XML stream writer for Scene + * \ingroup Scenes + */ + +/** + * Constructor. + */ +SceneXmlStreamWriter::SceneXmlStreamWriter() +: SceneXmlStreamBase() +{ + +} + +/** + * Destructor. + */ +SceneXmlStreamWriter::~SceneXmlStreamWriter() +{ +} + +/** + * Write the given scene using the given xml stream writer + * + * @param xmlWriter + * The XML writer + * @param scene + * The scene + * @param sceneIndex + * Index of the scene + * @param sceneFileName + * Name of the scene file + */ +void +SceneXmlStreamWriter::writeXML(QXmlStreamWriter* xmlWriter, + const Scene* scene, + const int32_t sceneIndex, + const AString& sceneFileName) +{ + CaretAssert(xmlWriter); + CaretAssert(scene); + CaretAssert(sceneIndex >= 0); + CaretAssert( ! sceneFileName.isEmpty()); + + m_xmlWriter = xmlWriter; + m_sceneFileName = sceneFileName; + + const SceneAttributes* sceneAttributes = scene->getAttributes(); + const AString sceneTypeName = SceneTypeEnum::toName(sceneAttributes->getSceneType()); + + m_xmlWriter->writeStartElement(ELEMENT_SCENE); + m_xmlWriter->writeAttribute(ATTRIBUTE_SCENE_INDEX, + QString::number(sceneIndex)); + m_xmlWriter->writeAttribute(ATTRIBUTE_SCENE_TYPE, + sceneTypeName); + + m_xmlWriter->writeTextElement(ELEMENT_SCENE_NAME, + scene->getName()); + m_xmlWriter->writeTextElement(ELEMENT_SCENE_DESCRIPTION, + scene->getDescription()); + + const int32_t numClasses = scene->getNumberOfClasses(); + for (int32_t i = 0; i < numClasses; i++) { + writeSceneClass(scene->getClassAtIndex(i)); + } + + m_xmlWriter->writeEndElement(); + + m_xmlWriter = NULL; +} + +/** + * Write the given scene class using the xml stream writer + * + * @param sceneClass + * Scene class to write + */ +void +SceneXmlStreamWriter::writeSceneClass(const SceneClass* sceneClass) +{ + CaretAssert(sceneClass); + if (sceneClass == NULL) { + return; + } + + const AString& objectTypeName = SceneObjectDataTypeEnum::toXmlName(sceneClass->getDataType()); + + m_xmlWriter->writeStartElement(ELEMENT_OBJECT); + m_xmlWriter->writeAttribute(ATTRIBUTE_OBJECT_TYPE, + objectTypeName); + m_xmlWriter->writeAttribute(ATTRIBUTE_OBJECT_CLASS, + sceneClass->getClassName()); + m_xmlWriter->writeAttribute(ATTRIBUTE_OBJECT_NAME, + sceneClass->getName()); + m_xmlWriter->writeAttribute(ATTRIBUTE_OBJECT_VERSION, + AString::number(sceneClass->getVersionNumber())); + + const int32_t numberOfObjects = sceneClass->getNumberOfObjects(); + for (int32_t i = 0; i < numberOfObjects; i++) { + writeSceneObject(sceneClass->getObjectAtIndex(i)); + } + m_xmlWriter->writeEndElement(); +} + +/** + * Write the given scene object using the xml stream writer + * + * @param sceneObject + * Scene object to write + */ +void +SceneXmlStreamWriter::writeSceneObject(const SceneObject* sceneObject) +{ + CaretAssert(sceneObject); + + switch (sceneObject->getContainerType()) { + case SceneObjectContainerTypeEnum::ARRAY: + writeArrayObject(sceneObject->castToSceneObjectArray()); + break; + case SceneObjectContainerTypeEnum::MAP: + writeMapObject(sceneObject->castToSceneObjectMapIntegerKey()); + break; + case SceneObjectContainerTypeEnum::SINGLE: + writeSingleObject(sceneObject); + break; + } + + switch (sceneObject->getDataType()) { + case SceneObjectDataTypeEnum::SCENE_BOOLEAN: + break; + case SceneObjectDataTypeEnum::SCENE_CLASS: + break; + case SceneObjectDataTypeEnum::SCENE_ENUMERATED_TYPE: + break; + case SceneObjectDataTypeEnum::SCENE_FLOAT: + break; + case SceneObjectDataTypeEnum::SCENE_INTEGER: + break; + case SceneObjectDataTypeEnum::SCENE_INVALID: + break; + case SceneObjectDataTypeEnum::SCENE_PATH_NAME: + break; + case SceneObjectDataTypeEnum::SCENE_STRING: + break; + case SceneObjectDataTypeEnum::SCENE_UNSIGNED_BYTE: + break; + } +} + +/** + * Write an object array + * + * @param objectArray + * The object array + */ +void +SceneXmlStreamWriter::writeArrayObject(const SceneObjectArray* objectArray) +{ + CaretAssert(objectArray); + if (objectArray == NULL) { + return; + } + + switch (objectArray->getDataType()) { + case SceneObjectDataTypeEnum::SCENE_BOOLEAN: + writeArrayPrimitiveType(objectArray->castToScenePrimitiveArray()); + break; + case SceneObjectDataTypeEnum::SCENE_CLASS: + writeArrayClass(objectArray->castToSceneClassArray()); + break; + case SceneObjectDataTypeEnum::SCENE_ENUMERATED_TYPE: + writeArrayEnumeratedType(objectArray->castToSceneEnumeratedTypeArray()); + break; + case SceneObjectDataTypeEnum::SCENE_FLOAT: + writeArrayPrimitiveType(objectArray->castToScenePrimitiveArray()); + break; + case SceneObjectDataTypeEnum::SCENE_INTEGER: + writeArrayPrimitiveType(objectArray->castToScenePrimitiveArray()); + break; + case SceneObjectDataTypeEnum::SCENE_INVALID: + CaretAssert(0); + break; + case SceneObjectDataTypeEnum::SCENE_PATH_NAME: + writeArrayPathName(objectArray->castToScenePathNameArray()); + break; + case SceneObjectDataTypeEnum::SCENE_STRING: + writeArrayPrimitiveType(objectArray->castToScenePrimitiveArray()); + break; + case SceneObjectDataTypeEnum::SCENE_UNSIGNED_BYTE: + writeArrayPrimitiveType(objectArray->castToScenePrimitiveArray()); + break; + } +} + +/** + * Write an object map + * + * @param objectMap + * The object map + */ +void +SceneXmlStreamWriter::writeMapObject(const SceneObjectMapIntegerKey* objectMap) +{ + CaretAssert(objectMap); + if (objectMap == NULL) { + return; + } + + const SceneObjectDataTypeEnum::Enum dataType = objectMap->getDataType(); + + m_xmlWriter->writeStartElement(ELEMENT_OBJECT_MAP); + m_xmlWriter->writeAttribute(ATTRIBUTE_OBJECT_MAP_TYPE, + SceneObjectDataTypeEnum::toXmlName(dataType)); + m_xmlWriter->writeAttribute(ATTRIBUTE_OBJECT_MAP_NAME, + objectMap->getName()); + + const std::map& sceneMap = objectMap->getMap(); + for (const auto iter : sceneMap) { + const int32_t key = iter.first; + const QString keyString(QString::number(key)); + + m_xmlWriter->writeStartElement(ELEMENT_OBJECT_MAP_VALUE); + m_xmlWriter->writeAttribute(ATTRIBUTE_OBJECT_MAP_VALUE_KEY, + keyString); + + const SceneObject* valueObject = iter.second; + switch (dataType) { + case SceneObjectDataTypeEnum::SCENE_INVALID: + CaretAssert(0); + break; + case SceneObjectDataTypeEnum::SCENE_CLASS: + { + const SceneClass* value = valueObject->castToSceneClass(); + CaretAssert(value); + writeSceneClass(value); + } + break; + case SceneObjectDataTypeEnum::SCENE_ENUMERATED_TYPE: + { + const SceneEnumeratedType* value = valueObject->castToSceneEnumeratedType(); + CaretAssert(value); + m_xmlWriter->writeCharacters(value->stringValue()); + } + break; + case SceneObjectDataTypeEnum::SCENE_BOOLEAN: + { + const ScenePrimitive* value = valueObject->castToScenePrimitive(); + CaretAssert(value); + m_xmlWriter->writeCharacters(value->stringValue()); + } + break; + case SceneObjectDataTypeEnum::SCENE_FLOAT: + { + const ScenePrimitive* value = valueObject->castToScenePrimitive(); + CaretAssert(value); + m_xmlWriter->writeCharacters(value->stringValue()); + } + break; + case SceneObjectDataTypeEnum::SCENE_INTEGER: + { + const ScenePrimitive* value = valueObject->castToScenePrimitive(); + CaretAssert(value); + m_xmlWriter->writeCharacters(value->stringValue()); + } + break; + case SceneObjectDataTypeEnum::SCENE_PATH_NAME: + { + const ScenePathName* pathName = valueObject->castToScenePathName(); + CaretAssert(pathName); + const AString path = pathName->getRelativePathToSceneFile(m_sceneFileName); + m_xmlWriter->writeCharacters(path); + } + break; + case SceneObjectDataTypeEnum::SCENE_STRING: + { + const ScenePrimitive* value = valueObject->castToScenePrimitive(); + CaretAssert(value); + m_xmlWriter->writeCharacters(value->stringValue()); + } + break; + case SceneObjectDataTypeEnum::SCENE_UNSIGNED_BYTE: + { + const ScenePrimitive* value = valueObject->castToScenePrimitive(); + CaretAssert(value); + m_xmlWriter->writeCharacters(value->stringValue()); + } + break; + } + + m_xmlWriter->writeEndElement(); + } + + + m_xmlWriter->writeEndElement(); +} + +/** + * Write a single object (not an array nor map) + * + * @param sceneObject + * The single object + */ +void +SceneXmlStreamWriter::writeSingleObject(const SceneObject* sceneObject) +{ + CaretAssert(sceneObject); + if (sceneObject == NULL) { + return; + } + + switch (sceneObject->getDataType()) { + case SceneObjectDataTypeEnum::SCENE_BOOLEAN: + writePrimitive(sceneObject->castToScenePrimitive()); + break; + case SceneObjectDataTypeEnum::SCENE_CLASS: + writeSceneClass(sceneObject->castToSceneClass()); + break; + case SceneObjectDataTypeEnum::SCENE_ENUMERATED_TYPE: + writeEnumeratedType(sceneObject->castToSceneEnumeratedType()); + break; + case SceneObjectDataTypeEnum::SCENE_FLOAT: + writePrimitive(sceneObject->castToScenePrimitive()); + break; + case SceneObjectDataTypeEnum::SCENE_INTEGER: + writePrimitive(sceneObject->castToScenePrimitive()); + break; + case SceneObjectDataTypeEnum::SCENE_INVALID: + CaretAssert(0); + break; + case SceneObjectDataTypeEnum::SCENE_PATH_NAME: + writePathName(sceneObject->castToScenePathName()); + break; + case SceneObjectDataTypeEnum::SCENE_STRING: + writePrimitive(sceneObject->castToScenePrimitive()); + break; + case SceneObjectDataTypeEnum::SCENE_UNSIGNED_BYTE: + writePrimitive(sceneObject->castToScenePrimitive()); + break; + } +} + +/** + * Write an enumerated type + * + * @param sceneEnumeratedType + * The object map + */ +void +SceneXmlStreamWriter::writeEnumeratedType(const SceneEnumeratedType* sceneEnumeratedType) +{ + CaretAssert(sceneEnumeratedType); + if (sceneEnumeratedType == NULL) { + return; + } + + m_xmlWriter->writeStartElement(ELEMENT_OBJECT); + m_xmlWriter->writeAttribute(ATTRIBUTE_OBJECT_TYPE, + SceneObjectDataTypeEnum::toXmlName(sceneEnumeratedType->getDataType())); + m_xmlWriter->writeAttribute(ATTRIBUTE_OBJECT_NAME, + sceneEnumeratedType->getName()); + m_xmlWriter->writeCharacters(sceneEnumeratedType->stringValue()); + m_xmlWriter->writeEndElement(); +} + +/** + * Write a path name + * + * @param scenePathName + * The path name + */ +void +SceneXmlStreamWriter::writePathName(const ScenePathName* scenePathName) +{ + CaretAssert(scenePathName); + if (scenePathName == NULL) { + return; + } + + m_xmlWriter->writeStartElement(ELEMENT_OBJECT); + m_xmlWriter->writeAttribute(ATTRIBUTE_OBJECT_TYPE, + SceneObjectDataTypeEnum::toXmlName(scenePathName->getDataType())); + m_xmlWriter->writeAttribute(ATTRIBUTE_OBJECT_NAME, + scenePathName->getName()); + m_xmlWriter->writeCharacters(scenePathName->getRelativePathToSceneFile(m_sceneFileName)); + m_xmlWriter->writeEndElement(); +} + +/** + * Write a primitive + * + * @param scenePrimitive + * The primitive + */ +void +SceneXmlStreamWriter::writePrimitive(const ScenePrimitive* scenePrimitive) +{ + CaretAssert(scenePrimitive); + if (scenePrimitive == NULL) { + return; + } + + m_xmlWriter->writeStartElement(ELEMENT_OBJECT); + m_xmlWriter->writeAttribute(ATTRIBUTE_OBJECT_TYPE, + SceneObjectDataTypeEnum::toXmlName(scenePrimitive->getDataType())); + m_xmlWriter->writeAttribute(ATTRIBUTE_OBJECT_NAME, + scenePrimitive->getName()); + m_xmlWriter->writeCharacters(scenePrimitive->stringValue()); + m_xmlWriter->writeEndElement(); +} + +/** + * Write an enumerated array + * + * @param enumeratedArray + * The enumerated array + */ +void +SceneXmlStreamWriter::writeArrayEnumeratedType(const SceneEnumeratedTypeArray* enumeratedArray) +{ + CaretAssert(enumeratedArray); + if (enumeratedArray == NULL) { + return; + } + + const int32_t numberOfElements = enumeratedArray->getNumberOfArrayElements(); + + m_xmlWriter->writeStartElement(ELEMENT_OBJECT_ARRAY); + m_xmlWriter->writeAttribute(ATTRIBUTE_OBJECT_ARRAY_TYPE, + SceneObjectDataTypeEnum::toXmlName(enumeratedArray->getDataType())); + m_xmlWriter->writeAttribute(ATTRIBUTE_OBJECT_ARRAY_NAME, + enumeratedArray->getName()); + m_xmlWriter->writeAttribute(ATTRIBUTE_OBJECT_ARRAY_LENGTH, + QString::number(numberOfElements)); + + for (int32_t i = 0; i < numberOfElements; i++) { + m_xmlWriter->writeStartElement(ELEMENT_OBJECT_ARRAY_ELEMENT); + m_xmlWriter->writeAttribute(ATTRIBUTE_OBJECT_ARRAY_ELEMENT_INDEX, + QString::number(i)); + m_xmlWriter->writeCharacters(enumeratedArray->stringValue(i)); + m_xmlWriter->writeEndElement(); + } + m_xmlWriter->writeEndElement(); +} + +/** + * Write a primitive array + * + * @param primitiveArray + * The primitive array + */ +void +SceneXmlStreamWriter::writeArrayPrimitiveType(const ScenePrimitiveArray* primitiveArray) +{ + CaretAssert(primitiveArray); + if (primitiveArray == NULL) { + return; + } + + const int32_t numberOfElements = primitiveArray->getNumberOfArrayElements(); + + m_xmlWriter->writeStartElement(ELEMENT_OBJECT_ARRAY); + m_xmlWriter->writeAttribute(ATTRIBUTE_OBJECT_ARRAY_TYPE, + SceneObjectDataTypeEnum::toXmlName(primitiveArray->getDataType())); + m_xmlWriter->writeAttribute(ATTRIBUTE_OBJECT_ARRAY_NAME, + primitiveArray->getName()); + m_xmlWriter->writeAttribute(ATTRIBUTE_OBJECT_ARRAY_LENGTH, + QString::number(numberOfElements)); + + for (int32_t i = 0; i < numberOfElements; i++) { + m_xmlWriter->writeStartElement(ELEMENT_OBJECT_ARRAY_ELEMENT); + m_xmlWriter->writeAttribute(ATTRIBUTE_OBJECT_ARRAY_ELEMENT_INDEX, + QString::number(i)); + m_xmlWriter->writeCharacters(primitiveArray->stringValue(i)); + m_xmlWriter->writeEndElement(); + } + m_xmlWriter->writeEndElement(); +} + +/** + * Write a class array + * + * @param classArray + * The class array + */ +void +SceneXmlStreamWriter::writeArrayClass(const SceneClassArray* classArray) +{ + CaretAssert(classArray); + if (classArray == NULL) { + return; + } + + const int32_t numberOfElements = classArray->getNumberOfArrayElements(); + + m_xmlWriter->writeStartElement(ELEMENT_OBJECT_ARRAY); + m_xmlWriter->writeAttribute(ATTRIBUTE_OBJECT_ARRAY_TYPE, + SceneObjectDataTypeEnum::toXmlName(classArray->getDataType())); + m_xmlWriter->writeAttribute(ATTRIBUTE_OBJECT_ARRAY_NAME, + classArray->getName()); + m_xmlWriter->writeAttribute(ATTRIBUTE_OBJECT_ARRAY_LENGTH, + QString::number(numberOfElements)); + + for (int32_t i = 0; i < numberOfElements; i++) { + m_xmlWriter->writeStartElement(ELEMENT_OBJECT_ARRAY_ELEMENT); + m_xmlWriter->writeAttribute(ATTRIBUTE_OBJECT_ARRAY_ELEMENT_INDEX, + QString::number(i)); + writeSceneClass(classArray->getClassAtIndex(i)); + m_xmlWriter->writeEndElement(); + } + m_xmlWriter->writeEndElement(); +} + +/** + * Write a path name array + * + * @param pathNameArray + * The path name array + */ +void +SceneXmlStreamWriter::writeArrayPathName(const ScenePathNameArray* pathNameArray) +{ + CaretAssert(pathNameArray); + if (pathNameArray == NULL) { + return; + } + + const int32_t numberOfElements = pathNameArray->getNumberOfArrayElements(); + + m_xmlWriter->writeStartElement(ELEMENT_OBJECT_ARRAY); + m_xmlWriter->writeAttribute(ATTRIBUTE_OBJECT_ARRAY_TYPE, + SceneObjectDataTypeEnum::toXmlName(pathNameArray->getDataType())); + m_xmlWriter->writeAttribute(ATTRIBUTE_OBJECT_ARRAY_NAME, + pathNameArray->getName()); + m_xmlWriter->writeAttribute(ATTRIBUTE_OBJECT_ARRAY_LENGTH, + QString::number(numberOfElements)); + + for (int32_t i = 0; i < numberOfElements; i++) { + m_xmlWriter->writeStartElement(ELEMENT_OBJECT_ARRAY_ELEMENT); + m_xmlWriter->writeAttribute(ATTRIBUTE_OBJECT_ARRAY_ELEMENT_INDEX, + QString::number(i)); + const ScenePathName* scenePathName = pathNameArray->getScenePathNameAtIndex(i); + const QString path = scenePathName->getRelativePathToSceneFile(m_sceneFileName); + m_xmlWriter->writeCharacters(path); + m_xmlWriter->writeEndElement(); + } + m_xmlWriter->writeEndElement(); +} diff --git a/src/Scenes/SceneXmlStreamWriter.h b/src/Scenes/SceneXmlStreamWriter.h new file mode 100644 index 000000000..80084b6f5 --- /dev/null +++ b/src/Scenes/SceneXmlStreamWriter.h @@ -0,0 +1,103 @@ +#ifndef __SCENE_XML_STREAM_WRITER_H__ +#define __SCENE_XML_STREAM_WRITER_H__ + +/*LICENSE_START*/ +/* + * Copyright (C) 2019 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + + + +#include + +#include "SceneXmlStreamBase.h" + +class QXmlStreamWriter; + +namespace caret { + + class Scene; + class SceneClass; + class SceneClassArray; + class SceneEnumeratedType; + class SceneEnumeratedTypeArray; + class SceneObject; + class SceneObjectArray; + class SceneObjectMapIntegerKey; + class ScenePathName; + class ScenePathNameArray; + class ScenePrimitive; + class ScenePrimitiveArray; + + class SceneXmlStreamWriter : public SceneXmlStreamBase { + + public: + SceneXmlStreamWriter(); + + virtual ~SceneXmlStreamWriter(); + + SceneXmlStreamWriter(const SceneXmlStreamWriter&) = delete; + + SceneXmlStreamWriter& operator=(const SceneXmlStreamWriter&) = delete; + + void writeXML(QXmlStreamWriter* xmlWriter, + const Scene* scene, + const int32_t sceneIndex, + const AString& sceneFileName); + + // ADD_NEW_METHODS_HERE + + private: + void writeSceneClass(const SceneClass* sceneClass); + + void writeSceneObject(const SceneObject* sceneObject); + + void writeArrayObject(const SceneObjectArray* objectArray); + + void writeMapObject(const SceneObjectMapIntegerKey* objectMap); + + void writeSingleObject(const SceneObject* sceneObject); + + void writeEnumeratedType(const SceneEnumeratedType* sceneEnumeratedType); + + void writePathName(const ScenePathName* scenePathName); + + void writePrimitive(const ScenePrimitive* scenePrimitive); + + void writeArrayEnumeratedType(const SceneEnumeratedTypeArray* enumeratedArray); + + void writeArrayPrimitiveType(const ScenePrimitiveArray* primitiveArray); + + void writeArrayClass(const SceneClassArray* classArray); + + void writeArrayPathName(const ScenePathNameArray* pathNameArray); + + QXmlStreamWriter* m_xmlWriter = NULL; + + AString m_sceneFileName; + + // ADD_NEW_MEMBERS_HERE + + }; + +#ifdef __SCENE_XML_STREAM_WRITER_DECLARE__ + // +#endif // __SCENE_XML_STREAM_WRITER_DECLARE__ + +} // namespace +#endif //__SCENE_XML_STREAM_WRITER_H__ -- GitLab From 8f153c1cb7d407eb5b62b6748f0d8da8cb00671a Mon Sep 17 00:00:00 2001 From: John Harwell Date: Fri, 25 Jan 2019 17:06:59 -0600 Subject: [PATCH 174/427] WB-834 Macros: Since the macros are saved in XML, Qt's XML Stream Reader are used since the SAX reader is depreciated and Qt claims that the Stream Reader is faster than the SAX reader. And since macros need to be in the scene file, a stream reader and writer were added for Scene files and they are faster (For a 120MB file, the read time dropped from 5 to 2 seconds and writing dropped from 3.25, to 2.2. wb_view was build optimized). --- src/Common/DeveloperFlagsEnum.cxx | 72 +- src/Common/DeveloperFlagsEnum.h | 17 +- src/Common/WuQMacroCommand.cxx | 1 + src/Common/WuQMacroFile.cxx | 21 +- src/Common/WuQMacroGroup.cxx | 13 +- src/Common/WuQMacroGroupXmlStreamBase.h | 6 +- src/Common/WuQMacroGroupXmlStreamReader.cxx | 295 ++++---- src/Common/WuQMacroGroupXmlStreamReader.h | 33 +- src/Common/WuQMacroGroupXmlStreamWriter.cxx | 4 +- src/Files/CMakeLists.txt | 2 + src/Files/SceneFile.cxx | 308 +++++++- src/Files/SceneFile.h | 8 + src/Files/SceneFileXmlStreamFormatTester.cxx | 327 ++++++++ src/Files/SceneFileXmlStreamFormatTester.h | 77 ++ src/Files/SceneFileXmlStreamReader.cxx | 249 ++++++ src/Files/SceneFileXmlStreamReader.h | 24 +- src/Files/SceneFileXmlStreamWriter.cxx | 4 +- src/FilesBase/GiftiMetaData.cxx | 10 + src/FilesBase/GiftiMetaData.h | 3 + src/GuiQt/BrainBrowserWindow.cxx | 34 +- src/GuiQt/BrainOpenGLWidget.cxx | 2 +- src/GuiQt/WuQMacroDialog.cxx | 3 + src/Scenes/SceneClass.cxx | 12 + src/Scenes/SceneClass.h | 2 + src/Scenes/SceneClassArray.cxx | 12 + src/Scenes/SceneClassArray.h | 4 +- src/Scenes/SceneEnumeratedType.cxx | 13 + src/Scenes/SceneEnumeratedType.h | 2 + src/Scenes/SceneEnumeratedTypeArray.cxx | 12 + src/Scenes/SceneEnumeratedTypeArray.h | 2 + src/Scenes/SceneInfoSaxReader.cxx | 12 +- src/Scenes/SceneInfoXmlStreamBase.h | 12 +- src/Scenes/SceneInfoXmlStreamReader.cxx | 90 +++ src/Scenes/SceneInfoXmlStreamReader.h | 10 +- src/Scenes/SceneInfoXmlStreamWriter.cxx | 6 +- src/Scenes/SceneObject.cxx | 73 ++ src/Scenes/SceneObject.h | 12 + src/Scenes/SceneObjectArray.cxx | 59 ++ src/Scenes/SceneObjectArray.h | 10 + src/Scenes/SceneObjectMapIntegerKey.cxx | 12 + src/Scenes/SceneObjectMapIntegerKey.h | 2 + src/Scenes/ScenePathName.cxx | 12 + src/Scenes/ScenePathName.h | 2 + src/Scenes/ScenePathNameArray.cxx | 12 + src/Scenes/ScenePathNameArray.h | 2 + src/Scenes/ScenePrimitive.cxx | 12 + src/Scenes/ScenePrimitive.h | 2 + src/Scenes/ScenePrimitiveArray.cxx | 12 + src/Scenes/ScenePrimitiveArray.h | 2 + src/Scenes/SceneXmlStreamReader.cxx | 756 +++++++++++++++++++ src/Scenes/SceneXmlStreamReader.h | 25 +- 51 files changed, 2478 insertions(+), 229 deletions(-) create mode 100644 src/Files/SceneFileXmlStreamFormatTester.cxx create mode 100644 src/Files/SceneFileXmlStreamFormatTester.h diff --git a/src/Common/DeveloperFlagsEnum.cxx b/src/Common/DeveloperFlagsEnum.cxx index 4cae6e410..2781a0225 100644 --- a/src/Common/DeveloperFlagsEnum.cxx +++ b/src/Common/DeveloperFlagsEnum.cxx @@ -76,18 +76,22 @@ using namespace caret; * Name of enumerated value. * @param guiName * User-friendly name for use in user-interface. + * @param checkable + * Checkable status (NO, YES) * @param defaultValue * Default value for flag */ DeveloperFlagsEnum::DeveloperFlagsEnum(const Enum enumValue, const AString& name, const AString& guiName, + const CheckableEnum checkable, const bool defaultValue) { this->enumValue = enumValue; this->integerCode = integerCodeCounter++; this->name = name; this->guiName = guiName; + this->checkable = checkable; this->flagStatus = defaultValue; } @@ -109,14 +113,44 @@ DeveloperFlagsEnum::initialize() } initializedFlag = true; - enumData.push_back(DeveloperFlagsEnum(DEVELOPER_FLAG_UNUSED, - "DEVELOPER_FLAG_UNUSED", - "Developer Flag Unused", - false)); - enumData.push_back(DeveloperFlagsEnum(DEVELOPER_FLAG_FLIP_PALETTE_NOT_DATA, - "DEVELOPER_FLAG_FLIP_PALETTE_NOT_DATA", - "Flip Palette Not Data", - false)); + std::vector checkableItems; + checkableItems.push_back(DeveloperFlagsEnum(DEVELOPER_FLAG_UNUSED, + "DEVELOPER_FLAG_UNUSED", + "Developer Flag Unused", + CheckableEnum::YES, + false)); + checkableItems.push_back(DeveloperFlagsEnum(DEVELOPER_FLAG_FLIP_PALETTE_NOT_DATA, + "DEVELOPER_FLAG_FLIP_PALETTE_NOT_DATA", + "Flip Palette Not Data", + CheckableEnum::YES, + false)); + checkableItems.push_back(DeveloperFlagsEnum(DEVELOPER_FLAG_NEW_SCENE_FILE_READ_WRITE, + "DEVELOPER_FLAG_NEW_SCENE_FILE_READ_WRITE", + "Use New Scene File Reader and Writer", + CheckableEnum::YES, + true)); + + std::vector notCheckableItems; + notCheckableItems.push_back(DeveloperFlagsEnum(DEVELOPER_FLAG_TEST_SCENE_FILE_READ_WRITE, + "DEVELOPER_FLAG_TEST_SCENE_FILE_READ_WRITE", + "Test Scene File Xml Stream Reading/Writing...", + CheckableEnum::NO, + false)); + notCheckableItems.push_back(DeveloperFlagsEnum(DEVELOPER_FLAG_TIME_SCENE_FILE_READ_WRITE, + "DEVELOPER_FLAG_TIME_SCENE_FILE_READ_WRITE", + "Time Scene File Xml Stream Timing...", + CheckableEnum::NO, + false)); + notCheckableItems.push_back(DeveloperFlagsEnum(DEVELOPER_FLAG_TEST_ALL_SCENE_FILES_IN_DIRECTORY, + "DEVELOPER_FLAG_TEST_ALL_SCENE_FILES_IN_DIRECTORY", + "Test Xml Stream Read/Write for All Scene Files in Directory...", + CheckableEnum::NO, + false)); + + enumData.insert(enumData.end(), + checkableItems.begin(), checkableItems.end()); + enumData.insert(enumData.end(), + notCheckableItems.begin(), notCheckableItems.end()); } /** @@ -407,4 +441,26 @@ DeveloperFlagsEnum::setFlag(const Enum enumValue, enumInstance->flagStatus = flagStatus; } +/** + * @return True if the developer flag is checkable + */ +bool +DeveloperFlagsEnum::isCheckable(const Enum enumValue) +{ + bool checkableStatus(false); + + if (initializedFlag == false) initialize(); + DeveloperFlagsEnum* enumInstance = findData(enumValue); + switch (enumInstance->checkable) { + case CheckableEnum::NO: + checkableStatus = false; + break; + case CheckableEnum::YES: + checkableStatus = true; + break; + } + + return checkableStatus; +} + diff --git a/src/Common/DeveloperFlagsEnum.h b/src/Common/DeveloperFlagsEnum.h index 2a352066e..63a32fa3d 100644 --- a/src/Common/DeveloperFlagsEnum.h +++ b/src/Common/DeveloperFlagsEnum.h @@ -36,7 +36,11 @@ public: */ enum Enum { DEVELOPER_FLAG_UNUSED, - DEVELOPER_FLAG_FLIP_PALETTE_NOT_DATA + DEVELOPER_FLAG_FLIP_PALETTE_NOT_DATA, + DEVELOPER_FLAG_NEW_SCENE_FILE_READ_WRITE, + DEVELOPER_FLAG_TEST_SCENE_FILE_READ_WRITE, + DEVELOPER_FLAG_TIME_SCENE_FILE_READ_WRITE, + DEVELOPER_FLAG_TEST_ALL_SCENE_FILES_IN_DIRECTORY }; ~DeveloperFlagsEnum(); @@ -64,10 +68,18 @@ public: static void setFlag(const Enum enumValue, const bool flagStatus); + static bool isCheckable(const Enum enumValue); + private: + enum class CheckableEnum { + NO, + YES + }; + DeveloperFlagsEnum(const Enum enumValue, const AString& name, const AString& guiName, + const CheckableEnum checkable, const bool defaultValue); static DeveloperFlagsEnum* findData(const Enum enumValue); @@ -98,6 +110,9 @@ private: /** Flag status */ bool flagStatus; + + /** Checkable status */ + CheckableEnum checkable; }; #ifdef __DEVELOPER_FLAGS_ENUM_DECLARE__ diff --git a/src/Common/WuQMacroCommand.cxx b/src/Common/WuQMacroCommand.cxx index 8e84f85a0..17be63c2b 100644 --- a/src/Common/WuQMacroCommand.cxx +++ b/src/Common/WuQMacroCommand.cxx @@ -318,6 +318,7 @@ WuQMacroCommand::type() const void WuQMacroCommand::copyHelperWuQMacroCommand(const WuQMacroCommand& obj) { + m_descriptiveName = obj.m_descriptiveName; m_classType = obj.m_classType; m_objectName = obj.m_objectName; m_dataValue = obj.m_dataValue; diff --git a/src/Common/WuQMacroFile.cxx b/src/Common/WuQMacroFile.cxx index 957bffffa..4c359f463 100644 --- a/src/Common/WuQMacroFile.cxx +++ b/src/Common/WuQMacroFile.cxx @@ -220,22 +220,17 @@ WuQMacroFile::readFile(const AString& filename) QTextStream textStream(&file); const QString fileContentString = textStream.readAll(); + QString errorMessage; WuQMacroGroupXmlStreamReader reader; - reader.readFromString(fileContentString, - m_macroGroup.get()); - file.close(); - if (reader.hasError()) { - throw DataFileException(reader.getErrorMessage()); + if ( ! reader.readFromString(fileContentString, + m_macroGroup.get(), + errorMessage)) { + file.close(); + throw DataFileException(errorMessage); } + setFileName(filename); - clearModified(); - - if (reader.hasWarnings()) { - CaretLogWarning("When reading " - + filename - + ": " - + reader.getWarningMessage()); - } + clearModified(); } else { throw DataFileException("Unable to open file for writing: " diff --git a/src/Common/WuQMacroGroup.cxx b/src/Common/WuQMacroGroup.cxx index 810cfd27a..c319c4098 100644 --- a/src/Common/WuQMacroGroup.cxx +++ b/src/Common/WuQMacroGroup.cxx @@ -535,17 +535,12 @@ WuQMacroGroup::readXmlFromStringOld(const QString& xmlString, nonFatalWarningMessageOut.clear(); WuQMacroGroupXmlStreamReader reader; - reader.readFromString(xmlString, - this); - if (reader.hasError()) { - errorMessageOut = reader.getErrorMessage(); + if ( ! reader.readFromString(xmlString, + this, + errorMessageOut)) { return false; } - else { - if (reader.hasWarnings()) { - nonFatalWarningMessageOut = reader.getWarningMessage(); - } - } + clearModified(); return true; diff --git a/src/Common/WuQMacroGroupXmlStreamBase.h b/src/Common/WuQMacroGroupXmlStreamBase.h index 4ac290488..d1c85052c 100644 --- a/src/Common/WuQMacroGroupXmlStreamBase.h +++ b/src/Common/WuQMacroGroupXmlStreamBase.h @@ -48,8 +48,6 @@ namespace caret { virtual AString toString() const; - protected: - static const QString ATTRIBUTE_NAME; static const QString ATTRIBUTE_OBJECT_CLASS; static const QString ATTRIBUTE_OBJECT_DATA_TYPE; @@ -80,9 +78,9 @@ namespace caret { static const QString ELEMENT_DESCRIPTION; static const QString ELEMENT_MACRO; static const QString ELEMENT_MACRO_COMMAND; + static const QString ELEMENT_MACRO_COMMAND_TOOL_TIP; static const QString ELEMENT_MACRO_GROUP; static const QString ELEMENT_MOUSE_EVENT_INFO; - static const QString ELEMENT_TOOL_TIP; static const QString VALUE_BOOL_FALSE; static const QString VALUE_BOOL_TRUE; static const QString VALUE_VERSION_ONE; @@ -121,9 +119,9 @@ namespace caret { const QString WuQMacroGroupXmlStreamBase::ELEMENT_DESCRIPTION = "Description"; const QString WuQMacroGroupXmlStreamBase::ELEMENT_MACRO = "Macro"; const QString WuQMacroGroupXmlStreamBase::ELEMENT_MACRO_COMMAND = "MacroCommand"; + const QString WuQMacroGroupXmlStreamBase::ELEMENT_MACRO_COMMAND_TOOL_TIP = "ToolTip"; const QString WuQMacroGroupXmlStreamBase::ELEMENT_MACRO_GROUP = "MacroGroup"; const QString WuQMacroGroupXmlStreamBase::ELEMENT_MOUSE_EVENT_INFO = "MouseEventInfo"; - const QString WuQMacroGroupXmlStreamBase::ELEMENT_TOOL_TIP = "ToolTip"; const QString WuQMacroGroupXmlStreamBase::VALUE_BOOL_FALSE = "false"; const QString WuQMacroGroupXmlStreamBase::VALUE_BOOL_TRUE = "true"; diff --git a/src/Common/WuQMacroGroupXmlStreamReader.cxx b/src/Common/WuQMacroGroupXmlStreamReader.cxx index 60c3eaae5..864d312ed 100644 --- a/src/Common/WuQMacroGroupXmlStreamReader.cxx +++ b/src/Common/WuQMacroGroupXmlStreamReader.cxx @@ -28,6 +28,7 @@ #include #include "CaretAssert.h" +#include "CaretLogger.h" #include "WuQMacro.h" #include "WuQMacroCommand.h" #include "WuQMacroGroup.h" @@ -65,115 +66,159 @@ WuQMacroGroupXmlStreamReader::~WuQMacroGroupXmlStreamReader() * The string containing XML * @param macroGroup * The macro group + * @param errorMessageOut + * Output error message + * @return + * True if successful, else false is returned and description in errorMessageOut */ -void +bool WuQMacroGroupXmlStreamReader::readFromString(const QString& xmlString, - WuQMacroGroup* macroGroup) + WuQMacroGroup* macroGroup, + QString& errorMessageOut) { + errorMessageOut.clear(); CaretAssert(macroGroup); macroGroup->clear(); + if (xmlString.isEmpty()) { - m_xmlStreamReader->raiseError("String that should contain XML is empty."); - return; + errorMessageOut = "String that should contain XML is empty."; + return false; } - m_xmlStreamReader.reset(new QXmlStreamReader(xmlString)); + QXmlStreamReader xmlReader(xmlString); - if (m_xmlStreamReader->atEnd()) { - m_xmlStreamReader->raiseError("At end when trying to start reading. Appears to have no XML content."); + if (xmlReader.atEnd()) { + xmlReader.raiseError("At end when trying to start reading. Appears to have no XML content."); } else { - if (m_xmlStreamReader->readNextStartElement()) { - const QStringRef groupElement = m_xmlStreamReader->name(); - if (groupElement == ELEMENT_MACRO_GROUP) { - const QXmlStreamAttributes attributes = m_xmlStreamReader->attributes(); - const QStringRef name = attributes.value(ATTRIBUTE_NAME); - const QStringRef versionText = attributes.value(ATTRIBUTE_VERSION); - QString uniqueIdentifier = attributes.value(ATTRIBUTE_UNIQUE_IDENTIFIER).toString(); - if (uniqueIdentifier.isEmpty()) { - addToWarnings(ELEMENT_MACRO_GROUP - + " is missing attribute or value is empty: " - + ATTRIBUTE_UNIQUE_IDENTIFIER); - } - if (versionText.isEmpty()) { - m_xmlStreamReader->raiseError(ATTRIBUTE_VERSION - + " is missing from element " - + ELEMENT_MACRO_GROUP); - } - else if (versionText == VALUE_VERSION_ONE) { - macroGroup->setName(name.toString()); - macroGroup->setUniqueIdentifier(uniqueIdentifier); - readVersionOne(macroGroup); - } - else { - m_xmlStreamReader->raiseError(ATTRIBUTE_VERSION - + "=" - + versionText.toString() - + " is not supported by " - + ELEMENT_MACRO_GROUP - + ". Check for software update."); - } - } - else { - m_xmlStreamReader->raiseError("First XML element is \"" - + m_xmlStreamReader->name().toString() - + "\" but should be \"" - + ELEMENT_MACRO_GROUP - + "\""); - } - } - else { - m_xmlStreamReader->raiseError("No XML elements found"); - } + xmlReader.readNextStartElement(); + readMacroGroup(xmlReader, + macroGroup); } - if (m_xmlStreamReader->hasError()) { + if (xmlReader.hasError()) { + errorMessageOut = xmlReader.errorString(); macroGroup->clear(); + return false; } macroGroup->clearModified(); + + return true; +} + +/** + * Read macro group from the given XML stream reader. It assumes that + * the start element for the macro group has already been read and is + * the current element. If xmlReader.hasError() is set after this + * method is called, there was an error reading the macro group. + * + * @param xmlReader + * The XML stream reader + * @param macroGroup + * The macro group + */ +void +WuQMacroGroupXmlStreamReader::readMacroGroup(QXmlStreamReader& xmlReader, + WuQMacroGroup* macroGroup) +{ + if (xmlReader.name() == ELEMENT_MACRO_GROUP) { + const QXmlStreamAttributes attributes = xmlReader.attributes(); + const QStringRef name = attributes.value(ATTRIBUTE_NAME); + const QStringRef versionText = attributes.value(ATTRIBUTE_VERSION); + QString uniqueIdentifier = attributes.value(ATTRIBUTE_UNIQUE_IDENTIFIER).toString(); + if (uniqueIdentifier.isEmpty()) { + addToWarnings(xmlReader, + ELEMENT_MACRO_GROUP + + " is missing attribute or value is empty: " + + ATTRIBUTE_UNIQUE_IDENTIFIER); + } + if (versionText.isEmpty()) { + xmlReader.raiseError(ATTRIBUTE_VERSION + + " is missing from element " + + ELEMENT_MACRO_GROUP); + } + else if (versionText == VALUE_VERSION_ONE) { + macroGroup->setName(name.toString()); + macroGroup->setUniqueIdentifier(uniqueIdentifier); + + readVersionOne(xmlReader, + macroGroup); + } + else { + xmlReader.raiseError(ATTRIBUTE_VERSION + + "=" + + versionText.toString() + + " is not supported by " + + ELEMENT_MACRO_GROUP + + ". Check for software update."); + } + } + else { + xmlReader.raiseError("Element should be \"" + + ELEMENT_MACRO_GROUP + + "\" but is \"" + + xmlReader.text().toString() + + "\" while reading MacroGroup"); + } + + if ( ! m_warningMessage.isEmpty()) { + CaretLogWarning("Reading Macro's Warnings: " + + m_warningMessage); + m_warningMessage.clear(); + } } /** * Read version one of macro group * + * @param xmlReader + * The XML stream reader * @param macroGroup * The macro group */ void -WuQMacroGroupXmlStreamReader::readVersionOne(WuQMacroGroup* macroGroup) +WuQMacroGroupXmlStreamReader::readVersionOne(QXmlStreamReader& xmlReader, + WuQMacroGroup* macroGroup) { CaretAssert(macroGroup); WuQMacro* macro(NULL); WuQMacroCommand* macroCommand(NULL); - while ( ! m_xmlStreamReader->atEnd()) { - m_xmlStreamReader->readNext(); - if (m_xmlStreamReader->isStartElement()) { - const QString elementName = m_xmlStreamReader->name().toString(); + /* + * Gets set when ending scene info directory element is read + */ + bool endElementFound(false); + + while ( (! xmlReader.atEnd()) + && ( ! endElementFound)) { + xmlReader.readNext(); + if (xmlReader.isStartElement()) { + const QString elementName = xmlReader.name().toString(); if (elementName == ELEMENT_MACRO) { - macro = readMacroVersionOne(); + macro = readMacroVersionOne(xmlReader); } else if (elementName == ELEMENT_DESCRIPTION) { - const QString text = m_xmlStreamReader->readElementText(); + const QString text = xmlReader.readElementText(); if (macro != NULL) { macro->setDescription(text); } } else if (elementName == ELEMENT_MACRO_COMMAND) { - macroCommand = readMacroCommandAttributesVersionOne(); + macroCommand = readMacroCommandAttributesVersionOne(xmlReader); } else if (elementName == ELEMENT_MOUSE_EVENT_INFO) { - WuQMacroMouseEventInfo* mouseEventInfo = readMacroMouseEventInfo(); + WuQMacroMouseEventInfo* mouseEventInfo = readMacroMouseEventInfo(xmlReader); if (mouseEventInfo != NULL) { if (macroCommand != NULL) { macroCommand->setMouseEventInfo(mouseEventInfo); } else { - addToWarnings("Read WuQMacroMouseEventInfo but macroCommand is NULL"); + addToWarnings(xmlReader, + "Read WuQMacroMouseEventInfo but macroCommand is NULL"); delete mouseEventInfo; mouseEventInfo = NULL; } @@ -188,21 +233,22 @@ WuQMacroGroupXmlStreamReader::readVersionOne(WuQMacroGroup* macroGroup) } } } - else if (elementName == ELEMENT_TOOL_TIP) { - const QString text = m_xmlStreamReader->readElementText(); + else if (elementName == ELEMENT_MACRO_COMMAND_TOOL_TIP) { + const QString text = xmlReader.readElementText(); if (macroCommand != NULL) { macroCommand->setToolTip(text); } } else { - addToWarnings("Unexpected element=" + addToWarnings(xmlReader, + "Unexpected element=" + elementName + "\""); - m_xmlStreamReader->skipCurrentElement(); + xmlReader.skipCurrentElement(); } } - else if (m_xmlStreamReader->isEndElement()) { - const QString elementName = m_xmlStreamReader->name().toString(); + else if (xmlReader.isEndElement()) { + const QString elementName = xmlReader.name().toString(); if (elementName == ELEMENT_MACRO) { if (macro != NULL) { macroGroup->addMacro(macro); @@ -216,10 +262,9 @@ WuQMacroGroupXmlStreamReader::readVersionOne(WuQMacroGroup* macroGroup) } macroCommand = NULL; } - } - - if (m_xmlStreamReader->hasError()) { - break; + else if (elementName == ELEMENT_MACRO_GROUP) { + endElementFound = true; + } } } } @@ -227,25 +272,29 @@ WuQMacroGroupXmlStreamReader::readVersionOne(WuQMacroGroup* macroGroup) /** * Read version one of macro * + * @param xmlReader + * The XML stream reader * @return The macro */ WuQMacro* -WuQMacroGroupXmlStreamReader::readMacroVersionOne() +WuQMacroGroupXmlStreamReader::readMacroVersionOne(QXmlStreamReader& xmlReader) { WuQMacro* macro(NULL); - const QXmlStreamAttributes attributes = m_xmlStreamReader->attributes(); + const QXmlStreamAttributes attributes = xmlReader.attributes(); QString macroName = attributes.value(ATTRIBUTE_NAME).toString(); QString shortCutKeyString = attributes.value(ATTRIBUTE_SHORT_CUT_KEY).toString(); if (shortCutKeyString.isEmpty()) { shortCutKeyString = WuQMacroShortCutKeyEnum::toName(WuQMacroShortCutKeyEnum::Key_None); - addToWarnings(ELEMENT_MACRO + addToWarnings(xmlReader, + ELEMENT_MACRO + " is missing attribute or value is empty: " + ATTRIBUTE_SHORT_CUT_KEY); } QString uniqueIdentifier = attributes.value(ATTRIBUTE_UNIQUE_IDENTIFIER).toString(); if (uniqueIdentifier.isEmpty()) { - addToWarnings(ELEMENT_MACRO + addToWarnings(xmlReader, + ELEMENT_MACRO + " is missing attribute or value is empty: " + ATTRIBUTE_UNIQUE_IDENTIFIER); } @@ -255,7 +304,8 @@ WuQMacroGroupXmlStreamReader::readMacroVersionOne() &validShortCutKey); if ( ! validShortCutKey) { shortCutKey = WuQMacroShortCutKeyEnum::Key_None; - addToWarnings(ELEMENT_MACRO + addToWarnings(xmlReader, + ELEMENT_MACRO + " attribute " + ATTRIBUTE_SHORT_CUT_KEY + " has invalid value " @@ -264,7 +314,8 @@ WuQMacroGroupXmlStreamReader::readMacroVersionOne() if (macroName.isEmpty()) { - addToWarnings(ELEMENT_MACRO + addToWarnings(xmlReader, + ELEMENT_MACRO + " is missing attribute or value is empty: " + ATTRIBUTE_NAME); static uint32_t missingCounter = 1; @@ -283,14 +334,16 @@ WuQMacroGroupXmlStreamReader::readMacroVersionOne() /** * Read version one of macro command attributes * + * @param xmlReader + * The XML stream reader * @Return The macro group */ WuQMacroCommand* -WuQMacroGroupXmlStreamReader::readMacroCommandAttributesVersionOne() +WuQMacroGroupXmlStreamReader::readMacroCommandAttributesVersionOne(QXmlStreamReader& xmlReader) { WuQMacroCommand* macroCommand(NULL); - const QXmlStreamAttributes attributes = m_xmlStreamReader->attributes(); + const QXmlStreamAttributes attributes = xmlReader.attributes(); const QStringRef objectName = attributes.value(ATTRIBUTE_NAME); const QStringRef dataTypeString = attributes.value(ATTRIBUTE_OBJECT_DATA_TYPE); const QStringRef delayString = attributes.value(ATTRIBUTE_DELAY); @@ -306,7 +359,8 @@ WuQMacroGroupXmlStreamReader::readMacroCommandAttributesVersionOne() if (dataTypeTwoString.isEmpty()) es.append(ATTRIBUTE_OBJECT_DATA_TYPE + " "); if (classString.isEmpty()) es.append(ATTRIBUTE_OBJECT_CLASS + " "); if ( ! es.isEmpty()) { - addToWarnings(ELEMENT_MACRO_COMMAND + addToWarnings(xmlReader, + ELEMENT_MACRO_COMMAND + " is missing attribute(s): " + es); return NULL; @@ -348,7 +402,8 @@ WuQMacroGroupXmlStreamReader::readMacroCommandAttributesVersionOne() if ( ! es.isEmpty()) { - addToWarnings(ELEMENT_MACRO_COMMAND + addToWarnings(xmlReader, + ELEMENT_MACRO_COMMAND + " has invalid attributes: " + es); return NULL; @@ -449,12 +504,14 @@ WuQMacroGroupXmlStreamReader::readMacroCommandAttributesVersionOne() } /** + * @param xmlReader + * The XML stream reader * @return Read and return the mouse event information */ WuQMacroMouseEventInfo* -WuQMacroGroupXmlStreamReader::readMacroMouseEventInfo() +WuQMacroGroupXmlStreamReader::readMacroMouseEventInfo(QXmlStreamReader& xmlReader) { - const QXmlStreamAttributes attributes = m_xmlStreamReader->attributes(); + const QXmlStreamAttributes attributes = xmlReader.attributes(); const QString mouseEventTypeString = attributes.value(ATTRIBUTE_MOUSE_EVENT_TYPE).toString(); const QString xLocalString = attributes.value(ATTRIBUTE_MOUSE_LOCAL_X).toString(); const QString yLocalString = attributes.value(ATTRIBUTE_MOUSE_LOCAL_Y).toString(); @@ -472,7 +529,8 @@ WuQMacroGroupXmlStreamReader::readMacroMouseEventInfo() if (widgetWidthString.isEmpty()) es.append(ATTRIBUTE_MOUSE_WIDGET_WIDTH + " "); if (widgetHeightString.isEmpty()) es.append(ATTRIBUTE_MOUSE_WIDGET_HEIGHT + " "); if ( ! es.isEmpty()) { - addToWarnings(ELEMENT_MOUSE_EVENT_INFO + addToWarnings(xmlReader, + ELEMENT_MOUSE_EVENT_INFO + " is missing required attribute(s): " + es); return NULL; @@ -482,7 +540,8 @@ WuQMacroGroupXmlStreamReader::readMacroMouseEventInfo() const WuQMacroMouseEventTypeEnum::Enum mouseEventType = WuQMacroMouseEventTypeEnum::fromName(mouseEventTypeString, &validMouseEventTypeFlag); if ( ! validMouseEventTypeFlag) { - addToWarnings(mouseEventTypeString + addToWarnings(xmlReader, + mouseEventTypeString + " is not valid for attribute " + ATTRIBUTE_MOUSE_EVENT_TYPE); return NULL; @@ -500,7 +559,7 @@ WuQMacroGroupXmlStreamReader::readMacroMouseEventInfo() yLocalString.toInt()); } - QString xyString = m_xmlStreamReader->readElementText(); + QString xyString = xmlReader.readElementText(); if ( ! xyString.isEmpty()) { QTextStream stream(&xyString); while ( ! stream.atEnd()) { @@ -516,69 +575,29 @@ WuQMacroGroupXmlStreamReader::readMacroMouseEventInfo() return mouseInfo; } +/** + * Add to the warning message. Warnings are used to skip over invalid + * elements instead of declaring the entire XML invalid. + * + * @param xmlReader + * The XML stream reader + * @param warning + * The warning message + */ void -WuQMacroGroupXmlStreamReader::addToWarnings(const QString& warning) +WuQMacroGroupXmlStreamReader::addToWarnings(QXmlStreamReader& xmlReader, + const QString& warning) { if ( ! m_warningMessage.isEmpty()) { m_warningMessage.append("\n"); } m_warningMessage.append("Line=" - + QString::number(m_xmlStreamReader->lineNumber()) + + QString::number(xmlReader.lineNumber()) + ", Column=" - + QString::number(m_xmlStreamReader->columnNumber()) + + QString::number(xmlReader.columnNumber()) + ": " + warning); } -/** - * @return True if there are non-fatal warnings - */ -bool -WuQMacroGroupXmlStreamReader::hasWarnings() const -{ - return ( ! m_warningMessage.isEmpty()); -} - -/** - * @return The warning message - */ -QString -WuQMacroGroupXmlStreamReader::getWarningMessage() const -{ - return m_warningMessage; -} - -/** - * @return True if there is an error - */ -bool -WuQMacroGroupXmlStreamReader::hasError() const -{ - if (m_xmlStreamReader != NULL) { - return m_xmlStreamReader->hasError(); - } - return false; -} - -/** - * @return The error message - */ -QString -WuQMacroGroupXmlStreamReader::getErrorMessage() const -{ - QString errorMessage; - - if (m_xmlStreamReader != NULL) { - errorMessage.append("Line Number=" - + QString::number(m_xmlStreamReader->lineNumber()) - + ", Column Number=" - + QString::number(m_xmlStreamReader->columnNumber()) - + ". "); - errorMessage.append(m_xmlStreamReader->errorString()); - } - - return errorMessage; -} - diff --git a/src/Common/WuQMacroGroupXmlStreamReader.h b/src/Common/WuQMacroGroupXmlStreamReader.h index ba0698f8e..c2623e096 100644 --- a/src/Common/WuQMacroGroupXmlStreamReader.h +++ b/src/Common/WuQMacroGroupXmlStreamReader.h @@ -29,7 +29,6 @@ class QXmlStreamReader; - namespace caret { class WuQMacro; @@ -47,35 +46,31 @@ namespace caret { WuQMacroGroupXmlStreamReader(const WuQMacroGroupXmlStreamReader&) = delete; WuQMacroGroupXmlStreamReader& operator=(const WuQMacroGroupXmlStreamReader&) = delete; - - bool hasError() const; - - QString getErrorMessage() const; - - bool hasWarnings() const; - - QString getWarningMessage() const; - void readFromString(const QString& xmlString, - WuQMacroGroup* macroGroup); + bool readFromString(const QString& xmlString, + WuQMacroGroup* macroGroup, + QString& errorMessageOut); + void readMacroGroup(QXmlStreamReader& xmlReader, + WuQMacroGroup* macroGroup); + // ADD_NEW_METHODS_HERE private: - WuQMacroMouseEventInfo* readMacroMouseEventInfo(); + WuQMacroMouseEventInfo* readMacroMouseEventInfo(QXmlStreamReader& xmlReader); - void readVersionOne(WuQMacroGroup* macroGroup); + void readVersionOne(QXmlStreamReader& xmlReader, + WuQMacroGroup* macroGroup); - WuQMacro* readMacroVersionOne(); + WuQMacro* readMacroVersionOne(QXmlStreamReader& xmlReader); - WuQMacroCommand* readMacroCommandAttributesVersionOne(); + WuQMacroCommand* readMacroCommandAttributesVersionOne(QXmlStreamReader& xmlReader); - WuQMacroCommand* readMacroCommandVersionOne(); + WuQMacroCommand* readMacroCommandVersionOne(QXmlStreamReader& xmlReader); - void addToWarnings(const QString& warning); + void addToWarnings(QXmlStreamReader& xmlReader, + const QString& warning); - std::unique_ptr m_xmlStreamReader; - QString m_warningMessage; // ADD_NEW_MEMBERS_HERE diff --git a/src/Common/WuQMacroGroupXmlStreamWriter.cxx b/src/Common/WuQMacroGroupXmlStreamWriter.cxx index 37f851eb3..d7e04b82f 100644 --- a/src/Common/WuQMacroGroupXmlStreamWriter.cxx +++ b/src/Common/WuQMacroGroupXmlStreamWriter.cxx @@ -73,7 +73,7 @@ WuQMacroGroupXmlStreamWriter::writeXml(QXmlStreamWriter* xmlWriter, CaretAssert(macroGroup); if (macroGroup->getNumberOfMacros() <= 0) { - xmlWriter->writeEmptyElement(ELEMENT_MACRO_GROUP); +// xmlWriter->writeEmptyElement(ELEMENT_MACRO_GROUP); return; } @@ -283,7 +283,7 @@ WuQMacroGroupXmlStreamWriter::writeMacroCommandToXML(const WuQMacroCommand* macr const QString toolTip = macroCommand->getObjectToolTip(); if (toolTip != NULL) { - m_xmlStreamWriter->writeTextElement(ELEMENT_TOOL_TIP, + m_xmlStreamWriter->writeTextElement(ELEMENT_MACRO_COMMAND_TOOL_TIP, toolTip); } m_xmlStreamWriter->writeEndElement(); diff --git a/src/Files/CMakeLists.txt b/src/Files/CMakeLists.txt index d94a498ab..bf381d784 100755 --- a/src/Files/CMakeLists.txt +++ b/src/Files/CMakeLists.txt @@ -128,6 +128,7 @@ SpecFile.h SpecFileDataFileTypeGroup.h SpecFileDataFile.h SpecFileSaxReader.h +SceneFileXmlStreamFormatTester.h StudyMetaDataLink.h StudyMetaDataLinkSet.h StudyMetaDataLinkSetSaxReader.h @@ -252,6 +253,7 @@ SceneDataFileInfo.cxx SceneFile.cxx SceneFileSaxReader.cxx SceneFileXmlStreamBase.cxx +SceneFileXmlStreamFormatTester.cxx SceneFileXmlStreamReader.cxx SceneFileXmlStreamWriter.cxx SignedDistanceHelper.cxx diff --git a/src/Files/SceneFile.cxx b/src/Files/SceneFile.cxx index 51b3eec03..f9b2aad0d 100644 --- a/src/Files/SceneFile.cxx +++ b/src/Files/SceneFile.cxx @@ -34,6 +34,7 @@ #include "CaretLogger.h" #include "DataFileContentInformation.h" #include "DataFileException.h" +#include "DeveloperFlagsEnum.h" #include "FileAdapter.h" #include "FileInformation.h" #include "GiftiMetaData.h" @@ -45,6 +46,7 @@ #include "SceneInfo.h" #include "ScenePathName.h" #include "SceneXmlElements.h" +#include "SceneFileXmlStreamReader.h" #include "SceneFileXmlStreamWriter.h" #include "SceneWriterXml.h" #include "SpecFile.h" @@ -604,6 +606,80 @@ SceneFile::readFile(const AString& filenameIn) checkFileReadability(filename); this->setFileName(filename); + + if (DeveloperFlagsEnum::isFlag(DeveloperFlagsEnum::DEVELOPER_FLAG_NEW_SCENE_FILE_READ_WRITE)) { + try { + SceneFileXmlStreamReader streamReader; + streamReader.readFile(filename, + this); + } + catch (const DataFileException& e) { + DataFileException dfe(filename, + e.whatString()); + CaretLogThrowing(dfe); + throw dfe; + } + + } + else { + SceneFileSaxReader saxReader(this, + filename); + std::auto_ptr parser(XmlSaxParser::createXmlParser()); + try { + parser->parseFile(filename, &saxReader); + } + catch (const XmlSaxParserException& e) { + clear(); + this->setFileName(""); + + int lineNum = e.getLineNumber(); + int colNum = e.getColumnNumber(); + + AString msg = "Parse Error while reading:"; + + if ((lineNum >= 0) && (colNum >= 0)) { + msg += (" line/col (" + + AString::number(e.getLineNumber()) + + "/" + + AString::number(e.getColumnNumber()) + + ")"); + } + + msg += (": " + e.whatString()); + + DataFileException dfe(filenameIn, + msg); + CaretLogThrowing(dfe); + throw dfe; + } + } + + this->setFileName(filename); + + this->clearModified(); +} + +/** + * Read the scene file use the old SAX parser + * @param filenameIn + * Name of scene file. + * @throws DataFileException + * If there is an error reading the file. + */ +void +SceneFile::readFileSaxReader(const AString& filenameIn) +{ + clear(); + + AString filename = filenameIn; + if (DataFile::isFileOnNetwork(filename) == false) { + FileInformation specInfo(filename); + filename = specInfo.getAbsoluteFilePath(); + } + checkFileReadability(filename); + + this->setFileName(filename); + SceneFileSaxReader saxReader(this, filename); std::auto_ptr parser(XmlSaxParser::createXmlParser()); @@ -634,12 +710,51 @@ SceneFile::readFile(const AString& filenameIn) CaretLogThrowing(dfe); throw dfe; } + + this->setFileName(filename); + + this->clearModified(); +} + +/** + * Read the scene file using the new Stream parser + * @param filenameIn + * Name of scene file. + * @throws DataFileException + * If there is an error reading the file. + */ +void +SceneFile::readFileStreamReader(const AString& filenameIn) +{ + clear(); + + AString filename = filenameIn; + if (DataFile::isFileOnNetwork(filename) == false) { + FileInformation specInfo(filename); + filename = specInfo.getAbsoluteFilePath(); + } + checkFileReadability(filename); this->setFileName(filename); + + try { + SceneFileXmlStreamReader streamReader; + streamReader.readFile(filename, + this); + } + catch (const DataFileException& e) { + DataFileException dfe(filename, + e.whatString()); + CaretLogThrowing(dfe); + throw dfe; + } + this->setFileName(filename); + this->clearModified(); } + /** * Write the scene file. * @param filename @@ -658,6 +773,162 @@ SceneFile::writeFile(const AString& filename) this->setFileName(filename); + try { + if (DeveloperFlagsEnum::isFlag(DeveloperFlagsEnum::DEVELOPER_FLAG_NEW_SCENE_FILE_READ_WRITE)) { + SceneFileXmlStreamWriter xmlStreamWriter; + xmlStreamWriter.writeFile(this); + } + else { + // + // Format the version string so that it ends with at most one zero + // Version 4 Added for New Annotation Coordinate Space "SPACER" since annotations + // may be saved to scene file. + // + const AString versionString = AString::number(SceneFile::getFileVersion(), + 'f', + 1); + + // + // Open the file + // + FileAdapter file; + AString errorMessage; + QTextStream* textStream = file.openQTextStreamForWritingFile(this->getFileName(), + errorMessage); + if (textStream == NULL) { + throw DataFileException(filename, + errorMessage); + } + + // + // Create the xml writer + // + XmlWriter xmlWriter(*textStream); + + // + // Write header info + // + xmlWriter.writeStartDocument("1.0"); + + // + // Write root element + // + XmlAttributes attributes; + + //attributes.addAttribute("xmlns:xsi", + // "http://www.w3.org/2001/XMLSchema-instance"); + //attributes.addAttribute("xsi:noNamespaceSchemaLocation", + // "http://brainvis.wustl.edu/caret6/xml_schemas/GIFTI_Caret.xsd"); + attributes.addAttribute(SceneFile::XML_ATTRIBUTE_VERSION, + versionString); + xmlWriter.writeStartElement(SceneFile::XML_TAG_SCENE_FILE, + attributes); + + // + // Write Metadata + // + if (m_metadata != NULL) { + m_metadata->writeAsXML(xmlWriter); + } + + const int32_t numScenes = this->getNumberOfScenes(); + + /* + * Write the scene info directory + */ + xmlWriter.writeStartElement(SceneFile::XML_TAG_SCENE_INFO_DIRECTORY_TAG); + xmlWriter.writeElementCData(SceneXmlElements::SCENE_INFO_BALSA_STUDY_ID_TAG, + getBalsaStudyID()); + xmlWriter.writeElementCData(SceneXmlElements::SCENE_INFO_BALSA_STUDY_TITLE_TAG, + getBalsaStudyTitle()); + switch (getBasePathType()) { + case SceneFileBasePathTypeEnum::AUTOMATIC: + xmlWriter.writeElementCData(SceneXmlElements::SCENE_INFO_BALSA_BASE_DIRECTORY_TAG, + ""); + break; + case SceneFileBasePathTypeEnum::CUSTOM: + { + /* + * Write base path as a path RELATIVE to the scene file + * but only when base path type is CUSTOM + * Note: we do not use FileInformation::getCanonicalFilePath() + * because it returns an empty string if the file DOES NOT exist + * and this may occur since the file may be new and has not + * been closed. + */ + if ( ! getBalsaCustomBaseDirectory().isEmpty()) { + const AString baseDirAbsPath = FileInformation(getBalsaCustomBaseDirectory()).getAbsoluteFilePath(); + const AString sceneFileAbsPath = FileInformation(filename).getAbsoluteFilePath(); + ScenePathName basePathName("basePathName", + baseDirAbsPath); + + const AString relativeBasePath = basePathName.getRelativePathToSceneFile(sceneFileAbsPath); + + //std::cout << "baseDirAbsPath: " << baseDirAbsPath << std::endl; + //std::cout << "sceneFileAbsPath: " << sceneFileAbsPath << std::endl; + //std::cout << "relativeTempFileName: " << relativeBasePath << std::endl; + + xmlWriter.writeElementCData(SceneXmlElements::SCENE_INFO_BALSA_BASE_DIRECTORY_TAG, + relativeBasePath); + } + } + break; + } + xmlWriter.writeElementCData(SceneXmlElements::SCENE_INFO_BALSA_EXTRACT_TO_DIRECTORY_TAG, + getBalsaExtractToDirectoryName()); + xmlWriter.writeElementCData(SceneXmlElements::SCENE_INFO_BASE_PATH_TYPE, + SceneFileBasePathTypeEnum::toName(getBasePathType())); + + for (int32_t i = 0; i < numScenes; i++) { + m_scenes[i]->getSceneInfo()->writeSceneInfo(xmlWriter, + i); + } + xmlWriter.writeEndElement(); + + // + // Write scenes + // + SceneWriterXml sceneWriter(xmlWriter, + this->getFileName()); + for (int32_t i = 0; i < numScenes; i++) { + sceneWriter.writeScene(*m_scenes[i], + i); + } + + xmlWriter.writeEndElement(); + xmlWriter.writeEndDocument(); + + file.close(); + } + + this->clearModified(); + } + catch (const GiftiException& e) { + throw DataFileException(e); + } + catch (const XmlException& e) { + throw DataFileException(e); + } +} + +/** + * Write the scene file using the (not exactly) sax writer + * @param filename + * Name of scene file. + * @throws DataFileException + * If there is an error writing the file. + */ +void +SceneFile::writeFileSaxWriter(const AString& filename) +{ + if (!(filename.endsWith(".scene") || filename.endsWith(".wb_scene"))) + { + CaretLogWarning("scene file '" + filename + "' should be saved ending in .scene"); + } + checkFileWritability(filename); + + this->setFileName(filename); + try { // // Format the version string so that it ends with at most one zero @@ -710,9 +981,9 @@ SceneFile::writeFile(const AString& filename) if (m_metadata != NULL) { m_metadata->writeAsXML(xmlWriter); } - + const int32_t numScenes = this->getNumberOfScenes(); - + /* * Write the scene info directory */ @@ -771,7 +1042,7 @@ SceneFile::writeFile(const AString& filename) SceneWriterXml sceneWriter(xmlWriter, this->getFileName()); for (int32_t i = 0; i < numScenes; i++) { - sceneWriter.writeScene(*m_scenes[i], + sceneWriter.writeScene(*m_scenes[i], i); } @@ -788,16 +1059,37 @@ SceneFile::writeFile(const AString& filename) catch (const XmlException& e) { throw DataFileException(e); } +} + +/** + * Write the scene file stream writer + * @param filename + * Name of scene file. + * @throws DataFileException + * If there is an error writing the file. + */ +void +SceneFile::writeFileStreamWriter(const AString& filename) +{ + if (!(filename.endsWith(".scene") || filename.endsWith(".wb_scene"))) + { + CaretLogWarning("scene file '" + filename + "' should be saved ending in .scene"); + } + checkFileWritability(filename); + + this->setFileName(filename); try { - CaretAssertToDoWarning(); SceneFileXmlStreamWriter xmlStreamWriter; xmlStreamWriter.writeFile(this); + + this->clearModified(); } - catch (const DataFileException& e) { - const QString msg("Xml Stream Writer failed: " - + e.whatString()); - throw DataFileException(msg); + catch (const GiftiException& e) { + throw DataFileException(e); + } + catch (const XmlException& e) { + throw DataFileException(e); } } diff --git a/src/Files/SceneFile.h b/src/Files/SceneFile.h index cf95b1f65..6ecf38dd9 100644 --- a/src/Files/SceneFile.h +++ b/src/Files/SceneFile.h @@ -54,8 +54,16 @@ namespace caret { void readFile(const AString& filename); + void readFileSaxReader(const AString& filename); + + void readFileStreamReader(const AString& filename); + void writeFile(const AString& filename); + void writeFileSaxWriter(const AString& filename); + + void writeFileStreamWriter(const AString& filename); + bool isEmpty() const; void addScene(Scene* scene); diff --git a/src/Files/SceneFileXmlStreamFormatTester.cxx b/src/Files/SceneFileXmlStreamFormatTester.cxx new file mode 100644 index 000000000..b73f78988 --- /dev/null +++ b/src/Files/SceneFileXmlStreamFormatTester.cxx @@ -0,0 +1,327 @@ + +/*LICENSE_START*/ +/* + * Copyright (C) 2019 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + +#include +#include + +#define __SCENE_FILE_XML_STREAM_FORMAT_TESTER_DECLARE__ +#include "SceneFileXmlStreamFormatTester.h" +#undef __SCENE_FILE_XML_STREAM_FORMAT_TESTER_DECLARE__ + +#include "CaretAssert.h" +#include "DataFileException.h" +#include "DataFileTypeEnum.h" +#include "ElapsedTimer.h" +#include "FileInformation.h" +#include "SceneFile.h" +#include "SystemUtilities.h" + +using namespace caret; + +/** + * \class caret::SceneFileXmlStreamFormatTester + * \brief Test new XML Stream Reader and Writer + * \ingroup Files + */ + +/** + * Constructor. + */ +SceneFileXmlStreamFormatTester::SceneFileXmlStreamFormatTester() +: CaretObject() +{ + +} + +/** + * Destructor. + */ +SceneFileXmlStreamFormatTester::~SceneFileXmlStreamFormatTester() +{ +} + +bool +SceneFileXmlStreamFormatTester::testReadingAndWritingInDirectory(const QString& directoryName, + const bool printResultsFlag) +{ + bool allSuccessfulFlag(false); + + QStringList extensions; + for (auto ext : DataFileTypeEnum::getAllFileExtensions(DataFileTypeEnum::SCENE)) { + extensions.append("*." + ext); + } + + QDir directory(directoryName); + QStringList sceneFileList = directory.entryList(extensions, + QDir::Files, + QDir::Name); + + QStringListIterator iter(sceneFileList); + while (iter.hasNext()) { + const QString filename = iter.next(); + const bool passFlag = testReadingAndWriting(filename, + printResultsFlag); + std::cout << (passFlag ? "OK: " : "FAILED: ") << filename << std::endl; + } + return allSuccessfulFlag; +} + +bool +SceneFileXmlStreamFormatTester::testReadingAndWriting(const QString& filename, + const bool printResultsFlag) +{ + const bool readFlag = testReading(filename, + printResultsFlag); + const bool writeFlag = testWriting(filename, + printResultsFlag); + const bool successFlag = (readFlag + && writeFlag); + if (printResultsFlag) { + if (successFlag) { + std::cout << "Success " << std::endl; + } + else { + if ( ! readFlag) { + std::cout << "Read FAILED" << std::endl; + } + if ( ! writeFlag) { + std::cout << "Write FAILED" << std::endl; + } + } + } + + return successFlag; +} + +/** + * Test reading of XML Stream Reader + * + * @param filename + * Name of file + */ +bool +SceneFileXmlStreamFormatTester::testReading(const QString& filename, + const bool printResultsFlag) +{ + try { + QString originalReWrittenFileName; + QString intermediateFileName; + QString finalFileName; + getTempFileNames(filename, + "Read", + originalReWrittenFileName, + intermediateFileName, + finalFileName); + + /* + * Save original format file since some changes to writing + * may have been made since original file was last written + * (perhaps CDATA added) + */ + SceneFile sceneFileUpdated; + sceneFileUpdated.readFileSaxReader(filename); + sceneFileUpdated.writeFileSaxWriter(originalReWrittenFileName); + + SceneFile sceneFile; + sceneFile.readFileStreamReader(originalReWrittenFileName); + + sceneFile.writeFileSaxWriter(finalFileName); + + FileInformation origFileInfo(filename); + FileInformation origReWrittenFileInfo(originalReWrittenFileName); + FileInformation finalFileInfo(finalFileName); + + const bool successFlag(origReWrittenFileInfo.size() == finalFileInfo.size()); + + if (printResultsFlag) { + const QString resultString(successFlag + ? "MATCHED" + : "FAILED"); + std::cout << resultString << std::endl; + std::cout << " Original File: " << origFileInfo.getFileName() << std::endl; + std::cout << " Original File (rewritten): " << origReWrittenFileInfo.getFileName() << std::endl; + std::cout << " Test output file name: " << finalFileInfo.getFileName() << std::endl; + std::cout << " Sizes (orig) " << origReWrittenFileInfo.size() + << " (output) " << finalFileInfo.size() << std::endl; + std::cout << " sdiff -s " << origReWrittenFileInfo.getFileName() + << " " << finalFileInfo.getFileName() << std::endl; + std::cout << std::endl; + } + + return successFlag; + } + catch (const DataFileException& dfe) { + if (printResultsFlag) { + std::cout << "Failed: " << dfe.whatString() << std::endl; + } + return false; + } +} + +bool +SceneFileXmlStreamFormatTester::testWriting(const QString& filename, + const bool printResultsFlag) +{ + try { + QString originalReWrittenFileName; + QString intermediateFileName; + QString finalFileName; + getTempFileNames(filename, + "Write", + originalReWrittenFileName, + intermediateFileName, + finalFileName); + + /* + * Read with old reader and write with old reader since + * writing may have changed since file was originally written + */ + SceneFile sceneFileUpdated; + sceneFileUpdated.readFileSaxReader(filename); + sceneFileUpdated.writeFileSaxWriter(originalReWrittenFileName); + + /* + * Write with new reader + */ + sceneFileUpdated.writeFileSaxWriter(intermediateFileName); + + /* + * Read file with old reader and write with + * old reader. + */ + SceneFile sceneFile; + sceneFile.readFileSaxReader(intermediateFileName); + sceneFile.writeFileSaxWriter(finalFileName); + + FileInformation origFileInfo(filename); + FileInformation rewrittenFileInfo(originalReWrittenFileName); + FileInformation intermediateFileInfo(intermediateFileName); + FileInformation finalFileInfo(finalFileName); + + /* + * If 'original re-written' matches final then + * the intermediate writing with new writer was + * successful + */ + const bool successFlag(rewrittenFileInfo.size() == finalFileInfo.size()); + + if (printResultsFlag) { + const QString resultString(successFlag + ? "MATCHED" + : "FAILED"); + std::cout << resultString << std::endl; + std::cout << " Original File: " << origFileInfo.getFileName() << std::endl; + std::cout << " Old Reritten File: " << rewrittenFileInfo.getFileName() << std::endl; + std::cout << " New Writer File: " << intermediateFileInfo.getFileName() << std::endl; + std::cout << " Final file: " << finalFileInfo.getFileName() << std::endl; + std::cout << " Sizes (re-written) " << rewrittenFileInfo.size() + << " (final) " << finalFileInfo.size() << std::endl; + std::cout << " sdiff -s " << rewrittenFileInfo.getFileName() + << " " << finalFileInfo.getFileName() << std::endl; + std::cout << std::endl; + } + + return successFlag; + } + catch (const DataFileException& dfe) { + if (printResultsFlag) { + std::cout << "Failed: " << dfe.whatString() << std::endl; + } + return false; + } +} + +void +SceneFileXmlStreamFormatTester::getTempFileNames(const QString& filename, + const QString& prefixName, + QString& originalFileReWrittenOut, + QString& intermediateFileNameOut, + QString& finalFileNameOut) +{ + + FileInformation fileInfo(filename); + AString absPath, nameNoExt, extNoDot; + fileInfo.getFileComponents(absPath, nameNoExt, extNoDot); + + const AString path = fileInfo.getAbsolutePath(); + const AString name = fileInfo.getFileName(); + + originalFileReWrittenOut = FileInformation::assembleFileComponents(absPath, prefixName + "TestOriginalReWritten", extNoDot); + intermediateFileNameOut = FileInformation::assembleFileComponents(absPath, + prefixName + "TestItermediate", + extNoDot); + + finalFileNameOut = FileInformation::assembleFileComponents(absPath, + prefixName + "TestFinal", + extNoDot); +} + +bool +SceneFileXmlStreamFormatTester::timeReadingAndWriting(const QString& filename) +{ + const bool successFlag(false); + + + AString path, name, ext; + FileInformation fileInfo(filename); + fileInfo.getFileComponents(path, name, ext); + + try { + std::cout << "Timing (seconds) for " << fileInfo.getFileName() << std::endl; + + { + SceneFile sceneFile; + ElapsedTimer timer; + timer.start(); + sceneFile.readFileSaxReader(filename); + std::cout << " Read OLD: " << timer.getElapsedTimeSeconds() << std::endl; + + timer.reset(); + + QString tempName(FileInformation::assembleFileComponents(QDir::tempPath(), "TestRead", ext)); + + timer.start(); + sceneFile.writeFileSaxWriter(tempName); + std::cout << " Write OLD: " << timer.getElapsedTimeSeconds() << std::endl; + } + + { + SceneFile sceneFile; + ElapsedTimer timer; + timer.start(); + sceneFile.readFileStreamReader(filename); + std::cout << " Read NEW: " << timer.getElapsedTimeSeconds() << std::endl; + + timer.reset(); + + QString tempName(FileInformation::assembleFileComponents(QDir::tempPath(), "TestWrite", ext)); + + timer.start(); + sceneFile.writeFileStreamWriter(tempName); + std::cout << " Write NEW: " << timer.getElapsedTimeSeconds() << std::endl; + } + } + catch (const DataFileException& dfe) { + std::cout << "Failed: " << dfe.whatString() << std::endl; + } + + return successFlag; +} diff --git a/src/Files/SceneFileXmlStreamFormatTester.h b/src/Files/SceneFileXmlStreamFormatTester.h new file mode 100644 index 000000000..fa578a80e --- /dev/null +++ b/src/Files/SceneFileXmlStreamFormatTester.h @@ -0,0 +1,77 @@ +#ifndef __SCENE_FILE_XML_STREAM_FORMAT_TESTER_H__ +#define __SCENE_FILE_XML_STREAM_FORMAT_TESTER_H__ + +/*LICENSE_START*/ +/* + * Copyright (C) 2019 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + + + +#include + +#include "CaretObject.h" + + + +namespace caret { + + class SceneFileXmlStreamFormatTester : public CaretObject { + + public: + SceneFileXmlStreamFormatTester(); + + virtual ~SceneFileXmlStreamFormatTester(); + + SceneFileXmlStreamFormatTester(const SceneFileXmlStreamFormatTester&) = delete; + + SceneFileXmlStreamFormatTester& operator=(const SceneFileXmlStreamFormatTester&) = delete; + + static bool testReadingAndWriting(const QString& filename, + const bool printResultsFlag); + + static bool testReading(const QString& filename, + const bool printResultsFlag); + + static bool testWriting(const QString& filename, + const bool printResultsFlag); + + static bool timeReadingAndWriting(const QString& filename); + + static bool testReadingAndWritingInDirectory(const QString& directoryName, + const bool printResultsFlag); + + // ADD_NEW_METHODS_HERE + + private: + static void getTempFileNames(const QString& filename, + const QString& prefixName, + QString& originalFileReWrittenOut, + QString& intermediateFileNameOut, + QString& finalFileNameOut); + + // ADD_NEW_MEMBERS_HERE + + }; + +#ifdef __SCENE_FILE_XML_STREAM_FORMAT_TESTER_DECLARE__ + // +#endif // __SCENE_FILE_XML_STREAM_FORMAT_TESTER_DECLARE__ + +} // namespace +#endif //__SCENE_FILE_XML_STREAM_FORMAT_TESTER_H__ diff --git a/src/Files/SceneFileXmlStreamReader.cxx b/src/Files/SceneFileXmlStreamReader.cxx index 117fa80eb..733afc3c5 100644 --- a/src/Files/SceneFileXmlStreamReader.cxx +++ b/src/Files/SceneFileXmlStreamReader.cxx @@ -19,11 +19,26 @@ */ /*LICENSE_END*/ +#include +#include +#include + #define __SCENE_FILE_XML_STREAM_READER_DECLARE__ #include "SceneFileXmlStreamReader.h" #undef __SCENE_FILE_XML_STREAM_READER_DECLARE__ #include "CaretAssert.h" +#include "CaretLogger.h" +#include "DataFileException.h" +#include "GiftiMetaData.h" +#include "GiftiXmlElements.h" +#include "Scene.h" +#include "SceneFile.h" +#include "SceneInfo.h" +#include "SceneInfoXmlStreamReader.h" +#include "SceneTypeEnum.h" +#include "SceneXmlStreamReader.h" + using namespace caret; @@ -50,3 +65,237 @@ SceneFileXmlStreamReader::~SceneFileXmlStreamReader() { } +/** + * Read into the given scene file from the file with the given name + * + * @param filename + * Name of the scene file + * @param sceneFile + * Name of the scene file + * @throws + * Data file exception + */ +void +SceneFileXmlStreamReader::readFile(const AString& filename, + SceneFile* sceneFile) +{ + CaretAssert(sceneFile); + if (sceneFile == NULL) { + throw DataFileException("Scene file is invalid (NULL)."); + } + + if (filename.isEmpty()) { + throw DataFileException("Scene file name is empty"); + } + + m_filename = filename; + + QFile file(m_filename); + if ( ! file.open(QFile::ReadOnly)) { + throw DataFileException("Unable to open for reading: " + + m_filename + + " Reason: " + + file.errorString()); + } + + QXmlStreamReader xmlReader(&file); + readFileContent(xmlReader, + sceneFile); + + AString errorMessage; + if (xmlReader.hasError()) { + errorMessage = xmlReader.errorString(); + errorMessage.appendWithNewLine("Line " + + AString::number(xmlReader.lineNumber()) + + " column " + + AString::number(xmlReader.columnNumber())); + } + + file.close(); + + if ( ! errorMessage.isEmpty()) { + throw DataFileException(errorMessage); + } +} + +/** + * Read the file's content + * + * @param xmlReader + * The XML stream reader + * @param sceneFile + * Into this scene file + */ +void +SceneFileXmlStreamReader::readFileContent(QXmlStreamReader& xmlReader, + SceneFile* sceneFile) +{ + CaretAssert(sceneFile); + + if (xmlReader.atEnd()) { + xmlReader.raiseError("At end of file when starting to read. Is file empty?"); + return; + } + + xmlReader.readNextStartElement(); + if (xmlReader.name() != ELEMENT_SCENE_FILE) { + xmlReader.raiseError("First element is \"" + + xmlReader.name() + + "\" but should be " + + ELEMENT_SCENE_FILE); + return; + } + + const QXmlStreamAttributes atts = xmlReader.attributes(); + const QStringRef versionAtt = atts.value(ATTRIBUTE_SCENE_FILE_VERSION); + m_fileVersion = 1; + if ( ! versionAtt.isEmpty()) { + m_fileVersion = static_cast(versionAtt.toFloat()); + } + + /* + * Set when ending scene file element is found + */ + bool endElementFound(false); + + while ( ( ! xmlReader.atEnd()) + && ( ! endElementFound)) { + xmlReader.readNext(); + switch (xmlReader.tokenType()) { + case QXmlStreamReader::StartElement: + if (xmlReader.name() == GiftiXmlElements::TAG_METADATA) { + GiftiMetaData* metaData = sceneFile->getFileMetaData(); + metaData->readSceneFile3(xmlReader); + } + else if (xmlReader.name() == ELEMENT_SCENE_FILE_INFO_DIRECTORY) { + readSceneInfoDirectory(xmlReader, + sceneFile); + } + else if (xmlReader.name() == SceneXmlStreamReader::ELEMENT_SCENE) { + const QXmlStreamAttributes attributes = xmlReader.attributes(); + const QStringRef typeString = attributes.value(SceneXmlStreamReader::ATTRIBUTE_SCENE_TYPE); + bool valid(false); + SceneTypeEnum::Enum sceneType = SceneTypeEnum::fromName(typeString.toString(), + &valid); + + const QStringRef indexString = attributes.value(SceneXmlStreamReader::ATTRIBUTE_SCENE_INDEX); + const int32_t sceneIndex = indexString.toInt(); + + Scene* scene = new Scene(sceneType); + SceneXmlStreamReader sceneReader; + sceneReader.readScene(xmlReader, + scene, + m_filename); + if ( ! xmlReader.hasError()) { + auto mapIter = m_sceneInfoMap.find(sceneIndex); + SceneInfo* sceneInfo = ((mapIter != m_sceneInfoMap.end()) + ? mapIter->second + : NULL); + scene->setSceneInfo(sceneInfo); + sceneFile->addScene(scene); + } + } + else { + m_unexpectedXmlElements.insert(xmlReader.name().toString()); + xmlReader.skipCurrentElement(); + } + break; + case QXmlStreamReader::EndElement: + if (xmlReader.name() == ELEMENT_SCENE_FILE) { + endElementFound = true; + } + break; + default: + break; + } + } +} + +/** + * Read the scene info directory + * + * @param xmlReader + * The XML stream reader + * @param sceneFile + * Into this scene file + */ +void +SceneFileXmlStreamReader::readSceneInfoDirectory(QXmlStreamReader& xmlReader, + SceneFile* sceneFile) +{ + /* + * Gets set when ending scene info directory element is read + */ + bool endElementFound(false); + + while ( (! xmlReader.atEnd()) + && ( ! endElementFound)) { + xmlReader.readNext(); + switch (xmlReader.tokenType()) { + case QXmlStreamReader::StartElement: +// { +// std::cout << "Start Element " << xmlReader.name().toString() +// << " characters " << xmlReader.readElementText() +// << " CDATA" << (xmlReader.isCDATA() ? " Yes" : " No") << std::endl; +// +// } + if (xmlReader.name() == ELEMENT_SCENE_FILE_BALSA_STUDY_ID) { + sceneFile->setBalsaStudyID(xmlReader.readElementText()); + } + else if (xmlReader.name() == ELEMENT_SCENE_FILE_BALSA_STUDY_TITLE) { + sceneFile->setBalsaStudyTitle(xmlReader.readElementText()); + } + else if (xmlReader.name() == ELEMENT_SCENE_FILE_BALSA_BASE_DIRECTORY) { + sceneFile->setBalsaCustomBaseDirectory(xmlReader.readElementText()); + } + else if (xmlReader.name() == ELEMENT_SCENE_FILE_BALSA_EXTRACT_TO_DIRECTORY) { + sceneFile->setBalsaExtractToDirectoryName(xmlReader.readElementText()); + } + else if (xmlReader.name() == ELEMENT_SCENE_FILE_BALSA_BASE_PATH_TYPE) { + const AString name = xmlReader.readElementText(); + bool valid(false); + const SceneFileBasePathTypeEnum::Enum basePathType = SceneFileBasePathTypeEnum::fromName(name, + &valid); + if (valid) { + sceneFile->setBasePathType(basePathType); + } + else { + sceneFile->setBasePathType(SceneFileBasePathTypeEnum::AUTOMATIC); + CaretLogWarning(m_filename + + "has invalid base path type: " + + name); + } + + } + else if (xmlReader.name() == SceneInfoXmlStreamReader::ELEMENT_SCENE_INFO) { + const QXmlStreamAttributes attributes = xmlReader.attributes(); + const QStringRef indexAttribute = attributes.value(SceneInfoXmlStreamReader::ATTRIBUTE_SCENE_INDEX); + if ( ! indexAttribute.isEmpty()) { + const int32_t sceneIndex = indexAttribute.toInt(); + SceneInfoXmlStreamReader infoReader; + SceneInfo* sceneInfo = new SceneInfo(); + infoReader.readSceneInfo(xmlReader, + sceneInfo); + + if ( ! xmlReader.hasError()) { + m_sceneInfoMap.insert(std::make_pair(sceneIndex, + sceneInfo)); + } + } + } + else { + m_unexpectedXmlElements.insert(xmlReader.name().toString()); + xmlReader.skipCurrentElement(); + } + break; + case QXmlStreamReader::EndElement: + if (xmlReader.name() == ELEMENT_SCENE_FILE_INFO_DIRECTORY) { + endElementFound = true; + } + break; + default: + break; + } + } +} + diff --git a/src/Files/SceneFileXmlStreamReader.h b/src/Files/SceneFileXmlStreamReader.h index 21d9fc783..6246d4529 100644 --- a/src/Files/SceneFileXmlStreamReader.h +++ b/src/Files/SceneFileXmlStreamReader.h @@ -24,13 +24,17 @@ #include +#include #include "SceneFileXmlStreamBase.h" - +class QXmlStreamReader; namespace caret { + class SceneFile; + class SceneInfo; + class SceneFileXmlStreamReader : public SceneFileXmlStreamBase { public: @@ -41,11 +45,27 @@ namespace caret { SceneFileXmlStreamReader(const SceneFileXmlStreamReader&) = delete; SceneFileXmlStreamReader& operator=(const SceneFileXmlStreamReader&) = delete; - + + void readFile(const AString& filename, + SceneFile* sceneFile); // ADD_NEW_METHODS_HERE private: + void readFileContent(QXmlStreamReader& xmlReader, + SceneFile* sceneFile); + + void readSceneInfoDirectory(QXmlStreamReader& xmlReader, + SceneFile* sceneFile); + + AString m_filename; + + int32_t m_fileVersion = -1; + + std::set m_unexpectedXmlElements; + + std::map m_sceneInfoMap; + // ADD_NEW_MEMBERS_HERE }; diff --git a/src/Files/SceneFileXmlStreamWriter.cxx b/src/Files/SceneFileXmlStreamWriter.cxx index f5a379bc9..3471551c1 100644 --- a/src/Files/SceneFileXmlStreamWriter.cxx +++ b/src/Files/SceneFileXmlStreamWriter.cxx @@ -76,7 +76,7 @@ SceneFileXmlStreamWriter::writeFile(const SceneFile* sceneFile) { CaretAssert(sceneFile); - const QString filename = "TestSceneXmlStream.scene"; //sceneFile->getFileName(); + const QString filename = sceneFile->getFileName(); if (filename.isEmpty()) { throw DataFileException("Name for writing annotation file is empty."); } @@ -133,7 +133,7 @@ SceneFileXmlStreamWriter::writeFileContentToXmlStreamWriter(QXmlStreamWriter& xm const GiftiMetaData* metaData = sceneFile->getFileMetaData(); if ( ! metaData->isEmpty()) { - metaData->writeCiftiXML1(xmlWriter); + metaData->writeSceneFile3(xmlWriter); } writeSceneInfoDirectory(xmlWriter, diff --git a/src/FilesBase/GiftiMetaData.cxx b/src/FilesBase/GiftiMetaData.cxx index fd7747b41..ca67eac6f 100644 --- a/src/FilesBase/GiftiMetaData.cxx +++ b/src/FilesBase/GiftiMetaData.cxx @@ -557,6 +557,11 @@ void GiftiMetaData::writeBorderFileXML3(QXmlStreamWriter& xmlWriter) const writeCiftiXML1(xmlWriter); } +void GiftiMetaData::writeSceneFile3(QXmlStreamWriter& xmlWriter) const +{ + writeCiftiXML1(xmlWriter); +} + void GiftiMetaData::readCiftiXML1(QXmlStreamReader& xml) { clear(false); @@ -593,6 +598,11 @@ void GiftiMetaData::readBorderFileXML3(QXmlStreamReader& xml) readCiftiXML1(xml); } +void GiftiMetaData::readSceneFile3(QXmlStreamReader& xml) +{ + readCiftiXML1(xml); +} + void GiftiMetaData::readEntry(QXmlStreamReader& xml) { AString key, value; diff --git a/src/FilesBase/GiftiMetaData.h b/src/FilesBase/GiftiMetaData.h index a98f3724c..1076fd73d 100644 --- a/src/FilesBase/GiftiMetaData.h +++ b/src/FilesBase/GiftiMetaData.h @@ -121,6 +121,9 @@ public: void readBorderFileXML1(QXmlStreamReader& xml); void readBorderFileXML3(QXmlStreamReader& xml); + void readSceneFile3(QXmlStreamReader& xml); + void writeSceneFile3(QXmlStreamWriter& xmlWriter) const; + void setModified(); void clearModified(); diff --git a/src/GuiQt/BrainBrowserWindow.cxx b/src/GuiQt/BrainBrowserWindow.cxx index c748c0ba2..4ac4a7e56 100644 --- a/src/GuiQt/BrainBrowserWindow.cxx +++ b/src/GuiQt/BrainBrowserWindow.cxx @@ -95,6 +95,7 @@ #include "SceneClassAssistant.h" #include "SceneEnumeratedType.h" #include "SceneFile.h" +#include "SceneFileXmlStreamFormatTester.h" #include "SceneWindowGeometry.h" #include "SessionManager.h" #include "SpacerTabContent.h" @@ -1605,15 +1606,23 @@ BrainBrowserWindow::createMenuDevelop() QObject::connect(m_developerFlagsActionGroup, SIGNAL(triggered(QAction*)), this, SLOT(developerMenuFlagTriggered(QAction*))); + bool lastItemCheckableFlag(true); for (std::vector::iterator iter = developerFlags.begin(); iter != developerFlags.end(); iter++) { const DeveloperFlagsEnum::Enum flag = *iter; - + + const bool itemCheckableFlag = DeveloperFlagsEnum::isCheckable(flag); + if (itemCheckableFlag != lastItemCheckableFlag) { + menu->addSeparator(); + } + QAction* action = menu->addAction(DeveloperFlagsEnum::toGuiName(flag)); - action->setCheckable(true); + action->setCheckable(itemCheckableFlag); action->setData(static_cast(DeveloperFlagsEnum::toIntegerCode(flag))); m_developerFlagsActionGroup->addAction(action); + + lastItemCheckableFlag = itemCheckableFlag; } } @@ -1667,6 +1676,27 @@ BrainBrowserWindow::developerMenuFlagTriggered(QAction* action) DeveloperFlagsEnum::setFlag(enumValue, action->isChecked()); + if (enumValue == DeveloperFlagsEnum::DEVELOPER_FLAG_TEST_SCENE_FILE_READ_WRITE) { + QString filename = CaretFileDialog::getOpenFileNameDialog(DataFileTypeEnum::SCENE); + if ( ! filename.isEmpty()) { + SceneFileXmlStreamFormatTester::testReadingAndWriting(filename, + true); + } + } + else if (enumValue == DeveloperFlagsEnum::DEVELOPER_FLAG_TIME_SCENE_FILE_READ_WRITE) { + QString filename = CaretFileDialog::getOpenFileNameDialog(DataFileTypeEnum::SCENE); + if ( ! filename.isEmpty()) { + SceneFileXmlStreamFormatTester::timeReadingAndWriting(filename); + } + } + else if (enumValue == DeveloperFlagsEnum::DEVELOPER_FLAG_TEST_ALL_SCENE_FILES_IN_DIRECTORY) { + QString dirName = CaretFileDialog::getExistingDirectoryDialog(); + if ( ! dirName.isEmpty()) { + SceneFileXmlStreamFormatTester::testReadingAndWritingInDirectory(dirName, + false); + } + } + /* * Update graphics and GUI */ diff --git a/src/GuiQt/BrainOpenGLWidget.cxx b/src/GuiQt/BrainOpenGLWidget.cxx index 220eae68c..5ecafaf1d 100644 --- a/src/GuiQt/BrainOpenGLWidget.cxx +++ b/src/GuiQt/BrainOpenGLWidget.cxx @@ -1434,7 +1434,7 @@ BrainOpenGLWidget::mouseMoveEvent(QMouseEvent* me) Qt::MouseButton button = me->button(); Qt::KeyboardModifiers keyModifiers = me->modifiers(); Qt::MouseButtons mouseButtons = me->buttons(); - + checkForMiddleMouseButton(mouseButtons, button, keyModifiers, diff --git a/src/GuiQt/WuQMacroDialog.cxx b/src/GuiQt/WuQMacroDialog.cxx index b48db497d..8446749e5 100644 --- a/src/GuiQt/WuQMacroDialog.cxx +++ b/src/GuiQt/WuQMacroDialog.cxx @@ -154,6 +154,9 @@ WuQMacroDialog::~WuQMacroDialog() { } +/** + * @return Widget with run and editing buttons + */ QWidget* WuQMacroDialog::createMacroRunAndEditingToolButtons() { diff --git a/src/Scenes/SceneClass.cxx b/src/Scenes/SceneClass.cxx index 1525cbb66..053cbed41 100644 --- a/src/Scenes/SceneClass.cxx +++ b/src/Scenes/SceneClass.cxx @@ -121,6 +121,18 @@ SceneClass::castToSceneClass() const return this; } +/** + * Cast an instance of SceneObject to a SceneClass. + * Is used to avoid dynamic casting and overridden by the class. + * + * @return Valid pointer (non-NULL) this is SceneClass + */ +SceneClass* +SceneClass::castToSceneClass() +{ + return this; +} + /** * @return Name of the class (NOT the instance). */ diff --git a/src/Scenes/SceneClass.h b/src/Scenes/SceneClass.h index 6f79ece93..7c62fde9a 100644 --- a/src/Scenes/SceneClass.h +++ b/src/Scenes/SceneClass.h @@ -48,6 +48,8 @@ namespace caret { virtual const SceneClass* castToSceneClass() const; + virtual SceneClass* castToSceneClass(); + AString getClassName() const; int32_t getVersionNumber() const; diff --git a/src/Scenes/SceneClassArray.cxx b/src/Scenes/SceneClassArray.cxx index 051601456..d80dd96a4 100644 --- a/src/Scenes/SceneClassArray.cxx +++ b/src/Scenes/SceneClassArray.cxx @@ -116,6 +116,18 @@ SceneClassArray::~SceneClassArray() m_values.clear(); } +/** + * Cast an instance of SceneObjectArray to a SceneClassArray. + * Is used to avoid dynamic casting and overridden by the class. + * + * @return Valid pointer (non-NULL) this is SceneClass + */ +SceneClassArray* +SceneClassArray::castToSceneClassArray() +{ + return this; +} + /** * Cast an instance of SceneObjectArray to a SceneClassArray. * Is used to avoid dynamic casting and overridden by the class. diff --git a/src/Scenes/SceneClassArray.h b/src/Scenes/SceneClassArray.h index c98a5b80d..cc930e128 100644 --- a/src/Scenes/SceneClassArray.h +++ b/src/Scenes/SceneClassArray.h @@ -45,8 +45,10 @@ namespace caret { virtual ~SceneClassArray(); - virtual const SceneClassArray* castToSceneClassArray() const; + virtual SceneClassArray* castToSceneClassArray(); + virtual const SceneClassArray* castToSceneClassArray() const; + void setClassAtIndex(const int32_t arrayIndex, SceneClass* sceneClass); diff --git a/src/Scenes/SceneEnumeratedType.cxx b/src/Scenes/SceneEnumeratedType.cxx index b1571ace5..8fd2ba97e 100644 --- a/src/Scenes/SceneEnumeratedType.cxx +++ b/src/Scenes/SceneEnumeratedType.cxx @@ -55,6 +55,18 @@ SceneEnumeratedType::~SceneEnumeratedType() } +/** + * Cast an instance of SceneObject to a SceneEnumeratedType. + * Is used to avoid dynamic casting and overridden by the class. + * + * @return Valid pointer (non-NULL) this is SceneEnumeratedType + */ +SceneEnumeratedType* +SceneEnumeratedType::castToSceneEnumeratedType() +{ + return this; +} + /** * Cast an instance of SceneObject to a SceneEnumeratedType. * Is used to avoid dynamic casting and overridden by the class. @@ -67,6 +79,7 @@ SceneEnumeratedType::castToSceneEnumeratedType() const return this; } + /** * @param enumeratedValueAsString * New value. diff --git a/src/Scenes/SceneEnumeratedType.h b/src/Scenes/SceneEnumeratedType.h index 78063952b..be0c24f40 100644 --- a/src/Scenes/SceneEnumeratedType.h +++ b/src/Scenes/SceneEnumeratedType.h @@ -34,6 +34,8 @@ namespace caret { virtual ~SceneEnumeratedType(); + virtual SceneEnumeratedType* castToSceneEnumeratedType(); + virtual const SceneEnumeratedType* castToSceneEnumeratedType() const; void setValue(const AString& enumeratedValueAsString); diff --git a/src/Scenes/SceneEnumeratedTypeArray.cxx b/src/Scenes/SceneEnumeratedTypeArray.cxx index 4bbf06924..b551fa412 100644 --- a/src/Scenes/SceneEnumeratedTypeArray.cxx +++ b/src/Scenes/SceneEnumeratedTypeArray.cxx @@ -109,6 +109,18 @@ SceneEnumeratedTypeArray::~SceneEnumeratedTypeArray() } +/** + * Cast an instance of SceneObjectArray to a SceneEnumeratedTypeArray. + * Is used to avoid dynamic casting and overridden by the class. + * + * @return Valid pointer (non-NULL) this is SceneClass + */ +SceneEnumeratedTypeArray* +SceneEnumeratedTypeArray::castToSceneEnumeratedTypeArray() +{ + return this; +} + /** * Cast an instance of SceneObjectArray to a SceneEnumeratedTypeArray. * Is used to avoid dynamic casting and overridden by the class. diff --git a/src/Scenes/SceneEnumeratedTypeArray.h b/src/Scenes/SceneEnumeratedTypeArray.h index 95c2fd70d..0578d99da 100644 --- a/src/Scenes/SceneEnumeratedTypeArray.h +++ b/src/Scenes/SceneEnumeratedTypeArray.h @@ -43,6 +43,8 @@ namespace caret { virtual ~SceneEnumeratedTypeArray(); + virtual SceneEnumeratedTypeArray* castToSceneEnumeratedTypeArray(); + virtual const SceneEnumeratedTypeArray* castToSceneEnumeratedTypeArray() const; void setValue(const int32_t arrayIndex, diff --git a/src/Scenes/SceneInfoSaxReader.cxx b/src/Scenes/SceneInfoSaxReader.cxx index dbc11fd3a..47b1914e9 100644 --- a/src/Scenes/SceneInfoSaxReader.cxx +++ b/src/Scenes/SceneInfoSaxReader.cxx @@ -179,14 +179,12 @@ SceneInfoSaxReader::endElement(const AString& /* namspaceURI */, case STATE_SCENE_INFO_MACRO_GROUP: { WuQMacroGroup* macroGroup = m_sceneInfo->getMacroGroup(); + QString errorMessage; WuQMacroGroupXmlStreamReader reader; - reader.readFromString(m_elementText, - macroGroup); - if (reader.hasError()) { - CaretLogSevere(reader.getErrorMessage()); - } - else if (reader.hasWarnings()) { - CaretLogWarning(reader.getWarningMessage()); + if ( ! reader.readFromString(m_elementText, + macroGroup, + errorMessage)) { + CaretLogSevere(errorMessage); } } break; diff --git a/src/Scenes/SceneInfoXmlStreamBase.h b/src/Scenes/SceneInfoXmlStreamBase.h index 055d16df2..30859e100 100644 --- a/src/Scenes/SceneInfoXmlStreamBase.h +++ b/src/Scenes/SceneInfoXmlStreamBase.h @@ -48,9 +48,9 @@ namespace caret { static const AString ELEMENT_NAME; static const AString ELEMENT_SCENE_INFO; - static const AString ATTRIBUTE_ENCODING; - static const AString ATTRIBUTE_FORMAT; - static const AString ATTRIBUTE_INDEX; + static const AString ATTRIBUTE_IMAGE_ENCODING; + static const AString ATTRIBUTE_IMAGE_FORMAT; + static const AString ATTRIBUTE_SCENE_INDEX; static const AString VALUE_ENCODING_BASE64; @@ -70,9 +70,9 @@ namespace caret { const AString SceneInfoXmlStreamBase::ELEMENT_NAME = "Name"; const AString SceneInfoXmlStreamBase::ELEMENT_SCENE_INFO = "SceneInfo"; - const AString SceneInfoXmlStreamBase::ATTRIBUTE_ENCODING = "Encoding"; - const AString SceneInfoXmlStreamBase::ATTRIBUTE_FORMAT = "Format"; - const AString SceneInfoXmlStreamBase::ATTRIBUTE_INDEX = "Index"; + const AString SceneInfoXmlStreamBase::ATTRIBUTE_IMAGE_ENCODING = "Encoding"; + const AString SceneInfoXmlStreamBase::ATTRIBUTE_IMAGE_FORMAT = "Format"; + const AString SceneInfoXmlStreamBase::ATTRIBUTE_SCENE_INDEX = "Index"; const AString SceneInfoXmlStreamBase::VALUE_ENCODING_BASE64 = "Base64"; #endif // __SCENE_INFO_XML_STREAM_BASE_DECLARE__ diff --git a/src/Scenes/SceneInfoXmlStreamReader.cxx b/src/Scenes/SceneInfoXmlStreamReader.cxx index 9a2deba27..4c37e17b8 100644 --- a/src/Scenes/SceneInfoXmlStreamReader.cxx +++ b/src/Scenes/SceneInfoXmlStreamReader.cxx @@ -23,7 +23,13 @@ #include "SceneInfoXmlStreamReader.h" #undef __SCENE_INFO_XML_STREAM_READER_DECLARE__ +#include +#include + #include "CaretAssert.h" +#include "SceneInfo.h" +#include "WuQMacroGroupXmlStreamReader.h" + using namespace caret; @@ -50,3 +56,87 @@ SceneInfoXmlStreamReader::~SceneInfoXmlStreamReader() { } +/** + * Read the scene info. + * If, after calling this method, xmlReader.hasError() return true, + * there was an error reading the SceneInfo. + * + * @param xmlReader + * The XML stream reader + * @param sceneInfo + * Read into this sceneInfo + */ +void +SceneInfoXmlStreamReader::readSceneInfo(QXmlStreamReader& xmlReader, + SceneInfo* sceneInfo) +{ + CaretAssert(sceneInfo); + if (sceneInfo == NULL) { + return; + } + + if (xmlReader.name() != ELEMENT_SCENE_INFO) { + xmlReader.raiseError("First element is \"" + + xmlReader.name() + + "\" but should be " + + ELEMENT_SCENE_INFO); + return; + } + + /* + * Gets set when ending scene info directory element is read + */ + bool endElementFound(false); + + while ( (! xmlReader.atEnd()) + && ( ! endElementFound)) { + xmlReader.readNext(); + switch (xmlReader.tokenType()) { + case QXmlStreamReader::StartElement: + if (xmlReader.name() == ELEMENT_NAME) { + sceneInfo->setName(xmlReader.readElementText()); + } + else if (xmlReader.name() == ELEMENT_BALSA_SCENE_ID) { + sceneInfo->setBalsaSceneID(xmlReader.readElementText()); + } + else if (xmlReader.name() == ELEMENT_DESCRIPTION) { + sceneInfo->setDescription(xmlReader.readElementText()); + } + else if (xmlReader.name() == ELEMENT_IMAGE) { + const QXmlStreamAttributes atts = xmlReader.attributes(); + const QString encodingName = atts.value(ATTRIBUTE_IMAGE_ENCODING).toString(); + const QString formatName = atts.value(ATTRIBUTE_IMAGE_FORMAT).toString(); + sceneInfo->setImageFromText(xmlReader.readElementText(), + encodingName, + formatName); + } + else if (xmlReader.name() == WuQMacroGroupXmlStreamReader::ELEMENT_MACRO_GROUP) { + WuQMacroGroupXmlStreamReader macroGroupReader; + macroGroupReader.readMacroGroup(xmlReader, + sceneInfo->getMacroGroup()); + } + else { + m_unexpectedXmlElements.insert(xmlReader.name().toString()); + xmlReader.skipCurrentElement(); + } + break; + case QXmlStreamReader::EndElement: + if (xmlReader.name() == ELEMENT_SCENE_INFO) { + endElementFound = true; + } + break; + default: + break; + } + } +} + +/** + * @return Any unexpected elements that were found, + * and ignored, while reading the SceneInfo XML. + */ +std::set +SceneInfoXmlStreamReader::getUnexpectedElements() const +{ + return m_unexpectedXmlElements; +} diff --git a/src/Scenes/SceneInfoXmlStreamReader.h b/src/Scenes/SceneInfoXmlStreamReader.h index ba6e7973b..e619a2c66 100644 --- a/src/Scenes/SceneInfoXmlStreamReader.h +++ b/src/Scenes/SceneInfoXmlStreamReader.h @@ -24,13 +24,16 @@ #include +#include #include "SceneInfoXmlStreamBase.h" - +class QXmlStreamReader; namespace caret { + class SceneInfo; + class SceneInfoXmlStreamReader : public SceneInfoXmlStreamBase { public: @@ -42,10 +45,15 @@ namespace caret { SceneInfoXmlStreamReader& operator=(const SceneInfoXmlStreamReader&) = delete; + void readSceneInfo(QXmlStreamReader& xmlReader, + SceneInfo* sceneInfo); + std::set getUnexpectedElements() const; + // ADD_NEW_METHODS_HERE private: + std::set m_unexpectedXmlElements; // ADD_NEW_MEMBERS_HERE }; diff --git a/src/Scenes/SceneInfoXmlStreamWriter.cxx b/src/Scenes/SceneInfoXmlStreamWriter.cxx index ec36528c6..30022d6f1 100644 --- a/src/Scenes/SceneInfoXmlStreamWriter.cxx +++ b/src/Scenes/SceneInfoXmlStreamWriter.cxx @@ -75,7 +75,7 @@ SceneInfoXmlStreamWriter::writeXML(QXmlStreamWriter* xmlWriter, m_xmlWriter = xmlWriter; m_xmlWriter->writeStartElement(ELEMENT_SCENE_INFO); - m_xmlWriter->writeAttribute(ATTRIBUTE_INDEX, + m_xmlWriter->writeAttribute(ATTRIBUTE_SCENE_INDEX, QString::number(sceneInfoIndex)); m_xmlWriter->writeTextElement(ELEMENT_NAME, @@ -116,8 +116,8 @@ SceneInfoXmlStreamWriter::writeImageElement(const SceneInfo* sceneInfo) QString base64String = QString::fromLatin1(base64ByteArray.constData(), base64ByteArray.size()); m_xmlWriter->writeStartElement(ELEMENT_IMAGE); - m_xmlWriter->writeAttribute(ATTRIBUTE_ENCODING, VALUE_ENCODING_BASE64); - m_xmlWriter->writeAttribute(ATTRIBUTE_FORMAT, imageFormat); + m_xmlWriter->writeAttribute(ATTRIBUTE_IMAGE_ENCODING, VALUE_ENCODING_BASE64); + m_xmlWriter->writeAttribute(ATTRIBUTE_IMAGE_FORMAT, imageFormat); m_xmlWriter->writeCharacters(base64String); m_xmlWriter->writeEndElement(); } diff --git a/src/Scenes/SceneObject.cxx b/src/Scenes/SceneObject.cxx index 57389a952..15ec970be 100644 --- a/src/Scenes/SceneObject.cxx +++ b/src/Scenes/SceneObject.cxx @@ -228,6 +228,18 @@ SceneObject::castToSceneClass() const return NULL; } +/** + * Cast an instance of SceneObject to a SceneClass. + * Is used to avoid dynamic casting and overridden by the class. + * + * @return Valid pointer (non-NULL) this is SceneClass + */ +SceneClass* +SceneObject::castToSceneClass() +{ + return NULL; +} + /** * Cast an instance of SceneObject to a SceneEnumeratedType. * Is used to avoid dynamic casting and overridden by the class. @@ -240,6 +252,18 @@ SceneObject::castToSceneEnumeratedType() const return NULL; } +/** + * Cast an instance of SceneObject to a SceneEnumeratedType. + * Is used to avoid dynamic casting and overridden by the class. + * + * @return Valid pointer (non-NULL) this is SceneEnumeratedType + */ +SceneEnumeratedType* +SceneObject::castToSceneEnumeratedType() +{ + return NULL; +} + /** * Cast an instance of SceneObject to a SceneObjectArray. * Is used to avoid dynamic casting and overridden by the class. @@ -252,6 +276,30 @@ SceneObject::castToSceneObjectArray() const return NULL; } +/** + * Cast an instance of SceneObject to a SceneObjectArray. + * Is used to avoid dynamic casting and overridden by the class. + * + * @return Valid pointer (non-NULL) this is SceneObjectArray + */ +SceneObjectArray* +SceneObject::castToSceneObjectArray() +{ + return NULL; +} + +/** + * Cast an instance of SceneObject to a SceneObjectMapIntegerKey. + * Is used to avoid dynamic casting and overridden by the class. + * + * @return Valid pointer (non-NULL) this is SceneObjectMapIntegerKey + */ +SceneObjectMapIntegerKey* +SceneObject::castToSceneObjectMapIntegerKey() +{ + return NULL; +} + /** * Cast an instance of SceneObject to a SceneObjectMapIntegerKey. * Is used to avoid dynamic casting and overridden by the class. @@ -264,6 +312,18 @@ SceneObject::castToSceneObjectMapIntegerKey() const return NULL; } +/** + * Cast an instance of SceneObject to a ScenePathName. + * Is used to avoid dynamic casting and overridden by the class. + * + * @return Valid pointer (non-NULL) this is ScenePathName + */ +ScenePathName* +SceneObject::castToScenePathName() +{ + return NULL; +} + /** * Cast an instance of SceneObject to a ScenePathName. * Is used to avoid dynamic casting and overridden by the class. @@ -276,6 +336,18 @@ SceneObject::castToScenePathName() const return NULL; } +/** + * Cast an instance of SceneObject to a ScenePrimitive. + * Is used to avoid dynamic casting and overridden by the class. + * + * @return Valid pointer (non-NULL) this is ScenePrimitive + */ +ScenePrimitive* +SceneObject::castToScenePrimitive() +{ + return NULL; +} + /** * Cast an instance of SceneObject to a ScenePrimitive. * Is used to avoid dynamic casting and overridden by the class. @@ -287,3 +359,4 @@ SceneObject::castToScenePrimitive() const { return NULL; } + diff --git a/src/Scenes/SceneObject.h b/src/Scenes/SceneObject.h index f3a33bc1f..3f2716442 100644 --- a/src/Scenes/SceneObject.h +++ b/src/Scenes/SceneObject.h @@ -65,16 +65,28 @@ namespace caret { /// Should be overridden by any sub-classes that have children virtual std::vector getDescendants() const; + virtual SceneClass* castToSceneClass(); + virtual const SceneClass* castToSceneClass() const; + virtual SceneEnumeratedType* castToSceneEnumeratedType(); + virtual const SceneEnumeratedType* castToSceneEnumeratedType() const; + virtual SceneObjectArray* castToSceneObjectArray(); + virtual const SceneObjectArray* castToSceneObjectArray() const; + virtual SceneObjectMapIntegerKey* castToSceneObjectMapIntegerKey(); + virtual const SceneObjectMapIntegerKey* castToSceneObjectMapIntegerKey() const; + virtual ScenePathName* castToScenePathName(); + virtual const ScenePathName* castToScenePathName() const; + virtual ScenePrimitive* castToScenePrimitive(); + virtual const ScenePrimitive* castToScenePrimitive() const; protected: diff --git a/src/Scenes/SceneObjectArray.cxx b/src/Scenes/SceneObjectArray.cxx index d51df2a35..da8d48dd2 100644 --- a/src/Scenes/SceneObjectArray.cxx +++ b/src/Scenes/SceneObjectArray.cxx @@ -61,6 +61,18 @@ SceneObjectArray::~SceneObjectArray() } +/** + * Cast an instance of SceneObject to a SceneObjectArray. + * Is used to avoid dynamic casting and overridden by the class. + * + * @return Valid pointer (non-NULL) this is SceneObjectArray + */ +SceneObjectArray* +SceneObjectArray::castToSceneObjectArray() +{ + return this; +} + /** * Cast an instance of SceneObject to a SceneObjectArray. * Is used to avoid dynamic casting and overridden by the class. @@ -73,6 +85,17 @@ SceneObjectArray::castToSceneObjectArray() const return this; } +/** + * Cast an instance of SceneObjectArray to a SceneClassArray. + * Is used to avoid dynamic casting and overridden by the class. + * + * @return Valid pointer (non-NULL) this is SceneClass + */ +SceneClassArray* +SceneObjectArray::castToSceneClassArray() +{ + return NULL; +} /** * Cast an instance of SceneObjectArray to a SceneClassArray. @@ -86,6 +109,18 @@ SceneObjectArray::castToSceneClassArray() const return NULL; } +/** + * Cast an instance of SceneObjectArray to a SceneEnumeratedTypeArray. + * Is used to avoid dynamic casting and overridden by the class. + * + * @return Valid pointer (non-NULL) this is SceneClass + */ +SceneEnumeratedTypeArray* +SceneObjectArray::castToSceneEnumeratedTypeArray() +{ + return NULL; +} + /** * Cast an instance of SceneObjectArray to a SceneEnumeratedTypeArray. * Is used to avoid dynamic casting and overridden by the class. @@ -98,6 +133,18 @@ SceneObjectArray::castToSceneEnumeratedTypeArray() const return NULL; } +/** + * Cast an instance of SceneObjectArray to a ScenePathNameArray. + * Is used to avoid dynamic casting and overridden by the class. + * + * @return Valid pointer (non-NULL) this is SceneClass + */ +ScenePathNameArray* +SceneObjectArray::castToScenePathNameArray() +{ + return NULL; +} + /** * Cast an instance of SceneObjectArray to a ScenePathNameArray. * Is used to avoid dynamic casting and overridden by the class. @@ -110,6 +157,18 @@ SceneObjectArray::castToScenePathNameArray() const return NULL; } +/** + * Cast an instance of SceneObjectArray to a ScenePrimitiveArray. + * Is used to avoid dynamic casting and overridden by the class. + * + * @return Valid pointer (non-NULL) this is SceneClass + */ +ScenePrimitiveArray* +SceneObjectArray::castToScenePrimitiveArray() +{ + return NULL; +} + /** * Cast an instance of SceneObjectArray to a ScenePrimitiveArray. * Is used to avoid dynamic casting and overridden by the class. diff --git a/src/Scenes/SceneObjectArray.h b/src/Scenes/SceneObjectArray.h index 7057eed24..847005947 100644 --- a/src/Scenes/SceneObjectArray.h +++ b/src/Scenes/SceneObjectArray.h @@ -38,14 +38,24 @@ namespace caret { virtual ~SceneObjectArray(); + virtual SceneObjectArray* castToSceneObjectArray(); + virtual const SceneObjectArray* castToSceneObjectArray() const; + virtual SceneClassArray* castToSceneClassArray(); + virtual const SceneClassArray* castToSceneClassArray() const; + virtual SceneEnumeratedTypeArray* castToSceneEnumeratedTypeArray(); + virtual const SceneEnumeratedTypeArray* castToSceneEnumeratedTypeArray() const; + virtual ScenePathNameArray* castToScenePathNameArray(); + virtual const ScenePathNameArray* castToScenePathNameArray() const; + virtual ScenePrimitiveArray* castToScenePrimitiveArray(); + virtual const ScenePrimitiveArray* castToScenePrimitiveArray() const; private: diff --git a/src/Scenes/SceneObjectMapIntegerKey.cxx b/src/Scenes/SceneObjectMapIntegerKey.cxx index 21151659f..c87302582 100644 --- a/src/Scenes/SceneObjectMapIntegerKey.cxx +++ b/src/Scenes/SceneObjectMapIntegerKey.cxx @@ -79,6 +79,18 @@ SceneObjectMapIntegerKey::~SceneObjectMapIntegerKey() } } +/** + * Cast an instance of SceneObject to a SceneObjectMapIntegerKey. + * Is used to avoid dynamic casting and overridden by the class. + * + * @return Valid pointer (non-NULL) this is SceneObjectMapIntegerKey + */ +SceneObjectMapIntegerKey* +SceneObjectMapIntegerKey::castToSceneObjectMapIntegerKey() +{ + return this; +} + /** * Cast an instance of SceneObject to a SceneObjectMapIntegerKey. * Is used to avoid dynamic casting and overridden by the class. diff --git a/src/Scenes/SceneObjectMapIntegerKey.h b/src/Scenes/SceneObjectMapIntegerKey.h index 70f0886f7..74a91fb3f 100644 --- a/src/Scenes/SceneObjectMapIntegerKey.h +++ b/src/Scenes/SceneObjectMapIntegerKey.h @@ -37,6 +37,8 @@ namespace caret { SceneObjectMapIntegerKey(const QString& name, const SceneObjectDataTypeEnum::Enum valueDataType); + virtual SceneObjectMapIntegerKey* castToSceneObjectMapIntegerKey(); + virtual const SceneObjectMapIntegerKey* castToSceneObjectMapIntegerKey() const; virtual std::vector getDescendants() const; diff --git a/src/Scenes/ScenePathName.cxx b/src/Scenes/ScenePathName.cxx index d9c09822d..c646731e7 100644 --- a/src/Scenes/ScenePathName.cxx +++ b/src/Scenes/ScenePathName.cxx @@ -89,6 +89,18 @@ ScenePathName::~ScenePathName() } +/** + * Cast an instance of SceneObject to a ScenePathName. + * Is used to avoid dynamic casting and overridden by the class. + * + * @return Valid pointer (non-NULL) this is ScenePathName + */ +ScenePathName* +ScenePathName::castToScenePathName() +{ + return this; +} + /** * Cast an instance of SceneObject to a ScenePathName. * Is used to avoid dynamic casting and overridden by the class. diff --git a/src/Scenes/ScenePathName.h b/src/Scenes/ScenePathName.h index d03e9f7e6..0a9a7eee1 100644 --- a/src/Scenes/ScenePathName.h +++ b/src/Scenes/ScenePathName.h @@ -36,6 +36,8 @@ namespace caret { virtual ~ScenePathName(); + virtual ScenePathName* castToScenePathName(); + virtual const ScenePathName* castToScenePathName() const; void setValue(const AString& value); diff --git a/src/Scenes/ScenePathNameArray.cxx b/src/Scenes/ScenePathNameArray.cxx index fc2d565d3..ad6f5f09e 100644 --- a/src/Scenes/ScenePathNameArray.cxx +++ b/src/Scenes/ScenePathNameArray.cxx @@ -122,6 +122,18 @@ ScenePathNameArray::~ScenePathNameArray() m_values.clear(); } +/** + * Cast an instance of SceneObjectArray to a ScenePathNameArray. + * Is used to avoid dynamic casting and overridden by the class. + * + * @return Valid pointer (non-NULL) this is SceneClass + */ +ScenePathNameArray* +ScenePathNameArray::castToScenePathNameArray() +{ + return this; +} + /** * Cast an instance of SceneObjectArray to a ScenePathNameArray. * Is used to avoid dynamic casting and overridden by the class. diff --git a/src/Scenes/ScenePathNameArray.h b/src/Scenes/ScenePathNameArray.h index 52a9c7a50..d639a15a0 100644 --- a/src/Scenes/ScenePathNameArray.h +++ b/src/Scenes/ScenePathNameArray.h @@ -45,6 +45,8 @@ namespace caret { virtual ~ScenePathNameArray(); + virtual ScenePathNameArray* castToScenePathNameArray(); + virtual const ScenePathNameArray* castToScenePathNameArray() const; void setScenePathNameAtIndex(const int32_t arrayIndex, diff --git a/src/Scenes/ScenePrimitive.cxx b/src/Scenes/ScenePrimitive.cxx index 550a561b7..fa29bcd6c 100644 --- a/src/Scenes/ScenePrimitive.cxx +++ b/src/Scenes/ScenePrimitive.cxx @@ -62,6 +62,18 @@ ScenePrimitive::~ScenePrimitive() } +/** + * Cast an instance of SceneObject to a ScenePrimitive. + * Is used to avoid dynamic casting and overridden by the class. + * + * @return Valid pointer (non-NULL) this is ScenePrimitive + */ +ScenePrimitive* +ScenePrimitive::castToScenePrimitive() +{ + return this; +} + /** * Cast an instance of SceneObject to a ScenePrimitive. * Is used to avoid dynamic casting and overridden by the class. diff --git a/src/Scenes/ScenePrimitive.h b/src/Scenes/ScenePrimitive.h index ee0d4a0e5..cbe27681b 100644 --- a/src/Scenes/ScenePrimitive.h +++ b/src/Scenes/ScenePrimitive.h @@ -31,6 +31,8 @@ namespace caret { public: virtual ~ScenePrimitive(); + virtual ScenePrimitive* castToScenePrimitive(); + virtual const ScenePrimitive* castToScenePrimitive() const; protected: diff --git a/src/Scenes/ScenePrimitiveArray.cxx b/src/Scenes/ScenePrimitiveArray.cxx index f2a1dfd92..56b3084ad 100644 --- a/src/Scenes/ScenePrimitiveArray.cxx +++ b/src/Scenes/ScenePrimitiveArray.cxx @@ -61,6 +61,18 @@ ScenePrimitiveArray::~ScenePrimitiveArray() } +/** + * Cast an instance of SceneObjectArray to a ScenePrimitiveArray. + * Is used to avoid dynamic casting and overridden by the class. + * + * @return Valid pointer (non-NULL) this is SceneClass + */ +ScenePrimitiveArray* +ScenePrimitiveArray::castToScenePrimitiveArray() +{ + return this; +} + /** * Cast an instance of SceneObjectArray to a ScenePrimitiveArray. * Is used to avoid dynamic casting and overridden by the class. diff --git a/src/Scenes/ScenePrimitiveArray.h b/src/Scenes/ScenePrimitiveArray.h index 4d25fa219..80596884b 100644 --- a/src/Scenes/ScenePrimitiveArray.h +++ b/src/Scenes/ScenePrimitiveArray.h @@ -31,6 +31,8 @@ namespace caret { public: virtual ~ScenePrimitiveArray(); + virtual ScenePrimitiveArray* castToScenePrimitiveArray(); + virtual const ScenePrimitiveArray* castToScenePrimitiveArray() const; protected: diff --git a/src/Scenes/SceneXmlStreamReader.cxx b/src/Scenes/SceneXmlStreamReader.cxx index 941fd7c2a..15699c616 100644 --- a/src/Scenes/SceneXmlStreamReader.cxx +++ b/src/Scenes/SceneXmlStreamReader.cxx @@ -23,7 +23,35 @@ #include "SceneXmlStreamReader.h" #undef __SCENE_XML_STREAM_READER_DECLARE__ +#include +#include + +#include + #include "CaretAssert.h" +#include "CaretLogger.h" +#include "DataFile.h" +#include "Scene.h" +#include "SceneBoolean.h" +#include "SceneBooleanArray.h" +#include "SceneClass.h" +#include "SceneClassArray.h" +#include "SceneEnumeratedType.h" +#include "SceneEnumeratedTypeArray.h" +#include "SceneFloat.h" +#include "SceneFloatArray.h" +#include "SceneInteger.h" +#include "SceneIntegerArray.h" +#include "SceneObjectMapIntegerKey.h" +#include "ScenePathName.h" +#include "ScenePathNameArray.h" +#include "SceneSaxReader.h" +#include "SceneString.h" +#include "SceneStringArray.h" +#include "SceneUnsignedByte.h" +#include "SceneUnsignedByteArray.h" +#include "SceneXmlElements.h" + using namespace caret; @@ -50,3 +78,731 @@ SceneXmlStreamReader::~SceneXmlStreamReader() { } +/** + * Read a scene from the given XML stream reader. It assumes that + * the start element for the scene has already been read and is + * the current element. If xmlReader.hasError() is set after this + * method is called, there was an error reading the scene. + * + * @param xmlReader + * The XML stream reader + * @param scene + * The scene + * @param sceneFileName + * Name of the scene file + */ +void +SceneXmlStreamReader::readScene(QXmlStreamReader& xmlReader, + Scene* scene, + const AString& sceneFileName) +{ + CaretAssert(scene); + m_filename = sceneFileName; + + if (xmlReader.name() == ELEMENT_SCENE) { + /* + * Set when ending scene element is found + */ + bool endElementFound(false); + + while ( ( ! xmlReader.atEnd()) + && ( ! endElementFound)) { + xmlReader.readNext(); + + switch (xmlReader.tokenType()) { + case QXmlStreamReader::StartElement: + { + if (xmlReader.name() == ELEMENT_SCENE_NAME) { + scene->setName(xmlReader.readElementText()); + } + else if (xmlReader.name() == ELEMENT_SCENE_DESCRIPTION) { + scene->setDescription(xmlReader.readElementText()); + } + else if (xmlReader.name() == ELEMENT_OBJECT) { + SceneObject* object = readSceneObject(xmlReader); + if (object != NULL) { + if (object->castToSceneClass()) { + scene->addClass(object->castToSceneClass()); + } + else { + delete object; + xmlReader.raiseError("Child of Scene is not a SceneClass"); + } + } + } + else { + m_unrecognizedElements.insert(xmlReader.name().toString()); + xmlReader.skipCurrentElement(); + } + } + break; + case QXmlStreamReader::EndElement: + if (xmlReader.name() == ELEMENT_SCENE) { + endElementFound = true; + } + break; + default: + break; + } + } + } + else { + xmlReader.raiseError("Element should be \"" + + ELEMENT_SCENE + + "\" but is \"" + + xmlReader.name().toString() + + "\" while reading Scene"); + } +} + +/** + * Read any of the scene object sub-classes + * + * @param xmlReader + * The XML stream reader + * @return + * Pointer to object read or NULL if not valid + */ +SceneObject* +SceneXmlStreamReader::readSceneObject(QXmlStreamReader& xmlReader) +{ + SceneObject* sceneObject(NULL); + + if (xmlReader.name() == ELEMENT_OBJECT) { + sceneObject = readSceneObjectSingle(xmlReader); + } + else if (xmlReader.name() == ELEMENT_OBJECT_ARRAY) { + sceneObject = readSceneObjectArray(xmlReader); + } + else if (xmlReader.name() == ELEMENT_OBJECT_MAP) { + sceneObject = readSceneObjectMap(xmlReader); + } + else { + xmlReader.raiseError("Unexpected element \"" + + xmlReader.name().toString() + + "\" that is none of " + + ELEMENT_OBJECT + + ", " + + ELEMENT_OBJECT_ARRAY + + ", " + + ELEMENT_OBJECT_MAP); + } + return sceneObject; +} + +/** + * Read any of the 'single' scene object sub-classes. + * 'Single' is one item (not an array nor a map) + * + * @param xmlReader + * The XML stream reader + * @return + * Pointer to object read or NULL if not valid + */ +SceneObject* +SceneXmlStreamReader::readSceneObjectSingle(QXmlStreamReader& xmlReader) +{ + if (xmlReader.name() != ELEMENT_OBJECT) { + xmlReader.raiseError("Current element should be " + + ELEMENT_OBJECT + + " at beginning of readSceneObjectSingle" + + " but is \"" + + xmlReader.name().toString()); + return NULL; + } + + const QXmlStreamAttributes attributes = xmlReader.attributes(); + const QString typeString = attributes.value(ATTRIBUTE_OBJECT_TYPE).toString(); + const QString className = attributes.value(ATTRIBUTE_OBJECT_CLASS).toString(); + const QString name = attributes.value(ATTRIBUTE_OBJECT_NAME).toString(); + const QString versionString = attributes.value(ATTRIBUTE_OBJECT_VERSION).toString(); + + SceneObjectDataTypeEnum::Enum dataType = SceneObjectDataTypeEnum::SCENE_INVALID; + + AString errorString; + if (typeString.isEmpty()) { + errorString.appendWithNewLine(ATTRIBUTE_OBJECT_TYPE + + " is missing on " + + ELEMENT_OBJECT); + } + else { + bool typeStringValid(false); + dataType = SceneObjectDataTypeEnum::fromXmlName(typeString, + &typeStringValid); + if ( ! typeStringValid) { + errorString.appendWithNewLine(ATTRIBUTE_OBJECT_TYPE + + " \"" + + typeString + + "\" is invalid on " + + ELEMENT_OBJECT); + } + } + + if (name.isEmpty()) { + errorString.appendWithNewLine(ATTRIBUTE_OBJECT_NAME + + " is missing on " + + ELEMENT_OBJECT); + } + if (dataType == SceneObjectDataTypeEnum::SCENE_CLASS) { + if (className.isEmpty()) { + errorString.appendWithNewLine(ATTRIBUTE_OBJECT_CLASS + + " is missing on " + + ELEMENT_OBJECT); + } + if (versionString.isEmpty()) { + errorString.appendWithNewLine(ATTRIBUTE_OBJECT_VERSION + + " is missing on " + + ELEMENT_OBJECT); + } + } + if ( ! errorString.isEmpty()) { + xmlReader.raiseError(errorString); + return NULL; + } + + SceneObject* sceneObject(NULL); + + SceneClass* sceneClass(NULL); + switch (dataType) { + case SceneObjectDataTypeEnum::SCENE_BOOLEAN: + sceneObject = new SceneBoolean(name, + AString(xmlReader.readElementText()).toBool()); + break; + case SceneObjectDataTypeEnum::SCENE_CLASS: + { + sceneClass = new SceneClass(name, + className, + versionString.toInt()); + sceneObject = sceneClass; + } + break; + case SceneObjectDataTypeEnum::SCENE_ENUMERATED_TYPE: + sceneObject = new SceneEnumeratedType(name, + xmlReader.readElementText()); + break; + case SceneObjectDataTypeEnum::SCENE_FLOAT: + sceneObject = new SceneFloat(name, + xmlReader.readElementText().toFloat()); + break; + case SceneObjectDataTypeEnum::SCENE_INTEGER: + sceneObject = new SceneInteger(name, + xmlReader.readElementText().toInt()); + break; + case SceneObjectDataTypeEnum::SCENE_INVALID: + CaretAssert(0); + break; + case SceneObjectDataTypeEnum::SCENE_PATH_NAME: + { + ScenePathName* pathName = new ScenePathName(name, + ""); + pathName->setValueToAbsolutePath(m_filename, + xmlReader.readElementText()); + sceneObject = pathName; + } + break; + case SceneObjectDataTypeEnum::SCENE_STRING: + sceneObject = new SceneString(name, + xmlReader.readElementText()); + break; + case SceneObjectDataTypeEnum::SCENE_UNSIGNED_BYTE: + { + uint32_t value = xmlReader.readElementText().toUInt(); + if (value > std::numeric_limits::max()) { + value = std::numeric_limits::max(); + } + const uint8_t byteValue = static_cast(value); + sceneObject = new SceneUnsignedByte(name, + byteValue); + } + break; + } + + /* + * Note: The 'primitive' values are read using QXmlStreamReader::readElementText() + * which reads throught the end element. Only a 'class' needs to continue until + * the end element is found + */ + if (sceneClass != NULL) { + /* + * Set when ending scene element is found + */ + bool endElementFound(false); + + while ( ( ! xmlReader.atEnd()) + && ( ! endElementFound)) { + xmlReader.readNext(); + + switch (xmlReader.tokenType()) { + case QXmlStreamReader::StartElement: + if (sceneClass != NULL) { + SceneObject* child = readSceneObject(xmlReader); + if (child != NULL) { + sceneClass->addChild(child); + } + } + else { + AString msg("Should not find a start element when reading a primitive type (not a class).." + "Element name \"" + + xmlReader.name() + + "\" at line " + + AString::number(xmlReader.lineNumber()) + + " column " + + AString::number(xmlReader.columnNumber())); + CaretAssertMessage(0, msg); + } + break; + case QXmlStreamReader::EndElement: + if (xmlReader.name() == ELEMENT_OBJECT) { + endElementFound = true; + } + break; + default: + break; + } + } + } + + return sceneObject; +} + +/** + * Read an array. + * + * @param xmlReader + * The XML stream reader + * @return + * Pointer to array read or NULL if not valid + */ +SceneObjectArray* +SceneXmlStreamReader::readSceneObjectArray(QXmlStreamReader& xmlReader) +{ + if (xmlReader.name() != ELEMENT_OBJECT_ARRAY) { + xmlReader.raiseError("Current element should be " + + ELEMENT_OBJECT_ARRAY + + " at beginning of readSceneObjectArray" + + " but is \"" + + xmlReader.name().toString()); + return NULL; + } + + const QXmlStreamAttributes arrayAttributes = xmlReader.attributes(); + const QString typeString = arrayAttributes.value(ATTRIBUTE_OBJECT_ARRAY_TYPE).toString(); + const QString name = arrayAttributes.value(ATTRIBUTE_OBJECT_ARRAY_NAME).toString(); + const QString lengthString = arrayAttributes.value(ATTRIBUTE_OBJECT_ARRAY_LENGTH).toString(); + + SceneObjectDataTypeEnum::Enum dataType = SceneObjectDataTypeEnum::SCENE_INVALID; + + AString errorString; + if (typeString.isEmpty()) { + errorString.appendWithNewLine(ATTRIBUTE_OBJECT_ARRAY_TYPE + + " is missing on " + + ELEMENT_OBJECT_ARRAY); + } + else { + bool typeStringValid(false); + dataType = SceneObjectDataTypeEnum::fromXmlName(typeString, + &typeStringValid); + if ( ! typeStringValid) { + errorString.appendWithNewLine(ATTRIBUTE_OBJECT_ARRAY_TYPE + + " \"" + + typeString + + "\" is invalid on " + + ELEMENT_OBJECT_ARRAY); + } + } + if (name.isEmpty()) { + errorString.appendWithNewLine(ATTRIBUTE_OBJECT_ARRAY_NAME + + " is missing on " + + ELEMENT_OBJECT_ARRAY); + } + + int32_t arrayLength(0); + if (lengthString.isEmpty()) { + errorString.appendWithNewLine(ATTRIBUTE_OBJECT_ARRAY_LENGTH + + " is missing on " + + ELEMENT_OBJECT_ARRAY); + } + else { + arrayLength = lengthString.toInt(); + if (arrayLength < 0) { + errorString.appendWithNewLine(ATTRIBUTE_OBJECT_ARRAY_LENGTH + + "=" + + lengthString + + " is invalid on " + + ELEMENT_OBJECT_ARRAY); + } +// else if (arrayLength == 0) { +// return NULL; +// } + } + + if ( ! errorString.isEmpty()) { + xmlReader.raiseError(errorString); + return NULL; + } + + SceneObjectArray* sceneArray(NULL); + SceneClassArray* classArray(NULL); + SceneEnumeratedTypeArray* enumeratedTypeArray(NULL); + ScenePathNameArray* pathNameArray(NULL); + SceneBooleanArray* booleanArray(NULL); + SceneFloatArray* floatArray(NULL); + SceneIntegerArray* integerArray(NULL); + SceneStringArray* stringArray(NULL); + SceneUnsignedByteArray* unsignedByteArray; + switch (dataType) { + case SceneObjectDataTypeEnum::SCENE_BOOLEAN: + booleanArray = new SceneBooleanArray(name, + arrayLength); + sceneArray = booleanArray; + break; + case SceneObjectDataTypeEnum::SCENE_CLASS: + classArray = new SceneClassArray(name, + arrayLength); + sceneArray = classArray; + break; + case SceneObjectDataTypeEnum::SCENE_ENUMERATED_TYPE: + enumeratedTypeArray = new SceneEnumeratedTypeArray(name, + arrayLength); + sceneArray = enumeratedTypeArray; + break; + case SceneObjectDataTypeEnum::SCENE_FLOAT: + floatArray = new SceneFloatArray(name, + arrayLength); + sceneArray = floatArray; + break; + case SceneObjectDataTypeEnum::SCENE_INTEGER: + integerArray = new SceneIntegerArray(name, + arrayLength); + sceneArray = integerArray; + break; + case SceneObjectDataTypeEnum::SCENE_INVALID: + CaretAssert(0); + break; + case SceneObjectDataTypeEnum::SCENE_PATH_NAME: + pathNameArray = new ScenePathNameArray(name, + arrayLength); + sceneArray = pathNameArray; + break; + case SceneObjectDataTypeEnum::SCENE_STRING: + stringArray = new SceneStringArray(name, + arrayLength); + sceneArray = stringArray; + break; + case SceneObjectDataTypeEnum::SCENE_UNSIGNED_BYTE: + unsignedByteArray = new SceneUnsignedByteArray(name, + arrayLength); + sceneArray = unsignedByteArray; + break; + } + CaretAssert(sceneArray); + + /* + * Set when ending scene element is found + */ + bool endElementFound(false); + + int32_t sceneArrayElementIndex = -1; + while ( ( ! xmlReader.atEnd()) + && ( ! endElementFound)) { + xmlReader.readNext(); + + switch (xmlReader.tokenType()) { + case QXmlStreamReader::StartElement: + if (xmlReader.name() == ELEMENT_OBJECT_ARRAY_ELEMENT) { + const QXmlStreamAttributes elementAttributes = xmlReader.attributes(); + + int32_t elementIndex(-1); + const QString elementIndexString = elementAttributes.value(ATTRIBUTE_OBJECT_ARRAY_ELEMENT_INDEX).toString(); + if (elementIndexString.isEmpty()) { + errorString.appendWithNewLine(ATTRIBUTE_OBJECT_ARRAY_ELEMENT_INDEX + + " is missing on " + + ELEMENT_OBJECT_ARRAY_ELEMENT); + } + else { + elementIndex = elementIndexString.toInt(); + if (elementIndex < 0) { + errorString.appendWithNewLine(ATTRIBUTE_OBJECT_ARRAY_ELEMENT_INDEX + + "=" + + elementIndexString + + " is invalid on " + + ELEMENT_OBJECT_ARRAY_ELEMENT); + } + } + if ( ! errorString.isEmpty()) { + xmlReader.raiseError(errorString); + if (sceneArray != NULL) { + delete sceneArray; + } + return NULL; + } + + switch (dataType) { + case SceneObjectDataTypeEnum::SCENE_BOOLEAN: + CaretAssert(booleanArray); + booleanArray->setValue(elementIndex, + AString(xmlReader.readElementText()).toBool()); + break; + case SceneObjectDataTypeEnum::SCENE_CLASS: + /* + * Child class is handled when start element is found + */ + sceneArrayElementIndex = elementIndex; + break; + case SceneObjectDataTypeEnum::SCENE_ENUMERATED_TYPE: + CaretAssert(enumeratedTypeArray); + enumeratedTypeArray->setValue(elementIndex, + xmlReader.readElementText()); + break; + case SceneObjectDataTypeEnum::SCENE_FLOAT: + CaretAssert(floatArray); + floatArray->setValue(elementIndex, + xmlReader.readElementText().toFloat()); + break; + case SceneObjectDataTypeEnum::SCENE_INTEGER: + CaretAssert(integerArray); + integerArray->setValue(elementIndex, + xmlReader.readElementText().toInt()); + break; + case SceneObjectDataTypeEnum::SCENE_INVALID: + CaretAssert(0); + break; + case SceneObjectDataTypeEnum::SCENE_PATH_NAME: + CaretAssert(pathNameArray); + pathNameArray->setScenePathNameAtIndex(elementIndex, + m_filename, + xmlReader.readElementText()); + break; + case SceneObjectDataTypeEnum::SCENE_STRING: + CaretAssert(stringArray); + stringArray->setValue(elementIndex, + xmlReader.readElementText()); + break; + case SceneObjectDataTypeEnum::SCENE_UNSIGNED_BYTE: + { + CaretAssert(unsignedByteArray); + uint32_t value = xmlReader.readElementText().toUInt(); + if (value > std::numeric_limits::max()) { + value = std::numeric_limits::max(); + } + const uint8_t byteValue = static_cast(value); + unsignedByteArray->setValue(elementIndex, + byteValue); + } + break; + } + } + else if (sceneArrayElementIndex >= 0) { + /* + * Must be child of a scene class + */ + CaretAssert(classArray); + SceneObject* elementObject = readSceneObject(xmlReader); + if (elementObject != NULL) { + SceneClass* elementClass = elementObject->castToSceneClass(); + CaretAssert(elementClass); + classArray->setClassAtIndex(sceneArrayElementIndex, elementClass); + } + } + else { + + } + + break; + case QXmlStreamReader::EndElement: + if (xmlReader.name() == ELEMENT_OBJECT_ARRAY) { + endElementFound = true; + } + else if (xmlReader.name() == ELEMENT_OBJECT_ARRAY_ELEMENT) { + sceneArrayElementIndex = -1; + } + break; + default: + break; + } + } + + return sceneArray; +} + +/** + * Read a map. + * + * @param xmlReader + * The XML stream reader + * @return + * Pointer to map read or NULL if not valid + */ +SceneObjectMapIntegerKey* +SceneXmlStreamReader::readSceneObjectMap(QXmlStreamReader& xmlReader) +{ + if (xmlReader.name() != ELEMENT_OBJECT_MAP) { + xmlReader.raiseError("Current element should be " + + ELEMENT_OBJECT_MAP + + " at beginning of readSceneObjectMap" + + " but is \"" + + xmlReader.name().toString()); + return NULL; + } + + const QXmlStreamAttributes mapAttributes = xmlReader.attributes(); + const QString typeString = mapAttributes.value(ATTRIBUTE_OBJECT_MAP_TYPE).toString(); + const QString name = mapAttributes.value(ATTRIBUTE_OBJECT_MAP_NAME).toString(); + + SceneObjectDataTypeEnum::Enum dataType = SceneObjectDataTypeEnum::SCENE_INVALID; + + AString errorString; + if (typeString.isEmpty()) { + errorString.appendWithNewLine(ATTRIBUTE_OBJECT_MAP_TYPE + + " is missing on " + + ELEMENT_OBJECT_MAP); + } + else { + bool typeStringValid(false); + dataType = SceneObjectDataTypeEnum::fromXmlName(typeString, + &typeStringValid); + if ( ! typeStringValid) { + errorString.appendWithNewLine(ATTRIBUTE_OBJECT_MAP_TYPE + + " \"" + + typeString + + "\" is invalid on " + + ELEMENT_OBJECT_MAP); + } + } + if (name.isEmpty()) { + errorString.appendWithNewLine(ATTRIBUTE_OBJECT_MAP_NAME + + " is missing on " + + ELEMENT_OBJECT_MAP); + } + + if ( ! errorString.isEmpty()) { + xmlReader.raiseError(errorString); + return NULL; + } + + SceneObjectMapIntegerKey* sceneMap = new SceneObjectMapIntegerKey(name, + dataType); + + /* + * Set when ending scene element is found + */ + bool endElementFound(false); + + int32_t sceneKeyIndex = -1; + while ( ( ! xmlReader.atEnd()) + && ( ! endElementFound)) { + xmlReader.readNext(); + + switch (xmlReader.tokenType()) { + case QXmlStreamReader::StartElement: + if (xmlReader.name() == ELEMENT_OBJECT_MAP_VALUE) { + const QXmlStreamAttributes valueAttributes = xmlReader.attributes(); + int32_t keyIndex(-1); + const QString keyString = valueAttributes.value(ATTRIBUTE_OBJECT_MAP_VALUE_KEY).toString(); + if (keyString.isEmpty()) { + errorString.appendWithNewLine(ATTRIBUTE_OBJECT_MAP_VALUE_KEY + + " is missing on " + + ELEMENT_OBJECT_MAP_VALUE); + } + else { + keyIndex = keyString.toInt(); + if (keyIndex < 0) { + errorString.appendWithNewLine(ATTRIBUTE_OBJECT_MAP_VALUE_KEY + + "=" + + keyIndex + + " is invalid on " + + ELEMENT_OBJECT_MAP_VALUE); + } + } + if ( ! errorString.isEmpty()) { + xmlReader.raiseError(); + if (sceneMap != NULL) { + delete sceneMap; + } + return NULL; + } + + switch (dataType) { + case SceneObjectDataTypeEnum::SCENE_BOOLEAN: + sceneMap->addBoolean(keyIndex, + AString(xmlReader.readElementText()).toBool()); + break; + case SceneObjectDataTypeEnum::SCENE_CLASS: + /* + * Child class is handled when start element is found + */ + sceneKeyIndex = keyIndex; + break; + case SceneObjectDataTypeEnum::SCENE_ENUMERATED_TYPE: + sceneMap->addEnumeratedType(keyIndex, + xmlReader.readElementText()); + break; + case SceneObjectDataTypeEnum::SCENE_FLOAT: + sceneMap->addFloat(keyIndex, + xmlReader.readElementText().toFloat()); + break; + case SceneObjectDataTypeEnum::SCENE_INTEGER: + sceneMap->addInteger(keyIndex, + xmlReader.readElementText().toInt()); + break; + case SceneObjectDataTypeEnum::SCENE_INVALID: + CaretAssert(0); + break; + case SceneObjectDataTypeEnum::SCENE_PATH_NAME: + { + ScenePathName spn("spn", ""); + spn.setValueToAbsolutePath(m_filename, + xmlReader.readElementText()); + sceneMap->addPathName(keyIndex, + spn.toString()); + } + break; + case SceneObjectDataTypeEnum::SCENE_STRING: + sceneMap->addString(keyIndex, + xmlReader.readElementText()); + break; + case SceneObjectDataTypeEnum::SCENE_UNSIGNED_BYTE: + { + uint32_t value = xmlReader.readElementText().toUInt(); + if (value > std::numeric_limits::max()) { + value = std::numeric_limits::max(); + } + const uint8_t byteValue = static_cast(value); + sceneMap->addUnsignedByte(keyIndex, + byteValue); + } + break; + } + } + else if (sceneKeyIndex >= 0) { + /* + * Must be child of a scene class + */ + SceneObject* elementObject = readSceneObject(xmlReader); + if (elementObject != NULL) { + SceneClass* elementClass = elementObject->castToSceneClass(); + CaretAssert(elementClass); + sceneMap->addClass(sceneKeyIndex, + elementClass); + } + } + else { + + } + + break; + case QXmlStreamReader::EndElement: + if (xmlReader.name() == ELEMENT_OBJECT_MAP) { + endElementFound = true; + } + else if (xmlReader.name() == ELEMENT_OBJECT_MAP_VALUE) { + sceneKeyIndex = -1; + } + break; + default: + break; + } + } + + return sceneMap; +} diff --git a/src/Scenes/SceneXmlStreamReader.h b/src/Scenes/SceneXmlStreamReader.h index ccf969631..78fb4ac17 100644 --- a/src/Scenes/SceneXmlStreamReader.h +++ b/src/Scenes/SceneXmlStreamReader.h @@ -24,13 +24,19 @@ #include +#include #include "SceneXmlStreamBase.h" - +class QXmlStreamReader; namespace caret { + class Scene; + class SceneObject; + class SceneObjectArray; + class SceneObjectMapIntegerKey; + class SceneXmlStreamReader : public SceneXmlStreamBase { public: @@ -41,11 +47,26 @@ namespace caret { SceneXmlStreamReader(const SceneXmlStreamReader&) = delete; SceneXmlStreamReader& operator=(const SceneXmlStreamReader&) = delete; - + + void readScene(QXmlStreamReader& xmlReader, + Scene* scene, + const AString& sceneFileName); // ADD_NEW_METHODS_HERE private: + SceneObject* readSceneObject(QXmlStreamReader& xmlReader); + + SceneObject* readSceneObjectSingle(QXmlStreamReader& xmlReader); + + SceneObjectArray* readSceneObjectArray(QXmlStreamReader& xmlReader); + + SceneObjectMapIntegerKey* readSceneObjectMap(QXmlStreamReader& xmlReader); + + std::set m_unrecognizedElements; + + AString m_filename; + // ADD_NEW_MEMBERS_HERE }; -- GitLab From 0b5eaf85dd9f6996a63275b04fafa4ef1bc77520 Mon Sep 17 00:00:00 2001 From: John Harwell Date: Tue, 29 Jan 2019 14:36:37 -0600 Subject: [PATCH 175/427] WB-834 Macros: Some cleanup of macros, scene files, and moved macros from SceneInfo to Scene inside the scene file. --- src/Common/WuQMacroGroup.cxx | 9 + src/Common/WuQMacroGroup.h | 2 + src/Files/SceneFile.cxx | 299 +++++++++++-------- src/Files/SceneFile.h | 24 +- src/Files/SceneFileSaxReader.cxx | 7 +- src/Files/SceneFileXmlStreamFormatTester.cxx | 19 +- src/Files/SceneFileXmlStreamReader.cxx | 3 + src/Files/SceneFileXmlStreamWriter.cxx | 4 +- src/GuiQt/GuiMacroHelper.cxx | 2 +- src/Scenes/Scene.cxx | 33 ++ src/Scenes/Scene.h | 8 + src/Scenes/SceneInfo.cxx | 50 +--- src/Scenes/SceneInfo.h | 19 +- src/Scenes/SceneInfoSaxReader.cxx | 13 +- src/Scenes/SceneInfoXmlStreamReader.cxx | 6 - src/Scenes/SceneInfoXmlStreamWriter.cxx | 4 - src/Scenes/SceneXmlStreamReader.cxx | 20 ++ src/Scenes/SceneXmlStreamWriter.cxx | 8 + 18 files changed, 301 insertions(+), 229 deletions(-) diff --git a/src/Common/WuQMacroGroup.cxx b/src/Common/WuQMacroGroup.cxx index c319c4098..00a029605 100644 --- a/src/Common/WuQMacroGroup.cxx +++ b/src/Common/WuQMacroGroup.cxx @@ -181,6 +181,15 @@ WuQMacroGroup::getNumberOfMacros() const return rowCount(); } +/** + * @return Is this empty (contains no macros) ? + */ +bool +WuQMacroGroup::isEmpty() const +{ + return (getNumberOfMacros() <= 0); +} + /** * Get the macro with the given name * diff --git a/src/Common/WuQMacroGroup.h b/src/Common/WuQMacroGroup.h index 026028c03..b4b19b5c0 100644 --- a/src/Common/WuQMacroGroup.h +++ b/src/Common/WuQMacroGroup.h @@ -58,6 +58,8 @@ namespace caret { int32_t getNumberOfMacros() const; + bool isEmpty() const; + WuQMacro* getMacroByName(const QString& name); const WuQMacro* getMacroByName(const QString& name) const; diff --git a/src/Files/SceneFile.cxx b/src/Files/SceneFile.cxx index f9b2aad0d..1399da267 100644 --- a/src/Files/SceneFile.cxx +++ b/src/Files/SceneFile.cxx @@ -51,6 +51,7 @@ #include "SceneWriterXml.h" #include "SpecFile.h" #include "SystemUtilities.h" +#include "WuQMacroGroup.h" #include "XmlSaxParser.h" #include "XmlUtilities.h" #include "XmlWriter.h" @@ -779,126 +780,124 @@ SceneFile::writeFile(const AString& filename) xmlStreamWriter.writeFile(this); } else { - // - // Format the version string so that it ends with at most one zero - // Version 4 Added for New Annotation Coordinate Space "SPACER" since annotations - // may be saved to scene file. - // - const AString versionString = AString::number(SceneFile::getFileVersion(), - 'f', - 1); + writeFileSaxWriter(filename); - // - // Open the file - // - FileAdapter file; - AString errorMessage; - QTextStream* textStream = file.openQTextStreamForWritingFile(this->getFileName(), - errorMessage); - if (textStream == NULL) { - throw DataFileException(filename, - errorMessage); - } - - // - // Create the xml writer - // - XmlWriter xmlWriter(*textStream); - - // - // Write header info - // - xmlWriter.writeStartDocument("1.0"); - - // - // Write root element - // - XmlAttributes attributes; - - //attributes.addAttribute("xmlns:xsi", - // "http://www.w3.org/2001/XMLSchema-instance"); - //attributes.addAttribute("xsi:noNamespaceSchemaLocation", - // "http://brainvis.wustl.edu/caret6/xml_schemas/GIFTI_Caret.xsd"); - attributes.addAttribute(SceneFile::XML_ATTRIBUTE_VERSION, - versionString); - xmlWriter.writeStartElement(SceneFile::XML_TAG_SCENE_FILE, - attributes); - - // - // Write Metadata - // - if (m_metadata != NULL) { - m_metadata->writeAsXML(xmlWriter); - } - - const int32_t numScenes = this->getNumberOfScenes(); - - /* - * Write the scene info directory - */ - xmlWriter.writeStartElement(SceneFile::XML_TAG_SCENE_INFO_DIRECTORY_TAG); - xmlWriter.writeElementCData(SceneXmlElements::SCENE_INFO_BALSA_STUDY_ID_TAG, - getBalsaStudyID()); - xmlWriter.writeElementCData(SceneXmlElements::SCENE_INFO_BALSA_STUDY_TITLE_TAG, - getBalsaStudyTitle()); - switch (getBasePathType()) { - case SceneFileBasePathTypeEnum::AUTOMATIC: - xmlWriter.writeElementCData(SceneXmlElements::SCENE_INFO_BALSA_BASE_DIRECTORY_TAG, - ""); - break; - case SceneFileBasePathTypeEnum::CUSTOM: - { - /* - * Write base path as a path RELATIVE to the scene file - * but only when base path type is CUSTOM - * Note: we do not use FileInformation::getCanonicalFilePath() - * because it returns an empty string if the file DOES NOT exist - * and this may occur since the file may be new and has not - * been closed. - */ - if ( ! getBalsaCustomBaseDirectory().isEmpty()) { - const AString baseDirAbsPath = FileInformation(getBalsaCustomBaseDirectory()).getAbsoluteFilePath(); - const AString sceneFileAbsPath = FileInformation(filename).getAbsoluteFilePath(); - ScenePathName basePathName("basePathName", - baseDirAbsPath); - - const AString relativeBasePath = basePathName.getRelativePathToSceneFile(sceneFileAbsPath); - - //std::cout << "baseDirAbsPath: " << baseDirAbsPath << std::endl; - //std::cout << "sceneFileAbsPath: " << sceneFileAbsPath << std::endl; - //std::cout << "relativeTempFileName: " << relativeBasePath << std::endl; - - xmlWriter.writeElementCData(SceneXmlElements::SCENE_INFO_BALSA_BASE_DIRECTORY_TAG, - relativeBasePath); - } - } - break; - } - xmlWriter.writeElementCData(SceneXmlElements::SCENE_INFO_BALSA_EXTRACT_TO_DIRECTORY_TAG, - getBalsaExtractToDirectoryName()); - xmlWriter.writeElementCData(SceneXmlElements::SCENE_INFO_BASE_PATH_TYPE, - SceneFileBasePathTypeEnum::toName(getBasePathType())); - - for (int32_t i = 0; i < numScenes; i++) { - m_scenes[i]->getSceneInfo()->writeSceneInfo(xmlWriter, - i); - } - xmlWriter.writeEndElement(); - - // - // Write scenes - // - SceneWriterXml sceneWriter(xmlWriter, - this->getFileName()); - for (int32_t i = 0; i < numScenes; i++) { - sceneWriter.writeScene(*m_scenes[i], - i); - } - - xmlWriter.writeEndElement(); - xmlWriter.writeEndDocument(); - - file.close(); +// /* +// * This writes an old file format that does not support macros +// */ +// const AString versionString = AString::number(getSceneFileVersionBeforeMacros()); +// +// // +// // Open the file +// // +// FileAdapter file; +// AString errorMessage; +// QTextStream* textStream = file.openQTextStreamForWritingFile(this->getFileName(), +// errorMessage); +// if (textStream == NULL) { +// throw DataFileException(filename, +// errorMessage); +// } +// +// // +// // Create the xml writer +// // +// XmlWriter xmlWriter(*textStream); +// +// // +// // Write header info +// // +// xmlWriter.writeStartDocument("1.0"); +// +// // +// // Write root element +// // +// XmlAttributes attributes; +// +// //attributes.addAttribute("xmlns:xsi", +// // "http://www.w3.org/2001/XMLSchema-instance"); +// //attributes.addAttribute("xsi:noNamespaceSchemaLocation", +// // "http://brainvis.wustl.edu/caret6/xml_schemas/GIFTI_Caret.xsd"); +// attributes.addAttribute(SceneFile::XML_ATTRIBUTE_VERSION, +// versionString); +// xmlWriter.writeStartElement(SceneFile::XML_TAG_SCENE_FILE, +// attributes); +// +// // +// // Write Metadata +// // +// if (m_metadata != NULL) { +// m_metadata->writeAsXML(xmlWriter); +// } +// +// const int32_t numScenes = this->getNumberOfScenes(); +// +// /* +// * Write the scene info directory +// */ +// xmlWriter.writeStartElement(SceneFile::XML_TAG_SCENE_INFO_DIRECTORY_TAG); +// xmlWriter.writeElementCData(SceneXmlElements::SCENE_INFO_BALSA_STUDY_ID_TAG, +// getBalsaStudyID()); +// xmlWriter.writeElementCData(SceneXmlElements::SCENE_INFO_BALSA_STUDY_TITLE_TAG, +// getBalsaStudyTitle()); +// switch (getBasePathType()) { +// case SceneFileBasePathTypeEnum::AUTOMATIC: +// xmlWriter.writeElementCData(SceneXmlElements::SCENE_INFO_BALSA_BASE_DIRECTORY_TAG, +// ""); +// break; +// case SceneFileBasePathTypeEnum::CUSTOM: +// { +// /* +// * Write base path as a path RELATIVE to the scene file +// * but only when base path type is CUSTOM +// * Note: we do not use FileInformation::getCanonicalFilePath() +// * because it returns an empty string if the file DOES NOT exist +// * and this may occur since the file may be new and has not +// * been closed. +// */ +// if ( ! getBalsaCustomBaseDirectory().isEmpty()) { +// const AString baseDirAbsPath = FileInformation(getBalsaCustomBaseDirectory()).getAbsoluteFilePath(); +// const AString sceneFileAbsPath = FileInformation(filename).getAbsoluteFilePath(); +// ScenePathName basePathName("basePathName", +// baseDirAbsPath); +// +// const AString relativeBasePath = basePathName.getRelativePathToSceneFile(sceneFileAbsPath); +// +// //std::cout << "baseDirAbsPath: " << baseDirAbsPath << std::endl; +// //std::cout << "sceneFileAbsPath: " << sceneFileAbsPath << std::endl; +// //std::cout << "relativeTempFileName: " << relativeBasePath << std::endl; +// +// xmlWriter.writeElementCData(SceneXmlElements::SCENE_INFO_BALSA_BASE_DIRECTORY_TAG, +// relativeBasePath); +// } +// } +// break; +// } +// xmlWriter.writeElementCData(SceneXmlElements::SCENE_INFO_BALSA_EXTRACT_TO_DIRECTORY_TAG, +// getBalsaExtractToDirectoryName()); +// xmlWriter.writeElementCData(SceneXmlElements::SCENE_INFO_BASE_PATH_TYPE, +// SceneFileBasePathTypeEnum::toName(getBasePathType())); +// +// for (int32_t i = 0; i < numScenes; i++) { +// m_scenes[i]->getSceneInfo()->writeSceneInfo(xmlWriter, +// i); +// } +// xmlWriter.writeEndElement(); +// +// // +// // Write scenes +// // +// SceneWriterXml sceneWriter(xmlWriter, +// this->getFileName()); +// for (int32_t i = 0; i < numScenes; i++) { +// sceneWriter.writeScene(*m_scenes[i], +// i); +// } +// +// xmlWriter.writeEndElement(); +// xmlWriter.writeEndDocument(); +// +// file.close(); } this->clearModified(); @@ -925,19 +924,22 @@ SceneFile::writeFileSaxWriter(const AString& filename) { CaretLogWarning("scene file '" + filename + "' should be saved ending in .scene"); } + + for (const auto s : m_scenes) { + if ( ! s->getMacroGroup()->isEmpty()) { + throw DataFileException("OLD scene writer does not support scene files containing macros. Use stream writer"); + } + } + checkFileWritability(filename); this->setFileName(filename); try { - // - // Format the version string so that it ends with at most one zero - // Version 4 Added for New Annotation Coordinate Space "SPACER" since annotations - // may be saved to scene file. - // - const AString versionString = AString::number(SceneFile::getFileVersion(), - 'f', - 1); + /* + * This writes an old file format that does not support macros + */ + const AString versionString = AString::number(getSceneFileVersionBeforeMacros()); // // Open the file @@ -1050,7 +1052,7 @@ SceneFile::writeFileSaxWriter(const AString& filename) xmlWriter.writeEndDocument(); file.close(); - + this->clearModified(); } catch (const GiftiException& e) { @@ -1595,4 +1597,45 @@ SceneFile::clearModified() } } +/** + * @return The version number to use when writing + * an instance of a scene. This number returned + * may version depending upon the content of the scene + * and may allow older versions of wb_view to read + * the scene file when it does not contain new stuff. + */ +int32_t +SceneFile::getSceneFileVersionForWriting() const +{ + int32_t version = s_sceneFileVersionBeforeMacros; + + for (const auto s : m_scenes) { + if ( ! s->getMacroGroup()->isEmpty()) { + version = s_sceneFileVersionContainingMacros; + break; + } + } + return version; +} + +/** + * @return The maximum scene file version supported + * by the scene file. + */ +int32_t +SceneFile::getMaxiumSupportedSceneFileVersion() +{ + return s_sceneFileVersionContainingMacros; +} + +/** + * @return The scene file version before macros were added. + */ +int32_t +SceneFile::getSceneFileVersionBeforeMacros() +{ + return s_sceneFileVersionBeforeMacros; +} + + diff --git a/src/Files/SceneFile.h b/src/Files/SceneFile.h index 6ecf38dd9..84411b25f 100644 --- a/src/Files/SceneFile.h +++ b/src/Files/SceneFile.h @@ -168,8 +168,11 @@ namespace caret { // ADD_NEW_METHODS_HERE - /** Version of file */ - static float getFileVersion() { return s_sceneFileVersion; } + int32_t getSceneFileVersionForWriting() const; + + static int32_t getSceneFileVersionBeforeMacros(); + + static int32_t getMaxiumSupportedSceneFileVersion(); /** XML Tag for scene file */ static const AString XML_TAG_SCENE_FILE; @@ -207,15 +210,26 @@ namespace caret { // ADD_NEW_MEMBERS_HERE - /** Version of this SceneFile */ - static const float s_sceneFileVersion; + /** Version of this SceneFile before addition of macros */ + static const int32_t s_sceneFileVersionBeforeMacros; + + /** Version of this SceneFile containing macros */ + static const int32_t s_sceneFileVersionContainingMacros; }; #ifdef __SCENE_FILE_DECLARE__ const AString SceneFile::XML_TAG_SCENE_FILE = "SceneFile"; const AString SceneFile::XML_ATTRIBUTE_VERSION = "Version"; const AString SceneFile::XML_TAG_SCENE_INFO_DIRECTORY_TAG = "SceneInfoDirectory"; - const float SceneFile::s_sceneFileVersion = 3.0; + + /* + * NOTE: If these scene file versions change, getSupportedSceneFileVersion() + * will need to be updated with the maximum scene file version that + * can be read + */ + const int32_t SceneFile::s_sceneFileVersionBeforeMacros = 3; + const int32_t SceneFile::s_sceneFileVersionContainingMacros = 4; + #endif // __SCENE_FILE_DECLARE__ } // namespace diff --git a/src/Files/SceneFileSaxReader.cxx b/src/Files/SceneFileSaxReader.cxx index 6d09e2a33..dd0b7f31f 100644 --- a/src/Files/SceneFileSaxReader.cxx +++ b/src/Files/SceneFileSaxReader.cxx @@ -103,9 +103,10 @@ SceneFileSaxReader::startElement(const AString& namespaceURI, // // Check version of file being read // - const float version = attributes.getValueAsFloat(SceneFile::XML_ATTRIBUTE_VERSION); - if (version > SceneFile::getFileVersion()) { - AString msg = XmlUtilities::createInvalidVersionMessage(SceneFile::getFileVersion(), + const float versionFloat = attributes.getValueAsFloat(SceneFile::XML_ATTRIBUTE_VERSION); + const int32_t version = static_cast(versionFloat); + if (version > SceneFile::getSceneFileVersionBeforeMacros()) { + AString msg = XmlUtilities::createInvalidVersionMessage(SceneFile::getSceneFileVersionBeforeMacros(), version); XmlSaxParserException e(msg); CaretLogThrowing(e); diff --git a/src/Files/SceneFileXmlStreamFormatTester.cxx b/src/Files/SceneFileXmlStreamFormatTester.cxx index b73f78988..27c6dc8de 100644 --- a/src/Files/SceneFileXmlStreamFormatTester.cxx +++ b/src/Files/SceneFileXmlStreamFormatTester.cxx @@ -153,7 +153,7 @@ SceneFileXmlStreamFormatTester::testReading(const QString& filename, if (printResultsFlag) { const QString resultString(successFlag - ? "MATCHED" + ? "READING MATCHED" : "FAILED"); std::cout << resultString << std::endl; std::cout << " Original File: " << origFileInfo.getFileName() << std::endl; @@ -225,7 +225,7 @@ SceneFileXmlStreamFormatTester::testWriting(const QString& filename, if (printResultsFlag) { const QString resultString(successFlag - ? "MATCHED" + ? "WRITING MATCHED" : "FAILED"); std::cout << resultString << std::endl; std::cout << " Original File: " << origFileInfo.getFileName() << std::endl; @@ -284,10 +284,9 @@ SceneFileXmlStreamFormatTester::timeReadingAndWriting(const QString& filename) FileInformation fileInfo(filename); fileInfo.getFileComponents(path, name, ext); - try { std::cout << "Timing (seconds) for " << fileInfo.getFileName() << std::endl; - { + try { SceneFile sceneFile; ElapsedTimer timer; timer.start(); @@ -301,9 +300,12 @@ SceneFileXmlStreamFormatTester::timeReadingAndWriting(const QString& filename) timer.start(); sceneFile.writeFileSaxWriter(tempName); std::cout << " Write OLD: " << timer.getElapsedTimeSeconds() << std::endl; - } + } + catch (const DataFileException& dfe) { + std::cout << "Test OLD Failed: " << dfe.whatString() << std::endl; + } - { + try { SceneFile sceneFile; ElapsedTimer timer; timer.start(); @@ -318,10 +320,9 @@ SceneFileXmlStreamFormatTester::timeReadingAndWriting(const QString& filename) sceneFile.writeFileStreamWriter(tempName); std::cout << " Write NEW: " << timer.getElapsedTimeSeconds() << std::endl; } - } catch (const DataFileException& dfe) { - std::cout << "Failed: " << dfe.whatString() << std::endl; + std::cout << "Test New Failed: " << dfe.whatString() << std::endl; } - + return successFlag; } diff --git a/src/Files/SceneFileXmlStreamReader.cxx b/src/Files/SceneFileXmlStreamReader.cxx index 733afc3c5..8b2e202ea 100644 --- a/src/Files/SceneFileXmlStreamReader.cxx +++ b/src/Files/SceneFileXmlStreamReader.cxx @@ -150,6 +150,9 @@ SceneFileXmlStreamReader::readFileContent(QXmlStreamReader& xmlReader, const QStringRef versionAtt = atts.value(ATTRIBUTE_SCENE_FILE_VERSION); m_fileVersion = 1; if ( ! versionAtt.isEmpty()) { + /* + * Note: version was float in previous versions, so get as float and convert to int + */ m_fileVersion = static_cast(versionAtt.toFloat()); } diff --git a/src/Files/SceneFileXmlStreamWriter.cxx b/src/Files/SceneFileXmlStreamWriter.cxx index 3471551c1..5fae42fac 100644 --- a/src/Files/SceneFileXmlStreamWriter.cxx +++ b/src/Files/SceneFileXmlStreamWriter.cxx @@ -119,9 +119,7 @@ SceneFileXmlStreamWriter::writeFileContentToXmlStreamWriter(QXmlStreamWriter& xm { CaretAssert(sceneFile); - const AString versionString = AString::number(SceneFile::getFileVersion(), - 'f', - 1); + const AString versionString = AString::number(sceneFile->getSceneFileVersionForWriting()); xmlWriter.setAutoFormatting(true); diff --git a/src/GuiQt/GuiMacroHelper.cxx b/src/GuiQt/GuiMacroHelper.cxx index cce9dd873..5e8323d53 100644 --- a/src/GuiQt/GuiMacroHelper.cxx +++ b/src/GuiQt/GuiMacroHelper.cxx @@ -96,7 +96,7 @@ GuiMacroHelper::getMacroGroups() if ( ! activeSceneEvent.isError()) { Scene* activeScene = activeSceneEvent.getScene(); if (activeScene != NULL) { - macroGroups.push_back(activeScene->getSceneInfo()->getMacroGroup()); + macroGroups.push_back(activeScene->getMacroGroup()); } } diff --git a/src/Scenes/Scene.cxx b/src/Scenes/Scene.cxx index 9f29f260d..32acb4a18 100644 --- a/src/Scenes/Scene.cxx +++ b/src/Scenes/Scene.cxx @@ -27,6 +27,7 @@ #include "SceneAttributes.h" #include "SceneClass.h" #include "SceneInfo.h" +#include "WuQMacroGroup.h" using namespace caret; @@ -237,6 +238,12 @@ Scene::Scene(const SceneTypeEnum::Enum sceneType) this); m_hasFilesWithRemotePaths = false; m_sceneInfo = new SceneInfo(); + + static int counter = 1; + const AString macroGroupName("SceneFile_" + + AString::number(counter)); + m_macroGroup.reset(new WuQMacroGroup(macroGroupName)); + m_macroGroup->clearModified(); } Scene::Scene(const Scene& rhs) @@ -378,6 +385,8 @@ void Scene::setName(const AString& sceneName) { m_sceneInfo->setName(sceneName); + m_macroGroup->setName("Scene: " + + sceneName); } /** @@ -512,6 +521,10 @@ Scene::isModified() const return true; } + if (m_macroGroup->isModified()) { + return true; + } + return false; } @@ -523,6 +536,26 @@ Scene::clearModified() { CaretObjectTracksModification::clearModified(); m_sceneInfo->clearModified(); + m_macroGroup->clearModified(); } +/** + * @return The macro group + */ +WuQMacroGroup* +Scene::getMacroGroup() +{ + return m_macroGroup.get(); +} + +/** + * @return The macro group (const method) + */ +const WuQMacroGroup* +Scene::getMacroGroup() const +{ + return m_macroGroup.get(); +} + + diff --git a/src/Scenes/Scene.h b/src/Scenes/Scene.h index 939edc7d2..97430a743 100644 --- a/src/Scenes/Scene.h +++ b/src/Scenes/Scene.h @@ -21,6 +21,7 @@ */ /*LICENSE_END*/ +#include #include "CaretObjectTracksModification.h" #include "SceneTypeEnum.h" @@ -30,6 +31,7 @@ namespace caret { class SceneClass; class SceneInfo; class SceneObject; + class WuQMacroGroup; class Scene : public CaretObjectTracksModification { @@ -91,6 +93,10 @@ namespace caret { static void setSceneBeingCreatedHasFilesWithRemotePaths(); + WuQMacroGroup* getMacroGroup(); + + const WuQMacroGroup* getMacroGroup() const; + private: /** Attributes of the scene*/ @@ -105,6 +111,8 @@ namespace caret { /** True if it found a ScenePathName with a remote file */ bool m_hasFilesWithRemotePaths; + std::unique_ptr m_macroGroup; + /** When a scene is being created, this will be set */ static Scene* s_sceneBeingCreated; diff --git a/src/Scenes/SceneInfo.cxx b/src/Scenes/SceneInfo.cxx index e3347042d..0c3de6da4 100644 --- a/src/Scenes/SceneInfo.cxx +++ b/src/Scenes/SceneInfo.cxx @@ -26,16 +26,12 @@ #include "CaretAssert.h" #include "CaretLogger.h" #include "SceneXmlElements.h" -#include "WuQMacroGroup.h" -#include "WuQMacroGroupXmlStreamWriter.h" #include "XmlAttributes.h" #include "XmlUtilities.h" #include "XmlWriter.h" -using namespace caret; - +using namespace caret; - /** * \class caret::SceneInfo * \brief Contains information about a scene. @@ -48,11 +44,6 @@ using namespace caret; SceneInfo::SceneInfo() : CaretObjectTracksModification() { - static int counter = 1; - const AString macroGroupName("SceneFile_" - + AString::number(counter)); - m_macroGroup.reset(new WuQMacroGroup(macroGroupName)); - m_macroGroup->clearModified(); } SceneInfo::SceneInfo(const SceneInfo& rhs) : CaretObjectTracksModification() @@ -81,10 +72,6 @@ SceneInfo::isModified() const return true; } - if (m_macroGroup->isModified()) { - return true; - } - return false; } @@ -95,8 +82,6 @@ void SceneInfo::clearModified() { CaretObjectTracksModification::clearModified(); - - m_macroGroup->clearModified(); } /** @@ -118,8 +103,6 @@ SceneInfo::setName(const AString& sceneName) { if (sceneName != m_sceneName) { m_sceneName = sceneName; - m_macroGroup->setName("Scene: " - + m_sceneName); setModified(); } } @@ -222,24 +205,6 @@ SceneInfo::setBalsaSceneID(const AString& balsaSceneID) } } -/** - * @return The macro group - */ -WuQMacroGroup* -SceneInfo::getMacroGroup() -{ - return m_macroGroup.get(); -} - -/** - * @return The macro group (const method) - */ -const WuQMacroGroup* -SceneInfo::getMacroGroup() const -{ - return m_macroGroup.get(); -} - /** * Write the scene info element. * @@ -268,19 +233,6 @@ SceneInfo::writeSceneInfo(XmlWriter& xmlWriter, xmlWriter.writeElementCData(SceneXmlElements::SCENE_INFO_DESCRIPTION_TAG, m_sceneDescription); - /* - * Write macro group - */ - if (m_macroGroup->getNumberOfMacros() > 0) { - QString textString; - WuQMacroGroupXmlStreamWriter macroWriter; - macroWriter.writeToString(m_macroGroup.get(), - textString); - textString = XmlUtilities::encodeXmlSpecialCharacters(textString); - xmlWriter.writeElementCharacters(SceneXmlElements::SCENE_INFO_MACRO_GROUP, - textString); - } - writeSceneInfoImage(xmlWriter, SceneXmlElements::SCENE_INFO_IMAGE_TAG, m_imageBytes, diff --git a/src/Scenes/SceneInfo.h b/src/Scenes/SceneInfo.h index abffe5a1d..7fb9326f7 100644 --- a/src/Scenes/SceneInfo.h +++ b/src/Scenes/SceneInfo.h @@ -27,8 +27,6 @@ namespace caret { - class WuQMacroGroup; - class XmlWriter; class SceneInfo : public CaretObjectTracksModification { @@ -46,8 +44,6 @@ namespace caret { AString getName() const; - void setName(const AString& sceneName); - AString getDescription() const; void setDescription(const AString& description); @@ -76,11 +72,13 @@ namespace caret { const QByteArray& imageBytes, const AString& imageFormat) const; - WuQMacroGroup* getMacroGroup(); - - const WuQMacroGroup* getMacroGroup() const; - private: + /* + * setName() is private as users should call Scene::setName() + * but the XML readers are allowed to call setName() + */ + void setName(const AString& sceneName); + SceneInfo& operator=(const SceneInfo&); /** name of scene*/ @@ -98,10 +96,11 @@ namespace caret { /** format of thumbnail image (eg: jpg, ppm, etc.) */ AString m_imageFormat; - std::unique_ptr m_macroGroup; - // ADD_NEW_MEMBERS_HERE + friend class Scene; + friend class SceneInfoSaxReader; + friend class SceneInfoXmlStreamReader; }; #ifdef __SCENE_INFO_DECLARE__ diff --git a/src/Scenes/SceneInfoSaxReader.cxx b/src/Scenes/SceneInfoSaxReader.cxx index 47b1914e9..04ac99f2b 100644 --- a/src/Scenes/SceneInfoSaxReader.cxx +++ b/src/Scenes/SceneInfoSaxReader.cxx @@ -24,7 +24,6 @@ #include "SceneInfo.h" #include "SceneInfoSaxReader.h" #include "SceneXmlElements.h" -#include "WuQMacroGroupXmlStreamReader.h" #include "XmlAttributes.h" #include "XmlException.h" #include "XmlUtilities.h" @@ -177,16 +176,8 @@ SceneInfoSaxReader::endElement(const AString& /* namspaceURI */, case STATE_SCENE_INFO_UNRECOGNIZED: break; case STATE_SCENE_INFO_MACRO_GROUP: - { - WuQMacroGroup* macroGroup = m_sceneInfo->getMacroGroup(); - QString errorMessage; - WuQMacroGroupXmlStreamReader reader; - if ( ! reader.readFromString(m_elementText, - macroGroup, - errorMessage)) { - CaretLogSevere(errorMessage); - } - } + /* Should skip over macro group */ + CaretLogSevere("Encountered WuQMacroGroup while reading. Need to use newer stream reader."); break; } diff --git a/src/Scenes/SceneInfoXmlStreamReader.cxx b/src/Scenes/SceneInfoXmlStreamReader.cxx index 4c37e17b8..86e7131e6 100644 --- a/src/Scenes/SceneInfoXmlStreamReader.cxx +++ b/src/Scenes/SceneInfoXmlStreamReader.cxx @@ -28,7 +28,6 @@ #include "CaretAssert.h" #include "SceneInfo.h" -#include "WuQMacroGroupXmlStreamReader.h" using namespace caret; @@ -110,11 +109,6 @@ SceneInfoXmlStreamReader::readSceneInfo(QXmlStreamReader& xmlReader, encodingName, formatName); } - else if (xmlReader.name() == WuQMacroGroupXmlStreamReader::ELEMENT_MACRO_GROUP) { - WuQMacroGroupXmlStreamReader macroGroupReader; - macroGroupReader.readMacroGroup(xmlReader, - sceneInfo->getMacroGroup()); - } else { m_unexpectedXmlElements.insert(xmlReader.name().toString()); xmlReader.skipCurrentElement(); diff --git a/src/Scenes/SceneInfoXmlStreamWriter.cxx b/src/Scenes/SceneInfoXmlStreamWriter.cxx index 30022d6f1..47159fb40 100644 --- a/src/Scenes/SceneInfoXmlStreamWriter.cxx +++ b/src/Scenes/SceneInfoXmlStreamWriter.cxx @@ -87,10 +87,6 @@ SceneInfoXmlStreamWriter::writeXML(QXmlStreamWriter* xmlWriter, m_xmlWriter->writeTextElement(ELEMENT_DESCRIPTION, sceneInfo->getDescription()); - WuQMacroGroupXmlStreamWriter macroGroupXmlWriter; - macroGroupXmlWriter.writeXml(xmlWriter, - sceneInfo->getMacroGroup()); - writeImageElement(sceneInfo); m_xmlWriter->writeEndElement(); diff --git a/src/Scenes/SceneXmlStreamReader.cxx b/src/Scenes/SceneXmlStreamReader.cxx index 15699c616..084cf9c42 100644 --- a/src/Scenes/SceneXmlStreamReader.cxx +++ b/src/Scenes/SceneXmlStreamReader.cxx @@ -51,6 +51,8 @@ #include "SceneUnsignedByte.h" #include "SceneUnsignedByteArray.h" #include "SceneXmlElements.h" +#include "WuQMacroGroup.h" +#include "WuQMacroGroupXmlStreamReader.h" using namespace caret; @@ -97,6 +99,10 @@ SceneXmlStreamReader::readScene(QXmlStreamReader& xmlReader, const AString& sceneFileName) { CaretAssert(scene); + if (scene == NULL) { + return; + } + m_filename = sceneFileName; if (xmlReader.name() == ELEMENT_SCENE) { @@ -118,6 +124,11 @@ SceneXmlStreamReader::readScene(QXmlStreamReader& xmlReader, else if (xmlReader.name() == ELEMENT_SCENE_DESCRIPTION) { scene->setDescription(xmlReader.readElementText()); } + else if (xmlReader.name() == WuQMacroGroupXmlStreamReader::ELEMENT_MACRO_GROUP) { + WuQMacroGroupXmlStreamReader macroGroupReader; + macroGroupReader.readMacroGroup(xmlReader, + scene->getMacroGroup()); + } else if (xmlReader.name() == ELEMENT_OBJECT) { SceneObject* object = readSceneObject(xmlReader); if (object != NULL) { @@ -145,6 +156,15 @@ SceneXmlStreamReader::readScene(QXmlStreamReader& xmlReader, break; } } + + if ( ! xmlReader.hasError()) { + /* + * This will cause update of the macro's name + * using the scene's name that is actually + * inside of SceneInfo. + */ + scene->setName(scene->getName()); + } } else { xmlReader.raiseError("Element should be \"" diff --git a/src/Scenes/SceneXmlStreamWriter.cxx b/src/Scenes/SceneXmlStreamWriter.cxx index eb5441ecb..ef5d2b77e 100644 --- a/src/Scenes/SceneXmlStreamWriter.cxx +++ b/src/Scenes/SceneXmlStreamWriter.cxx @@ -40,6 +40,8 @@ #include "ScenePrimitive.h" #include "ScenePrimitiveArray.h" #include "SceneXmlElements.h" +#include "WuQMacroGroup.h" +#include "WuQMacroGroupXmlStreamWriter.h" using namespace caret; @@ -107,6 +109,12 @@ SceneXmlStreamWriter::writeXML(QXmlStreamWriter* xmlWriter, m_xmlWriter->writeTextElement(ELEMENT_SCENE_DESCRIPTION, scene->getDescription()); + if (scene->getMacroGroup()->getNumberOfMacros() > 0) { + WuQMacroGroupXmlStreamWriter macroGroupXmlWriter; + macroGroupXmlWriter.writeXml(xmlWriter, + scene->getMacroGroup()); + } + const int32_t numClasses = scene->getNumberOfClasses(); for (int32_t i = 0; i < numClasses; i++) { writeSceneClass(scene->getClassAtIndex(i)); -- GitLab From 3d77251b02ce14196be5345702db8aef214b67ee Mon Sep 17 00:00:00 2001 From: John Harwell Date: Wed, 30 Jan 2019 15:14:18 -0600 Subject: [PATCH 176/427] WB-834 Macros: Revised macro commands so that they may have a variable number of parameters. --- src/Common/CMakeLists.txt | 2 + src/Common/WuQMacroCommand.cxx | 393 ++++++------------ src/Common/WuQMacroCommand.h | 41 +- src/Common/WuQMacroCommandParameter.cxx | 158 +++++++ src/Common/WuQMacroCommandParameter.h | 79 ++++ src/Common/WuQMacroGroupXmlStreamBase.h | 27 +- src/Common/WuQMacroGroupXmlStreamReader.cxx | 329 ++++++++------- src/Common/WuQMacroGroupXmlStreamReader.h | 3 + src/Common/WuQMacroGroupXmlStreamWriter.cxx | 109 ++--- src/GuiQt/WuQMacroDialog.cxx | 436 +++++++++----------- src/GuiQt/WuQMacroDialog.h | 46 ++- src/GuiQt/WuQMacroExecutor.cxx | 198 ++++++--- src/GuiQt/WuQMacroSignalWatcher.cxx | 239 ++++++++--- src/GuiQt/WuQMacroSignalWatcher.h | 10 +- 14 files changed, 1195 insertions(+), 875 deletions(-) create mode 100644 src/Common/WuQMacroCommandParameter.cxx create mode 100644 src/Common/WuQMacroCommandParameter.h diff --git a/src/Common/CMakeLists.txt b/src/Common/CMakeLists.txt index cfd35acd6..e1d1dfe75 100644 --- a/src/Common/CMakeLists.txt +++ b/src/Common/CMakeLists.txt @@ -164,6 +164,7 @@ WorkbenchSpecialVersionEnum.h WuQMacro.h WuQMacroClassTypeEnum.h WuQMacroCommand.h +WuQMacroCommandParameter.h WuQMacroDataValueTypeEnum.h WuQMacroFile.h WuQMacroGroup.h @@ -279,6 +280,7 @@ WorkbenchSpecialVersionEnum.cxx WuQMacro.cxx WuQMacroClassTypeEnum.cxx WuQMacroCommand.cxx +WuQMacroCommandParameter.cxx WuQMacroDataValueTypeEnum.cxx WuQMacroFile.cxx WuQMacroGroup.cxx diff --git a/src/Common/WuQMacroCommand.cxx b/src/Common/WuQMacroCommand.cxx index 17be63c2b..5c15ab0f2 100644 --- a/src/Common/WuQMacroCommand.cxx +++ b/src/Common/WuQMacroCommand.cxx @@ -25,6 +25,7 @@ #include "CaretAssert.h" #include "CaretLogger.h" +#include "WuQMacroCommandParameter.h" #include "WuQMacroMouseEventInfo.h" #include "WuQMacroStandardItemTypeEnum.h" @@ -52,8 +53,6 @@ WuQMacroCommand::WuQMacroCommand(const WuQMacroClassTypeEnum::Enum classType, : WuQMacroCommand(classType, objectName, objectDescriptiveName, - "", - "", "") { if (objectDescriptiveName.isEmpty()) { @@ -73,24 +72,16 @@ WuQMacroCommand::WuQMacroCommand(const WuQMacroClassTypeEnum::Enum classType, * Descriptive name of macro command * @param objectToolTip * ToolTip for object - * @param dataValue - * Data value for the command. - * @param dataValueTwo - * Second Data value for the command. */ WuQMacroCommand::WuQMacroCommand(const WuQMacroClassTypeEnum::Enum classType, const QString& objectName, const QString& objectDescriptiveName, - const QString& objectToolTip, - const QVariant dataValue, - const QVariant dataValueTwo) + const QString& objectToolTip) : QStandardItem(), TracksModificationInterface(), m_classType(classType), m_objectName(objectName), m_descriptiveName(objectDescriptiveName), -m_dataValue(dataValue), -m_dataValueTwo(dataValueTwo), m_macroMouseEvent(NULL) { if (objectDescriptiveName.isEmpty()) { @@ -102,120 +93,7 @@ m_macroMouseEvent(NULL) | Qt::ItemIsSelectable); setToolTip(objectToolTip); - /* - * Second data value is used by only some commands - */ - m_dataType = WuQMacroDataValueTypeEnum::INVALID; - m_dataTypeTwo = WuQMacroDataValueTypeEnum::INVALID; - switch (m_classType) { - case WuQMacroClassTypeEnum::ACTION: - m_dataType = WuQMacroDataValueTypeEnum::NONE; - m_dataValueUpdateLabelText = "Click/Select"; - break; - case WuQMacroClassTypeEnum::ACTION_CHECKABLE: - m_dataType = WuQMacroDataValueTypeEnum::BOOLEAN; - m_dataValueUpdateLabelText = "On/Off"; - break; - case WuQMacroClassTypeEnum::ACTION_GROUP: - m_dataType = WuQMacroDataValueTypeEnum::STRING; - m_dataTypeTwo = WuQMacroDataValueTypeEnum::INTEGER; - m_dataValueUpdateLabelText = "Select name"; - m_dataValueTwoUpdateLabelText = "Select by index"; - m_dataValueTwoValidFlag = true; - break; - case WuQMacroClassTypeEnum::BUTTON_GROUP: - m_dataType = WuQMacroDataValueTypeEnum::STRING; - m_dataTypeTwo = WuQMacroDataValueTypeEnum::INTEGER; - m_dataValueUpdateLabelText = "Select button with name"; - m_dataValueTwoUpdateLabelText = "Select button at index"; - m_dataValueTwoValidFlag = true; - break; - case WuQMacroClassTypeEnum::CHECK_BOX: - m_dataType = WuQMacroDataValueTypeEnum::BOOLEAN; - m_dataValueUpdateLabelText = "Check On/Off"; - break; - case WuQMacroClassTypeEnum::COMBO_BOX: - m_dataType = WuQMacroDataValueTypeEnum::STRING; - m_dataValueUpdateLabelText = "Select item with name"; - m_dataTypeTwo = WuQMacroDataValueTypeEnum::INTEGER; - m_dataValueTwoUpdateLabelText = "Select item at index"; - m_dataValueTwoValidFlag = true; - break; - case WuQMacroClassTypeEnum::DOUBLE_SPIN_BOX: - m_dataValueUpdateLabelText = "New Value"; - m_dataType = WuQMacroDataValueTypeEnum::FLOAT; - break; - case WuQMacroClassTypeEnum::INVALID: - break; - case WuQMacroClassTypeEnum::LINE_EDIT: - m_dataType = WuQMacroDataValueTypeEnum::STRING; - m_dataValueUpdateLabelText = "New Text"; - break; - case WuQMacroClassTypeEnum::LIST_WIDGET: - m_dataType = WuQMacroDataValueTypeEnum::STRING; - m_dataValueUpdateLabelText = "Select item with name"; - m_dataTypeTwo = WuQMacroDataValueTypeEnum::INTEGER; - m_dataValueTwoUpdateLabelText = "Select item at index"; - m_dataValueTwoValidFlag = true; - break; - case WuQMacroClassTypeEnum::MENU: - m_dataType = WuQMacroDataValueTypeEnum::STRING; - m_dataValueUpdateLabelText = "Select item with name"; - m_dataTypeTwo = WuQMacroDataValueTypeEnum::INTEGER; - m_dataValueTwoUpdateLabelText = "Select menu item at index"; - m_dataValueTwoValidFlag = true; - break; - case WuQMacroClassTypeEnum::MOUSE_USER_EVENT: - m_dataType = WuQMacroDataValueTypeEnum::MOUSE; - break; - case WuQMacroClassTypeEnum::PUSH_BUTTON: - m_dataType = WuQMacroDataValueTypeEnum::NONE; - m_dataValueUpdateLabelText = "Click button"; - break; - case WuQMacroClassTypeEnum::PUSH_BUTTON_CHECKABLE: - m_dataType = WuQMacroDataValueTypeEnum::BOOLEAN; - m_dataValueUpdateLabelText = "On/Off"; - break; - case WuQMacroClassTypeEnum::RADIO_BUTTON: - /* Note: Radio buttons cannot be "unselected" */ - m_dataType = WuQMacroDataValueTypeEnum::NONE; - m_dataValueUpdateLabelText = "Select button"; - break; - case WuQMacroClassTypeEnum::SLIDER: - m_dataType = WuQMacroDataValueTypeEnum::INTEGER; - m_dataValueUpdateLabelText = "Move slider to"; - break; - case WuQMacroClassTypeEnum::SPIN_BOX: - m_dataType = WuQMacroDataValueTypeEnum::INTEGER; - m_dataValueUpdateLabelText = "Enter value"; - break; - case WuQMacroClassTypeEnum::TAB_BAR: - m_dataType = WuQMacroDataValueTypeEnum::STRING; - m_dataTypeTwo = WuQMacroDataValueTypeEnum::INTEGER; - m_dataValueTwoValidFlag = true; - m_dataValueUpdateLabelText = "Select tab with name"; - m_dataValueTwoUpdateLabelText = "Select tab at index"; - break; - case WuQMacroClassTypeEnum::TAB_WIDGET: - m_dataType = WuQMacroDataValueTypeEnum::STRING; - m_dataTypeTwo = WuQMacroDataValueTypeEnum::INTEGER; - m_dataValueTwoValidFlag = true; - m_dataValueUpdateLabelText = "Select tab with name"; - m_dataValueTwoUpdateLabelText = "Select tab at index"; - break; - case WuQMacroClassTypeEnum::TOOL_BUTTON: - m_dataType = WuQMacroDataValueTypeEnum::NONE; - m_dataValueUpdateLabelText = "Select button"; - break; - case WuQMacroClassTypeEnum::TOOL_BUTTON_CHECKABLE: - m_dataType = WuQMacroDataValueTypeEnum::BOOLEAN; - m_dataValueUpdateLabelText = "On/Off"; - break; - } - - CaretAssert(m_dataType != WuQMacroDataValueTypeEnum::INVALID); - updateTitle(); setModified(); } @@ -238,8 +116,6 @@ TracksModificationInterface(), m_classType(WuQMacroClassTypeEnum::MOUSE_USER_EVENT), m_objectName(objectName), m_descriptiveName(objectDescriptiveName), -m_dataValue((int)0), -m_dataValueTwo(""), m_macroMouseEvent(mouseEventInfo) { if (objectDescriptiveName.isEmpty()) { @@ -249,8 +125,6 @@ m_macroMouseEvent(mouseEventInfo) setFlags(Qt::ItemIsEnabled | Qt::ItemIsSelectable); - m_dataType = WuQMacroDataValueTypeEnum::MOUSE; - m_dataTypeTwo = WuQMacroDataValueTypeEnum::STRING; setText("Mouse"); setModified(); @@ -264,6 +138,7 @@ WuQMacroCommand::~WuQMacroCommand() if (m_macroMouseEvent != NULL) { delete m_macroMouseEvent; } + removeAllParameters(); } /** @@ -321,34 +196,18 @@ WuQMacroCommand::copyHelperWuQMacroCommand(const WuQMacroCommand& obj) m_descriptiveName = obj.m_descriptiveName; m_classType = obj.m_classType; m_objectName = obj.m_objectName; - m_dataValue = obj.m_dataValue; - m_dataValueUpdateLabelText = obj.m_dataValueUpdateLabelText; - m_dataType = obj.m_dataType; - m_dataTypeTwo = obj.m_dataTypeTwo; - m_dataValueTwo = obj.m_dataValueTwo; - m_dataValueTwoUpdateLabelText = obj.m_dataValueTwoUpdateLabelText; m_macroMouseEvent = NULL; m_delayInSeconds = obj.m_delayInSeconds; - switch (m_dataType) { - case WuQMacroDataValueTypeEnum::INVALID: - break; - case WuQMacroDataValueTypeEnum::BOOLEAN: - break; - case WuQMacroDataValueTypeEnum::FLOAT: - break; - case WuQMacroDataValueTypeEnum::INTEGER: - break; - case WuQMacroDataValueTypeEnum::MOUSE: - if (obj.m_macroMouseEvent != NULL) { - m_macroMouseEvent = new WuQMacroMouseEventInfo(*obj.m_macroMouseEvent); - } - break; - case WuQMacroDataValueTypeEnum::NONE: - break; - case WuQMacroDataValueTypeEnum::STRING: - break; + if (obj.m_macroMouseEvent != NULL) { + m_macroMouseEvent = new WuQMacroMouseEventInfo(*obj.m_macroMouseEvent); } + + removeAllParameters(); + for (const auto p : obj.m_parameters) { + m_parameters.push_back(new WuQMacroCommandParameter(*p)); + } + setText(obj.text()); updateTitle(); } @@ -361,64 +220,76 @@ WuQMacroCommand::updateTitle() { QString title = "Unknown"; + QVariant dataValue; + if (getNumberOfParameters() > 0) { + CaretAssertVectorIndex(m_parameters, 0); + dataValue = m_parameters[0]->getValue(); + } + + QVariant dataValueTwo; + if (getNumberOfParameters() > 1) { + CaretAssertVectorIndex(m_parameters, 1); + dataValueTwo = m_parameters[1]->getValue(); + } + switch (m_classType) { case WuQMacroClassTypeEnum::ACTION: title = ("Turn " - + QString((m_dataValue.toBool() ? "On" : "Off"))); + + QString((dataValue.toBool() ? "On" : "Off"))); break; case WuQMacroClassTypeEnum::ACTION_CHECKABLE: title = ("Turn " - + QString((m_dataValue.toBool() ? "On" : "Off"))); + + QString((dataValue.toBool() ? "On" : "Off"))); break; case WuQMacroClassTypeEnum::ACTION_GROUP: title = ("Select Name " - + m_dataValue.toString() + + dataValue.toString() + " else " + " index " - + QString::number(m_dataValueTwo.toInt())); + + QString::number(dataValueTwo.toInt())); break; case WuQMacroClassTypeEnum::BUTTON_GROUP: title = ("Select Name " - + m_dataValue.toString() + + dataValue.toString() + " else " + " index " - + QString::number(m_dataValueTwo.toInt())); + + QString::number(dataValueTwo.toInt())); break; case WuQMacroClassTypeEnum::CHECK_BOX: title = ("Turn " - + QString((m_dataValue.toBool() ? "On" : "Off"))); + + QString((dataValue.toBool() ? "On" : "Off"))); break; case WuQMacroClassTypeEnum::COMBO_BOX: title = ("Select Name " - + m_dataValue.toString() + + dataValue.toString() + " else " + " index " - + QString::number(m_dataValueTwo.toInt())); + + QString::number(dataValueTwo.toInt())); break; case WuQMacroClassTypeEnum::DOUBLE_SPIN_BOX: title = ("Set to value " - + QString::number(m_dataValue.toFloat())); + + QString::number(dataValue.toFloat())); break; case WuQMacroClassTypeEnum::INVALID: title = ""; break; case WuQMacroClassTypeEnum::LINE_EDIT: title = ("Set to text " - + m_dataValue.toString()); + + dataValue.toString()); break; case WuQMacroClassTypeEnum::LIST_WIDGET: title = ("Select Name " - + m_dataValue.toString() + + dataValue.toString() + " else " + " index " - + QString::number(m_dataValueTwo.toInt())); + + QString::number(dataValueTwo.toInt())); break; case WuQMacroClassTypeEnum::MENU: title = ("Select Name " - + m_dataValue.toString() + + dataValue.toString() + " else " + " index " - + QString::number(m_dataValueTwo.toInt())); + + QString::number(dataValueTwo.toInt())); break; case WuQMacroClassTypeEnum::MOUSE_USER_EVENT: title = "MouseEvent"; @@ -428,39 +299,39 @@ WuQMacroCommand::updateTitle() break; case WuQMacroClassTypeEnum::PUSH_BUTTON_CHECKABLE: title = ("Turn " - + QString((m_dataValue.toBool() ? "On" : "Off"))); + + QString((dataValue.toBool() ? "On" : "Off"))); break; case WuQMacroClassTypeEnum::RADIO_BUTTON: title = "Click Button"; break; case WuQMacroClassTypeEnum::SLIDER: title = ("Move to " - + AString::number(m_dataValue.toInt())); + + AString::number(dataValue.toInt())); break; case WuQMacroClassTypeEnum::SPIN_BOX: title = ("Set to " - + AString::number(m_dataValue.toInt())); + + AString::number(dataValue.toInt())); break; case WuQMacroClassTypeEnum::TAB_BAR: title = ("Select Name " - + m_dataValue.toString() + + dataValue.toString() + " else " + " index " - + QString::number(m_dataValueTwo.toInt())); + + QString::number(dataValueTwo.toInt())); break; case WuQMacroClassTypeEnum::TAB_WIDGET: title = ("Select Name " - + m_dataValue.toString() + + dataValue.toString() + " else " + " index " - + QString::number(m_dataValueTwo.toInt())); + + QString::number(dataValueTwo.toInt())); break; case WuQMacroClassTypeEnum::TOOL_BUTTON: title = ("Click Button"); break; case WuQMacroClassTypeEnum::TOOL_BUTTON_CHECKABLE: title = ("Turn " - + QString((m_dataValue.toBool() ? "On" : "Off"))); + + QString((dataValue.toBool() ? "On" : "Off"))); break; } @@ -484,142 +355,134 @@ WuQMacroCommand::getClassType() const } /** - * @return Type of data value for object + * Add a parameter to the macro command + * + * @param dataType + * Data type of the parameter + * @param name + * Name of the parameter + * @param value + * Value of the parameter */ -WuQMacroDataValueTypeEnum::Enum -WuQMacroCommand::getDataType() const +void +WuQMacroCommand::addParameter(const WuQMacroDataValueTypeEnum::Enum dataType, + const QString& name, + const QVariant& value) { - return m_dataType; + WuQMacroCommandParameter* parameter = new WuQMacroCommandParameter(dataType, + name, + value); + addParameter(parameter); } /** - * @return Type of second data value for object + * Add a parameter to the macro command + * + * @param parameter + * Parameter to add */ -WuQMacroDataValueTypeEnum::Enum -WuQMacroCommand::getDataTypeTwo() const +void +WuQMacroCommand::addParameter(WuQMacroCommandParameter* parameter) { - return m_dataTypeTwo; + CaretAssert(parameter); + if (parameter == NULL) { + return; + } + + m_parameters.push_back(parameter); + setModified(); } -/** - * @return The object's name - */ -QString -WuQMacroCommand::getObjectName() const -{ - return m_objectName; -} /** - * @return The object's descriptive name + * @return The number of parameters */ -QString -WuQMacroCommand::getDescriptiveName() const +int32_t +WuQMacroCommand::getNumberOfParameters() const { - return m_descriptiveName; + return m_parameters.size(); } /** - * @return ToolTip for the object + * @return Parameter at the given index + * + * @param index + * Index of the parameter. */ -QString -WuQMacroCommand::getObjectToolTip() const +WuQMacroCommandParameter* +WuQMacroCommand::getParameterAtIndex(const int32_t index) { - return toolTip(); + CaretAssertVectorIndex(m_parameters, index); + return m_parameters[index]; } /** - * Set the object's tooltip + * @return Parameter at the given index (const method) * - * @param objectToolTip - * New value for object tooltip + * @param index + * Index of the parameter. */ -void -WuQMacroCommand::setObjectToolTip(const QString& objectToolTip) +const WuQMacroCommandParameter* +WuQMacroCommand::getParameterAtIndex(const int32_t index) const { - if (toolTip() != objectToolTip) { - setToolTip(objectToolTip); - setModified(); - } + CaretAssertVectorIndex(m_parameters, index); + return m_parameters[index]; } /** - * @return The object's data value. + * Remove all parameters in this command */ -QVariant -WuQMacroCommand::getDataValue() const +void +WuQMacroCommand::removeAllParameters() { - return m_dataValue; + for (auto p : m_parameters) { + delete p; + } + m_parameters.clear(); } /** - * Set the data value - * - * @param dataValue - * New data value + * @return The object's name */ -void -WuQMacroCommand::setDataValue(const QVariant& dataValue) +QString +WuQMacroCommand::getObjectName() const { - if (m_dataValue != dataValue) { - m_dataValue = dataValue; - updateTitle(); - setModified(); - } + return m_objectName; } /** - * @return Text used in dialog for updates the data value + * @return The object's descriptive name */ QString -WuQMacroCommand::getDataValueUpdateLabelText() const +WuQMacroCommand::getDescriptiveName() const { - return m_dataValueUpdateLabelText; + return m_descriptiveName; } /** - * @return The object's second data value. + * @return ToolTip for the object */ -QVariant -WuQMacroCommand::getDataValueTwo() const +QString +WuQMacroCommand::getObjectToolTip() const { - return m_dataValueTwo; + return toolTip(); } /** - * Set the data value two + * Set the object's tooltip * - * @param dataValue - * New data value + * @param objectToolTip + * New value for object tooltip */ void -WuQMacroCommand::setDataValueTwo(const QVariant& dataValue) +WuQMacroCommand::setObjectToolTip(const QString& objectToolTip) { - if (m_dataValueTwo != dataValue) { - m_dataValueTwo = dataValue; - updateTitle(); + if (toolTip() != objectToolTip) { + setToolTip(objectToolTip); setModified(); } } -/** - * @return true if data value two is valid - */ -bool -WuQMacroCommand::isDataValueTwoValid() const -{ - return m_dataValueTwoValidFlag; -} - -/** - * @return Text used in dialog for updates the data value - */ -QString -WuQMacroCommand::getDataValueTwoUpdateLabelText() const -{ - return m_dataValueTwoUpdateLabelText; -} - /** * @return Point to mouse event information */ @@ -743,6 +606,12 @@ WuQMacroCommand::isModified() const return true; } + for (const auto p : m_parameters) { + if (p->isModified()) { + return true; + } + } + return false; } @@ -753,6 +622,10 @@ void WuQMacroCommand::clearModified() { m_modifiedStatusFlag = false; + + for (auto p : m_parameters) { + p->clearModified(); + } } /** @@ -771,12 +644,14 @@ WuQMacroCommand::setModified() AString WuQMacroCommand::toString() const { - QString s("WuQMacroCommand text=%1 name=%2, type=%3, value=%4, valueTwo=%5"); + QString s("WuQMacroCommand text=%1 name=%2, type=%3"); s = s.arg(text() ).arg(m_objectName - ).arg(WuQMacroClassTypeEnum::toGuiName(m_classType) - ).arg(m_dataValue.toString() - ).arg(m_dataValueTwo.toString()); + ).arg(WuQMacroClassTypeEnum::toGuiName(m_classType)); + + for (const auto p : m_parameters) { + s.append(", value=" + p->getValue().toString()); + } return s; } diff --git a/src/Common/WuQMacroCommand.h b/src/Common/WuQMacroCommand.h index b8060045b..d045a2e88 100644 --- a/src/Common/WuQMacroCommand.h +++ b/src/Common/WuQMacroCommand.h @@ -36,6 +36,7 @@ class QObject; namespace caret { + class WuQMacroCommandParameter; class WuQMacroMouseEventInfo; class WuQMacroCommand : public QStandardItem, public TracksModificationInterface { @@ -45,9 +46,7 @@ namespace caret { WuQMacroCommand(const WuQMacroClassTypeEnum::Enum objectClassType, const QString& objectName, const QString& objectDescriptiveName, - const QString& objectToolTip, - const QVariant dataValue, - const QVariant dataValueTwo); + const QString& objectToolTip); WuQMacroCommand(const WuQMacroClassTypeEnum::Enum objectClassType, const QString& objectName, @@ -73,23 +72,17 @@ namespace caret { void setObjectToolTip(const QString& objectToolTip); - WuQMacroDataValueTypeEnum::Enum getDataType() const; + void addParameter(WuQMacroCommandParameter* parameter); - QVariant getDataValue() const; - - void setDataValue(const QVariant& dataValue); - - QString getDataValueUpdateLabelText() const; - - WuQMacroDataValueTypeEnum::Enum getDataTypeTwo() const; + void addParameter(const WuQMacroDataValueTypeEnum::Enum dataType, + const QString& name, + const QVariant& value); - QVariant getDataValueTwo() const; + int32_t getNumberOfParameters() const; - void setDataValueTwo(const QVariant& dataValue); + WuQMacroCommandParameter* getParameterAtIndex(const int32_t); - bool isDataValueTwoValid() const; - - QString getDataValueTwoUpdateLabelText() const; + const WuQMacroCommandParameter* getParameterAtIndex(const int32_t) const; WuQMacroMouseEventInfo* getMouseEventInfo(); @@ -120,23 +113,15 @@ namespace caret { void updateTitle(); + void removeAllParameters(); + WuQMacroClassTypeEnum::Enum m_classType; QString m_objectName; QString m_descriptiveName; - QVariant m_dataValue; - - WuQMacroDataValueTypeEnum::Enum m_dataType; - - QString m_dataValueUpdateLabelText; - - QVariant m_dataValueTwo; - - WuQMacroDataValueTypeEnum::Enum m_dataTypeTwo; - - QString m_dataValueTwoUpdateLabelText; + std::vector m_parameters; WuQMacroMouseEventInfo* m_macroMouseEvent; @@ -144,8 +129,6 @@ namespace caret { bool m_modifiedStatusFlag = false; - bool m_dataValueTwoValidFlag = false; - // ADD_NEW_MEMBERS_HERE }; diff --git a/src/Common/WuQMacroCommandParameter.cxx b/src/Common/WuQMacroCommandParameter.cxx new file mode 100644 index 000000000..54ce1365e --- /dev/null +++ b/src/Common/WuQMacroCommandParameter.cxx @@ -0,0 +1,158 @@ + +/*LICENSE_START*/ +/* + * Copyright (C) 2019 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + +#define __WU_Q_MACRO_COMMAND_PARAMETER_DECLARE__ +#include "WuQMacroCommandParameter.h" +#undef __WU_Q_MACRO_COMMAND_PARAMETER_DECLARE__ + +#include "CaretAssert.h" +using namespace caret; + + + +/** + * \class caret::WuQMacroCommandParameter + * \brief A paramater contained in a WuQMacroCommand + * \ingroup Common + */ + +/** + * Constructor. + */ +WuQMacroCommandParameter::WuQMacroCommandParameter() +: CaretObjectTracksModification() +{ + +} + +/** + * Constructor. + * + * @param dataType + * Data type of the parameter + * @param name + * Name of the parameter + * @param value + * Value of the parameter + */ +WuQMacroCommandParameter::WuQMacroCommandParameter(const WuQMacroDataValueTypeEnum::Enum dataType, + const QString& name, + const QVariant& value) +: CaretObjectTracksModification(), +m_dataType(dataType), +m_name(name), +m_value(value) +{ + +} + + +/** + * Destructor. + */ +WuQMacroCommandParameter::~WuQMacroCommandParameter() +{ +} + +/** + * Copy constructor. + * @param obj + * Object that is copied. + */ +WuQMacroCommandParameter::WuQMacroCommandParameter(const WuQMacroCommandParameter& obj) +: CaretObjectTracksModification(obj) +{ + this->copyHelperWuQMacroCommandParameter(obj); +} + +/** + * Assignment operator. + * @param obj + * Data copied from obj to this. + * @return + * Reference to this object. + */ +WuQMacroCommandParameter& +WuQMacroCommandParameter::operator=(const WuQMacroCommandParameter& obj) +{ + if (this != &obj) { + CaretObjectTracksModification::operator=(obj); + this->copyHelperWuQMacroCommandParameter(obj); + } + return *this; +} + +/** + * Helps with copying an object of this type. + * @param obj + * Object that is copied. + */ +void +WuQMacroCommandParameter::copyHelperWuQMacroCommandParameter(const WuQMacroCommandParameter& obj) +{ + m_dataType = obj.m_dataType; + m_name = obj.m_name; + m_value = obj.m_value; + setModified(); +} + +/** + * @return Data type of this parameter + */ +WuQMacroDataValueTypeEnum::Enum +WuQMacroCommandParameter::getDataType() const +{ + return m_dataType; +} + +/** + * @return Name of this parameter + */ +QString +WuQMacroCommandParameter::getName() const +{ + return m_name; +} + +/** + * @return Value of this parameter + */ +QVariant +WuQMacroCommandParameter::getValue() const +{ + return m_value; +} + +/** + * Set the value of this parameter + * + * @param value + * New value of parameter + */ +void +WuQMacroCommandParameter::setValue(const QVariant& value) +{ + if (value != m_value) { + m_value = value; + setModified(); + } +} + diff --git a/src/Common/WuQMacroCommandParameter.h b/src/Common/WuQMacroCommandParameter.h new file mode 100644 index 000000000..b5ca850b4 --- /dev/null +++ b/src/Common/WuQMacroCommandParameter.h @@ -0,0 +1,79 @@ +#ifndef __WU_Q_MACRO_COMMAND_PARAMETER_H__ +#define __WU_Q_MACRO_COMMAND_PARAMETER_H__ + +/*LICENSE_START*/ +/* + * Copyright (C) 2019 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + + +#include +#include + +#include "CaretObjectTracksModification.h" +#include "WuQMacroDataValueTypeEnum.h" + + +namespace caret { + + class WuQMacroCommandParameter : public CaretObjectTracksModification { + + public: + WuQMacroCommandParameter(const WuQMacroDataValueTypeEnum::Enum dataType, + const QString& name, + const QVariant& value); + + WuQMacroCommandParameter(); + + virtual ~WuQMacroCommandParameter(); + + WuQMacroCommandParameter(const WuQMacroCommandParameter& obj); + + WuQMacroCommandParameter& operator=(const WuQMacroCommandParameter& obj); + + WuQMacroDataValueTypeEnum::Enum getDataType() const; + + QString getName() const; + + QVariant getValue() const; + + void setValue(const QVariant& value); + + + // ADD_NEW_METHODS_HERE + + private: + void copyHelperWuQMacroCommandParameter(const WuQMacroCommandParameter& obj); + + WuQMacroDataValueTypeEnum::Enum m_dataType = WuQMacroDataValueTypeEnum::INVALID; + + QString m_name; + + QVariant m_value; + + + // ADD_NEW_MEMBERS_HERE + + }; + +#ifdef __WU_Q_MACRO_COMMAND_PARAMETER_DECLARE__ + // +#endif // __WU_Q_MACRO_COMMAND_PARAMETER_DECLARE__ + +} // namespace +#endif //__WU_Q_MACRO_COMMAND_PARAMETER_H__ diff --git a/src/Common/WuQMacroGroupXmlStreamBase.h b/src/Common/WuQMacroGroupXmlStreamBase.h index d1c85052c..19ef2a32d 100644 --- a/src/Common/WuQMacroGroupXmlStreamBase.h +++ b/src/Common/WuQMacroGroupXmlStreamBase.h @@ -50,11 +50,7 @@ namespace caret { static const QString ATTRIBUTE_NAME; static const QString ATTRIBUTE_OBJECT_CLASS; - static const QString ATTRIBUTE_OBJECT_DATA_TYPE; - static const QString ATTRIBUTE_OBJECT_DATA_TYPE_TWO; static const QString ATTRIBUTE_OBJECT_DESCRIPTIVE_NAME; - static const QString ATTRIBUTE_OBJECT_VALUE; - static const QString ATTRIBUTE_OBJECT_VALUE_TWO; static const QString ATTRIBUTE_DELAY; static const QString ATTRIBUTE_SHORT_CUT_KEY; static const QString ATTRIBUTE_UNIQUE_IDENTIFIER; @@ -73,14 +69,18 @@ namespace caret { static const QString ATTRIBUTE_MOUSE_WINDOW_X; static const QString ATTRIBUTE_MOUSE_WINDOW_Y; - - + static const QString ATTRIBUTE_MACRO_COMMAND_PARAMETER_DATA_TYPE; + static const QString ATTRIBUTE_MACRO_COMMAND_PARAMETER_NAME; + static const QString ATTRIBUTE_MACRO_COMMAND_PARAMETER_VALUE; + static const QString ELEMENT_DESCRIPTION; static const QString ELEMENT_MACRO; static const QString ELEMENT_MACRO_COMMAND; + static const QString ELEMENT_MACRO_COMMAND_MOUSE_EVENT_INFO; + static const QString ELEMENT_MACRO_COMMAND_PARAMETER; static const QString ELEMENT_MACRO_COMMAND_TOOL_TIP; static const QString ELEMENT_MACRO_GROUP; - static const QString ELEMENT_MOUSE_EVENT_INFO; + static const QString VALUE_BOOL_FALSE; static const QString VALUE_BOOL_TRUE; static const QString VALUE_VERSION_ONE; @@ -93,11 +93,7 @@ namespace caret { #ifdef __WU_Q_MACRO_GROUP_XML_STREAM_BASE_DECLARE__ const QString WuQMacroGroupXmlStreamBase::ATTRIBUTE_NAME = "Name"; const QString WuQMacroGroupXmlStreamBase::ATTRIBUTE_OBJECT_CLASS = "ObjectClass"; - const QString WuQMacroGroupXmlStreamBase::ATTRIBUTE_OBJECT_DATA_TYPE = "DataType"; - const QString WuQMacroGroupXmlStreamBase::ATTRIBUTE_OBJECT_DATA_TYPE_TWO = "DataTypeTwo"; const QString WuQMacroGroupXmlStreamBase::ATTRIBUTE_OBJECT_DESCRIPTIVE_NAME = "DescriptiveName"; - const QString WuQMacroGroupXmlStreamBase::ATTRIBUTE_OBJECT_VALUE = "DataValue"; - const QString WuQMacroGroupXmlStreamBase::ATTRIBUTE_OBJECT_VALUE_TWO = "DataValueTwo"; const QString WuQMacroGroupXmlStreamBase::ATTRIBUTE_DELAY = "Delay"; const QString WuQMacroGroupXmlStreamBase::ATTRIBUTE_SHORT_CUT_KEY = "ShortCutKey"; const QString WuQMacroGroupXmlStreamBase::ATTRIBUTE_UNIQUE_IDENTIFIER = "UniqueIdentifier"; @@ -115,13 +111,18 @@ namespace caret { const QString WuQMacroGroupXmlStreamBase::ATTRIBUTE_MOUSE_WIDGET_HEIGHT = "WidgetHeight"; const QString WuQMacroGroupXmlStreamBase::ATTRIBUTE_MOUSE_WINDOW_X = "WindowX"; const QString WuQMacroGroupXmlStreamBase::ATTRIBUTE_MOUSE_WINDOW_Y = "WindowY"; - + const QString WuQMacroGroupXmlStreamBase::ATTRIBUTE_MACRO_COMMAND_PARAMETER_DATA_TYPE = "DataType"; + const QString WuQMacroGroupXmlStreamBase::ATTRIBUTE_MACRO_COMMAND_PARAMETER_NAME = "Name"; + const QString WuQMacroGroupXmlStreamBase::ATTRIBUTE_MACRO_COMMAND_PARAMETER_VALUE = "Value"; + const QString WuQMacroGroupXmlStreamBase::ELEMENT_DESCRIPTION = "Description"; const QString WuQMacroGroupXmlStreamBase::ELEMENT_MACRO = "Macro"; const QString WuQMacroGroupXmlStreamBase::ELEMENT_MACRO_COMMAND = "MacroCommand"; + const QString WuQMacroGroupXmlStreamBase::ELEMENT_MACRO_COMMAND_MOUSE_EVENT_INFO = "MouseEventInfo"; + const QString WuQMacroGroupXmlStreamBase::ELEMENT_MACRO_COMMAND_PARAMETER = "Parameter"; const QString WuQMacroGroupXmlStreamBase::ELEMENT_MACRO_COMMAND_TOOL_TIP = "ToolTip"; const QString WuQMacroGroupXmlStreamBase::ELEMENT_MACRO_GROUP = "MacroGroup"; - const QString WuQMacroGroupXmlStreamBase::ELEMENT_MOUSE_EVENT_INFO = "MouseEventInfo"; + const QString WuQMacroGroupXmlStreamBase::VALUE_BOOL_FALSE = "false"; const QString WuQMacroGroupXmlStreamBase::VALUE_BOOL_TRUE = "true"; diff --git a/src/Common/WuQMacroGroupXmlStreamReader.cxx b/src/Common/WuQMacroGroupXmlStreamReader.cxx index 864d312ed..c103e2cf3 100644 --- a/src/Common/WuQMacroGroupXmlStreamReader.cxx +++ b/src/Common/WuQMacroGroupXmlStreamReader.cxx @@ -31,6 +31,7 @@ #include "CaretLogger.h" #include "WuQMacro.h" #include "WuQMacroCommand.h" +#include "WuQMacroCommandParameter.h" #include "WuQMacroGroup.h" #include "WuQMacroMouseEventInfo.h" @@ -185,7 +186,6 @@ WuQMacroGroupXmlStreamReader::readVersionOne(QXmlStreamReader& xmlReader, CaretAssert(macroGroup); WuQMacro* macro(NULL); - WuQMacroCommand* macroCommand(NULL); /* * Gets set when ending scene info directory element is read @@ -200,43 +200,8 @@ WuQMacroGroupXmlStreamReader::readVersionOne(QXmlStreamReader& xmlReader, if (elementName == ELEMENT_MACRO) { macro = readMacroVersionOne(xmlReader); - } - else if (elementName == ELEMENT_DESCRIPTION) { - const QString text = xmlReader.readElementText(); if (macro != NULL) { - macro->setDescription(text); - } - } - else if (elementName == ELEMENT_MACRO_COMMAND) { - macroCommand = readMacroCommandAttributesVersionOne(xmlReader); - } - else if (elementName == ELEMENT_MOUSE_EVENT_INFO) { - WuQMacroMouseEventInfo* mouseEventInfo = readMacroMouseEventInfo(xmlReader); - if (mouseEventInfo != NULL) { - if (macroCommand != NULL) { - macroCommand->setMouseEventInfo(mouseEventInfo); - } - else { - addToWarnings(xmlReader, - "Read WuQMacroMouseEventInfo but macroCommand is NULL"); - delete mouseEventInfo; - mouseEventInfo = NULL; - } - } - else { - if (macroCommand != NULL) { - /* - * Command is invalid - */ - delete macroCommand; - macroCommand = NULL; - } - } - } - else if (elementName == ELEMENT_MACRO_COMMAND_TOOL_TIP) { - const QString text = xmlReader.readElementText(); - if (macroCommand != NULL) { - macroCommand->setToolTip(text); + macroGroup->addMacro(macro); } } else { @@ -248,21 +213,7 @@ WuQMacroGroupXmlStreamReader::readVersionOne(QXmlStreamReader& xmlReader, } } else if (xmlReader.isEndElement()) { - const QString elementName = xmlReader.name().toString(); - if (elementName == ELEMENT_MACRO) { - if (macro != NULL) { - macroGroup->addMacro(macro); - } - } - else if (elementName == ELEMENT_MACRO_COMMAND) { - if (macro != NULL) { - if (macroCommand != NULL) { - macro->appendMacroCommand(macroCommand); - } - } - macroCommand = NULL; - } - else if (elementName == ELEMENT_MACRO_GROUP) { + if (xmlReader.name() == ELEMENT_MACRO_GROUP) { endElementFound = true; } } @@ -328,15 +279,183 @@ WuQMacroGroupXmlStreamReader::readMacroVersionOne(QXmlStreamReader& xmlReader) macro->setShortCutKey(shortCutKey); macro->setUniqueIdentifier(uniqueIdentifier); + /* + * Gets set when ending scene info directory element is read + */ + bool endElementFound(false); + + while ( (! xmlReader.atEnd()) + && ( ! endElementFound)) { + xmlReader.readNext(); + if (xmlReader.isStartElement()) { + const QString elementName = xmlReader.name().toString(); + + if (elementName == ELEMENT_MACRO_COMMAND) { + WuQMacroCommand* command = readMacroCommandVersionOne(xmlReader); + if (command != NULL) { + macro->appendMacroCommand(command); + } + } + else if (elementName == ELEMENT_DESCRIPTION) { + const QString text = xmlReader.readElementText(); + macro->setDescription(text); + } + else { + addToWarnings(xmlReader, + "Unexpected element=" + + elementName + + "\""); + xmlReader.skipCurrentElement(); + } + } + else if (xmlReader.isEndElement()) { + if (xmlReader.name() == ELEMENT_MACRO) { + endElementFound = true; + } + } + } + return macro; } +WuQMacroCommand* +WuQMacroGroupXmlStreamReader::readMacroCommandVersionOne(QXmlStreamReader& xmlReader) +{ + WuQMacroCommand* command = readMacroCommandAttributesVersionOne(xmlReader); + + if (command != NULL) { + /* + * Gets set when ending scene info directory element is read + */ + bool endElementFound(false); + + while ( (! xmlReader.atEnd()) + && ( ! endElementFound)) { + xmlReader.readNext(); + if (xmlReader.isStartElement()) { + const QString elementName = xmlReader.name().toString(); + + if (elementName == ELEMENT_MACRO_COMMAND_MOUSE_EVENT_INFO) { + WuQMacroMouseEventInfo* mouseEventInfo = readMacroMouseEventInfo(xmlReader); + if (mouseEventInfo != NULL) { + command->setMouseEventInfo(mouseEventInfo); + } + } + else if (elementName == ELEMENT_MACRO_COMMAND_TOOL_TIP) { + const QString text = xmlReader.readElementText(); + command->setToolTip(text); + } + else if (elementName == ELEMENT_MACRO_COMMAND_PARAMETER) { + WuQMacroCommandParameter* parameter = readMacroCommandParameter(xmlReader); + if (parameter != NULL) { + command->addParameter(parameter); + } + } + else { + addToWarnings(xmlReader, + "Unexpected element=" + + elementName + + "\""); + xmlReader.skipCurrentElement(); + } + } + else if (xmlReader.isEndElement()) { + if (xmlReader.name() == ELEMENT_MACRO_COMMAND) { + endElementFound = true; + } + } + } + } + else { + xmlReader.skipCurrentElement(); + } + + return command; +} + +/** + * Read version one of macro command parameter + * + * @param xmlReader + * The XML stream reader + * @Return The macro parameter + */ +WuQMacroCommandParameter* +WuQMacroGroupXmlStreamReader::readMacroCommandParameter(QXmlStreamReader& xmlReader) +{ + const QXmlStreamAttributes attributes = xmlReader.attributes(); + const QStringRef dataTypeString = attributes.value(ATTRIBUTE_MACRO_COMMAND_PARAMETER_DATA_TYPE); + const QStringRef valueString = attributes.value(ATTRIBUTE_MACRO_COMMAND_PARAMETER_VALUE); + const QString parameterName = attributes.value(ATTRIBUTE_MACRO_COMMAND_PARAMETER_NAME).toString(); + + QString es; + if (dataTypeString.isEmpty()) es.append(ATTRIBUTE_MACRO_COMMAND_PARAMETER_DATA_TYPE + " "); + if ( ! es.isEmpty()) { + addToWarnings(xmlReader, + ELEMENT_MACRO_COMMAND_PARAMETER + + " is missing attribute(s): " + + es); + return NULL; + } + + bool dataTypeValid(false); + WuQMacroDataValueTypeEnum::Enum dataType = WuQMacroDataValueTypeEnum::fromName(dataTypeString.toString(), + &dataTypeValid); + if (! dataTypeValid) { + es.append(dataTypeString.toString() + + " is not valid for attribute " + + ATTRIBUTE_MACRO_COMMAND_PARAMETER_DATA_TYPE + + " "); + return NULL; + } + + QVariant value; + switch (dataType) { + case WuQMacroDataValueTypeEnum::INVALID: + value.setValue(QString("")); + break; + case WuQMacroDataValueTypeEnum::BOOLEAN: + { + const bool boolValue = ((valueString == VALUE_BOOL_TRUE) ? true : false); + value.setValue(boolValue); + } + break; + case WuQMacroDataValueTypeEnum::FLOAT: + { + const double floatValue = valueString.toFloat(); + value.setValue(floatValue); + } + break; + case WuQMacroDataValueTypeEnum::INTEGER: + { + const int32_t intValue = valueString.toInt(); + value.setValue(intValue); + } + break; + case WuQMacroDataValueTypeEnum::MOUSE: + CaretAssertMessage(0, "Mouse is special case handled above"); + break; + case WuQMacroDataValueTypeEnum::NONE: + value.setValue(QString()); + break; + case WuQMacroDataValueTypeEnum::STRING: + value.setValue(valueString.toString()); + break; + } + + WuQMacroCommandParameter* parameter = new WuQMacroCommandParameter(dataType, + parameterName, + value); + + return parameter; +} + /** - * Read version one of macro command attributes + * Read version one of macro command attributes into a command * * @param xmlReader * The XML stream reader - * @Return The macro group + * @Return The macro command */ WuQMacroCommand* WuQMacroGroupXmlStreamReader::readMacroCommandAttributesVersionOne(QXmlStreamReader& xmlReader) @@ -345,18 +464,12 @@ WuQMacroGroupXmlStreamReader::readMacroCommandAttributesVersionOne(QXmlStreamRea const QXmlStreamAttributes attributes = xmlReader.attributes(); const QStringRef objectName = attributes.value(ATTRIBUTE_NAME); - const QStringRef dataTypeString = attributes.value(ATTRIBUTE_OBJECT_DATA_TYPE); const QStringRef delayString = attributes.value(ATTRIBUTE_DELAY); const QStringRef descriptiveNameString = attributes.value(ATTRIBUTE_OBJECT_DESCRIPTIVE_NAME); const QStringRef classString = attributes.value(ATTRIBUTE_OBJECT_CLASS); - const QStringRef valueString = attributes.value(ATTRIBUTE_OBJECT_VALUE); - const QStringRef dataTypeTwoString = attributes.value(ATTRIBUTE_OBJECT_DATA_TYPE_TWO); - const QStringRef valueTwoString = attributes.value(ATTRIBUTE_OBJECT_VALUE_TWO); QString es; if (objectName.isEmpty()) es.append(ATTRIBUTE_NAME + " "); - if (dataTypeString.isEmpty()) es.append(ATTRIBUTE_OBJECT_DATA_TYPE + " "); - if (dataTypeTwoString.isEmpty()) es.append(ATTRIBUTE_OBJECT_DATA_TYPE + " "); if (classString.isEmpty()) es.append(ATTRIBUTE_OBJECT_CLASS + " "); if ( ! es.isEmpty()) { addToWarnings(xmlReader, @@ -378,29 +491,6 @@ WuQMacroGroupXmlStreamReader::readMacroCommandAttributesVersionOne(QXmlStreamRea return NULL; } - bool dataTypeValid(false); - WuQMacroDataValueTypeEnum::Enum dataValueType = WuQMacroDataValueTypeEnum::fromName(dataTypeString.toString(), - &dataTypeValid); - if (! dataTypeValid) { - es.append(dataTypeString.toString() - + " is not valid for attribute " - + ATTRIBUTE_OBJECT_DATA_TYPE - + " "); - return NULL; - } - - bool dataTypeTwoValid(false); - WuQMacroDataValueTypeEnum::Enum dataValueTypeTwo = WuQMacroDataValueTypeEnum::fromName(dataTypeTwoString.toString(), - &dataTypeTwoValid); - if (! dataTypeTwoValid) { - es.append(dataTypeTwoString.toString() - + " is not valid for attribute " - + ATTRIBUTE_OBJECT_DATA_TYPE_TWO - + " "); - return NULL; - } - - if ( ! es.isEmpty()) { addToWarnings(xmlReader, ELEMENT_MACRO_COMMAND @@ -415,80 +505,11 @@ WuQMacroGroupXmlStreamReader::readMacroCommandAttributesVersionOne(QXmlStreamRea descriptiveNameString.toString()); } else { - QVariant value; - switch (dataValueType) { - case WuQMacroDataValueTypeEnum::INVALID: - value.setValue(QString("")); - break; - case WuQMacroDataValueTypeEnum::BOOLEAN: - { - const bool boolValue = ((valueString == VALUE_BOOL_TRUE) ? true : false); - value.setValue(boolValue); - } - break; - case WuQMacroDataValueTypeEnum::FLOAT: - { - const double floatValue = valueString.toFloat(); - value.setValue(floatValue); - } - break; - case WuQMacroDataValueTypeEnum::INTEGER: - { - const int32_t intValue = valueString.toInt(); - value.setValue(intValue); - } - break; - case WuQMacroDataValueTypeEnum::MOUSE: - CaretAssertMessage(0, "Mouse is special case handled above"); - break; - case WuQMacroDataValueTypeEnum::NONE: - value.setValue(QString()); - break; - case WuQMacroDataValueTypeEnum::STRING: - value.setValue(valueString.toString()); - break; - } - - QVariant valueTwo; - switch (dataValueTypeTwo) { - case WuQMacroDataValueTypeEnum::INVALID: - valueTwo.setValue(QString("")); - break; - case WuQMacroDataValueTypeEnum::BOOLEAN: - { - const bool boolValue = ((valueTwoString == VALUE_BOOL_TRUE) ? true : false); - valueTwo.setValue(boolValue); - } - break; - case WuQMacroDataValueTypeEnum::FLOAT: - { - const double floatValue = valueTwoString.toFloat(); - valueTwo.setValue(floatValue); - } - break; - case WuQMacroDataValueTypeEnum::INTEGER: - { - const int32_t intValue = valueTwoString.toInt(); - valueTwo.setValue(intValue); - } - break; - case WuQMacroDataValueTypeEnum::MOUSE: - break; - case WuQMacroDataValueTypeEnum::NONE: - value.setValue(QString()); - break; - case WuQMacroDataValueTypeEnum::STRING: - valueTwo.setValue(valueTwoString.toString()); - break; - } - QString tooltip; macroCommand = new WuQMacroCommand(objectClass, objectName.toString(), descriptiveNameString.toString(), - tooltip, - value, - valueTwo); + tooltip); if ( ! delayString.isEmpty()) { bool valid(false); @@ -530,7 +551,7 @@ WuQMacroGroupXmlStreamReader::readMacroMouseEventInfo(QXmlStreamReader& xmlReade if (widgetHeightString.isEmpty()) es.append(ATTRIBUTE_MOUSE_WIDGET_HEIGHT + " "); if ( ! es.isEmpty()) { addToWarnings(xmlReader, - ELEMENT_MOUSE_EVENT_INFO + ELEMENT_MACRO_COMMAND_MOUSE_EVENT_INFO + " is missing required attribute(s): " + es); return NULL; diff --git a/src/Common/WuQMacroGroupXmlStreamReader.h b/src/Common/WuQMacroGroupXmlStreamReader.h index c2623e096..daccc7a85 100644 --- a/src/Common/WuQMacroGroupXmlStreamReader.h +++ b/src/Common/WuQMacroGroupXmlStreamReader.h @@ -33,6 +33,7 @@ namespace caret { class WuQMacro; class WuQMacroCommand; + class WuQMacroCommandParameter; class WuQMacroGroup; class WuQMacroMouseEventInfo; @@ -68,6 +69,8 @@ namespace caret { WuQMacroCommand* readMacroCommandVersionOne(QXmlStreamReader& xmlReader); + WuQMacroCommandParameter* readMacroCommandParameter(QXmlStreamReader& xmlReader); + void addToWarnings(QXmlStreamReader& xmlReader, const QString& warning); diff --git a/src/Common/WuQMacroGroupXmlStreamWriter.cxx b/src/Common/WuQMacroGroupXmlStreamWriter.cxx index d7e04b82f..532426145 100644 --- a/src/Common/WuQMacroGroupXmlStreamWriter.cxx +++ b/src/Common/WuQMacroGroupXmlStreamWriter.cxx @@ -29,6 +29,7 @@ #include "CaretAssert.h" #include "WuQMacro.h" #include "WuQMacroCommand.h" +#include "WuQMacroCommandParameter.h" #include "WuQMacroGroup.h" #include "WuQMacroMouseEventInfo.h" @@ -170,70 +171,6 @@ WuQMacroGroupXmlStreamWriter::writeMacroCommandToXML(const WuQMacroCommand* macr macroCommand->getDescriptiveName()); m_xmlStreamWriter->writeAttribute(ATTRIBUTE_DELAY, QString::number(macroCommand->getDelayInSeconds(), 'f', 2)); - { - m_xmlStreamWriter->writeAttribute(ATTRIBUTE_OBJECT_DATA_TYPE, - WuQMacroDataValueTypeEnum::toName(macroCommand->getDataType())); - - const QVariant dataValue(macroCommand->getDataValue()); - QString stringValue; - switch (macroCommand->getDataType()) { - case WuQMacroDataValueTypeEnum::INVALID: - break; - case WuQMacroDataValueTypeEnum::BOOLEAN: - stringValue = (dataValue.toBool() ? VALUE_BOOL_TRUE : VALUE_BOOL_FALSE); - break; - case WuQMacroDataValueTypeEnum::FLOAT: - stringValue = QString::number(dataValue.toFloat()); - break; - case WuQMacroDataValueTypeEnum::INTEGER: - stringValue = QString::number(dataValue.toInt()); - break; - case WuQMacroDataValueTypeEnum::MOUSE: - stringValue = "MouseEvent"; - break; - case WuQMacroDataValueTypeEnum::NONE: - stringValue = ""; - break; - case WuQMacroDataValueTypeEnum::STRING: - stringValue = dataValue.toString(); - break; - } - m_xmlStreamWriter->writeAttribute(ATTRIBUTE_OBJECT_VALUE, - stringValue); - } - - { - m_xmlStreamWriter->writeAttribute(ATTRIBUTE_OBJECT_DATA_TYPE_TWO, - WuQMacroDataValueTypeEnum::toName(macroCommand->getDataTypeTwo())); - - const QVariant dataValueTwo(macroCommand->getDataValueTwo()); - QString stringValue; - switch (macroCommand->getDataTypeTwo()) { - case WuQMacroDataValueTypeEnum::INVALID: - break; - case WuQMacroDataValueTypeEnum::BOOLEAN: - stringValue = (dataValueTwo.toBool() ? VALUE_BOOL_TRUE : VALUE_BOOL_FALSE); - break; - case WuQMacroDataValueTypeEnum::FLOAT: - stringValue = QString::number(dataValueTwo.toFloat()); - break; - case WuQMacroDataValueTypeEnum::INTEGER: - stringValue = QString::number(dataValueTwo.toInt()); - break; - case WuQMacroDataValueTypeEnum::MOUSE: - stringValue = "MouseEvent"; - break; - case WuQMacroDataValueTypeEnum::NONE: - stringValue = ""; - break; - case WuQMacroDataValueTypeEnum::STRING: - stringValue = dataValueTwo.toString(); - break; - } - m_xmlStreamWriter->writeAttribute(ATTRIBUTE_OBJECT_VALUE_TWO, - stringValue); - } - switch (macroCommand->getClassType()) { case WuQMacroClassTypeEnum::ACTION: @@ -286,6 +223,48 @@ WuQMacroGroupXmlStreamWriter::writeMacroCommandToXML(const WuQMacroCommand* macr m_xmlStreamWriter->writeTextElement(ELEMENT_MACRO_COMMAND_TOOL_TIP, toolTip); } + + const int32_t numberOfParameters = macroCommand->getNumberOfParameters(); + for (int32_t i = 0; i < numberOfParameters; i++) { + const WuQMacroCommandParameter* parameter = macroCommand->getParameterAtIndex(i); + CaretAssert(parameter); + + m_xmlStreamWriter->writeStartElement(ELEMENT_MACRO_COMMAND_PARAMETER); + + m_xmlStreamWriter->writeAttribute(ATTRIBUTE_MACRO_COMMAND_PARAMETER_DATA_TYPE, + WuQMacroDataValueTypeEnum::toName(parameter->getDataType())); + + const QVariant value(parameter->getValue()); + QString stringValue; + switch (parameter->getDataType()) { + case WuQMacroDataValueTypeEnum::INVALID: + break; + case WuQMacroDataValueTypeEnum::BOOLEAN: + stringValue = (value.toBool() ? VALUE_BOOL_TRUE : VALUE_BOOL_FALSE); + break; + case WuQMacroDataValueTypeEnum::FLOAT: + stringValue = QString::number(value.toFloat()); + break; + case WuQMacroDataValueTypeEnum::INTEGER: + stringValue = QString::number(value.toInt()); + break; + case WuQMacroDataValueTypeEnum::MOUSE: + stringValue = "MouseEvent"; + break; + case WuQMacroDataValueTypeEnum::NONE: + stringValue = ""; + break; + case WuQMacroDataValueTypeEnum::STRING: + stringValue = value.toString(); + break; + } + m_xmlStreamWriter->writeAttribute(ATTRIBUTE_MACRO_COMMAND_PARAMETER_VALUE, + stringValue); + m_xmlStreamWriter->writeAttribute(ATTRIBUTE_MACRO_COMMAND_PARAMETER_NAME, + parameter->getName()); + m_xmlStreamWriter->writeEndElement(); + } + m_xmlStreamWriter->writeEndElement(); } @@ -300,7 +279,7 @@ WuQMacroGroupXmlStreamWriter::writeMacroMouseEventInfo(const WuQMacroMouseEventI { CaretAssert(mouseEventInfo); - m_xmlStreamWriter->writeStartElement(ELEMENT_MOUSE_EVENT_INFO); + m_xmlStreamWriter->writeStartElement(ELEMENT_MACRO_COMMAND_MOUSE_EVENT_INFO); m_xmlStreamWriter->writeAttribute(ATTRIBUTE_MOUSE_BUTTON, QString::number(mouseEventInfo->getMouseButton())); m_xmlStreamWriter->writeAttribute(ATTRIBUTE_MOUSE_BUTTONS_MASK, QString::number(mouseEventInfo->getMouseButtonsMask())); diff --git a/src/GuiQt/WuQMacroDialog.cxx b/src/GuiQt/WuQMacroDialog.cxx index 8446749e5..cfb73c6a0 100644 --- a/src/GuiQt/WuQMacroDialog.cxx +++ b/src/GuiQt/WuQMacroDialog.cxx @@ -37,6 +37,7 @@ #include #include #include +#include #include #include #include @@ -77,7 +78,6 @@ WuQMacroDialog::WuQMacroDialog(QWidget* parent) m_macroGroups = WuQMacroManager::instance()->getMacroGroups(); -// QLabel* macrosLabel = new QLabel("Macro:"); QLabel* macroGroupLabel = new QLabel("Macros in:"); m_macroGroupComboBox = new QComboBox(); @@ -103,7 +103,6 @@ WuQMacroDialog::WuQMacroDialog(QWidget* parent) gridLayout->addWidget(createHorizontalLine(), row, 0, 1, 3); row++; gridLayout->addWidget(createMacroRunAndEditingToolButtons(), row, 0, 1, 3); -// gridLayout->addWidget(macrosLabel, row, 0, (Qt::AlignTop| Qt::AlignLeft)); row++; gridLayout->addWidget(createMacroAndCommandSelectionWidget(), row, 0, 1, 3); row++; @@ -448,21 +447,7 @@ WuQMacroDialog::createCommandDisplayWidget() QLabel* typeLabel = new QLabel("Type:"); m_commandTypeLabel = new QLabel(); - - m_commandValueOnePushButton = new QPushButton("Value..."); - m_commandValueOnePushButton->setSizePolicy(QSizePolicy::Fixed, - m_commandValueOnePushButton->sizePolicy().verticalPolicy()); - QObject::connect(m_commandValueOnePushButton, &QPushButton::clicked, - [=] { setMacroCommandValue(ValueIndex::ONE); }); - m_commandValueOneLabel = new QLabel(); - - m_commandValueTwoPushButton = new QPushButton("Alt Value..."); - m_commandValueTwoPushButton->setSizePolicy(QSizePolicy::Fixed, - m_commandValueTwoPushButton->sizePolicy().verticalPolicy()); - QObject::connect(m_commandValueTwoPushButton, &QPushButton::clicked, - [=] { setMacroCommandValue(ValueIndex::TWO); }); - m_commandValueTwoLabel = new QLabel(); - + QLabel* delayLabel = new QLabel("Delay:"); m_commandDelaySpinBox = new QDoubleSpinBox(); m_commandDelaySpinBox->setMinimum(0.0); @@ -489,12 +474,6 @@ WuQMacroDialog::createCommandDisplayWidget() layout->addWidget(titleLabel, row, 0); layout->addWidget(m_commandTitleLabel, row, 1); row++; - layout->addWidget(m_commandValueOnePushButton, row, 0); - layout->addWidget(m_commandValueOneLabel, row, 1); - row++; - layout->addWidget(m_commandValueTwoPushButton, row, 0); - layout->addWidget(m_commandValueTwoLabel, row, 1); - row++; layout->addWidget(nameLabel, row, 0); layout->addWidget(m_commandNameLabel, row, 1); row++; @@ -508,6 +487,12 @@ WuQMacroDialog::createCommandDisplayWidget() layout->addWidget(m_commandToolTipTextEdit, row, 1); row++; + QWidget* parametersWidget = new QWidget(); + m_parameterWidgetsGridLayout = new QGridLayout(parametersWidget); + QScrollArea* parameterScrollArea = new QScrollArea(); + parameterScrollArea->setWidget(parametersWidget); + parameterScrollArea->setWidgetResizable(true); + QHBoxLayout* titleLayout = new QHBoxLayout(); titleLayout->setContentsMargins(0, 0, 0, 0); titleLayout->addWidget(new QLabel("Command")); @@ -517,6 +502,7 @@ WuQMacroDialog::createCommandDisplayWidget() QVBoxLayout* widgetLayout = new QVBoxLayout(widget); widgetLayout->addLayout(titleLayout); widgetLayout->addLayout(layout); + widgetLayout->addWidget(parameterScrollArea); widgetLayout->addStretch(); return widget; @@ -680,103 +666,68 @@ WuQMacroDialog::updateMacroWidget(WuQMacro* macro) void WuQMacroDialog::updateCommandWidget(WuQMacroCommand* command) { - bool validOneFlag = false; - QString newDataValueOne; - if (command != NULL) { - newDataValueOne = command->getDataValue().toString(); - - switch (command->getDataType()) { - case WuQMacroDataValueTypeEnum::INVALID: - break; - case WuQMacroDataValueTypeEnum::BOOLEAN: - if (command->getDataValue().toBool()) { - newDataValueOne = "On"; - } - else { - newDataValueOne = "Off"; - } - validOneFlag = true; - break; - case WuQMacroDataValueTypeEnum::FLOAT: - validOneFlag = true; - break; - case WuQMacroDataValueTypeEnum::INTEGER: - validOneFlag = true; - break; - case WuQMacroDataValueTypeEnum::MOUSE: - break; - case WuQMacroDataValueTypeEnum::NONE: - newDataValueOne = ""; - break; - case WuQMacroDataValueTypeEnum::STRING: - validOneFlag = true; - break; - } - } - - bool validTwoFlag = false; - QString newDataValueTwo; - if (command != NULL) { - newDataValueTwo = command->getDataValueTwo().toString(); - - switch (command->getDataTypeTwo()) { - case WuQMacroDataValueTypeEnum::INVALID: - break; - case WuQMacroDataValueTypeEnum::BOOLEAN: - if (command->getDataValueTwo().toBool()) { - newDataValueTwo = "On"; - } - else { - newDataValueTwo = "Off"; - } - validTwoFlag = true; - break; - case WuQMacroDataValueTypeEnum::FLOAT: - validTwoFlag = true; - break; - case WuQMacroDataValueTypeEnum::INTEGER: - validTwoFlag = true; - break; - case WuQMacroDataValueTypeEnum::MOUSE: - break; - case WuQMacroDataValueTypeEnum::NONE: - newDataValueTwo = ""; - break; - case WuQMacroDataValueTypeEnum::STRING: - validTwoFlag = true; - break; - } - } - QString title; QString name; QString type; - QString newDataTypeTwo; QString toolTip; float delay(0.0f); if (command != NULL) { title = command->text(); name = command->getObjectName(); type = WuQMacroClassTypeEnum::toGuiName(command->getClassType()); - newDataTypeTwo = WuQMacroDataValueTypeEnum::toGuiName(command->getDataTypeTwo()); toolTip = command->getObjectToolTip(); delay = command->getDelayInSeconds(); } m_commandTitleLabel->setText(title); m_commandNameLabel->setText(name); m_commandTypeLabel->setText(type); - m_commandValueOneLabel->setText(newDataValueOne); - m_commandValueOnePushButton->setEnabled(validOneFlag); - m_commandValueTwoLabel->setText(""); - m_commandValueTwoPushButton->setEnabled(false); - if (validTwoFlag) { - m_commandValueTwoLabel->setText(newDataValueTwo); - m_commandValueTwoPushButton->setEnabled(true); - } + QSignalBlocker delayBlocker(m_commandDelaySpinBox); m_commandDelaySpinBox->setValue(delay); m_commandToolTipTextEdit->setPlainText(toolTip); + + /** + * Update the parameter widgets + */ + const int32_t numParams = ((command != NULL) + ? command->getNumberOfParameters() + : 0); + int32_t numWidgets = static_cast(m_parameterWidgets.size()); + + for (int32_t i = numWidgets; i < numParams; i++) { + CommandParameterWidget* cpw = new CommandParameterWidget(i, + m_parameterWidgetsGridLayout, + this); + QObject::connect(cpw, &CommandParameterWidget::dataChanged, + this, &WuQMacroDialog::commandParamaterDataChanged); + m_parameterWidgets.push_back(cpw); + } + + for (int32_t i = 0; i < numParams; i++) { + m_parameterWidgets[i]->updateContent(command->getParameterAtIndex(i)); + } + + numWidgets = static_cast(m_parameterWidgets.size()); + for (int32_t i = numParams; i < numWidgets; i++) { + m_parameterWidgets[i]->updateContent(NULL); + } +} + +/** + * Called when a command parameter is changed + * + * @param index + * Index of the parameter + */ +void +WuQMacroDialog::commandParamaterDataChanged(int) +{ + WuQMacro* macro = getSelectedMacro(); + if (macro != NULL) { + WuQMacroManager::instance()->macroWasModified(macro); + } + updateDialogContents(); } /** @@ -994,140 +945,6 @@ WuQMacroDialog::macroCommandDelaySpinBoxValueChanged(double value) } } -/** - * Set the command value for the given value index - * - * @param valueIndex - * Value one or two - */ -void -WuQMacroDialog::setMacroCommandValue(const ValueIndex valueIndex) -{ - WuQMacroCommand* macroCommand = getSelectedMacroCommand(); - if (macroCommand == NULL) { - return; - } - - WuQMacroDataValueTypeEnum::Enum dataType = WuQMacroDataValueTypeEnum::INVALID; - QVariant dataValue; - QWidget* parentWidget(NULL); - QString dataValueUpdateText; - switch (valueIndex) { - case ValueIndex::ONE: - dataType = macroCommand->getDataType(); - dataValue = macroCommand->getDataValue(); - dataValueUpdateText = macroCommand->getDataValueUpdateLabelText(); - parentWidget = m_commandValueOnePushButton; - break; - case ValueIndex::TWO: - dataType = macroCommand->getDataTypeTwo(); - dataValue = macroCommand->getDataValueTwo(); - dataValueUpdateText = macroCommand->getDataValueTwoUpdateLabelText(); - parentWidget = m_commandValueTwoPushButton; - break; - } - - bool validFlag(false); - switch (dataType) { - case WuQMacroDataValueTypeEnum::BOOLEAN: - { - QStringList items; - items.push_back("On"); - items.push_back("Off"); - - bool ok(false); - int defaultIndex = (dataValue.toBool() ? 0 : 1); - const QString text = QInputDialog::getItem(parentWidget, - "New Status", - dataValueUpdateText, - items, - defaultIndex, - false, - &ok); - if (ok - && ( ! text.isEmpty())) { - bool result(text == items.at(0)); - dataValue.setValue(result); - validFlag = true; - } - } - break; - case WuQMacroDataValueTypeEnum::FLOAT: - { - bool ok(false); - const float f = QInputDialog::getDouble(parentWidget, - "New Value", - dataValueUpdateText, - dataValue.toDouble(), - -2147483647, - 2147483647, - 3, - &ok); - if (ok) { - dataValue.setValue(f); - validFlag = true; - } - } - break; - case WuQMacroDataValueTypeEnum::INTEGER: - { - bool ok(false); - const int i = QInputDialog::getInt(parentWidget, - "New Value", - dataValueUpdateText, - dataValue.toInt(), - -2147483647, - 2147483647, - 1, - &ok); - if (ok) { - dataValue.setValue(i); - validFlag = true; - } - } - break; - case WuQMacroDataValueTypeEnum::INVALID: - break; - case WuQMacroDataValueTypeEnum::MOUSE: - break; - case WuQMacroDataValueTypeEnum::NONE: - break; - case WuQMacroDataValueTypeEnum::STRING: - { - bool ok(false); - const QString text = QInputDialog::getText(parentWidget, - "New Text", - dataValueUpdateText, - QLineEdit::Normal, - dataValue.toString(), - &ok); - if (ok) { - dataValue.setValue(text); - validFlag = true; - } - } - break; - } - - if (validFlag) { - switch (valueIndex) { - case ValueIndex::ONE: - macroCommand->setDataValue(dataValue); - break; - case ValueIndex::TWO: - macroCommand->setDataValueTwo(dataValue); - break; - } - - WuQMacro* macro = getSelectedMacro(); - if (macro != NULL) { - WuQMacroManager::instance()->macroWasModified(macro); - } - } - - updateDialogContents(); -} - /** * @return a horizontal line */ @@ -1198,7 +1015,6 @@ WuQMacroDialog::editingMoveUpToolButtonClicked() if (macroGroup != NULL) { if (macro != NULL) { macroGroup->moveMacroUp(macro); -// updateMacroWidget(macro); m_treeView->setCurrentIndex(macro->index()); treeViewItemClicked(macro->index()); } @@ -1208,7 +1024,6 @@ WuQMacroDialog::editingMoveUpToolButtonClicked() if ((macro != NULL) && (command != NULL)) { macro->moveMacroCommandUp(command); -// updateCommandWidget(command); m_treeView->setCurrentIndex(command->index()); treeViewItemClicked(command->index()); } @@ -1233,7 +1048,6 @@ WuQMacroDialog::editingMoveDownToolButtonClicked() if (macroGroup != NULL) { if (macro != NULL) { macroGroup->moveMacroDown(macro); -// updateMacroWidget(macro); m_treeView->setCurrentIndex(macro->index()); treeViewItemClicked(macro->index()); } @@ -1243,7 +1057,6 @@ WuQMacroDialog::editingMoveDownToolButtonClicked() if ((macro != NULL) && (command != NULL)) { macro->moveMacroCommandDown(command); -// updateCommandWidget(command); m_treeView->setCurrentIndex(command->index()); treeViewItemClicked(command->index()); } @@ -1442,6 +1255,151 @@ WuQMacroDialog::createEditingToolButtonPixmap(const QWidget* widget, return pixmap; } +/** + * Constructor for command parameter widgets + * + * @param index + * Index of the parameter + * @param gridLayout + * Layout containing the widgets + * @param parent + * The parent widget + */ +CommandParameterWidget::CommandParameterWidget(const int32_t index, + QGridLayout* gridLayout, + QWidget* parent) +: QObject(parent), +m_index(index) +{ + m_label = new QLabel(); + + m_pushButton = new QPushButton(); + QObject::connect(m_pushButton, &QPushButton::clicked, + this, &CommandParameterWidget::pushButtonClicked); + + const int32_t row = gridLayout->rowCount(); + gridLayout->addWidget(m_pushButton, row, 0); + gridLayout->addWidget(m_label, row, 1, Qt::AlignLeft); +} + +/** + * Update content of a command parameter + * + * @param parameter + * The parameter + */ +void +CommandParameterWidget::updateContent(WuQMacroCommandParameter* parameter) +{ + m_parameter = parameter; + const bool validFlag(m_parameter != NULL); + if (validFlag) { + m_label->setText(m_parameter->getValue().toString()); + m_pushButton->setText(m_parameter->getName()); + } + m_label->setVisible(validFlag); + m_pushButton->setVisible(validFlag); +} +/** + * Called when command parameter push button is clicked + */ +void +CommandParameterWidget::pushButtonClicked() +{ + CaretAssert(m_parameter); + + const WuQMacroDataValueTypeEnum::Enum dataType = m_parameter->getDataType(); + QVariant dataValue = m_parameter->getValue(); + const QString dataValueUpdateText = m_parameter->getName(); + + bool validFlag(false); + switch (dataType) { + case WuQMacroDataValueTypeEnum::BOOLEAN: + { + QStringList items; + items.push_back("On"); + items.push_back("Off"); + + bool ok(false); + int defaultIndex = (dataValue.toBool() ? 0 : 1); + const QString text = QInputDialog::getItem(m_pushButton, + "New Status", + dataValueUpdateText, + items, + defaultIndex, + false, + &ok); + if (ok + && ( ! text.isEmpty())) { + bool result(text == items.at(0)); + dataValue.setValue(result); + validFlag = true; + } + } + break; + case WuQMacroDataValueTypeEnum::FLOAT: + { + bool ok(false); + const float f = QInputDialog::getDouble(m_pushButton, + "New Value", + dataValueUpdateText, + dataValue.toDouble(), + -2147483647, + 2147483647, + 3, + &ok); + if (ok) { + dataValue.setValue(f); + validFlag = true; + } + } + break; + case WuQMacroDataValueTypeEnum::INTEGER: + { + bool ok(false); + const int i = QInputDialog::getInt(m_pushButton, + "New Value", + dataValueUpdateText, + dataValue.toInt(), + -2147483647, + 2147483647, + 1, + &ok); + if (ok) { + dataValue.setValue(i); + validFlag = true; + } + } + break; + case WuQMacroDataValueTypeEnum::INVALID: + break; + case WuQMacroDataValueTypeEnum::MOUSE: + break; + case WuQMacroDataValueTypeEnum::NONE: + break; + case WuQMacroDataValueTypeEnum::STRING: + { + bool ok(false); + const QString text = QInputDialog::getText(m_pushButton, + "New Text", + dataValueUpdateText, + QLineEdit::Normal, + dataValue.toString(), + &ok); + if (ok) { + dataValue.setValue(text); + validFlag = true; + } + } + break; + } + + if (validFlag) { + m_parameter->setValue(dataValue); + + emit dataChanged(m_index); + } +} diff --git a/src/GuiQt/WuQMacroDialog.h b/src/GuiQt/WuQMacroDialog.h index 5530033fd..b6bf89096 100644 --- a/src/GuiQt/WuQMacroDialog.h +++ b/src/GuiQt/WuQMacroDialog.h @@ -27,6 +27,7 @@ #include +#include "WuQMacroCommandParameter.h" #include "WuQMacroShortCutKeyEnum.h" #include "WuQMacroStandardItemTypeEnum.h" @@ -35,6 +36,7 @@ class QCheckBox; class QComboBox; class QDialogButtonBox; class QDoubleSpinBox; +class QGridLayout; class QLabel; class QMenu; class QPlainTextEdit; @@ -45,6 +47,7 @@ class QToolButton; namespace caret { + class CommandParameterWidget; class WuQMacro; class WuQMacroCommand; class WuQMacroGroup; @@ -102,6 +105,8 @@ namespace caret { void macroCommandDelaySpinBoxValueChanged(double); + void commandParamaterDataChanged(int); + private: enum class ValueIndex { ONE, @@ -128,8 +133,6 @@ namespace caret { void updateCommandWidget(WuQMacroCommand* command); - void setMacroCommandValue(const ValueIndex valueIndex); - WuQMacroGroup* getSelectedMacroGroup(); WuQMacro* getSelectedMacro(); @@ -183,18 +186,14 @@ namespace caret { QLabel* m_commandNameLabel; - QPushButton* m_commandValueOnePushButton; - - QLabel* m_commandValueOneLabel; - - QPushButton* m_commandValueTwoPushButton; - - QLabel* m_commandValueTwoLabel; - QDoubleSpinBox* m_commandDelaySpinBox; QPlainTextEdit* m_commandToolTipTextEdit; + std::vector m_parameterWidgets; + + QGridLayout* m_parameterWidgetsGridLayout; + QToolButton* m_runMacroToolButton; QToolButton* m_editingMoveUpToolButton; @@ -209,6 +208,33 @@ namespace caret { }; + class CommandParameterWidget : public QObject { + Q_OBJECT + + public: + CommandParameterWidget(const int32_t index, + QGridLayout* gridLayout, + QWidget* parent); + + void updateContent(WuQMacroCommandParameter* parameter); + + QLabel* m_label; + + QPushButton* m_pushButton; + + signals: + void dataChanged(const int index); + + private slots: + void pushButtonClicked(); + + private: + const int32_t m_index; + + WuQMacroCommandParameter* m_parameter = NULL; + }; + + #ifdef __WU_Q_MACRO_DIALOG_DECLARE__ // #endif // __WU_Q_MACRO_DIALOG_DECLARE__ diff --git a/src/GuiQt/WuQMacroExecutor.cxx b/src/GuiQt/WuQMacroExecutor.cxx index 2f950652f..eab6b3326 100644 --- a/src/GuiQt/WuQMacroExecutor.cxx +++ b/src/GuiQt/WuQMacroExecutor.cxx @@ -49,6 +49,7 @@ #include "CaretLogger.h" #include "WuQMacro.h" #include "WuQMacroCommand.h" +#include "WuQMacroCommandParameter.h" #include "WuQMacroMouseEventInfo.h" #include "WuQMacroMouseEventWidgetInterface.h" #include "WuQMacroSignalEmitter.h" @@ -368,8 +369,6 @@ WuQMacroExecutor::runMacroCommand(const WuQMacroCommand* macroCommand, CaretAssert(object); const WuQMacroClassTypeEnum::Enum classType = macroCommand->getClassType(); - const QVariant dataValue = macroCommand->getDataValue(); - const QVariant dataValueTwo = macroCommand->getDataValueTwo(); QString objectErrorMessage; bool notFoundFlag(false); @@ -475,7 +474,11 @@ WuQMacroExecutor::runActionCommand(const WuQMacroCommand* macroCommand, QAction* action = qobject_cast(object); if (action != NULL) { moveMouseToWidget(action); - CaretAssert(macroCommand->getDataType() == WuQMacroDataValueTypeEnum::NONE); + CaretAssert(macroCommand->getNumberOfParameters() > 0); + const WuQMacroCommandParameter* parameterOne = macroCommand->getParameterAtIndex(0); + CaretAssert(parameterOne); + CaretAssert(parameterOne->getDataType() == WuQMacroDataValueTypeEnum::NONE); + WuQMacroSignalEmitter signalEmitter; signalEmitter.emitQActionSignal(action, true); @@ -506,10 +509,13 @@ WuQMacroExecutor::runActionCheckableCommand(const WuQMacroCommand* macroCommand, QAction* action = qobject_cast(object); if (action != NULL) { moveMouseToWidget(action); - CaretAssert(macroCommand->getDataType() == WuQMacroDataValueTypeEnum::BOOLEAN); + CaretAssert(macroCommand->getNumberOfParameters() > 0); + const WuQMacroCommandParameter* parameterOne = macroCommand->getParameterAtIndex(0); + CaretAssert(parameterOne); + CaretAssert(parameterOne->getDataType() == WuQMacroDataValueTypeEnum::BOOLEAN); WuQMacroSignalEmitter signalEmitter; signalEmitter.emitQActionSignal(action, - macroCommand->getDataValue().toBool()); + parameterOne->getValue().toBool()); } else { castFailureFlagOut = true; @@ -538,18 +544,25 @@ WuQMacroExecutor::runActionGroupCommand(const WuQMacroCommand* macroCommand, QActionGroup* actionGroup = qobject_cast(object); if (actionGroup != NULL) { moveMouseToWidget(actionGroup); - CaretAssert(macroCommand->getDataType() == WuQMacroDataValueTypeEnum::STRING); - CaretAssert(macroCommand->getDataTypeTwo() == WuQMacroDataValueTypeEnum::INTEGER); + + CaretAssert(macroCommand->getNumberOfParameters() > 1); + const WuQMacroCommandParameter* parameterOne = macroCommand->getParameterAtIndex(0); + CaretAssert(parameterOne); + const WuQMacroCommandParameter* parameterTwo = macroCommand->getParameterAtIndex(1); + CaretAssert(parameterTwo); + + CaretAssert(parameterOne->getDataType() == WuQMacroDataValueTypeEnum::STRING); + CaretAssert(parameterTwo->getDataType() == WuQMacroDataValueTypeEnum::INTEGER); QAction* textAction(NULL); QAction* indexAction(NULL); QList actions = actionGroup->actions(); for (int32_t i = 0; i < actions.size(); i++) { QAction* actionAtIndex = actions.at(i); - if (actionAtIndex->text() == macroCommand->getDataValue().toString()) { + if (actionAtIndex->text() == parameterOne->getValue().toString()) { textAction = actionAtIndex; } - if (macroCommand->getDataValueTwo().toInt() == i) { + if (parameterTwo->getValue().toInt() == i) { indexAction = actionAtIndex; } } @@ -567,9 +580,9 @@ WuQMacroExecutor::runActionGroupCommand(const WuQMacroCommand* macroCommand, errorMessageOut = ("For QActionGroup \"" + object->objectName() + "\", unable to find action with text \"" - + macroCommand->getDataValue().toString() + + parameterOne->getValue().toString() + "\" or index=" - + macroCommand->getDataValueTwo().toInt()); + + parameterTwo->getValue().toInt()); } } else { @@ -597,10 +610,17 @@ WuQMacroExecutor::runButtonGroupCommand(const WuQMacroCommand* macroCommand, { QButtonGroup* buttonGroup = qobject_cast(object); if (buttonGroup != NULL) { - CaretAssert(macroCommand->getDataType() == WuQMacroDataValueTypeEnum::STRING); - CaretAssert(macroCommand->getDataTypeTwo() == WuQMacroDataValueTypeEnum::INTEGER); - const QVariant dataValue = macroCommand->getDataValue(); - const QVariant dataValueTwo = macroCommand->getDataValueTwo(); + CaretAssert(macroCommand->getNumberOfParameters() > 1); + const WuQMacroCommandParameter* parameterOne = macroCommand->getParameterAtIndex(0); + CaretAssert(parameterOne); + const WuQMacroCommandParameter* parameterTwo = macroCommand->getParameterAtIndex(1); + CaretAssert(parameterTwo); + + CaretAssert(parameterOne->getDataType() == WuQMacroDataValueTypeEnum::STRING); + CaretAssert(parameterTwo->getDataType() == WuQMacroDataValueTypeEnum::INTEGER); + + const QVariant dataValue = parameterOne->getValue(); + const QVariant dataValueTwo = parameterTwo->getValue(); QAbstractButton* textButton(NULL); QAbstractButton* indexButton(NULL); @@ -661,10 +681,13 @@ WuQMacroExecutor::runCheckBoxCommand(const WuQMacroCommand* macroCommand, QCheckBox* checkBox = qobject_cast(object); if (checkBox != NULL) { moveMouseToWidget(checkBox); - CaretAssert(macroCommand->getDataType() == WuQMacroDataValueTypeEnum::BOOLEAN); + CaretAssert(macroCommand->getNumberOfParameters() > 0); + const WuQMacroCommandParameter* parameterOne = macroCommand->getParameterAtIndex(0); + CaretAssert(parameterOne); + CaretAssert(parameterOne->getDataType() == WuQMacroDataValueTypeEnum::BOOLEAN); WuQMacroSignalEmitter signalEmitter; signalEmitter.emitQCheckBoxSignal(checkBox, - macroCommand->getDataValue().toBool()); + parameterOne->getValue().toBool()); } else { castFailureFlagOut = true; @@ -692,10 +715,16 @@ WuQMacroExecutor::runComboBoxCommand(const WuQMacroCommand* macroCommand, { QComboBox* comboBox = qobject_cast(object); if (comboBox != NULL) { - const QVariant dataValue = macroCommand->getDataValue(); - const QVariant dataValueTwo = macroCommand->getDataValueTwo(); - CaretAssert(macroCommand->getDataType() == WuQMacroDataValueTypeEnum::STRING); - CaretAssert(macroCommand->getDataTypeTwo() == WuQMacroDataValueTypeEnum::INTEGER); + CaretAssert(macroCommand->getNumberOfParameters() > 1); + const WuQMacroCommandParameter* parameterOne = macroCommand->getParameterAtIndex(0); + CaretAssert(parameterOne); + const WuQMacroCommandParameter* parameterTwo = macroCommand->getParameterAtIndex(1); + CaretAssert(parameterTwo); + + const QVariant dataValue = parameterOne->getValue(); + const QVariant dataValueTwo = parameterTwo->getValue(); + CaretAssert(parameterOne->getDataType() == WuQMacroDataValueTypeEnum::STRING); + CaretAssert(parameterTwo->getDataType() == WuQMacroDataValueTypeEnum::INTEGER); int textIndex(-1); int indexIndex(-1); for (int32_t i = 0; i < comboBox->count(); i++) { @@ -750,8 +779,12 @@ WuQMacroExecutor::runDoubleSpinBoxCommand(const WuQMacroCommand* macroCommand, { QDoubleSpinBox* doubleSpinBox = qobject_cast(object); if (doubleSpinBox != NULL) { - const QVariant dataValue = macroCommand->getDataValue(); - const WuQMacroDataValueTypeEnum::Enum dataValueType = macroCommand->getDataType(); + CaretAssert(macroCommand->getNumberOfParameters() > 0); + const WuQMacroCommandParameter* parameterOne = macroCommand->getParameterAtIndex(0); + CaretAssert(parameterOne); + + const QVariant dataValue = parameterOne->getValue(); + const WuQMacroDataValueTypeEnum::Enum dataValueType = parameterOne->getDataType(); CaretAssert(dataValueType == WuQMacroDataValueTypeEnum::FLOAT); moveMouseToWidget(doubleSpinBox); @@ -784,8 +817,12 @@ WuQMacroExecutor::runLineEditCommand(const WuQMacroCommand* macroCommand, { QLineEdit* lineEdit = qobject_cast(object); if (lineEdit != NULL) { - const QVariant dataValue = macroCommand->getDataValue(); - const WuQMacroDataValueTypeEnum::Enum dataValueType = macroCommand->getDataType(); + CaretAssert(macroCommand->getNumberOfParameters() > 0); + const WuQMacroCommandParameter* parameterOne = macroCommand->getParameterAtIndex(0); + CaretAssert(parameterOne); + + const QVariant dataValue = parameterOne->getValue(); + const WuQMacroDataValueTypeEnum::Enum dataValueType = parameterOne->getDataType(); moveMouseToWidget(lineEdit); CaretAssert(dataValueType == WuQMacroDataValueTypeEnum::STRING); @@ -819,10 +856,16 @@ WuQMacroExecutor::runListWidgetCommand(const WuQMacroCommand* macroCommand, { QListWidget* listWidget = qobject_cast(object); if (listWidget != NULL) { - const QVariant dataValue = macroCommand->getDataValue(); - const WuQMacroDataValueTypeEnum::Enum dataValueType = macroCommand->getDataType(); - const QVariant dataValueTwo = macroCommand->getDataValueTwo(); - const WuQMacroDataValueTypeEnum::Enum dataValueTypeTwo = macroCommand->getDataTypeTwo(); + CaretAssert(macroCommand->getNumberOfParameters() > 1); + const WuQMacroCommandParameter* parameterOne = macroCommand->getParameterAtIndex(0); + CaretAssert(parameterOne); + const WuQMacroCommandParameter* parameterTwo = macroCommand->getParameterAtIndex(1); + CaretAssert(parameterTwo); + + const QVariant dataValue = parameterOne->getValue(); + const WuQMacroDataValueTypeEnum::Enum dataValueType = parameterOne->getDataType(); + const QVariant dataValueTwo = parameterTwo->getValue(); + const WuQMacroDataValueTypeEnum::Enum dataValueTypeTwo = parameterTwo->getDataType(); CaretAssert(dataValueType == WuQMacroDataValueTypeEnum::STRING); CaretAssert(dataValueTypeTwo == WuQMacroDataValueTypeEnum::INTEGER); @@ -884,10 +927,15 @@ WuQMacroExecutor::runMenuCommand(const WuQMacroCommand* macroCommand, { QMenu* menu = qobject_cast(object); if (menu != NULL) { - const QVariant dataValue = macroCommand->getDataValue(); - const WuQMacroDataValueTypeEnum::Enum dataValueType = macroCommand->getDataType(); - const QVariant dataValueTwo = macroCommand->getDataValueTwo(); - const WuQMacroDataValueTypeEnum::Enum dataValueTypeTwo = macroCommand->getDataTypeTwo(); + CaretAssert(macroCommand->getNumberOfParameters() > 1); + const WuQMacroCommandParameter* parameterOne = macroCommand->getParameterAtIndex(0); + CaretAssert(parameterOne); + const WuQMacroCommandParameter* parameterTwo = macroCommand->getParameterAtIndex(1); + + const QVariant dataValue = parameterOne->getValue(); + const WuQMacroDataValueTypeEnum::Enum dataValueType = parameterOne->getDataType(); + const QVariant dataValueTwo = parameterTwo->getValue(); + const WuQMacroDataValueTypeEnum::Enum dataValueTypeTwo = parameterTwo->getDataType(); CaretAssert(dataValueType == WuQMacroDataValueTypeEnum::STRING); CaretAssert(dataValueTypeTwo == WuQMacroDataValueTypeEnum::INTEGER); @@ -1037,8 +1085,12 @@ WuQMacroExecutor::runPushButtonCommand(const WuQMacroCommand* macroCommand, { QPushButton* pushButton = qobject_cast(object); if (pushButton != NULL) { - const QVariant dataValue = macroCommand->getDataValue(); - const WuQMacroDataValueTypeEnum::Enum dataValueType = macroCommand->getDataType(); + CaretAssert(macroCommand->getNumberOfParameters() > 0); + const WuQMacroCommandParameter* parameterOne = macroCommand->getParameterAtIndex(0); + CaretAssert(parameterOne); + + const QVariant dataValue = parameterOne->getValue(); + const WuQMacroDataValueTypeEnum::Enum dataValueType = parameterOne->getDataType(); moveMouseToWidget(pushButton); CaretAssert(dataValueType == WuQMacroDataValueTypeEnum::NONE); @@ -1071,8 +1123,12 @@ WuQMacroExecutor::runPushButtonCheckableCommand(const WuQMacroCommand* macroComm { QPushButton* pushButton = qobject_cast(object); if (pushButton != NULL) { - const QVariant dataValue = macroCommand->getDataValue(); - const WuQMacroDataValueTypeEnum::Enum dataValueType = macroCommand->getDataType(); + CaretAssert(macroCommand->getNumberOfParameters() > 0); + const WuQMacroCommandParameter* parameterOne = macroCommand->getParameterAtIndex(0); + CaretAssert(parameterOne); + + const QVariant dataValue = parameterOne->getValue(); + const WuQMacroDataValueTypeEnum::Enum dataValueType = parameterOne->getDataType(); moveMouseToWidget(pushButton); CaretAssert(dataValueType == WuQMacroDataValueTypeEnum::BOOLEAN); @@ -1105,8 +1161,12 @@ WuQMacroExecutor::runRadioButtonCommand(const WuQMacroCommand* macroCommand, { QRadioButton* radioButton = qobject_cast(object); if (radioButton != NULL) { - const QVariant dataValue = macroCommand->getDataValue(); - const WuQMacroDataValueTypeEnum::Enum dataValueType = macroCommand->getDataType(); + CaretAssert(macroCommand->getNumberOfParameters() > 0); + const WuQMacroCommandParameter* parameterOne = macroCommand->getParameterAtIndex(0); + CaretAssert(parameterOne); + + const QVariant dataValue = parameterOne->getValue(); + const WuQMacroDataValueTypeEnum::Enum dataValueType = parameterOne->getDataType(); moveMouseToWidget(radioButton); CaretAssert(dataValueType == WuQMacroDataValueTypeEnum::NONE); @@ -1140,8 +1200,12 @@ WuQMacroExecutor::runSliderCommand(const WuQMacroCommand* macroCommand, { QSlider* slider = qobject_cast(object); if (slider != NULL) { - const QVariant dataValue = macroCommand->getDataValue(); - const WuQMacroDataValueTypeEnum::Enum dataValueType = macroCommand->getDataType(); + CaretAssert(macroCommand->getNumberOfParameters() > 0); + const WuQMacroCommandParameter* parameterOne = macroCommand->getParameterAtIndex(0); + CaretAssert(parameterOne); + + const QVariant dataValue = parameterOne->getValue(); + const WuQMacroDataValueTypeEnum::Enum dataValueType = parameterOne->getDataType(); moveMouseToWidget(slider); CaretAssert(dataValueType == WuQMacroDataValueTypeEnum::INTEGER); @@ -1174,8 +1238,12 @@ WuQMacroExecutor::runSpinBoxCommand(const WuQMacroCommand* macroCommand, { QSpinBox* spinBox = qobject_cast(object); if (spinBox != NULL) { - const QVariant dataValue = macroCommand->getDataValue(); - const WuQMacroDataValueTypeEnum::Enum dataValueType = macroCommand->getDataType(); + CaretAssert(macroCommand->getNumberOfParameters() > 0); + const WuQMacroCommandParameter* parameterOne = macroCommand->getParameterAtIndex(0); + CaretAssert(parameterOne); + + const QVariant dataValue = parameterOne->getValue(); + const WuQMacroDataValueTypeEnum::Enum dataValueType = parameterOne->getDataType(); moveMouseToWidget(spinBox); CaretAssert(dataValueType == WuQMacroDataValueTypeEnum::INTEGER); @@ -1208,10 +1276,15 @@ WuQMacroExecutor::runTabBarCommand(const WuQMacroCommand* macroCommand, { QTabBar* tabBar = qobject_cast(object); if (tabBar != NULL) { - const QVariant dataValue = macroCommand->getDataValue(); - const WuQMacroDataValueTypeEnum::Enum dataValueType = macroCommand->getDataType(); - const QVariant dataValueTwo = macroCommand->getDataValueTwo(); - const WuQMacroDataValueTypeEnum::Enum dataValueTypeTwo = macroCommand->getDataTypeTwo(); + CaretAssert(macroCommand->getNumberOfParameters() > 1); + const WuQMacroCommandParameter* parameterOne = macroCommand->getParameterAtIndex(0); + CaretAssert(parameterOne); + const WuQMacroCommandParameter* parameterTwo = macroCommand->getParameterAtIndex(1); + + const QVariant dataValue = parameterOne->getValue(); + const WuQMacroDataValueTypeEnum::Enum dataValueType = parameterOne->getDataType(); + const QVariant dataValueTwo = parameterTwo->getValue(); + const WuQMacroDataValueTypeEnum::Enum dataValueTypeTwo = parameterTwo->getDataType(); CaretAssert(dataValueTypeTwo == WuQMacroDataValueTypeEnum::INTEGER); int32_t tabIndex = dataValueTwo.toInt(); @@ -1271,10 +1344,15 @@ WuQMacroExecutor::runTabWidgetCommand(const WuQMacroCommand* macroCommand, { QTabWidget* tabWidget = qobject_cast(object); if (tabWidget != NULL) { - const QVariant dataValue = macroCommand->getDataValue(); - const WuQMacroDataValueTypeEnum::Enum dataValueType = macroCommand->getDataType(); - const QVariant dataValueTwo = macroCommand->getDataValueTwo(); - const WuQMacroDataValueTypeEnum::Enum dataValueTypeTwo = macroCommand->getDataTypeTwo(); + CaretAssert(macroCommand->getNumberOfParameters() > 1); + const WuQMacroCommandParameter* parameterOne = macroCommand->getParameterAtIndex(0); + CaretAssert(parameterOne); + const WuQMacroCommandParameter* parameterTwo = macroCommand->getParameterAtIndex(1); + + const QVariant dataValue = parameterOne->getValue(); + const WuQMacroDataValueTypeEnum::Enum dataValueType = parameterOne->getDataType(); + const QVariant dataValueTwo = parameterTwo->getValue(); + const WuQMacroDataValueTypeEnum::Enum dataValueTypeTwo = parameterTwo->getDataType(); CaretAssert(dataValueTypeTwo == WuQMacroDataValueTypeEnum::INTEGER); QTabBar* tabBar = tabWidget->tabBar(); @@ -1337,9 +1415,12 @@ WuQMacroExecutor::runToolButtonCommand(const WuQMacroCommand* macroCommand, { QToolButton* toolButton = qobject_cast(object); if (toolButton != NULL) { - const QVariant dataValue = macroCommand->getDataValue(); - const WuQMacroDataValueTypeEnum::Enum dataValueType = macroCommand->getDataType(); - const QVariant dataValueTwo = macroCommand->getDataValueTwo(); + CaretAssert(macroCommand->getNumberOfParameters() > 0); + const WuQMacroCommandParameter* parameterOne = macroCommand->getParameterAtIndex(0); + CaretAssert(parameterOne); + + const QVariant dataValue = parameterOne->getValue(); + const WuQMacroDataValueTypeEnum::Enum dataValueType = parameterOne->getDataType(); moveMouseToWidget(toolButton); CaretAssert(dataValueType == WuQMacroDataValueTypeEnum::NONE); @@ -1372,9 +1453,12 @@ WuQMacroExecutor::runToolButtonCheckableCommand(const WuQMacroCommand* macroComm { QToolButton* toolButton = qobject_cast(object); if (toolButton != NULL) { - const QVariant dataValue = macroCommand->getDataValue(); - const WuQMacroDataValueTypeEnum::Enum dataValueType = macroCommand->getDataType(); - const QVariant dataValueTwo = macroCommand->getDataValueTwo(); + CaretAssert(macroCommand->getNumberOfParameters() > 0); + const WuQMacroCommandParameter* parameterOne = macroCommand->getParameterAtIndex(0); + CaretAssert(parameterOne); + + const QVariant dataValue = parameterOne->getValue(); + const WuQMacroDataValueTypeEnum::Enum dataValueType = parameterOne->getDataType(); moveMouseToWidget(toolButton); CaretAssert(dataValueType == WuQMacroDataValueTypeEnum::BOOLEAN); diff --git a/src/GuiQt/WuQMacroSignalWatcher.cxx b/src/GuiQt/WuQMacroSignalWatcher.cxx index 483f786ba..9d9af495e 100644 --- a/src/GuiQt/WuQMacroSignalWatcher.cxx +++ b/src/GuiQt/WuQMacroSignalWatcher.cxx @@ -43,6 +43,7 @@ #include "CaretAssert.h" #include "CaretLogger.h" #include "WuQMacroCommand.h" +#include "WuQMacroCommandParameter.h" #include "WuQMacroManager.h" using namespace caret; @@ -103,7 +104,7 @@ m_objectName(object->objectName()) QAction* action = qobject_cast(m_object); CaretAssert(action); QObject::connect(action, &QAction::triggered, - this, &WuQMacroSignalWatcher::actionTriggered); + this, &WuQMacroSignalWatcher::actionCheckableTriggered); m_toolTipText = action->toolTip(); } break; @@ -510,30 +511,31 @@ WuQMacroSignalWatcher::getToolTip() const return m_toolTipText; } + /** * If recording mode is enabled, create and send a macro command * to the macro manager. * - * @param value - * QVariant containing the data value. - * @param valueTwo - * QVariant containing the second data value. + * @param parameters + * Parameters for the command */ void -WuQMacroSignalWatcher::createAndSendMacroCommand(const QVariant& value, - const QVariant& valueTwo) +WuQMacroSignalWatcher::createAndSendMacroCommand(std::vector& parameters) { if (m_parentMacroManager->isModeRecording()) { WuQMacroCommand* mc = new WuQMacroCommand(m_objectType, m_objectName, m_descriptiveName, - m_toolTipText, - value, - valueTwo); + m_toolTipText); + for (auto p : parameters) { + mc->addParameter(p); + } + if ( ! m_parentMacroManager->addMacroCommandToRecording(mc)) { delete mc; } } + } /** @@ -560,8 +562,16 @@ WuQMacroSignalWatcher::actionGroupTriggered(QAction* action) const QString actionText((action != NULL) ? action->text() : ""); - createAndSendMacroCommand(actionText, - actionIndex); + + std::vector params; + params.push_back(new WuQMacroCommandParameter(WuQMacroDataValueTypeEnum::STRING, + "Select name", + actionText)); + params.push_back(new WuQMacroCommandParameter(WuQMacroDataValueTypeEnum::INTEGER, + "Select index", + actionIndex)); + + createAndSendMacroCommand(params); } @@ -572,10 +582,31 @@ WuQMacroSignalWatcher::actionGroupTriggered(QAction* action) * New checked status */ void -WuQMacroSignalWatcher::actionTriggered(bool checked) +WuQMacroSignalWatcher::actionTriggered(bool /*checked*/) +{ + std::vector params; + params.push_back(new WuQMacroCommandParameter(WuQMacroDataValueTypeEnum::NONE, + "Click/Select", + "")); + + createAndSendMacroCommand(params); +} + +/** + * Called when a checkable action is triggered + * + * @param checked + * New checked status + */ +void +WuQMacroSignalWatcher::actionCheckableTriggered(bool checked) { - createAndSendMacroCommand(checked, - ""); + std::vector params; + params.push_back(new WuQMacroCommandParameter(WuQMacroDataValueTypeEnum::BOOLEAN, + "On/Off", + checked)); + + createAndSendMacroCommand(params); } /** @@ -602,8 +633,15 @@ WuQMacroSignalWatcher::buttonGroupButtonClicked(QAbstractButton* button) const QString buttonText((button != NULL) ? button->text() : ""); - createAndSendMacroCommand(buttonText, - buttonIndex); + std::vector params; + params.push_back(new WuQMacroCommandParameter(WuQMacroDataValueTypeEnum::STRING, + "Select button with name", + buttonText)); + params.push_back(new WuQMacroCommandParameter(WuQMacroDataValueTypeEnum::INTEGER, + "Select button at index", + buttonIndex)); + + createAndSendMacroCommand(params); } @@ -616,8 +654,12 @@ WuQMacroSignalWatcher::buttonGroupButtonClicked(QAbstractButton* button) void WuQMacroSignalWatcher::checkBoxClicked(bool checked) { - createAndSendMacroCommand(checked, - ""); + std::vector params; + params.push_back(new WuQMacroCommandParameter(WuQMacroDataValueTypeEnum::BOOLEAN, + "On/Off", + checked)); + + createAndSendMacroCommand(params); } /** @@ -638,8 +680,15 @@ WuQMacroSignalWatcher::comboBoxActivated(int index) text = comboBox->itemText(index); } - createAndSendMacroCommand(text, - index); + std::vector params; + params.push_back(new WuQMacroCommandParameter(WuQMacroDataValueTypeEnum::STRING, + "Select item with name", + text)); + params.push_back(new WuQMacroCommandParameter(WuQMacroDataValueTypeEnum::INTEGER, + "Select item at index", + index)); + + createAndSendMacroCommand(params); } /** @@ -651,8 +700,12 @@ WuQMacroSignalWatcher::comboBoxActivated(int index) void WuQMacroSignalWatcher::doubleSpinBoxValueChanged(double value) { - createAndSendMacroCommand(value, - ""); + std::vector params; + params.push_back(new WuQMacroCommandParameter(WuQMacroDataValueTypeEnum::FLOAT, + "New value", + value)); + + createAndSendMacroCommand(params); } /** @@ -664,8 +717,12 @@ WuQMacroSignalWatcher::doubleSpinBoxValueChanged(double value) void WuQMacroSignalWatcher::lineEditTextEdited(const QString& text) { - createAndSendMacroCommand(text, - ""); + std::vector params; + params.push_back(new WuQMacroCommandParameter(WuQMacroDataValueTypeEnum::STRING, + "New text", + text)); + + createAndSendMacroCommand(params); } /** @@ -685,8 +742,15 @@ WuQMacroSignalWatcher::listWidgetItemActivated(QListWidgetItem* item) const QString text((item != NULL) ? item->text() : ""); - createAndSendMacroCommand(text, - rowIndex); + std::vector params; + params.push_back(new WuQMacroCommandParameter(WuQMacroDataValueTypeEnum::STRING, + "Select item with name", + text)); + params.push_back(new WuQMacroCommandParameter(WuQMacroDataValueTypeEnum::INTEGER, + "Select item at index", + rowIndex)); + + createAndSendMacroCommand(params); } /** @@ -713,23 +777,52 @@ WuQMacroSignalWatcher::menuTriggered(QAction* action) const QString text((action != NULL) ? action->text() : ""); - createAndSendMacroCommand(text, - actionIndex); + std::vector params; + params.push_back(new WuQMacroCommandParameter(WuQMacroDataValueTypeEnum::STRING, + "Select item with name", + text)); + params.push_back(new WuQMacroCommandParameter(WuQMacroDataValueTypeEnum::INTEGER, + "Select item at index", + actionIndex)); + + createAndSendMacroCommand(params); } /** - * Called when a + * Called when a push button is clicked * * @param checked * New checked status */ void -WuQMacroSignalWatcher::pushButtonClicked(bool checked) +WuQMacroSignalWatcher::pushButtonClicked(bool /*checked*/) { - createAndSendMacroCommand(checked, - ""); + std::vector params; + params.push_back(new WuQMacroCommandParameter(WuQMacroDataValueTypeEnum::NONE, + "Click button", + "")); + + createAndSendMacroCommand(params); } +/** + * Called when a checkable push button is clicked + * + * @param checked + * New checked status + */ +void +WuQMacroSignalWatcher::pushButtonCheckableClicked(bool checked) +{ + std::vector params; + params.push_back(new WuQMacroCommandParameter(WuQMacroDataValueTypeEnum::NONE, + "On/Off", + checked)); + + createAndSendMacroCommand(params); +} + + /** * Called when a radio button is clicked * @@ -737,10 +830,14 @@ WuQMacroSignalWatcher::pushButtonClicked(bool checked) * New checked status */ void -WuQMacroSignalWatcher::radioButtonClicked(bool checked) +WuQMacroSignalWatcher::radioButtonClicked(bool /*checked*/) { - createAndSendMacroCommand(checked, - ""); + std::vector params; + params.push_back(new WuQMacroCommandParameter(WuQMacroDataValueTypeEnum::NONE, + "Select button", + "")); + + createAndSendMacroCommand(params); } /** @@ -752,8 +849,13 @@ WuQMacroSignalWatcher::radioButtonClicked(bool checked) void WuQMacroSignalWatcher::sliderValueChanged(int value) { - createAndSendMacroCommand(value, - ""); + std::vector params; + + params.push_back(new WuQMacroCommandParameter(WuQMacroDataValueTypeEnum::INTEGER, + "Move slider to", + value)); + + createAndSendMacroCommand(params); } /** @@ -765,8 +867,13 @@ WuQMacroSignalWatcher::sliderValueChanged(int value) void WuQMacroSignalWatcher::spinBoxValueChanged(int value) { - createAndSendMacroCommand(value, - ""); + std::vector params; + + params.push_back(new WuQMacroCommandParameter(WuQMacroDataValueTypeEnum::INTEGER, + "Enter value", + value)); + + createAndSendMacroCommand(params); } /** @@ -781,8 +888,17 @@ WuQMacroSignalWatcher::tabBarCurrentChanged(int index) QTabBar* tabBar = qobject_cast(m_object); CaretAssert(tabBar); const QString tabText = tabBar->tabText(index); - createAndSendMacroCommand(tabText, - index); + + std::vector params; + + params.push_back(new WuQMacroCommandParameter(WuQMacroDataValueTypeEnum::STRING, + "Select tab with name", + tabText)); + params.push_back(new WuQMacroCommandParameter(WuQMacroDataValueTypeEnum::INTEGER, + "Select tab at index", + index)); + + createAndSendMacroCommand(params); } /** @@ -797,8 +913,16 @@ WuQMacroSignalWatcher::tabWidgetCurrentChanged(int index) QTabWidget* tabWidget = qobject_cast(m_object); CaretAssert(tabWidget); const QString tabText = tabWidget->tabText(index); - createAndSendMacroCommand(tabText, - index); + + std::vector params; + params.push_back(new WuQMacroCommandParameter(WuQMacroDataValueTypeEnum::STRING, + "Select tab with name", + tabText)); + params.push_back(new WuQMacroCommandParameter(WuQMacroDataValueTypeEnum::INTEGER, + "Select tab at index", + index)); + + createAndSendMacroCommand(params); } /** @@ -808,10 +932,31 @@ WuQMacroSignalWatcher::tabWidgetCurrentChanged(int index) * New check status */ void -WuQMacroSignalWatcher::toolButtonClicked(bool checked) +WuQMacroSignalWatcher::toolButtonClicked(bool /*checked*/) { - createAndSendMacroCommand(checked, - ""); + std::vector params; + params.push_back(new WuQMacroCommandParameter(WuQMacroDataValueTypeEnum::NONE, + "Select button", + "")); + + createAndSendMacroCommand(params); +} + +/** + * Called when a tool button is clicked + * + * @param checked + * New check status + */ +void +WuQMacroSignalWatcher::toolButtonCheckableClicked(bool checked) +{ + std::vector params; + params.push_back(new WuQMacroCommandParameter(WuQMacroDataValueTypeEnum::BOOLEAN, + "On/Off", + checked)); + + createAndSendMacroCommand(params); } /** diff --git a/src/GuiQt/WuQMacroSignalWatcher.h b/src/GuiQt/WuQMacroSignalWatcher.h index 30479b1b4..3cff8e7b4 100644 --- a/src/GuiQt/WuQMacroSignalWatcher.h +++ b/src/GuiQt/WuQMacroSignalWatcher.h @@ -35,6 +35,7 @@ class QListWidgetItem; namespace caret { + class WuQMacroCommandParameter; class WuQMacroManager; class WuQMacroSignalWatcher : public QObject { @@ -61,6 +62,8 @@ namespace caret { private slots: void actionTriggered(bool); + void actionCheckableTriggered(bool); + void actionGroupTriggered(QAction* action); void buttonGroupButtonClicked(QAbstractButton* button); @@ -79,6 +82,8 @@ namespace caret { void pushButtonClicked(bool); + void pushButtonCheckableClicked(bool); + void radioButtonClicked(bool); void sliderValueChanged(int); @@ -91,6 +96,8 @@ namespace caret { void toolButtonClicked(bool); + void toolButtonCheckableClicked(bool); + void objectWasDestroyed(QObject* obj); void objectNameWasChanged(const QString& name); @@ -106,8 +113,7 @@ namespace caret { WuQMacroManager* m_parentMacroManager; - void createAndSendMacroCommand(const QVariant& value, - const QVariant& valueTwo); + void createAndSendMacroCommand(std::vector& parameters); QObject* m_object; -- GitLab From 492988358b4274be057c8f80fa4f25affe6725c9 Mon Sep 17 00:00:00 2001 From: John Harwell Date: Mon, 4 Feb 2019 16:00:00 -0600 Subject: [PATCH 177/427] WB-834 Macros: Extensive updates for macros including more complex commands, some redesign of the XML file, dialog improvements, etc. --- src/Common/CMakeLists.txt | 6 +- src/Common/WuQMacroCommand.cxx | 360 +++++++++++++---- src/Common/WuQMacroCommand.h | 70 +++- src/Common/WuQMacroCommandParameter.cxx | 27 ++ src/Common/WuQMacroCommandParameter.h | 5 + src/Common/WuQMacroCommandTypeEnum.cxx | 377 ++++++++++++++++++ src/Common/WuQMacroCommandTypeEnum.h | 106 +++++ src/Common/WuQMacroDataValueTypeEnum.cxx | 5 +- src/Common/WuQMacroDataValueTypeEnum.h | 2 + src/Common/WuQMacroGroupXmlStreamBase.h | 16 +- src/Common/WuQMacroGroupXmlStreamReader.cxx | 142 +++++-- src/Common/WuQMacroGroupXmlStreamReader.h | 18 +- src/Common/WuQMacroGroupXmlStreamWriter.cxx | 65 +-- ...ypeEnum.cxx => WuQMacroWidgetTypeEnum.cxx} | 150 ++++--- ...assTypeEnum.h => WuQMacroWidgetTypeEnum.h} | 62 ++- src/GuiQt/CMakeLists.txt | 15 +- src/GuiQt/GuiManager.cxx | 12 +- src/GuiQt/WbMacroCustomDataTypeEnum.cxx | 369 +++++++++++++++++ src/GuiQt/WbMacroCustomDataTypeEnum.h | 102 +++++ src/GuiQt/WbMacroCustomOperationManager.cxx | 254 ++++++++++++ src/GuiQt/WbMacroCustomOperationManager.h | 75 ++++ ...croCustomOperationSurfaceInterpolation.cxx | 112 ++++++ ...MacroCustomOperationSurfaceInterpolation.h | 65 +++ src/GuiQt/WbMacroCustomOperationTypeEnum.cxx | 369 +++++++++++++++++ src/GuiQt/WbMacroCustomOperationTypeEnum.h | 102 +++++ .../{GuiMacroHelper.cxx => WbMacroHelper.cxx} | 27 +- .../{GuiMacroHelper.h => WbMacroHelper.h} | 27 +- .../WuQMacroCustomOperationManagerInterface.h | 103 +++++ src/GuiQt/WuQMacroDialog.cxx | 179 ++++++++- src/GuiQt/WuQMacroDialog.h | 4 + src/GuiQt/WuQMacroExecutor.cxx | 120 +++--- src/GuiQt/WuQMacroExecutor.h | 5 +- src/GuiQt/WuQMacroHelperInterface.h | 1 + src/GuiQt/WuQMacroManager.cxx | 152 +++++-- src/GuiQt/WuQMacroManager.h | 22 +- src/GuiQt/WuQMacroSignalWatcher.cxx | 144 ++++--- src/GuiQt/WuQMacroSignalWatcher.h | 6 +- 37 files changed, 3173 insertions(+), 503 deletions(-) create mode 100644 src/Common/WuQMacroCommandTypeEnum.cxx create mode 100644 src/Common/WuQMacroCommandTypeEnum.h rename src/Common/{WuQMacroClassTypeEnum.cxx => WuQMacroWidgetTypeEnum.cxx} (67%) rename src/Common/{WuQMacroClassTypeEnum.h => WuQMacroWidgetTypeEnum.h} (73%) create mode 100644 src/GuiQt/WbMacroCustomDataTypeEnum.cxx create mode 100644 src/GuiQt/WbMacroCustomDataTypeEnum.h create mode 100644 src/GuiQt/WbMacroCustomOperationManager.cxx create mode 100644 src/GuiQt/WbMacroCustomOperationManager.h create mode 100644 src/GuiQt/WbMacroCustomOperationSurfaceInterpolation.cxx create mode 100644 src/GuiQt/WbMacroCustomOperationSurfaceInterpolation.h create mode 100644 src/GuiQt/WbMacroCustomOperationTypeEnum.cxx create mode 100644 src/GuiQt/WbMacroCustomOperationTypeEnum.h rename src/GuiQt/{GuiMacroHelper.cxx => WbMacroHelper.cxx} (85%) rename src/GuiQt/{GuiMacroHelper.h => WbMacroHelper.h} (68%) create mode 100644 src/GuiQt/WuQMacroCustomOperationManagerInterface.h diff --git a/src/Common/CMakeLists.txt b/src/Common/CMakeLists.txt index e1d1dfe75..1032f7cff 100644 --- a/src/Common/CMakeLists.txt +++ b/src/Common/CMakeLists.txt @@ -162,9 +162,9 @@ VectorOperation.h VoxelIJK.h WorkbenchSpecialVersionEnum.h WuQMacro.h -WuQMacroClassTypeEnum.h WuQMacroCommand.h WuQMacroCommandParameter.h +WuQMacroCommandTypeEnum.h WuQMacroDataValueTypeEnum.h WuQMacroFile.h WuQMacroGroup.h @@ -176,6 +176,7 @@ WuQMacroMouseEventInfo.h WuQMacroMouseEventTypeEnum.h WuQMacroShortCutKeyEnum.h WuQMacroStandardItemTypeEnum.h +WuQMacroWidgetTypeEnum.h YokingGroupEnum.h ${MOC_SOURCE_FILES} @@ -278,8 +279,8 @@ Vector3D.cxx VectorOperation.cxx WorkbenchSpecialVersionEnum.cxx WuQMacro.cxx -WuQMacroClassTypeEnum.cxx WuQMacroCommand.cxx +WuQMacroCommandTypeEnum.cxx WuQMacroCommandParameter.cxx WuQMacroDataValueTypeEnum.cxx WuQMacroFile.cxx @@ -292,6 +293,7 @@ WuQMacroMouseEventInfo.cxx WuQMacroMouseEventTypeEnum.cxx WuQMacroShortCutKeyEnum.cxx WuQMacroStandardItemTypeEnum.cxx +WuQMacroWidgetTypeEnum.cxx YokingGroupEnum.cxx ) diff --git a/src/Common/WuQMacroCommand.cxx b/src/Common/WuQMacroCommand.cxx index 5c15ab0f2..99c5b7715 100644 --- a/src/Common/WuQMacroCommand.cxx +++ b/src/Common/WuQMacroCommand.cxx @@ -38,98 +38,238 @@ using namespace caret; */ /** - * Constructor for a macro command, usually a mouse command + * Create a new instance of a macro comand for a custom command * - * @param classType - * Type of object class + * @param commandType + * Type of command + * @param customOperationTypeName + * Name of a custom operation + * @param mouseEventInfo + * Information about mouse event. + * @param widgetType + * Type of widget + * @param version + * Version of command * @param objectName * Name of object * @param objectDescriptiveName * Descriptive name of macro command - */ -WuQMacroCommand::WuQMacroCommand(const WuQMacroClassTypeEnum::Enum classType, - const QString& objectName, - const QString& objectDescriptiveName) -: WuQMacroCommand(classType, - objectName, - objectDescriptiveName, - "") + * @param objectToolTip + * ToolTip for object + * @param delayInSeconds + * Delay in seconds + * @param errorMessageOut + * Output with error messag if new instance fails + * @return + * The widget command or NULL if failure + */ +WuQMacroCommand* +WuQMacroCommand::newInstanceCustomCommand(const QString& customOperationName, + const int32_t version, + const QString& objectName, + const QString& objectDescriptiveName, + const QString& objectToolTip, + const float delayInSeconds, + QString& errorMessageOut) { - if (objectDescriptiveName.isEmpty()) { - CaretLogWarning("Empty descriptive name for " - + objectName); + errorMessageOut.clear(); + + if (customOperationName.isEmpty()) { + errorMessageOut = "Custom operation may not be empty for a custom operation macro command"; + return NULL; } + + WuQMacroMouseEventInfo* mouseEventInfo(NULL); + WuQMacroCommand* mc = new WuQMacroCommand(WuQMacroCommandTypeEnum::CUSTOM_OPERATION, + customOperationName, + mouseEventInfo, + WuQMacroWidgetTypeEnum::INVALID, + version, + objectName, + objectDescriptiveName, + objectToolTip, + delayInSeconds); + return mc; } /** - * Constructor for a macro command + * Create a new instance of a macro command for mouse operation * - * @param classType - * Type of object class + * @param commandType + * Type of command + * @param customOperationTypeName + * Name of a custom operation + * @param mouseEventInfo + * Information about mouse event (will take ownership) + * @param widgetType + * Type of widget + * @param version + * Version of command * @param objectName * Name of object * @param objectDescriptiveName * Descriptive name of macro command * @param objectToolTip * ToolTip for object - */ -WuQMacroCommand::WuQMacroCommand(const WuQMacroClassTypeEnum::Enum classType, - const QString& objectName, - const QString& objectDescriptiveName, - const QString& objectToolTip) -: QStandardItem(), -TracksModificationInterface(), -m_classType(classType), -m_objectName(objectName), -m_descriptiveName(objectDescriptiveName), -m_macroMouseEvent(NULL) + * @param delayInSeconds + * Delay in seconds + * @param errorMessageOut + * Output with error messag if new instance fails + * @return + * The widget command or NULL if failure + */ +WuQMacroCommand* +WuQMacroCommand::newInstanceMouseCommand(WuQMacroMouseEventInfo* mouseEventInfo, + const int32_t version, + const QString& objectName, + const QString& objectDescriptiveName, + const QString& objectToolTip, + const float delayInSeconds, + QString& errorMessageOut) { - if (objectDescriptiveName.isEmpty()) { - CaretLogWarning("Empty descriptive name for " - + objectName); - } - - setFlags(Qt::ItemIsEnabled - | Qt::ItemIsSelectable); - setToolTip(objectToolTip); + errorMessageOut.clear(); + if (mouseEventInfo == NULL) { + errorMessageOut = "Mouse info is invalid (NULL) for a macro mouse command"; + return NULL; + } - updateTitle(); - setModified(); + const QString customOperationName(""); + WuQMacroCommand* mc = new WuQMacroCommand(WuQMacroCommandTypeEnum::MOUSE, + customOperationName, + mouseEventInfo, + WuQMacroWidgetTypeEnum::INVALID, + version, + objectName, + objectDescriptiveName, + objectToolTip, + delayInSeconds); + return mc; } /** - * Constructor for a macro command containing a mouse event + * Create a new instance of a macro command for a Qt Widget * + * @param commandType + * Type of command + * @param customOperationTypeName + * Name of a custom operation + * @param mouseEventInfo + * Information about mouse event. + * @param widgetType + * Type of widget + * @param version + * Version of command * @param objectName * Name of object * @param objectDescriptiveName * Descriptive name of macro command + * @param objectToolTip + * ToolTip for object + * @param delayInSeconds + * Delay in seconds + * @param errorMessageOut + * Output with error messag if new instance fails + * @return + * The widget command or NULL if failure + */ +WuQMacroCommand* +WuQMacroCommand::newInstanceWidgetCommand(const WuQMacroWidgetTypeEnum::Enum widgetType, + const int32_t version, + const QString& objectName, + const QString& objectDescriptiveName, + const QString& objectToolTip, + const float delayInSeconds, + QString& errorMessageOut) +{ + errorMessageOut.clear(); + + if (widgetType == WuQMacroWidgetTypeEnum::INVALID) { + errorMessageOut = "Widget type is invalid for Widget Macro Command"; + return NULL; + } + + const QString customOperationName(""); + WuQMacroMouseEventInfo* mouseEventInfo(NULL); + WuQMacroCommand* mc = new WuQMacroCommand(WuQMacroCommandTypeEnum::WIDGET, + customOperationName, + mouseEventInfo, + widgetType, + version, + objectName, + objectDescriptiveName, + objectToolTip, + delayInSeconds); + return mc; +} + +/** + * Constructor for a macro command + * + * @param commandType + * Type of command + * @param customOperationTypeName + * Name of a custom operation * @param mouseEventInfo * Information about mouse event. - */ -WuQMacroCommand::WuQMacroCommand(const QString& objectName, + * @param widgetType + * Type of widget + * @param version + * Version of command + * @param objectName + * Name of object + * @param objectDescriptiveName + * Descriptive name of macro command + * @param objectToolTip + * ToolTip for object + * @param delayInSeconds + * Delay in seconds + */ +WuQMacroCommand::WuQMacroCommand(const WuQMacroCommandTypeEnum::Enum commandType, + const QString& customOperationTypeName, + WuQMacroMouseEventInfo* mouseEventInfo, + const WuQMacroWidgetTypeEnum::Enum widgetType, + const int32_t version, + const QString& objectName, const QString& objectDescriptiveName, - WuQMacroMouseEventInfo* mouseEventInfo) + const QString& objectToolTip, + const float delayInSeconds) : QStandardItem(), TracksModificationInterface(), -m_classType(WuQMacroClassTypeEnum::MOUSE_USER_EVENT), +m_commandType(commandType), +m_customOperationTypeName(customOperationTypeName), +m_macroMouseEvent(mouseEventInfo), +m_widgetType(widgetType), +m_version(version), m_objectName(objectName), m_descriptiveName(objectDescriptiveName), -m_macroMouseEvent(mouseEventInfo) +m_delayInSeconds(delayInSeconds) { if (objectDescriptiveName.isEmpty()) { CaretLogWarning("Empty descriptive name for " + objectName); } - + + switch (m_commandType) { + case WuQMacroCommandTypeEnum::CUSTOM_OPERATION: + break; + case WuQMacroCommandTypeEnum::MOUSE: + CaretAssert(m_macroMouseEvent); + setText("Mouse"); + break; + case WuQMacroCommandTypeEnum::WIDGET: + break; + } + setFlags(Qt::ItemIsEnabled | Qt::ItemIsSelectable); - setText("Mouse"); - + setToolTip(objectToolTip); + + updateTitle(); setModified(); } + /** * Destructor. */ @@ -193,23 +333,26 @@ WuQMacroCommand::type() const void WuQMacroCommand::copyHelperWuQMacroCommand(const WuQMacroCommand& obj) { - m_descriptiveName = obj.m_descriptiveName; - m_classType = obj.m_classType; - m_objectName = obj.m_objectName; - m_macroMouseEvent = NULL; - m_delayInSeconds = obj.m_delayInSeconds; - + m_commandType = obj.m_commandType; + m_customOperationTypeName = obj.m_customOperationTypeName; + if (m_macroMouseEvent != NULL) { + m_macroMouseEvent = NULL; + } if (obj.m_macroMouseEvent != NULL) { m_macroMouseEvent = new WuQMacroMouseEventInfo(*obj.m_macroMouseEvent); } - + m_widgetType = obj.m_widgetType; + m_version = obj.m_version; + m_objectName = obj.m_objectName; + m_descriptiveName = obj.m_descriptiveName; + m_delayInSeconds = obj.m_delayInSeconds; removeAllParameters(); for (const auto p : obj.m_parameters) { m_parameters.push_back(new WuQMacroCommandParameter(*p)); } - setText(obj.text()); updateTitle(); + setModified(); } /** @@ -232,104 +375,100 @@ WuQMacroCommand::updateTitle() dataValueTwo = m_parameters[1]->getValue(); } - switch (m_classType) { - case WuQMacroClassTypeEnum::ACTION: + switch (m_widgetType) { + case WuQMacroWidgetTypeEnum::ACTION: title = ("Turn " + QString((dataValue.toBool() ? "On" : "Off"))); break; - case WuQMacroClassTypeEnum::ACTION_CHECKABLE: + case WuQMacroWidgetTypeEnum::ACTION_CHECKABLE: title = ("Turn " + QString((dataValue.toBool() ? "On" : "Off"))); break; - case WuQMacroClassTypeEnum::ACTION_GROUP: + case WuQMacroWidgetTypeEnum::ACTION_GROUP: title = ("Select Name " + dataValue.toString() + " else " + " index " + QString::number(dataValueTwo.toInt())); break; - case WuQMacroClassTypeEnum::BUTTON_GROUP: + case WuQMacroWidgetTypeEnum::BUTTON_GROUP: title = ("Select Name " + dataValue.toString() + " else " + " index " + QString::number(dataValueTwo.toInt())); break; - case WuQMacroClassTypeEnum::CHECK_BOX: + case WuQMacroWidgetTypeEnum::CHECK_BOX: title = ("Turn " + QString((dataValue.toBool() ? "On" : "Off"))); break; - case WuQMacroClassTypeEnum::COMBO_BOX: + case WuQMacroWidgetTypeEnum::COMBO_BOX: title = ("Select Name " + dataValue.toString() + " else " + " index " + QString::number(dataValueTwo.toInt())); break; - case WuQMacroClassTypeEnum::DOUBLE_SPIN_BOX: + case WuQMacroWidgetTypeEnum::DOUBLE_SPIN_BOX: title = ("Set to value " + QString::number(dataValue.toFloat())); break; - case WuQMacroClassTypeEnum::INVALID: - title = ""; + case WuQMacroWidgetTypeEnum::INVALID: break; - case WuQMacroClassTypeEnum::LINE_EDIT: + case WuQMacroWidgetTypeEnum::LINE_EDIT: title = ("Set to text " + dataValue.toString()); break; - case WuQMacroClassTypeEnum::LIST_WIDGET: + case WuQMacroWidgetTypeEnum::LIST_WIDGET: title = ("Select Name " + dataValue.toString() + " else " + " index " + QString::number(dataValueTwo.toInt())); break; - case WuQMacroClassTypeEnum::MENU: + case WuQMacroWidgetTypeEnum::MENU: title = ("Select Name " + dataValue.toString() + " else " + " index " + QString::number(dataValueTwo.toInt())); break; - case WuQMacroClassTypeEnum::MOUSE_USER_EVENT: - title = "MouseEvent"; - break; - case WuQMacroClassTypeEnum::PUSH_BUTTON: + case WuQMacroWidgetTypeEnum::PUSH_BUTTON: title = ("Click Button"); break; - case WuQMacroClassTypeEnum::PUSH_BUTTON_CHECKABLE: + case WuQMacroWidgetTypeEnum::PUSH_BUTTON_CHECKABLE: title = ("Turn " + QString((dataValue.toBool() ? "On" : "Off"))); break; - case WuQMacroClassTypeEnum::RADIO_BUTTON: + case WuQMacroWidgetTypeEnum::RADIO_BUTTON: title = "Click Button"; break; - case WuQMacroClassTypeEnum::SLIDER: + case WuQMacroWidgetTypeEnum::SLIDER: title = ("Move to " + AString::number(dataValue.toInt())); break; - case WuQMacroClassTypeEnum::SPIN_BOX: + case WuQMacroWidgetTypeEnum::SPIN_BOX: title = ("Set to " + AString::number(dataValue.toInt())); break; - case WuQMacroClassTypeEnum::TAB_BAR: + case WuQMacroWidgetTypeEnum::TAB_BAR: title = ("Select Name " + dataValue.toString() + " else " + " index " + QString::number(dataValueTwo.toInt())); break; - case WuQMacroClassTypeEnum::TAB_WIDGET: + case WuQMacroWidgetTypeEnum::TAB_WIDGET: title = ("Select Name " + dataValue.toString() + " else " + " index " + QString::number(dataValueTwo.toInt())); break; - case WuQMacroClassTypeEnum::TOOL_BUTTON: + case WuQMacroWidgetTypeEnum::TOOL_BUTTON: title = ("Click Button"); break; - case WuQMacroClassTypeEnum::TOOL_BUTTON_CHECKABLE: + case WuQMacroWidgetTypeEnum::TOOL_BUTTON_CHECKABLE: title = ("Turn " + QString((dataValue.toBool() ? "On" : "Off"))); break; @@ -346,12 +485,30 @@ WuQMacroCommand::updateTitle() } /** - * @return The object' class type + * @return The command type */ -WuQMacroClassTypeEnum::Enum -WuQMacroCommand::getClassType() const +WuQMacroCommandTypeEnum::Enum +WuQMacroCommand::getCommandType() const { - return m_classType; + return m_commandType; +} + +/** + * @return The command's widget type + */ +WuQMacroWidgetTypeEnum::Enum +WuQMacroCommand::getWidgetType() const +{ + return m_widgetType; +} + +/** + * @return Version of the command + */ +int32_t +WuQMacroCommand::getVersion() const +{ + return m_version; } /** @@ -550,8 +707,8 @@ WuQMacroCommand::setMouseEventInfo(WuQMacroMouseEventInfo* mouseEventInfo) bool WuQMacroCommand::isMouseEventMatch(const WuQMacroCommand* command) const { - if (getClassType() == WuQMacroClassTypeEnum::MOUSE_USER_EVENT) { - if (command->getClassType() == WuQMacroClassTypeEnum::MOUSE_USER_EVENT) { + if (command->getCommandType() == WuQMacroCommandTypeEnum::MOUSE) { + if (getCommandType() == WuQMacroCommandTypeEnum::MOUSE) { const WuQMacroMouseEventInfo* myMouse = getMouseEventInfo(); CaretAssert(myMouse); const WuQMacroMouseEventInfo* otherMouse = command->getMouseEventInfo(); @@ -596,6 +753,32 @@ WuQMacroCommand::setDelayInSeconds(const float seconds) } } +/** + * @return The custom operation command type name + * Used when class type is WuQMacroWidgetTypeEnum::CUSTOM_OPERATION + */ +QString +WuQMacroCommand::getCustomOperationTypeName() const +{ + return m_customOperationTypeName; +} + +/** + * Set the custom operation command type name + * Used when class type is WuQMacroWidgetTypeEnum::CUSTOM_OPERATION + * + * @param customOperationCommandTypeName + * New value + */ +void +WuQMacroCommand::setCustomOperationTypeName(const QString& customOperationTypeName) +{ + if (m_customOperationTypeName != customOperationTypeName) { + m_customOperationTypeName = customOperationTypeName; + setModified(); + } +} + /** * @return True if this instance is modified */ @@ -644,10 +827,11 @@ WuQMacroCommand::setModified() AString WuQMacroCommand::toString() const { - QString s("WuQMacroCommand text=%1 name=%2, type=%3"); + QString s("WuQMacroCommand text=%1 name=%2, commandType=%3, widgetType=%4"); s = s.arg(text() ).arg(m_objectName - ).arg(WuQMacroClassTypeEnum::toGuiName(m_classType)); + ).arg(WuQMacroCommandTypeEnum::toName(m_commandType) + ).arg(WuQMacroWidgetTypeEnum::toName(m_widgetType)); for (const auto p : m_parameters) { s.append(", value=" + p->getValue().toString()); diff --git a/src/Common/WuQMacroCommand.h b/src/Common/WuQMacroCommand.h index d045a2e88..cc4b8e8b4 100644 --- a/src/Common/WuQMacroCommand.h +++ b/src/Common/WuQMacroCommand.h @@ -29,8 +29,9 @@ #include #include "TracksModificationInterface.h" +#include "WuQMacroCommandTypeEnum.h" #include "WuQMacroDataValueTypeEnum.h" -#include "WuQMacroClassTypeEnum.h" +#include "WuQMacroWidgetTypeEnum.h" class QObject; @@ -43,18 +44,39 @@ namespace caret { public: - WuQMacroCommand(const WuQMacroClassTypeEnum::Enum objectClassType, + static WuQMacroCommand* newInstanceCustomCommand(const QString& customOperationName, + const int32_t version, + const QString& objectName, + const QString& objectDescriptiveName, + const QString& objectToolTip, + const float delay, + QString& errorMessageOut); + + static WuQMacroCommand* newInstanceMouseCommand(WuQMacroMouseEventInfo* mouseEventInfo, + const int32_t version, + const QString& objectName, + const QString& objectDescriptiveName, + const QString& objectToolTip, + const float delay, + QString& errorMessageOut); + + static WuQMacroCommand* newInstanceWidgetCommand(const WuQMacroWidgetTypeEnum::Enum widgetType, + const int32_t version, + const QString& objectName, + const QString& objectDescriptiveName, + const QString& objectToolTip, + const float delay, + QString& errorMessageOut); + + WuQMacroCommand(const WuQMacroCommandTypeEnum::Enum commandType, + const QString& customOperationTypeName, + WuQMacroMouseEventInfo* mouseEventInfo, + const WuQMacroWidgetTypeEnum::Enum widgetType, + const int32_t version, const QString& objectName, const QString& objectDescriptiveName, - const QString& objectToolTip); - - WuQMacroCommand(const WuQMacroClassTypeEnum::Enum objectClassType, - const QString& objectName, - const QString& objectDescriptiveName); - - WuQMacroCommand(const QString& objectName, - const QString& objectDescriptiveName, - WuQMacroMouseEventInfo* mouseEventInfo); + const QString& objectToolTip, + const float delayValue); virtual ~WuQMacroCommand(); @@ -62,7 +84,11 @@ namespace caret { WuQMacroCommand& operator=(const WuQMacroCommand& obj); - WuQMacroClassTypeEnum::Enum getClassType() const; + WuQMacroCommandTypeEnum::Enum getCommandType() const; + + WuQMacroWidgetTypeEnum::Enum getWidgetType() const; + + int32_t getVersion() const; QString getObjectName() const; @@ -96,6 +122,10 @@ namespace caret { void setDelayInSeconds(const float seconds); + QString getCustomOperationTypeName() const; + + void setCustomOperationTypeName(const QString& customOperationTypeName); + // ADD_NEW_METHODS_HERE virtual bool isModified() const override; @@ -115,18 +145,24 @@ namespace caret { void removeAllParameters(); - WuQMacroClassTypeEnum::Enum m_classType; + WuQMacroCommandTypeEnum::Enum m_commandType; - QString m_objectName; + QString m_customOperationTypeName; - QString m_descriptiveName; + WuQMacroMouseEventInfo* m_macroMouseEvent; - std::vector m_parameters; + WuQMacroWidgetTypeEnum::Enum m_widgetType; - WuQMacroMouseEventInfo* m_macroMouseEvent; + int32_t m_version; + + QString m_objectName; + + QString m_descriptiveName; float m_delayInSeconds = 1.0f; + std::vector m_parameters; + bool m_modifiedStatusFlag = false; // ADD_NEW_MEMBERS_HERE diff --git a/src/Common/WuQMacroCommandParameter.cxx b/src/Common/WuQMacroCommandParameter.cxx index 54ce1365e..f6986a8e2 100644 --- a/src/Common/WuQMacroCommandParameter.cxx +++ b/src/Common/WuQMacroCommandParameter.cxx @@ -110,6 +110,7 @@ WuQMacroCommandParameter::copyHelperWuQMacroCommandParameter(const WuQMacroComma { m_dataType = obj.m_dataType; m_name = obj.m_name; + m_customDataType = obj.m_customDataType; m_value = obj.m_value; setModified(); } @@ -156,3 +157,29 @@ WuQMacroCommandParameter::setValue(const QVariant& value) } } +/** + * @return The custom type data type that defines the data when + * getDataType() is CUSTOM_DATA + */ +QString +WuQMacroCommandParameter::getCustomDataType() const +{ + return m_customDataType; +} + +/** + * Set the custom type data type that defines the data when + * getDataType() is CUSTOM_DATA + * + * @param customDataType + * The custom data type + */ +void +WuQMacroCommandParameter::setCustomDataType(const QString& customDataType) +{ + if (m_customDataType != customDataType) { + m_customDataType = customDataType; + setModified(); + } +} + diff --git a/src/Common/WuQMacroCommandParameter.h b/src/Common/WuQMacroCommandParameter.h index b5ca850b4..8f95be048 100644 --- a/src/Common/WuQMacroCommandParameter.h +++ b/src/Common/WuQMacroCommandParameter.h @@ -54,7 +54,10 @@ namespace caret { void setValue(const QVariant& value); + QString getCustomDataType() const; + void setCustomDataType(const QString& userDataType); + // ADD_NEW_METHODS_HERE private: @@ -64,6 +67,8 @@ namespace caret { QString m_name; + QString m_customDataType; + QVariant m_value; diff --git a/src/Common/WuQMacroCommandTypeEnum.cxx b/src/Common/WuQMacroCommandTypeEnum.cxx new file mode 100644 index 000000000..6254b3b54 --- /dev/null +++ b/src/Common/WuQMacroCommandTypeEnum.cxx @@ -0,0 +1,377 @@ + +/*LICENSE_START*/ +/* + * Copyright (C) 2019 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + +#include +#define __WU_Q_MACRO_COMMAND_TYPE_ENUM_DECLARE__ +#include "WuQMacroCommandTypeEnum.h" +#undef __WU_Q_MACRO_COMMAND_TYPE_ENUM_DECLARE__ + +#include "CaretAssert.h" + +using namespace caret; + + +/** + * \class caret::WuQMacroCommandTypeEnum + * \brief Enumerated type for command type in a WuQMacroCommand + * + * Using this enumerated type in the GUI with an EnumComboBoxTemplate + * + * Header File (.h) + * Forward declare the data type: + * class EnumComboBoxTemplate; + * + * Declare the member: + * EnumComboBoxTemplate* m_wuQMacroCommandTypeEnumComboBox; + * + * Declare a slot that is called when user changes selection + * private slots: + * void wuQMacroCommandTypeEnumComboBoxItemActivated(); + * + * Implementation File (.cxx) + * Include the header files + * #include "EnumComboBoxTemplate.h" + * #include "WuQMacroCommandTypeEnum.h" + * + * Instatiate: + * m_wuQMacroCommandTypeEnumComboBox = new EnumComboBoxTemplate(this); + * m_wuQMacroCommandTypeEnumComboBox->setup(); + * + * Get notified when the user changes the selection: + * QObject::connect(m_wuQMacroCommandTypeEnumComboBox, SIGNAL(itemActivated()), + * this, SLOT(wuQMacroCommandTypeEnumComboBoxItemActivated())); + * + * Update the selection: + * m_wuQMacroCommandTypeEnumComboBox->setSelectedItem(NEW_VALUE); + * + * Read the selection: + * const WuQMacroCommandTypeEnum::Enum VARIABLE = m_wuQMacroCommandTypeEnumComboBox->getSelectedItem(); + * + */ + +/** + * Constructor. + * + * @param enumValue + * An enumerated value. + * @param name + * Name of enumerated value. + * + * @param guiName + * User-friendly name for use in user-interface. + */ +WuQMacroCommandTypeEnum::WuQMacroCommandTypeEnum(const Enum enumValue, + const AString& name, + const AString& guiName) +{ + this->enumValue = enumValue; + this->integerCode = integerCodeCounter++; + this->name = name; + this->guiName = guiName; +} + +/** + * Destructor. + */ +WuQMacroCommandTypeEnum::~WuQMacroCommandTypeEnum() +{ +} + +/** + * Initialize the enumerated metadata. + */ +void +WuQMacroCommandTypeEnum::initialize() +{ + if (initializedFlag) { + return; + } + initializedFlag = true; + + enumData.push_back(WuQMacroCommandTypeEnum(CUSTOM_OPERATION, + "CUSTOM_OPERATION", + "CustomOperation")); + + enumData.push_back(WuQMacroCommandTypeEnum(MOUSE, + "MOUSE", + "Mouse")); + + enumData.push_back(WuQMacroCommandTypeEnum(WIDGET, + "WIDGET", + "Widget")); + +} + +/** + * Find the data for and enumerated value. + * @param enumValue + * The enumerated value. + * @return Pointer to data for this enumerated type + * or NULL if no data for type or if type is invalid. + */ +const WuQMacroCommandTypeEnum* +WuQMacroCommandTypeEnum::findData(const Enum enumValue) +{ + if (initializedFlag == false) initialize(); + + size_t num = enumData.size(); + for (size_t i = 0; i < num; i++) { + const WuQMacroCommandTypeEnum* d = &enumData[i]; + if (d->enumValue == enumValue) { + return d; + } + } + + return NULL; +} + +/** + * Get a string representation of the enumerated type. + * @param enumValue + * Enumerated value. + * @return + * String representing enumerated value. + */ +AString +WuQMacroCommandTypeEnum::toName(Enum enumValue) { + if (initializedFlag == false) initialize(); + + const WuQMacroCommandTypeEnum* enumInstance = findData(enumValue); + return enumInstance->name; +} + +/** + * Get an enumerated value corresponding to its name. + * @param name + * Name of enumerated value. + * @param isValidOut + * If not NULL, it is set indicating that a + * enum value exists for the input name. + * @return + * Enumerated value. + */ +WuQMacroCommandTypeEnum::Enum +WuQMacroCommandTypeEnum::fromName(const AString& name, bool* isValidOut) +{ + if (initializedFlag == false) initialize(); + + bool validFlag = false; + Enum enumValue = WuQMacroCommandTypeEnum::enumData[0].enumValue; + + for (std::vector::iterator iter = enumData.begin(); + iter != enumData.end(); + iter++) { + const WuQMacroCommandTypeEnum& d = *iter; + if (d.name == name) { + enumValue = d.enumValue; + validFlag = true; + break; + } + } + + if (isValidOut != 0) { + *isValidOut = validFlag; + } + else if (validFlag == false) { + CaretAssertMessage(0, AString("Name " + name + "failed to match enumerated value for type WuQMacroCommandTypeEnum")); + } + return enumValue; +} + +/** + * Get a GUI string representation of the enumerated type. + * @param enumValue + * Enumerated value. + * @return + * String representing enumerated value. + */ +AString +WuQMacroCommandTypeEnum::toGuiName(Enum enumValue) { + if (initializedFlag == false) initialize(); + + const WuQMacroCommandTypeEnum* enumInstance = findData(enumValue); + return enumInstance->guiName; +} + +/** + * Get an enumerated value corresponding to its GUI name. + * @param s + * Name of enumerated value. + * @param isValidOut + * If not NULL, it is set indicating that a + * enum value exists for the input name. + * @return + * Enumerated value. + */ +WuQMacroCommandTypeEnum::Enum +WuQMacroCommandTypeEnum::fromGuiName(const AString& guiName, bool* isValidOut) +{ + if (initializedFlag == false) initialize(); + + bool validFlag = false; + Enum enumValue = WuQMacroCommandTypeEnum::enumData[0].enumValue; + + for (std::vector::iterator iter = enumData.begin(); + iter != enumData.end(); + iter++) { + const WuQMacroCommandTypeEnum& d = *iter; + if (d.guiName == guiName) { + enumValue = d.enumValue; + validFlag = true; + break; + } + } + + if (isValidOut != 0) { + *isValidOut = validFlag; + } + else if (validFlag == false) { + CaretAssertMessage(0, AString("guiName " + guiName + "failed to match enumerated value for type WuQMacroCommandTypeEnum")); + } + return enumValue; +} + +/** + * Get the integer code for a data type. + * + * @return + * Integer code for data type. + */ +int32_t +WuQMacroCommandTypeEnum::toIntegerCode(Enum enumValue) +{ + if (initializedFlag == false) initialize(); + const WuQMacroCommandTypeEnum* enumInstance = findData(enumValue); + return enumInstance->integerCode; +} + +/** + * Find the data type corresponding to an integer code. + * + * @param integerCode + * Integer code for enum. + * @param isValidOut + * If not NULL, on exit isValidOut will indicate if + * integer code is valid. + * @return + * Enum for integer code. + */ +WuQMacroCommandTypeEnum::Enum +WuQMacroCommandTypeEnum::fromIntegerCode(const int32_t integerCode, bool* isValidOut) +{ + if (initializedFlag == false) initialize(); + + bool validFlag = false; + Enum enumValue = WuQMacroCommandTypeEnum::enumData[0].enumValue; + + for (std::vector::iterator iter = enumData.begin(); + iter != enumData.end(); + iter++) { + const WuQMacroCommandTypeEnum& enumInstance = *iter; + if (enumInstance.integerCode == integerCode) { + enumValue = enumInstance.enumValue; + validFlag = true; + break; + } + } + + if (isValidOut != 0) { + *isValidOut = validFlag; + } + else if (validFlag == false) { + CaretAssertMessage(0, AString("Integer code " + AString::number(integerCode) + "failed to match enumerated value for type WuQMacroCommandTypeEnum")); + } + return enumValue; +} + +/** + * Get all of the enumerated type values. The values can be used + * as parameters to toXXX() methods to get associated metadata. + * + * @param allEnums + * A vector that is OUTPUT containing all of the enumerated values. + */ +void +WuQMacroCommandTypeEnum::getAllEnums(std::vector& allEnums) +{ + if (initializedFlag == false) initialize(); + + allEnums.clear(); + + for (std::vector::iterator iter = enumData.begin(); + iter != enumData.end(); + iter++) { + allEnums.push_back(iter->enumValue); + } +} + +/** + * Get all of the names of the enumerated type values. + * + * @param allNames + * A vector that is OUTPUT containing all of the names of the enumerated values. + * @param isSorted + * If true, the names are sorted in alphabetical order. + */ +void +WuQMacroCommandTypeEnum::getAllNames(std::vector& allNames, const bool isSorted) +{ + if (initializedFlag == false) initialize(); + + allNames.clear(); + + for (std::vector::iterator iter = enumData.begin(); + iter != enumData.end(); + iter++) { + allNames.push_back(WuQMacroCommandTypeEnum::toName(iter->enumValue)); + } + + if (isSorted) { + std::sort(allNames.begin(), allNames.end()); + } +} + +/** + * Get all of the GUI names of the enumerated type values. + * + * @param allNames + * A vector that is OUTPUT containing all of the GUI names of the enumerated values. + * @param isSorted + * If true, the names are sorted in alphabetical order. + */ +void +WuQMacroCommandTypeEnum::getAllGuiNames(std::vector& allGuiNames, const bool isSorted) +{ + if (initializedFlag == false) initialize(); + + allGuiNames.clear(); + + for (std::vector::iterator iter = enumData.begin(); + iter != enumData.end(); + iter++) { + allGuiNames.push_back(WuQMacroCommandTypeEnum::toGuiName(iter->enumValue)); + } + + if (isSorted) { + std::sort(allGuiNames.begin(), allGuiNames.end()); + } +} + diff --git a/src/Common/WuQMacroCommandTypeEnum.h b/src/Common/WuQMacroCommandTypeEnum.h new file mode 100644 index 000000000..6d51b5ddd --- /dev/null +++ b/src/Common/WuQMacroCommandTypeEnum.h @@ -0,0 +1,106 @@ +#ifndef __WU_Q_MACRO_COMMAND_TYPE_ENUM_H__ +#define __WU_Q_MACRO_COMMAND_TYPE_ENUM_H__ + +/*LICENSE_START*/ +/* + * Copyright (C) 2019 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + + +#include +#include +#include "AString.h" + +namespace caret { + +class WuQMacroCommandTypeEnum { + +public: + /** + * Enumerated values. + */ + enum Enum { + /** Custom operation */ + CUSTOM_OPERATION, + /** Mouse Operation */ + MOUSE, + /** Widget or Action Signal/Slot */ + WIDGET + }; + + + ~WuQMacroCommandTypeEnum(); + + static AString toName(Enum enumValue); + + static Enum fromName(const AString& name, bool* isValidOut); + + static AString toGuiName(Enum enumValue); + + static Enum fromGuiName(const AString& guiName, bool* isValidOut); + + static int32_t toIntegerCode(Enum enumValue); + + static Enum fromIntegerCode(const int32_t integerCode, bool* isValidOut); + + static void getAllEnums(std::vector& allEnums); + + static void getAllNames(std::vector& allNames, const bool isSorted); + + static void getAllGuiNames(std::vector& allGuiNames, const bool isSorted); + +private: + WuQMacroCommandTypeEnum(const Enum enumValue, + const AString& name, + const AString& guiName); + + static const WuQMacroCommandTypeEnum* findData(const Enum enumValue); + + /** Holds all instance of enum values and associated metadata */ + static std::vector enumData; + + /** Initialize instances that contain the enum values and metadata */ + static void initialize(); + + /** Indicates instance of enum values and metadata have been initialized */ + static bool initializedFlag; + + /** Auto generated integer codes */ + static int32_t integerCodeCounter; + + /** The enumerated type value for an instance */ + Enum enumValue; + + /** The integer code associated with an enumerated value */ + int32_t integerCode; + + /** The name, a text string that is identical to the enumerated value */ + AString name; + + /** A user-friendly name that is displayed in the GUI */ + AString guiName; +}; + +#ifdef __WU_Q_MACRO_COMMAND_TYPE_ENUM_DECLARE__ +std::vector WuQMacroCommandTypeEnum::enumData; +bool WuQMacroCommandTypeEnum::initializedFlag = false; +int32_t WuQMacroCommandTypeEnum::integerCodeCounter = 0; +#endif // __WU_Q_MACRO_COMMAND_TYPE_ENUM_DECLARE__ + +} // namespace +#endif //__WU_Q_MACRO_COMMAND_TYPE_ENUM_H__ diff --git a/src/Common/WuQMacroDataValueTypeEnum.cxx b/src/Common/WuQMacroDataValueTypeEnum.cxx index c7790efe5..caa09e907 100644 --- a/src/Common/WuQMacroDataValueTypeEnum.cxx +++ b/src/Common/WuQMacroDataValueTypeEnum.cxx @@ -114,6 +114,10 @@ WuQMacroDataValueTypeEnum::initialize() "BOOLEAN", "Boolean")); + enumData.push_back(WuQMacroDataValueTypeEnum(CUSTOM_DATA, + "CUSTOM_DATA", + "CustomData")); + enumData.push_back(WuQMacroDataValueTypeEnum(FLOAT, "FLOAT", "Float")); @@ -133,7 +137,6 @@ WuQMacroDataValueTypeEnum::initialize() enumData.push_back(WuQMacroDataValueTypeEnum(STRING, "STRING", "String")); - } /** diff --git a/src/Common/WuQMacroDataValueTypeEnum.h b/src/Common/WuQMacroDataValueTypeEnum.h index f6be45165..f061d83e4 100644 --- a/src/Common/WuQMacroDataValueTypeEnum.h +++ b/src/Common/WuQMacroDataValueTypeEnum.h @@ -39,6 +39,8 @@ public: INVALID, /** Boolean */ BOOLEAN, + /** Custom data that is not a fundamental type used with Custom Operations */ + CUSTOM_DATA, /** Float */ FLOAT, /** Integer */ diff --git a/src/Common/WuQMacroGroupXmlStreamBase.h b/src/Common/WuQMacroGroupXmlStreamBase.h index 19ef2a32d..e39c21fda 100644 --- a/src/Common/WuQMacroGroupXmlStreamBase.h +++ b/src/Common/WuQMacroGroupXmlStreamBase.h @@ -48,14 +48,16 @@ namespace caret { virtual AString toString() const; + static const QString ATTRIBUTE_COMMAND_TYPE; static const QString ATTRIBUTE_NAME; - static const QString ATTRIBUTE_OBJECT_CLASS; static const QString ATTRIBUTE_OBJECT_DESCRIPTIVE_NAME; static const QString ATTRIBUTE_DELAY; static const QString ATTRIBUTE_SHORT_CUT_KEY; static const QString ATTRIBUTE_UNIQUE_IDENTIFIER; + static const QString ATTRIBUTE_CUSTOM_OPERATION_TYPE_NAME; static const QString ATTRIBUTE_VERSION; - + static const QString ATTRIBUTE_WIDGET_TYPE; + static const QString ATTRIBUTE_MOUSE_BUTTON; static const QString ATTRIBUTE_MOUSE_BUTTONS_MASK; static const QString ATTRIBUTE_MOUSE_EVENT_TYPE; @@ -71,6 +73,7 @@ namespace caret { static const QString ATTRIBUTE_MACRO_COMMAND_PARAMETER_DATA_TYPE; static const QString ATTRIBUTE_MACRO_COMMAND_PARAMETER_NAME; + static const QString ATTRIBUTE_MACRO_COMMAND_PARAMETER_CUSTOM_DATA_TYPE; static const QString ATTRIBUTE_MACRO_COMMAND_PARAMETER_VALUE; static const QString ELEMENT_DESCRIPTION; @@ -91,14 +94,16 @@ namespace caret { }; #ifdef __WU_Q_MACRO_GROUP_XML_STREAM_BASE_DECLARE__ + const QString WuQMacroGroupXmlStreamBase::ATTRIBUTE_COMMAND_TYPE = "CommandType"; const QString WuQMacroGroupXmlStreamBase::ATTRIBUTE_NAME = "Name"; - const QString WuQMacroGroupXmlStreamBase::ATTRIBUTE_OBJECT_CLASS = "ObjectClass"; - const QString WuQMacroGroupXmlStreamBase::ATTRIBUTE_OBJECT_DESCRIPTIVE_NAME = "DescriptiveName"; + const QString WuQMacroGroupXmlStreamBase::ATTRIBUTE_CUSTOM_OPERATION_TYPE_NAME = "CustomOperationTypeName"; const QString WuQMacroGroupXmlStreamBase::ATTRIBUTE_DELAY = "Delay"; + const QString WuQMacroGroupXmlStreamBase::ATTRIBUTE_OBJECT_DESCRIPTIVE_NAME = "DescriptiveName"; const QString WuQMacroGroupXmlStreamBase::ATTRIBUTE_SHORT_CUT_KEY = "ShortCutKey"; const QString WuQMacroGroupXmlStreamBase::ATTRIBUTE_UNIQUE_IDENTIFIER = "UniqueIdentifier"; const QString WuQMacroGroupXmlStreamBase::ATTRIBUTE_VERSION = "Version"; - + const QString WuQMacroGroupXmlStreamBase::ATTRIBUTE_WIDGET_TYPE = "WidgetType"; + const QString WuQMacroGroupXmlStreamBase::ATTRIBUTE_MOUSE_BUTTON = "MouseButton"; const QString WuQMacroGroupXmlStreamBase::ATTRIBUTE_MOUSE_BUTTONS_MASK = "MouseButtonsMask"; const QString WuQMacroGroupXmlStreamBase::ATTRIBUTE_MOUSE_EVENT_TYPE = "MouseEventType"; @@ -113,6 +118,7 @@ namespace caret { const QString WuQMacroGroupXmlStreamBase::ATTRIBUTE_MOUSE_WINDOW_Y = "WindowY"; const QString WuQMacroGroupXmlStreamBase::ATTRIBUTE_MACRO_COMMAND_PARAMETER_DATA_TYPE = "DataType"; const QString WuQMacroGroupXmlStreamBase::ATTRIBUTE_MACRO_COMMAND_PARAMETER_NAME = "Name"; + const QString WuQMacroGroupXmlStreamBase::ATTRIBUTE_MACRO_COMMAND_PARAMETER_CUSTOM_DATA_TYPE = "CustomDataType"; const QString WuQMacroGroupXmlStreamBase::ATTRIBUTE_MACRO_COMMAND_PARAMETER_VALUE = "Value"; const QString WuQMacroGroupXmlStreamBase::ELEMENT_DESCRIPTION = "Description"; diff --git a/src/Common/WuQMacroGroupXmlStreamReader.cxx b/src/Common/WuQMacroGroupXmlStreamReader.cxx index c103e2cf3..b61f942a2 100644 --- a/src/Common/WuQMacroGroupXmlStreamReader.cxx +++ b/src/Common/WuQMacroGroupXmlStreamReader.cxx @@ -321,9 +321,10 @@ WuQMacroGroupXmlStreamReader::readMacroVersionOne(QXmlStreamReader& xmlReader) WuQMacroCommand* WuQMacroGroupXmlStreamReader::readMacroCommandVersionOne(QXmlStreamReader& xmlReader) { - WuQMacroCommand* command = readMacroCommandAttributesVersionOne(xmlReader); + WuQMacroCommand* macroCommand(NULL); + std::unique_ptr commandContent(readMacroCommandAttributesVersionOne(xmlReader)); - if (command != NULL) { + if (commandContent != NULL) { /* * Gets set when ending scene info directory element is read */ @@ -338,17 +339,17 @@ WuQMacroGroupXmlStreamReader::readMacroCommandVersionOne(QXmlStreamReader& xmlRe if (elementName == ELEMENT_MACRO_COMMAND_MOUSE_EVENT_INFO) { WuQMacroMouseEventInfo* mouseEventInfo = readMacroMouseEventInfo(xmlReader); if (mouseEventInfo != NULL) { - command->setMouseEventInfo(mouseEventInfo); + commandContent->m_mouseInfo = mouseEventInfo; } } else if (elementName == ELEMENT_MACRO_COMMAND_TOOL_TIP) { const QString text = xmlReader.readElementText(); - command->setToolTip(text); + commandContent->m_toolTip = text; } else if (elementName == ELEMENT_MACRO_COMMAND_PARAMETER) { WuQMacroCommandParameter* parameter = readMacroCommandParameter(xmlReader); if (parameter != NULL) { - command->addParameter(parameter); + commandContent->m_parameters.push_back(parameter); } } else { @@ -362,6 +363,47 @@ WuQMacroGroupXmlStreamReader::readMacroCommandVersionOne(QXmlStreamReader& xmlRe else if (xmlReader.isEndElement()) { if (xmlReader.name() == ELEMENT_MACRO_COMMAND) { endElementFound = true; + + QString errorMessage; + switch (commandContent->m_commandType) { + case WuQMacroCommandTypeEnum::CUSTOM_OPERATION: + macroCommand = WuQMacroCommand::newInstanceCustomCommand(commandContent->m_customOperationTypeName, + commandContent->m_version, + commandContent->m_objectName, + commandContent->m_descriptiveName, + commandContent->m_toolTip, + commandContent->m_delay, + errorMessage); + break; + case WuQMacroCommandTypeEnum::MOUSE: + macroCommand = WuQMacroCommand::newInstanceMouseCommand(commandContent->m_mouseInfo, + commandContent->m_version, + commandContent->m_objectName, + commandContent->m_descriptiveName, + commandContent->m_toolTip, + commandContent->m_delay, + errorMessage); + break; + case WuQMacroCommandTypeEnum::WIDGET: + macroCommand = WuQMacroCommand::newInstanceWidgetCommand(commandContent->m_widgetType, + commandContent->m_version, + commandContent->m_objectName, + commandContent->m_descriptiveName, + commandContent->m_toolTip, + commandContent->m_delay, + errorMessage); + break; + } + + if (macroCommand != NULL) { + for (auto p : commandContent->m_parameters) { + macroCommand->addParameter(p); + } + } + else { + CaretLogSevere("Error reading macro due to error: " + + errorMessage); + } } } } @@ -370,7 +412,7 @@ WuQMacroGroupXmlStreamReader::readMacroCommandVersionOne(QXmlStreamReader& xmlRe xmlReader.skipCurrentElement(); } - return command; + return macroCommand; } /** @@ -385,9 +427,10 @@ WuQMacroGroupXmlStreamReader::readMacroCommandParameter(QXmlStreamReader& xmlRea { const QXmlStreamAttributes attributes = xmlReader.attributes(); const QStringRef dataTypeString = attributes.value(ATTRIBUTE_MACRO_COMMAND_PARAMETER_DATA_TYPE); - const QStringRef valueString = attributes.value(ATTRIBUTE_MACRO_COMMAND_PARAMETER_VALUE); const QString parameterName = attributes.value(ATTRIBUTE_MACRO_COMMAND_PARAMETER_NAME).toString(); - + const QString customDataType = attributes.value(ATTRIBUTE_MACRO_COMMAND_PARAMETER_CUSTOM_DATA_TYPE).toString(); + const QStringRef valueString = attributes.value(ATTRIBUTE_MACRO_COMMAND_PARAMETER_VALUE); + QString es; if (dataTypeString.isEmpty()) es.append(ATTRIBUTE_MACRO_COMMAND_PARAMETER_DATA_TYPE + " "); if ( ! es.isEmpty()) { @@ -420,6 +463,9 @@ WuQMacroGroupXmlStreamReader::readMacroCommandParameter(QXmlStreamReader& xmlRea value.setValue(boolValue); } break; + case WuQMacroDataValueTypeEnum::CUSTOM_DATA: + value.setValue(valueString.toString()); + break; case WuQMacroDataValueTypeEnum::FLOAT: { const double floatValue = valueString.toFloat(); @@ -446,6 +492,7 @@ WuQMacroGroupXmlStreamReader::readMacroCommandParameter(QXmlStreamReader& xmlRea WuQMacroCommandParameter* parameter = new WuQMacroCommandParameter(dataType, parameterName, value); + parameter->setCustomDataType(customDataType); return parameter; } @@ -455,22 +502,36 @@ WuQMacroGroupXmlStreamReader::readMacroCommandParameter(QXmlStreamReader& xmlRea * * @param xmlReader * The XML stream reader - * @Return The macro command + * @Return The macro command's content */ -WuQMacroCommand* +WuQMacroGroupXmlStreamReader::MacroCommandContent* WuQMacroGroupXmlStreamReader::readMacroCommandAttributesVersionOne(QXmlStreamReader& xmlReader) { - WuQMacroCommand* macroCommand(NULL); - const QXmlStreamAttributes attributes = xmlReader.attributes(); const QStringRef objectName = attributes.value(ATTRIBUTE_NAME); const QStringRef delayString = attributes.value(ATTRIBUTE_DELAY); + const QStringRef versionString = attributes.value(ATTRIBUTE_VERSION); const QStringRef descriptiveNameString = attributes.value(ATTRIBUTE_OBJECT_DESCRIPTIVE_NAME); - const QStringRef classString = attributes.value(ATTRIBUTE_OBJECT_CLASS); + const QStringRef commandTypeString = attributes.value(ATTRIBUTE_COMMAND_TYPE); + const QStringRef widgetTypeString = attributes.value(ATTRIBUTE_WIDGET_TYPE); + const QStringRef customOperationCommandNameString = attributes.value(ATTRIBUTE_CUSTOM_OPERATION_TYPE_NAME); QString es; if (objectName.isEmpty()) es.append(ATTRIBUTE_NAME + " "); - if (classString.isEmpty()) es.append(ATTRIBUTE_OBJECT_CLASS + " "); + if (commandTypeString.isEmpty()) es.append(ATTRIBUTE_WIDGET_TYPE + " "); + + bool commandTypeValid(false); + const WuQMacroCommandTypeEnum::Enum commandType = WuQMacroCommandTypeEnum::fromName(commandTypeString.toString(), + &commandTypeValid); + + if (commandTypeValid) { + if (widgetTypeString.isEmpty()) { + es.append(ATTRIBUTE_WIDGET_TYPE + " "); + } + } + else { + es.append(ATTRIBUTE_COMMAND_TYPE + " "); + } if ( ! es.isEmpty()) { addToWarnings(xmlReader, ELEMENT_MACRO_COMMAND @@ -481,47 +542,44 @@ WuQMacroGroupXmlStreamReader::readMacroCommandAttributesVersionOne(QXmlStreamRea bool objectTypeValid(false); - WuQMacroClassTypeEnum::Enum objectClass = WuQMacroClassTypeEnum::fromName(classString.toString(), - &objectTypeValid); + WuQMacroWidgetTypeEnum::Enum widgetType = WuQMacroWidgetTypeEnum::fromName(widgetTypeString.toString(), + &objectTypeValid); if (! objectTypeValid) { - es.append(classString.toString() + es.append(widgetTypeString + " is not valid for attribute " - + ATTRIBUTE_OBJECT_CLASS + + ATTRIBUTE_WIDGET_TYPE + " "); return NULL; } if ( ! es.isEmpty()) { addToWarnings(xmlReader, - ELEMENT_MACRO_COMMAND - + " has invalid attributes: " - + es); + es); return NULL; } - if (objectClass == WuQMacroClassTypeEnum::MOUSE_USER_EVENT) { - macroCommand = new WuQMacroCommand(WuQMacroClassTypeEnum::MOUSE_USER_EVENT, - objectName.toString(), - descriptiveNameString.toString()); - } - else { - QString tooltip; - macroCommand = new WuQMacroCommand(objectClass, - objectName.toString(), - descriptiveNameString.toString(), - tooltip); - - if ( ! delayString.isEmpty()) { - bool valid(false); - float delayValue = delayString.toFloat(&valid); - if ( ! valid) { - delayValue = 1.0; - } - macroCommand->setDelayInSeconds(delayValue); - } + + const int32_t versionNumber = (versionString.isEmpty() + ? 1 + : versionString.toInt()); + + + bool valid(false); + float delayValue = delayString.toFloat(&valid); + if ( ! valid) { + delayValue = 1.0; } - return macroCommand; + MacroCommandContent* macroCommandContent = new MacroCommandContent(); + macroCommandContent->m_commandType = commandType; + macroCommandContent->m_customOperationTypeName = customOperationCommandNameString.toString(); + macroCommandContent->m_widgetType = widgetType; + macroCommandContent->m_version = versionNumber; + macroCommandContent->m_objectName = objectName.toString(); + macroCommandContent->m_descriptiveName = descriptiveNameString.toString(); + macroCommandContent->m_delay = delayValue; + + return macroCommandContent; } /** diff --git a/src/Common/WuQMacroGroupXmlStreamReader.h b/src/Common/WuQMacroGroupXmlStreamReader.h index daccc7a85..f58f9316c 100644 --- a/src/Common/WuQMacroGroupXmlStreamReader.h +++ b/src/Common/WuQMacroGroupXmlStreamReader.h @@ -25,7 +25,9 @@ #include +#include "WuQMacroCommandTypeEnum.h" #include "WuQMacroGroupXmlStreamBase.h" +#include "WuQMacroWidgetTypeEnum.h" class QXmlStreamReader; @@ -58,6 +60,20 @@ namespace caret { // ADD_NEW_METHODS_HERE private: + class MacroCommandContent { + public: + WuQMacroCommandTypeEnum::Enum m_commandType = WuQMacroCommandTypeEnum::WIDGET; + QString m_customOperationTypeName; + WuQMacroMouseEventInfo* m_mouseInfo = NULL; // DO NOT DELETE + WuQMacroWidgetTypeEnum::Enum m_widgetType = WuQMacroWidgetTypeEnum::INVALID; + int32_t m_version = - 1; + QString m_objectName; + QString m_descriptiveName; + QString m_toolTip; + float m_delay = 1.0f; + std::vector m_parameters; // DO NOT DELETE + }; + WuQMacroMouseEventInfo* readMacroMouseEventInfo(QXmlStreamReader& xmlReader); void readVersionOne(QXmlStreamReader& xmlReader, @@ -65,7 +81,7 @@ namespace caret { WuQMacro* readMacroVersionOne(QXmlStreamReader& xmlReader); - WuQMacroCommand* readMacroCommandAttributesVersionOne(QXmlStreamReader& xmlReader); + MacroCommandContent* readMacroCommandAttributesVersionOne(QXmlStreamReader& xmlReader); WuQMacroCommand* readMacroCommandVersionOne(QXmlStreamReader& xmlReader); diff --git a/src/Common/WuQMacroGroupXmlStreamWriter.cxx b/src/Common/WuQMacroGroupXmlStreamWriter.cxx index 532426145..cf2e0fa47 100644 --- a/src/Common/WuQMacroGroupXmlStreamWriter.cxx +++ b/src/Common/WuQMacroGroupXmlStreamWriter.cxx @@ -74,7 +74,6 @@ WuQMacroGroupXmlStreamWriter::writeXml(QXmlStreamWriter* xmlWriter, CaretAssert(macroGroup); if (macroGroup->getNumberOfMacros() <= 0) { -// xmlWriter->writeEmptyElement(ELEMENT_MACRO_GROUP); return; } @@ -165,56 +164,27 @@ WuQMacroGroupXmlStreamWriter::writeMacroCommandToXML(const WuQMacroCommand* macr m_xmlStreamWriter->writeStartElement(ELEMENT_MACRO_COMMAND); m_xmlStreamWriter->writeAttribute(ATTRIBUTE_NAME, macroCommand->getObjectName()); - m_xmlStreamWriter->writeAttribute(ATTRIBUTE_OBJECT_CLASS, - WuQMacroClassTypeEnum::toName(macroCommand->getClassType())); + m_xmlStreamWriter->writeAttribute(ATTRIBUTE_COMMAND_TYPE, + WuQMacroCommandTypeEnum::toName(macroCommand->getCommandType())); + m_xmlStreamWriter->writeAttribute(ATTRIBUTE_WIDGET_TYPE, + WuQMacroWidgetTypeEnum::toName(macroCommand->getWidgetType())); + m_xmlStreamWriter->writeAttribute(ATTRIBUTE_VERSION, + QString::number(macroCommand->getVersion())); m_xmlStreamWriter->writeAttribute(ATTRIBUTE_OBJECT_DESCRIPTIVE_NAME, macroCommand->getDescriptiveName()); m_xmlStreamWriter->writeAttribute(ATTRIBUTE_DELAY, QString::number(macroCommand->getDelayInSeconds(), 'f', 2)); + m_xmlStreamWriter->writeAttribute(ATTRIBUTE_CUSTOM_OPERATION_TYPE_NAME, + macroCommand->getCustomOperationTypeName()); + - switch (macroCommand->getClassType()) { - case WuQMacroClassTypeEnum::ACTION: - break; - case WuQMacroClassTypeEnum::ACTION_CHECKABLE: - break; - case WuQMacroClassTypeEnum::ACTION_GROUP: - break; - case WuQMacroClassTypeEnum::BUTTON_GROUP: - break; - case WuQMacroClassTypeEnum::CHECK_BOX: - break; - case WuQMacroClassTypeEnum::COMBO_BOX: - break; - case WuQMacroClassTypeEnum::DOUBLE_SPIN_BOX: + switch (macroCommand->getCommandType()) { + case WuQMacroCommandTypeEnum::CUSTOM_OPERATION: break; - case WuQMacroClassTypeEnum::INVALID: - break; - case WuQMacroClassTypeEnum::LINE_EDIT: - break; - case WuQMacroClassTypeEnum::LIST_WIDGET: - break; - case WuQMacroClassTypeEnum::MENU: - break; - case WuQMacroClassTypeEnum::MOUSE_USER_EVENT: + case WuQMacroCommandTypeEnum::MOUSE: writeMacroMouseEventInfo(macroCommand->getMouseEventInfo()); break; - case WuQMacroClassTypeEnum::PUSH_BUTTON: - break; - case WuQMacroClassTypeEnum::PUSH_BUTTON_CHECKABLE: - break; - case WuQMacroClassTypeEnum::RADIO_BUTTON: - break; - case WuQMacroClassTypeEnum::SLIDER: - break; - case WuQMacroClassTypeEnum::SPIN_BOX: - break; - case WuQMacroClassTypeEnum::TAB_BAR: - break; - case WuQMacroClassTypeEnum::TAB_WIDGET: - break; - case WuQMacroClassTypeEnum::TOOL_BUTTON: - break; - case WuQMacroClassTypeEnum::TOOL_BUTTON_CHECKABLE: + case WuQMacroCommandTypeEnum::WIDGET: break; } @@ -242,6 +212,9 @@ WuQMacroGroupXmlStreamWriter::writeMacroCommandToXML(const WuQMacroCommand* macr case WuQMacroDataValueTypeEnum::BOOLEAN: stringValue = (value.toBool() ? VALUE_BOOL_TRUE : VALUE_BOOL_FALSE); break; + case WuQMacroDataValueTypeEnum::CUSTOM_DATA: + stringValue = value.toString(); + break; case WuQMacroDataValueTypeEnum::FLOAT: stringValue = QString::number(value.toFloat()); break; @@ -258,10 +231,12 @@ WuQMacroGroupXmlStreamWriter::writeMacroCommandToXML(const WuQMacroCommand* macr stringValue = value.toString(); break; } - m_xmlStreamWriter->writeAttribute(ATTRIBUTE_MACRO_COMMAND_PARAMETER_VALUE, - stringValue); m_xmlStreamWriter->writeAttribute(ATTRIBUTE_MACRO_COMMAND_PARAMETER_NAME, parameter->getName()); + m_xmlStreamWriter->writeAttribute(ATTRIBUTE_MACRO_COMMAND_PARAMETER_CUSTOM_DATA_TYPE, + parameter->getCustomDataType()); + m_xmlStreamWriter->writeAttribute(ATTRIBUTE_MACRO_COMMAND_PARAMETER_VALUE, + stringValue); m_xmlStreamWriter->writeEndElement(); } diff --git a/src/Common/WuQMacroClassTypeEnum.cxx b/src/Common/WuQMacroWidgetTypeEnum.cxx similarity index 67% rename from src/Common/WuQMacroClassTypeEnum.cxx rename to src/Common/WuQMacroWidgetTypeEnum.cxx index 3bb4c4855..a6b8631b1 100644 --- a/src/Common/WuQMacroClassTypeEnum.cxx +++ b/src/Common/WuQMacroWidgetTypeEnum.cxx @@ -20,9 +20,9 @@ /*LICENSE_END*/ #include -#define __WU_Q_MACRO_CLASS_TYPE_ENUM_DECLARE__ -#include "WuQMacroClassTypeEnum.h" -#undef __WU_Q_MACRO_CLASS_TYPE_ENUM_DECLARE__ +#define __WU_Q_MACRO_WIDGET_TYPE_ENUM_DECLARE__ +#include "WuQMacroWidgetTypeEnum.h" +#undef __WU_Q_MACRO_WIDGET_TYPE_ENUM_DECLARE__ #include "CaretAssert.h" @@ -30,8 +30,8 @@ using namespace caret; /** - * \class caret::WuQMacroClassTypeEnum - * \brief Enumerated type for specific type of object in macro + * \class caret::WuQMacroWidgetTypeEnum + * \brief Enumerated type for widgets in a macro command * * Using this enumerated type in the GUI with an EnumComboBoxTemplate * @@ -40,30 +40,30 @@ using namespace caret; * class EnumComboBoxTemplate; * * Declare the member: - * EnumComboBoxTemplate* m_WuQMacroClassTypeEnumComboBox; + * EnumComboBoxTemplate* m_WuQMacroWidgetTypeEnumComboBox; * * Declare a slot that is called when user changes selection * private slots: - * void WuQMacroClassTypeEnumComboBoxItemActivated(); + * void WuQMacroWidgetTypeEnumComboBoxItemActivated(); * * Implementation File (.cxx) * Include the header files * #include "EnumComboBoxTemplate.h" - * #include "WuQMacroClassTypeEnum.h" + * #include "WuQMacroWidgetTypeEnum.h" * * Instatiate: - * m_WuQMacroClassTypeEnumComboBox = new EnumComboBoxTemplate(this); - * m_WuQMacroClassTypeEnumComboBox->setup(); + * m_WuQMacroWidgetTypeEnumComboBox = new EnumComboBoxTemplate(this); + * m_WuQMacroWidgetTypeEnumComboBox->setup(); * * Get notified when the user changes the selection: - * QObject::connect(m_WuQMacroClassTypeEnumComboBox, SIGNAL(itemActivated()), - * this, SLOT(WuQMacroClassTypeEnumComboBoxItemActivated())); + * QObject::connect(m_WuQMacroWidgetTypeEnumComboBox, SIGNAL(itemActivated()), + * this, SLOT(WuQMacroWidgetTypeEnumComboBoxItemActivated())); * * Update the selection: - * m_WuQMacroClassTypeEnumComboBox->setSelectedItem(NEW_VALUE); + * m_WuQMacroWidgetTypeEnumComboBox->setSelectedItem(NEW_VALUE); * * Read the selection: - * const WuQMacroClassTypeEnum::Enum VARIABLE = m_WuQMacroClassTypeEnumComboBox->getSelectedItem(); + * const WuQMacroWidgetTypeEnum::Enum VARIABLE = m_WuQMacroWidgetTypeEnumComboBox->getSelectedItem(); * */ @@ -78,7 +78,7 @@ using namespace caret; * @param guiName * User-friendly name for use in user-interface. */ -WuQMacroClassTypeEnum::WuQMacroClassTypeEnum(const Enum enumValue, +WuQMacroWidgetTypeEnum::WuQMacroWidgetTypeEnum(const Enum enumValue, const AString& name, const AString& guiName) { @@ -91,7 +91,7 @@ WuQMacroClassTypeEnum::WuQMacroClassTypeEnum(const Enum enumValue, /** * Destructor. */ -WuQMacroClassTypeEnum::~WuQMacroClassTypeEnum() +WuQMacroWidgetTypeEnum::~WuQMacroWidgetTypeEnum() { } @@ -99,94 +99,90 @@ WuQMacroClassTypeEnum::~WuQMacroClassTypeEnum() * Initialize the enumerated metadata. */ void -WuQMacroClassTypeEnum::initialize() +WuQMacroWidgetTypeEnum::initialize() { if (initializedFlag) { return; } initializedFlag = true; - enumData.push_back(WuQMacroClassTypeEnum(INVALID, + enumData.push_back(WuQMacroWidgetTypeEnum(INVALID, "INVALID", "Invalid")); - enumData.push_back(WuQMacroClassTypeEnum(ACTION, + enumData.push_back(WuQMacroWidgetTypeEnum(ACTION, "ACTION", "QAction")); - enumData.push_back(WuQMacroClassTypeEnum(ACTION_CHECKABLE, + enumData.push_back(WuQMacroWidgetTypeEnum(ACTION_CHECKABLE, "ACTION_CHECKABLE", "QActionCheckable")); - enumData.push_back(WuQMacroClassTypeEnum(ACTION_GROUP, + enumData.push_back(WuQMacroWidgetTypeEnum(ACTION_GROUP, "ACTION_GROUP", "QActionGroup")); - enumData.push_back(WuQMacroClassTypeEnum(BUTTON_GROUP, + enumData.push_back(WuQMacroWidgetTypeEnum(BUTTON_GROUP, "BUTTON_GROUP", "QButtonGroup")); - enumData.push_back(WuQMacroClassTypeEnum(CHECK_BOX, + enumData.push_back(WuQMacroWidgetTypeEnum(CHECK_BOX, "CHECK_BOX", "QCheckBox")); - enumData.push_back(WuQMacroClassTypeEnum(COMBO_BOX, + enumData.push_back(WuQMacroWidgetTypeEnum(COMBO_BOX, "COMBO_BOX", "QComboBox")); - enumData.push_back(WuQMacroClassTypeEnum(DOUBLE_SPIN_BOX, + enumData.push_back(WuQMacroWidgetTypeEnum(DOUBLE_SPIN_BOX, "DOUBLE_SPIN_BOX", "QDoubleSpinBox")); - enumData.push_back(WuQMacroClassTypeEnum(LINE_EDIT, + enumData.push_back(WuQMacroWidgetTypeEnum(LINE_EDIT, "LINE_EDIT", "QLineEdit")); - enumData.push_back(WuQMacroClassTypeEnum(LIST_WIDGET, + enumData.push_back(WuQMacroWidgetTypeEnum(LIST_WIDGET, "LIST_WIDGET", "QListWidget")); - enumData.push_back(WuQMacroClassTypeEnum(MENU, + enumData.push_back(WuQMacroWidgetTypeEnum(MENU, "MENU", "QMenu")); - enumData.push_back(WuQMacroClassTypeEnum(MOUSE_USER_EVENT, - "MOUSE_USER_EVENT", - "QMouseEvent")); - - enumData.push_back(WuQMacroClassTypeEnum(PUSH_BUTTON, + enumData.push_back(WuQMacroWidgetTypeEnum(PUSH_BUTTON, "PUSH_BUTTON", "QPushButton")); - enumData.push_back(WuQMacroClassTypeEnum(PUSH_BUTTON_CHECKABLE, + enumData.push_back(WuQMacroWidgetTypeEnum(PUSH_BUTTON_CHECKABLE, "PUSH_BUTTON_CHECKABLE", "QPushButtonCheckable")); - enumData.push_back(WuQMacroClassTypeEnum(RADIO_BUTTON, + enumData.push_back(WuQMacroWidgetTypeEnum(RADIO_BUTTON, "RADIO_BUTTON", "QRadioButton")); - enumData.push_back(WuQMacroClassTypeEnum(SLIDER, + enumData.push_back(WuQMacroWidgetTypeEnum(SLIDER, "SLIDER", "QSlider")); - enumData.push_back(WuQMacroClassTypeEnum(SPIN_BOX, + enumData.push_back(WuQMacroWidgetTypeEnum(SPIN_BOX, "SPIN_BOX", "QSpinBox")); - enumData.push_back(WuQMacroClassTypeEnum(TAB_BAR, + enumData.push_back(WuQMacroWidgetTypeEnum(TAB_BAR, "TAB_BAR", "QTabBar")); - enumData.push_back(WuQMacroClassTypeEnum(TAB_WIDGET, + enumData.push_back(WuQMacroWidgetTypeEnum(TAB_WIDGET, "TAB_WIDGET", "QTabWidget")); - enumData.push_back(WuQMacroClassTypeEnum(TOOL_BUTTON, + enumData.push_back(WuQMacroWidgetTypeEnum(TOOL_BUTTON, "TOOL_BUTTON", "QToolButton")); - enumData.push_back(WuQMacroClassTypeEnum(TOOL_BUTTON_CHECKABLE, + enumData.push_back(WuQMacroWidgetTypeEnum(TOOL_BUTTON_CHECKABLE, "TOOL_BUTTON_CHECKABLE", "QToolButtonCheckable")); } @@ -198,14 +194,14 @@ WuQMacroClassTypeEnum::initialize() * @return Pointer to data for this enumerated type * or NULL if no data for type or if type is invalid. */ -const WuQMacroClassTypeEnum* -WuQMacroClassTypeEnum::findData(const Enum enumValue) +const WuQMacroWidgetTypeEnum* +WuQMacroWidgetTypeEnum::findData(const Enum enumValue) { if (initializedFlag == false) initialize(); size_t num = enumData.size(); for (size_t i = 0; i < num; i++) { - const WuQMacroClassTypeEnum* d = &enumData[i]; + const WuQMacroWidgetTypeEnum* d = &enumData[i]; if (d->enumValue == enumValue) { return d; } @@ -222,10 +218,10 @@ WuQMacroClassTypeEnum::findData(const Enum enumValue) * String representing enumerated value. */ AString -WuQMacroClassTypeEnum::toName(Enum enumValue) { +WuQMacroWidgetTypeEnum::toName(Enum enumValue) { if (initializedFlag == false) initialize(); - const WuQMacroClassTypeEnum* enumInstance = findData(enumValue); + const WuQMacroWidgetTypeEnum* enumInstance = findData(enumValue); return enumInstance->name; } @@ -239,18 +235,18 @@ WuQMacroClassTypeEnum::toName(Enum enumValue) { * @return * Enumerated value. */ -WuQMacroClassTypeEnum::Enum -WuQMacroClassTypeEnum::fromName(const AString& name, bool* isValidOut) +WuQMacroWidgetTypeEnum::Enum +WuQMacroWidgetTypeEnum::fromName(const AString& name, bool* isValidOut) { if (initializedFlag == false) initialize(); bool validFlag = false; - Enum enumValue = WuQMacroClassTypeEnum::enumData[0].enumValue; + Enum enumValue = WuQMacroWidgetTypeEnum::enumData[0].enumValue; - for (std::vector::iterator iter = enumData.begin(); + for (std::vector::iterator iter = enumData.begin(); iter != enumData.end(); iter++) { - const WuQMacroClassTypeEnum& d = *iter; + const WuQMacroWidgetTypeEnum& d = *iter; if (d.name == name) { enumValue = d.enumValue; validFlag = true; @@ -262,7 +258,7 @@ WuQMacroClassTypeEnum::fromName(const AString& name, bool* isValidOut) *isValidOut = validFlag; } else if (validFlag == false) { - CaretAssertMessage(0, AString("Name " + name + "failed to match enumerated value for type WuQMacroClassTypeEnum")); + CaretAssertMessage(0, AString("Name " + name + "failed to match enumerated value for type WuQMacroWidgetTypeEnum")); } return enumValue; } @@ -275,10 +271,10 @@ WuQMacroClassTypeEnum::fromName(const AString& name, bool* isValidOut) * String representing enumerated value. */ AString -WuQMacroClassTypeEnum::toGuiName(Enum enumValue) { +WuQMacroWidgetTypeEnum::toGuiName(Enum enumValue) { if (initializedFlag == false) initialize(); - const WuQMacroClassTypeEnum* enumInstance = findData(enumValue); + const WuQMacroWidgetTypeEnum* enumInstance = findData(enumValue); return enumInstance->guiName; } @@ -292,8 +288,8 @@ WuQMacroClassTypeEnum::toGuiName(Enum enumValue) { * @return * Enumerated value. */ -WuQMacroClassTypeEnum::Enum -WuQMacroClassTypeEnum::fromGuiName(const AString& guiNameIn, bool* isValidOut) +WuQMacroWidgetTypeEnum::Enum +WuQMacroWidgetTypeEnum::fromGuiName(const AString& guiNameIn, bool* isValidOut) { if (initializedFlag == false) initialize(); @@ -309,12 +305,12 @@ WuQMacroClassTypeEnum::fromGuiName(const AString& guiNameIn, bool* isValidOut) } bool validFlag = false; - Enum enumValue = WuQMacroClassTypeEnum::enumData[0].enumValue; + Enum enumValue = WuQMacroWidgetTypeEnum::enumData[0].enumValue; - for (std::vector::iterator iter = enumData.begin(); + for (std::vector::iterator iter = enumData.begin(); iter != enumData.end(); iter++) { - const WuQMacroClassTypeEnum& d = *iter; + const WuQMacroWidgetTypeEnum& d = *iter; if (d.guiName == guiName) { enumValue = d.enumValue; validFlag = true; @@ -326,7 +322,7 @@ WuQMacroClassTypeEnum::fromGuiName(const AString& guiNameIn, bool* isValidOut) *isValidOut = validFlag; } else if (validFlag == false) { - CaretAssertMessage(0, AString("guiName " + guiName + "failed to match enumerated value for type WuQMacroClassTypeEnum")); + CaretAssertMessage(0, AString("guiName " + guiName + "failed to match enumerated value for type WuQMacroWidgetTypeEnum")); } return enumValue; } @@ -338,10 +334,10 @@ WuQMacroClassTypeEnum::fromGuiName(const AString& guiNameIn, bool* isValidOut) * Integer code for data type. */ int32_t -WuQMacroClassTypeEnum::toIntegerCode(Enum enumValue) +WuQMacroWidgetTypeEnum::toIntegerCode(Enum enumValue) { if (initializedFlag == false) initialize(); - const WuQMacroClassTypeEnum* enumInstance = findData(enumValue); + const WuQMacroWidgetTypeEnum* enumInstance = findData(enumValue); return enumInstance->integerCode; } @@ -356,18 +352,18 @@ WuQMacroClassTypeEnum::toIntegerCode(Enum enumValue) * @return * Enum for integer code. */ -WuQMacroClassTypeEnum::Enum -WuQMacroClassTypeEnum::fromIntegerCode(const int32_t integerCode, bool* isValidOut) +WuQMacroWidgetTypeEnum::Enum +WuQMacroWidgetTypeEnum::fromIntegerCode(const int32_t integerCode, bool* isValidOut) { if (initializedFlag == false) initialize(); bool validFlag = false; - Enum enumValue = WuQMacroClassTypeEnum::enumData[0].enumValue; + Enum enumValue = WuQMacroWidgetTypeEnum::enumData[0].enumValue; - for (std::vector::iterator iter = enumData.begin(); + for (std::vector::iterator iter = enumData.begin(); iter != enumData.end(); iter++) { - const WuQMacroClassTypeEnum& enumInstance = *iter; + const WuQMacroWidgetTypeEnum& enumInstance = *iter; if (enumInstance.integerCode == integerCode) { enumValue = enumInstance.enumValue; validFlag = true; @@ -379,7 +375,7 @@ WuQMacroClassTypeEnum::fromIntegerCode(const int32_t integerCode, bool* isValidO *isValidOut = validFlag; } else if (validFlag == false) { - CaretAssertMessage(0, AString("Integer code " + AString::number(integerCode) + "failed to match enumerated value for type WuQMacroClassTypeEnum")); + CaretAssertMessage(0, AString("Integer code " + AString::number(integerCode) + "failed to match enumerated value for type WuQMacroWidgetTypeEnum")); } return enumValue; } @@ -392,13 +388,13 @@ WuQMacroClassTypeEnum::fromIntegerCode(const int32_t integerCode, bool* isValidO * A vector that is OUTPUT containing all of the enumerated values. */ void -WuQMacroClassTypeEnum::getAllEnums(std::vector& allEnums) +WuQMacroWidgetTypeEnum::getAllEnums(std::vector& allEnums) { if (initializedFlag == false) initialize(); allEnums.clear(); - for (std::vector::iterator iter = enumData.begin(); + for (std::vector::iterator iter = enumData.begin(); iter != enumData.end(); iter++) { allEnums.push_back(iter->enumValue); @@ -414,16 +410,16 @@ WuQMacroClassTypeEnum::getAllEnums(std::vector& all * If true, the names are sorted in alphabetical order. */ void -WuQMacroClassTypeEnum::getAllNames(std::vector& allNames, const bool isSorted) +WuQMacroWidgetTypeEnum::getAllNames(std::vector& allNames, const bool isSorted) { if (initializedFlag == false) initialize(); allNames.clear(); - for (std::vector::iterator iter = enumData.begin(); + for (std::vector::iterator iter = enumData.begin(); iter != enumData.end(); iter++) { - allNames.push_back(WuQMacroClassTypeEnum::toName(iter->enumValue)); + allNames.push_back(WuQMacroWidgetTypeEnum::toName(iter->enumValue)); } if (isSorted) { @@ -440,16 +436,16 @@ WuQMacroClassTypeEnum::getAllNames(std::vector& allNames, const bool is * If true, the names are sorted in alphabetical order. */ void -WuQMacroClassTypeEnum::getAllGuiNames(std::vector& allGuiNames, const bool isSorted) +WuQMacroWidgetTypeEnum::getAllGuiNames(std::vector& allGuiNames, const bool isSorted) { if (initializedFlag == false) initialize(); allGuiNames.clear(); - for (std::vector::iterator iter = enumData.begin(); + for (std::vector::iterator iter = enumData.begin(); iter != enumData.end(); iter++) { - allGuiNames.push_back(WuQMacroClassTypeEnum::toGuiName(iter->enumValue)); + allGuiNames.push_back(WuQMacroWidgetTypeEnum::toGuiName(iter->enumValue)); } if (isSorted) { @@ -466,7 +462,7 @@ WuQMacroClassTypeEnum::getAllGuiNames(std::vector& allGuiNames, const b * Name of class that is derived from Qt widget with name 'widgetName' */ void -WuQMacroClassTypeEnum::addWidgetClassNameAlias(const QString& widgetName, +WuQMacroWidgetTypeEnum::addWidgetClassNameAlias(const QString& widgetName, const QString& aliasWidgetName) { s_widgetClassNameAliases.push_back(std::make_pair(widgetName, diff --git a/src/Common/WuQMacroClassTypeEnum.h b/src/Common/WuQMacroWidgetTypeEnum.h similarity index 73% rename from src/Common/WuQMacroClassTypeEnum.h rename to src/Common/WuQMacroWidgetTypeEnum.h index eb81511ff..26692da40 100644 --- a/src/Common/WuQMacroClassTypeEnum.h +++ b/src/Common/WuQMacroWidgetTypeEnum.h @@ -1,5 +1,5 @@ -#ifndef __WU_Q_MACRO_CLASS_TYPE_ENUM_H__ -#define __WU_Q_MACRO_CLASS_TYPE_ENUM_H__ +#ifndef __WU_Q_MACRO_WIDGET_TYPE_ENUM_H__ +#define __WU_Q_MACRO_WIDGET_TYPE_ENUM_H__ /*LICENSE_START*/ /* @@ -28,7 +28,7 @@ namespace caret { -class WuQMacroClassTypeEnum { +class WuQMacroWidgetTypeEnum { public: /** @@ -57,8 +57,6 @@ public: LIST_WIDGET, /** a QMenu */ MENU, - /** a Mouse Event */ - MOUSE_USER_EVENT, /** a QPushButton(not checkable) */ PUSH_BUTTON, /** a Checkable QPushButton */ @@ -81,42 +79,40 @@ public: /* switch (m_objectType) { - case WuQMacroClassTypeEnum::ACTION: + case WuQMacroWidgetTypeEnum::ACTION: break; - case WuQMacroClassTypeEnum::CHECK_BOX: + case WuQMacroWidgetTypeEnum::CHECK_BOX: break; - case WuQMacroClassTypeEnum::COMBO_BOX: + case WuQMacroWidgetTypeEnum::COMBO_BOX: break; - case WuQMacroClassTypeEnum::DOUBLE_SPIN_BOX: + case WuQMacroWidgetTypeEnum::DOUBLE_SPIN_BOX: break; - case WuQMacroClassTypeEnum::INVALID: + case WuQMacroWidgetTypeEnum::INVALID: break; - case WuQMacroClassTypeEnum::LINE_EDIT: + case WuQMacroWidgetTypeEnum::LINE_EDIT: break; - case WuQMacroClassTypeEnum::LIST_WIDGET: + case WuQMacroWidgetTypeEnum::LIST_WIDGET: break; - case WuQMacroClassTypeEnum::MENU: + case WuQMacroWidgetTypeEnum::MENU: break; - case WuQMacroClassTypeEnum::MOUSE_USER_EVENT: + case WuQMacroWidgetTypeEnum::PUSH_BUTTON: break; - case WuQMacroClassTypeEnum::PUSH_BUTTON: + case WuQMacroWidgetTypeEnum::RADIO_BUTTON: break; - case WuQMacroClassTypeEnum::RADIO_BUTTON: + case WuQMacroWidgetTypeEnum::SLIDER: break; - case WuQMacroClassTypeEnum::SLIDER: + case WuQMacroWidgetTypeEnum::SPIN_BOX: break; - case WuQMacroClassTypeEnum::SPIN_BOX: + case WuQMacroWidgetTypeEnum::TAB_BAR: break; - case WuQMacroClassTypeEnum::TAB_BAR: + case WuQMacroWidgetTypeEnum::TAB_WIDGET: break; - case WuQMacroClassTypeEnum::TAB_WIDGET: - break; - case WuQMacroClassTypeEnum::TOOL_BUTTON: + case WuQMacroWidgetTypeEnum::TOOL_BUTTON: break; } */ - ~WuQMacroClassTypeEnum(); + ~WuQMacroWidgetTypeEnum(); static AString toName(Enum enumValue); @@ -140,14 +136,14 @@ public: const QString& aliasWidgetName); private: - WuQMacroClassTypeEnum(const Enum enumValue, + WuQMacroWidgetTypeEnum(const Enum enumValue, const AString& name, const AString& guiName); - static const WuQMacroClassTypeEnum* findData(const Enum enumValue); + static const WuQMacroWidgetTypeEnum* findData(const Enum enumValue); /** Holds all instance of enum values and associated metadata */ - static std::vector enumData; + static std::vector enumData; /** Initialize instances that contain the enum values and metadata */ static void initialize(); @@ -174,12 +170,12 @@ private: static std::vector> s_widgetClassNameAliases; }; -#ifdef __WU_Q_MACRO_CLASS_TYPE_ENUM_DECLARE__ - std::vector WuQMacroClassTypeEnum::enumData; - bool WuQMacroClassTypeEnum::initializedFlag = false; - int32_t WuQMacroClassTypeEnum::integerCodeCounter = 0; - std::vector> WuQMacroClassTypeEnum::s_widgetClassNameAliases; -#endif // __WU_Q_MACRO_CLASS_TYPE_ENUM_DECLARE__ +#ifdef __WU_Q_MACRO_WIDGET_TYPE_ENUM_DECLARE__ + std::vector WuQMacroWidgetTypeEnum::enumData; + bool WuQMacroWidgetTypeEnum::initializedFlag = false; + int32_t WuQMacroWidgetTypeEnum::integerCodeCounter = 0; + std::vector> WuQMacroWidgetTypeEnum::s_widgetClassNameAliases; +#endif // __WU_Q_MACRO_WIDGET_TYPE_ENUM_DECLARE__ } // namespace -#endif //__WU_Q_MACRO_CLASS_TYPE_ENUM_H__ +#endif //__WU_Q_MACRO_WIDGET_TYPE_ENUM_H__ diff --git a/src/GuiQt/CMakeLists.txt b/src/GuiQt/CMakeLists.txt index 8048feb58..24a9d4ab4 100644 --- a/src/GuiQt/CMakeLists.txt +++ b/src/GuiQt/CMakeLists.txt @@ -125,7 +125,6 @@ ELSE() GiftiLabelTableEditor.h GiftiLabelTableSelectionComboBox.h GroupAndNameHierarchyViewController.h - GuiMacroHelper.h GuiManager.h HelpViewerDialog.h HyperLinkTextBrowser.h @@ -192,6 +191,7 @@ ELSE() VolumeSurfaceOutlineColorOrTabViewController.h VolumeSurfaceOutlineSetViewController.h VolumeSurfaceOutlineViewController.h + WbMacroHelper.h WuQCollapsibleWidget.h WuQDataEntryDialog.h WuQDialog.h @@ -367,7 +367,6 @@ GiftiLabelTableEditor.h GiftiLabelTableSelectionComboBox.h GroupAndNameHierarchyTreeWidgetItem.h GroupAndNameHierarchyViewController.h -GuiMacroHelper.h GuiManager.h HelpViewerDialog.h HyperLinkTextBrowser.h @@ -448,6 +447,11 @@ VolumeFileCreateDialog.h VolumeSurfaceOutlineColorOrTabViewController.h VolumeSurfaceOutlineSetViewController.h VolumeSurfaceOutlineViewController.h +WbMacroCustomDataTypeEnum.h +WbMacroCustomOperationManager.h +WbMacroCustomOperationSurfaceInterpolation.h +WbMacroCustomOperationTypeEnum.h +WbMacroHelper.h WuQCollapsibleWidget.h WuQDataEntryDialog.h WuQDialog.h @@ -462,6 +466,7 @@ WuQGroupBoxExclusiveWidget.h WuQImageLabel.h WuQListWidget.h WuQMacroCreateDialog.h +WuQMacroCustomOperationManagerInterface.h WuQMacroDialog.h WuQMacroExecutor.h WuQMacroExecutorOptions.h @@ -612,7 +617,6 @@ GiftiLabelTableEditor.cxx GiftiLabelTableSelectionComboBox.cxx GroupAndNameHierarchyTreeWidgetItem.cxx GroupAndNameHierarchyViewController.cxx -GuiMacroHelper.cxx GuiManager.cxx HelpViewerDialog.cxx HyperLinkTextBrowser.cxx @@ -693,6 +697,11 @@ VolumeFileCreateDialog.cxx VolumeSurfaceOutlineColorOrTabViewController.cxx VolumeSurfaceOutlineSetViewController.cxx VolumeSurfaceOutlineViewController.cxx +WbMacroCustomDataTypeEnum.cxx +WbMacroCustomOperationManager.cxx +WbMacroCustomOperationSurfaceInterpolation.cxx +WbMacroCustomOperationTypeEnum.cxx +WbMacroHelper.cxx WuQCollapsibleWidget.cxx WuQDataEntryDialog.cxx WuQDialog.cxx diff --git a/src/GuiQt/GuiManager.cxx b/src/GuiQt/GuiManager.cxx index 1484bd8da..19a6b2036 100644 --- a/src/GuiQt/GuiManager.cxx +++ b/src/GuiQt/GuiManager.cxx @@ -86,7 +86,8 @@ #include "EventUserInterfaceUpdate.h" #include "FociPropertiesEditorDialog.h" #include "GapsAndMarginsDialog.h" -#include "GuiMacroHelper.h" +#include "WbMacroCustomOperationManager.h" +#include "WbMacroHelper.h" #include "HelpViewerDialog.h" #include "IdentifiedItemNode.h" #include "IdentifiedItemVoxel.h" @@ -123,7 +124,7 @@ #include "TileTabsConfigurationDialog.h" #include "VolumeMappableInterface.h" #include "WuQMacroManager.h" -#include "WuQMacroClassTypeEnum.h" +#include "WuQMacroWidgetTypeEnum.h" #include "WuQMessageBox.h" #include "WuQtUtilities.h" @@ -193,12 +194,13 @@ GuiManager::initializeGuiManager() * of GuiManager and not found when searching a window * for children objects */ - WuQMacroClassTypeEnum::addWidgetClassNameAlias("QTabWidget", + WuQMacroWidgetTypeEnum::addWidgetClassNameAlias("QTabWidget", "caret::WuQTabWidgetWithSizeHint"); - WuQMacroClassTypeEnum::addWidgetClassNameAlias("QTabBar", + WuQMacroWidgetTypeEnum::addWidgetClassNameAlias("QTabBar", "caret::WuQTabBar"); WuQMacroManager::instance()->addParentObject(this); - WuQMacroManager::instance()->setMacroHelper(new GuiMacroHelper(this)); + WuQMacroManager::instance()->setMacroHelper(new WbMacroHelper(this)); + WuQMacroManager::instance()->setCustomCommandManager(new WbMacroCustomOperationManager()); /* * Windows vector never changes size diff --git a/src/GuiQt/WbMacroCustomDataTypeEnum.cxx b/src/GuiQt/WbMacroCustomDataTypeEnum.cxx new file mode 100644 index 000000000..cb6bb7acf --- /dev/null +++ b/src/GuiQt/WbMacroCustomDataTypeEnum.cxx @@ -0,0 +1,369 @@ + +/*LICENSE_START*/ +/* + * Copyright (C) 2019 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + +#include +#define __WB_MACRO_CUSTOM_DATA_TYPE_ENUM_DECLARE__ +#include "WbMacroCustomDataTypeEnum.h" +#undef __WB_MACRO_CUSTOM_DATA_TYPE_ENUM_DECLARE__ + +#include "CaretAssert.h" + +using namespace caret; + + +/** + * \class caret::WbMacroCustomDataTypeEnum + * \brief Enumerated type for a user defined macro data type + * + * Using this enumerated type in the GUI with an EnumComboBoxTemplate + * + * Header File (.h) + * Forward declare the data type: + * class EnumComboBoxTemplate; + * + * Declare the member: + * EnumComboBoxTemplate* m_WbMacroCustomDataTypeEnumComboBox; + * + * Declare a slot that is called when user changes selection + * private slots: + * void WbMacroCustomDataTypeEnumComboBoxItemActivated(); + * + * Implementation File (.cxx) + * Include the header files + * #include "EnumComboBoxTemplate.h" + * #include "WbMacroCustomDataTypeEnum.h" + * + * Instatiate: + * m_WbMacroCustomDataTypeEnumComboBox = new EnumComboBoxTemplate(this); + * m_WbMacroCustomDataTypeEnumComboBox->setup(); + * + * Get notified when the user changes the selection: + * QObject::connect(m_WbMacroCustomDataTypeEnumComboBox, SIGNAL(itemActivated()), + * this, SLOT(WbMacroCustomDataTypeEnumComboBoxItemActivated())); + * + * Update the selection: + * m_WbMacroCustomDataTypeEnumComboBox->setSelectedItem(NEW_VALUE); + * + * Read the selection: + * const WbMacroCustomDataTypeEnum::Enum VARIABLE = m_WbMacroCustomDataTypeEnumComboBox->getSelectedItem(); + * + */ + +/** + * Constructor. + * + * @param enumValue + * An enumerated value. + * @param name + * Name of enumerated value. + * + * @param guiName + * User-friendly name for use in user-interface. + */ +WbMacroCustomDataTypeEnum::WbMacroCustomDataTypeEnum(const Enum enumValue, + const AString& name, + const AString& guiName) +{ + this->enumValue = enumValue; + this->integerCode = integerCodeCounter++; + this->name = name; + this->guiName = guiName; +} + +/** + * Destructor. + */ +WbMacroCustomDataTypeEnum::~WbMacroCustomDataTypeEnum() +{ +} + +/** + * Initialize the enumerated metadata. + */ +void +WbMacroCustomDataTypeEnum::initialize() +{ + if (initializedFlag) { + return; + } + initializedFlag = true; + + enumData.push_back(WbMacroCustomDataTypeEnum(SURFACE, + "SURFACE", + "Surface")); + +} + +/** + * Find the data for and enumerated value. + * @param enumValue + * The enumerated value. + * @return Pointer to data for this enumerated type + * or NULL if no data for type or if type is invalid. + */ +const WbMacroCustomDataTypeEnum* +WbMacroCustomDataTypeEnum::findData(const Enum enumValue) +{ + if (initializedFlag == false) initialize(); + + size_t num = enumData.size(); + for (size_t i = 0; i < num; i++) { + const WbMacroCustomDataTypeEnum* d = &enumData[i]; + if (d->enumValue == enumValue) { + return d; + } + } + + return NULL; +} + +/** + * Get a string representation of the enumerated type. + * @param enumValue + * Enumerated value. + * @return + * String representing enumerated value. + */ +AString +WbMacroCustomDataTypeEnum::toName(Enum enumValue) { + if (initializedFlag == false) initialize(); + + const WbMacroCustomDataTypeEnum* enumInstance = findData(enumValue); + return enumInstance->name; +} + +/** + * Get an enumerated value corresponding to its name. + * @param name + * Name of enumerated value. + * @param isValidOut + * If not NULL, it is set indicating that a + * enum value exists for the input name. + * @return + * Enumerated value. + */ +WbMacroCustomDataTypeEnum::Enum +WbMacroCustomDataTypeEnum::fromName(const AString& name, bool* isValidOut) +{ + if (initializedFlag == false) initialize(); + + bool validFlag = false; + Enum enumValue = WbMacroCustomDataTypeEnum::enumData[0].enumValue; + + for (std::vector::iterator iter = enumData.begin(); + iter != enumData.end(); + iter++) { + const WbMacroCustomDataTypeEnum& d = *iter; + if (d.name == name) { + enumValue = d.enumValue; + validFlag = true; + break; + } + } + + if (isValidOut != 0) { + *isValidOut = validFlag; + } + else if (validFlag == false) { + CaretAssertMessage(0, AString("Name " + name + "failed to match enumerated value for type WbMacroCustomDataTypeEnum")); + } + return enumValue; +} + +/** + * Get a GUI string representation of the enumerated type. + * @param enumValue + * Enumerated value. + * @return + * String representing enumerated value. + */ +AString +WbMacroCustomDataTypeEnum::toGuiName(Enum enumValue) { + if (initializedFlag == false) initialize(); + + const WbMacroCustomDataTypeEnum* enumInstance = findData(enumValue); + return enumInstance->guiName; +} + +/** + * Get an enumerated value corresponding to its GUI name. + * @param s + * Name of enumerated value. + * @param isValidOut + * If not NULL, it is set indicating that a + * enum value exists for the input name. + * @return + * Enumerated value. + */ +WbMacroCustomDataTypeEnum::Enum +WbMacroCustomDataTypeEnum::fromGuiName(const AString& guiName, bool* isValidOut) +{ + if (initializedFlag == false) initialize(); + + bool validFlag = false; + Enum enumValue = WbMacroCustomDataTypeEnum::enumData[0].enumValue; + + for (std::vector::iterator iter = enumData.begin(); + iter != enumData.end(); + iter++) { + const WbMacroCustomDataTypeEnum& d = *iter; + if (d.guiName == guiName) { + enumValue = d.enumValue; + validFlag = true; + break; + } + } + + if (isValidOut != 0) { + *isValidOut = validFlag; + } + else if (validFlag == false) { + CaretAssertMessage(0, AString("guiName " + guiName + "failed to match enumerated value for type WbMacroCustomDataTypeEnum")); + } + return enumValue; +} + +/** + * Get the integer code for a data type. + * + * @return + * Integer code for data type. + */ +int32_t +WbMacroCustomDataTypeEnum::toIntegerCode(Enum enumValue) +{ + if (initializedFlag == false) initialize(); + const WbMacroCustomDataTypeEnum* enumInstance = findData(enumValue); + return enumInstance->integerCode; +} + +/** + * Find the data type corresponding to an integer code. + * + * @param integerCode + * Integer code for enum. + * @param isValidOut + * If not NULL, on exit isValidOut will indicate if + * integer code is valid. + * @return + * Enum for integer code. + */ +WbMacroCustomDataTypeEnum::Enum +WbMacroCustomDataTypeEnum::fromIntegerCode(const int32_t integerCode, bool* isValidOut) +{ + if (initializedFlag == false) initialize(); + + bool validFlag = false; + Enum enumValue = WbMacroCustomDataTypeEnum::enumData[0].enumValue; + + for (std::vector::iterator iter = enumData.begin(); + iter != enumData.end(); + iter++) { + const WbMacroCustomDataTypeEnum& enumInstance = *iter; + if (enumInstance.integerCode == integerCode) { + enumValue = enumInstance.enumValue; + validFlag = true; + break; + } + } + + if (isValidOut != 0) { + *isValidOut = validFlag; + } + else if (validFlag == false) { + CaretAssertMessage(0, AString("Integer code " + AString::number(integerCode) + "failed to match enumerated value for type WbMacroCustomDataTypeEnum")); + } + return enumValue; +} + +/** + * Get all of the enumerated type values. The values can be used + * as parameters to toXXX() methods to get associated metadata. + * + * @param allEnums + * A vector that is OUTPUT containing all of the enumerated values. + */ +void +WbMacroCustomDataTypeEnum::getAllEnums(std::vector& allEnums) +{ + if (initializedFlag == false) initialize(); + + allEnums.clear(); + + for (std::vector::iterator iter = enumData.begin(); + iter != enumData.end(); + iter++) { + allEnums.push_back(iter->enumValue); + } +} + +/** + * Get all of the names of the enumerated type values. + * + * @param allNames + * A vector that is OUTPUT containing all of the names of the enumerated values. + * @param isSorted + * If true, the names are sorted in alphabetical order. + */ +void +WbMacroCustomDataTypeEnum::getAllNames(std::vector& allNames, const bool isSorted) +{ + if (initializedFlag == false) initialize(); + + allNames.clear(); + + for (std::vector::iterator iter = enumData.begin(); + iter != enumData.end(); + iter++) { + allNames.push_back(WbMacroCustomDataTypeEnum::toName(iter->enumValue)); + } + + if (isSorted) { + std::sort(allNames.begin(), allNames.end()); + } +} + +/** + * Get all of the GUI names of the enumerated type values. + * + * @param allNames + * A vector that is OUTPUT containing all of the GUI names of the enumerated values. + * @param isSorted + * If true, the names are sorted in alphabetical order. + */ +void +WbMacroCustomDataTypeEnum::getAllGuiNames(std::vector& allGuiNames, const bool isSorted) +{ + if (initializedFlag == false) initialize(); + + allGuiNames.clear(); + + for (std::vector::iterator iter = enumData.begin(); + iter != enumData.end(); + iter++) { + allGuiNames.push_back(WbMacroCustomDataTypeEnum::toGuiName(iter->enumValue)); + } + + if (isSorted) { + std::sort(allGuiNames.begin(), allGuiNames.end()); + } +} + diff --git a/src/GuiQt/WbMacroCustomDataTypeEnum.h b/src/GuiQt/WbMacroCustomDataTypeEnum.h new file mode 100644 index 000000000..20a290d3b --- /dev/null +++ b/src/GuiQt/WbMacroCustomDataTypeEnum.h @@ -0,0 +1,102 @@ +#ifndef __WB_MACRO_CUSTOM_DATA_TYPE_ENUM_H__ +#define __WB_MACRO_CUSTOM_DATA_TYPE_ENUM_H__ + +/*LICENSE_START*/ +/* + * Copyright (C) 2019 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + + +#include +#include +#include "AString.h" + +namespace caret { + +class WbMacroCustomDataTypeEnum { + +public: + /** + * Enumerated values. + */ + enum Enum { + /** */ + SURFACE + }; + + + ~WbMacroCustomDataTypeEnum(); + + static AString toName(Enum enumValue); + + static Enum fromName(const AString& name, bool* isValidOut); + + static AString toGuiName(Enum enumValue); + + static Enum fromGuiName(const AString& guiName, bool* isValidOut); + + static int32_t toIntegerCode(Enum enumValue); + + static Enum fromIntegerCode(const int32_t integerCode, bool* isValidOut); + + static void getAllEnums(std::vector& allEnums); + + static void getAllNames(std::vector& allNames, const bool isSorted); + + static void getAllGuiNames(std::vector& allGuiNames, const bool isSorted); + +private: + WbMacroCustomDataTypeEnum(const Enum enumValue, + const AString& name, + const AString& guiName); + + static const WbMacroCustomDataTypeEnum* findData(const Enum enumValue); + + /** Holds all instance of enum values and associated metadata */ + static std::vector enumData; + + /** Initialize instances that contain the enum values and metadata */ + static void initialize(); + + /** Indicates instance of enum values and metadata have been initialized */ + static bool initializedFlag; + + /** Auto generated integer codes */ + static int32_t integerCodeCounter; + + /** The enumerated type value for an instance */ + Enum enumValue; + + /** The integer code associated with an enumerated value */ + int32_t integerCode; + + /** The name, a text string that is identical to the enumerated value */ + AString name; + + /** A user-friendly name that is displayed in the GUI */ + AString guiName; +}; + +#ifdef __WB_MACRO_CUSTOM_DATA_TYPE_ENUM_DECLARE__ +std::vector WbMacroCustomDataTypeEnum::enumData; +bool WbMacroCustomDataTypeEnum::initializedFlag = false; +int32_t WbMacroCustomDataTypeEnum::integerCodeCounter = 0; +#endif // __WB_MACRO_CUSTOM_DATA_TYPE_ENUM_DECLARE__ + +} // namespace +#endif //__WB_MACRO_CUSTOM_DATA_TYPE_ENUM_H__ diff --git a/src/GuiQt/WbMacroCustomOperationManager.cxx b/src/GuiQt/WbMacroCustomOperationManager.cxx new file mode 100644 index 000000000..6bedcc40d --- /dev/null +++ b/src/GuiQt/WbMacroCustomOperationManager.cxx @@ -0,0 +1,254 @@ + +/*LICENSE_START*/ +/* + * Copyright (C) 2019 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + +#define __WB_MACRO_CUSTOM_OPERATION_MANAGER_DECLARE__ +#include "WbMacroCustomOperationManager.h" +#undef __WB_MACRO_CUSTOM_OPERATION_MANAGER_DECLARE__ + +#include + +#include "CaretAssert.h" +#include "CaretLogger.h" +#include "EventManager.h" +#include "EventSurfacesGet.h" +#include "GuiManager.h" +#include "Surface.h" +#include "WbMacroCustomDataTypeEnum.h" +#include "WbMacroCustomOperationSurfaceInterpolation.h" +#include "WbMacroCustomOperationTypeEnum.h" +#include "WuQMacroCommand.h" +#include "WuQMacroCommandParameter.h" +#include "WuQMessageBox.h" + +using namespace caret; + +/** + * \class caret::WbMacroCustomOperationManager + * \brief Mananager for macro custom operations + * \ingroup GuiQt + */ + +/** + * Constructor. + */ +WbMacroCustomOperationManager::WbMacroCustomOperationManager() +: CaretObject() +{ + +} + +/** + * Destructor. + */ +WbMacroCustomOperationManager::~WbMacroCustomOperationManager() +{ +} + +/** + * Get a description of this object's content. + * @return String describing this object's content. + */ +AString +WbMacroCustomOperationManager::toString() const +{ + return "WbMacroCustomOperationManager"; +} + +/** + * Is called to edit a macro command parameter with a CUSTOM_DATA data type + * + * @param parent + * Parent widget for any dialogs + * @param parameter + * Parameter for editing + * @return + * True if the parameter was modified + */ +bool +WbMacroCustomOperationManager::editCustomDataValueParameter(QWidget* parent, + WuQMacroCommandParameter* parameter) +{ + bool modFlag(false); + + CaretAssert(parent); + CaretAssert(parameter); + + if (parameter->getDataType() != WuQMacroDataValueTypeEnum::CUSTOM_DATA) { + CaretLogSevere("Attempting to edit non-custom data type as custom data type"); + return false; + } + + const QString typeName(parameter->getCustomDataType()); + bool nameValid(false); + const WbMacroCustomDataTypeEnum::Enum userType = WbMacroCustomDataTypeEnum::fromName(typeName, + &nameValid); + if ( ! nameValid) { + CaretLogSevere("\"" + + typeName + + "\" is not a valid name for a custom parameter"); + return false; + } + + QString errorMessage; + switch (userType) { + case WbMacroCustomDataTypeEnum::SURFACE: + { + EventSurfacesGet surfacesEvent; + EventManager::get()->sendEvent(surfacesEvent.getPointer()); + std::vector surfaces = surfacesEvent.getSurfaces(); + if (surfaces.empty()) { + errorMessage = "There are no surfaces available"; + } + else { + QStringList surfaceNamesList; + for (const auto s : surfaces) { + surfaceNamesList.append(s->getFileNameNoPath()); + } + + bool okFlag(false); + QString selectedName = QInputDialog::getItem(parent, + "Choose Surface", + parameter->getName(), + surfaceNamesList, + 0, + false, + &okFlag); + if (okFlag) { + parameter->setValue(selectedName); + modFlag = true; + } + } + } + break; + } + + if ( ! errorMessage.isEmpty()) { + WuQMessageBox::critical(parent, + "Set Value", + errorMessage); + } + + return modFlag; + +} + +/** + * Run a custom-defined macro command + * + * @param parent + * Parent widget for any dialogs + * @param customMacroCommand + * Custom macro command to run + * @param errorMessageOut + * Contains any error information or empty if no error + * @return + * True if command executed successfully, else false + */ +bool +WbMacroCustomOperationManager::executeCustomOperationMacroCommand(QWidget* parent, + const WuQMacroCommand* customMacroCommand, + QString& errorMessageOut) +{ + CaretAssert(parent); + CaretAssert(customMacroCommand); + errorMessageOut.clear(); + + if (customMacroCommand->getCommandType() != WuQMacroCommandTypeEnum::CUSTOM_OPERATION) { + errorMessageOut = "Requesting non-custom command execution in user-executor"; + return false; + } + + const QString customCommandName(customMacroCommand->getCustomOperationTypeName()); + bool nameValid(false); + const WbMacroCustomOperationTypeEnum::Enum commandType = WbMacroCustomOperationTypeEnum::fromName(customCommandName, + &nameValid); + if ( ! nameValid) { + errorMessageOut = ("\"" + + customCommandName + + "\" is not a valid name for a custom macro command"); + return false; + } + + bool successFlag(false); + switch (commandType) { + case WbMacroCustomOperationTypeEnum::SURFACE_INTERPOLATION: + successFlag = WbMacroCustomOperationSurfaceInterpolation::executeCommand(parent, + customMacroCommand, + errorMessageOut); + break; + } + + return successFlag; +} + +/** + * @return Names of custom operation defined macro commands + */ +std::vector +WbMacroCustomOperationManager::getNamesOfCustomOperationMacroCommands() +{ + std::vector names; + + WbMacroCustomOperationTypeEnum::getAllNames(names, + true); + + std::vector namesOut(names.begin(), + names.end()); + return namesOut; +} + +/** + * Get a new instance of a custom operation for the given macro command name + * + * @param customMacroCommandName + * Name of custom macro command + * @param errorMessageOut + * Contains any error information or empty if no error + * @return + * Pointer to command or NULL if not valid + */ +WuQMacroCommand* +WbMacroCustomOperationManager::newInstanceOfCustomOperationMacroCommand(const QString& customMacroCommandName, + QString& errorMessageOut) +{ + errorMessageOut.clear(); + + bool nameValid(false); + const WbMacroCustomOperationTypeEnum::Enum commandType = WbMacroCustomOperationTypeEnum::fromName(customMacroCommandName, + &nameValid); + if ( ! nameValid) { + errorMessageOut = ("\"" + + customMacroCommandName + + "\" is not a valid name for a custom macro command"); + return NULL; + } + + WuQMacroCommand* command(NULL); + + switch (commandType) { + case WbMacroCustomOperationTypeEnum::SURFACE_INTERPOLATION: + command = WbMacroCustomOperationSurfaceInterpolation::createCommand(errorMessageOut); + break; + } + + return command; +} + diff --git a/src/GuiQt/WbMacroCustomOperationManager.h b/src/GuiQt/WbMacroCustomOperationManager.h new file mode 100644 index 000000000..107e70300 --- /dev/null +++ b/src/GuiQt/WbMacroCustomOperationManager.h @@ -0,0 +1,75 @@ +#ifndef __WB_MACRO_CUSTOM_OPERATION_MANAGER_H__ +#define __WB_MACRO_CUSTOM_OPERATION_MANAGER_H__ + +/*LICENSE_START*/ +/* + * Copyright (C) 2019 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + + + +#include + +#include "CaretObject.h" +#include "WuQMacroCustomOperationManagerInterface.h" +class QWidget; + +namespace caret { + class WuQMacroCommand; + class WuQMacroCommandParameter; + + class WbMacroCustomOperationManager : public CaretObject, public WuQMacroCustomOperationManagerInterface { + + public: + WbMacroCustomOperationManager(); + + virtual ~WbMacroCustomOperationManager(); + + WbMacroCustomOperationManager(const WbMacroCustomOperationManager&) = delete; + + WbMacroCustomOperationManager& operator=(const WbMacroCustomOperationManager&) = delete; + + virtual bool editCustomDataValueParameter(QWidget* parent, + WuQMacroCommandParameter* parameter); + + virtual bool executeCustomOperationMacroCommand(QWidget* parent, + const WuQMacroCommand* macroCommand, + QString& errorMessageOut); + + virtual std::vector getNamesOfCustomOperationMacroCommands(); + + virtual WuQMacroCommand* newInstanceOfCustomOperationMacroCommand(const QString& customMacroCommandName, + QString& errorMessageOut); + + + // ADD_NEW_METHODS_HERE + + virtual AString toString() const; + + private: + + // ADD_NEW_MEMBERS_HERE + + }; + +#ifdef __WB_MACRO_CUSTOM_OPERATION_MANAGER_DECLARE__ + // +#endif // __WB_MACRO_CUSTOM_OPERATION_MANAGER_DECLARE__ + +} // namespace +#endif //__WB_MACRO_CUSTOM_OPERATION_MANAGER_H__ diff --git a/src/GuiQt/WbMacroCustomOperationSurfaceInterpolation.cxx b/src/GuiQt/WbMacroCustomOperationSurfaceInterpolation.cxx new file mode 100644 index 000000000..c84af1c29 --- /dev/null +++ b/src/GuiQt/WbMacroCustomOperationSurfaceInterpolation.cxx @@ -0,0 +1,112 @@ + +/*LICENSE_START*/ +/* + * Copyright (C) 2019 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + +#define __WB_MACRO_CUSTOM_OPERATION_SURFACE_INTERPOLATION_DECLARE__ +#include "WbMacroCustomOperationSurfaceInterpolation.h" +#undef __WB_MACRO_CUSTOM_OPERATION_SURFACE_INTERPOLATION_DECLARE__ + +#include "CaretAssert.h" +#include "WbMacroCustomDataTypeEnum.h" +#include "WuQMacroCommand.h" +#include "WuQMacroCommandParameter.h" + +using namespace caret; + + + +/** + * \class caret::WbMacroCustomOperationSurfaceInterpolation + * \brief Custom Macro Command for Surface Interpolation + * \ingroup GuiQt + */ + +/** + * Run the command + * + * @param parent + * Parent widget for any dialogs + * @param macroCommand + * macro command to run + * @param errorMessageOut + * Contains any error information or empty if no error + * @return + * True if command executed successfully, else false + */ +bool +WbMacroCustomOperationSurfaceInterpolation::executeCommand(QWidget* parent, + const WuQMacroCommand* macroCommand, + QString& errorMessageOut) +{ + bool successFlag(false); + + return successFlag; +} + +/** + * Get a new instance of the command + * + * @param errorMessageOut + * Contains any error information or empty if no error + * @return + * Pointer to command or NULL if not valid + */ +WuQMacroCommand* +WbMacroCustomOperationSurfaceInterpolation::createCommand(QString& errorMessageOut) +{ + errorMessageOut.clear(); + + const int32_t versionOne(1); + + WuQMacroCommandParameter* paramSurfaceOne = new WuQMacroCommandParameter(WuQMacroDataValueTypeEnum::CUSTOM_DATA, + "Starting Surface", + ""); + paramSurfaceOne->setCustomDataType(WbMacroCustomDataTypeEnum::toName(WbMacroCustomDataTypeEnum::SURFACE)); + + WuQMacroCommandParameter* paramSurfaceTwo = new WuQMacroCommandParameter(WuQMacroDataValueTypeEnum::CUSTOM_DATA, + "Ending Surface", + ""); + paramSurfaceTwo->setCustomDataType(WbMacroCustomDataTypeEnum::toName(WbMacroCustomDataTypeEnum::SURFACE)); + + WuQMacroCommand* command = WuQMacroCommand::newInstanceCustomCommand("SurfaceInterpolation", + versionOne, + "none", + "Surface Interpolation", + "Interpolate Between Two Surface", + 1.0, + errorMessageOut); + if (command != NULL) { + command->addParameter(paramSurfaceOne); + command->addParameter(paramSurfaceTwo); + command->addParameter(WuQMacroDataValueTypeEnum::FLOAT, + "Duration (secs)", + (float)5.0); + } + + return command; +} + +/** + * Destructor. + */ +WbMacroCustomOperationSurfaceInterpolation::~WbMacroCustomOperationSurfaceInterpolation() +{ +} + diff --git a/src/GuiQt/WbMacroCustomOperationSurfaceInterpolation.h b/src/GuiQt/WbMacroCustomOperationSurfaceInterpolation.h new file mode 100644 index 000000000..702cb71af --- /dev/null +++ b/src/GuiQt/WbMacroCustomOperationSurfaceInterpolation.h @@ -0,0 +1,65 @@ +#ifndef __WB_MACRO_CUSTOM_OPERATION_SURFACE_INTERPOLATION_H__ +#define __WB_MACRO_CUSTOM_OPERATION_SURFACE_INTERPOLATION_H__ + +/*LICENSE_START*/ +/* + * Copyright (C) 2019 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + + + +#include + +class QString; +class QWidget; + +namespace caret { + + class WuQMacroCommand; + + class WbMacroCustomOperationSurfaceInterpolation { + + public: + static bool executeCommand(QWidget* parent, + const WuQMacroCommand* macroCommand, + QString& errorMessageOut); + + static WuQMacroCommand* createCommand(QString& errorMessageOut); + + WbMacroCustomOperationSurfaceInterpolation() = delete; + + virtual ~WbMacroCustomOperationSurfaceInterpolation(); + + WbMacroCustomOperationSurfaceInterpolation(const WbMacroCustomOperationSurfaceInterpolation&) = delete; + + WbMacroCustomOperationSurfaceInterpolation& operator=(const WbMacroCustomOperationSurfaceInterpolation&) = delete; + + + // ADD_NEW_METHODS_HERE + + private: + // ADD_NEW_MEMBERS_HERE + + }; + +#ifdef __WB_MACRO_CUSTOM_OPERATION_SURFACE_INTERPOLATION_DECLARE__ + // +#endif // __WB_MACRO_CUSTOM_OPERATION_SURFACE_INTERPOLATION_DECLARE__ + +} // namespace +#endif //__WB_MACRO_CUSTOM_OPERATION_SURFACE_INTERPOLATION_H__ diff --git a/src/GuiQt/WbMacroCustomOperationTypeEnum.cxx b/src/GuiQt/WbMacroCustomOperationTypeEnum.cxx new file mode 100644 index 000000000..79a7f156e --- /dev/null +++ b/src/GuiQt/WbMacroCustomOperationTypeEnum.cxx @@ -0,0 +1,369 @@ + +/*LICENSE_START*/ +/* + * Copyright (C) 2019 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + +#include +#define __WB_MACRO_CUSTOM_OPERATION_TYPE_ENUM_DECLARE__ +#include "WbMacroCustomOperationTypeEnum.h" +#undef __WB_MACRO_CUSTOM_OPERATION_TYPE_ENUM_DECLARE__ + +#include "CaretAssert.h" + +using namespace caret; + + +/** + * \class caret::WbMacroCustomOperationTypeEnum + * \brief Enumerated type for a user defined macro command + * + * Using this enumerated type in the GUI with an EnumComboBoxTemplate + * + * Header File (.h) + * Forward declare the data type: + * class EnumComboBoxTemplate; + * + * Declare the member: + * EnumComboBoxTemplate* m_WbMacroCustomOperationTypeEnumComboBox; + * + * Declare a slot that is called when user changes selection + * private slots: + * void WbMacroCustomOperationTypeEnumComboBoxItemActivated(); + * + * Implementation File (.cxx) + * Include the header files + * #include "EnumComboBoxTemplate.h" + * #include "WbMacroCustomOperationTypeEnum.h" + * + * Instatiate: + * m_WbMacroCustomOperationTypeEnumComboBox = new EnumComboBoxTemplate(this); + * m_WbMacroCustomOperationTypeEnumComboBox->setup(); + * + * Get notified when the user changes the selection: + * QObject::connect(m_WbMacroCustomOperationTypeEnumComboBox, SIGNAL(itemActivated()), + * this, SLOT(WbMacroCustomOperationTypeEnumComboBoxItemActivated())); + * + * Update the selection: + * m_WbMacroCustomOperationTypeEnumComboBox->setSelectedItem(NEW_VALUE); + * + * Read the selection: + * const WbMacroCustomOperationTypeEnum::Enum VARIABLE = m_WbMacroCustomOperationTypeEnumComboBox->getSelectedItem(); + * + */ + +/** + * Constructor. + * + * @param enumValue + * An enumerated value. + * @param name + * Name of enumerated value. + * + * @param guiName + * User-friendly name for use in user-interface. + */ +WbMacroCustomOperationTypeEnum::WbMacroCustomOperationTypeEnum(const Enum enumValue, + const AString& name, + const AString& guiName) +{ + this->enumValue = enumValue; + this->integerCode = integerCodeCounter++; + this->name = name; + this->guiName = guiName; +} + +/** + * Destructor. + */ +WbMacroCustomOperationTypeEnum::~WbMacroCustomOperationTypeEnum() +{ +} + +/** + * Initialize the enumerated metadata. + */ +void +WbMacroCustomOperationTypeEnum::initialize() +{ + if (initializedFlag) { + return; + } + initializedFlag = true; + + enumData.push_back(WbMacroCustomOperationTypeEnum(SURFACE_INTERPOLATION, + "SURFACE_INTERPOLATION", + "Surface Interpolation")); + +} + +/** + * Find the data for and enumerated value. + * @param enumValue + * The enumerated value. + * @return Pointer to data for this enumerated type + * or NULL if no data for type or if type is invalid. + */ +const WbMacroCustomOperationTypeEnum* +WbMacroCustomOperationTypeEnum::findData(const Enum enumValue) +{ + if (initializedFlag == false) initialize(); + + size_t num = enumData.size(); + for (size_t i = 0; i < num; i++) { + const WbMacroCustomOperationTypeEnum* d = &enumData[i]; + if (d->enumValue == enumValue) { + return d; + } + } + + return NULL; +} + +/** + * Get a string representation of the enumerated type. + * @param enumValue + * Enumerated value. + * @return + * String representing enumerated value. + */ +AString +WbMacroCustomOperationTypeEnum::toName(Enum enumValue) { + if (initializedFlag == false) initialize(); + + const WbMacroCustomOperationTypeEnum* enumInstance = findData(enumValue); + return enumInstance->name; +} + +/** + * Get an enumerated value corresponding to its name. + * @param name + * Name of enumerated value. + * @param isValidOut + * If not NULL, it is set indicating that a + * enum value exists for the input name. + * @return + * Enumerated value. + */ +WbMacroCustomOperationTypeEnum::Enum +WbMacroCustomOperationTypeEnum::fromName(const AString& name, bool* isValidOut) +{ + if (initializedFlag == false) initialize(); + + bool validFlag = false; + Enum enumValue = WbMacroCustomOperationTypeEnum::enumData[0].enumValue; + + for (std::vector::iterator iter = enumData.begin(); + iter != enumData.end(); + iter++) { + const WbMacroCustomOperationTypeEnum& d = *iter; + if (d.name == name) { + enumValue = d.enumValue; + validFlag = true; + break; + } + } + + if (isValidOut != 0) { + *isValidOut = validFlag; + } + else if (validFlag == false) { + CaretAssertMessage(0, AString("Name " + name + "failed to match enumerated value for type WbMacroCustomOperationTypeEnum")); + } + return enumValue; +} + +/** + * Get a GUI string representation of the enumerated type. + * @param enumValue + * Enumerated value. + * @return + * String representing enumerated value. + */ +AString +WbMacroCustomOperationTypeEnum::toGuiName(Enum enumValue) { + if (initializedFlag == false) initialize(); + + const WbMacroCustomOperationTypeEnum* enumInstance = findData(enumValue); + return enumInstance->guiName; +} + +/** + * Get an enumerated value corresponding to its GUI name. + * @param s + * Name of enumerated value. + * @param isValidOut + * If not NULL, it is set indicating that a + * enum value exists for the input name. + * @return + * Enumerated value. + */ +WbMacroCustomOperationTypeEnum::Enum +WbMacroCustomOperationTypeEnum::fromGuiName(const AString& guiName, bool* isValidOut) +{ + if (initializedFlag == false) initialize(); + + bool validFlag = false; + Enum enumValue = WbMacroCustomOperationTypeEnum::enumData[0].enumValue; + + for (std::vector::iterator iter = enumData.begin(); + iter != enumData.end(); + iter++) { + const WbMacroCustomOperationTypeEnum& d = *iter; + if (d.guiName == guiName) { + enumValue = d.enumValue; + validFlag = true; + break; + } + } + + if (isValidOut != 0) { + *isValidOut = validFlag; + } + else if (validFlag == false) { + CaretAssertMessage(0, AString("guiName " + guiName + "failed to match enumerated value for type WbMacroCustomOperationTypeEnum")); + } + return enumValue; +} + +/** + * Get the integer code for a data type. + * + * @return + * Integer code for data type. + */ +int32_t +WbMacroCustomOperationTypeEnum::toIntegerCode(Enum enumValue) +{ + if (initializedFlag == false) initialize(); + const WbMacroCustomOperationTypeEnum* enumInstance = findData(enumValue); + return enumInstance->integerCode; +} + +/** + * Find the data type corresponding to an integer code. + * + * @param integerCode + * Integer code for enum. + * @param isValidOut + * If not NULL, on exit isValidOut will indicate if + * integer code is valid. + * @return + * Enum for integer code. + */ +WbMacroCustomOperationTypeEnum::Enum +WbMacroCustomOperationTypeEnum::fromIntegerCode(const int32_t integerCode, bool* isValidOut) +{ + if (initializedFlag == false) initialize(); + + bool validFlag = false; + Enum enumValue = WbMacroCustomOperationTypeEnum::enumData[0].enumValue; + + for (std::vector::iterator iter = enumData.begin(); + iter != enumData.end(); + iter++) { + const WbMacroCustomOperationTypeEnum& enumInstance = *iter; + if (enumInstance.integerCode == integerCode) { + enumValue = enumInstance.enumValue; + validFlag = true; + break; + } + } + + if (isValidOut != 0) { + *isValidOut = validFlag; + } + else if (validFlag == false) { + CaretAssertMessage(0, AString("Integer code " + AString::number(integerCode) + "failed to match enumerated value for type WbMacroCustomOperationTypeEnum")); + } + return enumValue; +} + +/** + * Get all of the enumerated type values. The values can be used + * as parameters to toXXX() methods to get associated metadata. + * + * @param allEnums + * A vector that is OUTPUT containing all of the enumerated values. + */ +void +WbMacroCustomOperationTypeEnum::getAllEnums(std::vector& allEnums) +{ + if (initializedFlag == false) initialize(); + + allEnums.clear(); + + for (std::vector::iterator iter = enumData.begin(); + iter != enumData.end(); + iter++) { + allEnums.push_back(iter->enumValue); + } +} + +/** + * Get all of the names of the enumerated type values. + * + * @param allNames + * A vector that is OUTPUT containing all of the names of the enumerated values. + * @param isSorted + * If true, the names are sorted in alphabetical order. + */ +void +WbMacroCustomOperationTypeEnum::getAllNames(std::vector& allNames, const bool isSorted) +{ + if (initializedFlag == false) initialize(); + + allNames.clear(); + + for (std::vector::iterator iter = enumData.begin(); + iter != enumData.end(); + iter++) { + allNames.push_back(WbMacroCustomOperationTypeEnum::toName(iter->enumValue)); + } + + if (isSorted) { + std::sort(allNames.begin(), allNames.end()); + } +} + +/** + * Get all of the GUI names of the enumerated type values. + * + * @param allNames + * A vector that is OUTPUT containing all of the GUI names of the enumerated values. + * @param isSorted + * If true, the names are sorted in alphabetical order. + */ +void +WbMacroCustomOperationTypeEnum::getAllGuiNames(std::vector& allGuiNames, const bool isSorted) +{ + if (initializedFlag == false) initialize(); + + allGuiNames.clear(); + + for (std::vector::iterator iter = enumData.begin(); + iter != enumData.end(); + iter++) { + allGuiNames.push_back(WbMacroCustomOperationTypeEnum::toGuiName(iter->enumValue)); + } + + if (isSorted) { + std::sort(allGuiNames.begin(), allGuiNames.end()); + } +} + diff --git a/src/GuiQt/WbMacroCustomOperationTypeEnum.h b/src/GuiQt/WbMacroCustomOperationTypeEnum.h new file mode 100644 index 000000000..8d9370b48 --- /dev/null +++ b/src/GuiQt/WbMacroCustomOperationTypeEnum.h @@ -0,0 +1,102 @@ +#ifndef __WB_MACRO_CUSTOM_OPERATION_TYPE_ENUM_H__ +#define __WB_MACRO_CUSTOM_OPERATION_TYPE_ENUM_H__ + +/*LICENSE_START*/ +/* + * Copyright (C) 2019 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + + +#include +#include +#include "AString.h" + +namespace caret { + +class WbMacroCustomOperationTypeEnum { + +public: + /** + * Enumerated values. + */ + enum Enum { + /** */ + SURFACE_INTERPOLATION + }; + + + ~WbMacroCustomOperationTypeEnum(); + + static AString toName(Enum enumValue); + + static Enum fromName(const AString& name, bool* isValidOut); + + static AString toGuiName(Enum enumValue); + + static Enum fromGuiName(const AString& guiName, bool* isValidOut); + + static int32_t toIntegerCode(Enum enumValue); + + static Enum fromIntegerCode(const int32_t integerCode, bool* isValidOut); + + static void getAllEnums(std::vector& allEnums); + + static void getAllNames(std::vector& allNames, const bool isSorted); + + static void getAllGuiNames(std::vector& allGuiNames, const bool isSorted); + +private: + WbMacroCustomOperationTypeEnum(const Enum enumValue, + const AString& name, + const AString& guiName); + + static const WbMacroCustomOperationTypeEnum* findData(const Enum enumValue); + + /** Holds all instance of enum values and associated metadata */ + static std::vector enumData; + + /** Initialize instances that contain the enum values and metadata */ + static void initialize(); + + /** Indicates instance of enum values and metadata have been initialized */ + static bool initializedFlag; + + /** Auto generated integer codes */ + static int32_t integerCodeCounter; + + /** The enumerated type value for an instance */ + Enum enumValue; + + /** The integer code associated with an enumerated value */ + int32_t integerCode; + + /** The name, a text string that is identical to the enumerated value */ + AString name; + + /** A user-friendly name that is displayed in the GUI */ + AString guiName; +}; + +#ifdef __WB_MACRO_CUSTOM_OPERATION_TYPE_ENUM_DECLARE__ +std::vector WbMacroCustomOperationTypeEnum::enumData; +bool WbMacroCustomOperationTypeEnum::initializedFlag = false; +int32_t WbMacroCustomOperationTypeEnum::integerCodeCounter = 0; +#endif // __WB_MACRO_CUSTOM_OPERATION_TYPE_ENUM_DECLARE__ + +} // namespace +#endif //__WB_MACRO_CUSTOM_OPERATION_TYPE_ENUM_H__ diff --git a/src/GuiQt/GuiMacroHelper.cxx b/src/GuiQt/WbMacroHelper.cxx similarity index 85% rename from src/GuiQt/GuiMacroHelper.cxx rename to src/GuiQt/WbMacroHelper.cxx index 5e8323d53..bebfb65c3 100644 --- a/src/GuiQt/GuiMacroHelper.cxx +++ b/src/GuiQt/WbMacroHelper.cxx @@ -19,12 +19,13 @@ */ /*LICENSE_END*/ -#define __GUI_MACRO_HELPER_DECLARE__ -#include "GuiMacroHelper.h" -#undef __GUI_MACRO_HELPER_DECLARE__ +#define __WB_MACRO_HELPER_DECLARE__ +#include "WbMacroHelper.h" +#undef __WB_MACRO_HELPER_DECLARE__ #include "Brain.h" #include "CaretAssert.h" +#include "CaretLogger.h" #include "CaretPreferences.h" #include "EventManager.h" #include "EventSceneActive.h" @@ -34,14 +35,19 @@ #include "SceneFile.h" #include "SceneInfo.h" #include "SessionManager.h" +#include "WbMacroCustomOperationTypeEnum.h" +#include "WbMacroCustomDataTypeEnum.h" +#include "WuQMacroCommand.h" +#include "WuQMacroCommandParameter.h" #include "WuQMacroGroup.h" +#include "WuQMessageBox.h" using namespace caret; /** - * \class caret::GuiMacroHelper + * \class caret::WbMacroHelper * \brief Implementation of WuQMacroHelperInterface that provides macro groups * \ingroup GuiQt */ @@ -49,7 +55,7 @@ using namespace caret; /** * Constructor. */ -GuiMacroHelper::GuiMacroHelper(QObject* parent) +WbMacroHelper::WbMacroHelper(QObject* parent) : WuQMacroHelperInterface(parent) { EventManager::get()->addEventListener(this, EventTypeEnum::EVENT_USER_INTERFACE_UPDATE); @@ -58,7 +64,7 @@ GuiMacroHelper::GuiMacroHelper(QObject* parent) /** * Destructor. */ -GuiMacroHelper::~GuiMacroHelper() +WbMacroHelper::~WbMacroHelper() { EventManager::get()->removeAllEventsFromListener(this); } @@ -70,7 +76,7 @@ GuiMacroHelper::~GuiMacroHelper() * The event. */ void -GuiMacroHelper::receiveEvent(Event* event) +WbMacroHelper::receiveEvent(Event* event) { CaretAssert(event); if (event->getEventType() == EventTypeEnum::EVENT_USER_INTERFACE_UPDATE) { @@ -83,7 +89,7 @@ GuiMacroHelper::receiveEvent(Event* event) * @return All available macro groups */ std::vector -GuiMacroHelper::getMacroGroups() +WbMacroHelper::getMacroGroups() { std::vector macroGroups; @@ -110,7 +116,7 @@ GuiMacroHelper::getMacroGroups() * Macro that is modified */ void -GuiMacroHelper::macroWasModified(WuQMacro* macro) +WbMacroHelper::macroWasModified(WuQMacro* macro) { /* * Need to write to preferences if macro is from preferences @@ -132,7 +138,7 @@ GuiMacroHelper::macroWasModified(WuQMacro* macro) * Macro Group that is modified */ void -GuiMacroHelper::macroGroupWasModified(WuQMacroGroup* macroGroup) +WbMacroHelper::macroGroupWasModified(WuQMacroGroup* macroGroup) { /** * Need to write to preferences if macro group is from preferences @@ -145,3 +151,4 @@ GuiMacroHelper::macroGroupWasModified(WuQMacroGroup* macroGroup) EventManager::get()->sendEvent(EventUserInterfaceUpdate().getPointer()); } + diff --git a/src/GuiQt/GuiMacroHelper.h b/src/GuiQt/WbMacroHelper.h similarity index 68% rename from src/GuiQt/GuiMacroHelper.h rename to src/GuiQt/WbMacroHelper.h index b8d08d6c4..731319229 100644 --- a/src/GuiQt/GuiMacroHelper.h +++ b/src/GuiQt/WbMacroHelper.h @@ -1,5 +1,5 @@ -#ifndef __GUI_MACRO_HELPER_H__ -#define __GUI_MACRO_HELPER_H__ +#ifndef __WB_MACRO_HELPER_H__ +#define __WB_MACRO_HELPER_H__ /*LICENSE_START*/ /* @@ -27,28 +27,29 @@ #include "EventListenerInterface.h" #include "WuQMacroHelperInterface.h" +class QWidget; namespace caret { - class GuiMacroHelper : public WuQMacroHelperInterface, public EventListenerInterface { + class WbMacroHelper : public WuQMacroHelperInterface, public EventListenerInterface { Q_OBJECT public: - GuiMacroHelper(QObject* parent); + WbMacroHelper(QObject* parent); - virtual ~GuiMacroHelper(); + virtual ~WbMacroHelper(); - GuiMacroHelper(const GuiMacroHelper&) = delete; + WbMacroHelper(const WbMacroHelper&) = delete; - GuiMacroHelper& operator=(const GuiMacroHelper&) = delete; + WbMacroHelper& operator=(const WbMacroHelper&) = delete; void receiveEvent(Event* event) override; - virtual std::vector getMacroGroups(); + virtual std::vector getMacroGroups() override; - virtual void macroWasModified(WuQMacro* macro); + virtual void macroWasModified(WuQMacro* macro) override; - virtual void macroGroupWasModified(WuQMacroGroup* macroGroup); + virtual void macroGroupWasModified(WuQMacroGroup* macroGroup) override; // ADD_NEW_METHODS_HERE @@ -57,9 +58,9 @@ namespace caret { }; -#ifdef __GUI_MACRO_HELPER_DECLARE__ +#ifdef __WB_MACRO_HELPER_DECLARE__ // -#endif // __GUI_MACRO_HELPER_DECLARE__ +#endif // __WB_MACRO_HELPER_DECLARE__ } // namespace -#endif //__GUI_MACRO_HELPER_H__ +#endif //__WB_MACRO_HELPER_H__ diff --git a/src/GuiQt/WuQMacroCustomOperationManagerInterface.h b/src/GuiQt/WuQMacroCustomOperationManagerInterface.h new file mode 100644 index 000000000..808edf244 --- /dev/null +++ b/src/GuiQt/WuQMacroCustomOperationManagerInterface.h @@ -0,0 +1,103 @@ +#ifndef __WU_Q_MACRO_CUSTOM_OPERATION_MANAGER_INTERFACE_H__ +#define __WU_Q_MACRO_CUSTOM_OPERATION_MANAGER_INTERFACE_H__ + +/*LICENSE_START*/ +/* + * Copyright (C) 2019 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + + + +class QString; +class QWidget; + +namespace caret { + class WuQMacroCommand; + class WuQMacroCommandParameter; + + class WuQMacroCustomOperationManagerInterface { + + public: + WuQMacroCustomOperationManagerInterface() { } + + virtual ~WuQMacroCustomOperationManagerInterface() { } + + WuQMacroCustomOperationManagerInterface(const WuQMacroCustomOperationManagerInterface&) = delete; + + WuQMacroCustomOperationManagerInterface& operator=(const WuQMacroCustomOperationManagerInterface&) = delete; + + /** + * Is called to edit a macro command parameter with a CUSTOM_DATA data type + * + * @param parent + * Parent widget for any dialogs + * @param parameter + * Parameter for editing + * @return + * True if the parameter was modified + */ + virtual bool editCustomDataValueParameter(QWidget* parent, + WuQMacroCommandParameter* parameter) = 0; + + /** + * Run a custom-defined macro command + * + * @param parent + * Parent widget for any dialogs + * @param customMacroCommand + * Custom macro command to run + * @param errorMessageOut + * Contains any error information or empty if no error + * @return + * True if command executed successfully, else false + */ + virtual bool executeCustomOperationMacroCommand(QWidget* parent, + const WuQMacroCommand* macroCommand, + QString& errorMessageOut) = 0; + + /** + * @return Names of custom operation defined macro commands + */ + virtual std::vector getNamesOfCustomOperationMacroCommands() = 0; + + /** + * Get a new instance of a custom operation for the given macro command name + * + * @param customMacroCommandName + * Name of custom macro command + * @param errorMessageOut + * Contains any error information or empty if no error + * @return + * Pointer to command or NULL if not valid + */ + virtual WuQMacroCommand* newInstanceOfCustomOperationMacroCommand(const QString& customMacroCommandName, + QString& errorMessageOut) = 0; + + // ADD_NEW_METHODS_HERE + + private: + // ADD_NEW_MEMBERS_HERE + + }; + +#ifdef __WU_Q_MACRO_CUSTOM_OPERATION_MANAGER_INTERFACE_DECLARE__ + // +#endif // __WU_Q_MACRO_CUSTOM_OPERATION_MANAGER_INTERFACE_DECLARE__ + +} // namespace +#endif //__WU_Q_MACRO_CUSTOM_OPERATION_MANAGER_INTERFACE_H__ diff --git a/src/GuiQt/WuQMacroDialog.cxx b/src/GuiQt/WuQMacroDialog.cxx index cfb73c6a0..f3d13a17a 100644 --- a/src/GuiQt/WuQMacroDialog.cxx +++ b/src/GuiQt/WuQMacroDialog.cxx @@ -38,6 +38,7 @@ #include #include #include +#include #include #include #include @@ -104,29 +105,38 @@ WuQMacroDialog::WuQMacroDialog(QWidget* parent) row++; gridLayout->addWidget(createMacroRunAndEditingToolButtons(), row, 0, 1, 3); row++; - gridLayout->addWidget(createMacroAndCommandSelectionWidget(), row, 0, 1, 3); - row++; for (int32_t iRow = 0; iRow < gridLayout->rowCount(); iRow++) { gridLayout->setRowStretch(iRow, 0); } + QWidget* macroSelectionWidget = createMacroAndCommandSelectionWidget(); m_macroWidget = createMacroDisplayWidget(); m_commandWidget = createCommandDisplayWidget(); m_stackedWidget = new QStackedWidget(); m_stackedWidget->addWidget(m_macroWidget); m_stackedWidget->addWidget(m_commandWidget); m_stackedWidget->setCurrentWidget(m_macroWidget); - + QScrollArea* stackedScrollArea = new QScrollArea(); + stackedScrollArea->setWidget(m_stackedWidget); + stackedScrollArea->setWidgetResizable(true); + m_dialogButtonBox = new QDialogButtonBox(QDialogButtonBox::Close); QObject::connect(m_dialogButtonBox, &QDialogButtonBox::rejected, this, &WuQMacroDialog::close); QObject::connect(m_dialogButtonBox, &QDialogButtonBox::clicked, this, &WuQMacroDialog::buttonBoxButtonClicked); + QSplitter* splitter = new QSplitter(); + splitter->setOrientation(Qt::Vertical); + macroSelectionWidget->setMinimumHeight(50); + splitter->addWidget(macroSelectionWidget); + stackedScrollArea->setMinimumHeight(50); + splitter->addWidget(stackedScrollArea); + QVBoxLayout* dialogLayout = new QVBoxLayout(this); dialogLayout->addLayout(gridLayout); - dialogLayout->addWidget(m_stackedWidget); + dialogLayout->addWidget(splitter); dialogLayout->addWidget(m_dialogButtonBox); updateDialogContents(); @@ -465,6 +475,7 @@ WuQMacroDialog::createCommandDisplayWidget() m_commandToolTipTextEdit->setReadOnly(true); m_commandToolTipTextEdit->setMaximumHeight(100); + QLabel* parametersLabel = new QLabel("Parameters: "); QGridLayout* layout = new QGridLayout(); layout->setContentsMargins(0, 0, 0, 0); layout->setColumnStretch(0, 0); @@ -486,13 +497,14 @@ WuQMacroDialog::createCommandDisplayWidget() layout->addWidget(toolTipLabel, row, 0, (Qt::AlignLeft | Qt::AlignTop)); layout->addWidget(m_commandToolTipTextEdit, row, 1); row++; + layout->addWidget(parametersLabel, row, 0, 1, 2, Qt::AlignLeft); + row++; QWidget* parametersWidget = new QWidget(); m_parameterWidgetsGridLayout = new QGridLayout(parametersWidget); - QScrollArea* parameterScrollArea = new QScrollArea(); - parameterScrollArea->setWidget(parametersWidget); - parameterScrollArea->setWidgetResizable(true); - + m_parameterWidgetsGridLayout->setColumnStretch(0, 0); + m_parameterWidgetsGridLayout->setColumnStretch(1, 100); + QHBoxLayout* titleLayout = new QHBoxLayout(); titleLayout->setContentsMargins(0, 0, 0, 0); titleLayout->addWidget(new QLabel("Command")); @@ -502,7 +514,7 @@ WuQMacroDialog::createCommandDisplayWidget() QVBoxLayout* widgetLayout = new QVBoxLayout(widget); widgetLayout->addLayout(titleLayout); widgetLayout->addLayout(layout); - widgetLayout->addWidget(parameterScrollArea); + widgetLayout->addWidget(parametersWidget); widgetLayout->addStretch(); return widget; @@ -674,7 +686,7 @@ WuQMacroDialog::updateCommandWidget(WuQMacroCommand* command) if (command != NULL) { title = command->text(); name = command->getObjectName(); - type = WuQMacroClassTypeEnum::toGuiName(command->getClassType()); + type = WuQMacroWidgetTypeEnum::toGuiName(command->getWidgetType()); toolTip = command->getObjectToolTip(); delay = command->getDelayInSeconds(); } @@ -1120,7 +1132,133 @@ WuQMacroDialog::editingDeleteToolButtonClicked() void WuQMacroDialog::editingInsertToolButtonClicked() { - std::cout << "Inserrt button clicked" << std::endl; + WuQMacroGroup* macroGroup = getSelectedMacroGroup(); + if (macroGroup == NULL) { + return; + } + + bool addMacroFlag(true); + bool addMacroCommandFlag(false); + + switch (getSelectedItemType()) { + case WuQMacroStandardItemTypeEnum::INVALID: + break; + case WuQMacroStandardItemTypeEnum::MACRO: + addMacroCommandFlag = true; + break; + case WuQMacroStandardItemTypeEnum::MACRO_COMMAND: + addMacroCommandFlag = true; + break; + } + + QMenu menu(m_editingInsertToolButton); + if (addMacroFlag) { + menu.addAction("Insert New Macro...", + this, + &WuQMacroDialog::insertMenuNewMacroSelected); + } + if (addMacroCommandFlag) { + menu.addAction("Insert New Command...", + this, + &WuQMacroDialog::insertMenuNewMacroCommandSelected); + } + menu.exec(m_editingInsertToolButton->mapToGlobal(QPoint(0, 0))); +} + +/** + * Called when insert new macro menu item selected + */ +void +WuQMacroDialog::insertMenuNewMacroSelected() +{ + WuQMacroGroup* macroGroup = getSelectedMacroGroup(); + CaretAssert(macroGroup); + + bool okFlag(false); + const QString macroName = QInputDialog::getText(m_editingInsertToolButton, "Create Macro", "New Macro Name", + QLineEdit::Normal, + "", + &okFlag); + if (okFlag) { + if ( ! macroName.isEmpty()) { + WuQMacro* macro = new WuQMacro(); + macro->setName(macroName); + macroGroup->addMacro(macro); + updateDialogContents(); + } + } +} + +/** + * Called when insert new macro command menu item selected + */ +void +WuQMacroDialog::insertMenuNewMacroCommandSelected() +{ + std::vector customOperationNames = WuQMacroManager::instance()->getNamesOfCustomOperationMacroCommands(); + QStringList namesStringList; + for (const auto name : customOperationNames) { + namesStringList.append(name); + } + + if (namesStringList.isEmpty()) { + QMessageBox::critical(m_editingInsertToolButton, + "Error", + "No custom operations available"); + return; + } + + bool okFlag(false); + QString selectedName = QInputDialog::getItem(m_editingInsertToolButton, + "Choose Operation", + "New Operation", + namesStringList, + 0, + false, + &okFlag); + + if (okFlag + && ( ! selectedName.isEmpty())) { + QString errorMessage; + WuQMacroCommand* newCommand = WuQMacroManager::instance()->newInstanceOfCustomOperationMacroCommand(selectedName, + errorMessage); + if (newCommand == NULL) { + QMessageBox::critical(m_editingInsertToolButton, + "Error", + errorMessage); + return; + } + + switch (getSelectedItemType()) { + case WuQMacroStandardItemTypeEnum::INVALID: + CaretAssert(0); + break; + case WuQMacroStandardItemTypeEnum::MACRO: + { + WuQMacro* macro = getSelectedMacro(); + CaretAssert(macro); + macro->insertMacroCommandAtIndex(0, newCommand); + } + break; + case WuQMacroStandardItemTypeEnum::MACRO_COMMAND: + { + WuQMacro* macro = getSelectedMacro(); + CaretAssert(macro); + const WuQMacroCommand* selectedCommand = getSelectedMacroCommand(); + CaretAssert(selectedCommand); + const int32_t selectedIndex = macro->getIndexOfMacroCommand(selectedCommand); + macro->insertMacroCommandAtIndex(selectedIndex + 1, + newCommand); + } + } + + WuQMacroGroup* selectedGroup = getSelectedMacroGroup(); + CaretAssert(selectedGroup); + QModelIndex selectedIndex = selectedGroup->indexFromItem(newCommand); + m_treeView->setCurrentIndex(selectedIndex); + + updateDialogContents(); + } } /** @@ -1130,16 +1268,16 @@ WuQMacroDialog::editingInsertToolButtonClicked() void WuQMacroDialog::updateEditingToolButtons() { + WuQMacroGroup* macroGroup = getSelectedMacroGroup(); + WuQMacro* macro = getSelectedMacro(); + WuQMacroCommand* command = getSelectedMacroCommand(); + bool runValid(false); - bool insertValid(false); + bool insertValid(macroGroup != NULL); bool deleteValid(false); bool moveUpValid(false); bool moveDownValid(false); - WuQMacroGroup* macroGroup = getSelectedMacroGroup(); - WuQMacro* macro = getSelectedMacro(); - WuQMacroCommand* command = getSelectedMacroCommand(); - switch (getSelectedItemType()) { case WuQMacroStandardItemTypeEnum::INVALID: break; @@ -1338,6 +1476,15 @@ CommandParameterWidget::pushButtonClicked() } } break; + case WuQMacroDataValueTypeEnum::CUSTOM_DATA: + { + validFlag = WuQMacroManager::instance()->editCustomDataValueParameter(m_pushButton, + m_parameter); + if (validFlag) { + dataValue = m_parameter->getValue(); + } + } + break; case WuQMacroDataValueTypeEnum::FLOAT: { bool ok(false); diff --git a/src/GuiQt/WuQMacroDialog.h b/src/GuiQt/WuQMacroDialog.h index b6bf89096..464b22bdb 100644 --- a/src/GuiQt/WuQMacroDialog.h +++ b/src/GuiQt/WuQMacroDialog.h @@ -107,6 +107,10 @@ namespace caret { void commandParamaterDataChanged(int); + void insertMenuNewMacroSelected(); + + void insertMenuNewMacroCommandSelected(); + private: enum class ValueIndex { ONE, diff --git a/src/GuiQt/WuQMacroExecutor.cxx b/src/GuiQt/WuQMacroExecutor.cxx index eab6b3326..a368cd889 100644 --- a/src/GuiQt/WuQMacroExecutor.cxx +++ b/src/GuiQt/WuQMacroExecutor.cxx @@ -50,6 +50,7 @@ #include "WuQMacro.h" #include "WuQMacroCommand.h" #include "WuQMacroCommandParameter.h" +#include "WuQMacroManager.h" #include "WuQMacroMouseEventInfo.h" #include "WuQMacroMouseEventWidgetInterface.h" #include "WuQMacroSignalEmitter.h" @@ -277,7 +278,7 @@ WuQMacroExecutor::findObjectByName(const QString& objectName) const */ bool WuQMacroExecutor::runMacro(const WuQMacro* macro, - QObject* window, + QWidget* window, std::vector& otherObjectParents, const WuQMacroExecutorOptions* executorOptions, QString& errorMessageOut) const @@ -322,9 +323,32 @@ WuQMacroExecutor::runMacro(const WuQMacro* macro, } QString commandErrorMessage; - if ( ! runMacroCommand(mc, - object, - commandErrorMessage)) { + bool successFlag(false); + switch (mc->getCommandType()) { + case WuQMacroCommandTypeEnum::CUSTOM_OPERATION: + successFlag =WuQMacroManager::instance()->executeCustomOperationMacroCommand(window, + mc, + commandErrorMessage); + + break; + case WuQMacroCommandTypeEnum::MOUSE: + { + bool notFoundFlag(false); + successFlag = runMouseCommand(mc, + object, + commandErrorMessage, + notFoundFlag); + } + break; + case WuQMacroCommandTypeEnum::WIDGET: + successFlag = runMacroCommand(window, + mc, + object, + commandErrorMessage); + break; + } + + if ( ! successFlag) { errorMessageOut.append(commandErrorMessage + "\n"); if (m_runOptions.isStopOnError()) { return false; @@ -332,7 +356,7 @@ WuQMacroExecutor::runMacro(const WuQMacro* macro, } QGuiApplication::processEvents(); - if (mc->getClassType() != WuQMacroClassTypeEnum::MOUSE_USER_EVENT) { + if (mc->getCommandType() != WuQMacroCommandTypeEnum::MOUSE) { if (mc->getDelayInSeconds() > 0.0) { SystemUtilities::sleepSeconds(mc->getDelayInSeconds()); } @@ -347,19 +371,20 @@ WuQMacroExecutor::runMacro(const WuQMacro* macro, /** * Run the commands in the given macro. * + * @param parentWidget + * Parent widget for dialogs * @param macroCommand * Macro command that is run - * @param window - * The window from which macro was launched - * @param stopOnErrorFlag - * If true, stop running the commands if there is an error + * @param object + * Object on which command is run * @param errorMessageOut * Output containing any error messages * @return * True if the macro completed without errors, else false. */ bool -WuQMacroExecutor::runMacroCommand(const WuQMacroCommand* macroCommand, +WuQMacroExecutor::runMacroCommand(QWidget* parentWidget, + const WuQMacroCommand* macroCommand, QObject* object, QString& errorMessageOut) const { @@ -368,73 +393,70 @@ WuQMacroExecutor::runMacroCommand(const WuQMacroCommand* macroCommand, CaretAssert(macroCommand); CaretAssert(object); - const WuQMacroClassTypeEnum::Enum classType = macroCommand->getClassType(); + const WuQMacroWidgetTypeEnum::Enum classType = macroCommand->getWidgetType(); QString objectErrorMessage; bool notFoundFlag(false); switch (classType) { - case WuQMacroClassTypeEnum::ACTION: + case WuQMacroWidgetTypeEnum::ACTION: runActionCommand(macroCommand, object, objectErrorMessage, notFoundFlag); break; - case WuQMacroClassTypeEnum::ACTION_CHECKABLE: + case WuQMacroWidgetTypeEnum::ACTION_CHECKABLE: runActionCheckableCommand(macroCommand, object, objectErrorMessage, notFoundFlag); break; - case WuQMacroClassTypeEnum::ACTION_GROUP: + case WuQMacroWidgetTypeEnum::ACTION_GROUP: runActionGroupCommand(macroCommand, object, objectErrorMessage, notFoundFlag); break; - case WuQMacroClassTypeEnum::BUTTON_GROUP: + case WuQMacroWidgetTypeEnum::BUTTON_GROUP: runButtonGroupCommand(macroCommand, object, objectErrorMessage, notFoundFlag); break; - case WuQMacroClassTypeEnum::CHECK_BOX: + case WuQMacroWidgetTypeEnum::CHECK_BOX: runCheckBoxCommand(macroCommand, object, objectErrorMessage, notFoundFlag); break; - case WuQMacroClassTypeEnum::COMBO_BOX: - runComboBoxCommand(macroCommand, object, errorMessageOut, notFoundFlag); + case WuQMacroWidgetTypeEnum::COMBO_BOX: + runComboBoxCommand(macroCommand, object, objectErrorMessage, notFoundFlag); break; - case WuQMacroClassTypeEnum::DOUBLE_SPIN_BOX: - runDoubleSpinBoxCommand(macroCommand, object, errorMessageOut, notFoundFlag); + case WuQMacroWidgetTypeEnum::DOUBLE_SPIN_BOX: + runDoubleSpinBoxCommand(macroCommand, object, objectErrorMessage, notFoundFlag); break; - case WuQMacroClassTypeEnum::INVALID: + case WuQMacroWidgetTypeEnum::INVALID: CaretAssert(0); break; - case WuQMacroClassTypeEnum::LINE_EDIT: - runLineEditCommand(macroCommand, object, errorMessageOut, notFoundFlag); + case WuQMacroWidgetTypeEnum::LINE_EDIT: + runLineEditCommand(macroCommand, object, objectErrorMessage, notFoundFlag); break; - case WuQMacroClassTypeEnum::LIST_WIDGET: - runListWidgetCommand(macroCommand, object, errorMessageOut, notFoundFlag); + case WuQMacroWidgetTypeEnum::LIST_WIDGET: + runListWidgetCommand(macroCommand, object, objectErrorMessage, notFoundFlag); break; - case WuQMacroClassTypeEnum::MENU: - runMenuCommand(macroCommand, object, errorMessageOut, notFoundFlag); - break; - case WuQMacroClassTypeEnum::MOUSE_USER_EVENT: - return runMouseCommand(macroCommand, object, errorMessageOut, notFoundFlag); + case WuQMacroWidgetTypeEnum::MENU: + runMenuCommand(macroCommand, object, objectErrorMessage, notFoundFlag); break; - case WuQMacroClassTypeEnum::PUSH_BUTTON: - runPushButtonCommand(macroCommand, object, errorMessageOut, notFoundFlag); + case WuQMacroWidgetTypeEnum::PUSH_BUTTON: + runPushButtonCommand(macroCommand, object, objectErrorMessage, notFoundFlag); break; - case WuQMacroClassTypeEnum::PUSH_BUTTON_CHECKABLE: - runPushButtonCheckableCommand(macroCommand, object, errorMessageOut, notFoundFlag); + case WuQMacroWidgetTypeEnum::PUSH_BUTTON_CHECKABLE: + runPushButtonCheckableCommand(macroCommand, object, objectErrorMessage, notFoundFlag); break; - case WuQMacroClassTypeEnum::RADIO_BUTTON: - runRadioButtonCommand(macroCommand, object, errorMessageOut, notFoundFlag); + case WuQMacroWidgetTypeEnum::RADIO_BUTTON: + runRadioButtonCommand(macroCommand, object, objectErrorMessage, notFoundFlag); break; - case WuQMacroClassTypeEnum::SLIDER: - runSliderCommand(macroCommand, object, errorMessageOut, notFoundFlag); + case WuQMacroWidgetTypeEnum::SLIDER: + runSliderCommand(macroCommand, object, objectErrorMessage, notFoundFlag); break; - case WuQMacroClassTypeEnum::SPIN_BOX: - runSpinBoxCommand(macroCommand, object, errorMessageOut, notFoundFlag); + case WuQMacroWidgetTypeEnum::SPIN_BOX: + runSpinBoxCommand(macroCommand, object, objectErrorMessage, notFoundFlag); break; - case WuQMacroClassTypeEnum::TAB_BAR: - runTabBarCommand(macroCommand, object, errorMessageOut, notFoundFlag); + case WuQMacroWidgetTypeEnum::TAB_BAR: + runTabBarCommand(macroCommand, object, objectErrorMessage, notFoundFlag); break; - case WuQMacroClassTypeEnum::TAB_WIDGET: - runTabWidgetCommand(macroCommand, object, errorMessageOut, notFoundFlag); + case WuQMacroWidgetTypeEnum::TAB_WIDGET: + runTabWidgetCommand(macroCommand, object, objectErrorMessage, notFoundFlag); break; - case WuQMacroClassTypeEnum::TOOL_BUTTON: - runToolButtonCommand(macroCommand, object, errorMessageOut, notFoundFlag); + case WuQMacroWidgetTypeEnum::TOOL_BUTTON: + runToolButtonCommand(macroCommand, object, objectErrorMessage, notFoundFlag); break; - case WuQMacroClassTypeEnum::TOOL_BUTTON_CHECKABLE: - runToolButtonCheckableCommand(macroCommand, object, errorMessageOut, notFoundFlag); + case WuQMacroWidgetTypeEnum::TOOL_BUTTON_CHECKABLE: + runToolButtonCheckableCommand(macroCommand, object, objectErrorMessage, notFoundFlag); break; } @@ -442,7 +464,7 @@ WuQMacroExecutor::runMacroCommand(const WuQMacroCommand* macroCommand, errorMessageOut = ("ERROR: Unable to cast object named " + object->objectName() + " to " - + WuQMacroClassTypeEnum::toGuiName(classType)); + + WuQMacroWidgetTypeEnum::toGuiName(classType)); return false; } else if ( ! objectErrorMessage.isEmpty()) { diff --git a/src/GuiQt/WuQMacroExecutor.h b/src/GuiQt/WuQMacroExecutor.h index a23d93075..d2c50bab6 100644 --- a/src/GuiQt/WuQMacroExecutor.h +++ b/src/GuiQt/WuQMacroExecutor.h @@ -51,7 +51,7 @@ namespace caret { WuQMacroExecutor& operator=(const WuQMacroExecutor&) = delete; bool runMacro(const WuQMacro* macro, - QObject* window, + QWidget* window, std::vector& otherObjectParents, const WuQMacroExecutorOptions* executorOptions, QString& errorMessageOut) const; @@ -77,7 +77,8 @@ namespace caret { const QRect* objectRect = NULL, const bool hightlightFlag = false) const; - bool runMacroCommand(const WuQMacroCommand* macroCommand, + bool runMacroCommand(QWidget* parentWidget, + const WuQMacroCommand* macroCommand, QObject* object, QString& errorMessageOut) const; diff --git a/src/GuiQt/WuQMacroHelperInterface.h b/src/GuiQt/WuQMacroHelperInterface.h index 5feb7cff6..af359bb19 100644 --- a/src/GuiQt/WuQMacroHelperInterface.h +++ b/src/GuiQt/WuQMacroHelperInterface.h @@ -67,6 +67,7 @@ namespace caret { */ virtual void macroGroupWasModified(WuQMacroGroup* macroGroup) = 0; + // ADD_NEW_METHODS_HERE signals: diff --git a/src/GuiQt/WuQMacroManager.cxx b/src/GuiQt/WuQMacroManager.cxx index 4735e607c..e2484c76e 100644 --- a/src/GuiQt/WuQMacroManager.cxx +++ b/src/GuiQt/WuQMacroManager.cxx @@ -41,6 +41,7 @@ #include "WuQMacro.h" #include "WuQMacroCommand.h" #include "WuQMacroCreateDialog.h" +#include "WuQMacroCustomOperationManagerInterface.h" #include "WuQMacroDialog.h" #include "WuQMacroExecutor.h" #include "WuQMacroFile.h" @@ -87,6 +88,11 @@ WuQMacroManager::~WuQMacroManager() */ m_signalWatchers.clear(); + if (m_customCommandManager != NULL) { + delete m_customCommandManager; + m_customCommandManager = NULL; + } + /* * If an instance is being deleted it MUST be the * singleton so make it NULL. @@ -134,6 +140,19 @@ WuQMacroManager::setMacroHelper(WuQMacroHelperInterface* macroHelper) } } +/** + * Set the custom command manager for editing custom command parameters and + * running custom commands + * + * @param customCommandManager + * Pointer to custom command manager + */ +void +WuQMacroManager::setCustomCommandManager(WuQMacroCustomOperationManagerInterface* customCommandManager) +{ + m_customCommandManager = customCommandManager; +} + /** * @return Name of this macro manager */ @@ -236,25 +255,6 @@ WuQMacroManager::addMacroSupportToObjectWithToolTip(QObject* object, } } -///** -// * Add macro support to the given object. When recording, -// * The object's 'value changed' signal will be monitored so -// * that the new value can be part of a macro command. -// * -// * @param object -// * Object that is monitored. -// * @param toolTipTextOverride -// * Override of object's tooltip. This is primarily used when -// * an object of a particular class does not support a tooltip -// * such as a QButtonGroup -// */ -//bool -//WuQMacroManager::addMacroSupportToObject(QObject* object) -//{ -// return addMacroSupportToObject(object, -// "IN WORK"); -//} - /** * Add macro support to the given object. When recording, * The object's 'value changed' signal will be monitored so @@ -396,11 +396,17 @@ WuQMacroManager::addMouseEventToRecording(QWidget* widget, mouseInfo->addLocalXY(me->localPos().x(), me->localPos().y()); - m_macroBeingRecorded->appendMacroCommand(new WuQMacroCommand(name, - descriptiveName, - mouseInfo)); + const int32_t versionNumber(1); + QString errorMessage; + WuQMacroCommand* command = WuQMacroCommand::newInstanceMouseCommand(mouseInfo, + versionNumber, + name, + descriptiveName, + "mouse operation", + 1.0, + errorMessage); + m_macroBeingRecorded->appendMacroCommand(command); return true; - } } @@ -897,5 +903,105 @@ WuQMacroManager::getShortCutKeysMask() return mask; } +/** + * Is called to edit a macro command parameter with a CUSTOM_DATA data type + * + * @param parent + * Parent widget for any dialogs + * @param parameter + * Parameter for editing + * @return + * True if the parameter was modified + */ +bool +WuQMacroManager::editCustomDataValueParameter(QWidget* parent, + WuQMacroCommandParameter* parameter) +{ + bool modFlag(false); + + if (m_customCommandManager != NULL) { + modFlag = m_customCommandManager->editCustomDataValueParameter(parent, + parameter); + } + else { + CaretLogSevere("No Macro Helper available for editing custom values in a macro parameter"); + } + + return modFlag; +} + +/** + * Run a custom-defined macro command + * + * @param parent + * Parent widget for any dialogs + * @param macroCommand + * Custom macro command to run + * @param errorMessageOut + * Contains any error information or empty if no error + */ +bool +WuQMacroManager::executeCustomOperationMacroCommand(QWidget* parent, + const WuQMacroCommand* macroCommand, + QString& errorMessageOut) +{ + CaretAssert(parent); + CaretAssert(macroCommand); + + errorMessageOut.clear(); + + bool successFlag(false); + if (m_customCommandManager != NULL) { + successFlag = m_customCommandManager->executeCustomOperationMacroCommand(parent, + macroCommand, + errorMessageOut); + } + else { + CaretLogSevere("No Macro Helper available for running custom macro commands"); + } + + return successFlag; +} + +/** + * @return Names of custom operation defined macro commands + */ +std::vector +WuQMacroManager::getNamesOfCustomOperationMacroCommands() +{ + std::vector names; + + if (m_customCommandManager != NULL) { + names = m_customCommandManager->getNamesOfCustomOperationMacroCommands(); + } + return names; +} +/** + * Get a new instance of a custom operation for the given macro command name + * + * @param customMacroCommandName + * Name of custom macro command + * @param errorMessageOut + * Contains any error information or empty if no error + * @return + * Pointer to command or NULL if not valid + */ +WuQMacroCommand* +WuQMacroManager::newInstanceOfCustomOperationMacroCommand(const QString& macroCommandName, + QString& errorMessageOut) +{ + errorMessageOut.clear(); + WuQMacroCommand* command(NULL); + + if (m_customCommandManager != NULL) { + command = m_customCommandManager->newInstanceOfCustomOperationMacroCommand(macroCommandName, + errorMessageOut); + } + else { + errorMessageOut = "No Custom Operation Manager is available for creating custom commands"; + } + + return command; +} diff --git a/src/GuiQt/WuQMacroManager.h b/src/GuiQt/WuQMacroManager.h index 1a82bd3f4..bae8a8b38 100644 --- a/src/GuiQt/WuQMacroManager.h +++ b/src/GuiQt/WuQMacroManager.h @@ -39,6 +39,8 @@ namespace caret { class WuQMacro; class WuQMacroCommand; + class WuQMacroCommandParameter; + class WuQMacroCustomOperationManagerInterface; class WuQMacroDialog; class WuQMacroExecutorOptions; class WuQMacroGroup; @@ -59,10 +61,10 @@ namespace caret { void setMacroHelper(WuQMacroHelperInterface* macroHelper); + void setCustomCommandManager(WuQMacroCustomOperationManagerInterface* customCommandManager); + QString getName() const; -// bool addMacroSupportToObject(QObject* object); -// bool addMacroSupportToObject(QObject* object, const QString& descriptiveName); @@ -132,6 +134,18 @@ namespace caret { void macroWasModified(WuQMacro* macro); + bool editCustomDataValueParameter(QWidget* parent, + WuQMacroCommandParameter* parameter); + + bool executeCustomOperationMacroCommand(QWidget* parent, + const WuQMacroCommand* macroCommand, + QString& errorMessageOut); + + virtual std::vector getNamesOfCustomOperationMacroCommands(); + + virtual WuQMacroCommand* newInstanceOfCustomOperationMacroCommand(const QString& macroCommandName, + QString& errorMessageOut); + // ADD_NEW_METHODS_HERE public slots: @@ -157,7 +171,9 @@ namespace caret { std::unique_ptr m_executorOptions; - WuQMacroHelperInterface* m_macroHelper; + WuQMacroHelperInterface* m_macroHelper = NULL; + + WuQMacroCustomOperationManagerInterface* m_customCommandManager = NULL; // ADD_NEW_MEMBERS_HERE diff --git a/src/GuiQt/WuQMacroSignalWatcher.cxx b/src/GuiQt/WuQMacroSignalWatcher.cxx index 9d9af495e..4f0f08fe7 100644 --- a/src/GuiQt/WuQMacroSignalWatcher.cxx +++ b/src/GuiQt/WuQMacroSignalWatcher.cxx @@ -71,7 +71,7 @@ using namespace caret; */ WuQMacroSignalWatcher::WuQMacroSignalWatcher(WuQMacroManager* parentMacroManager, QObject* object, - const WuQMacroClassTypeEnum::Enum objectType, + const WuQMacroWidgetTypeEnum::Enum objectType, const QString& descriptiveName, const QString& toolTipTextOverride) : QObject(), @@ -90,7 +90,7 @@ m_objectName(object->objectName()) } switch (m_objectType) { - case WuQMacroClassTypeEnum::ACTION: + case WuQMacroWidgetTypeEnum::ACTION: { QAction* action = qobject_cast(m_object); CaretAssert(action); @@ -99,7 +99,7 @@ m_objectName(object->objectName()) m_toolTipText = action->toolTip(); } break; - case WuQMacroClassTypeEnum::ACTION_CHECKABLE: + case WuQMacroWidgetTypeEnum::ACTION_CHECKABLE: { QAction* action = qobject_cast(m_object); CaretAssert(action); @@ -108,7 +108,7 @@ m_objectName(object->objectName()) m_toolTipText = action->toolTip(); } break; - case WuQMacroClassTypeEnum::ACTION_GROUP: + case WuQMacroWidgetTypeEnum::ACTION_GROUP: { QActionGroup* actionGroup = qobject_cast(m_object); CaretAssert(actionGroup); @@ -116,7 +116,7 @@ m_objectName(object->objectName()) this, &WuQMacroSignalWatcher::actionGroupTriggered); } break; - case WuQMacroClassTypeEnum::BUTTON_GROUP: + case WuQMacroWidgetTypeEnum::BUTTON_GROUP: { QButtonGroup* buttonGroup = qobject_cast(m_object); CaretAssert(buttonGroup); @@ -124,7 +124,7 @@ m_objectName(object->objectName()) this, &WuQMacroSignalWatcher::buttonGroupButtonClicked); } break; - case WuQMacroClassTypeEnum::CHECK_BOX: + case WuQMacroWidgetTypeEnum::CHECK_BOX: { QCheckBox* checkBox = qobject_cast(m_object); CaretAssert(checkBox); @@ -132,7 +132,7 @@ m_objectName(object->objectName()) this, &WuQMacroSignalWatcher::checkBoxClicked); } break; - case WuQMacroClassTypeEnum::COMBO_BOX: + case WuQMacroWidgetTypeEnum::COMBO_BOX: { QComboBox* comboBox = qobject_cast(m_object); CaretAssert(comboBox); @@ -140,7 +140,7 @@ m_objectName(object->objectName()) this, &WuQMacroSignalWatcher::comboBoxActivated); } break; - case WuQMacroClassTypeEnum::DOUBLE_SPIN_BOX: + case WuQMacroWidgetTypeEnum::DOUBLE_SPIN_BOX: { QDoubleSpinBox* spinBox = qobject_cast(m_object); CaretAssert(spinBox); @@ -148,10 +148,10 @@ m_objectName(object->objectName()) this, &WuQMacroSignalWatcher::doubleSpinBoxValueChanged); } break; - case WuQMacroClassTypeEnum::INVALID: + case WuQMacroWidgetTypeEnum::INVALID: CaretAssert(0); break; - case WuQMacroClassTypeEnum::LINE_EDIT: + case WuQMacroWidgetTypeEnum::LINE_EDIT: { QLineEdit* lineEdit = qobject_cast(m_object); CaretAssert(lineEdit); @@ -159,7 +159,7 @@ m_objectName(object->objectName()) this, &WuQMacroSignalWatcher::lineEditTextEdited); } break; - case WuQMacroClassTypeEnum::LIST_WIDGET: + case WuQMacroWidgetTypeEnum::LIST_WIDGET: { QListWidget* listWidget = qobject_cast(m_object); CaretAssert(listWidget); @@ -167,7 +167,7 @@ m_objectName(object->objectName()) this, &WuQMacroSignalWatcher::listWidgetItemActivated); } break; - case WuQMacroClassTypeEnum::MENU: + case WuQMacroWidgetTypeEnum::MENU: { QMenu* menu = qobject_cast(m_object); CaretAssert(menu); @@ -175,10 +175,7 @@ m_objectName(object->objectName()) this, &WuQMacroSignalWatcher::menuTriggered); } break; - case WuQMacroClassTypeEnum::MOUSE_USER_EVENT: - CaretAssertToDoFatal(); - break; - case WuQMacroClassTypeEnum::PUSH_BUTTON: + case WuQMacroWidgetTypeEnum::PUSH_BUTTON: { QPushButton* pushButton = qobject_cast(m_object); CaretAssert(pushButton); @@ -186,7 +183,7 @@ m_objectName(object->objectName()) this, &WuQMacroSignalWatcher::pushButtonClicked); } break; - case WuQMacroClassTypeEnum::PUSH_BUTTON_CHECKABLE: + case WuQMacroWidgetTypeEnum::PUSH_BUTTON_CHECKABLE: { QPushButton* pushButton = qobject_cast(m_object); CaretAssert(pushButton); @@ -194,7 +191,7 @@ m_objectName(object->objectName()) this, &WuQMacroSignalWatcher::pushButtonClicked); } break; - case WuQMacroClassTypeEnum::RADIO_BUTTON: + case WuQMacroWidgetTypeEnum::RADIO_BUTTON: { QRadioButton* radioButton = qobject_cast(m_object); CaretAssert(radioButton); @@ -202,7 +199,7 @@ m_objectName(object->objectName()) this, &WuQMacroSignalWatcher::radioButtonClicked); } break; - case WuQMacroClassTypeEnum::SLIDER: + case WuQMacroWidgetTypeEnum::SLIDER: { QSlider* slider = qobject_cast(m_object); CaretAssert(slider); @@ -210,7 +207,7 @@ m_objectName(object->objectName()) this, &WuQMacroSignalWatcher::sliderValueChanged); } break; - case WuQMacroClassTypeEnum::SPIN_BOX: + case WuQMacroWidgetTypeEnum::SPIN_BOX: { QSpinBox* spinBox = qobject_cast(m_object); CaretAssert(spinBox); @@ -218,7 +215,7 @@ m_objectName(object->objectName()) this, &WuQMacroSignalWatcher::spinBoxValueChanged); } break; - case WuQMacroClassTypeEnum::TAB_BAR: + case WuQMacroWidgetTypeEnum::TAB_BAR: { QTabBar* tabBar = qobject_cast(m_object); CaretAssert(tabBar); @@ -226,7 +223,7 @@ m_objectName(object->objectName()) this, &WuQMacroSignalWatcher::tabBarCurrentChanged); } break; - case WuQMacroClassTypeEnum::TAB_WIDGET: + case WuQMacroWidgetTypeEnum::TAB_WIDGET: { QTabWidget* tabWidget = qobject_cast(m_object); CaretAssert(tabWidget); @@ -234,7 +231,7 @@ m_objectName(object->objectName()) this, &WuQMacroSignalWatcher::tabWidgetCurrentChanged); } break; - case WuQMacroClassTypeEnum::TOOL_BUTTON: + case WuQMacroWidgetTypeEnum::TOOL_BUTTON: { QToolButton* toolButton = qobject_cast(m_object); CaretAssert(toolButton); @@ -242,7 +239,7 @@ m_objectName(object->objectName()) this, &WuQMacroSignalWatcher::toolButtonClicked); } break; - case WuQMacroClassTypeEnum::TOOL_BUTTON_CHECKABLE: + case WuQMacroWidgetTypeEnum::TOOL_BUTTON_CHECKABLE: { QToolButton* toolButton = qobject_cast(m_object); CaretAssert(toolButton); @@ -338,7 +335,7 @@ WuQMacroSignalWatcher::newInstance(WuQMacroManager* parentMacroManager, QString objectClassName = object->metaObject()->className(); bool validFlag(false); - WuQMacroClassTypeEnum::Enum objectType = WuQMacroClassTypeEnum::fromGuiName(objectClassName, + WuQMacroWidgetTypeEnum::Enum objectType = WuQMacroWidgetTypeEnum::fromGuiName(objectClassName, &validFlag); /* @@ -347,7 +344,7 @@ WuQMacroSignalWatcher::newInstance(WuQMacroManager* parentMacroManager, * must be handled differently. */ switch (objectType) { - case WuQMacroClassTypeEnum::ACTION: + case WuQMacroWidgetTypeEnum::ACTION: { /* * Actions may have a 'checkable' status enabled @@ -364,7 +361,7 @@ WuQMacroSignalWatcher::newInstance(WuQMacroManager* parentMacroManager, /* * Probably checkable */ - objectType = WuQMacroClassTypeEnum::ACTION_CHECKABLE; + objectType = WuQMacroWidgetTypeEnum::ACTION_CHECKABLE; const QActionGroup* actionGroup = action->actionGroup(); if (actionGroup != NULL) { @@ -373,36 +370,34 @@ WuQMacroSignalWatcher::newInstance(WuQMacroManager* parentMacroManager, * In an exclusive group, actions CANNOT be * uchecked so treat as non-checkable action */ - objectType = WuQMacroClassTypeEnum::ACTION; + objectType = WuQMacroWidgetTypeEnum::ACTION; } } } } break; - case WuQMacroClassTypeEnum::ACTION_CHECKABLE: + case WuQMacroWidgetTypeEnum::ACTION_CHECKABLE: CaretAssertMessage(0, "ACTION_CHECKABLE is created by ACTION above"); break; - case WuQMacroClassTypeEnum::ACTION_GROUP: - break; - case WuQMacroClassTypeEnum::BUTTON_GROUP: + case WuQMacroWidgetTypeEnum::ACTION_GROUP: break; - case WuQMacroClassTypeEnum::CHECK_BOX: + case WuQMacroWidgetTypeEnum::BUTTON_GROUP: break; - case WuQMacroClassTypeEnum::COMBO_BOX: + case WuQMacroWidgetTypeEnum::CHECK_BOX: break; - case WuQMacroClassTypeEnum::DOUBLE_SPIN_BOX: + case WuQMacroWidgetTypeEnum::COMBO_BOX: break; - case WuQMacroClassTypeEnum::INVALID: + case WuQMacroWidgetTypeEnum::DOUBLE_SPIN_BOX: break; - case WuQMacroClassTypeEnum::LINE_EDIT: + case WuQMacroWidgetTypeEnum::INVALID: break; - case WuQMacroClassTypeEnum::LIST_WIDGET: + case WuQMacroWidgetTypeEnum::LINE_EDIT: break; - case WuQMacroClassTypeEnum::MENU: + case WuQMacroWidgetTypeEnum::LIST_WIDGET: break; - case WuQMacroClassTypeEnum::MOUSE_USER_EVENT: + case WuQMacroWidgetTypeEnum::MENU: break; - case WuQMacroClassTypeEnum::PUSH_BUTTON: + case WuQMacroWidgetTypeEnum::PUSH_BUTTON: { /* * Buttons may have a 'checkable' status enabled @@ -419,7 +414,7 @@ WuQMacroSignalWatcher::newInstance(WuQMacroManager* parentMacroManager, /* * Probably checkable */ - objectType = WuQMacroClassTypeEnum::PUSH_BUTTON_CHECKABLE; + objectType = WuQMacroWidgetTypeEnum::PUSH_BUTTON_CHECKABLE; const QButtonGroup* buttonGroup = button->group(); if (buttonGroup != NULL) { @@ -428,26 +423,26 @@ WuQMacroSignalWatcher::newInstance(WuQMacroManager* parentMacroManager, * In an exclusive group, buttons CANNOT be * uchecked so treat as non-checkable button */ - objectType = WuQMacroClassTypeEnum::PUSH_BUTTON; + objectType = WuQMacroWidgetTypeEnum::PUSH_BUTTON; } } } } break; - case WuQMacroClassTypeEnum::PUSH_BUTTON_CHECKABLE: + case WuQMacroWidgetTypeEnum::PUSH_BUTTON_CHECKABLE: CaretAssertMessage(0, "PUSH_BUTTON_CHECKABLE is created by PUSH_BUTTON case above"); break; - case WuQMacroClassTypeEnum::RADIO_BUTTON: + case WuQMacroWidgetTypeEnum::RADIO_BUTTON: break; - case WuQMacroClassTypeEnum::SLIDER: + case WuQMacroWidgetTypeEnum::SLIDER: break; - case WuQMacroClassTypeEnum::SPIN_BOX: + case WuQMacroWidgetTypeEnum::SPIN_BOX: break; - case WuQMacroClassTypeEnum::TAB_BAR: + case WuQMacroWidgetTypeEnum::TAB_BAR: break; - case WuQMacroClassTypeEnum::TAB_WIDGET: + case WuQMacroWidgetTypeEnum::TAB_WIDGET: break; - case WuQMacroClassTypeEnum::TOOL_BUTTON: + case WuQMacroWidgetTypeEnum::TOOL_BUTTON: { /* * Buttons may have a 'checkable' status enabled @@ -464,7 +459,7 @@ WuQMacroSignalWatcher::newInstance(WuQMacroManager* parentMacroManager, /* * Probably checkable */ - objectType = WuQMacroClassTypeEnum::TOOL_BUTTON_CHECKABLE; + objectType = WuQMacroWidgetTypeEnum::TOOL_BUTTON_CHECKABLE; const QButtonGroup* buttonGroup = button->group(); if (buttonGroup != NULL) { @@ -473,18 +468,18 @@ WuQMacroSignalWatcher::newInstance(WuQMacroManager* parentMacroManager, * In an exclusive group, buttons CANNOT be * uchecked so treat as non-checkable button */ - objectType = WuQMacroClassTypeEnum::TOOL_BUTTON; + objectType = WuQMacroWidgetTypeEnum::TOOL_BUTTON; } } } } break; - case WuQMacroClassTypeEnum::TOOL_BUTTON_CHECKABLE: + case WuQMacroWidgetTypeEnum::TOOL_BUTTON_CHECKABLE: CaretAssertMessage(0, "TOOL_BUTTON_CHECKABLE is created by TOOL_BUTTON case above"); break; } - if ((objectType == WuQMacroClassTypeEnum::INVALID) + if ((objectType == WuQMacroWidgetTypeEnum::INVALID) || ( ! validFlag)) { errorMessageOut = ("Widget named \"" + object->objectName() @@ -523,19 +518,38 @@ void WuQMacroSignalWatcher::createAndSendMacroCommand(std::vector& parameters) { if (m_parentMacroManager->isModeRecording()) { - WuQMacroCommand* mc = new WuQMacroCommand(m_objectType, - m_objectName, - m_descriptiveName, - m_toolTipText); - for (auto p : parameters) { - mc->addParameter(p); + const int32_t versionNumber(1); + QString errorMessage; + WuQMacroCommand* mc = WuQMacroCommand::newInstanceWidgetCommand(m_objectType, + versionNumber, + m_objectName, + m_descriptiveName, + m_toolTipText, + 1.0, + errorMessage); + if (mc != NULL) { + for (auto p : parameters) { + mc->addParameter(p); + } + + if ( ! m_parentMacroManager->addMacroCommandToRecording(mc)) { + delete mc; + } } - - if ( ! m_parentMacroManager->addMacroCommandToRecording(mc)) { - delete mc; + else { + for (auto p : parameters) { + delete p; + } + parameters.clear(); + CaretLogSevere(errorMessage); } } - + else { + for (auto p : parameters) { + delete p; + } + parameters.clear(); + } } /** @@ -967,7 +981,7 @@ WuQMacroSignalWatcher::toString() const { QString s(m_objectName + " type=" - + WuQMacroClassTypeEnum::toGuiName(m_objectType)); + + WuQMacroWidgetTypeEnum::toGuiName(m_objectType)); return s; } diff --git a/src/GuiQt/WuQMacroSignalWatcher.h b/src/GuiQt/WuQMacroSignalWatcher.h index 3cff8e7b4..733aeb42c 100644 --- a/src/GuiQt/WuQMacroSignalWatcher.h +++ b/src/GuiQt/WuQMacroSignalWatcher.h @@ -27,7 +27,7 @@ #include -#include "WuQMacroClassTypeEnum.h" +#include "WuQMacroWidgetTypeEnum.h" class QAbstractButton; class QAction; @@ -107,7 +107,7 @@ namespace caret { private: WuQMacroSignalWatcher(WuQMacroManager* parentMacroManager, QObject* object, - const WuQMacroClassTypeEnum::Enum objectType, + const WuQMacroWidgetTypeEnum::Enum objectType, const QString& descriptiveName, const QString& toolTipTextOverride); @@ -117,7 +117,7 @@ namespace caret { QObject* m_object; - const WuQMacroClassTypeEnum::Enum m_objectType; + const WuQMacroWidgetTypeEnum::Enum m_objectType; const QString m_descriptiveName; -- GitLab From 73e95b7ced7dad73300a4d5fa2d6523ee75962e1 Mon Sep 17 00:00:00 2001 From: John Harwell Date: Tue, 5 Feb 2019 15:30:40 -0600 Subject: [PATCH 178/427] WB-834 Macros: Implemented a 'custom macro' for surface interpolation. --- src/GuiQt/CMakeLists.txt | 2 + src/GuiQt/WbMacroCustomOperationBase.cxx | 172 +++++++++++ src/GuiQt/WbMacroCustomOperationBase.h | 93 ++++++ src/GuiQt/WbMacroCustomOperationManager.cxx | 19 +- ...croCustomOperationSurfaceInterpolation.cxx | 276 ++++++++++++++++-- ...MacroCustomOperationSurfaceInterpolation.h | 32 +- src/GuiQt/WbMacroHelper.cxx | 33 +++ src/GuiQt/WbMacroHelper.h | 4 + src/GuiQt/WuQMacroDialog.cxx | 41 ++- src/GuiQt/WuQMacroDialog.h | 2 + src/GuiQt/WuQMacroExecutor.cxx | 49 +++- src/GuiQt/WuQMacroHelperInterface.h | 16 + src/GuiQt/WuQMacroManager.cxx | 30 ++ src/GuiQt/WuQMacroManager.h | 5 + 14 files changed, 711 insertions(+), 63 deletions(-) create mode 100644 src/GuiQt/WbMacroCustomOperationBase.cxx create mode 100644 src/GuiQt/WbMacroCustomOperationBase.h diff --git a/src/GuiQt/CMakeLists.txt b/src/GuiQt/CMakeLists.txt index 24a9d4ab4..02af4408e 100644 --- a/src/GuiQt/CMakeLists.txt +++ b/src/GuiQt/CMakeLists.txt @@ -448,6 +448,7 @@ VolumeSurfaceOutlineColorOrTabViewController.h VolumeSurfaceOutlineSetViewController.h VolumeSurfaceOutlineViewController.h WbMacroCustomDataTypeEnum.h +WbMacroCustomOperationBase.h WbMacroCustomOperationManager.h WbMacroCustomOperationSurfaceInterpolation.h WbMacroCustomOperationTypeEnum.h @@ -698,6 +699,7 @@ VolumeSurfaceOutlineColorOrTabViewController.cxx VolumeSurfaceOutlineSetViewController.cxx VolumeSurfaceOutlineViewController.cxx WbMacroCustomDataTypeEnum.cxx +WbMacroCustomOperationBase.cxx WbMacroCustomOperationManager.cxx WbMacroCustomOperationSurfaceInterpolation.cxx WbMacroCustomOperationTypeEnum.cxx diff --git a/src/GuiQt/WbMacroCustomOperationBase.cxx b/src/GuiQt/WbMacroCustomOperationBase.cxx new file mode 100644 index 000000000..24a759e90 --- /dev/null +++ b/src/GuiQt/WbMacroCustomOperationBase.cxx @@ -0,0 +1,172 @@ + +/*LICENSE_START*/ +/* + * Copyright (C) 2019 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + +#define __WB_MACRO_CUSTOM_OPERATION_BASE_DECLARE__ +#include "WbMacroCustomOperationBase.h" +#undef __WB_MACRO_CUSTOM_OPERATION_BASE_DECLARE__ + +#include + +#include "CaretAssert.h" +#include "EventGraphicsUpdateAllWindows.h" +#include "EventManager.h" +#include "EventSurfacesGet.h" +#include "EventUserInterfaceUpdate.h" +#include "Surface.h" +#include "SystemUtilities.h" +#include "WuQMacroCommand.h" + +using namespace caret; + + + +/** + * \class caret::WbMacroCustomOperationBase + * \brief Base class for all macro custom operations + * \ingroup GuiQt + */ + +/** + * Constructor + */ +WbMacroCustomOperationBase::WbMacroCustomOperationBase() +{ + +} + + +/** + * Destructor + */ +WbMacroCustomOperationBase::~WbMacroCustomOperationBase() +{ + +} + +/** + * @return The error message + */ +QString +WbMacroCustomOperationBase::getErrorMessage() const +{ + return m_errorMessage; +} + +bool +WbMacroCustomOperationBase::validateCorrectNumberOfParameters(const WuQMacroCommand* command) +{ + std::unique_ptr validCommand(createCommand()); + const int32_t paramCount = command->getNumberOfParameters(); + const int32_t validCount = validCommand->getNumberOfParameters(); + + if (paramCount < validCount) { + appendToErrorMessage("Command should contain " + + QString::number(validCount) + + " parameters but contains " + + QString::number(paramCount) + + " parameters"); + return false; + } + + return true; +} + +/** + * Append text to the error message. If the current error message + * is not empty, a newline is added prior to the text. + * + * @param text + * Text to append to error message + */ +void +WbMacroCustomOperationBase::appendToErrorMessage(const QString& text) +{ + if ( m_errorMessage.isEmpty()) { + m_errorMessage.append("\n"); + } + m_errorMessage.append(text); +} + +/** + * Find the surface with the given name + * + * @param name + * Name of surface + * @param errorMessagePrefix + * Prefix inserted into error message if an error occurs + * @return + * Pointer to surface or NULL if not found. + * If not found getErrorMessage() explains why + */ +Surface* +WbMacroCustomOperationBase::findSurface(const QString& name, + const QString& errorMessagePrefix) +{ + if (name.isEmpty()) { + appendToErrorMessage(errorMessagePrefix + + " name is empty"); + return NULL; + } + + EventSurfacesGet eventSurfaces; + EventManager::get()->sendEvent(eventSurfaces.getPointer()); + std::vector allSurfaces = eventSurfaces.getSurfaces(); + + for (auto s : allSurfaces) { + if (s->getFileName().endsWith(name)) { + return s; + } + } + + appendToErrorMessage(errorMessagePrefix + + " with name \"" + + name + + "\" not found."); + return NULL; + +} + +/** + * Update graphics + */ +void +WbMacroCustomOperationBase::updateGraphics() +{ + /* + * Passing 'true' indicate do a repaint(). A 'repaint' is performed immediately. + * Otherwise, the graphics update is scheduled for a later time. + */ + EventManager::get()->sendEvent(EventGraphicsUpdateAllWindows(true).getPointer()); + + /* + * Qt needs time to update stuff + */ + QApplication::processEvents(); +} + +/** + * Update the user-interface + */ +void +WbMacroCustomOperationBase::updateUserInterface() +{ + EventManager::get()->sendEvent(EventUserInterfaceUpdate().getPointer()); +} diff --git a/src/GuiQt/WbMacroCustomOperationBase.h b/src/GuiQt/WbMacroCustomOperationBase.h new file mode 100644 index 000000000..9462e9fca --- /dev/null +++ b/src/GuiQt/WbMacroCustomOperationBase.h @@ -0,0 +1,93 @@ +#ifndef __WB_MACRO_CUSTOM_OPERATION_BASE_H__ +#define __WB_MACRO_CUSTOM_OPERATION_BASE_H__ + +/*LICENSE_START*/ +/* + * Copyright (C) 2019 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + + + +#include + +#include "CaretObject.h" + +class QString; +class QWidget; + +namespace caret { + class Surface; + class WuQMacroCommand; + + class WbMacroCustomOperationBase : public CaretObject { + public: + ~WbMacroCustomOperationBase(); + + WbMacroCustomOperationBase(const WbMacroCustomOperationBase&) = delete; + + WbMacroCustomOperationBase& operator=(const WbMacroCustomOperationBase&) = delete; + + /** + * Execute the macro command + * + * @param parent + * Parent widget for any dialogs + * @param macroCommand + * macro command to run + * @return + * True if command executed successfully, else false + * Use getErrorMessage() for error information if false returned + */ + virtual bool executeCommand(QWidget* parent, + const WuQMacroCommand* macroCommand) = 0; + + /** + * Get a new instance of the macro command + * + * @return + * Pointer to command or NULL if not valid + * Use getErrorMessage() for error information if NULL returned + */ + virtual WuQMacroCommand* createCommand() = 0; + + QString getErrorMessage() const; + + protected: + WbMacroCustomOperationBase(); + + bool validateCorrectNumberOfParameters(const WuQMacroCommand* command); + + Surface* findSurface(const QString& surfaceName, + const QString& errorMessagePrefix); + + void appendToErrorMessage(const QString& text); + + void updateGraphics(); + + void updateUserInterface(); + + QString m_errorMessage; + }; + + +#ifdef __WB_MACRO_CUSTOM_OPERATION_BASE_DECLARE__ + // +#endif // __WB_MACRO_CUSTOM_OPERATION_BASE_DECLARE__ + +} // namespace +#endif //__WB_MACRO_CUSTOM_OPERATION_BASE_H__ diff --git a/src/GuiQt/WbMacroCustomOperationManager.cxx b/src/GuiQt/WbMacroCustomOperationManager.cxx index 6bedcc40d..9e6913521 100644 --- a/src/GuiQt/WbMacroCustomOperationManager.cxx +++ b/src/GuiQt/WbMacroCustomOperationManager.cxx @@ -190,9 +190,14 @@ WbMacroCustomOperationManager::executeCustomOperationMacroCommand(QWidget* paren bool successFlag(false); switch (commandType) { case WbMacroCustomOperationTypeEnum::SURFACE_INTERPOLATION: - successFlag = WbMacroCustomOperationSurfaceInterpolation::executeCommand(parent, - customMacroCommand, - errorMessageOut); + { + WbMacroCustomOperationSurfaceInterpolation surfaceInterpolation; + successFlag = surfaceInterpolation.executeCommand(parent, + customMacroCommand); + if ( ! successFlag) { + errorMessageOut = surfaceInterpolation.getErrorMessage(); + } + } break; } @@ -245,7 +250,13 @@ WbMacroCustomOperationManager::newInstanceOfCustomOperationMacroCommand(const QS switch (commandType) { case WbMacroCustomOperationTypeEnum::SURFACE_INTERPOLATION: - command = WbMacroCustomOperationSurfaceInterpolation::createCommand(errorMessageOut); + { + WbMacroCustomOperationSurfaceInterpolation surfaceInterpolator; + command = surfaceInterpolator.createCommand(); + if (command == NULL) { + errorMessageOut = surfaceInterpolator.getErrorMessage(); + } + } break; } diff --git a/src/GuiQt/WbMacroCustomOperationSurfaceInterpolation.cxx b/src/GuiQt/WbMacroCustomOperationSurfaceInterpolation.cxx index c84af1c29..76321b127 100644 --- a/src/GuiQt/WbMacroCustomOperationSurfaceInterpolation.cxx +++ b/src/GuiQt/WbMacroCustomOperationSurfaceInterpolation.cxx @@ -23,8 +23,16 @@ #include "WbMacroCustomOperationSurfaceInterpolation.h" #undef __WB_MACRO_CUSTOM_OPERATION_SURFACE_INTERPOLATION_DECLARE__ +#include "BrainBrowserWindow.h" +#include "BrowserTabContent.h" #include "CaretAssert.h" +#include "GuiManager.h" +#include "MathFunctions.h" +#include "ModelSurface.h" +#include "ModelWholeBrain.h" +#include "Surface.h" #include "WbMacroCustomDataTypeEnum.h" +#include "WbMacroCustomOperationTypeEnum.h" #include "WuQMacroCommand.h" #include "WuQMacroCommandParameter.h" @@ -39,40 +47,29 @@ using namespace caret; */ /** - * Run the command - * - * @param parent - * Parent widget for any dialogs - * @param macroCommand - * macro command to run - * @param errorMessageOut - * Contains any error information or empty if no error - * @return - * True if command executed successfully, else false + * Constructor. */ -bool -WbMacroCustomOperationSurfaceInterpolation::executeCommand(QWidget* parent, - const WuQMacroCommand* macroCommand, - QString& errorMessageOut) +WbMacroCustomOperationSurfaceInterpolation::WbMacroCustomOperationSurfaceInterpolation() { - bool successFlag(false); - - return successFlag; } /** - * Get a new instance of the command + * Destructor. + */ +WbMacroCustomOperationSurfaceInterpolation::~WbMacroCustomOperationSurfaceInterpolation() +{ +} + +/** + * Get a new instance of the macro command * - * @param errorMessageOut - * Contains any error information or empty if no error * @return * Pointer to command or NULL if not valid + * Use getErrorMessage() for error information if NULL returned */ WuQMacroCommand* -WbMacroCustomOperationSurfaceInterpolation::createCommand(QString& errorMessageOut) +WbMacroCustomOperationSurfaceInterpolation::createCommand() { - errorMessageOut.clear(); - const int32_t versionOne(1); WuQMacroCommandParameter* paramSurfaceOne = new WuQMacroCommandParameter(WuQMacroDataValueTypeEnum::CUSTOM_DATA, @@ -85,13 +82,14 @@ WbMacroCustomOperationSurfaceInterpolation::createCommand(QString& errorMessageO ""); paramSurfaceTwo->setCustomDataType(WbMacroCustomDataTypeEnum::toName(WbMacroCustomDataTypeEnum::SURFACE)); - WuQMacroCommand* command = WuQMacroCommand::newInstanceCustomCommand("SurfaceInterpolation", + QString errorMessage; + WuQMacroCommand* command = WuQMacroCommand::newInstanceCustomCommand(WbMacroCustomOperationTypeEnum::toName(WbMacroCustomOperationTypeEnum::SURFACE_INTERPOLATION), versionOne, "none", "Surface Interpolation", "Interpolate Between Two Surface", 1.0, - errorMessageOut); + errorMessage); if (command != NULL) { command->addParameter(paramSurfaceOne); command->addParameter(paramSurfaceTwo); @@ -99,14 +97,238 @@ WbMacroCustomOperationSurfaceInterpolation::createCommand(QString& errorMessageO "Duration (secs)", (float)5.0); } + else { + appendToErrorMessage(errorMessage); + } return command; } /** - * Destructor. + * Execute the macro command + * + * @param parent + * Parent widget for any dialogs + * @param macroCommand + * macro command to run + * @return + * True if command executed successfully, else false + * Use getErrorMessage() for error information if false returned */ -WbMacroCustomOperationSurfaceInterpolation::~WbMacroCustomOperationSurfaceInterpolation() +bool +WbMacroCustomOperationSurfaceInterpolation::executeCommand(QWidget* parent, + const WuQMacroCommand* macroCommand) { + CaretAssert(parent); + CaretAssert(macroCommand); + + validateCorrectNumberOfParameters(macroCommand); + + BrainBrowserWindow* bbw = qobject_cast(parent); + if (bbw == NULL) { + appendToErrorMessage("Parent for running surface macro is not a browser window"); + return false; + } + + BrowserTabContent* tabContent = bbw->getBrowserTabContent(); + if (tabContent == NULL) { + appendToErrorMessage("No tab is selected in browser window"); + return false; + } + + ModelWholeBrain* wholeBrainModel = tabContent->getDisplayedWholeBrainModel(); + if (wholeBrainModel == NULL) { + appendToErrorMessage("View selected is not ALL view"); + return false; + } + + const QString startSurfaceName(macroCommand->getParameterAtIndex(0)->getValue().toString()); + Surface* startSurface = findSurface(startSurfaceName, + "Starting surface"); + + const QString endSurfaceName(macroCommand->getParameterAtIndex(1)->getValue().toString()); + Surface* endSurface = findSurface(endSurfaceName, + "Ending surface"); + + const float durationSeconds = macroCommand->getParameterAtIndex(2)->getValue().toFloat(); + + if ((startSurface != NULL) + && (endSurface != NULL)) { + if (startSurface == endSurface) { + appendToErrorMessage("Starting and ending surfaces are the same surfaces"); + } + if (startSurface->getStructure() != endSurface->getStructure()) { + appendToErrorMessage("The surfaces' structures are different"); + } + if (startSurface->getNumberOfNodes() != endSurface->getNumberOfNodes()) { + appendToErrorMessage("The surfaces contain a different number of vertices"); + } + switch (startSurface->getStructure()) { + case StructureEnum::CEREBELLUM: + case StructureEnum::CORTEX_LEFT: + case StructureEnum::CORTEX_RIGHT: + break; + default: + appendToErrorMessage("Supported surface structures are: " + + StructureEnum::toGuiName(StructureEnum::CEREBELLUM) + ", " + + StructureEnum::toGuiName(StructureEnum::CORTEX_LEFT) + ", " + + StructureEnum::toGuiName(StructureEnum::CORTEX_RIGHT)); + break; + } + } + + if ( ! getErrorMessage().isEmpty()) { + return false; + } + + bool successFlag = interpolateSurface(tabContent->getTabNumber(), + wholeBrainModel, + startSurface, + endSurface, + durationSeconds); + + return successFlag; +} + +/** + * Interpolate from starting to ending surface + * + * @param tabIndex + * Index of selected tab + * @param wholeBrainModel + * The whole brain model + * @param startSurface + * The starting surface + * @param endSurface + * The ending surface + * @param durationSeconds + * Total duration for surface interpolation + * @return + * True if successful, else false + */ +bool +WbMacroCustomOperationSurfaceInterpolation::interpolateSurface(const int32_t tabIndex, + ModelWholeBrain* wholeBrainModel, + Surface* startSurface, + Surface* endSurface, + const float durationSeconds) +{ + CaretAssert(wholeBrainModel); + CaretAssert(startSurface); + CaretAssert(endSurface); + + const StructureEnum::Enum structure = startSurface->getStructure(); + const bool endSurfaceModifiedStatus = endSurface->isModified(); + + + const float* startingXYZ = startSurface->getCoordinateData(); + const float* endingXYZ = endSurface->getCoordinateData(); + + const float numberOfSteps(50.0f); + const float iterationSleepTime = durationSeconds / numberOfSteps; + + /* + * XYZ that will be interpolated + */ + const int32_t numberOfVertices = endSurface->getNumberOfNodes(); + const int32_t numberOfComponents = numberOfVertices * 3; + + /* + * Initialize with starting coordinates + */ + std::vector xyz(startingXYZ, + startingXYZ + numberOfComponents); + + /* + * Amount to move each interpolation iteration + */ + std::vector deltaXYZ(numberOfComponents); + for (int32_t i = 0; i < numberOfComponents; i++) { + const float distance = endingXYZ[i] - startingXYZ[i]; + const float stepDistance = distance / numberOfSteps; + CaretAssertVectorIndex(deltaXYZ, i); + deltaXYZ[i] = stepDistance; + } + + /* + * Replace ending surface's coordinates with starting surfaces coordinates + */ + for (int32_t i = 0; i < numberOfVertices; i++) { + const int32_t i3 = i * 3; + endSurface->setCoordinate(i, &startingXYZ[i3]); + } + endSurface->computeNormals(); + + /* + * Put ending surface into view + */ + wholeBrainModel->setSelectedSurfaceType(tabIndex, + endSurface->getSurfaceType()); + wholeBrainModel->setSelectedSurface(structure, + tabIndex, + endSurface); + + switch (structure) { + case StructureEnum::CEREBELLUM: + break; + case StructureEnum::CORTEX_LEFT: + break; + case StructureEnum::CORTEX_RIGHT: + break; + default: + CaretAssert(0); + break; + } + updateUserInterface(); + updateGraphics(); + + for (int iStep = 0; iStep < numberOfSteps; iStep++) { + /* + * Move coordinate components + */ + for (int32_t i = 0; i < numberOfComponents; i++) { + CaretAssertVectorIndex(xyz, i); + CaretAssertVectorIndex(deltaXYZ, i); + xyz[i] += deltaXYZ[i]; + } + + + /* + * Update surface coordinates + */ + for (int32_t i = 0; i < numberOfVertices; i++) { + const int32_t i3 = i * 3; + endSurface->setCoordinate(i, + &xyz[i3]); + } + endSurface->computeNormals(); + + const bool debugFlag(false); + if (debugFlag) { + const int32_t vertexIndex = 16764; + const float* p = endSurface->getCoordinate(vertexIndex); + std::cout << "XYZ " << iStep << ": " + << AString::number(p[0]) << " " + << AString::number(p[1]) << " " + << AString::number(p[2]) << std::endl; + } + + updateGraphics(); + SystemUtilities::sleepSeconds(iterationSleepTime); + } + + /* + * Restore end surface coordinates + */ + for (int32_t i = 0; i < numberOfVertices; i++) { + const int32_t i3 = i * 3; + endSurface->setCoordinate(i, &endingXYZ[i3]); + } + if ( ! endSurfaceModifiedStatus) { + endSurface->clearModified(); + } + updateGraphics(); + + return true; } diff --git a/src/GuiQt/WbMacroCustomOperationSurfaceInterpolation.h b/src/GuiQt/WbMacroCustomOperationSurfaceInterpolation.h index 702cb71af..4c185261e 100644 --- a/src/GuiQt/WbMacroCustomOperationSurfaceInterpolation.h +++ b/src/GuiQt/WbMacroCustomOperationSurfaceInterpolation.h @@ -25,34 +25,42 @@ #include -class QString; -class QWidget; +#include "WbMacroCustomOperationBase.h" namespace caret { - class WuQMacroCommand; + class ModelSurface; + class ModelWholeBrain; + class Surface; - class WbMacroCustomOperationSurfaceInterpolation { + class WbMacroCustomOperationSurfaceInterpolation : public WbMacroCustomOperationBase { public: - static bool executeCommand(QWidget* parent, - const WuQMacroCommand* macroCommand, - QString& errorMessageOut); - - static WuQMacroCommand* createCommand(QString& errorMessageOut); - - WbMacroCustomOperationSurfaceInterpolation() = delete; + WbMacroCustomOperationSurfaceInterpolation(); virtual ~WbMacroCustomOperationSurfaceInterpolation(); WbMacroCustomOperationSurfaceInterpolation(const WbMacroCustomOperationSurfaceInterpolation&) = delete; - + WbMacroCustomOperationSurfaceInterpolation& operator=(const WbMacroCustomOperationSurfaceInterpolation&) = delete; + virtual bool executeCommand(QWidget* parent, + const WuQMacroCommand* macroCommand) override; + virtual WuQMacroCommand* createCommand() override; + + + // ADD_NEW_METHODS_HERE private: + bool interpolateSurface(const int32_t tabIndex, + ModelWholeBrain* wholeBrainModel, + Surface* startSurface, + Surface* endSurface, + const float durationSeconds); + + // ADD_NEW_MEMBERS_HERE }; diff --git a/src/GuiQt/WbMacroHelper.cxx b/src/GuiQt/WbMacroHelper.cxx index bebfb65c3..891028ad2 100644 --- a/src/GuiQt/WbMacroHelper.cxx +++ b/src/GuiQt/WbMacroHelper.cxx @@ -24,6 +24,7 @@ #undef __WB_MACRO_HELPER_DECLARE__ #include "Brain.h" +#include "BrainBrowserWindow.h" #include "CaretAssert.h" #include "CaretLogger.h" #include "CaretPreferences.h" @@ -152,3 +153,35 @@ WbMacroHelper::macroGroupWasModified(WuQMacroGroup* macroGroup) EventManager::get()->sendEvent(EventUserInterfaceUpdate().getPointer()); } +/** + * @return Identifiers of all available windows in which macros may be run + */ +std::vector +WbMacroHelper::getMainWindowIdentifiers() +{ + std::vector identifiers; + + std::vector windows = GuiManager::get()->getAllOpenBrainBrowserWindows(); + for (auto w : windows) { + identifiers.push_back(QString::number(w->getBrowserWindowIndex() + 1)); + } + + return identifiers; +} + +/** + * Get the main window with the given identifier + * + * @param identifier + * Window identifier + * @return + * Window with the given identifier or NULL if not available + */ +QMainWindow* +WbMacroHelper::getMainWindowWithIdentifier(const QString& identifier) +{ + const int32_t windowIndex(identifier.toInt() - 1); + QMainWindow* window = GuiManager::get()->getBrowserWindowByWindowIndex(windowIndex); + + return window; +} diff --git a/src/GuiQt/WbMacroHelper.h b/src/GuiQt/WbMacroHelper.h index 731319229..a9aa4d8b9 100644 --- a/src/GuiQt/WbMacroHelper.h +++ b/src/GuiQt/WbMacroHelper.h @@ -51,6 +51,10 @@ namespace caret { virtual void macroGroupWasModified(WuQMacroGroup* macroGroup) override; + virtual std::vector getMainWindowIdentifiers() override; + + virtual QMainWindow* getMainWindowWithIdentifier(const QString& identifier) override; + // ADD_NEW_METHODS_HERE private: diff --git a/src/GuiQt/WuQMacroDialog.cxx b/src/GuiQt/WuQMacroDialog.cxx index f3d13a17a..3fbb71a6f 100644 --- a/src/GuiQt/WuQMacroDialog.cxx +++ b/src/GuiQt/WuQMacroDialog.cxx @@ -32,6 +32,7 @@ #include #include #include +#include #include #include #include @@ -292,7 +293,6 @@ WuQMacroDialog::createMacroDisplayWidget() QHBoxLayout* runOptionsTitleLayout = new QHBoxLayout(); runOptionsTitleLayout->setContentsMargins(0, 0, 0, 0); - runOptionsTitleLayout->setContentsMargins(0, 0, 0, 0); runOptionsTitleLayout->addWidget(new QLabel("Run Macro Options ")); runOptionsTitleLayout->addWidget(createHorizontalLine(), 100); @@ -381,6 +381,10 @@ WuQMacroDialog::createRunOptionsWidget() { QWidget* widget = new QWidget(); + QLabel* windowLabel = new QLabel("Window"); + m_runOptionsWindowComboBox = new QComboBox(); + m_runOptionsWindowComboBox->setSizeAdjustPolicy(QComboBox::AdjustToMinimumContentsLength); + m_runOptionLoopCheckBox = new QCheckBox("Loop"); m_runOptionLoopCheckBox->setChecked(false); m_runOptionLoopCheckBox->setEnabled(false); @@ -395,10 +399,15 @@ WuQMacroDialog::createRunOptionsWidget() this, &WuQMacroDialog::runOptionMoveMouseCheckBoxClicked); QGridLayout* runOptionsLayout = new QGridLayout(widget); + runOptionsLayout->setColumnStretch(0, 0); + runOptionsLayout->setColumnStretch(1, 100); int row = 0; - runOptionsLayout->addWidget(m_runOptionLoopCheckBox, row, 0); + runOptionsLayout->addWidget(windowLabel, row, 0); + runOptionsLayout->addWidget(m_runOptionsWindowComboBox, row, 1, Qt::AlignLeft); + row++; + runOptionsLayout->addWidget(m_runOptionLoopCheckBox, row, 0, 1, 2, Qt::AlignLeft); row++; - runOptionsLayout->addWidget(m_runOptionMoveMouseCheckBox, row, 0); + runOptionsLayout->addWidget(m_runOptionMoveMouseCheckBox, row, 0, 1, 2, Qt::AlignLeft); row++; return widget; @@ -551,6 +560,14 @@ WuQMacroDialog::updateDialogContents() m_macroGroupComboBox->setCurrentIndex(selectedIndex); } + const QString windowText = m_runOptionsWindowComboBox->currentText(); + m_runOptionsWindowComboBox->clear(); + const std::vector windowIDs = WuQMacroManager::instance()->getMainWindowIdentifiers(); + for (const auto id : windowIDs) { + m_runOptionsWindowComboBox->addItem(id); + } + m_runOptionsWindowComboBox->setCurrentText(windowText); + const WuQMacroExecutorOptions* runOptions = WuQMacroManager::instance()->getExecutorOptions(); CaretAssert(runOptions); m_runOptionMoveMouseCheckBox->setChecked(runOptions->isShowMouseMovement()); @@ -902,7 +919,23 @@ WuQMacroDialog::runMacroToolButtonClicked() return; } - WuQMacroManager::instance()->runMacro(parentWidget(), + const QString windowID = m_runOptionsWindowComboBox->currentText(); + QWidget* window = WuQMacroManager::instance()->getMainWindowWithIdentifier(windowID); + if (window == NULL) { + window = this; + while (window != NULL) { + if (qobject_cast(window) != NULL) { + break; + } + else { + window = window->parentWidget(); + } + } + if (window == NULL) { + window = parentWidget(); + } + } + WuQMacroManager::instance()->runMacro(window, macro); } } diff --git a/src/GuiQt/WuQMacroDialog.h b/src/GuiQt/WuQMacroDialog.h index 464b22bdb..190695302 100644 --- a/src/GuiQt/WuQMacroDialog.h +++ b/src/GuiQt/WuQMacroDialog.h @@ -180,6 +180,8 @@ namespace caret { QDialogButtonBox* m_dialogButtonBox; + QComboBox* m_runOptionsWindowComboBox; + QCheckBox* m_runOptionLoopCheckBox; QCheckBox* m_runOptionMoveMouseCheckBox; diff --git a/src/GuiQt/WuQMacroExecutor.cxx b/src/GuiQt/WuQMacroExecutor.cxx index a368cd889..a0ad6c38f 100644 --- a/src/GuiQt/WuQMacroExecutor.cxx +++ b/src/GuiQt/WuQMacroExecutor.cxx @@ -301,25 +301,40 @@ WuQMacroExecutor::runMacro(const WuQMacro* macro, CaretAssert(mc); const QString objectName(mc->getObjectName()); + + bool requiresObjectFlag(false); + switch (mc->getCommandType()) { + case WuQMacroCommandTypeEnum::CUSTOM_OPERATION: + break; + case WuQMacroCommandTypeEnum::MOUSE: + requiresObjectFlag = true; + break; + case WuQMacroCommandTypeEnum::WIDGET: + requiresObjectFlag = true; + break; + } + QObject* object = findObjectByName(objectName); - if (object == NULL) { - errorMessageOut.append("Unable to find object named " - + objectName - + "\n"); - if (m_runOptions.isStopOnError()) { - return false; + if (requiresObjectFlag) { + if (object == NULL) { + errorMessageOut.append("Unable to find object named " + + objectName + + "\n"); + if (m_runOptions.isStopOnError()) { + return false; + } + continue; } - continue; - } - - if (object->signalsBlocked()) { - errorMessageOut.append("Object named " - + objectName - + " has signals blocked"); - if (m_runOptions.isStopOnError()) { - return false; + + if (object->signalsBlocked()) { + errorMessageOut.append("Object named " + + objectName + + " has signals blocked"); + if (m_runOptions.isStopOnError()) { + return false; + } + continue; } - continue; } QString commandErrorMessage; @@ -333,6 +348,7 @@ WuQMacroExecutor::runMacro(const WuQMacro* macro, break; case WuQMacroCommandTypeEnum::MOUSE: { + CaretAssert(object); bool notFoundFlag(false); successFlag = runMouseCommand(mc, object, @@ -341,6 +357,7 @@ WuQMacroExecutor::runMacro(const WuQMacro* macro, } break; case WuQMacroCommandTypeEnum::WIDGET: + CaretAssert(object); successFlag = runMacroCommand(window, mc, object, diff --git a/src/GuiQt/WuQMacroHelperInterface.h b/src/GuiQt/WuQMacroHelperInterface.h index af359bb19..f1d709879 100644 --- a/src/GuiQt/WuQMacroHelperInterface.h +++ b/src/GuiQt/WuQMacroHelperInterface.h @@ -30,6 +30,8 @@ #include #include +class QMainWindow; + namespace caret { class WuQMacro; class WuQMacroGroup; @@ -66,7 +68,21 @@ namespace caret { * Macro Group that is modified */ virtual void macroGroupWasModified(WuQMacroGroup* macroGroup) = 0; + + /** + * @return Identifiers of all available windows in which macros may be run + */ + virtual std::vector getMainWindowIdentifiers() = 0; + /** + * Get the main window with the given identifier + * + * @param identifier + * Window identifier + * @return + * Window with the given identifier or NULL if not available + */ + virtual QMainWindow* getMainWindowWithIdentifier(const QString& identifier) = 0; // ADD_NEW_METHODS_HERE diff --git a/src/GuiQt/WuQMacroManager.cxx b/src/GuiQt/WuQMacroManager.cxx index e2484c76e..5daccd50a 100644 --- a/src/GuiQt/WuQMacroManager.cxx +++ b/src/GuiQt/WuQMacroManager.cxx @@ -1005,3 +1005,33 @@ WuQMacroManager::newInstanceOfCustomOperationMacroCommand(const QString& macroCo return command; } + +/** + * @return Identifiers of all available windows in which macros may be run + */ +std::vector +WuQMacroManager::getMainWindowIdentifiers() +{ + std::vector identifiers; + if (m_macroHelper != NULL) { + identifiers = m_macroHelper->getMainWindowIdentifiers(); + } + return identifiers; +} + +/** + * Get the main window with the given identifier + * + * @param identifier + * Window identifier + * @return + * Window with the given identifier or NULL if not available + */ +QMainWindow* +WuQMacroManager::getMainWindowWithIdentifier(const QString& identifier) +{ + if (m_macroHelper != NULL) { + return m_macroHelper->getMainWindowWithIdentifier(identifier); + } + return NULL; +} diff --git a/src/GuiQt/WuQMacroManager.h b/src/GuiQt/WuQMacroManager.h index bae8a8b38..ac5d94668 100644 --- a/src/GuiQt/WuQMacroManager.h +++ b/src/GuiQt/WuQMacroManager.h @@ -32,6 +32,7 @@ #include "WuQMacroShortCutKeyEnum.h" class QKeyEvent; +class QMainWindow; class QMouseEvent; class QWidget; @@ -146,6 +147,10 @@ namespace caret { virtual WuQMacroCommand* newInstanceOfCustomOperationMacroCommand(const QString& macroCommandName, QString& errorMessageOut); + std::vector getMainWindowIdentifiers(); + + QMainWindow* getMainWindowWithIdentifier(const QString& identifier); + // ADD_NEW_METHODS_HERE public slots: -- GitLab From bdcd9b5d98e2e7877ec92da0fca15188e88db194 Mon Sep 17 00:00:00 2001 From: John Harwell Date: Tue, 5 Feb 2019 15:52:20 -0600 Subject: [PATCH 179/427] WB-834 Macros: A few layout improvements on Macros Dialog. --- src/GuiQt/WuQMacroDialog.cxx | 33 +++++++++++++++++++++++++++------ 1 file changed, 27 insertions(+), 6 deletions(-) diff --git a/src/GuiQt/WuQMacroDialog.cxx b/src/GuiQt/WuQMacroDialog.cxx index 3fbb71a6f..db2d0d6d5 100644 --- a/src/GuiQt/WuQMacroDialog.cxx +++ b/src/GuiQt/WuQMacroDialog.cxx @@ -52,6 +52,7 @@ #include "WuQMacroGroup.h" #include "WuQMacroExecutor.h" #include "WuQMacroManager.h" +#include "WuQMacroMouseEventInfo.h" #include "WuQMacroShortCutKeyComboBox.h" #include "WuQMacroStandardItemTypeEnum.h" #include "WuQtUtilities.h" @@ -134,6 +135,8 @@ WuQMacroDialog::WuQMacroDialog(QWidget* parent) splitter->addWidget(macroSelectionWidget); stackedScrollArea->setMinimumHeight(50); splitter->addWidget(stackedScrollArea); + splitter->setStretchFactor(0, 35); + splitter->setStretchFactor(1, 65); QVBoxLayout* dialogLayout = new QVBoxLayout(this); dialogLayout->addLayout(gridLayout); @@ -504,7 +507,7 @@ WuQMacroDialog::createCommandDisplayWidget() layout->addWidget(m_commandDelaySpinBox, row, 1); row++; layout->addWidget(toolTipLabel, row, 0, (Qt::AlignLeft | Qt::AlignTop)); - layout->addWidget(m_commandToolTipTextEdit, row, 1); + layout->addWidget(m_commandToolTipTextEdit, row, 1, 1, 2); row++; layout->addWidget(parametersLabel, row, 0, 1, 2, Qt::AlignLeft); row++; @@ -701,11 +704,29 @@ WuQMacroDialog::updateCommandWidget(WuQMacroCommand* command) QString toolTip; float delay(0.0f); if (command != NULL) { - title = command->text(); - name = command->getObjectName(); - type = WuQMacroWidgetTypeEnum::toGuiName(command->getWidgetType()); - toolTip = command->getObjectToolTip(); - delay = command->getDelayInSeconds(); + title = command->text(); + name = command->getObjectName(); + switch (command->getCommandType()) { + case WuQMacroCommandTypeEnum::CUSTOM_OPERATION: + { + const QString operationName = command->getCustomOperationTypeName(); + type = operationName; + } + break; + case WuQMacroCommandTypeEnum::MOUSE: + { + WuQMacroMouseEventInfo* mouseInfo = command->getMouseEventInfo(); + CaretAssert(mouseInfo); + WuQMacroMouseEventTypeEnum::Enum mouseEventType = mouseInfo->getMouseEventType(); + type = WuQMacroMouseEventTypeEnum::toGuiName(mouseEventType); + } + break; + case WuQMacroCommandTypeEnum::WIDGET: + type = WuQMacroWidgetTypeEnum::toGuiName(command->getWidgetType()); + break; + } + toolTip = command->getObjectToolTip(); + delay = command->getDelayInSeconds(); } m_commandTitleLabel->setText(title); m_commandNameLabel->setText(name); -- GitLab From 378d367ce84423af5ddd0a5f7768868b9f6bb5e2 Mon Sep 17 00:00:00 2001 From: John Harwell Date: Wed, 6 Feb 2019 11:09:55 -0600 Subject: [PATCH 180/427] WB-834 Macros: Implemented a custom macro command for rotation. --- src/GuiQt/CMakeLists.txt | 2 + src/GuiQt/WbMacroCustomDataTypeEnum.cxx | 9 +- src/GuiQt/WbMacroCustomDataTypeEnum.h | 3 +- src/GuiQt/WbMacroCustomOperationManager.cxx | 72 ++++-- .../WbMacroCustomOperationModelRotation.cxx | 209 ++++++++++++++++++ .../WbMacroCustomOperationModelRotation.h | 76 +++++++ src/GuiQt/WbMacroCustomOperationTypeEnum.cxx | 9 +- src/GuiQt/WbMacroCustomOperationTypeEnum.h | 4 +- 8 files changed, 360 insertions(+), 24 deletions(-) create mode 100644 src/GuiQt/WbMacroCustomOperationModelRotation.cxx create mode 100644 src/GuiQt/WbMacroCustomOperationModelRotation.h diff --git a/src/GuiQt/CMakeLists.txt b/src/GuiQt/CMakeLists.txt index 02af4408e..6e2eb8e21 100644 --- a/src/GuiQt/CMakeLists.txt +++ b/src/GuiQt/CMakeLists.txt @@ -450,6 +450,7 @@ VolumeSurfaceOutlineViewController.h WbMacroCustomDataTypeEnum.h WbMacroCustomOperationBase.h WbMacroCustomOperationManager.h +WbMacroCustomOperationModelRotation.h WbMacroCustomOperationSurfaceInterpolation.h WbMacroCustomOperationTypeEnum.h WbMacroHelper.h @@ -701,6 +702,7 @@ VolumeSurfaceOutlineViewController.cxx WbMacroCustomDataTypeEnum.cxx WbMacroCustomOperationBase.cxx WbMacroCustomOperationManager.cxx +WbMacroCustomOperationModelRotation.cxx WbMacroCustomOperationSurfaceInterpolation.cxx WbMacroCustomOperationTypeEnum.cxx WbMacroHelper.cxx diff --git a/src/GuiQt/WbMacroCustomDataTypeEnum.cxx b/src/GuiQt/WbMacroCustomDataTypeEnum.cxx index cb6bb7acf..2ddcf6018 100644 --- a/src/GuiQt/WbMacroCustomDataTypeEnum.cxx +++ b/src/GuiQt/WbMacroCustomDataTypeEnum.cxx @@ -106,10 +106,13 @@ WbMacroCustomDataTypeEnum::initialize() } initializedFlag = true; - enumData.push_back(WbMacroCustomDataTypeEnum(SURFACE, - "SURFACE", - "Surface")); + enumData.push_back(WbMacroCustomDataTypeEnum(SCREEN_AXIS, + "SCREEN_AXIS", + "Screen Axis")); + enumData.push_back(WbMacroCustomDataTypeEnum(SURFACE, + "SURFACE", + "Surface")); } /** diff --git a/src/GuiQt/WbMacroCustomDataTypeEnum.h b/src/GuiQt/WbMacroCustomDataTypeEnum.h index 20a290d3b..d24fab945 100644 --- a/src/GuiQt/WbMacroCustomDataTypeEnum.h +++ b/src/GuiQt/WbMacroCustomDataTypeEnum.h @@ -35,7 +35,8 @@ public: * Enumerated values. */ enum Enum { - /** */ + SCREEN_AXIS, + /** Surface selection */ SURFACE }; diff --git a/src/GuiQt/WbMacroCustomOperationManager.cxx b/src/GuiQt/WbMacroCustomOperationManager.cxx index 9e6913521..6709149a5 100644 --- a/src/GuiQt/WbMacroCustomOperationManager.cxx +++ b/src/GuiQt/WbMacroCustomOperationManager.cxx @@ -32,6 +32,7 @@ #include "GuiManager.h" #include "Surface.h" #include "WbMacroCustomDataTypeEnum.h" +#include "WbMacroCustomOperationModelRotation.h" #include "WbMacroCustomOperationSurfaceInterpolation.h" #include "WbMacroCustomOperationTypeEnum.h" #include "WuQMacroCommand.h" @@ -109,6 +110,27 @@ WbMacroCustomOperationManager::editCustomDataValueParameter(QWidget* parent, QString errorMessage; switch (userType) { + case WbMacroCustomDataTypeEnum::SCREEN_AXIS: + { + QStringList axisList; + axisList.append("X"); + axisList.append("Y"); + axisList.append("Z"); + + bool okFlag(false); + QString selectedName = QInputDialog::getItem(parent, + "Choose Surface", + parameter->getName(), + axisList, + 0, + false, + &okFlag); + if (okFlag) { + parameter->setValue(selectedName); + modFlag = true; + } + } + break; case WbMacroCustomDataTypeEnum::SURFACE: { EventSurfacesGet surfacesEvent; @@ -187,20 +209,29 @@ WbMacroCustomOperationManager::executeCustomOperationMacroCommand(QWidget* paren return false; } - bool successFlag(false); + std::unique_ptr customOperation; switch (commandType) { + case WbMacroCustomOperationTypeEnum::MODEL_ROTATION: + customOperation.reset(new WbMacroCustomOperationModelRotation()); + break; case WbMacroCustomOperationTypeEnum::SURFACE_INTERPOLATION: - { - WbMacroCustomOperationSurfaceInterpolation surfaceInterpolation; - successFlag = surfaceInterpolation.executeCommand(parent, - customMacroCommand); - if ( ! successFlag) { - errorMessageOut = surfaceInterpolation.getErrorMessage(); - } - } + customOperation.reset(new WbMacroCustomOperationSurfaceInterpolation()); break; } + bool successFlag(false); + if (customOperation) { + successFlag = customOperation->executeCommand(parent, + customMacroCommand); + if ( ! successFlag) { + errorMessageOut = customOperation->getErrorMessage(); + } + } + else { + errorMessageOut = "Custom Operation is missing"; + CaretLogSevere(errorMessageOut); + } + return successFlag; } @@ -248,17 +279,26 @@ WbMacroCustomOperationManager::newInstanceOfCustomOperationMacroCommand(const QS WuQMacroCommand* command(NULL); + std::unique_ptr customOperation; switch (commandType) { + case WbMacroCustomOperationTypeEnum::MODEL_ROTATION: + customOperation.reset(new WbMacroCustomOperationModelRotation()); + break; case WbMacroCustomOperationTypeEnum::SURFACE_INTERPOLATION: - { - WbMacroCustomOperationSurfaceInterpolation surfaceInterpolator; - command = surfaceInterpolator.createCommand(); - if (command == NULL) { - errorMessageOut = surfaceInterpolator.getErrorMessage(); - } - } + customOperation.reset(new WbMacroCustomOperationSurfaceInterpolation()); break; } + + if (customOperation) { + command = customOperation->createCommand(); + if (command == NULL) { + errorMessageOut = customOperation->getErrorMessage(); + } + } + else { + errorMessageOut = "Custom Operation is missing"; + CaretLogSevere(errorMessageOut); + } return command; } diff --git a/src/GuiQt/WbMacroCustomOperationModelRotation.cxx b/src/GuiQt/WbMacroCustomOperationModelRotation.cxx new file mode 100644 index 000000000..ffaadff6e --- /dev/null +++ b/src/GuiQt/WbMacroCustomOperationModelRotation.cxx @@ -0,0 +1,209 @@ + +/*LICENSE_START*/ +/* + * Copyright (C) 2019 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + +#define __WB_MACRO_CUSTOM_OPERATION_MODEL_ROTATION_DECLARE__ +#include "WbMacroCustomOperationModelRotation.h" +#undef __WB_MACRO_CUSTOM_OPERATION_MODEL_ROTATION_DECLARE__ + +#include "BrainBrowserWindow.h" +#include "BrowserTabContent.h" +#include "CaretAssert.h" +#include "Matrix4x4.h" +#include "Model.h" +#include "SystemUtilities.h" +#include "WbMacroCustomDataTypeEnum.h" +#include "WbMacroCustomOperationTypeEnum.h" +#include "WuQMacroCommand.h" +#include "WuQMacroCommandParameter.h" + +using namespace caret; + + + +/** + * \class caret::WbMacroCustomOperationModelRotation + * \brief Macro custom operation for model rotation + * \ingroup GuiQt + */ + +/** + * Constructor. + */ +WbMacroCustomOperationModelRotation::WbMacroCustomOperationModelRotation() +: WbMacroCustomOperationBase() +{ + +} + +/** + * Destructor. + */ +WbMacroCustomOperationModelRotation::~WbMacroCustomOperationModelRotation() +{ +} + +/** + * Get a new instance of the macro command + * + * @return + * Pointer to command or NULL if not valid + * Use getErrorMessage() for error information if NULL returned + */ +WuQMacroCommand* +WbMacroCustomOperationModelRotation::createCommand() +{ + const int32_t versionOne(1); + + WuQMacroCommandParameter* paramOne = new WuQMacroCommandParameter(WuQMacroDataValueTypeEnum::CUSTOM_DATA, + "Screen Axis", + "Y"); + paramOne->setCustomDataType(WbMacroCustomDataTypeEnum::toName(WbMacroCustomDataTypeEnum::SCREEN_AXIS)); + + QString errorMessage; + WuQMacroCommand* command = WuQMacroCommand::newInstanceCustomCommand(WbMacroCustomOperationTypeEnum::toName(WbMacroCustomOperationTypeEnum::MODEL_ROTATION), + versionOne, + "none", + "Model Rotation", + "Rotate a Model", + 1.0, + errorMessage); + if (command != NULL) { + command->addParameter(paramOne); + command->addParameter(WuQMacroDataValueTypeEnum::FLOAT, + "Total Rotation (Degrees)", + (float)360.0); + command->addParameter(WuQMacroDataValueTypeEnum::FLOAT, + "Duration (secs)", + (float)15.0); + } + else { + appendToErrorMessage(errorMessage); + } + + return command; +} + +/** + * Execute the macro command + * + * @param parent + * Parent widget for any dialogs + * @param macroCommand + * macro command to run + * @return + * True if command executed successfully, else false + * Use getErrorMessage() for error information if false returned + */ +bool +WbMacroCustomOperationModelRotation::executeCommand(QWidget* parent, + const WuQMacroCommand* macroCommand) +{ + CaretAssert(parent); + CaretAssert(macroCommand); + + validateCorrectNumberOfParameters(macroCommand); + + BrainBrowserWindow* bbw = qobject_cast(parent); + if (bbw == NULL) { + appendToErrorMessage("Parent for running surface macro is not a browser window."); + return false; + } + + BrowserTabContent* tabContent = bbw->getBrowserTabContent(); + if (tabContent == NULL) { + appendToErrorMessage("No tab is selected in browser window."); + return false; + } + + const QString axisName(macroCommand->getParameterAtIndex(0)->getValue().toString().toUpper()); + const float totalRotation = macroCommand->getParameterAtIndex(1)->getValue().toFloat(); + const float durationSeconds = macroCommand->getParameterAtIndex(2)->getValue().toFloat(); + + Axis axis = Axis::X; + if (axisName == "X") { + axis = Axis::X; + } + else if (axisName == "Y") { + axis = Axis::Y; + } + else if (axisName == "Z") { + axis = Axis::Z; + } + else { + appendToErrorMessage("Axis named \"" + + axisName + + "\" is invalid. Use X, Y, or Z."); + } + + if (totalRotation < 0.0) { + appendToErrorMessage("Total Rotation must be greater than zero."); + } + if (durationSeconds < 0.0) { + appendToErrorMessage("Duration must be greater than zero."); + } + + if ( ! getErrorMessage().isEmpty()) { + return false; + } + Model* model = tabContent->getModelForDisplay(); + if (model != NULL) { + } + + const bool successFlag = performRotation(tabContent, + axis, + totalRotation, + durationSeconds); + return successFlag; +} + +bool +WbMacroCustomOperationModelRotation::performRotation(BrowserTabContent* tabContent, + const Axis axis, + const float totalRotation, + const float durationSeconds) +{ + + const double rotationIncrement = 1.0; + const int32_t rotationStepCount = static_cast(totalRotation / rotationIncrement); + const float sleepTimeSeconds = durationSeconds / rotationStepCount; + + for (int32_t i = 0; i < rotationStepCount; i++) { + Matrix4x4 rotationMatrix = tabContent->getRotationMatrix(); + switch (axis) { + case Axis::X: + rotationMatrix.rotateX(rotationIncrement); + break; + case Axis::Y: + rotationMatrix.rotateY(rotationIncrement); + break; + case Axis::Z: + rotationMatrix.rotateZ(rotationIncrement); + break; + } + + tabContent->setRotationMatrix(rotationMatrix); + updateGraphics(); + + SystemUtilities::sleepSeconds(sleepTimeSeconds); + } + + return true; +} diff --git a/src/GuiQt/WbMacroCustomOperationModelRotation.h b/src/GuiQt/WbMacroCustomOperationModelRotation.h new file mode 100644 index 000000000..2f187eb8e --- /dev/null +++ b/src/GuiQt/WbMacroCustomOperationModelRotation.h @@ -0,0 +1,76 @@ +#ifndef __WB_MACRO_CUSTOM_OPERATION_MODEL_ROTATION_H__ +#define __WB_MACRO_CUSTOM_OPERATION_MODEL_ROTATION_H__ + +/*LICENSE_START*/ +/* + * Copyright (C) 2019 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + + + +#include + +#include "WbMacroCustomOperationBase.h" + + + +namespace caret { + + class BrowserTabContent; + + class WbMacroCustomOperationModelRotation : public WbMacroCustomOperationBase { + + public: + WbMacroCustomOperationModelRotation(); + + virtual ~WbMacroCustomOperationModelRotation(); + + WbMacroCustomOperationModelRotation(const WbMacroCustomOperationModelRotation&) = delete; + + WbMacroCustomOperationModelRotation& operator=(const WbMacroCustomOperationModelRotation&) = delete; + + virtual bool executeCommand(QWidget* parent, + const WuQMacroCommand* macroCommand) override; + + virtual WuQMacroCommand* createCommand() override; + + + // ADD_NEW_METHODS_HERE + + private: + enum class Axis { + X, + Y, + Z + }; + + bool performRotation(BrowserTabContent* tabContent, + const Axis axis, + const float totalRotation, + const float durationSeconds); + + // ADD_NEW_MEMBERS_HERE + + }; + +#ifdef __WB_MACRO_CUSTOM_OPERATION_MODEL_ROTATION_DECLARE__ + // +#endif // __WB_MACRO_CUSTOM_OPERATION_MODEL_ROTATION_DECLARE__ + +} // namespace +#endif //__WB_MACRO_CUSTOM_OPERATION_MODEL_ROTATION_H__ diff --git a/src/GuiQt/WbMacroCustomOperationTypeEnum.cxx b/src/GuiQt/WbMacroCustomOperationTypeEnum.cxx index 79a7f156e..32fe389ed 100644 --- a/src/GuiQt/WbMacroCustomOperationTypeEnum.cxx +++ b/src/GuiQt/WbMacroCustomOperationTypeEnum.cxx @@ -106,10 +106,13 @@ WbMacroCustomOperationTypeEnum::initialize() } initializedFlag = true; + enumData.push_back(WbMacroCustomOperationTypeEnum(MODEL_ROTATION, + "MODEL_ROTATION", + "Model Rotation")); + enumData.push_back(WbMacroCustomOperationTypeEnum(SURFACE_INTERPOLATION, - "SURFACE_INTERPOLATION", - "Surface Interpolation")); - + "SURFACE_INTERPOLATION", + "Surface Interpolation")); } /** diff --git a/src/GuiQt/WbMacroCustomOperationTypeEnum.h b/src/GuiQt/WbMacroCustomOperationTypeEnum.h index 8d9370b48..f9bf59a75 100644 --- a/src/GuiQt/WbMacroCustomOperationTypeEnum.h +++ b/src/GuiQt/WbMacroCustomOperationTypeEnum.h @@ -35,7 +35,9 @@ public: * Enumerated values. */ enum Enum { - /** */ + /** Rotation of viewed model */ + MODEL_ROTATION, + /** Surface Interpolation */ SURFACE_INTERPOLATION }; -- GitLab From 59fb88a1d644683aac1a29c483955e79b4a1b730 Mon Sep 17 00:00:00 2001 From: John Harwell Date: Wed, 6 Feb 2019 12:29:20 -0600 Subject: [PATCH 181/427] WB-834 Macros: When a scene is replaced, transfer the old scene's macros to the new scene. Also, layout improvements on Macros Dialog. --- src/Common/WuQMacroGroup.cxx | 24 ++++++++++++++++++++++++ src/Common/WuQMacroGroup.h | 2 ++ src/Files/SceneFile.cxx | 5 +++++ src/GuiQt/WuQMacroDialog.cxx | 23 ++++++++++++++--------- src/Scenes/Scene.cxx | 19 +++++++++++++++++++ src/Scenes/Scene.h | 2 ++ 6 files changed, 66 insertions(+), 9 deletions(-) diff --git a/src/Common/WuQMacroGroup.cxx b/src/Common/WuQMacroGroup.cxx index 00a029605..8ff4550ef 100644 --- a/src/Common/WuQMacroGroup.cxx +++ b/src/Common/WuQMacroGroup.cxx @@ -476,6 +476,30 @@ WuQMacroGroup::moveMacroUp(WuQMacro* macro) } } +/** + * Take all macros from this macro group. After calling + * this method, 'this' macro group contains no macros + * + * @return All macros from this group. + */ +std::vector +WuQMacroGroup::takeAllMacros() +{ + std::vector macrosOut; + + const int32_t numMacros = rowCount(); + for (int32_t i = 0; i < numMacros; i++) { + WuQMacro* macro = dynamic_cast(takeItem(i)); + CaretAssert(macro); + macrosOut.push_back(macro); + } + + removeRows(0, numMacros); + CaretAssert(rowCount() == 0); + + return macrosOut; +} + /** * @return True if this instance is modified */ diff --git a/src/Common/WuQMacroGroup.h b/src/Common/WuQMacroGroup.h index b4b19b5c0..6c5928908 100644 --- a/src/Common/WuQMacroGroup.h +++ b/src/Common/WuQMacroGroup.h @@ -88,6 +88,8 @@ namespace caret { void moveMacroUp(WuQMacro* macro); + std::vector takeAllMacros(); + virtual bool isModified() const override; virtual void clearModified() override; diff --git a/src/Files/SceneFile.cxx b/src/Files/SceneFile.cxx index 1399da267..c1db01496 100644 --- a/src/Files/SceneFile.cxx +++ b/src/Files/SceneFile.cxx @@ -202,6 +202,11 @@ SceneFile::replaceScene(Scene* newScene, const int32_t numScenes = getNumberOfScenes(); for (int32_t i = 0; i < numScenes; i++) { if (m_scenes[i] == sceneThatIsReplacedAndDeleted) { + /* + * Preserve macros by moving to new scene + */ + newScene->moveMacrosFromScene(sceneThatIsReplacedAndDeleted); + delete m_scenes[i]; m_scenes[i] = newScene; setModified(); diff --git a/src/GuiQt/WuQMacroDialog.cxx b/src/GuiQt/WuQMacroDialog.cxx index db2d0d6d5..5280ac0dd 100644 --- a/src/GuiQt/WuQMacroDialog.cxx +++ b/src/GuiQt/WuQMacroDialog.cxx @@ -95,9 +95,9 @@ WuQMacroDialog::WuQMacroDialog(QWidget* parent) QGridLayout* gridLayout = new QGridLayout(); gridLayout->setContentsMargins(0, 0, 0, 0); - gridLayout->setRowStretch(0, 0); - gridLayout->setRowStretch(1, 0); - gridLayout->setRowStretch(2, 100); + gridLayout->setColumnStretch(0, 0); + gridLayout->setColumnStretch(1, 100); + gridLayout->setColumnStretch(2, 0); int row = 0; gridLayout->addWidget(macroGroupLabel, row, 0); gridLayout->addWidget(m_macroGroupComboBox, row, 1); @@ -220,10 +220,11 @@ WuQMacroDialog::createMacroRunAndEditingToolButtons() toolButtonLayout->setContentsMargins(0, 0, 0, 0); toolButtonLayout->addWidget(m_runMacroToolButton); toolButtonLayout->addStretch(); + toolButtonLayout->addWidget(m_editingInsertToolButton); + toolButtonLayout->addSpacing(10); toolButtonLayout->addWidget(m_editingMoveUpToolButton); toolButtonLayout->addWidget(m_editingMoveDownToolButton); - toolButtonLayout->addWidget(m_editingInsertToolButton); - toolButtonLayout->addSpacing(15); + toolButtonLayout->addSpacing(10); toolButtonLayout->addWidget(m_editingDeleteToolButton); return widget; @@ -301,6 +302,7 @@ WuQMacroDialog::createMacroDisplayWidget() QWidget* widget = new QWidget(); QVBoxLayout* layout = new QVBoxLayout(widget); + layout->setSpacing(layout->spacing() / 2); layout->addLayout(titleLayout); layout->addLayout(gridLayout); layout->addLayout(runOptionsTitleLayout); @@ -487,7 +489,6 @@ WuQMacroDialog::createCommandDisplayWidget() m_commandToolTipTextEdit->setReadOnly(true); m_commandToolTipTextEdit->setMaximumHeight(100); - QLabel* parametersLabel = new QLabel("Parameters: "); QGridLayout* layout = new QGridLayout(); layout->setContentsMargins(0, 0, 0, 0); layout->setColumnStretch(0, 0); @@ -509,13 +510,17 @@ WuQMacroDialog::createCommandDisplayWidget() layout->addWidget(toolTipLabel, row, 0, (Qt::AlignLeft | Qt::AlignTop)); layout->addWidget(m_commandToolTipTextEdit, row, 1, 1, 2); row++; - layout->addWidget(parametersLabel, row, 0, 1, 2, Qt::AlignLeft); - row++; QWidget* parametersWidget = new QWidget(); m_parameterWidgetsGridLayout = new QGridLayout(parametersWidget); + m_parameterWidgetsGridLayout->setVerticalSpacing(static_cast(m_parameterWidgetsGridLayout->verticalSpacing() / 1.2)); m_parameterWidgetsGridLayout->setColumnStretch(0, 0); m_parameterWidgetsGridLayout->setColumnStretch(1, 100); + m_parameterWidgetsGridLayout->addWidget(new QLabel("Edit"), + 0, 0, Qt::AlignHCenter); + m_parameterWidgetsGridLayout->addWidget(new QLabel("Parameter Value"), + 0, 1, Qt::AlignLeft); + QHBoxLayout* titleLayout = new QHBoxLayout(); titleLayout->setContentsMargins(0, 0, 0, 0); @@ -1487,7 +1492,7 @@ CommandParameterWidget::updateContent(WuQMacroCommandParameter* parameter) const bool validFlag(m_parameter != NULL); if (validFlag) { m_label->setText(m_parameter->getValue().toString()); - m_pushButton->setText(m_parameter->getName()); + m_pushButton->setText(m_parameter->getName() + "..."); } m_label->setVisible(validFlag); m_pushButton->setVisible(validFlag); diff --git a/src/Scenes/Scene.cxx b/src/Scenes/Scene.cxx index 32acb4a18..6e31836b1 100644 --- a/src/Scenes/Scene.cxx +++ b/src/Scenes/Scene.cxx @@ -557,5 +557,24 @@ Scene::getMacroGroup() const return m_macroGroup.get(); } +/** + * Move any macros in the given scene to this scene + * + * @param scene + * Scene whose macros are moved to this scene. + */ +void +Scene::moveMacrosFromScene(Scene* scene) +{ + CaretAssert(scene); + std::vector macros = scene->getMacroGroup()->takeAllMacros(); + if ( ! macros.empty()) { + for (auto m : macros) { + getMacroGroup()->addMacro(m); + } + } + setModified(); +} + diff --git a/src/Scenes/Scene.h b/src/Scenes/Scene.h index 97430a743..f89ab2dd9 100644 --- a/src/Scenes/Scene.h +++ b/src/Scenes/Scene.h @@ -97,6 +97,8 @@ namespace caret { const WuQMacroGroup* getMacroGroup() const; + void moveMacrosFromScene(Scene* scene); + private: /** Attributes of the scene*/ -- GitLab From 09fb7ea11fb3b8aaf88c534d51bb21d366ce8700 Mon Sep 17 00:00:00 2001 From: John Harwell Date: Mon, 11 Feb 2019 16:01:11 -0600 Subject: [PATCH 182/427] WB-834 Macros: Implemented movie recording and various improvements to macros. --- src/Brain/CMakeLists.txt | 8 + src/Brain/MovieRecorder.cxx | 674 ++++++++++++++++++ src/Brain/MovieRecorder.h | 159 +++++ src/Brain/MovieRecorderModeEnum.cxx | 372 ++++++++++ src/Brain/MovieRecorderModeEnum.h | 104 +++ .../MovieRecorderVideoDimensionsTypeEnum.cxx | 377 ++++++++++ .../MovieRecorderVideoDimensionsTypeEnum.h | 106 +++ .../MovieRecorderVideoFormatTypeEnum.cxx | 404 +++++++++++ src/Brain/MovieRecorderVideoFormatTypeEnum.h | 114 +++ src/Brain/SessionManager.cxx | 19 + src/Brain/SessionManager.h | 6 + src/Common/EventManager.cxx | 1 + src/Common/EventTypeEnum.cxx | 4 + src/Common/EventTypeEnum.h | 2 + src/GuiQt/BrainBrowserWindow.cxx | 21 +- src/GuiQt/BrainBrowserWindow.h | 3 + src/GuiQt/BrainBrowserWindowToolBarTab.cxx | 2 +- src/GuiQt/BrainOpenGLWidget.cxx | 106 ++- src/GuiQt/CMakeLists.txt | 3 + src/GuiQt/EventMovieDialogUpdate.cxx | 56 ++ src/GuiQt/EventMovieDialogUpdate.h | 58 ++ src/GuiQt/GuiManager.cxx | 26 +- src/GuiQt/GuiManager.h | 4 + src/GuiQt/MovieRecordingDialog.cxx | 616 ++++++++++++++++ src/GuiQt/MovieRecordingDialog.h | 143 ++++ src/GuiQt/WbMacroCustomOperationBase.cxx | 56 ++ src/GuiQt/WbMacroCustomOperationBase.h | 7 + .../WbMacroCustomOperationModelRotation.cxx | 22 +- ...croCustomOperationSurfaceInterpolation.cxx | 19 +- src/GuiQt/WuQMacroDialog.cxx | 180 ++++- src/GuiQt/WuQMacroDialog.h | 13 +- src/GuiQt/WuQMacroExecutor.cxx | 67 +- src/GuiQt/WuQMacroExecutor.h | 9 + src/GuiQt/WuQMacroExecutorOptions.cxx | 22 + src/GuiQt/WuQMacroExecutorOptions.h | 6 + src/GuiQt/WuQMacroManager.cxx | 25 +- src/GuiQt/WuQMacroManager.h | 8 + 37 files changed, 3748 insertions(+), 74 deletions(-) create mode 100644 src/Brain/MovieRecorder.cxx create mode 100644 src/Brain/MovieRecorder.h create mode 100644 src/Brain/MovieRecorderModeEnum.cxx create mode 100644 src/Brain/MovieRecorderModeEnum.h create mode 100644 src/Brain/MovieRecorderVideoDimensionsTypeEnum.cxx create mode 100644 src/Brain/MovieRecorderVideoDimensionsTypeEnum.h create mode 100644 src/Brain/MovieRecorderVideoFormatTypeEnum.cxx create mode 100644 src/Brain/MovieRecorderVideoFormatTypeEnum.h create mode 100644 src/GuiQt/EventMovieDialogUpdate.cxx create mode 100644 src/GuiQt/EventMovieDialogUpdate.h create mode 100644 src/GuiQt/MovieRecordingDialog.cxx create mode 100644 src/GuiQt/MovieRecordingDialog.h diff --git a/src/Brain/CMakeLists.txt b/src/Brain/CMakeLists.txt index 4ffaab51b..78a8cfb78 100644 --- a/src/Brain/CMakeLists.txt +++ b/src/Brain/CMakeLists.txt @@ -126,6 +126,10 @@ ModelSurfaceSelector.h ModelTypeEnum.h ModelVolume.h ModelWholeBrain.h +MovieRecorder.h +MovieRecorderModeEnum.h +MovieRecorderVideoDimensionsTypeEnum.h +MovieRecorderVideoFormatTypeEnum.h Overlay.h OverlaySet.h OverlaySetArray.h @@ -278,6 +282,10 @@ ModelSurfaceSelector.cxx ModelTypeEnum.cxx ModelVolume.cxx ModelWholeBrain.cxx +MovieRecorder.cxx +MovieRecorderModeEnum.cxx +MovieRecorderVideoDimensionsTypeEnum.cxx +MovieRecorderVideoFormatTypeEnum.cxx Overlay.cxx OverlaySet.cxx OverlaySetArray.cxx diff --git a/src/Brain/MovieRecorder.cxx b/src/Brain/MovieRecorder.cxx new file mode 100644 index 000000000..8c20e3bcb --- /dev/null +++ b/src/Brain/MovieRecorder.cxx @@ -0,0 +1,674 @@ + +/*LICENSE_START*/ +/* + * Copyright (C) 2019 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + +#define __MOVIE_RECORDER_DECLARE__ +#include "MovieRecorder.h" +#undef __MOVIE_RECORDER_DECLARE__ + +#include +#include +#include +#include + +#include "CaretAssert.h" +#include "CaretLogger.h" +#include "DataFileException.h" +#include "FileInformation.h" +#include "ImageFile.h" +#include "TextFile.h" + +using namespace caret; + + + +/** + * \class caret::MovieRecorder + * \brief Records images and creates movie file from images + * \ingroup Brain + */ + +/** + * Constructor. + */ +MovieRecorder::MovieRecorder() +: CaretObject() +{ + m_temporaryImagesDirectory = SystemUtilities::getTempDirectory(); + m_tempImageFileNamePrefix = "movie"; + m_tempImageFileNameSuffix = ".png"; + //QDir::tempPath(); + reset(); +} + +/** + * Destructor. + */ +MovieRecorder::~MovieRecorder() +{ + reset(); +} + +/** + * Add an image to the movie, typically used during automatic mode recording + * + * @param image + * Image that is added + */ +void +MovieRecorder::addImageToMovie(const QImage* image) +{ + if (image == NULL) { + CaretLogSevere("Attempting to add NULL image to movie"); + return; + } + + CaretAssert(m_tempImageSequenceNumberOfDigits > 0); + + /* + * First image starts at 1 and is padded with zeros on the left + */ + const QString imageIndex = QString::number(getNumberOfFrames() + 1).rightJustified(m_tempImageSequenceNumberOfDigits, '0'); + const QString imageFileName(m_temporaryImagesDirectory + + "/" + + m_tempImageFileNamePrefix + + imageIndex + + m_tempImageFileNameSuffix); + + if (image->save(imageFileName)) { + m_imageFileNames.push_back(imageFileName); + } + else { + CaretLogSevere("Saving temporary image failed: " + + imageFileName); + } +// std::cout << "Adding image " << image->width() << ", " << image->height() << " " << imageFileName << std::endl; + + +} + +/** + * Add an image to the movie, typically used during automatic mode recording. + * After image is added, manual recording is disabled + * + * @param image + * Image that is added + */ +void +MovieRecorder::addImageToMovieWithManualDuration(const QImage* image) +{ + const int32_t recordingCount = m_frameRate * m_manualRecordingDurationSeconds; + for (int32_t i = 0; i < recordingCount; i++) { + addImageToMovie(image); + } + setManualRecordingOfImageRequested(false); +} + +/** + * Reset by removing all images and starting a new movie + */ +void +MovieRecorder::reset() +{ + const QString nameFilter(m_tempImageFileNamePrefix + + "*" + + m_tempImageFileNameSuffix); + QStringList allNameFilters; + allNameFilters.append(nameFilter); + QDir dir(m_temporaryImagesDirectory); + QFileInfoList fileInfoList = dir.entryInfoList(allNameFilters, + QDir::Files, + QDir::Name); + QListIterator iter(fileInfoList); + while (iter.hasNext()) { + QFile file(iter.next().absoluteFilePath()); + if (file.exists()) { + const bool successFlag = file.remove(); + } + } + + m_imageFileNames.clear(); +} + + +/** + * @return The recording mode + */ +MovieRecorderModeEnum::Enum +MovieRecorder::getRecordingMode() const +{ + return m_recordingMode; +} + +/** + * Set the recording mode + * + * @param recordingMode + * New recording mode + */ +void +MovieRecorder::setRecordingMode(const MovieRecorderModeEnum::Enum recordingMode) +{ + m_recordingMode = recordingMode; +} + +/** + * @return Index of window that is recorded + */ +int32_t +MovieRecorder::getRecordingWindowIndex() const +{ + return m_windowIndex; +} + +/** + * Set index of window that is recorded + * + * @param windowIndex + * Index of window + */ +void +MovieRecorder::setRecordingWindowIndex(const int32_t windowIndex) +{ + m_windowIndex = windowIndex; +} + +/** + * @return Video dimensions type + */ +MovieRecorderVideoDimensionsTypeEnum::Enum +MovieRecorder::getVideoDimensionsType() const +{ + return m_dimensionsType; +} + +/** + * Set the video dimensions type + * + * @param dimensionsType + * New dimensions type + */ +void +MovieRecorder::setVideoDimensionsType(const MovieRecorderVideoDimensionsTypeEnum::Enum dimensionsType) +{ + m_dimensionsType = dimensionsType; +} + +/** + * Get the video dimensions + * + * @param widthOut + * Output width + * @param heightOut + * Output height + */ +void +MovieRecorder::getVideoDimensions(int32_t& widthOut, + int32_t& heightOut) const +{ + widthOut = 100; + heightOut = 100; + switch (getVideoDimensionsType()) { + case MovieRecorderVideoDimensionsTypeEnum::CUSTOM: + getCustomDimensions(widthOut, + heightOut); + break; + case MovieRecorderVideoDimensionsTypeEnum::HD_1280_720: + widthOut = 1280; + heightOut = 720; + break; + case MovieRecorderVideoDimensionsTypeEnum::SD_640_480: + widthOut = 640; + heightOut = 480; + break; + } +} + +/** + * Get the custom dimensions + * + * @param widthOut + * Output width + * @param heightOut + * Output height + */ +void +MovieRecorder::getCustomDimensions(int32_t& widthOut, + int32_t& heightOut) const +{ + widthOut = m_customWidth; + heightOut = m_customHeight; +} + +/** + * Set the custom dimensions + * + * @param width + * New width + * @param height + * New height + */ +void +MovieRecorder::setCustomDimensions(const int32_t width, + const int32_t height) +{ + m_customWidth = width; + m_customHeight = height; +} + +/** + * @return Video format type + */ +MovieRecorderVideoFormatTypeEnum::Enum +MovieRecorder::getVideoFormatType() const +{ + return m_formatType; +} + +/** + * Set the video format type + * + * @param formatType + * New format type + */ +void +MovieRecorder::setVideoFormatType(const MovieRecorderVideoFormatTypeEnum::Enum formatType) +{ + m_formatType = formatType; +} + +/** + * @return Name of movie file + */ +AString +MovieRecorder::getMovieFileName() const +{ + if (m_movieFileName.isEmpty()) { + m_movieFileName = "Movie.mp4"; + } + + FileInformation fileInfo(m_movieFileName); + AString path, name, extension; + fileInfo.getFileComponents(path, + name, + extension); + + extension = MovieRecorderVideoFormatTypeEnum::toFileNameExtensionNoDot(getVideoFormatType()); + + m_movieFileName = FileInformation::assembleFileComponents(path, + name, + extension); + + return m_movieFileName; +} + +/** + * Set name of movie file + * + * @param filename + * New name for movie file + */ +void +MovieRecorder::setMovieFileName(const AString& filename) +{ + m_movieFileName = filename; +} + +/** + * Get a description of this object's content. + * @return String describing this object's content. + */ +AString +MovieRecorder::toString() const +{ + return "MovieRecorder"; +} + +/** + * @return The frame rate (number of frames per second) + */ +float +MovieRecorder::getFramesRate() const +{ + return m_frameRate; +} + +/** + * Set the frame rate (number of frames per second) + * + * @param frameRate + * New frame rate + */ +void +MovieRecorder::setFramesRate(const float frameRate) +{ + m_frameRate = frameRate; +} + +/** + * @return Duration of manual record duration in seconds + */ +float +MovieRecorder::getManualRecordingDurationSeconds() const +{ + return m_manualRecordingDurationSeconds; +} + +/** + * Set the duration of manual recording + * + * @param seconds + * The duration + */ +void +MovieRecorder::setManualRecordingDurationSeconds(const float seconds) +{ + m_manualRecordingDurationSeconds = seconds; +} + +/** + * @return Is request for manual recording of an image when graphics updated set? + * After image is recorded, manual recording request is reset to false + */ +bool +MovieRecorder::isManualRecordingOfImageRequested() const +{ + return m_manualRecordingOfImageRequested; +} + +/** + * Set request for manual recording of an image when graphics updated + * + * @param requestFlag + * New value for request + */ +void +MovieRecorder::setManualRecordingOfImageRequested(const bool requestFlag) +{ + m_manualRecordingOfImageRequested = requestFlag; +} + +/** + * @return Number of frames (images) that have been recorded + */ +int32_t +MovieRecorder::getNumberOfFrames() const +{ + return m_imageFileNames.size(); +} + +/** + * Create the movie using images captured thus far + * + * @param errorMessageOut + * Contains information if movie creation failed + * @return + * True if successful, else false + */ +bool +MovieRecorder::createMovie(AString& errorMessageOut) +{ + errorMessageOut.clear(); + + FileInformation fileInfo(m_movieFileName); + if (fileInfo.exists()) { + errorMessageOut = ("Movie file exists, delete or change name: " + + m_movieFileName); + return false; + } + + if (m_imageFileNames.empty()) { + errorMessageOut.appendWithNewLine("No images have been recorded for the movie."); + } + if (m_movieFileName.isEmpty()) { + errorMessageOut.appendWithNewLine("Movie file name is empty."); + } + + if ( ! errorMessageOut.isEmpty()) { + return false; + } + + const AString sequenceDigitsPattern("%0" + + AString::number(m_tempImageSequenceNumberOfDigits) + + "d"); + + const AString imagesRegularExpressionMatch(m_temporaryImagesDirectory + + "/" + + m_tempImageFileNamePrefix + + sequenceDigitsPattern + + m_tempImageFileNameSuffix); + QString workbenchHomeDir = SystemUtilities::getWorkbenchHome(); + + const QString ffmpegDir = qEnvironmentVariable("WORKBENCH_FFMPEG_DIR"); + if ( ! ffmpegDir.isEmpty()) { + workbenchHomeDir = ffmpegDir; + } + + const bool qProcessPipeFlag(false); + const QString textFileName(m_temporaryImagesDirectory + + "/" + + "images.txt"); + + const QString programName(workbenchHomeDir + + "/ffmpeg"); + FileInformation ffmpegInfo(programName); + if ( ! ffmpegInfo.exists()) { + errorMessageOut = ("Invalid path for ffmpeg: " + + programName + + "\n WORKBENCH_FFMPEG_DIR can be set to directory containing ffmpeg."); + return false; + } + + QStringList arguments; + arguments.append("-threads"); + arguments.append("4"); + arguments.append("-framerate"); + arguments.append(AString::number(m_frameRate)); + if (qProcessPipeFlag) { + /* list of images in file */ + arguments.append("-f"); + arguments.append("concat"); +// arguments.append("-safe"); +// arguments.append("0"); + arguments.append("-i"); + arguments.append(textFileName); + } + else { + arguments.append("-i"); + arguments.append(imagesRegularExpressionMatch); + } + arguments.append("-q:v"); + arguments.append("1"); + arguments.append(m_movieFileName); + + bool successFlag(false); + if (qProcessPipeFlag) { + successFlag = createMovieWithQProcessPipe(programName, + arguments, + textFileName, + errorMessageOut); + } + else { + const bool useQProcessFlag(true); + if (useQProcessFlag) { + successFlag = createMovieWithQProcess(programName, + arguments, + errorMessageOut); + } + else { + successFlag = createMovieWithSystemCommand(programName, + arguments, + errorMessageOut); + } + } + + return successFlag; +} + +/** + * Create the movie by using Qt's QProcess and using a + * pipe to send the images to ffmpeg + * + * @param programName + * Name of program + * @param arguments + * Arguments to program + * @param errorMessageOut + * Output containing error message + * @return + * True if movie was created or false if there was an error + */ +bool +MovieRecorder::createMovieWithQProcessPipe(const QString& programName, + const QStringList& arguments, + const QString& textFileName, + QString& errorMessageOut) +{ + /* + * https://trac.ffmpeg.org/wiki/Concatenate + * https://trac.ffmpeg.org/wiki/Slideshow + */ + bool successFlag(false); + + TextFile textFile; + try { + for (const auto name : m_imageFileNames) { + textFile.addLine("file " + + name); + } + textFile.writeFile(textFileName); + } + catch (const DataFileException& dfe) { + errorMessageOut = ("Error creating text file containing image names: " + + dfe.whatString()); + return false; + } + + QProcess process; + process.start(programName, + arguments); + process.closeWriteChannel(); + + const int noTimeout(-1); + const bool finishedFlag = process.waitForFinished(noTimeout); + if (finishedFlag) { + if (process.exitStatus() == QProcess::NormalExit) { + const int resultCode = process.exitCode(); + if (resultCode == 0) { + successFlag = true; + } + else { + QByteArray results = process.readAllStandardError(); + errorMessageOut = QString(results); + } + } + else if (process.exitStatus() == QProcess::CrashExit) { + errorMessageOut = "Running ffmpeg crashed"; + } + } + else { + errorMessageOut = "Creating movie was terminated for unknown reason"; + } + + return successFlag; +} + +/** + * Create the movie by using Qt's QProcess + * + * @param programName + * Name of program + * @param arguments + * Arguments to program + * @param errorMessageOut + * Output containing error message + * @return + * True if movie was created or false if there was an error + */ +bool +MovieRecorder::createMovieWithQProcess(const QString& programName, + const QStringList& arguments, + QString& errorMessageOut) +{ + bool successFlag(false); + + QProcess process; + process.start(programName, + arguments); + process.closeWriteChannel(); + + const int noTimeout(-1); + const bool finishedFlag = process.waitForFinished(noTimeout); + if (finishedFlag) { + if (process.exitStatus() == QProcess::NormalExit) { + const int resultCode = process.exitCode(); + if (resultCode == 0) { + successFlag = true; + } + else { + QByteArray results = process.readAllStandardError(); + errorMessageOut = QString(results); + } + } + else if (process.exitStatus() == QProcess::CrashExit) { + errorMessageOut = "Running ffmpeg crashed"; + } + } + else { + errorMessageOut = "Creating movie was terminated for unknown reason"; + } + + return successFlag; +} + +/** + * Create the movie by using the system command + * + * @param programName + * Name of program + * @param arguments + * Arguments to program + * @param errorMessageOut + * Output containing error message + * @return + * True if movie was created or false if there was an error + */ +bool +MovieRecorder::createMovieWithSystemCommand(const QString& programName, + const QStringList& arguments, + QString& errorMessageOut) +{ + const AString commandString(programName + + " " + + arguments.join(" ")); + //std::cout << "Command: " << commandString << std::endl; + const int result = system(commandString.toLatin1().constData()); + + bool successFlag(false); + if (result == 0) { + successFlag = true; + } + else { + errorMessageOut = ("Running ffmpeg failed with code=" + + AString::number(result)); + } + return successFlag; +} + diff --git a/src/Brain/MovieRecorder.h b/src/Brain/MovieRecorder.h new file mode 100644 index 000000000..e00a47368 --- /dev/null +++ b/src/Brain/MovieRecorder.h @@ -0,0 +1,159 @@ +#ifndef __MOVIE_RECORDER_H__ +#define __MOVIE_RECORDER_H__ + +/*LICENSE_START*/ +/* + * Copyright (C) 2019 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + + + +#include + +#include "CaretObject.h" +#include "MovieRecorderModeEnum.h" +#include "MovieRecorderVideoDimensionsTypeEnum.h" +#include "MovieRecorderVideoFormatTypeEnum.h" + +class QImage; +class QStringList; + +namespace caret { + + class MovieRecorder : public CaretObject { + + public: + + MovieRecorder(); + + virtual ~MovieRecorder(); + + MovieRecorder(const MovieRecorder&) = delete; + + MovieRecorder& operator=(const MovieRecorder&) = delete; + + MovieRecorderModeEnum::Enum getRecordingMode() const; + + void addImageToMovie(const QImage* image); + + void addImageToMovieWithManualDuration(const QImage* image); + + void setRecordingMode(const MovieRecorderModeEnum::Enum recordingMode); + + int32_t getRecordingWindowIndex() const; + + void setRecordingWindowIndex(const int32_t windowIndex); + + MovieRecorderVideoDimensionsTypeEnum::Enum getVideoDimensionsType() const; + + void setVideoDimensionsType(const MovieRecorderVideoDimensionsTypeEnum::Enum dimensionsType); + + void getVideoDimensions(int32_t& widthOut, + int32_t& heightOut) const; + + void getCustomDimensions(int32_t& widthOut, + int32_t& heightOut) const; + + void setCustomDimensions(const int32_t width, + const int32_t height); + + MovieRecorderVideoFormatTypeEnum::Enum getVideoFormatType() const; + + void setVideoFormatType(const MovieRecorderVideoFormatTypeEnum::Enum formatType); + + AString getMovieFileName() const; + + void setMovieFileName(const AString& filename); + + int32_t getNumberOfFrames() const; + + float getFramesRate() const; + + void setFramesRate(const float frameRate); + + float getManualRecordingDurationSeconds() const; + + void setManualRecordingDurationSeconds(const float seconds); + + bool isManualRecordingOfImageRequested() const; + + void setManualRecordingOfImageRequested(const bool requestFlag); + + void reset(); + + bool createMovie(AString& errorMessageOut); + + bool createMovieWithSystemCommand(const QString& programName, + const QStringList& arguments, + QString& errorMessageOut); + + bool createMovieWithQProcess(const QString& programName, + const QStringList& arguments, + QString& errorMessageOut); + + bool createMovieWithQProcessPipe(const QString& programName, + const QStringList& arguments, + const QString& textFileName, + QString& errorMessageOut); + + // ADD_NEW_METHODS_HERE + + virtual AString toString() const; + + private: + // ADD_NEW_MEMBERS_HERE + + MovieRecorderModeEnum::Enum m_recordingMode = MovieRecorderModeEnum::MANUAL; + + MovieRecorderVideoDimensionsTypeEnum::Enum m_dimensionsType = MovieRecorderVideoDimensionsTypeEnum::SD_640_480; + + MovieRecorderVideoFormatTypeEnum::Enum m_formatType = MovieRecorderVideoFormatTypeEnum::MPEG_4; + + int32_t m_windowIndex = 0; + + int32_t m_customWidth = 640; + + int32_t m_customHeight = 480; + + std::vector m_imageFileNames; + + mutable AString m_movieFileName; + + std::vector m_imageFrameFileNames; + + float m_frameRate = 20.0f; + + float m_manualRecordingDurationSeconds = 5.0f; + + bool m_manualRecordingOfImageRequested = false; + + AString m_temporaryImagesDirectory; + + AString m_tempImageFileNamePrefix; + + AString m_tempImageFileNameSuffix; + + const int32_t m_tempImageSequenceNumberOfDigits = 6; + + }; + +#ifdef __MOVIE_RECORDER_DECLARE__ +#endif // __MOVIE_RECORDER_DECLARE__ + +} // namespace +#endif //__MOVIE_RECORDER_H__ diff --git a/src/Brain/MovieRecorderModeEnum.cxx b/src/Brain/MovieRecorderModeEnum.cxx new file mode 100644 index 000000000..9e5410f97 --- /dev/null +++ b/src/Brain/MovieRecorderModeEnum.cxx @@ -0,0 +1,372 @@ + +/*LICENSE_START*/ +/* + * Copyright (C) 2019 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + +#include +#define __MOVIE_RECORDER_MODE_ENUM_DECLARE__ +#include "MovieRecorderModeEnum.h" +#undef __MOVIE_RECORDER_MODE_ENUM_DECLARE__ + +#include "CaretAssert.h" + +using namespace caret; + + +/** + * \class caret::MovieRecorderModeEnum + * \brief Mode for video recording + * + * Using this enumerated type in the GUI with an EnumComboBoxTemplate + * + * Header File (.h) + * Forward declare the data type: + * class EnumComboBoxTemplate; + * + * Declare the member: + * EnumComboBoxTemplate* m_movieRecorderModeEnumComboBox; + * + * Declare a slot that is called when user changes selection + * private slots: + * void movieRecorderModeEnumComboBoxItemActivated(); + * + * Implementation File (.cxx) + * Include the header files + * #include "EnumComboBoxTemplate.h" + * #include "MovieRecorderModeEnum.h" + * + * Instatiate: + * m_movieRecorderModeEnumComboBox = new EnumComboBoxTemplate(this); + * m_movieRecorderModeEnumComboBox->setup(); + * + * Get notified when the user changes the selection: + * QObject::connect(m_movieRecorderModeEnumComboBox, SIGNAL(itemActivated()), + * this, SLOT(movieRecorderModeEnumComboBoxItemActivated())); + * + * Update the selection: + * m_movieRecorderModeEnumComboBox->setSelectedItem(NEW_VALUE); + * + * Read the selection: + * const MovieRecorderModeEnum::Enum VARIABLE = m_movieRecorderModeEnumComboBox->getSelectedItem(); + * + */ + +/** + * Constructor. + * + * @param enumValue + * An enumerated value. + * @param name + * Name of enumerated value. + * + * @param guiName + * User-friendly name for use in user-interface. + */ +MovieRecorderModeEnum::MovieRecorderModeEnum(const Enum enumValue, + const AString& name, + const AString& guiName) +{ + this->enumValue = enumValue; + this->integerCode = integerCodeCounter++; + this->name = name; + this->guiName = guiName; +} + +/** + * Destructor. + */ +MovieRecorderModeEnum::~MovieRecorderModeEnum() +{ +} + +/** + * Initialize the enumerated metadata. + */ +void +MovieRecorderModeEnum::initialize() +{ + if (initializedFlag) { + return; + } + initializedFlag = true; + + enumData.push_back(MovieRecorderModeEnum(AUTOMATIC, + "AUTOMATIC", + "Automatic")); + + enumData.push_back(MovieRecorderModeEnum(MANUAL, + "MANUAL", + "Manual")); +} + +/** + * Find the data for and enumerated value. + * @param enumValue + * The enumerated value. + * @return Pointer to data for this enumerated type + * or NULL if no data for type or if type is invalid. + */ +const MovieRecorderModeEnum* +MovieRecorderModeEnum::findData(const Enum enumValue) +{ + if (initializedFlag == false) initialize(); + + size_t num = enumData.size(); + for (size_t i = 0; i < num; i++) { + const MovieRecorderModeEnum* d = &enumData[i]; + if (d->enumValue == enumValue) { + return d; + } + } + + return NULL; +} + +/** + * Get a string representation of the enumerated type. + * @param enumValue + * Enumerated value. + * @return + * String representing enumerated value. + */ +AString +MovieRecorderModeEnum::toName(Enum enumValue) { + if (initializedFlag == false) initialize(); + + const MovieRecorderModeEnum* enumInstance = findData(enumValue); + return enumInstance->name; +} + +/** + * Get an enumerated value corresponding to its name. + * @param name + * Name of enumerated value. + * @param isValidOut + * If not NULL, it is set indicating that a + * enum value exists for the input name. + * @return + * Enumerated value. + */ +MovieRecorderModeEnum::Enum +MovieRecorderModeEnum::fromName(const AString& name, bool* isValidOut) +{ + if (initializedFlag == false) initialize(); + + bool validFlag = false; + Enum enumValue = MovieRecorderModeEnum::MANUAL; + + for (std::vector::iterator iter = enumData.begin(); + iter != enumData.end(); + iter++) { + const MovieRecorderModeEnum& d = *iter; + if (d.name == name) { + enumValue = d.enumValue; + validFlag = true; + break; + } + } + + if (isValidOut != 0) { + *isValidOut = validFlag; + } + else if (validFlag == false) { + CaretAssertMessage(0, AString("Name " + name + "failed to match enumerated value for type MovieRecorderModeEnum")); + } + return enumValue; +} + +/** + * Get a GUI string representation of the enumerated type. + * @param enumValue + * Enumerated value. + * @return + * String representing enumerated value. + */ +AString +MovieRecorderModeEnum::toGuiName(Enum enumValue) { + if (initializedFlag == false) initialize(); + + const MovieRecorderModeEnum* enumInstance = findData(enumValue); + return enumInstance->guiName; +} + +/** + * Get an enumerated value corresponding to its GUI name. + * @param s + * Name of enumerated value. + * @param isValidOut + * If not NULL, it is set indicating that a + * enum value exists for the input name. + * @return + * Enumerated value. + */ +MovieRecorderModeEnum::Enum +MovieRecorderModeEnum::fromGuiName(const AString& guiName, bool* isValidOut) +{ + if (initializedFlag == false) initialize(); + + bool validFlag = false; + Enum enumValue = MovieRecorderModeEnum::enumData[0].enumValue; + + for (std::vector::iterator iter = enumData.begin(); + iter != enumData.end(); + iter++) { + const MovieRecorderModeEnum& d = *iter; + if (d.guiName == guiName) { + enumValue = d.enumValue; + validFlag = true; + break; + } + } + + if (isValidOut != 0) { + *isValidOut = validFlag; + } + else if (validFlag == false) { + CaretAssertMessage(0, AString("guiName " + guiName + "failed to match enumerated value for type MovieRecorderModeEnum")); + } + return enumValue; +} + +/** + * Get the integer code for a data type. + * + * @return + * Integer code for data type. + */ +int32_t +MovieRecorderModeEnum::toIntegerCode(Enum enumValue) +{ + if (initializedFlag == false) initialize(); + const MovieRecorderModeEnum* enumInstance = findData(enumValue); + return enumInstance->integerCode; +} + +/** + * Find the data type corresponding to an integer code. + * + * @param integerCode + * Integer code for enum. + * @param isValidOut + * If not NULL, on exit isValidOut will indicate if + * integer code is valid. + * @return + * Enum for integer code. + */ +MovieRecorderModeEnum::Enum +MovieRecorderModeEnum::fromIntegerCode(const int32_t integerCode, bool* isValidOut) +{ + if (initializedFlag == false) initialize(); + + bool validFlag = false; + Enum enumValue = MovieRecorderModeEnum::enumData[0].enumValue; + + for (std::vector::iterator iter = enumData.begin(); + iter != enumData.end(); + iter++) { + const MovieRecorderModeEnum& enumInstance = *iter; + if (enumInstance.integerCode == integerCode) { + enumValue = enumInstance.enumValue; + validFlag = true; + break; + } + } + + if (isValidOut != 0) { + *isValidOut = validFlag; + } + else if (validFlag == false) { + CaretAssertMessage(0, AString("Integer code " + AString::number(integerCode) + "failed to match enumerated value for type MovieRecorderModeEnum")); + } + return enumValue; +} + +/** + * Get all of the enumerated type values. The values can be used + * as parameters to toXXX() methods to get associated metadata. + * + * @param allEnums + * A vector that is OUTPUT containing all of the enumerated values. + */ +void +MovieRecorderModeEnum::getAllEnums(std::vector& allEnums) +{ + if (initializedFlag == false) initialize(); + + allEnums.clear(); + + for (std::vector::iterator iter = enumData.begin(); + iter != enumData.end(); + iter++) { + allEnums.push_back(iter->enumValue); + } +} + +/** + * Get all of the names of the enumerated type values. + * + * @param allNames + * A vector that is OUTPUT containing all of the names of the enumerated values. + * @param isSorted + * If true, the names are sorted in alphabetical order. + */ +void +MovieRecorderModeEnum::getAllNames(std::vector& allNames, const bool isSorted) +{ + if (initializedFlag == false) initialize(); + + allNames.clear(); + + for (std::vector::iterator iter = enumData.begin(); + iter != enumData.end(); + iter++) { + allNames.push_back(MovieRecorderModeEnum::toName(iter->enumValue)); + } + + if (isSorted) { + std::sort(allNames.begin(), allNames.end()); + } +} + +/** + * Get all of the GUI names of the enumerated type values. + * + * @param allNames + * A vector that is OUTPUT containing all of the GUI names of the enumerated values. + * @param isSorted + * If true, the names are sorted in alphabetical order. + */ +void +MovieRecorderModeEnum::getAllGuiNames(std::vector& allGuiNames, const bool isSorted) +{ + if (initializedFlag == false) initialize(); + + allGuiNames.clear(); + + for (std::vector::iterator iter = enumData.begin(); + iter != enumData.end(); + iter++) { + allGuiNames.push_back(MovieRecorderModeEnum::toGuiName(iter->enumValue)); + } + + if (isSorted) { + std::sort(allGuiNames.begin(), allGuiNames.end()); + } +} + diff --git a/src/Brain/MovieRecorderModeEnum.h b/src/Brain/MovieRecorderModeEnum.h new file mode 100644 index 000000000..d36302511 --- /dev/null +++ b/src/Brain/MovieRecorderModeEnum.h @@ -0,0 +1,104 @@ +#ifndef __MOVIE_RECORDER_MODE_ENUM_H__ +#define __MOVIE_RECORDER_MODE_ENUM_H__ + +/*LICENSE_START*/ +/* + * Copyright (C) 2019 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + + +#include +#include +#include "AString.h" + +namespace caret { + +class MovieRecorderModeEnum { + +public: + /** + * Enumerated values. + */ + enum Enum { + /** Automatic*/ + AUTOMATIC, + /** Manual */ + MANUAL + }; + + + ~MovieRecorderModeEnum(); + + static AString toName(Enum enumValue); + + static Enum fromName(const AString& name, bool* isValidOut); + + static AString toGuiName(Enum enumValue); + + static Enum fromGuiName(const AString& guiName, bool* isValidOut); + + static int32_t toIntegerCode(Enum enumValue); + + static Enum fromIntegerCode(const int32_t integerCode, bool* isValidOut); + + static void getAllEnums(std::vector& allEnums); + + static void getAllNames(std::vector& allNames, const bool isSorted); + + static void getAllGuiNames(std::vector& allGuiNames, const bool isSorted); + +private: + MovieRecorderModeEnum(const Enum enumValue, + const AString& name, + const AString& guiName); + + static const MovieRecorderModeEnum* findData(const Enum enumValue); + + /** Holds all instance of enum values and associated metadata */ + static std::vector enumData; + + /** Initialize instances that contain the enum values and metadata */ + static void initialize(); + + /** Indicates instance of enum values and metadata have been initialized */ + static bool initializedFlag; + + /** Auto generated integer codes */ + static int32_t integerCodeCounter; + + /** The enumerated type value for an instance */ + Enum enumValue; + + /** The integer code associated with an enumerated value */ + int32_t integerCode; + + /** The name, a text string that is identical to the enumerated value */ + AString name; + + /** A user-friendly name that is displayed in the GUI */ + AString guiName; +}; + +#ifdef __MOVIE_RECORDER_MODE_ENUM_DECLARE__ +std::vector MovieRecorderModeEnum::enumData; +bool MovieRecorderModeEnum::initializedFlag = false; +int32_t MovieRecorderModeEnum::integerCodeCounter = 0; +#endif // __MOVIE_RECORDER_MODE_ENUM_DECLARE__ + +} // namespace +#endif //__MOVIE_RECORDER_MODE_ENUM_H__ diff --git a/src/Brain/MovieRecorderVideoDimensionsTypeEnum.cxx b/src/Brain/MovieRecorderVideoDimensionsTypeEnum.cxx new file mode 100644 index 000000000..6dde1ff6c --- /dev/null +++ b/src/Brain/MovieRecorderVideoDimensionsTypeEnum.cxx @@ -0,0 +1,377 @@ + +/*LICENSE_START*/ +/* + * Copyright (C) 2019 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + +#include +#define __MOVIE_RECORDER_VIDEO_DIMENSIONS_TYPE_ENUM_DECLARE__ +#include "MovieRecorderVideoDimensionsTypeEnum.h" +#undef __MOVIE_RECORDER_VIDEO_DIMENSIONS_TYPE_ENUM_DECLARE__ + +#include "CaretAssert.h" + +using namespace caret; + + +/** + * \class caret::MovieRecorderVideoDimensionsTypeEnum + * \brief Dimensions for movie recording + * + * Using this enumerated type in the GUI with an EnumComboBoxTemplate + * + * Header File (.h) + * Forward declare the data type: + * class EnumComboBoxTemplate; + * + * Declare the member: + * EnumComboBoxTemplate* m_movieRecorderVideoDimensionsTypeEnumComboBox; + * + * Declare a slot that is called when user changes selection + * private slots: + * void movieRecorderVideoDimensionsTypeEnumComboBoxItemActivated(); + * + * Implementation File (.cxx) + * Include the header files + * #include "EnumComboBoxTemplate.h" + * #include "MovieRecorderVideoDimensionsTypeEnum.h" + * + * Instatiate: + * m_movieRecorderVideoDimensionsTypeEnumComboBox = new EnumComboBoxTemplate(this); + * m_movieRecorderVideoDimensionsTypeEnumComboBox->setup(); + * + * Get notified when the user changes the selection: + * QObject::connect(m_movieRecorderVideoDimensionsTypeEnumComboBox, SIGNAL(itemActivated()), + * this, SLOT(movieRecorderVideoDimensionsTypeEnumComboBoxItemActivated())); + * + * Update the selection: + * m_movieRecorderVideoDimensionsTypeEnumComboBox->setSelectedItem(NEW_VALUE); + * + * Read the selection: + * const MovieRecorderVideoDimensionsTypeEnum::Enum VARIABLE = m_movieRecorderVideoDimensionsTypeEnumComboBox->getSelectedItem(); + * + */ + +/** + * Constructor. + * + * @param enumValue + * An enumerated value. + * @param name + * Name of enumerated value. + * + * @param guiName + * User-friendly name for use in user-interface. + */ +MovieRecorderVideoDimensionsTypeEnum::MovieRecorderVideoDimensionsTypeEnum(const Enum enumValue, + const AString& name, + const AString& guiName) +{ + this->enumValue = enumValue; + this->integerCode = integerCodeCounter++; + this->name = name; + this->guiName = guiName; +} + +/** + * Destructor. + */ +MovieRecorderVideoDimensionsTypeEnum::~MovieRecorderVideoDimensionsTypeEnum() +{ +} + +/** + * Initialize the enumerated metadata. + */ +void +MovieRecorderVideoDimensionsTypeEnum::initialize() +{ + if (initializedFlag) { + return; + } + initializedFlag = true; + + enumData.push_back(MovieRecorderVideoDimensionsTypeEnum(CUSTOM, + "CUSTOM", + "Custom")); + + enumData.push_back(MovieRecorderVideoDimensionsTypeEnum(HD_1280_720, + "HD_1280_720", + "HD (1280x720)")); + + enumData.push_back(MovieRecorderVideoDimensionsTypeEnum(SD_640_480, + "SD_640_480", + "SD (640x480)")); + +} + +/** + * Find the data for and enumerated value. + * @param enumValue + * The enumerated value. + * @return Pointer to data for this enumerated type + * or NULL if no data for type or if type is invalid. + */ +const MovieRecorderVideoDimensionsTypeEnum* +MovieRecorderVideoDimensionsTypeEnum::findData(const Enum enumValue) +{ + if (initializedFlag == false) initialize(); + + size_t num = enumData.size(); + for (size_t i = 0; i < num; i++) { + const MovieRecorderVideoDimensionsTypeEnum* d = &enumData[i]; + if (d->enumValue == enumValue) { + return d; + } + } + + return NULL; +} + +/** + * Get a string representation of the enumerated type. + * @param enumValue + * Enumerated value. + * @return + * String representing enumerated value. + */ +AString +MovieRecorderVideoDimensionsTypeEnum::toName(Enum enumValue) { + if (initializedFlag == false) initialize(); + + const MovieRecorderVideoDimensionsTypeEnum* enumInstance = findData(enumValue); + return enumInstance->name; +} + +/** + * Get an enumerated value corresponding to its name. + * @param name + * Name of enumerated value. + * @param isValidOut + * If not NULL, it is set indicating that a + * enum value exists for the input name. + * @return + * Enumerated value. + */ +MovieRecorderVideoDimensionsTypeEnum::Enum +MovieRecorderVideoDimensionsTypeEnum::fromName(const AString& name, bool* isValidOut) +{ + if (initializedFlag == false) initialize(); + + bool validFlag = false; + Enum enumValue = MovieRecorderVideoDimensionsTypeEnum::enumData[0].enumValue; + + for (std::vector::iterator iter = enumData.begin(); + iter != enumData.end(); + iter++) { + const MovieRecorderVideoDimensionsTypeEnum& d = *iter; + if (d.name == name) { + enumValue = d.enumValue; + validFlag = true; + break; + } + } + + if (isValidOut != 0) { + *isValidOut = validFlag; + } + else if (validFlag == false) { + CaretAssertMessage(0, AString("Name " + name + "failed to match enumerated value for type MovieRecorderVideoDimensionsTypeEnum")); + } + return enumValue; +} + +/** + * Get a GUI string representation of the enumerated type. + * @param enumValue + * Enumerated value. + * @return + * String representing enumerated value. + */ +AString +MovieRecorderVideoDimensionsTypeEnum::toGuiName(Enum enumValue) { + if (initializedFlag == false) initialize(); + + const MovieRecorderVideoDimensionsTypeEnum* enumInstance = findData(enumValue); + return enumInstance->guiName; +} + +/** + * Get an enumerated value corresponding to its GUI name. + * @param s + * Name of enumerated value. + * @param isValidOut + * If not NULL, it is set indicating that a + * enum value exists for the input name. + * @return + * Enumerated value. + */ +MovieRecorderVideoDimensionsTypeEnum::Enum +MovieRecorderVideoDimensionsTypeEnum::fromGuiName(const AString& guiName, bool* isValidOut) +{ + if (initializedFlag == false) initialize(); + + bool validFlag = false; + Enum enumValue = MovieRecorderVideoDimensionsTypeEnum::enumData[0].enumValue; + + for (std::vector::iterator iter = enumData.begin(); + iter != enumData.end(); + iter++) { + const MovieRecorderVideoDimensionsTypeEnum& d = *iter; + if (d.guiName == guiName) { + enumValue = d.enumValue; + validFlag = true; + break; + } + } + + if (isValidOut != 0) { + *isValidOut = validFlag; + } + else if (validFlag == false) { + CaretAssertMessage(0, AString("guiName " + guiName + "failed to match enumerated value for type MovieRecorderVideoDimensionsTypeEnum")); + } + return enumValue; +} + +/** + * Get the integer code for a data type. + * + * @return + * Integer code for data type. + */ +int32_t +MovieRecorderVideoDimensionsTypeEnum::toIntegerCode(Enum enumValue) +{ + if (initializedFlag == false) initialize(); + const MovieRecorderVideoDimensionsTypeEnum* enumInstance = findData(enumValue); + return enumInstance->integerCode; +} + +/** + * Find the data type corresponding to an integer code. + * + * @param integerCode + * Integer code for enum. + * @param isValidOut + * If not NULL, on exit isValidOut will indicate if + * integer code is valid. + * @return + * Enum for integer code. + */ +MovieRecorderVideoDimensionsTypeEnum::Enum +MovieRecorderVideoDimensionsTypeEnum::fromIntegerCode(const int32_t integerCode, bool* isValidOut) +{ + if (initializedFlag == false) initialize(); + + bool validFlag = false; + Enum enumValue = MovieRecorderVideoDimensionsTypeEnum::enumData[0].enumValue; + + for (std::vector::iterator iter = enumData.begin(); + iter != enumData.end(); + iter++) { + const MovieRecorderVideoDimensionsTypeEnum& enumInstance = *iter; + if (enumInstance.integerCode == integerCode) { + enumValue = enumInstance.enumValue; + validFlag = true; + break; + } + } + + if (isValidOut != 0) { + *isValidOut = validFlag; + } + else if (validFlag == false) { + CaretAssertMessage(0, AString("Integer code " + AString::number(integerCode) + "failed to match enumerated value for type MovieRecorderVideoDimensionsTypeEnum")); + } + return enumValue; +} + +/** + * Get all of the enumerated type values. The values can be used + * as parameters to toXXX() methods to get associated metadata. + * + * @param allEnums + * A vector that is OUTPUT containing all of the enumerated values. + */ +void +MovieRecorderVideoDimensionsTypeEnum::getAllEnums(std::vector& allEnums) +{ + if (initializedFlag == false) initialize(); + + allEnums.clear(); + + for (std::vector::iterator iter = enumData.begin(); + iter != enumData.end(); + iter++) { + allEnums.push_back(iter->enumValue); + } +} + +/** + * Get all of the names of the enumerated type values. + * + * @param allNames + * A vector that is OUTPUT containing all of the names of the enumerated values. + * @param isSorted + * If true, the names are sorted in alphabetical order. + */ +void +MovieRecorderVideoDimensionsTypeEnum::getAllNames(std::vector& allNames, const bool isSorted) +{ + if (initializedFlag == false) initialize(); + + allNames.clear(); + + for (std::vector::iterator iter = enumData.begin(); + iter != enumData.end(); + iter++) { + allNames.push_back(MovieRecorderVideoDimensionsTypeEnum::toName(iter->enumValue)); + } + + if (isSorted) { + std::sort(allNames.begin(), allNames.end()); + } +} + +/** + * Get all of the GUI names of the enumerated type values. + * + * @param allNames + * A vector that is OUTPUT containing all of the GUI names of the enumerated values. + * @param isSorted + * If true, the names are sorted in alphabetical order. + */ +void +MovieRecorderVideoDimensionsTypeEnum::getAllGuiNames(std::vector& allGuiNames, const bool isSorted) +{ + if (initializedFlag == false) initialize(); + + allGuiNames.clear(); + + for (std::vector::iterator iter = enumData.begin(); + iter != enumData.end(); + iter++) { + allGuiNames.push_back(MovieRecorderVideoDimensionsTypeEnum::toGuiName(iter->enumValue)); + } + + if (isSorted) { + std::sort(allGuiNames.begin(), allGuiNames.end()); + } +} + diff --git a/src/Brain/MovieRecorderVideoDimensionsTypeEnum.h b/src/Brain/MovieRecorderVideoDimensionsTypeEnum.h new file mode 100644 index 000000000..3d25cdb5b --- /dev/null +++ b/src/Brain/MovieRecorderVideoDimensionsTypeEnum.h @@ -0,0 +1,106 @@ +#ifndef __MOVIE_RECORDER_VIDEO_DIMENSIONS_TYPE_ENUM_H__ +#define __MOVIE_RECORDER_VIDEO_DIMENSIONS_TYPE_ENUM_H__ + +/*LICENSE_START*/ +/* + * Copyright (C) 2019 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + + +#include +#include +#include "AString.h" + +namespace caret { + +class MovieRecorderVideoDimensionsTypeEnum { + +public: + /** + * Enumerated values. + */ + enum Enum { + /** Custom */ + CUSTOM, + /** HD */ + HD_1280_720, + /** SD */ + SD_640_480 + }; + + + ~MovieRecorderVideoDimensionsTypeEnum(); + + static AString toName(Enum enumValue); + + static Enum fromName(const AString& name, bool* isValidOut); + + static AString toGuiName(Enum enumValue); + + static Enum fromGuiName(const AString& guiName, bool* isValidOut); + + static int32_t toIntegerCode(Enum enumValue); + + static Enum fromIntegerCode(const int32_t integerCode, bool* isValidOut); + + static void getAllEnums(std::vector& allEnums); + + static void getAllNames(std::vector& allNames, const bool isSorted); + + static void getAllGuiNames(std::vector& allGuiNames, const bool isSorted); + +private: + MovieRecorderVideoDimensionsTypeEnum(const Enum enumValue, + const AString& name, + const AString& guiName); + + static const MovieRecorderVideoDimensionsTypeEnum* findData(const Enum enumValue); + + /** Holds all instance of enum values and associated metadata */ + static std::vector enumData; + + /** Initialize instances that contain the enum values and metadata */ + static void initialize(); + + /** Indicates instance of enum values and metadata have been initialized */ + static bool initializedFlag; + + /** Auto generated integer codes */ + static int32_t integerCodeCounter; + + /** The enumerated type value for an instance */ + Enum enumValue; + + /** The integer code associated with an enumerated value */ + int32_t integerCode; + + /** The name, a text string that is identical to the enumerated value */ + AString name; + + /** A user-friendly name that is displayed in the GUI */ + AString guiName; +}; + +#ifdef __MOVIE_RECORDER_VIDEO_DIMENSIONS_TYPE_ENUM_DECLARE__ +std::vector MovieRecorderVideoDimensionsTypeEnum::enumData; +bool MovieRecorderVideoDimensionsTypeEnum::initializedFlag = false; +int32_t MovieRecorderVideoDimensionsTypeEnum::integerCodeCounter = 0; +#endif // __MOVIE_RECORDER_VIDEO_DIMENSIONS_TYPE_ENUM_DECLARE__ + +} // namespace +#endif //__MOVIE_RECORDER_VIDEO_DIMENSIONS_TYPE_ENUM_H__ diff --git a/src/Brain/MovieRecorderVideoFormatTypeEnum.cxx b/src/Brain/MovieRecorderVideoFormatTypeEnum.cxx new file mode 100644 index 000000000..c28e066d4 --- /dev/null +++ b/src/Brain/MovieRecorderVideoFormatTypeEnum.cxx @@ -0,0 +1,404 @@ + +/*LICENSE_START*/ +/* + * Copyright (C) 2019 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + +#include +#define __MOVIE_RECORDER_VIDEO_FORMAT_TYPE_ENUM_DECLARE__ +#include "MovieRecorderVideoFormatTypeEnum.h" +#undef __MOVIE_RECORDER_VIDEO_FORMAT_TYPE_ENUM_DECLARE__ + +#include "CaretAssert.h" + +using namespace caret; + + +/** + * \class caret::MovieRecorderVideoFormatTypeEnum + * \brief Format for video recording + * + * Using this enumerated type in the GUI with an EnumComboBoxTemplate + * + * Header File (.h) + * Forward declare the data type: + * class EnumComboBoxTemplate; + * + * Declare the member: + * EnumComboBoxTemplate* m_movieRecorderVideoFormatTypeEnumComboBox; + * + * Declare a slot that is called when user changes selection + * private slots: + * void movieRecorderVideoFormatTypeEnumComboBoxItemActivated(); + * + * Implementation File (.cxx) + * Include the header files + * #include "EnumComboBoxTemplate.h" + * #include "MovieRecorderVideoFormatTypeEnum.h" + * + * Instatiate: + * m_movieRecorderVideoFormatTypeEnumComboBox = new EnumComboBoxTemplate(this); + * m_movieRecorderVideoFormatTypeEnumComboBox->setup(); + * + * Get notified when the user changes the selection: + * QObject::connect(m_movieRecorderVideoFormatTypeEnumComboBox, SIGNAL(itemActivated()), + * this, SLOT(movieRecorderVideoFormatTypeEnumComboBoxItemActivated())); + * + * Update the selection: + * m_movieRecorderVideoFormatTypeEnumComboBox->setSelectedItem(NEW_VALUE); + * + * Read the selection: + * const MovieRecorderVideoFormatTypeEnum::Enum VARIABLE = m_movieRecorderVideoFormatTypeEnumComboBox->getSelectedItem(); + * + */ + +/** + * Constructor. + * + * @param enumValue + * An enumerated value. + * @param name + * Name of enumerated value. + * @param guiName + * User-friendly name for use in user-interface. + * @param filenameExtensionNoDot + * Extension for file without a dot + * @param fileDialogFilter + * Filter for use in file dialogs + */ +MovieRecorderVideoFormatTypeEnum::MovieRecorderVideoFormatTypeEnum(const Enum enumValue, + const AString& name, + const AString& guiName, + const AString& filenameExtensionNoDot, + const AString& fileDialogFilter) +{ + this->enumValue = enumValue; + this->integerCode = integerCodeCounter++; + this->name = name; + this->guiName = guiName; + this->filenameExtensionNoDot = filenameExtensionNoDot; + this->fileDialogFilter = fileDialogFilter; +} + +/** + * Destructor. + */ +MovieRecorderVideoFormatTypeEnum::~MovieRecorderVideoFormatTypeEnum() +{ +} + +/** + * Initialize the enumerated metadata. + */ +void +MovieRecorderVideoFormatTypeEnum::initialize() +{ + if (initializedFlag) { + return; + } + initializedFlag = true; + + enumData.push_back(MovieRecorderVideoFormatTypeEnum(AVI, + "AVI", + "Avi", + "avi", + "AVI (*.avi)")); + + enumData.push_back(MovieRecorderVideoFormatTypeEnum(MPEG_4, + "MPEG_4", + "Mpeg 4", + "mp4", + "MPEG 4 (*.mp4)")); +} + +/** + * Find the data for and enumerated value. + * @param enumValue + * The enumerated value. + * @return Pointer to data for this enumerated type + * or NULL if no data for type or if type is invalid. + */ +const MovieRecorderVideoFormatTypeEnum* +MovieRecorderVideoFormatTypeEnum::findData(const Enum enumValue) +{ + if (initializedFlag == false) initialize(); + + size_t num = enumData.size(); + for (size_t i = 0; i < num; i++) { + const MovieRecorderVideoFormatTypeEnum* d = &enumData[i]; + if (d->enumValue == enumValue) { + return d; + } + } + + return NULL; +} + +/** + * Get a string representation of the enumerated type. + * @param enumValue + * Enumerated value. + * @return + * String representing enumerated value. + */ +AString +MovieRecorderVideoFormatTypeEnum::toName(Enum enumValue) { + if (initializedFlag == false) initialize(); + + const MovieRecorderVideoFormatTypeEnum* enumInstance = findData(enumValue); + return enumInstance->name; +} + +/** + * Get an enumerated value corresponding to its name. + * @param name + * Name of enumerated value. + * @param isValidOut + * If not NULL, it is set indicating that a + * enum value exists for the input name. + * @return + * Enumerated value. + */ +MovieRecorderVideoFormatTypeEnum::Enum +MovieRecorderVideoFormatTypeEnum::fromName(const AString& name, bool* isValidOut) +{ + if (initializedFlag == false) initialize(); + + bool validFlag = false; + Enum enumValue = MovieRecorderVideoFormatTypeEnum::enumData[0].enumValue; + + for (std::vector::iterator iter = enumData.begin(); + iter != enumData.end(); + iter++) { + const MovieRecorderVideoFormatTypeEnum& d = *iter; + if (d.name == name) { + enumValue = d.enumValue; + validFlag = true; + break; + } + } + + if (isValidOut != 0) { + *isValidOut = validFlag; + } + else if (validFlag == false) { + CaretAssertMessage(0, AString("Name " + name + "failed to match enumerated value for type MovieRecorderVideoFormatTypeEnum")); + } + return enumValue; +} + +/** + * Get a GUI string representation of the enumerated type. + * @param enumValue + * Enumerated value. + * @return + * String representing enumerated value. + */ +AString +MovieRecorderVideoFormatTypeEnum::toGuiName(Enum enumValue) { + if (initializedFlag == false) initialize(); + + const MovieRecorderVideoFormatTypeEnum* enumInstance = findData(enumValue); + return enumInstance->guiName; +} + +/** + * Get an enumerated value corresponding to its GUI name. + * @param s + * Name of enumerated value. + * @param isValidOut + * If not NULL, it is set indicating that a + * enum value exists for the input name. + * @return + * Enumerated value. + */ +MovieRecorderVideoFormatTypeEnum::Enum +MovieRecorderVideoFormatTypeEnum::fromGuiName(const AString& guiName, bool* isValidOut) +{ + if (initializedFlag == false) initialize(); + + bool validFlag = false; + Enum enumValue = MovieRecorderVideoFormatTypeEnum::enumData[0].enumValue; + + for (std::vector::iterator iter = enumData.begin(); + iter != enumData.end(); + iter++) { + const MovieRecorderVideoFormatTypeEnum& d = *iter; + if (d.guiName == guiName) { + enumValue = d.enumValue; + validFlag = true; + break; + } + } + + if (isValidOut != 0) { + *isValidOut = validFlag; + } + else if (validFlag == false) { + CaretAssertMessage(0, AString("guiName " + guiName + "failed to match enumerated value for type MovieRecorderVideoFormatTypeEnum")); + } + return enumValue; +} + +/** + * Get the integer code for a data type. + * + * @return + * Integer code for data type. + */ +int32_t +MovieRecorderVideoFormatTypeEnum::toIntegerCode(Enum enumValue) +{ + if (initializedFlag == false) initialize(); + const MovieRecorderVideoFormatTypeEnum* enumInstance = findData(enumValue); + return enumInstance->integerCode; +} + +/** + * Find the data type corresponding to an integer code. + * + * @param integerCode + * Integer code for enum. + * @param isValidOut + * If not NULL, on exit isValidOut will indicate if + * integer code is valid. + * @return + * Enum for integer code. + */ +MovieRecorderVideoFormatTypeEnum::Enum +MovieRecorderVideoFormatTypeEnum::fromIntegerCode(const int32_t integerCode, bool* isValidOut) +{ + if (initializedFlag == false) initialize(); + + bool validFlag = false; + Enum enumValue = MovieRecorderVideoFormatTypeEnum::enumData[0].enumValue; + + for (std::vector::iterator iter = enumData.begin(); + iter != enumData.end(); + iter++) { + const MovieRecorderVideoFormatTypeEnum& enumInstance = *iter; + if (enumInstance.integerCode == integerCode) { + enumValue = enumInstance.enumValue; + validFlag = true; + break; + } + } + + if (isValidOut != 0) { + *isValidOut = validFlag; + } + else if (validFlag == false) { + CaretAssertMessage(0, AString("Integer code " + AString::number(integerCode) + "failed to match enumerated value for type MovieRecorderVideoFormatTypeEnum")); + } + return enumValue; +} + +/** + * Get all of the enumerated type values. The values can be used + * as parameters to toXXX() methods to get associated metadata. + * + * @param allEnums + * A vector that is OUTPUT containing all of the enumerated values. + */ +void +MovieRecorderVideoFormatTypeEnum::getAllEnums(std::vector& allEnums) +{ + if (initializedFlag == false) initialize(); + + allEnums.clear(); + + for (std::vector::iterator iter = enumData.begin(); + iter != enumData.end(); + iter++) { + allEnums.push_back(iter->enumValue); + } +} + +/** + * Get all of the names of the enumerated type values. + * + * @param allNames + * A vector that is OUTPUT containing all of the names of the enumerated values. + * @param isSorted + * If true, the names are sorted in alphabetical order. + */ +void +MovieRecorderVideoFormatTypeEnum::getAllNames(std::vector& allNames, const bool isSorted) +{ + if (initializedFlag == false) initialize(); + + allNames.clear(); + + for (std::vector::iterator iter = enumData.begin(); + iter != enumData.end(); + iter++) { + allNames.push_back(MovieRecorderVideoFormatTypeEnum::toName(iter->enumValue)); + } + + if (isSorted) { + std::sort(allNames.begin(), allNames.end()); + } +} + +/** + * Get all of the GUI names of the enumerated type values. + * + * @param allNames + * A vector that is OUTPUT containing all of the GUI names of the enumerated values. + * @param isSorted + * If true, the names are sorted in alphabetical order. + */ +void +MovieRecorderVideoFormatTypeEnum::getAllGuiNames(std::vector& allGuiNames, const bool isSorted) +{ + if (initializedFlag == false) initialize(); + + allGuiNames.clear(); + + for (std::vector::iterator iter = enumData.begin(); + iter != enumData.end(); + iter++) { + allGuiNames.push_back(MovieRecorderVideoFormatTypeEnum::toGuiName(iter->enumValue)); + } + + if (isSorted) { + std::sort(allGuiNames.begin(), allGuiNames.end()); + } +} + +/** + * Get the filename extension for the video format + */ +AString +MovieRecorderVideoFormatTypeEnum::toFileNameExtensionNoDot(Enum enumValue) +{ + if (initializedFlag == false) initialize(); + const MovieRecorderVideoFormatTypeEnum* enumInstance = findData(enumValue); + return enumInstance->filenameExtensionNoDot; +} + +/** + * Get the file dialog filter for the video format + */ +AString +MovieRecorderVideoFormatTypeEnum::toFileDialogFilter(Enum enumValue) +{ + if (initializedFlag == false) initialize(); + const MovieRecorderVideoFormatTypeEnum* enumInstance = findData(enumValue); + return enumInstance->filenameExtensionNoDot; +} diff --git a/src/Brain/MovieRecorderVideoFormatTypeEnum.h b/src/Brain/MovieRecorderVideoFormatTypeEnum.h new file mode 100644 index 000000000..57dd608a9 --- /dev/null +++ b/src/Brain/MovieRecorderVideoFormatTypeEnum.h @@ -0,0 +1,114 @@ +#ifndef __MOVIE_RECORDER_VIDEO_FORMAT_TYPE_ENUM_H__ +#define __MOVIE_RECORDER_VIDEO_FORMAT_TYPE_ENUM_H__ + +/*LICENSE_START*/ +/* + * Copyright (C) 2019 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + + +#include +#include +#include "AString.h" + +namespace caret { + +class MovieRecorderVideoFormatTypeEnum { + +public: + /** + * Enumerated values. + */ + enum Enum { + /** AVI video */ + AVI, + /** MPEG-4 video */ + MPEG_4 + }; + + + ~MovieRecorderVideoFormatTypeEnum(); + + static AString toName(Enum enumValue); + + static Enum fromName(const AString& name, bool* isValidOut); + + static AString toGuiName(Enum enumValue); + + static Enum fromGuiName(const AString& guiName, bool* isValidOut); + + static int32_t toIntegerCode(Enum enumValue); + + static Enum fromIntegerCode(const int32_t integerCode, bool* isValidOut); + + static void getAllEnums(std::vector& allEnums); + + static void getAllNames(std::vector& allNames, const bool isSorted); + + static void getAllGuiNames(std::vector& allGuiNames, const bool isSorted); + + static AString toFileNameExtensionNoDot(Enum enumValue); + + static AString toFileDialogFilter(Enum enumValue); + +private: + MovieRecorderVideoFormatTypeEnum(const Enum enumValue, + const AString& name, + const AString& guiName, + const AString& filenameExtensionNoDot, + const AString& fileDialogFilter); + + static const MovieRecorderVideoFormatTypeEnum* findData(const Enum enumValue); + + /** Holds all instance of enum values and associated metadata */ + static std::vector enumData; + + /** Initialize instances that contain the enum values and metadata */ + static void initialize(); + + /** Indicates instance of enum values and metadata have been initialized */ + static bool initializedFlag; + + /** Auto generated integer codes */ + static int32_t integerCodeCounter; + + /** The enumerated type value for an instance */ + Enum enumValue; + + /** The integer code associated with an enumerated value */ + int32_t integerCode; + + /** The name, a text string that is identical to the enumerated value */ + AString name; + + /** A user-friendly name that is displayed in the GUI */ + AString guiName; + + AString filenameExtensionNoDot; + + AString fileDialogFilter; +}; + +#ifdef __MOVIE_RECORDER_VIDEO_FORMAT_TYPE_ENUM_DECLARE__ +std::vector MovieRecorderVideoFormatTypeEnum::enumData; +bool MovieRecorderVideoFormatTypeEnum::initializedFlag = false; +int32_t MovieRecorderVideoFormatTypeEnum::integerCodeCounter = 0; +#endif // __MOVIE_RECORDER_VIDEO_FORMAT_TYPE_ENUM_DECLARE__ + +} // namespace +#endif //__MOVIE_RECORDER_VIDEO_FORMAT_TYPE_ENUM_H__ diff --git a/src/Brain/SessionManager.cxx b/src/Brain/SessionManager.cxx index 78b81eec0..ca9ecd302 100644 --- a/src/Brain/SessionManager.cxx +++ b/src/Brain/SessionManager.cxx @@ -56,6 +56,7 @@ #include "LogManager.h" #include "MapYokingGroupEnum.h" #include "ModelWholeBrain.h" +#include "MovieRecorder.h" #include "Scene.h" #include "SceneAttributes.h" #include "SceneClass.h" @@ -77,6 +78,7 @@ SessionManager::SessionManager() m_caretPreferences = new CaretPreferences(); m_imageCaptureDialogSettings = new ImageCaptureSettings(); + m_movieRecorder.reset(new MovieRecorder()); m_ciftiConnectivityMatrixDataFileManager = new CiftiConnectivityMatrixDataFileManager(); m_ciftiFiberTrajectoryManager = new CiftiFiberTrajectoryManager(); @@ -1105,4 +1107,21 @@ SessionManager::getImageCaptureDialogSettings() const return m_imageCaptureDialogSettings; } +/** + * @return The movie recorder + */ +MovieRecorder* +SessionManager::getMovieRecorder() +{ + return m_movieRecorder.get(); +} + +/** + * @return The movie recorder (const method) + */ +const MovieRecorder* +SessionManager::getMovieRecorder() const +{ + return m_movieRecorder.get(); +} diff --git a/src/Brain/SessionManager.h b/src/Brain/SessionManager.h index 5f031bbef..9043cd9d8 100644 --- a/src/Brain/SessionManager.h +++ b/src/Brain/SessionManager.h @@ -43,6 +43,7 @@ namespace caret { class DataToolTipsManager; class ImageCaptureSettings; class Model; + class MovieRecorder; class SpacerTabContent; /// Manages a Caret session which contains 'global' brain data. @@ -81,6 +82,10 @@ namespace caret { const ImageCaptureSettings* getImageCaptureDialogSettings() const; + MovieRecorder* getMovieRecorder(); + + const MovieRecorder* getMovieRecorder() const; + virtual SceneClass* saveToScene(const SceneAttributes* sceneAttributes, const AString& instanceName); @@ -138,6 +143,7 @@ namespace caret { /** Map to spacer tabs where key is window index, row index, column index */ std::map m_spacerTabsMap; + std::unique_ptr m_movieRecorder; }; #ifdef __SESSION_MANAGER_DECLARE__ diff --git a/src/Common/EventManager.cxx b/src/Common/EventManager.cxx index 098156ccf..f210d473c 100644 --- a/src/Common/EventManager.cxx +++ b/src/Common/EventManager.cxx @@ -369,6 +369,7 @@ EventManager::sendSimpleEvent(const EventTypeEnum::Enum eventType) switch (eventType) { case EventTypeEnum::EVENT_ANNOTATION_TOOLBAR_UPDATE: case EventTypeEnum::EVENT_BROWSER_WINDOW_MENUS_UPDATE: + case EventTypeEnum::EVENT_MOVIE_RECORDING_DIALOG_UPDATE: { sendEvent(Event(eventType).getPointer()); } diff --git a/src/Common/EventTypeEnum.cxx b/src/Common/EventTypeEnum.cxx index d5fff5a1d..bde70d24d 100644 --- a/src/Common/EventTypeEnum.cxx +++ b/src/Common/EventTypeEnum.cxx @@ -322,6 +322,10 @@ EventTypeEnum::initialize() "EVENT_MODEL_SURFACE_GET", "Get a specific model surface")); + enumData.push_back(EventTypeEnum(EVENT_MOVIE_RECORDING_DIALOG_UPDATE, + "EVENT_MOVIE_RECORDING_DIALOG_UPDATE", + "Update the movie recording dialog")); + enumData.push_back(EventTypeEnum(EVENT_NODE_IDENTIFICATION_COLORS_GET_FROM_CHARTS, "EVENT_NODE_IDENTIFICATION_COLORS_GET_FROM_CHARTS", "Get the color for node identification symbols from all charts that contain nodes")); diff --git a/src/Common/EventTypeEnum.h b/src/Common/EventTypeEnum.h index 2fd301fbd..2fbd1b2ea 100644 --- a/src/Common/EventTypeEnum.h +++ b/src/Common/EventTypeEnum.h @@ -168,6 +168,8 @@ public: EVENT_MODEL_GET_ALL_DISPLAYED, /** model surface - get */ EVENT_MODEL_SURFACE_GET, + /** Update the movie dialog */ + EVENT_MOVIE_RECORDING_DIALOG_UPDATE, /** Get the color for a node's identification symbol from a chart that contains the node */ EVENT_NODE_IDENTIFICATION_COLORS_GET_FROM_CHARTS, /** Get the transformation for converting object coordinates to window coordinates */ diff --git a/src/GuiQt/BrainBrowserWindow.cxx b/src/GuiQt/BrainBrowserWindow.cxx index 4ac4a7e56..819027599 100644 --- a/src/GuiQt/BrainBrowserWindow.cxx +++ b/src/GuiQt/BrainBrowserWindow.cxx @@ -1336,8 +1336,15 @@ BrainBrowserWindow::createActions() this, SLOT(processCaptureImage())); - m_recordMovieAction = - WuQtUtilities::createAction("Animation Control...", + m_movieRecordingAction = + WuQtUtilities::createAction("Movie Recording...", + "Record the windows content", + this, + this, + SLOT(processMovieRecording())); + + m_recordMovieAction = + WuQtUtilities::createAction("(obsolete)Animation Control...", "Animate Brain Surface", this, this, @@ -1754,6 +1761,7 @@ BrainBrowserWindow::createMenuFile() menu->addSeparator(); menu->addAction(m_recordMovieAction); menu->addAction(m_captureImageAction); + menu->addAction(m_movieRecordingAction); menu->addSeparator(); menu->addAction(m_closeTabAction); menu->addAction(m_closeWindowAction); @@ -3048,6 +3056,15 @@ BrainBrowserWindow::processCaptureImage() GuiManager::get()->processShowImageCaptureDialog(this); } +/** + * Called when movie recording is selected. + */ +void +BrainBrowserWindow::processMovieRecording() +{ + GuiManager::get()->processShowMovieRecordingDialog(this); +} + /** * Called when capture image is selected. */ diff --git a/src/GuiQt/BrainBrowserWindow.h b/src/GuiQt/BrainBrowserWindow.h index 1ec2b7a81..707f27261 100644 --- a/src/GuiQt/BrainBrowserWindow.h +++ b/src/GuiQt/BrainBrowserWindow.h @@ -169,6 +169,7 @@ namespace caret { void processDataFileOpen(); void processManageSaveLoadedFiles(); void processCaptureImage(); + void processMovieRecording(); void processRecordMovie(); void processEditPreferences(); void processCloseAllFiles(); @@ -365,6 +366,8 @@ namespace caret { QAction* m_captureImageAction; + QAction* m_movieRecordingAction; + QAction* m_recordMovieAction; QAction* m_preferencesAction; diff --git a/src/GuiQt/BrainBrowserWindowToolBarTab.cxx b/src/GuiQt/BrainBrowserWindowToolBarTab.cxx index 2f574fa26..b675180e1 100644 --- a/src/GuiQt/BrainBrowserWindowToolBarTab.cxx +++ b/src/GuiQt/BrainBrowserWindowToolBarTab.cxx @@ -172,7 +172,7 @@ BrainBrowserWindowToolBarTab::updateContent(BrowserTabContent* browserTabContent m_lightingEnabledCheckBox->setChecked(browserTabContent->isLightingEnabled()); if (WuQMacroManager::instance()->isModeRecording()) { - m_macroRecordingLabel->setText("Recording"); + m_macroRecordingLabel->setText("Macro"); } else { m_macroRecordingLabel->setText(""); diff --git a/src/GuiQt/BrainOpenGLWidget.cxx b/src/GuiQt/BrainOpenGLWidget.cxx index 5ecafaf1d..78d3d0d69 100644 --- a/src/GuiQt/BrainOpenGLWidget.cxx +++ b/src/GuiQt/BrainOpenGLWidget.cxx @@ -26,6 +26,7 @@ #include #include +#include #include #include #ifdef WORKBENCH_USE_QT5_QOPENGL_WIDGET @@ -68,6 +69,7 @@ #include "Matrix4x4.h" #include "Model.h" #include "MouseEvent.h" +#include "MovieRecorder.h" #include "OffScreenOpenGLRenderer.h" #include "SelectionManager.h" #include "SelectionItemAnnotation.h" @@ -1557,6 +1559,9 @@ BrainOpenGLWidget::mouseMoveEvent(QMouseEvent* me) void BrainOpenGLWidget::receiveEvent(Event* event) { + bool doRepaintGraphicsFlag(false); + bool doUpdateGraphicsFlag(false); + if (event->getEventType() == EventTypeEnum::EVENT_BRAIN_RESET) { EventBrainReset* brainResetEvent = dynamic_cast(event); CaretAssert(brainResetEvent); @@ -1573,14 +1578,10 @@ BrainOpenGLWidget::receiveEvent(Event* event) updateAllEvent->setEventProcessed(); if (updateAllEvent->isRepaint()) { - this->repaint(); + doRepaintGraphicsFlag = true; } else { -#ifdef WORKBENCH_USE_QT5_QOPENGL_WIDGET - this->update(); -#else - this->updateGL(); -#endif + doUpdateGraphicsFlag = true; } } else if (event->getEventType() == EventTypeEnum::EVENT_GRAPHICS_UPDATE_ONE_WINDOW) { @@ -1590,12 +1591,7 @@ BrainOpenGLWidget::receiveEvent(Event* event) if (updateOneEvent->getWindowIndex() == this->windowIndex) { updateOneEvent->setEventProcessed(); - -#ifdef WORKBENCH_USE_QT5_QOPENGL_WIDGET - this->update(); -#else - this->updateGL(); -#endif + doUpdateGraphicsFlag = true; } else { /* @@ -1610,11 +1606,7 @@ BrainOpenGLWidget::receiveEvent(Event* event) bool needUpdate = false; if (needUpdate) { -#ifdef WORKBENCH_USE_QT5_QOPENGL_WIDGET - this->update(); -#else - this->updateGL(); -#endif + doUpdateGraphicsFlag = true; } } } @@ -1695,6 +1687,86 @@ BrainOpenGLWidget::receiveEvent(Event* event) else { } + + if (doRepaintGraphicsFlag + || doUpdateGraphicsFlag) { + + bool captureAutomaticImageForMovieFlag(false); + bool captureManualImageForMovieFlag(false); + MovieRecorder* movieRecorder = SessionManager::get()->getMovieRecorder(); + if (movieRecorder->getRecordingWindowIndex() == this->windowIndex) { + switch (movieRecorder->getRecordingMode()) { + case MovieRecorderModeEnum::MANUAL: + if (movieRecorder->isManualRecordingOfImageRequested()) { + captureManualImageForMovieFlag = true; + } + doRepaintGraphicsFlag = true; + break; + case MovieRecorderModeEnum::AUTOMATIC: + captureAutomaticImageForMovieFlag = true; + doRepaintGraphicsFlag = true; + break; + } + } + + if (doRepaintGraphicsFlag) { + this->repaint(); + + if (captureAutomaticImageForMovieFlag + || captureManualImageForMovieFlag) { + int captureOffsetX(0); + int captureOffsetY(0); + int captureWidth(0); + int captureHeight(0); + int widgetWidth(0); + int widgetHeight(0); + BrainBrowserWindow* browserWindow = GuiManager::get()->getBrowserWindowByWindowIndex(this->windowIndex); + + if (browserWindow != NULL) { + browserWindow->getGraphicsWidgetSize(captureOffsetX, + captureOffsetY, + captureWidth, + captureHeight, + widgetWidth, + widgetHeight, + true); + int imageWidth(0); + int imageHeight(0); + movieRecorder->getVideoDimensions(imageWidth, + imageHeight); + EventImageCapture captureEvent(this->windowIndex, + captureOffsetX, + captureOffsetY, + captureWidth, + captureHeight, + imageWidth, + imageHeight); + captureImage(&captureEvent); + + if (captureEvent.isError()) { + CaretLogSevere("Failed to capture image for movie recording"); + } + else { + QImage image = captureEvent.getImage(); + if (captureAutomaticImageForMovieFlag) { + movieRecorder->addImageToMovie(&image); + } + else if (captureManualImageForMovieFlag) { + movieRecorder->addImageToMovieWithManualDuration(&image); + } + EventManager::get()->sendSimpleEvent(EventTypeEnum::EVENT_MOVIE_RECORDING_DIALOG_UPDATE); + } + } + } + } + else if (doUpdateGraphicsFlag) { +#ifdef WORKBENCH_USE_QT5_QOPENGL_WIDGET + this->update(); +#else + this->updateGL(); +#endif + } + } } /** diff --git a/src/GuiQt/CMakeLists.txt b/src/GuiQt/CMakeLists.txt index 6e2eb8e21..46ce58b81 100644 --- a/src/GuiQt/CMakeLists.txt +++ b/src/GuiQt/CMakeLists.txt @@ -150,6 +150,7 @@ ELSE() MetaDataEditorDialog.h MetaDataEditorWidget.h MovieDialog.h + MovieRecordingDialog.h OffScreenOpenGLRenderer.h OverlaySetViewController.h OverlaySettingsEditorDialog.h @@ -394,6 +395,7 @@ MetaDataEditorDialog.h MetaDataEditorWidget.h MouseEvent.h MovieDialog.h +MovieRecordingDialog.h OffScreenOpenGLRenderer.h OverlaySetViewController.h OverlaySettingsEditorDialog.h @@ -646,6 +648,7 @@ MetaDataEditorDialog.cxx MetaDataEditorWidget.cxx MouseEvent.cxx MovieDialog.cxx +MovieRecordingDialog.cxx OffScreenOpenGLRenderer.cxx OverlaySetViewController.cxx OverlayViewController.cxx diff --git a/src/GuiQt/EventMovieDialogUpdate.cxx b/src/GuiQt/EventMovieDialogUpdate.cxx new file mode 100644 index 000000000..5d423b074 --- /dev/null +++ b/src/GuiQt/EventMovieDialogUpdate.cxx @@ -0,0 +1,56 @@ + +/*LICENSE_START*/ +/* + * Copyright (C) 2019 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + +#define __EVENT_MOVIE_DIALOG_UPDATE_DECLARE__ +#include "EventMovieDialogUpdate.h" +#undef __EVENT_MOVIE_DIALOG_UPDATE_DECLARE__ + +#include "CaretAssert.h" +#include "EventTypeEnum.h" + +using namespace caret; + + + +/** + * \class caret::EventMovieDialogUpdate + * \brief + * \ingroup GuiQt + * + * + */ + +/** + * Constructor. + */ +EventMovieDialogUpdate::EventMovieDialogUpdate() +: Event(EventTypeEnum::EVENT_MOVIE_DIALOG_UPDATE) +{ + +} + +/** + * Destructor. + */ +EventMovieDialogUpdate::~EventMovieDialogUpdate() +{ +} + diff --git a/src/GuiQt/EventMovieDialogUpdate.h b/src/GuiQt/EventMovieDialogUpdate.h new file mode 100644 index 000000000..16dd39393 --- /dev/null +++ b/src/GuiQt/EventMovieDialogUpdate.h @@ -0,0 +1,58 @@ +#ifndef __EVENT_MOVIE_DIALOG_UPDATE_H__ +#define __EVENT_MOVIE_DIALOG_UPDATE_H__ + +/*LICENSE_START*/ +/* + * Copyright (C) 2019 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + + + +#include + +#include "Event.h" + + + +namespace caret { + + class EventMovieDialogUpdate : public Event { + + public: + EventMovieDialogUpdate(); + + virtual ~EventMovieDialogUpdate(); + + EventMovieDialogUpdate(const EventMovieDialogUpdate&) = delete; + + EventMovieDialogUpdate& operator=(const EventMovieDialogUpdate&) = delete; + + + // ADD_NEW_METHODS_HERE + + private: + // ADD_NEW_MEMBERS_HERE + + }; + +#ifdef __EVENT_MOVIE_DIALOG_UPDATE_DECLARE__ + // +#endif // __EVENT_MOVIE_DIALOG_UPDATE_DECLARE__ + +} // namespace +#endif //__EVENT_MOVIE_DIALOG_UPDATE_H__ diff --git a/src/GuiQt/GuiManager.cxx b/src/GuiQt/GuiManager.cxx index 19a6b2036..b6238d5dd 100644 --- a/src/GuiQt/GuiManager.cxx +++ b/src/GuiQt/GuiManager.cxx @@ -86,8 +86,6 @@ #include "EventUserInterfaceUpdate.h" #include "FociPropertiesEditorDialog.h" #include "GapsAndMarginsDialog.h" -#include "WbMacroCustomOperationManager.h" -#include "WbMacroHelper.h" #include "HelpViewerDialog.h" #include "IdentifiedItemNode.h" #include "IdentifiedItemVoxel.h" @@ -101,6 +99,7 @@ #include "OverlaySettingsEditorDialog.h" #include "MacDockMenu.h" #include "MovieDialog.h" +#include "MovieRecordingDialog.h" #include "PaletteColorMappingEditorDialog.h" #include "PreferencesDialog.h" #include "SceneAttributes.h" @@ -123,6 +122,8 @@ #include "Surface.h" #include "TileTabsConfigurationDialog.h" #include "VolumeMappableInterface.h" +#include "WbMacroCustomOperationManager.h" +#include "WbMacroHelper.h" #include "WuQMacroManager.h" #include "WuQMacroWidgetTypeEnum.h" #include "WuQMessageBox.h" @@ -176,6 +177,7 @@ GuiManager::initializeGuiManager() m_gapsAndMarginsDialog = NULL; this->imageCaptureDialog = NULL; this->movieDialog = NULL; + m_movieRecordingDialog = NULL; m_informationDisplayDialog = NULL; m_identifyBrainordinateDialog = NULL; this->preferencesDialog = NULL; @@ -2364,6 +2366,23 @@ GuiManager::processShowImageCaptureDialog(BrainBrowserWindow* browserWindow) this->imageCaptureDialog->showDialog(); } +/** + * Show the image capture window. + * @param browserWindow + * Window on which dialog was requested. + */ +void +GuiManager::processShowMovieRecordingDialog(BrainBrowserWindow* browserWindow) +{ + if (m_movieRecordingDialog == NULL) { + m_movieRecordingDialog = new MovieRecordingDialog(browserWindow); + this->addNonModalDialog(m_movieRecordingDialog); + } + m_movieRecordingDialog->updateDialog(); + m_movieRecordingDialog->setBrowserWindowIndex(browserWindow->getBrowserWindowIndex()); + m_movieRecordingDialog->showDialog(); +} + /** * Show the gaps and margins window. * @param browserWindow @@ -2779,6 +2798,9 @@ GuiManager::restoreFromScene(const SceneAttributes* sceneAttributes, if (imageCaptureDialog != NULL) { imageCaptureDialog->updateDialog(); } + if (m_movieRecordingDialog != NULL) { + m_movieRecordingDialog->updateDialog(); + } progressEvent.setProgressMessage("Invalidating coloring and updating user interface"); EventManager::get()->sendEvent(progressEvent.getPointer()); diff --git a/src/GuiQt/GuiManager.h b/src/GuiQt/GuiManager.h index ef4ca8aa2..ed8c14b11 100644 --- a/src/GuiQt/GuiManager.h +++ b/src/GuiQt/GuiManager.h @@ -55,6 +55,7 @@ namespace caret { class ImageFile; class ImageCaptureDialog; class InformationDisplayDialog; + class MovieRecordingDialog; class OverlaySettingsEditorDialog; class Model; class PaletteColorMappingEditorDialog; @@ -136,6 +137,7 @@ namespace caret { void processShowCustomViewDialog(BrainBrowserWindow* browserWindow); void processShowGapsAndMarginsDialog(BrainBrowserWindow* browserWindow); void processShowImageCaptureDialog(BrainBrowserWindow* browserWindow); + void processShowMovieRecordingDialog(BrainBrowserWindow* browserWindow); void processShowMovieDialog(BrainBrowserWindow* browserWindow); void processShowPreferencesDialog(BrainBrowserWindow* browserWindow); void processShowInformationDisplayDialog(const bool forceDisplayOfDialog); @@ -263,6 +265,8 @@ namespace caret { ImageCaptureDialog* imageCaptureDialog; + MovieRecordingDialog* m_movieRecordingDialog; + GapsAndMarginsDialog* m_gapsAndMarginsDialog; MovieDialog* movieDialog; diff --git a/src/GuiQt/MovieRecordingDialog.cxx b/src/GuiQt/MovieRecordingDialog.cxx new file mode 100644 index 000000000..1739e310e --- /dev/null +++ b/src/GuiQt/MovieRecordingDialog.cxx @@ -0,0 +1,616 @@ + +/*LICENSE_START*/ +/* + * Copyright (C) 2019 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + +#define __MOVIE_RECORDING_DIALOG_DECLARE__ +#include "MovieRecordingDialog.h" +#undef __MOVIE_RECORDING_DIALOG_DECLARE__ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "BrainBrowserWindowComboBox.h" +#include "CaretAssert.h" +#include "CaretFileDialog.h" +#include "CursorDisplayScoped.h" +#include "Event.h" +#include "EventManager.h" +#include "EventGraphicsUpdateOneWindow.h" +#include "EnumComboBoxTemplate.h" +#include "FileInformation.h" +#include "MovieRecorder.h" +#include "SessionManager.h" +#include "WuQMessageBox.h" + +using namespace caret; + + + +/** + * \class caret::MovieRecordingDialog + * \brief Dialog for control of movie recording and creation + * \ingroup GuiQt + */ + +/** + * Constructor. + */ +MovieRecordingDialog::MovieRecordingDialog(QWidget* parent) +: WuQDialogNonModal("Movie Recording", + parent) +{ + QTabWidget* tabWidget = new QTabWidget(); + tabWidget->addTab(createMainWidget(), "Main"); + tabWidget->addTab(createSettingsWidget(), "Settings"); + + setCentralWidget(tabWidget, + SCROLL_AREA_NEVER); + + EventManager::get()->addEventListener(this, + EventTypeEnum::EVENT_MOVIE_RECORDING_DIALOG_UPDATE); + disableAutoDefaultForAllPushButtons(); +} + +/** + * Destructor. + */ +MovieRecordingDialog::~MovieRecordingDialog() +{ + EventManager::get()->removeAllEventsFromListener(this); +} + +/** + * Receive an event. + * + * @param event + * An event for which this instance is listening. + */ +void +MovieRecordingDialog::receiveEvent(Event* event) +{ + if (event->getEventType() == EventTypeEnum::EVENT_MOVIE_RECORDING_DIALOG_UPDATE) { + updateDialog(); + event->setEventProcessed(); + } +} + +/** + * May be called to update the dialog's content. + */ +void +MovieRecordingDialog::updateDialog() +{ + const MovieRecorder* movieRecorder = SessionManager::get()->getMovieRecorder(); + CaretAssert(movieRecorder); + + m_windowComboBox->updateComboBox(); + m_windowComboBox->setBrowserWindowByIndex(movieRecorder->getRecordingWindowIndex()); + + bool manualRecordingEnabledFlag(false); + switch (movieRecorder->getRecordingMode()) { + case MovieRecorderModeEnum::MANUAL: + m_recordingManualRadioButton->setChecked(true); + manualRecordingEnabledFlag = true; + break; + case MovieRecorderModeEnum::AUTOMATIC: + m_recordingAutomaticRadioButton->setChecked(true); + break; + } + + const MovieRecorderVideoFormatTypeEnum::Enum formatType = movieRecorder->getVideoFormatType(); + m_movieRecorderVideoFormatTypeEnumComboBox->setSelectedItem(formatType); + + const MovieRecorderVideoDimensionsTypeEnum::Enum dimType = movieRecorder->getVideoDimensionsType(); + m_movieRecorderVideoDimensionsTypeEnumComboBox->setSelectedItem(dimType); + + updateManualRecordingOptions(); + updateCustomWidthHeightSpinBoxes(); + updateFileNameLabel(); + updateFrameCountLabel(); +} + +/** + * Update the manual recording options + */ +void +MovieRecordingDialog::updateManualRecordingOptions() +{ + const MovieRecorder* movieRecorder = SessionManager::get()->getMovieRecorder(); + CaretAssert(movieRecorder); + + bool manualRecordingEnabledFlag(false); + switch (movieRecorder->getRecordingMode()) { + case MovieRecorderModeEnum::MANUAL: + manualRecordingEnabledFlag = true; + break; + case MovieRecorderModeEnum::AUTOMATIC: + break; + } + + m_manualCaptureSecondsSpinBox->setValue(movieRecorder->getManualRecordingDurationSeconds()); + m_manualCaptureToolButton->setEnabled(manualRecordingEnabledFlag); +} + +/** + * Update the custom width/height spin boxes + */ +void +MovieRecordingDialog::updateCustomWidthHeightSpinBoxes() +{ + const MovieRecorder* movieRecorder = SessionManager::get()->getMovieRecorder(); + CaretAssert(movieRecorder); + + int32_t customWidth(0); + int32_t customHeight(0); + movieRecorder->getCustomDimensions(customWidth, + customHeight); + QSignalBlocker widthBlocker(m_customWidthSpinBox); + m_customWidthSpinBox->setValue(customWidth); + QSignalBlocker heightBlocker(m_customHeightSpinBox); + m_customHeightSpinBox->setValue(customHeight); + QSignalBlocker frameRateBlocker(m_frameRateSpinBox); + m_frameRateSpinBox->setValue(movieRecorder->getFramesRate()); + + bool customSpinBoxesEnabled(false); + switch (movieRecorder->getVideoDimensionsType()) { + case MovieRecorderVideoDimensionsTypeEnum::CUSTOM: + customSpinBoxesEnabled = true; + break; + case MovieRecorderVideoDimensionsTypeEnum::HD_1280_720: + break; + case MovieRecorderVideoDimensionsTypeEnum::SD_640_480: + break; + } + m_customWidthSpinBox->setEnabled(customSpinBoxesEnabled); + m_customHeightSpinBox->setEnabled(customSpinBoxesEnabled); +} + +/** + * Update the filename label + */ +void +MovieRecordingDialog::updateFileNameLabel() +{ + QString name = SessionManager::get()->getMovieRecorder()->getMovieFileName(); + if ( ! name.isEmpty()) { + FileInformation fileInfo(name); + name = fileInfo.getFileName(); + } + + m_filenameLabel->setText(name); +} + +/** + * Update the frame count label + */ +void +MovieRecordingDialog::updateFrameCountLabel() +{ + m_frameCountNumberLabel->setNum(SessionManager::get()->getMovieRecorder()->getNumberOfFrames()); +} + +/** + * Called when window index is changed + * + * @param windowIndex + * Index of window for recording + */ +void +MovieRecordingDialog::windowIndexSelected(const int32_t windowIndex) +{ + SessionManager::get()->getMovieRecorder()->setRecordingWindowIndex(windowIndex); +} + +/** + * Called when video dimensions type is changed + */ +void +MovieRecordingDialog::movieRecorderVideoDimensionsTypeEnumComboBoxItemActivated() +{ + const MovieRecorderVideoDimensionsTypeEnum::Enum dimType = m_movieRecorderVideoDimensionsTypeEnumComboBox->getSelectedItem(); + SessionManager::get()->getMovieRecorder()->setVideoDimensionsType(dimType); + updateCustomWidthHeightSpinBoxes(); +} + +/** + * Called when recording format is changed + */ +void +MovieRecordingDialog::movieRecorderVideoFormatTypeEnumComboBoxItemActivated() +{ + const MovieRecorderVideoFormatTypeEnum::Enum formatType = m_movieRecorderVideoFormatTypeEnumComboBox->getSelectedItem(); + SessionManager::get()->getMovieRecorder()->setVideoFormatType(formatType); + updateFileNameLabel(); +} + +/** +* Set the selected browser window to the browser window with the +* given index. +* @param browserWindowIndex +* Index of browser window. +*/ +void +MovieRecordingDialog::setBrowserWindowIndex(const int32_t browserWindowIndex) +{ + m_windowComboBox->setBrowserWindowByIndex(browserWindowIndex); + windowIndexSelected(browserWindowIndex); +} + +/** + * @param Called when custom width spin box value changed + * + * @param width + * New custom width + */ +void +MovieRecordingDialog::customWidthSpinBoxValueChanged(int width) +{ + SessionManager::get()->getMovieRecorder()->setCustomDimensions(width, + m_customHeightSpinBox->value()); +} + +/** + * @param Called when custom height spin box value changed + * + * @param height + * New custom height + */ +void +MovieRecordingDialog::customHeightSpinBoxValueChanged(int height) +{ + SessionManager::get()->getMovieRecorder()->setCustomDimensions(m_customWidthSpinBox->value(), + height); +} + +/** + * @param Called when frame rate spin box value changed + * + * @param frameRate + * New frame rate + */ +void +MovieRecordingDialog::frameRateSpinBoxValueChanged(int frameRate) +{ + SessionManager::get()->getMovieRecorder()->setFramesRate(frameRate); +} + +/** + * Called when recording mode button is clicked + * + * @param button + * Button that was clicked + */ +void +MovieRecordingDialog::recordingModeRadioButtonClicked(QAbstractButton* button) +{ + MovieRecorder* movieRecorder = SessionManager::get()->getMovieRecorder(); + CaretAssert(movieRecorder); + + if (button == m_recordingAutomaticRadioButton) { + movieRecorder->setRecordingMode(MovieRecorderModeEnum::AUTOMATIC); + } + else if (button == m_recordingManualRadioButton) { + movieRecorder->setRecordingMode(MovieRecorderModeEnum::MANUAL); + } + else { + CaretAssert(0); + } + + updateManualRecordingOptions(); +} + +/** + * Called when manual capture tool button is clicked + */ +void +MovieRecordingDialog::manualCaptureToolButtonClicked() +{ + CursorDisplayScoped cursor; + cursor.showWaitCursor(); + + MovieRecorder* movieRecorder = SessionManager::get()->getMovieRecorder(); + CaretAssert(movieRecorder); + movieRecorder->setManualRecordingOfImageRequested(true); + EventManager::get()->sendEvent(EventGraphicsUpdateOneWindow(m_windowComboBox->getSelectedBrowserWindowIndex()).getPointer()); + movieRecorder->setManualRecordingOfImageRequested(false); + updateFrameCountLabel(); +} + +/** + * Called when manual capture seconds spin box value changed + */ +void +MovieRecordingDialog::manualCaptureSecondsSpinBoxValueChanged(int seconds) +{ + MovieRecorder* movieRecorder = SessionManager::get()->getMovieRecorder(); + CaretAssert(movieRecorder); + movieRecorder->setManualRecordingDurationSeconds(seconds); +} + +/** + * Called when movie file name button is clicked + */ +void +MovieRecordingDialog::fileNameButtonClicked() +{ + const MovieRecorderVideoFormatTypeEnum::Enum formatType = m_movieRecorderVideoFormatTypeEnumComboBox->getSelectedItem(); + QString filename = CaretFileDialog::getSaveFileNameDialog(m_filenamePushButton, + "Choose Movie File", + "", + MovieRecorderVideoFormatTypeEnum::toFileNameExtensionNoDot(formatType)); + if ( ! filename.isEmpty()) { + SessionManager::get()->getMovieRecorder()->setMovieFileName(filename); + updateFileNameLabel(); + } +} + +/** + * Called when create movie push button is clicked + */ +void +MovieRecordingDialog::createMoviePushButtonClicked() +{ + MovieRecorder* movieRecorder = SessionManager::get()->getMovieRecorder(); + FileInformation fileInfo(movieRecorder->getMovieFileName()); + if (fileInfo.exists()) { + AString msg("Movie file \"" + + fileInfo.getFileName() + + "\" exists, overwrite?"); + if ( ! WuQMessageBox::warningOkCancel(m_createMoviePushButton, + msg)) { + return; + } + if ( ! fileInfo.remove()) { + AString msg("Unable to remove movie file \"" + + fileInfo.getFileName() + + "\""); + WuQMessageBox::errorOk(m_createMoviePushButton, + msg); + } + } + + AString errorMessage; + const bool successFlag = movieRecorder->createMovie(errorMessage); + if (successFlag) { + const QString text("Movie has been created. Reset (delete) recorded images for new movie? "); + const QString infoText("If no is selected, creation of a new movie will include all images from " + "this movie and any addition recorded images. Selecting yes is " + "equivalent to clicking the Reset button."); + if (WuQMessageBox::warningYesNo(m_createMoviePushButton, + text, + infoText)) { + SessionManager::get()->getMovieRecorder()->reset(); + } + } + else { + WuQMessageBox::errorOk(m_createMoviePushButton, + errorMessage); + } + + updateDialog(); +} + +/** + * Called when reset push button is clicked + */ +void +MovieRecordingDialog::resetPushButtonClicked() +{ + if (WuQMessageBox::warningOkCancel(m_resetPushButton, + "Reset (delete) recorded images for new movie")) { + SessionManager::get()->getMovieRecorder()->reset(); + updateDialog(); + } +} + +/** + * @return New instance of main widget + */ +QWidget* +MovieRecordingDialog::createMainWidget() +{ + QLabel* windowLabel = new QLabel("Record from Window:"); + m_windowComboBox = new BrainBrowserWindowComboBox(BrainBrowserWindowComboBox::STYLE_NAME_AND_NUMBER, + this); + m_windowComboBox->setToolTip("Sets window that is recorded"); + QObject::connect(m_windowComboBox, &BrainBrowserWindowComboBox::browserWindowIndexSelected, + this, &MovieRecordingDialog::windowIndexSelected); + QHBoxLayout* windowLayout = new QHBoxLayout(); + windowLayout->addWidget(windowLabel); + windowLayout->addWidget(m_windowComboBox->getWidget()); + windowLayout->addStretch(); + + m_recordingAutomaticRadioButton = new QRadioButton(MovieRecorderModeEnum::toGuiName(MovieRecorderModeEnum::AUTOMATIC)); + m_recordingAutomaticRadioButton->setToolTip("When selected, images are recorded as graphics updated"); + + const QString recordButtonText("Record"); + m_recordingManualRadioButton = new QRadioButton(MovieRecorderModeEnum::toGuiName(MovieRecorderModeEnum::MANUAL)); + m_recordingManualRadioButton->setToolTip("When selected, images recorded when " + + recordButtonText + + " is clicked"); + QButtonGroup* recordingButtonGroup = new QButtonGroup(this); + recordingButtonGroup->addButton(m_recordingAutomaticRadioButton); + recordingButtonGroup->addButton(m_recordingManualRadioButton); + QObject::connect(recordingButtonGroup, QOverload::of(&QButtonGroup::buttonClicked), + this, &MovieRecordingDialog::recordingModeRadioButtonClicked); + + m_manualCaptureToolButton = new QToolButton(); + m_manualCaptureToolButton->setText(recordButtonText); + m_manualCaptureToolButton->setToolTip("Duration of image displayed in movie"); + QObject::connect(m_manualCaptureToolButton, &QToolButton::clicked, + this, &MovieRecordingDialog::manualCaptureToolButtonClicked); + + m_manualCaptureSecondsSpinBox = new QSpinBox(); + m_manualCaptureSecondsSpinBox->setMinimum(1); + m_manualCaptureSecondsSpinBox->setMaximum(100); + m_manualCaptureSecondsSpinBox->setSingleStep(1); + m_manualCaptureSecondsSpinBox->setSizePolicy(QSizePolicy::Fixed, + m_manualCaptureSecondsSpinBox->sizePolicy().verticalPolicy()); + + QLabel* captureSecondsLabel = new QLabel("seconds"); + + QGroupBox* modeGroupBox = new QGroupBox("Recording Mode"); + QGridLayout* modeLayout = new QGridLayout(modeGroupBox); + modeLayout->setColumnStretch(0, 0); + modeLayout->setColumnStretch(1, 0); + modeLayout->setColumnStretch(2, 0); + modeLayout->setColumnStretch(3, 0); + modeLayout->setColumnStretch(4, 100); + int32_t modeRow(0); + modeLayout->addWidget(m_recordingAutomaticRadioButton, modeRow, 0); + modeRow++; + modeLayout->addWidget(m_recordingManualRadioButton, modeRow, 0); + modeLayout->addWidget(m_manualCaptureToolButton, modeRow, 1); + modeLayout->addWidget(m_manualCaptureSecondsSpinBox, modeRow, 2); + modeLayout->addWidget(captureSecondsLabel, modeRow, 3); + modeRow++; + + m_filenamePushButton = new QPushButton("Movie File..."); + m_filenamePushButton->setToolTip("Select movie file using file dialog"); + QObject::connect(m_filenamePushButton, &QPushButton::clicked, + this, &MovieRecordingDialog::fileNameButtonClicked); + m_filenameLabel = new QLabel(""); + m_filenameLabel->setMinimumWidth(150); + + m_createMoviePushButton = new QPushButton("Create Movie"); + m_createMoviePushButton->setToolTip("Create a movie file using images that have been recorded"); + QObject::connect(m_createMoviePushButton, &QPushButton::clicked, this, + &MovieRecordingDialog::createMoviePushButtonClicked); + + m_resetPushButton = new QPushButton("Reset"); + m_resetPushButton->setToolTip("Remove all recorded images to start a new movie"); + QObject::connect(m_resetPushButton, &QPushButton::clicked, this, + &MovieRecordingDialog::resetPushButtonClicked); + + QLabel* frameCountLabel = new QLabel("Frames: "); + m_frameCountNumberLabel = new QLabel("0"); + + QGroupBox* movieFileGroupBox = new QGroupBox("Output Movie"); + QGridLayout* movieLayout = new QGridLayout(movieFileGroupBox); + movieLayout->setColumnStretch(0, 0); + movieLayout->setColumnStretch(1, 0); + movieLayout->setColumnStretch(2, 0); + int32_t movieRow(0); + movieLayout->addWidget(m_filenamePushButton, movieRow, 0); + movieLayout->addWidget(m_filenameLabel, movieRow, 1, 1, 2, Qt::AlignLeft); + movieRow++; + movieLayout->addWidget(m_createMoviePushButton, movieRow, 0); + movieRow++; + movieLayout->addWidget(m_resetPushButton, movieRow, 0); + movieLayout->addWidget(frameCountLabel, movieRow, 1); + movieLayout->addWidget(m_frameCountNumberLabel, movieRow, 2); + movieRow++; + + + QWidget* widget = new QWidget(); + QVBoxLayout* layout = new QVBoxLayout(widget); + layout->addLayout(windowLayout); + layout->addWidget(modeGroupBox); + layout->addWidget(movieFileGroupBox); + layout->addStretch(); + + return widget; +} + +/** + * @return New instance of settings widget + */ +QWidget* +MovieRecordingDialog::createSettingsWidget() +{ + const int spinBoxWidth(100); + + QLabel* formatLabel = new QLabel("Format:"); + m_movieRecorderVideoFormatTypeEnumComboBox = new EnumComboBoxTemplate(this); + m_movieRecorderVideoFormatTypeEnumComboBox->getWidget()->setToolTip("Choose video format (not all formats on all computers)"); + m_movieRecorderVideoFormatTypeEnumComboBox->setup(); + QObject::connect(m_movieRecorderVideoFormatTypeEnumComboBox, SIGNAL(itemActivated()), + this, SLOT(movieRecorderVideoFormatTypeEnumComboBoxItemActivated())); + + QLabel* dimensionsLabel = new QLabel("Dimensions:"); + m_movieRecorderVideoDimensionsTypeEnumComboBox = new EnumComboBoxTemplate(this); + m_movieRecorderVideoDimensionsTypeEnumComboBox->getWidget()->setToolTip("Choose width and height of movie"); + m_movieRecorderVideoDimensionsTypeEnumComboBox->setup(); + QObject::connect(m_movieRecorderVideoDimensionsTypeEnumComboBox, SIGNAL(itemActivated()), + this, SLOT(movieRecorderVideoDimensionsTypeEnumComboBoxItemActivated())); + + QLabel* customLabel = new QLabel("Custom Dimensions:"); + m_customWidthSpinBox = new QSpinBox(); + m_customWidthSpinBox->setMinimum(1); + m_customWidthSpinBox->setMaximum(500000); + m_customWidthSpinBox->setSingleStep(1); + QObject::connect(m_customWidthSpinBox, QOverload::of(&QSpinBox::valueChanged), + this, &MovieRecordingDialog::customWidthSpinBoxValueChanged); + m_customWidthSpinBox->setFixedWidth(spinBoxWidth); + + m_customHeightSpinBox = new QSpinBox(); + m_customHeightSpinBox->setMinimum(1); + m_customHeightSpinBox->setMaximum(500000); + m_customHeightSpinBox->setSingleStep(1); + m_customHeightSpinBox->setFixedWidth(spinBoxWidth); + QObject::connect(m_customHeightSpinBox, QOverload::of(&QSpinBox::valueChanged), + this, &MovieRecordingDialog::customHeightSpinBoxValueChanged); + + QLabel* frameRateLabel = new QLabel("Frames Per Second:"); + m_frameRateSpinBox = new QSpinBox(); + m_frameRateSpinBox->setToolTip("20 or 30 recommended"); + m_frameRateSpinBox->setMinimum(1); + m_frameRateSpinBox->setMaximum(1000); + m_frameRateSpinBox->setSingleStep(1); + m_frameRateSpinBox->setFixedWidth(spinBoxWidth); + QObject::connect(m_frameRateSpinBox, QOverload::of(&QSpinBox::valueChanged), + this, &MovieRecordingDialog::frameRateSpinBoxValueChanged); + + QWidget* widget = new QWidget(); + QGridLayout* gridLayout = new QGridLayout(widget); + gridLayout->setRowStretch(100, 100); + gridLayout->setColumnStretch(0, 0); + gridLayout->setColumnStretch(1, 0); + gridLayout->setColumnStretch(2, 0); + gridLayout->setColumnStretch(3, 100); + int32_t row(0); + gridLayout->addWidget(formatLabel, row, 0); + gridLayout->addWidget(m_movieRecorderVideoFormatTypeEnumComboBox->getWidget(), + row, 1, 1, 2, Qt::AlignLeft); + row++; + gridLayout->addWidget(dimensionsLabel, row, 0); + gridLayout->addWidget(m_movieRecorderVideoDimensionsTypeEnumComboBox->getWidget(), + row, 1, 1, 2, Qt::AlignLeft); + row++; + gridLayout->addWidget(customLabel, row, 0); + gridLayout->addWidget(m_customWidthSpinBox, row, 1); + gridLayout->addWidget(m_customHeightSpinBox, row, 2); + row++; + gridLayout->addWidget(frameRateLabel, row, 0); + gridLayout->addWidget(m_frameRateSpinBox, + row, 1, 1, 2, Qt::AlignLeft); + row++; + + return widget; +} + diff --git a/src/GuiQt/MovieRecordingDialog.h b/src/GuiQt/MovieRecordingDialog.h new file mode 100644 index 000000000..9942e83e8 --- /dev/null +++ b/src/GuiQt/MovieRecordingDialog.h @@ -0,0 +1,143 @@ +#ifndef __MOVIE_RECORDING_DIALOG_H__ +#define __MOVIE_RECORDING_DIALOG_H__ + +/*LICENSE_START*/ +/* + * Copyright (C) 2019 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + + + +#include + + +#include "EventListenerInterface.h" +#include "WuQDialogNonModal.h" + +class QAbstractButton; +class QLabel; +class QPushButton; +class QRadioButton; +class QSpinBox; +class QToolButton; + +namespace caret { + + class BrainBrowserWindowComboBox; + class EnumComboBoxTemplate; + + class MovieRecordingDialog : public WuQDialogNonModal, public EventListenerInterface { + + Q_OBJECT + + public: + MovieRecordingDialog(QWidget* parent); + + virtual ~MovieRecordingDialog(); + + MovieRecordingDialog(const MovieRecordingDialog&) = delete; + + MovieRecordingDialog& operator=(const MovieRecordingDialog&) = delete; + + void setBrowserWindowIndex(const int32_t browserWindowIndex); + + void updateDialog(); + + + // ADD_NEW_METHODS_HERE + + virtual void receiveEvent(Event* event); + + private slots: + void movieRecorderVideoDimensionsTypeEnumComboBoxItemActivated(); + + void movieRecorderVideoFormatTypeEnumComboBoxItemActivated(); + + void recordingModeRadioButtonClicked(QAbstractButton* button); + + void customWidthSpinBoxValueChanged(int width); + + void customHeightSpinBoxValueChanged(int width); + + void frameRateSpinBoxValueChanged(int frameRate); + + void windowIndexSelected(const int32_t windowIndex); + + void fileNameButtonClicked(); + + void createMoviePushButtonClicked(); + + void resetPushButtonClicked(); + + void manualCaptureToolButtonClicked(); + + void manualCaptureSecondsSpinBoxValueChanged(int seconds); + + private: + void updateFileNameLabel(); + + void updateFrameCountLabel(); + + void updateCustomWidthHeightSpinBoxes(); + + void updateManualRecordingOptions(); + + QWidget* createMainWidget(); + + QWidget* createSettingsWidget(); + + QRadioButton* m_recordingAutomaticRadioButton; + + QRadioButton* m_recordingManualRadioButton; + + QToolButton* m_manualCaptureToolButton; + + QSpinBox* m_manualCaptureSecondsSpinBox; + + EnumComboBoxTemplate* m_movieRecorderVideoDimensionsTypeEnumComboBox; + + EnumComboBoxTemplate* m_movieRecorderVideoFormatTypeEnumComboBox; + + QSpinBox* m_customWidthSpinBox; + + QSpinBox* m_customHeightSpinBox; + + QSpinBox* m_frameRateSpinBox; + + QPushButton* m_filenamePushButton; + + QPushButton* m_createMoviePushButton; + + QPushButton* m_resetPushButton; + + QLabel* m_filenameLabel; + + QLabel* m_frameCountNumberLabel; + + BrainBrowserWindowComboBox* m_windowComboBox; + + // ADD_NEW_MEMBERS_HERE + + }; + +#ifdef __MOVIE_RECORDING_DIALOG_DECLARE__ + // +#endif // __MOVIE_RECORDING_DIALOG_DECLARE__ + +} // namespace +#endif //__MOVIE_RECORDING_DIALOG_H__ diff --git a/src/GuiQt/WbMacroCustomOperationBase.cxx b/src/GuiQt/WbMacroCustomOperationBase.cxx index 24a759e90..05c378661 100644 --- a/src/GuiQt/WbMacroCustomOperationBase.cxx +++ b/src/GuiQt/WbMacroCustomOperationBase.cxx @@ -30,6 +30,8 @@ #include "EventManager.h" #include "EventSurfacesGet.h" #include "EventUserInterfaceUpdate.h" +#include "MovieRecorder.h" +#include "SessionManager.h" #include "Surface.h" #include "SystemUtilities.h" #include "WuQMacroCommand.h" @@ -170,3 +172,57 @@ WbMacroCustomOperationBase::updateUserInterface() { EventManager::get()->sendEvent(EventUserInterfaceUpdate().getPointer()); } + +/** + * Get number of steps and sleep time. If a movie is being recorded, the number of + * steps is set so that the command will run for the requested duration using the + * frame rate of the movie recorder. If a movie + * is NOT being recorded, the number of steps out is the default number of steps and + * the sleep time is set so that the command will run for approximately the duration. + * + * @param defaultNumberOfSteps + * The default number of steps used when a movie is not being recorded + * @param durationSeconds + * The number of seconds for which the command should run + * @param numberOfStepsOut + * Output with number of steps for the command + * @param sleepTimeOut + * Output with time command should sleep at the end of each iteration + * when a movie is not being recorded + */ +void +WbMacroCustomOperationBase::getNumberOfStepsAndSleepTime(const float defaultNumberOfSteps, + const float durationSeconds, + float& numberOfStepsOut, + float& sleepTimeOut) +{ + numberOfStepsOut = defaultNumberOfSteps; + sleepTimeOut = 0.0; + + const MovieRecorder* movieRecorder = SessionManager::get()->getMovieRecorder(); + switch (movieRecorder->getRecordingMode()) { + case MovieRecorderModeEnum::MANUAL: + sleepTimeOut = durationSeconds / numberOfStepsOut; + break; + case MovieRecorderModeEnum::AUTOMATIC: + numberOfStepsOut = (durationSeconds + * movieRecorder->getFramesRate()); + break; + } +} + +/** + * Sleep for the given number of seconds at the end of an iteration + * + * @param seconds + * Seconds to sleep + */ +void +WbMacroCustomOperationBase::sleepForSecondsAtEndOfIteration(const float seconds) +{ + if (seconds > 0.0) { + SystemUtilities::sleepSeconds(seconds); + } +} + + diff --git a/src/GuiQt/WbMacroCustomOperationBase.h b/src/GuiQt/WbMacroCustomOperationBase.h index 9462e9fca..4e53f4aef 100644 --- a/src/GuiQt/WbMacroCustomOperationBase.h +++ b/src/GuiQt/WbMacroCustomOperationBase.h @@ -67,6 +67,13 @@ namespace caret { QString getErrorMessage() const; + void getNumberOfStepsAndSleepTime(const float defaultNumberOfSteps, + const float durationSeconds, + float& numberOfStepsOut, + float& sleepTimeOut); + + void sleepForSecondsAtEndOfIteration(const float seconds); + protected: WbMacroCustomOperationBase(); diff --git a/src/GuiQt/WbMacroCustomOperationModelRotation.cxx b/src/GuiQt/WbMacroCustomOperationModelRotation.cxx index ffaadff6e..4d85099f3 100644 --- a/src/GuiQt/WbMacroCustomOperationModelRotation.cxx +++ b/src/GuiQt/WbMacroCustomOperationModelRotation.cxx @@ -180,11 +180,20 @@ WbMacroCustomOperationModelRotation::performRotation(BrowserTabContent* tabConte const float totalRotation, const float durationSeconds) { - - const double rotationIncrement = 1.0; - const int32_t rotationStepCount = static_cast(totalRotation / rotationIncrement); - const float sleepTimeSeconds = durationSeconds / rotationStepCount; - + const float defaultNumberOfSteps(60.0); + float numberOfSteps(0.0); + float iterationSleepTime(0.0); + getNumberOfStepsAndSleepTime(defaultNumberOfSteps, + durationSeconds, + numberOfSteps, + iterationSleepTime); + +// const double rotationIncrement = 1.0; +// const int32_t rotationStepCount = static_cast(totalRotation / rotationIncrement); +// const float sleepTimeSeconds = durationSeconds / rotationStepCount; + const int32_t rotationStepCount = numberOfSteps; + const float rotationIncrement = totalRotation / numberOfSteps; + for (int32_t i = 0; i < rotationStepCount; i++) { Matrix4x4 rotationMatrix = tabContent->getRotationMatrix(); switch (axis) { @@ -202,7 +211,8 @@ WbMacroCustomOperationModelRotation::performRotation(BrowserTabContent* tabConte tabContent->setRotationMatrix(rotationMatrix); updateGraphics(); - SystemUtilities::sleepSeconds(sleepTimeSeconds); + sleepForSecondsAtEndOfIteration(iterationSleepTime); +// SystemUtilities::sleepSeconds(sleepTimeSeconds); } return true; diff --git a/src/GuiQt/WbMacroCustomOperationSurfaceInterpolation.cxx b/src/GuiQt/WbMacroCustomOperationSurfaceInterpolation.cxx index 76321b127..e433ba730 100644 --- a/src/GuiQt/WbMacroCustomOperationSurfaceInterpolation.cxx +++ b/src/GuiQt/WbMacroCustomOperationSurfaceInterpolation.cxx @@ -50,6 +50,7 @@ using namespace caret; * Constructor. */ WbMacroCustomOperationSurfaceInterpolation::WbMacroCustomOperationSurfaceInterpolation() +: WbMacroCustomOperationBase() { } @@ -217,16 +218,20 @@ WbMacroCustomOperationSurfaceInterpolation::interpolateSurface(const int32_t tab CaretAssert(startSurface); CaretAssert(endSurface); + const float defaultNumberOfSteps(50.0); + float numberOfSteps(0.0); + float iterationSleepTime(0.0); + getNumberOfStepsAndSleepTime(defaultNumberOfSteps, + durationSeconds, + numberOfSteps, + iterationSleepTime); + const StructureEnum::Enum structure = startSurface->getStructure(); const bool endSurfaceModifiedStatus = endSurface->isModified(); - const float* startingXYZ = startSurface->getCoordinateData(); const float* endingXYZ = endSurface->getCoordinateData(); - const float numberOfSteps(50.0f); - const float iterationSleepTime = durationSeconds / numberOfSteps; - /* * XYZ that will be interpolated */ @@ -314,7 +319,11 @@ WbMacroCustomOperationSurfaceInterpolation::interpolateSurface(const int32_t tab } updateGraphics(); - SystemUtilities::sleepSeconds(iterationSleepTime); + + sleepForSecondsAtEndOfIteration(iterationSleepTime); +// if ( ! recordingFlag) { +// SystemUtilities::sleepSeconds(iterationSleepTime); +// } } /* diff --git a/src/GuiQt/WuQMacroDialog.cxx b/src/GuiQt/WuQMacroDialog.cxx index 5280ac0dd..15c5e6af0 100644 --- a/src/GuiQt/WuQMacroDialog.cxx +++ b/src/GuiQt/WuQMacroDialog.cxx @@ -24,6 +24,7 @@ #undef __WU_Q_MACRO_DIALOG_DECLARE__ #include +#include #include #include #include @@ -183,6 +184,16 @@ WuQMacroDialog::createMacroRunAndEditingToolButtons() QObject::connect(m_runMacroToolButton, &QToolButton::clicked, this, &WuQMacroDialog::runMacroToolButtonClicked); + m_stopMacroToolButton = new QToolButton(); + m_stopMacroToolButton->setToolButtonStyle(Qt::ToolButtonTextBesideIcon); + m_stopMacroToolButton->setText("Stop"); + QPixmap stopPixmap = createEditingToolButtonPixmap(m_stopMacroToolButton, + EditButton::STOP); + m_stopMacroToolButton->setIcon(stopPixmap); + m_stopMacroToolButton->setToolTip("Stop the currently running macro. Response may not be immediate."); + QObject::connect(m_stopMacroToolButton, &QToolButton::clicked, + this, &WuQMacroDialog::stopMacroToolButtonClicked); + m_editingMoveUpToolButton = new QToolButton(); QPixmap moveUpPixmap = createEditingToolButtonPixmap(m_editingMoveUpToolButton, EditButton::MOVE_UP); @@ -219,6 +230,7 @@ WuQMacroDialog::createMacroRunAndEditingToolButtons() QHBoxLayout* toolButtonLayout = new QHBoxLayout(widget); toolButtonLayout->setContentsMargins(0, 0, 0, 0); toolButtonLayout->addWidget(m_runMacroToolButton); + toolButtonLayout->addWidget(m_stopMacroToolButton); toolButtonLayout->addStretch(); toolButtonLayout->addWidget(m_editingInsertToolButton); toolButtonLayout->addSpacing(10); @@ -403,6 +415,12 @@ WuQMacroDialog::createRunOptionsWidget() QObject::connect(m_runOptionMoveMouseCheckBox, &QCheckBox::clicked, this, &WuQMacroDialog::runOptionMoveMouseCheckBoxClicked); + m_recordMovieWhileMacroRunsCheckBox = new QCheckBox("Record Movie While Macro Runs"); + m_recordMovieWhileMacroRunsCheckBox->setChecked(false); + m_recordMovieWhileMacroRunsCheckBox->setToolTip("While the macro runs, add images to Movie Recorder"); + QObject::connect(m_recordMovieWhileMacroRunsCheckBox, &QCheckBox::clicked, + this, &WuQMacroDialog::runOptionRecordMovieCheckBoxClicked); + QGridLayout* runOptionsLayout = new QGridLayout(widget); runOptionsLayout->setColumnStretch(0, 0); runOptionsLayout->setColumnStretch(1, 100); @@ -414,6 +432,8 @@ WuQMacroDialog::createRunOptionsWidget() row++; runOptionsLayout->addWidget(m_runOptionMoveMouseCheckBox, row, 0, 1, 2, Qt::AlignLeft); row++; + runOptionsLayout->addWidget(m_recordMovieWhileMacroRunsCheckBox, row, 0, 1, 2, Qt::AlignLeft); + row++; return widget; } @@ -446,6 +466,14 @@ WuQMacroDialog::runOptionLoopCheckBoxClicked(bool checked) options->setLooping(checked); } +void +WuQMacroDialog::runOptionRecordMovieCheckBoxClicked(bool checked) +{ + WuQMacroExecutorOptions* options = WuQMacroManager::instance()->getExecutorOptions(); + CaretAssert(options); + options->setRecordMovieDuringExecution(checked); +} + /** * Called when a button in dialog is clicked * @@ -479,10 +507,12 @@ WuQMacroDialog::createCommandDisplayWidget() m_commandDelaySpinBox->setSingleStep(1.0); m_commandDelaySpinBox->setDecimals(1); m_commandDelaySpinBox->setToolTip("Delay, in seconds, after running command"); - m_commandDelaySpinBox->setFixedWidth(150); - m_commandDelaySpinBox->setSuffix(" seconds"); +// m_commandDelaySpinBox->setFixedWidth(150); + m_commandDelaySpinBox->setSizePolicy(QSizePolicy::Fixed, + m_commandDelaySpinBox->sizePolicy().verticalPolicy()); QObject::connect(m_commandDelaySpinBox, static_cast(&QDoubleSpinBox::valueChanged), this, &WuQMacroDialog::macroCommandDelaySpinBoxValueChanged); + QLabel* delayTwoLabel = new QLabel("seconds after command"); QLabel* toolTipLabel = new QLabel("ToolTip:"); m_commandToolTipTextEdit = new QPlainTextEdit(); @@ -496,16 +526,17 @@ WuQMacroDialog::createCommandDisplayWidget() layout->setColumnStretch(2, 100); int row = 0; layout->addWidget(titleLabel, row, 0); - layout->addWidget(m_commandTitleLabel, row, 1); + layout->addWidget(m_commandTitleLabel, row, 1, 1, 2, Qt::AlignLeft); row++; layout->addWidget(nameLabel, row, 0); - layout->addWidget(m_commandNameLabel, row, 1); + layout->addWidget(m_commandNameLabel, row, 1, 1, 2, Qt::AlignLeft); row++; layout->addWidget(typeLabel, row, 0); - layout->addWidget(m_commandTypeLabel, row, 1); + layout->addWidget(m_commandTypeLabel, row, 1, 1, 2, Qt::AlignLeft); row++; layout->addWidget(delayLabel, row, 0); layout->addWidget(m_commandDelaySpinBox, row, 1); + layout->addWidget(delayTwoLabel, row, 2, Qt::AlignLeft); row++; layout->addWidget(toolTipLabel, row, 0, (Qt::AlignLeft | Qt::AlignTop)); layout->addWidget(m_commandToolTipTextEdit, row, 1, 1, 2); @@ -513,13 +544,17 @@ WuQMacroDialog::createCommandDisplayWidget() QWidget* parametersWidget = new QWidget(); m_parameterWidgetsGridLayout = new QGridLayout(parametersWidget); + int pl(0), pt(0), pr(0), pb(0); + m_parameterWidgetsGridLayout->getContentsMargins(&pl, &pt, &pr, &pb); + pt = 0; + m_parameterWidgetsGridLayout->setContentsMargins(pl, pt, pr, pb); m_parameterWidgetsGridLayout->setVerticalSpacing(static_cast(m_parameterWidgetsGridLayout->verticalSpacing() / 1.2)); m_parameterWidgetsGridLayout->setColumnStretch(0, 0); m_parameterWidgetsGridLayout->setColumnStretch(1, 100); - m_parameterWidgetsGridLayout->addWidget(new QLabel("Edit"), - 0, 0, Qt::AlignHCenter); - m_parameterWidgetsGridLayout->addWidget(new QLabel("Parameter Value"), - 0, 1, Qt::AlignLeft); +// m_parameterWidgetsGridLayout->addWidget(new QLabel("Edit"), +// 0, 0, Qt::AlignHCenter); +// m_parameterWidgetsGridLayout->addWidget(new QLabel("Parameter Value"), +// 0, 1, Qt::AlignLeft); QHBoxLayout* titleLayout = new QHBoxLayout(); @@ -527,10 +562,16 @@ WuQMacroDialog::createCommandDisplayWidget() titleLayout->addWidget(new QLabel("Command")); titleLayout->addWidget(createHorizontalLine(), 100); + QHBoxLayout* parametersLayout = new QHBoxLayout(); + parametersLayout->setContentsMargins(0, 0, 0, 0); + parametersLayout->addWidget(new QLabel("Edit Parameters")); + parametersLayout->addWidget(createHorizontalLine(), 100); + QWidget* widget = new QWidget(); QVBoxLayout* widgetLayout = new QVBoxLayout(widget); widgetLayout->addLayout(titleLayout); widgetLayout->addLayout(layout); + widgetLayout->addLayout(parametersLayout); widgetLayout->addWidget(parametersWidget); widgetLayout->addStretch(); @@ -580,6 +621,7 @@ WuQMacroDialog::updateDialogContents() CaretAssert(runOptions); m_runOptionMoveMouseCheckBox->setChecked(runOptions->isShowMouseMovement()); m_runOptionLoopCheckBox->setChecked(runOptions->isLooping()); + m_recordMovieWhileMacroRunsCheckBox->setChecked(runOptions->isRecordMovieDuringExecution()); macroGroupComboBoxActivated(selectedIndex); } @@ -961,11 +1003,28 @@ WuQMacroDialog::runMacroToolButtonClicked() window = parentWidget(); } } + + /* + * While macro runs, edit buttons are disabled + */ + m_macroIsRunningFlag = true; + updateEditingToolButtons(); WuQMacroManager::instance()->runMacro(window, macro); + m_macroIsRunningFlag = false; + updateEditingToolButtons(); } } +/** + * Called when stop button is clicked + */ +void +WuQMacroDialog::stopMacroToolButtonClicked() +{ + WuQMacroManager::instance()->stopMacro(); +} + /** * Called when import item is selected */ @@ -1332,39 +1391,48 @@ WuQMacroDialog::updateEditingToolButtons() WuQMacroCommand* command = getSelectedMacroCommand(); bool runValid(false); - bool insertValid(macroGroup != NULL); + bool stopValid(false); + bool insertValid(false); bool deleteValid(false); bool moveUpValid(false); bool moveDownValid(false); - switch (getSelectedItemType()) { - case WuQMacroStandardItemTypeEnum::INVALID: - break; - case WuQMacroStandardItemTypeEnum::MACRO: - if (macroGroup != NULL) { - if (macro != NULL) { - const int32_t macroIndex = macroGroup->getIndexOfMacro(macro); - deleteValid = true; - moveUpValid = (macroIndex > 0); - moveDownValid = (macroIndex < (macroGroup->getNumberOfMacros() - 1)); - runValid = true; + if (m_macroIsRunningFlag) { + stopValid = true; + } + else { + insertValid = (macroGroup != NULL); + + switch (getSelectedItemType()) { + case WuQMacroStandardItemTypeEnum::INVALID: + break; + case WuQMacroStandardItemTypeEnum::MACRO: + if (macroGroup != NULL) { + if (macro != NULL) { + const int32_t macroIndex = macroGroup->getIndexOfMacro(macro); + deleteValid = true; + moveUpValid = (macroIndex > 0); + moveDownValid = (macroIndex < (macroGroup->getNumberOfMacros() - 1)); + runValid = true; + } + } + break; + case WuQMacroStandardItemTypeEnum::MACRO_COMMAND: + { + if ((macro != NULL) + && (command != NULL)) { + const int32_t commandIndex = macro->getIndexOfMacroCommand(command); + deleteValid = true; + moveUpValid = (commandIndex > 0); + moveDownValid = (commandIndex < (macro->getNumberOfMacroCommands() - 1)); } } - break; - case WuQMacroStandardItemTypeEnum::MACRO_COMMAND: - { - if ((macro != NULL) - && (command != NULL)) { - const int32_t commandIndex = macro->getIndexOfMacroCommand(command); - deleteValid = true; - moveUpValid = (commandIndex > 0); - moveDownValid = (commandIndex < (macro->getNumberOfMacroCommands() - 1)); - } + break; } - break; } m_runMacroToolButton->setEnabled(runValid); + m_stopMacroToolButton->setEnabled(stopValid); m_editingDeleteToolButton->setEnabled(deleteValid); m_editingInsertToolButton->setEnabled(insertValid); m_editingMoveDownToolButton->setEnabled(moveDownValid); @@ -1402,6 +1470,10 @@ WuQMacroDialog::createEditingToolButtonPixmap(const QWidget* widget, /* allow background */ pixmapOptions = 0; break; + case EditButton::STOP: + /* allow background */ + pixmapOptions = 0; + break; } QPixmap pixmap(static_cast(pixmapSize), @@ -1415,27 +1487,42 @@ WuQMacroDialog::createEditingToolButtonPixmap(const QWidget* widget, switch (editButton) { case EditButton::DELETER: + /* + * 'X' symbol + */ pen.setColor(Qt::red); painter->setPen(pen); painter->drawLine(QPointF(-maxValue, maxValue), QPointF(maxValue, -maxValue)); painter->drawLine(QPointF(-maxValue, -maxValue), QPointF(maxValue, maxValue)); break; case EditButton::INSERTER: + /* + * Plus symbol + */ painter->drawLine(QPointF(0, maxValue), QPointF(0, -maxValue)); painter->drawLine(QPointF(-maxValue, 0), QPointF(maxValue, 0)); break; case EditButton::MOVE_DOWN: + /* + * Down arrow + */ painter->drawLine(QPointF(0, maxValue), QPointF(0, -maxValue)); painter->drawLine(QPointF(0, -maxValue), QPointF(arrowTip, -maxValue + arrowTip)); painter->drawLine(QPointF(0, -maxValue), QPointF(-arrowTip, -maxValue + arrowTip)); break; case EditButton::MOVE_UP: + /* + * Up arrow + */ painter->drawLine(QPointF(0, maxValue), QPointF(0, -maxValue)); painter->drawLine(QPointF(0, maxValue), QPointF(arrowTip, maxValue - arrowTip)); painter->drawLine(QPointF(0, maxValue), QPointF(-arrowTip, maxValue - arrowTip)); break; case EditButton::RUN: { + /* + * Triangle + */ painter->setPen(Qt::green); painter->setBrush(Qt::green); const qreal p = pixmapSize / 3; @@ -1447,6 +1534,26 @@ WuQMacroDialog::createEditingToolButtonPixmap(const QWidget* widget, painter->drawConvexPolygon(points, 3); } break; + case EditButton::STOP: + { + /* + * Hexagon + */ + painter->setPen(Qt::red); + painter->setBrush(Qt::red); + const qreal r = pixmapSize / 2; + const qreal r2 = r / 2.0; + const QPointF points[6] = { + QPointF( -r, 0.0), + QPointF(-r2, -r), + QPointF( r2, -r), + QPointF( r, 0.0), + QPointF( r2, r), + QPointF(-r2, r) + }; + painter->drawConvexPolygon(points, 6); + } + break; } return pixmap; @@ -1491,7 +1598,14 @@ CommandParameterWidget::updateContent(WuQMacroCommandParameter* parameter) m_parameter = parameter; const bool validFlag(m_parameter != NULL); if (validFlag) { - m_label->setText(m_parameter->getValue().toString()); + if (parameter->getDataType() == WuQMacroDataValueTypeEnum::BOOLEAN) { + m_label->setText(parameter->getValue().toBool() + ? "On" + : "Off"); + } + else { + m_label->setText(m_parameter->getValue().toString()); + } m_pushButton->setText(m_parameter->getName() + "..."); } m_label->setVisible(validFlag); diff --git a/src/GuiQt/WuQMacroDialog.h b/src/GuiQt/WuQMacroDialog.h index 190695302..fdce985ea 100644 --- a/src/GuiQt/WuQMacroDialog.h +++ b/src/GuiQt/WuQMacroDialog.h @@ -93,6 +93,8 @@ namespace caret { void runOptionLoopCheckBoxClicked(bool); + void runOptionRecordMovieCheckBoxClicked(bool); + void editingMoveUpToolButtonClicked(); void editingMoveDownToolButtonClicked(); @@ -103,6 +105,8 @@ namespace caret { void runMacroToolButtonClicked(); + void stopMacroToolButtonClicked(); + void macroCommandDelaySpinBoxValueChanged(double); void commandParamaterDataChanged(int); @@ -122,7 +126,8 @@ namespace caret { INSERTER, MOVE_DOWN, MOVE_UP, - RUN + RUN, + STOP }; QWidget* createMacroAndCommandSelectionWidget(); @@ -186,6 +191,8 @@ namespace caret { QCheckBox* m_runOptionMoveMouseCheckBox; + QCheckBox* m_recordMovieWhileMacroRunsCheckBox; + QLabel* m_commandTitleLabel; QLabel* m_commandTypeLabel; @@ -202,6 +209,8 @@ namespace caret { QToolButton* m_runMacroToolButton; + QToolButton* m_stopMacroToolButton; + QToolButton* m_editingMoveUpToolButton; QToolButton* m_editingMoveDownToolButton; @@ -210,6 +219,8 @@ namespace caret { QToolButton* m_editingInsertToolButton; + bool m_macroIsRunningFlag = false; + // ADD_NEW_MEMBERS_HERE }; diff --git a/src/GuiQt/WuQMacroExecutor.cxx b/src/GuiQt/WuQMacroExecutor.cxx index a0ad6c38f..b0458a8d8 100644 --- a/src/GuiQt/WuQMacroExecutor.cxx +++ b/src/GuiQt/WuQMacroExecutor.cxx @@ -47,6 +47,8 @@ #include "CaretAssert.h" #include "CaretLogger.h" +#include "MovieRecorder.h" +#include "SessionManager.h" #include "WuQMacro.h" #include "WuQMacroCommand.h" #include "WuQMacroCommandParameter.h" @@ -264,8 +266,8 @@ WuQMacroExecutor::findObjectByName(const QString& objectName) const * * @param macro * Macro that is run - * @param topLevelObject - * Top level object for finding children objects + * @param window + * Widget for parent * @param otherObjectParents * Additional objects that are searched for objects contained * in the macro commands @@ -282,6 +284,47 @@ WuQMacroExecutor::runMacro(const WuQMacro* macro, std::vector& otherObjectParents, const WuQMacroExecutorOptions* executorOptions, QString& errorMessageOut) const +{ + MovieRecorder* movieRecorder = SessionManager::get()->getMovieRecorder(); + const MovieRecorderModeEnum::Enum savedRecordingMode = movieRecorder->getRecordingMode(); + if (executorOptions->isRecordMovieDuringExecution()) { + movieRecorder->setRecordingMode(MovieRecorderModeEnum::AUTOMATIC); + } + + const bool result = runMacroPrivate(macro, + window, + otherObjectParents, + executorOptions, + errorMessageOut); + + movieRecorder->setRecordingMode(savedRecordingMode); + + return result; +} + +/** + * Run the commands in the given macro. + * + * @param macro + * Macro that is run + * @param window + * Widget for parent + * @param otherObjectParents + * Additional objects that are searched for objects contained + * in the macro commands + * @param executorOptions + * Executor options + * @param errorMessageOut + * Output containing any error messages + * @return + * True if the macro completed without errors, else false. + */ +bool +WuQMacroExecutor::runMacroPrivate(const WuQMacro* macro, + QWidget* window, + std::vector& otherObjectParents, + const WuQMacroExecutorOptions* executorOptions, + QString& errorMessageOut) const { CaretAssert(macro); CaretAssert(executorOptions); @@ -341,9 +384,9 @@ WuQMacroExecutor::runMacro(const WuQMacro* macro, bool successFlag(false); switch (mc->getCommandType()) { case WuQMacroCommandTypeEnum::CUSTOM_OPERATION: - successFlag =WuQMacroManager::instance()->executeCustomOperationMacroCommand(window, - mc, - commandErrorMessage); + successFlag = WuQMacroManager::instance()->executeCustomOperationMacroCommand(window, + mc, + commandErrorMessage); break; case WuQMacroCommandTypeEnum::MOUSE: @@ -373,6 +416,11 @@ WuQMacroExecutor::runMacro(const WuQMacro* macro, } QGuiApplication::processEvents(); + if (m_stopFlag) { + errorMessageOut = "Macro stopped at request of user"; + return false; + } + if (mc->getCommandType() != WuQMacroCommandTypeEnum::MOUSE) { if (mc->getDelayInSeconds() > 0.0) { SystemUtilities::sleepSeconds(mc->getDelayInSeconds()); @@ -385,6 +433,15 @@ WuQMacroExecutor::runMacro(const WuQMacro* macro, return (errorMessageOut.isEmpty()); } +/** + * Stop the macro that is executing + */ +void +WuQMacroExecutor::stopMacro() +{ + m_stopFlag = true; +} + /** * Run the commands in the given macro. * diff --git a/src/GuiQt/WuQMacroExecutor.h b/src/GuiQt/WuQMacroExecutor.h index d2c50bab6..df8276bb2 100644 --- a/src/GuiQt/WuQMacroExecutor.h +++ b/src/GuiQt/WuQMacroExecutor.h @@ -56,6 +56,7 @@ namespace caret { const WuQMacroExecutorOptions* executorOptions, QString& errorMessageOut) const; + void stopMacro(); // ADD_NEW_METHODS_HERE @@ -77,6 +78,12 @@ namespace caret { const QRect* objectRect = NULL, const bool hightlightFlag = false) const; + bool runMacroPrivate(const WuQMacro* macro, + QWidget* window, + std::vector& otherObjectParents, + const WuQMacroExecutorOptions* executorOptions, + QString& errorMessageOut) const; + bool runMacroCommand(QWidget* parentWidget, const WuQMacroCommand* macroCommand, QObject* object, @@ -188,6 +195,8 @@ namespace caret { mutable std::vector m_parentObjects; + bool m_stopFlag = false; + // ADD_NEW_MEMBERS_HERE }; diff --git a/src/GuiQt/WuQMacroExecutorOptions.cxx b/src/GuiQt/WuQMacroExecutorOptions.cxx index 09704f5b8..1cc233f32 100644 --- a/src/GuiQt/WuQMacroExecutorOptions.cxx +++ b/src/GuiQt/WuQMacroExecutorOptions.cxx @@ -89,6 +89,7 @@ WuQMacroExecutorOptions::copyHelperWuQMacroExecutorOptions(const WuQMacroExecuto m_loopingFlag = obj.m_loopingFlag; m_stopOnErrorFlag = obj.m_stopOnErrorFlag; m_showMouseMovementFlag = obj.m_showMouseMovementFlag; + m_recordMovieDuringExecutionFlag = obj.m_recordMovieDuringExecutionFlag; } /** @@ -163,3 +164,24 @@ WuQMacroExecutorOptions::setLooping(const bool status) { m_loopingFlag = status; } + +/** + * @return True if record movie while macro executes is on. + */ +bool +WuQMacroExecutorOptions::isRecordMovieDuringExecution() const +{ + return m_recordMovieDuringExecutionFlag; +} + +/** + * Set record movie during macro execution + * + * @param status + * New status + */ +void +WuQMacroExecutorOptions::setRecordMovieDuringExecution(const bool status) +{ + m_recordMovieDuringExecutionFlag = status; +} diff --git a/src/GuiQt/WuQMacroExecutorOptions.h b/src/GuiQt/WuQMacroExecutorOptions.h index 8f18fff6c..b6afb5297 100644 --- a/src/GuiQt/WuQMacroExecutorOptions.h +++ b/src/GuiQt/WuQMacroExecutorOptions.h @@ -54,6 +54,10 @@ namespace caret { void setLooping(const bool status); + bool isRecordMovieDuringExecution() const; + + void setRecordMovieDuringExecution(const bool status); + // ADD_NEW_METHODS_HERE virtual AString toString() const; @@ -67,6 +71,8 @@ namespace caret { bool m_loopingFlag = false; + bool m_recordMovieDuringExecutionFlag = false; + // ADD_NEW_MEMBERS_HERE }; diff --git a/src/GuiQt/WuQMacroManager.cxx b/src/GuiQt/WuQMacroManager.cxx index 5daccd50a..e575a7e81 100644 --- a/src/GuiQt/WuQMacroManager.cxx +++ b/src/GuiQt/WuQMacroManager.cxx @@ -526,8 +526,8 @@ WuQMacroManager::runMacro(QWidget* widget, QString errorMessage; - WuQMacroExecutor executor; - if ( ! executor.runMacro(macro, + m_macroExecutor = new WuQMacroExecutor(); + if ( ! m_macroExecutor->runMacro(macro, widget, m_parentObjects, m_executorOptions.get(), @@ -538,6 +538,27 @@ WuQMacroManager::runMacro(QWidget* widget, QMessageBox::Ok, QMessageBox::NoButton); } + + /* + * Mutex needed so stop() method does not try + * to access an invalid pointer to executor. + */ + QMutexLocker locker(&m_macroExecutorMutex); + delete m_macroExecutor; + m_macroExecutor = NULL; + locker.unlock(); +} + +/** + * If a macro is running, stop it + */ +void +WuQMacroManager::stopMacro() +{ + QMutexLocker locker(&m_macroExecutorMutex); + if (m_macroExecutor != NULL) { + m_macroExecutor->stopMacro(); + } } /** diff --git a/src/GuiQt/WuQMacroManager.h b/src/GuiQt/WuQMacroManager.h index ac5d94668..73012ad85 100644 --- a/src/GuiQt/WuQMacroManager.h +++ b/src/GuiQt/WuQMacroManager.h @@ -25,6 +25,7 @@ #include +#include #include #include "WuQMacroExecutor.h" @@ -43,6 +44,7 @@ namespace caret { class WuQMacroCommandParameter; class WuQMacroCustomOperationManagerInterface; class WuQMacroDialog; + class WuQMacroExecutor; class WuQMacroExecutorOptions; class WuQMacroGroup; class WuQMacroHelperInterface; @@ -118,6 +120,8 @@ namespace caret { void runMacro(QWidget* window, const WuQMacro* macro); + void stopMacro(); + bool runMacroWithShortCutKeyEvent(QWidget* window, const QKeyEvent* keyEvent); @@ -180,6 +184,10 @@ namespace caret { WuQMacroCustomOperationManagerInterface* m_customCommandManager = NULL; + WuQMacroExecutor* m_macroExecutor = NULL; + + QMutex m_macroExecutorMutex; + // ADD_NEW_MEMBERS_HERE }; -- GitLab From eeba66803512ffdddca9a340029efb7e8aaa5649 Mon Sep 17 00:00:00 2001 From: John Harwell Date: Tue, 12 Feb 2019 15:56:20 -0600 Subject: [PATCH 183/427] WB-834 Macros: Updates include capturing entire window (not just graphics) and other improvements. --- src/Brain/CMakeLists.txt | 2 + src/Brain/MovieRecorder.cxx | 46 ++- src/Brain/MovieRecorder.h | 9 + .../MovieRecorderCaptureRegionTypeEnum.cxx | 375 ++++++++++++++++++ .../MovieRecorderCaptureRegionTypeEnum.h | 104 +++++ src/Files/ImageFile.cxx | 93 +++++ src/Files/ImageFile.h | 6 + src/GuiQt/BrainOpenGLWidget.cxx | 60 ++- src/GuiQt/EventMovieDialogUpdate.cxx | 56 --- src/GuiQt/EventMovieDialogUpdate.h | 58 --- src/GuiQt/MovieRecordingDialog.cxx | 52 ++- src/GuiQt/MovieRecordingDialog.h | 4 + src/GuiQt/WbMacroHelper.cxx | 45 +++ src/GuiQt/WbMacroHelper.h | 11 + src/GuiQt/WuQMacroExecutor.cxx | 10 - src/GuiQt/WuQMacroHelperInterface.h | 30 ++ src/GuiQt/WuQMacroManager.cxx | 23 +- 17 files changed, 833 insertions(+), 151 deletions(-) create mode 100644 src/Brain/MovieRecorderCaptureRegionTypeEnum.cxx create mode 100644 src/Brain/MovieRecorderCaptureRegionTypeEnum.h delete mode 100644 src/GuiQt/EventMovieDialogUpdate.cxx delete mode 100644 src/GuiQt/EventMovieDialogUpdate.h diff --git a/src/Brain/CMakeLists.txt b/src/Brain/CMakeLists.txt index 78a8cfb78..d0c66ecb6 100644 --- a/src/Brain/CMakeLists.txt +++ b/src/Brain/CMakeLists.txt @@ -127,6 +127,7 @@ ModelTypeEnum.h ModelVolume.h ModelWholeBrain.h MovieRecorder.h +MovieRecorderCaptureRegionTypeEnum.h MovieRecorderModeEnum.h MovieRecorderVideoDimensionsTypeEnum.h MovieRecorderVideoFormatTypeEnum.h @@ -283,6 +284,7 @@ ModelTypeEnum.cxx ModelVolume.cxx ModelWholeBrain.cxx MovieRecorder.cxx +MovieRecorderCaptureRegionTypeEnum.cxx MovieRecorderModeEnum.cxx MovieRecorderVideoDimensionsTypeEnum.cxx MovieRecorderVideoFormatTypeEnum.cxx diff --git a/src/Brain/MovieRecorder.cxx b/src/Brain/MovieRecorder.cxx index 8c20e3bcb..3fdd9c637 100644 --- a/src/Brain/MovieRecorder.cxx +++ b/src/Brain/MovieRecorder.cxx @@ -85,7 +85,8 @@ MovieRecorder::addImageToMovie(const QImage* image) /* * First image starts at 1 and is padded with zeros on the left */ - const QString imageIndex = QString::number(getNumberOfFrames() + 1).rightJustified(m_tempImageSequenceNumberOfDigits, '0'); + const int32_t imageIndexInt = getNumberOfFrames() + 1; + const QString imageIndex = QString::number(imageIndexInt).rightJustified(m_tempImageSequenceNumberOfDigits, '0'); const QString imageFileName(m_temporaryImagesDirectory + "/" + m_tempImageFileNamePrefix @@ -93,7 +94,23 @@ MovieRecorder::addImageToMovie(const QImage* image) + m_tempImageFileNameSuffix); if (image->save(imageFileName)) { - m_imageFileNames.push_back(imageFileName); + if (m_imageFileNames.empty()) { + m_firstImageWidth = image->width(); + m_firstImageHeight = image->height(); + } + + if ((image->width() == m_firstImageWidth) + && (image->height() == m_firstImageHeight)) { + m_imageFileNames.push_back(imageFileName); + } + else { + CaretLogSevere("Attempting to create movie with images that are different sizes. " + "First image width=" + QString::number(m_firstImageWidth) + + ", height=" + QString::number(m_firstImageHeight) + + "Image number=" + QString::number(imageIndexInt) + + ", width=" + QString::number(image->width()) + + ", height=" + QString::number(image->height())); + } } else { CaretLogSevere("Saving temporary image failed: " @@ -140,11 +157,13 @@ MovieRecorder::reset() while (iter.hasNext()) { QFile file(iter.next().absoluteFilePath()); if (file.exists()) { - const bool successFlag = file.remove(); + file.remove(); } } m_imageFileNames.clear(); + m_firstImageWidth = -1; + m_firstImageHeight = -1; } @@ -294,6 +313,27 @@ MovieRecorder::setVideoFormatType(const MovieRecorderVideoFormatTypeEnum::Enum f m_formatType = formatType; } +/** + * @return The capture region type + */ +MovieRecorderCaptureRegionTypeEnum::Enum +MovieRecorder::getCaptureRegionType() const +{ + return m_captureRegionType; +} + +/** + * Set the capture region type + * + * @param captureRegionType + * New capture region type + */ +void +MovieRecorder::setCaptureRegionType(const MovieRecorderCaptureRegionTypeEnum::Enum captureRegionType) +{ + m_captureRegionType = captureRegionType; +} + /** * @return Name of movie file */ diff --git a/src/Brain/MovieRecorder.h b/src/Brain/MovieRecorder.h index e00a47368..4287b3218 100644 --- a/src/Brain/MovieRecorder.h +++ b/src/Brain/MovieRecorder.h @@ -26,6 +26,7 @@ #include #include "CaretObject.h" +#include "MovieRecorderCaptureRegionTypeEnum.h" #include "MovieRecorderModeEnum.h" #include "MovieRecorderVideoDimensionsTypeEnum.h" #include "MovieRecorderVideoFormatTypeEnum.h" @@ -76,6 +77,10 @@ namespace caret { void setVideoFormatType(const MovieRecorderVideoFormatTypeEnum::Enum formatType); + MovieRecorderCaptureRegionTypeEnum::Enum getCaptureRegionType() const; + + void setCaptureRegionType(const MovieRecorderCaptureRegionTypeEnum::Enum captureRegionType); + AString getMovieFileName() const; void setMovieFileName(const AString& filename); @@ -124,6 +129,8 @@ namespace caret { MovieRecorderVideoFormatTypeEnum::Enum m_formatType = MovieRecorderVideoFormatTypeEnum::MPEG_4; + MovieRecorderCaptureRegionTypeEnum::Enum m_captureRegionType = MovieRecorderCaptureRegionTypeEnum::GRAPHICS; + int32_t m_windowIndex = 0; int32_t m_customWidth = 640; @@ -150,6 +157,8 @@ namespace caret { const int32_t m_tempImageSequenceNumberOfDigits = 6; + int32_t m_firstImageWidth = -1; + int32_t m_firstImageHeight = -1; }; #ifdef __MOVIE_RECORDER_DECLARE__ diff --git a/src/Brain/MovieRecorderCaptureRegionTypeEnum.cxx b/src/Brain/MovieRecorderCaptureRegionTypeEnum.cxx new file mode 100644 index 000000000..50b1f4d1d --- /dev/null +++ b/src/Brain/MovieRecorderCaptureRegionTypeEnum.cxx @@ -0,0 +1,375 @@ + +/*LICENSE_START*/ +/* + * Copyright (C) 2019 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + +#include +#define __MOVIE_RECORDER_CAPTURE_REGION_TYPE_ENUM_DECLARE__ +#include "MovieRecorderCaptureRegionTypeEnum.h" +#undef __MOVIE_RECORDER_CAPTURE_REGION_TYPE_ENUM_DECLARE__ + +#include "CaretAssert.h" + +using namespace caret; + + +/** + * \class caret::MovieRecorderCaptureRegionTypeEnum + * \brief + * + * + * + * Using this enumerated type in the GUI with an EnumComboBoxTemplate + * + * Header File (.h) + * Forward declare the data type: + * class EnumComboBoxTemplate; + * + * Declare the member: + * EnumComboBoxTemplate* m_movieRecorderCaptureRegionTypeEnumComboBox; + * + * Declare a slot that is called when user changes selection + * private slots: + * void movieRecorderCaptureRegionTypeEnumComboBoxItemActivated(); + * + * Implementation File (.cxx) + * Include the header files + * #include "EnumComboBoxTemplate.h" + * #include "MovieRecorderCaptureRegionTypeEnum.h" + * + * Instatiate: + * m_movieRecorderCaptureRegionTypeEnumComboBox = new EnumComboBoxTemplate(this); + * m_movieRecorderCaptureRegionTypeEnumComboBox->setup(); + * + * Get notified when the user changes the selection: + * QObject::connect(m_movieRecorderCaptureRegionTypeEnumComboBox, SIGNAL(itemActivated()), + * this, SLOT(movieRecorderCaptureRegionTypeEnumComboBoxItemActivated())); + * + * Update the selection: + * m_movieRecorderCaptureRegionTypeEnumComboBox->setSelectedItem(NEW_VALUE); + * + * Read the selection: + * const MovieRecorderCaptureRegionTypeEnum::Enum VARIABLE = m_movieRecorderCaptureRegionTypeEnumComboBox->getSelectedItem(); + * + */ + +/** + * Constructor. + * + * @param enumValue + * An enumerated value. + * @param name + * Name of enumerated value. + * + * @param guiName + * User-friendly name for use in user-interface. + */ +MovieRecorderCaptureRegionTypeEnum::MovieRecorderCaptureRegionTypeEnum(const Enum enumValue, + const AString& name, + const AString& guiName) +{ + this->enumValue = enumValue; + this->integerCode = integerCodeCounter++; + this->name = name; + this->guiName = guiName; +} + +/** + * Destructor. + */ +MovieRecorderCaptureRegionTypeEnum::~MovieRecorderCaptureRegionTypeEnum() +{ +} + +/** + * Initialize the enumerated metadata. + */ +void +MovieRecorderCaptureRegionTypeEnum::initialize() +{ + if (initializedFlag) { + return; + } + initializedFlag = true; + + enumData.push_back(MovieRecorderCaptureRegionTypeEnum(GRAPHICS, + "GRAPHICS", + "Graphics")); + + enumData.push_back(MovieRecorderCaptureRegionTypeEnum(WINDOW, + "WINDOW", + "Window")); + +} + +/** + * Find the data for and enumerated value. + * @param enumValue + * The enumerated value. + * @return Pointer to data for this enumerated type + * or NULL if no data for type or if type is invalid. + */ +const MovieRecorderCaptureRegionTypeEnum* +MovieRecorderCaptureRegionTypeEnum::findData(const Enum enumValue) +{ + if (initializedFlag == false) initialize(); + + size_t num = enumData.size(); + for (size_t i = 0; i < num; i++) { + const MovieRecorderCaptureRegionTypeEnum* d = &enumData[i]; + if (d->enumValue == enumValue) { + return d; + } + } + + return NULL; +} + +/** + * Get a string representation of the enumerated type. + * @param enumValue + * Enumerated value. + * @return + * String representing enumerated value. + */ +AString +MovieRecorderCaptureRegionTypeEnum::toName(Enum enumValue) { + if (initializedFlag == false) initialize(); + + const MovieRecorderCaptureRegionTypeEnum* enumInstance = findData(enumValue); + return enumInstance->name; +} + +/** + * Get an enumerated value corresponding to its name. + * @param name + * Name of enumerated value. + * @param isValidOut + * If not NULL, it is set indicating that a + * enum value exists for the input name. + * @return + * Enumerated value. + */ +MovieRecorderCaptureRegionTypeEnum::Enum +MovieRecorderCaptureRegionTypeEnum::fromName(const AString& name, bool* isValidOut) +{ + if (initializedFlag == false) initialize(); + + bool validFlag = false; + Enum enumValue = MovieRecorderCaptureRegionTypeEnum::enumData[0].enumValue; + + for (std::vector::iterator iter = enumData.begin(); + iter != enumData.end(); + iter++) { + const MovieRecorderCaptureRegionTypeEnum& d = *iter; + if (d.name == name) { + enumValue = d.enumValue; + validFlag = true; + break; + } + } + + if (isValidOut != 0) { + *isValidOut = validFlag; + } + else if (validFlag == false) { + CaretAssertMessage(0, AString("Name " + name + "failed to match enumerated value for type MovieRecorderCaptureRegionTypeEnum")); + } + return enumValue; +} + +/** + * Get a GUI string representation of the enumerated type. + * @param enumValue + * Enumerated value. + * @return + * String representing enumerated value. + */ +AString +MovieRecorderCaptureRegionTypeEnum::toGuiName(Enum enumValue) { + if (initializedFlag == false) initialize(); + + const MovieRecorderCaptureRegionTypeEnum* enumInstance = findData(enumValue); + return enumInstance->guiName; +} + +/** + * Get an enumerated value corresponding to its GUI name. + * @param s + * Name of enumerated value. + * @param isValidOut + * If not NULL, it is set indicating that a + * enum value exists for the input name. + * @return + * Enumerated value. + */ +MovieRecorderCaptureRegionTypeEnum::Enum +MovieRecorderCaptureRegionTypeEnum::fromGuiName(const AString& guiName, bool* isValidOut) +{ + if (initializedFlag == false) initialize(); + + bool validFlag = false; + Enum enumValue = MovieRecorderCaptureRegionTypeEnum::enumData[0].enumValue; + + for (std::vector::iterator iter = enumData.begin(); + iter != enumData.end(); + iter++) { + const MovieRecorderCaptureRegionTypeEnum& d = *iter; + if (d.guiName == guiName) { + enumValue = d.enumValue; + validFlag = true; + break; + } + } + + if (isValidOut != 0) { + *isValidOut = validFlag; + } + else if (validFlag == false) { + CaretAssertMessage(0, AString("guiName " + guiName + "failed to match enumerated value for type MovieRecorderCaptureRegionTypeEnum")); + } + return enumValue; +} + +/** + * Get the integer code for a data type. + * + * @return + * Integer code for data type. + */ +int32_t +MovieRecorderCaptureRegionTypeEnum::toIntegerCode(Enum enumValue) +{ + if (initializedFlag == false) initialize(); + const MovieRecorderCaptureRegionTypeEnum* enumInstance = findData(enumValue); + return enumInstance->integerCode; +} + +/** + * Find the data type corresponding to an integer code. + * + * @param integerCode + * Integer code for enum. + * @param isValidOut + * If not NULL, on exit isValidOut will indicate if + * integer code is valid. + * @return + * Enum for integer code. + */ +MovieRecorderCaptureRegionTypeEnum::Enum +MovieRecorderCaptureRegionTypeEnum::fromIntegerCode(const int32_t integerCode, bool* isValidOut) +{ + if (initializedFlag == false) initialize(); + + bool validFlag = false; + Enum enumValue = MovieRecorderCaptureRegionTypeEnum::enumData[0].enumValue; + + for (std::vector::iterator iter = enumData.begin(); + iter != enumData.end(); + iter++) { + const MovieRecorderCaptureRegionTypeEnum& enumInstance = *iter; + if (enumInstance.integerCode == integerCode) { + enumValue = enumInstance.enumValue; + validFlag = true; + break; + } + } + + if (isValidOut != 0) { + *isValidOut = validFlag; + } + else if (validFlag == false) { + CaretAssertMessage(0, AString("Integer code " + AString::number(integerCode) + "failed to match enumerated value for type MovieRecorderCaptureRegionTypeEnum")); + } + return enumValue; +} + +/** + * Get all of the enumerated type values. The values can be used + * as parameters to toXXX() methods to get associated metadata. + * + * @param allEnums + * A vector that is OUTPUT containing all of the enumerated values. + */ +void +MovieRecorderCaptureRegionTypeEnum::getAllEnums(std::vector& allEnums) +{ + if (initializedFlag == false) initialize(); + + allEnums.clear(); + + for (std::vector::iterator iter = enumData.begin(); + iter != enumData.end(); + iter++) { + allEnums.push_back(iter->enumValue); + } +} + +/** + * Get all of the names of the enumerated type values. + * + * @param allNames + * A vector that is OUTPUT containing all of the names of the enumerated values. + * @param isSorted + * If true, the names are sorted in alphabetical order. + */ +void +MovieRecorderCaptureRegionTypeEnum::getAllNames(std::vector& allNames, const bool isSorted) +{ + if (initializedFlag == false) initialize(); + + allNames.clear(); + + for (std::vector::iterator iter = enumData.begin(); + iter != enumData.end(); + iter++) { + allNames.push_back(MovieRecorderCaptureRegionTypeEnum::toName(iter->enumValue)); + } + + if (isSorted) { + std::sort(allNames.begin(), allNames.end()); + } +} + +/** + * Get all of the GUI names of the enumerated type values. + * + * @param allNames + * A vector that is OUTPUT containing all of the GUI names of the enumerated values. + * @param isSorted + * If true, the names are sorted in alphabetical order. + */ +void +MovieRecorderCaptureRegionTypeEnum::getAllGuiNames(std::vector& allGuiNames, const bool isSorted) +{ + if (initializedFlag == false) initialize(); + + allGuiNames.clear(); + + for (std::vector::iterator iter = enumData.begin(); + iter != enumData.end(); + iter++) { + allGuiNames.push_back(MovieRecorderCaptureRegionTypeEnum::toGuiName(iter->enumValue)); + } + + if (isSorted) { + std::sort(allGuiNames.begin(), allGuiNames.end()); + } +} + diff --git a/src/Brain/MovieRecorderCaptureRegionTypeEnum.h b/src/Brain/MovieRecorderCaptureRegionTypeEnum.h new file mode 100644 index 000000000..7b479bc28 --- /dev/null +++ b/src/Brain/MovieRecorderCaptureRegionTypeEnum.h @@ -0,0 +1,104 @@ +#ifndef __MOVIE_RECORDER_CAPTURE_REGION_TYPE_ENUM_H__ +#define __MOVIE_RECORDER_CAPTURE_REGION_TYPE_ENUM_H__ + +/*LICENSE_START*/ +/* + * Copyright (C) 2019 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + + +#include +#include +#include "AString.h" + +namespace caret { + +class MovieRecorderCaptureRegionTypeEnum { + +public: + /** + * Enumerated values. + */ + enum Enum { + /** */ + GRAPHICS, + /** */ + WINDOW + }; + + + ~MovieRecorderCaptureRegionTypeEnum(); + + static AString toName(Enum enumValue); + + static Enum fromName(const AString& name, bool* isValidOut); + + static AString toGuiName(Enum enumValue); + + static Enum fromGuiName(const AString& guiName, bool* isValidOut); + + static int32_t toIntegerCode(Enum enumValue); + + static Enum fromIntegerCode(const int32_t integerCode, bool* isValidOut); + + static void getAllEnums(std::vector& allEnums); + + static void getAllNames(std::vector& allNames, const bool isSorted); + + static void getAllGuiNames(std::vector& allGuiNames, const bool isSorted); + +private: + MovieRecorderCaptureRegionTypeEnum(const Enum enumValue, + const AString& name, + const AString& guiName); + + static const MovieRecorderCaptureRegionTypeEnum* findData(const Enum enumValue); + + /** Holds all instance of enum values and associated metadata */ + static std::vector enumData; + + /** Initialize instances that contain the enum values and metadata */ + static void initialize(); + + /** Indicates instance of enum values and metadata have been initialized */ + static bool initializedFlag; + + /** Auto generated integer codes */ + static int32_t integerCodeCounter; + + /** The enumerated type value for an instance */ + Enum enumValue; + + /** The integer code associated with an enumerated value */ + int32_t integerCode; + + /** The name, a text string that is identical to the enumerated value */ + AString name; + + /** A user-friendly name that is displayed in the GUI */ + AString guiName; +}; + +#ifdef __MOVIE_RECORDER_CAPTURE_REGION_TYPE_ENUM_DECLARE__ +std::vector MovieRecorderCaptureRegionTypeEnum::enumData; +bool MovieRecorderCaptureRegionTypeEnum::initializedFlag = false; +int32_t MovieRecorderCaptureRegionTypeEnum::integerCodeCounter = 0; +#endif // __MOVIE_RECORDER_CAPTURE_REGION_TYPE_ENUM_DECLARE__ + +} // namespace +#endif //__MOVIE_RECORDER_CAPTURE_REGION_TYPE_ENUM_H__ diff --git a/src/Files/ImageFile.cxx b/src/Files/ImageFile.cxx index 45261135a..3e4932082 100644 --- a/src/Files/ImageFile.cxx +++ b/src/Files/ImageFile.cxx @@ -775,6 +775,99 @@ ImageFile::insertImage(const QImage& insertThisImage, } } +/** + * Scale the given image to the given width and height while preserving + * the aspect ratio. If the + * + * @param image + * The image + * @param width + * Width of the image + * @param height + * Height of the image + * @param fillColor + * If not NULL, padded region is this color + * @return + * Image that will be the requested width and height or + * a null image (.isNull()) if error. + */ +QImage +ImageFile::scaleToSizeWithPadding(const QImage& image, + const int width, + const int height, + const QColor* fillColor) +{ + /* + * Invalid image tests + */ + if (image.isNull()) { + return image; + } + if ((image.width() <= 0) + || (image.height() <= 0)) { + return QImage(); + } + + /* + * Nothing to do if image is correct size + */ + if ((image.width() == width) + && (image.height() == height)) { + return image; + } + + const QImage scaledImage = image.scaled(width, + height, + Qt::KeepAspectRatio, + Qt::SmoothTransformation); + const int scaledWidth = scaledImage.width(); + const int scaledHeight = scaledImage.height(); + if ((scaledWidth == width) + && (scaledHeight == height)) { + return scaledImage; + } + else if (scaledWidth > width) { + CaretLogSevere("Image scale width was made larger=" + + QString::number(scaledWidth) + + " than requested=" + + QString::number(width)); + return QImage(); + } + else if (scaledHeight > height) { + CaretLogSevere("Image scale height was made larger=" + + QString::number(scaledHeight) + + " than requested=" + + QString::number(height)); + return QImage(); + } + + QImage outputImage(width, + height, + image.format()); + if (fillColor != NULL) { + outputImage.fill(*fillColor); + } + else { + outputImage.fill(Qt::black); + } + + const int insertX = (width - scaledWidth) / 2; + const int insertY = (height - scaledHeight) / 2; + + try { + ImageFile::insertImage(scaledImage, + outputImage, + insertX, + insertY); + } + catch (const DataFileException& dfe) { + CaretLogSevere(dfe.whatString()); + outputImage = QImage(); + } + + return outputImage; +} + /** * Compare a file for unit testing (tolerance ignored). * diff --git a/src/Files/ImageFile.h b/src/Files/ImageFile.h index fd64cf4ca..0a833d541 100644 --- a/src/Files/ImageFile.h +++ b/src/Files/ImageFile.h @@ -24,6 +24,7 @@ #include "CaretDataFile.h" #include "CaretPointer.h" +class QColor; class QImage; namespace caret { @@ -196,6 +197,11 @@ public: static void getImageFileFilters(std::vector& imageFileFilters, AString& defaultFilter); + static QImage scaleToSizeWithPadding(const QImage& image, + const int width, + const int height, + const QColor* fillColor = NULL); + VolumeFile* convertToVolumeFile(const CONVERT_TO_VOLUME_COLOR_MODE colorMode, AString& errorMessageOut) const; diff --git a/src/GuiQt/BrainOpenGLWidget.cxx b/src/GuiQt/BrainOpenGLWidget.cxx index 78d3d0d69..5bb424311 100644 --- a/src/GuiQt/BrainOpenGLWidget.cxx +++ b/src/GuiQt/BrainOpenGLWidget.cxx @@ -64,6 +64,7 @@ #include "EventUserInterfaceUpdate.h" #include "FtglFontTextRenderer.h" #include "GuiManager.h" +#include "ImageFile.h" #include "KeyEvent.h" #include "MathFunctions.h" #include "Matrix4x4.h" @@ -1734,20 +1735,55 @@ BrainOpenGLWidget::receiveEvent(Event* event) int imageHeight(0); movieRecorder->getVideoDimensions(imageWidth, imageHeight); - EventImageCapture captureEvent(this->windowIndex, - captureOffsetX, - captureOffsetY, - captureWidth, - captureHeight, - imageWidth, - imageHeight); - captureImage(&captureEvent); - if (captureEvent.isError()) { - CaretLogSevere("Failed to capture image for movie recording"); + QImage image; + bool imageValid(false); + + switch (movieRecorder->getCaptureRegionType()) { + case MovieRecorderCaptureRegionTypeEnum::GRAPHICS: + { + EventImageCapture captureEvent(this->windowIndex, + captureOffsetX, + captureOffsetY, + captureWidth, + captureHeight, + imageWidth, + imageHeight); + captureImage(&captureEvent); + + if (captureEvent.isError()) { + CaretLogSevere("Failed to capture image of graphics for movie recording"); + } + else { + image = captureEvent.getImage(); + imageValid = true; + } + } + break; + case MovieRecorderCaptureRegionTypeEnum::WINDOW: + { + BrainBrowserWindow* bbw = GuiManager::get()->getBrowserWindowByWindowIndex(this->windowIndex); + QRect rect(0, 0, + bbw->width(), bbw->height()); + QPixmap pm = bbw->grab(rect); + if ((pm.width() > 0) + && (pm.height() > 0)) { + image = pm.toImage(); + imageValid = true; + + QImage scaledImage = ImageFile::scaleToSizeWithPadding(image, imageWidth, imageHeight); + if ( ! scaledImage.isNull()) { + image = scaledImage; + } + } + else { + CaretLogSevere("Failed to capture image of window"); + } + } + break; } - else { - QImage image = captureEvent.getImage(); + + if (imageValid) { if (captureAutomaticImageForMovieFlag) { movieRecorder->addImageToMovie(&image); } diff --git a/src/GuiQt/EventMovieDialogUpdate.cxx b/src/GuiQt/EventMovieDialogUpdate.cxx deleted file mode 100644 index 5d423b074..000000000 --- a/src/GuiQt/EventMovieDialogUpdate.cxx +++ /dev/null @@ -1,56 +0,0 @@ - -/*LICENSE_START*/ -/* - * Copyright (C) 2019 Washington University School of Medicine - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ -/*LICENSE_END*/ - -#define __EVENT_MOVIE_DIALOG_UPDATE_DECLARE__ -#include "EventMovieDialogUpdate.h" -#undef __EVENT_MOVIE_DIALOG_UPDATE_DECLARE__ - -#include "CaretAssert.h" -#include "EventTypeEnum.h" - -using namespace caret; - - - -/** - * \class caret::EventMovieDialogUpdate - * \brief - * \ingroup GuiQt - * - * - */ - -/** - * Constructor. - */ -EventMovieDialogUpdate::EventMovieDialogUpdate() -: Event(EventTypeEnum::EVENT_MOVIE_DIALOG_UPDATE) -{ - -} - -/** - * Destructor. - */ -EventMovieDialogUpdate::~EventMovieDialogUpdate() -{ -} - diff --git a/src/GuiQt/EventMovieDialogUpdate.h b/src/GuiQt/EventMovieDialogUpdate.h deleted file mode 100644 index 16dd39393..000000000 --- a/src/GuiQt/EventMovieDialogUpdate.h +++ /dev/null @@ -1,58 +0,0 @@ -#ifndef __EVENT_MOVIE_DIALOG_UPDATE_H__ -#define __EVENT_MOVIE_DIALOG_UPDATE_H__ - -/*LICENSE_START*/ -/* - * Copyright (C) 2019 Washington University School of Medicine - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ -/*LICENSE_END*/ - - - -#include - -#include "Event.h" - - - -namespace caret { - - class EventMovieDialogUpdate : public Event { - - public: - EventMovieDialogUpdate(); - - virtual ~EventMovieDialogUpdate(); - - EventMovieDialogUpdate(const EventMovieDialogUpdate&) = delete; - - EventMovieDialogUpdate& operator=(const EventMovieDialogUpdate&) = delete; - - - // ADD_NEW_METHODS_HERE - - private: - // ADD_NEW_MEMBERS_HERE - - }; - -#ifdef __EVENT_MOVIE_DIALOG_UPDATE_DECLARE__ - // -#endif // __EVENT_MOVIE_DIALOG_UPDATE_DECLARE__ - -} // namespace -#endif //__EVENT_MOVIE_DIALOG_UPDATE_H__ diff --git a/src/GuiQt/MovieRecordingDialog.cxx b/src/GuiQt/MovieRecordingDialog.cxx index 1739e310e..c39150c90 100644 --- a/src/GuiQt/MovieRecordingDialog.cxx +++ b/src/GuiQt/MovieRecordingDialog.cxx @@ -128,6 +128,9 @@ MovieRecordingDialog::updateDialog() const MovieRecorderVideoDimensionsTypeEnum::Enum dimType = movieRecorder->getVideoDimensionsType(); m_movieRecorderVideoDimensionsTypeEnumComboBox->setSelectedItem(dimType); + const MovieRecorderCaptureRegionTypeEnum::Enum captureType = movieRecorder->getCaptureRegionType(); + m_movieRecorderCaptureRegionTypeComboBox->setSelectedItem(captureType); + updateManualRecordingOptions(); updateCustomWidthHeightSpinBoxes(); updateFileNameLabel(); @@ -211,7 +214,13 @@ MovieRecordingDialog::updateFileNameLabel() void MovieRecordingDialog::updateFrameCountLabel() { - m_frameCountNumberLabel->setNum(SessionManager::get()->getMovieRecorder()->getNumberOfFrames()); + const int32_t numberOfFrames = SessionManager::get()->getMovieRecorder()->getNumberOfFrames(); + m_frameCountNumberLabel->setNum(numberOfFrames); + + /* + * Do not allow user to change image size once an image has been captured + */ + m_movieRecorderVideoDimensionsTypeEnumComboBox->getWidget()->setEnabled(numberOfFrames <= 0); } /** @@ -248,6 +257,16 @@ MovieRecordingDialog::movieRecorderVideoFormatTypeEnumComboBoxItemActivated() updateFileNameLabel(); } +/** + * Called when capture region type is changed + */ +void +MovieRecordingDialog::movieRecorderCaptureRegionTypeComboBoxActivated() +{ + const MovieRecorderCaptureRegionTypeEnum::Enum regionType = m_movieRecorderCaptureRegionTypeComboBox->getSelectedItem(); + SessionManager::get()->getMovieRecorder()->setCaptureRegionType(regionType); +} + /** * Set the selected browser window to the browser window with the * given index. @@ -435,16 +454,32 @@ QWidget* MovieRecordingDialog::createMainWidget() { QLabel* windowLabel = new QLabel("Record from Window:"); - m_windowComboBox = new BrainBrowserWindowComboBox(BrainBrowserWindowComboBox::STYLE_NAME_AND_NUMBER, + m_windowComboBox = new BrainBrowserWindowComboBox(BrainBrowserWindowComboBox::STYLE_NUMBER, this); m_windowComboBox->setToolTip("Sets window that is recorded"); QObject::connect(m_windowComboBox, &BrainBrowserWindowComboBox::browserWindowIndexSelected, this, &MovieRecordingDialog::windowIndexSelected); - QHBoxLayout* windowLayout = new QHBoxLayout(); - windowLayout->addWidget(windowLabel); - windowLayout->addWidget(m_windowComboBox->getWidget()); - windowLayout->addStretch(); + QLabel* regionLabel = new QLabel("Record Region:"); + m_movieRecorderCaptureRegionTypeComboBox = new EnumComboBoxTemplate(this); + m_movieRecorderCaptureRegionTypeComboBox->getWidget()->setToolTip("Choose region that is capture for movie"); + m_movieRecorderCaptureRegionTypeComboBox->setup(); + QObject::connect(m_movieRecorderCaptureRegionTypeComboBox, SIGNAL(itemActivated()), + this, SLOT(movieRecorderCaptureRegionTypeComboBoxActivated())); + + QGroupBox* sourceGroupBox = new QGroupBox("Source"); + QGridLayout* sourceLayout = new QGridLayout(sourceGroupBox); + sourceLayout->setColumnStretch(0, 0); + sourceLayout->setColumnStretch(0, 1); + sourceLayout->setColumnStretch(2, 100); + int sourceRow(0); + sourceLayout->addWidget(windowLabel, sourceRow, 0); + sourceLayout->addWidget(m_windowComboBox->getWidget(), sourceRow, 1); + sourceRow++; + sourceLayout->addWidget(regionLabel, sourceRow, 0); + sourceLayout->addWidget(m_movieRecorderCaptureRegionTypeComboBox->getWidget(), sourceRow, 1); + sourceRow++; + m_recordingAutomaticRadioButton = new QRadioButton(MovieRecorderModeEnum::toGuiName(MovieRecorderModeEnum::AUTOMATIC)); m_recordingAutomaticRadioButton->setToolTip("When selected, images are recorded as graphics updated"); @@ -471,6 +506,8 @@ MovieRecordingDialog::createMainWidget() m_manualCaptureSecondsSpinBox->setSingleStep(1); m_manualCaptureSecondsSpinBox->setSizePolicy(QSizePolicy::Fixed, m_manualCaptureSecondsSpinBox->sizePolicy().verticalPolicy()); + QObject::connect(m_manualCaptureSecondsSpinBox, QOverload::of(&QSpinBox::valueChanged), + this, &MovieRecordingDialog::manualCaptureSecondsSpinBoxValueChanged); QLabel* captureSecondsLabel = new QLabel("seconds"); @@ -515,6 +552,7 @@ MovieRecordingDialog::createMainWidget() movieLayout->setColumnStretch(0, 0); movieLayout->setColumnStretch(1, 0); movieLayout->setColumnStretch(2, 0); + movieLayout->setColumnStretch(3, 100); int32_t movieRow(0); movieLayout->addWidget(m_filenamePushButton, movieRow, 0); movieLayout->addWidget(m_filenameLabel, movieRow, 1, 1, 2, Qt::AlignLeft); @@ -529,7 +567,7 @@ MovieRecordingDialog::createMainWidget() QWidget* widget = new QWidget(); QVBoxLayout* layout = new QVBoxLayout(widget); - layout->addLayout(windowLayout); + layout->addWidget(sourceGroupBox); layout->addWidget(modeGroupBox); layout->addWidget(movieFileGroupBox); layout->addStretch(); diff --git a/src/GuiQt/MovieRecordingDialog.h b/src/GuiQt/MovieRecordingDialog.h index 9942e83e8..7ef287fec 100644 --- a/src/GuiQt/MovieRecordingDialog.h +++ b/src/GuiQt/MovieRecordingDialog.h @@ -68,6 +68,8 @@ namespace caret { void movieRecorderVideoFormatTypeEnumComboBoxItemActivated(); + void movieRecorderCaptureRegionTypeComboBoxActivated(); + void recordingModeRadioButtonClicked(QAbstractButton* button); void customWidthSpinBoxValueChanged(int width); @@ -131,6 +133,8 @@ namespace caret { BrainBrowserWindowComboBox* m_windowComboBox; + EnumComboBoxTemplate* m_movieRecorderCaptureRegionTypeComboBox; + // ADD_NEW_MEMBERS_HERE }; diff --git a/src/GuiQt/WbMacroHelper.cxx b/src/GuiQt/WbMacroHelper.cxx index 891028ad2..cac0bc896 100644 --- a/src/GuiQt/WbMacroHelper.cxx +++ b/src/GuiQt/WbMacroHelper.cxx @@ -32,6 +32,7 @@ #include "EventSceneActive.h" #include "EventUserInterfaceUpdate.h" #include "GuiManager.h" +#include "MovieRecorder.h" #include "Scene.h" #include "SceneFile.h" #include "SceneInfo.h" @@ -40,6 +41,7 @@ #include "WbMacroCustomDataTypeEnum.h" #include "WuQMacroCommand.h" #include "WuQMacroCommandParameter.h" +#include "WuQMacroExecutorOptions.h" #include "WuQMacroGroup.h" #include "WuQMessageBox.h" @@ -185,3 +187,46 @@ WbMacroHelper::getMainWindowWithIdentifier(const QString& identifier) return window; } + +/** + * Called just before executing the macro + * + * @param macro + * Macro that is run + * @param window + * Widget for parent + * @param executorOptions + * Executor options + */ +void +WbMacroHelper::macroExecutionStarting(const WuQMacro* /*macro*/, + QWidget* /*window*/, + const WuQMacroExecutorOptions* executorOptions) +{ + MovieRecorder* movieRecorder = SessionManager::get()->getMovieRecorder(); + m_savedRecordingMode = movieRecorder->getRecordingMode(); + if (executorOptions->isRecordMovieDuringExecution()) { + movieRecorder->setRecordingMode(MovieRecorderModeEnum::AUTOMATIC); + } + EventManager::get()->sendSimpleEvent(EventTypeEnum::EVENT_MOVIE_RECORDING_DIALOG_UPDATE); +} + +/** + * Called just after executing the macro + * + * @param macro + * Macro that is run + * @param window + * Widget for parent + * @param executorOptions + * Executor options + */ +void +WbMacroHelper::macroExecutionEnding(const WuQMacro* /*macro*/, + QWidget* /*window*/, + const WuQMacroExecutorOptions* /*executorOptions*/) +{ + MovieRecorder* movieRecorder = SessionManager::get()->getMovieRecorder(); + movieRecorder->setRecordingMode(m_savedRecordingMode); + EventManager::get()->sendSimpleEvent(EventTypeEnum::EVENT_MOVIE_RECORDING_DIALOG_UPDATE); +} diff --git a/src/GuiQt/WbMacroHelper.h b/src/GuiQt/WbMacroHelper.h index a9aa4d8b9..9bf4c8f9e 100644 --- a/src/GuiQt/WbMacroHelper.h +++ b/src/GuiQt/WbMacroHelper.h @@ -25,6 +25,7 @@ #include #include "EventListenerInterface.h" +#include "MovieRecorderModeEnum.h" #include "WuQMacroHelperInterface.h" class QWidget; @@ -55,9 +56,19 @@ namespace caret { virtual QMainWindow* getMainWindowWithIdentifier(const QString& identifier) override; + virtual void macroExecutionStarting(const WuQMacro* macro, + QWidget* window, + const WuQMacroExecutorOptions* executorOptions) override; + + virtual void macroExecutionEnding(const WuQMacro* macro, + QWidget* window, + const WuQMacroExecutorOptions* executorOptions) override; + // ADD_NEW_METHODS_HERE private: + MovieRecorderModeEnum::Enum m_savedRecordingMode = MovieRecorderModeEnum::MANUAL; + // ADD_NEW_MEMBERS_HERE }; diff --git a/src/GuiQt/WuQMacroExecutor.cxx b/src/GuiQt/WuQMacroExecutor.cxx index b0458a8d8..2c7770a7b 100644 --- a/src/GuiQt/WuQMacroExecutor.cxx +++ b/src/GuiQt/WuQMacroExecutor.cxx @@ -47,8 +47,6 @@ #include "CaretAssert.h" #include "CaretLogger.h" -#include "MovieRecorder.h" -#include "SessionManager.h" #include "WuQMacro.h" #include "WuQMacroCommand.h" #include "WuQMacroCommandParameter.h" @@ -285,20 +283,12 @@ WuQMacroExecutor::runMacro(const WuQMacro* macro, const WuQMacroExecutorOptions* executorOptions, QString& errorMessageOut) const { - MovieRecorder* movieRecorder = SessionManager::get()->getMovieRecorder(); - const MovieRecorderModeEnum::Enum savedRecordingMode = movieRecorder->getRecordingMode(); - if (executorOptions->isRecordMovieDuringExecution()) { - movieRecorder->setRecordingMode(MovieRecorderModeEnum::AUTOMATIC); - } - const bool result = runMacroPrivate(macro, window, otherObjectParents, executorOptions, errorMessageOut); - movieRecorder->setRecordingMode(savedRecordingMode); - return result; } diff --git a/src/GuiQt/WuQMacroHelperInterface.h b/src/GuiQt/WuQMacroHelperInterface.h index f1d709879..c97f93f48 100644 --- a/src/GuiQt/WuQMacroHelperInterface.h +++ b/src/GuiQt/WuQMacroHelperInterface.h @@ -31,9 +31,11 @@ #include class QMainWindow; +class QWidget; namespace caret { class WuQMacro; + class WuQMacroExecutorOptions; class WuQMacroGroup; class WuQMacroHelperInterface : public QObject { @@ -84,6 +86,34 @@ namespace caret { */ virtual QMainWindow* getMainWindowWithIdentifier(const QString& identifier) = 0; + /** + * Called by macro executor just before executing the macro + * + * @param macro + * Macro that is run + * @param window + * Widget for parent + * @param executorOptions + * Executor options + */ + virtual void macroExecutionStarting(const WuQMacro* macro, + QWidget* window, + const WuQMacroExecutorOptions* executorOptions) = 0; + + /** + * Called by macro executor just after executing the macro + * + * @param macro + * Macro that is run + * @param window + * Widget for parent + * @param executorOptions + * Executor options + */ + virtual void macroExecutionEnding(const WuQMacro* macro, + QWidget* window, + const WuQMacroExecutorOptions* executorOptions) = 0; + // ADD_NEW_METHODS_HERE signals: diff --git a/src/GuiQt/WuQMacroManager.cxx b/src/GuiQt/WuQMacroManager.cxx index e575a7e81..4e14ae803 100644 --- a/src/GuiQt/WuQMacroManager.cxx +++ b/src/GuiQt/WuQMacroManager.cxx @@ -527,11 +527,24 @@ WuQMacroManager::runMacro(QWidget* widget, QString errorMessage; m_macroExecutor = new WuQMacroExecutor(); - if ( ! m_macroExecutor->runMacro(macro, - widget, - m_parentObjects, - m_executorOptions.get(), - errorMessage)) { + + if (m_macroHelper != NULL) { + m_macroHelper->macroExecutionStarting(macro, + widget, + m_executorOptions.get()); + } + const bool resultFlag = m_macroExecutor->runMacro(macro, + widget, + m_parentObjects, + m_executorOptions.get(), + errorMessage); + if (m_macroHelper != NULL) { + m_macroHelper->macroExecutionEnding(macro, + widget, + m_executorOptions.get()); + } + + if ( ! resultFlag) { QMessageBox::critical(widget, "Run Macro Error", errorMessage, -- GitLab From 5ee77a2080606421bd2186c6e0e7173c44e6d641 Mon Sep 17 00:00:00 2001 From: John Harwell Date: Wed, 13 Feb 2019 10:32:28 -0600 Subject: [PATCH 184/427] Implemented volume slice increment macro command. Also various other macro and movie improvements. --- src/Brain/MovieRecorder.cxx | 2 +- src/GuiQt/CMakeLists.txt | 2 + src/GuiQt/WbMacroCustomOperationBase.cxx | 15 +- src/GuiQt/WbMacroCustomOperationBase.h | 7 +- src/GuiQt/WbMacroCustomOperationManager.cxx | 48 ++- src/GuiQt/WbMacroCustomOperationManager.h | 4 + .../WbMacroCustomOperationModelRotation.cxx | 2 +- ...croCustomOperationSurfaceInterpolation.cxx | 2 +- src/GuiQt/WbMacroCustomOperationTypeEnum.cxx | 4 + src/GuiQt/WbMacroCustomOperationTypeEnum.h | 4 +- ...croCustomOperationVolumeSliceIncrement.cxx | 310 ++++++++++++++++++ ...MacroCustomOperationVolumeSliceIncrement.h | 75 +++++ src/GuiQt/WuQMacroDialog.cxx | 19 +- src/GuiQt/WuQMacroDialog.h | 2 +- 14 files changed, 461 insertions(+), 35 deletions(-) create mode 100644 src/GuiQt/WbMacroCustomOperationVolumeSliceIncrement.cxx create mode 100644 src/GuiQt/WbMacroCustomOperationVolumeSliceIncrement.h diff --git a/src/Brain/MovieRecorder.cxx b/src/Brain/MovieRecorder.cxx index 3fdd9c637..99d5754a1 100644 --- a/src/Brain/MovieRecorder.cxx +++ b/src/Brain/MovieRecorder.cxx @@ -107,7 +107,7 @@ MovieRecorder::addImageToMovie(const QImage* image) CaretLogSevere("Attempting to create movie with images that are different sizes. " "First image width=" + QString::number(m_firstImageWidth) + ", height=" + QString::number(m_firstImageHeight) - + "Image number=" + QString::number(imageIndexInt) + + " Image number=" + QString::number(imageIndexInt) + ", width=" + QString::number(image->width()) + ", height=" + QString::number(image->height())); } diff --git a/src/GuiQt/CMakeLists.txt b/src/GuiQt/CMakeLists.txt index 46ce58b81..215a03bd5 100644 --- a/src/GuiQt/CMakeLists.txt +++ b/src/GuiQt/CMakeLists.txt @@ -455,6 +455,7 @@ WbMacroCustomOperationManager.h WbMacroCustomOperationModelRotation.h WbMacroCustomOperationSurfaceInterpolation.h WbMacroCustomOperationTypeEnum.h +WbMacroCustomOperationVolumeSliceIncrement.h WbMacroHelper.h WuQCollapsibleWidget.h WuQDataEntryDialog.h @@ -708,6 +709,7 @@ WbMacroCustomOperationManager.cxx WbMacroCustomOperationModelRotation.cxx WbMacroCustomOperationSurfaceInterpolation.cxx WbMacroCustomOperationTypeEnum.cxx +WbMacroCustomOperationVolumeSliceIncrement.cxx WbMacroHelper.cxx WuQCollapsibleWidget.cxx WuQDataEntryDialog.cxx diff --git a/src/GuiQt/WbMacroCustomOperationBase.cxx b/src/GuiQt/WbMacroCustomOperationBase.cxx index 05c378661..41c042266 100644 --- a/src/GuiQt/WbMacroCustomOperationBase.cxx +++ b/src/GuiQt/WbMacroCustomOperationBase.cxx @@ -48,8 +48,12 @@ using namespace caret; /** * Constructor + * + * @param operationTye + * Type of custom command operation */ -WbMacroCustomOperationBase::WbMacroCustomOperationBase() +WbMacroCustomOperationBase::WbMacroCustomOperationBase(const WbMacroCustomOperationTypeEnum::Enum operationType) +: m_operationType(operationType) { } @@ -63,6 +67,15 @@ WbMacroCustomOperationBase::~WbMacroCustomOperationBase() } +/** + * @return The custom operation type + */ +WbMacroCustomOperationTypeEnum::Enum +WbMacroCustomOperationBase::getOperationType() const +{ + return m_operationType; +} + /** * @return The error message */ diff --git a/src/GuiQt/WbMacroCustomOperationBase.h b/src/GuiQt/WbMacroCustomOperationBase.h index 4e53f4aef..483e7cd75 100644 --- a/src/GuiQt/WbMacroCustomOperationBase.h +++ b/src/GuiQt/WbMacroCustomOperationBase.h @@ -26,6 +26,7 @@ #include #include "CaretObject.h" +#include "WbMacroCustomOperationTypeEnum.h" class QString; class QWidget; @@ -42,6 +43,8 @@ namespace caret { WbMacroCustomOperationBase& operator=(const WbMacroCustomOperationBase&) = delete; + WbMacroCustomOperationTypeEnum::Enum getOperationType() const; + /** * Execute the macro command * @@ -75,7 +78,7 @@ namespace caret { void sleepForSecondsAtEndOfIteration(const float seconds); protected: - WbMacroCustomOperationBase(); + WbMacroCustomOperationBase(const WbMacroCustomOperationTypeEnum::Enum operationType); bool validateCorrectNumberOfParameters(const WuQMacroCommand* command); @@ -88,6 +91,8 @@ namespace caret { void updateUserInterface(); + const WbMacroCustomOperationTypeEnum::Enum m_operationType; + QString m_errorMessage; }; diff --git a/src/GuiQt/WbMacroCustomOperationManager.cxx b/src/GuiQt/WbMacroCustomOperationManager.cxx index 6709149a5..dbd8bded4 100644 --- a/src/GuiQt/WbMacroCustomOperationManager.cxx +++ b/src/GuiQt/WbMacroCustomOperationManager.cxx @@ -35,6 +35,7 @@ #include "WbMacroCustomOperationModelRotation.h" #include "WbMacroCustomOperationSurfaceInterpolation.h" #include "WbMacroCustomOperationTypeEnum.h" +#include "WbMacroCustomOperationVolumeSliceIncrement.h" #include "WuQMacroCommand.h" #include "WuQMacroCommandParameter.h" #include "WuQMessageBox.h" @@ -210,14 +211,7 @@ WbMacroCustomOperationManager::executeCustomOperationMacroCommand(QWidget* paren } std::unique_ptr customOperation; - switch (commandType) { - case WbMacroCustomOperationTypeEnum::MODEL_ROTATION: - customOperation.reset(new WbMacroCustomOperationModelRotation()); - break; - case WbMacroCustomOperationTypeEnum::SURFACE_INTERPOLATION: - customOperation.reset(new WbMacroCustomOperationSurfaceInterpolation()); - break; - } + customOperation.reset(createCommand(commandType)); bool successFlag(false); if (customOperation) { @@ -280,14 +274,7 @@ WbMacroCustomOperationManager::newInstanceOfCustomOperationMacroCommand(const QS WuQMacroCommand* command(NULL); std::unique_ptr customOperation; - switch (commandType) { - case WbMacroCustomOperationTypeEnum::MODEL_ROTATION: - customOperation.reset(new WbMacroCustomOperationModelRotation()); - break; - case WbMacroCustomOperationTypeEnum::SURFACE_INTERPOLATION: - customOperation.reset(new WbMacroCustomOperationSurfaceInterpolation()); - break; - } + customOperation.reset(createCommand(commandType)); if (customOperation) { command = customOperation->createCommand(); @@ -303,3 +290,32 @@ WbMacroCustomOperationManager::newInstanceOfCustomOperationMacroCommand(const QS return command; } +/** + * Create a custom operation of the given type + * + * @param operationType + * The operation type + * @return + * New instance of command caller is responsible for destroying + */ +WbMacroCustomOperationBase* +WbMacroCustomOperationManager::createCommand(const WbMacroCustomOperationTypeEnum::Enum operationType) +{ + WbMacroCustomOperationBase* operationOut; + + switch (operationType) { + case WbMacroCustomOperationTypeEnum::MODEL_ROTATION: + operationOut = new WbMacroCustomOperationModelRotation(); + break; + case WbMacroCustomOperationTypeEnum::SURFACE_INTERPOLATION: + operationOut = new WbMacroCustomOperationSurfaceInterpolation(); + break; + case WbMacroCustomOperationTypeEnum::VOLUME_SLICE_INCREMENT: + operationOut = new WbMacroCustomOperationVolumeSliceIncrement(); + break; + } + + CaretAssert(operationOut); + return operationOut; +} + diff --git a/src/GuiQt/WbMacroCustomOperationManager.h b/src/GuiQt/WbMacroCustomOperationManager.h index 107e70300..f0ec553bf 100644 --- a/src/GuiQt/WbMacroCustomOperationManager.h +++ b/src/GuiQt/WbMacroCustomOperationManager.h @@ -26,10 +26,12 @@ #include #include "CaretObject.h" +#include "WbMacroCustomOperationTypeEnum.h" #include "WuQMacroCustomOperationManagerInterface.h" class QWidget; namespace caret { + class WbMacroCustomOperationBase; class WuQMacroCommand; class WuQMacroCommandParameter; @@ -63,6 +65,8 @@ namespace caret { private: + WbMacroCustomOperationBase* createCommand(const WbMacroCustomOperationTypeEnum::Enum operationType); + // ADD_NEW_MEMBERS_HERE }; diff --git a/src/GuiQt/WbMacroCustomOperationModelRotation.cxx b/src/GuiQt/WbMacroCustomOperationModelRotation.cxx index 4d85099f3..29bf0d113 100644 --- a/src/GuiQt/WbMacroCustomOperationModelRotation.cxx +++ b/src/GuiQt/WbMacroCustomOperationModelRotation.cxx @@ -48,7 +48,7 @@ using namespace caret; * Constructor. */ WbMacroCustomOperationModelRotation::WbMacroCustomOperationModelRotation() -: WbMacroCustomOperationBase() +: WbMacroCustomOperationBase(WbMacroCustomOperationTypeEnum::MODEL_ROTATION) { } diff --git a/src/GuiQt/WbMacroCustomOperationSurfaceInterpolation.cxx b/src/GuiQt/WbMacroCustomOperationSurfaceInterpolation.cxx index e433ba730..de71abf75 100644 --- a/src/GuiQt/WbMacroCustomOperationSurfaceInterpolation.cxx +++ b/src/GuiQt/WbMacroCustomOperationSurfaceInterpolation.cxx @@ -50,7 +50,7 @@ using namespace caret; * Constructor. */ WbMacroCustomOperationSurfaceInterpolation::WbMacroCustomOperationSurfaceInterpolation() -: WbMacroCustomOperationBase() +: WbMacroCustomOperationBase(WbMacroCustomOperationTypeEnum::SURFACE_INTERPOLATION) { } diff --git a/src/GuiQt/WbMacroCustomOperationTypeEnum.cxx b/src/GuiQt/WbMacroCustomOperationTypeEnum.cxx index 32fe389ed..067ac9e4d 100644 --- a/src/GuiQt/WbMacroCustomOperationTypeEnum.cxx +++ b/src/GuiQt/WbMacroCustomOperationTypeEnum.cxx @@ -113,6 +113,10 @@ WbMacroCustomOperationTypeEnum::initialize() enumData.push_back(WbMacroCustomOperationTypeEnum(SURFACE_INTERPOLATION, "SURFACE_INTERPOLATION", "Surface Interpolation")); + + enumData.push_back(WbMacroCustomOperationTypeEnum(VOLUME_SLICE_INCREMENT, + "VOLUME_SLICE_INCREMENT", + "Volume Slice Increment")); } /** diff --git a/src/GuiQt/WbMacroCustomOperationTypeEnum.h b/src/GuiQt/WbMacroCustomOperationTypeEnum.h index f9bf59a75..2bd0c2f55 100644 --- a/src/GuiQt/WbMacroCustomOperationTypeEnum.h +++ b/src/GuiQt/WbMacroCustomOperationTypeEnum.h @@ -38,7 +38,9 @@ public: /** Rotation of viewed model */ MODEL_ROTATION, /** Surface Interpolation */ - SURFACE_INTERPOLATION + SURFACE_INTERPOLATION, + /** Volume slice increment */ + VOLUME_SLICE_INCREMENT }; diff --git a/src/GuiQt/WbMacroCustomOperationVolumeSliceIncrement.cxx b/src/GuiQt/WbMacroCustomOperationVolumeSliceIncrement.cxx new file mode 100644 index 000000000..2accb96e0 --- /dev/null +++ b/src/GuiQt/WbMacroCustomOperationVolumeSliceIncrement.cxx @@ -0,0 +1,310 @@ + +/*LICENSE_START*/ +/* + * Copyright (C) 2019 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + +#define __WB_MACRO_CUSTOM_OPERATION_VOLUME_SLICE_INCREMENT_DECLARE__ +#include "WbMacroCustomOperationVolumeSliceIncrement.h" +#undef __WB_MACRO_CUSTOM_OPERATION_VOLUME_SLICE_INCREMENT_DECLARE__ + +#include "BrainBrowserWindow.h" +#include "BrowserTabContent.h" +#include "CaretAssert.h" +#include "Matrix4x4.h" +#include "Model.h" +#include "ModelVolume.h" +#include "ModelWholeBrain.h" +#include "Overlay.h" +#include "OverlaySet.h" +#include "SystemUtilities.h" +#include "VolumeMappableInterface.h" +#include "WbMacroCustomDataTypeEnum.h" +#include "WbMacroCustomOperationTypeEnum.h" +#include "WuQMacroCommand.h" +#include "WuQMacroCommandParameter.h" + +using namespace caret; + + + +/** + * \class caret::WbMacroCustomOperationVolumeSliceIncrement + * \brief Macro custom operation incrementing volume slices + * \ingroup GuiQt + */ + +/** + * Constructor. + */ +WbMacroCustomOperationVolumeSliceIncrement::WbMacroCustomOperationVolumeSliceIncrement() +: WbMacroCustomOperationBase(WbMacroCustomOperationTypeEnum::VOLUME_SLICE_INCREMENT) +{ + +} + +/** + * Destructor. + */ +WbMacroCustomOperationVolumeSliceIncrement::~WbMacroCustomOperationVolumeSliceIncrement() +{ +} + +/** + * Get a new instance of the macro command + * + * @return + * Pointer to command or NULL if not valid + * Use getErrorMessage() for error information if NULL returned + */ +WuQMacroCommand* +WbMacroCustomOperationVolumeSliceIncrement::createCommand() +{ + const int32_t versionOne(1); + + + const QString description("Sequence through the volume slices: \n" + "(1) start at the \"selected slice\"; \n" + "(2) decrement the slice index to the first slice; \n" + "(3) increment the slice index to the last slice; \n" + "(4) decrement the slice index returning to the \"selected slice\""); + QString errorMessage; + WuQMacroCommand* command = WuQMacroCommand::newInstanceCustomCommand(WbMacroCustomOperationTypeEnum::toName(getOperationType()), + versionOne, + "none", + "Volume Slice Increment", + description, + 1.0, + errorMessage); + if (command != NULL) { + WuQMacroCommandParameter* paramOne = new WuQMacroCommandParameter(WuQMacroDataValueTypeEnum::CUSTOM_DATA, + "Screen Axis", + "Z"); + paramOne->setCustomDataType(WbMacroCustomDataTypeEnum::toName(WbMacroCustomDataTypeEnum::SCREEN_AXIS)); + command->addParameter(paramOne); + command->addParameter(WuQMacroDataValueTypeEnum::FLOAT, + "Duration (secs)", + (float)20.0); + } + else { + appendToErrorMessage(errorMessage); + } + + return command; +} + +/** + * Execute the macro command + * + * @param parent + * Parent widget for any dialogs + * @param macroCommand + * macro command to run + * @return + * True if command executed successfully, else false + * Use getErrorMessage() for error information if false returned + */ +bool +WbMacroCustomOperationVolumeSliceIncrement::executeCommand(QWidget* parent, + const WuQMacroCommand* macroCommand) +{ + CaretAssert(parent); + CaretAssert(macroCommand); + + validateCorrectNumberOfParameters(macroCommand); + + BrainBrowserWindow* bbw = qobject_cast(parent); + if (bbw == NULL) { + appendToErrorMessage("Parent for running surface macro is not a browser window."); + return false; + } + + BrowserTabContent* tabContent = bbw->getBrowserTabContent(); + if (tabContent == NULL) { + appendToErrorMessage("No tab is selected in browser window."); + return false; + } + + int32_t parameterIndex(0); + const QString axisName(macroCommand->getParameterAtIndex(parameterIndex)->getValue().toString().toUpper()); + parameterIndex++; + const float durationSeconds = macroCommand->getParameterAtIndex(parameterIndex)->getValue().toFloat(); + parameterIndex++; + + Axis axis = Axis::X; + if (axisName == "X") { + axis = Axis::X; + } + else if (axisName == "Y") { + axis = Axis::Y; + } + else if (axisName == "Z") { + axis = Axis::Z; + } + else { + appendToErrorMessage("Axis named \"" + + axisName + + "\" is invalid. Use X, Y, or Z."); + } + + if (durationSeconds < 0.0) { + appendToErrorMessage("Duration must be greater than zero."); + } + + Model* model = tabContent->getModelForDisplay(); + if (model == NULL) { + appendToErrorMessage("No model for surface rotation"); + } + + if ( ! getErrorMessage().isEmpty()) { + return false; + } + + const bool successFlag = performSliceIncrement(tabContent, + axis, + durationSeconds); + return successFlag; +} + +/** + * @param tabContent + * Content in the selected tab + * @param axis + * Axis for viewing + * @param durationSecondes + * Duration of time for command to run + */ +bool +WbMacroCustomOperationVolumeSliceIncrement::performSliceIncrement(BrowserTabContent* tabContent, + const Axis axis, + const float durationSeconds) +{ + ModelVolume* volumeModel(tabContent->getDisplayedVolumeModel()); + ModelWholeBrain* wholeBrainModel(tabContent->getDisplayedWholeBrainModel()); + if ((volumeModel == NULL) + && (wholeBrainModel == NULL)) { + appendToErrorMessage("For slice increment, View must be All or Volume"); + return false; + } + + const VolumeMappableInterface* vmi = tabContent->getOverlaySet()->getUnderlayVolume(); + if (vmi == NULL) { + appendToErrorMessage("No volume is selected as an overlay"); + return false; + } + std::vector dims; + vmi->getDimensions(dims); + if (dims.size() < 3) { + appendToErrorMessage("Dimensions are invalid for underlay volume"); + } + + int32_t numberOfSlices(0); + int32_t startingSliceIndex(0); + switch (axis) { + case Axis::X: + numberOfSlices = dims[0]; + startingSliceIndex = tabContent->getSliceIndexParasagittal(vmi); + break; + case Axis::Y: + numberOfSlices = dims[1]; + startingSliceIndex = tabContent->getSliceIndexCoronal(vmi); + break; + case Axis::Z: + numberOfSlices = dims[2]; + startingSliceIndex = tabContent->getSliceIndexAxial(vmi); + break; + } + if (numberOfSlices <= 0) { + appendToErrorMessage("No slices in underlay volume for selected dimension"); + } + + const float defaultNumberOfSteps(numberOfSlices); + float numberOfSteps(0.0); + float iterationSleepTime(0.0); + getNumberOfStepsAndSleepTime(defaultNumberOfSteps, + durationSeconds, + numberOfSteps, + iterationSleepTime); + + enum SliceMode { + DECREMENT_TO_ZERO, + INCREMENT_TO_LAST, + DECREMENT_TO_START + }; + SliceMode sliceMode = DECREMENT_TO_ZERO; + + float sliceIndex = startingSliceIndex; + float sliceIncrement = numberOfSlices / (numberOfSteps / 2); + + bool doneFlag(false); + while ( ! doneFlag) { + switch (axis) { + case Axis::X: + tabContent->setSliceIndexParasagittal(vmi, sliceIndex); + break; + case Axis::Y: + tabContent->setSliceIndexCoronal(vmi, sliceIndex); + break; + case Axis::Z: + tabContent->setSliceIndexAxial(vmi, sliceIndex); + break; + } + + switch (sliceMode) { + case DECREMENT_TO_START: + sliceIndex -= sliceIncrement; + if (sliceIndex <= startingSliceIndex) { + doneFlag = true; + } + break; + case DECREMENT_TO_ZERO: + sliceIndex -= sliceIncrement; + if (sliceIndex <= 0.0) { + sliceIndex = 0; + sliceMode = INCREMENT_TO_LAST; + } + break; + case INCREMENT_TO_LAST: + sliceIndex += sliceIncrement; + if (sliceIndex >= (numberOfSlices - 1)) { + sliceIndex = (numberOfSlices - 1); + sliceMode = DECREMENT_TO_START; + } + break; + } + + + updateGraphics(); + sleepForSecondsAtEndOfIteration(iterationSleepTime); + } + + switch (axis) { + case Axis::X: + tabContent->setSliceIndexParasagittal(vmi, startingSliceIndex); + break; + case Axis::Y: + tabContent->setSliceIndexCoronal(vmi, startingSliceIndex); + break; + case Axis::Z: + tabContent->setSliceIndexAxial(vmi, startingSliceIndex); + break; + } + updateGraphics(); + + return true; +} diff --git a/src/GuiQt/WbMacroCustomOperationVolumeSliceIncrement.h b/src/GuiQt/WbMacroCustomOperationVolumeSliceIncrement.h new file mode 100644 index 000000000..8ad63c977 --- /dev/null +++ b/src/GuiQt/WbMacroCustomOperationVolumeSliceIncrement.h @@ -0,0 +1,75 @@ +#ifndef __WB_MACRO_CUSTOM_OPERATION_VOLUME_SLICE_INCREMENT_H__ +#define __WB_MACRO_CUSTOM_OPERATION_VOLUME_SLICE_INCREMENT_H__ + +/*LICENSE_START*/ +/* + * Copyright (C) 2019 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + + + +#include + +#include "WbMacroCustomOperationBase.h" + + + +namespace caret { + + class BrowserTabContent; + + class WbMacroCustomOperationVolumeSliceIncrement : public WbMacroCustomOperationBase { + + public: + WbMacroCustomOperationVolumeSliceIncrement(); + + virtual ~WbMacroCustomOperationVolumeSliceIncrement(); + + WbMacroCustomOperationVolumeSliceIncrement(const WbMacroCustomOperationVolumeSliceIncrement&) = delete; + + WbMacroCustomOperationVolumeSliceIncrement& operator=(const WbMacroCustomOperationVolumeSliceIncrement&) = delete; + + virtual bool executeCommand(QWidget* parent, + const WuQMacroCommand* macroCommand) override; + + virtual WuQMacroCommand* createCommand() override; + + + // ADD_NEW_METHODS_HERE + + private: + enum class Axis { + X, + Y, + Z + }; + + bool performSliceIncrement(BrowserTabContent* tabContent, + const Axis axis, + const float durationSeconds); + + // ADD_NEW_MEMBERS_HERE + + }; + +#ifdef __WB_MACRO_CUSTOM_OPERATION_VOLUME_SLICE_INCREMENT_DECLARE__ + // +#endif // __WB_MACRO_CUSTOM_OPERATION_VOLUME_SLICE_INCREMENT_DECLARE__ + +} // namespace +#endif //__WB_MACRO_CUSTOM_OPERATION_VOLUME_SLICE_INCREMENT_H__ diff --git a/src/GuiQt/WuQMacroDialog.cxx b/src/GuiQt/WuQMacroDialog.cxx index 15c5e6af0..d43b0d048 100644 --- a/src/GuiQt/WuQMacroDialog.cxx +++ b/src/GuiQt/WuQMacroDialog.cxx @@ -514,10 +514,10 @@ WuQMacroDialog::createCommandDisplayWidget() this, &WuQMacroDialog::macroCommandDelaySpinBoxValueChanged); QLabel* delayTwoLabel = new QLabel("seconds after command"); - QLabel* toolTipLabel = new QLabel("ToolTip:"); - m_commandToolTipTextEdit = new QPlainTextEdit(); - m_commandToolTipTextEdit->setReadOnly(true); - m_commandToolTipTextEdit->setMaximumHeight(100); + QLabel* descriptionLabel = new QLabel("Description:"); + m_commandDescriptionTextEdit = new QPlainTextEdit(); + m_commandDescriptionTextEdit->setReadOnly(true); + m_commandDescriptionTextEdit->setMaximumHeight(100); QGridLayout* layout = new QGridLayout(); layout->setContentsMargins(0, 0, 0, 0); @@ -538,8 +538,8 @@ WuQMacroDialog::createCommandDisplayWidget() layout->addWidget(m_commandDelaySpinBox, row, 1); layout->addWidget(delayTwoLabel, row, 2, Qt::AlignLeft); row++; - layout->addWidget(toolTipLabel, row, 0, (Qt::AlignLeft | Qt::AlignTop)); - layout->addWidget(m_commandToolTipTextEdit, row, 1, 1, 2); + layout->addWidget(descriptionLabel, row, 0, (Qt::AlignLeft | Qt::AlignTop)); + layout->addWidget(m_commandDescriptionTextEdit, row, 1, 1, 2); row++; QWidget* parametersWidget = new QWidget(); @@ -551,11 +551,6 @@ WuQMacroDialog::createCommandDisplayWidget() m_parameterWidgetsGridLayout->setVerticalSpacing(static_cast(m_parameterWidgetsGridLayout->verticalSpacing() / 1.2)); m_parameterWidgetsGridLayout->setColumnStretch(0, 0); m_parameterWidgetsGridLayout->setColumnStretch(1, 100); -// m_parameterWidgetsGridLayout->addWidget(new QLabel("Edit"), -// 0, 0, Qt::AlignHCenter); -// m_parameterWidgetsGridLayout->addWidget(new QLabel("Parameter Value"), -// 0, 1, Qt::AlignLeft); - QHBoxLayout* titleLayout = new QHBoxLayout(); titleLayout->setContentsMargins(0, 0, 0, 0); @@ -782,7 +777,7 @@ WuQMacroDialog::updateCommandWidget(WuQMacroCommand* command) QSignalBlocker delayBlocker(m_commandDelaySpinBox); m_commandDelaySpinBox->setValue(delay); - m_commandToolTipTextEdit->setPlainText(toolTip); + m_commandDescriptionTextEdit->setPlainText(toolTip); /** * Update the parameter widgets diff --git a/src/GuiQt/WuQMacroDialog.h b/src/GuiQt/WuQMacroDialog.h index fdce985ea..d196a9cfe 100644 --- a/src/GuiQt/WuQMacroDialog.h +++ b/src/GuiQt/WuQMacroDialog.h @@ -201,7 +201,7 @@ namespace caret { QDoubleSpinBox* m_commandDelaySpinBox; - QPlainTextEdit* m_commandToolTipTextEdit; + QPlainTextEdit* m_commandDescriptionTextEdit; std::vector m_parameterWidgets; -- GitLab From 41298a1d0e3044367740d213b4e648fc02bf473c Mon Sep 17 00:00:00 2001 From: John Harwell Date: Wed, 13 Feb 2019 14:16:27 -0600 Subject: [PATCH 185/427] WB-834 Macros: Created new dialog for adding of custom macro commands to macros. --- src/GuiQt/CMakeLists.txt | 3 + src/GuiQt/WbMacroCustomOperationManager.cxx | 19 ++ src/GuiQt/WbMacroCustomOperationManager.h | 6 +- .../WuQMacroCustomOperationManagerInterface.h | 6 + src/GuiQt/WuQMacroDialog.cxx | 164 ++++++---- src/GuiQt/WuQMacroManager.cxx | 15 + src/GuiQt/WuQMacroManager.h | 2 + .../WuQMacroNewCommandSelectionDialog.cxx | 306 ++++++++++++++++++ src/GuiQt/WuQMacroNewCommandSelectionDialog.h | 101 ++++++ 9 files changed, 561 insertions(+), 61 deletions(-) create mode 100644 src/GuiQt/WuQMacroNewCommandSelectionDialog.cxx create mode 100644 src/GuiQt/WuQMacroNewCommandSelectionDialog.h diff --git a/src/GuiQt/CMakeLists.txt b/src/GuiQt/CMakeLists.txt index 215a03bd5..b22020b12 100644 --- a/src/GuiQt/CMakeLists.txt +++ b/src/GuiQt/CMakeLists.txt @@ -211,6 +211,7 @@ ELSE() WuQMacroHelperInterface.h WuQMacroManager.h WuQMacroMenu.h + WuQMacroNewCommandSelectionDialog.h WuQMacroShortCutKeyComboBox.h WuQMacroSignalEmitter.h WuQMacroSignalWatcher.h @@ -479,6 +480,7 @@ WuQMacroHelperInterface.h WuQMacroManager.h WuQMacroMenu.h WuQMacroMouseEventWidgetInterface.h +WuQMacroNewCommandSelectionDialog.h WuQMacroShortCutKeyComboBox.h WuQMacroSignalEmitter.h WuQMacroSignalWatcher.h @@ -730,6 +732,7 @@ WuQMacroExecutor.cxx WuQMacroExecutorOptions.cxx WuQMacroManager.cxx WuQMacroMenu.cxx +WuQMacroNewCommandSelectionDialog.cxx WuQMacroShortCutKeyComboBox.cxx WuQMacroSignalEmitter.cxx WuQMacroSignalWatcher.cxx diff --git a/src/GuiQt/WbMacroCustomOperationManager.cxx b/src/GuiQt/WbMacroCustomOperationManager.cxx index dbd8bded4..709b70113 100644 --- a/src/GuiQt/WbMacroCustomOperationManager.cxx +++ b/src/GuiQt/WbMacroCustomOperationManager.cxx @@ -245,6 +245,25 @@ WbMacroCustomOperationManager::getNamesOfCustomOperationMacroCommands() return namesOut; } +/** + * @return All custom operation commands. Caller is responsible for deleting + * all content of the returned vector. + */ +std::vector +WbMacroCustomOperationManager::getAllCustomOperationMacroCommands() +{ + std::vector customCommandTypes; + WbMacroCustomOperationTypeEnum::getAllEnums(customCommandTypes); + + std::vector customCommands; + for (auto cct : customCommandTypes) { + std::unique_ptr customOperation(createCommand(cct)); + customCommands.push_back(customOperation->createCommand()); + } + + return customCommands; +} + /** * Get a new instance of a custom operation for the given macro command name * diff --git a/src/GuiQt/WbMacroCustomOperationManager.h b/src/GuiQt/WbMacroCustomOperationManager.h index f0ec553bf..c52aedd6d 100644 --- a/src/GuiQt/WbMacroCustomOperationManager.h +++ b/src/GuiQt/WbMacroCustomOperationManager.h @@ -53,10 +53,12 @@ namespace caret { const WuQMacroCommand* macroCommand, QString& errorMessageOut); - virtual std::vector getNamesOfCustomOperationMacroCommands(); + virtual std::vector getNamesOfCustomOperationMacroCommands() override; + virtual std::vector getAllCustomOperationMacroCommands() override; + virtual WuQMacroCommand* newInstanceOfCustomOperationMacroCommand(const QString& customMacroCommandName, - QString& errorMessageOut); + QString& errorMessageOut) override; // ADD_NEW_METHODS_HERE diff --git a/src/GuiQt/WuQMacroCustomOperationManagerInterface.h b/src/GuiQt/WuQMacroCustomOperationManagerInterface.h index 808edf244..11a45517c 100644 --- a/src/GuiQt/WuQMacroCustomOperationManagerInterface.h +++ b/src/GuiQt/WuQMacroCustomOperationManagerInterface.h @@ -75,6 +75,12 @@ namespace caret { */ virtual std::vector getNamesOfCustomOperationMacroCommands() = 0; + /** + * @return All custom operation commands. Caller is responsible for deleting + * all content of the returned vector. + */ + virtual std::vector getAllCustomOperationMacroCommands() = 0; + /** * Get a new instance of a custom operation for the given macro command name * diff --git a/src/GuiQt/WuQMacroDialog.cxx b/src/GuiQt/WuQMacroDialog.cxx index d43b0d048..c4e43241e 100644 --- a/src/GuiQt/WuQMacroDialog.cxx +++ b/src/GuiQt/WuQMacroDialog.cxx @@ -54,6 +54,7 @@ #include "WuQMacroExecutor.h" #include "WuQMacroManager.h" #include "WuQMacroMouseEventInfo.h" +#include "WuQMacroNewCommandSelectionDialog.h" #include "WuQMacroShortCutKeyComboBox.h" #include "WuQMacroStandardItemTypeEnum.h" #include "WuQtUtilities.h" @@ -400,7 +401,7 @@ WuQMacroDialog::createRunOptionsWidget() QLabel* windowLabel = new QLabel("Window"); m_runOptionsWindowComboBox = new QComboBox(); - m_runOptionsWindowComboBox->setSizeAdjustPolicy(QComboBox::AdjustToMinimumContentsLength); + m_runOptionsWindowComboBox->setSizeAdjustPolicy(QComboBox::AdjustToContents); m_runOptionLoopCheckBox = new QCheckBox("Loop"); m_runOptionLoopCheckBox->setChecked(false); @@ -1308,70 +1309,115 @@ WuQMacroDialog::insertMenuNewMacroSelected() void WuQMacroDialog::insertMenuNewMacroCommandSelected() { - std::vector customOperationNames = WuQMacroManager::instance()->getNamesOfCustomOperationMacroCommands(); - QStringList namesStringList; - for (const auto name : customOperationNames) { - namesStringList.append(name); - } - - if (namesStringList.isEmpty()) { - QMessageBox::critical(m_editingInsertToolButton, - "Error", - "No custom operations available"); - return; - } - - bool okFlag(false); - QString selectedName = QInputDialog::getItem(m_editingInsertToolButton, - "Choose Operation", - "New Operation", - namesStringList, - 0, - false, - &okFlag); - - if (okFlag - && ( ! selectedName.isEmpty())) { + WuQMacroNewCommandSelectionDialog dialog(this); + if (dialog.exec() == WuQMacroNewCommandSelectionDialog::Accepted) { QString errorMessage; - WuQMacroCommand* newCommand = WuQMacroManager::instance()->newInstanceOfCustomOperationMacroCommand(selectedName, - errorMessage); - if (newCommand == NULL) { + WuQMacroCommand* newCommand = dialog.getNewInstanceOfSelectedCommand(errorMessage); + if (newCommand != NULL) { + switch (getSelectedItemType()) { + case WuQMacroStandardItemTypeEnum::INVALID: + CaretAssert(0); + break; + case WuQMacroStandardItemTypeEnum::MACRO: + { + WuQMacro* macro = getSelectedMacro(); + CaretAssert(macro); + macro->insertMacroCommandAtIndex(0, newCommand); + } + break; + case WuQMacroStandardItemTypeEnum::MACRO_COMMAND: + { + WuQMacro* macro = getSelectedMacro(); + CaretAssert(macro); + const WuQMacroCommand* selectedCommand = getSelectedMacroCommand(); + CaretAssert(selectedCommand); + const int32_t selectedIndex = macro->getIndexOfMacroCommand(selectedCommand); + macro->insertMacroCommandAtIndex(selectedIndex + 1, + newCommand); + } + } + + WuQMacroGroup* selectedGroup = getSelectedMacroGroup(); + CaretAssert(selectedGroup); + QModelIndex selectedIndex = selectedGroup->indexFromItem(newCommand); + m_treeView->setCurrentIndex(selectedIndex); + + updateDialogContents(); + } + else { QMessageBox::critical(m_editingInsertToolButton, "Error", errorMessage); - return; } - - switch (getSelectedItemType()) { - case WuQMacroStandardItemTypeEnum::INVALID: - CaretAssert(0); - break; - case WuQMacroStandardItemTypeEnum::MACRO: - { - WuQMacro* macro = getSelectedMacro(); - CaretAssert(macro); - macro->insertMacroCommandAtIndex(0, newCommand); - } - break; - case WuQMacroStandardItemTypeEnum::MACRO_COMMAND: - { - WuQMacro* macro = getSelectedMacro(); - CaretAssert(macro); - const WuQMacroCommand* selectedCommand = getSelectedMacroCommand(); - CaretAssert(selectedCommand); - const int32_t selectedIndex = macro->getIndexOfMacroCommand(selectedCommand); - macro->insertMacroCommandAtIndex(selectedIndex + 1, - newCommand); - } - } - - WuQMacroGroup* selectedGroup = getSelectedMacroGroup(); - CaretAssert(selectedGroup); - QModelIndex selectedIndex = selectedGroup->indexFromItem(newCommand); - m_treeView->setCurrentIndex(selectedIndex); - - updateDialogContents(); } + +// return; +// +// +// std::vector customOperationNames = WuQMacroManager::instance()->getNamesOfCustomOperationMacroCommands(); +// QStringList namesStringList; +// for (const auto name : customOperationNames) { +// namesStringList.append(name); +// } +// +// if (namesStringList.isEmpty()) { +// QMessageBox::critical(m_editingInsertToolButton, +// "Error", +// "No custom operations available"); +// return; +// } +// +// bool okFlag(false); +// QString selectedName = QInputDialog::getItem(m_editingInsertToolButton, +// "Choose Operation", +// "New Operation", +// namesStringList, +// 0, +// false, +// &okFlag); +// +// if (okFlag +// && ( ! selectedName.isEmpty())) { +// QString errorMessage; +// WuQMacroCommand* newCommand = WuQMacroManager::instance()->newInstanceOfCustomOperationMacroCommand(selectedName, +// errorMessage); +// if (newCommand == NULL) { +// QMessageBox::critical(m_editingInsertToolButton, +// "Error", +// errorMessage); +// return; +// } +// +// switch (getSelectedItemType()) { +// case WuQMacroStandardItemTypeEnum::INVALID: +// CaretAssert(0); +// break; +// case WuQMacroStandardItemTypeEnum::MACRO: +// { +// WuQMacro* macro = getSelectedMacro(); +// CaretAssert(macro); +// macro->insertMacroCommandAtIndex(0, newCommand); +// } +// break; +// case WuQMacroStandardItemTypeEnum::MACRO_COMMAND: +// { +// WuQMacro* macro = getSelectedMacro(); +// CaretAssert(macro); +// const WuQMacroCommand* selectedCommand = getSelectedMacroCommand(); +// CaretAssert(selectedCommand); +// const int32_t selectedIndex = macro->getIndexOfMacroCommand(selectedCommand); +// macro->insertMacroCommandAtIndex(selectedIndex + 1, +// newCommand); +// } +// } +// +// WuQMacroGroup* selectedGroup = getSelectedMacroGroup(); +// CaretAssert(selectedGroup); +// QModelIndex selectedIndex = selectedGroup->indexFromItem(newCommand); +// m_treeView->setCurrentIndex(selectedIndex); +// +// updateDialogContents(); +// } } /** diff --git a/src/GuiQt/WuQMacroManager.cxx b/src/GuiQt/WuQMacroManager.cxx index 4e14ae803..ffc4abae2 100644 --- a/src/GuiQt/WuQMacroManager.cxx +++ b/src/GuiQt/WuQMacroManager.cxx @@ -997,6 +997,21 @@ WuQMacroManager::executeCustomOperationMacroCommand(QWidget* parent, return successFlag; } +/** + * @return All custom operation commands. Caller is responsible for deleting + * all content of the returned vector. + */ +std::vector +WuQMacroManager::getAllCustomOperationMacroCommands() +{ + std::vector customCommands; + + if (m_customCommandManager != NULL) { + customCommands = m_customCommandManager->getAllCustomOperationMacroCommands(); + } + return customCommands; +} + /** * @return Names of custom operation defined macro commands */ diff --git a/src/GuiQt/WuQMacroManager.h b/src/GuiQt/WuQMacroManager.h index 73012ad85..d3d97f292 100644 --- a/src/GuiQt/WuQMacroManager.h +++ b/src/GuiQt/WuQMacroManager.h @@ -146,6 +146,8 @@ namespace caret { const WuQMacroCommand* macroCommand, QString& errorMessageOut); + virtual std::vector getAllCustomOperationMacroCommands(); + virtual std::vector getNamesOfCustomOperationMacroCommands(); virtual WuQMacroCommand* newInstanceOfCustomOperationMacroCommand(const QString& macroCommandName, diff --git a/src/GuiQt/WuQMacroNewCommandSelectionDialog.cxx b/src/GuiQt/WuQMacroNewCommandSelectionDialog.cxx new file mode 100644 index 000000000..a2c4e5597 --- /dev/null +++ b/src/GuiQt/WuQMacroNewCommandSelectionDialog.cxx @@ -0,0 +1,306 @@ + +/*LICENSE_START*/ +/* + * Copyright (C) 2018 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + +#define __WU_Q_MACRO_NEW_COMMAND_SELECTION_DIALOG_DECLARE__ +#include "WuQMacroNewCommandSelectionDialog.h" +#undef __WU_Q_MACRO_NEW_COMMAND_SELECTION_DIALOG_DECLARE__ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "CaretAssert.h" +#include "CaretLogger.h" +#include "EnumComboBoxTemplate.h" +#include "WuQMacro.h" +#include "WuQMacroCommand.h" +#include "WuQMacroGroup.h" +#include "WuQMacroManager.h" +#include "WuQMacroShortCutKeyComboBox.h" + +using namespace caret; + +/** + * \class caret::WuQMacroNewCommandSelectionDialog + * \brief Dialog for creating a new macro + * \ingroup WuQMacro + */ + +/** + * Constructor. + * + * @param parent + * The parent widget + */ +WuQMacroNewCommandSelectionDialog::WuQMacroNewCommandSelectionDialog(QWidget* parent) +: QDialog(parent) +{ + setWindowTitle("Add Macro Command"); + + QLabel* commandTypeLabel = new QLabel("Type:"); + QLabel* commandsLabel = new QLabel("Commands:"); + QLabel* descriptionLabel = new QLabel("Description:"); + + std::vector commandTypes; + commandTypes.push_back(WuQMacroCommandTypeEnum::CUSTOM_OPERATION); + // Not implemented yet: commandTypes.push_back(WuQMacroCommandTypeEnum::WIDGET); + m_commandTypeComboBox = new EnumComboBoxTemplate(this); + m_commandTypeComboBox->setupWithItems(commandTypes); + m_commandTypeComboBox->setSelectedItem(s_lastCommandTypeSelected); + QObject::connect(m_commandTypeComboBox, &EnumComboBoxTemplate::itemActivated, + this, &WuQMacroNewCommandSelectionDialog::commandTypeComboBoxActivated); + + m_customCommandListWidget = new QListWidget(); + QObject::connect(m_customCommandListWidget, &QListWidget::itemClicked, + this, &WuQMacroNewCommandSelectionDialog::customCommandListWidgetItemClicked); + + m_widgetCommandListWidget = new QListWidget(); + QObject::connect(m_widgetCommandListWidget, &QListWidget::itemClicked, + this, &WuQMacroNewCommandSelectionDialog::widgetCommandListWidgetItemClicked); + + m_stackedWidget = new QStackedWidget(); + m_stackedWidget->addWidget(m_customCommandListWidget); + m_stackedWidget->addWidget(m_widgetCommandListWidget); + + m_macroDescriptionTextEdit = new QPlainTextEdit(); + m_macroDescriptionTextEdit->setFixedHeight(100); + + QGridLayout* gridLayout = new QGridLayout(); + int row = 0; + gridLayout->addWidget(commandTypeLabel, row, 0); + gridLayout->addWidget(m_commandTypeComboBox->getWidget(), row, 1, 1, 2); + row++; + gridLayout->addWidget(commandsLabel, row, 0); + gridLayout->addWidget(m_stackedWidget, row, 1, 1, 2); + row++; + gridLayout->addWidget(descriptionLabel, row, 0); + gridLayout->addWidget(m_macroDescriptionTextEdit, row, 1, 1, 2); + row++; + + m_dialogButtonBox = new QDialogButtonBox(QDialogButtonBox::Ok + | QDialogButtonBox::Cancel); + QObject::connect(m_dialogButtonBox, &QDialogButtonBox::accepted, + this, &WuQMacroNewCommandSelectionDialog::accept); + QObject::connect(m_dialogButtonBox, &QDialogButtonBox::rejected, + this, &WuQMacroNewCommandSelectionDialog::reject); + + QVBoxLayout* dialogLayout = new QVBoxLayout(this); + dialogLayout->addLayout(gridLayout); + dialogLayout->addWidget(m_dialogButtonBox); + + commandTypeComboBoxActivated(); +} + +/** + * Destructor. + */ +WuQMacroNewCommandSelectionDialog::~WuQMacroNewCommandSelectionDialog() +{ + for (auto cc : m_customCommands) { + delete cc; + } + m_customCommands.clear(); +} + +/** + * Called when a selection is made from command type combo box + */ +void +WuQMacroNewCommandSelectionDialog::commandTypeComboBoxActivated() +{ + s_lastCommandTypeSelected = m_commandTypeComboBox->getSelectedItem(); + + m_macroDescriptionTextEdit->clear(); + + switch (s_lastCommandTypeSelected) { + case WuQMacroCommandTypeEnum::CUSTOM_OPERATION: + m_stackedWidget->setCurrentWidget(m_customCommandListWidget); + if (m_customCommandListWidget->count() <= 0) { + loadCustomCommandListWidget(); + if (m_customCommandListWidget->count() > 0) { + QListWidgetItem* firstItem = m_customCommandListWidget->item(0); + if (firstItem != NULL) { + m_customCommandListWidget->setCurrentItem(firstItem); + customCommandListWidgetItemClicked(firstItem); + } + } + } + break; + case WuQMacroCommandTypeEnum::MOUSE: + CaretAssert(0); + break; + case WuQMacroCommandTypeEnum::WIDGET: + m_stackedWidget->setCurrentWidget(m_widgetCommandListWidget); + if (m_widgetCommandListWidget->count() <= 0) { + loadWidgetCommandListWidget(); + } + break; + } +} + +/** + * Load the custom commands into the list widget + */ +void +WuQMacroNewCommandSelectionDialog::loadCustomCommandListWidget() +{ + m_customCommands = WuQMacroManager::instance()->getAllCustomOperationMacroCommands(); + for (auto cc : m_customCommands) { + const QString name = cc->getDescriptiveName(); + const QString customTypeName = cc->getCustomOperationTypeName(); + + QListWidgetItem* item = new QListWidgetItem(name); + item->setData(Qt::UserRole, customTypeName); + m_customCommandListWidget->addItem(item); + } +} + +/** + * Load the widget commands into the list widget + */ +void +WuQMacroNewCommandSelectionDialog::loadWidgetCommandListWidget() +{ + +} + +/** + * Called when an item in the custom list widget is clicked + * + * @param item + * Item that was clicked + */ +void +WuQMacroNewCommandSelectionDialog::customCommandListWidgetItemClicked(QListWidgetItem* item) +{ + QString description; + if (item != NULL) { + const QString customTypeName = item->data(Qt::UserRole).toString(); + for (auto cc : m_customCommands) { + if (customTypeName == cc->getCustomOperationTypeName()) { + description = cc->getObjectToolTip(); + } + } + } + + m_macroDescriptionTextEdit->setPlainText(description); +} + +/** + * Called when an item in the custom list widget is clicked + * + * @param item + * Item that was clicked + */ +void +WuQMacroNewCommandSelectionDialog::widgetCommandListWidgetItemClicked(QListWidgetItem* item) +{ + +} + +/** + * Called when user clicks OK or Cancel + * + * @param r + * The dialog code (Accepted or Rejected) + */ +void +WuQMacroNewCommandSelectionDialog::done(int r) +{ + if (r == QDialog::Accepted) { + bool validFlag(false); + switch (s_lastCommandTypeSelected) { + case WuQMacroCommandTypeEnum::CUSTOM_OPERATION: + validFlag = (m_customCommandListWidget->currentItem() != NULL); + break; + case WuQMacroCommandTypeEnum::MOUSE: + CaretAssert(0); + validFlag = false; + break; + case WuQMacroCommandTypeEnum::WIDGET: + validFlag = (m_widgetCommandListWidget->currentItem() != NULL); + break; + } + + if ( ! validFlag) { + QMessageBox::critical(this, + "Error", + "No command is selected", + QMessageBox::Ok, + QMessageBox::Ok); + } + } + + QDialog::done(r); +} + +/** + * Get new instance of the command selected + * + * @param errorMessageOut + * Output with error information. + * @return + Pointer to new command or NULL if it failed. + */ +WuQMacroCommand* +WuQMacroNewCommandSelectionDialog::getNewInstanceOfSelectedCommand(QString& errorMessageOut) +{ + errorMessageOut.clear(); + WuQMacroCommand* commandOut(NULL); + + switch (s_lastCommandTypeSelected) { + case WuQMacroCommandTypeEnum::CUSTOM_OPERATION: + { + QListWidgetItem* item = m_customCommandListWidget->currentItem(); + if (item != NULL) { + const QString customTypeName = item->data(Qt::UserRole).toString(); + QString errorMessage; + commandOut = WuQMacroManager::instance()->newInstanceOfCustomOperationMacroCommand(customTypeName, + errorMessage); + if (commandOut == NULL) { + errorMessageOut = ("New instance of custom command \"" + + customTypeName + + "\" failed"); + } + } + } + break; + case WuQMacroCommandTypeEnum::MOUSE: + errorMessageOut = "Mouse commands not supported for new instances"; + CaretAssert(0); + break; + case WuQMacroCommandTypeEnum::WIDGET: + errorMessageOut = "New widget commands has not been implemented"; + break; + } + + return commandOut; +} + + diff --git a/src/GuiQt/WuQMacroNewCommandSelectionDialog.h b/src/GuiQt/WuQMacroNewCommandSelectionDialog.h new file mode 100644 index 000000000..fe5825c74 --- /dev/null +++ b/src/GuiQt/WuQMacroNewCommandSelectionDialog.h @@ -0,0 +1,101 @@ +#ifndef __WU_Q_MACRO_NEW_COMMAND_SELECTION_DIALOG_H__ +#define __WU_Q_MACRO_NEW_COMMAND_SELECTION_DIALOG_H__ + +/*LICENSE_START*/ +/* + * Copyright (C) 2018 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + + +#include +#include + +#include + +#include "WuQMacroCommandTypeEnum.h" + +class QComboBox; +class QDialogButtonBox; +class QLineEdit; +class QListWidget; +class QListWidgetItem; +class QPlainTextEdit; +class QStackedWidget; + +namespace caret { + + class EnumComboBoxTemplate; + class WuQMacro; + class WuQMacroCommand; + class WuQMacroGroup; + class WuQMacroShortCutKeyComboBox; + + class WuQMacroNewCommandSelectionDialog : public QDialog { + + Q_OBJECT + + public: + WuQMacroNewCommandSelectionDialog(QWidget* parent = 0); + + virtual ~WuQMacroNewCommandSelectionDialog(); + + WuQMacroNewCommandSelectionDialog(const WuQMacroNewCommandSelectionDialog&) = delete; + + WuQMacroNewCommandSelectionDialog& operator=(const WuQMacroNewCommandSelectionDialog&) = delete; + + WuQMacroCommand* getNewInstanceOfSelectedCommand(QString& errorMessageOut); + + // ADD_NEW_METHODS_HERE + + public slots: + void commandTypeComboBoxActivated(); + + void customCommandListWidgetItemClicked(QListWidgetItem* item); + + void widgetCommandListWidgetItemClicked(QListWidgetItem* item); + + virtual void done(int r) override; + + private: + void loadCustomCommandListWidget(); + + void loadWidgetCommandListWidget(); + + EnumComboBoxTemplate* m_commandTypeComboBox; + + QStackedWidget* m_stackedWidget; + + QListWidget* m_customCommandListWidget; + + QListWidget* m_widgetCommandListWidget; + + QPlainTextEdit* m_macroDescriptionTextEdit; + + QDialogButtonBox* m_dialogButtonBox; + + std::vector m_customCommands; + + static WuQMacroCommandTypeEnum::Enum s_lastCommandTypeSelected; + }; + +#ifdef __WU_Q_MACRO_NEW_COMMAND_SELECTION_DIALOG_DECLARE__ + WuQMacroCommandTypeEnum::Enum WuQMacroNewCommandSelectionDialog::s_lastCommandTypeSelected = WuQMacroCommandTypeEnum::CUSTOM_OPERATION; +#endif // __WU_Q_MACRO_NEW_COMMAND_SELECTION_DIALOG_DECLARE__ + +} // namespace +#endif //__WU_Q_MACRO_NEW_COMMAND_SELECTION_DIALOG_H__ -- GitLab From 38eedfb93a5c4cb0ae4cc16053f8a75ae3be8c31 Mon Sep 17 00:00:00 2001 From: John Harwell Date: Wed, 13 Feb 2019 15:00:26 -0600 Subject: [PATCH 186/427] WB-834 Macros: Add additional video resolutions for recording movies --- src/Brain/CMakeLists.txt | 4 +- src/Brain/MovieRecorder.cxx | 27 ++- src/Brain/MovieRecorder.h | 10 +- ... MovieRecorderVideoResolutionTypeEnum.cxx} | 168 +++++++++++------- ...=> MovieRecorderVideoResolutionTypeEnum.h} | 36 ++-- src/GuiQt/MovieRecordingDialog.cxx | 33 ++-- src/GuiQt/MovieRecordingDialog.h | 4 +- 7 files changed, 162 insertions(+), 120 deletions(-) rename src/Brain/{MovieRecorderVideoDimensionsTypeEnum.cxx => MovieRecorderVideoResolutionTypeEnum.cxx} (57%) rename src/Brain/{MovieRecorderVideoDimensionsTypeEnum.h => MovieRecorderVideoResolutionTypeEnum.h} (70%) diff --git a/src/Brain/CMakeLists.txt b/src/Brain/CMakeLists.txt index d0c66ecb6..959856269 100644 --- a/src/Brain/CMakeLists.txt +++ b/src/Brain/CMakeLists.txt @@ -129,8 +129,8 @@ ModelWholeBrain.h MovieRecorder.h MovieRecorderCaptureRegionTypeEnum.h MovieRecorderModeEnum.h -MovieRecorderVideoDimensionsTypeEnum.h MovieRecorderVideoFormatTypeEnum.h +MovieRecorderVideoResolutionTypeEnum.h Overlay.h OverlaySet.h OverlaySetArray.h @@ -286,8 +286,8 @@ ModelWholeBrain.cxx MovieRecorder.cxx MovieRecorderCaptureRegionTypeEnum.cxx MovieRecorderModeEnum.cxx -MovieRecorderVideoDimensionsTypeEnum.cxx MovieRecorderVideoFormatTypeEnum.cxx +MovieRecorderVideoResolutionTypeEnum.cxx Overlay.cxx OverlaySet.cxx OverlaySetArray.cxx diff --git a/src/Brain/MovieRecorder.cxx b/src/Brain/MovieRecorder.cxx index 99d5754a1..cfd6a2131 100644 --- a/src/Brain/MovieRecorder.cxx +++ b/src/Brain/MovieRecorder.cxx @@ -212,7 +212,7 @@ MovieRecorder::setRecordingWindowIndex(const int32_t windowIndex) /** * @return Video dimensions type */ -MovieRecorderVideoDimensionsTypeEnum::Enum +MovieRecorderVideoResolutionTypeEnum::Enum MovieRecorder::getVideoDimensionsType() const { return m_dimensionsType; @@ -225,7 +225,7 @@ MovieRecorder::getVideoDimensionsType() const * New dimensions type */ void -MovieRecorder::setVideoDimensionsType(const MovieRecorderVideoDimensionsTypeEnum::Enum dimensionsType) +MovieRecorder::setVideoDimensionsType(const MovieRecorderVideoResolutionTypeEnum::Enum dimensionsType) { m_dimensionsType = dimensionsType; } @@ -244,19 +244,16 @@ MovieRecorder::getVideoDimensions(int32_t& widthOut, { widthOut = 100; heightOut = 100; - switch (getVideoDimensionsType()) { - case MovieRecorderVideoDimensionsTypeEnum::CUSTOM: - getCustomDimensions(widthOut, - heightOut); - break; - case MovieRecorderVideoDimensionsTypeEnum::HD_1280_720: - widthOut = 1280; - heightOut = 720; - break; - case MovieRecorderVideoDimensionsTypeEnum::SD_640_480: - widthOut = 640; - heightOut = 480; - break; + + const MovieRecorderVideoResolutionTypeEnum::Enum dimType = getVideoDimensionsType(); + if (dimType == MovieRecorderVideoResolutionTypeEnum::CUSTOM) { + getCustomDimensions(widthOut, + heightOut); + } + else { + MovieRecorderVideoResolutionTypeEnum::getDimensions(dimType, + widthOut, + heightOut); } } diff --git a/src/Brain/MovieRecorder.h b/src/Brain/MovieRecorder.h index 4287b3218..df826a2f5 100644 --- a/src/Brain/MovieRecorder.h +++ b/src/Brain/MovieRecorder.h @@ -28,7 +28,7 @@ #include "CaretObject.h" #include "MovieRecorderCaptureRegionTypeEnum.h" #include "MovieRecorderModeEnum.h" -#include "MovieRecorderVideoDimensionsTypeEnum.h" +#include "MovieRecorderVideoResolutionTypeEnum.h" #include "MovieRecorderVideoFormatTypeEnum.h" class QImage; @@ -60,9 +60,9 @@ namespace caret { void setRecordingWindowIndex(const int32_t windowIndex); - MovieRecorderVideoDimensionsTypeEnum::Enum getVideoDimensionsType() const; + MovieRecorderVideoResolutionTypeEnum::Enum getVideoDimensionsType() const; - void setVideoDimensionsType(const MovieRecorderVideoDimensionsTypeEnum::Enum dimensionsType); + void setVideoDimensionsType(const MovieRecorderVideoResolutionTypeEnum::Enum dimensionsType); void getVideoDimensions(int32_t& widthOut, int32_t& heightOut) const; @@ -125,7 +125,7 @@ namespace caret { MovieRecorderModeEnum::Enum m_recordingMode = MovieRecorderModeEnum::MANUAL; - MovieRecorderVideoDimensionsTypeEnum::Enum m_dimensionsType = MovieRecorderVideoDimensionsTypeEnum::SD_640_480; + MovieRecorderVideoResolutionTypeEnum::Enum m_dimensionsType = MovieRecorderVideoResolutionTypeEnum::SD_640_480; MovieRecorderVideoFormatTypeEnum::Enum m_formatType = MovieRecorderVideoFormatTypeEnum::MPEG_4; @@ -143,7 +143,7 @@ namespace caret { std::vector m_imageFrameFileNames; - float m_frameRate = 20.0f; + float m_frameRate = 30.0f; float m_manualRecordingDurationSeconds = 5.0f; diff --git a/src/Brain/MovieRecorderVideoDimensionsTypeEnum.cxx b/src/Brain/MovieRecorderVideoResolutionTypeEnum.cxx similarity index 57% rename from src/Brain/MovieRecorderVideoDimensionsTypeEnum.cxx rename to src/Brain/MovieRecorderVideoResolutionTypeEnum.cxx index 6dde1ff6c..cff4dcd23 100644 --- a/src/Brain/MovieRecorderVideoDimensionsTypeEnum.cxx +++ b/src/Brain/MovieRecorderVideoResolutionTypeEnum.cxx @@ -20,9 +20,9 @@ /*LICENSE_END*/ #include -#define __MOVIE_RECORDER_VIDEO_DIMENSIONS_TYPE_ENUM_DECLARE__ -#include "MovieRecorderVideoDimensionsTypeEnum.h" -#undef __MOVIE_RECORDER_VIDEO_DIMENSIONS_TYPE_ENUM_DECLARE__ +#define __MOVIE_RECORDER_VIDEO_RESOLUTION_TYPE_ENUM_DECLARE__ +#include "MovieRecorderVideoResolutionTypeEnum.h" +#undef __MOVIE_RECORDER_VIDEO_RESOLUTION_TYPE_ENUM_DECLARE__ #include "CaretAssert.h" @@ -30,8 +30,8 @@ using namespace caret; /** - * \class caret::MovieRecorderVideoDimensionsTypeEnum - * \brief Dimensions for movie recording + * \class caret::MovieRecorderVideoResolutionTypeEnum + * \brief Resolutions for movie recording * * Using this enumerated type in the GUI with an EnumComboBoxTemplate * @@ -40,30 +40,30 @@ using namespace caret; * class EnumComboBoxTemplate; * * Declare the member: - * EnumComboBoxTemplate* m_movieRecorderVideoDimensionsTypeEnumComboBox; + * EnumComboBoxTemplate* m_MovieRecorderVideoResolutionTypeEnumComboBox; * * Declare a slot that is called when user changes selection * private slots: - * void movieRecorderVideoDimensionsTypeEnumComboBoxItemActivated(); + * void MovieRecorderVideoResolutionTypeEnumComboBoxItemActivated(); * * Implementation File (.cxx) * Include the header files * #include "EnumComboBoxTemplate.h" - * #include "MovieRecorderVideoDimensionsTypeEnum.h" + * #include "MovieRecorderVideoResolutionTypeEnum.h" * * Instatiate: - * m_movieRecorderVideoDimensionsTypeEnumComboBox = new EnumComboBoxTemplate(this); - * m_movieRecorderVideoDimensionsTypeEnumComboBox->setup(); + * m_MovieRecorderVideoResolutionTypeEnumComboBox = new EnumComboBoxTemplate(this); + * m_MovieRecorderVideoResolutionTypeEnumComboBox->setup(); * * Get notified when the user changes the selection: - * QObject::connect(m_movieRecorderVideoDimensionsTypeEnumComboBox, SIGNAL(itemActivated()), - * this, SLOT(movieRecorderVideoDimensionsTypeEnumComboBoxItemActivated())); + * QObject::connect(m_MovieRecorderVideoResolutionTypeEnumComboBox, SIGNAL(itemActivated()), + * this, SLOT(MovieRecorderVideoResolutionTypeEnumComboBoxItemActivated())); * * Update the selection: - * m_movieRecorderVideoDimensionsTypeEnumComboBox->setSelectedItem(NEW_VALUE); + * m_MovieRecorderVideoResolutionTypeEnumComboBox->setSelectedItem(NEW_VALUE); * * Read the selection: - * const MovieRecorderVideoDimensionsTypeEnum::Enum VARIABLE = m_movieRecorderVideoDimensionsTypeEnumComboBox->getSelectedItem(); + * const MovieRecorderVideoResolutionTypeEnum::Enum VARIABLE = m_MovieRecorderVideoResolutionTypeEnumComboBox->getSelectedItem(); * */ @@ -78,7 +78,7 @@ using namespace caret; * @param guiName * User-friendly name for use in user-interface. */ -MovieRecorderVideoDimensionsTypeEnum::MovieRecorderVideoDimensionsTypeEnum(const Enum enumValue, +MovieRecorderVideoResolutionTypeEnum::MovieRecorderVideoResolutionTypeEnum(const Enum enumValue, const AString& name, const AString& guiName) { @@ -91,7 +91,7 @@ MovieRecorderVideoDimensionsTypeEnum::MovieRecorderVideoDimensionsTypeEnum(const /** * Destructor. */ -MovieRecorderVideoDimensionsTypeEnum::~MovieRecorderVideoDimensionsTypeEnum() +MovieRecorderVideoResolutionTypeEnum::~MovieRecorderVideoResolutionTypeEnum() { } @@ -99,25 +99,33 @@ MovieRecorderVideoDimensionsTypeEnum::~MovieRecorderVideoDimensionsTypeEnum() * Initialize the enumerated metadata. */ void -MovieRecorderVideoDimensionsTypeEnum::initialize() +MovieRecorderVideoResolutionTypeEnum::initialize() { if (initializedFlag) { return; } initializedFlag = true; - enumData.push_back(MovieRecorderVideoDimensionsTypeEnum(CUSTOM, - "CUSTOM", - "Custom")); + enumData.push_back(MovieRecorderVideoResolutionTypeEnum(CUSTOM, + "CUSTOM", + "Custom")); - enumData.push_back(MovieRecorderVideoDimensionsTypeEnum(HD_1280_720, - "HD_1280_720", - "HD (1280x720)")); + enumData.push_back(MovieRecorderVideoResolutionTypeEnum(UHD_3840_2160, + "UHD_3840_2160", + "UHD (3840x2160)")); - enumData.push_back(MovieRecorderVideoDimensionsTypeEnum(SD_640_480, - "SD_640_480", - "SD (640x480)")); + enumData.push_back(MovieRecorderVideoResolutionTypeEnum(FULL_HD_1920_1080, + "FULL_HD_1920_1080", + "Full HD (1920x1080)")); + enumData.push_back(MovieRecorderVideoResolutionTypeEnum(HD_1280_720, + "HD_1280_720", + "HD Ready (1280x720)")); + + enumData.push_back(MovieRecorderVideoResolutionTypeEnum(SD_640_480, + "SD_640_480", + "SD (640x480)")); + } /** @@ -127,14 +135,14 @@ MovieRecorderVideoDimensionsTypeEnum::initialize() * @return Pointer to data for this enumerated type * or NULL if no data for type or if type is invalid. */ -const MovieRecorderVideoDimensionsTypeEnum* -MovieRecorderVideoDimensionsTypeEnum::findData(const Enum enumValue) +const MovieRecorderVideoResolutionTypeEnum* +MovieRecorderVideoResolutionTypeEnum::findData(const Enum enumValue) { if (initializedFlag == false) initialize(); size_t num = enumData.size(); for (size_t i = 0; i < num; i++) { - const MovieRecorderVideoDimensionsTypeEnum* d = &enumData[i]; + const MovieRecorderVideoResolutionTypeEnum* d = &enumData[i]; if (d->enumValue == enumValue) { return d; } @@ -151,10 +159,10 @@ MovieRecorderVideoDimensionsTypeEnum::findData(const Enum enumValue) * String representing enumerated value. */ AString -MovieRecorderVideoDimensionsTypeEnum::toName(Enum enumValue) { +MovieRecorderVideoResolutionTypeEnum::toName(Enum enumValue) { if (initializedFlag == false) initialize(); - const MovieRecorderVideoDimensionsTypeEnum* enumInstance = findData(enumValue); + const MovieRecorderVideoResolutionTypeEnum* enumInstance = findData(enumValue); return enumInstance->name; } @@ -168,18 +176,18 @@ MovieRecorderVideoDimensionsTypeEnum::toName(Enum enumValue) { * @return * Enumerated value. */ -MovieRecorderVideoDimensionsTypeEnum::Enum -MovieRecorderVideoDimensionsTypeEnum::fromName(const AString& name, bool* isValidOut) +MovieRecorderVideoResolutionTypeEnum::Enum +MovieRecorderVideoResolutionTypeEnum::fromName(const AString& name, bool* isValidOut) { if (initializedFlag == false) initialize(); bool validFlag = false; - Enum enumValue = MovieRecorderVideoDimensionsTypeEnum::enumData[0].enumValue; + Enum enumValue = MovieRecorderVideoResolutionTypeEnum::enumData[0].enumValue; - for (std::vector::iterator iter = enumData.begin(); + for (std::vector::iterator iter = enumData.begin(); iter != enumData.end(); iter++) { - const MovieRecorderVideoDimensionsTypeEnum& d = *iter; + const MovieRecorderVideoResolutionTypeEnum& d = *iter; if (d.name == name) { enumValue = d.enumValue; validFlag = true; @@ -191,7 +199,7 @@ MovieRecorderVideoDimensionsTypeEnum::fromName(const AString& name, bool* isVali *isValidOut = validFlag; } else if (validFlag == false) { - CaretAssertMessage(0, AString("Name " + name + "failed to match enumerated value for type MovieRecorderVideoDimensionsTypeEnum")); + CaretAssertMessage(0, AString("Name " + name + "failed to match enumerated value for type MovieRecorderVideoResolutionTypeEnum")); } return enumValue; } @@ -204,10 +212,10 @@ MovieRecorderVideoDimensionsTypeEnum::fromName(const AString& name, bool* isVali * String representing enumerated value. */ AString -MovieRecorderVideoDimensionsTypeEnum::toGuiName(Enum enumValue) { +MovieRecorderVideoResolutionTypeEnum::toGuiName(Enum enumValue) { if (initializedFlag == false) initialize(); - const MovieRecorderVideoDimensionsTypeEnum* enumInstance = findData(enumValue); + const MovieRecorderVideoResolutionTypeEnum* enumInstance = findData(enumValue); return enumInstance->guiName; } @@ -221,18 +229,18 @@ MovieRecorderVideoDimensionsTypeEnum::toGuiName(Enum enumValue) { * @return * Enumerated value. */ -MovieRecorderVideoDimensionsTypeEnum::Enum -MovieRecorderVideoDimensionsTypeEnum::fromGuiName(const AString& guiName, bool* isValidOut) +MovieRecorderVideoResolutionTypeEnum::Enum +MovieRecorderVideoResolutionTypeEnum::fromGuiName(const AString& guiName, bool* isValidOut) { if (initializedFlag == false) initialize(); bool validFlag = false; - Enum enumValue = MovieRecorderVideoDimensionsTypeEnum::enumData[0].enumValue; + Enum enumValue = MovieRecorderVideoResolutionTypeEnum::enumData[0].enumValue; - for (std::vector::iterator iter = enumData.begin(); + for (std::vector::iterator iter = enumData.begin(); iter != enumData.end(); iter++) { - const MovieRecorderVideoDimensionsTypeEnum& d = *iter; + const MovieRecorderVideoResolutionTypeEnum& d = *iter; if (d.guiName == guiName) { enumValue = d.enumValue; validFlag = true; @@ -244,7 +252,7 @@ MovieRecorderVideoDimensionsTypeEnum::fromGuiName(const AString& guiName, bool* *isValidOut = validFlag; } else if (validFlag == false) { - CaretAssertMessage(0, AString("guiName " + guiName + "failed to match enumerated value for type MovieRecorderVideoDimensionsTypeEnum")); + CaretAssertMessage(0, AString("guiName " + guiName + "failed to match enumerated value for type MovieRecorderVideoResolutionTypeEnum")); } return enumValue; } @@ -256,10 +264,10 @@ MovieRecorderVideoDimensionsTypeEnum::fromGuiName(const AString& guiName, bool* * Integer code for data type. */ int32_t -MovieRecorderVideoDimensionsTypeEnum::toIntegerCode(Enum enumValue) +MovieRecorderVideoResolutionTypeEnum::toIntegerCode(Enum enumValue) { if (initializedFlag == false) initialize(); - const MovieRecorderVideoDimensionsTypeEnum* enumInstance = findData(enumValue); + const MovieRecorderVideoResolutionTypeEnum* enumInstance = findData(enumValue); return enumInstance->integerCode; } @@ -274,18 +282,18 @@ MovieRecorderVideoDimensionsTypeEnum::toIntegerCode(Enum enumValue) * @return * Enum for integer code. */ -MovieRecorderVideoDimensionsTypeEnum::Enum -MovieRecorderVideoDimensionsTypeEnum::fromIntegerCode(const int32_t integerCode, bool* isValidOut) +MovieRecorderVideoResolutionTypeEnum::Enum +MovieRecorderVideoResolutionTypeEnum::fromIntegerCode(const int32_t integerCode, bool* isValidOut) { if (initializedFlag == false) initialize(); bool validFlag = false; - Enum enumValue = MovieRecorderVideoDimensionsTypeEnum::enumData[0].enumValue; + Enum enumValue = MovieRecorderVideoResolutionTypeEnum::enumData[0].enumValue; - for (std::vector::iterator iter = enumData.begin(); + for (std::vector::iterator iter = enumData.begin(); iter != enumData.end(); iter++) { - const MovieRecorderVideoDimensionsTypeEnum& enumInstance = *iter; + const MovieRecorderVideoResolutionTypeEnum& enumInstance = *iter; if (enumInstance.integerCode == integerCode) { enumValue = enumInstance.enumValue; validFlag = true; @@ -297,7 +305,7 @@ MovieRecorderVideoDimensionsTypeEnum::fromIntegerCode(const int32_t integerCode, *isValidOut = validFlag; } else if (validFlag == false) { - CaretAssertMessage(0, AString("Integer code " + AString::number(integerCode) + "failed to match enumerated value for type MovieRecorderVideoDimensionsTypeEnum")); + CaretAssertMessage(0, AString("Integer code " + AString::number(integerCode) + "failed to match enumerated value for type MovieRecorderVideoResolutionTypeEnum")); } return enumValue; } @@ -310,13 +318,13 @@ MovieRecorderVideoDimensionsTypeEnum::fromIntegerCode(const int32_t integerCode, * A vector that is OUTPUT containing all of the enumerated values. */ void -MovieRecorderVideoDimensionsTypeEnum::getAllEnums(std::vector& allEnums) +MovieRecorderVideoResolutionTypeEnum::getAllEnums(std::vector& allEnums) { if (initializedFlag == false) initialize(); allEnums.clear(); - for (std::vector::iterator iter = enumData.begin(); + for (std::vector::iterator iter = enumData.begin(); iter != enumData.end(); iter++) { allEnums.push_back(iter->enumValue); @@ -332,16 +340,16 @@ MovieRecorderVideoDimensionsTypeEnum::getAllEnums(std::vector& allNames, const bool isSorted) +MovieRecorderVideoResolutionTypeEnum::getAllNames(std::vector& allNames, const bool isSorted) { if (initializedFlag == false) initialize(); allNames.clear(); - for (std::vector::iterator iter = enumData.begin(); + for (std::vector::iterator iter = enumData.begin(); iter != enumData.end(); iter++) { - allNames.push_back(MovieRecorderVideoDimensionsTypeEnum::toName(iter->enumValue)); + allNames.push_back(MovieRecorderVideoResolutionTypeEnum::toName(iter->enumValue)); } if (isSorted) { @@ -358,16 +366,16 @@ MovieRecorderVideoDimensionsTypeEnum::getAllNames(std::vector& allNames * If true, the names are sorted in alphabetical order. */ void -MovieRecorderVideoDimensionsTypeEnum::getAllGuiNames(std::vector& allGuiNames, const bool isSorted) +MovieRecorderVideoResolutionTypeEnum::getAllGuiNames(std::vector& allGuiNames, const bool isSorted) { if (initializedFlag == false) initialize(); allGuiNames.clear(); - for (std::vector::iterator iter = enumData.begin(); + for (std::vector::iterator iter = enumData.begin(); iter != enumData.end(); iter++) { - allGuiNames.push_back(MovieRecorderVideoDimensionsTypeEnum::toGuiName(iter->enumValue)); + allGuiNames.push_back(MovieRecorderVideoResolutionTypeEnum::toGuiName(iter->enumValue)); } if (isSorted) { @@ -375,3 +383,41 @@ MovieRecorderVideoDimensionsTypeEnum::getAllGuiNames(std::vector& allGu } } +/** + * Get the dimensions for given enumerated value. Output will be zeros + * for custom. + * + * @param enumValue + * Enumerated value. + * @param widthOut + * Output containing width + * @param heightOut + * Output containing height + */ +void +MovieRecorderVideoResolutionTypeEnum::getDimensions(const Enum enumValue, + int32_t& widthOut, + int32_t& heightOut) +{ + switch (enumValue) { + case CUSTOM: + break; + case FULL_HD_1920_1080: + widthOut = 1920; + heightOut = 1080; + break; + case HD_1280_720: + widthOut = 1280; + heightOut = 720; + break; + case SD_640_480: + widthOut = 640; + heightOut = 480; + break; + case UHD_3840_2160: + widthOut = 3840; + heightOut = 2160; + break; + } +} + diff --git a/src/Brain/MovieRecorderVideoDimensionsTypeEnum.h b/src/Brain/MovieRecorderVideoResolutionTypeEnum.h similarity index 70% rename from src/Brain/MovieRecorderVideoDimensionsTypeEnum.h rename to src/Brain/MovieRecorderVideoResolutionTypeEnum.h index 3d25cdb5b..47b9fbc85 100644 --- a/src/Brain/MovieRecorderVideoDimensionsTypeEnum.h +++ b/src/Brain/MovieRecorderVideoResolutionTypeEnum.h @@ -1,5 +1,5 @@ -#ifndef __MOVIE_RECORDER_VIDEO_DIMENSIONS_TYPE_ENUM_H__ -#define __MOVIE_RECORDER_VIDEO_DIMENSIONS_TYPE_ENUM_H__ +#ifndef __MOVIE_RECORDER_VIDEO_RESOLUTION_TYPE_ENUM_H__ +#define __MOVIE_RECORDER_VIDEO_RESOLUTION_TYPE_ENUM_H__ /*LICENSE_START*/ /* @@ -28,7 +28,7 @@ namespace caret { -class MovieRecorderVideoDimensionsTypeEnum { +class MovieRecorderVideoResolutionTypeEnum { public: /** @@ -37,14 +37,18 @@ public: enum Enum { /** Custom */ CUSTOM, - /** HD */ + /** UHD */ + UHD_3840_2160, + /** Full HD */ + FULL_HD_1920_1080, + /** HD Ready */ HD_1280_720, /** SD */ SD_640_480 }; - ~MovieRecorderVideoDimensionsTypeEnum(); + ~MovieRecorderVideoResolutionTypeEnum(); static AString toName(Enum enumValue); @@ -64,15 +68,19 @@ public: static void getAllGuiNames(std::vector& allGuiNames, const bool isSorted); + static void getDimensions(const Enum enumValue, + int32_t& widthOut, + int32_t& heightOut); + private: - MovieRecorderVideoDimensionsTypeEnum(const Enum enumValue, + MovieRecorderVideoResolutionTypeEnum(const Enum enumValue, const AString& name, const AString& guiName); - static const MovieRecorderVideoDimensionsTypeEnum* findData(const Enum enumValue); + static const MovieRecorderVideoResolutionTypeEnum* findData(const Enum enumValue); /** Holds all instance of enum values and associated metadata */ - static std::vector enumData; + static std::vector enumData; /** Initialize instances that contain the enum values and metadata */ static void initialize(); @@ -96,11 +104,11 @@ private: AString guiName; }; -#ifdef __MOVIE_RECORDER_VIDEO_DIMENSIONS_TYPE_ENUM_DECLARE__ -std::vector MovieRecorderVideoDimensionsTypeEnum::enumData; -bool MovieRecorderVideoDimensionsTypeEnum::initializedFlag = false; -int32_t MovieRecorderVideoDimensionsTypeEnum::integerCodeCounter = 0; -#endif // __MOVIE_RECORDER_VIDEO_DIMENSIONS_TYPE_ENUM_DECLARE__ +#ifdef __MOVIE_RECORDER_VIDEO_RESOLUTION_TYPE_ENUM_DECLARE__ +std::vector MovieRecorderVideoResolutionTypeEnum::enumData; +bool MovieRecorderVideoResolutionTypeEnum::initializedFlag = false; +int32_t MovieRecorderVideoResolutionTypeEnum::integerCodeCounter = 0; +#endif // __MOVIE_RECORDER_VIDEO_RESOLUTION_TYPE_ENUM_DECLARE__ } // namespace -#endif //__MOVIE_RECORDER_VIDEO_DIMENSIONS_TYPE_ENUM_H__ +#endif //__MOVIE_RECORDER_VIDEO_RESOLUTION_TYPE_ENUM_H__ diff --git a/src/GuiQt/MovieRecordingDialog.cxx b/src/GuiQt/MovieRecordingDialog.cxx index c39150c90..5870ac5e9 100644 --- a/src/GuiQt/MovieRecordingDialog.cxx +++ b/src/GuiQt/MovieRecordingDialog.cxx @@ -125,8 +125,8 @@ MovieRecordingDialog::updateDialog() const MovieRecorderVideoFormatTypeEnum::Enum formatType = movieRecorder->getVideoFormatType(); m_movieRecorderVideoFormatTypeEnumComboBox->setSelectedItem(formatType); - const MovieRecorderVideoDimensionsTypeEnum::Enum dimType = movieRecorder->getVideoDimensionsType(); - m_movieRecorderVideoDimensionsTypeEnumComboBox->setSelectedItem(dimType); + const MovieRecorderVideoResolutionTypeEnum::Enum dimType = movieRecorder->getVideoDimensionsType(); + m_movieRecorderVideoResolutionTypeEnumComboBox->setSelectedItem(dimType); const MovieRecorderCaptureRegionTypeEnum::Enum captureType = movieRecorder->getCaptureRegionType(); m_movieRecorderCaptureRegionTypeComboBox->setSelectedItem(captureType); @@ -179,16 +179,7 @@ MovieRecordingDialog::updateCustomWidthHeightSpinBoxes() QSignalBlocker frameRateBlocker(m_frameRateSpinBox); m_frameRateSpinBox->setValue(movieRecorder->getFramesRate()); - bool customSpinBoxesEnabled(false); - switch (movieRecorder->getVideoDimensionsType()) { - case MovieRecorderVideoDimensionsTypeEnum::CUSTOM: - customSpinBoxesEnabled = true; - break; - case MovieRecorderVideoDimensionsTypeEnum::HD_1280_720: - break; - case MovieRecorderVideoDimensionsTypeEnum::SD_640_480: - break; - } + const bool customSpinBoxesEnabled(movieRecorder->getVideoDimensionsType() == MovieRecorderVideoResolutionTypeEnum::CUSTOM); m_customWidthSpinBox->setEnabled(customSpinBoxesEnabled); m_customHeightSpinBox->setEnabled(customSpinBoxesEnabled); } @@ -220,7 +211,7 @@ MovieRecordingDialog::updateFrameCountLabel() /* * Do not allow user to change image size once an image has been captured */ - m_movieRecorderVideoDimensionsTypeEnumComboBox->getWidget()->setEnabled(numberOfFrames <= 0); + m_movieRecorderVideoResolutionTypeEnumComboBox->getWidget()->setEnabled(numberOfFrames <= 0); } /** @@ -239,9 +230,9 @@ MovieRecordingDialog::windowIndexSelected(const int32_t windowIndex) * Called when video dimensions type is changed */ void -MovieRecordingDialog::movieRecorderVideoDimensionsTypeEnumComboBoxItemActivated() +MovieRecordingDialog::movieRecorderVideoResolutionTypeEnumComboBoxItemActivated() { - const MovieRecorderVideoDimensionsTypeEnum::Enum dimType = m_movieRecorderVideoDimensionsTypeEnumComboBox->getSelectedItem(); + const MovieRecorderVideoResolutionTypeEnum::Enum dimType = m_movieRecorderVideoResolutionTypeEnumComboBox->getSelectedItem(); SessionManager::get()->getMovieRecorder()->setVideoDimensionsType(dimType); updateCustomWidthHeightSpinBoxes(); } @@ -591,11 +582,11 @@ MovieRecordingDialog::createSettingsWidget() this, SLOT(movieRecorderVideoFormatTypeEnumComboBoxItemActivated())); QLabel* dimensionsLabel = new QLabel("Dimensions:"); - m_movieRecorderVideoDimensionsTypeEnumComboBox = new EnumComboBoxTemplate(this); - m_movieRecorderVideoDimensionsTypeEnumComboBox->getWidget()->setToolTip("Choose width and height of movie"); - m_movieRecorderVideoDimensionsTypeEnumComboBox->setup(); - QObject::connect(m_movieRecorderVideoDimensionsTypeEnumComboBox, SIGNAL(itemActivated()), - this, SLOT(movieRecorderVideoDimensionsTypeEnumComboBoxItemActivated())); + m_movieRecorderVideoResolutionTypeEnumComboBox = new EnumComboBoxTemplate(this); + m_movieRecorderVideoResolutionTypeEnumComboBox->getWidget()->setToolTip("Choose width and height of movie"); + m_movieRecorderVideoResolutionTypeEnumComboBox->setup(); + QObject::connect(m_movieRecorderVideoResolutionTypeEnumComboBox, SIGNAL(itemActivated()), + this, SLOT(movieRecorderVideoResolutionTypeEnumComboBoxItemActivated())); QLabel* customLabel = new QLabel("Custom Dimensions:"); m_customWidthSpinBox = new QSpinBox(); @@ -637,7 +628,7 @@ MovieRecordingDialog::createSettingsWidget() row, 1, 1, 2, Qt::AlignLeft); row++; gridLayout->addWidget(dimensionsLabel, row, 0); - gridLayout->addWidget(m_movieRecorderVideoDimensionsTypeEnumComboBox->getWidget(), + gridLayout->addWidget(m_movieRecorderVideoResolutionTypeEnumComboBox->getWidget(), row, 1, 1, 2, Qt::AlignLeft); row++; gridLayout->addWidget(customLabel, row, 0); diff --git a/src/GuiQt/MovieRecordingDialog.h b/src/GuiQt/MovieRecordingDialog.h index 7ef287fec..f4fd969d0 100644 --- a/src/GuiQt/MovieRecordingDialog.h +++ b/src/GuiQt/MovieRecordingDialog.h @@ -64,7 +64,7 @@ namespace caret { virtual void receiveEvent(Event* event); private slots: - void movieRecorderVideoDimensionsTypeEnumComboBoxItemActivated(); + void movieRecorderVideoResolutionTypeEnumComboBoxItemActivated(); void movieRecorderVideoFormatTypeEnumComboBoxItemActivated(); @@ -111,7 +111,7 @@ namespace caret { QSpinBox* m_manualCaptureSecondsSpinBox; - EnumComboBoxTemplate* m_movieRecorderVideoDimensionsTypeEnumComboBox; + EnumComboBoxTemplate* m_movieRecorderVideoResolutionTypeEnumComboBox; EnumComboBoxTemplate* m_movieRecorderVideoFormatTypeEnumComboBox; -- GitLab From 990a5171f2c17733ae1a5756e99b4a137a69dc44 Mon Sep 17 00:00:00 2001 From: John Harwell Date: Wed, 13 Feb 2019 16:15:51 -0600 Subject: [PATCH 187/427] Fixed some compilation issues on Linux --- src/Brain/MovieRecorder.cxx | 3 ++- src/Common/WuQMacroGroupXmlStreamReader.cxx | 2 +- src/Files/SceneFile.cxx | 2 +- src/Files/SceneFileXmlStreamReader.cxx | 2 +- src/GuiQt/WbMacroCustomOperationManager.cxx | 2 +- src/Scenes/SceneInfoXmlStreamReader.cxx | 2 +- src/Scenes/SceneXmlStreamReader.cxx | 4 ++-- 7 files changed, 9 insertions(+), 8 deletions(-) diff --git a/src/Brain/MovieRecorder.cxx b/src/Brain/MovieRecorder.cxx index cfd6a2131..765f91861 100644 --- a/src/Brain/MovieRecorder.cxx +++ b/src/Brain/MovieRecorder.cxx @@ -493,7 +493,8 @@ MovieRecorder::createMovie(AString& errorMessageOut) + m_tempImageFileNameSuffix); QString workbenchHomeDir = SystemUtilities::getWorkbenchHome(); - const QString ffmpegDir = qEnvironmentVariable("WORKBENCH_FFMPEG_DIR"); + /* Qt after 5.? const QString ffmpegDir = qEnvironmentVariable("WORKBENCH_FFMPEG_DIR"); */ + const QString ffmpegDir = qgetenv("WORKBENCH_FFMPEG_DIR").constData(); if ( ! ffmpegDir.isEmpty()) { workbenchHomeDir = ffmpegDir; } diff --git a/src/Common/WuQMacroGroupXmlStreamReader.cxx b/src/Common/WuQMacroGroupXmlStreamReader.cxx index b61f942a2..3e048c20c 100644 --- a/src/Common/WuQMacroGroupXmlStreamReader.cxx +++ b/src/Common/WuQMacroGroupXmlStreamReader.cxx @@ -545,7 +545,7 @@ WuQMacroGroupXmlStreamReader::readMacroCommandAttributesVersionOne(QXmlStreamRea WuQMacroWidgetTypeEnum::Enum widgetType = WuQMacroWidgetTypeEnum::fromName(widgetTypeString.toString(), &objectTypeValid); if (! objectTypeValid) { - es.append(widgetTypeString + es.append(widgetTypeString.toString() + " is not valid for attribute " + ATTRIBUTE_WIDGET_TYPE + " "); diff --git a/src/Files/SceneFile.cxx b/src/Files/SceneFile.cxx index c1db01496..96d8e1b02 100644 --- a/src/Files/SceneFile.cxx +++ b/src/Files/SceneFile.cxx @@ -688,7 +688,7 @@ SceneFile::readFileSaxReader(const AString& filenameIn) SceneFileSaxReader saxReader(this, filename); - std::auto_ptr parser(XmlSaxParser::createXmlParser()); + std::unique_ptr parser(XmlSaxParser::createXmlParser()); try { parser->parseFile(filename, &saxReader); } diff --git a/src/Files/SceneFileXmlStreamReader.cxx b/src/Files/SceneFileXmlStreamReader.cxx index 8b2e202ea..853eeda5f 100644 --- a/src/Files/SceneFileXmlStreamReader.cxx +++ b/src/Files/SceneFileXmlStreamReader.cxx @@ -140,7 +140,7 @@ SceneFileXmlStreamReader::readFileContent(QXmlStreamReader& xmlReader, xmlReader.readNextStartElement(); if (xmlReader.name() != ELEMENT_SCENE_FILE) { xmlReader.raiseError("First element is \"" - + xmlReader.name() + + xmlReader.name().toString() + "\" but should be " + ELEMENT_SCENE_FILE); return; diff --git a/src/GuiQt/WbMacroCustomOperationManager.cxx b/src/GuiQt/WbMacroCustomOperationManager.cxx index 709b70113..0b1938b93 100644 --- a/src/GuiQt/WbMacroCustomOperationManager.cxx +++ b/src/GuiQt/WbMacroCustomOperationManager.cxx @@ -320,7 +320,7 @@ WbMacroCustomOperationManager::newInstanceOfCustomOperationMacroCommand(const QS WbMacroCustomOperationBase* WbMacroCustomOperationManager::createCommand(const WbMacroCustomOperationTypeEnum::Enum operationType) { - WbMacroCustomOperationBase* operationOut; + WbMacroCustomOperationBase* operationOut(NULL); switch (operationType) { case WbMacroCustomOperationTypeEnum::MODEL_ROTATION: diff --git a/src/Scenes/SceneInfoXmlStreamReader.cxx b/src/Scenes/SceneInfoXmlStreamReader.cxx index 86e7131e6..3bed9a3ac 100644 --- a/src/Scenes/SceneInfoXmlStreamReader.cxx +++ b/src/Scenes/SceneInfoXmlStreamReader.cxx @@ -76,7 +76,7 @@ SceneInfoXmlStreamReader::readSceneInfo(QXmlStreamReader& xmlReader, if (xmlReader.name() != ELEMENT_SCENE_INFO) { xmlReader.raiseError("First element is \"" - + xmlReader.name() + + xmlReader.name().toString() + "\" but should be " + ELEMENT_SCENE_INFO); return; diff --git a/src/Scenes/SceneXmlStreamReader.cxx b/src/Scenes/SceneXmlStreamReader.cxx index 084cf9c42..33e3bcb93 100644 --- a/src/Scenes/SceneXmlStreamReader.cxx +++ b/src/Scenes/SceneXmlStreamReader.cxx @@ -363,7 +363,7 @@ SceneXmlStreamReader::readSceneObjectSingle(QXmlStreamReader& xmlReader) else { AString msg("Should not find a start element when reading a primitive type (not a class).." "Element name \"" - + xmlReader.name() + + xmlReader.name().toString() + "\" at line " + AString::number(xmlReader.lineNumber()) + " column " @@ -469,7 +469,7 @@ SceneXmlStreamReader::readSceneObjectArray(QXmlStreamReader& xmlReader) SceneFloatArray* floatArray(NULL); SceneIntegerArray* integerArray(NULL); SceneStringArray* stringArray(NULL); - SceneUnsignedByteArray* unsignedByteArray; + SceneUnsignedByteArray* unsignedByteArray(NULL); switch (dataType) { case SceneObjectDataTypeEnum::SCENE_BOOLEAN: booleanArray = new SceneBooleanArray(name, -- GitLab From a0888151fd458e84cbb28f1ceff4f634f9f0d427 Mon Sep 17 00:00:00 2001 From: John Harwell Date: Thu, 14 Feb 2019 10:44:53 -0600 Subject: [PATCH 188/427] WB-834 Macros: Correct compilation errors and warnings on Linux and Windows. --- src/GuiQt/WuQMacroDialog.cxx | 3 -- src/GuiQt/WuQMacroExecutor.cxx | 64 ++++++++++++---------------------- 2 files changed, 22 insertions(+), 45 deletions(-) diff --git a/src/GuiQt/WuQMacroDialog.cxx b/src/GuiQt/WuQMacroDialog.cxx index c4e43241e..4a628b4fa 100644 --- a/src/GuiQt/WuQMacroDialog.cxx +++ b/src/GuiQt/WuQMacroDialog.cxx @@ -147,9 +147,6 @@ WuQMacroDialog::WuQMacroDialog(QWidget* parent) updateDialogContents(); - QPushButton* closeButton = m_dialogButtonBox->button(QDialogButtonBox::Close); - CaretAssert(closeButton); - /* * Disable auto default for all push buttons */ diff --git a/src/GuiQt/WuQMacroExecutor.cxx b/src/GuiQt/WuQMacroExecutor.cxx index 2c7770a7b..9857ba7e8 100644 --- a/src/GuiQt/WuQMacroExecutor.cxx +++ b/src/GuiQt/WuQMacroExecutor.cxx @@ -186,14 +186,12 @@ WuQMacroExecutor::moveMouseToWidgetImplementation(QObject* moveToObject, * Object may not be a widget so find ancestor * that is a widget */ - bool usingParentFlag(false); QWidget* moveToWidget(NULL); while ((moveToWidget == NULL) && (moveToObject != NULL)) { moveToWidget = qobject_cast(moveToObject); if (moveToWidget == NULL) { moveToObject = moveToObject->parent(); - usingParentFlag = true; } } @@ -447,7 +445,7 @@ WuQMacroExecutor::stopMacro() * True if the macro completed without errors, else false. */ bool -WuQMacroExecutor::runMacroCommand(QWidget* parentWidget, +WuQMacroExecutor::runMacroCommand(QWidget* /*parentWidget*/, const WuQMacroCommand* macroCommand, QObject* object, QString& errorMessageOut) const @@ -540,7 +538,7 @@ WuQMacroExecutor::runMacroCommand(QWidget* parentWidget, } /** - * Run a QAction selection command + * Run a non-checkable QAction selection command * * @param macroCommand * Macro command that is run @@ -552,7 +550,7 @@ WuQMacroExecutor::runMacroCommand(QWidget* parentWidget, * Set to true if unable to cast object to widget type */ void -WuQMacroExecutor::runActionCommand(const WuQMacroCommand* macroCommand, +WuQMacroExecutor::runActionCommand(const WuQMacroCommand* /*macroCommand*/, QObject* object, QString& /*errorMessageOut*/, bool& castFailureFlagOut) const @@ -560,11 +558,10 @@ WuQMacroExecutor::runActionCommand(const WuQMacroCommand* macroCommand, QAction* action = qobject_cast(object); if (action != NULL) { moveMouseToWidget(action); - CaretAssert(macroCommand->getNumberOfParameters() > 0); - const WuQMacroCommandParameter* parameterOne = macroCommand->getParameterAtIndex(0); - CaretAssert(parameterOne); - CaretAssert(parameterOne->getDataType() == WuQMacroDataValueTypeEnum::NONE); + /* + * Always emit true for a non-checkable action + */ WuQMacroSignalEmitter signalEmitter; signalEmitter.emitQActionSignal(action, true); @@ -869,10 +866,9 @@ WuQMacroExecutor::runDoubleSpinBoxCommand(const WuQMacroCommand* macroCommand, const WuQMacroCommandParameter* parameterOne = macroCommand->getParameterAtIndex(0); CaretAssert(parameterOne); + CaretAssert(parameterOne->getDataType() == WuQMacroDataValueTypeEnum::FLOAT); const QVariant dataValue = parameterOne->getValue(); - const WuQMacroDataValueTypeEnum::Enum dataValueType = parameterOne->getDataType(); - - CaretAssert(dataValueType == WuQMacroDataValueTypeEnum::FLOAT); + moveMouseToWidget(doubleSpinBox); WuQMacroSignalEmitter signalEmitter; signalEmitter.emitQDoubleSpinBoxSignal(doubleSpinBox, @@ -907,11 +903,10 @@ WuQMacroExecutor::runLineEditCommand(const WuQMacroCommand* macroCommand, const WuQMacroCommandParameter* parameterOne = macroCommand->getParameterAtIndex(0); CaretAssert(parameterOne); + CaretAssert(parameterOne->getDataType() == WuQMacroDataValueTypeEnum::STRING); const QVariant dataValue = parameterOne->getValue(); - const WuQMacroDataValueTypeEnum::Enum dataValueType = parameterOne->getDataType(); moveMouseToWidget(lineEdit); - CaretAssert(dataValueType == WuQMacroDataValueTypeEnum::STRING); WuQMacroSignalEmitter signalEmitter; signalEmitter.emitQLineEditSignal(lineEdit, dataValue.toString()); @@ -948,13 +943,10 @@ WuQMacroExecutor::runListWidgetCommand(const WuQMacroCommand* macroCommand, const WuQMacroCommandParameter* parameterTwo = macroCommand->getParameterAtIndex(1); CaretAssert(parameterTwo); + CaretAssert(parameterOne->getDataType() == WuQMacroDataValueTypeEnum::STRING); + CaretAssert(parameterTwo->getDataType() == WuQMacroDataValueTypeEnum::INTEGER); const QVariant dataValue = parameterOne->getValue(); - const WuQMacroDataValueTypeEnum::Enum dataValueType = parameterOne->getDataType(); const QVariant dataValueTwo = parameterTwo->getValue(); - const WuQMacroDataValueTypeEnum::Enum dataValueTypeTwo = parameterTwo->getDataType(); - - CaretAssert(dataValueType == WuQMacroDataValueTypeEnum::STRING); - CaretAssert(dataValueTypeTwo == WuQMacroDataValueTypeEnum::INTEGER); QListWidgetItem* textItem(NULL); QListWidgetItem* indexItem(NULL); @@ -1018,13 +1010,10 @@ WuQMacroExecutor::runMenuCommand(const WuQMacroCommand* macroCommand, CaretAssert(parameterOne); const WuQMacroCommandParameter* parameterTwo = macroCommand->getParameterAtIndex(1); + CaretAssert(parameterOne->getDataType() == WuQMacroDataValueTypeEnum::STRING); + CaretAssert(parameterTwo->getDataType() == WuQMacroDataValueTypeEnum::INTEGER); const QVariant dataValue = parameterOne->getValue(); - const WuQMacroDataValueTypeEnum::Enum dataValueType = parameterOne->getDataType(); const QVariant dataValueTwo = parameterTwo->getValue(); - const WuQMacroDataValueTypeEnum::Enum dataValueTypeTwo = parameterTwo->getDataType(); - - CaretAssert(dataValueType == WuQMacroDataValueTypeEnum::STRING); - CaretAssert(dataValueTypeTwo == WuQMacroDataValueTypeEnum::INTEGER); QAction* textAction(NULL); QAction* indexAction(NULL); @@ -1175,11 +1164,10 @@ WuQMacroExecutor::runPushButtonCommand(const WuQMacroCommand* macroCommand, const WuQMacroCommandParameter* parameterOne = macroCommand->getParameterAtIndex(0); CaretAssert(parameterOne); + CaretAssert(parameterOne->getDataType() == WuQMacroDataValueTypeEnum::NONE); const QVariant dataValue = parameterOne->getValue(); - const WuQMacroDataValueTypeEnum::Enum dataValueType = parameterOne->getDataType(); moveMouseToWidget(pushButton); - CaretAssert(dataValueType == WuQMacroDataValueTypeEnum::NONE); WuQMacroSignalEmitter signalEmitter; signalEmitter.emitQPushButtonSignal(pushButton, true); @@ -1213,11 +1201,10 @@ WuQMacroExecutor::runPushButtonCheckableCommand(const WuQMacroCommand* macroComm const WuQMacroCommandParameter* parameterOne = macroCommand->getParameterAtIndex(0); CaretAssert(parameterOne); + CaretAssert(parameterOne->getDataType() == WuQMacroDataValueTypeEnum::BOOLEAN); const QVariant dataValue = parameterOne->getValue(); - const WuQMacroDataValueTypeEnum::Enum dataValueType = parameterOne->getDataType(); moveMouseToWidget(pushButton); - CaretAssert(dataValueType == WuQMacroDataValueTypeEnum::BOOLEAN); WuQMacroSignalEmitter signalEmitter; signalEmitter.emitQPushButtonSignal(pushButton, dataValue.toBool()); @@ -1251,11 +1238,10 @@ WuQMacroExecutor::runRadioButtonCommand(const WuQMacroCommand* macroCommand, const WuQMacroCommandParameter* parameterOne = macroCommand->getParameterAtIndex(0); CaretAssert(parameterOne); + CaretAssert(parameterOne->getDataType() == WuQMacroDataValueTypeEnum::NONE); const QVariant dataValue = parameterOne->getValue(); - const WuQMacroDataValueTypeEnum::Enum dataValueType = parameterOne->getDataType(); moveMouseToWidget(radioButton); - CaretAssert(dataValueType == WuQMacroDataValueTypeEnum::NONE); WuQMacroSignalEmitter signalEmitter; /* Note: Radio buttons are always exclusive so always use a true value */ signalEmitter.emitQRadioButtonSignal(radioButton, @@ -1290,11 +1276,10 @@ WuQMacroExecutor::runSliderCommand(const WuQMacroCommand* macroCommand, const WuQMacroCommandParameter* parameterOne = macroCommand->getParameterAtIndex(0); CaretAssert(parameterOne); + CaretAssert(parameterOne->getDataType() == WuQMacroDataValueTypeEnum::INTEGER); const QVariant dataValue = parameterOne->getValue(); - const WuQMacroDataValueTypeEnum::Enum dataValueType = parameterOne->getDataType(); moveMouseToWidget(slider); - CaretAssert(dataValueType == WuQMacroDataValueTypeEnum::INTEGER); WuQMacroSignalEmitter signalEmitter; signalEmitter.emitQSliderSignal(slider, dataValue.toInt()); @@ -1328,11 +1313,10 @@ WuQMacroExecutor::runSpinBoxCommand(const WuQMacroCommand* macroCommand, const WuQMacroCommandParameter* parameterOne = macroCommand->getParameterAtIndex(0); CaretAssert(parameterOne); + CaretAssert(parameterOne->getDataType() == WuQMacroDataValueTypeEnum::INTEGER); const QVariant dataValue = parameterOne->getValue(); - const WuQMacroDataValueTypeEnum::Enum dataValueType = parameterOne->getDataType(); moveMouseToWidget(spinBox); - CaretAssert(dataValueType == WuQMacroDataValueTypeEnum::INTEGER); WuQMacroSignalEmitter signalEmitter; signalEmitter.emitQSpinBoxSignal(spinBox, dataValue.toInt()); @@ -1367,12 +1351,11 @@ WuQMacroExecutor::runTabBarCommand(const WuQMacroCommand* macroCommand, CaretAssert(parameterOne); const WuQMacroCommandParameter* parameterTwo = macroCommand->getParameterAtIndex(1); + CaretAssert(parameterTwo->getDataType() == WuQMacroDataValueTypeEnum::INTEGER); const QVariant dataValue = parameterOne->getValue(); const WuQMacroDataValueTypeEnum::Enum dataValueType = parameterOne->getDataType(); const QVariant dataValueTwo = parameterTwo->getValue(); - const WuQMacroDataValueTypeEnum::Enum dataValueTypeTwo = parameterTwo->getDataType(); - CaretAssert(dataValueTypeTwo == WuQMacroDataValueTypeEnum::INTEGER); int32_t tabIndex = dataValueTwo.toInt(); if (dataValueType == WuQMacroDataValueTypeEnum::STRING) { /* @@ -1435,12 +1418,11 @@ WuQMacroExecutor::runTabWidgetCommand(const WuQMacroCommand* macroCommand, CaretAssert(parameterOne); const WuQMacroCommandParameter* parameterTwo = macroCommand->getParameterAtIndex(1); + CaretAssert(parameterTwo->getDataType() == WuQMacroDataValueTypeEnum::INTEGER); const QVariant dataValue = parameterOne->getValue(); const WuQMacroDataValueTypeEnum::Enum dataValueType = parameterOne->getDataType(); const QVariant dataValueTwo = parameterTwo->getValue(); - const WuQMacroDataValueTypeEnum::Enum dataValueTypeTwo = parameterTwo->getDataType(); - CaretAssert(dataValueTypeTwo == WuQMacroDataValueTypeEnum::INTEGER); QTabBar* tabBar = tabWidget->tabBar(); CaretAssert(tabBar); int32_t tabIndex = dataValueTwo.toInt(); @@ -1505,11 +1487,10 @@ WuQMacroExecutor::runToolButtonCommand(const WuQMacroCommand* macroCommand, const WuQMacroCommandParameter* parameterOne = macroCommand->getParameterAtIndex(0); CaretAssert(parameterOne); + CaretAssert(parameterOne->getDataType() == WuQMacroDataValueTypeEnum::NONE); const QVariant dataValue = parameterOne->getValue(); - const WuQMacroDataValueTypeEnum::Enum dataValueType = parameterOne->getDataType(); moveMouseToWidget(toolButton); - CaretAssert(dataValueType == WuQMacroDataValueTypeEnum::NONE); WuQMacroSignalEmitter signalEmitter; signalEmitter.emitQToolButtonSignal(toolButton, true); @@ -1543,11 +1524,10 @@ WuQMacroExecutor::runToolButtonCheckableCommand(const WuQMacroCommand* macroComm const WuQMacroCommandParameter* parameterOne = macroCommand->getParameterAtIndex(0); CaretAssert(parameterOne); + CaretAssert(parameterOne->getDataType() == WuQMacroDataValueTypeEnum::BOOLEAN); const QVariant dataValue = parameterOne->getValue(); - const WuQMacroDataValueTypeEnum::Enum dataValueType = parameterOne->getDataType(); moveMouseToWidget(toolButton); - CaretAssert(dataValueType == WuQMacroDataValueTypeEnum::BOOLEAN); WuQMacroSignalEmitter signalEmitter; signalEmitter.emitQToolButtonSignal(toolButton, dataValue.toBool()); -- GitLab From fbaa54c5d66a5d0e55e295ef5789612a2226a3bc Mon Sep 17 00:00:00 2001 From: John Harwell Date: Thu, 14 Feb 2019 16:04:21 -0600 Subject: [PATCH 189/427] Added "cross-fade" commands for cross-fade between two overlays and from volume overlay to the surface. --- src/GuiQt/CMakeLists.txt | 4 + src/GuiQt/WbMacroCustomOperationBase.cxx | 10 + src/GuiQt/WbMacroCustomOperationBase.h | 2 + src/GuiQt/WbMacroCustomOperationManager.cxx | 8 + ...WbMacroCustomOperationOverlayCrossFade.cxx | 286 ++++++++++++++++++ .../WbMacroCustomOperationOverlayCrossFade.h | 69 +++++ src/GuiQt/WbMacroCustomOperationTypeEnum.cxx | 10 +- src/GuiQt/WbMacroCustomOperationTypeEnum.h | 6 +- ...ustomOperationVolumeToSurfaceCrossFade.cxx | 231 ++++++++++++++ ...oCustomOperationVolumeToSurfaceCrossFade.h | 68 +++++ src/GuiQt/WbMacroHelper.cxx | 9 +- 11 files changed, 698 insertions(+), 5 deletions(-) create mode 100644 src/GuiQt/WbMacroCustomOperationOverlayCrossFade.cxx create mode 100644 src/GuiQt/WbMacroCustomOperationOverlayCrossFade.h create mode 100644 src/GuiQt/WbMacroCustomOperationVolumeToSurfaceCrossFade.cxx create mode 100644 src/GuiQt/WbMacroCustomOperationVolumeToSurfaceCrossFade.h diff --git a/src/GuiQt/CMakeLists.txt b/src/GuiQt/CMakeLists.txt index b22020b12..40eb1e91b 100644 --- a/src/GuiQt/CMakeLists.txt +++ b/src/GuiQt/CMakeLists.txt @@ -454,9 +454,11 @@ WbMacroCustomDataTypeEnum.h WbMacroCustomOperationBase.h WbMacroCustomOperationManager.h WbMacroCustomOperationModelRotation.h +WbMacroCustomOperationOverlayCrossFade.h WbMacroCustomOperationSurfaceInterpolation.h WbMacroCustomOperationTypeEnum.h WbMacroCustomOperationVolumeSliceIncrement.h +WbMacroCustomOperationVolumeToSurfaceCrossFade.h WbMacroHelper.h WuQCollapsibleWidget.h WuQDataEntryDialog.h @@ -709,9 +711,11 @@ WbMacroCustomDataTypeEnum.cxx WbMacroCustomOperationBase.cxx WbMacroCustomOperationManager.cxx WbMacroCustomOperationModelRotation.cxx +WbMacroCustomOperationOverlayCrossFade.cxx WbMacroCustomOperationSurfaceInterpolation.cxx WbMacroCustomOperationTypeEnum.cxx WbMacroCustomOperationVolumeSliceIncrement.cxx +WbMacroCustomOperationVolumeToSurfaceCrossFade.cxx WbMacroHelper.cxx WuQCollapsibleWidget.cxx WuQDataEntryDialog.cxx diff --git a/src/GuiQt/WbMacroCustomOperationBase.cxx b/src/GuiQt/WbMacroCustomOperationBase.cxx index 41c042266..5b3f8b2b7 100644 --- a/src/GuiQt/WbMacroCustomOperationBase.cxx +++ b/src/GuiQt/WbMacroCustomOperationBase.cxx @@ -28,6 +28,7 @@ #include "CaretAssert.h" #include "EventGraphicsUpdateAllWindows.h" #include "EventManager.h" +#include "EventSurfaceColoringInvalidate.h" #include "EventSurfacesGet.h" #include "EventUserInterfaceUpdate.h" #include "MovieRecorder.h" @@ -186,6 +187,15 @@ WbMacroCustomOperationBase::updateUserInterface() EventManager::get()->sendEvent(EventUserInterfaceUpdate().getPointer()); } +/** + * Update surface coloring + */ +void +WbMacroCustomOperationBase::updateSurfaceColoring() +{ + EventManager::get()->sendEvent(EventSurfaceColoringInvalidate().getPointer()); +} + /** * Get number of steps and sleep time. If a movie is being recorded, the number of * steps is set so that the command will run for the requested duration using the diff --git a/src/GuiQt/WbMacroCustomOperationBase.h b/src/GuiQt/WbMacroCustomOperationBase.h index 483e7cd75..323fae27a 100644 --- a/src/GuiQt/WbMacroCustomOperationBase.h +++ b/src/GuiQt/WbMacroCustomOperationBase.h @@ -89,6 +89,8 @@ namespace caret { void updateGraphics(); + void updateSurfaceColoring(); + void updateUserInterface(); const WbMacroCustomOperationTypeEnum::Enum m_operationType; diff --git a/src/GuiQt/WbMacroCustomOperationManager.cxx b/src/GuiQt/WbMacroCustomOperationManager.cxx index 0b1938b93..dd76c0945 100644 --- a/src/GuiQt/WbMacroCustomOperationManager.cxx +++ b/src/GuiQt/WbMacroCustomOperationManager.cxx @@ -33,9 +33,11 @@ #include "Surface.h" #include "WbMacroCustomDataTypeEnum.h" #include "WbMacroCustomOperationModelRotation.h" +#include "WbMacroCustomOperationOverlayCrossFade.h" #include "WbMacroCustomOperationSurfaceInterpolation.h" #include "WbMacroCustomOperationTypeEnum.h" #include "WbMacroCustomOperationVolumeSliceIncrement.h" +#include "WbMacroCustomOperationVolumeToSurfaceCrossFade.h" #include "WuQMacroCommand.h" #include "WuQMacroCommandParameter.h" #include "WuQMessageBox.h" @@ -326,12 +328,18 @@ WbMacroCustomOperationManager::createCommand(const WbMacroCustomOperationTypeEnu case WbMacroCustomOperationTypeEnum::MODEL_ROTATION: operationOut = new WbMacroCustomOperationModelRotation(); break; + case WbMacroCustomOperationTypeEnum::OVERLAY_CROSS_FADE: + operationOut = new WbMacroCustomOperationOverlayCrossFade(); + break; case WbMacroCustomOperationTypeEnum::SURFACE_INTERPOLATION: operationOut = new WbMacroCustomOperationSurfaceInterpolation(); break; case WbMacroCustomOperationTypeEnum::VOLUME_SLICE_INCREMENT: operationOut = new WbMacroCustomOperationVolumeSliceIncrement(); break; + case WbMacroCustomOperationTypeEnum::VOLUME_TO_SURFACE_CROSS_FADE: + operationOut = new WbMacroCustomOperationVolumeToSurfaceCrossFade(); + break; } CaretAssert(operationOut); diff --git a/src/GuiQt/WbMacroCustomOperationOverlayCrossFade.cxx b/src/GuiQt/WbMacroCustomOperationOverlayCrossFade.cxx new file mode 100644 index 000000000..4e4024fcb --- /dev/null +++ b/src/GuiQt/WbMacroCustomOperationOverlayCrossFade.cxx @@ -0,0 +1,286 @@ + +/*LICENSE_START*/ +/* + * Copyright (C) 2019 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + +#define __WB_MACRO_CUSTOM_OPERATION_OVERLAY_CROSS_FADE_DECLARE__ +#include "WbMacroCustomOperationOverlayCrossFade.h" +#undef __WB_MACRO_CUSTOM_OPERATION_OVERLAY_CROSS_FADE_DECLARE__ + +#include "BrainBrowserWindow.h" +#include "BrowserTabContent.h" +#include "CaretAssert.h" +#include "GuiManager.h" +#include "Overlay.h" +#include "OverlaySet.h" +#include "WbMacroCustomDataTypeEnum.h" +#include "WbMacroCustomOperationTypeEnum.h" +#include "WuQMacroCommand.h" +#include "WuQMacroCommandParameter.h" + +using namespace caret; + + + +/** + * \class caret::WbMacroCustomOperationOverlayCrossFade + * \brief Custom Macro Command for Surface Interpolation + * \ingroup GuiQt + */ + +/** + * Constructor. + */ +WbMacroCustomOperationOverlayCrossFade::WbMacroCustomOperationOverlayCrossFade() +: WbMacroCustomOperationBase(WbMacroCustomOperationTypeEnum::OVERLAY_CROSS_FADE) +{ +} + +/** + * Destructor. + */ +WbMacroCustomOperationOverlayCrossFade::~WbMacroCustomOperationOverlayCrossFade() +{ +} + +/** + * Get a new instance of the macro command + * + * @return + * Pointer to command or NULL if not valid + * Use getErrorMessage() for error information if NULL returned + */ +WuQMacroCommand* +WbMacroCustomOperationOverlayCrossFade::createCommand() +{ + const int32_t versionOne(1); + + const QString description("Crossfade (blend) from one overlay to another:\n" + "(1) The opacity of the \"Fade to Overlay\" is set to zero;\n" + "(2) The opacity of the \"Fade from Overlay\" is set to one;\n" + "(3) The opacity of the \"Fade to Overlay\" increases until it is one\n" + " and simultaneously, the opacity of the \"Face from Overlay\"\n" + " decreases until it reaches zero.\n"); + QString errorMessage; + WuQMacroCommand* command = WuQMacroCommand::newInstanceCustomCommand(WbMacroCustomOperationTypeEnum::toName(getOperationType()), + versionOne, + "none", + "Overlay CrossFade", + description, + 1.0, + errorMessage); + if (command != NULL) { + command->addParameter(WuQMacroDataValueTypeEnum::INTEGER, + "Fade to Overlay", + (int)1); + command->addParameter(WuQMacroDataValueTypeEnum::INTEGER, + "Fade from Overlay", + (int)2); + command->addParameter(WuQMacroDataValueTypeEnum::FLOAT, + "Duration (secs)", + (float)10.0); + } + else { + appendToErrorMessage(errorMessage); + } + + return command; +} + +/** + * Execute the macro command + * + * @param parent + * Parent widget for any dialogs + * @param macroCommand + * macro command to run + * @return + * True if command executed successfully, else false + * Use getErrorMessage() for error information if false returned + */ +bool +WbMacroCustomOperationOverlayCrossFade::executeCommand(QWidget* parent, + const WuQMacroCommand* macroCommand) +{ + CaretAssert(parent); + CaretAssert(macroCommand); + + validateCorrectNumberOfParameters(macroCommand); + + BrainBrowserWindow* bbw = qobject_cast(parent); + if (bbw == NULL) { + appendToErrorMessage("Parent for running surface macro is not a browser window"); + return false; + } + + BrowserTabContent* tabContent = bbw->getBrowserTabContent(); + if (tabContent == NULL) { + appendToErrorMessage("No tab is selected in browser window"); + return false; + } + + Model* model = tabContent->getModelForDisplay(); + if (model == NULL) { + appendToErrorMessage("No model is displayed"); + return false; + } + + bool validModelTypeFlag(false); + switch (model->getModelType()) { + case ModelTypeEnum::MODEL_TYPE_CHART: + case ModelTypeEnum::MODEL_TYPE_CHART_TWO: + case ModelTypeEnum::MODEL_TYPE_INVALID: + break; + case ModelTypeEnum::MODEL_TYPE_SURFACE: + case ModelTypeEnum::MODEL_TYPE_SURFACE_MONTAGE: + case ModelTypeEnum::MODEL_TYPE_VOLUME_SLICES: + case ModelTypeEnum::MODEL_TYPE_WHOLE_BRAIN: + validModelTypeFlag = true; + break; + } + if ( ! validModelTypeFlag) { + appendToErrorMessage("For Overlay CrossFace, model must be a brain model"); + return false; + } + + /* + * Get overlay indices and subtract one from them since they stat at 1 + */ + const int32_t fadeToOverlayIndex(macroCommand->getParameterAtIndex(0)->getValue().toInt() - 1); + const int32_t fadeFromOverlayIndex(macroCommand->getParameterAtIndex(1)->getValue().toInt() - 1); + const float durationSeconds(macroCommand->getParameterAtIndex(2)->getValue().toFloat()); + + OverlaySet* overlaySet = tabContent->getOverlaySet(); + CaretAssert(overlaySet); + const int32_t numberOfOverlays = overlaySet->getNumberOfDisplayedOverlays(); + + if ((fadeToOverlayIndex < 0) + || (fadeToOverlayIndex >= numberOfOverlays)) { + appendToErrorMessage("Fade To Overlay Index is not a valid overlay"); + } + if ((fadeFromOverlayIndex < 0) + || (fadeFromOverlayIndex >= numberOfOverlays)) { + appendToErrorMessage("Fade From Overlay Index is not a valid overlay"); + } + if ( ! getErrorMessage().isEmpty()) { + return false; + } + + Overlay* fadeToOverlay = overlaySet->getOverlay(fadeToOverlayIndex); + if ( ! fadeToOverlay->isEnabled()) { + appendToErrorMessage("Fade To Overlay is not enabled"); + } + Overlay* fadeFromOverlay = overlaySet->getOverlay(fadeFromOverlayIndex); + if ( ! fadeFromOverlay->isEnabled()) { + appendToErrorMessage("Fade From Overlay is not enabled"); + } + if ( ! getErrorMessage().isEmpty()) { + return false; + } + + CaretMappableDataFile* fadeToMapFile(NULL); + int32_t fadeToMapIndex(-1); + fadeToOverlay->getSelectionData(fadeToMapFile, fadeToMapIndex); + if (fadeToMapFile == NULL) { + appendToErrorMessage("Fade To Overlay does not contain a valid data file"); + } + + CaretMappableDataFile* fadeFromMapFile(NULL); + int32_t fadeFromMapIndex(-1); + fadeToOverlay->getSelectionData(fadeFromMapFile, fadeFromMapIndex); + if (fadeFromMapFile == NULL) { + appendToErrorMessage("Fade From Overlay does not contain a valid data file"); + } + + if ( ! getErrorMessage().isEmpty()) { + return false; + } + + bool successFlag = performCrossFade(fadeToOverlay, + fadeFromOverlay, + durationSeconds); + + return successFlag; +} + +/** + * Interpolate from starting to ending surface + * + * @param fadeToOverlay + * Overlay that starts with opacity zero and increases to one + * @param fadeFromOverlay + * Overlay that starts with opacity one and decreases to zero + * @param durationSeconds + * Total duration for cross fade + * @return + * True if successful, else false + */ +bool +WbMacroCustomOperationOverlayCrossFade::performCrossFade(Overlay* fadeToOverlay, + Overlay* fadeFromOverlay, + const float durationSeconds) +{ + CaretAssert(fadeToOverlay); + CaretAssert(fadeFromOverlay); + + const float defaultNumberOfSteps(25.0); + float numberOfSteps(0.0); + float iterationSleepTime(0.0); + getNumberOfStepsAndSleepTime(defaultNumberOfSteps, + durationSeconds, + numberOfSteps, + iterationSleepTime); + + const float opacityDelta = 1.0 / numberOfSteps; + float fadeToOpacity(0.0); + float fadeFromOpacity(1.0); + + /* + * Initialize the opacities + */ + + for (int iStep = 0; iStep < numberOfSteps; iStep++) { + fadeToOverlay->setOpacity(fadeToOpacity); + fadeFromOverlay->setOpacity(fadeFromOpacity); + updateSurfaceColoring(); + updateUserInterface(); + updateGraphics(); + + fadeToOpacity += opacityDelta; + if (fadeToOpacity > 1.0) { + fadeToOpacity = 1.0; + } + fadeFromOpacity -= opacityDelta; + if (fadeFromOpacity < 0.0) { + fadeFromOpacity = 0.0; + } + + sleepForSecondsAtEndOfIteration(iterationSleepTime); + } + + fadeToOverlay->setOpacity(1.0); + fadeFromOverlay->setOpacity(0.0); + + updateSurfaceColoring(); + updateUserInterface(); + updateGraphics(); + + return true; +} + diff --git a/src/GuiQt/WbMacroCustomOperationOverlayCrossFade.h b/src/GuiQt/WbMacroCustomOperationOverlayCrossFade.h new file mode 100644 index 000000000..a06331cf9 --- /dev/null +++ b/src/GuiQt/WbMacroCustomOperationOverlayCrossFade.h @@ -0,0 +1,69 @@ +#ifndef __WB_MACRO_CUSTOM_OPERATION_OVERLAY_CROSS_FADE_H__ +#define __WB_MACRO_CUSTOM_OPERATION_OVERLAY_CROSS_FADE_H__ + +/*LICENSE_START*/ +/* + * Copyright (C) 2019 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + + + +#include + +#include "WbMacroCustomOperationBase.h" + +namespace caret { + + class Overlay; + + class WbMacroCustomOperationOverlayCrossFade : public WbMacroCustomOperationBase { + + public: + WbMacroCustomOperationOverlayCrossFade(); + + virtual ~WbMacroCustomOperationOverlayCrossFade(); + + WbMacroCustomOperationOverlayCrossFade(const WbMacroCustomOperationOverlayCrossFade&) = delete; + + WbMacroCustomOperationOverlayCrossFade& operator=(const WbMacroCustomOperationOverlayCrossFade&) = delete; + + virtual bool executeCommand(QWidget* parent, + const WuQMacroCommand* macroCommand) override; + + virtual WuQMacroCommand* createCommand() override; + + + + // ADD_NEW_METHODS_HERE + + private: + bool performCrossFade(Overlay* fadeToOverlay, + Overlay* fadeFromOverlay, + const float durationSeconds); + + + // ADD_NEW_MEMBERS_HERE + + }; + +#ifdef __WB_MACRO_CUSTOM_OPERATION_OVERLAY_CROSS_FADE_DECLARE__ + // +#endif // __WB_MACRO_CUSTOM_OPERATION_OVERLAY_CROSS_FADE_DECLARE__ + +} // namespace +#endif //__WB_MACRO_CUSTOM_OPERATION_OVERLAY_CROSS_FADE_H__ diff --git a/src/GuiQt/WbMacroCustomOperationTypeEnum.cxx b/src/GuiQt/WbMacroCustomOperationTypeEnum.cxx index 067ac9e4d..30e7453a7 100644 --- a/src/GuiQt/WbMacroCustomOperationTypeEnum.cxx +++ b/src/GuiQt/WbMacroCustomOperationTypeEnum.cxx @@ -109,7 +109,11 @@ WbMacroCustomOperationTypeEnum::initialize() enumData.push_back(WbMacroCustomOperationTypeEnum(MODEL_ROTATION, "MODEL_ROTATION", "Model Rotation")); - + + enumData.push_back(WbMacroCustomOperationTypeEnum(OVERLAY_CROSS_FADE, + "OVERLAY_CROSS_FADE", + "Overlay CrossFade")); + enumData.push_back(WbMacroCustomOperationTypeEnum(SURFACE_INTERPOLATION, "SURFACE_INTERPOLATION", "Surface Interpolation")); @@ -117,6 +121,10 @@ WbMacroCustomOperationTypeEnum::initialize() enumData.push_back(WbMacroCustomOperationTypeEnum(VOLUME_SLICE_INCREMENT, "VOLUME_SLICE_INCREMENT", "Volume Slice Increment")); + + enumData.push_back(WbMacroCustomOperationTypeEnum(VOLUME_TO_SURFACE_CROSS_FADE, + "VOLUME_TO_SURFACE_CROSS_FADE", + "Volume to Surface Cross Fade")); } /** diff --git a/src/GuiQt/WbMacroCustomOperationTypeEnum.h b/src/GuiQt/WbMacroCustomOperationTypeEnum.h index 2bd0c2f55..c72f22bc4 100644 --- a/src/GuiQt/WbMacroCustomOperationTypeEnum.h +++ b/src/GuiQt/WbMacroCustomOperationTypeEnum.h @@ -37,10 +37,14 @@ public: enum Enum { /** Rotation of viewed model */ MODEL_ROTATION, + /** CrossFade of overlays */ + OVERLAY_CROSS_FADE, /** Surface Interpolation */ SURFACE_INTERPOLATION, /** Volume slice increment */ - VOLUME_SLICE_INCREMENT + VOLUME_SLICE_INCREMENT, + /** Volume to surface corss fade */ + VOLUME_TO_SURFACE_CROSS_FADE }; diff --git a/src/GuiQt/WbMacroCustomOperationVolumeToSurfaceCrossFade.cxx b/src/GuiQt/WbMacroCustomOperationVolumeToSurfaceCrossFade.cxx new file mode 100644 index 000000000..53efdc2cf --- /dev/null +++ b/src/GuiQt/WbMacroCustomOperationVolumeToSurfaceCrossFade.cxx @@ -0,0 +1,231 @@ + +/*LICENSE_START*/ +/* + * Copyright (C) 2019 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + +#define __WB_MACRO_CUSTOM_OPERATION_VOLUME_TO_SURFACE_CROSS_FADE_DECLARE__ +#include "WbMacroCustomOperationVolumeToSurfaceCrossFade.h" +#undef __WB_MACRO_CUSTOM_OPERATION_VOLUME_TO_SURFACE_CROSS_FADE_DECLARE__ + +#include "Brain.h" +#include "BrainBrowserWindow.h" +#include "BrowserTabContent.h" +#include "CaretAssert.h" +#include "DisplayPropertiesSurface.h" +#include "GuiManager.h" +#include "ModelWholeBrain.h" +#include "Overlay.h" +#include "OverlaySet.h" +#include "WbMacroCustomDataTypeEnum.h" +#include "WbMacroCustomOperationTypeEnum.h" +#include "WuQMacroCommand.h" +#include "WuQMacroCommandParameter.h" + +using namespace caret; + + + +/** + * \class caret::WbMacroCustomOperationVolumeToSurfaceCrossFade + * \brief Custom Macro Command for Surface Interpolation + * \ingroup GuiQt + */ + +/** + * Constructor. + */ +WbMacroCustomOperationVolumeToSurfaceCrossFade::WbMacroCustomOperationVolumeToSurfaceCrossFade() +: WbMacroCustomOperationBase(WbMacroCustomOperationTypeEnum::VOLUME_TO_SURFACE_CROSS_FADE) +{ +} + +/** + * Destructor. + */ +WbMacroCustomOperationVolumeToSurfaceCrossFade::~WbMacroCustomOperationVolumeToSurfaceCrossFade() +{ +} + +/** + * Get a new instance of the macro command + * + * @return + * Pointer to command or NULL if not valid + * Use getErrorMessage() for error information if NULL returned + */ +WuQMacroCommand* +WbMacroCustomOperationVolumeToSurfaceCrossFade::createCommand() +{ + const int32_t versionOne(1); + + const QString description("Crossfade (blend) from volume to surface:\n" + "ALL View must be selected.\n" + "(1) The opacity of the surface is set to zero;\n" + "(2) The opacity of the volume overlay is set to one\n" + "(3) The opacity of the surface increases until it is one\n" + " and simultaneously, the opacity of the volume\n" + " decreases until it reaches zero.\n"); + QString errorMessage; + WuQMacroCommand* command = WuQMacroCommand::newInstanceCustomCommand(WbMacroCustomOperationTypeEnum::toName(getOperationType()), + versionOne, + "none", + "Volume to Surface CrossFade", + description, + 1.0, + errorMessage); + if (command != NULL) { +// command->addParameter(WuQMacroDataValueTypeEnum::INTEGER, +// "Fade from Overlay", +// (int)2); + command->addParameter(WuQMacroDataValueTypeEnum::FLOAT, + "Duration (secs)", + (float)10.0); + } + else { + appendToErrorMessage(errorMessage); + } + + return command; +} + +/** + * Execute the macro command + * + * @param parent + * Parent widget for any dialogs + * @param macroCommand + * macro command to run + * @return + * True if command executed successfully, else false + * Use getErrorMessage() for error information if false returned + */ +bool +WbMacroCustomOperationVolumeToSurfaceCrossFade::executeCommand(QWidget* parent, + const WuQMacroCommand* macroCommand) +{ + CaretAssert(parent); + CaretAssert(macroCommand); + + validateCorrectNumberOfParameters(macroCommand); + + BrainBrowserWindow* bbw = qobject_cast(parent); + if (bbw == NULL) { + appendToErrorMessage("Parent for running surface macro is not a browser window"); + return false; + } + + BrowserTabContent* tabContent = bbw->getBrowserTabContent(); + if (tabContent == NULL) { + appendToErrorMessage("No tab is selected in browser window"); + return false; + } + + ModelWholeBrain* wholeBrainModel = tabContent->getDisplayedWholeBrainModel(); + if (wholeBrainModel == NULL) { + appendToErrorMessage("All view must be selected"); + return false; + } + + int32_t paramIndex(0); + const float durationSeconds(macroCommand->getParameterAtIndex(paramIndex)->getValue().toFloat()); + paramIndex++; + + OverlaySet* overlaySet = tabContent->getOverlaySet(); + CaretAssert(overlaySet); + + Overlay* volumeOverlay = overlaySet->getUnderlayContainingVolume(); + if (volumeOverlay == NULL) { + appendToErrorMessage("An overlay must contain a volume"); + return false; + } + if ( ! getErrorMessage().isEmpty()) { + return false; + } + + + bool successFlag = performCrossFade(volumeOverlay, + durationSeconds); + + return successFlag; +} + +/** + * Interpolate from starting to ending surface + * + * @param volumeOverlay + * Overlay that contains the volume + * @param durationSeconds + * Total duration for cross fade + * @return + * True if successful, else false + */ +bool +WbMacroCustomOperationVolumeToSurfaceCrossFade::performCrossFade(Overlay* volumeOverlay, + const float durationSeconds) +{ + CaretAssert(volumeOverlay); + + const float defaultNumberOfSteps(25.0); + float numberOfSteps(0.0); + float iterationSleepTime(0.0); + getNumberOfStepsAndSleepTime(defaultNumberOfSteps, + durationSeconds, + numberOfSteps, + iterationSleepTime); + + const float opacityDelta = 1.0 / numberOfSteps; + float surfaceOpacity(0.0); + float volumeOpacity(1.0); + + DisplayPropertiesSurface* surfaceProperties = GuiManager::get()->getBrain()->getDisplayPropertiesSurface(); + CaretAssert(surfaceProperties); + + /* + * Initialize the opacities + */ + + for (int iStep = 0; iStep < numberOfSteps; iStep++) { + surfaceProperties->setOpacity(surfaceOpacity); + volumeOverlay->setOpacity(volumeOpacity); + updateSurfaceColoring(); + updateUserInterface(); + updateGraphics(); + + surfaceOpacity += opacityDelta; + if (surfaceOpacity > 1.0) { + surfaceOpacity = 1.0; + } + volumeOpacity -= opacityDelta; + if (volumeOpacity < 0.0) { + volumeOpacity = 0.0; + } + + sleepForSecondsAtEndOfIteration(iterationSleepTime); + } + + surfaceProperties->setOpacity(1.0); + volumeOverlay->setOpacity(0.0); + + updateSurfaceColoring(); + updateUserInterface(); + updateGraphics(); + + return true; +} + diff --git a/src/GuiQt/WbMacroCustomOperationVolumeToSurfaceCrossFade.h b/src/GuiQt/WbMacroCustomOperationVolumeToSurfaceCrossFade.h new file mode 100644 index 000000000..f4f70fd1f --- /dev/null +++ b/src/GuiQt/WbMacroCustomOperationVolumeToSurfaceCrossFade.h @@ -0,0 +1,68 @@ +#ifndef __WB_MACRO_CUSTOM_OPERATION_VOLUME_TO_SURFACE_CROSS_FADE_H__ +#define __WB_MACRO_CUSTOM_OPERATION_VOLUME_TO_SURFACE_CROSS_FADE_H__ + +/*LICENSE_START*/ +/* + * Copyright (C) 2019 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + + + +#include + +#include "WbMacroCustomOperationBase.h" + +namespace caret { + + class Overlay; + + class WbMacroCustomOperationVolumeToSurfaceCrossFade : public WbMacroCustomOperationBase { + + public: + WbMacroCustomOperationVolumeToSurfaceCrossFade(); + + virtual ~WbMacroCustomOperationVolumeToSurfaceCrossFade(); + + WbMacroCustomOperationVolumeToSurfaceCrossFade(const WbMacroCustomOperationVolumeToSurfaceCrossFade&) = delete; + + WbMacroCustomOperationVolumeToSurfaceCrossFade& operator=(const WbMacroCustomOperationVolumeToSurfaceCrossFade&) = delete; + + virtual bool executeCommand(QWidget* parent, + const WuQMacroCommand* macroCommand) override; + + virtual WuQMacroCommand* createCommand() override; + + + + // ADD_NEW_METHODS_HERE + + private: + bool performCrossFade(Overlay* volumeOverlay, + const float durationSeconds); + + + // ADD_NEW_MEMBERS_HERE + + }; + +#ifdef __WB_MACRO_CUSTOM_OPERATION_VOLUME_TO_SURFACE_CROSS_FADE_DECLARE__ + // +#endif // __WB_MACRO_CUSTOM_OPERATION_OVERLAY_CROSS_FADE_DECLARE__ + +} // namespace +#endif //__WB_MACRO_CUSTOM_OPERATION_VOLUME_TO_SURFACE_CROSS_FADE_H__ diff --git a/src/GuiQt/WbMacroHelper.cxx b/src/GuiQt/WbMacroHelper.cxx index cac0bc896..2ef5ed827 100644 --- a/src/GuiQt/WbMacroHelper.cxx +++ b/src/GuiQt/WbMacroHelper.cxx @@ -96,9 +96,12 @@ WbMacroHelper::getMacroGroups() { std::vector macroGroups; - CaretPreferences* preferences = SessionManager::get()->getCaretPreferences(); - CaretAssert(preferences); - macroGroups.push_back(preferences->getMacros()); + const bool includePreferencesFlag(false); + if (includePreferencesFlag) { + CaretPreferences* preferences = SessionManager::get()->getCaretPreferences(); + CaretAssert(preferences); + macroGroups.push_back(preferences->getMacros()); + } EventSceneActive activeSceneEvent(EventSceneActive::MODE_GET); EventManager::get()->sendEvent(activeSceneEvent.getPointer()); -- GitLab From c069d3d290d0cd8782d704f7f5e825351b6df1d1 Mon Sep 17 00:00:00 2001 From: John Harwell Date: Fri, 15 Feb 2019 14:57:30 -0600 Subject: [PATCH 190/427] WB-834 Macros: Changed macro menu so that there are separate items for Record Macro and Stop Recording Macro rather than one item that changes text. Movie Dialog: remove apply button; change 'dimension' to 'resolution'. Macro dialog: fixed adding a new macro so that it is placed after the selected macro instead of being placed at the end. Fix horizontal stretching problems on Add Macro Command dialog. --- src/Brain/MovieRecorder.cxx | 46 +++++++++---------- src/Brain/MovieRecorder.h | 18 ++++---- .../MovieRecorderVideoResolutionTypeEnum.cxx | 8 ++-- .../MovieRecorderVideoResolutionTypeEnum.h | 8 ++-- src/GuiQt/BrainOpenGLWidget.cxx | 4 +- src/GuiQt/MovieRecordingDialog.cxx | 30 ++++++------ src/GuiQt/WuQMacroCreateDialog.cxx | 16 +++++-- src/GuiQt/WuQMacroDialog.cxx | 16 ++++++- src/GuiQt/WuQMacroMenu.cxx | 39 ++++++++++++---- src/GuiQt/WuQMacroMenu.h | 4 ++ .../WuQMacroNewCommandSelectionDialog.cxx | 2 + 11 files changed, 121 insertions(+), 70 deletions(-) diff --git a/src/Brain/MovieRecorder.cxx b/src/Brain/MovieRecorder.cxx index 765f91861..49e49ac66 100644 --- a/src/Brain/MovieRecorder.cxx +++ b/src/Brain/MovieRecorder.cxx @@ -210,28 +210,28 @@ MovieRecorder::setRecordingWindowIndex(const int32_t windowIndex) } /** - * @return Video dimensions type + * @return Video resolution type */ MovieRecorderVideoResolutionTypeEnum::Enum -MovieRecorder::getVideoDimensionsType() const +MovieRecorder::getVideoResolutionType() const { - return m_dimensionsType; + return m_resolutionType; } /** - * Set the video dimensions type + * Set the video resolution type * - * @param dimensionsType - * New dimensions type + * @param resolutionType + * New resolution type */ void -MovieRecorder::setVideoDimensionsType(const MovieRecorderVideoResolutionTypeEnum::Enum dimensionsType) +MovieRecorder::setVideoResolutionType(const MovieRecorderVideoResolutionTypeEnum::Enum resolutionType) { - m_dimensionsType = dimensionsType; + m_resolutionType = resolutionType; } /** - * Get the video dimensions + * Get the video width and height * * @param widthOut * Output width @@ -239,26 +239,26 @@ MovieRecorder::setVideoDimensionsType(const MovieRecorderVideoResolutionTypeEnum * Output height */ void -MovieRecorder::getVideoDimensions(int32_t& widthOut, - int32_t& heightOut) const +MovieRecorder::getVideoWidthAndHeight(int32_t& widthOut, + int32_t& heightOut) const { widthOut = 100; heightOut = 100; - const MovieRecorderVideoResolutionTypeEnum::Enum dimType = getVideoDimensionsType(); + const MovieRecorderVideoResolutionTypeEnum::Enum dimType = getVideoResolutionType(); if (dimType == MovieRecorderVideoResolutionTypeEnum::CUSTOM) { - getCustomDimensions(widthOut, - heightOut); + getCustomWidthAndHeight(widthOut, + heightOut); } else { - MovieRecorderVideoResolutionTypeEnum::getDimensions(dimType, - widthOut, - heightOut); + MovieRecorderVideoResolutionTypeEnum::getWidthAndHeight(dimType, + widthOut, + heightOut); } } /** - * Get the custom dimensions + * Get the custom width and height * * @param widthOut * Output width @@ -266,15 +266,15 @@ MovieRecorder::getVideoDimensions(int32_t& widthOut, * Output height */ void -MovieRecorder::getCustomDimensions(int32_t& widthOut, - int32_t& heightOut) const +MovieRecorder::getCustomWidthAndHeight(int32_t& widthOut, + int32_t& heightOut) const { widthOut = m_customWidth; heightOut = m_customHeight; } /** - * Set the custom dimensions + * Set the custom width and height * * @param width * New width @@ -282,8 +282,8 @@ MovieRecorder::getCustomDimensions(int32_t& widthOut, * New height */ void -MovieRecorder::setCustomDimensions(const int32_t width, - const int32_t height) +MovieRecorder::setCustomWidthAndHeight(const int32_t width, + const int32_t height) { m_customWidth = width; m_customHeight = height; diff --git a/src/Brain/MovieRecorder.h b/src/Brain/MovieRecorder.h index df826a2f5..fe51fb41e 100644 --- a/src/Brain/MovieRecorder.h +++ b/src/Brain/MovieRecorder.h @@ -60,18 +60,18 @@ namespace caret { void setRecordingWindowIndex(const int32_t windowIndex); - MovieRecorderVideoResolutionTypeEnum::Enum getVideoDimensionsType() const; + MovieRecorderVideoResolutionTypeEnum::Enum getVideoResolutionType() const; - void setVideoDimensionsType(const MovieRecorderVideoResolutionTypeEnum::Enum dimensionsType); + void setVideoResolutionType(const MovieRecorderVideoResolutionTypeEnum::Enum resolutionType); - void getVideoDimensions(int32_t& widthOut, - int32_t& heightOut) const; + void getVideoWidthAndHeight(int32_t& widthOut, + int32_t& heightOut) const; - void getCustomDimensions(int32_t& widthOut, - int32_t& heightOut) const; + void getCustomWidthAndHeight(int32_t& widthOut, + int32_t& heightOut) const; - void setCustomDimensions(const int32_t width, - const int32_t height); + void setCustomWidthAndHeight(const int32_t width, + const int32_t height); MovieRecorderVideoFormatTypeEnum::Enum getVideoFormatType() const; @@ -125,7 +125,7 @@ namespace caret { MovieRecorderModeEnum::Enum m_recordingMode = MovieRecorderModeEnum::MANUAL; - MovieRecorderVideoResolutionTypeEnum::Enum m_dimensionsType = MovieRecorderVideoResolutionTypeEnum::SD_640_480; + MovieRecorderVideoResolutionTypeEnum::Enum m_resolutionType = MovieRecorderVideoResolutionTypeEnum::SD_640_480; MovieRecorderVideoFormatTypeEnum::Enum m_formatType = MovieRecorderVideoFormatTypeEnum::MPEG_4; diff --git a/src/Brain/MovieRecorderVideoResolutionTypeEnum.cxx b/src/Brain/MovieRecorderVideoResolutionTypeEnum.cxx index cff4dcd23..dd3da8740 100644 --- a/src/Brain/MovieRecorderVideoResolutionTypeEnum.cxx +++ b/src/Brain/MovieRecorderVideoResolutionTypeEnum.cxx @@ -384,7 +384,7 @@ MovieRecorderVideoResolutionTypeEnum::getAllGuiNames(std::vector& allGu } /** - * Get the dimensions for given enumerated value. Output will be zeros + * Get the width and height for given enumerated value. Output will be zeros * for custom. * * @param enumValue @@ -395,9 +395,9 @@ MovieRecorderVideoResolutionTypeEnum::getAllGuiNames(std::vector& allGu * Output containing height */ void -MovieRecorderVideoResolutionTypeEnum::getDimensions(const Enum enumValue, - int32_t& widthOut, - int32_t& heightOut) +MovieRecorderVideoResolutionTypeEnum::getWidthAndHeight(const Enum enumValue, + int32_t& widthOut, + int32_t& heightOut) { switch (enumValue) { case CUSTOM: diff --git a/src/Brain/MovieRecorderVideoResolutionTypeEnum.h b/src/Brain/MovieRecorderVideoResolutionTypeEnum.h index 47b9fbc85..fc6ddd55c 100644 --- a/src/Brain/MovieRecorderVideoResolutionTypeEnum.h +++ b/src/Brain/MovieRecorderVideoResolutionTypeEnum.h @@ -68,10 +68,10 @@ public: static void getAllGuiNames(std::vector& allGuiNames, const bool isSorted); - static void getDimensions(const Enum enumValue, - int32_t& widthOut, - int32_t& heightOut); - + static void getWidthAndHeight(const Enum enumValue, + int32_t& widthOut, + int32_t& heightOut); + private: MovieRecorderVideoResolutionTypeEnum(const Enum enumValue, const AString& name, diff --git a/src/GuiQt/BrainOpenGLWidget.cxx b/src/GuiQt/BrainOpenGLWidget.cxx index 5bb424311..863ce6c8e 100644 --- a/src/GuiQt/BrainOpenGLWidget.cxx +++ b/src/GuiQt/BrainOpenGLWidget.cxx @@ -1733,8 +1733,8 @@ BrainOpenGLWidget::receiveEvent(Event* event) true); int imageWidth(0); int imageHeight(0); - movieRecorder->getVideoDimensions(imageWidth, - imageHeight); + movieRecorder->getVideoWidthAndHeight(imageWidth, + imageHeight); QImage image; bool imageValid(false); diff --git a/src/GuiQt/MovieRecordingDialog.cxx b/src/GuiQt/MovieRecordingDialog.cxx index 5870ac5e9..3cc6db51e 100644 --- a/src/GuiQt/MovieRecordingDialog.cxx +++ b/src/GuiQt/MovieRecordingDialog.cxx @@ -73,6 +73,8 @@ MovieRecordingDialog::MovieRecordingDialog(QWidget* parent) EventManager::get()->addEventListener(this, EventTypeEnum::EVENT_MOVIE_RECORDING_DIALOG_UPDATE); + + setApplyButtonText(""); disableAutoDefaultForAllPushButtons(); } @@ -125,8 +127,8 @@ MovieRecordingDialog::updateDialog() const MovieRecorderVideoFormatTypeEnum::Enum formatType = movieRecorder->getVideoFormatType(); m_movieRecorderVideoFormatTypeEnumComboBox->setSelectedItem(formatType); - const MovieRecorderVideoResolutionTypeEnum::Enum dimType = movieRecorder->getVideoDimensionsType(); - m_movieRecorderVideoResolutionTypeEnumComboBox->setSelectedItem(dimType); + const MovieRecorderVideoResolutionTypeEnum::Enum resType = movieRecorder->getVideoResolutionType(); + m_movieRecorderVideoResolutionTypeEnumComboBox->setSelectedItem(resType); const MovieRecorderCaptureRegionTypeEnum::Enum captureType = movieRecorder->getCaptureRegionType(); m_movieRecorderCaptureRegionTypeComboBox->setSelectedItem(captureType); @@ -170,8 +172,8 @@ MovieRecordingDialog::updateCustomWidthHeightSpinBoxes() int32_t customWidth(0); int32_t customHeight(0); - movieRecorder->getCustomDimensions(customWidth, - customHeight); + movieRecorder->getCustomWidthAndHeight(customWidth, + customHeight); QSignalBlocker widthBlocker(m_customWidthSpinBox); m_customWidthSpinBox->setValue(customWidth); QSignalBlocker heightBlocker(m_customHeightSpinBox); @@ -179,7 +181,7 @@ MovieRecordingDialog::updateCustomWidthHeightSpinBoxes() QSignalBlocker frameRateBlocker(m_frameRateSpinBox); m_frameRateSpinBox->setValue(movieRecorder->getFramesRate()); - const bool customSpinBoxesEnabled(movieRecorder->getVideoDimensionsType() == MovieRecorderVideoResolutionTypeEnum::CUSTOM); + const bool customSpinBoxesEnabled(movieRecorder->getVideoResolutionType() == MovieRecorderVideoResolutionTypeEnum::CUSTOM); m_customWidthSpinBox->setEnabled(customSpinBoxesEnabled); m_customHeightSpinBox->setEnabled(customSpinBoxesEnabled); } @@ -227,13 +229,13 @@ MovieRecordingDialog::windowIndexSelected(const int32_t windowIndex) } /** - * Called when video dimensions type is changed + * Called when video resolution type is changed */ void MovieRecordingDialog::movieRecorderVideoResolutionTypeEnumComboBoxItemActivated() { const MovieRecorderVideoResolutionTypeEnum::Enum dimType = m_movieRecorderVideoResolutionTypeEnumComboBox->getSelectedItem(); - SessionManager::get()->getMovieRecorder()->setVideoDimensionsType(dimType); + SessionManager::get()->getMovieRecorder()->setVideoResolutionType(dimType); updateCustomWidthHeightSpinBoxes(); } @@ -280,8 +282,8 @@ MovieRecordingDialog::setBrowserWindowIndex(const int32_t browserWindowIndex) void MovieRecordingDialog::customWidthSpinBoxValueChanged(int width) { - SessionManager::get()->getMovieRecorder()->setCustomDimensions(width, - m_customHeightSpinBox->value()); + SessionManager::get()->getMovieRecorder()->setCustomWidthAndHeight(width, + m_customHeightSpinBox->value()); } /** @@ -293,8 +295,8 @@ MovieRecordingDialog::customWidthSpinBoxValueChanged(int width) void MovieRecordingDialog::customHeightSpinBoxValueChanged(int height) { - SessionManager::get()->getMovieRecorder()->setCustomDimensions(m_customWidthSpinBox->value(), - height); + SessionManager::get()->getMovieRecorder()->setCustomWidthAndHeight(m_customWidthSpinBox->value(), + height); } /** @@ -581,14 +583,14 @@ MovieRecordingDialog::createSettingsWidget() QObject::connect(m_movieRecorderVideoFormatTypeEnumComboBox, SIGNAL(itemActivated()), this, SLOT(movieRecorderVideoFormatTypeEnumComboBoxItemActivated())); - QLabel* dimensionsLabel = new QLabel("Dimensions:"); + QLabel* resolutionLabel = new QLabel("Resolution:"); m_movieRecorderVideoResolutionTypeEnumComboBox = new EnumComboBoxTemplate(this); m_movieRecorderVideoResolutionTypeEnumComboBox->getWidget()->setToolTip("Choose width and height of movie"); m_movieRecorderVideoResolutionTypeEnumComboBox->setup(); QObject::connect(m_movieRecorderVideoResolutionTypeEnumComboBox, SIGNAL(itemActivated()), this, SLOT(movieRecorderVideoResolutionTypeEnumComboBoxItemActivated())); - QLabel* customLabel = new QLabel("Custom Dimensions:"); + QLabel* customLabel = new QLabel("Custom Resolution:"); m_customWidthSpinBox = new QSpinBox(); m_customWidthSpinBox->setMinimum(1); m_customWidthSpinBox->setMaximum(500000); @@ -627,7 +629,7 @@ MovieRecordingDialog::createSettingsWidget() gridLayout->addWidget(m_movieRecorderVideoFormatTypeEnumComboBox->getWidget(), row, 1, 1, 2, Qt::AlignLeft); row++; - gridLayout->addWidget(dimensionsLabel, row, 0); + gridLayout->addWidget(resolutionLabel, row, 0); gridLayout->addWidget(m_movieRecorderVideoResolutionTypeEnumComboBox->getWidget(), row, 1, 1, 2, Qt::AlignLeft); row++; diff --git a/src/GuiQt/WuQMacroCreateDialog.cxx b/src/GuiQt/WuQMacroCreateDialog.cxx index fdfd9d6c8..d90b9d1ac 100644 --- a/src/GuiQt/WuQMacroCreateDialog.cxx +++ b/src/GuiQt/WuQMacroCreateDialog.cxx @@ -91,19 +91,23 @@ WuQMacroCreateDialog::WuQMacroCreateDialog(QWidget* parent) } QGridLayout* gridLayout = new QGridLayout(); + gridLayout->setColumnStretch(0, 0); + gridLayout->setColumnStretch(1, 0); + gridLayout->setColumnStretch(2, 0); + gridLayout->setColumnStretch(3, 100); int row = 0; gridLayout->addWidget(macroGroupLabel, row, 0); - gridLayout->addWidget(m_macroGroupComboBox, row, 1, 1, 2); + gridLayout->addWidget(m_macroGroupComboBox, row, 1, 1, 3); row++; gridLayout->addWidget(nameLabel, row, 0); - gridLayout->addWidget(m_macroNameLineEdit, row, 1, 1, 2); + gridLayout->addWidget(m_macroNameLineEdit, row, 1, 1, 3); row++; gridLayout->addWidget(shortCutKeyLabel, row, 0); gridLayout->addWidget(shortCutKeyMaskLabel, row, 1); - gridLayout->addWidget(m_macroShortCutKeyComboBox->getWidget(), row, 2, Qt::AlignLeft); + gridLayout->addWidget(m_macroShortCutKeyComboBox->getWidget(), row, 2); //, Qt::AlignLeft); row++; gridLayout->addWidget(descriptionLabel, row, 0); - gridLayout->addWidget(m_macroDescriptionTextEdit, row, 1, 1, 2); + gridLayout->addWidget(m_macroDescriptionTextEdit, row, 1, 1, 3); row++; m_dialogButtonBox = new QDialogButtonBox(QDialogButtonBox::Ok @@ -114,8 +118,10 @@ WuQMacroCreateDialog::WuQMacroCreateDialog(QWidget* parent) this, &WuQMacroCreateDialog::reject); QVBoxLayout* dialogLayout = new QVBoxLayout(this); - dialogLayout->addLayout(gridLayout); + dialogLayout->addLayout(gridLayout, 100); dialogLayout->addWidget(m_dialogButtonBox); + + setFixedHeight(sizeHint().height()); } /** diff --git a/src/GuiQt/WuQMacroDialog.cxx b/src/GuiQt/WuQMacroDialog.cxx index 4a628b4fa..f27ac9fdb 100644 --- a/src/GuiQt/WuQMacroDialog.cxx +++ b/src/GuiQt/WuQMacroDialog.cxx @@ -1292,10 +1292,24 @@ WuQMacroDialog::insertMenuNewMacroSelected() &okFlag); if (okFlag) { if ( ! macroName.isEmpty()) { + const WuQMacro* selectedMacro = getSelectedMacro(); WuQMacro* macro = new WuQMacro(); macro->setName(macroName); - macroGroup->addMacro(macro); + if (selectedMacro != NULL) { + const int32_t index = macroGroup->getIndexOfMacro(selectedMacro); + macroGroup->insertMacroAtIndex(index + 1, + macro); + } + else { + macroGroup->addMacro(macro); + } updateDialogContents(); + + QModelIndex selectedIndex = macroGroup->indexFromItem(macro); + if (selectedIndex.isValid()) { + m_treeView->setCurrentIndex(selectedIndex); + updateDialogContents(); + } } } } diff --git a/src/GuiQt/WuQMacroMenu.cxx b/src/GuiQt/WuQMacroMenu.cxx index 471cc8f21..5486509e4 100644 --- a/src/GuiQt/WuQMacroMenu.cxx +++ b/src/GuiQt/WuQMacroMenu.cxx @@ -61,6 +61,10 @@ m_windowParent(windowParent) QObject::connect(m_recordMacroAction, &QAction::triggered, this, &WuQMacroMenu::macroRecordSelected); + m_stopMacroAction = addAction("Stop Recording Macro"); + QObject::connect(m_stopMacroAction, &QAction::triggered, + this, &WuQMacroMenu::macroStopSelected); + { QMenu* developmentMenu = new QMenu("Development"); QAction* printAction = developmentMenu->addAction("Print Supported Widgets"); @@ -96,30 +100,27 @@ WuQMacroMenu::macroMenuAboutToShow() bool editValidFlag(false); bool recordValidFlag(false); - QString recordText; + bool stopValidFlag(false); switch (macroManager->getMode()) { case WuQMacroModeEnum::OFF: editValidFlag = true; - recordText = "Record Macro..."; recordValidFlag = true; break; case WuQMacroModeEnum::RECORDING: - recordText = "Stop Recording"; - recordValidFlag = true; + stopValidFlag = true; break; case WuQMacroModeEnum::RUNNING: - recordText = "Record Macro..."; break; } m_macroDialogAction->setEnabled(editValidFlag); m_recordMacroAction->setEnabled(recordValidFlag); - m_recordMacroAction->setText(recordText); + m_stopMacroAction->setEnabled(stopValidFlag); } /** - * Called to start/stop macro recording. + * Called to start macro recording. */ void WuQMacroMenu::macroRecordSelected() @@ -131,6 +132,28 @@ WuQMacroMenu::macroRecordSelected() case WuQMacroModeEnum::OFF: macroManager->startRecordingNewMacro(m_windowParent); break; + case WuQMacroModeEnum::RECORDING: + CaretAssert(0); + break; + case WuQMacroModeEnum::RUNNING: + CaretAssert(0); + break; + } +} + +/** + * Called to stop macro recording. + */ +void +WuQMacroMenu::macroStopSelected() +{ + WuQMacroManager* macroManager = WuQMacroManager::instance(); + CaretAssert(macroManager); + + switch (macroManager->getMode()) { + case WuQMacroModeEnum::OFF: + CaretAssert(0); + break; case WuQMacroModeEnum::RECORDING: macroManager->stopRecordingNewMacro(); break; @@ -165,4 +188,4 @@ void WuQMacroMenu::macroPrintTopLevelWidgets() { WuQMacroManager::instance()->printToLevelWidgetsToTerminal(); -} \ No newline at end of file +} diff --git a/src/GuiQt/WuQMacroMenu.h b/src/GuiQt/WuQMacroMenu.h index dc536612a..8cd60e93a 100644 --- a/src/GuiQt/WuQMacroMenu.h +++ b/src/GuiQt/WuQMacroMenu.h @@ -50,6 +50,8 @@ namespace caret { void macroRecordSelected(); + void macroStopSelected(); + void macroDialogSelected(); void macroPrintAllSelected(); @@ -64,6 +66,8 @@ namespace caret { QAction* m_macroDialogAction; QAction* m_recordMacroAction; + + QAction* m_stopMacroAction; }; #ifdef __WU_Q_MACRO_MENU_DECLARE__ diff --git a/src/GuiQt/WuQMacroNewCommandSelectionDialog.cxx b/src/GuiQt/WuQMacroNewCommandSelectionDialog.cxx index a2c4e5597..3e4372d61 100644 --- a/src/GuiQt/WuQMacroNewCommandSelectionDialog.cxx +++ b/src/GuiQt/WuQMacroNewCommandSelectionDialog.cxx @@ -92,6 +92,8 @@ WuQMacroNewCommandSelectionDialog::WuQMacroNewCommandSelectionDialog(QWidget* pa m_macroDescriptionTextEdit->setFixedHeight(100); QGridLayout* gridLayout = new QGridLayout(); + gridLayout->setColumnStretch(0, 0); + gridLayout->setColumnStretch(1, 100); int row = 0; gridLayout->addWidget(commandTypeLabel, row, 0); gridLayout->addWidget(m_commandTypeComboBox->getWidget(), row, 1, 1, 2); -- GitLab From 50dc5e1bf2e4bf0ef6877d225fb1af0cc0e0e4c7 Mon Sep 17 00:00:00 2001 From: John Harwell Date: Mon, 18 Feb 2019 11:01:29 -0600 Subject: [PATCH 191/427] WB-834 Macros: Update "Add Macro Command" dialog to include allow selection of all widgets so that they can be added to macros. --- src/GuiQt/BrainBrowserWindow.cxx | 12 +- src/GuiQt/WuQMacroExecutor.cxx | 1 + src/GuiQt/WuQMacroManager.cxx | 84 +++++ src/GuiQt/WuQMacroManager.h | 6 + .../WuQMacroNewCommandSelectionDialog.cxx | 35 +- src/GuiQt/WuQMacroNewCommandSelectionDialog.h | 3 + src/GuiQt/WuQMacroSignalWatcher.cxx | 326 +++++++++++------- src/GuiQt/WuQMacroSignalWatcher.h | 9 + 8 files changed, 349 insertions(+), 127 deletions(-) diff --git a/src/GuiQt/BrainBrowserWindow.cxx b/src/GuiQt/BrainBrowserWindow.cxx index 819027599..8ce54641c 100644 --- a/src/GuiQt/BrainBrowserWindow.cxx +++ b/src/GuiQt/BrainBrowserWindow.cxx @@ -1267,7 +1267,11 @@ BrainBrowserWindow::createActions() this, this, SLOT(processNewTab())); - + m_newTabAction->setObjectName(m_objectNamePrefix + + ":Menu:NewTabAction"); + WuQMacroManager::instance()->addMacroSupportToObject(m_newTabAction, + ("Create new tab in Window " + QString::number(m_browserWindowIndex + 1))); + m_duplicateTabAction = WuQtUtilities::createAction("Duplicate Tab", "Create a new tab (window pane) that duplicates the selected tab in the window", @@ -1275,7 +1279,11 @@ BrainBrowserWindow::createActions() this, this, SLOT(processDuplicateTab())); - + m_duplicateTabAction->setObjectName(m_objectNamePrefix + + ":Menu:DuplicateTabAction"); + WuQMacroManager::instance()->addMacroSupportToObject(m_duplicateTabAction, + ("Duplicate tab in Window " + QString::number(m_browserWindowIndex + 1))); + m_openFileAction = WuQtUtilities::createAction("Open File...", "Open a data file including a spec file located on the computer", diff --git a/src/GuiQt/WuQMacroExecutor.cxx b/src/GuiQt/WuQMacroExecutor.cxx index 9857ba7e8..ef5e52f5a 100644 --- a/src/GuiQt/WuQMacroExecutor.cxx +++ b/src/GuiQt/WuQMacroExecutor.cxx @@ -1238,6 +1238,7 @@ WuQMacroExecutor::runRadioButtonCommand(const WuQMacroCommand* macroCommand, const WuQMacroCommandParameter* parameterOne = macroCommand->getParameterAtIndex(0); CaretAssert(parameterOne); + /* Type=NONE since radio button is ALWAYS TRUE */ CaretAssert(parameterOne->getDataType() == WuQMacroDataValueTypeEnum::NONE); const QVariant dataValue = parameterOne->getValue(); diff --git a/src/GuiQt/WuQMacroManager.cxx b/src/GuiQt/WuQMacroManager.cxx index ffc4abae2..a16f7cf02 100644 --- a/src/GuiQt/WuQMacroManager.cxx +++ b/src/GuiQt/WuQMacroManager.cxx @@ -774,6 +774,90 @@ WuQMacroManager::exportMacros(QWidget* parent, return false; } +/** + * Get all signal watchers + * + * @param enabledItemsOnly + * Only valid signal watchers whose object currently exists are included. + * An object may cease to exists with something is closed (such as a window) + * + * @return + * All signal watchers + */ +std::vector +WuQMacroManager::getAllWidgetSignalWatchers(const bool enabledItemsOnly) const +{ + std::vector allWatchers; + allWatchers.reserve(m_signalWatchers.size()); + + for (auto iter : m_signalWatchers) { + WuQMacroSignalWatcher* watcher = iter.second; + bool validFlag(true); + if (enabledItemsOnly) { + validFlag = false; + for (auto po : m_parentObjects) { + QObject* object = po->findChild(watcher->getObjectName()); + if (object != NULL) { + validFlag = true; + break; + } + } + } + + if (validFlag) { + allWatchers.push_back(watcher); + } + } + return allWatchers; +} + +/** + * Get names of all signal watchers + * + * @param enabledItemsOnly + * Only valid signal watches are returned + * @return + * All signal watchers + */ +std::vector +WuQMacroManager::getAllWidgetSignalWatcherNames(const bool enabledItemsOnly) const +{ + std::vector allWatchers = getAllWidgetSignalWatchers(enabledItemsOnly); + + std::vector names; + names.reserve(m_signalWatchers.size()); + + for (auto iter : allWatchers) { + names.push_back(iter->getObjectName()); + } + + return names; +} + + +/** + * Get the widget signal watcher with the given name + * + * @param name + * Name of widget signal watcher + * @retrurn + * Pointer to watcher or NULL if not valid. + */ +WuQMacroSignalWatcher* +WuQMacroManager::getWidgetSignalWatcherWithName(const QString& name) +{ + auto watcher = m_signalWatchers.find(name); + if (watcher != m_signalWatchers.end()) { + return watcher->second; + } +// for (auto iter : m_signalWatchers) { +// if (iter.first == name) { +// return iter.second; +// } +// } + return NULL; +} + /** * Print supported widgets to the terminal window. */ diff --git a/src/GuiQt/WuQMacroManager.h b/src/GuiQt/WuQMacroManager.h index d3d97f292..0fbbd8eb2 100644 --- a/src/GuiQt/WuQMacroManager.h +++ b/src/GuiQt/WuQMacroManager.h @@ -157,6 +157,12 @@ namespace caret { QMainWindow* getMainWindowWithIdentifier(const QString& identifier); + std::vector getAllWidgetSignalWatchers(const bool enabledItemsOnly) const; + + std::vector getAllWidgetSignalWatcherNames(const bool enabledItemsOnly) const; + + WuQMacroSignalWatcher* getWidgetSignalWatcherWithName(const QString& name); + // ADD_NEW_METHODS_HERE public slots: diff --git a/src/GuiQt/WuQMacroNewCommandSelectionDialog.cxx b/src/GuiQt/WuQMacroNewCommandSelectionDialog.cxx index 3e4372d61..e26119c26 100644 --- a/src/GuiQt/WuQMacroNewCommandSelectionDialog.cxx +++ b/src/GuiQt/WuQMacroNewCommandSelectionDialog.cxx @@ -43,6 +43,7 @@ #include "WuQMacroGroup.h" #include "WuQMacroManager.h" #include "WuQMacroShortCutKeyComboBox.h" +#include "WuQMacroSignalWatcher.h" using namespace caret; @@ -69,7 +70,7 @@ WuQMacroNewCommandSelectionDialog::WuQMacroNewCommandSelectionDialog(QWidget* pa std::vector commandTypes; commandTypes.push_back(WuQMacroCommandTypeEnum::CUSTOM_OPERATION); - // Not implemented yet: commandTypes.push_back(WuQMacroCommandTypeEnum::WIDGET); + commandTypes.push_back(WuQMacroCommandTypeEnum::WIDGET); m_commandTypeComboBox = new EnumComboBoxTemplate(this); m_commandTypeComboBox->setupWithItems(commandTypes); m_commandTypeComboBox->setSelectedItem(s_lastCommandTypeSelected); @@ -189,7 +190,12 @@ WuQMacroNewCommandSelectionDialog::loadCustomCommandListWidget() void WuQMacroNewCommandSelectionDialog::loadWidgetCommandListWidget() { - + m_widgetCommands = WuQMacroManager::instance()->getAllWidgetSignalWatchers(false); + for (auto wc : m_widgetCommands) { + const QString name = wc->getObjectName(); + QListWidgetItem *item = new QListWidgetItem(name); + m_widgetCommandListWidget->addItem(item); + } } /** @@ -223,7 +229,14 @@ WuQMacroNewCommandSelectionDialog::customCommandListWidgetItemClicked(QListWidge void WuQMacroNewCommandSelectionDialog::widgetCommandListWidgetItemClicked(QListWidgetItem* item) { - + QString description; + const int index = m_widgetCommandListWidget->currentRow(); + if ((index >= 0) + && (index < static_cast(m_widgetCommands.size()))) { + description = m_widgetCommands[index]->getToolTip(); + } + + m_macroDescriptionTextEdit->setPlainText(description); } /** @@ -298,7 +311,21 @@ WuQMacroNewCommandSelectionDialog::getNewInstanceOfSelectedCommand(QString& erro CaretAssert(0); break; case WuQMacroCommandTypeEnum::WIDGET: - errorMessageOut = "New widget commands has not been implemented"; + { + QListWidgetItem* item = m_widgetCommandListWidget->currentItem(); + if (item != NULL) { + const QString widgetName = item->text(); + WuQMacroSignalWatcher* watcher = WuQMacroManager::instance()->getWidgetSignalWatcherWithName(widgetName); + if (watcher != NULL) { + commandOut = watcher->createMacroCommandWithDefaultParameters(errorMessageOut); + } + else { + errorMessageOut = ("Unable to find widget watcher with name \"" + + widgetName + + "\""); + } + } + } break; } diff --git a/src/GuiQt/WuQMacroNewCommandSelectionDialog.h b/src/GuiQt/WuQMacroNewCommandSelectionDialog.h index fe5825c74..5a9d322cd 100644 --- a/src/GuiQt/WuQMacroNewCommandSelectionDialog.h +++ b/src/GuiQt/WuQMacroNewCommandSelectionDialog.h @@ -44,6 +44,7 @@ namespace caret { class WuQMacroCommand; class WuQMacroGroup; class WuQMacroShortCutKeyComboBox; + class WuQMacroSignalWatcher; class WuQMacroNewCommandSelectionDialog : public QDialog { @@ -90,6 +91,8 @@ namespace caret { std::vector m_customCommands; + std::vector m_widgetCommands; + static WuQMacroCommandTypeEnum::Enum s_lastCommandTypeSelected; }; diff --git a/src/GuiQt/WuQMacroSignalWatcher.cxx b/src/GuiQt/WuQMacroSignalWatcher.cxx index 4f0f08fe7..d31ecdc56 100644 --- a/src/GuiQt/WuQMacroSignalWatcher.cxx +++ b/src/GuiQt/WuQMacroSignalWatcher.cxx @@ -97,6 +97,9 @@ m_objectName(object->objectName()) QObject::connect(action, &QAction::triggered, this, &WuQMacroSignalWatcher::actionTriggered); m_toolTipText = action->toolTip(); + m_objectParameters.push_back(new WuQMacroCommandParameter(WuQMacroDataValueTypeEnum::NONE, + "Click/Select", + "")); } break; case WuQMacroWidgetTypeEnum::ACTION_CHECKABLE: @@ -106,6 +109,9 @@ m_objectName(object->objectName()) QObject::connect(action, &QAction::triggered, this, &WuQMacroSignalWatcher::actionCheckableTriggered); m_toolTipText = action->toolTip(); + m_objectParameters.push_back(new WuQMacroCommandParameter(WuQMacroDataValueTypeEnum::BOOLEAN, + "On/Off", + true)); } break; case WuQMacroWidgetTypeEnum::ACTION_GROUP: @@ -114,6 +120,12 @@ m_objectName(object->objectName()) CaretAssert(actionGroup); QObject::connect(actionGroup, &QActionGroup::triggered, this, &WuQMacroSignalWatcher::actionGroupTriggered); + m_objectParameters.push_back(new WuQMacroCommandParameter(WuQMacroDataValueTypeEnum::STRING, + "Select name", + "")); + m_objectParameters.push_back(new WuQMacroCommandParameter(WuQMacroDataValueTypeEnum::INTEGER, + "Select index", + 1)); } break; case WuQMacroWidgetTypeEnum::BUTTON_GROUP: @@ -122,6 +134,12 @@ m_objectName(object->objectName()) CaretAssert(buttonGroup); QObject::connect(buttonGroup, static_cast(&QButtonGroup::buttonClicked), this, &WuQMacroSignalWatcher::buttonGroupButtonClicked); + m_objectParameters.push_back(new WuQMacroCommandParameter(WuQMacroDataValueTypeEnum::STRING, + "Select button with name", + "")); + m_objectParameters.push_back(new WuQMacroCommandParameter(WuQMacroDataValueTypeEnum::INTEGER, + "Select button at index", + 1)); } break; case WuQMacroWidgetTypeEnum::CHECK_BOX: @@ -130,6 +148,9 @@ m_objectName(object->objectName()) CaretAssert(checkBox); QObject::connect(checkBox, &QCheckBox::clicked, this, &WuQMacroSignalWatcher::checkBoxClicked); + m_objectParameters.push_back(new WuQMacroCommandParameter(WuQMacroDataValueTypeEnum::BOOLEAN, + "On/Off", + true)); } break; case WuQMacroWidgetTypeEnum::COMBO_BOX: @@ -138,6 +159,12 @@ m_objectName(object->objectName()) CaretAssert(comboBox); QObject::connect(comboBox, static_cast(&QComboBox::activated), this, &WuQMacroSignalWatcher::comboBoxActivated); + m_objectParameters.push_back(new WuQMacroCommandParameter(WuQMacroDataValueTypeEnum::STRING, + "Select item with name", + "")); + m_objectParameters.push_back(new WuQMacroCommandParameter(WuQMacroDataValueTypeEnum::INTEGER, + "Select item at index", + 1)); } break; case WuQMacroWidgetTypeEnum::DOUBLE_SPIN_BOX: @@ -146,6 +173,9 @@ m_objectName(object->objectName()) CaretAssert(spinBox); QObject::connect(spinBox, static_cast(&QDoubleSpinBox::valueChanged), this, &WuQMacroSignalWatcher::doubleSpinBoxValueChanged); + m_objectParameters.push_back(new WuQMacroCommandParameter(WuQMacroDataValueTypeEnum::FLOAT, + "New value", + spinBox->minimum())); } break; case WuQMacroWidgetTypeEnum::INVALID: @@ -157,6 +187,9 @@ m_objectName(object->objectName()) CaretAssert(lineEdit); QObject::connect(lineEdit, &QLineEdit::textEdited, this, &WuQMacroSignalWatcher::lineEditTextEdited); + m_objectParameters.push_back(new WuQMacroCommandParameter(WuQMacroDataValueTypeEnum::STRING, + "New text", + "")); } break; case WuQMacroWidgetTypeEnum::LIST_WIDGET: @@ -165,6 +198,12 @@ m_objectName(object->objectName()) CaretAssert(listWidget); QObject::connect(listWidget, &QListWidget::itemActivated, this, &WuQMacroSignalWatcher::listWidgetItemActivated); + m_objectParameters.push_back(new WuQMacroCommandParameter(WuQMacroDataValueTypeEnum::STRING, + "Select item with name", + "")); + m_objectParameters.push_back(new WuQMacroCommandParameter(WuQMacroDataValueTypeEnum::INTEGER, + "Select item at index", + 1)); } break; case WuQMacroWidgetTypeEnum::MENU: @@ -173,6 +212,12 @@ m_objectName(object->objectName()) CaretAssert(menu); QObject::connect(menu, &QMenu::triggered, this, &WuQMacroSignalWatcher::menuTriggered); + m_objectParameters.push_back(new WuQMacroCommandParameter(WuQMacroDataValueTypeEnum::STRING, + "Select item with name", + "")); + m_objectParameters.push_back(new WuQMacroCommandParameter(WuQMacroDataValueTypeEnum::INTEGER, + "Select item at index", + 1)); } break; case WuQMacroWidgetTypeEnum::PUSH_BUTTON: @@ -181,6 +226,9 @@ m_objectName(object->objectName()) CaretAssert(pushButton); QObject::connect(pushButton, &QPushButton::clicked, this, &WuQMacroSignalWatcher::pushButtonClicked); + m_objectParameters.push_back(new WuQMacroCommandParameter(WuQMacroDataValueTypeEnum::NONE, + "Click button", + "")); } break; case WuQMacroWidgetTypeEnum::PUSH_BUTTON_CHECKABLE: @@ -189,6 +237,9 @@ m_objectName(object->objectName()) CaretAssert(pushButton); QObject::connect(pushButton, &QPushButton::clicked, this, &WuQMacroSignalWatcher::pushButtonClicked); + m_objectParameters.push_back(new WuQMacroCommandParameter(WuQMacroDataValueTypeEnum::BOOLEAN, + "On/Off", + true)); } break; case WuQMacroWidgetTypeEnum::RADIO_BUTTON: @@ -197,6 +248,9 @@ m_objectName(object->objectName()) CaretAssert(radioButton); QObject::connect(radioButton, &QRadioButton::clicked, this, &WuQMacroSignalWatcher::radioButtonClicked); + m_objectParameters.push_back(new WuQMacroCommandParameter(WuQMacroDataValueTypeEnum::NONE, /* type=NONE - RADIO BUTTON ALWAYS TRUE !!! */ + "Select button", + true)); } break; case WuQMacroWidgetTypeEnum::SLIDER: @@ -205,6 +259,10 @@ m_objectName(object->objectName()) CaretAssert(slider); QObject::connect(slider, &QSlider::valueChanged, this, &WuQMacroSignalWatcher::sliderValueChanged); + m_objectParameters.push_back(new WuQMacroCommandParameter(WuQMacroDataValueTypeEnum::INTEGER, + "Move slider to", + slider->minimum())); + } break; case WuQMacroWidgetTypeEnum::SPIN_BOX: @@ -213,6 +271,9 @@ m_objectName(object->objectName()) CaretAssert(spinBox); QObject::connect(spinBox, static_cast(&QSpinBox::valueChanged), this, &WuQMacroSignalWatcher::spinBoxValueChanged); + m_objectParameters.push_back(new WuQMacroCommandParameter(WuQMacroDataValueTypeEnum::INTEGER, + "Enter value", + spinBox->minimum())); } break; case WuQMacroWidgetTypeEnum::TAB_BAR: @@ -221,6 +282,12 @@ m_objectName(object->objectName()) CaretAssert(tabBar); QObject::connect(tabBar, &QTabBar::tabBarClicked, this, &WuQMacroSignalWatcher::tabBarCurrentChanged); + m_objectParameters.push_back(new WuQMacroCommandParameter(WuQMacroDataValueTypeEnum::STRING, + "Select tab with name", + "")); + m_objectParameters.push_back(new WuQMacroCommandParameter(WuQMacroDataValueTypeEnum::INTEGER, + "Select tab at index", + 0)); } break; case WuQMacroWidgetTypeEnum::TAB_WIDGET: @@ -229,6 +296,12 @@ m_objectName(object->objectName()) CaretAssert(tabWidget); QObject::connect(tabWidget, &QTabWidget::tabBarClicked, this, &WuQMacroSignalWatcher::tabWidgetCurrentChanged); + m_objectParameters.push_back(new WuQMacroCommandParameter(WuQMacroDataValueTypeEnum::STRING, + "Select tab with name", + "")); + m_objectParameters.push_back(new WuQMacroCommandParameter(WuQMacroDataValueTypeEnum::INTEGER, + "Select tab at index", + 0)); } break; case WuQMacroWidgetTypeEnum::TOOL_BUTTON: @@ -237,6 +310,9 @@ m_objectName(object->objectName()) CaretAssert(toolButton); QObject::connect(toolButton, &QCheckBox::clicked, this, &WuQMacroSignalWatcher::toolButtonClicked); + m_objectParameters.push_back(new WuQMacroCommandParameter(WuQMacroDataValueTypeEnum::NONE, + "Select button", + true)); } break; case WuQMacroWidgetTypeEnum::TOOL_BUTTON_CHECKABLE: @@ -245,6 +321,9 @@ m_objectName(object->objectName()) CaretAssert(toolButton); QObject::connect(toolButton, &QCheckBox::clicked, this, &WuQMacroSignalWatcher::toolButtonClicked); + m_objectParameters.push_back(new WuQMacroCommandParameter(WuQMacroDataValueTypeEnum::BOOLEAN, + "On/Off", + true)); } break; } @@ -267,6 +346,10 @@ m_objectName(object->objectName()) */ WuQMacroSignalWatcher::~WuQMacroSignalWatcher() { + for (auto p : m_objectParameters) { + delete p; + } + m_objectParameters.clear(); } /** @@ -497,6 +580,15 @@ WuQMacroSignalWatcher::newInstance(WuQMacroManager* parentMacroManager, return ww; } +/** + * @return Name of the object + */ +QString +WuQMacroSignalWatcher::getObjectName() const +{ + return m_objectName; +} + /** * @return Tooltip for this signal watcher */ @@ -506,6 +598,36 @@ WuQMacroSignalWatcher::getToolTip() const return m_toolTipText; } +/** + * Create a macro command for this widget watcher with default + * (essentially unset) parameters that need to be set by user + * + * @param errorMessageOut + * Contains error information if failure. + * @return + * Pointer to new command or NULL if failure. Caller + * is responsible for destroying returned command + */ +WuQMacroCommand* +WuQMacroSignalWatcher::createMacroCommandWithDefaultParameters(QString& errorMessageOut) const +{ + const int32_t versionNumber(1); + WuQMacroCommand* mc = WuQMacroCommand::newInstanceWidgetCommand(m_objectType, + versionNumber, + m_objectName, + m_descriptiveName, + m_toolTipText, + 1.0, + errorMessageOut); + if (mc != NULL) { + std::vector parameters = getCopyOfObjectParameters(); + for (auto p : parameters) { + mc->addParameter(p); + } + } + + return mc; +} /** * If recording mode is enabled, create and send a macro command @@ -577,15 +699,11 @@ WuQMacroSignalWatcher::actionGroupTriggered(QAction* action) ? action->text() : ""); - std::vector params; - params.push_back(new WuQMacroCommandParameter(WuQMacroDataValueTypeEnum::STRING, - "Select name", - actionText)); - params.push_back(new WuQMacroCommandParameter(WuQMacroDataValueTypeEnum::INTEGER, - "Select index", - actionIndex)); - - createAndSendMacroCommand(params); + std::vector params = getCopyOfObjectParameters(); + CaretAssert(params.size() >= 2); + params[0]->setValue(actionText); + params[1]->setValue(actionIndex); + createAndSendMacroCommand(params); } @@ -598,11 +716,9 @@ WuQMacroSignalWatcher::actionGroupTriggered(QAction* action) void WuQMacroSignalWatcher::actionTriggered(bool /*checked*/) { - std::vector params; - params.push_back(new WuQMacroCommandParameter(WuQMacroDataValueTypeEnum::NONE, - "Click/Select", - "")); - + std::vector params = getCopyOfObjectParameters(); + CaretAssert(params.size() >= 1); + params[0]->setValue(""); createAndSendMacroCommand(params); } @@ -615,11 +731,9 @@ WuQMacroSignalWatcher::actionTriggered(bool /*checked*/) void WuQMacroSignalWatcher::actionCheckableTriggered(bool checked) { - std::vector params; - params.push_back(new WuQMacroCommandParameter(WuQMacroDataValueTypeEnum::BOOLEAN, - "On/Off", - checked)); - + std::vector params = getCopyOfObjectParameters(); + CaretAssert(params.size() >= 1); + params[0]->setValue(checked); createAndSendMacroCommand(params); } @@ -647,14 +761,11 @@ WuQMacroSignalWatcher::buttonGroupButtonClicked(QAbstractButton* button) const QString buttonText((button != NULL) ? button->text() : ""); - std::vector params; - params.push_back(new WuQMacroCommandParameter(WuQMacroDataValueTypeEnum::STRING, - "Select button with name", - buttonText)); - params.push_back(new WuQMacroCommandParameter(WuQMacroDataValueTypeEnum::INTEGER, - "Select button at index", - buttonIndex)); - + + std::vector params = getCopyOfObjectParameters(); + CaretAssert(params.size() >= 2); + params[0]->setValue(buttonText); + params[1]->setValue(buttonIndex); createAndSendMacroCommand(params); } @@ -668,11 +779,9 @@ WuQMacroSignalWatcher::buttonGroupButtonClicked(QAbstractButton* button) void WuQMacroSignalWatcher::checkBoxClicked(bool checked) { - std::vector params; - params.push_back(new WuQMacroCommandParameter(WuQMacroDataValueTypeEnum::BOOLEAN, - "On/Off", - checked)); - + std::vector params = getCopyOfObjectParameters(); + CaretAssert(params.size() >= 1); + params[0]->setValue(checked); createAndSendMacroCommand(params); } @@ -694,15 +803,12 @@ WuQMacroSignalWatcher::comboBoxActivated(int index) text = comboBox->itemText(index); } - std::vector params; - params.push_back(new WuQMacroCommandParameter(WuQMacroDataValueTypeEnum::STRING, - "Select item with name", - text)); - params.push_back(new WuQMacroCommandParameter(WuQMacroDataValueTypeEnum::INTEGER, - "Select item at index", - index)); - + std::vector params = getCopyOfObjectParameters(); + CaretAssert(params.size() >= 2); + params[0]->setValue(text); + params[1]->setValue(index); createAndSendMacroCommand(params); + } /** @@ -714,11 +820,9 @@ WuQMacroSignalWatcher::comboBoxActivated(int index) void WuQMacroSignalWatcher::doubleSpinBoxValueChanged(double value) { - std::vector params; - params.push_back(new WuQMacroCommandParameter(WuQMacroDataValueTypeEnum::FLOAT, - "New value", - value)); - + std::vector params = getCopyOfObjectParameters(); + CaretAssert(params.size() >= 1); + params[0]->setValue(value); createAndSendMacroCommand(params); } @@ -731,11 +835,9 @@ WuQMacroSignalWatcher::doubleSpinBoxValueChanged(double value) void WuQMacroSignalWatcher::lineEditTextEdited(const QString& text) { - std::vector params; - params.push_back(new WuQMacroCommandParameter(WuQMacroDataValueTypeEnum::STRING, - "New text", - text)); - + std::vector params = getCopyOfObjectParameters(); + CaretAssert(params.size() >= 1); + params[0]->setValue(text); createAndSendMacroCommand(params); } @@ -756,14 +858,11 @@ WuQMacroSignalWatcher::listWidgetItemActivated(QListWidgetItem* item) const QString text((item != NULL) ? item->text() : ""); - std::vector params; - params.push_back(new WuQMacroCommandParameter(WuQMacroDataValueTypeEnum::STRING, - "Select item with name", - text)); - params.push_back(new WuQMacroCommandParameter(WuQMacroDataValueTypeEnum::INTEGER, - "Select item at index", - rowIndex)); - + + std::vector params = getCopyOfObjectParameters(); + CaretAssert(params.size() >= 2); + params[0]->setValue(text); + params[1]->setValue(rowIndex); createAndSendMacroCommand(params); } @@ -791,14 +890,10 @@ WuQMacroSignalWatcher::menuTriggered(QAction* action) const QString text((action != NULL) ? action->text() : ""); - std::vector params; - params.push_back(new WuQMacroCommandParameter(WuQMacroDataValueTypeEnum::STRING, - "Select item with name", - text)); - params.push_back(new WuQMacroCommandParameter(WuQMacroDataValueTypeEnum::INTEGER, - "Select item at index", - actionIndex)); - + std::vector params = getCopyOfObjectParameters(); + CaretAssert(params.size() >= 2); + params[0]->setValue(text); + params[1]->setValue(actionIndex); createAndSendMacroCommand(params); } @@ -811,11 +906,9 @@ WuQMacroSignalWatcher::menuTriggered(QAction* action) void WuQMacroSignalWatcher::pushButtonClicked(bool /*checked*/) { - std::vector params; - params.push_back(new WuQMacroCommandParameter(WuQMacroDataValueTypeEnum::NONE, - "Click button", - "")); - + std::vector params = getCopyOfObjectParameters(); + CaretAssert(params.size() >= 1); + params[0]->setValue(""); createAndSendMacroCommand(params); } @@ -828,11 +921,9 @@ WuQMacroSignalWatcher::pushButtonClicked(bool /*checked*/) void WuQMacroSignalWatcher::pushButtonCheckableClicked(bool checked) { - std::vector params; - params.push_back(new WuQMacroCommandParameter(WuQMacroDataValueTypeEnum::NONE, - "On/Off", - checked)); - + std::vector params = getCopyOfObjectParameters(); + CaretAssert(params.size() >= 1); + params[0]->setValue(checked); createAndSendMacroCommand(params); } @@ -844,13 +935,11 @@ WuQMacroSignalWatcher::pushButtonCheckableClicked(bool checked) * New checked status */ void -WuQMacroSignalWatcher::radioButtonClicked(bool /*checked*/) +WuQMacroSignalWatcher::radioButtonClicked(bool checked) { - std::vector params; - params.push_back(new WuQMacroCommandParameter(WuQMacroDataValueTypeEnum::NONE, - "Select button", - "")); - + std::vector params = getCopyOfObjectParameters(); + CaretAssert(params.size() >= 1); + params[0]->setValue(checked); createAndSendMacroCommand(params); } @@ -863,12 +952,9 @@ WuQMacroSignalWatcher::radioButtonClicked(bool /*checked*/) void WuQMacroSignalWatcher::sliderValueChanged(int value) { - std::vector params; - - params.push_back(new WuQMacroCommandParameter(WuQMacroDataValueTypeEnum::INTEGER, - "Move slider to", - value)); - + std::vector params = getCopyOfObjectParameters(); + CaretAssert(params.size() >= 1); + params[0]->setValue(value); createAndSendMacroCommand(params); } @@ -881,12 +967,9 @@ WuQMacroSignalWatcher::sliderValueChanged(int value) void WuQMacroSignalWatcher::spinBoxValueChanged(int value) { - std::vector params; - - params.push_back(new WuQMacroCommandParameter(WuQMacroDataValueTypeEnum::INTEGER, - "Enter value", - value)); - + std::vector params = getCopyOfObjectParameters(); + CaretAssert(params.size() >= 1); + params[0]->setValue(value); createAndSendMacroCommand(params); } @@ -903,15 +986,10 @@ WuQMacroSignalWatcher::tabBarCurrentChanged(int index) CaretAssert(tabBar); const QString tabText = tabBar->tabText(index); - std::vector params; - - params.push_back(new WuQMacroCommandParameter(WuQMacroDataValueTypeEnum::STRING, - "Select tab with name", - tabText)); - params.push_back(new WuQMacroCommandParameter(WuQMacroDataValueTypeEnum::INTEGER, - "Select tab at index", - index)); - + std::vector params = getCopyOfObjectParameters(); + CaretAssert(params.size() >= 2); + params[0]->setValue(tabText); + params[1]->setValue(index); createAndSendMacroCommand(params); } @@ -928,14 +1006,10 @@ WuQMacroSignalWatcher::tabWidgetCurrentChanged(int index) CaretAssert(tabWidget); const QString tabText = tabWidget->tabText(index); - std::vector params; - params.push_back(new WuQMacroCommandParameter(WuQMacroDataValueTypeEnum::STRING, - "Select tab with name", - tabText)); - params.push_back(new WuQMacroCommandParameter(WuQMacroDataValueTypeEnum::INTEGER, - "Select tab at index", - index)); - + std::vector params = getCopyOfObjectParameters(); + CaretAssert(params.size() >= 2); + params[0]->setValue(tabText); + params[1]->setValue(index); createAndSendMacroCommand(params); } @@ -948,11 +1022,9 @@ WuQMacroSignalWatcher::tabWidgetCurrentChanged(int index) void WuQMacroSignalWatcher::toolButtonClicked(bool /*checked*/) { - std::vector params; - params.push_back(new WuQMacroCommandParameter(WuQMacroDataValueTypeEnum::NONE, - "Select button", - "")); - + std::vector params = getCopyOfObjectParameters(); + CaretAssert(params.size() >= 1); + params[0]->setValue(""); createAndSendMacroCommand(params); } @@ -965,11 +1037,9 @@ WuQMacroSignalWatcher::toolButtonClicked(bool /*checked*/) void WuQMacroSignalWatcher::toolButtonCheckableClicked(bool checked) { - std::vector params; - params.push_back(new WuQMacroCommandParameter(WuQMacroDataValueTypeEnum::BOOLEAN, - "On/Off", - checked)); - + std::vector params = getCopyOfObjectParameters(); + CaretAssert(params.size() >= 1); + params[0]->setValue(checked); createAndSendMacroCommand(params); } @@ -985,4 +1055,18 @@ WuQMacroSignalWatcher::toString() const return s; } +/** + * @return A copy of the object's parameters + */ +std::vector +WuQMacroSignalWatcher::getCopyOfObjectParameters() const +{ + std::vector paramsCopy; + for(auto p : m_objectParameters) { + paramsCopy.push_back(new WuQMacroCommandParameter(*p)); + } + + return paramsCopy; +} + diff --git a/src/GuiQt/WuQMacroSignalWatcher.h b/src/GuiQt/WuQMacroSignalWatcher.h index 733aeb42c..ce102a4ac 100644 --- a/src/GuiQt/WuQMacroSignalWatcher.h +++ b/src/GuiQt/WuQMacroSignalWatcher.h @@ -35,6 +35,7 @@ class QListWidgetItem; namespace caret { + class WuQMacroCommand; class WuQMacroCommandParameter; class WuQMacroManager; @@ -55,6 +56,10 @@ namespace caret { WuQMacroSignalWatcher& operator=(const WuQMacroSignalWatcher&) = delete; + WuQMacroCommand* createMacroCommandWithDefaultParameters(QString& errorMessageOut) const; + + QString getObjectName() const; + QString toString() const; QString getToolTip() const; @@ -115,6 +120,8 @@ namespace caret { void createAndSendMacroCommand(std::vector& parameters); + std::vector getCopyOfObjectParameters() const; + QObject* m_object; const WuQMacroWidgetTypeEnum::Enum m_objectType; @@ -125,6 +132,8 @@ namespace caret { QString m_toolTipText; + std::vector m_objectParameters; + // ADD_NEW_MEMBERS_HERE }; -- GitLab From 11da89a85615127a3da39945e5dc9d3bc6197906 Mon Sep 17 00:00:00 2001 From: John Harwell Date: Mon, 18 Feb 2019 14:58:48 -0600 Subject: [PATCH 192/427] WB-834 Macros: Improvements to Add Macro Command dialog including Apply button to allow adding multiple commands without closing dialog and preserve size of dialog between uses. --- src/GuiQt/WuQMacroDialog.cxx | 145 +++++------------ src/GuiQt/WuQMacroDialog.h | 2 + .../WuQMacroNewCommandSelectionDialog.cxx | 150 +++++++++++++++--- src/GuiQt/WuQMacroNewCommandSelectionDialog.h | 19 ++- 4 files changed, 185 insertions(+), 131 deletions(-) diff --git a/src/GuiQt/WuQMacroDialog.cxx b/src/GuiQt/WuQMacroDialog.cxx index f27ac9fdb..a26cec810 100644 --- a/src/GuiQt/WuQMacroDialog.cxx +++ b/src/GuiQt/WuQMacroDialog.cxx @@ -505,7 +505,6 @@ WuQMacroDialog::createCommandDisplayWidget() m_commandDelaySpinBox->setSingleStep(1.0); m_commandDelaySpinBox->setDecimals(1); m_commandDelaySpinBox->setToolTip("Delay, in seconds, after running command"); -// m_commandDelaySpinBox->setFixedWidth(150); m_commandDelaySpinBox->setSizePolicy(QSizePolicy::Fixed, m_commandDelaySpinBox->sizePolicy().verticalPolicy()); QObject::connect(m_commandDelaySpinBox, static_cast(&QDoubleSpinBox::valueChanged), @@ -1321,114 +1320,52 @@ void WuQMacroDialog::insertMenuNewMacroCommandSelected() { WuQMacroNewCommandSelectionDialog dialog(this); + QObject::connect(&dialog, &WuQMacroNewCommandSelectionDialog::signalNewMacroCommandCreated, + this, &WuQMacroDialog::addNewMacroCommand); if (dialog.exec() == WuQMacroNewCommandSelectionDialog::Accepted) { - QString errorMessage; - WuQMacroCommand* newCommand = dialog.getNewInstanceOfSelectedCommand(errorMessage); - if (newCommand != NULL) { - switch (getSelectedItemType()) { - case WuQMacroStandardItemTypeEnum::INVALID: - CaretAssert(0); - break; - case WuQMacroStandardItemTypeEnum::MACRO: - { - WuQMacro* macro = getSelectedMacro(); - CaretAssert(macro); - macro->insertMacroCommandAtIndex(0, newCommand); - } - break; - case WuQMacroStandardItemTypeEnum::MACRO_COMMAND: - { - WuQMacro* macro = getSelectedMacro(); - CaretAssert(macro); - const WuQMacroCommand* selectedCommand = getSelectedMacroCommand(); - CaretAssert(selectedCommand); - const int32_t selectedIndex = macro->getIndexOfMacroCommand(selectedCommand); - macro->insertMacroCommandAtIndex(selectedIndex + 1, - newCommand); - } - } - - WuQMacroGroup* selectedGroup = getSelectedMacroGroup(); - CaretAssert(selectedGroup); - QModelIndex selectedIndex = selectedGroup->indexFromItem(newCommand); - m_treeView->setCurrentIndex(selectedIndex); - - updateDialogContents(); + } +} + +/** + * Add a new macro command + */ +void +WuQMacroDialog::addNewMacroCommand(WuQMacroCommand* command) +{ + if (command == NULL) { + return; + } + + switch (getSelectedItemType()) { + case WuQMacroStandardItemTypeEnum::INVALID: + CaretAssert(0); + break; + case WuQMacroStandardItemTypeEnum::MACRO: + { + WuQMacro* macro = getSelectedMacro(); + CaretAssert(macro); + macro->insertMacroCommandAtIndex(0, + command); } - else { - QMessageBox::critical(m_editingInsertToolButton, - "Error", - errorMessage); + break; + case WuQMacroStandardItemTypeEnum::MACRO_COMMAND: + { + WuQMacro* macro = getSelectedMacro(); + CaretAssert(macro); + const WuQMacroCommand* selectedCommand = getSelectedMacroCommand(); + CaretAssert(selectedCommand); + const int32_t selectedIndex = macro->getIndexOfMacroCommand(selectedCommand); + macro->insertMacroCommandAtIndex(selectedIndex + 1, + command); } } -// return; -// -// -// std::vector customOperationNames = WuQMacroManager::instance()->getNamesOfCustomOperationMacroCommands(); -// QStringList namesStringList; -// for (const auto name : customOperationNames) { -// namesStringList.append(name); -// } -// -// if (namesStringList.isEmpty()) { -// QMessageBox::critical(m_editingInsertToolButton, -// "Error", -// "No custom operations available"); -// return; -// } -// -// bool okFlag(false); -// QString selectedName = QInputDialog::getItem(m_editingInsertToolButton, -// "Choose Operation", -// "New Operation", -// namesStringList, -// 0, -// false, -// &okFlag); -// -// if (okFlag -// && ( ! selectedName.isEmpty())) { -// QString errorMessage; -// WuQMacroCommand* newCommand = WuQMacroManager::instance()->newInstanceOfCustomOperationMacroCommand(selectedName, -// errorMessage); -// if (newCommand == NULL) { -// QMessageBox::critical(m_editingInsertToolButton, -// "Error", -// errorMessage); -// return; -// } -// -// switch (getSelectedItemType()) { -// case WuQMacroStandardItemTypeEnum::INVALID: -// CaretAssert(0); -// break; -// case WuQMacroStandardItemTypeEnum::MACRO: -// { -// WuQMacro* macro = getSelectedMacro(); -// CaretAssert(macro); -// macro->insertMacroCommandAtIndex(0, newCommand); -// } -// break; -// case WuQMacroStandardItemTypeEnum::MACRO_COMMAND: -// { -// WuQMacro* macro = getSelectedMacro(); -// CaretAssert(macro); -// const WuQMacroCommand* selectedCommand = getSelectedMacroCommand(); -// CaretAssert(selectedCommand); -// const int32_t selectedIndex = macro->getIndexOfMacroCommand(selectedCommand); -// macro->insertMacroCommandAtIndex(selectedIndex + 1, -// newCommand); -// } -// } -// -// WuQMacroGroup* selectedGroup = getSelectedMacroGroup(); -// CaretAssert(selectedGroup); -// QModelIndex selectedIndex = selectedGroup->indexFromItem(newCommand); -// m_treeView->setCurrentIndex(selectedIndex); -// -// updateDialogContents(); -// } + WuQMacroGroup* selectedGroup = getSelectedMacroGroup(); + CaretAssert(selectedGroup); + QModelIndex selectedIndex = selectedGroup->indexFromItem(command); + m_treeView->setCurrentIndex(selectedIndex); + + updateDialogContents(); } /** diff --git a/src/GuiQt/WuQMacroDialog.h b/src/GuiQt/WuQMacroDialog.h index d196a9cfe..3dc5dc771 100644 --- a/src/GuiQt/WuQMacroDialog.h +++ b/src/GuiQt/WuQMacroDialog.h @@ -115,6 +115,8 @@ namespace caret { void insertMenuNewMacroCommandSelected(); + void addNewMacroCommand(WuQMacroCommand* command); + private: enum class ValueIndex { ONE, diff --git a/src/GuiQt/WuQMacroNewCommandSelectionDialog.cxx b/src/GuiQt/WuQMacroNewCommandSelectionDialog.cxx index e26119c26..f4fd1d55c 100644 --- a/src/GuiQt/WuQMacroNewCommandSelectionDialog.cxx +++ b/src/GuiQt/WuQMacroNewCommandSelectionDialog.cxx @@ -23,6 +23,7 @@ #include "WuQMacroNewCommandSelectionDialog.h" #undef __WU_Q_MACRO_NEW_COMMAND_SELECTION_DIALOG_DECLARE__ +#include #include #include #include @@ -107,17 +108,20 @@ WuQMacroNewCommandSelectionDialog::WuQMacroNewCommandSelectionDialog(QWidget* pa row++; m_dialogButtonBox = new QDialogButtonBox(QDialogButtonBox::Ok + | QDialogButtonBox::Apply | QDialogButtonBox::Cancel); - QObject::connect(m_dialogButtonBox, &QDialogButtonBox::accepted, - this, &WuQMacroNewCommandSelectionDialog::accept); - QObject::connect(m_dialogButtonBox, &QDialogButtonBox::rejected, - this, &WuQMacroNewCommandSelectionDialog::reject); - + QObject::connect(m_dialogButtonBox, &QDialogButtonBox::clicked, + this, &WuQMacroNewCommandSelectionDialog::otherButtonClicked); + QVBoxLayout* dialogLayout = new QVBoxLayout(this); dialogLayout->addLayout(gridLayout); dialogLayout->addWidget(m_dialogButtonBox); commandTypeComboBoxActivated(); + + if ( ! s_previousDialogGeometry.isEmpty()) { + restoreGeometry(s_previousDialogGeometry); + } } /** @@ -125,6 +129,8 @@ WuQMacroNewCommandSelectionDialog::WuQMacroNewCommandSelectionDialog(QWidget* pa */ WuQMacroNewCommandSelectionDialog::~WuQMacroNewCommandSelectionDialog() { + s_previousDialogGeometry = saveGeometry(); + for (auto cc : m_customCommands) { delete cc; } @@ -165,6 +171,8 @@ WuQMacroNewCommandSelectionDialog::commandTypeComboBoxActivated() } break; } + + m_commandSelectionChangedSinceApplyClickedFlag = true; } /** @@ -218,6 +226,7 @@ WuQMacroNewCommandSelectionDialog::customCommandListWidgetItemClicked(QListWidge } m_macroDescriptionTextEdit->setPlainText(description); + m_commandSelectionChangedSinceApplyClickedFlag = true; } /** @@ -227,7 +236,7 @@ WuQMacroNewCommandSelectionDialog::customCommandListWidgetItemClicked(QListWidge * Item that was clicked */ void -WuQMacroNewCommandSelectionDialog::widgetCommandListWidgetItemClicked(QListWidgetItem* item) +WuQMacroNewCommandSelectionDialog::widgetCommandListWidgetItemClicked(QListWidgetItem* /*item*/) { QString description; const int index = m_widgetCommandListWidget->currentRow(); @@ -237,6 +246,114 @@ WuQMacroNewCommandSelectionDialog::widgetCommandListWidgetItemClicked(QListWidge } m_macroDescriptionTextEdit->setPlainText(description); + m_commandSelectionChangedSinceApplyClickedFlag = true; +} + +/** + * Called when a dialog button is clicked + */ +void +WuQMacroNewCommandSelectionDialog::otherButtonClicked(QAbstractButton* button) +{ + switch (m_dialogButtonBox->standardButton(button)) { + case QDialogButtonBox::Apply: + processApplyButtonClicked(false); + break; + case QDialogButtonBox::Ok: + accept(); + break; + case QDialogButtonBox::Cancel: + reject(); + break; + default: + CaretAssertMessage(0, ("Unknown button clicked with text \"" + + button->text() + + "\"")); + break; + } +} + +/** + * Called to process the apply button. + * + * @return True if apply button process was successful, else false. + */ +bool +WuQMacroNewCommandSelectionDialog::processApplyButtonClicked(const bool okButtonClicked) +{ + bool validFlag(false); + switch (s_lastCommandTypeSelected) { + case WuQMacroCommandTypeEnum::CUSTOM_OPERATION: + validFlag = (m_customCommandListWidget->currentItem() != NULL); + break; + case WuQMacroCommandTypeEnum::MOUSE: + CaretAssert(0); + validFlag = false; + break; + case WuQMacroCommandTypeEnum::WIDGET: + validFlag = (m_widgetCommandListWidget->currentItem() != NULL); + break; + } + + if (validFlag) { + bool addCommandFlag = true; + if (okButtonClicked) { + /* + * Both Apply and Ok add a command to the macro. If the user adds a command + * using apply and then clicks Ok without changing the selected command, + * warn the user to avoid unintentionally adding the command twice. + */ + if ( ! m_commandSelectionChangedSinceApplyClickedFlag) { + const QString msg("Selected command has not changed since Apply button was clicked. " + "Add command to macro again?"); + switch (QMessageBox::warning(this, + "Warning", + msg, + (QMessageBox::Yes | QMessageBox::No | QMessageBox::Cancel), + QMessageBox::No)) { + case QMessageBox::Yes: + addCommandFlag = true; + break; + case QMessageBox::No: + addCommandFlag = false; + break; + case QMessageBox::Cancel: + addCommandFlag = false; + validFlag = false; + break; + default: + break; + } + } + } + QString errorMessage; + if (addCommandFlag) { + WuQMacroCommand* command = NULL; + command = getNewInstanceOfSelectedCommand(errorMessage); + if (command != NULL) { + emit signalNewMacroCommandCreated(command); + if ( ! okButtonClicked) { + m_commandSelectionChangedSinceApplyClickedFlag = false; + } + } + else { + validFlag = false; + QMessageBox::critical(this, + "Error", + errorMessage, + QMessageBox::Ok, + QMessageBox::Ok); + } + } + } + else { + QMessageBox::critical(this, + "Error", + "No command is selected", + QMessageBox::Ok, + QMessageBox::Ok); + } + return validFlag; } /** @@ -249,26 +366,9 @@ void WuQMacroNewCommandSelectionDialog::done(int r) { if (r == QDialog::Accepted) { - bool validFlag(false); - switch (s_lastCommandTypeSelected) { - case WuQMacroCommandTypeEnum::CUSTOM_OPERATION: - validFlag = (m_customCommandListWidget->currentItem() != NULL); - break; - case WuQMacroCommandTypeEnum::MOUSE: - CaretAssert(0); - validFlag = false; - break; - case WuQMacroCommandTypeEnum::WIDGET: - validFlag = (m_widgetCommandListWidget->currentItem() != NULL); - break; - } - + const bool validFlag = processApplyButtonClicked(true); if ( ! validFlag) { - QMessageBox::critical(this, - "Error", - "No command is selected", - QMessageBox::Ok, - QMessageBox::Ok); + return; } } diff --git a/src/GuiQt/WuQMacroNewCommandSelectionDialog.h b/src/GuiQt/WuQMacroNewCommandSelectionDialog.h index 5a9d322cd..08a218e7e 100644 --- a/src/GuiQt/WuQMacroNewCommandSelectionDialog.h +++ b/src/GuiQt/WuQMacroNewCommandSelectionDialog.h @@ -29,6 +29,7 @@ #include "WuQMacroCommandTypeEnum.h" +class QAbstractButton; class QComboBox; class QDialogButtonBox; class QLineEdit; @@ -59,10 +60,11 @@ namespace caret { WuQMacroNewCommandSelectionDialog& operator=(const WuQMacroNewCommandSelectionDialog&) = delete; - WuQMacroCommand* getNewInstanceOfSelectedCommand(QString& errorMessageOut); - // ADD_NEW_METHODS_HERE + signals: + void signalNewMacroCommandCreated(WuQMacroCommand* command); + public slots: void commandTypeComboBoxActivated(); @@ -72,11 +74,17 @@ namespace caret { virtual void done(int r) override; + void otherButtonClicked(QAbstractButton* button); + private: + WuQMacroCommand* getNewInstanceOfSelectedCommand(QString& errorMessageOut); + void loadCustomCommandListWidget(); void loadWidgetCommandListWidget(); + bool processApplyButtonClicked(const bool okButtonClicked); + EnumComboBoxTemplate* m_commandTypeComboBox; QStackedWidget* m_stackedWidget; @@ -93,11 +101,18 @@ namespace caret { std::vector m_widgetCommands; + WuQMacroCommand* m_lastCustomCommandAdded = NULL; + + bool m_commandSelectionChangedSinceApplyClickedFlag = false; + static WuQMacroCommandTypeEnum::Enum s_lastCommandTypeSelected; + + static QByteArray s_previousDialogGeometry; }; #ifdef __WU_Q_MACRO_NEW_COMMAND_SELECTION_DIALOG_DECLARE__ WuQMacroCommandTypeEnum::Enum WuQMacroNewCommandSelectionDialog::s_lastCommandTypeSelected = WuQMacroCommandTypeEnum::CUSTOM_OPERATION; + QByteArray WuQMacroNewCommandSelectionDialog::s_previousDialogGeometry; #endif // __WU_Q_MACRO_NEW_COMMAND_SELECTION_DIALOG_DECLARE__ } // namespace -- GitLab From 5093479c0256ff7b4078e4800b9e3469018a2b35 Mon Sep 17 00:00:00 2001 From: John Harwell Date: Tue, 19 Feb 2019 10:38:47 -0600 Subject: [PATCH 193/427] Improvements to movie dialog, including: Improved default file name and path, fixed filename filters on movie file selection dialog, and show length (time) of movie images that have been captured. --- src/Brain/MovieRecorder.cxx | 62 +++++++--------- src/Brain/MovieRecorder.h | 9 +-- .../MovieRecorderVideoFormatTypeEnum.cxx | 2 +- src/GuiQt/MovieRecordingDialog.cxx | 74 +++++++++++-------- src/GuiQt/MovieRecordingDialog.h | 6 +- 5 files changed, 74 insertions(+), 79 deletions(-) diff --git a/src/Brain/MovieRecorder.cxx b/src/Brain/MovieRecorder.cxx index 49e49ac66..eb6602835 100644 --- a/src/Brain/MovieRecorder.cxx +++ b/src/Brain/MovieRecorder.cxx @@ -32,6 +32,7 @@ #include "CaretLogger.h" #include "DataFileException.h" #include "FileInformation.h" +#include "MovieRecorderVideoFormatTypeEnum.h" #include "ImageFile.h" #include "TextFile.h" @@ -289,27 +290,6 @@ MovieRecorder::setCustomWidthAndHeight(const int32_t width, m_customHeight = height; } -/** - * @return Video format type - */ -MovieRecorderVideoFormatTypeEnum::Enum -MovieRecorder::getVideoFormatType() const -{ - return m_formatType; -} - -/** - * Set the video format type - * - * @param formatType - * New format type - */ -void -MovieRecorder::setVideoFormatType(const MovieRecorderVideoFormatTypeEnum::Enum formatType) -{ - m_formatType = formatType; -} - /** * @return The capture region type */ @@ -337,22 +317,6 @@ MovieRecorder::setCaptureRegionType(const MovieRecorderCaptureRegionTypeEnum::En AString MovieRecorder::getMovieFileName() const { - if (m_movieFileName.isEmpty()) { - m_movieFileName = "Movie.mp4"; - } - - FileInformation fileInfo(m_movieFileName); - AString path, name, extension; - fileInfo.getFileComponents(path, - name, - extension); - - extension = MovieRecorderVideoFormatTypeEnum::toFileNameExtensionNoDot(getVideoFormatType()); - - m_movieFileName = FileInformation::assembleFileComponents(path, - name, - extension); - return m_movieFileName; } @@ -368,6 +332,25 @@ MovieRecorder::setMovieFileName(const AString& filename) m_movieFileName = filename; } +/** + * If the movie file name is empty, + * initialize the movie file to be in the given path. + * + * @param path + * Path of where to place the movie file + */ +void +MovieRecorder::initializeMovieFileName(const AString& path) +{ + if (m_movieFileName.isEmpty()) { + const QString extension = MovieRecorderVideoFormatTypeEnum::toFileNameExtensionNoDot(MovieRecorderVideoFormatTypeEnum::MPEG_4); + + m_movieFileName = FileInformation::assembleFileComponents(path, + "Movie", + extension); + } +} + /** * Get a description of this object's content. * @return String describing this object's content. @@ -464,6 +447,11 @@ MovieRecorder::createMovie(AString& errorMessageOut) { errorMessageOut.clear(); + if (m_movieFileName.isEmpty()) { + errorMessageOut = "Movie file name is invalid or empty"; + return false; + } + FileInformation fileInfo(m_movieFileName); if (fileInfo.exists()) { errorMessageOut = ("Movie file exists, delete or change name: " diff --git a/src/Brain/MovieRecorder.h b/src/Brain/MovieRecorder.h index fe51fb41e..a065a1e24 100644 --- a/src/Brain/MovieRecorder.h +++ b/src/Brain/MovieRecorder.h @@ -29,7 +29,6 @@ #include "MovieRecorderCaptureRegionTypeEnum.h" #include "MovieRecorderModeEnum.h" #include "MovieRecorderVideoResolutionTypeEnum.h" -#include "MovieRecorderVideoFormatTypeEnum.h" class QImage; class QStringList; @@ -73,10 +72,6 @@ namespace caret { void setCustomWidthAndHeight(const int32_t width, const int32_t height); - MovieRecorderVideoFormatTypeEnum::Enum getVideoFormatType() const; - - void setVideoFormatType(const MovieRecorderVideoFormatTypeEnum::Enum formatType); - MovieRecorderCaptureRegionTypeEnum::Enum getCaptureRegionType() const; void setCaptureRegionType(const MovieRecorderCaptureRegionTypeEnum::Enum captureRegionType); @@ -85,6 +80,8 @@ namespace caret { void setMovieFileName(const AString& filename); + void initializeMovieFileName(const AString& path); + int32_t getNumberOfFrames() const; float getFramesRate() const; @@ -127,8 +124,6 @@ namespace caret { MovieRecorderVideoResolutionTypeEnum::Enum m_resolutionType = MovieRecorderVideoResolutionTypeEnum::SD_640_480; - MovieRecorderVideoFormatTypeEnum::Enum m_formatType = MovieRecorderVideoFormatTypeEnum::MPEG_4; - MovieRecorderCaptureRegionTypeEnum::Enum m_captureRegionType = MovieRecorderCaptureRegionTypeEnum::GRAPHICS; int32_t m_windowIndex = 0; diff --git a/src/Brain/MovieRecorderVideoFormatTypeEnum.cxx b/src/Brain/MovieRecorderVideoFormatTypeEnum.cxx index c28e066d4..f13699585 100644 --- a/src/Brain/MovieRecorderVideoFormatTypeEnum.cxx +++ b/src/Brain/MovieRecorderVideoFormatTypeEnum.cxx @@ -400,5 +400,5 @@ MovieRecorderVideoFormatTypeEnum::toFileDialogFilter(Enum enumValue) { if (initializedFlag == false) initialize(); const MovieRecorderVideoFormatTypeEnum* enumInstance = findData(enumValue); - return enumInstance->filenameExtensionNoDot; + return enumInstance->fileDialogFilter; } diff --git a/src/GuiQt/MovieRecordingDialog.cxx b/src/GuiQt/MovieRecordingDialog.cxx index 3cc6db51e..b6ee87552 100644 --- a/src/GuiQt/MovieRecordingDialog.cxx +++ b/src/GuiQt/MovieRecordingDialog.cxx @@ -34,6 +34,7 @@ #include #include +#include "Brain.h" #include "BrainBrowserWindowComboBox.h" #include "CaretAssert.h" #include "CaretFileDialog.h" @@ -43,7 +44,9 @@ #include "EventGraphicsUpdateOneWindow.h" #include "EnumComboBoxTemplate.h" #include "FileInformation.h" +#include "GuiManager.h" #include "MovieRecorder.h" +#include "MovieRecorderVideoFormatTypeEnum.h" #include "SessionManager.h" #include "WuQMessageBox.h" @@ -76,6 +79,10 @@ MovieRecordingDialog::MovieRecordingDialog(QWidget* parent) setApplyButtonText(""); disableAutoDefaultForAllPushButtons(); + + MovieRecorder* movieRecorder = SessionManager::get()->getMovieRecorder(); + CaretAssert(movieRecorder); + movieRecorder->initializeMovieFileName(GuiManager::get()->getBrain()->getCurrentDirectory()); } /** @@ -124,9 +131,6 @@ MovieRecordingDialog::updateDialog() break; } - const MovieRecorderVideoFormatTypeEnum::Enum formatType = movieRecorder->getVideoFormatType(); - m_movieRecorderVideoFormatTypeEnumComboBox->setSelectedItem(formatType); - const MovieRecorderVideoResolutionTypeEnum::Enum resType = movieRecorder->getVideoResolutionType(); m_movieRecorderVideoResolutionTypeEnumComboBox->setSelectedItem(resType); @@ -207,9 +211,16 @@ MovieRecordingDialog::updateFileNameLabel() void MovieRecordingDialog::updateFrameCountLabel() { - const int32_t numberOfFrames = SessionManager::get()->getMovieRecorder()->getNumberOfFrames(); + MovieRecorder* movieRecorder = SessionManager::get()->getMovieRecorder(); + const int32_t numberOfFrames = movieRecorder->getNumberOfFrames(); m_frameCountNumberLabel->setNum(numberOfFrames); + const int32_t timeSeconds = (movieRecorder->getNumberOfFrames() + / movieRecorder->getFramesRate()); + QTime qtime(0, 0, 0, 0); + QTime timeForString = qtime.addSecs(timeSeconds); + m_lengthLabel->setText(timeForString.toString("h:mm:ss")); + /* * Do not allow user to change image size once an image has been captured */ @@ -239,17 +250,6 @@ MovieRecordingDialog::movieRecorderVideoResolutionTypeEnumComboBoxItemActivated( updateCustomWidthHeightSpinBoxes(); } -/** - * Called when recording format is changed - */ -void -MovieRecordingDialog::movieRecorderVideoFormatTypeEnumComboBoxItemActivated() -{ - const MovieRecorderVideoFormatTypeEnum::Enum formatType = m_movieRecorderVideoFormatTypeEnumComboBox->getSelectedItem(); - SessionManager::get()->getMovieRecorder()->setVideoFormatType(formatType); - updateFileNameLabel(); -} - /** * Called when capture region type is changed */ @@ -370,13 +370,33 @@ MovieRecordingDialog::manualCaptureSecondsSpinBoxValueChanged(int seconds) void MovieRecordingDialog::fileNameButtonClicked() { - const MovieRecorderVideoFormatTypeEnum::Enum formatType = m_movieRecorderVideoFormatTypeEnumComboBox->getSelectedItem(); + MovieRecorder* movieRecorder = SessionManager::get()->getMovieRecorder(); + QString currentFileName = movieRecorder->getMovieFileName(); + MovieRecorderVideoFormatTypeEnum::Enum formatType = MovieRecorderVideoFormatTypeEnum::MPEG_4; + + QString filters; + QString selectedFilter = MovieRecorderVideoFormatTypeEnum::toFileDialogFilter(formatType); + std::vector formatEnums; + MovieRecorderVideoFormatTypeEnum::getAllEnums(formatEnums); + for (auto fe : formatEnums) { + if ( ! filters.isEmpty()) { + filters.append(";;"); + } + filters.append(MovieRecorderVideoFormatTypeEnum::toFileDialogFilter(fe)); + + if (currentFileName.endsWith(MovieRecorderVideoFormatTypeEnum::toFileNameExtensionNoDot(fe))) { + formatType = fe; + selectedFilter = MovieRecorderVideoFormatTypeEnum::toFileDialogFilter(fe); + } + } + QString filename = CaretFileDialog::getSaveFileNameDialog(m_filenamePushButton, "Choose Movie File", - "", - MovieRecorderVideoFormatTypeEnum::toFileNameExtensionNoDot(formatType)); + currentFileName, + filters, + &selectedFilter); if ( ! filename.isEmpty()) { - SessionManager::get()->getMovieRecorder()->setMovieFileName(filename); + movieRecorder->setMovieFileName(filename); updateFileNameLabel(); } } @@ -540,6 +560,9 @@ MovieRecordingDialog::createMainWidget() QLabel* frameCountLabel = new QLabel("Frames: "); m_frameCountNumberLabel = new QLabel("0"); + QLabel* lengthLabel = new QLabel("Length: "); + m_lengthLabel = new QLabel("0"); + QGroupBox* movieFileGroupBox = new QGroupBox("Output Movie"); QGridLayout* movieLayout = new QGridLayout(movieFileGroupBox); movieLayout->setColumnStretch(0, 0); @@ -551,6 +574,8 @@ MovieRecordingDialog::createMainWidget() movieLayout->addWidget(m_filenameLabel, movieRow, 1, 1, 2, Qt::AlignLeft); movieRow++; movieLayout->addWidget(m_createMoviePushButton, movieRow, 0); + movieLayout->addWidget(lengthLabel, movieRow, 1); + movieLayout->addWidget(m_lengthLabel, movieRow, 2); movieRow++; movieLayout->addWidget(m_resetPushButton, movieRow, 0); movieLayout->addWidget(frameCountLabel, movieRow, 1); @@ -576,13 +601,6 @@ MovieRecordingDialog::createSettingsWidget() { const int spinBoxWidth(100); - QLabel* formatLabel = new QLabel("Format:"); - m_movieRecorderVideoFormatTypeEnumComboBox = new EnumComboBoxTemplate(this); - m_movieRecorderVideoFormatTypeEnumComboBox->getWidget()->setToolTip("Choose video format (not all formats on all computers)"); - m_movieRecorderVideoFormatTypeEnumComboBox->setup(); - QObject::connect(m_movieRecorderVideoFormatTypeEnumComboBox, SIGNAL(itemActivated()), - this, SLOT(movieRecorderVideoFormatTypeEnumComboBoxItemActivated())); - QLabel* resolutionLabel = new QLabel("Resolution:"); m_movieRecorderVideoResolutionTypeEnumComboBox = new EnumComboBoxTemplate(this); m_movieRecorderVideoResolutionTypeEnumComboBox->getWidget()->setToolTip("Choose width and height of movie"); @@ -625,10 +643,6 @@ MovieRecordingDialog::createSettingsWidget() gridLayout->setColumnStretch(2, 0); gridLayout->setColumnStretch(3, 100); int32_t row(0); - gridLayout->addWidget(formatLabel, row, 0); - gridLayout->addWidget(m_movieRecorderVideoFormatTypeEnumComboBox->getWidget(), - row, 1, 1, 2, Qt::AlignLeft); - row++; gridLayout->addWidget(resolutionLabel, row, 0); gridLayout->addWidget(m_movieRecorderVideoResolutionTypeEnumComboBox->getWidget(), row, 1, 1, 2, Qt::AlignLeft); diff --git a/src/GuiQt/MovieRecordingDialog.h b/src/GuiQt/MovieRecordingDialog.h index f4fd969d0..1682a1537 100644 --- a/src/GuiQt/MovieRecordingDialog.h +++ b/src/GuiQt/MovieRecordingDialog.h @@ -66,8 +66,6 @@ namespace caret { private slots: void movieRecorderVideoResolutionTypeEnumComboBoxItemActivated(); - void movieRecorderVideoFormatTypeEnumComboBoxItemActivated(); - void movieRecorderCaptureRegionTypeComboBoxActivated(); void recordingModeRadioButtonClicked(QAbstractButton* button); @@ -113,8 +111,6 @@ namespace caret { EnumComboBoxTemplate* m_movieRecorderVideoResolutionTypeEnumComboBox; - EnumComboBoxTemplate* m_movieRecorderVideoFormatTypeEnumComboBox; - QSpinBox* m_customWidthSpinBox; QSpinBox* m_customHeightSpinBox; @@ -131,6 +127,8 @@ namespace caret { QLabel* m_frameCountNumberLabel; + QLabel* m_lengthLabel; + BrainBrowserWindowComboBox* m_windowComboBox; EnumComboBoxTemplate* m_movieRecorderCaptureRegionTypeComboBox; -- GitLab From f5d3277d36c734d5d85b32903e91dc357aee67f4 Mon Sep 17 00:00:00 2001 From: John Harwell Date: Tue, 19 Feb 2019 14:26:01 -0600 Subject: [PATCH 194/427] Ensure movie file name has the correct extension when selected from a file selection dialog. --- src/GuiQt/MovieRecordingDialog.cxx | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/src/GuiQt/MovieRecordingDialog.cxx b/src/GuiQt/MovieRecordingDialog.cxx index b6ee87552..8e645f7bf 100644 --- a/src/GuiQt/MovieRecordingDialog.cxx +++ b/src/GuiQt/MovieRecordingDialog.cxx @@ -395,6 +395,19 @@ MovieRecordingDialog::fileNameButtonClicked() currentFileName, filters, &selectedFilter); + + if (selectedFilter.isEmpty()) { + for (auto fe : formatEnums) { + if (selectedFilter == MovieRecorderVideoFormatTypeEnum::toFileDialogFilter(fe)) { + const QString ext = ("." + MovieRecorderVideoFormatTypeEnum::toFileNameExtensionNoDot(fe)); + if ( ! filename.endsWith(ext)) { + filename.append(ext); + break; + } + } + } + } + if ( ! filename.isEmpty()) { movieRecorder->setMovieFileName(filename); updateFileNameLabel(); -- GitLab From 7d709d4f8a9e3b81612bad650b7e5b8580e6767b Mon Sep 17 00:00:00 2001 From: John Harwell Date: Tue, 19 Feb 2019 14:35:18 -0600 Subject: [PATCH 195/427] WB-834: Default 'move mouse' while running macro to OFF; Implemented capturing of images during the "Delay x seconds after command" if movie recording is on; Improved tooltips and text for new macro commands on macro dialog; enable 'run' macro button when a command within a macro is selected; Fixed failure to add extension to name of macro file when selected from a file dialog. On Add New Macro Command dialog: add splitter to allow sizing of commands and description section and preserve size of dialog between uses. --- src/GuiQt/WbMacroHelper.cxx | 43 +++++++++++++ src/GuiQt/WbMacroHelper.h | 3 + src/GuiQt/WuQMacroDialog.cxx | 29 ++------- src/GuiQt/WuQMacroDialog.h | 2 - src/GuiQt/WuQMacroExecutor.cxx | 3 + src/GuiQt/WuQMacroExecutor.h | 4 ++ src/GuiQt/WuQMacroExecutorOptions.h | 2 +- src/GuiQt/WuQMacroHelperInterface.h | 12 ++++ src/GuiQt/WuQMacroManager.cxx | 64 ++++++++++++++----- src/GuiQt/WuQMacroManager.h | 6 ++ .../WuQMacroNewCommandSelectionDialog.cxx | 49 +++++++++----- src/GuiQt/WuQMacroNewCommandSelectionDialog.h | 6 ++ 12 files changed, 164 insertions(+), 59 deletions(-) diff --git a/src/GuiQt/WbMacroHelper.cxx b/src/GuiQt/WbMacroHelper.cxx index 2ef5ed827..9d7346071 100644 --- a/src/GuiQt/WbMacroHelper.cxx +++ b/src/GuiQt/WbMacroHelper.cxx @@ -28,6 +28,8 @@ #include "CaretAssert.h" #include "CaretLogger.h" #include "CaretPreferences.h" +#include "EventGraphicsUpdateAllWindows.h" +#include "EventGraphicsUpdateOneWindow.h" #include "EventManager.h" #include "EventSceneActive.h" #include "EventUserInterfaceUpdate.h" @@ -233,3 +235,44 @@ WbMacroHelper::macroExecutionEnding(const WuQMacro* /*macro*/, movieRecorder->setRecordingMode(m_savedRecordingMode); EventManager::get()->sendSimpleEvent(EventTypeEnum::EVENT_MOVIE_RECORDING_DIALOG_UPDATE); } +/** + * Called by macro executor just after a command has completed execution + * + * @param window + * Widget for parent + * @param command + * Command that has just finished + */ +void +WbMacroHelper::macroCommandHasCompleted(QWidget* window, + const WuQMacroCommand* command) +{ + MovieRecorder* movieRecorder = SessionManager::get()->getMovieRecorder(); + CaretAssert(movieRecorder); + + if (command->getCommandType() != WuQMacroCommandTypeEnum::MOUSE) { + if (command->getDelayInSeconds() > 0.0) { + switch (movieRecorder->getRecordingMode()) { + case MovieRecorderModeEnum::AUTOMATIC: + { + BrainBrowserWindow* bbw = dynamic_cast(window); + const int32_t numberOfFrames = static_cast(movieRecorder->getFramesRate() + * command->getDelayInSeconds()); + for (int32_t i = 0; i < numberOfFrames; i++) { + movieRecorder->setManualRecordingOfImageRequested(true); + if (bbw != NULL) { + EventManager::get()->sendEvent(EventGraphicsUpdateOneWindow(bbw->getBrowserWindowIndex()).getPointer()); + } + else { + EventManager::get()->sendEvent(EventGraphicsUpdateAllWindows().getPointer()); + } + movieRecorder->setManualRecordingOfImageRequested(false); + } + } + break; + case MovieRecorderModeEnum::MANUAL: + break; + } + } + } +} diff --git a/src/GuiQt/WbMacroHelper.h b/src/GuiQt/WbMacroHelper.h index 9bf4c8f9e..8b52252ec 100644 --- a/src/GuiQt/WbMacroHelper.h +++ b/src/GuiQt/WbMacroHelper.h @@ -64,6 +64,9 @@ namespace caret { QWidget* window, const WuQMacroExecutorOptions* executorOptions) override; + virtual void macroCommandHasCompleted(QWidget* window, + const WuQMacroCommand* command); + // ADD_NEW_METHODS_HERE private: diff --git a/src/GuiQt/WuQMacroDialog.cxx b/src/GuiQt/WuQMacroDialog.cxx index a26cec810..d2355e893 100644 --- a/src/GuiQt/WuQMacroDialog.cxx +++ b/src/GuiQt/WuQMacroDialog.cxx @@ -178,7 +178,8 @@ WuQMacroDialog::createMacroRunAndEditingToolButtons() QPixmap runPixmap = createEditingToolButtonPixmap(m_runMacroToolButton, EditButton::RUN); m_runMacroToolButton->setIcon(runPixmap); - m_runMacroToolButton->setToolTip("Run the selected macro"); + m_runMacroToolButton->setToolTip("Runs the selected macro. If a command is selected, " + "the macro containing the command is run."); QObject::connect(m_runMacroToolButton, &QToolButton::clicked, this, &WuQMacroDialog::runMacroToolButtonClicked); @@ -220,7 +221,7 @@ WuQMacroDialog::createMacroRunAndEditingToolButtons() QPixmap insertPixmap = createEditingToolButtonPixmap(m_editingInsertToolButton, EditButton::INSERTER); m_editingInsertToolButton->setIcon(insertPixmap); - m_editingInsertToolButton->setToolTip("Insert a macro command"); + m_editingInsertToolButton->setToolTip("Insert a new macro or macro command below the selected item"); QObject::connect(m_editingInsertToolButton, &QToolButton::clicked, this, &WuQMacroDialog::editingInsertToolButtonClicked); @@ -1101,25 +1102,6 @@ WuQMacroDialog::macroGroupToolButtonClicked() delete menu; } -/** - * @return Instance of macro group menu - */ -QMenu* -WuQMacroDialog::createMacroGroupMenu() -{ - QMenu* menu = new QMenu(this); - - QAction* importAction = menu->addAction("Import..."); - QObject::connect(importAction, &QAction::triggered, - this, &WuQMacroDialog::importMacroGroupActionTriggered); - - QAction* exportAction = menu->addAction("Export..."); - QObject::connect(exportAction, &QAction::triggered, - this, &WuQMacroDialog::exportMacroGroupActionTriggered); - - return menu; -} - /** * Called when editing move up button clicked */ @@ -1263,12 +1245,12 @@ WuQMacroDialog::editingInsertToolButtonClicked() QMenu menu(m_editingInsertToolButton); if (addMacroFlag) { - menu.addAction("Insert New Macro...", + menu.addAction("Insert New Macro Below...", this, &WuQMacroDialog::insertMenuNewMacroSelected); } if (addMacroCommandFlag) { - menu.addAction("Insert New Command...", + menu.addAction("Insert New Command Below...", this, &WuQMacroDialog::insertMenuNewMacroCommandSelected); } @@ -1414,6 +1396,7 @@ WuQMacroDialog::updateEditingToolButtons() deleteValid = true; moveUpValid = (commandIndex > 0); moveDownValid = (commandIndex < (macro->getNumberOfMacroCommands() - 1)); + runValid = true; } } break; diff --git a/src/GuiQt/WuQMacroDialog.h b/src/GuiQt/WuQMacroDialog.h index 3dc5dc771..a66d16079 100644 --- a/src/GuiQt/WuQMacroDialog.h +++ b/src/GuiQt/WuQMacroDialog.h @@ -156,8 +156,6 @@ namespace caret { QWidget* createHorizontalLine() const; - QMenu* createMacroGroupMenu(); - QWidget* createMacroRunAndEditingToolButtons(); QPixmap createEditingToolButtonPixmap(const QWidget* widget, diff --git a/src/GuiQt/WuQMacroExecutor.cxx b/src/GuiQt/WuQMacroExecutor.cxx index ef5e52f5a..258963c91 100644 --- a/src/GuiQt/WuQMacroExecutor.cxx +++ b/src/GuiQt/WuQMacroExecutor.cxx @@ -409,6 +409,9 @@ WuQMacroExecutor::runMacroPrivate(const WuQMacro* macro, return false; } + emit macroCommandHasCompleted(window, + mc); + if (mc->getCommandType() != WuQMacroCommandTypeEnum::MOUSE) { if (mc->getDelayInSeconds() > 0.0) { SystemUtilities::sleepSeconds(mc->getDelayInSeconds()); diff --git a/src/GuiQt/WuQMacroExecutor.h b/src/GuiQt/WuQMacroExecutor.h index df8276bb2..1f92774aa 100644 --- a/src/GuiQt/WuQMacroExecutor.h +++ b/src/GuiQt/WuQMacroExecutor.h @@ -60,6 +60,10 @@ namespace caret { // ADD_NEW_METHODS_HERE + signals: + void macroCommandHasCompleted(QWidget* window, + const WuQMacroCommand* command) const; + private: void moveMouseToTabBarTab(QTabBar* tabBar, const int32_t tabIndex) const; diff --git a/src/GuiQt/WuQMacroExecutorOptions.h b/src/GuiQt/WuQMacroExecutorOptions.h index b6afb5297..18d105759 100644 --- a/src/GuiQt/WuQMacroExecutorOptions.h +++ b/src/GuiQt/WuQMacroExecutorOptions.h @@ -65,7 +65,7 @@ namespace caret { private: void copyHelperWuQMacroExecutorOptions(const WuQMacroExecutorOptions& obj); - bool m_showMouseMovementFlag = true; + bool m_showMouseMovementFlag = false; bool m_stopOnErrorFlag = true; diff --git a/src/GuiQt/WuQMacroHelperInterface.h b/src/GuiQt/WuQMacroHelperInterface.h index c97f93f48..80a76661a 100644 --- a/src/GuiQt/WuQMacroHelperInterface.h +++ b/src/GuiQt/WuQMacroHelperInterface.h @@ -35,6 +35,7 @@ class QWidget; namespace caret { class WuQMacro; + class WuQMacroCommand; class WuQMacroExecutorOptions; class WuQMacroGroup; @@ -114,6 +115,17 @@ namespace caret { QWidget* window, const WuQMacroExecutorOptions* executorOptions) = 0; + /** + * Called by macro executor just after a command has completed execution + * + * @param window + * Widget for parent + * @param command + * Command that has just finished + */ + virtual void macroCommandHasCompleted(QWidget* window, + const WuQMacroCommand* command) = 0; + // ADD_NEW_METHODS_HERE signals: diff --git a/src/GuiQt/WuQMacroManager.cxx b/src/GuiQt/WuQMacroManager.cxx index a16f7cf02..2f1aea38a 100644 --- a/src/GuiQt/WuQMacroManager.cxx +++ b/src/GuiQt/WuQMacroManager.cxx @@ -27,6 +27,7 @@ #include #include +#include #include #include #include @@ -34,7 +35,6 @@ #include "CaretAssert.h" #include "CaretLogger.h" -#include "CaretFileDialog.h" #include "DataFileException.h" #include "EventManager.h" #include "EventUserInterfaceUpdate.h" @@ -527,6 +527,8 @@ WuQMacroManager::runMacro(QWidget* widget, QString errorMessage; m_macroExecutor = new WuQMacroExecutor(); + QObject::connect(m_macroExecutor, &WuQMacroExecutor::macroCommandHasCompleted, + this, &WuQMacroManager::macroCommandCompletedExecution); if (m_macroHelper != NULL) { m_macroHelper->macroExecutionStarting(macro, @@ -574,6 +576,25 @@ WuQMacroManager::stopMacro() } } +/** + * Called by macro executor when a macro command has completed + * + * @param window + * Window in which command is running + * @param command + * Command that just finished execution + */ +void +WuQMacroManager::macroCommandCompletedExecution(QWidget* window, + const WuQMacroCommand* command) +{ + CaretAssert(command); + if (m_macroHelper != NULL) { + m_macroHelper->macroCommandHasCompleted(window, + command); + } +} + /** * Add a parent object that will be searched during macro * execution to find objects by name that are contained @@ -691,16 +712,20 @@ WuQMacroManager::importMacros(QWidget* parent, WuQMacroGroup* appendToMacroGroup) { QString fileFilterString(WuQMacroFile::getFileDialogFilter()); - const QString filename = CaretFileDialog::getOpenFileNameDialog(parent, - "Import Macros", - "", - fileFilterString, - &fileFilterString); + const QString filename = QFileDialog::getOpenFileName(parent, + "Import Macros", + s_importExportMacroFileDirectory, + fileFilterString, + &fileFilterString, + QFileDialog::DontUseNativeDialog); if ( ! filename.isEmpty()) { WuQMacroFile macroFile; try { macroFile.readFile(filename); + QFileInfo fileInfo(filename); + s_importExportMacroFileDirectory = fileInfo.absolutePath(); + const WuQMacroGroup* fileMacroGroup = macroFile.getMacroGroup(); if (fileMacroGroup->getNumberOfMacros() > 0) { appendToMacroGroup->appendMacroGroup(fileMacroGroup); @@ -740,11 +765,12 @@ WuQMacroManager::exportMacros(QWidget* parent, WuQMacro* macro) { QString fileFilterString(WuQMacroFile::getFileDialogFilter()); - const QString filename = CaretFileDialog::getSaveFileNameDialog(parent, - "Export Macros", - "", - fileFilterString, - &fileFilterString); + const QString filename = QFileDialog::getSaveFileName(parent, + "Export Macros", + s_importExportMacroFileDirectory, + fileFilterString, + &fileFilterString, + QFileDialog::DontUseNativeDialog); if ( ! filename.isEmpty()) { try { WuQMacroFile macroFile; @@ -759,7 +785,16 @@ WuQMacroManager::exportMacros(QWidget* parent, throw DataFileException("No macro group or macro for export"); } - macroFile.writeFile(filename); + QString filenameToWrite(filename); + if ( ! filenameToWrite.endsWith(WuQMacroFile::getFileExtension())) { + filenameToWrite.append(WuQMacroFile::getFileExtension()); + } + + macroFile.writeFile(filenameToWrite); + + QFileInfo fileInfo(filenameToWrite); + s_importExportMacroFileDirectory = fileInfo.absolutePath(); + return true; } catch (const DataFileException& dfe) { @@ -850,11 +885,6 @@ WuQMacroManager::getWidgetSignalWatcherWithName(const QString& name) if (watcher != m_signalWatchers.end()) { return watcher->second; } -// for (auto iter : m_signalWatchers) { -// if (iter.first == name) { -// return iter.second; -// } -// } return NULL; } diff --git a/src/GuiQt/WuQMacroManager.h b/src/GuiQt/WuQMacroManager.h index 0fbbd8eb2..301d96b6e 100644 --- a/src/GuiQt/WuQMacroManager.h +++ b/src/GuiQt/WuQMacroManager.h @@ -168,6 +168,9 @@ namespace caret { public slots: void updateNonModalDialogs(); + void macroCommandCompletedExecution(QWidget* window, + const WuQMacroCommand* command); + private: WuQMacroManager(const QString& name, QObject* parent = NULL); @@ -196,12 +199,15 @@ namespace caret { QMutex m_macroExecutorMutex; + static QString s_importExportMacroFileDirectory; + // ADD_NEW_MEMBERS_HERE }; #ifdef __WU_Q_MACRO_MANAGER_DECLARE__ WuQMacroManager* WuQMacroManager::s_singletonMacroManager = NULL; + QString WuQMacroManager::s_importExportMacroFileDirectory; #endif // __WU_Q_MACRO_MANAGER_DECLARE__ } // namespace diff --git a/src/GuiQt/WuQMacroNewCommandSelectionDialog.cxx b/src/GuiQt/WuQMacroNewCommandSelectionDialog.cxx index f4fd1d55c..731ddda0e 100644 --- a/src/GuiQt/WuQMacroNewCommandSelectionDialog.cxx +++ b/src/GuiQt/WuQMacroNewCommandSelectionDialog.cxx @@ -33,6 +33,7 @@ #include #include #include +#include #include #include @@ -65,7 +66,7 @@ WuQMacroNewCommandSelectionDialog::WuQMacroNewCommandSelectionDialog(QWidget* pa { setWindowTitle("Add Macro Command"); - QLabel* commandTypeLabel = new QLabel("Type:"); + QLabel* commandTypeLabel = new QLabel("Command Type:"); QLabel* commandsLabel = new QLabel("Commands:"); QLabel* descriptionLabel = new QLabel("Description:"); @@ -78,6 +79,12 @@ WuQMacroNewCommandSelectionDialog::WuQMacroNewCommandSelectionDialog(QWidget* pa QObject::connect(m_commandTypeComboBox, &EnumComboBoxTemplate::itemActivated, this, &WuQMacroNewCommandSelectionDialog::commandTypeComboBoxActivated); + QWidget* typeWidget = new QWidget(); + QHBoxLayout* typeLayout = new QHBoxLayout(typeWidget); + typeLayout->setContentsMargins(0, 0, 0, 0); + typeLayout->addWidget(commandTypeLabel, 0); + typeLayout->addWidget(m_commandTypeComboBox->getWidget(), 100); + m_customCommandListWidget = new QListWidget(); QObject::connect(m_customCommandListWidget, &QListWidget::itemClicked, this, &WuQMacroNewCommandSelectionDialog::customCommandListWidgetItemClicked); @@ -90,22 +97,27 @@ WuQMacroNewCommandSelectionDialog::WuQMacroNewCommandSelectionDialog(QWidget* pa m_stackedWidget->addWidget(m_customCommandListWidget); m_stackedWidget->addWidget(m_widgetCommandListWidget); + QWidget* commandsWidget = new QWidget(); + QHBoxLayout* commandsLayout = new QHBoxLayout(commandsWidget); + commandsLayout->setContentsMargins(0, 0, 0, 0); + commandsLayout->addWidget(commandsLabel, 0); + commandsLayout->addWidget(m_stackedWidget, 100); + m_macroDescriptionTextEdit = new QPlainTextEdit(); - m_macroDescriptionTextEdit->setFixedHeight(100); - QGridLayout* gridLayout = new QGridLayout(); - gridLayout->setColumnStretch(0, 0); - gridLayout->setColumnStretch(1, 100); - int row = 0; - gridLayout->addWidget(commandTypeLabel, row, 0); - gridLayout->addWidget(m_commandTypeComboBox->getWidget(), row, 1, 1, 2); - row++; - gridLayout->addWidget(commandsLabel, row, 0); - gridLayout->addWidget(m_stackedWidget, row, 1, 1, 2); - row++; - gridLayout->addWidget(descriptionLabel, row, 0); - gridLayout->addWidget(m_macroDescriptionTextEdit, row, 1, 1, 2); - row++; + QWidget* descriptionWidget = new QWidget(); + QHBoxLayout* descriptionLayout = new QHBoxLayout(descriptionWidget); + descriptionLayout->setContentsMargins(0, 0, 0, 0); + descriptionLayout->addWidget(descriptionLabel, 0); + descriptionLayout->addWidget(m_macroDescriptionTextEdit, 100); + + m_splitter = new QSplitter(); + m_splitter->setOrientation(Qt::Vertical); + m_splitter->addWidget(commandsWidget); + m_splitter->addWidget(descriptionWidget); + m_splitter->setStretchFactor(0, 75); + m_splitter->setStretchFactor(1, 25); + m_splitter->setChildrenCollapsible(false); m_dialogButtonBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Apply @@ -114,7 +126,8 @@ WuQMacroNewCommandSelectionDialog::WuQMacroNewCommandSelectionDialog(QWidget* pa this, &WuQMacroNewCommandSelectionDialog::otherButtonClicked); QVBoxLayout* dialogLayout = new QVBoxLayout(this); - dialogLayout->addLayout(gridLayout); + dialogLayout->addWidget(typeWidget); + dialogLayout->addWidget(m_splitter); dialogLayout->addWidget(m_dialogButtonBox); commandTypeComboBoxActivated(); @@ -122,6 +135,9 @@ WuQMacroNewCommandSelectionDialog::WuQMacroNewCommandSelectionDialog(QWidget* pa if ( ! s_previousDialogGeometry.isEmpty()) { restoreGeometry(s_previousDialogGeometry); } + if ( ! s_previousSplitterState.isEmpty()) { + m_splitter->restoreState(s_previousSplitterState); + } } /** @@ -130,6 +146,7 @@ WuQMacroNewCommandSelectionDialog::WuQMacroNewCommandSelectionDialog(QWidget* pa WuQMacroNewCommandSelectionDialog::~WuQMacroNewCommandSelectionDialog() { s_previousDialogGeometry = saveGeometry(); + s_previousSplitterState = m_splitter->saveState(); for (auto cc : m_customCommands) { delete cc; diff --git a/src/GuiQt/WuQMacroNewCommandSelectionDialog.h b/src/GuiQt/WuQMacroNewCommandSelectionDialog.h index 08a218e7e..e92b96ecd 100644 --- a/src/GuiQt/WuQMacroNewCommandSelectionDialog.h +++ b/src/GuiQt/WuQMacroNewCommandSelectionDialog.h @@ -36,6 +36,7 @@ class QLineEdit; class QListWidget; class QListWidgetItem; class QPlainTextEdit; +class QSplitter; class QStackedWidget; namespace caret { @@ -89,6 +90,8 @@ namespace caret { QStackedWidget* m_stackedWidget; + QSplitter* m_splitter; + QListWidget* m_customCommandListWidget; QListWidget* m_widgetCommandListWidget; @@ -108,11 +111,14 @@ namespace caret { static WuQMacroCommandTypeEnum::Enum s_lastCommandTypeSelected; static QByteArray s_previousDialogGeometry; + + static QByteArray s_previousSplitterState; }; #ifdef __WU_Q_MACRO_NEW_COMMAND_SELECTION_DIALOG_DECLARE__ WuQMacroCommandTypeEnum::Enum WuQMacroNewCommandSelectionDialog::s_lastCommandTypeSelected = WuQMacroCommandTypeEnum::CUSTOM_OPERATION; QByteArray WuQMacroNewCommandSelectionDialog::s_previousDialogGeometry; + QByteArray WuQMacroNewCommandSelectionDialog::s_previousSplitterState; #endif // __WU_Q_MACRO_NEW_COMMAND_SELECTION_DIALOG_DECLARE__ } // namespace -- GitLab From 8fd7e676cf28e0579bbe167e57cc1b56546703fb Mon Sep 17 00:00:00 2001 From: John Harwell Date: Wed, 20 Feb 2019 10:32:21 -0600 Subject: [PATCH 196/427] WB-834: Revised ALL view surface viewing tool buttons used for surface selection. Macros no longer watch the tool buttons since they pop-up a menu for selecting surfaces. The menus for these buttons are now always valid and the macros are attached to the menus to record surface selections. --- src/GuiQt/BrainBrowserWindowToolBar.cxx | 277 ++++++++++++++---------- src/GuiQt/BrainBrowserWindowToolBar.h | 12 +- 2 files changed, 178 insertions(+), 111 deletions(-) diff --git a/src/GuiQt/BrainBrowserWindowToolBar.cxx b/src/GuiQt/BrainBrowserWindowToolBar.cxx index db7cf3fde..e631e9097 100644 --- a/src/GuiQt/BrainBrowserWindowToolBar.cxx +++ b/src/GuiQt/BrainBrowserWindowToolBar.cxx @@ -2551,9 +2551,21 @@ BrainBrowserWindowToolBar::createWholeBrainSurfaceOptionsWidget() SLOT(wholeBrainSurfaceLeftToolButtonTriggered(bool))); WuQtUtilities::setToolButtonStyleForQt5Mac(wholeBrainLeftSurfaceToolButton); wholeBrainLeftSurfaceToolButton->setDefaultAction(leftSurfaceAction); - leftSurfaceAction->setObjectName(objectNamePrefix - + "SelectLeft"); - macroManager->addMacroSupportToObject(leftSurfaceAction, +// leftSurfaceAction->setObjectName(objectNamePrefix +// + "SelectLeft"); +// macroManager->addMacroSupportToObject(leftSurfaceAction, +// "Select all view left surface"); + + /* + * Left menu is displayed when tool button is clicked + */ + this->wholeBrainSurfaceLeftMenu = new QMenu(wholeBrainLeftSurfaceToolButton); + QObject::connect(this->wholeBrainSurfaceLeftMenu, &QMenu::triggered, + this, &BrainBrowserWindowToolBar::wholeBrainSurfaceLeftMenuTriggered); + this->wholeBrainSurfaceLeftMenu->setObjectName(objectNamePrefix + + "SelectLeftSurfaceMenu"); + this->wholeBrainSurfaceLeftMenu->setToolTip("Select all view left surface"); + macroManager->addMacroSupportToObject(this->wholeBrainSurfaceLeftMenu, "Select all view left surface"); /* @@ -2577,9 +2589,21 @@ BrainBrowserWindowToolBar::createWholeBrainSurfaceOptionsWidget() SLOT(wholeBrainSurfaceRightToolButtonTriggered(bool))); WuQtUtilities::setToolButtonStyleForQt5Mac(wholeBrainRightSurfaceToolButton); wholeBrainRightSurfaceToolButton->setDefaultAction(rightSurfaceAction); - rightSurfaceAction->setObjectName(objectNamePrefix - + "SelectRight"); - macroManager->addMacroSupportToObject(rightSurfaceAction, +// rightSurfaceAction->setObjectName(objectNamePrefix +// + "SelectRight"); +// macroManager->addMacroSupportToObject(rightSurfaceAction, +// "Select all view right surface"); + + /* + * Right menu is displayed when tool button is clicked + */ + this->wholeBrainSurfaceRightMenu = new QMenu(wholeBrainRightSurfaceToolButton); + QObject::connect(this->wholeBrainSurfaceRightMenu, &QMenu::triggered, + this, &BrainBrowserWindowToolBar::wholeBrainSurfaceRightMenuTriggered); + this->wholeBrainSurfaceRightMenu->setObjectName(objectNamePrefix + + "SelectRightSurfaceMenu"); + this->wholeBrainSurfaceRightMenu->setToolTip("Select all view right surface"); + macroManager->addMacroSupportToObject(this->wholeBrainSurfaceRightMenu, "Select all view right surface"); /* @@ -2603,11 +2627,23 @@ BrainBrowserWindowToolBar::createWholeBrainSurfaceOptionsWidget() SLOT(wholeBrainSurfaceCerebellumToolButtonTriggered(bool))); WuQtUtilities::setToolButtonStyleForQt5Mac(wholeBrainCerebellumSurfaceToolButton); wholeBrainCerebellumSurfaceToolButton->setDefaultAction(cerebellumSurfaceAction); - cerebellumSurfaceAction->setObjectName(objectNamePrefix - + "SurfaceCerebellum"); - macroManager->addMacroSupportToObject(cerebellumSurfaceAction, - "Select all view cerebellum surface"); +// cerebellumSurfaceAction->setObjectName(objectNamePrefix +// + "SurfaceCerebellum"); +// macroManager->addMacroSupportToObject(cerebellumSurfaceAction, +// "Select all view cerebellum surface"); + /* + * Cerebellum menu is displayed when tool button is clicked + */ + this->wholeBrainSurfaceCerebellumMenu = new QMenu(wholeBrainCerebellumSurfaceToolButton); + QObject::connect(this->wholeBrainSurfaceCerebellumMenu, &QMenu::triggered, + this, &BrainBrowserWindowToolBar::wholeBrainSurfaceCerebellumMenuTriggered); + this->wholeBrainSurfaceCerebellumMenu->setObjectName(objectNamePrefix + + "SelectCerebellumSurfaceMenu"); + this->wholeBrainSurfaceCerebellumMenu->setToolTip("Select all view cerebellum surface"); + macroManager->addMacroSupportToObject(this->wholeBrainSurfaceCerebellumMenu, + "Select all view cerebellum surface"); + /* * Left/Right separation */ @@ -2750,6 +2786,8 @@ BrainBrowserWindowToolBar::updateWholeBrainSurfaceOptionsWidget(BrowserTabConten this->wholeBrainSurfaceRightCheckBox->setChecked(browserTabContent->isWholeBrainRightEnabled()); this->wholeBrainSurfaceCerebellumCheckBox->setChecked(browserTabContent->isWholeBrainCerebellumEnabled()); + updateAllWholeBrainSurfaceMenus(); + this->wholeBrainSurfaceSeparationLeftRightSpinBox->setValue(browserTabContent->getWholeBrainLeftRightSeparation()); this->wholeBrainSurfaceSeparationCerebellumSpinBox->setValue(browserTabContent->getWholeBrainCerebellumSeparation()); @@ -4025,6 +4063,7 @@ BrainBrowserWindowToolBar::wholeBrainSurfaceTypeComboBoxIndexChanged(int /*indx* if (isValid) { wholeBrainModel->setSelectedSurfaceType(tabIndex, surfaceType); this->updateVolumeIndicesWidget(btc); /* slices may get deselected */ + this->updateAllWholeBrainSurfaceMenus(); this->updateGraphicsWindowAndYokedWindows(); } } @@ -4048,11 +4087,34 @@ BrainBrowserWindowToolBar::wholeBrainSurfaceLeftCheckBoxStateChanged(int /*state } /** - * Called when the left surface tool button is pressed. + * Update all whole brain surface selection menus */ -void -BrainBrowserWindowToolBar::wholeBrainSurfaceLeftToolButtonTriggered(bool /*checked*/) +void +BrainBrowserWindowToolBar::updateAllWholeBrainSurfaceMenus() { + updateWholeBrainSurfaceMenu(this->wholeBrainSurfaceLeftMenu, + StructureEnum::CORTEX_LEFT); + updateWholeBrainSurfaceMenu(this->wholeBrainSurfaceRightMenu, + StructureEnum::CORTEX_RIGHT); + updateWholeBrainSurfaceMenu(this->wholeBrainSurfaceCerebellumMenu, + StructureEnum::CEREBELLUM); +} + +/** + * Update the menu to contain surface for the given structure + * + * @param menu + * Menu that is updated + * @param structure + * Structure for surfaces + */ +void +BrainBrowserWindowToolBar::updateWholeBrainSurfaceMenu(QMenu* menu, + const StructureEnum::Enum structure) +{ + CaretAssert(menu); + menu->clear(); + BrowserTabContent* btc = this->getTabContentFromSelectedTab(); ModelWholeBrain* wholeBrainModel = btc->getDisplayedWholeBrainModel(); if (wholeBrainModel == NULL) { @@ -4061,7 +4123,7 @@ BrainBrowserWindowToolBar::wholeBrainSurfaceLeftToolButtonTriggered(bool /*check const int32_t tabIndex = btc->getTabNumber(); Brain* brain = GuiManager::get()->getBrain(); - BrainStructure* brainStructure = brain->getBrainStructure(StructureEnum::CORTEX_LEFT, false); + BrainStructure* brainStructure = brain->getBrainStructure(structure, false); if (brainStructure != NULL) { std::vector surfaces; brainStructure->getSurfacesOfType(wholeBrainModel->getSelectedSurfaceType(tabIndex), @@ -4069,143 +4131,138 @@ BrainBrowserWindowToolBar::wholeBrainSurfaceLeftToolButtonTriggered(bool /*check const int32_t numSurfaces = static_cast(surfaces.size()); if (numSurfaces > 0) { - Surface* selectedSurface = wholeBrainModel->getSelectedSurface(StructureEnum::CORTEX_LEFT, - tabIndex); - QMenu menu; - QActionGroup* actionGroup = new QActionGroup(&menu); - actionGroup->setExclusive(true); + Surface* selectedSurface = wholeBrainModel->getSelectedSurface(structure, + tabIndex); for (int32_t i = 0; i < numSurfaces; i++) { QString name = surfaces[i]->getFileNameNoPath(); - QAction* action = actionGroup->addAction(name); + QAction* action = new QAction(name); action->setCheckable(true); if (surfaces[i] == selectedSurface) { action->setChecked(true); } - menu.addAction(action); - } - QAction* result = menu.exec(QCursor::pos()); - if (result != NULL) { - QList actionList = actionGroup->actions(); - for (int32_t i = 0; i < numSurfaces; i++) { - if (result == actionList.at(i)) { - wholeBrainModel->setSelectedSurface(StructureEnum::CORTEX_LEFT, - tabIndex, - surfaces[i]); - this->updateGraphicsWindowAndYokedWindows(); - break; - } - } + action->setData(qVariantFromValue((void*)surfaces[i])); + menu->addAction(action); } } } } +/** + * Called when the left surface tool button is pressed. + */ +void +BrainBrowserWindowToolBar::wholeBrainSurfaceLeftToolButtonTriggered(bool /*checked*/) +{ + updateAllWholeBrainSurfaceMenus(); + if ( ! this->wholeBrainSurfaceLeftMenu->isEmpty()) { + this->wholeBrainSurfaceLeftMenu->exec(QCursor::pos()); + } +} + +/** + * Called when left surface is selected from menu + * + * @param action + * Action that was selected + */ +void +BrainBrowserWindowToolBar::wholeBrainSurfaceLeftMenuTriggered(QAction* action) +{ + BrowserTabContent* btc = this->getTabContentFromSelectedTab(); + ModelWholeBrain* wholeBrainModel = btc->getDisplayedWholeBrainModel(); + if (wholeBrainModel == NULL) { + return; + } + + if (action != NULL) { + QVariant data = action->data(); + void* p = data.value(); + Surface* surface = (Surface*)p; + wholeBrainModel->setSelectedSurface(StructureEnum::CORTEX_LEFT, + btc->getTabNumber(), + surface); + this->updateGraphicsWindowAndYokedWindows(); + } +} + /** * Called when the right surface tool button is pressed. */ void BrainBrowserWindowToolBar::wholeBrainSurfaceRightToolButtonTriggered(bool /*checked*/) +{ + updateAllWholeBrainSurfaceMenus(); + if ( ! this->wholeBrainSurfaceRightMenu->isEmpty()) { + this->wholeBrainSurfaceRightMenu->exec(QCursor::pos()); + } +} + +/** + * Called when right surface is selected from menu + * + * @param action + * Action that was selected + */ +void +BrainBrowserWindowToolBar::wholeBrainSurfaceRightMenuTriggered(QAction* action) { BrowserTabContent* btc = this->getTabContentFromSelectedTab(); ModelWholeBrain* wholeBrainModel = btc->getDisplayedWholeBrainModel(); if (wholeBrainModel == NULL) { return; } - const int32_t tabIndex = btc->getTabNumber(); - Brain* brain = GuiManager::get()->getBrain(); - BrainStructure* brainStructure = brain->getBrainStructure(StructureEnum::CORTEX_RIGHT, false); - if (brainStructure != NULL) { - std::vector surfaces; - brainStructure->getSurfacesOfType(wholeBrainModel->getSelectedSurfaceType(tabIndex), - surfaces); - - const int32_t numSurfaces = static_cast(surfaces.size()); - if (numSurfaces > 0) { - Surface* selectedSurface = wholeBrainModel->getSelectedSurface(StructureEnum::CORTEX_RIGHT, - tabIndex); - QMenu menu; - QActionGroup* actionGroup = new QActionGroup(&menu); - actionGroup->setExclusive(true); - for (int32_t i = 0; i < numSurfaces; i++) { - QString name = surfaces[i]->getFileNameNoPath(); - QAction* action = actionGroup->addAction(name); - action->setCheckable(true); - if (surfaces[i] == selectedSurface) { - action->setChecked(true); - } - menu.addAction(action); - } - QAction* result = menu.exec(QCursor::pos()); - if (result != NULL) { - QList actionList = actionGroup->actions(); - for (int32_t i = 0; i < numSurfaces; i++) { - if (result == actionList.at(i)) { - wholeBrainModel->setSelectedSurface(StructureEnum::CORTEX_RIGHT, - tabIndex, - surfaces[i]); - this->updateGraphicsWindowAndYokedWindows(); - break; - } - } - } - } + if (action != NULL) { + QVariant data = action->data(); + void* p = data.value(); + Surface* surface = (Surface*)p; + wholeBrainModel->setSelectedSurface(StructureEnum::CORTEX_RIGHT, + btc->getTabNumber(), + surface); + this->updateGraphicsWindowAndYokedWindows(); } } + /** * Called when the cerebellum surface tool button is pressed. */ void BrainBrowserWindowToolBar::wholeBrainSurfaceCerebellumToolButtonTriggered(bool /*checked*/) +{ + updateAllWholeBrainSurfaceMenus(); + if ( ! this->wholeBrainSurfaceCerebellumMenu->isEmpty()) { + this->wholeBrainSurfaceCerebellumMenu->exec(QCursor::pos()); + } +} + +/** + * Called when cerebellum surface is selected from menu + * + * @param action + * Action that was selected + */ +void +BrainBrowserWindowToolBar::wholeBrainSurfaceCerebellumMenuTriggered(QAction* action) { BrowserTabContent* btc = this->getTabContentFromSelectedTab(); ModelWholeBrain* wholeBrainModel = btc->getDisplayedWholeBrainModel(); if (wholeBrainModel == NULL) { return; } - const int32_t tabIndex = btc->getTabNumber(); - Brain* brain = GuiManager::get()->getBrain(); - BrainStructure* brainStructure = brain->getBrainStructure(StructureEnum::CEREBELLUM, false); - if (brainStructure != NULL) { - std::vector surfaces; - brainStructure->getSurfacesOfType(wholeBrainModel->getSelectedSurfaceType(tabIndex), - surfaces); - - const int32_t numSurfaces = static_cast(surfaces.size()); - if (numSurfaces > 0) { - Surface* selectedSurface = wholeBrainModel->getSelectedSurface(StructureEnum::CEREBELLUM, - tabIndex); - QMenu menu; - QActionGroup* actionGroup = new QActionGroup(&menu); - actionGroup->setExclusive(true); - for (int32_t i = 0; i < numSurfaces; i++) { - QString name = surfaces[i]->getFileNameNoPath(); - QAction* action = actionGroup->addAction(name); - action->setCheckable(true); - if (surfaces[i] == selectedSurface) { - action->setChecked(true); - } - menu.addAction(action); - } - QAction* result = menu.exec(QCursor::pos()); - if (result != NULL) { - QList actionList = actionGroup->actions(); - for (int32_t i = 0; i < numSurfaces; i++) { - if (result == actionList.at(i)) { - wholeBrainModel->setSelectedSurface(StructureEnum::CEREBELLUM, - tabIndex, - surfaces[i]); - this->updateGraphicsWindowAndYokedWindows(); - break; - } - } - } - } + if (action != NULL) { + QVariant data = action->data(); + void* p = data.value(); + Surface* surface = (Surface*)p; + wholeBrainModel->setSelectedSurface(StructureEnum::CEREBELLUM, + btc->getTabNumber(), + surface); + this->updateGraphicsWindowAndYokedWindows(); } } + /** * Called when whole brain surface right checkbox is toggled. */ diff --git a/src/GuiQt/BrainBrowserWindowToolBar.h b/src/GuiQt/BrainBrowserWindowToolBar.h index 8229aab31..423b9b6b9 100644 --- a/src/GuiQt/BrainBrowserWindowToolBar.h +++ b/src/GuiQt/BrainBrowserWindowToolBar.h @@ -350,8 +350,14 @@ namespace caret { QCheckBox* wholeBrainSurfaceLeftCheckBox; QCheckBox* wholeBrainSurfaceRightCheckBox; QCheckBox* wholeBrainSurfaceCerebellumCheckBox; + QMenu* wholeBrainSurfaceLeftMenu; + QMenu* wholeBrainSurfaceRightMenu; + QMenu* wholeBrainSurfaceCerebellumMenu; QDoubleSpinBox* wholeBrainSurfaceSeparationLeftRightSpinBox; QDoubleSpinBox* wholeBrainSurfaceSeparationCerebellumSpinBox; + void updateAllWholeBrainSurfaceMenus(); + void updateWholeBrainSurfaceMenu(QMenu* menu, + const StructureEnum::Enum structure); private slots: void wholeBrainSurfaceTypeComboBoxIndexChanged(int indx); @@ -363,7 +369,11 @@ namespace caret { void wholeBrainSurfaceLeftToolButtonTriggered(bool checked); void wholeBrainSurfaceRightToolButtonTriggered(bool checked); void wholeBrainSurfaceCerebellumToolButtonTriggered(bool checked); - + + void wholeBrainSurfaceLeftMenuTriggered(QAction*); + void wholeBrainSurfaceRightMenuTriggered(QAction*); + void wholeBrainSurfaceCerebellumMenuTriggered(QAction*); + private: StructureSurfaceSelectionControl* surfaceSurfaceSelectionControl; -- GitLab From 52bbc14e36b56c4f896ef05acf4cc12aee43beaf Mon Sep 17 00:00:00 2001 From: John Harwell Date: Wed, 20 Feb 2019 15:47:51 -0600 Subject: [PATCH 197/427] Add display properties for volume drawing that contains an overall volume opacity. While volume drawing has opacity for the individual layers, they have no effect on volume surface outline. This overall opacity is need for the "volume to surface crossfade" animation macro. At this time, when the overall volume opacity is being used (less than one), the opacities for the layers are ignored due to it causing "excess opacity" and there are also difference in oblique vs orthogonal due to the different ways that they are drawn. --- src/Brain/BrainOpenGLFixedPipeline.cxx | 34 +++ src/Brain/BrainOpenGLFixedPipeline.h | 2 + .../BrainOpenGLVolumeObliqueSliceDrawing.cxx | 10 +- src/Brain/BrainOpenGLVolumeSliceDrawing.cxx | 45 +++- src/Brain/DisplayPropertiesVolume.cxx | 31 +++ src/Brain/DisplayPropertiesVolume.h | 5 + src/GuiQt/BrainBrowserWindow.cxx | 17 +- src/GuiQt/BrainBrowserWindow.h | 1 + src/GuiQt/CMakeLists.txt | 3 + src/GuiQt/GuiManager.cxx | 42 ++++ src/GuiQt/GuiManager.h | 5 + src/GuiQt/VolumePropertiesEditorDialog.cxx | 194 ++++++++++++++++++ src/GuiQt/VolumePropertiesEditorDialog.h | 68 ++++++ 13 files changed, 444 insertions(+), 13 deletions(-) create mode 100644 src/GuiQt/VolumePropertiesEditorDialog.cxx create mode 100644 src/GuiQt/VolumePropertiesEditorDialog.h diff --git a/src/Brain/BrainOpenGLFixedPipeline.cxx b/src/Brain/BrainOpenGLFixedPipeline.cxx index 221941f83..43c58a921 100644 --- a/src/Brain/BrainOpenGLFixedPipeline.cxx +++ b/src/Brain/BrainOpenGLFixedPipeline.cxx @@ -3780,6 +3780,12 @@ BrainOpenGLFixedPipeline::drawVolumeModel(BrowserTabContent* browserTabContent, break; } + /* + * Allow blending of volume slices and volume surface outline + */ + glPushAttrib(GL_COLOR_BUFFER_BIT); + applyVolumePropertiesOpacity(); + if (useNewDrawingFlag) { BrainOpenGLVolumeSliceDrawing volumeSliceDrawing; volumeSliceDrawing.draw(this, @@ -3799,6 +3805,8 @@ BrainOpenGLFixedPipeline::drawVolumeModel(BrowserTabContent* browserTabContent, obliqueMaskType, viewport); } + + glPopAttrib(); } /** @@ -5292,6 +5300,12 @@ BrainOpenGLFixedPipeline::drawWholeBrainModel(BrowserTabContent* browserTabConte } if ( ! twoDimSliceDrawVolumeDrawInfo.empty()) { + /* + * Allow blending of volume slices and volume surface outline + */ + glPushAttrib(GL_COLOR_BUFFER_BIT); + applyVolumePropertiesOpacity(); + /* * Check for oblique slice drawing */ @@ -5324,6 +5338,8 @@ BrainOpenGLFixedPipeline::drawWholeBrainModel(BrowserTabContent* browserTabConte } break; } + + glPopAttrib(); } } } @@ -5417,6 +5433,24 @@ BrainOpenGLFixedPipeline::drawWholeBrainModel(BrowserTabContent* browserTabConte } } +/** + * Apply opacity from the volume properties + */ +void +BrainOpenGLFixedPipeline::applyVolumePropertiesOpacity() +{ + const DisplayPropertiesVolume* dpv = m_brain->getDisplayPropertiesVolume(); + const float opacity = dpv->getOpacity(); + const bool useBlendingFlag(opacity < 1.0f); + + if (useBlendingFlag) { + glEnable(GL_BLEND); + glBlendColor(opacity, opacity, opacity, opacity); + glBlendFunc(GL_CONSTANT_ALPHA, + GL_ONE_MINUS_CONSTANT_ALPHA); + } +} + /** * Draw a chart model. * diff --git a/src/Brain/BrainOpenGLFixedPipeline.h b/src/Brain/BrainOpenGLFixedPipeline.h index 668f97c9f..e3eb91fd9 100644 --- a/src/Brain/BrainOpenGLFixedPipeline.h +++ b/src/Brain/BrainOpenGLFixedPipeline.h @@ -510,6 +510,8 @@ namespace caret { const float height, const float rgb[3]); + void applyVolumePropertiesOpacity(); + /** Index of window */ int32_t m_windowIndex = -1; diff --git a/src/Brain/BrainOpenGLVolumeObliqueSliceDrawing.cxx b/src/Brain/BrainOpenGLVolumeObliqueSliceDrawing.cxx index 609036316..0c8e80991 100644 --- a/src/Brain/BrainOpenGLVolumeObliqueSliceDrawing.cxx +++ b/src/Brain/BrainOpenGLVolumeObliqueSliceDrawing.cxx @@ -42,6 +42,7 @@ #include "DeveloperFlagsEnum.h" #include "DisplayPropertiesFoci.h" #include "DisplayPropertiesLabels.h" +#include "DisplayPropertiesVolume.h" #include "ElapsedTimer.h" #include "FociFile.h" #include "Focus.h" @@ -3957,8 +3958,15 @@ BrainOpenGLVolumeObliqueSliceDrawing::ObliqueSlice::draw(BrainOpenGLFixedPipelin } } else { + /* + * Only allow layer blending when overall volume opacity is off (>= 1.0) + */ + //const DisplayPropertiesVolume* dpv = brain->getDisplayPropertiesVolume(); + const bool allowBlendingFlag(true); //dpv->getOpacity() >= 1.0f); + glPushAttrib(GL_COLOR_BUFFER_BIT); - if (drawWithBlendingFlag) { + if (drawWithBlendingFlag + && allowBlendingFlag) { glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); } diff --git a/src/Brain/BrainOpenGLVolumeSliceDrawing.cxx b/src/Brain/BrainOpenGLVolumeSliceDrawing.cxx index dc3b3fe8d..2efd1ae93 100644 --- a/src/Brain/BrainOpenGLVolumeSliceDrawing.cxx +++ b/src/Brain/BrainOpenGLVolumeSliceDrawing.cxx @@ -43,6 +43,7 @@ #include "DeveloperFlagsEnum.h" #include "DisplayPropertiesFoci.h" #include "DisplayPropertiesLabels.h" +#include "DisplayPropertiesVolume.h" #include "ElapsedTimer.h" #include "FociFile.h" #include "Focus.h" @@ -1511,9 +1512,16 @@ BrainOpenGLVolumeSliceDrawing::drawOrthogonalSlice(const VolumeSliceViewPlaneEnu /* * Enable alpha blending so voxels that are not drawn from higher layers * allow voxels from lower layers to be seen. + * + * Only allow layer blending when overall volume opacity is off (>= 1.0) */ - glEnable(GL_BLEND); - glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + const DisplayPropertiesVolume* dpv = m_brain->getDisplayPropertiesVolume(); + const bool allowBlendingFlag(dpv->getOpacity() >= 1.0f); + glPushAttrib(GL_COLOR_BUFFER_BIT); + if (allowBlendingFlag) { + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + } /* * Flat shading voxels not interpolated @@ -1791,8 +1799,8 @@ BrainOpenGLVolumeSliceDrawing::drawOrthogonalSlice(const VolumeSliceViewPlaneEnu showBrainordinateHighlightRegionOfInterest(sliceViewingPlane, sliceCoordinates, sliceNormalVector); - - glDisable(GL_BLEND); + glPopAttrib(); + //glDisable(GL_BLEND); glShadeModel(GL_SMOOTH); } @@ -2069,10 +2077,17 @@ BrainOpenGLVolumeSliceDrawing::drawOrthogonalSliceWithCulling(const VolumeSliceV /* * Enable alpha blending so voxels that are not drawn from higher layers * allow voxels from lower layers to be seen. + * + * Only allow layer blending when overall volume opacity is off (>= 1.0) */ - glEnable(GL_BLEND); - glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - + const DisplayPropertiesVolume* dpv = m_brain->getDisplayPropertiesVolume(); + const bool allowBlendingFlag(dpv->getOpacity() >= 1.0f); + glPushAttrib(GL_COLOR_BUFFER_BIT); + if (allowBlendingFlag) { + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + } + /* * Flat shading voxels not interpolated */ @@ -2436,7 +2451,7 @@ BrainOpenGLVolumeSliceDrawing::drawOrthogonalSliceWithCulling(const VolumeSliceV sliceCoordinates, sliceNormalVector); - glDisable(GL_BLEND); + glPopAttrib(); glShadeModel(GL_SMOOTH); } @@ -5353,9 +5368,16 @@ BrainOpenGLVolumeSliceDrawing::drawOrthogonalSliceAllView(const VolumeSliceViewP /* * Enable alpha blending so voxels that are not drawn from higher layers * allow voxels from lower layers to be seen. + * + * Only allow layer blending when overall volume opacity is off (>= 1.0) */ - glEnable(GL_BLEND); - glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + const DisplayPropertiesVolume* dpv = m_brain->getDisplayPropertiesVolume(); + const bool allowBlendingFlag(dpv->getOpacity() >= 1.0f); + glPushAttrib(GL_COLOR_BUFFER_BIT); + if (allowBlendingFlag) { + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + } /* * Flat shading voxels not interpolated @@ -5683,7 +5705,8 @@ BrainOpenGLVolumeSliceDrawing::drawOrthogonalSliceAllView(const VolumeSliceViewP sliceCoordinates, sliceNormalVector); - glDisable(GL_BLEND); +// glDisable(GL_BLEND); + glPopAttrib(); glShadeModel(GL_SMOOTH); } diff --git a/src/Brain/DisplayPropertiesVolume.cxx b/src/Brain/DisplayPropertiesVolume.cxx index 77392ff7a..4a98a66c9 100644 --- a/src/Brain/DisplayPropertiesVolume.cxx +++ b/src/Brain/DisplayPropertiesVolume.cxx @@ -25,6 +25,7 @@ #include "SceneAttributes.h" #include "SceneClass.h" +#include "SceneClassAssistant.h" using namespace caret; @@ -42,6 +43,9 @@ using namespace caret; DisplayPropertiesVolume::DisplayPropertiesVolume() : DisplayProperties() { + m_opacity = 1.0f; + m_sceneAssistant->add("m_opacity", + &m_opacity); } /** @@ -58,6 +62,7 @@ DisplayPropertiesVolume::~DisplayPropertiesVolume() void DisplayPropertiesVolume::reset() { + m_opacity = 1.0f; } /** @@ -83,6 +88,27 @@ DisplayPropertiesVolume::copyDisplayProperties(const int32_t /*sourceTabIndex*/, { } +/** + * @return The overall surface opacity. + */ +float +DisplayPropertiesVolume::getOpacity() const +{ + return m_opacity; +} + +/** + * Set the overall surface opacity. + * + * @param opacity + * New value for opacity. + */ +void +DisplayPropertiesVolume::setOpacity(const float opacity) +{ + m_opacity = opacity; +} + /** * Create a scene for an instance of a class. * @@ -103,6 +129,8 @@ DisplayPropertiesVolume::saveToScene(const SceneAttributes* sceneAttributes, "DisplayPropertiesVolume", 1); + m_sceneAssistant->saveMembers(sceneAttributes, + sceneClass); switch (sceneAttributes->getSceneType()) { case SceneTypeEnum::SCENE_TYPE_FULL: break; @@ -133,6 +161,9 @@ DisplayPropertiesVolume::restoreFromScene(const SceneAttributes* sceneAttributes return; } + m_sceneAssistant->restoreMembers(sceneAttributes, + sceneClass); + switch (sceneAttributes->getSceneType()) { case SceneTypeEnum::SCENE_TYPE_FULL: break; diff --git a/src/Brain/DisplayPropertiesVolume.h b/src/Brain/DisplayPropertiesVolume.h index 1dae14a9b..fe3f213ec 100644 --- a/src/Brain/DisplayPropertiesVolume.h +++ b/src/Brain/DisplayPropertiesVolume.h @@ -38,6 +38,10 @@ namespace caret { void update(); + float getOpacity() const; + + void setOpacity(const float opacity); + virtual void copyDisplayProperties(const int32_t sourceTabIndex, const int32_t targetTabIndex); @@ -52,6 +56,7 @@ namespace caret { DisplayPropertiesVolume& operator=(const DisplayPropertiesVolume&); + float m_opacity = 1.0f; }; #ifdef __DISPLAY_PROPERTIES_VOLUME_DECLARE__ diff --git a/src/GuiQt/BrainBrowserWindow.cxx b/src/GuiQt/BrainBrowserWindow.cxx index 8ce54641c..f956b0ccf 100644 --- a/src/GuiQt/BrainBrowserWindow.cxx +++ b/src/GuiQt/BrainBrowserWindow.cxx @@ -1234,6 +1234,15 @@ BrainBrowserWindow::processShowSurfacePropertiesDialog() GuiManager::get()->processShowSurfacePropertiesEditorDialog(this); } +/** + * Show the volume properties editor dialog. + */ +void +BrainBrowserWindow::processShowVolumePropertiesDialog() +{ + GuiManager::get()->processShowVolumePropertiesEditorDialog(this); +} + /** * Create actions for this window. * NOTE: This is called AFTER the toolbar is created. @@ -2829,7 +2838,13 @@ BrainBrowserWindow::processSurfaceMenuInformation() QMenu* BrainBrowserWindow::createMenuVolume() { - return NULL; + QMenu* menu = new QMenu("Volume", this); + + menu->addAction("Properties...", + this, + SLOT(processShowVolumePropertiesDialog())); + + return menu; } /** diff --git a/src/GuiQt/BrainBrowserWindow.h b/src/GuiQt/BrainBrowserWindow.h index 707f27261..03688d4fe 100644 --- a/src/GuiQt/BrainBrowserWindow.h +++ b/src/GuiQt/BrainBrowserWindow.h @@ -226,6 +226,7 @@ namespace caret { void processReportWorkbenchBug(); void processShowSurfacePropertiesDialog(); + void processShowVolumePropertiesDialog(); void processDevelopGraphicsTiming(); diff --git a/src/GuiQt/CMakeLists.txt b/src/GuiQt/CMakeLists.txt index 40eb1e91b..4d837f27f 100644 --- a/src/GuiQt/CMakeLists.txt +++ b/src/GuiQt/CMakeLists.txt @@ -189,6 +189,7 @@ ELSE() UserInputTileTabsContextMenu.h UsernamePasswordWidget.h VolumeFileCreateDialog.h + VolumePropertiesEditorDialog.h VolumeSurfaceOutlineColorOrTabViewController.h VolumeSurfaceOutlineSetViewController.h VolumeSurfaceOutlineViewController.h @@ -447,6 +448,7 @@ UserInputTileTabsContextMenu.h UsernamePasswordWidget.h ViewModeEnum.h VolumeFileCreateDialog.h +VolumePropertiesEditorDialog.h VolumeSurfaceOutlineColorOrTabViewController.h VolumeSurfaceOutlineSetViewController.h VolumeSurfaceOutlineViewController.h @@ -704,6 +706,7 @@ UserInputTileTabsContextMenu.cxx UsernamePasswordWidget.cxx ViewModeEnum.cxx VolumeFileCreateDialog.cxx +VolumePropertiesEditorDialog.cxx VolumeSurfaceOutlineColorOrTabViewController.cxx VolumeSurfaceOutlineSetViewController.cxx VolumeSurfaceOutlineViewController.cxx diff --git a/src/GuiQt/GuiManager.cxx b/src/GuiQt/GuiManager.cxx index b6238d5dd..239b3a97b 100644 --- a/src/GuiQt/GuiManager.cxx +++ b/src/GuiQt/GuiManager.cxx @@ -122,6 +122,7 @@ #include "Surface.h" #include "TileTabsConfigurationDialog.h" #include "VolumeMappableInterface.h" +#include "VolumePropertiesEditorDialog.h" #include "WbMacroCustomOperationManager.h" #include "WbMacroHelper.h" #include "WuQMacroManager.h" @@ -187,6 +188,7 @@ GuiManager::initializeGuiManager() m_chartTwoLineSeriesHistoryDialog = NULL; this->sceneDialog = NULL; m_surfacePropertiesEditorDialog = NULL; + m_volumePropertiesEditorDialog = NULL; m_tileTabsConfigurationDialog = NULL; this->cursorManager = new CursorManager(); @@ -1744,6 +1746,30 @@ GuiManager::processShowSurfacePropertiesEditorDialog(BrainBrowserWindow* browser } } +/** + * Show the volume properties editor dialog. + * @param browserWindow + * Browser window on which dialog is displayed. + */ +void +GuiManager::processShowVolumePropertiesEditorDialog(BrainBrowserWindow* browserWindow) +{ + bool wasCreatedFlag = false; + + if (this->m_volumePropertiesEditorDialog == NULL) { + m_volumePropertiesEditorDialog = new VolumePropertiesEditorDialog(browserWindow); + this->addNonModalDialog(m_volumePropertiesEditorDialog); + m_volumePropertiesEditorDialog->setSaveWindowPositionForNextTime(true); + wasCreatedFlag = true; + } + m_volumePropertiesEditorDialog->showDialog(); + + if (wasCreatedFlag) { + WuQtUtilities::moveWindowToSideOfParent(browserWindow, + m_volumePropertiesEditorDialog); + } +} + /** * @return The action for showing/hiding the scene dialog. */ @@ -2554,6 +2580,10 @@ GuiManager::saveToScene(const SceneAttributes* sceneAttributes, sceneClass->addClass(m_surfacePropertiesEditorDialog->saveToScene(sceneAttributes, "m_surfacePropertiesEditorDialog")); } + if (m_volumePropertiesEditorDialog != NULL) { + sceneClass->addClass(m_volumePropertiesEditorDialog->saveToScene(sceneAttributes, + "m_volumePropertiesEditorDialog")); + } switch (sceneAttributes->getSceneType()) { case SceneTypeEnum::SCENE_TYPE_FULL: @@ -2787,6 +2817,18 @@ GuiManager::restoreFromScene(const SceneAttributes* sceneAttributes, surfPropClass); } + const SceneClass* volPropClass = sceneClass->getClass("m_volumePropertiesEditorDialog"); + if (volPropClass != NULL) { + if (m_volumePropertiesEditorDialog == NULL) { + processShowVolumePropertiesEditorDialog(firstBrowserWindow); + } + else if ( ! m_volumePropertiesEditorDialog->isVisible()) { + processShowVolumePropertiesEditorDialog(firstBrowserWindow); + } + m_volumePropertiesEditorDialog->restoreFromScene(sceneAttributes, + volPropClass); + } + CaretLogFine("Time to restore information/property windows was " + QString::number(timer.getElapsedTimeSeconds(), 'f', 3) + " seconds"); diff --git a/src/GuiQt/GuiManager.h b/src/GuiQt/GuiManager.h index ed8c14b11..c99d47a67 100644 --- a/src/GuiQt/GuiManager.h +++ b/src/GuiQt/GuiManager.h @@ -66,6 +66,7 @@ namespace caret { class SelectionManager; class SpecFile; class SurfacePropertiesEditorDialog; + class VolumePropertiesEditorDialog; class TileTabsConfigurationDialog; /** @@ -149,6 +150,8 @@ namespace caret { void processShowSurfacePropertiesEditorDialog(BrainBrowserWindow* browserWindow); + void processShowVolumePropertiesEditorDialog(BrainBrowserWindow* browserWindow); + void processShowSceneDialogAndScene(BrainBrowserWindow* browserWindow, SceneFile* sceneFile, Scene* scene); @@ -283,6 +286,8 @@ namespace caret { SurfacePropertiesEditorDialog* m_surfacePropertiesEditorDialog; + VolumePropertiesEditorDialog* m_volumePropertiesEditorDialog; + WuQWebView* connectomeDatabaseWebView; CursorManager* cursorManager; diff --git a/src/GuiQt/VolumePropertiesEditorDialog.cxx b/src/GuiQt/VolumePropertiesEditorDialog.cxx new file mode 100644 index 000000000..118fd0f89 --- /dev/null +++ b/src/GuiQt/VolumePropertiesEditorDialog.cxx @@ -0,0 +1,194 @@ + +/*LICENSE_START*/ +/* + * Copyright (C) 2014 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + +#define __VOLUME_PROPERTIES_EDITOR_DIALOG_DECLARE__ +#include "VolumePropertiesEditorDialog.h" +#undef __VOLUME_PROPERTIES_EDITOR_DIALOG_DECLARE__ + +#include + +#include +#include +#include + +using namespace caret; + +#include "Brain.h" +#include "CaretAssert.h" +#include "DisplayPropertiesVolume.h" +#include "GuiManager.h" +#include "EventGraphicsUpdateAllWindows.h" +#include "EventManager.h" +#include "EventUserInterfaceUpdate.h" +#include "SceneClass.h" +#include "SceneWindowGeometry.h" +#include "WuQFactory.h" +#include "WuQtUtilities.h" + +/** + * \class caret::VolumePropertiesEditorDialog + * \brief Dialog for adjusting volume display properties. + * \ingroup GuitQt + */ + +/** + * Constructor. + */ +VolumePropertiesEditorDialog::VolumePropertiesEditorDialog(QWidget* parent) +: WuQDialogNonModal("Volume Properties", + parent) +{ + QLabel* opacityLabel = new QLabel("Opacity: "); + m_opacitySpinBox = WuQFactory::newDoubleSpinBox(); + m_opacitySpinBox->setRange(0.0, 1.0); + m_opacitySpinBox->setSingleStep(0.1); + m_opacitySpinBox->setDecimals(2); + QObject::connect(m_opacitySpinBox, SIGNAL(valueChanged(double)), + this, SLOT(displayPropertyChanged())); + + QWidget* w = new QWidget(); + QGridLayout* gridLayout = new QGridLayout(w); + WuQtUtilities::setLayoutSpacingAndMargins(gridLayout, 2, 2); + int row = gridLayout->rowCount(); + gridLayout->addWidget(opacityLabel, row, 0); + gridLayout->addWidget(m_opacitySpinBox, row, 1); + row++; + + setCentralWidget(w, + WuQDialog::SCROLL_AREA_NEVER); + + updateDialog(); + + EventManager::get()->addEventListener(this, + EventTypeEnum::EVENT_USER_INTERFACE_UPDATE); + + /* + * No apply button + */ + setApplyButtonText(""); +} + +/** + * Destructor. + */ +VolumePropertiesEditorDialog::~VolumePropertiesEditorDialog() +{ + EventManager::get()->removeAllEventsFromListener(this); +} + +/** + * Called when a display property is changed. + */ +void +VolumePropertiesEditorDialog::displayPropertyChanged() +{ + DisplayPropertiesVolume* dps = GuiManager::get()->getBrain()->getDisplayPropertiesVolume(); + dps->setOpacity(m_opacitySpinBox->value()); + + EventManager::get()->sendEvent(EventGraphicsUpdateAllWindows().getPointer()); +} + +/** + * Update the properties editor. + */ +void +VolumePropertiesEditorDialog::updateDialog() +{ + const DisplayPropertiesVolume* dpv = GuiManager::get()->getBrain()->getDisplayPropertiesVolume(); + + QSignalBlocker blocker(m_opacitySpinBox); + m_opacitySpinBox->setValue(dpv->getOpacity()); +} + +/** + * Receive events from the event manager. + * + * @param event + * Event sent by event manager. + */ +void +VolumePropertiesEditorDialog::receiveEvent(Event* event) +{ + if (event->getEventType() == EventTypeEnum::EVENT_USER_INTERFACE_UPDATE) { + CaretAssert(dynamic_cast(event) != NULL); + + updateDialog(); + } +} + +/** + * Create a scene for an instance of a class. + * + * @param sceneAttributes + * Attributes for the scene. Scenes may be of different types + * (full, generic, etc) and the attributes should be checked when + * saving the scene. + * + * @return Pointer to SceneClass object representing the state of + * this object. Under some circumstances a NULL pointer may be + * returned. Caller will take ownership of returned object. + */ +SceneClass* +VolumePropertiesEditorDialog::saveToScene(const SceneAttributes* sceneAttributes, + const AString& instanceName) +{ + SceneClass* sceneClass = new SceneClass(instanceName, + "VolumePropertiesEditorDialog", + 1); + /* + * Position and size + */ + SceneWindowGeometry swg(this); + sceneClass->addClass(swg.saveToScene(sceneAttributes, + "geometry")); + + return sceneClass; +} + +/** + * Restore the state of an instance of a class. + * + * @param sceneAttributes + * Attributes for the scene. Scenes may be of different types + * (full, generic, etc) and the attributes should be checked when + * restoring the scene. + * + * @param sceneClass + * SceneClass containing the state that was previously + * saved and should be restored. + */ +void +VolumePropertiesEditorDialog::restoreFromScene(const SceneAttributes* sceneAttributes, + const SceneClass* sceneClass) +{ + if (sceneClass == NULL) { + return; + } + + /* + * Position and size + */ + SceneWindowGeometry swg(this); + swg.restoreFromScene(sceneAttributes, sceneClass->getClass("geometry")); +} + + + diff --git a/src/GuiQt/VolumePropertiesEditorDialog.h b/src/GuiQt/VolumePropertiesEditorDialog.h new file mode 100644 index 000000000..1686f12c7 --- /dev/null +++ b/src/GuiQt/VolumePropertiesEditorDialog.h @@ -0,0 +1,68 @@ +#ifndef __VOLUME_PROPERTIES_EDITOR_DIALOG__H_ +#define __VOLUME_PROPERTIES_EDITOR_DIALOG__H_ + +/*LICENSE_START*/ +/* + * Copyright (C) 2014 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + +#include "EventListenerInterface.h" +#include "SceneableInterface.h" +#include "WuQDialogNonModal.h" + +class QDoubleSpinBox; + +namespace caret { + class VolumePropertiesEditorDialog : public WuQDialogNonModal, public EventListenerInterface, public SceneableInterface { + Q_OBJECT + + public: + VolumePropertiesEditorDialog(QWidget* parent = 0); + + virtual ~VolumePropertiesEditorDialog(); + + void receiveEvent(Event* event); + + void updateDialog(); + + virtual SceneClass* saveToScene(const SceneAttributes* sceneAttributes, + const AString& instanceName); + + virtual void restoreFromScene(const SceneAttributes* sceneAttributes, + const SceneClass* sceneClass); + + private slots: + void displayPropertyChanged(); + + private: + VolumePropertiesEditorDialog(const VolumePropertiesEditorDialog&); + + VolumePropertiesEditorDialog& operator=(const VolumePropertiesEditorDialog&); + + QDoubleSpinBox* m_opacitySpinBox; + + // ADD_NEW_MEMBERS_HERE + + }; + +#ifdef __VOLUME_PROPERTIES_EDITOR_DIALOG_DECLARE__ + // +#endif // __VOLUME_PROPERTIES_EDITOR_DIALOG_DECLARE__ + +} // namespace +#endif //__VOLUME_PROPERTIES_EDITOR_DIALOG__H_ -- GitLab From 39c940bc50d070a5647e6ab1326e8d1d6d9ae2b2 Mon Sep 17 00:00:00 2001 From: John Harwell Date: Wed, 20 Feb 2019 15:58:37 -0600 Subject: [PATCH 198/427] WB-834 Macros: Fixes so that addition of new macro or command triggers modified status on scene file dialog. --- src/GuiQt/WuQMacroDialog.cxx | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/src/GuiQt/WuQMacroDialog.cxx b/src/GuiQt/WuQMacroDialog.cxx index d2355e893..f9fbc489a 100644 --- a/src/GuiQt/WuQMacroDialog.cxx +++ b/src/GuiQt/WuQMacroDialog.cxx @@ -1291,6 +1291,8 @@ WuQMacroDialog::insertMenuNewMacroSelected() m_treeView->setCurrentIndex(selectedIndex); updateDialogContents(); } + + WuQMacroManager::instance()->macroWasModified(macro); } } } @@ -1318,13 +1320,14 @@ WuQMacroDialog::addNewMacroCommand(WuQMacroCommand* command) return; } + WuQMacro* macro(NULL); switch (getSelectedItemType()) { case WuQMacroStandardItemTypeEnum::INVALID: CaretAssert(0); break; case WuQMacroStandardItemTypeEnum::MACRO: { - WuQMacro* macro = getSelectedMacro(); + macro = getSelectedMacro(); CaretAssert(macro); macro->insertMacroCommandAtIndex(0, command); @@ -1332,7 +1335,7 @@ WuQMacroDialog::addNewMacroCommand(WuQMacroCommand* command) break; case WuQMacroStandardItemTypeEnum::MACRO_COMMAND: { - WuQMacro* macro = getSelectedMacro(); + macro = getSelectedMacro(); CaretAssert(macro); const WuQMacroCommand* selectedCommand = getSelectedMacroCommand(); CaretAssert(selectedCommand); @@ -1348,6 +1351,10 @@ WuQMacroDialog::addNewMacroCommand(WuQMacroCommand* command) m_treeView->setCurrentIndex(selectedIndex); updateDialogContents(); + + if (macro != NULL) { + WuQMacroManager::instance()->macroWasModified(macro); + } } /** -- GitLab From 8e1b64f4bc9764431401fe1dff4f08f44c7f2349 Mon Sep 17 00:00:00 2001 From: John Harwell Date: Wed, 20 Feb 2019 15:59:35 -0600 Subject: [PATCH 199/427] WB-834 Macros: Update macro animation volume to surface crossfade to use new volume properties opacity so that volume surface outlines fade way. --- .../WbMacroCustomOperationVolumeToSurfaceCrossFade.cxx | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/GuiQt/WbMacroCustomOperationVolumeToSurfaceCrossFade.cxx b/src/GuiQt/WbMacroCustomOperationVolumeToSurfaceCrossFade.cxx index 53efdc2cf..1c7ade4ac 100644 --- a/src/GuiQt/WbMacroCustomOperationVolumeToSurfaceCrossFade.cxx +++ b/src/GuiQt/WbMacroCustomOperationVolumeToSurfaceCrossFade.cxx @@ -28,6 +28,7 @@ #include "BrowserTabContent.h" #include "CaretAssert.h" #include "DisplayPropertiesSurface.h" +#include "DisplayPropertiesVolume.h" #include "GuiManager.h" #include "ModelWholeBrain.h" #include "Overlay.h" @@ -195,6 +196,8 @@ WbMacroCustomOperationVolumeToSurfaceCrossFade::performCrossFade(Overlay* volume DisplayPropertiesSurface* surfaceProperties = GuiManager::get()->getBrain()->getDisplayPropertiesSurface(); CaretAssert(surfaceProperties); + DisplayPropertiesVolume* volumeProperties = GuiManager::get()->getBrain()->getDisplayPropertiesVolume(); + CaretAssert(volumeProperties); /* * Initialize the opacities @@ -202,7 +205,7 @@ WbMacroCustomOperationVolumeToSurfaceCrossFade::performCrossFade(Overlay* volume for (int iStep = 0; iStep < numberOfSteps; iStep++) { surfaceProperties->setOpacity(surfaceOpacity); - volumeOverlay->setOpacity(volumeOpacity); + volumeProperties->setOpacity(volumeOpacity); updateSurfaceColoring(); updateUserInterface(); updateGraphics(); @@ -220,7 +223,7 @@ WbMacroCustomOperationVolumeToSurfaceCrossFade::performCrossFade(Overlay* volume } surfaceProperties->setOpacity(1.0); - volumeOverlay->setOpacity(0.0); + volumeProperties->setOpacity(0.0); updateSurfaceColoring(); updateUserInterface(); -- GitLab From 5568cd622e5a82a82f87386e068840601005cbae Mon Sep 17 00:00:00 2001 From: John Harwell Date: Fri, 22 Feb 2019 15:50:16 -0600 Subject: [PATCH 200/427] Added a developer menu option: "Setup All View Surface Matching for Movie Recording", it (1) Scales surfaces in a structure to match the primary anatomical and there is special processing for spheres; (2) Uses the anatomical surface for viewport sizing in All View; (3) Disables offset of surfaces in All view when there are only surfaces from one structure to draw. --- src/Brain/BrainOpenGLFixedPipeline.cxx | 79 +++++++++++---- src/Brain/BrainStructure.cxx | 22 ++++ src/Brain/BrainStructure.h | 2 + src/Common/DeveloperFlagsEnum.cxx | 7 +- src/Common/DeveloperFlagsEnum.h | 3 +- src/Files/SurfaceFile.cxx | 134 +++++++++++++++++++++++-- src/Files/SurfaceFile.h | 4 + src/GuiQt/BrainBrowserWindow.cxx | 14 +++ 8 files changed, 232 insertions(+), 33 deletions(-) diff --git a/src/Brain/BrainOpenGLFixedPipeline.cxx b/src/Brain/BrainOpenGLFixedPipeline.cxx index 43c58a921..04c95abdf 100644 --- a/src/Brain/BrainOpenGLFixedPipeline.cxx +++ b/src/Brain/BrainOpenGLFixedPipeline.cxx @@ -5203,6 +5203,29 @@ BrainOpenGLFixedPipeline::drawWholeBrainModel(BrowserTabContent* browserTabConte tabNumberIndex); Surface* rightSurface = wholeBrainModel->getSelectedSurface(StructureEnum::CORTEX_RIGHT, tabNumberIndex); + + if (DeveloperFlagsEnum::isFlag(DeveloperFlagsEnum::DEVELOPER_FLAG_ALL_VIEW_SURFACE_MATCH_MODE)) { + /* 2/22/19 ALL SURFACES SAME VIEWPORT */ + /* + * Use the primary anatomical surface for sizing any surface in the same + * structure so that size of viewport is the same. Otherwise, the viewport + * is scaled uniquely for each structure + */ + BrainStructure* leftStructure = m_brain->getBrainStructure(StructureEnum::CORTEX_LEFT, false); + if (leftStructure != NULL) { + Surface* leftPrimaryAnat = leftStructure->getPrimaryAnatomicalSurface(); + if (leftPrimaryAnat != NULL) { + leftSurface = leftPrimaryAnat; + } + } + BrainStructure* rightStructure = m_brain->getBrainStructure(StructureEnum::CORTEX_RIGHT, false); + Surface* rightPrimaryAnat = rightStructure->getPrimaryAnatomicalSurface(); + if (rightPrimaryAnat != NULL) { + rightSurface = rightPrimaryAnat; + } + /* 2/22/19 ALL SURFACES SAME VIEWPORT */ + } + /* * Center using volume, if it is available * Otherwise, see if surface is available, but a surface is offset @@ -5350,6 +5373,7 @@ BrainOpenGLFixedPipeline::drawWholeBrainModel(BrowserTabContent* browserTabConte /* * Draw surfaces last so that opacity works. */ + std::set uniqueStructuresToDraw; std::vector surfacesToDraw; const int32_t numberOfBrainStructures = m_brain->getNumberOfBrainStructures(); for (int32_t i = 0; i < numberOfBrainStructures; i++) { @@ -5375,11 +5399,22 @@ BrainOpenGLFixedPipeline::drawWholeBrainModel(BrowserTabContent* browserTabConte } if (drawIt) { + uniqueStructuresToDraw.insert(structure); surfacesToDraw.push_back(surface); } } } + /* + * When only one surface structure is displayed, disable offset of surfaces + */ + bool allowLeftRightSeparationFlag(true); + if (DeveloperFlagsEnum::isFlag(DeveloperFlagsEnum::DEVELOPER_FLAG_ALL_VIEW_SURFACE_MATCH_MODE)) { + if (uniqueStructuresToDraw.size() == 1) { + allowLeftRightSeparationFlag = false; + } + } + const int32_t numSurfaceToDraw = static_cast(surfacesToDraw.size()); for (int32_t i = 0; i < numSurfaceToDraw; i++) { CaretAssertVectorIndex(surfacesToDraw, i); @@ -5389,27 +5424,29 @@ BrainOpenGLFixedPipeline::drawWholeBrainModel(BrowserTabContent* browserTabConte float dx = 0.0; float dy = 0.0; float dz = 0.0; - switch (surface->getStructure()) { - case StructureEnum::CORTEX_LEFT: - dx = -browserTabContent->getWholeBrainLeftRightSeparation(); - if ((surfaceType != SurfaceTypeEnum::ANATOMICAL) - && (surfaceType != SurfaceTypeEnum::RECONSTRUCTION)) { - dx -= surface->getBoundingBox()->getMaxX(); - } - break; - case StructureEnum::CORTEX_RIGHT: - dx = browserTabContent->getWholeBrainLeftRightSeparation(); - if ((surfaceType != SurfaceTypeEnum::ANATOMICAL) - && (surfaceType != SurfaceTypeEnum::RECONSTRUCTION)) { - dx -= surface->getBoundingBox()->getMinX(); - } - break; - case StructureEnum::CEREBELLUM: - dz = browserTabContent->getWholeBrainCerebellumSeparation(); - break; - default: - CaretLogWarning("programmer-issure: Surface type not left/right/cerebellum"); - break; + if (allowLeftRightSeparationFlag) { + switch (surface->getStructure()) { + case StructureEnum::CORTEX_LEFT: + dx = -browserTabContent->getWholeBrainLeftRightSeparation(); + if ((surfaceType != SurfaceTypeEnum::ANATOMICAL) + && (surfaceType != SurfaceTypeEnum::RECONSTRUCTION)) { + dx -= surface->getBoundingBox()->getMaxX(); + } + break; + case StructureEnum::CORTEX_RIGHT: + dx = browserTabContent->getWholeBrainLeftRightSeparation(); + if ((surfaceType != SurfaceTypeEnum::ANATOMICAL) + && (surfaceType != SurfaceTypeEnum::RECONSTRUCTION)) { + dx -= surface->getBoundingBox()->getMinX(); + } + break; + case StructureEnum::CEREBELLUM: + dz = browserTabContent->getWholeBrainCerebellumSeparation(); + break; + default: + CaretLogWarning("programmer-issure: Surface type not left/right/cerebellum"); + break; + } } if (surface != NULL) { diff --git a/src/Brain/BrainStructure.cxx b/src/Brain/BrainStructure.cxx index 59165150b..e05861a6c 100644 --- a/src/Brain/BrainStructure.cxx +++ b/src/Brain/BrainStructure.cxx @@ -1526,3 +1526,25 @@ BrainStructure::restoreFromScene(const SceneAttributes* sceneAttributes, } +/** + * Match surface sizes to the primary anatomical surface + */ +void +BrainStructure::matchSurfacesToPrimaryAnatomical() +{ + const Surface* primaryAnatomical = getPrimaryAnatomicalSurface(); + if (primaryAnatomical == NULL) { + return; + } + + for (auto m : m_surfaces) { + if (m != primaryAnatomical) { + if (m->getSurfaceType() == SurfaceTypeEnum::SPHERICAL) { + m->matchSphereToSurface(primaryAnatomical); + } + else { + m->matchSurfaceBoundingBox(primaryAnatomical); + } + } + } +} diff --git a/src/Brain/BrainStructure.h b/src/Brain/BrainStructure.h index 1a9f7f581..b8f3f938d 100644 --- a/src/Brain/BrainStructure.h +++ b/src/Brain/BrainStructure.h @@ -166,6 +166,8 @@ namespace caret { void initializeOverlays(); + void matchSurfacesToPrimaryAnatomical(); + private: const Surface* getPrimaryAnatomicalSurfacePrivate() const; diff --git a/src/Common/DeveloperFlagsEnum.cxx b/src/Common/DeveloperFlagsEnum.cxx index 2781a0225..b3a2eb490 100644 --- a/src/Common/DeveloperFlagsEnum.cxx +++ b/src/Common/DeveloperFlagsEnum.cxx @@ -129,7 +129,12 @@ DeveloperFlagsEnum::initialize() "Use New Scene File Reader and Writer", CheckableEnum::YES, true)); - + checkableItems.push_back(DeveloperFlagsEnum(DEVELOPER_FLAG_ALL_VIEW_SURFACE_MATCH_MODE, + "DEVELOPER_FLAG_ALL_VIEW_SURFACE_MATCH_MODE", + "Setup All View Surface Matching for Movie Recording", + CheckableEnum::YES, + false)); + std::vector notCheckableItems; notCheckableItems.push_back(DeveloperFlagsEnum(DEVELOPER_FLAG_TEST_SCENE_FILE_READ_WRITE, "DEVELOPER_FLAG_TEST_SCENE_FILE_READ_WRITE", diff --git a/src/Common/DeveloperFlagsEnum.h b/src/Common/DeveloperFlagsEnum.h index 63a32fa3d..30ab30cef 100644 --- a/src/Common/DeveloperFlagsEnum.h +++ b/src/Common/DeveloperFlagsEnum.h @@ -40,7 +40,8 @@ public: DEVELOPER_FLAG_NEW_SCENE_FILE_READ_WRITE, DEVELOPER_FLAG_TEST_SCENE_FILE_READ_WRITE, DEVELOPER_FLAG_TIME_SCENE_FILE_READ_WRITE, - DEVELOPER_FLAG_TEST_ALL_SCENE_FILES_IN_DIRECTORY + DEVELOPER_FLAG_TEST_ALL_SCENE_FILES_IN_DIRECTORY, + DEVELOPER_FLAG_ALL_VIEW_SURFACE_MATCH_MODE }; ~DeveloperFlagsEnum(); diff --git a/src/Files/SurfaceFile.cxx b/src/Files/SurfaceFile.cxx index c0960bafe..15ae86916 100644 --- a/src/Files/SurfaceFile.cxx +++ b/src/Files/SurfaceFile.cxx @@ -18,6 +18,7 @@ */ /*LICENSE_END*/ +#include #include #include @@ -928,9 +929,122 @@ SurfaceFile::getBoundingBox() const return this->boundingBox; } +/** + * Match a sphere to the given surface while retaining the spherical + * shape. The center of gravity of the sphere will be the same as the center of gravity of + * the match surface and the radius of the sphere will be the + * distance furthest from the origin in the match surface. + * + * @param matchSurfaceFile + * Match to this surface file. + */ +void +SurfaceFile::matchSphereToSurface(const SurfaceFile* matchSurfaceFile) +{ + CaretAssert(matchSurfaceFile); + + const float oldRadius = getSphericalRadius(); + if (oldRadius <= 0.0) { + CaretLogWarning("Match sphere has invalid radius"); + return; + } + + /* + * Find match surface vertex distance furthest from origin (0,0,0) + * This value will become the radius of the sphere + */ + CaretPointer matchTH = matchSurfaceFile->getTopologyHelper(); + const int32_t numMatchVertices = matchSurfaceFile->getNumberOfNodes(); + float newRadius = 0.0; + int32_t furthestVertex = -1; + for (int32_t i = 0; i < numMatchVertices; i++) { + if (matchTH->getNodeHasNeighbors(i)) { + const float* xyz = matchSurfaceFile->getCoordinate(i); + const float dist = ((xyz[0]*xyz[0]) + + (xyz[1]*xyz[1]) + + (xyz[2]*xyz[2])); + if (dist > newRadius) { + newRadius = dist; + furthestVertex = i; + } + } + } + newRadius = std::sqrt(newRadius); +// std::cout << "Structure: " << StructureEnum::toGuiName(getStructure()) << std::endl; +// std::cout << " Old radius: " << oldRadius << " New radius: " << newRadius << std::endl; +// std::cout << " Match furthest vertex: " << furthestVertex << std::endl; + + float myCOG[3]; + getCenterOfGravity(myCOG); + + float matchCOG[3]; + matchSurfaceFile->getCenterOfGravity(matchCOG); + + + Matrix4x4 matrix; + + /* + * Scale to new radius + */ + const float scale = newRadius / oldRadius; + matrix.scale(scale, + scale, + scale); + +// std::cout << " Sphere COG: " << myCOG[0] << ", " << myCOG[1] << ", " << myCOG[2] << std::endl; +// std::cout << " Move to COG: " << matchCOG[0] << ", " << matchCOG[1] << ", " << matchCOG[2] << std::endl; + /* + * Translate to match surface center-of-gravity + */ +// DO NOT as is offsets sphere posterior of anatomical as anatomical COG-Y is about -20 +// matrix.translate(matchCOG[0], +// matchCOG[1], +// matchCOG[2]); + + + applyMatrix(matrix); +} + +/** + * Get the center of gravity (average coordinate) of the surface. + * + * param cogOut + * Output containing center of gravity. + */ +void +SurfaceFile::getCenterOfGravity(float cogOut[3]) const +{ + cogOut[0] = 0.0; + cogOut[1] = 0.0; + cogOut[2] = 0.0; + + const int32_t numberOfNodes = getNumberOfNodes(); + CaretPointer th = this->getTopologyHelper(); + + double numberOfNodesWithNeighbors = 0.0; + + double cx(0.0), cy(0.0), cz(0.0); + for (int32_t i = 0; i < numberOfNodes; i++) { + if (th->getNodeHasNeighbors(i)) { + const float* xyz = getCoordinate(i); + + cx += xyz[0]; + cy += xyz[1]; + cz += xyz[2]; + numberOfNodesWithNeighbors += 1.0; + } + } + + if (numberOfNodesWithNeighbors > 0.0) { + cogOut[0] = cx / numberOfNodesWithNeighbors; + cogOut[1] = cy / numberOfNodesWithNeighbors; + cogOut[2] = cz / numberOfNodesWithNeighbors; + } +} + /** * Match this surface to the given surface. That is, after this - * method is called, this surface and the given surface will + * method is called, this surface and the given surface will * fit within the same bounding box. * @param surfaceFile * Match to this surface file. @@ -949,18 +1063,18 @@ SurfaceFile::matchSurfaceBoundingBox(const SurfaceFile* surfaceFile) * Translate min x/y/z to origin */ matrix.translate(-myBoundingBox->getMinX(), - -myBoundingBox->getMinY(), - -myBoundingBox->getMinZ()); + -myBoundingBox->getMinY(), + -myBoundingBox->getMinZ()); /* * Scale to match size of match surface */ float scaleX = (targetBoundingBox->getDifferenceX() - / myBoundingBox->getDifferenceX()); + / myBoundingBox->getDifferenceX()); float scaleY = (targetBoundingBox->getDifferenceY() - / myBoundingBox->getDifferenceY()); + / myBoundingBox->getDifferenceY()); float scaleZ = (targetBoundingBox->getDifferenceZ() - / myBoundingBox->getDifferenceZ()); + / myBoundingBox->getDifferenceZ()); if (getSurfaceType() == SurfaceTypeEnum::FLAT) { /* @@ -976,16 +1090,16 @@ SurfaceFile::matchSurfaceBoundingBox(const SurfaceFile* surfaceFile) } matrix.scale(scaleX, - scaleY, - scaleZ); + scaleY, + scaleZ); /* * Translate to min x/y/z of match surface so that * the two surfaces are now within the same "shoebox". */ matrix.translate(targetBoundingBox->getMinX(), - targetBoundingBox->getMinY(), - targetBoundingBox->getMinZ()); + targetBoundingBox->getMinY(), + targetBoundingBox->getMinZ()); applyMatrix(matrix); } diff --git a/src/Files/SurfaceFile.h b/src/Files/SurfaceFile.h index c96b7a999..f871dc1a3 100644 --- a/src/Files/SurfaceFile.h +++ b/src/Files/SurfaceFile.h @@ -147,6 +147,10 @@ namespace caret { void matchSurfaceBoundingBox(const SurfaceFile* surfaceFile); + void matchSphereToSurface(const SurfaceFile* surfaceFile); + + void getCenterOfGravity(float cogOut[3]) const; + void applyMatrix(const Matrix4x4& matrix); void getNodesSpacingStatistics(DescriptiveStatistics& statsOut) const; diff --git a/src/GuiQt/BrainBrowserWindow.cxx b/src/GuiQt/BrainBrowserWindow.cxx index f956b0ccf..5fdc01617 100644 --- a/src/GuiQt/BrainBrowserWindow.cxx +++ b/src/GuiQt/BrainBrowserWindow.cxx @@ -1642,6 +1642,7 @@ BrainBrowserWindow::createMenuDevelop() } QAction* action = menu->addAction(DeveloperFlagsEnum::toGuiName(flag)); + action->setMenuRole(QAction::NoRole); // Menu item containing "Setup" is moved by Qt, see QTBUG-43588 action->setCheckable(itemCheckableFlag); action->setData(static_cast(DeveloperFlagsEnum::toIntegerCode(flag))); m_developerFlagsActionGroup->addAction(action); @@ -1720,6 +1721,19 @@ BrainBrowserWindow::developerMenuFlagTriggered(QAction* action) false); } } + else if (enumValue == DeveloperFlagsEnum::DEVELOPER_FLAG_ALL_VIEW_SURFACE_MATCH_MODE) { + if (DeveloperFlagsEnum::isFlag(DeveloperFlagsEnum::DEVELOPER_FLAG_ALL_VIEW_SURFACE_MATCH_MODE)) { + Brain* brain = GuiManager::get()->getBrain(); + const int32_t numBrainStructures = brain->getNumberOfBrainStructures(); + if (numBrainStructures <= 0) { + return; + } + for (int32_t i = 0; i < numBrainStructures; i++) { + BrainStructure* bs = brain->getBrainStructure(i); + bs->matchSurfacesToPrimaryAnatomical(); + } + } + } /* * Update graphics and GUI -- GitLab From 30b8371fa8fe0ea25624ddd96d80c1f5c3653b43 Mon Sep 17 00:00:00 2001 From: John Harwell Date: Fri, 22 Feb 2019 16:35:49 -0600 Subject: [PATCH 201/427] Fix problem with GL_BLEND being improperly disabled by using glPushAttrib() and glPopAttrib(). --- src/Brain/BrainOpenGLFixedPipeline.cxx | 11 +++++++++-- src/Brain/BrainOpenGLVolumeSliceDrawing.cxx | 12 ++++++++---- src/Brain/FtglFontTextRenderer.cxx | 18 +++++++++--------- 3 files changed, 26 insertions(+), 15 deletions(-) diff --git a/src/Brain/BrainOpenGLFixedPipeline.cxx b/src/Brain/BrainOpenGLFixedPipeline.cxx index 04c95abdf..8f40bf4a5 100644 --- a/src/Brain/BrainOpenGLFixedPipeline.cxx +++ b/src/Brain/BrainOpenGLFixedPipeline.cxx @@ -1811,6 +1811,8 @@ BrainOpenGLFixedPipeline::disableLighting() void BrainOpenGLFixedPipeline::enableLineAntiAliasing() { + glPushAttrib(GL_ENABLE_BIT + | GL_COLOR_BUFFER_BIT); /* * If multi-sampling is enabled, it handle anti-aliasing */ @@ -1839,8 +1841,9 @@ BrainOpenGLFixedPipeline::disableLineAntiAliasing() return; } - glDisable(GL_LINE_SMOOTH); - glDisable(GL_BLEND); + glPopAttrib(); +// glDisable(GL_LINE_SMOOTH); +// glDisable(GL_BLEND); } /** @@ -1918,6 +1921,8 @@ BrainOpenGLFixedPipeline::drawSurface(Surface* surface, const float* nodeColoringRGBA, const bool drawAnnotationsInModelSpaceFlag) { + glPushAttrib(GL_COLOR_BUFFER_BIT); + const DisplayPropertiesSurface* dps = m_brain->getDisplayPropertiesSurface(); glMatrixMode(GL_MODELVIEW); @@ -2111,6 +2116,8 @@ BrainOpenGLFixedPipeline::drawSurface(Surface* surface, this->disableLighting(); this->disableClippingPlanes(); + + glPopAttrib(); } /** diff --git a/src/Brain/BrainOpenGLVolumeSliceDrawing.cxx b/src/Brain/BrainOpenGLVolumeSliceDrawing.cxx index 2efd1ae93..c672f13b9 100644 --- a/src/Brain/BrainOpenGLVolumeSliceDrawing.cxx +++ b/src/Brain/BrainOpenGLVolumeSliceDrawing.cxx @@ -1516,7 +1516,7 @@ BrainOpenGLVolumeSliceDrawing::drawOrthogonalSlice(const VolumeSliceViewPlaneEnu * Only allow layer blending when overall volume opacity is off (>= 1.0) */ const DisplayPropertiesVolume* dpv = m_brain->getDisplayPropertiesVolume(); - const bool allowBlendingFlag(dpv->getOpacity() >= 1.0f); + const bool allowBlendingFlag(true); //dpv->getOpacity() >= 1.0f); glPushAttrib(GL_COLOR_BUFFER_BIT); if (allowBlendingFlag) { glEnable(GL_BLEND); @@ -2081,7 +2081,7 @@ BrainOpenGLVolumeSliceDrawing::drawOrthogonalSliceWithCulling(const VolumeSliceV * Only allow layer blending when overall volume opacity is off (>= 1.0) */ const DisplayPropertiesVolume* dpv = m_brain->getDisplayPropertiesVolume(); - const bool allowBlendingFlag(dpv->getOpacity() >= 1.0f); + const bool allowBlendingFlag(true); //dpv->getOpacity() >= 1.0f); glPushAttrib(GL_COLOR_BUFFER_BIT); if (allowBlendingFlag) { glEnable(GL_BLEND); @@ -3806,6 +3806,9 @@ BrainOpenGLVolumeSliceDrawing::drawOrthogonalSliceVoxels(const float sliceNormal drawWithQuadIndicesFlag = true; } + GLboolean blendOn = GL_FALSE; + glGetBooleanv(GL_BLEND, &blendOn); + if (drawWithQuadIndicesFlag) { drawOrthogonalSliceVoxelsQuadIndicesAndStrips(sliceNormalVector, coordinate, @@ -3832,7 +3835,8 @@ BrainOpenGLVolumeSliceDrawing::drawOrthogonalSliceVoxels(const float sliceNormal mapIndex, sliceOpacity); } - + glGetBooleanv(GL_BLEND, &blendOn); + } /** @@ -5372,7 +5376,7 @@ BrainOpenGLVolumeSliceDrawing::drawOrthogonalSliceAllView(const VolumeSliceViewP * Only allow layer blending when overall volume opacity is off (>= 1.0) */ const DisplayPropertiesVolume* dpv = m_brain->getDisplayPropertiesVolume(); - const bool allowBlendingFlag(dpv->getOpacity() >= 1.0f); + const bool allowBlendingFlag(true); //dpv->getOpacity() >= 1.0f); glPushAttrib(GL_COLOR_BUFFER_BIT); if (allowBlendingFlag) { glEnable(GL_BLEND); diff --git a/src/Brain/FtglFontTextRenderer.cxx b/src/Brain/FtglFontTextRenderer.cxx index 552992d9d..3b64a324b 100644 --- a/src/Brain/FtglFontTextRenderer.cxx +++ b/src/Brain/FtglFontTextRenderer.cxx @@ -583,6 +583,8 @@ FtglFontTextRenderer::drawUnderline(const double lineStartX, /* * Need to enable anti-aliasing for smooth lines */ + glPushAttrib(GL_COLOR_BUFFER_BIT + | GL_ENABLE_BIT); glEnable(GL_LINE_SMOOTH); glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); @@ -600,9 +602,7 @@ FtglFontTextRenderer::drawUnderline(const double lineStartX, foregroundRgba, GraphicsPrimitive::LineWidthType::PIXELS, underlineThickness); - - glDisable(GL_LINE_SMOOTH); - glDisable(GL_BLEND); + glPopAttrib(); } /** @@ -635,6 +635,8 @@ FtglFontTextRenderer::drawOutline(const double minX, /* * Need to enable anti-aliasing for smooth lines */ + glPushAttrib(GL_COLOR_BUFFER_BIT + | GL_ENABLE_BIT); glEnable(GL_LINE_SMOOTH); glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); @@ -650,9 +652,7 @@ FtglFontTextRenderer::drawOutline(const double minX, GraphicsShape::drawBoxOutlineByteColor(bottomLeft, bottomRight, topRight, topLeft, foregroundRgba, GraphicsPrimitive::LineWidthType::PIXELS, outlineThickness); - - glDisable(GL_LINE_SMOOTH); - glDisable(GL_BLEND); + glPopAttrib(); } /** @@ -682,6 +682,8 @@ FtglFontTextRenderer::drawOutline3D(float bottomLeft[3], /* * Need to enable anti-aliasing for smooth lines */ + glPushAttrib(GL_COLOR_BUFFER_BIT + | GL_ENABLE_BIT); glEnable(GL_LINE_SMOOTH); glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); @@ -690,9 +692,7 @@ FtglFontTextRenderer::drawOutline3D(float bottomLeft[3], GraphicsShape::drawBoxOutlineByteColor(bottomLeft, bottomRight, topRight, topLeft, foregroundRgba, GraphicsPrimitive::LineWidthType::PIXELS, outlineThickness); - - glDisable(GL_LINE_SMOOTH); - glDisable(GL_BLEND); + glPopAttrib(); } /** -- GitLab From ac8b36fffcc39cee08b2c240c12799a48e8548ae Mon Sep 17 00:00:00 2001 From: John Harwell Date: Fri, 22 Feb 2019 16:36:36 -0600 Subject: [PATCH 202/427] Improvement to volume to surface crossfade but still opacity problem with volume-surface-outline turning black when opacity is zero. --- src/GuiQt/WbMacroCustomOperationVolumeToSurfaceCrossFade.cxx | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/GuiQt/WbMacroCustomOperationVolumeToSurfaceCrossFade.cxx b/src/GuiQt/WbMacroCustomOperationVolumeToSurfaceCrossFade.cxx index 1c7ade4ac..129fb763d 100644 --- a/src/GuiQt/WbMacroCustomOperationVolumeToSurfaceCrossFade.cxx +++ b/src/GuiQt/WbMacroCustomOperationVolumeToSurfaceCrossFade.cxx @@ -206,6 +206,7 @@ WbMacroCustomOperationVolumeToSurfaceCrossFade::performCrossFade(Overlay* volume for (int iStep = 0; iStep < numberOfSteps; iStep++) { surfaceProperties->setOpacity(surfaceOpacity); volumeProperties->setOpacity(volumeOpacity); + volumeOverlay->setOpacity(volumeOpacity); updateSurfaceColoring(); updateUserInterface(); updateGraphics(); @@ -224,6 +225,7 @@ WbMacroCustomOperationVolumeToSurfaceCrossFade::performCrossFade(Overlay* volume surfaceProperties->setOpacity(1.0); volumeProperties->setOpacity(0.0); + volumeOverlay->setOpacity(0.0); updateSurfaceColoring(); updateUserInterface(); -- GitLab From a6dd415a0e201010a8399b3495e14e7230bcc202 Mon Sep 17 00:00:00 2001 From: John Harwell Date: Fri, 22 Feb 2019 17:40:31 -0600 Subject: [PATCH 203/427] Fix OpenGL stack overflow error. --- src/Brain/BrainOpenGLFixedPipeline.cxx | 3 ++- src/Brain/BrainOpenGLVolumeSliceDrawing.cxx | 21 +++++++++++---------- 2 files changed, 13 insertions(+), 11 deletions(-) diff --git a/src/Brain/BrainOpenGLFixedPipeline.cxx b/src/Brain/BrainOpenGLFixedPipeline.cxx index 8f40bf4a5..e89f1ea23 100644 --- a/src/Brain/BrainOpenGLFixedPipeline.cxx +++ b/src/Brain/BrainOpenGLFixedPipeline.cxx @@ -1834,6 +1834,8 @@ BrainOpenGLFixedPipeline::enableLineAntiAliasing() void BrainOpenGLFixedPipeline::disableLineAntiAliasing() { + glPopAttrib(); + /* * If multi-sampling is enabled, it handle anti-aliasing */ @@ -1841,7 +1843,6 @@ BrainOpenGLFixedPipeline::disableLineAntiAliasing() return; } - glPopAttrib(); // glDisable(GL_LINE_SMOOTH); // glDisable(GL_BLEND); } diff --git a/src/Brain/BrainOpenGLVolumeSliceDrawing.cxx b/src/Brain/BrainOpenGLVolumeSliceDrawing.cxx index c672f13b9..1f9a517a4 100644 --- a/src/Brain/BrainOpenGLVolumeSliceDrawing.cxx +++ b/src/Brain/BrainOpenGLVolumeSliceDrawing.cxx @@ -2074,6 +2074,17 @@ BrainOpenGLVolumeSliceDrawing::drawOrthogonalSliceWithCulling(const VolumeSliceV const int32_t browserTabIndex = m_browserTabContent->getTabNumber(); const DisplayPropertiesLabels* displayPropertiesLabels = m_brain->getDisplayPropertiesLabels(); const DisplayGroupEnum::Enum displayGroup = displayPropertiesLabels->getDisplayGroupForTab(browserTabIndex); + + /* + * Flat shading voxels not interpolated + */ + glShadeModel(GL_FLAT); + + CaretAssert(plane.isValidPlane()); + if (plane.isValidPlane() == false) { + return; + } + /* * Enable alpha blending so voxels that are not drawn from higher layers * allow voxels from lower layers to be seen. @@ -2087,16 +2098,6 @@ BrainOpenGLVolumeSliceDrawing::drawOrthogonalSliceWithCulling(const VolumeSliceV glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); } - - /* - * Flat shading voxels not interpolated - */ - glShadeModel(GL_FLAT); - - CaretAssert(plane.isValidPlane()); - if (plane.isValidPlane() == false) { - return; - } /* * Compute coordinate of point in center of first slice -- GitLab From 0469d9a377683797eaee0418bb842f6752d50fcd Mon Sep 17 00:00:00 2001 From: John Harwell Date: Mon, 25 Feb 2019 12:10:45 -0600 Subject: [PATCH 204/427] Changed developer menu option: "Setup All View Surface Matching for Movie Recording" so that it only matches inflated, very inflated, flat, and spherical surfaces. --- src/Brain/BrainStructure.cxx | 48 +++++++++++++++++++++++++++++++----- 1 file changed, 42 insertions(+), 6 deletions(-) diff --git a/src/Brain/BrainStructure.cxx b/src/Brain/BrainStructure.cxx index e05861a6c..60b88727b 100644 --- a/src/Brain/BrainStructure.cxx +++ b/src/Brain/BrainStructure.cxx @@ -1537,13 +1537,49 @@ BrainStructure::matchSurfacesToPrimaryAnatomical() return; } - for (auto m : m_surfaces) { - if (m != primaryAnatomical) { - if (m->getSurfaceType() == SurfaceTypeEnum::SPHERICAL) { - m->matchSphereToSurface(primaryAnatomical); + for (auto s : m_surfaces) { + if (s != primaryAnatomical) { + bool sphereMatchFlag(false); + bool matchFlag(false); + switch (s->getSurfaceType()) { + case SurfaceTypeEnum::ANATOMICAL: + break; + case SurfaceTypeEnum::ELLIPSOID: + break; + case SurfaceTypeEnum::FLAT: + matchFlag = true; + break; + case SurfaceTypeEnum::HULL: + break; + case SurfaceTypeEnum::INFLATED: + matchFlag = true; + break; + case SurfaceTypeEnum::RECONSTRUCTION: + break; + case SurfaceTypeEnum::SEMI_SPHERICAL: + break; + case SurfaceTypeEnum::SPHERICAL: + sphereMatchFlag = true; + break; + case SurfaceTypeEnum::UNKNOWN: + break; + case SurfaceTypeEnum::VERY_INFLATED: + matchFlag = true; + break; } - else { - m->matchSurfaceBoundingBox(primaryAnatomical); + + const bool modStatus(s->isModified()); + if (sphereMatchFlag) { + s->matchSphereToSurface(primaryAnatomical); + if ( ! modStatus) { + s->clearModified(); + } + } + else if (matchFlag) { + s->matchSurfaceBoundingBox(primaryAnatomical); + if ( ! modStatus) { + s->clearModified(); + } } } } -- GitLab From c709894dfba9a5d497a93c62993b39f954f0a6d8 Mon Sep 17 00:00:00 2001 From: John Harwell Date: Mon, 25 Feb 2019 14:34:09 -0600 Subject: [PATCH 205/427] WB-834 Macros: Moved command delay form after too before command. --- src/GuiQt/WbMacroHelper.cxx | 56 ++++++++++++++++++++++++++++- src/GuiQt/WbMacroHelper.h | 12 ++++++- src/GuiQt/WuQMacroDialog.cxx | 4 +-- src/GuiQt/WuQMacroExecutor.cxx | 35 ++++++++++++++---- src/GuiQt/WuQMacroExecutor.h | 9 ++++- src/GuiQt/WuQMacroHelperInterface.h | 19 +++++++++- src/GuiQt/WuQMacroManager.cxx | 35 ++++++++++++++++-- src/GuiQt/WuQMacroManager.h | 7 +++- 8 files changed, 161 insertions(+), 16 deletions(-) diff --git a/src/GuiQt/WbMacroHelper.cxx b/src/GuiQt/WbMacroHelper.cxx index 9d7346071..594e51d05 100644 --- a/src/GuiQt/WbMacroHelper.cxx +++ b/src/GuiQt/WbMacroHelper.cxx @@ -242,10 +242,37 @@ WbMacroHelper::macroExecutionEnding(const WuQMacro* /*macro*/, * Widget for parent * @param command * Command that has just finished + * @param allowDelayFlagOut + * Output indicating if delay after command is enabled */ void WbMacroHelper::macroCommandHasCompleted(QWidget* window, - const WuQMacroCommand* command) + const WuQMacroCommand* command, + bool& allowDelayFlagOut) +{ + const bool doDelayAfterCommandFlag(false); + allowDelayFlagOut = doDelayAfterCommandFlag; + if (doDelayAfterCommandFlag) { + recordImagesForDelay(window, + command, + allowDelayFlagOut); + } +} + +/** + * If movie recording is on, capture images for the command's delay time + * + * @param window + * Widget for parent + * @param command + * The command + * @param delay + * The delay in seconds + */ +void +WbMacroHelper::recordImagesForDelay(QWidget* window, + const WuQMacroCommand* command, + bool& allowDelayFlagOut) const { MovieRecorder* movieRecorder = SessionManager::get()->getMovieRecorder(); CaretAssert(movieRecorder); @@ -268,6 +295,8 @@ WbMacroHelper::macroCommandHasCompleted(QWidget* window, } movieRecorder->setManualRecordingOfImageRequested(false); } + + allowDelayFlagOut = false; } break; case MovieRecorderModeEnum::MANUAL: @@ -276,3 +305,28 @@ WbMacroHelper::macroCommandHasCompleted(QWidget* window, } } } + +/** + * Called by macro executor just before starting execution of a command + * + * @param window + * Widget for parent + * @param command + * Command that is about to start + * @param allowDelayFlagOut + * Output indicating if delay before command is enabled + */ +void +WbMacroHelper::macroCommandAboutToStart(QWidget* window, + const WuQMacroCommand* command, + bool& allowDelayFlagOut) +{ + const bool doDelayBeforeCommandFlag(true); + allowDelayFlagOut = doDelayBeforeCommandFlag; + if (doDelayBeforeCommandFlag) { + recordImagesForDelay(window, + command, + allowDelayFlagOut); + } +} + diff --git a/src/GuiQt/WbMacroHelper.h b/src/GuiQt/WbMacroHelper.h index 8b52252ec..201883fbd 100644 --- a/src/GuiQt/WbMacroHelper.h +++ b/src/GuiQt/WbMacroHelper.h @@ -65,11 +65,21 @@ namespace caret { const WuQMacroExecutorOptions* executorOptions) override; virtual void macroCommandHasCompleted(QWidget* window, - const WuQMacroCommand* command); + const WuQMacroCommand* command, + bool& allowDelayFlagOut) override; + + virtual void macroCommandAboutToStart(QWidget* window, + const WuQMacroCommand* command, + bool& allowDelayFlagOut) override; // ADD_NEW_METHODS_HERE private: + void + recordImagesForDelay(QWidget* window, + const WuQMacroCommand* command, + bool& allowDelayFlagOut) const; + MovieRecorderModeEnum::Enum m_savedRecordingMode = MovieRecorderModeEnum::MANUAL; // ADD_NEW_MEMBERS_HERE diff --git a/src/GuiQt/WuQMacroDialog.cxx b/src/GuiQt/WuQMacroDialog.cxx index f9fbc489a..5794d665f 100644 --- a/src/GuiQt/WuQMacroDialog.cxx +++ b/src/GuiQt/WuQMacroDialog.cxx @@ -505,12 +505,12 @@ WuQMacroDialog::createCommandDisplayWidget() m_commandDelaySpinBox->setMaximum(1000.0); m_commandDelaySpinBox->setSingleStep(1.0); m_commandDelaySpinBox->setDecimals(1); - m_commandDelaySpinBox->setToolTip("Delay, in seconds, after running command"); + m_commandDelaySpinBox->setToolTip("Delay, in seconds, before running command"); m_commandDelaySpinBox->setSizePolicy(QSizePolicy::Fixed, m_commandDelaySpinBox->sizePolicy().verticalPolicy()); QObject::connect(m_commandDelaySpinBox, static_cast(&QDoubleSpinBox::valueChanged), this, &WuQMacroDialog::macroCommandDelaySpinBoxValueChanged); - QLabel* delayTwoLabel = new QLabel("seconds after command"); + QLabel* delayTwoLabel = new QLabel("seconds before command"); QLabel* descriptionLabel = new QLabel("Description:"); m_commandDescriptionTextEdit = new QPlainTextEdit(); diff --git a/src/GuiQt/WuQMacroExecutor.cxx b/src/GuiQt/WuQMacroExecutor.cxx index 258963c91..de332017f 100644 --- a/src/GuiQt/WuQMacroExecutor.cxx +++ b/src/GuiQt/WuQMacroExecutor.cxx @@ -368,6 +368,14 @@ WuQMacroExecutor::runMacroPrivate(const WuQMacro* macro, } } + bool allowDelayBeforeCommandFlag(false); + emit macroCommandAboutToStart(window, + mc, + allowDelayBeforeCommandFlag); + if (allowDelayBeforeCommandFlag) { + performCommandDelay(mc); + } + QString commandErrorMessage; bool successFlag(false); switch (mc->getCommandType()) { @@ -409,13 +417,12 @@ WuQMacroExecutor::runMacroPrivate(const WuQMacro* macro, return false; } + bool allowDelayAfterCommandFlag(false); emit macroCommandHasCompleted(window, - mc); - - if (mc->getCommandType() != WuQMacroCommandTypeEnum::MOUSE) { - if (mc->getDelayInSeconds() > 0.0) { - SystemUtilities::sleepSeconds(mc->getDelayInSeconds()); - } + mc, + allowDelayAfterCommandFlag); + if (allowDelayAfterCommandFlag) { + performCommandDelay(mc); } QGuiApplication::processEvents(); @@ -424,6 +431,22 @@ WuQMacroExecutor::runMacroPrivate(const WuQMacro* macro, return (errorMessageOut.isEmpty()); } +/** + * Perform delay for the given macro command + * + * @param mc + * The macro command + */ +void +WuQMacroExecutor::performCommandDelay(const WuQMacroCommand* mc) const +{ + if (mc->getCommandType() != WuQMacroCommandTypeEnum::MOUSE) { + if (mc->getDelayInSeconds() > 0.0) { + SystemUtilities::sleepSeconds(mc->getDelayInSeconds()); + } + } +} + /** * Stop the macro that is executing */ diff --git a/src/GuiQt/WuQMacroExecutor.h b/src/GuiQt/WuQMacroExecutor.h index 1f92774aa..ec0ea34bb 100644 --- a/src/GuiQt/WuQMacroExecutor.h +++ b/src/GuiQt/WuQMacroExecutor.h @@ -61,8 +61,13 @@ namespace caret { // ADD_NEW_METHODS_HERE signals: + void macroCommandAboutToStart(QWidget* window, + const WuQMacroCommand* command, + bool& allowDelayFlagOut) const; + void macroCommandHasCompleted(QWidget* window, - const WuQMacroCommand* command) const; + const WuQMacroCommand* command, + bool& allowDelayFlagOut) const; private: void moveMouseToTabBarTab(QTabBar* tabBar, @@ -82,6 +87,8 @@ namespace caret { const QRect* objectRect = NULL, const bool hightlightFlag = false) const; + void performCommandDelay(const WuQMacroCommand* mc) const; + bool runMacroPrivate(const WuQMacro* macro, QWidget* window, std::vector& otherObjectParents, diff --git a/src/GuiQt/WuQMacroHelperInterface.h b/src/GuiQt/WuQMacroHelperInterface.h index 80a76661a..f3e3cf8b5 100644 --- a/src/GuiQt/WuQMacroHelperInterface.h +++ b/src/GuiQt/WuQMacroHelperInterface.h @@ -122,9 +122,26 @@ namespace caret { * Widget for parent * @param command * Command that has just finished + * @param allowDelayFlagOut + * Output indicating if delay after command is enabled */ virtual void macroCommandHasCompleted(QWidget* window, - const WuQMacroCommand* command) = 0; + const WuQMacroCommand* command, + bool& allowDelayFlagOut) = 0; + + /** + * Called by macro executor just before starting execution of a command + * + * @param window + * Widget for parent + * @param command + * Command that is about to start + * @param allowDelayFlagOut + * Output indicating if delay before command is enabled + */ + virtual void macroCommandAboutToStart(QWidget* window, + const WuQMacroCommand* command, + bool& allowDelayFlagOut) = 0; // ADD_NEW_METHODS_HERE diff --git a/src/GuiQt/WuQMacroManager.cxx b/src/GuiQt/WuQMacroManager.cxx index 2f1aea38a..a38e3a11a 100644 --- a/src/GuiQt/WuQMacroManager.cxx +++ b/src/GuiQt/WuQMacroManager.cxx @@ -527,9 +527,11 @@ WuQMacroManager::runMacro(QWidget* widget, QString errorMessage; m_macroExecutor = new WuQMacroExecutor(); + QObject::connect(m_macroExecutor, &WuQMacroExecutor::macroCommandAboutToStart, + this, &WuQMacroManager::macroCommandStartingExecution); QObject::connect(m_macroExecutor, &WuQMacroExecutor::macroCommandHasCompleted, this, &WuQMacroManager::macroCommandCompletedExecution); - + if (m_macroHelper != NULL) { m_macroHelper->macroExecutionStarting(macro, widget, @@ -583,15 +585,42 @@ WuQMacroManager::stopMacro() * Window in which command is running * @param command * Command that just finished execution + * @param allowDelayFlagOut + * Allow delay for after command has completed */ void WuQMacroManager::macroCommandCompletedExecution(QWidget* window, - const WuQMacroCommand* command) + const WuQMacroCommand* command, + bool& allowDelayFlagOut) { CaretAssert(command); if (m_macroHelper != NULL) { m_macroHelper->macroCommandHasCompleted(window, - command); + command, + allowDelayFlagOut); + } +} + +/** + * Called by macro executor when a macro command is about to start + * + * @param window + * Window in which command is running + * @param command + * Command that about to start execution + * @param allowDelayFlagOut + * Allow delay for after command has completed + */ +void +WuQMacroManager::macroCommandStartingExecution(QWidget* window, + const WuQMacroCommand* command, + bool& allowDelayFlagOut) +{ + CaretAssert(command); + if (m_macroHelper != NULL) { + m_macroHelper->macroCommandAboutToStart(window, + command, + allowDelayFlagOut); } } diff --git a/src/GuiQt/WuQMacroManager.h b/src/GuiQt/WuQMacroManager.h index 301d96b6e..c7451af4f 100644 --- a/src/GuiQt/WuQMacroManager.h +++ b/src/GuiQt/WuQMacroManager.h @@ -169,7 +169,12 @@ namespace caret { void updateNonModalDialogs(); void macroCommandCompletedExecution(QWidget* window, - const WuQMacroCommand* command); + const WuQMacroCommand* command, + bool& allowDelayFlagOut); + + void macroCommandStartingExecution(QWidget* window, + const WuQMacroCommand* command, + bool& allowDelayFlagOut); private: WuQMacroManager(const QString& name, -- GitLab From 030ef7480ada9dd90e111fdc20c3ba09a92d0e5d Mon Sep 17 00:00:00 2001 From: John Harwell Date: Mon, 25 Feb 2019 15:00:58 -0600 Subject: [PATCH 206/427] WB-834 Macros: Fixes and improvements for Macro dialog when no macros are present and/or no scene is active. --- src/GuiQt/WuQMacroDialog.cxx | 11 ++++++++++- src/GuiQt/WuQMacroDialog.h | 2 ++ 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/src/GuiQt/WuQMacroDialog.cxx b/src/GuiQt/WuQMacroDialog.cxx index 5794d665f..3b21d89da 100644 --- a/src/GuiQt/WuQMacroDialog.cxx +++ b/src/GuiQt/WuQMacroDialog.cxx @@ -117,10 +117,12 @@ WuQMacroDialog::WuQMacroDialog(QWidget* parent) QWidget* macroSelectionWidget = createMacroAndCommandSelectionWidget(); m_macroWidget = createMacroDisplayWidget(); m_commandWidget = createCommandDisplayWidget(); + m_emptyWidget = new QWidget(); m_stackedWidget = new QStackedWidget(); m_stackedWidget->addWidget(m_macroWidget); m_stackedWidget->addWidget(m_commandWidget); - m_stackedWidget->setCurrentWidget(m_macroWidget); + m_stackedWidget->addWidget(m_emptyWidget); + m_stackedWidget->setCurrentWidget(m_emptyWidget); QScrollArea* stackedScrollArea = new QScrollArea(); stackedScrollArea->setWidget(m_stackedWidget); stackedScrollArea->setWidgetResizable(true); @@ -602,6 +604,8 @@ WuQMacroDialog::updateDialogContents() m_macroGroupComboBox->setCurrentIndex(selectedIndex); } + m_macroGroupToolButton->setEnabled(m_macroGroupComboBox->count() > 0); + const QString windowText = m_runOptionsWindowComboBox->currentText(); m_runOptionsWindowComboBox->clear(); const std::vector windowIDs = WuQMacroManager::instance()->getMainWindowIdentifiers(); @@ -662,10 +666,14 @@ WuQMacroDialog::treeViewItemClicked(const QModelIndex& modelIndex) } } else { + m_stackedWidget->setCurrentWidget(m_emptyWidget); CaretAssertMessage(0, ("Invalid StandardItemModel type=" + AString::number(selectedItem->type()))); } } + else { + m_stackedWidget->setCurrentWidget(m_emptyWidget); + } updateMacroWidget(macro); updateCommandWidget(macroCommand); @@ -703,6 +711,7 @@ WuQMacroDialog::macroGroupComboBoxActivated(int) } else { m_treeView->setModel(new QStandardItemModel()); + treeViewItemClicked(QModelIndex()); } } diff --git a/src/GuiQt/WuQMacroDialog.h b/src/GuiQt/WuQMacroDialog.h index a66d16079..c6fa500b0 100644 --- a/src/GuiQt/WuQMacroDialog.h +++ b/src/GuiQt/WuQMacroDialog.h @@ -181,6 +181,8 @@ namespace caret { QWidget* m_commandWidget; + QWidget* m_emptyWidget; + QStackedWidget* m_stackedWidget; QDialogButtonBox* m_dialogButtonBox; -- GitLab From fe3d171c799a68b9bb8c1d50c7bfcd03fcf5acbc Mon Sep 17 00:00:00 2001 From: John Harwell Date: Tue, 26 Feb 2019 12:19:29 -0600 Subject: [PATCH 207/427] Fixed capture of movie images so that images are correct aspect ratio when the window width or height is extremely narrow. --- src/GuiQt/BrainOpenGLWidget.cxx | 181 +++++++++++++++++++++----------- 1 file changed, 120 insertions(+), 61 deletions(-) diff --git a/src/GuiQt/BrainOpenGLWidget.cxx b/src/GuiQt/BrainOpenGLWidget.cxx index 863ce6c8e..f283f588b 100644 --- a/src/GuiQt/BrainOpenGLWidget.cxx +++ b/src/GuiQt/BrainOpenGLWidget.cxx @@ -1715,82 +1715,141 @@ BrainOpenGLWidget::receiveEvent(Event* event) if (captureAutomaticImageForMovieFlag || captureManualImageForMovieFlag) { - int captureOffsetX(0); - int captureOffsetY(0); - int captureWidth(0); - int captureHeight(0); + int captureRegionOffsetX(0); + int captureRegionOffsetY(0); + int captureRegionWidth(0); + int captureRegionHeight(0); int widgetWidth(0); int widgetHeight(0); BrainBrowserWindow* browserWindow = GuiManager::get()->getBrowserWindowByWindowIndex(this->windowIndex); if (browserWindow != NULL) { - browserWindow->getGraphicsWidgetSize(captureOffsetX, - captureOffsetY, - captureWidth, - captureHeight, + browserWindow->getGraphicsWidgetSize(captureRegionOffsetX, + captureRegionOffsetY, + captureRegionWidth, + captureRegionHeight, widgetWidth, widgetHeight, true); - int imageWidth(0); - int imageHeight(0); - movieRecorder->getVideoWidthAndHeight(imageWidth, - imageHeight); + int outputImageWidth(0); + int outputImageHeight(0); + movieRecorder->getVideoWidthAndHeight(outputImageWidth, + outputImageHeight); - QImage image; - bool imageValid(false); - - switch (movieRecorder->getCaptureRegionType()) { - case MovieRecorderCaptureRegionTypeEnum::GRAPHICS: - { - EventImageCapture captureEvent(this->windowIndex, - captureOffsetX, - captureOffsetY, - captureWidth, - captureHeight, - imageWidth, - imageHeight); - captureImage(&captureEvent); - - if (captureEvent.isError()) { - CaretLogSevere("Failed to capture image of graphics for movie recording"); - } - else { - image = captureEvent.getImage(); - imageValid = true; - } - } - break; - case MovieRecorderCaptureRegionTypeEnum::WINDOW: - { - BrainBrowserWindow* bbw = GuiManager::get()->getBrowserWindowByWindowIndex(this->windowIndex); - QRect rect(0, 0, - bbw->width(), bbw->height()); - QPixmap pm = bbw->grab(rect); - if ((pm.width() > 0) - && (pm.height() > 0)) { - image = pm.toImage(); - imageValid = true; + AString msg; + if ((outputImageWidth <= 0) + || (outputImageHeight <= 0)) { + msg.appendWithNewLine("Movie width=" + + AString::number(outputImageWidth) + + ", height=" + + AString::number(outputImageHeight) + + " is invalid."); + } + if ((captureRegionWidth <= 0) + || (captureRegionHeight <= 0)) { + msg.appendWithNewLine("Movie capture region width=" + + AString::number(captureRegionWidth) + + ", height=" + + AString::number(captureRegionHeight) + + " is invalid."); + } + if ( ! msg.isEmpty()) { + CaretLogSevere(msg); + } + else { + QImage image; + bool imageValid(false); + + switch (movieRecorder->getCaptureRegionType()) { + case MovieRecorderCaptureRegionTypeEnum::GRAPHICS: + { + bool adjustImageSizeFlag(false); + int captureWidth = outputImageWidth; + int captureHeight = outputImageHeight; + if ((captureWidth != captureRegionWidth) + || (captureHeight != captureRegionHeight)) { + const float outputAspectRatio = (outputImageHeight / outputImageWidth); + const float captureRegionAspectRatio = (captureRegionHeight / captureRegionWidth); + if (captureRegionAspectRatio > outputAspectRatio) { + const float ratio = outputImageHeight / captureRegionHeight; + captureWidth = outputImageWidth * ratio; + captureHeight = outputImageHeight; + } + else { + const float ratio = outputImageWidth / captureRegionWidth; + captureHeight = outputImageHeight * ratio; + captureWidth = outputImageWidth; + + } + adjustImageSizeFlag = true; + } + + EventImageCapture captureEvent(this->windowIndex, + captureRegionOffsetX, + captureRegionOffsetY, + captureRegionWidth, + captureRegionHeight, + captureWidth, + captureHeight); + captureImage(&captureEvent); - QImage scaledImage = ImageFile::scaleToSizeWithPadding(image, imageWidth, imageHeight); - if ( ! scaledImage.isNull()) { - image = scaledImage; + if (captureEvent.isError()) { + CaretLogSevere("Failed to capture image of graphics for movie recording"); + } + else { + image = captureEvent.getImage(); + imageValid = true; + if (adjustImageSizeFlag) { + QImage scaledImage = ImageFile::scaleToSizeWithPadding(image, + outputImageWidth, + outputImageHeight); + if (scaledImage.isNull()) { + CaretLogSevere("Failed to scale image for movie recording"); + } + else { + image = scaledImage; + } + } } } - else { - CaretLogSevere("Failed to capture image of window"); + break; + case MovieRecorderCaptureRegionTypeEnum::WINDOW: + { + BrainBrowserWindow* bbw = GuiManager::get()->getBrowserWindowByWindowIndex(this->windowIndex); + QRect rect(0, 0, + bbw->width(), bbw->height()); + QPixmap pm = bbw->grab(rect); + if ((pm.width() > 0) + && (pm.height() > 0)) { + image = pm.toImage(); + imageValid = true; + + QImage scaledImage = ImageFile::scaleToSizeWithPadding(image, + outputImageWidth, + outputImageHeight); + if (scaledImage.isNull()) { + CaretLogSevere("Failed to scale image for movie recording"); + } + else { + image = scaledImage; + } + } + else { + CaretLogSevere("Failed to capture image of window"); + } } + break; } - break; - } - - if (imageValid) { - if (captureAutomaticImageForMovieFlag) { - movieRecorder->addImageToMovie(&image); - } - else if (captureManualImageForMovieFlag) { - movieRecorder->addImageToMovieWithManualDuration(&image); + + if (imageValid) { + if (captureAutomaticImageForMovieFlag) { + movieRecorder->addImageToMovie(&image); + } + else if (captureManualImageForMovieFlag) { + movieRecorder->addImageToMovieWithManualDuration(&image); + } + EventManager::get()->sendSimpleEvent(EventTypeEnum::EVENT_MOVIE_RECORDING_DIALOG_UPDATE); } - EventManager::get()->sendSimpleEvent(EventTypeEnum::EVENT_MOVIE_RECORDING_DIALOG_UPDATE); } } } -- GitLab From 6b74b537c5226b342ef258d2921a73b1e6bb9f57 Mon Sep 17 00:00:00 2001 From: John Harwell Date: Wed, 27 Feb 2019 12:19:39 -0600 Subject: [PATCH 208/427] WB-834 Macros: In macro dialog, fixed selection of macro or command using arrow keys. --- src/GuiQt/WuQMacroDialog.cxx | 83 ++++++++++++++++++++++++++++++++---- src/GuiQt/WuQMacroDialog.h | 6 +++ 2 files changed, 80 insertions(+), 9 deletions(-) diff --git a/src/GuiQt/WuQMacroDialog.cxx b/src/GuiQt/WuQMacroDialog.cxx index 3b21d89da..cad4ede01 100644 --- a/src/GuiQt/WuQMacroDialog.cxx +++ b/src/GuiQt/WuQMacroDialog.cxx @@ -31,6 +31,7 @@ #include #include #include +#include #include #include #include @@ -624,10 +625,31 @@ WuQMacroDialog::updateDialogContents() } /** - * Called when an item in the tree view is clicked + * Called when an item in the tree view is clicked by user + * + * @param modelIndex + * Model index of item selected + */ +void +WuQMacroDialog::treeViewItemClicked(const QModelIndex& /*modelIndex*/) +{ + /* + * Unused at this time. The signal only works when user + * click's an item and not when an arrow key is used + * to select an item. Replacement is for the selection + * model to connect to selectionModelRowChanged(). + */ +} + +/** + * Called when an item in the tree is selected in some way + * (mouse click, arrow key, etc) + * + * @param modelIndex + * Model index of item selected */ void -WuQMacroDialog::treeViewItemClicked(const QModelIndex& modelIndex) +WuQMacroDialog::treeItemSelected(const QModelIndex& modelIndex) { QStandardItemModel* selectedModel = NULL; if (modelIndex.isValid()) { @@ -702,19 +724,62 @@ WuQMacroDialog::macroGroupComboBoxActivated(int) selectedIndex = m_treeView->currentIndex(); } + m_blockSelectionModelRowChangedFlag = true; + m_treeView->setModel(selectedGroup); + + { + /* + * Need to (re)connect the selection model's row changed signal + * since setModel() was called. + * + * From the Qt Documentation for QAbstractItemView::setSelectionModel(): + * Note that, if you call setModel() after this function, the given + * selectionModel will be replaced by one created by the view. + */ + QItemSelectionModel* selectionModel = m_treeView->selectionModel(); + if (selectionModel != NULL) { + /* + * Use the option Qt::UniqueConnection to avoid creating + * a duplicate connection + */ + QObject::connect(selectionModel, &QItemSelectionModel::currentRowChanged, + this, &WuQMacroDialog::selectionModelRowChanged, + Qt::UniqueConnection); + } + } + m_blockSelectionModelRowChangedFlag = false; if (selectedIndex.isValid()) { m_treeView->setCurrentIndex(selectedIndex); } - treeViewItemClicked(m_treeView->currentIndex()); + treeItemSelected(m_treeView->currentIndex()); } else { m_treeView->setModel(new QStandardItemModel()); - treeViewItemClicked(QModelIndex()); + treeItemSelected(QModelIndex()); } } +/** + * Called when selection model's row is changed + * + * @param current + * Model index of current item + * @parm previous + * Model index of previous item + */ +void +WuQMacroDialog::selectionModelRowChanged(const QModelIndex& current, + const QModelIndex& /*previous*/) +{ + if (m_blockSelectionModelRowChangedFlag) { + return; + } + + treeItemSelected(current); +} + /** * Update the macro widget with the given macro * @@ -1129,7 +1194,7 @@ WuQMacroDialog::editingMoveUpToolButtonClicked() if (macro != NULL) { macroGroup->moveMacroUp(macro); m_treeView->setCurrentIndex(macro->index()); - treeViewItemClicked(macro->index()); + treeItemSelected(macro->index()); } } break; @@ -1138,7 +1203,7 @@ WuQMacroDialog::editingMoveUpToolButtonClicked() && (command != NULL)) { macro->moveMacroCommandUp(command); m_treeView->setCurrentIndex(command->index()); - treeViewItemClicked(command->index()); + treeItemSelected(command->index()); } break; } @@ -1162,7 +1227,7 @@ WuQMacroDialog::editingMoveDownToolButtonClicked() if (macro != NULL) { macroGroup->moveMacroDown(macro); m_treeView->setCurrentIndex(macro->index()); - treeViewItemClicked(macro->index()); + treeItemSelected(macro->index()); } } break; @@ -1171,7 +1236,7 @@ WuQMacroDialog::editingMoveDownToolButtonClicked() && (command != NULL)) { macro->moveMacroCommandDown(command); m_treeView->setCurrentIndex(command->index()); - treeViewItemClicked(command->index()); + treeItemSelected(command->index()); } break; } @@ -1208,7 +1273,7 @@ WuQMacroDialog::editingDeleteToolButtonClicked() && (macroIndex < macroGroup->getNumberOfMacros())) { QModelIndex modelIndex = macroGroup->getMacroAtIndex(macroIndex)->index(); m_treeView->setCurrentIndex(modelIndex); - treeViewItemClicked(modelIndex); + treeItemSelected(modelIndex); } } } diff --git a/src/GuiQt/WuQMacroDialog.h b/src/GuiQt/WuQMacroDialog.h index c6fa500b0..81fdcf0d2 100644 --- a/src/GuiQt/WuQMacroDialog.h +++ b/src/GuiQt/WuQMacroDialog.h @@ -117,6 +117,8 @@ namespace caret { void addNewMacroCommand(WuQMacroCommand* command); + void selectionModelRowChanged(const QModelIndex& current, const QModelIndex& previous); + private: enum class ValueIndex { ONE, @@ -163,6 +165,8 @@ namespace caret { void updateEditingToolButtons(); + void treeItemSelected(const QModelIndex& modelIndex); + std::vector m_macroGroups; QComboBox* m_macroGroupComboBox; @@ -223,6 +227,8 @@ namespace caret { bool m_macroIsRunningFlag = false; + bool m_blockSelectionModelRowChangedFlag = false; + // ADD_NEW_MEMBERS_HERE }; -- GitLab From 25f6c118feb569cd3781781e99b337b2642922ce Mon Sep 17 00:00:00 2001 From: John Harwell Date: Wed, 27 Feb 2019 14:32:47 -0600 Subject: [PATCH 209/427] WB-834 Macros: do not require user to confirm deletion of a macro command. --- src/GuiQt/WuQMacroManager.cxx | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/src/GuiQt/WuQMacroManager.cxx b/src/GuiQt/WuQMacroManager.cxx index a38e3a11a..b1dfce9cd 100644 --- a/src/GuiQt/WuQMacroManager.cxx +++ b/src/GuiQt/WuQMacroManager.cxx @@ -711,11 +711,16 @@ WuQMacroManager::deleteMacroCommand(QWidget* parent, CaretAssert(macroGroup); CaretAssert(macro); CaretAssert(macroCommand); - if (QMessageBox::warning(parent, - "Warning", - ("Delete the macro command: " + macroCommand->getDescriptiveName()), - QMessageBox::Ok | QMessageBox::Cancel, - QMessageBox::Ok) == QMessageBox::Ok) { + bool deleteFlag(true); + const bool confirmDeleteFlag(false); + if (confirmDeleteFlag) { + deleteFlag = (QMessageBox::warning(parent, + "Warning", + ("Delete the macro command: " + macroCommand->getDescriptiveName()), + QMessageBox::Ok | QMessageBox::Cancel, + QMessageBox::Ok) == QMessageBox::Ok); + } + if (deleteFlag) { macro->deleteMacroCommand(macroCommand); if (m_macroHelper) { m_macroHelper->macroGroupWasModified(macroGroup); -- GitLab From 51dbd59cbb87c7e26add767ef429ecf48e6d583e Mon Sep 17 00:00:00 2001 From: John Harwell Date: Wed, 27 Feb 2019 16:32:45 -0600 Subject: [PATCH 210/427] WB-834 Macros: Added toolbuttons to main window for macros dialog (a script icon) and a movie dialog (a movie camera icon). --- src/Brain/MovieRecorder.cxx | 6 +++ src/GuiQt/BrainBrowserWindowToolBar.cxx | 50 ++++++++++++++++++++- src/GuiQt/BrainBrowserWindowToolBar.h | 2 + src/GuiQt/WuQMacroMenu.cxx | 4 +- src/Resources/Gui/ToolBar/icon_credits.txt | 10 +++++ src/Resources/Gui/ToolBar/macro.png | Bin 0 -> 979 bytes src/Resources/Gui/ToolBar/movie.png | Bin 0 -> 742 bytes src/Resources/Gui/gui_resources.qrc | 2 + 8 files changed, 71 insertions(+), 3 deletions(-) create mode 100644 src/Resources/Gui/ToolBar/icon_credits.txt create mode 100644 src/Resources/Gui/ToolBar/macro.png create mode 100644 src/Resources/Gui/ToolBar/movie.png diff --git a/src/Brain/MovieRecorder.cxx b/src/Brain/MovieRecorder.cxx index eb6602835..557019098 100644 --- a/src/Brain/MovieRecorder.cxx +++ b/src/Brain/MovieRecorder.cxx @@ -81,6 +81,12 @@ MovieRecorder::addImageToMovie(const QImage* image) return; } + if (getNumberOfFrames() <= 0) { + std::cout << "Temporary Directory for movie images: " + << std::endl + << " " << m_temporaryImagesDirectory << std::endl << std::flush; + } + CaretAssert(m_tempImageSequenceNumberOfDigits > 0); /* diff --git a/src/GuiQt/BrainBrowserWindowToolBar.cxx b/src/GuiQt/BrainBrowserWindowToolBar.cxx index e631e9097..d11e0bca7 100644 --- a/src/GuiQt/BrainBrowserWindowToolBar.cxx +++ b/src/GuiQt/BrainBrowserWindowToolBar.cxx @@ -287,6 +287,38 @@ BrainBrowserWindowToolBar::BrainBrowserWindowToolBar(const int32_t browserWindow QToolButton* sceneDialogToolButton = new QToolButton(); sceneDialogToolButton->setDefaultAction(GuiManager::get()->getSceneDialogDisplayAction()); + QIcon movieIcon; + QAction* movieAction = new QAction(); + if (WuQtUtilities::loadIcon(":/ToolBar/movie.png", + movieIcon)) { + movieAction->setIcon(movieIcon); + } + else { + movieAction->setText("R"); + } + movieAction->setToolTip("Show movie recording dialog"); + QObject::connect(movieAction, &QAction::triggered, + parentBrainBrowserWindow, &BrainBrowserWindow::processMovieRecording); + QToolButton* movieToolButton = new QToolButton(); + movieToolButton->setDefaultAction(movieAction); + movieAction->setParent(movieToolButton); + + QIcon macrosIcon; + QAction* macrosAction = new QAction(); + if (WuQtUtilities::loadIcon(":/ToolBar/macro.png", + macrosIcon)) { + macrosAction->setIcon(macrosIcon); + } + else { + macrosAction->setText("M"); + } + macrosAction->setToolTip("Show macros dialog"); + QObject::connect(macrosAction, &QAction::triggered, + this, &BrainBrowserWindowToolBar::showMacroDialog); + QToolButton* macrosToolButton = new QToolButton(); + macrosToolButton->setDefaultAction(macrosAction); + macrosAction->setParent(macrosToolButton); + /* * Toolbar action and tool button at right of the tab bar */ @@ -333,7 +365,9 @@ BrainBrowserWindowToolBar::BrainBrowserWindowToolBar(const int32_t browserWindow /* * Make all tool buttons the same height */ - WuQtUtilities::matchWidgetHeights(helpDialogToolButton, + WuQtUtilities::matchWidgetHeights(macrosToolButton, + movieToolButton, + helpDialogToolButton, informationDialogToolButton, identifyDialogToolButton, sceneDialogToolButton, @@ -342,6 +376,8 @@ BrainBrowserWindowToolBar::BrainBrowserWindowToolBar(const int32_t browserWindow layersToolBoxToolButton, dataToolTipsToolButton); + WuQtUtilities::setToolButtonStyleForQt5Mac(macrosToolButton); + WuQtUtilities::setToolButtonStyleForQt5Mac(movieToolButton); WuQtUtilities::setToolButtonStyleForQt5Mac(helpDialogToolButton); WuQtUtilities::setToolButtonStyleForQt5Mac(informationDialogToolButton); WuQtUtilities::setToolButtonStyleForQt5Mac(identifyDialogToolButton); @@ -362,6 +398,8 @@ BrainBrowserWindowToolBar::BrainBrowserWindowToolBar(const int32_t browserWindow tabBarLayout->addWidget(helpDialogToolButton); tabBarLayout->addWidget(informationDialogToolButton); tabBarLayout->addWidget(identifyDialogToolButton); + tabBarLayout->addWidget(movieToolButton); + tabBarLayout->addWidget(macrosToolButton); tabBarLayout->addWidget(sceneDialogToolButton); tabBarLayout->addWidget(toolBarToolButton); tabBarLayout->addWidget(overlayToolBoxToolButton); @@ -926,6 +964,16 @@ BrainBrowserWindowToolBar::allowAddingNewTab() return false; } +/** + * Show the macro dialog + */ +void +BrainBrowserWindowToolBar::showMacroDialog() +{ + BrainBrowserWindow* bbw = GuiManager::get()->getBrowserWindowByWindowIndex(this->browserWindowIndex); + CaretAssert(bbw); + WuQMacroManager::instance()->showMacrosDialog(bbw); +} /** * Shows/hides the toolbar. diff --git a/src/GuiQt/BrainBrowserWindowToolBar.h b/src/GuiQt/BrainBrowserWindowToolBar.h index 423b9b6b9..666050ded 100644 --- a/src/GuiQt/BrainBrowserWindowToolBar.h +++ b/src/GuiQt/BrainBrowserWindowToolBar.h @@ -251,6 +251,8 @@ namespace caret { void showHideToolBar(bool showIt); + void showMacroDialog(); + private slots: void selectedTabChanged(int indx); void tabMoved(int, int); diff --git a/src/GuiQt/WuQMacroMenu.cxx b/src/GuiQt/WuQMacroMenu.cxx index 5486509e4..87ad0c78a 100644 --- a/src/GuiQt/WuQMacroMenu.cxx +++ b/src/GuiQt/WuQMacroMenu.cxx @@ -57,11 +57,11 @@ m_windowParent(windowParent) QObject::connect(m_macroDialogAction, &QAction::triggered, this, &WuQMacroMenu::macroDialogSelected); - m_recordMacroAction = addAction("Record Macro..."); + m_recordMacroAction = addAction("Start Recording New Macro..."); QObject::connect(m_recordMacroAction, &QAction::triggered, this, &WuQMacroMenu::macroRecordSelected); - m_stopMacroAction = addAction("Stop Recording Macro"); + m_stopMacroAction = addAction("Stop Recording New Macro"); QObject::connect(m_stopMacroAction, &QAction::triggered, this, &WuQMacroMenu::macroStopSelected); diff --git a/src/Resources/Gui/ToolBar/icon_credits.txt b/src/Resources/Gui/ToolBar/icon_credits.txt new file mode 100644 index 000000000..f1d0195d3 --- /dev/null +++ b/src/Resources/Gui/ToolBar/icon_credits.txt @@ -0,0 +1,10 @@ +macro.png Icon Author Credit: + +
        Icons made by Webalys Freebies from www.flaticon.com is licensed by CC 3.0 BY
        + + + +movie.png Icon: + +
        Icons made by Catalin Fertu from www.flaticon.com is licensed by CC 3.0 BY
        + diff --git a/src/Resources/Gui/ToolBar/macro.png b/src/Resources/Gui/ToolBar/macro.png new file mode 100644 index 0000000000000000000000000000000000000000..55d98e7b6f2cac69f134ea928781e3de098449cc GIT binary patch literal 979 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=fdzmUKs7M+SzC{oH>NSwWJ?9znhg z3{`3j3=J&|48MRv4KElNN(~qoUL`OvSj}Ky5HFasE6@fg!CBxDSzC zHb_`sNdc^+B->Ug!Z$#{Ilm}X!Bo#g&p^qJOF==wrYI%ND#*nRsvXF)RmvzSDX`Ml zFE20GD>v55FG|-pw6wI;H!#vSGSUUA&@HaaD@m--%_~-h7y>iLCAB!YD6^m>Ge1uO zWNu_O6mWvGGW- z%_F;~95Y?ZA75A=x;SQK!>29B-XD53JEU{sW|Iv|dFHQJ#wx=vd(Lw;Lwo%q!PNfK z0x#ZGhTl)=7Q25Y>{am{Cbf>4g{oX7RbIDvs&y_)+_Tz$zpF@NdT`g=TlFEwY?lMqEM_qnz7x6eaWVYD5QxU4~lUMAFU%WkSzvBvq-q(Sd2flNf{M6dV)~C2h z=W!+XzQyJHe(bR}IP@s{1CO1N&xGFUH;um0a$?e?a8`Sb@Hc%3)!u}^4PvFJzT3f80YjnCQ& yFYtXoY&v6+$}Zv1HP0jF{+6C}MBh<%#wS)L-RTQ>OvTMWS;y1W&t;ucLK6Uh<8z|` literal 0 HcmV?d00001 diff --git a/src/Resources/Gui/ToolBar/movie.png b/src/Resources/Gui/ToolBar/movie.png new file mode 100644 index 0000000000000000000000000000000000000000..7118cd48e889aa5dea418f3161ff5805e16337f3 GIT binary patch literal 742 zcmeAS@N?(olHy`uVBq!ia0vp^av;pX0wgC|rfC8xmUKs7M+SzC{oH>NS%G|oWRD45bDP46hOx7_4S6Fo+k-*%fF5lweBoc6VW5SkEak-ar*5QdymjWnPVT% z?={n~)X|s$ z;sg(wbiLlXcz+w;^x}0><6^7tzx+Pu`S0rc&*yyKlap+c`$FSYP)P2c()HqX9Erzl z-`1PIEA{Dja@(02`XlL1i_4EE59S{*R%;bVn|g^Yyw!F38;0FHT%W&PS$usB=j66t zuNFntxzj?vJgZ?o6d-4-5?E-swr_@*m)D`@U9PjHu6;6@pWEE%YhdU3dECdZAFbAz zbB?Q6YgX|z!^9|y4Sf5wV@}i^E#sb6Jk3bxO*H%a(t!IFEXS)dHpiiITUFu-K0G) zYxUc0w>yq}yA)yeEpd$~Nl7e8wMs5Z z1yT$~28Ncp21dF@<{?H#Rwfo!CMMbjhE@g!$9gP2NC6cwc)I$ztaD0e0ssL~DKP*5 literal 0 HcmV?d00001 diff --git a/src/Resources/Gui/gui_resources.qrc b/src/Resources/Gui/gui_resources.qrc index c9a45154b..92f718a5f 100644 --- a/src/Resources/Gui/gui_resources.qrc +++ b/src/Resources/Gui/gui_resources.qrc @@ -21,6 +21,8 @@ ./ToolBar/help.png ./ToolBar/identify.png ./ToolBar/info.png +./ToolBar/macro.png +./ToolBar/movie.png ./ToolBar/overlay_toolbox.png ./ToolBar/toolbar.png ./ToolBar/view-anterior.png -- GitLab From 3f8c5d366a49f9b96def785cb8d42377209b3128 Mon Sep 17 00:00:00 2001 From: John Harwell Date: Fri, 1 Mar 2019 11:34:25 -0600 Subject: [PATCH 211/427] Update of volume surface outline was causing an unnecessary user-interface update resulting from emission of a Qt signal when the enabled checkbox was updated. --- .../VolumeSurfaceOutlineViewController.cxx | 17 ++++++----------- src/GuiQt/VolumeSurfaceOutlineViewController.h | 2 +- 2 files changed, 7 insertions(+), 12 deletions(-) diff --git a/src/GuiQt/VolumeSurfaceOutlineViewController.cxx b/src/GuiQt/VolumeSurfaceOutlineViewController.cxx index dc84196e4..0407ffb6c 100644 --- a/src/GuiQt/VolumeSurfaceOutlineViewController.cxx +++ b/src/GuiQt/VolumeSurfaceOutlineViewController.cxx @@ -76,8 +76,8 @@ VolumeSurfaceOutlineViewController::VolumeSurfaceOutlineViewController(const Qt: this->outlineModel = NULL; this->enabledCheckBox = new QCheckBox(" "); - QObject::connect(this->enabledCheckBox, SIGNAL(stateChanged(int)), - this, SLOT(enabledCheckBoxStateChanged(int))); + QObject::connect(this->enabledCheckBox, &QCheckBox::clicked, + this, &VolumeSurfaceOutlineViewController::enabledCheckBoxChecked); this->enabledCheckBox->setToolTip("Enables display of this volume surface outline"); this->enabledCheckBox->setObjectName(objectNamePrefix + ":Enable"); @@ -188,15 +188,14 @@ VolumeSurfaceOutlineViewController::colorTabSelected(VolumeSurfaceOutlineColorOr /** * Called when enabled checkbox is selected. - * @param state + * @param checked * New state of checkbox. */ void -VolumeSurfaceOutlineViewController::enabledCheckBoxStateChanged(int state) +VolumeSurfaceOutlineViewController::enabledCheckBoxChecked(bool checked) { if (this->outlineModel != NULL) { - const bool selected = (state == Qt::Checked); - this->outlineModel->setDisplayed(selected); + this->outlineModel->setDisplayed(checked); } this->updateGraphics(); } @@ -226,11 +225,7 @@ VolumeSurfaceOutlineViewController::updateViewController(VolumeSurfaceOutlineMod this->outlineModel = outlineModel; if (this->outlineModel != NULL) { - Qt::CheckState state = Qt::Unchecked; - if (this->outlineModel->isDisplayed()) { - state = Qt::Checked; - } - this->enabledCheckBox->setCheckState(state); + this->enabledCheckBox->setChecked(this->outlineModel->isDisplayed()); this->thicknessSpinBox->blockSignals(true); float thickness = outlineModel->getThicknessPercentageViewportHeight(); diff --git a/src/GuiQt/VolumeSurfaceOutlineViewController.h b/src/GuiQt/VolumeSurfaceOutlineViewController.h index 2bd18af03..4b2401468 100644 --- a/src/GuiQt/VolumeSurfaceOutlineViewController.h +++ b/src/GuiQt/VolumeSurfaceOutlineViewController.h @@ -54,7 +54,7 @@ namespace caret { void updateViewController(VolumeSurfaceOutlineModel* outlineModel); private slots: - void enabledCheckBoxStateChanged(int); + void enabledCheckBoxChecked(bool checked); void thicknessSpinBoxValueChanged(double); -- GitLab From a79a822d356076cfa940e285b835d56276855b2c Mon Sep 17 00:00:00 2001 From: John Harwell Date: Mon, 4 Mar 2019 10:41:26 -0600 Subject: [PATCH 212/427] Rewrote surface interpolation command so that it performs interpolation using a temporary surface. This seems to fix the slight intensity change at the beginning of interpolation. --- ...croCustomOperationSurfaceInterpolation.cxx | 126 ++++++++++++------ ...MacroCustomOperationSurfaceInterpolation.h | 14 +- 2 files changed, 94 insertions(+), 46 deletions(-) diff --git a/src/GuiQt/WbMacroCustomOperationSurfaceInterpolation.cxx b/src/GuiQt/WbMacroCustomOperationSurfaceInterpolation.cxx index de71abf75..7658654f7 100644 --- a/src/GuiQt/WbMacroCustomOperationSurfaceInterpolation.cxx +++ b/src/GuiQt/WbMacroCustomOperationSurfaceInterpolation.cxx @@ -26,10 +26,15 @@ #include "BrainBrowserWindow.h" #include "BrowserTabContent.h" #include "CaretAssert.h" +#include "EventDataFileAdd.h" +#include "EventDataFileDelete.h" +#include "EventCaretDataFilesGet.h" +#include "FileInformation.h" #include "GuiManager.h" #include "MathFunctions.h" #include "ModelSurface.h" #include "ModelWholeBrain.h" +#include "SpecFile.h" #include "Surface.h" #include "WbMacroCustomDataTypeEnum.h" #include "WbMacroCustomOperationTypeEnum.h" @@ -210,8 +215,8 @@ WbMacroCustomOperationSurfaceInterpolation::executeCommand(QWidget* parent, bool WbMacroCustomOperationSurfaceInterpolation::interpolateSurface(const int32_t tabIndex, ModelWholeBrain* wholeBrainModel, - Surface* startSurface, - Surface* endSurface, + const Surface* startSurface, + const Surface* endSurface, const float durationSeconds) { CaretAssert(wholeBrainModel); @@ -225,13 +230,15 @@ WbMacroCustomOperationSurfaceInterpolation::interpolateSurface(const int32_t tab durationSeconds, numberOfSteps, iterationSleepTime); - + const StructureEnum::Enum structure = startSurface->getStructure(); - const bool endSurfaceModifiedStatus = endSurface->isModified(); + + createInterpolationSurface(startSurface); + CaretAssert(m_interpolationSurface); const float* startingXYZ = startSurface->getCoordinateData(); const float* endingXYZ = endSurface->getCoordinateData(); - + /* * XYZ that will be interpolated */ @@ -256,37 +263,17 @@ WbMacroCustomOperationSurfaceInterpolation::interpolateSurface(const int32_t tab } /* - * Replace ending surface's coordinates with starting surfaces coordinates - */ - for (int32_t i = 0; i < numberOfVertices; i++) { - const int32_t i3 = i * 3; - endSurface->setCoordinate(i, &startingXYZ[i3]); - } - endSurface->computeNormals(); - - /* - * Put ending surface into view + * Put interpolation surface into view */ wholeBrainModel->setSelectedSurfaceType(tabIndex, - endSurface->getSurfaceType()); + m_interpolationSurface->getSurfaceType()); wholeBrainModel->setSelectedSurface(structure, tabIndex, - endSurface); - - switch (structure) { - case StructureEnum::CEREBELLUM: - break; - case StructureEnum::CORTEX_LEFT: - break; - case StructureEnum::CORTEX_RIGHT: - break; - default: - CaretAssert(0); - break; - } + m_interpolationSurface); + updateUserInterface(); updateGraphics(); - + for (int iStep = 0; iStep < numberOfSteps; iStep++) { /* * Move coordinate components @@ -303,10 +290,11 @@ WbMacroCustomOperationSurfaceInterpolation::interpolateSurface(const int32_t tab */ for (int32_t i = 0; i < numberOfVertices; i++) { const int32_t i3 = i * 3; - endSurface->setCoordinate(i, + m_interpolationSurface->setCoordinate(i, &xyz[i3]); } - endSurface->computeNormals(); + m_interpolationSurface->invalidateNormals(); + m_interpolationSurface->computeNormals(); const bool debugFlag(false); if (debugFlag) { @@ -321,23 +309,73 @@ WbMacroCustomOperationSurfaceInterpolation::interpolateSurface(const int32_t tab updateGraphics(); sleepForSecondsAtEndOfIteration(iterationSleepTime); -// if ( ! recordingFlag) { -// SystemUtilities::sleepSeconds(iterationSleepTime); -// } } - + /* - * Restore end surface coordinates + * View ending surface */ - for (int32_t i = 0; i < numberOfVertices; i++) { - const int32_t i3 = i * 3; - endSurface->setCoordinate(i, &endingXYZ[i3]); - } - if ( ! endSurfaceModifiedStatus) { - endSurface->clearModified(); - } + /* + * Put interpolation surface into view + */ + wholeBrainModel->setSelectedSurfaceType(tabIndex, + endSurface->getSurfaceType()); + wholeBrainModel->setSelectedSurface(structure, + tabIndex, + const_cast(endSurface)); + updateGraphics(); + updateUserInterface(); + + deleteInterpolationSurface(); return true; } +/** + * Create the interpolation surface + * + * @param surface + * Surface that is copied to create the interpolation surface + */ +void +WbMacroCustomOperationSurfaceInterpolation::createInterpolationSurface(const Surface* surface) +{ + CaretAssert(surface); + + std::vector specFileVector = EventCaretDataFilesGet::getCaretDataFilesForType(DataFileTypeEnum::SPECIFICATION); + if ( ! specFileVector.empty()) { + m_specFile = dynamic_cast(specFileVector[0]); + m_specFileModificationStatus = m_specFile->isModified(); + } + + m_interpolationSurface = new Surface(*surface); + CaretAssert(m_interpolationSurface); + + FileInformation fileInfo(m_interpolationSurface->getFileName()); + AString path, name, ext; + fileInfo.getFileComponents(path, name, ext); + const AString newName = FileInformation::assembleFileComponents(path, "Interpolation", ext); + m_interpolationSurface->setFileName(newName); + + EventDataFileAdd addSurfaceEvent(m_interpolationSurface); + EventManager::get()->sendEvent(addSurfaceEvent.getPointer()); +} + +/** + * Delete the interpolation surface + */ +void +WbMacroCustomOperationSurfaceInterpolation::deleteInterpolationSurface() +{ + if (m_interpolationSurface != NULL) { + EventDataFileDelete deleteFileEvent(m_interpolationSurface); + EventManager::get()->sendEvent(deleteFileEvent.getPointer()); + } + + if (m_specFile != NULL) { + if ( ! m_specFileModificationStatus) { + m_specFile->clearModified(); + } + } +} + diff --git a/src/GuiQt/WbMacroCustomOperationSurfaceInterpolation.h b/src/GuiQt/WbMacroCustomOperationSurfaceInterpolation.h index 4c185261e..67fe52ae7 100644 --- a/src/GuiQt/WbMacroCustomOperationSurfaceInterpolation.h +++ b/src/GuiQt/WbMacroCustomOperationSurfaceInterpolation.h @@ -31,6 +31,7 @@ namespace caret { class ModelSurface; class ModelWholeBrain; + class SpecFile; class Surface; class WbMacroCustomOperationSurfaceInterpolation : public WbMacroCustomOperationBase { @@ -56,10 +57,19 @@ namespace caret { private: bool interpolateSurface(const int32_t tabIndex, ModelWholeBrain* wholeBrainModel, - Surface* startSurface, - Surface* endSurface, + const Surface* startSurface, + const Surface* endSurface, const float durationSeconds); + void createInterpolationSurface(const Surface* surface); + + void deleteInterpolationSurface(); + + Surface* m_interpolationSurface = NULL; + + SpecFile* m_specFile = NULL; + + bool m_specFileModificationStatus = false; // ADD_NEW_MEMBERS_HERE -- GitLab From e5975faf1d54501aa0faa6584bc7ea24f1d9ee2c Mon Sep 17 00:00:00 2001 From: John Harwell Date: Mon, 4 Mar 2019 11:51:44 -0600 Subject: [PATCH 213/427] Update macro and movie dialogs so that their positions and sizes are preserved when they are reopened in the same session. --- src/GuiQt/GuiManager.cxx | 1 + src/GuiQt/MovieRecordingDialog.cxx | 22 ++++++++++++++++++++++ src/GuiQt/MovieRecordingDialog.h | 7 ++++++- src/GuiQt/WuQMacroDialog.cxx | 23 +++++++++++++++++++++++ src/GuiQt/WuQMacroDialog.h | 9 ++++++++- src/GuiQt/WuQMacroManager.cxx | 1 + 6 files changed, 61 insertions(+), 2 deletions(-) diff --git a/src/GuiQt/GuiManager.cxx b/src/GuiQt/GuiManager.cxx index 239b3a97b..a75aad235 100644 --- a/src/GuiQt/GuiManager.cxx +++ b/src/GuiQt/GuiManager.cxx @@ -2407,6 +2407,7 @@ GuiManager::processShowMovieRecordingDialog(BrainBrowserWindow* browserWindow) m_movieRecordingDialog->updateDialog(); m_movieRecordingDialog->setBrowserWindowIndex(browserWindow->getBrowserWindowIndex()); m_movieRecordingDialog->showDialog(); + m_movieRecordingDialog->restorePositionAndSize(); } /** diff --git a/src/GuiQt/MovieRecordingDialog.cxx b/src/GuiQt/MovieRecordingDialog.cxx index 8e645f7bf..17952ca4c 100644 --- a/src/GuiQt/MovieRecordingDialog.cxx +++ b/src/GuiQt/MovieRecordingDialog.cxx @@ -93,6 +93,28 @@ MovieRecordingDialog::~MovieRecordingDialog() EventManager::get()->removeAllEventsFromListener(this); } +/** + * Called when close event is issuedf + * + * @param event + * The close event + */ +void +MovieRecordingDialog::closeEvent(QCloseEvent* event) +{ + s_previousDialogGeometry = saveGeometry(); + + WuQDialogNonModal::closeEvent(event); +} + +void +MovieRecordingDialog::restorePositionAndSize() +{ + if ( ! s_previousDialogGeometry.isEmpty()) { + restoreGeometry(s_previousDialogGeometry); + } +} + /** * Receive an event. * diff --git a/src/GuiQt/MovieRecordingDialog.h b/src/GuiQt/MovieRecordingDialog.h index 1682a1537..32338355b 100644 --- a/src/GuiQt/MovieRecordingDialog.h +++ b/src/GuiQt/MovieRecordingDialog.h @@ -58,6 +58,7 @@ namespace caret { void updateDialog(); + void restorePositionAndSize(); // ADD_NEW_METHODS_HERE @@ -88,6 +89,9 @@ namespace caret { void manualCaptureSecondsSpinBoxValueChanged(int seconds); + protected: + virtual void closeEvent(QCloseEvent* event) override; + private: void updateFileNameLabel(); @@ -133,12 +137,13 @@ namespace caret { EnumComboBoxTemplate* m_movieRecorderCaptureRegionTypeComboBox; + static QByteArray s_previousDialogGeometry; // ADD_NEW_MEMBERS_HERE }; #ifdef __MOVIE_RECORDING_DIALOG_DECLARE__ - // + QByteArray MovieRecordingDialog::s_previousDialogGeometry; #endif // __MOVIE_RECORDING_DIALOG_DECLARE__ } // namespace diff --git a/src/GuiQt/WuQMacroDialog.cxx b/src/GuiQt/WuQMacroDialog.cxx index cad4ede01..861464114 100644 --- a/src/GuiQt/WuQMacroDialog.cxx +++ b/src/GuiQt/WuQMacroDialog.cxx @@ -169,6 +169,29 @@ WuQMacroDialog::~WuQMacroDialog() { } +/** + * Called when close event is issuedf + * + * @param event + * The close event + */ +void +WuQMacroDialog::closeEvent(QCloseEvent* event) +{ + s_previousDialogGeometry = saveGeometry(); + + QDialog::closeEvent(event); +} + +void +WuQMacroDialog::restorePositionAndSize() +{ + if ( ! s_previousDialogGeometry.isEmpty()) { + restoreGeometry(s_previousDialogGeometry); + } +} + + /** * @return Widget with run and editing buttons */ diff --git a/src/GuiQt/WuQMacroDialog.h b/src/GuiQt/WuQMacroDialog.h index 81fdcf0d2..7e64ae415 100644 --- a/src/GuiQt/WuQMacroDialog.h +++ b/src/GuiQt/WuQMacroDialog.h @@ -68,6 +68,8 @@ namespace caret { void updateDialogContents(); + void restorePositionAndSize(); + // ADD_NEW_METHODS_HERE private slots: @@ -119,6 +121,9 @@ namespace caret { void selectionModelRowChanged(const QModelIndex& current, const QModelIndex& previous); + protected: + virtual void closeEvent(QCloseEvent* event) override; + private: enum class ValueIndex { ONE, @@ -229,6 +234,8 @@ namespace caret { bool m_blockSelectionModelRowChangedFlag = false; + static QByteArray s_previousDialogGeometry; + // ADD_NEW_MEMBERS_HERE }; @@ -261,7 +268,7 @@ namespace caret { #ifdef __WU_Q_MACRO_DIALOG_DECLARE__ - // + QByteArray WuQMacroDialog::s_previousDialogGeometry; #endif // __WU_Q_MACRO_DIALOG_DECLARE__ } // namespace diff --git a/src/GuiQt/WuQMacroManager.cxx b/src/GuiQt/WuQMacroManager.cxx index b1dfce9cd..d2824d3ab 100644 --- a/src/GuiQt/WuQMacroManager.cxx +++ b/src/GuiQt/WuQMacroManager.cxx @@ -480,6 +480,7 @@ WuQMacroManager::showMacrosDialog(QWidget* parent) m_macrosDialog->updateDialogContents(); m_macrosDialog->show(); m_macrosDialog->raise(); + m_macrosDialog->restorePositionAndSize(); } /** -- GitLab From 1a67af4fdd5d48cacd2a4ffc0e5d40deb929ad8f Mon Sep 17 00:00:00 2001 From: John Harwell Date: Mon, 4 Mar 2019 14:50:21 -0600 Subject: [PATCH 214/427] Use full path of movie filename in dialogs --- src/GuiQt/MovieRecordingDialog.cxx | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/GuiQt/MovieRecordingDialog.cxx b/src/GuiQt/MovieRecordingDialog.cxx index 17952ca4c..ced93d6ab 100644 --- a/src/GuiQt/MovieRecordingDialog.cxx +++ b/src/GuiQt/MovieRecordingDialog.cxx @@ -444,9 +444,10 @@ MovieRecordingDialog::createMoviePushButtonClicked() { MovieRecorder* movieRecorder = SessionManager::get()->getMovieRecorder(); FileInformation fileInfo(movieRecorder->getMovieFileName()); + const QString name(fileInfo.getCanonicalFilePath()); if (fileInfo.exists()) { AString msg("Movie file \"" - + fileInfo.getFileName() + + name + "\" exists, overwrite?"); if ( ! WuQMessageBox::warningOkCancel(m_createMoviePushButton, msg)) { @@ -454,7 +455,7 @@ MovieRecordingDialog::createMoviePushButtonClicked() } if ( ! fileInfo.remove()) { AString msg("Unable to remove movie file \"" - + fileInfo.getFileName() + + name + "\""); WuQMessageBox::errorOk(m_createMoviePushButton, msg); -- GitLab From e184cdd8e18eb37a1b754cb239d2a3d9e95dc92e Mon Sep 17 00:00:00 2001 From: John Harwell Date: Tue, 5 Mar 2019 12:01:46 -0600 Subject: [PATCH 215/427] Removed DeveloperFlagEnum::DEVELOPER_FLAG_ALL_VIEW_SURFACE_MATCH_MODE. Replaced it with "Match" checkbox in Toolbar --> Surface Viewing. When checked, non-anatomical surfaces are matched to position and size of the primary anatomical surface; ALL drawing viewport is sized with primary anatomical surface; surface separation is ignored. This "Match" option is useful when for animation (surface interpolation) and movie recording. --- src/Brain/Brain.cxx | 31 +++++++ src/Brain/Brain.h | 6 ++ src/Brain/BrainOpenGLFixedPipeline.cxx | 5 +- src/Brain/BrainStructure.cxx | 49 ++--------- src/Brain/BrainStructure.h | 2 +- src/Common/DeveloperFlagsEnum.cxx | 5 -- src/Common/DeveloperFlagsEnum.h | 3 +- src/Files/SurfaceFile.cxx | 109 +++++++++++++++++++++--- src/Files/SurfaceFile.h | 6 ++ src/GuiQt/BrainBrowserWindow.cxx | 13 --- src/GuiQt/BrainBrowserWindowToolBar.cxx | 73 +++++++++------- src/GuiQt/BrainBrowserWindowToolBar.h | 2 + 12 files changed, 192 insertions(+), 112 deletions(-) diff --git a/src/Brain/Brain.cxx b/src/Brain/Brain.cxx index cd27e5406..e5a8e0c92 100644 --- a/src/Brain/Brain.cxx +++ b/src/Brain/Brain.cxx @@ -184,6 +184,8 @@ Brain::Brain(const CaretPreferences* caretPreferences) m_displayPropertiesVolume = new DisplayPropertiesVolume(); m_displayProperties.push_back(m_displayPropertiesVolume); + m_surfaceMatchingToAnatomicalFlag = false; + EventManager::get()->addEventListener(this, EventTypeEnum::EVENT_CARET_DATA_FILES_GET); EventManager::get()->addEventListener(this, @@ -253,6 +255,8 @@ Brain::Brain(const CaretPreferences* caretPreferences) m_sceneAssistant->add("m_gapsAndMargins", "GapsAndMargins", m_gapsAndMargins); + m_sceneAssistant->add("m_surfaceMatchingToAnatomicalFlag", + &m_surfaceMatchingToAnatomicalFlag); m_selectionManager = new SelectionManager(); @@ -497,6 +501,8 @@ Brain::resetBrain(const ResetBrainKeepSceneFiles keepSceneFiles, m_isSpecFileBeingRead = false; m_activeScene = NULL; + m_surfaceMatchingToAnatomicalFlag = false; + /* * Clear the counters used to prevent duplicate file names. */ @@ -7588,6 +7594,8 @@ Brain::restoreFromScene(const SceneAttributes* sceneAttributes, m_sceneAnnotationFile->clearModified(); EventManager::get()->sendEvent(EventAnnotationTextSubstitutionInvalidate().getPointer()); + + setSurfaceMatchingToAnatomical(m_surfaceMatchingToAnatomicalFlag); } /** @@ -7723,5 +7731,28 @@ Brain::getFiberOrientationSphericalSamplesVectors(std::vectormatchSurfacesToPrimaryAnatomical(m_surfaceMatchingToAnatomicalFlag); + } +} + + diff --git a/src/Brain/Brain.h b/src/Brain/Brain.h index 2e6e29941..233092112 100644 --- a/src/Brain/Brain.h +++ b/src/Brain/Brain.h @@ -437,6 +437,10 @@ namespace caret { const GapsAndMargins* getGapsAndMargins() const; + bool isSurfaceMatchingToAnatomical() const; + + void setSurfaceMatchingToAnatomical(const bool matchStatus); + private: /** * Reset the brain scene file mode @@ -871,6 +875,8 @@ namespace caret { GapsAndMargins* m_gapsAndMargins; Scene* m_activeScene = NULL; + + bool m_surfaceMatchingToAnatomicalFlag = false; }; } // namespace diff --git a/src/Brain/BrainOpenGLFixedPipeline.cxx b/src/Brain/BrainOpenGLFixedPipeline.cxx index e89f1ea23..a72eddd8c 100644 --- a/src/Brain/BrainOpenGLFixedPipeline.cxx +++ b/src/Brain/BrainOpenGLFixedPipeline.cxx @@ -5212,8 +5212,7 @@ BrainOpenGLFixedPipeline::drawWholeBrainModel(BrowserTabContent* browserTabConte Surface* rightSurface = wholeBrainModel->getSelectedSurface(StructureEnum::CORTEX_RIGHT, tabNumberIndex); - if (DeveloperFlagsEnum::isFlag(DeveloperFlagsEnum::DEVELOPER_FLAG_ALL_VIEW_SURFACE_MATCH_MODE)) { - /* 2/22/19 ALL SURFACES SAME VIEWPORT */ + if (m_brain->isSurfaceMatchingToAnatomical()) { /* * Use the primary anatomical surface for sizing any surface in the same * structure so that size of viewport is the same. Otherwise, the viewport @@ -5417,7 +5416,7 @@ BrainOpenGLFixedPipeline::drawWholeBrainModel(BrowserTabContent* browserTabConte * When only one surface structure is displayed, disable offset of surfaces */ bool allowLeftRightSeparationFlag(true); - if (DeveloperFlagsEnum::isFlag(DeveloperFlagsEnum::DEVELOPER_FLAG_ALL_VIEW_SURFACE_MATCH_MODE)) { + if (m_brain->isSurfaceMatchingToAnatomical()) { if (uniqueStructuresToDraw.size() == 1) { allowLeftRightSeparationFlag = false; } diff --git a/src/Brain/BrainStructure.cxx b/src/Brain/BrainStructure.cxx index 60b88727b..700b053aa 100644 --- a/src/Brain/BrainStructure.cxx +++ b/src/Brain/BrainStructure.cxx @@ -1528,9 +1528,12 @@ BrainStructure::restoreFromScene(const SceneAttributes* sceneAttributes, /** * Match surface sizes to the primary anatomical surface + * + * @param matchStatus + * The match status */ void -BrainStructure::matchSurfacesToPrimaryAnatomical() +BrainStructure::matchSurfacesToPrimaryAnatomical(const bool matchStatus) { const Surface* primaryAnatomical = getPrimaryAnatomicalSurface(); if (primaryAnatomical == NULL) { @@ -1539,48 +1542,8 @@ BrainStructure::matchSurfacesToPrimaryAnatomical() for (auto s : m_surfaces) { if (s != primaryAnatomical) { - bool sphereMatchFlag(false); - bool matchFlag(false); - switch (s->getSurfaceType()) { - case SurfaceTypeEnum::ANATOMICAL: - break; - case SurfaceTypeEnum::ELLIPSOID: - break; - case SurfaceTypeEnum::FLAT: - matchFlag = true; - break; - case SurfaceTypeEnum::HULL: - break; - case SurfaceTypeEnum::INFLATED: - matchFlag = true; - break; - case SurfaceTypeEnum::RECONSTRUCTION: - break; - case SurfaceTypeEnum::SEMI_SPHERICAL: - break; - case SurfaceTypeEnum::SPHERICAL: - sphereMatchFlag = true; - break; - case SurfaceTypeEnum::UNKNOWN: - break; - case SurfaceTypeEnum::VERY_INFLATED: - matchFlag = true; - break; - } - - const bool modStatus(s->isModified()); - if (sphereMatchFlag) { - s->matchSphereToSurface(primaryAnatomical); - if ( ! modStatus) { - s->clearModified(); - } - } - else if (matchFlag) { - s->matchSurfaceBoundingBox(primaryAnatomical); - if ( ! modStatus) { - s->clearModified(); - } - } + s->matchToAnatomicalSurface(primaryAnatomical, + matchStatus); } } } diff --git a/src/Brain/BrainStructure.h b/src/Brain/BrainStructure.h index b8f3f938d..a6578ece2 100644 --- a/src/Brain/BrainStructure.h +++ b/src/Brain/BrainStructure.h @@ -166,7 +166,7 @@ namespace caret { void initializeOverlays(); - void matchSurfacesToPrimaryAnatomical(); + void matchSurfacesToPrimaryAnatomical(const bool matchStatus); private: const Surface* getPrimaryAnatomicalSurfacePrivate() const; diff --git a/src/Common/DeveloperFlagsEnum.cxx b/src/Common/DeveloperFlagsEnum.cxx index b3a2eb490..c8667b697 100644 --- a/src/Common/DeveloperFlagsEnum.cxx +++ b/src/Common/DeveloperFlagsEnum.cxx @@ -129,11 +129,6 @@ DeveloperFlagsEnum::initialize() "Use New Scene File Reader and Writer", CheckableEnum::YES, true)); - checkableItems.push_back(DeveloperFlagsEnum(DEVELOPER_FLAG_ALL_VIEW_SURFACE_MATCH_MODE, - "DEVELOPER_FLAG_ALL_VIEW_SURFACE_MATCH_MODE", - "Setup All View Surface Matching for Movie Recording", - CheckableEnum::YES, - false)); std::vector notCheckableItems; notCheckableItems.push_back(DeveloperFlagsEnum(DEVELOPER_FLAG_TEST_SCENE_FILE_READ_WRITE, diff --git a/src/Common/DeveloperFlagsEnum.h b/src/Common/DeveloperFlagsEnum.h index 30ab30cef..63a32fa3d 100644 --- a/src/Common/DeveloperFlagsEnum.h +++ b/src/Common/DeveloperFlagsEnum.h @@ -40,8 +40,7 @@ public: DEVELOPER_FLAG_NEW_SCENE_FILE_READ_WRITE, DEVELOPER_FLAG_TEST_SCENE_FILE_READ_WRITE, DEVELOPER_FLAG_TIME_SCENE_FILE_READ_WRITE, - DEVELOPER_FLAG_TEST_ALL_SCENE_FILES_IN_DIRECTORY, - DEVELOPER_FLAG_ALL_VIEW_SURFACE_MATCH_MODE + DEVELOPER_FLAG_TEST_ALL_SCENE_FILES_IN_DIRECTORY }; ~DeveloperFlagsEnum(); diff --git a/src/Files/SurfaceFile.cxx b/src/Files/SurfaceFile.cxx index 15ae86916..e20142806 100644 --- a/src/Files/SurfaceFile.cxx +++ b/src/Files/SurfaceFile.cxx @@ -18,6 +18,7 @@ */ /*LICENSE_END*/ +#include #include #include #include @@ -929,6 +930,100 @@ SurfaceFile::getBoundingBox() const return this->boundingBox; } +/** + * Match the surface to the given anatomical surface. If this + * surface is an anatomical or raw surface, no action is taken. + * + * @param anatomicalSurfaceFile + * Match to this anatomical surface file. + * @param matchStatus + * The match status + */ +void +SurfaceFile::matchToAnatomicalSurface(const SurfaceFile* anatomicalSurfaceFile, + const bool matchStatus) +{ + CaretAssert(anatomicalSurfaceFile); + if (anatomicalSurfaceFile == NULL) { + return; + } + if (this == anatomicalSurfaceFile) { + return; + } + + if (matchStatus) { + bool sphereMatchFlag(false); + bool matchFlag(false); + switch (getSurfaceType()) { + case SurfaceTypeEnum::ANATOMICAL: + break; + case SurfaceTypeEnum::ELLIPSOID: + break; + case SurfaceTypeEnum::FLAT: + matchFlag = true; + break; + case SurfaceTypeEnum::HULL: + break; + case SurfaceTypeEnum::INFLATED: + matchFlag = true; + break; + case SurfaceTypeEnum::RECONSTRUCTION: + break; + case SurfaceTypeEnum::SEMI_SPHERICAL: + break; + case SurfaceTypeEnum::SPHERICAL: + sphereMatchFlag = true; + break; + case SurfaceTypeEnum::UNKNOWN: + break; + case SurfaceTypeEnum::VERY_INFLATED: + matchFlag = true; + break; + } + + if (matchFlag + || sphereMatchFlag) { + /* + * Save the unmatched coordinates + */ + const int32_t numXYZ = getNumberOfNodes() * 3; + m_unmatchedCoordinates.resize(numXYZ); + CaretAssert(this->coordinatePointer); + std::copy_n(this->coordinatePointer, + numXYZ, + m_unmatchedCoordinates.begin()); + + const bool modStatus(isModified()); + if (sphereMatchFlag) { + matchSphereToSurface(anatomicalSurfaceFile); + if ( ! modStatus) { + clearModified(); + } + } + else if (matchFlag) { + matchSurfaceBoundingBox(anatomicalSurfaceFile); + if ( ! modStatus) { + clearModified(); + } + } + } + } + else { + /* + * Restore the unmatched coordinates + */ + const int32_t numXYZ = getNumberOfNodes() * 3; + if (static_cast(m_unmatchedCoordinates.size()) == numXYZ) { + CaretAssert(this->coordinatePointer); + std::copy_n(m_unmatchedCoordinates.begin(), + numXYZ, + this->coordinatePointer); + } + m_unmatchedCoordinates.clear(); + } +} + + /** * Match a sphere to the given surface while retaining the spherical * shape. The center of gravity of the sphere will be the same as the center of gravity of @@ -970,9 +1065,6 @@ SurfaceFile::matchSphereToSurface(const SurfaceFile* matchSurfaceFile) } } newRadius = std::sqrt(newRadius); -// std::cout << "Structure: " << StructureEnum::toGuiName(getStructure()) << std::endl; -// std::cout << " Old radius: " << oldRadius << " New radius: " << newRadius << std::endl; -// std::cout << " Match furthest vertex: " << furthestVertex << std::endl; float myCOG[3]; getCenterOfGravity(myCOG); @@ -991,17 +1083,6 @@ SurfaceFile::matchSphereToSurface(const SurfaceFile* matchSurfaceFile) scale, scale); -// std::cout << " Sphere COG: " << myCOG[0] << ", " << myCOG[1] << ", " << myCOG[2] << std::endl; -// std::cout << " Move to COG: " << matchCOG[0] << ", " << matchCOG[1] << ", " << matchCOG[2] << std::endl; - /* - * Translate to match surface center-of-gravity - */ -// DO NOT as is offsets sphere posterior of anatomical as anatomical COG-Y is about -20 -// matrix.translate(matchCOG[0], -// matchCOG[1], -// matchCOG[2]); - - applyMatrix(matrix); } diff --git a/src/Files/SurfaceFile.h b/src/Files/SurfaceFile.h index f871dc1a3..7347a1866 100644 --- a/src/Files/SurfaceFile.h +++ b/src/Files/SurfaceFile.h @@ -149,6 +149,9 @@ namespace caret { void matchSphereToSurface(const SurfaceFile* surfaceFile); + void matchToAnatomicalSurface(const SurfaceFile* anatomicalSurfaceFile, + const bool matchStatus); + void getCenterOfGravity(float cogOut[3]) const; void applyMatrix(const Matrix4x4& matrix); @@ -265,6 +268,9 @@ namespace caret { /** surface normal vectors. */ std::vector normalVectors; + /** Coordinates before matching surface to anatomical */ + std::vector m_unmatchedCoordinates; + bool m_normalsComputed; bool m_skipSanityCheck; diff --git a/src/GuiQt/BrainBrowserWindow.cxx b/src/GuiQt/BrainBrowserWindow.cxx index 5fdc01617..a8361f505 100644 --- a/src/GuiQt/BrainBrowserWindow.cxx +++ b/src/GuiQt/BrainBrowserWindow.cxx @@ -1721,19 +1721,6 @@ BrainBrowserWindow::developerMenuFlagTriggered(QAction* action) false); } } - else if (enumValue == DeveloperFlagsEnum::DEVELOPER_FLAG_ALL_VIEW_SURFACE_MATCH_MODE) { - if (DeveloperFlagsEnum::isFlag(DeveloperFlagsEnum::DEVELOPER_FLAG_ALL_VIEW_SURFACE_MATCH_MODE)) { - Brain* brain = GuiManager::get()->getBrain(); - const int32_t numBrainStructures = brain->getNumberOfBrainStructures(); - if (numBrainStructures <= 0) { - return; - } - for (int32_t i = 0; i < numBrainStructures; i++) { - BrainStructure* bs = brain->getBrainStructure(i); - bs->matchSurfacesToPrimaryAnatomical(); - } - } - } /* * Update graphics and GUI diff --git a/src/GuiQt/BrainBrowserWindowToolBar.cxx b/src/GuiQt/BrainBrowserWindowToolBar.cxx index d11e0bca7..3b13da2ca 100644 --- a/src/GuiQt/BrainBrowserWindowToolBar.cxx +++ b/src/GuiQt/BrainBrowserWindowToolBar.cxx @@ -2727,43 +2727,35 @@ BrainBrowserWindowToolBar::createWholeBrainSurfaceOptionsWidget() macroManager->addMacroSupportToObject(this->wholeBrainSurfaceSeparationCerebellumSpinBox, "Set all view cerebral/cerebellum separation"); - + this->wholeBrainSurfaceMatchCheckBox = new QCheckBox("Match"); + WuQtUtilities::setToolTipAndStatusTip(this->wholeBrainSurfaceMatchCheckBox, + "Match position and size of all surfaces to primary anatomical. Useful for " + "animation (surface interpolation) and recording movies."); + QObject::connect(this->wholeBrainSurfaceMatchCheckBox, &QCheckBox::clicked, + this, &BrainBrowserWindowToolBar::wholeBrainSurfaceMatchCheckBoxClicked); + this->wholeBrainSurfaceMatchCheckBox->setObjectName(objectNamePrefix + + "MatchSurface"); + macroManager->addMacroSupportToObject(this->wholeBrainSurfaceMatchCheckBox, + "Match position and size of all surfaces to primary anatomical"); - QLabel* columnTwoSpaceLabel = new QLabel(" "); wholeBrainLeftSurfaceToolButton->setText("L"); wholeBrainRightSurfaceToolButton->setText("R"); wholeBrainCerebellumSurfaceToolButton->setText("C"); - bool originalFlag = false; QGridLayout* gridLayout = new QGridLayout(); - if (originalFlag) { - gridLayout->setVerticalSpacing(2); - gridLayout->setHorizontalSpacing(2); - gridLayout->addWidget(this->wholeBrainSurfaceTypeComboBox, 0, 0, 1, 6); - gridLayout->addWidget(this->wholeBrainSurfaceLeftCheckBox, 1, 0); - gridLayout->addWidget(wholeBrainLeftSurfaceToolButton, 1, 1); - gridLayout->addWidget(columnTwoSpaceLabel, 1, 2); - gridLayout->addWidget(this->wholeBrainSurfaceRightCheckBox, 1, 3); - gridLayout->addWidget(wholeBrainRightSurfaceToolButton, 1, 4); - gridLayout->addWidget(this->wholeBrainSurfaceSeparationLeftRightSpinBox, 1, 5); - gridLayout->addWidget(this->wholeBrainSurfaceCerebellumCheckBox, 2, 0); - gridLayout->addWidget(wholeBrainCerebellumSurfaceToolButton, 2, 1); - gridLayout->addWidget(this->wholeBrainSurfaceSeparationCerebellumSpinBox, 2, 5); - } - else { - gridLayout->setVerticalSpacing(2); - gridLayout->setHorizontalSpacing(2); - gridLayout->addWidget(this->wholeBrainSurfaceTypeComboBox, 0, 0, 1, 6); - gridLayout->addWidget(this->wholeBrainSurfaceLeftCheckBox, 1, 0); - gridLayout->addWidget(wholeBrainLeftSurfaceToolButton, 1, 1); - gridLayout->addWidget(this->wholeBrainSurfaceRightCheckBox, 2, 0); - gridLayout->addWidget(wholeBrainRightSurfaceToolButton, 2, 1); - gridLayout->addWidget(this->wholeBrainSurfaceCerebellumCheckBox, 3, 0); - gridLayout->addWidget(wholeBrainCerebellumSurfaceToolButton, 3, 1); - gridLayout->addWidget(this->wholeBrainSurfaceSeparationLeftRightSpinBox, 1, 2, 2, 1); - gridLayout->addWidget(this->wholeBrainSurfaceSeparationCerebellumSpinBox, 3, 2); - } - + gridLayout->setVerticalSpacing(2); + gridLayout->setHorizontalSpacing(2); + gridLayout->addWidget(this->wholeBrainSurfaceTypeComboBox, 0, 0, 1, 6); + gridLayout->addWidget(this->wholeBrainSurfaceLeftCheckBox, 1, 0); + gridLayout->addWidget(wholeBrainLeftSurfaceToolButton, 1, 1); + gridLayout->addWidget(this->wholeBrainSurfaceRightCheckBox, 2, 0); + gridLayout->addWidget(wholeBrainRightSurfaceToolButton, 2, 1); + gridLayout->addWidget(this->wholeBrainSurfaceCerebellumCheckBox, 3, 0); + gridLayout->addWidget(wholeBrainCerebellumSurfaceToolButton, 3, 1); + gridLayout->addWidget(this->wholeBrainSurfaceSeparationLeftRightSpinBox, 1, 2, 2, 1); + gridLayout->addWidget(this->wholeBrainSurfaceSeparationCerebellumSpinBox, 3, 2); + gridLayout->addWidget(this->wholeBrainSurfaceMatchCheckBox, 4, 0, 1, 6); + QWidget* widget = new QWidget(); QVBoxLayout* layout = new QVBoxLayout(widget); WuQtUtilities::setLayoutSpacingAndMargins(layout, 0, 0); @@ -2779,6 +2771,7 @@ BrainBrowserWindowToolBar::createWholeBrainSurfaceOptionsWidget() this->wholeBrainSurfaceOptionsWidgetGroup->add(wholeBrainCerebellumSurfaceToolButton); this->wholeBrainSurfaceOptionsWidgetGroup->add(this->wholeBrainSurfaceSeparationLeftRightSpinBox); this->wholeBrainSurfaceOptionsWidgetGroup->add(this->wholeBrainSurfaceSeparationCerebellumSpinBox); + this->wholeBrainSurfaceOptionsWidgetGroup->add(this->wholeBrainSurfaceMatchCheckBox); QWidget* w = this->createToolWidget("Surface Viewing", widget, @@ -2838,6 +2831,7 @@ BrainBrowserWindowToolBar::updateWholeBrainSurfaceOptionsWidget(BrowserTabConten this->wholeBrainSurfaceSeparationLeftRightSpinBox->setValue(browserTabContent->getWholeBrainLeftRightSeparation()); this->wholeBrainSurfaceSeparationCerebellumSpinBox->setValue(browserTabContent->getWholeBrainCerebellumSeparation()); + this->wholeBrainSurfaceMatchCheckBox->setChecked(wholeBrainModel->getBrain()->isSurfaceMatchingToAnatomical()); this->wholeBrainSurfaceOptionsWidgetGroup->blockAllSignals(false); } @@ -4379,6 +4373,23 @@ BrainBrowserWindowToolBar::wholeBrainSurfaceSeparationCerebellumSpinBoxSelected( this->updateGraphicsWindowAndYokedWindows(); } +/** + * Called when match check box is clicked + * + * @param checked + * New checked status. + */ +void +BrainBrowserWindowToolBar::wholeBrainSurfaceMatchCheckBoxClicked(bool checked) +{ + Brain* brain = GuiManager::get()->getBrain(); + CaretAssert(brain); + + brain->setSurfaceMatchingToAnatomical(checked); + this->updateGraphicsWindowAndYokedWindows(); + this->updateUserInterface(); +} + /** * Called when a single surface control is changed. * @param structure diff --git a/src/GuiQt/BrainBrowserWindowToolBar.h b/src/GuiQt/BrainBrowserWindowToolBar.h index 666050ded..0f4f3aa44 100644 --- a/src/GuiQt/BrainBrowserWindowToolBar.h +++ b/src/GuiQt/BrainBrowserWindowToolBar.h @@ -357,6 +357,7 @@ namespace caret { QMenu* wholeBrainSurfaceCerebellumMenu; QDoubleSpinBox* wholeBrainSurfaceSeparationLeftRightSpinBox; QDoubleSpinBox* wholeBrainSurfaceSeparationCerebellumSpinBox; + QCheckBox* wholeBrainSurfaceMatchCheckBox; void updateAllWholeBrainSurfaceMenus(); void updateWholeBrainSurfaceMenu(QMenu* menu, const StructureEnum::Enum structure); @@ -371,6 +372,7 @@ namespace caret { void wholeBrainSurfaceLeftToolButtonTriggered(bool checked); void wholeBrainSurfaceRightToolButtonTriggered(bool checked); void wholeBrainSurfaceCerebellumToolButtonTriggered(bool checked); + void wholeBrainSurfaceMatchCheckBoxClicked(bool checked); void wholeBrainSurfaceLeftMenuTriggered(QAction*); void wholeBrainSurfaceRightMenuTriggered(QAction*); -- GitLab From b9e0d11a7d3446c50aca58995d11b36ff10c5079 Mon Sep 17 00:00:00 2001 From: John Harwell Date: Tue, 5 Mar 2019 14:12:30 -0600 Subject: [PATCH 216/427] If there is an active scene with macros, copy the macros into a new scene (add, insert, or replace). --- src/Files/SceneFile.cxx | 5 ----- src/GuiQt/SceneCreateReplaceDialog.cxx | 11 +++++++++++ src/Scenes/Scene.cxx | 19 +++++++++++++++++++ src/Scenes/Scene.h | 2 ++ 4 files changed, 32 insertions(+), 5 deletions(-) diff --git a/src/Files/SceneFile.cxx b/src/Files/SceneFile.cxx index 96d8e1b02..5dbe64a08 100644 --- a/src/Files/SceneFile.cxx +++ b/src/Files/SceneFile.cxx @@ -202,11 +202,6 @@ SceneFile::replaceScene(Scene* newScene, const int32_t numScenes = getNumberOfScenes(); for (int32_t i = 0; i < numScenes; i++) { if (m_scenes[i] == sceneThatIsReplacedAndDeleted) { - /* - * Preserve macros by moving to new scene - */ - newScene->moveMacrosFromScene(sceneThatIsReplacedAndDeleted); - delete m_scenes[i]; m_scenes[i] = newScene; setModified(); diff --git a/src/GuiQt/SceneCreateReplaceDialog.cxx b/src/GuiQt/SceneCreateReplaceDialog.cxx index 4c0bb9944..e38522e60 100644 --- a/src/GuiQt/SceneCreateReplaceDialog.cxx +++ b/src/GuiQt/SceneCreateReplaceDialog.cxx @@ -45,6 +45,7 @@ #include "EventBrowserTabGetAll.h" #include "EventBrowserTabGetAllViewed.h" #include "EventImageCapture.h" +#include "EventSceneActive.h" #include "EventManager.h" #include "GuiManager.h" #include "ImageFile.h" @@ -701,6 +702,16 @@ SceneCreateReplaceDialog::okButtonClicked() imageErrorMessage); } + /* + * Copy macros from active scene to the new scene + */ + EventSceneActive activeSceneEvent(EventSceneActive::MODE_GET); + EventManager::get()->sendEvent(activeSceneEvent.getPointer()); + const Scene* activeScene = activeSceneEvent.getScene(); + if (activeScene != NULL) { + newScene->copyMacrosFromScene(activeScene); + } + switch (m_mode) { case MODE_ADD_NEW_SCENE: m_sceneFile->addScene(newScene); diff --git a/src/Scenes/Scene.cxx b/src/Scenes/Scene.cxx index 6e31836b1..8bc2a9d72 100644 --- a/src/Scenes/Scene.cxx +++ b/src/Scenes/Scene.cxx @@ -576,5 +576,24 @@ Scene::moveMacrosFromScene(Scene* scene) setModified(); } +/** + * Copy any macros in the given scene to this scene + * + * @param scene + * Scene whose macros are copied to this scene. + */ +void +Scene::copyMacrosFromScene(const Scene* scene) +{ + CaretAssert(scene); + + const WuQMacroGroup* macroGroup = scene->getMacroGroup(); + if (macroGroup != NULL) { + if ( ! macroGroup->isEmpty()) { + getMacroGroup()->appendMacroGroup(macroGroup); + } + } +} + diff --git a/src/Scenes/Scene.h b/src/Scenes/Scene.h index f89ab2dd9..9f3cb430c 100644 --- a/src/Scenes/Scene.h +++ b/src/Scenes/Scene.h @@ -97,6 +97,8 @@ namespace caret { const WuQMacroGroup* getMacroGroup() const; + void copyMacrosFromScene(const Scene* scene); + void moveMacrosFromScene(Scene* scene); private: -- GitLab From 465780fb939e6bcce97693c0687e05cc87d6c7f2 Mon Sep 17 00:00:00 2001 From: John Harwell Date: Tue, 5 Mar 2019 14:30:26 -0600 Subject: [PATCH 217/427] When a macro is imported, update the scene file modified status on the scene dialog. --- src/GuiQt/WuQMacroManager.cxx | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/GuiQt/WuQMacroManager.cxx b/src/GuiQt/WuQMacroManager.cxx index d2824d3ab..f09cf106d 100644 --- a/src/GuiQt/WuQMacroManager.cxx +++ b/src/GuiQt/WuQMacroManager.cxx @@ -764,6 +764,9 @@ WuQMacroManager::importMacros(QWidget* parent, const WuQMacroGroup* fileMacroGroup = macroFile.getMacroGroup(); if (fileMacroGroup->getNumberOfMacros() > 0) { appendToMacroGroup->appendMacroGroup(fileMacroGroup); + if (m_macroHelper) { + m_macroHelper->macroGroupWasModified(appendToMacroGroup); + } return true; } else { -- GitLab From fc088134277897830722d49a497800b167f1612a Mon Sep 17 00:00:00 2001 From: John Harwell Date: Wed, 6 Mar 2019 08:40:45 -0600 Subject: [PATCH 218/427] Add a macro custom command for delay. --- src/GuiQt/CMakeLists.txt | 2 + src/GuiQt/WbMacroCustomOperationDelay.cxx | 106 +++++++++++++++++++ src/GuiQt/WbMacroCustomOperationDelay.h | 60 +++++++++++ src/GuiQt/WbMacroCustomOperationManager.cxx | 4 + src/GuiQt/WbMacroCustomOperationTypeEnum.cxx | 4 + src/GuiQt/WbMacroCustomOperationTypeEnum.h | 2 + 6 files changed, 178 insertions(+) create mode 100644 src/GuiQt/WbMacroCustomOperationDelay.cxx create mode 100644 src/GuiQt/WbMacroCustomOperationDelay.h diff --git a/src/GuiQt/CMakeLists.txt b/src/GuiQt/CMakeLists.txt index 4d837f27f..ec3fa2bc7 100644 --- a/src/GuiQt/CMakeLists.txt +++ b/src/GuiQt/CMakeLists.txt @@ -454,6 +454,7 @@ VolumeSurfaceOutlineSetViewController.h VolumeSurfaceOutlineViewController.h WbMacroCustomDataTypeEnum.h WbMacroCustomOperationBase.h +WbMacroCustomOperationDelay.h WbMacroCustomOperationManager.h WbMacroCustomOperationModelRotation.h WbMacroCustomOperationOverlayCrossFade.h @@ -712,6 +713,7 @@ VolumeSurfaceOutlineSetViewController.cxx VolumeSurfaceOutlineViewController.cxx WbMacroCustomDataTypeEnum.cxx WbMacroCustomOperationBase.cxx +WbMacroCustomOperationDelay.cxx WbMacroCustomOperationManager.cxx WbMacroCustomOperationModelRotation.cxx WbMacroCustomOperationOverlayCrossFade.cxx diff --git a/src/GuiQt/WbMacroCustomOperationDelay.cxx b/src/GuiQt/WbMacroCustomOperationDelay.cxx new file mode 100644 index 000000000..41d91297f --- /dev/null +++ b/src/GuiQt/WbMacroCustomOperationDelay.cxx @@ -0,0 +1,106 @@ + +/*LICENSE_START*/ +/* + * Copyright (C) 2019 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + +#define __WB_MACRO_CUSTOM_OPERATION_DELAY_DECLARE__ +#include "WbMacroCustomOperationDelay.h" +#undef __WB_MACRO_CUSTOM_OPERATION_DELAY_DECLARE__ + +#include "CaretAssert.h" +#include "WbMacroCustomOperationTypeEnum.h" +#include "WuQMacroCommand.h" +#include "WuQMacroCommandParameter.h" + +using namespace caret; + + + +/** + * \class caret::WbMacroCustomOperationDelay + * \brief Custom Macro Command for Delay + * \ingroup GuiQt + */ + +/** + * Constructor. + */ +WbMacroCustomOperationDelay::WbMacroCustomOperationDelay() +: WbMacroCustomOperationBase(WbMacroCustomOperationTypeEnum::DELAY) +{ +} + +/** + * Destructor. + */ +WbMacroCustomOperationDelay::~WbMacroCustomOperationDelay() +{ +} + +/** + * Get a new instance of the macro command + * + * @return + * Pointer to command or NULL if not valid + * Use getErrorMessage() for error information if NULL returned + */ +WuQMacroCommand* +WbMacroCustomOperationDelay::createCommand() +{ + const int32_t versionOne(1); + + /* + * No parameters are needed as the base command's delay value is used + */ + QString errorMessage; + WuQMacroCommand* command = WuQMacroCommand::newInstanceCustomCommand(WbMacroCustomOperationTypeEnum::toName(WbMacroCustomOperationTypeEnum::DELAY), + versionOne, + "none", + "Delay", + "Delay for Seconds", + 5.0, + errorMessage); + + return command; +} + +/** + * Execute the macro command + * + * @param parent + * Parent widget for any dialogs + * @param macroCommand + * macro command to run + * @return + * True if command executed successfully, else false + * Use getErrorMessage() for error information if false returned + */ +bool +WbMacroCustomOperationDelay::executeCommand(QWidget* parent, + const WuQMacroCommand* macroCommand) +{ + CaretAssert(parent); + CaretAssert(macroCommand); + + /* + * Nothing to do since the base command's delay time performs the delay + */ + return true; +} + diff --git a/src/GuiQt/WbMacroCustomOperationDelay.h b/src/GuiQt/WbMacroCustomOperationDelay.h new file mode 100644 index 000000000..c4f2fbb83 --- /dev/null +++ b/src/GuiQt/WbMacroCustomOperationDelay.h @@ -0,0 +1,60 @@ +#ifndef __WB_MACRO_CUSTOM_OPERATION_DELAY_H__ +#define __WB_MACRO_CUSTOM_OPERATION_DELAY_H__ + +/*LICENSE_START*/ +/* + * Copyright (C) 2019 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + + + +#include + +#include "WbMacroCustomOperationBase.h" + +namespace caret { + + class WbMacroCustomOperationDelay : public WbMacroCustomOperationBase { + + public: + WbMacroCustomOperationDelay(); + + virtual ~WbMacroCustomOperationDelay(); + + WbMacroCustomOperationDelay(const WbMacroCustomOperationDelay&) = delete; + + WbMacroCustomOperationDelay& operator=(const WbMacroCustomOperationDelay&) = delete; + + virtual bool executeCommand(QWidget* parent, + const WuQMacroCommand* macroCommand) override; + + virtual WuQMacroCommand* createCommand() override; + + // ADD_NEW_METHODS_HERE + + private: + // ADD_NEW_MEMBERS_HERE + + }; + +#ifdef __WB_MACRO_CUSTOM_OPERATION_DELAY_DECLARE__ + // +#endif // __WB_MACRO_CUSTOM_OPERATION_DELAY_DECLARE__ + +} // namespace +#endif //__WB_MACRO_CUSTOM_OPERATION_DELAY_H__ diff --git a/src/GuiQt/WbMacroCustomOperationManager.cxx b/src/GuiQt/WbMacroCustomOperationManager.cxx index dd76c0945..a4070662e 100644 --- a/src/GuiQt/WbMacroCustomOperationManager.cxx +++ b/src/GuiQt/WbMacroCustomOperationManager.cxx @@ -32,6 +32,7 @@ #include "GuiManager.h" #include "Surface.h" #include "WbMacroCustomDataTypeEnum.h" +#include "WbMacroCustomOperationDelay.h" #include "WbMacroCustomOperationModelRotation.h" #include "WbMacroCustomOperationOverlayCrossFade.h" #include "WbMacroCustomOperationSurfaceInterpolation.h" @@ -325,6 +326,9 @@ WbMacroCustomOperationManager::createCommand(const WbMacroCustomOperationTypeEnu WbMacroCustomOperationBase* operationOut(NULL); switch (operationType) { + case WbMacroCustomOperationTypeEnum::DELAY: + operationOut = new WbMacroCustomOperationDelay(); + break; case WbMacroCustomOperationTypeEnum::MODEL_ROTATION: operationOut = new WbMacroCustomOperationModelRotation(); break; diff --git a/src/GuiQt/WbMacroCustomOperationTypeEnum.cxx b/src/GuiQt/WbMacroCustomOperationTypeEnum.cxx index 30e7453a7..a9ca0993c 100644 --- a/src/GuiQt/WbMacroCustomOperationTypeEnum.cxx +++ b/src/GuiQt/WbMacroCustomOperationTypeEnum.cxx @@ -106,6 +106,10 @@ WbMacroCustomOperationTypeEnum::initialize() } initializedFlag = true; + enumData.push_back(WbMacroCustomOperationTypeEnum(DELAY, + "DELAY", + "Delay")); + enumData.push_back(WbMacroCustomOperationTypeEnum(MODEL_ROTATION, "MODEL_ROTATION", "Model Rotation")); diff --git a/src/GuiQt/WbMacroCustomOperationTypeEnum.h b/src/GuiQt/WbMacroCustomOperationTypeEnum.h index c72f22bc4..b7e5c64ca 100644 --- a/src/GuiQt/WbMacroCustomOperationTypeEnum.h +++ b/src/GuiQt/WbMacroCustomOperationTypeEnum.h @@ -35,6 +35,8 @@ public: * Enumerated values. */ enum Enum { + /** Delay **/ + DELAY, /** Rotation of viewed model */ MODEL_ROTATION, /** CrossFade of overlays */ -- GitLab From e7994fa751855a3a2f7c2fb6071901d4b5385210 Mon Sep 17 00:00:00 2001 From: John Harwell Date: Wed, 6 Mar 2019 09:15:44 -0600 Subject: [PATCH 219/427] Add new movie type "MPG"; Temporarily remove "volume to surface crossfade" from custom macro command selection; Remove Apply button from new macro command selection dialog; Fix axis label on volume slice increment command. --- src/Brain/MovieRecorder.cxx | 2 +- src/Brain/MovieRecorderVideoFormatTypeEnum.cxx | 6 ++++++ src/Brain/MovieRecorderVideoFormatTypeEnum.h | 2 ++ src/GuiQt/WbMacroCustomOperationManager.cxx | 3 +++ src/GuiQt/WbMacroCustomOperationVolumeSliceIncrement.cxx | 2 +- src/GuiQt/WuQMacroNewCommandSelectionDialog.cxx | 5 ++++- 6 files changed, 17 insertions(+), 3 deletions(-) diff --git a/src/Brain/MovieRecorder.cxx b/src/Brain/MovieRecorder.cxx index 557019098..c88069816 100644 --- a/src/Brain/MovieRecorder.cxx +++ b/src/Brain/MovieRecorder.cxx @@ -349,7 +349,7 @@ void MovieRecorder::initializeMovieFileName(const AString& path) { if (m_movieFileName.isEmpty()) { - const QString extension = MovieRecorderVideoFormatTypeEnum::toFileNameExtensionNoDot(MovieRecorderVideoFormatTypeEnum::MPEG_4); + const QString extension = MovieRecorderVideoFormatTypeEnum::toFileNameExtensionNoDot(MovieRecorderVideoFormatTypeEnum::MPEG); m_movieFileName = FileInformation::assembleFileComponents(path, "Movie", diff --git a/src/Brain/MovieRecorderVideoFormatTypeEnum.cxx b/src/Brain/MovieRecorderVideoFormatTypeEnum.cxx index f13699585..a3d842efc 100644 --- a/src/Brain/MovieRecorderVideoFormatTypeEnum.cxx +++ b/src/Brain/MovieRecorderVideoFormatTypeEnum.cxx @@ -119,6 +119,12 @@ MovieRecorderVideoFormatTypeEnum::initialize() "avi", "AVI (*.avi)")); + enumData.push_back(MovieRecorderVideoFormatTypeEnum(MPEG, + "MPEG", + "Mpeg", + "mpg", + "MPEG (*.mpg)")); + enumData.push_back(MovieRecorderVideoFormatTypeEnum(MPEG_4, "MPEG_4", "Mpeg 4", diff --git a/src/Brain/MovieRecorderVideoFormatTypeEnum.h b/src/Brain/MovieRecorderVideoFormatTypeEnum.h index 57dd608a9..6849812db 100644 --- a/src/Brain/MovieRecorderVideoFormatTypeEnum.h +++ b/src/Brain/MovieRecorderVideoFormatTypeEnum.h @@ -37,6 +37,8 @@ public: enum Enum { /** AVI video */ AVI, + /** MPEG */ + MPEG, /** MPEG-4 video */ MPEG_4 }; diff --git a/src/GuiQt/WbMacroCustomOperationManager.cxx b/src/GuiQt/WbMacroCustomOperationManager.cxx index a4070662e..33fa83846 100644 --- a/src/GuiQt/WbMacroCustomOperationManager.cxx +++ b/src/GuiQt/WbMacroCustomOperationManager.cxx @@ -260,6 +260,9 @@ WbMacroCustomOperationManager::getAllCustomOperationMacroCommands() std::vector customCommands; for (auto cct : customCommandTypes) { + if (cct == WbMacroCustomOperationTypeEnum::VOLUME_TO_SURFACE_CROSS_FADE) { + continue; + } std::unique_ptr customOperation(createCommand(cct)); customCommands.push_back(customOperation->createCommand()); } diff --git a/src/GuiQt/WbMacroCustomOperationVolumeSliceIncrement.cxx b/src/GuiQt/WbMacroCustomOperationVolumeSliceIncrement.cxx index 2accb96e0..4184069d6 100644 --- a/src/GuiQt/WbMacroCustomOperationVolumeSliceIncrement.cxx +++ b/src/GuiQt/WbMacroCustomOperationVolumeSliceIncrement.cxx @@ -93,7 +93,7 @@ WbMacroCustomOperationVolumeSliceIncrement::createCommand() errorMessage); if (command != NULL) { WuQMacroCommandParameter* paramOne = new WuQMacroCommandParameter(WuQMacroDataValueTypeEnum::CUSTOM_DATA, - "Screen Axis", + "Volume Axis", "Z"); paramOne->setCustomDataType(WbMacroCustomDataTypeEnum::toName(WbMacroCustomDataTypeEnum::SCREEN_AXIS)); command->addParameter(paramOne); diff --git a/src/GuiQt/WuQMacroNewCommandSelectionDialog.cxx b/src/GuiQt/WuQMacroNewCommandSelectionDialog.cxx index 731ddda0e..107cf9f79 100644 --- a/src/GuiQt/WuQMacroNewCommandSelectionDialog.cxx +++ b/src/GuiQt/WuQMacroNewCommandSelectionDialog.cxx @@ -120,8 +120,11 @@ WuQMacroNewCommandSelectionDialog::WuQMacroNewCommandSelectionDialog(QWidget* pa m_splitter->setChildrenCollapsible(false); m_dialogButtonBox = new QDialogButtonBox(QDialogButtonBox::Ok - | QDialogButtonBox::Apply | QDialogButtonBox::Cancel); + const bool allowApplyButtonFlag(false); + if (allowApplyButtonFlag) { + m_dialogButtonBox->addButton(QDialogButtonBox::Apply); + } QObject::connect(m_dialogButtonBox, &QDialogButtonBox::clicked, this, &WuQMacroNewCommandSelectionDialog::otherButtonClicked); -- GitLab From 9d8d4c4e888f5876f1577962b8b2ce2085f8cdbb Mon Sep 17 00:00:00 2001 From: John Harwell Date: Thu, 7 Mar 2019 10:46:19 -0600 Subject: [PATCH 220/427] Fixed method that verified number of parameters for custom macro commands. --- src/GuiQt/WbMacroCustomOperationBase.cxx | 46 +- src/GuiQt/WbMacroCustomOperationBase.h | 7 +- src/GuiQt/WbMacroCustomOperationManager.cxx | 550 ++++++++++++++++-- src/GuiQt/WbMacroCustomOperationManager.h | 36 +- .../WbMacroCustomOperationModelRotation.cxx | 13 +- ...croCustomOperationSurfaceInterpolation.cxx | 22 +- ...croCustomOperationVolumeSliceIncrement.cxx | 13 +- ...ustomOperationVolumeToSurfaceCrossFade.cxx | 11 +- .../WuQMacroCustomOperationManagerInterface.h | 3 + src/GuiQt/WuQMacroDialog.cxx | 15 +- src/GuiQt/WuQMacroDialog.h | 5 +- src/GuiQt/WuQMacroManager.cxx | 6 +- src/GuiQt/WuQMacroManager.h | 1 + 13 files changed, 635 insertions(+), 93 deletions(-) diff --git a/src/GuiQt/WbMacroCustomOperationBase.cxx b/src/GuiQt/WbMacroCustomOperationBase.cxx index 5b3f8b2b7..4e72b8c74 100644 --- a/src/GuiQt/WbMacroCustomOperationBase.cxx +++ b/src/GuiQt/WbMacroCustomOperationBase.cxx @@ -86,16 +86,24 @@ WbMacroCustomOperationBase::getErrorMessage() const return m_errorMessage; } +/** + * Validate that the command contains the correct number of parameters + * + * @param command + * The command + * @param correctNumberOfParameters + * The correct number of parameters (this must be passed in since a command + * may change its number of parameters in a new version of the command) + */ bool -WbMacroCustomOperationBase::validateCorrectNumberOfParameters(const WuQMacroCommand* command) +WbMacroCustomOperationBase::validateCorrectNumberOfParameters(const WuQMacroCommand* command, + const int32_t correctNumberOfParameters) { - std::unique_ptr validCommand(createCommand()); const int32_t paramCount = command->getNumberOfParameters(); - const int32_t validCount = validCommand->getNumberOfParameters(); - if (paramCount < validCount) { + if (paramCount < correctNumberOfParameters) { appendToErrorMessage("Command should contain " - + QString::number(validCount) + + QString::number(correctNumberOfParameters) + " parameters but contains " + QString::number(paramCount) + " parameters"); @@ -121,6 +129,24 @@ WbMacroCustomOperationBase::appendToErrorMessage(const QString& text) m_errorMessage.append(text); } +/** + * Create a unsupported version messagge and append it to the error message. + * + * @param unsupportedVersionNumber + * Verson not supported + */ +void +WbMacroCustomOperationBase::appendUnsupportedVersionToErrorMessage(const int32_t unsupportedVersionNumber) +{ + QString msg("Version " + + QString::number(unsupportedVersionNumber) + + " is not supported for " + + getOperationName() + + ". You may need to update your version of wb_view"); + appendToErrorMessage(msg); +} + + /** * Find the surface with the given name * @@ -248,4 +274,14 @@ WbMacroCustomOperationBase::sleepForSecondsAtEndOfIteration(const float seconds) } } +/** + * @return User friendly name for command + * Sub-classes may override to provide more descriptive name + */ +QString +WbMacroCustomOperationBase::getOperationName() const +{ + return WbMacroCustomOperationTypeEnum::toGuiName(m_operationType); +} + diff --git a/src/GuiQt/WbMacroCustomOperationBase.h b/src/GuiQt/WbMacroCustomOperationBase.h index 323fae27a..6e7df9890 100644 --- a/src/GuiQt/WbMacroCustomOperationBase.h +++ b/src/GuiQt/WbMacroCustomOperationBase.h @@ -77,16 +77,21 @@ namespace caret { void sleepForSecondsAtEndOfIteration(const float seconds); + virtual QString getOperationName() const; + protected: WbMacroCustomOperationBase(const WbMacroCustomOperationTypeEnum::Enum operationType); - bool validateCorrectNumberOfParameters(const WuQMacroCommand* command); + bool validateCorrectNumberOfParameters(const WuQMacroCommand* command, + const int32_t correctNumberOfParameters); Surface* findSurface(const QString& surfaceName, const QString& errorMessagePrefix); void appendToErrorMessage(const QString& text); + void appendUnsupportedVersionToErrorMessage(const int32_t unsupportedVersionNumber); + void updateGraphics(); void updateSurfaceColoring(); diff --git a/src/GuiQt/WbMacroCustomOperationManager.cxx b/src/GuiQt/WbMacroCustomOperationManager.cxx index 33fa83846..1ecdef088 100644 --- a/src/GuiQt/WbMacroCustomOperationManager.cxx +++ b/src/GuiQt/WbMacroCustomOperationManager.cxx @@ -25,11 +25,16 @@ #include +#include "BrainBrowserWindow.h" +#include "BrowserTabContent.h" #include "CaretAssert.h" #include "CaretLogger.h" +#include "EventCaretMappableDataFilesGet.h" #include "EventManager.h" #include "EventSurfacesGet.h" #include "GuiManager.h" +#include "Overlay.h" +#include "OverlaySet.h" #include "Surface.h" #include "WbMacroCustomDataTypeEnum.h" #include "WbMacroCustomOperationDelay.h" @@ -77,23 +82,426 @@ WbMacroCustomOperationManager::toString() const return "WbMacroCustomOperationManager"; } +/** + * Edit overlay index + * + * @param parentWidget + * Parent widget for any dialogs + * @param parameter + * Parameter for editing + * @return + * True if the parameter was modified + */ +bool +WbMacroCustomOperationManager::editOverlayIndex(QWidget* parentWidget, + WuQMacroCommandParameter* parameter) +{ + bool okFlag(false); + const int32_t currentValue(parameter->getValue().toInt()); + const int32_t minimumValue(1); + const int32_t maximumValue(BrainConstants::MAXIMUM_NUMBER_OF_OVERLAYS); + const int32_t stepValue(1); + int32_t index = QInputDialog::getInt(parentWidget, + "Overlay Index", + "Overlay Index (1 is at top)", + currentValue, + minimumValue, + maximumValue, + stepValue, + &okFlag); + if (okFlag) { + parameter->setValue(index); + } + return okFlag; +} + +/** + * Edit screen axis + * + * @param parentWidget + * Parent widget for any dialogs + * @param parameter + * Parameter for editing + * @return + * True if the parameter was modified + */ +bool +WbMacroCustomOperationManager::editScreenAxis(QWidget* parentWidget, + WuQMacroCommandParameter* parameter) +{ + QStringList axisList; + axisList.append("X"); + axisList.append("Y"); + axisList.append("Z"); + + bool okFlag(false); + const bool editableFlag(false); + QString selectedName = QInputDialog::getItem(parentWidget, + "Screen Axis", + parameter->getName(), + axisList, + 0, + editableFlag, + &okFlag); + if (okFlag) { + parameter->setValue(selectedName); + } + return okFlag; +} + +/** + * Edit surface selection + * + * @param parentWidget + * Parent widget for any dialogs + * @param parameter + * Parameter for editing + * @param errorMessageOut + * Output error message if modifying parameter fails + * @return + * True if the parameter was modified + */ +bool +WbMacroCustomOperationManager::editSurface(QWidget* parentWidget, + WuQMacroCommandParameter* parameter, + QString& errorMessageOut) +{ + EventSurfacesGet surfacesEvent; + EventManager::get()->sendEvent(surfacesEvent.getPointer()); + std::vector surfaces = surfacesEvent.getSurfaces(); + bool okFlag(false); + if (surfaces.empty()) { + errorMessageOut = "There are no surfaces available"; + } + else { + QStringList surfaceNamesList; + for (const auto s : surfaces) { + surfaceNamesList.append(s->getFileNameNoPath()); + } + + const bool editableFlag(false); + QString selectedName = QInputDialog::getItem(parentWidget, + "Choose Surface", + parameter->getName(), + surfaceNamesList, + 0, + editableFlag, + &okFlag); + if (okFlag) { + parameter->setValue(selectedName); + } + } + + return okFlag; +} + +/** + * Get the map file in the overlay for this command + * + * @param parentWidget + * Parent widget for any dialogs + * @param macroCommand + * Macro command containing parameter being edited + * @param mapFilesOut + * Map files in the overlay + * @param selectedMapFileOut + * Selected map file in the overlay + * @param errorMessageOut + * Output error message if finding map files fails + * @return + * True if the parameter was modified + */ +bool +WbMacroCustomOperationManager::getMapFilesInOverlay(QWidget* parentWidget, + WuQMacroCommand* macroCommand, + std::vector& mapFilesOut, + CaretMappableDataFile* &selectedMapFileOut, + QString& errorMessageOut) +{ + mapFilesOut.clear(); + selectedMapFileOut = NULL; + + /* + * Find the overlay index parameter that should be before this parameter + */ + int32_t overlayIndex(-1); + const int32_t numParams = macroCommand->getNumberOfParameters(); + for (int32_t ip = 0; ip < numParams; ip++) { + const WuQMacroCommandParameter* p = macroCommand->getParameterAtIndex(ip); + if (p->getDataType() == WuQMacroDataValueTypeEnum::CUSTOM_DATA) { + bool valid(false); + WbMacroCustomDataTypeEnum::Enum customType = WbMacroCustomDataTypeEnum::fromName(p->getCustomDataType(), + &valid); + if (valid) { + if (customType == WbMacroCustomDataTypeEnum::OVERLAY_INDEX) { + overlayIndex = p->getValue().toInt(); + break; + } + } + } + } + + /* + * Overlay index is 1..N for user + */ + if (overlayIndex < 1) { + errorMessageOut = "Unable to find overlay index"; + return false; + } + --overlayIndex; + + BrowserTabContent* tabContent = getTabContent(parentWidget, + "Window containing overlay", + errorMessageOut); + if (tabContent == NULL) { + return false; + } + + Overlay* overlay = tabContent->getOverlaySet()->getOverlay(overlayIndex); + if (overlay == NULL) { + errorMessageOut = ("Overlay " + + AString::number(overlayIndex) + + " not found"); + return false; + } + + int32_t selectedMapIndex(-1); + overlay->getSelectionData(mapFilesOut, + selectedMapFileOut, + selectedMapIndex); + + if (mapFilesOut.empty()) { + errorMessageOut = "The overlay does not contain any files"; + return false; + } + return true; +} + +/** + * Edit overlay file + * + * @param parentWidget + * Parent widget for any dialogs + * @param macroCommand + * Macro command containing parameter being edited + * @param parameter + * Parameter for editing + * @param errorMessageOut + * Output error message if modifying parameter fails + * @return + * True if the parameter was modified + */ +bool +WbMacroCustomOperationManager::editOverlayFile(QWidget* parentWidget, + WuQMacroCommand* macroCommand, + WuQMacroCommandParameter* parameter, + QString& errorMessageOut) +{ +// /* +// * Find the overlay index parameter that should be before this parameter +// */ +// int32_t overlayIndex(-1); +// const int32_t numParams = macroCommand->getNumberOfParameters(); +// for (int32_t ip = 0; ip < numParams; ip++) { +// const WuQMacroCommandParameter* p = macroCommand->getParameterAtIndex(ip); +// if (p->getDataType() == WuQMacroDataValueTypeEnum::CUSTOM_DATA) { +// bool valid(false); +// WbMacroCustomDataTypeEnum::Enum customType = WbMacroCustomDataTypeEnum::fromName(p->getCustomDataType(), +// &valid); +// if (valid) { +// if (customType == WbMacroCustomDataTypeEnum::OVERLAY_INDEX) { +// overlayIndex = p->getValue().toInt(); +// break; +// } +// } +// } +// } +// +// /* +// * Overlay index is 1..N for user +// */ +// if (overlayIndex < 1) { +// errorMessageOut = "Unable to find overlay index"; +// return false; +// } +// --overlayIndex; +// +// BrowserTabContent* tabContent = getTabContent(parentWidget, +// "Window containing overlay", +// errorMessageOut); +// if (tabContent == NULL) { +// return false; +// } +// +// Overlay* overlay = tabContent->getOverlaySet()->getOverlay(overlayIndex); +// if (overlay == NULL) { +// errorMessageOut = ("Overlay " +// + AString::number(overlayIndex) +// + " not found"); +// return false; +// } +// +// std::vector mapFiles; +// CaretMappableDataFile* selectedMapFile(NULL); +// int32_t selectedMapIndex(-1); +// overlay->getSelectionData(mapFiles, +// selectedMapFile, +// selectedMapIndex); +// +// if (mapFiles.empty()) { +// errorMessageOut = "The overlay does not contain any files"; +// return false; +// } + + + std::vector mapFiles; + CaretMappableDataFile* selectedMapFile(NULL); + if ( ! getMapFilesInOverlay(parentWidget, + macroCommand, + mapFiles, + selectedMapFile, + errorMessageOut)) { + return false; + } + + + const QString selectedFileName = parameter->getValue().toString(); + int32_t selectedIndex(0); + QStringList fileNameList; + for (const auto mf : mapFiles) { + const QString name(mf->getFileNameNoPath()); + if (selectedFileName == name) { + selectedIndex = fileNameList.count(); + } + fileNameList.append(name); + } + + bool okFlag(false); + const bool editableFlag(false); + QString selectedName = QInputDialog::getItem(parentWidget, + "Choose map file", + "Choose map file", + fileNameList, + selectedIndex, + editableFlag, + &okFlag); + if (okFlag) { + parameter->setValue(selectedName); + return true; + } + + return false; +} + +/** + * Edit overlay file + * + * @param parentWidget + * Parent widget for any dialogs + * @param macroCommand + * Macro command containing parameter being edited + * @param parameter + * Parameter for editing + * @param errorMessageOut + * Output error message if modifying parameter fails + * @return + * True if the parameter was modified + */ +bool +WbMacroCustomOperationManager::editOverlayMap(QWidget* parentWidget, + WuQMacroCommand* macroCommand, + WuQMacroCommandParameter* parameter, + QString& errorMessageOut) +{ + /* + * Find the overlay file parameter that should be before this parameter + */ + QString filename; + const int32_t numParams = macroCommand->getNumberOfParameters(); + for (int32_t ip = 0; ip < numParams; ip++) { + const WuQMacroCommandParameter* p = macroCommand->getParameterAtIndex(ip); + if (p->getDataType() == WuQMacroDataValueTypeEnum::CUSTOM_DATA) { + bool valid(false); + WbMacroCustomDataTypeEnum::Enum customType = WbMacroCustomDataTypeEnum::fromName(p->getCustomDataType(), + &valid); + if (valid) { + if (customType == WbMacroCustomDataTypeEnum::OVERLAY_FILE_NAME_OR_FILE_INDEX) { + filename = p->getValue().toString(); + break; + } + } + } + } + + if (filename.isEmpty()) { + errorMessageOut = "Name of overlay file is empty, select a file"; + return false; + } + + EventCaretMappableDataFilesGet filesEvent; + EventManager::get()->sendEvent(filesEvent.getPointer()); + std::vector allMapFiles; + filesEvent.getAllFiles(allMapFiles); + + CaretMappableDataFile* mapFile(NULL); + for (auto cmdf : allMapFiles) { + if (cmdf->getFileName().endsWith(filename)) { + mapFile = cmdf; + break; + } + } + if (mapFile == NULL) { + errorMessageOut = ("Unable to find data file with name \"" + + filename + + "\""); + return false; + } + + const QString currentMapName = parameter->getValue().toString(); + + const int32_t numMaps = mapFile->getNumberOfMaps(); + QStringList mapNameList; + for (int32_t i = 0; i < numMaps; i++) { + mapNameList.append(mapFile->getMapName(i)); + } + + bool okFlag(false); + const bool editableFlag(false); + QString selectedName = QInputDialog::getItem(parentWidget, + "Choose Map Name", + "Choose map name", + mapNameList, + 0, + editableFlag, + &okFlag); + if (okFlag) { + parameter->setValue(selectedName); + return true; + } + + return false; +} + /** * Is called to edit a macro command parameter with a CUSTOM_DATA data type * - * @param parent + * @param parentWidget * Parent widget for any dialogs + * @param macroCommand + * Macro command that contains the parameter for editing * @param parameter * Parameter for editing * @return * True if the parameter was modified */ bool -WbMacroCustomOperationManager::editCustomDataValueParameter(QWidget* parent, +WbMacroCustomOperationManager::editCustomDataValueParameter(QWidget* parentWidget, + WuQMacroCommand* macroCommand, WuQMacroCommandParameter* parameter) { bool modFlag(false); - CaretAssert(parent); + CaretAssert(parentWidget); CaretAssert(parameter); if (parameter->getDataType() != WuQMacroDataValueTypeEnum::CUSTOM_DATA) { @@ -114,60 +522,35 @@ WbMacroCustomOperationManager::editCustomDataValueParameter(QWidget* parent, QString errorMessage; switch (userType) { + case WbMacroCustomDataTypeEnum::OVERLAY_INDEX: + modFlag = editOverlayIndex(parentWidget, + parameter); + break; + case WbMacroCustomDataTypeEnum::OVERLAY_FILE_NAME_OR_FILE_INDEX: + modFlag = editOverlayFile(parentWidget, + macroCommand, + parameter, + errorMessage); + break; + case WbMacroCustomDataTypeEnum::OVERLAY_MAP_NAME_OR_MAP_INDEX: + modFlag = editOverlayMap(parentWidget, + macroCommand, + parameter, + errorMessage); + break; case WbMacroCustomDataTypeEnum::SCREEN_AXIS: - { - QStringList axisList; - axisList.append("X"); - axisList.append("Y"); - axisList.append("Z"); - - bool okFlag(false); - QString selectedName = QInputDialog::getItem(parent, - "Choose Surface", - parameter->getName(), - axisList, - 0, - false, - &okFlag); - if (okFlag) { - parameter->setValue(selectedName); - modFlag = true; - } - } + modFlag = editScreenAxis(parentWidget, + parameter); break; case WbMacroCustomDataTypeEnum::SURFACE: - { - EventSurfacesGet surfacesEvent; - EventManager::get()->sendEvent(surfacesEvent.getPointer()); - std::vector surfaces = surfacesEvent.getSurfaces(); - if (surfaces.empty()) { - errorMessage = "There are no surfaces available"; - } - else { - QStringList surfaceNamesList; - for (const auto s : surfaces) { - surfaceNamesList.append(s->getFileNameNoPath()); - } - - bool okFlag(false); - QString selectedName = QInputDialog::getItem(parent, - "Choose Surface", - parameter->getName(), - surfaceNamesList, - 0, - false, - &okFlag); - if (okFlag) { - parameter->setValue(selectedName); - modFlag = true; - } - } - } + modFlag = editSurface(parentWidget, + parameter, + errorMessage); break; } if ( ! errorMessage.isEmpty()) { - WuQMessageBox::critical(parent, + WuQMessageBox::critical(parentWidget, "Set Value", errorMessage); } @@ -353,3 +736,70 @@ WbMacroCustomOperationManager::createCommand(const WbMacroCustomOperationTypeEnu return operationOut; } +/** + * Get the active tab content in the active window. If there is more + * than one window open, the user is prompted to select a window + * + * @param parentWidget + * Widget for any dialogs + * @param promptMessage + * Message displayed in dialog if more than one window is open + * @param errorMessageOut + * Output with error information if failure + * @return + * Pointer to active tab content or none found + */ +BrowserTabContent* +WbMacroCustomOperationManager::getTabContent(QWidget* parentWidget, + const QString& promptMessage, + QString& errorMessageOut) +{ + errorMessageOut.clear(); + + const std::vector allWindows = GuiManager::get()->getAllOpenBrainBrowserWindows(); + if (allWindows.empty()) { + errorMessageOut = "No window are open. This should never happen."; + return NULL; + } + + BrowserTabContent* tabContent(NULL); + BrainBrowserWindow* bbw(NULL); + if (allWindows.size() == 1) { + CaretAssertVectorIndex(allWindows, 0); + bbw = allWindows[0]; + } + else { + QStringList windowNameList; + for (const auto w : allWindows) { + windowNameList.append(w->windowTitle()); + } + + bool okFlag(false); + const bool editableFlag(false); + QString selectedName = QInputDialog::getItem(parentWidget, + "Choose window", + promptMessage, + windowNameList, + 0, + editableFlag, + &okFlag); + if (okFlag) { + for (auto w : allWindows) { + if (selectedName == w->windowTitle()) { + bbw = w; + break; + } + } + } + } + + if (bbw != NULL) { + tabContent = bbw->getBrowserTabContent(); + } + else { + errorMessageOut = "Failed to find window selected by user"; + } + + return tabContent; +} + diff --git a/src/GuiQt/WbMacroCustomOperationManager.h b/src/GuiQt/WbMacroCustomOperationManager.h index c52aedd6d..975f730e6 100644 --- a/src/GuiQt/WbMacroCustomOperationManager.h +++ b/src/GuiQt/WbMacroCustomOperationManager.h @@ -28,9 +28,12 @@ #include "CaretObject.h" #include "WbMacroCustomOperationTypeEnum.h" #include "WuQMacroCustomOperationManagerInterface.h" + class QWidget; namespace caret { + class BrowserTabContent; + class CaretMappableDataFile; class WbMacroCustomOperationBase; class WuQMacroCommand; class WuQMacroCommandParameter; @@ -45,8 +48,9 @@ namespace caret { WbMacroCustomOperationManager(const WbMacroCustomOperationManager&) = delete; WbMacroCustomOperationManager& operator=(const WbMacroCustomOperationManager&) = delete; - + virtual bool editCustomDataValueParameter(QWidget* parent, + WuQMacroCommand* macroCommand, WuQMacroCommandParameter* parameter); virtual bool executeCustomOperationMacroCommand(QWidget* parent, @@ -69,6 +73,36 @@ namespace caret { WbMacroCustomOperationBase* createCommand(const WbMacroCustomOperationTypeEnum::Enum operationType); + bool editOverlayIndex(QWidget* parentWidget, + WuQMacroCommandParameter* parameter); + + bool editOverlayFile(QWidget* parentWidget, + WuQMacroCommand* macroCommand, + WuQMacroCommandParameter* parameter, + QString& errorMessageOut); + + bool editOverlayMap(QWidget* parentWidget, + WuQMacroCommand* macroCommand, + WuQMacroCommandParameter* parameter, + QString& errorMessageOut); + + bool editScreenAxis(QWidget* parentWidget, + WuQMacroCommandParameter* parameter); + + bool editSurface(QWidget* parentWidget, + WuQMacroCommandParameter* parameter, + QString& errorMessageOut); + + bool getMapFilesInOverlay(QWidget* parentWidget, + WuQMacroCommand* macroCommand, + std::vector& mapFilesOut, + CaretMappableDataFile* &selectedMapFileOut, + QString& errorMessageOut); + + BrowserTabContent* getTabContent(QWidget* parentWidget, + const QString& promptMessage, + QString& errorMessageOut); + // ADD_NEW_MEMBERS_HERE }; diff --git a/src/GuiQt/WbMacroCustomOperationModelRotation.cxx b/src/GuiQt/WbMacroCustomOperationModelRotation.cxx index 29bf0d113..16d596617 100644 --- a/src/GuiQt/WbMacroCustomOperationModelRotation.cxx +++ b/src/GuiQt/WbMacroCustomOperationModelRotation.cxx @@ -119,8 +119,13 @@ WbMacroCustomOperationModelRotation::executeCommand(QWidget* parent, CaretAssert(parent); CaretAssert(macroCommand); - validateCorrectNumberOfParameters(macroCommand); - + if ( ! validateCorrectNumberOfParameters(macroCommand, 3)) { + return false; + } + const QString axisName(macroCommand->getParameterAtIndex(0)->getValue().toString().toUpper()); + const float totalRotation = macroCommand->getParameterAtIndex(1)->getValue().toFloat(); + const float durationSeconds = macroCommand->getParameterAtIndex(2)->getValue().toFloat(); + BrainBrowserWindow* bbw = qobject_cast(parent); if (bbw == NULL) { appendToErrorMessage("Parent for running surface macro is not a browser window."); @@ -133,10 +138,6 @@ WbMacroCustomOperationModelRotation::executeCommand(QWidget* parent, return false; } - const QString axisName(macroCommand->getParameterAtIndex(0)->getValue().toString().toUpper()); - const float totalRotation = macroCommand->getParameterAtIndex(1)->getValue().toFloat(); - const float durationSeconds = macroCommand->getParameterAtIndex(2)->getValue().toFloat(); - Axis axis = Axis::X; if (axisName == "X") { axis = Axis::X; diff --git a/src/GuiQt/WbMacroCustomOperationSurfaceInterpolation.cxx b/src/GuiQt/WbMacroCustomOperationSurfaceInterpolation.cxx index 7658654f7..7d2785beb 100644 --- a/src/GuiQt/WbMacroCustomOperationSurfaceInterpolation.cxx +++ b/src/GuiQt/WbMacroCustomOperationSurfaceInterpolation.cxx @@ -128,7 +128,17 @@ WbMacroCustomOperationSurfaceInterpolation::executeCommand(QWidget* parent, CaretAssert(parent); CaretAssert(macroCommand); - validateCorrectNumberOfParameters(macroCommand); + if ( ! validateCorrectNumberOfParameters(macroCommand, 3)) { + return false; + } + const QString startSurfaceName(macroCommand->getParameterAtIndex(0)->getValue().toString()); + Surface* startSurface = findSurface(startSurfaceName, + "Starting surface"); + const QString endSurfaceName(macroCommand->getParameterAtIndex(1)->getValue().toString()); + Surface* endSurface = findSurface(endSurfaceName, + "Ending surface"); + const float durationSeconds = macroCommand->getParameterAtIndex(2)->getValue().toFloat(); + BrainBrowserWindow* bbw = qobject_cast(parent); if (bbw == NULL) { @@ -148,16 +158,6 @@ WbMacroCustomOperationSurfaceInterpolation::executeCommand(QWidget* parent, return false; } - const QString startSurfaceName(macroCommand->getParameterAtIndex(0)->getValue().toString()); - Surface* startSurface = findSurface(startSurfaceName, - "Starting surface"); - - const QString endSurfaceName(macroCommand->getParameterAtIndex(1)->getValue().toString()); - Surface* endSurface = findSurface(endSurfaceName, - "Ending surface"); - - const float durationSeconds = macroCommand->getParameterAtIndex(2)->getValue().toFloat(); - if ((startSurface != NULL) && (endSurface != NULL)) { if (startSurface == endSurface) { diff --git a/src/GuiQt/WbMacroCustomOperationVolumeSliceIncrement.cxx b/src/GuiQt/WbMacroCustomOperationVolumeSliceIncrement.cxx index 4184069d6..b51b67c59 100644 --- a/src/GuiQt/WbMacroCustomOperationVolumeSliceIncrement.cxx +++ b/src/GuiQt/WbMacroCustomOperationVolumeSliceIncrement.cxx @@ -126,8 +126,12 @@ WbMacroCustomOperationVolumeSliceIncrement::executeCommand(QWidget* parent, CaretAssert(parent); CaretAssert(macroCommand); - validateCorrectNumberOfParameters(macroCommand); - + if ( ! validateCorrectNumberOfParameters(macroCommand, 2)) { + return false; + } + const QString axisName(macroCommand->getParameterAtIndex(0)->getValue().toString().toUpper()); + const float durationSeconds = macroCommand->getParameterAtIndex(1)->getValue().toFloat(); + BrainBrowserWindow* bbw = qobject_cast(parent); if (bbw == NULL) { appendToErrorMessage("Parent for running surface macro is not a browser window."); @@ -140,11 +144,6 @@ WbMacroCustomOperationVolumeSliceIncrement::executeCommand(QWidget* parent, return false; } - int32_t parameterIndex(0); - const QString axisName(macroCommand->getParameterAtIndex(parameterIndex)->getValue().toString().toUpper()); - parameterIndex++; - const float durationSeconds = macroCommand->getParameterAtIndex(parameterIndex)->getValue().toFloat(); - parameterIndex++; Axis axis = Axis::X; if (axisName == "X") { diff --git a/src/GuiQt/WbMacroCustomOperationVolumeToSurfaceCrossFade.cxx b/src/GuiQt/WbMacroCustomOperationVolumeToSurfaceCrossFade.cxx index 129fb763d..e95cfbc78 100644 --- a/src/GuiQt/WbMacroCustomOperationVolumeToSurfaceCrossFade.cxx +++ b/src/GuiQt/WbMacroCustomOperationVolumeToSurfaceCrossFade.cxx @@ -123,8 +123,11 @@ WbMacroCustomOperationVolumeToSurfaceCrossFade::executeCommand(QWidget* parent, CaretAssert(parent); CaretAssert(macroCommand); - validateCorrectNumberOfParameters(macroCommand); - + if ( ! validateCorrectNumberOfParameters(macroCommand, 1)) { + return false; + } + const float durationSeconds(macroCommand->getParameterAtIndex(0)->getValue().toFloat()); + BrainBrowserWindow* bbw = qobject_cast(parent); if (bbw == NULL) { appendToErrorMessage("Parent for running surface macro is not a browser window"); @@ -143,10 +146,6 @@ WbMacroCustomOperationVolumeToSurfaceCrossFade::executeCommand(QWidget* parent, return false; } - int32_t paramIndex(0); - const float durationSeconds(macroCommand->getParameterAtIndex(paramIndex)->getValue().toFloat()); - paramIndex++; - OverlaySet* overlaySet = tabContent->getOverlaySet(); CaretAssert(overlaySet); diff --git a/src/GuiQt/WuQMacroCustomOperationManagerInterface.h b/src/GuiQt/WuQMacroCustomOperationManagerInterface.h index 11a45517c..4a7a8939b 100644 --- a/src/GuiQt/WuQMacroCustomOperationManagerInterface.h +++ b/src/GuiQt/WuQMacroCustomOperationManagerInterface.h @@ -46,12 +46,15 @@ namespace caret { * * @param parent * Parent widget for any dialogs + * @param macroCommand + * Macro command that contains the parameter for editing * @param parameter * Parameter for editing * @return * True if the parameter was modified */ virtual bool editCustomDataValueParameter(QWidget* parent, + WuQMacroCommand* macroCommand, WuQMacroCommandParameter* parameter) = 0; /** diff --git a/src/GuiQt/WuQMacroDialog.cxx b/src/GuiQt/WuQMacroDialog.cxx index 861464114..ff9afcbe1 100644 --- a/src/GuiQt/WuQMacroDialog.cxx +++ b/src/GuiQt/WuQMacroDialog.cxx @@ -892,12 +892,14 @@ WuQMacroDialog::updateCommandWidget(WuQMacroCommand* command) } for (int32_t i = 0; i < numParams; i++) { - m_parameterWidgets[i]->updateContent(command->getParameterAtIndex(i)); + m_parameterWidgets[i]->updateContent(command, + command->getParameterAtIndex(i)); } numWidgets = static_cast(m_parameterWidgets.size()); for (int32_t i = numParams; i < numWidgets; i++) { - m_parameterWidgets[i]->updateContent(NULL); + m_parameterWidgets[i]->updateContent(NULL, + NULL); } } @@ -1665,13 +1667,17 @@ m_index(index) /** * Update content of a command parameter * + * @param macroCommand + * Macro command containing the parameter * @param parameter * The parameter */ void -CommandParameterWidget::updateContent(WuQMacroCommandParameter* parameter) +CommandParameterWidget::updateContent(WuQMacroCommand* macroCommand, + WuQMacroCommandParameter* parameter) { - m_parameter = parameter; + m_macroCommand = macroCommand; + m_parameter = parameter; const bool validFlag(m_parameter != NULL); if (validFlag) { if (parameter->getDataType() == WuQMacroDataValueTypeEnum::BOOLEAN) { @@ -1728,6 +1734,7 @@ CommandParameterWidget::pushButtonClicked() case WuQMacroDataValueTypeEnum::CUSTOM_DATA: { validFlag = WuQMacroManager::instance()->editCustomDataValueParameter(m_pushButton, + m_macroCommand, m_parameter); if (validFlag) { dataValue = m_parameter->getValue(); diff --git a/src/GuiQt/WuQMacroDialog.h b/src/GuiQt/WuQMacroDialog.h index 7e64ae415..ee56ed95e 100644 --- a/src/GuiQt/WuQMacroDialog.h +++ b/src/GuiQt/WuQMacroDialog.h @@ -248,7 +248,8 @@ namespace caret { QGridLayout* gridLayout, QWidget* parent); - void updateContent(WuQMacroCommandParameter* parameter); + void updateContent(WuQMacroCommand* macroCommand, + WuQMacroCommandParameter* parameter); QLabel* m_label; @@ -263,6 +264,8 @@ namespace caret { private: const int32_t m_index; + WuQMacroCommand* m_macroCommand = NULL; + WuQMacroCommandParameter* m_parameter = NULL; }; diff --git a/src/GuiQt/WuQMacroManager.cxx b/src/GuiQt/WuQMacroManager.cxx index f09cf106d..054dfdf9d 100644 --- a/src/GuiQt/WuQMacroManager.cxx +++ b/src/GuiQt/WuQMacroManager.cxx @@ -1094,6 +1094,8 @@ WuQMacroManager::getShortCutKeysMask() * * @param parent * Parent widget for any dialogs + * @param macroCommand + * Macro command that contains the parameter for editing * @param parameter * Parameter for editing * @return @@ -1101,12 +1103,14 @@ WuQMacroManager::getShortCutKeysMask() */ bool WuQMacroManager::editCustomDataValueParameter(QWidget* parent, - WuQMacroCommandParameter* parameter) + WuQMacroCommand* macroCommand, + WuQMacroCommandParameter* parameter) { bool modFlag(false); if (m_customCommandManager != NULL) { modFlag = m_customCommandManager->editCustomDataValueParameter(parent, + macroCommand, parameter); } else { diff --git a/src/GuiQt/WuQMacroManager.h b/src/GuiQt/WuQMacroManager.h index c7451af4f..38f20e48b 100644 --- a/src/GuiQt/WuQMacroManager.h +++ b/src/GuiQt/WuQMacroManager.h @@ -140,6 +140,7 @@ namespace caret { void macroWasModified(WuQMacro* macro); bool editCustomDataValueParameter(QWidget* parent, + WuQMacroCommand* macroCommand, WuQMacroCommandParameter* parameter); bool executeCustomOperationMacroCommand(QWidget* parent, -- GitLab From 07371b753d5a32296ff8b70996f2b4a78a5512fd Mon Sep 17 00:00:00 2001 From: John Harwell Date: Thu, 7 Mar 2019 10:46:52 -0600 Subject: [PATCH 221/427] Redesigned overlay crossfade macro command and added new data types for overlay index, file, and map. --- src/GuiQt/WbMacroCustomDataTypeEnum.cxx | 16 +- src/GuiQt/WbMacroCustomDataTypeEnum.h | 7 + ...WbMacroCustomOperationOverlayCrossFade.cxx | 374 +++++++++++++++++- .../WbMacroCustomOperationOverlayCrossFade.h | 25 +- 4 files changed, 405 insertions(+), 17 deletions(-) diff --git a/src/GuiQt/WbMacroCustomDataTypeEnum.cxx b/src/GuiQt/WbMacroCustomDataTypeEnum.cxx index 2ddcf6018..773b2b02d 100644 --- a/src/GuiQt/WbMacroCustomDataTypeEnum.cxx +++ b/src/GuiQt/WbMacroCustomDataTypeEnum.cxx @@ -106,9 +106,21 @@ WbMacroCustomDataTypeEnum::initialize() } initializedFlag = true; + enumData.push_back(WbMacroCustomDataTypeEnum(OVERLAY_INDEX, + "OVERLAY_INDEX", + "Overlay Index")); + + enumData.push_back(WbMacroCustomDataTypeEnum(OVERLAY_FILE_NAME_OR_FILE_INDEX, + "OVERLAY_FILE_NAME_OR_FILE_INDEX", + "Overlay File or File Index")); + + enumData.push_back(WbMacroCustomDataTypeEnum(OVERLAY_MAP_NAME_OR_MAP_INDEX, + "OVERLAY_MAP_NAME_OR_MAP_INDEX", + "Overlay Map Name or Index")); + enumData.push_back(WbMacroCustomDataTypeEnum(SCREEN_AXIS, - "SCREEN_AXIS", - "Screen Axis")); + "SCREEN_AXIS", + "Screen Axis")); enumData.push_back(WbMacroCustomDataTypeEnum(SURFACE, "SURFACE", diff --git a/src/GuiQt/WbMacroCustomDataTypeEnum.h b/src/GuiQt/WbMacroCustomDataTypeEnum.h index d24fab945..bf10c22fb 100644 --- a/src/GuiQt/WbMacroCustomDataTypeEnum.h +++ b/src/GuiQt/WbMacroCustomDataTypeEnum.h @@ -35,6 +35,13 @@ public: * Enumerated values. */ enum Enum { + /** Index of an overlay */ + OVERLAY_INDEX, + /** Name of file in overlay or index of file */ + OVERLAY_FILE_NAME_OR_FILE_INDEX, + /** Name of map or index of map */ + OVERLAY_MAP_NAME_OR_MAP_INDEX, + /** A Screen Axis */ SCREEN_AXIS, /** Surface selection */ SURFACE diff --git a/src/GuiQt/WbMacroCustomOperationOverlayCrossFade.cxx b/src/GuiQt/WbMacroCustomOperationOverlayCrossFade.cxx index 4e4024fcb..bf7b3da66 100644 --- a/src/GuiQt/WbMacroCustomOperationOverlayCrossFade.cxx +++ b/src/GuiQt/WbMacroCustomOperationOverlayCrossFade.cxx @@ -26,6 +26,7 @@ #include "BrainBrowserWindow.h" #include "BrowserTabContent.h" #include "CaretAssert.h" +#include "CaretMappableDataFile.h" #include "GuiManager.h" #include "Overlay.h" #include "OverlaySet.h" @@ -69,8 +70,33 @@ WbMacroCustomOperationOverlayCrossFade::~WbMacroCustomOperationOverlayCrossFade( WuQMacroCommand* WbMacroCustomOperationOverlayCrossFade::createCommand() { - const int32_t versionOne(1); + WuQMacroCommand* command(NULL); + const int32_t versionNumber(2); + switch (versionNumber) { + case 1: + command = createCommandVersionOne(); + break; + case 2: + command = createCommandVersionTwo(); + break; + } + CaretAssert(command); + + return command; +} +/** + * Get a new instance of the macro command for version one + * + * @return + * Pointer to command or NULL if not valid + * Use getErrorMessage() for error information if NULL returned + */ +WuQMacroCommand* +WbMacroCustomOperationOverlayCrossFade::createCommandVersionOne() +{ + const int32_t versionOne(1); + const QString description("Crossfade (blend) from one overlay to another:\n" "(1) The opacity of the \"Fade to Overlay\" is set to zero;\n" "(2) The opacity of the \"Fade from Overlay\" is set to one;\n" @@ -103,6 +129,67 @@ WbMacroCustomOperationOverlayCrossFade::createCommand() return command; } +/** + * Get a new instance of the macro command for version two + * + * @return + * Pointer to command or NULL if not valid + * Use getErrorMessage() for error information if NULL returned + */ +WuQMacroCommand* +WbMacroCustomOperationOverlayCrossFade::createCommandVersionTwo() +{ + const int32_t versionTwo(2); + + const QString description("Crossfade (blend) from file in overlay to fade file/map:\n" + "(1) A copy of the selected overlay is inserted below it; \n" + "(2) Selected file/map is placed in new the overlay;\n" + "(3) The opacity of selected overlay is decreased until it\n" + " becomes 0.0 revealing the selected file/map;\n" + "(4) The selected overlay is removed;" + "(5) The selected file/map remains displayed in its overlay\n"); + + WuQMacroCommandParameter* paramSurfaceOne = new WuQMacroCommandParameter(WuQMacroDataValueTypeEnum::CUSTOM_DATA, + "Overlay Number", + 1); + paramSurfaceOne->setCustomDataType(WbMacroCustomDataTypeEnum::toName(WbMacroCustomDataTypeEnum::OVERLAY_INDEX)); + + WuQMacroCommandParameter* paramSurfaceTwo = new WuQMacroCommandParameter(WuQMacroDataValueTypeEnum::CUSTOM_DATA, + "Fade to File", + ""); + paramSurfaceTwo->setCustomDataType(WbMacroCustomDataTypeEnum::toName(WbMacroCustomDataTypeEnum::OVERLAY_FILE_NAME_OR_FILE_INDEX)); + + WuQMacroCommandParameter* paramSurfaceThree = new WuQMacroCommandParameter(WuQMacroDataValueTypeEnum::CUSTOM_DATA, + "Fade to Map", + ""); + paramSurfaceThree->setCustomDataType(WbMacroCustomDataTypeEnum::toName(WbMacroCustomDataTypeEnum::OVERLAY_MAP_NAME_OR_MAP_INDEX)); + + + QString errorMessage; + WuQMacroCommand* command = WuQMacroCommand::newInstanceCustomCommand(WbMacroCustomOperationTypeEnum::toName(getOperationType()), + versionTwo, + "none", + "Overlay CrossFade", + description, + 1.0, + errorMessage); + if (command != NULL) { + command->addParameter(paramSurfaceOne); + command->addParameter(paramSurfaceTwo); + command->addParameter(paramSurfaceThree); + command->addParameter(WuQMacroDataValueTypeEnum::FLOAT, + "Duration (secs)", + (float)2.0); + } + else { + appendToErrorMessage(errorMessage); + } + + return command; + +} + + /** * Execute the macro command * @@ -116,13 +203,58 @@ WbMacroCustomOperationOverlayCrossFade::createCommand() */ bool WbMacroCustomOperationOverlayCrossFade::executeCommand(QWidget* parent, - const WuQMacroCommand* macroCommand) + const WuQMacroCommand* macroCommand) { CaretAssert(parent); CaretAssert(macroCommand); - validateCorrectNumberOfParameters(macroCommand); + bool resultFlag(false); + switch (macroCommand->getVersion()) { + case 1: + resultFlag = executeCommandVersionOne(parent, + macroCommand); + break; + case 2: + resultFlag = executeCommandVersionTwo(parent, + macroCommand); + break; + default: + appendUnsupportedVersionToErrorMessage(macroCommand->getVersion()); + break; + } + return resultFlag; +} + +/** + * Execute the macro command + * + * @param parent + * Parent widget for any dialogs + * @param macroCommand + * macro command to run + * @return + * True if command executed successfully, else false + * Use getErrorMessage() for error information if false returned + */ +bool +WbMacroCustomOperationOverlayCrossFade::executeCommandVersionTwo(QWidget* parent, + const WuQMacroCommand* macroCommand) +{ + CaretAssert(parent); + CaretAssert(macroCommand); + + if ( ! validateCorrectNumberOfParameters(macroCommand, 4)) { + return false; + } + /* + * Get overlay indices and subtract one from them since they stat at 1 + */ + const int32_t overlayIndex(macroCommand->getParameterAtIndex(0)->getValue().toInt() - 1); + const QString mapFileName(macroCommand->getParameterAtIndex(1)->getValue().toString()); + const QString mapName(macroCommand->getParameterAtIndex(2)->getValue().toString()); + const float durationSeconds(macroCommand->getParameterAtIndex(3)->getValue().toFloat()); + BrainBrowserWindow* bbw = qobject_cast(parent); if (bbw == NULL) { appendToErrorMessage("Parent for running surface macro is not a browser window"); @@ -159,12 +291,232 @@ WbMacroCustomOperationOverlayCrossFade::executeCommand(QWidget* parent, return false; } + OverlaySet* overlaySet = tabContent->getOverlaySet(); + CaretAssert(overlaySet); + const int32_t numberOfOverlays = overlaySet->getNumberOfDisplayedOverlays(); + if ((overlayIndex < 0) + || (overlayIndex >= numberOfOverlays)) { + appendToErrorMessage("Overlay index is invalid."); + } + if (overlayIndex == (BrainConstants::MAXIMUM_NUMBER_OF_OVERLAYS - 1)) { + appendToErrorMessage("Selected overlay cannot be the last (bottom-most) overlay"); + } + if (mapFileName.isEmpty()) { + appendToErrorMessage("Map File Name is empty."); + } + if (mapName.isEmpty()) { + appendToErrorMessage("Map Name is empty."); + } + if ( ! getErrorMessage().isEmpty()) { + return false; + } + + Overlay* overlay = overlaySet->getOverlay(overlayIndex); + if ( ! overlay->isEnabled()) { + overlay->setEnabled(true); + } + + std::vector mapFiles; + CaretMappableDataFile* selectedMapFile(NULL); + int32_t selectedMapIndex(-1); + overlay->getSelectionData(mapFiles, + selectedMapFile, + selectedMapIndex); + + CaretMappableDataFile* fadeToMapFile(NULL); + for (auto mf : mapFiles) { + if (mf->getFileName().endsWith(mapFileName)) { + fadeToMapFile = mf; + break; + } + } + if (fadeToMapFile == NULL) { + appendToErrorMessage("Unable to find data file with name " + + mapFileName); + return false; + } + + int32_t fadeToMapIndex(-1); + const int32_t numMaps = fadeToMapFile->getNumberOfMaps(); + for (int32_t i = 0; i < numMaps; i++) { + if (mapName == fadeToMapFile->getMapName(i)) { + fadeToMapIndex = i; + break; + } + } + if (fadeToMapIndex < 0) { + appendToErrorMessage("Unable to find map named " + + mapName); + return false; + } + + const bool successFlag = performCrossFadeVersionTwo(overlaySet, + overlayIndex, + fadeToMapFile, + fadeToMapIndex, + durationSeconds); + + return successFlag; +} + +/** + * Perform crossfade version two + * + * @param overlaySet + * OverlaySet for the in the tab + * @param overlayIndex + * Index of the overlay that fades off + * @param fadeToMapFile + * File that is fades on + * @param fadeToMapIndex + * Index of map in file that fades on + * @param durationSeconds + * Total duration for cross fade + * @return + * True if successful, else false + */ +bool +WbMacroCustomOperationOverlayCrossFade::performCrossFadeVersionTwo(OverlaySet* overlaySet, + const int32_t overlayIndex, + CaretMappableDataFile* fadeToMapFile, + const int32_t fadeToMapIndex, + const float durationSeconds) +{ + CaretAssert(overlaySet); + CaretAssert(fadeToMapFile); + CaretAssert((fadeToMapIndex >= 0) + && (fadeToMapIndex < fadeToMapFile->getNumberOfMaps())); + + /* + * Insert a new overlay below the fade-from overlay + */ + overlaySet->insertOverlayBelow(overlayIndex); + + /* + * Copy content of fade-from overlay to the new fade-to overlay + */ + Overlay* fadeFromOverlay = overlaySet->getOverlay(overlayIndex); + const int32_t nextOverlayIndex = overlayIndex + 1; + Overlay* fadeToOverlay = overlaySet->getOverlay(nextOverlayIndex); + fadeToOverlay->copyData(fadeFromOverlay); + + /* + * Load fade-to file and map into the new overlay + */ + fadeToOverlay->setSelectionData(fadeToMapFile, + fadeToMapIndex); + + const float defaultNumberOfSteps(25.0); + float numberOfSteps(0.0); + float iterationSleepTime(0.0); + getNumberOfStepsAndSleepTime(defaultNumberOfSteps, + durationSeconds, + numberOfSteps, + iterationSleepTime); + + float fadeFromOpacity(fadeFromOverlay->getOpacity()); + const float opacityDelta = fadeFromOpacity / numberOfSteps; + + fadeToOverlay->setEnabled(true); + fadeFromOverlay->setEnabled(true); + + /* + * Iterate while decreasing the opacity of the fade-from overlay + */ + for (int iStep = 0; iStep < numberOfSteps; iStep++) { + fadeFromOverlay->setOpacity(fadeFromOpacity); + updateSurfaceColoring(); + updateUserInterface(); + updateGraphics(); + + fadeFromOpacity -= opacityDelta; + if (fadeFromOpacity < 0.0) { + fadeFromOpacity = 0.0; + } + + sleepForSecondsAtEndOfIteration(iterationSleepTime); + } + + fadeFromOverlay->setOpacity(0.0); + + /* + * Remove the fade-from overlay so that the fade-to overlay is visible + */ + overlaySet->removeDisplayedOverlay(overlayIndex); + + updateSurfaceColoring(); + updateUserInterface(); + updateGraphics(); + + return true; + +} + + + +/** + * Execute the macro command + * + * @param parent + * Parent widget for any dialogs + * @param macroCommand + * macro command to run + * @return + * True if command executed successfully, else false + * Use getErrorMessage() for error information if false returned + */ +bool +WbMacroCustomOperationOverlayCrossFade::executeCommandVersionOne(QWidget* parent, + const WuQMacroCommand* macroCommand) +{ + CaretAssert(parent); + CaretAssert(macroCommand); + + if ( ! validateCorrectNumberOfParameters(macroCommand, 3)) { + return false; + } /* * Get overlay indices and subtract one from them since they stat at 1 */ const int32_t fadeToOverlayIndex(macroCommand->getParameterAtIndex(0)->getValue().toInt() - 1); const int32_t fadeFromOverlayIndex(macroCommand->getParameterAtIndex(1)->getValue().toInt() - 1); const float durationSeconds(macroCommand->getParameterAtIndex(2)->getValue().toFloat()); + + BrainBrowserWindow* bbw = qobject_cast(parent); + if (bbw == NULL) { + appendToErrorMessage("Parent for running surface macro is not a browser window"); + return false; + } + + BrowserTabContent* tabContent = bbw->getBrowserTabContent(); + if (tabContent == NULL) { + appendToErrorMessage("No tab is selected in browser window"); + return false; + } + + Model* model = tabContent->getModelForDisplay(); + if (model == NULL) { + appendToErrorMessage("No model is displayed"); + return false; + } + + bool validModelTypeFlag(false); + switch (model->getModelType()) { + case ModelTypeEnum::MODEL_TYPE_CHART: + case ModelTypeEnum::MODEL_TYPE_CHART_TWO: + case ModelTypeEnum::MODEL_TYPE_INVALID: + break; + case ModelTypeEnum::MODEL_TYPE_SURFACE: + case ModelTypeEnum::MODEL_TYPE_SURFACE_MONTAGE: + case ModelTypeEnum::MODEL_TYPE_VOLUME_SLICES: + case ModelTypeEnum::MODEL_TYPE_WHOLE_BRAIN: + validModelTypeFlag = true; + break; + } + if ( ! validModelTypeFlag) { + appendToErrorMessage("For Overlay CrossFace, model must be a brain model"); + return false; + } OverlaySet* overlaySet = tabContent->getOverlaySet(); CaretAssert(overlaySet); @@ -212,15 +564,15 @@ WbMacroCustomOperationOverlayCrossFade::executeCommand(QWidget* parent, return false; } - bool successFlag = performCrossFade(fadeToOverlay, - fadeFromOverlay, - durationSeconds); - + bool successFlag = performCrossFadeVersionOne(fadeToOverlay, + fadeFromOverlay, + durationSeconds); + return successFlag; } /** - * Interpolate from starting to ending surface + * Perform crossfade version one * * @param fadeToOverlay * Overlay that starts with opacity zero and increases to one @@ -232,9 +584,9 @@ WbMacroCustomOperationOverlayCrossFade::executeCommand(QWidget* parent, * True if successful, else false */ bool -WbMacroCustomOperationOverlayCrossFade::performCrossFade(Overlay* fadeToOverlay, - Overlay* fadeFromOverlay, - const float durationSeconds) +WbMacroCustomOperationOverlayCrossFade::performCrossFadeVersionOne(Overlay* fadeToOverlay, + Overlay* fadeFromOverlay, + const float durationSeconds) { CaretAssert(fadeToOverlay); CaretAssert(fadeFromOverlay); diff --git a/src/GuiQt/WbMacroCustomOperationOverlayCrossFade.h b/src/GuiQt/WbMacroCustomOperationOverlayCrossFade.h index a06331cf9..76a584060 100644 --- a/src/GuiQt/WbMacroCustomOperationOverlayCrossFade.h +++ b/src/GuiQt/WbMacroCustomOperationOverlayCrossFade.h @@ -28,8 +28,9 @@ #include "WbMacroCustomOperationBase.h" namespace caret { - + class CaretMappableDataFile; class Overlay; + class OverlaySet; class WbMacroCustomOperationOverlayCrossFade : public WbMacroCustomOperationBase { @@ -52,10 +53,26 @@ namespace caret { // ADD_NEW_METHODS_HERE private: - bool performCrossFade(Overlay* fadeToOverlay, - Overlay* fadeFromOverlay, - const float durationSeconds); + WuQMacroCommand* createCommandVersionOne(); + + WuQMacroCommand* createCommandVersionTwo(); + + virtual bool executeCommandVersionTwo(QWidget* parent, + const WuQMacroCommand* macroCommand); + bool performCrossFadeVersionTwo(OverlaySet* overlaySet, + const int32_t overlayIndex, + CaretMappableDataFile* fadeToMapFile, + const int32_t fadeToMapIndex, + const float durationSeconds); + + virtual bool executeCommandVersionOne(QWidget* parent, + const WuQMacroCommand* macroCommand); + + bool performCrossFadeVersionOne(Overlay* fadeToOverlay, + Overlay* fadeFromOverlay, + const float durationSeconds); + // ADD_NEW_MEMBERS_HERE -- GitLab From 5fe7fec32cb21eac1e998c259cbd5682b1ca8dee Mon Sep 17 00:00:00 2001 From: John Harwell Date: Fri, 8 Mar 2019 09:22:23 -0600 Subject: [PATCH 222/427] Add Copy Macro to Macro Dialog's insert menu. Copy Macro displays a dialog that allows the user to select a macro group and then a macro within the group. If the user clicks OK, the a copy of the macro in added to the macro group in the macro dialog. --- src/GuiQt/CMakeLists.txt | 3 + src/GuiQt/WbMacroHelper.cxx | 36 +++- src/GuiQt/WbMacroHelper.h | 4 +- src/GuiQt/WuQMacroCopyDialog.cxx | 247 ++++++++++++++++++++++++++++ src/GuiQt/WuQMacroCopyDialog.h | 88 ++++++++++ src/GuiQt/WuQMacroCreateDialog.cxx | 2 +- src/GuiQt/WuQMacroDialog.cxx | 147 +++++++++++++---- src/GuiQt/WuQMacroDialog.h | 4 + src/GuiQt/WuQMacroHelperInterface.h | 12 +- src/GuiQt/WuQMacroManager.cxx | 25 ++- src/GuiQt/WuQMacroManager.h | 4 +- 11 files changed, 526 insertions(+), 46 deletions(-) create mode 100644 src/GuiQt/WuQMacroCopyDialog.cxx create mode 100644 src/GuiQt/WuQMacroCopyDialog.h diff --git a/src/GuiQt/CMakeLists.txt b/src/GuiQt/CMakeLists.txt index ec3fa2bc7..272780b98 100644 --- a/src/GuiQt/CMakeLists.txt +++ b/src/GuiQt/CMakeLists.txt @@ -206,6 +206,7 @@ ELSE() WuQGroupBoxExclusiveWidget.h WuQImageLabel.h WuQListWidget.h + WuQMacroCopyDialog.h WuQMacroCreateDialog.h WuQMacroDialog.h WuQMacroExecutor.h @@ -476,6 +477,7 @@ WuQGridLayoutGroup.h WuQGroupBoxExclusiveWidget.h WuQImageLabel.h WuQListWidget.h +WuQMacroCopyDialog.h WuQMacroCreateDialog.h WuQMacroCustomOperationManagerInterface.h WuQMacroDialog.h @@ -735,6 +737,7 @@ WuQGridLayoutGroup.cxx WuQGroupBoxExclusiveWidget.cxx WuQImageLabel.cxx WuQListWidget.cxx +WuQMacroCopyDialog.cxx WuQMacroCreateDialog.cxx WuQMacroDialog.cxx WuQMacroExecutor.cxx diff --git a/src/GuiQt/WbMacroHelper.cxx b/src/GuiQt/WbMacroHelper.cxx index 594e51d05..fd16a7c31 100644 --- a/src/GuiQt/WbMacroHelper.cxx +++ b/src/GuiQt/WbMacroHelper.cxx @@ -28,6 +28,7 @@ #include "CaretAssert.h" #include "CaretLogger.h" #include "CaretPreferences.h" +#include "EventCaretDataFilesGet.h" #include "EventGraphicsUpdateAllWindows.h" #include "EventGraphicsUpdateOneWindow.h" #include "EventManager.h" @@ -91,10 +92,12 @@ WbMacroHelper::receiveEvent(Event* event) } /** - * @return All available macro groups + * @return All 'active' available macro groups. + * Macros groups that are editible. Other macro + * groups are exluded. */ std::vector -WbMacroHelper::getMacroGroups() +WbMacroHelper::getActiveMacroGroups() { std::vector macroGroups; @@ -117,6 +120,35 @@ WbMacroHelper::getMacroGroups() return macroGroups; } +/** + * @return All macro groups including those that are + * be valid (editable) at this time. + */ +std::vector +WbMacroHelper::getAllMacroGroups() const +{ + std::vector macroGroups; + + const bool includePreferencesFlag(false); + if (includePreferencesFlag) { + CaretPreferences* preferences = SessionManager::get()->getCaretPreferences(); + CaretAssert(preferences); + macroGroups.push_back(preferences->getMacros()); + } + + const auto sceneFiles = EventCaretDataFilesGet::getCaretDataFilesForType(DataFileTypeEnum::SCENE); + for (const auto dataFile : sceneFiles) { + const SceneFile* sceneFile = dynamic_cast(dataFile); + CaretAssert(sceneFile); + const int32_t numberOfScenes = sceneFile->getNumberOfScenes(); + for (int32_t i = 0; i < numberOfScenes; i++) { + macroGroups.push_back(sceneFile->getSceneAtIndex(i)->getMacroGroup()); + } + } + + return macroGroups; +} + /** * Is called when the given macro is modified * diff --git a/src/GuiQt/WbMacroHelper.h b/src/GuiQt/WbMacroHelper.h index 201883fbd..d81d20590 100644 --- a/src/GuiQt/WbMacroHelper.h +++ b/src/GuiQt/WbMacroHelper.h @@ -46,7 +46,9 @@ namespace caret { void receiveEvent(Event* event) override; - virtual std::vector getMacroGroups() override; + virtual std::vector getActiveMacroGroups(); + + virtual std::vector getAllMacroGroups() const; virtual void macroWasModified(WuQMacro* macro) override; diff --git a/src/GuiQt/WuQMacroCopyDialog.cxx b/src/GuiQt/WuQMacroCopyDialog.cxx new file mode 100644 index 000000000..a6c530c8b --- /dev/null +++ b/src/GuiQt/WuQMacroCopyDialog.cxx @@ -0,0 +1,247 @@ + +/*LICENSE_START*/ +/* + * Copyright (C) 2018 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + +#define __WU_Q_MACRO_COPY_DIALOG_DECLARE__ +#include "WuQMacroCopyDialog.h" +#undef __WU_Q_MACRO_COPY_DIALOG_DECLARE__ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "CaretAssert.h" +#include "CaretLogger.h" +#include "WuQMacro.h" +#include "WuQMacroGroup.h" +#include "WuQMacroManager.h" + +using namespace caret; + +/** + * \class caret::WuQMacroCopyDialog + * \brief Dialog for creating a new macro + * \ingroup WuQMacro + */ + +/** + * Constructor. + * + * @param parent + * The parent widget + */ +WuQMacroCopyDialog::WuQMacroCopyDialog(QWidget* parent) +: QDialog(parent) +{ + setWindowTitle("Copy Macro"); + + m_macroGroups = WuQMacroManager::instance()->getAllMacroGroups(); + + QLabel* nameLabel = new QLabel("Macro:"); + QLabel* descriptionLabel = new QLabel("Description:"); + QLabel* macroGroupLabel = new QLabel("Macro From:"); + + m_macroDescriptionTextEdit = new QPlainTextEdit(); + m_macroDescriptionTextEdit->setFixedHeight(100); + m_macroDescriptionTextEdit->setReadOnly(true); + + int32_t selectedMacroGroupIndex(-1); + m_macroGroupComboBox = new QComboBox(); + QObject::connect(m_macroGroupComboBox, QOverload::of(&QComboBox::activated), + this, &WuQMacroCopyDialog::macroGroupComboBoxItemActivated); + for (auto mg : m_macroGroups) { + if ( ! mg->isEmpty()) { + if (mg->getUniqueIdentifier() == s_lastSelectedMacroGroupIdentifier) { + selectedMacroGroupIndex = m_macroGroupComboBox->count(); + } + m_macroGroupComboBox->addItem(mg->getName()); + } + } + if (selectedMacroGroupIndex < 0) { + selectedMacroGroupIndex = m_macroGroupComboBox->count() - 1; + } + if ((selectedMacroGroupIndex >= 0) + && (selectedMacroGroupIndex < m_macroGroupComboBox->count())) { + m_macroGroupComboBox->setCurrentIndex(selectedMacroGroupIndex); + } + + m_macroNameComboBox = new QComboBox(); + QObject::connect(m_macroNameComboBox, QOverload::of(&QComboBox::activated), + this, &WuQMacroCopyDialog::macroComboBoxItemActivated); + + + + QGridLayout* gridLayout = new QGridLayout(); + gridLayout->setColumnStretch(0, 0); + gridLayout->setColumnStretch(1, 0); + gridLayout->setColumnStretch(2, 0); + gridLayout->setColumnStretch(3, 100); + int row = 0; + gridLayout->addWidget(macroGroupLabel, row, 0); + gridLayout->addWidget(m_macroGroupComboBox, row, 1, 1, 3); + row++; + gridLayout->addWidget(nameLabel, row, 0); + gridLayout->addWidget(m_macroNameComboBox, row, 1, 1, 3); + row++; + gridLayout->addWidget(descriptionLabel, row, 0); + gridLayout->addWidget(m_macroDescriptionTextEdit, row, 1, 1, 3); + row++; + + m_dialogButtonBox = new QDialogButtonBox(QDialogButtonBox::Ok + | QDialogButtonBox::Cancel); + QObject::connect(m_dialogButtonBox, &QDialogButtonBox::accepted, + this, &WuQMacroCopyDialog::accept); + QObject::connect(m_dialogButtonBox, &QDialogButtonBox::rejected, + this, &WuQMacroCopyDialog::reject); + + QVBoxLayout* dialogLayout = new QVBoxLayout(this); + dialogLayout->addLayout(gridLayout, 100); + dialogLayout->addWidget(m_dialogButtonBox); + + setFixedHeight(sizeHint().height()); + + macroGroupComboBoxItemActivated(m_macroGroupComboBox->currentIndex()); +} + +/** + * Destructor. + */ +WuQMacroCopyDialog::~WuQMacroCopyDialog() +{ +} + +/** + * Called when macro group is selected from combo box + * + * @param index + * Index of item selected + */ +void +WuQMacroCopyDialog::macroGroupComboBoxItemActivated(int /*index*/) +{ + m_macroNameComboBox->clear(); + + const WuQMacroGroup* mg = getMacroGroup(); + if (mg != NULL) { + const int32_t numMacros = mg->getNumberOfMacros(); + for (int32_t i = 0; i < numMacros; i++) { + m_macroNameComboBox->addItem(mg->getMacroAtIndex(i)->getName()); + } + } + macroComboBoxItemActivated(m_macroNameComboBox->currentIndex()); +} + +/** + * Called when macro group is selected from combo box + * + * @param index + * Index of item selected + */ +void +WuQMacroCopyDialog::macroComboBoxItemActivated(int /*index*/) +{ + QString text; + const WuQMacro* macro = getMacroToCopy(); + if (macro != NULL) { + text = macro->getDescription(); + } + m_macroDescriptionTextEdit->setPlainText(text); +} + +/** + * Called when user clicks OK or Cancel + * + * @param r + * The dialog code (Accepted or Rejected) + */ +void +WuQMacroCopyDialog::done(int r) +{ + if (r == QDialog::Accepted) { +// const QString name(m_macroNameLineEdit->text().trimmed()); +// if (name.isEmpty()) { +// QMessageBox::critical(this, +// "Error", +// "Name is missing", +// QMessageBox::Ok, +// QMessageBox::Ok); +// return; +// } +// +// m_macro = new WuQMacro(); +// m_macro->setName(name); +// m_macro->setDescription(m_macroDescriptionTextEdit->toPlainText()); +// +// const int32_t groupIndex = m_macroGroupComboBox->currentIndex(); +// if (groupIndex >= 0) { +// WuQMacroGroup* macroGroup = m_macroGroups[groupIndex]; +// macroGroup->addMacro(m_macro); +// s_lastSelectedMacroGroupIdentifier = macroGroup->getUniqueIdentifier(); +// } + } + + QDialog::done(r); +} + +/** + * @return Pointer to selected macro group or NULL if none available + */ +const WuQMacroGroup* +WuQMacroCopyDialog::getMacroGroup() const +{ + const WuQMacroGroup* macroGroup(NULL); + const int32_t groupIndex = m_macroGroupComboBox->currentIndex(); + if ((groupIndex >= 0) + && (groupIndex < static_cast(m_macroGroups.size()))) { + CaretAssertVectorIndex(m_macroGroups, groupIndex); + macroGroup = m_macroGroups[groupIndex]; + CaretAssert(macroGroup); + } + + return macroGroup; +} + +/** + * @return Pointer to macro that was selected and should be copied. + * NULL if no macro to copy. + */ +const WuQMacro* +WuQMacroCopyDialog::getMacroToCopy() const +{ + const WuQMacro* macro(NULL); + + const WuQMacroGroup* mg = getMacroGroup(); + if (mg != NULL) { + const int32_t macroIndex = m_macroNameComboBox->currentIndex(); + if ((macroIndex >= 0) + && (macroIndex < mg->getNumberOfMacros())) { + macro = mg->getMacroAtIndex(macroIndex); + CaretAssert(macro); + } + } + + return macro; +} + diff --git a/src/GuiQt/WuQMacroCopyDialog.h b/src/GuiQt/WuQMacroCopyDialog.h new file mode 100644 index 000000000..f90bff48d --- /dev/null +++ b/src/GuiQt/WuQMacroCopyDialog.h @@ -0,0 +1,88 @@ +#ifndef __WU_Q_MACRO_COPY_DIALOG_H__ +#define __WU_Q_MACRO_COPY_DIALOG_H__ + +/*LICENSE_START*/ +/* + * Copyright (C) 2018 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + + + +#include + +#include + +class QComboBox; +class QDialogButtonBox; +class QLineEdit; +class QPlainTextEdit; + + +namespace caret { + + class WuQMacro; + class WuQMacroGroup; + class WuQMacroShortCutKeyComboBox; + + class WuQMacroCopyDialog : public QDialog { + + Q_OBJECT + + public: + WuQMacroCopyDialog(QWidget* parent = 0); + + virtual ~WuQMacroCopyDialog(); + + WuQMacroCopyDialog(const WuQMacroCopyDialog&) = delete; + + WuQMacroCopyDialog& operator=(const WuQMacroCopyDialog&) = delete; + + const WuQMacro* getMacroToCopy() const; + + // ADD_NEW_METHODS_HERE + + public slots: + virtual void done(int r) override; + + private slots: + void macroGroupComboBoxItemActivated(int); + + void macroComboBoxItemActivated(int); + + private: + const WuQMacroGroup* getMacroGroup() const; + + std::vector m_macroGroups; + + QComboBox* m_macroGroupComboBox; + + QComboBox* m_macroNameComboBox; + + QPlainTextEdit* m_macroDescriptionTextEdit; + + QDialogButtonBox* m_dialogButtonBox; + + static QString s_lastSelectedMacroGroupIdentifier; + }; + +#ifdef __WU_Q_MACRO_COPY_DIALOG_DECLARE__ + QString WuQMacroCopyDialog::s_lastSelectedMacroGroupIdentifier = ""; +#endif // __WU_Q_MACRO_COPY_DIALOG_DECLARE__ + +} // namespace +#endif //__WU_Q_MACRO_COPY_DIALOG_H__ diff --git a/src/GuiQt/WuQMacroCreateDialog.cxx b/src/GuiQt/WuQMacroCreateDialog.cxx index d90b9d1ac..fd92eef55 100644 --- a/src/GuiQt/WuQMacroCreateDialog.cxx +++ b/src/GuiQt/WuQMacroCreateDialog.cxx @@ -60,7 +60,7 @@ WuQMacroCreateDialog::WuQMacroCreateDialog(QWidget* parent) { setWindowTitle("Record Macro"); - m_macroGroups = WuQMacroManager::instance()->getMacroGroups(); + m_macroGroups = WuQMacroManager::instance()->getActiveMacroGroups(); QLabel* nameLabel = new QLabel("Macro name:"); QLabel* shortCutKeyLabel = new QLabel("Short Cut Key:"); diff --git a/src/GuiQt/WuQMacroDialog.cxx b/src/GuiQt/WuQMacroDialog.cxx index ff9afcbe1..e01b9315d 100644 --- a/src/GuiQt/WuQMacroDialog.cxx +++ b/src/GuiQt/WuQMacroDialog.cxx @@ -50,6 +50,7 @@ #include "CaretAssert.h" #include "CaretLogger.h" #include "WuQMacro.h" +#include "WuQMacroCopyDialog.h" #include "WuQMacroCommand.h" #include "WuQMacroGroup.h" #include "WuQMacroExecutor.h" @@ -82,7 +83,7 @@ WuQMacroDialog::WuQMacroDialog(QWidget* parent) setWindowTitle("Macros"); this->setAttribute(Qt::WA_DeleteOnClose, false); - m_macroGroups = WuQMacroManager::instance()->getMacroGroups(); + m_macroGroups = WuQMacroManager::instance()->getActiveMacroGroups(); QLabel* macroGroupLabel = new QLabel("Macros in:"); @@ -611,7 +612,7 @@ WuQMacroDialog::updateDialogContents() } } - m_macroGroups = WuQMacroManager::instance()->getMacroGroups(); + m_macroGroups = WuQMacroManager::instance()->getActiveMacroGroups(); m_macroGroupComboBox->clear(); for (auto mg : m_macroGroups) { @@ -1328,43 +1329,114 @@ WuQMacroDialog::editingInsertToolButtonClicked() return; } - bool addMacroFlag(true); - bool addMacroCommandFlag(false); + bool insertMacroCommandValidFlag(false); + bool copyAndInsertMacroValidFlag(false); + const std::vector allGroups = WuQMacroManager::instance()->getAllMacroGroups(); + for (const auto mg : allGroups) { + if (mg->getNumberOfMacros() > 0) { + copyAndInsertMacroValidFlag = true; + break; + } + } switch (getSelectedItemType()) { case WuQMacroStandardItemTypeEnum::INVALID: break; case WuQMacroStandardItemTypeEnum::MACRO: - addMacroCommandFlag = true; + insertMacroCommandValidFlag = true; break; case WuQMacroStandardItemTypeEnum::MACRO_COMMAND: - addMacroCommandFlag = true; + insertMacroCommandValidFlag = true; break; } QMenu menu(m_editingInsertToolButton); - if (addMacroFlag) { - menu.addAction("Insert New Macro Below...", - this, - &WuQMacroDialog::insertMenuNewMacroSelected); - } - if (addMacroCommandFlag) { - menu.addAction("Insert New Command Below...", - this, - &WuQMacroDialog::insertMenuNewMacroCommandSelected); + + /* + * Macro command items + */ + QAction* insertNewCommandAction = menu.addAction("Insert New Command Below...", + this, + &WuQMacroDialog::insertMenuNewMacroCommandSelected); + insertNewCommandAction->setEnabled(insertMacroCommandValidFlag); + + /* + * Macro items + */ + if (menu.actions().count() > 0) { + menu.addSeparator(); } + + QAction* copyMacroAction = menu.addAction("Copy and Insert Macro Below...", + this, + &WuQMacroDialog::insertMenuCopyMacroSelected); + copyMacroAction->setEnabled(copyAndInsertMacroValidFlag); + + menu.addAction("Insert New Macro Below...", + this, + &WuQMacroDialog::insertMenuNewMacroSelected); + menu.exec(m_editingInsertToolButton->mapToGlobal(QPoint(0, 0))); } /** - * Called when insert new macro menu item selected + * Called when copy and insert macro menu item is selected */ void -WuQMacroDialog::insertMenuNewMacroSelected() +WuQMacroDialog::insertMenuCopyMacroSelected() +{ + WuQMacroCopyDialog dialog(this); + if (dialog.exec() == WuQMacroCopyDialog::Accepted) { + const WuQMacro* macro = dialog.getMacroToCopy(); + if (macro != NULL) { + WuQMacro* newMacro = new WuQMacro(*macro); + newMacro->setName("Copy of " + + macro->getName()); + insertNewMacro(newMacro); + } + } +} + +/** + * Insert a new macro + * + * @param macro + * Macro to insert, must be valid + */ +void +WuQMacroDialog::insertNewMacro(WuQMacro* macro) { + CaretAssert(macro); + + const WuQMacro* selectedMacro = getSelectedMacro(); WuQMacroGroup* macroGroup = getSelectedMacroGroup(); CaretAssert(macroGroup); + if (selectedMacro != NULL) { + const int32_t index = macroGroup->getIndexOfMacro(selectedMacro); + macroGroup->insertMacroAtIndex(index + 1, + macro); + } + else { + macroGroup->addMacro(macro); + } + updateDialogContents(); + + QModelIndex selectedIndex = macroGroup->indexFromItem(macro); + if (selectedIndex.isValid()) { + m_treeView->setCurrentIndex(selectedIndex); + updateDialogContents(); + } + + WuQMacroManager::instance()->macroWasModified(macro); +} + +/** + * Called when insert new macro menu item selected + */ +void +WuQMacroDialog::insertMenuNewMacroSelected() +{ bool okFlag(false); const QString macroName = QInputDialog::getText(m_editingInsertToolButton, "Create Macro", "New Macro Name", QLineEdit::Normal, @@ -1372,26 +1444,31 @@ WuQMacroDialog::insertMenuNewMacroSelected() &okFlag); if (okFlag) { if ( ! macroName.isEmpty()) { - const WuQMacro* selectedMacro = getSelectedMacro(); WuQMacro* macro = new WuQMacro(); macro->setName(macroName); - if (selectedMacro != NULL) { - const int32_t index = macroGroup->getIndexOfMacro(selectedMacro); - macroGroup->insertMacroAtIndex(index + 1, - macro); - } - else { - macroGroup->addMacro(macro); - } - updateDialogContents(); - - QModelIndex selectedIndex = macroGroup->indexFromItem(macro); - if (selectedIndex.isValid()) { - m_treeView->setCurrentIndex(selectedIndex); - updateDialogContents(); - } - - WuQMacroManager::instance()->macroWasModified(macro); + insertNewMacro(macro); + +// const WuQMacro* selectedMacro = getSelectedMacro(); +// WuQMacroGroup* macroGroup = getSelectedMacroGroup(); +// CaretAssert(macroGroup); +// +// if (selectedMacro != NULL) { +// const int32_t index = macroGroup->getIndexOfMacro(selectedMacro); +// macroGroup->insertMacroAtIndex(index + 1, +// macro); +// } +// else { +// macroGroup->addMacro(macro); +// } +// updateDialogContents(); +// +// QModelIndex selectedIndex = macroGroup->indexFromItem(macro); +// if (selectedIndex.isValid()) { +// m_treeView->setCurrentIndex(selectedIndex); +// updateDialogContents(); +// } +// +// WuQMacroManager::instance()->macroWasModified(macro); } } } diff --git a/src/GuiQt/WuQMacroDialog.h b/src/GuiQt/WuQMacroDialog.h index ee56ed95e..94c00cb31 100644 --- a/src/GuiQt/WuQMacroDialog.h +++ b/src/GuiQt/WuQMacroDialog.h @@ -113,6 +113,8 @@ namespace caret { void commandParamaterDataChanged(int); + void insertMenuCopyMacroSelected(); + void insertMenuNewMacroSelected(); void insertMenuNewMacroCommandSelected(); @@ -172,6 +174,8 @@ namespace caret { void treeItemSelected(const QModelIndex& modelIndex); + void insertNewMacro(WuQMacro* macro); + std::vector m_macroGroups; QComboBox* m_macroGroupComboBox; diff --git a/src/GuiQt/WuQMacroHelperInterface.h b/src/GuiQt/WuQMacroHelperInterface.h index f3e3cf8b5..e3d569c18 100644 --- a/src/GuiQt/WuQMacroHelperInterface.h +++ b/src/GuiQt/WuQMacroHelperInterface.h @@ -52,10 +52,18 @@ namespace caret { WuQMacroHelperInterface& operator=(const WuQMacroHelperInterface&) = delete; /** - * @return All available macro groups + * @return All 'active' available macro groups. + * Macros groups that are editible. Other macro + * groups are exluded. */ - virtual std::vector getMacroGroups() = 0; + virtual std::vector getActiveMacroGroups() = 0; + /** + * @return All macro groups including those that are + * be valid (editable) at this time. + */ + virtual std::vector getAllMacroGroups() const = 0; + /** * Is called when the given macro is modified * diff --git a/src/GuiQt/WuQMacroManager.cxx b/src/GuiQt/WuQMacroManager.cxx index 054dfdf9d..0e2e2d2b6 100644 --- a/src/GuiQt/WuQMacroManager.cxx +++ b/src/GuiQt/WuQMacroManager.cxx @@ -415,18 +415,35 @@ WuQMacroManager::addMouseEventToRecording(QWidget* widget, /** - * @return Vector containing all Macro Groups. + * @return All 'active' available macro groups. + * Macros groups that are editible. Other macro + * groups are exluded. */ std::vector -WuQMacroManager::getMacroGroups() const +WuQMacroManager::getActiveMacroGroups() const { std::vector macroGroups; if (m_macroHelper) { - macroGroups = m_macroHelper->getMacroGroups(); + macroGroups = m_macroHelper->getActiveMacroGroups(); } return macroGroups; } +/** + * @return All macro groups including those that are + * be valid (editable) at this time. + */ +std::vector +WuQMacroManager::getAllMacroGroups() const +{ + std::vector macroGroups; + if (m_macroHelper) { + macroGroups = m_macroHelper->getAllMacroGroups(); + } + return macroGroups; +} + + /** * Start recording a new macro using the New Macro dialog. * @@ -1065,7 +1082,7 @@ WuQMacroManager::getMacroWithShortCutKey(const WuQMacroShortCutKeyEnum::Enum sho { WuQMacro* macro(NULL); - const auto macroGroups = getMacroGroups(); + const auto macroGroups = getActiveMacroGroups(); for (const auto mg : macroGroups) { macro = mg->getMacroWithShortCutKey(shortCutKey); if (macro != NULL) { diff --git a/src/GuiQt/WuQMacroManager.h b/src/GuiQt/WuQMacroManager.h index 38f20e48b..f64a0cff6 100644 --- a/src/GuiQt/WuQMacroManager.h +++ b/src/GuiQt/WuQMacroManager.h @@ -87,8 +87,10 @@ namespace caret { bool isModeRecording() const; - std::vector getMacroGroups() const; + std::vector getActiveMacroGroups() const; + std::vector getAllMacroGroups() const; + void startRecordingNewMacro(QWidget* parent); void stopRecordingNewMacro(); -- GitLab From 7aa7228baa9255567b1ac034c77937790327f97b Mon Sep 17 00:00:00 2001 From: John Harwell Date: Fri, 8 Mar 2019 09:33:49 -0600 Subject: [PATCH 223/427] Fix description for overlay crossfade macro operation. --- src/GuiQt/WbMacroCustomOperationManager.cxx | 57 ------------------- ...WbMacroCustomOperationOverlayCrossFade.cxx | 4 +- 2 files changed, 2 insertions(+), 59 deletions(-) diff --git a/src/GuiQt/WbMacroCustomOperationManager.cxx b/src/GuiQt/WbMacroCustomOperationManager.cxx index 1ecdef088..c3936ac0b 100644 --- a/src/GuiQt/WbMacroCustomOperationManager.cxx +++ b/src/GuiQt/WbMacroCustomOperationManager.cxx @@ -297,63 +297,6 @@ WbMacroCustomOperationManager::editOverlayFile(QWidget* parentWidget, WuQMacroCommandParameter* parameter, QString& errorMessageOut) { -// /* -// * Find the overlay index parameter that should be before this parameter -// */ -// int32_t overlayIndex(-1); -// const int32_t numParams = macroCommand->getNumberOfParameters(); -// for (int32_t ip = 0; ip < numParams; ip++) { -// const WuQMacroCommandParameter* p = macroCommand->getParameterAtIndex(ip); -// if (p->getDataType() == WuQMacroDataValueTypeEnum::CUSTOM_DATA) { -// bool valid(false); -// WbMacroCustomDataTypeEnum::Enum customType = WbMacroCustomDataTypeEnum::fromName(p->getCustomDataType(), -// &valid); -// if (valid) { -// if (customType == WbMacroCustomDataTypeEnum::OVERLAY_INDEX) { -// overlayIndex = p->getValue().toInt(); -// break; -// } -// } -// } -// } -// -// /* -// * Overlay index is 1..N for user -// */ -// if (overlayIndex < 1) { -// errorMessageOut = "Unable to find overlay index"; -// return false; -// } -// --overlayIndex; -// -// BrowserTabContent* tabContent = getTabContent(parentWidget, -// "Window containing overlay", -// errorMessageOut); -// if (tabContent == NULL) { -// return false; -// } -// -// Overlay* overlay = tabContent->getOverlaySet()->getOverlay(overlayIndex); -// if (overlay == NULL) { -// errorMessageOut = ("Overlay " -// + AString::number(overlayIndex) -// + " not found"); -// return false; -// } -// -// std::vector mapFiles; -// CaretMappableDataFile* selectedMapFile(NULL); -// int32_t selectedMapIndex(-1); -// overlay->getSelectionData(mapFiles, -// selectedMapFile, -// selectedMapIndex); -// -// if (mapFiles.empty()) { -// errorMessageOut = "The overlay does not contain any files"; -// return false; -// } - - std::vector mapFiles; CaretMappableDataFile* selectedMapFile(NULL); if ( ! getMapFilesInOverlay(parentWidget, diff --git a/src/GuiQt/WbMacroCustomOperationOverlayCrossFade.cxx b/src/GuiQt/WbMacroCustomOperationOverlayCrossFade.cxx index bf7b3da66..4ec7ceb3e 100644 --- a/src/GuiQt/WbMacroCustomOperationOverlayCrossFade.cxx +++ b/src/GuiQt/WbMacroCustomOperationOverlayCrossFade.cxx @@ -141,12 +141,12 @@ WbMacroCustomOperationOverlayCrossFade::createCommandVersionTwo() { const int32_t versionTwo(2); - const QString description("Crossfade (blend) from file in overlay to fade file/map:\n" + const QString description("Crossfade (blend) from file in overlay to selected file/map:\n" "(1) A copy of the selected overlay is inserted below it; \n" "(2) Selected file/map is placed in new the overlay;\n" "(3) The opacity of selected overlay is decreased until it\n" " becomes 0.0 revealing the selected file/map;\n" - "(4) The selected overlay is removed;" + "(4) The selected overlay is removed;\n" "(5) The selected file/map remains displayed in its overlay\n"); WuQMacroCommandParameter* paramSurfaceOne = new WuQMacroCommandParameter(WuQMacroDataValueTypeEnum::CUSTOM_DATA, -- GitLab From 9baee11d3696b7d41220f17354f381360ea15a0e Mon Sep 17 00:00:00 2001 From: John Harwell Date: Fri, 8 Mar 2019 15:47:14 -0600 Subject: [PATCH 224/427] Added a "Pause" button to the Macro Dialog that allows to pause and continue the execution of macro. --- src/GuiQt/CMakeLists.txt | 3 + src/GuiQt/WbMacroCustomOperationBase.h | 4 + src/GuiQt/WbMacroCustomOperationDelay.cxx | 10 +- src/GuiQt/WbMacroCustomOperationDelay.h | 3 +- src/GuiQt/WbMacroCustomOperationManager.cxx | 4 + src/GuiQt/WbMacroCustomOperationManager.h | 1 + .../WbMacroCustomOperationModelRotation.cxx | 33 +++- .../WbMacroCustomOperationModelRotation.h | 4 +- ...WbMacroCustomOperationOverlayCrossFade.cxx | 21 ++- .../WbMacroCustomOperationOverlayCrossFade.h | 7 +- ...croCustomOperationSurfaceInterpolation.cxx | 17 +- ...MacroCustomOperationSurfaceInterpolation.h | 6 +- ...croCustomOperationVolumeSliceIncrement.cxx | 18 +- ...MacroCustomOperationVolumeSliceIncrement.h | 4 +- ...ustomOperationVolumeToSurfaceCrossFade.cxx | 21 ++- ...oCustomOperationVolumeToSurfaceCrossFade.h | 6 +- src/GuiQt/WbMacroHelper.cxx | 10 ++ .../WuQMacroCustomOperationManagerInterface.h | 2 + src/GuiQt/WuQMacroDialog.cxx | 123 ++++++++++---- src/GuiQt/WuQMacroDialog.h | 5 + src/GuiQt/WuQMacroExecutor.cxx | 37 ++++- src/GuiQt/WuQMacroExecutor.h | 4 + src/GuiQt/WuQMacroExecutorMonitor.cxx | 155 ++++++++++++++++++ src/GuiQt/WuQMacroExecutorMonitor.h | 79 +++++++++ src/GuiQt/WuQMacroManager.cxx | 57 ++++++- src/GuiQt/WuQMacroManager.h | 8 + 26 files changed, 574 insertions(+), 68 deletions(-) create mode 100644 src/GuiQt/WuQMacroExecutorMonitor.cxx create mode 100644 src/GuiQt/WuQMacroExecutorMonitor.h diff --git a/src/GuiQt/CMakeLists.txt b/src/GuiQt/CMakeLists.txt index 272780b98..b836b72d4 100644 --- a/src/GuiQt/CMakeLists.txt +++ b/src/GuiQt/CMakeLists.txt @@ -210,6 +210,7 @@ ELSE() WuQMacroCreateDialog.h WuQMacroDialog.h WuQMacroExecutor.h + WuQMacroExecutorMonitor.h WuQMacroHelperInterface.h WuQMacroManager.h WuQMacroMenu.h @@ -482,6 +483,7 @@ WuQMacroCreateDialog.h WuQMacroCustomOperationManagerInterface.h WuQMacroDialog.h WuQMacroExecutor.h +WuQMacroExecutorMonitor.h WuQMacroExecutorOptions.h WuQMacroHelperInterface.h WuQMacroManager.h @@ -741,6 +743,7 @@ WuQMacroCopyDialog.cxx WuQMacroCreateDialog.cxx WuQMacroDialog.cxx WuQMacroExecutor.cxx +WuQMacroExecutorMonitor.cxx WuQMacroExecutorOptions.cxx WuQMacroManager.cxx WuQMacroMenu.cxx diff --git a/src/GuiQt/WbMacroCustomOperationBase.h b/src/GuiQt/WbMacroCustomOperationBase.h index 6e7df9890..d20a712fd 100644 --- a/src/GuiQt/WbMacroCustomOperationBase.h +++ b/src/GuiQt/WbMacroCustomOperationBase.h @@ -33,6 +33,7 @@ class QWidget; namespace caret { class Surface; + class WuQMacroExecutorMonitor; class WuQMacroCommand; class WbMacroCustomOperationBase : public CaretObject { @@ -50,6 +51,8 @@ namespace caret { * * @param parent * Parent widget for any dialogs + * @param executorMonitor + * the macro executor monitor * @param macroCommand * macro command to run * @return @@ -57,6 +60,7 @@ namespace caret { * Use getErrorMessage() for error information if false returned */ virtual bool executeCommand(QWidget* parent, + const WuQMacroExecutorMonitor* executorMonitor, const WuQMacroCommand* macroCommand) = 0; /** diff --git a/src/GuiQt/WbMacroCustomOperationDelay.cxx b/src/GuiQt/WbMacroCustomOperationDelay.cxx index 41d91297f..fde26ca7b 100644 --- a/src/GuiQt/WbMacroCustomOperationDelay.cxx +++ b/src/GuiQt/WbMacroCustomOperationDelay.cxx @@ -85,6 +85,8 @@ WbMacroCustomOperationDelay::createCommand() * * @param parent * Parent widget for any dialogs + * @param executorMonitor + * the macro executor monitor * @param macroCommand * macro command to run * @return @@ -92,12 +94,10 @@ WbMacroCustomOperationDelay::createCommand() * Use getErrorMessage() for error information if false returned */ bool -WbMacroCustomOperationDelay::executeCommand(QWidget* parent, - const WuQMacroCommand* macroCommand) +WbMacroCustomOperationDelay::executeCommand(QWidget* /*parent*/, + const WuQMacroExecutorMonitor* /*executorMonitor*/, + const WuQMacroCommand* /*macroCommand*/) { - CaretAssert(parent); - CaretAssert(macroCommand); - /* * Nothing to do since the base command's delay time performs the delay */ diff --git a/src/GuiQt/WbMacroCustomOperationDelay.h b/src/GuiQt/WbMacroCustomOperationDelay.h index c4f2fbb83..cbb35086c 100644 --- a/src/GuiQt/WbMacroCustomOperationDelay.h +++ b/src/GuiQt/WbMacroCustomOperationDelay.h @@ -41,7 +41,8 @@ namespace caret { WbMacroCustomOperationDelay& operator=(const WbMacroCustomOperationDelay&) = delete; virtual bool executeCommand(QWidget* parent, - const WuQMacroCommand* macroCommand) override; + const WuQMacroExecutorMonitor* executorMonitor, + const WuQMacroCommand* macroCommand) override; virtual WuQMacroCommand* createCommand() override; diff --git a/src/GuiQt/WbMacroCustomOperationManager.cxx b/src/GuiQt/WbMacroCustomOperationManager.cxx index c3936ac0b..fb2ab6493 100644 --- a/src/GuiQt/WbMacroCustomOperationManager.cxx +++ b/src/GuiQt/WbMacroCustomOperationManager.cxx @@ -507,6 +507,8 @@ WbMacroCustomOperationManager::editCustomDataValueParameter(QWidget* parentWidge * * @param parent * Parent widget for any dialogs + * @param executorMonitor + * The executor monitor * @param customMacroCommand * Custom macro command to run * @param errorMessageOut @@ -516,6 +518,7 @@ WbMacroCustomOperationManager::editCustomDataValueParameter(QWidget* parentWidge */ bool WbMacroCustomOperationManager::executeCustomOperationMacroCommand(QWidget* parent, + const WuQMacroExecutorMonitor* executorMonitor, const WuQMacroCommand* customMacroCommand, QString& errorMessageOut) { @@ -545,6 +548,7 @@ WbMacroCustomOperationManager::executeCustomOperationMacroCommand(QWidget* paren bool successFlag(false); if (customOperation) { successFlag = customOperation->executeCommand(parent, + executorMonitor, customMacroCommand); if ( ! successFlag) { errorMessageOut = customOperation->getErrorMessage(); diff --git a/src/GuiQt/WbMacroCustomOperationManager.h b/src/GuiQt/WbMacroCustomOperationManager.h index 975f730e6..9e5a38ebc 100644 --- a/src/GuiQt/WbMacroCustomOperationManager.h +++ b/src/GuiQt/WbMacroCustomOperationManager.h @@ -54,6 +54,7 @@ namespace caret { WuQMacroCommandParameter* parameter); virtual bool executeCustomOperationMacroCommand(QWidget* parent, + const WuQMacroExecutorMonitor* executorMonitor, const WuQMacroCommand* macroCommand, QString& errorMessageOut); diff --git a/src/GuiQt/WbMacroCustomOperationModelRotation.cxx b/src/GuiQt/WbMacroCustomOperationModelRotation.cxx index 16d596617..35bca2090 100644 --- a/src/GuiQt/WbMacroCustomOperationModelRotation.cxx +++ b/src/GuiQt/WbMacroCustomOperationModelRotation.cxx @@ -33,6 +33,7 @@ #include "WbMacroCustomOperationTypeEnum.h" #include "WuQMacroCommand.h" #include "WuQMacroCommandParameter.h" +#include "WuQMacroExecutorMonitor.h" using namespace caret; @@ -106,6 +107,8 @@ WbMacroCustomOperationModelRotation::createCommand() * * @param parent * Parent widget for any dialogs + * @param executorMonitor + * the macro executor monitor * @param macroCommand * macro command to run * @return @@ -114,7 +117,8 @@ WbMacroCustomOperationModelRotation::createCommand() */ bool WbMacroCustomOperationModelRotation::executeCommand(QWidget* parent, - const WuQMacroCommand* macroCommand) + const WuQMacroExecutorMonitor* executorMonitor, + const WuQMacroCommand* macroCommand) { CaretAssert(parent); CaretAssert(macroCommand); @@ -168,15 +172,32 @@ WbMacroCustomOperationModelRotation::executeCommand(QWidget* parent, if (model != NULL) { } - const bool successFlag = performRotation(tabContent, + const bool successFlag = performRotation(executorMonitor, + tabContent, axis, totalRotation, durationSeconds); return successFlag; } +/** + * Perform the rotation + * + * @param executorMonitor + * the macro executor monitor + * @param tabContent + * Content of the tab + * @param axis + * The screen axis of rotation + * @param totalRotation + * Total amount of rotation + * @param durationSeconds + * To time for command to run + * + */ bool -WbMacroCustomOperationModelRotation::performRotation(BrowserTabContent* tabContent, +WbMacroCustomOperationModelRotation::performRotation(const WuQMacroExecutorMonitor* executorMonitor, + BrowserTabContent* tabContent, const Axis axis, const float totalRotation, const float durationSeconds) @@ -212,8 +233,12 @@ WbMacroCustomOperationModelRotation::performRotation(BrowserTabContent* tabConte tabContent->setRotationMatrix(rotationMatrix); updateGraphics(); + if (executorMonitor->testForStop()) { + appendToErrorMessage(executorMonitor->getStoppedByUserMessage()); + return false; + } + sleepForSecondsAtEndOfIteration(iterationSleepTime); -// SystemUtilities::sleepSeconds(sleepTimeSeconds); } return true; diff --git a/src/GuiQt/WbMacroCustomOperationModelRotation.h b/src/GuiQt/WbMacroCustomOperationModelRotation.h index 2f187eb8e..6e55c0b2f 100644 --- a/src/GuiQt/WbMacroCustomOperationModelRotation.h +++ b/src/GuiQt/WbMacroCustomOperationModelRotation.h @@ -45,6 +45,7 @@ namespace caret { WbMacroCustomOperationModelRotation& operator=(const WbMacroCustomOperationModelRotation&) = delete; virtual bool executeCommand(QWidget* parent, + const WuQMacroExecutorMonitor* executorMonitor, const WuQMacroCommand* macroCommand) override; virtual WuQMacroCommand* createCommand() override; @@ -59,7 +60,8 @@ namespace caret { Z }; - bool performRotation(BrowserTabContent* tabContent, + bool performRotation(const WuQMacroExecutorMonitor* executorMonitor, + BrowserTabContent* tabContent, const Axis axis, const float totalRotation, const float durationSeconds); diff --git a/src/GuiQt/WbMacroCustomOperationOverlayCrossFade.cxx b/src/GuiQt/WbMacroCustomOperationOverlayCrossFade.cxx index 4ec7ceb3e..95daa1cba 100644 --- a/src/GuiQt/WbMacroCustomOperationOverlayCrossFade.cxx +++ b/src/GuiQt/WbMacroCustomOperationOverlayCrossFade.cxx @@ -34,6 +34,7 @@ #include "WbMacroCustomOperationTypeEnum.h" #include "WuQMacroCommand.h" #include "WuQMacroCommandParameter.h" +#include "WuQMacroExecutorMonitor.h" using namespace caret; @@ -195,6 +196,8 @@ WbMacroCustomOperationOverlayCrossFade::createCommandVersionTwo() * * @param parent * Parent widget for any dialogs + * @param executorMonitor + * the macro executor monitor * @param macroCommand * macro command to run * @return @@ -203,6 +206,7 @@ WbMacroCustomOperationOverlayCrossFade::createCommandVersionTwo() */ bool WbMacroCustomOperationOverlayCrossFade::executeCommand(QWidget* parent, + const WuQMacroExecutorMonitor* executorMonitor, const WuQMacroCommand* macroCommand) { CaretAssert(parent); @@ -216,6 +220,7 @@ WbMacroCustomOperationOverlayCrossFade::executeCommand(QWidget* parent, break; case 2: resultFlag = executeCommandVersionTwo(parent, + executorMonitor, macroCommand); break; default: @@ -231,6 +236,8 @@ WbMacroCustomOperationOverlayCrossFade::executeCommand(QWidget* parent, * * @param parent * Parent widget for any dialogs + * @param executorMonitor + * The macro executor's monitor * @param macroCommand * macro command to run * @return @@ -239,6 +246,7 @@ WbMacroCustomOperationOverlayCrossFade::executeCommand(QWidget* parent, */ bool WbMacroCustomOperationOverlayCrossFade::executeCommandVersionTwo(QWidget* parent, + const WuQMacroExecutorMonitor* executorMonitor, const WuQMacroCommand* macroCommand) { CaretAssert(parent); @@ -350,7 +358,8 @@ WbMacroCustomOperationOverlayCrossFade::executeCommandVersionTwo(QWidget* parent return false; } - const bool successFlag = performCrossFadeVersionTwo(overlaySet, + const bool successFlag = performCrossFadeVersionTwo(executorMonitor, + overlaySet, overlayIndex, fadeToMapFile, fadeToMapIndex, @@ -362,6 +371,8 @@ WbMacroCustomOperationOverlayCrossFade::executeCommandVersionTwo(QWidget* parent /** * Perform crossfade version two * + * @param executorMonitor + * The macro executor's monitor * @param overlaySet * OverlaySet for the in the tab * @param overlayIndex @@ -376,7 +387,8 @@ WbMacroCustomOperationOverlayCrossFade::executeCommandVersionTwo(QWidget* parent * True if successful, else false */ bool -WbMacroCustomOperationOverlayCrossFade::performCrossFadeVersionTwo(OverlaySet* overlaySet, +WbMacroCustomOperationOverlayCrossFade::performCrossFadeVersionTwo(const WuQMacroExecutorMonitor* executorMonitor, + OverlaySet* overlaySet, const int32_t overlayIndex, CaretMappableDataFile* fadeToMapFile, const int32_t fadeToMapIndex, @@ -434,6 +446,11 @@ WbMacroCustomOperationOverlayCrossFade::performCrossFadeVersionTwo(OverlaySet* o fadeFromOpacity = 0.0; } + if (executorMonitor->testForStop()) { + appendToErrorMessage(executorMonitor->getStoppedByUserMessage()); + return false; + } + sleepForSecondsAtEndOfIteration(iterationSleepTime); } diff --git a/src/GuiQt/WbMacroCustomOperationOverlayCrossFade.h b/src/GuiQt/WbMacroCustomOperationOverlayCrossFade.h index 76a584060..74395b1e2 100644 --- a/src/GuiQt/WbMacroCustomOperationOverlayCrossFade.h +++ b/src/GuiQt/WbMacroCustomOperationOverlayCrossFade.h @@ -44,7 +44,8 @@ namespace caret { WbMacroCustomOperationOverlayCrossFade& operator=(const WbMacroCustomOperationOverlayCrossFade&) = delete; virtual bool executeCommand(QWidget* parent, - const WuQMacroCommand* macroCommand) override; + const WuQMacroExecutorMonitor* executorMonitor, + const WuQMacroCommand* macroCommand) override; virtual WuQMacroCommand* createCommand() override; @@ -58,9 +59,11 @@ namespace caret { WuQMacroCommand* createCommandVersionTwo(); virtual bool executeCommandVersionTwo(QWidget* parent, + const WuQMacroExecutorMonitor* executorMonitor, const WuQMacroCommand* macroCommand); - bool performCrossFadeVersionTwo(OverlaySet* overlaySet, + bool performCrossFadeVersionTwo(const WuQMacroExecutorMonitor* executorMonitor, + OverlaySet* overlaySet, const int32_t overlayIndex, CaretMappableDataFile* fadeToMapFile, const int32_t fadeToMapIndex, diff --git a/src/GuiQt/WbMacroCustomOperationSurfaceInterpolation.cxx b/src/GuiQt/WbMacroCustomOperationSurfaceInterpolation.cxx index 7d2785beb..cdff5869d 100644 --- a/src/GuiQt/WbMacroCustomOperationSurfaceInterpolation.cxx +++ b/src/GuiQt/WbMacroCustomOperationSurfaceInterpolation.cxx @@ -40,6 +40,7 @@ #include "WbMacroCustomOperationTypeEnum.h" #include "WuQMacroCommand.h" #include "WuQMacroCommandParameter.h" +#include "WuQMacroExecutorMonitor.h" using namespace caret; @@ -115,6 +116,8 @@ WbMacroCustomOperationSurfaceInterpolation::createCommand() * * @param parent * Parent widget for any dialogs + * @param executorMonitor + * the macro executor monitor * @param macroCommand * macro command to run * @return @@ -123,6 +126,7 @@ WbMacroCustomOperationSurfaceInterpolation::createCommand() */ bool WbMacroCustomOperationSurfaceInterpolation::executeCommand(QWidget* parent, + const WuQMacroExecutorMonitor* executorMonitor, const WuQMacroCommand* macroCommand) { CaretAssert(parent); @@ -187,7 +191,8 @@ WbMacroCustomOperationSurfaceInterpolation::executeCommand(QWidget* parent, return false; } - bool successFlag = interpolateSurface(tabContent->getTabNumber(), + bool successFlag = interpolateSurface(executorMonitor, + tabContent->getTabNumber(), wholeBrainModel, startSurface, endSurface, @@ -199,6 +204,8 @@ WbMacroCustomOperationSurfaceInterpolation::executeCommand(QWidget* parent, /** * Interpolate from starting to ending surface * + * @param executorMonitor + * The macro executor's monitor * @param tabIndex * Index of selected tab * @param wholeBrainModel @@ -213,7 +220,8 @@ WbMacroCustomOperationSurfaceInterpolation::executeCommand(QWidget* parent, * True if successful, else false */ bool -WbMacroCustomOperationSurfaceInterpolation::interpolateSurface(const int32_t tabIndex, +WbMacroCustomOperationSurfaceInterpolation::interpolateSurface(const WuQMacroExecutorMonitor* executorMonitor, + const int32_t tabIndex, ModelWholeBrain* wholeBrainModel, const Surface* startSurface, const Surface* endSurface, @@ -308,6 +316,11 @@ WbMacroCustomOperationSurfaceInterpolation::interpolateSurface(const int32_t tab updateGraphics(); + if (executorMonitor->testForStop()) { + appendToErrorMessage(executorMonitor->getStoppedByUserMessage()); + return false; + } + sleepForSecondsAtEndOfIteration(iterationSleepTime); } diff --git a/src/GuiQt/WbMacroCustomOperationSurfaceInterpolation.h b/src/GuiQt/WbMacroCustomOperationSurfaceInterpolation.h index 67fe52ae7..9a7461b19 100644 --- a/src/GuiQt/WbMacroCustomOperationSurfaceInterpolation.h +++ b/src/GuiQt/WbMacroCustomOperationSurfaceInterpolation.h @@ -46,7 +46,8 @@ namespace caret { WbMacroCustomOperationSurfaceInterpolation& operator=(const WbMacroCustomOperationSurfaceInterpolation&) = delete; virtual bool executeCommand(QWidget* parent, - const WuQMacroCommand* macroCommand) override; + const WuQMacroExecutorMonitor* executorMonitor, + const WuQMacroCommand* macroCommand) override; virtual WuQMacroCommand* createCommand() override; @@ -55,7 +56,8 @@ namespace caret { // ADD_NEW_METHODS_HERE private: - bool interpolateSurface(const int32_t tabIndex, + bool interpolateSurface(const WuQMacroExecutorMonitor* executorMonitor, + const int32_t tabIndex, ModelWholeBrain* wholeBrainModel, const Surface* startSurface, const Surface* endSurface, diff --git a/src/GuiQt/WbMacroCustomOperationVolumeSliceIncrement.cxx b/src/GuiQt/WbMacroCustomOperationVolumeSliceIncrement.cxx index b51b67c59..90ca97d6d 100644 --- a/src/GuiQt/WbMacroCustomOperationVolumeSliceIncrement.cxx +++ b/src/GuiQt/WbMacroCustomOperationVolumeSliceIncrement.cxx @@ -38,6 +38,7 @@ #include "WbMacroCustomOperationTypeEnum.h" #include "WuQMacroCommand.h" #include "WuQMacroCommandParameter.h" +#include "WuQMacroExecutorMonitor.h" using namespace caret; @@ -113,6 +114,8 @@ WbMacroCustomOperationVolumeSliceIncrement::createCommand() * * @param parent * Parent widget for any dialogs + * @param executorMonitor + * the macro executor monitor * @param macroCommand * macro command to run * @return @@ -121,6 +124,7 @@ WbMacroCustomOperationVolumeSliceIncrement::createCommand() */ bool WbMacroCustomOperationVolumeSliceIncrement::executeCommand(QWidget* parent, + const WuQMacroExecutorMonitor* executorMonitor, const WuQMacroCommand* macroCommand) { CaretAssert(parent); @@ -174,13 +178,16 @@ WbMacroCustomOperationVolumeSliceIncrement::executeCommand(QWidget* parent, return false; } - const bool successFlag = performSliceIncrement(tabContent, + const bool successFlag = performSliceIncrement(executorMonitor, + tabContent, axis, durationSeconds); return successFlag; } /** + * @param executorMonitor + * The macro executor's monitor * @param tabContent * Content in the selected tab * @param axis @@ -189,7 +196,8 @@ WbMacroCustomOperationVolumeSliceIncrement::executeCommand(QWidget* parent, * Duration of time for command to run */ bool -WbMacroCustomOperationVolumeSliceIncrement::performSliceIncrement(BrowserTabContent* tabContent, +WbMacroCustomOperationVolumeSliceIncrement::performSliceIncrement(const WuQMacroExecutorMonitor* executorMonitor, + BrowserTabContent* tabContent, const Axis axis, const float durationSeconds) { @@ -289,6 +297,12 @@ WbMacroCustomOperationVolumeSliceIncrement::performSliceIncrement(BrowserTabCont updateGraphics(); + + if (executorMonitor->testForStop()) { + appendToErrorMessage(executorMonitor->getStoppedByUserMessage()); + return false; + } + sleepForSecondsAtEndOfIteration(iterationSleepTime); } diff --git a/src/GuiQt/WbMacroCustomOperationVolumeSliceIncrement.h b/src/GuiQt/WbMacroCustomOperationVolumeSliceIncrement.h index 8ad63c977..7aef43695 100644 --- a/src/GuiQt/WbMacroCustomOperationVolumeSliceIncrement.h +++ b/src/GuiQt/WbMacroCustomOperationVolumeSliceIncrement.h @@ -45,6 +45,7 @@ namespace caret { WbMacroCustomOperationVolumeSliceIncrement& operator=(const WbMacroCustomOperationVolumeSliceIncrement&) = delete; virtual bool executeCommand(QWidget* parent, + const WuQMacroExecutorMonitor* executorMonitor, const WuQMacroCommand* macroCommand) override; virtual WuQMacroCommand* createCommand() override; @@ -59,7 +60,8 @@ namespace caret { Z }; - bool performSliceIncrement(BrowserTabContent* tabContent, + bool performSliceIncrement(const WuQMacroExecutorMonitor* executorMonitor, + BrowserTabContent* tabContent, const Axis axis, const float durationSeconds); diff --git a/src/GuiQt/WbMacroCustomOperationVolumeToSurfaceCrossFade.cxx b/src/GuiQt/WbMacroCustomOperationVolumeToSurfaceCrossFade.cxx index e95cfbc78..c27e44164 100644 --- a/src/GuiQt/WbMacroCustomOperationVolumeToSurfaceCrossFade.cxx +++ b/src/GuiQt/WbMacroCustomOperationVolumeToSurfaceCrossFade.cxx @@ -37,6 +37,7 @@ #include "WbMacroCustomOperationTypeEnum.h" #include "WuQMacroCommand.h" #include "WuQMacroCommandParameter.h" +#include "WuQMacroExecutorMonitor.h" using namespace caret; @@ -110,6 +111,8 @@ WbMacroCustomOperationVolumeToSurfaceCrossFade::createCommand() * * @param parent * Parent widget for any dialogs + * @param executorMonitor + * the macro executor monitor * @param macroCommand * macro command to run * @return @@ -118,7 +121,8 @@ WbMacroCustomOperationVolumeToSurfaceCrossFade::createCommand() */ bool WbMacroCustomOperationVolumeToSurfaceCrossFade::executeCommand(QWidget* parent, - const WuQMacroCommand* macroCommand) + const WuQMacroExecutorMonitor* executorMonitor, + const WuQMacroCommand* macroCommand) { CaretAssert(parent); CaretAssert(macroCommand); @@ -159,7 +163,8 @@ WbMacroCustomOperationVolumeToSurfaceCrossFade::executeCommand(QWidget* parent, } - bool successFlag = performCrossFade(volumeOverlay, + bool successFlag = performCrossFade(executorMonitor, + volumeOverlay, durationSeconds); return successFlag; @@ -168,6 +173,8 @@ WbMacroCustomOperationVolumeToSurfaceCrossFade::executeCommand(QWidget* parent, /** * Interpolate from starting to ending surface * + * @param executorMonitor + * The macro executor's monitor * @param volumeOverlay * Overlay that contains the volume * @param durationSeconds @@ -176,8 +183,9 @@ WbMacroCustomOperationVolumeToSurfaceCrossFade::executeCommand(QWidget* parent, * True if successful, else false */ bool -WbMacroCustomOperationVolumeToSurfaceCrossFade::performCrossFade(Overlay* volumeOverlay, - const float durationSeconds) +WbMacroCustomOperationVolumeToSurfaceCrossFade::performCrossFade(const WuQMacroExecutorMonitor* executorMonitor, + Overlay* volumeOverlay, + const float durationSeconds) { CaretAssert(volumeOverlay); @@ -219,6 +227,11 @@ WbMacroCustomOperationVolumeToSurfaceCrossFade::performCrossFade(Overlay* volume volumeOpacity = 0.0; } + if (executorMonitor->testForStop()) { + appendToErrorMessage(executorMonitor->getStoppedByUserMessage()); + return false; + } + sleepForSecondsAtEndOfIteration(iterationSleepTime); } diff --git a/src/GuiQt/WbMacroCustomOperationVolumeToSurfaceCrossFade.h b/src/GuiQt/WbMacroCustomOperationVolumeToSurfaceCrossFade.h index f4f70fd1f..fe8b8118b 100644 --- a/src/GuiQt/WbMacroCustomOperationVolumeToSurfaceCrossFade.h +++ b/src/GuiQt/WbMacroCustomOperationVolumeToSurfaceCrossFade.h @@ -43,7 +43,8 @@ namespace caret { WbMacroCustomOperationVolumeToSurfaceCrossFade& operator=(const WbMacroCustomOperationVolumeToSurfaceCrossFade&) = delete; virtual bool executeCommand(QWidget* parent, - const WuQMacroCommand* macroCommand) override; + const WuQMacroExecutorMonitor* executorMonitor, + const WuQMacroCommand* macroCommand) override; virtual WuQMacroCommand* createCommand() override; @@ -52,7 +53,8 @@ namespace caret { // ADD_NEW_METHODS_HERE private: - bool performCrossFade(Overlay* volumeOverlay, + bool performCrossFade(const WuQMacroExecutorMonitor* executorMonitor, + Overlay* volumeOverlay, const float durationSeconds); diff --git a/src/GuiQt/WbMacroHelper.cxx b/src/GuiQt/WbMacroHelper.cxx index fd16a7c31..575b83a58 100644 --- a/src/GuiQt/WbMacroHelper.cxx +++ b/src/GuiQt/WbMacroHelper.cxx @@ -44,8 +44,10 @@ #include "WbMacroCustomDataTypeEnum.h" #include "WuQMacroCommand.h" #include "WuQMacroCommandParameter.h" +#include "WuQMacroExecutorMonitor.h" #include "WuQMacroExecutorOptions.h" #include "WuQMacroGroup.h" +#include "WuQMacroManager.h" #include "WuQMessageBox.h" using namespace caret; @@ -309,6 +311,7 @@ WbMacroHelper::recordImagesForDelay(QWidget* window, MovieRecorder* movieRecorder = SessionManager::get()->getMovieRecorder(); CaretAssert(movieRecorder); + const WuQMacroExecutorMonitor* execMonitor = WuQMacroManager::instance()->getMacroExecutorMonitor(); if (command->getCommandType() != WuQMacroCommandTypeEnum::MOUSE) { if (command->getDelayInSeconds() > 0.0) { switch (movieRecorder->getRecordingMode()) { @@ -326,6 +329,13 @@ WbMacroHelper::recordImagesForDelay(QWidget* window, EventManager::get()->sendEvent(EventGraphicsUpdateAllWindows().getPointer()); } movieRecorder->setManualRecordingOfImageRequested(false); + + /* + * User may pause or stop + */ + if (execMonitor->doPause()) { + break; + } } allowDelayFlagOut = false; diff --git a/src/GuiQt/WuQMacroCustomOperationManagerInterface.h b/src/GuiQt/WuQMacroCustomOperationManagerInterface.h index 4a7a8939b..febaea662 100644 --- a/src/GuiQt/WuQMacroCustomOperationManagerInterface.h +++ b/src/GuiQt/WuQMacroCustomOperationManagerInterface.h @@ -29,6 +29,7 @@ class QWidget; namespace caret { class WuQMacroCommand; class WuQMacroCommandParameter; + class WuQMacroExecutorMonitor; class WuQMacroCustomOperationManagerInterface { @@ -70,6 +71,7 @@ namespace caret { * True if command executed successfully, else false */ virtual bool executeCustomOperationMacroCommand(QWidget* parent, + const WuQMacroExecutorMonitor* executorMonitor, const WuQMacroCommand* macroCommand, QString& errorMessageOut) = 0; diff --git a/src/GuiQt/WuQMacroDialog.cxx b/src/GuiQt/WuQMacroDialog.cxx index e01b9315d..2d419f4fb 100644 --- a/src/GuiQt/WuQMacroDialog.cxx +++ b/src/GuiQt/WuQMacroDialog.cxx @@ -54,6 +54,7 @@ #include "WuQMacroCommand.h" #include "WuQMacroGroup.h" #include "WuQMacroExecutor.h" +#include "WuQMacroExecutorMonitor.h" #include "WuQMacroManager.h" #include "WuQMacroMouseEventInfo.h" #include "WuQMacroNewCommandSelectionDialog.h" @@ -210,6 +211,18 @@ WuQMacroDialog::createMacroRunAndEditingToolButtons() QObject::connect(m_runMacroToolButton, &QToolButton::clicked, this, &WuQMacroDialog::runMacroToolButtonClicked); + m_pauseMacroToolButton = new QToolButton(); + m_pauseMacroToolButton->setToolButtonStyle(Qt::ToolButtonTextBesideIcon); + m_pauseMacroToolButton->setText("Pause"); + QPixmap pausePixmap = createEditingToolButtonPixmap(m_pauseMacroToolButton, + EditButton::PAUSE); + m_pauseMacroToolButton->setIcon(pausePixmap); + m_pauseMacroToolButton->setToolTip("Pause or continue a macro. Button is highlighted " + "when a macro is paused."); + m_pauseMacroToolButton->setCheckable(true); + QObject::connect(m_pauseMacroToolButton, &QToolButton::clicked, + this, &WuQMacroDialog::pauseContinueMacroToolButtonClicked); + m_stopMacroToolButton = new QToolButton(); m_stopMacroToolButton->setToolButtonStyle(Qt::ToolButtonTextBesideIcon); m_stopMacroToolButton->setText("Stop"); @@ -256,6 +269,7 @@ WuQMacroDialog::createMacroRunAndEditingToolButtons() QHBoxLayout* toolButtonLayout = new QHBoxLayout(widget); toolButtonLayout->setContentsMargins(0, 0, 0, 0); toolButtonLayout->addWidget(m_runMacroToolButton); + toolButtonLayout->addWidget(m_pauseMacroToolButton); toolButtonLayout->addWidget(m_stopMacroToolButton); toolButtonLayout->addStretch(); toolButtonLayout->addWidget(m_editingInsertToolButton); @@ -1054,6 +1068,16 @@ WuQMacroDialog::getSelectedItemType() const return itemType; } +/** + * Called to pause/continue a macro + */ +void +WuQMacroDialog::pauseContinueMacroToolButtonClicked() +{ + WuQMacroManager::instance()->pauseContinueMacro(); + updateEditingToolButtons(); +} + /** * Called when run button is clicked */ @@ -1070,43 +1094,46 @@ WuQMacroDialog::runMacroToolButtonClicked() } WuQMacro* macro = getSelectedMacro(); - if (macro != NULL) { - if (macro->getNumberOfMacroCommands() <= 0) { - QMessageBox::critical(m_runMacroToolButton, - "Error", - "Macro does not contain any commands", - QMessageBox::Ok, - QMessageBox::NoButton); - return; - } - - const QString windowID = m_runOptionsWindowComboBox->currentText(); - QWidget* window = WuQMacroManager::instance()->getMainWindowWithIdentifier(windowID); - if (window == NULL) { - window = this; - while (window != NULL) { - if (qobject_cast(window) != NULL) { - break; - } - else { - window = window->parentWidget(); - } + if (macro == NULL) { + return; + } + if (macro->getNumberOfMacroCommands() <= 0) { + QMessageBox::critical(m_runMacroToolButton, + "Error", + "Macro does not contain any commands", + QMessageBox::Ok, + QMessageBox::NoButton); + return; + } + + const QString windowID = m_runOptionsWindowComboBox->currentText(); + QWidget* window = WuQMacroManager::instance()->getMainWindowWithIdentifier(windowID); + if (window == NULL) { + window = this; + while (window != NULL) { + if (qobject_cast(window) != NULL) { + break; } - if (window == NULL) { - window = parentWidget(); + else { + window = window->parentWidget(); } } - - /* - * While macro runs, edit buttons are disabled - */ - m_macroIsRunningFlag = true; - updateEditingToolButtons(); - WuQMacroManager::instance()->runMacro(window, - macro); - m_macroIsRunningFlag = false; - updateEditingToolButtons(); + if (window == NULL) { + window = parentWidget(); + } } + + /* + * While macro runs, edit buttons are disabled + */ + m_macroIsRunningFlag = true; + updateEditingToolButtons(); + QApplication::processEvents(); + WuQMacroManager::instance()->runMacro(window, + macro); + m_macroIsRunningFlag = false; + + updateEditingToolButtons(); } /** @@ -1116,6 +1143,7 @@ void WuQMacroDialog::stopMacroToolButtonClicked() { WuQMacroManager::instance()->stopMacro(); + updateEditingToolButtons(); } /** @@ -1550,9 +1578,21 @@ WuQMacroDialog::updateEditingToolButtons() bool deleteValid(false); bool moveUpValid(false); bool moveDownValid(false); + bool pauseValid(false); + bool pauseChecked(false); if (m_macroIsRunningFlag) { - stopValid = true; + pauseValid = true; + switch (WuQMacroManager::instance()->getMacroExecutorMonitor()->getMode()) { + case WuQMacroExecutorMonitor::Mode::PAUSE: + pauseChecked = true; + break; + case WuQMacroExecutorMonitor::Mode::RUN: + break; + case WuQMacroExecutorMonitor::Mode::STOP: + break; + } + stopValid = true; } else { insertValid = (macroGroup != NULL); @@ -1586,6 +1626,8 @@ WuQMacroDialog::updateEditingToolButtons() } } + m_pauseMacroToolButton->setEnabled(pauseValid); + m_pauseMacroToolButton->setChecked(pauseChecked); m_runMacroToolButton->setEnabled(runValid); m_stopMacroToolButton->setEnabled(stopValid); m_editingDeleteToolButton->setEnabled(deleteValid); @@ -1621,6 +1663,8 @@ WuQMacroDialog::createEditingToolButtonPixmap(const QWidget* widget, break; case EditButton::MOVE_UP: break; + case EditButton::PAUSE: + break; case EditButton::RUN: /* allow background */ pixmapOptions = 0; @@ -1673,6 +1717,17 @@ WuQMacroDialog::createEditingToolButtonPixmap(const QWidget* widget, painter->drawLine(QPointF(0, maxValue), QPointF(arrowTip, maxValue - arrowTip)); painter->drawLine(QPointF(0, maxValue), QPointF(-arrowTip, maxValue - arrowTip)); break; + case EditButton::PAUSE: + { + const qreal x = maxValue / 3; + const qreal y = maxValue * 0.667; + /* + * Parallel vertical lines + */ + painter->drawLine(QPointF(-x, y), QPointF(-x, -y)); + painter->drawLine(QPointF( x, y), QPointF( x, -y)); + } + break; case EditButton::RUN: { /* diff --git a/src/GuiQt/WuQMacroDialog.h b/src/GuiQt/WuQMacroDialog.h index 94c00cb31..082681590 100644 --- a/src/GuiQt/WuQMacroDialog.h +++ b/src/GuiQt/WuQMacroDialog.h @@ -105,6 +105,8 @@ namespace caret { void editingInsertToolButtonClicked(); + void pauseContinueMacroToolButtonClicked(); + void runMacroToolButtonClicked(); void stopMacroToolButtonClicked(); @@ -137,6 +139,7 @@ namespace caret { INSERTER, MOVE_DOWN, MOVE_UP, + PAUSE, RUN, STOP }; @@ -222,6 +225,8 @@ namespace caret { QGridLayout* m_parameterWidgetsGridLayout; + QToolButton* m_pauseMacroToolButton; + QToolButton* m_runMacroToolButton; QToolButton* m_stopMacroToolButton; diff --git a/src/GuiQt/WuQMacroExecutor.cxx b/src/GuiQt/WuQMacroExecutor.cxx index de332017f..2e88a6b4c 100644 --- a/src/GuiQt/WuQMacroExecutor.cxx +++ b/src/GuiQt/WuQMacroExecutor.cxx @@ -50,6 +50,7 @@ #include "WuQMacro.h" #include "WuQMacroCommand.h" #include "WuQMacroCommandParameter.h" +#include "WuQMacroExecutorMonitor.h" #include "WuQMacroManager.h" #include "WuQMacroMouseEventInfo.h" #include "WuQMacroMouseEventWidgetInterface.h" @@ -267,6 +268,8 @@ WuQMacroExecutor::findObjectByName(const QString& objectName) const * @param otherObjectParents * Additional objects that are searched for objects contained * in the macro commands + * @param executorMonitor + * The executor monitor * @param executorOptions * Executor options * @param errorMessageOut @@ -278,12 +281,14 @@ bool WuQMacroExecutor::runMacro(const WuQMacro* macro, QWidget* window, std::vector& otherObjectParents, + const WuQMacroExecutorMonitor* executorMonitor, const WuQMacroExecutorOptions* executorOptions, QString& errorMessageOut) const { const bool result = runMacroPrivate(macro, window, otherObjectParents, + executorMonitor, executorOptions, errorMessageOut); @@ -300,6 +305,8 @@ WuQMacroExecutor::runMacro(const WuQMacro* macro, * @param otherObjectParents * Additional objects that are searched for objects contained * in the macro commands + * @param executorMonitor + * The executor monitor * @param executorOptions * Executor options * @param errorMessageOut @@ -311,6 +318,7 @@ bool WuQMacroExecutor::runMacroPrivate(const WuQMacro* macro, QWidget* window, std::vector& otherObjectParents, + const WuQMacroExecutorMonitor* executorMonitor, const WuQMacroExecutorOptions* executorOptions, QString& errorMessageOut) const { @@ -372,6 +380,20 @@ WuQMacroExecutor::runMacroPrivate(const WuQMacro* macro, emit macroCommandAboutToStart(window, mc, allowDelayBeforeCommandFlag); + /* + * May get stopped by user in macroCommandAboutToStart() + */ + switch (executorMonitor->getMode()) { + case WuQMacroExecutorMonitor::Mode::PAUSE: + break; + case WuQMacroExecutorMonitor::Mode::RUN: + break; + case WuQMacroExecutorMonitor::Mode::STOP: + errorMessageOut = executorMonitor->getStoppedByUserMessage(); + return false; + break; + } + if (allowDelayBeforeCommandFlag) { performCommandDelay(mc); } @@ -381,6 +403,7 @@ WuQMacroExecutor::runMacroPrivate(const WuQMacro* macro, switch (mc->getCommandType()) { case WuQMacroCommandTypeEnum::CUSTOM_OPERATION: successFlag = WuQMacroManager::instance()->executeCustomOperationMacroCommand(window, + executorMonitor, mc, commandErrorMessage); @@ -398,6 +421,7 @@ WuQMacroExecutor::runMacroPrivate(const WuQMacro* macro, case WuQMacroCommandTypeEnum::WIDGET: CaretAssert(object); successFlag = runMacroCommand(window, + executorMonitor, mc, object, commandErrorMessage); @@ -411,9 +435,10 @@ WuQMacroExecutor::runMacroPrivate(const WuQMacro* macro, } } + QGuiApplication::processEvents(); if (m_stopFlag) { - errorMessageOut = "Macro stopped at request of user"; + errorMessageOut = "OBSOLETE: Macro stopped at request of user"; return false; } @@ -421,6 +446,13 @@ WuQMacroExecutor::runMacroPrivate(const WuQMacro* macro, emit macroCommandHasCompleted(window, mc, allowDelayAfterCommandFlag); + + const bool stopFlag = executorMonitor->testForStop(); + if (stopFlag) { + errorMessageOut = executorMonitor->getStoppedByUserMessage(); + return false; + } + if (allowDelayAfterCommandFlag) { performCommandDelay(mc); } @@ -461,6 +493,8 @@ WuQMacroExecutor::stopMacro() * * @param parentWidget * Parent widget for dialogs + * @param executorMonitor + * The executor monitor * @param macroCommand * Macro command that is run * @param object @@ -472,6 +506,7 @@ WuQMacroExecutor::stopMacro() */ bool WuQMacroExecutor::runMacroCommand(QWidget* /*parentWidget*/, + const WuQMacroExecutorMonitor* /*executorMonitor*/, const WuQMacroCommand* macroCommand, QObject* object, QString& errorMessageOut) const diff --git a/src/GuiQt/WuQMacroExecutor.h b/src/GuiQt/WuQMacroExecutor.h index ec0ea34bb..72ffcc836 100644 --- a/src/GuiQt/WuQMacroExecutor.h +++ b/src/GuiQt/WuQMacroExecutor.h @@ -35,6 +35,7 @@ namespace caret { class WuQMacro; class WuQMacroCommand; + class WuQMacroExecutorMonitor; class WuQMacroExecutorOptions; class WuQMacroExecutor : public QObject { @@ -53,6 +54,7 @@ namespace caret { bool runMacro(const WuQMacro* macro, QWidget* window, std::vector& otherObjectParents, + const WuQMacroExecutorMonitor* executorMonitor, const WuQMacroExecutorOptions* executorOptions, QString& errorMessageOut) const; @@ -92,10 +94,12 @@ namespace caret { bool runMacroPrivate(const WuQMacro* macro, QWidget* window, std::vector& otherObjectParents, + const WuQMacroExecutorMonitor* executorMonitor, const WuQMacroExecutorOptions* executorOptions, QString& errorMessageOut) const; bool runMacroCommand(QWidget* parentWidget, + const WuQMacroExecutorMonitor* executorMonitor, const WuQMacroCommand* macroCommand, QObject* object, QString& errorMessageOut) const; diff --git a/src/GuiQt/WuQMacroExecutorMonitor.cxx b/src/GuiQt/WuQMacroExecutorMonitor.cxx new file mode 100644 index 000000000..6687efb57 --- /dev/null +++ b/src/GuiQt/WuQMacroExecutorMonitor.cxx @@ -0,0 +1,155 @@ + +/*LICENSE_START*/ +/* + * Copyright (C) 2019 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + +#define __WU_Q_MACRO_EXECUTOR_MONITOR_DECLARE__ +#include "WuQMacroExecutorMonitor.h" +#undef __WU_Q_MACRO_EXECUTOR_MONITOR_DECLARE__ + +#include + +#include +#include + +#include "CaretAssert.h" +using namespace caret; + + + +/** + * \class caret::WuQMacroExecutorMonitor + * \brief Allows interuption to pause or stop a macro + * \ingroup GuiQt + */ + +/** + * Constructor. + * + * @param parent + * The parent object + */ +WuQMacroExecutorMonitor::WuQMacroExecutorMonitor(QObject* parent) +: QObject(parent) +{ + +} + +/** + * Destructor. + */ +WuQMacroExecutorMonitor::~WuQMacroExecutorMonitor() +{ +} + +/** + * @return Error message indicating macro stopped by + * user. Use this method for error message so that it + * is consistent. + */ +QString +WuQMacroExecutorMonitor::getStoppedByUserMessage() const +{ + return "Execution stopped by user"; +} + +/** + * @return The mode + */ +WuQMacroExecutorMonitor::Mode +WuQMacroExecutorMonitor::getMode() const +{ + QMutexLocker locker(&m_modeMutex); + return m_mode; +} + +/** + * Set the mode + * + * @param mode + * New mode + */ +void +WuQMacroExecutorMonitor::setMode(const Mode mode) +{ + QMutexLocker locker(&m_modeMutex); + m_mode = mode; + switch (m_mode) { + case Mode::PAUSE: + break; + case Mode::RUN: + break; + case Mode::STOP: + break; + } +} + +/** + * @return True if execution should stop and the command + * should cleanup and return + */ +bool +WuQMacroExecutorMonitor::testForStop() const +{ + bool stopFlag(false); + switch (getMode()) { + case Mode::PAUSE: + stopFlag = doPause(); + break; + case Mode::RUN: + break; + case Mode::STOP: + stopFlag = true; + break; + } + return stopFlag; +} + +/** + * Pause execution and return when mode changes to run or stop. + * @return True if execution should stop and the command + * should cleanup and return. + */ +bool +WuQMacroExecutorMonitor::doPause() const +{ + bool stopFlag(false); + + bool waitFlag(true); + while (waitFlag) { + QApplication::processEvents(); + + switch (getMode()) { + case Mode::PAUSE: + break; + case Mode::RUN: + stopFlag = false; + waitFlag = false; + break; + case Mode::STOP: + stopFlag = true; + waitFlag = false; + break; + } + } + + return stopFlag; +} + + diff --git a/src/GuiQt/WuQMacroExecutorMonitor.h b/src/GuiQt/WuQMacroExecutorMonitor.h new file mode 100644 index 000000000..b8ee0a0f3 --- /dev/null +++ b/src/GuiQt/WuQMacroExecutorMonitor.h @@ -0,0 +1,79 @@ +#ifndef __WU_Q_MACRO_EXECUTOR_MONITOR_H__ +#define __WU_Q_MACRO_EXECUTOR_MONITOR_H__ + +/*LICENSE_START*/ +/* + * Copyright (C) 2019 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + + + +#include + +#include +#include + +namespace caret { + + class WuQMacroExecutorMonitor : public QObject { + Q_OBJECT + + public: + enum class Mode { + RUN, + PAUSE, + STOP + }; + + WuQMacroExecutorMonitor(QObject* parent); + + virtual ~WuQMacroExecutorMonitor(); + + WuQMacroExecutorMonitor(const WuQMacroExecutorMonitor&) = delete; + + WuQMacroExecutorMonitor& operator=(const WuQMacroExecutorMonitor&) = delete; + + QString getStoppedByUserMessage() const; + + Mode getMode() const; + + bool testForStop() const; + + void setMode(const Mode mode); + + bool doPause() const; + + mutable Mode m_mode = Mode::STOP; + + mutable QMutex m_modeMutex; + + // ADD_NEW_METHODS_HERE + + private: + // ADD_NEW_MEMBERS_HERE + + friend class WuQMacroExecutor; + friend class WuQMacroManager; + }; + +#ifdef __WU_Q_MACRO_EXECUTOR_MONITOR_DECLARE__ + // +#endif // __WU_Q_MACRO_EXECUTOR_MONITOR_DECLARE__ + +} // namespace +#endif //__WU_Q_MACRO_EXECUTOR_MONITOR_H__ diff --git a/src/GuiQt/WuQMacroManager.cxx b/src/GuiQt/WuQMacroManager.cxx index 0e2e2d2b6..45e820dc7 100644 --- a/src/GuiQt/WuQMacroManager.cxx +++ b/src/GuiQt/WuQMacroManager.cxx @@ -44,6 +44,7 @@ #include "WuQMacroCustomOperationManagerInterface.h" #include "WuQMacroDialog.h" #include "WuQMacroExecutor.h" +#include "WuQMacroExecutorMonitor.h" #include "WuQMacroFile.h" #include "WuQMacroGroup.h" #include "WuQMacroMouseEventInfo.h" @@ -72,6 +73,8 @@ WuQMacroManager::WuQMacroManager(const QString& name, m_name(name) { setObjectName(name); + + m_macroExecutorMonitor = new WuQMacroExecutorMonitor(this); m_executorOptions.reset(new WuQMacroExecutorOptions()); } @@ -80,6 +83,11 @@ m_name(name) */ WuQMacroManager::~WuQMacroManager() { + /* + * Note: Do not delete the executor monitor as it is a QObject + * with 'this' as a parent so Qt will take of of deleting it + */ + /* * Do not delete the WuQMacroSignalWatcher instances in * m_signalWatchers. This WuQMacroManager is set as the @@ -550,6 +558,8 @@ WuQMacroManager::runMacro(QWidget* widget, QObject::connect(m_macroExecutor, &WuQMacroExecutor::macroCommandHasCompleted, this, &WuQMacroManager::macroCommandCompletedExecution); + m_macroExecutorMonitor->setMode(WuQMacroExecutorMonitor::Mode::RUN); + if (m_macroHelper != NULL) { m_macroHelper->macroExecutionStarting(macro, widget, @@ -558,6 +568,7 @@ WuQMacroManager::runMacro(QWidget* widget, const bool resultFlag = m_macroExecutor->runMacro(macro, widget, m_parentObjects, + m_macroExecutorMonitor, m_executorOptions.get(), errorMessage); if (m_macroHelper != NULL) { @@ -566,6 +577,8 @@ WuQMacroManager::runMacro(QWidget* widget, m_executorOptions.get()); } + m_macroExecutorMonitor->setMode(WuQMacroExecutorMonitor::Mode::STOP); + if ( ! resultFlag) { QMessageBox::critical(widget, "Run Macro Error", @@ -590,9 +603,29 @@ WuQMacroManager::runMacro(QWidget* widget, void WuQMacroManager::stopMacro() { - QMutexLocker locker(&m_macroExecutorMutex); - if (m_macroExecutor != NULL) { - m_macroExecutor->stopMacro(); + m_macroExecutorMonitor->setMode(WuQMacroExecutorMonitor::Mode::STOP); + +// QMutexLocker locker(&m_macroExecutorMutex); +// if (m_macroExecutor != NULL) { +// m_macroExecutor->stopMacro(); +// } +} + +/** + * Pause or continue a macro + */ +void +WuQMacroManager::pauseContinueMacro() +{ + switch (m_macroExecutorMonitor->getMode()) { + case WuQMacroExecutorMonitor::Mode::PAUSE: + m_macroExecutorMonitor->setMode(WuQMacroExecutorMonitor::Mode::RUN); + break; + case WuQMacroExecutorMonitor::Mode::RUN: + m_macroExecutorMonitor->setMode(WuQMacroExecutorMonitor::Mode::PAUSE); + break; + case WuQMacroExecutorMonitor::Mode::STOP: + break; } } @@ -1025,6 +1058,16 @@ WuQMacroManager::getExecutorOptions() return m_executorOptions.get(); } +/** + * @return Pointer to the executor monitor. + * Always returns valid pointer, even if macro is not running. + */ +const WuQMacroExecutorMonitor* +WuQMacroManager::getMacroExecutorMonitor() const +{ + return m_macroExecutorMonitor; +} + /** * Process a key press event for macro shortcut * @@ -1142,6 +1185,8 @@ WuQMacroManager::editCustomDataValueParameter(QWidget* parent, * * @param parent * Parent widget for any dialogs + * @param executorMonitor + * The executor monitor * @param macroCommand * Custom macro command to run * @param errorMessageOut @@ -1149,8 +1194,9 @@ WuQMacroManager::editCustomDataValueParameter(QWidget* parent, */ bool WuQMacroManager::executeCustomOperationMacroCommand(QWidget* parent, - const WuQMacroCommand* macroCommand, - QString& errorMessageOut) + const WuQMacroExecutorMonitor* executorMonitor, + const WuQMacroCommand* macroCommand, + QString& errorMessageOut) { CaretAssert(parent); CaretAssert(macroCommand); @@ -1160,6 +1206,7 @@ WuQMacroManager::executeCustomOperationMacroCommand(QWidget* parent, bool successFlag(false); if (m_customCommandManager != NULL) { successFlag = m_customCommandManager->executeCustomOperationMacroCommand(parent, + executorMonitor, macroCommand, errorMessageOut); } diff --git a/src/GuiQt/WuQMacroManager.h b/src/GuiQt/WuQMacroManager.h index f64a0cff6..99f4edbda 100644 --- a/src/GuiQt/WuQMacroManager.h +++ b/src/GuiQt/WuQMacroManager.h @@ -45,6 +45,7 @@ namespace caret { class WuQMacroCustomOperationManagerInterface; class WuQMacroDialog; class WuQMacroExecutor; + class WuQMacroExecutorMonitor; class WuQMacroExecutorOptions; class WuQMacroGroup; class WuQMacroHelperInterface; @@ -103,6 +104,8 @@ namespace caret { const WuQMacroExecutorOptions* getExecutorOptions() const; + const WuQMacroExecutorMonitor* getMacroExecutorMonitor() const; + bool deleteMacro(QWidget* parent, WuQMacroGroup* macroGroup, WuQMacro* macro); @@ -124,6 +127,8 @@ namespace caret { void stopMacro(); + void pauseContinueMacro(); + bool runMacroWithShortCutKeyEvent(QWidget* window, const QKeyEvent* keyEvent); @@ -146,6 +151,7 @@ namespace caret { WuQMacroCommandParameter* parameter); bool executeCustomOperationMacroCommand(QWidget* parent, + const WuQMacroExecutorMonitor* executorMonitor, const WuQMacroCommand* macroCommand, QString& errorMessageOut); @@ -205,6 +211,8 @@ namespace caret { WuQMacroExecutor* m_macroExecutor = NULL; + WuQMacroExecutorMonitor* m_macroExecutorMonitor = NULL; + QMutex m_macroExecutorMutex; static QString s_importExportMacroFileDirectory; -- GitLab From 085e2dc629d73fcb53fc30962c079da4de5c1055 Mon Sep 17 00:00:00 2001 From: John Harwell Date: Mon, 11 Mar 2019 15:45:06 -0500 Subject: [PATCH 225/427] Add a pop-up menu to the toolbar's scene tool button that displays a menu for reloading the current scene without having to go to the scene dialog. --- src/GuiQt/BrainBrowserWindow.cxx | 4 +- src/GuiQt/BrainBrowserWindowToolBar.cxx | 82 ++++++++++++++++++++- src/GuiQt/BrainBrowserWindowToolBar.h | 9 +++ src/GuiQt/GuiManager.cxx | 96 +++++++++++++++++++------ src/GuiQt/GuiManager.h | 5 +- src/GuiQt/SceneDialog.cxx | 68 +++++++++++++----- src/GuiQt/SceneDialog.h | 15 ++-- 7 files changed, 230 insertions(+), 49 deletions(-) diff --git a/src/GuiQt/BrainBrowserWindow.cxx b/src/GuiQt/BrainBrowserWindow.cxx index a8361f505..c2595769b 100644 --- a/src/GuiQt/BrainBrowserWindow.cxx +++ b/src/GuiQt/BrainBrowserWindow.cxx @@ -3385,9 +3385,11 @@ BrainBrowserWindow::loadSceneFromCommandLine(const AString& sceneFileName, } if (scene != NULL) { + const bool showSceneDialogFlag(true); GuiManager::get()->processShowSceneDialogAndScene(this, sf, - scene); + scene, + showSceneDialogFlag); haveSceneError = false; break; } diff --git a/src/GuiQt/BrainBrowserWindowToolBar.cxx b/src/GuiQt/BrainBrowserWindowToolBar.cxx index 3b13da2ca..5f73c71f5 100644 --- a/src/GuiQt/BrainBrowserWindowToolBar.cxx +++ b/src/GuiQt/BrainBrowserWindowToolBar.cxx @@ -94,6 +94,7 @@ #include "EventUserInterfaceUpdate.h" #include "EventManager.h" #include "EventModelGetAll.h" +#include "EventSceneActive.h" #include "EventSurfaceColoringInvalidate.h" #include "EventUpdateYokedWindows.h" #include "GuiManager.h" @@ -108,6 +109,7 @@ #include "ModelVolume.h" #include "ModelWholeBrain.h" #include "OverlaySet.h" +#include "Scene.h" #include "SceneAttributes.h" #include "SceneClass.h" #include "SceneIntegerArray.h" @@ -283,9 +285,30 @@ BrainBrowserWindowToolBar::BrainBrowserWindowToolBar(const int32_t browserWindow QToolButton* helpDialogToolButton = new QToolButton(); helpDialogToolButton->setDefaultAction(GuiManager::get()->getHelpViewerDialogDisplayAction()); - + + const QString sceneButtonToolTip("Click to display the Scene Dialog. " + "Hold down mouse button to display a menu for loading scenes " + "including reloading the current scene."); QToolButton* sceneDialogToolButton = new QToolButton(); + sceneDialogToolButton->setIcon(GuiManager::get()->getSceneDialogDisplayAction()->icon()); sceneDialogToolButton->setDefaultAction(GuiManager::get()->getSceneDialogDisplayAction()); + sceneDialogToolButton->setPopupMode(QToolButton::DelayedPopup); + sceneDialogToolButton->setToolButtonStyle(Qt::ToolButtonIconOnly); + WuQtUtilities::setWordWrappedToolTip(sceneDialogToolButton, + sceneButtonToolTip); + QObject::connect(sceneDialogToolButton, &QToolButton::clicked, + this, &BrainBrowserWindowToolBar::sceneToolButtonClicked); + + /* + * The scene dialog tool button menu must be created AFTER the tool button. Seems weird. + * https://forums.autodesk.com/t5/maya-forum/maya-2017-and-qtoolbutton-popup-menu-not-showing/td-p/6927575 + */ + m_sceneToolButtonMenu = new QMenu(sceneDialogToolButton); + QObject::connect(m_sceneToolButtonMenu, &QMenu::triggered, + this, &BrainBrowserWindowToolBar::sceneToolButtonMenuTriggered); + QObject::connect(m_sceneToolButtonMenu, &QMenu::aboutToShow, + this, &BrainBrowserWindowToolBar::sceneToolButtonMenuAboutToShow); + sceneDialogToolButton->setMenu(m_sceneToolButtonMenu); QIcon movieIcon; QAction* movieAction = new QAction(); @@ -4039,6 +4062,63 @@ BrainBrowserWindowToolBar::orientationAnteriorPosteriorToolButtonTriggered(bool this->updateGraphicsWindowAndYokedWindows(); } +/** + * Called when the scene tool button is clicked to show scene dialog + */ +void +BrainBrowserWindowToolBar::sceneToolButtonClicked() +{ + GuiManager::get()->getSceneDialogDisplayAction()->trigger(); +} + +/** + * Called when an item is selected from the scene tool button menu + * + * @param action + * Action of menu selected + */ +void +BrainBrowserWindowToolBar::sceneToolButtonMenuTriggered(QAction* action) +{ + if (action != NULL) { + void* scenePointer = action->data().value(); + if (scenePointer != NULL) { + BrainBrowserWindow* bbw = GuiManager::get()->getBrowserWindowByWindowIndex(browserWindowIndex); + CaretAssert(bbw); + + Scene* scene = (Scene*)scenePointer; + SceneFile* invalidSceneFile(NULL); + const bool showSceneDialogFlag(false); + GuiManager::get()->processShowSceneDialogAndScene(bbw, + invalidSceneFile, + scene, + showSceneDialogFlag); + } + } +} + +/** + * Called when the scene menu is about to show + */ +void +BrainBrowserWindowToolBar::sceneToolButtonMenuAboutToShow() +{ + m_sceneToolButtonMenu->clear(); + + EventSceneActive sceneEvent(EventSceneActive::MODE_GET); + EventManager::get()->sendEvent(sceneEvent.getPointer()); + Scene* activeScene = sceneEvent.getScene(); + if (activeScene != NULL) { + QString name(activeScene->getName().trimmed()); + if (name.isEmpty()) { + name = ""; + } + QAction* action = m_sceneToolButtonMenu->addAction("Reload Current Scene: " + + name); + action->setData(qVariantFromValue((void*)activeScene)); + } +} + /** * Called when custom view is triggered and displays Custom View Menu. */ diff --git a/src/GuiQt/BrainBrowserWindowToolBar.h b/src/GuiQt/BrainBrowserWindowToolBar.h index 0f4f3aa44..355c7a70e 100644 --- a/src/GuiQt/BrainBrowserWindowToolBar.h +++ b/src/GuiQt/BrainBrowserWindowToolBar.h @@ -297,6 +297,12 @@ namespace caret { void customViewActionTriggered(); + void sceneToolButtonMenuTriggered(QAction* action); + + void sceneToolButtonMenuAboutToShow(); + + void sceneToolButtonClicked(); + private: QAction* orientationLateralMedialToolButtonAction; QAction* orientationDorsalVentralToolButtonAction; @@ -333,6 +339,9 @@ namespace caret { QIcon* viewOrientationLeftMedialIcon; QIcon* viewOrientationRightLateralIcon; QIcon* viewOrientationRightMedialIcon; + + QMenu* m_sceneToolButtonMenu = NULL; + private slots: void orientationLeftOrLateralToolButtonTriggered(bool checked); void orientationRightOrMedialToolButtonTriggered(bool checked); diff --git a/src/GuiQt/GuiManager.cxx b/src/GuiQt/GuiManager.cxx index a75aad235..372bf163d 100644 --- a/src/GuiQt/GuiManager.cxx +++ b/src/GuiQt/GuiManager.cxx @@ -102,10 +102,12 @@ #include "MovieRecordingDialog.h" #include "PaletteColorMappingEditorDialog.h" #include "PreferencesDialog.h" +#include "Scene.h" #include "SceneAttributes.h" #include "SceneClass.h" #include "SceneClassArray.h" #include "SceneDialog.h" +#include "SceneFile.h" #include "SceneWindowGeometry.h" #include "SelectionManager.h" #include "SelectionItemChartMatrix.h" @@ -125,6 +127,7 @@ #include "VolumePropertiesEditorDialog.h" #include "WbMacroCustomOperationManager.h" #include "WbMacroHelper.h" +#include "WuQMessageBox.h" #include "WuQMacroManager.h" #include "WuQMacroWidgetTypeEnum.h" #include "WuQMessageBox.h" @@ -277,11 +280,9 @@ GuiManager::initializeGuiManager() /* * Scene dialog action */ - m_sceneDialogDisplayAction = WuQtUtilities::createAction("Scenes...", - "Show/Hide the Scenes Window", - this, - this, - SLOT(sceneDialogDisplayActionToggled(bool))); + m_sceneDialogDisplayAction = new QAction(this); + QObject::connect(m_sceneDialogDisplayAction, &QAction::triggered, + this, &GuiManager::sceneDialogDisplayActionTriggered); QIcon clapBoardIcon; const bool clapBoardIconValid = WuQtUtilities::loadIcon(":/ToolBar/clapboard.png", clapBoardIcon); @@ -293,8 +294,7 @@ GuiManager::initializeGuiManager() m_sceneDialogDisplayAction->setIconText("Scenes"); } m_sceneDialogDisplayAction->blockSignals(true); - m_sceneDialogDisplayAction->setCheckable(true); - m_sceneDialogDisplayAction->setChecked(false); + m_sceneDialogDisplayAction->setCheckable(false); m_sceneDialogDisplayAction->blockSignals(false); m_sceneDialogDisplayAction->setObjectName("ToolBar:ShowScenesWindow"); WuQMacroManager::instance()->addMacroSupportToObject(m_sceneDialogDisplayAction, @@ -1786,9 +1786,9 @@ GuiManager::getSceneDialogDisplayAction() * New status (true display dialog, false hide it). */ void -GuiManager::sceneDialogDisplayActionToggled(bool status) +GuiManager::sceneDialogDisplayActionTriggered(bool /*status*/) { - showHideSceneDialog(status, + showHideSceneDialog(true, NULL); } @@ -1801,7 +1801,6 @@ void GuiManager::sceneDialogWasClosed() { m_sceneDialogDisplayAction->blockSignals(true); - m_sceneDialogDisplayAction->setChecked(false); m_sceneDialogDisplayAction->blockSignals(false); } @@ -1865,7 +1864,6 @@ GuiManager::showHideSceneDialog(const bool status, } m_sceneDialogDisplayAction->blockSignals(true); - m_sceneDialogDisplayAction->setChecked(status); m_sceneDialogDisplayAction->blockSignals(false); } @@ -1890,25 +1888,79 @@ GuiManager::processShowSceneDialog(BrainBrowserWindow* browserWindowIn) * * @param browserWindow * Parent of scene dialog if it needs to be created. - * @param sceneFile - * Scene File that contains the scene. + * @param sceneFileIn + * Scene File that contains the scene. If NULL, the scene file + * containing the scene will be located and used * @param scene * Scene that is displayed. + * @param showSceneDialogFlag + * If true, update the scene dialog. Otherwise, load the scene + * without showing the scene dialog */ void GuiManager::processShowSceneDialogAndScene(BrainBrowserWindow* browserWindow, - SceneFile* sceneFile, - Scene* scene) + SceneFile* sceneFileIn, + Scene* scene, + const bool showSceneDialogFlag) { - showHideSceneDialog(true, - browserWindow); + CaretAssert(browserWindow); + CaretAssert(scene); + + SceneFile* sceneFile(sceneFileIn); + if (sceneFile == NULL) { + /* + * If scene file is not valid, find scene file containing the scene + */ + Brain* brain = getBrain(); + CaretAssert(brain); + const int32_t numSceneFiles = brain->getNumberOfSceneFiles(); + for (int32_t i = 0; i < numSceneFiles; i++) { + SceneFile* sf = brain->getSceneFile(i); + CaretAssert(sf); + const int32_t numScenes = sf->getNumberOfScenes(); + for (int32_t j = 0; j < numScenes; j++) { + if (sf->getSceneAtIndex(j) == scene) { + sceneFile = sf; + break; + } + } + if (sceneFile != NULL) { + break; + } + } + + if (sceneFile == NULL) { + const QString msg("Cannot load scene. Unable to find scene file containing scene named \"" + + scene->getName() + + "\""); + WuQMessageBox::errorOk(browserWindow, + msg); + return; + } + } - const bool sceneWasDisplayed = this->sceneDialog->displayScene(sceneFile, - scene); - if (sceneWasDisplayed) { - showHideSceneDialog(false, - NULL); + /* + * Update scene dialog if it is open or if it should be displayed + */ + const bool updateSceneDialogFlag(showSceneDialogFlag + || (this->sceneDialog != NULL)); + if (updateSceneDialogFlag) { + showHideSceneDialog(true, + browserWindow); + + const bool sceneWasDisplayed = this->sceneDialog->displayScene(sceneFile, + scene); + if (sceneWasDisplayed) { + showHideSceneDialog(false, + NULL); + } } + else { + SceneDialog::displaySceneWithErrorMessageDialog(browserWindow, + sceneFile, + scene); + } + } /** diff --git a/src/GuiQt/GuiManager.h b/src/GuiQt/GuiManager.h index c99d47a67..7d72d9a34 100644 --- a/src/GuiQt/GuiManager.h +++ b/src/GuiQt/GuiManager.h @@ -154,7 +154,8 @@ namespace caret { void processShowSceneDialogAndScene(BrainBrowserWindow* browserWindow, SceneFile* sceneFile, - Scene* scene); + Scene* scene, + const bool showSceneDialogFlag); void processShowAllenDataBaseWebView(BrainBrowserWindow* browserWindow); void processShowConnectomeDataBaseWebView(BrainBrowserWindow* browserWindow); @@ -200,7 +201,7 @@ namespace caret { void showIdentifyBrainordinateDialogActionToggled(bool); - void sceneDialogDisplayActionToggled(bool); + void sceneDialogDisplayActionTriggered(bool); void showHelpDialogActionToggled(bool); diff --git a/src/GuiQt/SceneDialog.cxx b/src/GuiQt/SceneDialog.cxx index af26fd541..1d9aab1d8 100644 --- a/src/GuiQt/SceneDialog.cxx +++ b/src/GuiQt/SceneDialog.cxx @@ -1364,10 +1364,10 @@ SceneDialog::replaceAllScenesPushButtonClicked() * Display the scene */ AString errorMessage; - displayScenePrivateWithErrorMessage(sceneFile, - origScene, - false, - errorMessage); + SceneDialog::displayScenePrivateWithErrorMessage(sceneFile, + origScene, + false, + errorMessage); sceneNames.push_back(origScene->getName()); /* @@ -1657,10 +1657,10 @@ SceneDialog::testScenesPushButtonClicked() } AString errorMessage; - displayScenePrivateWithErrorMessage(sceneFile, - origScene, - false, - errorMessage); + SceneDialog::displayScenePrivateWithErrorMessage(sceneFile, + origScene, + false, + errorMessage); /* * Set the active scene */ @@ -2452,9 +2452,10 @@ SceneDialog::showSceneButtonClicked() this); progressDialog.setValue(0); - displayScenePrivateWithErrorMessageDialog(sceneFile, - scene, - false); + displayScenePrivateWithErrorMessageDialog(this, + sceneFile, + scene, + false); } /* Ensures macros dialog gets updated with active scene */ @@ -2598,9 +2599,10 @@ bool SceneDialog::displayScene(SceneFile* sceneFile, Scene* scene) { - const bool isSuccessful = displayScenePrivateWithErrorMessageDialog(sceneFile, - scene, - true); + const bool isSuccessful = displayScenePrivateWithErrorMessageDialog(this, + sceneFile, + scene, + true); loadSceneFileComboBox(sceneFile); loadScenesIntoDialog(scene); @@ -2615,17 +2617,47 @@ SceneDialog::displayScene(SceneFile* sceneFile, * If the scene fails to load, an error message is displayed * in a dialog. * + * @param dialogParent + * Parent for error message dialog * @param sceneFile * Scene file. * @param scene * Scene that is displayed. - * param showWaitCursor - * Show a wait cursor while loading scene * @return * true if scene was displayed without error, else false. */ bool -SceneDialog::displayScenePrivateWithErrorMessageDialog(SceneFile* sceneFile, +SceneDialog::displaySceneWithErrorMessageDialog(QWidget* dialogParent, + SceneFile* sceneFile, + Scene* scene) +{ + const bool showWaitCursorFlag(true); + const bool flag = SceneDialog::displayScenePrivateWithErrorMessageDialog(dialogParent, + sceneFile, + scene, + showWaitCursorFlag); + return flag; +} + +/** + * Display the given scene from the given scene file. + * If the scene fails to load, an error message is displayed + * in a dialog. + * + * @param dialogParent + * Parent for error message dialog + * @param sceneFile + * Scene file. + * @param scene + * Scene that is displayed. + * @param showWaitCursor + * If true, show a wait cursor + * @return + * true if scene was displayed without error, else false. + */ +bool +SceneDialog::displayScenePrivateWithErrorMessageDialog(QWidget* dialogParent, + SceneFile* sceneFile, Scene* scene, const bool showWaitCursor) { @@ -2644,7 +2676,7 @@ SceneDialog::displayScenePrivateWithErrorMessageDialog(SceneFile* sceneFile, CaretLogInfo(msg); if ( ! successFlag) { - WuQMessageBox::errorOk(this, + WuQMessageBox::errorOk(dialogParent, errorMessage); } diff --git a/src/GuiQt/SceneDialog.h b/src/GuiQt/SceneDialog.h index 1df6c24d3..0b4e4f5c2 100644 --- a/src/GuiQt/SceneDialog.h +++ b/src/GuiQt/SceneDialog.h @@ -68,6 +68,10 @@ namespace caret { static bool checkForModifiedFiles(const GuiManager::TestModifiedMode testMode, QWidget* parent); + static bool displaySceneWithErrorMessageDialog(QWidget* dialogParent, + SceneFile* sceneFile, + Scene* scene); + private: SceneDialog(const SceneDialog&); @@ -154,11 +158,12 @@ namespace caret { QWidget* createSceneFileWidget(); - bool displayScenePrivateWithErrorMessageDialog(SceneFile* sceneFile, - Scene* scene, - const bool showWaitCursor); - - bool displayScenePrivateWithErrorMessage(SceneFile* sceneFile, + static bool displayScenePrivateWithErrorMessageDialog(QWidget* dialogParent, + SceneFile* sceneFile, + Scene* scene, + const bool showWaitCursor); + + static bool displayScenePrivateWithErrorMessage(SceneFile* sceneFile, Scene* scene, const bool showWaitCursor, AString& errorMessageOut); -- GitLab From b43fbafc9092ace83173c0050e1b0623539245f3 Mon Sep 17 00:00:00 2001 From: John Harwell Date: Mon, 11 Mar 2019 16:34:55 -0500 Subject: [PATCH 226/427] Fixed overlap of 'menu down arrow' in toolbar's Scene button by setting button to both icon and text and using a single blank character for the text. --- src/GuiQt/BrainBrowserWindowToolBar.cxx | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/GuiQt/BrainBrowserWindowToolBar.cxx b/src/GuiQt/BrainBrowserWindowToolBar.cxx index 5f73c71f5..777330a51 100644 --- a/src/GuiQt/BrainBrowserWindowToolBar.cxx +++ b/src/GuiQt/BrainBrowserWindowToolBar.cxx @@ -290,15 +290,17 @@ BrainBrowserWindowToolBar::BrainBrowserWindowToolBar(const int32_t browserWindow "Hold down mouse button to display a menu for loading scenes " "including reloading the current scene."); QToolButton* sceneDialogToolButton = new QToolButton(); + sceneDialogToolButton->setText(""); sceneDialogToolButton->setIcon(GuiManager::get()->getSceneDialogDisplayAction()->icon()); sceneDialogToolButton->setDefaultAction(GuiManager::get()->getSceneDialogDisplayAction()); sceneDialogToolButton->setPopupMode(QToolButton::DelayedPopup); - sceneDialogToolButton->setToolButtonStyle(Qt::ToolButtonIconOnly); + sceneDialogToolButton->setText(" "); /* prevents 'menu down arrow' from overlapping icon */ + sceneDialogToolButton->setToolButtonStyle(Qt::ToolButtonTextBesideIcon); WuQtUtilities::setWordWrappedToolTip(sceneDialogToolButton, sceneButtonToolTip); QObject::connect(sceneDialogToolButton, &QToolButton::clicked, this, &BrainBrowserWindowToolBar::sceneToolButtonClicked); - + /* * The scene dialog tool button menu must be created AFTER the tool button. Seems weird. * https://forums.autodesk.com/t5/maya-forum/maya-2017-and-qtoolbutton-popup-menu-not-showing/td-p/6927575 -- GitLab From c3cf4f0c7c580551d4c50a0e7e6c9b297e74a3df Mon Sep 17 00:00:00 2001 From: John Harwell Date: Tue, 12 Mar 2019 09:17:46 -0500 Subject: [PATCH 227/427] Change style of scene tool button so that an arrow is displayed for the menu. --- src/GuiQt/BrainBrowserWindowToolBar.cxx | 9 ++++----- src/GuiQt/GuiManager.cxx | 1 + 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/GuiQt/BrainBrowserWindowToolBar.cxx b/src/GuiQt/BrainBrowserWindowToolBar.cxx index 777330a51..bf6d47714 100644 --- a/src/GuiQt/BrainBrowserWindowToolBar.cxx +++ b/src/GuiQt/BrainBrowserWindowToolBar.cxx @@ -286,15 +286,14 @@ BrainBrowserWindowToolBar::BrainBrowserWindowToolBar(const int32_t browserWindow QToolButton* helpDialogToolButton = new QToolButton(); helpDialogToolButton->setDefaultAction(GuiManager::get()->getHelpViewerDialogDisplayAction()); - const QString sceneButtonToolTip("Click to display the Scene Dialog. " - "Hold down mouse button to display a menu for loading scenes " - "including reloading the current scene."); + const QString sceneButtonToolTip("Display the Scene Dialog. " + "Arrow displays a menu for loading scenes " + "including reloading or the current scene."); QToolButton* sceneDialogToolButton = new QToolButton(); + sceneDialogToolButton->setPopupMode(QToolButton::MenuButtonPopup); sceneDialogToolButton->setText(""); sceneDialogToolButton->setIcon(GuiManager::get()->getSceneDialogDisplayAction()->icon()); sceneDialogToolButton->setDefaultAction(GuiManager::get()->getSceneDialogDisplayAction()); - sceneDialogToolButton->setPopupMode(QToolButton::DelayedPopup); - sceneDialogToolButton->setText(" "); /* prevents 'menu down arrow' from overlapping icon */ sceneDialogToolButton->setToolButtonStyle(Qt::ToolButtonTextBesideIcon); WuQtUtilities::setWordWrappedToolTip(sceneDialogToolButton, sceneButtonToolTip); diff --git a/src/GuiQt/GuiManager.cxx b/src/GuiQt/GuiManager.cxx index 372bf163d..4d929b93c 100644 --- a/src/GuiQt/GuiManager.cxx +++ b/src/GuiQt/GuiManager.cxx @@ -297,6 +297,7 @@ GuiManager::initializeGuiManager() m_sceneDialogDisplayAction->setCheckable(false); m_sceneDialogDisplayAction->blockSignals(false); m_sceneDialogDisplayAction->setObjectName("ToolBar:ShowScenesWindow"); + m_sceneDialogDisplayAction->setToolTip("Show the scenes window"); WuQMacroManager::instance()->addMacroSupportToObject(m_sceneDialogDisplayAction, "Display Scene Dialog"); -- GitLab From 652d0a230921bb6d4fac735e3a102ce805414b16 Mon Sep 17 00:00:00 2001 From: John Harwell Date: Tue, 12 Mar 2019 10:56:23 -0500 Subject: [PATCH 228/427] Removed popup for confirmation of deleting temporary images after creation of a movie. There is now a checkbox option on the Move Dialog's Settings page to set the status for deletion of temporary images. --- src/Brain/MovieRecorder.cxx | 37 ++++++++++++++++++++---- src/Brain/MovieRecorder.h | 8 +++++- src/GuiQt/MovieRecordingDialog.cxx | 46 +++++++++++++++++++++--------- src/GuiQt/MovieRecordingDialog.h | 5 ++++ 4 files changed, 77 insertions(+), 19 deletions(-) diff --git a/src/Brain/MovieRecorder.cxx b/src/Brain/MovieRecorder.cxx index c88069816..9caf71a8f 100644 --- a/src/Brain/MovieRecorder.cxx +++ b/src/Brain/MovieRecorder.cxx @@ -55,8 +55,7 @@ MovieRecorder::MovieRecorder() m_temporaryImagesDirectory = SystemUtilities::getTempDirectory(); m_tempImageFileNamePrefix = "movie"; m_tempImageFileNameSuffix = ".png"; - //QDir::tempPath(); - reset(); + removeTemporaryImages(); } /** @@ -64,7 +63,7 @@ MovieRecorder::MovieRecorder() */ MovieRecorder::~MovieRecorder() { - reset(); + removeTemporaryImages(); } /** @@ -146,10 +145,10 @@ MovieRecorder::addImageToMovieWithManualDuration(const QImage* image) } /** - * Reset by removing all images and starting a new movie + * Remove all images and starting a new movie */ void -MovieRecorder::reset() +MovieRecorder::removeTemporaryImages() { const QString nameFilter(m_tempImageFileNamePrefix + "*" @@ -431,6 +430,28 @@ MovieRecorder::setManualRecordingOfImageRequested(const bool requestFlag) m_manualRecordingOfImageRequested = requestFlag; } +/** + * @return True if temporary images should be removed + * after creation of a movie + */ +bool +MovieRecorder::isRemoveTemporaryImagesAfterMovieCreation() const +{ + return m_removeTemporaryImagesAfterMovieCreationFlag; +} + +/** + * Set temporary images should be removed after creation of a movie + * + * @param status + * New status + */ +void +MovieRecorder::setRemoveTemporaryImagesAfterMovieCreation(const bool status) +{ + m_removeTemporaryImagesAfterMovieCreationFlag = status; +} + /** * @return Number of frames (images) that have been recorded */ @@ -551,6 +572,12 @@ MovieRecorder::createMovie(AString& errorMessageOut) } } + if (successFlag) { + if (m_removeTemporaryImagesAfterMovieCreationFlag) { + removeTemporaryImages(); + } + } + return successFlag; } diff --git a/src/Brain/MovieRecorder.h b/src/Brain/MovieRecorder.h index a065a1e24..7012c4e41 100644 --- a/src/Brain/MovieRecorder.h +++ b/src/Brain/MovieRecorder.h @@ -96,7 +96,11 @@ namespace caret { void setManualRecordingOfImageRequested(const bool requestFlag); - void reset(); + bool isRemoveTemporaryImagesAfterMovieCreation() const; + + void setRemoveTemporaryImagesAfterMovieCreation(const bool status); + + void removeTemporaryImages(); bool createMovie(AString& errorMessageOut); @@ -150,6 +154,8 @@ namespace caret { AString m_tempImageFileNameSuffix; + bool m_removeTemporaryImagesAfterMovieCreationFlag = true; + const int32_t m_tempImageSequenceNumberOfDigits = 6; int32_t m_firstImageWidth = -1; diff --git a/src/GuiQt/MovieRecordingDialog.cxx b/src/GuiQt/MovieRecordingDialog.cxx index ced93d6ab..58e5169a6 100644 --- a/src/GuiQt/MovieRecordingDialog.cxx +++ b/src/GuiQt/MovieRecordingDialog.cxx @@ -24,6 +24,7 @@ #undef __MOVIE_RECORDING_DIALOG_DECLARE__ #include +#include #include #include #include @@ -206,6 +207,7 @@ MovieRecordingDialog::updateCustomWidthHeightSpinBoxes() m_customHeightSpinBox->setValue(customHeight); QSignalBlocker frameRateBlocker(m_frameRateSpinBox); m_frameRateSpinBox->setValue(movieRecorder->getFramesRate()); + m_removeTemporaryImagesAfterMovieCreationCheckBox->setChecked(movieRecorder->isRemoveTemporaryImagesAfterMovieCreation()); const bool customSpinBoxesEnabled(movieRecorder->getVideoResolutionType() == MovieRecorderVideoResolutionTypeEnum::CUSTOM); m_customWidthSpinBox->setEnabled(customSpinBoxesEnabled); @@ -333,6 +335,27 @@ MovieRecordingDialog::frameRateSpinBoxValueChanged(int frameRate) SessionManager::get()->getMovieRecorder()->setFramesRate(frameRate); } +/** + * @param Called when remove temporary images checkbox is clicked + * + * @param checked + * New checked status + */ +void +MovieRecordingDialog::removeTemporaryImagesCheckBoxClicked(bool checked) +{ + if ( ! checked) { + const QString text("If this is deselected, additional movies may contain images from previous movies."); + if ( ! WuQMessageBox::warningOkCancel(m_removeTemporaryImagesAfterMovieCreationCheckBox, + text)) { + checked = true; + } + } + + SessionManager::get()->getMovieRecorder()->setRemoveTemporaryImagesAfterMovieCreation(checked); + m_removeTemporaryImagesAfterMovieCreationCheckBox->setChecked(checked); +} + /** * Called when recording mode button is clicked * @@ -464,18 +487,7 @@ MovieRecordingDialog::createMoviePushButtonClicked() AString errorMessage; const bool successFlag = movieRecorder->createMovie(errorMessage); - if (successFlag) { - const QString text("Movie has been created. Reset (delete) recorded images for new movie? "); - const QString infoText("If no is selected, creation of a new movie will include all images from " - "this movie and any addition recorded images. Selecting yes is " - "equivalent to clicking the Reset button."); - if (WuQMessageBox::warningYesNo(m_createMoviePushButton, - text, - infoText)) { - SessionManager::get()->getMovieRecorder()->reset(); - } - } - else { + if ( ! successFlag) { WuQMessageBox::errorOk(m_createMoviePushButton, errorMessage); } @@ -491,7 +503,7 @@ MovieRecordingDialog::resetPushButtonClicked() { if (WuQMessageBox::warningOkCancel(m_resetPushButton, "Reset (delete) recorded images for new movie")) { - SessionManager::get()->getMovieRecorder()->reset(); + SessionManager::get()->getMovieRecorder()->removeTemporaryImages(); updateDialog(); } } @@ -671,6 +683,11 @@ MovieRecordingDialog::createSettingsWidget() QObject::connect(m_frameRateSpinBox, QOverload::of(&QSpinBox::valueChanged), this, &MovieRecordingDialog::frameRateSpinBoxValueChanged); + m_removeTemporaryImagesAfterMovieCreationCheckBox = new QCheckBox("Remove Temporary Images After Movie Creation"); + m_removeTemporaryImagesAfterMovieCreationCheckBox->setToolTip("Temporary images are removed after a movie is created"); + QObject::connect(m_removeTemporaryImagesAfterMovieCreationCheckBox, &QCheckBox::clicked, + this, &MovieRecordingDialog::removeTemporaryImagesCheckBoxClicked); + QWidget* widget = new QWidget(); QGridLayout* gridLayout = new QGridLayout(widget); gridLayout->setRowStretch(100, 100); @@ -691,6 +708,9 @@ MovieRecordingDialog::createSettingsWidget() gridLayout->addWidget(m_frameRateSpinBox, row, 1, 1, 2, Qt::AlignLeft); row++; + gridLayout->addWidget(m_removeTemporaryImagesAfterMovieCreationCheckBox, + row, 0, 1, 3, Qt::AlignLeft); + row++; return widget; } diff --git a/src/GuiQt/MovieRecordingDialog.h b/src/GuiQt/MovieRecordingDialog.h index 32338355b..f2f2cbfd1 100644 --- a/src/GuiQt/MovieRecordingDialog.h +++ b/src/GuiQt/MovieRecordingDialog.h @@ -30,6 +30,7 @@ #include "WuQDialogNonModal.h" class QAbstractButton; +class QCheckBox; class QLabel; class QPushButton; class QRadioButton; @@ -77,6 +78,8 @@ namespace caret { void frameRateSpinBoxValueChanged(int frameRate); + void removeTemporaryImagesCheckBoxClicked(bool checked); + void windowIndexSelected(const int32_t windowIndex); void fileNameButtonClicked(); @@ -121,6 +124,8 @@ namespace caret { QSpinBox* m_frameRateSpinBox; + QCheckBox* m_removeTemporaryImagesAfterMovieCreationCheckBox; + QPushButton* m_filenamePushButton; QPushButton* m_createMoviePushButton; -- GitLab From 302268bbf76bfe8434cff467bd0b76df09114e71 Mon Sep 17 00:00:00 2001 From: John Harwell Date: Tue, 12 Mar 2019 14:13:03 -0500 Subject: [PATCH 229/427] On Movie Dialog, remove button to set name of movie file. Instead, pop-up file selection dialog (without overwrite confirmation) when Create Movie button is clicked. Add menu to Toolbar's Movie button so that user can create movie without having to access movie dialog. --- src/Brain/MovieRecorder.cxx | 25 ++++++-- src/Brain/MovieRecorder.h | 30 +++++---- src/Brain/SessionManager.cxx | 2 +- src/GuiQt/BrainBrowserWindowToolBar.cxx | 59 ++++++++++++----- src/GuiQt/BrainBrowserWindowToolBar.h | 4 ++ src/GuiQt/GuiManager.cxx | 5 ++ src/GuiQt/MovieRecordingDialog.cxx | 84 +++++++++---------------- src/GuiQt/MovieRecordingDialog.h | 10 +-- 8 files changed, 120 insertions(+), 99 deletions(-) diff --git a/src/Brain/MovieRecorder.cxx b/src/Brain/MovieRecorder.cxx index 9caf71a8f..f6e5e2e39 100644 --- a/src/Brain/MovieRecorder.cxx +++ b/src/Brain/MovieRecorder.cxx @@ -28,6 +28,7 @@ #include #include +#include "Brain.h" #include "CaretAssert.h" #include "CaretLogger.h" #include "DataFileException.h" @@ -48,9 +49,13 @@ using namespace caret; /** * Constructor. + * + * @param brain + * The brain */ -MovieRecorder::MovieRecorder() -: CaretObject() +MovieRecorder::MovieRecorder(Brain* brain) +: CaretObject(), +m_brain(brain) { m_temporaryImagesDirectory = SystemUtilities::getTempDirectory(); m_tempImageFileNamePrefix = "movie"; @@ -322,6 +327,10 @@ MovieRecorder::setCaptureRegionType(const MovieRecorderCaptureRegionTypeEnum::En AString MovieRecorder::getMovieFileName() const { + if (m_movieFileName.isEmpty()) { + initializeMovieFileName(); + } + return m_movieFileName; } @@ -345,12 +354,13 @@ MovieRecorder::setMovieFileName(const AString& filename) * Path of where to place the movie file */ void -MovieRecorder::initializeMovieFileName(const AString& path) +MovieRecorder::initializeMovieFileName() const { + const QString currentDirectory = m_brain->getCurrentDirectory(); if (m_movieFileName.isEmpty()) { const QString extension = MovieRecorderVideoFormatTypeEnum::toFileNameExtensionNoDot(MovieRecorderVideoFormatTypeEnum::MPEG); - m_movieFileName = FileInformation::assembleFileComponents(path, + m_movieFileName = FileInformation::assembleFileComponents(currentDirectory, "Movie", extension); } @@ -464,16 +474,21 @@ MovieRecorder::getNumberOfFrames() const /** * Create the movie using images captured thus far * + * @param filename + * File name for movie. * @param errorMessageOut * Contains information if movie creation failed * @return * True if successful, else false */ bool -MovieRecorder::createMovie(AString& errorMessageOut) +MovieRecorder::createMovie(const AString& filename, + AString& errorMessageOut) { errorMessageOut.clear(); + m_movieFileName = filename; + if (m_movieFileName.isEmpty()) { errorMessageOut = "Movie file name is invalid or empty"; return false; diff --git a/src/Brain/MovieRecorder.h b/src/Brain/MovieRecorder.h index 7012c4e41..1804a82e3 100644 --- a/src/Brain/MovieRecorder.h +++ b/src/Brain/MovieRecorder.h @@ -34,12 +34,13 @@ class QImage; class QStringList; namespace caret { - + class Brain; + class MovieRecorder : public CaretObject { public: - MovieRecorder(); + MovieRecorder(Brain* brain); virtual ~MovieRecorder(); @@ -80,8 +81,6 @@ namespace caret { void setMovieFileName(const AString& filename); - void initializeMovieFileName(const AString& path); - int32_t getNumberOfFrames() const; float getFramesRate() const; @@ -102,8 +101,16 @@ namespace caret { void removeTemporaryImages(); - bool createMovie(AString& errorMessageOut); + bool createMovie(const AString& filename, + AString& errorMessageOut); + // ADD_NEW_METHODS_HERE + + virtual AString toString() const; + + private: + // ADD_NEW_MEMBERS_HERE + bool createMovieWithSystemCommand(const QString& programName, const QStringList& arguments, QString& errorMessageOut); @@ -111,19 +118,16 @@ namespace caret { bool createMovieWithQProcess(const QString& programName, const QStringList& arguments, QString& errorMessageOut); - + bool createMovieWithQProcessPipe(const QString& programName, const QStringList& arguments, const QString& textFileName, QString& errorMessageOut); - - // ADD_NEW_METHODS_HERE - - virtual AString toString() const; - private: - // ADD_NEW_MEMBERS_HERE - + void initializeMovieFileName() const; + + Brain* m_brain = NULL; + MovieRecorderModeEnum::Enum m_recordingMode = MovieRecorderModeEnum::MANUAL; MovieRecorderVideoResolutionTypeEnum::Enum m_resolutionType = MovieRecorderVideoResolutionTypeEnum::SD_640_480; diff --git a/src/Brain/SessionManager.cxx b/src/Brain/SessionManager.cxx index ca9ecd302..34ce2fdf1 100644 --- a/src/Brain/SessionManager.cxx +++ b/src/Brain/SessionManager.cxx @@ -78,7 +78,6 @@ SessionManager::SessionManager() m_caretPreferences = new CaretPreferences(); m_imageCaptureDialogSettings = new ImageCaptureSettings(); - m_movieRecorder.reset(new MovieRecorder()); m_ciftiConnectivityMatrixDataFileManager = new CiftiConnectivityMatrixDataFileManager(); m_ciftiFiberTrajectoryManager = new CiftiFiberTrajectoryManager(); @@ -109,6 +108,7 @@ SessionManager::SessionManager() Brain* brain = new Brain(m_caretPreferences); m_brains.push_back(brain); + m_movieRecorder.reset(new MovieRecorder(brain)); } /** diff --git a/src/GuiQt/BrainBrowserWindowToolBar.cxx b/src/GuiQt/BrainBrowserWindowToolBar.cxx index bf6d47714..1b71e1a7b 100644 --- a/src/GuiQt/BrainBrowserWindowToolBar.cxx +++ b/src/GuiQt/BrainBrowserWindowToolBar.cxx @@ -108,6 +108,7 @@ #include "ModelTransform.h" #include "ModelVolume.h" #include "ModelWholeBrain.h" +#include "MovieRecordingDialog.h" #include "OverlaySet.h" #include "Scene.h" #include "SceneAttributes.h" @@ -286,6 +287,9 @@ BrainBrowserWindowToolBar::BrainBrowserWindowToolBar(const int32_t browserWindow QToolButton* helpDialogToolButton = new QToolButton(); helpDialogToolButton->setDefaultAction(GuiManager::get()->getHelpViewerDialogDisplayAction()); + /* + * Scene button + */ const QString sceneButtonToolTip("Display the Scene Dialog. " "Arrow displays a menu for loading scenes " "including reloading or the current scene."); @@ -311,22 +315,36 @@ BrainBrowserWindowToolBar::BrainBrowserWindowToolBar(const int32_t browserWindow this, &BrainBrowserWindowToolBar::sceneToolButtonMenuAboutToShow); sceneDialogToolButton->setMenu(m_sceneToolButtonMenu); + /* + * Movie button + */ QIcon movieIcon; - QAction* movieAction = new QAction(); - if (WuQtUtilities::loadIcon(":/ToolBar/movie.png", - movieIcon)) { - movieAction->setIcon(movieIcon); - } - else { - movieAction->setText("R"); - } - movieAction->setToolTip("Show movie recording dialog"); - QObject::connect(movieAction, &QAction::triggered, + QString movieButtonText; + if ( ! WuQtUtilities::loadIcon(":/ToolBar/movie.png", + movieIcon)) { + movieButtonText = "Movie"; + } + const QString movieButtonToolTip("Show movie recording dialog. " + "Arrow displays a menu for creating movie " + "without using movie dialog."); + m_movieToolButton = new QToolButton(); + WuQtUtilities::setWordWrappedToolTip(m_movieToolButton, + movieButtonToolTip); + m_movieToolButton->setPopupMode(QToolButton::MenuButtonPopup); + m_movieToolButton->setText(movieButtonText); + m_movieToolButton->setIcon(movieIcon); + m_movieToolButton->setToolButtonStyle(Qt::ToolButtonTextBesideIcon); + QObject::connect(m_movieToolButton, &QToolButton::clicked, parentBrainBrowserWindow, &BrainBrowserWindow::processMovieRecording); - QToolButton* movieToolButton = new QToolButton(); - movieToolButton->setDefaultAction(movieAction); - movieAction->setParent(movieToolButton); + QMenu* movieToolButtonMenu = new QMenu(); + movieToolButtonMenu->addAction("Create Movie...", + this, &BrainBrowserWindowToolBar::createMovieToolButtonMenuItemTriggered); + m_movieToolButton->setMenu(movieToolButtonMenu); + + /* + * Macros button + */ QIcon macrosIcon; QAction* macrosAction = new QAction(); if (WuQtUtilities::loadIcon(":/ToolBar/macro.png", @@ -390,7 +408,7 @@ BrainBrowserWindowToolBar::BrainBrowserWindowToolBar(const int32_t browserWindow * Make all tool buttons the same height */ WuQtUtilities::matchWidgetHeights(macrosToolButton, - movieToolButton, + m_movieToolButton, helpDialogToolButton, informationDialogToolButton, identifyDialogToolButton, @@ -401,7 +419,7 @@ BrainBrowserWindowToolBar::BrainBrowserWindowToolBar(const int32_t browserWindow dataToolTipsToolButton); WuQtUtilities::setToolButtonStyleForQt5Mac(macrosToolButton); - WuQtUtilities::setToolButtonStyleForQt5Mac(movieToolButton); + WuQtUtilities::setToolButtonStyleForQt5Mac(m_movieToolButton); WuQtUtilities::setToolButtonStyleForQt5Mac(helpDialogToolButton); WuQtUtilities::setToolButtonStyleForQt5Mac(informationDialogToolButton); WuQtUtilities::setToolButtonStyleForQt5Mac(identifyDialogToolButton); @@ -422,7 +440,7 @@ BrainBrowserWindowToolBar::BrainBrowserWindowToolBar(const int32_t browserWindow tabBarLayout->addWidget(helpDialogToolButton); tabBarLayout->addWidget(informationDialogToolButton); tabBarLayout->addWidget(identifyDialogToolButton); - tabBarLayout->addWidget(movieToolButton); + tabBarLayout->addWidget(m_movieToolButton); tabBarLayout->addWidget(macrosToolButton); tabBarLayout->addWidget(sceneDialogToolButton); tabBarLayout->addWidget(toolBarToolButton); @@ -4120,6 +4138,15 @@ BrainBrowserWindowToolBar::sceneToolButtonMenuAboutToShow() } } +/** + * Called create movie menu item is selected from movie tool button + */ +void +BrainBrowserWindowToolBar::createMovieToolButtonMenuItemTriggered() +{ + MovieRecordingDialog::createMovie(m_movieToolButton); +} + /** * Called when custom view is triggered and displays Custom View Menu. */ diff --git a/src/GuiQt/BrainBrowserWindowToolBar.h b/src/GuiQt/BrainBrowserWindowToolBar.h index 355c7a70e..8a1ca8499 100644 --- a/src/GuiQt/BrainBrowserWindowToolBar.h +++ b/src/GuiQt/BrainBrowserWindowToolBar.h @@ -303,6 +303,8 @@ namespace caret { void sceneToolButtonClicked(); + void createMovieToolButtonMenuItemTriggered(); + private: QAction* orientationLateralMedialToolButtonAction; QAction* orientationDorsalVentralToolButtonAction; @@ -342,6 +344,8 @@ namespace caret { QMenu* m_sceneToolButtonMenu = NULL; + QToolButton* m_movieToolButton = NULL; + private slots: void orientationLeftOrLateralToolButtonTriggered(bool checked); void orientationRightOrMedialToolButtonTriggered(bool checked); diff --git a/src/GuiQt/GuiManager.cxx b/src/GuiQt/GuiManager.cxx index 4d929b93c..76fb393f5 100644 --- a/src/GuiQt/GuiManager.cxx +++ b/src/GuiQt/GuiManager.cxx @@ -1577,6 +1577,11 @@ GuiManager::receiveEvent(Event* event) warningEvent->setEventProcessed(); } + else if (event->getEventType() == EventTypeEnum::EVENT_USER_INTERFACE_UPDATE) { + if (m_movieRecordingDialog != NULL) { + m_movieRecordingDialog->updateDialog(); + } + } } /** diff --git a/src/GuiQt/MovieRecordingDialog.cxx b/src/GuiQt/MovieRecordingDialog.cxx index 58e5169a6..7a6b2ebf1 100644 --- a/src/GuiQt/MovieRecordingDialog.cxx +++ b/src/GuiQt/MovieRecordingDialog.cxx @@ -43,6 +43,7 @@ #include "Event.h" #include "EventManager.h" #include "EventGraphicsUpdateOneWindow.h" +#include "EventUserInterfaceUpdate.h" #include "EnumComboBoxTemplate.h" #include "FileInformation.h" #include "GuiManager.h" @@ -83,7 +84,6 @@ MovieRecordingDialog::MovieRecordingDialog(QWidget* parent) MovieRecorder* movieRecorder = SessionManager::get()->getMovieRecorder(); CaretAssert(movieRecorder); - movieRecorder->initializeMovieFileName(GuiManager::get()->getBrain()->getCurrentDirectory()); } /** @@ -162,7 +162,6 @@ MovieRecordingDialog::updateDialog() updateManualRecordingOptions(); updateCustomWidthHeightSpinBoxes(); - updateFileNameLabel(); updateFrameCountLabel(); } @@ -214,21 +213,6 @@ MovieRecordingDialog::updateCustomWidthHeightSpinBoxes() m_customHeightSpinBox->setEnabled(customSpinBoxesEnabled); } -/** - * Update the filename label - */ -void -MovieRecordingDialog::updateFileNameLabel() -{ - QString name = SessionManager::get()->getMovieRecorder()->getMovieFileName(); - if ( ! name.isEmpty()) { - FileInformation fileInfo(name); - name = fileInfo.getFileName(); - } - - m_filenameLabel->setText(name); -} - /** * Update the frame count label */ @@ -410,10 +394,22 @@ MovieRecordingDialog::manualCaptureSecondsSpinBoxValueChanged(int seconds) } /** - * Called when movie file name button is clicked + * Called when create movie push button is clicked + */ +void +MovieRecordingDialog::createMoviePushButtonClicked() +{ + createMovie(m_createMoviePushButton); +} + +/** + * Create a movie from captured images. + * + * @param parent + * Parent widget for error message dialog. */ void -MovieRecordingDialog::fileNameButtonClicked() +MovieRecordingDialog::createMovie(QWidget* parent) { MovieRecorder* movieRecorder = SessionManager::get()->getMovieRecorder(); QString currentFileName = movieRecorder->getMovieFileName(); @@ -435,12 +431,17 @@ MovieRecordingDialog::fileNameButtonClicked() } } - QString filename = CaretFileDialog::getSaveFileNameDialog(m_filenamePushButton, + QString filename = CaretFileDialog::getSaveFileNameDialog(parent, "Choose Movie File", currentFileName, filters, - &selectedFilter); + &selectedFilter, + CaretFileDialog::DontConfirmOverwrite); + if (filename.isEmpty()) { + return; + } + if (selectedFilter.isEmpty()) { for (auto fe : formatEnums) { if (selectedFilter == MovieRecorderVideoFormatTypeEnum::toFileDialogFilter(fe)) { @@ -453,46 +454,27 @@ MovieRecordingDialog::fileNameButtonClicked() } } - if ( ! filename.isEmpty()) { - movieRecorder->setMovieFileName(filename); - updateFileNameLabel(); - } -} - -/** - * Called when create movie push button is clicked - */ -void -MovieRecordingDialog::createMoviePushButtonClicked() -{ - MovieRecorder* movieRecorder = SessionManager::get()->getMovieRecorder(); - FileInformation fileInfo(movieRecorder->getMovieFileName()); + FileInformation fileInfo(filename); const QString name(fileInfo.getCanonicalFilePath()); if (fileInfo.exists()) { - AString msg("Movie file \"" - + name - + "\" exists, overwrite?"); - if ( ! WuQMessageBox::warningOkCancel(m_createMoviePushButton, - msg)) { - return; - } if ( ! fileInfo.remove()) { AString msg("Unable to remove movie file \"" + name + "\""); - WuQMessageBox::errorOk(m_createMoviePushButton, + WuQMessageBox::errorOk(parent, msg); } } AString errorMessage; - const bool successFlag = movieRecorder->createMovie(errorMessage); + const bool successFlag = movieRecorder->createMovie(filename, + errorMessage); if ( ! successFlag) { - WuQMessageBox::errorOk(m_createMoviePushButton, + WuQMessageBox::errorOk(parent, errorMessage); } - updateDialog(); + EventManager::get()->sendEvent(EventUserInterfaceUpdate().getPointer()); } /** @@ -588,13 +570,6 @@ MovieRecordingDialog::createMainWidget() modeLayout->addWidget(captureSecondsLabel, modeRow, 3); modeRow++; - m_filenamePushButton = new QPushButton("Movie File..."); - m_filenamePushButton->setToolTip("Select movie file using file dialog"); - QObject::connect(m_filenamePushButton, &QPushButton::clicked, - this, &MovieRecordingDialog::fileNameButtonClicked); - m_filenameLabel = new QLabel(""); - m_filenameLabel->setMinimumWidth(150); - m_createMoviePushButton = new QPushButton("Create Movie"); m_createMoviePushButton->setToolTip("Create a movie file using images that have been recorded"); QObject::connect(m_createMoviePushButton, &QPushButton::clicked, this, @@ -618,9 +593,6 @@ MovieRecordingDialog::createMainWidget() movieLayout->setColumnStretch(2, 0); movieLayout->setColumnStretch(3, 100); int32_t movieRow(0); - movieLayout->addWidget(m_filenamePushButton, movieRow, 0); - movieLayout->addWidget(m_filenameLabel, movieRow, 1, 1, 2, Qt::AlignLeft); - movieRow++; movieLayout->addWidget(m_createMoviePushButton, movieRow, 0); movieLayout->addWidget(lengthLabel, movieRow, 1); movieLayout->addWidget(m_lengthLabel, movieRow, 2); diff --git a/src/GuiQt/MovieRecordingDialog.h b/src/GuiQt/MovieRecordingDialog.h index f2f2cbfd1..27ad6cd25 100644 --- a/src/GuiQt/MovieRecordingDialog.h +++ b/src/GuiQt/MovieRecordingDialog.h @@ -65,6 +65,8 @@ namespace caret { virtual void receiveEvent(Event* event); + static void createMovie(QWidget* parent); + private slots: void movieRecorderVideoResolutionTypeEnumComboBoxItemActivated(); @@ -82,8 +84,6 @@ namespace caret { void windowIndexSelected(const int32_t windowIndex); - void fileNameButtonClicked(); - void createMoviePushButtonClicked(); void resetPushButtonClicked(); @@ -96,8 +96,6 @@ namespace caret { virtual void closeEvent(QCloseEvent* event) override; private: - void updateFileNameLabel(); - void updateFrameCountLabel(); void updateCustomWidthHeightSpinBoxes(); @@ -126,14 +124,10 @@ namespace caret { QCheckBox* m_removeTemporaryImagesAfterMovieCreationCheckBox; - QPushButton* m_filenamePushButton; - QPushButton* m_createMoviePushButton; QPushButton* m_resetPushButton; - QLabel* m_filenameLabel; - QLabel* m_frameCountNumberLabel; QLabel* m_lengthLabel; -- GitLab From 06b4f405c910cb6c87aed3d3995fbe085e6611a3 Mon Sep 17 00:00:00 2001 From: John Harwell Date: Tue, 12 Mar 2019 15:32:34 -0500 Subject: [PATCH 230/427] Update macro dialog so that macro name and description and macro command description can be directly edited (no longer have to press button to change text). --- src/GuiQt/WuQMacroDialog.cxx | 116 ++++++++++++++++++---------------- src/GuiQt/WuQMacroDialog.h | 15 ++++- src/GuiQt/WuQMacroManager.cxx | 3 +- 3 files changed, 74 insertions(+), 60 deletions(-) diff --git a/src/GuiQt/WuQMacroDialog.cxx b/src/GuiQt/WuQMacroDialog.cxx index 2d419f4fb..7b9a5c8fc 100644 --- a/src/GuiQt/WuQMacroDialog.cxx +++ b/src/GuiQt/WuQMacroDialog.cxx @@ -302,12 +302,10 @@ WuQMacroDialog::createMacroAndCommandSelectionWidget() QWidget* WuQMacroDialog::createMacroDisplayWidget() { - m_macroNameLabel = new QLabel(); - QPushButton* nameEditPushButton = new QPushButton("Name..."); - nameEditPushButton->setSizePolicy(QSizePolicy::Fixed, - nameEditPushButton->sizePolicy().verticalPolicy()); - QObject::connect(nameEditPushButton, &QPushButton::clicked, - this, &WuQMacroDialog::macroNameEditButtonClicked); + QLabel* macroNameLabel = new QLabel("Name:"); + m_macroNameLineEdit = new QLineEdit(); + QObject::connect(m_macroNameLineEdit, &QLineEdit::textChanged, + this, &WuQMacroDialog::macroNameLineEditTextChanged); QLabel* shortCutKeyLabel = new QLabel("Short Cut Key:"); QLabel* shortCutKeyMaskLabel = new QLabel(WuQMacroManager::getShortCutKeysMask()); @@ -315,30 +313,26 @@ WuQMacroDialog::createMacroDisplayWidget() QObject::connect(m_macroShortCutKeyComboBox, &WuQMacroShortCutKeyComboBox::shortCutKeySelected, this, &WuQMacroDialog::macroShortCutKeySelected); + QLabel* descriptionLabel = new QLabel("Description:"); m_macroDescriptionTextEdit = new QPlainTextEdit(); - m_macroDescriptionTextEdit->setReadOnly(true); m_macroDescriptionTextEdit->setFixedHeight(100); + QObject::connect(m_macroDescriptionTextEdit, &QPlainTextEdit::textChanged, + this, &WuQMacroDialog::macroDescriptionTextEditChanged); - QPushButton* descriptionEditPushButton = new QPushButton("Description..."); - descriptionEditPushButton->setSizePolicy(QSizePolicy::Fixed, - descriptionEditPushButton->sizePolicy().verticalPolicy()); - QObject::connect(descriptionEditPushButton, &QPushButton::clicked, - this, &WuQMacroDialog::macroDescriptionEditButtonClicked); - QGridLayout* gridLayout = new QGridLayout(); gridLayout->setContentsMargins(0, 0, 0, 0); gridLayout->setColumnStretch(0, 0); gridLayout->setColumnStretch(1, 0); gridLayout->setColumnStretch(2, 100); int row = 0; - gridLayout->addWidget(nameEditPushButton, row, 0); - gridLayout->addWidget(m_macroNameLabel, row, 1, 1, 2, Qt::AlignLeft); + gridLayout->addWidget(macroNameLabel, row, 0); + gridLayout->addWidget(m_macroNameLineEdit, row, 1, 1, 2); row++; gridLayout->addWidget(shortCutKeyLabel, row, 0); gridLayout->addWidget(shortCutKeyMaskLabel, row, 1); gridLayout->addWidget(m_macroShortCutKeyComboBox->getWidget(), row, 2, Qt::AlignLeft); row++; - gridLayout->addWidget(descriptionEditPushButton, row, 0, Qt::AlignTop); + gridLayout->addWidget(descriptionLabel, row, 0, Qt::AlignTop); gridLayout->addWidget(m_macroDescriptionTextEdit, row, 1, 1, 2); row++; @@ -365,27 +359,18 @@ WuQMacroDialog::createMacroDisplayWidget() } /** - * Called to edit macro name + * Called when macro name line edit text changed + * @param text */ void -WuQMacroDialog::macroNameEditButtonClicked() +WuQMacroDialog::macroNameLineEditTextChanged(const QString& text) { WuQMacro* macro = getSelectedMacro(); if (macro != NULL) { - bool ok(false); - const QString text = QInputDialog::getText(this, - "New Macro Name", - "Name", - QLineEdit::Normal, - macro->getName(), - &ok); - if (ok) { - if ( ! text.isEmpty()) { - macro->setName(text); - WuQMacroManager::instance()->macroWasModified(macro); - updateMacroWidget(macro); - } - } + macro->setName(text); + m_macroNameLineEditBlockUpdateFlag = true; + WuQMacroManager::instance()->macroWasModified(macro); + m_macroNameLineEditBlockUpdateFlag = false; } } @@ -407,26 +392,18 @@ WuQMacroDialog::macroShortCutKeySelected(const WuQMacroShortCutKeyEnum::Enum sho } /** - * Called to edit macro description + * Called when macro description text edit is changed */ void -WuQMacroDialog::macroDescriptionEditButtonClicked() +WuQMacroDialog::macroDescriptionTextEditChanged() { WuQMacro* macro = getSelectedMacro(); if (macro != NULL) { - bool ok(false); - const QString text = QInputDialog::getMultiLineText(this, - "New Macro Description", - "Description", - macro->getDescription(), - &ok); - if (ok) { - if ( ! text.isEmpty()) { - macro->setDescription(text); - WuQMacroManager::instance()->macroWasModified(macro); - updateMacroWidget(macro); - } - } + const QString text = m_macroDescriptionTextEdit->toPlainText(); + macro->setDescription(text); + m_macroDescriptionTextEditBlockUpdateFlag = true; + WuQMacroManager::instance()->macroWasModified(macro); + m_macroDescriptionTextEditBlockUpdateFlag = false; } } @@ -555,9 +532,10 @@ WuQMacroDialog::createCommandDisplayWidget() QLabel* descriptionLabel = new QLabel("Description:"); m_commandDescriptionTextEdit = new QPlainTextEdit(); - m_commandDescriptionTextEdit->setReadOnly(true); m_commandDescriptionTextEdit->setMaximumHeight(100); - + QObject::connect(m_commandDescriptionTextEdit, &QPlainTextEdit::textChanged, + this, &WuQMacroDialog::macroCommandDescriptionTextEditChanged); + QGridLayout* layout = new QGridLayout(); layout->setContentsMargins(0, 0, 0, 0); layout->setColumnStretch(0, 0); @@ -836,9 +814,15 @@ WuQMacroDialog::updateMacroWidget(WuQMacro* macro) shortCutKey = macro->getShortCutKey(); } - m_macroNameLabel->setText(name); + if ( ! m_macroNameLineEditBlockUpdateFlag) { + m_macroNameLineEdit->setText(name); + } + m_macroShortCutKeyComboBox->setSelectedShortCutKey(shortCutKey); - m_macroDescriptionTextEdit->setPlainText(text); + if ( ! m_macroDescriptionTextEditBlockUpdateFlag) { + QSignalBlocker DescriptionBlocker(m_macroDescriptionTextEdit); + m_macroDescriptionTextEdit->setPlainText(text); + } } /** @@ -887,7 +871,10 @@ WuQMacroDialog::updateCommandWidget(WuQMacroCommand* command) QSignalBlocker delayBlocker(m_commandDelaySpinBox); m_commandDelaySpinBox->setValue(delay); - m_commandDescriptionTextEdit->setPlainText(toolTip); + if ( ! m_macroDescriptionCommandTextEditBlockUpdateFlag) { + QSignalBlocker descriptionBlocker(m_commandDescriptionTextEdit); + m_commandDescriptionTextEdit->setPlainText(toolTip); + } /** * Update the parameter widgets @@ -1170,10 +1157,27 @@ WuQMacroDialog::exportMacroGroupActionTriggered() WuQMacroGroup* macroGroup = getSelectedMacroGroup(); WuQMacro* macro = getSelectedMacro(); - if (WuQMacroManager::instance()->exportMacros(m_macroGroupToolButton, - macroGroup, - macro)) { - updateDialogContents(); + if (macro != NULL) { + if (WuQMacroManager::instance()->exportMacros(m_macroGroupToolButton, + macroGroup, + macro)) { + updateDialogContents(); + } + } +} + +/** + * Called when macro command description text edit is changed + */ +void +WuQMacroDialog::macroCommandDescriptionTextEditChanged() +{ + WuQMacroCommand* command = getSelectedMacroCommand(); + if (command != NULL) { + command->setObjectToolTip(m_commandDescriptionTextEdit->toPlainText()); + m_macroDescriptionCommandTextEditBlockUpdateFlag = true; + WuQMacroManager::instance()->macroWasModified(getSelectedMacro()); + m_macroDescriptionCommandTextEditBlockUpdateFlag = false; } } diff --git a/src/GuiQt/WuQMacroDialog.h b/src/GuiQt/WuQMacroDialog.h index 082681590..605e41923 100644 --- a/src/GuiQt/WuQMacroDialog.h +++ b/src/GuiQt/WuQMacroDialog.h @@ -38,6 +38,7 @@ class QDialogButtonBox; class QDoubleSpinBox; class QGridLayout; class QLabel; +class QLineEdit; class QMenu; class QPlainTextEdit; class QStackedWidget; @@ -85,9 +86,9 @@ namespace caret { void macroGroupToolButtonClicked(); - void macroNameEditButtonClicked(); + void macroNameLineEditTextChanged(const QString& text); - void macroDescriptionEditButtonClicked(); + void macroDescriptionTextEditChanged(); void macroShortCutKeySelected(const WuQMacroShortCutKeyEnum::Enum); @@ -113,6 +114,8 @@ namespace caret { void macroCommandDelaySpinBoxValueChanged(double); + void macroCommandDescriptionTextEditChanged(); + void commandParamaterDataChanged(int); void insertMenuCopyMacroSelected(); @@ -187,12 +190,16 @@ namespace caret { QTreeView* m_treeView; - QLabel* m_macroNameLabel; + QLineEdit* m_macroNameLineEdit; + + bool m_macroNameLineEditBlockUpdateFlag = false; WuQMacroShortCutKeyComboBox* m_macroShortCutKeyComboBox; QPlainTextEdit* m_macroDescriptionTextEdit; + bool m_macroDescriptionTextEditBlockUpdateFlag = false; + QWidget* m_macroWidget; QWidget* m_commandWidget; @@ -221,6 +228,8 @@ namespace caret { QPlainTextEdit* m_commandDescriptionTextEdit; + bool m_macroDescriptionCommandTextEditBlockUpdateFlag = false; + std::vector m_parameterWidgets; QGridLayout* m_parameterWidgetsGridLayout; diff --git a/src/GuiQt/WuQMacroManager.cxx b/src/GuiQt/WuQMacroManager.cxx index 45e820dc7..3bd0a234a 100644 --- a/src/GuiQt/WuQMacroManager.cxx +++ b/src/GuiQt/WuQMacroManager.cxx @@ -858,7 +858,8 @@ WuQMacroManager::exportMacros(QWidget* parent, s_importExportMacroFileDirectory, fileFilterString, &fileFilterString, - QFileDialog::DontUseNativeDialog); + (QFileDialog::DontUseNativeDialog + | QFileDialog::DontConfirmOverwrite)); if ( ! filename.isEmpty()) { try { WuQMacroFile macroFile; -- GitLab From fe2079d317388b90607d629c78231463a0582db6 Mon Sep 17 00:00:00 2001 From: John Harwell Date: Tue, 12 Mar 2019 15:43:29 -0500 Subject: [PATCH 231/427] One last update, wrong signal was used from QLineEdit that resulted in an empty macro name when the selection in macro dialog changed from macro to another macro or macro command. --- src/GuiQt/WuQMacroDialog.cxx | 6 +++--- src/GuiQt/WuQMacroDialog.h | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/GuiQt/WuQMacroDialog.cxx b/src/GuiQt/WuQMacroDialog.cxx index 7b9a5c8fc..a23db9bbf 100644 --- a/src/GuiQt/WuQMacroDialog.cxx +++ b/src/GuiQt/WuQMacroDialog.cxx @@ -304,8 +304,8 @@ WuQMacroDialog::createMacroDisplayWidget() { QLabel* macroNameLabel = new QLabel("Name:"); m_macroNameLineEdit = new QLineEdit(); - QObject::connect(m_macroNameLineEdit, &QLineEdit::textChanged, - this, &WuQMacroDialog::macroNameLineEditTextChanged); + QObject::connect(m_macroNameLineEdit, &QLineEdit::textEdited, + this, &WuQMacroDialog::macroNameLineEditTextEdited); QLabel* shortCutKeyLabel = new QLabel("Short Cut Key:"); QLabel* shortCutKeyMaskLabel = new QLabel(WuQMacroManager::getShortCutKeysMask()); @@ -363,7 +363,7 @@ WuQMacroDialog::createMacroDisplayWidget() * @param text */ void -WuQMacroDialog::macroNameLineEditTextChanged(const QString& text) +WuQMacroDialog::macroNameLineEditTextEdited(const QString& text) { WuQMacro* macro = getSelectedMacro(); if (macro != NULL) { diff --git a/src/GuiQt/WuQMacroDialog.h b/src/GuiQt/WuQMacroDialog.h index 605e41923..f954a5331 100644 --- a/src/GuiQt/WuQMacroDialog.h +++ b/src/GuiQt/WuQMacroDialog.h @@ -86,7 +86,7 @@ namespace caret { void macroGroupToolButtonClicked(); - void macroNameLineEditTextChanged(const QString& text); + void macroNameLineEditTextEdited(const QString& text); void macroDescriptionTextEditChanged(); -- GitLab From 63a3644380daa230d27db4d2f72b11a164c36127 Mon Sep 17 00:00:00 2001 From: John Harwell Date: Wed, 13 Mar 2019 11:03:41 -0500 Subject: [PATCH 232/427] Macros: Add ability to run a macro but stop after a selected command via two methods: (1) Select the macro, check the 'stop after selected command', select the command, press Run button. (2) Right-click over a command and select 'Run and Stop After this Command' from the pop-up menu. --- src/GuiQt/WuQMacroDialog.cxx | 96 ++++++++++++++++++++++++--- src/GuiQt/WuQMacroDialog.h | 10 ++- src/GuiQt/WuQMacroExecutor.cxx | 23 +++++++ src/GuiQt/WuQMacroExecutor.h | 2 + src/GuiQt/WuQMacroExecutorOptions.cxx | 29 +++++++- src/GuiQt/WuQMacroExecutorOptions.h | 6 ++ src/GuiQt/WuQMacroManager.cxx | 9 ++- src/GuiQt/WuQMacroManager.h | 3 +- 8 files changed, 160 insertions(+), 18 deletions(-) diff --git a/src/GuiQt/WuQMacroDialog.cxx b/src/GuiQt/WuQMacroDialog.cxx index a23db9bbf..78d3cc489 100644 --- a/src/GuiQt/WuQMacroDialog.cxx +++ b/src/GuiQt/WuQMacroDialog.cxx @@ -292,6 +292,9 @@ WuQMacroDialog::createMacroAndCommandSelectionWidget() m_treeView->setHeaderHidden(true); QObject::connect(m_treeView, &QTreeView::clicked, this, &WuQMacroDialog::treeViewItemClicked); + m_treeView->setContextMenuPolicy(Qt::CustomContextMenu); + QObject::connect(m_treeView, &QTreeView::customContextMenuRequested, + this, &WuQMacroDialog::treeViewCustomContextMenuRequested); return m_treeView; } @@ -432,12 +435,19 @@ WuQMacroDialog::createRunOptionsWidget() QObject::connect(m_runOptionMoveMouseCheckBox, &QCheckBox::clicked, this, &WuQMacroDialog::runOptionMoveMouseCheckBoxClicked); - m_recordMovieWhileMacroRunsCheckBox = new QCheckBox("Record Movie While Macro Runs"); - m_recordMovieWhileMacroRunsCheckBox->setChecked(false); - m_recordMovieWhileMacroRunsCheckBox->setToolTip("While the macro runs, add images to Movie Recorder"); - QObject::connect(m_recordMovieWhileMacroRunsCheckBox, &QCheckBox::clicked, + m_runOptionRecordMovieWhileMacroRunsCheckBox = new QCheckBox("Record movie while macro runs"); + m_runOptionRecordMovieWhileMacroRunsCheckBox->setChecked(false); + m_runOptionRecordMovieWhileMacroRunsCheckBox->setToolTip("While the macro runs, add images to Movie Recorder"); + QObject::connect(m_runOptionRecordMovieWhileMacroRunsCheckBox, &QCheckBox::clicked, this, &WuQMacroDialog::runOptionRecordMovieCheckBoxClicked); + m_runOptionStopAfterSelectedCommandCheckBox = new QCheckBox("Stop after selected command"); + const QString stopToolTip("Stop execution of macro after the selected command. One can also right-click " + "over a command to Run the macro and stop after the command."); + m_runOptionStopAfterSelectedCommandCheckBox->setToolTip(QTextDocument(stopToolTip).toHtml()); + QObject::connect(m_runOptionStopAfterSelectedCommandCheckBox, &QCheckBox::clicked, + this, &WuQMacroDialog::runOptionStopAfterSelectedCommandCheckBoxClicked); + QGridLayout* runOptionsLayout = new QGridLayout(widget); runOptionsLayout->setColumnStretch(0, 0); runOptionsLayout->setColumnStretch(1, 100); @@ -449,7 +459,9 @@ WuQMacroDialog::createRunOptionsWidget() row++; runOptionsLayout->addWidget(m_runOptionMoveMouseCheckBox, row, 0, 1, 2, Qt::AlignLeft); row++; - runOptionsLayout->addWidget(m_recordMovieWhileMacroRunsCheckBox, row, 0, 1, 2, Qt::AlignLeft); + runOptionsLayout->addWidget(m_runOptionRecordMovieWhileMacroRunsCheckBox, row, 0, 1, 2, Qt::AlignLeft); + row++; + runOptionsLayout->addWidget(m_runOptionStopAfterSelectedCommandCheckBox, row, 0, 1, 2, Qt::AlignLeft); row++; return widget; @@ -458,8 +470,8 @@ WuQMacroDialog::createRunOptionsWidget() /** * Called when run options move mouse checkbox is changed * - * @param value - * New value. + * @param checked + * New checked status. */ void WuQMacroDialog::runOptionMoveMouseCheckBoxClicked(bool checked) @@ -472,8 +484,8 @@ WuQMacroDialog::runOptionMoveMouseCheckBoxClicked(bool checked) /** * Called when run options loop checkbox is changed * - * @param value - * New value. + * @param checked + * New checked status. */ void WuQMacroDialog::runOptionLoopCheckBoxClicked(bool checked) @@ -483,6 +495,12 @@ WuQMacroDialog::runOptionLoopCheckBoxClicked(bool checked) options->setLooping(checked); } +/** + * Called when run options record movie checkbox is changed + * + * @param checked + * New checked status. + */ void WuQMacroDialog::runOptionRecordMovieCheckBoxClicked(bool checked) { @@ -491,6 +509,20 @@ WuQMacroDialog::runOptionRecordMovieCheckBoxClicked(bool checked) options->setRecordMovieDuringExecution(checked); } +/** + * Called when run options stop after selected command checkbox is changed + * + * @param checked + * New checked status. + */ +void +WuQMacroDialog::runOptionStopAfterSelectedCommandCheckBoxClicked(bool checked) +{ + WuQMacroExecutorOptions* options = WuQMacroManager::instance()->getExecutorOptions(); + CaretAssert(options); + options->setStopAfterSelectedCommand(checked); +} + /** * Called when a button in dialog is clicked * @@ -635,7 +667,8 @@ WuQMacroDialog::updateDialogContents() CaretAssert(runOptions); m_runOptionMoveMouseCheckBox->setChecked(runOptions->isShowMouseMovement()); m_runOptionLoopCheckBox->setChecked(runOptions->isLooping()); - m_recordMovieWhileMacroRunsCheckBox->setChecked(runOptions->isRecordMovieDuringExecution()); + m_runOptionRecordMovieWhileMacroRunsCheckBox->setChecked(runOptions->isRecordMovieDuringExecution()); + m_runOptionStopAfterSelectedCommandCheckBox->setChecked(runOptions->isStopAfterSelectedCommand()); macroGroupComboBoxActivated(selectedIndex); } @@ -657,6 +690,46 @@ WuQMacroDialog::treeViewItemClicked(const QModelIndex& /*modelIndex*/) */ } +/** + * Called to display custom context menu for the tree view + */ +void +WuQMacroDialog::treeViewCustomContextMenuRequested(const QPoint& pos) +{ + QModelIndex modelIndex = m_treeView->indexAt(pos); + if (modelIndex.isValid()) { + WuQMacroCommand* command = getSelectedMacroCommand(); + if (command != NULL) { + QMenu menu(this); + const QString runToText("Run Macro and Stop After this Command"); + menu.addAction(runToText, + this, &WuQMacroDialog::runAndStopAfterSelectedCommandMenuItemSelected); + menu.exec(m_treeView->mapToGlobal(pos)); + } + } +} + +/** + * Called when 'run and stop after...' is selected from + * context (pop-up) menu. Runs macro to selected command. + */ +void +WuQMacroDialog::runAndStopAfterSelectedCommandMenuItemSelected() +{ + WuQMacroCommand* command = getSelectedMacroCommand(); + if (command != NULL) { + WuQMacroManager* macroManager = WuQMacroManager::instance(); + WuQMacroExecutorOptions* runOptions = macroManager->getExecutorOptions(); + const bool savedStopAfterOptionFlag = runOptions->isStopAfterSelectedCommand(); + runOptions->setStopAfterSelectedCommand(true); + runMacroToolButtonClicked(); + runOptions->setStopAfterSelectedCommand(savedStopAfterOptionFlag); + } + else { + QMessageBox::warning(this, "Error", "No macro command is selected"); + } +} + /** * Called when an item in the tree is selected in some way * (mouse click, arrow key, etc) @@ -1117,7 +1190,8 @@ WuQMacroDialog::runMacroToolButtonClicked() updateEditingToolButtons(); QApplication::processEvents(); WuQMacroManager::instance()->runMacro(window, - macro); + macro, + getSelectedMacroCommand()); m_macroIsRunningFlag = false; updateEditingToolButtons(); diff --git a/src/GuiQt/WuQMacroDialog.h b/src/GuiQt/WuQMacroDialog.h index f954a5331..9e270d27f 100644 --- a/src/GuiQt/WuQMacroDialog.h +++ b/src/GuiQt/WuQMacroDialog.h @@ -76,6 +76,10 @@ namespace caret { private slots: void treeViewItemClicked(const QModelIndex& modelIndex); + void treeViewCustomContextMenuRequested(const QPoint& pos); + + void runAndStopAfterSelectedCommandMenuItemSelected(); + void macroGroupComboBoxActivated(int); void buttonBoxButtonClicked(QAbstractButton* button); @@ -98,6 +102,8 @@ namespace caret { void runOptionRecordMovieCheckBoxClicked(bool); + void runOptionStopAfterSelectedCommandCheckBoxClicked(bool); + void editingMoveUpToolButtonClicked(); void editingMoveDownToolButtonClicked(); @@ -216,7 +222,9 @@ namespace caret { QCheckBox* m_runOptionMoveMouseCheckBox; - QCheckBox* m_recordMovieWhileMacroRunsCheckBox; + QCheckBox* m_runOptionRecordMovieWhileMacroRunsCheckBox; + + QCheckBox* m_runOptionStopAfterSelectedCommandCheckBox; QLabel* m_commandTitleLabel; diff --git a/src/GuiQt/WuQMacroExecutor.cxx b/src/GuiQt/WuQMacroExecutor.cxx index 2e88a6b4c..4ae9dde52 100644 --- a/src/GuiQt/WuQMacroExecutor.cxx +++ b/src/GuiQt/WuQMacroExecutor.cxx @@ -263,6 +263,8 @@ WuQMacroExecutor::findObjectByName(const QString& objectName) const * * @param macro * Macro that is run + * @param macroCommandToStopAfter + * Macro command that the executor may stop after, depending upon options * @param window * Widget for parent * @param otherObjectParents @@ -279,6 +281,7 @@ WuQMacroExecutor::findObjectByName(const QString& objectName) const */ bool WuQMacroExecutor::runMacro(const WuQMacro* macro, + const WuQMacroCommand* macroCommandToStopAfter, QWidget* window, std::vector& otherObjectParents, const WuQMacroExecutorMonitor* executorMonitor, @@ -286,6 +289,7 @@ WuQMacroExecutor::runMacro(const WuQMacro* macro, QString& errorMessageOut) const { const bool result = runMacroPrivate(macro, + macroCommandToStopAfter, window, otherObjectParents, executorMonitor, @@ -300,6 +304,8 @@ WuQMacroExecutor::runMacro(const WuQMacro* macro, * * @param macro * Macro that is run + * @param macroCommandToStopAfter + * Macro command that the executor may stop after, depending upon options * @param window * Widget for parent * @param otherObjectParents @@ -316,6 +322,7 @@ WuQMacroExecutor::runMacro(const WuQMacro* macro, */ bool WuQMacroExecutor::runMacroPrivate(const WuQMacro* macro, + const WuQMacroCommand* macroCommandToStopAfter, QWidget* window, std::vector& otherObjectParents, const WuQMacroExecutorMonitor* executorMonitor, @@ -331,7 +338,14 @@ WuQMacroExecutor::runMacroPrivate(const WuQMacro* macro, otherObjectParents.begin(), otherObjectParents.end()); m_runOptions = *executorOptions; + if (m_runOptions.isStopAfterSelectedCommand()) { + if (macroCommandToStopAfter == NULL) { + errorMessageOut = ("Option to stop after selected command is on but no command is selected"); + return false; + } + } + errorMessageOut.clear(); const int32_t numberOfMacroCommands = macro->getNumberOfMacroCommands(); @@ -447,6 +461,15 @@ WuQMacroExecutor::runMacroPrivate(const WuQMacro* macro, mc, allowDelayAfterCommandFlag); + if (m_runOptions.isStopAfterSelectedCommand()) { + QGuiApplication::processEvents(); + if (mc == macroCommandToStopAfter) { + errorMessageOut = ("Macro stopped after " + + mc->getDescriptiveName()); + return false; + } + } + const bool stopFlag = executorMonitor->testForStop(); if (stopFlag) { errorMessageOut = executorMonitor->getStoppedByUserMessage(); diff --git a/src/GuiQt/WuQMacroExecutor.h b/src/GuiQt/WuQMacroExecutor.h index 72ffcc836..ca2c43854 100644 --- a/src/GuiQt/WuQMacroExecutor.h +++ b/src/GuiQt/WuQMacroExecutor.h @@ -52,6 +52,7 @@ namespace caret { WuQMacroExecutor& operator=(const WuQMacroExecutor&) = delete; bool runMacro(const WuQMacro* macro, + const WuQMacroCommand* macroCommandToStopAfter, QWidget* window, std::vector& otherObjectParents, const WuQMacroExecutorMonitor* executorMonitor, @@ -92,6 +93,7 @@ namespace caret { void performCommandDelay(const WuQMacroCommand* mc) const; bool runMacroPrivate(const WuQMacro* macro, + const WuQMacroCommand* macroCommandToStopAfter, QWidget* window, std::vector& otherObjectParents, const WuQMacroExecutorMonitor* executorMonitor, diff --git a/src/GuiQt/WuQMacroExecutorOptions.cxx b/src/GuiQt/WuQMacroExecutorOptions.cxx index 1cc233f32..6b8ad09c4 100644 --- a/src/GuiQt/WuQMacroExecutorOptions.cxx +++ b/src/GuiQt/WuQMacroExecutorOptions.cxx @@ -86,10 +86,11 @@ WuQMacroExecutorOptions::operator=(const WuQMacroExecutorOptions& obj) void WuQMacroExecutorOptions::copyHelperWuQMacroExecutorOptions(const WuQMacroExecutorOptions& obj) { - m_loopingFlag = obj.m_loopingFlag; - m_stopOnErrorFlag = obj.m_stopOnErrorFlag; - m_showMouseMovementFlag = obj.m_showMouseMovementFlag; + m_loopingFlag = obj.m_loopingFlag; + m_stopOnErrorFlag = obj.m_stopOnErrorFlag; + m_showMouseMovementFlag = obj.m_showMouseMovementFlag; m_recordMovieDuringExecutionFlag = obj.m_recordMovieDuringExecutionFlag; + m_stopAfterSelectedCommandFlag = obj.m_stopAfterSelectedCommandFlag; } /** @@ -185,3 +186,25 @@ WuQMacroExecutorOptions::setRecordMovieDuringExecution(const bool status) { m_recordMovieDuringExecutionFlag = status; } + +/** + * @return True if execution should stop after the command selected in macro dialog + */ +bool +WuQMacroExecutorOptions::isStopAfterSelectedCommand() const +{ + return m_stopAfterSelectedCommandFlag; +} + +/** + * Set execution should stop after selected command in macro dialog + * + * @param status + * New status + */ +void +WuQMacroExecutorOptions::setStopAfterSelectedCommand(const bool status) +{ + m_stopAfterSelectedCommandFlag = status; +} + diff --git a/src/GuiQt/WuQMacroExecutorOptions.h b/src/GuiQt/WuQMacroExecutorOptions.h index 18d105759..545fd2802 100644 --- a/src/GuiQt/WuQMacroExecutorOptions.h +++ b/src/GuiQt/WuQMacroExecutorOptions.h @@ -58,6 +58,10 @@ namespace caret { void setRecordMovieDuringExecution(const bool status); + bool isStopAfterSelectedCommand() const; + + void setStopAfterSelectedCommand(const bool status); + // ADD_NEW_METHODS_HERE virtual AString toString() const; @@ -73,6 +77,8 @@ namespace caret { bool m_recordMovieDuringExecutionFlag = false; + bool m_stopAfterSelectedCommandFlag = false; + // ADD_NEW_MEMBERS_HERE }; diff --git a/src/GuiQt/WuQMacroManager.cxx b/src/GuiQt/WuQMacroManager.cxx index 3bd0a234a..d0827de21 100644 --- a/src/GuiQt/WuQMacroManager.cxx +++ b/src/GuiQt/WuQMacroManager.cxx @@ -542,10 +542,13 @@ WuQMacroManager::updateNonModalDialogs() * Widget used for parent of dialogs * @param macro * Macro that is run + * @param macroCommandToStopAfter + * Macro command that the executor may stop after, depending upon options */ void WuQMacroManager::runMacro(QWidget* widget, - const WuQMacro* macro) + const WuQMacro* macro, + const WuQMacroCommand* macroCommandToStopAfter) { CaretAssert(widget); CaretAssert(macro); @@ -566,6 +569,7 @@ WuQMacroManager::runMacro(QWidget* widget, m_executorOptions.get()); } const bool resultFlag = m_macroExecutor->runMacro(macro, + macroCommandToStopAfter, widget, m_parentObjects, m_macroExecutorMonitor, @@ -1106,7 +1110,8 @@ WuQMacroManager::runMacroWithShortCutKeyEvent(QWidget* window, const WuQMacro* macro = getMacroWithShortCutKey(shortCutKey); if (macro != NULL) { runMacro(window, - macro); + macro, + NULL); return true; } } diff --git a/src/GuiQt/WuQMacroManager.h b/src/GuiQt/WuQMacroManager.h index 99f4edbda..6e258931e 100644 --- a/src/GuiQt/WuQMacroManager.h +++ b/src/GuiQt/WuQMacroManager.h @@ -123,7 +123,8 @@ namespace caret { WuQMacro* macro); void runMacro(QWidget* window, - const WuQMacro* macro); + const WuQMacro* macro, + const WuQMacroCommand* macroCommandToStopAfter); void stopMacro(); -- GitLab From 442998631d581d9a1cdcb8fd2861927749a97300 Mon Sep 17 00:00:00 2001 From: John Harwell Date: Wed, 13 Mar 2019 14:44:35 -0500 Subject: [PATCH 233/427] Movie Recording: Eliminate the default file name so that the user is forced to choose a file name when creating a movie. --- src/Brain/MovieRecorder.cxx | 6 +++--- src/GuiQt/MovieRecordingDialog.cxx | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Brain/MovieRecorder.cxx b/src/Brain/MovieRecorder.cxx index f6e5e2e39..0c99f1212 100644 --- a/src/Brain/MovieRecorder.cxx +++ b/src/Brain/MovieRecorder.cxx @@ -327,9 +327,9 @@ MovieRecorder::setCaptureRegionType(const MovieRecorderCaptureRegionTypeEnum::En AString MovieRecorder::getMovieFileName() const { - if (m_movieFileName.isEmpty()) { - initializeMovieFileName(); - } +// if (m_movieFileName.isEmpty()) { +// initializeMovieFileName(); +// } return m_movieFileName; } diff --git a/src/GuiQt/MovieRecordingDialog.cxx b/src/GuiQt/MovieRecordingDialog.cxx index 7a6b2ebf1..176a5c935 100644 --- a/src/GuiQt/MovieRecordingDialog.cxx +++ b/src/GuiQt/MovieRecordingDialog.cxx @@ -413,7 +413,7 @@ MovieRecordingDialog::createMovie(QWidget* parent) { MovieRecorder* movieRecorder = SessionManager::get()->getMovieRecorder(); QString currentFileName = movieRecorder->getMovieFileName(); - MovieRecorderVideoFormatTypeEnum::Enum formatType = MovieRecorderVideoFormatTypeEnum::MPEG_4; + MovieRecorderVideoFormatTypeEnum::Enum formatType = MovieRecorderVideoFormatTypeEnum::MPEG; QString filters; QString selectedFilter = MovieRecorderVideoFormatTypeEnum::toFileDialogFilter(formatType); -- GitLab From 72fc0c3c9f83e9ae480d5f85eb3306889282522e Mon Sep 17 00:00:00 2001 From: John Harwell Date: Wed, 13 Mar 2019 16:25:02 -0500 Subject: [PATCH 234/427] Macros: Add option on macros dialog to ignore all delays/durations during macro execution. Also, a right-click over a macro command can be used to stop after that command while ignoring delays/durations. --- src/GuiQt/WbMacroCustomOperationBase.cxx | 9 ++- src/GuiQt/WbMacroCustomOperationBase.h | 15 +++-- src/GuiQt/WbMacroCustomOperationDelay.cxx | 3 + src/GuiQt/WbMacroCustomOperationDelay.h | 1 + src/GuiQt/WbMacroCustomOperationManager.cxx | 4 ++ src/GuiQt/WbMacroCustomOperationManager.h | 3 +- .../WbMacroCustomOperationModelRotation.cxx | 10 +++- .../WbMacroCustomOperationModelRotation.h | 2 + ...WbMacroCustomOperationOverlayCrossFade.cxx | 29 ++++++++-- .../WbMacroCustomOperationOverlayCrossFade.h | 7 ++- ...croCustomOperationSurfaceInterpolation.cxx | 10 +++- ...MacroCustomOperationSurfaceInterpolation.h | 2 + ...croCustomOperationVolumeSliceIncrement.cxx | 10 +++- ...MacroCustomOperationVolumeSliceIncrement.h | 2 + ...ustomOperationVolumeToSurfaceCrossFade.cxx | 10 +++- ...oCustomOperationVolumeToSurfaceCrossFade.h | 2 + src/GuiQt/WbMacroHelper.cxx | 16 +++++- src/GuiQt/WbMacroHelper.h | 2 + .../WuQMacroCustomOperationManagerInterface.h | 6 ++ src/GuiQt/WuQMacroDialog.cxx | 56 ++++++++++++++++++- src/GuiQt/WuQMacroDialog.h | 6 ++ src/GuiQt/WuQMacroExecutor.cxx | 9 +++ src/GuiQt/WuQMacroExecutor.h | 2 + src/GuiQt/WuQMacroExecutorOptions.cxx | 23 ++++++++ src/GuiQt/WuQMacroExecutorOptions.h | 6 ++ src/GuiQt/WuQMacroHelperInterface.h | 6 ++ src/GuiQt/WuQMacroManager.cxx | 12 +++- src/GuiQt/WuQMacroManager.h | 3 + 28 files changed, 245 insertions(+), 21 deletions(-) diff --git a/src/GuiQt/WbMacroCustomOperationBase.cxx b/src/GuiQt/WbMacroCustomOperationBase.cxx index 4e72b8c74..b52235509 100644 --- a/src/GuiQt/WbMacroCustomOperationBase.cxx +++ b/src/GuiQt/WbMacroCustomOperationBase.cxx @@ -36,6 +36,7 @@ #include "Surface.h" #include "SystemUtilities.h" #include "WuQMacroCommand.h" +#include "WuQMacroExecutorOptions.h" using namespace caret; @@ -240,7 +241,8 @@ WbMacroCustomOperationBase::updateSurfaceColoring() * when a movie is not being recorded */ void -WbMacroCustomOperationBase::getNumberOfStepsAndSleepTime(const float defaultNumberOfSteps, +WbMacroCustomOperationBase::getNumberOfStepsAndSleepTime(const WuQMacroExecutorOptions* executorOptions, + const float defaultNumberOfSteps, const float durationSeconds, float& numberOfStepsOut, float& sleepTimeOut) @@ -258,6 +260,11 @@ WbMacroCustomOperationBase::getNumberOfStepsAndSleepTime(const float defaultNumb * movieRecorder->getFramesRate()); break; } + + if (executorOptions->isIgnoreDelaysAndDurations()) { + sleepTimeOut = 0.0; + numberOfStepsOut = 2; + } } /** diff --git a/src/GuiQt/WbMacroCustomOperationBase.h b/src/GuiQt/WbMacroCustomOperationBase.h index d20a712fd..89f910cc6 100644 --- a/src/GuiQt/WbMacroCustomOperationBase.h +++ b/src/GuiQt/WbMacroCustomOperationBase.h @@ -34,6 +34,7 @@ class QWidget; namespace caret { class Surface; class WuQMacroExecutorMonitor; + class WuQMacroExecutorOptions; class WuQMacroCommand; class WbMacroCustomOperationBase : public CaretObject { @@ -53,6 +54,8 @@ namespace caret { * Parent widget for any dialogs * @param executorMonitor * the macro executor monitor + * @param executorOptions, + * Options for the executor * @param macroCommand * macro command to run * @return @@ -61,6 +64,7 @@ namespace caret { */ virtual bool executeCommand(QWidget* parent, const WuQMacroExecutorMonitor* executorMonitor, + const WuQMacroExecutorOptions* executorOptions, const WuQMacroCommand* macroCommand) = 0; /** @@ -74,11 +78,6 @@ namespace caret { QString getErrorMessage() const; - void getNumberOfStepsAndSleepTime(const float defaultNumberOfSteps, - const float durationSeconds, - float& numberOfStepsOut, - float& sleepTimeOut); - void sleepForSecondsAtEndOfIteration(const float seconds); virtual QString getOperationName() const; @@ -86,6 +85,12 @@ namespace caret { protected: WbMacroCustomOperationBase(const WbMacroCustomOperationTypeEnum::Enum operationType); + void getNumberOfStepsAndSleepTime(const WuQMacroExecutorOptions* executorOptions, + const float defaultNumberOfSteps, + const float durationSeconds, + float& numberOfStepsOut, + float& sleepTimeOut); + bool validateCorrectNumberOfParameters(const WuQMacroCommand* command, const int32_t correctNumberOfParameters); diff --git a/src/GuiQt/WbMacroCustomOperationDelay.cxx b/src/GuiQt/WbMacroCustomOperationDelay.cxx index fde26ca7b..d4b65788e 100644 --- a/src/GuiQt/WbMacroCustomOperationDelay.cxx +++ b/src/GuiQt/WbMacroCustomOperationDelay.cxx @@ -87,6 +87,8 @@ WbMacroCustomOperationDelay::createCommand() * Parent widget for any dialogs * @param executorMonitor * the macro executor monitor + * @param executorOptions + * Options for executor * @param macroCommand * macro command to run * @return @@ -96,6 +98,7 @@ WbMacroCustomOperationDelay::createCommand() bool WbMacroCustomOperationDelay::executeCommand(QWidget* /*parent*/, const WuQMacroExecutorMonitor* /*executorMonitor*/, + const WuQMacroExecutorOptions* /*executorOptions*/, const WuQMacroCommand* /*macroCommand*/) { /* diff --git a/src/GuiQt/WbMacroCustomOperationDelay.h b/src/GuiQt/WbMacroCustomOperationDelay.h index cbb35086c..0f0848a99 100644 --- a/src/GuiQt/WbMacroCustomOperationDelay.h +++ b/src/GuiQt/WbMacroCustomOperationDelay.h @@ -42,6 +42,7 @@ namespace caret { virtual bool executeCommand(QWidget* parent, const WuQMacroExecutorMonitor* executorMonitor, + const WuQMacroExecutorOptions* executorOptions, const WuQMacroCommand* macroCommand) override; virtual WuQMacroCommand* createCommand() override; diff --git a/src/GuiQt/WbMacroCustomOperationManager.cxx b/src/GuiQt/WbMacroCustomOperationManager.cxx index fb2ab6493..183c0ee23 100644 --- a/src/GuiQt/WbMacroCustomOperationManager.cxx +++ b/src/GuiQt/WbMacroCustomOperationManager.cxx @@ -509,6 +509,8 @@ WbMacroCustomOperationManager::editCustomDataValueParameter(QWidget* parentWidge * Parent widget for any dialogs * @param executorMonitor * The executor monitor + * @param executorOptions + * Options for the executor * @param customMacroCommand * Custom macro command to run * @param errorMessageOut @@ -519,6 +521,7 @@ WbMacroCustomOperationManager::editCustomDataValueParameter(QWidget* parentWidge bool WbMacroCustomOperationManager::executeCustomOperationMacroCommand(QWidget* parent, const WuQMacroExecutorMonitor* executorMonitor, + const WuQMacroExecutorOptions* executorOptions, const WuQMacroCommand* customMacroCommand, QString& errorMessageOut) { @@ -549,6 +552,7 @@ WbMacroCustomOperationManager::executeCustomOperationMacroCommand(QWidget* paren if (customOperation) { successFlag = customOperation->executeCommand(parent, executorMonitor, + executorOptions, customMacroCommand); if ( ! successFlag) { errorMessageOut = customOperation->getErrorMessage(); diff --git a/src/GuiQt/WbMacroCustomOperationManager.h b/src/GuiQt/WbMacroCustomOperationManager.h index 9e5a38ebc..4421a28ed 100644 --- a/src/GuiQt/WbMacroCustomOperationManager.h +++ b/src/GuiQt/WbMacroCustomOperationManager.h @@ -55,8 +55,9 @@ namespace caret { virtual bool executeCustomOperationMacroCommand(QWidget* parent, const WuQMacroExecutorMonitor* executorMonitor, + const WuQMacroExecutorOptions* executorOptions, const WuQMacroCommand* macroCommand, - QString& errorMessageOut); + QString& errorMessageOut) override; virtual std::vector getNamesOfCustomOperationMacroCommands() override; diff --git a/src/GuiQt/WbMacroCustomOperationModelRotation.cxx b/src/GuiQt/WbMacroCustomOperationModelRotation.cxx index 35bca2090..f201601bf 100644 --- a/src/GuiQt/WbMacroCustomOperationModelRotation.cxx +++ b/src/GuiQt/WbMacroCustomOperationModelRotation.cxx @@ -109,6 +109,8 @@ WbMacroCustomOperationModelRotation::createCommand() * Parent widget for any dialogs * @param executorMonitor * the macro executor monitor + * @param executorOptions + * Options for executor * @param macroCommand * macro command to run * @return @@ -118,6 +120,7 @@ WbMacroCustomOperationModelRotation::createCommand() bool WbMacroCustomOperationModelRotation::executeCommand(QWidget* parent, const WuQMacroExecutorMonitor* executorMonitor, + const WuQMacroExecutorOptions* executorOptions, const WuQMacroCommand* macroCommand) { CaretAssert(parent); @@ -173,6 +176,7 @@ WbMacroCustomOperationModelRotation::executeCommand(QWidget* parent, } const bool successFlag = performRotation(executorMonitor, + executorOptions, tabContent, axis, totalRotation, @@ -185,6 +189,8 @@ WbMacroCustomOperationModelRotation::executeCommand(QWidget* parent, * * @param executorMonitor * the macro executor monitor + * @param executorOptions + * executor options * @param tabContent * Content of the tab * @param axis @@ -197,6 +203,7 @@ WbMacroCustomOperationModelRotation::executeCommand(QWidget* parent, */ bool WbMacroCustomOperationModelRotation::performRotation(const WuQMacroExecutorMonitor* executorMonitor, + const WuQMacroExecutorOptions* executorOptions, BrowserTabContent* tabContent, const Axis axis, const float totalRotation, @@ -205,7 +212,8 @@ WbMacroCustomOperationModelRotation::performRotation(const WuQMacroExecutorMonit const float defaultNumberOfSteps(60.0); float numberOfSteps(0.0); float iterationSleepTime(0.0); - getNumberOfStepsAndSleepTime(defaultNumberOfSteps, + getNumberOfStepsAndSleepTime(executorOptions, + defaultNumberOfSteps, durationSeconds, numberOfSteps, iterationSleepTime); diff --git a/src/GuiQt/WbMacroCustomOperationModelRotation.h b/src/GuiQt/WbMacroCustomOperationModelRotation.h index 6e55c0b2f..173b412c6 100644 --- a/src/GuiQt/WbMacroCustomOperationModelRotation.h +++ b/src/GuiQt/WbMacroCustomOperationModelRotation.h @@ -46,6 +46,7 @@ namespace caret { virtual bool executeCommand(QWidget* parent, const WuQMacroExecutorMonitor* executorMonitor, + const WuQMacroExecutorOptions* executorOptions, const WuQMacroCommand* macroCommand) override; virtual WuQMacroCommand* createCommand() override; @@ -61,6 +62,7 @@ namespace caret { }; bool performRotation(const WuQMacroExecutorMonitor* executorMonitor, + const WuQMacroExecutorOptions* executorOptions, BrowserTabContent* tabContent, const Axis axis, const float totalRotation, diff --git a/src/GuiQt/WbMacroCustomOperationOverlayCrossFade.cxx b/src/GuiQt/WbMacroCustomOperationOverlayCrossFade.cxx index 95daa1cba..0ca791e4e 100644 --- a/src/GuiQt/WbMacroCustomOperationOverlayCrossFade.cxx +++ b/src/GuiQt/WbMacroCustomOperationOverlayCrossFade.cxx @@ -198,6 +198,8 @@ WbMacroCustomOperationOverlayCrossFade::createCommandVersionTwo() * Parent widget for any dialogs * @param executorMonitor * the macro executor monitor + * @param executorOptions + * the executor options * @param macroCommand * macro command to run * @return @@ -207,6 +209,7 @@ WbMacroCustomOperationOverlayCrossFade::createCommandVersionTwo() bool WbMacroCustomOperationOverlayCrossFade::executeCommand(QWidget* parent, const WuQMacroExecutorMonitor* executorMonitor, + const WuQMacroExecutorOptions* executorOptions, const WuQMacroCommand* macroCommand) { CaretAssert(parent); @@ -216,11 +219,13 @@ WbMacroCustomOperationOverlayCrossFade::executeCommand(QWidget* parent, switch (macroCommand->getVersion()) { case 1: resultFlag = executeCommandVersionOne(parent, + executorOptions, macroCommand); break; case 2: resultFlag = executeCommandVersionTwo(parent, executorMonitor, + executorOptions, macroCommand); break; default: @@ -238,6 +243,8 @@ WbMacroCustomOperationOverlayCrossFade::executeCommand(QWidget* parent, * Parent widget for any dialogs * @param executorMonitor * The macro executor's monitor + * @param executorOptions + * The executor options, * @param macroCommand * macro command to run * @return @@ -247,6 +254,7 @@ WbMacroCustomOperationOverlayCrossFade::executeCommand(QWidget* parent, bool WbMacroCustomOperationOverlayCrossFade::executeCommandVersionTwo(QWidget* parent, const WuQMacroExecutorMonitor* executorMonitor, + const WuQMacroExecutorOptions* executorOptions, const WuQMacroCommand* macroCommand) { CaretAssert(parent); @@ -359,6 +367,7 @@ WbMacroCustomOperationOverlayCrossFade::executeCommandVersionTwo(QWidget* parent } const bool successFlag = performCrossFadeVersionTwo(executorMonitor, + executorOptions, overlaySet, overlayIndex, fadeToMapFile, @@ -373,6 +382,8 @@ WbMacroCustomOperationOverlayCrossFade::executeCommandVersionTwo(QWidget* parent * * @param executorMonitor * The macro executor's monitor + * @param executorOptions + * The executor options * @param overlaySet * OverlaySet for the in the tab * @param overlayIndex @@ -388,6 +399,7 @@ WbMacroCustomOperationOverlayCrossFade::executeCommandVersionTwo(QWidget* parent */ bool WbMacroCustomOperationOverlayCrossFade::performCrossFadeVersionTwo(const WuQMacroExecutorMonitor* executorMonitor, + const WuQMacroExecutorOptions* executorOptions, OverlaySet* overlaySet, const int32_t overlayIndex, CaretMappableDataFile* fadeToMapFile, @@ -421,7 +433,8 @@ WbMacroCustomOperationOverlayCrossFade::performCrossFadeVersionTwo(const WuQMacr const float defaultNumberOfSteps(25.0); float numberOfSteps(0.0); float iterationSleepTime(0.0); - getNumberOfStepsAndSleepTime(defaultNumberOfSteps, + getNumberOfStepsAndSleepTime(executorOptions, + defaultNumberOfSteps, durationSeconds, numberOfSteps, iterationSleepTime); @@ -476,6 +489,8 @@ WbMacroCustomOperationOverlayCrossFade::performCrossFadeVersionTwo(const WuQMacr * * @param parent * Parent widget for any dialogs + * @param executorOptions + * The executor options * @param macroCommand * macro command to run * @return @@ -484,6 +499,7 @@ WbMacroCustomOperationOverlayCrossFade::performCrossFadeVersionTwo(const WuQMacr */ bool WbMacroCustomOperationOverlayCrossFade::executeCommandVersionOne(QWidget* parent, + const WuQMacroExecutorOptions* executorOptions, const WuQMacroCommand* macroCommand) { CaretAssert(parent); @@ -581,7 +597,8 @@ WbMacroCustomOperationOverlayCrossFade::executeCommandVersionOne(QWidget* parent return false; } - bool successFlag = performCrossFadeVersionOne(fadeToOverlay, + bool successFlag = performCrossFadeVersionOne(executorOptions, + fadeToOverlay, fadeFromOverlay, durationSeconds); @@ -591,6 +608,8 @@ WbMacroCustomOperationOverlayCrossFade::executeCommandVersionOne(QWidget* parent /** * Perform crossfade version one * + * @param executorOptions + * The executor options * @param fadeToOverlay * Overlay that starts with opacity zero and increases to one * @param fadeFromOverlay @@ -601,7 +620,8 @@ WbMacroCustomOperationOverlayCrossFade::executeCommandVersionOne(QWidget* parent * True if successful, else false */ bool -WbMacroCustomOperationOverlayCrossFade::performCrossFadeVersionOne(Overlay* fadeToOverlay, +WbMacroCustomOperationOverlayCrossFade::performCrossFadeVersionOne(const WuQMacroExecutorOptions* executorOptions, + Overlay* fadeToOverlay, Overlay* fadeFromOverlay, const float durationSeconds) { @@ -611,7 +631,8 @@ WbMacroCustomOperationOverlayCrossFade::performCrossFadeVersionOne(Overlay* fade const float defaultNumberOfSteps(25.0); float numberOfSteps(0.0); float iterationSleepTime(0.0); - getNumberOfStepsAndSleepTime(defaultNumberOfSteps, + getNumberOfStepsAndSleepTime(executorOptions, + defaultNumberOfSteps, durationSeconds, numberOfSteps, iterationSleepTime); diff --git a/src/GuiQt/WbMacroCustomOperationOverlayCrossFade.h b/src/GuiQt/WbMacroCustomOperationOverlayCrossFade.h index 74395b1e2..9b8880bf7 100644 --- a/src/GuiQt/WbMacroCustomOperationOverlayCrossFade.h +++ b/src/GuiQt/WbMacroCustomOperationOverlayCrossFade.h @@ -45,6 +45,7 @@ namespace caret { virtual bool executeCommand(QWidget* parent, const WuQMacroExecutorMonitor* executorMonitor, + const WuQMacroExecutorOptions* executorOptions, const WuQMacroCommand* macroCommand) override; virtual WuQMacroCommand* createCommand() override; @@ -60,9 +61,11 @@ namespace caret { virtual bool executeCommandVersionTwo(QWidget* parent, const WuQMacroExecutorMonitor* executorMonitor, + const WuQMacroExecutorOptions* executorOptions, const WuQMacroCommand* macroCommand); bool performCrossFadeVersionTwo(const WuQMacroExecutorMonitor* executorMonitor, + const WuQMacroExecutorOptions* executorOptions, OverlaySet* overlaySet, const int32_t overlayIndex, CaretMappableDataFile* fadeToMapFile, @@ -70,9 +73,11 @@ namespace caret { const float durationSeconds); virtual bool executeCommandVersionOne(QWidget* parent, + const WuQMacroExecutorOptions* executorOptions, const WuQMacroCommand* macroCommand); - bool performCrossFadeVersionOne(Overlay* fadeToOverlay, + bool performCrossFadeVersionOne(const WuQMacroExecutorOptions* executorOptions, + Overlay* fadeToOverlay, Overlay* fadeFromOverlay, const float durationSeconds); diff --git a/src/GuiQt/WbMacroCustomOperationSurfaceInterpolation.cxx b/src/GuiQt/WbMacroCustomOperationSurfaceInterpolation.cxx index cdff5869d..746eb649b 100644 --- a/src/GuiQt/WbMacroCustomOperationSurfaceInterpolation.cxx +++ b/src/GuiQt/WbMacroCustomOperationSurfaceInterpolation.cxx @@ -118,6 +118,8 @@ WbMacroCustomOperationSurfaceInterpolation::createCommand() * Parent widget for any dialogs * @param executorMonitor * the macro executor monitor + * @param executorOptions + * Options for executor * @param macroCommand * macro command to run * @return @@ -127,6 +129,7 @@ WbMacroCustomOperationSurfaceInterpolation::createCommand() bool WbMacroCustomOperationSurfaceInterpolation::executeCommand(QWidget* parent, const WuQMacroExecutorMonitor* executorMonitor, + const WuQMacroExecutorOptions* executorOptions, const WuQMacroCommand* macroCommand) { CaretAssert(parent); @@ -192,6 +195,7 @@ WbMacroCustomOperationSurfaceInterpolation::executeCommand(QWidget* parent, } bool successFlag = interpolateSurface(executorMonitor, + executorOptions, tabContent->getTabNumber(), wholeBrainModel, startSurface, @@ -206,6 +210,8 @@ WbMacroCustomOperationSurfaceInterpolation::executeCommand(QWidget* parent, * * @param executorMonitor * The macro executor's monitor + * @param executorOptions + * The executor options * @param tabIndex * Index of selected tab * @param wholeBrainModel @@ -221,6 +227,7 @@ WbMacroCustomOperationSurfaceInterpolation::executeCommand(QWidget* parent, */ bool WbMacroCustomOperationSurfaceInterpolation::interpolateSurface(const WuQMacroExecutorMonitor* executorMonitor, + const WuQMacroExecutorOptions* executorOptions, const int32_t tabIndex, ModelWholeBrain* wholeBrainModel, const Surface* startSurface, @@ -234,7 +241,8 @@ WbMacroCustomOperationSurfaceInterpolation::interpolateSurface(const WuQMacroExe const float defaultNumberOfSteps(50.0); float numberOfSteps(0.0); float iterationSleepTime(0.0); - getNumberOfStepsAndSleepTime(defaultNumberOfSteps, + getNumberOfStepsAndSleepTime(executorOptions, + defaultNumberOfSteps, durationSeconds, numberOfSteps, iterationSleepTime); diff --git a/src/GuiQt/WbMacroCustomOperationSurfaceInterpolation.h b/src/GuiQt/WbMacroCustomOperationSurfaceInterpolation.h index 9a7461b19..2c59d9cbd 100644 --- a/src/GuiQt/WbMacroCustomOperationSurfaceInterpolation.h +++ b/src/GuiQt/WbMacroCustomOperationSurfaceInterpolation.h @@ -47,6 +47,7 @@ namespace caret { virtual bool executeCommand(QWidget* parent, const WuQMacroExecutorMonitor* executorMonitor, + const WuQMacroExecutorOptions* executorOptions, const WuQMacroCommand* macroCommand) override; virtual WuQMacroCommand* createCommand() override; @@ -57,6 +58,7 @@ namespace caret { private: bool interpolateSurface(const WuQMacroExecutorMonitor* executorMonitor, + const WuQMacroExecutorOptions* executorOptions, const int32_t tabIndex, ModelWholeBrain* wholeBrainModel, const Surface* startSurface, diff --git a/src/GuiQt/WbMacroCustomOperationVolumeSliceIncrement.cxx b/src/GuiQt/WbMacroCustomOperationVolumeSliceIncrement.cxx index 90ca97d6d..d9814546c 100644 --- a/src/GuiQt/WbMacroCustomOperationVolumeSliceIncrement.cxx +++ b/src/GuiQt/WbMacroCustomOperationVolumeSliceIncrement.cxx @@ -116,6 +116,8 @@ WbMacroCustomOperationVolumeSliceIncrement::createCommand() * Parent widget for any dialogs * @param executorMonitor * the macro executor monitor + * @param executorOptions + * Options for executor * @param macroCommand * macro command to run * @return @@ -125,6 +127,7 @@ WbMacroCustomOperationVolumeSliceIncrement::createCommand() bool WbMacroCustomOperationVolumeSliceIncrement::executeCommand(QWidget* parent, const WuQMacroExecutorMonitor* executorMonitor, + const WuQMacroExecutorOptions* executorOptions, const WuQMacroCommand* macroCommand) { CaretAssert(parent); @@ -179,6 +182,7 @@ WbMacroCustomOperationVolumeSliceIncrement::executeCommand(QWidget* parent, } const bool successFlag = performSliceIncrement(executorMonitor, + executorOptions, tabContent, axis, durationSeconds); @@ -188,6 +192,8 @@ WbMacroCustomOperationVolumeSliceIncrement::executeCommand(QWidget* parent, /** * @param executorMonitor * The macro executor's monitor + * @param executorOptions + * The executor options * @param tabContent * Content in the selected tab * @param axis @@ -197,6 +203,7 @@ WbMacroCustomOperationVolumeSliceIncrement::executeCommand(QWidget* parent, */ bool WbMacroCustomOperationVolumeSliceIncrement::performSliceIncrement(const WuQMacroExecutorMonitor* executorMonitor, + const WuQMacroExecutorOptions* executorOptions, BrowserTabContent* tabContent, const Axis axis, const float durationSeconds) @@ -243,7 +250,8 @@ WbMacroCustomOperationVolumeSliceIncrement::performSliceIncrement(const WuQMacro const float defaultNumberOfSteps(numberOfSlices); float numberOfSteps(0.0); float iterationSleepTime(0.0); - getNumberOfStepsAndSleepTime(defaultNumberOfSteps, + getNumberOfStepsAndSleepTime(executorOptions, + defaultNumberOfSteps, durationSeconds, numberOfSteps, iterationSleepTime); diff --git a/src/GuiQt/WbMacroCustomOperationVolumeSliceIncrement.h b/src/GuiQt/WbMacroCustomOperationVolumeSliceIncrement.h index 7aef43695..75aa0f082 100644 --- a/src/GuiQt/WbMacroCustomOperationVolumeSliceIncrement.h +++ b/src/GuiQt/WbMacroCustomOperationVolumeSliceIncrement.h @@ -46,6 +46,7 @@ namespace caret { virtual bool executeCommand(QWidget* parent, const WuQMacroExecutorMonitor* executorMonitor, + const WuQMacroExecutorOptions* executorOptions, const WuQMacroCommand* macroCommand) override; virtual WuQMacroCommand* createCommand() override; @@ -61,6 +62,7 @@ namespace caret { }; bool performSliceIncrement(const WuQMacroExecutorMonitor* executorMonitor, + const WuQMacroExecutorOptions* executorOptions, BrowserTabContent* tabContent, const Axis axis, const float durationSeconds); diff --git a/src/GuiQt/WbMacroCustomOperationVolumeToSurfaceCrossFade.cxx b/src/GuiQt/WbMacroCustomOperationVolumeToSurfaceCrossFade.cxx index c27e44164..f08090344 100644 --- a/src/GuiQt/WbMacroCustomOperationVolumeToSurfaceCrossFade.cxx +++ b/src/GuiQt/WbMacroCustomOperationVolumeToSurfaceCrossFade.cxx @@ -113,6 +113,8 @@ WbMacroCustomOperationVolumeToSurfaceCrossFade::createCommand() * Parent widget for any dialogs * @param executorMonitor * the macro executor monitor + * @param executorOptions + * Options for executor * @param macroCommand * macro command to run * @return @@ -122,6 +124,7 @@ WbMacroCustomOperationVolumeToSurfaceCrossFade::createCommand() bool WbMacroCustomOperationVolumeToSurfaceCrossFade::executeCommand(QWidget* parent, const WuQMacroExecutorMonitor* executorMonitor, + const WuQMacroExecutorOptions* executorOptions, const WuQMacroCommand* macroCommand) { CaretAssert(parent); @@ -164,6 +167,7 @@ WbMacroCustomOperationVolumeToSurfaceCrossFade::executeCommand(QWidget* parent, bool successFlag = performCrossFade(executorMonitor, + executorOptions, volumeOverlay, durationSeconds); @@ -175,6 +179,8 @@ WbMacroCustomOperationVolumeToSurfaceCrossFade::executeCommand(QWidget* parent, * * @param executorMonitor * The macro executor's monitor + * @param executorOptions + * The executor options * @param volumeOverlay * Overlay that contains the volume * @param durationSeconds @@ -184,6 +190,7 @@ WbMacroCustomOperationVolumeToSurfaceCrossFade::executeCommand(QWidget* parent, */ bool WbMacroCustomOperationVolumeToSurfaceCrossFade::performCrossFade(const WuQMacroExecutorMonitor* executorMonitor, + const WuQMacroExecutorOptions* executorOptions, Overlay* volumeOverlay, const float durationSeconds) { @@ -192,7 +199,8 @@ WbMacroCustomOperationVolumeToSurfaceCrossFade::performCrossFade(const WuQMacroE const float defaultNumberOfSteps(25.0); float numberOfSteps(0.0); float iterationSleepTime(0.0); - getNumberOfStepsAndSleepTime(defaultNumberOfSteps, + getNumberOfStepsAndSleepTime(executorOptions, + defaultNumberOfSteps, durationSeconds, numberOfSteps, iterationSleepTime); diff --git a/src/GuiQt/WbMacroCustomOperationVolumeToSurfaceCrossFade.h b/src/GuiQt/WbMacroCustomOperationVolumeToSurfaceCrossFade.h index fe8b8118b..be19c991a 100644 --- a/src/GuiQt/WbMacroCustomOperationVolumeToSurfaceCrossFade.h +++ b/src/GuiQt/WbMacroCustomOperationVolumeToSurfaceCrossFade.h @@ -44,6 +44,7 @@ namespace caret { virtual bool executeCommand(QWidget* parent, const WuQMacroExecutorMonitor* executorMonitor, + const WuQMacroExecutorOptions* executorOptions, const WuQMacroCommand* macroCommand) override; virtual WuQMacroCommand* createCommand() override; @@ -54,6 +55,7 @@ namespace caret { private: bool performCrossFade(const WuQMacroExecutorMonitor* executorMonitor, + const WuQMacroExecutorOptions* executorOptions, Overlay* volumeOverlay, const float durationSeconds); diff --git a/src/GuiQt/WbMacroHelper.cxx b/src/GuiQt/WbMacroHelper.cxx index 575b83a58..87e0e6f8b 100644 --- a/src/GuiQt/WbMacroHelper.cxx +++ b/src/GuiQt/WbMacroHelper.cxx @@ -282,9 +282,13 @@ WbMacroHelper::macroExecutionEnding(const WuQMacro* /*macro*/, void WbMacroHelper::macroCommandHasCompleted(QWidget* window, const WuQMacroCommand* command, + const WuQMacroExecutorOptions* executorOptions, bool& allowDelayFlagOut) { - const bool doDelayAfterCommandFlag(false); + bool doDelayAfterCommandFlag(false); // change value to enable/disable delays after command + if (executorOptions->isIgnoreDelaysAndDurations()) { + doDelayAfterCommandFlag = false; + } allowDelayFlagOut = doDelayAfterCommandFlag; if (doDelayAfterCommandFlag) { recordImagesForDelay(window, @@ -300,6 +304,8 @@ WbMacroHelper::macroCommandHasCompleted(QWidget* window, * Widget for parent * @param command * The command + * @param executorOptions + * Executor options * @param delay * The delay in seconds */ @@ -355,15 +361,21 @@ WbMacroHelper::recordImagesForDelay(QWidget* window, * Widget for parent * @param command * Command that is about to start + * @param executorOptions + * Executor options * @param allowDelayFlagOut * Output indicating if delay before command is enabled */ void WbMacroHelper::macroCommandAboutToStart(QWidget* window, const WuQMacroCommand* command, + const WuQMacroExecutorOptions* executorOptions, bool& allowDelayFlagOut) { - const bool doDelayBeforeCommandFlag(true); + bool doDelayBeforeCommandFlag(true); + if (executorOptions->isIgnoreDelaysAndDurations()) { + doDelayBeforeCommandFlag = false; + } allowDelayFlagOut = doDelayBeforeCommandFlag; if (doDelayBeforeCommandFlag) { recordImagesForDelay(window, diff --git a/src/GuiQt/WbMacroHelper.h b/src/GuiQt/WbMacroHelper.h index d81d20590..6adb773d2 100644 --- a/src/GuiQt/WbMacroHelper.h +++ b/src/GuiQt/WbMacroHelper.h @@ -68,10 +68,12 @@ namespace caret { virtual void macroCommandHasCompleted(QWidget* window, const WuQMacroCommand* command, + const WuQMacroExecutorOptions* executorOptions, bool& allowDelayFlagOut) override; virtual void macroCommandAboutToStart(QWidget* window, const WuQMacroCommand* command, + const WuQMacroExecutorOptions* executorOptions, bool& allowDelayFlagOut) override; // ADD_NEW_METHODS_HERE diff --git a/src/GuiQt/WuQMacroCustomOperationManagerInterface.h b/src/GuiQt/WuQMacroCustomOperationManagerInterface.h index febaea662..07a02e221 100644 --- a/src/GuiQt/WuQMacroCustomOperationManagerInterface.h +++ b/src/GuiQt/WuQMacroCustomOperationManagerInterface.h @@ -30,6 +30,7 @@ namespace caret { class WuQMacroCommand; class WuQMacroCommandParameter; class WuQMacroExecutorMonitor; + class WuQMacroExecutorOptions; class WuQMacroCustomOperationManagerInterface { @@ -63,6 +64,10 @@ namespace caret { * * @param parent * Parent widget for any dialogs + * @param executorMonitor + * The executor monitor + * @param executorOptions + * Options for the executor * @param customMacroCommand * Custom macro command to run * @param errorMessageOut @@ -72,6 +77,7 @@ namespace caret { */ virtual bool executeCustomOperationMacroCommand(QWidget* parent, const WuQMacroExecutorMonitor* executorMonitor, + const WuQMacroExecutorOptions* executorOptions, const WuQMacroCommand* macroCommand, QString& errorMessageOut) = 0; diff --git a/src/GuiQt/WuQMacroDialog.cxx b/src/GuiQt/WuQMacroDialog.cxx index 78d3cc489..cb77fee08 100644 --- a/src/GuiQt/WuQMacroDialog.cxx +++ b/src/GuiQt/WuQMacroDialog.cxx @@ -448,6 +448,13 @@ WuQMacroDialog::createRunOptionsWidget() QObject::connect(m_runOptionStopAfterSelectedCommandCheckBox, &QCheckBox::clicked, this, &WuQMacroDialog::runOptionStopAfterSelectedCommandCheckBoxClicked); + m_ignoreDelaysAndDurationsCheckBox = new QCheckBox("Ignore delays and durations"); + const QString ignoreToolTip("Ignore delays and durations and minimize iterations " + "to quickly execute macro (for debugging)"); + m_ignoreDelaysAndDurationsCheckBox->setToolTip(ignoreToolTip); + QObject::connect(m_ignoreDelaysAndDurationsCheckBox, &QCheckBox::clicked, + this, &WuQMacroDialog::runOptionIgnoreDelaysAndDurationsCheckBoxClicked); + QGridLayout* runOptionsLayout = new QGridLayout(widget); runOptionsLayout->setColumnStretch(0, 0); runOptionsLayout->setColumnStretch(1, 100); @@ -463,7 +470,9 @@ WuQMacroDialog::createRunOptionsWidget() row++; runOptionsLayout->addWidget(m_runOptionStopAfterSelectedCommandCheckBox, row, 0, 1, 2, Qt::AlignLeft); row++; - + runOptionsLayout->addWidget(m_ignoreDelaysAndDurationsCheckBox, row, 0, 1, 2, Qt::AlignLeft); + row++; + return widget; } @@ -523,6 +532,20 @@ WuQMacroDialog::runOptionStopAfterSelectedCommandCheckBoxClicked(bool checked) options->setStopAfterSelectedCommand(checked); } +/** + * Called when run options ignore delays and durations checkbox is changed + * + * @param checked + * New checked status. + */ +void +WuQMacroDialog::runOptionIgnoreDelaysAndDurationsCheckBoxClicked(bool checked) +{ + WuQMacroExecutorOptions* options = WuQMacroManager::instance()->getExecutorOptions(); + CaretAssert(options); + options->setIgnoreDelaysAndDurations(checked); +} + /** * Called when a button in dialog is clicked * @@ -669,6 +692,7 @@ WuQMacroDialog::updateDialogContents() m_runOptionLoopCheckBox->setChecked(runOptions->isLooping()); m_runOptionRecordMovieWhileMacroRunsCheckBox->setChecked(runOptions->isRecordMovieDuringExecution()); m_runOptionStopAfterSelectedCommandCheckBox->setChecked(runOptions->isStopAfterSelectedCommand()); + m_ignoreDelaysAndDurationsCheckBox->setChecked(runOptions->isIgnoreDelaysAndDurations()); macroGroupComboBoxActivated(selectedIndex); } @@ -701,9 +725,15 @@ WuQMacroDialog::treeViewCustomContextMenuRequested(const QPoint& pos) WuQMacroCommand* command = getSelectedMacroCommand(); if (command != NULL) { QMenu menu(this); + const QString runToText("Run Macro and Stop After this Command"); menu.addAction(runToText, this, &WuQMacroDialog::runAndStopAfterSelectedCommandMenuItemSelected); + + const QString runToFastText("Run Macro and Stop After this Command Without Delays/Durations"); + menu.addAction(runToFastText, + this, &WuQMacroDialog::runAndStopAfterWithNoDelayDurationSelectedCommandMenuItemSelected); + menu.exec(m_treeView->mapToGlobal(pos)); } } @@ -730,6 +760,30 @@ WuQMacroDialog::runAndStopAfterSelectedCommandMenuItemSelected() } } +/** + * Called when 'run and stop after without delay duration...' is selected from + * context (pop-up) menu. Runs macro to selected command. + */ +void +WuQMacroDialog::runAndStopAfterWithNoDelayDurationSelectedCommandMenuItemSelected() +{ + WuQMacroCommand* command = getSelectedMacroCommand(); + if (command != NULL) { + WuQMacroManager* macroManager = WuQMacroManager::instance(); + WuQMacroExecutorOptions* runOptions = macroManager->getExecutorOptions(); + const bool savedStopAfterOptionFlag = runOptions->isStopAfterSelectedCommand(); + const bool savedIgnoreDelaysFlag = runOptions->isIgnoreDelaysAndDurations(); + runOptions->setStopAfterSelectedCommand(true); + runOptions->setIgnoreDelaysAndDurations(true); + runMacroToolButtonClicked(); + runOptions->setStopAfterSelectedCommand(savedStopAfterOptionFlag); + runOptions->setIgnoreDelaysAndDurations(savedIgnoreDelaysFlag); + } + else { + QMessageBox::warning(this, "Error", "No macro command is selected"); + } +} + /** * Called when an item in the tree is selected in some way * (mouse click, arrow key, etc) diff --git a/src/GuiQt/WuQMacroDialog.h b/src/GuiQt/WuQMacroDialog.h index 9e270d27f..327de3251 100644 --- a/src/GuiQt/WuQMacroDialog.h +++ b/src/GuiQt/WuQMacroDialog.h @@ -80,6 +80,8 @@ namespace caret { void runAndStopAfterSelectedCommandMenuItemSelected(); + void runAndStopAfterWithNoDelayDurationSelectedCommandMenuItemSelected(); + void macroGroupComboBoxActivated(int); void buttonBoxButtonClicked(QAbstractButton* button); @@ -104,6 +106,8 @@ namespace caret { void runOptionStopAfterSelectedCommandCheckBoxClicked(bool); + void runOptionIgnoreDelaysAndDurationsCheckBoxClicked(bool); + void editingMoveUpToolButtonClicked(); void editingMoveDownToolButtonClicked(); @@ -226,6 +230,8 @@ namespace caret { QCheckBox* m_runOptionStopAfterSelectedCommandCheckBox; + QCheckBox* m_ignoreDelaysAndDurationsCheckBox; + QLabel* m_commandTitleLabel; QLabel* m_commandTypeLabel; diff --git a/src/GuiQt/WuQMacroExecutor.cxx b/src/GuiQt/WuQMacroExecutor.cxx index 4ae9dde52..027d63204 100644 --- a/src/GuiQt/WuQMacroExecutor.cxx +++ b/src/GuiQt/WuQMacroExecutor.cxx @@ -393,6 +393,7 @@ WuQMacroExecutor::runMacroPrivate(const WuQMacro* macro, bool allowDelayBeforeCommandFlag(false); emit macroCommandAboutToStart(window, mc, + executorOptions, allowDelayBeforeCommandFlag); /* * May get stopped by user in macroCommandAboutToStart() @@ -408,6 +409,9 @@ WuQMacroExecutor::runMacroPrivate(const WuQMacro* macro, break; } + if (executorOptions->isIgnoreDelaysAndDurations()) { + allowDelayBeforeCommandFlag = false; + } if (allowDelayBeforeCommandFlag) { performCommandDelay(mc); } @@ -418,6 +422,7 @@ WuQMacroExecutor::runMacroPrivate(const WuQMacro* macro, case WuQMacroCommandTypeEnum::CUSTOM_OPERATION: successFlag = WuQMacroManager::instance()->executeCustomOperationMacroCommand(window, executorMonitor, + executorOptions, mc, commandErrorMessage); @@ -459,6 +464,7 @@ WuQMacroExecutor::runMacroPrivate(const WuQMacro* macro, bool allowDelayAfterCommandFlag(false); emit macroCommandHasCompleted(window, mc, + executorOptions, allowDelayAfterCommandFlag); if (m_runOptions.isStopAfterSelectedCommand()) { @@ -476,6 +482,9 @@ WuQMacroExecutor::runMacroPrivate(const WuQMacro* macro, return false; } + if (executorOptions->isIgnoreDelaysAndDurations()) { + allowDelayAfterCommandFlag = false; + } if (allowDelayAfterCommandFlag) { performCommandDelay(mc); } diff --git a/src/GuiQt/WuQMacroExecutor.h b/src/GuiQt/WuQMacroExecutor.h index ca2c43854..b5ffeaa8f 100644 --- a/src/GuiQt/WuQMacroExecutor.h +++ b/src/GuiQt/WuQMacroExecutor.h @@ -66,10 +66,12 @@ namespace caret { signals: void macroCommandAboutToStart(QWidget* window, const WuQMacroCommand* command, + const WuQMacroExecutorOptions* executorOptions, bool& allowDelayFlagOut) const; void macroCommandHasCompleted(QWidget* window, const WuQMacroCommand* command, + const WuQMacroExecutorOptions* executorOptions, bool& allowDelayFlagOut) const; private: diff --git a/src/GuiQt/WuQMacroExecutorOptions.cxx b/src/GuiQt/WuQMacroExecutorOptions.cxx index 6b8ad09c4..210d3f4df 100644 --- a/src/GuiQt/WuQMacroExecutorOptions.cxx +++ b/src/GuiQt/WuQMacroExecutorOptions.cxx @@ -91,6 +91,7 @@ WuQMacroExecutorOptions::copyHelperWuQMacroExecutorOptions(const WuQMacroExecuto m_showMouseMovementFlag = obj.m_showMouseMovementFlag; m_recordMovieDuringExecutionFlag = obj.m_recordMovieDuringExecutionFlag; m_stopAfterSelectedCommandFlag = obj.m_stopAfterSelectedCommandFlag; + m_ignoreDelaysAndDurationsFlag = obj.m_ignoreDelaysAndDurationsFlag; } /** @@ -208,3 +209,25 @@ WuQMacroExecutorOptions::setStopAfterSelectedCommand(const bool status) m_stopAfterSelectedCommandFlag = status; } +/** + * @return True if delays and durations should be ignored, + * usually for editing and debugging macros. + */ +bool +WuQMacroExecutorOptions::isIgnoreDelaysAndDurations() const +{ + return m_ignoreDelaysAndDurationsFlag; +} + +/** + * Set delays and durations should be ignored, + * usually for editing and debugging macros + * + * @param status + * New status + */ +void +WuQMacroExecutorOptions::setIgnoreDelaysAndDurations(const bool status) +{ + m_ignoreDelaysAndDurationsFlag = status; +} diff --git a/src/GuiQt/WuQMacroExecutorOptions.h b/src/GuiQt/WuQMacroExecutorOptions.h index 545fd2802..303112a5c 100644 --- a/src/GuiQt/WuQMacroExecutorOptions.h +++ b/src/GuiQt/WuQMacroExecutorOptions.h @@ -62,6 +62,10 @@ namespace caret { void setStopAfterSelectedCommand(const bool status); + bool isIgnoreDelaysAndDurations() const; + + void setIgnoreDelaysAndDurations(const bool status); + // ADD_NEW_METHODS_HERE virtual AString toString() const; @@ -79,6 +83,8 @@ namespace caret { bool m_stopAfterSelectedCommandFlag = false; + bool m_ignoreDelaysAndDurationsFlag = false; + // ADD_NEW_MEMBERS_HERE }; diff --git a/src/GuiQt/WuQMacroHelperInterface.h b/src/GuiQt/WuQMacroHelperInterface.h index e3d569c18..5070b7e87 100644 --- a/src/GuiQt/WuQMacroHelperInterface.h +++ b/src/GuiQt/WuQMacroHelperInterface.h @@ -130,11 +130,14 @@ namespace caret { * Widget for parent * @param command * Command that has just finished + * @param executorOptions + * Executor options * @param allowDelayFlagOut * Output indicating if delay after command is enabled */ virtual void macroCommandHasCompleted(QWidget* window, const WuQMacroCommand* command, + const WuQMacroExecutorOptions* executorOptions, bool& allowDelayFlagOut) = 0; /** @@ -144,11 +147,14 @@ namespace caret { * Widget for parent * @param command * Command that is about to start + * @param executorOptions + * Executor options * @param allowDelayFlagOut * Output indicating if delay before command is enabled */ virtual void macroCommandAboutToStart(QWidget* window, const WuQMacroCommand* command, + const WuQMacroExecutorOptions* executorOptions, bool& allowDelayFlagOut) = 0; // ADD_NEW_METHODS_HERE diff --git a/src/GuiQt/WuQMacroManager.cxx b/src/GuiQt/WuQMacroManager.cxx index d0827de21..354d5f17c 100644 --- a/src/GuiQt/WuQMacroManager.cxx +++ b/src/GuiQt/WuQMacroManager.cxx @@ -646,12 +646,14 @@ WuQMacroManager::pauseContinueMacro() void WuQMacroManager::macroCommandCompletedExecution(QWidget* window, const WuQMacroCommand* command, + const WuQMacroExecutorOptions* executorOptions, bool& allowDelayFlagOut) { CaretAssert(command); if (m_macroHelper != NULL) { m_macroHelper->macroCommandHasCompleted(window, command, + executorOptions, allowDelayFlagOut); } } @@ -668,13 +670,15 @@ WuQMacroManager::macroCommandCompletedExecution(QWidget* window, */ void WuQMacroManager::macroCommandStartingExecution(QWidget* window, - const WuQMacroCommand* command, - bool& allowDelayFlagOut) + const WuQMacroCommand* command, + const WuQMacroExecutorOptions* executorOptions, + bool& allowDelayFlagOut) { CaretAssert(command); if (m_macroHelper != NULL) { m_macroHelper->macroCommandAboutToStart(window, command, + executorOptions, allowDelayFlagOut); } } @@ -1193,6 +1197,8 @@ WuQMacroManager::editCustomDataValueParameter(QWidget* parent, * Parent widget for any dialogs * @param executorMonitor * The executor monitor + * @param executorOptions + * Options for the executor * @param macroCommand * Custom macro command to run * @param errorMessageOut @@ -1201,6 +1207,7 @@ WuQMacroManager::editCustomDataValueParameter(QWidget* parent, bool WuQMacroManager::executeCustomOperationMacroCommand(QWidget* parent, const WuQMacroExecutorMonitor* executorMonitor, + const WuQMacroExecutorOptions* executorOptions, const WuQMacroCommand* macroCommand, QString& errorMessageOut) { @@ -1213,6 +1220,7 @@ WuQMacroManager::executeCustomOperationMacroCommand(QWidget* parent, if (m_customCommandManager != NULL) { successFlag = m_customCommandManager->executeCustomOperationMacroCommand(parent, executorMonitor, + executorOptions, macroCommand, errorMessageOut); } diff --git a/src/GuiQt/WuQMacroManager.h b/src/GuiQt/WuQMacroManager.h index 6e258931e..2090a1321 100644 --- a/src/GuiQt/WuQMacroManager.h +++ b/src/GuiQt/WuQMacroManager.h @@ -153,6 +153,7 @@ namespace caret { bool executeCustomOperationMacroCommand(QWidget* parent, const WuQMacroExecutorMonitor* executorMonitor, + const WuQMacroExecutorOptions* executorOptions, const WuQMacroCommand* macroCommand, QString& errorMessageOut); @@ -180,10 +181,12 @@ namespace caret { void macroCommandCompletedExecution(QWidget* window, const WuQMacroCommand* command, + const WuQMacroExecutorOptions* executorOptions, bool& allowDelayFlagOut); void macroCommandStartingExecution(QWidget* window, const WuQMacroCommand* command, + const WuQMacroExecutorOptions* executorOptions, bool& allowDelayFlagOut); private: -- GitLab From 5ad4d0159145f2e533af2eddb595e80fa38eb474 Mon Sep 17 00:00:00 2001 From: John Harwell Date: Thu, 14 Mar 2019 13:39:33 -0500 Subject: [PATCH 235/427] On Macro Dialog 'plus' button menu, added and implemented: (1) "Record and Insert New Macro"; (2) "Stop Recording" --- src/GuiQt/WuQMacroCreateDialog.cxx | 91 +++++++++++-------- src/GuiQt/WuQMacroCreateDialog.h | 10 ++- src/GuiQt/WuQMacroDialog.cxx | 137 ++++++++++++++++++++++------- src/GuiQt/WuQMacroDialog.h | 6 ++ src/GuiQt/WuQMacroManager.cxx | 76 ++++++++++++++-- src/GuiQt/WuQMacroManager.h | 7 ++ 6 files changed, 248 insertions(+), 79 deletions(-) diff --git a/src/GuiQt/WuQMacroCreateDialog.cxx b/src/GuiQt/WuQMacroCreateDialog.cxx index fd92eef55..52d730a62 100644 --- a/src/GuiQt/WuQMacroCreateDialog.cxx +++ b/src/GuiQt/WuQMacroCreateDialog.cxx @@ -52,11 +52,20 @@ using namespace caret; /** * Constructor. * + * @param insertIntoMacroGroup + * If not NULL, do not show group selection and insert new macro + * into this group + * @param insertMacroAfter + * Used when inserting macro into a specific group * @param parent * The parent widget */ -WuQMacroCreateDialog::WuQMacroCreateDialog(QWidget* parent) -: QDialog(parent) +WuQMacroCreateDialog::WuQMacroCreateDialog(WuQMacroGroup* insertIntoMacroGroup, + WuQMacro* insertAfterMacro, + QWidget* parent) +: QDialog(parent), +m_insertIntoMacroGroup(insertIntoMacroGroup), +m_insertAfterMacro(insertAfterMacro) { setWindowTitle("Record Macro"); @@ -69,15 +78,22 @@ WuQMacroCreateDialog::WuQMacroCreateDialog(QWidget* parent) QLabel* macroGroupLabel = new QLabel("Store macro in:"); m_macroNameLineEdit = new QLineEdit(); - m_macroNameLineEdit->setText(getDefaultMacroName()); + m_macroNameLineEdit->setText(WuQMacroManager::instance()->getNewMacroDefaultName()); m_macroShortCutKeyComboBox = new WuQMacroShortCutKeyComboBox(this); m_macroDescriptionTextEdit = new QPlainTextEdit(); m_macroDescriptionTextEdit->setFixedHeight(100); + bool insertIntoMacroGroupMatchFlag(false); int32_t selectedMacroGroupIndex(-1); m_macroGroupComboBox = new QComboBox(); for (auto mg : m_macroGroups) { - if (mg->getUniqueIdentifier() == s_lastSelectedMacroGroupIdentifier) { + if (insertIntoMacroGroup != NULL) { + if (mg == insertIntoMacroGroup) { + insertIntoMacroGroupMatchFlag = true; + selectedMacroGroupIndex = m_macroGroupComboBox->count(); + } + } + else if (mg->getUniqueIdentifier() == s_lastSelectedMacroGroupIdentifier) { selectedMacroGroupIndex = m_macroGroupComboBox->count(); } m_macroGroupComboBox->addItem(mg->getName()); @@ -90,6 +106,10 @@ WuQMacroCreateDialog::WuQMacroCreateDialog(QWidget* parent) m_macroGroupComboBox->setCurrentIndex(selectedMacroGroupIndex); } + if (insertIntoMacroGroupMatchFlag) { + m_macroGroupComboBox->setEnabled(false); + } + QGridLayout* gridLayout = new QGridLayout(); gridLayout->setColumnStretch(0, 0); gridLayout->setColumnStretch(1, 0); @@ -104,7 +124,7 @@ WuQMacroCreateDialog::WuQMacroCreateDialog(QWidget* parent) row++; gridLayout->addWidget(shortCutKeyLabel, row, 0); gridLayout->addWidget(shortCutKeyMaskLabel, row, 1); - gridLayout->addWidget(m_macroShortCutKeyComboBox->getWidget(), row, 2); //, Qt::AlignLeft); + gridLayout->addWidget(m_macroShortCutKeyComboBox->getWidget(), row, 2); row++; gridLayout->addWidget(descriptionLabel, row, 0); gridLayout->addWidget(m_macroDescriptionTextEdit, row, 1, 1, 3); @@ -122,47 +142,29 @@ WuQMacroCreateDialog::WuQMacroCreateDialog(QWidget* parent) dialogLayout->addWidget(m_dialogButtonBox); setFixedHeight(sizeHint().height()); + } + /** - * Destructor. + * Constructor. + * + * @param parent + * The parent widget */ -WuQMacroCreateDialog::~WuQMacroCreateDialog() +WuQMacroCreateDialog::WuQMacroCreateDialog(QWidget* parent) +: WuQMacroCreateDialog(NULL, + NULL, + parent) { + /* delegating constructor does the work */ } /** - * @return A default name for a macro + * Destructor. */ -QString -WuQMacroCreateDialog::getDefaultMacroName() const +WuQMacroCreateDialog::~WuQMacroCreateDialog() { - QString name(""); - bool foundFlag(false); - static int32_t minimumIndex = 1; - - for (int32_t i = minimumIndex; i < 10000; i++) { - name = ("Macro " - + QString::number(i)); - foundFlag = false; - for (auto mg : m_macroGroups) { - if (mg->getMacroByName(name) != NULL) { - foundFlag = true; - break; - } - } - - if ( ! foundFlag) { - /* - * If a default name was created, do not allow a "lower-numbered" - * default name. Start with same index since user may not use it. - */ - minimumIndex = i; - break; - } - } - - return name; } /** @@ -193,7 +195,22 @@ WuQMacroCreateDialog::done(int r) const int32_t groupIndex = m_macroGroupComboBox->currentIndex(); if (groupIndex >= 0) { WuQMacroGroup* macroGroup = m_macroGroups[groupIndex]; - macroGroup->addMacro(m_macro); + if (macroGroup == m_insertIntoMacroGroup) { + int32_t insertAtIndex = 0; + if (m_insertAfterMacro != NULL) { + insertAtIndex = macroGroup->getIndexOfMacro(m_insertAfterMacro) + 1; + } + if (insertAtIndex >= 0) { + macroGroup->insertMacroAtIndex(insertAtIndex, + m_macro); + } + else { + macroGroup->addMacro(m_macro); + } + } + else { + macroGroup->addMacro(m_macro); + } s_lastSelectedMacroGroupIdentifier = macroGroup->getUniqueIdentifier(); } } diff --git a/src/GuiQt/WuQMacroCreateDialog.h b/src/GuiQt/WuQMacroCreateDialog.h index 231ce91b1..e26cd1c05 100644 --- a/src/GuiQt/WuQMacroCreateDialog.h +++ b/src/GuiQt/WuQMacroCreateDialog.h @@ -46,6 +46,10 @@ namespace caret { public: WuQMacroCreateDialog(QWidget* parent = 0); + WuQMacroCreateDialog(WuQMacroGroup* insertIntoMacroGroup, + WuQMacro* insertAfterMacro, + QWidget* parent = 0); + virtual ~WuQMacroCreateDialog(); WuQMacroCreateDialog(const WuQMacroCreateDialog&) = delete; @@ -62,8 +66,6 @@ namespace caret { virtual void done(int r) override; private: - QString getDefaultMacroName() const; - std::vector m_macroGroups; QComboBox* m_macroGroupComboBox; @@ -78,6 +80,10 @@ namespace caret { WuQMacro* m_macro = NULL; + WuQMacroGroup* m_insertIntoMacroGroup = NULL; + + WuQMacro* m_insertAfterMacro = NULL; + static QString s_lastSelectedMacroGroupIdentifier; }; diff --git a/src/GuiQt/WuQMacroDialog.cxx b/src/GuiQt/WuQMacroDialog.cxx index cb77fee08..a3b3554ee 100644 --- a/src/GuiQt/WuQMacroDialog.cxx +++ b/src/GuiQt/WuQMacroDialog.cxx @@ -1488,28 +1488,58 @@ WuQMacroDialog::editingInsertToolButtonClicked() if (macroGroup == NULL) { return; } + const WuQMacro* selectedMacro = getSelectedMacro(); - bool insertMacroCommandValidFlag(false); - bool copyAndInsertMacroValidFlag(false); - const std::vector allGroups = WuQMacroManager::instance()->getAllMacroGroups(); + WuQMacroManager* macroManager = WuQMacroManager::instance(); + + /* + * Copy is valid when there is at least one macro, in any macro group + * and the macro is not the selected macro + */ + bool copyValidFlag(false); + const std::vector allGroups = macroManager->getAllMacroGroups(); for (const auto mg : allGroups) { - if (mg->getNumberOfMacros() > 0) { - copyAndInsertMacroValidFlag = true; - break; + const int32_t nm = mg->getNumberOfMacros(); + for (int32_t i = 0; i < nm; i++) { + if (mg->getMacroAtIndex(i) != selectedMacro) { + copyValidFlag = true; + } } } + bool insertMacroValidFlag(false); + bool insertMacroCommandValidFlag(false); switch (getSelectedItemType()) { case WuQMacroStandardItemTypeEnum::INVALID: + insertMacroValidFlag = true; break; case WuQMacroStandardItemTypeEnum::MACRO: + insertMacroValidFlag = true; insertMacroCommandValidFlag = true; break; case WuQMacroStandardItemTypeEnum::MACRO_COMMAND: + insertMacroValidFlag = true; insertMacroCommandValidFlag = true; break; } + bool stopValidFlag(false); + switch (macroManager->getMode()) { + case WuQMacroModeEnum::OFF: + break; + case WuQMacroModeEnum::RECORDING: + insertMacroValidFlag = false; + copyValidFlag = false; + insertMacroCommandValidFlag = false; + stopValidFlag = true; + break; + case WuQMacroModeEnum::RUNNING: + insertMacroValidFlag = false; + copyValidFlag = false; + insertMacroCommandValidFlag = false; + break; + } + QMenu menu(m_editingInsertToolButton); /* @@ -1520,6 +1550,12 @@ WuQMacroDialog::editingInsertToolButtonClicked() &WuQMacroDialog::insertMenuNewMacroCommandSelected); insertNewCommandAction->setEnabled(insertMacroCommandValidFlag); + + QAction* insertRecordNewCommandAction = menu.addAction("Record and Insert New Commands Below", + this, + &WuQMacroDialog::insertMenuRecordNewMacroCommandSelected); + insertRecordNewCommandAction->setEnabled(insertMacroCommandValidFlag); + /* * Macro items */ @@ -1530,15 +1566,38 @@ WuQMacroDialog::editingInsertToolButtonClicked() QAction* copyMacroAction = menu.addAction("Copy and Insert Macro Below...", this, &WuQMacroDialog::insertMenuCopyMacroSelected); - copyMacroAction->setEnabled(copyAndInsertMacroValidFlag); + copyMacroAction->setEnabled(copyValidFlag + && insertMacroValidFlag); - menu.addAction("Insert New Macro Below...", - this, - &WuQMacroDialog::insertMenuNewMacroSelected); + QAction* insertMenuAction = menu.addAction("Insert New Macro Below...", + this, + &WuQMacroDialog::insertMenuNewMacroSelected); + insertMenuAction->setEnabled(insertMacroValidFlag); + + QAction* recordNewMacroAction = menu.addAction("Record and Insert Macro Below...", + this, + &WuQMacroDialog::recordAndInsertNewMacroSelected); + recordNewMacroAction->setEnabled(insertMacroValidFlag); + + menu.addSeparator(); + + QAction* stopRecordingAction = menu.addAction("Stop Recording", + this, + &WuQMacroDialog::stopRecordingSelected); + stopRecordingAction->setEnabled(stopValidFlag); menu.exec(m_editingInsertToolButton->mapToGlobal(QPoint(0, 0))); } +/** + * Called to stop recording + */ +void +WuQMacroDialog::stopRecordingSelected() +{ + WuQMacroManager::instance()->stopRecordingNewMacro(); +} + /** * Called when copy and insert macro menu item is selected */ @@ -1557,6 +1616,26 @@ WuQMacroDialog::insertMenuCopyMacroSelected() } } +/** + * Called when copy and record and insert macro menu item is selected + */ +void +WuQMacroDialog::recordAndInsertNewMacroSelected() +{ + WuQMacroGroup* macroGroup = getSelectedMacroGroup(); + CaretAssert(macroGroup); + + WuQMacro* newMacro = WuQMacroManager::instance()->startRecordingNewMacro(m_editingInsertToolButton, + macroGroup, + getSelectedMacro()); + if (newMacro != NULL) { + const QModelIndex modelIndex = newMacro->index(); + m_treeView->setCurrentIndex(modelIndex); + treeItemSelected(modelIndex); + } +} + + /** * Insert a new macro * @@ -1598,37 +1677,18 @@ void WuQMacroDialog::insertMenuNewMacroSelected() { bool okFlag(false); - const QString macroName = QInputDialog::getText(m_editingInsertToolButton, "Create Macro", "New Macro Name", + const QString defaultName = WuQMacroManager::instance()->getNewMacroDefaultName(); + const QString macroName = QInputDialog::getText(m_editingInsertToolButton, + "Create Macro", + "New Macro Name", QLineEdit::Normal, - "", + defaultName, &okFlag); if (okFlag) { if ( ! macroName.isEmpty()) { WuQMacro* macro = new WuQMacro(); macro->setName(macroName); insertNewMacro(macro); - -// const WuQMacro* selectedMacro = getSelectedMacro(); -// WuQMacroGroup* macroGroup = getSelectedMacroGroup(); -// CaretAssert(macroGroup); -// -// if (selectedMacro != NULL) { -// const int32_t index = macroGroup->getIndexOfMacro(selectedMacro); -// macroGroup->insertMacroAtIndex(index + 1, -// macro); -// } -// else { -// macroGroup->addMacro(macro); -// } -// updateDialogContents(); -// -// QModelIndex selectedIndex = macroGroup->indexFromItem(macro); -// if (selectedIndex.isValid()) { -// m_treeView->setCurrentIndex(selectedIndex); -// updateDialogContents(); -// } -// -// WuQMacroManager::instance()->macroWasModified(macro); } } } @@ -1646,6 +1706,15 @@ WuQMacroDialog::insertMenuNewMacroCommandSelected() } } +/** + * Called when insert and record a new macro command menu item selected + */ +void +WuQMacroDialog::insertMenuRecordNewMacroCommandSelected() +{ + QMessageBox::warning(m_editingInsertToolButton, "", "Not implemented"); +} + /** * Add a new macro command */ diff --git a/src/GuiQt/WuQMacroDialog.h b/src/GuiQt/WuQMacroDialog.h index 327de3251..4ede2835a 100644 --- a/src/GuiQt/WuQMacroDialog.h +++ b/src/GuiQt/WuQMacroDialog.h @@ -130,10 +130,16 @@ namespace caret { void insertMenuCopyMacroSelected(); + void recordAndInsertNewMacroSelected(); + void insertMenuNewMacroSelected(); void insertMenuNewMacroCommandSelected(); + void insertMenuRecordNewMacroCommandSelected(); + + void stopRecordingSelected(); + void addNewMacroCommand(WuQMacroCommand* command); void selectionModelRowChanged(const QModelIndex& current, const QModelIndex& previous); diff --git a/src/GuiQt/WuQMacroManager.cxx b/src/GuiQt/WuQMacroManager.cxx index 354d5f17c..101e87051 100644 --- a/src/GuiQt/WuQMacroManager.cxx +++ b/src/GuiQt/WuQMacroManager.cxx @@ -460,10 +460,36 @@ WuQMacroManager::getAllMacroGroups() const */ void WuQMacroManager::startRecordingNewMacro(QWidget* parent) +{ + startRecordingNewMacro(parent, + NULL, + NULL); +} + +/** + * Start recording a new macro using the New Macro dialog. + * New macro inserted into the given macro group and after the given macro. + * + * @param parent + * Parent for dialog. + * @param insertIntoMacroGroup + * Insert new macro into this macro group + * @param insertAfterMacro + * Insert new macro after this macro (if NULL + * new macro is inserted at beginning of group) + * @return + * Pointer to new macro or NULL if user cancelled + */ +WuQMacro* +WuQMacroManager::startRecordingNewMacro(QWidget* parent, + WuQMacroGroup* insertIntoMacroGroup, + WuQMacro* insertAfterMacro) { CaretAssert(m_mode == WuQMacroModeEnum::OFF); - WuQMacroCreateDialog createMacroDialog(parent); + WuQMacroCreateDialog createMacroDialog(insertIntoMacroGroup, + insertAfterMacro, + parent); if (createMacroDialog.exec() == WuQMacroCreateDialog::Accepted) { m_mode = WuQMacroModeEnum::RECORDING; m_macroBeingRecorded = createMacroDialog.getNewMacro(); @@ -471,8 +497,15 @@ WuQMacroManager::startRecordingNewMacro(QWidget* parent) EventManager::get()->sendEvent(EventUserInterfaceUpdate().getPointer()); } + else { + m_macroBeingRecorded = NULL; + } + + return m_macroBeingRecorded; } + + /** * Stop recording the macro. */ @@ -608,11 +641,6 @@ void WuQMacroManager::stopMacro() { m_macroExecutorMonitor->setMode(WuQMacroExecutorMonitor::Mode::STOP); - -// QMutexLocker locker(&m_macroExecutorMutex); -// if (m_macroExecutor != NULL) { -// m_macroExecutor->stopMacro(); -// } } /** @@ -1318,3 +1346,39 @@ WuQMacroManager::getMainWindowWithIdentifier(const QString& identifier) } return NULL; } + +/** + * @return A default name for a macro + */ +QString +WuQMacroManager::getNewMacroDefaultName() const +{ + QString name(""); + bool foundFlag(false); + static int32_t minimumIndex = 1; + + const std::vector macroGroups = getAllMacroGroups(); + for (int32_t i = minimumIndex; i < 10000; i++) { + name = ("Macro " + + QString::number(i)); + foundFlag = false; + for (auto mg : macroGroups) { + if (mg->getMacroByName(name) != NULL) { + foundFlag = true; + break; + } + } + + if ( ! foundFlag) { + /* + * If a default name was created, do not allow a "lower-numbered" + * default name. Start with same index since user may not use it. + */ + minimumIndex = i; + break; + } + } + + return name; +} + diff --git a/src/GuiQt/WuQMacroManager.h b/src/GuiQt/WuQMacroManager.h index 2090a1321..efe0e4b8e 100644 --- a/src/GuiQt/WuQMacroManager.h +++ b/src/GuiQt/WuQMacroManager.h @@ -94,6 +94,10 @@ namespace caret { void startRecordingNewMacro(QWidget* parent); + WuQMacro* startRecordingNewMacro(QWidget* parent, + WuQMacroGroup* insertIntoMacroGroup, + WuQMacro* insertAfterMacro); + void stopRecordingNewMacro(); void showMacrosDialog(QWidget* parent); @@ -174,6 +178,9 @@ namespace caret { WuQMacroSignalWatcher* getWidgetSignalWatcherWithName(const QString& name); + QString getNewMacroDefaultName() const; + + // ADD_NEW_METHODS_HERE public slots: -- GitLab From 9d3d21780ae7b1a66c2322d5bafc96b0eed2d3d6 Mon Sep 17 00:00:00 2001 From: John Harwell Date: Fri, 15 Mar 2019 08:57:00 -0500 Subject: [PATCH 236/427] Implemented recording and insertion of new commands into a macro. --- src/Common/WuQMacroModeEnum.cxx | 10 +- src/Common/WuQMacroModeEnum.h | 10 +- src/GuiQt/BrainBrowserWindowToolBarTab.cxx | 15 ++- src/GuiQt/WuQMacroDialog.cxx | 36 +++++-- src/GuiQt/WuQMacroManager.cxx | 107 ++++++++++++++++----- src/GuiQt/WuQMacroManager.h | 9 +- src/GuiQt/WuQMacroMenu.cxx | 11 ++- src/GuiQt/WuQMacroSignalWatcher.cxx | 13 ++- 8 files changed, 162 insertions(+), 49 deletions(-) diff --git a/src/Common/WuQMacroModeEnum.cxx b/src/Common/WuQMacroModeEnum.cxx index 13019462f..74e3f6e1b 100644 --- a/src/Common/WuQMacroModeEnum.cxx +++ b/src/Common/WuQMacroModeEnum.cxx @@ -112,9 +112,13 @@ WuQMacroModeEnum::initialize() "OFF", "Off")); - enumData.push_back(WuQMacroModeEnum(RECORDING, - "RECORDING", - "Recording")); + enumData.push_back(WuQMacroModeEnum(RECORDING_INSERT_COMMANDS, + "RECORDING_INSERT_COMMANDS", + "Recording Insert Commands")); + + enumData.push_back(WuQMacroModeEnum(RECORDING_NEW_MACRO, + "RECORDING_NEW_MACRO", + "Recording New Macro")); enumData.push_back(WuQMacroModeEnum(RUNNING, "RUNNING", diff --git a/src/Common/WuQMacroModeEnum.h b/src/Common/WuQMacroModeEnum.h index 4acc8714c..f5fff4fc5 100644 --- a/src/Common/WuQMacroModeEnum.h +++ b/src/Common/WuQMacroModeEnum.h @@ -35,11 +35,13 @@ public: * Enumerated values. */ enum Enum { - /** */ + /** Off */ OFF, - /** */ - RECORDING, - /** */ + /** Recording and inserting commands into a macro */ + RECORDING_INSERT_COMMANDS, + /** Recording a new macro */ + RECORDING_NEW_MACRO, + /** Macro is running */ RUNNING }; diff --git a/src/GuiQt/BrainBrowserWindowToolBarTab.cxx b/src/GuiQt/BrainBrowserWindowToolBarTab.cxx index b675180e1..9f31eec1b 100644 --- a/src/GuiQt/BrainBrowserWindowToolBarTab.cxx +++ b/src/GuiQt/BrainBrowserWindowToolBarTab.cxx @@ -171,11 +171,16 @@ BrainBrowserWindowToolBarTab::updateContent(BrowserTabContent* browserTabContent m_lightingEnabledCheckBox->setChecked(browserTabContent->isLightingEnabled()); - if (WuQMacroManager::instance()->isModeRecording()) { - m_macroRecordingLabel->setText("Macro"); - } - else { - m_macroRecordingLabel->setText(""); + m_macroRecordingLabel->setText(""); + switch (WuQMacroManager::instance()->getMode()) { + case WuQMacroModeEnum::OFF: + break; + case WuQMacroModeEnum::RECORDING_INSERT_COMMANDS: + case WuQMacroModeEnum::RECORDING_NEW_MACRO: + m_macroRecordingLabel->setText("Macro"); + break; + case WuQMacroModeEnum::RUNNING: + break; } blockAllSignals(false); diff --git a/src/GuiQt/WuQMacroDialog.cxx b/src/GuiQt/WuQMacroDialog.cxx index a3b3554ee..f862e0f22 100644 --- a/src/GuiQt/WuQMacroDialog.cxx +++ b/src/GuiQt/WuQMacroDialog.cxx @@ -1198,13 +1198,21 @@ WuQMacroDialog::pauseContinueMacroToolButtonClicked() void WuQMacroDialog::runMacroToolButtonClicked() { - if (WuQMacroManager::instance()->isModeRecording()) { - QMessageBox::critical(m_runMacroToolButton, - "Error", - "A macro is being recorded. Finish recording of macro.", - QMessageBox::Ok, - QMessageBox::NoButton); - return; + switch (WuQMacroManager::instance()->getMode()) { + case WuQMacroModeEnum::OFF: + break; + case WuQMacroModeEnum::RECORDING_INSERT_COMMANDS: + case WuQMacroModeEnum::RECORDING_NEW_MACRO: + { + QMessageBox::critical(m_runMacroToolButton, + "Error", + "A macro is being recorded. Finish recording of macro.", + QMessageBox::Ok, + QMessageBox::NoButton); + return; + } break; + case WuQMacroModeEnum::RUNNING: + break; } WuQMacro* macro = getSelectedMacro(); @@ -1527,7 +1535,8 @@ WuQMacroDialog::editingInsertToolButtonClicked() switch (macroManager->getMode()) { case WuQMacroModeEnum::OFF: break; - case WuQMacroModeEnum::RECORDING: + case WuQMacroModeEnum::RECORDING_INSERT_COMMANDS: + case WuQMacroModeEnum::RECORDING_NEW_MACRO: insertMacroValidFlag = false; copyValidFlag = false; insertMacroCommandValidFlag = false; @@ -1712,7 +1721,16 @@ WuQMacroDialog::insertMenuNewMacroCommandSelected() void WuQMacroDialog::insertMenuRecordNewMacroCommandSelected() { - QMessageBox::warning(m_editingInsertToolButton, "", "Not implemented"); + WuQMacro* macro = getSelectedMacro(); + if (macro == NULL) { + QMessageBox::warning(m_editingInsertToolButton, + "Error", + "No macro is selected for recording new commands"); + return; + } + + WuQMacroManager::instance()->startRecordingNewCommandInsertion(macro, + getSelectedMacroCommand()); } /** diff --git a/src/GuiQt/WuQMacroManager.cxx b/src/GuiQt/WuQMacroManager.cxx index 101e87051..9ba2f1cd6 100644 --- a/src/GuiQt/WuQMacroManager.cxx +++ b/src/GuiQt/WuQMacroManager.cxx @@ -191,15 +191,6 @@ WuQMacroManager::setMode(const WuQMacroModeEnum::Enum mode) m_mode = mode; } -/** - * @return True if mode is RECORDING - */ -bool -WuQMacroManager::isModeRecording() const -{ - return (m_mode == WuQMacroModeEnum::RECORDING); -} - /** * Add macro support to the given object. When recording, * The object's 'value changed' signal will be monitored so @@ -321,10 +312,23 @@ WuQMacroManager::addMacroCommandToRecording(WuQMacroCommand* macroCommand) { CaretAssert(macroCommand); - if (isModeRecording()) { - CaretAssert(m_macroBeingRecorded); - m_macroBeingRecorded->appendMacroCommand(macroCommand); - return true; + switch (getMode()) { + case WuQMacroModeEnum::OFF: + break; + case WuQMacroModeEnum::RECORDING_INSERT_COMMANDS: + CaretAssert(m_macroInsertCommandBeingRecorded); + m_macroInsertCommandBeingRecorded->insertRow(m_macroInsertCommandBeingRecordedOffset, + macroCommand); + m_macroInsertCommandBeingRecordedOffset++; + return true; + break; + case WuQMacroModeEnum::RECORDING_NEW_MACRO: + CaretAssert(m_macroBeingRecorded); + m_macroBeingRecorded->appendMacroCommand(macroCommand); + return true; + break; + case WuQMacroModeEnum::RUNNING: + break; } return false; @@ -351,7 +355,22 @@ WuQMacroManager::addMouseEventToRecording(QWidget* widget, CaretAssert(widget); CaretAssert(me); - if (isModeRecording()) { + WuQMacro* recordingMacro(NULL); + switch (getMode()) { + case WuQMacroModeEnum::OFF: + break; + case WuQMacroModeEnum::RECORDING_INSERT_COMMANDS: + CaretAssert(m_macroInsertCommandBeingRecorded); + recordingMacro = m_macroInsertCommandBeingRecorded; + break; + case WuQMacroModeEnum::RECORDING_NEW_MACRO: + CaretAssert(m_macroBeingRecorded); + recordingMacro = m_macroBeingRecorded; + break; + case WuQMacroModeEnum::RUNNING: + break; + } + if (recordingMacro != NULL) { const QString name(widget->objectName()); if (name.isEmpty()) { CaretLogSevere("Widget name is empty for recording of mouse event\n" @@ -364,7 +383,6 @@ WuQMacroManager::addMouseEventToRecording(QWidget* widget, + "\n" + SystemUtilities::getBackTrace()); } - CaretAssert(m_macroBeingRecorded); bool validMouseEventFlag(true); WuQMacroMouseEventTypeEnum::Enum mouseEventType = WuQMacroMouseEventTypeEnum::MOVE; @@ -413,7 +431,7 @@ WuQMacroManager::addMouseEventToRecording(QWidget* widget, "mouse operation", 1.0, errorMessage); - m_macroBeingRecorded->appendMacroCommand(command); + recordingMacro->appendMacroCommand(command); return true; } } @@ -491,7 +509,7 @@ WuQMacroManager::startRecordingNewMacro(QWidget* parent, insertAfterMacro, parent); if (createMacroDialog.exec() == WuQMacroCreateDialog::Accepted) { - m_mode = WuQMacroModeEnum::RECORDING; + m_mode = WuQMacroModeEnum::RECORDING_NEW_MACRO; m_macroBeingRecorded = createMacroDialog.getNewMacro(); CaretAssert(m_macroBeingRecorded); @@ -504,7 +522,33 @@ WuQMacroManager::startRecordingNewMacro(QWidget* parent, return m_macroBeingRecorded; } - +/** + * Start recording commands and insert them into the given macro after + * the given macro command. + * + * @param insertIntoMacro + * Macro into which new commands are inserted. + * @param insertAfterMacroCommand + * New commands are inserted after this command or at beginning + * if this command is NULL. + */ +void +WuQMacroManager::startRecordingNewCommandInsertion(WuQMacro* insertIntoMacro, + WuQMacroCommand* insertAfterMacroCommand) +{ + CaretAssert(m_mode == WuQMacroModeEnum::OFF); + CaretAssert(insertIntoMacro); + + m_mode = WuQMacroModeEnum::RECORDING_INSERT_COMMANDS; + m_macroInsertCommandBeingRecorded = insertIntoMacro; + m_macroInsertCommandBeingRecordedOffset = 0; + if (insertAfterMacroCommand != NULL) { + const int32_t index = insertIntoMacro->getIndexOfMacroCommand(insertAfterMacroCommand) + 1; + CaretAssert(index >= 0); + m_macroInsertCommandBeingRecordedOffset = index; + EventManager::get()->sendEvent(EventUserInterfaceUpdate().getPointer()); + } +} /** * Stop recording the macro. @@ -512,14 +556,33 @@ WuQMacroManager::startRecordingNewMacro(QWidget* parent, void WuQMacroManager::stopRecordingNewMacro() { - CaretAssert(m_mode == WuQMacroModeEnum::RECORDING); + switch (m_mode) { + case WuQMacroModeEnum::OFF: + CaretAssert(0); + break; + case WuQMacroModeEnum::RECORDING_INSERT_COMMANDS: + CaretAssert(m_macroInsertCommandBeingRecorded); + if (m_macroHelper) { + m_macroHelper->macroWasModified(m_macroInsertCommandBeingRecorded); + } + break; + case WuQMacroModeEnum::RECORDING_NEW_MACRO: + CaretAssert(m_macroBeingRecorded); + if (m_macroHelper) { + m_macroHelper->macroWasModified(m_macroBeingRecorded); + } + break; + case WuQMacroModeEnum::RUNNING: + CaretAssert(0); + break; + } m_mode = WuQMacroModeEnum::OFF; - if (m_macroHelper) { - m_macroHelper->macroWasModified(m_macroBeingRecorded); - } m_macroBeingRecorded = NULL; + m_macroInsertCommandBeingRecorded = NULL; + m_macroInsertCommandBeingRecordedOffset = -1; + EventManager::get()->sendEvent(EventUserInterfaceUpdate().getPointer()); updateNonModalDialogs(); } diff --git a/src/GuiQt/WuQMacroManager.h b/src/GuiQt/WuQMacroManager.h index efe0e4b8e..d9e07d584 100644 --- a/src/GuiQt/WuQMacroManager.h +++ b/src/GuiQt/WuQMacroManager.h @@ -86,8 +86,6 @@ namespace caret { void setMode(const WuQMacroModeEnum::Enum mode); - bool isModeRecording() const; - std::vector getActiveMacroGroups() const; std::vector getAllMacroGroups() const; @@ -98,6 +96,9 @@ namespace caret { WuQMacroGroup* insertIntoMacroGroup, WuQMacro* insertAfterMacro); + void startRecordingNewCommandInsertion(WuQMacro* insertIntoMacro, + WuQMacroCommand* insertAfterMacroCommand); + void stopRecordingNewMacro(); void showMacrosDialog(QWidget* parent); @@ -210,6 +211,10 @@ namespace caret { WuQMacro* m_macroBeingRecorded = NULL; + WuQMacro* m_macroInsertCommandBeingRecorded = NULL; + + int32_t m_macroInsertCommandBeingRecordedOffset = -1; + WuQMacroDialog* m_macrosDialog = NULL; static WuQMacroManager* s_singletonMacroManager; diff --git a/src/GuiQt/WuQMacroMenu.cxx b/src/GuiQt/WuQMacroMenu.cxx index 87ad0c78a..099f8c3a8 100644 --- a/src/GuiQt/WuQMacroMenu.cxx +++ b/src/GuiQt/WuQMacroMenu.cxx @@ -107,7 +107,8 @@ WuQMacroMenu::macroMenuAboutToShow() editValidFlag = true; recordValidFlag = true; break; - case WuQMacroModeEnum::RECORDING: + case WuQMacroModeEnum::RECORDING_INSERT_COMMANDS: + case WuQMacroModeEnum::RECORDING_NEW_MACRO: stopValidFlag = true; break; case WuQMacroModeEnum::RUNNING: @@ -132,7 +133,10 @@ WuQMacroMenu::macroRecordSelected() case WuQMacroModeEnum::OFF: macroManager->startRecordingNewMacro(m_windowParent); break; - case WuQMacroModeEnum::RECORDING: + case WuQMacroModeEnum::RECORDING_INSERT_COMMANDS: + CaretAssert(0); + break; + case WuQMacroModeEnum::RECORDING_NEW_MACRO: CaretAssert(0); break; case WuQMacroModeEnum::RUNNING: @@ -154,7 +158,8 @@ WuQMacroMenu::macroStopSelected() case WuQMacroModeEnum::OFF: CaretAssert(0); break; - case WuQMacroModeEnum::RECORDING: + case WuQMacroModeEnum::RECORDING_INSERT_COMMANDS: + case WuQMacroModeEnum::RECORDING_NEW_MACRO: macroManager->stopRecordingNewMacro(); break; case WuQMacroModeEnum::RUNNING: diff --git a/src/GuiQt/WuQMacroSignalWatcher.cxx b/src/GuiQt/WuQMacroSignalWatcher.cxx index d31ecdc56..190623900 100644 --- a/src/GuiQt/WuQMacroSignalWatcher.cxx +++ b/src/GuiQt/WuQMacroSignalWatcher.cxx @@ -639,7 +639,18 @@ WuQMacroSignalWatcher::createMacroCommandWithDefaultParameters(QString& errorMes void WuQMacroSignalWatcher::createAndSendMacroCommand(std::vector& parameters) { - if (m_parentMacroManager->isModeRecording()) { + bool recordingFlag(false); + switch (m_parentMacroManager->getMode()) { + case WuQMacroModeEnum::OFF: + break; + case WuQMacroModeEnum::RECORDING_INSERT_COMMANDS: + case WuQMacroModeEnum::RECORDING_NEW_MACRO: + recordingFlag = true; + break; + case WuQMacroModeEnum::RUNNING: + break; + } + if (recordingFlag) { const int32_t versionNumber(1); QString errorMessage; WuQMacroCommand* mc = WuQMacroCommand::newInstanceWidgetCommand(m_objectType, -- GitLab From 2ea5b0fb38c1f618a76c7a875e7c5b02fcf767a5 Mon Sep 17 00:00:00 2001 From: John Harwell Date: Fri, 15 Mar 2019 09:02:01 -0500 Subject: [PATCH 237/427] Fixed surface matching crash that occurred when there was no right surface, only a left surface. --- src/Brain/BrainOpenGLFixedPipeline.cxx | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/Brain/BrainOpenGLFixedPipeline.cxx b/src/Brain/BrainOpenGLFixedPipeline.cxx index a72eddd8c..108c89535 100644 --- a/src/Brain/BrainOpenGLFixedPipeline.cxx +++ b/src/Brain/BrainOpenGLFixedPipeline.cxx @@ -5226,9 +5226,11 @@ BrainOpenGLFixedPipeline::drawWholeBrainModel(BrowserTabContent* browserTabConte } } BrainStructure* rightStructure = m_brain->getBrainStructure(StructureEnum::CORTEX_RIGHT, false); - Surface* rightPrimaryAnat = rightStructure->getPrimaryAnatomicalSurface(); - if (rightPrimaryAnat != NULL) { - rightSurface = rightPrimaryAnat; + if (rightStructure != NULL) { + Surface* rightPrimaryAnat = rightStructure->getPrimaryAnatomicalSurface(); + if (rightPrimaryAnat != NULL) { + rightSurface = rightPrimaryAnat; + } } /* 2/22/19 ALL SURFACES SAME VIEWPORT */ } -- GitLab From caa9ef90b6c7c4d69217e840033b4002f31385bc Mon Sep 17 00:00:00 2001 From: John Harwell Date: Fri, 15 Mar 2019 15:05:12 -0500 Subject: [PATCH 238/427] On Macros dialog, move the record macro or macro command menu items from insert button (plus symbol) menu into a new button "Record". The Record button is used for starting and stopping recording of macros on the macros dialog. --- src/GuiQt/WuQMacroDialog.cxx | 162 ++++++++++++++++++++++++++-------- src/GuiQt/WuQMacroDialog.h | 11 +++ src/GuiQt/WuQMacroManager.cxx | 2 +- 3 files changed, 138 insertions(+), 37 deletions(-) diff --git a/src/GuiQt/WuQMacroDialog.cxx b/src/GuiQt/WuQMacroDialog.cxx index f862e0f22..fd6d767c4 100644 --- a/src/GuiQt/WuQMacroDialog.cxx +++ b/src/GuiQt/WuQMacroDialog.cxx @@ -233,6 +233,18 @@ WuQMacroDialog::createMacroRunAndEditingToolButtons() QObject::connect(m_stopMacroToolButton, &QToolButton::clicked, this, &WuQMacroDialog::stopMacroToolButtonClicked); + m_recordMacroToolButton = new QToolButton(); + m_recordMacroToolButton->setToolButtonStyle(Qt::ToolButtonTextBesideIcon); + m_recordMacroToolButton->setText("Record"); + m_recordMacroToolButtonIconOff = createEditingToolButtonPixmap(m_recordMacroToolButton, + EditButton::RECORD_OFF); + m_recordMacroToolButtonIconOn = createEditingToolButtonPixmap(m_recordMacroToolButton, + EditButton::RECORD_ON); + m_recordMacroToolButton->setIcon(m_recordMacroToolButtonIconOff); + m_recordMacroToolButton->setToolTip("Record a new macro or record new commands"); + QObject::connect(m_recordMacroToolButton, &QToolButton::clicked, + this, &WuQMacroDialog::recordMacroToolButtonClicked); + m_editingMoveUpToolButton = new QToolButton(); QPixmap moveUpPixmap = createEditingToolButtonPixmap(m_editingMoveUpToolButton, EditButton::MOVE_UP); @@ -265,18 +277,21 @@ WuQMacroDialog::createMacroRunAndEditingToolButtons() QObject::connect(m_editingInsertToolButton, &QToolButton::clicked, this, &WuQMacroDialog::editingInsertToolButtonClicked); + const int spaceAmount(7); QWidget* widget = new QWidget(); QHBoxLayout* toolButtonLayout = new QHBoxLayout(widget); toolButtonLayout->setContentsMargins(0, 0, 0, 0); + toolButtonLayout->addWidget(m_stopMacroToolButton); toolButtonLayout->addWidget(m_runMacroToolButton); toolButtonLayout->addWidget(m_pauseMacroToolButton); - toolButtonLayout->addWidget(m_stopMacroToolButton); + toolButtonLayout->addWidget(m_recordMacroToolButton); + toolButtonLayout->addSpacing(3 * spaceAmount); toolButtonLayout->addStretch(); toolButtonLayout->addWidget(m_editingInsertToolButton); - toolButtonLayout->addSpacing(10); + toolButtonLayout->addSpacing(spaceAmount); toolButtonLayout->addWidget(m_editingMoveUpToolButton); toolButtonLayout->addWidget(m_editingMoveDownToolButton); - toolButtonLayout->addSpacing(10); + toolButtonLayout->addSpacing(spaceAmount); toolButtonLayout->addWidget(m_editingDeleteToolButton); return widget; @@ -1269,6 +1284,47 @@ WuQMacroDialog::stopMacroToolButtonClicked() updateEditingToolButtons(); } +/** + * Called when record button is clicked + */ +void +WuQMacroDialog::recordMacroToolButtonClicked() +{ + bool startRecordingValid(false); + bool stopRecordingValid(false); + switch (WuQMacroManager::instance()->getMode()) { + case WuQMacroModeEnum::OFF: + startRecordingValid = true; + break; + case WuQMacroModeEnum::RECORDING_INSERT_COMMANDS: + case WuQMacroModeEnum::RECORDING_NEW_MACRO: + stopRecordingValid = true; + break; + case WuQMacroModeEnum::RUNNING: + break; + } + + if (startRecordingValid) { + QMenu menu(m_editingInsertToolButton); + QAction* newCommandAction = menu.addAction("Record and Insert New Commands Below", + this, + &WuQMacroDialog::insertMenuRecordNewMacroCommandSelected); + newCommandAction->setEnabled(getSelectedMacro() != NULL); + + menu.addSeparator(); + + QAction* newMacroAction = menu.addAction("Record and Insert Macro Below...", + this, + &WuQMacroDialog::recordAndInsertNewMacroSelected); + newMacroAction->setEnabled(getSelectedMacroGroup() != NULL); + + menu.exec(m_recordMacroToolButton->mapToGlobal(QPoint(0, 0))); + } + else if (stopRecordingValid) { + stopRecordingSelected(); + } +} + /** * Called when import item is selected */ @@ -1549,6 +1605,8 @@ WuQMacroDialog::editingInsertToolButtonClicked() break; } + const bool showRecordItemsFlag(false); + QMenu menu(m_editingInsertToolButton); /* @@ -1559,11 +1617,12 @@ WuQMacroDialog::editingInsertToolButtonClicked() &WuQMacroDialog::insertMenuNewMacroCommandSelected); insertNewCommandAction->setEnabled(insertMacroCommandValidFlag); - - QAction* insertRecordNewCommandAction = menu.addAction("Record and Insert New Commands Below", - this, - &WuQMacroDialog::insertMenuRecordNewMacroCommandSelected); - insertRecordNewCommandAction->setEnabled(insertMacroCommandValidFlag); + if (showRecordItemsFlag) { + QAction* insertRecordNewCommandAction = menu.addAction("Record and Insert New Commands Below", + this, + &WuQMacroDialog::insertMenuRecordNewMacroCommandSelected); + insertRecordNewCommandAction->setEnabled(insertMacroCommandValidFlag); + } /* * Macro items @@ -1583,17 +1642,12 @@ WuQMacroDialog::editingInsertToolButtonClicked() &WuQMacroDialog::insertMenuNewMacroSelected); insertMenuAction->setEnabled(insertMacroValidFlag); - QAction* recordNewMacroAction = menu.addAction("Record and Insert Macro Below...", - this, - &WuQMacroDialog::recordAndInsertNewMacroSelected); - recordNewMacroAction->setEnabled(insertMacroValidFlag); - - menu.addSeparator(); - - QAction* stopRecordingAction = menu.addAction("Stop Recording", - this, - &WuQMacroDialog::stopRecordingSelected); - stopRecordingAction->setEnabled(stopValidFlag); + if (showRecordItemsFlag) { + QAction* recordNewMacroAction = menu.addAction("Record and Insert Macro Below...", + this, + &WuQMacroDialog::recordAndInsertNewMacroSelected); + recordNewMacroAction->setEnabled(insertMacroValidFlag); + } menu.exec(m_editingInsertToolButton->mapToGlobal(QPoint(0, 0))); } @@ -1853,6 +1907,23 @@ WuQMacroDialog::updateEditingToolButtons() m_editingInsertToolButton->setEnabled(insertValid); m_editingMoveDownToolButton->setEnabled(moveDownValid); m_editingMoveUpToolButton->setEnabled(moveUpValid); + + m_recordMacroToolButton->setEnabled(true); + switch (WuQMacroManager::instance()->getMode()) { + case WuQMacroModeEnum::OFF: + m_recordMacroToolButton->setIcon(m_recordMacroToolButtonIconOff); + break; + case WuQMacroModeEnum::RECORDING_INSERT_COMMANDS: + m_recordMacroToolButton->setIcon(m_recordMacroToolButtonIconOn); + break; + case WuQMacroModeEnum::RECORDING_NEW_MACRO: + m_recordMacroToolButton->setIcon(m_recordMacroToolButtonIconOn); + break; + case WuQMacroModeEnum::RUNNING: + m_recordMacroToolButton->setIcon(m_recordMacroToolButtonIconOff); + m_recordMacroToolButton->setEnabled(true); + break; + } } /** @@ -1884,6 +1955,14 @@ WuQMacroDialog::createEditingToolButtonPixmap(const QWidget* widget, break; case EditButton::PAUSE: break; + case EditButton::RECORD_OFF: + /* allow background */ + //pixmapOptions = 0; + break; + case EditButton::RECORD_ON: + /* allow background */ + pixmapOptions = 0; + break; case EditButton::RUN: /* allow background */ pixmapOptions = 0; @@ -1947,18 +2026,33 @@ WuQMacroDialog::createEditingToolButtonPixmap(const QWidget* widget, painter->drawLine(QPointF( x, y), QPointF( x, -y)); } break; + case EditButton::RECORD_OFF: + { + painter->setPen(Qt::red); + painter->drawEllipse(QPointF(0, 0), + maxValue, maxValue); + } + break; + case EditButton::RECORD_ON: + { + painter->setPen(Qt::red); + painter->setBrush(Qt::red); + painter->drawEllipse(QPointF(0, 0), + maxValue, maxValue); + } + break; case EditButton::RUN: { /* * Triangle */ - painter->setPen(Qt::green); - painter->setBrush(Qt::green); + const qreal y = maxValue * 0.85; + painter->setBrush(pen.color()); const qreal p = pixmapSize / 3; const QPointF points[3] = { - QPointF(-p, -maxValue), + QPointF(-p, -y), QPointF( p, 0.0), - QPointF(-p, maxValue) + QPointF(-p, y) }; painter->drawConvexPolygon(points, 3); } @@ -1966,21 +2060,17 @@ WuQMacroDialog::createEditingToolButtonPixmap(const QWidget* widget, case EditButton::STOP: { /* - * Hexagon + * Square */ - painter->setPen(Qt::red); - painter->setBrush(Qt::red); - const qreal r = pixmapSize / 2; - const qreal r2 = r / 2.0; - const QPointF points[6] = { - QPointF( -r, 0.0), - QPointF(-r2, -r), - QPointF( r2, -r), - QPointF( r, 0.0), - QPointF( r2, r), - QPointF(-r2, r) + painter->setBrush(pen.color()); + const qreal a = maxValue * 0.85; + const QPointF points[4] = { + QPointF(-a, -a), + QPointF( a, -a), + QPointF( a, a), + QPointF(-a, a) }; - painter->drawConvexPolygon(points, 6); + painter->drawConvexPolygon(points, 4); } break; } diff --git a/src/GuiQt/WuQMacroDialog.h b/src/GuiQt/WuQMacroDialog.h index 4ede2835a..3abbac43f 100644 --- a/src/GuiQt/WuQMacroDialog.h +++ b/src/GuiQt/WuQMacroDialog.h @@ -26,6 +26,7 @@ #include #include +#include #include "WuQMacroCommandParameter.h" #include "WuQMacroShortCutKeyEnum.h" @@ -122,6 +123,8 @@ namespace caret { void stopMacroToolButtonClicked(); + void recordMacroToolButtonClicked(); + void macroCommandDelaySpinBoxValueChanged(double); void macroCommandDescriptionTextEditChanged(); @@ -159,6 +162,8 @@ namespace caret { MOVE_DOWN, MOVE_UP, PAUSE, + RECORD_OFF, + RECORD_ON, RUN, STOP }; @@ -260,6 +265,12 @@ namespace caret { QToolButton* m_stopMacroToolButton; + QToolButton* m_recordMacroToolButton; + + QIcon m_recordMacroToolButtonIconOff; + + QIcon m_recordMacroToolButtonIconOn; + QToolButton* m_editingMoveUpToolButton; QToolButton* m_editingMoveDownToolButton; diff --git a/src/GuiQt/WuQMacroManager.cxx b/src/GuiQt/WuQMacroManager.cxx index 9ba2f1cd6..810e1b9cc 100644 --- a/src/GuiQt/WuQMacroManager.cxx +++ b/src/GuiQt/WuQMacroManager.cxx @@ -546,8 +546,8 @@ WuQMacroManager::startRecordingNewCommandInsertion(WuQMacro* insertIntoMacro, const int32_t index = insertIntoMacro->getIndexOfMacroCommand(insertAfterMacroCommand) + 1; CaretAssert(index >= 0); m_macroInsertCommandBeingRecordedOffset = index; - EventManager::get()->sendEvent(EventUserInterfaceUpdate().getPointer()); } + EventManager::get()->sendEvent(EventUserInterfaceUpdate().getPointer()); } /** -- GitLab From 72e9978764f9938a90542f4875f0832214f41225 Mon Sep 17 00:00:00 2001 From: John Harwell Date: Mon, 18 Mar 2019 12:08:53 -0500 Subject: [PATCH 239/427] Add some timing to movie image capture. Capture of image is slower than writing of image. Timing is disabled. --- src/GuiQt/BrainOpenGLWidget.cxx | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/src/GuiQt/BrainOpenGLWidget.cxx b/src/GuiQt/BrainOpenGLWidget.cxx index f283f588b..0630a4215 100644 --- a/src/GuiQt/BrainOpenGLWidget.cxx +++ b/src/GuiQt/BrainOpenGLWidget.cxx @@ -51,6 +51,7 @@ #include "DataToolTipsManager.h" #include "DeveloperFlagsEnum.h" #include "DummyFontTextRenderer.h" +#include "ElapsedTimer.h" #include "EventBrainReset.h" #include "EventImageCapture.h" #include "EventModelGetAll.h" @@ -1715,6 +1716,8 @@ BrainOpenGLWidget::receiveEvent(Event* event) if (captureAutomaticImageForMovieFlag || captureManualImageForMovieFlag) { + const bool showTimingResultFlag(false); + int captureRegionOffsetX(0); int captureRegionOffsetY(0); int captureRegionWidth(0); @@ -1784,6 +1787,8 @@ BrainOpenGLWidget::receiveEvent(Event* event) adjustImageSizeFlag = true; } + ElapsedTimer timer; + timer.start(); EventImageCapture captureEvent(this->windowIndex, captureRegionOffsetX, captureRegionOffsetY, @@ -1792,6 +1797,9 @@ BrainOpenGLWidget::receiveEvent(Event* event) captureWidth, captureHeight); captureImage(&captureEvent); + if (showTimingResultFlag) { + std::cout << "Capture time: " << timer.getElapsedTimeSeconds() << std::endl; + } if (captureEvent.isError()) { CaretLogSevere("Failed to capture image of graphics for movie recording"); @@ -1842,12 +1850,17 @@ BrainOpenGLWidget::receiveEvent(Event* event) } if (imageValid) { + ElapsedTimer imageTimer; + imageTimer.start(); if (captureAutomaticImageForMovieFlag) { movieRecorder->addImageToMovie(&image); } else if (captureManualImageForMovieFlag) { movieRecorder->addImageToMovieWithManualDuration(&image); } + if (showTimingResultFlag) { + std::cout << " Image write time: " << imageTimer.getElapsedTimeSeconds() << std::endl; + } EventManager::get()->sendSimpleEvent(EventTypeEnum::EVENT_MOVIE_RECORDING_DIALOG_UPDATE); } } -- GitLab From 19984e89e42a12cf2661d3f2793f55a68b59d32a Mon Sep 17 00:00:00 2001 From: John Harwell Date: Mon, 18 Mar 2019 12:34:37 -0500 Subject: [PATCH 240/427] Minor changes in list of Run Options on Macro Dialog. --- src/GuiQt/WuQMacroDialog.cxx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/GuiQt/WuQMacroDialog.cxx b/src/GuiQt/WuQMacroDialog.cxx index fd6d767c4..1eeb27b8d 100644 --- a/src/GuiQt/WuQMacroDialog.cxx +++ b/src/GuiQt/WuQMacroDialog.cxx @@ -479,13 +479,13 @@ WuQMacroDialog::createRunOptionsWidget() row++; runOptionsLayout->addWidget(m_runOptionLoopCheckBox, row, 0, 1, 2, Qt::AlignLeft); row++; - runOptionsLayout->addWidget(m_runOptionMoveMouseCheckBox, row, 0, 1, 2, Qt::AlignLeft); + runOptionsLayout->addWidget(m_ignoreDelaysAndDurationsCheckBox, row, 0, 1, 2, Qt::AlignLeft); row++; - runOptionsLayout->addWidget(m_runOptionRecordMovieWhileMacroRunsCheckBox, row, 0, 1, 2, Qt::AlignLeft); + runOptionsLayout->addWidget(m_runOptionMoveMouseCheckBox, row, 0, 1, 2, Qt::AlignLeft); row++; runOptionsLayout->addWidget(m_runOptionStopAfterSelectedCommandCheckBox, row, 0, 1, 2, Qt::AlignLeft); row++; - runOptionsLayout->addWidget(m_ignoreDelaysAndDurationsCheckBox, row, 0, 1, 2, Qt::AlignLeft); + runOptionsLayout->addWidget(m_runOptionRecordMovieWhileMacroRunsCheckBox, row, 0, 1, 2, Qt::AlignLeft); row++; return widget; -- GitLab From 95da7abcc0be578591e8591252f015e33fa94df5 Mon Sep 17 00:00:00 2001 From: John Harwell Date: Mon, 18 Mar 2019 15:39:01 -0500 Subject: [PATCH 241/427] Implemented looping mode for macros. --- src/GuiQt/WuQMacroDialog.cxx | 12 ++-- src/GuiQt/WuQMacroManager.cxx | 107 +++++++++++++++++++--------------- src/GuiQt/WuQMacroManager.h | 6 +- 3 files changed, 70 insertions(+), 55 deletions(-) diff --git a/src/GuiQt/WuQMacroDialog.cxx b/src/GuiQt/WuQMacroDialog.cxx index 1eeb27b8d..b6f66269f 100644 --- a/src/GuiQt/WuQMacroDialog.cxx +++ b/src/GuiQt/WuQMacroDialog.cxx @@ -439,7 +439,6 @@ WuQMacroDialog::createRunOptionsWidget() m_runOptionLoopCheckBox = new QCheckBox("Loop"); m_runOptionLoopCheckBox->setChecked(false); - m_runOptionLoopCheckBox->setEnabled(false); QObject::connect(m_runOptionLoopCheckBox, &QCheckBox::clicked, this, &WuQMacroDialog::runOptionLoopCheckBoxClicked); @@ -1266,11 +1265,16 @@ WuQMacroDialog::runMacroToolButtonClicked() m_macroIsRunningFlag = true; updateEditingToolButtons(); QApplication::processEvents(); - WuQMacroManager::instance()->runMacro(window, - macro, - getSelectedMacroCommand()); + WuQMacro* lastMacroRun = WuQMacroManager::instance()->runMacro(window, + macro, + getSelectedMacroCommand()); m_macroIsRunningFlag = false; + if (lastMacroRun != getSelectedMacro()) { + QModelIndex modelIndex = macro->index(); + m_treeView->setCurrentIndex(modelIndex); + treeItemSelected(modelIndex); + } updateEditingToolButtons(); } diff --git a/src/GuiQt/WuQMacroManager.cxx b/src/GuiQt/WuQMacroManager.cxx index 810e1b9cc..e57eae143 100644 --- a/src/GuiQt/WuQMacroManager.cxx +++ b/src/GuiQt/WuQMacroManager.cxx @@ -636,65 +636,76 @@ WuQMacroManager::updateNonModalDialogs() * * @param widget * Widget used for parent of dialogs - * @param macro + * @param macroToRun * Macro that is run * @param macroCommandToStopAfter * Macro command that the executor may stop after, depending upon options + * @return + * Pointer to macro that was last run. The selected macro could change + * when looping is enabled. */ -void +WuQMacro* WuQMacroManager::runMacro(QWidget* widget, - const WuQMacro* macro, + const WuQMacro* macroToRun, const WuQMacroCommand* macroCommandToStopAfter) { CaretAssert(widget); - CaretAssert(macro); - - - QString errorMessage; - m_macroExecutor = new WuQMacroExecutor(); - QObject::connect(m_macroExecutor, &WuQMacroExecutor::macroCommandAboutToStart, - this, &WuQMacroManager::macroCommandStartingExecution); - QObject::connect(m_macroExecutor, &WuQMacroExecutor::macroCommandHasCompleted, - this, &WuQMacroManager::macroCommandCompletedExecution); - - m_macroExecutorMonitor->setMode(WuQMacroExecutorMonitor::Mode::RUN); - - if (m_macroHelper != NULL) { - m_macroHelper->macroExecutionStarting(macro, - widget, - m_executorOptions.get()); - } - const bool resultFlag = m_macroExecutor->runMacro(macro, - macroCommandToStopAfter, - widget, - m_parentObjects, - m_macroExecutorMonitor, - m_executorOptions.get(), - errorMessage); - if (m_macroHelper != NULL) { - m_macroHelper->macroExecutionEnding(macro, - widget, - m_executorOptions.get()); - } - - m_macroExecutorMonitor->setMode(WuQMacroExecutorMonitor::Mode::STOP); + CaretAssert(macroToRun); + WuQMacro* macro(const_cast(macroToRun)); - if ( ! resultFlag) { - QMessageBox::critical(widget, - "Run Macro Error", - errorMessage, - QMessageBox::Ok, - QMessageBox::NoButton); + bool resultFlag(false); + bool loopFlag(true); + while (loopFlag) { + loopFlag = m_executorOptions->isLooping(); + QString errorMessage; + m_macroExecutor = new WuQMacroExecutor(); + QObject::connect(m_macroExecutor, &WuQMacroExecutor::macroCommandAboutToStart, + this, &WuQMacroManager::macroCommandStartingExecution); + QObject::connect(m_macroExecutor, &WuQMacroExecutor::macroCommandHasCompleted, + this, &WuQMacroManager::macroCommandCompletedExecution); + + m_macroExecutorMonitor->setMode(WuQMacroExecutorMonitor::Mode::RUN); + + if (m_macroHelper != NULL) { + m_macroHelper->macroExecutionStarting(macro, + widget, + m_executorOptions.get()); + } + resultFlag = m_macroExecutor->runMacro(macro, + macroCommandToStopAfter, + widget, + m_parentObjects, + m_macroExecutorMonitor, + m_executorOptions.get(), + errorMessage); + if (m_macroHelper != NULL) { + m_macroHelper->macroExecutionEnding(macro, + widget, + m_executorOptions.get()); + } + + m_macroExecutorMonitor->setMode(WuQMacroExecutorMonitor::Mode::STOP); + + if ( ! resultFlag) { + QMessageBox::critical(widget, + "Run Macro Error", + errorMessage, + QMessageBox::Ok, + QMessageBox::NoButton); + loopFlag = false; + } + + /* + * Mutex needed so stop() method does not try + * to access an invalid pointer to executor. + */ + QMutexLocker locker(&m_macroExecutorMutex); + delete m_macroExecutor; + m_macroExecutor = NULL; + locker.unlock(); } - /* - * Mutex needed so stop() method does not try - * to access an invalid pointer to executor. - */ - QMutexLocker locker(&m_macroExecutorMutex); - delete m_macroExecutor; - m_macroExecutor = NULL; - locker.unlock(); + return macro; } /** diff --git a/src/GuiQt/WuQMacroManager.h b/src/GuiQt/WuQMacroManager.h index d9e07d584..b69ae32e4 100644 --- a/src/GuiQt/WuQMacroManager.h +++ b/src/GuiQt/WuQMacroManager.h @@ -127,9 +127,9 @@ namespace caret { WuQMacroGroup* macroGroup, WuQMacro* macro); - void runMacro(QWidget* window, - const WuQMacro* macro, - const WuQMacroCommand* macroCommandToStopAfter); + WuQMacro* runMacro(QWidget* window, + const WuQMacro* macro, + const WuQMacroCommand* macroCommandToStopAfter); void stopMacro(); -- GitLab From 795ca6e29aff7589a5333c3e49168366166d607b Mon Sep 17 00:00:00 2001 From: John Harwell Date: Tue, 19 Mar 2019 09:17:49 -0500 Subject: [PATCH 242/427] Added macro option to reset at end of macro (reload the scene). --- src/GuiQt/WbMacroHelper.cxx | 73 +++++++++++++++++++++++++++ src/GuiQt/WbMacroHelper.h | 3 ++ src/GuiQt/WuQMacroDialog.cxx | 24 +++++++++ src/GuiQt/WuQMacroDialog.h | 4 ++ src/GuiQt/WuQMacroExecutorOptions.cxx | 21 ++++++++ src/GuiQt/WuQMacroExecutorOptions.h | 6 +++ src/GuiQt/WuQMacroHelperInterface.h | 14 +++++ src/GuiQt/WuQMacroManager.cxx | 18 +++++++ 8 files changed, 163 insertions(+) diff --git a/src/GuiQt/WbMacroHelper.cxx b/src/GuiQt/WbMacroHelper.cxx index 87e0e6f8b..fd7b4b4c8 100644 --- a/src/GuiQt/WbMacroHelper.cxx +++ b/src/GuiQt/WbMacroHelper.cxx @@ -42,6 +42,7 @@ #include "SessionManager.h" #include "WbMacroCustomOperationTypeEnum.h" #include "WbMacroCustomDataTypeEnum.h" +#include "WuQMacro.h" #include "WuQMacroCommand.h" #include "WuQMacroCommandParameter.h" #include "WuQMacroExecutorMonitor.h" @@ -269,6 +270,78 @@ WbMacroHelper::macroExecutionEnding(const WuQMacro* /*macro*/, movieRecorder->setRecordingMode(m_savedRecordingMode); EventManager::get()->sendSimpleEvent(EventTypeEnum::EVENT_MOVIE_RECORDING_DIALOG_UPDATE); } + +/** + * Reset the macro to its beginning state + * + * @param macro + * Macro that is run + * @param window + * Widget for parent + * @return + * Pointer to current macro. May be different than the macro + * passsed in. + */ +WuQMacro* +WbMacroHelper::resetMacroStateToBeginning(const WuQMacro* macro, + QWidget* window) +{ + CaretAssert(macro); + + WuQMacro* macroOut = const_cast(macro); + + BrainBrowserWindow* bbw = dynamic_cast(window); + + EventSceneActive sceneEvent(EventSceneActive::MODE_GET); + EventManager::get()->sendEvent(sceneEvent.getPointer()); + Scene* scene = sceneEvent.getScene(); + if (scene != NULL) { + WuQMacroGroup* macroGroup = scene->getMacroGroup(); + CaretAssert(macroGroup); + const QString macroName = macroOut->getName(); + const int32_t macroIndex = macroGroup->getIndexOfMacro(macroOut); + + /* + * Reload scene + */ + SceneFile* invalidSceneFile(NULL); + const bool showSceneDialogFlag(false); + GuiManager::get()->processShowSceneDialogAndScene(bbw, + invalidSceneFile, + scene, + showSceneDialogFlag); + + macroOut = NULL; + + /* + * Find macro that was previously selected + */ + EventManager::get()->sendEvent(sceneEvent.getPointer()); + scene = sceneEvent.getScene(); + if (scene != NULL) { + macroGroup = scene->getMacroGroup(); + CaretAssert(macroGroup); + if ((macroIndex >= 0) + && (macroIndex < macroGroup->getNumberOfMacros())) { + macroOut = macroGroup->getMacroAtIndex(macroIndex); + + if (macroOut->getName() != macroName) { + for (int32_t i = 0; i < macroGroup->getNumberOfMacros(); i++) { + WuQMacro* m = macroGroup->getMacroAtIndex(i); + if (m->getName() == macroName) { + macroOut = m; + break; + } + } + } + } + } + } + + return macroOut; +} + + /** * Called by macro executor just after a command has completed execution * diff --git a/src/GuiQt/WbMacroHelper.h b/src/GuiQt/WbMacroHelper.h index 6adb773d2..5b0c42333 100644 --- a/src/GuiQt/WbMacroHelper.h +++ b/src/GuiQt/WbMacroHelper.h @@ -65,6 +65,9 @@ namespace caret { virtual void macroExecutionEnding(const WuQMacro* macro, QWidget* window, const WuQMacroExecutorOptions* executorOptions) override; + + virtual WuQMacro* resetMacroStateToBeginning(const WuQMacro* macro, + QWidget* window) override; virtual void macroCommandHasCompleted(QWidget* window, const WuQMacroCommand* command, diff --git a/src/GuiQt/WuQMacroDialog.cxx b/src/GuiQt/WuQMacroDialog.cxx index b6f66269f..d975f5c27 100644 --- a/src/GuiQt/WuQMacroDialog.cxx +++ b/src/GuiQt/WuQMacroDialog.cxx @@ -439,9 +439,16 @@ WuQMacroDialog::createRunOptionsWidget() m_runOptionLoopCheckBox = new QCheckBox("Loop"); m_runOptionLoopCheckBox->setChecked(false); + m_runOptionLoopCheckBox->setToolTip("Run macro in a loop until stopped by user"); QObject::connect(m_runOptionLoopCheckBox, &QCheckBox::clicked, this, &WuQMacroDialog::runOptionLoopCheckBoxClicked); + m_runOptionResetAtEndOfMacroCheckBox = new QCheckBox("Reset at end of macro"); + m_runOptionResetAtEndOfMacroCheckBox->setChecked(false); + m_runOptionResetAtEndOfMacroCheckBox->setToolTip("When macro completes, reset to state at beginning of macro"); + QObject::connect(m_runOptionResetAtEndOfMacroCheckBox, &QCheckBox::clicked, + this, &WuQMacroDialog::runOptionResetAtEndOfMacroCheckBoxClicked); + m_runOptionMoveMouseCheckBox = new QCheckBox("Move mouse to to highlight controls"); m_runOptionMoveMouseCheckBox->setChecked(true); m_runOptionMoveMouseCheckBox->setToolTip("As macro runs, the mouse is moved to\n" @@ -478,6 +485,8 @@ WuQMacroDialog::createRunOptionsWidget() row++; runOptionsLayout->addWidget(m_runOptionLoopCheckBox, row, 0, 1, 2, Qt::AlignLeft); row++; + runOptionsLayout->addWidget(m_runOptionResetAtEndOfMacroCheckBox, row, 0, 1, 2, Qt::AlignLeft); + row++; runOptionsLayout->addWidget(m_ignoreDelaysAndDurationsCheckBox, row, 0, 1, 2, Qt::AlignLeft); row++; runOptionsLayout->addWidget(m_runOptionMoveMouseCheckBox, row, 0, 1, 2, Qt::AlignLeft); @@ -518,6 +527,20 @@ WuQMacroDialog::runOptionLoopCheckBoxClicked(bool checked) options->setLooping(checked); } +/** + * Called when run options reset at end of macro checkbox is changed + * + * @param checked + * New checked status. + */ +void +WuQMacroDialog::runOptionResetAtEndOfMacroCheckBoxClicked(bool checked) +{ + WuQMacroExecutorOptions* options = WuQMacroManager::instance()->getExecutorOptions(); + CaretAssert(options); + options->setResetAtEndOfMacro(checked); +} + /** * Called when run options record movie checkbox is changed * @@ -704,6 +727,7 @@ WuQMacroDialog::updateDialogContents() CaretAssert(runOptions); m_runOptionMoveMouseCheckBox->setChecked(runOptions->isShowMouseMovement()); m_runOptionLoopCheckBox->setChecked(runOptions->isLooping()); + m_runOptionResetAtEndOfMacroCheckBox->setChecked(runOptions->isResetAtEndOfMacro()); m_runOptionRecordMovieWhileMacroRunsCheckBox->setChecked(runOptions->isRecordMovieDuringExecution()); m_runOptionStopAfterSelectedCommandCheckBox->setChecked(runOptions->isStopAfterSelectedCommand()); m_ignoreDelaysAndDurationsCheckBox->setChecked(runOptions->isIgnoreDelaysAndDurations()); diff --git a/src/GuiQt/WuQMacroDialog.h b/src/GuiQt/WuQMacroDialog.h index 3abbac43f..65ceb4427 100644 --- a/src/GuiQt/WuQMacroDialog.h +++ b/src/GuiQt/WuQMacroDialog.h @@ -103,6 +103,8 @@ namespace caret { void runOptionLoopCheckBoxClicked(bool); + void runOptionResetAtEndOfMacroCheckBoxClicked(bool); + void runOptionRecordMovieCheckBoxClicked(bool); void runOptionStopAfterSelectedCommandCheckBoxClicked(bool); @@ -235,6 +237,8 @@ namespace caret { QCheckBox* m_runOptionLoopCheckBox; + QCheckBox* m_runOptionResetAtEndOfMacroCheckBox; + QCheckBox* m_runOptionMoveMouseCheckBox; QCheckBox* m_runOptionRecordMovieWhileMacroRunsCheckBox; diff --git a/src/GuiQt/WuQMacroExecutorOptions.cxx b/src/GuiQt/WuQMacroExecutorOptions.cxx index 210d3f4df..1ce4e58e6 100644 --- a/src/GuiQt/WuQMacroExecutorOptions.cxx +++ b/src/GuiQt/WuQMacroExecutorOptions.cxx @@ -87,6 +87,7 @@ void WuQMacroExecutorOptions::copyHelperWuQMacroExecutorOptions(const WuQMacroExecutorOptions& obj) { m_loopingFlag = obj.m_loopingFlag; + m_resetAtEndOfMacroFlag = obj.m_resetAtEndOfMacroFlag; m_stopOnErrorFlag = obj.m_stopOnErrorFlag; m_showMouseMovementFlag = obj.m_showMouseMovementFlag; m_recordMovieDuringExecutionFlag = obj.m_recordMovieDuringExecutionFlag; @@ -167,6 +168,26 @@ WuQMacroExecutorOptions::setLooping(const bool status) m_loopingFlag = status; } +/** + * @return Reset when macro completes. If looping is on, + * reset is performed at end of each loop. + */ +bool +WuQMacroExecutorOptions::isResetAtEndOfMacro() const +{ + return m_resetAtEndOfMacroFlag; +} + +/** + * Set reset when macro completes. If looping is on, + * reset is performed at end of each loop. + */ +void +WuQMacroExecutorOptions::setResetAtEndOfMacro(const bool status) +{ + m_resetAtEndOfMacroFlag = status; +} + /** * @return True if record movie while macro executes is on. */ diff --git a/src/GuiQt/WuQMacroExecutorOptions.h b/src/GuiQt/WuQMacroExecutorOptions.h index 303112a5c..930fe8330 100644 --- a/src/GuiQt/WuQMacroExecutorOptions.h +++ b/src/GuiQt/WuQMacroExecutorOptions.h @@ -54,6 +54,10 @@ namespace caret { void setLooping(const bool status); + bool isResetAtEndOfMacro() const; + + void setResetAtEndOfMacro(const bool status); + bool isRecordMovieDuringExecution() const; void setRecordMovieDuringExecution(const bool status); @@ -79,6 +83,8 @@ namespace caret { bool m_loopingFlag = false; + bool m_resetAtEndOfMacroFlag = false; + bool m_recordMovieDuringExecutionFlag = false; bool m_stopAfterSelectedCommandFlag = false; diff --git a/src/GuiQt/WuQMacroHelperInterface.h b/src/GuiQt/WuQMacroHelperInterface.h index 5070b7e87..d6b102fcb 100644 --- a/src/GuiQt/WuQMacroHelperInterface.h +++ b/src/GuiQt/WuQMacroHelperInterface.h @@ -123,6 +123,20 @@ namespace caret { QWidget* window, const WuQMacroExecutorOptions* executorOptions) = 0; + /** + * Reset the macro to its beginning state + * + * @param macro + * Macro that is run + * @param window + * Widget for parent + * @return + * Pointer to current macro. May be different than the macro + * passsed in. + */ + virtual WuQMacro* resetMacroStateToBeginning(const WuQMacro* macro, + QWidget* window) = 0; + /** * Called by macro executor just after a command has completed execution * diff --git a/src/GuiQt/WuQMacroManager.cxx b/src/GuiQt/WuQMacroManager.cxx index e57eae143..7448a3631 100644 --- a/src/GuiQt/WuQMacroManager.cxx +++ b/src/GuiQt/WuQMacroManager.cxx @@ -703,6 +703,24 @@ WuQMacroManager::runMacro(QWidget* widget, delete m_macroExecutor; m_macroExecutor = NULL; locker.unlock(); + + /* + * May want to reset state to before macro was run + */ + if (loopFlag) { + if (m_executorOptions->isResetAtEndOfMacro()) { + if (m_macroHelper != NULL) { + WuQMacro* newMacro = m_macroHelper->resetMacroStateToBeginning(macro, + widget); + if (newMacro != NULL) { + macro = newMacro; + } + else { + loopFlag = false; + } + } + } + } } return macro; -- GitLab From 22ffdcad315f01164e01dd3f39980ef97367be52 Mon Sep 17 00:00:00 2001 From: John Harwell Date: Wed, 20 Mar 2019 11:23:26 -0500 Subject: [PATCH 243/427] Implement "Reload Scene" button on Macros Dialog. --- src/GuiQt/WuQMacroDialog.cxx | 125 +++++++++++++++++++++++++++------- src/GuiQt/WuQMacroDialog.h | 7 ++ src/GuiQt/WuQMacroManager.cxx | 23 ++++++- src/GuiQt/WuQMacroManager.h | 3 + 4 files changed, 133 insertions(+), 25 deletions(-) diff --git a/src/GuiQt/WuQMacroDialog.cxx b/src/GuiQt/WuQMacroDialog.cxx index d975f5c27..e252cb097 100644 --- a/src/GuiQt/WuQMacroDialog.cxx +++ b/src/GuiQt/WuQMacroDialog.cxx @@ -92,25 +92,43 @@ WuQMacroDialog::WuQMacroDialog(QWidget* parent) QObject::connect(m_macroGroupComboBox, static_cast(&QComboBox::activated), this, &WuQMacroDialog::macroGroupComboBoxActivated); + m_resetMacroGroupToolButton = new QToolButton(); + m_resetMacroGroupToolButton->setText(""); + m_resetMacroGroupToolButton->setToolTip("Reload current scene"); + QPixmap resetPixmap = createEditingToolButtonPixmap(m_resetMacroGroupToolButton, + EditButton::RESET); + m_resetMacroGroupToolButton->setIcon(resetPixmap); + QObject::connect(m_resetMacroGroupToolButton, &QToolButton::clicked, + this, &WuQMacroDialog::macroGroupResetToolButtonClicked); + m_macroGroupToolButton = new QToolButton(); m_macroGroupToolButton->setText("..."); m_macroGroupToolButton->setToolTip("Export and import macro groups"); QObject::connect(m_macroGroupToolButton, &QToolButton::clicked, this, &WuQMacroDialog::macroGroupToolButtonClicked); + QSize buttonSize(std::max(m_resetMacroGroupToolButton->sizeHint().width(), + m_macroGroupToolButton->sizeHint().width()), + std::max(m_resetMacroGroupToolButton->sizeHint().height(), + m_macroGroupToolButton->sizeHint().height())); + m_resetMacroGroupToolButton->setFixedSize(buttonSize); + m_macroGroupToolButton->setFixedSize(buttonSize); + QGridLayout* gridLayout = new QGridLayout(); gridLayout->setContentsMargins(0, 0, 0, 0); gridLayout->setColumnStretch(0, 0); gridLayout->setColumnStretch(1, 100); gridLayout->setColumnStretch(2, 0); + gridLayout->setColumnStretch(3, 0); int row = 0; gridLayout->addWidget(macroGroupLabel, row, 0); gridLayout->addWidget(m_macroGroupComboBox, row, 1); - gridLayout->addWidget(m_macroGroupToolButton, row, 2); + gridLayout->addWidget(m_resetMacroGroupToolButton, row, 2); + gridLayout->addWidget(m_macroGroupToolButton, row, 3); row++; - gridLayout->addWidget(createHorizontalLine(), row, 0, 1, 3); + gridLayout->addWidget(createHorizontalLine(), row, 0, 1, 4); row++; - gridLayout->addWidget(createMacroRunAndEditingToolButtons(), row, 0, 1, 3); + gridLayout->addWidget(createMacroRunAndEditingToolButtons(), row, 0, 1, 4); row++; for (int32_t iRow = 0; iRow < gridLayout->rowCount(); iRow++) { @@ -443,7 +461,7 @@ WuQMacroDialog::createRunOptionsWidget() QObject::connect(m_runOptionLoopCheckBox, &QCheckBox::clicked, this, &WuQMacroDialog::runOptionLoopCheckBoxClicked); - m_runOptionResetAtEndOfMacroCheckBox = new QCheckBox("Reset at end of macro"); + m_runOptionResetAtEndOfMacroCheckBox = new QCheckBox("Reload scene at end of macro"); m_runOptionResetAtEndOfMacroCheckBox->setChecked(false); m_runOptionResetAtEndOfMacroCheckBox->setToolTip("When macro completes, reset to state at beginning of macro"); QObject::connect(m_runOptionResetAtEndOfMacroCheckBox, &QCheckBox::clicked, @@ -1266,23 +1284,8 @@ WuQMacroDialog::runMacroToolButtonClicked() return; } - const QString windowID = m_runOptionsWindowComboBox->currentText(); - QWidget* window = WuQMacroManager::instance()->getMainWindowWithIdentifier(windowID); - if (window == NULL) { - window = this; - while (window != NULL) { - if (qobject_cast(window) != NULL) { - break; - } - else { - window = window->parentWidget(); - } - } - if (window == NULL) { - window = parentWidget(); - } - } - + QWidget* window = getWindow(); + /* * While macro runs, edit buttons are disabled */ @@ -1302,6 +1305,32 @@ WuQMacroDialog::runMacroToolButtonClicked() updateEditingToolButtons(); } +/** + * @return the parent main window + */ +QWidget* +WuQMacroDialog::getWindow() +{ + const QString windowID = m_runOptionsWindowComboBox->currentText(); + QWidget* window = WuQMacroManager::instance()->getMainWindowWithIdentifier(windowID); + if (window == NULL) { + window = this; + while (window != NULL) { + if (qobject_cast(window) != NULL) { + break; + } + else { + window = window->parentWidget(); + } + } + if (window == NULL) { + window = parentWidget(); + } + } + + return window; +} + /** * Called when stop button is clicked */ @@ -1454,6 +1483,21 @@ WuQMacroDialog::macroGroupToolButtonClicked() delete menu; } +/** + * Called when macro group reset tool button is clicked + */ +void +WuQMacroDialog::macroGroupResetToolButtonClicked() +{ + WuQMacro* macro = WuQMacroManager::instance()->resetMacro(getWindow(), + getSelectedMacro()); + updateDialogContents(); + if (macro != NULL) { + m_treeView->setCurrentIndex(macro->index()); + treeItemSelected(macro->index()); + } +} + /** * Called when editing move up button clicked */ @@ -1984,13 +2028,15 @@ WuQMacroDialog::createEditingToolButtonPixmap(const QWidget* widget, case EditButton::PAUSE: break; case EditButton::RECORD_OFF: - /* allow background */ - //pixmapOptions = 0; break; case EditButton::RECORD_ON: /* allow background */ pixmapOptions = 0; break; + case EditButton::RESET: + /* allow background */ + pixmapOptions = 0; + break; case EditButton::RUN: /* allow background */ pixmapOptions = 0; @@ -2069,6 +2115,39 @@ WuQMacroDialog::createEditingToolButtonPixmap(const QWidget* widget, maxValue, maxValue); } break; + case EditButton::RESET: + { + QPen pen = painter->pen(); + pen.setWidth(2); + painter->setPen(pen); + /* + * From drawArc() documentation + * Zero is at 3 o'clock + * Units are 1/16th of a degree + * Positive is counter-clockwise + * BUT perhaps due to us transforming coordinate system positive is clockwise + */ + const int sz(4); + const int widthHeight(maxValue * 2 - sz); + QRect rectangle(-maxValue, -maxValue, widthHeight, widthHeight); + const int startAngle(0); + const int spanAngle(270 * 16); + painter->drawArc(rectangle, + startAngle, + spanAngle); + + const int ts(sz + 2); + const int x(-maxValue + widthHeight/2); + const int y(-maxValue + widthHeight); + QPoint trianglePoints[3] = { + { x, y + ts }, + { x + ts, y }, + { x, y - ts } + }; + painter->setBrush(pen.color()); + painter->drawPolygon(trianglePoints, 3); + } + break; case EditButton::RUN: { /* diff --git a/src/GuiQt/WuQMacroDialog.h b/src/GuiQt/WuQMacroDialog.h index 65ceb4427..6cffcd723 100644 --- a/src/GuiQt/WuQMacroDialog.h +++ b/src/GuiQt/WuQMacroDialog.h @@ -93,6 +93,8 @@ namespace caret { void macroGroupToolButtonClicked(); + void macroGroupResetToolButtonClicked(); + void macroNameLineEditTextEdited(const QString& text); void macroDescriptionTextEditChanged(); @@ -166,6 +168,7 @@ namespace caret { PAUSE, RECORD_OFF, RECORD_ON, + RESET, RUN, STOP }; @@ -205,10 +208,14 @@ namespace caret { void insertNewMacro(WuQMacro* macro); + QWidget* getWindow(); + std::vector m_macroGroups; QComboBox* m_macroGroupComboBox; + QToolButton* m_resetMacroGroupToolButton; + QToolButton* m_macroGroupToolButton; QTreeView* m_treeView; diff --git a/src/GuiQt/WuQMacroManager.cxx b/src/GuiQt/WuQMacroManager.cxx index 7448a3631..aa282395b 100644 --- a/src/GuiQt/WuQMacroManager.cxx +++ b/src/GuiQt/WuQMacroManager.cxx @@ -710,8 +710,7 @@ WuQMacroManager::runMacro(QWidget* widget, if (loopFlag) { if (m_executorOptions->isResetAtEndOfMacro()) { if (m_macroHelper != NULL) { - WuQMacro* newMacro = m_macroHelper->resetMacroStateToBeginning(macro, - widget); + WuQMacro* newMacro = resetMacro(widget, macro); if (newMacro != NULL) { macro = newMacro; } @@ -803,6 +802,26 @@ WuQMacroManager::macroCommandStartingExecution(QWidget* window, } } +/** + * Reset the given macro + * + * @param parentg + * Parent for any dialogs + * @param macro + * Macro that is reset to beginning state + */ +WuQMacro* +WuQMacroManager::resetMacro(QWidget* parent, + WuQMacro* macro) +{ + WuQMacro* newMacro(macro); + if (m_macroHelper != NULL) { + newMacro = m_macroHelper->resetMacroStateToBeginning(macro, + parent); + } + return newMacro; +} + /** * Add a parent object that will be searched during macro * execution to find objects by name that are contained diff --git a/src/GuiQt/WuQMacroManager.h b/src/GuiQt/WuQMacroManager.h index b69ae32e4..3fca46537 100644 --- a/src/GuiQt/WuQMacroManager.h +++ b/src/GuiQt/WuQMacroManager.h @@ -131,6 +131,9 @@ namespace caret { const WuQMacro* macro, const WuQMacroCommand* macroCommandToStopAfter); + WuQMacro* resetMacro(QWidget* parent, + WuQMacro* macro); + void stopMacro(); void pauseContinueMacro(); -- GitLab From 164a77ce78e3d1d5fe0a4bd93c4955d2639ed19a Mon Sep 17 00:00:00 2001 From: John Harwell Date: Wed, 20 Mar 2019 11:30:36 -0500 Subject: [PATCH 244/427] Removed pop-up menus from movie and scene tool buttons in main window toolbar. --- src/GuiQt/BrainBrowserWindowToolBar.cxx | 78 ------------------------- src/GuiQt/BrainBrowserWindowToolBar.h | 8 --- 2 files changed, 86 deletions(-) diff --git a/src/GuiQt/BrainBrowserWindowToolBar.cxx b/src/GuiQt/BrainBrowserWindowToolBar.cxx index 1b71e1a7b..a9a524456 100644 --- a/src/GuiQt/BrainBrowserWindowToolBar.cxx +++ b/src/GuiQt/BrainBrowserWindowToolBar.cxx @@ -108,7 +108,6 @@ #include "ModelTransform.h" #include "ModelVolume.h" #include "ModelWholeBrain.h" -#include "MovieRecordingDialog.h" #include "OverlaySet.h" #include "Scene.h" #include "SceneAttributes.h" @@ -294,27 +293,14 @@ BrainBrowserWindowToolBar::BrainBrowserWindowToolBar(const int32_t browserWindow "Arrow displays a menu for loading scenes " "including reloading or the current scene."); QToolButton* sceneDialogToolButton = new QToolButton(); - sceneDialogToolButton->setPopupMode(QToolButton::MenuButtonPopup); sceneDialogToolButton->setText(""); sceneDialogToolButton->setIcon(GuiManager::get()->getSceneDialogDisplayAction()->icon()); sceneDialogToolButton->setDefaultAction(GuiManager::get()->getSceneDialogDisplayAction()); - sceneDialogToolButton->setToolButtonStyle(Qt::ToolButtonTextBesideIcon); WuQtUtilities::setWordWrappedToolTip(sceneDialogToolButton, sceneButtonToolTip); QObject::connect(sceneDialogToolButton, &QToolButton::clicked, this, &BrainBrowserWindowToolBar::sceneToolButtonClicked); - /* - * The scene dialog tool button menu must be created AFTER the tool button. Seems weird. - * https://forums.autodesk.com/t5/maya-forum/maya-2017-and-qtoolbutton-popup-menu-not-showing/td-p/6927575 - */ - m_sceneToolButtonMenu = new QMenu(sceneDialogToolButton); - QObject::connect(m_sceneToolButtonMenu, &QMenu::triggered, - this, &BrainBrowserWindowToolBar::sceneToolButtonMenuTriggered); - QObject::connect(m_sceneToolButtonMenu, &QMenu::aboutToShow, - this, &BrainBrowserWindowToolBar::sceneToolButtonMenuAboutToShow); - sceneDialogToolButton->setMenu(m_sceneToolButtonMenu); - /* * Movie button */ @@ -330,18 +316,11 @@ BrainBrowserWindowToolBar::BrainBrowserWindowToolBar(const int32_t browserWindow m_movieToolButton = new QToolButton(); WuQtUtilities::setWordWrappedToolTip(m_movieToolButton, movieButtonToolTip); - m_movieToolButton->setPopupMode(QToolButton::MenuButtonPopup); m_movieToolButton->setText(movieButtonText); m_movieToolButton->setIcon(movieIcon); - m_movieToolButton->setToolButtonStyle(Qt::ToolButtonTextBesideIcon); QObject::connect(m_movieToolButton, &QToolButton::clicked, parentBrainBrowserWindow, &BrainBrowserWindow::processMovieRecording); - QMenu* movieToolButtonMenu = new QMenu(); - movieToolButtonMenu->addAction("Create Movie...", - this, &BrainBrowserWindowToolBar::createMovieToolButtonMenuItemTriggered); - m_movieToolButton->setMenu(movieToolButtonMenu); - /* * Macros button */ @@ -4090,63 +4069,6 @@ BrainBrowserWindowToolBar::sceneToolButtonClicked() GuiManager::get()->getSceneDialogDisplayAction()->trigger(); } -/** - * Called when an item is selected from the scene tool button menu - * - * @param action - * Action of menu selected - */ -void -BrainBrowserWindowToolBar::sceneToolButtonMenuTriggered(QAction* action) -{ - if (action != NULL) { - void* scenePointer = action->data().value(); - if (scenePointer != NULL) { - BrainBrowserWindow* bbw = GuiManager::get()->getBrowserWindowByWindowIndex(browserWindowIndex); - CaretAssert(bbw); - - Scene* scene = (Scene*)scenePointer; - SceneFile* invalidSceneFile(NULL); - const bool showSceneDialogFlag(false); - GuiManager::get()->processShowSceneDialogAndScene(bbw, - invalidSceneFile, - scene, - showSceneDialogFlag); - } - } -} - -/** - * Called when the scene menu is about to show - */ -void -BrainBrowserWindowToolBar::sceneToolButtonMenuAboutToShow() -{ - m_sceneToolButtonMenu->clear(); - - EventSceneActive sceneEvent(EventSceneActive::MODE_GET); - EventManager::get()->sendEvent(sceneEvent.getPointer()); - Scene* activeScene = sceneEvent.getScene(); - if (activeScene != NULL) { - QString name(activeScene->getName().trimmed()); - if (name.isEmpty()) { - name = ""; - } - QAction* action = m_sceneToolButtonMenu->addAction("Reload Current Scene: " - + name); - action->setData(qVariantFromValue((void*)activeScene)); - } -} - -/** - * Called create movie menu item is selected from movie tool button - */ -void -BrainBrowserWindowToolBar::createMovieToolButtonMenuItemTriggered() -{ - MovieRecordingDialog::createMovie(m_movieToolButton); -} - /** * Called when custom view is triggered and displays Custom View Menu. */ diff --git a/src/GuiQt/BrainBrowserWindowToolBar.h b/src/GuiQt/BrainBrowserWindowToolBar.h index 8a1ca8499..ed635a07e 100644 --- a/src/GuiQt/BrainBrowserWindowToolBar.h +++ b/src/GuiQt/BrainBrowserWindowToolBar.h @@ -297,14 +297,8 @@ namespace caret { void customViewActionTriggered(); - void sceneToolButtonMenuTriggered(QAction* action); - - void sceneToolButtonMenuAboutToShow(); - void sceneToolButtonClicked(); - void createMovieToolButtonMenuItemTriggered(); - private: QAction* orientationLateralMedialToolButtonAction; QAction* orientationDorsalVentralToolButtonAction; @@ -342,8 +336,6 @@ namespace caret { QIcon* viewOrientationRightLateralIcon; QIcon* viewOrientationRightMedialIcon; - QMenu* m_sceneToolButtonMenu = NULL; - QToolButton* m_movieToolButton = NULL; private slots: -- GitLab From a8ef79d636277cab48ddea7cd48f839b79d1e9e3 Mon Sep 17 00:00:00 2001 From: John Harwell Date: Wed, 20 Mar 2019 11:38:03 -0500 Subject: [PATCH 245/427] Removed "run to selected command" checkbox option from Macros dialog. It is easier to right-click a command in the tree view and selected "run to this command" from the pop-up menu. --- src/GuiQt/WuQMacroDialog.cxx | 24 ------------------------ src/GuiQt/WuQMacroDialog.h | 4 ---- 2 files changed, 28 deletions(-) diff --git a/src/GuiQt/WuQMacroDialog.cxx b/src/GuiQt/WuQMacroDialog.cxx index e252cb097..530808fc6 100644 --- a/src/GuiQt/WuQMacroDialog.cxx +++ b/src/GuiQt/WuQMacroDialog.cxx @@ -480,13 +480,6 @@ WuQMacroDialog::createRunOptionsWidget() QObject::connect(m_runOptionRecordMovieWhileMacroRunsCheckBox, &QCheckBox::clicked, this, &WuQMacroDialog::runOptionRecordMovieCheckBoxClicked); - m_runOptionStopAfterSelectedCommandCheckBox = new QCheckBox("Stop after selected command"); - const QString stopToolTip("Stop execution of macro after the selected command. One can also right-click " - "over a command to Run the macro and stop after the command."); - m_runOptionStopAfterSelectedCommandCheckBox->setToolTip(QTextDocument(stopToolTip).toHtml()); - QObject::connect(m_runOptionStopAfterSelectedCommandCheckBox, &QCheckBox::clicked, - this, &WuQMacroDialog::runOptionStopAfterSelectedCommandCheckBoxClicked); - m_ignoreDelaysAndDurationsCheckBox = new QCheckBox("Ignore delays and durations"); const QString ignoreToolTip("Ignore delays and durations and minimize iterations " "to quickly execute macro (for debugging)"); @@ -509,8 +502,6 @@ WuQMacroDialog::createRunOptionsWidget() row++; runOptionsLayout->addWidget(m_runOptionMoveMouseCheckBox, row, 0, 1, 2, Qt::AlignLeft); row++; - runOptionsLayout->addWidget(m_runOptionStopAfterSelectedCommandCheckBox, row, 0, 1, 2, Qt::AlignLeft); - row++; runOptionsLayout->addWidget(m_runOptionRecordMovieWhileMacroRunsCheckBox, row, 0, 1, 2, Qt::AlignLeft); row++; @@ -573,20 +564,6 @@ WuQMacroDialog::runOptionRecordMovieCheckBoxClicked(bool checked) options->setRecordMovieDuringExecution(checked); } -/** - * Called when run options stop after selected command checkbox is changed - * - * @param checked - * New checked status. - */ -void -WuQMacroDialog::runOptionStopAfterSelectedCommandCheckBoxClicked(bool checked) -{ - WuQMacroExecutorOptions* options = WuQMacroManager::instance()->getExecutorOptions(); - CaretAssert(options); - options->setStopAfterSelectedCommand(checked); -} - /** * Called when run options ignore delays and durations checkbox is changed * @@ -747,7 +724,6 @@ WuQMacroDialog::updateDialogContents() m_runOptionLoopCheckBox->setChecked(runOptions->isLooping()); m_runOptionResetAtEndOfMacroCheckBox->setChecked(runOptions->isResetAtEndOfMacro()); m_runOptionRecordMovieWhileMacroRunsCheckBox->setChecked(runOptions->isRecordMovieDuringExecution()); - m_runOptionStopAfterSelectedCommandCheckBox->setChecked(runOptions->isStopAfterSelectedCommand()); m_ignoreDelaysAndDurationsCheckBox->setChecked(runOptions->isIgnoreDelaysAndDurations()); macroGroupComboBoxActivated(selectedIndex); diff --git a/src/GuiQt/WuQMacroDialog.h b/src/GuiQt/WuQMacroDialog.h index 6cffcd723..87fed661f 100644 --- a/src/GuiQt/WuQMacroDialog.h +++ b/src/GuiQt/WuQMacroDialog.h @@ -109,8 +109,6 @@ namespace caret { void runOptionRecordMovieCheckBoxClicked(bool); - void runOptionStopAfterSelectedCommandCheckBoxClicked(bool); - void runOptionIgnoreDelaysAndDurationsCheckBoxClicked(bool); void editingMoveUpToolButtonClicked(); @@ -250,8 +248,6 @@ namespace caret { QCheckBox* m_runOptionRecordMovieWhileMacroRunsCheckBox; - QCheckBox* m_runOptionStopAfterSelectedCommandCheckBox; - QCheckBox* m_ignoreDelaysAndDurationsCheckBox; QLabel* m_commandTitleLabel; -- GitLab From d2ae4b6f2539e64d81370890572176d2012f4afa Mon Sep 17 00:00:00 2001 From: John Harwell Date: Wed, 20 Mar 2019 12:02:06 -0500 Subject: [PATCH 246/427] Removed checkbox option "Reload scene at end of macro" and instead, always reload the scene when running a macro in loop mode. --- src/GuiQt/WuQMacroDialog.cxx | 23 ----------------------- src/GuiQt/WuQMacroDialog.h | 4 ---- src/GuiQt/WuQMacroExecutorOptions.cxx | 21 --------------------- src/GuiQt/WuQMacroExecutorOptions.h | 6 ------ src/GuiQt/WuQMacroManager.cxx | 19 +++++++------------ 5 files changed, 7 insertions(+), 66 deletions(-) diff --git a/src/GuiQt/WuQMacroDialog.cxx b/src/GuiQt/WuQMacroDialog.cxx index 530808fc6..53f97ad80 100644 --- a/src/GuiQt/WuQMacroDialog.cxx +++ b/src/GuiQt/WuQMacroDialog.cxx @@ -461,12 +461,6 @@ WuQMacroDialog::createRunOptionsWidget() QObject::connect(m_runOptionLoopCheckBox, &QCheckBox::clicked, this, &WuQMacroDialog::runOptionLoopCheckBoxClicked); - m_runOptionResetAtEndOfMacroCheckBox = new QCheckBox("Reload scene at end of macro"); - m_runOptionResetAtEndOfMacroCheckBox->setChecked(false); - m_runOptionResetAtEndOfMacroCheckBox->setToolTip("When macro completes, reset to state at beginning of macro"); - QObject::connect(m_runOptionResetAtEndOfMacroCheckBox, &QCheckBox::clicked, - this, &WuQMacroDialog::runOptionResetAtEndOfMacroCheckBoxClicked); - m_runOptionMoveMouseCheckBox = new QCheckBox("Move mouse to to highlight controls"); m_runOptionMoveMouseCheckBox->setChecked(true); m_runOptionMoveMouseCheckBox->setToolTip("As macro runs, the mouse is moved to\n" @@ -496,8 +490,6 @@ WuQMacroDialog::createRunOptionsWidget() row++; runOptionsLayout->addWidget(m_runOptionLoopCheckBox, row, 0, 1, 2, Qt::AlignLeft); row++; - runOptionsLayout->addWidget(m_runOptionResetAtEndOfMacroCheckBox, row, 0, 1, 2, Qt::AlignLeft); - row++; runOptionsLayout->addWidget(m_ignoreDelaysAndDurationsCheckBox, row, 0, 1, 2, Qt::AlignLeft); row++; runOptionsLayout->addWidget(m_runOptionMoveMouseCheckBox, row, 0, 1, 2, Qt::AlignLeft); @@ -536,20 +528,6 @@ WuQMacroDialog::runOptionLoopCheckBoxClicked(bool checked) options->setLooping(checked); } -/** - * Called when run options reset at end of macro checkbox is changed - * - * @param checked - * New checked status. - */ -void -WuQMacroDialog::runOptionResetAtEndOfMacroCheckBoxClicked(bool checked) -{ - WuQMacroExecutorOptions* options = WuQMacroManager::instance()->getExecutorOptions(); - CaretAssert(options); - options->setResetAtEndOfMacro(checked); -} - /** * Called when run options record movie checkbox is changed * @@ -722,7 +700,6 @@ WuQMacroDialog::updateDialogContents() CaretAssert(runOptions); m_runOptionMoveMouseCheckBox->setChecked(runOptions->isShowMouseMovement()); m_runOptionLoopCheckBox->setChecked(runOptions->isLooping()); - m_runOptionResetAtEndOfMacroCheckBox->setChecked(runOptions->isResetAtEndOfMacro()); m_runOptionRecordMovieWhileMacroRunsCheckBox->setChecked(runOptions->isRecordMovieDuringExecution()); m_ignoreDelaysAndDurationsCheckBox->setChecked(runOptions->isIgnoreDelaysAndDurations()); diff --git a/src/GuiQt/WuQMacroDialog.h b/src/GuiQt/WuQMacroDialog.h index 87fed661f..c83d970ec 100644 --- a/src/GuiQt/WuQMacroDialog.h +++ b/src/GuiQt/WuQMacroDialog.h @@ -105,8 +105,6 @@ namespace caret { void runOptionLoopCheckBoxClicked(bool); - void runOptionResetAtEndOfMacroCheckBoxClicked(bool); - void runOptionRecordMovieCheckBoxClicked(bool); void runOptionIgnoreDelaysAndDurationsCheckBoxClicked(bool); @@ -242,8 +240,6 @@ namespace caret { QCheckBox* m_runOptionLoopCheckBox; - QCheckBox* m_runOptionResetAtEndOfMacroCheckBox; - QCheckBox* m_runOptionMoveMouseCheckBox; QCheckBox* m_runOptionRecordMovieWhileMacroRunsCheckBox; diff --git a/src/GuiQt/WuQMacroExecutorOptions.cxx b/src/GuiQt/WuQMacroExecutorOptions.cxx index 1ce4e58e6..210d3f4df 100644 --- a/src/GuiQt/WuQMacroExecutorOptions.cxx +++ b/src/GuiQt/WuQMacroExecutorOptions.cxx @@ -87,7 +87,6 @@ void WuQMacroExecutorOptions::copyHelperWuQMacroExecutorOptions(const WuQMacroExecutorOptions& obj) { m_loopingFlag = obj.m_loopingFlag; - m_resetAtEndOfMacroFlag = obj.m_resetAtEndOfMacroFlag; m_stopOnErrorFlag = obj.m_stopOnErrorFlag; m_showMouseMovementFlag = obj.m_showMouseMovementFlag; m_recordMovieDuringExecutionFlag = obj.m_recordMovieDuringExecutionFlag; @@ -168,26 +167,6 @@ WuQMacroExecutorOptions::setLooping(const bool status) m_loopingFlag = status; } -/** - * @return Reset when macro completes. If looping is on, - * reset is performed at end of each loop. - */ -bool -WuQMacroExecutorOptions::isResetAtEndOfMacro() const -{ - return m_resetAtEndOfMacroFlag; -} - -/** - * Set reset when macro completes. If looping is on, - * reset is performed at end of each loop. - */ -void -WuQMacroExecutorOptions::setResetAtEndOfMacro(const bool status) -{ - m_resetAtEndOfMacroFlag = status; -} - /** * @return True if record movie while macro executes is on. */ diff --git a/src/GuiQt/WuQMacroExecutorOptions.h b/src/GuiQt/WuQMacroExecutorOptions.h index 930fe8330..303112a5c 100644 --- a/src/GuiQt/WuQMacroExecutorOptions.h +++ b/src/GuiQt/WuQMacroExecutorOptions.h @@ -54,10 +54,6 @@ namespace caret { void setLooping(const bool status); - bool isResetAtEndOfMacro() const; - - void setResetAtEndOfMacro(const bool status); - bool isRecordMovieDuringExecution() const; void setRecordMovieDuringExecution(const bool status); @@ -83,8 +79,6 @@ namespace caret { bool m_loopingFlag = false; - bool m_resetAtEndOfMacroFlag = false; - bool m_recordMovieDuringExecutionFlag = false; bool m_stopAfterSelectedCommandFlag = false; diff --git a/src/GuiQt/WuQMacroManager.cxx b/src/GuiQt/WuQMacroManager.cxx index aa282395b..97c3e0184 100644 --- a/src/GuiQt/WuQMacroManager.cxx +++ b/src/GuiQt/WuQMacroManager.cxx @@ -704,19 +704,14 @@ WuQMacroManager::runMacro(QWidget* widget, m_macroExecutor = NULL; locker.unlock(); - /* - * May want to reset state to before macro was run - */ if (loopFlag) { - if (m_executorOptions->isResetAtEndOfMacro()) { - if (m_macroHelper != NULL) { - WuQMacro* newMacro = resetMacro(widget, macro); - if (newMacro != NULL) { - macro = newMacro; - } - else { - loopFlag = false; - } + if (m_macroHelper != NULL) { + WuQMacro* newMacro = resetMacro(widget, macro); + if (newMacro != NULL) { + macro = newMacro; + } + else { + loopFlag = false; } } } -- GitLab From 3d87463297bd7bccbe82ad2ad7da161bb0a43860 Mon Sep 17 00:00:00 2001 From: John Harwell Date: Wed, 20 Mar 2019 14:10:55 -0500 Subject: [PATCH 247/427] When a macro is running in loop mode: Do not show scene dialog; show a message box while the scene is reloading. --- src/GuiQt/GuiManager.cxx | 11 ++++------- src/GuiQt/WbMacroHelper.cxx | 3 +++ src/GuiQt/WuQMacroManager.cxx | 16 ++++++++++++++-- 3 files changed, 21 insertions(+), 9 deletions(-) diff --git a/src/GuiQt/GuiManager.cxx b/src/GuiQt/GuiManager.cxx index 76fb393f5..acbd9ad96 100644 --- a/src/GuiQt/GuiManager.cxx +++ b/src/GuiQt/GuiManager.cxx @@ -1951,13 +1951,10 @@ GuiManager::processShowSceneDialogAndScene(BrainBrowserWindow* browserWindow, const bool updateSceneDialogFlag(showSceneDialogFlag || (this->sceneDialog != NULL)); if (updateSceneDialogFlag) { - showHideSceneDialog(true, - browserWindow); - - const bool sceneWasDisplayed = this->sceneDialog->displayScene(sceneFile, - scene); - if (sceneWasDisplayed) { - showHideSceneDialog(false, + this->sceneDialog->displayScene(sceneFile, + scene); + if (showSceneDialogFlag) { + showHideSceneDialog(true, NULL); } } diff --git a/src/GuiQt/WbMacroHelper.cxx b/src/GuiQt/WbMacroHelper.cxx index fd7b4b4c8..dd9798558 100644 --- a/src/GuiQt/WbMacroHelper.cxx +++ b/src/GuiQt/WbMacroHelper.cxx @@ -311,6 +311,9 @@ WbMacroHelper::resetMacroStateToBeginning(const WuQMacro* macro, scene, showSceneDialogFlag); + EventManager::get()->sendEvent(EventUserInterfaceUpdate().getPointer()); + EventManager::get()->sendEvent(EventGraphicsUpdateAllWindows().getPointer()); + macroOut = NULL; /* diff --git a/src/GuiQt/WuQMacroManager.cxx b/src/GuiQt/WuQMacroManager.cxx index 97c3e0184..7efc771e6 100644 --- a/src/GuiQt/WuQMacroManager.cxx +++ b/src/GuiQt/WuQMacroManager.cxx @@ -25,6 +25,7 @@ #include +#include #include #include #include @@ -687,8 +688,8 @@ WuQMacroManager::runMacro(QWidget* widget, m_macroExecutorMonitor->setMode(WuQMacroExecutorMonitor::Mode::STOP); if ( ! resultFlag) { - QMessageBox::critical(widget, - "Run Macro Error", + QMessageBox::information(widget, + "Macro Done", errorMessage, QMessageBox::Ok, QMessageBox::NoButton); @@ -706,6 +707,14 @@ WuQMacroManager::runMacro(QWidget* widget, if (loopFlag) { if (m_macroHelper != NULL) { + QMessageBox* msgBox = new QMessageBox(QMessageBox::Information, + "Wait", + "Reloading scene", + QMessageBox::Ok, + widget); + msgBox->button(QMessageBox::Ok)->setVisible(false); + msgBox->show(); + WuQMacro* newMacro = resetMacro(widget, macro); if (newMacro != NULL) { macro = newMacro; @@ -713,6 +722,9 @@ WuQMacroManager::runMacro(QWidget* widget, else { loopFlag = false; } + + msgBox->hide(); + msgBox->deleteLater(); } } } -- GitLab From 3fc18b1c0ded49aa30cf8986ae4818ee56997388 Mon Sep 17 00:00:00 2001 From: John Harwell Date: Wed, 20 Mar 2019 14:43:26 -0500 Subject: [PATCH 248/427] Disable record and reset buttons on macro dialog while a macro is running --- src/GuiQt/WuQMacroDialog.cxx | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/src/GuiQt/WuQMacroDialog.cxx b/src/GuiQt/WuQMacroDialog.cxx index 53f97ad80..c61182ca3 100644 --- a/src/GuiQt/WuQMacroDialog.cxx +++ b/src/GuiQt/WuQMacroDialog.cxx @@ -1870,6 +1870,7 @@ WuQMacroDialog::updateEditingToolButtons() WuQMacro* macro = getSelectedMacro(); WuQMacroCommand* command = getSelectedMacroCommand(); + bool resetValid(false); bool runValid(false); bool stopValid(false); bool insertValid(false); @@ -1878,6 +1879,7 @@ WuQMacroDialog::updateEditingToolButtons() bool moveDownValid(false); bool pauseValid(false); bool pauseChecked(false); + bool recordValid(false); if (m_macroIsRunningFlag) { pauseValid = true; @@ -1894,6 +1896,8 @@ WuQMacroDialog::updateEditingToolButtons() } else { insertValid = (macroGroup != NULL); + recordValid = (macroGroup != NULL); + resetValid = (macroGroup != NULL); switch (getSelectedItemType()) { case WuQMacroStandardItemTypeEnum::INVALID: @@ -1926,14 +1930,17 @@ WuQMacroDialog::updateEditingToolButtons() m_pauseMacroToolButton->setEnabled(pauseValid); m_pauseMacroToolButton->setChecked(pauseChecked); + m_resetMacroGroupToolButton->setEnabled(resetValid); + m_macroGroupToolButton->setEnabled(resetValid); m_runMacroToolButton->setEnabled(runValid); m_stopMacroToolButton->setEnabled(stopValid); + m_recordMacroToolButton->setEnabled(recordValid); m_editingDeleteToolButton->setEnabled(deleteValid); m_editingInsertToolButton->setEnabled(insertValid); m_editingMoveDownToolButton->setEnabled(moveDownValid); m_editingMoveUpToolButton->setEnabled(moveUpValid); - m_recordMacroToolButton->setEnabled(true); +// m_recordMacroToolButton->setEnabled(true); switch (WuQMacroManager::instance()->getMode()) { case WuQMacroModeEnum::OFF: m_recordMacroToolButton->setIcon(m_recordMacroToolButtonIconOff); @@ -1946,7 +1953,7 @@ WuQMacroDialog::updateEditingToolButtons() break; case WuQMacroModeEnum::RUNNING: m_recordMacroToolButton->setIcon(m_recordMacroToolButtonIconOff); - m_recordMacroToolButton->setEnabled(true); +// m_recordMacroToolButton->setEnabled(false); break; } } -- GitLab From 82018c600db2c809bdc09b05c17498d36aba0d5c Mon Sep 17 00:00:00 2001 From: John Harwell Date: Wed, 20 Mar 2019 15:13:53 -0500 Subject: [PATCH 249/427] While a macro is executing, select the executing command in the macros dialog. --- src/GuiQt/WuQMacroDialog.cxx | 31 +++++++++++++++++++++++++++++-- src/GuiQt/WuQMacroDialog.h | 4 ++++ src/GuiQt/WuQMacroExecutor.cxx | 6 ++++++ src/GuiQt/WuQMacroExecutor.h | 4 ++++ src/GuiQt/WuQMacroManager.cxx | 5 +++++ 5 files changed, 48 insertions(+), 2 deletions(-) diff --git a/src/GuiQt/WuQMacroDialog.cxx b/src/GuiQt/WuQMacroDialog.cxx index c61182ca3..ea7220c0d 100644 --- a/src/GuiQt/WuQMacroDialog.cxx +++ b/src/GuiQt/WuQMacroDialog.cxx @@ -1812,6 +1812,35 @@ WuQMacroDialog::insertMenuRecordNewMacroCommandSelected() getSelectedMacroCommand()); } +/** + * Select the given command (if it is not NULL) or the macro (if it is not NULL) + * + * @param macro + * The macro + * @param command + * The macro command + */ +void +WuQMacroDialog::selectMacroCommand(const WuQMacro* macro, + const WuQMacroCommand* command) +{ + QModelIndex selectedIndex; + if (command != NULL) { + selectedIndex = command->index(); + } + else if (macro != NULL) { + if (macro != NULL) { + m_treeView->setExpanded(macro->index(), + true); + } + selectedIndex = macro->index(); + } + + if (selectedIndex.isValid()) { + m_treeView->setCurrentIndex(selectedIndex); + } +} + /** * Add a new macro command */ @@ -1940,7 +1969,6 @@ WuQMacroDialog::updateEditingToolButtons() m_editingMoveDownToolButton->setEnabled(moveDownValid); m_editingMoveUpToolButton->setEnabled(moveUpValid); -// m_recordMacroToolButton->setEnabled(true); switch (WuQMacroManager::instance()->getMode()) { case WuQMacroModeEnum::OFF: m_recordMacroToolButton->setIcon(m_recordMacroToolButtonIconOff); @@ -1953,7 +1981,6 @@ WuQMacroDialog::updateEditingToolButtons() break; case WuQMacroModeEnum::RUNNING: m_recordMacroToolButton->setIcon(m_recordMacroToolButtonIconOff); -// m_recordMacroToolButton->setEnabled(false); break; } } diff --git a/src/GuiQt/WuQMacroDialog.h b/src/GuiQt/WuQMacroDialog.h index c83d970ec..27b7ef013 100644 --- a/src/GuiQt/WuQMacroDialog.h +++ b/src/GuiQt/WuQMacroDialog.h @@ -74,6 +74,10 @@ namespace caret { // ADD_NEW_METHODS_HERE + public slots: + void selectMacroCommand(const WuQMacro* macro, + const WuQMacroCommand* command); + private slots: void treeViewItemClicked(const QModelIndex& modelIndex); diff --git a/src/GuiQt/WuQMacroExecutor.cxx b/src/GuiQt/WuQMacroExecutor.cxx index 027d63204..931b1fbd4 100644 --- a/src/GuiQt/WuQMacroExecutor.cxx +++ b/src/GuiQt/WuQMacroExecutor.cxx @@ -353,6 +353,9 @@ WuQMacroExecutor::runMacroPrivate(const WuQMacro* macro, const WuQMacroCommand* mc = macro->getMacroCommandAtIndex(i); CaretAssert(mc); + emit macroCommandStarting(macro, + mc); + const QString objectName(mc->getObjectName()); bool requiresObjectFlag(false); @@ -492,6 +495,9 @@ WuQMacroExecutor::runMacroPrivate(const WuQMacro* macro, QGuiApplication::processEvents(); } + macroCommandStarting(macro, + NULL); + return (errorMessageOut.isEmpty()); } diff --git a/src/GuiQt/WuQMacroExecutor.h b/src/GuiQt/WuQMacroExecutor.h index b5ffeaa8f..fc03d0af9 100644 --- a/src/GuiQt/WuQMacroExecutor.h +++ b/src/GuiQt/WuQMacroExecutor.h @@ -74,6 +74,10 @@ namespace caret { const WuQMacroExecutorOptions* executorOptions, bool& allowDelayFlagOut) const; + void macroCommandStarting(const WuQMacro* macro, + const WuQMacroCommand* command) const; + + private: void moveMouseToTabBarTab(QTabBar* tabBar, const int32_t tabIndex) const; diff --git a/src/GuiQt/WuQMacroManager.cxx b/src/GuiQt/WuQMacroManager.cxx index 7efc771e6..6237478fb 100644 --- a/src/GuiQt/WuQMacroManager.cxx +++ b/src/GuiQt/WuQMacroManager.cxx @@ -665,6 +665,11 @@ WuQMacroManager::runMacro(QWidget* widget, QObject::connect(m_macroExecutor, &WuQMacroExecutor::macroCommandHasCompleted, this, &WuQMacroManager::macroCommandCompletedExecution); + if (m_macrosDialog != NULL) { + QObject::connect(m_macroExecutor, &WuQMacroExecutor::macroCommandStarting, + m_macrosDialog, &WuQMacroDialog::selectMacroCommand); + } + m_macroExecutorMonitor->setMode(WuQMacroExecutorMonitor::Mode::RUN); if (m_macroHelper != NULL) { -- GitLab From 21df95068501e1592c205d757ec6cd90c197fc13 Mon Sep 17 00:00:00 2001 From: John Harwell Date: Thu, 21 Mar 2019 09:48:46 -0500 Subject: [PATCH 250/427] Added writing of images using other threads during movie recording. --- src/Brain/MovieRecorder.cxx | 135 ++++++++++++++++++++++++++++------- src/Brain/MovieRecorder.h | 32 +++++++++ src/GuiQt/WuQMacroDialog.cxx | 4 ++ 3 files changed, 147 insertions(+), 24 deletions(-) diff --git a/src/Brain/MovieRecorder.cxx b/src/Brain/MovieRecorder.cxx index 0c99f1212..1774d4f6e 100644 --- a/src/Brain/MovieRecorder.cxx +++ b/src/Brain/MovieRecorder.cxx @@ -28,6 +28,8 @@ #include #include +#include + #include "Brain.h" #include "CaretAssert.h" #include "CaretLogger.h" @@ -104,32 +106,42 @@ MovieRecorder::addImageToMovie(const QImage* image) + imageIndex + m_tempImageFileNameSuffix); - if (image->save(imageFileName)) { - if (m_imageFileNames.empty()) { - m_firstImageWidth = image->width(); - m_firstImageHeight = image->height(); - } - - if ((image->width() == m_firstImageWidth) - && (image->height() == m_firstImageHeight)) { + switch (m_imageMode) { + case ImageMode::IMMEDITATE: + if (image->save(imageFileName)) { + if (m_imageFileNames.empty()) { + m_firstImageWidth = image->width(); + m_firstImageHeight = image->height(); + } + + if ((image->width() == m_firstImageWidth) + && (image->height() == m_firstImageHeight)) { + m_imageFileNames.push_back(imageFileName); + } + else { + CaretLogSevere("Attempting to create movie with images that are different sizes. " + "First image width=" + QString::number(m_firstImageWidth) + + ", height=" + QString::number(m_firstImageHeight) + + " Image number=" + QString::number(imageIndexInt) + + ", width=" + QString::number(image->width()) + + ", height=" + QString::number(image->height())); + } + } + else { + CaretLogSevere("Saving temporary image failed: " + + imageFileName); + } + break; + case ImageMode::PARALLEL: + { + ImageWriter* iw = new ImageWriter(image, imageFileName); + m_imageWriters.push_back(iw); + QFuture f = QtConcurrent::run(iw, &ImageWriter::writeImage); + m_imageWriteResultFutures.push_back(f); m_imageFileNames.push_back(imageFileName); } - else { - CaretLogSevere("Attempting to create movie with images that are different sizes. " - "First image width=" + QString::number(m_firstImageWidth) - + ", height=" + QString::number(m_firstImageHeight) - + " Image number=" + QString::number(imageIndexInt) - + ", width=" + QString::number(image->width()) - + ", height=" + QString::number(image->height())); - } - } - else { - CaretLogSevere("Saving temporary image failed: " - + imageFileName); + break; } -// std::cout << "Adding image " << image->width() << ", " << image->height() << " " << imageFileName << std::endl; - - } /** @@ -149,12 +161,45 @@ MovieRecorder::addImageToMovieWithManualDuration(const QImage* image) setManualRecordingOfImageRequested(false); } +/** + * @return True if all images were written succussfully + * if parallel image file writing is enabled. Returns + * true if image writing mode is immediate. + */ +bool +MovieRecorder::waitForImagesToFinishWriting() +{ + bool allValid(true); + + switch (m_imageMode) { + case ImageMode::IMMEDITATE: + break; + case ImageMode::PARALLEL: + { + for (auto f : m_imageWriteResultFutures) { + f.waitForFinished(); + } + } + break; + } + + return allValid; +} + /** * Remove all images and starting a new movie */ void MovieRecorder::removeTemporaryImages() { + waitForImagesToFinishWriting(); + m_imageWriteResultFutures.clear(); + + for (auto iw : m_imageWriters) { + delete iw; + } + m_imageWriters.clear(); + const QString nameFilter(m_tempImageFileNamePrefix + "*" + m_tempImageFileNameSuffix); @@ -486,6 +531,11 @@ MovieRecorder::createMovie(const AString& filename, AString& errorMessageOut) { errorMessageOut.clear(); + + if ( ! waitForImagesToFinishWriting()) { + errorMessageOut = "There was a problem writing the image files."; + return false; + } m_movieFileName = filename; @@ -732,7 +782,6 @@ MovieRecorder::createMovieWithSystemCommand(const QString& programName, const AString commandString(programName + " " + arguments.join(" ")); - //std::cout << "Command: " << commandString << std::endl; const int result = system(commandString.toLatin1().constData()); bool successFlag(false); @@ -746,3 +795,41 @@ MovieRecorder::createMovieWithSystemCommand(const QString& programName, return successFlag; } + +/** + * Constructor for image writer + * + * @param image + * The image file + * @param filename + * Name of file + */ +MovieRecorder::ImageWriter::ImageWriter(const QImage* image, + const QString& filename) +: m_image(new QImage(*image)), +m_filename(filename) +{ + CaretAssert(m_image); + +} + +/** + * Destructor + */ +MovieRecorder::ImageWriter::~ImageWriter() +{ +} + +/** + * Write the image + * + * @return True if written, false if error. + */ +bool +MovieRecorder::ImageWriter::writeImage() +{ + CaretAssert(m_image); + return m_image->save(m_filename); +} + + diff --git a/src/Brain/MovieRecorder.h b/src/Brain/MovieRecorder.h index 1804a82e3..e75614576 100644 --- a/src/Brain/MovieRecorder.h +++ b/src/Brain/MovieRecorder.h @@ -25,6 +25,8 @@ #include +#include + #include "CaretObject.h" #include "MovieRecorderCaptureRegionTypeEnum.h" #include "MovieRecorderModeEnum.h" @@ -109,6 +111,28 @@ namespace caret { virtual AString toString() const; private: + enum class ImageMode { + IMMEDITATE, + PARALLEL + }; + + /** + * Used to write images in separate thread + */ + class ImageWriter { + public: + ImageWriter(const QImage* image, + const QString& filename); + + ~ImageWriter(); + + bool writeImage(); + private: + std::unique_ptr m_image; + + const QString m_filename; + }; + // ADD_NEW_MEMBERS_HERE bool createMovieWithSystemCommand(const QString& programName, @@ -126,6 +150,8 @@ namespace caret { void initializeMovieFileName() const; + bool waitForImagesToFinishWriting(); + Brain* m_brain = NULL; MovieRecorderModeEnum::Enum m_recordingMode = MovieRecorderModeEnum::MANUAL; @@ -142,6 +168,12 @@ namespace caret { std::vector m_imageFileNames; + std::vector> m_imageWriteResultFutures; + + std::vector m_imageWriters; + + ImageMode m_imageMode = ImageMode::PARALLEL; + mutable AString m_movieFileName; std::vector m_imageFrameFileNames; diff --git a/src/GuiQt/WuQMacroDialog.cxx b/src/GuiQt/WuQMacroDialog.cxx index ea7220c0d..33bc06937 100644 --- a/src/GuiQt/WuQMacroDialog.cxx +++ b/src/GuiQt/WuQMacroDialog.cxx @@ -49,6 +49,7 @@ #include "CaretAssert.h" #include "CaretLogger.h" +#include "ElapsedTimer.h" #include "WuQMacro.h" #include "WuQMacroCopyDialog.h" #include "WuQMacroCommand.h" @@ -1245,9 +1246,12 @@ WuQMacroDialog::runMacroToolButtonClicked() m_macroIsRunningFlag = true; updateEditingToolButtons(); QApplication::processEvents(); + ElapsedTimer timer; + timer.start(); WuQMacro* lastMacroRun = WuQMacroManager::instance()->runMacro(window, macro, getSelectedMacroCommand()); + std::cout << "Time to run macro: " << timer.getElapsedTimeSeconds() << std::endl; m_macroIsRunningFlag = false; if (lastMacroRun != getSelectedMacro()) { -- GitLab From dfb208ddc9119d332e2a131ae26bcb05dec29f62 Mon Sep 17 00:00:00 2001 From: John Harwell Date: Thu, 21 Mar 2019 13:53:03 -0500 Subject: [PATCH 251/427] Updated manual mode movie capture (and used for macro delays) so that an image of graphics is captured once and written "duration" times. The current version of ffmpeg (used to create movies) takes a wild card for the image file names so recording a "duration" requires writing the same image to many files . When ffmpeg is updated, it will be possible to provide ffmpeg with a text file of image names and thus the same image file name can be specified many times and only written once. --- src/Brain/MovieRecorder.cxx | 92 +++------------------ src/Brain/MovieRecorder.h | 21 +---- src/Common/EventManager.cxx | 1 + src/Common/EventTypeEnum.cxx | 4 + src/Common/EventTypeEnum.h | 2 + src/GuiQt/BrainOpenGLWidget.cxx | 46 ++++++++--- src/GuiQt/CMakeLists.txt | 2 + src/GuiQt/EventMovieManualModeRecording.cxx | 80 ++++++++++++++++++ src/GuiQt/EventMovieManualModeRecording.h | 66 +++++++++++++++ src/GuiQt/MovieRecordingDialog.cxx | 15 ++-- src/GuiQt/WbMacroHelper.cxx | 31 ++----- src/GuiQt/WuQMacroDialog.cxx | 2 +- 12 files changed, 223 insertions(+), 139 deletions(-) create mode 100644 src/GuiQt/EventMovieManualModeRecording.cxx create mode 100644 src/GuiQt/EventMovieManualModeRecording.h diff --git a/src/Brain/MovieRecorder.cxx b/src/Brain/MovieRecorder.cxx index 1774d4f6e..4ae773bd5 100644 --- a/src/Brain/MovieRecorder.cxx +++ b/src/Brain/MovieRecorder.cxx @@ -106,8 +106,8 @@ MovieRecorder::addImageToMovie(const QImage* image) + imageIndex + m_tempImageFileNameSuffix); - switch (m_imageMode) { - case ImageMode::IMMEDITATE: + switch (m_imageWriteMode) { + case ImageWriteMode::IMMEDITATE: if (image->save(imageFileName)) { if (m_imageFileNames.empty()) { m_firstImageWidth = image->width(); @@ -132,7 +132,7 @@ MovieRecorder::addImageToMovie(const QImage* image) + imageFileName); } break; - case ImageMode::PARALLEL: + case ImageWriteMode::PARALLEL: { ImageWriter* iw = new ImageWriter(image, imageFileName); m_imageWriters.push_back(iw); @@ -145,20 +145,21 @@ MovieRecorder::addImageToMovie(const QImage* image) } /** - * Add an image to the movie, typically used during automatic mode recording. - * After image is added, manual recording is disabled + * Add copies of an image to the movie for the given number of copies. + * Typically used during manual mode recording. * * @param image * Image that is added + * @param numberOfCopies + * Number of copies for the image. */ void -MovieRecorder::addImageToMovieWithManualDuration(const QImage* image) +MovieRecorder::addImageToMovieWithCopies(const QImage* image, + const int32_t numberOfCopies) { - const int32_t recordingCount = m_frameRate * m_manualRecordingDurationSeconds; - for (int32_t i = 0; i < recordingCount; i++) { + for (int32_t i = 0; i < numberOfCopies; i++) { addImageToMovie(image); } - setManualRecordingOfImageRequested(false); } /** @@ -171,10 +172,10 @@ MovieRecorder::waitForImagesToFinishWriting() { bool allValid(true); - switch (m_imageMode) { - case ImageMode::IMMEDITATE: + switch (m_imageWriteMode) { + case ImageWriteMode::IMMEDITATE: break; - case ImageMode::PARALLEL: + case ImageWriteMode::PARALLEL: { for (auto f : m_imageWriteResultFutures) { f.waitForFinished(); @@ -372,10 +373,6 @@ MovieRecorder::setCaptureRegionType(const MovieRecorderCaptureRegionTypeEnum::En AString MovieRecorder::getMovieFileName() const { -// if (m_movieFileName.isEmpty()) { -// initializeMovieFileName(); -// } - return m_movieFileName; } @@ -391,26 +388,6 @@ MovieRecorder::setMovieFileName(const AString& filename) m_movieFileName = filename; } -/** - * If the movie file name is empty, - * initialize the movie file to be in the given path. - * - * @param path - * Path of where to place the movie file - */ -void -MovieRecorder::initializeMovieFileName() const -{ - const QString currentDirectory = m_brain->getCurrentDirectory(); - if (m_movieFileName.isEmpty()) { - const QString extension = MovieRecorderVideoFormatTypeEnum::toFileNameExtensionNoDot(MovieRecorderVideoFormatTypeEnum::MPEG); - - m_movieFileName = FileInformation::assembleFileComponents(currentDirectory, - "Movie", - extension); - } -} - /** * Get a description of this object's content. * @return String describing this object's content. @@ -442,49 +419,6 @@ MovieRecorder::setFramesRate(const float frameRate) m_frameRate = frameRate; } -/** - * @return Duration of manual record duration in seconds - */ -float -MovieRecorder::getManualRecordingDurationSeconds() const -{ - return m_manualRecordingDurationSeconds; -} - -/** - * Set the duration of manual recording - * - * @param seconds - * The duration - */ -void -MovieRecorder::setManualRecordingDurationSeconds(const float seconds) -{ - m_manualRecordingDurationSeconds = seconds; -} - -/** - * @return Is request for manual recording of an image when graphics updated set? - * After image is recorded, manual recording request is reset to false - */ -bool -MovieRecorder::isManualRecordingOfImageRequested() const -{ - return m_manualRecordingOfImageRequested; -} - -/** - * Set request for manual recording of an image when graphics updated - * - * @param requestFlag - * New value for request - */ -void -MovieRecorder::setManualRecordingOfImageRequested(const bool requestFlag) -{ - m_manualRecordingOfImageRequested = requestFlag; -} - /** * @return True if temporary images should be removed * after creation of a movie diff --git a/src/Brain/MovieRecorder.h b/src/Brain/MovieRecorder.h index e75614576..cb1791667 100644 --- a/src/Brain/MovieRecorder.h +++ b/src/Brain/MovieRecorder.h @@ -54,7 +54,8 @@ namespace caret { void addImageToMovie(const QImage* image); - void addImageToMovieWithManualDuration(const QImage* image); + void addImageToMovieWithCopies(const QImage* image, + const int32_t numberOfCopies); void setRecordingMode(const MovieRecorderModeEnum::Enum recordingMode); @@ -89,14 +90,6 @@ namespace caret { void setFramesRate(const float frameRate); - float getManualRecordingDurationSeconds() const; - - void setManualRecordingDurationSeconds(const float seconds); - - bool isManualRecordingOfImageRequested() const; - - void setManualRecordingOfImageRequested(const bool requestFlag); - bool isRemoveTemporaryImagesAfterMovieCreation() const; void setRemoveTemporaryImagesAfterMovieCreation(const bool status); @@ -111,7 +104,7 @@ namespace caret { virtual AString toString() const; private: - enum class ImageMode { + enum class ImageWriteMode { IMMEDITATE, PARALLEL }; @@ -148,8 +141,6 @@ namespace caret { const QString& textFileName, QString& errorMessageOut); - void initializeMovieFileName() const; - bool waitForImagesToFinishWriting(); Brain* m_brain = NULL; @@ -172,7 +163,7 @@ namespace caret { std::vector m_imageWriters; - ImageMode m_imageMode = ImageMode::PARALLEL; + ImageWriteMode m_imageWriteMode = ImageWriteMode::PARALLEL; mutable AString m_movieFileName; @@ -180,10 +171,6 @@ namespace caret { float m_frameRate = 30.0f; - float m_manualRecordingDurationSeconds = 5.0f; - - bool m_manualRecordingOfImageRequested = false; - AString m_temporaryImagesDirectory; AString m_tempImageFileNamePrefix; diff --git a/src/Common/EventManager.cxx b/src/Common/EventManager.cxx index f210d473c..e39f3396c 100644 --- a/src/Common/EventManager.cxx +++ b/src/Common/EventManager.cxx @@ -446,6 +446,7 @@ EventManager::sendSimpleEvent(const EventTypeEnum::Enum eventType) case EventTypeEnum::EVENT_MODEL_GET_ALL: case EventTypeEnum::EVENT_MODEL_GET_ALL_DISPLAYED: case EventTypeEnum::EVENT_MODEL_SURFACE_GET: + case EventTypeEnum::EVENT_MOVIE_RECORDING_MANUAL_MODE_CAPTURE: case EventTypeEnum::EVENT_NODE_IDENTIFICATION_COLORS_GET_FROM_CHARTS: case EventTypeEnum::EVENT_OPENGL_OBJECT_TO_WINDOW_TRANSFORM: case EventTypeEnum::EVENT_OPERATING_SYSTEM_REQUEST_OPEN_DATA_FILE: diff --git a/src/Common/EventTypeEnum.cxx b/src/Common/EventTypeEnum.cxx index bde70d24d..308fbcf02 100644 --- a/src/Common/EventTypeEnum.cxx +++ b/src/Common/EventTypeEnum.cxx @@ -322,6 +322,10 @@ EventTypeEnum::initialize() "EVENT_MODEL_SURFACE_GET", "Get a specific model surface")); + enumData.push_back(EventTypeEnum(EVENT_MOVIE_RECORDING_MANUAL_MODE_CAPTURE, + "EVENT_MOVIE_RECORDING_MANUAL_MODE_CAPTURE", + "Movie recording manual mode capture")); + enumData.push_back(EventTypeEnum(EVENT_MOVIE_RECORDING_DIALOG_UPDATE, "EVENT_MOVIE_RECORDING_DIALOG_UPDATE", "Update the movie recording dialog")); diff --git a/src/Common/EventTypeEnum.h b/src/Common/EventTypeEnum.h index 2fbd1b2ea..8c3d8fac2 100644 --- a/src/Common/EventTypeEnum.h +++ b/src/Common/EventTypeEnum.h @@ -170,6 +170,8 @@ public: EVENT_MODEL_SURFACE_GET, /** Update the movie dialog */ EVENT_MOVIE_RECORDING_DIALOG_UPDATE, + /** Movie manual mode image recording */ + EVENT_MOVIE_RECORDING_MANUAL_MODE_CAPTURE, /** Get the color for a node's identification symbol from a chart that contains the node */ EVENT_NODE_IDENTIFICATION_COLORS_GET_FROM_CHARTS, /** Get the transformation for converting object coordinates to window coordinates */ diff --git a/src/GuiQt/BrainOpenGLWidget.cxx b/src/GuiQt/BrainOpenGLWidget.cxx index 0630a4215..985cc77d9 100644 --- a/src/GuiQt/BrainOpenGLWidget.cxx +++ b/src/GuiQt/BrainOpenGLWidget.cxx @@ -62,6 +62,7 @@ #include "EventGraphicsUpdateOneWindow.h" #include "EventGetOrSetUserInputModeProcessor.h" #include "EventIdentificationRequest.h" +#include "EventMovieManualModeRecording.h" #include "EventUserInterfaceUpdate.h" #include "FtglFontTextRenderer.h" #include "GuiManager.h" @@ -171,6 +172,7 @@ windowIndex(windowIndex) EventManager::get()->addEventListener(this, EventTypeEnum::EVENT_GET_OR_SET_USER_INPUT_MODE); EventManager::get()->addEventListener(this, EventTypeEnum::EVENT_IDENTIFICATION_REQUEST); EventManager::get()->addEventListener(this, EventTypeEnum::EVENT_IMAGE_CAPTURE); + EventManager::get()->addEventListener(this, EventTypeEnum::EVENT_MOVIE_RECORDING_MANUAL_MODE_CAPTURE); EventManager::get()->addEventListener(this, EventTypeEnum::EVENT_USER_INTERFACE_UPDATE); m_openGLContextSharingValid = true; @@ -1561,8 +1563,12 @@ BrainOpenGLWidget::mouseMoveEvent(QMouseEvent* me) void BrainOpenGLWidget::receiveEvent(Event* event) { + MovieRecorder* movieRecorder = SessionManager::get()->getMovieRecorder(); + MovieRecorderModeEnum::Enum movieRecordingMode = movieRecorder->getRecordingMode(); + bool doRepaintGraphicsFlag(false); bool doUpdateGraphicsFlag(false); + int32_t captureManualMovieModeImageRepeatCount(-1); if (event->getEventType() == EventTypeEnum::EVENT_BRAIN_RESET) { EventBrainReset* brainResetEvent = dynamic_cast(event); @@ -1679,6 +1685,31 @@ BrainOpenGLWidget::receiveEvent(Event* event) idRequestEvent->setEventProcessed(); } } + else if (event->getEventType() == EventTypeEnum::EVENT_MOVIE_RECORDING_MANUAL_MODE_CAPTURE) { + EventMovieManualModeRecording* movieEvent = dynamic_cast(event); + CaretAssert(movieEvent); + + const int32_t windowIndex = movieEvent->getBrowserWindowIndex(); + if ((windowIndex < 0) + || (windowIndex == this->windowIndex)) { + /* + * Movie mode may be automatic but override with manual + * so that images are captured + */ + movieRecordingMode = MovieRecorderModeEnum::MANUAL; + + const float durationSeconds = movieEvent->getDurationSeconds(); + const float frameRate = movieRecorder->getFramesRate(); + captureManualMovieModeImageRepeatCount = static_cast(frameRate + * durationSeconds); + if (captureManualMovieModeImageRepeatCount < 1) { + captureManualMovieModeImageRepeatCount = 1; + } + doRepaintGraphicsFlag = true; + + movieEvent->setEventProcessed(); + } + } else if (event->getEventType() == EventTypeEnum::EVENT_USER_INTERFACE_UPDATE) { EventUserInterfaceUpdate* guiUpdateEvent = dynamic_cast(event); CaretAssert(guiUpdateEvent); @@ -1694,15 +1725,9 @@ BrainOpenGLWidget::receiveEvent(Event* event) || doUpdateGraphicsFlag) { bool captureAutomaticImageForMovieFlag(false); - bool captureManualImageForMovieFlag(false); - MovieRecorder* movieRecorder = SessionManager::get()->getMovieRecorder(); if (movieRecorder->getRecordingWindowIndex() == this->windowIndex) { - switch (movieRecorder->getRecordingMode()) { + switch (movieRecordingMode) { case MovieRecorderModeEnum::MANUAL: - if (movieRecorder->isManualRecordingOfImageRequested()) { - captureManualImageForMovieFlag = true; - } - doRepaintGraphicsFlag = true; break; case MovieRecorderModeEnum::AUTOMATIC: captureAutomaticImageForMovieFlag = true; @@ -1715,7 +1740,7 @@ BrainOpenGLWidget::receiveEvent(Event* event) this->repaint(); if (captureAutomaticImageForMovieFlag - || captureManualImageForMovieFlag) { + || (captureManualMovieModeImageRepeatCount > 0)) { const bool showTimingResultFlag(false); int captureRegionOffsetX(0); @@ -1855,8 +1880,9 @@ BrainOpenGLWidget::receiveEvent(Event* event) if (captureAutomaticImageForMovieFlag) { movieRecorder->addImageToMovie(&image); } - else if (captureManualImageForMovieFlag) { - movieRecorder->addImageToMovieWithManualDuration(&image); + else if (captureManualMovieModeImageRepeatCount > 0) { + movieRecorder->addImageToMovieWithCopies(&image, + captureManualMovieModeImageRepeatCount); } if (showTimingResultFlag) { std::cout << " Image write time: " << imageTimer.getElapsedTimeSeconds() << std::endl; diff --git a/src/GuiQt/CMakeLists.txt b/src/GuiQt/CMakeLists.txt index b836b72d4..b0c5c0d27 100644 --- a/src/GuiQt/CMakeLists.txt +++ b/src/GuiQt/CMakeLists.txt @@ -354,6 +354,7 @@ EventHelpViewerDisplay.h EventIdentificationRequest.h EventImageCapture.h EventMacDockMenuUpdate.h +EventMovieManualModeRecording.h EventOperatingSystemRequestOpenDataFile.h EventOverlaySettingsEditorDialogRequest.h EventPaletteColorMappingEditorDialogRequest.h @@ -615,6 +616,7 @@ EventHelpViewerDisplay.cxx EventIdentificationRequest.cxx EventImageCapture.cxx EventMacDockMenuUpdate.cxx +EventMovieManualModeRecording.cxx EventOperatingSystemRequestOpenDataFile.cxx EventOverlaySettingsEditorDialogRequest.cxx EventPaletteColorMappingEditorDialogRequest.cxx diff --git a/src/GuiQt/EventMovieManualModeRecording.cxx b/src/GuiQt/EventMovieManualModeRecording.cxx new file mode 100644 index 000000000..480e29770 --- /dev/null +++ b/src/GuiQt/EventMovieManualModeRecording.cxx @@ -0,0 +1,80 @@ + +/*LICENSE_START*/ +/* + * Copyright (C) 2019 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + +#define __EVENT_MOVIE_MANUAL_MODE_RECORDING_DECLARE__ +#include "EventMovieManualModeRecording.h" +#undef __EVENT_MOVIE_MANUAL_MODE_RECORDING_DECLARE__ + +#include "CaretAssert.h" +#include "EventTypeEnum.h" + +using namespace caret; + + + +/** + * \class caret::EventMovieManualModeRecording + * \brief Manual mode recording image capture + * \ingroup GuiQt + */ + +/** + * Constructor. + * + * @param browserWindowIndex + * Index of the browser window + * @param durationSeconds + * Time in seconds for manual recording (used with movie recording frame + * rate to determine number of images captured) + */ +EventMovieManualModeRecording::EventMovieManualModeRecording(const int32_t browserWindowIndex, + const float durationSeconds) +: Event(EventTypeEnum::EVENT_MOVIE_RECORDING_MANUAL_MODE_CAPTURE), +m_browserWindowIndex(browserWindowIndex), +m_durationSeconds(durationSeconds) +{ + +} + +/** + * Destructor. + */ +EventMovieManualModeRecording::~EventMovieManualModeRecording() +{ +} + +/** + * @return Index of browser window or negative for all windows + */ +int32_t +EventMovieManualModeRecording::getBrowserWindowIndex() const +{ + return m_browserWindowIndex; +} + +/** + * @return Duration in seconds + */ +float +EventMovieManualModeRecording::getDurationSeconds() const +{ + return m_durationSeconds; +} diff --git a/src/GuiQt/EventMovieManualModeRecording.h b/src/GuiQt/EventMovieManualModeRecording.h new file mode 100644 index 000000000..4631ac7a3 --- /dev/null +++ b/src/GuiQt/EventMovieManualModeRecording.h @@ -0,0 +1,66 @@ +#ifndef __EVENT_MOVIE_MANUAL_MODE_RECORDING_H__ +#define __EVENT_MOVIE_MANUAL_MODE_RECORDING_H__ + +/*LICENSE_START*/ +/* + * Copyright (C) 2019 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + + + +#include + +#include "Event.h" + + + +namespace caret { + + class EventMovieManualModeRecording : public Event { + + public: + EventMovieManualModeRecording(const int32_t browserWindowIndex, + const float durationSeconds); + + virtual ~EventMovieManualModeRecording(); + + EventMovieManualModeRecording(const EventMovieManualModeRecording&) = delete; + + EventMovieManualModeRecording& operator=(const EventMovieManualModeRecording&) = delete; + + int32_t getBrowserWindowIndex() const; + + float getDurationSeconds() const; + + // ADD_NEW_METHODS_HERE + + private: + const int32_t m_browserWindowIndex; + + const float m_durationSeconds; + + // ADD_NEW_MEMBERS_HERE + + }; + +#ifdef __EVENT_MOVIE_MANUAL_MODE_RECORDING_DECLARE__ + // +#endif // __EVENT_MOVIE_MANUAL_MODE_RECORDING_DECLARE__ + +} // namespace +#endif //__EVENT_MOVIE_MANUAL_MODE_RECORDING_H__ diff --git a/src/GuiQt/MovieRecordingDialog.cxx b/src/GuiQt/MovieRecordingDialog.cxx index 176a5c935..63d803b44 100644 --- a/src/GuiQt/MovieRecordingDialog.cxx +++ b/src/GuiQt/MovieRecordingDialog.cxx @@ -43,6 +43,7 @@ #include "Event.h" #include "EventManager.h" #include "EventGraphicsUpdateOneWindow.h" +#include "EventMovieManualModeRecording.h" #include "EventUserInterfaceUpdate.h" #include "EnumComboBoxTemplate.h" #include "FileInformation.h" @@ -183,7 +184,6 @@ MovieRecordingDialog::updateManualRecordingOptions() break; } - m_manualCaptureSecondsSpinBox->setValue(movieRecorder->getManualRecordingDurationSeconds()); m_manualCaptureToolButton->setEnabled(manualRecordingEnabledFlag); } @@ -374,11 +374,9 @@ MovieRecordingDialog::manualCaptureToolButtonClicked() CursorDisplayScoped cursor; cursor.showWaitCursor(); - MovieRecorder* movieRecorder = SessionManager::get()->getMovieRecorder(); - CaretAssert(movieRecorder); - movieRecorder->setManualRecordingOfImageRequested(true); - EventManager::get()->sendEvent(EventGraphicsUpdateOneWindow(m_windowComboBox->getSelectedBrowserWindowIndex()).getPointer()); - movieRecorder->setManualRecordingOfImageRequested(false); + EventMovieManualModeRecording movieEvent(m_windowComboBox->getSelectedBrowserWindowIndex(), + m_manualCaptureSecondsSpinBox->value()); + EventManager::get()->sendEvent(movieEvent.getPointer()); updateFrameCountLabel(); } @@ -386,11 +384,8 @@ MovieRecordingDialog::manualCaptureToolButtonClicked() * Called when manual capture seconds spin box value changed */ void -MovieRecordingDialog::manualCaptureSecondsSpinBoxValueChanged(int seconds) +MovieRecordingDialog::manualCaptureSecondsSpinBoxValueChanged(int /*seconds*/) { - MovieRecorder* movieRecorder = SessionManager::get()->getMovieRecorder(); - CaretAssert(movieRecorder); - movieRecorder->setManualRecordingDurationSeconds(seconds); } /** diff --git a/src/GuiQt/WbMacroHelper.cxx b/src/GuiQt/WbMacroHelper.cxx index dd9798558..99bdeea2b 100644 --- a/src/GuiQt/WbMacroHelper.cxx +++ b/src/GuiQt/WbMacroHelper.cxx @@ -32,6 +32,7 @@ #include "EventGraphicsUpdateAllWindows.h" #include "EventGraphicsUpdateOneWindow.h" #include "EventManager.h" +#include "EventMovieManualModeRecording.h" #include "EventSceneActive.h" #include "EventUserInterfaceUpdate.h" #include "GuiManager.h" @@ -361,7 +362,7 @@ WbMacroHelper::macroCommandHasCompleted(QWidget* window, const WuQMacroExecutorOptions* executorOptions, bool& allowDelayFlagOut) { - bool doDelayAfterCommandFlag(false); // change value to enable/disable delays after command + bool doDelayAfterCommandFlag(false); /* change value to enable/disable delays after command */ if (executorOptions->isIgnoreDelaysAndDurations()) { doDelayAfterCommandFlag = false; } @@ -393,33 +394,19 @@ WbMacroHelper::recordImagesForDelay(QWidget* window, MovieRecorder* movieRecorder = SessionManager::get()->getMovieRecorder(); CaretAssert(movieRecorder); - const WuQMacroExecutorMonitor* execMonitor = WuQMacroManager::instance()->getMacroExecutorMonitor(); if (command->getCommandType() != WuQMacroCommandTypeEnum::MOUSE) { if (command->getDelayInSeconds() > 0.0) { switch (movieRecorder->getRecordingMode()) { case MovieRecorderModeEnum::AUTOMATIC: { BrainBrowserWindow* bbw = dynamic_cast(window); - const int32_t numberOfFrames = static_cast(movieRecorder->getFramesRate() - * command->getDelayInSeconds()); - for (int32_t i = 0; i < numberOfFrames; i++) { - movieRecorder->setManualRecordingOfImageRequested(true); - if (bbw != NULL) { - EventManager::get()->sendEvent(EventGraphicsUpdateOneWindow(bbw->getBrowserWindowIndex()).getPointer()); - } - else { - EventManager::get()->sendEvent(EventGraphicsUpdateAllWindows().getPointer()); - } - movieRecorder->setManualRecordingOfImageRequested(false); - - /* - * User may pause or stop - */ - if (execMonitor->doPause()) { - break; - } - } - + const int32_t windowIndex = ((bbw != NULL) + ? bbw->getBrowserWindowIndex() + : -1); + EventMovieManualModeRecording movieEvent(windowIndex, + command->getDelayInSeconds()); + EventManager::get()->sendEvent(movieEvent.getPointer()); + allowDelayFlagOut = false; } break; diff --git a/src/GuiQt/WuQMacroDialog.cxx b/src/GuiQt/WuQMacroDialog.cxx index 33bc06937..5f7788b38 100644 --- a/src/GuiQt/WuQMacroDialog.cxx +++ b/src/GuiQt/WuQMacroDialog.cxx @@ -1251,7 +1251,7 @@ WuQMacroDialog::runMacroToolButtonClicked() WuQMacro* lastMacroRun = WuQMacroManager::instance()->runMacro(window, macro, getSelectedMacroCommand()); - std::cout << "Time to run macro: " << timer.getElapsedTimeSeconds() << std::endl; + // may use this later when testing std::cout << "Time to run macro: " << timer.getElapsedTimeSeconds() << std::endl; m_macroIsRunningFlag = false; if (lastMacroRun != getSelectedMacro()) { -- GitLab From 3905ea183f6a9350cf28d9371550528ffb14452e Mon Sep 17 00:00:00 2001 From: John Harwell Date: Thu, 21 Mar 2019 14:21:38 -0500 Subject: [PATCH 252/427] When capturing images for a movie, place them into a subdirectory of the temporary directory (instead of just in the temporary directory). --- src/Brain/MovieRecorder.cxx | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/src/Brain/MovieRecorder.cxx b/src/Brain/MovieRecorder.cxx index 4ae773bd5..4ae8ed274 100644 --- a/src/Brain/MovieRecorder.cxx +++ b/src/Brain/MovieRecorder.cxx @@ -59,7 +59,24 @@ MovieRecorder::MovieRecorder(Brain* brain) : CaretObject(), m_brain(brain) { - m_temporaryImagesDirectory = SystemUtilities::getTempDirectory(); + const QString tempSubDir("WbViewMovie"); + QDir tempDir(QDir::temp()); + /* + * "CD" will fail if temporary subdirectory does not exist + */ + if ( ! tempDir.cd(tempSubDir)) { + /* + * Try to create subdirectory + */ + if (tempDir.mkdir(tempSubDir)) { + /* + * Go to temp subdirectory + */ + tempDir.cd(tempSubDir); + } + } + m_temporaryImagesDirectory = tempDir.absolutePath(); + m_tempImageFileNamePrefix = "movie"; m_tempImageFileNameSuffix = ".png"; removeTemporaryImages(); -- GitLab From bd6eac75928b907ee471a078fbdd36c7f2927558 Mon Sep 17 00:00:00 2001 From: John Harwell Date: Thu, 21 Mar 2019 15:44:36 -0500 Subject: [PATCH 253/427] Add option to Macro Dialog that will create the movie file when the macro completes. --- src/GuiQt/MovieRecordingDialog.cxx | 70 +++++++++++++++++++++------ src/GuiQt/MovieRecordingDialog.h | 5 ++ src/GuiQt/WbMacroHelper.cxx | 9 +++- src/GuiQt/WuQMacroDialog.cxx | 66 ++++++++++++++++++++++++- src/GuiQt/WuQMacroDialog.h | 6 +++ src/GuiQt/WuQMacroExecutorOptions.cxx | 34 ++++++++++--- src/GuiQt/WuQMacroExecutorOptions.h | 6 +++ 7 files changed, 171 insertions(+), 25 deletions(-) diff --git a/src/GuiQt/MovieRecordingDialog.cxx b/src/GuiQt/MovieRecordingDialog.cxx index 63d803b44..cfbbbdff4 100644 --- a/src/GuiQt/MovieRecordingDialog.cxx +++ b/src/GuiQt/MovieRecordingDialog.cxx @@ -394,17 +394,20 @@ MovieRecordingDialog::manualCaptureSecondsSpinBoxValueChanged(int /*seconds*/) void MovieRecordingDialog::createMoviePushButtonClicked() { - createMovie(m_createMoviePushButton); + createMoviePrivate(m_createMoviePushButton, + true); } /** - * Create a movie from captured images. + * Get the movie file name * * @param parent - * Parent widget for error message dialog. + * Widget as parent for file selection dialog + * @return + * Name for movie file or empty string if canceled. */ -void -MovieRecordingDialog::createMovie(QWidget* parent) +QString +MovieRecordingDialog::getMovieFileNameFromFileDialog(QWidget* parent) { MovieRecorder* movieRecorder = SessionManager::get()->getMovieRecorder(); QString currentFileName = movieRecorder->getMovieFileName(); @@ -434,20 +437,57 @@ MovieRecordingDialog::createMovie(QWidget* parent) CaretFileDialog::DontConfirmOverwrite); if (filename.isEmpty()) { - return; + return ""; } - - if (selectedFilter.isEmpty()) { - for (auto fe : formatEnums) { - if (selectedFilter == MovieRecorderVideoFormatTypeEnum::toFileDialogFilter(fe)) { - const QString ext = ("." + MovieRecorderVideoFormatTypeEnum::toFileNameExtensionNoDot(fe)); - if ( ! filename.endsWith(ext)) { - filename.append(ext); - break; - } + + for (auto fe : formatEnums) { + if (selectedFilter == MovieRecorderVideoFormatTypeEnum::toFileDialogFilter(fe)) { + const QString ext = ("." + MovieRecorderVideoFormatTypeEnum::toFileNameExtensionNoDot(fe)); + if ( ! filename.endsWith(ext)) { + filename.append(ext); + break; } } } + + return filename; +} + +/** + * Create a movie from captured images and if current + * filename is empty, ask for name in file dialog + * + * @param parent + * Parent widget for error message dialog. + */ +void +MovieRecordingDialog::createMovie(QWidget* parent) +{ + createMoviePrivate(parent, + false); +} +/** + * Create a movie from captured images. + * + * @param parent + * Parent widget for error message dialog. + * @param askForFileNameFlag + * If true always query for filename, even if filename is valid + */ +void +MovieRecordingDialog::createMoviePrivate(QWidget* parent, + const bool askForFileNameFlag) +{ + MovieRecorder* movieRecorder = SessionManager::get()->getMovieRecorder(); + QString filename(movieRecorder->getMovieFileName()); + if (filename.isEmpty() + || askForFileNameFlag) { + filename = getMovieFileNameFromFileDialog(parent); + } + + if (filename.isEmpty()) { + return; + } FileInformation fileInfo(filename); const QString name(fileInfo.getCanonicalFilePath()); diff --git a/src/GuiQt/MovieRecordingDialog.h b/src/GuiQt/MovieRecordingDialog.h index 27ad6cd25..0b2abcf02 100644 --- a/src/GuiQt/MovieRecordingDialog.h +++ b/src/GuiQt/MovieRecordingDialog.h @@ -67,6 +67,8 @@ namespace caret { static void createMovie(QWidget* parent); + static QString getMovieFileNameFromFileDialog(QWidget* parent); + private slots: void movieRecorderVideoResolutionTypeEnumComboBoxItemActivated(); @@ -106,6 +108,9 @@ namespace caret { QWidget* createSettingsWidget(); + static void createMoviePrivate(QWidget* parent, + const bool askForFileNameFlag); + QRadioButton* m_recordingAutomaticRadioButton; QRadioButton* m_recordingManualRadioButton; diff --git a/src/GuiQt/WbMacroHelper.cxx b/src/GuiQt/WbMacroHelper.cxx index 99bdeea2b..037f28c32 100644 --- a/src/GuiQt/WbMacroHelper.cxx +++ b/src/GuiQt/WbMacroHelper.cxx @@ -37,6 +37,7 @@ #include "EventUserInterfaceUpdate.h" #include "GuiManager.h" #include "MovieRecorder.h" +#include "MovieRecordingDialog.h" #include "Scene.h" #include "SceneFile.h" #include "SceneInfo.h" @@ -264,12 +265,16 @@ WbMacroHelper::macroExecutionStarting(const WuQMacro* /*macro*/, */ void WbMacroHelper::macroExecutionEnding(const WuQMacro* /*macro*/, - QWidget* /*window*/, - const WuQMacroExecutorOptions* /*executorOptions*/) + QWidget* window, + const WuQMacroExecutorOptions* executorOptions) { MovieRecorder* movieRecorder = SessionManager::get()->getMovieRecorder(); movieRecorder->setRecordingMode(m_savedRecordingMode); EventManager::get()->sendSimpleEvent(EventTypeEnum::EVENT_MOVIE_RECORDING_DIALOG_UPDATE); + + if (executorOptions->isCreateMovieAfterMacroExecution()) { + MovieRecordingDialog::createMovie(window); + } } /** diff --git a/src/GuiQt/WuQMacroDialog.cxx b/src/GuiQt/WuQMacroDialog.cxx index 5f7788b38..30a3619f9 100644 --- a/src/GuiQt/WuQMacroDialog.cxx +++ b/src/GuiQt/WuQMacroDialog.cxx @@ -50,6 +50,9 @@ #include "CaretAssert.h" #include "CaretLogger.h" #include "ElapsedTimer.h" +#include "MovieRecorder.h" +#include "MovieRecordingDialog.h" +#include "SessionManager.h" #include "WuQMacro.h" #include "WuQMacroCopyDialog.h" #include "WuQMacroCommand.h" @@ -475,6 +478,12 @@ WuQMacroDialog::createRunOptionsWidget() QObject::connect(m_runOptionRecordMovieWhileMacroRunsCheckBox, &QCheckBox::clicked, this, &WuQMacroDialog::runOptionRecordMovieCheckBoxClicked); + m_runOptionCreateMovieAfterMacroRunsCheckBox = new QCheckBox("Create movie after macro finishes"); + m_runOptionCreateMovieAfterMacroRunsCheckBox->setChecked(false); + m_runOptionCreateMovieAfterMacroRunsCheckBox->setToolTip("After macro finishes, create the movie"); + QObject::connect(m_runOptionCreateMovieAfterMacroRunsCheckBox, &QCheckBox::clicked, + this, &WuQMacroDialog::runOptionCreateMovieCheckBoxClicked); + m_ignoreDelaysAndDurationsCheckBox = new QCheckBox("Ignore delays and durations"); const QString ignoreToolTip("Ignore delays and durations and minimize iterations " "to quickly execute macro (for debugging)"); @@ -482,12 +491,21 @@ WuQMacroDialog::createRunOptionsWidget() QObject::connect(m_ignoreDelaysAndDurationsCheckBox, &QCheckBox::clicked, this, &WuQMacroDialog::runOptionIgnoreDelaysAndDurationsCheckBoxClicked); + QHBoxLayout* windowLayout = new QHBoxLayout(); + windowLayout->setContentsMargins(0, 0, 0, 0); + windowLayout->addWidget(windowLabel); + windowLayout->addWidget(m_runOptionsWindowComboBox); + windowLayout->addStretch(); + + /* + * In layout, create movie option is indented + */ QGridLayout* runOptionsLayout = new QGridLayout(widget); + runOptionsLayout->setColumnMinimumWidth(0, 15); runOptionsLayout->setColumnStretch(0, 0); runOptionsLayout->setColumnStretch(1, 100); int row = 0; - runOptionsLayout->addWidget(windowLabel, row, 0); - runOptionsLayout->addWidget(m_runOptionsWindowComboBox, row, 1, Qt::AlignLeft); + runOptionsLayout->addLayout(windowLayout, row, 0, 1, 2, Qt::AlignLeft); row++; runOptionsLayout->addWidget(m_runOptionLoopCheckBox, row, 0, 1, 2, Qt::AlignLeft); row++; @@ -497,6 +515,8 @@ WuQMacroDialog::createRunOptionsWidget() row++; runOptionsLayout->addWidget(m_runOptionRecordMovieWhileMacroRunsCheckBox, row, 0, 1, 2, Qt::AlignLeft); row++; + runOptionsLayout->addWidget(m_runOptionCreateMovieAfterMacroRunsCheckBox, row, 1, Qt::AlignLeft); + row++; return widget; } @@ -541,6 +561,34 @@ WuQMacroDialog::runOptionRecordMovieCheckBoxClicked(bool checked) WuQMacroExecutorOptions* options = WuQMacroManager::instance()->getExecutorOptions(); CaretAssert(options); options->setRecordMovieDuringExecution(checked); + updateCreateMovieCheckBox(); +} + +/** + * Called when run options create movie checkbox is changed + * + * @param checked + * New checked status. + */ +void +WuQMacroDialog::runOptionCreateMovieCheckBoxClicked(bool checked) +{ + WuQMacroExecutorOptions* options = WuQMacroManager::instance()->getExecutorOptions(); + CaretAssert(options); + + /* + * If transitioning from OFF to ON, verify file name + */ + if (checked + && ( ! options->isCreateMovieAfterMacroExecution())) { + MovieRecorder* movieRecorder = SessionManager::get()->getMovieRecorder(); + const QString filename = MovieRecordingDialog::getMovieFileNameFromFileDialog(m_runOptionCreateMovieAfterMacroRunsCheckBox); + if (filename.isEmpty()) { + return; + } + movieRecorder->setMovieFileName(filename); + } + options->setCreateMovieAfterMacroExecution(checked); } /** @@ -704,9 +752,23 @@ WuQMacroDialog::updateDialogContents() m_runOptionRecordMovieWhileMacroRunsCheckBox->setChecked(runOptions->isRecordMovieDuringExecution()); m_ignoreDelaysAndDurationsCheckBox->setChecked(runOptions->isIgnoreDelaysAndDurations()); + updateCreateMovieCheckBox(); + macroGroupComboBoxActivated(selectedIndex); } +/** + * Update the create movie checkbox status + */ +void +WuQMacroDialog::updateCreateMovieCheckBox() +{ + const WuQMacroExecutorOptions* runOptions = WuQMacroManager::instance()->getExecutorOptions(); + CaretAssert(runOptions); + m_runOptionCreateMovieAfterMacroRunsCheckBox->setChecked(runOptions->isCreateMovieAfterMacroExecution()); + m_runOptionCreateMovieAfterMacroRunsCheckBox->setEnabled(runOptions->isRecordMovieDuringExecution()); +} + /** * Called when an item in the tree view is clicked by user * diff --git a/src/GuiQt/WuQMacroDialog.h b/src/GuiQt/WuQMacroDialog.h index 27b7ef013..56abb83dd 100644 --- a/src/GuiQt/WuQMacroDialog.h +++ b/src/GuiQt/WuQMacroDialog.h @@ -111,6 +111,10 @@ namespace caret { void runOptionRecordMovieCheckBoxClicked(bool); + void runOptionCreateMovieCheckBoxClicked(bool); + + void updateCreateMovieCheckBox(); + void runOptionIgnoreDelaysAndDurationsCheckBoxClicked(bool); void editingMoveUpToolButtonClicked(); @@ -248,6 +252,8 @@ namespace caret { QCheckBox* m_runOptionRecordMovieWhileMacroRunsCheckBox; + QCheckBox* m_runOptionCreateMovieAfterMacroRunsCheckBox; + QCheckBox* m_ignoreDelaysAndDurationsCheckBox; QLabel* m_commandTitleLabel; diff --git a/src/GuiQt/WuQMacroExecutorOptions.cxx b/src/GuiQt/WuQMacroExecutorOptions.cxx index 210d3f4df..85e78abc3 100644 --- a/src/GuiQt/WuQMacroExecutorOptions.cxx +++ b/src/GuiQt/WuQMacroExecutorOptions.cxx @@ -86,12 +86,13 @@ WuQMacroExecutorOptions::operator=(const WuQMacroExecutorOptions& obj) void WuQMacroExecutorOptions::copyHelperWuQMacroExecutorOptions(const WuQMacroExecutorOptions& obj) { - m_loopingFlag = obj.m_loopingFlag; - m_stopOnErrorFlag = obj.m_stopOnErrorFlag; - m_showMouseMovementFlag = obj.m_showMouseMovementFlag; - m_recordMovieDuringExecutionFlag = obj.m_recordMovieDuringExecutionFlag; - m_stopAfterSelectedCommandFlag = obj.m_stopAfterSelectedCommandFlag; - m_ignoreDelaysAndDurationsFlag = obj.m_ignoreDelaysAndDurationsFlag; + m_loopingFlag = obj.m_loopingFlag; + m_stopOnErrorFlag = obj.m_stopOnErrorFlag; + m_showMouseMovementFlag = obj.m_showMouseMovementFlag; + m_recordMovieDuringExecutionFlag = obj.m_recordMovieDuringExecutionFlag; + m_createMovieAfterMacroExecutionFlag = obj.m_createMovieAfterMacroExecutionFlag; + m_stopAfterSelectedCommandFlag = obj.m_stopAfterSelectedCommandFlag; + m_ignoreDelaysAndDurationsFlag = obj.m_ignoreDelaysAndDurationsFlag; } /** @@ -188,6 +189,27 @@ WuQMacroExecutorOptions::setRecordMovieDuringExecution(const bool status) m_recordMovieDuringExecutionFlag = status; } +/** + * @return True if create movie after macro executes is on. + */ +bool +WuQMacroExecutorOptions::isCreateMovieAfterMacroExecution() const +{ + return m_createMovieAfterMacroExecutionFlag; +} + +/** + * Set create movie after macro execution + * + * @param status + * New status + */ +void +WuQMacroExecutorOptions::setCreateMovieAfterMacroExecution(const bool status) +{ + m_createMovieAfterMacroExecutionFlag = status; +} + /** * @return True if execution should stop after the command selected in macro dialog */ diff --git a/src/GuiQt/WuQMacroExecutorOptions.h b/src/GuiQt/WuQMacroExecutorOptions.h index 303112a5c..de642a82e 100644 --- a/src/GuiQt/WuQMacroExecutorOptions.h +++ b/src/GuiQt/WuQMacroExecutorOptions.h @@ -58,6 +58,10 @@ namespace caret { void setRecordMovieDuringExecution(const bool status); + bool isCreateMovieAfterMacroExecution() const; + + void setCreateMovieAfterMacroExecution(const bool status); + bool isStopAfterSelectedCommand() const; void setStopAfterSelectedCommand(const bool status); @@ -81,6 +85,8 @@ namespace caret { bool m_recordMovieDuringExecutionFlag = false; + bool m_createMovieAfterMacroExecutionFlag = false; + bool m_stopAfterSelectedCommandFlag = false; bool m_ignoreDelaysAndDurationsFlag = false; -- GitLab From f52d51572c5c0e3dee307842db817155d9bc2063 Mon Sep 17 00:00:00 2001 From: John Harwell Date: Thu, 21 Mar 2019 15:51:31 -0500 Subject: [PATCH 254/427] Beep at the conclusion of a macro running or after a movie file is created. --- src/GuiQt/MovieRecordingDialog.cxx | 2 ++ src/GuiQt/WuQMacroManager.cxx | 5 +++++ 2 files changed, 7 insertions(+) diff --git a/src/GuiQt/MovieRecordingDialog.cxx b/src/GuiQt/MovieRecordingDialog.cxx index cfbbbdff4..5e242119b 100644 --- a/src/GuiQt/MovieRecordingDialog.cxx +++ b/src/GuiQt/MovieRecordingDialog.cxx @@ -23,6 +23,7 @@ #include "MovieRecordingDialog.h" #undef __MOVIE_RECORDING_DIALOG_DECLARE__ +#include #include #include #include @@ -396,6 +397,7 @@ MovieRecordingDialog::createMoviePushButtonClicked() { createMoviePrivate(m_createMoviePushButton, true); + QApplication::beep(); } /** diff --git a/src/GuiQt/WuQMacroManager.cxx b/src/GuiQt/WuQMacroManager.cxx index 6237478fb..3d9dcc880 100644 --- a/src/GuiQt/WuQMacroManager.cxx +++ b/src/GuiQt/WuQMacroManager.cxx @@ -654,6 +654,7 @@ WuQMacroManager::runMacro(QWidget* widget, CaretAssert(macroToRun); WuQMacro* macro(const_cast(macroToRun)); + bool dialogWasDisplayed(false); bool resultFlag(false); bool loopFlag(true); while (loopFlag) { @@ -699,6 +700,7 @@ WuQMacroManager::runMacro(QWidget* widget, QMessageBox::Ok, QMessageBox::NoButton); loopFlag = false; + dialogWasDisplayed = true; } /* @@ -734,6 +736,9 @@ WuQMacroManager::runMacro(QWidget* widget, } } + if ( ! dialogWasDisplayed) { + QApplication::beep(); + } return macro; } -- GitLab From 666b0e7c3d8d443aa9668a5cff986f7b83158488 Mon Sep 17 00:00:00 2001 From: John Harwell Date: Fri, 22 Mar 2019 14:33:57 -0500 Subject: [PATCH 255/427] In Qt 5.12, QOpenGLWidget::repaint() does not function as described in the Qt documentation. It is supposed to repaint the graphics (call paintGL()), before returning. However, it does not and functions like QOpenGLWidget::update() and returns with the graphics being updated later by Qt. There are two Qt bug reports for this (QT-BUG-74404 and QTBUG-53107). For now, calling QApplication::processEvents() immediately after repaint() seems to cause update of the graphics. Note that Qt 5.12 is used only on a development system. The releases use Qt 5.7.x and the deprecated QGLWidget (instead of QOpenGLWidget). --- src/Common/EventManager.cxx | 1 + src/Common/EventTypeEnum.cxx | 4 ++ src/Common/EventTypeEnum.h | 2 + src/GuiQt/BrainBrowserWindow.cxx | 5 +- src/GuiQt/BrainOpenGLWidget.cxx | 57 ++++++++++++++---- src/GuiQt/BrainOpenGLWidget.h | 2 + src/GuiQt/CMakeLists.txt | 2 + src/GuiQt/EventGraphicsTimingOneWindow.cxx | 67 ++++++++++++++++++++++ src/GuiQt/EventGraphicsTimingOneWindow.h | 61 ++++++++++++++++++++ 9 files changed, 189 insertions(+), 12 deletions(-) create mode 100644 src/GuiQt/EventGraphicsTimingOneWindow.cxx create mode 100644 src/GuiQt/EventGraphicsTimingOneWindow.h diff --git a/src/Common/EventManager.cxx b/src/Common/EventManager.cxx index e39f3396c..3a3792060 100644 --- a/src/Common/EventManager.cxx +++ b/src/Common/EventManager.cxx @@ -431,6 +431,7 @@ EventManager::sendSimpleEvent(const EventTypeEnum::Enum eventType) case EventTypeEnum::EVENT_GRAPHICS_OPENGL_CREATE_TEXTURE_NAME: case EventTypeEnum::EVENT_GRAPHICS_OPENGL_DELETE_BUFFER_OBJECT: case EventTypeEnum::EVENT_GRAPHICS_OPENGL_DELETE_TEXTURE_NAME: + case EventTypeEnum::EVENT_GRAPHICS_TIMING_ONE_WINDOW: case EventTypeEnum::EVENT_GRAPHICS_UPDATE_ALL_WINDOWS: case EventTypeEnum::EVENT_GRAPHICS_UPDATE_ONE_WINDOW: case EventTypeEnum::EVENT_HELP_VIEWER_DISPLAY: diff --git a/src/Common/EventTypeEnum.cxx b/src/Common/EventTypeEnum.cxx index 308fbcf02..1e536279a 100644 --- a/src/Common/EventTypeEnum.cxx +++ b/src/Common/EventTypeEnum.cxx @@ -262,6 +262,10 @@ EventTypeEnum::initialize() "EVENT_GET_VIEWPORT_SIZE", "Get the viewport size")); + enumData.push_back(EventTypeEnum(EVENT_GRAPHICS_TIMING_ONE_WINDOW, + "EVENT_GRAPHICS_TIMING_ONE_WINDOW", + "Graphics timing in one window")); + enumData.push_back(EventTypeEnum(EVENT_GRAPHICS_UPDATE_ALL_WINDOWS, "EVENT_GRAPHICS_UPDATE_ALL_WINDOWS", "Update all graphics windows")); diff --git a/src/Common/EventTypeEnum.h b/src/Common/EventTypeEnum.h index 8c3d8fac2..8cea87dd1 100644 --- a/src/Common/EventTypeEnum.h +++ b/src/Common/EventTypeEnum.h @@ -138,6 +138,8 @@ public: EVENT_GRAPHICS_OPENGL_DELETE_BUFFER_OBJECT, /** Delete a texture name for an OpenGL context */ EVENT_GRAPHICS_OPENGL_DELETE_TEXTURE_NAME, + /** Time the OpenGL graphics in a window */ + EVENT_GRAPHICS_TIMING_ONE_WINDOW, /** Update all graphics windows */ EVENT_GRAPHICS_UPDATE_ALL_WINDOWS, /** Update graphics in a window */ diff --git a/src/GuiQt/BrainBrowserWindow.cxx b/src/GuiQt/BrainBrowserWindow.cxx index c2595769b..21a098029 100644 --- a/src/GuiQt/BrainBrowserWindow.cxx +++ b/src/GuiQt/BrainBrowserWindow.cxx @@ -73,6 +73,7 @@ #include "EventManager.h" #include "EventModelGetAll.h" #include "EventGetOrSetUserInputModeProcessor.h" +#include "EventGraphicsTimingOneWindow.h" #include "EventGraphicsUpdateAllWindows.h" #include "EventGraphicsUpdateOneWindow.h" #include "EventSpecFileReadDataFiles.h" @@ -2962,9 +2963,9 @@ BrainBrowserWindow::processDevelopGraphicsTiming() ElapsedTimer et; et.start(); - const int32_t numTimes = 5; + const float numTimes(10.0); for (int32_t i = 0; i < numTimes; i++) { - EventManager::get()->sendEvent(EventGraphicsUpdateOneWindow(m_browserWindowIndex).getPointer()); + EventManager::get()->sendEvent(EventGraphicsTimingOneWindow(m_browserWindowIndex).getPointer()); } const float time = et.getElapsedTimeSeconds() / numTimes; diff --git a/src/GuiQt/BrainOpenGLWidget.cxx b/src/GuiQt/BrainOpenGLWidget.cxx index 985cc77d9..0018e806b 100644 --- a/src/GuiQt/BrainOpenGLWidget.cxx +++ b/src/GuiQt/BrainOpenGLWidget.cxx @@ -25,6 +25,7 @@ #include #include +#include #include #include #include @@ -58,6 +59,7 @@ #include "EventManager.h" #include "EventBrowserWindowDrawingContent.h" #include "EventBrowserWindowGraphicsRedrawn.h" +#include "EventGraphicsTimingOneWindow.h" #include "EventGraphicsUpdateAllWindows.h" #include "EventGraphicsUpdateOneWindow.h" #include "EventGetOrSetUserInputModeProcessor.h" @@ -167,6 +169,7 @@ windowIndex(windowIndex) setMouseTracking(true); EventManager::get()->addEventListener(this, EventTypeEnum::EVENT_BRAIN_RESET); + EventManager::get()->addEventListener(this, EventTypeEnum::EVENT_GRAPHICS_TIMING_ONE_WINDOW); EventManager::get()->addEventListener(this, EventTypeEnum::EVENT_GRAPHICS_UPDATE_ALL_WINDOWS); EventManager::get()->addEventListener(this, EventTypeEnum::EVENT_GRAPHICS_UPDATE_ONE_WINDOW); EventManager::get()->addEventListener(this, EventTypeEnum::EVENT_GET_OR_SET_USER_INPUT_MODE); @@ -1235,10 +1238,10 @@ BrainOpenGLWidget::performIdentification(const int x, * immediately) to redraw the models. Otherwise, * the graphics flash with strange looking drawing. */ - this->repaint(); + this->repaintGraphics(); this->doneCurrent(); #else - this->repaint(); + this->repaintGraphics(); #endif return idManager; @@ -1299,10 +1302,10 @@ BrainOpenGLWidget::performIdentificationAnnotations(const int x, * immediately) to redraw the models. Otherwise, * the graphics flash with strange looking drawing. */ - this->repaint(); + this->repaintGraphics(); this->doneCurrent(); #else - this->repaint(); + this->repaintGraphics(); #endif return annotationID; @@ -1365,10 +1368,10 @@ BrainOpenGLWidget::performIdentificationVoxelEditing(VolumeFile* editingVolumeFi * immediately) to redraw the models. Otherwise, * the graphics flash with strange looking drawing. */ - this->repaint(); + this->repaintGraphics(); this->doneCurrent(); #else - this->repaint(); + this->repaintGraphics(); #endif return idManager; @@ -1414,7 +1417,7 @@ BrainOpenGLWidget::performProjection(const int x, * immediately) to redraw the models. Otherwise, * the graphics flash with strange looking drawing. */ - this->repaint(); + this->repaintGraphics(); this->doneCurrent(); #endif } @@ -1578,6 +1581,15 @@ BrainOpenGLWidget::receiveEvent(Event* event) brainResetEvent->setEventProcessed(); } + else if (event->getEventType() == EventTypeEnum::EVENT_GRAPHICS_TIMING_ONE_WINDOW) { + EventGraphicsTimingOneWindow* timingEvent = dynamic_cast(event); + CaretAssert(timingEvent); + + if (timingEvent->getWindowIndex() == this->windowIndex) { + doRepaintGraphicsFlag = true; + timingEvent->setEventProcessed(); + } + } else if (event->getEventType() == EventTypeEnum::EVENT_GRAPHICS_UPDATE_ALL_WINDOWS) { EventGraphicsUpdateAllWindows* updateAllEvent = dynamic_cast(event); @@ -1737,8 +1749,7 @@ BrainOpenGLWidget::receiveEvent(Event* event) } if (doRepaintGraphicsFlag) { - this->repaint(); - + repaintGraphics(); if (captureAutomaticImageForMovieFlag || (captureManualMovieModeImageRepeatCount > 0)) { const bool showTimingResultFlag(false); @@ -1903,6 +1914,32 @@ BrainOpenGLWidget::receiveEvent(Event* event) } } +/** + * Perform an immediate repaint of the graphics + */ +void +BrainOpenGLWidget::repaintGraphics() +{ + repaint(); + +#ifdef WORKBENCH_USE_QT5_QOPENGL_WIDGET + /* + * When using QOpenGLWidget, calling repaint() returns before + * it calls paintGL() so graphics are not updated even though + * the documentation states that drawing will complete during + * the call to repaint(). Essentially, repaint() functions + * the same as update(). + * + * Two Qt bug reports have been submitted by others: + * QTBUG-74404, QTBUG-53107. + * + * For now, allowing events to process seems to allow the + * graphics to update. + */ + QApplication::processEvents(); +#endif +} + /** * Capture an image using the parameters from the event. * @@ -1969,7 +2006,7 @@ BrainOpenGLWidget::captureImage(EventImageCapture* imageCaptureEvent) * buffer is updated. (repaint() updates immediately, * update() is a scheduled update). */ - repaint(); + repaintGraphics(); #ifdef WORKBENCH_USE_QT5_QOPENGL_WIDGET image = grabFramebuffer(); #else diff --git a/src/GuiQt/BrainOpenGLWidget.h b/src/GuiQt/BrainOpenGLWidget.h index daf1ac5be..5f5942eee 100644 --- a/src/GuiQt/BrainOpenGLWidget.h +++ b/src/GuiQt/BrainOpenGLWidget.h @@ -167,6 +167,8 @@ namespace caret { void captureImage(EventImageCapture* imageCaptureEvent); + void repaintGraphics(); + const int32_t windowIndex; BrainOpenGLWindowContent m_windowContent; diff --git a/src/GuiQt/CMakeLists.txt b/src/GuiQt/CMakeLists.txt index b0c5c0d27..b6e614bdd 100644 --- a/src/GuiQt/CMakeLists.txt +++ b/src/GuiQt/CMakeLists.txt @@ -348,6 +348,7 @@ EventBrowserWindowGraphicsRedrawn.h EventBrowserWindowNew.h EventBrowserWindowTileTabOperation.h EventGetOrSetUserInputModeProcessor.h +EventGraphicsTimingOneWindow.h EventGraphicsUpdateAllWindows.h EventGraphicsUpdateOneWindow.h EventHelpViewerDisplay.h @@ -610,6 +611,7 @@ EventBrowserWindowGraphicsRedrawn.cxx EventBrowserWindowNew.cxx EventBrowserWindowTileTabOperation.cxx EventGetOrSetUserInputModeProcessor.cxx +EventGraphicsTimingOneWindow.cxx EventGraphicsUpdateAllWindows.cxx EventGraphicsUpdateOneWindow.cxx EventHelpViewerDisplay.cxx diff --git a/src/GuiQt/EventGraphicsTimingOneWindow.cxx b/src/GuiQt/EventGraphicsTimingOneWindow.cxx new file mode 100644 index 000000000..31db78d7b --- /dev/null +++ b/src/GuiQt/EventGraphicsTimingOneWindow.cxx @@ -0,0 +1,67 @@ + +/*LICENSE_START*/ +/* + * Copyright (C) 2019 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + +#define __EVENT_GRAPHICS_TIMING_ONE_WINDOW_DECLARE__ +#include "EventGraphicsTimingOneWindow.h" +#undef __EVENT_GRAPHICS_TIMING_ONE_WINDOW_DECLARE__ + +#include "CaretAssert.h" +#include "EventTypeEnum.h" + +using namespace caret; + + + +/** + * \class caret::EventGraphicsTimingOneWindow + * \brief Event for timing graphics + * \ingroup GuiQt + */ + +/** + * Constructor. + * + * @param windowIndex + * Index of window for timing + */ +EventGraphicsTimingOneWindow::EventGraphicsTimingOneWindow(const int32_t windowIndex) +: Event(EventTypeEnum::EVENT_GRAPHICS_TIMING_ONE_WINDOW), +m_windowIndex(windowIndex) +{ + +} + +/** + * Destructor. + */ +EventGraphicsTimingOneWindow::~EventGraphicsTimingOneWindow() +{ +} + +/** + * @return Index of window for timinig test + */ +int32_t +EventGraphicsTimingOneWindow::getWindowIndex() const +{ + return m_windowIndex; +} + diff --git a/src/GuiQt/EventGraphicsTimingOneWindow.h b/src/GuiQt/EventGraphicsTimingOneWindow.h new file mode 100644 index 000000000..2ad53dd40 --- /dev/null +++ b/src/GuiQt/EventGraphicsTimingOneWindow.h @@ -0,0 +1,61 @@ +#ifndef __EVENT_GRAPHICS_TIMING_ONE_WINDOW_H__ +#define __EVENT_GRAPHICS_TIMING_ONE_WINDOW_H__ + +/*LICENSE_START*/ +/* + * Copyright (C) 2019 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + + + +#include + +#include "Event.h" + + + +namespace caret { + + class EventGraphicsTimingOneWindow : public Event { + + public: + EventGraphicsTimingOneWindow(const int32_t windowIndex); + + virtual ~EventGraphicsTimingOneWindow(); + + EventGraphicsTimingOneWindow(const EventGraphicsTimingOneWindow&) = delete; + + EventGraphicsTimingOneWindow& operator=(const EventGraphicsTimingOneWindow&) = delete; + + int32_t getWindowIndex() const; + + // ADD_NEW_METHODS_HERE + + private: + int32_t m_windowIndex; + + // ADD_NEW_MEMBERS_HERE + + }; + +#ifdef __EVENT_GRAPHICS_TIMING_ONE_WINDOW_DECLARE__ + // +#endif // __EVENT_GRAPHICS_TIMING_ONE_WINDOW_DECLARE__ + +} // namespace +#endif //__EVENT_GRAPHICS_TIMING_ONE_WINDOW_H__ -- GitLab From 393e50a9a8d6651f938249717c7e6017d7b6d275 Mon Sep 17 00:00:00 2001 From: John Harwell Date: Tue, 26 Mar 2019 16:31:37 -0500 Subject: [PATCH 256/427] Macros: Revised editing of macro parameters to eliminated pop-up dialogs. --- src/Common/WuQMacroCommand.cxx | 24 + src/Common/WuQMacroCommand.h | 2 + src/Common/WuQMacroCommandParameter.cxx | 6 +- src/Common/WuQMacroDataValueTypeEnum.cxx | 12 +- src/Common/WuQMacroDataValueTypeEnum.h | 8 +- src/Common/WuQMacroGroupXmlStreamReader.cxx | 9 +- src/Common/WuQMacroGroupXmlStreamWriter.cxx | 9 +- src/GuiQt/CMakeLists.txt | 5 + src/GuiQt/WbMacroCustomDataInfo.cxx | 175 ++++++ src/GuiQt/WbMacroCustomDataInfo.h | 81 +++ src/GuiQt/WbMacroCustomOperationManager.cxx | 512 +++++++----------- src/GuiQt/WbMacroCustomOperationManager.h | 46 +- .../WbMacroCustomOperationModelRotation.cxx | 3 +- ...WbMacroCustomOperationOverlayCrossFade.cxx | 6 +- ...croCustomOperationSurfaceInterpolation.cxx | 4 +- ...croCustomOperationVolumeSliceIncrement.cxx | 3 +- src/GuiQt/WuQDialog.h | 3 - src/GuiQt/WuQMacroCommandParameterWidget.cxx | 396 ++++++++++++++ src/GuiQt/WuQMacroCommandParameterWidget.h | 118 ++++ .../WuQMacroCustomOperationManagerInterface.h | 22 +- src/GuiQt/WuQMacroDialog.cxx | 186 +------ src/GuiQt/WuQMacroDialog.h | 33 +- src/GuiQt/WuQMacroManager.cxx | 27 +- src/GuiQt/WuQMacroManager.h | 8 +- 24 files changed, 1086 insertions(+), 612 deletions(-) create mode 100644 src/GuiQt/WbMacroCustomDataInfo.cxx create mode 100644 src/GuiQt/WbMacroCustomDataInfo.h create mode 100644 src/GuiQt/WuQMacroCommandParameterWidget.cxx create mode 100644 src/GuiQt/WuQMacroCommandParameterWidget.h diff --git a/src/Common/WuQMacroCommand.cxx b/src/Common/WuQMacroCommand.cxx index 99c5b7715..904d18ca7 100644 --- a/src/Common/WuQMacroCommand.cxx +++ b/src/Common/WuQMacroCommand.cxx @@ -586,6 +586,30 @@ WuQMacroCommand::getParameterAtIndex(const int32_t index) const return m_parameters[index]; } +/** + * Get the index of the given parameter in this macro + * + * @param parameter + * Paramater for which index is requested + * @return + * Index of parameter or -1 If not found + */ +int32_t +WuQMacroCommand::getIndexOfParameter(const WuQMacroCommandParameter* parameter) const +{ + CaretAssert(parameter); + + const int32_t numParams = getNumberOfParameters(); + for (int32_t i = 0; i < numParams; i++) { + CaretAssertVectorIndex(m_parameters, i); + if (m_parameters[i] == parameter) { + return i; + } + } + + return -1; +} + /** * Remove all parameters in this command */ diff --git a/src/Common/WuQMacroCommand.h b/src/Common/WuQMacroCommand.h index cc4b8e8b4..af0fd5612 100644 --- a/src/Common/WuQMacroCommand.h +++ b/src/Common/WuQMacroCommand.h @@ -110,6 +110,8 @@ namespace caret { const WuQMacroCommandParameter* getParameterAtIndex(const int32_t) const; + int32_t getIndexOfParameter(const WuQMacroCommandParameter* parameter) const; + WuQMacroMouseEventInfo* getMouseEventInfo(); const WuQMacroMouseEventInfo* getMouseEventInfo() const; diff --git a/src/Common/WuQMacroCommandParameter.cxx b/src/Common/WuQMacroCommandParameter.cxx index f6986a8e2..c4bb92c5f 100644 --- a/src/Common/WuQMacroCommandParameter.cxx +++ b/src/Common/WuQMacroCommandParameter.cxx @@ -158,8 +158,7 @@ WuQMacroCommandParameter::setValue(const QVariant& value) } /** - * @return The custom type data type that defines the data when - * getDataType() is CUSTOM_DATA + * @return The custom type data type name */ QString WuQMacroCommandParameter::getCustomDataType() const @@ -168,8 +167,7 @@ WuQMacroCommandParameter::getCustomDataType() const } /** - * Set the custom type data type that defines the data when - * getDataType() is CUSTOM_DATA + * Set the custom type data type name * * @param customDataType * The custom data type diff --git a/src/Common/WuQMacroDataValueTypeEnum.cxx b/src/Common/WuQMacroDataValueTypeEnum.cxx index caa09e907..aded8ebc0 100644 --- a/src/Common/WuQMacroDataValueTypeEnum.cxx +++ b/src/Common/WuQMacroDataValueTypeEnum.cxx @@ -110,14 +110,14 @@ WuQMacroDataValueTypeEnum::initialize() "INVALID", "Invalid")); + enumData.push_back(WuQMacroDataValueTypeEnum(AXIS, + "AXIS", + "Axis")); + enumData.push_back(WuQMacroDataValueTypeEnum(BOOLEAN, "BOOLEAN", "Boolean")); - enumData.push_back(WuQMacroDataValueTypeEnum(CUSTOM_DATA, - "CUSTOM_DATA", - "CustomData")); - enumData.push_back(WuQMacroDataValueTypeEnum(FLOAT, "FLOAT", "Float")); @@ -137,6 +137,10 @@ WuQMacroDataValueTypeEnum::initialize() enumData.push_back(WuQMacroDataValueTypeEnum(STRING, "STRING", "String")); + + enumData.push_back(WuQMacroDataValueTypeEnum(STRING_LIST, + "STRING_LIST", + "StringList")); } /** diff --git a/src/Common/WuQMacroDataValueTypeEnum.h b/src/Common/WuQMacroDataValueTypeEnum.h index f061d83e4..bbb7d5512 100644 --- a/src/Common/WuQMacroDataValueTypeEnum.h +++ b/src/Common/WuQMacroDataValueTypeEnum.h @@ -37,10 +37,10 @@ public: enum Enum { /** Invalid */ INVALID, + /** Axis */ + AXIS, /** Boolean */ BOOLEAN, - /** Custom data that is not a fundamental type used with Custom Operations */ - CUSTOM_DATA, /** Float */ FLOAT, /** Integer */ @@ -50,7 +50,9 @@ public: /** None (no data value associated with object */ NONE, /** String */ - STRING + STRING, + /** String List*/ + STRING_LIST }; diff --git a/src/Common/WuQMacroGroupXmlStreamReader.cxx b/src/Common/WuQMacroGroupXmlStreamReader.cxx index 3e048c20c..2f243521d 100644 --- a/src/Common/WuQMacroGroupXmlStreamReader.cxx +++ b/src/Common/WuQMacroGroupXmlStreamReader.cxx @@ -454,6 +454,9 @@ WuQMacroGroupXmlStreamReader::readMacroCommandParameter(QXmlStreamReader& xmlRea QVariant value; switch (dataType) { + case WuQMacroDataValueTypeEnum::AXIS: + value.setValue(valueString.toString()); + break; case WuQMacroDataValueTypeEnum::INVALID: value.setValue(QString("")); break; @@ -463,9 +466,6 @@ WuQMacroGroupXmlStreamReader::readMacroCommandParameter(QXmlStreamReader& xmlRea value.setValue(boolValue); } break; - case WuQMacroDataValueTypeEnum::CUSTOM_DATA: - value.setValue(valueString.toString()); - break; case WuQMacroDataValueTypeEnum::FLOAT: { const double floatValue = valueString.toFloat(); @@ -487,6 +487,9 @@ WuQMacroGroupXmlStreamReader::readMacroCommandParameter(QXmlStreamReader& xmlRea case WuQMacroDataValueTypeEnum::STRING: value.setValue(valueString.toString()); break; + case WuQMacroDataValueTypeEnum::STRING_LIST: + value.setValue(valueString.toString()); + break; } WuQMacroCommandParameter* parameter = new WuQMacroCommandParameter(dataType, diff --git a/src/Common/WuQMacroGroupXmlStreamWriter.cxx b/src/Common/WuQMacroGroupXmlStreamWriter.cxx index cf2e0fa47..04280a192 100644 --- a/src/Common/WuQMacroGroupXmlStreamWriter.cxx +++ b/src/Common/WuQMacroGroupXmlStreamWriter.cxx @@ -209,12 +209,12 @@ WuQMacroGroupXmlStreamWriter::writeMacroCommandToXML(const WuQMacroCommand* macr switch (parameter->getDataType()) { case WuQMacroDataValueTypeEnum::INVALID: break; + case WuQMacroDataValueTypeEnum::AXIS: + stringValue = value.toString(); + break; case WuQMacroDataValueTypeEnum::BOOLEAN: stringValue = (value.toBool() ? VALUE_BOOL_TRUE : VALUE_BOOL_FALSE); break; - case WuQMacroDataValueTypeEnum::CUSTOM_DATA: - stringValue = value.toString(); - break; case WuQMacroDataValueTypeEnum::FLOAT: stringValue = QString::number(value.toFloat()); break; @@ -230,6 +230,9 @@ WuQMacroGroupXmlStreamWriter::writeMacroCommandToXML(const WuQMacroCommand* macr case WuQMacroDataValueTypeEnum::STRING: stringValue = value.toString(); break; + case WuQMacroDataValueTypeEnum::STRING_LIST: + stringValue = value.toString(); + break; } m_xmlStreamWriter->writeAttribute(ATTRIBUTE_MACRO_COMMAND_PARAMETER_NAME, parameter->getName()); diff --git a/src/GuiQt/CMakeLists.txt b/src/GuiQt/CMakeLists.txt index b6e614bdd..e10179000 100644 --- a/src/GuiQt/CMakeLists.txt +++ b/src/GuiQt/CMakeLists.txt @@ -206,6 +206,7 @@ ELSE() WuQGroupBoxExclusiveWidget.h WuQImageLabel.h WuQListWidget.h + WuQMacroCommandParameterWidget.h WuQMacroCopyDialog.h WuQMacroCreateDialog.h WuQMacroDialog.h @@ -456,6 +457,7 @@ VolumePropertiesEditorDialog.h VolumeSurfaceOutlineColorOrTabViewController.h VolumeSurfaceOutlineSetViewController.h VolumeSurfaceOutlineViewController.h +WbMacroCustomDataInfo.h WbMacroCustomDataTypeEnum.h WbMacroCustomOperationBase.h WbMacroCustomOperationDelay.h @@ -480,6 +482,7 @@ WuQGridLayoutGroup.h WuQGroupBoxExclusiveWidget.h WuQImageLabel.h WuQListWidget.h +WuQMacroCommandParameterWidget.h WuQMacroCopyDialog.h WuQMacroCreateDialog.h WuQMacroCustomOperationManagerInterface.h @@ -719,6 +722,7 @@ VolumePropertiesEditorDialog.cxx VolumeSurfaceOutlineColorOrTabViewController.cxx VolumeSurfaceOutlineSetViewController.cxx VolumeSurfaceOutlineViewController.cxx +WbMacroCustomDataInfo.cxx WbMacroCustomDataTypeEnum.cxx WbMacroCustomOperationBase.cxx WbMacroCustomOperationDelay.cxx @@ -743,6 +747,7 @@ WuQGridLayoutGroup.cxx WuQGroupBoxExclusiveWidget.cxx WuQImageLabel.cxx WuQListWidget.cxx +WuQMacroCommandParameterWidget.cxx WuQMacroCopyDialog.cxx WuQMacroCreateDialog.cxx WuQMacroDialog.cxx diff --git a/src/GuiQt/WbMacroCustomDataInfo.cxx b/src/GuiQt/WbMacroCustomDataInfo.cxx new file mode 100644 index 000000000..5dfd0a437 --- /dev/null +++ b/src/GuiQt/WbMacroCustomDataInfo.cxx @@ -0,0 +1,175 @@ + +/*LICENSE_START*/ +/* + * Copyright (C) 2019 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + +#define __WB_MACRO_CUSTOM_DATA_INFO_DECLARE__ +#include "WbMacroCustomDataInfo.h" +#undef __WB_MACRO_CUSTOM_DATA_INFO_DECLARE__ + +#include + +#include "CaretAssert.h" +using namespace caret; + + + +/** + * \class caret::WbMacroCustomDataInfo + * \brief Information about custom data such as valid data range + * \ingroup GuiQt + */ + +/** + * Constructor. + */ +WbMacroCustomDataInfo::WbMacroCustomDataInfo(const WuQMacroDataValueTypeEnum::Enum dataType) +: CaretObject(), +m_dataType(dataType) +{ + m_integerRange[0] = std::numeric_limits::min(); + m_integerRange[1] = std::numeric_limits::max(); + + m_floatRange[0] = -std::numeric_limits::max(); + m_floatRange[1] = std::numeric_limits::max(); +} + +/** + * Destructor. + */ +WbMacroCustomDataInfo::~WbMacroCustomDataInfo() +{ +} + +/** + * Copy constructor. + * @param obj + * Object that is copied. + */ +WbMacroCustomDataInfo::WbMacroCustomDataInfo(const WbMacroCustomDataInfo& obj) +: CaretObject(obj) +{ + this->copyHelperWbMacroCustomDataInfo(obj); +} + +/** + * Assignment operator. + * @param obj + * Data copied from obj to this. + * @return + * Reference to this object. + */ +WbMacroCustomDataInfo& +WbMacroCustomDataInfo::operator=(const WbMacroCustomDataInfo& obj) +{ + if (this != &obj) { + CaretObject::operator=(obj); + this->copyHelperWbMacroCustomDataInfo(obj); + } + return *this; +} + +/** + * Helps with copying an object of this type. + * @param obj + * Object that is copied. + */ +void +WbMacroCustomDataInfo::copyHelperWbMacroCustomDataInfo(const WbMacroCustomDataInfo& obj) +{ + m_dataType = obj.m_dataType; + m_floatRange = obj.m_floatRange; + m_integerRange = obj.m_integerRange; + m_stringListValues = obj.m_stringListValues; +} + +/** + * @return The data type + */ +WuQMacroDataValueTypeEnum::Enum +WbMacroCustomDataInfo::getDataType() const +{ + return m_dataType; +} + +/** + * @return Range for float data + */ +std::array +WbMacroCustomDataInfo::getFloatRange() const +{ + return m_floatRange; +} + +/** + * Set the range of float data + * + * @param range + * Range of the float data + */ +void +WbMacroCustomDataInfo::setFloatRange(const std::array& range) +{ + m_floatRange = range; +} + +/** + * @return Range for integer data + */ +std::array +WbMacroCustomDataInfo::getIntegerRange() const +{ + return m_integerRange; +} + +/** + * Set the range of integer data + * + * @param range + * Range of the integer data + */ +void +WbMacroCustomDataInfo::setIntegerRange(const std::array& range) +{ + m_integerRange = range; +} + +/** + * @return List of string values + */ +std::vector +WbMacroCustomDataInfo::getStringListValues() const +{ + return m_stringListValues; +} + +/** + * Set the list of string values + * + * @param range + * List of string values + */ +void +WbMacroCustomDataInfo::setStringListValues(const std::vector& values) +{ + m_stringListValues = values; +} + + + diff --git a/src/GuiQt/WbMacroCustomDataInfo.h b/src/GuiQt/WbMacroCustomDataInfo.h new file mode 100644 index 000000000..87aa81f3c --- /dev/null +++ b/src/GuiQt/WbMacroCustomDataInfo.h @@ -0,0 +1,81 @@ +#ifndef __WB_MACRO_CUSTOM_DATA_INFO_H__ +#define __WB_MACRO_CUSTOM_DATA_INFO_H__ + +/*LICENSE_START*/ +/* + * Copyright (C) 2019 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + + +#include +#include + +#include "CaretObject.h" +#include "WuQMacroDataValueTypeEnum.h" + + +namespace caret { + + class WbMacroCustomDataInfo : public CaretObject { + + public: + WbMacroCustomDataInfo(const WuQMacroDataValueTypeEnum::Enum dataType); + + virtual ~WbMacroCustomDataInfo(); + + WbMacroCustomDataInfo(const WbMacroCustomDataInfo& obj); + + WbMacroCustomDataInfo& operator=(const WbMacroCustomDataInfo& obj); + + WuQMacroDataValueTypeEnum::Enum getDataType() const; + + std::array getFloatRange() const; + + void setFloatRange(const std::array& range); + + std::array getIntegerRange() const; + + void setIntegerRange(const std::array& range); + + std::vector getStringListValues() const; + + void setStringListValues(const std::vector& values); + + // ADD_NEW_METHODS_HERE + + private: + void copyHelperWbMacroCustomDataInfo(const WbMacroCustomDataInfo& obj); + + WuQMacroDataValueTypeEnum::Enum m_dataType; + + std::array m_floatRange; + + std::array m_integerRange; + + std::vector m_stringListValues; + + // ADD_NEW_MEMBERS_HERE + + }; + +#ifdef __WB_MACRO_CUSTOM_DATA_INFO_DECLARE__ + // +#endif // __WB_MACRO_CUSTOM_DATA_INFO_DECLARE__ + +} // namespace +#endif //__WB_MACRO_CUSTOM_DATA_INFO_H__ diff --git a/src/GuiQt/WbMacroCustomOperationManager.cxx b/src/GuiQt/WbMacroCustomOperationManager.cxx index 183c0ee23..5ba5e6292 100644 --- a/src/GuiQt/WbMacroCustomOperationManager.cxx +++ b/src/GuiQt/WbMacroCustomOperationManager.cxx @@ -29,6 +29,7 @@ #include "BrowserTabContent.h" #include "CaretAssert.h" #include "CaretLogger.h" +#include "CaretMappableDataFile.h" #include "EventCaretMappableDataFilesGet.h" #include "EventManager.h" #include "EventSurfacesGet.h" @@ -44,6 +45,7 @@ #include "WbMacroCustomOperationTypeEnum.h" #include "WbMacroCustomOperationVolumeSliceIncrement.h" #include "WbMacroCustomOperationVolumeToSurfaceCrossFade.h" +#include "WbMacroCustomDataInfo.h" #include "WuQMacroCommand.h" #include "WuQMacroCommandParameter.h" #include "WuQMessageBox.h" @@ -83,89 +85,21 @@ WbMacroCustomOperationManager::toString() const } /** - * Edit overlay index + * Get names of all surfaces * - * @param parentWidget - * Parent widget for any dialogs - * @param parameter - * Parameter for editing - * @return - * True if the parameter was modified - */ -bool -WbMacroCustomOperationManager::editOverlayIndex(QWidget* parentWidget, - WuQMacroCommandParameter* parameter) -{ - bool okFlag(false); - const int32_t currentValue(parameter->getValue().toInt()); - const int32_t minimumValue(1); - const int32_t maximumValue(BrainConstants::MAXIMUM_NUMBER_OF_OVERLAYS); - const int32_t stepValue(1); - int32_t index = QInputDialog::getInt(parentWidget, - "Overlay Index", - "Overlay Index (1 is at top)", - currentValue, - minimumValue, - maximumValue, - stepValue, - &okFlag); - if (okFlag) { - parameter->setValue(index); - } - return okFlag; -} - -/** - * Edit screen axis - * - * @param parentWidget - * Parent widget for any dialogs - * @param parameter - * Parameter for editing - * @return - * True if the parameter was modified - */ -bool -WbMacroCustomOperationManager::editScreenAxis(QWidget* parentWidget, - WuQMacroCommandParameter* parameter) -{ - QStringList axisList; - axisList.append("X"); - axisList.append("Y"); - axisList.append("Z"); - - bool okFlag(false); - const bool editableFlag(false); - QString selectedName = QInputDialog::getItem(parentWidget, - "Screen Axis", - parameter->getName(), - axisList, - 0, - editableFlag, - &okFlag); - if (okFlag) { - parameter->setValue(selectedName); - } - return okFlag; -} - -/** - * Edit surface selection - * - * @param parentWidget - * Parent widget for any dialogs - * @param parameter - * Parameter for editing + * @param surfaceNamesOut + * Output with names of surfaces * @param errorMessageOut - * Output error message if modifying parameter fails + * Ouput with error messages * @return - * True if the parameter was modified + * True if surface names are valid, else false */ bool -WbMacroCustomOperationManager::editSurface(QWidget* parentWidget, - WuQMacroCommandParameter* parameter, - QString& errorMessageOut) +WbMacroCustomOperationManager::getSurfaceNames(std::vector& surfaceNamesOut, + QString& errorMessageOut) { + surfaceNamesOut.clear(); + EventSurfacesGet surfacesEvent; EventManager::get()->sendEvent(surfacesEvent.getPointer()); std::vector surfaces = surfacesEvent.getSurfaces(); @@ -174,22 +108,10 @@ WbMacroCustomOperationManager::editSurface(QWidget* parentWidget, errorMessageOut = "There are no surfaces available"; } else { - QStringList surfaceNamesList; for (const auto s : surfaces) { - surfaceNamesList.append(s->getFileNameNoPath()); - } - - const bool editableFlag(false); - QString selectedName = QInputDialog::getItem(parentWidget, - "Choose Surface", - parameter->getName(), - surfaceNamesList, - 0, - editableFlag, - &okFlag); - if (okFlag) { - parameter->setValue(selectedName); + surfaceNamesOut.push_back(s->getFileNameNoPath()); } + okFlag = true; } return okFlag; @@ -198,37 +120,54 @@ WbMacroCustomOperationManager::editSurface(QWidget* parentWidget, /** * Get the map file in the overlay for this command * - * @param parentWidget - * Parent widget for any dialogs + * @param browserWindowIndex + * Index of browser window * @param macroCommand * Macro command containing parameter being edited - * @param mapFilesOut - * Map files in the overlay + * @param overlayFileParameter + * Parameter for overlay file. Note that index of the overlay + * will be obtained from the most previous parameter that is of + * type OVERLAY_INDEX. + * @param mapFileNamesOut + * Output with map file names * @param selectedMapFileOut - * Selected map file in the overlay + * Map file selected in overlay or NULL if none selected + * @param selectedMapFileMapNamesOut + * Names of maps in selected map file, empty if no map file * @param errorMessageOut * Output error message if finding map files fails * @return - * True if the parameter was modified + * True if map file names valid */ bool -WbMacroCustomOperationManager::getMapFilesInOverlay(QWidget* parentWidget, - WuQMacroCommand* macroCommand, - std::vector& mapFilesOut, - CaretMappableDataFile* &selectedMapFileOut, - QString& errorMessageOut) +WbMacroCustomOperationManager::getMapFileNamesInOverlay(const int32_t browserWindowIndex, + const WuQMacroCommand* macroCommand, + const WuQMacroCommandParameter* overlayFileParameter, + std::vector& mapFileNamesOut, + CaretMappableDataFile* &selectedMapFileOut, + std::vector& selectedMapFileMapNamesOut, + QString& errorMessageOut) { - mapFilesOut.clear(); + CaretAssert(macroCommand); + CaretAssert(overlayFileParameter); + + mapFileNamesOut.clear(); selectedMapFileOut = NULL; + selectedMapFileMapNamesOut.clear(); /* * Find the overlay index parameter that should be before this parameter */ + const int32_t parameterIndex = macroCommand->getIndexOfParameter(overlayFileParameter); + if (parameterIndex < 0) { + errorMessageOut = "Parameter is invalid for command"; + return false; + } + int32_t overlayIndex(-1); - const int32_t numParams = macroCommand->getNumberOfParameters(); - for (int32_t ip = 0; ip < numParams; ip++) { + for (int32_t ip = (parameterIndex - 1); ip >= 0; --ip) { const WuQMacroCommandParameter* p = macroCommand->getParameterAtIndex(ip); - if (p->getDataType() == WuQMacroDataValueTypeEnum::CUSTOM_DATA) { + if (p->getDataType() == WuQMacroDataValueTypeEnum::INTEGER) { bool valid(false); WbMacroCustomDataTypeEnum::Enum customType = WbMacroCustomDataTypeEnum::fromName(p->getCustomDataType(), &valid); @@ -242,7 +181,7 @@ WbMacroCustomOperationManager::getMapFilesInOverlay(QWidget* parentWidget, } /* - * Overlay index is 1..N for user + * Overlay index is 1..N for user so need to decrement */ if (overlayIndex < 1) { errorMessageOut = "Unable to find overlay index"; @@ -250,10 +189,10 @@ WbMacroCustomOperationManager::getMapFilesInOverlay(QWidget* parentWidget, } --overlayIndex; - BrowserTabContent* tabContent = getTabContent(parentWidget, - "Window containing overlay", + BrowserTabContent* tabContent = getTabContent(browserWindowIndex, errorMessageOut); if (tabContent == NULL) { + errorMessageOut = "Unable to find tab content"; return false; } @@ -266,240 +205,184 @@ WbMacroCustomOperationManager::getMapFilesInOverlay(QWidget* parentWidget, } int32_t selectedMapIndex(-1); - overlay->getSelectionData(mapFilesOut, - selectedMapFileOut, + std::vector mapFiles; + CaretMappableDataFile* selectedMapFile(NULL); + overlay->getSelectionData(mapFiles, + selectedMapFile, selectedMapIndex); - if (mapFilesOut.empty()) { + if (mapFiles.empty()) { errorMessageOut = "The overlay does not contain any files"; return false; } - return true; -} - -/** - * Edit overlay file - * - * @param parentWidget - * Parent widget for any dialogs - * @param macroCommand - * Macro command containing parameter being edited - * @param parameter - * Parameter for editing - * @param errorMessageOut - * Output error message if modifying parameter fails - * @return - * True if the parameter was modified - */ -bool -WbMacroCustomOperationManager::editOverlayFile(QWidget* parentWidget, - WuQMacroCommand* macroCommand, - WuQMacroCommandParameter* parameter, - QString& errorMessageOut) -{ - std::vector mapFiles; - CaretMappableDataFile* selectedMapFile(NULL); - if ( ! getMapFilesInOverlay(parentWidget, - macroCommand, - mapFiles, - selectedMapFile, - errorMessageOut)) { - return false; - } - - const QString selectedFileName = parameter->getValue().toString(); - int32_t selectedIndex(0); - QStringList fileNameList; + selectedMapFileOut = selectedMapFile; for (const auto mf : mapFiles) { - const QString name(mf->getFileNameNoPath()); - if (selectedFileName == name) { - selectedIndex = fileNameList.count(); + mapFileNamesOut.push_back(mf->getFileNameNoPath()); + } + if (selectedMapFileOut != NULL) { + const int32_t numMaps = selectedMapFileOut->getNumberOfMaps(); + for (int32_t i = 0; i < numMaps; i++) { + selectedMapFileMapNamesOut.push_back(selectedMapFileOut->getMapName(i)); } - fileNameList.append(name); } - bool okFlag(false); - const bool editableFlag(false); - QString selectedName = QInputDialog::getItem(parentWidget, - "Choose map file", - "Choose map file", - fileNameList, - selectedIndex, - editableFlag, - &okFlag); - if (okFlag) { - parameter->setValue(selectedName); - return true; - } - - return false; + return true; } /** - * Edit overlay file + * Get info for data in a custom parameter * - * @param parentWidget - * Parent widget for any dialogs + * @param browserWindowIndex + * Index of browser window * @param macroCommand - * Macro command containing parameter being edited + * Macro command that contains the parameter * @param parameter - * Parameter for editing - * @param errorMessageOut - * Output error message if modifying parameter fails + * Parameter for info + * @param dataInfo + * Updated with data info in this method * @return - * True if the parameter was modified + * True if the data info is valid */ bool -WbMacroCustomOperationManager::editOverlayMap(QWidget* parentWidget, - WuQMacroCommand* macroCommand, - WuQMacroCommandParameter* parameter, - QString& errorMessageOut) +WbMacroCustomOperationManager::getCustomParameterDataInfo(const int32_t browserWindowIndex, + const WuQMacroCommand* macroCommand, + const WuQMacroCommandParameter* parameter, + WbMacroCustomDataInfo& dataInfoOut) { - /* - * Find the overlay file parameter that should be before this parameter - */ - QString filename; - const int32_t numParams = macroCommand->getNumberOfParameters(); - for (int32_t ip = 0; ip < numParams; ip++) { - const WuQMacroCommandParameter* p = macroCommand->getParameterAtIndex(ip); - if (p->getDataType() == WuQMacroDataValueTypeEnum::CUSTOM_DATA) { - bool valid(false); - WbMacroCustomDataTypeEnum::Enum customType = WbMacroCustomDataTypeEnum::fromName(p->getCustomDataType(), - &valid); - if (valid) { - if (customType == WbMacroCustomDataTypeEnum::OVERLAY_FILE_NAME_OR_FILE_INDEX) { - filename = p->getValue().toString(); - break; - } - } - } - } + CaretAssert(macroCommand); + CaretAssert(parameter); - if (filename.isEmpty()) { - errorMessageOut = "Name of overlay file is empty, select a file"; + const QString customTypeName(parameter->getCustomDataType()); + bool customTypeNameValid(false); + const WbMacroCustomDataTypeEnum::Enum userType = WbMacroCustomDataTypeEnum::fromName(customTypeName, + &customTypeNameValid); + if ( ! customTypeNameValid) { + CaretLogSevere("\"" + + customTypeName + + "\" is not a valid name for a custom parameter"); return false; } - EventCaretMappableDataFilesGet filesEvent; - EventManager::get()->sendEvent(filesEvent.getPointer()); - std::vector allMapFiles; - filesEvent.getAllFiles(allMapFiles); + const QString dataTypeName(WuQMacroDataValueTypeEnum::toName(dataInfoOut.getDataType())); - CaretMappableDataFile* mapFile(NULL); - for (auto cmdf : allMapFiles) { - if (cmdf->getFileName().endsWith(filename)) { - mapFile = cmdf; + bool unsupportedFlag(false); + switch (dataInfoOut.getDataType()) { + case WuQMacroDataValueTypeEnum::AXIS: + unsupportedFlag = true; + break; + case WuQMacroDataValueTypeEnum::BOOLEAN: + unsupportedFlag = true; + break; + case WuQMacroDataValueTypeEnum::FLOAT: + break; + case WuQMacroDataValueTypeEnum::INTEGER: + break; + case WuQMacroDataValueTypeEnum::INVALID: + unsupportedFlag = true; + break; + case WuQMacroDataValueTypeEnum::MOUSE: + unsupportedFlag = true; + break; + case WuQMacroDataValueTypeEnum::NONE: + unsupportedFlag = true; + break; + case WuQMacroDataValueTypeEnum::STRING: + unsupportedFlag = true; + break; + case WuQMacroDataValueTypeEnum::STRING_LIST: break; - } - } - if (mapFile == NULL) { - errorMessageOut = ("Unable to find data file with name \"" - + filename - + "\""); - return false; - } - - const QString currentMapName = parameter->getValue().toString(); - - const int32_t numMaps = mapFile->getNumberOfMaps(); - QStringList mapNameList; - for (int32_t i = 0; i < numMaps; i++) { - mapNameList.append(mapFile->getMapName(i)); - } - - bool okFlag(false); - const bool editableFlag(false); - QString selectedName = QInputDialog::getItem(parentWidget, - "Choose Map Name", - "Choose map name", - mapNameList, - 0, - editableFlag, - &okFlag); - if (okFlag) { - parameter->setValue(selectedName); - return true; - } - - return false; -} - -/** - * Is called to edit a macro command parameter with a CUSTOM_DATA data type - * - * @param parentWidget - * Parent widget for any dialogs - * @param macroCommand - * Macro command that contains the parameter for editing - * @param parameter - * Parameter for editing - * @return - * True if the parameter was modified - */ -bool -WbMacroCustomOperationManager::editCustomDataValueParameter(QWidget* parentWidget, - WuQMacroCommand* macroCommand, - WuQMacroCommandParameter* parameter) -{ - bool modFlag(false); - - CaretAssert(parentWidget); - CaretAssert(parameter); - - if (parameter->getDataType() != WuQMacroDataValueTypeEnum::CUSTOM_DATA) { - CaretLogSevere("Attempting to edit non-custom data type as custom data type"); - return false; } - const QString typeName(parameter->getCustomDataType()); - bool nameValid(false); - const WbMacroCustomDataTypeEnum::Enum userType = WbMacroCustomDataTypeEnum::fromName(typeName, - &nameValid); - if ( ! nameValid) { - CaretLogSevere("\"" - + typeName - + "\" is not a valid name for a custom parameter"); + if (unsupportedFlag) { + CaretLogSevere("Unsupported data type for parameter info " + + WuQMacroDataValueTypeEnum::toName(dataInfoOut.getDataType())); return false; } - QString errorMessage; + bool validFlag(false); + QString errorMessage("Unknown error"); + QString invalidTypeName; switch (userType) { case WbMacroCustomDataTypeEnum::OVERLAY_INDEX: - modFlag = editOverlayIndex(parentWidget, - parameter); + if (dataInfoOut.getDataType() == WuQMacroDataValueTypeEnum::INTEGER) { + dataInfoOut.setIntegerRange({ 1, BrainConstants::MAXIMUM_NUMBER_OF_OVERLAYS }); + validFlag = true; + } + else { + invalidTypeName = WuQMacroDataValueTypeEnum::toName(WuQMacroDataValueTypeEnum::INTEGER); + } break; case WbMacroCustomDataTypeEnum::OVERLAY_FILE_NAME_OR_FILE_INDEX: - modFlag = editOverlayFile(parentWidget, - macroCommand, - parameter, - errorMessage); + if (dataInfoOut.getDataType() == WuQMacroDataValueTypeEnum::STRING_LIST) { + std::vector filenames; + CaretMappableDataFile* selectedMapFile(NULL); + std::vector mapNames; + if (getMapFileNamesInOverlay(browserWindowIndex, + macroCommand, + parameter, + filenames, + selectedMapFile, + mapNames, + errorMessage)) { + dataInfoOut.setStringListValues(filenames); + validFlag = true; + } + } + else { + invalidTypeName = WuQMacroDataValueTypeEnum::toName(WuQMacroDataValueTypeEnum::STRING_LIST); + } break; case WbMacroCustomDataTypeEnum::OVERLAY_MAP_NAME_OR_MAP_INDEX: - modFlag = editOverlayMap(parentWidget, - macroCommand, - parameter, - errorMessage); + if (dataInfoOut.getDataType() == WuQMacroDataValueTypeEnum::STRING_LIST) { + std::vector filenames; + CaretMappableDataFile* selectedMapFile(NULL); + std::vector mapNames; + if (getMapFileNamesInOverlay(browserWindowIndex, + macroCommand, + parameter, + filenames, + selectedMapFile, + mapNames, + errorMessage)) { + dataInfoOut.setStringListValues(mapNames); + validFlag = true; + } + } + else { + invalidTypeName = WuQMacroDataValueTypeEnum::toName(WuQMacroDataValueTypeEnum::STRING_LIST); + } break; case WbMacroCustomDataTypeEnum::SCREEN_AXIS: - modFlag = editScreenAxis(parentWidget, - parameter); break; case WbMacroCustomDataTypeEnum::SURFACE: - modFlag = editSurface(parentWidget, - parameter, - errorMessage); + if (dataInfoOut.getDataType() == WuQMacroDataValueTypeEnum::STRING_LIST) { + std::vector surfaceNames; + if (getSurfaceNames(surfaceNames, errorMessage)) { + dataInfoOut.setStringListValues(surfaceNames); + validFlag = true; + } + } + else { + invalidTypeName = WuQMacroDataValueTypeEnum::toName(WuQMacroDataValueTypeEnum::STRING_LIST); + } break; } - if ( ! errorMessage.isEmpty()) { - WuQMessageBox::critical(parentWidget, - "Set Value", - errorMessage); + if ( ! validFlag) { + if ( ! invalidTypeName.isEmpty()) { + errorMessage = ("Custom parameter " + + customTypeName + + " data type should be " + + invalidTypeName + + " but is " + + dataTypeName); + } + + CaretLogSevere(errorMessage); } - return modFlag; - + return validFlag; } /** @@ -691,18 +574,15 @@ WbMacroCustomOperationManager::createCommand(const WbMacroCustomOperationTypeEnu * Get the active tab content in the active window. If there is more * than one window open, the user is prompted to select a window * - * @param parentWidget + * @param browserWindowIndex * Widget for any dialogs - * @param promptMessage - * Message displayed in dialog if more than one window is open * @param errorMessageOut * Output with error information if failure * @return * Pointer to active tab content or none found */ BrowserTabContent* -WbMacroCustomOperationManager::getTabContent(QWidget* parentWidget, - const QString& promptMessage, +WbMacroCustomOperationManager::getTabContent(const int32_t browserWindowIndex, QString& errorMessageOut) { errorMessageOut.clear(); @@ -712,34 +592,19 @@ WbMacroCustomOperationManager::getTabContent(QWidget* parentWidget, errorMessageOut = "No window are open. This should never happen."; return NULL; } - + BrowserTabContent* tabContent(NULL); BrainBrowserWindow* bbw(NULL); + if (allWindows.size() == 1) { CaretAssertVectorIndex(allWindows, 0); bbw = allWindows[0]; } else { - QStringList windowNameList; - for (const auto w : allWindows) { - windowNameList.append(w->windowTitle()); - } - - bool okFlag(false); - const bool editableFlag(false); - QString selectedName = QInputDialog::getItem(parentWidget, - "Choose window", - promptMessage, - windowNameList, - 0, - editableFlag, - &okFlag); - if (okFlag) { - for (auto w : allWindows) { - if (selectedName == w->windowTitle()) { - bbw = w; - break; - } + for (auto w : allWindows) { + if (w->getBrowserWindowIndex() == browserWindowIndex) { + bbw = w; + break; } } } @@ -753,4 +618,3 @@ WbMacroCustomOperationManager::getTabContent(QWidget* parentWidget, return tabContent; } - diff --git a/src/GuiQt/WbMacroCustomOperationManager.h b/src/GuiQt/WbMacroCustomOperationManager.h index 4421a28ed..59cbfff8d 100644 --- a/src/GuiQt/WbMacroCustomOperationManager.h +++ b/src/GuiQt/WbMacroCustomOperationManager.h @@ -49,10 +49,11 @@ namespace caret { WbMacroCustomOperationManager& operator=(const WbMacroCustomOperationManager&) = delete; - virtual bool editCustomDataValueParameter(QWidget* parent, - WuQMacroCommand* macroCommand, - WuQMacroCommandParameter* parameter); - + virtual bool getCustomParameterDataInfo(const int32_t browserWindowIndex, + const WuQMacroCommand* macroCommand, + const WuQMacroCommandParameter* parameter, + WbMacroCustomDataInfo& dataInfoOut) override; + virtual bool executeCustomOperationMacroCommand(QWidget* parent, const WuQMacroExecutorMonitor* executorMonitor, const WuQMacroExecutorOptions* executorOptions, @@ -75,36 +76,19 @@ namespace caret { WbMacroCustomOperationBase* createCommand(const WbMacroCustomOperationTypeEnum::Enum operationType); - bool editOverlayIndex(QWidget* parentWidget, - WuQMacroCommandParameter* parameter); - - bool editOverlayFile(QWidget* parentWidget, - WuQMacroCommand* macroCommand, - WuQMacroCommandParameter* parameter, - QString& errorMessageOut); + bool getSurfaceNames(std::vector& surfaceNamesOut, + QString& errorMessageOut); - bool editOverlayMap(QWidget* parentWidget, - WuQMacroCommand* macroCommand, - WuQMacroCommandParameter* parameter, - QString& errorMessageOut); + bool getMapFileNamesInOverlay(const int32_t browserWindowIndex, + const WuQMacroCommand* macroCommand, + const WuQMacroCommandParameter* overlayFileParameter, + std::vector& mapFileNamesOut, + CaretMappableDataFile* &selectedMapFileOut, + std::vector& selectedMapFileMapNamesOut, + QString& errorMessageOut); - bool editScreenAxis(QWidget* parentWidget, - WuQMacroCommandParameter* parameter); - - bool editSurface(QWidget* parentWidget, - WuQMacroCommandParameter* parameter, - QString& errorMessageOut); - - bool getMapFilesInOverlay(QWidget* parentWidget, - WuQMacroCommand* macroCommand, - std::vector& mapFilesOut, - CaretMappableDataFile* &selectedMapFileOut, - QString& errorMessageOut); - - BrowserTabContent* getTabContent(QWidget* parentWidget, - const QString& promptMessage, + BrowserTabContent* getTabContent(const int32_t browserWindowIndex, QString& errorMessageOut); - // ADD_NEW_MEMBERS_HERE }; diff --git a/src/GuiQt/WbMacroCustomOperationModelRotation.cxx b/src/GuiQt/WbMacroCustomOperationModelRotation.cxx index f201601bf..11092a2af 100644 --- a/src/GuiQt/WbMacroCustomOperationModelRotation.cxx +++ b/src/GuiQt/WbMacroCustomOperationModelRotation.cxx @@ -73,10 +73,9 @@ WbMacroCustomOperationModelRotation::createCommand() { const int32_t versionOne(1); - WuQMacroCommandParameter* paramOne = new WuQMacroCommandParameter(WuQMacroDataValueTypeEnum::CUSTOM_DATA, + WuQMacroCommandParameter* paramOne = new WuQMacroCommandParameter(WuQMacroDataValueTypeEnum::AXIS, "Screen Axis", "Y"); - paramOne->setCustomDataType(WbMacroCustomDataTypeEnum::toName(WbMacroCustomDataTypeEnum::SCREEN_AXIS)); QString errorMessage; WuQMacroCommand* command = WuQMacroCommand::newInstanceCustomCommand(WbMacroCustomOperationTypeEnum::toName(WbMacroCustomOperationTypeEnum::MODEL_ROTATION), diff --git a/src/GuiQt/WbMacroCustomOperationOverlayCrossFade.cxx b/src/GuiQt/WbMacroCustomOperationOverlayCrossFade.cxx index 0ca791e4e..dc6407aca 100644 --- a/src/GuiQt/WbMacroCustomOperationOverlayCrossFade.cxx +++ b/src/GuiQt/WbMacroCustomOperationOverlayCrossFade.cxx @@ -150,17 +150,17 @@ WbMacroCustomOperationOverlayCrossFade::createCommandVersionTwo() "(4) The selected overlay is removed;\n" "(5) The selected file/map remains displayed in its overlay\n"); - WuQMacroCommandParameter* paramSurfaceOne = new WuQMacroCommandParameter(WuQMacroDataValueTypeEnum::CUSTOM_DATA, + WuQMacroCommandParameter* paramSurfaceOne = new WuQMacroCommandParameter(WuQMacroDataValueTypeEnum::INTEGER, "Overlay Number", 1); paramSurfaceOne->setCustomDataType(WbMacroCustomDataTypeEnum::toName(WbMacroCustomDataTypeEnum::OVERLAY_INDEX)); - WuQMacroCommandParameter* paramSurfaceTwo = new WuQMacroCommandParameter(WuQMacroDataValueTypeEnum::CUSTOM_DATA, + WuQMacroCommandParameter* paramSurfaceTwo = new WuQMacroCommandParameter(WuQMacroDataValueTypeEnum::STRING_LIST, "Fade to File", ""); paramSurfaceTwo->setCustomDataType(WbMacroCustomDataTypeEnum::toName(WbMacroCustomDataTypeEnum::OVERLAY_FILE_NAME_OR_FILE_INDEX)); - WuQMacroCommandParameter* paramSurfaceThree = new WuQMacroCommandParameter(WuQMacroDataValueTypeEnum::CUSTOM_DATA, + WuQMacroCommandParameter* paramSurfaceThree = new WuQMacroCommandParameter(WuQMacroDataValueTypeEnum::STRING_LIST, "Fade to Map", ""); paramSurfaceThree->setCustomDataType(WbMacroCustomDataTypeEnum::toName(WbMacroCustomDataTypeEnum::OVERLAY_MAP_NAME_OR_MAP_INDEX)); diff --git a/src/GuiQt/WbMacroCustomOperationSurfaceInterpolation.cxx b/src/GuiQt/WbMacroCustomOperationSurfaceInterpolation.cxx index 746eb649b..c7ad4f268 100644 --- a/src/GuiQt/WbMacroCustomOperationSurfaceInterpolation.cxx +++ b/src/GuiQt/WbMacroCustomOperationSurfaceInterpolation.cxx @@ -79,12 +79,12 @@ WbMacroCustomOperationSurfaceInterpolation::createCommand() { const int32_t versionOne(1); - WuQMacroCommandParameter* paramSurfaceOne = new WuQMacroCommandParameter(WuQMacroDataValueTypeEnum::CUSTOM_DATA, + WuQMacroCommandParameter* paramSurfaceOne = new WuQMacroCommandParameter(WuQMacroDataValueTypeEnum::STRING_LIST, "Starting Surface", ""); paramSurfaceOne->setCustomDataType(WbMacroCustomDataTypeEnum::toName(WbMacroCustomDataTypeEnum::SURFACE)); - WuQMacroCommandParameter* paramSurfaceTwo = new WuQMacroCommandParameter(WuQMacroDataValueTypeEnum::CUSTOM_DATA, + WuQMacroCommandParameter* paramSurfaceTwo = new WuQMacroCommandParameter(WuQMacroDataValueTypeEnum::STRING_LIST, "Ending Surface", ""); paramSurfaceTwo->setCustomDataType(WbMacroCustomDataTypeEnum::toName(WbMacroCustomDataTypeEnum::SURFACE)); diff --git a/src/GuiQt/WbMacroCustomOperationVolumeSliceIncrement.cxx b/src/GuiQt/WbMacroCustomOperationVolumeSliceIncrement.cxx index d9814546c..3385f7d22 100644 --- a/src/GuiQt/WbMacroCustomOperationVolumeSliceIncrement.cxx +++ b/src/GuiQt/WbMacroCustomOperationVolumeSliceIncrement.cxx @@ -93,10 +93,9 @@ WbMacroCustomOperationVolumeSliceIncrement::createCommand() 1.0, errorMessage); if (command != NULL) { - WuQMacroCommandParameter* paramOne = new WuQMacroCommandParameter(WuQMacroDataValueTypeEnum::CUSTOM_DATA, + WuQMacroCommandParameter* paramOne = new WuQMacroCommandParameter(WuQMacroDataValueTypeEnum::AXIS, "Volume Axis", "Z"); - paramOne->setCustomDataType(WbMacroCustomDataTypeEnum::toName(WbMacroCustomDataTypeEnum::SCREEN_AXIS)); command->addParameter(paramOne); command->addParameter(WuQMacroDataValueTypeEnum::FLOAT, "Duration (secs)", diff --git a/src/GuiQt/WuQDialog.h b/src/GuiQt/WuQDialog.h index ed9602046..570dbb2aa 100644 --- a/src/GuiQt/WuQDialog.h +++ b/src/GuiQt/WuQDialog.h @@ -147,9 +147,6 @@ namespace caret { virtual void showEvent(QShowEvent* event); -// void setDialogSizeHint(const int32_t width, -// const int32_t height); - private slots: void clicked(QAbstractButton* button); diff --git a/src/GuiQt/WuQMacroCommandParameterWidget.cxx b/src/GuiQt/WuQMacroCommandParameterWidget.cxx new file mode 100644 index 000000000..8409e6b50 --- /dev/null +++ b/src/GuiQt/WuQMacroCommandParameterWidget.cxx @@ -0,0 +1,396 @@ + +/*LICENSE_START*/ +/* + * Copyright (C) 2019 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + +#define __WU_Q_MACRO_COMMAND_PARAMETER_WIDGET_DECLARE__ +#include "WuQMacroCommandParameterWidget.h" +#undef __WU_Q_MACRO_COMMAND_PARAMETER_WIDGET_DECLARE__ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "CaretAssert.h" +#include "WbMacroCustomDataInfo.h" +#include "WuQMacroCommand.h" +#include "WuQMacroCommandParameter.h" +#include "WuQMacroManager.h" + +using namespace caret; + + + +/** + * \class caret::WuQMacroCommandParameterWidget + * \brief Widget for display and editing a macro command parameter value + * \ingroup GuiQt + */ + +/** + * Constructor for command parameter widgets + * + * @param index + * Index of the parameter + * @param gridLayout + * Layout containing the widgets + * @param parent + * The parent widget + */ +WuQMacroCommandParameterWidget::WuQMacroCommandParameterWidget(const int32_t index, + QGridLayout* gridLayout, + QWidget* parent) +: QObject(parent), +m_index(index) +{ + m_nameLabel = new QLabel(); + + m_booleanOnAction = new QAction("On"); + m_booleanOnAction->setCheckable(true); + QObject::connect(m_booleanOnAction, &QAction::triggered, + this, &WuQMacroCommandParameterWidget::booleanOnActionTriggered); + QToolButton* booleanOnToolButton = new QToolButton(); + booleanOnToolButton->setDefaultAction(m_booleanOnAction); + + m_booleanOffAction = new QAction("Off"); + m_booleanOffAction->setCheckable(true); + QObject::connect(m_booleanOffAction, &QAction::triggered, + this, &WuQMacroCommandParameterWidget::booleanOffActionTriggered); + QToolButton* booleanOffToolButton = new QToolButton(); + booleanOffToolButton->setDefaultAction(m_booleanOffAction); + + QActionGroup* booleanActionGroup = new QActionGroup(this); + booleanActionGroup->addAction(m_booleanOffAction); + booleanActionGroup->addAction(m_booleanOnAction); + booleanActionGroup->setExclusive(true); + + m_comboBox = new QComboBox(); + m_comboBox->setFixedHeight(m_comboBox->sizeHint().height()); + m_comboBox->setEditable(true); + QObject::connect(m_comboBox, QOverload::of(&QComboBox::activated), + this, &WuQMacroCommandParameterWidget::comboBoxActivated); + + m_doubleSpinBox = new QDoubleSpinBox(); + m_doubleSpinBox->setFixedWidth(120); + m_doubleSpinBox->setFixedHeight(m_doubleSpinBox->sizeHint().height()); + QObject::connect(m_doubleSpinBox, QOverload::of(&QDoubleSpinBox::valueChanged), + this, &WuQMacroCommandParameterWidget::doubleSpinBoxValueChanged); + + m_lineEdit = new QLineEdit(); + m_lineEdit->setFixedHeight(m_lineEdit->sizeHint().height()); + QObject::connect(m_lineEdit, &QLineEdit::textEdited, + this, &WuQMacroCommandParameterWidget::lineEditTextEdited); + + m_spinBox = new QSpinBox(); + m_spinBox->setFixedWidth(120); + m_spinBox->setFixedHeight(m_spinBox->sizeHint().height()); + QObject::connect(m_spinBox, QOverload::of(&QSpinBox::valueChanged), + this, &WuQMacroCommandParameterWidget::spinBoxValueChanged); + + m_noValueWidget = new QWidget(); + m_noValueWidget->setFixedWidth(50); + m_noValueWidget->setFixedHeight(10); + + m_booleanWidget = new QWidget(); + QHBoxLayout* booleanLayout = new QHBoxLayout(m_booleanWidget); + booleanLayout->setContentsMargins(0, 0, 0, 0); + booleanLayout->addWidget(booleanOnToolButton); + booleanLayout->addWidget(booleanOffToolButton); + booleanLayout->addStretch(); + m_booleanWidget->setFixedHeight(m_booleanWidget->sizeHint().height()); + + m_stackedWidget = new QStackedWidget(); + m_stackedWidget->addWidget(m_booleanWidget); + m_stackedWidget->addWidget(m_comboBox); + m_stackedWidget->addWidget(m_doubleSpinBox); + m_stackedWidget->addWidget(m_lineEdit); + m_stackedWidget->addWidget(m_spinBox); + m_stackedWidget->addWidget(m_noValueWidget); + + /* + * Note: A QStackedWidget aligns its current widget at the top. + * So, align the label at the top so that the label and widget + * are approximately aligned + */ + const int32_t row = gridLayout->rowCount(); + gridLayout->addWidget(m_nameLabel, row, 0, Qt::AlignTop); + gridLayout->addWidget(m_stackedWidget, row, 1); +} + +/** + * Destructor. + */ +WuQMacroCommandParameterWidget::~WuQMacroCommandParameterWidget() +{ +} + +/** + * Update content of a command parameter + * + * @param windowIndex + * Index of window + * @param macroCommand + * Macro command containing the parameter + * @param parameter + * The parameter + */ +void +WuQMacroCommandParameterWidget::updateContent(int32_t windowIndex, + WuQMacroCommand* macroCommand, + WuQMacroCommandParameter* parameter) +{ + m_windowIndex = windowIndex; + m_macroCommand = macroCommand; + m_parameter = parameter; + + QWidget* activeWidget(m_noValueWidget); + + const bool validFlag(m_parameter != NULL); + if (validFlag) { + CaretAssert(m_macroCommand); + CaretAssert(m_parameter); + + m_nameLabel->setText(m_parameter->getName()); + + const QString customDataTypeName = parameter->getCustomDataType(); + const bool customDataFlag( ! customDataTypeName.isEmpty()); + + switch (parameter->getDataType()) { + case WuQMacroDataValueTypeEnum::AXIS: + { + m_comboBox->clear(); + m_comboBox->setEditable(false); + + std::vector axisValues { "X", "Y", "Z" }; + + int32_t defaultIndex(-1); + const QString value = m_parameter->getValue().toString(); + const int32_t numAxes = static_cast(axisValues.size()); + for (int32_t i = 0; i < numAxes; i++) { + CaretAssertVectorIndex(axisValues, i); + if (value == axisValues[i]) { + defaultIndex = i; + } + + m_comboBox->addItem(axisValues[i]); + } + + if (defaultIndex < 0) { + if ( ! value.isEmpty()) { + defaultIndex = m_comboBox->count(); + m_comboBox->addItem(value); + } + } + if (defaultIndex >= 0) { + m_comboBox->setCurrentIndex(defaultIndex); + } + + activeWidget = m_comboBox; + } + break; + case WuQMacroDataValueTypeEnum::BOOLEAN: + { + if (parameter->getValue().toBool()) { + m_booleanOnAction->setChecked(true); + } + else { + m_booleanOffAction->setChecked(true); + } + activeWidget = m_booleanWidget; + } + break; + case WuQMacroDataValueTypeEnum::FLOAT: + { + QSignalBlocker blocker(m_doubleSpinBox); + m_doubleSpinBox->setValue(m_parameter->getValue().toFloat()); + activeWidget = m_doubleSpinBox; + } + break; + case WuQMacroDataValueTypeEnum::INTEGER: + { + QSignalBlocker blocker(m_spinBox); + m_spinBox->setValue(m_parameter->getValue().toInt()); + activeWidget = m_spinBox; + } + break; + case WuQMacroDataValueTypeEnum::INVALID: + break; + case WuQMacroDataValueTypeEnum::MOUSE: + break; + case WuQMacroDataValueTypeEnum::NONE: + break; + case WuQMacroDataValueTypeEnum::STRING: + m_lineEdit->setText(m_parameter->getValue().toString()); + activeWidget = m_lineEdit; + break; + case WuQMacroDataValueTypeEnum::STRING_LIST: + { + m_comboBox->clear(); + + std::vector stringValues; + bool editableFlag(false); + + if (customDataFlag) { + WbMacroCustomDataInfo customDataInfo(WuQMacroDataValueTypeEnum::STRING_LIST); + WuQMacroManager::instance()->getCustomParameterDataInfo(windowIndex, + macroCommand, + parameter, + customDataInfo); + stringValues = customDataInfo.getStringListValues(); + } + + int32_t defaultIndex(-1); + const QString value = m_parameter->getValue().toString(); + const int32_t numAxes = static_cast(stringValues.size()); + for (int32_t i = 0; i < numAxes; i++) { + CaretAssertVectorIndex(stringValues, i); + if (value == stringValues[i]) { + defaultIndex = i; + } + + m_comboBox->addItem(stringValues[i]); + } + + if (editableFlag) { + m_comboBox->setEditable(true); + if (defaultIndex < 0) { + if ( ! value.isEmpty()) { + defaultIndex = m_comboBox->count(); + m_comboBox->addItem(value); + } + } + } + else { + m_comboBox->setEditable(false); + } + + if (defaultIndex >= 0) { + m_comboBox->setCurrentIndex(defaultIndex); + } + + activeWidget = m_comboBox; + } + break; + } + } + + m_stackedWidget->setCurrentWidget(activeWidget); + + m_nameLabel->setVisible(validFlag); + m_stackedWidget->setVisible(validFlag); + +// std::cout << "Stacked " << m_index << " height " << m_stackedWidget->sizeHint().height() << std::endl; +// for (int32_t i = 0; i < m_stackedWidget->count(); i++) { +// QWidget* w = m_stackedWidget->widget(i); +// std::cout << " " << i << " height " << w->sizeHint().height() << " " << w->metaObject()->className() << std::endl; +// } +} + +/** + * Called when boolean OFF action is triggered + */ +void +WuQMacroCommandParameterWidget::booleanOffActionTriggered(bool) +{ + CaretAssert(m_parameter); + m_parameter->setValue(false); + + emit dataChanged(m_index); +} + +/** + * Called when boolean ON action is triggered + */ +void +WuQMacroCommandParameterWidget::booleanOnActionTriggered(bool) +{ + CaretAssert(m_parameter); + m_parameter->setValue(true); + + emit dataChanged(m_index); +} + +/** + * Called when combo box is activated + * + * @param index + * index of item selecteed + */ +void +WuQMacroCommandParameterWidget::comboBoxActivated(int /*index*/) +{ + CaretAssert(m_parameter); + m_parameter->setValue(m_comboBox->currentText()); + + emit dataChanged(m_index); +} + +/** + * Called when value of double spin box is changed + * + * @param value + * The new value + */ +void +WuQMacroCommandParameterWidget::doubleSpinBoxValueChanged(double value) +{ + CaretAssert(m_parameter); + m_parameter->setValue(static_cast(value)); + + emit dataChanged(m_index); +} + +/** + * Called when value of the line edit is changed + * + * @param text + * The new text value + */ +void +WuQMacroCommandParameterWidget::lineEditTextEdited(const QString& text) +{ + CaretAssert(m_parameter); + m_parameter->setValue(text); + + emit dataChanged(m_index); +} + +/** + * Called when value of double spin box is changed + * + * @param value + * The new value + */ +void +WuQMacroCommandParameterWidget::spinBoxValueChanged(int value) +{ + CaretAssert(m_parameter); + m_parameter->setValue(value); + + emit dataChanged(m_index); +} diff --git a/src/GuiQt/WuQMacroCommandParameterWidget.h b/src/GuiQt/WuQMacroCommandParameterWidget.h new file mode 100644 index 000000000..77bc4e928 --- /dev/null +++ b/src/GuiQt/WuQMacroCommandParameterWidget.h @@ -0,0 +1,118 @@ +#ifndef __WU_Q_MACRO_COMMAND_PARAMETER_WIDGET_H__ +#define __WU_Q_MACRO_COMMAND_PARAMETER_WIDGET_H__ + +/*LICENSE_START*/ +/* + * Copyright (C) 2019 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + + + +#include + +#include + +class QComboBox; +class QDoubleSpinBox; +class QGridLayout; +class QLabel; +class QLineEdit; +class QSpinBox; +class QStackedWidget; + +namespace caret { + + class WuQMacroCommand; + class WuQMacroCommandParameter; + + class WuQMacroCommandParameterWidget : public QObject { + + Q_OBJECT + + public: + WuQMacroCommandParameterWidget(const int32_t index, + QGridLayout* gridLayout, + QWidget* parent); + + virtual ~WuQMacroCommandParameterWidget(); + + WuQMacroCommandParameterWidget(const WuQMacroCommandParameterWidget&) = delete; + + WuQMacroCommandParameterWidget& operator=(const WuQMacroCommandParameterWidget&) = delete; + + void updateContent(int32_t windowIndex, + WuQMacroCommand* macroCommand, + WuQMacroCommandParameter* parameter); + + // ADD_NEW_METHODS_HERE + + signals: + void dataChanged(const int index); + + private slots: + void booleanOffActionTriggered(bool); + + void booleanOnActionTriggered(bool); + + void comboBoxActivated(int); + + void doubleSpinBoxValueChanged(double); + + void lineEditTextEdited(const QString&); + + void spinBoxValueChanged(int); + + private: + QLabel* m_nameLabel; + + const int32_t m_index; + + int32_t m_windowIndex = -1; + + WuQMacroCommand* m_macroCommand = NULL; + + WuQMacroCommandParameter* m_parameter = NULL; + + QStackedWidget* m_stackedWidget; + + QAction* m_booleanOnAction; + + QAction* m_booleanOffAction; + + QWidget* m_booleanWidget; + + QComboBox* m_comboBox; + + QDoubleSpinBox* m_doubleSpinBox; + + QLineEdit* m_lineEdit; + + QSpinBox* m_spinBox; + + QWidget* m_noValueWidget; + + // ADD_NEW_MEMBERS_HERE + + }; + +#ifdef __WU_Q_MACRO_COMMAND_PARAMETER_WIDGET_DECLARE__ + // +#endif // __WU_Q_MACRO_COMMAND_PARAMETER_WIDGET_DECLARE__ + +} // namespace +#endif //__WU_Q_MACRO_COMMAND_PARAMETER_WIDGET_H__ diff --git a/src/GuiQt/WuQMacroCustomOperationManagerInterface.h b/src/GuiQt/WuQMacroCustomOperationManagerInterface.h index 07a02e221..93589d674 100644 --- a/src/GuiQt/WuQMacroCustomOperationManagerInterface.h +++ b/src/GuiQt/WuQMacroCustomOperationManagerInterface.h @@ -29,6 +29,7 @@ class QWidget; namespace caret { class WuQMacroCommand; class WuQMacroCommandParameter; + class WbMacroCustomDataInfo; class WuQMacroExecutorMonitor; class WuQMacroExecutorOptions; @@ -44,20 +45,23 @@ namespace caret { WuQMacroCustomOperationManagerInterface& operator=(const WuQMacroCustomOperationManagerInterface&) = delete; /** - * Is called to edit a macro command parameter with a CUSTOM_DATA data type + * Get info for data in a custom parameter * - * @param parent - * Parent widget for any dialogs + * @param browserWindowIndex + * Index of browser window * @param macroCommand - * Macro command that contains the parameter for editing + * Macro command that contains the parameter * @param parameter - * Parameter for editing + * Parameter for info + * @param dataInfo + * Updated with data info in this method * @return - * True if the parameter was modified + * True if the data info is valid */ - virtual bool editCustomDataValueParameter(QWidget* parent, - WuQMacroCommand* macroCommand, - WuQMacroCommandParameter* parameter) = 0; + virtual bool getCustomParameterDataInfo(const int32_t browserWindowIndex, + const WuQMacroCommand* macroCommand, + const WuQMacroCommandParameter* parameter, + WbMacroCustomDataInfo& dataInfoOut) = 0; /** * Run a custom-defined macro command diff --git a/src/GuiQt/WuQMacroDialog.cxx b/src/GuiQt/WuQMacroDialog.cxx index 30a3619f9..0f797947e 100644 --- a/src/GuiQt/WuQMacroDialog.cxx +++ b/src/GuiQt/WuQMacroDialog.cxx @@ -56,6 +56,7 @@ #include "WuQMacro.h" #include "WuQMacroCopyDialog.h" #include "WuQMacroCommand.h" +#include "WuQMacroCommandParameterWidget.h" #include "WuQMacroGroup.h" #include "WuQMacroExecutor.h" #include "WuQMacroExecutorMonitor.h" @@ -682,6 +683,7 @@ WuQMacroDialog::createCommandDisplayWidget() m_parameterWidgetsGridLayout->setVerticalSpacing(static_cast(m_parameterWidgetsGridLayout->verticalSpacing() / 1.2)); m_parameterWidgetsGridLayout->setColumnStretch(0, 0); m_parameterWidgetsGridLayout->setColumnStretch(1, 100); + m_parameterWidgetsGridLayout->setRowStretch(1000, 1000); /* push items up */ QHBoxLayout* titleLayout = new QHBoxLayout(); titleLayout->setContentsMargins(0, 0, 0, 0); @@ -1084,22 +1086,29 @@ WuQMacroDialog::updateCommandWidget(WuQMacroCommand* command) int32_t numWidgets = static_cast(m_parameterWidgets.size()); for (int32_t i = numWidgets; i < numParams; i++) { - CommandParameterWidget* cpw = new CommandParameterWidget(i, + WuQMacroCommandParameterWidget* cpw = new WuQMacroCommandParameterWidget(i, m_parameterWidgetsGridLayout, this); - QObject::connect(cpw, &CommandParameterWidget::dataChanged, + QObject::connect(cpw, &WuQMacroCommandParameterWidget::dataChanged, this, &WuQMacroDialog::commandParamaterDataChanged); m_parameterWidgets.push_back(cpw); } + const QString windowID = m_runOptionsWindowComboBox->currentText(); + int32_t windowIndex = windowID.toInt(); + if (windowIndex > 0) { + --windowIndex; /* Range 1..N but need 0..N-1 */ + } for (int32_t i = 0; i < numParams; i++) { - m_parameterWidgets[i]->updateContent(command, + m_parameterWidgets[i]->updateContent(windowIndex, + command, command->getParameterAtIndex(i)); } numWidgets = static_cast(m_parameterWidgets.size()); for (int32_t i = numParams; i < numWidgets; i++) { - m_parameterWidgets[i]->updateContent(NULL, + m_parameterWidgets[i]->updateContent(-1, + NULL, NULL); } } @@ -2238,172 +2247,3 @@ WuQMacroDialog::createEditingToolButtonPixmap(const QWidget* widget, return pixmap; } -/** - * Constructor for command parameter widgets - * - * @param index - * Index of the parameter - * @param gridLayout - * Layout containing the widgets - * @param parent - * The parent widget - */ -CommandParameterWidget::CommandParameterWidget(const int32_t index, - QGridLayout* gridLayout, - QWidget* parent) -: QObject(parent), -m_index(index) -{ - m_label = new QLabel(); - - m_pushButton = new QPushButton(); - QObject::connect(m_pushButton, &QPushButton::clicked, - this, &CommandParameterWidget::pushButtonClicked); - - const int32_t row = gridLayout->rowCount(); - gridLayout->addWidget(m_pushButton, row, 0); - gridLayout->addWidget(m_label, row, 1, Qt::AlignLeft); -} - -/** - * Update content of a command parameter - * - * @param macroCommand - * Macro command containing the parameter - * @param parameter - * The parameter - */ -void -CommandParameterWidget::updateContent(WuQMacroCommand* macroCommand, - WuQMacroCommandParameter* parameter) -{ - m_macroCommand = macroCommand; - m_parameter = parameter; - const bool validFlag(m_parameter != NULL); - if (validFlag) { - if (parameter->getDataType() == WuQMacroDataValueTypeEnum::BOOLEAN) { - m_label->setText(parameter->getValue().toBool() - ? "On" - : "Off"); - } - else { - m_label->setText(m_parameter->getValue().toString()); - } - m_pushButton->setText(m_parameter->getName() + "..."); - } - m_label->setVisible(validFlag); - m_pushButton->setVisible(validFlag); -} - -/** - * Called when command parameter push button is clicked - */ -void -CommandParameterWidget::pushButtonClicked() -{ - CaretAssert(m_parameter); - - const WuQMacroDataValueTypeEnum::Enum dataType = m_parameter->getDataType(); - QVariant dataValue = m_parameter->getValue(); - const QString dataValueUpdateText = m_parameter->getName(); - - bool validFlag(false); - switch (dataType) { - case WuQMacroDataValueTypeEnum::BOOLEAN: - { - QStringList items; - items.push_back("On"); - items.push_back("Off"); - - bool ok(false); - int defaultIndex = (dataValue.toBool() ? 0 : 1); - const QString text = QInputDialog::getItem(m_pushButton, - "New Status", - dataValueUpdateText, - items, - defaultIndex, - false, - &ok); - if (ok - && ( ! text.isEmpty())) { - bool result(text == items.at(0)); - dataValue.setValue(result); - validFlag = true; - } - } - break; - case WuQMacroDataValueTypeEnum::CUSTOM_DATA: - { - validFlag = WuQMacroManager::instance()->editCustomDataValueParameter(m_pushButton, - m_macroCommand, - m_parameter); - if (validFlag) { - dataValue = m_parameter->getValue(); - } - } - break; - case WuQMacroDataValueTypeEnum::FLOAT: - { - bool ok(false); - const float f = QInputDialog::getDouble(m_pushButton, - "New Value", - dataValueUpdateText, - dataValue.toDouble(), - -2147483647, - 2147483647, - 3, - &ok); - if (ok) { - dataValue.setValue(f); - validFlag = true; - } - } - break; - case WuQMacroDataValueTypeEnum::INTEGER: - { - bool ok(false); - const int i = QInputDialog::getInt(m_pushButton, - "New Value", - dataValueUpdateText, - dataValue.toInt(), - -2147483647, - 2147483647, - 1, - &ok); - if (ok) { - dataValue.setValue(i); - validFlag = true; - } - } - break; - case WuQMacroDataValueTypeEnum::INVALID: - break; - case WuQMacroDataValueTypeEnum::MOUSE: - break; - case WuQMacroDataValueTypeEnum::NONE: - break; - case WuQMacroDataValueTypeEnum::STRING: - { - bool ok(false); - const QString text = QInputDialog::getText(m_pushButton, - "New Text", - dataValueUpdateText, - QLineEdit::Normal, - dataValue.toString(), - &ok); - if (ok) { - dataValue.setValue(text); - validFlag = true; - } - } - break; - } - - if (validFlag) { - m_parameter->setValue(dataValue); - - emit dataChanged(m_index); - } -} - - diff --git a/src/GuiQt/WuQMacroDialog.h b/src/GuiQt/WuQMacroDialog.h index 56abb83dd..2b50d1907 100644 --- a/src/GuiQt/WuQMacroDialog.h +++ b/src/GuiQt/WuQMacroDialog.h @@ -52,6 +52,7 @@ namespace caret { class CommandParameterWidget; class WuQMacro; class WuQMacroCommand; + class WuQMacroCommandParameterWidget; class WuQMacroGroup; class WuQMacroShortCutKeyComboBox; @@ -268,7 +269,7 @@ namespace caret { bool m_macroDescriptionCommandTextEditBlockUpdateFlag = false; - std::vector m_parameterWidgets; + std::vector m_parameterWidgets; QGridLayout* m_parameterWidgetsGridLayout; @@ -302,36 +303,6 @@ namespace caret { }; - class CommandParameterWidget : public QObject { - Q_OBJECT - - public: - CommandParameterWidget(const int32_t index, - QGridLayout* gridLayout, - QWidget* parent); - - void updateContent(WuQMacroCommand* macroCommand, - WuQMacroCommandParameter* parameter); - - QLabel* m_label; - - QPushButton* m_pushButton; - - signals: - void dataChanged(const int index); - - private slots: - void pushButtonClicked(); - - private: - const int32_t m_index; - - WuQMacroCommand* m_macroCommand = NULL; - - WuQMacroCommandParameter* m_parameter = NULL; - }; - - #ifdef __WU_Q_MACRO_DIALOG_DECLARE__ QByteArray WuQMacroDialog::s_previousDialogGeometry; #endif // __WU_Q_MACRO_DIALOG_DECLARE__ diff --git a/src/GuiQt/WuQMacroManager.cxx b/src/GuiQt/WuQMacroManager.cxx index 3d9dcc880..1fd3edc2f 100644 --- a/src/GuiQt/WuQMacroManager.cxx +++ b/src/GuiQt/WuQMacroManager.cxx @@ -1316,34 +1316,37 @@ WuQMacroManager::getShortCutKeysMask() } /** - * Is called to edit a macro command parameter with a CUSTOM_DATA data type + * Get information about a custom parameter that may be a range or + * a list of valid values * - * @param parent - * Parent widget for any dialogs + * @param browserWindowIndex + * Index of window * @param macroCommand * Macro command that contains the parameter for editing * @param parameter * Parameter for editing + * @param dataInfoOut + * Information about the data * @return * True if the parameter was modified */ bool -WuQMacroManager::editCustomDataValueParameter(QWidget* parent, - WuQMacroCommand* macroCommand, - WuQMacroCommandParameter* parameter) +WuQMacroManager::getCustomParameterDataInfo(const int32_t browserWindowIndex, + const WuQMacroCommand* macroCommand, + const WuQMacroCommandParameter* parameter, + WbMacroCustomDataInfo& dataInfoOut) { - bool modFlag(false); - + bool validFlag(false); if (m_customCommandManager != NULL) { - modFlag = m_customCommandManager->editCustomDataValueParameter(parent, + validFlag = m_customCommandManager->getCustomParameterDataInfo(browserWindowIndex, macroCommand, - parameter); + parameter, + dataInfoOut); } else { CaretLogSevere("No Macro Helper available for editing custom values in a macro parameter"); } - - return modFlag; + return validFlag; } /** diff --git a/src/GuiQt/WuQMacroManager.h b/src/GuiQt/WuQMacroManager.h index 3fca46537..15259ef31 100644 --- a/src/GuiQt/WuQMacroManager.h +++ b/src/GuiQt/WuQMacroManager.h @@ -39,6 +39,7 @@ class QWidget; namespace caret { + class WbMacroCustomDataInfo; class WuQMacro; class WuQMacroCommand; class WuQMacroCommandParameter; @@ -155,9 +156,10 @@ namespace caret { void macroWasModified(WuQMacro* macro); - bool editCustomDataValueParameter(QWidget* parent, - WuQMacroCommand* macroCommand, - WuQMacroCommandParameter* parameter); + bool getCustomParameterDataInfo(const int32_t browserWindowIndex, + const WuQMacroCommand* macroCommand, + const WuQMacroCommandParameter* parameter, + WbMacroCustomDataInfo& dataInfoOut); bool executeCustomOperationMacroCommand(QWidget* parent, const WuQMacroExecutorMonitor* executorMonitor, -- GitLab From 454e1e07fc58d850e2d36d555e3dc68a16140346 Mon Sep 17 00:00:00 2001 From: John Harwell Date: Wed, 27 Mar 2019 14:36:43 -0500 Subject: [PATCH 257/427] Macros: For overlay selection in a macro parameter, improve and fix selection of the file and map. --- src/GuiQt/WbMacroCustomOperationManager.cxx | 203 ++++++++++++++++--- src/GuiQt/WbMacroCustomOperationManager.h | 25 ++- src/GuiQt/WuQMacroCommandParameterWidget.cxx | 44 ++-- 3 files changed, 224 insertions(+), 48 deletions(-) diff --git a/src/GuiQt/WbMacroCustomOperationManager.cxx b/src/GuiQt/WbMacroCustomOperationManager.cxx index 5ba5e6292..ce121391a 100644 --- a/src/GuiQt/WbMacroCustomOperationManager.cxx +++ b/src/GuiQt/WbMacroCustomOperationManager.cxx @@ -117,6 +117,159 @@ WbMacroCustomOperationManager::getSurfaceNames(std::vector& surfaceName return okFlag; } +/** + * Get mappable files selection info + * + * @param macroCommand + * Macro command containing parameter being edited + * @param overlayFileParameter + * Parameter for overlay file selection + * @param mapNameParameter + * Parameter for map name selection + * @param mapFileNamesOut + * Output with map file names + * @param selectedFileNameOut + * Output with name of selected file + * @param mapNamesOut + * Output with name of maps in selected file + * @param selectedMapNameOut + * Name of selected map + * @param errorMessageOut + * Output error message if finding map files fails + * @return + * True if map file names valid + */ +bool +WbMacroCustomOperationManager::getMappableFilesSelection(const WuQMacroCommand* macroCommand, + const WuQMacroCommandParameter* overlayFileParameterIn, + const WuQMacroCommandParameter* mapParameterIn, + std::vector& mapFileNamesOut, + QString& selectedFileNameOut, + std::vector& mapNamesOut, + QString& selectedMapNameOut, + QString& errorMessageOut) +{ + CaretAssert(macroCommand); + mapFileNamesOut.clear(); + selectedFileNameOut.clear(); + mapNamesOut.clear(); + selectedMapNameOut.clear(); + errorMessageOut.clear(); + + const WuQMacroCommandParameter* fileParameter = overlayFileParameterIn; + const WuQMacroCommandParameter* mapParameter = mapParameterIn; + if ((fileParameter != NULL) + && (mapParameter != NULL)) { + /* OK, have both */ + } + else { + /* + * Map selection parameter should be immediately after file selection parameter + */ + int32_t fileParameterIndex(-1); + int32_t mapParameterIndex(-1); + if (fileParameter != NULL) { + fileParameterIndex = macroCommand->getIndexOfParameter(fileParameter); + mapParameterIndex = fileParameterIndex + 1; + } + else if (mapParameter != NULL) { + mapParameterIndex = macroCommand->getIndexOfParameter(mapParameter); + fileParameterIndex = mapParameterIndex - 1; + } + else { + errorMessageOut = "Both overlay file and map parameter are NULL, one must be valid"; + return false; + } + + if ((fileParameterIndex < 0) + || (fileParameterIndex >= macroCommand->getNumberOfParameters())) { + errorMessageOut.append("Unable to find file parameter. "); + } + if ((mapParameterIndex < 0) + || (mapParameterIndex >= macroCommand->getNumberOfParameters())) { + errorMessageOut.append("Unable to find map parameter. "); + } + if ( ! errorMessageOut.isEmpty()) { + return false; + } + + fileParameter = macroCommand->getParameterAtIndex(fileParameterIndex); + mapParameter = macroCommand->getParameterAtIndex(mapParameterIndex); + } + + CaretAssert(fileParameter); + CaretAssert(mapParameter); + + bool validFileParamFlag(false); + const WbMacroCustomDataTypeEnum::Enum overlayParamType = WbMacroCustomDataTypeEnum::fromName(fileParameter->getCustomDataType(), + &validFileParamFlag); + if (overlayParamType != WbMacroCustomDataTypeEnum::OVERLAY_FILE_NAME_OR_FILE_INDEX) { + errorMessageOut.append("Overlay file parameter is not of type OVERLAY_FILE_NAME_OR_FILE_INDEX. "); + } + + bool validMapNameParamFlag(false); + const WbMacroCustomDataTypeEnum::Enum mapParamType = WbMacroCustomDataTypeEnum::fromName(mapParameter->getCustomDataType(), + &validMapNameParamFlag); + if (mapParamType != WbMacroCustomDataTypeEnum::OVERLAY_MAP_NAME_OR_MAP_INDEX) { + errorMessageOut.append("Overlay map parameter is not of type OVERLAY_MAP_NAME_OR_MAP_INDEX. "); + } + + if ( ! errorMessageOut.isEmpty()) { + return false; + } + + const QString selectedFileName = fileParameter->getValue().toString(); + const QString selectedMapName = mapParameter->getValue().toString(); + + CaretMappableDataFile* selectedFile(NULL); + EventCaretMappableDataFilesGet mapFilesEvent; + EventManager::get()->sendEvent(mapFilesEvent.getPointer()); + std::vector allFiles; + mapFilesEvent.getAllFiles(allFiles); + if ( ! allFiles.empty()) { + for (auto mf : allFiles) { + CaretAssert(mf); + const QString name(mf->getFileNameNoPath()); + if ( ! selectedFileName.isEmpty()) { + if (name == selectedFileName) { + selectedFile = mf; + } + } + mapFileNamesOut.push_back(name); + } + + if (selectedFile == NULL) { + CaretAssertVectorIndex(allFiles, 0); + selectedFile = allFiles[0]; + } + CaretAssert(selectedFile); + selectedFileNameOut = selectedFile->getFileNameNoPath(); + + bool selectedMapNameFoundFlag(false); + const int32_t numMaps = selectedFile->getNumberOfMaps(); + for (int32_t i = 0; i < numMaps; i++) { + const QString name(selectedFile->getMapName(i)); + if ( ! selectedMapName.isEmpty()) { + if (selectedMapName == name) { + selectedMapNameFoundFlag = true; + } + } + mapNamesOut.push_back(name); + } + + if (selectedMapNameFoundFlag) { + selectedMapNameOut = selectedMapName; + } + else { + if (selectedFile->getNumberOfMaps() > 0) { + selectedMapNameOut = selectedFile->getMapName(0); + } + } + } + + return true; +} + /** * Get the map file in the overlay for this command * @@ -140,13 +293,13 @@ WbMacroCustomOperationManager::getSurfaceNames(std::vector& surfaceName * True if map file names valid */ bool -WbMacroCustomOperationManager::getMapFileNamesInOverlay(const int32_t browserWindowIndex, - const WuQMacroCommand* macroCommand, - const WuQMacroCommandParameter* overlayFileParameter, - std::vector& mapFileNamesOut, - CaretMappableDataFile* &selectedMapFileOut, - std::vector& selectedMapFileMapNamesOut, - QString& errorMessageOut) +WbMacroCustomOperationManager::getOverlayContents(const int32_t browserWindowIndex, + const WuQMacroCommand* macroCommand, + const WuQMacroCommandParameter* overlayFileParameter, + std::vector& mapFileNamesOut, + CaretMappableDataFile* &selectedMapFileOut, + std::vector& selectedMapFileMapNamesOut, + QString& errorMessageOut) { CaretAssert(macroCommand); CaretAssert(overlayFileParameter); @@ -316,15 +469,17 @@ WbMacroCustomOperationManager::getCustomParameterDataInfo(const int32_t browserW case WbMacroCustomDataTypeEnum::OVERLAY_FILE_NAME_OR_FILE_INDEX: if (dataInfoOut.getDataType() == WuQMacroDataValueTypeEnum::STRING_LIST) { std::vector filenames; - CaretMappableDataFile* selectedMapFile(NULL); std::vector mapNames; - if (getMapFileNamesInOverlay(browserWindowIndex, - macroCommand, - parameter, - filenames, - selectedMapFile, - mapNames, - errorMessage)) { + QString selectedFileName; + QString selectedMapName; + if (getMappableFilesSelection(macroCommand, + parameter, + NULL, + filenames, + selectedFileName, + mapNames, + selectedMapName, + errorMessage)) { dataInfoOut.setStringListValues(filenames); validFlag = true; } @@ -336,15 +491,17 @@ WbMacroCustomOperationManager::getCustomParameterDataInfo(const int32_t browserW case WbMacroCustomDataTypeEnum::OVERLAY_MAP_NAME_OR_MAP_INDEX: if (dataInfoOut.getDataType() == WuQMacroDataValueTypeEnum::STRING_LIST) { std::vector filenames; - CaretMappableDataFile* selectedMapFile(NULL); std::vector mapNames; - if (getMapFileNamesInOverlay(browserWindowIndex, - macroCommand, - parameter, - filenames, - selectedMapFile, - mapNames, - errorMessage)) { + QString selectedFileName; + QString selectedMapName; + if (getMappableFilesSelection(macroCommand, + NULL, + parameter, + filenames, + selectedFileName, + mapNames, + selectedMapName, + errorMessage)) { dataInfoOut.setStringListValues(mapNames); validFlag = true; } diff --git a/src/GuiQt/WbMacroCustomOperationManager.h b/src/GuiQt/WbMacroCustomOperationManager.h index 59cbfff8d..834e3c3a9 100644 --- a/src/GuiQt/WbMacroCustomOperationManager.h +++ b/src/GuiQt/WbMacroCustomOperationManager.h @@ -79,14 +79,23 @@ namespace caret { bool getSurfaceNames(std::vector& surfaceNamesOut, QString& errorMessageOut); - bool getMapFileNamesInOverlay(const int32_t browserWindowIndex, - const WuQMacroCommand* macroCommand, - const WuQMacroCommandParameter* overlayFileParameter, - std::vector& mapFileNamesOut, - CaretMappableDataFile* &selectedMapFileOut, - std::vector& selectedMapFileMapNamesOut, - QString& errorMessageOut); - + bool getOverlayContents(const int32_t browserWindowIndex, + const WuQMacroCommand* macroCommand, + const WuQMacroCommandParameter* overlayFileParameter, + std::vector& mapFileNamesOut, + CaretMappableDataFile* &selectedMapFileOut, + std::vector& selectedMapFileMapNamesOut, + QString& errorMessageOut); + + bool getMappableFilesSelection(const WuQMacroCommand* macroCommand, + const WuQMacroCommandParameter* overlayFileParameter, + const WuQMacroCommandParameter* mapParameter, + std::vector& mapFileNamesOut, + QString& selectedFileNameOut, + std::vector& mapNamesOut, + QString& selectedMapNameOut, + QString& errorMessageOut); + BrowserTabContent* getTabContent(const int32_t browserWindowIndex, QString& errorMessageOut); // ADD_NEW_MEMBERS_HERE diff --git a/src/GuiQt/WuQMacroCommandParameterWidget.cxx b/src/GuiQt/WuQMacroCommandParameterWidget.cxx index 8409e6b50..db778d99e 100644 --- a/src/GuiQt/WuQMacroCommandParameterWidget.cxx +++ b/src/GuiQt/WuQMacroCommandParameterWidget.cxx @@ -253,7 +253,6 @@ WuQMacroCommandParameterWidget::updateContent(int32_t windowIndex, m_comboBox->clear(); std::vector stringValues; - bool editableFlag(false); if (customDataFlag) { WbMacroCustomDataInfo customDataInfo(WuQMacroDataValueTypeEnum::STRING_LIST); @@ -265,28 +264,39 @@ WuQMacroCommandParameterWidget::updateContent(int32_t windowIndex, } int32_t defaultIndex(-1); - const QString value = m_parameter->getValue().toString(); - const int32_t numAxes = static_cast(stringValues.size()); - for (int32_t i = 0; i < numAxes; i++) { - CaretAssertVectorIndex(stringValues, i); - if (value == stringValues[i]) { - defaultIndex = i; + const QString selectedValue = m_parameter->getValue().toString(); + + const int32_t numValues = static_cast(stringValues.size()); + if (numValues > 0) { + for (int32_t i = 0; i < numValues; i++) { + CaretAssertVectorIndex(stringValues, i); + if (selectedValue == stringValues[i]) { + defaultIndex = i; + } + + m_comboBox->addItem(stringValues[i]); } - m_comboBox->addItem(stringValues[i]); - } - - if (editableFlag) { - m_comboBox->setEditable(true); - if (defaultIndex < 0) { - if ( ! value.isEmpty()) { - defaultIndex = m_comboBox->count(); - m_comboBox->addItem(value); + bool editableFlag(false); + if (editableFlag) { + m_comboBox->setEditable(true); + if (defaultIndex < 0) { + if ( ! selectedValue.isEmpty()) { + defaultIndex = m_comboBox->count(); + m_comboBox->addItem(selectedValue); + } } } + else { + m_comboBox->setEditable(false); + } } else { - m_comboBox->setEditable(false); + if ( ! selectedValue.isEmpty()) { + defaultIndex = m_comboBox->count(); + m_comboBox->addItem(selectedValue); + } + m_comboBox->setEditable(true); } if (defaultIndex >= 0) { -- GitLab From c0758a4bbd7045f86d36e202bd25f901aa13d844 Mon Sep 17 00:00:00 2001 From: John Harwell Date: Wed, 27 Mar 2019 15:02:40 -0500 Subject: [PATCH 258/427] Macros: Disable Macro Menu -> Record Macro when there are no macro groups (macro goes into a macro group). --- src/GuiQt/WbMacroCustomDataTypeEnum.cxx | 4 ---- src/GuiQt/WbMacroCustomDataTypeEnum.h | 2 -- src/GuiQt/WbMacroCustomOperationManager.cxx | 2 -- src/GuiQt/WuQMacroMenu.cxx | 3 ++- 4 files changed, 2 insertions(+), 9 deletions(-) diff --git a/src/GuiQt/WbMacroCustomDataTypeEnum.cxx b/src/GuiQt/WbMacroCustomDataTypeEnum.cxx index 773b2b02d..4df4a71d6 100644 --- a/src/GuiQt/WbMacroCustomDataTypeEnum.cxx +++ b/src/GuiQt/WbMacroCustomDataTypeEnum.cxx @@ -118,10 +118,6 @@ WbMacroCustomDataTypeEnum::initialize() "OVERLAY_MAP_NAME_OR_MAP_INDEX", "Overlay Map Name or Index")); - enumData.push_back(WbMacroCustomDataTypeEnum(SCREEN_AXIS, - "SCREEN_AXIS", - "Screen Axis")); - enumData.push_back(WbMacroCustomDataTypeEnum(SURFACE, "SURFACE", "Surface")); diff --git a/src/GuiQt/WbMacroCustomDataTypeEnum.h b/src/GuiQt/WbMacroCustomDataTypeEnum.h index bf10c22fb..1cc46abcf 100644 --- a/src/GuiQt/WbMacroCustomDataTypeEnum.h +++ b/src/GuiQt/WbMacroCustomDataTypeEnum.h @@ -41,8 +41,6 @@ public: OVERLAY_FILE_NAME_OR_FILE_INDEX, /** Name of map or index of map */ OVERLAY_MAP_NAME_OR_MAP_INDEX, - /** A Screen Axis */ - SCREEN_AXIS, /** Surface selection */ SURFACE }; diff --git a/src/GuiQt/WbMacroCustomOperationManager.cxx b/src/GuiQt/WbMacroCustomOperationManager.cxx index ce121391a..7a4ecf85d 100644 --- a/src/GuiQt/WbMacroCustomOperationManager.cxx +++ b/src/GuiQt/WbMacroCustomOperationManager.cxx @@ -510,8 +510,6 @@ WbMacroCustomOperationManager::getCustomParameterDataInfo(const int32_t browserW invalidTypeName = WuQMacroDataValueTypeEnum::toName(WuQMacroDataValueTypeEnum::STRING_LIST); } break; - case WbMacroCustomDataTypeEnum::SCREEN_AXIS: - break; case WbMacroCustomDataTypeEnum::SURFACE: if (dataInfoOut.getDataType() == WuQMacroDataValueTypeEnum::STRING_LIST) { std::vector surfaceNames; diff --git a/src/GuiQt/WuQMacroMenu.cxx b/src/GuiQt/WuQMacroMenu.cxx index 099f8c3a8..907fd3bc3 100644 --- a/src/GuiQt/WuQMacroMenu.cxx +++ b/src/GuiQt/WuQMacroMenu.cxx @@ -98,6 +98,7 @@ WuQMacroMenu::macroMenuAboutToShow() WuQMacroManager* macroManager = WuQMacroManager::instance(); CaretAssert(macroManager); + const bool hasMacroGroupFlag = ( ! macroManager->getActiveMacroGroups().empty()); bool editValidFlag(false); bool recordValidFlag(false); bool stopValidFlag(false); @@ -105,7 +106,7 @@ WuQMacroMenu::macroMenuAboutToShow() switch (macroManager->getMode()) { case WuQMacroModeEnum::OFF: editValidFlag = true; - recordValidFlag = true; + recordValidFlag = hasMacroGroupFlag; break; case WuQMacroModeEnum::RECORDING_INSERT_COMMANDS: case WuQMacroModeEnum::RECORDING_NEW_MACRO: -- GitLab From 7bce385a3383719dbf314ad4c7468e7b7586e9bb Mon Sep 17 00:00:00 2001 From: John Harwell Date: Thu, 28 Mar 2019 10:51:47 -0500 Subject: [PATCH 259/427] Fix compilation error on windows. Enumerated type value 'DOUBLE_CLICK' appears to conflict with a microsoft 'DOUBLE_CLICK' and has been changed to 'DOUBLE_CLICKED' to fix compilation. --- src/Common/WuQMacroCommand.cxx | 2 +- src/Common/WuQMacroMouseEventTypeEnum.cxx | 6 +++--- src/Common/WuQMacroMouseEventTypeEnum.h | 2 +- src/GuiQt/WuQMacroExecutor.cxx | 2 +- src/GuiQt/WuQMacroManager.cxx | 2 +- 5 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/Common/WuQMacroCommand.cxx b/src/Common/WuQMacroCommand.cxx index 904d18ca7..8caf70750 100644 --- a/src/Common/WuQMacroCommand.cxx +++ b/src/Common/WuQMacroCommand.cxx @@ -708,7 +708,7 @@ WuQMacroCommand::setMouseEventInfo(WuQMacroMouseEventInfo* mouseEventInfo) case WuQMacroMouseEventTypeEnum::BUTTON_RELEASE: title = "Mouse Release "; break; - case WuQMacroMouseEventTypeEnum::DOUBLE_CLICK: + case WuQMacroMouseEventTypeEnum::DOUBLE_CLICKED: title = "Mouse Double Click "; break; case WuQMacroMouseEventTypeEnum::MOVE: diff --git a/src/Common/WuQMacroMouseEventTypeEnum.cxx b/src/Common/WuQMacroMouseEventTypeEnum.cxx index 10705e81a..a2294319b 100644 --- a/src/Common/WuQMacroMouseEventTypeEnum.cxx +++ b/src/Common/WuQMacroMouseEventTypeEnum.cxx @@ -114,9 +114,9 @@ WuQMacroMouseEventTypeEnum::initialize() "BUTTON_RELEASE", "Button-Release")); - enumData.push_back(WuQMacroMouseEventTypeEnum(DOUBLE_CLICK, - "DOUBLE_CLICK", - "Double-Click")); + enumData.push_back(WuQMacroMouseEventTypeEnum(DOUBLE_CLICKED, + "DOUBLE_CLICKED", + "Double-Clicked")); enumData.push_back(WuQMacroMouseEventTypeEnum(MOVE, "MOVE", diff --git a/src/Common/WuQMacroMouseEventTypeEnum.h b/src/Common/WuQMacroMouseEventTypeEnum.h index d8f9ca5a5..36bff2b49 100644 --- a/src/Common/WuQMacroMouseEventTypeEnum.h +++ b/src/Common/WuQMacroMouseEventTypeEnum.h @@ -40,7 +40,7 @@ public: /** Mouse button is released */ BUTTON_RELEASE, /** Mouse is double-clicked */ - DOUBLE_CLICK, + DOUBLE_CLICKED, /** Mouse is moved */ MOVE }; diff --git a/src/GuiQt/WuQMacroExecutor.cxx b/src/GuiQt/WuQMacroExecutor.cxx index 931b1fbd4..236c7ffb5 100644 --- a/src/GuiQt/WuQMacroExecutor.cxx +++ b/src/GuiQt/WuQMacroExecutor.cxx @@ -1199,7 +1199,7 @@ WuQMacroExecutor::runMouseCommand(const WuQMacroCommand* macroCommand, case WuQMacroMouseEventTypeEnum::BUTTON_RELEASE: qtEventType = QEvent::MouseButtonRelease; break; - case WuQMacroMouseEventTypeEnum::DOUBLE_CLICK: + case WuQMacroMouseEventTypeEnum::DOUBLE_CLICKED: qtEventType = QEvent::MouseButtonDblClick; break; case WuQMacroMouseEventTypeEnum::MOVE: diff --git a/src/GuiQt/WuQMacroManager.cxx b/src/GuiQt/WuQMacroManager.cxx index 1fd3edc2f..818cc1424 100644 --- a/src/GuiQt/WuQMacroManager.cxx +++ b/src/GuiQt/WuQMacroManager.cxx @@ -395,7 +395,7 @@ WuQMacroManager::addMouseEventToRecording(QWidget* widget, mouseEventType = WuQMacroMouseEventTypeEnum::BUTTON_RELEASE; break; case QEvent::MouseButtonDblClick: - mouseEventType = WuQMacroMouseEventTypeEnum::DOUBLE_CLICK; + mouseEventType = WuQMacroMouseEventTypeEnum::DOUBLE_CLICKED; break; case QEvent::MouseMove: mouseEventType = WuQMacroMouseEventTypeEnum::MOVE; -- GitLab From 167a660dd17471397cb20adf970d7046f85b1211 Mon Sep 17 00:00:00 2001 From: John Harwell Date: Thu, 28 Mar 2019 11:54:23 -0500 Subject: [PATCH 260/427] Fixed compilation warnings. --- src/Brain/BrainOpenGLVolumeSliceDrawing.cxx | 9 +++------ src/Files/SurfaceFile.cxx | 2 -- src/GuiQt/MovieRecordingDialog.cxx | 5 +---- src/GuiQt/WbMacroCustomOperationManager.cxx | 2 +- src/GuiQt/WuQMacroDialog.cxx | 2 -- 5 files changed, 5 insertions(+), 15 deletions(-) diff --git a/src/Brain/BrainOpenGLVolumeSliceDrawing.cxx b/src/Brain/BrainOpenGLVolumeSliceDrawing.cxx index 1f9a517a4..2fadddd9b 100644 --- a/src/Brain/BrainOpenGLVolumeSliceDrawing.cxx +++ b/src/Brain/BrainOpenGLVolumeSliceDrawing.cxx @@ -1515,8 +1515,7 @@ BrainOpenGLVolumeSliceDrawing::drawOrthogonalSlice(const VolumeSliceViewPlaneEnu * * Only allow layer blending when overall volume opacity is off (>= 1.0) */ - const DisplayPropertiesVolume* dpv = m_brain->getDisplayPropertiesVolume(); - const bool allowBlendingFlag(true); //dpv->getOpacity() >= 1.0f); + const bool allowBlendingFlag(true); glPushAttrib(GL_COLOR_BUFFER_BIT); if (allowBlendingFlag) { glEnable(GL_BLEND); @@ -2091,8 +2090,7 @@ BrainOpenGLVolumeSliceDrawing::drawOrthogonalSliceWithCulling(const VolumeSliceV * * Only allow layer blending when overall volume opacity is off (>= 1.0) */ - const DisplayPropertiesVolume* dpv = m_brain->getDisplayPropertiesVolume(); - const bool allowBlendingFlag(true); //dpv->getOpacity() >= 1.0f); + const bool allowBlendingFlag(true); glPushAttrib(GL_COLOR_BUFFER_BIT); if (allowBlendingFlag) { glEnable(GL_BLEND); @@ -5376,8 +5374,7 @@ BrainOpenGLVolumeSliceDrawing::drawOrthogonalSliceAllView(const VolumeSliceViewP * * Only allow layer blending when overall volume opacity is off (>= 1.0) */ - const DisplayPropertiesVolume* dpv = m_brain->getDisplayPropertiesVolume(); - const bool allowBlendingFlag(true); //dpv->getOpacity() >= 1.0f); + const bool allowBlendingFlag(true); glPushAttrib(GL_COLOR_BUFFER_BIT); if (allowBlendingFlag) { glEnable(GL_BLEND); diff --git a/src/Files/SurfaceFile.cxx b/src/Files/SurfaceFile.cxx index e20142806..29f513ddc 100644 --- a/src/Files/SurfaceFile.cxx +++ b/src/Files/SurfaceFile.cxx @@ -1051,7 +1051,6 @@ SurfaceFile::matchSphereToSurface(const SurfaceFile* matchSurfaceFile) CaretPointer matchTH = matchSurfaceFile->getTopologyHelper(); const int32_t numMatchVertices = matchSurfaceFile->getNumberOfNodes(); float newRadius = 0.0; - int32_t furthestVertex = -1; for (int32_t i = 0; i < numMatchVertices; i++) { if (matchTH->getNodeHasNeighbors(i)) { const float* xyz = matchSurfaceFile->getCoordinate(i); @@ -1060,7 +1059,6 @@ SurfaceFile::matchSphereToSurface(const SurfaceFile* matchSurfaceFile) + (xyz[2]*xyz[2])); if (dist > newRadius) { newRadius = dist; - furthestVertex = i; } } } diff --git a/src/GuiQt/MovieRecordingDialog.cxx b/src/GuiQt/MovieRecordingDialog.cxx index 5e242119b..7adc51027 100644 --- a/src/GuiQt/MovieRecordingDialog.cxx +++ b/src/GuiQt/MovieRecordingDialog.cxx @@ -84,8 +84,7 @@ MovieRecordingDialog::MovieRecordingDialog(QWidget* parent) setApplyButtonText(""); disableAutoDefaultForAllPushButtons(); - MovieRecorder* movieRecorder = SessionManager::get()->getMovieRecorder(); - CaretAssert(movieRecorder); + CaretAssert(SessionManager::get()->getMovieRecorder()); } /** @@ -145,11 +144,9 @@ MovieRecordingDialog::updateDialog() m_windowComboBox->updateComboBox(); m_windowComboBox->setBrowserWindowByIndex(movieRecorder->getRecordingWindowIndex()); - bool manualRecordingEnabledFlag(false); switch (movieRecorder->getRecordingMode()) { case MovieRecorderModeEnum::MANUAL: m_recordingManualRadioButton->setChecked(true); - manualRecordingEnabledFlag = true; break; case MovieRecorderModeEnum::AUTOMATIC: m_recordingAutomaticRadioButton->setChecked(true); diff --git a/src/GuiQt/WbMacroCustomOperationManager.cxx b/src/GuiQt/WbMacroCustomOperationManager.cxx index 7a4ecf85d..85f5ada7a 100644 --- a/src/GuiQt/WbMacroCustomOperationManager.cxx +++ b/src/GuiQt/WbMacroCustomOperationManager.cxx @@ -398,7 +398,7 @@ WbMacroCustomOperationManager::getOverlayContents(const int32_t browserWindowInd * True if the data info is valid */ bool -WbMacroCustomOperationManager::getCustomParameterDataInfo(const int32_t browserWindowIndex, +WbMacroCustomOperationManager::getCustomParameterDataInfo(const int32_t /*browserWindowIndex*/, const WuQMacroCommand* macroCommand, const WuQMacroCommandParameter* parameter, WbMacroCustomDataInfo& dataInfoOut) diff --git a/src/GuiQt/WuQMacroDialog.cxx b/src/GuiQt/WuQMacroDialog.cxx index 0f797947e..711676088 100644 --- a/src/GuiQt/WuQMacroDialog.cxx +++ b/src/GuiQt/WuQMacroDialog.cxx @@ -1687,7 +1687,6 @@ WuQMacroDialog::editingInsertToolButtonClicked() break; } - bool stopValidFlag(false); switch (macroManager->getMode()) { case WuQMacroModeEnum::OFF: break; @@ -1696,7 +1695,6 @@ WuQMacroDialog::editingInsertToolButtonClicked() insertMacroValidFlag = false; copyValidFlag = false; insertMacroCommandValidFlag = false; - stopValidFlag = true; break; case WuQMacroModeEnum::RUNNING: insertMacroValidFlag = false; -- GitLab From f53d51604e118f5fdafdf6774a4aa9a55feebfdf Mon Sep 17 00:00:00 2001 From: John Harwell Date: Thu, 28 Mar 2019 15:54:01 -0500 Subject: [PATCH 261/427] Small updates too labels in dialogs. --- src/GuiQt/MovieRecordingDialog.cxx | 2 +- src/GuiQt/WuQMacroDialog.cxx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/GuiQt/MovieRecordingDialog.cxx b/src/GuiQt/MovieRecordingDialog.cxx index 7adc51027..192405dc7 100644 --- a/src/GuiQt/MovieRecordingDialog.cxx +++ b/src/GuiQt/MovieRecordingDialog.cxx @@ -689,7 +689,7 @@ MovieRecordingDialog::createSettingsWidget() QObject::connect(m_frameRateSpinBox, QOverload::of(&QSpinBox::valueChanged), this, &MovieRecordingDialog::frameRateSpinBoxValueChanged); - m_removeTemporaryImagesAfterMovieCreationCheckBox = new QCheckBox("Remove Temporary Images After Movie Creation"); + m_removeTemporaryImagesAfterMovieCreationCheckBox = new QCheckBox("Remove temporary images after movie creation"); m_removeTemporaryImagesAfterMovieCreationCheckBox->setToolTip("Temporary images are removed after a movie is created"); QObject::connect(m_removeTemporaryImagesAfterMovieCreationCheckBox, &QCheckBox::clicked, this, &MovieRecordingDialog::removeTemporaryImagesCheckBoxClicked); diff --git a/src/GuiQt/WuQMacroDialog.cxx b/src/GuiQt/WuQMacroDialog.cxx index 711676088..4fd80e727 100644 --- a/src/GuiQt/WuQMacroDialog.cxx +++ b/src/GuiQt/WuQMacroDialog.cxx @@ -1398,7 +1398,7 @@ WuQMacroDialog::recordMacroToolButtonClicked() menu.addSeparator(); - QAction* newMacroAction = menu.addAction("Record and Insert Macro Below...", + QAction* newMacroAction = menu.addAction("Record and Insert New Macro Below...", this, &WuQMacroDialog::recordAndInsertNewMacroSelected); newMacroAction->setEnabled(getSelectedMacroGroup() != NULL); -- GitLab From 0014d7e34a17ce0cce35d364ebff5771a6374914 Mon Sep 17 00:00:00 2001 From: John Harwell Date: Mon, 1 Apr 2019 12:11:06 -0500 Subject: [PATCH 262/427] Fixed bug in macro editing. If a macro contained an overlay (file and map), changing the file did not update the name of the selected map. Selected map needs to change if the map name from the old file is not in the new file. --- src/GuiQt/WbMacroCustomOperationManager.cxx | 26 ++++++++++++++----- src/GuiQt/WbMacroCustomOperationManager.h | 10 +++---- .../WuQMacroCustomOperationManagerInterface.h | 4 +-- src/GuiQt/WuQMacroManager.cxx | 4 +-- src/GuiQt/WuQMacroManager.h | 4 +-- 5 files changed, 30 insertions(+), 18 deletions(-) diff --git a/src/GuiQt/WbMacroCustomOperationManager.cxx b/src/GuiQt/WbMacroCustomOperationManager.cxx index 85f5ada7a..d7fd70c82 100644 --- a/src/GuiQt/WbMacroCustomOperationManager.cxx +++ b/src/GuiQt/WbMacroCustomOperationManager.cxx @@ -140,9 +140,9 @@ WbMacroCustomOperationManager::getSurfaceNames(std::vector& surfaceName * True if map file names valid */ bool -WbMacroCustomOperationManager::getMappableFilesSelection(const WuQMacroCommand* macroCommand, - const WuQMacroCommandParameter* overlayFileParameterIn, - const WuQMacroCommandParameter* mapParameterIn, +WbMacroCustomOperationManager::getMappableFilesSelection(WuQMacroCommand* macroCommand, + WuQMacroCommandParameter* overlayFileParameterIn, + WuQMacroCommandParameter* mapParameterIn, std::vector& mapFileNamesOut, QString& selectedFileNameOut, std::vector& mapNamesOut, @@ -156,8 +156,8 @@ WbMacroCustomOperationManager::getMappableFilesSelection(const WuQMacroCommand* selectedMapNameOut.clear(); errorMessageOut.clear(); - const WuQMacroCommandParameter* fileParameter = overlayFileParameterIn; - const WuQMacroCommandParameter* mapParameter = mapParameterIn; + WuQMacroCommandParameter* fileParameter = overlayFileParameterIn; + WuQMacroCommandParameter* mapParameter = mapParameterIn; if ((fileParameter != NULL) && (mapParameter != NULL)) { /* OK, have both */ @@ -267,6 +267,18 @@ WbMacroCustomOperationManager::getMappableFilesSelection(const WuQMacroCommand* } } + /** + * Selection may change. For example, if the selected overlay file + * is changed, the selected map name is likely to change so that + * it is a map that is in the new file and was not in the previous file + */ + if (selectedFileNameOut != fileParameter->getValue().toString()) { + fileParameter->setValue(selectedFileNameOut); + } + if (selectedMapNameOut != mapParameter->getValue().toString()) { + mapParameter->setValue(selectedMapNameOut); + } + return true; } @@ -399,8 +411,8 @@ WbMacroCustomOperationManager::getOverlayContents(const int32_t browserWindowInd */ bool WbMacroCustomOperationManager::getCustomParameterDataInfo(const int32_t /*browserWindowIndex*/, - const WuQMacroCommand* macroCommand, - const WuQMacroCommandParameter* parameter, + WuQMacroCommand* macroCommand, + WuQMacroCommandParameter* parameter, WbMacroCustomDataInfo& dataInfoOut) { CaretAssert(macroCommand); diff --git a/src/GuiQt/WbMacroCustomOperationManager.h b/src/GuiQt/WbMacroCustomOperationManager.h index 834e3c3a9..e2d5fbe06 100644 --- a/src/GuiQt/WbMacroCustomOperationManager.h +++ b/src/GuiQt/WbMacroCustomOperationManager.h @@ -50,8 +50,8 @@ namespace caret { WbMacroCustomOperationManager& operator=(const WbMacroCustomOperationManager&) = delete; virtual bool getCustomParameterDataInfo(const int32_t browserWindowIndex, - const WuQMacroCommand* macroCommand, - const WuQMacroCommandParameter* parameter, + WuQMacroCommand* macroCommand, + WuQMacroCommandParameter* parameter, WbMacroCustomDataInfo& dataInfoOut) override; virtual bool executeCustomOperationMacroCommand(QWidget* parent, @@ -87,9 +87,9 @@ namespace caret { std::vector& selectedMapFileMapNamesOut, QString& errorMessageOut); - bool getMappableFilesSelection(const WuQMacroCommand* macroCommand, - const WuQMacroCommandParameter* overlayFileParameter, - const WuQMacroCommandParameter* mapParameter, + bool getMappableFilesSelection(WuQMacroCommand* macroCommand, + WuQMacroCommandParameter* overlayFileParameter, + WuQMacroCommandParameter* mapParameter, std::vector& mapFileNamesOut, QString& selectedFileNameOut, std::vector& mapNamesOut, diff --git a/src/GuiQt/WuQMacroCustomOperationManagerInterface.h b/src/GuiQt/WuQMacroCustomOperationManagerInterface.h index 93589d674..2a8011c0d 100644 --- a/src/GuiQt/WuQMacroCustomOperationManagerInterface.h +++ b/src/GuiQt/WuQMacroCustomOperationManagerInterface.h @@ -59,8 +59,8 @@ namespace caret { * True if the data info is valid */ virtual bool getCustomParameterDataInfo(const int32_t browserWindowIndex, - const WuQMacroCommand* macroCommand, - const WuQMacroCommandParameter* parameter, + WuQMacroCommand* macroCommand, + WuQMacroCommandParameter* parameter, WbMacroCustomDataInfo& dataInfoOut) = 0; /** diff --git a/src/GuiQt/WuQMacroManager.cxx b/src/GuiQt/WuQMacroManager.cxx index 818cc1424..f3980f704 100644 --- a/src/GuiQt/WuQMacroManager.cxx +++ b/src/GuiQt/WuQMacroManager.cxx @@ -1332,8 +1332,8 @@ WuQMacroManager::getShortCutKeysMask() */ bool WuQMacroManager::getCustomParameterDataInfo(const int32_t browserWindowIndex, - const WuQMacroCommand* macroCommand, - const WuQMacroCommandParameter* parameter, + WuQMacroCommand* macroCommand, + WuQMacroCommandParameter* parameter, WbMacroCustomDataInfo& dataInfoOut) { bool validFlag(false); diff --git a/src/GuiQt/WuQMacroManager.h b/src/GuiQt/WuQMacroManager.h index 15259ef31..ea3e9d51d 100644 --- a/src/GuiQt/WuQMacroManager.h +++ b/src/GuiQt/WuQMacroManager.h @@ -157,8 +157,8 @@ namespace caret { void macroWasModified(WuQMacro* macro); bool getCustomParameterDataInfo(const int32_t browserWindowIndex, - const WuQMacroCommand* macroCommand, - const WuQMacroCommandParameter* parameter, + WuQMacroCommand* macroCommand, + WuQMacroCommandParameter* parameter, WbMacroCustomDataInfo& dataInfoOut); bool executeCustomOperationMacroCommand(QWidget* parent, -- GitLab From 5df71430c868ee99f4c4b1579d1e88eda249246d Mon Sep 17 00:00:00 2001 From: John Harwell Date: Mon, 1 Apr 2019 12:35:47 -0500 Subject: [PATCH 263/427] Macros Dialog: While recording a new macro or new commands, enable the STOP button to allow the user to stop recording (same function as pressing Record to turn off recording). --- src/GuiQt/WuQMacroDialog.cxx | 54 ++++++++++++++++++++++++++---------- 1 file changed, 39 insertions(+), 15 deletions(-) diff --git a/src/GuiQt/WuQMacroDialog.cxx b/src/GuiQt/WuQMacroDialog.cxx index 4fd80e727..a2488b8ca 100644 --- a/src/GuiQt/WuQMacroDialog.cxx +++ b/src/GuiQt/WuQMacroDialog.cxx @@ -1365,6 +1365,21 @@ WuQMacroDialog::getWindow() void WuQMacroDialog::stopMacroToolButtonClicked() { + /* + * If recording, STOP button stops recording + */ + switch (WuQMacroManager::instance()->getMode()) { + case WuQMacroModeEnum::OFF: + break; + case WuQMacroModeEnum::RECORDING_INSERT_COMMANDS: + case WuQMacroModeEnum::RECORDING_NEW_MACRO: + recordMacroToolButtonClicked(); + return; + break; + case WuQMacroModeEnum::RUNNING: + break; + } + WuQMacroManager::instance()->stopMacro(); updateEditingToolButtons(); } @@ -2030,6 +2045,29 @@ WuQMacroDialog::updateEditingToolButtons() } } + bool recordingFlag(false); + switch (WuQMacroManager::instance()->getMode()) { + case WuQMacroModeEnum::OFF: + break; + case WuQMacroModeEnum::RECORDING_INSERT_COMMANDS: + recordingFlag = true; + break; + case WuQMacroModeEnum::RECORDING_NEW_MACRO: + recordingFlag = true; + break; + case WuQMacroModeEnum::RUNNING: + break; + } + + if (recordingFlag) { + m_recordMacroToolButton->setIcon(m_recordMacroToolButtonIconOn); + runValid = false; + stopValid = true; + } + else { + m_recordMacroToolButton->setIcon(m_recordMacroToolButtonIconOff); + } + m_pauseMacroToolButton->setEnabled(pauseValid); m_pauseMacroToolButton->setChecked(pauseChecked); m_resetMacroGroupToolButton->setEnabled(resetValid); @@ -2041,21 +2079,7 @@ WuQMacroDialog::updateEditingToolButtons() m_editingInsertToolButton->setEnabled(insertValid); m_editingMoveDownToolButton->setEnabled(moveDownValid); m_editingMoveUpToolButton->setEnabled(moveUpValid); - - switch (WuQMacroManager::instance()->getMode()) { - case WuQMacroModeEnum::OFF: - m_recordMacroToolButton->setIcon(m_recordMacroToolButtonIconOff); - break; - case WuQMacroModeEnum::RECORDING_INSERT_COMMANDS: - m_recordMacroToolButton->setIcon(m_recordMacroToolButtonIconOn); - break; - case WuQMacroModeEnum::RECORDING_NEW_MACRO: - m_recordMacroToolButton->setIcon(m_recordMacroToolButtonIconOn); - break; - case WuQMacroModeEnum::RUNNING: - m_recordMacroToolButton->setIcon(m_recordMacroToolButtonIconOff); - break; - } + } /** -- GitLab From f5116d1896d96aa9120f263765ba1ca07f7a24e9 Mon Sep 17 00:00:00 2001 From: John Harwell Date: Mon, 1 Apr 2019 12:57:18 -0500 Subject: [PATCH 264/427] Macros Dialog: After reload scene button is used, update dialog contents to possibly a fix reported on Linux but not observed on the Linux build system computer. --- src/GuiQt/WuQMacroDialog.cxx | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/GuiQt/WuQMacroDialog.cxx b/src/GuiQt/WuQMacroDialog.cxx index a2488b8ca..35d2c5e19 100644 --- a/src/GuiQt/WuQMacroDialog.cxx +++ b/src/GuiQt/WuQMacroDialog.cxx @@ -1539,6 +1539,8 @@ WuQMacroDialog::macroGroupResetToolButtonClicked() m_treeView->setCurrentIndex(macro->index()); treeItemSelected(macro->index()); } + + updateDialogContents(); } /** -- GitLab From df0319560f296fdbb68587f2ab0ac5eae5396f1f Mon Sep 17 00:00:00 2001 From: John Harwell Date: Tue, 2 Apr 2019 08:48:27 -0500 Subject: [PATCH 265/427] Macros Dialog: Add to command right-click menu: (1) Run this Command; (2) Run and Start with this Command; (3) Run and Start with this Command No Delays/Durations --- src/GuiQt/WuQMacroDialog.cxx | 100 ++++++++++++++++++++++++++++++--- src/GuiQt/WuQMacroDialog.h | 9 +++ src/GuiQt/WuQMacroExecutor.cxx | 42 +++++++++----- src/GuiQt/WuQMacroExecutor.h | 2 + src/GuiQt/WuQMacroManager.cxx | 9 ++- src/GuiQt/WuQMacroManager.h | 1 + 6 files changed, 140 insertions(+), 23 deletions(-) diff --git a/src/GuiQt/WuQMacroDialog.cxx b/src/GuiQt/WuQMacroDialog.cxx index 35d2c5e19..6e3538577 100644 --- a/src/GuiQt/WuQMacroDialog.cxx +++ b/src/GuiQt/WuQMacroDialog.cxx @@ -799,13 +799,24 @@ WuQMacroDialog::treeViewCustomContextMenuRequested(const QPoint& pos) WuQMacroCommand* command = getSelectedMacroCommand(); if (command != NULL) { QMenu menu(this); + + menu.addAction("Run this Command", + this, &WuQMacroDialog::runOnlySelectedCommandMenuItemSelected); + + menu.addSeparator(); + + menu.addAction("Run Macro and Start With this Command", + this, &WuQMacroDialog::runAndStartWithSelectedCommandMenuItemSelected); + + menu.addAction("Run Macro and Start With this Command Without Delays/Durations", + this, &WuQMacroDialog::runAndStartWithNoDelayDurationSelectedCommandMenuItemSelected); - const QString runToText("Run Macro and Stop After this Command"); - menu.addAction(runToText, + menu.addSeparator(); + + menu.addAction("Run Macro and Stop After this Command", this, &WuQMacroDialog::runAndStopAfterSelectedCommandMenuItemSelected); - const QString runToFastText("Run Macro and Stop After this Command Without Delays/Durations"); - menu.addAction(runToFastText, + menu.addAction("Run Macro and Stop After this Command Without Delays/Durations", this, &WuQMacroDialog::runAndStopAfterWithNoDelayDurationSelectedCommandMenuItemSelected); menu.exec(m_treeView->mapToGlobal(pos)); @@ -813,6 +824,62 @@ WuQMacroDialog::treeViewCustomContextMenuRequested(const QPoint& pos) } } +/** + * Called when 'run and start with...' is selected from + * context (pop-up) menu. Runs macro starting with selected command. + */ +void +WuQMacroDialog::runAndStartWithSelectedCommandMenuItemSelected() +{ + WuQMacroCommand* command = getSelectedMacroCommand(); + if (command != NULL) { + runSelectedMacro(command, + NULL); + } + else { + QMessageBox::warning(this, "Error", "No macro command is selected"); + } +} + +/** + * Called when 'run this command only...' is selected from + * context (pop-up) menu. Runs macro to selected command. + */ +void +WuQMacroDialog::runOnlySelectedCommandMenuItemSelected() +{ + WuQMacroCommand* command = getSelectedMacroCommand(); + if (command != NULL) { + runSelectedMacro(command, + command); + } + else { + QMessageBox::warning(this, "Error", "No macro command is selected"); + } +} + +/** + * Called when 'run and start with without delay duration...' is selected from + * context (pop-up) menu. Runs macro to selected command. + */ +void +WuQMacroDialog::runAndStartWithNoDelayDurationSelectedCommandMenuItemSelected() +{ + WuQMacroCommand* command = getSelectedMacroCommand(); + if (command != NULL) { + WuQMacroManager* macroManager = WuQMacroManager::instance(); + WuQMacroExecutorOptions* runOptions = macroManager->getExecutorOptions(); + const bool savedIgnoreDelaysFlag = runOptions->isIgnoreDelaysAndDurations(); + runOptions->setIgnoreDelaysAndDurations(true); + runSelectedMacro(command, + NULL); + runOptions->setIgnoreDelaysAndDurations(savedIgnoreDelaysFlag); + } + else { + QMessageBox::warning(this, "Error", "No macro command is selected"); + } +} + /** * Called when 'run and stop after...' is selected from * context (pop-up) menu. Runs macro to selected command. @@ -826,7 +893,8 @@ WuQMacroDialog::runAndStopAfterSelectedCommandMenuItemSelected() WuQMacroExecutorOptions* runOptions = macroManager->getExecutorOptions(); const bool savedStopAfterOptionFlag = runOptions->isStopAfterSelectedCommand(); runOptions->setStopAfterSelectedCommand(true); - runMacroToolButtonClicked(); + runSelectedMacro(NULL, + command); runOptions->setStopAfterSelectedCommand(savedStopAfterOptionFlag); } else { @@ -849,7 +917,8 @@ WuQMacroDialog::runAndStopAfterWithNoDelayDurationSelectedCommandMenuItemSelecte const bool savedIgnoreDelaysFlag = runOptions->isIgnoreDelaysAndDurations(); runOptions->setStopAfterSelectedCommand(true); runOptions->setIgnoreDelaysAndDurations(true); - runMacroToolButtonClicked(); + runSelectedMacro(NULL, + command); runOptions->setStopAfterSelectedCommand(savedStopAfterOptionFlag); runOptions->setIgnoreDelaysAndDurations(savedIgnoreDelaysFlag); } @@ -1278,6 +1347,22 @@ WuQMacroDialog::pauseContinueMacroToolButtonClicked() */ void WuQMacroDialog::runMacroToolButtonClicked() +{ + runSelectedMacro(NULL, + NULL); +} +/** + * Called when run button is clicked + * + * @param macroCommandToStartAt + * Macro command at which execution should begin. If NULL, start + * with the first command in the macro + * @param macroCommandToStopAfter + * Macro command that the executor may stop after, depending upon options + */ +void +WuQMacroDialog::runSelectedMacro(const WuQMacroCommand* macroCommandToStartAt, + const WuQMacroCommand* macroCommandToStopAfter) { switch (WuQMacroManager::instance()->getMode()) { case WuQMacroModeEnum::OFF: @@ -1321,7 +1406,8 @@ WuQMacroDialog::runMacroToolButtonClicked() timer.start(); WuQMacro* lastMacroRun = WuQMacroManager::instance()->runMacro(window, macro, - getSelectedMacroCommand()); + macroCommandToStartAt, + macroCommandToStopAfter); // may use this later when testing std::cout << "Time to run macro: " << timer.getElapsedTimeSeconds() << std::endl; m_macroIsRunningFlag = false; diff --git a/src/GuiQt/WuQMacroDialog.h b/src/GuiQt/WuQMacroDialog.h index 2b50d1907..b8224675a 100644 --- a/src/GuiQt/WuQMacroDialog.h +++ b/src/GuiQt/WuQMacroDialog.h @@ -84,6 +84,12 @@ namespace caret { void treeViewCustomContextMenuRequested(const QPoint& pos); + void runOnlySelectedCommandMenuItemSelected(); + + void runAndStartWithSelectedCommandMenuItemSelected(); + + void runAndStartWithNoDelayDurationSelectedCommandMenuItemSelected(); + void runAndStopAfterSelectedCommandMenuItemSelected(); void runAndStopAfterWithNoDelayDurationSelectedCommandMenuItemSelected(); @@ -215,6 +221,9 @@ namespace caret { QWidget* getWindow(); + void runSelectedMacro(const WuQMacroCommand* macroCommandToStartAt, + const WuQMacroCommand* macroCommandToStopAfter); + std::vector m_macroGroups; QComboBox* m_macroGroupComboBox; diff --git a/src/GuiQt/WuQMacroExecutor.cxx b/src/GuiQt/WuQMacroExecutor.cxx index 236c7ffb5..2eef4f5df 100644 --- a/src/GuiQt/WuQMacroExecutor.cxx +++ b/src/GuiQt/WuQMacroExecutor.cxx @@ -263,6 +263,9 @@ WuQMacroExecutor::findObjectByName(const QString& objectName) const * * @param macro * Macro that is run + * @param macroCommandToStartAt + * Macro command at which execution should begin. If NULL, start + * with the first command in the macro * @param macroCommandToStopAfter * Macro command that the executor may stop after, depending upon options * @param window @@ -281,6 +284,7 @@ WuQMacroExecutor::findObjectByName(const QString& objectName) const */ bool WuQMacroExecutor::runMacro(const WuQMacro* macro, + const WuQMacroCommand* macroCommandToStartAt, const WuQMacroCommand* macroCommandToStopAfter, QWidget* window, std::vector& otherObjectParents, @@ -289,6 +293,7 @@ WuQMacroExecutor::runMacro(const WuQMacro* macro, QString& errorMessageOut) const { const bool result = runMacroPrivate(macro, + macroCommandToStartAt, macroCommandToStopAfter, window, otherObjectParents, @@ -304,6 +309,9 @@ WuQMacroExecutor::runMacro(const WuQMacro* macro, * * @param macro * Macro that is run + * @param macroCommandToStartAt + * Macro command at which execution should begin. If NULL, start + * with the first command in the macro * @param macroCommandToStopAfter * Macro command that the executor may stop after, depending upon options * @param window @@ -322,6 +330,7 @@ WuQMacroExecutor::runMacro(const WuQMacro* macro, */ bool WuQMacroExecutor::runMacroPrivate(const WuQMacro* macro, + const WuQMacroCommand* macroCommandToStartAt, const WuQMacroCommand* macroCommandToStopAfter, QWidget* window, std::vector& otherObjectParents, @@ -338,18 +347,23 @@ WuQMacroExecutor::runMacroPrivate(const WuQMacro* macro, otherObjectParents.begin(), otherObjectParents.end()); m_runOptions = *executorOptions; - if (m_runOptions.isStopAfterSelectedCommand()) { - if (macroCommandToStopAfter == NULL) { - errorMessageOut = ("Option to stop after selected command is on but no command is selected"); + + errorMessageOut.clear(); + + int32_t startCommandIndex(0); + if (macroCommandToStartAt != NULL) { + const int32_t commandIndex = macro->getIndexOfMacroCommand(macroCommandToStartAt); + if (commandIndex < 0) { + errorMessageOut = ("Macro command to start at not found in macro \"" + + macroCommandToStartAt->getDescriptiveName() + + "\""); return false; } + startCommandIndex = commandIndex; } - - errorMessageOut.clear(); - const int32_t numberOfMacroCommands = macro->getNumberOfMacroCommands(); - for (int32_t i = 0; i < numberOfMacroCommands; i++) { + for (int32_t i = startCommandIndex; i < numberOfMacroCommands; i++) { const WuQMacroCommand* mc = macro->getMacroCommandAtIndex(i); CaretAssert(mc); @@ -470,15 +484,13 @@ WuQMacroExecutor::runMacroPrivate(const WuQMacro* macro, executorOptions, allowDelayAfterCommandFlag); - if (m_runOptions.isStopAfterSelectedCommand()) { - QGuiApplication::processEvents(); - if (mc == macroCommandToStopAfter) { - errorMessageOut = ("Macro stopped after " - + mc->getDescriptiveName()); - return false; - } + QGuiApplication::processEvents(); + if (mc == macroCommandToStopAfter) { + errorMessageOut = ("Macro stopped after " + + mc->getDescriptiveName()); + return false; } - + const bool stopFlag = executorMonitor->testForStop(); if (stopFlag) { errorMessageOut = executorMonitor->getStoppedByUserMessage(); diff --git a/src/GuiQt/WuQMacroExecutor.h b/src/GuiQt/WuQMacroExecutor.h index fc03d0af9..d6d7338ec 100644 --- a/src/GuiQt/WuQMacroExecutor.h +++ b/src/GuiQt/WuQMacroExecutor.h @@ -52,6 +52,7 @@ namespace caret { WuQMacroExecutor& operator=(const WuQMacroExecutor&) = delete; bool runMacro(const WuQMacro* macro, + const WuQMacroCommand* macroCommandToStartAt, const WuQMacroCommand* macroCommandToStopAfter, QWidget* window, std::vector& otherObjectParents, @@ -99,6 +100,7 @@ namespace caret { void performCommandDelay(const WuQMacroCommand* mc) const; bool runMacroPrivate(const WuQMacro* macro, + const WuQMacroCommand* macroCommandToStartAt, const WuQMacroCommand* macroCommandToStopAfter, QWidget* window, std::vector& otherObjectParents, diff --git a/src/GuiQt/WuQMacroManager.cxx b/src/GuiQt/WuQMacroManager.cxx index f3980f704..37ddefdbd 100644 --- a/src/GuiQt/WuQMacroManager.cxx +++ b/src/GuiQt/WuQMacroManager.cxx @@ -639,8 +639,12 @@ WuQMacroManager::updateNonModalDialogs() * Widget used for parent of dialogs * @param macroToRun * Macro that is run + * @param macroCommandToStartAt + * Macro command at which execution should begin. If NULL, start + * with the first command in the macro * @param macroCommandToStopAfter - * Macro command that the executor may stop after, depending upon options + * Macro command that the executor may stop after, depending upon options. + * If NULL, end after last command is executed. * @return * Pointer to macro that was last run. The selected macro could change * when looping is enabled. @@ -648,6 +652,7 @@ WuQMacroManager::updateNonModalDialogs() WuQMacro* WuQMacroManager::runMacro(QWidget* widget, const WuQMacro* macroToRun, + const WuQMacroCommand* macroCommandToStartAt, const WuQMacroCommand* macroCommandToStopAfter) { CaretAssert(widget); @@ -679,6 +684,7 @@ WuQMacroManager::runMacro(QWidget* widget, m_executorOptions.get()); } resultFlag = m_macroExecutor->runMacro(macro, + macroCommandToStartAt, macroCommandToStopAfter, widget, m_parentObjects, @@ -1271,6 +1277,7 @@ WuQMacroManager::runMacroWithShortCutKeyEvent(QWidget* window, if (macro != NULL) { runMacro(window, macro, + NULL, NULL); return true; } diff --git a/src/GuiQt/WuQMacroManager.h b/src/GuiQt/WuQMacroManager.h index ea3e9d51d..d142f201a 100644 --- a/src/GuiQt/WuQMacroManager.h +++ b/src/GuiQt/WuQMacroManager.h @@ -130,6 +130,7 @@ namespace caret { WuQMacro* runMacro(QWidget* window, const WuQMacro* macro, + const WuQMacroCommand* macroCommandToStartAt, const WuQMacroCommand* macroCommandToStopAfter); WuQMacro* resetMacro(QWidget* parent, -- GitLab From 8fb8af781aa983d0966a23c163168ced3db0290c Mon Sep 17 00:00:00 2001 From: John Harwell Date: Tue, 2 Apr 2019 10:00:53 -0500 Subject: [PATCH 266/427] Macros Dialog: correct and improve some layout issues including excessive space between command parameters when height of dialog was increased. --- src/GuiQt/WuQMacroDialog.cxx | 59 +++++++++++++++++------------------- 1 file changed, 28 insertions(+), 31 deletions(-) diff --git a/src/GuiQt/WuQMacroDialog.cxx b/src/GuiQt/WuQMacroDialog.cxx index 6e3538577..f8ce15453 100644 --- a/src/GuiQt/WuQMacroDialog.cxx +++ b/src/GuiQt/WuQMacroDialog.cxx @@ -362,6 +362,7 @@ WuQMacroDialog::createMacroDisplayWidget() QGridLayout* gridLayout = new QGridLayout(); gridLayout->setContentsMargins(0, 0, 0, 0); + gridLayout->setVerticalSpacing(5); gridLayout->setColumnStretch(0, 0); gridLayout->setColumnStretch(1, 0); gridLayout->setColumnStretch(2, 100); @@ -626,10 +627,10 @@ WuQMacroDialog::createCommandDisplayWidget() QLabel* titleLabel = new QLabel("Title:"); m_commandTitleLabel = new QLabel(); - QLabel* nameLabel = new QLabel("Name:"); + QLabel* nameLabel = new QLabel("GUI Name:"); m_commandNameLabel = new QLabel(); - QLabel* typeLabel = new QLabel("Type:"); + QLabel* typeLabel = new QLabel("GUI Type:"); m_commandTypeLabel = new QLabel(); QLabel* delayLabel = new QLabel("Delay:"); @@ -651,54 +652,50 @@ WuQMacroDialog::createCommandDisplayWidget() QObject::connect(m_commandDescriptionTextEdit, &QPlainTextEdit::textChanged, this, &WuQMacroDialog::macroCommandDescriptionTextEditChanged); - QGridLayout* layout = new QGridLayout(); - layout->setContentsMargins(0, 0, 0, 0); - layout->setColumnStretch(0, 0); - layout->setColumnStretch(1, 0); - layout->setColumnStretch(2, 100); + QWidget* commandInfoWidget = new QWidget(); + commandInfoWidget->setSizePolicy(QSizePolicy(QSizePolicy::MinimumExpanding, + QSizePolicy::Fixed)); + QGridLayout* commandInfoLayout = new QGridLayout(commandInfoWidget); + commandInfoLayout->setContentsMargins(0, 0, 0, 0); + commandInfoLayout->setColumnStretch(0, 0); + commandInfoLayout->setColumnStretch(1, 0); + commandInfoLayout->setColumnStretch(2, 100); int row = 0; - layout->addWidget(titleLabel, row, 0); - layout->addWidget(m_commandTitleLabel, row, 1, 1, 2, Qt::AlignLeft); + commandInfoLayout->addWidget(titleLabel, row, 0); + commandInfoLayout->addWidget(m_commandTitleLabel, row, 1, 1, 2, Qt::AlignLeft); row++; - layout->addWidget(nameLabel, row, 0); - layout->addWidget(m_commandNameLabel, row, 1, 1, 2, Qt::AlignLeft); + commandInfoLayout->addWidget(nameLabel, row, 0); + commandInfoLayout->addWidget(m_commandNameLabel, row, 1, 1, 2, Qt::AlignLeft); row++; - layout->addWidget(typeLabel, row, 0); - layout->addWidget(m_commandTypeLabel, row, 1, 1, 2, Qt::AlignLeft); + commandInfoLayout->addWidget(typeLabel, row, 0); + commandInfoLayout->addWidget(m_commandTypeLabel, row, 1, 1, 2, Qt::AlignLeft); row++; - layout->addWidget(delayLabel, row, 0); - layout->addWidget(m_commandDelaySpinBox, row, 1); - layout->addWidget(delayTwoLabel, row, 2, Qt::AlignLeft); + commandInfoLayout->addWidget(delayLabel, row, 0); + commandInfoLayout->addWidget(m_commandDelaySpinBox, row, 1); + commandInfoLayout->addWidget(delayTwoLabel, row, 2, Qt::AlignLeft); row++; - layout->addWidget(descriptionLabel, row, 0, (Qt::AlignLeft | Qt::AlignTop)); - layout->addWidget(m_commandDescriptionTextEdit, row, 1, 1, 2); + commandInfoLayout->addWidget(descriptionLabel, row, 0, (Qt::AlignLeft | Qt::AlignTop)); + commandInfoLayout->addWidget(m_commandDescriptionTextEdit, row, 1, 1, 2); row++; QWidget* parametersWidget = new QWidget(); + parametersWidget->setSizePolicy(QSizePolicy(QSizePolicy::MinimumExpanding, + QSizePolicy::Fixed)); m_parameterWidgetsGridLayout = new QGridLayout(parametersWidget); - int pl(0), pt(0), pr(0), pb(0); - m_parameterWidgetsGridLayout->getContentsMargins(&pl, &pt, &pr, &pb); - pt = 0; - m_parameterWidgetsGridLayout->setContentsMargins(pl, pt, pr, pb); - m_parameterWidgetsGridLayout->setVerticalSpacing(static_cast(m_parameterWidgetsGridLayout->verticalSpacing() / 1.2)); + m_parameterWidgetsGridLayout->setContentsMargins(0, 0, 0, 0); + m_parameterWidgetsGridLayout->setVerticalSpacing(2); m_parameterWidgetsGridLayout->setColumnStretch(0, 0); m_parameterWidgetsGridLayout->setColumnStretch(1, 100); - m_parameterWidgetsGridLayout->setRowStretch(1000, 1000); /* push items up */ - QHBoxLayout* titleLayout = new QHBoxLayout(); - titleLayout->setContentsMargins(0, 0, 0, 0); - titleLayout->addWidget(new QLabel("Command")); - titleLayout->addWidget(createHorizontalLine(), 100); QHBoxLayout* parametersLayout = new QHBoxLayout(); parametersLayout->setContentsMargins(0, 0, 0, 0); - parametersLayout->addWidget(new QLabel("Edit Parameters")); + parametersLayout->addWidget(new QLabel("Parameters")); parametersLayout->addWidget(createHorizontalLine(), 100); QWidget* widget = new QWidget(); QVBoxLayout* widgetLayout = new QVBoxLayout(widget); - widgetLayout->addLayout(titleLayout); - widgetLayout->addLayout(layout); + widgetLayout->addWidget(commandInfoWidget); widgetLayout->addLayout(parametersLayout); widgetLayout->addWidget(parametersWidget); widgetLayout->addStretch(); -- GitLab From e13ac723131877f95b79557d2d8d934b71ca8fb4 Mon Sep 17 00:00:00 2001 From: John Harwell Date: Tue, 2 Apr 2019 11:11:33 -0500 Subject: [PATCH 267/427] Macros Dialog: Implement Matt's suggestion for Macros Dialog. Removed 'splitter' between macro/command selection and macro/command editing widgets. Let selection widget expand to fill any extra vertical space. --- src/GuiQt/WuQMacroDialog.cxx | 45 +++++++++++++++++++++++++----------- 1 file changed, 32 insertions(+), 13 deletions(-) diff --git a/src/GuiQt/WuQMacroDialog.cxx b/src/GuiQt/WuQMacroDialog.cxx index f8ce15453..375937de9 100644 --- a/src/GuiQt/WuQMacroDialog.cxx +++ b/src/GuiQt/WuQMacroDialog.cxx @@ -159,19 +159,38 @@ WuQMacroDialog::WuQMacroDialog(QWidget* parent) QObject::connect(m_dialogButtonBox, &QDialogButtonBox::clicked, this, &WuQMacroDialog::buttonBoxButtonClicked); - QSplitter* splitter = new QSplitter(); - splitter->setOrientation(Qt::Vertical); - macroSelectionWidget->setMinimumHeight(50); - splitter->addWidget(macroSelectionWidget); - stackedScrollArea->setMinimumHeight(50); - splitter->addWidget(stackedScrollArea); - splitter->setStretchFactor(0, 35); - splitter->setStretchFactor(1, 65); - - QVBoxLayout* dialogLayout = new QVBoxLayout(this); - dialogLayout->addLayout(gridLayout); - dialogLayout->addWidget(splitter); - dialogLayout->addWidget(m_dialogButtonBox); + const bool splitterFlag(false); + if (splitterFlag) { + /* + * Use a splitter between the list widget (macro/command selection) + * and the parameters (macro/command editing). + * Splitter allows user to allocate space between the two. + */ + QSplitter* splitter = new QSplitter(); + splitter->setOrientation(Qt::Vertical); + macroSelectionWidget->setMinimumHeight(50); + splitter->addWidget(macroSelectionWidget); + stackedScrollArea->setMinimumHeight(50); + splitter->addWidget(stackedScrollArea); + splitter->setStretchFactor(0, 35); + splitter->setStretchFactor(1, 65); + + QVBoxLayout* dialogLayout = new QVBoxLayout(this); + dialogLayout->addLayout(gridLayout); + dialogLayout->addWidget(splitter); + dialogLayout->addWidget(m_dialogButtonBox); + } + else { + /* + * Stretch the list widget (macro/command selection) + * but no stretch for the parameters (macro/command editing) + */ + QVBoxLayout* dialogLayout = new QVBoxLayout(this); + dialogLayout->addLayout(gridLayout); + dialogLayout->addWidget(macroSelectionWidget, 100); + dialogLayout->addWidget(stackedScrollArea, 0); + dialogLayout->addWidget(m_dialogButtonBox, 0); + } updateDialogContents(); -- GitLab From 1288188039674d05890403ab7f5d66d7725747eb Mon Sep 17 00:00:00 2001 From: John Harwell Date: Tue, 2 Apr 2019 14:06:44 -0500 Subject: [PATCH 268/427] Macros: Fix bug that improperly limited range of float/integer parameters in macro commands on macros dialog. --- src/GuiQt/WuQMacroCommandParameterWidget.cxx | 22 ++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/src/GuiQt/WuQMacroCommandParameterWidget.cxx b/src/GuiQt/WuQMacroCommandParameterWidget.cxx index db778d99e..688ba5512 100644 --- a/src/GuiQt/WuQMacroCommandParameterWidget.cxx +++ b/src/GuiQt/WuQMacroCommandParameterWidget.cxx @@ -226,14 +226,36 @@ WuQMacroCommandParameterWidget::updateContent(int32_t windowIndex, break; case WuQMacroDataValueTypeEnum::FLOAT: { + std::array floatRange { -1.0e6, 1.0e6 }; + if (customDataFlag) { + WbMacroCustomDataInfo customDataInfo(WuQMacroDataValueTypeEnum::FLOAT); + WuQMacroManager::instance()->getCustomParameterDataInfo(windowIndex, + macroCommand, + parameter, + customDataInfo); + floatRange = customDataInfo.getFloatRange(); + } + QSignalBlocker blocker(m_doubleSpinBox); + m_doubleSpinBox->setRange(floatRange[0], floatRange[1]); m_doubleSpinBox->setValue(m_parameter->getValue().toFloat()); activeWidget = m_doubleSpinBox; } break; case WuQMacroDataValueTypeEnum::INTEGER: { + std::array intRange { -100000, 100000 }; + if (customDataFlag) { + WbMacroCustomDataInfo customDataInfo(WuQMacroDataValueTypeEnum::INTEGER); + WuQMacroManager::instance()->getCustomParameterDataInfo(windowIndex, + macroCommand, + parameter, + customDataInfo); + intRange = customDataInfo.getIntegerRange(); + } + QSignalBlocker blocker(m_spinBox); + m_spinBox->setRange(intRange[0], intRange[1]); m_spinBox->setValue(m_parameter->getValue().toInt()); activeWidget = m_spinBox; } -- GitLab From c46a38ef5ba84cef469cfe2cae067db202b2afaf Mon Sep 17 00:00:00 2001 From: John Harwell Date: Tue, 2 Apr 2019 14:44:24 -0500 Subject: [PATCH 269/427] Macros dialog: More improvements including using only a horizontal scrollbar for the bottom part of the dialog containing information about selected macro or command. --- src/GuiQt/WuQMacroCommandParameterWidget.cxx | 23 ++++++++++++++++++++ src/GuiQt/WuQMacroCommandParameterWidget.h | 2 ++ src/GuiQt/WuQMacroDialog.cxx | 5 ++++- 3 files changed, 29 insertions(+), 1 deletion(-) diff --git a/src/GuiQt/WuQMacroCommandParameterWidget.cxx b/src/GuiQt/WuQMacroCommandParameterWidget.cxx index 688ba5512..fee97c228 100644 --- a/src/GuiQt/WuQMacroCommandParameterWidget.cxx +++ b/src/GuiQt/WuQMacroCommandParameterWidget.cxx @@ -69,6 +69,7 @@ WuQMacroCommandParameterWidget::WuQMacroCommandParameterWidget(const int32_t ind m_index(index) { m_nameLabel = new QLabel(); + modifySizePolicy(m_nameLabel); m_booleanOnAction = new QAction("On"); m_booleanOnAction->setCheckable(true); @@ -132,6 +133,12 @@ m_index(index) m_stackedWidget->addWidget(m_spinBox); m_stackedWidget->addWidget(m_noValueWidget); + const int numWidgets = m_stackedWidget->count(); + for (int32_t i = 0; i < numWidgets; i++) { + modifySizePolicy(m_stackedWidget->widget(i)); + } + modifySizePolicy(m_stackedWidget); + /* * Note: A QStackedWidget aligns its current widget at the top. * So, align the label at the top so that the label and widget @@ -149,6 +156,22 @@ WuQMacroCommandParameterWidget::~WuQMacroCommandParameterWidget() { } +/** + * Modify the size policy so that widget does not retain + * size when hidden + * + * @param w + * The widget + */ +void +WuQMacroCommandParameterWidget::modifySizePolicy(QWidget* w) +{ + CaretAssert(w); + QSizePolicy sp = w->sizePolicy(); + sp.setRetainSizeWhenHidden(false); + w->setSizePolicy(sp); +} + /** * Update content of a command parameter * diff --git a/src/GuiQt/WuQMacroCommandParameterWidget.h b/src/GuiQt/WuQMacroCommandParameterWidget.h index 77bc4e928..5b6fbc38b 100644 --- a/src/GuiQt/WuQMacroCommandParameterWidget.h +++ b/src/GuiQt/WuQMacroCommandParameterWidget.h @@ -78,6 +78,8 @@ namespace caret { void spinBoxValueChanged(int); private: + void modifySizePolicy(QWidget* w); + QLabel* m_nameLabel; const int32_t m_index; diff --git a/src/GuiQt/WuQMacroDialog.cxx b/src/GuiQt/WuQMacroDialog.cxx index 375937de9..b429729fc 100644 --- a/src/GuiQt/WuQMacroDialog.cxx +++ b/src/GuiQt/WuQMacroDialog.cxx @@ -145,11 +145,14 @@ WuQMacroDialog::WuQMacroDialog(QWidget* parent) m_commandWidget = createCommandDisplayWidget(); m_emptyWidget = new QWidget(); m_stackedWidget = new QStackedWidget(); + m_stackedWidget->setSizePolicy(QSizePolicy::Fixed, + QSizePolicy::Fixed); m_stackedWidget->addWidget(m_macroWidget); m_stackedWidget->addWidget(m_commandWidget); m_stackedWidget->addWidget(m_emptyWidget); m_stackedWidget->setCurrentWidget(m_emptyWidget); QScrollArea* stackedScrollArea = new QScrollArea(); + stackedScrollArea->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff); stackedScrollArea->setWidget(m_stackedWidget); stackedScrollArea->setWidgetResizable(true); @@ -170,7 +173,6 @@ WuQMacroDialog::WuQMacroDialog(QWidget* parent) splitter->setOrientation(Qt::Vertical); macroSelectionWidget->setMinimumHeight(50); splitter->addWidget(macroSelectionWidget); - stackedScrollArea->setMinimumHeight(50); splitter->addWidget(stackedScrollArea); splitter->setStretchFactor(0, 35); splitter->setStretchFactor(1, 65); @@ -717,6 +719,7 @@ WuQMacroDialog::createCommandDisplayWidget() widgetLayout->addWidget(commandInfoWidget); widgetLayout->addLayout(parametersLayout); widgetLayout->addWidget(parametersWidget); + widgetLayout->addSpacing(6); widgetLayout->addStretch(); return widget; -- GitLab From 75360162f8caef2f2ec9816581a83b292b2599aa Mon Sep 17 00:00:00 2001 From: John Harwell Date: Thu, 4 Apr 2019 11:23:06 -0500 Subject: [PATCH 270/427] Volume Surface Outline Drawing: Implemented caching of the surface outlines for orthogonal slice drawing. In an instance with A, C, and P slices with four contours, drawing time is reduced from 0.064 seconds to 0.018 seconds. An item on the Develop Menu, "New (faster) Volume Surface Outline" allows toggling between this new cached and the old, uncached volume slice drawing for timing comparison. --- .../BrainOpenGLVolumeObliqueSliceDrawing.cxx | 3 + src/Brain/BrainOpenGLVolumeSliceDrawing.cxx | 248 +++++++++++++++++- src/Brain/BrainOpenGLVolumeSliceDrawing.h | 17 ++ src/Brain/CMakeLists.txt | 4 + .../VolumeSurfaceOutlineColorOrTabModel.cxx | 13 + .../VolumeSurfaceOutlineColorOrTabModel.h | 2 + src/Brain/VolumeSurfaceOutlineModel.cxx | 143 +++++++++- src/Brain/VolumeSurfaceOutlineModel.h | 34 ++- .../VolumeSurfaceOutlineModelCacheKey.cxx | 158 +++++++++++ src/Brain/VolumeSurfaceOutlineModelCacheKey.h | 70 +++++ .../VolumeSurfaceOutlineModelCacheValue.cxx | 95 +++++++ .../VolumeSurfaceOutlineModelCacheValue.h | 68 +++++ src/Common/DeveloperFlagsEnum.cxx | 7 + src/Common/DeveloperFlagsEnum.h | 1 + 14 files changed, 855 insertions(+), 8 deletions(-) create mode 100644 src/Brain/VolumeSurfaceOutlineModelCacheKey.cxx create mode 100644 src/Brain/VolumeSurfaceOutlineModelCacheKey.h create mode 100644 src/Brain/VolumeSurfaceOutlineModelCacheValue.cxx create mode 100644 src/Brain/VolumeSurfaceOutlineModelCacheValue.h diff --git a/src/Brain/BrainOpenGLVolumeObliqueSliceDrawing.cxx b/src/Brain/BrainOpenGLVolumeObliqueSliceDrawing.cxx index 0c8e80991..39e07c4b5 100644 --- a/src/Brain/BrainOpenGLVolumeObliqueSliceDrawing.cxx +++ b/src/Brain/BrainOpenGLVolumeObliqueSliceDrawing.cxx @@ -1098,6 +1098,9 @@ BrainOpenGLVolumeObliqueSliceDrawing::drawLayers(const VolumeSliceDrawingTypeEnu if (drawOutlineFlag) { BrainOpenGLVolumeSliceDrawing::drawSurfaceOutline(m_modelType, + sliceProjectionType, + sliceViewPlane, + sliceCoordinates, slicePlane, m_browserTabContent->getVolumeSurfaceOutlineSet(), m_fixedPipelineDrawing, diff --git a/src/Brain/BrainOpenGLVolumeSliceDrawing.cxx b/src/Brain/BrainOpenGLVolumeSliceDrawing.cxx index 2fadddd9b..630de1671 100644 --- a/src/Brain/BrainOpenGLVolumeSliceDrawing.cxx +++ b/src/Brain/BrainOpenGLVolumeSliceDrawing.cxx @@ -2704,6 +2704,9 @@ BrainOpenGLVolumeSliceDrawing::drawLayers(const VolumeSliceDrawingTypeEnum::Enum if (drawOutlineFlag) { drawSurfaceOutline(m_modelType, + sliceProjectionType, + sliceViewPlane, + sliceCoordinates, slicePlane, m_browserTabContent->getVolumeSurfaceOutlineSet(), m_fixedPipelineDrawing, @@ -2765,6 +2768,12 @@ BrainOpenGLVolumeSliceDrawing::drawLayers(const VolumeSliceDrawingTypeEnum::Enum * * @param modelType * Type of model being drawn. + * @param sliceProjectionType + * Projection type (oblique/orthogonal) + * @param sliceViewPlane + * Slice view plane (axial, coronal, parasagittal) + * @param sliceXYZ + * Coordinates of slices * @param plane * Plane of the volume slice on which surface outlines are drawn. * @param outlineSet @@ -2776,15 +2785,78 @@ BrainOpenGLVolumeSliceDrawing::drawLayers(const VolumeSliceDrawingTypeEnum::Enum */ void BrainOpenGLVolumeSliceDrawing::drawSurfaceOutline(const ModelTypeEnum::Enum modelType, + const VolumeSliceProjectionTypeEnum::Enum sliceProjectionType, + const VolumeSliceViewPlaneEnum::Enum sliceViewPlane, + const float sliceXYZ[3], const Plane& plane, VolumeSurfaceOutlineSetModel* outlineSet, BrainOpenGLFixedPipeline* fixedPipelineDrawing, const bool useNegativePolygonOffsetFlag) +{ + /* + * Code still here to allow comparison with + * previous algorithm + */ + bool drawCachedFlag(false); + switch (sliceProjectionType) { + case VolumeSliceProjectionTypeEnum::VOLUME_SLICE_PROJECTION_OBLIQUE: + break; + case VolumeSliceProjectionTypeEnum::VOLUME_SLICE_PROJECTION_ORTHOGONAL: + if (DeveloperFlagsEnum::isFlag(DeveloperFlagsEnum::DEVELOPER_FLAG_NEW_VOLUME_SURFACE_OUTLINE)) { + drawCachedFlag = true; + } + break; + } + + if (drawCachedFlag) { + drawSurfaceOutlineCached(modelType, + sliceViewPlane, + sliceXYZ, + plane, + outlineSet, + fixedPipelineDrawing, + useNegativePolygonOffsetFlag); + } + else { + drawSurfaceOutlineNotCached(modelType, + plane, + outlineSet, + fixedPipelineDrawing, + useNegativePolygonOffsetFlag); + } +} + +/** + * Draw surface outlines on the volume slices + * + * @param modelType + * Type of model being drawn. + * @param sliceViewPlane + * Slice view plane (axial, coronal, parasagittal) + * @param sliceXYZ + * Coordinates of slices + * @param plane + * Plane of the volume slice on which surface outlines are drawn. + * @param outlineSet + * The surface outline set. + * @param fixedPipelineDrawing + * The fixed pipeline drawing. + * @param useNegativePolygonOffsetFlag + * If true, use a negative offset for polygon offset + */ +void +BrainOpenGLVolumeSliceDrawing::drawSurfaceOutlineCached(const ModelTypeEnum::Enum modelType, + const VolumeSliceViewPlaneEnum::Enum sliceViewPlane, + const float sliceXYZ[3], + const Plane& plane, + VolumeSurfaceOutlineSetModel* outlineSet, + BrainOpenGLFixedPipeline* fixedPipelineDrawing, + const bool useNegativePolygonOffsetFlag) { glPushAttrib(GL_ENABLE_BIT); glDisable(GL_DEPTH_TEST); glDisable(GL_LIGHTING); - + switch (modelType) { case ModelTypeEnum::MODEL_TYPE_CHART: break; @@ -2808,7 +2880,173 @@ BrainOpenGLVolumeSliceDrawing::drawSurfaceOutline(const ModelTypeEnum::Enum mode glEnable(GL_DEPTH_TEST); break; } + + float sliceCoordinate(0.0); + switch (sliceViewPlane) { + case VolumeSliceViewPlaneEnum::ALL: + break; + case VolumeSliceViewPlaneEnum::AXIAL: + sliceCoordinate = sliceXYZ[2]; + break; + case VolumeSliceViewPlaneEnum::CORONAL: + sliceCoordinate = sliceXYZ[1]; + break; + case VolumeSliceViewPlaneEnum::PARASAGITTAL: + sliceCoordinate = sliceXYZ[0]; + break; + } + + /* + * Key for outline cache + */ + VolumeSurfaceOutlineModelCacheKey outlineCacheKey(sliceViewPlane, + sliceCoordinate); + + /* + * Process each surface outline + * As of 24 May, "zero" is on top so draw in reverse order + */ + const int32_t numberOfOutlines = outlineSet->getNumberOfDislayedVolumeSurfaceOutlines(); + for (int32_t io = (numberOfOutlines - 1); + io >= 0; + io--) { + std::vector contourPrimitives; + + VolumeSurfaceOutlineModel* outline = outlineSet->getVolumeSurfaceOutlineModel(io); + if (outline->isDisplayed()) { + Surface* surface = outline->getSurface(); + if (surface != NULL) { + float thicknessPercentage = outline->getThicknessPercentageViewportHeight(); + const float thicknessPixels = outline->getThicknessPixelsObsolete(); + + /* + * Thickness was changed from pixels to percentage viewport height on Feb 02, 2018 + * If thickness percentage is negative, it was not present in an old + * scene so convert pixels to percentage using viewports dimensions + */ + if (thicknessPercentage < 0.0f) { + thicknessPercentage = GraphicsUtilitiesOpenGL::convertPixelsToPercentageOfViewportHeight(thicknessPixels); + if (thicknessPercentage > 0.0f) { + outline->setThicknessPercentageViewportHeight(thicknessPercentage); + } + } + + if (outline->getOutlineCachePrimitives(outlineCacheKey, + contourPrimitives)) { + /* OK, have cached primitives to draw */ + } + else { + CaretColorEnum::Enum outlineColor = CaretColorEnum::BLACK; + int32_t colorSourceBrowserTabIndex = -1; + + VolumeSurfaceOutlineColorOrTabModel* colorOrTabModel = outline->getColorOrTabModel(); + VolumeSurfaceOutlineColorOrTabModel::Item* selectedColorOrTabItem = colorOrTabModel->getSelectedItem(); + switch (selectedColorOrTabItem->getItemType()) { + case VolumeSurfaceOutlineColorOrTabModel::Item::ITEM_TYPE_BROWSER_TAB: + colorSourceBrowserTabIndex = selectedColorOrTabItem->getBrowserTabIndex(); + outlineColor = CaretColorEnum::CUSTOM; + break; + case VolumeSurfaceOutlineColorOrTabModel::Item::ITEM_TYPE_COLOR: + outlineColor = selectedColorOrTabItem->getColor(); + break; + } + const bool surfaceColorFlag = (colorSourceBrowserTabIndex >= 0); + + float* nodeColoringRGBA = NULL; + if (surfaceColorFlag) { + nodeColoringRGBA = fixedPipelineDrawing->surfaceNodeColoring->colorSurfaceNodes(NULL, + surface, + colorSourceBrowserTabIndex); + } + + SurfacePlaneIntersectionToContour contour(surface, + plane, + outlineColor, + nodeColoringRGBA, + thicknessPercentage); + AString errorMessage; + if ( ! contour.createContours(contourPrimitives, + errorMessage)) { + CaretLogSevere(errorMessage); + } + + outline->setOutlineCachePrimitives(outlineCacheKey, + contourPrimitives); + } + } + } + + /** + * Draw the contours. + * Note: The primitives are now cached so DO NOT delete them. + */ + for (auto primitive : contourPrimitives) { + if (useNegativePolygonOffsetFlag) { + glPolygonOffset(-1.0, -1.0); + } + else { + glPolygonOffset(1.0, 1.0); + } + glEnable(GL_POLYGON_OFFSET_FILL); + + GraphicsEngineDataOpenGL::draw(primitive); + + glDisable(GL_POLYGON_OFFSET_FILL); + } + } + + glPopAttrib(); +} +/** + * Draw surface outlines on the volume slices WITHOUT caching + * + * @param modelType + * Type of model being drawn. + * @param plane + * Plane of the volume slice on which surface outlines are drawn. + * @param outlineSet + * The surface outline set. + * @param fixedPipelineDrawing + * The fixed pipeline drawing. + * @param useNegativePolygonOffsetFlag + * If true, use a negative offset for polygon offset + */ +void +BrainOpenGLVolumeSliceDrawing::drawSurfaceOutlineNotCached(const ModelTypeEnum::Enum modelType, + const Plane& plane, + VolumeSurfaceOutlineSetModel* outlineSet, + BrainOpenGLFixedPipeline* fixedPipelineDrawing, + const bool useNegativePolygonOffsetFlag) +{ + glPushAttrib(GL_ENABLE_BIT); + glDisable(GL_DEPTH_TEST); + glDisable(GL_LIGHTING); + + switch (modelType) { + case ModelTypeEnum::MODEL_TYPE_CHART: + break; + case ModelTypeEnum::MODEL_TYPE_CHART_TWO: + break; + case ModelTypeEnum::MODEL_TYPE_INVALID: + break; + case ModelTypeEnum::MODEL_TYPE_SURFACE: + break; + case ModelTypeEnum::MODEL_TYPE_SURFACE_MONTAGE: + break; + case ModelTypeEnum::MODEL_TYPE_VOLUME_SLICES: + break; + case ModelTypeEnum::MODEL_TYPE_WHOLE_BRAIN: + /* + * Enable depth so outlines in front or in back + * of the slices. Without this the volume surface + * outlines "behind" the slices are visible and + * it looks weird + */ + glEnable(GL_DEPTH_TEST); + break; + } + /* * Process each surface outline * As of 24 May, "zero" is on top so draw in reverse order @@ -2834,7 +3072,6 @@ BrainOpenGLVolumeSliceDrawing::drawSurfaceOutline(const ModelTypeEnum::Enum mode if (thicknessPercentage < 0.0f) { thicknessPercentage = GraphicsUtilitiesOpenGL::convertPixelsToPercentageOfViewportHeight(thicknessPixels); if (thicknessPercentage > 0.0f) { - //std::cout << "Converted pixel thickness=" << thicknessPixels << " to millimeters=" << thicknessMillimeters << std::endl; outline->setThicknessPercentageViewportHeight(thicknessPercentage); } } @@ -2858,8 +3095,8 @@ BrainOpenGLVolumeSliceDrawing::drawSurfaceOutline(const ModelTypeEnum::Enum mode float* nodeColoringRGBA = NULL; if (surfaceColorFlag) { nodeColoringRGBA = fixedPipelineDrawing->surfaceNodeColoring->colorSurfaceNodes(NULL, - surface, - colorSourceBrowserTabIndex); + surface, + colorSourceBrowserTabIndex); } //const float thicknessPercentage = GraphicsUtilitiesOpenGL::convertPixelsToPercentageOfViewportHeight(thicknessMillimeters); @@ -2897,7 +3134,8 @@ BrainOpenGLVolumeSliceDrawing::drawSurfaceOutline(const ModelTypeEnum::Enum mode glPopAttrib(); } - + + /** * Draw foci on volume slice. * diff --git a/src/Brain/BrainOpenGLVolumeSliceDrawing.h b/src/Brain/BrainOpenGLVolumeSliceDrawing.h index 5fbdf9d04..df8196c0f 100644 --- a/src/Brain/BrainOpenGLVolumeSliceDrawing.h +++ b/src/Brain/BrainOpenGLVolumeSliceDrawing.h @@ -252,11 +252,28 @@ namespace caret { const float sliceCoordinates[3]); static void drawSurfaceOutline(const ModelTypeEnum::Enum modelType, + const VolumeSliceProjectionTypeEnum::Enum sliceProjectionType, + const VolumeSliceViewPlaneEnum::Enum sliceViewPlane, + const float sliceXYZ[3], const Plane& plane, VolumeSurfaceOutlineSetModel* outlineSet, BrainOpenGLFixedPipeline* fixedPipelineDrawing, const bool useNegativePolygonOffsetFlag); + static void drawSurfaceOutlineCached(const ModelTypeEnum::Enum modelType, + const VolumeSliceViewPlaneEnum::Enum sliceViewPlane, + const float sliceXYZ[3], + const Plane& plane, + VolumeSurfaceOutlineSetModel* outlineSet, + BrainOpenGLFixedPipeline* fixedPipelineDrawing, + const bool useNegativePolygonOffsetFlag); + + static void drawSurfaceOutlineNotCached(const ModelTypeEnum::Enum modelType, + const Plane& plane, + VolumeSurfaceOutlineSetModel* outlineSet, + BrainOpenGLFixedPipeline* fixedPipelineDrawing, + const bool useNegativePolygonOffsetFlag); + void drawVolumeSliceFoci(const Plane& plane); void drawAxesCrosshairs(const VolumeSliceProjectionTypeEnum::Enum sliceProjectionType, diff --git a/src/Brain/CMakeLists.txt b/src/Brain/CMakeLists.txt index 959856269..f4a80cf1f 100644 --- a/src/Brain/CMakeLists.txt +++ b/src/Brain/CMakeLists.txt @@ -182,6 +182,8 @@ VolumeSliceSettings.h VolumeSliceViewAllPlanesLayoutEnum.h VolumeSurfaceOutlineColorOrTabModel.h VolumeSurfaceOutlineModel.h +VolumeSurfaceOutlineModelCacheKey.h +VolumeSurfaceOutlineModelCacheValue.h VolumeSurfaceOutlineSetModel.h WholeBrainSurfaceSettings.h WholeBrainVoxelDrawingMode.h @@ -339,6 +341,8 @@ VolumeSliceSettings.cxx VolumeSliceViewAllPlanesLayoutEnum.cxx VolumeSurfaceOutlineColorOrTabModel.cxx VolumeSurfaceOutlineModel.cxx +VolumeSurfaceOutlineModelCacheKey.cxx +VolumeSurfaceOutlineModelCacheValue.cxx VolumeSurfaceOutlineSetModel.cxx WholeBrainSurfaceSettings.cxx WholeBrainVoxelDrawingMode.cxx diff --git a/src/Brain/VolumeSurfaceOutlineColorOrTabModel.cxx b/src/Brain/VolumeSurfaceOutlineColorOrTabModel.cxx index 08fc0c622..bc8d0eee2 100644 --- a/src/Brain/VolumeSurfaceOutlineColorOrTabModel.cxx +++ b/src/Brain/VolumeSurfaceOutlineColorOrTabModel.cxx @@ -351,6 +351,19 @@ VolumeSurfaceOutlineColorOrTabModel::Item::~Item() } +/** + * Copy constructor. + * + * @param + * Item that is copied. + */ +VolumeSurfaceOutlineColorOrTabModel::Item::Item(const Item& item) +{ + m_color = item.m_color; + m_browserTabIndex = item.m_browserTabIndex; + m_itemType = item.m_itemType; +} + /** * Is this item equal to another item? * diff --git a/src/Brain/VolumeSurfaceOutlineColorOrTabModel.h b/src/Brain/VolumeSurfaceOutlineColorOrTabModel.h index 333256326..7d116d7ef 100644 --- a/src/Brain/VolumeSurfaceOutlineColorOrTabModel.h +++ b/src/Brain/VolumeSurfaceOutlineColorOrTabModel.h @@ -49,6 +49,8 @@ namespace caret { Item(const int32_t browserTabIndex); + Item(const Item& item); + ~Item(); bool isValid() const; diff --git a/src/Brain/VolumeSurfaceOutlineModel.cxx b/src/Brain/VolumeSurfaceOutlineModel.cxx index 25dc6e9e5..d72bd9e73 100644 --- a/src/Brain/VolumeSurfaceOutlineModel.cxx +++ b/src/Brain/VolumeSurfaceOutlineModel.cxx @@ -23,15 +23,18 @@ #include "VolumeSurfaceOutlineModel.h" #undef __VOLUME_SURFACE_OUTLINE_MODEL_DECLARE__ +#include "EventManager.h" +#include "EventSurfaceColoringInvalidate.h" #include "SceneClass.h" #include "SceneClassAssistant.h" #include "SurfaceSelectionModel.h" #include "SurfaceTypeEnum.h" #include "VolumeSurfaceOutlineColorOrTabModel.h" +#include "VolumeSurfaceOutlineModelCacheValue.h" using namespace caret; - +static const bool debugFlag(false); /** * \class VolumeSurfaceOutlineSelection @@ -73,6 +76,8 @@ VolumeSurfaceOutlineModel::VolumeSurfaceOutlineModel() m_thicknessPercentageViewportHeight = -1.0f; m_sceneAssistant->add("m_thicknessPercentageViewportHeight", &m_thicknessPercentageViewportHeight); m_thicknessPercentageViewportHeight = VolumeSurfaceOutlineModel::DEFAULT_LINE_THICKNESS_PERCENTAGE_VIEWPORT_HEIGHT; + + EventManager::get()->addEventListener(this, EventTypeEnum::EVENT_SURFACE_COLORING_INVALIDATE); } /** @@ -80,10 +85,14 @@ VolumeSurfaceOutlineModel::VolumeSurfaceOutlineModel() */ VolumeSurfaceOutlineModel::~VolumeSurfaceOutlineModel() { + clearOutlineCache(); + delete m_surfaceSelectionModel; delete m_colorOrTabModel; delete m_sceneAssistant; + + EventManager::get()->removeAllEventsFromListener(this); } /** @@ -101,6 +110,28 @@ VolumeSurfaceOutlineModel::copyVolumeSurfaceOutlineModel(VolumeSurfaceOutlineMod VolumeSurfaceOutlineColorOrTabModel* colorTabToCopy = modelToCopy->getColorOrTabModel(); m_colorOrTabModel->copyVolumeSurfaceOutlineColorOrTabModel(colorTabToCopy); + + clearOutlineCache(); +} + +/** + * Receive an event. + * + * @param event + * The event that the receive can respond to. + */ +void +VolumeSurfaceOutlineModel::receiveEvent(Event* event) +{ + CaretAssert(event); + + if (event->getEventType() == EventTypeEnum::EVENT_SURFACE_COLORING_INVALIDATE) { + EventSurfaceColoringInvalidate* colorEvent = dynamic_cast(event); + CaretAssert(colorEvent); + colorEvent->setEventProcessed(); + + clearOutlineCache(); + } } /** @@ -131,6 +162,10 @@ void VolumeSurfaceOutlineModel::setDisplayed(const bool displayed) { m_displayed = displayed; + + if ( ! m_displayed) { + clearOutlineCache(); + } } /** @@ -262,6 +297,112 @@ VolumeSurfaceOutlineModel::restoreFromScene(const SceneAttributes* sceneAttribut return; } + clearOutlineCache(); + m_sceneAssistant->restoreMembers(sceneAttributes, sceneClass); } + +/** + * Set the outline primitives for the given cache key + * + * @param key + * Key into the outline cache identifying axis and slice + * @param primitivesOut + * Input containing the primitives + */ +void +VolumeSurfaceOutlineModel::setOutlineCachePrimitives(const VolumeSurfaceOutlineModelCacheKey& key, + const std::vector& primitives) +{ + auto iter = m_outlineCache.find(key); + if (iter != m_outlineCache.end()) { + iter->second->setGraphicsPrimitive(primitives); + return; + } + + if (m_outlineCache.empty()) { + m_outlineCacheSurface = getSurface(); + m_outlineCacheThicknessPercentageViewportHeight = m_thicknessPercentageViewportHeight; + m_outlineCacheColorItem.reset(new VolumeSurfaceOutlineColorOrTabModel::Item(*m_colorOrTabModel->getSelectedItem())); + } + + if (debugFlag) { + std::cout << "Adding " << key.toString() << std::endl; + } + VolumeSurfaceOutlineModelCacheValue* value = new VolumeSurfaceOutlineModelCacheValue(); + value->setGraphicsPrimitive(primitives); + m_outlineCache.insert(std::make_pair(key, value)); +} + + +/** + * Get the outline primitives for the given cache key + * + * @param key + * Key into the outline cache identifying axis and slice + * @param primitivesOut + * Output containing the primitives + * @return + * Truie if outline primitives valid, else false. + */ +bool +VolumeSurfaceOutlineModel::getOutlineCachePrimitives(const VolumeSurfaceOutlineModelCacheKey& key, + std::vector& primitivesOut) +{ + validateOutlineCache(); + + auto iter = m_outlineCache.find(key); + if (iter != m_outlineCache.end()) { + primitivesOut = iter->second->getGraphicsPrimitives(); + if (debugFlag) { + std::cout << "Found " << iter->first.toString() << std::endl; + } + return true; + } + + return false; +} + +/** + * Validate the outline cache and if not valid, clear outline cache + */ +void +VolumeSurfaceOutlineModel::validateOutlineCache() +{ + /* + * Test for cache validity + */ + if (m_outlineCacheSurface == NULL) { + return; + } + + /* + * Has anything changed that invalidates the outline cache ? + */ + if ((m_outlineCacheSurface != getSurface()) + || (m_outlineCacheThicknessPercentageViewportHeight != m_thicknessPercentageViewportHeight) + || ( ! m_outlineCacheColorItem->equals(*m_colorOrTabModel->getSelectedItem()))) { + clearOutlineCache(); + } +} + +/** + * Clear the outline cache + */ +void +VolumeSurfaceOutlineModel::clearOutlineCache() +{ + if (debugFlag) { + std::cout << "Invalidating surface outline cache" << std::endl; + } + m_outlineCacheSurface = NULL; + m_outlineCacheThicknessPercentageViewportHeight = -1.0; + m_outlineCacheColorItem.reset(); + + for (auto iter : m_outlineCache) { + delete iter.second; + } + m_outlineCache.clear(); +} + diff --git a/src/Brain/VolumeSurfaceOutlineModel.h b/src/Brain/VolumeSurfaceOutlineModel.h index 64d7e47a3..85714b9dc 100644 --- a/src/Brain/VolumeSurfaceOutlineModel.h +++ b/src/Brain/VolumeSurfaceOutlineModel.h @@ -21,18 +21,24 @@ */ /*LICENSE_END*/ +#include + #include "CaretObject.h" +#include "EventListenerInterface.h" #include "SceneableInterface.h" +#include "VolumeSurfaceOutlineColorOrTabModel.h" +#include "VolumeSurfaceOutlineModelCacheKey.h" namespace caret { + class GraphicsPrimitive; class Surface; class SceneAttributes; class SceneClassAssistant; class SurfaceSelectionModel; - class VolumeSurfaceOutlineColorOrTabModel; + class VolumeSurfaceOutlineModelCacheValue; - class VolumeSurfaceOutlineModel : public CaretObject, public SceneableInterface { + class VolumeSurfaceOutlineModel : public CaretObject, public EventListenerInterface, public SceneableInterface { public: VolumeSurfaceOutlineModel(); @@ -41,6 +47,8 @@ namespace caret { void copyVolumeSurfaceOutlineModel(VolumeSurfaceOutlineModel* modelToCopy); + virtual void receiveEvent(Event* event) override; + bool isDisplayed() const; void setDisplayed(const bool displayed); @@ -63,6 +71,12 @@ namespace caret { const VolumeSurfaceOutlineColorOrTabModel* getColorOrTabModel() const; + void setOutlineCachePrimitives(const VolumeSurfaceOutlineModelCacheKey& key, + const std::vector& primitives); + + bool getOutlineCachePrimitives(const VolumeSurfaceOutlineModelCacheKey& key, + std::vector& primitivesOut); + virtual SceneClass* saveToScene(const SceneAttributes* sceneAttributes, const AString& instanceName); @@ -81,6 +95,10 @@ namespace caret { virtual AString toString() const; private: + void validateOutlineCache(); + + void clearOutlineCache(); + bool m_displayed; float m_thicknessPixelsObsolete; @@ -92,6 +110,18 @@ namespace caret { VolumeSurfaceOutlineColorOrTabModel* m_colorOrTabModel; SceneClassAssistant* m_sceneAssistant; + + /** Thickness when first outline is added to outline cache */ + float m_outlineCacheThicknessPercentageViewportHeight = -1.0; + + /** Surface when first outline is added to outline cache */ + Surface* m_outlineCacheSurface = NULL; + + /** Color Or Tab selection item when first outline is added to cache */ + std::unique_ptr m_outlineCacheColorItem; + + /** Cache for volume surface outlines */ + std::map m_outlineCache; }; #ifdef __VOLUME_SURFACE_OUTLINE_MODEL_DECLARE__ diff --git a/src/Brain/VolumeSurfaceOutlineModelCacheKey.cxx b/src/Brain/VolumeSurfaceOutlineModelCacheKey.cxx new file mode 100644 index 000000000..3c966c751 --- /dev/null +++ b/src/Brain/VolumeSurfaceOutlineModelCacheKey.cxx @@ -0,0 +1,158 @@ + +/*LICENSE_START*/ +/* + * Copyright (C) 2019 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + +#define __VOLUME_SURFACE_OUTLINE_MODEL_CACHE_KEY_DECLARE__ +#include "VolumeSurfaceOutlineModelCacheKey.h" +#undef __VOLUME_SURFACE_OUTLINE_MODEL_CACHE_KEY_DECLARE__ + +#include "CaretAssert.h" +using namespace caret; + + + +/** + * \class caret::VolumeSurfaceOutlineModelCacheKey + * \brief Key for a cached volume surface outline model + * \ingroup Brain + */ + +/** + * Constructor. + */ +VolumeSurfaceOutlineModelCacheKey::VolumeSurfaceOutlineModelCacheKey(const VolumeSliceViewPlaneEnum::Enum slicePlane, + const float sliceCoordinate) +: CaretObject(), +m_slicePlane(slicePlane), +m_sliceCoordinateScaled(static_cast(sliceCoordinate * 10.0)) +{ +} + +/** + * Destructor. + */ +VolumeSurfaceOutlineModelCacheKey::~VolumeSurfaceOutlineModelCacheKey() +{ +} + +/** + * Copy constructor. + * @param obj + * Object that is copied. + */ +VolumeSurfaceOutlineModelCacheKey::VolumeSurfaceOutlineModelCacheKey(const VolumeSurfaceOutlineModelCacheKey& obj) +: CaretObject(obj) +{ + this->copyHelperVolumeSurfaceOutlineModelCacheKey(obj); +} + +/** + * Assignment operator. + * @param obj + * Data copied from obj to this. + * @return + * Reference to this object. + */ +VolumeSurfaceOutlineModelCacheKey& +VolumeSurfaceOutlineModelCacheKey::operator=(const VolumeSurfaceOutlineModelCacheKey& obj) +{ + if (this != &obj) { + CaretObject::operator=(obj); + this->copyHelperVolumeSurfaceOutlineModelCacheKey(obj); + } + return *this; +} + +/** + * Helps with copying an object of this type. + * @param obj + * Object that is copied. + */ +void +VolumeSurfaceOutlineModelCacheKey::copyHelperVolumeSurfaceOutlineModelCacheKey(const VolumeSurfaceOutlineModelCacheKey& obj) +{ + m_slicePlane = obj.m_slicePlane; + m_sliceCoordinateScaled = obj.m_sliceCoordinateScaled; +} + +/** + * Equality operator. + * @param obj + * Instance compared to this for equality. + * @return + * True if this instance and 'obj' instance are considered equal. + */ +bool +VolumeSurfaceOutlineModelCacheKey::operator==(const VolumeSurfaceOutlineModelCacheKey& obj) const +{ + if (this == &obj) { + return true; + } + + /* perform equality testing HERE and return true if equal ! */ + if ((m_slicePlane == obj.m_slicePlane) + && (m_sliceCoordinateScaled == obj.m_sliceCoordinateScaled)) { + return true; + } + + return false; +} + +/** + * Less than operator. + * + * @param obj + * Instance compared to this for equality. + * @return + * True if this instance and 'obj' instance are considered equal. + */ +bool +VolumeSurfaceOutlineModelCacheKey::operator<(const VolumeSurfaceOutlineModelCacheKey& obj) const +{ + if (this == &obj) { + return false; + } + + /* perform equality testing HERE and return true if equal ! */ + if (static_cast(m_slicePlane) < static_cast(obj.m_slicePlane)) { + return true; + } + if (m_sliceCoordinateScaled < obj.m_sliceCoordinateScaled) { + return true; + } + + return false; +} + +/** + * Get a description of this object's content. + * @return String describing this object's content. + */ +AString +VolumeSurfaceOutlineModelCacheKey::toString() const +{ + QString str("Plane=" + + VolumeSliceViewPlaneEnum::toName(m_slicePlane) + + ", " + + "ScaledCoordinate=" + + QString::number(m_sliceCoordinateScaled)); + return str; +} + diff --git a/src/Brain/VolumeSurfaceOutlineModelCacheKey.h b/src/Brain/VolumeSurfaceOutlineModelCacheKey.h new file mode 100644 index 000000000..7484327ce --- /dev/null +++ b/src/Brain/VolumeSurfaceOutlineModelCacheKey.h @@ -0,0 +1,70 @@ +#ifndef __VOLUME_SURFACE_OUTLINE_MODEL_CACHE_KEY_H__ +#define __VOLUME_SURFACE_OUTLINE_MODEL_CACHE_KEY_H__ + +/*LICENSE_START*/ +/* + * Copyright (C) 2019 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + + + +#include + +#include "CaretObject.h" + +#include "VolumeSliceViewPlaneEnum.h" + +namespace caret { + + class VolumeSurfaceOutlineModelCacheKey : public CaretObject { + + public: + VolumeSurfaceOutlineModelCacheKey(const VolumeSliceViewPlaneEnum::Enum slicePlane, + const float sliceCoordinate); + + virtual ~VolumeSurfaceOutlineModelCacheKey(); + + VolumeSurfaceOutlineModelCacheKey(const VolumeSurfaceOutlineModelCacheKey& obj); + + VolumeSurfaceOutlineModelCacheKey& operator=(const VolumeSurfaceOutlineModelCacheKey& obj); + + bool operator==(const VolumeSurfaceOutlineModelCacheKey& obj) const; + + bool operator<(const VolumeSurfaceOutlineModelCacheKey& obj) const; + + // ADD_NEW_METHODS_HERE + + virtual AString toString() const; + + private: + void copyHelperVolumeSurfaceOutlineModelCacheKey(const VolumeSurfaceOutlineModelCacheKey& obj); + + VolumeSliceViewPlaneEnum::Enum m_slicePlane = VolumeSliceViewPlaneEnum::ALL; + + int32_t m_sliceCoordinateScaled = 100000; + + // ADD_NEW_MEMBERS_HERE + + }; + +#ifdef __VOLUME_SURFACE_OUTLINE_MODEL_CACHE_KEY_DECLARE__ + // +#endif // __VOLUME_SURFACE_OUTLINE_MODEL_CACHE_KEY_DECLARE__ + +} // namespace +#endif //__VOLUME_SURFACE_OUTLINE_MODEL_CACHE_KEY_H__ diff --git a/src/Brain/VolumeSurfaceOutlineModelCacheValue.cxx b/src/Brain/VolumeSurfaceOutlineModelCacheValue.cxx new file mode 100644 index 000000000..d7af8a145 --- /dev/null +++ b/src/Brain/VolumeSurfaceOutlineModelCacheValue.cxx @@ -0,0 +1,95 @@ + +/*LICENSE_START*/ +/* + * Copyright (C) 2019 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + +#define __VOLUME_SURFACE_OUTLINE_MODEL_CACHE_VALUE_DECLARE__ +#include "VolumeSurfaceOutlineModelCacheValue.h" +#undef __VOLUME_SURFACE_OUTLINE_MODEL_CACHE_VALUE_DECLARE__ + +#include "CaretAssert.h" +#include "GraphicsPrimitive.h" + +using namespace caret; + +/** + * \class caret::VolumeSurfaceOutlineModelCacheValue + * \brief Data for a cached surface outline model + * \ingroup Brain + */ + +/** + * Constructor. + */ +VolumeSurfaceOutlineModelCacheValue::VolumeSurfaceOutlineModelCacheValue() +: CaretObject() +{ + +} + +/** + * Destructor. + */ +VolumeSurfaceOutlineModelCacheValue::~VolumeSurfaceOutlineModelCacheValue() +{ + deletePrimitives(); +} + +/** + * Delete the primitives. + */ +void +VolumeSurfaceOutlineModelCacheValue::deletePrimitives() +{ + for (auto p : m_contourLinePrimitives) { + delete p; + } + m_contourLinePrimitives.clear(); +} + +/** + * @return Reference to the graphics primitives; + */ +std::vector +VolumeSurfaceOutlineModelCacheValue::getGraphicsPrimitives() const +{ + return m_contourLinePrimitives; +} + +/** + * Set the primitives in this cache. Any current primitives are replaced and destroyed + */ +void +VolumeSurfaceOutlineModelCacheValue::setGraphicsPrimitive(const std::vector& primitives) +{ + deletePrimitives(); + + m_contourLinePrimitives = primitives; +} + +/** + * Get a description of this object's content. + * @return String describing this object's content. + */ +AString +VolumeSurfaceOutlineModelCacheValue::toString() const +{ + return "VolumeSurfaceOutlineModelCacheValue"; +} + diff --git a/src/Brain/VolumeSurfaceOutlineModelCacheValue.h b/src/Brain/VolumeSurfaceOutlineModelCacheValue.h new file mode 100644 index 000000000..755777478 --- /dev/null +++ b/src/Brain/VolumeSurfaceOutlineModelCacheValue.h @@ -0,0 +1,68 @@ +#ifndef __VOLUME_SURFACE_OUTLINE_MODEL_CACHE_VALUE_H__ +#define __VOLUME_SURFACE_OUTLINE_MODEL_CACHE_VALUE_H__ + +/*LICENSE_START*/ +/* + * Copyright (C) 2019 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + + + +#include + +#include "CaretObject.h" + + + +namespace caret { + class GraphicsPrimitive; + + class VolumeSurfaceOutlineModelCacheValue : public CaretObject { + + public: + VolumeSurfaceOutlineModelCacheValue(); + + virtual ~VolumeSurfaceOutlineModelCacheValue(); + + VolumeSurfaceOutlineModelCacheValue(const VolumeSurfaceOutlineModelCacheValue&) = delete; + + VolumeSurfaceOutlineModelCacheValue& operator=(const VolumeSurfaceOutlineModelCacheValue&) = delete; + + void setGraphicsPrimitive(const std::vector& primitives); + + std::vector getGraphicsPrimitives() const; + + // ADD_NEW_METHODS_HERE + + virtual AString toString() const; + + private: + void deletePrimitives(); + + std::vector m_contourLinePrimitives; + + // ADD_NEW_MEMBERS_HERE + + }; + +#ifdef __VOLUME_SURFACE_OUTLINE_MODEL_CACHE_VALUE_DECLARE__ + // +#endif // __VOLUME_SURFACE_OUTLINE_MODEL_CACHE_VALUE_DECLARE__ + +} // namespace +#endif //__VOLUME_SURFACE_OUTLINE_MODEL_CACHE_VALUE_H__ diff --git a/src/Common/DeveloperFlagsEnum.cxx b/src/Common/DeveloperFlagsEnum.cxx index c8667b697..80e9eb80d 100644 --- a/src/Common/DeveloperFlagsEnum.cxx +++ b/src/Common/DeveloperFlagsEnum.cxx @@ -124,6 +124,13 @@ DeveloperFlagsEnum::initialize() "Flip Palette Not Data", CheckableEnum::YES, false)); + checkableItems.push_back(DeveloperFlagsEnum(DEVELOPER_FLAG_NEW_VOLUME_SURFACE_OUTLINE, + "DEVELOPER_FLAG_NEW_VOLUME_SURFACE_OUTLINE", + "New (faster) Volume Surface Outline", + CheckableEnum::YES, + true)); + + checkableItems.push_back(DeveloperFlagsEnum(DEVELOPER_FLAG_NEW_SCENE_FILE_READ_WRITE, "DEVELOPER_FLAG_NEW_SCENE_FILE_READ_WRITE", "Use New Scene File Reader and Writer", diff --git a/src/Common/DeveloperFlagsEnum.h b/src/Common/DeveloperFlagsEnum.h index 63a32fa3d..057f32be2 100644 --- a/src/Common/DeveloperFlagsEnum.h +++ b/src/Common/DeveloperFlagsEnum.h @@ -37,6 +37,7 @@ public: enum Enum { DEVELOPER_FLAG_UNUSED, DEVELOPER_FLAG_FLIP_PALETTE_NOT_DATA, + DEVELOPER_FLAG_NEW_VOLUME_SURFACE_OUTLINE, DEVELOPER_FLAG_NEW_SCENE_FILE_READ_WRITE, DEVELOPER_FLAG_TEST_SCENE_FILE_READ_WRITE, DEVELOPER_FLAG_TIME_SCENE_FILE_READ_WRITE, -- GitLab From 039d7515a4108b2df9ea2942e9214221129750b0 Mon Sep 17 00:00:00 2001 From: John Harwell Date: Thu, 4 Apr 2019 12:01:18 -0500 Subject: [PATCH 271/427] Volume Surface Outline Drawing: Fixed bug in cached volume surface outline key's 'less than' method and use rounding when converting the cached sliced coordinate to a scaled integer. --- src/Brain/VolumeSurfaceOutlineModelCacheKey.cxx | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/Brain/VolumeSurfaceOutlineModelCacheKey.cxx b/src/Brain/VolumeSurfaceOutlineModelCacheKey.cxx index 3c966c751..ddce02bf5 100644 --- a/src/Brain/VolumeSurfaceOutlineModelCacheKey.cxx +++ b/src/Brain/VolumeSurfaceOutlineModelCacheKey.cxx @@ -23,6 +23,8 @@ #include "VolumeSurfaceOutlineModelCacheKey.h" #undef __VOLUME_SURFACE_OUTLINE_MODEL_CACHE_KEY_DECLARE__ +#include + #include "CaretAssert.h" using namespace caret; @@ -41,7 +43,7 @@ VolumeSurfaceOutlineModelCacheKey::VolumeSurfaceOutlineModelCacheKey(const Volum const float sliceCoordinate) : CaretObject(), m_slicePlane(slicePlane), -m_sliceCoordinateScaled(static_cast(sliceCoordinate * 10.0)) +m_sliceCoordinateScaled(static_cast(std::round(sliceCoordinate * 10.0))) { } @@ -130,10 +132,13 @@ VolumeSurfaceOutlineModelCacheKey::operator<(const VolumeSurfaceOutlineModelCach return false; } - /* perform equality testing HERE and return true if equal ! */ if (static_cast(m_slicePlane) < static_cast(obj.m_slicePlane)) { return true; } + else if (static_cast(m_slicePlane) > static_cast(obj.m_slicePlane)) { + return false; + } + if (m_sliceCoordinateScaled < obj.m_sliceCoordinateScaled) { return true; } -- GitLab From 6a01c9ff4c630e8dd971e6b03d6ad9f6911aefe4 Mon Sep 17 00:00:00 2001 From: John Harwell Date: Thu, 4 Apr 2019 14:59:08 -0500 Subject: [PATCH 272/427] Macros: Renamed custom operation commands to indicate that they perform an animation operation. --- src/GuiQt/CMakeLists.txt | 20 +++---- ...ustomOperationAnimateOverlayCrossFade.cxx} | 34 +++++------ ...oCustomOperationAnimateOverlayCrossFade.h} | 20 +++---- ...WbMacroCustomOperationAnimateRotation.cxx} | 26 ++++---- ...> WbMacroCustomOperationAnimateRotation.h} | 20 +++---- ...mOperationAnimateSurfaceInterpolation.cxx} | 30 +++++----- ...tomOperationAnimateSurfaceInterpolation.h} | 20 +++---- ...omOperationAnimateVolumeSliceSequence.cxx} | 22 +++---- ...stomOperationAnimateVolumeSliceSequence.h} | 20 +++---- ...rationAnimateVolumeToSurfaceCrossFade.cxx} | 22 +++---- ...perationAnimateVolumeToSurfaceCrossFade.h} | 20 +++---- src/GuiQt/WbMacroCustomOperationManager.cxx | 32 +++++----- src/GuiQt/WbMacroCustomOperationTypeEnum.cxx | 60 ++++++++++++------- src/GuiQt/WbMacroCustomOperationTypeEnum.h | 20 +++---- 14 files changed, 193 insertions(+), 173 deletions(-) rename src/GuiQt/{WbMacroCustomOperationOverlayCrossFade.cxx => WbMacroCustomOperationAnimateOverlayCrossFade.cxx} (94%) rename src/GuiQt/{WbMacroCustomOperationOverlayCrossFade.h => WbMacroCustomOperationAnimateOverlayCrossFade.h} (79%) rename src/GuiQt/{WbMacroCustomOperationModelRotation.cxx => WbMacroCustomOperationAnimateRotation.cxx} (90%) rename src/GuiQt/{WbMacroCustomOperationModelRotation.h => WbMacroCustomOperationAnimateRotation.h} (73%) rename src/GuiQt/{WbMacroCustomOperationSurfaceInterpolation.cxx => WbMacroCustomOperationAnimateSurfaceInterpolation.cxx} (91%) rename src/GuiQt/{WbMacroCustomOperationSurfaceInterpolation.h => WbMacroCustomOperationAnimateSurfaceInterpolation.h} (73%) rename src/GuiQt/{WbMacroCustomOperationVolumeSliceIncrement.cxx => WbMacroCustomOperationAnimateVolumeSliceSequence.cxx} (92%) rename src/GuiQt/{WbMacroCustomOperationVolumeSliceIncrement.h => WbMacroCustomOperationAnimateVolumeSliceSequence.h} (70%) rename src/GuiQt/{WbMacroCustomOperationVolumeToSurfaceCrossFade.cxx => WbMacroCustomOperationAnimateVolumeToSurfaceCrossFade.cxx} (89%) rename src/GuiQt/{WbMacroCustomOperationVolumeToSurfaceCrossFade.h => WbMacroCustomOperationAnimateVolumeToSurfaceCrossFade.h} (66%) diff --git a/src/GuiQt/CMakeLists.txt b/src/GuiQt/CMakeLists.txt index e10179000..068740fd2 100644 --- a/src/GuiQt/CMakeLists.txt +++ b/src/GuiQt/CMakeLists.txt @@ -460,14 +460,14 @@ VolumeSurfaceOutlineViewController.h WbMacroCustomDataInfo.h WbMacroCustomDataTypeEnum.h WbMacroCustomOperationBase.h +WbMacroCustomOperationAnimateRotation.h WbMacroCustomOperationDelay.h WbMacroCustomOperationManager.h -WbMacroCustomOperationModelRotation.h -WbMacroCustomOperationOverlayCrossFade.h -WbMacroCustomOperationSurfaceInterpolation.h +WbMacroCustomOperationAnimateOverlayCrossFade.h +WbMacroCustomOperationAnimateSurfaceInterpolation.h WbMacroCustomOperationTypeEnum.h -WbMacroCustomOperationVolumeSliceIncrement.h -WbMacroCustomOperationVolumeToSurfaceCrossFade.h +WbMacroCustomOperationAnimateVolumeSliceSequence.h +WbMacroCustomOperationAnimateVolumeToSurfaceCrossFade.h WbMacroHelper.h WuQCollapsibleWidget.h WuQDataEntryDialog.h @@ -724,15 +724,15 @@ VolumeSurfaceOutlineSetViewController.cxx VolumeSurfaceOutlineViewController.cxx WbMacroCustomDataInfo.cxx WbMacroCustomDataTypeEnum.cxx +WbMacroCustomOperationAnimateRotation.cxx WbMacroCustomOperationBase.cxx WbMacroCustomOperationDelay.cxx WbMacroCustomOperationManager.cxx -WbMacroCustomOperationModelRotation.cxx -WbMacroCustomOperationOverlayCrossFade.cxx -WbMacroCustomOperationSurfaceInterpolation.cxx +WbMacroCustomOperationAnimateOverlayCrossFade.cxx +WbMacroCustomOperationAnimateSurfaceInterpolation.cxx WbMacroCustomOperationTypeEnum.cxx -WbMacroCustomOperationVolumeSliceIncrement.cxx -WbMacroCustomOperationVolumeToSurfaceCrossFade.cxx +WbMacroCustomOperationAnimateVolumeSliceSequence.cxx +WbMacroCustomOperationAnimateVolumeToSurfaceCrossFade.cxx WbMacroHelper.cxx WuQCollapsibleWidget.cxx WuQDataEntryDialog.cxx diff --git a/src/GuiQt/WbMacroCustomOperationOverlayCrossFade.cxx b/src/GuiQt/WbMacroCustomOperationAnimateOverlayCrossFade.cxx similarity index 94% rename from src/GuiQt/WbMacroCustomOperationOverlayCrossFade.cxx rename to src/GuiQt/WbMacroCustomOperationAnimateOverlayCrossFade.cxx index dc6407aca..3ab2c97eb 100644 --- a/src/GuiQt/WbMacroCustomOperationOverlayCrossFade.cxx +++ b/src/GuiQt/WbMacroCustomOperationAnimateOverlayCrossFade.cxx @@ -19,9 +19,9 @@ */ /*LICENSE_END*/ -#define __WB_MACRO_CUSTOM_OPERATION_OVERLAY_CROSS_FADE_DECLARE__ -#include "WbMacroCustomOperationOverlayCrossFade.h" -#undef __WB_MACRO_CUSTOM_OPERATION_OVERLAY_CROSS_FADE_DECLARE__ +#define __WB_MACRO_CUSTOM_OPERATION_ANIMATE_OVERLAY_CROSS_FADE_DECLARE__ +#include "WbMacroCustomOperationAnimateOverlayCrossFade.h" +#undef __WB_MACRO_CUSTOM_OPERATION_ANIMATE_OVERLAY_CROSS_FADE_DECLARE__ #include "BrainBrowserWindow.h" #include "BrowserTabContent.h" @@ -41,23 +41,23 @@ using namespace caret; /** - * \class caret::WbMacroCustomOperationOverlayCrossFade - * \brief Custom Macro Command for Surface Interpolation + * \class caret::WbMacroCustomOperationAnimateOverlayCrossFade + * \brief Custom Macro Command for Overlay Crossfade * \ingroup GuiQt */ /** * Constructor. */ -WbMacroCustomOperationOverlayCrossFade::WbMacroCustomOperationOverlayCrossFade() -: WbMacroCustomOperationBase(WbMacroCustomOperationTypeEnum::OVERLAY_CROSS_FADE) +WbMacroCustomOperationAnimateOverlayCrossFade::WbMacroCustomOperationAnimateOverlayCrossFade() +: WbMacroCustomOperationBase(WbMacroCustomOperationTypeEnum::ANIMATE_OVERLAY_CROSS_FADE) { } /** * Destructor. */ -WbMacroCustomOperationOverlayCrossFade::~WbMacroCustomOperationOverlayCrossFade() +WbMacroCustomOperationAnimateOverlayCrossFade::~WbMacroCustomOperationAnimateOverlayCrossFade() { } @@ -69,7 +69,7 @@ WbMacroCustomOperationOverlayCrossFade::~WbMacroCustomOperationOverlayCrossFade( * Use getErrorMessage() for error information if NULL returned */ WuQMacroCommand* -WbMacroCustomOperationOverlayCrossFade::createCommand() +WbMacroCustomOperationAnimateOverlayCrossFade::createCommand() { WuQMacroCommand* command(NULL); const int32_t versionNumber(2); @@ -94,7 +94,7 @@ WbMacroCustomOperationOverlayCrossFade::createCommand() * Use getErrorMessage() for error information if NULL returned */ WuQMacroCommand* -WbMacroCustomOperationOverlayCrossFade::createCommandVersionOne() +WbMacroCustomOperationAnimateOverlayCrossFade::createCommandVersionOne() { const int32_t versionOne(1); @@ -138,7 +138,7 @@ WbMacroCustomOperationOverlayCrossFade::createCommandVersionOne() * Use getErrorMessage() for error information if NULL returned */ WuQMacroCommand* -WbMacroCustomOperationOverlayCrossFade::createCommandVersionTwo() +WbMacroCustomOperationAnimateOverlayCrossFade::createCommandVersionTwo() { const int32_t versionTwo(2); @@ -170,7 +170,7 @@ WbMacroCustomOperationOverlayCrossFade::createCommandVersionTwo() WuQMacroCommand* command = WuQMacroCommand::newInstanceCustomCommand(WbMacroCustomOperationTypeEnum::toName(getOperationType()), versionTwo, "none", - "Overlay CrossFade", + WbMacroCustomOperationTypeEnum::toGuiName(getOperationType()), description, 1.0, errorMessage); @@ -207,7 +207,7 @@ WbMacroCustomOperationOverlayCrossFade::createCommandVersionTwo() * Use getErrorMessage() for error information if false returned */ bool -WbMacroCustomOperationOverlayCrossFade::executeCommand(QWidget* parent, +WbMacroCustomOperationAnimateOverlayCrossFade::executeCommand(QWidget* parent, const WuQMacroExecutorMonitor* executorMonitor, const WuQMacroExecutorOptions* executorOptions, const WuQMacroCommand* macroCommand) @@ -252,7 +252,7 @@ WbMacroCustomOperationOverlayCrossFade::executeCommand(QWidget* parent, * Use getErrorMessage() for error information if false returned */ bool -WbMacroCustomOperationOverlayCrossFade::executeCommandVersionTwo(QWidget* parent, +WbMacroCustomOperationAnimateOverlayCrossFade::executeCommandVersionTwo(QWidget* parent, const WuQMacroExecutorMonitor* executorMonitor, const WuQMacroExecutorOptions* executorOptions, const WuQMacroCommand* macroCommand) @@ -398,7 +398,7 @@ WbMacroCustomOperationOverlayCrossFade::executeCommandVersionTwo(QWidget* parent * True if successful, else false */ bool -WbMacroCustomOperationOverlayCrossFade::performCrossFadeVersionTwo(const WuQMacroExecutorMonitor* executorMonitor, +WbMacroCustomOperationAnimateOverlayCrossFade::performCrossFadeVersionTwo(const WuQMacroExecutorMonitor* executorMonitor, const WuQMacroExecutorOptions* executorOptions, OverlaySet* overlaySet, const int32_t overlayIndex, @@ -498,7 +498,7 @@ WbMacroCustomOperationOverlayCrossFade::performCrossFadeVersionTwo(const WuQMacr * Use getErrorMessage() for error information if false returned */ bool -WbMacroCustomOperationOverlayCrossFade::executeCommandVersionOne(QWidget* parent, +WbMacroCustomOperationAnimateOverlayCrossFade::executeCommandVersionOne(QWidget* parent, const WuQMacroExecutorOptions* executorOptions, const WuQMacroCommand* macroCommand) { @@ -620,7 +620,7 @@ WbMacroCustomOperationOverlayCrossFade::executeCommandVersionOne(QWidget* parent * True if successful, else false */ bool -WbMacroCustomOperationOverlayCrossFade::performCrossFadeVersionOne(const WuQMacroExecutorOptions* executorOptions, +WbMacroCustomOperationAnimateOverlayCrossFade::performCrossFadeVersionOne(const WuQMacroExecutorOptions* executorOptions, Overlay* fadeToOverlay, Overlay* fadeFromOverlay, const float durationSeconds) diff --git a/src/GuiQt/WbMacroCustomOperationOverlayCrossFade.h b/src/GuiQt/WbMacroCustomOperationAnimateOverlayCrossFade.h similarity index 79% rename from src/GuiQt/WbMacroCustomOperationOverlayCrossFade.h rename to src/GuiQt/WbMacroCustomOperationAnimateOverlayCrossFade.h index 9b8880bf7..03baf81e3 100644 --- a/src/GuiQt/WbMacroCustomOperationOverlayCrossFade.h +++ b/src/GuiQt/WbMacroCustomOperationAnimateOverlayCrossFade.h @@ -1,5 +1,5 @@ -#ifndef __WB_MACRO_CUSTOM_OPERATION_OVERLAY_CROSS_FADE_H__ -#define __WB_MACRO_CUSTOM_OPERATION_OVERLAY_CROSS_FADE_H__ +#ifndef __WB_MACRO_CUSTOM_OPERATION_ANIMATE_OVERLAY_CROSS_FADE_H__ +#define __WB_MACRO_CUSTOM_OPERATION_ANIMATE_OVERLAY_CROSS_FADE_H__ /*LICENSE_START*/ /* @@ -32,16 +32,16 @@ namespace caret { class Overlay; class OverlaySet; - class WbMacroCustomOperationOverlayCrossFade : public WbMacroCustomOperationBase { + class WbMacroCustomOperationAnimateOverlayCrossFade : public WbMacroCustomOperationBase { public: - WbMacroCustomOperationOverlayCrossFade(); + WbMacroCustomOperationAnimateOverlayCrossFade(); - virtual ~WbMacroCustomOperationOverlayCrossFade(); + virtual ~WbMacroCustomOperationAnimateOverlayCrossFade(); - WbMacroCustomOperationOverlayCrossFade(const WbMacroCustomOperationOverlayCrossFade&) = delete; + WbMacroCustomOperationAnimateOverlayCrossFade(const WbMacroCustomOperationAnimateOverlayCrossFade&) = delete; - WbMacroCustomOperationOverlayCrossFade& operator=(const WbMacroCustomOperationOverlayCrossFade&) = delete; + WbMacroCustomOperationAnimateOverlayCrossFade& operator=(const WbMacroCustomOperationAnimateOverlayCrossFade&) = delete; virtual bool executeCommand(QWidget* parent, const WuQMacroExecutorMonitor* executorMonitor, @@ -86,9 +86,9 @@ namespace caret { }; -#ifdef __WB_MACRO_CUSTOM_OPERATION_OVERLAY_CROSS_FADE_DECLARE__ +#ifdef __WB_MACRO_CUSTOM_OPERATION_ANIMATE_OVERLAY_CROSS_FADE_DECLARE__ // -#endif // __WB_MACRO_CUSTOM_OPERATION_OVERLAY_CROSS_FADE_DECLARE__ +#endif // __WB_MACRO_CUSTOM_OPERATION_ANIMATE_OVERLAY_CROSS_FADE_DECLARE__ } // namespace -#endif //__WB_MACRO_CUSTOM_OPERATION_OVERLAY_CROSS_FADE_H__ +#endif //__WB_MACRO_CUSTOM_OPERATION_ANIMATE_OVERLAY_CROSS_FADE_H__ diff --git a/src/GuiQt/WbMacroCustomOperationModelRotation.cxx b/src/GuiQt/WbMacroCustomOperationAnimateRotation.cxx similarity index 90% rename from src/GuiQt/WbMacroCustomOperationModelRotation.cxx rename to src/GuiQt/WbMacroCustomOperationAnimateRotation.cxx index 11092a2af..205d338fb 100644 --- a/src/GuiQt/WbMacroCustomOperationModelRotation.cxx +++ b/src/GuiQt/WbMacroCustomOperationAnimateRotation.cxx @@ -19,9 +19,9 @@ */ /*LICENSE_END*/ -#define __WB_MACRO_CUSTOM_OPERATION_MODEL_ROTATION_DECLARE__ -#include "WbMacroCustomOperationModelRotation.h" -#undef __WB_MACRO_CUSTOM_OPERATION_MODEL_ROTATION_DECLARE__ +#define __WB_MACRO_CUSTOM_OPERATION_ANIMATE_ROTATION_DECLARE__ +#include "WbMacroCustomOperationAnimateRotation.h" +#undef __WB_MACRO_CUSTOM_OPERATION_ANIMATE_ROTATION_DECLARE__ #include "BrainBrowserWindow.h" #include "BrowserTabContent.h" @@ -40,7 +40,7 @@ using namespace caret; /** - * \class caret::WbMacroCustomOperationModelRotation + * \class caret::WbMacroCustomOperationAnimateRotation * \brief Macro custom operation for model rotation * \ingroup GuiQt */ @@ -48,8 +48,8 @@ using namespace caret; /** * Constructor. */ -WbMacroCustomOperationModelRotation::WbMacroCustomOperationModelRotation() -: WbMacroCustomOperationBase(WbMacroCustomOperationTypeEnum::MODEL_ROTATION) +WbMacroCustomOperationAnimateRotation::WbMacroCustomOperationAnimateRotation() +: WbMacroCustomOperationBase(WbMacroCustomOperationTypeEnum::ANIMATE_ROTATION) { } @@ -57,7 +57,7 @@ WbMacroCustomOperationModelRotation::WbMacroCustomOperationModelRotation() /** * Destructor. */ -WbMacroCustomOperationModelRotation::~WbMacroCustomOperationModelRotation() +WbMacroCustomOperationAnimateRotation::~WbMacroCustomOperationAnimateRotation() { } @@ -69,7 +69,7 @@ WbMacroCustomOperationModelRotation::~WbMacroCustomOperationModelRotation() * Use getErrorMessage() for error information if NULL returned */ WuQMacroCommand* -WbMacroCustomOperationModelRotation::createCommand() +WbMacroCustomOperationAnimateRotation::createCommand() { const int32_t versionOne(1); @@ -78,11 +78,11 @@ WbMacroCustomOperationModelRotation::createCommand() "Y"); QString errorMessage; - WuQMacroCommand* command = WuQMacroCommand::newInstanceCustomCommand(WbMacroCustomOperationTypeEnum::toName(WbMacroCustomOperationTypeEnum::MODEL_ROTATION), + WuQMacroCommand* command = WuQMacroCommand::newInstanceCustomCommand(WbMacroCustomOperationTypeEnum::toName(WbMacroCustomOperationTypeEnum::ANIMATE_ROTATION), versionOne, "none", - "Model Rotation", - "Rotate a Model", + WbMacroCustomOperationTypeEnum::toGuiName(getOperationType()), + "Rotate the Brain Model About a Screen Axis", 1.0, errorMessage); if (command != NULL) { @@ -117,7 +117,7 @@ WbMacroCustomOperationModelRotation::createCommand() * Use getErrorMessage() for error information if false returned */ bool -WbMacroCustomOperationModelRotation::executeCommand(QWidget* parent, +WbMacroCustomOperationAnimateRotation::executeCommand(QWidget* parent, const WuQMacroExecutorMonitor* executorMonitor, const WuQMacroExecutorOptions* executorOptions, const WuQMacroCommand* macroCommand) @@ -201,7 +201,7 @@ WbMacroCustomOperationModelRotation::executeCommand(QWidget* parent, * */ bool -WbMacroCustomOperationModelRotation::performRotation(const WuQMacroExecutorMonitor* executorMonitor, +WbMacroCustomOperationAnimateRotation::performRotation(const WuQMacroExecutorMonitor* executorMonitor, const WuQMacroExecutorOptions* executorOptions, BrowserTabContent* tabContent, const Axis axis, diff --git a/src/GuiQt/WbMacroCustomOperationModelRotation.h b/src/GuiQt/WbMacroCustomOperationAnimateRotation.h similarity index 73% rename from src/GuiQt/WbMacroCustomOperationModelRotation.h rename to src/GuiQt/WbMacroCustomOperationAnimateRotation.h index 173b412c6..69e8d627a 100644 --- a/src/GuiQt/WbMacroCustomOperationModelRotation.h +++ b/src/GuiQt/WbMacroCustomOperationAnimateRotation.h @@ -1,5 +1,5 @@ -#ifndef __WB_MACRO_CUSTOM_OPERATION_MODEL_ROTATION_H__ -#define __WB_MACRO_CUSTOM_OPERATION_MODEL_ROTATION_H__ +#ifndef __WB_MACRO_CUSTOM_OPERATION_ANIMATE_ROTATION_H__ +#define __WB_MACRO_CUSTOM_OPERATION_ANIMATE_ROTATION_H__ /*LICENSE_START*/ /* @@ -33,16 +33,16 @@ namespace caret { class BrowserTabContent; - class WbMacroCustomOperationModelRotation : public WbMacroCustomOperationBase { + class WbMacroCustomOperationAnimateRotation : public WbMacroCustomOperationBase { public: - WbMacroCustomOperationModelRotation(); + WbMacroCustomOperationAnimateRotation(); - virtual ~WbMacroCustomOperationModelRotation(); + virtual ~WbMacroCustomOperationAnimateRotation(); - WbMacroCustomOperationModelRotation(const WbMacroCustomOperationModelRotation&) = delete; + WbMacroCustomOperationAnimateRotation(const WbMacroCustomOperationAnimateRotation&) = delete; - WbMacroCustomOperationModelRotation& operator=(const WbMacroCustomOperationModelRotation&) = delete; + WbMacroCustomOperationAnimateRotation& operator=(const WbMacroCustomOperationAnimateRotation&) = delete; virtual bool executeCommand(QWidget* parent, const WuQMacroExecutorMonitor* executorMonitor, @@ -72,9 +72,9 @@ namespace caret { }; -#ifdef __WB_MACRO_CUSTOM_OPERATION_MODEL_ROTATION_DECLARE__ +#ifdef __WB_MACRO_CUSTOM_OPERATION_ANIMATE_ROTATION_DECLARE__ // -#endif // __WB_MACRO_CUSTOM_OPERATION_MODEL_ROTATION_DECLARE__ +#endif // __WB_MACRO_CUSTOM_OPERATION_ANIMATE_ROTATION_DECLARE__ } // namespace -#endif //__WB_MACRO_CUSTOM_OPERATION_MODEL_ROTATION_H__ +#endif //__WB_MACRO_CUSTOM_OPERATION_ANIMATE_ROTATION_H__ diff --git a/src/GuiQt/WbMacroCustomOperationSurfaceInterpolation.cxx b/src/GuiQt/WbMacroCustomOperationAnimateSurfaceInterpolation.cxx similarity index 91% rename from src/GuiQt/WbMacroCustomOperationSurfaceInterpolation.cxx rename to src/GuiQt/WbMacroCustomOperationAnimateSurfaceInterpolation.cxx index c7ad4f268..f2d9ef320 100644 --- a/src/GuiQt/WbMacroCustomOperationSurfaceInterpolation.cxx +++ b/src/GuiQt/WbMacroCustomOperationAnimateSurfaceInterpolation.cxx @@ -19,9 +19,9 @@ */ /*LICENSE_END*/ -#define __WB_MACRO_CUSTOM_OPERATION_SURFACE_INTERPOLATION_DECLARE__ -#include "WbMacroCustomOperationSurfaceInterpolation.h" -#undef __WB_MACRO_CUSTOM_OPERATION_SURFACE_INTERPOLATION_DECLARE__ +#define __WB_MACRO_CUSTOM_OPERATION_ANIMATE_SURFACE_INTERPOLATION_DECLARE__ +#include "WbMacroCustomOperationAnimateSurfaceInterpolation.h" +#undef __WB_MACRO_CUSTOM_OPERATION_ANIMATE_SURFACE_INTERPOLATION_DECLARE__ #include "BrainBrowserWindow.h" #include "BrowserTabContent.h" @@ -47,7 +47,7 @@ using namespace caret; /** - * \class caret::WbMacroCustomOperationSurfaceInterpolation + * \class caret::WbMacroCustomOperationAnimateSurfaceInterpolation * \brief Custom Macro Command for Surface Interpolation * \ingroup GuiQt */ @@ -55,15 +55,15 @@ using namespace caret; /** * Constructor. */ -WbMacroCustomOperationSurfaceInterpolation::WbMacroCustomOperationSurfaceInterpolation() -: WbMacroCustomOperationBase(WbMacroCustomOperationTypeEnum::SURFACE_INTERPOLATION) +WbMacroCustomOperationAnimateSurfaceInterpolation::WbMacroCustomOperationAnimateSurfaceInterpolation() +: WbMacroCustomOperationBase(WbMacroCustomOperationTypeEnum::ANIMATE_SURFACE_INTERPOLATION) { } /** * Destructor. */ -WbMacroCustomOperationSurfaceInterpolation::~WbMacroCustomOperationSurfaceInterpolation() +WbMacroCustomOperationAnimateSurfaceInterpolation::~WbMacroCustomOperationAnimateSurfaceInterpolation() { } @@ -75,7 +75,7 @@ WbMacroCustomOperationSurfaceInterpolation::~WbMacroCustomOperationSurfaceInterp * Use getErrorMessage() for error information if NULL returned */ WuQMacroCommand* -WbMacroCustomOperationSurfaceInterpolation::createCommand() +WbMacroCustomOperationAnimateSurfaceInterpolation::createCommand() { const int32_t versionOne(1); @@ -90,11 +90,11 @@ WbMacroCustomOperationSurfaceInterpolation::createCommand() paramSurfaceTwo->setCustomDataType(WbMacroCustomDataTypeEnum::toName(WbMacroCustomDataTypeEnum::SURFACE)); QString errorMessage; - WuQMacroCommand* command = WuQMacroCommand::newInstanceCustomCommand(WbMacroCustomOperationTypeEnum::toName(WbMacroCustomOperationTypeEnum::SURFACE_INTERPOLATION), + WuQMacroCommand* command = WuQMacroCommand::newInstanceCustomCommand(WbMacroCustomOperationTypeEnum::toName(WbMacroCustomOperationTypeEnum::ANIMATE_SURFACE_INTERPOLATION), versionOne, "none", - "Surface Interpolation", - "Interpolate Between Two Surface", + WbMacroCustomOperationTypeEnum::toGuiName(getOperationType()), + "Interpolate Between Two Surfaces", 1.0, errorMessage); if (command != NULL) { @@ -127,7 +127,7 @@ WbMacroCustomOperationSurfaceInterpolation::createCommand() * Use getErrorMessage() for error information if false returned */ bool -WbMacroCustomOperationSurfaceInterpolation::executeCommand(QWidget* parent, +WbMacroCustomOperationAnimateSurfaceInterpolation::executeCommand(QWidget* parent, const WuQMacroExecutorMonitor* executorMonitor, const WuQMacroExecutorOptions* executorOptions, const WuQMacroCommand* macroCommand) @@ -226,7 +226,7 @@ WbMacroCustomOperationSurfaceInterpolation::executeCommand(QWidget* parent, * True if successful, else false */ bool -WbMacroCustomOperationSurfaceInterpolation::interpolateSurface(const WuQMacroExecutorMonitor* executorMonitor, +WbMacroCustomOperationAnimateSurfaceInterpolation::interpolateSurface(const WuQMacroExecutorMonitor* executorMonitor, const WuQMacroExecutorOptions* executorOptions, const int32_t tabIndex, ModelWholeBrain* wholeBrainModel, @@ -359,7 +359,7 @@ WbMacroCustomOperationSurfaceInterpolation::interpolateSurface(const WuQMacroExe * Surface that is copied to create the interpolation surface */ void -WbMacroCustomOperationSurfaceInterpolation::createInterpolationSurface(const Surface* surface) +WbMacroCustomOperationAnimateSurfaceInterpolation::createInterpolationSurface(const Surface* surface) { CaretAssert(surface); @@ -386,7 +386,7 @@ WbMacroCustomOperationSurfaceInterpolation::createInterpolationSurface(const Sur * Delete the interpolation surface */ void -WbMacroCustomOperationSurfaceInterpolation::deleteInterpolationSurface() +WbMacroCustomOperationAnimateSurfaceInterpolation::deleteInterpolationSurface() { if (m_interpolationSurface != NULL) { EventDataFileDelete deleteFileEvent(m_interpolationSurface); diff --git a/src/GuiQt/WbMacroCustomOperationSurfaceInterpolation.h b/src/GuiQt/WbMacroCustomOperationAnimateSurfaceInterpolation.h similarity index 73% rename from src/GuiQt/WbMacroCustomOperationSurfaceInterpolation.h rename to src/GuiQt/WbMacroCustomOperationAnimateSurfaceInterpolation.h index 2c59d9cbd..51d4495e0 100644 --- a/src/GuiQt/WbMacroCustomOperationSurfaceInterpolation.h +++ b/src/GuiQt/WbMacroCustomOperationAnimateSurfaceInterpolation.h @@ -1,5 +1,5 @@ -#ifndef __WB_MACRO_CUSTOM_OPERATION_SURFACE_INTERPOLATION_H__ -#define __WB_MACRO_CUSTOM_OPERATION_SURFACE_INTERPOLATION_H__ +#ifndef __WB_MACRO_CUSTOM_OPERATION_ANIMATE_SURFACE_INTERPOLATION_H__ +#define __WB_MACRO_CUSTOM_OPERATION_ANIMATE_SURFACE_INTERPOLATION_H__ /*LICENSE_START*/ /* @@ -34,16 +34,16 @@ namespace caret { class SpecFile; class Surface; - class WbMacroCustomOperationSurfaceInterpolation : public WbMacroCustomOperationBase { + class WbMacroCustomOperationAnimateSurfaceInterpolation : public WbMacroCustomOperationBase { public: - WbMacroCustomOperationSurfaceInterpolation(); + WbMacroCustomOperationAnimateSurfaceInterpolation(); - virtual ~WbMacroCustomOperationSurfaceInterpolation(); + virtual ~WbMacroCustomOperationAnimateSurfaceInterpolation(); - WbMacroCustomOperationSurfaceInterpolation(const WbMacroCustomOperationSurfaceInterpolation&) = delete; + WbMacroCustomOperationAnimateSurfaceInterpolation(const WbMacroCustomOperationAnimateSurfaceInterpolation&) = delete; - WbMacroCustomOperationSurfaceInterpolation& operator=(const WbMacroCustomOperationSurfaceInterpolation&) = delete; + WbMacroCustomOperationAnimateSurfaceInterpolation& operator=(const WbMacroCustomOperationAnimateSurfaceInterpolation&) = delete; virtual bool executeCommand(QWidget* parent, const WuQMacroExecutorMonitor* executorMonitor, @@ -79,9 +79,9 @@ namespace caret { }; -#ifdef __WB_MACRO_CUSTOM_OPERATION_SURFACE_INTERPOLATION_DECLARE__ +#ifdef __WB_MACRO_CUSTOM_OPERATION_ANIMATE_SURFACE_INTERPOLATION_DECLARE__ // -#endif // __WB_MACRO_CUSTOM_OPERATION_SURFACE_INTERPOLATION_DECLARE__ +#endif // __WB_MACRO_CUSTOM_OPERATION_ANIMATE_SURFACE_INTERPOLATION_DECLARE__ } // namespace -#endif //__WB_MACRO_CUSTOM_OPERATION_SURFACE_INTERPOLATION_H__ +#endif //__WB_MACRO_CUSTOM_OPERATION_ANIMATE_SURFACE_INTERPOLATION_H__ diff --git a/src/GuiQt/WbMacroCustomOperationVolumeSliceIncrement.cxx b/src/GuiQt/WbMacroCustomOperationAnimateVolumeSliceSequence.cxx similarity index 92% rename from src/GuiQt/WbMacroCustomOperationVolumeSliceIncrement.cxx rename to src/GuiQt/WbMacroCustomOperationAnimateVolumeSliceSequence.cxx index 3385f7d22..1edf91d78 100644 --- a/src/GuiQt/WbMacroCustomOperationVolumeSliceIncrement.cxx +++ b/src/GuiQt/WbMacroCustomOperationAnimateVolumeSliceSequence.cxx @@ -19,9 +19,9 @@ */ /*LICENSE_END*/ -#define __WB_MACRO_CUSTOM_OPERATION_VOLUME_SLICE_INCREMENT_DECLARE__ -#include "WbMacroCustomOperationVolumeSliceIncrement.h" -#undef __WB_MACRO_CUSTOM_OPERATION_VOLUME_SLICE_INCREMENT_DECLARE__ +#define __WB_MACRO_CUSTOM_OPERATION_ANIMATE_VOLUME_SLICE_DECLARE__ +#include "WbMacroCustomOperationAnimateVolumeSliceSequence.h" +#undef __WB_MACRO_CUSTOM_OPERATION_ANIMATE_VOLUME_SLICE_DECLARE__ #include "BrainBrowserWindow.h" #include "BrowserTabContent.h" @@ -45,7 +45,7 @@ using namespace caret; /** - * \class caret::WbMacroCustomOperationVolumeSliceIncrement + * \class caret::WbMacroCustomOperationAnimateVolumeSliceSequence * \brief Macro custom operation incrementing volume slices * \ingroup GuiQt */ @@ -53,8 +53,8 @@ using namespace caret; /** * Constructor. */ -WbMacroCustomOperationVolumeSliceIncrement::WbMacroCustomOperationVolumeSliceIncrement() -: WbMacroCustomOperationBase(WbMacroCustomOperationTypeEnum::VOLUME_SLICE_INCREMENT) +WbMacroCustomOperationAnimateVolumeSliceSequence::WbMacroCustomOperationAnimateVolumeSliceSequence() +: WbMacroCustomOperationBase(WbMacroCustomOperationTypeEnum::ANIMATE_VOLUME_SLICE_SEQUENCE) { } @@ -62,7 +62,7 @@ WbMacroCustomOperationVolumeSliceIncrement::WbMacroCustomOperationVolumeSliceInc /** * Destructor. */ -WbMacroCustomOperationVolumeSliceIncrement::~WbMacroCustomOperationVolumeSliceIncrement() +WbMacroCustomOperationAnimateVolumeSliceSequence::~WbMacroCustomOperationAnimateVolumeSliceSequence() { } @@ -74,7 +74,7 @@ WbMacroCustomOperationVolumeSliceIncrement::~WbMacroCustomOperationVolumeSliceIn * Use getErrorMessage() for error information if NULL returned */ WuQMacroCommand* -WbMacroCustomOperationVolumeSliceIncrement::createCommand() +WbMacroCustomOperationAnimateVolumeSliceSequence::createCommand() { const int32_t versionOne(1); @@ -88,7 +88,7 @@ WbMacroCustomOperationVolumeSliceIncrement::createCommand() WuQMacroCommand* command = WuQMacroCommand::newInstanceCustomCommand(WbMacroCustomOperationTypeEnum::toName(getOperationType()), versionOne, "none", - "Volume Slice Increment", + WbMacroCustomOperationTypeEnum::toGuiName(getOperationType()), description, 1.0, errorMessage); @@ -124,7 +124,7 @@ WbMacroCustomOperationVolumeSliceIncrement::createCommand() * Use getErrorMessage() for error information if false returned */ bool -WbMacroCustomOperationVolumeSliceIncrement::executeCommand(QWidget* parent, +WbMacroCustomOperationAnimateVolumeSliceSequence::executeCommand(QWidget* parent, const WuQMacroExecutorMonitor* executorMonitor, const WuQMacroExecutorOptions* executorOptions, const WuQMacroCommand* macroCommand) @@ -201,7 +201,7 @@ WbMacroCustomOperationVolumeSliceIncrement::executeCommand(QWidget* parent, * Duration of time for command to run */ bool -WbMacroCustomOperationVolumeSliceIncrement::performSliceIncrement(const WuQMacroExecutorMonitor* executorMonitor, +WbMacroCustomOperationAnimateVolumeSliceSequence::performSliceIncrement(const WuQMacroExecutorMonitor* executorMonitor, const WuQMacroExecutorOptions* executorOptions, BrowserTabContent* tabContent, const Axis axis, diff --git a/src/GuiQt/WbMacroCustomOperationVolumeSliceIncrement.h b/src/GuiQt/WbMacroCustomOperationAnimateVolumeSliceSequence.h similarity index 70% rename from src/GuiQt/WbMacroCustomOperationVolumeSliceIncrement.h rename to src/GuiQt/WbMacroCustomOperationAnimateVolumeSliceSequence.h index 75aa0f082..a57c5afe4 100644 --- a/src/GuiQt/WbMacroCustomOperationVolumeSliceIncrement.h +++ b/src/GuiQt/WbMacroCustomOperationAnimateVolumeSliceSequence.h @@ -1,5 +1,5 @@ -#ifndef __WB_MACRO_CUSTOM_OPERATION_VOLUME_SLICE_INCREMENT_H__ -#define __WB_MACRO_CUSTOM_OPERATION_VOLUME_SLICE_INCREMENT_H__ +#ifndef __WB_MACRO_CUSTOM_OPERATION_ANIMATE_VOLUME_SLICE_SEQUENCE_H__ +#define __WB_MACRO_CUSTOM_OPERATION_ANIMATE_VOLUME_SLICE_SEQUENCE_H__ /*LICENSE_START*/ /* @@ -33,16 +33,16 @@ namespace caret { class BrowserTabContent; - class WbMacroCustomOperationVolumeSliceIncrement : public WbMacroCustomOperationBase { + class WbMacroCustomOperationAnimateVolumeSliceSequence : public WbMacroCustomOperationBase { public: - WbMacroCustomOperationVolumeSliceIncrement(); + WbMacroCustomOperationAnimateVolumeSliceSequence(); - virtual ~WbMacroCustomOperationVolumeSliceIncrement(); + virtual ~WbMacroCustomOperationAnimateVolumeSliceSequence(); - WbMacroCustomOperationVolumeSliceIncrement(const WbMacroCustomOperationVolumeSliceIncrement&) = delete; + WbMacroCustomOperationAnimateVolumeSliceSequence(const WbMacroCustomOperationAnimateVolumeSliceSequence&) = delete; - WbMacroCustomOperationVolumeSliceIncrement& operator=(const WbMacroCustomOperationVolumeSliceIncrement&) = delete; + WbMacroCustomOperationAnimateVolumeSliceSequence& operator=(const WbMacroCustomOperationAnimateVolumeSliceSequence&) = delete; virtual bool executeCommand(QWidget* parent, const WuQMacroExecutorMonitor* executorMonitor, @@ -71,9 +71,9 @@ namespace caret { }; -#ifdef __WB_MACRO_CUSTOM_OPERATION_VOLUME_SLICE_INCREMENT_DECLARE__ +#ifdef __WB_MACRO_CUSTOM_OPERATION_ANIMATE_VOLUME_SLICE_DECLARE__ // -#endif // __WB_MACRO_CUSTOM_OPERATION_VOLUME_SLICE_INCREMENT_DECLARE__ +#endif // __WB_MACRO_CUSTOM_OPERATION_ANIMATE_VOLUME_SLICE_DECLARE__ } // namespace -#endif //__WB_MACRO_CUSTOM_OPERATION_VOLUME_SLICE_INCREMENT_H__ +#endif //__WB_MACRO_CUSTOM_OPERATION_ANIMATE_VOLUME_SLICE_SEQUENCE_H__ diff --git a/src/GuiQt/WbMacroCustomOperationVolumeToSurfaceCrossFade.cxx b/src/GuiQt/WbMacroCustomOperationAnimateVolumeToSurfaceCrossFade.cxx similarity index 89% rename from src/GuiQt/WbMacroCustomOperationVolumeToSurfaceCrossFade.cxx rename to src/GuiQt/WbMacroCustomOperationAnimateVolumeToSurfaceCrossFade.cxx index f08090344..43d2313f4 100644 --- a/src/GuiQt/WbMacroCustomOperationVolumeToSurfaceCrossFade.cxx +++ b/src/GuiQt/WbMacroCustomOperationAnimateVolumeToSurfaceCrossFade.cxx @@ -19,9 +19,9 @@ */ /*LICENSE_END*/ -#define __WB_MACRO_CUSTOM_OPERATION_VOLUME_TO_SURFACE_CROSS_FADE_DECLARE__ -#include "WbMacroCustomOperationVolumeToSurfaceCrossFade.h" -#undef __WB_MACRO_CUSTOM_OPERATION_VOLUME_TO_SURFACE_CROSS_FADE_DECLARE__ +#define __WB_MACRO_CUSTOM_OPERATION_ANIMATE_VOLUME_TO_SURFACE_CROSS_FADE_DECLARE__ +#include "WbMacroCustomOperationAnimateVolumeToSurfaceCrossFade.h" +#undef __WB_MACRO_CUSTOM_OPERATION_ANIMATE_VOLUME_TO_SURFACE_CROSS_FADE_DECLARE__ #include "Brain.h" #include "BrainBrowserWindow.h" @@ -44,7 +44,7 @@ using namespace caret; /** - * \class caret::WbMacroCustomOperationVolumeToSurfaceCrossFade + * \class caret::WbMacroCustomOperationAnimateVolumeToSurfaceCrossFade * \brief Custom Macro Command for Surface Interpolation * \ingroup GuiQt */ @@ -52,15 +52,15 @@ using namespace caret; /** * Constructor. */ -WbMacroCustomOperationVolumeToSurfaceCrossFade::WbMacroCustomOperationVolumeToSurfaceCrossFade() -: WbMacroCustomOperationBase(WbMacroCustomOperationTypeEnum::VOLUME_TO_SURFACE_CROSS_FADE) +WbMacroCustomOperationAnimateVolumeToSurfaceCrossFade::WbMacroCustomOperationAnimateVolumeToSurfaceCrossFade() +: WbMacroCustomOperationBase(WbMacroCustomOperationTypeEnum::ANIMATE_VOLUME_TO_SURFACE_CROSS_FADE) { } /** * Destructor. */ -WbMacroCustomOperationVolumeToSurfaceCrossFade::~WbMacroCustomOperationVolumeToSurfaceCrossFade() +WbMacroCustomOperationAnimateVolumeToSurfaceCrossFade::~WbMacroCustomOperationAnimateVolumeToSurfaceCrossFade() { } @@ -72,7 +72,7 @@ WbMacroCustomOperationVolumeToSurfaceCrossFade::~WbMacroCustomOperationVolumeToS * Use getErrorMessage() for error information if NULL returned */ WuQMacroCommand* -WbMacroCustomOperationVolumeToSurfaceCrossFade::createCommand() +WbMacroCustomOperationAnimateVolumeToSurfaceCrossFade::createCommand() { const int32_t versionOne(1); @@ -87,7 +87,7 @@ WbMacroCustomOperationVolumeToSurfaceCrossFade::createCommand() WuQMacroCommand* command = WuQMacroCommand::newInstanceCustomCommand(WbMacroCustomOperationTypeEnum::toName(getOperationType()), versionOne, "none", - "Volume to Surface CrossFade", + WbMacroCustomOperationTypeEnum::toGuiName(getOperationType()), description, 1.0, errorMessage); @@ -122,7 +122,7 @@ WbMacroCustomOperationVolumeToSurfaceCrossFade::createCommand() * Use getErrorMessage() for error information if false returned */ bool -WbMacroCustomOperationVolumeToSurfaceCrossFade::executeCommand(QWidget* parent, +WbMacroCustomOperationAnimateVolumeToSurfaceCrossFade::executeCommand(QWidget* parent, const WuQMacroExecutorMonitor* executorMonitor, const WuQMacroExecutorOptions* executorOptions, const WuQMacroCommand* macroCommand) @@ -189,7 +189,7 @@ WbMacroCustomOperationVolumeToSurfaceCrossFade::executeCommand(QWidget* parent, * True if successful, else false */ bool -WbMacroCustomOperationVolumeToSurfaceCrossFade::performCrossFade(const WuQMacroExecutorMonitor* executorMonitor, +WbMacroCustomOperationAnimateVolumeToSurfaceCrossFade::performCrossFade(const WuQMacroExecutorMonitor* executorMonitor, const WuQMacroExecutorOptions* executorOptions, Overlay* volumeOverlay, const float durationSeconds) diff --git a/src/GuiQt/WbMacroCustomOperationVolumeToSurfaceCrossFade.h b/src/GuiQt/WbMacroCustomOperationAnimateVolumeToSurfaceCrossFade.h similarity index 66% rename from src/GuiQt/WbMacroCustomOperationVolumeToSurfaceCrossFade.h rename to src/GuiQt/WbMacroCustomOperationAnimateVolumeToSurfaceCrossFade.h index be19c991a..3790d6b37 100644 --- a/src/GuiQt/WbMacroCustomOperationVolumeToSurfaceCrossFade.h +++ b/src/GuiQt/WbMacroCustomOperationAnimateVolumeToSurfaceCrossFade.h @@ -1,5 +1,5 @@ -#ifndef __WB_MACRO_CUSTOM_OPERATION_VOLUME_TO_SURFACE_CROSS_FADE_H__ -#define __WB_MACRO_CUSTOM_OPERATION_VOLUME_TO_SURFACE_CROSS_FADE_H__ +#ifndef __WB_MACRO_CUSTOM_OPERATION_ANIMATE_VOLUME_TO_SURFACE_CROSS_FADE_H__ +#define __WB_MACRO_CUSTOM_OPERATION_ANIMATE_VOLUME_TO_SURFACE_CROSS_FADE_H__ /*LICENSE_START*/ /* @@ -31,16 +31,16 @@ namespace caret { class Overlay; - class WbMacroCustomOperationVolumeToSurfaceCrossFade : public WbMacroCustomOperationBase { + class WbMacroCustomOperationAnimateVolumeToSurfaceCrossFade : public WbMacroCustomOperationBase { public: - WbMacroCustomOperationVolumeToSurfaceCrossFade(); + WbMacroCustomOperationAnimateVolumeToSurfaceCrossFade(); - virtual ~WbMacroCustomOperationVolumeToSurfaceCrossFade(); + virtual ~WbMacroCustomOperationAnimateVolumeToSurfaceCrossFade(); - WbMacroCustomOperationVolumeToSurfaceCrossFade(const WbMacroCustomOperationVolumeToSurfaceCrossFade&) = delete; + WbMacroCustomOperationAnimateVolumeToSurfaceCrossFade(const WbMacroCustomOperationAnimateVolumeToSurfaceCrossFade&) = delete; - WbMacroCustomOperationVolumeToSurfaceCrossFade& operator=(const WbMacroCustomOperationVolumeToSurfaceCrossFade&) = delete; + WbMacroCustomOperationAnimateVolumeToSurfaceCrossFade& operator=(const WbMacroCustomOperationAnimateVolumeToSurfaceCrossFade&) = delete; virtual bool executeCommand(QWidget* parent, const WuQMacroExecutorMonitor* executorMonitor, @@ -64,9 +64,9 @@ namespace caret { }; -#ifdef __WB_MACRO_CUSTOM_OPERATION_VOLUME_TO_SURFACE_CROSS_FADE_DECLARE__ +#ifdef __WB_MACRO_CUSTOM_OPERATION_ANIMATE_VOLUME_TO_SURFACE_CROSS_FADE_DECLARE__ // -#endif // __WB_MACRO_CUSTOM_OPERATION_OVERLAY_CROSS_FADE_DECLARE__ +#endif // __WB_MACRO_CUSTOM_OPERATION_ANIMATE_VOLUME_TO_SURFACE_CROSS_FADE_DECLARE__ } // namespace -#endif //__WB_MACRO_CUSTOM_OPERATION_VOLUME_TO_SURFACE_CROSS_FADE_H__ +#endif //__WB_MACRO_CUSTOM_OPERATION_ANIMATE_VOLUME_TO_SURFACE_CROSS_FADE_H__ diff --git a/src/GuiQt/WbMacroCustomOperationManager.cxx b/src/GuiQt/WbMacroCustomOperationManager.cxx index d7fd70c82..c40444e92 100644 --- a/src/GuiQt/WbMacroCustomOperationManager.cxx +++ b/src/GuiQt/WbMacroCustomOperationManager.cxx @@ -39,12 +39,12 @@ #include "Surface.h" #include "WbMacroCustomDataTypeEnum.h" #include "WbMacroCustomOperationDelay.h" -#include "WbMacroCustomOperationModelRotation.h" -#include "WbMacroCustomOperationOverlayCrossFade.h" -#include "WbMacroCustomOperationSurfaceInterpolation.h" +#include "WbMacroCustomOperationAnimateRotation.h" +#include "WbMacroCustomOperationAnimateOverlayCrossFade.h" +#include "WbMacroCustomOperationAnimateSurfaceInterpolation.h" #include "WbMacroCustomOperationTypeEnum.h" -#include "WbMacroCustomOperationVolumeSliceIncrement.h" -#include "WbMacroCustomOperationVolumeToSurfaceCrossFade.h" +#include "WbMacroCustomOperationAnimateVolumeSliceSequence.h" +#include "WbMacroCustomOperationAnimateVolumeToSurfaceCrossFade.h" #include "WbMacroCustomDataInfo.h" #include "WuQMacroCommand.h" #include "WuQMacroCommandParameter.h" @@ -644,7 +644,7 @@ WbMacroCustomOperationManager::getAllCustomOperationMacroCommands() std::vector customCommands; for (auto cct : customCommandTypes) { - if (cct == WbMacroCustomOperationTypeEnum::VOLUME_TO_SURFACE_CROSS_FADE) { + if (cct == WbMacroCustomOperationTypeEnum::ANIMATE_VOLUME_TO_SURFACE_CROSS_FADE) { continue; } std::unique_ptr customOperation(createCommand(cct)); @@ -716,20 +716,20 @@ WbMacroCustomOperationManager::createCommand(const WbMacroCustomOperationTypeEnu case WbMacroCustomOperationTypeEnum::DELAY: operationOut = new WbMacroCustomOperationDelay(); break; - case WbMacroCustomOperationTypeEnum::MODEL_ROTATION: - operationOut = new WbMacroCustomOperationModelRotation(); + case WbMacroCustomOperationTypeEnum::ANIMATE_ROTATION: + operationOut = new WbMacroCustomOperationAnimateRotation(); break; - case WbMacroCustomOperationTypeEnum::OVERLAY_CROSS_FADE: - operationOut = new WbMacroCustomOperationOverlayCrossFade(); + case WbMacroCustomOperationTypeEnum::ANIMATE_OVERLAY_CROSS_FADE: + operationOut = new WbMacroCustomOperationAnimateOverlayCrossFade(); break; - case WbMacroCustomOperationTypeEnum::SURFACE_INTERPOLATION: - operationOut = new WbMacroCustomOperationSurfaceInterpolation(); + case WbMacroCustomOperationTypeEnum::ANIMATE_SURFACE_INTERPOLATION: + operationOut = new WbMacroCustomOperationAnimateSurfaceInterpolation(); break; - case WbMacroCustomOperationTypeEnum::VOLUME_SLICE_INCREMENT: - operationOut = new WbMacroCustomOperationVolumeSliceIncrement(); + case WbMacroCustomOperationTypeEnum::ANIMATE_VOLUME_SLICE_SEQUENCE: + operationOut = new WbMacroCustomOperationAnimateVolumeSliceSequence(); break; - case WbMacroCustomOperationTypeEnum::VOLUME_TO_SURFACE_CROSS_FADE: - operationOut = new WbMacroCustomOperationVolumeToSurfaceCrossFade(); + case WbMacroCustomOperationTypeEnum::ANIMATE_VOLUME_TO_SURFACE_CROSS_FADE: + operationOut = new WbMacroCustomOperationAnimateVolumeToSurfaceCrossFade(); break; } diff --git a/src/GuiQt/WbMacroCustomOperationTypeEnum.cxx b/src/GuiQt/WbMacroCustomOperationTypeEnum.cxx index a9ca0993c..f7e0a2b49 100644 --- a/src/GuiQt/WbMacroCustomOperationTypeEnum.cxx +++ b/src/GuiQt/WbMacroCustomOperationTypeEnum.cxx @@ -106,29 +106,29 @@ WbMacroCustomOperationTypeEnum::initialize() } initializedFlag = true; - enumData.push_back(WbMacroCustomOperationTypeEnum(DELAY, - "DELAY", - "Delay")); - - enumData.push_back(WbMacroCustomOperationTypeEnum(MODEL_ROTATION, - "MODEL_ROTATION", - "Model Rotation")); + enumData.push_back(WbMacroCustomOperationTypeEnum(ANIMATE_ROTATION, + "ANIMATE_ROTATION", + "Animate Rotation")); - enumData.push_back(WbMacroCustomOperationTypeEnum(OVERLAY_CROSS_FADE, - "OVERLAY_CROSS_FADE", - "Overlay CrossFade")); + enumData.push_back(WbMacroCustomOperationTypeEnum(ANIMATE_OVERLAY_CROSS_FADE, + "ANIMATE_OVERLAY_CROSS_FADE", + "Animate Overlay CrossFade")); - enumData.push_back(WbMacroCustomOperationTypeEnum(SURFACE_INTERPOLATION, - "SURFACE_INTERPOLATION", - "Surface Interpolation")); + enumData.push_back(WbMacroCustomOperationTypeEnum(ANIMATE_SURFACE_INTERPOLATION, + "ANIMATE_SURFACE_INTERPOLATION", + "Animate Surface Interpolation")); - enumData.push_back(WbMacroCustomOperationTypeEnum(VOLUME_SLICE_INCREMENT, - "VOLUME_SLICE_INCREMENT", - "Volume Slice Increment")); + enumData.push_back(WbMacroCustomOperationTypeEnum(ANIMATE_VOLUME_SLICE_SEQUENCE, + "ANIMATE_VOLUME_SLICE_SEQUENCE", + "Animate Volume Slice Sequence")); - enumData.push_back(WbMacroCustomOperationTypeEnum(VOLUME_TO_SURFACE_CROSS_FADE, - "VOLUME_TO_SURFACE_CROSS_FADE", - "Volume to Surface Cross Fade")); + enumData.push_back(WbMacroCustomOperationTypeEnum(ANIMATE_VOLUME_TO_SURFACE_CROSS_FADE, + "ANIMATE_VOLUME_TO_SURFACE_CROSS_FADE", + "Animate Volume to Surface Cross Fade")); + + enumData.push_back(WbMacroCustomOperationTypeEnum(DELAY, + "DELAY", + "Delay")); } /** @@ -180,9 +180,29 @@ WbMacroCustomOperationTypeEnum::toName(Enum enumValue) { * Enumerated value. */ WbMacroCustomOperationTypeEnum::Enum -WbMacroCustomOperationTypeEnum::fromName(const AString& name, bool* isValidOut) +WbMacroCustomOperationTypeEnum::fromName(const AString& nameIn, bool* isValidOut) { if (initializedFlag == false) initialize(); + AString name(nameIn); + + /* + * Convert old names for operations that were renamed + */ + if (nameIn == "MODEL_ROTATION") { + name = WbMacroCustomOperationTypeEnum::toName(WbMacroCustomOperationTypeEnum::ANIMATE_ROTATION); + } + else if (nameIn == "OVERLAY_CROSS_FADE") { + name = WbMacroCustomOperationTypeEnum::toName(WbMacroCustomOperationTypeEnum::ANIMATE_OVERLAY_CROSS_FADE); + } + else if (nameIn == "SURFACE_INTERPOLATION") { + name = WbMacroCustomOperationTypeEnum::toName(WbMacroCustomOperationTypeEnum::ANIMATE_SURFACE_INTERPOLATION); + } + else if (nameIn == "VOLUME_SLICE_INCREMENT") { + name = WbMacroCustomOperationTypeEnum::toName(WbMacroCustomOperationTypeEnum::ANIMATE_VOLUME_SLICE_SEQUENCE); + } + else if (nameIn == "VOLUME_TO_SURFACE_CROSS_FADE") { + name = WbMacroCustomOperationTypeEnum::toName(WbMacroCustomOperationTypeEnum::ANIMATE_VOLUME_TO_SURFACE_CROSS_FADE); + } bool validFlag = false; Enum enumValue = WbMacroCustomOperationTypeEnum::enumData[0].enumValue; diff --git a/src/GuiQt/WbMacroCustomOperationTypeEnum.h b/src/GuiQt/WbMacroCustomOperationTypeEnum.h index b7e5c64ca..9f4687ff9 100644 --- a/src/GuiQt/WbMacroCustomOperationTypeEnum.h +++ b/src/GuiQt/WbMacroCustomOperationTypeEnum.h @@ -37,16 +37,16 @@ public: enum Enum { /** Delay **/ DELAY, - /** Rotation of viewed model */ - MODEL_ROTATION, - /** CrossFade of overlays */ - OVERLAY_CROSS_FADE, - /** Surface Interpolation */ - SURFACE_INTERPOLATION, - /** Volume slice increment */ - VOLUME_SLICE_INCREMENT, - /** Volume to surface corss fade */ - VOLUME_TO_SURFACE_CROSS_FADE + /** Animate rotation of viewed model */ + ANIMATE_ROTATION, + /** Animate CrossFade of overlays */ + ANIMATE_OVERLAY_CROSS_FADE, + /** Animate Surface Interpolation */ + ANIMATE_SURFACE_INTERPOLATION, + /** Animate Volume slice sequence */ + ANIMATE_VOLUME_SLICE_SEQUENCE, + /** Animate Volume to surface cross fade */ + ANIMATE_VOLUME_TO_SURFACE_CROSS_FADE }; -- GitLab From a03670c2500c4f35f1ab3026a234d819f96dcbbc Mon Sep 17 00:00:00 2001 From: John Harwell Date: Thu, 4 Apr 2019 15:21:29 -0500 Subject: [PATCH 273/427] Macro Add New Command Dialog: Description of new command was not updating when the command selection was changed using an arrow key. --- .../WuQMacroNewCommandSelectionDialog.cxx | 44 ++++++++++++------- src/GuiQt/WuQMacroNewCommandSelectionDialog.h | 4 +- 2 files changed, 31 insertions(+), 17 deletions(-) diff --git a/src/GuiQt/WuQMacroNewCommandSelectionDialog.cxx b/src/GuiQt/WuQMacroNewCommandSelectionDialog.cxx index 107cf9f79..93a916f17 100644 --- a/src/GuiQt/WuQMacroNewCommandSelectionDialog.cxx +++ b/src/GuiQt/WuQMacroNewCommandSelectionDialog.cxx @@ -86,12 +86,12 @@ WuQMacroNewCommandSelectionDialog::WuQMacroNewCommandSelectionDialog(QWidget* pa typeLayout->addWidget(m_commandTypeComboBox->getWidget(), 100); m_customCommandListWidget = new QListWidget(); - QObject::connect(m_customCommandListWidget, &QListWidget::itemClicked, - this, &WuQMacroNewCommandSelectionDialog::customCommandListWidgetItemClicked); + QObject::connect(m_customCommandListWidget, &QListWidget::currentItemChanged, + this, &WuQMacroNewCommandSelectionDialog::customCommandListWidgetCurrentItemChanged); m_widgetCommandListWidget = new QListWidget(); - QObject::connect(m_widgetCommandListWidget, &QListWidget::itemClicked, - this, &WuQMacroNewCommandSelectionDialog::widgetCommandListWidgetItemClicked); + QObject::connect(m_widgetCommandListWidget, &QListWidget::currentItemChanged, + this, &WuQMacroNewCommandSelectionDialog::widgetCommandListWidgetCurrentItemChanged); m_stackedWidget = new QStackedWidget(); m_stackedWidget->addWidget(m_customCommandListWidget); @@ -133,6 +133,9 @@ WuQMacroNewCommandSelectionDialog::WuQMacroNewCommandSelectionDialog(QWidget* pa dialogLayout->addWidget(m_splitter); dialogLayout->addWidget(m_dialogButtonBox); + loadCustomCommandListWidget(); + loadWidgetCommandListWidget(); + commandTypeComboBoxActivated(); if ( ! s_previousDialogGeometry.isEmpty()) { @@ -169,26 +172,37 @@ WuQMacroNewCommandSelectionDialog::commandTypeComboBoxActivated() switch (s_lastCommandTypeSelected) { case WuQMacroCommandTypeEnum::CUSTOM_OPERATION: + { m_stackedWidget->setCurrentWidget(m_customCommandListWidget); - if (m_customCommandListWidget->count() <= 0) { - loadCustomCommandListWidget(); + QListWidgetItem* item = m_customCommandListWidget->currentItem(); + if (item == NULL) { if (m_customCommandListWidget->count() > 0) { - QListWidgetItem* firstItem = m_customCommandListWidget->item(0); - if (firstItem != NULL) { - m_customCommandListWidget->setCurrentItem(firstItem); - customCommandListWidgetItemClicked(firstItem); - } + item = m_customCommandListWidget->item(0); } } + if (item != NULL) { + m_customCommandListWidget->setCurrentItem(item); + customCommandListWidgetCurrentItemChanged(item); + } + } break; case WuQMacroCommandTypeEnum::MOUSE: CaretAssert(0); break; case WuQMacroCommandTypeEnum::WIDGET: + { m_stackedWidget->setCurrentWidget(m_widgetCommandListWidget); - if (m_widgetCommandListWidget->count() <= 0) { - loadWidgetCommandListWidget(); + QListWidgetItem* item = m_widgetCommandListWidget->currentItem(); + if (item == NULL) { + if (m_widgetCommandListWidget->count() > 0) { + item = m_widgetCommandListWidget->item(0); + } } + if (item != NULL) { + m_widgetCommandListWidget->setCurrentItem(item); + widgetCommandListWidgetCurrentItemChanged(item); + } + } break; } @@ -233,7 +247,7 @@ WuQMacroNewCommandSelectionDialog::loadWidgetCommandListWidget() * Item that was clicked */ void -WuQMacroNewCommandSelectionDialog::customCommandListWidgetItemClicked(QListWidgetItem* item) +WuQMacroNewCommandSelectionDialog::customCommandListWidgetCurrentItemChanged(QListWidgetItem* item) { QString description; if (item != NULL) { @@ -256,7 +270,7 @@ WuQMacroNewCommandSelectionDialog::customCommandListWidgetItemClicked(QListWidge * Item that was clicked */ void -WuQMacroNewCommandSelectionDialog::widgetCommandListWidgetItemClicked(QListWidgetItem* /*item*/) +WuQMacroNewCommandSelectionDialog::widgetCommandListWidgetCurrentItemChanged(QListWidgetItem* /*item*/) { QString description; const int index = m_widgetCommandListWidget->currentRow(); diff --git a/src/GuiQt/WuQMacroNewCommandSelectionDialog.h b/src/GuiQt/WuQMacroNewCommandSelectionDialog.h index e92b96ecd..dec696b60 100644 --- a/src/GuiQt/WuQMacroNewCommandSelectionDialog.h +++ b/src/GuiQt/WuQMacroNewCommandSelectionDialog.h @@ -69,9 +69,9 @@ namespace caret { public slots: void commandTypeComboBoxActivated(); - void customCommandListWidgetItemClicked(QListWidgetItem* item); + void customCommandListWidgetCurrentItemChanged(QListWidgetItem* item); - void widgetCommandListWidgetItemClicked(QListWidgetItem* item); + void widgetCommandListWidgetCurrentItemChanged(QListWidgetItem* item); virtual void done(int r) override; -- GitLab From cb057e8dc4723b5f1eb0a3ff7799bb1ff9cb75ab Mon Sep 17 00:00:00 2001 From: John Harwell Date: Fri, 5 Apr 2019 09:44:06 -0500 Subject: [PATCH 274/427] Macros: Added an incremental rotation custom command to macros. --- src/Brain/BrowserTabContent.cxx | 249 +++++++++--------- src/Brain/BrowserTabContent.h | 8 +- src/GuiQt/CMakeLists.txt | 20 +- ...bMacroCustomOperationIncrementRotation.cxx | 232 ++++++++++++++++ .../WbMacroCustomOperationIncrementRotation.h | 74 ++++++ src/GuiQt/WbMacroCustomOperationManager.cxx | 4 + src/GuiQt/WbMacroCustomOperationTypeEnum.cxx | 6 +- src/GuiQt/WbMacroCustomOperationTypeEnum.h | 8 +- 8 files changed, 464 insertions(+), 137 deletions(-) create mode 100644 src/GuiQt/WbMacroCustomOperationIncrementRotation.cxx create mode 100644 src/GuiQt/WbMacroCustomOperationIncrementRotation.h diff --git a/src/Brain/BrowserTabContent.cxx b/src/Brain/BrowserTabContent.cxx index aa7a56953..7e696586a 100644 --- a/src/Brain/BrowserTabContent.cxx +++ b/src/Brain/BrowserTabContent.cxx @@ -2476,141 +2476,150 @@ BrowserTabContent::applyMouseRotation(BrainOpenGLViewportContent* viewportConten if (isVolumeSlicesDisplayed()) { switch (getSliceProjectionType()) { case VolumeSliceProjectionTypeEnum::VOLUME_SLICE_PROJECTION_OBLIQUE: - { - int viewport[4]; - viewportContent->getModelViewport(viewport); - VolumeSliceViewPlaneEnum::Enum slicePlane = this->getSliceViewPlane(); - int sliceViewport[4] = { - viewport[0], - viewport[1], - viewport[2], - viewport[3] - }; - if (slicePlane == VolumeSliceViewPlaneEnum::ALL) { - slicePlane = BrainOpenGLViewportContent::getSliceViewPlaneForVolumeAllSliceView(viewport, - getSlicePlanesAllViewLayout(), - mousePressX, - mousePressY, - sliceViewport); - } - - Matrix4x4 rotationMatrix = getObliqueVolumeRotationMatrix(); - - if (slicePlane == VolumeSliceViewPlaneEnum::ALL) { + if (viewportContent == NULL) { + /* + * When no viewport content is available, apply 'ALL' rotation + */ + Matrix4x4 rotationMatrix = getObliqueVolumeRotationMatrix(); rotationMatrix.rotateX(-mouseDeltaY); rotationMatrix.rotateY(mouseDeltaX); + setObliqueVolumeRotationMatrix(rotationMatrix); } else { - if ((mouseDeltaX != 0) - || (mouseDeltaY != 0)) { - - const int previousMouseX = mouseX - mouseDeltaX; - const int previousMouseY = mouseY - mouseDeltaY; - - /* - * Need to account for the quadrants!!!! - */ - const float viewportCenter[3] = { - (float)(sliceViewport[0] + sliceViewport[2] / 2), - ((float)sliceViewport[1] + sliceViewport[3] / 2), - 0.0 - }; - - const float oldPos[3] = { - (float)previousMouseX, - (float)previousMouseY, - 0.0 - }; - - const float newPos[3] = { - (float)mouseX, - (float)mouseY, - 0.0 - }; - - /* - * Compute normal vector from viewport center to - * old mouse position to new mouse position. - * If normal-Z is positive, mouse has been moved - * in a counter clockwise motion relative to center. - * If normal-Z is negative, mouse has moved clockwise. - */ - float normalDirection[3]; - MathFunctions::normalVectorDirection(viewportCenter, - oldPos, - newPos, - normalDirection); - bool isClockwise = false; - bool isCounterClockwise = false; - if (normalDirection[2] > 0.0) { - isCounterClockwise = true; - } - else if (normalDirection[2] < 0.0) { - isClockwise = true; - } - - if (isClockwise - || isCounterClockwise) { - float mouseDelta = std::sqrt(static_cast((mouseDeltaX * mouseDeltaX) - + (mouseDeltaY * mouseDeltaY))); + int viewport[4]; + viewportContent->getModelViewport(viewport); + VolumeSliceViewPlaneEnum::Enum slicePlane = this->getSliceViewPlane(); + int sliceViewport[4] = { + viewport[0], + viewport[1], + viewport[2], + viewport[3] + }; + if (slicePlane == VolumeSliceViewPlaneEnum::ALL) { + slicePlane = BrainOpenGLViewportContent::getSliceViewPlaneForVolumeAllSliceView(viewport, + getSlicePlanesAllViewLayout(), + mousePressX, + mousePressY, + sliceViewport); + } + + Matrix4x4 rotationMatrix = getObliqueVolumeRotationMatrix(); + + if (slicePlane == VolumeSliceViewPlaneEnum::ALL) { + rotationMatrix.rotateX(-mouseDeltaY); + rotationMatrix.rotateY(mouseDeltaX); + } + else { + if ((mouseDeltaX != 0) + || (mouseDeltaY != 0)) { -// /* -// * Rotation needs to be oppposite for newer -// * oblique slice drawing for volumes that -// * do not have a voxel corresponding to -// * the origin. -// */ -// mouseDelta = -mouseDelta; + const int previousMouseX = mouseX - mouseDeltaX; + const int previousMouseY = mouseY - mouseDeltaY; - switch (slicePlane) { - case VolumeSliceViewPlaneEnum::ALL: - { - CaretAssert(0); - } - break; - case VolumeSliceViewPlaneEnum::AXIAL: - { - Matrix4x4 rotation; - if (isClockwise) { - rotation.rotateZ(mouseDelta); - } - else if (isCounterClockwise) { - rotation.rotateZ(-mouseDelta); - } - rotationMatrix.premultiply(rotation); - } - break; - case VolumeSliceViewPlaneEnum::CORONAL: - { - Matrix4x4 rotation; - if (isClockwise) { - rotation.rotateY(-mouseDelta); + /* + * Need to account for the quadrants!!!! + */ + const float viewportCenter[3] = { + (float)(sliceViewport[0] + sliceViewport[2] / 2), + ((float)sliceViewport[1] + sliceViewport[3] / 2), + 0.0 + }; + + const float oldPos[3] = { + (float)previousMouseX, + (float)previousMouseY, + 0.0 + }; + + const float newPos[3] = { + (float)mouseX, + (float)mouseY, + 0.0 + }; + + /* + * Compute normal vector from viewport center to + * old mouse position to new mouse position. + * If normal-Z is positive, mouse has been moved + * in a counter clockwise motion relative to center. + * If normal-Z is negative, mouse has moved clockwise. + */ + float normalDirection[3]; + MathFunctions::normalVectorDirection(viewportCenter, + oldPos, + newPos, + normalDirection); + bool isClockwise = false; + bool isCounterClockwise = false; + if (normalDirection[2] > 0.0) { + isCounterClockwise = true; + } + else if (normalDirection[2] < 0.0) { + isClockwise = true; + } + + if (isClockwise + || isCounterClockwise) { + float mouseDelta = std::sqrt(static_cast((mouseDeltaX * mouseDeltaX) + + (mouseDeltaY * mouseDeltaY))); + + // /* + // * Rotation needs to be oppposite for newer + // * oblique slice drawing for volumes that + // * do not have a voxel corresponding to + // * the origin. + // */ + // mouseDelta = -mouseDelta; + + switch (slicePlane) { + case VolumeSliceViewPlaneEnum::ALL: + { + CaretAssert(0); } - else if (isCounterClockwise) { - rotation.rotateY(mouseDelta); + break; + case VolumeSliceViewPlaneEnum::AXIAL: + { + Matrix4x4 rotation; + if (isClockwise) { + rotation.rotateZ(mouseDelta); + } + else if (isCounterClockwise) { + rotation.rotateZ(-mouseDelta); + } + rotationMatrix.premultiply(rotation); } - rotationMatrix.premultiply(rotation); - } - break; - case VolumeSliceViewPlaneEnum::PARASAGITTAL: - { - Matrix4x4 rotation; - if (isClockwise) { - rotation.rotateX(-mouseDelta); + break; + case VolumeSliceViewPlaneEnum::CORONAL: + { + Matrix4x4 rotation; + if (isClockwise) { + rotation.rotateY(-mouseDelta); + } + else if (isCounterClockwise) { + rotation.rotateY(mouseDelta); + } + rotationMatrix.premultiply(rotation); } - else if (isCounterClockwise) { - rotation.rotateX(mouseDelta); + break; + case VolumeSliceViewPlaneEnum::PARASAGITTAL: + { + Matrix4x4 rotation; + if (isClockwise) { + rotation.rotateX(-mouseDelta); + } + else if (isCounterClockwise) { + rotation.rotateX(mouseDelta); + } + rotationMatrix.premultiply(rotation); } - rotationMatrix.premultiply(rotation); + break; } - break; } } } + + setObliqueVolumeRotationMatrix(rotationMatrix); } - - setObliqueVolumeRotationMatrix(rotationMatrix); - } break; case VolumeSliceProjectionTypeEnum::VOLUME_SLICE_PROJECTION_ORTHOGONAL: break; diff --git a/src/Brain/BrowserTabContent.h b/src/Brain/BrowserTabContent.h index bbefac0e7..28f0547af 100644 --- a/src/Brain/BrowserTabContent.h +++ b/src/Brain/BrowserTabContent.h @@ -432,6 +432,10 @@ namespace caret { void setWholeBrainCerebellumSeparation(const float separation); + ViewingTransformations* getViewingTransformation(); + + const ViewingTransformations* getViewingTransformation() const; + virtual SceneClass* saveToScene(const SceneAttributes* sceneAttributes, const AString& instanceName); @@ -463,10 +467,6 @@ namespace caret { // const int32_t mousePressY, // int sliceViewportOut[4]) const; - ViewingTransformations* getViewingTransformation(); - - const ViewingTransformations* getViewingTransformation() const; - void updateBrainModelYokedBrowserTabs(); void updateYokedModelBrowserTabs(); diff --git a/src/GuiQt/CMakeLists.txt b/src/GuiQt/CMakeLists.txt index 068740fd2..5120d694a 100644 --- a/src/GuiQt/CMakeLists.txt +++ b/src/GuiQt/CMakeLists.txt @@ -459,15 +459,16 @@ VolumeSurfaceOutlineSetViewController.h VolumeSurfaceOutlineViewController.h WbMacroCustomDataInfo.h WbMacroCustomDataTypeEnum.h -WbMacroCustomOperationBase.h -WbMacroCustomOperationAnimateRotation.h -WbMacroCustomOperationDelay.h -WbMacroCustomOperationManager.h WbMacroCustomOperationAnimateOverlayCrossFade.h +WbMacroCustomOperationAnimateRotation.h WbMacroCustomOperationAnimateSurfaceInterpolation.h -WbMacroCustomOperationTypeEnum.h WbMacroCustomOperationAnimateVolumeSliceSequence.h WbMacroCustomOperationAnimateVolumeToSurfaceCrossFade.h +WbMacroCustomOperationBase.h +WbMacroCustomOperationDelay.h +WbMacroCustomOperationIncrementRotation.h +WbMacroCustomOperationManager.h +WbMacroCustomOperationTypeEnum.h WbMacroHelper.h WuQCollapsibleWidget.h WuQDataEntryDialog.h @@ -724,15 +725,16 @@ VolumeSurfaceOutlineSetViewController.cxx VolumeSurfaceOutlineViewController.cxx WbMacroCustomDataInfo.cxx WbMacroCustomDataTypeEnum.cxx +WbMacroCustomOperationAnimateOverlayCrossFade.cxx WbMacroCustomOperationAnimateRotation.cxx +WbMacroCustomOperationAnimateSurfaceInterpolation.cxx +WbMacroCustomOperationAnimateVolumeSliceSequence.cxx +WbMacroCustomOperationAnimateVolumeToSurfaceCrossFade.cxx WbMacroCustomOperationBase.cxx WbMacroCustomOperationDelay.cxx +WbMacroCustomOperationIncrementRotation.cxx WbMacroCustomOperationManager.cxx -WbMacroCustomOperationAnimateOverlayCrossFade.cxx -WbMacroCustomOperationAnimateSurfaceInterpolation.cxx WbMacroCustomOperationTypeEnum.cxx -WbMacroCustomOperationAnimateVolumeSliceSequence.cxx -WbMacroCustomOperationAnimateVolumeToSurfaceCrossFade.cxx WbMacroHelper.cxx WuQCollapsibleWidget.cxx WuQDataEntryDialog.cxx diff --git a/src/GuiQt/WbMacroCustomOperationIncrementRotation.cxx b/src/GuiQt/WbMacroCustomOperationIncrementRotation.cxx new file mode 100644 index 000000000..29704efb9 --- /dev/null +++ b/src/GuiQt/WbMacroCustomOperationIncrementRotation.cxx @@ -0,0 +1,232 @@ + +/*LICENSE_START*/ +/* + * Copyright (C) 2019 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + +#define __WB_MACRO_CUSTOM_OPERATION_INCREMENT_ROTATION_DECLARE__ +#include "WbMacroCustomOperationIncrementRotation.h" +#undef __WB_MACRO_CUSTOM_OPERATION_INCREMENT_ROTATION_DECLARE__ + +#include "BrainBrowserWindow.h" +#include "BrowserTabContent.h" +#include "CaretAssert.h" +#include "Matrix4x4.h" +#include "Model.h" +#include "SystemUtilities.h" +#include "ViewingTransformations.h" +#include "WbMacroCustomDataTypeEnum.h" +#include "WbMacroCustomOperationTypeEnum.h" +#include "WuQMacroCommand.h" +#include "WuQMacroCommandParameter.h" +#include "WuQMacroExecutorMonitor.h" + +using namespace caret; + + + +/** + * \class caret::WbMacroCustomOperationIncrementRotation + * \brief Macro custom operation for incremental rotation + * \ingroup GuiQt + */ + +/** + * Constructor. + */ +WbMacroCustomOperationIncrementRotation::WbMacroCustomOperationIncrementRotation() +: WbMacroCustomOperationBase(WbMacroCustomOperationTypeEnum::INCREMENTAL_ROTATION) +{ + +} + +/** + * Destructor. + */ +WbMacroCustomOperationIncrementRotation::~WbMacroCustomOperationIncrementRotation() +{ +} + +/** + * Get a new instance of the macro command + * + * @return + * Pointer to command or NULL if not valid + * Use getErrorMessage() for error information if NULL returned + */ +WuQMacroCommand* +WbMacroCustomOperationIncrementRotation::createCommand() +{ + const int32_t versionOne(1); + + QString errorMessage; + WuQMacroCommand* command = WuQMacroCommand::newInstanceCustomCommand(WbMacroCustomOperationTypeEnum::toName(getOperationType()), + versionOne, + "none", + WbMacroCustomOperationTypeEnum::toGuiName(getOperationType()), + "Incremental Rotation (degrees) About Screen Axis", + 1.0, + errorMessage); + if (command != NULL) { + command->addParameter(WuQMacroDataValueTypeEnum::AXIS, + "Screen Axis", + "Y"); + command->addParameter(WuQMacroDataValueTypeEnum::FLOAT, + "Rotation (Degrees)", + (float)360.0); + } + else { + appendToErrorMessage(errorMessage); + } + + return command; +} + +/** + * Execute the macro command + * + * @param parent + * Parent widget for any dialogs + * @param executorMonitor + * the macro executor monitor + * @param executorOptions + * Options for executor + * @param macroCommand + * macro command to run + * @return + * True if command executed successfully, else false + * Use getErrorMessage() for error information if false returned + */ +bool +WbMacroCustomOperationIncrementRotation::executeCommand(QWidget* parent, + const WuQMacroExecutorMonitor* /*executorMonitor*/, + const WuQMacroExecutorOptions* /*executorOptions*/, + const WuQMacroCommand* macroCommand) +{ + CaretAssert(parent); + CaretAssert(macroCommand); + + if ( ! validateCorrectNumberOfParameters(macroCommand, 2)) { + return false; + } + const QString axisName(macroCommand->getParameterAtIndex(0)->getValue().toString().toUpper()); + const float incrementalRotation = macroCommand->getParameterAtIndex(1)->getValue().toFloat(); + + BrainBrowserWindow* bbw = qobject_cast(parent); + if (bbw == NULL) { + appendToErrorMessage("Parent for running surface macro is not a browser window."); + return false; + } + + BrowserTabContent* tabContent = bbw->getBrowserTabContent(); + if (tabContent == NULL) { + appendToErrorMessage("No tab is selected in browser window."); + return false; + } + + Axis axis = Axis::X; + if (axisName == "X") { + axis = Axis::X; + } + else if (axisName == "Y") { + axis = Axis::Y; + } + else if (axisName == "Z") { + axis = Axis::Z; + } + else { + appendToErrorMessage("Axis named \"" + + axisName + + "\" is invalid. Use X, Y, or Z."); + } + + if ( ! getErrorMessage().isEmpty()) { + return false; + } + Model* model = tabContent->getModelForDisplay(); + if (model == NULL) { + return false; + } + + + + int32_t mousePressX(0); + int32_t mousePressY(0); + int32_t mouseX(0); + int32_t mouseY(0); + int32_t mouseDeltaX(0); + int32_t mouseDeltaY(0); + bool doMouseFlag(false); + bool doZFlag(false); + switch (axis) { + case Axis::X: + doMouseFlag = true; + mousePressX = 50; + mouseX = 50; + mouseDeltaY = incrementalRotation; + if (mouseDeltaY >= 0.0) { + mousePressY = 1; + mouseY = mouseDeltaY + mousePressY; + } + else { + mouseY = 1; + mousePressY = 1 - mouseDeltaY; + } + break; + case Axis::Y: + doMouseFlag = true; + mousePressY = 50; + mouseY = 50; + mouseDeltaX = incrementalRotation; + if (mouseDeltaX >= 0.0) { + mousePressX = 1; + mouseX = mouseDeltaX + mousePressX; + } + else { + mouseX = 1; + mousePressX = 1 - mouseDeltaX; + } + break; + case Axis::Z: + if (incrementalRotation != 0.0) { + doZFlag = true; + } + break; + } + + if (doMouseFlag) { + BrainOpenGLViewportContent* viewportContent(NULL); + tabContent->applyMouseRotation(viewportContent, + mousePressX, + mousePressY, + mouseX, + mouseY, + mouseDeltaX, + mouseDeltaY); + } + else if (doZFlag) { + ViewingTransformations* viewingTransform = tabContent->getViewingTransformation(); + Matrix4x4 rotationMatrix = viewingTransform->getRotationMatrix(); + rotationMatrix.rotateZ(incrementalRotation); + viewingTransform->setRotationMatrix(rotationMatrix); + } + + updateGraphics(); + + return true; +} diff --git a/src/GuiQt/WbMacroCustomOperationIncrementRotation.h b/src/GuiQt/WbMacroCustomOperationIncrementRotation.h new file mode 100644 index 000000000..b33a8cb41 --- /dev/null +++ b/src/GuiQt/WbMacroCustomOperationIncrementRotation.h @@ -0,0 +1,74 @@ +#ifndef __WB_MACRO_CUSTOM_OPERATION_INCREMENT_ROTATION_H__ +#define __WB_MACRO_CUSTOM_OPERATION_INCREMENT_ROTATION_H__ + +/*LICENSE_START*/ +/* + * Copyright (C) 2019 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + + + +#include + +#include "WbMacroCustomOperationBase.h" + + + +namespace caret { + + class BrowserTabContent; + + class WbMacroCustomOperationIncrementRotation : public WbMacroCustomOperationBase { + + public: + WbMacroCustomOperationIncrementRotation(); + + virtual ~WbMacroCustomOperationIncrementRotation(); + + WbMacroCustomOperationIncrementRotation(const WbMacroCustomOperationIncrementRotation&) = delete; + + WbMacroCustomOperationIncrementRotation& operator=(const WbMacroCustomOperationIncrementRotation&) = delete; + + virtual bool executeCommand(QWidget* parent, + const WuQMacroExecutorMonitor* executorMonitor, + const WuQMacroExecutorOptions* executorOptions, + const WuQMacroCommand* macroCommand) override; + + virtual WuQMacroCommand* createCommand() override; + + + // ADD_NEW_METHODS_HERE + + private: + enum class Axis { + X, + Y, + Z + }; + + + // ADD_NEW_MEMBERS_HERE + + }; + +#ifdef __WB_MACRO_CUSTOM_OPERATION_INCREMENT_ROTATION_DECLARE__ + // +#endif // __WB_MACRO_CUSTOM_OPERATION_INCREMENT_ROTATION_DECLARE__ + +} // namespace +#endif //__WB_MACRO_CUSTOM_OPERATION_INCREMENT_ROTATION_H__ diff --git a/src/GuiQt/WbMacroCustomOperationManager.cxx b/src/GuiQt/WbMacroCustomOperationManager.cxx index c40444e92..64e7c8ff1 100644 --- a/src/GuiQt/WbMacroCustomOperationManager.cxx +++ b/src/GuiQt/WbMacroCustomOperationManager.cxx @@ -45,6 +45,7 @@ #include "WbMacroCustomOperationTypeEnum.h" #include "WbMacroCustomOperationAnimateVolumeSliceSequence.h" #include "WbMacroCustomOperationAnimateVolumeToSurfaceCrossFade.h" +#include "WbMacroCustomOperationIncrementRotation.h" #include "WbMacroCustomDataInfo.h" #include "WuQMacroCommand.h" #include "WuQMacroCommandParameter.h" @@ -731,6 +732,9 @@ WbMacroCustomOperationManager::createCommand(const WbMacroCustomOperationTypeEnu case WbMacroCustomOperationTypeEnum::ANIMATE_VOLUME_TO_SURFACE_CROSS_FADE: operationOut = new WbMacroCustomOperationAnimateVolumeToSurfaceCrossFade(); break; + case WbMacroCustomOperationTypeEnum::INCREMENTAL_ROTATION: + operationOut = new WbMacroCustomOperationIncrementRotation(); + break; } CaretAssert(operationOut); diff --git a/src/GuiQt/WbMacroCustomOperationTypeEnum.cxx b/src/GuiQt/WbMacroCustomOperationTypeEnum.cxx index f7e0a2b49..8f9c72516 100644 --- a/src/GuiQt/WbMacroCustomOperationTypeEnum.cxx +++ b/src/GuiQt/WbMacroCustomOperationTypeEnum.cxx @@ -128,7 +128,11 @@ WbMacroCustomOperationTypeEnum::initialize() enumData.push_back(WbMacroCustomOperationTypeEnum(DELAY, "DELAY", - "Delay")); + "Delay")); + + enumData.push_back(WbMacroCustomOperationTypeEnum(INCREMENTAL_ROTATION, + "INCREMENTAL_ROTATION", + "Incremental Rotation")); } /** diff --git a/src/GuiQt/WbMacroCustomOperationTypeEnum.h b/src/GuiQt/WbMacroCustomOperationTypeEnum.h index 9f4687ff9..4fb4da6bb 100644 --- a/src/GuiQt/WbMacroCustomOperationTypeEnum.h +++ b/src/GuiQt/WbMacroCustomOperationTypeEnum.h @@ -35,8 +35,6 @@ public: * Enumerated values. */ enum Enum { - /** Delay **/ - DELAY, /** Animate rotation of viewed model */ ANIMATE_ROTATION, /** Animate CrossFade of overlays */ @@ -46,7 +44,11 @@ public: /** Animate Volume slice sequence */ ANIMATE_VOLUME_SLICE_SEQUENCE, /** Animate Volume to surface cross fade */ - ANIMATE_VOLUME_TO_SURFACE_CROSS_FADE + ANIMATE_VOLUME_TO_SURFACE_CROSS_FADE, + /** Delay **/ + DELAY, + /** Incremental Rotation */ + INCREMENTAL_ROTATION }; -- GitLab From 6b1de2db2c484195abc1b1b8d089888cf5fb6c8f Mon Sep 17 00:00:00 2001 From: John Harwell Date: Fri, 5 Apr 2019 11:14:14 -0500 Subject: [PATCH 275/427] Macros: Added a volume slice increment command --- src/GuiQt/CMakeLists.txt | 2 + ...bMacroCustomOperationIncrementRotation.cxx | 6 +- ...croCustomOperationIncrementVolumeSlice.cxx | 191 ++++++++++++++++++ ...MacroCustomOperationIncrementVolumeSlice.h | 74 +++++++ src/GuiQt/WbMacroCustomOperationManager.cxx | 4 + src/GuiQt/WbMacroCustomOperationTypeEnum.cxx | 5 + src/GuiQt/WbMacroCustomOperationTypeEnum.h | 4 +- 7 files changed, 282 insertions(+), 4 deletions(-) create mode 100644 src/GuiQt/WbMacroCustomOperationIncrementVolumeSlice.cxx create mode 100644 src/GuiQt/WbMacroCustomOperationIncrementVolumeSlice.h diff --git a/src/GuiQt/CMakeLists.txt b/src/GuiQt/CMakeLists.txt index 5120d694a..522e13064 100644 --- a/src/GuiQt/CMakeLists.txt +++ b/src/GuiQt/CMakeLists.txt @@ -467,6 +467,7 @@ WbMacroCustomOperationAnimateVolumeToSurfaceCrossFade.h WbMacroCustomOperationBase.h WbMacroCustomOperationDelay.h WbMacroCustomOperationIncrementRotation.h +WbMacroCustomOperationIncrementVolumeSlice.h WbMacroCustomOperationManager.h WbMacroCustomOperationTypeEnum.h WbMacroHelper.h @@ -733,6 +734,7 @@ WbMacroCustomOperationAnimateVolumeToSurfaceCrossFade.cxx WbMacroCustomOperationBase.cxx WbMacroCustomOperationDelay.cxx WbMacroCustomOperationIncrementRotation.cxx +WbMacroCustomOperationIncrementVolumeSlice.cxx WbMacroCustomOperationManager.cxx WbMacroCustomOperationTypeEnum.cxx WbMacroHelper.cxx diff --git a/src/GuiQt/WbMacroCustomOperationIncrementRotation.cxx b/src/GuiQt/WbMacroCustomOperationIncrementRotation.cxx index 29704efb9..cc6319e24 100644 --- a/src/GuiQt/WbMacroCustomOperationIncrementRotation.cxx +++ b/src/GuiQt/WbMacroCustomOperationIncrementRotation.cxx @@ -153,13 +153,12 @@ WbMacroCustomOperationIncrementRotation::executeCommand(QWidget* parent, appendToErrorMessage("Axis named \"" + axisName + "\" is invalid. Use X, Y, or Z."); - } - - if ( ! getErrorMessage().isEmpty()) { return false; } + Model* model = tabContent->getModelForDisplay(); if (model == NULL) { + appendToErrorMessage("No model is displayed that can be rotated"); return false; } @@ -227,6 +226,7 @@ WbMacroCustomOperationIncrementRotation::executeCommand(QWidget* parent, } updateGraphics(); + updateUserInterface(); return true; } diff --git a/src/GuiQt/WbMacroCustomOperationIncrementVolumeSlice.cxx b/src/GuiQt/WbMacroCustomOperationIncrementVolumeSlice.cxx new file mode 100644 index 000000000..f64d6fe49 --- /dev/null +++ b/src/GuiQt/WbMacroCustomOperationIncrementVolumeSlice.cxx @@ -0,0 +1,191 @@ + +/*LICENSE_START*/ +/* + * Copyright (C) 2019 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + +#define __WB_MACRO_CUSTOM_OPERATION_INCREMENT_VOLUME_SLICE_DECLARE__ +#include "WbMacroCustomOperationIncrementVolumeSlice.h" +#undef __WB_MACRO_CUSTOM_OPERATION_INCREMENT_VOLUME_SLICE_DECLARE__ + +#include "BrainBrowserWindow.h" +#include "BrowserTabContent.h" +#include "CaretAssert.h" +#include "Matrix4x4.h" +#include "Model.h" +#include "ModelVolume.h" +#include "ModelWholeBrain.h" +#include "SystemUtilities.h" +#include "ViewingTransformations.h" +#include "WbMacroCustomDataTypeEnum.h" +#include "WbMacroCustomOperationTypeEnum.h" +#include "WuQMacroCommand.h" +#include "WuQMacroCommandParameter.h" +#include "WuQMacroExecutorMonitor.h" + +using namespace caret; + + + +/** + * \class caret::WbMacroCustomOperationIncrementVolumeSlice + * \brief Macro custom operation for incremental rotation + * \ingroup GuiQt + */ + +/** + * Constructor. + */ +WbMacroCustomOperationIncrementVolumeSlice::WbMacroCustomOperationIncrementVolumeSlice() +: WbMacroCustomOperationBase(WbMacroCustomOperationTypeEnum::INCREMENTAL_VOLUME_SLICE) +{ + +} + +/** + * Destructor. + */ +WbMacroCustomOperationIncrementVolumeSlice::~WbMacroCustomOperationIncrementVolumeSlice() +{ +} + +/** + * Get a new instance of the macro command + * + * @return + * Pointer to command or NULL if not valid + * Use getErrorMessage() for error information if NULL returned + */ +WuQMacroCommand* +WbMacroCustomOperationIncrementVolumeSlice::createCommand() +{ + const int32_t versionOne(1); + + QString errorMessage; + WuQMacroCommand* command = WuQMacroCommand::newInstanceCustomCommand(WbMacroCustomOperationTypeEnum::toName(getOperationType()), + versionOne, + "none", + WbMacroCustomOperationTypeEnum::toGuiName(getOperationType()), + "Increment Volume Slice", + 1.0, + errorMessage); + if (command != NULL) { + command->addParameter(WuQMacroDataValueTypeEnum::INTEGER, + "Increment Slice Index By", + (int)1); + } + else { + appendToErrorMessage(errorMessage); + } + + return command; +} + +/** + * Execute the macro command + * + * @param parent + * Parent widget for any dialogs + * @param executorMonitor + * the macro executor monitor + * @param executorOptions + * Options for executor + * @param macroCommand + * macro command to run + * @return + * True if command executed successfully, else false + * Use getErrorMessage() for error information if false returned + */ +bool +WbMacroCustomOperationIncrementVolumeSlice::executeCommand(QWidget* parent, + const WuQMacroExecutorMonitor* /*executorMonitor*/, + const WuQMacroExecutorOptions* /*executorOptions*/, + const WuQMacroCommand* macroCommand) +{ + CaretAssert(parent); + CaretAssert(macroCommand); + + if ( ! validateCorrectNumberOfParameters(macroCommand, 1)) { + return false; + } + + const float incrementSlice = macroCommand->getParameterAtIndex(0)->getValue().toFloat(); + + BrainBrowserWindow* bbw = qobject_cast(parent); + if (bbw == NULL) { + appendToErrorMessage("Parent for running macro is not a browser window."); + return false; + } + + BrowserTabContent* tabContent = bbw->getBrowserTabContent(); + if (tabContent == NULL) { + appendToErrorMessage("No tab is selected in browser window."); + return false; + } + const int32_t tabIndex = tabContent->getTabNumber(); + + VolumeMappableInterface* underlayVolumeFile(NULL); + ModelVolume* volumeModel = tabContent->getDisplayedVolumeModel(); + + if (volumeModel != NULL) { + underlayVolumeFile = volumeModel->getUnderlayVolumeFile(tabIndex); + } + ModelWholeBrain* wholeBrainModel = tabContent->getDisplayedWholeBrainModel(); + if (wholeBrainModel != NULL) { + underlayVolumeFile = wholeBrainModel->getUnderlayVolumeFile(tabIndex); + } + + if (underlayVolumeFile == NULL) { + appendToErrorMessage("Must have ALL or Volume view for slice increment or no volume is displayed."); + return false; + } + + int32_t sliceIndexAxial = tabContent->getSliceIndexAxial(underlayVolumeFile); + int32_t sliceIndexCoronal = tabContent->getSliceIndexCoronal(underlayVolumeFile); + int32_t sliceIndexParasagittal = tabContent->getSliceIndexParasagittal(underlayVolumeFile); + + VolumeSliceViewPlaneEnum::Enum slicePlane = tabContent->getSliceViewPlane(); + switch (slicePlane) { + case VolumeSliceViewPlaneEnum::ALL: + sliceIndexAxial += incrementSlice; + sliceIndexCoronal += incrementSlice; + sliceIndexParasagittal += incrementSlice; + break; + case VolumeSliceViewPlaneEnum::AXIAL: + sliceIndexAxial += incrementSlice; + break; + case VolumeSliceViewPlaneEnum::CORONAL: + sliceIndexCoronal += incrementSlice; + break; + case VolumeSliceViewPlaneEnum::PARASAGITTAL: + sliceIndexParasagittal += incrementSlice; + break; + } + + tabContent->setSliceIndexAxial(underlayVolumeFile, + sliceIndexAxial); + tabContent->setSliceIndexCoronal(underlayVolumeFile, + sliceIndexCoronal); + tabContent->setSliceIndexParasagittal(underlayVolumeFile, + sliceIndexParasagittal); + + updateGraphics(); + updateUserInterface(); + + return true; +} diff --git a/src/GuiQt/WbMacroCustomOperationIncrementVolumeSlice.h b/src/GuiQt/WbMacroCustomOperationIncrementVolumeSlice.h new file mode 100644 index 000000000..fd1cf1051 --- /dev/null +++ b/src/GuiQt/WbMacroCustomOperationIncrementVolumeSlice.h @@ -0,0 +1,74 @@ +#ifndef __WB_MACRO_CUSTOM_OPERATION_INCREMENT_VOLUME_SLICE_H__ +#define __WB_MACRO_CUSTOM_OPERATION_INCREMENT_VOLUME_SLICE_H__ + +/*LICENSE_START*/ +/* + * Copyright (C) 2019 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + + + +#include + +#include "WbMacroCustomOperationBase.h" + + + +namespace caret { + + class BrowserTabContent; + + class WbMacroCustomOperationIncrementVolumeSlice : public WbMacroCustomOperationBase { + + public: + WbMacroCustomOperationIncrementVolumeSlice(); + + virtual ~WbMacroCustomOperationIncrementVolumeSlice(); + + WbMacroCustomOperationIncrementVolumeSlice(const WbMacroCustomOperationIncrementVolumeSlice&) = delete; + + WbMacroCustomOperationIncrementVolumeSlice& operator=(const WbMacroCustomOperationIncrementVolumeSlice&) = delete; + + virtual bool executeCommand(QWidget* parent, + const WuQMacroExecutorMonitor* executorMonitor, + const WuQMacroExecutorOptions* executorOptions, + const WuQMacroCommand* macroCommand) override; + + virtual WuQMacroCommand* createCommand() override; + + + // ADD_NEW_METHODS_HERE + + private: + enum class Axis { + X, + Y, + Z + }; + + + // ADD_NEW_MEMBERS_HERE + + }; + +#ifdef __WB_MACRO_CUSTOM_OPERATION_INCREMENT_VOLUME_SLICE_DECLARE__ + // +#endif // __WB_MACRO_CUSTOM_OPERATION_INCREMENT_VOLUME_SLICE_DECLARE__ + +} // namespace +#endif //__WB_MACRO_CUSTOM_OPERATION_INCREMENT_VOLUME_SLICE_H__ diff --git a/src/GuiQt/WbMacroCustomOperationManager.cxx b/src/GuiQt/WbMacroCustomOperationManager.cxx index 64e7c8ff1..586f791e2 100644 --- a/src/GuiQt/WbMacroCustomOperationManager.cxx +++ b/src/GuiQt/WbMacroCustomOperationManager.cxx @@ -46,6 +46,7 @@ #include "WbMacroCustomOperationAnimateVolumeSliceSequence.h" #include "WbMacroCustomOperationAnimateVolumeToSurfaceCrossFade.h" #include "WbMacroCustomOperationIncrementRotation.h" +#include "WbMacroCustomOperationIncrementVolumeSlice.h" #include "WbMacroCustomDataInfo.h" #include "WuQMacroCommand.h" #include "WuQMacroCommandParameter.h" @@ -735,6 +736,9 @@ WbMacroCustomOperationManager::createCommand(const WbMacroCustomOperationTypeEnu case WbMacroCustomOperationTypeEnum::INCREMENTAL_ROTATION: operationOut = new WbMacroCustomOperationIncrementRotation(); break; + case WbMacroCustomOperationTypeEnum::INCREMENTAL_VOLUME_SLICE: + operationOut = new WbMacroCustomOperationIncrementVolumeSlice(); + break; } CaretAssert(operationOut); diff --git a/src/GuiQt/WbMacroCustomOperationTypeEnum.cxx b/src/GuiQt/WbMacroCustomOperationTypeEnum.cxx index 8f9c72516..c5d08aa69 100644 --- a/src/GuiQt/WbMacroCustomOperationTypeEnum.cxx +++ b/src/GuiQt/WbMacroCustomOperationTypeEnum.cxx @@ -133,6 +133,11 @@ WbMacroCustomOperationTypeEnum::initialize() enumData.push_back(WbMacroCustomOperationTypeEnum(INCREMENTAL_ROTATION, "INCREMENTAL_ROTATION", "Incremental Rotation")); + + enumData.push_back(WbMacroCustomOperationTypeEnum(INCREMENTAL_VOLUME_SLICE, + "INCREMENTAL_VOLUME_SLICE", + "Increment Volume Slice")); + } /** diff --git a/src/GuiQt/WbMacroCustomOperationTypeEnum.h b/src/GuiQt/WbMacroCustomOperationTypeEnum.h index 4fb4da6bb..dc7041702 100644 --- a/src/GuiQt/WbMacroCustomOperationTypeEnum.h +++ b/src/GuiQt/WbMacroCustomOperationTypeEnum.h @@ -48,7 +48,9 @@ public: /** Delay **/ DELAY, /** Incremental Rotation */ - INCREMENTAL_ROTATION + INCREMENTAL_ROTATION, + /** Increment volume slice */ + INCREMENTAL_VOLUME_SLICE }; -- GitLab From c31633c31d06209039ece64a6a5b3b5da5d4889b Mon Sep 17 00:00:00 2001 From: John Harwell Date: Fri, 5 Apr 2019 11:56:16 -0500 Subject: [PATCH 276/427] Fixed handling of keyEvent. If a KeyEvent is received, and the processing for the event (such as running a macro) calls QApplication::processEvents(), ::keyEvent() may be called AGAIN from QApplication::processEvents() before the original KeyEvent's processing has completed. So, when a KeyEvent is received, ignore any other KeyEvents until the original one has completed. --- src/GuiQt/BrainBrowserWindow.cxx | 16 ++++++++++++++++ src/GuiQt/BrainBrowserWindow.h | 3 +++ 2 files changed, 19 insertions(+) diff --git a/src/GuiQt/BrainBrowserWindow.cxx b/src/GuiQt/BrainBrowserWindow.cxx index 21a098029..2005bf9fd 100644 --- a/src/GuiQt/BrainBrowserWindow.cxx +++ b/src/GuiQt/BrainBrowserWindow.cxx @@ -772,6 +772,20 @@ BrainBrowserWindow::closeEvent(QCloseEvent* event) void BrainBrowserWindow::keyPressEvent(QKeyEvent* event) { + /* + * When there is a key press, it may take time to process such + * as when a macro runs. If the macro calls QApplication::processEvents(), + * it may result in this method getting called again by Qt + * before the macro has completed from a previous key press event. + * This flag will ignore key press events until a macro + * has had time to finish + */ + if (m_keyEventProcessingFlag) { + return; + } + m_keyEventProcessingFlag = true; + + bool keyEventWasProcessed = false; /* @@ -798,6 +812,8 @@ BrainBrowserWindow::keyPressEvent(QKeyEvent* event) if ( ! keyEventWasProcessed) { QMainWindow::keyPressEvent(event); } + + m_keyEventProcessingFlag = false; } /** diff --git a/src/GuiQt/BrainBrowserWindow.h b/src/GuiQt/BrainBrowserWindow.h index 03688d4fe..45d516c93 100644 --- a/src/GuiQt/BrainBrowserWindow.h +++ b/src/GuiQt/BrainBrowserWindow.h @@ -470,6 +470,9 @@ namespace caret { static std::set s_brainBrowserWindows; QString m_objectNamePrefix; + + bool m_keyEventProcessingFlag = false; + }; #ifdef __BRAIN_BROWSER_WINDOW_DECLARE__ std::set BrainBrowserWindow::s_brainBrowserWindows; -- GitLab From 872ae5056409ab5f843a4215462c4c914e67e354 Mon Sep 17 00:00:00 2001 From: John Harwell Date: Fri, 5 Apr 2019 16:04:09 -0500 Subject: [PATCH 277/427] MacOS Only: Added an option to wb_view "-mac-menu-duplicate". This is an experimental option and creates a menu bar at the top of first created window that duplicates the content of the menu bar that appears at the top of the display (similar to menu bar on Linux and Windows versions of wb_view). All menu items function except for menus whose content are determined at the time the menu is displayed (such as the list of recent scene files). This options may be useful when creating images for tutorials so that the menu bar is similar to Linux and Windows and make it easier to include the menu in the image without have to capture the menu bar at the top of the window. --- src/Desktop/desktop.cxx | 10 ++ src/GuiQt/BrainBrowserWindow.cxx | 20 ++- src/GuiQt/BrainBrowserWindow.h | 6 + src/GuiQt/BrainBrowserWindowToolBar.cxx | 34 +++++- src/GuiQt/BrainBrowserWindowToolBar.h | 10 ++ src/GuiQt/CMakeLists.txt | 3 + src/GuiQt/MacDuplicateMenuBar.cxx | 156 ++++++++++++++++++++++++ src/GuiQt/MacDuplicateMenuBar.h | 65 ++++++++++ 8 files changed, 298 insertions(+), 6 deletions(-) create mode 100644 src/GuiQt/MacDuplicateMenuBar.cxx create mode 100644 src/GuiQt/MacDuplicateMenuBar.h diff --git a/src/Desktop/desktop.cxx b/src/Desktop/desktop.cxx index 68d5035bb..f52333c30 100644 --- a/src/Desktop/desktop.cxx +++ b/src/Desktop/desktop.cxx @@ -736,6 +736,14 @@ void printHelp(const AString& progName) cout << endl + << " -mac-menu-duplicate" << endl + << " MacOS Only - Adds menus to the top of the Browser Window " << endl + << " that duplicate the menu bar at the top of the window. " << endl + << " The menus are similar to that on Linux and Windows. " << endl + << " May be useful for creating tutorial images." << endl + << " This functionality is EXPERIMENTAL and subject to " << endl + << " removal in future versions of wb_view." << endl + << endl << " -no-splash" << endl << " disable all splash screens" << endl << endl @@ -812,6 +820,8 @@ void parseCommandLine(const AString& progName, ProgramParameters* myParams, Prog hasFatalError = true; } } + } else if (thisParam == "-mac-menu-duplicate") { + BrainBrowserWindow::setEnableMacDuplicateMenuBar(true); } else if (thisParam == "-no-splash") { myState.showSplash = false; } else if (thisParam == "-scene-load") { diff --git a/src/GuiQt/BrainBrowserWindow.cxx b/src/GuiQt/BrainBrowserWindow.cxx index 2005bf9fd..72e8a6870 100644 --- a/src/GuiQt/BrainBrowserWindow.cxx +++ b/src/GuiQt/BrainBrowserWindow.cxx @@ -242,7 +242,13 @@ m_browserWindowIndex(browserWindowIndex) createActions(); createMenus(); - + + if (s_enableMacDuplicateMenuBarFlag) { + s_enableMacDuplicateMenuBarFlag = false; + + m_toolbar->insertDuplicateMenuBar(this); + } + m_toolbar->updateToolBar(); processShowOverlayToolBox(m_overlayToolBoxAction->isChecked()); @@ -4911,4 +4917,16 @@ BrainBrowserWindow::showDataFileReadWarningsDialog() } } +/** + * Set the enabled status for enabling mac duplicate menu bar for + * the next created toolbar. + */ +void +BrainBrowserWindow::setEnableMacDuplicateMenuBar(bool status) +{ + s_enableMacDuplicateMenuBarFlag = status; +} + + + diff --git a/src/GuiQt/BrainBrowserWindow.h b/src/GuiQt/BrainBrowserWindow.h index 45d516c93..34b964654 100644 --- a/src/GuiQt/BrainBrowserWindow.h +++ b/src/GuiQt/BrainBrowserWindow.h @@ -155,6 +155,8 @@ namespace caret { void resizeDockWidgets(const QList &docks, const QList &sizes, Qt::Orientation orientation); + static void setEnableMacDuplicateMenuBar(bool status); + protected: void closeEvent(QCloseEvent* event); void keyPressEvent(QKeyEvent* event); @@ -473,6 +475,8 @@ namespace caret { bool m_keyEventProcessingFlag = false; + static bool s_enableMacDuplicateMenuBarFlag; + }; #ifdef __BRAIN_BROWSER_WINDOW_DECLARE__ std::set BrainBrowserWindow::s_brainBrowserWindows; @@ -483,6 +487,8 @@ namespace caret { bool BrainBrowserWindow::s_firstWindowFlag = true; int32_t BrainBrowserWindow::s_sceneFileFirstWindowX = -1; int32_t BrainBrowserWindow::s_sceneFileFirstWindowY = -1; + + bool BrainBrowserWindow::s_enableMacDuplicateMenuBarFlag = false; #endif // __BRAIN_BROWSER_WINDOW_DECLARE__ } diff --git a/src/GuiQt/BrainBrowserWindowToolBar.cxx b/src/GuiQt/BrainBrowserWindowToolBar.cxx index a9a524456..7b09e2aa0 100644 --- a/src/GuiQt/BrainBrowserWindowToolBar.cxx +++ b/src/GuiQt/BrainBrowserWindowToolBar.cxx @@ -99,6 +99,7 @@ #include "EventUpdateYokedWindows.h" #include "GuiManager.h" #include "LockAspectWarningDialog.h" +#include "MacDuplicateMenuBar.h" #include "Model.h" #include "ModelChart.h" #include "ModelChartTwo.h" @@ -513,11 +514,11 @@ BrainBrowserWindowToolBar::BrainBrowserWindowToolBar(const int32_t browserWindow * Arrange the tabbar and the toolbar vertically. */ QWidget* w = new QWidget(); - QVBoxLayout* layout = new QVBoxLayout(w); - WuQtUtilities::setLayoutSpacingAndMargins(layout, 1, 0); - layout->addWidget(this->tabBarWidget); - layout->addWidget(m_toolbarWidget); - layout->addWidget(this->userInputControlsWidget); + m_toolBarMainLayout = new QVBoxLayout(w); + WuQtUtilities::setLayoutSpacingAndMargins(m_toolBarMainLayout, 1, 0); + m_toolBarMainLayout->addWidget(this->tabBarWidget); + m_toolBarMainLayout->addWidget(m_toolbarWidget); + m_toolBarMainLayout->addWidget(this->userInputControlsWidget); this->addWidget(w); @@ -624,6 +625,29 @@ BrainBrowserWindowToolBar::~BrainBrowserWindowToolBar() this->isDestructionInProgress = false; } +/** + * Insert a duplicate of the application's menu at the top of the toolbar. + * This is only enabled on MacOSX. + * + * @param mainWindow + * Main window whose menus are copied. + */ +void +BrainBrowserWindowToolBar::insertDuplicateMenuBar(QMainWindow* mainWindow) +{ +#ifndef CARET_OS_MACOSX + return; +#endif + MacDuplicateMenuBar* menuBar = new MacDuplicateMenuBar(mainWindow, + this); + CaretAssert(menuBar); + + /* + * Insert at top of tool bar + */ + m_toolBarMainLayout->insertWidget(0, menuBar); +} + /** * Create a new tab. NOTE: This method only creates * a new tab, it DOES NOT add the tab to the toolbar. diff --git a/src/GuiQt/BrainBrowserWindowToolBar.h b/src/GuiQt/BrainBrowserWindowToolBar.h index ed635a07e..99d3516df 100644 --- a/src/GuiQt/BrainBrowserWindowToolBar.h +++ b/src/GuiQt/BrainBrowserWindowToolBar.h @@ -42,10 +42,12 @@ class QDoubleSpinBox; class QHBoxLayout; class QIcon; class QLabel; +class QMainWindow; class QMenu; class QRadioButton; class QSpinBox; class QToolButton; +class QVBoxLayout; namespace caret { @@ -103,11 +105,14 @@ namespace caret { int32_t getNumberOfTabs() const; + void insertDuplicateMenuBar(QMainWindow* mainWindow); + virtual SceneClass* saveToScene(const SceneAttributes* sceneAttributes, const AString& instanceName); virtual void restoreFromScene(const SceneAttributes* sceneAttributes, const SceneClass* sceneClass); + signals: void viewedModelChanged(); @@ -212,6 +217,8 @@ namespace caret { WuQWidgetObjectGroup* modeWidgetGroup; WuQWidgetObjectGroup* singleSurfaceSelectionWidgetGroup; + QVBoxLayout* m_toolBarMainLayout; + QWidget* fullToolBarWidget; QWidget* m_toolbarWidget; QHBoxLayout* toolbarWidgetLayout; @@ -471,6 +478,9 @@ namespace caret { friend class BrainBrowserWindowToolBarTabPopUpMenu; }; + +#ifdef __BRAIN_BROWSER_WINDOW_TOOLBAR_DECLARE__ +#endif // __BRAIN_BROWSER_WINDOW_TOOLBAR_DECLARE__ } #endif // __BRAIN_BROWSER_WINDOW_TOOLBAR_H__ diff --git a/src/GuiQt/CMakeLists.txt b/src/GuiQt/CMakeLists.txt index 522e13064..407d9fd1d 100644 --- a/src/GuiQt/CMakeLists.txt +++ b/src/GuiQt/CMakeLists.txt @@ -138,6 +138,7 @@ ELSE() LockAspectWarningDialog.h MacApplication.h MacDockMenu.h + MacDuplicateMenuBar.h MapSettingsChartTwoLineHistoryWidget.h MapSettingsColorBarPaletteOptionsWidget.h MapSettingsColorBarWidget.h @@ -389,6 +390,7 @@ LabelSelectionViewController.h LockAspectWarningDialog.h MacApplication.h MacDockMenu.h +MacDuplicateMenuBar.h MapSettingsChartTwoLineHistoryWidget.h MapSettingsColorBarPaletteOptionsWidget.h MapSettingsColorBarWidget.h @@ -656,6 +658,7 @@ LabelSelectionViewController.cxx LockAspectWarningDialog.cxx MacApplication.cxx MacDockMenu.cxx +MacDuplicateMenuBar.cxx MapSettingsChartTwoLineHistoryWidget.cxx MapSettingsColorBarPaletteOptionsWidget.cxx MapSettingsColorBarWidget.cxx diff --git a/src/GuiQt/MacDuplicateMenuBar.cxx b/src/GuiQt/MacDuplicateMenuBar.cxx new file mode 100644 index 000000000..d0f0b832a --- /dev/null +++ b/src/GuiQt/MacDuplicateMenuBar.cxx @@ -0,0 +1,156 @@ + +/*LICENSE_START*/ +/* + * Copyright (C) 2019 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + +#define __MAC_DUPLICATE_MENU_BAR_DECLARE__ +#include "MacDuplicateMenuBar.h" +#undef __MAC_DUPLICATE_MENU_BAR_DECLARE__ + +#include + +#include +#include +#include +#include +#include + + +#include "AString.h" +#include "CaretAssert.h" +using namespace caret; + + + +/** + * \class caret::MacDuplicateMenuBar + * \brief A duplicate of the mac menu bar placed in the main window + * \ingroup GuiQt + * + * The menu on macs is separate from the main window and placed at the + * top of the display. When creating screen images, such as for tutorials, + * it makes it difficult to include the menu bar in the image. This class + * creates a copy of the menu bar in a widget that can be added to + * the main window. + */ + +/** + * Constructor. + * + * @param mainWindow + * Main window whose menu bar is copied. + * @param parent + * Optional parent widget. + */ +MacDuplicateMenuBar::MacDuplicateMenuBar(QMainWindow* mainWindow, + QWidget* parent) +: QWidget(parent) +{ + QHBoxLayout* layout = new QHBoxLayout(this); + QMargins margins = layout->contentsMargins(); + margins.setTop(0); + margins.setBottom(0); + layout->setContentsMargins(margins); + + CaretAssert(mainWindow); + + /* + * Examine contents of menu bar and duplicate all items in it + */ + QList menuList = mainWindow->menuBar()->actions(); + QListIterator iter(menuList); + while (iter.hasNext()) { + QAction* action = iter.next(); + QMenu* menu = action->menu(); + if (menu != NULL) { + QMenu* dupMenu = duplicateMenu(menu); + if (dupMenu != NULL) { + /* + * Cannot add a menu bar to the window so each menu + * is attached to a new QToolButton + */ + QToolButton* tb = new QToolButton(); + tb->setPopupMode(QToolButton::InstantPopup); + tb->setText(menu->title()); + tb->setMenu(dupMenu); + layout->addWidget(tb); + } + } + } + layout->addStretch(); +} + +/** + * Destructor. + */ +MacDuplicateMenuBar::~MacDuplicateMenuBar() +{ +} + +/** + * Recursively duplicate the given menu. + * + * @param copyFromMenu + * Menu that is examined and copied + * @return + * Pointer to duplicated menu or NULL if failed to duplicate. + */ +QMenu* +MacDuplicateMenuBar::duplicateMenu(QMenu* copyFromMenu) +{ + const bool printFlag(false); + + if (printFlag) { + std::cout << std::endl; + std::cout << m_indentText << "Menu: " << copyFromMenu->title() << std::endl; + } + m_indentText.append(" "); + + QMenu* newMenu = new QMenu(copyFromMenu->title()); + QList actionList = copyFromMenu->actions(); + QListIterator iter(actionList); + while (iter.hasNext()) { + QAction* action = iter.next(); + QMenu* subMenu = action->menu(); + if (subMenu != NULL) { + QMenu* dupMenu = duplicateMenu(subMenu); + if (dupMenu != NULL) { + newMenu->addMenu(dupMenu); + } + } + else if (action->isSeparator()) { + if (printFlag) { + std::cout << m_indentText << "Separator" << std::endl; + } + newMenu->addSeparator(); + } + else { + if (printFlag) { + std::cout << m_indentText << "Item: " << action->text() << std::endl; + } + newMenu->addAction(action); + } + } + + m_indentText.resize(m_indentText.length() - 3); + + return newMenu; +} + + diff --git a/src/GuiQt/MacDuplicateMenuBar.h b/src/GuiQt/MacDuplicateMenuBar.h new file mode 100644 index 000000000..6ca9bb0e9 --- /dev/null +++ b/src/GuiQt/MacDuplicateMenuBar.h @@ -0,0 +1,65 @@ +#ifndef __MAC_DUPLICATE_MENU_BAR_H__ +#define __MAC_DUPLICATE_MENU_BAR_H__ + +/*LICENSE_START*/ +/* + * Copyright (C) 2019 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + + + +#include + +#include + +class QMainWindow; +class QMenu; +namespace caret { + + class MacDuplicateMenuBar : public QWidget { + + Q_OBJECT + + public: + MacDuplicateMenuBar(QMainWindow* mainWindow, + QWidget* parent = 0); + + virtual ~MacDuplicateMenuBar(); + + MacDuplicateMenuBar(const MacDuplicateMenuBar&) = delete; + + MacDuplicateMenuBar& operator=(const MacDuplicateMenuBar&) = delete; + + + // ADD_NEW_METHODS_HERE + + private: + QMenu* duplicateMenu(QMenu* menu); + + QString m_indentText; + + // ADD_NEW_MEMBERS_HERE + + }; + +#ifdef __MAC_DUPLICATE_MENU_BAR_DECLARE__ + // +#endif // __MAC_DUPLICATE_MENU_BAR_DECLARE__ + +} // namespace +#endif //__MAC_DUPLICATE_MENU_BAR_H__ -- GitLab From dc8edd8b5ec703164c2c9eab904c6ad81c4294a6 Mon Sep 17 00:00:00 2001 From: John Harwell Date: Mon, 8 Apr 2019 12:03:29 -0500 Subject: [PATCH 278/427] Volume Surface Outline Drawing: Invalidate cached outlines if the underlay volume changes. For the "scaled coordinate" in the cache, account for the voxel size to prevent adjacent slices from using the same cached outline. --- .../BrainOpenGLVolumeObliqueSliceDrawing.cxx | 3 +- src/Brain/BrainOpenGLVolumeSliceDrawing.cxx | 27 ++-- src/Brain/BrainOpenGLVolumeSliceDrawing.h | 6 +- src/Brain/VolumeSurfaceOutlineModel.cxx | 120 +++++++++++++----- src/Brain/VolumeSurfaceOutlineModel.h | 46 +++++-- .../VolumeSurfaceOutlineModelCacheKey.cxx | 42 +++++- src/Brain/VolumeSurfaceOutlineModelCacheKey.h | 5 +- 7 files changed, 193 insertions(+), 56 deletions(-) diff --git a/src/Brain/BrainOpenGLVolumeObliqueSliceDrawing.cxx b/src/Brain/BrainOpenGLVolumeObliqueSliceDrawing.cxx index 39e07c4b5..8be409e12 100644 --- a/src/Brain/BrainOpenGLVolumeObliqueSliceDrawing.cxx +++ b/src/Brain/BrainOpenGLVolumeObliqueSliceDrawing.cxx @@ -1097,7 +1097,8 @@ BrainOpenGLVolumeObliqueSliceDrawing::drawLayers(const VolumeSliceDrawingTypeEnu glPolygonOffset(0.0, 1.0); if (drawOutlineFlag) { - BrainOpenGLVolumeSliceDrawing::drawSurfaceOutline(m_modelType, + BrainOpenGLVolumeSliceDrawing::drawSurfaceOutline(m_underlayVolume, + m_modelType, sliceProjectionType, sliceViewPlane, sliceCoordinates, diff --git a/src/Brain/BrainOpenGLVolumeSliceDrawing.cxx b/src/Brain/BrainOpenGLVolumeSliceDrawing.cxx index 630de1671..f1fc4054a 100644 --- a/src/Brain/BrainOpenGLVolumeSliceDrawing.cxx +++ b/src/Brain/BrainOpenGLVolumeSliceDrawing.cxx @@ -2703,7 +2703,8 @@ BrainOpenGLVolumeSliceDrawing::drawLayers(const VolumeSliceDrawingTypeEnum::Enum glPolygonOffset(0.0, 1.0); if (drawOutlineFlag) { - drawSurfaceOutline(m_modelType, + drawSurfaceOutline(m_underlayVolume, + m_modelType, sliceProjectionType, sliceViewPlane, sliceCoordinates, @@ -2766,6 +2767,8 @@ BrainOpenGLVolumeSliceDrawing::drawLayers(const VolumeSliceDrawingTypeEnum::Enum /** * Draw surface outlines on the volume slices * + * @param underlayVolume + * The underlay volume * @param modelType * Type of model being drawn. * @param sliceProjectionType @@ -2784,7 +2787,8 @@ BrainOpenGLVolumeSliceDrawing::drawLayers(const VolumeSliceDrawingTypeEnum::Enum * If true, use a negative offset for polygon offset */ void -BrainOpenGLVolumeSliceDrawing::drawSurfaceOutline(const ModelTypeEnum::Enum modelType, +BrainOpenGLVolumeSliceDrawing::drawSurfaceOutline(const VolumeMappableInterface* underlayVolume, + const ModelTypeEnum::Enum modelType, const VolumeSliceProjectionTypeEnum::Enum sliceProjectionType, const VolumeSliceViewPlaneEnum::Enum sliceViewPlane, const float sliceXYZ[3], @@ -2809,7 +2813,8 @@ BrainOpenGLVolumeSliceDrawing::drawSurfaceOutline(const ModelTypeEnum::Enum mode } if (drawCachedFlag) { - drawSurfaceOutlineCached(modelType, + drawSurfaceOutlineCached(underlayVolume, + modelType, sliceViewPlane, sliceXYZ, plane, @@ -2829,6 +2834,8 @@ BrainOpenGLVolumeSliceDrawing::drawSurfaceOutline(const ModelTypeEnum::Enum mode /** * Draw surface outlines on the volume slices * + * @param underlayVolume + * The underlay volume * @param modelType * Type of model being drawn. * @param sliceViewPlane @@ -2845,7 +2852,8 @@ BrainOpenGLVolumeSliceDrawing::drawSurfaceOutline(const ModelTypeEnum::Enum mode * If true, use a negative offset for polygon offset */ void -BrainOpenGLVolumeSliceDrawing::drawSurfaceOutlineCached(const ModelTypeEnum::Enum modelType, +BrainOpenGLVolumeSliceDrawing::drawSurfaceOutlineCached(const VolumeMappableInterface* underlayVolume, + const ModelTypeEnum::Enum modelType, const VolumeSliceViewPlaneEnum::Enum sliceViewPlane, const float sliceXYZ[3], const Plane& plane, @@ -2899,7 +2907,8 @@ BrainOpenGLVolumeSliceDrawing::drawSurfaceOutlineCached(const ModelTypeEnum::Enu /* * Key for outline cache */ - VolumeSurfaceOutlineModelCacheKey outlineCacheKey(sliceViewPlane, + VolumeSurfaceOutlineModelCacheKey outlineCacheKey(underlayVolume, + sliceViewPlane, sliceCoordinate); /* @@ -2931,8 +2940,9 @@ BrainOpenGLVolumeSliceDrawing::drawSurfaceOutlineCached(const ModelTypeEnum::Enu } } - if (outline->getOutlineCachePrimitives(outlineCacheKey, - contourPrimitives)) { + if (outline->getOutlineCachePrimitives(underlayVolume, + outlineCacheKey, + contourPrimitives)) { /* OK, have cached primitives to draw */ } else { @@ -2970,7 +2980,8 @@ BrainOpenGLVolumeSliceDrawing::drawSurfaceOutlineCached(const ModelTypeEnum::Enu CaretLogSevere(errorMessage); } - outline->setOutlineCachePrimitives(outlineCacheKey, + outline->setOutlineCachePrimitives(underlayVolume, + outlineCacheKey, contourPrimitives); } } diff --git a/src/Brain/BrainOpenGLVolumeSliceDrawing.h b/src/Brain/BrainOpenGLVolumeSliceDrawing.h index df8196c0f..99c8373dd 100644 --- a/src/Brain/BrainOpenGLVolumeSliceDrawing.h +++ b/src/Brain/BrainOpenGLVolumeSliceDrawing.h @@ -251,7 +251,8 @@ namespace caret { const Plane& slicePlane, const float sliceCoordinates[3]); - static void drawSurfaceOutline(const ModelTypeEnum::Enum modelType, + static void drawSurfaceOutline(const VolumeMappableInterface* underlayVolume, + const ModelTypeEnum::Enum modelType, const VolumeSliceProjectionTypeEnum::Enum sliceProjectionType, const VolumeSliceViewPlaneEnum::Enum sliceViewPlane, const float sliceXYZ[3], @@ -260,7 +261,8 @@ namespace caret { BrainOpenGLFixedPipeline* fixedPipelineDrawing, const bool useNegativePolygonOffsetFlag); - static void drawSurfaceOutlineCached(const ModelTypeEnum::Enum modelType, + static void drawSurfaceOutlineCached(const VolumeMappableInterface* underlayVolume, + const ModelTypeEnum::Enum modelType, const VolumeSliceViewPlaneEnum::Enum sliceViewPlane, const float sliceXYZ[3], const Plane& plane, diff --git a/src/Brain/VolumeSurfaceOutlineModel.cxx b/src/Brain/VolumeSurfaceOutlineModel.cxx index d72bd9e73..a09b21b58 100644 --- a/src/Brain/VolumeSurfaceOutlineModel.cxx +++ b/src/Brain/VolumeSurfaceOutlineModel.cxx @@ -29,6 +29,7 @@ #include "SceneClassAssistant.h" #include "SurfaceSelectionModel.h" #include "SurfaceTypeEnum.h" +#include "VolumeMappableInterface.h" #include "VolumeSurfaceOutlineColorOrTabModel.h" #include "VolumeSurfaceOutlineModelCacheValue.h" @@ -312,7 +313,8 @@ VolumeSurfaceOutlineModel::restoreFromScene(const SceneAttributes* sceneAttribut * Input containing the primitives */ void -VolumeSurfaceOutlineModel::setOutlineCachePrimitives(const VolumeSurfaceOutlineModelCacheKey& key, +VolumeSurfaceOutlineModel::setOutlineCachePrimitives(const VolumeMappableInterface* underlayVolume, + const VolumeSurfaceOutlineModelCacheKey& key, const std::vector& primitives) { auto iter = m_outlineCache.find(key); @@ -322,9 +324,8 @@ VolumeSurfaceOutlineModel::setOutlineCachePrimitives(const VolumeSurfaceOutlineM } if (m_outlineCache.empty()) { - m_outlineCacheSurface = getSurface(); - m_outlineCacheThicknessPercentageViewportHeight = m_thicknessPercentageViewportHeight; - m_outlineCacheColorItem.reset(new VolumeSurfaceOutlineColorOrTabModel::Item(*m_colorOrTabModel->getSelectedItem())); + m_outlineCacheInfo.update(this, + underlayVolume); } if (debugFlag) { @@ -339,6 +340,8 @@ VolumeSurfaceOutlineModel::setOutlineCachePrimitives(const VolumeSurfaceOutlineM /** * Get the outline primitives for the given cache key * + * @param underlayVolume + * The underlay volume * @param key * Key into the outline cache identifying axis and slice * @param primitivesOut @@ -347,11 +350,15 @@ VolumeSurfaceOutlineModel::setOutlineCachePrimitives(const VolumeSurfaceOutlineM * Truie if outline primitives valid, else false. */ bool -VolumeSurfaceOutlineModel::getOutlineCachePrimitives(const VolumeSurfaceOutlineModelCacheKey& key, +VolumeSurfaceOutlineModel::getOutlineCachePrimitives(const VolumeMappableInterface* underlayVolume, + const VolumeSurfaceOutlineModelCacheKey& key, std::vector& primitivesOut) { - validateOutlineCache(); - + if ( ! m_outlineCacheInfo.isValid(this, + underlayVolume)) { + clearOutlineCache(); + } + auto iter = m_outlineCache.find(key); if (iter != m_outlineCache.end()) { primitivesOut = iter->second->getGraphicsPrimitives(); @@ -365,44 +372,95 @@ VolumeSurfaceOutlineModel::getOutlineCachePrimitives(const VolumeSurfaceOutlineM } /** - * Validate the outline cache and if not valid, clear outline cache + * Clear the outline cache */ void -VolumeSurfaceOutlineModel::validateOutlineCache() +VolumeSurfaceOutlineModel::clearOutlineCache() { - /* - * Test for cache validity - */ - if (m_outlineCacheSurface == NULL) { - return; + if (debugFlag) { + std::cout << "Invalidating surface outline cache" << std::endl; } + m_outlineCacheInfo.clear(); - /* - * Has anything changed that invalidates the outline cache ? - */ - if ((m_outlineCacheSurface != getSurface()) - || (m_outlineCacheThicknessPercentageViewportHeight != m_thicknessPercentageViewportHeight) - || ( ! m_outlineCacheColorItem->equals(*m_colorOrTabModel->getSelectedItem()))) { - clearOutlineCache(); + for (auto iter : m_outlineCache) { + delete iter.second; } + m_outlineCache.clear(); +} + +/* ==========================================================================================*/ + +/** + * Constructor for outline cache + */ +VolumeSurfaceOutlineModel::OutlineCacheInfo::OutlineCacheInfo() +{ + clear(); +} + +/** + * Destructor + */ +VolumeSurfaceOutlineModel::OutlineCacheInfo::~OutlineCacheInfo() +{ + clear(); } /** * Clear the outline cache */ void -VolumeSurfaceOutlineModel::clearOutlineCache() +VolumeSurfaceOutlineModel::OutlineCacheInfo::clear() { - if (debugFlag) { - std::cout << "Invalidating surface outline cache" << std::endl; + m_surface = NULL; + m_thicknessPercentageViewportHeight = -1.0; + m_colorItem.reset(); +} + +/** + * Is the outline cache valid? + * + * @param surfaceOutlineModel + * The parent surface outline model + * @param underlayVolume + * The underlay volume + * @return + * True if cache is valid, else false + */ +bool +VolumeSurfaceOutlineModel::OutlineCacheInfo::isValid(VolumeSurfaceOutlineModel* surfaceOutlineModel, + const VolumeMappableInterface* underlayVolume) +{ + bool validFlag(false); + if (m_surface != NULL) { + if ((m_surface == surfaceOutlineModel->getSurface()) + && (m_underlayVolume == underlayVolume) + && (m_thicknessPercentageViewportHeight == surfaceOutlineModel->getThicknessPercentageViewportHeight())) { + if (m_colorItem != NULL) { + if (m_colorItem->equals(*(surfaceOutlineModel->getColorOrTabModel()->getSelectedItem()))) { + validFlag = true; + } + } + } } - m_outlineCacheSurface = NULL; - m_outlineCacheThicknessPercentageViewportHeight = -1.0; - m_outlineCacheColorItem.reset(); - for (auto iter : m_outlineCache) { - delete iter.second; - } - m_outlineCache.clear(); + return validFlag; } +/** + * Update the cache info from the parent surface outline model + * + * @param surfaceOutlineModel + * The surface outline model + * @param underlayVolume + * The underlay volume + */ +void +VolumeSurfaceOutlineModel::OutlineCacheInfo::update(VolumeSurfaceOutlineModel* surfaceOutlineModel, + const VolumeMappableInterface* underlayVolume) +{ + m_underlayVolume = underlayVolume; + m_surface = surfaceOutlineModel->getSurface(); + m_thicknessPercentageViewportHeight = surfaceOutlineModel->getThicknessPercentageViewportHeight(); + m_colorItem.reset(new VolumeSurfaceOutlineColorOrTabModel::Item(*(surfaceOutlineModel->getColorOrTabModel()->getSelectedItem()))); +} diff --git a/src/Brain/VolumeSurfaceOutlineModel.h b/src/Brain/VolumeSurfaceOutlineModel.h index 85714b9dc..3150529d5 100644 --- a/src/Brain/VolumeSurfaceOutlineModel.h +++ b/src/Brain/VolumeSurfaceOutlineModel.h @@ -36,6 +36,7 @@ namespace caret { class SceneAttributes; class SceneClassAssistant; class SurfaceSelectionModel; + class VolumeMappableInterface; class VolumeSurfaceOutlineModelCacheValue; class VolumeSurfaceOutlineModel : public CaretObject, public EventListenerInterface, public SceneableInterface { @@ -71,10 +72,12 @@ namespace caret { const VolumeSurfaceOutlineColorOrTabModel* getColorOrTabModel() const; - void setOutlineCachePrimitives(const VolumeSurfaceOutlineModelCacheKey& key, + void setOutlineCachePrimitives(const VolumeMappableInterface* underlayVolume, + const VolumeSurfaceOutlineModelCacheKey& key, const std::vector& primitives); - bool getOutlineCachePrimitives(const VolumeSurfaceOutlineModelCacheKey& key, + bool getOutlineCachePrimitives(const VolumeMappableInterface* underlayVolume, + const VolumeSurfaceOutlineModelCacheKey& key, std::vector& primitivesOut); virtual SceneClass* saveToScene(const SceneAttributes* sceneAttributes, @@ -95,8 +98,6 @@ namespace caret { virtual AString toString() const; private: - void validateOutlineCache(); - void clearOutlineCache(); bool m_displayed; @@ -111,14 +112,35 @@ namespace caret { SceneClassAssistant* m_sceneAssistant; - /** Thickness when first outline is added to outline cache */ - float m_outlineCacheThicknessPercentageViewportHeight = -1.0; - - /** Surface when first outline is added to outline cache */ - Surface* m_outlineCacheSurface = NULL; - - /** Color Or Tab selection item when first outline is added to cache */ - std::unique_ptr m_outlineCacheColorItem; + class OutlineCacheInfo { + public: + OutlineCacheInfo(); + + ~OutlineCacheInfo(); + + void clear(); + + bool isValid(VolumeSurfaceOutlineModel* surfaceOutlineModel, + const VolumeMappableInterface* underlayVolume); + + void update(VolumeSurfaceOutlineModel* surfaceOutlineModel, + const VolumeMappableInterface* underlayVolume); + + /** The underlay volume */ + const VolumeMappableInterface* m_underlayVolume; + + /** Thickness when first outline is added to outline cache */ + float m_thicknessPercentageViewportHeight = -1.0; + + /** Surface when first outline is added to outline cache */ + Surface* m_surface = NULL; + + /** Color Or Tab selection item when first outline is added to cache */ + std::unique_ptr m_colorItem; + }; + + /** info about outline cache that tracks validity of cache */ + OutlineCacheInfo m_outlineCacheInfo; /** Cache for volume surface outlines */ std::map m_outlineCache; diff --git a/src/Brain/VolumeSurfaceOutlineModelCacheKey.cxx b/src/Brain/VolumeSurfaceOutlineModelCacheKey.cxx index ddce02bf5..d47f8a5b3 100644 --- a/src/Brain/VolumeSurfaceOutlineModelCacheKey.cxx +++ b/src/Brain/VolumeSurfaceOutlineModelCacheKey.cxx @@ -26,6 +26,8 @@ #include #include "CaretAssert.h" +#include "VolumeMappableInterface.h" + using namespace caret; @@ -39,12 +41,50 @@ using namespace caret; /** * Constructor. */ -VolumeSurfaceOutlineModelCacheKey::VolumeSurfaceOutlineModelCacheKey(const VolumeSliceViewPlaneEnum::Enum slicePlane, +VolumeSurfaceOutlineModelCacheKey::VolumeSurfaceOutlineModelCacheKey(const VolumeMappableInterface* underlayVolume, + const VolumeSliceViewPlaneEnum::Enum slicePlane, const float sliceCoordinate) : CaretObject(), m_slicePlane(slicePlane), m_sliceCoordinateScaled(static_cast(std::round(sliceCoordinate * 10.0))) { + CaretAssert(underlayVolume); + if (underlayVolume != NULL) { + float voxelSizesMM[3]; + underlayVolume->getVoxelSpacing(voxelSizesMM[0], + voxelSizesMM[1], + voxelSizesMM[2]); + + float voxelSize(0.0); + switch (slicePlane) { + case VolumeSliceViewPlaneEnum::ALL: + voxelSize = voxelSizesMM[2]; + break; + case VolumeSliceViewPlaneEnum::AXIAL: + voxelSize = voxelSizesMM[2]; + break; + case VolumeSliceViewPlaneEnum::CORONAL: + voxelSize = voxelSizesMM[1]; + break; + case VolumeSliceViewPlaneEnum::PARASAGITTAL: + voxelSize = voxelSizesMM[0]; + break; + } + + if (voxelSize > 0.0) { + /* + * The coordinate for the slice is stored as an integer since + * since integer comparison is precise oppposed to a float comparison + * that requires some sort of tolerance. + * + * (1.0 / voxelSize) is used so that the scale factor will + * be larger for small voxels and prevent an outline from + * being used by adjacent volume slices + */ + const float scaleFactor = 10.0 * (1.0 / voxelSize); + m_sliceCoordinateScaled = static_cast(std::round(sliceCoordinate * scaleFactor)); + } + } } /** diff --git a/src/Brain/VolumeSurfaceOutlineModelCacheKey.h b/src/Brain/VolumeSurfaceOutlineModelCacheKey.h index 7484327ce..1785ab2b6 100644 --- a/src/Brain/VolumeSurfaceOutlineModelCacheKey.h +++ b/src/Brain/VolumeSurfaceOutlineModelCacheKey.h @@ -31,10 +31,13 @@ namespace caret { + class VolumeMappableInterface; + class VolumeSurfaceOutlineModelCacheKey : public CaretObject { public: - VolumeSurfaceOutlineModelCacheKey(const VolumeSliceViewPlaneEnum::Enum slicePlane, + VolumeSurfaceOutlineModelCacheKey(const VolumeMappableInterface* underlayVolume, + const VolumeSliceViewPlaneEnum::Enum slicePlane, const float sliceCoordinate); virtual ~VolumeSurfaceOutlineModelCacheKey(); -- GitLab From 7024ab4d2280620e2825d339a23a352276941fcf Mon Sep 17 00:00:00 2001 From: John Harwell Date: Mon, 8 Apr 2019 14:56:13 -0500 Subject: [PATCH 279/427] Removed DeveloperFlags related to Scene File (for addition of macros) and related testing as new Stream Reader and Writer for Scene File is functioning correctly. --- src/Common/DeveloperFlagsEnum.cxx | 23 +----- src/Common/DeveloperFlagsEnum.h | 6 +- src/Files/SceneFile.cxx | 131 +++--------------------------- src/GuiQt/BrainBrowserWindow.cxx | 28 ++----- 4 files changed, 22 insertions(+), 166 deletions(-) diff --git a/src/Common/DeveloperFlagsEnum.cxx b/src/Common/DeveloperFlagsEnum.cxx index 80e9eb80d..e6652c823 100644 --- a/src/Common/DeveloperFlagsEnum.cxx +++ b/src/Common/DeveloperFlagsEnum.cxx @@ -126,33 +126,12 @@ DeveloperFlagsEnum::initialize() false)); checkableItems.push_back(DeveloperFlagsEnum(DEVELOPER_FLAG_NEW_VOLUME_SURFACE_OUTLINE, "DEVELOPER_FLAG_NEW_VOLUME_SURFACE_OUTLINE", - "New (faster) Volume Surface Outline", + "Faster Volume Surface Outline", CheckableEnum::YES, true)); - checkableItems.push_back(DeveloperFlagsEnum(DEVELOPER_FLAG_NEW_SCENE_FILE_READ_WRITE, - "DEVELOPER_FLAG_NEW_SCENE_FILE_READ_WRITE", - "Use New Scene File Reader and Writer", - CheckableEnum::YES, - true)); - std::vector notCheckableItems; - notCheckableItems.push_back(DeveloperFlagsEnum(DEVELOPER_FLAG_TEST_SCENE_FILE_READ_WRITE, - "DEVELOPER_FLAG_TEST_SCENE_FILE_READ_WRITE", - "Test Scene File Xml Stream Reading/Writing...", - CheckableEnum::NO, - false)); - notCheckableItems.push_back(DeveloperFlagsEnum(DEVELOPER_FLAG_TIME_SCENE_FILE_READ_WRITE, - "DEVELOPER_FLAG_TIME_SCENE_FILE_READ_WRITE", - "Time Scene File Xml Stream Timing...", - CheckableEnum::NO, - false)); - notCheckableItems.push_back(DeveloperFlagsEnum(DEVELOPER_FLAG_TEST_ALL_SCENE_FILES_IN_DIRECTORY, - "DEVELOPER_FLAG_TEST_ALL_SCENE_FILES_IN_DIRECTORY", - "Test Xml Stream Read/Write for All Scene Files in Directory...", - CheckableEnum::NO, - false)); enumData.insert(enumData.end(), checkableItems.begin(), checkableItems.end()); diff --git a/src/Common/DeveloperFlagsEnum.h b/src/Common/DeveloperFlagsEnum.h index 057f32be2..763eec287 100644 --- a/src/Common/DeveloperFlagsEnum.h +++ b/src/Common/DeveloperFlagsEnum.h @@ -37,11 +37,7 @@ public: enum Enum { DEVELOPER_FLAG_UNUSED, DEVELOPER_FLAG_FLIP_PALETTE_NOT_DATA, - DEVELOPER_FLAG_NEW_VOLUME_SURFACE_OUTLINE, - DEVELOPER_FLAG_NEW_SCENE_FILE_READ_WRITE, - DEVELOPER_FLAG_TEST_SCENE_FILE_READ_WRITE, - DEVELOPER_FLAG_TIME_SCENE_FILE_READ_WRITE, - DEVELOPER_FLAG_TEST_ALL_SCENE_FILES_IN_DIRECTORY + DEVELOPER_FLAG_NEW_VOLUME_SURFACE_OUTLINE }; ~DeveloperFlagsEnum(); diff --git a/src/Files/SceneFile.cxx b/src/Files/SceneFile.cxx index 5dbe64a08..cafd92062 100644 --- a/src/Files/SceneFile.cxx +++ b/src/Files/SceneFile.cxx @@ -608,7 +608,12 @@ SceneFile::readFile(const AString& filenameIn) this->setFileName(filename); - if (DeveloperFlagsEnum::isFlag(DeveloperFlagsEnum::DEVELOPER_FLAG_NEW_SCENE_FILE_READ_WRITE)) { + /* + * Stream reader is newer and supports macro in scene file. + * Stream reader is also faster than sax reader. + */ + const bool useStreamReaderFlag(true); + if (useStreamReaderFlag) { try { SceneFileXmlStreamReader streamReader; streamReader.readFile(filename, @@ -774,130 +779,18 @@ SceneFile::writeFile(const AString& filename) this->setFileName(filename); + try { - if (DeveloperFlagsEnum::isFlag(DeveloperFlagsEnum::DEVELOPER_FLAG_NEW_SCENE_FILE_READ_WRITE)) { + /* + * Stream Writer is newer and supports macros in scene file + */ + const bool useStreamWriterFlag(true); + if (useStreamWriterFlag) { SceneFileXmlStreamWriter xmlStreamWriter; xmlStreamWriter.writeFile(this); } else { writeFileSaxWriter(filename); - -// /* -// * This writes an old file format that does not support macros -// */ -// const AString versionString = AString::number(getSceneFileVersionBeforeMacros()); -// -// // -// // Open the file -// // -// FileAdapter file; -// AString errorMessage; -// QTextStream* textStream = file.openQTextStreamForWritingFile(this->getFileName(), -// errorMessage); -// if (textStream == NULL) { -// throw DataFileException(filename, -// errorMessage); -// } -// -// // -// // Create the xml writer -// // -// XmlWriter xmlWriter(*textStream); -// -// // -// // Write header info -// // -// xmlWriter.writeStartDocument("1.0"); -// -// // -// // Write root element -// // -// XmlAttributes attributes; -// -// //attributes.addAttribute("xmlns:xsi", -// // "http://www.w3.org/2001/XMLSchema-instance"); -// //attributes.addAttribute("xsi:noNamespaceSchemaLocation", -// // "http://brainvis.wustl.edu/caret6/xml_schemas/GIFTI_Caret.xsd"); -// attributes.addAttribute(SceneFile::XML_ATTRIBUTE_VERSION, -// versionString); -// xmlWriter.writeStartElement(SceneFile::XML_TAG_SCENE_FILE, -// attributes); -// -// // -// // Write Metadata -// // -// if (m_metadata != NULL) { -// m_metadata->writeAsXML(xmlWriter); -// } -// -// const int32_t numScenes = this->getNumberOfScenes(); -// -// /* -// * Write the scene info directory -// */ -// xmlWriter.writeStartElement(SceneFile::XML_TAG_SCENE_INFO_DIRECTORY_TAG); -// xmlWriter.writeElementCData(SceneXmlElements::SCENE_INFO_BALSA_STUDY_ID_TAG, -// getBalsaStudyID()); -// xmlWriter.writeElementCData(SceneXmlElements::SCENE_INFO_BALSA_STUDY_TITLE_TAG, -// getBalsaStudyTitle()); -// switch (getBasePathType()) { -// case SceneFileBasePathTypeEnum::AUTOMATIC: -// xmlWriter.writeElementCData(SceneXmlElements::SCENE_INFO_BALSA_BASE_DIRECTORY_TAG, -// ""); -// break; -// case SceneFileBasePathTypeEnum::CUSTOM: -// { -// /* -// * Write base path as a path RELATIVE to the scene file -// * but only when base path type is CUSTOM -// * Note: we do not use FileInformation::getCanonicalFilePath() -// * because it returns an empty string if the file DOES NOT exist -// * and this may occur since the file may be new and has not -// * been closed. -// */ -// if ( ! getBalsaCustomBaseDirectory().isEmpty()) { -// const AString baseDirAbsPath = FileInformation(getBalsaCustomBaseDirectory()).getAbsoluteFilePath(); -// const AString sceneFileAbsPath = FileInformation(filename).getAbsoluteFilePath(); -// ScenePathName basePathName("basePathName", -// baseDirAbsPath); -// -// const AString relativeBasePath = basePathName.getRelativePathToSceneFile(sceneFileAbsPath); -// -// //std::cout << "baseDirAbsPath: " << baseDirAbsPath << std::endl; -// //std::cout << "sceneFileAbsPath: " << sceneFileAbsPath << std::endl; -// //std::cout << "relativeTempFileName: " << relativeBasePath << std::endl; -// -// xmlWriter.writeElementCData(SceneXmlElements::SCENE_INFO_BALSA_BASE_DIRECTORY_TAG, -// relativeBasePath); -// } -// } -// break; -// } -// xmlWriter.writeElementCData(SceneXmlElements::SCENE_INFO_BALSA_EXTRACT_TO_DIRECTORY_TAG, -// getBalsaExtractToDirectoryName()); -// xmlWriter.writeElementCData(SceneXmlElements::SCENE_INFO_BASE_PATH_TYPE, -// SceneFileBasePathTypeEnum::toName(getBasePathType())); -// -// for (int32_t i = 0; i < numScenes; i++) { -// m_scenes[i]->getSceneInfo()->writeSceneInfo(xmlWriter, -// i); -// } -// xmlWriter.writeEndElement(); -// -// // -// // Write scenes -// // -// SceneWriterXml sceneWriter(xmlWriter, -// this->getFileName()); -// for (int32_t i = 0; i < numScenes; i++) { -// sceneWriter.writeScene(*m_scenes[i], -// i); -// } -// -// xmlWriter.writeEndElement(); -// xmlWriter.writeEndDocument(); -// -// file.close(); } this->clearModified(); diff --git a/src/GuiQt/BrainBrowserWindow.cxx b/src/GuiQt/BrainBrowserWindow.cxx index 72e8a6870..5ac20cfbc 100644 --- a/src/GuiQt/BrainBrowserWindow.cxx +++ b/src/GuiQt/BrainBrowserWindow.cxx @@ -1724,26 +1724,14 @@ BrainBrowserWindow::developerMenuFlagTriggered(QAction* action) DeveloperFlagsEnum::setFlag(enumValue, action->isChecked()); - if (enumValue == DeveloperFlagsEnum::DEVELOPER_FLAG_TEST_SCENE_FILE_READ_WRITE) { - QString filename = CaretFileDialog::getOpenFileNameDialog(DataFileTypeEnum::SCENE); - if ( ! filename.isEmpty()) { - SceneFileXmlStreamFormatTester::testReadingAndWriting(filename, - true); - } - } - else if (enumValue == DeveloperFlagsEnum::DEVELOPER_FLAG_TIME_SCENE_FILE_READ_WRITE) { - QString filename = CaretFileDialog::getOpenFileNameDialog(DataFileTypeEnum::SCENE); - if ( ! filename.isEmpty()) { - SceneFileXmlStreamFormatTester::timeReadingAndWriting(filename); - } - } - else if (enumValue == DeveloperFlagsEnum::DEVELOPER_FLAG_TEST_ALL_SCENE_FILES_IN_DIRECTORY) { - QString dirName = CaretFileDialog::getExistingDirectoryDialog(); - if ( ! dirName.isEmpty()) { - SceneFileXmlStreamFormatTester::testReadingAndWritingInDirectory(dirName, - false); - } - } + /* + * If a developer flag is "not checkable" and should call a function + * test for the flag here and call the function + * + * if (enumValue == DeveloperFlagsEnum::) { + * someFunction(); + * } + */ /* * Update graphics and GUI -- GitLab From ad0d3ebd826c7cd0685371d625b0c83a43d63ab4 Mon Sep 17 00:00:00 2001 From: John Harwell Date: Tue, 9 Apr 2019 12:27:31 -0500 Subject: [PATCH 280/427] Volume Surface Outline Drawing: Added cached volume surface outline drawing for oblique volume slice drawing. --- src/Brain/BrainOpenGLVolumeSliceDrawing.cxx | 17 ++ src/Brain/BrainOpenGLVolumeSliceDrawing.h | 1 + src/Brain/VolumeSurfaceOutlineModel.cxx | 4 +- .../VolumeSurfaceOutlineModelCacheKey.cxx | 225 +++++++++++++----- src/Brain/VolumeSurfaceOutlineModelCacheKey.h | 28 ++- 5 files changed, 211 insertions(+), 64 deletions(-) diff --git a/src/Brain/BrainOpenGLVolumeSliceDrawing.cxx b/src/Brain/BrainOpenGLVolumeSliceDrawing.cxx index f1fc4054a..27973e2ad 100644 --- a/src/Brain/BrainOpenGLVolumeSliceDrawing.cxx +++ b/src/Brain/BrainOpenGLVolumeSliceDrawing.cxx @@ -2804,6 +2804,9 @@ BrainOpenGLVolumeSliceDrawing::drawSurfaceOutline(const VolumeMappableInterface* bool drawCachedFlag(false); switch (sliceProjectionType) { case VolumeSliceProjectionTypeEnum::VOLUME_SLICE_PROJECTION_OBLIQUE: + if (DeveloperFlagsEnum::isFlag(DeveloperFlagsEnum::DEVELOPER_FLAG_NEW_VOLUME_SURFACE_OUTLINE)) { + drawCachedFlag = true; + } break; case VolumeSliceProjectionTypeEnum::VOLUME_SLICE_PROJECTION_ORTHOGONAL: if (DeveloperFlagsEnum::isFlag(DeveloperFlagsEnum::DEVELOPER_FLAG_NEW_VOLUME_SURFACE_OUTLINE)) { @@ -2815,6 +2818,7 @@ BrainOpenGLVolumeSliceDrawing::drawSurfaceOutline(const VolumeMappableInterface* if (drawCachedFlag) { drawSurfaceOutlineCached(underlayVolume, modelType, + sliceProjectionType, sliceViewPlane, sliceXYZ, plane, @@ -2838,6 +2842,10 @@ BrainOpenGLVolumeSliceDrawing::drawSurfaceOutline(const VolumeMappableInterface* * The underlay volume * @param modelType * Type of model being drawn. + * @param sliceProjectionType + Type of slice projection + * @param sliceProjectionType + * Type of slice projection * @param sliceViewPlane * Slice view plane (axial, coronal, parasagittal) * @param sliceXYZ @@ -2854,6 +2862,7 @@ BrainOpenGLVolumeSliceDrawing::drawSurfaceOutline(const VolumeMappableInterface* void BrainOpenGLVolumeSliceDrawing::drawSurfaceOutlineCached(const VolumeMappableInterface* underlayVolume, const ModelTypeEnum::Enum modelType, + const VolumeSliceProjectionTypeEnum::Enum sliceProjectionType, const VolumeSliceViewPlaneEnum::Enum sliceViewPlane, const float sliceXYZ[3], const Plane& plane, @@ -2910,6 +2919,14 @@ BrainOpenGLVolumeSliceDrawing::drawSurfaceOutlineCached(const VolumeMappableInte VolumeSurfaceOutlineModelCacheKey outlineCacheKey(underlayVolume, sliceViewPlane, sliceCoordinate); + switch (sliceProjectionType) { + case VolumeSliceProjectionTypeEnum::VOLUME_SLICE_PROJECTION_OBLIQUE: + outlineCacheKey = VolumeSurfaceOutlineModelCacheKey(underlayVolume, + plane); + break; + case VolumeSliceProjectionTypeEnum::VOLUME_SLICE_PROJECTION_ORTHOGONAL: + break; + } /* * Process each surface outline diff --git a/src/Brain/BrainOpenGLVolumeSliceDrawing.h b/src/Brain/BrainOpenGLVolumeSliceDrawing.h index 99c8373dd..0881142b4 100644 --- a/src/Brain/BrainOpenGLVolumeSliceDrawing.h +++ b/src/Brain/BrainOpenGLVolumeSliceDrawing.h @@ -263,6 +263,7 @@ namespace caret { static void drawSurfaceOutlineCached(const VolumeMappableInterface* underlayVolume, const ModelTypeEnum::Enum modelType, + const VolumeSliceProjectionTypeEnum::Enum sliceProjectionType, const VolumeSliceViewPlaneEnum::Enum sliceViewPlane, const float sliceXYZ[3], const Plane& plane, diff --git a/src/Brain/VolumeSurfaceOutlineModel.cxx b/src/Brain/VolumeSurfaceOutlineModel.cxx index a09b21b58..b5956312c 100644 --- a/src/Brain/VolumeSurfaceOutlineModel.cxx +++ b/src/Brain/VolumeSurfaceOutlineModel.cxx @@ -378,7 +378,9 @@ void VolumeSurfaceOutlineModel::clearOutlineCache() { if (debugFlag) { - std::cout << "Invalidating surface outline cache" << std::endl; + if ( ! m_outlineCache.empty()) { + std::cout << "Invalidating non-empty surface outline cache" << std::endl; + } } m_outlineCacheInfo.clear(); diff --git a/src/Brain/VolumeSurfaceOutlineModelCacheKey.cxx b/src/Brain/VolumeSurfaceOutlineModelCacheKey.cxx index d47f8a5b3..83d98bb57 100644 --- a/src/Brain/VolumeSurfaceOutlineModelCacheKey.cxx +++ b/src/Brain/VolumeSurfaceOutlineModelCacheKey.cxx @@ -26,6 +26,7 @@ #include #include "CaretAssert.h" +#include "Plane.h" #include "VolumeMappableInterface.h" using namespace caret; @@ -40,37 +41,108 @@ using namespace caret; /** * Constructor. + * + * @param underlayVolume + * The underlay volume + * @param sliceViewPlane + * The orthogonal slice view plane + * @param sliceCoordinate + * Coordinate of slice in orthogonal view plane */ VolumeSurfaceOutlineModelCacheKey::VolumeSurfaceOutlineModelCacheKey(const VolumeMappableInterface* underlayVolume, - const VolumeSliceViewPlaneEnum::Enum slicePlane, + const VolumeSliceViewPlaneEnum::Enum sliceViewPlane, const float sliceCoordinate) : CaretObject(), -m_slicePlane(slicePlane), -m_sliceCoordinateScaled(static_cast(std::round(sliceCoordinate * 10.0))) +m_mode(Mode::SLICE_VIEW_PLANE), +m_sliceViewPlane(sliceViewPlane) +{ + const float scaleFactor = computeScaleFactor(underlayVolume); + m_sliceCoordinateScaled = static_cast(sliceCoordinate * scaleFactor); + +// CaretAssert(underlayVolume); +// if (underlayVolume != NULL) { +// float voxelSizesMM[3]; +// underlayVolume->getVoxelSpacing(voxelSizesMM[0], +// voxelSizesMM[1], +// voxelSizesMM[2]); +// +// float voxelSize(0.0); +// switch (sliceViewPlane) { +// case VolumeSliceViewPlaneEnum::ALL: +// voxelSize = voxelSizesMM[2]; +// break; +// case VolumeSliceViewPlaneEnum::AXIAL: +// voxelSize = voxelSizesMM[2]; +// break; +// case VolumeSliceViewPlaneEnum::CORONAL: +// voxelSize = voxelSizesMM[1]; +// break; +// case VolumeSliceViewPlaneEnum::PARASAGITTAL: +// voxelSize = voxelSizesMM[0]; +// break; +// } +// +// if (voxelSize > 0.0) { +// /* +// * The coordinate for the slice is stored as an integer since +// * since integer comparison is precise oppposed to a float comparison +// * that requires some sort of tolerance. +// * +// * (1.0 / voxelSize) is used so that the scale factor will +// * be larger for small voxels and prevent an outline from +// * being used by adjacent volume slices +// */ +// const float scaleFactor = 10.0 * (1.0 / voxelSize); +// m_sliceCoordinateScaled = static_cast(std::round(sliceCoordinate * scaleFactor)); +// } +// } +} + +/** + * Constructor. + * + * @param underlayVolume + * The underlay volume + * @param sliceViewPlane + * The orthogonal slice view plane + * @param sliceCoordinate + * Coordinate of slice in orthogonal view plane + */ +VolumeSurfaceOutlineModelCacheKey::VolumeSurfaceOutlineModelCacheKey(const VolumeMappableInterface* underlayVolume, + const Plane& plane) +: m_mode(Mode::PLANE_EQUATION) { CaretAssert(underlayVolume); + if (underlayVolume != NULL) { + const float scaleFactor = computeScaleFactor(underlayVolume); + double a, b, c, d; + plane.getPlane(a, b, c, d); + m_planeEquationScaled[0] = scaleFactor * a; + m_planeEquationScaled[1] = scaleFactor * b; + m_planeEquationScaled[2] = scaleFactor * c; + m_planeEquationScaled[3] = scaleFactor * d; + } +} + +/** + * Compute the scale that is used to scale float values into integers + * + * @param underlayVolume + * The underlay volume whose voxel sizes are used to determine the scale factor + * @return The scale factor + */ +float +VolumeSurfaceOutlineModelCacheKey::computeScaleFactor(const VolumeMappableInterface* underlayVolume) const +{ + float scaleFactor(10.0); + if (underlayVolume != NULL) { float voxelSizesMM[3]; underlayVolume->getVoxelSpacing(voxelSizesMM[0], voxelSizesMM[1], voxelSizesMM[2]); - - float voxelSize(0.0); - switch (slicePlane) { - case VolumeSliceViewPlaneEnum::ALL: - voxelSize = voxelSizesMM[2]; - break; - case VolumeSliceViewPlaneEnum::AXIAL: - voxelSize = voxelSizesMM[2]; - break; - case VolumeSliceViewPlaneEnum::CORONAL: - voxelSize = voxelSizesMM[1]; - break; - case VolumeSliceViewPlaneEnum::PARASAGITTAL: - voxelSize = voxelSizesMM[0]; - break; - } - + const float voxelSize = std::min(std::min(voxelSizesMM[0], voxelSizesMM[1]), + voxelSizesMM[2]); if (voxelSize > 0.0) { /* * The coordinate for the slice is stored as an integer since @@ -81,10 +153,11 @@ m_sliceCoordinateScaled(static_cast(std::round(sliceCoordinate * 10.0)) * be larger for small voxels and prevent an outline from * being used by adjacent volume slices */ - const float scaleFactor = 10.0 * (1.0 / voxelSize); - m_sliceCoordinateScaled = static_cast(std::round(sliceCoordinate * scaleFactor)); + scaleFactor = 10.0 * (1.0 / voxelSize); } } + + return scaleFactor; } /** @@ -130,32 +203,34 @@ VolumeSurfaceOutlineModelCacheKey::operator=(const VolumeSurfaceOutlineModelCach void VolumeSurfaceOutlineModelCacheKey::copyHelperVolumeSurfaceOutlineModelCacheKey(const VolumeSurfaceOutlineModelCacheKey& obj) { - m_slicePlane = obj.m_slicePlane; + m_mode = obj.m_mode; + m_sliceViewPlane = obj.m_sliceViewPlane; m_sliceCoordinateScaled = obj.m_sliceCoordinateScaled; + m_planeEquationScaled = obj.m_planeEquationScaled; } -/** - * Equality operator. - * @param obj - * Instance compared to this for equality. - * @return - * True if this instance and 'obj' instance are considered equal. - */ -bool -VolumeSurfaceOutlineModelCacheKey::operator==(const VolumeSurfaceOutlineModelCacheKey& obj) const -{ - if (this == &obj) { - return true; - } - - /* perform equality testing HERE and return true if equal ! */ - if ((m_slicePlane == obj.m_slicePlane) - && (m_sliceCoordinateScaled == obj.m_sliceCoordinateScaled)) { - return true; - } - - return false; -} +///** +// * Equality operator. +// * @param obj +// * Instance compared to this for equality. +// * @return +// * True if this instance and 'obj' instance are considered equal. +// */ +//bool +//VolumeSurfaceOutlineModelCacheKey::operator==(const VolumeSurfaceOutlineModelCacheKey& obj) const +//{ +// if (this == &obj) { +// return true; +// } +// +// /* perform equality testing HERE and return true if equal ! */ +// if ((m_sliceViewPlane == obj.m_sliceViewPlane) +// && (m_sliceCoordinateScaled == obj.m_sliceCoordinateScaled)) { +// return true; +// } +// +// return false; +//} /** * Less than operator. @@ -172,16 +247,42 @@ VolumeSurfaceOutlineModelCacheKey::operator<(const VolumeSurfaceOutlineModelCach return false; } - if (static_cast(m_slicePlane) < static_cast(obj.m_slicePlane)) { - return true; + if (m_mode == obj.m_mode) { + switch (m_mode) { + case Mode::PLANE_EQUATION: + { + for (int32_t i = 0; i < static_cast(m_planeEquationScaled.size()); i++) { + if (m_planeEquationScaled[i] < obj.m_planeEquationScaled[i]) { + return true; + } + else if (m_planeEquationScaled[i] > obj.m_planeEquationScaled[i]) { + return false; + } + } + } + break; + case Mode::SLICE_VIEW_PLANE: + { + if (static_cast(m_sliceViewPlane) < static_cast(obj.m_sliceViewPlane)) { + return true; + } + else if (static_cast(m_sliceViewPlane) > static_cast(obj.m_sliceViewPlane)) { + return false; + } + + if (m_sliceCoordinateScaled < obj.m_sliceCoordinateScaled) { + return true; + } + } + break; + } } - else if (static_cast(m_slicePlane) > static_cast(obj.m_slicePlane)) { - return false; + else { + if (static_cast(m_mode) < static_cast(obj.m_mode)) { + return true; + } } - if (m_sliceCoordinateScaled < obj.m_sliceCoordinateScaled) { - return true; - } return false; } @@ -193,11 +294,21 @@ VolumeSurfaceOutlineModelCacheKey::operator<(const VolumeSurfaceOutlineModelCach AString VolumeSurfaceOutlineModelCacheKey::toString() const { - QString str("Plane=" - + VolumeSliceViewPlaneEnum::toName(m_slicePlane) - + ", " - + "ScaledCoordinate=" - + QString::number(m_sliceCoordinateScaled)); + QString str; + switch (m_mode) { + case Mode::PLANE_EQUATION: + str = ("Plane Equation = " + + AString::fromNumbers(&m_planeEquationScaled[0], m_planeEquationScaled.size(), ",")); + break; + case Mode::SLICE_VIEW_PLANE: + str = ("Slice View Plane=" + + VolumeSliceViewPlaneEnum::toName(m_sliceViewPlane) + + ", " + + "ScaledCoordinate=" + + QString::number(m_sliceCoordinateScaled)); + break; + } + return str; } diff --git a/src/Brain/VolumeSurfaceOutlineModelCacheKey.h b/src/Brain/VolumeSurfaceOutlineModelCacheKey.h index 1785ab2b6..4ebbecdc1 100644 --- a/src/Brain/VolumeSurfaceOutlineModelCacheKey.h +++ b/src/Brain/VolumeSurfaceOutlineModelCacheKey.h @@ -22,31 +22,36 @@ /*LICENSE_END*/ - +#include #include #include "CaretObject.h" - +#include "Plane.h" #include "VolumeSliceViewPlaneEnum.h" namespace caret { + class Plane; + class VolumeMappableInterface; class VolumeSurfaceOutlineModelCacheKey : public CaretObject { public: VolumeSurfaceOutlineModelCacheKey(const VolumeMappableInterface* underlayVolume, - const VolumeSliceViewPlaneEnum::Enum slicePlane, + const VolumeSliceViewPlaneEnum::Enum sliceViewPlane, const float sliceCoordinate); + VolumeSurfaceOutlineModelCacheKey(const VolumeMappableInterface* underlayVolume, + const Plane& plane); + virtual ~VolumeSurfaceOutlineModelCacheKey(); VolumeSurfaceOutlineModelCacheKey(const VolumeSurfaceOutlineModelCacheKey& obj); VolumeSurfaceOutlineModelCacheKey& operator=(const VolumeSurfaceOutlineModelCacheKey& obj); - bool operator==(const VolumeSurfaceOutlineModelCacheKey& obj) const; +// bool operator==(const VolumeSurfaceOutlineModelCacheKey& obj) const; bool operator<(const VolumeSurfaceOutlineModelCacheKey& obj) const; @@ -55,11 +60,22 @@ namespace caret { virtual AString toString() const; private: + enum class Mode { + PLANE_EQUATION, + SLICE_VIEW_PLANE + }; + void copyHelperVolumeSurfaceOutlineModelCacheKey(const VolumeSurfaceOutlineModelCacheKey& obj); - VolumeSliceViewPlaneEnum::Enum m_slicePlane = VolumeSliceViewPlaneEnum::ALL; + float computeScaleFactor(const VolumeMappableInterface* underlayVolume) const; + + Mode m_mode; + + VolumeSliceViewPlaneEnum::Enum m_sliceViewPlane = VolumeSliceViewPlaneEnum::ALL; - int32_t m_sliceCoordinateScaled = 100000; + int64_t m_sliceCoordinateScaled = 100000; + + std::array m_planeEquationScaled; // ADD_NEW_MEMBERS_HERE -- GitLab From 7a14c9885b1096de8649617997281045eaeace0b Mon Sep 17 00:00:00 2001 From: John Harwell Date: Thu, 11 Apr 2019 09:34:21 -0500 Subject: [PATCH 281/427] Improve warning message for invalid macro command data type and include command name when number of parameters is incorrect. --- src/Common/WuQMacroGroupXmlStreamReader.cxx | 9 +++++---- src/GuiQt/WbMacroCustomOperationBase.cxx | 4 +++- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/src/Common/WuQMacroGroupXmlStreamReader.cxx b/src/Common/WuQMacroGroupXmlStreamReader.cxx index 2f243521d..d4f02ca2a 100644 --- a/src/Common/WuQMacroGroupXmlStreamReader.cxx +++ b/src/Common/WuQMacroGroupXmlStreamReader.cxx @@ -445,10 +445,11 @@ WuQMacroGroupXmlStreamReader::readMacroCommandParameter(QXmlStreamReader& xmlRea WuQMacroDataValueTypeEnum::Enum dataType = WuQMacroDataValueTypeEnum::fromName(dataTypeString.toString(), &dataTypeValid); if (! dataTypeValid) { - es.append(dataTypeString.toString() - + " is not valid for attribute " - + ATTRIBUTE_MACRO_COMMAND_PARAMETER_DATA_TYPE - + " "); + addToWarnings(xmlReader, + (dataTypeString.toString() + + " is not valid for attribute " + + ATTRIBUTE_MACRO_COMMAND_PARAMETER_DATA_TYPE + + " ")); return NULL; } diff --git a/src/GuiQt/WbMacroCustomOperationBase.cxx b/src/GuiQt/WbMacroCustomOperationBase.cxx index b52235509..4c5ad830c 100644 --- a/src/GuiQt/WbMacroCustomOperationBase.cxx +++ b/src/GuiQt/WbMacroCustomOperationBase.cxx @@ -103,7 +103,9 @@ WbMacroCustomOperationBase::validateCorrectNumberOfParameters(const WuQMacroComm const int32_t paramCount = command->getNumberOfParameters(); if (paramCount < correctNumberOfParameters) { - appendToErrorMessage("Command should contain " + appendToErrorMessage("Command " + + command->getDescriptiveName() + + " should contain " + QString::number(correctNumberOfParameters) + " parameters but contains " + QString::number(paramCount) -- GitLab From d68f5f0ce97958fa5916e1bb04c0f866959c2b3c Mon Sep 17 00:00:00 2001 From: John Harwell Date: Thu, 11 Apr 2019 11:01:08 -0500 Subject: [PATCH 282/427] When a scene is reloaded through the Macro Dialog, restore the expanded status of the macro and selection of the macro or the command. --- src/GuiQt/WuQMacroDialog.cxx | 50 +++++++++++++++++++++++++++++++++++- 1 file changed, 49 insertions(+), 1 deletion(-) diff --git a/src/GuiQt/WuQMacroDialog.cxx b/src/GuiQt/WuQMacroDialog.cxx index b429729fc..802f7aeba 100644 --- a/src/GuiQt/WuQMacroDialog.cxx +++ b/src/GuiQt/WuQMacroDialog.cxx @@ -1637,12 +1637,60 @@ WuQMacroDialog::macroGroupToolButtonClicked() void WuQMacroDialog::macroGroupResetToolButtonClicked() { + /* + * Save expanded status of selected macro + */ + bool expandedFlag = false; + WuQMacro* selectedMacro = getSelectedMacro(); + if (selectedMacro != NULL) { + QModelIndex modelIndex = selectedMacro->index(); + if (modelIndex.isValid()) { + expandedFlag = m_treeView->isExpanded(modelIndex); + } + } + + /* + * If expanded, a command may be selected + */ + int32_t selectedCommandIndex = -1; + if (expandedFlag) { + WuQMacroCommand* selectedCommand = getSelectedMacroCommand(); + if (selectedCommand != NULL) { + if (selectedMacro != NULL) { + selectedCommandIndex = selectedMacro->getIndexOfMacroCommand(selectedCommand); + } + } + } + + /* + * Reload the macro + */ WuQMacro* macro = WuQMacroManager::instance()->resetMacro(getWindow(), getSelectedMacro()); updateDialogContents(); + if (macro != NULL) { - m_treeView->setCurrentIndex(macro->index()); + /* + * Restore expanded status of macro + */ + QModelIndex modelIndex = macro->index(); treeItemSelected(macro->index()); + m_treeView->setExpanded(modelIndex, + expandedFlag); + + /* + * May restore selection of command + */ + if (selectedCommandIndex >= 0) { + if (selectedCommandIndex < macro->getNumberOfMacroCommands()) { + modelIndex = macro->getMacroCommandAtIndex(selectedCommandIndex)->index(); + } + } + + /* + * Select macro or command that was selected before reset + */ + m_treeView->setCurrentIndex(modelIndex); } updateDialogContents(); -- GitLab From 71b1c1c073c21863f1e6a4052c4cc9c3e1687c18 Mon Sep 17 00:00:00 2001 From: John Harwell Date: Thu, 11 Apr 2019 14:40:31 -0500 Subject: [PATCH 283/427] The text "Scenes..." was missing on the Window Menu (item was blank). Cleaned up a a toolbar button tooltips. --- src/GuiQt/BrainBrowserWindowToolBar.cxx | 14 +++----------- src/GuiQt/GuiManager.cxx | 3 ++- 2 files changed, 5 insertions(+), 12 deletions(-) diff --git a/src/GuiQt/BrainBrowserWindowToolBar.cxx b/src/GuiQt/BrainBrowserWindowToolBar.cxx index 7b09e2aa0..3a1af7f40 100644 --- a/src/GuiQt/BrainBrowserWindowToolBar.cxx +++ b/src/GuiQt/BrainBrowserWindowToolBar.cxx @@ -290,15 +290,10 @@ BrainBrowserWindowToolBar::BrainBrowserWindowToolBar(const int32_t browserWindow /* * Scene button */ - const QString sceneButtonToolTip("Display the Scene Dialog. " - "Arrow displays a menu for loading scenes " - "including reloading or the current scene."); QToolButton* sceneDialogToolButton = new QToolButton(); sceneDialogToolButton->setText(""); sceneDialogToolButton->setIcon(GuiManager::get()->getSceneDialogDisplayAction()->icon()); sceneDialogToolButton->setDefaultAction(GuiManager::get()->getSceneDialogDisplayAction()); - WuQtUtilities::setWordWrappedToolTip(sceneDialogToolButton, - sceneButtonToolTip); QObject::connect(sceneDialogToolButton, &QToolButton::clicked, this, &BrainBrowserWindowToolBar::sceneToolButtonClicked); @@ -311,14 +306,11 @@ BrainBrowserWindowToolBar::BrainBrowserWindowToolBar(const int32_t browserWindow movieIcon)) { movieButtonText = "Movie"; } - const QString movieButtonToolTip("Show movie recording dialog. " - "Arrow displays a menu for creating movie " - "without using movie dialog."); + const QString movieButtonToolTip("Show movie recording window"); m_movieToolButton = new QToolButton(); - WuQtUtilities::setWordWrappedToolTip(m_movieToolButton, - movieButtonToolTip); m_movieToolButton->setText(movieButtonText); m_movieToolButton->setIcon(movieIcon); + m_movieToolButton->setToolTip(movieButtonToolTip); QObject::connect(m_movieToolButton, &QToolButton::clicked, parentBrainBrowserWindow, &BrainBrowserWindow::processMovieRecording); @@ -334,7 +326,7 @@ BrainBrowserWindowToolBar::BrainBrowserWindowToolBar(const int32_t browserWindow else { macrosAction->setText("M"); } - macrosAction->setToolTip("Show macros dialog"); + macrosAction->setToolTip("Show macros window"); QObject::connect(macrosAction, &QAction::triggered, this, &BrainBrowserWindowToolBar::showMacroDialog); QToolButton* macrosToolButton = new QToolButton(); diff --git a/src/GuiQt/GuiManager.cxx b/src/GuiQt/GuiManager.cxx index acbd9ad96..bebcd7ef7 100644 --- a/src/GuiQt/GuiManager.cxx +++ b/src/GuiQt/GuiManager.cxx @@ -280,7 +280,8 @@ GuiManager::initializeGuiManager() /* * Scene dialog action */ - m_sceneDialogDisplayAction = new QAction(this); + m_sceneDialogDisplayAction = new QAction("Scenes...", + this); QObject::connect(m_sceneDialogDisplayAction, &QAction::triggered, this, &GuiManager::sceneDialogDisplayActionTriggered); QIcon clapBoardIcon; -- GitLab From e04e20f287da473265c286c20f47a42ce833e745 Mon Sep 17 00:00:00 2001 From: John Harwell Date: Tue, 23 Apr 2019 14:40:43 -0500 Subject: [PATCH 284/427] Added a method to GraphicsPrimitive that simplifies a line by removing every "n-th" vertex but does not improve performance much and lines look different. May try to improve it later. --- src/Graphics/GraphicsPrimitive.cxx | 153 +++++++++++++++++++++++++++++ src/Graphics/GraphicsPrimitive.h | 7 +- 2 files changed, 156 insertions(+), 4 deletions(-) diff --git a/src/Graphics/GraphicsPrimitive.cxx b/src/Graphics/GraphicsPrimitive.cxx index 4327e4c96..6f526f83f 100644 --- a/src/Graphics/GraphicsPrimitive.cxx +++ b/src/Graphics/GraphicsPrimitive.cxx @@ -1519,6 +1519,159 @@ GraphicsPrimitive::setTextureImage(const uint8_t* imageBytesRGBA, } } +/** + * Simplify line types by removing every 'skipVertexCount' vertex. + * When 'skipVertexCount' is 2, every other point in the line is removed. + * Nothing is done when 'skipVertexCount' is less than 2 or primitive + * type is not a line. + * First and last point are alwyas preserved. + * + * @param skipVertexCount + * Number of vertices to skip. + */ +void +GraphicsPrimitive::simplfyLines(const int32_t skipVertexCount) +{ + if (skipVertexCount < 2) { + return; + } + + bool lineTypeFlag(false); + switch (m_primitiveType) { + case PrimitiveType::OPENGL_LINE_LOOP: + lineTypeFlag = true; + break; + case PrimitiveType::OPENGL_LINE_STRIP: + lineTypeFlag = true; + break; + case PrimitiveType::OPENGL_LINES: + break; + case PrimitiveType::OPENGL_POINTS: + break; + case PrimitiveType::OPENGL_TRIANGLE_FAN: + break; + case PrimitiveType::OPENGL_TRIANGLE_STRIP: + break; + case PrimitiveType::OPENGL_TRIANGLES: + break; + case PrimitiveType::MODEL_SPACE_POLYGONAL_LINE_LOOP_BEVEL_JOIN: + case PrimitiveType::MODEL_SPACE_POLYGONAL_LINE_LOOP_MITER_JOIN: + lineTypeFlag = true; + break; + case PrimitiveType::MODEL_SPACE_POLYGONAL_LINE_STRIP_BEVEL_JOIN: + case PrimitiveType::MODEL_SPACE_POLYGONAL_LINE_STRIP_MITER_JOIN: + lineTypeFlag = true; + break; + case PrimitiveType::MODEL_SPACE_POLYGONAL_LINES: + lineTypeFlag = true; + break; + case PrimitiveType::POLYGONAL_LINE_LOOP_BEVEL_JOIN: + case PrimitiveType::POLYGONAL_LINE_LOOP_MITER_JOIN: + lineTypeFlag = true; + break; + case PrimitiveType::POLYGONAL_LINE_STRIP_BEVEL_JOIN: + case PrimitiveType::POLYGONAL_LINE_STRIP_MITER_JOIN: + lineTypeFlag = true; + break; + case PrimitiveType::POLYGONAL_LINES: + lineTypeFlag = true; + break; + case PrimitiveType::SPHERES: + break; + } + + if ( ! lineTypeFlag) { + return; + } + + std::vector xyz; + std::vector normals; + std::vector rgbaFloat; + std::vector rgbaByte; + std::vector textureSTR; + + const int32_t numVertices = getNumberOfVertices(); + xyz.reserve(m_xyz.size()); + normals.reserve(m_floatNormalVectorXYZ.size()); + rgbaFloat.reserve(m_floatRGBA.size()); + rgbaByte.reserve(m_unsignedByteRGBA.size()); + textureSTR.reserve(m_floatTextureSTR.size()); + + const int32_t lastIndex = numVertices - 1; + for (int32_t i = 0; i < numVertices; i++) { + bool addFlag(false); + if (i == 0) { + addFlag = true; + } + else if (i == lastIndex) { + addFlag = true; + } + else { + const int remainder = i % skipVertexCount; + if (remainder == 0) { + addFlag = true; + } + } + + if (addFlag) { + std::cout << "Adding vertex: " << i << std::endl; + + const int32_t i3 = i * 3; + const int32_t i4 = i * 4; + switch (m_vertexDataType) { + case VertexDataType::FLOAT_XYZ: + xyz.push_back(m_xyz[i3]); + xyz.push_back(m_xyz[i3+1]); + xyz.push_back(m_xyz[i3+2]); + break; + } + + switch (m_normalVectorDataType) { + case NormalVectorDataType::FLOAT_XYZ: + normals.push_back(m_floatNormalVectorXYZ[i3]); + normals.push_back(m_floatNormalVectorXYZ[i3+1]); + normals.push_back(m_floatNormalVectorXYZ[i3+2]); + break; + case NormalVectorDataType::NONE: + break; + } + + switch (m_colorDataType) { + case ColorDataType::FLOAT_RGBA: + rgbaFloat.push_back(m_floatRGBA[i4]); + rgbaFloat.push_back(m_floatRGBA[i4+1]); + rgbaFloat.push_back(m_floatRGBA[i4+2]); + rgbaFloat.push_back(m_floatRGBA[i4+3]); + break; + case ColorDataType::UNSIGNED_BYTE_RGBA: + rgbaByte.push_back(m_unsignedByteRGBA[i4]); + rgbaByte.push_back(m_unsignedByteRGBA[i4+1]); + rgbaByte.push_back(m_unsignedByteRGBA[i4+2]); + rgbaByte.push_back(m_unsignedByteRGBA[i4+3]); + break; + case ColorDataType::NONE: + break; + } + + switch (m_textureDataType) { + case TextureDataType::FLOAT_STR: + textureSTR.push_back(m_floatTextureSTR[i3]); + textureSTR.push_back(m_floatTextureSTR[i3+1]); + textureSTR.push_back(m_floatTextureSTR[i3+2]); + break; + case TextureDataType::NONE: + break; + } + } + } + + m_xyz = std::move(xyz); + m_floatNormalVectorXYZ = normals; + m_floatRGBA = rgbaFloat; + m_unsignedByteRGBA = rgbaByte; + m_floatTextureSTR = textureSTR; +} + /** * Get the OpenGL graphics engine data in this instance. * diff --git a/src/Graphics/GraphicsPrimitive.h b/src/Graphics/GraphicsPrimitive.h index 1ee0ea101..bfc30a56e 100644 --- a/src/Graphics/GraphicsPrimitive.h +++ b/src/Graphics/GraphicsPrimitive.h @@ -432,6 +432,8 @@ namespace caret { * Clone this primitive. */ virtual GraphicsPrimitive* clone() const = 0; + + void simplfyLines(const int32_t skipVertexCount); protected: AString toStringPrivate(const bool includeAllDataFlag) const; @@ -522,14 +524,11 @@ namespace caret { std::vector m_floatTextureSTR; std::vector m_textureImageBytesRGBA; + friend class GraphicsEngineDataOpenGL; friend class GraphicsOpenGLPolylineTriangles; friend class GraphicsPrimitiveSelectionHelper; - std::vector m_dummyFloatRGBAVector; - - std::vector m_dummyUnsignedByteRGBAVector; - // ADD_NEW_MEMBERS_HERE }; -- GitLab From 25eece978962b2a34a6d7498ee3b7b89d8000f97 Mon Sep 17 00:00:00 2001 From: John Harwell Date: Thu, 25 Apr 2019 14:45:58 -0500 Subject: [PATCH 285/427] Resolve WB-847 Disable normal test for surface drawing: Culling of back-facing triangles has been disabled when drawing a surface. --- src/Brain/BrainOpenGLFixedPipeline.cxx | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/Brain/BrainOpenGLFixedPipeline.cxx b/src/Brain/BrainOpenGLFixedPipeline.cxx index 108c89535..1c3b7ee1f 100644 --- a/src/Brain/BrainOpenGLFixedPipeline.cxx +++ b/src/Brain/BrainOpenGLFixedPipeline.cxx @@ -1988,8 +1988,11 @@ BrainOpenGLFixedPipeline::drawSurface(Surface* surface, glPolygonOffset(factor, units); } + glPushAttrib(GL_ENABLE_BIT); + glDisable(GL_CULL_FACE); this->drawSurfaceTrianglesWithVertexArrays(surface, nodeColoringRGBA); + glPopAttrib(); if (borderAboveSurfaceOffset != 0.0) { glDisable(GL_POLYGON_OFFSET_FILL); -- GitLab From 44aa5998f9ce73fdb95755905ec6288f8cb00222 Mon Sep 17 00:00:00 2001 From: John Harwell Date: Thu, 25 Apr 2019 15:07:56 -0500 Subject: [PATCH 286/427] Resolve WB-846 Change default palette to absolute percentile: absolute percentile is now the default palette for all data except data interpreted as anatomical volume data that remains percentile. --- src/Files/PaletteFile.cxx | 8 ++++---- src/Palette/PaletteColorMapping.cxx | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/Files/PaletteFile.cxx b/src/Files/PaletteFile.cxx index d9bc82b19..fc9540670 100644 --- a/src/Files/PaletteFile.cxx +++ b/src/Files/PaletteFile.cxx @@ -1780,14 +1780,14 @@ PaletteFile::setDefaultPaletteColorMapping(PaletteColorMapping* paletteColorMapp // paletteColorMapping->setUserScaleNegativeMinimum(0.0); // paletteColorMapping->setUserScalePositiveMinimum(0.0); // paletteColorMapping->setUserScalePositiveMaximum(1.5); - paletteColorMapping->setScaleMode(PaletteScaleModeEnum::MODE_AUTO_SCALE_PERCENTAGE); + paletteColorMapping->setScaleMode(PaletteScaleModeEnum::MODE_AUTO_SCALE_ABSOLUTE_PERCENTAGE); paletteColorMapping->setAutoScalePercentageNegativeMaximum(98.0); paletteColorMapping->setAutoScalePercentageNegativeMinimum(2.0); paletteColorMapping->setAutoScalePercentagePositiveMinimum(2.0); paletteColorMapping->setAutoScalePercentagePositiveMaximum(98.0); } else { - paletteColorMapping->setScaleMode(PaletteScaleModeEnum::MODE_AUTO_SCALE_PERCENTAGE); + paletteColorMapping->setScaleMode(PaletteScaleModeEnum::MODE_AUTO_SCALE_ABSOLUTE_PERCENTAGE); paletteColorMapping->setAutoScalePercentageNegativeMaximum(98.0); paletteColorMapping->setAutoScalePercentageNegativeMinimum(2.0); paletteColorMapping->setAutoScalePercentagePositiveMinimum(2.0); @@ -1803,7 +1803,7 @@ PaletteFile::setDefaultPaletteColorMapping(PaletteColorMapping* paletteColorMapp paletteColorMapping->setSelectedPaletteName("videen-style"); paletteColorMapping->setSelectedPaletteName("ROY-BIG-BL"); paletteColorMapping->setInterpolatePaletteFlag(true); - paletteColorMapping->setScaleMode(PaletteScaleModeEnum::MODE_AUTO_SCALE_PERCENTAGE); + paletteColorMapping->setScaleMode(PaletteScaleModeEnum::MODE_AUTO_SCALE_ABSOLUTE_PERCENTAGE); paletteColorMapping->setAutoScalePercentageNegativeMaximum(98.0); paletteColorMapping->setAutoScalePercentageNegativeMinimum(2.0); paletteColorMapping->setAutoScalePercentagePositiveMinimum(2.0); @@ -1817,7 +1817,7 @@ PaletteFile::setDefaultPaletteColorMapping(PaletteColorMapping* paletteColorMapp paletteColorMapping->setSelectedPaletteName("videen-style"); paletteColorMapping->setSelectedPaletteName("ROY-BIG-BL"); paletteColorMapping->setInterpolatePaletteFlag(true); - paletteColorMapping->setScaleMode(PaletteScaleModeEnum::MODE_AUTO_SCALE_PERCENTAGE); + paletteColorMapping->setScaleMode(PaletteScaleModeEnum::MODE_AUTO_SCALE_ABSOLUTE_PERCENTAGE); paletteColorMapping->setAutoScalePercentageNegativeMaximum(98.0); paletteColorMapping->setAutoScalePercentageNegativeMinimum(2.0); paletteColorMapping->setAutoScalePercentagePositiveMinimum(2.0); diff --git a/src/Palette/PaletteColorMapping.cxx b/src/Palette/PaletteColorMapping.cxx index 97ea9fc17..102f78e46 100644 --- a/src/Palette/PaletteColorMapping.cxx +++ b/src/Palette/PaletteColorMapping.cxx @@ -243,7 +243,7 @@ PaletteColorMapping::operator==(const PaletteColorMapping& pcm) const void PaletteColorMapping::initializeMembersPaletteColorMapping() { - this->scaleMode = PaletteScaleModeEnum::MODE_AUTO_SCALE_PERCENTAGE; + this->scaleMode = PaletteScaleModeEnum::MODE_AUTO_SCALE_ABSOLUTE_PERCENTAGE; this->autoScalePercentageNegativeMaximum = 98.0f; this->autoScalePercentageNegativeMinimum = 2.0f; this->autoScalePercentagePositiveMinimum = 2.0f; -- GitLab From e91361ae516863e78802d8d3256b9a8e3c470d98 Mon Sep 17 00:00:00 2001 From: John Harwell Date: Thu, 25 Apr 2019 15:17:57 -0500 Subject: [PATCH 287/427] WB-842 Opening a spec or scene file from Finder on MacOS Mojave (10.14) does not work: This works by the MacApplication class receiving a QFileOpenEvent and then passing it along through the Workbench event system to GuiManager. It seems that in MacOS Mojave, this is happening very quickly, before GuiManager is created. So, move the creation of GuiManager to just after the creation MacApplication. --- src/Desktop/desktop.cxx | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/src/Desktop/desktop.cxx b/src/Desktop/desktop.cxx index f52333c30..b6c7a923e 100644 --- a/src/Desktop/desktop.cxx +++ b/src/Desktop/desktop.cxx @@ -342,7 +342,17 @@ main(int argc, char* argv[]) #else //CARET_OS_MACOSX QApplication app(argc, argv); #endif //CARET_OS_MACOSX - + + /* + * Create the GUI Manager. + * Moved here as part of WB-842. In OSX Mojave (10.14), + * the open file event appears to be delivered very quickly + * so the GUI manager need to be created sooner than + * before. + */ + GuiManager::createGuiManager(); + app.processEvents(); + ApplicationInformation applicationInformation; QApplication::addLibraryPath( @@ -433,11 +443,6 @@ main(int argc, char* argv[]) } } - /* - * Create the GUI Manager. - */ - GuiManager::createGuiManager(); - /* * Letting the App process events will allow the message for a * double-clicked spec file in Mac OSX to get processed. -- GitLab From ed4fd02f48063e97ac7eb02d36f74e2dc35997ed Mon Sep 17 00:00:00 2001 From: John Harwell Date: Thu, 25 Apr 2019 15:41:32 -0500 Subject: [PATCH 288/427] Added frames per second to dialog that shows graphics timing. --- src/GuiQt/BrainBrowserWindow.cxx | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/GuiQt/BrainBrowserWindow.cxx b/src/GuiQt/BrainBrowserWindow.cxx index 5ac20cfbc..8ad9624e1 100644 --- a/src/GuiQt/BrainBrowserWindow.cxx +++ b/src/GuiQt/BrainBrowserWindow.cxx @@ -2981,8 +2981,16 @@ BrainBrowserWindow::processDevelopGraphicsTiming() const float time = et.getElapsedTimeSeconds() / numTimes; const AString timeString = AString::number(time, 'f', 5); + AString fpsString; + if (time > 0.0) { + const float fps(1.0 / time); + fpsString = ("\nFrame per second: " + + AString::number(fps, 'f', 3)); + } const AString msg = ("Time to draw graphics (seconds): " - + timeString); + + timeString + + fpsString); + WuQMessageBox::informationOk(this, msg); } -- GitLab From bbeaaa62e95145b510749ac4f617735895d5882d Mon Sep 17 00:00:00 2001 From: John Harwell Date: Thu, 25 Apr 2019 16:32:54 -0500 Subject: [PATCH 289/427] Manage Files Dialog: Added a file information option to sprocket menu for files that are in memory. --- src/GuiQt/SpecFileManagementDialog.cxx | 31 ++++++++++++++++++++++++++ src/GuiQt/SpecFileManagementDialog.h | 2 ++ 2 files changed, 33 insertions(+) diff --git a/src/GuiQt/SpecFileManagementDialog.cxx b/src/GuiQt/SpecFileManagementDialog.cxx index 83dbc3b68..28e8ee78d 100644 --- a/src/GuiQt/SpecFileManagementDialog.cxx +++ b/src/GuiQt/SpecFileManagementDialog.cxx @@ -56,6 +56,7 @@ #include "DataFileContentCopyMoveDialog.h" #include "DataFileContentCopyMoveInterface.h" #include "DataFileException.h" +#include "DataFileContentInformation.h" #include "EventBrowserTabGetAllViewed.h" #include "EventDataFileRead.h" #include "EventDataFileReload.h" @@ -77,6 +78,7 @@ #include "UsernamePasswordWidget.h" #include "WuQImageLabel.h" #include "WuQMessageBox.h" +#include "WuQTextEditorDialog.h" #include "WuQWidgetObjectGroup.h" #include "WuQtUtilities.h" @@ -2273,6 +2275,7 @@ SpecFileManagementDialog::fileOptionsActionSelected(int rowIndex) QAction* copyMoveFileContentAction = NULL; QAction* editMetaDataAction = NULL; QAction* setFileNameAction = NULL; + QAction* showFileInformationAction = NULL; QAction* setStructureAction = NULL; QAction* unloadFileMapsAction = NULL; QAction* viewMetaDataAction = NULL; @@ -2291,6 +2294,7 @@ SpecFileManagementDialog::fileOptionsActionSelected(int rowIndex) copyFilePathToClipboardAction = menu.addAction(copyPathText); editMetaDataAction = menu.addAction("Edit Metadata..."); setFileNameAction = menu.addAction("Set File Name..."); + showFileInformationAction = menu.addAction("Show File Information..."); } } else if ( ! sceneAnnotationFileFlag) { @@ -2328,6 +2332,9 @@ SpecFileManagementDialog::fileOptionsActionSelected(int rowIndex) specFileDataFile, caretDataFile); } + else if (selectedAction == showFileInformationAction) { + showFileInformation(caretDataFile); + } else if (selectedAction == setStructureAction) { CaretAssert(0); } @@ -2537,6 +2544,30 @@ SpecFileManagementDialog::changeFileName(QWidget* parent, // } } +/** + * Show information about a file. + * + * @param caretDataFileIn + * File for which information is displayed. + */ +void +SpecFileManagementDialog::showFileInformation(CaretDataFile* caretDataFile) +{ + DataFileContentInformation dataFileContentInformation; + const bool showMapInformationFlag(true); + dataFileContentInformation.setOptionFlag(DataFileContentInformation::OPTION_SHOW_MAP_INFORMATION, + showMapInformationFlag); + + caretDataFile->addToDataFileContentInformation(dataFileContentInformation); + + WuQTextEditorDialog::runNonModal("File Information", + dataFileContentInformation.getInformationInString(), + WuQTextEditorDialog::TextMode::PLAIN, + WuQTextEditorDialog::WrapMode::NO, + this); +} + + ///** // * Called when spec file options tool button is triggered. // */ diff --git a/src/GuiQt/SpecFileManagementDialog.h b/src/GuiQt/SpecFileManagementDialog.h index f3beb59ab..780a53d8a 100644 --- a/src/GuiQt/SpecFileManagementDialog.h +++ b/src/GuiQt/SpecFileManagementDialog.h @@ -194,6 +194,8 @@ namespace caret { SpecFileDataFile* specFileDataFile, CaretDataFile* caretDataFile); + void showFileInformation(CaretDataFile* caretDataFile); + void copyMoveFileContent(QWidget* parent, CaretDataFile* caretDataFile); -- GitLab From f2084e4a9a5e0ee03dabbbeef376fe80ba8bd06b Mon Sep 17 00:00:00 2001 From: John Harwell Date: Mon, 29 Apr 2019 12:04:54 -0500 Subject: [PATCH 290/427] Fix some of the compilation warnings from most recent update. --- src/Brain/MovieRecorder.cxx | 8 ++------ src/Brain/MovieRecorder.h | 6 +----- src/Brain/SessionManager.cxx | 2 +- src/Brain/VolumeSurfaceOutlineColorOrTabModel.cxx | 1 + src/GuiQt/WbMacroCustomOperationManager.cxx | 2 +- src/GuiQt/WuQMacroCommandParameterWidget.cxx | 4 ++-- 6 files changed, 8 insertions(+), 15 deletions(-) diff --git a/src/Brain/MovieRecorder.cxx b/src/Brain/MovieRecorder.cxx index 4ae8ed274..bb5256853 100644 --- a/src/Brain/MovieRecorder.cxx +++ b/src/Brain/MovieRecorder.cxx @@ -51,13 +51,9 @@ using namespace caret; /** * Constructor. - * - * @param brain - * The brain */ -MovieRecorder::MovieRecorder(Brain* brain) -: CaretObject(), -m_brain(brain) +MovieRecorder::MovieRecorder() +: CaretObject() { const QString tempSubDir("WbViewMovie"); QDir tempDir(QDir::temp()); diff --git a/src/Brain/MovieRecorder.h b/src/Brain/MovieRecorder.h index cb1791667..96df94bbc 100644 --- a/src/Brain/MovieRecorder.h +++ b/src/Brain/MovieRecorder.h @@ -36,13 +36,11 @@ class QImage; class QStringList; namespace caret { - class Brain; - class MovieRecorder : public CaretObject { public: - MovieRecorder(Brain* brain); + MovieRecorder(); virtual ~MovieRecorder(); @@ -143,8 +141,6 @@ namespace caret { bool waitForImagesToFinishWriting(); - Brain* m_brain = NULL; - MovieRecorderModeEnum::Enum m_recordingMode = MovieRecorderModeEnum::MANUAL; MovieRecorderVideoResolutionTypeEnum::Enum m_resolutionType = MovieRecorderVideoResolutionTypeEnum::SD_640_480; diff --git a/src/Brain/SessionManager.cxx b/src/Brain/SessionManager.cxx index 34ce2fdf1..f94cdbbdc 100644 --- a/src/Brain/SessionManager.cxx +++ b/src/Brain/SessionManager.cxx @@ -108,7 +108,7 @@ SessionManager::SessionManager() Brain* brain = new Brain(m_caretPreferences); m_brains.push_back(brain); - m_movieRecorder.reset(new MovieRecorder(brain)); + m_movieRecorder.reset(new MovieRecorder()); } /** diff --git a/src/Brain/VolumeSurfaceOutlineColorOrTabModel.cxx b/src/Brain/VolumeSurfaceOutlineColorOrTabModel.cxx index bc8d0eee2..d725d0138 100644 --- a/src/Brain/VolumeSurfaceOutlineColorOrTabModel.cxx +++ b/src/Brain/VolumeSurfaceOutlineColorOrTabModel.cxx @@ -358,6 +358,7 @@ VolumeSurfaceOutlineColorOrTabModel::Item::~Item() * Item that is copied. */ VolumeSurfaceOutlineColorOrTabModel::Item::Item(const Item& item) +: SceneableInterface(item) { m_color = item.m_color; m_browserTabIndex = item.m_browserTabIndex; diff --git a/src/GuiQt/WbMacroCustomOperationManager.cxx b/src/GuiQt/WbMacroCustomOperationManager.cxx index 586f791e2..45e4f4076 100644 --- a/src/GuiQt/WbMacroCustomOperationManager.cxx +++ b/src/GuiQt/WbMacroCustomOperationManager.cxx @@ -473,7 +473,7 @@ WbMacroCustomOperationManager::getCustomParameterDataInfo(const int32_t /*browse switch (userType) { case WbMacroCustomDataTypeEnum::OVERLAY_INDEX: if (dataInfoOut.getDataType() == WuQMacroDataValueTypeEnum::INTEGER) { - dataInfoOut.setIntegerRange({ 1, BrainConstants::MAXIMUM_NUMBER_OF_OVERLAYS }); + dataInfoOut.setIntegerRange({{ 1, BrainConstants::MAXIMUM_NUMBER_OF_OVERLAYS }}); validFlag = true; } else { diff --git a/src/GuiQt/WuQMacroCommandParameterWidget.cxx b/src/GuiQt/WuQMacroCommandParameterWidget.cxx index fee97c228..567c67bd3 100644 --- a/src/GuiQt/WuQMacroCommandParameterWidget.cxx +++ b/src/GuiQt/WuQMacroCommandParameterWidget.cxx @@ -249,7 +249,7 @@ WuQMacroCommandParameterWidget::updateContent(int32_t windowIndex, break; case WuQMacroDataValueTypeEnum::FLOAT: { - std::array floatRange { -1.0e6, 1.0e6 }; + std::array floatRange {{ -1.0e6, 1.0e6 }}; if (customDataFlag) { WbMacroCustomDataInfo customDataInfo(WuQMacroDataValueTypeEnum::FLOAT); WuQMacroManager::instance()->getCustomParameterDataInfo(windowIndex, @@ -267,7 +267,7 @@ WuQMacroCommandParameterWidget::updateContent(int32_t windowIndex, break; case WuQMacroDataValueTypeEnum::INTEGER: { - std::array intRange { -100000, 100000 }; + std::array intRange {{ -100000, 100000 }}; if (customDataFlag) { WbMacroCustomDataInfo customDataInfo(WuQMacroDataValueTypeEnum::INTEGER); WuQMacroManager::instance()->getCustomParameterDataInfo(windowIndex, -- GitLab From 0b8e2798af5ca6cdbd2667f35ecc5ce754280b93 Mon Sep 17 00:00:00 2001 From: John Harwell Date: Mon, 29 Apr 2019 12:28:01 -0500 Subject: [PATCH 291/427] Remove fixes for 'initializer list' compilation warnings seen on Mac build as it causes a compiler error on Linux systems. It appears that this warning on Mac may be invalid (see https://stackoverflow.com/questions/13905200/is-it-wise-to-ignore-gcc-clangs-wmissing-braces-warning). --- src/GuiQt/WbMacroCustomOperationManager.cxx | 2 +- src/GuiQt/WuQMacroCommandParameterWidget.cxx | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/GuiQt/WbMacroCustomOperationManager.cxx b/src/GuiQt/WbMacroCustomOperationManager.cxx index 45e4f4076..586f791e2 100644 --- a/src/GuiQt/WbMacroCustomOperationManager.cxx +++ b/src/GuiQt/WbMacroCustomOperationManager.cxx @@ -473,7 +473,7 @@ WbMacroCustomOperationManager::getCustomParameterDataInfo(const int32_t /*browse switch (userType) { case WbMacroCustomDataTypeEnum::OVERLAY_INDEX: if (dataInfoOut.getDataType() == WuQMacroDataValueTypeEnum::INTEGER) { - dataInfoOut.setIntegerRange({{ 1, BrainConstants::MAXIMUM_NUMBER_OF_OVERLAYS }}); + dataInfoOut.setIntegerRange({ 1, BrainConstants::MAXIMUM_NUMBER_OF_OVERLAYS }); validFlag = true; } else { diff --git a/src/GuiQt/WuQMacroCommandParameterWidget.cxx b/src/GuiQt/WuQMacroCommandParameterWidget.cxx index 567c67bd3..fee97c228 100644 --- a/src/GuiQt/WuQMacroCommandParameterWidget.cxx +++ b/src/GuiQt/WuQMacroCommandParameterWidget.cxx @@ -249,7 +249,7 @@ WuQMacroCommandParameterWidget::updateContent(int32_t windowIndex, break; case WuQMacroDataValueTypeEnum::FLOAT: { - std::array floatRange {{ -1.0e6, 1.0e6 }}; + std::array floatRange { -1.0e6, 1.0e6 }; if (customDataFlag) { WbMacroCustomDataInfo customDataInfo(WuQMacroDataValueTypeEnum::FLOAT); WuQMacroManager::instance()->getCustomParameterDataInfo(windowIndex, @@ -267,7 +267,7 @@ WuQMacroCommandParameterWidget::updateContent(int32_t windowIndex, break; case WuQMacroDataValueTypeEnum::INTEGER: { - std::array intRange {{ -100000, 100000 }}; + std::array intRange { -100000, 100000 }; if (customDataFlag) { WbMacroCustomDataInfo customDataInfo(WuQMacroDataValueTypeEnum::INTEGER); WuQMacroManager::instance()->getCustomParameterDataInfo(windowIndex, -- GitLab From 30a49a602e8e4da44f9a749012f37226f7599217 Mon Sep 17 00:00:00 2001 From: John Harwell Date: Mon, 29 Apr 2019 12:48:05 -0500 Subject: [PATCH 292/427] Try to fix compilation error on RedHat Linux. --- src/GuiQt/WbMacroCustomOperationManager.cxx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/GuiQt/WbMacroCustomOperationManager.cxx b/src/GuiQt/WbMacroCustomOperationManager.cxx index 586f791e2..f92123655 100644 --- a/src/GuiQt/WbMacroCustomOperationManager.cxx +++ b/src/GuiQt/WbMacroCustomOperationManager.cxx @@ -473,7 +473,8 @@ WbMacroCustomOperationManager::getCustomParameterDataInfo(const int32_t /*browse switch (userType) { case WbMacroCustomDataTypeEnum::OVERLAY_INDEX: if (dataInfoOut.getDataType() == WuQMacroDataValueTypeEnum::INTEGER) { - dataInfoOut.setIntegerRange({ 1, BrainConstants::MAXIMUM_NUMBER_OF_OVERLAYS }); + std::array dataRange { 1, BrainConstants::MAXIMUM_NUMBER_OF_OVERLAYS }; + dataInfoOut.setIntegerRange(dataRange); validFlag = true; } else { -- GitLab From b9fdb1b74b66f7f2913c2869f763849260515c62 Mon Sep 17 00:00:00 2001 From: John Harwell Date: Mon, 29 Apr 2019 14:41:01 -0500 Subject: [PATCH 293/427] Resolve WB-850 Data Tooltips should show top-most enabled overlay. Data tooltips was always showing the top overlay, even if the top overlay is DISABLED. This has been corrected to show the top-most ENABLED overlay in the tooltip. --- src/Brain/DataToolTipsManager.cxx | 10 +- src/Brain/DataToolTipsManager.h | 6 +- src/Brain/IdentificationTextGenerator.cxx | 106 +++++++++++++--------- src/Brain/IdentificationTextGenerator.h | 4 + src/GuiQt/BrainOpenGLWidget.cxx | 6 +- 5 files changed, 81 insertions(+), 51 deletions(-) diff --git a/src/Brain/DataToolTipsManager.cxx b/src/Brain/DataToolTipsManager.cxx index 8c17c1eb2..247bcc092 100644 --- a/src/Brain/DataToolTipsManager.cxx +++ b/src/Brain/DataToolTipsManager.cxx @@ -180,12 +180,12 @@ DataToolTipsManager::setShowVolumeUnderlay(const bool status) /** - * @return Is show top layer enabled? + * @return Is show top enabled layer enabled? */ bool -DataToolTipsManager::isShowTopLayer() const +DataToolTipsManager::isShowTopEnabledLayer() const { - return m_showTopLayerFlag; + return m_showTopEnabledLayerFlag; } /** @@ -195,9 +195,9 @@ DataToolTipsManager::isShowTopLayer() const * New status. */ void -DataToolTipsManager::setShowTopLayer(const bool status) +DataToolTipsManager::setShowTopEnabledLayer(const bool status) { - m_showTopLayerFlag = status; + m_showTopEnabledLayerFlag = status; } /** diff --git a/src/Brain/DataToolTipsManager.h b/src/Brain/DataToolTipsManager.h index 5843c42a6..8b5b00c86 100644 --- a/src/Brain/DataToolTipsManager.h +++ b/src/Brain/DataToolTipsManager.h @@ -70,9 +70,9 @@ namespace caret { void setShowVolumeUnderlay(const bool status); - bool isShowTopLayer() const; + bool isShowTopEnabledLayer() const; - void setShowTopLayer(const bool status); + void setShowTopEnabledLayer(const bool status); bool isShowBorder() const; @@ -124,7 +124,7 @@ namespace caret { bool m_showVolumeUnderlayFlag = true; - bool m_showTopLayerFlag = true; + bool m_showTopEnabledLayerFlag = true; bool m_showBorderFlag = true; diff --git a/src/Brain/IdentificationTextGenerator.cxx b/src/Brain/IdentificationTextGenerator.cxx index 520a31841..98e52a0b5 100644 --- a/src/Brain/IdentificationTextGenerator.cxx +++ b/src/Brain/IdentificationTextGenerator.cxx @@ -1610,28 +1610,29 @@ IdentificationTextGenerator::generateSurfaceToolTip(const Brain* brain, AString::fromNumbers(xyz, 3, ", ")); } - if (dataToolTipsManager->isShowTopLayer()) { - const OverlaySet* overlaySet = browserTab->getOverlaySet(); + if (dataToolTipsManager->isShowTopEnabledLayer()) { + OverlaySet* overlaySet = const_cast(browserTab->getOverlaySet()); CaretAssert(overlaySet); - Overlay* overlay = const_cast(overlaySet->getOverlay(0)); - CaretAssert(overlay); - CaretMappableDataFile* mapFile(NULL); - int32_t mapIndex(-1); - overlay->getSelectionData(mapFile, - mapIndex); - if ((mapFile != NULL) - && (mapIndex >= 0)) { - std::vector mapIndices { mapIndex }; - AString textValue; - mapFile->getSurfaceNodeIdentificationForMaps(mapIndices, - surfaceStructure, - surfaceNodeIndex, - surfaceNumberOfNodes, - textValue); - if ( ! textValue.isEmpty()) { - idText.addLine(indentFlag, - "Top Layer", - textValue); + Overlay* overlay = getTopEnabledOverlay(overlaySet); + if (overlay != NULL) { + CaretMappableDataFile* mapFile(NULL); + int32_t mapIndex(-1); + overlay->getSelectionData(mapFile, + mapIndex); + if ((mapFile != NULL) + && (mapIndex >= 0)) { + std::vector mapIndices { mapIndex }; + AString textValue; + mapFile->getSurfaceNodeIdentificationForMaps(mapIndices, + surfaceStructure, + surfaceNodeIndex, + surfaceNumberOfNodes, + textValue); + if ( ! textValue.isEmpty()) { + idText.addLine(indentFlag, + "Top Enabled Layer", + textValue); + } } } } @@ -1761,30 +1762,51 @@ IdentificationTextGenerator::generateVolumeToolTip(const BrowserTabContent* brow } } - if (dataToolTipsManager->isShowTopLayer()) { - Overlay* overlay = const_cast(overlaySet->getOverlay(0)); - CaretAssert(overlay); - CaretMappableDataFile* mapFile(NULL); - int32_t mapIndex(-1); - overlay->getSelectionData(mapFile, - mapIndex); - if ((mapFile != NULL) - && (mapIndex >= 0)) { - std::vector mapIndices { mapIndex }; - AString textValue; - int64_t ijk[3]; - mapFile->getVolumeVoxelIdentificationForMaps(mapIndices, - xyz, - ijk, - textValue); - if ( ! textValue.isEmpty()) { - idText.addLine(indentFlag, - ("Top Layer: " - + textValue)); + if (dataToolTipsManager->isShowTopEnabledLayer()) { + Overlay* overlay = getTopEnabledOverlay(overlaySet); + if (overlay != NULL) { + CaretMappableDataFile* mapFile(NULL); + int32_t mapIndex(-1); + overlay->getSelectionData(mapFile, + mapIndex); + if ((mapFile != NULL) + && (mapIndex >= 0)) { + std::vector mapIndices { mapIndex }; + AString textValue; + int64_t ijk[3]; + mapFile->getVolumeVoxelIdentificationForMaps(mapIndices, + xyz, + ijk, + textValue); + if ( ! textValue.isEmpty()) { + idText.addLine(indentFlag, + ("Top Enabled Layer: " + + textValue)); + } } } } - +} + +/** + * @return Get the top-most enabled overlay. NULL if no overlays enabled + * + * @param overlaySet + * Overlay set for overlay. + */ +Overlay* +IdentificationTextGenerator::getTopEnabledOverlay(OverlaySet* overlaySet) const +{ + CaretAssert(overlaySet); + const int32_t numberOfOverlays = overlaySet->getNumberOfDisplayedOverlays(); + for (int32_t i = 0; i < numberOfOverlays; i++) { + Overlay* overlay = overlaySet->getOverlay(i); + CaretAssert(overlay); + if (overlay->isEnabled()) { + return overlay; + } + } + return NULL; } /** diff --git a/src/Brain/IdentificationTextGenerator.h b/src/Brain/IdentificationTextGenerator.h index 8dbc10621..df3320e58 100644 --- a/src/Brain/IdentificationTextGenerator.h +++ b/src/Brain/IdentificationTextGenerator.h @@ -32,6 +32,8 @@ namespace caret { class ChartDataSource; class DataToolTipsManager; class MapFileDataSelector; + class Overlay; + class OverlaySet; class SelectionItemBorderSurface; class SelectionItemChartDataSeries; class SelectionItemChartFrequencySeries; @@ -144,6 +146,8 @@ namespace caret { void generateMapFileSelectorText(IdentificationStringBuilder& idText, const MapFileDataSelector* mapFileDataSelector) const; + Overlay* getTopEnabledOverlay(OverlaySet* overlaySet) const; + friend class DataToolTipsManager; }; diff --git a/src/GuiQt/BrainOpenGLWidget.cxx b/src/GuiQt/BrainOpenGLWidget.cxx index 0018e806b..90dff892b 100644 --- a/src/GuiQt/BrainOpenGLWidget.cxx +++ b/src/GuiQt/BrainOpenGLWidget.cxx @@ -715,8 +715,12 @@ BrainOpenGLWidget::event(QEvent* event) event->ignore(); } else { + const int millisecondsDisplayTime = (3 * 1000); QToolTip::showText(globalXY, - toolTipText); + toolTipText, + nullptr, + QRect(), + millisecondsDisplayTime); } return true; -- GitLab From 5979e3145c72a3c5777de4e97c8cd2232a2c8093 Mon Sep 17 00:00:00 2001 From: John Harwell Date: Wed, 1 May 2019 16:21:30 -0500 Subject: [PATCH 294/427] Added WuQMacroWidgetAction class that allows widgets in dialogs modal and non-modal dialogs (note: widgets may not exist when macro is run) to function with the macro system. --- src/Common/WuQMacroCommand.cxx | 3 + src/Common/WuQMacroDataValueTypeEnum.cxx | 2 +- src/Common/WuQMacroWidgetTypeEnum.cxx | 4 + src/Common/WuQMacroWidgetTypeEnum.h | 2 + src/GuiQt/CMakeLists.txt | 3 + src/GuiQt/SurfacePropertiesEditorDialog.cxx | 26 +- src/GuiQt/SurfacePropertiesEditorDialog.h | 5 +- src/GuiQt/WbMacroHelper.cxx | 61 ++++ src/GuiQt/WbMacroHelper.h | 9 + src/GuiQt/WuQMacroExecutor.cxx | 42 ++- src/GuiQt/WuQMacroExecutor.h | 5 + src/GuiQt/WuQMacroHelperInterface.h | 16 + src/GuiQt/WuQMacroManager.cxx | 29 ++ src/GuiQt/WuQMacroManager.h | 5 + src/GuiQt/WuQMacroSignalEmitter.cxx | 25 ++ src/GuiQt/WuQMacroSignalEmitter.h | 7 + src/GuiQt/WuQMacroSignalWatcher.cxx | 29 ++ src/GuiQt/WuQMacroSignalWatcher.h | 2 + src/GuiQt/WuQMacroWidgetAction.cxx | 338 ++++++++++++++++++++ src/GuiQt/WuQMacroWidgetAction.h | 141 ++++++++ 20 files changed, 748 insertions(+), 6 deletions(-) create mode 100644 src/GuiQt/WuQMacroWidgetAction.cxx create mode 100644 src/GuiQt/WuQMacroWidgetAction.h diff --git a/src/Common/WuQMacroCommand.cxx b/src/Common/WuQMacroCommand.cxx index 8caf70750..a5b590334 100644 --- a/src/Common/WuQMacroCommand.cxx +++ b/src/Common/WuQMacroCommand.cxx @@ -426,6 +426,9 @@ WuQMacroCommand::updateTitle() + " index " + QString::number(dataValueTwo.toInt())); break; + case WuQMacroWidgetTypeEnum::MACRO_WIDGET_ACTION: + title = "Set value"; + break; case WuQMacroWidgetTypeEnum::MENU: title = ("Select Name " + dataValue.toString() diff --git a/src/Common/WuQMacroDataValueTypeEnum.cxx b/src/Common/WuQMacroDataValueTypeEnum.cxx index aded8ebc0..4b40c1403 100644 --- a/src/Common/WuQMacroDataValueTypeEnum.cxx +++ b/src/Common/WuQMacroDataValueTypeEnum.cxx @@ -126,7 +126,7 @@ WuQMacroDataValueTypeEnum::initialize() "INTEGER", "Integer")); - enumData.push_back(WuQMacroDataValueTypeEnum(MOUSE, + enumData.push_back(WuQMacroDataValueTypeEnum(MOUSE, "MOUSE", "Mouse")); diff --git a/src/Common/WuQMacroWidgetTypeEnum.cxx b/src/Common/WuQMacroWidgetTypeEnum.cxx index a6b8631b1..d45ea0a87 100644 --- a/src/Common/WuQMacroWidgetTypeEnum.cxx +++ b/src/Common/WuQMacroWidgetTypeEnum.cxx @@ -146,6 +146,10 @@ WuQMacroWidgetTypeEnum::initialize() "LIST_WIDGET", "QListWidget")); + enumData.push_back(WuQMacroWidgetTypeEnum(MACRO_WIDGET_ACTION, + "MACRO_WIDGET_ACTION", + "caret::WuQMacroWidgetAction")); + enumData.push_back(WuQMacroWidgetTypeEnum(MENU, "MENU", "QMenu")); diff --git a/src/Common/WuQMacroWidgetTypeEnum.h b/src/Common/WuQMacroWidgetTypeEnum.h index 26692da40..e2951669d 100644 --- a/src/Common/WuQMacroWidgetTypeEnum.h +++ b/src/Common/WuQMacroWidgetTypeEnum.h @@ -55,6 +55,8 @@ public: LINE_EDIT, /** a QListWidget */ LIST_WIDGET, + /** a Macro Widget Action */ + MACRO_WIDGET_ACTION, /** a QMenu */ MENU, /** a QPushButton(not checkable) */ diff --git a/src/GuiQt/CMakeLists.txt b/src/GuiQt/CMakeLists.txt index 407d9fd1d..9182b1b96 100644 --- a/src/GuiQt/CMakeLists.txt +++ b/src/GuiQt/CMakeLists.txt @@ -220,6 +220,7 @@ ELSE() WuQMacroShortCutKeyComboBox.h WuQMacroSignalEmitter.h WuQMacroSignalWatcher.h + WuQMacroWidgetAction.h WuQMessageBox.h WuQwtPlot.h WuQSpecialIncrementDoubleSpinBox.h @@ -502,6 +503,7 @@ WuQMacroNewCommandSelectionDialog.h WuQMacroShortCutKeyComboBox.h WuQMacroSignalEmitter.h WuQMacroSignalWatcher.h +WuQMacroWidgetAction.h WuQMessageBox.h WuQwtPlot.h WuQSpecialIncrementDoubleSpinBox.h @@ -767,6 +769,7 @@ WuQMacroNewCommandSelectionDialog.cxx WuQMacroShortCutKeyComboBox.cxx WuQMacroSignalEmitter.cxx WuQMacroSignalWatcher.cxx +WuQMacroWidgetAction.cxx WuQMessageBox.cxx WuQwtPlot.cxx WuQSpecialIncrementDoubleSpinBox.cxx diff --git a/src/GuiQt/SurfacePropertiesEditorDialog.cxx b/src/GuiQt/SurfacePropertiesEditorDialog.cxx index 96baaf7ae..988a873b6 100644 --- a/src/GuiQt/SurfacePropertiesEditorDialog.cxx +++ b/src/GuiQt/SurfacePropertiesEditorDialog.cxx @@ -43,6 +43,8 @@ using namespace caret; #include "SceneClass.h" #include "SceneWindowGeometry.h" #include "WuQFactory.h" +#include "WuQMacroManager.h" +#include "WuQMacroWidgetAction.h" #include "WuQTrueFalseComboBox.h" #include "WuQtUtilities.h" @@ -91,12 +93,26 @@ SurfacePropertiesEditorDialog::SurfacePropertiesEditorDialog(QWidget* parent) this, SLOT(surfaceDisplayPropertyChanged())); QLabel* opacityLabel = new QLabel("Opacity: "); - m_opacitySpinBox = WuQFactory::newDoubleSpinBox(); + + m_opacityMacroWidgetAction = WuQMacroManager::instance()->getMacroWidgetActionByName("SurfaceProperties:surfaceOpacity"); + if (m_opacityMacroWidgetAction != NULL) { + QWidget* widget = m_opacityMacroWidgetAction->requestWidget(this); + if (widget != NULL) { + QDoubleSpinBox* dsb = qobject_cast(widget); + if (dsb != NULL) { + m_opacitySpinBox = dsb; + } + } + } + if (m_opacitySpinBox == NULL) { + m_opacityMacroWidgetAction = NULL; + m_opacitySpinBox = WuQFactory::newDoubleSpinBox(); + QObject::connect(m_opacitySpinBox, SIGNAL(valueChanged(double)), + this, SLOT(surfaceDisplayPropertyChanged())); + } m_opacitySpinBox->setRange(0.0, 1.0); m_opacitySpinBox->setSingleStep(0.1); m_opacitySpinBox->setDecimals(2); - QObject::connect(m_opacitySpinBox, SIGNAL(valueChanged(double)), - this, SLOT(surfaceDisplayPropertyChanged())); QWidget* w = new QWidget(); QGridLayout* gridLayout = new QGridLayout(w); @@ -138,6 +154,10 @@ SurfacePropertiesEditorDialog::SurfacePropertiesEditorDialog(QWidget* parent) SurfacePropertiesEditorDialog::~SurfacePropertiesEditorDialog() { EventManager::get()->removeAllEventsFromListener(this); + + if (m_opacityMacroWidgetAction != NULL) { + m_opacityMacroWidgetAction->releaseWidget(m_opacitySpinBox); + } } /** diff --git a/src/GuiQt/SurfacePropertiesEditorDialog.h b/src/GuiQt/SurfacePropertiesEditorDialog.h index 1f3d3dc24..c27447250 100644 --- a/src/GuiQt/SurfacePropertiesEditorDialog.h +++ b/src/GuiQt/SurfacePropertiesEditorDialog.h @@ -30,7 +30,8 @@ class QDoubleSpinBox; namespace caret { class EnumComboBoxTemplate; class WuQTrueFalseComboBox; - + class WuQMacroWidgetAction; + class SurfacePropertiesEditorDialog : public WuQDialogNonModal, public EventListenerInterface, public SceneableInterface { Q_OBJECT @@ -67,6 +68,8 @@ namespace caret { QDoubleSpinBox* m_opacitySpinBox; + WuQMacroWidgetAction* m_opacityMacroWidgetAction; + bool m_updateInProgress; // ADD_NEW_MEMBERS_HERE diff --git a/src/GuiQt/WbMacroHelper.cxx b/src/GuiQt/WbMacroHelper.cxx index 037f28c32..d54380c31 100644 --- a/src/GuiQt/WbMacroHelper.cxx +++ b/src/GuiQt/WbMacroHelper.cxx @@ -28,12 +28,14 @@ #include "CaretAssert.h" #include "CaretLogger.h" #include "CaretPreferences.h" +#include "DisplayPropertiesSurface.h" #include "EventCaretDataFilesGet.h" #include "EventGraphicsUpdateAllWindows.h" #include "EventGraphicsUpdateOneWindow.h" #include "EventManager.h" #include "EventMovieManualModeRecording.h" #include "EventSceneActive.h" +#include "EventSurfaceColoringInvalidate.h" #include "EventUserInterfaceUpdate.h" #include "GuiManager.h" #include "MovieRecorder.h" @@ -52,6 +54,7 @@ #include "WuQMacroGroup.h" #include "WuQMacroManager.h" #include "WuQMessageBox.h" +#include "WuQMacroWidgetAction.h" using namespace caret; @@ -452,3 +455,61 @@ WbMacroHelper::macroCommandAboutToStart(QWidget* window, } } +/** + * Called by macro manager to get macro widget actions typically + * used by modal dialogs. + * + * Override to provide macro widget actions. + * + * @return Vector containing the macro widget actions. + */ +std::vector +WbMacroHelper::getMacroWidgetActions() +{ + if (m_macroWidgetActions.empty()) { + WuQMacroWidgetAction* surfaceOpacityAction + = new WuQMacroWidgetAction(WuQMacroWidgetAction::WidgetType::SPIN_BOX_FLOAT, + "SurfaceProperties:surfaceOpacity", + "Set the surface opacity", + parent()); + m_macroWidgetActions.push_back(surfaceOpacityAction); + + QObject::connect(surfaceOpacityAction, &WuQMacroWidgetAction::setModelValue, + this, &WbMacroHelper::setModelOpacity); + QObject::connect(surfaceOpacityAction, &WuQMacroWidgetAction::getModelValue, + this, &WbMacroHelper::getModelOpacity); + } + + return m_macroWidgetActions; +} + +/** + * Set opacity in the model + * + * @param value + * New value for opacity + */ +void +WbMacroHelper::setModelOpacity(const QVariant& value) +{ + DisplayPropertiesSurface* dsp = GuiManager::get()->getBrain()->getDisplayPropertiesSurface(); + CaretAssert(dsp); + dsp->setOpacity(value.toFloat()); + + EventManager::get()->sendEvent(EventSurfaceColoringInvalidate().getPointer()); + EventManager::get()->sendEvent(EventGraphicsUpdateAllWindows().getPointer()); +} + +/** + * Set opacity in the model + * + * @param value + * Updated value for opacity + */ +void +WbMacroHelper::getModelOpacity(QVariant& value) +{ + DisplayPropertiesSurface* dsp = GuiManager::get()->getBrain()->getDisplayPropertiesSurface(); + CaretAssert(dsp); + value = dsp->getOpacity(); +} diff --git a/src/GuiQt/WbMacroHelper.h b/src/GuiQt/WbMacroHelper.h index 5b0c42333..34a6b5bdf 100644 --- a/src/GuiQt/WbMacroHelper.h +++ b/src/GuiQt/WbMacroHelper.h @@ -79,8 +79,15 @@ namespace caret { const WuQMacroExecutorOptions* executorOptions, bool& allowDelayFlagOut) override; + virtual std::vector getMacroWidgetActions(); + // ADD_NEW_METHODS_HERE + private slots: + void setModelOpacity(const QVariant& value); + + void getModelOpacity(QVariant& value); + private: void recordImagesForDelay(QWidget* window, @@ -89,6 +96,8 @@ namespace caret { MovieRecorderModeEnum::Enum m_savedRecordingMode = MovieRecorderModeEnum::MANUAL; + std::vector m_macroWidgetActions; + // ADD_NEW_MEMBERS_HERE }; diff --git a/src/GuiQt/WuQMacroExecutor.cxx b/src/GuiQt/WuQMacroExecutor.cxx index 2eef4f5df..cfbf515c0 100644 --- a/src/GuiQt/WuQMacroExecutor.cxx +++ b/src/GuiQt/WuQMacroExecutor.cxx @@ -55,6 +55,7 @@ #include "WuQMacroMouseEventInfo.h" #include "WuQMacroMouseEventWidgetInterface.h" #include "WuQMacroSignalEmitter.h" +#include "WuQMacroWidgetAction.h" using namespace caret; @@ -600,7 +601,10 @@ WuQMacroExecutor::runMacroCommand(QWidget* /*parentWidget*/, break; case WuQMacroWidgetTypeEnum::LIST_WIDGET: runListWidgetCommand(macroCommand, object, objectErrorMessage, notFoundFlag); - break; + break; + case WuQMacroWidgetTypeEnum::MACRO_WIDGET_ACTION: + runMacroWidgetActionCommand(macroCommand, object, objectErrorMessage, notFoundFlag); + break; case WuQMacroWidgetTypeEnum::MENU: runMenuCommand(macroCommand, object, objectErrorMessage, notFoundFlag); break; @@ -1096,6 +1100,42 @@ WuQMacroExecutor::runListWidgetCommand(const WuQMacroCommand* macroCommand, } } +/** + * Run a menu selection selection command + * + * @param macroCommand + * Macro command that is run + * @param object + * The object that is cast to specific object/widget + * @param errorMessageOut + * Error message from execution + * @param castFailureFlagOut + * Set to true if unable to cast object to widget type + */ +void +WuQMacroExecutor::runMacroWidgetActionCommand(const WuQMacroCommand* macroCommand, + QObject* object, + QString& errorMessageOut, + bool& castFailureFlagOut) const +{ + WuQMacroWidgetAction* macroWidgetAction = qobject_cast(object); + if (macroWidgetAction != NULL) { + CaretAssert(macroCommand->getNumberOfParameters() > 0); + const WuQMacroCommandParameter* parameterOne = macroCommand->getParameterAtIndex(0); + CaretAssert(parameterOne); + + CaretAssert(parameterOne->getDataType() == WuQMacroDataValueTypeEnum::STRING); + const QVariant dataValue = parameterOne->getValue(); + + WuQMacroSignalEmitter signalEmitter; + signalEmitter.emitMacroWidgetActionSignal(macroWidgetAction, + dataValue); + } + else { + castFailureFlagOut = true; + } +} + /** * Run a menu selection selection command * diff --git a/src/GuiQt/WuQMacroExecutor.h b/src/GuiQt/WuQMacroExecutor.h index d6d7338ec..7230492bb 100644 --- a/src/GuiQt/WuQMacroExecutor.h +++ b/src/GuiQt/WuQMacroExecutor.h @@ -161,6 +161,11 @@ namespace caret { QString& errorMessageOut, bool& castFailureFlagOut) const; + void runMacroWidgetActionCommand(const WuQMacroCommand* macroCommand, + QObject* object, + QString& errorMessageOut, + bool& castFailureFlagOut) const; + void runMenuCommand(const WuQMacroCommand* macroCommand, QObject* object, QString& errorMessageOut, diff --git a/src/GuiQt/WuQMacroHelperInterface.h b/src/GuiQt/WuQMacroHelperInterface.h index d6b102fcb..e7180fa5c 100644 --- a/src/GuiQt/WuQMacroHelperInterface.h +++ b/src/GuiQt/WuQMacroHelperInterface.h @@ -38,6 +38,7 @@ namespace caret { class WuQMacroCommand; class WuQMacroExecutorOptions; class WuQMacroGroup; + class WuQMacroWidgetAction; class WuQMacroHelperInterface : public QObject { Q_OBJECT @@ -171,6 +172,21 @@ namespace caret { const WuQMacroExecutorOptions* executorOptions, bool& allowDelayFlagOut) = 0; + + /** + * Called by macro manager to get macro widget actions typically + * used by modal dialogs. + * + * Override to provide macro widget actions. + * + * @return Vector containing the macro widget actions. + */ + virtual std::vector getMacroWidgetActions() + { + std::vector emptyActions; + return emptyActions; + } + // ADD_NEW_METHODS_HERE signals: diff --git a/src/GuiQt/WuQMacroManager.cxx b/src/GuiQt/WuQMacroManager.cxx index 37ddefdbd..f945916b9 100644 --- a/src/GuiQt/WuQMacroManager.cxx +++ b/src/GuiQt/WuQMacroManager.cxx @@ -52,6 +52,7 @@ #include "WuQMacroHelperInterface.h" #include "WuQMacroExecutorOptions.h" #include "WuQMacroSignalWatcher.h" +#include "WuQMacroWidgetAction.h" using namespace caret; @@ -146,6 +147,13 @@ WuQMacroManager::setMacroHelper(WuQMacroHelperInterface* macroHelper) if (macroHelper != NULL) { QObject::connect(macroHelper, &WuQMacroHelperInterface::requestDialogsUpdate, this, &WuQMacroManager::updateNonModalDialogs); + + m_macroWidgetActions = macroHelper->getMacroWidgetActions(); + + for (auto mwa : m_macroWidgetActions) { + addMacroSupportToObject(mwa, + mwa->getToolTip()); + } } } @@ -280,6 +288,10 @@ WuQMacroManager::addMacroSupportToObject(QObject* object, if (widget != NULL) { toolTipText = widget->toolTip(); } + WuQMacroWidgetAction* macroWidgetAction = qobject_cast(object); + if (macroWidgetAction != NULL) { + toolTipText = macroWidgetAction->getToolTip(); + } const bool resultFlag = addMacroSupportToObjectWithToolTip(object, descriptiveName, @@ -1520,3 +1532,20 @@ WuQMacroManager::getNewMacroDefaultName() const return name; } +/** + * @return The macro widget action with the given name or + * NULL if not found + * + * @param name + * Name of macro widget action + */ +WuQMacroWidgetAction* +WuQMacroManager::getMacroWidgetActionByName(const QString& name) +{ + for (auto mwa : m_macroWidgetActions) { + if (mwa->getName() == name) { + return mwa; + } + } + return NULL; +} diff --git a/src/GuiQt/WuQMacroManager.h b/src/GuiQt/WuQMacroManager.h index d142f201a..8200aff00 100644 --- a/src/GuiQt/WuQMacroManager.h +++ b/src/GuiQt/WuQMacroManager.h @@ -24,6 +24,7 @@ #include +#include #include #include @@ -51,6 +52,7 @@ namespace caret { class WuQMacroGroup; class WuQMacroHelperInterface; class WuQMacroSignalWatcher; + class WuQMacroWidgetAction; class WuQMacroManager : public QObject { Q_OBJECT @@ -187,6 +189,7 @@ namespace caret { QString getNewMacroDefaultName() const; + WuQMacroWidgetAction* getMacroWidgetActionByName(const QString& name); // ADD_NEW_METHODS_HERE @@ -231,6 +234,8 @@ namespace caret { WuQMacroCustomOperationManagerInterface* m_customCommandManager = NULL; + std::vector m_macroWidgetActions; + WuQMacroExecutor* m_macroExecutor = NULL; WuQMacroExecutorMonitor* m_macroExecutorMonitor = NULL; diff --git a/src/GuiQt/WuQMacroSignalEmitter.cxx b/src/GuiQt/WuQMacroSignalEmitter.cxx index bda4800fe..b952cc0f2 100644 --- a/src/GuiQt/WuQMacroSignalEmitter.cxx +++ b/src/GuiQt/WuQMacroSignalEmitter.cxx @@ -42,6 +42,7 @@ #include "CaretAssert.h" #include "CaretLogger.h" +#include "WuQMacroWidgetAction.h" using namespace caret; @@ -332,6 +333,30 @@ WuQMacroSignalEmitter::emitQLineEditSignal(QLineEdit* lineEdit, QObject::disconnect(this, &WuQMacroSignalEmitter::valueChangedSignalString, 0, 0); } +/** + * Update a Macro Widget Action's value and cause emission + * of its valueChanged() signal + * + * @param macroWidgetAction + * The Macro Widget Action + * @param value + * New value + */ +void +WuQMacroSignalEmitter::emitMacroWidgetActionSignal(WuQMacroWidgetAction* macroWidgetAction, + const QVariant& value) +{ + CaretAssert(macroWidgetAction); + macroWidgetAction->setDataValue(value); + + QObject::connect(this, & WuQMacroSignalEmitter::valueChangedSignalVariant, + macroWidgetAction, &WuQMacroWidgetAction::setModelValue); + + emit valueChangedSignalVariant(value); + + QObject::disconnect(this, & WuQMacroSignalEmitter::valueChangedSignalVariant, 0, 0); +} + /** * Select an item from a menu using the item's text * diff --git a/src/GuiQt/WuQMacroSignalEmitter.h b/src/GuiQt/WuQMacroSignalEmitter.h index ed185720c..0d58bb7d9 100644 --- a/src/GuiQt/WuQMacroSignalEmitter.h +++ b/src/GuiQt/WuQMacroSignalEmitter.h @@ -26,6 +26,7 @@ #include #include +#include class QAction; class QActionGroup; @@ -48,6 +49,8 @@ class QToolButton; namespace caret { + class WuQMacroWidgetAction; + class WuQMacroSignalEmitter : public QObject { Q_OBJECT @@ -85,6 +88,9 @@ namespace caret { void emitQListWidgetSignal(QListWidget* listWidget, const QString& text); + void emitMacroWidgetActionSignal(WuQMacroWidgetAction* macroWidgetAction, + const QVariant& value); + void emitQMenuSignal(QMenu* menu, const QString& text); @@ -128,6 +134,7 @@ namespace caret { void valueChangeSignalListWidgetItem(QListWidgetItem*); + void valueChangedSignalVariant(QVariant); private: // ADD_NEW_MEMBERS_HERE diff --git a/src/GuiQt/WuQMacroSignalWatcher.cxx b/src/GuiQt/WuQMacroSignalWatcher.cxx index 190623900..2bfe378b8 100644 --- a/src/GuiQt/WuQMacroSignalWatcher.cxx +++ b/src/GuiQt/WuQMacroSignalWatcher.cxx @@ -45,6 +45,7 @@ #include "WuQMacroCommand.h" #include "WuQMacroCommandParameter.h" #include "WuQMacroManager.h" +#include "WuQMacroWidgetAction.h" using namespace caret; @@ -206,6 +207,17 @@ m_objectName(object->objectName()) 1)); } break; + case WuQMacroWidgetTypeEnum::MACRO_WIDGET_ACTION: + { + WuQMacroWidgetAction* macroWidgetAction = qobject_cast(m_object); + CaretAssert(macroWidgetAction); + QObject::connect(macroWidgetAction, &WuQMacroWidgetAction::valueChanged, + this, &WuQMacroSignalWatcher::macroWidgetActionValueChanged); + m_objectParameters.push_back(new WuQMacroCommandParameter(WuQMacroDataValueTypeEnum::STRING, + "Data Value", + "")); + } + break; case WuQMacroWidgetTypeEnum::MENU: { QMenu* menu = qobject_cast(m_object); @@ -478,6 +490,8 @@ WuQMacroSignalWatcher::newInstance(WuQMacroManager* parentMacroManager, break; case WuQMacroWidgetTypeEnum::LIST_WIDGET: break; + case WuQMacroWidgetTypeEnum::MACRO_WIDGET_ACTION: + break; case WuQMacroWidgetTypeEnum::MENU: break; case WuQMacroWidgetTypeEnum::PUSH_BUTTON: @@ -852,6 +866,21 @@ WuQMacroSignalWatcher::lineEditTextEdited(const QString& text) createAndSendMacroCommand(params); } +/** + * Called when a macro widget action value is changed + * + * @param value + * New value + */ +void +WuQMacroSignalWatcher::macroWidgetActionValueChanged(const QVariant& value) +{ + std::vector params = getCopyOfObjectParameters(); + CaretAssert(params.size() >= 1); + params[0]->setValue(value); + createAndSendMacroCommand(params); +} + /** * Called when a list widget item is activated * diff --git a/src/GuiQt/WuQMacroSignalWatcher.h b/src/GuiQt/WuQMacroSignalWatcher.h index ce102a4ac..695babe60 100644 --- a/src/GuiQt/WuQMacroSignalWatcher.h +++ b/src/GuiQt/WuQMacroSignalWatcher.h @@ -83,6 +83,8 @@ namespace caret { void listWidgetItemActivated(QListWidgetItem*); + void macroWidgetActionValueChanged(const QVariant& value); + void menuTriggered(QAction* action); void pushButtonClicked(bool); diff --git a/src/GuiQt/WuQMacroWidgetAction.cxx b/src/GuiQt/WuQMacroWidgetAction.cxx new file mode 100644 index 000000000..96dc5f1c6 --- /dev/null +++ b/src/GuiQt/WuQMacroWidgetAction.cxx @@ -0,0 +1,338 @@ + +/*LICENSE_START*/ +/* + * Copyright (C) 2019 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + +#define __WU_Q_MACRO_WIDGET_ACTION_DECLARE__ +#include "WuQMacroWidgetAction.h" +#undef __WU_Q_MACRO_WIDGET_ACTION_DECLARE__ + +#include +#include +#include +#include +#include + +#include "CaretAssert.h" +#include "CaretLogger.h" +#include "EventManager.h" +using namespace caret; + + + +/** + * \class caret::WuQMacroWidgetAction + * \brief Like QWidgetAction but for use with widgets monitored by macro system but in dialogs + * \ingroup GuiQt + * + * Widgets in modal dialogs exist only while the dialog is active. Widgets in non-modal + * dialogs do not exist until the dialog is created. This presents a problem + * when the macro executor wants to trigger the widget's signal but the widget does not + * exist. This class is used by the macro system and an instance contains a signal corresponding + * to a widget in a modal dialog. + */ + + +/** + * Constructor. + * + * @param widgetType + * Type of widget + * @param objectName + * Name of this object + * @param objectToolTip + * Tooltip for object + * @param parent + * Parent for object + */ +WuQMacroWidgetAction::WuQMacroWidgetAction(const WidgetType widgetType, + const QString& objectName, + const QString& objectToolTip, + QObject* parent) +: QObject(parent), +m_widgetType(widgetType), +m_toolTip(objectToolTip) +{ + setObjectName(objectName); + + QObject::connect(this, &WuQMacroWidgetAction::valueChanged, + this, &WuQMacroWidgetAction::setValuePrivate); + +// EventManager::get()->addEventListener(this, EventTypeEnum::); +} + +/** + * Destructor. + */ +WuQMacroWidgetAction::~WuQMacroWidgetAction() +{ + EventManager::get()->removeAllEventsFromListener(this); +} + +/** + * @return The type of data value + */ +WuQMacroWidgetAction::WidgetType +WuQMacroWidgetAction::getWidgetType() const +{ + return m_widgetType; +} + +/** + * @return A widget the represents the action with the given parent. + * Caller is responsibled for deleting the widget and must call + * releaseWidget() prior to deleting the widget. + * + * @param Parent for the returned widget + */ +QWidget* +WuQMacroWidgetAction::requestWidget(QWidget* parent) +{ + QWidget* w(NULL); + + /* + * Create widget and attach to signal + */ + switch (m_widgetType) { + case WidgetType::CHECK_BOX_BOOLEAN: + { + QCheckBox* cb = new QCheckBox(parent); + QObject::connect(cb, &QCheckBox::clicked, + this, [=](bool checked) { emit valueChanged(checked); }); + w = cb; + } + break; + case WidgetType::COMBO_BOX_STRING_LIST: + { + QComboBox* cb = new QComboBox(parent); + QObject::connect(cb, QOverload::of(&QComboBox::activated), + this, [=](const QString& value) { emit valueChanged(value); } ); + w = cb; + } + break; + case WidgetType::LINE_EDIT_STRING: + { + QLineEdit* le = new QLineEdit(parent); + QObject::connect(le, QOverload::of(&QLineEdit::textEdited), + this, [=](const QString& value) { emit valueChanged(value); } ); + w = le; + } + break; + case WidgetType::SPIN_BOX_FLOAT: + { + QDoubleSpinBox* sb = new QDoubleSpinBox(parent); + QObject::connect(sb, QOverload::of(&QDoubleSpinBox::valueChanged), + this, [=](const double value) { emit valueChanged(static_cast(value)); } ); + w = sb; + } + break; + case WidgetType::SPIN_BOX_INTEGER: + { + QSpinBox* sb = new QSpinBox(parent); + QObject::connect(sb, QOverload::of(&QSpinBox::valueChanged), + this, [=](const int value) { emit valueChanged(value); } ); + w = sb; + } + break; + } + + CaretAssert(w); + + w->setToolTip(m_toolTip); + w->setObjectName(objectName() + + ":" + + w->metaObject()->className()); + + m_widgets.insert(w); + + updateWidgetWithModelValue(w); + + return w; +} + +/** + * Release the specified widget. Any signals are disconnected + * and this widget is no longer updated. The caller is + * responsible for deleting the widget. + */ +void +WuQMacroWidgetAction::releaseWidget(QWidget* widget) +{ + CaretAssert(widget); + + /* + * Disconnect all signals in widget from 'this' + */ + QObject::disconnect(widget, 0, + this, 0); + + m_widgets.erase(widget); +} + + +/** + * @return The tooltip + */ +QString +WuQMacroWidgetAction::getToolTip() const +{ + return m_toolTip; +} + +/** + * @return The name of the widget action + */ +QString +WuQMacroWidgetAction::getName() const +{ + return objectName(); +} + +/** + * Set the data value but DOES NOT emit valueChanged() signal + * + * @param value + * New data value for widget action + */ +void +WuQMacroWidgetAction::setDataValue(const QVariant& value) +{ + for (auto w : m_widgets) { + setWidgetValue(w, + value); + } +} + +/** + * Set the value and will emit the valueChanged signal + * + * @param value + * New value. + */ +void +WuQMacroWidgetAction::setValuePrivate(const QVariant& value) +{ + for (auto w : m_widgets) { + setWidgetValue(w, + value); + } + + emit setModelValue(value); +} + +/** + * Set a widget's value + * + * @param widget + * The widget + * @param value + * Value for the widget + */ +void +WuQMacroWidgetAction::setWidgetValue(QWidget* widget, + const QVariant& value) +{ + /* + * Some widgets, such as spin box, will emit a signal + * when its value is changed so block its signals + */ + QSignalBlocker sb(widget); + + switch (m_widgetType) { + case WidgetType::CHECK_BOX_BOOLEAN: + { + QCheckBox* cb = qobject_cast(widget); + CaretAssert(cb); + cb->setChecked(value.toBool()); + } + break; + case WidgetType::COMBO_BOX_STRING_LIST: + { + QComboBox* cb = qobject_cast(widget); + CaretAssert(cb); + cb->setCurrentText(value.toString()); + } + break; + case WidgetType::LINE_EDIT_STRING: + { + QLineEdit* le = qobject_cast(widget); + CaretAssert(le); + le->setText(value.toString()); + } + break; + case WidgetType::SPIN_BOX_FLOAT: + { + QDoubleSpinBox* sb = qobject_cast(widget); + CaretAssert(sb); + sb->setValue(value.toFloat()); + } + break; + case WidgetType::SPIN_BOX_INTEGER: + { + QSpinBox* sb = qobject_cast(widget); + CaretAssert(sb); + sb->setValue(value.toInt()); + } + break; + } +} + +/** + * May be called when a dialog is created or updated + * to update the model value in the given widget + * + * @param widget + * The widget + */ +void +WuQMacroWidgetAction::updateWidgetWithModelValue(QWidget* widget) +{ + CaretAssert(widget); + + QVariant value; + emit getModelValue(value); + + if (value.isNull()) { + const QString msg("Value was not updated by model, need to connect getModelValue() signal"); + CaretAssertMessage(0, msg); + CaretLogSevere(msg); + } + else { + setWidgetValue(widget, + value); + } +} + +/** + * Receive an event. + * + * @param event + * An event for which this instance is listening. + */ +void +WuQMacroWidgetAction::receiveEvent(Event* /*event*/) +{ +// if (event->getEventType() == EventTypeEnum::) { +// eventName = dynamic_cast(event); +// CaretAssert(eventName); +// +// event->setEventProcessed(); +// } +} + diff --git a/src/GuiQt/WuQMacroWidgetAction.h b/src/GuiQt/WuQMacroWidgetAction.h new file mode 100644 index 000000000..179d29508 --- /dev/null +++ b/src/GuiQt/WuQMacroWidgetAction.h @@ -0,0 +1,141 @@ +#ifndef __WU_Q_MACRO_WIDGET_ACTION_H__ +#define __WU_Q_MACRO_WIDGET_ACTION_H__ + +/*LICENSE_START*/ +/* + * Copyright (C) 2019 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + +#include +#include + +#include + +#include "EventListenerInterface.h" +#include "WuQMacroDataValueTypeEnum.h" + +namespace caret { + +// class ModelPropertySetGet { +// public: +// virtual QVariant getValue() const = 0; +// virtual void setValue(const QVariant& value) = 0; +// }; + + class WuQMacroWidgetAction : public QObject, public EventListenerInterface { + + Q_OBJECT + + public: + /** + * Type of widget + */ + enum class WidgetType { + /** CheckBox for boolean value */ + CHECK_BOX_BOOLEAN, + /** Combo Box for string list selection */ + COMBO_BOX_STRING_LIST, + /** Line edit for text */ + LINE_EDIT_STRING, + /** Spin box for float value */ + SPIN_BOX_FLOAT, + /** Spin box for integer value */ + SPIN_BOX_INTEGER + }; + + WuQMacroWidgetAction(const WidgetType widgetType, + const QString& objectName, + const QString& objectToolTip, + QObject* parent); + + static void initialize(std::map& namesAndTypes); + + static void destroy(); + + virtual ~WuQMacroWidgetAction(); + + WuQMacroWidgetAction(const WuQMacroWidgetAction&) = delete; + + WuQMacroWidgetAction& operator=(const WuQMacroWidgetAction&) = delete; + + QWidget* requestWidget(QWidget* parent); + + void releaseWidget(QWidget* widget); + + WidgetType getWidgetType() const; + + QString getName() const; + + QString getToolTip() const; + + // ADD_NEW_METHODS_HERE + + virtual void receiveEvent(Event* event); + + void setDataValue(const QVariant& value); + + void updateWidgetWithModelValue(QWidget* widget); + + signals: + /** + * Emitted when the value changes. Used by macro signal watcher. + * + * @param value + * The new value + */ + void valueChanged(const QVariant& value); + + /** + * Connect this signal to get the model's data value + * + * @param valueOut + * Reference for signal receiver to set with model's data value + */ + void getModelValue(QVariant& valueOut); + + /** + * Connect this signal to set the model's data value + * + * @param value + * New value for model + */ + void setModelValue(const QVariant& value); + + private slots: + void setValuePrivate(const QVariant& value); + + private: + void setWidgetValue(QWidget* widget, + const QVariant& value); + + const WidgetType m_widgetType; + + const QString m_toolTip; + + std::set m_widgets; + + // ADD_NEW_MEMBERS_HERE + + }; + +#ifdef __WU_Q_MACRO_WIDGET_ACTION_DECLARE__ + // +#endif // __WU_Q_MACRO_WIDGET_ACTION_DECLARE__ + +} // namespace +#endif //__WU_Q_MACRO_WIDGET_ACTION_H__ -- GitLab From bd2a8cd217561a6caf5ecc4863587290485c0efb Mon Sep 17 00:00:00 2001 From: John Harwell Date: Thu, 2 May 2019 12:27:45 -0500 Subject: [PATCH 295/427] Added some static methods to make it easier to issue command events such as update surface coloring, update user-interface, update graphics. --- src/GuiQt/GuiManager.cxx | 36 ++++++++++++++++++++++++++++++++++++ src/GuiQt/GuiManager.h | 8 ++++++++ 2 files changed, 44 insertions(+) diff --git a/src/GuiQt/GuiManager.cxx b/src/GuiQt/GuiManager.cxx index bebcd7ef7..ecab154ab 100644 --- a/src/GuiQt/GuiManager.cxx +++ b/src/GuiQt/GuiManager.cxx @@ -422,6 +422,42 @@ GuiManager::beep() QApplication::beep(); } +/** + * Send an event to update the user interface + */ +void +GuiManager::updateUserInterface() +{ + EventManager::get()->sendEvent(EventUserInterfaceUpdate().getPointer()); +} + +/** + * Send an event to update all graphics windows + */ +void +GuiManager::updateGraphicsAllWindows() +{ + EventManager::get()->sendEvent(EventGraphicsUpdateAllWindows().getPointer()); +} + +/** + * Send an event to update one graphics window + */ +void +GuiManager::updateGraphicsOneWindow(const int32_t windowIndex) +{ + EventManager::get()->sendEvent(EventGraphicsUpdateOneWindow(windowIndex).getPointer()); +} + +/** + * Send an event to update surface coloring + */ +void +GuiManager::updateSurfaceColoring() +{ + EventManager::get()->sendEvent(EventSurfaceColoringInvalidate().getPointer()); +} + /** * @return The brain. */ diff --git a/src/GuiQt/GuiManager.h b/src/GuiQt/GuiManager.h index 7d72d9a34..080280235 100644 --- a/src/GuiQt/GuiManager.h +++ b/src/GuiQt/GuiManager.h @@ -85,6 +85,14 @@ namespace caret { static void beep(); + static void updateUserInterface(); + + static void updateGraphicsAllWindows(); + + static void updateGraphicsOneWindow(const int32_t windowIndex); + + static void updateSurfaceColoring(); + Brain* getBrain() const; int32_t getNumberOfOpenBrainBrowserWindows() const; -- GitLab From 0902c76dde6809c38036c5d4e19b35200cecd6ee Mon Sep 17 00:00:00 2001 From: John Harwell Date: Thu, 2 May 2019 12:28:49 -0500 Subject: [PATCH 296/427] Improve creation and removal of dialog widgets associated with macro widget actions. --- src/GuiQt/CMakeLists.txt | 4 + src/GuiQt/SurfacePropertiesEditorDialog.cxx | 55 +++--- src/GuiQt/SurfacePropertiesEditorDialog.h | 4 - src/GuiQt/WbMacroHelper.cxx | 49 +---- src/GuiQt/WbMacroHelper.h | 9 +- src/GuiQt/WbMacroWidgetActionNames.h | 63 +++++++ src/GuiQt/WbMacroWidgetActionsManager.cxx | 192 ++++++++++++++++++++ src/GuiQt/WbMacroWidgetActionsManager.h | 84 +++++++++ src/GuiQt/WuQMacroManager.cxx | 52 ++++++ src/GuiQt/WuQMacroManager.h | 5 + src/GuiQt/WuQMacroWidgetAction.cxx | 31 +++- src/GuiQt/WuQMacroWidgetAction.h | 2 +- 12 files changed, 467 insertions(+), 83 deletions(-) create mode 100644 src/GuiQt/WbMacroWidgetActionNames.h create mode 100644 src/GuiQt/WbMacroWidgetActionsManager.cxx create mode 100644 src/GuiQt/WbMacroWidgetActionsManager.h diff --git a/src/GuiQt/CMakeLists.txt b/src/GuiQt/CMakeLists.txt index 9182b1b96..1f492edc9 100644 --- a/src/GuiQt/CMakeLists.txt +++ b/src/GuiQt/CMakeLists.txt @@ -195,6 +195,7 @@ ELSE() VolumeSurfaceOutlineSetViewController.h VolumeSurfaceOutlineViewController.h WbMacroHelper.h + WbMacroWidgetActionsManager.h WuQCollapsibleWidget.h WuQDataEntryDialog.h WuQDialog.h @@ -474,6 +475,8 @@ WbMacroCustomOperationIncrementVolumeSlice.h WbMacroCustomOperationManager.h WbMacroCustomOperationTypeEnum.h WbMacroHelper.h +WbMacroWidgetActionNames.h +WbMacroWidgetActionsManager.h WuQCollapsibleWidget.h WuQDataEntryDialog.h WuQDialog.h @@ -743,6 +746,7 @@ WbMacroCustomOperationIncrementVolumeSlice.cxx WbMacroCustomOperationManager.cxx WbMacroCustomOperationTypeEnum.cxx WbMacroHelper.cxx +WbMacroWidgetActionsManager.cxx WuQCollapsibleWidget.cxx WuQDataEntryDialog.cxx WuQDialog.cxx diff --git a/src/GuiQt/SurfacePropertiesEditorDialog.cxx b/src/GuiQt/SurfacePropertiesEditorDialog.cxx index 988a873b6..37f7e1ae8 100644 --- a/src/GuiQt/SurfacePropertiesEditorDialog.cxx +++ b/src/GuiQt/SurfacePropertiesEditorDialog.cxx @@ -45,6 +45,7 @@ using namespace caret; #include "WuQFactory.h" #include "WuQMacroManager.h" #include "WuQMacroWidgetAction.h" +#include "WbMacroWidgetActionNames.h" #include "WuQTrueFalseComboBox.h" #include "WuQtUtilities.h" @@ -63,6 +64,9 @@ SurfacePropertiesEditorDialog::SurfacePropertiesEditorDialog(QWidget* parent) { m_updateInProgress = true; + WuQMacroManager* mm = WuQMacroManager::instance(); + CaretAssert(mm); + QLabel* surfaceDrawingTypeLabel = new QLabel("Drawing Type: "); m_surfaceDrawingTypeComboBox = new EnumComboBoxTemplate(this); QObject::connect(m_surfaceDrawingTypeComboBox, SIGNAL(itemActivated()), @@ -70,7 +74,16 @@ SurfacePropertiesEditorDialog::SurfacePropertiesEditorDialog(QWidget* parent) m_surfaceDrawingTypeComboBox->setup(); QLabel* linkSizeLabel = new QLabel("Link Diameter: "); - m_linkSizeSpinBox = WuQFactory::newDoubleSpinBox(); + + QWidget* linkSizeMacroWidget = mm->getWidgetForMacroWidgetActionByName(WbMacroWidgetActionNames::getSurfacePropertiesLinkDiameterName()); + if (linkSizeMacroWidget != NULL) { + m_linkSizeSpinBox = qobject_cast(linkSizeMacroWidget); + CaretAssert(m_linkSizeSpinBox); + } + if (m_linkSizeSpinBox == NULL) { + m_linkSizeSpinBox = WuQFactory::newDoubleSpinBox(); + m_linkSizeSpinBox->setEnabled(false); + } m_linkSizeSpinBox->setRange(0.0, std::numeric_limits::max()); m_linkSizeSpinBox->setSingleStep(1.0); m_linkSizeSpinBox->setDecimals(1); @@ -79,7 +92,16 @@ SurfacePropertiesEditorDialog::SurfacePropertiesEditorDialog(QWidget* parent) this, SLOT(surfaceDisplayPropertyChanged())); QLabel* nodeSizeLabel = new QLabel("Vertex Diameter: "); - m_nodeSizeSpinBox = WuQFactory::newDoubleSpinBox(); + + QWidget* nodeSizeWidget = mm->getWidgetForMacroWidgetActionByName(WbMacroWidgetActionNames::getSurfacePropertiesVertexDiameterName()); + if (nodeSizeWidget != NULL) { + m_nodeSizeSpinBox = qobject_cast(nodeSizeWidget); + CaretAssert(m_nodeSizeSpinBox); + } + if (m_nodeSizeSpinBox == NULL) { + m_nodeSizeSpinBox = WuQFactory::newDoubleSpinBox(); + m_nodeSizeSpinBox->setEnabled(false); + } m_nodeSizeSpinBox->setRange(0.0, std::numeric_limits::max()); m_nodeSizeSpinBox->setSingleStep(1.0); m_nodeSizeSpinBox->setDecimals(1); @@ -94,21 +116,14 @@ SurfacePropertiesEditorDialog::SurfacePropertiesEditorDialog(QWidget* parent) QLabel* opacityLabel = new QLabel("Opacity: "); - m_opacityMacroWidgetAction = WuQMacroManager::instance()->getMacroWidgetActionByName("SurfaceProperties:surfaceOpacity"); - if (m_opacityMacroWidgetAction != NULL) { - QWidget* widget = m_opacityMacroWidgetAction->requestWidget(this); - if (widget != NULL) { - QDoubleSpinBox* dsb = qobject_cast(widget); - if (dsb != NULL) { - m_opacitySpinBox = dsb; - } - } + QWidget* opacityMacroWidget = mm->getWidgetForMacroWidgetActionByName(WbMacroWidgetActionNames::getSurfacePropertiesOpacityName()); + if (opacityMacroWidget != NULL) { + m_opacitySpinBox = qobject_cast(opacityMacroWidget); + CaretAssert(m_opacitySpinBox); } if (m_opacitySpinBox == NULL) { - m_opacityMacroWidgetAction = NULL; - m_opacitySpinBox = WuQFactory::newDoubleSpinBox(); - QObject::connect(m_opacitySpinBox, SIGNAL(valueChanged(double)), - this, SLOT(surfaceDisplayPropertyChanged())); + m_opacitySpinBox = new QDoubleSpinBox(); + m_opacitySpinBox->setEnabled(false); } m_opacitySpinBox->setRange(0.0, 1.0); m_opacitySpinBox->setSingleStep(0.1); @@ -155,9 +170,9 @@ SurfacePropertiesEditorDialog::~SurfacePropertiesEditorDialog() { EventManager::get()->removeAllEventsFromListener(this); - if (m_opacityMacroWidgetAction != NULL) { - m_opacityMacroWidgetAction->releaseWidget(m_opacitySpinBox); - } + WuQMacroManager::instance()->releaseWidgetFromMacroWidgetAction(m_linkSizeSpinBox); + WuQMacroManager::instance()->releaseWidgetFromMacroWidgetAction(m_nodeSizeSpinBox); + WuQMacroManager::instance()->releaseWidgetFromMacroWidgetAction(m_opacitySpinBox); } /** @@ -180,7 +195,7 @@ SurfacePropertiesEditorDialog::surfaceDisplayPropertyChanged() dps->setDisplayNormalVectors(m_displayNormalVectorsComboBox->isTrue()); dps->setLinkSize(m_linkSizeSpinBox->value()); dps->setNodeSize(m_nodeSizeSpinBox->value()); - dps->setOpacity(m_opacitySpinBox->value()); +// dps->setOpacity(m_opacitySpinBox->value()); EventManager::get()->sendEvent(EventSurfaceColoringInvalidate().getPointer()); EventManager::get()->sendEvent(EventGraphicsUpdateAllWindows().getPointer()); @@ -277,5 +292,3 @@ SurfacePropertiesEditorDialog::restoreFromScene(const SceneAttributes* sceneAttr swg.restoreFromScene(sceneAttributes, sceneClass->getClass("geometry")); } - - diff --git a/src/GuiQt/SurfacePropertiesEditorDialog.h b/src/GuiQt/SurfacePropertiesEditorDialog.h index c27447250..1cc948499 100644 --- a/src/GuiQt/SurfacePropertiesEditorDialog.h +++ b/src/GuiQt/SurfacePropertiesEditorDialog.h @@ -30,7 +30,6 @@ class QDoubleSpinBox; namespace caret { class EnumComboBoxTemplate; class WuQTrueFalseComboBox; - class WuQMacroWidgetAction; class SurfacePropertiesEditorDialog : public WuQDialogNonModal, public EventListenerInterface, public SceneableInterface { Q_OBJECT @@ -68,8 +67,6 @@ namespace caret { QDoubleSpinBox* m_opacitySpinBox; - WuQMacroWidgetAction* m_opacityMacroWidgetAction; - bool m_updateInProgress; // ADD_NEW_MEMBERS_HERE @@ -77,7 +74,6 @@ namespace caret { }; #ifdef __SURFACE_PROPERTIES_EDITOR_DIALOG_DECLARE__ - // #endif // __SURFACE_PROPERTIES_EDITOR_DIALOG_DECLARE__ } // namespace diff --git a/src/GuiQt/WbMacroHelper.cxx b/src/GuiQt/WbMacroHelper.cxx index d54380c31..7c84c77c2 100644 --- a/src/GuiQt/WbMacroHelper.cxx +++ b/src/GuiQt/WbMacroHelper.cxx @@ -46,6 +46,7 @@ #include "SessionManager.h" #include "WbMacroCustomOperationTypeEnum.h" #include "WbMacroCustomDataTypeEnum.h" +#include "WbMacroWidgetActionsManager.h" #include "WuQMacro.h" #include "WuQMacroCommand.h" #include "WuQMacroCommandParameter.h" @@ -466,50 +467,10 @@ WbMacroHelper::macroCommandAboutToStart(QWidget* window, std::vector WbMacroHelper::getMacroWidgetActions() { - if (m_macroWidgetActions.empty()) { - WuQMacroWidgetAction* surfaceOpacityAction - = new WuQMacroWidgetAction(WuQMacroWidgetAction::WidgetType::SPIN_BOX_FLOAT, - "SurfaceProperties:surfaceOpacity", - "Set the surface opacity", - parent()); - m_macroWidgetActions.push_back(surfaceOpacityAction); - - QObject::connect(surfaceOpacityAction, &WuQMacroWidgetAction::setModelValue, - this, &WbMacroHelper::setModelOpacity); - QObject::connect(surfaceOpacityAction, &WuQMacroWidgetAction::getModelValue, - this, &WbMacroHelper::getModelOpacity); + if (m_macroWidgetActionsManager == NULL) { + m_macroWidgetActionsManager = new WbMacroWidgetActionsManager(this); } - - return m_macroWidgetActions; -} - -/** - * Set opacity in the model - * - * @param value - * New value for opacity - */ -void -WbMacroHelper::setModelOpacity(const QVariant& value) -{ - DisplayPropertiesSurface* dsp = GuiManager::get()->getBrain()->getDisplayPropertiesSurface(); - CaretAssert(dsp); - dsp->setOpacity(value.toFloat()); - - EventManager::get()->sendEvent(EventSurfaceColoringInvalidate().getPointer()); - EventManager::get()->sendEvent(EventGraphicsUpdateAllWindows().getPointer()); + CaretAssert(m_macroWidgetActionsManager); + return m_macroWidgetActionsManager->getMacroWidgetActions(); } -/** - * Set opacity in the model - * - * @param value - * Updated value for opacity - */ -void -WbMacroHelper::getModelOpacity(QVariant& value) -{ - DisplayPropertiesSurface* dsp = GuiManager::get()->getBrain()->getDisplayPropertiesSurface(); - CaretAssert(dsp); - value = dsp->getOpacity(); -} diff --git a/src/GuiQt/WbMacroHelper.h b/src/GuiQt/WbMacroHelper.h index 34a6b5bdf..d72fd490c 100644 --- a/src/GuiQt/WbMacroHelper.h +++ b/src/GuiQt/WbMacroHelper.h @@ -32,6 +32,8 @@ class QWidget; namespace caret { + class WbMacroWidgetActionsManager; + class WbMacroHelper : public WuQMacroHelperInterface, public EventListenerInterface { Q_OBJECT @@ -83,11 +85,6 @@ namespace caret { // ADD_NEW_METHODS_HERE - private slots: - void setModelOpacity(const QVariant& value); - - void getModelOpacity(QVariant& value); - private: void recordImagesForDelay(QWidget* window, @@ -96,7 +93,7 @@ namespace caret { MovieRecorderModeEnum::Enum m_savedRecordingMode = MovieRecorderModeEnum::MANUAL; - std::vector m_macroWidgetActions; + WbMacroWidgetActionsManager* m_macroWidgetActionsManager = NULL; // ADD_NEW_MEMBERS_HERE diff --git a/src/GuiQt/WbMacroWidgetActionNames.h b/src/GuiQt/WbMacroWidgetActionNames.h new file mode 100644 index 000000000..cad6534bb --- /dev/null +++ b/src/GuiQt/WbMacroWidgetActionNames.h @@ -0,0 +1,63 @@ +#ifndef __WB_MACRO_WIDGET_ACTION_NAMES_H__ +#define __WB_MACRO_WIDGET_ACTION_NAMES_H__ + +/*LICENSE_START*/ +/* + * Copyright (C) 2019 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + + +#include + +namespace caret { + + class WbMacroWidgetActionNames { + + public: + /* + * NOTE: These names are saved into scene and changing the names will break scenes + */ + + static QString getSurfacePropertiesOpacityName() { return "SurfaceProperties:surfaceOpacity"; } + + static QString getSurfacePropertiesLinkDiameterName() { return "SurfaceProperties:linkDiameter"; } + + static QString getSurfacePropertiesVertexDiameterName() { return "SurfaceProperties:vertexDiameter"; } + + WbMacroWidgetActionNames() = delete; + + virtual ~WbMacroWidgetActionNames() = delete; + + WbMacroWidgetActionNames(const WbMacroWidgetActionNames&) = delete; + + WbMacroWidgetActionNames& operator=(const WbMacroWidgetActionNames&) = delete; + + + // ADD_NEW_METHODS_HERE + + private: + // ADD_NEW_MEMBERS_HERE + + }; + +#ifdef __WB_MACRO_WIDGET_ACTION_NAMES_DECLARE__ + // +#endif // __WB_MACRO_WIDGET_ACTION_NAMES_DECLARE__ + +} // namespace +#endif //__WB_MACRO_WIDGET_ACTION_NAMES_H__ diff --git a/src/GuiQt/WbMacroWidgetActionsManager.cxx b/src/GuiQt/WbMacroWidgetActionsManager.cxx new file mode 100644 index 000000000..0b4984f03 --- /dev/null +++ b/src/GuiQt/WbMacroWidgetActionsManager.cxx @@ -0,0 +1,192 @@ + +/*LICENSE_START*/ +/* + * Copyright (C) 2019 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + +#define __WB_MACRO_WIDGET_ACTIONS_MANAGER_DECLARE__ +#include "WbMacroWidgetActionsManager.h" +#undef __WB_MACRO_WIDGET_ACTIONS_MANAGER_DECLARE__ + +#include + +#include "CaretAssert.h" +#include "Brain.h" +#include "DisplayPropertiesSurface.h" +#include "GuiManager.h" +#include "WbMacroWidgetActionNames.h" +#include "WuQMacroWidgetAction.h" + +using namespace caret; + + + +/** + * \class caret::WbMacroWidgetActionsManager + * \brief Manager for macro widget actions used by workbench + * \ingroup GuiQt + */ + +/** + * Constructor. + * + * @param parent + * The parent object + */ +WbMacroWidgetActionsManager::WbMacroWidgetActionsManager(QObject* parent) +: QObject(parent) +{ + +} + +/** + * Destructor. + */ +WbMacroWidgetActionsManager::~WbMacroWidgetActionsManager() +{ +} + +/** + * @return All macro widget actions used by workbench + */ +std::vector +WbMacroWidgetActionsManager::getMacroWidgetActions() +{ + if (m_macroWidgetActions.empty()) { + m_surfacePropertiesLinkDiameterWidgetAction = createSurfacePropertiesLinkDiameterWidgetAction(); + m_macroWidgetActions.push_back(m_surfacePropertiesLinkDiameterWidgetAction); + + m_surfacePropertiesOpacityWidgetAction = createSurfacePropertiesOpacityWidgetAction(); + m_macroWidgetActions.push_back(m_surfacePropertiesOpacityWidgetAction); + + m_surfacePropertiesVertexDiameterWidgetAction = createSurfacePropertiesVertexDiameterWidgetAction(); + m_macroWidgetActions.push_back(m_surfacePropertiesVertexDiameterWidgetAction); + } + + return m_macroWidgetActions; +} + +/** + * @return New instance of surface opacity widget action + */ +WuQMacroWidgetAction* +WbMacroWidgetActionsManager::createSurfacePropertiesOpacityWidgetAction() +{ + WuQMacroWidgetAction* action = new WuQMacroWidgetAction(WuQMacroWidgetAction::WidgetType::SPIN_BOX_FLOAT, + WbMacroWidgetActionNames::getSurfacePropertiesOpacityName(), + "Set the surface opacity", + parent()); + + DisplayPropertiesSurface* dsp = GuiManager::get()->getBrain()->getDisplayPropertiesSurface(); + + QObject::connect(action, &WuQMacroWidgetAction::getModelValue, + this, [=](QVariant& value) { + value = dsp->getOpacity(); + }); + + QObject::connect(action, &WuQMacroWidgetAction::setModelValue, + this, [=](const QVariant& value) { + GuiManager::get()->getBrain()->getDisplayPropertiesSurface()->setOpacity(value.toFloat()); + GuiManager::updateSurfaceColoring(); + GuiManager::updateGraphicsAllWindows(); + }); + + return action; +} + +/** + * @return The surface opacity widget action + */ +WuQMacroWidgetAction* +WbMacroWidgetActionsManager::getSurfacePropertiesOpacityWidgetAction() +{ + CaretAssert(m_surfacePropertiesOpacityWidgetAction); + return m_surfacePropertiesOpacityWidgetAction; +} + +/** + * @return The surface link diameter widget action + */ +WuQMacroWidgetAction* +WbMacroWidgetActionsManager::getSurfacePropertiesLinkDiameterWidgetAction() +{ + return m_surfacePropertiesLinkDiameterWidgetAction; +} + +/** + * @return New instance of surface link diameter widget action + */ +WuQMacroWidgetAction* +WbMacroWidgetActionsManager::createSurfacePropertiesLinkDiameterWidgetAction() +{ + WuQMacroWidgetAction* action = new WuQMacroWidgetAction(WuQMacroWidgetAction::WidgetType::SPIN_BOX_FLOAT, + WbMacroWidgetActionNames::getSurfacePropertiesLinkDiameterName(), + "Set the link (edge) diameter", + parent()); + + DisplayPropertiesSurface* dsp = GuiManager::get()->getBrain()->getDisplayPropertiesSurface(); + + QObject::connect(action, &WuQMacroWidgetAction::getModelValue, + this, [=](QVariant& value) { + value = dsp->getLinkSize(); + }); + + QObject::connect(action, &WuQMacroWidgetAction::setModelValue, + this, [=](const QVariant& value) { + GuiManager::get()->getBrain()->getDisplayPropertiesSurface()->setLinkSize(value.toFloat()); + GuiManager::updateGraphicsAllWindows(); + }); + + return action; +} + +/** + * @return New instance of surface vertex diameter widget action + */ +WuQMacroWidgetAction* +WbMacroWidgetActionsManager::createSurfacePropertiesVertexDiameterWidgetAction() +{ + WuQMacroWidgetAction* action = new WuQMacroWidgetAction(WuQMacroWidgetAction::WidgetType::SPIN_BOX_FLOAT, + WbMacroWidgetActionNames::getSurfacePropertiesVertexDiameterName(), + "Set the vertex diameter", + parent()); + + DisplayPropertiesSurface* dsp = GuiManager::get()->getBrain()->getDisplayPropertiesSurface(); + + QObject::connect(action, &WuQMacroWidgetAction::getModelValue, + this, [=](QVariant& value) { + value = dsp->getNodeSize(); + }); + + QObject::connect(action, &WuQMacroWidgetAction::setModelValue, + this, [=](const QVariant& value) { + GuiManager::get()->getBrain()->getDisplayPropertiesSurface()->setNodeSize(value.toFloat()); + GuiManager::updateGraphicsAllWindows(); + }); + + return action; +} + +/** + * @return The surface link diameter widget action + */ +WuQMacroWidgetAction* +WbMacroWidgetActionsManager::getSurfacePropertiesVertexDiameterWidgetAction() +{ + return m_surfacePropertiesVertexDiameterWidgetAction; +} diff --git a/src/GuiQt/WbMacroWidgetActionsManager.h b/src/GuiQt/WbMacroWidgetActionsManager.h new file mode 100644 index 000000000..a929d2c3c --- /dev/null +++ b/src/GuiQt/WbMacroWidgetActionsManager.h @@ -0,0 +1,84 @@ +#ifndef __WB_MACRO_WIDGET_ACTIONS_MANAGER_H__ +#define __WB_MACRO_WIDGET_ACTIONS_MANAGER_H__ + +/*LICENSE_START*/ +/* + * Copyright (C) 2019 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + + + +#include + +#include + + + +namespace caret { + + class WuQMacroWidgetAction; + + class WbMacroWidgetActionsManager : public QObject { + + Q_OBJECT + + public: + WbMacroWidgetActionsManager(QObject* parent); + + virtual ~WbMacroWidgetActionsManager(); + + + WbMacroWidgetActionsManager(const WbMacroWidgetActionsManager&) = delete; + + WbMacroWidgetActionsManager& operator=(const WbMacroWidgetActionsManager&) = delete; + + std::vector getMacroWidgetActions(); + + WuQMacroWidgetAction* getSurfacePropertiesLinkDiameterWidgetAction(); + + WuQMacroWidgetAction* getSurfacePropertiesOpacityWidgetAction(); + + WuQMacroWidgetAction* getSurfacePropertiesVertexDiameterWidgetAction(); + + // ADD_NEW_METHODS_HERE + + private: + std::vector m_macroWidgetActions; + + WuQMacroWidgetAction* createSurfacePropertiesOpacityWidgetAction(); + + WuQMacroWidgetAction* m_surfacePropertiesOpacityWidgetAction; + + WuQMacroWidgetAction* createSurfacePropertiesLinkDiameterWidgetAction(); + + WuQMacroWidgetAction* m_surfacePropertiesLinkDiameterWidgetAction; + + WuQMacroWidgetAction* createSurfacePropertiesVertexDiameterWidgetAction(); + + WuQMacroWidgetAction* m_surfacePropertiesVertexDiameterWidgetAction; + + // ADD_NEW_MEMBERS_HERE + + }; + +#ifdef __WB_MACRO_WIDGET_ACTIONS_MANAGER_DECLARE__ + // +#endif // __WB_MACRO_WIDGET_ACTIONS_MANAGER_DECLARE__ + +} // namespace +#endif //__WB_MACRO_WIDGET_ACTIONS_MANAGER_H__ diff --git a/src/GuiQt/WuQMacroManager.cxx b/src/GuiQt/WuQMacroManager.cxx index f945916b9..7648284df 100644 --- a/src/GuiQt/WuQMacroManager.cxx +++ b/src/GuiQt/WuQMacroManager.cxx @@ -1549,3 +1549,55 @@ WuQMacroManager::getMacroWidgetActionByName(const QString& name) } return NULL; } + +/** + * Get a widget for the macro widget action with the given name + * + * @param name + * Name of the macro widget action + * @param parentWidget + * Optional parent widget for the returned widget + * @return + * Widget associated with the widget macro action or NULL if failure. + */ +QWidget* +WuQMacroManager::getWidgetForMacroWidgetActionByName(const QString& name, + QWidget* parentWidget) +{ + QWidget* widget(NULL); + WuQMacroWidgetAction* mwa = getMacroWidgetActionByName(name); + if (mwa != NULL) { + widget = mwa->requestWidget(parentWidget); + if (widget == NULL) { + const QString msg("Failed to create widget for macro widget action with name \"" + + name + + "."); + CaretAssertMessage(0, msg); + CaretLogSevere(msg); + } + } + else { + const QString msg("No macro widget action with name \"" + + name + + "\" was found."); + CaretAssertMessage(0, msg); + CaretLogSevere(msg); + } + + return widget; +} + +/** + * Release the widget from its associated macro widget action. + * Note: The widget is NOT destroyed as that is the responsibility + * of the widget owner (typically a dialog). + */ +void +WuQMacroManager::releaseWidgetFromMacroWidgetAction(QWidget* widget) +{ + CaretAssert(widget); + + for (auto mwa : m_macroWidgetActions) { + mwa->releaseWidget(widget); + } +} diff --git a/src/GuiQt/WuQMacroManager.h b/src/GuiQt/WuQMacroManager.h index 8200aff00..c6ff03e1a 100644 --- a/src/GuiQt/WuQMacroManager.h +++ b/src/GuiQt/WuQMacroManager.h @@ -191,6 +191,11 @@ namespace caret { WuQMacroWidgetAction* getMacroWidgetActionByName(const QString& name); + QWidget* getWidgetForMacroWidgetActionByName(const QString& name, + QWidget* parentWidget = 0); + + void releaseWidgetFromMacroWidgetAction(QWidget* widget); + // ADD_NEW_METHODS_HERE public slots: diff --git a/src/GuiQt/WuQMacroWidgetAction.cxx b/src/GuiQt/WuQMacroWidgetAction.cxx index 96dc5f1c6..f648be248 100644 --- a/src/GuiQt/WuQMacroWidgetAction.cxx +++ b/src/GuiQt/WuQMacroWidgetAction.cxx @@ -170,22 +170,39 @@ WuQMacroWidgetAction::requestWidget(QWidget* parent) * Release the specified widget. Any signals are disconnected * and this widget is no longer updated. The caller is * responsible for deleting the widget. + * + * It is okay to call this method with a widget + * that belongs to another macro widget action + * and in this case no action is taken. + * + * @return + * True if the widget was monitored by this + * macro widget action, otherwise false. */ -void +bool WuQMacroWidgetAction::releaseWidget(QWidget* widget) { CaretAssert(widget); + if (m_widgets.find(widget) != m_widgets.end()) { + /* + * Disconnect all signals in widget from 'this' + */ + QObject::disconnect(widget, 0, + this, 0); + + m_widgets.erase(widget); + + std::cout << "Released widget: " << widget->objectName() << std::endl; + return true; + } + /* - * Disconnect all signals in widget from 'this' + * If here, widget was not in this macro widget action */ - QObject::disconnect(widget, 0, - this, 0); - - m_widgets.erase(widget); + return false; } - /** * @return The tooltip */ diff --git a/src/GuiQt/WuQMacroWidgetAction.h b/src/GuiQt/WuQMacroWidgetAction.h index 179d29508..385c3f092 100644 --- a/src/GuiQt/WuQMacroWidgetAction.h +++ b/src/GuiQt/WuQMacroWidgetAction.h @@ -75,7 +75,7 @@ namespace caret { QWidget* requestWidget(QWidget* parent); - void releaseWidget(QWidget* widget); + bool releaseWidget(QWidget* widget); WidgetType getWidgetType() const; -- GitLab From c2f4933c28053b73bb1f921b81ae7b3278923651 Mon Sep 17 00:00:00 2001 From: John Harwell Date: Thu, 2 May 2019 15:58:35 -0500 Subject: [PATCH 297/427] Add more macro support for widgets on surface properties dialog and improvements in creation and management of macro widget actions. --- src/GuiQt/SurfacePropertiesEditorDialog.cxx | 41 +++-- src/GuiQt/SurfacePropertiesEditorDialog.h | 3 +- src/GuiQt/WbMacroWidgetActionNames.h | 4 + src/GuiQt/WbMacroWidgetActionsManager.cxx | 174 ++++++++++---------- src/GuiQt/WbMacroWidgetActionsManager.h | 14 +- src/GuiQt/WuQMacroManager.cxx | 42 ++++- src/GuiQt/WuQMacroManager.h | 15 +- src/GuiQt/WuQMacroWidgetAction.cxx | 32 ++-- src/GuiQt/WuQMacroWidgetAction.h | 2 +- 9 files changed, 193 insertions(+), 134 deletions(-) diff --git a/src/GuiQt/SurfacePropertiesEditorDialog.cxx b/src/GuiQt/SurfacePropertiesEditorDialog.cxx index 37f7e1ae8..8b02d2b1b 100644 --- a/src/GuiQt/SurfacePropertiesEditorDialog.cxx +++ b/src/GuiQt/SurfacePropertiesEditorDialog.cxx @@ -25,6 +25,7 @@ #include +#include #include #include #include @@ -88,8 +89,6 @@ SurfacePropertiesEditorDialog::SurfacePropertiesEditorDialog(QWidget* parent) m_linkSizeSpinBox->setSingleStep(1.0); m_linkSizeSpinBox->setDecimals(1); m_linkSizeSpinBox->setSuffix("mm"); - QObject::connect(m_linkSizeSpinBox, SIGNAL(valueChanged(double)), - this, SLOT(surfaceDisplayPropertyChanged())); QLabel* nodeSizeLabel = new QLabel("Vertex Diameter: "); @@ -106,13 +105,17 @@ SurfacePropertiesEditorDialog::SurfacePropertiesEditorDialog(QWidget* parent) m_nodeSizeSpinBox->setSingleStep(1.0); m_nodeSizeSpinBox->setDecimals(1); m_nodeSizeSpinBox->setSuffix("mm"); - QObject::connect(m_nodeSizeSpinBox, SIGNAL(valueChanged(double)), - this, SLOT(surfaceDisplayPropertyChanged())); - QLabel* displayNormalVectorsLabel = new QLabel("Display Normal Vectors: "); - m_displayNormalVectorsComboBox = new WuQTrueFalseComboBox(this); - QObject::connect(m_displayNormalVectorsComboBox, SIGNAL(statusChanged(bool)), - this, SLOT(surfaceDisplayPropertyChanged())); + QWidget* displayNormalsWidget = mm->getWidgetForMacroWidgetActionByName(WbMacroWidgetActionNames::getSurfacePropertiesDisplayNormalVectorsName()); + if (displayNormalsWidget != NULL) { + m_displayNormalVectorsCheckBox = qobject_cast(displayNormalsWidget); + CaretAssert(m_displayNormalVectorsCheckBox); + } + if (m_displayNormalVectorsCheckBox == NULL) { + m_displayNormalVectorsCheckBox = new QCheckBox(); + m_displayNormalVectorsCheckBox->setEnabled(false); + } + m_displayNormalVectorsCheckBox->setText("Display Normal Vectors"); QLabel* opacityLabel = new QLabel("Opacity: "); @@ -133,8 +136,7 @@ SurfacePropertiesEditorDialog::SurfacePropertiesEditorDialog(QWidget* parent) QGridLayout* gridLayout = new QGridLayout(w); WuQtUtilities::setLayoutSpacingAndMargins(gridLayout, 2, 2); int row = gridLayout->rowCount(); - gridLayout->addWidget(displayNormalVectorsLabel, row, 0); - gridLayout->addWidget(m_displayNormalVectorsComboBox->getWidget(), row, 1); + gridLayout->addWidget(m_displayNormalVectorsCheckBox, row, 0, 1, 2); row++; gridLayout->addWidget(surfaceDrawingTypeLabel, row, 0); gridLayout->addWidget(m_surfaceDrawingTypeComboBox->getWidget(), row, 1); @@ -170,9 +172,10 @@ SurfacePropertiesEditorDialog::~SurfacePropertiesEditorDialog() { EventManager::get()->removeAllEventsFromListener(this); - WuQMacroManager::instance()->releaseWidgetFromMacroWidgetAction(m_linkSizeSpinBox); - WuQMacroManager::instance()->releaseWidgetFromMacroWidgetAction(m_nodeSizeSpinBox); - WuQMacroManager::instance()->releaseWidgetFromMacroWidgetAction(m_opacitySpinBox); + WuQMacroManager::instance()->releaseWidgetFromMacroWidgetAction(m_linkSizeSpinBox, + m_nodeSizeSpinBox, + m_opacitySpinBox, + m_displayNormalVectorsCheckBox); } /** @@ -192,10 +195,6 @@ SurfacePropertiesEditorDialog::surfaceDisplayPropertyChanged() DisplayPropertiesSurface* dps = GuiManager::get()->getBrain()->getDisplayPropertiesSurface(); dps->setSurfaceDrawingType(surfaceDrawingType); - dps->setDisplayNormalVectors(m_displayNormalVectorsComboBox->isTrue()); - dps->setLinkSize(m_linkSizeSpinBox->value()); - dps->setNodeSize(m_nodeSizeSpinBox->value()); -// dps->setOpacity(m_opacitySpinBox->value()); EventManager::get()->sendEvent(EventSurfaceColoringInvalidate().getPointer()); EventManager::get()->sendEvent(EventGraphicsUpdateAllWindows().getPointer()); @@ -212,10 +211,10 @@ SurfacePropertiesEditorDialog::updateDialog() const DisplayPropertiesSurface* dps = GuiManager::get()->getBrain()->getDisplayPropertiesSurface(); m_surfaceDrawingTypeComboBox->setSelectedItem(dps->getSurfaceDrawingType()); - m_displayNormalVectorsComboBox->setStatus(dps->isDisplayNormalVectors()); - m_linkSizeSpinBox->setValue(dps->getLinkSize()); - m_nodeSizeSpinBox->setValue(dps->getNodeSize()); - m_opacitySpinBox->setValue(dps->getOpacity()); + WuQMacroManager::instance()->updateValueInWidgetFromMacroWidgetAction(m_linkSizeSpinBox, + m_nodeSizeSpinBox, + m_opacitySpinBox, + m_displayNormalVectorsCheckBox); m_updateInProgress = false; } diff --git a/src/GuiQt/SurfacePropertiesEditorDialog.h b/src/GuiQt/SurfacePropertiesEditorDialog.h index 1cc948499..ae02dbb1c 100644 --- a/src/GuiQt/SurfacePropertiesEditorDialog.h +++ b/src/GuiQt/SurfacePropertiesEditorDialog.h @@ -25,6 +25,7 @@ #include "SceneableInterface.h" #include "WuQDialogNonModal.h" +class QCheckBox; class QDoubleSpinBox; namespace caret { @@ -57,7 +58,7 @@ namespace caret { SurfacePropertiesEditorDialog& operator=(const SurfacePropertiesEditorDialog&); - WuQTrueFalseComboBox* m_displayNormalVectorsComboBox; + QCheckBox* m_displayNormalVectorsCheckBox; QDoubleSpinBox* m_linkSizeSpinBox; diff --git a/src/GuiQt/WbMacroWidgetActionNames.h b/src/GuiQt/WbMacroWidgetActionNames.h index cad6534bb..a87604c32 100644 --- a/src/GuiQt/WbMacroWidgetActionNames.h +++ b/src/GuiQt/WbMacroWidgetActionNames.h @@ -39,6 +39,10 @@ namespace caret { static QString getSurfacePropertiesVertexDiameterName() { return "SurfaceProperties:vertexDiameter"; } + static QString getSurfacePropertiesDisplayNormalVectorsName() { return "SurfaceProperties:displayNormalVectors"; } + + static QString getSurfacePropertiesDrawingTypeName() { return "SurfaceProperties:drawingType"; } + WbMacroWidgetActionNames() = delete; virtual ~WbMacroWidgetActionNames() = delete; diff --git a/src/GuiQt/WbMacroWidgetActionsManager.cxx b/src/GuiQt/WbMacroWidgetActionsManager.cxx index 0b4984f03..94f751a87 100644 --- a/src/GuiQt/WbMacroWidgetActionsManager.cxx +++ b/src/GuiQt/WbMacroWidgetActionsManager.cxx @@ -68,53 +68,44 @@ std::vector WbMacroWidgetActionsManager::getMacroWidgetActions() { if (m_macroWidgetActions.empty()) { - m_surfacePropertiesLinkDiameterWidgetAction = createSurfacePropertiesLinkDiameterWidgetAction(); - m_macroWidgetActions.push_back(m_surfacePropertiesLinkDiameterWidgetAction); + m_macroWidgetActions.push_back(getSurfacePropertiesLinkDiameterWidgetAction()); - m_surfacePropertiesOpacityWidgetAction = createSurfacePropertiesOpacityWidgetAction(); - m_macroWidgetActions.push_back(m_surfacePropertiesOpacityWidgetAction); + m_macroWidgetActions.push_back(getSurfacePropertiesOpacityWidgetAction()); - m_surfacePropertiesVertexDiameterWidgetAction = createSurfacePropertiesVertexDiameterWidgetAction(); - m_macroWidgetActions.push_back(m_surfacePropertiesVertexDiameterWidgetAction); + m_macroWidgetActions.push_back(getSurfacePropertiesVertexDiameterWidgetAction()); + + m_macroWidgetActions.push_back(getSurfacePropertiesDisplayNormalVectorsWidgetAction()); } return m_macroWidgetActions; } -/** - * @return New instance of surface opacity widget action - */ -WuQMacroWidgetAction* -WbMacroWidgetActionsManager::createSurfacePropertiesOpacityWidgetAction() -{ - WuQMacroWidgetAction* action = new WuQMacroWidgetAction(WuQMacroWidgetAction::WidgetType::SPIN_BOX_FLOAT, - WbMacroWidgetActionNames::getSurfacePropertiesOpacityName(), - "Set the surface opacity", - parent()); - - DisplayPropertiesSurface* dsp = GuiManager::get()->getBrain()->getDisplayPropertiesSurface(); - - QObject::connect(action, &WuQMacroWidgetAction::getModelValue, - this, [=](QVariant& value) { - value = dsp->getOpacity(); - }); - - QObject::connect(action, &WuQMacroWidgetAction::setModelValue, - this, [=](const QVariant& value) { - GuiManager::get()->getBrain()->getDisplayPropertiesSurface()->setOpacity(value.toFloat()); - GuiManager::updateSurfaceColoring(); - GuiManager::updateGraphicsAllWindows(); - }); - - return action; -} - /** * @return The surface opacity widget action */ WuQMacroWidgetAction* WbMacroWidgetActionsManager::getSurfacePropertiesOpacityWidgetAction() { + if (m_surfacePropertiesOpacityWidgetAction == NULL) { + m_surfacePropertiesOpacityWidgetAction = new WuQMacroWidgetAction(WuQMacroWidgetAction::WidgetType::SPIN_BOX_FLOAT, + WbMacroWidgetActionNames::getSurfacePropertiesOpacityName(), + "Set the surface opacity", + this); + + DisplayPropertiesSurface* dsp = GuiManager::get()->getBrain()->getDisplayPropertiesSurface(); + + QObject::connect(m_surfacePropertiesOpacityWidgetAction, &WuQMacroWidgetAction::getModelValue, + this, [=](QVariant& value) { + value = dsp->getOpacity(); + }); + + QObject::connect(m_surfacePropertiesOpacityWidgetAction, &WuQMacroWidgetAction::setModelValue, + this, [=](const QVariant& value) { + dsp->setOpacity(value.toFloat()); + GuiManager::updateSurfaceColoring(); + GuiManager::updateGraphicsAllWindows(); + }); } + CaretAssert(m_surfacePropertiesOpacityWidgetAction); return m_surfacePropertiesOpacityWidgetAction; } @@ -125,68 +116,83 @@ WbMacroWidgetActionsManager::getSurfacePropertiesOpacityWidgetAction() WuQMacroWidgetAction* WbMacroWidgetActionsManager::getSurfacePropertiesLinkDiameterWidgetAction() { + if (m_surfacePropertiesLinkDiameterWidgetAction == NULL) { + m_surfacePropertiesLinkDiameterWidgetAction = new WuQMacroWidgetAction(WuQMacroWidgetAction::WidgetType::SPIN_BOX_FLOAT, + WbMacroWidgetActionNames::getSurfacePropertiesLinkDiameterName(), + "Set the link (edge) diameter", + this); + + DisplayPropertiesSurface* dsp = GuiManager::get()->getBrain()->getDisplayPropertiesSurface(); + + QObject::connect(m_surfacePropertiesLinkDiameterWidgetAction, &WuQMacroWidgetAction::getModelValue, + this, [=](QVariant& value) { + value = dsp->getLinkSize(); + }); + + QObject::connect(m_surfacePropertiesLinkDiameterWidgetAction, &WuQMacroWidgetAction::setModelValue, + this, [=](const QVariant& value) { + dsp->setLinkSize(value.toFloat()); + GuiManager::updateGraphicsAllWindows(); + }); + } + return m_surfacePropertiesLinkDiameterWidgetAction; } /** - * @return New instance of surface link diameter widget action + * @return The surface link diameter widget action */ WuQMacroWidgetAction* -WbMacroWidgetActionsManager::createSurfacePropertiesLinkDiameterWidgetAction() +WbMacroWidgetActionsManager::getSurfacePropertiesVertexDiameterWidgetAction() { - WuQMacroWidgetAction* action = new WuQMacroWidgetAction(WuQMacroWidgetAction::WidgetType::SPIN_BOX_FLOAT, - WbMacroWidgetActionNames::getSurfacePropertiesLinkDiameterName(), - "Set the link (edge) diameter", - parent()); - - DisplayPropertiesSurface* dsp = GuiManager::get()->getBrain()->getDisplayPropertiesSurface(); - - QObject::connect(action, &WuQMacroWidgetAction::getModelValue, - this, [=](QVariant& value) { - value = dsp->getLinkSize(); - }); - - QObject::connect(action, &WuQMacroWidgetAction::setModelValue, - this, [=](const QVariant& value) { - GuiManager::get()->getBrain()->getDisplayPropertiesSurface()->setLinkSize(value.toFloat()); - GuiManager::updateGraphicsAllWindows(); - }); - - return action; + if (m_surfacePropertiesVertexDiameterWidgetAction == NULL) { + m_surfacePropertiesVertexDiameterWidgetAction = new WuQMacroWidgetAction(WuQMacroWidgetAction::WidgetType::SPIN_BOX_FLOAT, + WbMacroWidgetActionNames::getSurfacePropertiesVertexDiameterName(), + "Set the vertex diameter", + this); + + DisplayPropertiesSurface* dsp = GuiManager::get()->getBrain()->getDisplayPropertiesSurface(); + + QObject::connect(m_surfacePropertiesVertexDiameterWidgetAction, &WuQMacroWidgetAction::getModelValue, + this, [=](QVariant& value) { + value = dsp->getNodeSize(); + }); + + QObject::connect(m_surfacePropertiesVertexDiameterWidgetAction, &WuQMacroWidgetAction::setModelValue, + this, [=](const QVariant& value) { + dsp->setNodeSize(value.toFloat()); + GuiManager::updateGraphicsAllWindows(); + }); + } + return m_surfacePropertiesVertexDiameterWidgetAction; } -/** - * @return New instance of surface vertex diameter widget action - */ -WuQMacroWidgetAction* -WbMacroWidgetActionsManager::createSurfacePropertiesVertexDiameterWidgetAction() -{ - WuQMacroWidgetAction* action = new WuQMacroWidgetAction(WuQMacroWidgetAction::WidgetType::SPIN_BOX_FLOAT, - WbMacroWidgetActionNames::getSurfacePropertiesVertexDiameterName(), - "Set the vertex diameter", - parent()); - - DisplayPropertiesSurface* dsp = GuiManager::get()->getBrain()->getDisplayPropertiesSurface(); - - QObject::connect(action, &WuQMacroWidgetAction::getModelValue, - this, [=](QVariant& value) { - value = dsp->getNodeSize(); - }); - - QObject::connect(action, &WuQMacroWidgetAction::setModelValue, - this, [=](const QVariant& value) { - GuiManager::get()->getBrain()->getDisplayPropertiesSurface()->setNodeSize(value.toFloat()); - GuiManager::updateGraphicsAllWindows(); - }); - - return action; -} /** - * @return The surface link diameter widget action + * @return Get (and in needed create) the surface properties display normal vectors widget action */ WuQMacroWidgetAction* -WbMacroWidgetActionsManager::getSurfacePropertiesVertexDiameterWidgetAction() +WbMacroWidgetActionsManager::getSurfacePropertiesDisplayNormalVectorsWidgetAction() { - return m_surfacePropertiesVertexDiameterWidgetAction; + if (m_surfacePropertiesDisplayNormalVectorsWidgetAction == NULL) { + m_surfacePropertiesDisplayNormalVectorsWidgetAction = new WuQMacroWidgetAction(WuQMacroWidgetAction::WidgetType::CHECK_BOX_BOOLEAN, + WbMacroWidgetActionNames::getSurfacePropertiesDisplayNormalVectorsName(), + "Display normal vectors on a surface", + this); + + DisplayPropertiesSurface* dsp = GuiManager::get()->getBrain()->getDisplayPropertiesSurface(); + + QObject::connect(m_surfacePropertiesDisplayNormalVectorsWidgetAction, &WuQMacroWidgetAction::getModelValue, + this, [=](QVariant& value) { + value = dsp->isDisplayNormalVectors(); + }); + + QObject::connect(m_surfacePropertiesDisplayNormalVectorsWidgetAction, &WuQMacroWidgetAction::setModelValue, + this, [=](const QVariant& value) { + dsp->setDisplayNormalVectors(value.toBool()); + GuiManager::updateGraphicsAllWindows(); + }); + } + + return m_surfacePropertiesDisplayNormalVectorsWidgetAction; } diff --git a/src/GuiQt/WbMacroWidgetActionsManager.h b/src/GuiQt/WbMacroWidgetActionsManager.h index a929d2c3c..c3394022a 100644 --- a/src/GuiQt/WbMacroWidgetActionsManager.h +++ b/src/GuiQt/WbMacroWidgetActionsManager.h @@ -55,22 +55,20 @@ namespace caret { WuQMacroWidgetAction* getSurfacePropertiesVertexDiameterWidgetAction(); + WuQMacroWidgetAction* getSurfacePropertiesDisplayNormalVectorsWidgetAction(); + // ADD_NEW_METHODS_HERE private: std::vector m_macroWidgetActions; - WuQMacroWidgetAction* createSurfacePropertiesOpacityWidgetAction(); - - WuQMacroWidgetAction* m_surfacePropertiesOpacityWidgetAction; - - WuQMacroWidgetAction* createSurfacePropertiesLinkDiameterWidgetAction(); + WuQMacroWidgetAction* m_surfacePropertiesOpacityWidgetAction = NULL; - WuQMacroWidgetAction* m_surfacePropertiesLinkDiameterWidgetAction; + WuQMacroWidgetAction* m_surfacePropertiesLinkDiameterWidgetAction = NULL; - WuQMacroWidgetAction* createSurfacePropertiesVertexDiameterWidgetAction(); + WuQMacroWidgetAction* m_surfacePropertiesVertexDiameterWidgetAction = NULL; - WuQMacroWidgetAction* m_surfacePropertiesVertexDiameterWidgetAction; + WuQMacroWidgetAction* m_surfacePropertiesDisplayNormalVectorsWidgetAction = NULL; // ADD_NEW_MEMBERS_HERE diff --git a/src/GuiQt/WuQMacroManager.cxx b/src/GuiQt/WuQMacroManager.cxx index 7648284df..9d1ddb01c 100644 --- a/src/GuiQt/WuQMacroManager.cxx +++ b/src/GuiQt/WuQMacroManager.cxx @@ -1593,11 +1593,43 @@ WuQMacroManager::getWidgetForMacroWidgetActionByName(const QString& name, * of the widget owner (typically a dialog). */ void -WuQMacroManager::releaseWidgetFromMacroWidgetAction(QWidget* widget) +WuQMacroManager::releaseWidgetFromMacroWidgetAction(QWidget* widget, + QWidget* widget2, + QWidget* widget3, + QWidget* widget4, + QWidget* widget5, + QWidget* widget6) { - CaretAssert(widget); - - for (auto mwa : m_macroWidgetActions) { - mwa->releaseWidget(widget); + for (auto widget : { widget, widget2, widget3, widget4, widget5, widget6 } ) { + if (widget != NULL) { + for (auto mwa : m_macroWidgetActions) { + mwa->releaseWidget(widget); + } + } + } +} + +/** + * Update the value of a widget from its macro widget action. + * + * @param widget + * Widget that gets its value update + */ +void +WuQMacroManager::updateValueInWidgetFromMacroWidgetAction(QWidget* widget, + QWidget* widget2, + QWidget* widget3, + QWidget* widget4, + QWidget* widget5, + QWidget* widget6) +{ + for (auto widget : { widget, widget2, widget3, widget4, widget5, widget6 } ) { + if (widget != NULL) { + for (auto mwa : m_macroWidgetActions) { + if (mwa->updateWidgetWithModelValue(widget)) { + return; + } + } + } } } diff --git a/src/GuiQt/WuQMacroManager.h b/src/GuiQt/WuQMacroManager.h index c6ff03e1a..a1fa8df22 100644 --- a/src/GuiQt/WuQMacroManager.h +++ b/src/GuiQt/WuQMacroManager.h @@ -24,6 +24,7 @@ #include +#include #include #include @@ -194,7 +195,19 @@ namespace caret { QWidget* getWidgetForMacroWidgetActionByName(const QString& name, QWidget* parentWidget = 0); - void releaseWidgetFromMacroWidgetAction(QWidget* widget); + void releaseWidgetFromMacroWidgetAction(QWidget* widget, + QWidget* widget2 = NULL, + QWidget* widget3 = NULL, + QWidget* widget4 = NULL, + QWidget* widget5 = NULL, + QWidget* widget6 = NULL); + + void updateValueInWidgetFromMacroWidgetAction(QWidget* widget, + QWidget* widget2 = NULL, + QWidget* widget3 = NULL, + QWidget* widget4 = NULL, + QWidget* widget5 = NULL, + QWidget* widget6 = NULL); // ADD_NEW_METHODS_HERE diff --git a/src/GuiQt/WuQMacroWidgetAction.cxx b/src/GuiQt/WuQMacroWidgetAction.cxx index f648be248..42efbc2fc 100644 --- a/src/GuiQt/WuQMacroWidgetAction.cxx +++ b/src/GuiQt/WuQMacroWidgetAction.cxx @@ -316,24 +316,30 @@ WuQMacroWidgetAction::setWidgetValue(QWidget* widget, * * @param widget * The widget + * @return + * True if widget was updated with model value */ -void +bool WuQMacroWidgetAction::updateWidgetWithModelValue(QWidget* widget) { CaretAssert(widget); - - QVariant value; - emit getModelValue(value); - - if (value.isNull()) { - const QString msg("Value was not updated by model, need to connect getModelValue() signal"); - CaretAssertMessage(0, msg); - CaretLogSevere(msg); - } - else { - setWidgetValue(widget, - value); + if (m_widgets.find(widget) != m_widgets.end()) { + QVariant value; + emit getModelValue(value); + + if (value.isNull()) { + const QString msg("Value was not updated by model, need to connect getModelValue() signal"); + CaretAssertMessage(0, msg); + CaretLogSevere(msg); + } + else { + setWidgetValue(widget, + value); + return true; + } } + + return false; } /** diff --git a/src/GuiQt/WuQMacroWidgetAction.h b/src/GuiQt/WuQMacroWidgetAction.h index 385c3f092..7d4a99e39 100644 --- a/src/GuiQt/WuQMacroWidgetAction.h +++ b/src/GuiQt/WuQMacroWidgetAction.h @@ -89,7 +89,7 @@ namespace caret { void setDataValue(const QVariant& value); - void updateWidgetWithModelValue(QWidget* widget); + bool updateWidgetWithModelValue(QWidget* widget); signals: /** -- GitLab From 65bb0862a5db55f7f3ce792d01e324765b4b46ed Mon Sep 17 00:00:00 2001 From: John Harwell Date: Fri, 3 May 2019 09:57:36 -0500 Subject: [PATCH 298/427] Complete macro support for all widgets on Surface Properties Dialog. --- src/GuiQt/SurfacePropertiesEditorDialog.cxx | 61 +++++------------- src/GuiQt/SurfacePropertiesEditorDialog.h | 6 +- src/GuiQt/WbMacroWidgetActionsManager.cxx | 57 ++++++++++++++++- src/GuiQt/WbMacroWidgetActionsManager.h | 4 ++ src/GuiQt/WuQMacroWidgetAction.cxx | 69 ++++++++++++++++++++- src/GuiQt/WuQMacroWidgetAction.h | 27 ++++++++ 6 files changed, 171 insertions(+), 53 deletions(-) diff --git a/src/GuiQt/SurfacePropertiesEditorDialog.cxx b/src/GuiQt/SurfacePropertiesEditorDialog.cxx index 8b02d2b1b..c3116b362 100644 --- a/src/GuiQt/SurfacePropertiesEditorDialog.cxx +++ b/src/GuiQt/SurfacePropertiesEditorDialog.cxx @@ -32,13 +32,7 @@ using namespace caret; -#include "Brain.h" #include "CaretAssert.h" -#include "DisplayPropertiesSurface.h" -#include "GuiManager.h" -#include "EnumComboBoxTemplate.h" -#include "EventGraphicsUpdateAllWindows.h" -#include "EventSurfaceColoringInvalidate.h" #include "EventManager.h" #include "EventUserInterfaceUpdate.h" #include "SceneClass.h" @@ -69,10 +63,15 @@ SurfacePropertiesEditorDialog::SurfacePropertiesEditorDialog(QWidget* parent) CaretAssert(mm); QLabel* surfaceDrawingTypeLabel = new QLabel("Drawing Type: "); - m_surfaceDrawingTypeComboBox = new EnumComboBoxTemplate(this); - QObject::connect(m_surfaceDrawingTypeComboBox, SIGNAL(itemActivated()), - this, SLOT(surfaceDisplayPropertyChanged())); - m_surfaceDrawingTypeComboBox->setup(); + QWidget* drawTypeWidget = mm->getWidgetForMacroWidgetActionByName(WbMacroWidgetActionNames::getSurfacePropertiesDrawingTypeName()); + if (drawTypeWidget != NULL) { + m_surfaceDrawingTypeComboBox = qobject_cast(drawTypeWidget); + CaretAssert(m_surfaceDrawingTypeComboBox); + } + if (m_surfaceDrawingTypeComboBox == NULL) { + m_surfaceDrawingTypeComboBox = new QComboBox(); + m_surfaceDrawingTypeComboBox->setEnabled(false); + } QLabel* linkSizeLabel = new QLabel("Link Diameter: "); @@ -85,9 +84,6 @@ SurfacePropertiesEditorDialog::SurfacePropertiesEditorDialog(QWidget* parent) m_linkSizeSpinBox = WuQFactory::newDoubleSpinBox(); m_linkSizeSpinBox->setEnabled(false); } - m_linkSizeSpinBox->setRange(0.0, std::numeric_limits::max()); - m_linkSizeSpinBox->setSingleStep(1.0); - m_linkSizeSpinBox->setDecimals(1); m_linkSizeSpinBox->setSuffix("mm"); QLabel* nodeSizeLabel = new QLabel("Vertex Diameter: "); @@ -101,9 +97,6 @@ SurfacePropertiesEditorDialog::SurfacePropertiesEditorDialog(QWidget* parent) m_nodeSizeSpinBox = WuQFactory::newDoubleSpinBox(); m_nodeSizeSpinBox->setEnabled(false); } - m_nodeSizeSpinBox->setRange(0.0, std::numeric_limits::max()); - m_nodeSizeSpinBox->setSingleStep(1.0); - m_nodeSizeSpinBox->setDecimals(1); m_nodeSizeSpinBox->setSuffix("mm"); QWidget* displayNormalsWidget = mm->getWidgetForMacroWidgetActionByName(WbMacroWidgetActionNames::getSurfacePropertiesDisplayNormalVectorsName()); @@ -128,9 +121,6 @@ SurfacePropertiesEditorDialog::SurfacePropertiesEditorDialog(QWidget* parent) m_opacitySpinBox = new QDoubleSpinBox(); m_opacitySpinBox->setEnabled(false); } - m_opacitySpinBox->setRange(0.0, 1.0); - m_opacitySpinBox->setSingleStep(0.1); - m_opacitySpinBox->setDecimals(2); QWidget* w = new QWidget(); QGridLayout* gridLayout = new QGridLayout(w); @@ -139,7 +129,7 @@ SurfacePropertiesEditorDialog::SurfacePropertiesEditorDialog(QWidget* parent) gridLayout->addWidget(m_displayNormalVectorsCheckBox, row, 0, 1, 2); row++; gridLayout->addWidget(surfaceDrawingTypeLabel, row, 0); - gridLayout->addWidget(m_surfaceDrawingTypeComboBox->getWidget(), row, 1); + gridLayout->addWidget(m_surfaceDrawingTypeComboBox, row, 1); row++; gridLayout->addWidget(linkSizeLabel, row, 0); gridLayout->addWidget(m_linkSizeSpinBox, row, 1); @@ -172,34 +162,13 @@ SurfacePropertiesEditorDialog::~SurfacePropertiesEditorDialog() { EventManager::get()->removeAllEventsFromListener(this); - WuQMacroManager::instance()->releaseWidgetFromMacroWidgetAction(m_linkSizeSpinBox, + WuQMacroManager::instance()->releaseWidgetFromMacroWidgetAction(m_surfaceDrawingTypeComboBox, + m_linkSizeSpinBox, m_nodeSizeSpinBox, m_opacitySpinBox, m_displayNormalVectorsCheckBox); } -/** - * Called when a surface display property is changed. - */ -void -SurfacePropertiesEditorDialog::surfaceDisplayPropertyChanged() -{ - /* - * Updating some widgets causes signals to be emitted - */ - if (m_updateInProgress) { - return; - } - - const SurfaceDrawingTypeEnum::Enum surfaceDrawingType = m_surfaceDrawingTypeComboBox->getSelectedItem(); - - DisplayPropertiesSurface* dps = GuiManager::get()->getBrain()->getDisplayPropertiesSurface(); - dps->setSurfaceDrawingType(surfaceDrawingType); - - EventManager::get()->sendEvent(EventSurfaceColoringInvalidate().getPointer()); - EventManager::get()->sendEvent(EventGraphicsUpdateAllWindows().getPointer()); -} - /** * Update the properties editor. */ @@ -208,10 +177,8 @@ SurfacePropertiesEditorDialog::updateDialog() { m_updateInProgress = true; - const DisplayPropertiesSurface* dps = GuiManager::get()->getBrain()->getDisplayPropertiesSurface(); - - m_surfaceDrawingTypeComboBox->setSelectedItem(dps->getSurfaceDrawingType()); - WuQMacroManager::instance()->updateValueInWidgetFromMacroWidgetAction(m_linkSizeSpinBox, + WuQMacroManager::instance()->updateValueInWidgetFromMacroWidgetAction(m_surfaceDrawingTypeComboBox, + m_linkSizeSpinBox, m_nodeSizeSpinBox, m_opacitySpinBox, m_displayNormalVectorsCheckBox); diff --git a/src/GuiQt/SurfacePropertiesEditorDialog.h b/src/GuiQt/SurfacePropertiesEditorDialog.h index ae02dbb1c..1f56343d4 100644 --- a/src/GuiQt/SurfacePropertiesEditorDialog.h +++ b/src/GuiQt/SurfacePropertiesEditorDialog.h @@ -26,6 +26,7 @@ #include "WuQDialogNonModal.h" class QCheckBox; +class QComboBox; class QDoubleSpinBox; namespace caret { @@ -50,9 +51,6 @@ namespace caret { virtual void restoreFromScene(const SceneAttributes* sceneAttributes, const SceneClass* sceneClass); - private slots: - void surfaceDisplayPropertyChanged(); - private: SurfacePropertiesEditorDialog(const SurfacePropertiesEditorDialog&); @@ -64,7 +62,7 @@ namespace caret { QDoubleSpinBox* m_nodeSizeSpinBox; - EnumComboBoxTemplate* m_surfaceDrawingTypeComboBox; + QComboBox* m_surfaceDrawingTypeComboBox; QDoubleSpinBox* m_opacitySpinBox; diff --git a/src/GuiQt/WbMacroWidgetActionsManager.cxx b/src/GuiQt/WbMacroWidgetActionsManager.cxx index 94f751a87..6db1b2b49 100644 --- a/src/GuiQt/WbMacroWidgetActionsManager.cxx +++ b/src/GuiQt/WbMacroWidgetActionsManager.cxx @@ -23,9 +23,12 @@ #include "WbMacroWidgetActionsManager.h" #undef __WB_MACRO_WIDGET_ACTIONS_MANAGER_DECLARE__ +#include + #include #include "CaretAssert.h" +#include "CaretLogger.h" #include "Brain.h" #include "DisplayPropertiesSurface.h" #include "GuiManager.h" @@ -75,6 +78,8 @@ WbMacroWidgetActionsManager::getMacroWidgetActions() m_macroWidgetActions.push_back(getSurfacePropertiesVertexDiameterWidgetAction()); m_macroWidgetActions.push_back(getSurfacePropertiesDisplayNormalVectorsWidgetAction()); + + m_macroWidgetActions.push_back(getSurfacePropertiesSurfaceDrawingTypeWidgetAction()); } return m_macroWidgetActions; @@ -91,6 +96,7 @@ WbMacroWidgetActionsManager::getSurfacePropertiesOpacityWidgetAction() WbMacroWidgetActionNames::getSurfacePropertiesOpacityName(), "Set the surface opacity", this); + m_surfacePropertiesOpacityWidgetAction->setDoubleSpinBoxMinMaxStepDecimals(0.0, 1.0, 0.1, 2); DisplayPropertiesSurface* dsp = GuiManager::get()->getBrain()->getDisplayPropertiesSurface(); @@ -121,6 +127,7 @@ WbMacroWidgetActionsManager::getSurfacePropertiesLinkDiameterWidgetAction() WbMacroWidgetActionNames::getSurfacePropertiesLinkDiameterName(), "Set the link (edge) diameter", this); + m_surfacePropertiesLinkDiameterWidgetAction->setDoubleSpinBoxMinMaxStepDecimals(0.0, std::numeric_limits::max(), 1.0, 1); DisplayPropertiesSurface* dsp = GuiManager::get()->getBrain()->getDisplayPropertiesSurface(); @@ -150,7 +157,8 @@ WbMacroWidgetActionsManager::getSurfacePropertiesVertexDiameterWidgetAction() WbMacroWidgetActionNames::getSurfacePropertiesVertexDiameterName(), "Set the vertex diameter", this); - + m_surfacePropertiesVertexDiameterWidgetAction->setDoubleSpinBoxMinMaxStepDecimals(0.0, std::numeric_limits::max(), 1.0, 1); + DisplayPropertiesSurface* dsp = GuiManager::get()->getBrain()->getDisplayPropertiesSurface(); QObject::connect(m_surfacePropertiesVertexDiameterWidgetAction, &WuQMacroWidgetAction::getModelValue, @@ -196,3 +204,50 @@ WbMacroWidgetActionsManager::getSurfacePropertiesDisplayNormalVectorsWidgetActio return m_surfacePropertiesDisplayNormalVectorsWidgetAction; } + +/** + * @return Get (and in needed create) the surface properties surface drawing type widget action + */ +WuQMacroWidgetAction* +WbMacroWidgetActionsManager::getSurfacePropertiesSurfaceDrawingTypeWidgetAction() +{ + if (m_surfacePropertiesSurfaceDrawingTypeWidgetAction == NULL) { + m_surfacePropertiesSurfaceDrawingTypeWidgetAction = new WuQMacroWidgetAction(WuQMacroWidgetAction::WidgetType::COMBO_BOX_STRING_LIST, + WbMacroWidgetActionNames::getSurfacePropertiesDrawingTypeName(), + "Drawing type for surface", + this); + + std::vector drawTypeNames; + const bool sortNamesFlag(false); + SurfaceDrawingTypeEnum::getAllGuiNames(drawTypeNames, + sortNamesFlag); + std::vector qsDrawTypeNames(drawTypeNames.begin(), + drawTypeNames.end()); + m_surfacePropertiesSurfaceDrawingTypeWidgetAction->setComboBoxStringList(qsDrawTypeNames); + + DisplayPropertiesSurface* dsp = GuiManager::get()->getBrain()->getDisplayPropertiesSurface(); + + QObject::connect(m_surfacePropertiesSurfaceDrawingTypeWidgetAction, &WuQMacroWidgetAction::getModelValue, + this, [=](QVariant& value) { + const SurfaceDrawingTypeEnum::Enum sdt = dsp->getSurfaceDrawingType(); + value = SurfaceDrawingTypeEnum::toGuiName(sdt); + }); + + QObject::connect(m_surfacePropertiesSurfaceDrawingTypeWidgetAction, &WuQMacroWidgetAction::setModelValue, + this, [=](const QVariant& value) { + bool validFlag(false); + const SurfaceDrawingTypeEnum::Enum std = SurfaceDrawingTypeEnum::fromGuiName(value.toString(), &validFlag); + if (validFlag) { + dsp->setSurfaceDrawingType(std); + } + else { + CaretLogSevere("Failed to convert to surface drawing type: " + + value.toString()); + } + GuiManager::updateGraphicsAllWindows(); + }); + } + + return m_surfacePropertiesSurfaceDrawingTypeWidgetAction; +} + diff --git a/src/GuiQt/WbMacroWidgetActionsManager.h b/src/GuiQt/WbMacroWidgetActionsManager.h index c3394022a..57a9b00f3 100644 --- a/src/GuiQt/WbMacroWidgetActionsManager.h +++ b/src/GuiQt/WbMacroWidgetActionsManager.h @@ -57,6 +57,8 @@ namespace caret { WuQMacroWidgetAction* getSurfacePropertiesDisplayNormalVectorsWidgetAction(); + WuQMacroWidgetAction* getSurfacePropertiesSurfaceDrawingTypeWidgetAction(); + // ADD_NEW_METHODS_HERE private: @@ -70,6 +72,8 @@ namespace caret { WuQMacroWidgetAction* m_surfacePropertiesDisplayNormalVectorsWidgetAction = NULL; + WuQMacroWidgetAction* m_surfacePropertiesSurfaceDrawingTypeWidgetAction = NULL; + // ADD_NEW_MEMBERS_HERE }; diff --git a/src/GuiQt/WuQMacroWidgetAction.cxx b/src/GuiQt/WuQMacroWidgetAction.cxx index 42efbc2fc..01d853d1c 100644 --- a/src/GuiQt/WuQMacroWidgetAction.cxx +++ b/src/GuiQt/WuQMacroWidgetAction.cxx @@ -121,6 +121,9 @@ WuQMacroWidgetAction::requestWidget(QWidget* parent) case WidgetType::COMBO_BOX_STRING_LIST: { QComboBox* cb = new QComboBox(parent); + for (auto s : m_comboBoxStringListItems) { + cb->addItem(s); + } QObject::connect(cb, QOverload::of(&QComboBox::activated), this, [=](const QString& value) { emit valueChanged(value); } ); w = cb; @@ -137,6 +140,11 @@ WuQMacroWidgetAction::requestWidget(QWidget* parent) case WidgetType::SPIN_BOX_FLOAT: { QDoubleSpinBox* sb = new QDoubleSpinBox(parent); + sb->setMinimum(m_spinBoxFloatMinMaxStep.m_minValue); + sb->setMaximum(m_spinBoxFloatMinMaxStep.m_maxValue); + sb->setSingleStep(m_spinBoxFloatMinMaxStep.m_step); + sb->setDecimals(m_spinBoxFloatMinMaxStep.m_decimals); + sb->setValue(sb->minimum()); QObject::connect(sb, QOverload::of(&QDoubleSpinBox::valueChanged), this, [=](const double value) { emit valueChanged(static_cast(value)); } ); w = sb; @@ -145,6 +153,10 @@ WuQMacroWidgetAction::requestWidget(QWidget* parent) case WidgetType::SPIN_BOX_INTEGER: { QSpinBox* sb = new QSpinBox(parent); + sb->setMinimum(m_spinBoxIntegerMinMaxStep.m_minValue); + sb->setMaximum(m_spinBoxIntegerMinMaxStep.m_maxValue); + sb->setSingleStep(m_spinBoxIntegerMinMaxStep.m_step); + sb->setValue(sb->minimum()); QObject::connect(sb, QOverload::of(&QSpinBox::valueChanged), this, [=](const int value) { emit valueChanged(value); } ); w = sb; @@ -193,7 +205,7 @@ WuQMacroWidgetAction::releaseWidget(QWidget* widget) m_widgets.erase(widget); - std::cout << "Released widget: " << widget->objectName() << std::endl; + //std::cout << "Released widget: " << widget->objectName() << std::endl; return true; } @@ -359,3 +371,58 @@ WuQMacroWidgetAction::receiveEvent(Event* /*event*/) // } } +/** + * Setup widget that will be provided by the macro widget action + * + * @param comboBoxTextItems + * Text items for combo box + */ +void +WuQMacroWidgetAction::setComboBoxStringList(std::vector& comboBoxTextItems) +{ + m_comboBoxStringListItems = comboBoxTextItems; +} + +/** + * Setup widget that will be provided by the macro widget action + * + * @param minValue + * Minimum value for spin box + * @param maxValue + * Maximum value for spin box + * @param step + * Step value for spin box + */ +void +WuQMacroWidgetAction::setSpinBoxMinMaxStep(const int32_t minValue, + const int32_t maxValue, + const int32_t step) +{ + m_spinBoxIntegerMinMaxStep.m_minValue = minValue; + m_spinBoxIntegerMinMaxStep.m_maxValue = maxValue; + m_spinBoxIntegerMinMaxStep.m_step = step; +} + +/** + * Setup widget that will be provided by the macro widget action + * + * @param minValue + * Minimum value for spin box + * @param maxValue + * Maximum value for spin box + * @param step + * Step value for spin box + * @param decimals + * Number of decimals right of decimal point + */ +void +WuQMacroWidgetAction::setDoubleSpinBoxMinMaxStepDecimals(const double minValue, + const double maxValue, + const double step, + const int32_t decimals) +{ + m_spinBoxFloatMinMaxStep.m_minValue = minValue; + m_spinBoxFloatMinMaxStep.m_maxValue = maxValue; + m_spinBoxFloatMinMaxStep.m_step = step; + m_spinBoxFloatMinMaxStep.m_decimals = decimals; +} diff --git a/src/GuiQt/WuQMacroWidgetAction.h b/src/GuiQt/WuQMacroWidgetAction.h index 7d4a99e39..fc8871690 100644 --- a/src/GuiQt/WuQMacroWidgetAction.h +++ b/src/GuiQt/WuQMacroWidgetAction.h @@ -91,6 +91,17 @@ namespace caret { bool updateWidgetWithModelValue(QWidget* widget); + void setComboBoxStringList(std::vector& comboBoxTextItems); + + void setSpinBoxMinMaxStep(const int32_t minValue, + const int32_t maxValue, + const int32_t step); + + void setDoubleSpinBoxMinMaxStepDecimals(const double minValue, + const double maxValue, + const double step, + const int32_t decimals); + signals: /** * Emitted when the value changes. Used by macro signal watcher. @@ -129,6 +140,22 @@ namespace caret { std::set m_widgets; + std::vector m_comboBoxStringListItems; + + struct { + int32_t m_minValue = 0; + int32_t m_maxValue = 32000; + int32_t m_step = 1; + } m_spinBoxIntegerMinMaxStep; + + struct { + double m_minValue = 0.0; + double m_maxValue = 32000.0; + double m_step = 1.0; + int32_t m_decimals = 2; + } m_spinBoxFloatMinMaxStep; + + // ADD_NEW_MEMBERS_HERE }; -- GitLab From bd3a62dfbdd71eaf325362bc8f03d3ee7c5cbacb Mon Sep 17 00:00:00 2001 From: John Harwell Date: Fri, 3 May 2019 11:41:23 -0500 Subject: [PATCH 299/427] WB-846 Change default palette to absolute percentile: While changes for palette initialization were made to PaletteFile::setDefaultPaletteColorMapping, it is never used. VolumeFile::validateMembers() has been updated so that if a volume's map does not have palette color mapping (and is not a label volume), the default will be use to use the Absolute Percentile and the ROY_BIG-BL palette except for an ANATOMY volume with one map which will use the GRAY_INTERP_POSITIVE palette. --- src/Files/VolumeFile.cxx | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Files/VolumeFile.cxx b/src/Files/VolumeFile.cxx index e0c5fcf91..ea9a0f152 100644 --- a/src/Files/VolumeFile.cxx +++ b/src/Files/VolumeFile.cxx @@ -696,17 +696,17 @@ void VolumeFile::validateMembers() if (m_caretVolExt.m_attributes[i]->m_palette == NULL) { m_caretVolExt.m_attributes[i]->m_palette.grabNew(new PaletteColorMapping()); - if (theType == SubvolumeAttributes::ANATOMY) + m_caretVolExt.m_attributes[i]->m_palette->setScaleMode(PaletteScaleModeEnum::MODE_AUTO_SCALE_ABSOLUTE_PERCENTAGE); + if ((theType == SubvolumeAttributes::ANATOMY) && (numMaps == 1)) { m_caretVolExt.m_attributes[i]->m_palette->setSelectedPaletteName(Palette::GRAY_INTERP_POSITIVE_PALETTE_NAME); - m_caretVolExt.m_attributes[i]->m_palette->setScaleMode(PaletteScaleModeEnum::MODE_AUTO_SCALE_PERCENTAGE); + } else { + m_caretVolExt.m_attributes[i]->m_palette->setSelectedPaletteName(Palette::ROY_BIG_BL_PALETTE_NAME); } } } } - //setPaletteNormalizationMode(PaletteNormalizationModeEnum::NORMALIZATION_SELECTED_MAP_DATA); - m_singleSliceFlag = false; if ((dimensions[0] == 1) || (dimensions[1] == 1) -- GitLab From f58ea8286c5494f0ffc7ad3c8b802792ae730df5 Mon Sep 17 00:00:00 2001 From: John Harwell Date: Fri, 3 May 2019 12:06:48 -0500 Subject: [PATCH 300/427] Fix compilation error on linux. Compilers do not support iterating directly with an initializer list. --- src/GuiQt/WuQMacroManager.cxx | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/GuiQt/WuQMacroManager.cxx b/src/GuiQt/WuQMacroManager.cxx index 9d1ddb01c..19c4c906a 100644 --- a/src/GuiQt/WuQMacroManager.cxx +++ b/src/GuiQt/WuQMacroManager.cxx @@ -1600,7 +1600,8 @@ WuQMacroManager::releaseWidgetFromMacroWidgetAction(QWidget* widget, QWidget* widget5, QWidget* widget6) { - for (auto widget : { widget, widget2, widget3, widget4, widget5, widget6 } ) { + std::vector allWidgets { widget, widget2, widget3, widget4, widget5, widget6 }; + for (auto widget : allWidgets) { if (widget != NULL) { for (auto mwa : m_macroWidgetActions) { mwa->releaseWidget(widget); @@ -1623,7 +1624,8 @@ WuQMacroManager::updateValueInWidgetFromMacroWidgetAction(QWidget* widget, QWidget* widget5, QWidget* widget6) { - for (auto widget : { widget, widget2, widget3, widget4, widget5, widget6 } ) { + std::vector allWidgets { widget, widget2, widget3, widget4, widget5, widget6 }; + for (auto widget : allWidgets) { if (widget != NULL) { for (auto mwa : m_macroWidgetActions) { if (mwa->updateWidgetWithModelValue(widget)) { -- GitLab From c8ad285ca6260560f68f7626da612c58aec02135 Mon Sep 17 00:00:00 2001 From: John Harwell Date: Fri, 3 May 2019 15:21:55 -0500 Subject: [PATCH 301/427] WB-852 Volume Scrolling: Incrementing of volume slices in Volume View and in Orthogonal mode is now performed by dragging the mouse in a vertical direction. The change in slice indices is a function of how slow or fast the mouse is moved. --- src/Brain/BrowserTabContent.cxx | 112 +++++++++++++++++- src/Brain/BrowserTabContent.h | 5 + src/Common/EventManager.cxx | 1 + src/Common/EventTypeEnum.cxx | 4 + src/Common/EventTypeEnum.h | 2 + ...rainBrowserWindowToolBarSliceSelection.cxx | 23 ++++ .../BrainBrowserWindowToolBarSliceSelection.h | 3 +- src/GuiQt/UserInputModeView.cxx | 35 ++++-- 8 files changed, 174 insertions(+), 11 deletions(-) diff --git a/src/Brain/BrowserTabContent.cxx b/src/Brain/BrowserTabContent.cxx index 7e696586a..7cc111d1f 100644 --- a/src/Brain/BrowserTabContent.cxx +++ b/src/Brain/BrowserTabContent.cxx @@ -2449,12 +2449,120 @@ BrowserTabContent::ventralView() } /** - * Apply mouse rotation to the displayed model. + * Apply volume slice increment while dragging mouse * + * @param viewportContent + * Content of viewport * @param mousePressX * X coordinate of where mouse was pressed. * @param mousePressY + * Y coordinate of where mouse was pressed. + * @param mouseDY + * Change in mouse Y coordinate. + */ +void +BrowserTabContent::applyMouseVolumeSliceIncrement(BrainOpenGLViewportContent* viewportContent, + const int32_t mousePressX, + const int32_t mousePressY, + const int32_t mouseDY) +{ + bool incrementFlag(false); + if (isVolumeSlicesDisplayed()) { + switch (getSliceProjectionType()) { + case VolumeSliceProjectionTypeEnum::VOLUME_SLICE_PROJECTION_OBLIQUE: + break; + case VolumeSliceProjectionTypeEnum::VOLUME_SLICE_PROJECTION_ORTHOGONAL: + incrementFlag = true; + break; + } + } + + if ( ! incrementFlag) { + return; + } + if (mouseDY == 0) { + return; + } + + /* + * Prevents "too fast" scrolling. + * If set to a very large number, it will result in the + * slice increment becoming one + */ + int32_t slowDownIncrementer = 3; + + int32_t sliceDelta = mouseDY; + if (sliceDelta > 1) { + sliceDelta /= slowDownIncrementer; + if (sliceDelta == 0) { + sliceDelta = 1; + } + } + else if (sliceDelta < -1) { + sliceDelta /= slowDownIncrementer; + if (sliceDelta == 0) { + sliceDelta = -1; + } + } + + const int32_t tabIndex = viewportContent->getTabIndex(); + VolumeMappableInterface* underlayVolume(NULL); + ModelVolume* volumeModel = getDisplayedVolumeModel(); + if (volumeModel != NULL) { + underlayVolume = volumeModel->getUnderlayVolumeFile(tabIndex); + } + + VolumeSliceViewPlaneEnum::Enum sliceViewPlane = getSliceViewPlane(); + if (sliceViewPlane == VolumeSliceViewPlaneEnum::ALL) { + int viewport[4]; + viewportContent->getModelViewport(viewport); + int sliceViewport[4] = { + viewport[0], + viewport[1], + viewport[2], + viewport[3] + }; + sliceViewPlane = BrainOpenGLViewportContent::getSliceViewPlaneForVolumeAllSliceView(viewport, + getSlicePlanesAllViewLayout(), + mousePressX, + mousePressY, + sliceViewport); + } + + /* + * Note: Functions that set slice indices will prevent + * invalid slice indices + */ + switch (sliceViewPlane) { + case VolumeSliceViewPlaneEnum::ALL: + break; + case VolumeSliceViewPlaneEnum::AXIAL: + setSliceIndexAxial(underlayVolume, + (getSliceIndexAxial(underlayVolume) + sliceDelta)); + break; + case VolumeSliceViewPlaneEnum::CORONAL: + setSliceIndexCoronal(underlayVolume, + (getSliceIndexCoronal(underlayVolume) + sliceDelta)); + break; + case VolumeSliceViewPlaneEnum::PARASAGITTAL: + setSliceIndexParasagittal(underlayVolume, + (getSliceIndexParasagittal(underlayVolume) + sliceDelta)); + break; + } + + updateYokedModelBrowserTabs(); +} + + +/** + * Apply mouse rotation to the displayed model. + * + * @param viewportContent + * Content of viewport + * @param mousePressX * X coordinate of where mouse was pressed. + * @param mousePressY + * Y coordinate of where mouse was pressed. * @param mouseX * X coordinate of mouse. * @param mouseY @@ -2623,8 +2731,6 @@ BrowserTabContent::applyMouseRotation(BrainOpenGLViewportContent* viewportConten break; case VolumeSliceProjectionTypeEnum::VOLUME_SLICE_PROJECTION_ORTHOGONAL: break; - /* Orthogonal olume slices are not rotated */ - break; } } else if (isChartOneDisplayed() diff --git a/src/Brain/BrowserTabContent.h b/src/Brain/BrowserTabContent.h index 28f0547af..d9babb5bf 100644 --- a/src/Brain/BrowserTabContent.h +++ b/src/Brain/BrowserTabContent.h @@ -263,6 +263,11 @@ namespace caret { void ventralView(); + void applyMouseVolumeSliceIncrement(BrainOpenGLViewportContent* viewportContent, + const int32_t mousePressX, + const int32_t mousePressY, + const int32_t mouseDY); + void applyMouseRotation(BrainOpenGLViewportContent* viewportContent, const int32_t mousePressX, const int32_t mousePressY, diff --git a/src/Common/EventManager.cxx b/src/Common/EventManager.cxx index 3a3792060..17ce49d8c 100644 --- a/src/Common/EventManager.cxx +++ b/src/Common/EventManager.cxx @@ -370,6 +370,7 @@ EventManager::sendSimpleEvent(const EventTypeEnum::Enum eventType) case EventTypeEnum::EVENT_ANNOTATION_TOOLBAR_UPDATE: case EventTypeEnum::EVENT_BROWSER_WINDOW_MENUS_UPDATE: case EventTypeEnum::EVENT_MOVIE_RECORDING_DIALOG_UPDATE: + case EventTypeEnum::EVENT_UPDATE_VOLUME_SLICE_INDICES_COORDS_TOOLBAR: { sendEvent(Event(eventType).getPointer()); } diff --git a/src/Common/EventTypeEnum.cxx b/src/Common/EventTypeEnum.cxx index 1e536279a..8e3619a83 100644 --- a/src/Common/EventTypeEnum.cxx +++ b/src/Common/EventTypeEnum.cxx @@ -418,6 +418,10 @@ EventTypeEnum::initialize() "EVENT_UPDATE_VOLUME_EDITING_TOOLBAR", "Update the volume editing toolbar")); + enumData.push_back(EventTypeEnum(EVENT_UPDATE_VOLUME_SLICE_INDICES_COORDS_TOOLBAR, + "EVENT_UPDATE_VOLUME_SLICE_INDICES_COORDS_TOOLBAR", + "Update the volume slices indices and coords in the toolbar")); + enumData.push_back(EventTypeEnum(EVENT_COUNT, "EVENT_COUNT", "Count of events")); diff --git a/src/Common/EventTypeEnum.h b/src/Common/EventTypeEnum.h index 8cea87dd1..ade6bdd81 100644 --- a/src/Common/EventTypeEnum.h +++ b/src/Common/EventTypeEnum.h @@ -214,6 +214,8 @@ public: EVENT_UPDATE_INFORMATION_WINDOWS, /** Update the volume editing toolbar */ EVENT_UPDATE_VOLUME_EDITING_TOOLBAR, + /** Update the slice indices and coordinates in the toolbar */ + EVENT_UPDATE_VOLUME_SLICE_INDICES_COORDS_TOOLBAR, /** Update yoked windows */ EVENT_UPDATE_YOKED_WINDOWS, /* THIS MUST ALWAYS BE LAST - NOT an event type but is number of event types */ diff --git a/src/GuiQt/BrainBrowserWindowToolBarSliceSelection.cxx b/src/GuiQt/BrainBrowserWindowToolBarSliceSelection.cxx index 21ffaeaa0..25fa7dedb 100644 --- a/src/GuiQt/BrainBrowserWindowToolBarSliceSelection.cxx +++ b/src/GuiQt/BrainBrowserWindowToolBarSliceSelection.cxx @@ -293,6 +293,8 @@ m_parentToolBar(parentToolBar) m_volumeIndicesWidgetGroup->add(m_volumeIndicesZcoordSpinBox); m_volumeIndicesWidgetGroup->add(m_volumeSliceProjectionTypeEnumComboBox->getWidget()); m_volumeIndicesWidgetGroup->add(m_volumeIdentificationUpdatesSlicesAction); + + EventManager::get()->addEventListener(this, EventTypeEnum::EVENT_UPDATE_VOLUME_SLICE_INDICES_COORDS_TOOLBAR); } /** @@ -300,6 +302,27 @@ m_parentToolBar(parentToolBar) */ BrainBrowserWindowToolBarSliceSelection::~BrainBrowserWindowToolBarSliceSelection() { + EventManager::get()->removeAllEventsFromListener(this); +} + +/** + * Receive an event. + * + * @param event + * An event for which this instance is listening. + */ +void +BrainBrowserWindowToolBarSliceSelection::receiveEvent(Event* event) +{ + if (event->getEventType() == EventTypeEnum::EVENT_UPDATE_VOLUME_SLICE_INDICES_COORDS_TOOLBAR) { + m_volumeIndicesWidgetGroup->blockAllSignals(true); + this->updateSliceIndicesAndCoordinatesRanges(); + m_volumeIndicesWidgetGroup->blockAllSignals(false); + event->setEventProcessed(); + } + else { + BrainBrowserWindowToolBarSliceSelection::receiveEvent(event); + } } /** diff --git a/src/GuiQt/BrainBrowserWindowToolBarSliceSelection.h b/src/GuiQt/BrainBrowserWindowToolBarSliceSelection.h index 1e489a4cc..cf38a2439 100644 --- a/src/GuiQt/BrainBrowserWindowToolBarSliceSelection.h +++ b/src/GuiQt/BrainBrowserWindowToolBarSliceSelection.h @@ -45,7 +45,8 @@ namespace caret { virtual ~BrainBrowserWindowToolBarSliceSelection(); virtual void updateContent(BrowserTabContent* browserTabContent); - + + virtual void receiveEvent(Event* event) override; // ADD_NEW_METHODS_HERE diff --git a/src/GuiQt/UserInputModeView.cxx b/src/GuiQt/UserInputModeView.cxx index 158453e5e..efd0329cb 100644 --- a/src/GuiQt/UserInputModeView.cxx +++ b/src/GuiQt/UserInputModeView.cxx @@ -279,13 +279,34 @@ UserInputModeView::mouseLeftDrag(const MouseEvent& mouseEvent) if (browserTabContent == NULL) { return; } - browserTabContent->applyMouseRotation(viewportContent, - mouseEvent.getPressedX(), - mouseEvent.getPressedY(), - mouseEvent.getX(), - mouseEvent.getY(), - mouseEvent.getDx(), - mouseEvent.getDy()); + + bool scrollVolumeSlicesFlag(false); + if (browserTabContent->isVolumeSlicesDisplayed()) { + switch (browserTabContent->getSliceProjectionType()) { + case VolumeSliceProjectionTypeEnum::VOLUME_SLICE_PROJECTION_OBLIQUE: + break; + case VolumeSliceProjectionTypeEnum::VOLUME_SLICE_PROJECTION_ORTHOGONAL: + scrollVolumeSlicesFlag = true; + break; + } + } + if (scrollVolumeSlicesFlag) { + browserTabContent->applyMouseVolumeSliceIncrement(viewportContent, + mouseEvent.getPressedX(), + mouseEvent.getPressedY(), + mouseEvent.getDy()); + EventManager::get()->sendSimpleEvent(EventTypeEnum::EVENT_UPDATE_VOLUME_SLICE_INDICES_COORDS_TOOLBAR); + } + else { + browserTabContent->applyMouseRotation(viewportContent, + mouseEvent.getPressedX(), + mouseEvent.getPressedY(), + mouseEvent.getX(), + mouseEvent.getY(), + mouseEvent.getDx(), + mouseEvent.getDy()); + } + /* * Update graphics. */ -- GitLab From d58b4fa798f882cc786d12ad365480f77b2da111 Mon Sep 17 00:00:00 2001 From: John Harwell Date: Mon, 6 May 2019 15:18:45 -0500 Subject: [PATCH 302/427] Fix problem with "Exit Tile Tabs" not displayed in the View Menu on MacOS 10.14 and Qt 5.12 by setting the Tile Tab Menu's "Menu Role" to QAction::NoRole. This problem may have affected other versions of MacOS and/or Qt. --- src/GuiQt/BrainBrowserWindow.cxx | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/GuiQt/BrainBrowserWindow.cxx b/src/GuiQt/BrainBrowserWindow.cxx index 8ad9624e1..c395fbb95 100644 --- a/src/GuiQt/BrainBrowserWindow.cxx +++ b/src/GuiQt/BrainBrowserWindow.cxx @@ -1436,7 +1436,14 @@ BrainBrowserWindow::createActions() this); QObject::connect(m_viewTileTabsAction, SIGNAL(triggered()), this, SLOT(processViewTileTabs())); - + /* + * Fix on MacOS with Qt 5.12: + * Set role to 'NoRole' or else Qt may interpret + * "Enter Tile Tabs" and "Exit Tile Tabs" as "Enter + * Full Screen" and remove this item from the menu + */ + m_viewTileTabsAction->setMenuRole(QAction::NoRole); + m_viewTileTabsConfigurationDialogAction = WuQtUtilities::createAction("Edit Tile Tabs Configurations...", "", this, -- GitLab From 0551605383fe5f04f23b63a03655665907dacf82 Mon Sep 17 00:00:00 2001 From: John Harwell Date: Tue, 7 May 2019 08:56:06 -0500 Subject: [PATCH 303/427] Fix problem with "Enter Full Screen" not functioning in the View Menu on MacOS 10.14 and Qt 5.12 by setting the "Enter Full Screen's" 'Menu Role' to QAction::NoRole. This problem may have affected other versions of MacOS and/or Qt. --- src/GuiQt/BrainBrowserWindow.cxx | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/GuiQt/BrainBrowserWindow.cxx b/src/GuiQt/BrainBrowserWindow.cxx index c395fbb95..131e3117f 100644 --- a/src/GuiQt/BrainBrowserWindow.cxx +++ b/src/GuiQt/BrainBrowserWindow.cxx @@ -1425,7 +1425,12 @@ BrainBrowserWindow::createActions() this); QObject::connect(m_viewFullScreenAction, SIGNAL(triggered()), this, SLOT(processViewFullScreenSelected())); - + /* + * "Full Screen" Fix on MacOS with Qt 5.12: + * Without this, the menu item may disappear on MacOS + */ + m_viewFullScreenAction->setMenuRole(QAction::NoRole); + /* * Note: If shortcut key is changed, also change the shortcut key * for the tile tabs configuration dialog menu item to match. -- GitLab From 74e26ac6346cfa842eff3668812356e69cabbad9 Mon Sep 17 00:00:00 2001 From: John Harwell Date: Tue, 7 May 2019 15:47:09 -0500 Subject: [PATCH 304/427] Added support for annotation files to Operation Parameters and Command Parser. --- src/Commands/CommandParser.cxx | 41 +++++++++++++++++++ src/OperationsBase/OperationParameters.cxx | 23 +++++++++++ src/OperationsBase/OperationParameters.h | 14 +++++++ .../OperationParametersEnum.cxx | 5 +++ src/OperationsBase/OperationParametersEnum.h | 3 +- 5 files changed, 85 insertions(+), 1 deletion(-) diff --git a/src/Commands/CommandParser.cxx b/src/Commands/CommandParser.cxx index 2f0c4226a..1486779ba 100644 --- a/src/Commands/CommandParser.cxx +++ b/src/Commands/CommandParser.cxx @@ -21,6 +21,7 @@ #include "CommandParser.h" #include "AlgorithmException.h" +#include "AnnotationFile.h" #include "ApplicationInformation.h" #include "BorderFile.h" #include "CaretAssert.h" @@ -154,6 +155,30 @@ void CommandParser::parseComponent(ParameterComponent* myComponent, ProgramParam try { switch (myComponent->m_paramList[i]->getType()) { + case OperationParametersEnum::ANNOTATION: + { + CaretPointer myFile(new AnnotationFile()); + myFile->readFile(nextArg); + if (m_doProvenance) + { + const GiftiMetaData* md = myFile->getFileMetaData(); + if (md != NULL) + { + AString prov = md->get(PROVENANCE_NAME); + if (prov != "") + { + m_parentProvenance += nextArg + ":\n" + prov + "\n\n"; + } + } + } + ((AnnotationParameter*)myComponent->m_paramList[i])->m_parameter = myFile; + if (debug) + { + cout << "Parameter <" << myComponent->m_paramList[i]->m_shortName << "> opened file with name "; + cout << nextArg << endl; + } + break; + } case OperationParametersEnum::BOOL: { parameters.backup(); @@ -375,6 +400,7 @@ void CommandParser::parseComponent(ParameterComponent* myComponent, ProgramParam catch (const bad_alloc&) { switch (nextType) { + case OperationParametersEnum::ANNOTATION: case OperationParametersEnum::BORDER: case OperationParametersEnum::CIFTI: case OperationParametersEnum::FOCI: @@ -427,6 +453,12 @@ void CommandParser::parseComponent(ParameterComponent* myComponent, ProgramParam tempItem.m_param = myComponent->m_outputList[i]; switch (myComponent->m_outputList[i]->getType())//allocate outputs that only have in-memory implementations { + case OperationParametersEnum::ANNOTATION: + { + CaretPointer& myFile = ((AnnotationParameter*)(myComponent->m_outputList[i]))->m_parameter; + myFile.grabNew(new AnnotationFile()); + break; + } case OperationParametersEnum::BORDER: { CaretPointer& myFile = ((BorderParameter*)(myComponent->m_outputList[i]))->m_parameter; @@ -611,6 +643,15 @@ CommandParser::CompletionInfo CommandParser::completionComponent(ParameterCompon }//the above conditional does a continue unless we need to do completion now switch (myComponent->m_paramList[i]->getType()) { + case OperationParametersEnum::ANNOTATION: + if (ret.completionHints != "") ret.completionHints += " "; + if (useExtGlob) + { + ret.completionHints += "fileglob *.?(wb_)annot"; + } else { + ret.completionHints += "fileglob *.annot fileglob *.wb_annot"; + } + break; case OperationParametersEnum::BOOL: if (ret.completionHints != "") ret.completionHints += " "; ret.completionHints += "wordlist true\\ TRUE\\ false\\ FALSE"; diff --git a/src/OperationsBase/OperationParameters.cxx b/src/OperationsBase/OperationParameters.cxx index 8f1784178..6f3a3f27b 100644 --- a/src/OperationsBase/OperationParameters.cxx +++ b/src/OperationsBase/OperationParameters.cxx @@ -23,6 +23,7 @@ #include "CaretAssert.h" #include "CaretLogger.h" +#include "AnnotationFile.h" #include "BorderFile.h" #include "CiftiFile.h" #include "FociFile.h" @@ -291,6 +292,12 @@ void ParameterComponent::addDoubleParameter(const int32_t key, const AString& na m_paramList.push_back(new DoubleParameter(key, name, description)); } +void ParameterComponent::addAnnotationParameter(const int32_t key, const AString& name, const AString& description) +{ + CaretAssertMessage(checkUniqueInput(key, OperationParametersEnum::ANNOTATION), "input annotation parameter created with previously used key"); + m_paramList.push_back(new AnnotationParameter(key, name, description)); +} + void ParameterComponent::addMetricParameter(const int32_t key, const AString& name, const AString& description) { CaretAssertMessage(checkUniqueInput(key, OperationParametersEnum::METRIC), "input metric parameter created with previously used key"); @@ -350,6 +357,12 @@ void ParameterComponent::addBorderOutputParameter(const int32_t key, const AStri m_outputList.push_back(new BorderParameter(key, name, description)); } +void ParameterComponent::addAnnotationOutputParameter(const int32_t key, const AString& name, const AString& description) +{ + CaretAssertMessage(checkUniqueOutput(key, OperationParametersEnum::ANNOTATION), "output annotation parameter created with previously used key"); + m_outputList.push_back(new AnnotationParameter(key, name, description)); +} + void ParameterComponent::addMetricOutputParameter(const int32_t key, const AString& name, const AString& description) { CaretAssertMessage(checkUniqueOutput(key, OperationParametersEnum::METRIC), "output metric parameter created with previously used key"); @@ -418,6 +431,11 @@ LabelFile* ParameterComponent::getLabel(const int32_t key) return ((LabelParameter*)getInputParameter(key, OperationParametersEnum::LABEL))->m_parameter.getPointer(); } +AnnotationFile* ParameterComponent::getAnnotation(const int32_t key) +{ + return ((AnnotationParameter*)getInputParameter(key, OperationParametersEnum::ANNOTATION))->m_parameter.getPointer(); +} + MetricFile* ParameterComponent::getMetric(const int32_t key) { return ((MetricParameter*)getInputParameter(key, OperationParametersEnum::METRIC))->m_parameter.getPointer(); @@ -468,6 +486,11 @@ VolumeFile* ParameterComponent::getOutputVolume(const int32_t key) return ((VolumeParameter*)getOutputParameter(key, OperationParametersEnum::VOLUME))->m_parameter.getPointer(); } +AnnotationFile* ParameterComponent::getOutputAnnotation(const int32_t key) +{ + return ((AnnotationParameter*)getOutputParameter(key, OperationParametersEnum::ANNOTATION))->m_parameter.getPointer(); +} + MetricFile* ParameterComponent::getOutputMetric(const int32_t key) { return ((MetricParameter*)getOutputParameter(key, OperationParametersEnum::METRIC))->m_parameter.getPointer(); diff --git a/src/OperationsBase/OperationParameters.h b/src/OperationsBase/OperationParameters.h index 0f8bd0790..493711276 100644 --- a/src/OperationsBase/OperationParameters.h +++ b/src/OperationsBase/OperationParameters.h @@ -29,6 +29,7 @@ namespace caret { + class AnnotationFile; class BorderFile; class CiftiFile; class FociFile; @@ -110,6 +111,12 @@ namespace caret { ///get a volume with a key VolumeFile* getVolume(const int32_t key); + ///add a parameter to get next item as an annotation file + void addAnnotationParameter(const int32_t key, const AString& name, const AString& description); + + ///get an annotation with a key + AnnotationFile* getAnnotation(const int32_t key); + ///add a parameter to get next item as a functional file (metric) void addMetricParameter(const int32_t key, const AString& name, const AString& description); @@ -152,6 +159,12 @@ namespace caret { ///get a volume with a key VolumeFile* getOutputVolume(const int32_t key); + ///add a parameter to get next item as an annotation file + void addAnnotationOutputParameter(const int32_t key, const AString& name, const AString& description); + + ///get an annotation with a key + AnnotationFile* getOutputAnnotation(const int32_t key); + ///add a parameter to get next item as a functional file (metric) void addMetricOutputParameter(const int32_t key, const AString& name, const AString& description); @@ -327,6 +340,7 @@ namespace caret { //some friendlier names typedef PointerTemplateParameter SurfaceParameter; typedef PointerTemplateParameter VolumeParameter; + typedef PointerTemplateParameter AnnotationParameter; typedef PointerTemplateParameter MetricParameter; typedef PointerTemplateParameter LabelParameter; typedef PointerTemplateParameter CiftiParameter; diff --git a/src/OperationsBase/OperationParametersEnum.cxx b/src/OperationsBase/OperationParametersEnum.cxx index ff2b6e5d1..ef93e6b5a 100644 --- a/src/OperationsBase/OperationParametersEnum.cxx +++ b/src/OperationsBase/OperationParametersEnum.cxx @@ -133,6 +133,11 @@ OperationParametersEnum::initialize() "Boolean", "Boolean")); + enumData.push_back(OperationParametersEnum(ANNOTATION, + 11, + "Annotation File", + "Annotation")); + } /** diff --git a/src/OperationsBase/OperationParametersEnum.h b/src/OperationsBase/OperationParametersEnum.h index b86acb8ba..45b8cf3d0 100644 --- a/src/OperationsBase/OperationParametersEnum.h +++ b/src/OperationsBase/OperationParametersEnum.h @@ -45,7 +45,8 @@ public: DOUBLE, INT, STRING, - BOOL + BOOL, + ANNOTATION }; -- GitLab From 020f3fea7ae930bd4106ffbaed442a8acc5ed7c4 Mon Sep 17 00:00:00 2001 From: John Harwell Date: Tue, 7 May 2019 15:47:57 -0500 Subject: [PATCH 305/427] Added initial implementation of new wb_command -annotation-resample --- .../AlgorithmAnnotationResample.cxx | 409 ++++++++++++++++++ src/Algorithms/AlgorithmAnnotationResample.h | 82 ++++ src/Algorithms/CMakeLists.txt | 2 + src/Commands/CommandOperationManager.cxx | 2 + 4 files changed, 495 insertions(+) create mode 100644 src/Algorithms/AlgorithmAnnotationResample.cxx create mode 100644 src/Algorithms/AlgorithmAnnotationResample.h diff --git a/src/Algorithms/AlgorithmAnnotationResample.cxx b/src/Algorithms/AlgorithmAnnotationResample.cxx new file mode 100644 index 000000000..db92dde97 --- /dev/null +++ b/src/Algorithms/AlgorithmAnnotationResample.cxx @@ -0,0 +1,409 @@ + +/*LICENSE_START*/ +/* + * Copyright (C) 2019 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + +#include "CaretAssert.h" +#include "CaretLogger.h" + +#include "AlgorithmAnnotationResample.h" +#include "AlgorithmException.h" +#include "Annotation.h" +#include "AnnotationCoordinate.h" +#include "AnnotationFile.h" +#include "AnnotationGroup.h" +#include "AnnotationOneDimensionalShape.h" +#include "AnnotationTwoDimensionalShape.h" +#include "DataFileException.h" +#include "SurfaceFile.h" + +using namespace caret; + +/** + * \class caret::AlgorithmAnnotationResample + * \brief RESAMPLE AN ANNOTATION FILE TO DIFFERENT MESHES + */ + +/** + * @return Command line switch + */ +AString +AlgorithmAnnotationResample::getCommandSwitch() +{ + return "-annotation-resample"; +} + +/** + * @return Short description of algorithm + */ +AString +AlgorithmAnnotationResample::getShortDescription() +{ + return "RESAMPLE AN ANNOTATION FILE TO DIFFERENT MESHES"; +} + +/** + * @return Parameters for algorithm + */ +OperationParameters* +AlgorithmAnnotationResample::getParameters() +{ + OperationParameters* ret = new OperationParameters(); + + int32_t paramIndex(1); + + ret->addAnnotationParameter(paramIndex, "annotation-in", "the annotation file to resample"); + paramIndex++; + + /* + * We need to preserve 'annotation groups' that may be created by the user. + * By modifying the annotation file that was read, any user created + * groups are preserved. + */ + ret->addStringParameter(paramIndex, "annotation-out", "name of resampled annotation file"); + paramIndex++; + + ParameterComponent* sourceSurfOpt = ret->createRepeatableParameter(paramIndex, + "-source-surface", + "surface with mesh used by input annotation file"); + sourceSurfOpt->addSurfaceParameter(1, "source-surface", "source surface file"); + paramIndex++; + + ParameterComponent* targetSurfOpt = ret->createRepeatableParameter(paramIndex, + "-target-surface", + "surface with mesh for output annotation file"); + targetSurfOpt->addSurfaceParameter(1, "target-surface", "target surface file"); + paramIndex++; + + AString helpText = ("Resample an annotation file from the source mesh to the target mesh.\n\n" + "Only annotations in surface space are modified, no changes are made to " + "annotations in other spaces. " + "Both -source-surface and -target-surface options must be repeated for all " + "structures used by surface space annotations."); + + ret->setHelpText(helpText); + + return ret; +} + +/** + * Use Parameters and perform algorithm + * @param myParams + * Parameters for algorithm + * @param myProgObj + * The progress object + * @throws + * AlgorithmException if errors + */ +void +AlgorithmAnnotationResample::useParameters(OperationParameters* myParams, + ProgressObject* myProgObj) +{ + int32_t paramIndex(1); + + AnnotationFile* annotIn = myParams->getAnnotation(paramIndex); + paramIndex++; + + AString outputFileName = myParams->getString(paramIndex); + paramIndex++; + + std::vector sourceSurfaces; + for (auto instance : *(myParams->getRepeatableParameterInstances(paramIndex))) { + sourceSurfaces.push_back(instance->getSurface(1)); + } + paramIndex++; + + std::vector targetSurfaces; + for (auto instance : *(myParams->getRepeatableParameterInstances(paramIndex))) { + targetSurfaces.push_back(instance->getSurface(1)); + } + paramIndex++; + + /* + * Constructs and executes the algorithm + */ + AlgorithmAnnotationResample(myProgObj, + annotIn, + outputFileName, + sourceSurfaces, + targetSurfaces); + +} + +/** + * Constructor + * + * Calling the constructor will execute the algorithm + * + * @param myProgObj + * Parameters for algorithm + */ +AlgorithmAnnotationResample::AlgorithmAnnotationResample(ProgressObject* myProgObj, + AnnotationFile* annotationFile, + const AString& annotationFileName, + const std::vector& sourceSurfaces, + const std::vector& targetSurfaces) + : AbstractAlgorithm(myProgObj) +{ + /* + * Sets the algorithm up to use the progress object, and will + * finish the progress object automatically when the algorithm terminates + */ + LevelProgress myProgress(myProgObj); + + if (annotationFileName.isEmpty()) { + throw AlgorithmException("Output annotation file name is empty."); + } + + setupSurfaces(sourceSurfaces, + targetSurfaces); + + + std::vector allAnnotations; + annotationFile->getAllAnnotations(allAnnotations); + + for (auto ann : allAnnotations) { + CaretAssert(ann); + if (ann->getCoordinateSpace() == AnnotationCoordinateSpaceEnum::SURFACE) { + resampleAnnotation(ann); + } + } + + try { + const AString outputFileName(DataFileTypeEnum::addFileExtensionIfMissing(annotationFileName, + DataFileTypeEnum::ANNOTATION)); + annotationFile->writeFile(outputFileName); + } + catch (const DataFileException& dfe) { + throw AlgorithmException(dfe); + } +} + +/** + * Resample the given annotation + * + * @param ann + * The annotation + */ +void +AlgorithmAnnotationResample::resampleAnnotation(Annotation* ann) +{ + CaretAssert(ann); + CaretAssert(ann->getCoordinateSpace() == AnnotationCoordinateSpaceEnum::SURFACE); + + AnnotationOneDimensionalShape* oneDimAnn = ann->castToOneDimensionalShape(); + AnnotationTwoDimensionalShape* twoDimAnn = ann->castToTwoDimensionalShape(); + + std::vector coordinates; + if (oneDimAnn != NULL) { + coordinates.push_back(oneDimAnn->getStartCoordinate()); + coordinates.push_back(oneDimAnn->getEndCoordinate()); + } + else if (twoDimAnn != NULL) { + coordinates.push_back(twoDimAnn->getCoordinate()); + } + else { + const AString msg("Annotation is neither one- nor two-dimensional " + + ann->toString()); + CaretAssertMessage(0, msg); + CaretLogSevere(msg); + return; + } + + for (auto coord : coordinates) { + StructureEnum::Enum structure = StructureEnum::INVALID; + int32_t numberOfVertices(-1); + int32_t vertexIndex(-1); + coord->getSurfaceSpace(structure, numberOfVertices, vertexIndex); + + if (numberOfVertices < 0) { + throw AlgorithmException("Annotation in surface space has invalid number of vertices=" + + AString::number(numberOfVertices) + + " for " + + ann->toString()); + } + if (vertexIndex < 0) { + throw AlgorithmException("Annotation in surface space has invalid vertex index=" + + AString::number(vertexIndex) + + " for " + + ann->toString()); + } + if (vertexIndex > numberOfVertices) { + throw AlgorithmException("Annotation has invalid vertex index=" + + AString::number(vertexIndex) + + " but number of vertices=" + + AString::number(numberOfVertices) + + " for " + + ann->toString()); + } + + const auto stsIter = m_surfaces.find(structure); + if (stsIter != m_surfaces.end()) { + SourceTargetSurface* sts = stsIter->second; + CaretAssert(sts); + CaretAssert(sts->m_source); + CaretAssert(sts->m_target); + + if (numberOfVertices != sts->m_source->getNumberOfNodes()) { + throw AlgorithmException("Source surface with structure " + + StructureEnum::toName(structure) + + " contains " + + AString::number(sts->m_source->getNumberOfNodes()) + + " vertices but annotation requires surface with " + + AString::number(numberOfVertices) + + " for " + + ann->toString()); + } + float xyz[3]; + sts->m_source->getCoordinate(vertexIndex, + xyz); + + const int32_t targetVertexIndex = sts->m_target->closestNode(xyz); + if (targetVertexIndex >= 0) { + coord->setSurfaceSpace(structure, + sts->m_target->getNumberOfNodes(), + targetVertexIndex); + } + else { + throw AlgorithmException("Unable to find closest vertex in target surface for " + + ann->toString()); + } + } + else { + throw AlgorithmException("There are no surfaces for annotation with structure " + + StructureEnum::toName(structure) + + " for " + + ann->toString()); + } + } +} + +/** + * Setup the surfaces and validate them + * + * @param sourceSurfaces + * The source surfaces + * @param targetSurfaces + * The target surfaces + */ +void +AlgorithmAnnotationResample::setupSurfaces(const std::vector& sourceSurfaces, + const std::vector& targetSurfaces) +{ + if (sourceSurfaces.empty()) { + throw AlgorithmException("There are no source surfaces"); + } + if (targetSurfaces.empty()) { + throw AlgorithmException("There are no target surfaces"); + } + + /* + * Verify each source surface uses a unique structure + */ + for (const auto surf : sourceSurfaces) { + const StructureEnum::Enum structure = surf->getStructure(); + const auto iter = m_surfaces.find(structure); + if (iter == m_surfaces.end()) { + m_surfaces.insert(std::make_pair(structure, + new SourceTargetSurface(surf))); + } + else { + throw AlgorithmException("Source surfaces have same structure: " + + iter->second->m_source->getFileName() + + " and " + + surf->getFileName()); + } + } + + /* + * Verify target surfaces use unique structure and + * there is a source surface using the same structure + */ + for (const auto surf : targetSurfaces) { + const StructureEnum::Enum structure = surf->getStructure(); + auto iter = m_surfaces.find(structure); + if (iter == m_surfaces.end()) { + throw AlgorithmException("Target surface " + + surf->getFileName() + + " uses structure " + + StructureEnum::toName(structure) + + " but there is no source surface with the structure."); + } + else { + SourceTargetSurface* sts = iter->second; + CaretAssert(sts); + if (sts->m_target == NULL) { + sts->m_target = surf; + } + else { + throw AlgorithmException("Target surfaces have same structure: " + + sts->m_target->getFileName() + + " and " + + surf->getFileName()); + } + } + } + + /* + * Verify no missing target surfaces + */ + for (auto iter : m_surfaces) { + SourceTargetSurface* sts = iter.second; + CaretAssert(sts); + CaretAssert(sts->m_source); + if (sts->m_target == NULL) { + throw AlgorithmException("There is no target surface with structure " + + StructureEnum::toName(iter.first) + + " for source surface " + + sts->m_source->getFileName()); + } + } + + for (auto iter : m_surfaces) { + CaretAssert(iter.second); + CaretAssert(iter.second->m_source); + CaretAssert(iter.second->m_target); + } +} + +/** + * @return Algorithm internal weight + */ +float +AlgorithmAnnotationResample::getAlgorithmInternalWeight() +{ + /* + * override this if needed, if the progress bar isn't smooth + */ + return 1.0f; +} + +/** + * @return Algorithm sub-algorithm weight + */ +float +AlgorithmAnnotationResample::getSubAlgorithmWeight() +{ + /* + * If you use a subalgorithm + */ + //return AlgorithmInsertNameHere::getAlgorithmWeight() + return 0.0f; +} + diff --git a/src/Algorithms/AlgorithmAnnotationResample.h b/src/Algorithms/AlgorithmAnnotationResample.h new file mode 100644 index 000000000..e1d46d443 --- /dev/null +++ b/src/Algorithms/AlgorithmAnnotationResample.h @@ -0,0 +1,82 @@ +#ifndef __ALGORITHM_ANNOTATION_RESAMPLE_H__ +#define __ALGORITHM_ANNOTATION_RESAMPLE_H__ + +/*LICENSE_START*/ +/* + * Copyright (C) 2019 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + +#include + +#include "AbstractAlgorithm.h" + +namespace caret { + + class Annotation; + + class AlgorithmAnnotationResample : public AbstractAlgorithm { + + private: + AlgorithmAnnotationResample(); + + protected: + static float getSubAlgorithmWeight(); + + static float getAlgorithmInternalWeight(); + + public: + AlgorithmAnnotationResample(ProgressObject* myProgObj, + AnnotationFile* annotationFile, + const AString& annotationFileName, + const std::vector& sourceSurfaces, + const std::vector& targetSurfaces); + + static OperationParameters* getParameters(); + + static void useParameters(OperationParameters* myParams, + ProgressObject* myProgObj); + + static AString getCommandSwitch(); + + static AString getShortDescription(); + + private: + class SourceTargetSurface { + public: + SourceTargetSurface(const SurfaceFile* source) + : m_source(source), + m_target(NULL) { } + + const SurfaceFile* m_source; + const SurfaceFile* m_target; + }; + + std::map m_surfaces; + + void setupSurfaces(const std::vector& sourceSurfaces, + const std::vector& targetSurfaces); + + void resampleAnnotation(Annotation* ann); + }; + + typedef TemplateAutoOperation AutoAlgorithmAnnotationResample; + +} // namespace + +#endif //__ALGORITHM_ANNOTATION_RESAMPLE_H__ + diff --git a/src/Algorithms/CMakeLists.txt b/src/Algorithms/CMakeLists.txt index e7ff9b755..0acf354f7 100644 --- a/src/Algorithms/CMakeLists.txt +++ b/src/Algorithms/CMakeLists.txt @@ -23,6 +23,7 @@ ENDIF () # ADD_LIBRARY(Algorithms AbstractAlgorithm.h +AlgorithmAnnotationResample.h AlgorithmBorderResample.h AlgorithmBorderToVertices.h AlgorithmCiftiAllLabelsToROIs.h @@ -141,6 +142,7 @@ AlgorithmVolumeWarpfieldResample.h OverlapLogicEnum.h AbstractAlgorithm.cxx +AlgorithmAnnotationResample.cxx AlgorithmBorderResample.cxx AlgorithmBorderToVertices.cxx AlgorithmCiftiAllLabelsToROIs.cxx diff --git a/src/Commands/CommandOperationManager.cxx b/src/Commands/CommandOperationManager.cxx index e0f29abfd..e87d9ba93 100644 --- a/src/Commands/CommandOperationManager.cxx +++ b/src/Commands/CommandOperationManager.cxx @@ -24,6 +24,7 @@ #include "CommandOperationManager.h" #undef __COMMAND_OPERATION_MANAGER_DEFINE__ +#include "AlgorithmAnnotationResample.h" #include "AlgorithmBorderResample.h" #include "AlgorithmBorderToVertices.h" #include "AlgorithmCiftiAllLabelsToROIs.h" @@ -285,6 +286,7 @@ CommandOperationManager::deleteCommandOperationManager() */ CommandOperationManager::CommandOperationManager() { + this->commandOperations.push_back(new CommandParser(new AutoAlgorithmAnnotationResample())); this->commandOperations.push_back(new CommandParser(new AutoAlgorithmBorderResample())); this->commandOperations.push_back(new CommandParser(new AutoAlgorithmBorderToVertices())); this->commandOperations.push_back(new CommandParser(new AutoAlgorithmCiftiAllLabelsToROIs())); -- GitLab From 7993d1fe8657cffa124e2e1b78d12fd73ff734fc Mon Sep 17 00:00:00 2001 From: John Harwell Date: Wed, 8 May 2019 11:20:56 -0500 Subject: [PATCH 306/427] In Features ToolBox --> Annotations, add On and Off buttons so that user can select more than one (often many) annotations in the list of annotations and turn their display status checkboxes on and off. --- .../DisplayGroupAndTabItemViewController.cxx | 133 +++++++++++++++++- .../DisplayGroupAndTabItemViewController.h | 13 ++ 2 files changed, 144 insertions(+), 2 deletions(-) diff --git a/src/GuiQt/DisplayGroupAndTabItemViewController.cxx b/src/GuiQt/DisplayGroupAndTabItemViewController.cxx index 0fe90256a..8ac882c00 100644 --- a/src/GuiQt/DisplayGroupAndTabItemViewController.cxx +++ b/src/GuiQt/DisplayGroupAndTabItemViewController.cxx @@ -23,7 +23,10 @@ #include "DisplayGroupAndTabItemViewController.h" #undef __DISPLAY_GROUP_AND_TAB_ITEM_VIEW_CONTROLLER_DECLARE__ +#include +#include #include +#include #include #include "Annotation.h" @@ -66,9 +69,32 @@ DisplayGroupAndTabItemViewController::DisplayGroupAndTabItemViewController(const m_dataFileType(dataFileType), m_browserWindowIndex(browserWindowIndex) { + const QString onOffToolTip("" + "To select more than one item:
        " + "* For a contiguous selection, click an item " + "and then click another item while holding down " + "the SHIFT key.
        " + "* For non-contiguous selection, select items while " + "holding down the CTRL key (Command key on Apple)" + ""); + m_turnOnSelectedItemsAction = new QAction("On"); + m_turnOnSelectedItemsAction->setToolTip(onOffToolTip); + m_turnOnSelectedItemsAction->setCheckable(false); + QObject::connect(m_turnOnSelectedItemsAction, &QAction::triggered, + this, &DisplayGroupAndTabItemViewController::turnOnSelectedItemsTriggered); + QToolButton* turnOnToolButton = new QToolButton(); + turnOnToolButton->setDefaultAction(m_turnOnSelectedItemsAction); + + m_turnOffSelectedItemsAction = new QAction("Off"); + m_turnOffSelectedItemsAction->setToolTip(onOffToolTip); + m_turnOffSelectedItemsAction->setCheckable(false); + QObject::connect(m_turnOffSelectedItemsAction, &QAction::triggered, + this, &DisplayGroupAndTabItemViewController::turnOffSelectedItemsTriggered); + QToolButton* turnOffToolButton = new QToolButton(); + turnOffToolButton->setDefaultAction(m_turnOffSelectedItemsAction); + m_treeWidget = new QTreeWidget(); m_treeWidget->setHeaderHidden(true); - //m_treeWidget->setSelectionMode(QTreeWidget::ExtendedSelection); m_treeWidget->setSelectionMode(QTreeWidget::NoSelection); QObject::connect(m_treeWidget, SIGNAL(itemCollapsed(QTreeWidgetItem*)), @@ -79,8 +105,20 @@ m_browserWindowIndex(browserWindowIndex) this, SLOT(itemWasChanged(QTreeWidgetItem*, int))); QObject::connect(m_treeWidget, SIGNAL(itemSelectionChanged()), this, SLOT(itemsWereSelected())); + m_treeWidget->setContextMenuPolicy(Qt::CustomContextMenu); + QObject::connect(m_treeWidget, &QTreeWidget::customContextMenuRequested, + this, &DisplayGroupAndTabItemViewController::displayContextMenu); + + QHBoxLayout* buttonLayout = new QHBoxLayout(); + buttonLayout->setContentsMargins(0, 0, 0, 0); + buttonLayout->addWidget(new QLabel("Selected Items: ")); + buttonLayout->addWidget(turnOnToolButton); + buttonLayout->addSpacing(5); + buttonLayout->addWidget(turnOffToolButton); + buttonLayout->addStretch(); QVBoxLayout* layout = new QVBoxLayout(this); + layout->addLayout(buttonLayout); layout->addWidget(m_treeWidget, 100); s_allViewControllers.insert(this); @@ -102,6 +140,7 @@ DisplayGroupAndTabItemViewController::itemsWereSelected() { QList itemsSelected = m_treeWidget->selectedItems(); + if ( ! itemsSelected.empty()) { std::vector itemInterfacesVector; @@ -129,11 +168,39 @@ DisplayGroupAndTabItemViewController::itemsWereSelected() getDisplayGroupAndTabIndex(displayGroup, tabIndex); updateSelectedAndExpandedCheckboxes(displayGroup, tabIndex); - //updateSelectedAndExpandedCheckboxesInOtherViewControllers(); updateGraphics(); } +/** + * Display a context sensitive (right-click) menu. + * + * @param pos + * Position for context menu + */ +void +DisplayGroupAndTabItemViewController::displayContextMenu(const QPoint& pos) +{ + QList itemsSelected = m_treeWidget->selectedItems(); + + if (itemsSelected.isEmpty()) { + return; + } + + QMenu menu(this); + QAction* onAction = menu.addAction("Turn all selected items ON"); + menu.addAction("Turn all selected items OFF"); + + QSignalBlocker blocker(m_treeWidget); + QAction* selectedAction = menu.exec(m_treeWidget->mapToGlobal(pos)); + if (selectedAction == NULL) { + return; + } + + const bool newStatus = (selectedAction == onAction); + setCheckedStatusOfSelectedItems(newStatus); +} + /** * Process the selection of annotations. * @@ -414,6 +481,10 @@ DisplayGroupAndTabItemViewController::updateSelectedAndExpandedCheckboxes(const } } + const bool itemsSelectedFlag = ( ! m_treeWidget->selectedItems().isEmpty()); + m_turnOnSelectedItemsAction->setEnabled(itemsSelectedFlag); + m_turnOffSelectedItemsAction->setEnabled(itemsSelectedFlag); + m_treeWidget->blockSignals(false); } @@ -442,3 +513,61 @@ DisplayGroupAndTabItemViewController::updateSelectedAndExpandedCheckboxesInOther } } +/** + * Turn on all selected items + */ +void +DisplayGroupAndTabItemViewController::turnOnSelectedItemsTriggered() +{ + setCheckedStatusOfSelectedItems(true); +} + +/** + * Turn off all selected items + */ +void +DisplayGroupAndTabItemViewController::turnOffSelectedItemsTriggered() +{ + setCheckedStatusOfSelectedItems(false); +} + +/** + * Set the checked status of all selected itemsj + * + * @param checkedStatus + * Checked status + */ +void +DisplayGroupAndTabItemViewController::setCheckedStatusOfSelectedItems(const bool checkedStatus) +{ + QList itemsSelected = m_treeWidget->selectedItems(); + + if (itemsSelected.isEmpty()) { + return; + } + + DisplayGroupEnum::Enum displayGroup = DisplayGroupEnum::DISPLAY_GROUP_TAB; + int32_t tabIndex = -1; + getDisplayGroupAndTabIndex(displayGroup, tabIndex); + + + const Qt::CheckState newCheckState = (checkedStatus + ? Qt::Checked + : Qt::Unchecked); + QListIterator iter(itemsSelected); + while (iter.hasNext()) { + QTreeWidgetItem* item = iter.next(); + DisplayGroupAndTabItemInterface* dataItem = getDataItem(item); + + const TriStateSelectionStatusEnum::Enum itemCheckState = DisplayGroupAndTabItemTreeWidgetItem::fromQCheckState(newCheckState); + dataItem->setItemDisplaySelected(displayGroup, + tabIndex, + itemCheckState); + } + + updateSelectedAndExpandedCheckboxes(displayGroup, + tabIndex); + updateSelectedAndExpandedCheckboxesInOtherViewControllers(); + + updateGraphics();} + diff --git a/src/GuiQt/DisplayGroupAndTabItemViewController.h b/src/GuiQt/DisplayGroupAndTabItemViewController.h index 48ddc927c..54658c8b8 100644 --- a/src/GuiQt/DisplayGroupAndTabItemViewController.h +++ b/src/GuiQt/DisplayGroupAndTabItemViewController.h @@ -28,6 +28,7 @@ #include "DataFileTypeEnum.h" #include "DisplayGroupEnum.h" +class QAction; class QTreeWidget; class QTreeWidgetItem; namespace caret { @@ -63,6 +64,12 @@ namespace caret { void itemsWereSelected(); + void displayContextMenu(const QPoint& pos); + + void turnOnSelectedItemsTriggered(); + + void turnOffSelectedItemsTriggered(); + private: DisplayGroupAndTabItemViewController(const DisplayGroupAndTabItemViewController&); @@ -87,12 +94,18 @@ namespace caret { void updateSelectedAndExpandedCheckboxesInOtherViewControllers(); + void setCheckedStatusOfSelectedItems(const bool checkedFlag); + const DataFileTypeEnum::Enum m_dataFileType; const int32_t m_browserWindowIndex; QTreeWidget* m_treeWidget; + QAction* m_turnOnSelectedItemsAction; + + QAction* m_turnOffSelectedItemsAction; + static std::set s_allViewControllers; // ADD_NEW_MEMBERS_HERE -- GitLab From fe75e92952a4f6b64fc5963ab6d5a5b4b5f96e45 Mon Sep 17 00:00:00 2001 From: John Harwell Date: Wed, 8 May 2019 14:39:29 -0500 Subject: [PATCH 307/427] Added ON and OFF buttons to the Annotation controls in the Features Toolbox. This allows the user to select groups of annotations while using the shift/ctrl keys and change the display status of a large numbers of annotations. Prior to this change the user had to turn on/off each annotation individually by clicking its checkbox. In addition, the user may also select annotations in the Features Toolbox while NOT in Annotation Mode so that groups of then can be turned on/off (note that the "sizing handles" that are drawn around each of the selected annotations are only displayed in Annotation Mode). --- src/Brain/BrainOpenGL.cxx | 22 +- src/Brain/BrainOpenGL.h | 32 +- ...inOpenGLAnnotationDrawingFixedPipeline.cxx | 8 + ...rainOpenGLAnnotationDrawingFixedPipeline.h | 7 +- src/Brain/BrainOpenGLFixedPipeline.cxx | 58 ++- src/Brain/BrainOpenGLFixedPipeline.h | 6 + .../BrainOpenGLVolumeObliqueSliceDrawing.cxx | 4 +- src/Brain/BrainOpenGLVolumeSliceDrawing.cxx | 4 +- src/Brain/CMakeLists.txt | 2 + src/Brain/UserInputModeEnum.cxx | 393 ++++++++++++++++++ src/Brain/UserInputModeEnum.h | 114 +++++ .../AnnotationSelectionViewController.cxx | 7 +- .../BrainBrowserWindowOrientedToolBox.cxx | 16 +- src/GuiQt/BrainBrowserWindowToolBar.cxx | 34 +- src/GuiQt/BrainOpenGLWidget.cxx | 27 +- .../EventGetOrSetUserInputModeProcessor.cxx | 6 +- .../EventGetOrSetUserInputModeProcessor.h | 6 +- src/GuiQt/UserInputModeAbstract.cxx | 4 +- src/GuiQt/UserInputModeAbstract.h | 26 +- src/GuiQt/UserInputModeAnnotations.cxx | 2 +- src/GuiQt/UserInputModeBorders.cxx | 2 +- src/GuiQt/UserInputModeFoci.cxx | 2 +- src/GuiQt/UserInputModeImage.cxx | 2 +- src/GuiQt/UserInputModeView.cxx | 4 +- src/GuiQt/UserInputModeView.h | 2 +- src/GuiQt/UserInputModeVolumeEdit.cxx | 2 +- src/GuiQt/WuQMacroExecutor.cxx | 2 +- 27 files changed, 685 insertions(+), 109 deletions(-) create mode 100644 src/Brain/UserInputModeEnum.cxx create mode 100644 src/Brain/UserInputModeEnum.h diff --git a/src/Brain/BrainOpenGL.cxx b/src/Brain/BrainOpenGL.cxx index 0e21fbd0b..0dbd254fc 100644 --- a/src/Brain/BrainOpenGL.cxx +++ b/src/Brain/BrainOpenGL.cxx @@ -200,6 +200,8 @@ BrainOpenGL::receiveEvent(Event* event) * * @param windowIndex * Index of window for drawing. + * @param windowsUserInputMode + * User input mode for window * @param brain * The brain (must be valid!) * @param contextSharingGroupPointer @@ -208,6 +210,7 @@ BrainOpenGL::receiveEvent(Event* event) * Viewport info for drawing. */ void BrainOpenGL::drawModels(const int32_t windowIndex, + const UserInputModeEnum::Enum windowsUserInputMode, Brain* brain, void* contextSharingGroupPointer, const std::vector& viewportContents) @@ -222,6 +225,7 @@ void BrainOpenGL::drawModels(const int32_t windowIndex, drawModelsImplementation(windowIndex, + windowsUserInputMode, brain, vpContents); @@ -235,6 +239,8 @@ void BrainOpenGL::drawModels(const int32_t windowIndex, * * @param windowIndex * Index of window for selection. + * @param windowsUserInputMode + * User input mode for window * @param brain * The brain (must be valid!) * @param contextSharingGroupPointer @@ -254,6 +260,7 @@ void BrainOpenGL::drawModels(const int32_t windowIndex, * selected. */ void BrainOpenGL::selectModel(const int32_t windowIndex, + const UserInputModeEnum::Enum windowsUserInputMode, Brain* brain, void* contextSharingGroupPointer, const BrainOpenGLViewportContent* viewportContent, @@ -264,6 +271,7 @@ void BrainOpenGL::selectModel(const int32_t windowIndex, m_contextSharingGroupPointer = contextSharingGroupPointer; selectModelImplementation(windowIndex, + windowsUserInputMode, brain, viewportContent, mouseX, @@ -283,6 +291,8 @@ void BrainOpenGL::selectModel(const int32_t windowIndex, * * @param windowIndex * Index of window for projection + * @param windowsUserInputMode + * User input mode for window * @param brain * The brain (must be valid!) * @param contextSharingGroupPointer @@ -297,16 +307,18 @@ void BrainOpenGL::selectModel(const int32_t windowIndex, * Output with projection result. */ void BrainOpenGL::projectToModel(const int32_t windowIndex, + const UserInputModeEnum::Enum windowUserInputMode, Brain* brain, - void* contextSharingGroupPointer, - const BrainOpenGLViewportContent* viewportContent, - const int32_t mouseX, - const int32_t mouseY, - SurfaceProjectedItem& projectionOut) + void* contextSharingGroupPointer, + const BrainOpenGLViewportContent* viewportContent, + const int32_t mouseX, + const int32_t mouseY, + SurfaceProjectedItem& projectionOut) { m_contextSharingGroupPointer = contextSharingGroupPointer; projectToModelImplementation(windowIndex, + windowUserInputMode, brain, viewportContent, mouseX, diff --git a/src/Brain/BrainOpenGL.h b/src/Brain/BrainOpenGL.h index 38d41d474..83ddf55ec 100644 --- a/src/Brain/BrainOpenGL.h +++ b/src/Brain/BrainOpenGL.h @@ -29,8 +29,8 @@ #include #include "CaretOpenGLInclude.h" - #include "CaretObject.h" +#include "UserInputModeEnum.h" #undef BRAIN_OPENGL_INFO_SUPPORTS_DISPLAY_LISTS #undef BRAIN_OPENGL_INFO_SUPPORTS_IMMEDIATE @@ -103,11 +103,13 @@ namespace caret { BrainOpenGLTextRenderInterface* getTextRenderer(); void drawModels(const int32_t windowIndex, + const UserInputModeEnum::Enum windowUserInputMode, Brain* brain, void* contextSharingGroupPointer, const std::vector& viewportContents); void selectModel(const int32_t windowIndex, + const UserInputModeEnum::Enum windowUserInputMode, Brain* brain, void* contextSharingGroupPointer, const BrainOpenGLViewportContent* viewportContent, @@ -116,6 +118,7 @@ namespace caret { const bool applySelectionBackgroundFiltering); void projectToModel(const int32_t windowIndex, + const UserInputModeEnum::Enum windowUserInputMode, Brain* brain, void* contextSharingGroupPointer, const BrainOpenGLViewportContent* viewportContent, @@ -233,12 +236,15 @@ namespace caret { * * @param windowIndex * Index of window for drawing + * @param windowUserInputMode + * Input mode for window * @param brain * The brain (must be valid!) * @param viewportContents * Viewport info for drawing. */ virtual void drawModelsImplementation(const int32_t windowIndex, + const UserInputModeEnum::Enum windowUserInputMode, Brain* brain, const std::vector& viewportContents) = 0; @@ -247,6 +253,8 @@ namespace caret { * * @param windowIndex * Index of window for selection + * @param userInputMode + * Input mode for window * @param brain * The brain (must be valid!) * @param viewportContent @@ -264,12 +272,13 @@ namespace caret { * selected. */ virtual void selectModelImplementation(const int32_t windowIndex, + const UserInputModeEnum::Enum windowUserInputMode, Brain* brain, - const BrainOpenGLViewportContent* viewportContent, - const int32_t mouseX, - const int32_t mouseY, - const bool applySelectionBackgroundFiltering) = 0; - + const BrainOpenGLViewportContent* viewportContent, + const int32_t mouseX, + const int32_t mouseY, + const bool applySelectionBackgroundFiltering) = 0; + /** * Project the given window coordinate to the active models. * If the projection is successful, The 'original' XYZ @@ -278,6 +287,8 @@ namespace caret { * * @param windowIndex * Index of window for projection + * @param userInputMode + * Input mode for window * @param brain * The brain (must be valid!) * @param viewportContent @@ -290,11 +301,12 @@ namespace caret { * Output with projection result. */ virtual void projectToModelImplementation(const int32_t windowIndex, + const UserInputModeEnum::Enum windowUserInputMode, Brain* brain, - const BrainOpenGLViewportContent* viewportContent, - const int32_t mouseX, - const int32_t mouseY, - SurfaceProjectedItem& projectionOut) = 0; + const BrainOpenGLViewportContent* viewportContent, + const int32_t mouseX, + const int32_t mouseY, + SurfaceProjectedItem& projectionOut) = 0; AString getOpenGLEnabledEnumAsText(const AString& enumName, const GLenum enumValue) const; diff --git a/src/Brain/BrainOpenGLAnnotationDrawingFixedPipeline.cxx b/src/Brain/BrainOpenGLAnnotationDrawingFixedPipeline.cxx index 4ab4949a1..75c5a92c7 100644 --- a/src/Brain/BrainOpenGLAnnotationDrawingFixedPipeline.cxx +++ b/src/Brain/BrainOpenGLAnnotationDrawingFixedPipeline.cxx @@ -4334,6 +4334,10 @@ BrainOpenGLAnnotationDrawingFixedPipeline::drawAnnotationOneDimSizingHandles(Ann const float secondPoint[3], const float lineThickness) { + if ( ! m_inputs->m_annotationUserInputModeFlag) { + return; + } + CaretAssert(annotation); float lengthVector[3]; MathFunctions::subtractVectors(secondPoint, firstPoint, lengthVector); @@ -4445,6 +4449,10 @@ BrainOpenGLAnnotationDrawingFixedPipeline::drawAnnotationTwoDimSizingHandles(Ann const float lineThickness, const float rotationAngle) { + if ( ! m_inputs->m_annotationUserInputModeFlag) { + return; + } + CaretAssert(annotation); AnnotationText* textAnn = dynamic_cast(annotation); const bool modelSpaceTangentTextFlag = annotation->isInSurfaceSpaceWithTangentOffset(); diff --git a/src/Brain/BrainOpenGLAnnotationDrawingFixedPipeline.h b/src/Brain/BrainOpenGLAnnotationDrawingFixedPipeline.h index ba1ec79d6..0207f0f6e 100644 --- a/src/Brain/BrainOpenGLAnnotationDrawingFixedPipeline.h +++ b/src/Brain/BrainOpenGLAnnotationDrawingFixedPipeline.h @@ -69,14 +69,16 @@ namespace caret { const int32_t windowIndex, const int32_t tabIndex, const SpacerTabIndex &spacerTabIndex, - const WindowDrawingMode windowDrawingMode) + const WindowDrawingMode windowDrawingMode, + const bool annotationUserInputModeFlag) : m_brain(brain), m_drawingMode(drawingMode), m_centerToEyeDistance(centerToEyeDistance), m_windowIndex(windowIndex), m_tabIndex(tabIndex), m_spacerTabIndex(spacerTabIndex), - m_windowDrawingMode(windowDrawingMode) { + m_windowDrawingMode(windowDrawingMode), + m_annotationUserInputModeFlag(annotationUserInputModeFlag) { } Brain* m_brain; @@ -86,6 +88,7 @@ namespace caret { const int32_t m_tabIndex; const SpacerTabIndex m_spacerTabIndex; const WindowDrawingMode m_windowDrawingMode; + const bool m_annotationUserInputModeFlag; }; BrainOpenGLAnnotationDrawingFixedPipeline(BrainOpenGLFixedPipeline* brainOpenGLFixedPipeline); diff --git a/src/Brain/BrainOpenGLFixedPipeline.cxx b/src/Brain/BrainOpenGLFixedPipeline.cxx index 1c3b7ee1f..6d6e1c3ff 100644 --- a/src/Brain/BrainOpenGLFixedPipeline.cxx +++ b/src/Brain/BrainOpenGLFixedPipeline.cxx @@ -226,6 +226,8 @@ BrainOpenGLFixedPipeline::~BrainOpenGLFixedPipeline() * * @param windowIndex * Index of window for selection + * @param userInputMode + * Input mode for window * @param brain * The brain (must be valid!) * @param viewportContent @@ -244,14 +246,17 @@ BrainOpenGLFixedPipeline::~BrainOpenGLFixedPipeline() */ void BrainOpenGLFixedPipeline::selectModelImplementation(const int32_t windowIndex, + const UserInputModeEnum::Enum windowUserInputMode, Brain* brain, - const BrainOpenGLViewportContent* viewportContent, - const int32_t mouseX, - const int32_t mouseY, - const bool applySelectionBackgroundFiltering) + const BrainOpenGLViewportContent* viewportContent, + const int32_t mouseX, + const int32_t mouseY, + const bool applySelectionBackgroundFiltering) { m_brain = brain; m_windowIndex = windowIndex; + m_windowUserInputMode = windowUserInputMode; + CaretAssert(m_brain); CaretAssert((m_windowIndex >= 0) && (m_windowIndex < BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_WINDOWS)); @@ -304,6 +309,7 @@ BrainOpenGLFixedPipeline::selectModelImplementation(const int32_t windowIndex, m_brain = NULL; m_windowIndex = -1; + m_windowUserInputMode = UserInputModeEnum::INVALID; } /** @@ -314,6 +320,8 @@ BrainOpenGLFixedPipeline::selectModelImplementation(const int32_t windowIndex, * * @param windowIndex * Index of window for projection + * @param userInputMode + * Input mode for window * @param brain * The brain (must be valid!) * @param viewportContent @@ -327,14 +335,16 @@ BrainOpenGLFixedPipeline::selectModelImplementation(const int32_t windowIndex, */ void BrainOpenGLFixedPipeline::projectToModelImplementation(const int32_t windowIndex, + const UserInputModeEnum::Enum windowUserInputMode, Brain* brain, - const BrainOpenGLViewportContent* viewportContent, - const int32_t mouseX, - const int32_t mouseY, - SurfaceProjectedItem& projectionOut) + const BrainOpenGLViewportContent* viewportContent, + const int32_t mouseX, + const int32_t mouseY, + SurfaceProjectedItem& projectionOut) { m_brain = brain; m_windowIndex = windowIndex; + m_windowUserInputMode = windowUserInputMode; CaretAssert(m_brain); CaretAssert((m_windowIndex >= 0) && (m_windowIndex < BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_WINDOWS)); @@ -368,6 +378,7 @@ BrainOpenGLFixedPipeline::projectToModelImplementation(const int32_t windowIndex this->modeProjectionData = NULL; m_brain = NULL; m_windowIndex = -1; + m_windowUserInputMode = UserInputModeEnum::INVALID; } /** @@ -531,6 +542,8 @@ BrainOpenGLFixedPipeline::setAnnotationColorBarsForDrawing(const std::vector& viewportContents) + const std::vector& viewportContents) { m_brain = brain; m_windowIndex = windowIndex; + m_windowUserInputMode = windowUserInputMode; CaretAssert(m_brain); CaretAssert((m_windowIndex >= 0) && (m_windowIndex < BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_WINDOWS)); @@ -737,6 +752,7 @@ BrainOpenGLFixedPipeline::drawModelsImplementation(const int32_t windowIndex, m_brain = NULL; m_windowIndex = -1; + m_windowUserInputMode = UserInputModeEnum::INVALID; } /** @@ -864,13 +880,15 @@ BrainOpenGLFixedPipeline::drawChartCoordinateSpaceAnnotations(const BrainOpenGLV * Draw annotations for this surface and maybe draw * the model annotations. */ + const bool annotationModeFlag = (m_windowUserInputMode == UserInputModeEnum::ANNOTATIONS); BrainOpenGLAnnotationDrawingFixedPipeline::Inputs inputs(this->m_brain, this->mode, BrainOpenGLFixedPipeline::s_gluLookAtCenterFromEyeOffsetDistance, m_windowIndex, this->windowTabIndex, SpacerTabIndex(), - BrainOpenGLAnnotationDrawingFixedPipeline::Inputs::WINDOW_DRAWING_NO); + BrainOpenGLAnnotationDrawingFixedPipeline::Inputs::WINDOW_DRAWING_NO, + annotationModeFlag); std::vector emptyColorBars; std::vector emptyViewportAnnotations; m_annotationDrawing->drawAnnotations(&inputs, @@ -929,13 +947,15 @@ BrainOpenGLFixedPipeline::drawSpacerAnnotations(const BrainOpenGLViewportContent CaretAssert(spacerTabContent); spacerTabIndex = spacerTabContent->getSpacerTabIndex(); + const bool annotationModeFlag = (m_windowUserInputMode == UserInputModeEnum::ANNOTATIONS); BrainOpenGLAnnotationDrawingFixedPipeline::Inputs inputs(this->m_brain, this->mode, BrainOpenGLFixedPipeline::s_gluLookAtCenterFromEyeOffsetDistance, m_windowIndex, this->windowTabIndex, spacerTabIndex, - BrainOpenGLAnnotationDrawingFixedPipeline::Inputs::WINDOW_DRAWING_NO); + BrainOpenGLAnnotationDrawingFixedPipeline::Inputs::WINDOW_DRAWING_NO, + annotationModeFlag); m_annotationDrawing->drawAnnotations(&inputs, AnnotationCoordinateSpaceEnum::SPACER, m_annotationColorBarsForDrawing, @@ -983,13 +1003,15 @@ BrainOpenGLFixedPipeline::drawTabAnnotations(const BrainOpenGLViewportContent* t this->windowTabIndex = this->browserTabContent->getTabNumber(); + const bool annotationModeFlag = (m_windowUserInputMode == UserInputModeEnum::ANNOTATIONS); BrainOpenGLAnnotationDrawingFixedPipeline::Inputs inputs(this->m_brain, this->mode, BrainOpenGLFixedPipeline::s_gluLookAtCenterFromEyeOffsetDistance, m_windowIndex, this->windowTabIndex, SpacerTabIndex(), - BrainOpenGLAnnotationDrawingFixedPipeline::Inputs::WINDOW_DRAWING_NO); + BrainOpenGLAnnotationDrawingFixedPipeline::Inputs::WINDOW_DRAWING_NO, + annotationModeFlag); m_annotationDrawing->drawAnnotations(&inputs, AnnotationCoordinateSpaceEnum::TAB, m_annotationColorBarsForDrawing, @@ -1044,13 +1066,15 @@ BrainOpenGLFixedPipeline::drawWindowAnnotations(const int windowViewport[4]) */ this->windowTabIndex = -1; + const bool annotationModeFlag = (m_windowUserInputMode == UserInputModeEnum::ANNOTATIONS); BrainOpenGLAnnotationDrawingFixedPipeline::Inputs inputs(this->m_brain, this->mode, BrainOpenGLFixedPipeline::s_gluLookAtCenterFromEyeOffsetDistance, m_windowIndex, this->windowTabIndex, SpacerTabIndex(), - windowDrawingMode); + windowDrawingMode, + annotationModeFlag); m_annotationDrawing->drawAnnotations(&inputs, AnnotationCoordinateSpaceEnum::WINDOW, @@ -2018,13 +2042,15 @@ BrainOpenGLFixedPipeline::drawSurface(Surface* surface, * Draw annotations for this surface and maybe draw * the model annotations. */ + const bool annotationModeFlag = (m_windowUserInputMode == UserInputModeEnum::ANNOTATIONS); BrainOpenGLAnnotationDrawingFixedPipeline::Inputs inputs(this->m_brain, this->mode, BrainOpenGLFixedPipeline::s_gluLookAtCenterFromEyeOffsetDistance, m_windowIndex, this->windowTabIndex, SpacerTabIndex(), - BrainOpenGLAnnotationDrawingFixedPipeline::Inputs::WINDOW_DRAWING_NO); + BrainOpenGLAnnotationDrawingFixedPipeline::Inputs::WINDOW_DRAWING_NO, + annotationModeFlag); std::vector emptyColorBars; std::vector emptyViewportAnnotations; @@ -2071,13 +2097,15 @@ BrainOpenGLFixedPipeline::drawSurface(Surface* surface, * Draw annotations for this surface and maybe draw * the model annotations. */ + const bool annotationModeFlag = (m_windowUserInputMode == UserInputModeEnum::ANNOTATIONS); BrainOpenGLAnnotationDrawingFixedPipeline::Inputs inputs(this->m_brain, this->mode, BrainOpenGLFixedPipeline::s_gluLookAtCenterFromEyeOffsetDistance, m_windowIndex, this->windowTabIndex, SpacerTabIndex(), - BrainOpenGLAnnotationDrawingFixedPipeline::Inputs::WINDOW_DRAWING_NO); + BrainOpenGLAnnotationDrawingFixedPipeline::Inputs::WINDOW_DRAWING_NO, + annotationModeFlag); std::vector emptyColorBars; std::vector emptyViewportAnnotations; m_annotationDrawing->drawAnnotations(&inputs, diff --git a/src/Brain/BrainOpenGLFixedPipeline.h b/src/Brain/BrainOpenGLFixedPipeline.h index e3eb91fd9..d5639f4a1 100644 --- a/src/Brain/BrainOpenGLFixedPipeline.h +++ b/src/Brain/BrainOpenGLFixedPipeline.h @@ -115,10 +115,12 @@ namespace caret { protected: void drawModelsImplementation(const int32_t windowIndex, + const UserInputModeEnum::Enum windowUserInputMode, Brain* brain, const std::vector& viewportContents) override; void selectModelImplementation(const int32_t windowIndex, + const UserInputModeEnum::Enum windowUserInputMode, Brain* brain, const BrainOpenGLViewportContent* viewportContent, const int32_t mouseX, @@ -126,6 +128,7 @@ namespace caret { const bool applySelectionBackgroundFiltering) override; void projectToModelImplementation(const int32_t windowIndex, + const UserInputModeEnum::Enum windowUserInputMode, Brain* brain, const BrainOpenGLViewportContent* viewportContent, const int32_t mouseX, @@ -515,6 +518,9 @@ namespace caret { /** Index of window */ int32_t m_windowIndex = -1; + /** User input mode for window */ + UserInputModeEnum::Enum m_windowUserInputMode; + /** Indicates OpenGL has been initialized */ bool initializedOpenGLFlag; diff --git a/src/Brain/BrainOpenGLVolumeObliqueSliceDrawing.cxx b/src/Brain/BrainOpenGLVolumeObliqueSliceDrawing.cxx index 8be409e12..ec0ce2b6e 100644 --- a/src/Brain/BrainOpenGLVolumeObliqueSliceDrawing.cxx +++ b/src/Brain/BrainOpenGLVolumeObliqueSliceDrawing.cxx @@ -846,13 +846,15 @@ BrainOpenGLVolumeObliqueSliceDrawing::drawVolumeSliceViewProjection(const BrainO } } } + const bool annotationModeFlag = (m_fixedPipelineDrawing->m_windowUserInputMode == UserInputModeEnum::ANNOTATIONS); BrainOpenGLAnnotationDrawingFixedPipeline::Inputs inputs(this->m_brain, m_fixedPipelineDrawing->mode, BrainOpenGLFixedPipeline::s_gluLookAtCenterFromEyeOffsetDistance, m_fixedPipelineDrawing->m_windowIndex, m_fixedPipelineDrawing->windowTabIndex, SpacerTabIndex(), - BrainOpenGLAnnotationDrawingFixedPipeline::Inputs::WINDOW_DRAWING_NO); + BrainOpenGLAnnotationDrawingFixedPipeline::Inputs::WINDOW_DRAWING_NO, + annotationModeFlag); m_fixedPipelineDrawing->m_annotationDrawing->drawModelSpaceAnnotationsOnVolumeSlice(&inputs, slicePlane, sliceThickness); diff --git a/src/Brain/BrainOpenGLVolumeSliceDrawing.cxx b/src/Brain/BrainOpenGLVolumeSliceDrawing.cxx index 27973e2ad..cb4f3210d 100644 --- a/src/Brain/BrainOpenGLVolumeSliceDrawing.cxx +++ b/src/Brain/BrainOpenGLVolumeSliceDrawing.cxx @@ -888,13 +888,15 @@ BrainOpenGLVolumeSliceDrawing::drawVolumeSliceViewProjection(const AllSliceViewM } } } + const bool annotationModeFlag = (m_fixedPipelineDrawing->m_windowUserInputMode == UserInputModeEnum::ANNOTATIONS); BrainOpenGLAnnotationDrawingFixedPipeline::Inputs inputs(this->m_brain, m_fixedPipelineDrawing->mode, BrainOpenGLFixedPipeline::s_gluLookAtCenterFromEyeOffsetDistance, m_fixedPipelineDrawing->m_windowIndex, m_fixedPipelineDrawing->windowTabIndex, SpacerTabIndex(), - BrainOpenGLAnnotationDrawingFixedPipeline::Inputs::WINDOW_DRAWING_NO); + BrainOpenGLAnnotationDrawingFixedPipeline::Inputs::WINDOW_DRAWING_NO, + annotationModeFlag); m_fixedPipelineDrawing->m_annotationDrawing->drawModelSpaceAnnotationsOnVolumeSlice(&inputs, slicePlane, sliceThickness); diff --git a/src/Brain/CMakeLists.txt b/src/Brain/CMakeLists.txt index f4a80cf1f..abad43509 100644 --- a/src/Brain/CMakeLists.txt +++ b/src/Brain/CMakeLists.txt @@ -173,6 +173,7 @@ SurfaceMontageViewport.h SurfaceNodeColoring.h SurfaceSelectionModel.h TabContentBase.h +UserInputModeEnum.h ViewingTransformations.h ViewingTransformationsCerebellum.h ViewingTransformationsVolume.h @@ -332,6 +333,7 @@ SurfaceMontageViewport.cxx SurfaceNodeColoring.cxx SurfaceSelectionModel.cxx TabContentBase.cxx +UserInputModeEnum.cxx ViewingTransformations.cxx ViewingTransformationsCerebellum.cxx ViewingTransformationsVolume.cxx diff --git a/src/Brain/UserInputModeEnum.cxx b/src/Brain/UserInputModeEnum.cxx new file mode 100644 index 000000000..9a9749fea --- /dev/null +++ b/src/Brain/UserInputModeEnum.cxx @@ -0,0 +1,393 @@ + +/*LICENSE_START*/ +/* + * Copyright (C) 2019 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + +#include +#define __USER_INPUT_MODE_ENUM_DECLARE__ +#include "UserInputModeEnum.h" +#undef __USER_INPUT_MODE_ENUM_DECLARE__ + +#include "CaretAssert.h" + +using namespace caret; + + +/** + * \class caret::UserInputModeEnum + * \brief User Input Modes for Browser Window + * + * Using this enumerated type in the GUI with an EnumComboBoxTemplate + * + * Header File (.h) + * Forward declare the data type: + * class EnumComboBoxTemplate; + * + * Declare the member: + * EnumComboBoxTemplate* m_userInputModeEnumComboBox; + * + * Declare a slot that is called when user changes selection + * private slots: + * void userInputModeEnumComboBoxItemActivated(); + * + * Implementation File (.cxx) + * Include the header files + * #include "EnumComboBoxTemplate.h" + * #include "UserInputModeEnum.h" + * + * Instatiate: + * m_userInputModeEnumComboBox = new EnumComboBoxTemplate(this); + * m_userInputModeEnumComboBox->setup(); + * + * Get notified when the user changes the selection: + * QObject::connect(m_userInputModeEnumComboBox, SIGNAL(itemActivated()), + * this, SLOT(userInputModeEnumComboBoxItemActivated())); + * + * Update the selection: + * m_userInputModeEnumComboBox->setSelectedItem(NEW_VALUE); + * + * Read the selection: + * const UserInputModeEnum::Enum VARIABLE = m_userInputModeEnumComboBox->getSelectedItem(); + * + */ + +/** + * Constructor. + * + * @param enumValue + * An enumerated value. + * @param name + * Name of enumerated value. + * + * @param guiName + * User-friendly name for use in user-interface. + */ +UserInputModeEnum::UserInputModeEnum(const Enum enumValue, + const AString& name, + const AString& guiName) +{ + this->enumValue = enumValue; + this->integerCode = integerCodeCounter++; + this->name = name; + this->guiName = guiName; +} + +/** + * Destructor. + */ +UserInputModeEnum::~UserInputModeEnum() +{ +} + +/** + * Initialize the enumerated metadata. + */ +void +UserInputModeEnum::initialize() +{ + if (initializedFlag) { + return; + } + initializedFlag = true; + + enumData.push_back(UserInputModeEnum(INVALID, + "INVALID", + "Invalid")); + + enumData.push_back(UserInputModeEnum(ANNOTATIONS, + "ANNOTATIONS", + "Annotations")); + + enumData.push_back(UserInputModeEnum(BORDERS, + "BORDERS", + "Borders")); + + enumData.push_back(UserInputModeEnum(FOCI, + "FOCI", + "Foci")); + + enumData.push_back(UserInputModeEnum(IMAGE, + "IMAGE", + "Image")); + + enumData.push_back(UserInputModeEnum(VIEW, + "VIEW", + "View")); + + enumData.push_back(UserInputModeEnum(VOLUME_EDIT, + "VOLUME_EDIT", + "Volume Edit")); + +} + +/** + * Find the data for and enumerated value. + * @param enumValue + * The enumerated value. + * @return Pointer to data for this enumerated type + * or NULL if no data for type or if type is invalid. + */ +const UserInputModeEnum* +UserInputModeEnum::findData(const Enum enumValue) +{ + if (initializedFlag == false) initialize(); + + size_t num = enumData.size(); + for (size_t i = 0; i < num; i++) { + const UserInputModeEnum* d = &enumData[i]; + if (d->enumValue == enumValue) { + return d; + } + } + + return NULL; +} + +/** + * Get a string representation of the enumerated type. + * @param enumValue + * Enumerated value. + * @return + * String representing enumerated value. + */ +AString +UserInputModeEnum::toName(Enum enumValue) { + if (initializedFlag == false) initialize(); + + const UserInputModeEnum* enumInstance = findData(enumValue); + return enumInstance->name; +} + +/** + * Get an enumerated value corresponding to its name. + * @param name + * Name of enumerated value. + * @param isValidOut + * If not NULL, it is set indicating that a + * enum value exists for the input name. + * @return + * Enumerated value. + */ +UserInputModeEnum::Enum +UserInputModeEnum::fromName(const AString& name, bool* isValidOut) +{ + if (initializedFlag == false) initialize(); + + bool validFlag = false; + Enum enumValue = UserInputModeEnum::enumData[0].enumValue; + + for (std::vector::iterator iter = enumData.begin(); + iter != enumData.end(); + iter++) { + const UserInputModeEnum& d = *iter; + if (d.name == name) { + enumValue = d.enumValue; + validFlag = true; + break; + } + } + + if (isValidOut != 0) { + *isValidOut = validFlag; + } + else if (validFlag == false) { + CaretAssertMessage(0, AString("Name " + name + "failed to match enumerated value for type UserInputModeEnum")); + } + return enumValue; +} + +/** + * Get a GUI string representation of the enumerated type. + * @param enumValue + * Enumerated value. + * @return + * String representing enumerated value. + */ +AString +UserInputModeEnum::toGuiName(Enum enumValue) { + if (initializedFlag == false) initialize(); + + const UserInputModeEnum* enumInstance = findData(enumValue); + return enumInstance->guiName; +} + +/** + * Get an enumerated value corresponding to its GUI name. + * @param s + * Name of enumerated value. + * @param isValidOut + * If not NULL, it is set indicating that a + * enum value exists for the input name. + * @return + * Enumerated value. + */ +UserInputModeEnum::Enum +UserInputModeEnum::fromGuiName(const AString& guiName, bool* isValidOut) +{ + if (initializedFlag == false) initialize(); + + bool validFlag = false; + Enum enumValue = UserInputModeEnum::enumData[0].enumValue; + + for (std::vector::iterator iter = enumData.begin(); + iter != enumData.end(); + iter++) { + const UserInputModeEnum& d = *iter; + if (d.guiName == guiName) { + enumValue = d.enumValue; + validFlag = true; + break; + } + } + + if (isValidOut != 0) { + *isValidOut = validFlag; + } + else if (validFlag == false) { + CaretAssertMessage(0, AString("guiName " + guiName + "failed to match enumerated value for type UserInputModeEnum")); + } + return enumValue; +} + +/** + * Get the integer code for a data type. + * + * @return + * Integer code for data type. + */ +int32_t +UserInputModeEnum::toIntegerCode(Enum enumValue) +{ + if (initializedFlag == false) initialize(); + const UserInputModeEnum* enumInstance = findData(enumValue); + return enumInstance->integerCode; +} + +/** + * Find the data type corresponding to an integer code. + * + * @param integerCode + * Integer code for enum. + * @param isValidOut + * If not NULL, on exit isValidOut will indicate if + * integer code is valid. + * @return + * Enum for integer code. + */ +UserInputModeEnum::Enum +UserInputModeEnum::fromIntegerCode(const int32_t integerCode, bool* isValidOut) +{ + if (initializedFlag == false) initialize(); + + bool validFlag = false; + Enum enumValue = UserInputModeEnum::enumData[0].enumValue; + + for (std::vector::iterator iter = enumData.begin(); + iter != enumData.end(); + iter++) { + const UserInputModeEnum& enumInstance = *iter; + if (enumInstance.integerCode == integerCode) { + enumValue = enumInstance.enumValue; + validFlag = true; + break; + } + } + + if (isValidOut != 0) { + *isValidOut = validFlag; + } + else if (validFlag == false) { + CaretAssertMessage(0, AString("Integer code " + AString::number(integerCode) + "failed to match enumerated value for type UserInputModeEnum")); + } + return enumValue; +} + +/** + * Get all of the enumerated type values. The values can be used + * as parameters to toXXX() methods to get associated metadata. + * + * @param allEnums + * A vector that is OUTPUT containing all of the enumerated values. + */ +void +UserInputModeEnum::getAllEnums(std::vector& allEnums) +{ + if (initializedFlag == false) initialize(); + + allEnums.clear(); + + for (std::vector::iterator iter = enumData.begin(); + iter != enumData.end(); + iter++) { + allEnums.push_back(iter->enumValue); + } +} + +/** + * Get all of the names of the enumerated type values. + * + * @param allNames + * A vector that is OUTPUT containing all of the names of the enumerated values. + * @param isSorted + * If true, the names are sorted in alphabetical order. + */ +void +UserInputModeEnum::getAllNames(std::vector& allNames, const bool isSorted) +{ + if (initializedFlag == false) initialize(); + + allNames.clear(); + + for (std::vector::iterator iter = enumData.begin(); + iter != enumData.end(); + iter++) { + allNames.push_back(UserInputModeEnum::toName(iter->enumValue)); + } + + if (isSorted) { + std::sort(allNames.begin(), allNames.end()); + } +} + +/** + * Get all of the GUI names of the enumerated type values. + * + * @param allNames + * A vector that is OUTPUT containing all of the GUI names of the enumerated values. + * @param isSorted + * If true, the names are sorted in alphabetical order. + */ +void +UserInputModeEnum::getAllGuiNames(std::vector& allGuiNames, const bool isSorted) +{ + if (initializedFlag == false) initialize(); + + allGuiNames.clear(); + + for (std::vector::iterator iter = enumData.begin(); + iter != enumData.end(); + iter++) { + allGuiNames.push_back(UserInputModeEnum::toGuiName(iter->enumValue)); + } + + if (isSorted) { + std::sort(allGuiNames.begin(), allGuiNames.end()); + } +} + diff --git a/src/Brain/UserInputModeEnum.h b/src/Brain/UserInputModeEnum.h new file mode 100644 index 000000000..564e4c488 --- /dev/null +++ b/src/Brain/UserInputModeEnum.h @@ -0,0 +1,114 @@ +#ifndef __USER_INPUT_MODE_ENUM_H__ +#define __USER_INPUT_MODE_ENUM_H__ + +/*LICENSE_START*/ +/* + * Copyright (C) 2019 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + + +#include +#include +#include "AString.h" + +namespace caret { + +class UserInputModeEnum { + +public: + /** + * Enumerated values. + */ + enum Enum { + /** Invalid */ + INVALID, + /** Annotations */ + ANNOTATIONS, + /** Borders */ + BORDERS, + /** Foci */ + FOCI, + /** Image */ + IMAGE, + /** View */ + VIEW, + /** Volume Edit */ + VOLUME_EDIT + }; + + + ~UserInputModeEnum(); + + static AString toName(Enum enumValue); + + static Enum fromName(const AString& name, bool* isValidOut); + + static AString toGuiName(Enum enumValue); + + static Enum fromGuiName(const AString& guiName, bool* isValidOut); + + static int32_t toIntegerCode(Enum enumValue); + + static Enum fromIntegerCode(const int32_t integerCode, bool* isValidOut); + + static void getAllEnums(std::vector& allEnums); + + static void getAllNames(std::vector& allNames, const bool isSorted); + + static void getAllGuiNames(std::vector& allGuiNames, const bool isSorted); + +private: + UserInputModeEnum(const Enum enumValue, + const AString& name, + const AString& guiName); + + static const UserInputModeEnum* findData(const Enum enumValue); + + /** Holds all instance of enum values and associated metadata */ + static std::vector enumData; + + /** Initialize instances that contain the enum values and metadata */ + static void initialize(); + + /** Indicates instance of enum values and metadata have been initialized */ + static bool initializedFlag; + + /** Auto generated integer codes */ + static int32_t integerCodeCounter; + + /** The enumerated type value for an instance */ + Enum enumValue; + + /** The integer code associated with an enumerated value */ + int32_t integerCode; + + /** The name, a text string that is identical to the enumerated value */ + AString name; + + /** A user-friendly name that is displayed in the GUI */ + AString guiName; +}; + +#ifdef __USER_INPUT_MODE_ENUM_DECLARE__ +std::vector UserInputModeEnum::enumData; +bool UserInputModeEnum::initializedFlag = false; +int32_t UserInputModeEnum::integerCodeCounter = 0; +#endif // __USER_INPUT_MODE_ENUM_DECLARE__ + +} // namespace +#endif //__USER_INPUT_MODE_ENUM_H__ diff --git a/src/GuiQt/AnnotationSelectionViewController.cxx b/src/GuiQt/AnnotationSelectionViewController.cxx index 0bd1a7a20..3fc754f3b 100644 --- a/src/GuiQt/AnnotationSelectionViewController.cxx +++ b/src/GuiQt/AnnotationSelectionViewController.cxx @@ -229,15 +229,12 @@ AnnotationSelectionViewController::updateAnnotationSelections() const DisplayGroupEnum::Enum displayGroup = dpa->getDisplayGroupForTab(browserTabIndex); m_displayGroupComboBox->setSelectedDisplayGroup(displayGroup); - EventGetOrSetUserInputModeProcessor inputModeEvent(m_browserWindowIndex); - EventManager::get()->sendEvent(inputModeEvent.getPointer()); - UserInputModeAbstract::UserInputMode mode = inputModeEvent.getUserInputMode(); - const bool annotationsValidFlag = (mode == UserInputModeAbstract::ANNOTATIONS); + const bool allowAnnotationSelectionFlag(true); m_selectionViewController->updateContent(fileItems, displayGroup, browserTabIndex, - annotationsValidFlag); + allowAnnotationSelectionFlag); } QWidget* diff --git a/src/GuiQt/BrainBrowserWindowOrientedToolBox.cxx b/src/GuiQt/BrainBrowserWindowOrientedToolBox.cxx index 70acb9d02..e74290b2e 100644 --- a/src/GuiQt/BrainBrowserWindowOrientedToolBox.cxx +++ b/src/GuiQt/BrainBrowserWindowOrientedToolBox.cxx @@ -817,11 +817,11 @@ BrainBrowserWindowOrientedToolBox::receiveEvent(Event* event) EventGetOrSetUserInputModeProcessor inputModeEvent(m_browserWindowIndex); EventManager::get()->sendEvent(inputModeEvent.getPointer()); - const UserInputModeAbstract::UserInputMode inputMode = inputModeEvent.getUserInputMode(); + const UserInputModeEnum::Enum inputMode = inputModeEvent.getUserInputMode(); switch (inputMode) { - case UserInputModeAbstract::ANNOTATIONS: + case UserInputModeEnum::ANNOTATIONS: break; - case UserInputModeAbstract::BORDERS: + case UserInputModeEnum::BORDERS: /* * Enable borders tab if the input mode is 'borders' so that user * can edit border point size while drawing a border before any @@ -829,15 +829,15 @@ BrainBrowserWindowOrientedToolBox::receiveEvent(Event* event) */ haveBorders = true; break; - case UserInputModeAbstract::FOCI: + case UserInputModeEnum::FOCI: break; - case UserInputModeAbstract::IMAGE: + case UserInputModeEnum::IMAGE: break; - case UserInputModeAbstract::INVALID: + case UserInputModeEnum::INVALID: break; - case UserInputModeAbstract::VIEW: + case UserInputModeEnum::VIEW: break; - case UserInputModeAbstract::VOLUME_EDIT: + case UserInputModeEnum::VOLUME_EDIT: break; } diff --git a/src/GuiQt/BrainBrowserWindowToolBar.cxx b/src/GuiQt/BrainBrowserWindowToolBar.cxx index 3a1af7f40..b4333d6f6 100644 --- a/src/GuiQt/BrainBrowserWindowToolBar.cxx +++ b/src/GuiQt/BrainBrowserWindowToolBar.cxx @@ -3113,12 +3113,12 @@ BrainBrowserWindowToolBar::modeInputModeActionTriggered(QAction* action) EventGetOrSetUserInputModeProcessor getInputModeEvent(this->browserWindowIndex); EventManager::get()->sendEvent(getInputModeEvent.getPointer()); - const UserInputModeAbstract::UserInputMode currentMode = getInputModeEvent.getUserInputMode(); + const UserInputModeEnum::Enum currentMode = getInputModeEvent.getUserInputMode(); - UserInputModeAbstract::UserInputMode inputMode = UserInputModeAbstract::INVALID; + UserInputModeEnum::Enum inputMode = UserInputModeEnum::INVALID; if (action == this->modeInputModeAnnotationsAction) { - if (currentMode != UserInputModeAbstract::ANNOTATIONS) { + if (currentMode != UserInputModeEnum::ANNOTATIONS) { BrainBrowserWindow* bbw = GuiManager::get()->getBrowserWindowByWindowIndex(browserWindowIndex); CaretAssert(bbw); if ( ! bbw->changeInputModeToAnnotationsWarningDialog()) { @@ -3129,10 +3129,10 @@ BrainBrowserWindowToolBar::modeInputModeActionTriggered(QAction* action) return; } } - inputMode = UserInputModeAbstract::ANNOTATIONS; + inputMode = UserInputModeEnum::ANNOTATIONS; } else if (action == this->modeInputModeBordersAction) { - inputMode = UserInputModeAbstract::BORDERS; + inputMode = UserInputModeEnum::BORDERS; /* * If borders are not displayed, display them @@ -3150,17 +3150,17 @@ BrainBrowserWindowToolBar::modeInputModeActionTriggered(QAction* action) } } else if (action == this->modeInputModeFociAction) { - inputMode = UserInputModeAbstract::FOCI; + inputMode = UserInputModeEnum::FOCI; } else if ((action == this->modeInputModeImageAction) && (this->modeInputModeImageAction != NULL)) { - inputMode = UserInputModeAbstract::IMAGE; + inputMode = UserInputModeEnum::IMAGE; } else if (action == this->modeInputVolumeEditAction) { - inputMode = UserInputModeAbstract::VOLUME_EDIT; + inputMode = UserInputModeEnum::VOLUME_EDIT; } else if (action == this->modeInputModeViewAction) { - inputMode = UserInputModeAbstract::VIEW; + inputMode = UserInputModeEnum::VIEW; } else { CaretAssertMessage(0, "Tools input mode action is invalid, new action added???"); @@ -3193,27 +3193,27 @@ BrainBrowserWindowToolBar::updateModeWidget(BrowserTabContent* /*browserTabConte EventManager::get()->sendEvent(getInputModeEvent.getPointer()); switch (getInputModeEvent.getUserInputMode()) { - case UserInputModeAbstract::INVALID: + case UserInputModeEnum::INVALID: /* may get here when program is exiting and widgets are being destroyed */ break; - case UserInputModeAbstract::ANNOTATIONS: + case UserInputModeEnum::ANNOTATIONS: this->modeInputModeAnnotationsAction->setChecked(true); break; - case UserInputModeAbstract::BORDERS: + case UserInputModeEnum::BORDERS: this->modeInputModeBordersAction->setChecked(true); break; - case UserInputModeAbstract::FOCI: + case UserInputModeEnum::FOCI: this->modeInputModeFociAction->setChecked(true); break; - case UserInputModeAbstract::IMAGE: + case UserInputModeEnum::IMAGE: if (this->modeInputModeImageAction != NULL) { this->modeInputModeImageAction->setChecked(true); } break; - case UserInputModeAbstract::VOLUME_EDIT: + case UserInputModeEnum::VOLUME_EDIT: this->modeInputVolumeEditAction->setChecked(true); break; - case UserInputModeAbstract::VIEW: + case UserInputModeEnum::VIEW: this->modeInputModeViewAction->setChecked(true); break; } @@ -3279,7 +3279,7 @@ BrainBrowserWindowToolBar::updateDisplayedModeUserInputWidget() } } - if (userInputProcessor->getUserInputMode() != UserInputModeAbstract::ANNOTATIONS) { + if (userInputProcessor->getUserInputMode() != UserInputModeEnum::ANNOTATIONS) { /* * Delete all selected annotations and update graphics and UI. */ diff --git a/src/GuiQt/BrainOpenGLWidget.cxx b/src/GuiQt/BrainOpenGLWidget.cxx index 90dff892b..7042a31c7 100644 --- a/src/GuiQt/BrainOpenGLWidget.cxx +++ b/src/GuiQt/BrainOpenGLWidget.cxx @@ -36,6 +36,7 @@ #include #include +#include "AnnotationManager.h" #include "Border.h" #include "Brain.h" #include "BrainBrowserWindow.h" @@ -483,6 +484,7 @@ BrainOpenGLWidget::performOffScreenImageCapture(const int32_t imageWidth, windowContent); s_singletonOpenGL->drawModels(this->windowIndex, + this->selectedUserInputProcessor->getUserInputMode(), GuiManager::get()->getBrain(), m_contextShareGroupPointer, windowContent.getAllTabViewports()); @@ -663,6 +665,7 @@ BrainOpenGLWidget::paintGL() s_singletonOpenGL->setBorderBeingDrawn(NULL); } s_singletonOpenGL->drawModels(this->windowIndex, + this->selectedUserInputProcessor->getUserInputMode(), GuiManager::get()->getBrain(), m_contextShareGroupPointer, m_windowContent.getAllTabViewports()); @@ -1224,6 +1227,7 @@ BrainOpenGLWidget::performIdentification(const int x, if (idViewport != NULL) { s_singletonOpenGL->selectModel(this->windowIndex, + this->selectedUserInputProcessor->getUserInputMode(), GuiManager::get()->getBrain(), m_contextShareGroupPointer, idViewport, @@ -1288,6 +1292,7 @@ BrainOpenGLWidget::performIdentificationAnnotations(const int x, const int idY = y - vp[1]; */ s_singletonOpenGL->selectModel(this->windowIndex, + this->selectedUserInputProcessor->getUserInputMode(), GuiManager::get()->getBrain(), m_contextShareGroupPointer, idViewport, @@ -1354,6 +1359,7 @@ BrainOpenGLWidget::performIdentificationVoxelEditing(VolumeFile* editingVolumeFi const int idY = y - vp[1]; */ s_singletonOpenGL->selectModel(this->windowIndex, + this->selectedUserInputProcessor->getUserInputMode(), GuiManager::get()->getBrain(), m_contextShareGroupPointer, idViewport, @@ -1403,6 +1409,7 @@ BrainOpenGLWidget::performProjection(const int x, if (projectionViewport != NULL) { s_singletonOpenGL->projectToModel(this->windowIndex, + this->selectedUserInputProcessor->getUserInputMode(), GuiManager::get()->getBrain(), m_contextShareGroupPointer, projectionViewport, @@ -1646,29 +1653,35 @@ BrainOpenGLWidget::receiveEvent(Event* event) else if (inputModeEvent->isSetUserInputMode()) { UserInputModeAbstract* newUserInputProcessor = NULL; switch (inputModeEvent->getUserInputMode()) { - case UserInputModeAbstract::INVALID: + case UserInputModeEnum::INVALID: CaretAssertMessage(0, "INVALID is NOT allowed for user input mode"); break; - case UserInputModeAbstract::ANNOTATIONS: + case UserInputModeEnum::ANNOTATIONS: newUserInputProcessor = this->userInputAnnotationsModeProcessor; break; - case UserInputModeAbstract::BORDERS: + case UserInputModeEnum::BORDERS: newUserInputProcessor = this->userInputBordersModeProcessor; break; - case UserInputModeAbstract::FOCI: + case UserInputModeEnum::FOCI: newUserInputProcessor = this->userInputFociModeProcessor; break; - case UserInputModeAbstract::IMAGE: + case UserInputModeEnum::IMAGE: newUserInputProcessor = this->userInputImageModeProcessor; break; - case UserInputModeAbstract::VOLUME_EDIT: + case UserInputModeEnum::VOLUME_EDIT: newUserInputProcessor = this->userInputVolumeEditModeProcessor; break; - case UserInputModeAbstract::VIEW: + case UserInputModeEnum::VIEW: newUserInputProcessor = this->userInputViewModeProcessor; break; } + if ((newUserInputProcessor == this->userInputAnnotationsModeProcessor) + || (this->selectedUserInputProcessor == this->userInputAnnotationsModeProcessor)) { + AnnotationManager* annMan = GuiManager::get()->getBrain()->getAnnotationManager(); + CaretAssert(annMan); + annMan->deselectAllAnnotationsForEditing(this->windowIndex); + } if (newUserInputProcessor != NULL) { if (newUserInputProcessor != this->selectedUserInputProcessor) { this->selectedUserInputProcessor->finish(); diff --git a/src/GuiQt/EventGetOrSetUserInputModeProcessor.cxx b/src/GuiQt/EventGetOrSetUserInputModeProcessor.cxx index aa8a9edb6..3ca428ecc 100644 --- a/src/GuiQt/EventGetOrSetUserInputModeProcessor.cxx +++ b/src/GuiQt/EventGetOrSetUserInputModeProcessor.cxx @@ -36,7 +36,7 @@ using namespace caret; * The requested input mode. */ EventGetOrSetUserInputModeProcessor::EventGetOrSetUserInputModeProcessor(const int32_t windowIndex, - const UserInputModeAbstract::UserInputMode userInputMode) + const UserInputModeEnum::Enum userInputMode) : Event(EventTypeEnum::EVENT_GET_OR_SET_USER_INPUT_MODE) { this->userInputProcessor = NULL; @@ -55,7 +55,7 @@ EventGetOrSetUserInputModeProcessor::EventGetOrSetUserInputModeProcessor(const i : Event(EventTypeEnum::EVENT_GET_OR_SET_USER_INPUT_MODE) { this->userInputProcessor = NULL; - this->userInputMode = UserInputModeAbstract::INVALID; + this->userInputMode = UserInputModeEnum::INVALID; this->windowIndex = windowIndex; this->modeGetOrSet = GET; } @@ -79,7 +79,7 @@ EventGetOrSetUserInputModeProcessor::getWindowIndex() const /** * @return The requested input mode. */ -UserInputModeAbstract::UserInputMode +UserInputModeEnum::Enum EventGetOrSetUserInputModeProcessor::getUserInputMode() const { return this->userInputMode; diff --git a/src/GuiQt/EventGetOrSetUserInputModeProcessor.h b/src/GuiQt/EventGetOrSetUserInputModeProcessor.h index e2437105d..b12f7a197 100644 --- a/src/GuiQt/EventGetOrSetUserInputModeProcessor.h +++ b/src/GuiQt/EventGetOrSetUserInputModeProcessor.h @@ -32,7 +32,7 @@ namespace caret { public: EventGetOrSetUserInputModeProcessor(const int32_t windowIndex, - const UserInputModeAbstract::UserInputMode userInputMode); + const UserInputModeEnum::Enum userInputMode); EventGetOrSetUserInputModeProcessor(const int32_t windowIndex); @@ -44,7 +44,7 @@ namespace caret { int32_t getWindowIndex() const; - UserInputModeAbstract::UserInputMode getUserInputMode() const; + UserInputModeEnum::Enum getUserInputMode() const; void setUserInputProcessor(UserInputModeAbstract* userInputProcessor); @@ -64,7 +64,7 @@ namespace caret { UserInputModeAbstract* userInputProcessor; /** Requested input mode for SETTING and set when GETTING*/ - UserInputModeAbstract::UserInputMode userInputMode; + UserInputModeEnum::Enum userInputMode; /** index of window for update */ int32_t windowIndex; diff --git a/src/GuiQt/UserInputModeAbstract.cxx b/src/GuiQt/UserInputModeAbstract.cxx index 0d8c48059..453b20674 100644 --- a/src/GuiQt/UserInputModeAbstract.cxx +++ b/src/GuiQt/UserInputModeAbstract.cxx @@ -48,7 +48,7 @@ using namespace caret; /** * Constructor. */ -UserInputModeAbstract::UserInputModeAbstract(const UserInputMode inputMode) +UserInputModeAbstract::UserInputModeAbstract(const UserInputModeEnum::Enum inputMode) : CaretObject(), m_userInputMode(inputMode), m_widgetForToolBar(NULL), @@ -87,7 +87,7 @@ UserInputModeAbstract::~UserInputModeAbstract() /** * @return The input mode enumerated type. */ -UserInputModeAbstract::UserInputMode +UserInputModeEnum::Enum UserInputModeAbstract::getUserInputMode() const { return m_userInputMode; diff --git a/src/GuiQt/UserInputModeAbstract.h b/src/GuiQt/UserInputModeAbstract.h index 41796be61..7ff4cc8be 100644 --- a/src/GuiQt/UserInputModeAbstract.h +++ b/src/GuiQt/UserInputModeAbstract.h @@ -25,6 +25,7 @@ #include "CaretObject.h" #include "CaretPointer.h" #include "CursorEnum.h" +#include "UserInputModeEnum.h" class QPoint; class QWidget; @@ -39,25 +40,8 @@ namespace caret { class UserInputModeAbstract : public CaretObject { public: - /** Enumerated type for input modes */ - enum UserInputMode { - /** Invalid */ - INVALID, - /** Annotation Operations */ - ANNOTATIONS, - /** Border Operations */ - BORDERS, - /** Foci Operations */ - FOCI, - /** Image Operations */ - IMAGE, - /** Viewing Operations */ - VIEW, - /** Volume Edit Operations */ - VOLUME_EDIT - }; - - UserInputModeAbstract(const UserInputMode inputMode); + + UserInputModeAbstract(const UserInputModeEnum::Enum inputMode); virtual ~UserInputModeAbstract(); @@ -65,7 +49,7 @@ namespace caret { /** * @return The input mode enumerated type. */ - UserInputMode getUserInputMode() const; + UserInputModeEnum::Enum getUserInputMode() const; /** * Called when 'this' user input receiver is set @@ -242,7 +226,7 @@ namespace caret { UserInputModeAbstract& operator=(const UserInputModeAbstract&); - const UserInputMode m_userInputMode; + const UserInputModeEnum::Enum m_userInputMode; QWidget* m_widgetForToolBar; diff --git a/src/GuiQt/UserInputModeAnnotations.cxx b/src/GuiQt/UserInputModeAnnotations.cxx index 08c83d03c..91e649bdb 100644 --- a/src/GuiQt/UserInputModeAnnotations.cxx +++ b/src/GuiQt/UserInputModeAnnotations.cxx @@ -84,7 +84,7 @@ using namespace caret; * Constructor. */ UserInputModeAnnotations::UserInputModeAnnotations(const int32_t windowIndex) -: UserInputModeView(UserInputModeAbstract::ANNOTATIONS), +: UserInputModeView(UserInputModeEnum::ANNOTATIONS), m_browserWindowIndex(windowIndex), m_annotationUnderMouse(NULL), m_annotationBeingDragged(NULL) diff --git a/src/GuiQt/UserInputModeBorders.cxx b/src/GuiQt/UserInputModeBorders.cxx index 6679dd8db..041c82703 100644 --- a/src/GuiQt/UserInputModeBorders.cxx +++ b/src/GuiQt/UserInputModeBorders.cxx @@ -72,7 +72,7 @@ using namespace caret; */ UserInputModeBorders::UserInputModeBorders(Border* borderBeingDrawnByOpenGL, const int32_t windowIndex) -: UserInputModeView(UserInputModeAbstract::BORDERS) +: UserInputModeView(UserInputModeEnum::BORDERS) { this->borderBeingDrawnByOpenGL = borderBeingDrawnByOpenGL; this->windowIndex = windowIndex; diff --git a/src/GuiQt/UserInputModeFoci.cxx b/src/GuiQt/UserInputModeFoci.cxx index 3d6ffa0d5..7205b558a 100644 --- a/src/GuiQt/UserInputModeFoci.cxx +++ b/src/GuiQt/UserInputModeFoci.cxx @@ -61,7 +61,7 @@ using namespace caret; * Constructor. */ UserInputModeFoci::UserInputModeFoci(const int32_t windowIndex) -: UserInputModeView(UserInputModeAbstract::FOCI), +: UserInputModeView(UserInputModeEnum::FOCI), m_windowIndex(windowIndex) { m_inputModeFociWidget = new UserInputModeFociWidget(this, diff --git a/src/GuiQt/UserInputModeImage.cxx b/src/GuiQt/UserInputModeImage.cxx index 7b567a8ad..4404dd03e 100644 --- a/src/GuiQt/UserInputModeImage.cxx +++ b/src/GuiQt/UserInputModeImage.cxx @@ -63,7 +63,7 @@ using namespace caret; * Constructor. */ UserInputModeImage::UserInputModeImage(const int32_t windowIndex) -: UserInputModeView(UserInputModeAbstract::IMAGE), +: UserInputModeView(UserInputModeEnum::IMAGE), m_windowIndex(windowIndex) { m_inputModeImageWidget = new UserInputModeImageWidget(this, diff --git a/src/GuiQt/UserInputModeView.cxx b/src/GuiQt/UserInputModeView.cxx index efd0329cb..f139cae63 100644 --- a/src/GuiQt/UserInputModeView.cxx +++ b/src/GuiQt/UserInputModeView.cxx @@ -61,7 +61,7 @@ using namespace caret; * Constructor. */ UserInputModeView::UserInputModeView() -: UserInputModeAbstract(UserInputModeAbstract::VIEW) +: UserInputModeAbstract(UserInputModeEnum::VIEW) { } @@ -72,7 +72,7 @@ UserInputModeView::UserInputModeView() * @param inputMode * Subclass' input mode. */ -UserInputModeView::UserInputModeView(const UserInputMode inputMode) +UserInputModeView::UserInputModeView(const UserInputModeEnum::Enum inputMode) : UserInputModeAbstract(inputMode) { diff --git a/src/GuiQt/UserInputModeView.h b/src/GuiQt/UserInputModeView.h index e11923075..6f8e7b86b 100644 --- a/src/GuiQt/UserInputModeView.h +++ b/src/GuiQt/UserInputModeView.h @@ -63,7 +63,7 @@ namespace caret { BrainOpenGLWidget* openGLWidget); protected: - UserInputModeView(const UserInputMode inputMode); + UserInputModeView(const UserInputModeEnum::Enum inputMode); private: UserInputModeView(const UserInputModeView&); diff --git a/src/GuiQt/UserInputModeVolumeEdit.cxx b/src/GuiQt/UserInputModeVolumeEdit.cxx index 44bd838d0..c5900750c 100644 --- a/src/GuiQt/UserInputModeVolumeEdit.cxx +++ b/src/GuiQt/UserInputModeVolumeEdit.cxx @@ -62,7 +62,7 @@ using namespace caret; * Index of window using this volume editor input handler. */ UserInputModeVolumeEdit::UserInputModeVolumeEdit(const int32_t windowIndex) -: UserInputModeView(UserInputModeAbstract::VOLUME_EDIT), +: UserInputModeView(UserInputModeEnum::VOLUME_EDIT), m_windowIndex(windowIndex) { m_inputModeVolumeEditWidget = new UserInputModeVolumeEditWidget(this, diff --git a/src/GuiQt/WuQMacroExecutor.cxx b/src/GuiQt/WuQMacroExecutor.cxx index cfbf515c0..97acdd929 100644 --- a/src/GuiQt/WuQMacroExecutor.cxx +++ b/src/GuiQt/WuQMacroExecutor.cxx @@ -1115,7 +1115,7 @@ WuQMacroExecutor::runListWidgetCommand(const WuQMacroCommand* macroCommand, void WuQMacroExecutor::runMacroWidgetActionCommand(const WuQMacroCommand* macroCommand, QObject* object, - QString& errorMessageOut, + QString& /*errorMessageOut*/, bool& castFailureFlagOut) const { WuQMacroWidgetAction* macroWidgetAction = qobject_cast(object); -- GitLab From 05bc12a574263badbfe005314f8cda6a7661a6e5 Mon Sep 17 00:00:00 2001 From: John Harwell Date: Wed, 8 May 2019 15:14:32 -0500 Subject: [PATCH 308/427] Correct compilation error in OperationShowScene caused by a failure of my build system properly detecting MesaGL libraries (without mesa, some code is not compiled that contained a function with changed parameters). --- src/Operations/OperationShowScene.cxx | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/Operations/OperationShowScene.cxx b/src/Operations/OperationShowScene.cxx index bc29f0ee9..587402084 100644 --- a/src/Operations/OperationShowScene.cxx +++ b/src/Operations/OperationShowScene.cxx @@ -512,6 +512,7 @@ OperationShowScene::useParameters(OperationParameters* myParams, std::vector constViewports(viewports.begin(), viewports.end()); brainOpenGL->drawModels(windowIndex, + UserInputModeEnum::VIEW, brain, mesaContext, constViewports); @@ -561,6 +562,7 @@ OperationShowScene::useParameters(OperationParameters* myParams, viewportContents.push_back(content); brainOpenGL->drawModels(windowIndex, + UserInputModeEnum::VIEW, brain, mesaContext, viewportContents); -- GitLab From 9fb70377c63f3084a8b135689d15140f9efc1793 Mon Sep 17 00:00:00 2001 From: John Harwell Date: Thu, 9 May 2019 10:08:28 -0500 Subject: [PATCH 309/427] For wb_command -annotation-resample; Added '-surface-pair ' option. It replaces the '-source-surface' and '-target-surface' options that are still available but produce a depreciated warning. The old options will be removed in about a week. --- .../AlgorithmAnnotationResample.cxx | 36 ++++++++++++++++--- 1 file changed, 31 insertions(+), 5 deletions(-) diff --git a/src/Algorithms/AlgorithmAnnotationResample.cxx b/src/Algorithms/AlgorithmAnnotationResample.cxx index db92dde97..441b5c59f 100644 --- a/src/Algorithms/AlgorithmAnnotationResample.cxx +++ b/src/Algorithms/AlgorithmAnnotationResample.cxx @@ -74,17 +74,26 @@ AlgorithmAnnotationResample::getParameters() /* * We need to preserve 'annotation groups' that may be created by the user. * By modifying the annotation file that was read, any user created - * groups are preserved. + * groups are preserved. In addition, we are only modifying annotations + * in 'surface space'; all others are preserved without modification. + * So, we get a string for the output filename instead of an output annotation file */ ret->addStringParameter(paramIndex, "annotation-out", "name of resampled annotation file"); paramIndex++; + ParameterComponent* surfacePairOpt = ret->createRepeatableParameter(paramIndex, + "-surface-pair", + "pair of surfaces for resampling surface annotations for one structure"); + surfacePairOpt->addSurfaceParameter(1, "source-surface", "the midthickness surface of the current mesh the annotations use"); + surfacePairOpt->addSurfaceParameter(2, "target-surface", "the midthickness surface of the mesh the annotations should be transferred to"); + paramIndex++; + ParameterComponent* sourceSurfOpt = ret->createRepeatableParameter(paramIndex, "-source-surface", "surface with mesh used by input annotation file"); sourceSurfOpt->addSurfaceParameter(1, "source-surface", "source surface file"); paramIndex++; - + ParameterComponent* targetSurfOpt = ret->createRepeatableParameter(paramIndex, "-target-surface", "surface with mesh for output annotation file"); @@ -94,8 +103,11 @@ AlgorithmAnnotationResample::getParameters() AString helpText = ("Resample an annotation file from the source mesh to the target mesh.\n\n" "Only annotations in surface space are modified, no changes are made to " "annotations in other spaces. " - "Both -source-surface and -target-surface options must be repeated for all " - "structures used by surface space annotations."); + "The -surface-pair option may be repeated for additional " + "structures used by surface space annotations." + "\n\n" + "Note: -source-surface and -target-surface options are deprecated " + "and will be removed."); ret->setHelpText(helpText); @@ -124,17 +136,31 @@ AlgorithmAnnotationResample::useParameters(OperationParameters* myParams, paramIndex++; std::vector sourceSurfaces; + std::vector targetSurfaces; for (auto instance : *(myParams->getRepeatableParameterInstances(paramIndex))) { sourceSurfaces.push_back(instance->getSurface(1)); + targetSurfaces.push_back(instance->getSurface(2)); + } + paramIndex++; + + bool haveDeprectedOptionsFlag(false); + for (auto instance : *(myParams->getRepeatableParameterInstances(paramIndex))) { + sourceSurfaces.push_back(instance->getSurface(1)); + haveDeprectedOptionsFlag = true; } paramIndex++; - std::vector targetSurfaces; for (auto instance : *(myParams->getRepeatableParameterInstances(paramIndex))) { targetSurfaces.push_back(instance->getSurface(1)); + haveDeprectedOptionsFlag = true; } paramIndex++; + if (haveDeprectedOptionsFlag) { + std::cout << "WARNING: -source-surface and -target-surface options are deprecated." << std::endl; + std::cout << " Replace with -surface-pair " << std::endl; + } + /* * Constructs and executes the algorithm */ -- GitLab From 0e8f788c2f22d4c552db304d0b35657977d5f3fa Mon Sep 17 00:00:00 2001 From: Tim Coalson Date: Fri, 10 May 2019 15:29:56 -0500 Subject: [PATCH 310/427] add base 2 logarithm to math commands --- src/Common/CaretMathExpression.cxx | 5 +++++ src/Common/MathFunctionEnum.cxx | 2 +- src/Common/MathFunctionEnum.h | 1 + 3 files changed, 7 insertions(+), 1 deletion(-) diff --git a/src/Common/CaretMathExpression.cxx b/src/Common/CaretMathExpression.cxx index 52299f401..7e822f9c7 100644 --- a/src/Common/CaretMathExpression.cxx +++ b/src/Common/CaretMathExpression.cxx @@ -295,6 +295,10 @@ double CaretMathExpression::MathNode::eval(const vector& values) const CaretAssert(m_arguments.size() == 1); ret = log10(m_arguments[0]->eval(values)); break; + case MathFunctionEnum::LOG2: + CaretAssert(m_arguments.size() == 1); + ret = log2(m_arguments[0]->eval(values)); + break; case MathFunctionEnum::SQRT: CaretAssert(m_arguments.size() == 1); ret = sqrt(m_arguments[0]->eval(values)); @@ -835,6 +839,7 @@ CaretPointer CaretMathExpression::funcExpr() case MathFunctionEnum::LN: case MathFunctionEnum::EXP: case MathFunctionEnum::LOG: + case MathFunctionEnum::LOG2: case MathFunctionEnum::SQRT: case MathFunctionEnum::ABS: case MathFunctionEnum::FLOOR: diff --git a/src/Common/MathFunctionEnum.cxx b/src/Common/MathFunctionEnum.cxx index ca02a1bf1..5bd8d899b 100644 --- a/src/Common/MathFunctionEnum.cxx +++ b/src/Common/MathFunctionEnum.cxx @@ -67,7 +67,6 @@ MathFunctionEnum::initialize() } initializedFlag = true; - //enumData.push_back(MathFunctionEnum(INVALID, "INVALID"));//should this be in the data? I don't think it should, it is a placeholder for "no matching enum value" enumData.push_back(MathFunctionEnum(SIN, "sin", "1 argument, the sine of the argument (units are radians)")); enumData.push_back(MathFunctionEnum(COS, "cos", "1 argument, the cosine of the argument (units are radians)")); enumData.push_back(MathFunctionEnum(TAN, "tan", "1 argument, the tangent of the argument (units are radians)")); @@ -84,6 +83,7 @@ MathFunctionEnum::initialize() enumData.push_back(MathFunctionEnum(LN, "ln", "1 argument, the natural logarithm of the argument")); enumData.push_back(MathFunctionEnum(EXP, "exp", "1 argument, the constant e raised to the power of the argument")); enumData.push_back(MathFunctionEnum(LOG, "log", "1 argument, the base 10 logarithm of the argument")); + enumData.push_back(MathFunctionEnum(LOG2, "log2", "1 argument, the base 2 logarithm of the argument")); enumData.push_back(MathFunctionEnum(SQRT, "sqrt", "1 argument, the square root of the argument")); enumData.push_back(MathFunctionEnum(ABS, "abs", "1 argument, the absolute value of the argument")); enumData.push_back(MathFunctionEnum(FLOOR, "floor", "1 argument, the largest integer not greater than the argument")); diff --git a/src/Common/MathFunctionEnum.h b/src/Common/MathFunctionEnum.h index f2070aa4a..fe744fd85 100644 --- a/src/Common/MathFunctionEnum.h +++ b/src/Common/MathFunctionEnum.h @@ -57,6 +57,7 @@ public: LN, EXP, LOG, + LOG2, SQRT, ABS, FLOOR, -- GitLab From 7fa4744423d720c59eb3d69296b54f270f81ee0a Mon Sep 17 00:00:00 2001 From: John Harwell Date: Fri, 10 May 2019 16:01:43 -0500 Subject: [PATCH 311/427] Partial implementation of WB-851 Crosshair improvement: Adjustable gap is functioning for orthogonal mode and saved/restored from scenes and in preferences. Next is adding gap to crosshairs in oblique mode. --- src/Brain/Brain.cxx | 1 + src/Brain/BrainOpenGLVolumeSliceDrawing.cxx | 214 ++++++------------ src/Brain/BrainOpenGLVolumeSliceDrawing.h | 65 +++--- src/Brain/SessionManager.cxx | 72 +++++- src/Brain/SessionManager.h | 6 + src/Common/CMakeLists.txt | 2 + src/Common/CaretPreferenceDataValue.cxx | 192 ++++++++++++++++ src/Common/CaretPreferenceDataValue.h | 113 +++++++++ src/Common/CaretPreferences.cxx | 45 ++++ src/Common/CaretPreferences.h | 11 + src/Graphics/GraphicsUtilitiesOpenGL.cxx | 41 ++++ src/Graphics/GraphicsUtilitiesOpenGL.h | 2 + src/GuiQt/BrainBrowserWindow.cxx | 1 + .../BrainBrowserWindowToolBarSlicePlane.cxx | 92 +++++++- .../BrainBrowserWindowToolBarSlicePlane.h | 9 + 15 files changed, 687 insertions(+), 179 deletions(-) create mode 100644 src/Common/CaretPreferenceDataValue.cxx create mode 100644 src/Common/CaretPreferenceDataValue.h diff --git a/src/Brain/Brain.cxx b/src/Brain/Brain.cxx index e5a8e0c92..954453fce 100644 --- a/src/Brain/Brain.cxx +++ b/src/Brain/Brain.cxx @@ -5497,6 +5497,7 @@ Brain::loadFilesSelectedInSpecFile(EventSpecFileReadDataFiles* readSpecFileDataF resetBrain(); CaretPreferences* prefs = SessionManager::get()->getCaretPreferences(); + prefs->invalidSceneDataValues(); prefs->setBackgroundAndForegroundColorsMode(BackgroundAndForegroundColorsModeEnum::USER_PREFERENCES); const AString specFileName = sf->getFileName(); diff --git a/src/Brain/BrainOpenGLVolumeSliceDrawing.cxx b/src/Brain/BrainOpenGLVolumeSliceDrawing.cxx index cb4f3210d..3162d02e2 100644 --- a/src/Brain/BrainOpenGLVolumeSliceDrawing.cxx +++ b/src/Brain/BrainOpenGLVolumeSliceDrawing.cxx @@ -38,6 +38,7 @@ #include "CaretAssert.h" #include "CaretLogger.h" #include "CaretOpenGLInclude.h" +#include "CaretPreferenceDataValue.h" #include "CaretPreferences.h" #include "CiftiMappableDataFile.h" #include "DeveloperFlagsEnum.h" @@ -485,13 +486,6 @@ BrainOpenGLVolumeSliceDrawing::drawVolumeSliceViewTypeMontage(const AllSliceView const int32_t montageCoordPrecision = m_browserTabContent->getVolumeMontageCoordinatePrecision(); const GapsAndMargins* gapsAndMargins = m_brain->getGapsAndMargins(); -// const int32_t horizontalMargin = static_cast(viewport[2] * gapsAndMargins->getVolumeMontageHorizontalGap()); -// const int32_t verticalMargin = static_cast(viewport[3] * gapsAndMargins->getVolumeMontageVerticalGap()); -// -// const int32_t totalGapX = horizontalMargin * (numCols - 1); -// const int32_t vpSizeX = (viewport[2] - totalGapX) / numCols; -// const int32_t totalGapY = verticalMargin * (numRows - 1); -// const int32_t vpSizeY = (viewport[3] - totalGapY) / numRows; const int32_t windowIndex = m_fixedPipelineDrawing->m_windowIndex; @@ -668,11 +662,10 @@ BrainOpenGLVolumeSliceDrawing::drawVolumeSliceViewTypeMontage(const AllSliceView if (m_browserTabContent->isVolumeAxesCrosshairLabelsDisplayed()) { - drawAxesCrosshairsOrthoAndOblique(sliceProjectionType, - sliceViewPlane, - sliceCoordinates, - false, - true); + drawAxesCrosshairsOrtho(sliceViewPlane, + sliceCoordinates, + false, + true); } } @@ -1651,7 +1644,6 @@ BrainOpenGLVolumeSliceDrawing::drawOrthogonalSlice(const VolumeSliceViewPlaneEnu int64_t sliceVoxelIndices[3] = { 0, 0, 0 }; float sliceVoxelXYZ[3] = { 0.0, 0.0, 0.0 }; sliceVoxelXYZ[sliceViewingPlaneIndexIntoXYZ] = selectedSliceCoordinate; - //volumeFile->indexToSpace(sliceVoxelIndices, sliceVoxelXYZ); volumeFile->enclosingVoxel(sliceVoxelXYZ[0], sliceVoxelXYZ[1], sliceVoxelXYZ[2], sliceVoxelIndices[0], sliceVoxelIndices[1], sliceVoxelIndices[2]); @@ -3412,11 +3404,10 @@ BrainOpenGLVolumeSliceDrawing::drawAxesCrosshairs(const VolumeSliceProjectionTyp } switch (sliceProjectionType) { case VolumeSliceProjectionTypeEnum::VOLUME_SLICE_PROJECTION_ORTHOGONAL: - drawAxesCrosshairsOrthoAndOblique(sliceProjectionType, - sliceViewPlane, - sliceCoordinates, - drawCrosshairsFlag, - drawCrosshairLabelsFlag); + drawAxesCrosshairsOrtho(sliceViewPlane, + sliceCoordinates, + drawCrosshairsFlag, + drawCrosshairLabelsFlag); break; case VolumeSliceProjectionTypeEnum::VOLUME_SLICE_PROJECTION_OBLIQUE: { @@ -3431,11 +3422,10 @@ BrainOpenGLVolumeSliceDrawing::drawAxesCrosshairs(const VolumeSliceProjectionTyp float trans[3]; m_browserTabContent->getTranslation(trans); glTranslatef(trans[0], trans[1], trans[2]); - drawAxesCrosshairsOrthoAndOblique(sliceProjectionType, - sliceViewPlane, - sliceCoordinates, - drawCrosshairsFlag, - drawCrosshairLabelsFlag); + drawAxesCrosshairsOrtho(sliceViewPlane, + sliceCoordinates, + drawCrosshairsFlag, + drawCrosshairLabelsFlag); glPopMatrix(); } break; @@ -3445,11 +3435,9 @@ BrainOpenGLVolumeSliceDrawing::drawAxesCrosshairs(const VolumeSliceProjectionTyp /** * Draw the axes crosshairs for an orthogonal slice. * - * @param sliceProjectionType - * Type of projection for the slice drawing (oblique, orthogonal) * @param sliceViewPlane * The slice plane view. - * @param sliceCoordinates + * @param sliceCoordinatesIn * Coordinates of the selected slices. * @param drawCrosshairsFlag * If true, draw the crosshairs. @@ -3457,89 +3445,35 @@ BrainOpenGLVolumeSliceDrawing::drawAxesCrosshairs(const VolumeSliceProjectionTyp * If true, draw the crosshair labels. */ void -BrainOpenGLVolumeSliceDrawing::drawAxesCrosshairsOrthoAndOblique(const VolumeSliceProjectionTypeEnum::Enum sliceProjectionType, - const VolumeSliceViewPlaneEnum::Enum sliceViewPlane, - const float sliceCoordinates[3], - const bool drawCrosshairsFlag, - const bool drawCrosshairLabelsFlag) +BrainOpenGLVolumeSliceDrawing::drawAxesCrosshairsOrtho(const VolumeSliceViewPlaneEnum::Enum sliceViewPlane, + const float sliceCoordinatesIn[3], + const bool drawCrosshairsFlag, + const bool drawCrosshairLabelsFlag) { - bool obliqueModeFlag = false; - switch (sliceProjectionType) { - case VolumeSliceProjectionTypeEnum::VOLUME_SLICE_PROJECTION_OBLIQUE: - obliqueModeFlag = true; - break; - case VolumeSliceProjectionTypeEnum::VOLUME_SLICE_PROJECTION_ORTHOGONAL: - break; - } + CaretPreferenceDataValue* gapPref = SessionManager::get()->getCaretPreferences()->getVolumeCrossHairGapPreference(); + CaretAssert(gapPref); + const float gapPercentViewportHeight = gapPref->getValue().toDouble(); + const float gapMM = GraphicsUtilitiesOpenGL::convertPercentageOfViewportHeightToMillimeters(gapPercentViewportHeight); + std::array sliceCoordinates { sliceCoordinatesIn[0], sliceCoordinatesIn[1], sliceCoordinatesIn[2] }; + GLboolean depthEnabled = GL_FALSE; glGetBooleanv(GL_DEPTH_TEST, &depthEnabled); glDisable(GL_DEPTH_TEST); - const float bigValue = 10000.0; - - float horizontalAxisStartXYZ[3] = { - sliceCoordinates[0], - sliceCoordinates[1], - sliceCoordinates[2] - }; - float horizontalAxisEndXYZ[3] = { - sliceCoordinates[0], - sliceCoordinates[1], - sliceCoordinates[2] - }; + const float bigValue = 10000.0 + gapMM; - float verticalAxisStartXYZ[3] = { - sliceCoordinates[0], - sliceCoordinates[1], - sliceCoordinates[2] - }; - float verticalAxisEndXYZ[3] = { - sliceCoordinates[0], - sliceCoordinates[1], - sliceCoordinates[2] - }; - - if (obliqueModeFlag) { - switch (sliceViewPlane) { - case VolumeSliceViewPlaneEnum::ALL: - break; - case VolumeSliceViewPlaneEnum::AXIAL: - break; - case VolumeSliceViewPlaneEnum::CORONAL: - horizontalAxisStartXYZ[0] = sliceCoordinates[0]; - horizontalAxisStartXYZ[1] = sliceCoordinates[2]; - horizontalAxisStartXYZ[2] = sliceCoordinates[1]; - horizontalAxisEndXYZ[0] = sliceCoordinates[0]; - horizontalAxisEndXYZ[1] = sliceCoordinates[2]; - horizontalAxisEndXYZ[2] = sliceCoordinates[1]; - - verticalAxisStartXYZ[0] = sliceCoordinates[0]; - verticalAxisStartXYZ[1] = sliceCoordinates[1]; - verticalAxisStartXYZ[2] = sliceCoordinates[2]; - verticalAxisEndXYZ[0] = sliceCoordinates[0]; - verticalAxisEndXYZ[1] = sliceCoordinates[1]; - verticalAxisEndXYZ[2] = sliceCoordinates[2]; - break; - case VolumeSliceViewPlaneEnum::PARASAGITTAL: - horizontalAxisStartXYZ[0] = sliceCoordinates[1]; - horizontalAxisStartXYZ[1] = sliceCoordinates[2]; - horizontalAxisStartXYZ[2] = sliceCoordinates[0]; - horizontalAxisEndXYZ[0] = sliceCoordinates[1]; - horizontalAxisEndXYZ[1] = sliceCoordinates[2]; - horizontalAxisEndXYZ[2] = sliceCoordinates[0]; - - verticalAxisStartXYZ[0] = -sliceCoordinates[1]; - verticalAxisStartXYZ[1] = sliceCoordinates[0]; - verticalAxisStartXYZ[2] = sliceCoordinates[2]; - verticalAxisEndXYZ[0] = -sliceCoordinates[1]; - verticalAxisEndXYZ[1] = sliceCoordinates[0]; - verticalAxisEndXYZ[2] = sliceCoordinates[2]; - break; - } - } + std::array horizontalAxisPosStartXYZ { sliceCoordinates }; + std::array horizontalAxisPosEndXYZ { sliceCoordinates }; + std::array verticalAxisPosStartXYZ { sliceCoordinates }; + std::array verticalAxisPosEndXYZ { sliceCoordinates }; + std::array horizontalAxisNegStartXYZ { horizontalAxisPosStartXYZ }; + std::array horizontalAxisNegEndXYZ { horizontalAxisPosEndXYZ }; + std::array verticalAxisNegStartXYZ { verticalAxisPosStartXYZ }; + std::array verticalAxisNegEndXYZ { verticalAxisPosEndXYZ }; + float axialRGBA[4]; getAxesColor(VolumeSliceViewPlaneEnum::AXIAL, axialRGBA); @@ -3567,64 +3501,52 @@ BrainOpenGLVolumeSliceDrawing::drawAxesCrosshairsOrthoAndOblique(const VolumeSli horizontalLeftText = "L"; horizontalRightText = "R"; horizontalAxisRGBA = coronalRGBA; - horizontalAxisStartXYZ[0] -= bigValue; - horizontalAxisEndXYZ[0] += bigValue; + horizontalAxisPosStartXYZ[0] += gapMM; + horizontalAxisPosEndXYZ[0] += bigValue; + horizontalAxisNegStartXYZ[0] -= gapMM; + horizontalAxisNegEndXYZ[0] -= bigValue; verticalBottomText = "P"; verticalTopText = "A"; verticalAxisRGBA = paraRGBA; - verticalAxisStartXYZ[1] -= bigValue; - verticalAxisEndXYZ[1] += bigValue; + verticalAxisPosStartXYZ[1] += gapMM; + verticalAxisPosEndXYZ[1] += bigValue; + verticalAxisNegStartXYZ[1] -= gapMM; + verticalAxisNegEndXYZ[1] -= bigValue; break; case VolumeSliceViewPlaneEnum::CORONAL: horizontalLeftText = "L"; horizontalRightText = "R"; horizontalAxisRGBA = axialRGBA; - if (obliqueModeFlag) { - horizontalAxisStartXYZ[0] -= bigValue; - horizontalAxisEndXYZ[0] += bigValue; - } - else { - horizontalAxisStartXYZ[0] -= bigValue; - horizontalAxisEndXYZ[0] += bigValue; - } - + horizontalAxisPosStartXYZ[0] += gapMM; + horizontalAxisPosEndXYZ[0] += bigValue; + horizontalAxisNegStartXYZ[0] -= gapMM; + horizontalAxisNegEndXYZ[0] -= bigValue; + verticalBottomText = "I"; verticalTopText = "S"; verticalAxisRGBA = paraRGBA; - if (obliqueModeFlag) { - verticalAxisStartXYZ[1] -= bigValue; - verticalAxisEndXYZ[1] += bigValue; - } - else { - verticalAxisStartXYZ[2] -= bigValue; - verticalAxisEndXYZ[2] += bigValue; - } + verticalAxisPosStartXYZ[2] += gapMM; + verticalAxisPosEndXYZ[2] += bigValue; + verticalAxisNegStartXYZ[2] -= gapMM; + verticalAxisNegEndXYZ[2] -= bigValue; break; case VolumeSliceViewPlaneEnum::PARASAGITTAL: horizontalLeftText = "A"; horizontalRightText = "P"; horizontalAxisRGBA = axialRGBA; - if (obliqueModeFlag) { - horizontalAxisStartXYZ[0] -= bigValue; - horizontalAxisEndXYZ[0] += bigValue; - } - else { - horizontalAxisStartXYZ[1] -= bigValue; - horizontalAxisEndXYZ[1] += bigValue; - } + horizontalAxisPosStartXYZ[1] += gapMM; + horizontalAxisPosEndXYZ[1] += bigValue; + horizontalAxisNegStartXYZ[1] -= gapMM; + horizontalAxisNegEndXYZ[1] -= bigValue; verticalBottomText = "I"; verticalTopText = "S"; verticalAxisRGBA = coronalRGBA; - if (obliqueModeFlag) { - verticalAxisStartXYZ[1] -= bigValue; - verticalAxisEndXYZ[1] += bigValue; - } - else { - verticalAxisStartXYZ[2] -= bigValue; - verticalAxisEndXYZ[2] += bigValue; - } + verticalAxisPosStartXYZ[2] += gapMM; + verticalAxisPosEndXYZ[2] += bigValue; + verticalAxisNegStartXYZ[2] -= gapMM; + verticalAxisNegEndXYZ[2] -= bigValue; break; } @@ -3658,11 +3580,17 @@ BrainOpenGLVolumeSliceDrawing::drawAxesCrosshairsOrthoAndOblique(const VolumeSli */ if (drawCrosshairsFlag) { std::unique_ptr xhairPrimitive(GraphicsPrimitive::newPrimitiveV3fC4f(GraphicsPrimitive::PrimitiveType::POLYGONAL_LINES)); - xhairPrimitive->addVertex(horizontalAxisStartXYZ, horizontalAxisRGBA); - xhairPrimitive->addVertex(horizontalAxisEndXYZ, horizontalAxisRGBA); - xhairPrimitive->addVertex(verticalAxisStartXYZ, verticalAxisRGBA); - xhairPrimitive->addVertex(verticalAxisEndXYZ, verticalAxisRGBA); + xhairPrimitive->addVertex(&horizontalAxisPosStartXYZ[0], horizontalAxisRGBA); + xhairPrimitive->addVertex(&horizontalAxisPosEndXYZ[0], horizontalAxisRGBA); + xhairPrimitive->addVertex(&horizontalAxisNegStartXYZ[0], horizontalAxisRGBA); + xhairPrimitive->addVertex(&horizontalAxisNegEndXYZ[0], horizontalAxisRGBA); + + xhairPrimitive->addVertex(&verticalAxisPosStartXYZ[0], verticalAxisRGBA); + xhairPrimitive->addVertex(&verticalAxisPosEndXYZ[0], verticalAxisRGBA); + xhairPrimitive->addVertex(&verticalAxisNegStartXYZ[0], verticalAxisRGBA); + xhairPrimitive->addVertex(&verticalAxisNegEndXYZ[0], verticalAxisRGBA); xhairPrimitive->setLineWidth(GraphicsPrimitive::LineWidthType::PIXELS, 2.0f); + GraphicsEngineDataOpenGL::draw(xhairPrimitive.get()); } @@ -3681,7 +3609,7 @@ BrainOpenGLVolumeSliceDrawing::drawAxesCrosshairsOrthoAndOblique(const VolumeSli annotationText.setTextColor(CaretColorEnum::CUSTOM); annotationText.setCustomTextColor(horizontalAxisRGBA); annotationText.setCustomBackgroundColor(backgroundRGBA); - + annotationText.setHorizontalAlignment(AnnotationTextAlignHorizontalEnum::LEFT); annotationText.setVerticalAlignment(AnnotationTextAlignVerticalEnum::MIDDLE); annotationText.setText(horizontalLeftText); @@ -3697,7 +3625,7 @@ BrainOpenGLVolumeSliceDrawing::drawAxesCrosshairsOrthoAndOblique(const VolumeSli annotationText); annotationText.setCustomTextColor(verticalAxisRGBA); - + annotationText.setHorizontalAlignment(AnnotationTextAlignHorizontalEnum::CENTER); annotationText.setVerticalAlignment(AnnotationTextAlignVerticalEnum::BOTTOM); annotationText.setText(verticalBottomText); diff --git a/src/Brain/BrainOpenGLVolumeSliceDrawing.h b/src/Brain/BrainOpenGLVolumeSliceDrawing.h index 0881142b4..73afc8844 100644 --- a/src/Brain/BrainOpenGLVolumeSliceDrawing.h +++ b/src/Brain/BrainOpenGLVolumeSliceDrawing.h @@ -46,6 +46,15 @@ namespace caret { class BrainOpenGLVolumeSliceDrawing : public CaretObject { public: + /** + * Indicates drawing volume sclice "ALL that shows + * axial, coronal, and parasagittal at same time + */ + enum class AllSliceViewMode { + ALL_YES, + ALL_NO + }; + BrainOpenGLVolumeSliceDrawing(); virtual ~BrainOpenGLVolumeSliceDrawing(); @@ -57,18 +66,26 @@ namespace caret { const VolumeSliceProjectionTypeEnum::Enum sliceProjectionType, const int32_t viewport[4]); + static void setOrthographicProjection(const AllSliceViewMode allSliceViewMode, + const VolumeSliceViewPlaneEnum::Enum sliceViewPlane, + const BoundingBox& boundingBox, + const float zoomFactor, + const int viewport[4], + double orthographicBoundsOut[6]); + + static void drawSurfaceOutline(const VolumeMappableInterface* underlayVolume, + const ModelTypeEnum::Enum modelType, + const VolumeSliceProjectionTypeEnum::Enum sliceProjectionType, + const VolumeSliceViewPlaneEnum::Enum sliceViewPlane, + const float sliceXYZ[3], + const Plane& plane, + VolumeSurfaceOutlineSetModel* outlineSet, + BrainOpenGLFixedPipeline* fixedPipelineDrawing, + const bool useNegativePolygonOffsetFlag); + // ADD_NEW_METHODS_HERE private: - /** - * Indicates drawing volume sclice "ALL that shows - * axial, coronal, and parasagittal at same time - */ - enum class AllSliceViewMode { - ALL_YES, - ALL_NO - }; - /** * Holds values in the slice for a volume so that they * can be colored all at once which is more efficient than @@ -232,12 +249,11 @@ namespace caret { const float sliceCoordinates[3], Plane& planeOut); - void drawAxesCrosshairsOrthoAndOblique(const VolumeSliceProjectionTypeEnum::Enum sliceProjectionType, - const VolumeSliceViewPlaneEnum::Enum sliceViewPlane, - const float sliceCoordinates[3], - const bool drawCrosshairsFlag, - const bool drawCrosshairLabelsFlag); - + void drawAxesCrosshairsOrtho(const VolumeSliceViewPlaneEnum::Enum sliceViewPlane, + const float sliceCoordinates[3], + const bool drawCrosshairsFlag, + const bool drawCrosshairLabelsFlag); + void setVolumeSliceViewingAndModelingTransformations(const VolumeSliceProjectionTypeEnum::Enum sliceProjectionType, const VolumeSliceViewPlaneEnum::Enum sliceViewPlane, const Plane& plane); @@ -251,16 +267,6 @@ namespace caret { const Plane& slicePlane, const float sliceCoordinates[3]); - static void drawSurfaceOutline(const VolumeMappableInterface* underlayVolume, - const ModelTypeEnum::Enum modelType, - const VolumeSliceProjectionTypeEnum::Enum sliceProjectionType, - const VolumeSliceViewPlaneEnum::Enum sliceViewPlane, - const float sliceXYZ[3], - const Plane& plane, - VolumeSurfaceOutlineSetModel* outlineSet, - BrainOpenGLFixedPipeline* fixedPipelineDrawing, - const bool useNegativePolygonOffsetFlag); - static void drawSurfaceOutlineCached(const VolumeMappableInterface* underlayVolume, const ModelTypeEnum::Enum modelType, const VolumeSliceProjectionTypeEnum::Enum sliceProjectionType, @@ -294,13 +300,6 @@ namespace caret { const VolumeSliceViewPlaneEnum::Enum sliceViewPlane, const int viewport[4]); - static void setOrthographicProjection(const AllSliceViewMode allSliceViewMode, - const VolumeSliceViewPlaneEnum::Enum sliceViewPlane, - const BoundingBox& boundingBox, - const float zoomFactor, - const int viewport[4], - double orthographicBoundsOut[6]); - void drawOrthogonalSliceVoxels(const float sliceNormalVector[3], const float coordinate[3], const float rowStep[3], @@ -400,8 +399,6 @@ namespace caret { static const int32_t IDENTIFICATION_INDICES_PER_VOXEL; - friend class BrainOpenGLVolumeObliqueSliceDrawing; - // ADD_NEW_MEMBERS_HERE }; diff --git a/src/Brain/SessionManager.cxx b/src/Brain/SessionManager.cxx index f94cdbbdc..7ab23744b 100644 --- a/src/Brain/SessionManager.cxx +++ b/src/Brain/SessionManager.cxx @@ -32,6 +32,7 @@ #include "BrowserWindowContent.h" #include "CaretAssert.h" #include "CaretLogger.h" +#include "CaretPreferenceDataValue.h" #include "CaretPreferences.h" #include "CiftiConnectivityMatrixDataFileManager.h" #include "CiftiFiberTrajectoryManager.h" @@ -675,6 +676,9 @@ SessionManager::saveToScene(const SceneAttributes* sceneAttributes, sceneClass->addChild(colorHelper.saveToScene(sceneAttributes, "backgroundAndForegroundColors")); + sceneClass->addClass(savePreferencesToScene(sceneAttributes, + "ScenePreferenceDataValues")); + return sceneClass; } @@ -692,12 +696,13 @@ SessionManager::saveToScene(const SceneAttributes* sceneAttributes, */ void SessionManager::restoreFromScene(const SceneAttributes* sceneAttributes, - const SceneClass* sceneClass) + const SceneClass* sceneClass) { /* * Default to user preferences for colors */ m_caretPreferences->setBackgroundAndForegroundColorsMode(BackgroundAndForegroundColorsModeEnum::USER_PREFERENCES); + m_caretPreferences->invalidSceneDataValues(); if (sceneClass == NULL) { return; @@ -992,6 +997,9 @@ SessionManager::restoreFromScene(const SceneAttributes* sceneAttributes, } } + restorePreferencesFromScene(sceneAttributes, + sceneClass->getClass("ScenePreferenceDataValues")); + m_imageCaptureDialogSettings->restoreFromScene(sceneAttributes, sceneClass->getClass("m_imageCaptureDialogSettings")); @@ -1009,6 +1017,68 @@ SessionManager::restoreFromScene(const SceneAttributes* sceneAttributes, } } +/** + * Save items in preferences to the scene. + * + * @param sceneAttributes + * Attributes for the scene. Scenes may be of different types + * (full, generic, etc) and the attributes should be checked when + * saving the scene. + * + * @param instanceName + * Name for the scene class + * + * @return Pointer to scene class containing preferences + */ +SceneClass* +SessionManager::savePreferencesToScene(const SceneAttributes* /*sceneAttributes*/, + const AString& instanceName) +{ + SceneClass* sceneClass = new SceneClass(instanceName, + "ScenePreferences", + 1); + std::vector sceneDataValues = m_caretPreferences->getPreferenceSceneDataValues(); + for (auto scv : sceneDataValues) { + sceneClass->addString(scv->getName(), + scv->getPreferenceValue().toString()); + } + + return sceneClass; +} + +/** + * Restore items in preferences from the scene + * + * @param sceneAttributes + * Attributes for the scene. Scenes may be of different types + * (full, generic, etc) and the attributes should be checked when + * restoring the scene. + * + * @param sceneClass + * sceneClass containing the preference items. + */ +void +SessionManager::restorePreferencesFromScene(const SceneAttributes* /*sceneAttributes*/, + const SceneClass* sceneClass) +{ + if (sceneClass == NULL) { + return; + } + + const QString invalidValueName("InVaLiDvAlUe"); + m_caretPreferences->invalidSceneDataValues(); + + std::vector sceneDataValues = m_caretPreferences->getPreferenceSceneDataValues(); + for (auto scv : sceneDataValues) { + const QString name = scv->getName(); + const QString value = sceneClass->getStringValue(scv->getName(), + invalidValueName); + if (value != invalidValueName) { + scv->setSceneValue(QVariant(value)); + } + } +} + /** * Reset the first brain and remove all other brains. */ diff --git a/src/Brain/SessionManager.h b/src/Brain/SessionManager.h index 9043cd9d8..319985f1a 100644 --- a/src/Brain/SessionManager.h +++ b/src/Brain/SessionManager.h @@ -110,6 +110,12 @@ namespace caret { void clearSpacerTabs(); + SceneClass* savePreferencesToScene(const SceneAttributes* sceneAttributes, + const AString& instanceName); + + void restorePreferencesFromScene(const SceneAttributes* sceneAttributes, + const SceneClass* sceneClass); + /** The session manager */ static SessionManager* s_singletonSessionManager; diff --git a/src/Common/CMakeLists.txt b/src/Common/CMakeLists.txt index 1032f7cff..365279325 100644 --- a/src/Common/CMakeLists.txt +++ b/src/Common/CMakeLists.txt @@ -77,6 +77,7 @@ CaretObjectTracksModification.h CaretOMP.h CaretPointer.h CaretPointLocator.h +CaretPreferenceDataValue.h CaretPreferences.h CaretTemporaryFile.h CaretUndoCommand.h @@ -203,6 +204,7 @@ CaretMathExpression.cxx CaretObject.cxx CaretObjectTracksModification.cxx CaretPointLocator.cxx +CaretPreferenceDataValue.cxx CaretPreferences.cxx CaretTemporaryFile.cxx CaretUndoCommand.cxx diff --git a/src/Common/CaretPreferenceDataValue.cxx b/src/Common/CaretPreferenceDataValue.cxx new file mode 100644 index 000000000..475fbc3f9 --- /dev/null +++ b/src/Common/CaretPreferenceDataValue.cxx @@ -0,0 +1,192 @@ + +/*LICENSE_START*/ +/* + * Copyright (C) 2019 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + +#define __CARET_PREFERENCE_DATA_VALUE_DECLARE__ +#include "CaretPreferenceDataValue.h" +#undef __CARET_PREFERENCE_DATA_VALUE_DECLARE__ + +#include + +#include "CaretAssert.h" +using namespace caret; + + + +/** + * \class caret::CaretPreferenceDataValue + * \brief Maintains a single caret preference data valud + * \ingroup Common + */ + +/** + * Constructor. + * + * @param preferenceSettings + * QSettings from caret preferences + * @param preferenceName + * Name of the preference + * @param dataType + * Data type of the preference + * @param defaultValue + * Default value for the preference + */ +CaretPreferenceDataValue::CaretPreferenceDataValue(QSettings* preferenceSettings, + const QString& preferenceName, + const DataType dataType, + const QVariant defaultValue) +: CaretObject(), +m_preferenceSettings(preferenceSettings), +m_preferenceName(preferenceName), +m_dataType(dataType) +{ + CaretAssert(m_preferenceSettings); + CaretAssert( ! m_preferenceName.isEmpty()); + CaretAssert( ! defaultValue.isNull()); + + m_dataValue = m_preferenceSettings->value(m_preferenceName, + defaultValue); + m_sceneDataValue = m_dataValue; +} + +/** + * Destructor. + */ +CaretPreferenceDataValue::~CaretPreferenceDataValue() +{ +} + +/** + * @return Name of preference + */ +QString +CaretPreferenceDataValue::getName() const +{ + return m_preferenceName; +} + +/** + * @return The data type + */ +CaretPreferenceDataValue::DataType +CaretPreferenceDataValue::getDataType() const +{ + return m_dataType; +} + +/** + * @return The data value. Some preferences may be overridden by + * a scene value. If the scene value is valid, it is returned, + * otherwise, the preference value is returned. + * + * @seealso getPreferenceValue() + * + * @param valueType + * Type of value returned (active/preference/scene) + */ +QVariant +CaretPreferenceDataValue::getValue(/*const ValueType valueType*/) const +{ + QVariant valueOut; + if (m_sceneDataValueValid) { + valueOut = m_sceneDataValue; + } + else { + valueOut = m_dataValue; + } + return valueOut; + +// switch (valueType) { +// case ValueType::ACTIVE: +// if (m_sceneDataValueValid) { +// valueOut = m_sceneDataValue; +// } +// else { +// valueOut = m_dataValue; +// } +// break; +// case ValueType::PREFERENCE: +// valueOut = m_dataValue; +// break; +// case ValueType::SCENE: // may not need scene value +// valueOut = m_sceneDataValue; +// break; +// } +// +// CaretAssert( ! valueOut.isNull()); +// return valueOut; +} + +/** + * @return Always returns the preferences value. + */ +QVariant +CaretPreferenceDataValue::getPreferenceValue() const +{ + return m_dataValue; +} + +/** + * Set the value. This does invalidate the scene value. + * + * @param value + * New value + */ +void +CaretPreferenceDataValue::setValue(const QVariant& value) +{ + /* + * Setting value invalidates scene value + */ + m_sceneDataValueValid = false; + + if (value != m_dataValue) { + m_dataValue = value; + m_preferenceSettings->setValue(m_preferenceName, + m_dataValue); + m_preferenceSettings->sync(); + } +} + +/** + * Set the scene value. Also sets the scene value valid. + * + * @param value + * Value of parameter from the scene. + */ +void +CaretPreferenceDataValue::setSceneValue(const QVariant& value) +{ + m_sceneDataValue = value; + m_sceneDataValueValid = true; +} + +/** + * Set the validity of the scene value. + * + * @param validStats + * New validity status of the scene value + */ +void +CaretPreferenceDataValue::setSceneValueValid(const bool validStatus) +{ + m_sceneDataValueValid = validStatus; +} + diff --git a/src/Common/CaretPreferenceDataValue.h b/src/Common/CaretPreferenceDataValue.h new file mode 100644 index 000000000..6e5eb6aae --- /dev/null +++ b/src/Common/CaretPreferenceDataValue.h @@ -0,0 +1,113 @@ +#ifndef __CARET_PREFERENCE_DATA_VALUE_H__ +#define __CARET_PREFERENCE_DATA_VALUE_H__ + +/*LICENSE_START*/ +/* + * Copyright (C) 2019 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + + + +#include + +#include + +#include "CaretObject.h" + +class QSettings; + +namespace caret { + + class CaretPreferences; + + class CaretPreferenceDataValue : public CaretObject { + + public: + enum class DataType { + DOUBLE, + }; + + /** + * Type of value returned by getValue() + */ + enum class ValueType { + /** + * Scene value if it is valid, otherwise preference value. + * Should be used when preferencd is applied. + */ + ACTIVE, + /** + * The preference value used by preferences dialog + */ + PREFERENCE, + /** + * The scene value (even if not valid) + */ + SCENE + }; + + CaretPreferenceDataValue(QSettings* preferenceSettings, + const QString& preferenceName, + const DataType dataType, + const QVariant defaultValue); + + virtual ~CaretPreferenceDataValue(); + + CaretPreferenceDataValue(const CaretPreferenceDataValue&) = delete; + + CaretPreferenceDataValue& operator=(const CaretPreferenceDataValue&) = delete; + + QString getName() const; + + DataType getDataType() const; + + QVariant getPreferenceValue() const; + + QVariant getValue(/*const ValueType valueType*/) const; + + void setValue(const QVariant& value); + + void setSceneValue(const QVariant& value); + + void setSceneValueValid(const bool validStatus); + + // ADD_NEW_METHODS_HERE + + private: + QSettings* m_preferenceSettings; + + const QString m_preferenceName; + + const DataType m_dataType; + + QVariant m_dataValue; + + QVariant m_sceneDataValue; + + bool m_sceneDataValueValid = false; + + // ADD_NEW_MEMBERS_HERE + + }; + +#ifdef __CARET_PREFERENCE_DATA_VALUE_DECLARE__ + // +#endif // __CARET_PREFERENCE_DATA_VALUE_DECLARE__ + +} // namespace +#endif //__CARET_PREFERENCE_DATA_VALUE_H__ diff --git a/src/Common/CaretPreferences.cxx b/src/Common/CaretPreferences.cxx index 1a834b48c..65705380b 100644 --- a/src/Common/CaretPreferences.cxx +++ b/src/Common/CaretPreferences.cxx @@ -31,6 +31,7 @@ #include "CaretAssert.h" #include "CaretLogger.h" +#include "CaretPreferenceDataValue.h" #include "ModelTransform.h" #include "TileTabsConfiguration.h" #include "WuQMacroGroup.h" @@ -58,6 +59,12 @@ CaretPreferences::CaretPreferences() "Caret7"); this->readPreferences(); + m_volumeCrossHairGapPreference.reset(new CaretPreferenceDataValue(this->qSettings, + "volumeAxesCrosshairGap", + CaretPreferenceDataValue::DataType::DOUBLE, + 2.0)); + m_preferenceDataValues.push_back(m_volumeCrossHairGapPreference.get()); + m_colorsMode = BackgroundAndForegroundColorsModeEnum::USER_PREFERENCES; } @@ -66,6 +73,12 @@ CaretPreferences::CaretPreferences() */ CaretPreferences::~CaretPreferences() { + /** + * Note DO NOT delete items in this vector as they are pointers to items + * in unique_ptr's + */ + m_preferenceDataValues.clear(); + this->removeAllCustomViews(); this->removeAllTileTabsConfigurations(); @@ -73,6 +86,29 @@ CaretPreferences::~CaretPreferences() delete this->qSettings; } +/** + * Some preferences are temporarily overriden with a value from a scene + * and these 'scene overrides' are invalidated by this method + */ +void +CaretPreferences::invalidSceneDataValues() +{ + for (auto pdv : m_preferenceDataValues) { + pdv->setSceneValueValid(false); + } + setBackgroundAndForegroundColorsMode(BackgroundAndForegroundColorsModeEnum::USER_PREFERENCES); +} + +/** + * @return The scene data value for items that are saved to + * and restored from scenes. Primarily for use by SessionManager. + */ +std::vector +CaretPreferences::getPreferenceSceneDataValues() +{ + return m_preferenceDataValues; +} + /** * Get the boolean value for the given preference name. * @param name @@ -1225,6 +1261,15 @@ CaretPreferences::setVolumeAxesCrosshairsDisplayed(const bool displayed) this->qSettings->sync(); } +/** + * @return Preference for the volume crosshair gap + */ +CaretPreferenceDataValue* +CaretPreferences::getVolumeCrossHairGapPreference() const +{ + return m_volumeCrossHairGapPreference.get(); +} + /** * @return Are axes labels displayed? */ diff --git a/src/Common/CaretPreferences.h b/src/Common/CaretPreferences.h index b8943e74a..8da7c187d 100644 --- a/src/Common/CaretPreferences.h +++ b/src/Common/CaretPreferences.h @@ -37,6 +37,7 @@ class QStringList; namespace caret { + class CaretPreferenceDataValue; class ModelTransform; class TileTabsConfiguration; class WuQMacroGroup; @@ -90,6 +91,8 @@ namespace caret { void setOpenGLDrawingMethod(const OpenGLDrawingMethodEnum::Enum openGLDrawingMethod); + CaretPreferenceDataValue* getVolumeCrossHairGapPreference() const; + bool isVolumeAxesCrosshairsDisplayed() const; void setVolumeAxesCrosshairsDisplayed(const bool displayed); @@ -204,6 +207,10 @@ namespace caret { void writeMacros(); + void invalidSceneDataValues(); + + std::vector getPreferenceSceneDataValues(); + private: CaretPreferences(const CaretPreferences&); @@ -285,6 +292,10 @@ namespace caret { int32_t volumeMontageCoordinatePrecision; + std::unique_ptr m_volumeCrossHairGapPreference; + + std::vector m_preferenceDataValues; + bool splashScreenEnabled; bool developMenuEnabled; diff --git a/src/Graphics/GraphicsUtilitiesOpenGL.cxx b/src/Graphics/GraphicsUtilitiesOpenGL.cxx index 33916758b..93e99ce8e 100644 --- a/src/Graphics/GraphicsUtilitiesOpenGL.cxx +++ b/src/Graphics/GraphicsUtilitiesOpenGL.cxx @@ -88,6 +88,47 @@ GraphicsUtilitiesOpenGL::convertPixelsToPercentageOfViewportHeight(const float p } +/** + * Converts percentage of viewport height to millimeters. + * The current transformations must be for drawing in millimeters. + * + * @param percentOfViewportHeight + * The value in percentage of viewport height. + * @return + * Millimeters + */ +float +GraphicsUtilitiesOpenGL::convertPercentageOfViewportHeightToMillimeters(const float percentOfViewportHeight) +{ + float millimeters = -1.0f; + + EventOpenGLObjectToWindowTransform xform(EventOpenGLObjectToWindowTransform::SpaceType::VOLUME_SLICE_MODEL); + EventManager::get()->sendEvent(xform.getPointer()); + if (xform.isValid()) { + const std::array viewport = xform.getViewport(); + + const float windowZ = 0.0f; + float bottomWindowXYZ[3] = { (float)viewport[0], (float)viewport[1], (float)windowZ }; + float topWindowXYZ[3] = { (float)viewport[0], (float)(viewport[1] + viewport[3]), (float)windowZ }; + + float bottomModelXYZ[3]; + float topModelXYZ[3]; + + xform.inverseTransformPoint(bottomWindowXYZ, bottomModelXYZ); + xform.inverseTransformPoint(topWindowXYZ, topModelXYZ); + + const float rangeMillimeters = MathFunctions::distance3D(bottomModelXYZ, + topModelXYZ); + const float rangePixels = viewport[3]; + if ((rangePixels > 0) + && (rangeMillimeters > 0)) { + millimeters = (percentOfViewportHeight / 100.0) * rangeMillimeters; + } + } + + return millimeters ; +} + /** * Converts millimeters to a percentage of the viewport height. * The current transformations must be for drawing in millimeters. diff --git a/src/Graphics/GraphicsUtilitiesOpenGL.h b/src/Graphics/GraphicsUtilitiesOpenGL.h index 7a1adc2b9..9bc38f405 100644 --- a/src/Graphics/GraphicsUtilitiesOpenGL.h +++ b/src/Graphics/GraphicsUtilitiesOpenGL.h @@ -37,6 +37,8 @@ namespace caret { static float convertMillimetersToPercentageOfViewportHeight(const float millimeters); + static float convertPercentageOfViewportHeightToMillimeters(const float percentOfViewportHeight); + static float convertMillimetersToPixels(const float millimeters); static float convertPixelsToPercentageOfViewportHeight(const float pixels); diff --git a/src/GuiQt/BrainBrowserWindow.cxx b/src/GuiQt/BrainBrowserWindow.cxx index 131e3117f..d77fa9896 100644 --- a/src/GuiQt/BrainBrowserWindow.cxx +++ b/src/GuiQt/BrainBrowserWindow.cxx @@ -3170,6 +3170,7 @@ BrainBrowserWindow::processCloseAllFiles() CaretPreferences* prefs = SessionManager::get()->getCaretPreferences(); prefs->setBackgroundAndForegroundColorsMode(BackgroundAndForegroundColorsModeEnum::USER_PREFERENCES); + prefs->invalidSceneDataValues(); EventManager::get()->sendEvent(EventUserInterfaceUpdate().getPointer()); EventManager::get()->sendEvent(EventGraphicsUpdateAllWindows().getPointer()); diff --git a/src/GuiQt/BrainBrowserWindowToolBarSlicePlane.cxx b/src/GuiQt/BrainBrowserWindowToolBarSlicePlane.cxx index da4c2b551..7462c17f4 100644 --- a/src/GuiQt/BrainBrowserWindowToolBarSlicePlane.cxx +++ b/src/GuiQt/BrainBrowserWindowToolBarSlicePlane.cxx @@ -25,15 +25,22 @@ #include #include +#include #include +#include #include #include #include +#include #include "BrainBrowserWindowToolBar.h" #include "BrowserTabContent.h" #include "CaretAssert.h" #include "CaretLogger.h" +#include "CaretPreferenceDataValue.h" +#include "CaretPreferences.h" +#include "GuiManager.h" +#include "SessionManager.h" #include "WuQMacroManager.h" #include "WuQWidgetObjectGroup.h" #include "WuQtUtilities.h" @@ -190,7 +197,11 @@ m_parentToolBar(parentToolBar) m_volumeAxisCrosshairsToolButtonAction = new QAction("", this); m_volumeAxisCrosshairsToolButtonAction->setCheckable(true); - m_volumeAxisCrosshairsToolButtonAction->setToolTip("Show crosshairs on slice planes"); + m_volumeAxisCrosshairsToolButtonAction->setToolTip("" + "Show crosshairs on slice planes.
        " + "Press arrow to adjust gap" + ""); + m_volumeAxisCrosshairsToolButtonAction->setMenu(createCrosshairMenu(objectNamePrefix)); QObject::connect(m_volumeAxisCrosshairsToolButtonAction, &QAction::triggered, this, &BrainBrowserWindowToolBarSlicePlane::volumeAxisCrosshairsTriggered); m_volumeAxisCrosshairsToolButtonAction->setObjectName(objectNamePrefix @@ -323,6 +334,85 @@ BrainBrowserWindowToolBarSlicePlane::createViewAllSlicesLayoutMenu(const QString return menu; } +/** + * @return A new instance of the crosshair menu + * @param objectNamePrefix + * Prefix for object names in macro system + * @return + * Menu for crosshair button + */ +QMenu* +BrainBrowserWindowToolBarSlicePlane::createCrosshairMenu(const QString& objectNamePrefix) +{ + m_crosshairGapSpinBox = new QDoubleSpinBox(); + m_crosshairGapSpinBox->setMinimum(0.0); + m_crosshairGapSpinBox->setMaximum(100); + m_crosshairGapSpinBox->setSingleStep(0.1); + m_crosshairGapSpinBox->setDecimals(1); + m_crosshairGapSpinBox->setSuffix("%"); + m_crosshairGapSpinBox->setObjectName(objectNamePrefix + + "CrossHairGapSinBox"); + m_crosshairGapSpinBox->setToolTip("Gap for cross hairs as percentage of window height"); + + QObject::connect(m_crosshairGapSpinBox, QOverload::of(&QDoubleSpinBox::valueChanged), + this, &BrainBrowserWindowToolBarSlicePlane::crosshairGapSpinBoxValueChanged); + + QLabel* crosshairLabel = new QLabel("Gap "); + + QWidget* crosshairWidget = new QWidget(); + QHBoxLayout* crosshairLayout = new QHBoxLayout(crosshairWidget); + crosshairLayout->setContentsMargins(0, 0, 0, 0); + crosshairLayout->addWidget(crosshairLabel); + crosshairLayout->addWidget(m_crosshairGapSpinBox); + crosshairWidget->setSizePolicy(QSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed)); + + QWidgetAction* crossHairGapWidgetAction = new QWidgetAction(this); + crossHairGapWidgetAction->setDefaultWidget(crosshairWidget); + + QMenu* menu = new QMenu(this); + QObject::connect(menu, &QMenu::aboutToShow, + this, &BrainBrowserWindowToolBarSlicePlane::crosshairMenuAboutToShow); + menu->addAction(crossHairGapWidgetAction); + + return menu; +} + +/** + * Called when crosshair menu is about to show + */ +void +BrainBrowserWindowToolBarSlicePlane::crosshairMenuAboutToShow() +{ + CaretPreferences* prefs = SessionManager::get()->getCaretPreferences(); + CaretAssert(prefs); + CaretPreferenceDataValue* gapPref = prefs->getVolumeCrossHairGapPreference(); + CaretAssert(gapPref); + CaretAssert(gapPref->getDataType() == CaretPreferenceDataValue::DataType::DOUBLE); + const double gapValue = gapPref->getValue().toDouble(); + + QSignalBlocker blocker(m_crosshairGapSpinBox); + m_crosshairGapSpinBox->setValue(gapValue); +} + +/** + * Called when crosshair gap spin box value changed + * + * @param value + * New value for crosshair gap. + */ +void +BrainBrowserWindowToolBarSlicePlane::crosshairGapSpinBoxValueChanged(double value) +{ + CaretPreferences* prefs = SessionManager::get()->getCaretPreferences(); + CaretAssert(prefs); + CaretPreferenceDataValue* gapPref = prefs->getVolumeCrossHairGapPreference(); + CaretAssert(gapPref); + CaretAssert(gapPref->getDataType() == CaretPreferenceDataValue::DataType::DOUBLE); + gapPref->setValue(value); + + GuiManager::updateGraphicsAllWindows(); +} + /** * Gets called when the user selects an item on the view all slices layout menu. * diff --git a/src/GuiQt/BrainBrowserWindowToolBarSlicePlane.h b/src/GuiQt/BrainBrowserWindowToolBarSlicePlane.h index 524e719ad..4259b9755 100644 --- a/src/GuiQt/BrainBrowserWindowToolBarSlicePlane.h +++ b/src/GuiQt/BrainBrowserWindowToolBarSlicePlane.h @@ -25,6 +25,7 @@ #include "BrainBrowserWindowToolBarComponent.h" class QActionGroup; +class QDoubleSpinBox; class QMenu; class QPixmap; @@ -54,6 +55,8 @@ namespace caret { void volumeAxisCrosshairsTriggered(bool checked); void volumeAxisCrosshairLabelsTriggered(bool checked); + void crosshairMenuAboutToShow(); + private: BrainBrowserWindowToolBarSlicePlane(const BrainBrowserWindowToolBarSlicePlane&); @@ -67,6 +70,10 @@ namespace caret { QPixmap createCrosshairLabelsIcon(const QWidget* widget); + QMenu* createCrosshairMenu(const QString& objectNamePrefix); + + void crosshairGapSpinBoxValueChanged(double value); + BrainBrowserWindowToolBar* m_parentToolBar; std::vector m_viewAllSliceLayoutMenuActions; @@ -83,6 +90,8 @@ namespace caret { QActionGroup* m_volumePlaneActionGroup; + QDoubleSpinBox* m_crosshairGapSpinBox; + // ADD_NEW_MEMBERS_HERE }; -- GitLab From 47f553cfc420ed9f2923cf84f4febfc0da9a64f2 Mon Sep 17 00:00:00 2001 From: John Harwell Date: Mon, 13 May 2019 10:45:23 -0500 Subject: [PATCH 312/427] WB-851 Crosshair improvement: Crosshair gaps implemented for oblique slice drawing. --- .../BrainOpenGLVolumeObliqueSliceDrawing.cxx | 258 ++++++++---------- .../BrainOpenGLVolumeObliqueSliceDrawing.h | 11 +- 2 files changed, 121 insertions(+), 148 deletions(-) diff --git a/src/Brain/BrainOpenGLVolumeObliqueSliceDrawing.cxx b/src/Brain/BrainOpenGLVolumeObliqueSliceDrawing.cxx index ec0ce2b6e..542f47891 100644 --- a/src/Brain/BrainOpenGLVolumeObliqueSliceDrawing.cxx +++ b/src/Brain/BrainOpenGLVolumeObliqueSliceDrawing.cxx @@ -37,6 +37,7 @@ #include "CaretAssert.h" #include "CaretLogger.h" #include "CaretOpenGLInclude.h" +#include "CaretPreferenceDataValue.h" #include "CaretPreferences.h" #include "CiftiMappableDataFile.h" #include "DeveloperFlagsEnum.h" @@ -53,6 +54,7 @@ #include "GraphicsEngineDataOpenGL.h" #include "GraphicsPrimitiveV3fC4f.h" #include "GraphicsPrimitiveV3fC4ub.h" +#include "GraphicsUtilitiesOpenGL.h" #include "IdentificationWithColor.h" #include "LabelDrawingProperties.h" #include "MathFunctions.h" @@ -658,8 +660,7 @@ BrainOpenGLVolumeObliqueSliceDrawing::drawVolumeSliceViewTypeMontage(const Brain glViewport(viewport[0], viewport[1], viewport[2], viewport[3]); if (m_browserTabContent->isVolumeAxesCrosshairLabelsDisplayed()) { - drawAxesCrosshairsOrthoAndOblique(sliceProjectionType, - sliceViewPlane, + drawAxesCrosshairsOblique(sliceViewPlane, sliceCoordinates, false, true); @@ -1412,11 +1413,10 @@ BrainOpenGLVolumeObliqueSliceDrawing::drawAxesCrosshairs(const VolumeSliceProjec { glPushMatrix(); glLoadIdentity(); - drawAxesCrosshairsOrthoAndOblique(sliceProjectionType, - sliceViewPlane, - sliceCoordinates, - drawCrosshairsFlag, - drawCrosshairLabelsFlag); + drawAxesCrosshairsOblique(sliceViewPlane, + sliceCoordinates, + drawCrosshairsFlag, + drawCrosshairLabelsFlag); glPopMatrix(); } break; @@ -1430,7 +1430,7 @@ BrainOpenGLVolumeObliqueSliceDrawing::drawAxesCrosshairs(const VolumeSliceProjec * Type of projection for the slice drawing (oblique, orthogonal) * @param sliceViewPlane * The slice plane view. - * @param sliceCoordinates + * @param sliceCoordinatesIn * Coordinates of the selected slices. * @param drawCrosshairsFlag * If true, draw the crosshairs. @@ -1438,112 +1438,94 @@ BrainOpenGLVolumeObliqueSliceDrawing::drawAxesCrosshairs(const VolumeSliceProjec * If true, draw the crosshair labels. */ void -BrainOpenGLVolumeObliqueSliceDrawing::drawAxesCrosshairsOrthoAndOblique(const VolumeSliceProjectionTypeEnum::Enum sliceProjectionType, - const VolumeSliceViewPlaneEnum::Enum sliceViewPlane, - const float sliceCoordinates[3], - const bool drawCrosshairsFlag, - const bool drawCrosshairLabelsFlag) +BrainOpenGLVolumeObliqueSliceDrawing::drawAxesCrosshairsOblique(const VolumeSliceViewPlaneEnum::Enum sliceViewPlane, + const float sliceCoordinatesIn[3], + const bool drawCrosshairsFlag, + const bool drawCrosshairLabelsFlag) { - bool obliqueModeFlag = false; - switch (sliceProjectionType) { - case VolumeSliceProjectionTypeEnum::VOLUME_SLICE_PROJECTION_OBLIQUE: - obliqueModeFlag = true; - break; - case VolumeSliceProjectionTypeEnum::VOLUME_SLICE_PROJECTION_ORTHOGONAL: - CaretAssert(0); - break; - } + CaretPreferenceDataValue* gapPref = SessionManager::get()->getCaretPreferences()->getVolumeCrossHairGapPreference(); + CaretAssert(gapPref); + const float gapPercentViewportHeight = gapPref->getValue().toDouble(); + const float gapMM = GraphicsUtilitiesOpenGL::convertPercentageOfViewportHeightToMillimeters(gapPercentViewportHeight); + const std::array sliceCoordinates { sliceCoordinatesIn[0], sliceCoordinatesIn[1], sliceCoordinatesIn[2] }; GLboolean depthEnabled = GL_FALSE; glGetBooleanv(GL_DEPTH_TEST, &depthEnabled); glDisable(GL_DEPTH_TEST); - const float bigValue = 10000.0; - - float horizontalAxisStartXYZ[3] = { - sliceCoordinates[0], - sliceCoordinates[1], - sliceCoordinates[2] - }; - float horizontalAxisEndXYZ[3] = { - sliceCoordinates[0], - sliceCoordinates[1], - sliceCoordinates[2] - }; + const float bigValue = 10000.0 + gapMM; - float verticalAxisStartXYZ[3] = { - sliceCoordinates[0], - sliceCoordinates[1], - sliceCoordinates[2] - }; - float verticalAxisEndXYZ[3] = { - sliceCoordinates[0], - sliceCoordinates[1], - sliceCoordinates[2] - }; + std::array horizontalAxisPosStartXYZ { sliceCoordinates }; + std::array horizontalAxisPosEndXYZ { sliceCoordinates }; + std::array verticalAxisPosStartXYZ { sliceCoordinates }; + std::array verticalAxisPosEndXYZ { sliceCoordinates }; float trans[3]; m_browserTabContent->getTranslation(trans); - + float horizTrans[3] = { trans[0], trans[1], trans[2] }; float vertTrans[3] = { trans[0], trans[1], trans[2] }; - if (obliqueModeFlag) { - switch (sliceViewPlane) { - case VolumeSliceViewPlaneEnum::ALL: - break; - case VolumeSliceViewPlaneEnum::AXIAL: - break; - case VolumeSliceViewPlaneEnum::CORONAL: - horizontalAxisStartXYZ[0] = sliceCoordinates[0]; - horizontalAxisStartXYZ[1] = sliceCoordinates[2]; - horizontalAxisStartXYZ[2] = sliceCoordinates[1]; - horizontalAxisEndXYZ[0] = sliceCoordinates[0]; - horizontalAxisEndXYZ[1] = sliceCoordinates[2]; - horizontalAxisEndXYZ[2] = sliceCoordinates[1]; - - horizTrans[0] = trans[0]; - horizTrans[1] = trans[2]; - horizTrans[2] = trans[1]; - - verticalAxisStartXYZ[0] = sliceCoordinates[0]; - verticalAxisStartXYZ[1] = sliceCoordinates[1]; - verticalAxisStartXYZ[2] = sliceCoordinates[2]; - verticalAxisEndXYZ[0] = sliceCoordinates[0]; - verticalAxisEndXYZ[1] = sliceCoordinates[1]; - verticalAxisEndXYZ[2] = sliceCoordinates[2]; + switch (sliceViewPlane) { + case VolumeSliceViewPlaneEnum::ALL: + break; + case VolumeSliceViewPlaneEnum::AXIAL: + break; + case VolumeSliceViewPlaneEnum::CORONAL: + horizontalAxisPosStartXYZ[0] = sliceCoordinates[0]; + horizontalAxisPosStartXYZ[1] = sliceCoordinates[2]; + horizontalAxisPosStartXYZ[2] = sliceCoordinates[1]; + horizontalAxisPosEndXYZ[0] = sliceCoordinates[0]; + horizontalAxisPosEndXYZ[1] = sliceCoordinates[2]; + horizontalAxisPosEndXYZ[2] = sliceCoordinates[1]; + + horizTrans[0] = trans[0]; + horizTrans[1] = trans[2]; + horizTrans[2] = trans[1]; + + verticalAxisPosStartXYZ[0] = sliceCoordinates[0]; + verticalAxisPosStartXYZ[1] = sliceCoordinates[2]; + verticalAxisPosStartXYZ[2] = sliceCoordinates[1]; + verticalAxisPosEndXYZ[0] = sliceCoordinates[0]; + verticalAxisPosEndXYZ[1] = sliceCoordinates[2]; + verticalAxisPosEndXYZ[2] = sliceCoordinates[1]; + + vertTrans[0] = trans[0]; + vertTrans[1] = trans[2]; + vertTrans[2] = trans[1]; + break; + case VolumeSliceViewPlaneEnum::PARASAGITTAL: + horizontalAxisPosStartXYZ[0] = -sliceCoordinates[1]; + horizontalAxisPosStartXYZ[1] = sliceCoordinates[2]; + horizontalAxisPosStartXYZ[2] = sliceCoordinates[0]; + horizontalAxisPosEndXYZ[0] = -sliceCoordinates[1]; + horizontalAxisPosEndXYZ[1] = sliceCoordinates[2]; + horizontalAxisPosEndXYZ[2] = sliceCoordinates[0]; + + horizTrans[0] = -trans[1]; + horizTrans[1] = trans[2]; + horizTrans[2] = trans[0]; + + verticalAxisPosStartXYZ[0] = -sliceCoordinates[1]; + verticalAxisPosStartXYZ[1] = sliceCoordinates[2]; + verticalAxisPosStartXYZ[2] = sliceCoordinates[0]; + verticalAxisPosEndXYZ[0] = -sliceCoordinates[1]; + verticalAxisPosEndXYZ[1] = sliceCoordinates[2]; + verticalAxisPosEndXYZ[2] = sliceCoordinates[0]; - vertTrans[0] = trans[0]; - vertTrans[1] = trans[1]; - vertTrans[2] = trans[2]; - break; - case VolumeSliceViewPlaneEnum::PARASAGITTAL: - horizontalAxisStartXYZ[0] = sliceCoordinates[1]; - horizontalAxisStartXYZ[1] = sliceCoordinates[2]; - horizontalAxisStartXYZ[2] = sliceCoordinates[0]; - horizontalAxisEndXYZ[0] = sliceCoordinates[1]; - horizontalAxisEndXYZ[1] = sliceCoordinates[2]; - horizontalAxisEndXYZ[2] = sliceCoordinates[0]; - - horizTrans[0] = trans[1]; - horizTrans[1] = trans[2]; - horizTrans[2] = trans[0]; - - verticalAxisStartXYZ[0] = -sliceCoordinates[1]; - verticalAxisStartXYZ[1] = sliceCoordinates[0]; - verticalAxisStartXYZ[2] = sliceCoordinates[2]; - verticalAxisEndXYZ[0] = -sliceCoordinates[1]; - verticalAxisEndXYZ[1] = sliceCoordinates[0]; - verticalAxisEndXYZ[2] = sliceCoordinates[2]; - - vertTrans[0] = -trans[1]; - vertTrans[1] = trans[0]; - vertTrans[2] = trans[2]; - break; - } + vertTrans[0] = -trans[1]; + vertTrans[1] = trans[2]; + vertTrans[2] = trans[0]; + break; } + std::array horizontalAxisNegStartXYZ { horizontalAxisPosStartXYZ }; + std::array horizontalAxisNegEndXYZ { horizontalAxisPosEndXYZ }; + std::array verticalAxisNegStartXYZ { verticalAxisPosStartXYZ }; + std::array verticalAxisNegEndXYZ { verticalAxisPosEndXYZ }; + + float axialRGBA[4]; getAxesColor(VolumeSliceViewPlaneEnum::AXIAL, axialRGBA); @@ -1571,64 +1553,52 @@ BrainOpenGLVolumeObliqueSliceDrawing::drawAxesCrosshairsOrthoAndOblique(const Vo horizontalLeftText = "L"; horizontalRightText = "R"; horizontalAxisRGBA = coronalRGBA; - horizontalAxisStartXYZ[0] -= bigValue; - horizontalAxisEndXYZ[0] += bigValue; - + horizontalAxisPosStartXYZ[0] += gapMM; + horizontalAxisPosEndXYZ[0] += bigValue; + horizontalAxisNegStartXYZ[0] -= gapMM; + horizontalAxisNegEndXYZ[0] -= bigValue; + verticalBottomText = "P"; verticalTopText = "A"; verticalAxisRGBA = paraRGBA; - verticalAxisStartXYZ[1] -= bigValue; - verticalAxisEndXYZ[1] += bigValue; + verticalAxisPosStartXYZ[1] += gapMM; + verticalAxisPosEndXYZ[1] += bigValue; + verticalAxisNegStartXYZ[1] -= gapMM; + verticalAxisNegEndXYZ[1] -= bigValue; break; case VolumeSliceViewPlaneEnum::CORONAL: horizontalLeftText = "L"; horizontalRightText = "R"; horizontalAxisRGBA = axialRGBA; - if (obliqueModeFlag) { - horizontalAxisStartXYZ[0] -= bigValue; - horizontalAxisEndXYZ[0] += bigValue; - } - else { - horizontalAxisStartXYZ[0] -= bigValue; - horizontalAxisEndXYZ[0] += bigValue; - } - + horizontalAxisPosStartXYZ[0] += gapMM; + horizontalAxisPosEndXYZ[0] += bigValue; + horizontalAxisNegStartXYZ[0] -= gapMM; + horizontalAxisNegEndXYZ[0] -= bigValue; + verticalBottomText = "I"; verticalTopText = "S"; verticalAxisRGBA = paraRGBA; - if (obliqueModeFlag) { - verticalAxisStartXYZ[1] -= bigValue; - verticalAxisEndXYZ[1] += bigValue; - } - else { - verticalAxisStartXYZ[2] -= bigValue; - verticalAxisEndXYZ[2] += bigValue; - } + verticalAxisPosStartXYZ[1] += gapMM; + verticalAxisPosEndXYZ[1] += bigValue; + verticalAxisNegStartXYZ[1] -= gapMM; + verticalAxisNegEndXYZ[1] -= bigValue; break; case VolumeSliceViewPlaneEnum::PARASAGITTAL: horizontalLeftText = "A"; horizontalRightText = "P"; horizontalAxisRGBA = axialRGBA; - if (obliqueModeFlag) { - horizontalAxisStartXYZ[0] -= bigValue; - horizontalAxisEndXYZ[0] += bigValue; - } - else { - horizontalAxisStartXYZ[1] -= bigValue; - horizontalAxisEndXYZ[1] += bigValue; - } - + horizontalAxisPosStartXYZ[0] += gapMM; + horizontalAxisPosEndXYZ[0] += bigValue; + horizontalAxisNegStartXYZ[0] -= gapMM; + horizontalAxisNegEndXYZ[0] -= bigValue; + verticalBottomText = "I"; verticalTopText = "S"; verticalAxisRGBA = coronalRGBA; - if (obliqueModeFlag) { - verticalAxisStartXYZ[1] -= bigValue; - verticalAxisEndXYZ[1] += bigValue; - } - else { - verticalAxisStartXYZ[2] -= bigValue; - verticalAxisEndXYZ[2] += bigValue; - } + verticalAxisPosStartXYZ[1] += gapMM; + verticalAxisPosEndXYZ[1] += bigValue; + verticalAxisNegStartXYZ[1] -= gapMM; + verticalAxisNegEndXYZ[1] -= bigValue; break; } @@ -1664,8 +1634,10 @@ BrainOpenGLVolumeObliqueSliceDrawing::drawAxesCrosshairsOrthoAndOblique(const Vo glPushMatrix(); glTranslatef(horizTrans[0], horizTrans[1], horizTrans[2]); std::unique_ptr horizHairPrimitive(GraphicsPrimitive::newPrimitiveV3fC4f(GraphicsPrimitive::PrimitiveType::POLYGONAL_LINES)); - horizHairPrimitive->addVertex(horizontalAxisStartXYZ, horizontalAxisRGBA); - horizHairPrimitive->addVertex(horizontalAxisEndXYZ, horizontalAxisRGBA); + horizHairPrimitive->addVertex(&horizontalAxisPosStartXYZ[0], horizontalAxisRGBA); + horizHairPrimitive->addVertex(&horizontalAxisPosEndXYZ[0], horizontalAxisRGBA); + horizHairPrimitive->addVertex(&horizontalAxisNegStartXYZ[0], horizontalAxisRGBA); + horizHairPrimitive->addVertex(&horizontalAxisNegEndXYZ[0], horizontalAxisRGBA); horizHairPrimitive->setLineWidth(GraphicsPrimitive::LineWidthType::PIXELS, 2.0f); GraphicsEngineDataOpenGL::draw(horizHairPrimitive.get()); glPopMatrix(); @@ -1673,8 +1645,10 @@ BrainOpenGLVolumeObliqueSliceDrawing::drawAxesCrosshairsOrthoAndOblique(const Vo glPushMatrix(); glTranslatef(vertTrans[0], vertTrans[1], vertTrans[2]); std::unique_ptr vertHairPrimitive(GraphicsPrimitive::newPrimitiveV3fC4f(GraphicsPrimitive::PrimitiveType::POLYGONAL_LINES)); - vertHairPrimitive->addVertex(verticalAxisStartXYZ, verticalAxisRGBA); - vertHairPrimitive->addVertex(verticalAxisEndXYZ, verticalAxisRGBA); + vertHairPrimitive->addVertex(&verticalAxisPosStartXYZ[0], verticalAxisRGBA); + vertHairPrimitive->addVertex(&verticalAxisPosEndXYZ[0], verticalAxisRGBA); + vertHairPrimitive->addVertex(&verticalAxisNegStartXYZ[0], verticalAxisRGBA); + vertHairPrimitive->addVertex(&verticalAxisNegEndXYZ[0], verticalAxisRGBA); vertHairPrimitive->setLineWidth(GraphicsPrimitive::LineWidthType::PIXELS, 2.0f); GraphicsEngineDataOpenGL::draw(vertHairPrimitive.get()); glPopMatrix(); @@ -1799,7 +1773,7 @@ BrainOpenGLVolumeObliqueSliceDrawing::drawAxesCrosshairsOrthoAndOblique(const Vo annotationText.setBackgroundColor(CaretColorEnum::CUSTOM); annotationText.setCustomTextColor(horizontalAxisRGBA); annotationText.setCustomBackgroundColor(backgroundRGBA); - + annotationText.setHorizontalAlignment(AnnotationTextAlignHorizontalEnum::LEFT); annotationText.setVerticalAlignment(AnnotationTextAlignVerticalEnum::MIDDLE); annotationText.setText(horizontalLeftText); @@ -1821,7 +1795,7 @@ BrainOpenGLVolumeObliqueSliceDrawing::drawAxesCrosshairsOrthoAndOblique(const Vo m_fixedPipelineDrawing->drawTextAtViewportCoords(textBottomWindowXY[0], textBottomWindowXY[1], annotationText); - + annotationText.setText(verticalTopText); annotationText.setHorizontalAlignment(AnnotationTextAlignHorizontalEnum::CENTER); annotationText.setVerticalAlignment(AnnotationTextAlignVerticalEnum::TOP); diff --git a/src/Brain/BrainOpenGLVolumeObliqueSliceDrawing.h b/src/Brain/BrainOpenGLVolumeObliqueSliceDrawing.h index 99dcdf8ba..17b2f79b0 100644 --- a/src/Brain/BrainOpenGLVolumeObliqueSliceDrawing.h +++ b/src/Brain/BrainOpenGLVolumeObliqueSliceDrawing.h @@ -222,12 +222,11 @@ namespace caret { const float sliceCoordinates[3], Plane& planeOut); - void drawAxesCrosshairsOrthoAndOblique(const VolumeSliceProjectionTypeEnum::Enum sliceProjectionType, - const VolumeSliceViewPlaneEnum::Enum sliceViewPlane, - const float sliceCoordinates[3], - const bool drawCrosshairsFlag, - const bool drawCrosshairLabelsFlag); - + void drawAxesCrosshairsOblique(const VolumeSliceViewPlaneEnum::Enum sliceViewPlane, + const float sliceCoordinates[3], + const bool drawCrosshairsFlag, + const bool drawCrosshairLabelsFlag); + void setVolumeSliceViewingAndModelingTransformations(const VolumeSliceProjectionTypeEnum::Enum sliceProjectionType, const VolumeSliceViewPlaneEnum::Enum sliceViewPlane, const Plane& plane, -- GitLab From abfba84bc7ffaf48c0c6843baae98c25025f2da6 Mon Sep 17 00:00:00 2001 From: John Harwell Date: Mon, 13 May 2019 10:55:49 -0500 Subject: [PATCH 313/427] WB-851 Crosshair improvement: Cleanup code for oblique crosshair drawing. --- .../BrainOpenGLVolumeObliqueSliceDrawing.cxx | 41 ++++--------------- 1 file changed, 7 insertions(+), 34 deletions(-) diff --git a/src/Brain/BrainOpenGLVolumeObliqueSliceDrawing.cxx b/src/Brain/BrainOpenGLVolumeObliqueSliceDrawing.cxx index 542f47891..3957270dd 100644 --- a/src/Brain/BrainOpenGLVolumeObliqueSliceDrawing.cxx +++ b/src/Brain/BrainOpenGLVolumeObliqueSliceDrawing.cxx @@ -1457,15 +1457,11 @@ BrainOpenGLVolumeObliqueSliceDrawing::drawAxesCrosshairsOblique(const VolumeSlic const float bigValue = 10000.0 + gapMM; std::array horizontalAxisPosStartXYZ { sliceCoordinates }; - std::array horizontalAxisPosEndXYZ { sliceCoordinates }; - std::array verticalAxisPosStartXYZ { sliceCoordinates }; - std::array verticalAxisPosEndXYZ { sliceCoordinates }; float trans[3]; m_browserTabContent->getTranslation(trans); - float horizTrans[3] = { trans[0], trans[1], trans[2] }; - float vertTrans[3] = { trans[0], trans[1], trans[2] }; + std::array horizTrans { trans[0], trans[1], trans[2] }; switch (sliceViewPlane) { case VolumeSliceViewPlaneEnum::ALL: @@ -1476,56 +1472,33 @@ BrainOpenGLVolumeObliqueSliceDrawing::drawAxesCrosshairsOblique(const VolumeSlic horizontalAxisPosStartXYZ[0] = sliceCoordinates[0]; horizontalAxisPosStartXYZ[1] = sliceCoordinates[2]; horizontalAxisPosStartXYZ[2] = sliceCoordinates[1]; - horizontalAxisPosEndXYZ[0] = sliceCoordinates[0]; - horizontalAxisPosEndXYZ[1] = sliceCoordinates[2]; - horizontalAxisPosEndXYZ[2] = sliceCoordinates[1]; horizTrans[0] = trans[0]; horizTrans[1] = trans[2]; horizTrans[2] = trans[1]; - - verticalAxisPosStartXYZ[0] = sliceCoordinates[0]; - verticalAxisPosStartXYZ[1] = sliceCoordinates[2]; - verticalAxisPosStartXYZ[2] = sliceCoordinates[1]; - verticalAxisPosEndXYZ[0] = sliceCoordinates[0]; - verticalAxisPosEndXYZ[1] = sliceCoordinates[2]; - verticalAxisPosEndXYZ[2] = sliceCoordinates[1]; - - vertTrans[0] = trans[0]; - vertTrans[1] = trans[2]; - vertTrans[2] = trans[1]; break; case VolumeSliceViewPlaneEnum::PARASAGITTAL: horizontalAxisPosStartXYZ[0] = -sliceCoordinates[1]; horizontalAxisPosStartXYZ[1] = sliceCoordinates[2]; horizontalAxisPosStartXYZ[2] = sliceCoordinates[0]; - horizontalAxisPosEndXYZ[0] = -sliceCoordinates[1]; - horizontalAxisPosEndXYZ[1] = sliceCoordinates[2]; - horizontalAxisPosEndXYZ[2] = sliceCoordinates[0]; horizTrans[0] = -trans[1]; horizTrans[1] = trans[2]; horizTrans[2] = trans[0]; - - verticalAxisPosStartXYZ[0] = -sliceCoordinates[1]; - verticalAxisPosStartXYZ[1] = sliceCoordinates[2]; - verticalAxisPosStartXYZ[2] = sliceCoordinates[0]; - verticalAxisPosEndXYZ[0] = -sliceCoordinates[1]; - verticalAxisPosEndXYZ[1] = sliceCoordinates[2]; - verticalAxisPosEndXYZ[2] = sliceCoordinates[0]; - - vertTrans[0] = -trans[1]; - vertTrans[1] = trans[2]; - vertTrans[2] = trans[0]; break; } + std::array horizontalAxisPosEndXYZ { horizontalAxisPosStartXYZ }; + std::array verticalAxisPosStartXYZ { horizontalAxisPosStartXYZ }; + std::array verticalAxisPosEndXYZ { horizontalAxisPosStartXYZ }; + std::array horizontalAxisNegStartXYZ { horizontalAxisPosStartXYZ }; std::array horizontalAxisNegEndXYZ { horizontalAxisPosEndXYZ }; std::array verticalAxisNegStartXYZ { verticalAxisPosStartXYZ }; std::array verticalAxisNegEndXYZ { verticalAxisPosEndXYZ }; - + std::array vertTrans { horizTrans }; + float axialRGBA[4]; getAxesColor(VolumeSliceViewPlaneEnum::AXIAL, axialRGBA); -- GitLab From 287570d0e6a255f620e2ca206d65ae0a34c0ba85 Mon Sep 17 00:00:00 2001 From: John Harwell Date: Mon, 13 May 2019 14:38:29 -0500 Subject: [PATCH 314/427] WB-857: Older code fixed a problem with identical file names by saving the full path into the scene file and then performing a 'right-most characters" match to restore the data. While it works, we should not have absolute paths in the scene file. It was probably done this way since the file was the name of a scene XML element and the scene path absolute/relative mechanism only works on 'ScenePathName' elements. Now the name of the element is just the name of the file (no path). A new child element, using a ScenePathName element has been added that is the absolute path in memory and a relative path in the scene file is now used to properly restore the data file's scene data. --- src/Brain/Brain.cxx | 103 +++++++++++++++++++++++++++++++++++--------- 1 file changed, 82 insertions(+), 21 deletions(-) diff --git a/src/Brain/Brain.cxx b/src/Brain/Brain.cxx index 954453fce..283b5f29e 100644 --- a/src/Brain/Brain.cxx +++ b/src/Brain/Brain.cxx @@ -7248,27 +7248,56 @@ Brain::saveToScene(const SceneAttributes* sceneAttributes, std::vector allCaretDataFiles; getAllDataFiles(allCaretDataFiles); - /* - * Save data files into an array. - * Note that data file's saveToScene returns NULL if no data for saving. - */ - std::vector allCaretDataFileScenes; - for (std::vector::iterator iter = allCaretDataFiles.begin(); - iter != allCaretDataFiles.end(); - iter++) { - CaretDataFile* cdf = *iter; - - const AString caretDataFileName = cdf->getFileName(); // use full path 7/16/2015 cdf->getFileNameNoPath(); - SceneClass* caretDataFileSceneClass = cdf->saveToScene(sceneAttributes, - caretDataFileName); - if (caretDataFileSceneClass != NULL) { - allCaretDataFileScenes.push_back(caretDataFileSceneClass); + bool testNewFilePathsFlag(true); + if (testNewFilePathsFlag) { + /* + * Save data files into an array. + * Note that data file's saveToScene returns NULL if no data for saving. + */ + std::vector allCaretDataFileScenes; + for (std::vector::iterator iter = allCaretDataFiles.begin(); + iter != allCaretDataFiles.end(); + iter++) { + CaretDataFile* cdf = *iter; + + const AString caretDataFileName = cdf->getFileNameNoPath(); + SceneClass* caretDataFileSceneClass = cdf->saveToScene(sceneAttributes, + caretDataFileName); + if (caretDataFileSceneClass != NULL) { + caretDataFileSceneClass->addPathName("dataFileName_V2", + cdf->getFileName()); + allCaretDataFileScenes.push_back(caretDataFileSceneClass); + } + } + if (allCaretDataFileScenes.empty() == false) { + SceneClassArray* caretDataFileSceneArray = new SceneClassArray("allCaretDataFiles_V2", + allCaretDataFileScenes); + sceneClass->addChild(caretDataFileSceneArray); } } - if (allCaretDataFileScenes.empty() == false) { - SceneClassArray* caretDataFileSceneArray = new SceneClassArray("allCaretDataFiles", - allCaretDataFileScenes); - sceneClass->addChild(caretDataFileSceneArray); + else { + /* + * Save data files into an array. + * Note that data file's saveToScene returns NULL if no data for saving. + */ + std::vector allCaretDataFileScenes; + for (std::vector::iterator iter = allCaretDataFiles.begin(); + iter != allCaretDataFiles.end(); + iter++) { + CaretDataFile* cdf = *iter; + + const AString caretDataFileName = cdf->getFileName(); // use full path 7/16/2015 cdf->getFileNameNoPath(); + SceneClass* caretDataFileSceneClass = cdf->saveToScene(sceneAttributes, + caretDataFileName); + if (caretDataFileSceneClass != NULL) { + allCaretDataFileScenes.push_back(caretDataFileSceneClass); + } + } + if (allCaretDataFileScenes.empty() == false) { + SceneClassArray* caretDataFileSceneArray = new SceneClassArray("allCaretDataFiles", + allCaretDataFileScenes); + sceneClass->addChild(caretDataFileSceneArray); + } } if (isSaveSpecFile) { @@ -7424,10 +7453,42 @@ Brain::restoreFromScene(const SceneAttributes* sceneAttributes, getAllDataFiles(allCaretDataFiles); /* - * Restore data files + * Restore data files. Try to restore "V2" and if not found, restore older version */ + const SceneClassArray* caretDataFileSceneArrayV2 = sceneClass->getClassArray("allCaretDataFiles_V2"); const SceneClassArray* caretDataFileSceneArray = sceneClass->getClassArray("allCaretDataFiles"); - if (caretDataFileSceneArray != NULL) { + if (caretDataFileSceneArrayV2 != NULL) { + /* + * Note: Name of element is name of file without path. + * A child of the element contains a ScenePathName containing + * name of file (full path in memory and relative to scene + * file name when file is written. + */ + for (auto caretDataFile : allCaretDataFiles) { + CaretAssert(caretDataFile); + const AString caretDataFileNameFullPath = caretDataFile->getFileName(); + const AString caretDataFileName = caretDataFile->getFileNameNoPath(); + + const int32_t numCaretDataFileScenes = caretDataFileSceneArrayV2->getNumberOfArrayElements(); + for (int32_t i = 0; i < numCaretDataFileScenes; i++) { + const SceneClass* fileSceneClass = caretDataFileSceneArrayV2->getClassAtIndex(i); + const AString fileName = fileSceneClass->getName(); + if (fileName == caretDataFileName) { + const AString fullPathFileName = fileSceneClass->getPathNameValue("dataFileName_V2"); + if (fullPathFileName == caretDataFileNameFullPath) { + caretDataFile->restoreFromScene(sceneAttributes, + fileSceneClass); + loadMatrixChartingFileDefaultRowOrColumn(caretDataFile); + break; // get out of inner file loop + } + } + } + } + } + else if (caretDataFileSceneArray != NULL) { + /* + * Restore old file data that used full pathss + */ for (std::vector::iterator iter = allCaretDataFiles.begin(); iter != allCaretDataFiles.end(); iter++) { -- GitLab From 197421e29608e181aac7e71135285706d0d16529 Mon Sep 17 00:00:00 2001 From: John Harwell Date: Mon, 13 May 2019 14:46:30 -0500 Subject: [PATCH 315/427] More on WB-857: Removed code that was replaced by new relative path for writing scene. Older code for restoring remains and is used with scene files created prior to this bug fix. --- src/Brain/Brain.cxx | 71 ++++++++++++++------------------------------- 1 file changed, 22 insertions(+), 49 deletions(-) diff --git a/src/Brain/Brain.cxx b/src/Brain/Brain.cxx index 283b5f29e..0e74c795e 100644 --- a/src/Brain/Brain.cxx +++ b/src/Brain/Brain.cxx @@ -7248,58 +7248,31 @@ Brain::saveToScene(const SceneAttributes* sceneAttributes, std::vector allCaretDataFiles; getAllDataFiles(allCaretDataFiles); - bool testNewFilePathsFlag(true); - if (testNewFilePathsFlag) { - /* - * Save data files into an array. - * Note that data file's saveToScene returns NULL if no data for saving. - */ - std::vector allCaretDataFileScenes; - for (std::vector::iterator iter = allCaretDataFiles.begin(); - iter != allCaretDataFiles.end(); - iter++) { - CaretDataFile* cdf = *iter; - - const AString caretDataFileName = cdf->getFileNameNoPath(); - SceneClass* caretDataFileSceneClass = cdf->saveToScene(sceneAttributes, - caretDataFileName); - if (caretDataFileSceneClass != NULL) { - caretDataFileSceneClass->addPathName("dataFileName_V2", - cdf->getFileName()); - allCaretDataFileScenes.push_back(caretDataFileSceneClass); - } - } - if (allCaretDataFileScenes.empty() == false) { - SceneClassArray* caretDataFileSceneArray = new SceneClassArray("allCaretDataFiles_V2", - allCaretDataFileScenes); - sceneClass->addChild(caretDataFileSceneArray); + /* + * Save data files into an array. + * Note that data file's saveToScene returns NULL if no data for saving. + */ + std::vector allCaretDataFileScenes; + for (std::vector::iterator iter = allCaretDataFiles.begin(); + iter != allCaretDataFiles.end(); + iter++) { + CaretDataFile* cdf = *iter; + + const AString caretDataFileName = cdf->getFileNameNoPath(); + SceneClass* caretDataFileSceneClass = cdf->saveToScene(sceneAttributes, + caretDataFileName); + if (caretDataFileSceneClass != NULL) { + caretDataFileSceneClass->addPathName("dataFileName_V2", + cdf->getFileName()); + allCaretDataFileScenes.push_back(caretDataFileSceneClass); } } - else { - /* - * Save data files into an array. - * Note that data file's saveToScene returns NULL if no data for saving. - */ - std::vector allCaretDataFileScenes; - for (std::vector::iterator iter = allCaretDataFiles.begin(); - iter != allCaretDataFiles.end(); - iter++) { - CaretDataFile* cdf = *iter; - - const AString caretDataFileName = cdf->getFileName(); // use full path 7/16/2015 cdf->getFileNameNoPath(); - SceneClass* caretDataFileSceneClass = cdf->saveToScene(sceneAttributes, - caretDataFileName); - if (caretDataFileSceneClass != NULL) { - allCaretDataFileScenes.push_back(caretDataFileSceneClass); - } - } - if (allCaretDataFileScenes.empty() == false) { - SceneClassArray* caretDataFileSceneArray = new SceneClassArray("allCaretDataFiles", - allCaretDataFileScenes); - sceneClass->addChild(caretDataFileSceneArray); - } + if (allCaretDataFileScenes.empty() == false) { + SceneClassArray* caretDataFileSceneArray = new SceneClassArray("allCaretDataFiles_V2", + allCaretDataFileScenes); + sceneClass->addChild(caretDataFileSceneArray); } - + if (isSaveSpecFile) { SpecFile sf; sf.setFileName(m_specFile->getFileName()); -- GitLab From 1f891533a5cc0c37426c2118d892494021ecb159 Mon Sep 17 00:00:00 2001 From: John Harwell Date: Mon, 13 May 2019 15:28:04 -0500 Subject: [PATCH 316/427] WB-857: Absolute paths were also found for surfaces used for volume surface outline. This has been corrected to use ScenePathName so that the file names are absolute in memory and relative in the scene file. --- src/Brain/SurfaceSelectionModel.cxx | 75 +++++++++++++++++------------ 1 file changed, 44 insertions(+), 31 deletions(-) diff --git a/src/Brain/SurfaceSelectionModel.cxx b/src/Brain/SurfaceSelectionModel.cxx index 57add87ca..7d3aeb367 100644 --- a/src/Brain/SurfaceSelectionModel.cxx +++ b/src/Brain/SurfaceSelectionModel.cxx @@ -389,10 +389,8 @@ SurfaceSelectionModel::saveToScene(const SceneAttributes* /*sceneAttributes*/, Surface* surface = getSurface(); if (surface != NULL) { - sceneClass->addString("m_selectedSurfaceFullPath", - surface->getFileName()); - sceneClass->addString("m_selectedSurface", - surface->getFileNameNoPath()); + sceneClass->addPathName("m_selectedSurfacePathName", + surface->getFileName()); } return sceneClass; @@ -420,41 +418,56 @@ SurfaceSelectionModel::restoreFromScene(const SceneAttributes* /*sceneAttributes std::vector allSurfaces = getAvailableSurfaces(); - const AString& surfaceFileNameFullPath = sceneClass->getStringValue("m_selectedSurfaceFullPath", - ""); /* - * For full path, find the best match using the right-most characters that - * will contain any relative path. When scene files are moved to different - * computers the full path may change the parts of the path nearest the - * name of the file will match. + * Element "m_selectedSurfacePathName" replaces older elements + * and fixes problem with absolute paths in the scene file */ + const AString surfacePathName = sceneClass->getPathNameValue("m_selectedSurfacePathName"); Surface* pathNameMatchSurface = NULL; - int32_t pathNameMatchLength = 0; - if ( ! surfaceFileNameFullPath.isEmpty()) { + Surface* nameMatchSurface = NULL; + if ( ! surfacePathName.isEmpty()) { for (auto surface : allSurfaces) { - const AString name = surface->getFileName(); - const int32_t numMatch = name.countMatchingCharactersFromEnd(surfaceFileNameFullPath); - if (numMatch > pathNameMatchLength) { - pathNameMatchLength = numMatch; + if (surface->getFileName() == surfacePathName) { pathNameMatchSurface = surface; + break; } } } - - /* - * Match name of file with NO path - * Always restore this so that the object is marked as restored - * (within the 'get' method). Otherwise if compiled debug, this - * object will get logged as 'not restored'. - */ - Surface* nameMatchSurface = NULL; - const AString& surfaceFileName = sceneClass->getStringValue("m_selectedSurface", - ""); - if ( ! surfaceFileName.isEmpty()) { - for (auto surface : allSurfaces) { - if (surface->getFileNameNoPath() == surfaceFileName) { - nameMatchSurface = surface; - break; + else { + /* + * For full path, find the best match using the right-most characters that + * will contain any relative path. When scene files are moved to different + * computers the full path may change the parts of the path nearest the + * name of the file will match. + */ + const AString surfaceFileNameFullPath = sceneClass->getStringValue("m_selectedSurfaceFullPath", + ""); + int32_t pathNameMatchLength = 0; + if ( ! surfaceFileNameFullPath.isEmpty()) { + for (auto surface : allSurfaces) { + const AString name = surface->getFileName(); + const int32_t numMatch = name.countMatchingCharactersFromEnd(surfaceFileNameFullPath); + if (numMatch > pathNameMatchLength) { + pathNameMatchLength = numMatch; + pathNameMatchSurface = surface; + } + } + } + + /* + * Match name of file with NO path + * Always restore this so that the object is marked as restored + * (within the 'get' method). Otherwise if compiled debug, this + * object will get logged as 'not restored'. + */ + const AString surfaceFileName = sceneClass->getStringValue("m_selectedSurface", + ""); + if ( ! surfaceFileName.isEmpty()) { + for (auto surface : allSurfaces) { + if (surface->getFileNameNoPath() == surfaceFileName) { + nameMatchSurface = surface; + break; + } } } } -- GitLab From 60b12a68759812f650200b082c214c0c8ac143dd Mon Sep 17 00:00:00 2001 From: John Harwell Date: Mon, 13 May 2019 16:04:01 -0500 Subject: [PATCH 317/427] Fixing compilation errors on Linux systems from use of std::array initialization with another std::array. --- .../BrainOpenGLVolumeObliqueSliceDrawing.cxx | 18 +++++++++--------- src/Brain/BrainOpenGLVolumeSliceDrawing.cxx | 18 +++++++++--------- 2 files changed, 18 insertions(+), 18 deletions(-) diff --git a/src/Brain/BrainOpenGLVolumeObliqueSliceDrawing.cxx b/src/Brain/BrainOpenGLVolumeObliqueSliceDrawing.cxx index 3957270dd..096734901 100644 --- a/src/Brain/BrainOpenGLVolumeObliqueSliceDrawing.cxx +++ b/src/Brain/BrainOpenGLVolumeObliqueSliceDrawing.cxx @@ -1456,7 +1456,7 @@ BrainOpenGLVolumeObliqueSliceDrawing::drawAxesCrosshairsOblique(const VolumeSlic const float bigValue = 10000.0 + gapMM; - std::array horizontalAxisPosStartXYZ { sliceCoordinates }; + std::array horizontalAxisPosStartXYZ = sliceCoordinates; float trans[3]; m_browserTabContent->getTranslation(trans); @@ -1488,16 +1488,16 @@ BrainOpenGLVolumeObliqueSliceDrawing::drawAxesCrosshairsOblique(const VolumeSlic break; } - std::array horizontalAxisPosEndXYZ { horizontalAxisPosStartXYZ }; - std::array verticalAxisPosStartXYZ { horizontalAxisPosStartXYZ }; - std::array verticalAxisPosEndXYZ { horizontalAxisPosStartXYZ }; + std::array horizontalAxisPosEndXYZ = horizontalAxisPosStartXYZ; + std::array verticalAxisPosStartXYZ = horizontalAxisPosStartXYZ; + std::array verticalAxisPosEndXYZ = horizontalAxisPosStartXYZ; - std::array horizontalAxisNegStartXYZ { horizontalAxisPosStartXYZ }; - std::array horizontalAxisNegEndXYZ { horizontalAxisPosEndXYZ }; - std::array verticalAxisNegStartXYZ { verticalAxisPosStartXYZ }; - std::array verticalAxisNegEndXYZ { verticalAxisPosEndXYZ }; + std::array horizontalAxisNegStartXYZ = horizontalAxisPosStartXYZ; + std::array horizontalAxisNegEndXYZ = horizontalAxisPosEndXYZ; + std::array verticalAxisNegStartXYZ = verticalAxisPosStartXYZ; + std::array verticalAxisNegEndXYZ = verticalAxisPosEndXYZ; - std::array vertTrans { horizTrans }; + std::array vertTrans = horizTrans; float axialRGBA[4]; getAxesColor(VolumeSliceViewPlaneEnum::AXIAL, diff --git a/src/Brain/BrainOpenGLVolumeSliceDrawing.cxx b/src/Brain/BrainOpenGLVolumeSliceDrawing.cxx index 3162d02e2..1890c3bfe 100644 --- a/src/Brain/BrainOpenGLVolumeSliceDrawing.cxx +++ b/src/Brain/BrainOpenGLVolumeSliceDrawing.cxx @@ -3464,15 +3464,15 @@ BrainOpenGLVolumeSliceDrawing::drawAxesCrosshairsOrtho(const VolumeSliceViewPlan const float bigValue = 10000.0 + gapMM; - std::array horizontalAxisPosStartXYZ { sliceCoordinates }; - std::array horizontalAxisPosEndXYZ { sliceCoordinates }; - std::array verticalAxisPosStartXYZ { sliceCoordinates }; - std::array verticalAxisPosEndXYZ { sliceCoordinates }; - - std::array horizontalAxisNegStartXYZ { horizontalAxisPosStartXYZ }; - std::array horizontalAxisNegEndXYZ { horizontalAxisPosEndXYZ }; - std::array verticalAxisNegStartXYZ { verticalAxisPosStartXYZ }; - std::array verticalAxisNegEndXYZ { verticalAxisPosEndXYZ }; + std::array horizontalAxisPosStartXYZ = sliceCoordinates; + std::array horizontalAxisPosEndXYZ = sliceCoordinates; + std::array verticalAxisPosStartXYZ = sliceCoordinates; + std::array verticalAxisPosEndXYZ = sliceCoordinates; + + std::array horizontalAxisNegStartXYZ = horizontalAxisPosStartXYZ; + std::array horizontalAxisNegEndXYZ = horizontalAxisPosEndXYZ; + std::array verticalAxisNegStartXYZ = verticalAxisPosStartXYZ; + std::array verticalAxisNegEndXYZ = verticalAxisPosEndXYZ; float axialRGBA[4]; getAxesColor(VolumeSliceViewPlaneEnum::AXIAL, -- GitLab From fe4df3b911207fd18e522f434962a83212f79b0e Mon Sep 17 00:00:00 2001 From: John Harwell Date: Mon, 13 May 2019 16:52:49 -0500 Subject: [PATCH 318/427] WB-851 Cross-hair improvement: Default crosshair gap in preferences to 0.0 so that crosshairs do not have a gap by default. --- src/Common/CaretPreferences.cxx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Common/CaretPreferences.cxx b/src/Common/CaretPreferences.cxx index 65705380b..3bdfc4222 100644 --- a/src/Common/CaretPreferences.cxx +++ b/src/Common/CaretPreferences.cxx @@ -62,7 +62,7 @@ CaretPreferences::CaretPreferences() m_volumeCrossHairGapPreference.reset(new CaretPreferenceDataValue(this->qSettings, "volumeAxesCrosshairGap", CaretPreferenceDataValue::DataType::DOUBLE, - 2.0)); + 0.0)); m_preferenceDataValues.push_back(m_volumeCrossHairGapPreference.get()); m_colorsMode = BackgroundAndForegroundColorsModeEnum::USER_PREFERENCES; -- GitLab From cbf122da2ae3a3bbfa7a04a33540a7d2034b524d Mon Sep 17 00:00:00 2001 From: John Harwell Date: Tue, 14 May 2019 08:58:22 -0500 Subject: [PATCH 319/427] Add a few more items to OpenGL Information Text. --- src/Brain/BrainOpenGL.cxx | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/Brain/BrainOpenGL.cxx b/src/Brain/BrainOpenGL.cxx index 0dbd254fc..4b881bfa9 100644 --- a/src/Brain/BrainOpenGL.cxx +++ b/src/Brain/BrainOpenGL.cxx @@ -1130,6 +1130,16 @@ BrainOpenGL::getOpenGLInformation() lineInfo += ("\nSamples Count: " + AString::number(sampleCount)); lineInfo += ("\nMultisampling Enabled: " + AString::fromBool(glIsEnabled(GL_MULTISAMPLE))); + GLint textureMax, texture3DMax; + glGetIntegerv(GL_MAX_TEXTURE_SIZE, + &textureMax); + glGetIntegerv(GL_MAX_3D_TEXTURE_SIZE, + &texture3DMax); + lineInfo += "\n"; + lineInfo += ("\nTexture Max: " + AString::number(textureMax)); + lineInfo += ("\nTexture 3D Max: " + AString::number(texture3DMax)); + lineInfo += "\n"; + lineInfo += "\n"; lineInfo += "\n"; lineInfo += "Note that State of OpenGL may be different when drawing objects.\n"; -- GitLab From 2431c4770bc15bb13a308a0a7a0b462f13809e90 Mon Sep 17 00:00:00 2001 From: John Harwell Date: Tue, 14 May 2019 16:14:55 -0500 Subject: [PATCH 320/427] Improved usage of new CaretPreferenceDataValue for the crosshair gap preference so that user just sets/gets gap value from CaretPreferences. --- .../BrainOpenGLVolumeObliqueSliceDrawing.cxx | 4 +--- src/Brain/BrainOpenGLVolumeSliceDrawing.cxx | 4 +--- src/Common/CaretPreferenceDataValue.cxx | 20 ------------------- src/Common/CaretPreferenceDataValue.h | 19 ------------------ src/Common/CaretPreferences.cxx | 20 +++++++++++++++---- src/Common/CaretPreferences.h | 4 +++- .../BrainBrowserWindowToolBarSlicePlane.cxx | 16 ++------------- 7 files changed, 23 insertions(+), 64 deletions(-) diff --git a/src/Brain/BrainOpenGLVolumeObliqueSliceDrawing.cxx b/src/Brain/BrainOpenGLVolumeObliqueSliceDrawing.cxx index 096734901..084a56835 100644 --- a/src/Brain/BrainOpenGLVolumeObliqueSliceDrawing.cxx +++ b/src/Brain/BrainOpenGLVolumeObliqueSliceDrawing.cxx @@ -1443,9 +1443,7 @@ BrainOpenGLVolumeObliqueSliceDrawing::drawAxesCrosshairsOblique(const VolumeSlic const bool drawCrosshairsFlag, const bool drawCrosshairLabelsFlag) { - CaretPreferenceDataValue* gapPref = SessionManager::get()->getCaretPreferences()->getVolumeCrossHairGapPreference(); - CaretAssert(gapPref); - const float gapPercentViewportHeight = gapPref->getValue().toDouble(); + const float gapPercentViewportHeight = SessionManager::get()->getCaretPreferences()->getVolumeCrosshairGap(); const float gapMM = GraphicsUtilitiesOpenGL::convertPercentageOfViewportHeightToMillimeters(gapPercentViewportHeight); const std::array sliceCoordinates { sliceCoordinatesIn[0], sliceCoordinatesIn[1], sliceCoordinatesIn[2] }; diff --git a/src/Brain/BrainOpenGLVolumeSliceDrawing.cxx b/src/Brain/BrainOpenGLVolumeSliceDrawing.cxx index 1890c3bfe..c49860804 100644 --- a/src/Brain/BrainOpenGLVolumeSliceDrawing.cxx +++ b/src/Brain/BrainOpenGLVolumeSliceDrawing.cxx @@ -3450,9 +3450,7 @@ BrainOpenGLVolumeSliceDrawing::drawAxesCrosshairsOrtho(const VolumeSliceViewPlan const bool drawCrosshairsFlag, const bool drawCrosshairLabelsFlag) { - CaretPreferenceDataValue* gapPref = SessionManager::get()->getCaretPreferences()->getVolumeCrossHairGapPreference(); - CaretAssert(gapPref); - const float gapPercentViewportHeight = gapPref->getValue().toDouble(); + const float gapPercentViewportHeight = SessionManager::get()->getCaretPreferences()->getVolumeCrosshairGap(); const float gapMM = GraphicsUtilitiesOpenGL::convertPercentageOfViewportHeightToMillimeters(gapPercentViewportHeight); std::array sliceCoordinates { sliceCoordinatesIn[0], sliceCoordinatesIn[1], sliceCoordinatesIn[2] }; diff --git a/src/Common/CaretPreferenceDataValue.cxx b/src/Common/CaretPreferenceDataValue.cxx index 475fbc3f9..2611f982f 100644 --- a/src/Common/CaretPreferenceDataValue.cxx +++ b/src/Common/CaretPreferenceDataValue.cxx @@ -112,26 +112,6 @@ CaretPreferenceDataValue::getValue(/*const ValueType valueType*/) const valueOut = m_dataValue; } return valueOut; - -// switch (valueType) { -// case ValueType::ACTIVE: -// if (m_sceneDataValueValid) { -// valueOut = m_sceneDataValue; -// } -// else { -// valueOut = m_dataValue; -// } -// break; -// case ValueType::PREFERENCE: -// valueOut = m_dataValue; -// break; -// case ValueType::SCENE: // may not need scene value -// valueOut = m_sceneDataValue; -// break; -// } -// -// CaretAssert( ! valueOut.isNull()); -// return valueOut; } /** diff --git a/src/Common/CaretPreferenceDataValue.h b/src/Common/CaretPreferenceDataValue.h index 6e5eb6aae..2fecdd54d 100644 --- a/src/Common/CaretPreferenceDataValue.h +++ b/src/Common/CaretPreferenceDataValue.h @@ -42,25 +42,6 @@ namespace caret { DOUBLE, }; - /** - * Type of value returned by getValue() - */ - enum class ValueType { - /** - * Scene value if it is valid, otherwise preference value. - * Should be used when preferencd is applied. - */ - ACTIVE, - /** - * The preference value used by preferences dialog - */ - PREFERENCE, - /** - * The scene value (even if not valid) - */ - SCENE - }; - CaretPreferenceDataValue(QSettings* preferenceSettings, const QString& preferenceName, const DataType dataType, diff --git a/src/Common/CaretPreferences.cxx b/src/Common/CaretPreferences.cxx index 3bdfc4222..2d6e38cd6 100644 --- a/src/Common/CaretPreferences.cxx +++ b/src/Common/CaretPreferences.cxx @@ -1262,12 +1262,24 @@ CaretPreferences::setVolumeAxesCrosshairsDisplayed(const bool displayed) } /** - * @return Preference for the volume crosshair gap + * @return The crosshair gap */ -CaretPreferenceDataValue* -CaretPreferences::getVolumeCrossHairGapPreference() const +float +CaretPreferences::getVolumeCrosshairGap() const { - return m_volumeCrossHairGapPreference.get(); + return m_volumeCrossHairGapPreference->getValue().toFloat(); +} + +/** + * Set the volume crosshair gap + * + * @param gap + * New value for crosshair gap. + */ +void +CaretPreferences::setVolumeCrosshairGap(const float gap) +{ + return m_volumeCrossHairGapPreference->setValue(gap); } /** diff --git a/src/Common/CaretPreferences.h b/src/Common/CaretPreferences.h index 8da7c187d..07bf0e242 100644 --- a/src/Common/CaretPreferences.h +++ b/src/Common/CaretPreferences.h @@ -91,7 +91,9 @@ namespace caret { void setOpenGLDrawingMethod(const OpenGLDrawingMethodEnum::Enum openGLDrawingMethod); - CaretPreferenceDataValue* getVolumeCrossHairGapPreference() const; + float getVolumeCrosshairGap() const; + + void setVolumeCrosshairGap(const float gap); bool isVolumeAxesCrosshairsDisplayed() const; diff --git a/src/GuiQt/BrainBrowserWindowToolBarSlicePlane.cxx b/src/GuiQt/BrainBrowserWindowToolBarSlicePlane.cxx index 7462c17f4..762693d18 100644 --- a/src/GuiQt/BrainBrowserWindowToolBarSlicePlane.cxx +++ b/src/GuiQt/BrainBrowserWindowToolBarSlicePlane.cxx @@ -383,13 +383,7 @@ BrainBrowserWindowToolBarSlicePlane::createCrosshairMenu(const QString& objectNa void BrainBrowserWindowToolBarSlicePlane::crosshairMenuAboutToShow() { - CaretPreferences* prefs = SessionManager::get()->getCaretPreferences(); - CaretAssert(prefs); - CaretPreferenceDataValue* gapPref = prefs->getVolumeCrossHairGapPreference(); - CaretAssert(gapPref); - CaretAssert(gapPref->getDataType() == CaretPreferenceDataValue::DataType::DOUBLE); - const double gapValue = gapPref->getValue().toDouble(); - + const float gapValue = SessionManager::get()->getCaretPreferences()->getVolumeCrosshairGap(); QSignalBlocker blocker(m_crosshairGapSpinBox); m_crosshairGapSpinBox->setValue(gapValue); } @@ -403,13 +397,7 @@ BrainBrowserWindowToolBarSlicePlane::crosshairMenuAboutToShow() void BrainBrowserWindowToolBarSlicePlane::crosshairGapSpinBoxValueChanged(double value) { - CaretPreferences* prefs = SessionManager::get()->getCaretPreferences(); - CaretAssert(prefs); - CaretPreferenceDataValue* gapPref = prefs->getVolumeCrossHairGapPreference(); - CaretAssert(gapPref); - CaretAssert(gapPref->getDataType() == CaretPreferenceDataValue::DataType::DOUBLE); - gapPref->setValue(value); - + SessionManager::get()->getCaretPreferences()->setVolumeCrosshairGap(value); GuiManager::updateGraphicsAllWindows(); } -- GitLab From d453edc6253f18c0b8545cf5c60582364de5d2e2 Mon Sep 17 00:00:00 2001 From: John Harwell Date: Wed, 15 May 2019 09:28:07 -0500 Subject: [PATCH 321/427] Resolve WB-855: Change All Slice Plane Default to Row: The slice planes layout for new tabs is now set in Preferences --> New Tabs. The default layout remains GRID as it has always been. Users desiring ROW or COLUMN just need to change the Preference one time. Note that the All Slice Planes Layout is a 'per tab' property and affected by yoking. Thus, if new tabs are "Yoked to Group A", the All Slice Planes Layout will be that associated with "Tab A" and may be different than the Preference for All Slice Planes Layout. --- src/Brain/CMakeLists.txt | 2 - src/Brain/VolumeSliceSettings.cxx | 5 ++- src/Common/CMakeLists.txt | 2 + src/Common/CaretPreferenceDataValue.cxx | 24 +++++++++++- src/Common/CaretPreferenceDataValue.h | 12 +++++- src/Common/CaretPreferences.cxx | 37 ++++++++++++++++++- src/Common/CaretPreferences.h | 7 ++++ .../VolumeSliceViewAllPlanesLayoutEnum.cxx | 0 .../VolumeSliceViewAllPlanesLayoutEnum.h | 0 src/GuiQt/PreferencesDialog.cxx | 29 +++++++++++++-- src/GuiQt/PreferencesDialog.h | 2 + 11 files changed, 110 insertions(+), 10 deletions(-) rename src/{Brain => Common}/VolumeSliceViewAllPlanesLayoutEnum.cxx (100%) rename src/{Brain => Common}/VolumeSliceViewAllPlanesLayoutEnum.h (100%) diff --git a/src/Brain/CMakeLists.txt b/src/Brain/CMakeLists.txt index abad43509..87674524c 100644 --- a/src/Brain/CMakeLists.txt +++ b/src/Brain/CMakeLists.txt @@ -180,7 +180,6 @@ ViewingTransformationsVolume.h VolumeSliceDrawingTypeEnum.h VolumeSliceInterpolationEdgeEffectsMaskingEnum.h VolumeSliceSettings.h -VolumeSliceViewAllPlanesLayoutEnum.h VolumeSurfaceOutlineColorOrTabModel.h VolumeSurfaceOutlineModel.h VolumeSurfaceOutlineModelCacheKey.h @@ -340,7 +339,6 @@ ViewingTransformationsVolume.cxx VolumeSliceDrawingTypeEnum.cxx VolumeSliceInterpolationEdgeEffectsMaskingEnum.cxx VolumeSliceSettings.cxx -VolumeSliceViewAllPlanesLayoutEnum.cxx VolumeSurfaceOutlineColorOrTabModel.cxx VolumeSurfaceOutlineModel.cxx VolumeSurfaceOutlineModelCacheKey.cxx diff --git a/src/Brain/VolumeSliceSettings.cxx b/src/Brain/VolumeSliceSettings.cxx index a74ae6bb3..cd026c797 100644 --- a/src/Brain/VolumeSliceSettings.cxx +++ b/src/Brain/VolumeSliceSettings.cxx @@ -23,12 +23,14 @@ #include "VolumeSliceSettings.h" #undef __VOLUME_SLICE_SETTINGS_DECLARE__ +#include "CaretPreferences.h" #include "DeveloperFlagsEnum.h" #include "CaretLogger.h" #include "PlainTextStringBuilder.h" #include "SceneClass.h" #include "SceneClassAssistant.h" #include "SceneEnumeratedType.h" +#include "SessionManager.h" #include "VolumeFile.h" using namespace caret; @@ -49,8 +51,9 @@ const static VolumeSliceInterpolationEdgeEffectsMaskingEnum::Enum defaultVolumeS VolumeSliceSettings::VolumeSliceSettings() : CaretObject() { + CaretPreferences* prefs = SessionManager::get()->getCaretPreferences(); m_sliceViewPlane = VolumeSliceViewPlaneEnum::AXIAL; - m_slicePlanesAllViewLayout = VolumeSliceViewAllPlanesLayoutEnum::GRID_LAYOUT; + m_slicePlanesAllViewLayout = prefs->getVolumeAllSlicePlanesLayout(); m_sliceDrawingType = VolumeSliceDrawingTypeEnum::VOLUME_SLICE_DRAW_SINGLE; m_sliceProjectionType = VolumeSliceProjectionTypeEnum::VOLUME_SLICE_PROJECTION_ORTHOGONAL; m_volumeSliceInterpolationEdgeEffectsMaskingType = defaultVolumeSliceInterpolationEdgeMaskType; diff --git a/src/Common/CMakeLists.txt b/src/Common/CMakeLists.txt index 365279325..19662195b 100644 --- a/src/Common/CMakeLists.txt +++ b/src/Common/CMakeLists.txt @@ -160,6 +160,7 @@ TracksModificationInterface.h TriStateSelectionStatusEnum.h Vector3D.h VectorOperation.h +VolumeSliceViewAllPlanesLayoutEnum.h VoxelIJK.h WorkbenchSpecialVersionEnum.h WuQMacro.h @@ -279,6 +280,7 @@ TileTabsRowColumnStretchTypeEnum.cxx TriStateSelectionStatusEnum.cxx Vector3D.cxx VectorOperation.cxx +VolumeSliceViewAllPlanesLayoutEnum.cxx WorkbenchSpecialVersionEnum.cxx WuQMacro.cxx WuQMacroCommand.cxx diff --git a/src/Common/CaretPreferenceDataValue.cxx b/src/Common/CaretPreferenceDataValue.cxx index 2611f982f..57ba3e7ce 100644 --- a/src/Common/CaretPreferenceDataValue.cxx +++ b/src/Common/CaretPreferenceDataValue.cxx @@ -45,17 +45,21 @@ using namespace caret; * Name of the preference * @param dataType * Data type of the preference + * @param savedInScene + * Indicates if item is saved to scene and may override the preference when scene restored. * @param defaultValue * Default value for the preference */ CaretPreferenceDataValue::CaretPreferenceDataValue(QSettings* preferenceSettings, const QString& preferenceName, const DataType dataType, + const SavedInScene savedInScene, const QVariant defaultValue) : CaretObject(), m_preferenceSettings(preferenceSettings), m_preferenceName(preferenceName), -m_dataType(dataType) +m_dataType(dataType), +m_savedInScene(savedInScene) { CaretAssert(m_preferenceSettings); CaretAssert( ! m_preferenceName.isEmpty()); @@ -156,6 +160,15 @@ CaretPreferenceDataValue::setSceneValue(const QVariant& value) { m_sceneDataValue = value; m_sceneDataValueValid = true; + + switch (m_savedInScene) { + case SavedInScene::SAVE_NO: + /* do not allow scene value */ + m_sceneDataValueValid = false; + break; + case SavedInScene::SAVE_YES: + break; + } } /** @@ -168,5 +181,14 @@ void CaretPreferenceDataValue::setSceneValueValid(const bool validStatus) { m_sceneDataValueValid = validStatus; + + switch (m_savedInScene) { + case SavedInScene::SAVE_NO: + /* do not allow scene value */ + m_sceneDataValueValid = false; + break; + case SavedInScene::SAVE_YES: + break; + } } diff --git a/src/Common/CaretPreferenceDataValue.h b/src/Common/CaretPreferenceDataValue.h index 2fecdd54d..c7256e158 100644 --- a/src/Common/CaretPreferenceDataValue.h +++ b/src/Common/CaretPreferenceDataValue.h @@ -39,12 +39,20 @@ namespace caret { public: enum class DataType { - DOUBLE, + FLOAT, + INTEGER, + STRING + }; + + enum class SavedInScene { + SAVE_NO, + SAVE_YES }; CaretPreferenceDataValue(QSettings* preferenceSettings, const QString& preferenceName, const DataType dataType, + const SavedInScene savedInScene, const QVariant defaultValue); virtual ~CaretPreferenceDataValue(); @@ -76,6 +84,8 @@ namespace caret { const DataType m_dataType; + const SavedInScene m_savedInScene; + QVariant m_dataValue; QVariant m_sceneDataValue; diff --git a/src/Common/CaretPreferences.cxx b/src/Common/CaretPreferences.cxx index 2d6e38cd6..c51cdd10e 100644 --- a/src/Common/CaretPreferences.cxx +++ b/src/Common/CaretPreferences.cxx @@ -61,9 +61,18 @@ CaretPreferences::CaretPreferences() m_volumeCrossHairGapPreference.reset(new CaretPreferenceDataValue(this->qSettings, "volumeAxesCrosshairGap", - CaretPreferenceDataValue::DataType::DOUBLE, + CaretPreferenceDataValue::DataType::FLOAT, + CaretPreferenceDataValue::SavedInScene::SAVE_YES, 0.0)); m_preferenceDataValues.push_back(m_volumeCrossHairGapPreference.get()); + + const QString defAllSliceLayout = VolumeSliceViewAllPlanesLayoutEnum::toName(VolumeSliceViewAllPlanesLayoutEnum::GRID_LAYOUT); + m_volumeAllSlicePlanesLayout.reset(new CaretPreferenceDataValue(this->qSettings, + "volumeAllSlicePlanesLayout", + CaretPreferenceDataValue::DataType::STRING, + CaretPreferenceDataValue::SavedInScene::SAVE_NO, + defAllSliceLayout)); + m_preferenceDataValues.push_back(m_volumeAllSlicePlanesLayout.get()); m_colorsMode = BackgroundAndForegroundColorsModeEnum::USER_PREFERENCES; } @@ -1261,6 +1270,32 @@ CaretPreferences::setVolumeAxesCrosshairsDisplayed(const bool displayed) this->qSettings->sync(); } +/** + * @return The volume all slice planes layout + */ +VolumeSliceViewAllPlanesLayoutEnum::Enum +CaretPreferences::getVolumeAllSlicePlanesLayout() const +{ + QString stringValue(m_volumeAllSlicePlanesLayout->getValue().toString()); + bool validFlag(false); + VolumeSliceViewAllPlanesLayoutEnum::Enum enumValue = + VolumeSliceViewAllPlanesLayoutEnum::fromName(stringValue, &validFlag); + return enumValue; +} + +/** + * Set volume all slice planes layout + * + * @param allViewLayout + * The all slice planes layout + */ +void +CaretPreferences::setVolumeAllSlicePlanesLayout(const VolumeSliceViewAllPlanesLayoutEnum::Enum allViewLayout) +{ + const QString stringValue = VolumeSliceViewAllPlanesLayoutEnum::toName(allViewLayout); + m_volumeAllSlicePlanesLayout->setValue(stringValue); +} + /** * @return The crosshair gap */ diff --git a/src/Common/CaretPreferences.h b/src/Common/CaretPreferences.h index 07bf0e242..da9fc5eb3 100644 --- a/src/Common/CaretPreferences.h +++ b/src/Common/CaretPreferences.h @@ -31,6 +31,7 @@ #include "ImageCaptureMethodEnum.h" #include "OpenGLDrawingMethodEnum.h" #include "SpecFileDialogViewFilesTypeEnum.h" +#include "VolumeSliceViewAllPlanesLayoutEnum.h" class QSettings; class QStringList; @@ -91,6 +92,10 @@ namespace caret { void setOpenGLDrawingMethod(const OpenGLDrawingMethodEnum::Enum openGLDrawingMethod); + VolumeSliceViewAllPlanesLayoutEnum::Enum getVolumeAllSlicePlanesLayout() const; + + void setVolumeAllSlicePlanesLayout(const VolumeSliceViewAllPlanesLayoutEnum::Enum allViewLayout); + float getVolumeCrosshairGap() const; void setVolumeCrosshairGap(const float gap); @@ -294,6 +299,8 @@ namespace caret { int32_t volumeMontageCoordinatePrecision; + std::unique_ptr m_volumeAllSlicePlanesLayout; + std::unique_ptr m_volumeCrossHairGapPreference; std::vector m_preferenceDataValues; diff --git a/src/Brain/VolumeSliceViewAllPlanesLayoutEnum.cxx b/src/Common/VolumeSliceViewAllPlanesLayoutEnum.cxx similarity index 100% rename from src/Brain/VolumeSliceViewAllPlanesLayoutEnum.cxx rename to src/Common/VolumeSliceViewAllPlanesLayoutEnum.cxx diff --git a/src/Brain/VolumeSliceViewAllPlanesLayoutEnum.h b/src/Common/VolumeSliceViewAllPlanesLayoutEnum.h similarity index 100% rename from src/Brain/VolumeSliceViewAllPlanesLayoutEnum.h rename to src/Common/VolumeSliceViewAllPlanesLayoutEnum.h diff --git a/src/GuiQt/PreferencesDialog.cxx b/src/GuiQt/PreferencesDialog.cxx index aff20b762..d0843b656 100644 --- a/src/GuiQt/PreferencesDialog.cxx +++ b/src/GuiQt/PreferencesDialog.cxx @@ -635,10 +635,15 @@ PreferencesDialog::createTabDefaltsWidget() SLOT(volumeMontageCoordinatePrecisionChanged(int))); m_allWidgets->add(m_volumeMontageCoordinatePrecisionSpinBox); - m_allWidgets->add(m_volumeAxesCrosshairsComboBox); - m_allWidgets->add(m_volumeAxesLabelsComboBox); - m_allWidgets->add(m_volumeAxesMontageCoordinatesComboBox); - m_allWidgets->add(m_volumeMontageCoordinatePrecisionSpinBox); + /* + * All slice planes layout default + */ + m_volumeAllSlicePlanesLayoutComboBox = new EnumComboBoxTemplate(this); + m_volumeAllSlicePlanesLayoutComboBox->setup(); + QObject::connect(m_volumeAllSlicePlanesLayoutComboBox, SIGNAL(itemActivated()), + this, SLOT(m_volumeAllSlicePlanesLayoutItemActivated())); + m_allWidgets->add(m_volumeAllSlicePlanesLayoutComboBox->getWidget()); + QGridLayout* gridLayout = new QGridLayout(); @@ -670,6 +675,9 @@ PreferencesDialog::createTabDefaltsWidget() addWidgetToLayout(gridLayout, "Volume Montage Precision: ", m_volumeMontageCoordinatePrecisionSpinBox); + addWidgetToLayout(gridLayout, + "All Slice Planes Layout: ", + m_volumeAllSlicePlanesLayoutComboBox->getWidget()); QWidget* widget = new QWidget(); QVBoxLayout* layout = new QVBoxLayout(widget); @@ -692,6 +700,7 @@ PreferencesDialog::updateVolumeWidget(CaretPreferences* prefs) m_volumeAxesMontageCoordinatesComboBox->setStatus(prefs->isVolumeMontageAxesCoordinatesDisplayed()); m_volumeIdentificationComboBox->setStatus(prefs->isVolumeIdentificationDefaultedOn()); m_volumeMontageCoordinatePrecisionSpinBox->setValue(prefs->getVolumeMontageCoordinatePrecision()); + m_volumeAllSlicePlanesLayoutComboBox->setSelectedItem(prefs->getVolumeAllSlicePlanesLayout()); } /** @@ -982,6 +991,18 @@ PreferencesDialog::volumeAxesMontageCoordinatesComboBoxToggled(bool value) EventManager::get()->sendEvent(EventGraphicsUpdateAllWindows().getPointer()); } +/** + * Called when ALL view slice plane layout changed by user + */ +void +PreferencesDialog::m_volumeAllSlicePlanesLayoutItemActivated() +{ + VolumeSliceViewAllPlanesLayoutEnum::Enum layoutValue = m_volumeAllSlicePlanesLayoutComboBox->getSelectedItem(); + CaretPreferences* prefs = SessionManager::get()->getCaretPreferences(); + prefs->setVolumeAllSlicePlanesLayout(layoutValue); + EventManager::get()->sendEvent(EventGraphicsUpdateAllWindows().getPointer()); + +} /** * Called when volume montage coordinate precision value is changed. */ diff --git a/src/GuiQt/PreferencesDialog.h b/src/GuiQt/PreferencesDialog.h index 7bcac0dc6..aa9dc3245 100644 --- a/src/GuiQt/PreferencesDialog.h +++ b/src/GuiQt/PreferencesDialog.h @@ -70,6 +70,7 @@ namespace caret { void volumeAxesMontageCoordinatesComboBoxToggled(bool value); void volumeMontageCoordinatePrecisionChanged(int value); void volumeIdentificationComboBoxToggled(bool value); + void m_volumeAllSlicePlanesLayoutItemActivated(); void yokingComboBoxToggled(bool value); @@ -143,6 +144,7 @@ namespace caret { WuQTrueFalseComboBox* m_dynamicConnectivityComboBox; + EnumComboBoxTemplate* m_volumeAllSlicePlanesLayoutComboBox; WuQTrueFalseComboBox* m_volumeAxesCrosshairsComboBox; WuQTrueFalseComboBox* m_volumeAxesLabelsComboBox; WuQTrueFalseComboBox* m_volumeAxesMontageCoordinatesComboBox; -- GitLab From ffd0cab62ff3f80b3aa8ec094a3a5bee5b2abb2d Mon Sep 17 00:00:00 2001 From: John Harwell Date: Wed, 15 May 2019 10:02:41 -0500 Subject: [PATCH 322/427] Resolve WB-853 Converting annotation labels from one mesh to another: Old parameters (-source-surface and -target-surface) were removed. Users must use "-surface-pair" for source and target surfaces. --- .../AlgorithmAnnotationResample.cxx | 51 +++---------------- 1 file changed, 6 insertions(+), 45 deletions(-) diff --git a/src/Algorithms/AlgorithmAnnotationResample.cxx b/src/Algorithms/AlgorithmAnnotationResample.cxx index 441b5c59f..745eb4643 100644 --- a/src/Algorithms/AlgorithmAnnotationResample.cxx +++ b/src/Algorithms/AlgorithmAnnotationResample.cxx @@ -66,10 +66,7 @@ AlgorithmAnnotationResample::getParameters() { OperationParameters* ret = new OperationParameters(); - int32_t paramIndex(1); - - ret->addAnnotationParameter(paramIndex, "annotation-in", "the annotation file to resample"); - paramIndex++; + ret->addAnnotationParameter(1, "annotation-in", "the annotation file to resample"); /* * We need to preserve 'annotation groups' that may be created by the user. @@ -78,27 +75,14 @@ AlgorithmAnnotationResample::getParameters() * in 'surface space'; all others are preserved without modification. * So, we get a string for the output filename instead of an output annotation file */ - ret->addStringParameter(paramIndex, "annotation-out", "name of resampled annotation file"); - paramIndex++; + ret->addStringParameter(2, "annotation-out", "name of resampled annotation file"); - ParameterComponent* surfacePairOpt = ret->createRepeatableParameter(paramIndex, + ParameterComponent* surfacePairOpt = ret->createRepeatableParameter(3, "-surface-pair", "pair of surfaces for resampling surface annotations for one structure"); surfacePairOpt->addSurfaceParameter(1, "source-surface", "the midthickness surface of the current mesh the annotations use"); surfacePairOpt->addSurfaceParameter(2, "target-surface", "the midthickness surface of the mesh the annotations should be transferred to"); - paramIndex++; - - ParameterComponent* sourceSurfOpt = ret->createRepeatableParameter(paramIndex, - "-source-surface", - "surface with mesh used by input annotation file"); - sourceSurfOpt->addSurfaceParameter(1, "source-surface", "source surface file"); - paramIndex++; - ParameterComponent* targetSurfOpt = ret->createRepeatableParameter(paramIndex, - "-target-surface", - "surface with mesh for output annotation file"); - targetSurfOpt->addSurfaceParameter(1, "target-surface", "target surface file"); - paramIndex++; AString helpText = ("Resample an annotation file from the source mesh to the target mesh.\n\n" "Only annotations in surface space are modified, no changes are made to " @@ -127,39 +111,16 @@ void AlgorithmAnnotationResample::useParameters(OperationParameters* myParams, ProgressObject* myProgObj) { - int32_t paramIndex(1); - - AnnotationFile* annotIn = myParams->getAnnotation(paramIndex); - paramIndex++; + AnnotationFile* annotIn = myParams->getAnnotation(1); - AString outputFileName = myParams->getString(paramIndex); - paramIndex++; + AString outputFileName = myParams->getString(2); std::vector sourceSurfaces; std::vector targetSurfaces; - for (auto instance : *(myParams->getRepeatableParameterInstances(paramIndex))) { + for (auto instance : *(myParams->getRepeatableParameterInstances(3))) { sourceSurfaces.push_back(instance->getSurface(1)); targetSurfaces.push_back(instance->getSurface(2)); } - paramIndex++; - - bool haveDeprectedOptionsFlag(false); - for (auto instance : *(myParams->getRepeatableParameterInstances(paramIndex))) { - sourceSurfaces.push_back(instance->getSurface(1)); - haveDeprectedOptionsFlag = true; - } - paramIndex++; - - for (auto instance : *(myParams->getRepeatableParameterInstances(paramIndex))) { - targetSurfaces.push_back(instance->getSurface(1)); - haveDeprectedOptionsFlag = true; - } - paramIndex++; - - if (haveDeprectedOptionsFlag) { - std::cout << "WARNING: -source-surface and -target-surface options are deprecated." << std::endl; - std::cout << " Replace with -surface-pair " << std::endl; - } /* * Constructs and executes the algorithm -- GitLab From 4a9c729e1c678e3e8729925b50213801ca7f017d Mon Sep 17 00:00:00 2001 From: John Harwell Date: Thu, 16 May 2019 15:23:43 -0500 Subject: [PATCH 323/427] WB-855 Change All Slice Plane Default to Row: Slice order in ROW is not Parasagittal, Coronal, and Axial (consistent with other layouts). Preference default is now ROW. --- src/Brain/BrainOpenGLViewportContent.cxx | 4 ++-- src/Common/CaretPreferences.cxx | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Brain/BrainOpenGLViewportContent.cxx b/src/Brain/BrainOpenGLViewportContent.cxx index 11c870e5a..e26a2eb0b 100644 --- a/src/Brain/BrainOpenGLViewportContent.cxx +++ b/src/Brain/BrainOpenGLViewportContent.cxx @@ -1184,13 +1184,13 @@ BrainOpenGLViewportContent::getSliceAllViewViewport(const int32_t tabViewport[4] viewportOut[3] = vpHeight; break; case VolumeSliceViewPlaneEnum::CORONAL: - viewportOut[0] = tabViewportX; + viewportOut[0] = tabViewportX + vpOffsetX; viewportOut[1] = tabViewportY; viewportOut[2] = vpWidth; viewportOut[3] = vpHeight; break; case VolumeSliceViewPlaneEnum::PARASAGITTAL: - viewportOut[0] = tabViewportX + vpOffsetX; + viewportOut[0] = tabViewportX; viewportOut[1] = tabViewportY; viewportOut[2] = vpWidth; viewportOut[3] = vpHeight; diff --git a/src/Common/CaretPreferences.cxx b/src/Common/CaretPreferences.cxx index c51cdd10e..424d60d00 100644 --- a/src/Common/CaretPreferences.cxx +++ b/src/Common/CaretPreferences.cxx @@ -66,7 +66,7 @@ CaretPreferences::CaretPreferences() 0.0)); m_preferenceDataValues.push_back(m_volumeCrossHairGapPreference.get()); - const QString defAllSliceLayout = VolumeSliceViewAllPlanesLayoutEnum::toName(VolumeSliceViewAllPlanesLayoutEnum::GRID_LAYOUT); + const QString defAllSliceLayout = VolumeSliceViewAllPlanesLayoutEnum::toName(VolumeSliceViewAllPlanesLayoutEnum::ROW_LAYOUT); m_volumeAllSlicePlanesLayout.reset(new CaretPreferenceDataValue(this->qSettings, "volumeAllSlicePlanesLayout", CaretPreferenceDataValue::DataType::STRING, -- GitLab From 1a8c9c38d520dbd3464abdd246d7514191c3e217 Mon Sep 17 00:00:00 2001 From: John Harwell Date: Fri, 17 May 2019 10:22:39 -0500 Subject: [PATCH 324/427] WB-855 Change All Slice Plane Default to Row: Fix for older scenes prior to the addition of the All Slice Planes value. Theses older scenes need to restore to GRID and ignore the user's preference for the layout. --- src/Brain/SessionManager.cxx | 18 +++++++++++------- src/Brain/VolumeSliceSettings.cxx | 8 ++++++++ src/Common/CaretPreferenceDataValue.cxx | 20 ++++++++++++++++++++ src/Common/CaretPreferenceDataValue.h | 2 ++ 4 files changed, 41 insertions(+), 7 deletions(-) diff --git a/src/Brain/SessionManager.cxx b/src/Brain/SessionManager.cxx index 7ab23744b..1ec23b50a 100644 --- a/src/Brain/SessionManager.cxx +++ b/src/Brain/SessionManager.cxx @@ -1039,8 +1039,10 @@ SessionManager::savePreferencesToScene(const SceneAttributes* /*sceneAttributes* 1); std::vector sceneDataValues = m_caretPreferences->getPreferenceSceneDataValues(); for (auto scv : sceneDataValues) { - sceneClass->addString(scv->getName(), - scv->getPreferenceValue().toString()); + if (scv->isSavedToScenes()) { + sceneClass->addString(scv->getName(), + scv->getPreferenceValue().toString()); + } } return sceneClass; @@ -1070,11 +1072,13 @@ SessionManager::restorePreferencesFromScene(const SceneAttributes* /*sceneAttrib std::vector sceneDataValues = m_caretPreferences->getPreferenceSceneDataValues(); for (auto scv : sceneDataValues) { - const QString name = scv->getName(); - const QString value = sceneClass->getStringValue(scv->getName(), - invalidValueName); - if (value != invalidValueName) { - scv->setSceneValue(QVariant(value)); + if (scv->isSavedToScenes()) { + const QString name = scv->getName(); + const QString value = sceneClass->getStringValue(scv->getName(), + invalidValueName); + if (value != invalidValueName) { + scv->setSceneValue(QVariant(value)); + } } } } diff --git a/src/Brain/VolumeSliceSettings.cxx b/src/Brain/VolumeSliceSettings.cxx index cd026c797..e43e307c1 100644 --- a/src/Brain/VolumeSliceSettings.cxx +++ b/src/Brain/VolumeSliceSettings.cxx @@ -955,6 +955,14 @@ VolumeSliceSettings::restoreFromScene(const SceneAttributes* sceneAttributes, } } + /* + * For scenes before all view layout, use GRID so that the + * older scenes display correctly + */ + if (sceneClass->getObjectWithName("m_slicePlanesAllViewLayout") == NULL) { + m_slicePlanesAllViewLayout = VolumeSliceViewAllPlanesLayoutEnum::GRID_LAYOUT; + } + /* * Restoring scene initialize all members. * If this is not done, the slices will be reset diff --git a/src/Common/CaretPreferenceDataValue.cxx b/src/Common/CaretPreferenceDataValue.cxx index 57ba3e7ce..fc0728d4d 100644 --- a/src/Common/CaretPreferenceDataValue.cxx +++ b/src/Common/CaretPreferenceDataValue.cxx @@ -192,3 +192,23 @@ CaretPreferenceDataValue::setSceneValueValid(const bool validStatus) } } +/** + * @return True if preferences is saved to scenes + */ +bool +CaretPreferenceDataValue::isSavedToScenes() const +{ + bool savedFlag(false); + + switch (m_savedInScene) { + case SavedInScene::SAVE_NO: + break; + case SavedInScene::SAVE_YES: + savedFlag = true; + break; + } + + return savedFlag; +} + + diff --git a/src/Common/CaretPreferenceDataValue.h b/src/Common/CaretPreferenceDataValue.h index c7256e158..390c6ee31 100644 --- a/src/Common/CaretPreferenceDataValue.h +++ b/src/Common/CaretPreferenceDataValue.h @@ -75,6 +75,8 @@ namespace caret { void setSceneValueValid(const bool validStatus); + bool isSavedToScenes() const; + // ADD_NEW_METHODS_HERE private: -- GitLab From 67f8c6d72d56abe24ee8b02a6d28f8c1c2b68202 Mon Sep 17 00:00:00 2001 From: John Harwell Date: Thu, 16 May 2019 14:52:46 -0500 Subject: [PATCH 325/427] For testing purposes only, added an partial implementation of oblique slice drawing that uses a 3D Texture and offers better performance than the current oblique implementation. It must be enabled using an item in the Develop Menu. --- src/Brain/BrainOpenGLFixedPipeline.cxx | 29 +- src/Brain/BrainOpenGLFixedPipeline.h | 2 +- .../BrainOpenGLVolumeTextureSliceDrawing.cxx | 4389 +++++++++++++++++ .../BrainOpenGLVolumeTextureSliceDrawing.h | 332 ++ src/Brain/CMakeLists.txt | 2 + src/Common/DeveloperFlagsEnum.cxx | 5 + src/Common/DeveloperFlagsEnum.h | 3 +- 7 files changed, 4752 insertions(+), 10 deletions(-) create mode 100644 src/Brain/BrainOpenGLVolumeTextureSliceDrawing.cxx create mode 100644 src/Brain/BrainOpenGLVolumeTextureSliceDrawing.h diff --git a/src/Brain/BrainOpenGLFixedPipeline.cxx b/src/Brain/BrainOpenGLFixedPipeline.cxx index 6d6e1c3ff..fa6e32ff9 100644 --- a/src/Brain/BrainOpenGLFixedPipeline.cxx +++ b/src/Brain/BrainOpenGLFixedPipeline.cxx @@ -47,6 +47,7 @@ #include "BrainOpenGLPrimitiveDrawing.h" #include "BrainOpenGLVolumeObliqueSliceDrawing.h" #include "BrainOpenGLVolumeSliceDrawing.h" +#include "BrainOpenGLVolumeTextureSliceDrawing.h" #include "BrainOpenGLShapeCone.h" #include "BrainOpenGLShapeCube.h" #include "BrainOpenGLShapeCylinder.h" @@ -3835,14 +3836,26 @@ BrainOpenGLFixedPipeline::drawVolumeModel(BrowserTabContent* browserTabContent, viewport); } else { - BrainOpenGLVolumeObliqueSliceDrawing obliqueVolumeSliceDrawing; - obliqueVolumeSliceDrawing.draw(this, - browserTabContent, - volumeDrawInfo, - sliceDrawingType, - sliceProjectionType, - obliqueMaskType, - viewport); + if (DeveloperFlagsEnum::isFlag(DeveloperFlagsEnum::DEVELOPER_FLAG_TEXTURE_VOLUME)) { + BrainOpenGLVolumeTextureSliceDrawing textureSliceDrawing; + textureSliceDrawing.draw(this, + browserTabContent, + volumeDrawInfo, + sliceDrawingType, + sliceProjectionType, + obliqueMaskType, + viewport); + } + else { + BrainOpenGLVolumeObliqueSliceDrawing obliqueVolumeSliceDrawing; + obliqueVolumeSliceDrawing.draw(this, + browserTabContent, + volumeDrawInfo, + sliceDrawingType, + sliceProjectionType, + obliqueMaskType, + viewport); + } } glPopAttrib(); diff --git a/src/Brain/BrainOpenGLFixedPipeline.h b/src/Brain/BrainOpenGLFixedPipeline.h index d5639f4a1..172935495 100644 --- a/src/Brain/BrainOpenGLFixedPipeline.h +++ b/src/Brain/BrainOpenGLFixedPipeline.h @@ -624,7 +624,7 @@ namespace caret { friend class BrainOpenGLChartTwoDrawingFixedPipeline; friend class BrainOpenGLVolumeObliqueSliceDrawing; friend class BrainOpenGLVolumeSliceDrawing; - friend class OldBrainOpenGLVolumeSliceDrawing; + friend class BrainOpenGLVolumeTextureSliceDrawing; }; #ifdef __BRAIN_OPENGL_FIXED_PIPELINE_DEFINE_H diff --git a/src/Brain/BrainOpenGLVolumeTextureSliceDrawing.cxx b/src/Brain/BrainOpenGLVolumeTextureSliceDrawing.cxx new file mode 100644 index 000000000..f199ca30d --- /dev/null +++ b/src/Brain/BrainOpenGLVolumeTextureSliceDrawing.cxx @@ -0,0 +1,4389 @@ + +/*LICENSE_START*/ +/* + * Copyright (C) 2014 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + +#include + +#define __BRAIN_OPEN_GL_VOLUME_TEXTURE_SLICE_DRAWING_DECLARE__ +#include "BrainOpenGLVolumeTextureSliceDrawing.h" +#undef __BRAIN_OPEN_GL_VOLUME_TEXTURE_SLICE_DRAWING_DECLARE__ + +#include "AnnotationCoordinate.h" +#include "AnnotationPercentSizeText.h" +#include "BoundingBox.h" +#include "Brain.h" +#include "BrainOpenGLAnnotationDrawingFixedPipeline.h" +#include "BrainOpenGLPrimitiveDrawing.h" +#include "BrainOpenGLViewportContent.h" +#include "BrainOpenGLVolumeSliceDrawing.h" +#include "BrowserTabContent.h" +#include "CaretAssert.h" +#include "CaretLogger.h" +#include "CaretOpenGLInclude.h" +#include "CaretPreferenceDataValue.h" +#include "CaretPreferences.h" +#include "CiftiMappableDataFile.h" +#include "DeveloperFlagsEnum.h" +#include "DisplayPropertiesFoci.h" +#include "DisplayPropertiesLabels.h" +#include "DisplayPropertiesVolume.h" +#include "ElapsedTimer.h" +#include "FociFile.h" +#include "Focus.h" +#include "GapsAndMargins.h" +#include "GiftiLabel.h" +#include "GiftiLabelTable.h" +#include "GroupAndNameHierarchyModel.h" +#include "GraphicsEngineDataOpenGL.h" +#include "GraphicsPrimitiveV3fC4f.h" +#include "GraphicsPrimitiveV3fC4ub.h" +#include "GraphicsUtilitiesOpenGL.h" +#include "IdentificationWithColor.h" +#include "LabelDrawingProperties.h" +#include "MathFunctions.h" +#include "Matrix4x4.h" +#include "ModelVolume.h" +#include "ModelWholeBrain.h" +#include "NodeAndVoxelColoring.h" +#include "SelectionItemFocusVolume.h" +#include "SelectionItemVoxel.h" +#include "SelectionItemVoxelEditing.h" +#include "SelectionManager.h" +#include "SessionManager.h" +#include "SpacerTabIndex.h" +#include "Surface.h" +#include "SurfacePlaneIntersectionToContour.h" +#include "VolumeFile.h" +#include "VolumeSurfaceOutlineColorOrTabModel.h" +#include "VolumeSurfaceOutlineModel.h" +#include "VolumeSurfaceOutlineSetModel.h" + +using namespace caret; + +static const bool debugFlag = false; + +/** + * \class caret::BrainOpenGLVolumeTextureSliceDrawing + * \brief Draws volume slices using OpenGL + * \ingroup Brain + */ + +/** + * Constructor. + */ +BrainOpenGLVolumeTextureSliceDrawing::BrainOpenGLVolumeTextureSliceDrawing() +: CaretObject() +{ + +} + +/** + * Destructor. + */ +BrainOpenGLVolumeTextureSliceDrawing::~BrainOpenGLVolumeTextureSliceDrawing() +{ +} + +/** + * Draw Volume Slices or slices for ALL Stuctures View. + * + * @param fixedPipelineDrawing + * The OpenGL drawing. + * @param browserTabContent + * Content of browser tab that is to be drawn. + * @param volumeDrawInfo + * Info on each volume layers for drawing. + * @param sliceDrawingType + * Type of slice drawing (montage, single) + * @param sliceProjectionType + * Type of projection for the slice drawing (oblique, orthogonal) + * @param obliqueSliceMaskingType + * Masking for oblique slice drawing + * @param viewport + * The viewport (region of graphics area) for drawing slices. + */ +void +BrainOpenGLVolumeTextureSliceDrawing::draw(BrainOpenGLFixedPipeline* fixedPipelineDrawing, + BrowserTabContent* browserTabContent, + std::vector& volumeDrawInfo, + const VolumeSliceDrawingTypeEnum::Enum sliceDrawingType, + const VolumeSliceProjectionTypeEnum::Enum sliceProjectionType, + const VolumeSliceInterpolationEdgeEffectsMaskingEnum::Enum obliqueSliceMaskingType, + const int32_t viewport[4]) +{ + CaretAssert(sliceProjectionType == VolumeSliceProjectionTypeEnum::VOLUME_SLICE_PROJECTION_OBLIQUE); + + if (volumeDrawInfo.empty()) { + return; + } + + CaretAssert(fixedPipelineDrawing); + CaretAssert(browserTabContent); + m_browserTabContent = browserTabContent; + m_fixedPipelineDrawing = fixedPipelineDrawing; + m_obliqueSliceMaskingType = obliqueSliceMaskingType; + /* + * No lighting for drawing slices + */ + m_fixedPipelineDrawing->disableLighting(); + + /* + * Initialize class members which help reduce the number of + * parameters that are passed to methods. + */ + m_brain = NULL; + m_modelVolume = NULL; + m_modelWholeBrain = NULL; + m_modelType = ModelTypeEnum::MODEL_TYPE_INVALID; + if (m_browserTabContent->getDisplayedVolumeModel() != NULL) { + m_modelVolume = m_browserTabContent->getDisplayedVolumeModel(); + m_brain = m_modelVolume->getBrain(); + m_modelType = m_modelVolume->getModelType(); + } + else if (m_browserTabContent->getDisplayedWholeBrainModel() != NULL) { + m_modelWholeBrain = m_browserTabContent->getDisplayedWholeBrainModel(); + m_brain = m_modelWholeBrain->getBrain(); + m_modelType = m_modelWholeBrain->getModelType(); + } + else { + CaretAssertMessage(0, "Invalid model for volume slice drawing."); + } + CaretAssert(m_brain); + CaretAssert(m_modelType != ModelTypeEnum::MODEL_TYPE_INVALID); + + m_volumeDrawInfo = volumeDrawInfo; + if (m_volumeDrawInfo.empty()) { + return; + } + m_underlayVolume = m_volumeDrawInfo[0].volumeFile; + + const DisplayPropertiesLabels* dsl = m_brain->getDisplayPropertiesLabels(); + m_displayGroup = dsl->getDisplayGroupForTab(m_fixedPipelineDrawing->windowTabIndex); + + m_tabIndex = m_browserTabContent->getTabNumber(); + + /* + * Cifti files are slow at getting individual voxels since they + * provide no access to individual voxels. The reason is that + * the data may be on a server (Dense data) and accessing a single + * voxel would require requesting the entire map. So, for + * each Cifti file, get the enter map. This also, eliminate multiple + * requests for the same map when drawing an ALL view. + */ + const int32_t numVolumes = static_cast(m_volumeDrawInfo.size()); + for (int32_t i = 0; i < numVolumes; i++) { + std::vector ciftiMapData; + m_ciftiMappableFileData.push_back(ciftiMapData); + + const CiftiMappableDataFile* ciftiMapFile = dynamic_cast(m_volumeDrawInfo[i].volumeFile); + if (ciftiMapFile != NULL) { + ciftiMapFile->getMapData(m_volumeDrawInfo[i].mapIndex, + m_ciftiMappableFileData[i]); + } + } + + if (browserTabContent->getDisplayedVolumeModel() != NULL) { + drawVolumeSliceViewPlane(sliceDrawingType, + sliceProjectionType, + browserTabContent->getSliceViewPlane(), + browserTabContent->getSlicePlanesAllViewLayout(), + viewport); + } + else if (browserTabContent->getDisplayedWholeBrainModel() != NULL) { + drawVolumeSlicesForAllStructuresView(sliceProjectionType, + viewport); + } +} + +/** + * Draw volume view slices for the given view plane. + * + * @param sliceDrawingType + * Type of slice drawing (montage, single) + * @param sliceProjectionType + * Type of projection for the slice drawing (oblique, orthogonal) + * @param sliceViewPlane + * The plane for slice drawing. + * @param allPlanesLayout + * The layout in ALL slices view. + * @param viewport + * The viewport (region of graphics area) for drawing slices. + */ +void +BrainOpenGLVolumeTextureSliceDrawing::drawVolumeSliceViewPlane(const VolumeSliceDrawingTypeEnum::Enum sliceDrawingType, + const VolumeSliceProjectionTypeEnum::Enum sliceProjectionType, + const VolumeSliceViewPlaneEnum::Enum sliceViewPlane, + const VolumeSliceViewAllPlanesLayoutEnum::Enum allPlanesLayout, + const int32_t viewport[4]) +{ + switch (sliceViewPlane) { + case VolumeSliceViewPlaneEnum::ALL: + { + int32_t paraVP[4] = { 0, 0, 0, 0 }; + int32_t coronalVP[4] = { 0, 0, 0, 0 }; + int32_t axialVP[4] = { 0, 0, 0, 0 }; + + BrainOpenGLViewportContent::getSliceAllViewViewport(viewport, + VolumeSliceViewPlaneEnum::PARASAGITTAL, + allPlanesLayout, + paraVP); + BrainOpenGLViewportContent::getSliceAllViewViewport(viewport, + VolumeSliceViewPlaneEnum::CORONAL, + allPlanesLayout, + coronalVP); + BrainOpenGLViewportContent::getSliceAllViewViewport(viewport, + VolumeSliceViewPlaneEnum::AXIAL, + allPlanesLayout, + axialVP); + + /* + * Draw parasagittal slice + */ + glPushMatrix(); + drawVolumeSliceViewType(BrainOpenGLVolumeSliceDrawing::AllSliceViewMode::ALL_YES, + sliceDrawingType, + sliceProjectionType, + VolumeSliceViewPlaneEnum::PARASAGITTAL, + paraVP); + glPopMatrix(); + + + /* + * Draw coronal slice + */ + glPushMatrix(); + drawVolumeSliceViewType(BrainOpenGLVolumeSliceDrawing::AllSliceViewMode::ALL_YES, + sliceDrawingType, + sliceProjectionType, + VolumeSliceViewPlaneEnum::CORONAL, + coronalVP); + glPopMatrix(); + + + /* + * Draw axial slice + */ + glPushMatrix(); + drawVolumeSliceViewType(BrainOpenGLVolumeSliceDrawing::AllSliceViewMode::ALL_YES, + sliceDrawingType, + sliceProjectionType, + VolumeSliceViewPlaneEnum::AXIAL, + axialVP); + glPopMatrix(); + + if (allPlanesLayout == VolumeSliceViewAllPlanesLayoutEnum::GRID_LAYOUT) { + /* + * 4th quadrant is used for axis showing orientation + */ + int32_t allVP[4] = { 0, 0, 0, 0 }; + BrainOpenGLViewportContent::getSliceAllViewViewport(viewport, + VolumeSliceViewPlaneEnum::ALL, + allPlanesLayout, + allVP); + + switch (sliceProjectionType) { + case VolumeSliceProjectionTypeEnum::VOLUME_SLICE_PROJECTION_OBLIQUE: + drawOrientationAxes(allVP); + break; + case VolumeSliceProjectionTypeEnum::VOLUME_SLICE_PROJECTION_ORTHOGONAL: + CaretAssert(0); + break; + } + } + } + break; + case VolumeSliceViewPlaneEnum::AXIAL: + case VolumeSliceViewPlaneEnum::CORONAL: + case VolumeSliceViewPlaneEnum::PARASAGITTAL: + drawVolumeSliceViewType(BrainOpenGLVolumeSliceDrawing::AllSliceViewMode::ALL_NO, + sliceDrawingType, + sliceProjectionType, + sliceViewPlane, + viewport); + break; + } +} + +/** + * Draw slices for the all structures view. + * + * @param sliceProjectionType + * Type of projection for the slice drawing (oblique, orthogonal) + * @param viewport + * The viewport. + */ +void +BrainOpenGLVolumeTextureSliceDrawing::drawVolumeSlicesForAllStructuresView(const VolumeSliceProjectionTypeEnum::Enum sliceProjectionType, + const int32_t viewport[4]) +{ + m_orthographicBounds[0] = m_fixedPipelineDrawing->orthographicLeft; + m_orthographicBounds[1] = m_fixedPipelineDrawing->orthographicRight; + m_orthographicBounds[2] = m_fixedPipelineDrawing->orthographicBottom; + m_orthographicBounds[3] = m_fixedPipelineDrawing->orthographicTop; + m_orthographicBounds[4] = m_fixedPipelineDrawing->orthographicNear; + m_orthographicBounds[5] = m_fixedPipelineDrawing->orthographicFar; + + /* + * Enlarge the region + */ + { + const float left = m_fixedPipelineDrawing->orthographicLeft; + const float right = m_fixedPipelineDrawing->orthographicRight; + const float bottom = m_fixedPipelineDrawing->orthographicBottom; + const float top = m_fixedPipelineDrawing->orthographicTop; + + const float scale = 2.0; + + const float centerX = (left + right) / 2.0; + const float dx = (right - left) / 2.0; + const float newLeft = centerX - (dx * scale); + const float newRight = centerX + (dx * scale); + + const float centerY = (bottom + top) / 2.0; + const float dy = (top - bottom) / 2.0; + const float newBottom = centerY - (dy * scale); + const float newTop = centerY + (dy * scale); + + m_orthographicBounds[0] = newLeft; + m_orthographicBounds[1] = newRight; + m_orthographicBounds[2] = newBottom; + m_orthographicBounds[3] = newTop; + } + + const float sliceCoordinates[3] = { + m_browserTabContent->getSliceCoordinateParasagittal(), + m_browserTabContent->getSliceCoordinateCoronal(), + m_browserTabContent->getSliceCoordinateAxial() + }; + + if (m_browserTabContent->isSliceAxialEnabled()) { + glPushMatrix(); + drawVolumeSliceViewProjection(BrainOpenGLVolumeSliceDrawing::AllSliceViewMode::ALL_NO, + VolumeSliceDrawingTypeEnum::VOLUME_SLICE_DRAW_SINGLE, + sliceProjectionType, + VolumeSliceViewPlaneEnum::AXIAL, + sliceCoordinates, + viewport); + glPopMatrix(); + } + + if (m_browserTabContent->isSliceCoronalEnabled()) { + glPushMatrix(); + drawVolumeSliceViewProjection(BrainOpenGLVolumeSliceDrawing::AllSliceViewMode::ALL_NO, + VolumeSliceDrawingTypeEnum::VOLUME_SLICE_DRAW_SINGLE, + sliceProjectionType, + VolumeSliceViewPlaneEnum::CORONAL, + sliceCoordinates, + viewport); + glPopMatrix(); + } + + if (m_browserTabContent->isSliceParasagittalEnabled()) { + glPushMatrix(); + drawVolumeSliceViewProjection(BrainOpenGLVolumeSliceDrawing::AllSliceViewMode::ALL_NO, + VolumeSliceDrawingTypeEnum::VOLUME_SLICE_DRAW_SINGLE, + sliceProjectionType, + VolumeSliceViewPlaneEnum::PARASAGITTAL, + sliceCoordinates, + viewport); + glPopMatrix(); + } +} + +/** + * Draw single or montage volume view slices. + * + * @param allSliceViewMode + * Indicates drawing of ALL slices volume view (axial, coronal, parasagittal in one view) + * @param sliceDrawingType + * Type of slice drawing (montage, single) + * @param sliceProjectionType + * Type of projection for the slice drawing (oblique, orthogonal) + * @param sliceViewPlane + * The plane for slice drawing. + * @param viewport + * The viewport (region of graphics area) for drawing slices. + */ +void +BrainOpenGLVolumeTextureSliceDrawing::drawVolumeSliceViewType(const BrainOpenGLVolumeSliceDrawing::AllSliceViewMode allSliceViewMode, + const VolumeSliceDrawingTypeEnum::Enum sliceDrawingType, + const VolumeSliceProjectionTypeEnum::Enum sliceProjectionType, + const VolumeSliceViewPlaneEnum::Enum sliceViewPlane, + const int32_t viewport[4]) +{ + switch (sliceDrawingType) { + case VolumeSliceDrawingTypeEnum::VOLUME_SLICE_DRAW_MONTAGE: + drawVolumeSliceViewTypeMontage(allSliceViewMode, + sliceDrawingType, + sliceProjectionType, + sliceViewPlane, + viewport); + break; + case VolumeSliceDrawingTypeEnum::VOLUME_SLICE_DRAW_SINGLE: + { + const float sliceCoordinates[3] = { + m_browserTabContent->getSliceCoordinateParasagittal(), + m_browserTabContent->getSliceCoordinateCoronal(), + m_browserTabContent->getSliceCoordinateAxial() + }; + drawVolumeSliceViewProjection(allSliceViewMode, + sliceDrawingType, + sliceProjectionType, + sliceViewPlane, + sliceCoordinates, + viewport); + } + break; + } + +} + +/** + * Draw montage slices. + * + * @param allSliceViewMode + * Indicates drawing of ALL slices volume view (axial, coronal, parasagittal in one view) + * @param sliceDrawingType + * Type of slice drawing (montage, single) + * @param sliceProjectionType + * Type of projection for the slice drawing (oblique, orthogonal) + * @param sliceViewPlane + * The plane for slice drawing. + * @param viewport + * The viewport (region of graphics area) for drawing slices. + */ +void +BrainOpenGLVolumeTextureSliceDrawing::drawVolumeSliceViewTypeMontage(const BrainOpenGLVolumeSliceDrawing::AllSliceViewMode allSliceViewMode, + const VolumeSliceDrawingTypeEnum::Enum sliceDrawingType, + const VolumeSliceProjectionTypeEnum::Enum sliceProjectionType, + const VolumeSliceViewPlaneEnum::Enum sliceViewPlane, + const int32_t viewport[4]) +{ + const int32_t numRows = m_browserTabContent->getMontageNumberOfRows(); + CaretAssert(numRows > 0); + const int32_t numCols = m_browserTabContent->getMontageNumberOfColumns(); + CaretAssert(numCols > 0); + + const int32_t montageCoordPrecision = m_browserTabContent->getVolumeMontageCoordinatePrecision(); + + const GapsAndMargins* gapsAndMargins = m_brain->getGapsAndMargins(); + + const int32_t windowIndex = m_fixedPipelineDrawing->m_windowIndex; + + int32_t vpSizeY = 0; + int32_t verticalMargin = 0; + BrainOpenGLFixedPipeline::createSubViewportSizeAndGaps(viewport[3], + gapsAndMargins->getVolumeMontageVerticalGapForWindow(windowIndex), + -1, + numRows, + vpSizeY, + verticalMargin); + + int32_t vpSizeX = 0; + int32_t horizontalMargin = 0; + BrainOpenGLFixedPipeline::createSubViewportSizeAndGaps(viewport[2], + gapsAndMargins->getVolumeMontageHorizontalGapForWindow(windowIndex), + -1, + numCols, + vpSizeX, + horizontalMargin); + + /* + * Voxel sizes for underlay volume + */ + float originX, originY, originZ; + float x1, y1, z1; + m_underlayVolume->indexToSpace(0, 0, 0, originX, originY, originZ); + m_underlayVolume->indexToSpace(1, 1, 1, x1, y1, z1); + float sliceThickness = 0.0; + float sliceOrigin = 0.0; + + AString axisLetter = ""; + + float sliceCoordinates[3] = { + m_browserTabContent->getSliceCoordinateParasagittal(), + m_browserTabContent->getSliceCoordinateCoronal(), + m_browserTabContent->getSliceCoordinateAxial() + }; + + int32_t sliceIndex = -1; + int32_t maximumSliceIndex = -1; + int64_t dimI, dimJ, dimK, numMaps, numComponents; + m_underlayVolume->getDimensions(dimI, dimJ, dimK, numMaps, numComponents); + const int32_t sliceStep = m_browserTabContent->getMontageSliceSpacing(); + switch (sliceViewPlane) { + case VolumeSliceViewPlaneEnum::ALL: + sliceIndex = -1; + break; + case VolumeSliceViewPlaneEnum::AXIAL: + sliceIndex = m_browserTabContent->getSliceIndexAxial(m_underlayVolume); + maximumSliceIndex = dimK; + sliceThickness = z1 - originZ; + sliceOrigin = originZ; + axisLetter = "Z"; + break; + case VolumeSliceViewPlaneEnum::CORONAL: + sliceIndex = m_browserTabContent->getSliceIndexCoronal(m_underlayVolume); + maximumSliceIndex = dimJ; + sliceThickness = y1 - originY; + sliceOrigin = originY; + axisLetter = "Y"; + break; + case VolumeSliceViewPlaneEnum::PARASAGITTAL: + sliceIndex = m_browserTabContent->getSliceIndexParasagittal(m_underlayVolume); + maximumSliceIndex = dimI; + sliceThickness = x1 - originX; + sliceOrigin = originX; + axisLetter = "X"; + break; + } + + /* + * Foreground color for slice coordinate text + */ + const CaretPreferences* prefs = SessionManager::get()->getCaretPreferences(); + uint8_t foregroundRGBA[4]; + prefs->getBackgroundAndForegroundColors()->getColorForegroundVolumeView(foregroundRGBA); + foregroundRGBA[3] = 255; + uint8_t backgroundRGBA[4]; + prefs->getBackgroundAndForegroundColors()->getColorBackgroundVolumeView(backgroundRGBA); + backgroundRGBA[3] = 255; + const bool showCoordinates = m_browserTabContent->isVolumeMontageAxesCoordinatesDisplayed(); + + /* + * Determine a slice offset to selected slices is in + * the center of the montage + */ + const int32_t numSlicesViewed = (numCols * numRows); + const int32_t sliceOffset = ((numSlicesViewed / 2) + * sliceStep); + + sliceIndex += sliceOffset; + + /* + * Find first valid slice for montage + */ + while (sliceIndex >= 0) { + if (sliceIndex < maximumSliceIndex) { + break; + } + sliceIndex -= sliceStep; + } + + if (sliceIndex >= 0) { + for (int32_t i = 0; i < numRows; i++) { + for (int32_t j = 0; j < numCols; j++) { + if ((sliceIndex >= 0) + && (sliceIndex < maximumSliceIndex)) { + const int32_t vpX = (j * (vpSizeX + horizontalMargin)); + const int32_t vpY = ((numRows - i - 1) * (vpSizeY + verticalMargin)); + int32_t vp[4] = { + viewport[0] + vpX, + viewport[1] + vpY, + vpSizeX, + vpSizeY + }; + + if ((vp[2] <= 0) + || (vp[3] <= 0)) { + continue; + } + + const float sliceCoord = (sliceOrigin + + sliceThickness * sliceIndex); + switch (sliceViewPlane) { + case VolumeSliceViewPlaneEnum::ALL: + break; + case VolumeSliceViewPlaneEnum::AXIAL: + sliceCoordinates[2] = sliceCoord; + break; + case VolumeSliceViewPlaneEnum::CORONAL: + sliceCoordinates[1] = sliceCoord; + break; + case VolumeSliceViewPlaneEnum::PARASAGITTAL: + sliceCoordinates[0] = sliceCoord; + break; + } + + drawVolumeSliceViewProjection(allSliceViewMode, + sliceDrawingType, + sliceProjectionType, + sliceViewPlane, + sliceCoordinates, + vp); + + if (showCoordinates) { + const AString coordText = (axisLetter + + "=" + + AString::number(sliceCoord, 'f', montageCoordPrecision)); + + AnnotationPercentSizeText annotationText(AnnotationAttributesDefaultTypeEnum::NORMAL); + annotationText.setHorizontalAlignment(AnnotationTextAlignHorizontalEnum::RIGHT); + annotationText.setVerticalAlignment(AnnotationTextAlignVerticalEnum::BOTTOM); + annotationText.setFontPercentViewportSize(10.0f); + annotationText.setLineColor(CaretColorEnum::NONE); + annotationText.setTextColor(CaretColorEnum::CUSTOM); + annotationText.setBackgroundColor(CaretColorEnum::CUSTOM); + annotationText.setCustomTextColor(foregroundRGBA); + annotationText.setCustomBackgroundColor(backgroundRGBA); + annotationText.setText(coordText); + m_fixedPipelineDrawing->drawTextAtViewportCoords((vpSizeX - 5), + 5.0, + annotationText); + } + } + sliceIndex -= sliceStep; + } + } + } + + /* + * Draw the axes labels for the montage view + */ + glViewport(viewport[0], viewport[1], viewport[2], viewport[3]); + + if (m_browserTabContent->isVolumeAxesCrosshairLabelsDisplayed()) { + drawAxesCrosshairsOblique(sliceViewPlane, + sliceCoordinates, + false, + true); + } +} + +/** + * Draw a slice for either projection mode (oblique, orthogonal) + * + * @param allSliceViewMode + * Indicates drawing of ALL slices volume view (axial, coronal, parasagittal in one view) + * @param sliceDrawingType + * Type of slice drawing (montage, single) + * @param sliceProjectionType + * Type of projection for the slice drawing (oblique, orthogonal) + * @param sliceViewPlane + * The plane for slice drawing. + * @param sliceCoordinates + * Coordinates of the selected slice. + * @param viewport + * The viewport (region of graphics area) for drawing slices. + */ +void +BrainOpenGLVolumeTextureSliceDrawing::drawVolumeSliceViewProjection(const BrainOpenGLVolumeSliceDrawing::AllSliceViewMode allSliceViewMode, + const VolumeSliceDrawingTypeEnum::Enum sliceDrawingType, + const VolumeSliceProjectionTypeEnum::Enum sliceProjectionType, + const VolumeSliceViewPlaneEnum::Enum sliceViewPlane, + const float sliceCoordinates[3], + const int32_t viewport[4]) +{ + bool twoDimSliceViewFlag = false; + if (m_modelVolume != NULL) { + twoDimSliceViewFlag = true; + } + else if (m_modelWholeBrain != NULL) { + /* nothing */ + } + else { + CaretAssertMessage(0, "Invalid model type."); + } + + if (twoDimSliceViewFlag) { + glMatrixMode(GL_MODELVIEW); + glLoadIdentity(); + + glViewport(viewport[0], + viewport[1], + viewport[2], + viewport[3]); + + /* + * Set the orthographic projection to fit the slice axis + */ + setOrthographicProjection(allSliceViewMode, + sliceViewPlane, + viewport); + } + + /* + * Create the plane equation for the slice + */ + Plane slicePlane; + createSlicePlaneEquation(sliceProjectionType, + sliceViewPlane, + sliceCoordinates, + slicePlane); + CaretAssert(slicePlane.isValidPlane()); + if (slicePlane.isValidPlane() == false) { + return; + } + + + if (twoDimSliceViewFlag) { + /* + * Set the viewing transformation (camera position) + */ + setVolumeSliceViewingAndModelingTransformations(sliceProjectionType, + sliceViewPlane, + slicePlane, + sliceCoordinates); + } + + SelectionItemVoxel* voxelID = m_brain->getSelectionManager()->getVoxelIdentification(); + SelectionItemVoxelEditing* voxelEditingID = m_brain->getSelectionManager()->getVoxelEditingIdentification(); + + m_fixedPipelineDrawing->applyClippingPlanes(BrainOpenGLFixedPipeline::CLIPPING_DATA_TYPE_VOLUME, + StructureEnum::ALL); + + /* + * Check for a 'selection' type mode + */ + bool drawVolumeSlicesFlag = true; + m_identificationModeFlag = false; + switch (m_fixedPipelineDrawing->mode) { + case BrainOpenGLFixedPipeline::MODE_DRAWING: + break; + case BrainOpenGLFixedPipeline::MODE_IDENTIFICATION: + if (voxelID->isEnabledForSelection() + || voxelEditingID->isEnabledForSelection()) { + m_identificationModeFlag = true; + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + } + else { + /* + * Don't return. Allow other items (such as annotations) to be drawn. + */ + drawVolumeSlicesFlag = false; + } + break; + case BrainOpenGLFixedPipeline::MODE_PROJECTION: + return; + break; + } + + resetIdentification(); + + GLboolean cullFaceOn = glIsEnabled(GL_CULL_FACE); + + if (drawVolumeSlicesFlag) { + /* + * Disable culling so that both sides of the triangles/quads are drawn. + */ + glDisable(GL_CULL_FACE); + + switch (sliceProjectionType) { + case VolumeSliceProjectionTypeEnum::VOLUME_SLICE_PROJECTION_ORTHOGONAL: + CaretAssert(0); + break; + case VolumeSliceProjectionTypeEnum::VOLUME_SLICE_PROJECTION_OBLIQUE: + { + /* + * Create the oblique slice transformation matrix + */ + Matrix4x4 obliqueTransformationMatrix; + createObliqueTransformationMatrix(sliceCoordinates, + obliqueTransformationMatrix); + + drawObliqueSliceWithOutlines(sliceViewPlane, + obliqueTransformationMatrix); + } + break; + } + + /* + * Process selection + */ + if (m_identificationModeFlag) { + processIdentification(); + } + } + + if ( ! m_identificationModeFlag) { + if (slicePlane.isValidPlane()) { + drawLayers(sliceDrawingType, + sliceProjectionType, + sliceViewPlane, + slicePlane, + sliceCoordinates); + } + } + + /* + * Draw model space annotaitons on the volume slice + */ + float sliceThickness = 1.0; + if ( ! m_volumeDrawInfo.empty()) { + if (m_volumeDrawInfo[0].volumeFile != NULL) { + float spaceX = 0.0, spaceY = 0.0, spaceZ = 0.0; + m_volumeDrawInfo[0].volumeFile->getVoxelSpacing(spaceX, spaceY, spaceZ); + + switch (sliceViewPlane) { + case VolumeSliceViewPlaneEnum::ALL: + CaretAssert(0); + break; + case VolumeSliceViewPlaneEnum::AXIAL: + sliceThickness = spaceZ; + break; + case VolumeSliceViewPlaneEnum::CORONAL: + sliceThickness = spaceY; + break; + case VolumeSliceViewPlaneEnum::PARASAGITTAL: + sliceThickness = spaceX; + break; + } + } + } + const bool annotationModeFlag = (m_fixedPipelineDrawing->m_windowUserInputMode == UserInputModeEnum::ANNOTATIONS); + BrainOpenGLAnnotationDrawingFixedPipeline::Inputs inputs(this->m_brain, + m_fixedPipelineDrawing->mode, + BrainOpenGLFixedPipeline::s_gluLookAtCenterFromEyeOffsetDistance, + m_fixedPipelineDrawing->m_windowIndex, + m_fixedPipelineDrawing->windowTabIndex, + SpacerTabIndex(), + BrainOpenGLAnnotationDrawingFixedPipeline::Inputs::WINDOW_DRAWING_NO, + annotationModeFlag); + m_fixedPipelineDrawing->m_annotationDrawing->drawModelSpaceAnnotationsOnVolumeSlice(&inputs, + slicePlane, + sliceThickness); + + m_fixedPipelineDrawing->disableClippingPlanes(); + + + if (cullFaceOn) { + glEnable(GL_CULL_FACE); + } +} + +/** + * Create the equation for the slice plane + * + * @param sliceProjectionType + * Type of projection for the slice drawing (oblique, orthogonal) + * @param sliceViewPlane + * View plane that is displayed. + * @param montageSliceIndex + * Selected montage slice index + * @param planeOut + * OUTPUT plane of slice after transforms. + */ +void +BrainOpenGLVolumeTextureSliceDrawing::createSlicePlaneEquation(const VolumeSliceProjectionTypeEnum::Enum sliceProjectionType, + const VolumeSliceViewPlaneEnum::Enum sliceViewPlane, + const float sliceCoordinates[3], + Plane& planeOut) +{ + /* + * Default the slice normal vector to an orthogonal view + */ + float sliceNormalVector[3] = { 0.0, 0.0, 0.0 }; + switch (sliceViewPlane) { + case VolumeSliceViewPlaneEnum::ALL: + case VolumeSliceViewPlaneEnum::AXIAL: + sliceNormalVector[2] = 1.0; + break; + case VolumeSliceViewPlaneEnum::CORONAL: + sliceNormalVector[1] = -1.0; + break; + case VolumeSliceViewPlaneEnum::PARASAGITTAL: + sliceNormalVector[0] = -1.0; + break; + } + + switch (sliceProjectionType) { + break; + case VolumeSliceProjectionTypeEnum::VOLUME_SLICE_PROJECTION_OBLIQUE: + { + /* + * Transform the slice normal vector by the oblique rotation + * matrix so that the normal vector points out of the slice + */ + const Matrix4x4 obliqueRotationMatrix = m_browserTabContent->getObliqueVolumeRotationMatrix(); + obliqueRotationMatrix.multiplyPoint3(sliceNormalVector); + MathFunctions::normalizeVector(sliceNormalVector); + } + break; + case VolumeSliceProjectionTypeEnum::VOLUME_SLICE_PROJECTION_ORTHOGONAL: + CaretAssert(0); + break; + } + + Plane plane(sliceNormalVector, + sliceCoordinates); + planeOut = plane; + + m_lookAtCenter[0] = sliceCoordinates[0]; + m_lookAtCenter[1] = sliceCoordinates[1]; + m_lookAtCenter[2] = sliceCoordinates[2]; +} + +/** + * Set the volume slice viewing transformation. This sets the position and + * orientation of the camera. + * + * @param sliceProjectionType + * Type of projection for the slice drawing (oblique, orthogonal) + * @param sliceViewPlane + * View plane that is displayed. + * @param plane + * Plane equation of selected slice. + * @param sliceCoordinates + * Coordinates of the selected slices. + */ +void +BrainOpenGLVolumeTextureSliceDrawing::setVolumeSliceViewingAndModelingTransformations(const VolumeSliceProjectionTypeEnum::Enum sliceProjectionType, + const VolumeSliceViewPlaneEnum::Enum sliceViewPlane, + const Plane& plane, + const float sliceCoordinates[3]) +{ + /* + * Initialize the modelview matrix to the identity matrix + * This places the camera at the origin, pointing down the + * negative-Z axis with the up vector set to (0,1,0 => + * positive-Y is up). + */ + glMatrixMode(GL_MODELVIEW); + glLoadIdentity(); + + const float* userTranslation = m_browserTabContent->getTranslation(); + + /* + * Move the camera with the user's translation + */ + float viewTranslationX = 0.0; + float viewTranslationY = 0.0; + float viewTranslationZ = 0.0; + + switch (sliceViewPlane) { + case VolumeSliceViewPlaneEnum::ALL: + case VolumeSliceViewPlaneEnum::AXIAL: + viewTranslationX = sliceCoordinates[0] + userTranslation[0]; + viewTranslationY = sliceCoordinates[1] + userTranslation[1]; + break; + case VolumeSliceViewPlaneEnum::CORONAL: + viewTranslationX = sliceCoordinates[0] + userTranslation[0]; + viewTranslationY = sliceCoordinates[2] + userTranslation[2]; + break; + case VolumeSliceViewPlaneEnum::PARASAGITTAL: + viewTranslationX = -(sliceCoordinates[1] + userTranslation[1]); + viewTranslationY = sliceCoordinates[2] + userTranslation[2]; + break; + } + + glTranslatef(viewTranslationX, + viewTranslationY, + viewTranslationZ); + + + + + glGetDoublev(GL_MODELVIEW_MATRIX, + m_viewingMatrix); + + /* + * Since an orthographic projection is used, the camera only needs + * to be a little bit from the center along the plane's normal vector. + */ + double planeNormal[3]; + plane.getNormalVector(planeNormal); + double cameraXYZ[3] = { + m_lookAtCenter[0] + planeNormal[0] * BrainOpenGLFixedPipeline::s_gluLookAtCenterFromEyeOffsetDistance, + m_lookAtCenter[1] + planeNormal[1] * BrainOpenGLFixedPipeline::s_gluLookAtCenterFromEyeOffsetDistance, + m_lookAtCenter[2] + planeNormal[2] * BrainOpenGLFixedPipeline::s_gluLookAtCenterFromEyeOffsetDistance, + }; + + /* + * Set the up vector which indices which way is up (screen Y) + */ + float up[3] = { 0.0, 0.0, 0.0 }; + switch (sliceViewPlane) { + case VolumeSliceViewPlaneEnum::ALL: + case VolumeSliceViewPlaneEnum::AXIAL: + up[1] = 1.0; + break; + case VolumeSliceViewPlaneEnum::CORONAL: + up[2] = 1.0; + break; + case VolumeSliceViewPlaneEnum::PARASAGITTAL: + up[2] = 1.0; + break; + } + + /* + * For oblique viewing, the up vector needs to be rotated by the + * oblique rotation matrix. + */ + switch (sliceProjectionType) { + case VolumeSliceProjectionTypeEnum::VOLUME_SLICE_PROJECTION_OBLIQUE: + m_browserTabContent->getObliqueVolumeRotationMatrix().multiplyPoint3(up); + break; + case VolumeSliceProjectionTypeEnum::VOLUME_SLICE_PROJECTION_ORTHOGONAL: + CaretAssert(0); + break; + } + + /* + * Now set the camera to look at the selected coordinate (center) + * with the camera offset a little bit from the center. + * This allows the slice's voxels to be drawn in the actual coordinates. + */ + gluLookAt(cameraXYZ[0], cameraXYZ[1], cameraXYZ[2], + m_lookAtCenter[0], m_lookAtCenter[1], m_lookAtCenter[2], + up[0], up[1], up[2]); +} + +/** + * Draw the layers type data. + * + * @param sliceDrawingType + * Type of slice drawing (montage, single) + * @param sliceProjectionType + * Type of projection for the slice drawing (oblique, orthogonal) + * @param sliceViewPlane + * View plane that is displayed. + * @param slicePlane + * Plane of the slice. + * @param sliceCoordinates + * Coordinates of the selected slices. + */ +void +BrainOpenGLVolumeTextureSliceDrawing::drawLayers(const VolumeSliceDrawingTypeEnum::Enum sliceDrawingType, + const VolumeSliceProjectionTypeEnum::Enum sliceProjectionType, + const VolumeSliceViewPlaneEnum::Enum sliceViewPlane, + const Plane& slicePlane, + const float sliceCoordinates[3]) +{ + bool drawCrosshairsFlag = true; + bool drawFibersFlag = true; + bool drawFociFlag = true; + bool drawOutlineFlag = true; + + if (m_modelWholeBrain != NULL) { + drawCrosshairsFlag = false; + drawFibersFlag = false; + drawFociFlag = false; + } + + if ( ! m_identificationModeFlag) { + if (slicePlane.isValidPlane()) { + /* + * Disable culling so that both sides of the triangles/quads are drawn. + */ + GLboolean cullFaceOn = glIsEnabled(GL_CULL_FACE); + glDisable(GL_CULL_FACE); + + glPushMatrix(); + + GLboolean depthBufferEnabled = false; + glGetBooleanv(GL_DEPTH_TEST, + &depthBufferEnabled); + + /* + * Use some polygon offset that will adjust the depth values of the + * layers so that the layers depth values place the layers in front of + * the volume slice. + */ + glEnable(GL_POLYGON_OFFSET_FILL); + glPolygonOffset(0.0, 1.0); + + if (drawOutlineFlag) { + BrainOpenGLVolumeSliceDrawing::drawSurfaceOutline(m_underlayVolume, + m_modelType, + sliceProjectionType, + sliceViewPlane, + sliceCoordinates, + slicePlane, + m_browserTabContent->getVolumeSurfaceOutlineSet(), + m_fixedPipelineDrawing, + true); + } + + if (drawFibersFlag) { + glDisable(GL_DEPTH_TEST); + m_fixedPipelineDrawing->drawFiberOrientations(&slicePlane, + StructureEnum::ALL); + m_fixedPipelineDrawing->drawFiberTrajectories(&slicePlane, + StructureEnum::ALL); + if (depthBufferEnabled) { + glEnable(GL_DEPTH_TEST); + } + else { + glDisable(GL_DEPTH_TEST); + } + } + if (drawFociFlag) { + glDisable(GL_DEPTH_TEST); + drawVolumeSliceFoci(slicePlane); + if (depthBufferEnabled) { + glEnable(GL_DEPTH_TEST); + } + else { + glDisable(GL_DEPTH_TEST); + } + } + + glDisable(GL_POLYGON_OFFSET_FILL); + + if (drawCrosshairsFlag) { + glPushMatrix(); + drawAxesCrosshairs(sliceProjectionType, + sliceDrawingType, + sliceViewPlane, + sliceCoordinates); + glPopMatrix(); + if (depthBufferEnabled) { + glEnable(GL_DEPTH_TEST); + } + else { + glDisable(GL_DEPTH_TEST); + } + } + + glPopMatrix(); + + if (cullFaceOn) { + glEnable(GL_CULL_FACE); + } + } + } +} + +/** + * Draw foci on volume slice. + * + * @param plane + * Plane of the volume slice on which surface outlines are drawn. + */ +void +BrainOpenGLVolumeTextureSliceDrawing::drawVolumeSliceFoci(const Plane& plane) +{ + SelectionItemFocusVolume* idFocus = m_brain->getSelectionManager()->getVolumeFocusIdentification(); + + /* + * Check for a 'selection' type mode + */ + bool isSelect = false; + switch (m_fixedPipelineDrawing->mode) { + case BrainOpenGLFixedPipeline::MODE_DRAWING: + break; + case BrainOpenGLFixedPipeline::MODE_IDENTIFICATION: + if (idFocus->isEnabledForSelection()) { + isSelect = true; + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + } + else { + return; + } + break; + case BrainOpenGLFixedPipeline::MODE_PROJECTION: + return; + break; + } + + VolumeMappableInterface* underlayVolume = m_volumeDrawInfo[0].volumeFile; + float minVoxelSpacing; + float maxVoxelSpacing; + if ( ! getMinMaxVoxelSpacing(underlayVolume, minVoxelSpacing, maxVoxelSpacing)) { + return; + } + + const float sliceThickness = maxVoxelSpacing; + const float halfSliceThickness = sliceThickness * 0.5; + + + const DisplayPropertiesFoci* fociDisplayProperties = m_brain->getDisplayPropertiesFoci(); + const DisplayGroupEnum::Enum displayGroup = fociDisplayProperties->getDisplayGroupForTab(m_fixedPipelineDrawing->windowTabIndex); + + if (fociDisplayProperties->isDisplayed(displayGroup, + m_fixedPipelineDrawing->windowTabIndex) == false) { + return; + } + const float focusDiameter = fociDisplayProperties->getFociSize(displayGroup, + m_fixedPipelineDrawing->windowTabIndex); + const FeatureColoringTypeEnum::Enum fociColoringType = fociDisplayProperties->getColoringType(displayGroup, + m_fixedPipelineDrawing->windowTabIndex); + + const CaretColorEnum::Enum caretColor = fociDisplayProperties->getStandardColorType(displayGroup, + m_fixedPipelineDrawing->windowTabIndex); + float caretColorRGBA[4]; + CaretColorEnum::toRGBAFloat(caretColor, caretColorRGBA); + + bool drawAsSpheres = false; + switch (fociDisplayProperties->getDrawingType(displayGroup, + m_fixedPipelineDrawing->windowTabIndex)) { + case FociDrawingTypeEnum::DRAW_AS_SPHERES: + drawAsSpheres = true; + break; + case FociDrawingTypeEnum::DRAW_AS_SQUARES: + break; + } + + /* + * Process each foci file + */ + const int32_t numberOfFociFiles = m_brain->getNumberOfFociFiles(); + for (int32_t iFile = 0; iFile < numberOfFociFiles; iFile++) { + FociFile* fociFile = m_brain->getFociFile(iFile); + + const GroupAndNameHierarchyModel* classAndNameSelection = fociFile->getGroupAndNameHierarchyModel(); + if (classAndNameSelection->isSelected(displayGroup, + m_fixedPipelineDrawing->windowTabIndex) == false) { + continue; + } + + const GiftiLabelTable* classColorTable = fociFile->getClassColorTable(); + const GiftiLabelTable* nameColorTable = fociFile->getNameColorTable(); + + const int32_t numFoci = fociFile->getNumberOfFoci(); + + for (int32_t j = 0; j < numFoci; j++) { + Focus* focus = fociFile->getFocus(j); + + const GroupAndNameHierarchyItem* groupNameItem = focus->getGroupNameSelectionItem(); + if (groupNameItem != NULL) { + if (groupNameItem->isSelected(displayGroup, + m_fixedPipelineDrawing->windowTabIndex) == false) { + continue; + } + } + + float rgba[4] = { 0.0, 0.0, 0.0, 1.0 }; + switch (fociColoringType) { + case FeatureColoringTypeEnum::FEATURE_COLORING_TYPE_CLASS: + if (focus->isClassRgbaValid() == false) { + const GiftiLabel* colorLabel = classColorTable->getLabelBestMatching(focus->getClassName()); + if (colorLabel != NULL) { + colorLabel->getColor(rgba); + focus->setClassRgba(rgba); + } + else { + focus->setClassRgba(rgba); + } + } + focus->getClassRgba(rgba); + break; + case FeatureColoringTypeEnum::FEATURE_COLORING_TYPE_STANDARD_COLOR: + rgba[0] = caretColorRGBA[0]; + rgba[1] = caretColorRGBA[1]; + rgba[2] = caretColorRGBA[2]; + rgba[3] = caretColorRGBA[3]; + break; + case FeatureColoringTypeEnum::FEATURE_COLORING_TYPE_NAME: + if (focus->isNameRgbaValid() == false) { + const GiftiLabel* colorLabel = nameColorTable->getLabelBestMatching(focus->getName()); + if (colorLabel != NULL) { + colorLabel->getColor(rgba); + focus->setNameRgba(rgba); + } + else { + focus->setNameRgba(rgba); + } + } + focus->getNameRgba(rgba); + break; + } + + const int32_t numProjections = focus->getNumberOfProjections(); + for (int32_t k = 0; k < numProjections; k++) { + const SurfaceProjectedItem* spi = focus->getProjection(k); + if (spi->isVolumeXYZValid()) { + float xyz[3]; + spi->getVolumeXYZ(xyz); + + bool drawIt = false; + if (plane.absoluteDistanceToPlane(xyz) < halfSliceThickness) { + drawIt = true; + } + + if (drawIt) { + glPushMatrix(); + glTranslatef(xyz[0], xyz[1], xyz[2]); + if (isSelect) { + uint8_t idRGBA[4]; + m_fixedPipelineDrawing->colorIdentification->addItem(idRGBA, + SelectionItemDataTypeEnum::FOCUS_VOLUME, + iFile, // file index + j, // focus index + k);// projection index + idRGBA[3] = 255; + if (drawAsSpheres) { + m_fixedPipelineDrawing->drawSphereWithDiameter(idRGBA, + focusDiameter); + } + else { + glColor4ubv(idRGBA); + drawSquare(focusDiameter); + } + } + else { + if (drawAsSpheres) { + m_fixedPipelineDrawing->drawSphereWithDiameter(rgba, + focusDiameter); + } + else { + glColor3fv(rgba); + drawSquare(focusDiameter); + } + } + glPopMatrix(); + } + } + } + } + } + + if (isSelect) { + int32_t fociFileIndex = -1; + int32_t focusIndex = -1; + int32_t focusProjectionIndex = -1; + float depth = -1.0; + m_fixedPipelineDrawing->getIndexFromColorSelection(SelectionItemDataTypeEnum::FOCUS_VOLUME, + m_fixedPipelineDrawing->mouseX, + m_fixedPipelineDrawing->mouseY, + fociFileIndex, + focusIndex, + focusProjectionIndex, + depth); + if (fociFileIndex >= 0) { + if (idFocus->isOtherScreenDepthCloserToViewer(depth)) { + Focus* focus = m_brain->getFociFile(fociFileIndex)->getFocus(focusIndex); + idFocus->setBrain(m_brain); + idFocus->setFocus(focus); + idFocus->setFociFile(m_brain->getFociFile(fociFileIndex)); + idFocus->setFocusIndex(focusIndex); + idFocus->setFocusProjectionIndex(focusProjectionIndex); + idFocus->setVolumeFile(underlayVolume); + idFocus->setScreenDepth(depth); + float xyz[3]; + const SurfaceProjectedItem* spi = focus->getProjection(focusProjectionIndex); + spi->getVolumeXYZ(xyz); + m_fixedPipelineDrawing->setSelectedItemScreenXYZ(idFocus, xyz); + CaretLogFine("Selected Volume Focus Identification Symbol: " + QString::number(focusIndex)); + } + } + } +} + +/** + * Draw the axes crosshairs. + * + * @param sliceProjectionType + * Type of projection for the slice drawing (oblique, orthogonal) + * @param sliceDrawingType + * Type of slice drawing (montage, single) + * @param sliceViewPlane + * View plane that is displayed. + * @param sliceCoordinates + * Coordinates of the selected slices. + */ +void +BrainOpenGLVolumeTextureSliceDrawing::drawAxesCrosshairs(const VolumeSliceProjectionTypeEnum::Enum sliceProjectionType, + const VolumeSliceDrawingTypeEnum::Enum sliceDrawingType, + const VolumeSliceViewPlaneEnum::Enum sliceViewPlane, + const float sliceCoordinates[3]) +{ + const bool drawCrosshairsFlag = m_browserTabContent->isVolumeAxesCrosshairsDisplayed(); + bool drawCrosshairLabelsFlag = m_browserTabContent->isVolumeAxesCrosshairLabelsDisplayed(); + + switch (sliceDrawingType) { + case VolumeSliceDrawingTypeEnum::VOLUME_SLICE_DRAW_MONTAGE: + drawCrosshairLabelsFlag = false; + break; + case VolumeSliceDrawingTypeEnum::VOLUME_SLICE_DRAW_SINGLE: + break; + } + switch (sliceProjectionType) { + case VolumeSliceProjectionTypeEnum::VOLUME_SLICE_PROJECTION_ORTHOGONAL: + CaretAssert(0); + break; + case VolumeSliceProjectionTypeEnum::VOLUME_SLICE_PROJECTION_OBLIQUE: + { + glPushMatrix(); + glLoadIdentity(); + drawAxesCrosshairsOblique(sliceViewPlane, + sliceCoordinates, + drawCrosshairsFlag, + drawCrosshairLabelsFlag); + glPopMatrix(); + } + break; + } +} + +/** + * Draw the axes crosshairs for an orthogonal slice. + * + * @param sliceProjectionType + * Type of projection for the slice drawing (oblique, orthogonal) + * @param sliceViewPlane + * The slice plane view. + * @param sliceCoordinatesIn + * Coordinates of the selected slices. + * @param drawCrosshairsFlag + * If true, draw the crosshairs. + * @param drawCrosshairLabelsFlag + * If true, draw the crosshair labels. + */ +void +BrainOpenGLVolumeTextureSliceDrawing::drawAxesCrosshairsOblique(const VolumeSliceViewPlaneEnum::Enum sliceViewPlane, + const float sliceCoordinatesIn[3], + const bool drawCrosshairsFlag, + const bool drawCrosshairLabelsFlag) +{ + const float gapPercentViewportHeight = SessionManager::get()->getCaretPreferences()->getVolumeCrosshairGap(); + const float gapMM = GraphicsUtilitiesOpenGL::convertPercentageOfViewportHeightToMillimeters(gapPercentViewportHeight); + + const std::array sliceCoordinates { sliceCoordinatesIn[0], sliceCoordinatesIn[1], sliceCoordinatesIn[2] }; + GLboolean depthEnabled = GL_FALSE; + glGetBooleanv(GL_DEPTH_TEST, + &depthEnabled); + glDisable(GL_DEPTH_TEST); + + const float bigValue = 10000.0 + gapMM; + + std::array horizontalAxisPosStartXYZ = sliceCoordinates; + + float trans[3]; + m_browserTabContent->getTranslation(trans); + + std::array horizTrans { trans[0], trans[1], trans[2] }; + + switch (sliceViewPlane) { + case VolumeSliceViewPlaneEnum::ALL: + break; + case VolumeSliceViewPlaneEnum::AXIAL: + break; + case VolumeSliceViewPlaneEnum::CORONAL: + horizontalAxisPosStartXYZ[0] = sliceCoordinates[0]; + horizontalAxisPosStartXYZ[1] = sliceCoordinates[2]; + horizontalAxisPosStartXYZ[2] = sliceCoordinates[1]; + + horizTrans[0] = trans[0]; + horizTrans[1] = trans[2]; + horizTrans[2] = trans[1]; + break; + case VolumeSliceViewPlaneEnum::PARASAGITTAL: + horizontalAxisPosStartXYZ[0] = -sliceCoordinates[1]; + horizontalAxisPosStartXYZ[1] = sliceCoordinates[2]; + horizontalAxisPosStartXYZ[2] = sliceCoordinates[0]; + + horizTrans[0] = -trans[1]; + horizTrans[1] = trans[2]; + horizTrans[2] = trans[0]; + break; + } + + std::array horizontalAxisPosEndXYZ = horizontalAxisPosStartXYZ; + std::array verticalAxisPosStartXYZ = horizontalAxisPosStartXYZ; + std::array verticalAxisPosEndXYZ = horizontalAxisPosStartXYZ; + + std::array horizontalAxisNegStartXYZ = horizontalAxisPosStartXYZ; + std::array horizontalAxisNegEndXYZ = horizontalAxisPosEndXYZ; + std::array verticalAxisNegStartXYZ = verticalAxisPosStartXYZ; + std::array verticalAxisNegEndXYZ = verticalAxisPosEndXYZ; + + std::array vertTrans = horizTrans; + + float axialRGBA[4]; + getAxesColor(VolumeSliceViewPlaneEnum::AXIAL, + axialRGBA); + + float coronalRGBA[4]; + getAxesColor(VolumeSliceViewPlaneEnum::CORONAL, + coronalRGBA); + + float paraRGBA[4]; + getAxesColor(VolumeSliceViewPlaneEnum::PARASAGITTAL, + paraRGBA); + + AString horizontalLeftText = ""; + AString horizontalRightText = ""; + AString verticalBottomText = ""; + AString verticalTopText = ""; + + float* horizontalAxisRGBA = axialRGBA; + float* verticalAxisRGBA = axialRGBA; + + switch (sliceViewPlane) { + case VolumeSliceViewPlaneEnum::ALL: + break; + case VolumeSliceViewPlaneEnum::AXIAL: + horizontalLeftText = "L"; + horizontalRightText = "R"; + horizontalAxisRGBA = coronalRGBA; + horizontalAxisPosStartXYZ[0] += gapMM; + horizontalAxisPosEndXYZ[0] += bigValue; + horizontalAxisNegStartXYZ[0] -= gapMM; + horizontalAxisNegEndXYZ[0] -= bigValue; + + verticalBottomText = "P"; + verticalTopText = "A"; + verticalAxisRGBA = paraRGBA; + verticalAxisPosStartXYZ[1] += gapMM; + verticalAxisPosEndXYZ[1] += bigValue; + verticalAxisNegStartXYZ[1] -= gapMM; + verticalAxisNegEndXYZ[1] -= bigValue; + break; + case VolumeSliceViewPlaneEnum::CORONAL: + horizontalLeftText = "L"; + horizontalRightText = "R"; + horizontalAxisRGBA = axialRGBA; + horizontalAxisPosStartXYZ[0] += gapMM; + horizontalAxisPosEndXYZ[0] += bigValue; + horizontalAxisNegStartXYZ[0] -= gapMM; + horizontalAxisNegEndXYZ[0] -= bigValue; + + verticalBottomText = "I"; + verticalTopText = "S"; + verticalAxisRGBA = paraRGBA; + verticalAxisPosStartXYZ[1] += gapMM; + verticalAxisPosEndXYZ[1] += bigValue; + verticalAxisNegStartXYZ[1] -= gapMM; + verticalAxisNegEndXYZ[1] -= bigValue; + break; + case VolumeSliceViewPlaneEnum::PARASAGITTAL: + horizontalLeftText = "A"; + horizontalRightText = "P"; + horizontalAxisRGBA = axialRGBA; + horizontalAxisPosStartXYZ[0] += gapMM; + horizontalAxisPosEndXYZ[0] += bigValue; + horizontalAxisNegStartXYZ[0] -= gapMM; + horizontalAxisNegEndXYZ[0] -= bigValue; + + verticalBottomText = "I"; + verticalTopText = "S"; + verticalAxisRGBA = coronalRGBA; + verticalAxisPosStartXYZ[1] += gapMM; + verticalAxisPosEndXYZ[1] += bigValue; + verticalAxisNegStartXYZ[1] -= gapMM; + verticalAxisNegEndXYZ[1] -= bigValue; + break; + } + + /* + * Offset text labels be a percentage of viewort width/height + */ + GLint viewport[4]; + glGetIntegerv(GL_VIEWPORT, + viewport); + const int textOffsetX = viewport[2] * 0.01f; + const int textOffsetY = viewport[3] * 0.01f; + const int textLeftWindowXY[2] = { + textOffsetX, + (viewport[3] / 2) + }; + const int textRightWindowXY[2] = { + viewport[2] - textOffsetX, + (viewport[3] / 2) + }; + const int textBottomWindowXY[2] = { + viewport[2] / 2, + textOffsetY + }; + const int textTopWindowXY[2] = { + (viewport[2] / 2), + viewport[3] - textOffsetY + }; + + /* + * Crosshairs + */ + if (drawCrosshairsFlag) { + glPushMatrix(); + glTranslatef(horizTrans[0], horizTrans[1], horizTrans[2]); + std::unique_ptr horizHairPrimitive(GraphicsPrimitive::newPrimitiveV3fC4f(GraphicsPrimitive::PrimitiveType::POLYGONAL_LINES)); + horizHairPrimitive->addVertex(&horizontalAxisPosStartXYZ[0], horizontalAxisRGBA); + horizHairPrimitive->addVertex(&horizontalAxisPosEndXYZ[0], horizontalAxisRGBA); + horizHairPrimitive->addVertex(&horizontalAxisNegStartXYZ[0], horizontalAxisRGBA); + horizHairPrimitive->addVertex(&horizontalAxisNegEndXYZ[0], horizontalAxisRGBA); + horizHairPrimitive->setLineWidth(GraphicsPrimitive::LineWidthType::PIXELS, 2.0f); + GraphicsEngineDataOpenGL::draw(horizHairPrimitive.get()); + glPopMatrix(); + + glPushMatrix(); + glTranslatef(vertTrans[0], vertTrans[1], vertTrans[2]); + std::unique_ptr vertHairPrimitive(GraphicsPrimitive::newPrimitiveV3fC4f(GraphicsPrimitive::PrimitiveType::POLYGONAL_LINES)); + vertHairPrimitive->addVertex(&verticalAxisPosStartXYZ[0], verticalAxisRGBA); + vertHairPrimitive->addVertex(&verticalAxisPosEndXYZ[0], verticalAxisRGBA); + vertHairPrimitive->addVertex(&verticalAxisNegStartXYZ[0], verticalAxisRGBA); + vertHairPrimitive->addVertex(&verticalAxisNegEndXYZ[0], verticalAxisRGBA); + vertHairPrimitive->setLineWidth(GraphicsPrimitive::LineWidthType::PIXELS, 2.0f); + GraphicsEngineDataOpenGL::draw(vertHairPrimitive.get()); + glPopMatrix(); + } + + if (drawCrosshairLabelsFlag) { + const AnnotationTextFontPointSizeEnum::Enum fontSize = AnnotationTextFontPointSizeEnum::SIZE18; + + const int textCenter[2] = { + textLeftWindowXY[0], + textLeftWindowXY[1] + }; + const int halfFontSize = AnnotationTextFontPointSizeEnum::toSizeNumeric(fontSize) / 2; + + uint8_t backgroundRGBA[4] = { + m_fixedPipelineDrawing->m_backgroundColorByte[0], + m_fixedPipelineDrawing->m_backgroundColorByte[1], + m_fixedPipelineDrawing->m_backgroundColorByte[2], + m_fixedPipelineDrawing->m_backgroundColorByte[3] + }; + + GLint savedViewport[4]; + glGetIntegerv(GL_VIEWPORT, savedViewport); + + int vpLeftX = savedViewport[0] + textCenter[0] - halfFontSize; + int vpRightX = savedViewport[0] + textCenter[0] + halfFontSize; + int vpBottomY = savedViewport[1] + textCenter[1] - halfFontSize; + int vpTopY = savedViewport[1] + textCenter[1] + halfFontSize; + MathFunctions::limitRange(vpLeftX, + savedViewport[0], + savedViewport[0] + savedViewport[2]); + MathFunctions::limitRange(vpRightX, + savedViewport[0], + savedViewport[0] + savedViewport[2]); + MathFunctions::limitRange(vpBottomY, + savedViewport[1], + savedViewport[1] + savedViewport[3]); + MathFunctions::limitRange(vpTopY, + savedViewport[1], + savedViewport[1] + savedViewport[3]); + + const int vpSizeX = vpRightX - vpLeftX; + const int vpSizeY = vpTopY - vpBottomY; + glViewport(vpLeftX, vpBottomY, vpSizeX, vpSizeY); + + glMatrixMode(GL_PROJECTION); + glPushMatrix(); + glLoadIdentity(); + glOrtho(-1.0, 1.0, -1.0, 1.0, -1.0, 1.0); + + glMatrixMode(GL_MODELVIEW); + glPushMatrix(); + glLoadIdentity(); + + std::vector rgba; + std::vector coords, normals; + + coords.push_back(-1.0); + coords.push_back(-1.0); + coords.push_back( 0.0); + normals.push_back(0.0); + normals.push_back(0.0); + normals.push_back(1.0); + rgba.push_back(backgroundRGBA[0]); + rgba.push_back(backgroundRGBA[1]); + rgba.push_back(backgroundRGBA[2]); + rgba.push_back(backgroundRGBA[3]); + + coords.push_back( 1.0); + coords.push_back(-1.0); + coords.push_back( 0.0); + normals.push_back(0.0); + normals.push_back(0.0); + normals.push_back(1.0); + rgba.push_back(backgroundRGBA[0]); + rgba.push_back(backgroundRGBA[1]); + rgba.push_back(backgroundRGBA[2]); + rgba.push_back(backgroundRGBA[3]); + + coords.push_back( 1.0); + coords.push_back( 1.0); + coords.push_back( 0.0); + normals.push_back(0.0); + normals.push_back(0.0); + normals.push_back(1.0); + rgba.push_back(backgroundRGBA[0]); + rgba.push_back(backgroundRGBA[1]); + rgba.push_back(backgroundRGBA[2]); + rgba.push_back(backgroundRGBA[3]); + + coords.push_back(-1.0); + coords.push_back( 1.0); + coords.push_back( 0.0); + normals.push_back(0.0); + normals.push_back(0.0); + normals.push_back(1.0); + rgba.push_back(backgroundRGBA[0]); + rgba.push_back(backgroundRGBA[1]); + rgba.push_back(backgroundRGBA[2]); + rgba.push_back(backgroundRGBA[3]); + + + BrainOpenGLPrimitiveDrawing::drawQuads(coords, + normals, + rgba); + + glPopMatrix(); + + glMatrixMode(GL_PROJECTION); + glPopMatrix(); + glMatrixMode(GL_MODELVIEW); + + glViewport(savedViewport[0], + savedViewport[1], + savedViewport[2], + savedViewport[3]); + + AnnotationPercentSizeText annotationText(AnnotationAttributesDefaultTypeEnum::NORMAL); + annotationText.setBoldStyleEnabled(true); + annotationText.setFontPercentViewportSize(5.0f); + annotationText.setTextColor(CaretColorEnum::CUSTOM); + annotationText.setBackgroundColor(CaretColorEnum::CUSTOM); + annotationText.setCustomTextColor(horizontalAxisRGBA); + annotationText.setCustomBackgroundColor(backgroundRGBA); + + annotationText.setHorizontalAlignment(AnnotationTextAlignHorizontalEnum::LEFT); + annotationText.setVerticalAlignment(AnnotationTextAlignVerticalEnum::MIDDLE); + annotationText.setText(horizontalLeftText); + m_fixedPipelineDrawing->drawTextAtViewportCoords(textLeftWindowXY[0], + textLeftWindowXY[1], + annotationText); + + annotationText.setText(horizontalRightText); + annotationText.setHorizontalAlignment(AnnotationTextAlignHorizontalEnum::RIGHT); + annotationText.setVerticalAlignment(AnnotationTextAlignVerticalEnum::MIDDLE); + m_fixedPipelineDrawing->drawTextAtViewportCoords(textRightWindowXY[0], + textRightWindowXY[1], + annotationText); + + annotationText.setHorizontalAlignment(AnnotationTextAlignHorizontalEnum::CENTER); + annotationText.setVerticalAlignment(AnnotationTextAlignVerticalEnum::BOTTOM); + annotationText.setCustomTextColor(verticalAxisRGBA); + annotationText.setText(verticalBottomText); + m_fixedPipelineDrawing->drawTextAtViewportCoords(textBottomWindowXY[0], + textBottomWindowXY[1], + annotationText); + + annotationText.setText(verticalTopText); + annotationText.setHorizontalAlignment(AnnotationTextAlignHorizontalEnum::CENTER); + annotationText.setVerticalAlignment(AnnotationTextAlignVerticalEnum::TOP); + annotationText.getCoordinate()->setXYZ(textTopWindowXY[0], textTopWindowXY[1], 0.0); + m_fixedPipelineDrawing->drawTextAtViewportCoords(textTopWindowXY[0], + textTopWindowXY[1], + annotationText); + } + + if (depthEnabled) { + glEnable(GL_DEPTH_TEST); + } +} + +/** + * Get the RGBA coloring for a slice view plane. + * + * @param sliceViewPlane + * The slice view plane. + * @param rgbaOut + * Output colors ranging 0.0 to 1.0 + */ +void +BrainOpenGLVolumeTextureSliceDrawing::getAxesColor(const VolumeSliceViewPlaneEnum::Enum sliceViewPlane, + float rgbaOut[4]) const +{ + switch (sliceViewPlane) { + case VolumeSliceViewPlaneEnum::ALL: + CaretAssert(0); + break; + case VolumeSliceViewPlaneEnum::AXIAL: + rgbaOut[0] = 0.0; + rgbaOut[1] = 0.0; + rgbaOut[2] = 1.0; + rgbaOut[3] = 1.0; + break; + case VolumeSliceViewPlaneEnum::CORONAL: + rgbaOut[0] = 0.0; + rgbaOut[1] = 1.0; + rgbaOut[2] = 0.0; + rgbaOut[3] = 1.0; + break; + case VolumeSliceViewPlaneEnum::PARASAGITTAL: + rgbaOut[0] = 1.0; + rgbaOut[1] = 0.0; + rgbaOut[2] = 0.0; + rgbaOut[3] = 1.0; + break; + } +} + +/** + * Draw a one millimeter square facing the user. + * NOTE: This method will alter the current + * modelviewing matrices so caller may need + * to enclose the call to this method within + * glPushMatrix() and glPopMatrix(). + * + * @param size + * Size of square. + */ +void +BrainOpenGLVolumeTextureSliceDrawing::drawSquare(const float size) +{ + const float length = size * 0.5; + + /* + * Draw both front and back side since in some instances, + * such as surface montage, we are viweing from the far + * side (from back of monitor) + */ + glBegin(GL_QUADS); + glNormal3f(0.0, 0.0, 1.0); + glVertex3f(-length, -length, 0.0); + glVertex3f( length, -length, 0.0); + glVertex3f( length, length, 0.0); + glVertex3f(-length, length, 0.0); + glNormal3f(0.0, 0.0, -1.0); + glVertex3f(-length, -length, 0.0); + glVertex3f(-length, length, 0.0); + glVertex3f( length, length, 0.0); + glVertex3f( length, -length, 0.0); + glEnd(); +} + +/** + * Get the minimum and maximum distance between adjacent voxels in all + * slices planes. Output spacing value are always non-negative even if + * a right-to-left orientation. + * + * @param volume + * Volume for which min/max spacing is requested. + * @param minSpacingOut + * Output minimum spacing. + * @param maxSpacingOut + * Output maximum spacing. + * @return + * True if min and max spacing are greater than zero. + */ +bool +BrainOpenGLVolumeTextureSliceDrawing::getMinMaxVoxelSpacing(const VolumeMappableInterface* volume, + float& minSpacingOut, + float& maxSpacingOut) const +{ + CaretAssert(volume); + + float originX, originY, originZ; + float x1, y1, z1; + volume->indexToSpace(0, 0, 0, originX, originY, originZ); + volume->indexToSpace(1, 1, 1, x1, y1, z1); + const float dx = std::fabs(x1 - originX); + const float dy = std::fabs(y1 - originY); + const float dz = std::fabs(z1 - originZ); + + minSpacingOut = std::min(std::min(dx, dy), dz); + maxSpacingOut = std::max(std::max(dx, dy), dz); + + if ((minSpacingOut > 0.0) + && (maxSpacingOut > 0.0)) { + return true; + } + return false; +} + +/** + * Draw orientation axes + * + * @param viewport + * The viewport region for the orientation axes. + */ +void +BrainOpenGLVolumeTextureSliceDrawing::drawOrientationAxes(const int viewport[4]) +{ + const bool drawCylindersFlag = m_browserTabContent->isVolumeAxesCrosshairsDisplayed(); + const bool drawLabelsFlag = m_browserTabContent->isVolumeAxesCrosshairLabelsDisplayed(); + + /* + * Set the viewport + */ + glViewport(viewport[0], + viewport[1], + viewport[2], + viewport[3]); + const double viewportWidth = viewport[2]; + const double viewportHeight = viewport[3]; + + /* + * Determine bounds for orthographic projection + */ + const double maxCoord = 100.0; + const double minCoord = -maxCoord; + double left = 0.0; + double right = 0.0; + double top = 0.0; + double bottom = 0.0; + const double nearDepth = -1000.0; + const double farDepth = 1000.0; + if (viewportHeight > viewportWidth) { + left = minCoord; + right = maxCoord; + const double aspectRatio = (viewportHeight + / viewportWidth); + top = maxCoord * aspectRatio; + bottom = minCoord * aspectRatio; + } + else { + const double aspectRatio = (viewportWidth + / viewportHeight); + top = maxCoord; + bottom = minCoord; + left = minCoord * aspectRatio; + right = maxCoord * aspectRatio; + } + + /* + * Set the orthographic projection + */ + glMatrixMode(GL_PROJECTION); + glPushMatrix(); + glLoadIdentity(); + glOrtho(left, right, + bottom, top, + nearDepth, farDepth); + + + glMatrixMode(GL_MODELVIEW); + glPushMatrix(); + glLoadIdentity(); + { + /* + * Set the viewing transformation, places 'eye' so that it looks + * at the 'model' which is, in this case, the axes + */ + double eyeX = 0.0; + double eyeY = 0.0; + double eyeZ = BrainOpenGLFixedPipeline::s_gluLookAtCenterFromEyeOffsetDistance; //100.0; + const double centerX = 0; + const double centerY = 0; + const double centerZ = 0; + const double upX = 0; + const double upY = 1; + const double upZ = 0; + gluLookAt(eyeX, eyeY, eyeZ, + centerX, centerY, centerZ, + upX, upY, upZ); + + /* + * Set the modeling transformation + */ + const Matrix4x4 obliqueRotationMatrix = m_browserTabContent->getObliqueVolumeRotationMatrix(); + double rotationMatrix[16]; + obliqueRotationMatrix.getMatrixForOpenGL(rotationMatrix); + glMultMatrixd(rotationMatrix); + + /* + * Disable depth buffer. Otherwise, when volume slices are drawn + * black regions of the slices may set depth buffer and the occlude + * the axes from display. + */ + GLboolean depthBufferEnabled = false; + glGetBooleanv(GL_DEPTH_TEST, + &depthBufferEnabled); + glDisable(GL_DEPTH_TEST); + const float red[4] = { + 1.0, 0.0, 0.0, 1.0 + }; + const float green[4] = { + 0.0, 1.0, 0.0, 1.0 + }; + const float blue[4] = { + 0.0, 0.0, 1.0, 1.0 + }; + + const double axisMaxCoord = maxCoord * 0.8; + const double axisMinCoord = -axisMaxCoord; + const double textMaxCoord = maxCoord * 0.9; + const double textMinCoord = -textMaxCoord; + + + const float axialPlaneMin[3] = { 0.0, 0.0, (float)axisMinCoord }; + const float axialPlaneMax[3] = { 0.0, 0.0, (float)axisMaxCoord }; + const double axialTextMin[3] = { 0.0, 0.0, (float)textMinCoord }; + const double axialTextMax[3] = { 0.0, 0.0, (float)textMaxCoord }; + + const float coronalPlaneMin[3] = { (float)axisMinCoord, 0.0, 0.0 }; + const float coronalPlaneMax[3] = { (float)axisMaxCoord, 0.0, 0.0 }; + const double coronalTextMin[3] = { (float)textMinCoord, 0.0, 0.0 }; + const double coronalTextMax[3] = { (float)textMaxCoord, 0.0, 0.0 }; + + const float paraPlaneMin[3] = { 0.0, (float)axisMinCoord, 0.0 }; + const float paraPlaneMax[3] = { 0.0, (float)axisMaxCoord, 0.0 }; + const double paraTextMin[3] = { 0.0, (float)textMinCoord, 0.0 }; + const double paraTextMax[3] = { 0.0, (float)textMaxCoord, 0.0 }; + + /* + * Set radius as percentage of viewport height + */ + float axesCrosshairRadius = 1.0; + if (viewportHeight > 0) { + const float percentageRadius = 0.005f; + axesCrosshairRadius = percentageRadius * viewportHeight; + } + + if (drawCylindersFlag) { + m_fixedPipelineDrawing->drawCylinder(blue, + axialPlaneMin, + axialPlaneMax, + axesCrosshairRadius * 0.5f); + } + + AnnotationPercentSizeText annotationText(AnnotationAttributesDefaultTypeEnum::NORMAL); + annotationText.setHorizontalAlignment(AnnotationTextAlignHorizontalEnum::CENTER); + annotationText.setVerticalAlignment(AnnotationTextAlignVerticalEnum::MIDDLE); + annotationText.setFontPercentViewportSize(5.0f); + annotationText.setCoordinateSpace(AnnotationCoordinateSpaceEnum::STEREOTAXIC); + annotationText.setTextColor(CaretColorEnum::CUSTOM); + + if (drawLabelsFlag) { + annotationText.setCustomTextColor(blue); + annotationText.setText("I"); + m_fixedPipelineDrawing->drawTextAtModelCoords(axialTextMin, + annotationText); + annotationText.setText("S"); + m_fixedPipelineDrawing->drawTextAtModelCoords(axialTextMax, + annotationText); + } + + + if (drawCylindersFlag) { + m_fixedPipelineDrawing->drawCylinder(green, + coronalPlaneMin, + coronalPlaneMax, + axesCrosshairRadius * 0.5f); + } + + if (drawLabelsFlag) { + annotationText.setCustomTextColor(green); + annotationText.setText("L"); + m_fixedPipelineDrawing->drawTextAtModelCoords(coronalTextMin, + annotationText); + annotationText.setText("R"); + m_fixedPipelineDrawing->drawTextAtModelCoords(coronalTextMax, + annotationText); + } + + + if (drawCylindersFlag) { + m_fixedPipelineDrawing->drawCylinder(red, + paraPlaneMin, + paraPlaneMax, + axesCrosshairRadius * 0.5f); + } + + if (drawLabelsFlag) { + annotationText.setCustomTextColor(red); + annotationText.setText("P"); + m_fixedPipelineDrawing->drawTextAtModelCoords(paraTextMin, + annotationText); + + annotationText.setText("A"); + m_fixedPipelineDrawing->drawTextAtModelCoords(paraTextMax, + annotationText); + } + } + glPopMatrix(); + + glMatrixMode(GL_PROJECTION); + glPopMatrix(); + glMatrixMode(GL_MODELVIEW); + +} + +/** + * Set the orthographic projection. + * + * @param allSliceViewMode + * Indicates drawing of ALL slices volume view (axial, coronal, parasagittal in one view) + * @param sliceViewPlane + * View plane that is displayed. + * @param viewport + * The viewport. + */ +void +BrainOpenGLVolumeTextureSliceDrawing::setOrthographicProjection(const BrainOpenGLVolumeSliceDrawing::AllSliceViewMode allSliceViewMode, + const VolumeSliceViewPlaneEnum::Enum sliceViewPlane, + const int viewport[4]) +{ + const bool useOrthosDrawingProjectionFlag = false; /* does not work as expected when oblique */ + if (useOrthosDrawingProjectionFlag) { + /* + * Determine model size in screen Y when viewed + */ + BoundingBox boundingBox; + m_volumeDrawInfo[0].volumeFile->getVoxelSpaceBoundingBox(boundingBox); + + const double zoomFactor = m_browserTabContent->getScaling(); + BrainOpenGLVolumeSliceDrawing::setOrthographicProjection(allSliceViewMode, + sliceViewPlane, + boundingBox, + zoomFactor, + viewport, + m_orthographicBounds); + + return; + } + + /* + * Determine model size in screen Y when viewed + */ + BoundingBox boundingBox; + m_volumeDrawInfo[0].volumeFile->getVoxelSpaceBoundingBox(boundingBox); + + /* + * Set top and bottom to the min/max coordinate + * that runs vertically on the screen + */ + double modelTop = 200.0; + double modelBottom = -200.0; + switch (sliceViewPlane) { + case VolumeSliceViewPlaneEnum::ALL: + CaretAssertMessage(0, "Should never get here"); + break; + case VolumeSliceViewPlaneEnum::AXIAL: + modelTop = boundingBox.getMaxY(); + modelBottom = boundingBox.getMinY(); + break; + case VolumeSliceViewPlaneEnum::CORONAL: + modelTop = boundingBox.getMaxZ(); + modelBottom = boundingBox.getMinZ(); + break; + case VolumeSliceViewPlaneEnum::PARASAGITTAL: + modelTop = boundingBox.getMaxZ(); + modelBottom = boundingBox.getMinZ(); + break; + } + + switch (allSliceViewMode) { + case BrainOpenGLVolumeSliceDrawing::AllSliceViewMode::ALL_YES: + { + /* + * Parasagittal and Coronal Views have Brain's Z-axis in Screen Y + * Axial View has Brain's Y-axis in Screen Y + * So, use maximum of Brain's Y- and Z-axes for sizing height of slice + * so that voxels are same size for each slice in each axis view + */ + const float maxRangeYZ = std::max(boundingBox.getDifferenceY(), + boundingBox.getDifferenceZ()); + const float range = modelTop - modelBottom; + if (maxRangeYZ > range) { + const float diff = maxRangeYZ - range; + const float halfDiff = diff / 2.0; + modelTop += halfDiff; + modelBottom -= halfDiff; + } + } + break; + case BrainOpenGLVolumeSliceDrawing::AllSliceViewMode::ALL_NO: + break; + } + + /* + * Scale ratio makes region slightly larger than model + */ + const double zoom = m_browserTabContent->getScaling(); + double scaleRatio = (1.0 / 0.98); + if (zoom > 0.0) { + scaleRatio /= zoom; + } + modelTop *= scaleRatio; + modelBottom *= scaleRatio; + + /* + * Determine aspect ratio of viewport + */ + const double viewportWidth = viewport[2]; + const double viewportHeight = viewport[3]; + const double aspectRatio = (viewportWidth + / viewportHeight); + + /* + * Set bounds of orthographic projection + */ + const double halfModelY = ((modelTop - modelBottom) / 2.0); + const double orthoBottom = modelBottom; + const double orthoTop = modelTop; + const double orthoRight = halfModelY * aspectRatio; + const double orthoLeft = -halfModelY * aspectRatio; + const double nearDepth = -1000.0; + const double farDepth = 1000.0; + m_orthographicBounds[0] = orthoLeft; + m_orthographicBounds[1] = orthoRight; + m_orthographicBounds[2] = orthoBottom; + m_orthographicBounds[3] = orthoTop; + m_orthographicBounds[4] = nearDepth; + m_orthographicBounds[5] = farDepth; + + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); + glOrtho(m_orthographicBounds[0], + m_orthographicBounds[1], + m_orthographicBounds[2], + m_orthographicBounds[3], + m_orthographicBounds[4], + m_orthographicBounds[5]); + glMatrixMode(GL_MODELVIEW); +} + +/** + * Reset for volume identification. + * + * Clear identification indices and if identification is enabled, + * reserve a reasonable amount of space for the indices. + */ +void +BrainOpenGLVolumeTextureSliceDrawing::resetIdentification() +{ + m_identificationIndices.clear(); + + if (m_identificationModeFlag) { + int64_t estimatedNumberOfItems = 0; + + std::vector volumeDims; + m_volumeDrawInfo[0].volumeFile->getDimensions(volumeDims); + if (volumeDims.size() >= 3) { + const int64_t maxDim = std::max(volumeDims[0], + std::max(volumeDims[1], volumeDims[2])); + estimatedNumberOfItems = maxDim * maxDim * IDENTIFICATION_INDICES_PER_VOXEL; + } + + m_identificationIndices.reserve(estimatedNumberOfItems); + } +} + +/** + * Add a voxel to the identification indices. + * + * @param volumeIndex + * Index of the volume. + * @param mapIndex + * Index of the volume map. + * @param voxelI + * Voxel Index I + * @param voxelJ + * Voxel Index J + * @param voxelK + * Voxel Index K + * @param voxelDiffXYZ + * Change in XYZ from voxel bottom left to top right + * @param rgbaForColorIdentificationOut + * Encoded identification in RGBA color OUTPUT + */ +void +BrainOpenGLVolumeTextureSliceDrawing::addVoxelToIdentification(const int32_t volumeIndex, + const int32_t mapIndex, + const int32_t voxelI, + const int32_t voxelJ, + const int32_t voxelK, + const float voxelDiffXYZ[3], + uint8_t rgbaForColorIdentificationOut[4]) +{ + const int32_t idIndex = m_identificationIndices.size() / IDENTIFICATION_INDICES_PER_VOXEL; + + m_fixedPipelineDrawing->colorIdentification->addItem(rgbaForColorIdentificationOut, + SelectionItemDataTypeEnum::VOXEL, + idIndex); + rgbaForColorIdentificationOut[3] = 255; + + /* + * ID stack requires integers to + * use an integer pointer to the float values + */ + CaretAssert(sizeof(float) == sizeof(int32_t)); + const int32_t* intPointerDiffXYZ = (int32_t*)voxelDiffXYZ; + + /* + * If these items change, need to update reset and + * processing of identification. + */ + m_identificationIndices.push_back(volumeIndex); + m_identificationIndices.push_back(mapIndex); + m_identificationIndices.push_back(voxelI); + m_identificationIndices.push_back(voxelJ); + m_identificationIndices.push_back(voxelK); + m_identificationIndices.push_back(intPointerDiffXYZ[0]); + m_identificationIndices.push_back(intPointerDiffXYZ[1]); + m_identificationIndices.push_back(intPointerDiffXYZ[2]); +} + +/** + * Process voxel identification. + */ +void +BrainOpenGLVolumeTextureSliceDrawing::processIdentification() +{ + int32_t identifiedItemIndex; + float depth = -1.0; + m_fixedPipelineDrawing->getIndexFromColorSelection(SelectionItemDataTypeEnum::VOXEL, + m_fixedPipelineDrawing->mouseX, + m_fixedPipelineDrawing->mouseY, + identifiedItemIndex, + depth); + if (identifiedItemIndex >= 0) { + const int32_t idIndex = identifiedItemIndex * IDENTIFICATION_INDICES_PER_VOXEL; + const int32_t volDrawInfoIndex = m_identificationIndices[idIndex]; + CaretAssertVectorIndex(m_volumeDrawInfo, volDrawInfoIndex); + VolumeMappableInterface* vf = m_volumeDrawInfo[volDrawInfoIndex].volumeFile; + const int64_t voxelIndices[3] = { + m_identificationIndices[idIndex + 2], + m_identificationIndices[idIndex + 3], + m_identificationIndices[idIndex + 4] + }; + + const float* floatDiffXYZ = (float*)&m_identificationIndices[idIndex + 5]; + + SelectionItemVoxel* voxelID = m_brain->getSelectionManager()->getVoxelIdentification(); + if (voxelID->isEnabledForSelection()) { + if (voxelID->isOtherScreenDepthCloserToViewer(depth)) { + voxelID->setVoxelIdentification(m_brain, + vf, + voxelIndices, + depth); + + float voxelCoordinates[3]; + vf->indexToSpace(voxelIndices[0], voxelIndices[1], voxelIndices[2], + voxelCoordinates[0], voxelCoordinates[1], voxelCoordinates[2]); + + m_fixedPipelineDrawing->setSelectedItemScreenXYZ(voxelID, + voxelCoordinates); + CaretLogFinest("Selected Voxel (3D): " + AString::fromNumbers(voxelIndices, 3, ",")); + } + } + + SelectionItemVoxelEditing* voxelEditID = m_brain->getSelectionManager()->getVoxelEditingIdentification(); + if (voxelEditID->isEnabledForSelection()) { + if (voxelEditID->getVolumeFileForEditing() == vf) { + if (voxelEditID->isOtherScreenDepthCloserToViewer(depth)) { + voxelEditID->setVoxelIdentification(m_brain, + vf, + voxelIndices, + depth); + voxelEditID->setVoxelDiffXYZ(floatDiffXYZ); + + float voxelCoordinates[3]; + vf->indexToSpace(voxelIndices[0], voxelIndices[1], voxelIndices[2], + voxelCoordinates[0], voxelCoordinates[1], voxelCoordinates[2]); + + m_fixedPipelineDrawing->setSelectedItemScreenXYZ(voxelEditID, + voxelCoordinates); + CaretLogFinest("Selected Voxel Editing (3D): Indices (" + + AString::fromNumbers(voxelIndices, 3, ",") + + ") Diff XYZ (" + + AString::fromNumbers(floatDiffXYZ, 3, ",") + + ")"); + } + } + } + } +} + +/** + * Get the maximum bounds that enclose the volumes and the minimum + * voxel spacing from the volumes. + * + * @param boundsOut + * Bounds of the volumes. + * @param spacingOut + * Minimum voxel spacing from the volumes. Always positive values (even if + * volumes is oriented right to left). + * + */ +bool +BrainOpenGLVolumeTextureSliceDrawing::getVoxelCoordinateBoundsAndSpacing(float boundsOut[6], + float spacingOut[3]) +{ + const int32_t numberOfVolumesToDraw = static_cast(m_volumeDrawInfo.size()); + if (numberOfVolumesToDraw <= 0) { + return false; + } + + /* + * Find maximum extent of all voxels and smallest voxel + * size in each dimension. + */ + float minVoxelX = std::numeric_limits::max(); + float maxVoxelX = -std::numeric_limits::max(); + float minVoxelY = std::numeric_limits::max(); + float maxVoxelY = -std::numeric_limits::max(); + float minVoxelZ = std::numeric_limits::max(); + float maxVoxelZ = -std::numeric_limits::max(); + float voxelStepX = std::numeric_limits::max(); + float voxelStepY = std::numeric_limits::max(); + float voxelStepZ = std::numeric_limits::max(); + for (int32_t i = 0; i < numberOfVolumesToDraw; i++) { + const VolumeMappableInterface* volumeFile = m_volumeDrawInfo[i].volumeFile; + int64_t dimI, dimJ, dimK, numMaps, numComponents; + volumeFile->getDimensions(dimI, dimJ, dimK, numMaps, numComponents); + + float originX, originY, originZ; + float x1, y1, z1; + float lastX, lastY, lastZ; + volumeFile->indexToSpace(0, 0, 0, originX, originY, originZ); + volumeFile->indexToSpace(1, 1, 1, x1, y1, z1); + volumeFile->indexToSpace(dimI - 1, dimJ - 1, dimK - 1, lastX, lastY, lastZ); + const float dx = x1 - originX; + const float dy = y1 - originY; + const float dz = z1 - originZ; + voxelStepX = std::min(voxelStepX, std::fabs(dx)); + voxelStepY = std::min(voxelStepY, std::fabs(dy)); + voxelStepZ = std::min(voxelStepZ, std::fabs(dz)); + + minVoxelX = std::min(minVoxelX, std::min(originX, lastX)); + maxVoxelX = std::max(maxVoxelX, std::max(originX, lastX)); + minVoxelY = std::min(minVoxelY, std::min(originY, lastY)); + maxVoxelY = std::max(maxVoxelY, std::max(originY, lastY)); + minVoxelZ = std::min(minVoxelZ, std::min(originZ, lastZ)); + maxVoxelZ = std::max(maxVoxelZ, std::max(originZ, lastZ)); + } + + boundsOut[0] = minVoxelX; + boundsOut[1] = maxVoxelX; + boundsOut[2] = minVoxelY; + boundsOut[3] = maxVoxelY; + boundsOut[4] = minVoxelZ; + boundsOut[5] = maxVoxelZ; + + spacingOut[0] = voxelStepX; + spacingOut[1] = voxelStepY; + spacingOut[2] = voxelStepZ; + + /* + * Two dimensions: single slice + * Three dimensions: multiple slices + */ + int32_t validDimCount = 0; + if (maxVoxelX > minVoxelX) validDimCount++; + if (maxVoxelY > minVoxelY) validDimCount++; + if (maxVoxelZ > minVoxelZ) validDimCount++; + + bool valid = false; + if ((validDimCount >= 2) + && (voxelStepX > 0.0) + && (voxelStepY > 0.0) + && (voxelStepZ > 0.0)) { + valid = true; + } + + return valid; +} + +/** + * Create the oblique transformation matrix. + * + * @param sliceCoordinates + * Slice that is being drawn. + * @param obliqueTransformationMatrixOut + * OUTPUT transformation matrix for oblique viewing. + */ +void +BrainOpenGLVolumeTextureSliceDrawing::createObliqueTransformationMatrix(const float sliceCoordinates[3], + Matrix4x4& obliqueTransformationMatrixOut) +{ + /* + * Initialize the oblique transformation matrix + */ + obliqueTransformationMatrixOut.identity(); + + /* + * Get the oblique rotation matrix + */ + Matrix4x4 obliqueRotationMatrix = m_browserTabContent->getObliqueVolumeRotationMatrix(); + + /* + * Create the transformation matrix + */ + obliqueTransformationMatrixOut.postmultiply(obliqueRotationMatrix); + + /* + * Translate to selected coordinate + */ + obliqueTransformationMatrixOut.translate(sliceCoordinates[0], + sliceCoordinates[1], + sliceCoordinates[2]); +} + +/* ======================================================================= */ + +static bool +getTextureCoordinates(const VolumeFile* vf, + const float xyz[3], + const float maxStr[3], + float rstOut[3]) +{ + std::vector dims(5); + vf->getDimensions(dims); + int64_t smallCornerIJK[3] = { 0, 0, 0}; + float smallCornerXYZ[3] = { 0.0, 0.0, 0.0 }; + vf->indexToSpace(smallCornerIJK, smallCornerXYZ); + + int64_t bigCornerIJK[3] = { dims[0] - 1, dims[1] - 1, dims[2] - 1 }; + float bigCornerXYZ[3] = { 0.0, 0.0, 0.0 }; + vf->indexToSpace(bigCornerIJK, bigCornerXYZ); + + const float rangeXYZ[3] = { + bigCornerXYZ[0] - smallCornerXYZ[0], + bigCornerXYZ[1] - smallCornerXYZ[1], + bigCornerXYZ[2] - smallCornerXYZ[2] + }; + + const float normalizedOffset[3] = { + (xyz[0] - smallCornerXYZ[0]) / rangeXYZ[0], + (xyz[1] - smallCornerXYZ[1]) / rangeXYZ[1], + (xyz[2] - smallCornerXYZ[2]) / rangeXYZ[2], + }; + + rstOut[0] = -1.0 + (normalizedOffset[0] * 2.0); + rstOut[1] = -1.0 + (normalizedOffset[1] * 2.0); + rstOut[2] = -1.0 + (normalizedOffset[2] * 2.0); + rstOut[0] = normalizedOffset[0] * maxStr[0]; + rstOut[1] = normalizedOffset[1] * maxStr[1]; + rstOut[2] = normalizedOffset[2] * maxStr[2]; + + return true; +} + +static GLuint +createTextureName(BrainOpenGLFixedPipeline* fixedPipelineDrawing, + const VolumeFile* vf, + const DisplayGroupEnum::Enum displayGroup, + const int32_t tabIndex, + float maxStrOut[3]) +{ + GLuint textureName(0); + glPushClientAttrib(GL_CLIENT_PIXEL_STORE_BIT); + + glGenTextures(1, &textureName); + glBindTexture(GL_TEXTURE_3D, textureName); + + glPixelStorei(GL_UNPACK_SWAP_BYTES, GL_FALSE); + glPixelStorei(GL_UNPACK_LSB_FIRST, GL_FALSE); + glPixelStorei(GL_UNPACK_ROW_LENGTH, 0); + glPixelStorei(GL_UNPACK_IMAGE_HEIGHT, 0); + glPixelStorei(GL_UNPACK_SKIP_ROWS, 0); + glPixelStorei(GL_UNPACK_SKIP_PIXELS, 0); + glPixelStorei(GL_UNPACK_SKIP_IMAGES, 0); + glPixelStorei(GL_UNPACK_ALIGNMENT, 1); + + +// glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_S, GL_REPEAT); //GL_CLAMP); +// glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_T, GL_REPEAT); //GL_CLAMP); +// glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_R, GL_REPEAT); //GL_CLAMP); + + glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_S, GL_CLAMP); + glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_T, GL_CLAMP); + glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_R, GL_CLAMP); + + glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); //GL_NEAREST); //GL_LINEAR); + glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); //GL_NEAREST); //GL_LINEAR_MIPMAP_LINEAR); + + glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER); + glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER); + glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_BORDER); + + GLfloat borderColor[4] = { 0.0, 0.0, 0.0, 0.0 }; + glTexParameterfv(GL_TEXTURE_3D, GL_TEXTURE_BORDER_COLOR, borderColor); + + std::vector dims(5); + vf->getDimensions(dims); + + const int64_t mapIndex(0); + const int64_t numberOfSlices = dims[2]; + const int64_t numberOfRows = dims[1]; + const int64_t numberOfColumns = dims[0]; + const int64_t numSliceBytes = (numberOfRows * numberOfColumns * 4); + std::vector rgbaSlice(numSliceBytes); + + const int64_t numberOfBytes = (numberOfSlices * numSliceBytes); + std::vector rgbaColors; + rgbaColors.reserve(numberOfBytes); + + const int64_t textureDims = 256; + const int64_t textureBytes = (textureDims * textureDims * textureDims * 4); + + std::vector texture(textureBytes, 0); + + for (int64_t k = 0; k < numberOfSlices; k++) { + int64_t firstVoxelIJK[3] = { 0, 0, k }; + int64_t rowStepIJK[3] = { 0, 1, 0 }; + int64_t columnStepIJK[3] = { 1, 0, 0 }; + + vf->getVoxelColorsForSliceInMap(mapIndex, + firstVoxelIJK, + rowStepIJK, + columnStepIJK, + numberOfRows, + numberOfColumns, + displayGroup, + tabIndex, + &rgbaSlice[0]); + + for (int32_t j = 0; j < numberOfRows; j++) { + for (int32_t i = 0; i < numberOfColumns; i++) { + const int32_t sliceOffset = ((j * numberOfColumns) + i) * 4; + const int32_t textureOffset = ((k * textureDims * textureDims) + + (j * textureDims) + i) * 4; + for (int32_t m = 0; m < 4; m++) { + CaretAssertVectorIndex(texture, (textureOffset + 3)); + CaretAssertVectorIndex(rgbaSlice, sliceOffset + m); + texture[textureOffset + m] = rgbaSlice[sliceOffset + m]; + } + } + } + + rgbaColors.insert(rgbaColors.end(), + rgbaSlice.begin(), rgbaSlice.end()); + } + CaretAssert(static_cast(rgbaColors.size()) == numberOfBytes); + CaretAssert(static_cast(rgbaColors.size()) == + (numberOfColumns * numberOfRows * numberOfSlices * 4)); + + maxStrOut[0] = static_cast(dims[0]) / static_cast(textureDims); + maxStrOut[1] = static_cast(dims[1]) / static_cast(textureDims); + maxStrOut[2] = static_cast(dims[2]) / static_cast(textureDims); + if (debugFlag) std::cout << "max STR: " << AString::fromNumbers(maxStrOut, 3, ",") << std::endl; + +// for (int64_t k = 0; k < textureDims; k++) { +// for (int32_t j = 0; j < textureDims; j++) { +// for (int32_t i = 0; i < textureDims; i++) { +// const int64_t offset = ((k * textureDims * textureDims) +// + (j * textureDims) + i) * 4; +// CaretAssertVectorIndex(texture, offset + 3); +// texture[offset] = k; +// texture[offset+1] = 0; +// texture[offset+2] = 0; +// texture[offset+3] = 255; +// } +// } +// } +// fixedPipelineDrawing->testForOpenGLError("Before building mipmaps"); +// int result = gluBuild3DMipmaps(GL_TEXTURE_3D, +// GL_RGBA, +// numberOfColumns, +// numberOfRows, +// numberOfSlices, +// GL_RGBA, +// GL_UNSIGNED_BYTE, +// &rgbaColors); +// int result = gluBuild3DMipmapLevels(GL_TEXTURE_3D, +// GL_RGBA, +// numberOfColumns, +// numberOfRows, +// numberOfSlices, +// GL_RGBA, +// GL_UNSIGNED_BYTE, +// 0, +// 0, +// 1, +// &rgbaColors); +// if (result != 0) { +// std::cout << "Building mipmaps failed" << std::endl; +// std::cout << "GLU Error: " << gluErrorString(result) << std::endl; +// } +// fixedPipelineDrawing->testForOpenGLError("After building mipmaps"); + +// glTexImage3D(GL_TEXTURE_3D, +// 0, +// GL_RGBA, +// numberOfColumns, +// numberOfRows, +// numberOfSlices, +// 0, +// GL_RGBA, +// GL_UNSIGNED_BYTE, +// &rgbaColors[0]); + glTexImage3D(GL_TEXTURE_3D, + 0, + GL_RGBA, + textureDims, + textureDims, + textureDims, + 0, + GL_RGBA, + GL_UNSIGNED_BYTE, + &texture[0]); + + glBindTexture(GL_TEXTURE_3D, 0); + glPopClientAttrib(); + + return textureName; +} + +static std::map volumeTextureIDs; + +/** + * Draw an oblique slice with support for outlining labels and thresholded palette data. + * + * @param sliceViewPlane + * The plane for slice drawing. + * @param transformationMatrix + * The for oblique viewing. + */ +void +BrainOpenGLVolumeTextureSliceDrawing::drawObliqueSliceWithOutlines(const VolumeSliceViewPlaneEnum::Enum sliceViewPlane, + Matrix4x4& transformationMatrix) +{ + /* + * When performing voxel identification for editing voxels, + * we need to draw EVERY voxel since the user may click + * regions where the voxels are "off". + */ + float voxelEditingValue = 1.0; + VolumeFile* voxelEditingVolumeFile = NULL; + if (m_identificationModeFlag) { + SelectionItemVoxelEditing* voxelEditID = m_brain->getSelectionManager()->getVoxelEditingIdentification(); + if (voxelEditID->isEnabledForSelection()) { + voxelEditingVolumeFile = voxelEditID->getVolumeFileForEditing(); + if (voxelEditingVolumeFile != NULL) { + if (voxelEditingVolumeFile->isMappedWithLabelTable()) { + if (voxelEditingVolumeFile->getNumberOfMaps() > 0) { + voxelEditingValue = voxelEditingVolumeFile->getMapLabelTable(0)->getUnassignedLabelKey(); + } + } + } + } + } + + const bool obliqueSliceModeThreeDimFlag = false; + + float m[16]; + glGetFloatv(GL_MODELVIEW_MATRIX, m); + Matrix4x4 tm; + tm.setMatrixFromOpenGL(m); + + const int32_t numVolumes = static_cast(m_volumeDrawInfo.size()); + + /* + * Get the maximum bounds of the voxels from all slices + * and the smallest voxel spacing + */ + float voxelBounds[6]; + float voxelSpacing[3]; + if ( ! getVoxelCoordinateBoundsAndSpacing(voxelBounds, + voxelSpacing)) { + return; + } + float voxelSize = std::min(voxelSpacing[0], + std::min(voxelSpacing[1], + voxelSpacing[2])); + + /* + * Use a larger voxel size for the 3D view in volume slice viewing + * since it draws all three slices and this takes time + */ + if (obliqueSliceModeThreeDimFlag) { + voxelSize *= 3.0; + } + + /* + * Look at point is in center of volume + */ + float translation[3]; + m_browserTabContent->getTranslation(translation); + float viewOffsetX = 0.0; + float viewOffsetY = 0.0; + switch (sliceViewPlane) { + case VolumeSliceViewPlaneEnum::ALL: + CaretAssert(0); + break; + case VolumeSliceViewPlaneEnum::AXIAL: + viewOffsetX = (m_lookAtCenter[0] + translation[0]); + viewOffsetY = (m_lookAtCenter[1] + translation[1]); + break; + case VolumeSliceViewPlaneEnum::CORONAL: + viewOffsetX = (m_lookAtCenter[0] + translation[0]); + viewOffsetY = (m_lookAtCenter[2] + translation[2]); + break; + case VolumeSliceViewPlaneEnum::PARASAGITTAL: + viewOffsetX = (m_lookAtCenter[1] + translation[1]); + viewOffsetY = (m_lookAtCenter[2] + translation[2]); + break; + } + + float minScreenX = m_orthographicBounds[0] - viewOffsetX; + float maxScreenX = m_orthographicBounds[1] - viewOffsetX; + float minScreenY = m_orthographicBounds[2] - viewOffsetY; + float maxScreenY = m_orthographicBounds[3] - viewOffsetY; + + + /* + * Get origin voxel IJK + */ + const float zeroXYZ[3] = { 0.0, 0.0, 0.0 }; + int64_t originIJK[3]; + m_volumeDrawInfo[0].volumeFile->enclosingVoxel(zeroXYZ[0], zeroXYZ[1], zeroXYZ[2], + originIJK[0], originIJK[1], originIJK[2]); + + + /* + * Get XYZ center of origin Voxel + */ + float originVoxelXYZ[3]; + m_volumeDrawInfo[0].volumeFile->indexToSpace(originIJK, originVoxelXYZ); + float actualOrigin[3]; + m_volumeDrawInfo[0].volumeFile->indexToSpace(originIJK, actualOrigin); + + float screenOffsetX = 0.0; + float screenOffsetY = 0.0; + float originOffsetX = 0.0; + float originOffsetY = 0.0; + switch (sliceViewPlane) { + case VolumeSliceViewPlaneEnum::ALL: + CaretAssert(0); + break; + case VolumeSliceViewPlaneEnum::AXIAL: + screenOffsetX = m_lookAtCenter[0]; + screenOffsetY = m_lookAtCenter[1]; + originOffsetX = actualOrigin[0]; + originOffsetY = actualOrigin[1]; + break; + case VolumeSliceViewPlaneEnum::CORONAL: + screenOffsetX = m_lookAtCenter[0]; + screenOffsetY = m_lookAtCenter[2]; + originOffsetX = actualOrigin[0]; + originOffsetY = actualOrigin[2]; + break; + case VolumeSliceViewPlaneEnum::PARASAGITTAL: + screenOffsetX = m_lookAtCenter[1]; + screenOffsetY = m_lookAtCenter[2]; + originOffsetX = actualOrigin[1]; + originOffsetY = actualOrigin[2]; + break; + } + + const int32_t alignVoxelsFlag = 1; + if (alignVoxelsFlag == 1) { + /* + * Adjust for when selected slices are not at the origin + */ + const float xOffset = MathFunctions::remainder(screenOffsetX, voxelSize); + const float yOffset = MathFunctions::remainder(screenOffsetY, voxelSize); + originOffsetX -= xOffset; + originOffsetY -= yOffset; + + const int64_t numVoxelsToLeft = static_cast(MathFunctions::round(minScreenX + originOffsetX) / voxelSize); + const int64_t numVoxelsToRight = static_cast(MathFunctions::round(maxScreenX + originOffsetX) / voxelSize); + const int64_t numVoxelsToBottom = static_cast(MathFunctions::round(minScreenY + originOffsetY) / voxelSize); + const int64_t numVoxelsToTop = static_cast(MathFunctions::round(maxScreenY + originOffsetY)/ voxelSize); + + const float halfVoxel = voxelSize / 2.0; + + const float firstVoxelCenterX = (numVoxelsToLeft * voxelSize) + originOffsetX; + const float lastVoxelCenterX = (numVoxelsToRight * voxelSize) + originOffsetX; + + const float firstVoxelCenterY = (numVoxelsToBottom * voxelSize) + originOffsetY; + const float lastVoxelCenterY = (numVoxelsToTop * voxelSize) + originOffsetY; + + float newMinScreenX = firstVoxelCenterX - halfVoxel; + float newMaxScreenX = lastVoxelCenterX + halfVoxel; + float newMinScreenY = firstVoxelCenterY - halfVoxel; + float newMaxScreenY = lastVoxelCenterY + halfVoxel; + + if (debugFlag) { + const AString msg2 = ("Origin Voxel Coordinate: (" + + AString::fromNumbers(actualOrigin, 3, ",") + + "\n Oblique Screen X: (" + + AString::number(minScreenX) + + "," + + AString::number(maxScreenX) + + ") Y: (" + + AString::number(minScreenY) + + "," + + AString::number(maxScreenY) + + ")\nNew X: (" + + AString::number(newMinScreenX) + + "," + + AString::number(newMaxScreenX) + + ") Y: (" + + AString::number(newMinScreenY) + + "," + + AString::number(newMaxScreenY) + + ") Diff: (" + + AString::number((newMaxScreenX - newMinScreenX) / voxelSize) + + "," + + AString::number((newMaxScreenY - newMinScreenY) / voxelSize) + + ")"); + std::cout << qPrintable(msg2) << std::endl; + } + + minScreenX = newMinScreenX; + maxScreenX = newMaxScreenX; + minScreenY = newMinScreenY; + maxScreenY = newMaxScreenY; + } + + /* + * Set the corners of the screen for the respective view + */ + float bottomLeft[3]; + float bottomRight[3]; + float topRight[3]; + float topLeft[3]; + switch (sliceViewPlane) { + case VolumeSliceViewPlaneEnum::ALL: + CaretAssert(0); + break; + case VolumeSliceViewPlaneEnum::AXIAL: + bottomLeft[0] = minScreenX; + bottomLeft[1] = minScreenY; + bottomLeft[2] = 0.0; + bottomRight[0] = maxScreenX; + bottomRight[1] = minScreenY; + bottomRight[2] = 0.0; + topRight[0] = maxScreenX; + topRight[1] = maxScreenY; + topRight[2] = 0.0; + topLeft[0] = minScreenX; + topLeft[1] = maxScreenY; + topLeft[2] = 0.0; + break; + case VolumeSliceViewPlaneEnum::CORONAL: + bottomLeft[0] = minScreenX; + bottomLeft[1] = 0.0; + bottomLeft[2] = minScreenY; + bottomRight[0] = maxScreenX; + bottomRight[1] = 0.0; + bottomRight[2] = minScreenY; + topRight[0] = maxScreenX; + topRight[1] = 0.0; + topRight[2] = maxScreenY; + topLeft[0] = minScreenX; + topLeft[1] = 0.0; + topLeft[2] = maxScreenY; + break; + case VolumeSliceViewPlaneEnum::PARASAGITTAL: + bottomLeft[0] = 0.0; + bottomLeft[1] = minScreenX; + bottomLeft[2] = minScreenY; + bottomRight[0] = 0.0; + bottomRight[1] = maxScreenX; + bottomRight[2] = minScreenY; + topRight[0] = 0.0; + topRight[1] = maxScreenX; + topRight[2] = maxScreenY; + topLeft[0] = 0.0; + topLeft[1] = minScreenX; + topLeft[2] = maxScreenY; + break; + } + + + /* + * Transform the corners of the screen into model coordinates + */ + transformationMatrix.multiplyPoint3(bottomLeft); + transformationMatrix.multiplyPoint3(bottomRight); + transformationMatrix.multiplyPoint3(topRight); + transformationMatrix.multiplyPoint3(topLeft); + + if (debugFlag) { + const double bottomDist = MathFunctions::distance3D(bottomLeft, bottomRight); + const double topDist = MathFunctions::distance3D(topLeft, topRight); + const double bottomVoxels = bottomDist / voxelSize; + const double topVoxels = topDist / voxelSize; + const AString msg = ("Bottom Dist: " + + AString::number(bottomDist) + + " voxel size: " + + AString::number(bottomVoxels) + + " Top Dist: " + + AString::number(bottomDist) + + " voxel size: " + + AString::number(topVoxels)); + std::cout << qPrintable(msg) << std::endl; + } + + if (debugFlag) { + m_fixedPipelineDrawing->setLineWidth(3.0); + glColor3f(1.0, 0.0, 0.0); + glBegin(GL_LINE_LOOP); + glVertex3fv(bottomLeft); + glVertex3fv(bottomRight); + glVertex3fv(topRight); + glVertex3fv(topLeft); + glEnd(); + } + + /* + * Unit vector and distance in model coords along left side of screen + */ + double bottomLeftToTopLeftUnitVector[3] = { + topLeft[0] - bottomLeft[0], + topLeft[1] - bottomLeft[1], + topLeft[2] - bottomLeft[2], + }; + MathFunctions::normalizeVector(bottomLeftToTopLeftUnitVector); + const double bottomLeftToTopLeftDistance = MathFunctions::distance3D(bottomLeft, + topLeft); + + /* + * Unit vector and distance in model coords along right side of screen + */ + double bottomRightToTopRightUnitVector[3] = { + topRight[0] - bottomRight[0], + topRight[1] - bottomRight[1], + topRight[2] - bottomRight[2] + }; + MathFunctions::normalizeVector(bottomRightToTopRightUnitVector); + const double bottomRightToTopRightDistance = MathFunctions::distance3D(bottomRight, + topRight); + + if ((bottomLeftToTopLeftDistance > 0) + && (bottomRightToTopRightDistance > 0)) { + glPushAttrib(GL_COLOR_BUFFER_BIT); + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + + for (int32_t iVol = 0; iVol < numVolumes; iVol++) { + const BrainOpenGLFixedPipeline::VolumeDrawInfo& vdi = m_volumeDrawInfo[iVol]; + VolumeFile* vf = dynamic_cast(vdi.volumeFile); + if (vf != NULL) { +// std::vector dims(5); +// vf->getDimensions(dims); +// int64_t smallCornerIJK[3] = { 0, 0, 0}; +// float smallCornerXYZ[3] = { 0.0, 0.0, 0.0 }; +// vf->indexToSpace(smallCornerIJK, smallCornerXYZ); +// +// int64_t bigCornerIJK[3] = { dims[0] - 1, dims[1] - 1, dims[2] - 1 }; +// float bigCornerXYZ[3] = { 0.0, 0.0, 0.0 }; +// vf->indexToSpace(bigCornerIJK, bigCornerXYZ); +// +// float rangeXYZ[3] = { +// bigCornerXYZ[0] - smallCornerXYZ[0], +// bigCornerXYZ[1] - smallCornerXYZ[1], +// bigCornerXYZ[2] - smallCornerXYZ[2] +// }; + + + static float maxStr[3] = { 1.0, 1.0, 1.0 }; + GLuint textureID = 0; + auto idIter = volumeTextureIDs.find(vf); + if (idIter != volumeTextureIDs.end()) { + textureID = idIter->second; + } + else { + m_fixedPipelineDrawing->testForOpenGLError("Before creating texture"); + textureID = createTextureName(m_fixedPipelineDrawing, + vf, + m_displayGroup, + m_tabIndex, + maxStr); + m_fixedPipelineDrawing->testForOpenGLError("After creating texture"); + if (textureID != 0) { + volumeTextureIDs.insert(std::make_pair(vf, textureID)); + if (debugFlag) std::cout << "Created texture: " << textureID << std::endl; + } + else { + if (debugFlag) std::cout << "Failed to create texture ID" << std::endl; + } + } + + if (textureID > 0) { + float textureBottomLeft[3]; + getTextureCoordinates(vf, bottomLeft, maxStr, textureBottomLeft); + float textureBottomRight[3]; + getTextureCoordinates(vf, bottomRight, maxStr, textureBottomRight); + float textureTopLeft[3]; + getTextureCoordinates(vf, topLeft, maxStr, textureTopLeft); + float textureTopRight[3]; + getTextureCoordinates(vf, topRight, maxStr, textureTopRight); + + if (debugFlag) { + std::cout << "Bottom Left RST: " << AString::fromNumbers(textureBottomLeft, 3, ", ") << std::endl; + std::cout << "Bottom Right RST: " << AString::fromNumbers(textureBottomRight, 3, ", ") << std::endl; + std::cout << "Top Right RST: " << AString::fromNumbers(textureTopRight, 3, ", ") << std::endl; + std::cout << "Top Left RST: " << AString::fromNumbers(textureTopLeft, 3, ", ") << std::endl; + std::cout << std::endl; + } + + glDisable(GL_CULL_FACE); + glDisable(GL_DEPTH_TEST); + m_fixedPipelineDrawing->testForOpenGLError("Before drawing with texture"); + glEnable(GL_TEXTURE_3D); + glBindTexture(GL_TEXTURE_3D, textureID); + glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); + + glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_S, GL_CLAMP); + glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_T, GL_CLAMP); + glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_R, GL_CLAMP); + + glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + + glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER); + glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER); + glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_BORDER); + GLfloat borderColor[4] = { 0.0, 0.0, 0.0, 0.0 }; + glTexParameterfv(GL_TEXTURE_3D, GL_TEXTURE_BORDER_COLOR, borderColor); + + glBegin(GL_QUADS); + + glColor3f(0.0, 0.0, 0.0); + glTexCoord3fv(textureBottomLeft); + glVertex3fv(bottomLeft); + glTexCoord3fv(textureBottomRight); + glVertex3fv(bottomRight); + glTexCoord3fv(textureTopRight); + glVertex3fv(topRight); + glTexCoord3fv(textureTopLeft); + glVertex3fv(topLeft); + + +// glTexCoord3f(0.0, 0.0, 0.5); +// glVertex3fv(bottomLeft); +// glTexCoord3f(1.0, 0.0, 0.5); +// glVertex3fv(bottomRight); +// glTexCoord3f(1.0, 1.0, 0.5); +// glVertex3fv(topRight); +// glTexCoord3f(0.0, 1.0, 0.5); +// glVertex3fv(topLeft); + +// glColor3f(0.0, 0.0, 0.5); +// glVertex3fv(bottomLeft); +// glColor3f(1.0, 0.0, 0.5); +// glVertex3fv(bottomRight); +// glColor3f(1.0, 1.0, 0.5); +// glVertex3fv(topRight); +// glColor3f(0.0, 1.0, 0.5); +// glVertex3fv(topLeft); + + glEnd(); + + + glBindTexture(GL_TEXTURE_3D, 0); + glDisable(GL_TEXTURE_3D); + m_fixedPipelineDrawing->testForOpenGLError("After drawing with texture"); + } + } + } + + glPopAttrib(); + + + + + +// float leftToRightStepXYZ[3] = { +// bottomRight[0] - bottomLeft[0], +// bottomRight[1] - bottomLeft[1], +// bottomRight[2] - bottomLeft[2] +// }; +// const float bottomLeftToBottomRightDistance = MathFunctions::normalizeVector(leftToRightStepXYZ); +// leftToRightStepXYZ[0] *= voxelSize; +// leftToRightStepXYZ[1] *= voxelSize; +// leftToRightStepXYZ[2] *= voxelSize; +// +// const int32_t numberOfRows = std::round(bottomLeftToTopLeftDistance / voxelSize); +// const int32_t numberOfColumns = std::round(bottomLeftToBottomRightDistance / voxelSize); +// +// +// float bottomToTopStepXYZ[3] = { +// topLeft[0] - bottomLeft[0], +// topLeft[1] - bottomLeft[1], +// topLeft[2] - bottomLeft[2] +// }; +// MathFunctions::normalizeVector(bottomToTopStepXYZ); +// bottomToTopStepXYZ[0] *= voxelSize; +// bottomToTopStepXYZ[1] *= voxelSize; +// bottomToTopStepXYZ[2] *= voxelSize; +// +// const int32_t browserTabIndex = m_browserTabContent->getTabNumber(); +// const DisplayPropertiesLabels* displayPropertiesLabels = m_brain->getDisplayPropertiesLabels(); +// const DisplayGroupEnum::Enum displayGroup = displayPropertiesLabels->getDisplayGroupForTab(browserTabIndex); +// +// bool haveAlphaBlendingFlag(false); +// std::vector slices; +// for (int32_t iVol = 0; iVol < numVolumes; iVol++) { +// const BrainOpenGLFixedPipeline::VolumeDrawInfo& vdi = m_volumeDrawInfo[iVol]; +// VolumeMappableInterface* volInter = vdi.volumeFile; +// +// bool volumeEditDrawAllVoxelsFlag = false; +// if (voxelEditingVolumeFile != NULL) { +// if (voxelEditingVolumeFile == m_volumeDrawInfo[iVol].volumeFile) { +// volumeEditDrawAllVoxelsFlag = true; +// } +// } +// +// const bool bottomLayerFlag(iVol == 0); +// ObliqueSlice* slice = new ObliqueSlice(m_fixedPipelineDrawing, +// volInter, +// m_volumeDrawInfo[iVol].opacity, +// m_volumeDrawInfo[iVol].mapIndex, +// numberOfRows, +// numberOfColumns, +// browserTabIndex, +// displayPropertiesLabels, +// displayGroup, +// bottomLeft, +// leftToRightStepXYZ, +// bottomToTopStepXYZ, +// m_obliqueSliceMaskingType, +// voxelEditingValue, +// volumeEditDrawAllVoxelsFlag, +// m_identificationModeFlag, +// bottomLayerFlag); +// slices.push_back(slice); +// +// if (m_volumeDrawInfo[iVol].opacity < 1.0) { +// haveAlphaBlendingFlag = true; +// } +// } +// +// const int32_t numSlices = static_cast(slices.size()); +// if (numSlices > 0) { +// bool drawEachSliceFlag = true; +// bool compositeFlag = true; +// if (haveAlphaBlendingFlag +// || m_identificationModeFlag) { +// /* +// * Do not composite slices if blending is used +// * or if in identification mode +// */ +// compositeFlag = false; +// } +// if (compositeFlag) { +// CaretAssertVectorIndex(slices, 0); +// ObliqueSlice* underlaySlice = slices[0]; +// if (numSlices > 1) { +// std::vector overlaySlices; +// for (int32_t i = 1; i < numSlices; i++) { +// CaretAssertVectorIndex(slices, i); +// overlaySlices.push_back(slices[i]); +// } +// if (underlaySlice->compositeSlicesRGBA(overlaySlices)) { +// underlaySlice->draw(m_fixedPipelineDrawing); +// drawEachSliceFlag = false; +// } +// } +// } +// +// if (drawEachSliceFlag) { +// for (auto s : slices) { +// s->draw(m_fixedPipelineDrawing); +// } +// } +// } +// +// for (auto s : slices) { +// delete s; +// } +// slices.clear(); + } +} + +/** + * Constructor for drawing an oblique slice. + * + * @param fixedPipelineDrawing + * The fixed pipeline drawing. + * @param volumeInterface + * Interface for volume-type file being drawn. + * @param opacity + * Opacity for drawing the slice. + * @param mapIndex + * Index of map in file being drawn. + * @param numberOfRows + * Number of rows used when drawing slice. + * @param numberOfColumns + * Number of columns used when drawing slice. + * @param browserTabIndex + * Index of browser tab being drawn. + * @param displayPropertiesLabels + * Display properties for labels. + * @param displayGroup + * Selected display group for labels. + * @param originXYZ + * XYZ coordinate of first voxel in slice. + * @param leftToRightStepXYZ + * XYZ step for one voxel in screen left to right. + * @param bottomToTopStepXYZ + * XYZ step for one voxel in screen bottom to top. + * @param maskingType + * Masking type to eliminate interpolation artifacts + * @param voxelEditingValue + * Value used when editing voxels. + * @param volumeEditingDrawAllVoxelsFlag + * Draw all voxels when editing a volume. + * @param identificationModeFlag + * True if identification mode is active. + * @param bottomLayerFlag + * True if bottom layer. + */ +BrainOpenGLVolumeTextureSliceDrawing::ObliqueSlice::ObliqueSlice(BrainOpenGLFixedPipeline* fixedPipelineDrawing, + VolumeMappableInterface* volumeInterface, + const float opacity, + const int32_t mapIndex, + const int32_t numberOfRows, + const int32_t numberOfColumns, + const int32_t browserTabIndex, + const DisplayPropertiesLabels* displayPropertiesLabels, + const DisplayGroupEnum::Enum displayGroup, + const float originXYZ[3], + const float leftToRightStepXYZ[3], + const float bottomToTopStepXYZ[3], + const VolumeSliceInterpolationEdgeEffectsMaskingEnum::Enum maskingType, + const float voxelEditingValue, + const bool volumeEditingDrawAllVoxelsFlag, + const bool identificationModeFlag, + const bool bottomLayerFlag) +: +m_volumeInterface(volumeInterface), +m_opacity(opacity), +m_mapFile(dynamic_cast(volumeInterface)), +m_mapIndex(mapIndex), +m_numberOfRows(numberOfRows), +m_numberOfColumns(numberOfColumns), +m_browserTabIndex(browserTabIndex), +m_displayPropertiesLabels(displayPropertiesLabels), +m_displayGroup(displayGroup), +m_identificationX(fixedPipelineDrawing->mouseX), +m_identificationY(fixedPipelineDrawing->mouseY), +m_identificationModeFlag(identificationModeFlag), +m_bottomLayerFlag(bottomLayerFlag) +{ + CaretAssert(volumeInterface); + CaretAssert(m_mapFile); + + m_opacityByte = 255; + if (m_opacity >= 1.0) { + m_opacityByte = 255; + } + if (m_opacity <= 0.0) { + m_opacityByte = 0; + } + else { + m_opacityByte = static_cast(m_opacity * 255.0f); + } + + for (int32_t i = 0; i < 3; i++) { + m_selectionIJK[i] = -1; + m_leftToRightStepXYZ[i] = leftToRightStepXYZ[i]; + m_bottomToTopStepXYZ[i] = bottomToTopStepXYZ[i]; + m_originXYZ[i] = originXYZ[i]; + } + + const VolumeFile* volumeFileConst = dynamic_cast(volumeInterface); + m_volumeFile = const_cast(volumeFileConst); + const CiftiMappableDataFile* ciftiMappableFileConst = dynamic_cast(volumeInterface); + m_ciftiMappableFile = const_cast(ciftiMappableFileConst); + + m_dataValueType = DataValueType::INVALID; + if (m_mapFile->isMappedWithPalette()) { + if (m_volumeFile != NULL) { + m_dataValueType = DataValueType::VOLUME_PALETTE; + } + else if (m_ciftiMappableFile != NULL) { + m_dataValueType = DataValueType::CIFTI_PALETTE; + } + else { + CaretAssert(0); + } + } + else if (m_mapFile->isMappedWithLabelTable()) { + if (m_volumeFile != NULL) { + m_dataValueType = DataValueType::VOLUME_LABEL; + } + else if (m_ciftiMappableFile != NULL) { + m_dataValueType = DataValueType::CIFTI_LABEL; + } + else { + CaretAssert(0); + } + } + else if (m_mapFile->isMappedWithRGBA()) { + if (m_volumeFile != NULL) { + if (m_volumeFile->getNumberOfComponents() == 4) { + m_dataValueType = DataValueType::VOLUME_RGBA; + } + else if (m_volumeFile->getNumberOfComponents() == 3) { + m_dataValueType = DataValueType::VOLUME_RGB; + } + } + else { + CaretAssert(0); + } + } + CaretAssert(m_dataValueType != DataValueType::INVALID); + + m_sliceNumberOfVoxels = m_numberOfRows * m_numberOfColumns; + m_voxelNumberOfComponents = 1; + switch (m_dataValueType) { + case DataValueType::INVALID: + CaretAssert(0); + break; + case DataValueType::CIFTI_LABEL: + case DataValueType::CIFTI_PALETTE: + case DataValueType::VOLUME_LABEL: + case DataValueType::VOLUME_PALETTE: + break; + case DataValueType::VOLUME_RGB: + case DataValueType::VOLUME_RGBA: + m_voxelNumberOfComponents = 4; + break; + } + m_data.reserve(m_sliceNumberOfVoxels + * m_voxelNumberOfComponents); + + m_thresholdData.reserve(m_data.size()); + + m_rgba.reserve(m_sliceNumberOfVoxels * 4); + + if (m_identificationModeFlag) { + m_identificationIJK.reserve(m_sliceNumberOfVoxels * 3); + m_identificationHelper.reset(new IdentificationWithColor()); + } + + setThresholdFileAndMap(); + + loadData(maskingType, + voxelEditingValue, + volumeEditingDrawAllVoxelsFlag); + + assignRgba(volumeEditingDrawAllVoxelsFlag); + + if ( ! m_identificationModeFlag) { + addOutlines(); + } + + CaretAssert((m_sliceNumberOfVoxels*4) == static_cast(m_rgba.size())); +} + +/** + * Set thresholding file and map, if applicable. + */ +bool +BrainOpenGLVolumeTextureSliceDrawing::ObliqueSlice::setThresholdFileAndMap() +{ + switch (m_dataValueType) { + case DataValueType::INVALID: + CaretAssert(0); + break; + case DataValueType::CIFTI_LABEL: + break; + case DataValueType::CIFTI_PALETTE: + { + const PaletteColorMapping* pcm = m_ciftiMappableFile->getMapPaletteColorMapping(m_mapIndex); + CaretAssert(pcm); + switch (pcm->getThresholdType()) { + case PaletteThresholdTypeEnum::THRESHOLD_TYPE_OFF: + break; + case PaletteThresholdTypeEnum::THRESHOLD_TYPE_NORMAL: + m_thresholdCiftiMappableFile = m_ciftiMappableFile; + m_thresholdMapIndex = m_mapIndex; + break; + case PaletteThresholdTypeEnum::THRESHOLD_TYPE_FILE: + { + CaretMappableDataFileAndMapSelectionModel* selector = m_ciftiMappableFile->getMapThresholdFileSelectionModel(m_mapIndex); + m_thresholdCiftiMappableFile = dynamic_cast(selector->getSelectedFile()); + if (m_thresholdCiftiMappableFile != NULL) { + m_thresholdMapIndex = selector->getSelectedMapIndex(); + } + } + break; + case PaletteThresholdTypeEnum::THRESHOLD_TYPE_MAPPED: + case PaletteThresholdTypeEnum::THRESHOLD_TYPE_MAPPED_AVERAGE_AREA: + CaretAssert(0); + break; + } + if (m_thresholdCiftiMappableFile != NULL) { + if ((m_thresholdMapIndex >= 0) + && (m_thresholdMapIndex < m_thresholdCiftiMappableFile->getNumberOfMaps())) { + m_thresholdPaletteColorMapping = m_thresholdCiftiMappableFile->getMapPaletteColorMapping(m_thresholdMapIndex); + } + else { + m_thresholdCiftiMappableFile = NULL; + m_thresholdMapIndex = -1; + } + } + } + break; + case DataValueType::VOLUME_LABEL: + break; + case DataValueType::VOLUME_PALETTE: + { + const PaletteColorMapping* pcm = m_volumeFile->getMapPaletteColorMapping(m_mapIndex); + CaretAssert(pcm); + switch (pcm->getThresholdType()) { + case PaletteThresholdTypeEnum::THRESHOLD_TYPE_OFF: + break; + case PaletteThresholdTypeEnum::THRESHOLD_TYPE_NORMAL: + m_thresholdVolumeFile = m_volumeFile; + m_thresholdMapIndex = m_mapIndex; + break; + case PaletteThresholdTypeEnum::THRESHOLD_TYPE_FILE: + { + CaretMappableDataFileAndMapSelectionModel* selector = m_volumeFile->getMapThresholdFileSelectionModel(m_mapIndex); + m_thresholdVolumeFile = dynamic_cast(selector->getSelectedFile()); + if (m_thresholdVolumeFile != NULL) { + m_thresholdMapIndex = selector->getSelectedMapIndex(); + } + } + break; + case PaletteThresholdTypeEnum::THRESHOLD_TYPE_MAPPED: + case PaletteThresholdTypeEnum::THRESHOLD_TYPE_MAPPED_AVERAGE_AREA: + CaretAssert(0); + break; + } + if (m_thresholdVolumeFile != NULL) { + if ((m_thresholdMapIndex >= 0) + && (m_thresholdMapIndex < m_thresholdVolumeFile->getNumberOfMaps())) { + m_thresholdPaletteColorMapping = m_thresholdVolumeFile->getMapPaletteColorMapping(m_thresholdMapIndex); + } + else { + m_thresholdVolumeFile = NULL; + m_thresholdMapIndex = -1; + } + } + } + break; + case DataValueType::VOLUME_RGB: + break; + case DataValueType::VOLUME_RGBA: + break; + } + + if (m_thresholdMapIndex < 0) { + m_thresholdCiftiMappableFile = NULL; + m_thresholdVolumeFile = NULL; + m_thresholdPaletteColorMapping = NULL; + } + + return (m_thresholdMapIndex >= 0); +} + +/** + * Assign RGBA coloring for the voxels. + * + * @param volumeEditingDrawAllVoxelsFlag + * True if editing and all voxels should be drawn for selection. + */ +void +BrainOpenGLVolumeTextureSliceDrawing::ObliqueSlice::assignRgba(const bool volumeEditingDrawAllVoxelsFlag) +{ + if (m_data.empty()) { + return; + } + + switch (m_dataValueType) { + case DataValueType::INVALID: + CaretAssert(0); + break; + case DataValueType::CIFTI_LABEL: + case DataValueType::CIFTI_PALETTE: + case DataValueType::VOLUME_LABEL: + case DataValueType::VOLUME_PALETTE: + /* + * One data value per voxel. + */ + m_rgba.resize(m_data.size() * 4); + break; + case DataValueType::VOLUME_RGB: + case DataValueType::VOLUME_RGBA: + /* + * For RGBA, there are four data values per voxel + */ + m_rgba.resize(m_data.size()); + break; + } + + if (volumeEditingDrawAllVoxelsFlag) { + const int32_t numVoxels = static_cast(m_rgba.size() / 4); + for (int32_t i = 0; i < numVoxels; i++) { + const int32_t i4 = i * 4; + m_rgba[i4] = 255; + m_rgba[i4+1] = 255; + m_rgba[i4+2] = 255; + m_rgba[i4+3] = 255; + } + + return; + } + + switch (m_dataValueType) { + case DataValueType::INVALID: + break; + case DataValueType::CIFTI_LABEL: + { + CaretAssert(m_ciftiMappableFile); + const GiftiLabelTable* labelTable = m_ciftiMappableFile->getMapLabelTable(m_mapIndex); + CaretAssert(labelTable); + NodeAndVoxelColoring::colorIndicesWithLabelTableForDisplayGroupTab(labelTable, + &m_data[0], + m_data.size(), + m_displayGroup, + m_browserTabIndex, + &m_rgba[0]); + } + break; + case DataValueType::CIFTI_PALETTE: + { + CaretAssert(m_ciftiMappableFile); + PaletteColorMapping* pcm = m_ciftiMappableFile->getMapPaletteColorMapping(m_mapIndex); + CaretAssert(pcm); + const FastStatistics* stats = m_ciftiMappableFile->getMapFastStatistics(m_mapIndex); + CaretAssert(stats); + CaretAssert(m_data.size() == m_thresholdData.size()); + + PaletteColorMapping* threshPcm = ((m_thresholdPaletteColorMapping != NULL) + ? m_thresholdPaletteColorMapping + : pcm); + NodeAndVoxelColoring::colorScalarsWithPalette(stats, + pcm, + &m_data[0], + threshPcm, + &m_thresholdData[0], + m_data.size(), + &m_rgba[0]); + } + break; + case DataValueType::VOLUME_LABEL: + { + CaretAssert(m_volumeFile); + const GiftiLabelTable* labelTable = m_volumeFile->getMapLabelTable(m_mapIndex); + CaretAssert(labelTable); + NodeAndVoxelColoring::colorIndicesWithLabelTableForDisplayGroupTab(labelTable, + &m_data[0], + m_data.size(), + m_displayGroup, + m_browserTabIndex, + &m_rgba[0]); + } + break; + case DataValueType::VOLUME_PALETTE: + { + CaretAssert(m_volumeFile); + PaletteColorMapping* pcm = m_volumeFile->getMapPaletteColorMapping(m_mapIndex); + CaretAssert(pcm); + const FastStatistics* stats = m_volumeFile->getMapFastStatistics(m_mapIndex); + CaretAssert(stats); + CaretAssert(m_data.size() == m_thresholdData.size()); + + PaletteColorMapping* threshPcm = ((m_thresholdPaletteColorMapping != NULL) + ? m_thresholdPaletteColorMapping + : pcm); + NodeAndVoxelColoring::colorScalarsWithPalette(stats, + pcm, + &m_data[0], + threshPcm, + &m_thresholdData[0], + m_data.size(), + &m_rgba[0]); + } + break; + case DataValueType::VOLUME_RGB: + case DataValueType::VOLUME_RGBA: + { + /* + * Test RGB data to see if it ranges from [0, 1] or [0, 255]. + * Assume if any component is greater than 1, then the data is [0, 255] + */ + bool range255Flag = std::any_of(m_data.begin(), + m_data.end(), + [](float f) { return f > 1.0f; }); + + CaretAssert(m_data.size() == m_rgba.size()); + const int32_t numVoxels = static_cast(m_data.size() / 4); + if (range255Flag) { + for (int32_t i = 0; i < numVoxels; i++) { + const int32_t i4 = i * 4; + m_rgba[i4] = static_cast(m_data[i4]); + m_rgba[i4+1] = static_cast(m_data[i4+1]); + m_rgba[i4+2] = static_cast(m_data[i4+2]); + if (m_dataValueType == DataValueType::VOLUME_RGB) { + m_rgba[i4+3] = ((m_data[i4+3] > 0) + ? 255 + : 0); + } + } + } + else { + for (int32_t i = 0; i < numVoxels; i++) { + const int32_t i4 = i * 4; + m_rgba[i4] = static_cast(m_data[i4] * 255.0); + m_rgba[i4+1] = static_cast(m_data[i4+1] * 255.0); + m_rgba[i4+2] = static_cast(m_data[i4+2] * 255.0); + m_rgba[i4+3] = static_cast(m_data[i4+3] * 255.0); + } + } + } + break; + } +} + +/** + * Add any outlines for labels or thresholded palette data. + */ +void +BrainOpenGLVolumeTextureSliceDrawing::ObliqueSlice::addOutlines() +{ + LabelDrawingProperties* labelDrawingProperties = NULL; + PaletteColorMapping* paletteColorMapping = NULL; + switch (m_dataValueType) { + case DataValueType::INVALID: + break; + case DataValueType::CIFTI_LABEL: + CaretAssert(m_ciftiMappableFile); + labelDrawingProperties = m_ciftiMappableFile->getLabelDrawingProperties(); + break; + case DataValueType::CIFTI_PALETTE: + CaretAssert(m_ciftiMappableFile); + paletteColorMapping = m_ciftiMappableFile->getMapPaletteColorMapping(m_mapIndex); + break; + case DataValueType::VOLUME_LABEL: + CaretAssert(m_volumeFile); + labelDrawingProperties = m_volumeFile->getLabelDrawingProperties(); + break; + case DataValueType::VOLUME_PALETTE: + CaretAssert(m_volumeFile); + paletteColorMapping = m_volumeFile->getMapPaletteColorMapping(m_mapIndex); + break; + case DataValueType::VOLUME_RGB: + break; + case DataValueType::VOLUME_RGBA: + break; + } + + if (labelDrawingProperties != NULL) { + const LabelDrawingTypeEnum::Enum labelDrawingType = labelDrawingProperties->getDrawingType(); + if (labelDrawingType != LabelDrawingTypeEnum::DRAW_FILLED) { + const CaretColorEnum::Enum outlineColor = labelDrawingProperties->getOutlineColor(); + NodeAndVoxelColoring::convertSliceColoringToOutlineMode(&m_rgba[0], + labelDrawingType, + outlineColor, + m_numberOfColumns, + m_numberOfRows); + } + } + + if (paletteColorMapping != NULL) { + const PaletteThresholdOutlineDrawingModeEnum::Enum outlineMode = paletteColorMapping->getThresholdOutlineDrawingMode(); + if (outlineMode != PaletteThresholdOutlineDrawingModeEnum::OFF) { + const CaretColorEnum::Enum outlineColor = paletteColorMapping->getThresholdOutlineDrawingColor(); + NodeAndVoxelColoring::convertPaletteSliceColoringToOutlineMode(&m_rgba[0], + outlineMode, + outlineColor, + m_numberOfColumns, + m_numberOfRows); + } + } +} + +/** + * Loads voxels data into the 'slice'. + * + * @param maskingType + * Masking type to hide artifacts of interpolation + * @param voxelEditingValue + * Value for voxels when editing a volume + * @param volumeEditingDrawAllVoxelsFlag + * True if editing and all voxels should be drawn for selection. + */ +void +BrainOpenGLVolumeTextureSliceDrawing::ObliqueSlice::loadData(const VolumeSliceInterpolationEdgeEffectsMaskingEnum::Enum maskingType, + const float voxelEditingValue, + const bool volumeEditingDrawAllVoxelsFlag) +{ + m_validVoxelCount = 0; + + bool needCiftiMapDataFlag = false; + switch (m_dataValueType) { + case DataValueType::INVALID: + break; + case DataValueType::CIFTI_LABEL: + case DataValueType::CIFTI_PALETTE: + needCiftiMapDataFlag = true; + break; + case DataValueType::VOLUME_LABEL: + case DataValueType::VOLUME_PALETTE: + case DataValueType::VOLUME_RGB: + case DataValueType::VOLUME_RGBA: + break; + } + + float voxelCenterOffset[3] = { + ((m_leftToRightStepXYZ[0] / 2.0f) + (m_bottomToTopStepXYZ[0] / 2.0f)), + ((m_leftToRightStepXYZ[1] / 2.0f) + (m_bottomToTopStepXYZ[1] / 2.0f)), + ((m_leftToRightStepXYZ[2] / 2.0f) + (m_bottomToTopStepXYZ[2] / 2.0f)), + }; + + std::vector ciftiFileMapData; + std::vector thresholdCiftiFileMapData; + if (needCiftiMapDataFlag) { + /* + * Cifti files are slow at getting individual voxels since they + * provide no access to individual voxels. The reason is that + * the data may be on a server (Dense data) and accessing a single + * voxel would require requesting the entire map. So, for + * each Cifti file, get the entire map. + */ + CaretAssert(m_ciftiMappableFile); + m_ciftiMappableFile->getMapData(m_mapIndex, + ciftiFileMapData); + + if (m_thresholdCiftiMappableFile != NULL) { + m_thresholdCiftiMappableFile->getMapData(m_thresholdMapIndex, + thresholdCiftiFileMapData); + } + } + + for (int32_t iRow = 0; iRow < m_numberOfRows; iRow++) { + float rowStartXYZ[3] = { + m_originXYZ[0] + (iRow * m_bottomToTopStepXYZ[0]), + m_originXYZ[1] + (iRow * m_bottomToTopStepXYZ[1]), + m_originXYZ[2] + (iRow * m_bottomToTopStepXYZ[2]) + }; + + for (int32_t iCol = 0; iCol < m_numberOfColumns; iCol++) { + + const float voxelBottomLeft[3] = { + rowStartXYZ[0] + (iCol * m_leftToRightStepXYZ[0]), + rowStartXYZ[1] + (iCol * m_leftToRightStepXYZ[1]), + rowStartXYZ[2] + (iCol * m_leftToRightStepXYZ[2]) + }; + + const float voxelCenter[3] = { + voxelBottomLeft[0] + voxelCenterOffset[0], + voxelBottomLeft[1] + voxelCenterOffset[1], + voxelBottomLeft[2] + voxelCenterOffset[2] + }; + + float values[4] = { 0.0, 0.0, 0.0, 0.0 }; + bool valueValidFlag = false; + + float thresholdValue = 0.0; + bool thresholdValueValidFlag = false; + + switch (m_dataValueType) { + case DataValueType::INVALID: + CaretAssert(0); + break; + case DataValueType::CIFTI_LABEL: + case DataValueType::CIFTI_PALETTE: + { + CaretAssert(m_ciftiMappableFile); + const int64_t voxelOffset = m_ciftiMappableFile->getMapDataOffsetForVoxelAtCoordinate(voxelCenter, + m_mapIndex); + if (voxelOffset >= 0) { + CaretAssertVectorIndex(ciftiFileMapData, voxelOffset); + values[0] = ciftiFileMapData[voxelOffset]; + valueValidFlag = true; + } + + if (m_thresholdCiftiMappableFile != NULL) { + const int64_t thresholdVoxelOffset = m_thresholdCiftiMappableFile->getMapDataOffsetForVoxelAtCoordinate(voxelCenter, + m_thresholdMapIndex); + if (thresholdVoxelOffset >= 0) { + CaretAssertVectorIndex(thresholdCiftiFileMapData, thresholdVoxelOffset); + thresholdValue = thresholdCiftiFileMapData[thresholdVoxelOffset]; + thresholdValueValidFlag = true; + } + } + } + break; + case DataValueType::VOLUME_LABEL: + { + CaretAssert(m_volumeFile); + values[0] = m_volumeFile->getVoxelValue(voxelCenter, + &valueValidFlag, + m_mapIndex, + 0); + } + break; + case DataValueType::VOLUME_PALETTE: + { + CaretAssert(m_volumeFile); + values[0] = m_volumeFile->interpolateValue(voxelCenter, + VolumeFile::CUBIC, + &valueValidFlag, + m_mapIndex); + + if (valueValidFlag + && (values[0] != 0.0f)) { + /* + * Apply masking to oblique voxels (WB-750). + * In some instances, CUBIC interpolation may result in a voxel + * receiving a very small value (0.000000000000000210882405) and + * this will cause the slice drawing to look very unusual. Masking + * is used to prevent this from occurring. + * + */ + bool maskValidFlag = false; + float maskValue = 0.0f; + switch (maskingType) { + case VolumeSliceInterpolationEdgeEffectsMaskingEnum::OFF: + maskValidFlag = false; + break; + case VolumeSliceInterpolationEdgeEffectsMaskingEnum::LOOSE: + maskValue = m_volumeFile->interpolateValue(voxelCenter, + VolumeFile::TRILINEAR, + &maskValidFlag, + m_mapIndex); + break; + case VolumeSliceInterpolationEdgeEffectsMaskingEnum::TIGHT: + maskValue = m_volumeFile->interpolateValue(voxelCenter, + VolumeFile::ENCLOSING_VOXEL, + &maskValidFlag, + m_mapIndex); + break; + } + + if (maskValidFlag + && (maskValue == 0.0f)) { + values[0] = 0.0f; + valueValidFlag = false; + } + } + + if (m_thresholdVolumeFile != NULL) { + thresholdValue = m_thresholdVolumeFile->getVoxelValue(voxelCenter, + &thresholdValueValidFlag, + m_thresholdMapIndex, + 0); + } + } + break; + case DataValueType::VOLUME_RGB: + case DataValueType::VOLUME_RGBA: + { + values[0] = m_volumeFile->getVoxelValue(voxelCenter, + &valueValidFlag, + m_mapIndex, + 0); + if (valueValidFlag) { + values[1] = m_volumeFile->getVoxelValue(voxelCenter, + &valueValidFlag, + m_mapIndex, + 1); + values[2] = m_volumeFile->getVoxelValue(voxelCenter, + &valueValidFlag, + m_mapIndex, + 2); + if (m_dataValueType == DataValueType::VOLUME_RGBA) { + values[3] = m_volumeFile->getVoxelValue(voxelCenter, + &valueValidFlag, + m_mapIndex, + 3); + } + else { + values[3] = 1.0; + } + } + } + break; + } + + if (m_thresholdMapIndex < 0) { + thresholdValue = values[0]; + } + + /* + * Need to draw all voxels when editing + */ + if (volumeEditingDrawAllVoxelsFlag) { + if (! valueValidFlag) { + values[0] = voxelEditingValue; + valueValidFlag = true; + } + } + + switch (m_dataValueType) { + case DataValueType::INVALID: + break; + case DataValueType::CIFTI_LABEL: + case DataValueType::CIFTI_PALETTE: + case DataValueType::VOLUME_LABEL: + case DataValueType::VOLUME_PALETTE: + m_data.push_back(values[0]); + m_thresholdData.push_back(thresholdValue); + break; + case DataValueType::VOLUME_RGB: + case DataValueType::VOLUME_RGBA: + m_data.push_back(values[0]); + m_data.push_back(values[1]); + m_data.push_back(values[2]); + m_data.push_back(values[3]); + break; + } + + if (m_identificationModeFlag) { + /* + * When identifying, need an IJK triplet + * for each voxel + */ + int64_t ijk[3] = { -1, -1, -1 }; + if (valueValidFlag) { + if (m_volumeFile != NULL) { + m_volumeFile->enclosingVoxel(voxelCenter, ijk); + if ( ! m_volumeFile->indexValid(ijk)) { + ijk[0] = -1; + ijk[1] = -1; + ijk[2] = -1; + valueValidFlag = false; + } + } + else if (m_ciftiMappableFile != NULL) { + int64_t i(-1), j(-1), k(-1); + m_ciftiMappableFile->enclosingVoxel(voxelCenter[0], voxelCenter[1], voxelCenter[2], + i, j, k); + if (m_ciftiMappableFile->indexValid(i, j, k)) { + ijk[0] = i; + ijk[1] = j; + ijk[2] = k; + } + else { + valueValidFlag = false; + } + } + } + m_identificationIJK.insert(m_identificationIJK.end(), + ijk, ijk + 3); + } + + if (valueValidFlag) { + m_validVoxelCount++; + } + } + } + + CaretAssert((m_sliceNumberOfVoxels * m_voxelNumberOfComponents) == static_cast(m_data.size())); + if (m_thresholdMapIndex >= 0) { + CaretAssert(m_data.size() == m_thresholdData.size()); + } + + if (m_identificationModeFlag) { + CaretAssert((m_data.size() * 3) == m_identificationIJK.size()); + } +} + +/** + * Draw the oblique slice with the graphics primitives. + * + * @param fixedPipelineDrawing + * The fixed pipeline drawing. + */ +void +BrainOpenGLVolumeTextureSliceDrawing::ObliqueSlice::draw(BrainOpenGLFixedPipeline* fixedPipelineDrawing) +{ + Brain* brain = fixedPipelineDrawing->m_brain; + SelectionItemVoxel* selectionItemVoxel = brain->getSelectionManager()->getVoxelIdentification(); + SelectionItemVoxelEditing* selectionItemVoxelEditing = brain->getSelectionManager()->getVoxelEditingIdentification(); + + const GraphicsPrimitive::PrimitiveType primitiveType = GraphicsPrimitive::PrimitiveType::OPENGL_TRIANGLES; + std::unique_ptr primitive(GraphicsPrimitive::newPrimitiveV3fC4ub(primitiveType)); + + float rowXYZ[3] = { + m_originXYZ[0], + m_originXYZ[1], + m_originXYZ[2] + }; + + uint8_t sliceAlpha = 255; + bool drawWithBlendingFlag(false); +// if (m_bottomLayerFlag) { + if (m_opacity < 1.0) { + sliceAlpha = static_cast(m_opacity * 255.0); + drawWithBlendingFlag = true; + } +// } + + std::vector selectionIJK; + for (int32_t iRow = 0; iRow < m_numberOfRows; iRow++) { + float voxelXYZ[3] = { + rowXYZ[0], + rowXYZ[1], + rowXYZ[2] + }; + + const int32_t rowRgbaOffset = (iRow * m_numberOfColumns * 4); + for (int32_t iCol = 0; iCol < m_numberOfColumns; iCol++) { + float nextXYZ[3] = { + voxelXYZ[0] + m_leftToRightStepXYZ[0], + voxelXYZ[1] + m_leftToRightStepXYZ[1], + voxelXYZ[2] + m_leftToRightStepXYZ[2], + }; + + const int32_t rgbaIndex = rowRgbaOffset + (iCol * 4); + CaretAssertVectorIndex(m_rgba, rgbaIndex + 3); + const uint8_t alpha = m_rgba[rgbaIndex +3]; + if (alpha > 0) { + const float upperLeftXYZ[3] = { + voxelXYZ[0] + m_bottomToTopStepXYZ[0], + voxelXYZ[1] + m_bottomToTopStepXYZ[1], + voxelXYZ[2] + m_bottomToTopStepXYZ[2] + }; + const float upperRightXYZ[3] = { + nextXYZ[0] + m_bottomToTopStepXYZ[0], + nextXYZ[1] + m_bottomToTopStepXYZ[1], + nextXYZ[2] + m_bottomToTopStepXYZ[2] + }; + uint8_t* rgba = &m_rgba[rgbaIndex]; + if (drawWithBlendingFlag) { + if ((rgba[0] > 0) + || (rgba[1] > 0) + || (rgba[2] > 0)) { + rgba[3] = sliceAlpha; + } + } + + /* + * Each voxel is drawn with two triangles because: + * (1) Voxels may be sparse and triangles strips do not support 'restart' + * until newer versions of OpenGL. + * (2) Support for Quads is removed in newer versions of OpenGL. + */ + primitive->addVertex(voxelXYZ, + rgba); + primitive->addVertex(nextXYZ, + rgba); + primitive->addVertex(upperLeftXYZ, + rgba); + primitive->addVertex(upperLeftXYZ, + rgba); + primitive->addVertex(nextXYZ, + rgba); + primitive->addVertex(upperRightXYZ, + rgba); + + if (m_identificationModeFlag) { + /* + * Each voxel is drawn with two triangles so need to put ID + * in twice since so that number of selections equals number + * of triangles + */ + const int32_t idIndex = ((iRow * m_numberOfColumns * 3) + (iCol * 3)); + CaretAssertVectorIndex(m_identificationIJK, idIndex + 2); + selectionIJK.push_back(m_identificationIJK[idIndex]); + selectionIJK.push_back(m_identificationIJK[idIndex+1]); + selectionIJK.push_back(m_identificationIJK[idIndex+2]); + selectionIJK.push_back(m_identificationIJK[idIndex]); + selectionIJK.push_back(m_identificationIJK[idIndex+1]); + selectionIJK.push_back(m_identificationIJK[idIndex+2]); + } + } + + voxelXYZ[0] = nextXYZ[0]; + voxelXYZ[1] = nextXYZ[1]; + voxelXYZ[2] = nextXYZ[2]; + } + + rowXYZ[0] += m_bottomToTopStepXYZ[0]; + rowXYZ[1] += m_bottomToTopStepXYZ[1]; + rowXYZ[2] += m_bottomToTopStepXYZ[2]; + } + + if (primitive->isValid()) { + if (m_identificationModeFlag) { + int32_t selectedIndex = -1; + float selectedDepth = -1.0; + GraphicsEngineDataOpenGL::drawWithSelection(primitive.get(), + fixedPipelineDrawing->mouseX, + fixedPipelineDrawing->mouseY, + selectedIndex, + selectedDepth); + + if (selectedIndex >= 0) { + const int32_t ijkIndex = selectedIndex * 3; + CaretAssertVectorIndex(selectionIJK, ijkIndex + 2); + + m_selectionIJK[0] = selectionIJK[ijkIndex]; + m_selectionIJK[1] = selectionIJK[ijkIndex+1]; + m_selectionIJK[2] = selectionIJK[ijkIndex+2]; + + if ((m_selectionIJK[0] >= 0) + && (m_selectionIJK[1] >= 0) + && (m_selectionIJK[2])) { + if (selectionItemVoxel->isEnabledForSelection()) { + if (selectionItemVoxel->isOtherScreenDepthCloserToViewer(selectedDepth)) { + selectionItemVoxel->setVoxelIdentification(brain, + m_volumeInterface, + m_selectionIJK, + selectedDepth); + + float voxelCoordinates[3]; + m_volumeInterface->indexToSpace(m_selectionIJK[0], m_selectionIJK[1], m_selectionIJK[2], + voxelCoordinates[0], voxelCoordinates[1], voxelCoordinates[2]); + + fixedPipelineDrawing->setSelectedItemScreenXYZ(selectionItemVoxel, + voxelCoordinates); + CaretLogFinest("Selected Voxel (3D): " + AString::fromNumbers(m_selectionIJK, 3, ",")); + } + } + + if (selectionItemVoxelEditing->isEnabledForSelection()) { + if (selectionItemVoxelEditing->getVolumeFileForEditing() == m_volumeFile) { + if (selectionItemVoxelEditing->isOtherScreenDepthCloserToViewer(selectedDepth)) { + selectionItemVoxelEditing->setVoxelIdentification(brain, + m_volumeInterface, + m_selectionIJK, + selectedDepth); + float floatDiffXYZ[3] = { + m_leftToRightStepXYZ[0] + m_bottomToTopStepXYZ[0], + m_leftToRightStepXYZ[1] + m_bottomToTopStepXYZ[1], + m_leftToRightStepXYZ[2] + m_bottomToTopStepXYZ[2] + }; + selectionItemVoxelEditing->setVoxelDiffXYZ(floatDiffXYZ); + + float voxelCoordinates[3]; + m_volumeInterface->indexToSpace(m_selectionIJK[0], m_selectionIJK[1], m_selectionIJK[2], + voxelCoordinates[0], voxelCoordinates[1], voxelCoordinates[2]); + + fixedPipelineDrawing->setSelectedItemScreenXYZ(selectionItemVoxelEditing, + voxelCoordinates); + CaretLogFinest("Selected Voxel Editing (3D): Indices (" + + AString::fromNumbers(m_selectionIJK, 3, ",") + + ") Diff XYZ (" + + AString::fromNumbers(floatDiffXYZ, 3, ",") + + ")"); + } + } + } + } + } + } + else { + /* + * Only allow layer blending when overall volume opacity is off (>= 1.0) + */ + //const DisplayPropertiesVolume* dpv = brain->getDisplayPropertiesVolume(); + const bool allowBlendingFlag(true); //dpv->getOpacity() >= 1.0f); + + glPushAttrib(GL_COLOR_BUFFER_BIT); + if (drawWithBlendingFlag + && allowBlendingFlag) { + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + } + GraphicsEngineDataOpenGL::draw(primitive.get()); + glPopAttrib(); + } + } +} + +/** + * @return True if the output slice indices are valid, else false. + * + * @param ijkOut + * Output slice indices. + */ +bool +BrainOpenGLVolumeTextureSliceDrawing::ObliqueSlice::getSelectionIJK(int32_t ijkOut[3]) const +{ + ijkOut[0] = m_selectionIJK[0]; + ijkOut[1] = m_selectionIJK[1]; + ijkOut[2] = m_selectionIJK[2]; + return (m_selectionIJK[0] >= 0); +} + +/** + * @return True if the given slice spatially matches (voxel row/columns and coordinates). + * + * @param slice + * Slice for comparison. + */ +bool +BrainOpenGLVolumeTextureSliceDrawing::ObliqueSlice::spatialMatch(const ObliqueSlice* slice) +{ + CaretAssert(slice); + + const float diffMax = 0.001f; + if ((m_numberOfRows == slice->m_numberOfRows) + && (m_numberOfColumns == slice->m_numberOfColumns) + && (MathFunctions::distanceSquared3D(m_originXYZ, slice->m_originXYZ) < diffMax) + && (MathFunctions::distanceSquared3D(m_leftToRightStepXYZ, slice->m_leftToRightStepXYZ) < diffMax) + && (MathFunctions::distanceSquared3D(m_bottomToTopStepXYZ, slice->m_bottomToTopStepXYZ) < diffMax)) { + return true; + } + + return false; +} + +/** + * Composite the given slices onto this slice + * + * @param slices + * Slices that are composited. + * @return + * True if able to composite all slices, else false. + */ +bool +BrainOpenGLVolumeTextureSliceDrawing::ObliqueSlice::compositeSlicesRGBA(const std::vector& slices) +{ + std::vector matchedSlices; + for (auto s : slices) { + if (s != this) { + if (spatialMatch(s)) { + if (m_rgba.size() == s->m_rgba.size()) { + matchedSlices.push_back(s); + } + else { + return false; + } + } + } + } + + if (matchedSlices.empty()) { + return false; + } + + const int32_t numSlices = static_cast(matchedSlices.size()); + const int32_t rgbaLength = static_cast(m_rgba.size() / 4); + + for (int32_t i = 0; i < rgbaLength; i++) { + const int32_t i4 = i * 4; + for (int32_t iSlice = 0; iSlice < numSlices; iSlice++) { + CaretAssertVectorIndex(matchedSlices, iSlice); + const uint8_t* rgbaTop = &matchedSlices[iSlice]->m_rgba[i4]; + uint8_t alphaTop = rgbaTop[3]; + if (matchedSlices[iSlice]->m_opacity < 1.0f) { + float sliceAlphaFloat = ((static_cast(alphaTop) / 255.0f) * matchedSlices[iSlice]->m_opacity); + if (sliceAlphaFloat >= 1.0f) { + alphaTop = 255; + } + else if (sliceAlphaFloat <= 0.0f) { + alphaTop = 0; + } + else { + alphaTop = static_cast(sliceAlphaFloat * 255); + } + } + if (alphaTop > 0) { + uint8_t* rgbaBottom = &m_rgba[i4]; + if ((alphaTop == 255) || + (rgbaBottom[3] == 0)) { + rgbaBottom[0] = rgbaTop[0]; + rgbaBottom[1] = rgbaTop[1]; + rgbaBottom[2] = rgbaTop[2]; + rgbaBottom[3] = 255; + } + else if (alphaTop > 0) { + const float alpha = static_cast(alphaTop) / 255.0f; + const float oneMinusAlpha = 1.0f - alpha; + for (int32_t m = 0; m < 3; m++) { + const int32_t top = static_cast(static_cast(rgbaTop[m]) * alpha); + const int32_t bottom = static_cast(static_cast(rgbaBottom[m]) * oneMinusAlpha); + int32_t blended = (top + bottom); + if (blended > 255) blended = 255; + else if (blended < 0) blended = 0; + + rgbaBottom[m] = static_cast(blended); + } + rgbaBottom[3] = 255; + } + } + } + } + + return true; +} + + + + diff --git a/src/Brain/BrainOpenGLVolumeTextureSliceDrawing.h b/src/Brain/BrainOpenGLVolumeTextureSliceDrawing.h new file mode 100644 index 000000000..9ba80ff9b --- /dev/null +++ b/src/Brain/BrainOpenGLVolumeTextureSliceDrawing.h @@ -0,0 +1,332 @@ +#ifndef __BRAIN_OPEN_GL_VOLUME_TEXTURE_SLICE_DRAWING_H__ +#define __BRAIN_OPEN_GL_VOLUME_TEXTURE_SLICE_DRAWING_H__ + +/*LICENSE_START*/ +/* + * Copyright (C) 2014 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + +#include + +#include "BrainOpenGLFixedPipeline.h" +#include "BrainOpenGLVolumeSliceDrawing.h" +#include "CaretObject.h" +#include "DisplayGroupEnum.h" +#include "ModelTypeEnum.h" +#include "VolumeSliceInterpolationEdgeEffectsMaskingEnum.h" +#include "VolumeSliceProjectionTypeEnum.h" +#include "VolumeSliceDrawingTypeEnum.h" +#include "VolumeSliceViewAllPlanesLayoutEnum.h" +#include "VolumeSliceViewPlaneEnum.h" + +namespace caret { + + class Brain; + class BrowserTabContent; + class CiftiMappableDataFile; + class Matrix4x4; + class ModelVolume; + class ModelWholeBrain; + class Plane; + class VolumeMappableInterface; + + class BrainOpenGLVolumeTextureSliceDrawing : public CaretObject { + + public: + BrainOpenGLVolumeTextureSliceDrawing(); + + virtual ~BrainOpenGLVolumeTextureSliceDrawing(); + + void draw(BrainOpenGLFixedPipeline* fixedPipelineDrawing, + BrowserTabContent* browserTabContent, + std::vector& volumeDrawInfo, + const VolumeSliceDrawingTypeEnum::Enum sliceDrawingType, + const VolumeSliceProjectionTypeEnum::Enum sliceProjectionType, + const VolumeSliceInterpolationEdgeEffectsMaskingEnum::Enum obliqueSliceMaskingType, + const int32_t viewport[4]); + + // ADD_NEW_METHODS_HERE + + private: + class ObliqueSlice { + public: + enum class DataValueType { + INVALID, + CIFTI_LABEL, + CIFTI_PALETTE, + VOLUME_LABEL, + VOLUME_PALETTE, + VOLUME_RGB, + VOLUME_RGBA + }; + + ObliqueSlice(BrainOpenGLFixedPipeline* fixedPipelineDrawing, + VolumeMappableInterface* volumeInterface, + const float opacity, + const int32_t mapIndex, + const int32_t numberOfRows, + const int32_t numberOfColumns, + const int32_t browserTabIndex, + const DisplayPropertiesLabels* displayPropertiesLabels, + const DisplayGroupEnum::Enum displayGroup, + const float originXYZ[3], + const float leftToRightStepXYZ[3], + const float bottomToTopStepXYZ[3], + const VolumeSliceInterpolationEdgeEffectsMaskingEnum::Enum maskingType, + const float voxelEditingValue, + const bool volumeEditingDrawAllVoxelsFlag, + const bool identificationModeFlag, + const bool bottomLayerFlag); + + void assignRgba(const bool volumeEditingDrawAllVoxelsFlag); + + void addOutlines(); + + void loadData(const VolumeSliceInterpolationEdgeEffectsMaskingEnum::Enum maskingType, + const float voxelEditingValue, + const bool volumeEditingDrawAllVoxelsFlag); + + bool setThresholdFileAndMap(); + + bool getSelectionIJK(int32_t ijkOut[3]) const; + + void draw(BrainOpenGLFixedPipeline* fixedPipelineDrawing); + + bool spatialMatch(const ObliqueSlice* slice); + + bool compositeSlicesRGBA(const std::vector& slices); + + VolumeMappableInterface* m_volumeInterface; + + const float m_opacity; + + const CaretMappableDataFile* m_mapFile = NULL; + + const int32_t m_mapIndex; + + const int32_t m_numberOfRows; + + const int32_t m_numberOfColumns; + + const int32_t m_browserTabIndex; + + const DisplayPropertiesLabels* m_displayPropertiesLabels; + + const DisplayGroupEnum::Enum m_displayGroup; + + const int32_t m_identificationX; + + const int32_t m_identificationY; + + const bool m_identificationModeFlag; + + const bool m_bottomLayerFlag; + + CiftiMappableDataFile* m_ciftiMappableFile = NULL; + + VolumeFile* m_volumeFile = NULL; + + uint8_t m_opacityByte = 255; + + CiftiMappableDataFile* m_thresholdCiftiMappableFile = NULL; + + VolumeFile* m_thresholdVolumeFile = NULL; + + PaletteColorMapping* m_thresholdPaletteColorMapping = NULL; + + int32_t m_thresholdMapIndex = -1; + + float m_originXYZ[3]; + + float m_leftToRightStepXYZ[3]; + + float m_bottomToTopStepXYZ[3]; + + DataValueType m_dataValueType = DataValueType::INVALID; + + std::vector m_data; + + std::vector m_thresholdData; + + std::vector m_rgba; + + std::vector m_identificationIJK; + + int32_t m_validVoxelCount = 0; + + int32_t m_sliceNumberOfVoxels = 0; + + int32_t m_voxelNumberOfComponents = 1; + + std::unique_ptr m_identificationHelper; + + int64_t m_selectionIJK[3]; + }; + + BrainOpenGLVolumeTextureSliceDrawing(const BrainOpenGLVolumeTextureSliceDrawing&); + + BrainOpenGLVolumeTextureSliceDrawing& operator=(const BrainOpenGLVolumeTextureSliceDrawing&); + + void drawVolumeSlicesForAllStructuresView(const VolumeSliceProjectionTypeEnum::Enum sliceProjectionType, + const int32_t viewport[4]); + + void drawVolumeSliceViewPlane(const VolumeSliceDrawingTypeEnum::Enum sliceDrawingType, + const VolumeSliceProjectionTypeEnum::Enum sliceProjectionType, + const VolumeSliceViewPlaneEnum::Enum sliceViewPlane, + const VolumeSliceViewAllPlanesLayoutEnum::Enum allPlanesLayout, + const int32_t viewport[4]); + + void drawVolumeSliceViewType(const BrainOpenGLVolumeSliceDrawing::AllSliceViewMode allSliceViewMode, + const VolumeSliceDrawingTypeEnum::Enum sliceDrawingType, + const VolumeSliceProjectionTypeEnum::Enum sliceProjectionType, + const VolumeSliceViewPlaneEnum::Enum sliceViewPlane, + const int32_t viewport[4]); + + void drawVolumeSliceViewTypeMontage(const BrainOpenGLVolumeSliceDrawing::AllSliceViewMode allSliceViewMode, + const VolumeSliceDrawingTypeEnum::Enum sliceDrawingType, + const VolumeSliceProjectionTypeEnum::Enum sliceProjectionType, + const VolumeSliceViewPlaneEnum::Enum sliceViewPlane, + const int32_t viewport[4]); + + void drawVolumeSliceViewProjection(const BrainOpenGLVolumeSliceDrawing::AllSliceViewMode allSliceViewMode, + const VolumeSliceDrawingTypeEnum::Enum sliceDrawingType, + const VolumeSliceProjectionTypeEnum::Enum sliceProjectionType, + const VolumeSliceViewPlaneEnum::Enum sliceViewPlane, + const float sliceCoordinates[3], + const int32_t viewport[4]); + + void drawObliqueSlice(const VolumeSliceViewPlaneEnum::Enum sliceViewPlane, + Matrix4x4& transformationMatrix, + const Plane& plane); + + void drawObliqueSliceWithOutlines(const VolumeSliceViewPlaneEnum::Enum sliceViewPlane, + Matrix4x4& transformationMatrix); + + void createSlicePlaneEquation(const VolumeSliceProjectionTypeEnum::Enum sliceProjectionType, + const VolumeSliceViewPlaneEnum::Enum sliceViewPlane, + const float sliceCoordinates[3], + Plane& planeOut); + + void drawAxesCrosshairsOblique(const VolumeSliceViewPlaneEnum::Enum sliceViewPlane, + const float sliceCoordinates[3], + const bool drawCrosshairsFlag, + const bool drawCrosshairLabelsFlag); + + void setVolumeSliceViewingAndModelingTransformations(const VolumeSliceProjectionTypeEnum::Enum sliceProjectionType, + const VolumeSliceViewPlaneEnum::Enum sliceViewPlane, + const Plane& plane, + const float sliceCoordinates[3]); + + void getAxesColor(const VolumeSliceViewPlaneEnum::Enum sliceViewPlane, + float rgbaOut[4]) const; + + void drawLayers(const VolumeSliceDrawingTypeEnum::Enum sliceDrawingType, + const VolumeSliceProjectionTypeEnum::Enum sliceProjectionType, + const VolumeSliceViewPlaneEnum::Enum sliceViewPlane, + const Plane& slicePlane, + const float sliceCoordinates[3]); + + void drawVolumeSliceFoci(const Plane& plane); + + void drawAxesCrosshairs(const VolumeSliceProjectionTypeEnum::Enum sliceProjectionType, + const VolumeSliceDrawingTypeEnum::Enum sliceDrawingType, + const VolumeSliceViewPlaneEnum::Enum sliceViewPlane, + const float sliceCoordinates[3]); + + bool getMinMaxVoxelSpacing(const VolumeMappableInterface* volume, + float& minSpacingOut, + float& maxSpacingOut) const; + + void drawSquare(const float size); + + void drawOrientationAxes(const int viewport[4]); + + void setOrthographicProjection(const BrainOpenGLVolumeSliceDrawing::AllSliceViewMode allSliceViewMode, + const VolumeSliceViewPlaneEnum::Enum sliceViewPlane, + const int viewport[4]); + + bool getVoxelCoordinateBoundsAndSpacing(float boundsOut[6], + float spacingOut[3]); + + void createObliqueTransformationMatrix(const float sliceCoordinates[3], + Matrix4x4& obliqueTransformationMatrixOut); + + void addVoxelToIdentification(const int32_t volumeIndex, + const int32_t mapIndex, + const int32_t voxelI, + const int32_t voxelJ, + const int32_t voxelK, + const float voxelDiffXYZ[3], + uint8_t rgbaForColorIdentificationOut[4]); + + bool getVolumeDrawingViewDependentCulling(const VolumeSliceViewPlaneEnum::Enum sliceViewPlane, + const float selectedSliceCoordinate, + const VolumeMappableInterface* volumeFile, + int64_t culledFirstVoxelIJKOut[3], + int64_t culledLastVoxelIJKOut[3], + float voxelDeltaXYZOut[3]); + + void processIdentification(); + + void resetIdentification(); + + ModelVolume* m_modelVolume; + + ModelWholeBrain* m_modelWholeBrain; + + ModelTypeEnum::Enum m_modelType; + + VolumeMappableInterface* m_underlayVolume; + + Brain* m_brain; + + std::vector > m_ciftiMappableFileData; + + BrainOpenGLFixedPipeline* m_fixedPipelineDrawing; + + std::vector m_volumeDrawInfo; + + BrowserTabContent* m_browserTabContent; + + DisplayGroupEnum::Enum m_displayGroup; + + int32_t m_tabIndex; + + double m_lookAtCenter[3]; + + double m_viewingMatrix[16]; + + double m_orthographicBounds[6]; + + VolumeSliceInterpolationEdgeEffectsMaskingEnum::Enum m_obliqueSliceMaskingType = VolumeSliceInterpolationEdgeEffectsMaskingEnum::OFF; + + std::vector m_identificationIndices; + + bool m_identificationModeFlag; + + static const int32_t IDENTIFICATION_INDICES_PER_VOXEL; + + // ADD_NEW_MEMBERS_HERE + }; + +#ifdef __BRAIN_OPEN_GL_VOLUME_TEXTURE_SLICE_DRAWING_DECLARE__ + const int32_t BrainOpenGLVolumeTextureSliceDrawing::IDENTIFICATION_INDICES_PER_VOXEL = 8; +#endif // __BRAIN_OPEN_GL_VOLUME_TEXTURE_SLICE_DRAWING_DECLARE__ + +} // namespace +#endif //__BRAIN_OPEN_GL_VOLUME_TEXTURE_SLICE_DRAWING_H__ diff --git a/src/Brain/CMakeLists.txt b/src/Brain/CMakeLists.txt index 87674524c..9e40d104d 100644 --- a/src/Brain/CMakeLists.txt +++ b/src/Brain/CMakeLists.txt @@ -48,6 +48,7 @@ BrainOpenGLTextRenderInterface.h BrainOpenGLViewportContent.h BrainOpenGLVolumeObliqueSliceDrawing.h BrainOpenGLVolumeSliceDrawing.h +BrainOpenGLVolumeTextureSliceDrawing.h BrainOpenGLWindowContent.h BrainStructure.h BrainStructureNodeAttributes.h @@ -209,6 +210,7 @@ BrainOpenGLTextRenderInterface.cxx BrainOpenGLViewportContent.cxx BrainOpenGLVolumeObliqueSliceDrawing.cxx BrainOpenGLVolumeSliceDrawing.cxx +BrainOpenGLVolumeTextureSliceDrawing.cxx BrainOpenGLWindowContent.cxx BrainStructure.cxx BrainStructureNodeAttributes.cxx diff --git a/src/Common/DeveloperFlagsEnum.cxx b/src/Common/DeveloperFlagsEnum.cxx index e6652c823..1c97cd34b 100644 --- a/src/Common/DeveloperFlagsEnum.cxx +++ b/src/Common/DeveloperFlagsEnum.cxx @@ -129,6 +129,11 @@ DeveloperFlagsEnum::initialize() "Faster Volume Surface Outline", CheckableEnum::YES, true)); + checkableItems.push_back(DeveloperFlagsEnum(DEVELOPER_FLAG_TEXTURE_VOLUME, + "DEVELOPER_FLAG_TEXTURE_VOLUME", + "Texture Volume Drawing", + CheckableEnum::YES, + false)); std::vector notCheckableItems; diff --git a/src/Common/DeveloperFlagsEnum.h b/src/Common/DeveloperFlagsEnum.h index 763eec287..fb1e84d54 100644 --- a/src/Common/DeveloperFlagsEnum.h +++ b/src/Common/DeveloperFlagsEnum.h @@ -37,7 +37,8 @@ public: enum Enum { DEVELOPER_FLAG_UNUSED, DEVELOPER_FLAG_FLIP_PALETTE_NOT_DATA, - DEVELOPER_FLAG_NEW_VOLUME_SURFACE_OUTLINE + DEVELOPER_FLAG_NEW_VOLUME_SURFACE_OUTLINE, + DEVELOPER_FLAG_TEXTURE_VOLUME }; ~DeveloperFlagsEnum(); -- GitLab From cbda6a0fd5b03aec0451e018eaef9ffbfea5095e Mon Sep 17 00:00:00 2001 From: John Harwell Date: Thu, 16 May 2019 16:26:08 -0500 Subject: [PATCH 326/427] Removed developer flag for cached volume surface outline drawing which is now on all of the time. --- src/Brain/BrainOpenGLVolumeSliceDrawing.cxx | 15 +-------------- src/Common/DeveloperFlagsEnum.cxx | 5 ----- src/Common/DeveloperFlagsEnum.h | 1 - 3 files changed, 1 insertion(+), 20 deletions(-) diff --git a/src/Brain/BrainOpenGLVolumeSliceDrawing.cxx b/src/Brain/BrainOpenGLVolumeSliceDrawing.cxx index c49860804..93bb46fc2 100644 --- a/src/Brain/BrainOpenGLVolumeSliceDrawing.cxx +++ b/src/Brain/BrainOpenGLVolumeSliceDrawing.cxx @@ -2795,20 +2795,7 @@ BrainOpenGLVolumeSliceDrawing::drawSurfaceOutline(const VolumeMappableInterface* * Code still here to allow comparison with * previous algorithm */ - bool drawCachedFlag(false); - switch (sliceProjectionType) { - case VolumeSliceProjectionTypeEnum::VOLUME_SLICE_PROJECTION_OBLIQUE: - if (DeveloperFlagsEnum::isFlag(DeveloperFlagsEnum::DEVELOPER_FLAG_NEW_VOLUME_SURFACE_OUTLINE)) { - drawCachedFlag = true; - } - break; - case VolumeSliceProjectionTypeEnum::VOLUME_SLICE_PROJECTION_ORTHOGONAL: - if (DeveloperFlagsEnum::isFlag(DeveloperFlagsEnum::DEVELOPER_FLAG_NEW_VOLUME_SURFACE_OUTLINE)) { - drawCachedFlag = true; - } - break; - } - + bool drawCachedFlag(true); if (drawCachedFlag) { drawSurfaceOutlineCached(underlayVolume, modelType, diff --git a/src/Common/DeveloperFlagsEnum.cxx b/src/Common/DeveloperFlagsEnum.cxx index 1c97cd34b..3f47d45cf 100644 --- a/src/Common/DeveloperFlagsEnum.cxx +++ b/src/Common/DeveloperFlagsEnum.cxx @@ -124,11 +124,6 @@ DeveloperFlagsEnum::initialize() "Flip Palette Not Data", CheckableEnum::YES, false)); - checkableItems.push_back(DeveloperFlagsEnum(DEVELOPER_FLAG_NEW_VOLUME_SURFACE_OUTLINE, - "DEVELOPER_FLAG_NEW_VOLUME_SURFACE_OUTLINE", - "Faster Volume Surface Outline", - CheckableEnum::YES, - true)); checkableItems.push_back(DeveloperFlagsEnum(DEVELOPER_FLAG_TEXTURE_VOLUME, "DEVELOPER_FLAG_TEXTURE_VOLUME", "Texture Volume Drawing", diff --git a/src/Common/DeveloperFlagsEnum.h b/src/Common/DeveloperFlagsEnum.h index fb1e84d54..4f4ff9069 100644 --- a/src/Common/DeveloperFlagsEnum.h +++ b/src/Common/DeveloperFlagsEnum.h @@ -37,7 +37,6 @@ public: enum Enum { DEVELOPER_FLAG_UNUSED, DEVELOPER_FLAG_FLIP_PALETTE_NOT_DATA, - DEVELOPER_FLAG_NEW_VOLUME_SURFACE_OUTLINE, DEVELOPER_FLAG_TEXTURE_VOLUME }; -- GitLab From ca4b8f4cd47d255067bf2dc810a43b8544010c1b Mon Sep 17 00:00:00 2001 From: John Harwell Date: Mon, 20 May 2019 12:00:01 -0500 Subject: [PATCH 327/427] Added a developer flag for a tile tabs layout modification as Develop Menu --> Tile Tabs Vertical Centering. When this is enabled, each row in Tile Tabs is set to the minimum needed and the positions of the rows is adjusted so that any extra space is above all rows and below all rows; NOT between the rows. A scene created in an older version of wb_view (September 2018) did not display correctly with extra space between the two rows of tabs. The available height for each tab was greater than the height needed by aspect locking so tabs were taller than needed. It is possible that the user locked aspect before entering tile tabs as this has been found to cause a similar layout problem. Using the item in the Develop Menu, fixes this scene and others in the same scene file. --- src/Brain/BrainOpenGLViewportContent.cxx | 47 +++++++++++++++++++++++- src/Common/DeveloperFlagsEnum.cxx | 5 +++ src/Common/DeveloperFlagsEnum.h | 3 +- 3 files changed, 52 insertions(+), 3 deletions(-) diff --git a/src/Brain/BrainOpenGLViewportContent.cxx b/src/Brain/BrainOpenGLViewportContent.cxx index e26a2eb0b..ba4d5d89c 100644 --- a/src/Brain/BrainOpenGLViewportContent.cxx +++ b/src/Brain/BrainOpenGLViewportContent.cxx @@ -32,6 +32,7 @@ #include "BrowserWindowContent.h" #include "CaretAssert.h" #include "CaretLogger.h" +#include "DeveloperFlagsEnum.h" #include "EventBrowserWindowContent.h" #include "EventManager.h" #include "EventSpacerTabGet.h" @@ -611,8 +612,8 @@ BrainOpenGLViewportContent::createViewportContentForTileTabs(std::vector tabRowHeights(numRows, 0); + std::vector tabColumnWidths(numColumns, 0); + { + for (auto tsi : tabSizeInfoVector) { + const int32_t rowIndex = tsi.m_rowIndexFromTop; + tabRowHeights[rowIndex] = std::max(tabRowHeights[rowIndex], + tsi.m_height); + + const int32_t columnIndex(tsi.m_columnIndex); + tabColumnWidths[columnIndex] = std::max(tabColumnWidths[columnIndex], + tsi.m_width); + } + + for (int32_t i = 0; i < numRows; i++) { + rowHeights[i] = tabRowHeights[i]; + } + for (int32_t i = 0; i < numColumns; i++) { + columnWidths[i] = tabColumnWidths[i]; + } + } + const int32_t allTabsHeight = std::accumulate(tabRowHeights.begin(), tabRowHeights.end(), 0); + windowY -= ((windowHeight - allTabsHeight) / 2); + + // const int32_t allTabsWidth = std::accumulate(tabColumnWidths.begin(), tabColumnWidths.end(), 0); + // windowX = -((windowWidth - allTabsWidth) / 2); + } + + + /* * Note: There may be more tabs than there are cells (rows * columns) * so some tabs may not be displayed. @@ -855,6 +893,11 @@ BrainOpenGLViewportContent::createViewportContentForTileTabs(std::vectorgetTabIndex() == 0) { +// std::cout << "TAB 1: " << vpc->toString() << std::endl << std::endl; +// } +// } return viewportContentsOut; } diff --git a/src/Common/DeveloperFlagsEnum.cxx b/src/Common/DeveloperFlagsEnum.cxx index 3f47d45cf..d7b53f0cc 100644 --- a/src/Common/DeveloperFlagsEnum.cxx +++ b/src/Common/DeveloperFlagsEnum.cxx @@ -130,6 +130,11 @@ DeveloperFlagsEnum::initialize() CheckableEnum::YES, false)); + checkableItems.push_back(DeveloperFlagsEnum(DEVELOPER_FLAG_TILE_TABS_VERTICAL_CENTERING, + "DEVELOPER_FLAG_TILE_TABS_VERTICAL_CENTERING", + "Tile Tabs Vertical Centering", + CheckableEnum::YES, + false)); std::vector notCheckableItems; diff --git a/src/Common/DeveloperFlagsEnum.h b/src/Common/DeveloperFlagsEnum.h index 4f4ff9069..3369bf4b6 100644 --- a/src/Common/DeveloperFlagsEnum.h +++ b/src/Common/DeveloperFlagsEnum.h @@ -37,7 +37,8 @@ public: enum Enum { DEVELOPER_FLAG_UNUSED, DEVELOPER_FLAG_FLIP_PALETTE_NOT_DATA, - DEVELOPER_FLAG_TEXTURE_VOLUME + DEVELOPER_FLAG_TEXTURE_VOLUME, + DEVELOPER_FLAG_TILE_TABS_VERTICAL_CENTERING }; ~DeveloperFlagsEnum(); -- GitLab From 2b37a30c50fd909fb18893e4b54223d4d1f23a34 Mon Sep 17 00:00:00 2001 From: John Harwell Date: Mon, 20 May 2019 14:23:33 -0500 Subject: [PATCH 328/427] Add horizontal centering and correction and change Develop Menu item to Tile Tabs Centering Correction. --- src/Brain/BrainOpenGLViewportContent.cxx | 7 ++++--- src/Common/DeveloperFlagsEnum.cxx | 6 +++--- src/Common/DeveloperFlagsEnum.h | 2 +- 3 files changed, 8 insertions(+), 7 deletions(-) diff --git a/src/Brain/BrainOpenGLViewportContent.cxx b/src/Brain/BrainOpenGLViewportContent.cxx index ba4d5d89c..26778a215 100644 --- a/src/Brain/BrainOpenGLViewportContent.cxx +++ b/src/Brain/BrainOpenGLViewportContent.cxx @@ -742,7 +742,7 @@ BrainOpenGLViewportContent::createViewportContentForTileTabs(std::vector Date: Tue, 21 May 2019 15:52:45 -0500 Subject: [PATCH 329/427] For testing purposes only, made updates to oblique slice drawing that uses a 3D Texture and offers better performance than the current oblique implementation. It must be enabled using an item in the Develop Menu. --- .../BrainOpenGLVolumeTextureSliceDrawing.cxx | 224 ++++++++---------- 1 file changed, 98 insertions(+), 126 deletions(-) diff --git a/src/Brain/BrainOpenGLVolumeTextureSliceDrawing.cxx b/src/Brain/BrainOpenGLVolumeTextureSliceDrawing.cxx index f199ca30d..be093932a 100644 --- a/src/Brain/BrainOpenGLVolumeTextureSliceDrawing.cxx +++ b/src/Brain/BrainOpenGLVolumeTextureSliceDrawing.cxx @@ -2573,24 +2573,56 @@ createTextureName(BrainOpenGLFixedPipeline* fixedPipelineDrawing, glPixelStorei(GL_UNPACK_SKIP_IMAGES, 0); glPixelStorei(GL_UNPACK_ALIGNMENT, 1); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_BASE_LEVEL, 0); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 0); + /* + * From https://www.khronos.org/opengl/wiki/Common_Mistakes#Texture_edge_color_problem + * - Never use GL_CLAMP, use GL_CLAMP_TO_EDGE + */ -// glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_S, GL_REPEAT); //GL_CLAMP); -// glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_T, GL_REPEAT); //GL_CLAMP); -// glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_R, GL_REPEAT); //GL_CLAMP); + if (vf->isMappedWithPalette()) { + /* + * Magnification: pixel is smaller than a texel + * Minification: pixel is larger than a texel and thus multiple texels + * are mapped to the pixel + * GL_NEAREST: Use texel with coordinates nearest center of pixel + * Faster but may cause artifacts + * GL_LINEAR: Use weighted linear average of 2x2x2 texel array + * nearest center of pixel + * Smoother appearance but may be slower + */ + /* Linear filtering causes white line around left and top edges */ + glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + + GLfloat borderColor[4] = { 0.0, 0.0, 0.0, 0.0 }; + glTexParameterfv(GL_TEXTURE_3D, GL_TEXTURE_BORDER_COLOR, borderColor); - glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_S, GL_CLAMP); - glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_T, GL_CLAMP); - glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_R, GL_CLAMP); - glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); //GL_NEAREST); //GL_LINEAR); - glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); //GL_NEAREST); //GL_LINEAR_MIPMAP_LINEAR); + /* + * Clamp to edge seems to extend any data that is an edge voxel such as + * an ear in slice -18 in glassr volume + */ + glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE); - glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER); - glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER); - glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_BORDER); - - GLfloat borderColor[4] = { 0.0, 0.0, 0.0, 0.0 }; - glTexParameterfv(GL_TEXTURE_3D, GL_TEXTURE_BORDER_COLOR, borderColor); + /* + * Clamp to border seems to work best + */ + glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER); + glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER); + glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_BORDER); + + } + else { + glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + + glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE); + } std::vector dims(5); vf->getDimensions(dims); @@ -2616,6 +2648,7 @@ createTextureName(BrainOpenGLFixedPipeline* fixedPipelineDrawing, int64_t rowStepIJK[3] = { 0, 1, 0 }; int64_t columnStepIJK[3] = { 1, 0, 0 }; + std::fill(rgbaSlice.begin(), rgbaSlice.end(), 0); vf->getVoxelColorsForSliceInMap(mapIndex, firstVoxelIJK, rowStepIJK, @@ -2626,6 +2659,7 @@ createTextureName(BrainOpenGLFixedPipeline* fixedPipelineDrawing, tabIndex, &rgbaSlice[0]); + const bool edgeFlag(false); for (int32_t j = 0; j < numberOfRows; j++) { for (int32_t i = 0; i < numberOfColumns; i++) { const int32_t sliceOffset = ((j * numberOfColumns) + i) * 4; @@ -2636,6 +2670,19 @@ createTextureName(BrainOpenGLFixedPipeline* fixedPipelineDrawing, CaretAssertVectorIndex(rgbaSlice, sliceOffset + m); texture[textureOffset + m] = rgbaSlice[sliceOffset + m]; } + + if (edgeFlag) { + if (i == (numberOfColumns - 1)) { + if (numberOfColumns < textureDims) { + for (int32_t m = 0; m < 4; m++) { + CaretAssertVectorIndex(texture, (textureOffset + 3 + 4)); + CaretAssertVectorIndex(rgbaSlice, sliceOffset + m); + texture[textureOffset + m + 4] = rgbaSlice[sliceOffset + m]; + } + texture[textureOffset + 3 + 4] = 0; + } + } + } } } @@ -2651,55 +2698,6 @@ createTextureName(BrainOpenGLFixedPipeline* fixedPipelineDrawing, maxStrOut[2] = static_cast(dims[2]) / static_cast(textureDims); if (debugFlag) std::cout << "max STR: " << AString::fromNumbers(maxStrOut, 3, ",") << std::endl; -// for (int64_t k = 0; k < textureDims; k++) { -// for (int32_t j = 0; j < textureDims; j++) { -// for (int32_t i = 0; i < textureDims; i++) { -// const int64_t offset = ((k * textureDims * textureDims) -// + (j * textureDims) + i) * 4; -// CaretAssertVectorIndex(texture, offset + 3); -// texture[offset] = k; -// texture[offset+1] = 0; -// texture[offset+2] = 0; -// texture[offset+3] = 255; -// } -// } -// } -// fixedPipelineDrawing->testForOpenGLError("Before building mipmaps"); -// int result = gluBuild3DMipmaps(GL_TEXTURE_3D, -// GL_RGBA, -// numberOfColumns, -// numberOfRows, -// numberOfSlices, -// GL_RGBA, -// GL_UNSIGNED_BYTE, -// &rgbaColors); -// int result = gluBuild3DMipmapLevels(GL_TEXTURE_3D, -// GL_RGBA, -// numberOfColumns, -// numberOfRows, -// numberOfSlices, -// GL_RGBA, -// GL_UNSIGNED_BYTE, -// 0, -// 0, -// 1, -// &rgbaColors); -// if (result != 0) { -// std::cout << "Building mipmaps failed" << std::endl; -// std::cout << "GLU Error: " << gluErrorString(result) << std::endl; -// } -// fixedPipelineDrawing->testForOpenGLError("After building mipmaps"); - -// glTexImage3D(GL_TEXTURE_3D, -// 0, -// GL_RGBA, -// numberOfColumns, -// numberOfRows, -// numberOfSlices, -// 0, -// GL_RGBA, -// GL_UNSIGNED_BYTE, -// &rgbaColors[0]); glTexImage3D(GL_TEXTURE_3D, 0, GL_RGBA, @@ -2717,7 +2715,12 @@ createTextureName(BrainOpenGLFixedPipeline* fixedPipelineDrawing, return textureName; } -static std::map volumeTextureIDs; +struct TextureInfo { + GLuint m_textureID; + std::array m_maxSTR; +}; + +static std::map volumeTextureInfo; /** * Draw an oblique slice with support for outlining labels and thresholded palette data. @@ -3041,32 +3044,33 @@ BrainOpenGLVolumeTextureSliceDrawing::drawObliqueSliceWithOutlines(const VolumeS glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + bool firstFlag(true); for (int32_t iVol = 0; iVol < numVolumes; iVol++) { const BrainOpenGLFixedPipeline::VolumeDrawInfo& vdi = m_volumeDrawInfo[iVol]; VolumeFile* vf = dynamic_cast(vdi.volumeFile); if (vf != NULL) { -// std::vector dims(5); -// vf->getDimensions(dims); -// int64_t smallCornerIJK[3] = { 0, 0, 0}; -// float smallCornerXYZ[3] = { 0.0, 0.0, 0.0 }; -// vf->indexToSpace(smallCornerIJK, smallCornerXYZ); -// -// int64_t bigCornerIJK[3] = { dims[0] - 1, dims[1] - 1, dims[2] - 1 }; -// float bigCornerXYZ[3] = { 0.0, 0.0, 0.0 }; -// vf->indexToSpace(bigCornerIJK, bigCornerXYZ); -// -// float rangeXYZ[3] = { -// bigCornerXYZ[0] - smallCornerXYZ[0], -// bigCornerXYZ[1] - smallCornerXYZ[1], -// bigCornerXYZ[2] - smallCornerXYZ[2] -// }; - + if (debugFlag) { + std::cout << "Vol: " << iVol << ": " << vf->getFileNameNoPath() << std::endl; + } - static float maxStr[3] = { 1.0, 1.0, 1.0 }; + if (firstFlag) { + /* + * Using GL_ONE prevents an edge artifact + * (narrow line on texture edges). + */ + glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA); + firstFlag = false; + } + else { + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + } + std::array maxStr = { 1.0, 1.0, 1.0 }; GLuint textureID = 0; - auto idIter = volumeTextureIDs.find(vf); - if (idIter != volumeTextureIDs.end()) { - textureID = idIter->second; + auto idIter = volumeTextureInfo.find(vf); + if (idIter != volumeTextureInfo.end()) { + TextureInfo textureInfo = idIter->second; + textureID = textureInfo.m_textureID; + maxStr = textureInfo.m_maxSTR; } else { m_fixedPipelineDrawing->testForOpenGLError("Before creating texture"); @@ -3074,10 +3078,13 @@ BrainOpenGLVolumeTextureSliceDrawing::drawObliqueSliceWithOutlines(const VolumeS vf, m_displayGroup, m_tabIndex, - maxStr); + maxStr.data()); m_fixedPipelineDrawing->testForOpenGLError("After creating texture"); if (textureID != 0) { - volumeTextureIDs.insert(std::make_pair(vf, textureID)); + TextureInfo textureInfo; + textureInfo.m_textureID = textureID; + textureInfo.m_maxSTR = maxStr; + volumeTextureInfo.insert(std::make_pair(vf, textureInfo)); if (debugFlag) std::cout << "Created texture: " << textureID << std::endl; } else { @@ -3087,13 +3094,13 @@ BrainOpenGLVolumeTextureSliceDrawing::drawObliqueSliceWithOutlines(const VolumeS if (textureID > 0) { float textureBottomLeft[3]; - getTextureCoordinates(vf, bottomLeft, maxStr, textureBottomLeft); + getTextureCoordinates(vf, bottomLeft, maxStr.data(), textureBottomLeft); float textureBottomRight[3]; - getTextureCoordinates(vf, bottomRight, maxStr, textureBottomRight); + getTextureCoordinates(vf, bottomRight, maxStr.data(), textureBottomRight); float textureTopLeft[3]; - getTextureCoordinates(vf, topLeft, maxStr, textureTopLeft); + getTextureCoordinates(vf, topLeft, maxStr.data(), textureTopLeft); float textureTopRight[3]; - getTextureCoordinates(vf, topRight, maxStr, textureTopRight); + getTextureCoordinates(vf, topRight, maxStr.data(), textureTopRight); if (debugFlag) { std::cout << "Bottom Left RST: " << AString::fromNumbers(textureBottomLeft, 3, ", ") << std::endl; @@ -3110,22 +3117,8 @@ BrainOpenGLVolumeTextureSliceDrawing::drawObliqueSliceWithOutlines(const VolumeS glBindTexture(GL_TEXTURE_3D, textureID); glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); - glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_S, GL_CLAMP); - glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_T, GL_CLAMP); - glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_R, GL_CLAMP); - - glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - - glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER); - glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER); - glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_BORDER); - GLfloat borderColor[4] = { 0.0, 0.0, 0.0, 0.0 }; - glTexParameterfv(GL_TEXTURE_3D, GL_TEXTURE_BORDER_COLOR, borderColor); - glBegin(GL_QUADS); - - glColor3f(0.0, 0.0, 0.0); + glColor4f(0.0, 0.0, 1.0, 0.0); glTexCoord3fv(textureBottomLeft); glVertex3fv(bottomLeft); glTexCoord3fv(textureBottomRight); @@ -3134,28 +3127,7 @@ BrainOpenGLVolumeTextureSliceDrawing::drawObliqueSliceWithOutlines(const VolumeS glVertex3fv(topRight); glTexCoord3fv(textureTopLeft); glVertex3fv(topLeft); - - -// glTexCoord3f(0.0, 0.0, 0.5); -// glVertex3fv(bottomLeft); -// glTexCoord3f(1.0, 0.0, 0.5); -// glVertex3fv(bottomRight); -// glTexCoord3f(1.0, 1.0, 0.5); -// glVertex3fv(topRight); -// glTexCoord3f(0.0, 1.0, 0.5); -// glVertex3fv(topLeft); - -// glColor3f(0.0, 0.0, 0.5); -// glVertex3fv(bottomLeft); -// glColor3f(1.0, 0.0, 0.5); -// glVertex3fv(bottomRight); -// glColor3f(1.0, 1.0, 0.5); -// glVertex3fv(topRight); -// glColor3f(0.0, 1.0, 0.5); -// glVertex3fv(topLeft); - glEnd(); - glBindTexture(GL_TEXTURE_3D, 0); glDisable(GL_TEXTURE_3D); -- GitLab From 3ee0e590970ad1c970c844c1f48b9855dde8da1f Mon Sep 17 00:00:00 2001 From: John Harwell Date: Wed, 22 May 2019 14:09:13 -0500 Subject: [PATCH 330/427] Removed the developer flag for "tile tabs centering correction". An "Options" group has been added to the Tile Tabs Configuration dialog that contains a "Centering Correction" checkbox. Use of this option corrects the layout of tabs from an older scene file, particularly when the height of tab is limited by the aspect ratio and this height is less than the height of the row containing the tab. --- src/Brain/BrainOpenGLViewportContent.cxx | 4 +- src/Common/DeveloperFlagsEnum.cxx | 6 --- src/Common/DeveloperFlagsEnum.h | 3 +- src/Common/TileTabsConfiguration.cxx | 41 +++++++++++++++ src/Common/TileTabsConfiguration.h | 9 ++++ src/GuiQt/TileTabsConfigurationDialog.cxx | 62 +++++++++++++++++++++++ src/GuiQt/TileTabsConfigurationDialog.h | 12 ++++- 7 files changed, 125 insertions(+), 12 deletions(-) diff --git a/src/Brain/BrainOpenGLViewportContent.cxx b/src/Brain/BrainOpenGLViewportContent.cxx index 26778a215..ae7842ed4 100644 --- a/src/Brain/BrainOpenGLViewportContent.cxx +++ b/src/Brain/BrainOpenGLViewportContent.cxx @@ -32,7 +32,6 @@ #include "BrowserWindowContent.h" #include "CaretAssert.h" #include "CaretLogger.h" -#include "DeveloperFlagsEnum.h" #include "EventBrowserWindowContent.h" #include "EventManager.h" #include "EventSpacerTabGet.h" @@ -741,8 +740,7 @@ BrainOpenGLViewportContent::createViewportContentForTileTabs(std::vectorisCenteringCorrectionEnabled()) { /* * Kludge that fixes old scenes * NEED TO ADJUST FOR X too ? diff --git a/src/Common/DeveloperFlagsEnum.cxx b/src/Common/DeveloperFlagsEnum.cxx index baf5d40be..87103bdf1 100644 --- a/src/Common/DeveloperFlagsEnum.cxx +++ b/src/Common/DeveloperFlagsEnum.cxx @@ -130,12 +130,6 @@ DeveloperFlagsEnum::initialize() CheckableEnum::YES, false)); - checkableItems.push_back(DeveloperFlagsEnum(DEVELOPER_FLAG_TILE_TABS_CENTERING_CORRECTION, - "DEVELOPER_FLAG_TILE_TABS_CENTERING_CORRECTION", - "Tile Tabs Centering Correction", - CheckableEnum::YES, - false)); - std::vector notCheckableItems; enumData.insert(enumData.end(), diff --git a/src/Common/DeveloperFlagsEnum.h b/src/Common/DeveloperFlagsEnum.h index c9282a1f2..4f4ff9069 100644 --- a/src/Common/DeveloperFlagsEnum.h +++ b/src/Common/DeveloperFlagsEnum.h @@ -37,8 +37,7 @@ public: enum Enum { DEVELOPER_FLAG_UNUSED, DEVELOPER_FLAG_FLIP_PALETTE_NOT_DATA, - DEVELOPER_FLAG_TEXTURE_VOLUME, - DEVELOPER_FLAG_TILE_TABS_CENTERING_CORRECTION + DEVELOPER_FLAG_TEXTURE_VOLUME }; ~DeveloperFlagsEnum(); diff --git a/src/Common/TileTabsConfiguration.cxx b/src/Common/TileTabsConfiguration.cxx index 922c1c572..79c773921 100644 --- a/src/Common/TileTabsConfiguration.cxx +++ b/src/Common/TileTabsConfiguration.cxx @@ -123,6 +123,7 @@ TileTabsConfiguration::initialize() m_name.clear(); m_uniqueIdentifier = SystemUtilities::createUniqueID(); + m_centeringCorrectionEnabled = false; } /** @@ -140,6 +141,7 @@ TileTabsConfiguration::copyHelperTileTabsConfiguration(const TileTabsConfigurati m_name = obj.m_name; m_columns = obj.m_columns; m_rows = obj.m_rows; + m_centeringCorrectionEnabled = obj.m_centeringCorrectionEnabled; //DO NOT CHANGE THE UNIQUE IDENTIFIER: m_uniqueIdentifier } @@ -508,6 +510,28 @@ TileTabsConfiguration::getRowsAndColumnsForNumberOfTabs(const int32_t numberOfTa } } +/** + * @return True if the centering correction is enabled. + */ +bool +TileTabsConfiguration::isCenteringCorrectionEnabled() const +{ + return m_centeringCorrectionEnabled; +} + +/** + * Set the enabled status of the centering correction + * + * @param status + * New status for enabling the centering correction + */ +void +TileTabsConfiguration::setCenteringCorrectionEnabled(const bool status) +{ + m_centeringCorrectionEnabled = status; +} + + /** * Updates the number of rows and columns for the automatic configuration * based upon the number of tabs. @@ -622,6 +646,7 @@ TileTabsConfiguration::encodeInXMLWithStreamWriterVersionTwo() const writer.writeTextElement(s_nameTagName, m_name); writer.writeTextElement(s_uniqueIdentifierTagName, m_uniqueIdentifier); + writer.writeTextElement(s_v2_centeringCorrectionName, AString::fromBool(m_centeringCorrectionEnabled)); encodeRowColumnElement(writer, s_v2_columnsTagName, m_columns); encodeRowColumnElement(writer, s_v2_rowsTagName, m_rows); @@ -674,6 +699,8 @@ bool TileTabsConfiguration::decodeFromXMLWithStreamReader(const AString& xmlString, AString& errorMessageOut) { + m_centeringCorrectionEnabled = false; + QXmlStreamReader xml(xmlString); if (xml.readNextStartElement()) { @@ -903,6 +930,8 @@ TileTabsConfiguration::decodeFromXMLWithStreamReaderVersionTwo(QXmlStreamReader& }; ReadMode readMode = ReadMode::OTHER; + AString centeringCorrectionTextString; + while ( ! xml.atEnd()) { xml.readNext(); @@ -921,6 +950,9 @@ TileTabsConfiguration::decodeFromXMLWithStreamReaderVersionTwo(QXmlStreamReader& else if (tagName == s_v2_rowsTagName) { readMode = ReadMode::ROWS; } + else if (tagName == s_v2_centeringCorrectionName) { + centeringCorrectionTextString = xml.readElementText(); + } else if (tagName == s_v2_elementTagName) { switch (readMode) { case ReadMode::OTHER: @@ -993,6 +1025,15 @@ TileTabsConfiguration::decodeFromXMLWithStreamReaderVersionTwo(QXmlStreamReader& if (message.isEmpty()) { m_name = name; m_uniqueIdentifier = uniqueID; + + /* + * Only set centering correction if it is found. This allows usage + * of the default value in the event this is not found in the XML. + */ + if ( ! centeringCorrectionTextString.isEmpty()) { + m_centeringCorrectionEnabled = centeringCorrectionTextString.toBool(); + } + } else { xml.raiseError(message); diff --git a/src/Common/TileTabsConfiguration.h b/src/Common/TileTabsConfiguration.h index c49813f56..a28bf5792 100644 --- a/src/Common/TileTabsConfiguration.h +++ b/src/Common/TileTabsConfiguration.h @@ -84,6 +84,10 @@ namespace caret { void updateAutomaticConfigurationRowsAndColumns(const int32_t numberOfTabs); + bool isCenteringCorrectionEnabled() const; + + void setCenteringCorrectionEnabled(const bool status); + AString toString() const override; static bool lessThanComparisonByName(const TileTabsConfiguration* ttc1, @@ -130,6 +134,8 @@ namespace caret { std::vector m_rows; + bool m_centeringCorrectionEnabled = false; + static const AString s_nameTagName; static const AString s_uniqueIdentifierTagName; @@ -152,6 +158,8 @@ namespace caret { static const AString s_v2_rowsTagName; static const AString s_v2_stretchTypeAttributeName; static const AString s_v2_weightStretchAttributeName; + static const AString s_v2_centeringCorrectionName; + }; #ifdef __TILE_TABS_CONFIGURATION_DECLARE__ @@ -177,6 +185,7 @@ namespace caret { const AString TileTabsConfiguration::s_v2_rowsTagName = "Rows"; const AString TileTabsConfiguration::s_v2_stretchTypeAttributeName = "StretchType"; const AString TileTabsConfiguration::s_v2_weightStretchAttributeName = "WeightStretch"; + const AString TileTabsConfiguration::s_v2_centeringCorrectionName = "CenteringCorrection"; #endif // __TILE_TABS_CONFIGURATION_DECLARE__ } // namespace diff --git a/src/GuiQt/TileTabsConfigurationDialog.cxx b/src/GuiQt/TileTabsConfigurationDialog.cxx index 4d1aeabe1..e97420c9f 100644 --- a/src/GuiQt/TileTabsConfigurationDialog.cxx +++ b/src/GuiQt/TileTabsConfigurationDialog.cxx @@ -20,6 +20,7 @@ /*LICENSE_END*/ #include +#include #include #include #include @@ -363,6 +364,59 @@ TileTabsConfigurationDialog::createWorkbenchWindowWidget() return widget; } +/** + * @return The rows/columns stretch layout + */ +QWidget* +TileTabsConfigurationDialog::createCustomOptionsWidget() +{ + const QString toolTip("" + "Removes any space between rows and columns in the tile tabs configuration. " + "Some scenes created in previous versions of wb_view may not appear correctly " + "due to changes in layout of the tabs. Enabling this option may fix the " + "problem. In addition, if the Lock Aspect option is selected prior to " + "to enabling tile tabs, this option may improve the layout." + ""); + m_centeringCorrectionCheckBox = new QCheckBox("Centering Correction"); + m_centeringCorrectionCheckBox->setToolTip(toolTip); + QObject::connect(m_centeringCorrectionCheckBox, &QCheckBox::clicked, + this, &TileTabsConfigurationDialog::centeringCorrectionCheckBoxClicked); + + QGroupBox* groupBox = new QGroupBox("Options"); + QVBoxLayout* layout = new QVBoxLayout(groupBox); + layout->addWidget(m_centeringCorrectionCheckBox); + + return groupBox; +} + +/** + * Called when user checks/unchecks the centering correction checkbox + * + * @bool checked + * New checked status + */ +void +TileTabsConfigurationDialog::centeringCorrectionCheckBoxClicked(bool checked) +{ + TileTabsConfiguration* config = getCustomTileTabsConfiguration(); + if (config != NULL) { + config->setCenteringCorrectionEnabled(checked); + updateGraphicsWindow(); + } +} + +/** + * Update the custom options + */ +void +TileTabsConfigurationDialog::updateCustomOptionsWidget() +{ + TileTabsConfiguration* config = getCustomTileTabsConfiguration(); + if (config != NULL) { + m_centeringCorrectionCheckBox->setChecked(config->isCenteringCorrectionEnabled()); + } +} + /** * @return The rows/columns stretch layout */ @@ -466,6 +520,8 @@ TileTabsConfigurationDialog::updateRowColumnStretchWidgets(TileTabsConfiguration m_columnElementsGridLayout->setSizeConstraint(QLayout::SetMinAndMaxSize); } + updateCustomOptionsWidget(); + // m_customConfigurationWidget->adjustSize(); } @@ -552,6 +608,10 @@ TileTabsConfigurationDialog::createActiveConfigurationWidget() m_customConfigurationWidget = createRowColumnStretchWidget(); m_customConfigurationWidget->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); + m_customOptionsWidget = createCustomOptionsWidget(); + m_customOptionsWidget->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); + + QScrollArea* stretchFactorScrollArea = new QScrollArea(); stretchFactorScrollArea->setWidget(m_customConfigurationWidget); stretchFactorScrollArea->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); @@ -588,6 +648,8 @@ TileTabsConfigurationDialog::createActiveConfigurationWidget() 3, 4, 1, 1); widgetLayout->addWidget(stretchFactorScrollArea, 4, 0, 1, 6); + widgetLayout->addWidget(m_customOptionsWidget, + 5, 0, 1, 6, Qt::AlignLeft); widget->setFixedWidth(widget->sizeHint().width()); diff --git a/src/GuiQt/TileTabsConfigurationDialog.h b/src/GuiQt/TileTabsConfigurationDialog.h index 5c20a4deb..f42f5d433 100644 --- a/src/GuiQt/TileTabsConfigurationDialog.h +++ b/src/GuiQt/TileTabsConfigurationDialog.h @@ -27,6 +27,7 @@ #include "TileTabsRowColumnStretchTypeEnum.h" #include "WuQDialogNonModal.h" +class QCheckBox; class QDoubleSpinBox; class QGridLayout; class QLabel; @@ -93,7 +94,8 @@ namespace caret { void automaticCustomButtonClicked(QAbstractButton*); void tileTabsModificationRequested(EventTileTabsConfigurationModification& modification); - + + void centeringCorrectionCheckBoxClicked(bool checked); protected: void focusGained(); @@ -121,6 +123,8 @@ namespace caret { QWidget* createRowColumnStretchWidget(); + QWidget* createCustomOptionsWidget(); + void updateRowColumnStretchWidgets(TileTabsConfiguration* configuration); void addRowColumnStretchWidget(const EventTileTabsConfigurationModification::RowColumnType rowColumnType, @@ -131,6 +135,8 @@ namespace caret { void updateGraphicsWindow(); + void updateCustomOptionsWidget(); + void readConfigurationsFromPreferences(); BrainBrowserWindow* getBrowserWindow(); @@ -141,6 +147,8 @@ namespace caret { QWidget* m_customConfigurationWidget; + QWidget* m_customOptionsWidget; + QRadioButton* m_automaticConfigurationRadioButton; QRadioButton* m_customConfigurationRadioButton; @@ -169,6 +177,8 @@ namespace caret { QGridLayout* m_columnElementsGridLayout = NULL; + QCheckBox* m_centeringCorrectionCheckBox; + /** Blocks reading of preferences since that may invalidate data pointers */ bool m_blockReadConfigurationsFromPreferences; -- GitLab From 77e348aa17a786e38bbd98b55e6f13db771f55cf Mon Sep 17 00:00:00 2001 From: John Harwell Date: Wed, 22 May 2019 14:55:49 -0500 Subject: [PATCH 331/427] On Enter Annotations Mode dialog, emphasize that tile tabs should be enabled prior to entering Annotations mode. --- src/GuiQt/LockAspectWarningDialog.cxx | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/src/GuiQt/LockAspectWarningDialog.cxx b/src/GuiQt/LockAspectWarningDialog.cxx index e3fbb1a15..218a17075 100644 --- a/src/GuiQt/LockAspectWarningDialog.cxx +++ b/src/GuiQt/LockAspectWarningDialog.cxx @@ -117,8 +117,15 @@ m_brainBrowserWindow(brainBrowserWindow) const QString mainInstructions("Do you want to lock the aspect ratio while entering annotations mode?"); const QString supplementalInstructions("" - "Prior to locking the aspect ratio, the user should adjust the size " - "of the window and optionally enable Tile Tabs for a multi-tab view. " + "Prior to locking the aspect ratio, the user should: " + "
          " + "
        • Adjust the size of the window" + "
        • Optionally enable Tile Tabs for a multi-tab view. " + "If annotating a Tile Tabs view, Tile Tabs " + "(View Menu -> Enter Tile Tabs) should ALWAYS be enabled prior to " + "locking the aspect ratio. Failure to do so may cause excess, " + "undesirable space around and between the drawing of tab rows." + "
        " "If this has not been done, click the Cancel button, make those " "adjustments, and then click the Toolbar's Annotate Mode button." "

        " -- GitLab From f39dd6b13e4e8b26524c66d16eb835cdb6ca7dcc Mon Sep 17 00:00:00 2001 From: John Harwell Date: Wed, 22 May 2019 15:51:57 -0500 Subject: [PATCH 332/427] When user clicks the Lock Aspect button and it is not selected and Tile Tabs is not enabled, warn user that it is important to adjust the window size and enable tile tabs prior to locking the aspect ratio. --- src/GuiQt/BrainBrowserWindow.cxx | 43 +++++++++++++++++++- src/GuiQt/BrainBrowserWindow.h | 2 + src/GuiQt/LockAspectWarningDialog.cxx | 58 ++++++++++++++++++--------- src/GuiQt/LockAspectWarningDialog.h | 8 ++++ 4 files changed, 92 insertions(+), 19 deletions(-) diff --git a/src/GuiQt/BrainBrowserWindow.cxx b/src/GuiQt/BrainBrowserWindow.cxx index d77fa9896..2e673e09b 100644 --- a/src/GuiQt/BrainBrowserWindow.cxx +++ b/src/GuiQt/BrainBrowserWindow.cxx @@ -953,7 +953,7 @@ BrainBrowserWindow::changeInputModeToAnnotationsWarningDialog() okFlag = false; break; case LockAspectWarningDialog::Result::LOCK_ASPECT: - processToolBarLockWindowAndAllTabAspectTriggered(true); + processToolBarLockWindowAndAllTabAspectsRatios(true); break; case LockAspectWarningDialog::Result::NO_CHANGES: break; @@ -1042,6 +1042,47 @@ BrainBrowserWindow::lockAllTabAspectRatios(const bool checked) */ void BrainBrowserWindow::processToolBarLockWindowAndAllTabAspectTriggered(bool checked) +{ + static bool doNotShowAgainFlag(false); + + if (checked) { + if ( ! isTileTabsSelected()) { + if ( ! doNotShowAgainFlag) { + const QString labelText = LockAspectWarningDialog::getLockingInstructionsText("Lock Aspect", + false); + QLabel* warningLabel = new QLabel(labelText); + warningLabel->setWordWrap(true); + + const QString cbText("Do not show again. User will not be warned about aspect locking"); + QCheckBox* doNotShowAgainCheckBox = new QCheckBox(cbText); + + WuQDataEntryDialog warningDialog("Lock Aspect", + m_toolBarLockWindowAndAllTabAspectRatioButton, + WuQDialog::SCROLL_AREA_NEVER); + warningDialog.addWidget("", warningLabel); + warningDialog.addWidget("", doNotShowAgainCheckBox); + + const int result = warningDialog.exec(); + doNotShowAgainFlag = doNotShowAgainCheckBox->isChecked(); + if (result == WuQDataEntryDialog::Rejected) { + m_toolBarLockWindowAndAllTabAspectRatioAction->setChecked(false); + return; + } + } + } + } + + processToolBarLockWindowAndAllTabAspectsRatios(checked); +} + +/** + * Called when the toolbar's lock window and all tab aspect is triggered + * + * @param checked + * True if checked, false if unchecked + */ +void +BrainBrowserWindow::processToolBarLockWindowAndAllTabAspectsRatios(bool checked) { lockWindowAspectRatio(checked); lockAllTabAspectRatios(checked); diff --git a/src/GuiQt/BrainBrowserWindow.h b/src/GuiQt/BrainBrowserWindow.h index 34b964654..a4363d4ad 100644 --- a/src/GuiQt/BrainBrowserWindow.h +++ b/src/GuiQt/BrainBrowserWindow.h @@ -326,6 +326,8 @@ namespace caret { void lockAllTabAspectRatios(const bool checked); void updateActionsForLockingAspectRatios(); + void processToolBarLockWindowAndAllTabAspectsRatios(bool checked); + float getAspectRatioFromDialog(const AspectRatioMode aspectRatioMode, const QString& title, const float aspectRatio, diff --git a/src/GuiQt/LockAspectWarningDialog.cxx b/src/GuiQt/LockAspectWarningDialog.cxx index 218a17075..ce5e28ba3 100644 --- a/src/GuiQt/LockAspectWarningDialog.cxx +++ b/src/GuiQt/LockAspectWarningDialog.cxx @@ -93,6 +93,44 @@ LockAspectWarningDialog::runDialog(const int32_t browserWindowIndex) return Result::CANCEL; } +/** + * @return Locking aspect instruction containing importance of window sizing + * and locking tabs. + * + * @parm buttonText + * Text of button that user has clicked + * @param showBestPracticesLink + If true, include a link to the best practices guide. + */ +QString +LockAspectWarningDialog::getLockingInstructionsText(const QString& buttonText, + const bool showBestPracticesLink) +{ + QString text("" + "Prior to locking the aspect ratio, the user should: " + "

          " + "
        • Adjust the size of the window;" + "
        • Optionally enable Tile Tabs for a multi-tab view; " + "If annotating a Tile Tabs view, Tile Tabs " + "(View Menu -> Enter Tile Tabs) should ALWAYS be enabled prior to " + "locking the aspect ratio. Failure to do so may cause excess, " + "undesirable space around and between the drawing of tab rows." + "
        " + "If this has not been done, click the Cancel button, make those " + "adjustments, and then click the " + buttonText + " button."); + if (showBestPracticesLink) { + text.append("

        " + "View the Best Practices Guide (this dialog " + "will close). This guide explains the importance of aspect " + "locking and documents procedures for successful creation of annotations " + "and scenes."); + } + text.append(""); + + return text; +} + + /** * Constructor. * @@ -116,24 +154,8 @@ m_brainBrowserWindow(brainBrowserWindow) { const QString mainInstructions("Do you want to lock the aspect ratio while entering annotations mode?"); - const QString supplementalInstructions("" - "Prior to locking the aspect ratio, the user should: " - "

          " - "
        • Adjust the size of the window" - "
        • Optionally enable Tile Tabs for a multi-tab view. " - "If annotating a Tile Tabs view, Tile Tabs " - "(View Menu -> Enter Tile Tabs) should ALWAYS be enabled prior to " - "locking the aspect ratio. Failure to do so may cause excess, " - "undesirable space around and between the drawing of tab rows." - "
        " - "If this has not been done, click the Cancel button, make those " - "adjustments, and then click the Toolbar's Annotate Mode button." - "

        " - "View the Best Practices Guide (this dialog " - "will close). This guide explains the importance of aspect " - "locking and documents procedures for successful creation of annotations " - "and scenes." - ""); + const QString supplementalInstructions(getLockingInstructionsText("Toolbar's Annotate Mode", + true)); const QString lockAspectInstructions("Locking the aspect ratio, and never unlocking the aspect " "ratio, ensures annotations stay in the correct location."); diff --git a/src/GuiQt/LockAspectWarningDialog.h b/src/GuiQt/LockAspectWarningDialog.h index 85f8c398c..eb160b20d 100644 --- a/src/GuiQt/LockAspectWarningDialog.h +++ b/src/GuiQt/LockAspectWarningDialog.h @@ -57,6 +57,14 @@ namespace caret { // ADD_NEW_METHODS_HERE + enum class LockingMode { + ENTER_ANNOTATIONS_MODE, + LOCK_ASPECT + }; + + static QString getLockingInstructionsText(const QString& buttonText, + const bool showBestPracticesLink); + private slots: void detailsLabelLinkActivated(const QString& link); -- GitLab From 7053dd1426632b8da8343e85d465e5b21b3ebcd4 Mon Sep 17 00:00:00 2001 From: John Harwell Date: Thu, 23 May 2019 14:15:10 -0500 Subject: [PATCH 333/427] For warning when Lock Aspect button is clicked, add an additional test so that the warning is not displayed when there is only one tab in the window. --- src/GuiQt/BrainBrowserWindow.cxx | 49 ++++++++++++++++++++------------ 1 file changed, 31 insertions(+), 18 deletions(-) diff --git a/src/GuiQt/BrainBrowserWindow.cxx b/src/GuiQt/BrainBrowserWindow.cxx index 2e673e09b..4ec74751e 100644 --- a/src/GuiQt/BrainBrowserWindow.cxx +++ b/src/GuiQt/BrainBrowserWindow.cxx @@ -1045,28 +1045,41 @@ BrainBrowserWindow::processToolBarLockWindowAndAllTabAspectTriggered(bool checke { static bool doNotShowAgainFlag(false); + /* + * Show warning if all of these conditions are met: + * + * (1) Lock Aspect being turned ON + * (2) Tile tabs is NOT selected + * (3) User has NOT set do not show again + * (4) There is more than one tab in the window + */ if (checked) { if ( ! isTileTabsSelected()) { if ( ! doNotShowAgainFlag) { - const QString labelText = LockAspectWarningDialog::getLockingInstructionsText("Lock Aspect", - false); - QLabel* warningLabel = new QLabel(labelText); - warningLabel->setWordWrap(true); - - const QString cbText("Do not show again. User will not be warned about aspect locking"); - QCheckBox* doNotShowAgainCheckBox = new QCheckBox(cbText); - - WuQDataEntryDialog warningDialog("Lock Aspect", - m_toolBarLockWindowAndAllTabAspectRatioButton, - WuQDialog::SCROLL_AREA_NEVER); - warningDialog.addWidget("", warningLabel); - warningDialog.addWidget("", doNotShowAgainCheckBox); + std::vector allTabContent; + m_toolbar->getAllTabContent(allTabContent); - const int result = warningDialog.exec(); - doNotShowAgainFlag = doNotShowAgainCheckBox->isChecked(); - if (result == WuQDataEntryDialog::Rejected) { - m_toolBarLockWindowAndAllTabAspectRatioAction->setChecked(false); - return; + if (allTabContent.size() > 1) { + const QString labelText = LockAspectWarningDialog::getLockingInstructionsText("Lock Aspect", + false); + QLabel* warningLabel = new QLabel(labelText); + warningLabel->setWordWrap(true); + + const QString cbText("Do not show again. User will not be warned about aspect locking"); + QCheckBox* doNotShowAgainCheckBox = new QCheckBox(cbText); + + WuQDataEntryDialog warningDialog("Lock Aspect", + m_toolBarLockWindowAndAllTabAspectRatioButton, + WuQDialog::SCROLL_AREA_NEVER); + warningDialog.addWidget("", warningLabel); + warningDialog.addWidget("", doNotShowAgainCheckBox); + + const int result = warningDialog.exec(); + doNotShowAgainFlag = doNotShowAgainCheckBox->isChecked(); + if (result == WuQDataEntryDialog::Rejected) { + m_toolBarLockWindowAndAllTabAspectRatioAction->setChecked(false); + return; + } } } } -- GitLab From f33ce16f8d2c94c95033bc79c91522a95dc720d0 Mon Sep 17 00:00:00 2001 From: John Harwell Date: Fri, 24 May 2019 10:10:37 -0500 Subject: [PATCH 334/427] Modified LockAspectWarning dialog so that it also used, under some conditions, when the user clicks the ToolBar's Lock Aspect button. --- src/GuiQt/BrainBrowserWindow.cxx | 49 ++---- src/GuiQt/LockAspectWarningDialog.cxx | 217 +++++++++++++++++--------- src/GuiQt/LockAspectWarningDialog.h | 26 ++- 3 files changed, 170 insertions(+), 122 deletions(-) diff --git a/src/GuiQt/BrainBrowserWindow.cxx b/src/GuiQt/BrainBrowserWindow.cxx index 4ec74751e..14a18ccc1 100644 --- a/src/GuiQt/BrainBrowserWindow.cxx +++ b/src/GuiQt/BrainBrowserWindow.cxx @@ -944,7 +944,7 @@ BrainBrowserWindow::createActionsUsedByToolBar() bool BrainBrowserWindow::changeInputModeToAnnotationsWarningDialog() { - LockAspectWarningDialog::Result result = LockAspectWarningDialog::runDialog(m_browserWindowIndex); + LockAspectWarningDialog::Result result = LockAspectWarningDialog::runDialogEnterAnnotationsMode(m_browserWindowIndex); bool okFlag = true; @@ -1043,45 +1043,16 @@ BrainBrowserWindow::lockAllTabAspectRatios(const bool checked) void BrainBrowserWindow::processToolBarLockWindowAndAllTabAspectTriggered(bool checked) { - static bool doNotShowAgainFlag(false); - - /* - * Show warning if all of these conditions are met: - * - * (1) Lock Aspect being turned ON - * (2) Tile tabs is NOT selected - * (3) User has NOT set do not show again - * (4) There is more than one tab in the window - */ if (checked) { - if ( ! isTileTabsSelected()) { - if ( ! doNotShowAgainFlag) { - std::vector allTabContent; - m_toolbar->getAllTabContent(allTabContent); - - if (allTabContent.size() > 1) { - const QString labelText = LockAspectWarningDialog::getLockingInstructionsText("Lock Aspect", - false); - QLabel* warningLabel = new QLabel(labelText); - warningLabel->setWordWrap(true); - - const QString cbText("Do not show again. User will not be warned about aspect locking"); - QCheckBox* doNotShowAgainCheckBox = new QCheckBox(cbText); - - WuQDataEntryDialog warningDialog("Lock Aspect", - m_toolBarLockWindowAndAllTabAspectRatioButton, - WuQDialog::SCROLL_AREA_NEVER); - warningDialog.addWidget("", warningLabel); - warningDialog.addWidget("", doNotShowAgainCheckBox); - - const int result = warningDialog.exec(); - doNotShowAgainFlag = doNotShowAgainCheckBox->isChecked(); - if (result == WuQDataEntryDialog::Rejected) { - m_toolBarLockWindowAndAllTabAspectRatioAction->setChecked(false); - return; - } - } - } + std::vector allTabContent; + m_toolbar->getAllTabContent(allTabContent); + const int32_t tabCount = static_cast(allTabContent.size()); + + const bool lockFlag = LockAspectWarningDialog::runDialogToolBarLockAspect(this, + tabCount); + if ( ! lockFlag) { + m_toolBarLockWindowAndAllTabAspectRatioAction->setChecked(false); + return; } } diff --git a/src/GuiQt/LockAspectWarningDialog.cxx b/src/GuiQt/LockAspectWarningDialog.cxx index ce5e28ba3..aa71e5556 100644 --- a/src/GuiQt/LockAspectWarningDialog.cxx +++ b/src/GuiQt/LockAspectWarningDialog.cxx @@ -53,7 +53,50 @@ using namespace caret; */ /** - * Run the lock aspect warning dialog and return the result. + * Run the dialog for when the user turns on the Lock Aspect button in the toolbar + * + * @param bbw + * Parent brain browser window + * @param numberOfTabsInWindow + * Number of tabs in the window + */ +bool +LockAspectWarningDialog::runDialogToolBarLockAspect(BrainBrowserWindow* bbw, + const int32_t numberOfTabsInWindow) +{ + CaretAssert(bbw); + + /* + * Show warning if all of these conditions are met: + * + * (1) User has NOT set do not show again + * (2) Tile tabs is NOT selected + * (3) There is more than one tab in the window + */ + if (s_doNotShowAgainLockAspectModeStatusFlag) { + return true; + } + if (bbw->isTileTabsSelected()) { + return true; + } + if (numberOfTabsInWindow <= 1) { + return true; + } + + LockAspectWarningDialog dialog(bbw, + Mode::TOOLBAR_LOCK_ASPECT); + + + const bool acceptedFlag = (dialog.exec() == LockAspectWarningDialog::Accepted); + s_doNotShowAgainLockAspectModeStatusFlag = dialog.isDoNotShowAgainChecked(); + + return acceptedFlag; +} + + +/** + * Run the lock aspect warning dialog when the user presses the + * Annotation mode button and return the result. * * @param browserWindowIndex * Index of the browser window. @@ -61,7 +104,7 @@ using namespace caret; * Result of user interaction with dialog. */ LockAspectWarningDialog::Result -LockAspectWarningDialog::runDialog(const int32_t browserWindowIndex) +LockAspectWarningDialog::runDialogEnterAnnotationsMode(const int32_t browserWindowIndex) { BrainBrowserWindow* bbw = GuiManager::get()->getBrowserWindowByWindowIndex(browserWindowIndex); CaretAssert(bbw); @@ -79,14 +122,15 @@ LockAspectWarningDialog::runDialog(const int32_t browserWindowIndex) return Result::NO_CHANGES; } - if (s_doNotShowAgainStatusFlag) { + if (s_doNotShowAgainEnterAnnotationsModeStatusFlag) { return Result::NO_CHANGES; } - LockAspectWarningDialog dialog(bbw); + LockAspectWarningDialog dialog(bbw, + Mode::ENTER_ANNOTATIONS_MODE); if (dialog.exec() == LockAspectWarningDialog::Accepted) { - s_doNotShowAgainStatusFlag = dialog.isDoNotShowAgainChecked(); + s_doNotShowAgainEnterAnnotationsModeStatusFlag = dialog.isDoNotShowAgainChecked(); return dialog.getOkResult(); } @@ -134,94 +178,114 @@ LockAspectWarningDialog::getLockingInstructionsText(const QString& buttonText, /** * Constructor. * - * @param tabMode - * The mode for tabs (selected or all) - * @param tileTabsEnabled - * True if tile tabs is enabled. - * @param browserWindowAspectLocked - * True if window aspect is locked. - * @param tabAspectLockedCount - * Count of tabs with aspect locked. - * @param tabCount - * Count of tabs. + * @param mode + * The mode for the dialog + * @param brainBrowserWindow + * Parent window for help dialog that is linked from this dialog * @param parent * The parent widget. */ -LockAspectWarningDialog::LockAspectWarningDialog(BrainBrowserWindow* brainBrowserWindow) +LockAspectWarningDialog::LockAspectWarningDialog(BrainBrowserWindow* brainBrowserWindow, + const Mode mode) : WuQDialogModal("Enter Annotations Mode", brainBrowserWindow), -m_brainBrowserWindow(brainBrowserWindow) +m_brainBrowserWindow(brainBrowserWindow), +m_mode(mode) { - const QString mainInstructions("Do you want to lock the aspect ratio while entering annotations mode?"); - - const QString supplementalInstructions(getLockingInstructionsText("Toolbar's Annotate Mode", - true)); - - const QString lockAspectInstructions("Locking the aspect ratio, and never unlocking the aspect " - "ratio, ensures annotations stay in the correct location."); - const QString leaveUnlockedInstructions("Advanced users may choose to lock and unlock the aspect ratio"); + QString buttonName; + switch (m_mode) { + case Mode::ENTER_ANNOTATIONS_MODE: + setWindowTitle("Enter Annotations Mode"); + buttonName = "Toolbar's Annotate Mode"; + break; + case Mode::TOOLBAR_LOCK_ASPECT: + setWindowTitle("Lock Aspect Ratio"); + buttonName = "Lock Aspect"; + break; + } - QLabel* mainInstructionsLabel = new QLabel(mainInstructions); - QFont font = mainInstructionsLabel->font(); - font.setPointSize(font.pointSize() * 1.4); - font.setBold(true); - mainInstructionsLabel->setFont(font); + const QString supplementalInstructions(getLockingInstructionsText(buttonName, + true)); QLabel* supplementalInstructionLabel = new QLabel(supplementalInstructions); supplementalInstructionLabel->setWordWrap(true); QObject::connect(supplementalInstructionLabel, &QLabel::linkActivated, this, &LockAspectWarningDialog::detailsLabelLinkActivated); - QLabel* lockAspectLabel = new QLabel(lockAspectInstructions); - lockAspectLabel->setWordWrap(true); - QLabel* lockAspectRadioButtonLabel = new QLabel("Lock Aspect Ratio (Recommended)"); - m_lockAspectRadioButton = new QRadioButton(); - - QLabel* leaveUnlockedAspectRadioButtonLabel = new QLabel("Leave Aspect Ratio Unlocked"); - m_leaveUnlockedAspectRadioButton = new QRadioButton(); - QLabel* leaveUnlockedLabel = new QLabel(leaveUnlockedInstructions); - - QButtonGroup* buttGroup = new QButtonGroup(this); - buttGroup->addButton(m_lockAspectRadioButton); - buttGroup->addButton(m_leaveUnlockedAspectRadioButton); - m_lockAspectRadioButton->setChecked(true); - - m_doNotShowAgainCheckBox = new QCheckBox("Do not show again. User will not be warned about " + m_doNotShowAgainCheckBox = new QCheckBox("Do not show this dialog again. User will not be warned about " "aspect locking and unlocking."); - - /* - * No text is added to readio buttons since - * radio buttons and labels seem to get a different - * height in a grid layout. - */ - const int COL_RADIO_EMPTY = 0; - const int COL_RADIO_BUTTON = 1; - const int COL_RADIO_LABEL = 2; - const int COL_RADIO_INFO = 3; - const int COL_RADIO_STRETCH = 4; - QGridLayout* buttonGridLayout = new QGridLayout(); - buttonGridLayout->setVerticalSpacing(4); - buttonGridLayout->setColumnMinimumWidth(COL_RADIO_EMPTY, 20); - buttonGridLayout->setColumnMinimumWidth(COL_RADIO_LABEL, 20); - buttonGridLayout->setColumnStretch(COL_RADIO_EMPTY, 0); - buttonGridLayout->setColumnStretch(COL_RADIO_BUTTON, 0); - buttonGridLayout->setColumnStretch(COL_RADIO_STRETCH, 100); - int row = 0; - buttonGridLayout->addWidget(m_lockAspectRadioButton, row, COL_RADIO_BUTTON); - buttonGridLayout->addWidget(lockAspectRadioButtonLabel, row, COL_RADIO_LABEL, 1, 2); - row++; - buttonGridLayout->addWidget(lockAspectLabel, row, COL_RADIO_INFO); - row++; - buttonGridLayout->addWidget(m_leaveUnlockedAspectRadioButton, row, COL_RADIO_BUTTON); - buttonGridLayout->addWidget(leaveUnlockedAspectRadioButtonLabel, row, COL_RADIO_LABEL, 1, 2); - row++; - buttonGridLayout->addWidget(leaveUnlockedLabel, row, COL_RADIO_INFO); + QWidget* dialogWidget = new QWidget; QVBoxLayout* dialogLayout = new QVBoxLayout(dialogWidget); - dialogLayout->addWidget(mainInstructionsLabel); - dialogLayout->addLayout(buttonGridLayout); - dialogLayout->addSpacing(10); + switch (m_mode) { + case Mode::ENTER_ANNOTATIONS_MODE: + { + const QString mainInstructions("Do you want to lock the aspect ratio while entering annotations mode?"); + + + const QString lockAspectInstructions("Locking the aspect ratio, and never unlocking the aspect " + "ratio, ensures annotations stay in the correct location."); + const QString leaveUnlockedInstructions("Advanced users may choose to lock and unlock the aspect ratio"); + + QLabel* mainInstructionsLabel = new QLabel(mainInstructions); + QFont font = mainInstructionsLabel->font(); + font.setPointSize(font.pointSize() * 1.4); + font.setBold(true); + mainInstructionsLabel->setFont(font); + + QLabel* lockAspectLabel = new QLabel(lockAspectInstructions); + lockAspectLabel->setWordWrap(true); + QLabel* lockAspectRadioButtonLabel = new QLabel("Lock Aspect Ratio (Recommended)"); + m_lockAspectRadioButton = new QRadioButton(); + + QLabel* leaveUnlockedAspectRadioButtonLabel = new QLabel("Leave Aspect Ratio Unlocked"); + m_leaveUnlockedAspectRadioButton = new QRadioButton(); + QLabel* leaveUnlockedLabel = new QLabel(leaveUnlockedInstructions); + + QButtonGroup* buttGroup = new QButtonGroup(this); + buttGroup->addButton(m_lockAspectRadioButton); + buttGroup->addButton(m_leaveUnlockedAspectRadioButton); + m_lockAspectRadioButton->setChecked(true); + + + /* + * No text is added to readio buttons since + * radio buttons and labels seem to get a different + * height in a grid layout. + */ + const int COL_RADIO_EMPTY = 0; + const int COL_RADIO_BUTTON = 1; + const int COL_RADIO_LABEL = 2; + const int COL_RADIO_INFO = 3; + const int COL_RADIO_STRETCH = 4; + QGridLayout* buttonGridLayout = new QGridLayout(); + buttonGridLayout->setVerticalSpacing(4); + buttonGridLayout->setColumnMinimumWidth(COL_RADIO_EMPTY, 20); + buttonGridLayout->setColumnMinimumWidth(COL_RADIO_LABEL, 20); + buttonGridLayout->setColumnStretch(COL_RADIO_EMPTY, 0); + buttonGridLayout->setColumnStretch(COL_RADIO_BUTTON, 0); + buttonGridLayout->setColumnStretch(COL_RADIO_STRETCH, 100); + int row = 0; + buttonGridLayout->addWidget(m_lockAspectRadioButton, row, COL_RADIO_BUTTON); + buttonGridLayout->addWidget(lockAspectRadioButtonLabel, row, COL_RADIO_LABEL, 1, 2); + row++; + buttonGridLayout->addWidget(lockAspectLabel, row, COL_RADIO_INFO); + row++; + buttonGridLayout->addWidget(m_leaveUnlockedAspectRadioButton, row, COL_RADIO_BUTTON); + buttonGridLayout->addWidget(leaveUnlockedAspectRadioButtonLabel, row, COL_RADIO_LABEL, 1, 2); + row++; + buttonGridLayout->addWidget(leaveUnlockedLabel, row, COL_RADIO_INFO); + + dialogLayout->addWidget(mainInstructionsLabel); + dialogLayout->addLayout(buttonGridLayout); + dialogLayout->addSpacing(10); + } + break; + case Mode::TOOLBAR_LOCK_ASPECT: + break; + } + dialogLayout->addWidget(supplementalInstructionLabel); dialogLayout->addSpacing(10); dialogLayout->addWidget(m_doNotShowAgainCheckBox); @@ -230,7 +294,6 @@ m_brainBrowserWindow(brainBrowserWindow) WuQDialogModal::SCROLL_AREA_NEVER); setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); - } /** diff --git a/src/GuiQt/LockAspectWarningDialog.h b/src/GuiQt/LockAspectWarningDialog.h index eb160b20d..6acd1af84 100644 --- a/src/GuiQt/LockAspectWarningDialog.h +++ b/src/GuiQt/LockAspectWarningDialog.h @@ -45,9 +45,18 @@ namespace caret { CANCEL }; - static Result runDialog(const int32_t browserWindowIndex); + enum class Mode { + ENTER_ANNOTATIONS_MODE, + TOOLBAR_LOCK_ASPECT + }; + + static bool runDialogToolBarLockAspect(BrainBrowserWindow* bbw, + const int32_t numberOfTabsInWindow); + + static Result runDialogEnterAnnotationsMode(const int32_t browserWindowIndex); - LockAspectWarningDialog(BrainBrowserWindow* brainBrowserWindow); + LockAspectWarningDialog(BrainBrowserWindow* brainBrowserWindow, + const Mode mode); virtual ~LockAspectWarningDialog(); @@ -75,22 +84,27 @@ namespace caret { BrainBrowserWindow* m_brainBrowserWindow; + const Mode m_mode; + Result m_result = Result::CANCEL; QCheckBox* m_doNotShowAgainCheckBox; - QRadioButton* m_lockAspectRadioButton; + QRadioButton* m_lockAspectRadioButton = NULL; + + QRadioButton* m_leaveUnlockedAspectRadioButton = NULL; - QRadioButton* m_leaveUnlockedAspectRadioButton; + static bool s_doNotShowAgainEnterAnnotationsModeStatusFlag; - static bool s_doNotShowAgainStatusFlag; + static bool s_doNotShowAgainLockAspectModeStatusFlag; // ADD_NEW_MEMBERS_HERE }; #ifdef __LOCK_ASPECT_WARNING_DIALOG_DECLARE__ - bool LockAspectWarningDialog::s_doNotShowAgainStatusFlag = false; + bool LockAspectWarningDialog::s_doNotShowAgainEnterAnnotationsModeStatusFlag = false; + bool LockAspectWarningDialog::s_doNotShowAgainLockAspectModeStatusFlag = false; #endif // __LOCK_ASPECT_WARNING_DIALOG_DECLARE__ } // namespace -- GitLab From ac9ae2debcb7ea4541e9ea412dcf4ea023851027 Mon Sep 17 00:00:00 2001 From: John Harwell Date: Fri, 24 May 2019 14:03:19 -0500 Subject: [PATCH 335/427] Added a priority for textures in experimental volume texture drawing. --- src/Brain/BrainOpenGLVolumeTextureSliceDrawing.cxx | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/Brain/BrainOpenGLVolumeTextureSliceDrawing.cxx b/src/Brain/BrainOpenGLVolumeTextureSliceDrawing.cxx index be093932a..8ade2104f 100644 --- a/src/Brain/BrainOpenGLVolumeTextureSliceDrawing.cxx +++ b/src/Brain/BrainOpenGLVolumeTextureSliceDrawing.cxx @@ -3085,6 +3085,11 @@ BrainOpenGLVolumeTextureSliceDrawing::drawObliqueSliceWithOutlines(const VolumeS textureInfo.m_textureID = textureID; textureInfo.m_maxSTR = maxStr; volumeTextureInfo.insert(std::make_pair(vf, textureInfo)); + + /* 1.0 is highest priority texture so that texture is resident */ + const GLclampf priority(1.0); + glPrioritizeTextures(1, &textureID, &priority); + if (debugFlag) std::cout << "Created texture: " << textureID << std::endl; } else { -- GitLab From e64689a3ca32a5c4ee9a11501e52023f96c1ea1d Mon Sep 17 00:00:00 2001 From: John Harwell Date: Fri, 24 May 2019 16:17:57 -0500 Subject: [PATCH 336/427] Fix macro error cause by volume interpolation masking menu items not have Qt object names. --- .../BrainBrowserWindowToolBarSliceSelection.cxx | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/src/GuiQt/BrainBrowserWindowToolBarSliceSelection.cxx b/src/GuiQt/BrainBrowserWindowToolBarSliceSelection.cxx index 25fa7dedb..cf9eb4754 100644 --- a/src/GuiQt/BrainBrowserWindowToolBarSliceSelection.cxx +++ b/src/GuiQt/BrainBrowserWindowToolBarSliceSelection.cxx @@ -843,6 +843,8 @@ BrainBrowserWindowToolBarSliceSelection::volumeIdentificationToggled(bool value) void BrainBrowserWindowToolBarSliceSelection::obliqueMaskingActionTriggered(bool) { + static bool addMacroSupportStaticFlag(true); + BrowserTabContent* browserTabContent = this->getTabContentFromSelectedTab(); if (browserTabContent == NULL) { return; @@ -856,13 +858,20 @@ BrainBrowserWindowToolBarSliceSelection::obliqueMaskingActionTriggered(bool) QAction* selectedAction = NULL; for (auto maskEnum : allMaskEnums) { QAction* action = maskActionGroup->addAction(VolumeSliceInterpolationEdgeEffectsMaskingEnum::toGuiName(maskEnum)); + action->setObjectName(m_obliqueMaskingAction->objectName() + + ":" + + VolumeSliceInterpolationEdgeEffectsMaskingEnum::toName(maskEnum)); action->setCheckable(true); action->setData(VolumeSliceInterpolationEdgeEffectsMaskingEnum::toIntegerCode(maskEnum)); if (maskEnum == browserTabContent->getVolumeSliceInterpolationEdgeEffectsMaskingType()) { selectedAction = action; } - WuQMacroManager::instance()->addMacroSupportToObject(action, - "Select " + action->text() + " oblique sampling"); + + if (addMacroSupportStaticFlag) { + addMacroSupportStaticFlag = false; + WuQMacroManager::instance()->addMacroSupportToObject(action, + "Select " + action->text() + " oblique sampling"); + } obliqueMaskingMenu.addAction(action); } -- GitLab From 54bed3db6956e945f6fe18cac66b36cc56ffc4e6 Mon Sep 17 00:00:00 2001 From: John Harwell Date: Tue, 28 May 2019 15:11:49 -0500 Subject: [PATCH 337/427] Fixed crash in experimental volume slice drawing using a texture when a volume dimension greater than 256. Also fix compilation warnings on a couple of systems. --- .../BrainOpenGLVolumeTextureSliceDrawing.cxx | 25 ++++++++++++++----- 1 file changed, 19 insertions(+), 6 deletions(-) diff --git a/src/Brain/BrainOpenGLVolumeTextureSliceDrawing.cxx b/src/Brain/BrainOpenGLVolumeTextureSliceDrawing.cxx index 8ade2104f..5ae74ebd4 100644 --- a/src/Brain/BrainOpenGLVolumeTextureSliceDrawing.cxx +++ b/src/Brain/BrainOpenGLVolumeTextureSliceDrawing.cxx @@ -1446,7 +1446,7 @@ BrainOpenGLVolumeTextureSliceDrawing::drawAxesCrosshairsOblique(const VolumeSlic const float gapPercentViewportHeight = SessionManager::get()->getCaretPreferences()->getVolumeCrosshairGap(); const float gapMM = GraphicsUtilitiesOpenGL::convertPercentageOfViewportHeightToMillimeters(gapPercentViewportHeight); - const std::array sliceCoordinates { sliceCoordinatesIn[0], sliceCoordinatesIn[1], sliceCoordinatesIn[2] }; + const std::array sliceCoordinates = { sliceCoordinatesIn[0], sliceCoordinatesIn[1], sliceCoordinatesIn[2] }; GLboolean depthEnabled = GL_FALSE; glGetBooleanv(GL_DEPTH_TEST, &depthEnabled); @@ -1459,7 +1459,7 @@ BrainOpenGLVolumeTextureSliceDrawing::drawAxesCrosshairsOblique(const VolumeSlic float trans[3]; m_browserTabContent->getTranslation(trans); - std::array horizTrans { trans[0], trans[1], trans[2] }; + std::array horizTrans = { trans[0], trans[1], trans[2] }; switch (sliceViewPlane) { case VolumeSliceViewPlaneEnum::ALL: @@ -2558,6 +2558,22 @@ createTextureName(BrainOpenGLFixedPipeline* fixedPipelineDrawing, const int32_t tabIndex, float maxStrOut[3]) { + std::vector dims(5); + vf->getDimensions(dims); + int64_t textureDims = 256; + const int64_t dimLargest = *std::max_element(dims.begin(), dims.begin() + 3); + if (dimLargest > 512) { + const QString msg("Volume dimensions too large for texture support. Dimensions=" + + AString::fromNumbers(&dims[0], 3, ",") + + " for volume " + + vf->getFileNameNoPath()); + CaretLogSevere(msg); + return 0; + } + else if (dimLargest > 256) { + textureDims = 512; + } + GLuint textureName(0); glPushClientAttrib(GL_CLIENT_PIXEL_STORE_BIT); @@ -2624,9 +2640,7 @@ createTextureName(BrainOpenGLFixedPipeline* fixedPipelineDrawing, glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE); } - std::vector dims(5); - vf->getDimensions(dims); - + const int64_t mapIndex(0); const int64_t numberOfSlices = dims[2]; const int64_t numberOfRows = dims[1]; @@ -2638,7 +2652,6 @@ createTextureName(BrainOpenGLFixedPipeline* fixedPipelineDrawing, std::vector rgbaColors; rgbaColors.reserve(numberOfBytes); - const int64_t textureDims = 256; const int64_t textureBytes = (textureDims * textureDims * textureDims * 4); std::vector texture(textureBytes, 0); -- GitLab From 77094d174d9cb7faa12308b6a14201708cdf56cc Mon Sep 17 00:00:00 2001 From: John Harwell Date: Tue, 28 May 2019 16:00:52 -0500 Subject: [PATCH 338/427] Implement WB-858: Do not yoke the volume slice plane (Axial, Coronal, Parasagittal, or All) --- src/Brain/BrowserTabContent.cxx | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/Brain/BrowserTabContent.cxx b/src/Brain/BrowserTabContent.cxx index 7cc111d1f..10f8bb5cc 100644 --- a/src/Brain/BrowserTabContent.cxx +++ b/src/Brain/BrowserTabContent.cxx @@ -4836,7 +4836,9 @@ BrowserTabContent::updateBrainModelYokedBrowserTabs() *btc->m_flatSurfaceViewingTransformation = *m_flatSurfaceViewingTransformation; *btc->m_cerebellumViewingTransformation = *m_cerebellumViewingTransformation; *btc->m_volumeSliceViewingTransformation = *m_volumeSliceViewingTransformation; + const VolumeSliceViewPlaneEnum::Enum slicePlane = btc->m_volumeSliceSettings->getSliceViewPlane(); *btc->m_volumeSliceSettings = *m_volumeSliceSettings; + btc->m_volumeSliceSettings->setSliceViewPlane(slicePlane); // do not yoke the slice plane *btc->m_obliqueVolumeRotationMatrix = *m_obliqueVolumeRotationMatrix; *btc->m_clippingPlaneGroup = *m_clippingPlaneGroup; btc->m_identificationUpdatesVolumeSlices = m_identificationUpdatesVolumeSlices; -- GitLab From 3d5b8f97d3551c59b8d40c188bb50853f4549d15 Mon Sep 17 00:00:00 2001 From: John Harwell Date: Tue, 28 May 2019 16:31:58 -0500 Subject: [PATCH 339/427] WB-858 Do not yoke the volume slice plane (Axial, Coronal, Parasagittal, or All): Fixed change of slice plane caused by a change in tab yoking. --- src/Brain/BrowserTabContent.cxx | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/Brain/BrowserTabContent.cxx b/src/Brain/BrowserTabContent.cxx index 10f8bb5cc..8b784f509 100644 --- a/src/Brain/BrowserTabContent.cxx +++ b/src/Brain/BrowserTabContent.cxx @@ -4733,7 +4733,9 @@ BrowserTabContent::setBrainModelYokingGroup(const YokingGroupEnum::Enum brainMod *m_flatSurfaceViewingTransformation = *btc->m_flatSurfaceViewingTransformation; *m_cerebellumViewingTransformation = *btc->m_cerebellumViewingTransformation; *m_volumeSliceViewingTransformation = *btc->m_volumeSliceViewingTransformation; + const VolumeSliceViewPlaneEnum::Enum slicePlane = m_volumeSliceSettings->getSliceViewPlane(); *m_volumeSliceSettings = *btc->m_volumeSliceSettings; + m_volumeSliceSettings->setSliceViewPlane(slicePlane); // do not yoke the slice plane *m_obliqueVolumeRotationMatrix = *btc->m_obliqueVolumeRotationMatrix; *m_clippingPlaneGroup = *btc->m_clippingPlaneGroup; m_identificationUpdatesVolumeSlices = btc->m_identificationUpdatesVolumeSlices; -- GitLab From e5fbc507b04619f34181aa20d55edade24604a03 Mon Sep 17 00:00:00 2001 From: John Harwell Date: Wed, 29 May 2019 09:53:33 -0500 Subject: [PATCH 340/427] For the experimental texture volume drawing, changed the texture 'magnification filter' to GL_NEAREST. When zoomed in on a slice, the drawing is much like the current cubic interpolation drawing. The different is when the volume slice is rotated. The interpolated drawing draws voxels whose edges horizontal and vertical (aligned with screen axes). Whereas, in texture drawing the voxel edges are aligned with the stereotaxic axes and thus the voxels edges rotate with the slice rotations. --- .../BrainOpenGLVolumeTextureSliceDrawing.cxx | 53 +++++++++++++++---- 1 file changed, 43 insertions(+), 10 deletions(-) diff --git a/src/Brain/BrainOpenGLVolumeTextureSliceDrawing.cxx b/src/Brain/BrainOpenGLVolumeTextureSliceDrawing.cxx index 5ae74ebd4..6d9fb8a3e 100644 --- a/src/Brain/BrainOpenGLVolumeTextureSliceDrawing.cxx +++ b/src/Brain/BrainOpenGLVolumeTextureSliceDrawing.cxx @@ -2598,19 +2598,52 @@ createTextureName(BrainOpenGLFixedPipeline* fixedPipelineDrawing, if (vf->isMappedWithPalette()) { /* - * Magnification: pixel is smaller than a texel - * Minification: pixel is larger than a texel and thus multiple texels - * are mapped to the pixel - * GL_NEAREST: Use texel with coordinates nearest center of pixel - * Faster but may cause artifacts - * GL_LINEAR: Use weighted linear average of 2x2x2 texel array - * nearest center of pixel - * Smoother appearance but may be slower + * This combination MIN=Linear, MAG=Nearest + * seems to produce voxel drawing that is + * nearly identical to cubic interpolation when zoomed in so + * that the voxels are large. The difference is when the slices + * are rotated; In cubic interpolation, the "scan lines" are + * horizontal (left to right on the screen) but with texture, + * the "scan lines" follow the volume rotation. + * + * From the OpenGL documentation (man page) + * + * GL_TEXTURE_MIN_FILTER - The texture minifying function is used + * whenever the pixel being textured maps to an area greater than one + * texture element. + * GL_NEAREST - Returns the value of the texture element that is + * nearest (in Manhattan distance) to the center of + * the pixel being textured. + * GL_LINEAR - Returns the weighted average of the four texture + * elements that are closest to the center of the + * pixel being textured. + * + * Pixel area is GREATER THAN texel area so ZOOMED OUT + * + * Thus, Pixel contains more than one texel */ - /* Linear filtering causes white line around left and top edges */ - glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + /* + * GL_TEXTURE_MAG_FILTER - The texture magnification function + * is used when the pixel being textured maps to an area less than or + * equal to one texture element. + * GL_NEAREST - Returns the value of the texture element that is + * nearest (in Manhattan distance) to the center of + * the pixel being textured. + * GL_LINEAR Returns the weighted average of the four texture + * elements that are closest to the center of the + * pixel being textured. + * + * Pixel area is LESS THAN Texel area so ZOOMED IN + * + * Thus, pixel may be inside a texel + * + * If GL_LINEAR is used the voxel "blockiness" is smoothed out + */ + glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + + GLfloat borderColor[4] = { 0.0, 0.0, 0.0, 0.0 }; glTexParameterfv(GL_TEXTURE_3D, GL_TEXTURE_BORDER_COLOR, borderColor); -- GitLab From 84877ba2366b9519040c2d8621079e654bccbefd Mon Sep 17 00:00:00 2001 From: John Harwell Date: Wed, 29 May 2019 14:48:25 -0500 Subject: [PATCH 341/427] Code that processed mouse wheel (track pad is reported as mouse wheel event) was using an obsolete function, QWheelEvent::delete(), and it was replaced with QWheelEvent::pixelDelta() and QWheelEvent::angleDelta() as a fallback. This seems to have fixed the sort oscillation that was noticed when trying to use the track pad on Macs. --- src/GuiQt/BrainOpenGLWidget.cxx | 65 +++++++++++++++++++++------------ 1 file changed, 42 insertions(+), 23 deletions(-) diff --git a/src/GuiQt/BrainOpenGLWidget.cxx b/src/GuiQt/BrainOpenGLWidget.cxx index 7042a31c7..577445a2e 100644 --- a/src/GuiQt/BrainOpenGLWidget.cxx +++ b/src/GuiQt/BrainOpenGLWidget.cxx @@ -782,31 +782,50 @@ BrainOpenGLWidget::wheelEvent(QWheelEvent* we) { const int wheelX = we->x(); const int wheelY = this->windowHeight[this->windowIndex] - we->y(); - int delta = we->delta(); - delta = MathFunctions::limitRange(delta, -2, 2); - - /* - * Use location of mouse press so that the model - * being manipulated does not change if mouse moves - * out of its viewport without releasing the mouse - * button. - */ - const BrainOpenGLViewportContent* viewportContent = this->getViewportContentAtXY(wheelX, - wheelY); - if (viewportContent != NULL) { - MouseEvent mouseEvent(viewportContent, - this, - this->windowIndex, - wheelX, - wheelY, - 0, - delta, - 0, - 0, - this->mouseNewDraggingStartedFlag); - this->selectedUserInputProcessor->mouseLeftDragWithCtrl(mouseEvent); + const QPoint pixelDelta = we->pixelDelta(); + const QPoint degrees = we->angleDelta(); + int delta(0); + if ( ! pixelDelta.isNull()) { + delta = pixelDelta.y(); + } + else if ( ! degrees.isNull()) { + delta = degrees.y(); } + if (delta != 0) { + if ( ! we->inverted()) { + delta = -delta; + } + + /* + * If not limited, it is way too fast + */ + const int limitValue(8); + delta = MathFunctions::limitRange(delta, -limitValue, limitValue); + + /* + * Use location of mouse press so that the model + * being manipulated does not change if mouse moves + * out of its viewport without releasing the mouse + * button. + */ + const BrainOpenGLViewportContent* viewportContent = this->getViewportContentAtXY(wheelX, + wheelY); + if (viewportContent != NULL) { + MouseEvent mouseEvent(viewportContent, + this, + this->windowIndex, + wheelX, + wheelY, + 0, + delta, + 0, + 0, + this->mouseNewDraggingStartedFlag); + this->selectedUserInputProcessor->mouseLeftDragWithCtrl(mouseEvent); + } + } + we->accept(); } -- GitLab From b0f028d870dd0d0d38e32d99a8da29daa8bb205d Mon Sep 17 00:00:00 2001 From: John Harwell Date: Wed, 29 May 2019 15:19:53 -0500 Subject: [PATCH 342/427] When an identification operation was performed, the identification window became the top window and the keyboard focus was moved to the identification window. This has been changed so that the keyboard focus no longer is transferred to the identification window and the keyboard focus remains with the main window. --- src/GuiQt/UserInputModeView.cxx | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/GuiQt/UserInputModeView.cxx b/src/GuiQt/UserInputModeView.cxx index f139cae63..ef8717a95 100644 --- a/src/GuiQt/UserInputModeView.cxx +++ b/src/GuiQt/UserInputModeView.cxx @@ -117,7 +117,13 @@ UserInputModeView::processModelViewIdentification(BrainOpenGLViewportContent* vi const int32_t tabIndex = btc->getTabNumber(); GuiManager::get()->processIdentification(tabIndex, selectionManager, - openGLWidget); + openGLWidget); + + /* + * Keep the main window as the active window NOT the identification window + */ + openGLWidget->parentWidget()->activateWindow(); + openGLWidget->parentWidget()->raise(); } } -- GitLab From 74b443aecbccfb397931c3ec36bdf65ceec9b98f Mon Sep 17 00:00:00 2001 From: John Harwell Date: Wed, 29 May 2019 15:49:10 -0500 Subject: [PATCH 343/427] Previous commit for keeping main window active after identification operation was obscuring the identification window if it was behind the main window. This fix (at least on Mac) keeps the main window as the active window (mouse focus) but keeps identification window visible over the main window. --- src/GuiQt/UserInputModeView.cxx | 1 - 1 file changed, 1 deletion(-) diff --git a/src/GuiQt/UserInputModeView.cxx b/src/GuiQt/UserInputModeView.cxx index ef8717a95..f47640d3c 100644 --- a/src/GuiQt/UserInputModeView.cxx +++ b/src/GuiQt/UserInputModeView.cxx @@ -123,7 +123,6 @@ UserInputModeView::processModelViewIdentification(BrainOpenGLViewportContent* vi * Keep the main window as the active window NOT the identification window */ openGLWidget->parentWidget()->activateWindow(); - openGLWidget->parentWidget()->raise(); } } -- GitLab From 0d21ff9a7c6a83569cd201cfc04483da9aba69be Mon Sep 17 00:00:00 2001 From: John Harwell Date: Wed, 29 May 2019 16:06:51 -0500 Subject: [PATCH 344/427] Unfortunately, the previous commit for keeping the main window active does not function as desires on Linux. It appears (on Linux) that the active window is always the top most window. The functionality remains for Mac so that the Identification window is over the main window after an ID but the main window retains mouse focus. A difference between the main window on Linux is that if the main window is not active, clicking any control (slice spin box), makes the window active. On Mac it is different as the user must click the titlebar to make the window active (clicking a control such as slice spin box does not activate the window). --- src/GuiQt/UserInputModeView.cxx | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/GuiQt/UserInputModeView.cxx b/src/GuiQt/UserInputModeView.cxx index f47640d3c..c92d5b611 100644 --- a/src/GuiQt/UserInputModeView.cxx +++ b/src/GuiQt/UserInputModeView.cxx @@ -120,10 +120,14 @@ UserInputModeView::processModelViewIdentification(BrainOpenGLViewportContent* vi openGLWidget); /* - * Keep the main window as the active window NOT the identification window + * Keep the main window as the active window NOT the identification window. + * This does not work correctly on Linux as the identication window + * may hide behind the main window. */ +#ifdef CARET_OS_MACOSX openGLWidget->parentWidget()->activateWindow(); - } +#endif + } } /** -- GitLab From 2c21aac69c8cc73928c8dd89a659de0c03820665 Mon Sep 17 00:00:00 2001 From: John Harwell Date: Fri, 31 May 2019 08:55:52 -0500 Subject: [PATCH 345/427] This change only affects the usage of QOpenGLWidget which is used with Qt 5.12.0 on John's Mac. All build systems use QGLWidget with Qt 5.7.x. Anyway, there is a bug in QOpenGLWidget::repaint() as it does not function as described in the Qt documentation. It is supposed to repaint the graphics (call paintGL()), before returning. However, it does not and functions like QOpenGLWidget::update() and returns with the graphics being updated later by Qt. There are two Qt bug reports for this (QT-BUG-74404 and QTBUG-53107). A previous work-around made a call to QApplication::processEvents() after the call to repaint() and it appeared to fix the problem. However, annotation dragging was found to be unusable (annotations kept moving after release of the mouse and did not follow the mouse) due to processEvents() allowing mouse event while still updating graphics. Now, the call to processEvents() has been replaced with a call to grabFrameBuffer(). This captures an image and I assume it waits until the graphics have updated before capturing the image. This fixes the problem with annotation dragging. --- src/GuiQt/BrainOpenGLWidget.cxx | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/src/GuiQt/BrainOpenGLWidget.cxx b/src/GuiQt/BrainOpenGLWidget.cxx index 577445a2e..ef2d437c7 100644 --- a/src/GuiQt/BrainOpenGLWidget.cxx +++ b/src/GuiQt/BrainOpenGLWidget.cxx @@ -1960,6 +1960,8 @@ BrainOpenGLWidget::repaintGraphics() #ifdef WORKBENCH_USE_QT5_QOPENGL_WIDGET /* + * As of QT 5.12.0. + * * When using QOpenGLWidget, calling repaint() returns before * it calls paintGL() so graphics are not updated even though * the documentation states that drawing will complete during @@ -1969,10 +1971,17 @@ BrainOpenGLWidget::repaintGraphics() * Two Qt bug reports have been submitted by others: * QTBUG-74404, QTBUG-53107. * - * For now, allowing events to process seems to allow the - * graphics to update. + * A previous kludge to fix this problem made a call to + * QApplication::processEvents(). However, it was found + * to cause a problem with annotation dragging as multiple + * mouse events were getting issued. + * Commit: 666b0e7c3d8d443aa9668a5cff986f7b83158488 + * + * Now, the kludge is to call grabFramebuffer() which I + * assume has to complete drawing before capturing an + * image. This fixes the problem with dragging annotations. */ - QApplication::processEvents(); + (void)grabFramebuffer(); #endif } -- GitLab From 84a2137f2611a8f3a011776f0065d07b35f82f54 Mon Sep 17 00:00:00 2001 From: John Harwell Date: Mon, 3 Jun 2019 12:28:03 -0500 Subject: [PATCH 346/427] Fix charting bug with scalar data-series files. If the user clicked in a matrix view, line charts for the file were not updating to show the row's data. --- src/Brain/ChartTwoOverlay.cxx | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/src/Brain/ChartTwoOverlay.cxx b/src/Brain/ChartTwoOverlay.cxx index dc5602ba4..572ab36f1 100644 --- a/src/Brain/ChartTwoOverlay.cxx +++ b/src/Brain/ChartTwoOverlay.cxx @@ -1010,10 +1010,21 @@ ChartTwoOverlay::setSelectionData(CaretMappableDataFile* selectedMapFile, ChartableTwoFileDelegate* chartDelegate = m_selectedMapFile->getChartingDelegate(); CaretAssert(chartDelegate); matrixChart = chartDelegate->getMatrixCharting(); -// ChartableTwoFileMatrixChart* matrixChart = chartDelegate->getMatrixCharting(); -// CaretAssert(matrixChart); -// matrixChart->setSelectedRowColumnIndex(m_parentChartTwoOverlaySet->m_tabIndex, -// selectedMapIndex); + + if (m_selectedMapFile->getDataFileType() == DataFileTypeEnum::CONNECTIVITY_SCALAR_DATA_SERIES) { + ChartableTwoFileLineSeriesChart* lineChart = chartDelegate->getLineSeriesCharting(); + if (lineChart != NULL) { + switch (lineChart->getLineSeriesContentType()) { + case ChartTwoLineSeriesContentTypeEnum::LINE_SERIES_CONTENT_UNSUPPORTED: + break; + case ChartTwoLineSeriesContentTypeEnum::LINE_SERIES_CONTENT_BRAINORDINATE_DATA: + break; + case ChartTwoLineSeriesContentTypeEnum::LINE_SERIES_CONTENT_ROW_SCALAR_DATA: + lineSeriesChart = lineChart; + break; + } + } + } } break; } -- GitLab From cacf0f492513d930f23e8c48814c8fa376f06049 Mon Sep 17 00:00:00 2001 From: Tim Coalson Date: Mon, 3 Jun 2019 17:06:30 -0500 Subject: [PATCH 347/427] fix row/column mixup causing crash on cifti separate ROW with more columns than rows --- src/Algorithms/AlgorithmCiftiSeparate.cxx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Algorithms/AlgorithmCiftiSeparate.cxx b/src/Algorithms/AlgorithmCiftiSeparate.cxx index d346768d5..5cfd6a3e4 100644 --- a/src/Algorithms/AlgorithmCiftiSeparate.cxx +++ b/src/Algorithms/AlgorithmCiftiSeparate.cxx @@ -614,7 +614,7 @@ AlgorithmCiftiSeparate::AlgorithmCiftiSeparate(ProgressObject* myProgObj, const volOut->reinitialize(newdims, mySform); volOut->setValueAllVoxels(0.0f); const CiftiMappingType& myNamesMap = *(myXML.getMap(1 - myDir)); - for (int j = 0; j < rowSize; ++j) + for (int j = 0; j < colSize; ++j) { volOut->setMapName(j, myNamesMap.getIndexName(j)); } -- GitLab From 51d2e2535954859744cbcb48f0226bd2f8560a02 Mon Sep 17 00:00:00 2001 From: Tim Coalson Date: Mon, 3 Jun 2019 18:20:07 -0500 Subject: [PATCH 348/427] additional similar copy/paste errors --- src/Algorithms/AlgorithmCiftiSeparate.cxx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Algorithms/AlgorithmCiftiSeparate.cxx b/src/Algorithms/AlgorithmCiftiSeparate.cxx index 5cfd6a3e4..271ea6fb0 100644 --- a/src/Algorithms/AlgorithmCiftiSeparate.cxx +++ b/src/Algorithms/AlgorithmCiftiSeparate.cxx @@ -257,7 +257,7 @@ AlgorithmCiftiSeparate::AlgorithmCiftiSeparate(ProgressObject* myProgObj, const metricOut->setNumberOfNodesAndColumns(numNodes, colSize); metricOut->setStructure(myStruct); const CiftiMappingType& myNamesMap = *(myXML.getMap(1 - myDir)); - for (int j = 0; j < rowSize; ++j) + for (int j = 0; j < colSize; ++j) { metricOut->setMapName(j, myNamesMap.getIndexName(j)); } @@ -365,7 +365,7 @@ AlgorithmCiftiSeparate::AlgorithmCiftiSeparate(ProgressObject* myProgObj, const labelOut->setNumberOfNodesAndColumns(numNodes, colSize); labelOut->setStructure(myStruct); const CiftiMappingType& myNamesMap = *(myXML.getMap(1 - myDir)); - for (int j = 0; j < rowSize; ++j) + for (int j = 0; j < colSize; ++j) { labelOut->setMapName(j, myNamesMap.getIndexName(j)); } @@ -496,7 +496,7 @@ AlgorithmCiftiSeparate::AlgorithmCiftiSeparate(ProgressObject* myProgObj, const volOut->reinitialize(newdims, mySform); volOut->setValueAllVoxels(0.0f); const CiftiMappingType& myNamesMap = *(myXML.getMap(1 - myDir)); - for (int j = 0; j < rowSize; ++j) + for (int j = 0; j < colSize; ++j) { volOut->setMapName(j, myNamesMap.getIndexName(j)); } -- GitLab From 7bdc2519e41f5e63cfeac303de81430f47990b21 Mon Sep 17 00:00:00 2001 From: John Harwell Date: Tue, 4 Jun 2019 11:48:01 -0500 Subject: [PATCH 349/427] Map yoking combo boxes were set to a fixed size on Mac. On newer Qt (5.12.0), these combo boxes were too small. This logic was removed and QComboBox::setSizeAdjustPolicyt() is used to size the combo boxes to their content. --- src/GuiQt/ChartLinesSelectionViewController.cxx | 3 --- src/GuiQt/ChartMatrixSeriesSelectionViewController.cxx | 3 --- src/GuiQt/ChartTwoOverlayViewController.cxx | 3 --- src/GuiQt/MapYokingGroupComboBox.cxx | 3 ++- src/GuiQt/OverlayViewController.cxx | 3 --- 5 files changed, 2 insertions(+), 13 deletions(-) diff --git a/src/GuiQt/ChartLinesSelectionViewController.cxx b/src/GuiQt/ChartLinesSelectionViewController.cxx index 76c21e2b1..748fc6545 100644 --- a/src/GuiQt/ChartLinesSelectionViewController.cxx +++ b/src/GuiQt/ChartLinesSelectionViewController.cxx @@ -189,9 +189,6 @@ ChartLinesSelectionViewController::updateSelectionViewController() yokeComboBox = new MapYokingGroupComboBox(this); yokeComboBox->getWidget()->setStatusTip("Synchronize enabled status and map indices)"); yokeComboBox->getWidget()->setToolTip("Yoke to Overlay Mapped Files"); -#ifdef CARET_OS_MACOSX - yokeComboBox->getWidget()->setFixedWidth(yokeComboBox->getWidget()->sizeHint().width() - 20); -#endif // CARET_OS_MACOSX QObject::connect(yokeComboBox, SIGNAL(itemActivated()), m_signalMapperBrainordinateYokingComboBox, SLOT(map())); m_signalMapperBrainordinateYokingComboBox->setMapping(yokeComboBox, i); diff --git a/src/GuiQt/ChartMatrixSeriesSelectionViewController.cxx b/src/GuiQt/ChartMatrixSeriesSelectionViewController.cxx index 37e68c1d1..a67c888f0 100644 --- a/src/GuiQt/ChartMatrixSeriesSelectionViewController.cxx +++ b/src/GuiQt/ChartMatrixSeriesSelectionViewController.cxx @@ -136,9 +136,6 @@ m_browserWindowIndex(browserWindowIndex) m_matrixSeriesYokingComboBox = new MapYokingGroupComboBox(this); m_matrixSeriesYokingComboBox->getWidget()->setStatusTip("Synchronize enabled status and map indices)"); m_matrixSeriesYokingComboBox->getWidget()->setToolTip("Yoke to Overlay Mapped Files"); -#ifdef CARET_OS_MACOSX - m_matrixSeriesYokingComboBox->getWidget()->setFixedWidth(m_matrixSeriesYokingComboBox->getWidget()->sizeHint().width() - 20); -#endif // CARET_OS_MACOSX QObject::connect(m_matrixSeriesYokingComboBox, SIGNAL(itemActivated()), this, SLOT(matrixSeriesYokingGroupActivated())); diff --git a/src/GuiQt/ChartTwoOverlayViewController.cxx b/src/GuiQt/ChartTwoOverlayViewController.cxx index ed18907e9..f75c243be 100644 --- a/src/GuiQt/ChartTwoOverlayViewController.cxx +++ b/src/GuiQt/ChartTwoOverlayViewController.cxx @@ -262,9 +262,6 @@ m_chartOverlay(NULL) m_mapRowOrColumnYokingGroupComboBox = new MapYokingGroupComboBox(this); m_mapRowOrColumnYokingGroupComboBox->getWidget()->setStatusTip("Synchronize enabled status and map indices)"); m_mapRowOrColumnYokingGroupComboBox->getWidget()->setToolTip("Yoke to Overlay Mapped Files"); -#ifdef CARET_OS_MACOSX - m_mapRowOrColumnYokingGroupComboBox->getWidget()->setFixedWidth(m_mapRowOrColumnYokingGroupComboBox->getWidget()->sizeHint().width() - 20); -#endif // CARET_OS_MACOSX QObject::connect(m_mapRowOrColumnYokingGroupComboBox, SIGNAL(itemActivated()), this, SLOT(yokingGroupActivated())); diff --git a/src/GuiQt/MapYokingGroupComboBox.cxx b/src/GuiQt/MapYokingGroupComboBox.cxx index 17c8757a0..75eac1158 100644 --- a/src/GuiQt/MapYokingGroupComboBox.cxx +++ b/src/GuiQt/MapYokingGroupComboBox.cxx @@ -87,8 +87,9 @@ MapYokingGroupComboBox::MapYokingGroupComboBox(QObject* parent, m_comboBox->setup(); m_comboBox->getWidget()->setStatusTip("Synchronize selected map indices (and selection status for overlays)"); m_comboBox->getWidget()->setToolTip("Synchronize selected map indices (and selection status for overlays)"); + m_comboBox->getComboBox()->setSizeAdjustPolicy(QComboBox::AdjustToContents); #ifdef CARET_OS_MACOSX - m_comboBox->getComboBox()->setFixedWidth(m_comboBox->getComboBox()->sizeHint().width() - 20); +// m_comboBox->getComboBox()->setFixedWidth(m_comboBox->getComboBox()->sizeHint().width() - 20); #endif // CARET_OS_MACOSX QObject::connect(m_comboBox, SIGNAL(itemActivated()), this, SLOT(comboBoxActivated())); diff --git a/src/GuiQt/OverlayViewController.cxx b/src/GuiQt/OverlayViewController.cxx index 53950b557..8466ee3a9 100644 --- a/src/GuiQt/OverlayViewController.cxx +++ b/src/GuiQt/OverlayViewController.cxx @@ -278,9 +278,6 @@ OverlayViewController::OverlayViewController(const Qt::Orientation orientation, descriptivePrefix); m_mapYokingGroupComboBox->getWidget()->setStatusTip("Synchronize enabled status and map indices)"); m_mapYokingGroupComboBox->getWidget()->setToolTip("Yoke to Overlay Mapped Files"); -#ifdef CARET_OS_MACOSX - m_mapYokingGroupComboBox->getWidget()->setFixedWidth(m_mapYokingGroupComboBox->getWidget()->sizeHint().width() - 20); -#endif // CARET_OS_MACOSX QObject::connect(m_mapYokingGroupComboBox, SIGNAL(itemActivated()), this, SLOT(yokingGroupActivated())); -- GitLab From 12fad2b11c796e992d2abe9a01e2d8ae8a0d1239 Mon Sep 17 00:00:00 2001 From: John Harwell Date: Wed, 5 Jun 2019 12:20:39 -0500 Subject: [PATCH 350/427] Fix two line chart (scalar data-series) bugs: (1) When yoked, a duplicate line series chart was being loaded and displayed in the history list; (2) Row and column index in history list has been updated to start at one and thus matches the row and column index in the chart overlays. --- src/Charting/ChartTwoLineSeriesHistory.cxx | 14 +++++ src/Charting/MapFileDataSelector.cxx | 70 +++++++++++++++++++++- src/Charting/MapFileDataSelector.h | 2 + 3 files changed, 84 insertions(+), 2 deletions(-) diff --git a/src/Charting/ChartTwoLineSeriesHistory.cxx b/src/Charting/ChartTwoLineSeriesHistory.cxx index 4ec766e75..9af40706f 100644 --- a/src/Charting/ChartTwoLineSeriesHistory.cxx +++ b/src/Charting/ChartTwoLineSeriesHistory.cxx @@ -27,6 +27,7 @@ #include "CaretAssert.h" #include "CaretLogger.h" #include "ChartTwoDataCartesian.h" +#include "MapFileDataSelector.h" #include "SceneAttributes.h" #include "SceneClass.h" #include "SceneClassAssistant.h" @@ -371,6 +372,19 @@ void ChartTwoLineSeriesHistory::addHistoryItem(ChartTwoDataCartesian* historyItem) { CaretAssert(historyItem); + + /* + * Do not add to history if new history item matches history + * item at front of the deque + */ + if ( ! m_chartHistory.empty()) { + const MapFileDataSelector* newFileMap = historyItem->getMapFileDataSelector(); + const MapFileDataSelector* frontFileMap = m_chartHistory.at(0)->getMapFileDataSelector(); + if (*newFileMap == *frontFileMap) { + return; + } + } + historyItem->setColor(m_defaultColor); historyItem->setLineWidth(m_defaultLineWidth); addHistoryItemNoDefaults(historyItem); diff --git a/src/Charting/MapFileDataSelector.cxx b/src/Charting/MapFileDataSelector.cxx index 2f3db8d52..a3abf3268 100644 --- a/src/Charting/MapFileDataSelector.cxx +++ b/src/Charting/MapFileDataSelector.cxx @@ -124,6 +124,72 @@ MapFileDataSelector::copyHelperMapFileDataSelector(const MapFileDataSelector& ob m_rowMapFileName = obj.m_rowMapFileName; } +/** + * Equality operator. + * + * @param obj + * Instance compared to 'this' instance. + * + * @return True if this and the compared instance are the same, else false. + */ +bool +MapFileDataSelector::operator==(const MapFileDataSelector& obj) const +{ + if (this == &obj) { + return true; + } + if (m_dataSelectionType != obj.m_dataSelectionType) { + return false; + } + + switch (m_dataSelectionType) { + case DataSelectionType::COLUMN_DATA: + if ((m_columnIndex == obj.m_columnIndex) + && (m_columnMapFile == obj.m_columnMapFile) + && (m_columnMapFileName == obj.m_columnMapFileName)) { + return true; + } + break; + case DataSelectionType::INVALID: + break; + case DataSelectionType::ROW_DATA: + if ((m_rowIndex == obj.m_rowIndex) + && (m_rowMapFile == obj.m_rowMapFile) + && (m_rowMapFileName == obj.m_rowMapFileName)) { + return true; + } + break; + case DataSelectionType::SURFACE_VERTEX: + if ((m_surfaceNumberOfVertices == obj.m_surfaceNumberOfVertices) + && (m_surfaceStructure == obj.m_surfaceStructure) + && (m_surfaceVertexIndex == obj.m_surfaceVertexIndex)) { + return true; + } + break; + case DataSelectionType::SURFACE_VERTICES_AVERAGE: + if ((m_surfaceNumberOfVertices == obj.m_surfaceNumberOfVertices) + && (m_surfaceStructure == obj.m_surfaceStructure)) { + if ( ! m_surfaceVertexAverageIndices.empty()) { + if (std::equal(m_surfaceVertexAverageIndices.begin(), + m_surfaceVertexAverageIndices.end(), + obj.m_surfaceVertexAverageIndices.begin())) { + return true; + } + } + } + break; + case DataSelectionType::VOLUME_XYZ: + if ((m_voxelXYZ[0] == obj.m_voxelXYZ[0]) + && (m_voxelXYZ[1] == obj.m_voxelXYZ[1]) + && (m_voxelXYZ[2] == obj.m_voxelXYZ[2])) { + return true; + } + break; + } + + return false; +} + /** * @return The data selection type. */ @@ -380,11 +446,11 @@ MapFileDataSelector::toString() const break; case DataSelectionType::COLUMN_DATA: s = ("Column " - + AString::number(m_columnIndex)); + + AString::number(m_columnIndex + 1)); break; case DataSelectionType::ROW_DATA: s = ("Row " - + AString::number(m_rowIndex)); + + AString::number(m_rowIndex + 1)); break; case DataSelectionType::SURFACE_VERTEX: s = ("Vertex " diff --git a/src/Charting/MapFileDataSelector.h b/src/Charting/MapFileDataSelector.h index c6ac3e95d..6307d0352 100644 --- a/src/Charting/MapFileDataSelector.h +++ b/src/Charting/MapFileDataSelector.h @@ -54,6 +54,8 @@ namespace caret { MapFileDataSelector& operator=(const MapFileDataSelector& obj); + bool operator==(const MapFileDataSelector& obj) const; + DataSelectionType getDataSelectionType() const; static AString getDataSelectionTypeName(const DataSelectionType dataSelectionType); -- GitLab From 412455d9bbbb4516807060ea8f15523f21790082 Mon Sep 17 00:00:00 2001 From: John Harwell Date: Thu, 6 Jun 2019 12:30:35 -0500 Subject: [PATCH 351/427] Additional correction to prevent duplicate lines series charts from being loaded when yoking was active. Fixed memory leak caused by failure to delete the duplicate line charts. --- src/Charting/ChartTwoLineSeriesHistory.cxx | 1 + src/Charting/MapFileDataSelector.cxx | 18 ++++++++++-------- 2 files changed, 11 insertions(+), 8 deletions(-) diff --git a/src/Charting/ChartTwoLineSeriesHistory.cxx b/src/Charting/ChartTwoLineSeriesHistory.cxx index 9af40706f..bc2e2092f 100644 --- a/src/Charting/ChartTwoLineSeriesHistory.cxx +++ b/src/Charting/ChartTwoLineSeriesHistory.cxx @@ -381,6 +381,7 @@ ChartTwoLineSeriesHistory::addHistoryItem(ChartTwoDataCartesian* historyItem) const MapFileDataSelector* newFileMap = historyItem->getMapFileDataSelector(); const MapFileDataSelector* frontFileMap = m_chartHistory.at(0)->getMapFileDataSelector(); if (*newFileMap == *frontFileMap) { + delete historyItem; return; } } diff --git a/src/Charting/MapFileDataSelector.cxx b/src/Charting/MapFileDataSelector.cxx index a3abf3268..607baed61 100644 --- a/src/Charting/MapFileDataSelector.cxx +++ b/src/Charting/MapFileDataSelector.cxx @@ -144,19 +144,21 @@ MapFileDataSelector::operator==(const MapFileDataSelector& obj) const switch (m_dataSelectionType) { case DataSelectionType::COLUMN_DATA: - if ((m_columnIndex == obj.m_columnIndex) - && (m_columnMapFile == obj.m_columnMapFile) - && (m_columnMapFileName == obj.m_columnMapFileName)) { - return true; + if (m_columnIndex == obj.m_columnIndex) { + if ((m_columnMapFile == obj.m_columnMapFile) + || (m_columnMapFileName == obj.m_columnMapFileName)) { + return true; + } } break; case DataSelectionType::INVALID: break; case DataSelectionType::ROW_DATA: - if ((m_rowIndex == obj.m_rowIndex) - && (m_rowMapFile == obj.m_rowMapFile) - && (m_rowMapFileName == obj.m_rowMapFileName)) { - return true; + if (m_rowIndex == obj.m_rowIndex) { + if ((m_rowMapFile == obj.m_rowMapFile) + || (m_rowMapFileName == obj.m_rowMapFileName)) { + return true; + } } break; case DataSelectionType::SURFACE_VERTEX: -- GitLab From b2d87378a7275c729f64c4d50032dfeb618e4a82 Mon Sep 17 00:00:00 2001 From: John Harwell Date: Thu, 6 Jun 2019 15:47:21 -0500 Subject: [PATCH 352/427] For the experimental texture volume drawing, added drawing of orthogonal slices. --- src/Brain/BrainOpenGLFixedPipeline.cxx | 26 +++++-- .../BrainOpenGLVolumeTextureSliceDrawing.cxx | 69 ++++++++++++++----- .../BrainOpenGLVolumeTextureSliceDrawing.h | 1 + 3 files changed, 72 insertions(+), 24 deletions(-) diff --git a/src/Brain/BrainOpenGLFixedPipeline.cxx b/src/Brain/BrainOpenGLFixedPipeline.cxx index fa6e32ff9..8bebf575a 100644 --- a/src/Brain/BrainOpenGLFixedPipeline.cxx +++ b/src/Brain/BrainOpenGLFixedPipeline.cxx @@ -3827,13 +3827,25 @@ BrainOpenGLFixedPipeline::drawVolumeModel(BrowserTabContent* browserTabContent, applyVolumePropertiesOpacity(); if (useNewDrawingFlag) { - BrainOpenGLVolumeSliceDrawing volumeSliceDrawing; - volumeSliceDrawing.draw(this, - browserTabContent, - volumeDrawInfo, - sliceDrawingType, - sliceProjectionType, - viewport); + if (DeveloperFlagsEnum::isFlag(DeveloperFlagsEnum::DEVELOPER_FLAG_TEXTURE_VOLUME)) { + BrainOpenGLVolumeTextureSliceDrawing textureSliceDrawing; + textureSliceDrawing.draw(this, + browserTabContent, + volumeDrawInfo, + sliceDrawingType, + sliceProjectionType, + obliqueMaskType, + viewport); + } + else { + BrainOpenGLVolumeSliceDrawing volumeSliceDrawing; + volumeSliceDrawing.draw(this, + browserTabContent, + volumeDrawInfo, + sliceDrawingType, + sliceProjectionType, + viewport); + } } else { if (DeveloperFlagsEnum::isFlag(DeveloperFlagsEnum::DEVELOPER_FLAG_TEXTURE_VOLUME)) { diff --git a/src/Brain/BrainOpenGLVolumeTextureSliceDrawing.cxx b/src/Brain/BrainOpenGLVolumeTextureSliceDrawing.cxx index 6d9fb8a3e..b9ce572cd 100644 --- a/src/Brain/BrainOpenGLVolumeTextureSliceDrawing.cxx +++ b/src/Brain/BrainOpenGLVolumeTextureSliceDrawing.cxx @@ -128,7 +128,7 @@ BrainOpenGLVolumeTextureSliceDrawing::draw(BrainOpenGLFixedPipeline* fixedPipeli const VolumeSliceInterpolationEdgeEffectsMaskingEnum::Enum obliqueSliceMaskingType, const int32_t viewport[4]) { - CaretAssert(sliceProjectionType == VolumeSliceProjectionTypeEnum::VOLUME_SLICE_PROJECTION_OBLIQUE); +// CaretAssert(sliceProjectionType == VolumeSliceProjectionTypeEnum::VOLUME_SLICE_PROJECTION_OBLIQUE); if (volumeDrawInfo.empty()) { return; @@ -303,7 +303,7 @@ BrainOpenGLVolumeTextureSliceDrawing::drawVolumeSliceViewPlane(const VolumeSlice drawOrientationAxes(allVP); break; case VolumeSliceProjectionTypeEnum::VOLUME_SLICE_PROJECTION_ORTHOGONAL: - CaretAssert(0); + drawOrientationAxes(allVP); break; } } @@ -787,8 +787,6 @@ BrainOpenGLVolumeTextureSliceDrawing::drawVolumeSliceViewProjection(const BrainO switch (sliceProjectionType) { case VolumeSliceProjectionTypeEnum::VOLUME_SLICE_PROJECTION_ORTHOGONAL: - CaretAssert(0); - break; case VolumeSliceProjectionTypeEnum::VOLUME_SLICE_PROJECTION_OBLIQUE: { /* @@ -799,6 +797,7 @@ BrainOpenGLVolumeTextureSliceDrawing::drawVolumeSliceViewProjection(const BrainO obliqueTransformationMatrix); drawObliqueSliceWithOutlines(sliceViewPlane, + sliceProjectionType, obliqueTransformationMatrix); } break; @@ -917,7 +916,6 @@ BrainOpenGLVolumeTextureSliceDrawing::createSlicePlaneEquation(const VolumeSlice } break; case VolumeSliceProjectionTypeEnum::VOLUME_SLICE_PROJECTION_ORTHOGONAL: - CaretAssert(0); break; } @@ -1031,7 +1029,6 @@ BrainOpenGLVolumeTextureSliceDrawing::setVolumeSliceViewingAndModelingTransforma m_browserTabContent->getObliqueVolumeRotationMatrix().multiplyPoint3(up); break; case VolumeSliceProjectionTypeEnum::VOLUME_SLICE_PROJECTION_ORTHOGONAL: - CaretAssert(0); break; } @@ -1407,8 +1404,7 @@ BrainOpenGLVolumeTextureSliceDrawing::drawAxesCrosshairs(const VolumeSliceProjec } switch (sliceProjectionType) { case VolumeSliceProjectionTypeEnum::VOLUME_SLICE_PROJECTION_ORTHOGONAL: - CaretAssert(0); - break; +// break; case VolumeSliceProjectionTypeEnum::VOLUME_SLICE_PROJECTION_OBLIQUE: { glPushMatrix(); @@ -2553,6 +2549,7 @@ getTextureCoordinates(const VolumeFile* vf, static GLuint createTextureName(BrainOpenGLFixedPipeline* fixedPipelineDrawing, + const VolumeSliceProjectionTypeEnum::Enum sliceProjectionType, const VolumeFile* vf, const DisplayGroupEnum::Enum displayGroup, const int32_t tabIndex, @@ -2643,6 +2640,14 @@ createTextureName(BrainOpenGLFixedPipeline* fixedPipelineDrawing, */ glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + switch (sliceProjectionType) { + case caret::VolumeSliceProjectionTypeEnum::VOLUME_SLICE_PROJECTION_OBLIQUE: + break; + case caret::VolumeSliceProjectionTypeEnum::VOLUME_SLICE_PROJECTION_ORTHOGONAL: + glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + break; + } GLfloat borderColor[4] = { 0.0, 0.0, 0.0, 0.0 }; glTexParameterfv(GL_TEXTURE_3D, GL_TEXTURE_BORDER_COLOR, borderColor); @@ -2766,8 +2771,8 @@ struct TextureInfo { std::array m_maxSTR; }; -static std::map volumeTextureInfo; - +static std::map obliqueVolumeTextureInfo; +static std::map orthogonalVolumeTextureInfo; /** * Draw an oblique slice with support for outlining labels and thresholded palette data. * @@ -2778,6 +2783,7 @@ static std::map volumeTextureInfo; */ void BrainOpenGLVolumeTextureSliceDrawing::drawObliqueSliceWithOutlines(const VolumeSliceViewPlaneEnum::Enum sliceViewPlane, + const VolumeSliceProjectionTypeEnum::Enum sliceProjectionType, Matrix4x4& transformationMatrix) { /* @@ -3112,15 +3118,36 @@ BrainOpenGLVolumeTextureSliceDrawing::drawObliqueSliceWithOutlines(const VolumeS } std::array maxStr = { 1.0, 1.0, 1.0 }; GLuint textureID = 0; - auto idIter = volumeTextureInfo.find(vf); - if (idIter != volumeTextureInfo.end()) { - TextureInfo textureInfo = idIter->second; - textureID = textureInfo.m_textureID; - maxStr = textureInfo.m_maxSTR; + + switch (sliceProjectionType) { + case caret::VolumeSliceProjectionTypeEnum::VOLUME_SLICE_PROJECTION_OBLIQUE: + { + auto idIter = obliqueVolumeTextureInfo.find(vf); + if (idIter != obliqueVolumeTextureInfo.end()) { + TextureInfo textureInfo = idIter->second; + textureID = textureInfo.m_textureID; + maxStr = textureInfo.m_maxSTR; + } + + } + break; + case caret::VolumeSliceProjectionTypeEnum::VOLUME_SLICE_PROJECTION_ORTHOGONAL: + { + auto idIter = orthogonalVolumeTextureInfo.find(vf); + if (idIter != orthogonalVolumeTextureInfo.end()) { + TextureInfo textureInfo = idIter->second; + textureID = textureInfo.m_textureID; + maxStr = textureInfo.m_maxSTR; + } + + } + break; } - else { + + if (textureID == 0) { m_fixedPipelineDrawing->testForOpenGLError("Before creating texture"); textureID = createTextureName(m_fixedPipelineDrawing, + sliceProjectionType, vf, m_displayGroup, m_tabIndex, @@ -3130,7 +3157,15 @@ BrainOpenGLVolumeTextureSliceDrawing::drawObliqueSliceWithOutlines(const VolumeS TextureInfo textureInfo; textureInfo.m_textureID = textureID; textureInfo.m_maxSTR = maxStr; - volumeTextureInfo.insert(std::make_pair(vf, textureInfo)); + + switch (sliceProjectionType) { + case caret::VolumeSliceProjectionTypeEnum::VOLUME_SLICE_PROJECTION_OBLIQUE: + obliqueVolumeTextureInfo.insert(std::make_pair(vf, textureInfo)); + break; + case caret::VolumeSliceProjectionTypeEnum::VOLUME_SLICE_PROJECTION_ORTHOGONAL: + orthogonalVolumeTextureInfo.insert(std::make_pair(vf, textureInfo)); + break; + } /* 1.0 is highest priority texture so that texture is resident */ const GLclampf priority(1.0); diff --git a/src/Brain/BrainOpenGLVolumeTextureSliceDrawing.h b/src/Brain/BrainOpenGLVolumeTextureSliceDrawing.h index 9ba80ff9b..6ca757818 100644 --- a/src/Brain/BrainOpenGLVolumeTextureSliceDrawing.h +++ b/src/Brain/BrainOpenGLVolumeTextureSliceDrawing.h @@ -215,6 +215,7 @@ namespace caret { const Plane& plane); void drawObliqueSliceWithOutlines(const VolumeSliceViewPlaneEnum::Enum sliceViewPlane, + const VolumeSliceProjectionTypeEnum::Enum sliceProjectionType, Matrix4x4& transformationMatrix); void createSlicePlaneEquation(const VolumeSliceProjectionTypeEnum::Enum sliceProjectionType, -- GitLab From 2c2ea09eadd7b7a91e70b2894ff70e1a5b52184b Mon Sep 17 00:00:00 2001 From: John Harwell Date: Fri, 7 Jun 2019 16:39:31 -0500 Subject: [PATCH 353/427] Implement WB-713 Window Background Color: Added window foreground and background colors that are set on Preferences. If an older scene is loaded that does not contain window colors, the surface colors are used (previous functionality used surface color for background). --- src/Brain/BrainOpenGLFixedPipeline.cxx | 67 ++++++++--------- src/Common/BackgroundAndForegroundColors.cxx | 72 +++++++++++++++++++ src/Common/BackgroundAndForegroundColors.h | 13 ++++ src/Common/CaretPreferences.cxx | 17 ++++- src/Common/CaretPreferences.h | 4 ++ src/GuiQt/PreferencesDialog.cxx | 37 ++++++++++ src/GuiQt/PreferencesDialog.h | 6 +- ...ckgroundAndForegroundColorsSceneHelper.cxx | 25 ++++++- 8 files changed, 200 insertions(+), 41 deletions(-) diff --git a/src/Brain/BrainOpenGLFixedPipeline.cxx b/src/Brain/BrainOpenGLFixedPipeline.cxx index 8bebf575a..5fdadd06f 100644 --- a/src/Brain/BrainOpenGLFixedPipeline.cxx +++ b/src/Brain/BrainOpenGLFixedPipeline.cxx @@ -424,12 +424,12 @@ void BrainOpenGLFixedPipeline::updateForegroundAndBackgroundColors(const BrainOpenGLViewportContent* vpContent) { /* - * Default to colors for surface + * Default to colors for window */ CaretPreferences* prefs = SessionManager::get()->getCaretPreferences(); - prefs->getBackgroundAndForegroundColors()->getColorForegroundSurfaceView(m_foregroundColorByte); + prefs->getBackgroundAndForegroundColors()->getColorForegroundWindow(m_foregroundColorByte); m_foregroundColorByte[3] = 255; - prefs->getBackgroundAndForegroundColors()->getColorBackgroundSurfaceView(m_backgroundColorByte); + prefs->getBackgroundAndForegroundColors()->getColorBackgroundWindow(m_backgroundColorByte); m_backgroundColorByte[3] = 255; if (vpContent != NULL) { @@ -577,15 +577,9 @@ BrainOpenGLFixedPipeline::drawModelsImplementation(const int32_t windowIndex, this->checkForOpenGLError(NULL, "At beginning of drawModels()"); /* - * Default the background colors to first model - * NOTE: If there are no models, the surface background color is used + * NULL will retrieve Window colors (window colors added on 07jul2019) */ - if (viewportContents.empty()) { - updateForegroundAndBackgroundColors(NULL); - } - else { - updateForegroundAndBackgroundColors(viewportContents[0]); - } + updateForegroundAndBackgroundColors(NULL); /* * Use the background color as the clear color. @@ -663,36 +657,33 @@ BrainOpenGLFixedPipeline::drawModelsImplementation(const int32_t windowIndex, updateForegroundAndBackgroundColors(vpContent); /* - * If this is NOT the first viewport content, - * AND the background color for this viewport content is + * If the background color for this viewport content is * different that the clear color, THEN * draw a rectangle with the background color. */ - if (i > 0) { - if ((m_backgroundColorByte[0] != clearColorByte[0]) - || (m_backgroundColorByte[1] != clearColorByte[1]) - || (m_backgroundColorByte[2] != clearColorByte[2])) { - GLboolean depthEnabledFlag; - glGetBooleanv(GL_DEPTH_TEST, - &depthEnabledFlag); - glDisable(GL_DEPTH_TEST); - - glMatrixMode(GL_PROJECTION); - glLoadIdentity(); - glOrtho(0.0, 1.0, 0.0, 1.0, -1.0, 1.0); - glMatrixMode(GL_MODELVIEW); - glLoadIdentity(); - glColor3ubv(m_backgroundColorByte); - glBegin(GL_POLYGON); - glVertex2f(0.0, 0.0); - glVertex2f(1.0, 0.0); - glVertex2f(1.0, 1.0); - glVertex2f(0.0, 1.0); - glEnd(); - - if (depthEnabledFlag) { - glEnable(GL_DEPTH_TEST); - } + if ((m_backgroundColorByte[0] != clearColorByte[0]) + || (m_backgroundColorByte[1] != clearColorByte[1]) + || (m_backgroundColorByte[2] != clearColorByte[2])) { + GLboolean depthEnabledFlag; + glGetBooleanv(GL_DEPTH_TEST, + &depthEnabledFlag); + glDisable(GL_DEPTH_TEST); + + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); + glOrtho(0.0, 1.0, 0.0, 1.0, -1.0, 1.0); + glMatrixMode(GL_MODELVIEW); + glLoadIdentity(); + glColor3ubv(m_backgroundColorByte); + glBegin(GL_POLYGON); + glVertex2f(0.0, 0.0); + glVertex2f(1.0, 0.0); + glVertex2f(1.0, 1.0); + glVertex2f(0.0, 1.0); + glEnd(); + + if (depthEnabledFlag) { + glEnable(GL_DEPTH_TEST); } } diff --git a/src/Common/BackgroundAndForegroundColors.cxx b/src/Common/BackgroundAndForegroundColors.cxx index 30eb6ff63..9df91a4ba 100644 --- a/src/Common/BackgroundAndForegroundColors.cxx +++ b/src/Common/BackgroundAndForegroundColors.cxx @@ -92,6 +92,14 @@ BackgroundAndForegroundColors::operator==(const BackgroundAndForegroundColors& o bool equalFlag(true); for (int32_t i = 0; i < 3; i++) { + if (m_colorForegroundWindow[i] != obj.m_colorForegroundWindow[i]) { + equalFlag = false; + break; + } + if (m_colorBackgroundWindow[i] !=obj.m_colorBackgroundWindow[i]) { + equalFlag = false; + break; + } if (m_colorForegroundAll[i] != obj.m_colorForegroundAll[i]) { equalFlag = false; break; @@ -146,6 +154,8 @@ void BackgroundAndForegroundColors::copyHelperBackgroundAndForegroundColors(const BackgroundAndForegroundColors& obj) { for (int32_t i = 0; i < 3; i++) { + m_colorForegroundWindow[i] = obj.m_colorForegroundWindow[i]; + m_colorBackgroundWindow[i] = obj.m_colorBackgroundWindow[i]; m_colorForegroundAll[i] = obj.m_colorForegroundAll[i]; m_colorBackgroundAll[i] = obj.m_colorBackgroundAll[i]; m_colorForegroundChart[i] = obj.m_colorForegroundChart[i]; @@ -173,6 +183,10 @@ BackgroundAndForegroundColors::reset() const uint8_t foreGreen = 255; const uint8_t foreBlue = 255; + setColor(m_colorForegroundWindow, foreRed, foreGreen, foreBlue); + + setColor(m_colorBackgroundWindow, backRed, backGreen, backBlue); + setColor(m_colorForegroundAll, foreRed, foreGreen, foreBlue); setColor(m_colorBackgroundAll, backRed, backGreen, backBlue); @@ -200,6 +214,64 @@ BackgroundAndForegroundColors::reset() setColor(m_colorChartHistogramThreshold, threshRed, threshGreen, threshBlue); } +/** + * Get the foreground color for the window. + * + * @param colorForeground + * RGB color components ranging [0, 255]. + */ +void +BackgroundAndForegroundColors::getColorForegroundWindow(uint8_t colorForeground[3]) const +{ + for (int32_t i = 0; i < 3; i++) { + colorForeground[i] = m_colorForegroundWindow[i]; + } +} + +/** + * Set the foreground color for the window. + * + * @param colorForeground + * RGB color components ranging [0, 255]. + */ +void +BackgroundAndForegroundColors::setColorForegroundWindow(const uint8_t colorForeground[3]) +{ + for (int32_t i = 0; i < 3; i++) { + m_colorForegroundWindow[i] = colorForeground[i]; + } +} + +/** + * Get the background color for the window. + * + * @param colorBackground + * RGB color components ranging [0, 255]. + */ +void +BackgroundAndForegroundColors::getColorBackgroundWindow(uint8_t colorBackground[3]) const +{ + for (int32_t i = 0; i < 3; i++) { + colorBackground[i] = m_colorBackgroundWindow[i]; + } +} + +/** + * Set the background color for the window. + * + * @param colorBackground + * RGB color components ranging [0, 255]. + */ +void +BackgroundAndForegroundColors::setColorBackgroundWindow(const uint8_t colorBackground[3]) +{ + for (int32_t i = 0; i < 3; i++) { + m_colorBackgroundWindow[i] = colorBackground[i]; + } +} + + + /** * Get the foreground color for viewing the ALL model. * diff --git a/src/Common/BackgroundAndForegroundColors.h b/src/Common/BackgroundAndForegroundColors.h index f77ac1d95..eb123170b 100644 --- a/src/Common/BackgroundAndForegroundColors.h +++ b/src/Common/BackgroundAndForegroundColors.h @@ -44,6 +44,15 @@ namespace caret { // ADD_NEW_METHODS_HERE + void getColorBackgroundWindow(uint8_t colorBackground[3]) const; + + void setColorBackgroundWindow(const uint8_t colorBackground[3]); + + void getColorForegroundWindow(uint8_t colorForeground[3]) const; + + void setColorForegroundWindow(const uint8_t colorForeground[3]); + + void getColorForegroundAllView(uint8_t colorForeground[3]) const; void setColorForegroundAllView(const uint8_t colorForeground[3]); @@ -95,6 +104,10 @@ namespace caret { const uint8_t green, const uint8_t blue); + uint8_t m_colorBackgroundWindow[3]; + + uint8_t m_colorForegroundWindow[3]; + uint8_t m_colorForegroundAll[3]; uint8_t m_colorBackgroundAll[3]; diff --git a/src/Common/CaretPreferences.cxx b/src/Common/CaretPreferences.cxx index 424d60d00..41f965bc0 100644 --- a/src/Common/CaretPreferences.cxx +++ b/src/Common/CaretPreferences.cxx @@ -105,7 +105,6 @@ CaretPreferences::invalidSceneDataValues() for (auto pdv : m_preferenceDataValues) { pdv->setSceneValueValid(false); } - setBackgroundAndForegroundColorsMode(BackgroundAndForegroundColorsModeEnum::USER_PREFERENCES); } /** @@ -694,6 +693,10 @@ CaretPreferences::setUserBackgroundAndForegroundColors(const BackgroundAndForegr /* * Update preferences file with colors */ + writeUnsignedByteArray(NAME_COLOR_BACKGROUND_WINDOW, + this->userColors.m_colorBackgroundWindow, + 3); + writeUnsignedByteArray(NAME_COLOR_FOREGROUND_ALL, this->userColors.m_colorForegroundAll, 3); @@ -1640,6 +1643,18 @@ CaretPreferences::readPreferences() uint8_t colorRGB[3] = { 0, 0, 0 }; + userColors.getColorForegroundWindow(colorRGB); + readUnsignedByteArray(NAME_COLOR_FOREGROUND_WINDOW, + colorRGB, + 3); + userColors.setColorForegroundWindow(colorRGB); + + userColors.getColorBackgroundWindow(colorRGB); + readUnsignedByteArray(NAME_COLOR_BACKGROUND_WINDOW, + colorRGB, + 3); + userColors.setColorBackgroundWindow(colorRGB); + userColors.getColorForegroundAllView(colorRGB); readUnsignedByteArray(NAME_COLOR_FOREGROUND_ALL, colorRGB, diff --git a/src/Common/CaretPreferences.h b/src/Common/CaretPreferences.h index da9fc5eb3..2e034067c 100644 --- a/src/Common/CaretPreferences.h +++ b/src/Common/CaretPreferences.h @@ -342,6 +342,8 @@ namespace caret { static const AString NAME_VOLUME_MONTAGE_COORDINATE_PRECISION; static const AString NAME_COLOR_BACKGROUND; static const AString NAME_COLOR_FOREGROUND; + static const AString NAME_COLOR_BACKGROUND_WINDOW; + static const AString NAME_COLOR_FOREGROUND_WINDOW; static const AString NAME_COLOR_BACKGROUND_ALL; static const AString NAME_COLOR_FOREGROUND_ALL; static const AString NAME_COLOR_BACKGROUND_CHART; @@ -387,6 +389,8 @@ namespace caret { const AString CaretPreferences::NAME_VOLUME_MONTAGE_COORDINATE_PRECISION = "volumeMontageCoordinatePrecision"; const AString CaretPreferences::NAME_COLOR_BACKGROUND = "colorBackground"; const AString CaretPreferences::NAME_COLOR_FOREGROUND = "colorForeground"; + const AString CaretPreferences::NAME_COLOR_BACKGROUND_WINDOW = "colorBackgroundWindow"; + const AString CaretPreferences::NAME_COLOR_FOREGROUND_WINDOW = "colorForegroundWindow"; const AString CaretPreferences::NAME_COLOR_BACKGROUND_ALL = "colorBackgroundAll"; const AString CaretPreferences::NAME_COLOR_FOREGROUND_ALL = "colorForegroundAll"; const AString CaretPreferences::NAME_COLOR_BACKGROUND_CHART = "colorBackgroundChart"; diff --git a/src/GuiQt/PreferencesDialog.cxx b/src/GuiQt/PreferencesDialog.cxx index d0843b656..c7084ce82 100644 --- a/src/GuiQt/PreferencesDialog.cxx +++ b/src/GuiQt/PreferencesDialog.cxx @@ -176,6 +176,14 @@ PreferencesDialog::addColorButtonAndSwatch(QGridLayout* gridLayout, buttonText = "Chart Threshold"; m_chartHistogramThresholdColorWidget = colorSwatchWidget; break; + case PREF_COLOR_BACKGROUND_WINDOW: + buttonText = "Window Background"; + m_backgroundColorWindowWidget = colorSwatchWidget; + break; + case PREF_COLOR_FOREGROUND_WINDOW: + buttonText = "Window Foreground"; + m_foregroundColorWindowWidget = colorSwatchWidget; + break; case NUMBER_OF_PREF_COLORS: CaretAssert(0); break; @@ -207,6 +215,13 @@ PreferencesDialog::createColorsWidget() QGridLayout* gridLayout = new QGridLayout(); + addColorButtonAndSwatch(gridLayout, + PREF_COLOR_FOREGROUND_WINDOW, + colorSignalMapper); + addColorButtonAndSwatch(gridLayout, + PREF_COLOR_BACKGROUND_WINDOW, + colorSignalMapper); + addColorButtonAndSwatch(gridLayout, PREF_COLOR_FOREGROUND_ALL, colorSignalMapper); @@ -311,6 +326,14 @@ PreferencesDialog::updateColorWidget(CaretPreferences* prefs) colors.getColorChartHistogramThreshold(rgb); colorSwatchWidget = m_chartHistogramThresholdColorWidget; break; + case PREF_COLOR_BACKGROUND_WINDOW: + colors.getColorBackgroundWindow(rgb); + colorSwatchWidget = m_backgroundColorWindowWidget; + break; + case PREF_COLOR_FOREGROUND_WINDOW: + colors.getColorForegroundWindow(rgb); + colorSwatchWidget = m_foregroundColorWindowWidget; + break; case NUMBER_OF_PREF_COLORS: CaretAssert(0); break; @@ -827,6 +850,14 @@ PreferencesDialog::updateColorWithDialog(const PREF_COLOR prefColor) colors.getColorChartHistogramThreshold(rgb); prefColorName = "Chart Histogram Threshold"; break; + case PREF_COLOR_BACKGROUND_WINDOW: + colors.getColorBackgroundWindow(rgb); + prefColorName = "Background - Window"; + break; + case PREF_COLOR_FOREGROUND_WINDOW: + colors.getColorForegroundWindow(rgb); + prefColorName = "Foreground - Window"; + break; case NUMBER_OF_PREF_COLORS: CaretAssert(0); break; @@ -880,6 +911,12 @@ PreferencesDialog::updateColorWithDialog(const PREF_COLOR prefColor) case PREF_COLOR_CHART_THRESHOLD: colors.setColorChartHistogramThreshold(rgb); break; + case PREF_COLOR_BACKGROUND_WINDOW: + colors.setColorBackgroundWindow(rgb); + break; + case PREF_COLOR_FOREGROUND_WINDOW: + colors.setColorForegroundWindow(rgb); + break; case NUMBER_OF_PREF_COLORS: CaretAssert(0); break; diff --git a/src/GuiQt/PreferencesDialog.h b/src/GuiQt/PreferencesDialog.h index aa9dc3245..1ff954fe9 100644 --- a/src/GuiQt/PreferencesDialog.h +++ b/src/GuiQt/PreferencesDialog.h @@ -88,7 +88,9 @@ namespace caret { PREF_COLOR_FOREGROUND_VOLUME = 7, PREF_COLOR_CHART_MATRIX_GRID_LINES = 8, PREF_COLOR_CHART_THRESHOLD = 9, - NUMBER_OF_PREF_COLORS = 10 + PREF_COLOR_BACKGROUND_WINDOW = 10, + PREF_COLOR_FOREGROUND_WINDOW = 11, + NUMBER_OF_PREF_COLORS = 12 }; QWidget* createColorsWidget(); @@ -121,10 +123,12 @@ namespace caret { PreferencesDialog& operator=(const PreferencesDialog&); + QWidget* m_foregroundColorWindowWidget; QWidget* m_foregroundColorAllWidget; QWidget* m_foregroundColorChartWidget; QWidget* m_foregroundColorSurfaceWidget; QWidget* m_foregroundColorVolumeWidget; + QWidget* m_backgroundColorWindowWidget; QWidget* m_backgroundColorAllWidget; QWidget* m_backgroundColorChartWidget; QWidget* m_backgroundColorSurfaceWidget; diff --git a/src/Scenes/BackgroundAndForegroundColorsSceneHelper.cxx b/src/Scenes/BackgroundAndForegroundColorsSceneHelper.cxx index 0b7d3215a..089d948ac 100644 --- a/src/Scenes/BackgroundAndForegroundColorsSceneHelper.cxx +++ b/src/Scenes/BackgroundAndForegroundColorsSceneHelper.cxx @@ -49,6 +49,11 @@ m_wasRestoredFromSceneFlag(false) m_sceneAssistant = new SceneClassAssistant(); + m_sceneAssistant->addArray("m_colorForegroundWindow", + m_colors.m_colorForegroundWindow, 3, 255); + m_sceneAssistant->addArray("m_colorBackgroundWindow", + m_colors.m_colorBackgroundWindow, 3, 0); + m_sceneAssistant->addArray("m_colorForegroundAll", m_colors.m_colorForegroundAll, 3, 255); m_sceneAssistant->addArray("m_colorBackgroundAll", @@ -71,6 +76,8 @@ m_wasRestoredFromSceneFlag(false) m_sceneAssistant->addArray("m_colorChartMatrixGridLines", m_colors.m_colorChartMatrixGridLines, 3, 0); + m_sceneAssistant->addArray("m_colorChartHistogramThreshold", + m_colors.m_colorChartHistogramThreshold, 3, 0); } /** @@ -117,9 +124,12 @@ BackgroundAndForegroundColorsSceneHelper::saveToScene(const SceneAttributes* sce { m_wasRestoredFromSceneFlag = false; + /* + * Version 2: Added window foreground and background colors + */ SceneClass* sceneClass = new SceneClass(instanceName, "BackgroundAndForegroundColorsSceneHelper", - 1); + 2); m_sceneAssistant->saveMembers(sceneAttributes, sceneClass); @@ -159,6 +169,19 @@ BackgroundAndForegroundColorsSceneHelper::restoreFromScene(const SceneAttributes m_sceneAssistant->restoreMembers(sceneAttributes, sceneClass); + if (sceneClass->getVersionNumber() <= 1) { + /* + * Version 1 did not have window foreground and background colors so + * use the surface background colors + */ + uint8_t rgb[3]; + m_colors.getColorBackgroundSurfaceView(rgb); + m_colors.setColorBackgroundWindow(rgb); + + m_colors.getColorForegroundSurfaceView(rgb); + m_colors.setColorForegroundWindow(rgb); + } + m_wasRestoredFromSceneFlag = true; //Uncomment if sub-classes must restore from scene -- GitLab From 320c5308a93bad82fb1857fa390fd3b46ad65ad2 Mon Sep 17 00:00:00 2001 From: John Harwell Date: Mon, 10 Jun 2019 11:18:55 -0500 Subject: [PATCH 354/427] Correct naming/spelling error in a method name. --- src/Brain/Brain.cxx | 2 +- src/Brain/SessionManager.cxx | 4 ++-- src/Common/CaretPreferences.cxx | 2 +- src/Common/CaretPreferences.h | 2 +- src/GuiQt/BrainBrowserWindow.cxx | 2 +- 5 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/Brain/Brain.cxx b/src/Brain/Brain.cxx index 0e74c795e..5205c7c54 100644 --- a/src/Brain/Brain.cxx +++ b/src/Brain/Brain.cxx @@ -5497,7 +5497,7 @@ Brain::loadFilesSelectedInSpecFile(EventSpecFileReadDataFiles* readSpecFileDataF resetBrain(); CaretPreferences* prefs = SessionManager::get()->getCaretPreferences(); - prefs->invalidSceneDataValues(); + prefs->invalidateSceneDataValues(); prefs->setBackgroundAndForegroundColorsMode(BackgroundAndForegroundColorsModeEnum::USER_PREFERENCES); const AString specFileName = sf->getFileName(); diff --git a/src/Brain/SessionManager.cxx b/src/Brain/SessionManager.cxx index 1ec23b50a..7907d2ead 100644 --- a/src/Brain/SessionManager.cxx +++ b/src/Brain/SessionManager.cxx @@ -702,7 +702,7 @@ SessionManager::restoreFromScene(const SceneAttributes* sceneAttributes, * Default to user preferences for colors */ m_caretPreferences->setBackgroundAndForegroundColorsMode(BackgroundAndForegroundColorsModeEnum::USER_PREFERENCES); - m_caretPreferences->invalidSceneDataValues(); + m_caretPreferences->invalidateSceneDataValues(); if (sceneClass == NULL) { return; @@ -1068,7 +1068,7 @@ SessionManager::restorePreferencesFromScene(const SceneAttributes* /*sceneAttrib } const QString invalidValueName("InVaLiDvAlUe"); - m_caretPreferences->invalidSceneDataValues(); + m_caretPreferences->invalidateSceneDataValues(); std::vector sceneDataValues = m_caretPreferences->getPreferenceSceneDataValues(); for (auto scv : sceneDataValues) { diff --git a/src/Common/CaretPreferences.cxx b/src/Common/CaretPreferences.cxx index 41f965bc0..791e93d85 100644 --- a/src/Common/CaretPreferences.cxx +++ b/src/Common/CaretPreferences.cxx @@ -100,7 +100,7 @@ CaretPreferences::~CaretPreferences() * and these 'scene overrides' are invalidated by this method */ void -CaretPreferences::invalidSceneDataValues() +CaretPreferences::invalidateSceneDataValues() { for (auto pdv : m_preferenceDataValues) { pdv->setSceneValueValid(false); diff --git a/src/Common/CaretPreferences.h b/src/Common/CaretPreferences.h index 2e034067c..b839d160b 100644 --- a/src/Common/CaretPreferences.h +++ b/src/Common/CaretPreferences.h @@ -214,7 +214,7 @@ namespace caret { void writeMacros(); - void invalidSceneDataValues(); + void invalidateSceneDataValues(); std::vector getPreferenceSceneDataValues(); diff --git a/src/GuiQt/BrainBrowserWindow.cxx b/src/GuiQt/BrainBrowserWindow.cxx index 14a18ccc1..5525de30c 100644 --- a/src/GuiQt/BrainBrowserWindow.cxx +++ b/src/GuiQt/BrainBrowserWindow.cxx @@ -3195,7 +3195,7 @@ BrainBrowserWindow::processCloseAllFiles() CaretPreferences* prefs = SessionManager::get()->getCaretPreferences(); prefs->setBackgroundAndForegroundColorsMode(BackgroundAndForegroundColorsModeEnum::USER_PREFERENCES); - prefs->invalidSceneDataValues(); + prefs->invalidateSceneDataValues(); EventManager::get()->sendEvent(EventUserInterfaceUpdate().getPointer()); EventManager::get()->sendEvent(EventGraphicsUpdateAllWindows().getPointer()); -- GitLab From 9c30b2beca239b59f54a5fac36431664e8cb6af8 Mon Sep 17 00:00:00 2001 From: John Harwell Date: Mon, 10 Jun 2019 12:37:37 -0500 Subject: [PATCH 355/427] WB-830 Line Series Yoked Within File: Add arrows to line chart history items so that they the line chart history can be re-ordered. --- src/Charting/ChartTwoLineSeriesHistory.cxx | 45 +++++++ src/Charting/ChartTwoLineSeriesHistory.h | 4 + .../MapSettingsChartTwoLineHistoryWidget.cxx | 115 +++++++++++++++++- .../MapSettingsChartTwoLineHistoryWidget.h | 24 +++- 4 files changed, 182 insertions(+), 6 deletions(-) diff --git a/src/Charting/ChartTwoLineSeriesHistory.cxx b/src/Charting/ChartTwoLineSeriesHistory.cxx index bc2e2092f..6cb8dacdf 100644 --- a/src/Charting/ChartTwoLineSeriesHistory.cxx +++ b/src/Charting/ChartTwoLineSeriesHistory.cxx @@ -431,6 +431,12 @@ ChartTwoLineSeriesHistory::getHistoryItem(const int32_t index) const return m_chartHistory[index]; } +/** + * Remove the history item at the given index + * + * @param index + * Index of the item. + */ void ChartTwoLineSeriesHistory::removeHistoryItem(const int32_t index) { @@ -439,6 +445,45 @@ ChartTwoLineSeriesHistory::removeHistoryItem(const int32_t index) m_chartHistory.erase(m_chartHistory.begin() + index); } +/** + * Move the history item down at the given index + * + * @param index + * Index of the item. + */ +void +ChartTwoLineSeriesHistory::moveDownHistoryItem(const int32_t index) +{ + if (getHistoryCount() <= 1) { + return; + } + + const int32_t lastIndex = getHistoryCount() - 1; + if (index < lastIndex) { + std::swap(m_chartHistory[index], + m_chartHistory[index + 1]); + } +} + +/** + * Move the history item up at the given index + * + * @param index + * Index of the item. + */ +void +ChartTwoLineSeriesHistory::moveUpHistoryItem(const int32_t index) +{ + if (getHistoryCount() <= 1) { + return; + } + + if (index > 0) { + std::swap(m_chartHistory[index - 1], + m_chartHistory[index]); + } +} + /** * @return Clear the history. */ diff --git a/src/Charting/ChartTwoLineSeriesHistory.h b/src/Charting/ChartTwoLineSeriesHistory.h index 24231f400..5fc50cd6f 100644 --- a/src/Charting/ChartTwoLineSeriesHistory.h +++ b/src/Charting/ChartTwoLineSeriesHistory.h @@ -74,6 +74,10 @@ namespace caret { void removeHistoryItem(const int32_t index); + void moveDownHistoryItem(const int32_t index); + + void moveUpHistoryItem(const int32_t index); + void clearHistory(); bool getBounds(BoundingBox& boundingBoxOut) const; diff --git a/src/GuiQt/MapSettingsChartTwoLineHistoryWidget.cxx b/src/GuiQt/MapSettingsChartTwoLineHistoryWidget.cxx index a416b961d..c4f16c128 100644 --- a/src/GuiQt/MapSettingsChartTwoLineHistoryWidget.cxx +++ b/src/GuiQt/MapSettingsChartTwoLineHistoryWidget.cxx @@ -25,6 +25,7 @@ #include #include +#include #include #include #include @@ -73,7 +74,6 @@ MapSettingsChartTwoLineHistoryWidget::MapSettingsChartTwoLineHistoryWidget(QWidg "displayed) is limited to this value or " + maxHistoryString + ", whichever is larger."); m_removeAllHistoryIcon = WuQtUtilities::loadIcon(":/SpecFileDialog/delete_icon.png"); - m_filenameLabel = new QLabel(""); QLabel* defaultColorLabel = new QLabel("Default Color: "); @@ -329,6 +329,33 @@ MapSettingsChartTwoLineHistoryWidget::loadHistoryIntoTableWidget(ChartTwoLineSer COLUMN_REMOVE, removeHistoryLabel); } + else if (j == COLUMN_MOVE) { + QLabel dummyLabel(""); + QPixmap moveDownPixmap = createIcon(&dummyLabel, IconType::ARROW_DOWN); + WuQImageLabel* moveDownLabel = new WuQImageLabel(moveDownPixmap, + "Move Down"); + QObject::connect(moveDownLabel, &WuQImageLabel::clicked, + this, [=] { moveDownHistoryItemSelected(iRow); }); + + QPixmap moveUpPixmap = createIcon(&dummyLabel, IconType::ARROW_UP); + WuQImageLabel* moveUpLabel = new WuQImageLabel(moveUpPixmap, + "Move Up"); + QObject::connect(moveUpLabel, &WuQImageLabel::clicked, + this, [=] { moveUpHistoryItemSelected(iRow); }); + + QWidget* moveWidget = new QWidget; + QHBoxLayout* moveLayout = new QHBoxLayout(moveWidget); + moveLayout->setContentsMargins(4, 0, 4, 0); + moveLayout->setSpacing(6); + moveLayout->addWidget(moveDownLabel); + moveLayout->addWidget(moveUpLabel); + moveWidget->setSizePolicy(QSizePolicy::Fixed, + QSizePolicy::Fixed); + + m_tableWidget->setCellWidget(iRow, + COLUMN_MOVE, + moveWidget); + } else if (j == COLUMN_COLOR) { CaretColorEnumComboBox* caretColorComboBox = new CaretColorEnumComboBox(this); QObject::connect(caretColorComboBox, SIGNAL(colorSelected(const CaretColorEnum::Enum)), @@ -405,6 +432,8 @@ MapSettingsChartTwoLineHistoryWidget::loadHistoryIntoTableWidget(ChartTwoLineSer new QTableWidgetItem("View")); m_tableWidget->setHorizontalHeaderItem(COLUMN_REMOVE, new QTableWidgetItem("Remove")); + m_tableWidget->setHorizontalHeaderItem(COLUMN_MOVE, + new QTableWidgetItem("Move")); m_tableWidget->setHorizontalHeaderItem(COLUMN_COLOR, new QTableWidgetItem("Color")); m_tableWidget->setHorizontalHeaderItem(COLUMN_LINE_WIDTH, @@ -414,6 +443,7 @@ MapSettingsChartTwoLineHistoryWidget::loadHistoryIntoTableWidget(ChartTwoLineSer m_tableWidget->resizeColumnToContents(COLUMN_VIEW); m_tableWidget->resizeColumnToContents(COLUMN_REMOVE); + m_tableWidget->resizeColumnToContents(COLUMN_MOVE); m_tableWidget->resizeColumnToContents(COLUMN_COLOR); m_tableWidget->resizeColumnToContents(COLUMN_LINE_WIDTH); m_tableWidget->setColumnWidth(COLUMN_DESCRIPTION, @@ -475,6 +505,36 @@ MapSettingsChartTwoLineHistoryWidget::removeHistoryItemSelected(int rowIndex) EventManager::get()->sendEvent(EventGraphicsUpdateAllWindows().getPointer()); } +/** + * Move a history item up + * + * @param rowIndex + * Row index of item. + */ +void +MapSettingsChartTwoLineHistoryWidget::moveUpHistoryItemSelected(int rowIndex) +{ + ChartTwoLineSeriesHistory* lineSeriesHistory = getLineSeriesHistory(); + lineSeriesHistory->moveUpHistoryItem(rowIndex); + updateDialogContentPrivate(); + EventManager::get()->sendEvent(EventGraphicsUpdateAllWindows().getPointer()); +} + +/** + * Move a history item down + * + * @param rowIndex + * Row index of item. + */ +void +MapSettingsChartTwoLineHistoryWidget::moveDownHistoryItemSelected(int rowIndex) +{ + ChartTwoLineSeriesHistory* lineSeriesHistory = getLineSeriesHistory(); + lineSeriesHistory->moveDownHistoryItem(rowIndex); + updateDialogContentPrivate(); + EventManager::get()->sendEvent(EventGraphicsUpdateAllWindows().getPointer()); +} + /** * Color item selected * @@ -580,4 +640,55 @@ MapSettingsChartTwoLineHistoryWidget::viewedMaximumSpinBoxValueChanged(int num) updateDialogContentPrivate(); EventManager::get()->sendEvent(EventGraphicsUpdateAllWindows().getPointer()); } -} \ No newline at end of file +} + +/** + * Create a pixmap for the given widget + * + * @param editButton + * The edit button identifier + * @return + * Pixmap for the given button + */ +QPixmap +MapSettingsChartTwoLineHistoryWidget::createIcon(const QWidget* widget, + const IconType iconType) const +{ + CaretAssert(widget); + const qreal pixmapSize = 22.0; + const qreal maxValue = pixmapSize / 2.0 - 1.0; + const qreal arrowTip = maxValue * (2.0 / 3.0); + + uint32_t pixmapOptions(static_cast(WuQtUtilities::PixMapCreationOptions::TransparentBackground)); + + QPixmap pixmap(static_cast(pixmapSize), + static_cast(pixmapSize)); + QSharedPointer painter = WuQtUtilities::createPixmapWidgetPainterOriginCenter(widget, + pixmap, + pixmapOptions); + QPen pen(painter->pen()); + pen.setWidth(3); + painter->setPen(pen); + + switch (iconType) { + case IconType::ARROW_DOWN: + /* + * Down arrow + */ + painter->drawLine(QPointF(0, maxValue), QPointF(0, -maxValue)); + painter->drawLine(QPointF(0, -maxValue), QPointF(arrowTip, -maxValue + arrowTip)); + painter->drawLine(QPointF(0, -maxValue), QPointF(-arrowTip, -maxValue + arrowTip)); + break; + case IconType::ARROW_UP: + /* + * Up arrow + */ + painter->drawLine(QPointF(0, maxValue), QPointF(0, -maxValue)); + painter->drawLine(QPointF(0, maxValue), QPointF(arrowTip, maxValue - arrowTip)); + painter->drawLine(QPointF(0, maxValue), QPointF(-arrowTip, maxValue - arrowTip)); + break; + } + + return pixmap; +} + diff --git a/src/GuiQt/MapSettingsChartTwoLineHistoryWidget.h b/src/GuiQt/MapSettingsChartTwoLineHistoryWidget.h index 2594fa802..8c0a60868 100644 --- a/src/GuiQt/MapSettingsChartTwoLineHistoryWidget.h +++ b/src/GuiQt/MapSettingsChartTwoLineHistoryWidget.h @@ -63,6 +63,10 @@ namespace caret { void removeHistoryItemSelected(int rowIndex); + void moveUpHistoryItemSelected(int rowIndex); + + void moveDownHistoryItemSelected(int rowIndex); + void colorItemSelected(int rowIndex); void lineWidthItemSelected(int rowIndex); @@ -76,6 +80,10 @@ namespace caret { void viewedMaximumSpinBoxValueChanged(int); private: + enum class IconType { + ARROW_DOWN, + ARROW_UP + }; MapSettingsChartTwoLineHistoryWidget(const MapSettingsChartTwoLineHistoryWidget&); @@ -91,12 +99,19 @@ namespace caret { CaretMappableDataFile* getMapFile(); + QPixmap createIcon(const QWidget* widget, + const IconType iconType) const; + std::weak_ptr m_chartOverlayWeakPointer; QTableWidget* m_tableWidget; QIcon* m_removeAllHistoryIcon; + QIcon* m_downArrowIcon; + + QIcon* m_upArrowIcon; + QSignalMapper* m_removeHistoryItemSignalMapper; QSignalMapper* m_colorItemSignalMapper; @@ -117,10 +132,11 @@ namespace caret { static const int32_t COLUMN_VIEW = 0; static const int32_t COLUMN_REMOVE = 1; - static const int32_t COLUMN_COLOR = 2; - static const int32_t COLUMN_LINE_WIDTH = 3; - static const int32_t COLUMN_DESCRIPTION = 4; - static const int32_t COLUMN_COUNT = 5; + static const int32_t COLUMN_MOVE = 2; + static const int32_t COLUMN_COLOR = 3; + static const int32_t COLUMN_LINE_WIDTH = 4; + static const int32_t COLUMN_DESCRIPTION = 5; + static const int32_t COLUMN_COUNT = 6; // ADD_NEW_MEMBERS_HERE -- GitLab From 16ba6ee119bc15a374a6730947967e59e455d078 Mon Sep 17 00:00:00 2001 From: John Harwell Date: Mon, 10 Jun 2019 15:20:58 -0500 Subject: [PATCH 356/427] WB-830 Line Series Yoked Within File: If a scalar data series file is selected in an enabled overlay, do not show the file in any lower level overlays. --- src/Brain/ChartTwoOverlay.cxx | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/src/Brain/ChartTwoOverlay.cxx b/src/Brain/ChartTwoOverlay.cxx index 572ab36f1..c99043c61 100644 --- a/src/Brain/ChartTwoOverlay.cxx +++ b/src/Brain/ChartTwoOverlay.cxx @@ -739,6 +739,23 @@ ChartTwoOverlay::getSelectionDataPrivate(std::vector& ma * and dimensions must also match */ useIt = true; + + /* + * If file is a scalar data series, and the same scalar data series file + * is enabled in a "higher" chart overlay, do not show the file in this + * overlay. + */ + if (mapFile->getDataFileType() == DataFileTypeEnum::CONNECTIVITY_SCALAR_DATA_SERIES) { + for (int32_t io = 0; io < m_overlayIndex; io++) { + const ChartTwoOverlay* otherOverlay = m_parentChartTwoOverlaySet->getOverlay(io); + CaretAssert(otherOverlay); + if (otherOverlay->isEnabled()) { + if (otherOverlay->getSelectedMapFile() == mapFile) { + useIt = false; + } + } + } + } } } } -- GitLab From c940917bf995f41e3ce6d8909c8f954feaa42e24 Mon Sep 17 00:00:00 2001 From: John Harwell Date: Mon, 10 Jun 2019 15:47:39 -0500 Subject: [PATCH 357/427] WB-830 Line Series Yoked Within File: Updated to only hide a scalar data series file if the overlay is disabled --- src/Brain/ChartTwoOverlay.cxx | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/src/Brain/ChartTwoOverlay.cxx b/src/Brain/ChartTwoOverlay.cxx index c99043c61..02b1941e3 100644 --- a/src/Brain/ChartTwoOverlay.cxx +++ b/src/Brain/ChartTwoOverlay.cxx @@ -743,15 +743,17 @@ ChartTwoOverlay::getSelectionDataPrivate(std::vector& ma /* * If file is a scalar data series, and the same scalar data series file * is enabled in a "higher" chart overlay, do not show the file in this - * overlay. + * overlay. Updated to only hide this file in disabled overlays. */ if (mapFile->getDataFileType() == DataFileTypeEnum::CONNECTIVITY_SCALAR_DATA_SERIES) { - for (int32_t io = 0; io < m_overlayIndex; io++) { - const ChartTwoOverlay* otherOverlay = m_parentChartTwoOverlaySet->getOverlay(io); - CaretAssert(otherOverlay); - if (otherOverlay->isEnabled()) { - if (otherOverlay->getSelectedMapFile() == mapFile) { - useIt = false; + if ( ! isEnabled()) { + for (int32_t io = 0; io < m_overlayIndex; io++) { + const ChartTwoOverlay* otherOverlay = m_parentChartTwoOverlaySet->getOverlay(io); + CaretAssert(otherOverlay); + if (otherOverlay->isEnabled()) { + if (otherOverlay->getSelectedMapFile() == mapFile) { + useIt = false; + } } } } -- GitLab From 7e5338247cf94d8aab6aa3903522b7f8dfab6a4b Mon Sep 17 00:00:00 2001 From: John Harwell Date: Tue, 11 Jun 2019 09:15:06 -0500 Subject: [PATCH 358/427] The line chart history widget (part of overlay and map settings dialog) listed the name of the file containing the line charts. This filename has been removed since the filename is listed at the top of the overlay and map settings dialog. --- src/Brain/ChartTwoOverlay.cxx | 19 +++++++++++-------- .../MapSettingsChartTwoLineHistoryWidget.cxx | 7 ------- .../MapSettingsChartTwoLineHistoryWidget.h | 2 -- src/GuiQt/OverlaySettingsEditorDialog.cxx | 9 +++++---- 4 files changed, 16 insertions(+), 21 deletions(-) diff --git a/src/Brain/ChartTwoOverlay.cxx b/src/Brain/ChartTwoOverlay.cxx index 02b1941e3..17e0be2da 100644 --- a/src/Brain/ChartTwoOverlay.cxx +++ b/src/Brain/ChartTwoOverlay.cxx @@ -745,14 +745,17 @@ ChartTwoOverlay::getSelectionDataPrivate(std::vector& ma * is enabled in a "higher" chart overlay, do not show the file in this * overlay. Updated to only hide this file in disabled overlays. */ - if (mapFile->getDataFileType() == DataFileTypeEnum::CONNECTIVITY_SCALAR_DATA_SERIES) { - if ( ! isEnabled()) { - for (int32_t io = 0; io < m_overlayIndex; io++) { - const ChartTwoOverlay* otherOverlay = m_parentChartTwoOverlaySet->getOverlay(io); - CaretAssert(otherOverlay); - if (otherOverlay->isEnabled()) { - if (otherOverlay->getSelectedMapFile() == mapFile) { - useIt = false; + const bool enableSdsFilterFlag(true); + if (enableSdsFilterFlag) { + if (mapFile->getDataFileType() == DataFileTypeEnum::CONNECTIVITY_SCALAR_DATA_SERIES) { + if ( ! isEnabled()) { + for (int32_t io = 0; io < m_overlayIndex; io++) { + const ChartTwoOverlay* otherOverlay = m_parentChartTwoOverlaySet->getOverlay(io); + CaretAssert(otherOverlay); + if (otherOverlay->isEnabled()) { + if (otherOverlay->getSelectedMapFile() == mapFile) { + useIt = false; + } } } } diff --git a/src/GuiQt/MapSettingsChartTwoLineHistoryWidget.cxx b/src/GuiQt/MapSettingsChartTwoLineHistoryWidget.cxx index c4f16c128..1a214d2ca 100644 --- a/src/GuiQt/MapSettingsChartTwoLineHistoryWidget.cxx +++ b/src/GuiQt/MapSettingsChartTwoLineHistoryWidget.cxx @@ -74,7 +74,6 @@ MapSettingsChartTwoLineHistoryWidget::MapSettingsChartTwoLineHistoryWidget(QWidg "displayed) is limited to this value or " + maxHistoryString + ", whichever is larger."); m_removeAllHistoryIcon = WuQtUtilities::loadIcon(":/SpecFileDialog/delete_icon.png"); - m_filenameLabel = new QLabel(""); QLabel* defaultColorLabel = new QLabel("Default Color: "); defaultColorLabel->setToolTip(defaultColorToolTip); @@ -130,8 +129,6 @@ MapSettingsChartTwoLineHistoryWidget::MapSettingsChartTwoLineHistoryWidget(QWidg topLayout->setColumnStretch(3, 0); topLayout->setColumnStretch(4, 100); int gridRow = 0; - topLayout->addWidget(m_filenameLabel, gridRow, 0, 1, 5, Qt::AlignLeft); - gridRow++; topLayout->addWidget(defaultLineWidthLabel, gridRow, 0); topLayout->addWidget(m_defaultLineWidthSpinBox->getWidget(), gridRow, 1); topLayout->addWidget(defaultColorLabel, gridRow, 2); @@ -219,10 +216,6 @@ MapSettingsChartTwoLineHistoryWidget::updateDialogContentPrivate() if (lineSeriesHistory != NULL) { const CaretMappableDataFile* mapFile = getMapFile(); - if (mapFile != NULL) { - m_filenameLabel->setText("Filename: " + mapFile->getFileNameNoPath()); - m_filenameLabel->setToolTip(mapFile->getFileName()); - } m_defaultColorComboBox->setSelectedColor(lineSeriesHistory->getDefaultColor()); m_defaultLineWidthSpinBox->blockSignals(true); m_defaultLineWidthSpinBox->setValue(lineSeriesHistory->getDefaultLineWidth()); diff --git a/src/GuiQt/MapSettingsChartTwoLineHistoryWidget.h b/src/GuiQt/MapSettingsChartTwoLineHistoryWidget.h index 8c0a60868..87d09e390 100644 --- a/src/GuiQt/MapSettingsChartTwoLineHistoryWidget.h +++ b/src/GuiQt/MapSettingsChartTwoLineHistoryWidget.h @@ -122,8 +122,6 @@ namespace caret { std::vector m_lineWidthSpinBoxes; - QLabel* m_filenameLabel; - CaretColorEnumComboBox* m_defaultColorComboBox; WuQDoubleSpinBox* m_defaultLineWidthSpinBox; diff --git a/src/GuiQt/OverlaySettingsEditorDialog.cxx b/src/GuiQt/OverlaySettingsEditorDialog.cxx index 3ce6004f2..675ccfe0f 100644 --- a/src/GuiQt/OverlaySettingsEditorDialog.cxx +++ b/src/GuiQt/OverlaySettingsEditorDialog.cxx @@ -405,6 +405,8 @@ OverlaySettingsEditorDialog::updateDialogContentPrivate(Overlay* brainordinateOv } } else if (m_chartOverlay != NULL) { + mapFileName = m_caretMappableDataFile->getFileNameNoPath(); + bool hasMapsFlag = false; bool hasHistoryFlag = false; switch (m_chartOverlay->getChartTwoDataType()) { @@ -447,10 +449,6 @@ OverlaySettingsEditorDialog::updateDialogContentPrivate(Overlay* brainordinateOv } if ((selectedMapIndex >= 0) && (selectedMapIndex < m_caretMappableDataFile->getNumberOfMaps())) { - /* - * Get name of file and map - */ - mapFileName = m_caretMappableDataFile->getFileNameNoPath(); if (m_selectedMapFileIndex >= 0) { mapName = m_caretMappableDataFile->getMapName(selectedMapIndex); } @@ -501,6 +499,9 @@ OverlaySettingsEditorDialog::updateDialogContentPrivate(Overlay* brainordinateOv else if (hasHistoryFlag) { isLinesValid = true; m_lineHistoryWidget->updateContent(m_chartOverlay); + if (mapName.isEmpty()) { + mapName = "Line Chart History"; + } } } else { -- GitLab From a2302f5fef39fef52512b80f08d9182616eb07d3 Mon Sep 17 00:00:00 2001 From: John Harwell Date: Tue, 11 Jun 2019 09:36:19 -0500 Subject: [PATCH 359/427] Disabled filtering of files listed in chart overlays for scalar data series files as the requirement is not well defined and we have not considered problems that may result. --- src/Brain/ChartTwoOverlay.cxx | 2 +- src/GuiQt/MapSettingsChartTwoLineHistoryWidget.cxx | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/src/Brain/ChartTwoOverlay.cxx b/src/Brain/ChartTwoOverlay.cxx index 17e0be2da..56f551505 100644 --- a/src/Brain/ChartTwoOverlay.cxx +++ b/src/Brain/ChartTwoOverlay.cxx @@ -745,7 +745,7 @@ ChartTwoOverlay::getSelectionDataPrivate(std::vector& ma * is enabled in a "higher" chart overlay, do not show the file in this * overlay. Updated to only hide this file in disabled overlays. */ - const bool enableSdsFilterFlag(true); + const bool enableSdsFilterFlag(false); if (enableSdsFilterFlag) { if (mapFile->getDataFileType() == DataFileTypeEnum::CONNECTIVITY_SCALAR_DATA_SERIES) { if ( ! isEnabled()) { diff --git a/src/GuiQt/MapSettingsChartTwoLineHistoryWidget.cxx b/src/GuiQt/MapSettingsChartTwoLineHistoryWidget.cxx index 1a214d2ca..02fff7927 100644 --- a/src/GuiQt/MapSettingsChartTwoLineHistoryWidget.cxx +++ b/src/GuiQt/MapSettingsChartTwoLineHistoryWidget.cxx @@ -215,7 +215,6 @@ MapSettingsChartTwoLineHistoryWidget::updateDialogContentPrivate() ChartTwoLineSeriesHistory* lineSeriesHistory = getLineSeriesHistory(); if (lineSeriesHistory != NULL) { - const CaretMappableDataFile* mapFile = getMapFile(); m_defaultColorComboBox->setSelectedColor(lineSeriesHistory->getDefaultColor()); m_defaultLineWidthSpinBox->blockSignals(true); m_defaultLineWidthSpinBox->setValue(lineSeriesHistory->getDefaultLineWidth()); -- GitLab From 7c1fa648b59a823203c2ca1a6eb3a9add75830c4 Mon Sep 17 00:00:00 2001 From: John Harwell Date: Wed, 12 Jun 2019 12:07:20 -0500 Subject: [PATCH 360/427] WB-830 Line Series Yoked Within File Even When Yoking Off: Updated Identify Brainordinate Dialog so that it supports a CiftiScalarDataSeries file for row selection. This allows the user to choose a row that is loaded into a line chart for the file. --- src/GuiQt/IdentifyBrainordinateDialog.cxx | 161 +++++++++++++--------- 1 file changed, 97 insertions(+), 64 deletions(-) diff --git a/src/GuiQt/IdentifyBrainordinateDialog.cxx b/src/GuiQt/IdentifyBrainordinateDialog.cxx index fccfdfec5..249e4a1c1 100644 --- a/src/GuiQt/IdentifyBrainordinateDialog.cxx +++ b/src/GuiQt/IdentifyBrainordinateDialog.cxx @@ -41,12 +41,16 @@ using namespace caret; #include "CaretLogger.h" #include "CiftiFiberTrajectoryFile.h" #include "CaretMappableDataFile.h" +#include "ChartableTwoFileDelegate.h" +#include "ChartableTwoFileLineSeriesChart.h" +#include "ChartableTwoFileMatrixChart.h" #include "CiftiConnectivityMatrixDataFileManager.h" #include "CiftiFiberTrajectoryManager.h" #include "CiftiMappableConnectivityMatrixDataFile.h" #include "CaretMappableDataFileAndMapSelectionModel.h" #include "CaretMappableDataFileAndMapSelectorObject.h" #include "CiftiParcelSelectionComboBox.h" +#include "CiftiScalarDataSeriesFile.h" #include "EventGraphicsUpdateAllWindows.h" #include "EventIdentificationHighlightLocation.h" #include "EventManager.h" @@ -153,6 +157,7 @@ IdentifyBrainordinateDialog::IdentifyBrainordinateDialog(QWidget* parent) ciftiRowFlag = true; break; case DataFileTypeEnum::CONNECTIVITY_SCALAR_DATA_SERIES: + ciftiRowFlag = true; break; case DataFileTypeEnum::FOCI: break; @@ -208,7 +213,7 @@ IdentifyBrainordinateDialog::IdentifyBrainordinateDialog(QWidget* parent) m_ciftiParcelWidget = createCiftiParcelWidget(); m_widgetBox = new WuQGroupBoxExclusiveWidget(); - m_widgetBox->addWidget(m_ciftiRowWidget, "Identify Brainordinate from CIFTI File Row"); + m_widgetBox->addWidget(m_ciftiRowWidget, "Identify from CIFTI File Row"); m_widgetBox->addWidget(m_ciftiParcelWidget, "Identify CIFTI File Parcel"); m_widgetBox->addWidget(m_labelFileWidgets.m_widget, "Identify Label"); m_widgetBox->addWidget(m_surfaceVertexWidget, "Identify Surface Vertex"); @@ -336,8 +341,9 @@ IdentifyBrainordinateDialog::createCiftiRowWidget(const std::vector::max(), 1); - QObject::connect(m_ciftiRowFileIndexSpinBox, SIGNAL(valueChanged(int)), + QObject::connect(m_ciftiRowFileIndexSpinBox, SIGNAL(editingFinished()), this, SLOT(apply())); + m_ciftiRowFileIndexSpinBox->setFixedWidth(INDEX_SPIN_BOX_WIDTH); switch (CiftiMappableDataFile::getCiftiFileRowColumnIndexBaseForGUI()) { case 0: @@ -760,6 +766,7 @@ IdentifyBrainordinateDialog::processCiftiRowWidget(AString& errorMessageOut) if (dataFile != NULL) { CiftiMappableDataFile* ciftiMapFile = dynamic_cast(dataFile); CiftiFiberTrajectoryFile* ciftiTrajFile = dynamic_cast(dataFile); + CiftiScalarDataSeriesFile* ciftiSdsFile = dynamic_cast(dataFile); const int32_t selectedCiftiRowIndex = m_ciftiRowFileIndexSpinBox->value() - m_ciftiRowFileIndexSpinBox->minimum(); @@ -771,75 +778,101 @@ IdentifyBrainordinateDialog::processCiftiRowWidget(AString& errorMessageOut) int64_t voxelIJK[3]; float voxelXYZ[3]; bool voxelValid = false; - if (ciftiMapFile != NULL) { - ciftiMapFile->getBrainordinateFromRowIndex(selectedCiftiRowIndex, - surfaceStructure, - surfaceNodeIndex, - surfaceNumberOfNodes, - surfaceNodeValid, - voxelIJK, - voxelXYZ, - voxelValid); - } - else if (ciftiTrajFile != NULL) { - ciftiTrajFile->getBrainordinateFromRowIndex(selectedCiftiRowIndex, - surfaceStructure, - surfaceNodeIndex, - surfaceNumberOfNodes, - surfaceNodeValid, - voxelIJK, - voxelXYZ, - voxelValid); - } - else { - errorMessageOut = "Neither CIFTI Mappable nor CIFTI Trajectory file. Has new file type been added?"; - } - - if (surfaceNodeValid) { - SelectionItemSurfaceNode* surfaceID = selectionManager->getSurfaceNodeIdentification(); - const Surface* surface = brain->getPrimaryAnatomicalSurfaceForStructure(surfaceStructure); - if (surface != NULL) { - if ((surfaceNodeIndex >= 0) - && (surfaceNodeIndex < surface->getNumberOfNodes())) { - surfaceID->setSurface(const_cast(surface)); - surfaceID->setBrain(brain); - const float* xyz = surface->getCoordinate(surfaceNodeIndex); - const double doubleXYZ[3] = { xyz[0], xyz[1], xyz[2] }; - surfaceID->setModelXYZ(doubleXYZ); - surfaceID->setNodeNumber(surfaceNodeIndex); + if (ciftiSdsFile != NULL) { + ChartableTwoFileDelegate* chartDelegate = ciftiSdsFile->getChartingDelegate(); + CaretAssert(chartDelegate); + ChartableTwoFileMatrixChart* matrixChart = chartDelegate->getMatrixCharting(); + ChartableTwoFileLineSeriesChart* lineSeriesChart = chartDelegate->getLineSeriesCharting(); + if (matrixChart != NULL) { + int32_t rowCount(0), columnCount(0); + matrixChart->getMatrixDimensions(rowCount, + columnCount); + if (selectedCiftiRowIndex < rowCount) { + const int32_t tabIndex = 0; // selections are same in all tabs + matrixChart->setSelectedRowColumnIndex(tabIndex, + selectedCiftiRowIndex); + if (lineSeriesChart) { + lineSeriesChart->loadDataForRowOrColumn(tabIndex, + selectedCiftiRowIndex); + } - GuiManager::get()->processIdentification(-1, // invalid tab index - selectionManager, - this); - } - else { - errorMessageOut = ("Surface vertex index " - + AString::number(surfaceNodeIndex) - + " is not valid for surface " - + surface->getFileNameNoPath()); + EventManager::get()->sendEvent(EventUserInterfaceUpdate().getPointer()); + EventManager::get()->sendEvent(EventGraphicsUpdateAllWindows().getPointer()); } } - else{ - errorMessageOut = ("No surfaces are loaded for structure " - + StructureEnum::toGuiName(surfaceStructure)); + } + else { + if (ciftiMapFile != NULL) { + ciftiMapFile->getBrainordinateFromRowIndex(selectedCiftiRowIndex, + surfaceStructure, + surfaceNodeIndex, + surfaceNumberOfNodes, + surfaceNodeValid, + voxelIJK, + voxelXYZ, + voxelValid); + } + else if (ciftiTrajFile != NULL) { + ciftiTrajFile->getBrainordinateFromRowIndex(selectedCiftiRowIndex, + surfaceStructure, + surfaceNodeIndex, + surfaceNumberOfNodes, + surfaceNodeValid, + voxelIJK, + voxelXYZ, + voxelValid); + } + else { + errorMessageOut = "Neither CIFTI Mappable nor CIFTI Trajectory file. Has new file type been added?"; } - } - else if (voxelValid) { - SelectionItemVoxel* voxelID = selectionManager->getVoxelIdentification(); - voxelID->setBrain(brain); - voxelID->setEnabledForSelection(true); - voxelID->setVoxelIdentification(brain, - ciftiMapFile, - voxelIJK, - 0.0); - const double doubleXYZ[3] = { voxelXYZ[0], voxelXYZ[1], voxelXYZ[2] }; - voxelID->setModelXYZ(doubleXYZ); - GuiManager::get()->processIdentification(-1, // invalid tab index - selectionManager, - this); + if (surfaceNodeValid) { + SelectionItemSurfaceNode* surfaceID = selectionManager->getSurfaceNodeIdentification(); + const Surface* surface = brain->getPrimaryAnatomicalSurfaceForStructure(surfaceStructure); + if (surface != NULL) { + if ((surfaceNodeIndex >= 0) + && (surfaceNodeIndex < surface->getNumberOfNodes())) { + surfaceID->setSurface(const_cast(surface)); + surfaceID->setBrain(brain); + const float* xyz = surface->getCoordinate(surfaceNodeIndex); + const double doubleXYZ[3] = { xyz[0], xyz[1], xyz[2] }; + surfaceID->setModelXYZ(doubleXYZ); + surfaceID->setNodeNumber(surfaceNodeIndex); + + GuiManager::get()->processIdentification(-1, // invalid tab index + selectionManager, + this); + } + else { + errorMessageOut = ("Surface vertex index " + + AString::number(surfaceNodeIndex) + + " is not valid for surface " + + surface->getFileNameNoPath()); + } + } + else{ + errorMessageOut = ("No surfaces are loaded for structure " + + StructureEnum::toGuiName(surfaceStructure)); + } + + } + else if (voxelValid) { + SelectionItemVoxel* voxelID = selectionManager->getVoxelIdentification(); + voxelID->setBrain(brain); + voxelID->setEnabledForSelection(true); + voxelID->setVoxelIdentification(brain, + ciftiMapFile, + voxelIJK, + 0.0); + const double doubleXYZ[3] = { voxelXYZ[0], voxelXYZ[1], voxelXYZ[2] }; + voxelID->setModelXYZ(doubleXYZ); + + GuiManager::get()->processIdentification(-1, // invalid tab index + selectionManager, + this); + } } } catch (const DataFileException& dfe) { -- GitLab From ffee494c37fdd8fb76f7bfd42941a6dcdd8bf568 Mon Sep 17 00:00:00 2001 From: John Harwell Date: Wed, 12 Jun 2019 12:54:03 -0500 Subject: [PATCH 361/427] Added WuQSpinBox that emits a return pressed signal for a QSpinBox. --- src/GuiQt/CMakeLists.txt | 3 ++ src/GuiQt/WuQSpinBox.cxx | 66 ++++++++++++++++++++++++++++++++++++++++ src/GuiQt/WuQSpinBox.h | 66 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 135 insertions(+) create mode 100644 src/GuiQt/WuQSpinBox.cxx create mode 100644 src/GuiQt/WuQSpinBox.h diff --git a/src/GuiQt/CMakeLists.txt b/src/GuiQt/CMakeLists.txt index 1f492edc9..e7a651749 100644 --- a/src/GuiQt/CMakeLists.txt +++ b/src/GuiQt/CMakeLists.txt @@ -225,6 +225,7 @@ ELSE() WuQMessageBox.h WuQwtPlot.h WuQSpecialIncrementDoubleSpinBox.h + WuQSpinBox.h WuQSpinBoxGroup.h WuQSpinBoxOddValue.h WuQTabBar.h @@ -510,6 +511,7 @@ WuQMacroWidgetAction.h WuQMessageBox.h WuQwtPlot.h WuQSpecialIncrementDoubleSpinBox.h +WuQSpinBox.h WuQSpinBoxGroup.h WuQSpinBoxOddValue.h WuQTabBar.h @@ -777,6 +779,7 @@ WuQMacroWidgetAction.cxx WuQMessageBox.cxx WuQwtPlot.cxx WuQSpecialIncrementDoubleSpinBox.cxx +WuQSpinBox.cxx WuQSpinBoxGroup.cxx WuQSpinBoxOddValue.cxx WuQTabBar.cxx diff --git a/src/GuiQt/WuQSpinBox.cxx b/src/GuiQt/WuQSpinBox.cxx new file mode 100644 index 000000000..9a9f14962 --- /dev/null +++ b/src/GuiQt/WuQSpinBox.cxx @@ -0,0 +1,66 @@ + +/*LICENSE_START*/ +/* + * Copyright (C) 2019 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + +#define __WU_Q_SPIN_BOX_DECLARE__ +#include "WuQSpinBox.h" +#undef __WU_Q_SPIN_BOX_DECLARE__ + +#include + +#include "CaretAssert.h" + +using namespace caret; + +#include + + +/** + * \class caret::WuQSpinBox + * \brief Emits a signal when the return key is pressed + * \ingroup GuiQt + */ + +/** + * Constructor. + */ +WuQSpinBox::WuQSpinBox() +: QSpinBox() +{ +} + +/** + * Destructor. + */ +WuQSpinBox::~WuQSpinBox() +{ +} + +void +WuQSpinBox::keyPressEvent(QKeyEvent* event) +{ + if (event->key() == Qt::Key_Return) { + std::cout << "Return key pressed"; + emit signalReturnPressed(); + } + + QSpinBox::keyPressEvent(event); +} + diff --git a/src/GuiQt/WuQSpinBox.h b/src/GuiQt/WuQSpinBox.h new file mode 100644 index 000000000..d11858300 --- /dev/null +++ b/src/GuiQt/WuQSpinBox.h @@ -0,0 +1,66 @@ +#ifndef __WU_Q_SPIN_BOX_H__ +#define __WU_Q_SPIN_BOX_H__ + +/*LICENSE_START*/ +/* + * Copyright (C) 2019 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + + + +#include + +#include + + + +namespace caret { + + class WuQSpinBox : public QSpinBox { + + Q_OBJECT + + public: + WuQSpinBox(); + + virtual ~WuQSpinBox(); + + WuQSpinBox(const WuQSpinBox&) = delete; + + WuQSpinBox& operator=(const WuQSpinBox&) = delete; + + + // ADD_NEW_METHODS_HERE + + signals: + void signalReturnPressed(); + + protected: + void keyPressEvent(QKeyEvent* event) override; + + private: + // ADD_NEW_MEMBERS_HERE + + }; + +#ifdef __WU_Q_SPIN_BOX_DECLARE__ + // +#endif // __WU_Q_SPIN_BOX_DECLARE__ + +} // namespace +#endif //__WU_Q_SPIN_BOX_H__ -- GitLab From a21c36cee1ed2189f2e3bd81ad0541c19ae7b201 Mon Sep 17 00:00:00 2001 From: John Harwell Date: Wed, 12 Jun 2019 12:55:09 -0500 Subject: [PATCH 362/427] WB-830 Line Series Yoked Within File Even When Yoking Off: Updated Cifti Row Spin Box so that if row is identified if the user presses the return key. --- src/GuiQt/IdentifyBrainordinateDialog.cxx | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/GuiQt/IdentifyBrainordinateDialog.cxx b/src/GuiQt/IdentifyBrainordinateDialog.cxx index 249e4a1c1..8fb622d2d 100644 --- a/src/GuiQt/IdentifyBrainordinateDialog.cxx +++ b/src/GuiQt/IdentifyBrainordinateDialog.cxx @@ -72,6 +72,7 @@ using namespace caret; #include "WuQFactory.h" #include "WuQGroupBoxExclusiveWidget.h" #include "WuQMessageBox.h" +#include "WuQSpinBox.h" #include "WuQtUtilities.h" #include @@ -338,12 +339,12 @@ IdentifyBrainordinateDialog::createCiftiRowWidget(const std::vector::max(), - 1); - QObject::connect(m_ciftiRowFileIndexSpinBox, SIGNAL(editingFinished()), + m_ciftiRowFileIndexSpinBox = new WuQSpinBox(); + m_ciftiRowFileIndexSpinBox->setMinimum(1); + m_ciftiRowFileIndexSpinBox->setMaximum(std::numeric_limits::max()); + QObject::connect(m_ciftiRowFileIndexSpinBox, SIGNAL(signalReturnPressed()), this, SLOT(apply())); - + m_ciftiRowFileIndexSpinBox->setFixedWidth(INDEX_SPIN_BOX_WIDTH); switch (CiftiMappableDataFile::getCiftiFileRowColumnIndexBaseForGUI()) { case 0: -- GitLab From 865ab8bd00ebe27e784b404fd529bda3b102f14c Mon Sep 17 00:00:00 2001 From: John Harwell Date: Wed, 12 Jun 2019 14:33:05 -0500 Subject: [PATCH 363/427] Fix in WuQSpinBox: Need to avoid calling parent's keyPressEvent() when the return key is processed in WuQSpinBox. --- src/GuiQt/WuQSpinBox.cxx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/GuiQt/WuQSpinBox.cxx b/src/GuiQt/WuQSpinBox.cxx index 9a9f14962..80fc73658 100644 --- a/src/GuiQt/WuQSpinBox.cxx +++ b/src/GuiQt/WuQSpinBox.cxx @@ -57,8 +57,8 @@ void WuQSpinBox::keyPressEvent(QKeyEvent* event) { if (event->key() == Qt::Key_Return) { - std::cout << "Return key pressed"; emit signalReturnPressed(); + return; } QSpinBox::keyPressEvent(event); -- GitLab From a86e74e073936e93ba5ace0cb47fd464966c3497 Mon Sep 17 00:00:00 2001 From: John Harwell Date: Thu, 13 Jun 2019 12:07:18 -0500 Subject: [PATCH 364/427] Coloring code for CIFTI volume slice viewing was incorrectly setting the alpha value. The RGBA colors are unsigned bytes but the alpha value was treated as a float. It has been corrected to assign the byte alpha value to the voxel and not multiply the alpha by 255 (incorrectly converting float to byte). This resulted in a numerical overflow and and incorrect alpha value. --- src/Files/CiftiMappableDataFile.cxx | 147 ++++++++++------------------ 1 file changed, 51 insertions(+), 96 deletions(-) diff --git a/src/Files/CiftiMappableDataFile.cxx b/src/Files/CiftiMappableDataFile.cxx index 450cb4b0c..0265b405d 100644 --- a/src/Files/CiftiMappableDataFile.cxx +++ b/src/Files/CiftiMappableDataFile.cxx @@ -3230,17 +3230,9 @@ CiftiMappableDataFile::getVoxelColorsForSliceInMap(const int32_t mapIndex, rgbaOut[rgbaOffset] = mapRGBA[dataOffset4]; rgbaOut[rgbaOffset+1] = mapRGBA[dataOffset4+1]; rgbaOut[rgbaOffset+2] = mapRGBA[dataOffset4+2]; - /* - * A negative value for alpha indicates "do not draw". - * Since unsigned bytes do not have negative values, - * change the value to zero (which indicates "transparent"). - */ - float alpha = mapRGBA[dataOffset4+3]; - if (alpha < 0.0) { - alpha = 0.0; - } + uint8_t alpha = mapRGBA[dataOffset4+3]; - if (alpha > 0.0) { + if (alpha > 0) { if (labelTable != NULL) { /* * For label data, verify that the label is displayed. @@ -3254,17 +3246,17 @@ CiftiMappableDataFile::getVoxelColorsForSliceInMap(const int32_t mapIndex, const GroupAndNameHierarchyItem* item = label->getGroupNameSelectionItem(); CaretAssert(item); if (item->isSelected(displayGroup, tabIndex) == false) { - alpha = 0.0; + alpha = 0; } } } } - if (alpha > 0.0) { + if (alpha > 0) { ++validVoxelCount; } - rgbaOut[rgbaOffset+3] = (alpha * 255.0); + rgbaOut[rgbaOffset+3] = alpha; } } } @@ -3284,17 +3276,9 @@ CiftiMappableDataFile::getVoxelColorsForSliceInMap(const int32_t mapIndex, rgbaOut[rgbaOffset] = mapRGBA[dataOffset4]; rgbaOut[rgbaOffset+1] = mapRGBA[dataOffset4+1]; rgbaOut[rgbaOffset+2] = mapRGBA[dataOffset4+2]; - /* - * A negative value for alpha indicates "do not draw". - * Since unsigned bytes do not have negative values, - * change the value to zero (which indicates "transparent"). - */ - float alpha = mapRGBA[dataOffset4+3]; - if (alpha < 0.0) { - alpha = 0.0; - } + uint8_t alpha = mapRGBA[dataOffset4+3]; - if (alpha > 0.0) { + if (alpha > 0) { if (labelTable != NULL) { /* * For label data, verify that the label is displayed. @@ -3308,17 +3292,17 @@ CiftiMappableDataFile::getVoxelColorsForSliceInMap(const int32_t mapIndex, const GroupAndNameHierarchyItem* item = label->getGroupNameSelectionItem(); CaretAssert(item); if (item->isSelected(displayGroup, tabIndex) == false) { - alpha = 0.0; + alpha = 0; } } } } - if (alpha > 0.0) { + if (alpha > 0) { ++validVoxelCount; } - rgbaOut[rgbaOffset+3] = (alpha * 255.0); + rgbaOut[rgbaOffset+3] = alpha; } } } @@ -3338,17 +3322,12 @@ CiftiMappableDataFile::getVoxelColorsForSliceInMap(const int32_t mapIndex, rgbaOut[rgbaOffset] = mapRGBA[dataOffset4]; rgbaOut[rgbaOffset+1] = mapRGBA[dataOffset4+1]; rgbaOut[rgbaOffset+2] = mapRGBA[dataOffset4+2]; - /* - * A negative value for alpha indicates "do not draw". - * Since unsigned bytes do not have negative values, - * change the value to zero (which indicates "transparent"). - */ - float alpha = mapRGBA[dataOffset4+3]; - if (alpha < 0.0) { - alpha = 0.0; + uint8_t alpha = mapRGBA[dataOffset4+3]; + if (alpha < 0) { + alpha = 0; } - if (alpha > 0.0) { + if (alpha > 0) { if (labelTable != NULL) { /* * For label data, verify that the label is displayed. @@ -3362,17 +3341,17 @@ CiftiMappableDataFile::getVoxelColorsForSliceInMap(const int32_t mapIndex, const GroupAndNameHierarchyItem* item = label->getGroupNameSelectionItem(); CaretAssert(item); if (item->isSelected(displayGroup, tabIndex) == false) { - alpha = 0.0; + alpha = 0; } } } } - if (alpha > 0.0) { + if (alpha > 0) { ++validVoxelCount; } - rgbaOut[rgbaOffset+3] = (alpha * 255.0); + rgbaOut[rgbaOffset+3] = alpha; } } } @@ -3475,18 +3454,9 @@ CiftiMappableDataFile::getVoxelColorsForSliceInMap(const int32_t mapIndex, rgba[0] = mapRGBA[dataOffset4]; rgba[1] = mapRGBA[dataOffset4+1]; rgba[2] = mapRGBA[dataOffset4+2]; + uint8_t alpha = mapRGBA[dataOffset4+3]; - /* - * A negative value for alpha indicates "do not draw". - * Since unsigned bytes do not have negative values, - * change the value to zero (which indicates "transparent"). - */ - float alpha = mapRGBA[dataOffset4+3]; - if (alpha < 0.0) { - alpha = 0.0; - } - - if (alpha > 0.0) { + if (alpha > 0) { if (labelTable != NULL) { /* * For label data, verify that the label is displayed. @@ -3500,23 +3470,32 @@ CiftiMappableDataFile::getVoxelColorsForSliceInMap(const int32_t mapIndex, const GroupAndNameHierarchyItem* item = label->getGroupNameSelectionItem(); CaretAssert(item); if (item->isSelected(displayGroup, tabIndex) == false) { - alpha = 0.0; + alpha = 0; } } } } - if (alpha > 0.0) { + if (alpha > 0) { ++validVoxelCount; } - rgba[3] = (alpha * 255.0); + rgba[3] = alpha; } - rgbaOut[rgbaOutIndex4] = rgba[0]; - rgbaOut[rgbaOutIndex4+1] = rgba[1]; - rgbaOut[rgbaOutIndex4+2] = rgba[2]; - rgbaOut[rgbaOutIndex4+3] = rgba[3]; + if (rgba[3] > 0) { + rgbaOut[rgbaOutIndex4] = rgba[0]; + rgbaOut[rgbaOutIndex4+1] = rgba[1]; + rgbaOut[rgbaOutIndex4+2] = rgba[2]; + rgbaOut[rgbaOutIndex4+3] = rgba[3]; + } + else { + /* Fixes blending */ + rgbaOut[rgbaOutIndex4] = 0; + rgbaOut[rgbaOutIndex4+1] = 0; + rgbaOut[rgbaOutIndex4+2] = 0; + rgbaOut[rgbaOutIndex4+3] = 0; + } rgbaOutIndex4 += 4; if (rgba[3] > 0) { @@ -3709,17 +3688,9 @@ CiftiMappableDataFile::getVoxelColorsForSubSliceInMap(const int32_t mapIndex, rgbaOut[rgbaOffset] = mapRGBA[dataOffset4]; rgbaOut[rgbaOffset+1] = mapRGBA[dataOffset4+1]; rgbaOut[rgbaOffset+2] = mapRGBA[dataOffset4+2]; - /* - * A negative value for alpha indicates "do not draw". - * Since unsigned bytes do not have negative values, - * change the value to zero (which indicates "transparent"). - */ - float alpha = mapRGBA[dataOffset4+3]; - if (alpha < 0.0) { - alpha = 0.0; - } + uint8_t alpha = mapRGBA[dataOffset4+3]; - if (alpha > 0.0) { + if (alpha > 0) { if (labelTable != NULL) { /* * For label data, verify that the label is displayed. @@ -3733,17 +3704,17 @@ CiftiMappableDataFile::getVoxelColorsForSubSliceInMap(const int32_t mapIndex, const GroupAndNameHierarchyItem* item = label->getGroupNameSelectionItem(); CaretAssert(item); if (item->isSelected(displayGroup, tabIndex) == false) { - alpha = 0.0; + alpha = 0; } } } } - if (alpha > 0.0) { + if (alpha > 0) { ++validVoxelCount; } - rgbaOut[rgbaOffset+3] = (alpha * 255.0); + rgbaOut[rgbaOffset+3] = alpha; } if (i == iEnd) { @@ -3789,17 +3760,9 @@ CiftiMappableDataFile::getVoxelColorsForSubSliceInMap(const int32_t mapIndex, rgbaOut[rgbaOffset] = mapRGBA[dataOffset4]; rgbaOut[rgbaOffset+1] = mapRGBA[dataOffset4+1]; rgbaOut[rgbaOffset+2] = mapRGBA[dataOffset4+2]; - /* - * A negative value for alpha indicates "do not draw". - * Since unsigned bytes do not have negative values, - * change the value to zero (which indicates "transparent"). - */ - float alpha = mapRGBA[dataOffset4+3]; - if (alpha < 0.0) { - alpha = 0.0; - } + uint8_t alpha = mapRGBA[dataOffset4+3]; - if (alpha > 0.0) { + if (alpha > 0) { if (labelTable != NULL) { /* * For label data, verify that the label is displayed. @@ -3813,17 +3776,17 @@ CiftiMappableDataFile::getVoxelColorsForSubSliceInMap(const int32_t mapIndex, const GroupAndNameHierarchyItem* item = label->getGroupNameSelectionItem(); CaretAssert(item); if (item->isSelected(displayGroup, tabIndex) == false) { - alpha = 0.0; + alpha = 0; } } } } - if (alpha > 0.0) { + if (alpha > 0) { ++validVoxelCount; } - rgbaOut[rgbaOffset+3] = (alpha * 255.0); + rgbaOut[rgbaOffset+3] = alpha; } @@ -3870,17 +3833,9 @@ CiftiMappableDataFile::getVoxelColorsForSubSliceInMap(const int32_t mapIndex, rgbaOut[rgbaOffset] = mapRGBA[dataOffset4]; rgbaOut[rgbaOffset+1] = mapRGBA[dataOffset4+1]; rgbaOut[rgbaOffset+2] = mapRGBA[dataOffset4+2]; - /* - * A negative value for alpha indicates "do not draw". - * Since unsigned bytes do not have negative values, - * change the value to zero (which indicates "transparent"). - */ - float alpha = mapRGBA[dataOffset4+3]; - if (alpha < 0.0) { - alpha = 0.0; - } + uint8_t alpha = mapRGBA[dataOffset4+3]; - if (alpha > 0.0) { + if (alpha > 0) { if (labelTable != NULL) { /* * For label data, verify that the label is displayed. @@ -3894,17 +3849,17 @@ CiftiMappableDataFile::getVoxelColorsForSubSliceInMap(const int32_t mapIndex, const GroupAndNameHierarchyItem* item = label->getGroupNameSelectionItem(); CaretAssert(item); if (item->isSelected(displayGroup, tabIndex) == false) { - alpha = 0.0; + alpha = 0; } } } } - if (alpha > 0.0) { + if (alpha > 0) { ++validVoxelCount; } - rgbaOut[rgbaOffset+3] = (alpha * 255.0); + rgbaOut[rgbaOffset+3] = alpha; } if (j == jEnd) { @@ -3998,7 +3953,7 @@ CiftiMappableDataFile::getVoxelColorInMapForLabelData(const std::vector& const GroupAndNameHierarchyItem* item = label->getGroupNameSelectionItem(); if (item != NULL) { if (item->isSelected(displayGroup, tabIndex) == false) { - rgbaOut[3] = 0.0; + rgbaOut[3] = 0; } } } -- GitLab From 01550375500553007cdb7d79d5469e241a636d1a Mon Sep 17 00:00:00 2001 From: John Harwell Date: Thu, 13 Jun 2019 12:07:48 -0500 Subject: [PATCH 365/427] Added drawing of CIFTI voxels to volume texture drawing. --- .../BrainOpenGLVolumeTextureSliceDrawing.cxx | 346 ++++++++++++------ 1 file changed, 230 insertions(+), 116 deletions(-) diff --git a/src/Brain/BrainOpenGLVolumeTextureSliceDrawing.cxx b/src/Brain/BrainOpenGLVolumeTextureSliceDrawing.cxx index b9ce572cd..d416db465 100644 --- a/src/Brain/BrainOpenGLVolumeTextureSliceDrawing.cxx +++ b/src/Brain/BrainOpenGLVolumeTextureSliceDrawing.cxx @@ -2510,7 +2510,7 @@ BrainOpenGLVolumeTextureSliceDrawing::createObliqueTransformationMatrix(const fl /* ======================================================================= */ static bool -getTextureCoordinates(const VolumeFile* vf, +getTextureCoordinates(const VolumeMappableInterface* vf, const float xyz[3], const float maxStr[3], float rstOut[3]) @@ -2547,28 +2547,165 @@ getTextureCoordinates(const VolumeFile* vf, return true; } +//static bool +//createCiftiTexture(const CiftiMappableDataFile* ciftiMapFile, +// const DisplayGroupEnum::Enum displayGroup, +// const int32_t tabIndex, +// const bool allowNonPowerOfTwoTextureFlag, +// std::vector& rgbaColorsOut, +// std::array& textureDimsOut, +// std::array& maxStrOut) +//{ +// CaretAssert(ciftiMapFile); +// const CiftiFile* ciftiFile = ciftiMapFile->getCiftiFile(); +// const CiftiXML& ciftiXML = ciftiMapFile->getCiftiXML(); +// if (ciftiXML.) +// +// return true; +//} + +static bool +createVolumeTexture(const VolumeMappableInterface* volumeFile, + const DisplayGroupEnum::Enum displayGroup, + const int32_t tabIndex, + const bool allowNonPowerOfTwoTextureFlag, + std::vector& rgbaColorsOut, + std::array& textureDimsOut, + std::array& maxStrOut) +{ + maxStrOut.fill(0.0); + + std::vector dims(5); + volumeFile->getDimensions(dims); + textureDimsOut.fill(256); + const int64_t dimLargest = *std::max_element(dims.begin(), dims.begin() + 3); + if (allowNonPowerOfTwoTextureFlag) { + + } + else { + if (dimLargest > 512) { + const CaretMappableDataFile* mapFile = dynamic_cast(volumeFile); + const QString filename((mapFile != NULL) + ? mapFile->getFileNameNoPath() + : "no file name available"); + const QString msg("Dimensions too large for volume texture support. Dimensions=" + + AString::fromNumbers(&dims[0], 3, ",") + + " for volume " + + filename); + CaretLogSevere(msg); + return false; + } + else if (dimLargest > 256) { + textureDimsOut.fill(512); + } + } + + const int64_t mapIndex(0); + const int64_t numberOfSlices = dims[2]; + const int64_t numberOfRows = dims[1]; + const int64_t numberOfColumns = dims[0]; + const int64_t numSliceBytes = (numberOfRows * numberOfColumns * 4); + std::vector rgbaSlice(numSliceBytes); + + if (allowNonPowerOfTwoTextureFlag) { + textureDimsOut[0] = numberOfColumns; + textureDimsOut[1] = numberOfRows; + textureDimsOut[2] = numberOfSlices; + } + const int64_t textureBytes = (textureDimsOut[0] * textureDimsOut[1] * textureDimsOut[2] * 4); + + rgbaColorsOut.clear(); + rgbaColorsOut.resize(textureBytes, 0); + + /* + * To avoid resampling of the voxel data, the texture has larger dimensions + * and the volume's voxels are placed in the bottom left corner of the texture + * and extra texture texel's are zeros. + */ + for (int64_t k = 0; k < numberOfSlices; k++) { + int64_t firstVoxelIJK[3] = { 0, 0, k }; + int64_t rowStepIJK[3] = { 0, 1, 0 }; + int64_t columnStepIJK[3] = { 1, 0, 0 }; + + std::fill(rgbaSlice.begin(), rgbaSlice.end(), 0); + volumeFile->getVoxelColorsForSliceInMap(mapIndex, + firstVoxelIJK, + rowStepIJK, + columnStepIJK, + numberOfRows, + numberOfColumns, + displayGroup, + tabIndex, + &rgbaSlice[0]); + + for (int32_t j = 0; j < numberOfRows; j++) { + for (int32_t i = 0; i < numberOfColumns; i++) { + const int32_t sliceOffset = ((j * numberOfColumns) + i) * 4; + const int32_t textureOffset = ((k * textureDimsOut[0] * textureDimsOut[1]) + + (j * textureDimsOut[0]) + i) * 4; + for (int32_t m = 0; m < 4; m++) { + CaretAssertVectorIndex(rgbaColorsOut, (textureOffset + 3)); + CaretAssertVectorIndex(rgbaSlice, sliceOffset + m); + rgbaColorsOut[textureOffset + m] = rgbaSlice[sliceOffset + m]; + } + } + } + } + + maxStrOut[0] = static_cast(dims[0]) / static_cast(textureDimsOut[0]); + maxStrOut[1] = static_cast(dims[1]) / static_cast(textureDimsOut[1]); + maxStrOut[2] = static_cast(dims[2]) / static_cast(textureDimsOut[2]); + if (debugFlag) std::cout << "max STR: " << AString::fromNumbers(maxStrOut.data(), 3, ",") << std::endl; + + return true; + +} + static GLuint createTextureName(BrainOpenGLFixedPipeline* fixedPipelineDrawing, const VolumeSliceProjectionTypeEnum::Enum sliceProjectionType, - const VolumeFile* vf, + const VolumeMappableInterface* volumeMappableInterface, const DisplayGroupEnum::Enum displayGroup, const int32_t tabIndex, - float maxStrOut[3]) + std::array& maxStrOut) { - std::vector dims(5); - vf->getDimensions(dims); - int64_t textureDims = 256; - const int64_t dimLargest = *std::max_element(dims.begin(), dims.begin() + 3); - if (dimLargest > 512) { - const QString msg("Volume dimensions too large for texture support. Dimensions=" - + AString::fromNumbers(&dims[0], 3, ",") - + " for volume " - + vf->getFileNameNoPath()); - CaretLogSevere(msg); - return 0; + const CaretMappableDataFile* mapFile = dynamic_cast(volumeMappableInterface); + CaretAssert(mapFile); + + const VolumeFile* volumeFile = dynamic_cast(volumeMappableInterface); + const CiftiMappableDataFile* ciftiFile = dynamic_cast(volumeMappableInterface); + + std::vector rgbaColors; + std::array textureDims; + + /* + * OpenGL 2.0 or later supports non-power-of-two texture dimensions + */ + const bool allowNonPowerOfTwoTextureFlag(true); + if (volumeFile != NULL) { + if ( ! createVolumeTexture(volumeFile, + displayGroup, + tabIndex, + allowNonPowerOfTwoTextureFlag, + rgbaColors, + textureDims, + maxStrOut)) { + return 0; + } } - else if (dimLargest > 256) { - textureDims = 512; + else if (ciftiFile != NULL) { + if ( ! createVolumeTexture(ciftiFile, + displayGroup, + tabIndex, + allowNonPowerOfTwoTextureFlag, + rgbaColors, + textureDims, + maxStrOut)) { + return 0; + } + } + else { + CaretAssert(0); } GLuint textureName(0); @@ -2593,7 +2730,7 @@ createTextureName(BrainOpenGLFixedPipeline* fixedPipelineDrawing, * - Never use GL_CLAMP, use GL_CLAMP_TO_EDGE */ - if (vf->isMappedWithPalette()) { + if (mapFile->isMappedWithPalette()) { /* * This combination MIN=Linear, MAG=Nearest * seems to produce voxel drawing that is @@ -2651,114 +2788,90 @@ createTextureName(BrainOpenGLFixedPipeline* fixedPipelineDrawing, GLfloat borderColor[4] = { 0.0, 0.0, 0.0, 0.0 }; glTexParameterfv(GL_TEXTURE_3D, GL_TEXTURE_BORDER_COLOR, borderColor); - - - /* - * Clamp to edge seems to extend any data that is an edge voxel such as - * an ear in slice -18 in glassr volume - */ - glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); - glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); - glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE); - - /* - * Clamp to border seems to work best - */ - glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER); - glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER); - glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_BORDER); - } else { + /* + * No interpolation for Label or RGBA data + */ glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); - - glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); - glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); - glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE); } + /* + * Always clamp to border. If no texels (voxels) are available, pixel + * maps to area outside of the volume, the border color is used. + */ + glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER); + glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER); + glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_BORDER); - const int64_t mapIndex(0); - const int64_t numberOfSlices = dims[2]; - const int64_t numberOfRows = dims[1]; - const int64_t numberOfColumns = dims[0]; - const int64_t numSliceBytes = (numberOfRows * numberOfColumns * 4); - std::vector rgbaSlice(numSliceBytes); - - const int64_t numberOfBytes = (numberOfSlices * numSliceBytes); - std::vector rgbaColors; - rgbaColors.reserve(numberOfBytes); - - const int64_t textureBytes = (textureDims * textureDims * textureDims * 4); - - std::vector texture(textureBytes, 0); - - for (int64_t k = 0; k < numberOfSlices; k++) { - int64_t firstVoxelIJK[3] = { 0, 0, k }; - int64_t rowStepIJK[3] = { 0, 1, 0 }; - int64_t columnStepIJK[3] = { 1, 0, 0 }; - - std::fill(rgbaSlice.begin(), rgbaSlice.end(), 0); - vf->getVoxelColorsForSliceInMap(mapIndex, - firstVoxelIJK, - rowStepIJK, - columnStepIJK, - numberOfRows, - numberOfColumns, - displayGroup, - tabIndex, - &rgbaSlice[0]); - - const bool edgeFlag(false); - for (int32_t j = 0; j < numberOfRows; j++) { - for (int32_t i = 0; i < numberOfColumns; i++) { - const int32_t sliceOffset = ((j * numberOfColumns) + i) * 4; - const int32_t textureOffset = ((k * textureDims * textureDims) - + (j * textureDims) + i) * 4; - for (int32_t m = 0; m < 4; m++) { - CaretAssertVectorIndex(texture, (textureOffset + 3)); - CaretAssertVectorIndex(rgbaSlice, sliceOffset + m); - texture[textureOffset + m] = rgbaSlice[sliceOffset + m]; - } - - if (edgeFlag) { - if (i == (numberOfColumns - 1)) { - if (numberOfColumns < textureDims) { - for (int32_t m = 0; m < 4; m++) { - CaretAssertVectorIndex(texture, (textureOffset + 3 + 4)); - CaretAssertVectorIndex(rgbaSlice, sliceOffset + m); - texture[textureOffset + m + 4] = rgbaSlice[sliceOffset + m]; - } - texture[textureOffset + 3 + 4] = 0; - } - } - } - } - } - - rgbaColors.insert(rgbaColors.end(), - rgbaSlice.begin(), rgbaSlice.end()); - } - CaretAssert(static_cast(rgbaColors.size()) == numberOfBytes); - CaretAssert(static_cast(rgbaColors.size()) == - (numberOfColumns * numberOfRows * numberOfSlices * 4)); - - maxStrOut[0] = static_cast(dims[0]) / static_cast(textureDims); - maxStrOut[1] = static_cast(dims[1]) / static_cast(textureDims); - maxStrOut[2] = static_cast(dims[2]) / static_cast(textureDims); - if (debugFlag) std::cout << "max STR: " << AString::fromNumbers(maxStrOut, 3, ",") << std::endl; +// const int64_t mapIndex(0); +// const int64_t numberOfSlices = dims[2]; +// const int64_t numberOfRows = dims[1]; +// const int64_t numberOfColumns = dims[0]; +// const int64_t numSliceBytes = (numberOfRows * numberOfColumns * 4); +// std::vector rgbaSlice(numSliceBytes); +// +// const int64_t numberOfBytes = (numberOfSlices * numSliceBytes); +// std::vector rgbaColors; +// rgbaColors.reserve(numberOfBytes); +// +// const int64_t textureBytes = (textureDims * textureDims * textureDims * 4); +// +// std::vector texture(textureBytes, 0); +// +// for (int64_t k = 0; k < numberOfSlices; k++) { +// int64_t firstVoxelIJK[3] = { 0, 0, k }; +// int64_t rowStepIJK[3] = { 0, 1, 0 }; +// int64_t columnStepIJK[3] = { 1, 0, 0 }; +// +// std::fill(rgbaSlice.begin(), rgbaSlice.end(), 0); +// volumeFile->getVoxelColorsForSliceInMap(mapIndex, +// firstVoxelIJK, +// rowStepIJK, +// columnStepIJK, +// numberOfRows, +// numberOfColumns, +// displayGroup, +// tabIndex, +// &rgbaSlice[0]); +// +// for (int32_t j = 0; j < numberOfRows; j++) { +// for (int32_t i = 0; i < numberOfColumns; i++) { +// const int32_t sliceOffset = ((j * numberOfColumns) + i) * 4; +// const int32_t textureOffset = ((k * textureDims * textureDims) +// + (j * textureDims) + i) * 4; +// for (int32_t m = 0; m < 4; m++) { +// CaretAssertVectorIndex(texture, (textureOffset + 3)); +// CaretAssertVectorIndex(rgbaSlice, sliceOffset + m); +// texture[textureOffset + m] = rgbaSlice[sliceOffset + m]; +// } +// } +// } +// +// rgbaColors.insert(rgbaColors.end(), +// rgbaSlice.begin(), rgbaSlice.end()); +// } +// CaretAssert(static_cast(rgbaColors.size()) == numberOfBytes); +// CaretAssert(static_cast(rgbaColors.size()) == +// (numberOfColumns * numberOfRows * numberOfSlices * 4)); +// +// maxStrOut[0] = static_cast(dims[0]) / static_cast(textureDims); +// maxStrOut[1] = static_cast(dims[1]) / static_cast(textureDims); +// maxStrOut[2] = static_cast(dims[2]) / static_cast(textureDims); +// if (debugFlag) std::cout << "max STR: " << AString::fromNumbers(maxStrOut, 3, ",") << std::endl; + std::cout << "Texture Dimensions: " << AString::fromNumbers(textureDims.data(), 3, ", ") << std::endl; glTexImage3D(GL_TEXTURE_3D, 0, GL_RGBA, - textureDims, - textureDims, - textureDims, + textureDims[0], + textureDims[1], + textureDims[2], 0, GL_RGBA, GL_UNSIGNED_BYTE, - &texture[0]); + &rgbaColors[0]); glBindTexture(GL_TEXTURE_3D, 0); glPopClientAttrib(); @@ -2771,8 +2884,8 @@ struct TextureInfo { std::array m_maxSTR; }; -static std::map obliqueVolumeTextureInfo; -static std::map orthogonalVolumeTextureInfo; +static std::map obliqueVolumeTextureInfo; +static std::map orthogonalVolumeTextureInfo; /** * Draw an oblique slice with support for outlining labels and thresholded palette data. * @@ -3099,10 +3212,11 @@ BrainOpenGLVolumeTextureSliceDrawing::drawObliqueSliceWithOutlines(const VolumeS bool firstFlag(true); for (int32_t iVol = 0; iVol < numVolumes; iVol++) { const BrainOpenGLFixedPipeline::VolumeDrawInfo& vdi = m_volumeDrawInfo[iVol]; - VolumeFile* vf = dynamic_cast(vdi.volumeFile); + VolumeMappableInterface* vf = vdi.volumeFile; +// VolumeFile* vf = dynamic_cast(vdi.volumeFile); if (vf != NULL) { if (debugFlag) { - std::cout << "Vol: " << iVol << ": " << vf->getFileNameNoPath() << std::endl; + //std::cout << "Vol: " << iVol << ": " << vf->getFileNameNoPath() << std::endl; } if (firstFlag) { @@ -3151,7 +3265,7 @@ BrainOpenGLVolumeTextureSliceDrawing::drawObliqueSliceWithOutlines(const VolumeS vf, m_displayGroup, m_tabIndex, - maxStr.data()); + maxStr); m_fixedPipelineDrawing->testForOpenGLError("After creating texture"); if (textureID != 0) { TextureInfo textureInfo; -- GitLab From f62f87d5d547ba2d6651efaca3b5bd626bc2e625 Mon Sep 17 00:00:00 2001 From: John Harwell Date: Thu, 13 Jun 2019 13:54:25 -0500 Subject: [PATCH 366/427] For the experimental texture volume drawing, add drawing of CIFTI voxel data. --- .../BrainOpenGLVolumeTextureSliceDrawing.cxx | 57 ------------------- 1 file changed, 57 deletions(-) diff --git a/src/Brain/BrainOpenGLVolumeTextureSliceDrawing.cxx b/src/Brain/BrainOpenGLVolumeTextureSliceDrawing.cxx index d416db465..3967444e0 100644 --- a/src/Brain/BrainOpenGLVolumeTextureSliceDrawing.cxx +++ b/src/Brain/BrainOpenGLVolumeTextureSliceDrawing.cxx @@ -2805,63 +2805,6 @@ createTextureName(BrainOpenGLFixedPipeline* fixedPipelineDrawing, glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER); glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_BORDER); -// const int64_t mapIndex(0); -// const int64_t numberOfSlices = dims[2]; -// const int64_t numberOfRows = dims[1]; -// const int64_t numberOfColumns = dims[0]; -// const int64_t numSliceBytes = (numberOfRows * numberOfColumns * 4); -// std::vector rgbaSlice(numSliceBytes); -// -// const int64_t numberOfBytes = (numberOfSlices * numSliceBytes); -// std::vector rgbaColors; -// rgbaColors.reserve(numberOfBytes); -// -// const int64_t textureBytes = (textureDims * textureDims * textureDims * 4); -// -// std::vector texture(textureBytes, 0); -// -// for (int64_t k = 0; k < numberOfSlices; k++) { -// int64_t firstVoxelIJK[3] = { 0, 0, k }; -// int64_t rowStepIJK[3] = { 0, 1, 0 }; -// int64_t columnStepIJK[3] = { 1, 0, 0 }; -// -// std::fill(rgbaSlice.begin(), rgbaSlice.end(), 0); -// volumeFile->getVoxelColorsForSliceInMap(mapIndex, -// firstVoxelIJK, -// rowStepIJK, -// columnStepIJK, -// numberOfRows, -// numberOfColumns, -// displayGroup, -// tabIndex, -// &rgbaSlice[0]); -// -// for (int32_t j = 0; j < numberOfRows; j++) { -// for (int32_t i = 0; i < numberOfColumns; i++) { -// const int32_t sliceOffset = ((j * numberOfColumns) + i) * 4; -// const int32_t textureOffset = ((k * textureDims * textureDims) -// + (j * textureDims) + i) * 4; -// for (int32_t m = 0; m < 4; m++) { -// CaretAssertVectorIndex(texture, (textureOffset + 3)); -// CaretAssertVectorIndex(rgbaSlice, sliceOffset + m); -// texture[textureOffset + m] = rgbaSlice[sliceOffset + m]; -// } -// } -// } -// -// rgbaColors.insert(rgbaColors.end(), -// rgbaSlice.begin(), rgbaSlice.end()); -// } -// CaretAssert(static_cast(rgbaColors.size()) == numberOfBytes); -// CaretAssert(static_cast(rgbaColors.size()) == -// (numberOfColumns * numberOfRows * numberOfSlices * 4)); -// -// maxStrOut[0] = static_cast(dims[0]) / static_cast(textureDims); -// maxStrOut[1] = static_cast(dims[1]) / static_cast(textureDims); -// maxStrOut[2] = static_cast(dims[2]) / static_cast(textureDims); -// if (debugFlag) std::cout << "max STR: " << AString::fromNumbers(maxStrOut, 3, ",") << std::endl; - - std::cout << "Texture Dimensions: " << AString::fromNumbers(textureDims.data(), 3, ", ") << std::endl; glTexImage3D(GL_TEXTURE_3D, 0, GL_RGBA, -- GitLab From 9df5cedb43c7db3b9e34527d861c9f73bbace4fc Mon Sep 17 00:00:00 2001 From: John Harwell Date: Fri, 14 Jun 2019 11:18:15 -0500 Subject: [PATCH 367/427] For the experimental texture volume drawing, added identification and some fixes for ALL view slices. --- src/Brain/BrainOpenGLFixedPipeline.cxx | 63 +- .../BrainOpenGLVolumeTextureSliceDrawing.cxx | 1733 ++++------------- .../BrainOpenGLVolumeTextureSliceDrawing.h | 156 +- 3 files changed, 426 insertions(+), 1526 deletions(-) diff --git a/src/Brain/BrainOpenGLFixedPipeline.cxx b/src/Brain/BrainOpenGLFixedPipeline.cxx index 5fdadd06f..eecac8610 100644 --- a/src/Brain/BrainOpenGLFixedPipeline.cxx +++ b/src/Brain/BrainOpenGLFixedPipeline.cxx @@ -5391,33 +5391,46 @@ BrainOpenGLFixedPipeline::drawWholeBrainModel(BrowserTabContent* browserTabConte VolumeSliceDrawingTypeEnum::Enum sliceDrawingType = browserTabContent->getSliceDrawingType(); VolumeSliceProjectionTypeEnum::Enum sliceProjectionType = browserTabContent->getSliceProjectionType(); - switch (sliceProjectionType) { - case VolumeSliceProjectionTypeEnum::VOLUME_SLICE_PROJECTION_OBLIQUE: - { - VolumeSliceInterpolationEdgeEffectsMaskingEnum::Enum obliqueMaskType = browserTabContent->getVolumeSliceInterpolationEdgeEffectsMaskingType(); - BrainOpenGLVolumeObliqueSliceDrawing volumeSliceDrawing; - volumeSliceDrawing.draw(this, - browserTabContent, - twoDimSliceDrawVolumeDrawInfo, - sliceDrawingType, - sliceProjectionType, - obliqueMaskType, - viewport); - } - break; - case VolumeSliceProjectionTypeEnum::VOLUME_SLICE_PROJECTION_ORTHOGONAL: - { - BrainOpenGLVolumeSliceDrawing volumeSliceDrawing; - volumeSliceDrawing.draw(this, - browserTabContent, - twoDimSliceDrawVolumeDrawInfo, - sliceDrawingType, - sliceProjectionType, - viewport); + if (DeveloperFlagsEnum::isFlag(DeveloperFlagsEnum::DEVELOPER_FLAG_TEXTURE_VOLUME)) { + VolumeSliceInterpolationEdgeEffectsMaskingEnum::Enum obliqueMaskType = browserTabContent->getVolumeSliceInterpolationEdgeEffectsMaskingType(); + BrainOpenGLVolumeTextureSliceDrawing textureSliceDrawing; + textureSliceDrawing.draw(this, + browserTabContent, + twoDimSliceDrawVolumeDrawInfo, + sliceDrawingType, + sliceProjectionType, + obliqueMaskType, + viewport); + } + else { + switch (sliceProjectionType) { + case VolumeSliceProjectionTypeEnum::VOLUME_SLICE_PROJECTION_OBLIQUE: + { + VolumeSliceInterpolationEdgeEffectsMaskingEnum::Enum obliqueMaskType = browserTabContent->getVolumeSliceInterpolationEdgeEffectsMaskingType(); + BrainOpenGLVolumeObliqueSliceDrawing volumeSliceDrawing; + volumeSliceDrawing.draw(this, + browserTabContent, + twoDimSliceDrawVolumeDrawInfo, + sliceDrawingType, + sliceProjectionType, + obliqueMaskType, + viewport); + } + break; + case VolumeSliceProjectionTypeEnum::VOLUME_SLICE_PROJECTION_ORTHOGONAL: + { + BrainOpenGLVolumeSliceDrawing volumeSliceDrawing; + volumeSliceDrawing.draw(this, + browserTabContent, + twoDimSliceDrawVolumeDrawInfo, + sliceDrawingType, + sliceProjectionType, + viewport); + } + break; } - break; } - + glPopAttrib(); } } diff --git a/src/Brain/BrainOpenGLVolumeTextureSliceDrawing.cxx b/src/Brain/BrainOpenGLVolumeTextureSliceDrawing.cxx index 3967444e0..046e0fb94 100644 --- a/src/Brain/BrainOpenGLVolumeTextureSliceDrawing.cxx +++ b/src/Brain/BrainOpenGLVolumeTextureSliceDrawing.cxx @@ -2509,11 +2509,11 @@ BrainOpenGLVolumeTextureSliceDrawing::createObliqueTransformationMatrix(const fl /* ======================================================================= */ -static bool -getTextureCoordinates(const VolumeMappableInterface* vf, - const float xyz[3], - const float maxStr[3], - float rstOut[3]) +bool +BrainOpenGLVolumeTextureSliceDrawing::getTextureCoordinates(const VolumeMappableInterface* vf, + const float xyz[3], + const float maxStr[3], + float rstOut[3]) const { std::vector dims(5); vf->getDimensions(dims); @@ -2525,10 +2525,29 @@ getTextureCoordinates(const VolumeMappableInterface* vf, float bigCornerXYZ[3] = { 0.0, 0.0, 0.0 }; vf->indexToSpace(bigCornerIJK, bigCornerXYZ); + /* + * Coordinates from volume are at CENTER of the voxel + * so increase size of volume so that range is from + * outside edge to outside edge of the volume. + */ + float voxelOneXYZ[3]; + vf->indexToSpace(0, 0, 0, voxelOneXYZ); + float voxelTwoXYZ[3]; + vf->indexToSpace(1, 1, 1, voxelTwoXYZ); + const float halfVoxelSizeXYZ[3] { + (voxelTwoXYZ[0] - voxelOneXYZ[0]) / 2.0f, + (voxelTwoXYZ[1] - voxelOneXYZ[1]) / 2.0f, + (voxelTwoXYZ[2] - voxelOneXYZ[2]) / 2.0f, + }; + for (int32_t i = 0; i < 3; i++) { + smallCornerXYZ[i] -= halfVoxelSizeXYZ[i]; + bigCornerXYZ[i] += halfVoxelSizeXYZ[i]; + } + const float rangeXYZ[3] = { - bigCornerXYZ[0] - smallCornerXYZ[0], - bigCornerXYZ[1] - smallCornerXYZ[1], - bigCornerXYZ[2] - smallCornerXYZ[2] + (bigCornerXYZ[0] - smallCornerXYZ[0]), + (bigCornerXYZ[1] - smallCornerXYZ[1]), + (bigCornerXYZ[2] - smallCornerXYZ[2]) }; const float normalizedOffset[3] = { @@ -2543,38 +2562,22 @@ getTextureCoordinates(const VolumeMappableInterface* vf, rstOut[0] = normalizedOffset[0] * maxStr[0]; rstOut[1] = normalizedOffset[1] * maxStr[1]; rstOut[2] = normalizedOffset[2] * maxStr[2]; - + return true; } -//static bool -//createCiftiTexture(const CiftiMappableDataFile* ciftiMapFile, -// const DisplayGroupEnum::Enum displayGroup, -// const int32_t tabIndex, -// const bool allowNonPowerOfTwoTextureFlag, -// std::vector& rgbaColorsOut, -// std::array& textureDimsOut, -// std::array& maxStrOut) -//{ -// CaretAssert(ciftiMapFile); -// const CiftiFile* ciftiFile = ciftiMapFile->getCiftiFile(); -// const CiftiXML& ciftiXML = ciftiMapFile->getCiftiXML(); -// if (ciftiXML.) -// -// return true; -//} - -static bool -createVolumeTexture(const VolumeMappableInterface* volumeFile, - const DisplayGroupEnum::Enum displayGroup, - const int32_t tabIndex, - const bool allowNonPowerOfTwoTextureFlag, - std::vector& rgbaColorsOut, - std::array& textureDimsOut, - std::array& maxStrOut) +bool +BrainOpenGLVolumeTextureSliceDrawing::createVolumeTexture(const VolumeMappableInterface* volumeFile, + const DisplayGroupEnum::Enum displayGroup, + const int32_t tabIndex, + const bool allowNonPowerOfTwoTextureFlag, + const bool identificationTextureFlag, + std::vector& rgbaColorsOut, + std::array& textureDimsOut, + std::array& maxStrOut) const { maxStrOut.fill(0.0); - + std::vector dims(5); volumeFile->getDimensions(dims); textureDimsOut.fill(256); @@ -2599,13 +2602,12 @@ createVolumeTexture(const VolumeMappableInterface* volumeFile, textureDimsOut.fill(512); } } - + const int64_t mapIndex(0); const int64_t numberOfSlices = dims[2]; const int64_t numberOfRows = dims[1]; const int64_t numberOfColumns = dims[0]; const int64_t numSliceBytes = (numberOfRows * numberOfColumns * 4); - std::vector rgbaSlice(numSliceBytes); if (allowNonPowerOfTwoTextureFlag) { textureDimsOut[0] = numberOfColumns; @@ -2617,36 +2619,68 @@ createVolumeTexture(const VolumeMappableInterface* volumeFile, rgbaColorsOut.clear(); rgbaColorsOut.resize(textureBytes, 0); - /* - * To avoid resampling of the voxel data, the texture has larger dimensions - * and the volume's voxels are placed in the bottom left corner of the texture - * and extra texture texel's are zeros. - */ - for (int64_t k = 0; k < numberOfSlices; k++) { - int64_t firstVoxelIJK[3] = { 0, 0, k }; - int64_t rowStepIJK[3] = { 0, 1, 0 }; - int64_t columnStepIJK[3] = { 1, 0, 0 }; - - std::fill(rgbaSlice.begin(), rgbaSlice.end(), 0); - volumeFile->getVoxelColorsForSliceInMap(mapIndex, - firstVoxelIJK, - rowStepIJK, - columnStepIJK, - numberOfRows, - numberOfColumns, - displayGroup, - tabIndex, - &rgbaSlice[0]); - - for (int32_t j = 0; j < numberOfRows; j++) { - for (int32_t i = 0; i < numberOfColumns; i++) { - const int32_t sliceOffset = ((j * numberOfColumns) + i) * 4; - const int32_t textureOffset = ((k * textureDimsOut[0] * textureDimsOut[1]) - + (j * textureDimsOut[0]) + i) * 4; - for (int32_t m = 0; m < 4; m++) { + if (identificationTextureFlag) { + uint32_t offsetID = 0; + for (int64_t k = 0; k < numberOfSlices; k++) { + for (int32_t j = 0; j < numberOfRows; j++) { + for (int32_t i = 0; i < numberOfColumns; i++) { + const int32_t textureOffset = ((k * textureDimsOut[0] * textureDimsOut[1]) + + (j * textureDimsOut[0]) + i) * 4; CaretAssertVectorIndex(rgbaColorsOut, (textureOffset + 3)); - CaretAssertVectorIndex(rgbaSlice, sliceOffset + m); - rgbaColorsOut[textureOffset + m] = rgbaSlice[sliceOffset + m]; + + /* + * An offset is used and encoded into the R, G, and B bytes. + * While we could use IJK, we cannot if a dimension is + * greater than 255. With the offset, number of voxels + * must only be less than 255**3. + */ + const uint8_t offsetRed = static_cast((offsetID >> 16) & 0xff); + const uint8_t offsetGreen = static_cast((offsetID >> 8) & 0xff); + const uint8_t offsetBlue = static_cast((offsetID) & 0xff); + rgbaColorsOut[textureOffset] = offsetRed; + rgbaColorsOut[textureOffset+1] = offsetGreen; + rgbaColorsOut[textureOffset+2] = offsetBlue; + rgbaColorsOut[textureOffset+3] = 255; + + offsetID++; + } + } + } + } + else { + /* + * When non power-of-two textures are NOT allowed (OpenGL < 2.0) + * To avoid resampling of the voxel data, the texture has larger dimensions + * and the volume's voxels are placed in the bottom left corner of the texture + * and extra texture texel's are zeros. + */ + std::vector rgbaSlice(numSliceBytes); + for (int64_t k = 0; k < numberOfSlices; k++) { + int64_t firstVoxelIJK[3] = { 0, 0, k }; + int64_t rowStepIJK[3] = { 0, 1, 0 }; + int64_t columnStepIJK[3] = { 1, 0, 0 }; + + std::fill(rgbaSlice.begin(), rgbaSlice.end(), 0); + volumeFile->getVoxelColorsForSliceInMap(mapIndex, + firstVoxelIJK, + rowStepIJK, + columnStepIJK, + numberOfRows, + numberOfColumns, + displayGroup, + tabIndex, + &rgbaSlice[0]); + + for (int32_t j = 0; j < numberOfRows; j++) { + for (int32_t i = 0; i < numberOfColumns; i++) { + const int32_t sliceOffset = ((j * numberOfColumns) + i) * 4; + const int32_t textureOffset = ((k * textureDimsOut[0] * textureDimsOut[1]) + + (j * textureDimsOut[0]) + i) * 4; + for (int32_t m = 0; m < 4; m++) { + CaretAssertVectorIndex(rgbaColorsOut, (textureOffset + 3)); + CaretAssertVectorIndex(rgbaSlice, sliceOffset + m); + rgbaColorsOut[textureOffset + m] = rgbaSlice[sliceOffset + m]; + } } } } @@ -2656,18 +2690,17 @@ createVolumeTexture(const VolumeMappableInterface* volumeFile, maxStrOut[1] = static_cast(dims[1]) / static_cast(textureDimsOut[1]); maxStrOut[2] = static_cast(dims[2]) / static_cast(textureDimsOut[2]); if (debugFlag) std::cout << "max STR: " << AString::fromNumbers(maxStrOut.data(), 3, ",") << std::endl; - + return true; - + } -static GLuint -createTextureName(BrainOpenGLFixedPipeline* fixedPipelineDrawing, - const VolumeSliceProjectionTypeEnum::Enum sliceProjectionType, - const VolumeMappableInterface* volumeMappableInterface, - const DisplayGroupEnum::Enum displayGroup, - const int32_t tabIndex, - std::array& maxStrOut) +GLuint +BrainOpenGLVolumeTextureSliceDrawing::createTextureName(const VolumeMappableInterface* volumeMappableInterface, + const bool identificationTextureFlag, + const DisplayGroupEnum::Enum displayGroup, + const int32_t tabIndex, + std::array& maxStrOut) const { const CaretMappableDataFile* mapFile = dynamic_cast(volumeMappableInterface); CaretAssert(mapFile); @@ -2687,6 +2720,7 @@ createTextureName(BrainOpenGLFixedPipeline* fixedPipelineDrawing, displayGroup, tabIndex, allowNonPowerOfTwoTextureFlag, + identificationTextureFlag, rgbaColors, textureDims, maxStrOut)) { @@ -2698,6 +2732,7 @@ createTextureName(BrainOpenGLFixedPipeline* fixedPipelineDrawing, displayGroup, tabIndex, allowNonPowerOfTwoTextureFlag, + identificationTextureFlag, rgbaColors, textureDims, maxStrOut)) { @@ -2710,10 +2745,10 @@ createTextureName(BrainOpenGLFixedPipeline* fixedPipelineDrawing, GLuint textureName(0); glPushClientAttrib(GL_CLIENT_PIXEL_STORE_BIT); - + glGenTextures(1, &textureName); glBindTexture(GL_TEXTURE_3D, textureName); - + glPixelStorei(GL_UNPACK_SWAP_BYTES, GL_FALSE); glPixelStorei(GL_UNPACK_LSB_FIRST, GL_FALSE); glPixelStorei(GL_UNPACK_ROW_LENGTH, 0); @@ -2722,80 +2757,9 @@ createTextureName(BrainOpenGLFixedPipeline* fixedPipelineDrawing, glPixelStorei(GL_UNPACK_SKIP_PIXELS, 0); glPixelStorei(GL_UNPACK_SKIP_IMAGES, 0); glPixelStorei(GL_UNPACK_ALIGNMENT, 1); - + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_BASE_LEVEL, 0); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 0); - /* - * From https://www.khronos.org/opengl/wiki/Common_Mistakes#Texture_edge_color_problem - * - Never use GL_CLAMP, use GL_CLAMP_TO_EDGE - */ - - if (mapFile->isMappedWithPalette()) { - /* - * This combination MIN=Linear, MAG=Nearest - * seems to produce voxel drawing that is - * nearly identical to cubic interpolation when zoomed in so - * that the voxels are large. The difference is when the slices - * are rotated; In cubic interpolation, the "scan lines" are - * horizontal (left to right on the screen) but with texture, - * the "scan lines" follow the volume rotation. - * - * From the OpenGL documentation (man page) - * - * GL_TEXTURE_MIN_FILTER - The texture minifying function is used - * whenever the pixel being textured maps to an area greater than one - * texture element. - * GL_NEAREST - Returns the value of the texture element that is - * nearest (in Manhattan distance) to the center of - * the pixel being textured. - * GL_LINEAR - Returns the weighted average of the four texture - * elements that are closest to the center of the - * pixel being textured. - * - * Pixel area is GREATER THAN texel area so ZOOMED OUT - * - * Thus, Pixel contains more than one texel - */ - glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - - /* - * GL_TEXTURE_MAG_FILTER - The texture magnification function - * is used when the pixel being textured maps to an area less than or - * equal to one texture element. - * GL_NEAREST - Returns the value of the texture element that is - * nearest (in Manhattan distance) to the center of - * the pixel being textured. - * GL_LINEAR Returns the weighted average of the four texture - * elements that are closest to the center of the - * pixel being textured. - * - * Pixel area is LESS THAN Texel area so ZOOMED IN - * - * Thus, pixel may be inside a texel - * - * If GL_LINEAR is used the voxel "blockiness" is smoothed out - */ - glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); - - switch (sliceProjectionType) { - case caret::VolumeSliceProjectionTypeEnum::VOLUME_SLICE_PROJECTION_OBLIQUE: - break; - case caret::VolumeSliceProjectionTypeEnum::VOLUME_SLICE_PROJECTION_ORTHOGONAL: - glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); - glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); - break; - } - - GLfloat borderColor[4] = { 0.0, 0.0, 0.0, 0.0 }; - glTexParameterfv(GL_TEXTURE_3D, GL_TEXTURE_BORDER_COLOR, borderColor); - } - else { - /* - * No interpolation for Label or RGBA data - */ - glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); - glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); - } /* * Always clamp to border. If no texels (voxels) are available, pixel @@ -2804,7 +2768,7 @@ createTextureName(BrainOpenGLFixedPipeline* fixedPipelineDrawing, glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER); glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER); glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_BORDER); - + glTexImage3D(GL_TEXTURE_3D, 0, GL_RGBA, @@ -2815,20 +2779,13 @@ createTextureName(BrainOpenGLFixedPipeline* fixedPipelineDrawing, GL_RGBA, GL_UNSIGNED_BYTE, &rgbaColors[0]); - + glBindTexture(GL_TEXTURE_3D, 0); glPopClientAttrib(); return textureName; } -struct TextureInfo { - GLuint m_textureID; - std::array m_maxSTR; -}; - -static std::map obliqueVolumeTextureInfo; -static std::map orthogonalVolumeTextureInfo; /** * Draw an oblique slice with support for outlining labels and thresholded palette data. * @@ -2970,7 +2927,7 @@ BrainOpenGLVolumeTextureSliceDrawing::drawObliqueSliceWithOutlines(const VolumeS break; } - const int32_t alignVoxelsFlag = 1; + const int32_t alignVoxelsFlag = 0;//1; if (alignVoxelsFlag == 1) { /* * Adjust for when selected slices are not at the origin @@ -3155,9 +3112,8 @@ BrainOpenGLVolumeTextureSliceDrawing::drawObliqueSliceWithOutlines(const VolumeS bool firstFlag(true); for (int32_t iVol = 0; iVol < numVolumes; iVol++) { const BrainOpenGLFixedPipeline::VolumeDrawInfo& vdi = m_volumeDrawInfo[iVol]; - VolumeMappableInterface* vf = vdi.volumeFile; -// VolumeFile* vf = dynamic_cast(vdi.volumeFile); - if (vf != NULL) { + VolumeMappableInterface* volumeInterface = vdi.volumeFile; + if (volumeInterface != NULL) { if (debugFlag) { //std::cout << "Vol: " << iVol << ": " << vf->getFileNameNoPath() << std::endl; } @@ -3176,36 +3132,27 @@ BrainOpenGLVolumeTextureSliceDrawing::drawObliqueSliceWithOutlines(const VolumeS std::array maxStr = { 1.0, 1.0, 1.0 }; GLuint textureID = 0; - switch (sliceProjectionType) { - case caret::VolumeSliceProjectionTypeEnum::VOLUME_SLICE_PROJECTION_OBLIQUE: - { - auto idIter = obliqueVolumeTextureInfo.find(vf); - if (idIter != obliqueVolumeTextureInfo.end()) { - TextureInfo textureInfo = idIter->second; - textureID = textureInfo.m_textureID; - maxStr = textureInfo.m_maxSTR; - } - + if (m_identificationModeFlag) { + auto idIter = s_identificationTextureInfo.find(volumeInterface); + if (idIter != s_identificationTextureInfo.end()) { + TextureInfo textureInfo = idIter->second; + textureID = textureInfo.m_textureID; + maxStr = textureInfo.m_maxSTR; } - break; - case caret::VolumeSliceProjectionTypeEnum::VOLUME_SLICE_PROJECTION_ORTHOGONAL: - { - auto idIter = orthogonalVolumeTextureInfo.find(vf); - if (idIter != orthogonalVolumeTextureInfo.end()) { - TextureInfo textureInfo = idIter->second; - textureID = textureInfo.m_textureID; - maxStr = textureInfo.m_maxSTR; - } - + } + else { + auto idIter = s_volumeTextureInfo.find(volumeInterface); + if (idIter != s_volumeTextureInfo.end()) { + TextureInfo textureInfo = idIter->second; + textureID = textureInfo.m_textureID; + maxStr = textureInfo.m_maxSTR; } - break; } - + if (textureID == 0) { m_fixedPipelineDrawing->testForOpenGLError("Before creating texture"); - textureID = createTextureName(m_fixedPipelineDrawing, - sliceProjectionType, - vf, + textureID = createTextureName(volumeInterface, + m_identificationModeFlag, m_displayGroup, m_tabIndex, maxStr); @@ -3215,13 +3162,11 @@ BrainOpenGLVolumeTextureSliceDrawing::drawObliqueSliceWithOutlines(const VolumeS textureInfo.m_textureID = textureID; textureInfo.m_maxSTR = maxStr; - switch (sliceProjectionType) { - case caret::VolumeSliceProjectionTypeEnum::VOLUME_SLICE_PROJECTION_OBLIQUE: - obliqueVolumeTextureInfo.insert(std::make_pair(vf, textureInfo)); - break; - case caret::VolumeSliceProjectionTypeEnum::VOLUME_SLICE_PROJECTION_ORTHOGONAL: - orthogonalVolumeTextureInfo.insert(std::make_pair(vf, textureInfo)); - break; + if (m_identificationModeFlag) { + s_identificationTextureInfo.insert(std::make_pair(volumeInterface, textureInfo)); + } + else { + s_volumeTextureInfo.insert(std::make_pair(volumeInterface, textureInfo)); } /* 1.0 is highest priority texture so that texture is resident */ @@ -3237,13 +3182,13 @@ BrainOpenGLVolumeTextureSliceDrawing::drawObliqueSliceWithOutlines(const VolumeS if (textureID > 0) { float textureBottomLeft[3]; - getTextureCoordinates(vf, bottomLeft, maxStr.data(), textureBottomLeft); + getTextureCoordinates(volumeInterface, bottomLeft, maxStr.data(), textureBottomLeft); float textureBottomRight[3]; - getTextureCoordinates(vf, bottomRight, maxStr.data(), textureBottomRight); + getTextureCoordinates(volumeInterface, bottomRight, maxStr.data(), textureBottomRight); float textureTopLeft[3]; - getTextureCoordinates(vf, topLeft, maxStr.data(), textureTopLeft); + getTextureCoordinates(volumeInterface, topLeft, maxStr.data(), textureTopLeft); float textureTopRight[3]; - getTextureCoordinates(vf, topRight, maxStr.data(), textureTopRight); + getTextureCoordinates(volumeInterface, topRight, maxStr.data(), textureTopRight); if (debugFlag) { std::cout << "Bottom Left RST: " << AString::fromNumbers(textureBottomLeft, 3, ", ") << std::endl; @@ -3254,12 +3199,24 @@ BrainOpenGLVolumeTextureSliceDrawing::drawObliqueSliceWithOutlines(const VolumeS } glDisable(GL_CULL_FACE); - glDisable(GL_DEPTH_TEST); + if (m_modelWholeBrain == NULL) { + glDisable(GL_DEPTH_TEST); + } m_fixedPipelineDrawing->testForOpenGLError("Before drawing with texture"); glEnable(GL_TEXTURE_3D); glBindTexture(GL_TEXTURE_3D, textureID); glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); + CaretMappableDataFile* mapFile = dynamic_cast(volumeInterface); + CaretAssert(mapFile); + + /* + * Setup pixel to texel filtering + */ + setupTextureFiltering(mapFile, sliceProjectionType); + + std::cout << "Depth Test On: " << AString::fromBool(glIsEnabled(GL_DEPTH_TEST)) << std::endl; + glBegin(GL_QUADS); glColor4f(0.0, 0.0, 1.0, 0.0); glTexCoord3fv(textureBottomLeft); @@ -3275,1230 +3232,236 @@ BrainOpenGLVolumeTextureSliceDrawing::drawObliqueSliceWithOutlines(const VolumeS glBindTexture(GL_TEXTURE_3D, 0); glDisable(GL_TEXTURE_3D); m_fixedPipelineDrawing->testForOpenGLError("After drawing with texture"); + + + if (m_identificationModeFlag) { + processTextureVoxelIdentification(volumeInterface); + + /* + * Exit loop so that only underlay volume is used for identification + */ + iVol = numVolumes; + } } } } glPopAttrib(); - - - - - -// float leftToRightStepXYZ[3] = { -// bottomRight[0] - bottomLeft[0], -// bottomRight[1] - bottomLeft[1], -// bottomRight[2] - bottomLeft[2] -// }; -// const float bottomLeftToBottomRightDistance = MathFunctions::normalizeVector(leftToRightStepXYZ); -// leftToRightStepXYZ[0] *= voxelSize; -// leftToRightStepXYZ[1] *= voxelSize; -// leftToRightStepXYZ[2] *= voxelSize; -// -// const int32_t numberOfRows = std::round(bottomLeftToTopLeftDistance / voxelSize); -// const int32_t numberOfColumns = std::round(bottomLeftToBottomRightDistance / voxelSize); -// -// -// float bottomToTopStepXYZ[3] = { -// topLeft[0] - bottomLeft[0], -// topLeft[1] - bottomLeft[1], -// topLeft[2] - bottomLeft[2] -// }; -// MathFunctions::normalizeVector(bottomToTopStepXYZ); -// bottomToTopStepXYZ[0] *= voxelSize; -// bottomToTopStepXYZ[1] *= voxelSize; -// bottomToTopStepXYZ[2] *= voxelSize; -// -// const int32_t browserTabIndex = m_browserTabContent->getTabNumber(); -// const DisplayPropertiesLabels* displayPropertiesLabels = m_brain->getDisplayPropertiesLabels(); -// const DisplayGroupEnum::Enum displayGroup = displayPropertiesLabels->getDisplayGroupForTab(browserTabIndex); -// -// bool haveAlphaBlendingFlag(false); -// std::vector slices; -// for (int32_t iVol = 0; iVol < numVolumes; iVol++) { -// const BrainOpenGLFixedPipeline::VolumeDrawInfo& vdi = m_volumeDrawInfo[iVol]; -// VolumeMappableInterface* volInter = vdi.volumeFile; -// -// bool volumeEditDrawAllVoxelsFlag = false; -// if (voxelEditingVolumeFile != NULL) { -// if (voxelEditingVolumeFile == m_volumeDrawInfo[iVol].volumeFile) { -// volumeEditDrawAllVoxelsFlag = true; -// } -// } -// -// const bool bottomLayerFlag(iVol == 0); -// ObliqueSlice* slice = new ObliqueSlice(m_fixedPipelineDrawing, -// volInter, -// m_volumeDrawInfo[iVol].opacity, -// m_volumeDrawInfo[iVol].mapIndex, -// numberOfRows, -// numberOfColumns, -// browserTabIndex, -// displayPropertiesLabels, -// displayGroup, -// bottomLeft, -// leftToRightStepXYZ, -// bottomToTopStepXYZ, -// m_obliqueSliceMaskingType, -// voxelEditingValue, -// volumeEditDrawAllVoxelsFlag, -// m_identificationModeFlag, -// bottomLayerFlag); -// slices.push_back(slice); -// -// if (m_volumeDrawInfo[iVol].opacity < 1.0) { -// haveAlphaBlendingFlag = true; -// } -// } -// -// const int32_t numSlices = static_cast(slices.size()); -// if (numSlices > 0) { -// bool drawEachSliceFlag = true; -// bool compositeFlag = true; -// if (haveAlphaBlendingFlag -// || m_identificationModeFlag) { -// /* -// * Do not composite slices if blending is used -// * or if in identification mode -// */ -// compositeFlag = false; -// } -// if (compositeFlag) { -// CaretAssertVectorIndex(slices, 0); -// ObliqueSlice* underlaySlice = slices[0]; -// if (numSlices > 1) { -// std::vector overlaySlices; -// for (int32_t i = 1; i < numSlices; i++) { -// CaretAssertVectorIndex(slices, i); -// overlaySlices.push_back(slices[i]); -// } -// if (underlaySlice->compositeSlicesRGBA(overlaySlices)) { -// underlaySlice->draw(m_fixedPipelineDrawing); -// drawEachSliceFlag = false; -// } -// } -// } -// -// if (drawEachSliceFlag) { -// for (auto s : slices) { -// s->draw(m_fixedPipelineDrawing); -// } -// } -// } -// -// for (auto s : slices) { -// delete s; -// } -// slices.clear(); - } -} - -/** - * Constructor for drawing an oblique slice. - * - * @param fixedPipelineDrawing - * The fixed pipeline drawing. - * @param volumeInterface - * Interface for volume-type file being drawn. - * @param opacity - * Opacity for drawing the slice. - * @param mapIndex - * Index of map in file being drawn. - * @param numberOfRows - * Number of rows used when drawing slice. - * @param numberOfColumns - * Number of columns used when drawing slice. - * @param browserTabIndex - * Index of browser tab being drawn. - * @param displayPropertiesLabels - * Display properties for labels. - * @param displayGroup - * Selected display group for labels. - * @param originXYZ - * XYZ coordinate of first voxel in slice. - * @param leftToRightStepXYZ - * XYZ step for one voxel in screen left to right. - * @param bottomToTopStepXYZ - * XYZ step for one voxel in screen bottom to top. - * @param maskingType - * Masking type to eliminate interpolation artifacts - * @param voxelEditingValue - * Value used when editing voxels. - * @param volumeEditingDrawAllVoxelsFlag - * Draw all voxels when editing a volume. - * @param identificationModeFlag - * True if identification mode is active. - * @param bottomLayerFlag - * True if bottom layer. - */ -BrainOpenGLVolumeTextureSliceDrawing::ObliqueSlice::ObliqueSlice(BrainOpenGLFixedPipeline* fixedPipelineDrawing, - VolumeMappableInterface* volumeInterface, - const float opacity, - const int32_t mapIndex, - const int32_t numberOfRows, - const int32_t numberOfColumns, - const int32_t browserTabIndex, - const DisplayPropertiesLabels* displayPropertiesLabels, - const DisplayGroupEnum::Enum displayGroup, - const float originXYZ[3], - const float leftToRightStepXYZ[3], - const float bottomToTopStepXYZ[3], - const VolumeSliceInterpolationEdgeEffectsMaskingEnum::Enum maskingType, - const float voxelEditingValue, - const bool volumeEditingDrawAllVoxelsFlag, - const bool identificationModeFlag, - const bool bottomLayerFlag) -: -m_volumeInterface(volumeInterface), -m_opacity(opacity), -m_mapFile(dynamic_cast(volumeInterface)), -m_mapIndex(mapIndex), -m_numberOfRows(numberOfRows), -m_numberOfColumns(numberOfColumns), -m_browserTabIndex(browserTabIndex), -m_displayPropertiesLabels(displayPropertiesLabels), -m_displayGroup(displayGroup), -m_identificationX(fixedPipelineDrawing->mouseX), -m_identificationY(fixedPipelineDrawing->mouseY), -m_identificationModeFlag(identificationModeFlag), -m_bottomLayerFlag(bottomLayerFlag) -{ - CaretAssert(volumeInterface); - CaretAssert(m_mapFile); - - m_opacityByte = 255; - if (m_opacity >= 1.0) { - m_opacityByte = 255; - } - if (m_opacity <= 0.0) { - m_opacityByte = 0; - } - else { - m_opacityByte = static_cast(m_opacity * 255.0f); - } - - for (int32_t i = 0; i < 3; i++) { - m_selectionIJK[i] = -1; - m_leftToRightStepXYZ[i] = leftToRightStepXYZ[i]; - m_bottomToTopStepXYZ[i] = bottomToTopStepXYZ[i]; - m_originXYZ[i] = originXYZ[i]; - } - - const VolumeFile* volumeFileConst = dynamic_cast(volumeInterface); - m_volumeFile = const_cast(volumeFileConst); - const CiftiMappableDataFile* ciftiMappableFileConst = dynamic_cast(volumeInterface); - m_ciftiMappableFile = const_cast(ciftiMappableFileConst); - - m_dataValueType = DataValueType::INVALID; - if (m_mapFile->isMappedWithPalette()) { - if (m_volumeFile != NULL) { - m_dataValueType = DataValueType::VOLUME_PALETTE; - } - else if (m_ciftiMappableFile != NULL) { - m_dataValueType = DataValueType::CIFTI_PALETTE; - } - else { - CaretAssert(0); - } - } - else if (m_mapFile->isMappedWithLabelTable()) { - if (m_volumeFile != NULL) { - m_dataValueType = DataValueType::VOLUME_LABEL; - } - else if (m_ciftiMappableFile != NULL) { - m_dataValueType = DataValueType::CIFTI_LABEL; - } - else { - CaretAssert(0); - } - } - else if (m_mapFile->isMappedWithRGBA()) { - if (m_volumeFile != NULL) { - if (m_volumeFile->getNumberOfComponents() == 4) { - m_dataValueType = DataValueType::VOLUME_RGBA; - } - else if (m_volumeFile->getNumberOfComponents() == 3) { - m_dataValueType = DataValueType::VOLUME_RGB; - } - } - else { - CaretAssert(0); - } - } - CaretAssert(m_dataValueType != DataValueType::INVALID); - - m_sliceNumberOfVoxels = m_numberOfRows * m_numberOfColumns; - m_voxelNumberOfComponents = 1; - switch (m_dataValueType) { - case DataValueType::INVALID: - CaretAssert(0); - break; - case DataValueType::CIFTI_LABEL: - case DataValueType::CIFTI_PALETTE: - case DataValueType::VOLUME_LABEL: - case DataValueType::VOLUME_PALETTE: - break; - case DataValueType::VOLUME_RGB: - case DataValueType::VOLUME_RGBA: - m_voxelNumberOfComponents = 4; - break; - } - m_data.reserve(m_sliceNumberOfVoxels - * m_voxelNumberOfComponents); - - m_thresholdData.reserve(m_data.size()); - - m_rgba.reserve(m_sliceNumberOfVoxels * 4); - - if (m_identificationModeFlag) { - m_identificationIJK.reserve(m_sliceNumberOfVoxels * 3); - m_identificationHelper.reset(new IdentificationWithColor()); - } - - setThresholdFileAndMap(); - - loadData(maskingType, - voxelEditingValue, - volumeEditingDrawAllVoxelsFlag); - - assignRgba(volumeEditingDrawAllVoxelsFlag); - - if ( ! m_identificationModeFlag) { - addOutlines(); } - - CaretAssert((m_sliceNumberOfVoxels*4) == static_cast(m_rgba.size())); } -/** - * Set thresholding file and map, if applicable. - */ -bool -BrainOpenGLVolumeTextureSliceDrawing::ObliqueSlice::setThresholdFileAndMap() -{ - switch (m_dataValueType) { - case DataValueType::INVALID: - CaretAssert(0); - break; - case DataValueType::CIFTI_LABEL: - break; - case DataValueType::CIFTI_PALETTE: - { - const PaletteColorMapping* pcm = m_ciftiMappableFile->getMapPaletteColorMapping(m_mapIndex); - CaretAssert(pcm); - switch (pcm->getThresholdType()) { - case PaletteThresholdTypeEnum::THRESHOLD_TYPE_OFF: - break; - case PaletteThresholdTypeEnum::THRESHOLD_TYPE_NORMAL: - m_thresholdCiftiMappableFile = m_ciftiMappableFile; - m_thresholdMapIndex = m_mapIndex; - break; - case PaletteThresholdTypeEnum::THRESHOLD_TYPE_FILE: - { - CaretMappableDataFileAndMapSelectionModel* selector = m_ciftiMappableFile->getMapThresholdFileSelectionModel(m_mapIndex); - m_thresholdCiftiMappableFile = dynamic_cast(selector->getSelectedFile()); - if (m_thresholdCiftiMappableFile != NULL) { - m_thresholdMapIndex = selector->getSelectedMapIndex(); - } - } - break; - case PaletteThresholdTypeEnum::THRESHOLD_TYPE_MAPPED: - case PaletteThresholdTypeEnum::THRESHOLD_TYPE_MAPPED_AVERAGE_AREA: - CaretAssert(0); - break; - } - if (m_thresholdCiftiMappableFile != NULL) { - if ((m_thresholdMapIndex >= 0) - && (m_thresholdMapIndex < m_thresholdCiftiMappableFile->getNumberOfMaps())) { - m_thresholdPaletteColorMapping = m_thresholdCiftiMappableFile->getMapPaletteColorMapping(m_thresholdMapIndex); - } - else { - m_thresholdCiftiMappableFile = NULL; - m_thresholdMapIndex = -1; - } - } - } - break; - case DataValueType::VOLUME_LABEL: - break; - case DataValueType::VOLUME_PALETTE: - { - const PaletteColorMapping* pcm = m_volumeFile->getMapPaletteColorMapping(m_mapIndex); - CaretAssert(pcm); - switch (pcm->getThresholdType()) { - case PaletteThresholdTypeEnum::THRESHOLD_TYPE_OFF: - break; - case PaletteThresholdTypeEnum::THRESHOLD_TYPE_NORMAL: - m_thresholdVolumeFile = m_volumeFile; - m_thresholdMapIndex = m_mapIndex; - break; - case PaletteThresholdTypeEnum::THRESHOLD_TYPE_FILE: - { - CaretMappableDataFileAndMapSelectionModel* selector = m_volumeFile->getMapThresholdFileSelectionModel(m_mapIndex); - m_thresholdVolumeFile = dynamic_cast(selector->getSelectedFile()); - if (m_thresholdVolumeFile != NULL) { - m_thresholdMapIndex = selector->getSelectedMapIndex(); - } - } - break; - case PaletteThresholdTypeEnum::THRESHOLD_TYPE_MAPPED: - case PaletteThresholdTypeEnum::THRESHOLD_TYPE_MAPPED_AVERAGE_AREA: - CaretAssert(0); - break; - } - if (m_thresholdVolumeFile != NULL) { - if ((m_thresholdMapIndex >= 0) - && (m_thresholdMapIndex < m_thresholdVolumeFile->getNumberOfMaps())) { - m_thresholdPaletteColorMapping = m_thresholdVolumeFile->getMapPaletteColorMapping(m_thresholdMapIndex); - } - else { - m_thresholdVolumeFile = NULL; - m_thresholdMapIndex = -1; - } - } - } - break; - case DataValueType::VOLUME_RGB: - break; - case DataValueType::VOLUME_RGBA: - break; - } - - if (m_thresholdMapIndex < 0) { - m_thresholdCiftiMappableFile = NULL; - m_thresholdVolumeFile = NULL; - m_thresholdPaletteColorMapping = NULL; - } - - return (m_thresholdMapIndex >= 0); -} - -/** - * Assign RGBA coloring for the voxels. - * - * @param volumeEditingDrawAllVoxelsFlag - * True if editing and all voxels should be drawn for selection. - */ void -BrainOpenGLVolumeTextureSliceDrawing::ObliqueSlice::assignRgba(const bool volumeEditingDrawAllVoxelsFlag) +BrainOpenGLVolumeTextureSliceDrawing::processTextureVoxelIdentification(VolumeMappableInterface* volumeMappableInterface) { - if (m_data.empty()) { - return; - } + /* + * Saves glPixelStore parameters + */ + glPushClientAttrib(GL_CLIENT_PIXEL_STORE_BIT); - switch (m_dataValueType) { - case DataValueType::INVALID: - CaretAssert(0); - break; - case DataValueType::CIFTI_LABEL: - case DataValueType::CIFTI_PALETTE: - case DataValueType::VOLUME_LABEL: - case DataValueType::VOLUME_PALETTE: - /* - * One data value per voxel. - */ - m_rgba.resize(m_data.size() * 4); - break; - case DataValueType::VOLUME_RGB: - case DataValueType::VOLUME_RGBA: - /* - * For RGBA, there are four data values per voxel - */ - m_rgba.resize(m_data.size()); - break; - } - - if (volumeEditingDrawAllVoxelsFlag) { - const int32_t numVoxels = static_cast(m_rgba.size() / 4); - for (int32_t i = 0; i < numVoxels; i++) { - const int32_t i4 = i * 4; - m_rgba[i4] = 255; - m_rgba[i4+1] = 255; - m_rgba[i4+2] = 255; - m_rgba[i4+3] = 255; - } + /* + * Determine item picked by examination of color in back buffer + * + * QOpenGLWidget Note: The QOpenGLWidget always renders in a + * frame buffer object (see its documentation). This is + * probably why calls to glReadBuffer() always cause an + * OpenGL error. + */ +#ifdef WORKBENCH_USE_QT5_QOPENGL_WIDGET + /* do not call glReadBuffer() */ +#else + glReadBuffer(GL_BACK); +#endif + glPixelStorei(GL_PACK_SKIP_ROWS, 0); + glPixelStorei(GL_PACK_SKIP_PIXELS, 0); + glPixelStorei(GL_PACK_ALIGNMENT, 1); + uint8_t pixels[4]; + glReadPixels((int)m_fixedPipelineDrawing->mouseX, + (int)m_fixedPipelineDrawing->mouseY, + 1, + 1, + GL_RGBA, + GL_UNSIGNED_BYTE, + pixels); + + CaretLogFine("ID color RGBA " + + QString::number(pixels[0]) + ", " + + QString::number(pixels[1]) + ", " + + QString::number(pixels[2]) + ", " + + QString::number(pixels[3])); + + std::cout << "Pixel ID: " << AString::fromNumbers(pixels, 4, ",") << std::endl; + + uint32_t alphaInt(pixels[3]); + if (alphaInt == 255) { + uint32_t redInt(pixels[0]); + uint32_t greenInt(pixels[1]); + uint32_t blueInt(pixels[2]); + uint32_t offset = ((redInt << 16) + + (greenInt << 8) + + (blueInt)); + std::cout << " Offset: " << offset << std::endl; + + std::vector dims; + volumeMappableInterface->getDimensions(dims); + const int64_t sliceSize = dims[0] * dims[1]; + const int64_t sliceK = offset / sliceSize; + const int64_t sliceOffset = offset % sliceSize; + const int64_t sliceJ = sliceOffset / dims[0]; + const int64_t sliceI = sliceOffset % dims[0]; + std::cout << " Voxel IJK: " << sliceI << ", " << sliceJ << ", " << sliceK << std::endl; + + const int64_t voxelIndices[3] { + sliceI, + sliceJ, + sliceK + }; - return; - } - - switch (m_dataValueType) { - case DataValueType::INVALID: - break; - case DataValueType::CIFTI_LABEL: - { - CaretAssert(m_ciftiMappableFile); - const GiftiLabelTable* labelTable = m_ciftiMappableFile->getMapLabelTable(m_mapIndex); - CaretAssert(labelTable); - NodeAndVoxelColoring::colorIndicesWithLabelTableForDisplayGroupTab(labelTable, - &m_data[0], - m_data.size(), - m_displayGroup, - m_browserTabIndex, - &m_rgba[0]); - } - break; - case DataValueType::CIFTI_PALETTE: - { - CaretAssert(m_ciftiMappableFile); - PaletteColorMapping* pcm = m_ciftiMappableFile->getMapPaletteColorMapping(m_mapIndex); - CaretAssert(pcm); - const FastStatistics* stats = m_ciftiMappableFile->getMapFastStatistics(m_mapIndex); - CaretAssert(stats); - CaretAssert(m_data.size() == m_thresholdData.size()); - - PaletteColorMapping* threshPcm = ((m_thresholdPaletteColorMapping != NULL) - ? m_thresholdPaletteColorMapping - : pcm); - NodeAndVoxelColoring::colorScalarsWithPalette(stats, - pcm, - &m_data[0], - threshPcm, - &m_thresholdData[0], - m_data.size(), - &m_rgba[0]); - } - break; - case DataValueType::VOLUME_LABEL: - { - CaretAssert(m_volumeFile); - const GiftiLabelTable* labelTable = m_volumeFile->getMapLabelTable(m_mapIndex); - CaretAssert(labelTable); - NodeAndVoxelColoring::colorIndicesWithLabelTableForDisplayGroupTab(labelTable, - &m_data[0], - m_data.size(), - m_displayGroup, - m_browserTabIndex, - &m_rgba[0]); - } - break; - case DataValueType::VOLUME_PALETTE: - { - CaretAssert(m_volumeFile); - PaletteColorMapping* pcm = m_volumeFile->getMapPaletteColorMapping(m_mapIndex); - CaretAssert(pcm); - const FastStatistics* stats = m_volumeFile->getMapFastStatistics(m_mapIndex); - CaretAssert(stats); - CaretAssert(m_data.size() == m_thresholdData.size()); - - PaletteColorMapping* threshPcm = ((m_thresholdPaletteColorMapping != NULL) - ? m_thresholdPaletteColorMapping - : pcm); - NodeAndVoxelColoring::colorScalarsWithPalette(stats, - pcm, - &m_data[0], - threshPcm, - &m_thresholdData[0], - m_data.size(), - &m_rgba[0]); - } - break; - case DataValueType::VOLUME_RGB: - case DataValueType::VOLUME_RGBA: - { - /* - * Test RGB data to see if it ranges from [0, 1] or [0, 255]. - * Assume if any component is greater than 1, then the data is [0, 255] - */ - bool range255Flag = std::any_of(m_data.begin(), - m_data.end(), - [](float f) { return f > 1.0f; }); - - CaretAssert(m_data.size() == m_rgba.size()); - const int32_t numVoxels = static_cast(m_data.size() / 4); - if (range255Flag) { - for (int32_t i = 0; i < numVoxels; i++) { - const int32_t i4 = i * 4; - m_rgba[i4] = static_cast(m_data[i4]); - m_rgba[i4+1] = static_cast(m_data[i4+1]); - m_rgba[i4+2] = static_cast(m_data[i4+2]); - if (m_dataValueType == DataValueType::VOLUME_RGB) { - m_rgba[i4+3] = ((m_data[i4+3] > 0) - ? 255 - : 0); - } - } - } - else { - for (int32_t i = 0; i < numVoxels; i++) { - const int32_t i4 = i * 4; - m_rgba[i4] = static_cast(m_data[i4] * 255.0); - m_rgba[i4+1] = static_cast(m_data[i4+1] * 255.0); - m_rgba[i4+2] = static_cast(m_data[i4+2] * 255.0); - m_rgba[i4+3] = static_cast(m_data[i4+3] * 255.0); - } - } - } - break; - } -} - -/** - * Add any outlines for labels or thresholded palette data. - */ -void -BrainOpenGLVolumeTextureSliceDrawing::ObliqueSlice::addOutlines() -{ - LabelDrawingProperties* labelDrawingProperties = NULL; - PaletteColorMapping* paletteColorMapping = NULL; - switch (m_dataValueType) { - case DataValueType::INVALID: - break; - case DataValueType::CIFTI_LABEL: - CaretAssert(m_ciftiMappableFile); - labelDrawingProperties = m_ciftiMappableFile->getLabelDrawingProperties(); - break; - case DataValueType::CIFTI_PALETTE: - CaretAssert(m_ciftiMappableFile); - paletteColorMapping = m_ciftiMappableFile->getMapPaletteColorMapping(m_mapIndex); - break; - case DataValueType::VOLUME_LABEL: - CaretAssert(m_volumeFile); - labelDrawingProperties = m_volumeFile->getLabelDrawingProperties(); - break; - case DataValueType::VOLUME_PALETTE: - CaretAssert(m_volumeFile); - paletteColorMapping = m_volumeFile->getMapPaletteColorMapping(m_mapIndex); - break; - case DataValueType::VOLUME_RGB: - break; - case DataValueType::VOLUME_RGBA: - break; - } - - if (labelDrawingProperties != NULL) { - const LabelDrawingTypeEnum::Enum labelDrawingType = labelDrawingProperties->getDrawingType(); - if (labelDrawingType != LabelDrawingTypeEnum::DRAW_FILLED) { - const CaretColorEnum::Enum outlineColor = labelDrawingProperties->getOutlineColor(); - NodeAndVoxelColoring::convertSliceColoringToOutlineMode(&m_rgba[0], - labelDrawingType, - outlineColor, - m_numberOfColumns, - m_numberOfRows); - } - } - - if (paletteColorMapping != NULL) { - const PaletteThresholdOutlineDrawingModeEnum::Enum outlineMode = paletteColorMapping->getThresholdOutlineDrawingMode(); - if (outlineMode != PaletteThresholdOutlineDrawingModeEnum::OFF) { - const CaretColorEnum::Enum outlineColor = paletteColorMapping->getThresholdOutlineDrawingColor(); - NodeAndVoxelColoring::convertPaletteSliceColoringToOutlineMode(&m_rgba[0], - outlineMode, - outlineColor, - m_numberOfColumns, - m_numberOfRows); - } - } -} - -/** - * Loads voxels data into the 'slice'. - * - * @param maskingType - * Masking type to hide artifacts of interpolation - * @param voxelEditingValue - * Value for voxels when editing a volume - * @param volumeEditingDrawAllVoxelsFlag - * True if editing and all voxels should be drawn for selection. - */ -void -BrainOpenGLVolumeTextureSliceDrawing::ObliqueSlice::loadData(const VolumeSliceInterpolationEdgeEffectsMaskingEnum::Enum maskingType, - const float voxelEditingValue, - const bool volumeEditingDrawAllVoxelsFlag) -{ - m_validVoxelCount = 0; - - bool needCiftiMapDataFlag = false; - switch (m_dataValueType) { - case DataValueType::INVALID: - break; - case DataValueType::CIFTI_LABEL: - case DataValueType::CIFTI_PALETTE: - needCiftiMapDataFlag = true; - break; - case DataValueType::VOLUME_LABEL: - case DataValueType::VOLUME_PALETTE: - case DataValueType::VOLUME_RGB: - case DataValueType::VOLUME_RGBA: - break; - } - - float voxelCenterOffset[3] = { - ((m_leftToRightStepXYZ[0] / 2.0f) + (m_bottomToTopStepXYZ[0] / 2.0f)), - ((m_leftToRightStepXYZ[1] / 2.0f) + (m_bottomToTopStepXYZ[1] / 2.0f)), - ((m_leftToRightStepXYZ[2] / 2.0f) + (m_bottomToTopStepXYZ[2] / 2.0f)), - }; - - std::vector ciftiFileMapData; - std::vector thresholdCiftiFileMapData; - if (needCiftiMapDataFlag) { /* - * Cifti files are slow at getting individual voxels since they - * provide no access to individual voxels. The reason is that - * the data may be on a server (Dense data) and accessing a single - * voxel would require requesting the entire map. So, for - * each Cifti file, get the entire map. + * Get depth from depth buffer */ - CaretAssert(m_ciftiMappableFile); - m_ciftiMappableFile->getMapData(m_mapIndex, - ciftiFileMapData); + glPixelStorei(GL_PACK_ALIGNMENT, 4); + float depth(0.0); + glReadPixels(m_fixedPipelineDrawing->mouseX, + m_fixedPipelineDrawing->mouseY, + 1, + 1, + GL_DEPTH_COMPONENT, + GL_FLOAT, + &depth); - if (m_thresholdCiftiMappableFile != NULL) { - m_thresholdCiftiMappableFile->getMapData(m_thresholdMapIndex, - thresholdCiftiFileMapData); + SelectionItemVoxel* voxelID = m_brain->getSelectionManager()->getVoxelIdentification(); + if (voxelID->isEnabledForSelection()) { + if (voxelID->isOtherScreenDepthCloserToViewer(depth)) { + voxelID->setVoxelIdentification(m_brain, + volumeMappableInterface, + voxelIndices, + depth); + + float voxelCoordinates[3]; + volumeMappableInterface->indexToSpace(voxelIndices[0], voxelIndices[1], voxelIndices[2], + voxelCoordinates[0], voxelCoordinates[1], voxelCoordinates[2]); + + m_fixedPipelineDrawing->setSelectedItemScreenXYZ(voxelID, + voxelCoordinates); + CaretLogFinest("Selected Voxel (3D): " + AString::fromNumbers(voxelIndices, 3, ",")); + } } - } - - for (int32_t iRow = 0; iRow < m_numberOfRows; iRow++) { - float rowStartXYZ[3] = { - m_originXYZ[0] + (iRow * m_bottomToTopStepXYZ[0]), - m_originXYZ[1] + (iRow * m_bottomToTopStepXYZ[1]), - m_originXYZ[2] + (iRow * m_bottomToTopStepXYZ[2]) - }; - for (int32_t iCol = 0; iCol < m_numberOfColumns; iCol++) { - - const float voxelBottomLeft[3] = { - rowStartXYZ[0] + (iCol * m_leftToRightStepXYZ[0]), - rowStartXYZ[1] + (iCol * m_leftToRightStepXYZ[1]), - rowStartXYZ[2] + (iCol * m_leftToRightStepXYZ[2]) - }; - - const float voxelCenter[3] = { - voxelBottomLeft[0] + voxelCenterOffset[0], - voxelBottomLeft[1] + voxelCenterOffset[1], - voxelBottomLeft[2] + voxelCenterOffset[2] - }; - - float values[4] = { 0.0, 0.0, 0.0, 0.0 }; - bool valueValidFlag = false; - - float thresholdValue = 0.0; - bool thresholdValueValidFlag = false; - - switch (m_dataValueType) { - case DataValueType::INVALID: - CaretAssert(0); - break; - case DataValueType::CIFTI_LABEL: - case DataValueType::CIFTI_PALETTE: - { - CaretAssert(m_ciftiMappableFile); - const int64_t voxelOffset = m_ciftiMappableFile->getMapDataOffsetForVoxelAtCoordinate(voxelCenter, - m_mapIndex); - if (voxelOffset >= 0) { - CaretAssertVectorIndex(ciftiFileMapData, voxelOffset); - values[0] = ciftiFileMapData[voxelOffset]; - valueValidFlag = true; - } - - if (m_thresholdCiftiMappableFile != NULL) { - const int64_t thresholdVoxelOffset = m_thresholdCiftiMappableFile->getMapDataOffsetForVoxelAtCoordinate(voxelCenter, - m_thresholdMapIndex); - if (thresholdVoxelOffset >= 0) { - CaretAssertVectorIndex(thresholdCiftiFileMapData, thresholdVoxelOffset); - thresholdValue = thresholdCiftiFileMapData[thresholdVoxelOffset]; - thresholdValueValidFlag = true; - } - } - } - break; - case DataValueType::VOLUME_LABEL: - { - CaretAssert(m_volumeFile); - values[0] = m_volumeFile->getVoxelValue(voxelCenter, - &valueValidFlag, - m_mapIndex, - 0); - } - break; - case DataValueType::VOLUME_PALETTE: - { - CaretAssert(m_volumeFile); - values[0] = m_volumeFile->interpolateValue(voxelCenter, - VolumeFile::CUBIC, - &valueValidFlag, - m_mapIndex); + SelectionItemVoxelEditing* voxelEditID = m_brain->getSelectionManager()->getVoxelEditingIdentification(); + if (voxelEditID->isEnabledForSelection()) { + if (voxelEditID->getVolumeFileForEditing() == volumeMappableInterface) { + if (voxelEditID->isOtherScreenDepthCloserToViewer(depth)) { + voxelEditID->setVoxelIdentification(m_brain, + volumeMappableInterface, + voxelIndices, + depth); + const float floatDiffXYZ[3] = { 1.0, 1.0, 1.0 }; + voxelEditID->setVoxelDiffXYZ(floatDiffXYZ); - if (valueValidFlag - && (values[0] != 0.0f)) { - /* - * Apply masking to oblique voxels (WB-750). - * In some instances, CUBIC interpolation may result in a voxel - * receiving a very small value (0.000000000000000210882405) and - * this will cause the slice drawing to look very unusual. Masking - * is used to prevent this from occurring. - * - */ - bool maskValidFlag = false; - float maskValue = 0.0f; - switch (maskingType) { - case VolumeSliceInterpolationEdgeEffectsMaskingEnum::OFF: - maskValidFlag = false; - break; - case VolumeSliceInterpolationEdgeEffectsMaskingEnum::LOOSE: - maskValue = m_volumeFile->interpolateValue(voxelCenter, - VolumeFile::TRILINEAR, - &maskValidFlag, - m_mapIndex); - break; - case VolumeSliceInterpolationEdgeEffectsMaskingEnum::TIGHT: - maskValue = m_volumeFile->interpolateValue(voxelCenter, - VolumeFile::ENCLOSING_VOXEL, - &maskValidFlag, - m_mapIndex); - break; - } - - if (maskValidFlag - && (maskValue == 0.0f)) { - values[0] = 0.0f; - valueValidFlag = false; - } - } + float voxelCoordinates[3]; + volumeMappableInterface->indexToSpace(voxelIndices[0], voxelIndices[1], voxelIndices[2], + voxelCoordinates[0], voxelCoordinates[1], voxelCoordinates[2]); - if (m_thresholdVolumeFile != NULL) { - thresholdValue = m_thresholdVolumeFile->getVoxelValue(voxelCenter, - &thresholdValueValidFlag, - m_thresholdMapIndex, - 0); - } - } - break; - case DataValueType::VOLUME_RGB: - case DataValueType::VOLUME_RGBA: - { - values[0] = m_volumeFile->getVoxelValue(voxelCenter, - &valueValidFlag, - m_mapIndex, - 0); - if (valueValidFlag) { - values[1] = m_volumeFile->getVoxelValue(voxelCenter, - &valueValidFlag, - m_mapIndex, - 1); - values[2] = m_volumeFile->getVoxelValue(voxelCenter, - &valueValidFlag, - m_mapIndex, - 2); - if (m_dataValueType == DataValueType::VOLUME_RGBA) { - values[3] = m_volumeFile->getVoxelValue(voxelCenter, - &valueValidFlag, - m_mapIndex, - 3); - } - else { - values[3] = 1.0; - } - } - } - break; - } - - if (m_thresholdMapIndex < 0) { - thresholdValue = values[0]; - } - - /* - * Need to draw all voxels when editing - */ - if (volumeEditingDrawAllVoxelsFlag) { - if (! valueValidFlag) { - values[0] = voxelEditingValue; - valueValidFlag = true; - } - } - - switch (m_dataValueType) { - case DataValueType::INVALID: - break; - case DataValueType::CIFTI_LABEL: - case DataValueType::CIFTI_PALETTE: - case DataValueType::VOLUME_LABEL: - case DataValueType::VOLUME_PALETTE: - m_data.push_back(values[0]); - m_thresholdData.push_back(thresholdValue); - break; - case DataValueType::VOLUME_RGB: - case DataValueType::VOLUME_RGBA: - m_data.push_back(values[0]); - m_data.push_back(values[1]); - m_data.push_back(values[2]); - m_data.push_back(values[3]); - break; - } - - if (m_identificationModeFlag) { - /* - * When identifying, need an IJK triplet - * for each voxel - */ - int64_t ijk[3] = { -1, -1, -1 }; - if (valueValidFlag) { - if (m_volumeFile != NULL) { - m_volumeFile->enclosingVoxel(voxelCenter, ijk); - if ( ! m_volumeFile->indexValid(ijk)) { - ijk[0] = -1; - ijk[1] = -1; - ijk[2] = -1; - valueValidFlag = false; - } - } - else if (m_ciftiMappableFile != NULL) { - int64_t i(-1), j(-1), k(-1); - m_ciftiMappableFile->enclosingVoxel(voxelCenter[0], voxelCenter[1], voxelCenter[2], - i, j, k); - if (m_ciftiMappableFile->indexValid(i, j, k)) { - ijk[0] = i; - ijk[1] = j; - ijk[2] = k; - } - else { - valueValidFlag = false; - } - } + m_fixedPipelineDrawing->setSelectedItemScreenXYZ(voxelEditID, + voxelCoordinates); + CaretLogFinest("Selected Voxel Editing (3D): Indices (" + + AString::fromNumbers(voxelIndices, 3, ",") + + ") Diff XYZ (" + + AString::fromNumbers(floatDiffXYZ, 3, ",") + + ")"); } - m_identificationIJK.insert(m_identificationIJK.end(), - ijk, ijk + 3); - } - - if (valueValidFlag) { - m_validVoxelCount++; } } } - CaretAssert((m_sliceNumberOfVoxels * m_voxelNumberOfComponents) == static_cast(m_data.size())); - if (m_thresholdMapIndex >= 0) { - CaretAssert(m_data.size() == m_thresholdData.size()); - } - - if (m_identificationModeFlag) { - CaretAssert((m_data.size() * 3) == m_identificationIJK.size()); - } + glPopClientAttrib(); + } + /** - * Draw the oblique slice with the graphics primitives. + * Set the texture filtering that controls mapping of texels to pixels * - * @param fixedPipelineDrawing - * The fixed pipeline drawing. + * @param mapFile + * File is is being drawn + * @param sliceProjectionType + * Type of projection */ void -BrainOpenGLVolumeTextureSliceDrawing::ObliqueSlice::draw(BrainOpenGLFixedPipeline* fixedPipelineDrawing) +BrainOpenGLVolumeTextureSliceDrawing::setupTextureFiltering(const CaretMappableDataFile* mapFile, + const VolumeSliceProjectionTypeEnum::Enum sliceProjectionType) { - Brain* brain = fixedPipelineDrawing->m_brain; - SelectionItemVoxel* selectionItemVoxel = brain->getSelectionManager()->getVoxelIdentification(); - SelectionItemVoxelEditing* selectionItemVoxelEditing = brain->getSelectionManager()->getVoxelEditingIdentification(); - - const GraphicsPrimitive::PrimitiveType primitiveType = GraphicsPrimitive::PrimitiveType::OPENGL_TRIANGLES; - std::unique_ptr primitive(GraphicsPrimitive::newPrimitiveV3fC4ub(primitiveType)); - - float rowXYZ[3] = { - m_originXYZ[0], - m_originXYZ[1], - m_originXYZ[2] - }; - - uint8_t sliceAlpha = 255; - bool drawWithBlendingFlag(false); -// if (m_bottomLayerFlag) { - if (m_opacity < 1.0) { - sliceAlpha = static_cast(m_opacity * 255.0); - drawWithBlendingFlag = true; - } -// } - - std::vector selectionIJK; - for (int32_t iRow = 0; iRow < m_numberOfRows; iRow++) { - float voxelXYZ[3] = { - rowXYZ[0], - rowXYZ[1], - rowXYZ[2] - }; - - const int32_t rowRgbaOffset = (iRow * m_numberOfColumns * 4); - for (int32_t iCol = 0; iCol < m_numberOfColumns; iCol++) { - float nextXYZ[3] = { - voxelXYZ[0] + m_leftToRightStepXYZ[0], - voxelXYZ[1] + m_leftToRightStepXYZ[1], - voxelXYZ[2] + m_leftToRightStepXYZ[2], - }; - - const int32_t rgbaIndex = rowRgbaOffset + (iCol * 4); - CaretAssertVectorIndex(m_rgba, rgbaIndex + 3); - const uint8_t alpha = m_rgba[rgbaIndex +3]; - if (alpha > 0) { - const float upperLeftXYZ[3] = { - voxelXYZ[0] + m_bottomToTopStepXYZ[0], - voxelXYZ[1] + m_bottomToTopStepXYZ[1], - voxelXYZ[2] + m_bottomToTopStepXYZ[2] - }; - const float upperRightXYZ[3] = { - nextXYZ[0] + m_bottomToTopStepXYZ[0], - nextXYZ[1] + m_bottomToTopStepXYZ[1], - nextXYZ[2] + m_bottomToTopStepXYZ[2] - }; - uint8_t* rgba = &m_rgba[rgbaIndex]; - if (drawWithBlendingFlag) { - if ((rgba[0] > 0) - || (rgba[1] > 0) - || (rgba[2] > 0)) { - rgba[3] = sliceAlpha; - } - } - + if (mapFile->isMappedWithPalette()) { + switch (sliceProjectionType) { + case caret::VolumeSliceProjectionTypeEnum::VOLUME_SLICE_PROJECTION_OBLIQUE: + { /* - * Each voxel is drawn with two triangles because: - * (1) Voxels may be sparse and triangles strips do not support 'restart' - * until newer versions of OpenGL. - * (2) Support for Quads is removed in newer versions of OpenGL. + * This combination MIN=Linear, MAG=Nearest + * seems to produce voxel drawing that is + * nearly identical to cubic interpolation when zoomed in so + * that the voxels are large. The difference is when the slices + * are rotated; In cubic interpolation, the "scan lines" are + * horizontal (left to right on the screen) but with texture, + * the "scan lines" follow the volume rotation. + * + * From the OpenGL documentation (man page) + * + * GL_TEXTURE_MIN_FILTER - The texture minifying function is used + * whenever the pixel being textured maps to an area greater than one + * texture element. + * GL_NEAREST - Returns the value of the texture element that is + * nearest (in Manhattan distance) to the center of + * the pixel being textured. + * GL_LINEAR - Returns the weighted average of the four texture + * elements that are closest to the center of the + * pixel being textured. + * + * Pixel area is GREATER THAN texel area so ZOOMED OUT + * + * Thus, Pixel contains more than one texel */ - primitive->addVertex(voxelXYZ, - rgba); - primitive->addVertex(nextXYZ, - rgba); - primitive->addVertex(upperLeftXYZ, - rgba); - primitive->addVertex(upperLeftXYZ, - rgba); - primitive->addVertex(nextXYZ, - rgba); - primitive->addVertex(upperRightXYZ, - rgba); - - if (m_identificationModeFlag) { - /* - * Each voxel is drawn with two triangles so need to put ID - * in twice since so that number of selections equals number - * of triangles - */ - const int32_t idIndex = ((iRow * m_numberOfColumns * 3) + (iCol * 3)); - CaretAssertVectorIndex(m_identificationIJK, idIndex + 2); - selectionIJK.push_back(m_identificationIJK[idIndex]); - selectionIJK.push_back(m_identificationIJK[idIndex+1]); - selectionIJK.push_back(m_identificationIJK[idIndex+2]); - selectionIJK.push_back(m_identificationIJK[idIndex]); - selectionIJK.push_back(m_identificationIJK[idIndex+1]); - selectionIJK.push_back(m_identificationIJK[idIndex+2]); - } - } - - voxelXYZ[0] = nextXYZ[0]; - voxelXYZ[1] = nextXYZ[1]; - voxelXYZ[2] = nextXYZ[2]; - } - - rowXYZ[0] += m_bottomToTopStepXYZ[0]; - rowXYZ[1] += m_bottomToTopStepXYZ[1]; - rowXYZ[2] += m_bottomToTopStepXYZ[2]; - } - - if (primitive->isValid()) { - if (m_identificationModeFlag) { - int32_t selectedIndex = -1; - float selectedDepth = -1.0; - GraphicsEngineDataOpenGL::drawWithSelection(primitive.get(), - fixedPipelineDrawing->mouseX, - fixedPipelineDrawing->mouseY, - selectedIndex, - selectedDepth); - - if (selectedIndex >= 0) { - const int32_t ijkIndex = selectedIndex * 3; - CaretAssertVectorIndex(selectionIJK, ijkIndex + 2); - - m_selectionIJK[0] = selectionIJK[ijkIndex]; - m_selectionIJK[1] = selectionIJK[ijkIndex+1]; - m_selectionIJK[2] = selectionIJK[ijkIndex+2]; + glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - if ((m_selectionIJK[0] >= 0) - && (m_selectionIJK[1] >= 0) - && (m_selectionIJK[2])) { - if (selectionItemVoxel->isEnabledForSelection()) { - if (selectionItemVoxel->isOtherScreenDepthCloserToViewer(selectedDepth)) { - selectionItemVoxel->setVoxelIdentification(brain, - m_volumeInterface, - m_selectionIJK, - selectedDepth); - - float voxelCoordinates[3]; - m_volumeInterface->indexToSpace(m_selectionIJK[0], m_selectionIJK[1], m_selectionIJK[2], - voxelCoordinates[0], voxelCoordinates[1], voxelCoordinates[2]); - - fixedPipelineDrawing->setSelectedItemScreenXYZ(selectionItemVoxel, - voxelCoordinates); - CaretLogFinest("Selected Voxel (3D): " + AString::fromNumbers(m_selectionIJK, 3, ",")); - } - } - - if (selectionItemVoxelEditing->isEnabledForSelection()) { - if (selectionItemVoxelEditing->getVolumeFileForEditing() == m_volumeFile) { - if (selectionItemVoxelEditing->isOtherScreenDepthCloserToViewer(selectedDepth)) { - selectionItemVoxelEditing->setVoxelIdentification(brain, - m_volumeInterface, - m_selectionIJK, - selectedDepth); - float floatDiffXYZ[3] = { - m_leftToRightStepXYZ[0] + m_bottomToTopStepXYZ[0], - m_leftToRightStepXYZ[1] + m_bottomToTopStepXYZ[1], - m_leftToRightStepXYZ[2] + m_bottomToTopStepXYZ[2] - }; - selectionItemVoxelEditing->setVoxelDiffXYZ(floatDiffXYZ); - - float voxelCoordinates[3]; - m_volumeInterface->indexToSpace(m_selectionIJK[0], m_selectionIJK[1], m_selectionIJK[2], - voxelCoordinates[0], voxelCoordinates[1], voxelCoordinates[2]); - - fixedPipelineDrawing->setSelectedItemScreenXYZ(selectionItemVoxelEditing, - voxelCoordinates); - CaretLogFinest("Selected Voxel Editing (3D): Indices (" - + AString::fromNumbers(m_selectionIJK, 3, ",") - + ") Diff XYZ (" - + AString::fromNumbers(floatDiffXYZ, 3, ",") - + ")"); - } - } - } - } - } - } - else { - /* - * Only allow layer blending when overall volume opacity is off (>= 1.0) - */ - //const DisplayPropertiesVolume* dpv = brain->getDisplayPropertiesVolume(); - const bool allowBlendingFlag(true); //dpv->getOpacity() >= 1.0f); - - glPushAttrib(GL_COLOR_BUFFER_BIT); - if (drawWithBlendingFlag - && allowBlendingFlag) { - glEnable(GL_BLEND); - glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + /* + * GL_TEXTURE_MAG_FILTER - The texture magnification function + * is used when the pixel being textured maps to an area less than or + * equal to one texture element. + * GL_NEAREST - Returns the value of the texture element that is + * nearest (in Manhattan distance) to the center of + * the pixel being textured. + * GL_LINEAR Returns the weighted average of the four texture + * elements that are closest to the center of the + * pixel being textured. + * + * Pixel area is LESS THAN Texel area so ZOOMED IN + * + * Thus, pixel may be inside a texel + * + * If GL_LINEAR is used the voxel "blockiness" is smoothed out + */ + glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); } - GraphicsEngineDataOpenGL::draw(primitive.get()); - glPopAttrib(); - } - } -} - -/** - * @return True if the output slice indices are valid, else false. - * - * @param ijkOut - * Output slice indices. - */ -bool -BrainOpenGLVolumeTextureSliceDrawing::ObliqueSlice::getSelectionIJK(int32_t ijkOut[3]) const -{ - ijkOut[0] = m_selectionIJK[0]; - ijkOut[1] = m_selectionIJK[1]; - ijkOut[2] = m_selectionIJK[2]; - return (m_selectionIJK[0] >= 0); -} - -/** - * @return True if the given slice spatially matches (voxel row/columns and coordinates). - * - * @param slice - * Slice for comparison. - */ -bool -BrainOpenGLVolumeTextureSliceDrawing::ObliqueSlice::spatialMatch(const ObliqueSlice* slice) -{ - CaretAssert(slice); - - const float diffMax = 0.001f; - if ((m_numberOfRows == slice->m_numberOfRows) - && (m_numberOfColumns == slice->m_numberOfColumns) - && (MathFunctions::distanceSquared3D(m_originXYZ, slice->m_originXYZ) < diffMax) - && (MathFunctions::distanceSquared3D(m_leftToRightStepXYZ, slice->m_leftToRightStepXYZ) < diffMax) - && (MathFunctions::distanceSquared3D(m_bottomToTopStepXYZ, slice->m_bottomToTopStepXYZ) < diffMax)) { - return true; - } - - return false; -} - -/** - * Composite the given slices onto this slice - * - * @param slices - * Slices that are composited. - * @return - * True if able to composite all slices, else false. - */ -bool -BrainOpenGLVolumeTextureSliceDrawing::ObliqueSlice::compositeSlicesRGBA(const std::vector& slices) -{ - std::vector matchedSlices; - for (auto s : slices) { - if (s != this) { - if (spatialMatch(s)) { - if (m_rgba.size() == s->m_rgba.size()) { - matchedSlices.push_back(s); - } - else { - return false; - } + break; + case caret::VolumeSliceProjectionTypeEnum::VOLUME_SLICE_PROJECTION_ORTHOGONAL: + { + /* + * No interpolation for orthogonal slice viewing + */ + glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); } + break; } + + GLfloat borderColor[4] = { 0.0, 0.0, 0.0, 0.0 }; + glTexParameterfv(GL_TEXTURE_3D, GL_TEXTURE_BORDER_COLOR, borderColor); } - - if (matchedSlices.empty()) { - return false; - } - - const int32_t numSlices = static_cast(matchedSlices.size()); - const int32_t rgbaLength = static_cast(m_rgba.size() / 4); - - for (int32_t i = 0; i < rgbaLength; i++) { - const int32_t i4 = i * 4; - for (int32_t iSlice = 0; iSlice < numSlices; iSlice++) { - CaretAssertVectorIndex(matchedSlices, iSlice); - const uint8_t* rgbaTop = &matchedSlices[iSlice]->m_rgba[i4]; - uint8_t alphaTop = rgbaTop[3]; - if (matchedSlices[iSlice]->m_opacity < 1.0f) { - float sliceAlphaFloat = ((static_cast(alphaTop) / 255.0f) * matchedSlices[iSlice]->m_opacity); - if (sliceAlphaFloat >= 1.0f) { - alphaTop = 255; - } - else if (sliceAlphaFloat <= 0.0f) { - alphaTop = 0; - } - else { - alphaTop = static_cast(sliceAlphaFloat * 255); - } - } - if (alphaTop > 0) { - uint8_t* rgbaBottom = &m_rgba[i4]; - if ((alphaTop == 255) || - (rgbaBottom[3] == 0)) { - rgbaBottom[0] = rgbaTop[0]; - rgbaBottom[1] = rgbaTop[1]; - rgbaBottom[2] = rgbaTop[2]; - rgbaBottom[3] = 255; - } - else if (alphaTop > 0) { - const float alpha = static_cast(alphaTop) / 255.0f; - const float oneMinusAlpha = 1.0f - alpha; - for (int32_t m = 0; m < 3; m++) { - const int32_t top = static_cast(static_cast(rgbaTop[m]) * alpha); - const int32_t bottom = static_cast(static_cast(rgbaBottom[m]) * oneMinusAlpha); - int32_t blended = (top + bottom); - if (blended > 255) blended = 255; - else if (blended < 0) blended = 0; - - rgbaBottom[m] = static_cast(blended); - } - rgbaBottom[3] = 255; - } - } - } + else { + /* + * No interpolation for Label or RGBA data + */ + glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); } - - return true; } - - - diff --git a/src/Brain/BrainOpenGLVolumeTextureSliceDrawing.h b/src/Brain/BrainOpenGLVolumeTextureSliceDrawing.h index 6ca757818..9e4455053 100644 --- a/src/Brain/BrainOpenGLVolumeTextureSliceDrawing.h +++ b/src/Brain/BrainOpenGLVolumeTextureSliceDrawing.h @@ -62,122 +62,7 @@ namespace caret { // ADD_NEW_METHODS_HERE - private: - class ObliqueSlice { - public: - enum class DataValueType { - INVALID, - CIFTI_LABEL, - CIFTI_PALETTE, - VOLUME_LABEL, - VOLUME_PALETTE, - VOLUME_RGB, - VOLUME_RGBA - }; - - ObliqueSlice(BrainOpenGLFixedPipeline* fixedPipelineDrawing, - VolumeMappableInterface* volumeInterface, - const float opacity, - const int32_t mapIndex, - const int32_t numberOfRows, - const int32_t numberOfColumns, - const int32_t browserTabIndex, - const DisplayPropertiesLabels* displayPropertiesLabels, - const DisplayGroupEnum::Enum displayGroup, - const float originXYZ[3], - const float leftToRightStepXYZ[3], - const float bottomToTopStepXYZ[3], - const VolumeSliceInterpolationEdgeEffectsMaskingEnum::Enum maskingType, - const float voxelEditingValue, - const bool volumeEditingDrawAllVoxelsFlag, - const bool identificationModeFlag, - const bool bottomLayerFlag); - - void assignRgba(const bool volumeEditingDrawAllVoxelsFlag); - - void addOutlines(); - - void loadData(const VolumeSliceInterpolationEdgeEffectsMaskingEnum::Enum maskingType, - const float voxelEditingValue, - const bool volumeEditingDrawAllVoxelsFlag); - - bool setThresholdFileAndMap(); - - bool getSelectionIJK(int32_t ijkOut[3]) const; - - void draw(BrainOpenGLFixedPipeline* fixedPipelineDrawing); - - bool spatialMatch(const ObliqueSlice* slice); - - bool compositeSlicesRGBA(const std::vector& slices); - - VolumeMappableInterface* m_volumeInterface; - - const float m_opacity; - - const CaretMappableDataFile* m_mapFile = NULL; - - const int32_t m_mapIndex; - - const int32_t m_numberOfRows; - - const int32_t m_numberOfColumns; - - const int32_t m_browserTabIndex; - - const DisplayPropertiesLabels* m_displayPropertiesLabels; - - const DisplayGroupEnum::Enum m_displayGroup; - - const int32_t m_identificationX; - - const int32_t m_identificationY; - - const bool m_identificationModeFlag; - - const bool m_bottomLayerFlag; - - CiftiMappableDataFile* m_ciftiMappableFile = NULL; - - VolumeFile* m_volumeFile = NULL; - - uint8_t m_opacityByte = 255; - - CiftiMappableDataFile* m_thresholdCiftiMappableFile = NULL; - - VolumeFile* m_thresholdVolumeFile = NULL; - - PaletteColorMapping* m_thresholdPaletteColorMapping = NULL; - - int32_t m_thresholdMapIndex = -1; - - float m_originXYZ[3]; - - float m_leftToRightStepXYZ[3]; - - float m_bottomToTopStepXYZ[3]; - - DataValueType m_dataValueType = DataValueType::INVALID; - - std::vector m_data; - - std::vector m_thresholdData; - - std::vector m_rgba; - - std::vector m_identificationIJK; - - int32_t m_validVoxelCount = 0; - - int32_t m_sliceNumberOfVoxels = 0; - - int32_t m_voxelNumberOfComponents = 1; - - std::unique_ptr m_identificationHelper; - - int64_t m_selectionIJK[3]; - }; - + private: BrainOpenGLVolumeTextureSliceDrawing(const BrainOpenGLVolumeTextureSliceDrawing&); BrainOpenGLVolumeTextureSliceDrawing& operator=(const BrainOpenGLVolumeTextureSliceDrawing&); @@ -286,6 +171,42 @@ namespace caret { void resetIdentification(); + bool getTextureCoordinates(const VolumeMappableInterface* vf, + const float xyz[3], + const float maxStr[3], + float rstOut[3]) const; + + bool createVolumeTexture(const VolumeMappableInterface* volumeFile, + const DisplayGroupEnum::Enum displayGroup, + const int32_t tabIndex, + const bool allowNonPowerOfTwoTextureFlag, + const bool identificationTextureFlag, + std::vector& rgbaColorsOut, + std::array& textureDimsOut, + std::array& maxStrOut) const; + + GLuint createTextureName(const VolumeMappableInterface* volumeMappableInterface, + const bool identificationTextureFlag, + const DisplayGroupEnum::Enum displayGroup, + const int32_t tabIndex, + std::array& maxStrOut) const; + + void setupTextureFiltering(const CaretMappableDataFile* mapFile, + const VolumeSliceProjectionTypeEnum::Enum sliceProjectionType); + + void processTextureVoxelIdentification(VolumeMappableInterface* volumeMappableInterface); + + struct TextureInfo { + GLuint m_textureID; + std::array m_maxSTR; + }; + + /* + * These items will eventually be moved into the volume and cifti files + */ + static std::map s_volumeTextureInfo; + static std::map s_identificationTextureInfo; + ModelVolume* m_modelVolume; ModelWholeBrain* m_modelWholeBrain; @@ -327,6 +248,9 @@ namespace caret { #ifdef __BRAIN_OPEN_GL_VOLUME_TEXTURE_SLICE_DRAWING_DECLARE__ const int32_t BrainOpenGLVolumeTextureSliceDrawing::IDENTIFICATION_INDICES_PER_VOXEL = 8; + std::map BrainOpenGLVolumeTextureSliceDrawing::s_volumeTextureInfo; + std::map BrainOpenGLVolumeTextureSliceDrawing::s_identificationTextureInfo; + #endif // __BRAIN_OPEN_GL_VOLUME_TEXTURE_SLICE_DRAWING_DECLARE__ } // namespace -- GitLab From 5afa176e1b31551dac914709d43941677978f9e8 Mon Sep 17 00:00:00 2001 From: John Harwell Date: Mon, 17 Jun 2019 12:15:44 -0500 Subject: [PATCH 368/427] For the experimental texture volume drawing, fixed drawing in all view and removed some unused code. --- .../BrainOpenGLVolumeTextureSliceDrawing.cxx | 418 ++++++------------ .../BrainOpenGLVolumeTextureSliceDrawing.h | 27 +- 2 files changed, 130 insertions(+), 315 deletions(-) diff --git a/src/Brain/BrainOpenGLVolumeTextureSliceDrawing.cxx b/src/Brain/BrainOpenGLVolumeTextureSliceDrawing.cxx index 046e0fb94..7e1ce8569 100644 --- a/src/Brain/BrainOpenGLVolumeTextureSliceDrawing.cxx +++ b/src/Brain/BrainOpenGLVolumeTextureSliceDrawing.cxx @@ -775,8 +775,6 @@ BrainOpenGLVolumeTextureSliceDrawing::drawVolumeSliceViewProjection(const BrainO break; } - resetIdentification(); - GLboolean cullFaceOn = glIsEnabled(GL_CULL_FACE); if (drawVolumeSlicesFlag) { @@ -802,13 +800,6 @@ BrainOpenGLVolumeTextureSliceDrawing::drawVolumeSliceViewProjection(const BrainO } break; } - - /* - * Process selection - */ - if (m_identificationModeFlag) { - processIdentification(); - } } if ( ! m_identificationModeFlag) { @@ -874,8 +865,8 @@ BrainOpenGLVolumeTextureSliceDrawing::drawVolumeSliceViewProjection(const BrainO * Type of projection for the slice drawing (oblique, orthogonal) * @param sliceViewPlane * View plane that is displayed. - * @param montageSliceIndex - * Selected montage slice index + * @param sliceCoordinates + * Slice coordinates * @param planeOut * OUTPUT plane of slice after transforms. */ @@ -1422,8 +1413,6 @@ BrainOpenGLVolumeTextureSliceDrawing::drawAxesCrosshairs(const VolumeSliceProjec /** * Draw the axes crosshairs for an orthogonal slice. * - * @param sliceProjectionType - * Type of projection for the slice drawing (oblique, orthogonal) * @param sliceViewPlane * The slice plane view. * @param sliceCoordinatesIn @@ -2230,158 +2219,6 @@ BrainOpenGLVolumeTextureSliceDrawing::setOrthographicProjection(const BrainOpenG glMatrixMode(GL_MODELVIEW); } -/** - * Reset for volume identification. - * - * Clear identification indices and if identification is enabled, - * reserve a reasonable amount of space for the indices. - */ -void -BrainOpenGLVolumeTextureSliceDrawing::resetIdentification() -{ - m_identificationIndices.clear(); - - if (m_identificationModeFlag) { - int64_t estimatedNumberOfItems = 0; - - std::vector volumeDims; - m_volumeDrawInfo[0].volumeFile->getDimensions(volumeDims); - if (volumeDims.size() >= 3) { - const int64_t maxDim = std::max(volumeDims[0], - std::max(volumeDims[1], volumeDims[2])); - estimatedNumberOfItems = maxDim * maxDim * IDENTIFICATION_INDICES_PER_VOXEL; - } - - m_identificationIndices.reserve(estimatedNumberOfItems); - } -} - -/** - * Add a voxel to the identification indices. - * - * @param volumeIndex - * Index of the volume. - * @param mapIndex - * Index of the volume map. - * @param voxelI - * Voxel Index I - * @param voxelJ - * Voxel Index J - * @param voxelK - * Voxel Index K - * @param voxelDiffXYZ - * Change in XYZ from voxel bottom left to top right - * @param rgbaForColorIdentificationOut - * Encoded identification in RGBA color OUTPUT - */ -void -BrainOpenGLVolumeTextureSliceDrawing::addVoxelToIdentification(const int32_t volumeIndex, - const int32_t mapIndex, - const int32_t voxelI, - const int32_t voxelJ, - const int32_t voxelK, - const float voxelDiffXYZ[3], - uint8_t rgbaForColorIdentificationOut[4]) -{ - const int32_t idIndex = m_identificationIndices.size() / IDENTIFICATION_INDICES_PER_VOXEL; - - m_fixedPipelineDrawing->colorIdentification->addItem(rgbaForColorIdentificationOut, - SelectionItemDataTypeEnum::VOXEL, - idIndex); - rgbaForColorIdentificationOut[3] = 255; - - /* - * ID stack requires integers to - * use an integer pointer to the float values - */ - CaretAssert(sizeof(float) == sizeof(int32_t)); - const int32_t* intPointerDiffXYZ = (int32_t*)voxelDiffXYZ; - - /* - * If these items change, need to update reset and - * processing of identification. - */ - m_identificationIndices.push_back(volumeIndex); - m_identificationIndices.push_back(mapIndex); - m_identificationIndices.push_back(voxelI); - m_identificationIndices.push_back(voxelJ); - m_identificationIndices.push_back(voxelK); - m_identificationIndices.push_back(intPointerDiffXYZ[0]); - m_identificationIndices.push_back(intPointerDiffXYZ[1]); - m_identificationIndices.push_back(intPointerDiffXYZ[2]); -} - -/** - * Process voxel identification. - */ -void -BrainOpenGLVolumeTextureSliceDrawing::processIdentification() -{ - int32_t identifiedItemIndex; - float depth = -1.0; - m_fixedPipelineDrawing->getIndexFromColorSelection(SelectionItemDataTypeEnum::VOXEL, - m_fixedPipelineDrawing->mouseX, - m_fixedPipelineDrawing->mouseY, - identifiedItemIndex, - depth); - if (identifiedItemIndex >= 0) { - const int32_t idIndex = identifiedItemIndex * IDENTIFICATION_INDICES_PER_VOXEL; - const int32_t volDrawInfoIndex = m_identificationIndices[idIndex]; - CaretAssertVectorIndex(m_volumeDrawInfo, volDrawInfoIndex); - VolumeMappableInterface* vf = m_volumeDrawInfo[volDrawInfoIndex].volumeFile; - const int64_t voxelIndices[3] = { - m_identificationIndices[idIndex + 2], - m_identificationIndices[idIndex + 3], - m_identificationIndices[idIndex + 4] - }; - - const float* floatDiffXYZ = (float*)&m_identificationIndices[idIndex + 5]; - - SelectionItemVoxel* voxelID = m_brain->getSelectionManager()->getVoxelIdentification(); - if (voxelID->isEnabledForSelection()) { - if (voxelID->isOtherScreenDepthCloserToViewer(depth)) { - voxelID->setVoxelIdentification(m_brain, - vf, - voxelIndices, - depth); - - float voxelCoordinates[3]; - vf->indexToSpace(voxelIndices[0], voxelIndices[1], voxelIndices[2], - voxelCoordinates[0], voxelCoordinates[1], voxelCoordinates[2]); - - m_fixedPipelineDrawing->setSelectedItemScreenXYZ(voxelID, - voxelCoordinates); - CaretLogFinest("Selected Voxel (3D): " + AString::fromNumbers(voxelIndices, 3, ",")); - } - } - - SelectionItemVoxelEditing* voxelEditID = m_brain->getSelectionManager()->getVoxelEditingIdentification(); - if (voxelEditID->isEnabledForSelection()) { - if (voxelEditID->getVolumeFileForEditing() == vf) { - if (voxelEditID->isOtherScreenDepthCloserToViewer(depth)) { - voxelEditID->setVoxelIdentification(m_brain, - vf, - voxelIndices, - depth); - voxelEditID->setVoxelDiffXYZ(floatDiffXYZ); - - float voxelCoordinates[3]; - vf->indexToSpace(voxelIndices[0], voxelIndices[1], voxelIndices[2], - voxelCoordinates[0], voxelCoordinates[1], voxelCoordinates[2]); - - m_fixedPipelineDrawing->setSelectedItemScreenXYZ(voxelEditID, - voxelCoordinates); - CaretLogFinest("Selected Voxel Editing (3D): Indices (" - + AString::fromNumbers(voxelIndices, 3, ",") - + ") Diff XYZ (" - + AString::fromNumbers(floatDiffXYZ, 3, ",") - + ")"); - } - } - } - } -} - /** * Get the maximum bounds that enclose the volumes and the minimum * voxel spacing from the volumes. @@ -2509,21 +2346,35 @@ BrainOpenGLVolumeTextureSliceDrawing::createObliqueTransformationMatrix(const fl /* ======================================================================= */ +/** + * Get the texture coordinates for an XYZ-coordinate + * + * @param volumeMappableInterface + * The volume file + * @param xyz + * The XYZ coordinate + * @param maxStr + * The maximum texture str coordinate + * @param strOut + * Output texture str coordinate + * @return + * True if output coordinate is valid, else false. + */ bool -BrainOpenGLVolumeTextureSliceDrawing::getTextureCoordinates(const VolumeMappableInterface* vf, - const float xyz[3], - const float maxStr[3], - float rstOut[3]) const +BrainOpenGLVolumeTextureSliceDrawing::getTextureCoordinates(const VolumeMappableInterface* volumeMappableInterface, + const std::array& xyz, + const std::array& maxStr, + std::array& strOut) const { std::vector dims(5); - vf->getDimensions(dims); + volumeMappableInterface->getDimensions(dims); int64_t smallCornerIJK[3] = { 0, 0, 0}; float smallCornerXYZ[3] = { 0.0, 0.0, 0.0 }; - vf->indexToSpace(smallCornerIJK, smallCornerXYZ); + volumeMappableInterface->indexToSpace(smallCornerIJK, smallCornerXYZ); int64_t bigCornerIJK[3] = { dims[0] - 1, dims[1] - 1, dims[2] - 1 }; float bigCornerXYZ[3] = { 0.0, 0.0, 0.0 }; - vf->indexToSpace(bigCornerIJK, bigCornerXYZ); + volumeMappableInterface->indexToSpace(bigCornerIJK, bigCornerXYZ); /* * Coordinates from volume are at CENTER of the voxel @@ -2531,9 +2382,9 @@ BrainOpenGLVolumeTextureSliceDrawing::getTextureCoordinates(const VolumeMappable * outside edge to outside edge of the volume. */ float voxelOneXYZ[3]; - vf->indexToSpace(0, 0, 0, voxelOneXYZ); + volumeMappableInterface->indexToSpace(0, 0, 0, voxelOneXYZ); float voxelTwoXYZ[3]; - vf->indexToSpace(1, 1, 1, voxelTwoXYZ); + volumeMappableInterface->indexToSpace(1, 1, 1, voxelTwoXYZ); const float halfVoxelSizeXYZ[3] { (voxelTwoXYZ[0] - voxelOneXYZ[0]) / 2.0f, (voxelTwoXYZ[1] - voxelOneXYZ[1]) / 2.0f, @@ -2556,18 +2407,37 @@ BrainOpenGLVolumeTextureSliceDrawing::getTextureCoordinates(const VolumeMappable (xyz[2] - smallCornerXYZ[2]) / rangeXYZ[2], }; - rstOut[0] = -1.0 + (normalizedOffset[0] * 2.0); - rstOut[1] = -1.0 + (normalizedOffset[1] * 2.0); - rstOut[2] = -1.0 + (normalizedOffset[2] * 2.0); - rstOut[0] = normalizedOffset[0] * maxStr[0]; - rstOut[1] = normalizedOffset[1] * maxStr[1]; - rstOut[2] = normalizedOffset[2] * maxStr[2]; + strOut[0] = normalizedOffset[0] * maxStr[0]; + strOut[1] = normalizedOffset[1] * maxStr[1]; + strOut[2] = normalizedOffset[2] * maxStr[2]; return true; } +/** + * Create RGBA coloring for volume's texture + * + * @param volumeMappableInterface + * The volume file + * @param displayGroup + * Display group for current tab + * @param tabIndex + * Index of tab + * @param allowNonPowerOfTwoTextureFlag + * Allow non power-of-two texture + * @param identificationTextureFlag + * True if creating texture for voxel identification + * @param rgbaColorsOut + * Output containing RGBA coloring + * @param textureDimsOut + * Output with dimensions for texture + * @param maxStrOut + * Output with maximum Texture str coordinates + * @return + * True if output rgba coloring is valid, else false. + */ bool -BrainOpenGLVolumeTextureSliceDrawing::createVolumeTexture(const VolumeMappableInterface* volumeFile, +BrainOpenGLVolumeTextureSliceDrawing::createVolumeTexture(const VolumeMappableInterface* volumeMappableInterface, const DisplayGroupEnum::Enum displayGroup, const int32_t tabIndex, const bool allowNonPowerOfTwoTextureFlag, @@ -2579,7 +2449,7 @@ BrainOpenGLVolumeTextureSliceDrawing::createVolumeTexture(const VolumeMappableIn maxStrOut.fill(0.0); std::vector dims(5); - volumeFile->getDimensions(dims); + volumeMappableInterface->getDimensions(dims); textureDimsOut.fill(256); const int64_t dimLargest = *std::max_element(dims.begin(), dims.begin() + 3); if (allowNonPowerOfTwoTextureFlag) { @@ -2587,7 +2457,7 @@ BrainOpenGLVolumeTextureSliceDrawing::createVolumeTexture(const VolumeMappableIn } else { if (dimLargest > 512) { - const CaretMappableDataFile* mapFile = dynamic_cast(volumeFile); + const CaretMappableDataFile* mapFile = dynamic_cast(volumeMappableInterface); const QString filename((mapFile != NULL) ? mapFile->getFileNameNoPath() : "no file name available"); @@ -2661,7 +2531,7 @@ BrainOpenGLVolumeTextureSliceDrawing::createVolumeTexture(const VolumeMappableIn int64_t columnStepIJK[3] = { 1, 0, 0 }; std::fill(rgbaSlice.begin(), rgbaSlice.end(), 0); - volumeFile->getVoxelColorsForSliceInMap(mapIndex, + volumeMappableInterface->getVoxelColorsForSliceInMap(mapIndex, firstVoxelIJK, rowStepIJK, columnStepIJK, @@ -2695,6 +2565,20 @@ BrainOpenGLVolumeTextureSliceDrawing::createVolumeTexture(const VolumeMappableIn } +/** + * Create a volume's texture + * + * @param volumeMappableInterface + * The volume file + * @param identificationTextureFlag + * True if creating texture for voxel identification + * @param displayGroup + * Display group for current tab + * @param maxStrOut + * Output with maximum Texture str coordinates + * @return + * True if output rgba coloring is valid, else false. + */ GLuint BrainOpenGLVolumeTextureSliceDrawing::createTextureName(const VolumeMappableInterface* volumeMappableInterface, const bool identificationTextureFlag, @@ -2769,9 +2653,13 @@ BrainOpenGLVolumeTextureSliceDrawing::createTextureName(const VolumeMappableInte glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER); glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_BORDER); + /* + * Compression works but appears lossy for palette mapped data + * but may be okay for label mapped data + */ glTexImage3D(GL_TEXTURE_3D, 0, - GL_RGBA, + GL_RGBA, //GL_COMPRESSED_RGBA, //GL_RGBA, textureDims[0], textureDims[1], textureDims[2], @@ -2791,6 +2679,8 @@ BrainOpenGLVolumeTextureSliceDrawing::createTextureName(const VolumeMappableInte * * @param sliceViewPlane * The plane for slice drawing. + * @param sliceProjectionType + * The slice projection type * @param transformationMatrix * The for oblique viewing. */ @@ -2822,11 +2712,6 @@ BrainOpenGLVolumeTextureSliceDrawing::drawObliqueSliceWithOutlines(const VolumeS const bool obliqueSliceModeThreeDimFlag = false; - float m[16]; - glGetFloatv(GL_MODELVIEW_MATRIX, m); - Matrix4x4 tm; - tm.setMatrixFromOpenGL(m); - const int32_t numVolumes = static_cast(m_volumeDrawInfo.size()); /* @@ -2927,74 +2812,14 @@ BrainOpenGLVolumeTextureSliceDrawing::drawObliqueSliceWithOutlines(const VolumeS break; } - const int32_t alignVoxelsFlag = 0;//1; - if (alignVoxelsFlag == 1) { - /* - * Adjust for when selected slices are not at the origin - */ - const float xOffset = MathFunctions::remainder(screenOffsetX, voxelSize); - const float yOffset = MathFunctions::remainder(screenOffsetY, voxelSize); - originOffsetX -= xOffset; - originOffsetY -= yOffset; - - const int64_t numVoxelsToLeft = static_cast(MathFunctions::round(minScreenX + originOffsetX) / voxelSize); - const int64_t numVoxelsToRight = static_cast(MathFunctions::round(maxScreenX + originOffsetX) / voxelSize); - const int64_t numVoxelsToBottom = static_cast(MathFunctions::round(minScreenY + originOffsetY) / voxelSize); - const int64_t numVoxelsToTop = static_cast(MathFunctions::round(maxScreenY + originOffsetY)/ voxelSize); - - const float halfVoxel = voxelSize / 2.0; - - const float firstVoxelCenterX = (numVoxelsToLeft * voxelSize) + originOffsetX; - const float lastVoxelCenterX = (numVoxelsToRight * voxelSize) + originOffsetX; - - const float firstVoxelCenterY = (numVoxelsToBottom * voxelSize) + originOffsetY; - const float lastVoxelCenterY = (numVoxelsToTop * voxelSize) + originOffsetY; - - float newMinScreenX = firstVoxelCenterX - halfVoxel; - float newMaxScreenX = lastVoxelCenterX + halfVoxel; - float newMinScreenY = firstVoxelCenterY - halfVoxel; - float newMaxScreenY = lastVoxelCenterY + halfVoxel; - - if (debugFlag) { - const AString msg2 = ("Origin Voxel Coordinate: (" - + AString::fromNumbers(actualOrigin, 3, ",") - + "\n Oblique Screen X: (" - + AString::number(minScreenX) - + "," - + AString::number(maxScreenX) - + ") Y: (" - + AString::number(minScreenY) - + "," - + AString::number(maxScreenY) - + ")\nNew X: (" - + AString::number(newMinScreenX) - + "," - + AString::number(newMaxScreenX) - + ") Y: (" - + AString::number(newMinScreenY) - + "," - + AString::number(newMaxScreenY) - + ") Diff: (" - + AString::number((newMaxScreenX - newMinScreenX) / voxelSize) - + "," - + AString::number((newMaxScreenY - newMinScreenY) / voxelSize) - + ")"); - std::cout << qPrintable(msg2) << std::endl; - } - - minScreenX = newMinScreenX; - maxScreenX = newMaxScreenX; - minScreenY = newMinScreenY; - maxScreenY = newMaxScreenY; - } /* * Set the corners of the screen for the respective view */ - float bottomLeft[3]; - float bottomRight[3]; - float topRight[3]; - float topLeft[3]; + std::array bottomLeft; + std::array bottomRight; + std::array topRight; + std::array topLeft; switch (sliceViewPlane) { case VolumeSliceViewPlaneEnum::ALL: CaretAssert(0); @@ -3047,14 +2872,14 @@ BrainOpenGLVolumeTextureSliceDrawing::drawObliqueSliceWithOutlines(const VolumeS /* * Transform the corners of the screen into model coordinates */ - transformationMatrix.multiplyPoint3(bottomLeft); - transformationMatrix.multiplyPoint3(bottomRight); - transformationMatrix.multiplyPoint3(topRight); - transformationMatrix.multiplyPoint3(topLeft); + transformationMatrix.multiplyPoint3(bottomLeft.data()); + transformationMatrix.multiplyPoint3(bottomRight.data()); + transformationMatrix.multiplyPoint3(topRight.data()); + transformationMatrix.multiplyPoint3(topLeft.data()); if (debugFlag) { - const double bottomDist = MathFunctions::distance3D(bottomLeft, bottomRight); - const double topDist = MathFunctions::distance3D(topLeft, topRight); + const double bottomDist = MathFunctions::distance3D(bottomLeft.data(), bottomRight.data()); + const double topDist = MathFunctions::distance3D(topLeft.data(), topRight.data()); const double bottomVoxels = bottomDist / voxelSize; const double topVoxels = topDist / voxelSize; const AString msg = ("Bottom Dist: " @@ -3072,10 +2897,10 @@ BrainOpenGLVolumeTextureSliceDrawing::drawObliqueSliceWithOutlines(const VolumeS m_fixedPipelineDrawing->setLineWidth(3.0); glColor3f(1.0, 0.0, 0.0); glBegin(GL_LINE_LOOP); - glVertex3fv(bottomLeft); - glVertex3fv(bottomRight); - glVertex3fv(topRight); - glVertex3fv(topLeft); + glVertex3fv(bottomLeft.data()); + glVertex3fv(bottomRight.data()); + glVertex3fv(topRight.data()); + glVertex3fv(topLeft.data()); glEnd(); } @@ -3088,8 +2913,8 @@ BrainOpenGLVolumeTextureSliceDrawing::drawObliqueSliceWithOutlines(const VolumeS topLeft[2] - bottomLeft[2], }; MathFunctions::normalizeVector(bottomLeftToTopLeftUnitVector); - const double bottomLeftToTopLeftDistance = MathFunctions::distance3D(bottomLeft, - topLeft); + const double bottomLeftToTopLeftDistance = MathFunctions::distance3D(bottomLeft.data(), + topLeft.data()); /* * Unit vector and distance in model coords along right side of screen @@ -3100,8 +2925,8 @@ BrainOpenGLVolumeTextureSliceDrawing::drawObliqueSliceWithOutlines(const VolumeS topRight[2] - bottomRight[2] }; MathFunctions::normalizeVector(bottomRightToTopRightUnitVector); - const double bottomRightToTopRightDistance = MathFunctions::distance3D(bottomRight, - topRight); + const double bottomRightToTopRightDistance = MathFunctions::distance3D(bottomRight.data(), + topRight.data()); if ((bottomLeftToTopLeftDistance > 0) && (bottomRightToTopRightDistance > 0)) { @@ -3109,6 +2934,15 @@ BrainOpenGLVolumeTextureSliceDrawing::drawObliqueSliceWithOutlines(const VolumeS glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + if (m_modelWholeBrain != NULL) { + glAlphaFunc(GL_GEQUAL, 0.95); + glEnable(GL_ALPHA_TEST); + glEnable(GL_DEPTH_TEST); + } + else { + glDisable(GL_DEPTH_TEST); + } + bool firstFlag(true); for (int32_t iVol = 0; iVol < numVolumes; iVol++) { const BrainOpenGLFixedPipeline::VolumeDrawInfo& vdi = m_volumeDrawInfo[iVol]; @@ -3181,27 +3015,24 @@ BrainOpenGLVolumeTextureSliceDrawing::drawObliqueSliceWithOutlines(const VolumeS } if (textureID > 0) { - float textureBottomLeft[3]; - getTextureCoordinates(volumeInterface, bottomLeft, maxStr.data(), textureBottomLeft); - float textureBottomRight[3]; - getTextureCoordinates(volumeInterface, bottomRight, maxStr.data(), textureBottomRight); - float textureTopLeft[3]; - getTextureCoordinates(volumeInterface, topLeft, maxStr.data(), textureTopLeft); - float textureTopRight[3]; - getTextureCoordinates(volumeInterface, topRight, maxStr.data(), textureTopRight); + std::array textureBottomLeft; + getTextureCoordinates(volumeInterface, bottomLeft, maxStr, textureBottomLeft); + std::array textureBottomRight; + getTextureCoordinates(volumeInterface, bottomRight, maxStr, textureBottomRight); + std::array textureTopLeft; + getTextureCoordinates(volumeInterface, topLeft, maxStr, textureTopLeft); + std::array textureTopRight; + getTextureCoordinates(volumeInterface, topRight, maxStr, textureTopRight); if (debugFlag) { - std::cout << "Bottom Left RST: " << AString::fromNumbers(textureBottomLeft, 3, ", ") << std::endl; - std::cout << "Bottom Right RST: " << AString::fromNumbers(textureBottomRight, 3, ", ") << std::endl; - std::cout << "Top Right RST: " << AString::fromNumbers(textureTopRight, 3, ", ") << std::endl; - std::cout << "Top Left RST: " << AString::fromNumbers(textureTopLeft, 3, ", ") << std::endl; + std::cout << "Bottom Left RST: " << AString::fromNumbers(textureBottomLeft.data(), 3, ", ") << std::endl; + std::cout << "Bottom Right RST: " << AString::fromNumbers(textureBottomRight.data(), 3, ", ") << std::endl; + std::cout << "Top Right RST: " << AString::fromNumbers(textureTopRight.data(), 3, ", ") << std::endl; + std::cout << "Top Left RST: " << AString::fromNumbers(textureTopLeft.data(), 3, ", ") << std::endl; std::cout << std::endl; } glDisable(GL_CULL_FACE); - if (m_modelWholeBrain == NULL) { - glDisable(GL_DEPTH_TEST); - } m_fixedPipelineDrawing->testForOpenGLError("Before drawing with texture"); glEnable(GL_TEXTURE_3D); glBindTexture(GL_TEXTURE_3D, textureID); @@ -3215,18 +3046,16 @@ BrainOpenGLVolumeTextureSliceDrawing::drawObliqueSliceWithOutlines(const VolumeS */ setupTextureFiltering(mapFile, sliceProjectionType); - std::cout << "Depth Test On: " << AString::fromBool(glIsEnabled(GL_DEPTH_TEST)) << std::endl; - glBegin(GL_QUADS); glColor4f(0.0, 0.0, 1.0, 0.0); - glTexCoord3fv(textureBottomLeft); - glVertex3fv(bottomLeft); - glTexCoord3fv(textureBottomRight); - glVertex3fv(bottomRight); - glTexCoord3fv(textureTopRight); - glVertex3fv(topRight); - glTexCoord3fv(textureTopLeft); - glVertex3fv(topLeft); + glTexCoord3fv(textureBottomLeft.data()); + glVertex3fv(bottomLeft.data()); + glTexCoord3fv(textureBottomRight.data()); + glVertex3fv(bottomRight.data()); + glTexCoord3fv(textureTopRight.data()); + glVertex3fv(topRight.data()); + glTexCoord3fv(textureTopLeft.data()); + glVertex3fv(topLeft.data()); glEnd(); glBindTexture(GL_TEXTURE_3D, 0); @@ -3250,6 +3079,9 @@ BrainOpenGLVolumeTextureSliceDrawing::drawObliqueSliceWithOutlines(const VolumeS } } +/** + * Process identification from the texture volume + */ void BrainOpenGLVolumeTextureSliceDrawing::processTextureVoxelIdentification(VolumeMappableInterface* volumeMappableInterface) { diff --git a/src/Brain/BrainOpenGLVolumeTextureSliceDrawing.h b/src/Brain/BrainOpenGLVolumeTextureSliceDrawing.h index 9e4455053..cad8f5fe4 100644 --- a/src/Brain/BrainOpenGLVolumeTextureSliceDrawing.h +++ b/src/Brain/BrainOpenGLVolumeTextureSliceDrawing.h @@ -152,14 +152,6 @@ namespace caret { void createObliqueTransformationMatrix(const float sliceCoordinates[3], Matrix4x4& obliqueTransformationMatrixOut); - void addVoxelToIdentification(const int32_t volumeIndex, - const int32_t mapIndex, - const int32_t voxelI, - const int32_t voxelJ, - const int32_t voxelK, - const float voxelDiffXYZ[3], - uint8_t rgbaForColorIdentificationOut[4]); - bool getVolumeDrawingViewDependentCulling(const VolumeSliceViewPlaneEnum::Enum sliceViewPlane, const float selectedSliceCoordinate, const VolumeMappableInterface* volumeFile, @@ -167,14 +159,10 @@ namespace caret { int64_t culledLastVoxelIJKOut[3], float voxelDeltaXYZOut[3]); - void processIdentification(); - - void resetIdentification(); - - bool getTextureCoordinates(const VolumeMappableInterface* vf, - const float xyz[3], - const float maxStr[3], - float rstOut[3]) const; + bool getTextureCoordinates(const VolumeMappableInterface* volumeMappableInterface, + const std::array& xyz, + const std::array& maxStr, + std::array& strOut) const; bool createVolumeTexture(const VolumeMappableInterface* volumeFile, const DisplayGroupEnum::Enum displayGroup, @@ -217,7 +205,7 @@ namespace caret { Brain* m_brain; - std::vector > m_ciftiMappableFileData; + std::vector> m_ciftiMappableFileData; BrainOpenGLFixedPipeline* m_fixedPipelineDrawing; @@ -237,17 +225,12 @@ namespace caret { VolumeSliceInterpolationEdgeEffectsMaskingEnum::Enum m_obliqueSliceMaskingType = VolumeSliceInterpolationEdgeEffectsMaskingEnum::OFF; - std::vector m_identificationIndices; - bool m_identificationModeFlag; - static const int32_t IDENTIFICATION_INDICES_PER_VOXEL; - // ADD_NEW_MEMBERS_HERE }; #ifdef __BRAIN_OPEN_GL_VOLUME_TEXTURE_SLICE_DRAWING_DECLARE__ - const int32_t BrainOpenGLVolumeTextureSliceDrawing::IDENTIFICATION_INDICES_PER_VOXEL = 8; std::map BrainOpenGLVolumeTextureSliceDrawing::s_volumeTextureInfo; std::map BrainOpenGLVolumeTextureSliceDrawing::s_identificationTextureInfo; -- GitLab From c6bd18da30d28d3a43b6aac696a0176d8b58e0de Mon Sep 17 00:00:00 2001 From: John Harwell Date: Tue, 18 Jun 2019 11:38:27 -0500 Subject: [PATCH 369/427] Added display of sform and coordinates of corner voxels for volume files in "wb_command -file-information". --- src/Files/VolumeFile.cxx | 61 ++++++++++++++++++++++++++++++++-------- 1 file changed, 49 insertions(+), 12 deletions(-) diff --git a/src/Files/VolumeFile.cxx b/src/Files/VolumeFile.cxx index ea9a0f152..746eea8d2 100644 --- a/src/Files/VolumeFile.cxx +++ b/src/Files/VolumeFile.cxx @@ -2010,18 +2010,55 @@ VolumeFile::addToDataFileContentInformation(DataFileContentInformation& dataFile dimString += AString::number(dims[i]); } dataFileInformation.addNameAndValue("Dimensions", dimString); - const int64_t zero64 = 0; - if (indexValid(zero64, zero64, zero64)) { - float x, y, z; - indexToSpace(zero64, zero64, zero64, x, y, z); - dataFileInformation.addNameAndValue("IJK = (0,0,0)", - ("XYZ = (" - + AString::number(x) - + ", " - + AString::number(y) - + ", " - + AString::number(z) - + ")")); + + if (dims.size() >= 3) { + const int64_t maxI((dims[0] > 1) ? dims[0] - 1 : 0); + const int64_t maxJ((dims[1] > 1) ? dims[1] - 1 : 0); + const int64_t maxK((dims[2] > 1) ? dims[2] - 1 : 0); + int64_t corners[8][3] = { + { 0, 0, 0 }, + { maxI, 0, 0 }, + { maxI, maxJ, 0 }, + { 0, maxJ, 0}, + { 0, 0, maxK }, + { maxI, 0, maxK }, + { maxI, maxJ, maxK }, + { 0, maxJ, maxK} + }; + for (int32_t m = 0; m < 8; m++) { + const int64_t i(corners[m][0]); + const int64_t j(corners[m][1]); + const int64_t k(corners[m][2]); + if (indexValid(i, j, k)) { + float x, y, z; + indexToSpace(i, j, k, x, y, z); + dataFileInformation.addNameAndValue("IJK = (" + + AString::number(i) + + "," + + AString::number(j) + + "," + + AString::number(k) + + ")", + ("XYZ = (" + + AString::number(x) + + ", " + + AString::number(y) + + ", " + + AString::number(z) + + ")")); + } + } + } + + const std::vector>& sform = getVolumeSpace().getSform(); + QString sformName("sform"); + for (const auto& row : sform) { + AString s; + for (const auto element : row) { + s.append(AString::number(element, 'f', 6) + " "); + } + dataFileInformation.addNameAndValue(sformName, s); + sformName.clear(); } BoundingBox boundingBox; -- GitLab From 84008741874999e3f0895327a8e00094b6fdd2e0 Mon Sep 17 00:00:00 2001 From: John Harwell Date: Tue, 18 Jun 2019 11:40:27 -0500 Subject: [PATCH 370/427] For the experimental texture volume drawing, fixed drawing of volumes with 'other' orientations such as SRP. --- .../BrainOpenGLVolumeTextureSliceDrawing.cxx | 158 +++++++++++++----- 1 file changed, 116 insertions(+), 42 deletions(-) diff --git a/src/Brain/BrainOpenGLVolumeTextureSliceDrawing.cxx b/src/Brain/BrainOpenGLVolumeTextureSliceDrawing.cxx index 7e1ce8569..ed8d708c4 100644 --- a/src/Brain/BrainOpenGLVolumeTextureSliceDrawing.cxx +++ b/src/Brain/BrainOpenGLVolumeTextureSliceDrawing.cxx @@ -2368,49 +2368,69 @@ BrainOpenGLVolumeTextureSliceDrawing::getTextureCoordinates(const VolumeMappable { std::vector dims(5); volumeMappableInterface->getDimensions(dims); - int64_t smallCornerIJK[3] = { 0, 0, 0}; - float smallCornerXYZ[3] = { 0.0, 0.0, 0.0 }; - volumeMappableInterface->indexToSpace(smallCornerIJK, smallCornerXYZ); - int64_t bigCornerIJK[3] = { dims[0] - 1, dims[1] - 1, dims[2] - 1 }; - float bigCornerXYZ[3] = { 0.0, 0.0, 0.0 }; - volumeMappableInterface->indexToSpace(bigCornerIJK, bigCornerXYZ); +// int64_t smallCornerIJK[3] = { 0, 0, 0}; +// float smallCornerXYZ[3] = { 0.0, 0.0, 0.0 }; +// volumeMappableInterface->indexToSpace(smallCornerIJK, smallCornerXYZ); +// +// int64_t bigCornerIJK[3] = { dims[0] - 1, dims[1] - 1, dims[2] - 1 }; +// float bigCornerXYZ[3] = { 0.0, 0.0, 0.0 }; +// volumeMappableInterface->indexToSpace(bigCornerIJK, bigCornerXYZ); +// +// /* +// * Coordinates from volume are at CENTER of the voxel +// * so increase size of volume so that range is from +// * outside edge to outside edge of the volume. +// */ +// float voxelOneXYZ[3]; +// volumeMappableInterface->indexToSpace(0, 0, 0, voxelOneXYZ); +// float voxelTwoXYZ[3]; +// volumeMappableInterface->indexToSpace(1, 1, 1, voxelTwoXYZ); +// const float halfVoxelSizeXYZ[3] { +// (voxelTwoXYZ[0] - voxelOneXYZ[0]) / 2.0f, +// (voxelTwoXYZ[1] - voxelOneXYZ[1]) / 2.0f, +// (voxelTwoXYZ[2] - voxelOneXYZ[2]) / 2.0f, +// }; +// for (int32_t i = 0; i < 3; i++) { +// smallCornerXYZ[i] -= halfVoxelSizeXYZ[i]; +// bigCornerXYZ[i] += halfVoxelSizeXYZ[i]; +// } +// +// const float rangeXYZ[3] = { +// (bigCornerXYZ[0] - smallCornerXYZ[0]), +// (bigCornerXYZ[1] - smallCornerXYZ[1]), +// (bigCornerXYZ[2] - smallCornerXYZ[2]) +// }; +// +// const float normalizedOffset[3] = { +// (xyz[0] - smallCornerXYZ[0]) / rangeXYZ[0], +// (xyz[1] - smallCornerXYZ[1]) / rangeXYZ[1], +// (xyz[2] - smallCornerXYZ[2]) / rangeXYZ[2], +// }; +// +// strOut[0] = normalizedOffset[0] * maxStr[0]; +// strOut[1] = normalizedOffset[1] * maxStr[1]; +// strOut[2] = normalizedOffset[2] * maxStr[2]; - /* - * Coordinates from volume are at CENTER of the voxel - * so increase size of volume so that range is from - * outside edge to outside edge of the volume. - */ - float voxelOneXYZ[3]; - volumeMappableInterface->indexToSpace(0, 0, 0, voxelOneXYZ); - float voxelTwoXYZ[3]; - volumeMappableInterface->indexToSpace(1, 1, 1, voxelTwoXYZ); - const float halfVoxelSizeXYZ[3] { - (voxelTwoXYZ[0] - voxelOneXYZ[0]) / 2.0f, - (voxelTwoXYZ[1] - voxelOneXYZ[1]) / 2.0f, - (voxelTwoXYZ[2] - voxelOneXYZ[2]) / 2.0f, - }; - for (int32_t i = 0; i < 3; i++) { - smallCornerXYZ[i] -= halfVoxelSizeXYZ[i]; - bigCornerXYZ[i] += halfVoxelSizeXYZ[i]; + { + const VolumeSpace& volumeSpace = volumeMappableInterface->getVolumeSpace(); + std::array ijk; + volumeSpace.spaceToIndex(xyz.data(), ijk.data()); + + const std::array normalizedIJK { + (ijk[0] / dims[0]), + (ijk[1] / dims[1]), + (ijk[2] / dims[2]) + }; + std::array str { + (normalizedIJK[0] * maxStr[0]), + (normalizedIJK[1] * maxStr[1]), + (normalizedIJK[2] * maxStr[2]) + }; + + strOut = str; } - const float rangeXYZ[3] = { - (bigCornerXYZ[0] - smallCornerXYZ[0]), - (bigCornerXYZ[1] - smallCornerXYZ[1]), - (bigCornerXYZ[2] - smallCornerXYZ[2]) - }; - - const float normalizedOffset[3] = { - (xyz[0] - smallCornerXYZ[0]) / rangeXYZ[0], - (xyz[1] - smallCornerXYZ[1]) / rangeXYZ[1], - (xyz[2] - smallCornerXYZ[2]) / rangeXYZ[2], - }; - - strOut[0] = normalizedOffset[0] * maxStr[0]; - strOut[1] = normalizedOffset[1] * maxStr[1]; - strOut[2] = normalizedOffset[2] * maxStr[2]; - return true; } @@ -3008,6 +3028,60 @@ BrainOpenGLVolumeTextureSliceDrawing::drawObliqueSliceWithOutlines(const VolumeS glPrioritizeTextures(1, &textureID, &priority); if (debugFlag) std::cout << "Created texture: " << textureID << std::endl; + + if (debugFlag) { + std::vector dims; + volumeInterface->getDimensions(dims); + if (dims.size() >= 3) { + const int64_t maxI((dims[0] > 1) ? dims[0] - 1 : 0); + const int64_t maxJ((dims[1] > 1) ? dims[1] - 1 : 0); + const int64_t maxK((dims[2] > 1) ? dims[2] - 1 : 0); + int64_t corners[8][3] = { + { 0, 0, 0 }, + { maxI, 0, 0 }, + { maxI, maxJ, 0 }, + { 0, maxJ, 0}, + { 0, 0, maxK }, + { maxI, 0, maxK }, + { maxI, maxJ, maxK }, + { 0, maxJ, maxK} + }; + for (int32_t m = 0; m < 8; m++) { + const int64_t i(corners[m][0]); + const int64_t j(corners[m][1]); + const int64_t k(corners[m][2]); + if (volumeInterface->indexValid(i, j, k)) { + float x, y, z; + volumeInterface->indexToSpace(i, j, k, x, y, z); + std::cout << ("IJK = (" + + AString::number(i) + + "," + + AString::number(j) + + "," + + AString::number(k) + + ")") << std::endl; + std::cout << (" XYZ = (" + + AString::number(x) + + ", " + + AString::number(y) + + ", " + + AString::number(z) + + ")") << std::endl; + + std::array str; + std::array xyz { x, y, z }; + getTextureCoordinates(volumeInterface, xyz, maxStr, str); + std::cout << (" STR = (" + + AString::number(str[0]) + + ", " + + AString::number(str[1]) + + ", " + + AString::number(str[2]) + + ")") << std::endl; + } + } + } + } } else { if (debugFlag) std::cout << "Failed to create texture ID" << std::endl; @@ -3121,7 +3195,7 @@ BrainOpenGLVolumeTextureSliceDrawing::processTextureVoxelIdentification(VolumeMa + QString::number(pixels[2]) + ", " + QString::number(pixels[3])); - std::cout << "Pixel ID: " << AString::fromNumbers(pixels, 4, ",") << std::endl; + if (debugFlag) std::cout << "Pixel ID: " << AString::fromNumbers(pixels, 4, ",") << std::endl; uint32_t alphaInt(pixels[3]); if (alphaInt == 255) { @@ -3131,7 +3205,7 @@ BrainOpenGLVolumeTextureSliceDrawing::processTextureVoxelIdentification(VolumeMa uint32_t offset = ((redInt << 16) + (greenInt << 8) + (blueInt)); - std::cout << " Offset: " << offset << std::endl; + if (debugFlag) std::cout << " Offset: " << offset << std::endl; std::vector dims; volumeMappableInterface->getDimensions(dims); @@ -3140,7 +3214,7 @@ BrainOpenGLVolumeTextureSliceDrawing::processTextureVoxelIdentification(VolumeMa const int64_t sliceOffset = offset % sliceSize; const int64_t sliceJ = sliceOffset / dims[0]; const int64_t sliceI = sliceOffset % dims[0]; - std::cout << " Voxel IJK: " << sliceI << ", " << sliceJ << ", " << sliceK << std::endl; + if (debugFlag) std::cout << " Voxel IJK: " << sliceI << ", " << sliceJ << ", " << sliceK << std::endl; const int64_t voxelIndices[3] { sliceI, -- GitLab From 0e916b7edfe63bae7d269e548e3f542eeff9301f Mon Sep 17 00:00:00 2001 From: John Harwell Date: Tue, 18 Jun 2019 14:17:22 -0500 Subject: [PATCH 371/427] For the experimental texture volume drawing, detect a volume that has dimensions too large and avoid creating a texture and then log a warning. --- .../BrainOpenGLVolumeTextureSliceDrawing.cxx | 23 +++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/src/Brain/BrainOpenGLVolumeTextureSliceDrawing.cxx b/src/Brain/BrainOpenGLVolumeTextureSliceDrawing.cxx index ed8d708c4..3ed236325 100644 --- a/src/Brain/BrainOpenGLVolumeTextureSliceDrawing.cxx +++ b/src/Brain/BrainOpenGLVolumeTextureSliceDrawing.cxx @@ -2597,7 +2597,7 @@ BrainOpenGLVolumeTextureSliceDrawing::createVolumeTexture(const VolumeMappableIn * @param maxStrOut * Output with maximum Texture str coordinates * @return - * True if output rgba coloring is valid, else false. + * Valid texture ID (greater than zero) if texture was created, else 0. */ GLuint BrainOpenGLVolumeTextureSliceDrawing::createTextureName(const VolumeMappableInterface* volumeMappableInterface, @@ -2647,6 +2647,23 @@ BrainOpenGLVolumeTextureSliceDrawing::createTextureName(const VolumeMappableInte CaretAssert(0); } + GLint64 maxTextureSize(0); + glGetInteger64v(GL_MAX_3D_TEXTURE_SIZE, + &maxTextureSize); + if (maxTextureSize > 0) { + for (int32_t i = 0; i < static_cast(textureDims.size()); i++) { + if (textureDims[i] > maxTextureSize) { + CaretLogSevere("Texture maximum dimension is " + + AString::number(maxTextureSize) + + ". Volume " + + mapFile->getFileNameNoPath() + + " dimensions = (" + + AString::fromNumbers(textureDims.data(), 3, ", ") + + ")"); + return 0; + } + } + } GLuint textureName(0); glPushClientAttrib(GL_CLIENT_PIXEL_STORE_BIT); @@ -2677,6 +2694,7 @@ BrainOpenGLVolumeTextureSliceDrawing::createTextureName(const VolumeMappableInte * Compression works but appears lossy for palette mapped data * but may be okay for label mapped data */ + m_fixedPipelineDrawing->testForOpenGLError("Before glTexImage3D"); glTexImage3D(GL_TEXTURE_3D, 0, GL_RGBA, //GL_COMPRESSED_RGBA, //GL_RGBA, @@ -2687,7 +2705,8 @@ BrainOpenGLVolumeTextureSliceDrawing::createTextureName(const VolumeMappableInte GL_RGBA, GL_UNSIGNED_BYTE, &rgbaColors[0]); - + m_fixedPipelineDrawing->testForOpenGLError("After glTexImage3D"); + glBindTexture(GL_TEXTURE_3D, 0); glPopClientAttrib(); -- GitLab From 457f4710bcd95ebcbb9740ec43ad4db01161e7e4 Mon Sep 17 00:00:00 2001 From: John Harwell Date: Thu, 20 Jun 2019 10:53:13 -0500 Subject: [PATCH 372/427] Fixed compilation warning. --- src/Files/CiftiMappableDataFile.cxx | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/Files/CiftiMappableDataFile.cxx b/src/Files/CiftiMappableDataFile.cxx index 0265b405d..2ba8541d9 100644 --- a/src/Files/CiftiMappableDataFile.cxx +++ b/src/Files/CiftiMappableDataFile.cxx @@ -3323,9 +3323,6 @@ CiftiMappableDataFile::getVoxelColorsForSliceInMap(const int32_t mapIndex, rgbaOut[rgbaOffset+1] = mapRGBA[dataOffset4+1]; rgbaOut[rgbaOffset+2] = mapRGBA[dataOffset4+2]; uint8_t alpha = mapRGBA[dataOffset4+3]; - if (alpha < 0) { - alpha = 0; - } if (alpha > 0) { if (labelTable != NULL) { -- GitLab From 3c3a50cda0f475a84fb69ea2160b0f828d67833b Mon Sep 17 00:00:00 2001 From: John Harwell Date: Fri, 21 Jun 2019 14:32:17 -0500 Subject: [PATCH 373/427] Show OpenGL Shading Language (GLSL) Version in OpenGL information. --- src/Brain/BrainOpenGL.cxx | 223 +------------------------------------- 1 file changed, 3 insertions(+), 220 deletions(-) diff --git a/src/Brain/BrainOpenGL.cxx b/src/Brain/BrainOpenGL.cxx index 4b881bfa9..b51d38094 100644 --- a/src/Brain/BrainOpenGL.cxx +++ b/src/Brain/BrainOpenGL.cxx @@ -650,226 +650,6 @@ BrainOpenGL::getOpenGLMajorMinorVersions(const AString& versionString, } } -///** -// * Initialize the drawing mode using the most optimal drawing given -// * the compile time and run time constraints. -// */ -//void -//BrainOpenGL::initializeOpenGL() -//{ -// AString compileVersions = "OpenGL Header File Versions Supported: "; -//#ifdef GL_VERSION_1_1 -// compileVersions += " 1.1"; -//#endif -//#ifdef GL_VERSION_1_2 -// compileVersions += " 1.2"; -//#endif -//#ifdef GL_VERSION_1_3 -// compileVersions += " 1.3"; -//#endif -//#ifdef GL_VERSION_1_4 -// compileVersions += " 1.4"; -//#endif -//#ifdef GL_VERSION_1_5 -// compileVersions += " 1.5"; -//#endif -//#ifdef GL_VERSION_2_0 -// compileVersions += " 2.0"; -//#endif -//#ifdef GL_VERSION_2_1 -// compileVersions += " 2.1"; -//#endif -//#ifdef GL_VERSION_3_0 -// compileVersions += " 3.0"; -//#endif -//#ifdef GL_VERSION_3_1 -// compileVersions += " 3.1"; -//#endif -//#ifdef GL_VERSION_3_2 -// compileVersions += " 3.2"; -//#endif -//#ifdef GL_VERSION_3_3 -// compileVersions += " 3.3"; -//#endif -//#ifdef GL_VERSION_4_0 -// compileVersions += " 4.0"; -//#endif -//#ifdef GL_VERSION_4_1 -// compileVersions += " 4.1"; -//#endif -//#ifdef GL_VERSION_4_2 -// compileVersions += " 4.2"; -//#endif -//#ifdef GL_VERSION_4_3 -// compileVersions += " 4.3"; -//#endif -//#ifdef GL_VERSION_4_4 -// compileVersions += " 4.4"; -//#endif -//#ifdef GL_VERSION_4_4 -// compileVersions += " 4.4"; -//#endif -//#ifdef GL_VERSION_4_5 -// compileVersions += " 4.5"; -//#endif -//#ifdef GL_VERSION_5_0 -// compileVersions += " 5.0"; -//#endif -// -//#ifdef GL_OES_VERSION_1_0 -// compileVersions += " ES_1.0"; -//#endif -//#ifdef GL_ES_VERSION_2_0 -// compileVersions += " ES_2.0"; -//#endif -//#ifdef GL_ES_VERSION_3_0 -// compileVersions += " ES_3.0"; -//#endif -// -// s_runtimeLibraryVersionOfOpenGL = QLatin1String(reinterpret_cast(glGetString(GL_VERSION))); -// if (s_runtimeLibraryVersionOfOpenGL.isEmpty()) { -// s_runtimeLibraryVersionOfOpenGL = "1.1"; -// } -// getOpenGLMajorMinorVersions(s_runtimeLibraryVersionOfOpenGL, -// s_runtimeLibraryMajorVersionOfOpenGL, -// s_runtimeLibraryMinorVersionOfOpenGL); -// -// // -// // Note: The version string might be something like 1.2.4. std::atof() -// // will get just the 1.2 which is okay. -// // -// const char* vendorStr = (char*)(glGetString(GL_VENDOR)); -// const char* renderStr = (char*)(glGetString(GL_RENDERER)); -// AString lineInfo = (compileVersions -// + "\nOpenGL Runtime Version: " + s_runtimeLibraryVersionOfOpenGL -// + "\nMajor Runtime Version: " + BrainOpenGL::s_runtimeLibraryMajorVersionOfOpenGL -// + "\nMinor Runtime Version: " + BrainOpenGL::s_runtimeLibraryMinorVersionOfOpenGL -// + "\nOpenGL Vendor: " + AString(vendorStr) -// + "\nOpenGL Renderer: " + AString(renderStr)); -// -// lineInfo += "\n"; -// lineInfo += ("\nFont Renderer: " + m_textRenderer->getName()); -// lineInfo += "\n"; -// -//#ifdef GL_VERSION_2_0 -// if (testForVersionOfOpenGLSupported("2.0")) { -// GLfloat values[2]; -// glGetFloatv (GL_ALIASED_LINE_WIDTH_RANGE, values); -// const AString aliasedLineWidthRange = ("GL_ALIASED_LINE_WIDTH_RANGE value is " -// + AString::fromNumbers(values, 2, ", ")); -// -// glGetFloatv (GL_SMOOTH_LINE_WIDTH_RANGE, values); -// const AString smoothLineWidthRange = ("GL_SMOOTH_LINE_WIDTH_RANGE value is " -// + AString::fromNumbers(values, 2, ", ")); -// -// glGetFloatv (GL_SMOOTH_LINE_WIDTH_GRANULARITY, values); -// const AString smoothLineWidthGranularity = ("GL_SMOOTH_LINE_WIDTH_GRANULARITY value is " -// + AString::number(values[0])); -// -// lineInfo += ("\n" + aliasedLineWidthRange -// + "\n" + smoothLineWidthRange -// + "\n" + smoothLineWidthGranularity); -// } -//#endif // GL_VERSION_2_0 -////#else // GL_VERSION_2_0 -// GLfloat values[2]; -// glGetFloatv (GL_LINE_WIDTH_RANGE, values); -// const AString lineWidthRange = ("GL_LINE_WIDTH_RANGE value is " -// + AString::fromNumbers(values, 2, ", ")); -// -// glGetFloatv (GL_LINE_WIDTH_GRANULARITY, values); -// const AString lineWidthGranularity = ("GL_LINE_WIDTH_GRANULARITY value is " -// + AString::number(values[0])); -// lineInfo += ("\n" + lineWidthRange -// + "\n" + lineWidthGranularity); -////#endif // GL_VERSION_2_0 -// -// float sizes[2]; -// glGetFloatv(GL_POINT_SIZE_RANGE, sizes); -// s_minPointSize = sizes[0]; -// s_maxPointSize = sizes[1]; -// glGetFloatv(GL_LINE_WIDTH_RANGE, sizes); -// s_minLineWidth = sizes[0]; -// s_maxLineWidth = sizes[1]; -// -// s_supportsDisplayLists = false; -// s_supportsImmediateMode = false; -// s_supportsVertexBuffers = false; -// -// GLint maximumNumberOfClipPlanes; -// glGetIntegerv(GL_MAX_CLIP_PLANES, -// & maximumNumberOfClipPlanes); -// lineInfo += ("\n\nMaximum number of clipping planes is " -// + AString::number(maximumNumberOfClipPlanes)); -// -// GLint redBits, greenBits, blueBits, alphaBits; -// glGetIntegerv(GL_RED_BITS, &redBits); -// glGetIntegerv(GL_GREEN_BITS, &greenBits); -// glGetIntegerv(GL_BLUE_BITS, &blueBits); -// glGetIntegerv(GL_ALPHA_BITS, &alphaBits); -// lineInfo += ("\n\nBuffer bits red/green/blue/apha: (" -// + AString::number(redBits) + ", " -// + AString::number(greenBits) + ", " -// + AString::number(blueBits) + ", " -// + AString::number(alphaBits) + ")"); -// -// /* -// * Get the OpenGL Extensions. -// */ -// bool haveARBCompatibility = false; -// const QString extensionsString((char*)glGetString(GL_EXTENSIONS)); -// const QStringList extensionsList = extensionsString.split(QChar(' ')); -// QStringListIterator extensionsIterator(extensionsList); -// AString extInfo = ("\n\nOpenGL Extensions:"); -// while (extensionsIterator.hasNext()) { -// const QString ext = extensionsIterator.next(); -// extInfo += ("\n " + ext); -// -// if (ext == "GL_ARB_compatibility") { -// haveARBCompatibility = true; -// } -// } -// -// if (testForVersionOfOpenGLSupported("3.1")) { -// if (haveARBCompatibility == false) { -// CaretLogSevere("OpenGL 3.1 or later and ARB compatibilty extensions not found.\n" -// "OpenGL may fail."); -// } -// } -// -//#if BRAIN_OPENGL_INFO_SUPPORTS_IMMEDIATE -// s_supportsImmediateMode = true; -//#endif // BRAIN_OPENGL_INFO_SUPPORTS_IMMEDIATE -// -//#if BRAIN_OPENGL_INFO_SUPPORTS_DISPLAY_LISTS -// s_supportsDisplayLists = true; -//#endif // BRAIN_OPENGL_INFO_SUPPORTS_DISPLAY_LISTS -// -//#ifdef BRAIN_OPENGL_INFO_SUPPORTS_VERTEX_BUFFERS -// s_supportsVertexBuffers = true; -//#endif // BRAIN_OPENGL_INFO_SUPPORTS_VERTEX_BUFFERS -// -// lineInfo += ("\n\nBest Drawing Mode: " -// + BrainOpenGL::getBestDrawingModeName()); -// lineInfo += ("\nDisplay Lists Supported: " -// + AString::fromBool(s_supportsDisplayLists)); -// lineInfo += ("\nImmediate Mode Supported: " -// + AString::fromBool(s_supportsImmediateMode)); -// lineInfo += ("\nVertex Buffers Supported: " -// + AString::fromBool(s_supportsVertexBuffers)); -// -// lineInfo += extInfo; -// -// CaretLogConfig(lineInfo); -// -// m_openGLInformation = lineInfo; -// -// /* -// * Call to validate the draw mode selection logic. -// */ -// getBestDrawingMode(); -//} - /** * Initialize the drawing mode using the most optimal drawing given * the compile time and run time constraints. @@ -1043,6 +823,9 @@ BrainOpenGL::getOpenGLInformation() #ifdef GL_VERSION_2_0 if (testForVersionOfOpenGLSupported("2.0")) { + const char* glslVersionString = (char*)glGetString(GL_SHADING_LANGUAGE_VERSION); + lineInfo += ("\nOpenGL Shading Language (GLSL) Version: " + AString(glslVersionString) + "\n"); + GLfloat values[2]; glGetFloatv (GL_ALIASED_LINE_WIDTH_RANGE, values); const AString aliasedLineWidthRange = ("GL_ALIASED_LINE_WIDTH_RANGE value is " -- GitLab From ed1b8cab5f104e153ef2f2768e86796f58cd4ced Mon Sep 17 00:00:00 2001 From: John Harwell Date: Tue, 25 Jun 2019 11:51:32 -0500 Subject: [PATCH 374/427] Added OpenGL Mathematics (GLM) library that will be used in future OpenGL code. From the GLM description: "a header-only C++ mathematics library for graphics software based on the OpenGL Shading Language (GLSL) specifications. An extension system provides extended capabilities such as matrix transformations and quaternions." It also contains functions that replace some of the GLU functions and some that replace OpenGL functions that are in the Fixed Pipeline OpenGL API but not in the Programmable Pipeline OpenGL. On Linux, the CMakeLists.txt file uses the PKG_CHECK_MODULES command to look for a GLM package and, if found, is used instead of the GLM headers in the Workbench source code. --- src/CMakeLists.txt | 31 + src/GLMath/glm/CMakeLists.txt | 66 + src/GLMath/glm/common.hpp | 539 ++++ src/GLMath/glm/detail/_features.hpp | 394 +++ src/GLMath/glm/detail/_fixes.hpp | 27 + src/GLMath/glm/detail/_noise.hpp | 81 + src/GLMath/glm/detail/_swizzle.hpp | 804 +++++ src/GLMath/glm/detail/_swizzle_func.hpp | 682 ++++ src/GLMath/glm/detail/_vectorize.hpp | 123 + src/GLMath/glm/detail/compute_common.hpp | 50 + .../glm/detail/compute_vector_relational.hpp | 30 + src/GLMath/glm/detail/func_common.inl | 787 +++++ src/GLMath/glm/detail/func_common_simd.inl | 231 ++ src/GLMath/glm/detail/func_exponential.inl | 152 + .../glm/detail/func_exponential_simd.inl | 37 + src/GLMath/glm/detail/func_geometric.inl | 243 ++ src/GLMath/glm/detail/func_geometric_simd.inl | 99 + src/GLMath/glm/detail/func_integer.inl | 362 +++ src/GLMath/glm/detail/func_integer_simd.inl | 65 + src/GLMath/glm/detail/func_matrix.inl | 398 +++ src/GLMath/glm/detail/func_matrix_simd.inl | 94 + src/GLMath/glm/detail/func_packing.inl | 189 ++ src/GLMath/glm/detail/func_packing_simd.inl | 6 + src/GLMath/glm/detail/func_trigonometric.inl | 197 ++ .../glm/detail/func_trigonometric_simd.inl | 0 .../glm/detail/func_vector_relational.inl | 87 + .../detail/func_vector_relational_simd.inl | 6 + src/GLMath/glm/detail/glm.cpp | 263 ++ src/GLMath/glm/detail/qualifier.hpp | 210 ++ src/GLMath/glm/detail/setup.hpp | 1086 +++++++ src/GLMath/glm/detail/type_float.hpp | 68 + src/GLMath/glm/detail/type_half.hpp | 16 + src/GLMath/glm/detail/type_half.inl | 241 ++ src/GLMath/glm/detail/type_mat2x2.hpp | 177 ++ src/GLMath/glm/detail/type_mat2x2.inl | 536 ++++ src/GLMath/glm/detail/type_mat2x3.hpp | 159 + src/GLMath/glm/detail/type_mat2x3.inl | 510 +++ src/GLMath/glm/detail/type_mat2x4.hpp | 161 + src/GLMath/glm/detail/type_mat2x4.inl | 520 +++ src/GLMath/glm/detail/type_mat3x2.hpp | 167 + src/GLMath/glm/detail/type_mat3x2.inl | 532 ++++ src/GLMath/glm/detail/type_mat3x3.hpp | 184 ++ src/GLMath/glm/detail/type_mat3x3.inl | 601 ++++ src/GLMath/glm/detail/type_mat3x4.hpp | 166 + src/GLMath/glm/detail/type_mat3x4.inl | 578 ++++ src/GLMath/glm/detail/type_mat4x2.hpp | 171 + src/GLMath/glm/detail/type_mat4x2.inl | 574 ++++ src/GLMath/glm/detail/type_mat4x3.hpp | 171 + src/GLMath/glm/detail/type_mat4x3.inl | 598 ++++ src/GLMath/glm/detail/type_mat4x4.hpp | 189 ++ src/GLMath/glm/detail/type_mat4x4.inl | 706 +++++ src/GLMath/glm/detail/type_mat4x4_simd.inl | 6 + src/GLMath/glm/detail/type_quat.hpp | 190 ++ src/GLMath/glm/detail/type_quat.inl | 379 +++ src/GLMath/glm/detail/type_quat_simd.inl | 188 ++ src/GLMath/glm/detail/type_vec1.hpp | 308 ++ src/GLMath/glm/detail/type_vec1.inl | 551 ++++ src/GLMath/glm/detail/type_vec2.hpp | 399 +++ src/GLMath/glm/detail/type_vec2.inl | 913 ++++++ src/GLMath/glm/detail/type_vec3.hpp | 432 +++ src/GLMath/glm/detail/type_vec3.inl | 1068 +++++++ src/GLMath/glm/detail/type_vec4.hpp | 504 +++ src/GLMath/glm/detail/type_vec4.inl | 1140 +++++++ src/GLMath/glm/detail/type_vec4_simd.inl | 463 +++ src/GLMath/glm/exponential.hpp | 110 + src/GLMath/glm/ext.hpp | 196 ++ src/GLMath/glm/ext/matrix_clip_space.hpp | 522 ++++ src/GLMath/glm/ext/matrix_clip_space.inl | 534 ++++ src/GLMath/glm/ext/matrix_common.hpp | 36 + src/GLMath/glm/ext/matrix_common.inl | 16 + src/GLMath/glm/ext/matrix_double2x2.hpp | 23 + .../glm/ext/matrix_double2x2_precision.hpp | 49 + src/GLMath/glm/ext/matrix_double2x3.hpp | 18 + .../glm/ext/matrix_double2x3_precision.hpp | 31 + src/GLMath/glm/ext/matrix_double2x4.hpp | 18 + .../glm/ext/matrix_double2x4_precision.hpp | 31 + src/GLMath/glm/ext/matrix_double3x2.hpp | 18 + .../glm/ext/matrix_double3x2_precision.hpp | 31 + src/GLMath/glm/ext/matrix_double3x3.hpp | 23 + .../glm/ext/matrix_double3x3_precision.hpp | 49 + src/GLMath/glm/ext/matrix_double3x4.hpp | 18 + .../glm/ext/matrix_double3x4_precision.hpp | 31 + src/GLMath/glm/ext/matrix_double4x2.hpp | 18 + .../glm/ext/matrix_double4x2_precision.hpp | 31 + src/GLMath/glm/ext/matrix_double4x3.hpp | 18 + .../glm/ext/matrix_double4x3_precision.hpp | 31 + src/GLMath/glm/ext/matrix_double4x4.hpp | 23 + .../glm/ext/matrix_double4x4_precision.hpp | 49 + src/GLMath/glm/ext/matrix_float2x2.hpp | 23 + .../glm/ext/matrix_float2x2_precision.hpp | 49 + src/GLMath/glm/ext/matrix_float2x3.hpp | 18 + .../glm/ext/matrix_float2x3_precision.hpp | 31 + src/GLMath/glm/ext/matrix_float2x4.hpp | 18 + .../glm/ext/matrix_float2x4_precision.hpp | 31 + src/GLMath/glm/ext/matrix_float3x2.hpp | 18 + .../glm/ext/matrix_float3x2_precision.hpp | 31 + src/GLMath/glm/ext/matrix_float3x3.hpp | 23 + .../glm/ext/matrix_float3x3_precision.hpp | 49 + src/GLMath/glm/ext/matrix_float3x4.hpp | 18 + .../glm/ext/matrix_float3x4_precision.hpp | 31 + src/GLMath/glm/ext/matrix_float4x2.hpp | 18 + .../glm/ext/matrix_float4x2_precision.hpp | 31 + src/GLMath/glm/ext/matrix_float4x3.hpp | 18 + .../glm/ext/matrix_float4x3_precision.hpp | 31 + src/GLMath/glm/ext/matrix_float4x4.hpp | 23 + .../glm/ext/matrix_float4x4_precision.hpp | 49 + src/GLMath/glm/ext/matrix_projection.hpp | 149 + src/GLMath/glm/ext/matrix_projection.inl | 104 + src/GLMath/glm/ext/matrix_relational.hpp | 132 + src/GLMath/glm/ext/matrix_relational.inl | 82 + src/GLMath/glm/ext/matrix_transform.hpp | 144 + src/GLMath/glm/ext/matrix_transform.inl | 152 + src/GLMath/glm/ext/quaternion_common.hpp | 120 + src/GLMath/glm/ext/quaternion_common.inl | 107 + src/GLMath/glm/ext/quaternion_common_simd.inl | 18 + src/GLMath/glm/ext/quaternion_double.hpp | 39 + .../glm/ext/quaternion_double_precision.hpp | 42 + src/GLMath/glm/ext/quaternion_exponential.hpp | 63 + src/GLMath/glm/ext/quaternion_exponential.inl | 69 + src/GLMath/glm/ext/quaternion_float.hpp | 39 + .../glm/ext/quaternion_float_precision.hpp | 36 + src/GLMath/glm/ext/quaternion_geometric.hpp | 70 + src/GLMath/glm/ext/quaternion_geometric.inl | 36 + src/GLMath/glm/ext/quaternion_relational.hpp | 62 + src/GLMath/glm/ext/quaternion_relational.inl | 35 + src/GLMath/glm/ext/quaternion_transform.hpp | 47 + src/GLMath/glm/ext/quaternion_transform.inl | 24 + .../glm/ext/quaternion_trigonometric.hpp | 63 + .../glm/ext/quaternion_trigonometric.inl | 27 + src/GLMath/glm/ext/scalar_common.hpp | 103 + src/GLMath/glm/ext/scalar_common.inl | 115 + src/GLMath/glm/ext/scalar_constants.hpp | 36 + src/GLMath/glm/ext/scalar_constants.inl | 18 + src/GLMath/glm/ext/scalar_int_sized.hpp | 70 + src/GLMath/glm/ext/scalar_relational.hpp | 65 + src/GLMath/glm/ext/scalar_relational.inl | 43 + src/GLMath/glm/ext/scalar_uint_sized.hpp | 70 + src/GLMath/glm/ext/scalar_ulp.hpp | 74 + src/GLMath/glm/ext/scalar_ulp.inl | 284 ++ src/GLMath/glm/ext/vector_bool1.hpp | 30 + src/GLMath/glm/ext/vector_bool1_precision.hpp | 34 + src/GLMath/glm/ext/vector_bool2.hpp | 18 + src/GLMath/glm/ext/vector_bool2_precision.hpp | 31 + src/GLMath/glm/ext/vector_bool3.hpp | 18 + src/GLMath/glm/ext/vector_bool3_precision.hpp | 31 + src/GLMath/glm/ext/vector_bool4.hpp | 18 + src/GLMath/glm/ext/vector_bool4_precision.hpp | 31 + src/GLMath/glm/ext/vector_common.hpp | 144 + src/GLMath/glm/ext/vector_common.inl | 88 + src/GLMath/glm/ext/vector_double1.hpp | 31 + .../glm/ext/vector_double1_precision.hpp | 36 + src/GLMath/glm/ext/vector_double2.hpp | 18 + .../glm/ext/vector_double2_precision.hpp | 31 + src/GLMath/glm/ext/vector_double3.hpp | 18 + .../glm/ext/vector_double3_precision.hpp | 34 + src/GLMath/glm/ext/vector_double4.hpp | 18 + .../glm/ext/vector_double4_precision.hpp | 35 + src/GLMath/glm/ext/vector_float1.hpp | 31 + .../glm/ext/vector_float1_precision.hpp | 36 + src/GLMath/glm/ext/vector_float2.hpp | 18 + .../glm/ext/vector_float2_precision.hpp | 31 + src/GLMath/glm/ext/vector_float3.hpp | 18 + .../glm/ext/vector_float3_precision.hpp | 31 + src/GLMath/glm/ext/vector_float4.hpp | 18 + .../glm/ext/vector_float4_precision.hpp | 31 + src/GLMath/glm/ext/vector_int1.hpp | 32 + src/GLMath/glm/ext/vector_int1_precision.hpp | 34 + src/GLMath/glm/ext/vector_int2.hpp | 18 + src/GLMath/glm/ext/vector_int2_precision.hpp | 31 + src/GLMath/glm/ext/vector_int3.hpp | 18 + src/GLMath/glm/ext/vector_int3_precision.hpp | 31 + src/GLMath/glm/ext/vector_int4.hpp | 18 + src/GLMath/glm/ext/vector_int4_precision.hpp | 31 + src/GLMath/glm/ext/vector_relational.hpp | 104 + src/GLMath/glm/ext/vector_relational.inl | 75 + src/GLMath/glm/ext/vector_uint1.hpp | 32 + src/GLMath/glm/ext/vector_uint1_precision.hpp | 40 + src/GLMath/glm/ext/vector_uint2.hpp | 18 + src/GLMath/glm/ext/vector_uint2_precision.hpp | 31 + src/GLMath/glm/ext/vector_uint3.hpp | 18 + src/GLMath/glm/ext/vector_uint3_precision.hpp | 31 + src/GLMath/glm/ext/vector_uint4.hpp | 18 + src/GLMath/glm/ext/vector_uint4_precision.hpp | 31 + src/GLMath/glm/ext/vector_ulp.hpp | 109 + src/GLMath/glm/ext/vector_ulp.inl | 74 + src/GLMath/glm/fwd.hpp | 818 +++++ src/GLMath/glm/geometric.hpp | 116 + src/GLMath/glm/glm.hpp | 136 + src/GLMath/glm/gtc/bitfield.hpp | 266 ++ src/GLMath/glm/gtc/bitfield.inl | 626 ++++ src/GLMath/glm/gtc/color_space.hpp | 56 + src/GLMath/glm/gtc/color_space.inl | 84 + src/GLMath/glm/gtc/constants.hpp | 165 + src/GLMath/glm/gtc/constants.inl | 166 + src/GLMath/glm/gtc/epsilon.hpp | 60 + src/GLMath/glm/gtc/epsilon.inl | 80 + src/GLMath/glm/gtc/integer.hpp | 65 + src/GLMath/glm/gtc/integer.inl | 68 + src/GLMath/glm/gtc/matrix_access.hpp | 60 + src/GLMath/glm/gtc/matrix_access.inl | 62 + src/GLMath/glm/gtc/matrix_integer.hpp | 487 +++ src/GLMath/glm/gtc/matrix_inverse.hpp | 50 + src/GLMath/glm/gtc/matrix_inverse.inl | 118 + src/GLMath/glm/gtc/matrix_transform.hpp | 36 + src/GLMath/glm/gtc/matrix_transform.inl | 3 + src/GLMath/glm/gtc/noise.hpp | 61 + src/GLMath/glm/gtc/noise.inl | 807 +++++ src/GLMath/glm/gtc/packing.hpp | 728 +++++ src/GLMath/glm/gtc/packing.inl | 938 ++++++ src/GLMath/glm/gtc/quaternion.hpp | 173 + src/GLMath/glm/gtc/quaternion.inl | 200 ++ src/GLMath/glm/gtc/quaternion_simd.inl | 0 src/GLMath/glm/gtc/random.hpp | 82 + src/GLMath/glm/gtc/random.inl | 303 ++ src/GLMath/glm/gtc/reciprocal.hpp | 135 + src/GLMath/glm/gtc/reciprocal.inl | 191 ++ src/GLMath/glm/gtc/round.hpp | 202 ++ src/GLMath/glm/gtc/round.inl | 343 ++ src/GLMath/glm/gtc/type_aligned.hpp | 1315 ++++++++ src/GLMath/glm/gtc/type_precision.hpp | 2138 +++++++++++++ src/GLMath/glm/gtc/type_precision.inl | 6 + src/GLMath/glm/gtc/type_ptr.hpp | 230 ++ src/GLMath/glm/gtc/type_ptr.inl | 386 +++ src/GLMath/glm/gtc/ulp.hpp | 24 + src/GLMath/glm/gtc/ulp.inl | 3 + src/GLMath/glm/gtc/vec1.hpp | 30 + src/GLMath/glm/gtx/associated_min_max.hpp | 207 ++ src/GLMath/glm/gtx/associated_min_max.inl | 354 +++ src/GLMath/glm/gtx/bit.hpp | 98 + src/GLMath/glm/gtx/bit.inl | 92 + src/GLMath/glm/gtx/closest_point.hpp | 49 + src/GLMath/glm/gtx/closest_point.inl | 45 + src/GLMath/glm/gtx/color_encoding.hpp | 54 + src/GLMath/glm/gtx/color_encoding.inl | 45 + src/GLMath/glm/gtx/color_space.hpp | 72 + src/GLMath/glm/gtx/color_space.inl | 141 + src/GLMath/glm/gtx/color_space_YCoCg.hpp | 60 + src/GLMath/glm/gtx/color_space_YCoCg.inl | 107 + src/GLMath/glm/gtx/common.hpp | 76 + src/GLMath/glm/gtx/common.inl | 125 + src/GLMath/glm/gtx/compatibility.hpp | 133 + src/GLMath/glm/gtx/compatibility.inl | 62 + src/GLMath/glm/gtx/component_wise.hpp | 69 + src/GLMath/glm/gtx/component_wise.inl | 127 + src/GLMath/glm/gtx/dual_quaternion.hpp | 274 ++ src/GLMath/glm/gtx/dual_quaternion.inl | 352 +++ src/GLMath/glm/gtx/easing.hpp | 219 ++ src/GLMath/glm/gtx/easing.inl | 436 +++ src/GLMath/glm/gtx/euler_angles.hpp | 335 ++ src/GLMath/glm/gtx/euler_angles.inl | 899 ++++++ src/GLMath/glm/gtx/extend.hpp | 42 + src/GLMath/glm/gtx/extend.inl | 48 + src/GLMath/glm/gtx/extended_min_max.hpp | 182 ++ src/GLMath/glm/gtx/extended_min_max.inl | 218 ++ src/GLMath/glm/gtx/exterior_product.hpp | 45 + src/GLMath/glm/gtx/exterior_product.inl | 26 + src/GLMath/glm/gtx/fast_exponential.hpp | 95 + src/GLMath/glm/gtx/fast_exponential.inl | 136 + src/GLMath/glm/gtx/fast_square_root.hpp | 92 + src/GLMath/glm/gtx/fast_square_root.inl | 80 + src/GLMath/glm/gtx/fast_trigonometry.hpp | 79 + src/GLMath/glm/gtx/fast_trigonometry.inl | 142 + src/GLMath/glm/gtx/float_notmalize.inl | 13 + src/GLMath/glm/gtx/functions.hpp | 56 + src/GLMath/glm/gtx/functions.inl | 30 + src/GLMath/glm/gtx/gradient_paint.hpp | 53 + src/GLMath/glm/gtx/gradient_paint.inl | 36 + .../glm/gtx/handed_coordinate_space.hpp | 50 + .../glm/gtx/handed_coordinate_space.inl | 26 + src/GLMath/glm/gtx/hash.hpp | 142 + src/GLMath/glm/gtx/hash.inl | 184 ++ src/GLMath/glm/gtx/integer.hpp | 76 + src/GLMath/glm/gtx/integer.inl | 185 ++ src/GLMath/glm/gtx/intersect.hpp | 92 + src/GLMath/glm/gtx/intersect.inl | 197 ++ src/GLMath/glm/gtx/io.hpp | 201 ++ src/GLMath/glm/gtx/io.inl | 440 +++ src/GLMath/glm/gtx/log_base.hpp | 48 + src/GLMath/glm/gtx/log_base.inl | 16 + src/GLMath/glm/gtx/matrix_cross_product.hpp | 47 + src/GLMath/glm/gtx/matrix_cross_product.inl | 37 + src/GLMath/glm/gtx/matrix_decompose.hpp | 46 + src/GLMath/glm/gtx/matrix_decompose.inl | 186 ++ src/GLMath/glm/gtx/matrix_factorisation.hpp | 69 + src/GLMath/glm/gtx/matrix_factorisation.inl | 84 + src/GLMath/glm/gtx/matrix_interpolation.hpp | 60 + src/GLMath/glm/gtx/matrix_interpolation.inl | 129 + src/GLMath/glm/gtx/matrix_major_storage.hpp | 119 + src/GLMath/glm/gtx/matrix_major_storage.inl | 166 + src/GLMath/glm/gtx/matrix_operation.hpp | 103 + src/GLMath/glm/gtx/matrix_operation.inl | 176 ++ src/GLMath/glm/gtx/matrix_query.hpp | 77 + src/GLMath/glm/gtx/matrix_query.inl | 113 + src/GLMath/glm/gtx/matrix_transform_2d.hpp | 81 + src/GLMath/glm/gtx/matrix_transform_2d.inl | 68 + src/GLMath/glm/gtx/mixed_product.hpp | 41 + src/GLMath/glm/gtx/mixed_product.inl | 15 + src/GLMath/glm/gtx/norm.hpp | 76 + src/GLMath/glm/gtx/norm.inl | 83 + src/GLMath/glm/gtx/normal.hpp | 41 + src/GLMath/glm/gtx/normal.inl | 15 + src/GLMath/glm/gtx/normalize_dot.hpp | 49 + src/GLMath/glm/gtx/normalize_dot.inl | 16 + src/GLMath/glm/gtx/number_precision.hpp | 61 + src/GLMath/glm/gtx/number_precision.inl | 6 + src/GLMath/glm/gtx/optimum_pow.hpp | 54 + src/GLMath/glm/gtx/optimum_pow.inl | 22 + src/GLMath/glm/gtx/orthonormalize.hpp | 49 + src/GLMath/glm/gtx/orthonormalize.inl | 29 + src/GLMath/glm/gtx/perpendicular.hpp | 41 + src/GLMath/glm/gtx/perpendicular.inl | 10 + src/GLMath/glm/gtx/polar_coordinates.hpp | 48 + src/GLMath/glm/gtx/polar_coordinates.inl | 36 + src/GLMath/glm/gtx/projection.hpp | 43 + src/GLMath/glm/gtx/projection.inl | 10 + src/GLMath/glm/gtx/quaternion.hpp | 174 ++ src/GLMath/glm/gtx/quaternion.inl | 159 + src/GLMath/glm/gtx/range.hpp | 98 + src/GLMath/glm/gtx/raw_data.hpp | 51 + src/GLMath/glm/gtx/raw_data.inl | 2 + src/GLMath/glm/gtx/rotate_normalized_axis.hpp | 68 + src/GLMath/glm/gtx/rotate_normalized_axis.inl | 58 + src/GLMath/glm/gtx/rotate_vector.hpp | 123 + src/GLMath/glm/gtx/rotate_vector.inl | 187 ++ src/GLMath/glm/gtx/scalar_multiplication.hpp | 75 + src/GLMath/glm/gtx/scalar_relational.hpp | 36 + src/GLMath/glm/gtx/scalar_relational.inl | 88 + src/GLMath/glm/gtx/spline.hpp | 65 + src/GLMath/glm/gtx/spline.inl | 60 + src/GLMath/glm/gtx/std_based_type.hpp | 68 + src/GLMath/glm/gtx/std_based_type.inl | 6 + src/GLMath/glm/gtx/string_cast.hpp | 52 + src/GLMath/glm/gtx/string_cast.inl | 492 +++ src/GLMath/glm/gtx/texture.hpp | 46 + src/GLMath/glm/gtx/texture.inl | 17 + src/GLMath/glm/gtx/transform.hpp | 60 + src/GLMath/glm/gtx/transform.inl | 23 + src/GLMath/glm/gtx/transform2.hpp | 89 + src/GLMath/glm/gtx/transform2.inl | 125 + src/GLMath/glm/gtx/type_aligned.hpp | 982 ++++++ src/GLMath/glm/gtx/type_aligned.inl | 6 + src/GLMath/glm/gtx/type_trait.hpp | 85 + src/GLMath/glm/gtx/type_trait.inl | 61 + src/GLMath/glm/gtx/vec_swizzle.hpp | 2782 +++++++++++++++++ src/GLMath/glm/gtx/vector_angle.hpp | 57 + src/GLMath/glm/gtx/vector_angle.inl | 44 + src/GLMath/glm/gtx/vector_query.hpp | 66 + src/GLMath/glm/gtx/vector_query.inl | 154 + src/GLMath/glm/gtx/wrap.hpp | 55 + src/GLMath/glm/gtx/wrap.inl | 57 + src/GLMath/glm/integer.hpp | 212 ++ src/GLMath/glm/mat2x2.hpp | 9 + src/GLMath/glm/mat2x3.hpp | 9 + src/GLMath/glm/mat2x4.hpp | 9 + src/GLMath/glm/mat3x2.hpp | 9 + src/GLMath/glm/mat3x3.hpp | 8 + src/GLMath/glm/mat3x4.hpp | 8 + src/GLMath/glm/mat4x2.hpp | 9 + src/GLMath/glm/mat4x3.hpp | 8 + src/GLMath/glm/mat4x4.hpp | 9 + src/GLMath/glm/matrix.hpp | 161 + src/GLMath/glm/packing.hpp | 173 + src/GLMath/glm/simd/common.h | 240 ++ src/GLMath/glm/simd/exponential.h | 20 + src/GLMath/glm/simd/geometric.h | 124 + src/GLMath/glm/simd/integer.h | 115 + src/GLMath/glm/simd/matrix.h | 1028 ++++++ src/GLMath/glm/simd/packing.h | 8 + src/GLMath/glm/simd/platform.h | 366 +++ src/GLMath/glm/simd/trigonometric.h | 9 + src/GLMath/glm/simd/vector_relational.h | 8 + src/GLMath/glm/trigonometric.hpp | 210 ++ src/GLMath/glm/vec2.hpp | 14 + src/GLMath/glm/vec3.hpp | 14 + src/GLMath/glm/vec4.hpp | 15 + src/GLMath/glm/vector_relational.hpp | 121 + 376 files changed, 58620 insertions(+) create mode 100644 src/GLMath/glm/CMakeLists.txt create mode 100644 src/GLMath/glm/common.hpp create mode 100644 src/GLMath/glm/detail/_features.hpp create mode 100644 src/GLMath/glm/detail/_fixes.hpp create mode 100644 src/GLMath/glm/detail/_noise.hpp create mode 100644 src/GLMath/glm/detail/_swizzle.hpp create mode 100644 src/GLMath/glm/detail/_swizzle_func.hpp create mode 100644 src/GLMath/glm/detail/_vectorize.hpp create mode 100644 src/GLMath/glm/detail/compute_common.hpp create mode 100644 src/GLMath/glm/detail/compute_vector_relational.hpp create mode 100644 src/GLMath/glm/detail/func_common.inl create mode 100644 src/GLMath/glm/detail/func_common_simd.inl create mode 100644 src/GLMath/glm/detail/func_exponential.inl create mode 100644 src/GLMath/glm/detail/func_exponential_simd.inl create mode 100644 src/GLMath/glm/detail/func_geometric.inl create mode 100644 src/GLMath/glm/detail/func_geometric_simd.inl create mode 100644 src/GLMath/glm/detail/func_integer.inl create mode 100644 src/GLMath/glm/detail/func_integer_simd.inl create mode 100644 src/GLMath/glm/detail/func_matrix.inl create mode 100644 src/GLMath/glm/detail/func_matrix_simd.inl create mode 100644 src/GLMath/glm/detail/func_packing.inl create mode 100644 src/GLMath/glm/detail/func_packing_simd.inl create mode 100644 src/GLMath/glm/detail/func_trigonometric.inl create mode 100644 src/GLMath/glm/detail/func_trigonometric_simd.inl create mode 100644 src/GLMath/glm/detail/func_vector_relational.inl create mode 100644 src/GLMath/glm/detail/func_vector_relational_simd.inl create mode 100644 src/GLMath/glm/detail/glm.cpp create mode 100644 src/GLMath/glm/detail/qualifier.hpp create mode 100644 src/GLMath/glm/detail/setup.hpp create mode 100644 src/GLMath/glm/detail/type_float.hpp create mode 100644 src/GLMath/glm/detail/type_half.hpp create mode 100644 src/GLMath/glm/detail/type_half.inl create mode 100644 src/GLMath/glm/detail/type_mat2x2.hpp create mode 100644 src/GLMath/glm/detail/type_mat2x2.inl create mode 100644 src/GLMath/glm/detail/type_mat2x3.hpp create mode 100644 src/GLMath/glm/detail/type_mat2x3.inl create mode 100644 src/GLMath/glm/detail/type_mat2x4.hpp create mode 100644 src/GLMath/glm/detail/type_mat2x4.inl create mode 100644 src/GLMath/glm/detail/type_mat3x2.hpp create mode 100644 src/GLMath/glm/detail/type_mat3x2.inl create mode 100644 src/GLMath/glm/detail/type_mat3x3.hpp create mode 100644 src/GLMath/glm/detail/type_mat3x3.inl create mode 100644 src/GLMath/glm/detail/type_mat3x4.hpp create mode 100644 src/GLMath/glm/detail/type_mat3x4.inl create mode 100644 src/GLMath/glm/detail/type_mat4x2.hpp create mode 100644 src/GLMath/glm/detail/type_mat4x2.inl create mode 100644 src/GLMath/glm/detail/type_mat4x3.hpp create mode 100644 src/GLMath/glm/detail/type_mat4x3.inl create mode 100644 src/GLMath/glm/detail/type_mat4x4.hpp create mode 100644 src/GLMath/glm/detail/type_mat4x4.inl create mode 100644 src/GLMath/glm/detail/type_mat4x4_simd.inl create mode 100644 src/GLMath/glm/detail/type_quat.hpp create mode 100644 src/GLMath/glm/detail/type_quat.inl create mode 100644 src/GLMath/glm/detail/type_quat_simd.inl create mode 100644 src/GLMath/glm/detail/type_vec1.hpp create mode 100644 src/GLMath/glm/detail/type_vec1.inl create mode 100644 src/GLMath/glm/detail/type_vec2.hpp create mode 100644 src/GLMath/glm/detail/type_vec2.inl create mode 100644 src/GLMath/glm/detail/type_vec3.hpp create mode 100644 src/GLMath/glm/detail/type_vec3.inl create mode 100644 src/GLMath/glm/detail/type_vec4.hpp create mode 100644 src/GLMath/glm/detail/type_vec4.inl create mode 100644 src/GLMath/glm/detail/type_vec4_simd.inl create mode 100644 src/GLMath/glm/exponential.hpp create mode 100644 src/GLMath/glm/ext.hpp create mode 100644 src/GLMath/glm/ext/matrix_clip_space.hpp create mode 100644 src/GLMath/glm/ext/matrix_clip_space.inl create mode 100644 src/GLMath/glm/ext/matrix_common.hpp create mode 100644 src/GLMath/glm/ext/matrix_common.inl create mode 100644 src/GLMath/glm/ext/matrix_double2x2.hpp create mode 100644 src/GLMath/glm/ext/matrix_double2x2_precision.hpp create mode 100644 src/GLMath/glm/ext/matrix_double2x3.hpp create mode 100644 src/GLMath/glm/ext/matrix_double2x3_precision.hpp create mode 100644 src/GLMath/glm/ext/matrix_double2x4.hpp create mode 100644 src/GLMath/glm/ext/matrix_double2x4_precision.hpp create mode 100644 src/GLMath/glm/ext/matrix_double3x2.hpp create mode 100644 src/GLMath/glm/ext/matrix_double3x2_precision.hpp create mode 100644 src/GLMath/glm/ext/matrix_double3x3.hpp create mode 100644 src/GLMath/glm/ext/matrix_double3x3_precision.hpp create mode 100644 src/GLMath/glm/ext/matrix_double3x4.hpp create mode 100644 src/GLMath/glm/ext/matrix_double3x4_precision.hpp create mode 100644 src/GLMath/glm/ext/matrix_double4x2.hpp create mode 100644 src/GLMath/glm/ext/matrix_double4x2_precision.hpp create mode 100644 src/GLMath/glm/ext/matrix_double4x3.hpp create mode 100644 src/GLMath/glm/ext/matrix_double4x3_precision.hpp create mode 100644 src/GLMath/glm/ext/matrix_double4x4.hpp create mode 100644 src/GLMath/glm/ext/matrix_double4x4_precision.hpp create mode 100644 src/GLMath/glm/ext/matrix_float2x2.hpp create mode 100644 src/GLMath/glm/ext/matrix_float2x2_precision.hpp create mode 100644 src/GLMath/glm/ext/matrix_float2x3.hpp create mode 100644 src/GLMath/glm/ext/matrix_float2x3_precision.hpp create mode 100644 src/GLMath/glm/ext/matrix_float2x4.hpp create mode 100644 src/GLMath/glm/ext/matrix_float2x4_precision.hpp create mode 100644 src/GLMath/glm/ext/matrix_float3x2.hpp create mode 100644 src/GLMath/glm/ext/matrix_float3x2_precision.hpp create mode 100644 src/GLMath/glm/ext/matrix_float3x3.hpp create mode 100644 src/GLMath/glm/ext/matrix_float3x3_precision.hpp create mode 100644 src/GLMath/glm/ext/matrix_float3x4.hpp create mode 100644 src/GLMath/glm/ext/matrix_float3x4_precision.hpp create mode 100644 src/GLMath/glm/ext/matrix_float4x2.hpp create mode 100644 src/GLMath/glm/ext/matrix_float4x2_precision.hpp create mode 100644 src/GLMath/glm/ext/matrix_float4x3.hpp create mode 100644 src/GLMath/glm/ext/matrix_float4x3_precision.hpp create mode 100644 src/GLMath/glm/ext/matrix_float4x4.hpp create mode 100644 src/GLMath/glm/ext/matrix_float4x4_precision.hpp create mode 100644 src/GLMath/glm/ext/matrix_projection.hpp create mode 100644 src/GLMath/glm/ext/matrix_projection.inl create mode 100644 src/GLMath/glm/ext/matrix_relational.hpp create mode 100644 src/GLMath/glm/ext/matrix_relational.inl create mode 100644 src/GLMath/glm/ext/matrix_transform.hpp create mode 100644 src/GLMath/glm/ext/matrix_transform.inl create mode 100644 src/GLMath/glm/ext/quaternion_common.hpp create mode 100644 src/GLMath/glm/ext/quaternion_common.inl create mode 100644 src/GLMath/glm/ext/quaternion_common_simd.inl create mode 100644 src/GLMath/glm/ext/quaternion_double.hpp create mode 100644 src/GLMath/glm/ext/quaternion_double_precision.hpp create mode 100644 src/GLMath/glm/ext/quaternion_exponential.hpp create mode 100644 src/GLMath/glm/ext/quaternion_exponential.inl create mode 100644 src/GLMath/glm/ext/quaternion_float.hpp create mode 100644 src/GLMath/glm/ext/quaternion_float_precision.hpp create mode 100644 src/GLMath/glm/ext/quaternion_geometric.hpp create mode 100644 src/GLMath/glm/ext/quaternion_geometric.inl create mode 100644 src/GLMath/glm/ext/quaternion_relational.hpp create mode 100644 src/GLMath/glm/ext/quaternion_relational.inl create mode 100644 src/GLMath/glm/ext/quaternion_transform.hpp create mode 100644 src/GLMath/glm/ext/quaternion_transform.inl create mode 100644 src/GLMath/glm/ext/quaternion_trigonometric.hpp create mode 100644 src/GLMath/glm/ext/quaternion_trigonometric.inl create mode 100644 src/GLMath/glm/ext/scalar_common.hpp create mode 100644 src/GLMath/glm/ext/scalar_common.inl create mode 100644 src/GLMath/glm/ext/scalar_constants.hpp create mode 100644 src/GLMath/glm/ext/scalar_constants.inl create mode 100644 src/GLMath/glm/ext/scalar_int_sized.hpp create mode 100644 src/GLMath/glm/ext/scalar_relational.hpp create mode 100644 src/GLMath/glm/ext/scalar_relational.inl create mode 100644 src/GLMath/glm/ext/scalar_uint_sized.hpp create mode 100644 src/GLMath/glm/ext/scalar_ulp.hpp create mode 100644 src/GLMath/glm/ext/scalar_ulp.inl create mode 100644 src/GLMath/glm/ext/vector_bool1.hpp create mode 100644 src/GLMath/glm/ext/vector_bool1_precision.hpp create mode 100644 src/GLMath/glm/ext/vector_bool2.hpp create mode 100644 src/GLMath/glm/ext/vector_bool2_precision.hpp create mode 100644 src/GLMath/glm/ext/vector_bool3.hpp create mode 100644 src/GLMath/glm/ext/vector_bool3_precision.hpp create mode 100644 src/GLMath/glm/ext/vector_bool4.hpp create mode 100644 src/GLMath/glm/ext/vector_bool4_precision.hpp create mode 100644 src/GLMath/glm/ext/vector_common.hpp create mode 100644 src/GLMath/glm/ext/vector_common.inl create mode 100644 src/GLMath/glm/ext/vector_double1.hpp create mode 100644 src/GLMath/glm/ext/vector_double1_precision.hpp create mode 100644 src/GLMath/glm/ext/vector_double2.hpp create mode 100644 src/GLMath/glm/ext/vector_double2_precision.hpp create mode 100644 src/GLMath/glm/ext/vector_double3.hpp create mode 100644 src/GLMath/glm/ext/vector_double3_precision.hpp create mode 100644 src/GLMath/glm/ext/vector_double4.hpp create mode 100644 src/GLMath/glm/ext/vector_double4_precision.hpp create mode 100644 src/GLMath/glm/ext/vector_float1.hpp create mode 100644 src/GLMath/glm/ext/vector_float1_precision.hpp create mode 100644 src/GLMath/glm/ext/vector_float2.hpp create mode 100644 src/GLMath/glm/ext/vector_float2_precision.hpp create mode 100644 src/GLMath/glm/ext/vector_float3.hpp create mode 100644 src/GLMath/glm/ext/vector_float3_precision.hpp create mode 100644 src/GLMath/glm/ext/vector_float4.hpp create mode 100644 src/GLMath/glm/ext/vector_float4_precision.hpp create mode 100644 src/GLMath/glm/ext/vector_int1.hpp create mode 100644 src/GLMath/glm/ext/vector_int1_precision.hpp create mode 100644 src/GLMath/glm/ext/vector_int2.hpp create mode 100644 src/GLMath/glm/ext/vector_int2_precision.hpp create mode 100644 src/GLMath/glm/ext/vector_int3.hpp create mode 100644 src/GLMath/glm/ext/vector_int3_precision.hpp create mode 100644 src/GLMath/glm/ext/vector_int4.hpp create mode 100644 src/GLMath/glm/ext/vector_int4_precision.hpp create mode 100644 src/GLMath/glm/ext/vector_relational.hpp create mode 100644 src/GLMath/glm/ext/vector_relational.inl create mode 100644 src/GLMath/glm/ext/vector_uint1.hpp create mode 100644 src/GLMath/glm/ext/vector_uint1_precision.hpp create mode 100644 src/GLMath/glm/ext/vector_uint2.hpp create mode 100644 src/GLMath/glm/ext/vector_uint2_precision.hpp create mode 100644 src/GLMath/glm/ext/vector_uint3.hpp create mode 100644 src/GLMath/glm/ext/vector_uint3_precision.hpp create mode 100644 src/GLMath/glm/ext/vector_uint4.hpp create mode 100644 src/GLMath/glm/ext/vector_uint4_precision.hpp create mode 100644 src/GLMath/glm/ext/vector_ulp.hpp create mode 100644 src/GLMath/glm/ext/vector_ulp.inl create mode 100644 src/GLMath/glm/fwd.hpp create mode 100644 src/GLMath/glm/geometric.hpp create mode 100644 src/GLMath/glm/glm.hpp create mode 100644 src/GLMath/glm/gtc/bitfield.hpp create mode 100644 src/GLMath/glm/gtc/bitfield.inl create mode 100644 src/GLMath/glm/gtc/color_space.hpp create mode 100644 src/GLMath/glm/gtc/color_space.inl create mode 100644 src/GLMath/glm/gtc/constants.hpp create mode 100644 src/GLMath/glm/gtc/constants.inl create mode 100644 src/GLMath/glm/gtc/epsilon.hpp create mode 100644 src/GLMath/glm/gtc/epsilon.inl create mode 100644 src/GLMath/glm/gtc/integer.hpp create mode 100644 src/GLMath/glm/gtc/integer.inl create mode 100644 src/GLMath/glm/gtc/matrix_access.hpp create mode 100644 src/GLMath/glm/gtc/matrix_access.inl create mode 100644 src/GLMath/glm/gtc/matrix_integer.hpp create mode 100644 src/GLMath/glm/gtc/matrix_inverse.hpp create mode 100644 src/GLMath/glm/gtc/matrix_inverse.inl create mode 100644 src/GLMath/glm/gtc/matrix_transform.hpp create mode 100644 src/GLMath/glm/gtc/matrix_transform.inl create mode 100644 src/GLMath/glm/gtc/noise.hpp create mode 100644 src/GLMath/glm/gtc/noise.inl create mode 100644 src/GLMath/glm/gtc/packing.hpp create mode 100644 src/GLMath/glm/gtc/packing.inl create mode 100644 src/GLMath/glm/gtc/quaternion.hpp create mode 100644 src/GLMath/glm/gtc/quaternion.inl create mode 100644 src/GLMath/glm/gtc/quaternion_simd.inl create mode 100644 src/GLMath/glm/gtc/random.hpp create mode 100644 src/GLMath/glm/gtc/random.inl create mode 100644 src/GLMath/glm/gtc/reciprocal.hpp create mode 100644 src/GLMath/glm/gtc/reciprocal.inl create mode 100644 src/GLMath/glm/gtc/round.hpp create mode 100644 src/GLMath/glm/gtc/round.inl create mode 100644 src/GLMath/glm/gtc/type_aligned.hpp create mode 100644 src/GLMath/glm/gtc/type_precision.hpp create mode 100644 src/GLMath/glm/gtc/type_precision.inl create mode 100644 src/GLMath/glm/gtc/type_ptr.hpp create mode 100644 src/GLMath/glm/gtc/type_ptr.inl create mode 100644 src/GLMath/glm/gtc/ulp.hpp create mode 100644 src/GLMath/glm/gtc/ulp.inl create mode 100644 src/GLMath/glm/gtc/vec1.hpp create mode 100644 src/GLMath/glm/gtx/associated_min_max.hpp create mode 100644 src/GLMath/glm/gtx/associated_min_max.inl create mode 100644 src/GLMath/glm/gtx/bit.hpp create mode 100644 src/GLMath/glm/gtx/bit.inl create mode 100644 src/GLMath/glm/gtx/closest_point.hpp create mode 100644 src/GLMath/glm/gtx/closest_point.inl create mode 100644 src/GLMath/glm/gtx/color_encoding.hpp create mode 100644 src/GLMath/glm/gtx/color_encoding.inl create mode 100644 src/GLMath/glm/gtx/color_space.hpp create mode 100644 src/GLMath/glm/gtx/color_space.inl create mode 100644 src/GLMath/glm/gtx/color_space_YCoCg.hpp create mode 100644 src/GLMath/glm/gtx/color_space_YCoCg.inl create mode 100644 src/GLMath/glm/gtx/common.hpp create mode 100644 src/GLMath/glm/gtx/common.inl create mode 100644 src/GLMath/glm/gtx/compatibility.hpp create mode 100644 src/GLMath/glm/gtx/compatibility.inl create mode 100644 src/GLMath/glm/gtx/component_wise.hpp create mode 100644 src/GLMath/glm/gtx/component_wise.inl create mode 100644 src/GLMath/glm/gtx/dual_quaternion.hpp create mode 100644 src/GLMath/glm/gtx/dual_quaternion.inl create mode 100644 src/GLMath/glm/gtx/easing.hpp create mode 100644 src/GLMath/glm/gtx/easing.inl create mode 100644 src/GLMath/glm/gtx/euler_angles.hpp create mode 100644 src/GLMath/glm/gtx/euler_angles.inl create mode 100644 src/GLMath/glm/gtx/extend.hpp create mode 100644 src/GLMath/glm/gtx/extend.inl create mode 100644 src/GLMath/glm/gtx/extended_min_max.hpp create mode 100644 src/GLMath/glm/gtx/extended_min_max.inl create mode 100644 src/GLMath/glm/gtx/exterior_product.hpp create mode 100644 src/GLMath/glm/gtx/exterior_product.inl create mode 100644 src/GLMath/glm/gtx/fast_exponential.hpp create mode 100644 src/GLMath/glm/gtx/fast_exponential.inl create mode 100644 src/GLMath/glm/gtx/fast_square_root.hpp create mode 100644 src/GLMath/glm/gtx/fast_square_root.inl create mode 100644 src/GLMath/glm/gtx/fast_trigonometry.hpp create mode 100644 src/GLMath/glm/gtx/fast_trigonometry.inl create mode 100644 src/GLMath/glm/gtx/float_notmalize.inl create mode 100644 src/GLMath/glm/gtx/functions.hpp create mode 100644 src/GLMath/glm/gtx/functions.inl create mode 100644 src/GLMath/glm/gtx/gradient_paint.hpp create mode 100644 src/GLMath/glm/gtx/gradient_paint.inl create mode 100644 src/GLMath/glm/gtx/handed_coordinate_space.hpp create mode 100644 src/GLMath/glm/gtx/handed_coordinate_space.inl create mode 100644 src/GLMath/glm/gtx/hash.hpp create mode 100644 src/GLMath/glm/gtx/hash.inl create mode 100644 src/GLMath/glm/gtx/integer.hpp create mode 100644 src/GLMath/glm/gtx/integer.inl create mode 100644 src/GLMath/glm/gtx/intersect.hpp create mode 100644 src/GLMath/glm/gtx/intersect.inl create mode 100644 src/GLMath/glm/gtx/io.hpp create mode 100644 src/GLMath/glm/gtx/io.inl create mode 100644 src/GLMath/glm/gtx/log_base.hpp create mode 100644 src/GLMath/glm/gtx/log_base.inl create mode 100644 src/GLMath/glm/gtx/matrix_cross_product.hpp create mode 100644 src/GLMath/glm/gtx/matrix_cross_product.inl create mode 100644 src/GLMath/glm/gtx/matrix_decompose.hpp create mode 100644 src/GLMath/glm/gtx/matrix_decompose.inl create mode 100644 src/GLMath/glm/gtx/matrix_factorisation.hpp create mode 100644 src/GLMath/glm/gtx/matrix_factorisation.inl create mode 100644 src/GLMath/glm/gtx/matrix_interpolation.hpp create mode 100644 src/GLMath/glm/gtx/matrix_interpolation.inl create mode 100644 src/GLMath/glm/gtx/matrix_major_storage.hpp create mode 100644 src/GLMath/glm/gtx/matrix_major_storage.inl create mode 100644 src/GLMath/glm/gtx/matrix_operation.hpp create mode 100644 src/GLMath/glm/gtx/matrix_operation.inl create mode 100644 src/GLMath/glm/gtx/matrix_query.hpp create mode 100644 src/GLMath/glm/gtx/matrix_query.inl create mode 100644 src/GLMath/glm/gtx/matrix_transform_2d.hpp create mode 100644 src/GLMath/glm/gtx/matrix_transform_2d.inl create mode 100644 src/GLMath/glm/gtx/mixed_product.hpp create mode 100644 src/GLMath/glm/gtx/mixed_product.inl create mode 100644 src/GLMath/glm/gtx/norm.hpp create mode 100644 src/GLMath/glm/gtx/norm.inl create mode 100644 src/GLMath/glm/gtx/normal.hpp create mode 100644 src/GLMath/glm/gtx/normal.inl create mode 100644 src/GLMath/glm/gtx/normalize_dot.hpp create mode 100644 src/GLMath/glm/gtx/normalize_dot.inl create mode 100644 src/GLMath/glm/gtx/number_precision.hpp create mode 100644 src/GLMath/glm/gtx/number_precision.inl create mode 100644 src/GLMath/glm/gtx/optimum_pow.hpp create mode 100644 src/GLMath/glm/gtx/optimum_pow.inl create mode 100644 src/GLMath/glm/gtx/orthonormalize.hpp create mode 100644 src/GLMath/glm/gtx/orthonormalize.inl create mode 100644 src/GLMath/glm/gtx/perpendicular.hpp create mode 100644 src/GLMath/glm/gtx/perpendicular.inl create mode 100644 src/GLMath/glm/gtx/polar_coordinates.hpp create mode 100644 src/GLMath/glm/gtx/polar_coordinates.inl create mode 100644 src/GLMath/glm/gtx/projection.hpp create mode 100644 src/GLMath/glm/gtx/projection.inl create mode 100644 src/GLMath/glm/gtx/quaternion.hpp create mode 100644 src/GLMath/glm/gtx/quaternion.inl create mode 100644 src/GLMath/glm/gtx/range.hpp create mode 100644 src/GLMath/glm/gtx/raw_data.hpp create mode 100644 src/GLMath/glm/gtx/raw_data.inl create mode 100644 src/GLMath/glm/gtx/rotate_normalized_axis.hpp create mode 100644 src/GLMath/glm/gtx/rotate_normalized_axis.inl create mode 100644 src/GLMath/glm/gtx/rotate_vector.hpp create mode 100644 src/GLMath/glm/gtx/rotate_vector.inl create mode 100644 src/GLMath/glm/gtx/scalar_multiplication.hpp create mode 100644 src/GLMath/glm/gtx/scalar_relational.hpp create mode 100644 src/GLMath/glm/gtx/scalar_relational.inl create mode 100644 src/GLMath/glm/gtx/spline.hpp create mode 100644 src/GLMath/glm/gtx/spline.inl create mode 100644 src/GLMath/glm/gtx/std_based_type.hpp create mode 100644 src/GLMath/glm/gtx/std_based_type.inl create mode 100644 src/GLMath/glm/gtx/string_cast.hpp create mode 100644 src/GLMath/glm/gtx/string_cast.inl create mode 100644 src/GLMath/glm/gtx/texture.hpp create mode 100644 src/GLMath/glm/gtx/texture.inl create mode 100644 src/GLMath/glm/gtx/transform.hpp create mode 100644 src/GLMath/glm/gtx/transform.inl create mode 100644 src/GLMath/glm/gtx/transform2.hpp create mode 100644 src/GLMath/glm/gtx/transform2.inl create mode 100644 src/GLMath/glm/gtx/type_aligned.hpp create mode 100644 src/GLMath/glm/gtx/type_aligned.inl create mode 100644 src/GLMath/glm/gtx/type_trait.hpp create mode 100644 src/GLMath/glm/gtx/type_trait.inl create mode 100644 src/GLMath/glm/gtx/vec_swizzle.hpp create mode 100644 src/GLMath/glm/gtx/vector_angle.hpp create mode 100644 src/GLMath/glm/gtx/vector_angle.inl create mode 100644 src/GLMath/glm/gtx/vector_query.hpp create mode 100644 src/GLMath/glm/gtx/vector_query.inl create mode 100644 src/GLMath/glm/gtx/wrap.hpp create mode 100644 src/GLMath/glm/gtx/wrap.inl create mode 100644 src/GLMath/glm/integer.hpp create mode 100644 src/GLMath/glm/mat2x2.hpp create mode 100644 src/GLMath/glm/mat2x3.hpp create mode 100644 src/GLMath/glm/mat2x4.hpp create mode 100644 src/GLMath/glm/mat3x2.hpp create mode 100644 src/GLMath/glm/mat3x3.hpp create mode 100644 src/GLMath/glm/mat3x4.hpp create mode 100644 src/GLMath/glm/mat4x2.hpp create mode 100644 src/GLMath/glm/mat4x3.hpp create mode 100644 src/GLMath/glm/mat4x4.hpp create mode 100644 src/GLMath/glm/matrix.hpp create mode 100644 src/GLMath/glm/packing.hpp create mode 100644 src/GLMath/glm/simd/common.h create mode 100644 src/GLMath/glm/simd/exponential.h create mode 100644 src/GLMath/glm/simd/geometric.h create mode 100644 src/GLMath/glm/simd/integer.h create mode 100644 src/GLMath/glm/simd/matrix.h create mode 100644 src/GLMath/glm/simd/packing.h create mode 100644 src/GLMath/glm/simd/platform.h create mode 100644 src/GLMath/glm/simd/trigonometric.h create mode 100644 src/GLMath/glm/simd/vector_relational.h create mode 100644 src/GLMath/glm/trigonometric.hpp create mode 100644 src/GLMath/glm/vec2.hpp create mode 100644 src/GLMath/glm/vec3.hpp create mode 100644 src/GLMath/glm/vec4.hpp create mode 100644 src/GLMath/glm/vector_relational.hpp diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 786742615..f7c03d9b0 100755 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -245,6 +245,35 @@ ELSE (Qwt_FOUND) SET(QWT_RESULT "In Workbench Source Code") ENDIF (Qwt_FOUND) +########################################################################################## +# +# glm Math Library is header filess only containing C++ templates and functions. +# Since there is no code to link, only need the path to the include files +# https://glm.g-truc.net/0.9.9/index.html +# +SET(GLMATH_RESULT "Not Found") +IF (NOT WIN32) + PKG_CHECK_MODULES(GLMath glm) +ENDIF (NOT WIN32) +IF (GLMath_FOUND) + SET(GLMATH_RESULT "Using OpenGL Math directory ${GLMath_INCLUDE_DIRS}") + INCLUDE_DIRECTORIES("${GLMath_INCLUDE_DIRS}") +ELSE (GLMath_FOUND) + SET(GLMATH_RESULT "In Workbench Source Code") + INCLUDE_DIRECTORIES("${CMAKE_SOURCE_DIR}/GLMath") +ENDIF (GLMath_FOUND) + + +########################################################################################## +# +# Can Use OpenCL +# +SET(OPENCL_RESULT "Not found (OK, unused at this time)") +FIND_PACKAGE(OpenCL) +IF (OpenCL_FOUND) + SET(OPENCL_RESULT "Version: ${OpenCL_VERSION_STRING}") +ENDIF (OpenCL_FOUND) + ########################################################################################## # # Need OpenGL @@ -589,6 +618,8 @@ IF(WB_CMAKE_VERBOSE_OUTPUT_FLAG) MESSAGE(${MSG_MODE} ${MSG_INDENT} "FTGL: " ${FTGL_RESULT}) MESSAGE(${MSG_MODE} ${MSG_INDENT} "FreeType: " ${FREETYPE_RESULT}) MESSAGE(${MSG_MODE} ${MSG_INDENT} "GLEW: " ${GLEW_RESULT}) + MESSAGE(${MSG_MODE} ${MSG_INDENT} "GLMath: " ${GLMATH_RESULT}) + MESSAGE(${MSG_MODE} ${MSG_INDENT} "OpenCL: " ${OPENCL_RESULT}) MESSAGE(${MSG_MODE} ${MSG_INDENT} "OpenGL: " ${OPENGL_RESULT}) MESSAGE(${MSG_MODE} ${MSG_INDENT} "OpenMP: " ${OPENMP_RESULT}) MESSAGE(${MSG_MODE} ${MSG_INDENT} "OpenSSL: " ${OPENSSL_RESULT}) diff --git a/src/GLMath/glm/CMakeLists.txt b/src/GLMath/glm/CMakeLists.txt new file mode 100644 index 000000000..032340d47 --- /dev/null +++ b/src/GLMath/glm/CMakeLists.txt @@ -0,0 +1,66 @@ +file(GLOB ROOT_SOURCE *.cpp) +file(GLOB ROOT_INLINE *.inl) +file(GLOB ROOT_HEADER *.hpp) +file(GLOB ROOT_TEXT ../*.txt) +file(GLOB ROOT_MD ../*.md) +file(GLOB ROOT_NAT ../util/glm.natvis) + +file(GLOB_RECURSE CORE_SOURCE ./detail/*.cpp) +file(GLOB_RECURSE CORE_INLINE ./detail/*.inl) +file(GLOB_RECURSE CORE_HEADER ./detail/*.hpp) + +file(GLOB_RECURSE EXT_SOURCE ./ext/*.cpp) +file(GLOB_RECURSE EXT_INLINE ./ext/*.inl) +file(GLOB_RECURSE EXT_HEADER ./ext/*.hpp) + +file(GLOB_RECURSE GTC_SOURCE ./gtc/*.cpp) +file(GLOB_RECURSE GTC_INLINE ./gtc/*.inl) +file(GLOB_RECURSE GTC_HEADER ./gtc/*.hpp) + +file(GLOB_RECURSE GTX_SOURCE ./gtx/*.cpp) +file(GLOB_RECURSE GTX_INLINE ./gtx/*.inl) +file(GLOB_RECURSE GTX_HEADER ./gtx/*.hpp) + +file(GLOB_RECURSE SIMD_SOURCE ./simd/*.cpp) +file(GLOB_RECURSE SIMD_INLINE ./simd/*.inl) +file(GLOB_RECURSE SIMD_HEADER ./simd/*.h) + +source_group("Text Files" FILES ${ROOT_TEXT} ${ROOT_MD}) +source_group("Core Files" FILES ${CORE_SOURCE}) +source_group("Core Files" FILES ${CORE_INLINE}) +source_group("Core Files" FILES ${CORE_HEADER}) +source_group("EXT Files" FILES ${EXT_SOURCE}) +source_group("EXT Files" FILES ${EXT_INLINE}) +source_group("EXT Files" FILES ${EXT_HEADER}) +source_group("GTC Files" FILES ${GTC_SOURCE}) +source_group("GTC Files" FILES ${GTC_INLINE}) +source_group("GTC Files" FILES ${GTC_HEADER}) +source_group("GTX Files" FILES ${GTX_SOURCE}) +source_group("GTX Files" FILES ${GTX_INLINE}) +source_group("GTX Files" FILES ${GTX_HEADER}) +source_group("SIMD Files" FILES ${SIMD_SOURCE}) +source_group("SIMD Files" FILES ${SIMD_INLINE}) +source_group("SIMD Files" FILES ${SIMD_HEADER}) + +include_directories(${CMAKE_CURRENT_SOURCE_DIR}/..) + +if(BUILD_STATIC_LIBS) +add_library(glm_static STATIC ${ROOT_TEXT} ${ROOT_MD} ${ROOT_NAT} + ${ROOT_SOURCE} ${ROOT_INLINE} ${ROOT_HEADER} + ${CORE_SOURCE} ${CORE_INLINE} ${CORE_HEADER} + ${EXT_SOURCE} ${EXT_INLINE} ${EXT_HEADER} + ${GTC_SOURCE} ${GTC_INLINE} ${GTC_HEADER} + ${GTX_SOURCE} ${GTX_INLINE} ${GTX_HEADER} + ${SIMD_SOURCE} ${SIMD_INLINE} ${SIMD_HEADER}) +endif() + +if(BUILD_SHARED_LIBS) +add_library(glm_shared SHARED ${ROOT_TEXT} ${ROOT_MD} ${ROOT_NAT} + ${ROOT_SOURCE} ${ROOT_INLINE} ${ROOT_HEADER} + ${CORE_SOURCE} ${CORE_INLINE} ${CORE_HEADER} + ${EXT_SOURCE} ${EXT_INLINE} ${EXT_HEADER} + ${GTC_SOURCE} ${GTC_INLINE} ${GTC_HEADER} + ${GTX_SOURCE} ${GTX_INLINE} ${GTX_HEADER} + ${SIMD_SOURCE} ${SIMD_INLINE} ${SIMD_HEADER}) +endif() + diff --git a/src/GLMath/glm/common.hpp b/src/GLMath/glm/common.hpp new file mode 100644 index 000000000..1f923aac2 --- /dev/null +++ b/src/GLMath/glm/common.hpp @@ -0,0 +1,539 @@ +/// @ref core +/// @file glm/common.hpp +/// +/// @see GLSL 4.20.8 specification, section 8.3 Common Functions +/// +/// @defgroup core_func_common Common functions +/// @ingroup core +/// +/// Provides GLSL common functions +/// +/// These all operate component-wise. The description is per component. +/// +/// Include to use these core features. + +#pragma once + +#include "detail/qualifier.hpp" +#include "detail/_fixes.hpp" + +namespace glm +{ + /// @addtogroup core_func_common + /// @{ + + /// Returns x if x >= 0; otherwise, it returns -x. + /// + /// @tparam genType floating-point or signed integer; scalar or vector types. + /// + /// @see GLSL abs man page + /// @see GLSL 4.20.8 specification, section 8.3 Common Functions + template + GLM_FUNC_DECL GLM_CONSTEXPR genType abs(genType x); + + /// Returns x if x >= 0; otherwise, it returns -x. + /// + /// @tparam L Integer between 1 and 4 included that qualify the dimension of the vector + /// @tparam T Floating-point or signed integer scalar types + /// @tparam Q Value from qualifier enum + /// + /// @see GLSL abs man page + /// @see GLSL 4.20.8 specification, section 8.3 Common Functions + template + GLM_FUNC_DECL GLM_CONSTEXPR vec abs(vec const& x); + + /// Returns 1.0 if x > 0, 0.0 if x == 0, or -1.0 if x < 0. + /// + /// @tparam L Integer between 1 and 4 included that qualify the dimension of the vector + /// @tparam T Floating-point scalar types + /// @tparam Q Value from qualifier enum + /// + /// @see GLSL sign man page + /// @see GLSL 4.20.8 specification, section 8.3 Common Functions + template + GLM_FUNC_DECL vec sign(vec const& x); + + /// Returns a value equal to the nearest integer that is less then or equal to x. + /// + /// @tparam L Integer between 1 and 4 included that qualify the dimension of the vector + /// @tparam T Floating-point scalar types + /// @tparam Q Value from qualifier enum + /// + /// @see GLSL floor man page + /// @see GLSL 4.20.8 specification, section 8.3 Common Functions + template + GLM_FUNC_DECL vec floor(vec const& x); + + /// Returns a value equal to the nearest integer to x + /// whose absolute value is not larger than the absolute value of x. + /// + /// @tparam L Integer between 1 and 4 included that qualify the dimension of the vector + /// @tparam T Floating-point scalar types + /// @tparam Q Value from qualifier enum + /// + /// @see GLSL trunc man page + /// @see GLSL 4.20.8 specification, section 8.3 Common Functions + template + GLM_FUNC_DECL vec trunc(vec const& x); + + /// Returns a value equal to the nearest integer to x. + /// The fraction 0.5 will round in a direction chosen by the + /// implementation, presumably the direction that is fastest. + /// This includes the possibility that round(x) returns the + /// same value as roundEven(x) for all values of x. + /// + /// @tparam L Integer between 1 and 4 included that qualify the dimension of the vector + /// @tparam T Floating-point scalar types + /// @tparam Q Value from qualifier enum + /// + /// @see GLSL round man page + /// @see GLSL 4.20.8 specification, section 8.3 Common Functions + template + GLM_FUNC_DECL vec round(vec const& x); + + /// Returns a value equal to the nearest integer to x. + /// A fractional part of 0.5 will round toward the nearest even + /// integer. (Both 3.5 and 4.5 for x will return 4.0.) + /// + /// @tparam L Integer between 1 and 4 included that qualify the dimension of the vector + /// @tparam T Floating-point scalar types + /// @tparam Q Value from qualifier enum + /// + /// @see GLSL roundEven man page + /// @see GLSL 4.20.8 specification, section 8.3 Common Functions + /// @see New round to even technique + template + GLM_FUNC_DECL vec roundEven(vec const& x); + + /// Returns a value equal to the nearest integer + /// that is greater than or equal to x. + /// + /// @tparam L Integer between 1 and 4 included that qualify the dimension of the vector + /// @tparam T Floating-point scalar types + /// @tparam Q Value from qualifier enum + /// + /// @see GLSL ceil man page + /// @see GLSL 4.20.8 specification, section 8.3 Common Functions + template + GLM_FUNC_DECL vec ceil(vec const& x); + + /// Return x - floor(x). + /// + /// @tparam genType Floating-point scalar or vector types. + /// + /// @see GLSL fract man page + /// @see GLSL 4.20.8 specification, section 8.3 Common Functions + template + GLM_FUNC_DECL genType fract(genType x); + + /// Return x - floor(x). + /// + /// @tparam L Integer between 1 and 4 included that qualify the dimension of the vector + /// @tparam T Floating-point scalar types + /// @tparam Q Value from qualifier enum + /// + /// @see GLSL fract man page + /// @see GLSL 4.20.8 specification, section 8.3 Common Functions + template + GLM_FUNC_DECL vec fract(vec const& x); + + template + GLM_FUNC_DECL genType mod(genType x, genType y); + + template + GLM_FUNC_DECL vec mod(vec const& x, T y); + + /// Modulus. Returns x - y * floor(x / y) + /// for each component in x using the floating point value y. + /// + /// @tparam L Integer between 1 and 4 included that qualify the dimension of the vector + /// @tparam T Floating-point scalar types, include glm/gtc/integer for integer scalar types support + /// @tparam Q Value from qualifier enum + /// + /// @see GLSL mod man page + /// @see GLSL 4.20.8 specification, section 8.3 Common Functions + template + GLM_FUNC_DECL vec mod(vec const& x, vec const& y); + + /// Returns the fractional part of x and sets i to the integer + /// part (as a whole number floating point value). Both the + /// return value and the output parameter will have the same + /// sign as x. + /// + /// @tparam genType Floating-point scalar or vector types. + /// + /// @see GLSL modf man page + /// @see GLSL 4.20.8 specification, section 8.3 Common Functions + template + GLM_FUNC_DECL genType modf(genType x, genType& i); + + /// Returns y if y < x; otherwise, it returns x. + /// + /// @tparam genType Floating-point or integer; scalar or vector types. + /// + /// @see GLSL min man page + /// @see GLSL 4.20.8 specification, section 8.3 Common Functions + template + GLM_FUNC_DECL GLM_CONSTEXPR genType min(genType x, genType y); + + /// Returns y if y < x; otherwise, it returns x. + /// + /// @tparam L Integer between 1 and 4 included that qualify the dimension of the vector + /// @tparam T Floating-point or integer scalar types + /// @tparam Q Value from qualifier enum + /// + /// @see GLSL min man page + /// @see GLSL 4.20.8 specification, section 8.3 Common Functions + template + GLM_FUNC_DECL GLM_CONSTEXPR vec min(vec const& x, T y); + + /// Returns y if y < x; otherwise, it returns x. + /// + /// @tparam L Integer between 1 and 4 included that qualify the dimension of the vector + /// @tparam T Floating-point or integer scalar types + /// @tparam Q Value from qualifier enum + /// + /// @see GLSL min man page + /// @see GLSL 4.20.8 specification, section 8.3 Common Functions + template + GLM_FUNC_DECL GLM_CONSTEXPR vec min(vec const& x, vec const& y); + + /// Returns y if x < y; otherwise, it returns x. + /// + /// @tparam genType Floating-point or integer; scalar or vector types. + /// + /// @see GLSL max man page + /// @see GLSL 4.20.8 specification, section 8.3 Common Functions + template + GLM_FUNC_DECL GLM_CONSTEXPR genType max(genType x, genType y); + + /// Returns y if x < y; otherwise, it returns x. + /// + /// @tparam L Integer between 1 and 4 included that qualify the dimension of the vector + /// @tparam T Floating-point or integer scalar types + /// @tparam Q Value from qualifier enum + /// + /// @see GLSL max man page + /// @see GLSL 4.20.8 specification, section 8.3 Common Functions + template + GLM_FUNC_DECL GLM_CONSTEXPR vec max(vec const& x, T y); + + /// Returns y if x < y; otherwise, it returns x. + /// + /// @tparam L Integer between 1 and 4 included that qualify the dimension of the vector + /// @tparam T Floating-point or integer scalar types + /// @tparam Q Value from qualifier enum + /// + /// @see GLSL max man page + /// @see GLSL 4.20.8 specification, section 8.3 Common Functions + template + GLM_FUNC_DECL GLM_CONSTEXPR vec max(vec const& x, vec const& y); + + /// Returns min(max(x, minVal), maxVal) for each component in x + /// using the floating-point values minVal and maxVal. + /// + /// @tparam genType Floating-point or integer; scalar or vector types. + /// + /// @see GLSL clamp man page + /// @see GLSL 4.20.8 specification, section 8.3 Common Functions + template + GLM_FUNC_DECL GLM_CONSTEXPR genType clamp(genType x, genType minVal, genType maxVal); + + /// Returns min(max(x, minVal), maxVal) for each component in x + /// using the floating-point values minVal and maxVal. + /// + /// @tparam L Integer between 1 and 4 included that qualify the dimension of the vector + /// @tparam T Floating-point or integer scalar types + /// @tparam Q Value from qualifier enum + /// + /// @see GLSL clamp man page + /// @see GLSL 4.20.8 specification, section 8.3 Common Functions + template + GLM_FUNC_DECL GLM_CONSTEXPR vec clamp(vec const& x, T minVal, T maxVal); + + /// Returns min(max(x, minVal), maxVal) for each component in x + /// using the floating-point values minVal and maxVal. + /// + /// @tparam L Integer between 1 and 4 included that qualify the dimension of the vector + /// @tparam T Floating-point or integer scalar types + /// @tparam Q Value from qualifier enum + /// + /// @see GLSL clamp man page + /// @see GLSL 4.20.8 specification, section 8.3 Common Functions + template + GLM_FUNC_DECL GLM_CONSTEXPR vec clamp(vec const& x, vec const& minVal, vec const& maxVal); + + /// If genTypeU is a floating scalar or vector: + /// Returns x * (1.0 - a) + y * a, i.e., the linear blend of + /// x and y using the floating-point value a. + /// The value for a is not restricted to the range [0, 1]. + /// + /// If genTypeU is a boolean scalar or vector: + /// Selects which vector each returned component comes + /// from. For a component of 'a' that is false, the + /// corresponding component of 'x' is returned. For a + /// component of 'a' that is true, the corresponding + /// component of 'y' is returned. Components of 'x' and 'y' that + /// are not selected are allowed to be invalid floating point + /// values and will have no effect on the results. Thus, this + /// provides different functionality than + /// genType mix(genType x, genType y, genType(a)) + /// where a is a Boolean vector. + /// + /// @see GLSL mix man page + /// @see GLSL 4.20.8 specification, section 8.3 Common Functions + /// + /// @param[in] x Value to interpolate. + /// @param[in] y Value to interpolate. + /// @param[in] a Interpolant. + /// + /// @tparam genTypeT Floating point scalar or vector. + /// @tparam genTypeU Floating point or boolean scalar or vector. It can't be a vector if it is the length of genTypeT. + /// + /// @code + /// #include + /// ... + /// float a; + /// bool b; + /// glm::dvec3 e; + /// glm::dvec3 f; + /// glm::vec4 g; + /// glm::vec4 h; + /// ... + /// glm::vec4 r = glm::mix(g, h, a); // Interpolate with a floating-point scalar two vectors. + /// glm::vec4 s = glm::mix(g, h, b); // Returns g or h; + /// glm::dvec3 t = glm::mix(e, f, a); // Types of the third parameter is not required to match with the first and the second. + /// glm::vec4 u = glm::mix(g, h, r); // Interpolations can be perform per component with a vector for the last parameter. + /// @endcode + template + GLM_FUNC_DECL genTypeT mix(genTypeT x, genTypeT y, genTypeU a); + + template + GLM_FUNC_DECL vec mix(vec const& x, vec const& y, vec const& a); + + template + GLM_FUNC_DECL vec mix(vec const& x, vec const& y, U a); + + /// Returns 0.0 if x < edge, otherwise it returns 1.0 for each component of a genType. + /// + /// @see GLSL step man page + /// @see GLSL 4.20.8 specification, section 8.3 Common Functions + template + GLM_FUNC_DECL genType step(genType edge, genType x); + + /// Returns 0.0 if x < edge, otherwise it returns 1.0. + /// + /// @tparam L Integer between 1 and 4 included that qualify the dimension of the vector + /// @tparam T Floating-point scalar types + /// @tparam Q Value from qualifier enum + /// + /// @see GLSL step man page + /// @see GLSL 4.20.8 specification, section 8.3 Common Functions + template + GLM_FUNC_DECL vec step(T edge, vec const& x); + + /// Returns 0.0 if x < edge, otherwise it returns 1.0. + /// + /// @tparam L Integer between 1 and 4 included that qualify the dimension of the vector + /// @tparam T Floating-point scalar types + /// @tparam Q Value from qualifier enum + /// + /// @see GLSL step man page + /// @see GLSL 4.20.8 specification, section 8.3 Common Functions + template + GLM_FUNC_DECL vec step(vec const& edge, vec const& x); + + /// Returns 0.0 if x <= edge0 and 1.0 if x >= edge1 and + /// performs smooth Hermite interpolation between 0 and 1 + /// when edge0 < x < edge1. This is useful in cases where + /// you would want a threshold function with a smooth + /// transition. This is equivalent to: + /// genType t; + /// t = clamp ((x - edge0) / (edge1 - edge0), 0, 1); + /// return t * t * (3 - 2 * t); + /// Results are undefined if edge0 >= edge1. + /// + /// @tparam genType Floating-point scalar or vector types. + /// + /// @see GLSL smoothstep man page + /// @see GLSL 4.20.8 specification, section 8.3 Common Functions + template + GLM_FUNC_DECL genType smoothstep(genType edge0, genType edge1, genType x); + + template + GLM_FUNC_DECL vec smoothstep(T edge0, T edge1, vec const& x); + + template + GLM_FUNC_DECL vec smoothstep(vec const& edge0, vec const& edge1, vec const& x); + + /// Returns true if x holds a NaN (not a number) + /// representation in the underlying implementation's set of + /// floating point representations. Returns false otherwise, + /// including for implementations with no NaN + /// representations. + /// + /// /!\ When using compiler fast math, this function may fail. + /// + /// @tparam L Integer between 1 and 4 included that qualify the dimension of the vector + /// @tparam T Floating-point scalar types + /// @tparam Q Value from qualifier enum + /// + /// @see GLSL isnan man page + /// @see GLSL 4.20.8 specification, section 8.3 Common Functions + template + GLM_FUNC_DECL vec isnan(vec const& x); + + /// Returns true if x holds a positive infinity or negative + /// infinity representation in the underlying implementation's + /// set of floating point representations. Returns false + /// otherwise, including for implementations with no infinity + /// representations. + /// + /// @tparam L Integer between 1 and 4 included that qualify the dimension of the vector + /// @tparam T Floating-point scalar types + /// @tparam Q Value from qualifier enum + /// + /// @see GLSL isinf man page + /// @see GLSL 4.20.8 specification, section 8.3 Common Functions + template + GLM_FUNC_DECL vec isinf(vec const& x); + + /// Returns a signed integer value representing + /// the encoding of a floating-point value. The floating-point + /// value's bit-level representation is preserved. + /// + /// @see GLSL floatBitsToInt man page + /// @see GLSL 4.20.8 specification, section 8.3 Common Functions + GLM_FUNC_DECL int floatBitsToInt(float const& v); + + /// Returns a signed integer value representing + /// the encoding of a floating-point value. The floatingpoint + /// value's bit-level representation is preserved. + /// + /// @tparam L Integer between 1 and 4 included that qualify the dimension of the vector + /// @tparam Q Value from qualifier enum + /// + /// @see GLSL floatBitsToInt man page + /// @see GLSL 4.20.8 specification, section 8.3 Common Functions + template + GLM_FUNC_DECL vec floatBitsToInt(vec const& v); + + /// Returns a unsigned integer value representing + /// the encoding of a floating-point value. The floatingpoint + /// value's bit-level representation is preserved. + /// + /// @see GLSL floatBitsToUint man page + /// @see GLSL 4.20.8 specification, section 8.3 Common Functions + GLM_FUNC_DECL uint floatBitsToUint(float const& v); + + /// Returns a unsigned integer value representing + /// the encoding of a floating-point value. The floatingpoint + /// value's bit-level representation is preserved. + /// + /// @tparam L Integer between 1 and 4 included that qualify the dimension of the vector + /// @tparam Q Value from qualifier enum + /// + /// @see GLSL floatBitsToUint man page + /// @see GLSL 4.20.8 specification, section 8.3 Common Functions + template + GLM_FUNC_DECL vec floatBitsToUint(vec const& v); + + /// Returns a floating-point value corresponding to a signed + /// integer encoding of a floating-point value. + /// If an inf or NaN is passed in, it will not signal, and the + /// resulting floating point value is unspecified. Otherwise, + /// the bit-level representation is preserved. + /// + /// @see GLSL intBitsToFloat man page + /// @see GLSL 4.20.8 specification, section 8.3 Common Functions + GLM_FUNC_DECL float intBitsToFloat(int const& v); + + /// Returns a floating-point value corresponding to a signed + /// integer encoding of a floating-point value. + /// If an inf or NaN is passed in, it will not signal, and the + /// resulting floating point value is unspecified. Otherwise, + /// the bit-level representation is preserved. + /// + /// @tparam L Integer between 1 and 4 included that qualify the dimension of the vector + /// @tparam Q Value from qualifier enum + /// + /// @see GLSL intBitsToFloat man page + /// @see GLSL 4.20.8 specification, section 8.3 Common Functions + template + GLM_FUNC_DECL vec intBitsToFloat(vec const& v); + + /// Returns a floating-point value corresponding to a + /// unsigned integer encoding of a floating-point value. + /// If an inf or NaN is passed in, it will not signal, and the + /// resulting floating point value is unspecified. Otherwise, + /// the bit-level representation is preserved. + /// + /// @see GLSL uintBitsToFloat man page + /// @see GLSL 4.20.8 specification, section 8.3 Common Functions + GLM_FUNC_DECL float uintBitsToFloat(uint const& v); + + /// Returns a floating-point value corresponding to a + /// unsigned integer encoding of a floating-point value. + /// If an inf or NaN is passed in, it will not signal, and the + /// resulting floating point value is unspecified. Otherwise, + /// the bit-level representation is preserved. + /// + /// @tparam L Integer between 1 and 4 included that qualify the dimension of the vector + /// @tparam Q Value from qualifier enum + /// + /// @see GLSL uintBitsToFloat man page + /// @see GLSL 4.20.8 specification, section 8.3 Common Functions + template + GLM_FUNC_DECL vec uintBitsToFloat(vec const& v); + + /// Computes and returns a * b + c. + /// + /// @tparam genType Floating-point scalar or vector types. + /// + /// @see GLSL fma man page + /// @see GLSL 4.20.8 specification, section 8.3 Common Functions + template + GLM_FUNC_DECL genType fma(genType const& a, genType const& b, genType const& c); + + /// Splits x into a floating-point significand in the range + /// [0.5, 1.0) and an integral exponent of two, such that: + /// x = significand * exp(2, exponent) + /// + /// The significand is returned by the function and the + /// exponent is returned in the parameter exp. For a + /// floating-point value of zero, the significant and exponent + /// are both zero. For a floating-point value that is an + /// infinity or is not a number, the results are undefined. + /// + /// @tparam genType Floating-point scalar or vector types. + /// + /// @see GLSL frexp man page + /// @see GLSL 4.20.8 specification, section 8.3 Common Functions + template + GLM_FUNC_DECL genType frexp(genType const& x, genIType& exp); + + template + GLM_FUNC_DECL vec frexp(vec const& v, vec& exp); + + /// Builds a floating-point number from x and the + /// corresponding integral exponent of two in exp, returning: + /// significand * exp(2, exponent) + /// + /// If this product is too large to be represented in the + /// floating-point type, the result is undefined. + /// + /// @tparam genType Floating-point scalar or vector types. + /// + /// @see GLSL ldexp man page; + /// @see GLSL 4.20.8 specification, section 8.3 Common Functions + template + GLM_FUNC_DECL genType ldexp(genType const& x, genIType const& exp); + + template + GLM_FUNC_DECL vec ldexp(vec const& v, vec const& exp); + + /// @} +}//namespace glm + +#include "detail/func_common.inl" + diff --git a/src/GLMath/glm/detail/_features.hpp b/src/GLMath/glm/detail/_features.hpp new file mode 100644 index 000000000..b0cbe9ff0 --- /dev/null +++ b/src/GLMath/glm/detail/_features.hpp @@ -0,0 +1,394 @@ +#pragma once + +// #define GLM_CXX98_EXCEPTIONS +// #define GLM_CXX98_RTTI + +// #define GLM_CXX11_RVALUE_REFERENCES +// Rvalue references - GCC 4.3 +// http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2006/n2118.html + +// GLM_CXX11_TRAILING_RETURN +// Rvalue references for *this - GCC not supported +// http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2007/n2439.htm + +// GLM_CXX11_NONSTATIC_MEMBER_INIT +// Initialization of class objects by rvalues - GCC any +// http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2004/n1610.html + +// GLM_CXX11_NONSTATIC_MEMBER_INIT +// Non-static data member initializers - GCC 4.7 +// http://www.open-std.org/JTC1/SC22/WG21/docs/papers/2008/n2756.htm + +// #define GLM_CXX11_VARIADIC_TEMPLATE +// Variadic templates - GCC 4.3 +// http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2007/n2242.pdf + +// +// Extending variadic template template parameters - GCC 4.4 +// http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2008/n2555.pdf + +// #define GLM_CXX11_GENERALIZED_INITIALIZERS +// Initializer lists - GCC 4.4 +// http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2008/n2672.htm + +// #define GLM_CXX11_STATIC_ASSERT +// Static assertions - GCC 4.3 +// http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2004/n1720.html + +// #define GLM_CXX11_AUTO_TYPE +// auto-typed variables - GCC 4.4 +// http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2006/n1984.pdf + +// #define GLM_CXX11_AUTO_TYPE +// Multi-declarator auto - GCC 4.4 +// http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2004/n1737.pdf + +// #define GLM_CXX11_AUTO_TYPE +// Removal of auto as a storage-class specifier - GCC 4.4 +// http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2008/n2546.htm + +// #define GLM_CXX11_AUTO_TYPE +// New function declarator syntax - GCC 4.4 +// http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2008/n2541.htm + +// #define GLM_CXX11_LAMBDAS +// New wording for C++0x lambdas - GCC 4.5 +// http://www.open-std.org/JTC1/SC22/WG21/docs/papers/2009/n2927.pdf + +// #define GLM_CXX11_DECLTYPE +// Declared type of an expression - GCC 4.3 +// http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2007/n2343.pdf + +// +// Right angle brackets - GCC 4.3 +// http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2005/n1757.html + +// +// Default template arguments for function templates DR226 GCC 4.3 +// http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_defects.html#226 + +// +// Solving the SFINAE problem for expressions DR339 GCC 4.4 +// http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2008/n2634.html + +// #define GLM_CXX11_ALIAS_TEMPLATE +// Template aliases N2258 GCC 4.7 +// http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2007/n2258.pdf + +// +// Extern templates N1987 Yes +// http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2006/n1987.htm + +// #define GLM_CXX11_NULLPTR +// Null pointer constant N2431 GCC 4.6 +// http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2007/n2431.pdf + +// #define GLM_CXX11_STRONG_ENUMS +// Strongly-typed enums N2347 GCC 4.4 +// http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2007/n2347.pdf + +// +// Forward declarations for enums N2764 GCC 4.6 +// http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2008/n2764.pdf + +// +// Generalized attributes N2761 GCC 4.8 +// http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2008/n2761.pdf + +// +// Generalized constant expressions N2235 GCC 4.6 +// http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2007/n2235.pdf + +// +// Alignment support N2341 GCC 4.8 +// http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2007/n2341.pdf + +// #define GLM_CXX11_DELEGATING_CONSTRUCTORS +// Delegating constructors N1986 GCC 4.7 +// http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2006/n1986.pdf + +// +// Inheriting constructors N2540 GCC 4.8 +// http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2008/n2540.htm + +// #define GLM_CXX11_EXPLICIT_CONVERSIONS +// Explicit conversion operators N2437 GCC 4.5 +// http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2007/n2437.pdf + +// +// New character types N2249 GCC 4.4 +// http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2007/n2249.html + +// +// Unicode string literals N2442 GCC 4.5 +// http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2007/n2442.htm + +// +// Raw string literals N2442 GCC 4.5 +// http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2007/n2442.htm + +// +// Universal character name literals N2170 GCC 4.5 +// http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2007/n2170.html + +// #define GLM_CXX11_USER_LITERALS +// User-defined literals N2765 GCC 4.7 +// http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2008/n2765.pdf + +// +// Standard Layout Types N2342 GCC 4.5 +// http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2007/n2342.htm + +// #define GLM_CXX11_DEFAULTED_FUNCTIONS +// #define GLM_CXX11_DELETED_FUNCTIONS +// Defaulted and deleted functions N2346 GCC 4.4 +// http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2007/n2346.htm + +// +// Extended friend declarations N1791 GCC 4.7 +// http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2005/n1791.pdf + +// +// Extending sizeof N2253 GCC 4.4 +// http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2007/n2253.html + +// #define GLM_CXX11_INLINE_NAMESPACES +// Inline namespaces N2535 GCC 4.4 +// http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2008/n2535.htm + +// #define GLM_CXX11_UNRESTRICTED_UNIONS +// Unrestricted unions N2544 GCC 4.6 +// http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2008/n2544.pdf + +// #define GLM_CXX11_LOCAL_TYPE_TEMPLATE_ARGS +// Local and unnamed types as template arguments N2657 GCC 4.5 +// http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2008/n2657.htm + +// #define GLM_CXX11_RANGE_FOR +// Range-based for N2930 GCC 4.6 +// http://www.open-std.org/JTC1/SC22/WG21/docs/papers/2009/n2930.html + +// #define GLM_CXX11_OVERRIDE_CONTROL +// Explicit virtual overrides N2928 N3206 N3272 GCC 4.7 +// http://www.open-std.org/JTC1/SC22/WG21/docs/papers/2009/n2928.htm +// http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2010/n3206.htm +// http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2011/n3272.htm + +// +// Minimal support for garbage collection and reachability-based leak detection N2670 No +// http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2008/n2670.htm + +// #define GLM_CXX11_NOEXCEPT +// Allowing move constructors to throw [noexcept] N3050 GCC 4.6 (core language only) +// http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2010/n3050.html + +// +// Defining move special member functions N3053 GCC 4.6 +// http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2010/n3053.html + +// +// Sequence points N2239 Yes +// http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2007/n2239.html + +// +// Atomic operations N2427 GCC 4.4 +// http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2007/n2239.html + +// +// Strong Compare and Exchange N2748 GCC 4.5 +// http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2007/n2427.html + +// +// Bidirectional Fences N2752 GCC 4.8 +// http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2008/n2752.htm + +// +// Memory model N2429 GCC 4.8 +// http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2007/n2429.htm + +// +// Data-dependency ordering: atomics and memory model N2664 GCC 4.4 +// http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2008/n2664.htm + +// +// Propagating exceptions N2179 GCC 4.4 +// http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2007/n2179.html + +// +// Abandoning a process and at_quick_exit N2440 GCC 4.8 +// http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2007/n2440.htm + +// +// Allow atomics use in signal handlers N2547 Yes +// http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2008/n2547.htm + +// +// Thread-local storage N2659 GCC 4.8 +// http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2008/n2659.htm + +// +// Dynamic initialization and destruction with concurrency N2660 GCC 4.3 +// http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2008/n2660.htm + +// +// __func__ predefined identifier N2340 GCC 4.3 +// http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2007/n2340.htm + +// +// C99 preprocessor N1653 GCC 4.3 +// http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2004/n1653.htm + +// +// long long N1811 GCC 4.3 +// http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2005/n1811.pdf + +// +// Extended integral types N1988 Yes +// http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2006/n1988.pdf + +#if(GLM_COMPILER & GLM_COMPILER_GCC) + +# define GLM_CXX11_STATIC_ASSERT + +#elif(GLM_COMPILER & GLM_COMPILER_CLANG) +# if(__has_feature(cxx_exceptions)) +# define GLM_CXX98_EXCEPTIONS +# endif + +# if(__has_feature(cxx_rtti)) +# define GLM_CXX98_RTTI +# endif + +# if(__has_feature(cxx_access_control_sfinae)) +# define GLM_CXX11_ACCESS_CONTROL_SFINAE +# endif + +# if(__has_feature(cxx_alias_templates)) +# define GLM_CXX11_ALIAS_TEMPLATE +# endif + +# if(__has_feature(cxx_alignas)) +# define GLM_CXX11_ALIGNAS +# endif + +# if(__has_feature(cxx_attributes)) +# define GLM_CXX11_ATTRIBUTES +# endif + +# if(__has_feature(cxx_constexpr)) +# define GLM_CXX11_CONSTEXPR +# endif + +# if(__has_feature(cxx_decltype)) +# define GLM_CXX11_DECLTYPE +# endif + +# if(__has_feature(cxx_default_function_template_args)) +# define GLM_CXX11_DEFAULT_FUNCTION_TEMPLATE_ARGS +# endif + +# if(__has_feature(cxx_defaulted_functions)) +# define GLM_CXX11_DEFAULTED_FUNCTIONS +# endif + +# if(__has_feature(cxx_delegating_constructors)) +# define GLM_CXX11_DELEGATING_CONSTRUCTORS +# endif + +# if(__has_feature(cxx_deleted_functions)) +# define GLM_CXX11_DELETED_FUNCTIONS +# endif + +# if(__has_feature(cxx_explicit_conversions)) +# define GLM_CXX11_EXPLICIT_CONVERSIONS +# endif + +# if(__has_feature(cxx_generalized_initializers)) +# define GLM_CXX11_GENERALIZED_INITIALIZERS +# endif + +# if(__has_feature(cxx_implicit_moves)) +# define GLM_CXX11_IMPLICIT_MOVES +# endif + +# if(__has_feature(cxx_inheriting_constructors)) +# define GLM_CXX11_INHERITING_CONSTRUCTORS +# endif + +# if(__has_feature(cxx_inline_namespaces)) +# define GLM_CXX11_INLINE_NAMESPACES +# endif + +# if(__has_feature(cxx_lambdas)) +# define GLM_CXX11_LAMBDAS +# endif + +# if(__has_feature(cxx_local_type_template_args)) +# define GLM_CXX11_LOCAL_TYPE_TEMPLATE_ARGS +# endif + +# if(__has_feature(cxx_noexcept)) +# define GLM_CXX11_NOEXCEPT +# endif + +# if(__has_feature(cxx_nonstatic_member_init)) +# define GLM_CXX11_NONSTATIC_MEMBER_INIT +# endif + +# if(__has_feature(cxx_nullptr)) +# define GLM_CXX11_NULLPTR +# endif + +# if(__has_feature(cxx_override_control)) +# define GLM_CXX11_OVERRIDE_CONTROL +# endif + +# if(__has_feature(cxx_reference_qualified_functions)) +# define GLM_CXX11_REFERENCE_QUALIFIED_FUNCTIONS +# endif + +# if(__has_feature(cxx_range_for)) +# define GLM_CXX11_RANGE_FOR +# endif + +# if(__has_feature(cxx_raw_string_literals)) +# define GLM_CXX11_RAW_STRING_LITERALS +# endif + +# if(__has_feature(cxx_rvalue_references)) +# define GLM_CXX11_RVALUE_REFERENCES +# endif + +# if(__has_feature(cxx_static_assert)) +# define GLM_CXX11_STATIC_ASSERT +# endif + +# if(__has_feature(cxx_auto_type)) +# define GLM_CXX11_AUTO_TYPE +# endif + +# if(__has_feature(cxx_strong_enums)) +# define GLM_CXX11_STRONG_ENUMS +# endif + +# if(__has_feature(cxx_trailing_return)) +# define GLM_CXX11_TRAILING_RETURN +# endif + +# if(__has_feature(cxx_unicode_literals)) +# define GLM_CXX11_UNICODE_LITERALS +# endif + +# if(__has_feature(cxx_unrestricted_unions)) +# define GLM_CXX11_UNRESTRICTED_UNIONS +# endif + +# if(__has_feature(cxx_user_literals)) +# define GLM_CXX11_USER_LITERALS +# endif + +# if(__has_feature(cxx_variadic_templates)) +# define GLM_CXX11_VARIADIC_TEMPLATES +# endif + +#endif//(GLM_COMPILER & GLM_COMPILER_CLANG) diff --git a/src/GLMath/glm/detail/_fixes.hpp b/src/GLMath/glm/detail/_fixes.hpp new file mode 100644 index 000000000..a503c7c0d --- /dev/null +++ b/src/GLMath/glm/detail/_fixes.hpp @@ -0,0 +1,27 @@ +#include + +//! Workaround for compatibility with other libraries +#ifdef max +#undef max +#endif + +//! Workaround for compatibility with other libraries +#ifdef min +#undef min +#endif + +//! Workaround for Android +#ifdef isnan +#undef isnan +#endif + +//! Workaround for Android +#ifdef isinf +#undef isinf +#endif + +//! Workaround for Chrone Native Client +#ifdef log2 +#undef log2 +#endif + diff --git a/src/GLMath/glm/detail/_noise.hpp b/src/GLMath/glm/detail/_noise.hpp new file mode 100644 index 000000000..5a874a022 --- /dev/null +++ b/src/GLMath/glm/detail/_noise.hpp @@ -0,0 +1,81 @@ +#pragma once + +#include "../common.hpp" + +namespace glm{ +namespace detail +{ + template + GLM_FUNC_QUALIFIER T mod289(T const& x) + { + return x - floor(x * (static_cast(1.0) / static_cast(289.0))) * static_cast(289.0); + } + + template + GLM_FUNC_QUALIFIER T permute(T const& x) + { + return mod289(((x * static_cast(34)) + static_cast(1)) * x); + } + + template + GLM_FUNC_QUALIFIER vec<2, T, Q> permute(vec<2, T, Q> const& x) + { + return mod289(((x * static_cast(34)) + static_cast(1)) * x); + } + + template + GLM_FUNC_QUALIFIER vec<3, T, Q> permute(vec<3, T, Q> const& x) + { + return mod289(((x * static_cast(34)) + static_cast(1)) * x); + } + + template + GLM_FUNC_QUALIFIER vec<4, T, Q> permute(vec<4, T, Q> const& x) + { + return mod289(((x * static_cast(34)) + static_cast(1)) * x); + } + + template + GLM_FUNC_QUALIFIER T taylorInvSqrt(T const& r) + { + return static_cast(1.79284291400159) - static_cast(0.85373472095314) * r; + } + + template + GLM_FUNC_QUALIFIER vec<2, T, Q> taylorInvSqrt(vec<2, T, Q> const& r) + { + return static_cast(1.79284291400159) - static_cast(0.85373472095314) * r; + } + + template + GLM_FUNC_QUALIFIER vec<3, T, Q> taylorInvSqrt(vec<3, T, Q> const& r) + { + return static_cast(1.79284291400159) - static_cast(0.85373472095314) * r; + } + + template + GLM_FUNC_QUALIFIER vec<4, T, Q> taylorInvSqrt(vec<4, T, Q> const& r) + { + return static_cast(1.79284291400159) - static_cast(0.85373472095314) * r; + } + + template + GLM_FUNC_QUALIFIER vec<2, T, Q> fade(vec<2, T, Q> const& t) + { + return (t * t * t) * (t * (t * static_cast(6) - static_cast(15)) + static_cast(10)); + } + + template + GLM_FUNC_QUALIFIER vec<3, T, Q> fade(vec<3, T, Q> const& t) + { + return (t * t * t) * (t * (t * static_cast(6) - static_cast(15)) + static_cast(10)); + } + + template + GLM_FUNC_QUALIFIER vec<4, T, Q> fade(vec<4, T, Q> const& t) + { + return (t * t * t) * (t * (t * static_cast(6) - static_cast(15)) + static_cast(10)); + } +}//namespace detail +}//namespace glm + diff --git a/src/GLMath/glm/detail/_swizzle.hpp b/src/GLMath/glm/detail/_swizzle.hpp new file mode 100644 index 000000000..87896ef4f --- /dev/null +++ b/src/GLMath/glm/detail/_swizzle.hpp @@ -0,0 +1,804 @@ +#pragma once + +namespace glm{ +namespace detail +{ + // Internal class for implementing swizzle operators + template + struct _swizzle_base0 + { + protected: + GLM_FUNC_QUALIFIER T& elem(size_t i){ return (reinterpret_cast(_buffer))[i]; } + GLM_FUNC_QUALIFIER T const& elem(size_t i) const{ return (reinterpret_cast(_buffer))[i]; } + + // Use an opaque buffer to *ensure* the compiler doesn't call a constructor. + // The size 1 buffer is assumed to aligned to the actual members so that the + // elem() + char _buffer[1]; + }; + + template + struct _swizzle_base1 : public _swizzle_base0 + { + }; + + template + struct _swizzle_base1<2, T, Q, E0,E1,-1,-2, Aligned> : public _swizzle_base0 + { + GLM_FUNC_QUALIFIER vec<2, T, Q> operator ()() const { return vec<2, T, Q>(this->elem(E0), this->elem(E1)); } + }; + + template + struct _swizzle_base1<3, T, Q, E0,E1,E2,-1, Aligned> : public _swizzle_base0 + { + GLM_FUNC_QUALIFIER vec<3, T, Q> operator ()() const { return vec<3, T, Q>(this->elem(E0), this->elem(E1), this->elem(E2)); } + }; + + template + struct _swizzle_base1<4, T, Q, E0,E1,E2,E3, Aligned> : public _swizzle_base0 + { + GLM_FUNC_QUALIFIER vec<4, T, Q> operator ()() const { return vec<4, T, Q>(this->elem(E0), this->elem(E1), this->elem(E2), this->elem(E3)); } + }; + + // Internal class for implementing swizzle operators + /* + Template parameters: + + T = type of scalar values (e.g. float, double) + N = number of components in the vector (e.g. 3) + E0...3 = what index the n-th element of this swizzle refers to in the unswizzled vec + + DUPLICATE_ELEMENTS = 1 if there is a repeated element, 0 otherwise (used to specialize swizzles + containing duplicate elements so that they cannot be used as r-values). + */ + template + struct _swizzle_base2 : public _swizzle_base1::value> + { + struct op_equal + { + GLM_FUNC_QUALIFIER void operator() (T& e, T& t) const{ e = t; } + }; + + struct op_minus + { + GLM_FUNC_QUALIFIER void operator() (T& e, T& t) const{ e -= t; } + }; + + struct op_plus + { + GLM_FUNC_QUALIFIER void operator() (T& e, T& t) const{ e += t; } + }; + + struct op_mul + { + GLM_FUNC_QUALIFIER void operator() (T& e, T& t) const{ e *= t; } + }; + + struct op_div + { + GLM_FUNC_QUALIFIER void operator() (T& e, T& t) const{ e /= t; } + }; + + public: + GLM_FUNC_QUALIFIER _swizzle_base2& operator= (const T& t) + { + for (int i = 0; i < N; ++i) + (*this)[i] = t; + return *this; + } + + GLM_FUNC_QUALIFIER _swizzle_base2& operator= (vec const& that) + { + _apply_op(that, op_equal()); + return *this; + } + + GLM_FUNC_QUALIFIER void operator -= (vec const& that) + { + _apply_op(that, op_minus()); + } + + GLM_FUNC_QUALIFIER void operator += (vec const& that) + { + _apply_op(that, op_plus()); + } + + GLM_FUNC_QUALIFIER void operator *= (vec const& that) + { + _apply_op(that, op_mul()); + } + + GLM_FUNC_QUALIFIER void operator /= (vec const& that) + { + _apply_op(that, op_div()); + } + + GLM_FUNC_QUALIFIER T& operator[](size_t i) + { + const int offset_dst[4] = { E0, E1, E2, E3 }; + return this->elem(offset_dst[i]); + } + GLM_FUNC_QUALIFIER T operator[](size_t i) const + { + const int offset_dst[4] = { E0, E1, E2, E3 }; + return this->elem(offset_dst[i]); + } + + protected: + template + GLM_FUNC_QUALIFIER void _apply_op(vec const& that, const U& op) + { + // Make a copy of the data in this == &that. + // The copier should optimize out the copy in cases where the function is + // properly inlined and the copy is not necessary. + T t[N]; + for (int i = 0; i < N; ++i) + t[i] = that[i]; + for (int i = 0; i < N; ++i) + op( (*this)[i], t[i] ); + } + }; + + // Specialization for swizzles containing duplicate elements. These cannot be modified. + template + struct _swizzle_base2 : public _swizzle_base1::value> + { + struct Stub {}; + + GLM_FUNC_QUALIFIER _swizzle_base2& operator= (Stub const&) { return *this; } + + GLM_FUNC_QUALIFIER T operator[] (size_t i) const + { + const int offset_dst[4] = { E0, E1, E2, E3 }; + return this->elem(offset_dst[i]); + } + }; + + template + struct _swizzle : public _swizzle_base2 + { + typedef _swizzle_base2 base_type; + + using base_type::operator=; + + GLM_FUNC_QUALIFIER operator vec () const { return (*this)(); } + }; + +// +// To prevent the C++ syntax from getting entirely overwhelming, define some alias macros +// +#define GLM_SWIZZLE_TEMPLATE1 template +#define GLM_SWIZZLE_TEMPLATE2 template +#define GLM_SWIZZLE_TYPE1 _swizzle +#define GLM_SWIZZLE_TYPE2 _swizzle + +// +// Wrapper for a binary operator (e.g. u.yy + v.zy) +// +#define GLM_SWIZZLE_VECTOR_BINARY_OPERATOR_IMPLEMENTATION(OPERAND) \ + GLM_SWIZZLE_TEMPLATE2 \ + GLM_FUNC_QUALIFIER vec operator OPERAND ( const GLM_SWIZZLE_TYPE1& a, const GLM_SWIZZLE_TYPE2& b) \ + { \ + return a() OPERAND b(); \ + } \ + GLM_SWIZZLE_TEMPLATE1 \ + GLM_FUNC_QUALIFIER vec operator OPERAND ( const GLM_SWIZZLE_TYPE1& a, const vec& b) \ + { \ + return a() OPERAND b; \ + } \ + GLM_SWIZZLE_TEMPLATE1 \ + GLM_FUNC_QUALIFIER vec operator OPERAND ( const vec& a, const GLM_SWIZZLE_TYPE1& b) \ + { \ + return a OPERAND b(); \ + } + +// +// Wrapper for a operand between a swizzle and a binary (e.g. 1.0f - u.xyz) +// +#define GLM_SWIZZLE_SCALAR_BINARY_OPERATOR_IMPLEMENTATION(OPERAND) \ + GLM_SWIZZLE_TEMPLATE1 \ + GLM_FUNC_QUALIFIER vec operator OPERAND ( const GLM_SWIZZLE_TYPE1& a, const T& b) \ + { \ + return a() OPERAND b; \ + } \ + GLM_SWIZZLE_TEMPLATE1 \ + GLM_FUNC_QUALIFIER vec operator OPERAND ( const T& a, const GLM_SWIZZLE_TYPE1& b) \ + { \ + return a OPERAND b(); \ + } + +// +// Macro for wrapping a function taking one argument (e.g. abs()) +// +#define GLM_SWIZZLE_FUNCTION_1_ARGS(RETURN_TYPE,FUNCTION) \ + GLM_SWIZZLE_TEMPLATE1 \ + GLM_FUNC_QUALIFIER typename GLM_SWIZZLE_TYPE1::RETURN_TYPE FUNCTION(const GLM_SWIZZLE_TYPE1& a) \ + { \ + return FUNCTION(a()); \ + } + +// +// Macro for wrapping a function taking two vector arguments (e.g. dot()). +// +#define GLM_SWIZZLE_FUNCTION_2_ARGS(RETURN_TYPE,FUNCTION) \ + GLM_SWIZZLE_TEMPLATE2 \ + GLM_FUNC_QUALIFIER typename GLM_SWIZZLE_TYPE1::RETURN_TYPE FUNCTION(const GLM_SWIZZLE_TYPE1& a, const GLM_SWIZZLE_TYPE2& b) \ + { \ + return FUNCTION(a(), b()); \ + } \ + GLM_SWIZZLE_TEMPLATE1 \ + GLM_FUNC_QUALIFIER typename GLM_SWIZZLE_TYPE1::RETURN_TYPE FUNCTION(const GLM_SWIZZLE_TYPE1& a, const GLM_SWIZZLE_TYPE1& b) \ + { \ + return FUNCTION(a(), b()); \ + } \ + GLM_SWIZZLE_TEMPLATE1 \ + GLM_FUNC_QUALIFIER typename GLM_SWIZZLE_TYPE1::RETURN_TYPE FUNCTION(const GLM_SWIZZLE_TYPE1& a, const typename V& b) \ + { \ + return FUNCTION(a(), b); \ + } \ + GLM_SWIZZLE_TEMPLATE1 \ + GLM_FUNC_QUALIFIER typename GLM_SWIZZLE_TYPE1::RETURN_TYPE FUNCTION(const V& a, const GLM_SWIZZLE_TYPE1& b) \ + { \ + return FUNCTION(a, b()); \ + } + +// +// Macro for wrapping a function take 2 vec arguments followed by a scalar (e.g. mix()). +// +#define GLM_SWIZZLE_FUNCTION_2_ARGS_SCALAR(RETURN_TYPE,FUNCTION) \ + GLM_SWIZZLE_TEMPLATE2 \ + GLM_FUNC_QUALIFIER typename GLM_SWIZZLE_TYPE1::RETURN_TYPE FUNCTION(const GLM_SWIZZLE_TYPE1& a, const GLM_SWIZZLE_TYPE2& b, const T& c) \ + { \ + return FUNCTION(a(), b(), c); \ + } \ + GLM_SWIZZLE_TEMPLATE1 \ + GLM_FUNC_QUALIFIER typename GLM_SWIZZLE_TYPE1::RETURN_TYPE FUNCTION(const GLM_SWIZZLE_TYPE1& a, const GLM_SWIZZLE_TYPE1& b, const T& c) \ + { \ + return FUNCTION(a(), b(), c); \ + } \ + GLM_SWIZZLE_TEMPLATE1 \ + GLM_FUNC_QUALIFIER typename GLM_SWIZZLE_TYPE1::RETURN_TYPE FUNCTION(const GLM_SWIZZLE_TYPE1& a, const typename S0::vec_type& b, const T& c)\ + { \ + return FUNCTION(a(), b, c); \ + } \ + GLM_SWIZZLE_TEMPLATE1 \ + GLM_FUNC_QUALIFIER typename GLM_SWIZZLE_TYPE1::RETURN_TYPE FUNCTION(const typename V& a, const GLM_SWIZZLE_TYPE1& b, const T& c) \ + { \ + return FUNCTION(a, b(), c); \ + } + +}//namespace detail +}//namespace glm + +namespace glm +{ + namespace detail + { + GLM_SWIZZLE_SCALAR_BINARY_OPERATOR_IMPLEMENTATION(-) + GLM_SWIZZLE_SCALAR_BINARY_OPERATOR_IMPLEMENTATION(*) + GLM_SWIZZLE_VECTOR_BINARY_OPERATOR_IMPLEMENTATION(+) + GLM_SWIZZLE_VECTOR_BINARY_OPERATOR_IMPLEMENTATION(-) + GLM_SWIZZLE_VECTOR_BINARY_OPERATOR_IMPLEMENTATION(*) + GLM_SWIZZLE_VECTOR_BINARY_OPERATOR_IMPLEMENTATION(/) + } + + // + // Swizzles are distinct types from the unswizzled type. The below macros will + // provide template specializations for the swizzle types for the given functions + // so that the compiler does not have any ambiguity to choosing how to handle + // the function. + // + // The alternative is to use the operator()() when calling the function in order + // to explicitly convert the swizzled type to the unswizzled type. + // + + //GLM_SWIZZLE_FUNCTION_1_ARGS(vec_type, abs); + //GLM_SWIZZLE_FUNCTION_1_ARGS(vec_type, acos); + //GLM_SWIZZLE_FUNCTION_1_ARGS(vec_type, acosh); + //GLM_SWIZZLE_FUNCTION_1_ARGS(vec_type, all); + //GLM_SWIZZLE_FUNCTION_1_ARGS(vec_type, any); + + //GLM_SWIZZLE_FUNCTION_2_ARGS(value_type, dot); + //GLM_SWIZZLE_FUNCTION_2_ARGS(vec_type, cross); + //GLM_SWIZZLE_FUNCTION_2_ARGS(vec_type, step); + //GLM_SWIZZLE_FUNCTION_2_ARGS_SCALAR(vec_type, mix); +} + +#define GLM_SWIZZLE2_2_MEMBERS(T, Q, E0,E1) \ + struct { detail::_swizzle<2, T, Q, 0,0,-1,-2> E0 ## E0; }; \ + struct { detail::_swizzle<2, T, Q, 0,1,-1,-2> E0 ## E1; }; \ + struct { detail::_swizzle<2, T, Q, 1,0,-1,-2> E1 ## E0; }; \ + struct { detail::_swizzle<2, T, Q, 1,1,-1,-2> E1 ## E1; }; + +#define GLM_SWIZZLE2_3_MEMBERS(T, Q, E0,E1) \ + struct { detail::_swizzle<3,T, Q, 0,0,0,-1> E0 ## E0 ## E0; }; \ + struct { detail::_swizzle<3,T, Q, 0,0,1,-1> E0 ## E0 ## E1; }; \ + struct { detail::_swizzle<3,T, Q, 0,1,0,-1> E0 ## E1 ## E0; }; \ + struct { detail::_swizzle<3,T, Q, 0,1,1,-1> E0 ## E1 ## E1; }; \ + struct { detail::_swizzle<3,T, Q, 1,0,0,-1> E1 ## E0 ## E0; }; \ + struct { detail::_swizzle<3,T, Q, 1,0,1,-1> E1 ## E0 ## E1; }; \ + struct { detail::_swizzle<3,T, Q, 1,1,0,-1> E1 ## E1 ## E0; }; \ + struct { detail::_swizzle<3,T, Q, 1,1,1,-1> E1 ## E1 ## E1; }; + +#define GLM_SWIZZLE2_4_MEMBERS(T, Q, E0,E1) \ + struct { detail::_swizzle<4,T, Q, 0,0,0,0> E0 ## E0 ## E0 ## E0; }; \ + struct { detail::_swizzle<4,T, Q, 0,0,0,1> E0 ## E0 ## E0 ## E1; }; \ + struct { detail::_swizzle<4,T, Q, 0,0,1,0> E0 ## E0 ## E1 ## E0; }; \ + struct { detail::_swizzle<4,T, Q, 0,0,1,1> E0 ## E0 ## E1 ## E1; }; \ + struct { detail::_swizzle<4,T, Q, 0,1,0,0> E0 ## E1 ## E0 ## E0; }; \ + struct { detail::_swizzle<4,T, Q, 0,1,0,1> E0 ## E1 ## E0 ## E1; }; \ + struct { detail::_swizzle<4,T, Q, 0,1,1,0> E0 ## E1 ## E1 ## E0; }; \ + struct { detail::_swizzle<4,T, Q, 0,1,1,1> E0 ## E1 ## E1 ## E1; }; \ + struct { detail::_swizzle<4,T, Q, 1,0,0,0> E1 ## E0 ## E0 ## E0; }; \ + struct { detail::_swizzle<4,T, Q, 1,0,0,1> E1 ## E0 ## E0 ## E1; }; \ + struct { detail::_swizzle<4,T, Q, 1,0,1,0> E1 ## E0 ## E1 ## E0; }; \ + struct { detail::_swizzle<4,T, Q, 1,0,1,1> E1 ## E0 ## E1 ## E1; }; \ + struct { detail::_swizzle<4,T, Q, 1,1,0,0> E1 ## E1 ## E0 ## E0; }; \ + struct { detail::_swizzle<4,T, Q, 1,1,0,1> E1 ## E1 ## E0 ## E1; }; \ + struct { detail::_swizzle<4,T, Q, 1,1,1,0> E1 ## E1 ## E1 ## E0; }; \ + struct { detail::_swizzle<4,T, Q, 1,1,1,1> E1 ## E1 ## E1 ## E1; }; + +#define GLM_SWIZZLE3_2_MEMBERS(T, Q, E0,E1,E2) \ + struct { detail::_swizzle<2,T, Q, 0,0,-1,-2> E0 ## E0; }; \ + struct { detail::_swizzle<2,T, Q, 0,1,-1,-2> E0 ## E1; }; \ + struct { detail::_swizzle<2,T, Q, 0,2,-1,-2> E0 ## E2; }; \ + struct { detail::_swizzle<2,T, Q, 1,0,-1,-2> E1 ## E0; }; \ + struct { detail::_swizzle<2,T, Q, 1,1,-1,-2> E1 ## E1; }; \ + struct { detail::_swizzle<2,T, Q, 1,2,-1,-2> E1 ## E2; }; \ + struct { detail::_swizzle<2,T, Q, 2,0,-1,-2> E2 ## E0; }; \ + struct { detail::_swizzle<2,T, Q, 2,1,-1,-2> E2 ## E1; }; \ + struct { detail::_swizzle<2,T, Q, 2,2,-1,-2> E2 ## E2; }; + +#define GLM_SWIZZLE3_3_MEMBERS(T, Q ,E0,E1,E2) \ + struct { detail::_swizzle<3, T, Q, 0,0,0,-1> E0 ## E0 ## E0; }; \ + struct { detail::_swizzle<3, T, Q, 0,0,1,-1> E0 ## E0 ## E1; }; \ + struct { detail::_swizzle<3, T, Q, 0,0,2,-1> E0 ## E0 ## E2; }; \ + struct { detail::_swizzle<3, T, Q, 0,1,0,-1> E0 ## E1 ## E0; }; \ + struct { detail::_swizzle<3, T, Q, 0,1,1,-1> E0 ## E1 ## E1; }; \ + struct { detail::_swizzle<3, T, Q, 0,1,2,-1> E0 ## E1 ## E2; }; \ + struct { detail::_swizzle<3, T, Q, 0,2,0,-1> E0 ## E2 ## E0; }; \ + struct { detail::_swizzle<3, T, Q, 0,2,1,-1> E0 ## E2 ## E1; }; \ + struct { detail::_swizzle<3, T, Q, 0,2,2,-1> E0 ## E2 ## E2; }; \ + struct { detail::_swizzle<3, T, Q, 1,0,0,-1> E1 ## E0 ## E0; }; \ + struct { detail::_swizzle<3, T, Q, 1,0,1,-1> E1 ## E0 ## E1; }; \ + struct { detail::_swizzle<3, T, Q, 1,0,2,-1> E1 ## E0 ## E2; }; \ + struct { detail::_swizzle<3, T, Q, 1,1,0,-1> E1 ## E1 ## E0; }; \ + struct { detail::_swizzle<3, T, Q, 1,1,1,-1> E1 ## E1 ## E1; }; \ + struct { detail::_swizzle<3, T, Q, 1,1,2,-1> E1 ## E1 ## E2; }; \ + struct { detail::_swizzle<3, T, Q, 1,2,0,-1> E1 ## E2 ## E0; }; \ + struct { detail::_swizzle<3, T, Q, 1,2,1,-1> E1 ## E2 ## E1; }; \ + struct { detail::_swizzle<3, T, Q, 1,2,2,-1> E1 ## E2 ## E2; }; \ + struct { detail::_swizzle<3, T, Q, 2,0,0,-1> E2 ## E0 ## E0; }; \ + struct { detail::_swizzle<3, T, Q, 2,0,1,-1> E2 ## E0 ## E1; }; \ + struct { detail::_swizzle<3, T, Q, 2,0,2,-1> E2 ## E0 ## E2; }; \ + struct { detail::_swizzle<3, T, Q, 2,1,0,-1> E2 ## E1 ## E0; }; \ + struct { detail::_swizzle<3, T, Q, 2,1,1,-1> E2 ## E1 ## E1; }; \ + struct { detail::_swizzle<3, T, Q, 2,1,2,-1> E2 ## E1 ## E2; }; \ + struct { detail::_swizzle<3, T, Q, 2,2,0,-1> E2 ## E2 ## E0; }; \ + struct { detail::_swizzle<3, T, Q, 2,2,1,-1> E2 ## E2 ## E1; }; \ + struct { detail::_swizzle<3, T, Q, 2,2,2,-1> E2 ## E2 ## E2; }; + +#define GLM_SWIZZLE3_4_MEMBERS(T, Q, E0,E1,E2) \ + struct { detail::_swizzle<4,T, Q, 0,0,0,0> E0 ## E0 ## E0 ## E0; }; \ + struct { detail::_swizzle<4,T, Q, 0,0,0,1> E0 ## E0 ## E0 ## E1; }; \ + struct { detail::_swizzle<4,T, Q, 0,0,0,2> E0 ## E0 ## E0 ## E2; }; \ + struct { detail::_swizzle<4,T, Q, 0,0,1,0> E0 ## E0 ## E1 ## E0; }; \ + struct { detail::_swizzle<4,T, Q, 0,0,1,1> E0 ## E0 ## E1 ## E1; }; \ + struct { detail::_swizzle<4,T, Q, 0,0,1,2> E0 ## E0 ## E1 ## E2; }; \ + struct { detail::_swizzle<4,T, Q, 0,0,2,0> E0 ## E0 ## E2 ## E0; }; \ + struct { detail::_swizzle<4,T, Q, 0,0,2,1> E0 ## E0 ## E2 ## E1; }; \ + struct { detail::_swizzle<4,T, Q, 0,0,2,2> E0 ## E0 ## E2 ## E2; }; \ + struct { detail::_swizzle<4,T, Q, 0,1,0,0> E0 ## E1 ## E0 ## E0; }; \ + struct { detail::_swizzle<4,T, Q, 0,1,0,1> E0 ## E1 ## E0 ## E1; }; \ + struct { detail::_swizzle<4,T, Q, 0,1,0,2> E0 ## E1 ## E0 ## E2; }; \ + struct { detail::_swizzle<4,T, Q, 0,1,1,0> E0 ## E1 ## E1 ## E0; }; \ + struct { detail::_swizzle<4,T, Q, 0,1,1,1> E0 ## E1 ## E1 ## E1; }; \ + struct { detail::_swizzle<4,T, Q, 0,1,1,2> E0 ## E1 ## E1 ## E2; }; \ + struct { detail::_swizzle<4,T, Q, 0,1,2,0> E0 ## E1 ## E2 ## E0; }; \ + struct { detail::_swizzle<4,T, Q, 0,1,2,1> E0 ## E1 ## E2 ## E1; }; \ + struct { detail::_swizzle<4,T, Q, 0,1,2,2> E0 ## E1 ## E2 ## E2; }; \ + struct { detail::_swizzle<4,T, Q, 0,2,0,0> E0 ## E2 ## E0 ## E0; }; \ + struct { detail::_swizzle<4,T, Q, 0,2,0,1> E0 ## E2 ## E0 ## E1; }; \ + struct { detail::_swizzle<4,T, Q, 0,2,0,2> E0 ## E2 ## E0 ## E2; }; \ + struct { detail::_swizzle<4,T, Q, 0,2,1,0> E0 ## E2 ## E1 ## E0; }; \ + struct { detail::_swizzle<4,T, Q, 0,2,1,1> E0 ## E2 ## E1 ## E1; }; \ + struct { detail::_swizzle<4,T, Q, 0,2,1,2> E0 ## E2 ## E1 ## E2; }; \ + struct { detail::_swizzle<4,T, Q, 0,2,2,0> E0 ## E2 ## E2 ## E0; }; \ + struct { detail::_swizzle<4,T, Q, 0,2,2,1> E0 ## E2 ## E2 ## E1; }; \ + struct { detail::_swizzle<4,T, Q, 0,2,2,2> E0 ## E2 ## E2 ## E2; }; \ + struct { detail::_swizzle<4,T, Q, 1,0,0,0> E1 ## E0 ## E0 ## E0; }; \ + struct { detail::_swizzle<4,T, Q, 1,0,0,1> E1 ## E0 ## E0 ## E1; }; \ + struct { detail::_swizzle<4,T, Q, 1,0,0,2> E1 ## E0 ## E0 ## E2; }; \ + struct { detail::_swizzle<4,T, Q, 1,0,1,0> E1 ## E0 ## E1 ## E0; }; \ + struct { detail::_swizzle<4,T, Q, 1,0,1,1> E1 ## E0 ## E1 ## E1; }; \ + struct { detail::_swizzle<4,T, Q, 1,0,1,2> E1 ## E0 ## E1 ## E2; }; \ + struct { detail::_swizzle<4,T, Q, 1,0,2,0> E1 ## E0 ## E2 ## E0; }; \ + struct { detail::_swizzle<4,T, Q, 1,0,2,1> E1 ## E0 ## E2 ## E1; }; \ + struct { detail::_swizzle<4,T, Q, 1,0,2,2> E1 ## E0 ## E2 ## E2; }; \ + struct { detail::_swizzle<4,T, Q, 1,1,0,0> E1 ## E1 ## E0 ## E0; }; \ + struct { detail::_swizzle<4,T, Q, 1,1,0,1> E1 ## E1 ## E0 ## E1; }; \ + struct { detail::_swizzle<4,T, Q, 1,1,0,2> E1 ## E1 ## E0 ## E2; }; \ + struct { detail::_swizzle<4,T, Q, 1,1,1,0> E1 ## E1 ## E1 ## E0; }; \ + struct { detail::_swizzle<4,T, Q, 1,1,1,1> E1 ## E1 ## E1 ## E1; }; \ + struct { detail::_swizzle<4,T, Q, 1,1,1,2> E1 ## E1 ## E1 ## E2; }; \ + struct { detail::_swizzle<4,T, Q, 1,1,2,0> E1 ## E1 ## E2 ## E0; }; \ + struct { detail::_swizzle<4,T, Q, 1,1,2,1> E1 ## E1 ## E2 ## E1; }; \ + struct { detail::_swizzle<4,T, Q, 1,1,2,2> E1 ## E1 ## E2 ## E2; }; \ + struct { detail::_swizzle<4,T, Q, 1,2,0,0> E1 ## E2 ## E0 ## E0; }; \ + struct { detail::_swizzle<4,T, Q, 1,2,0,1> E1 ## E2 ## E0 ## E1; }; \ + struct { detail::_swizzle<4,T, Q, 1,2,0,2> E1 ## E2 ## E0 ## E2; }; \ + struct { detail::_swizzle<4,T, Q, 1,2,1,0> E1 ## E2 ## E1 ## E0; }; \ + struct { detail::_swizzle<4,T, Q, 1,2,1,1> E1 ## E2 ## E1 ## E1; }; \ + struct { detail::_swizzle<4,T, Q, 1,2,1,2> E1 ## E2 ## E1 ## E2; }; \ + struct { detail::_swizzle<4,T, Q, 1,2,2,0> E1 ## E2 ## E2 ## E0; }; \ + struct { detail::_swizzle<4,T, Q, 1,2,2,1> E1 ## E2 ## E2 ## E1; }; \ + struct { detail::_swizzle<4,T, Q, 1,2,2,2> E1 ## E2 ## E2 ## E2; }; \ + struct { detail::_swizzle<4,T, Q, 2,0,0,0> E2 ## E0 ## E0 ## E0; }; \ + struct { detail::_swizzle<4,T, Q, 2,0,0,1> E2 ## E0 ## E0 ## E1; }; \ + struct { detail::_swizzle<4,T, Q, 2,0,0,2> E2 ## E0 ## E0 ## E2; }; \ + struct { detail::_swizzle<4,T, Q, 2,0,1,0> E2 ## E0 ## E1 ## E0; }; \ + struct { detail::_swizzle<4,T, Q, 2,0,1,1> E2 ## E0 ## E1 ## E1; }; \ + struct { detail::_swizzle<4,T, Q, 2,0,1,2> E2 ## E0 ## E1 ## E2; }; \ + struct { detail::_swizzle<4,T, Q, 2,0,2,0> E2 ## E0 ## E2 ## E0; }; \ + struct { detail::_swizzle<4,T, Q, 2,0,2,1> E2 ## E0 ## E2 ## E1; }; \ + struct { detail::_swizzle<4,T, Q, 2,0,2,2> E2 ## E0 ## E2 ## E2; }; \ + struct { detail::_swizzle<4,T, Q, 2,1,0,0> E2 ## E1 ## E0 ## E0; }; \ + struct { detail::_swizzle<4,T, Q, 2,1,0,1> E2 ## E1 ## E0 ## E1; }; \ + struct { detail::_swizzle<4,T, Q, 2,1,0,2> E2 ## E1 ## E0 ## E2; }; \ + struct { detail::_swizzle<4,T, Q, 2,1,1,0> E2 ## E1 ## E1 ## E0; }; \ + struct { detail::_swizzle<4,T, Q, 2,1,1,1> E2 ## E1 ## E1 ## E1; }; \ + struct { detail::_swizzle<4,T, Q, 2,1,1,2> E2 ## E1 ## E1 ## E2; }; \ + struct { detail::_swizzle<4,T, Q, 2,1,2,0> E2 ## E1 ## E2 ## E0; }; \ + struct { detail::_swizzle<4,T, Q, 2,1,2,1> E2 ## E1 ## E2 ## E1; }; \ + struct { detail::_swizzle<4,T, Q, 2,1,2,2> E2 ## E1 ## E2 ## E2; }; \ + struct { detail::_swizzle<4,T, Q, 2,2,0,0> E2 ## E2 ## E0 ## E0; }; \ + struct { detail::_swizzle<4,T, Q, 2,2,0,1> E2 ## E2 ## E0 ## E1; }; \ + struct { detail::_swizzle<4,T, Q, 2,2,0,2> E2 ## E2 ## E0 ## E2; }; \ + struct { detail::_swizzle<4,T, Q, 2,2,1,0> E2 ## E2 ## E1 ## E0; }; \ + struct { detail::_swizzle<4,T, Q, 2,2,1,1> E2 ## E2 ## E1 ## E1; }; \ + struct { detail::_swizzle<4,T, Q, 2,2,1,2> E2 ## E2 ## E1 ## E2; }; \ + struct { detail::_swizzle<4,T, Q, 2,2,2,0> E2 ## E2 ## E2 ## E0; }; \ + struct { detail::_swizzle<4,T, Q, 2,2,2,1> E2 ## E2 ## E2 ## E1; }; \ + struct { detail::_swizzle<4,T, Q, 2,2,2,2> E2 ## E2 ## E2 ## E2; }; + +#define GLM_SWIZZLE4_2_MEMBERS(T, Q, E0,E1,E2,E3) \ + struct { detail::_swizzle<2,T, Q, 0,0,-1,-2> E0 ## E0; }; \ + struct { detail::_swizzle<2,T, Q, 0,1,-1,-2> E0 ## E1; }; \ + struct { detail::_swizzle<2,T, Q, 0,2,-1,-2> E0 ## E2; }; \ + struct { detail::_swizzle<2,T, Q, 0,3,-1,-2> E0 ## E3; }; \ + struct { detail::_swizzle<2,T, Q, 1,0,-1,-2> E1 ## E0; }; \ + struct { detail::_swizzle<2,T, Q, 1,1,-1,-2> E1 ## E1; }; \ + struct { detail::_swizzle<2,T, Q, 1,2,-1,-2> E1 ## E2; }; \ + struct { detail::_swizzle<2,T, Q, 1,3,-1,-2> E1 ## E3; }; \ + struct { detail::_swizzle<2,T, Q, 2,0,-1,-2> E2 ## E0; }; \ + struct { detail::_swizzle<2,T, Q, 2,1,-1,-2> E2 ## E1; }; \ + struct { detail::_swizzle<2,T, Q, 2,2,-1,-2> E2 ## E2; }; \ + struct { detail::_swizzle<2,T, Q, 2,3,-1,-2> E2 ## E3; }; \ + struct { detail::_swizzle<2,T, Q, 3,0,-1,-2> E3 ## E0; }; \ + struct { detail::_swizzle<2,T, Q, 3,1,-1,-2> E3 ## E1; }; \ + struct { detail::_swizzle<2,T, Q, 3,2,-1,-2> E3 ## E2; }; \ + struct { detail::_swizzle<2,T, Q, 3,3,-1,-2> E3 ## E3; }; + +#define GLM_SWIZZLE4_3_MEMBERS(T, Q, E0,E1,E2,E3) \ + struct { detail::_swizzle<3, T, Q, 0,0,0,-1> E0 ## E0 ## E0; }; \ + struct { detail::_swizzle<3, T, Q, 0,0,1,-1> E0 ## E0 ## E1; }; \ + struct { detail::_swizzle<3, T, Q, 0,0,2,-1> E0 ## E0 ## E2; }; \ + struct { detail::_swizzle<3, T, Q, 0,0,3,-1> E0 ## E0 ## E3; }; \ + struct { detail::_swizzle<3, T, Q, 0,1,0,-1> E0 ## E1 ## E0; }; \ + struct { detail::_swizzle<3, T, Q, 0,1,1,-1> E0 ## E1 ## E1; }; \ + struct { detail::_swizzle<3, T, Q, 0,1,2,-1> E0 ## E1 ## E2; }; \ + struct { detail::_swizzle<3, T, Q, 0,1,3,-1> E0 ## E1 ## E3; }; \ + struct { detail::_swizzle<3, T, Q, 0,2,0,-1> E0 ## E2 ## E0; }; \ + struct { detail::_swizzle<3, T, Q, 0,2,1,-1> E0 ## E2 ## E1; }; \ + struct { detail::_swizzle<3, T, Q, 0,2,2,-1> E0 ## E2 ## E2; }; \ + struct { detail::_swizzle<3, T, Q, 0,2,3,-1> E0 ## E2 ## E3; }; \ + struct { detail::_swizzle<3, T, Q, 0,3,0,-1> E0 ## E3 ## E0; }; \ + struct { detail::_swizzle<3, T, Q, 0,3,1,-1> E0 ## E3 ## E1; }; \ + struct { detail::_swizzle<3, T, Q, 0,3,2,-1> E0 ## E3 ## E2; }; \ + struct { detail::_swizzle<3, T, Q, 0,3,3,-1> E0 ## E3 ## E3; }; \ + struct { detail::_swizzle<3, T, Q, 1,0,0,-1> E1 ## E0 ## E0; }; \ + struct { detail::_swizzle<3, T, Q, 1,0,1,-1> E1 ## E0 ## E1; }; \ + struct { detail::_swizzle<3, T, Q, 1,0,2,-1> E1 ## E0 ## E2; }; \ + struct { detail::_swizzle<3, T, Q, 1,0,3,-1> E1 ## E0 ## E3; }; \ + struct { detail::_swizzle<3, T, Q, 1,1,0,-1> E1 ## E1 ## E0; }; \ + struct { detail::_swizzle<3, T, Q, 1,1,1,-1> E1 ## E1 ## E1; }; \ + struct { detail::_swizzle<3, T, Q, 1,1,2,-1> E1 ## E1 ## E2; }; \ + struct { detail::_swizzle<3, T, Q, 1,1,3,-1> E1 ## E1 ## E3; }; \ + struct { detail::_swizzle<3, T, Q, 1,2,0,-1> E1 ## E2 ## E0; }; \ + struct { detail::_swizzle<3, T, Q, 1,2,1,-1> E1 ## E2 ## E1; }; \ + struct { detail::_swizzle<3, T, Q, 1,2,2,-1> E1 ## E2 ## E2; }; \ + struct { detail::_swizzle<3, T, Q, 1,2,3,-1> E1 ## E2 ## E3; }; \ + struct { detail::_swizzle<3, T, Q, 1,3,0,-1> E1 ## E3 ## E0; }; \ + struct { detail::_swizzle<3, T, Q, 1,3,1,-1> E1 ## E3 ## E1; }; \ + struct { detail::_swizzle<3, T, Q, 1,3,2,-1> E1 ## E3 ## E2; }; \ + struct { detail::_swizzle<3, T, Q, 1,3,3,-1> E1 ## E3 ## E3; }; \ + struct { detail::_swizzle<3, T, Q, 2,0,0,-1> E2 ## E0 ## E0; }; \ + struct { detail::_swizzle<3, T, Q, 2,0,1,-1> E2 ## E0 ## E1; }; \ + struct { detail::_swizzle<3, T, Q, 2,0,2,-1> E2 ## E0 ## E2; }; \ + struct { detail::_swizzle<3, T, Q, 2,0,3,-1> E2 ## E0 ## E3; }; \ + struct { detail::_swizzle<3, T, Q, 2,1,0,-1> E2 ## E1 ## E0; }; \ + struct { detail::_swizzle<3, T, Q, 2,1,1,-1> E2 ## E1 ## E1; }; \ + struct { detail::_swizzle<3, T, Q, 2,1,2,-1> E2 ## E1 ## E2; }; \ + struct { detail::_swizzle<3, T, Q, 2,1,3,-1> E2 ## E1 ## E3; }; \ + struct { detail::_swizzle<3, T, Q, 2,2,0,-1> E2 ## E2 ## E0; }; \ + struct { detail::_swizzle<3, T, Q, 2,2,1,-1> E2 ## E2 ## E1; }; \ + struct { detail::_swizzle<3, T, Q, 2,2,2,-1> E2 ## E2 ## E2; }; \ + struct { detail::_swizzle<3, T, Q, 2,2,3,-1> E2 ## E2 ## E3; }; \ + struct { detail::_swizzle<3, T, Q, 2,3,0,-1> E2 ## E3 ## E0; }; \ + struct { detail::_swizzle<3, T, Q, 2,3,1,-1> E2 ## E3 ## E1; }; \ + struct { detail::_swizzle<3, T, Q, 2,3,2,-1> E2 ## E3 ## E2; }; \ + struct { detail::_swizzle<3, T, Q, 2,3,3,-1> E2 ## E3 ## E3; }; \ + struct { detail::_swizzle<3, T, Q, 3,0,0,-1> E3 ## E0 ## E0; }; \ + struct { detail::_swizzle<3, T, Q, 3,0,1,-1> E3 ## E0 ## E1; }; \ + struct { detail::_swizzle<3, T, Q, 3,0,2,-1> E3 ## E0 ## E2; }; \ + struct { detail::_swizzle<3, T, Q, 3,0,3,-1> E3 ## E0 ## E3; }; \ + struct { detail::_swizzle<3, T, Q, 3,1,0,-1> E3 ## E1 ## E0; }; \ + struct { detail::_swizzle<3, T, Q, 3,1,1,-1> E3 ## E1 ## E1; }; \ + struct { detail::_swizzle<3, T, Q, 3,1,2,-1> E3 ## E1 ## E2; }; \ + struct { detail::_swizzle<3, T, Q, 3,1,3,-1> E3 ## E1 ## E3; }; \ + struct { detail::_swizzle<3, T, Q, 3,2,0,-1> E3 ## E2 ## E0; }; \ + struct { detail::_swizzle<3, T, Q, 3,2,1,-1> E3 ## E2 ## E1; }; \ + struct { detail::_swizzle<3, T, Q, 3,2,2,-1> E3 ## E2 ## E2; }; \ + struct { detail::_swizzle<3, T, Q, 3,2,3,-1> E3 ## E2 ## E3; }; \ + struct { detail::_swizzle<3, T, Q, 3,3,0,-1> E3 ## E3 ## E0; }; \ + struct { detail::_swizzle<3, T, Q, 3,3,1,-1> E3 ## E3 ## E1; }; \ + struct { detail::_swizzle<3, T, Q, 3,3,2,-1> E3 ## E3 ## E2; }; \ + struct { detail::_swizzle<3, T, Q, 3,3,3,-1> E3 ## E3 ## E3; }; + +#define GLM_SWIZZLE4_4_MEMBERS(T, Q, E0,E1,E2,E3) \ + struct { detail::_swizzle<4, T, Q, 0,0,0,0> E0 ## E0 ## E0 ## E0; }; \ + struct { detail::_swizzle<4, T, Q, 0,0,0,1> E0 ## E0 ## E0 ## E1; }; \ + struct { detail::_swizzle<4, T, Q, 0,0,0,2> E0 ## E0 ## E0 ## E2; }; \ + struct { detail::_swizzle<4, T, Q, 0,0,0,3> E0 ## E0 ## E0 ## E3; }; \ + struct { detail::_swizzle<4, T, Q, 0,0,1,0> E0 ## E0 ## E1 ## E0; }; \ + struct { detail::_swizzle<4, T, Q, 0,0,1,1> E0 ## E0 ## E1 ## E1; }; \ + struct { detail::_swizzle<4, T, Q, 0,0,1,2> E0 ## E0 ## E1 ## E2; }; \ + struct { detail::_swizzle<4, T, Q, 0,0,1,3> E0 ## E0 ## E1 ## E3; }; \ + struct { detail::_swizzle<4, T, Q, 0,0,2,0> E0 ## E0 ## E2 ## E0; }; \ + struct { detail::_swizzle<4, T, Q, 0,0,2,1> E0 ## E0 ## E2 ## E1; }; \ + struct { detail::_swizzle<4, T, Q, 0,0,2,2> E0 ## E0 ## E2 ## E2; }; \ + struct { detail::_swizzle<4, T, Q, 0,0,2,3> E0 ## E0 ## E2 ## E3; }; \ + struct { detail::_swizzle<4, T, Q, 0,0,3,0> E0 ## E0 ## E3 ## E0; }; \ + struct { detail::_swizzle<4, T, Q, 0,0,3,1> E0 ## E0 ## E3 ## E1; }; \ + struct { detail::_swizzle<4, T, Q, 0,0,3,2> E0 ## E0 ## E3 ## E2; }; \ + struct { detail::_swizzle<4, T, Q, 0,0,3,3> E0 ## E0 ## E3 ## E3; }; \ + struct { detail::_swizzle<4, T, Q, 0,1,0,0> E0 ## E1 ## E0 ## E0; }; \ + struct { detail::_swizzle<4, T, Q, 0,1,0,1> E0 ## E1 ## E0 ## E1; }; \ + struct { detail::_swizzle<4, T, Q, 0,1,0,2> E0 ## E1 ## E0 ## E2; }; \ + struct { detail::_swizzle<4, T, Q, 0,1,0,3> E0 ## E1 ## E0 ## E3; }; \ + struct { detail::_swizzle<4, T, Q, 0,1,1,0> E0 ## E1 ## E1 ## E0; }; \ + struct { detail::_swizzle<4, T, Q, 0,1,1,1> E0 ## E1 ## E1 ## E1; }; \ + struct { detail::_swizzle<4, T, Q, 0,1,1,2> E0 ## E1 ## E1 ## E2; }; \ + struct { detail::_swizzle<4, T, Q, 0,1,1,3> E0 ## E1 ## E1 ## E3; }; \ + struct { detail::_swizzle<4, T, Q, 0,1,2,0> E0 ## E1 ## E2 ## E0; }; \ + struct { detail::_swizzle<4, T, Q, 0,1,2,1> E0 ## E1 ## E2 ## E1; }; \ + struct { detail::_swizzle<4, T, Q, 0,1,2,2> E0 ## E1 ## E2 ## E2; }; \ + struct { detail::_swizzle<4, T, Q, 0,1,2,3> E0 ## E1 ## E2 ## E3; }; \ + struct { detail::_swizzle<4, T, Q, 0,1,3,0> E0 ## E1 ## E3 ## E0; }; \ + struct { detail::_swizzle<4, T, Q, 0,1,3,1> E0 ## E1 ## E3 ## E1; }; \ + struct { detail::_swizzle<4, T, Q, 0,1,3,2> E0 ## E1 ## E3 ## E2; }; \ + struct { detail::_swizzle<4, T, Q, 0,1,3,3> E0 ## E1 ## E3 ## E3; }; \ + struct { detail::_swizzle<4, T, Q, 0,2,0,0> E0 ## E2 ## E0 ## E0; }; \ + struct { detail::_swizzle<4, T, Q, 0,2,0,1> E0 ## E2 ## E0 ## E1; }; \ + struct { detail::_swizzle<4, T, Q, 0,2,0,2> E0 ## E2 ## E0 ## E2; }; \ + struct { detail::_swizzle<4, T, Q, 0,2,0,3> E0 ## E2 ## E0 ## E3; }; \ + struct { detail::_swizzle<4, T, Q, 0,2,1,0> E0 ## E2 ## E1 ## E0; }; \ + struct { detail::_swizzle<4, T, Q, 0,2,1,1> E0 ## E2 ## E1 ## E1; }; \ + struct { detail::_swizzle<4, T, Q, 0,2,1,2> E0 ## E2 ## E1 ## E2; }; \ + struct { detail::_swizzle<4, T, Q, 0,2,1,3> E0 ## E2 ## E1 ## E3; }; \ + struct { detail::_swizzle<4, T, Q, 0,2,2,0> E0 ## E2 ## E2 ## E0; }; \ + struct { detail::_swizzle<4, T, Q, 0,2,2,1> E0 ## E2 ## E2 ## E1; }; \ + struct { detail::_swizzle<4, T, Q, 0,2,2,2> E0 ## E2 ## E2 ## E2; }; \ + struct { detail::_swizzle<4, T, Q, 0,2,2,3> E0 ## E2 ## E2 ## E3; }; \ + struct { detail::_swizzle<4, T, Q, 0,2,3,0> E0 ## E2 ## E3 ## E0; }; \ + struct { detail::_swizzle<4, T, Q, 0,2,3,1> E0 ## E2 ## E3 ## E1; }; \ + struct { detail::_swizzle<4, T, Q, 0,2,3,2> E0 ## E2 ## E3 ## E2; }; \ + struct { detail::_swizzle<4, T, Q, 0,2,3,3> E0 ## E2 ## E3 ## E3; }; \ + struct { detail::_swizzle<4, T, Q, 0,3,0,0> E0 ## E3 ## E0 ## E0; }; \ + struct { detail::_swizzle<4, T, Q, 0,3,0,1> E0 ## E3 ## E0 ## E1; }; \ + struct { detail::_swizzle<4, T, Q, 0,3,0,2> E0 ## E3 ## E0 ## E2; }; \ + struct { detail::_swizzle<4, T, Q, 0,3,0,3> E0 ## E3 ## E0 ## E3; }; \ + struct { detail::_swizzle<4, T, Q, 0,3,1,0> E0 ## E3 ## E1 ## E0; }; \ + struct { detail::_swizzle<4, T, Q, 0,3,1,1> E0 ## E3 ## E1 ## E1; }; \ + struct { detail::_swizzle<4, T, Q, 0,3,1,2> E0 ## E3 ## E1 ## E2; }; \ + struct { detail::_swizzle<4, T, Q, 0,3,1,3> E0 ## E3 ## E1 ## E3; }; \ + struct { detail::_swizzle<4, T, Q, 0,3,2,0> E0 ## E3 ## E2 ## E0; }; \ + struct { detail::_swizzle<4, T, Q, 0,3,2,1> E0 ## E3 ## E2 ## E1; }; \ + struct { detail::_swizzle<4, T, Q, 0,3,2,2> E0 ## E3 ## E2 ## E2; }; \ + struct { detail::_swizzle<4, T, Q, 0,3,2,3> E0 ## E3 ## E2 ## E3; }; \ + struct { detail::_swizzle<4, T, Q, 0,3,3,0> E0 ## E3 ## E3 ## E0; }; \ + struct { detail::_swizzle<4, T, Q, 0,3,3,1> E0 ## E3 ## E3 ## E1; }; \ + struct { detail::_swizzle<4, T, Q, 0,3,3,2> E0 ## E3 ## E3 ## E2; }; \ + struct { detail::_swizzle<4, T, Q, 0,3,3,3> E0 ## E3 ## E3 ## E3; }; \ + struct { detail::_swizzle<4, T, Q, 1,0,0,0> E1 ## E0 ## E0 ## E0; }; \ + struct { detail::_swizzle<4, T, Q, 1,0,0,1> E1 ## E0 ## E0 ## E1; }; \ + struct { detail::_swizzle<4, T, Q, 1,0,0,2> E1 ## E0 ## E0 ## E2; }; \ + struct { detail::_swizzle<4, T, Q, 1,0,0,3> E1 ## E0 ## E0 ## E3; }; \ + struct { detail::_swizzle<4, T, Q, 1,0,1,0> E1 ## E0 ## E1 ## E0; }; \ + struct { detail::_swizzle<4, T, Q, 1,0,1,1> E1 ## E0 ## E1 ## E1; }; \ + struct { detail::_swizzle<4, T, Q, 1,0,1,2> E1 ## E0 ## E1 ## E2; }; \ + struct { detail::_swizzle<4, T, Q, 1,0,1,3> E1 ## E0 ## E1 ## E3; }; \ + struct { detail::_swizzle<4, T, Q, 1,0,2,0> E1 ## E0 ## E2 ## E0; }; \ + struct { detail::_swizzle<4, T, Q, 1,0,2,1> E1 ## E0 ## E2 ## E1; }; \ + struct { detail::_swizzle<4, T, Q, 1,0,2,2> E1 ## E0 ## E2 ## E2; }; \ + struct { detail::_swizzle<4, T, Q, 1,0,2,3> E1 ## E0 ## E2 ## E3; }; \ + struct { detail::_swizzle<4, T, Q, 1,0,3,0> E1 ## E0 ## E3 ## E0; }; \ + struct { detail::_swizzle<4, T, Q, 1,0,3,1> E1 ## E0 ## E3 ## E1; }; \ + struct { detail::_swizzle<4, T, Q, 1,0,3,2> E1 ## E0 ## E3 ## E2; }; \ + struct { detail::_swizzle<4, T, Q, 1,0,3,3> E1 ## E0 ## E3 ## E3; }; \ + struct { detail::_swizzle<4, T, Q, 1,1,0,0> E1 ## E1 ## E0 ## E0; }; \ + struct { detail::_swizzle<4, T, Q, 1,1,0,1> E1 ## E1 ## E0 ## E1; }; \ + struct { detail::_swizzle<4, T, Q, 1,1,0,2> E1 ## E1 ## E0 ## E2; }; \ + struct { detail::_swizzle<4, T, Q, 1,1,0,3> E1 ## E1 ## E0 ## E3; }; \ + struct { detail::_swizzle<4, T, Q, 1,1,1,0> E1 ## E1 ## E1 ## E0; }; \ + struct { detail::_swizzle<4, T, Q, 1,1,1,1> E1 ## E1 ## E1 ## E1; }; \ + struct { detail::_swizzle<4, T, Q, 1,1,1,2> E1 ## E1 ## E1 ## E2; }; \ + struct { detail::_swizzle<4, T, Q, 1,1,1,3> E1 ## E1 ## E1 ## E3; }; \ + struct { detail::_swizzle<4, T, Q, 1,1,2,0> E1 ## E1 ## E2 ## E0; }; \ + struct { detail::_swizzle<4, T, Q, 1,1,2,1> E1 ## E1 ## E2 ## E1; }; \ + struct { detail::_swizzle<4, T, Q, 1,1,2,2> E1 ## E1 ## E2 ## E2; }; \ + struct { detail::_swizzle<4, T, Q, 1,1,2,3> E1 ## E1 ## E2 ## E3; }; \ + struct { detail::_swizzle<4, T, Q, 1,1,3,0> E1 ## E1 ## E3 ## E0; }; \ + struct { detail::_swizzle<4, T, Q, 1,1,3,1> E1 ## E1 ## E3 ## E1; }; \ + struct { detail::_swizzle<4, T, Q, 1,1,3,2> E1 ## E1 ## E3 ## E2; }; \ + struct { detail::_swizzle<4, T, Q, 1,1,3,3> E1 ## E1 ## E3 ## E3; }; \ + struct { detail::_swizzle<4, T, Q, 1,2,0,0> E1 ## E2 ## E0 ## E0; }; \ + struct { detail::_swizzle<4, T, Q, 1,2,0,1> E1 ## E2 ## E0 ## E1; }; \ + struct { detail::_swizzle<4, T, Q, 1,2,0,2> E1 ## E2 ## E0 ## E2; }; \ + struct { detail::_swizzle<4, T, Q, 1,2,0,3> E1 ## E2 ## E0 ## E3; }; \ + struct { detail::_swizzle<4, T, Q, 1,2,1,0> E1 ## E2 ## E1 ## E0; }; \ + struct { detail::_swizzle<4, T, Q, 1,2,1,1> E1 ## E2 ## E1 ## E1; }; \ + struct { detail::_swizzle<4, T, Q, 1,2,1,2> E1 ## E2 ## E1 ## E2; }; \ + struct { detail::_swizzle<4, T, Q, 1,2,1,3> E1 ## E2 ## E1 ## E3; }; \ + struct { detail::_swizzle<4, T, Q, 1,2,2,0> E1 ## E2 ## E2 ## E0; }; \ + struct { detail::_swizzle<4, T, Q, 1,2,2,1> E1 ## E2 ## E2 ## E1; }; \ + struct { detail::_swizzle<4, T, Q, 1,2,2,2> E1 ## E2 ## E2 ## E2; }; \ + struct { detail::_swizzle<4, T, Q, 1,2,2,3> E1 ## E2 ## E2 ## E3; }; \ + struct { detail::_swizzle<4, T, Q, 1,2,3,0> E1 ## E2 ## E3 ## E0; }; \ + struct { detail::_swizzle<4, T, Q, 1,2,3,1> E1 ## E2 ## E3 ## E1; }; \ + struct { detail::_swizzle<4, T, Q, 1,2,3,2> E1 ## E2 ## E3 ## E2; }; \ + struct { detail::_swizzle<4, T, Q, 1,2,3,3> E1 ## E2 ## E3 ## E3; }; \ + struct { detail::_swizzle<4, T, Q, 1,3,0,0> E1 ## E3 ## E0 ## E0; }; \ + struct { detail::_swizzle<4, T, Q, 1,3,0,1> E1 ## E3 ## E0 ## E1; }; \ + struct { detail::_swizzle<4, T, Q, 1,3,0,2> E1 ## E3 ## E0 ## E2; }; \ + struct { detail::_swizzle<4, T, Q, 1,3,0,3> E1 ## E3 ## E0 ## E3; }; \ + struct { detail::_swizzle<4, T, Q, 1,3,1,0> E1 ## E3 ## E1 ## E0; }; \ + struct { detail::_swizzle<4, T, Q, 1,3,1,1> E1 ## E3 ## E1 ## E1; }; \ + struct { detail::_swizzle<4, T, Q, 1,3,1,2> E1 ## E3 ## E1 ## E2; }; \ + struct { detail::_swizzle<4, T, Q, 1,3,1,3> E1 ## E3 ## E1 ## E3; }; \ + struct { detail::_swizzle<4, T, Q, 1,3,2,0> E1 ## E3 ## E2 ## E0; }; \ + struct { detail::_swizzle<4, T, Q, 1,3,2,1> E1 ## E3 ## E2 ## E1; }; \ + struct { detail::_swizzle<4, T, Q, 1,3,2,2> E1 ## E3 ## E2 ## E2; }; \ + struct { detail::_swizzle<4, T, Q, 1,3,2,3> E1 ## E3 ## E2 ## E3; }; \ + struct { detail::_swizzle<4, T, Q, 1,3,3,0> E1 ## E3 ## E3 ## E0; }; \ + struct { detail::_swizzle<4, T, Q, 1,3,3,1> E1 ## E3 ## E3 ## E1; }; \ + struct { detail::_swizzle<4, T, Q, 1,3,3,2> E1 ## E3 ## E3 ## E2; }; \ + struct { detail::_swizzle<4, T, Q, 1,3,3,3> E1 ## E3 ## E3 ## E3; }; \ + struct { detail::_swizzle<4, T, Q, 2,0,0,0> E2 ## E0 ## E0 ## E0; }; \ + struct { detail::_swizzle<4, T, Q, 2,0,0,1> E2 ## E0 ## E0 ## E1; }; \ + struct { detail::_swizzle<4, T, Q, 2,0,0,2> E2 ## E0 ## E0 ## E2; }; \ + struct { detail::_swizzle<4, T, Q, 2,0,0,3> E2 ## E0 ## E0 ## E3; }; \ + struct { detail::_swizzle<4, T, Q, 2,0,1,0> E2 ## E0 ## E1 ## E0; }; \ + struct { detail::_swizzle<4, T, Q, 2,0,1,1> E2 ## E0 ## E1 ## E1; }; \ + struct { detail::_swizzle<4, T, Q, 2,0,1,2> E2 ## E0 ## E1 ## E2; }; \ + struct { detail::_swizzle<4, T, Q, 2,0,1,3> E2 ## E0 ## E1 ## E3; }; \ + struct { detail::_swizzle<4, T, Q, 2,0,2,0> E2 ## E0 ## E2 ## E0; }; \ + struct { detail::_swizzle<4, T, Q, 2,0,2,1> E2 ## E0 ## E2 ## E1; }; \ + struct { detail::_swizzle<4, T, Q, 2,0,2,2> E2 ## E0 ## E2 ## E2; }; \ + struct { detail::_swizzle<4, T, Q, 2,0,2,3> E2 ## E0 ## E2 ## E3; }; \ + struct { detail::_swizzle<4, T, Q, 2,0,3,0> E2 ## E0 ## E3 ## E0; }; \ + struct { detail::_swizzle<4, T, Q, 2,0,3,1> E2 ## E0 ## E3 ## E1; }; \ + struct { detail::_swizzle<4, T, Q, 2,0,3,2> E2 ## E0 ## E3 ## E2; }; \ + struct { detail::_swizzle<4, T, Q, 2,0,3,3> E2 ## E0 ## E3 ## E3; }; \ + struct { detail::_swizzle<4, T, Q, 2,1,0,0> E2 ## E1 ## E0 ## E0; }; \ + struct { detail::_swizzle<4, T, Q, 2,1,0,1> E2 ## E1 ## E0 ## E1; }; \ + struct { detail::_swizzle<4, T, Q, 2,1,0,2> E2 ## E1 ## E0 ## E2; }; \ + struct { detail::_swizzle<4, T, Q, 2,1,0,3> E2 ## E1 ## E0 ## E3; }; \ + struct { detail::_swizzle<4, T, Q, 2,1,1,0> E2 ## E1 ## E1 ## E0; }; \ + struct { detail::_swizzle<4, T, Q, 2,1,1,1> E2 ## E1 ## E1 ## E1; }; \ + struct { detail::_swizzle<4, T, Q, 2,1,1,2> E2 ## E1 ## E1 ## E2; }; \ + struct { detail::_swizzle<4, T, Q, 2,1,1,3> E2 ## E1 ## E1 ## E3; }; \ + struct { detail::_swizzle<4, T, Q, 2,1,2,0> E2 ## E1 ## E2 ## E0; }; \ + struct { detail::_swizzle<4, T, Q, 2,1,2,1> E2 ## E1 ## E2 ## E1; }; \ + struct { detail::_swizzle<4, T, Q, 2,1,2,2> E2 ## E1 ## E2 ## E2; }; \ + struct { detail::_swizzle<4, T, Q, 2,1,2,3> E2 ## E1 ## E2 ## E3; }; \ + struct { detail::_swizzle<4, T, Q, 2,1,3,0> E2 ## E1 ## E3 ## E0; }; \ + struct { detail::_swizzle<4, T, Q, 2,1,3,1> E2 ## E1 ## E3 ## E1; }; \ + struct { detail::_swizzle<4, T, Q, 2,1,3,2> E2 ## E1 ## E3 ## E2; }; \ + struct { detail::_swizzle<4, T, Q, 2,1,3,3> E2 ## E1 ## E3 ## E3; }; \ + struct { detail::_swizzle<4, T, Q, 2,2,0,0> E2 ## E2 ## E0 ## E0; }; \ + struct { detail::_swizzle<4, T, Q, 2,2,0,1> E2 ## E2 ## E0 ## E1; }; \ + struct { detail::_swizzle<4, T, Q, 2,2,0,2> E2 ## E2 ## E0 ## E2; }; \ + struct { detail::_swizzle<4, T, Q, 2,2,0,3> E2 ## E2 ## E0 ## E3; }; \ + struct { detail::_swizzle<4, T, Q, 2,2,1,0> E2 ## E2 ## E1 ## E0; }; \ + struct { detail::_swizzle<4, T, Q, 2,2,1,1> E2 ## E2 ## E1 ## E1; }; \ + struct { detail::_swizzle<4, T, Q, 2,2,1,2> E2 ## E2 ## E1 ## E2; }; \ + struct { detail::_swizzle<4, T, Q, 2,2,1,3> E2 ## E2 ## E1 ## E3; }; \ + struct { detail::_swizzle<4, T, Q, 2,2,2,0> E2 ## E2 ## E2 ## E0; }; \ + struct { detail::_swizzle<4, T, Q, 2,2,2,1> E2 ## E2 ## E2 ## E1; }; \ + struct { detail::_swizzle<4, T, Q, 2,2,2,2> E2 ## E2 ## E2 ## E2; }; \ + struct { detail::_swizzle<4, T, Q, 2,2,2,3> E2 ## E2 ## E2 ## E3; }; \ + struct { detail::_swizzle<4, T, Q, 2,2,3,0> E2 ## E2 ## E3 ## E0; }; \ + struct { detail::_swizzle<4, T, Q, 2,2,3,1> E2 ## E2 ## E3 ## E1; }; \ + struct { detail::_swizzle<4, T, Q, 2,2,3,2> E2 ## E2 ## E3 ## E2; }; \ + struct { detail::_swizzle<4, T, Q, 2,2,3,3> E2 ## E2 ## E3 ## E3; }; \ + struct { detail::_swizzle<4, T, Q, 2,3,0,0> E2 ## E3 ## E0 ## E0; }; \ + struct { detail::_swizzle<4, T, Q, 2,3,0,1> E2 ## E3 ## E0 ## E1; }; \ + struct { detail::_swizzle<4, T, Q, 2,3,0,2> E2 ## E3 ## E0 ## E2; }; \ + struct { detail::_swizzle<4, T, Q, 2,3,0,3> E2 ## E3 ## E0 ## E3; }; \ + struct { detail::_swizzle<4, T, Q, 2,3,1,0> E2 ## E3 ## E1 ## E0; }; \ + struct { detail::_swizzle<4, T, Q, 2,3,1,1> E2 ## E3 ## E1 ## E1; }; \ + struct { detail::_swizzle<4, T, Q, 2,3,1,2> E2 ## E3 ## E1 ## E2; }; \ + struct { detail::_swizzle<4, T, Q, 2,3,1,3> E2 ## E3 ## E1 ## E3; }; \ + struct { detail::_swizzle<4, T, Q, 2,3,2,0> E2 ## E3 ## E2 ## E0; }; \ + struct { detail::_swizzle<4, T, Q, 2,3,2,1> E2 ## E3 ## E2 ## E1; }; \ + struct { detail::_swizzle<4, T, Q, 2,3,2,2> E2 ## E3 ## E2 ## E2; }; \ + struct { detail::_swizzle<4, T, Q, 2,3,2,3> E2 ## E3 ## E2 ## E3; }; \ + struct { detail::_swizzle<4, T, Q, 2,3,3,0> E2 ## E3 ## E3 ## E0; }; \ + struct { detail::_swizzle<4, T, Q, 2,3,3,1> E2 ## E3 ## E3 ## E1; }; \ + struct { detail::_swizzle<4, T, Q, 2,3,3,2> E2 ## E3 ## E3 ## E2; }; \ + struct { detail::_swizzle<4, T, Q, 2,3,3,3> E2 ## E3 ## E3 ## E3; }; \ + struct { detail::_swizzle<4, T, Q, 3,0,0,0> E3 ## E0 ## E0 ## E0; }; \ + struct { detail::_swizzle<4, T, Q, 3,0,0,1> E3 ## E0 ## E0 ## E1; }; \ + struct { detail::_swizzle<4, T, Q, 3,0,0,2> E3 ## E0 ## E0 ## E2; }; \ + struct { detail::_swizzle<4, T, Q, 3,0,0,3> E3 ## E0 ## E0 ## E3; }; \ + struct { detail::_swizzle<4, T, Q, 3,0,1,0> E3 ## E0 ## E1 ## E0; }; \ + struct { detail::_swizzle<4, T, Q, 3,0,1,1> E3 ## E0 ## E1 ## E1; }; \ + struct { detail::_swizzle<4, T, Q, 3,0,1,2> E3 ## E0 ## E1 ## E2; }; \ + struct { detail::_swizzle<4, T, Q, 3,0,1,3> E3 ## E0 ## E1 ## E3; }; \ + struct { detail::_swizzle<4, T, Q, 3,0,2,0> E3 ## E0 ## E2 ## E0; }; \ + struct { detail::_swizzle<4, T, Q, 3,0,2,1> E3 ## E0 ## E2 ## E1; }; \ + struct { detail::_swizzle<4, T, Q, 3,0,2,2> E3 ## E0 ## E2 ## E2; }; \ + struct { detail::_swizzle<4, T, Q, 3,0,2,3> E3 ## E0 ## E2 ## E3; }; \ + struct { detail::_swizzle<4, T, Q, 3,0,3,0> E3 ## E0 ## E3 ## E0; }; \ + struct { detail::_swizzle<4, T, Q, 3,0,3,1> E3 ## E0 ## E3 ## E1; }; \ + struct { detail::_swizzle<4, T, Q, 3,0,3,2> E3 ## E0 ## E3 ## E2; }; \ + struct { detail::_swizzle<4, T, Q, 3,0,3,3> E3 ## E0 ## E3 ## E3; }; \ + struct { detail::_swizzle<4, T, Q, 3,1,0,0> E3 ## E1 ## E0 ## E0; }; \ + struct { detail::_swizzle<4, T, Q, 3,1,0,1> E3 ## E1 ## E0 ## E1; }; \ + struct { detail::_swizzle<4, T, Q, 3,1,0,2> E3 ## E1 ## E0 ## E2; }; \ + struct { detail::_swizzle<4, T, Q, 3,1,0,3> E3 ## E1 ## E0 ## E3; }; \ + struct { detail::_swizzle<4, T, Q, 3,1,1,0> E3 ## E1 ## E1 ## E0; }; \ + struct { detail::_swizzle<4, T, Q, 3,1,1,1> E3 ## E1 ## E1 ## E1; }; \ + struct { detail::_swizzle<4, T, Q, 3,1,1,2> E3 ## E1 ## E1 ## E2; }; \ + struct { detail::_swizzle<4, T, Q, 3,1,1,3> E3 ## E1 ## E1 ## E3; }; \ + struct { detail::_swizzle<4, T, Q, 3,1,2,0> E3 ## E1 ## E2 ## E0; }; \ + struct { detail::_swizzle<4, T, Q, 3,1,2,1> E3 ## E1 ## E2 ## E1; }; \ + struct { detail::_swizzle<4, T, Q, 3,1,2,2> E3 ## E1 ## E2 ## E2; }; \ + struct { detail::_swizzle<4, T, Q, 3,1,2,3> E3 ## E1 ## E2 ## E3; }; \ + struct { detail::_swizzle<4, T, Q, 3,1,3,0> E3 ## E1 ## E3 ## E0; }; \ + struct { detail::_swizzle<4, T, Q, 3,1,3,1> E3 ## E1 ## E3 ## E1; }; \ + struct { detail::_swizzle<4, T, Q, 3,1,3,2> E3 ## E1 ## E3 ## E2; }; \ + struct { detail::_swizzle<4, T, Q, 3,1,3,3> E3 ## E1 ## E3 ## E3; }; \ + struct { detail::_swizzle<4, T, Q, 3,2,0,0> E3 ## E2 ## E0 ## E0; }; \ + struct { detail::_swizzle<4, T, Q, 3,2,0,1> E3 ## E2 ## E0 ## E1; }; \ + struct { detail::_swizzle<4, T, Q, 3,2,0,2> E3 ## E2 ## E0 ## E2; }; \ + struct { detail::_swizzle<4, T, Q, 3,2,0,3> E3 ## E2 ## E0 ## E3; }; \ + struct { detail::_swizzle<4, T, Q, 3,2,1,0> E3 ## E2 ## E1 ## E0; }; \ + struct { detail::_swizzle<4, T, Q, 3,2,1,1> E3 ## E2 ## E1 ## E1; }; \ + struct { detail::_swizzle<4, T, Q, 3,2,1,2> E3 ## E2 ## E1 ## E2; }; \ + struct { detail::_swizzle<4, T, Q, 3,2,1,3> E3 ## E2 ## E1 ## E3; }; \ + struct { detail::_swizzle<4, T, Q, 3,2,2,0> E3 ## E2 ## E2 ## E0; }; \ + struct { detail::_swizzle<4, T, Q, 3,2,2,1> E3 ## E2 ## E2 ## E1; }; \ + struct { detail::_swizzle<4, T, Q, 3,2,2,2> E3 ## E2 ## E2 ## E2; }; \ + struct { detail::_swizzle<4, T, Q, 3,2,2,3> E3 ## E2 ## E2 ## E3; }; \ + struct { detail::_swizzle<4, T, Q, 3,2,3,0> E3 ## E2 ## E3 ## E0; }; \ + struct { detail::_swizzle<4, T, Q, 3,2,3,1> E3 ## E2 ## E3 ## E1; }; \ + struct { detail::_swizzle<4, T, Q, 3,2,3,2> E3 ## E2 ## E3 ## E2; }; \ + struct { detail::_swizzle<4, T, Q, 3,2,3,3> E3 ## E2 ## E3 ## E3; }; \ + struct { detail::_swizzle<4, T, Q, 3,3,0,0> E3 ## E3 ## E0 ## E0; }; \ + struct { detail::_swizzle<4, T, Q, 3,3,0,1> E3 ## E3 ## E0 ## E1; }; \ + struct { detail::_swizzle<4, T, Q, 3,3,0,2> E3 ## E3 ## E0 ## E2; }; \ + struct { detail::_swizzle<4, T, Q, 3,3,0,3> E3 ## E3 ## E0 ## E3; }; \ + struct { detail::_swizzle<4, T, Q, 3,3,1,0> E3 ## E3 ## E1 ## E0; }; \ + struct { detail::_swizzle<4, T, Q, 3,3,1,1> E3 ## E3 ## E1 ## E1; }; \ + struct { detail::_swizzle<4, T, Q, 3,3,1,2> E3 ## E3 ## E1 ## E2; }; \ + struct { detail::_swizzle<4, T, Q, 3,3,1,3> E3 ## E3 ## E1 ## E3; }; \ + struct { detail::_swizzle<4, T, Q, 3,3,2,0> E3 ## E3 ## E2 ## E0; }; \ + struct { detail::_swizzle<4, T, Q, 3,3,2,1> E3 ## E3 ## E2 ## E1; }; \ + struct { detail::_swizzle<4, T, Q, 3,3,2,2> E3 ## E3 ## E2 ## E2; }; \ + struct { detail::_swizzle<4, T, Q, 3,3,2,3> E3 ## E3 ## E2 ## E3; }; \ + struct { detail::_swizzle<4, T, Q, 3,3,3,0> E3 ## E3 ## E3 ## E0; }; \ + struct { detail::_swizzle<4, T, Q, 3,3,3,1> E3 ## E3 ## E3 ## E1; }; \ + struct { detail::_swizzle<4, T, Q, 3,3,3,2> E3 ## E3 ## E3 ## E2; }; \ + struct { detail::_swizzle<4, T, Q, 3,3,3,3> E3 ## E3 ## E3 ## E3; }; diff --git a/src/GLMath/glm/detail/_swizzle_func.hpp b/src/GLMath/glm/detail/_swizzle_func.hpp new file mode 100644 index 000000000..d93c6afd5 --- /dev/null +++ b/src/GLMath/glm/detail/_swizzle_func.hpp @@ -0,0 +1,682 @@ +#pragma once + +#define GLM_SWIZZLE_GEN_VEC2_ENTRY(T, P, CONST, A, B) \ + vec<2, T, Q> A ## B() CONST \ + { \ + return vec<2, T, Q>(this->A, this->B); \ + } + +#define GLM_SWIZZLE_GEN_VEC3_ENTRY(T, P, CONST, A, B, C) \ + vec<3, T, Q> A ## B ## C() CONST \ + { \ + return vec<3, T, Q>(this->A, this->B, this->C); \ + } + +#define GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, CONST, A, B, C, D) \ + vec<4, T, Q> A ## B ## C ## D() CONST \ + { \ + return vec<4, T, Q>(this->A, this->B, this->C, this->D); \ + } + +#define GLM_SWIZZLE_GEN_VEC2_ENTRY_DEF(T, P, L, CONST, A, B) \ + template \ + vec vec::A ## B() CONST \ + { \ + return vec<2, T, Q>(this->A, this->B); \ + } + +#define GLM_SWIZZLE_GEN_VEC3_ENTRY_DEF(T, P, L, CONST, A, B, C) \ + template \ + vec<3, T, Q> vec::A ## B ## C() CONST \ + { \ + return vec<3, T, Q>(this->A, this->B, this->C); \ + } + +#define GLM_SWIZZLE_GEN_VEC4_ENTRY_DEF(T, P, L, CONST, A, B, C, D) \ + template \ + vec<4, T, Q> vec::A ## B ## C ## D() CONST \ + { \ + return vec<4, T, Q>(this->A, this->B, this->C, this->D); \ + } + +#define GLM_MUTABLE + +#define GLM_SWIZZLE_GEN_REF2_FROM_VEC2_SWIZZLE(T, P, A, B) \ + GLM_SWIZZLE_GEN_VEC2_ENTRY(T, P, 2, GLM_MUTABLE, A, B) \ + GLM_SWIZZLE_GEN_VEC2_ENTRY(T, P, 2, GLM_MUTABLE, B, A) + +#define GLM_SWIZZLE_GEN_REF_FROM_VEC2(T, P) \ + GLM_SWIZZLE_GEN_REF2_FROM_VEC2_SWIZZLE(T, P, x, y) \ + GLM_SWIZZLE_GEN_REF2_FROM_VEC2_SWIZZLE(T, P, r, g) \ + GLM_SWIZZLE_GEN_REF2_FROM_VEC2_SWIZZLE(T, P, s, t) + +#define GLM_SWIZZLE_GEN_REF2_FROM_VEC3_SWIZZLE(T, P, A, B, C) \ + GLM_SWIZZLE_GEN_VEC2_ENTRY(T, P, GLM_MUTABLE, A, B) \ + GLM_SWIZZLE_GEN_VEC2_ENTRY(T, P, GLM_MUTABLE, A, C) \ + GLM_SWIZZLE_GEN_VEC2_ENTRY(T, P, GLM_MUTABLE, B, A) \ + GLM_SWIZZLE_GEN_VEC2_ENTRY(T, P, GLM_MUTABLE, B, C) \ + GLM_SWIZZLE_GEN_VEC2_ENTRY(T, P, GLM_MUTABLE, C, A) \ + GLM_SWIZZLE_GEN_VEC2_ENTRY(T, P, GLM_MUTABLE, C, B) + +#define GLM_SWIZZLE_GEN_REF3_FROM_VEC3_SWIZZLE(T, P, A, B, C) \ + GLM_SWIZZLE_GEN_VEC3_ENTRY(T, P, GLM_MUTABLE, A, B, C) \ + GLM_SWIZZLE_GEN_VEC3_ENTRY(T, P, GLM_MUTABLE, A, C, B) \ + GLM_SWIZZLE_GEN_VEC3_ENTRY(T, P, GLM_MUTABLE, B, A, C) \ + GLM_SWIZZLE_GEN_VEC3_ENTRY(T, P, GLM_MUTABLE, B, C, A) \ + GLM_SWIZZLE_GEN_VEC3_ENTRY(T, P, GLM_MUTABLE, C, A, B) \ + GLM_SWIZZLE_GEN_VEC3_ENTRY(T, P, GLM_MUTABLE, C, B, A) + +#define GLM_SWIZZLE_GEN_REF_FROM_VEC3_COMP(T, P, A, B, C) \ + GLM_SWIZZLE_GEN_REF3_FROM_VEC3_SWIZZLE(T, P, A, B, C) \ + GLM_SWIZZLE_GEN_REF2_FROM_VEC3_SWIZZLE(T, P, A, B, C) + +#define GLM_SWIZZLE_GEN_REF_FROM_VEC3(T, P) \ + GLM_SWIZZLE_GEN_REF_FROM_VEC3_COMP(T, P, x, y, z) \ + GLM_SWIZZLE_GEN_REF_FROM_VEC3_COMP(T, P, r, g, b) \ + GLM_SWIZZLE_GEN_REF_FROM_VEC3_COMP(T, P, s, t, p) + +#define GLM_SWIZZLE_GEN_REF2_FROM_VEC4_SWIZZLE(T, P, A, B, C, D) \ + GLM_SWIZZLE_GEN_VEC2_ENTRY(T, P, GLM_MUTABLE, A, B) \ + GLM_SWIZZLE_GEN_VEC2_ENTRY(T, P, GLM_MUTABLE, A, C) \ + GLM_SWIZZLE_GEN_VEC2_ENTRY(T, P, GLM_MUTABLE, A, D) \ + GLM_SWIZZLE_GEN_VEC2_ENTRY(T, P, GLM_MUTABLE, B, A) \ + GLM_SWIZZLE_GEN_VEC2_ENTRY(T, P, GLM_MUTABLE, B, C) \ + GLM_SWIZZLE_GEN_VEC2_ENTRY(T, P, GLM_MUTABLE, B, D) \ + GLM_SWIZZLE_GEN_VEC2_ENTRY(T, P, GLM_MUTABLE, C, A) \ + GLM_SWIZZLE_GEN_VEC2_ENTRY(T, P, GLM_MUTABLE, C, B) \ + GLM_SWIZZLE_GEN_VEC2_ENTRY(T, P, GLM_MUTABLE, C, D) \ + GLM_SWIZZLE_GEN_VEC2_ENTRY(T, P, GLM_MUTABLE, D, A) \ + GLM_SWIZZLE_GEN_VEC2_ENTRY(T, P, GLM_MUTABLE, D, B) \ + GLM_SWIZZLE_GEN_VEC2_ENTRY(T, P, GLM_MUTABLE, D, C) + +#define GLM_SWIZZLE_GEN_REF3_FROM_VEC4_SWIZZLE(T, P, A, B, C, D) \ + GLM_SWIZZLE_GEN_VEC3_ENTRY(T, P, , A, B, C) \ + GLM_SWIZZLE_GEN_VEC3_ENTRY(T, P, , A, B, D) \ + GLM_SWIZZLE_GEN_VEC3_ENTRY(T, P, , A, C, B) \ + GLM_SWIZZLE_GEN_VEC3_ENTRY(T, P, , A, C, D) \ + GLM_SWIZZLE_GEN_VEC3_ENTRY(T, P, , A, D, B) \ + GLM_SWIZZLE_GEN_VEC3_ENTRY(T, P, , A, D, C) \ + GLM_SWIZZLE_GEN_VEC3_ENTRY(T, P, , B, A, C) \ + GLM_SWIZZLE_GEN_VEC3_ENTRY(T, P, , B, A, D) \ + GLM_SWIZZLE_GEN_VEC3_ENTRY(T, P, , B, C, A) \ + GLM_SWIZZLE_GEN_VEC3_ENTRY(T, P, , B, C, D) \ + GLM_SWIZZLE_GEN_VEC3_ENTRY(T, P, , B, D, A) \ + GLM_SWIZZLE_GEN_VEC3_ENTRY(T, P, , B, D, C) \ + GLM_SWIZZLE_GEN_VEC3_ENTRY(T, P, , C, A, B) \ + GLM_SWIZZLE_GEN_VEC3_ENTRY(T, P, , C, A, D) \ + GLM_SWIZZLE_GEN_VEC3_ENTRY(T, P, , C, B, A) \ + GLM_SWIZZLE_GEN_VEC3_ENTRY(T, P, , C, B, D) \ + GLM_SWIZZLE_GEN_VEC3_ENTRY(T, P, , C, D, A) \ + GLM_SWIZZLE_GEN_VEC3_ENTRY(T, P, , C, D, B) \ + GLM_SWIZZLE_GEN_VEC3_ENTRY(T, P, , D, A, B) \ + GLM_SWIZZLE_GEN_VEC3_ENTRY(T, P, , D, A, C) \ + GLM_SWIZZLE_GEN_VEC3_ENTRY(T, P, , D, B, A) \ + GLM_SWIZZLE_GEN_VEC3_ENTRY(T, P, , D, B, C) \ + GLM_SWIZZLE_GEN_VEC3_ENTRY(T, P, , D, C, A) \ + GLM_SWIZZLE_GEN_VEC3_ENTRY(T, P, , D, C, B) + +#define GLM_SWIZZLE_GEN_REF4_FROM_VEC4_SWIZZLE(T, P, A, B, C, D) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, , A, C, B, D) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, , A, C, D, B) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, , A, D, B, C) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, , A, D, C, B) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, , A, B, D, C) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, , A, B, C, D) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, , B, C, A, D) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, , B, C, D, A) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, , B, D, A, C) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, , B, D, C, A) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, , B, A, D, C) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, , B, A, C, D) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, , C, B, A, D) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, , C, B, D, A) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, , C, D, A, B) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, , C, D, B, A) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, , C, A, D, B) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, , C, A, B, D) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, , D, C, B, A) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, , D, C, A, B) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, , D, A, B, C) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, , D, A, C, B) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, , D, B, A, C) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, , D, B, C, A) + +#define GLM_SWIZZLE_GEN_REF_FROM_VEC4_COMP(T, P, A, B, C, D) \ + GLM_SWIZZLE_GEN_REF2_FROM_VEC4_SWIZZLE(T, P, A, B, C, D) \ + GLM_SWIZZLE_GEN_REF3_FROM_VEC4_SWIZZLE(T, P, A, B, C, D) \ + GLM_SWIZZLE_GEN_REF4_FROM_VEC4_SWIZZLE(T, P, A, B, C, D) + +#define GLM_SWIZZLE_GEN_REF_FROM_VEC4(T, P) \ + GLM_SWIZZLE_GEN_REF_FROM_VEC4_COMP(T, P, x, y, z, w) \ + GLM_SWIZZLE_GEN_REF_FROM_VEC4_COMP(T, P, r, g, b, a) \ + GLM_SWIZZLE_GEN_REF_FROM_VEC4_COMP(T, P, s, t, p, q) + +#define GLM_SWIZZLE_GEN_VEC2_FROM_VEC2_SWIZZLE(T, P, A, B) \ + GLM_SWIZZLE_GEN_VEC2_ENTRY(T, P, const, A, A) \ + GLM_SWIZZLE_GEN_VEC2_ENTRY(T, P, const, A, B) \ + GLM_SWIZZLE_GEN_VEC2_ENTRY(T, P, const, B, A) \ + GLM_SWIZZLE_GEN_VEC2_ENTRY(T, P, const, B, B) + +#define GLM_SWIZZLE_GEN_VEC3_FROM_VEC2_SWIZZLE(T, P, A, B) \ + GLM_SWIZZLE_GEN_VEC3_ENTRY(T, P, const, A, A, A) \ + GLM_SWIZZLE_GEN_VEC3_ENTRY(T, P, const, A, A, B) \ + GLM_SWIZZLE_GEN_VEC3_ENTRY(T, P, const, A, B, A) \ + GLM_SWIZZLE_GEN_VEC3_ENTRY(T, P, const, A, B, B) \ + GLM_SWIZZLE_GEN_VEC3_ENTRY(T, P, const, B, A, A) \ + GLM_SWIZZLE_GEN_VEC3_ENTRY(T, P, const, B, A, B) \ + GLM_SWIZZLE_GEN_VEC3_ENTRY(T, P, const, B, B, A) \ + GLM_SWIZZLE_GEN_VEC3_ENTRY(T, P, const, B, B, B) + +#define GLM_SWIZZLE_GEN_VEC4_FROM_VEC2_SWIZZLE(T, P, A, B) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, A, A, A, A) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, A, A, A, B) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, A, A, B, A) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, A, A, B, B) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, A, B, A, A) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, A, B, A, B) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, A, B, B, A) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, A, B, B, B) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, B, A, A, A) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, B, A, A, B) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, B, A, B, A) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, B, A, B, B) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, B, B, A, A) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, B, B, A, B) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, B, B, B, A) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, B, B, B, B) + +#define GLM_SWIZZLE_GEN_VEC_FROM_VEC2_COMP(T, P, A, B) \ + GLM_SWIZZLE_GEN_VEC2_FROM_VEC2_SWIZZLE(T, P, A, B) \ + GLM_SWIZZLE_GEN_VEC3_FROM_VEC2_SWIZZLE(T, P, A, B) \ + GLM_SWIZZLE_GEN_VEC4_FROM_VEC2_SWIZZLE(T, P, A, B) + +#define GLM_SWIZZLE_GEN_VEC_FROM_VEC2(T, P) \ + GLM_SWIZZLE_GEN_VEC_FROM_VEC2_COMP(T, P, x, y) \ + GLM_SWIZZLE_GEN_VEC_FROM_VEC2_COMP(T, P, r, g) \ + GLM_SWIZZLE_GEN_VEC_FROM_VEC2_COMP(T, P, s, t) + +#define GLM_SWIZZLE_GEN_VEC2_FROM_VEC3_SWIZZLE(T, P, A, B, C) \ + GLM_SWIZZLE_GEN_VEC2_ENTRY(T, P, const, A, A) \ + GLM_SWIZZLE_GEN_VEC2_ENTRY(T, P, const, A, B) \ + GLM_SWIZZLE_GEN_VEC2_ENTRY(T, P, const, A, C) \ + GLM_SWIZZLE_GEN_VEC2_ENTRY(T, P, const, B, A) \ + GLM_SWIZZLE_GEN_VEC2_ENTRY(T, P, const, B, B) \ + GLM_SWIZZLE_GEN_VEC2_ENTRY(T, P, const, B, C) \ + GLM_SWIZZLE_GEN_VEC2_ENTRY(T, P, const, C, A) \ + GLM_SWIZZLE_GEN_VEC2_ENTRY(T, P, const, C, B) \ + GLM_SWIZZLE_GEN_VEC2_ENTRY(T, P, const, C, C) + +#define GLM_SWIZZLE_GEN_VEC3_FROM_VEC3_SWIZZLE(T, P, A, B, C) \ + GLM_SWIZZLE_GEN_VEC3_ENTRY(T, P, const, A, A, A) \ + GLM_SWIZZLE_GEN_VEC3_ENTRY(T, P, const, A, A, B) \ + GLM_SWIZZLE_GEN_VEC3_ENTRY(T, P, const, A, A, C) \ + GLM_SWIZZLE_GEN_VEC3_ENTRY(T, P, const, A, B, A) \ + GLM_SWIZZLE_GEN_VEC3_ENTRY(T, P, const, A, B, B) \ + GLM_SWIZZLE_GEN_VEC3_ENTRY(T, P, const, A, B, C) \ + GLM_SWIZZLE_GEN_VEC3_ENTRY(T, P, const, A, C, A) \ + GLM_SWIZZLE_GEN_VEC3_ENTRY(T, P, const, A, C, B) \ + GLM_SWIZZLE_GEN_VEC3_ENTRY(T, P, const, A, C, C) \ + GLM_SWIZZLE_GEN_VEC3_ENTRY(T, P, const, B, A, A) \ + GLM_SWIZZLE_GEN_VEC3_ENTRY(T, P, const, B, A, B) \ + GLM_SWIZZLE_GEN_VEC3_ENTRY(T, P, const, B, A, C) \ + GLM_SWIZZLE_GEN_VEC3_ENTRY(T, P, const, B, B, A) \ + GLM_SWIZZLE_GEN_VEC3_ENTRY(T, P, const, B, B, B) \ + GLM_SWIZZLE_GEN_VEC3_ENTRY(T, P, const, B, B, C) \ + GLM_SWIZZLE_GEN_VEC3_ENTRY(T, P, const, B, C, A) \ + GLM_SWIZZLE_GEN_VEC3_ENTRY(T, P, const, B, C, B) \ + GLM_SWIZZLE_GEN_VEC3_ENTRY(T, P, const, B, C, C) \ + GLM_SWIZZLE_GEN_VEC3_ENTRY(T, P, const, C, A, A) \ + GLM_SWIZZLE_GEN_VEC3_ENTRY(T, P, const, C, A, B) \ + GLM_SWIZZLE_GEN_VEC3_ENTRY(T, P, const, C, A, C) \ + GLM_SWIZZLE_GEN_VEC3_ENTRY(T, P, const, C, B, A) \ + GLM_SWIZZLE_GEN_VEC3_ENTRY(T, P, const, C, B, B) \ + GLM_SWIZZLE_GEN_VEC3_ENTRY(T, P, const, C, B, C) \ + GLM_SWIZZLE_GEN_VEC3_ENTRY(T, P, const, C, C, A) \ + GLM_SWIZZLE_GEN_VEC3_ENTRY(T, P, const, C, C, B) \ + GLM_SWIZZLE_GEN_VEC3_ENTRY(T, P, const, C, C, C) + +#define GLM_SWIZZLE_GEN_VEC4_FROM_VEC3_SWIZZLE(T, P, A, B, C) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, A, A, A, A) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, A, A, A, B) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, A, A, A, C) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, A, A, B, A) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, A, A, B, B) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, A, A, B, C) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, A, A, C, A) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, A, A, C, B) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, A, A, C, C) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, A, B, A, A) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, A, B, A, B) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, A, B, A, C) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, A, B, B, A) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, A, B, B, B) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, A, B, B, C) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, A, B, C, A) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, A, B, C, B) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, A, B, C, C) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, A, C, A, A) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, A, C, A, B) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, A, C, A, C) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, A, C, B, A) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, A, C, B, B) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, A, C, B, C) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, A, C, C, A) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, A, C, C, B) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, A, C, C, C) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, B, A, A, A) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, B, A, A, B) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, B, A, A, C) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, B, A, B, A) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, B, A, B, B) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, B, A, B, C) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, B, A, C, A) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, B, A, C, B) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, B, A, C, C) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, B, B, A, A) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, B, B, A, B) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, B, B, A, C) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, B, B, B, A) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, B, B, B, B) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, B, B, B, C) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, B, B, C, A) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, B, B, C, B) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, B, B, C, C) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, B, C, A, A) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, B, C, A, B) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, B, C, A, C) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, B, C, B, A) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, B, C, B, B) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, B, C, B, C) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, B, C, C, A) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, B, C, C, B) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, B, C, C, C) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, C, A, A, A) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, C, A, A, B) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, C, A, A, C) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, C, A, B, A) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, C, A, B, B) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, C, A, B, C) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, C, A, C, A) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, C, A, C, B) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, C, A, C, C) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, C, B, A, A) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, C, B, A, B) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, C, B, A, C) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, C, B, B, A) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, C, B, B, B) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, C, B, B, C) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, C, B, C, A) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, C, B, C, B) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, C, B, C, C) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, C, C, A, A) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, C, C, A, B) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, C, C, A, C) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, C, C, B, A) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, C, C, B, B) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, C, C, B, C) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, C, C, C, A) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, C, C, C, B) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, C, C, C, C) + +#define GLM_SWIZZLE_GEN_VEC_FROM_VEC3_COMP(T, P, A, B, C) \ + GLM_SWIZZLE_GEN_VEC2_FROM_VEC3_SWIZZLE(T, P, A, B, C) \ + GLM_SWIZZLE_GEN_VEC3_FROM_VEC3_SWIZZLE(T, P, A, B, C) \ + GLM_SWIZZLE_GEN_VEC4_FROM_VEC3_SWIZZLE(T, P, A, B, C) + +#define GLM_SWIZZLE_GEN_VEC_FROM_VEC3(T, P) \ + GLM_SWIZZLE_GEN_VEC_FROM_VEC3_COMP(T, P, x, y, z) \ + GLM_SWIZZLE_GEN_VEC_FROM_VEC3_COMP(T, P, r, g, b) \ + GLM_SWIZZLE_GEN_VEC_FROM_VEC3_COMP(T, P, s, t, p) + +#define GLM_SWIZZLE_GEN_VEC2_FROM_VEC4_SWIZZLE(T, P, A, B, C, D) \ + GLM_SWIZZLE_GEN_VEC2_ENTRY(T, P, const, A, A) \ + GLM_SWIZZLE_GEN_VEC2_ENTRY(T, P, const, A, B) \ + GLM_SWIZZLE_GEN_VEC2_ENTRY(T, P, const, A, C) \ + GLM_SWIZZLE_GEN_VEC2_ENTRY(T, P, const, A, D) \ + GLM_SWIZZLE_GEN_VEC2_ENTRY(T, P, const, B, A) \ + GLM_SWIZZLE_GEN_VEC2_ENTRY(T, P, const, B, B) \ + GLM_SWIZZLE_GEN_VEC2_ENTRY(T, P, const, B, C) \ + GLM_SWIZZLE_GEN_VEC2_ENTRY(T, P, const, B, D) \ + GLM_SWIZZLE_GEN_VEC2_ENTRY(T, P, const, C, A) \ + GLM_SWIZZLE_GEN_VEC2_ENTRY(T, P, const, C, B) \ + GLM_SWIZZLE_GEN_VEC2_ENTRY(T, P, const, C, C) \ + GLM_SWIZZLE_GEN_VEC2_ENTRY(T, P, const, C, D) \ + GLM_SWIZZLE_GEN_VEC2_ENTRY(T, P, const, D, A) \ + GLM_SWIZZLE_GEN_VEC2_ENTRY(T, P, const, D, B) \ + GLM_SWIZZLE_GEN_VEC2_ENTRY(T, P, const, D, C) \ + GLM_SWIZZLE_GEN_VEC2_ENTRY(T, P, const, D, D) + +#define GLM_SWIZZLE_GEN_VEC3_FROM_VEC4_SWIZZLE(T, P, A, B, C, D) \ + GLM_SWIZZLE_GEN_VEC3_ENTRY(T, P, const, A, A, A) \ + GLM_SWIZZLE_GEN_VEC3_ENTRY(T, P, const, A, A, B) \ + GLM_SWIZZLE_GEN_VEC3_ENTRY(T, P, const, A, A, C) \ + GLM_SWIZZLE_GEN_VEC3_ENTRY(T, P, const, A, A, D) \ + GLM_SWIZZLE_GEN_VEC3_ENTRY(T, P, const, A, B, A) \ + GLM_SWIZZLE_GEN_VEC3_ENTRY(T, P, const, A, B, B) \ + GLM_SWIZZLE_GEN_VEC3_ENTRY(T, P, const, A, B, C) \ + GLM_SWIZZLE_GEN_VEC3_ENTRY(T, P, const, A, B, D) \ + GLM_SWIZZLE_GEN_VEC3_ENTRY(T, P, const, A, C, A) \ + GLM_SWIZZLE_GEN_VEC3_ENTRY(T, P, const, A, C, B) \ + GLM_SWIZZLE_GEN_VEC3_ENTRY(T, P, const, A, C, C) \ + GLM_SWIZZLE_GEN_VEC3_ENTRY(T, P, const, A, C, D) \ + GLM_SWIZZLE_GEN_VEC3_ENTRY(T, P, const, A, D, A) \ + GLM_SWIZZLE_GEN_VEC3_ENTRY(T, P, const, A, D, B) \ + GLM_SWIZZLE_GEN_VEC3_ENTRY(T, P, const, A, D, C) \ + GLM_SWIZZLE_GEN_VEC3_ENTRY(T, P, const, A, D, D) \ + GLM_SWIZZLE_GEN_VEC3_ENTRY(T, P, const, B, A, A) \ + GLM_SWIZZLE_GEN_VEC3_ENTRY(T, P, const, B, A, B) \ + GLM_SWIZZLE_GEN_VEC3_ENTRY(T, P, const, B, A, C) \ + GLM_SWIZZLE_GEN_VEC3_ENTRY(T, P, const, B, A, D) \ + GLM_SWIZZLE_GEN_VEC3_ENTRY(T, P, const, B, B, A) \ + GLM_SWIZZLE_GEN_VEC3_ENTRY(T, P, const, B, B, B) \ + GLM_SWIZZLE_GEN_VEC3_ENTRY(T, P, const, B, B, C) \ + GLM_SWIZZLE_GEN_VEC3_ENTRY(T, P, const, B, B, D) \ + GLM_SWIZZLE_GEN_VEC3_ENTRY(T, P, const, B, C, A) \ + GLM_SWIZZLE_GEN_VEC3_ENTRY(T, P, const, B, C, B) \ + GLM_SWIZZLE_GEN_VEC3_ENTRY(T, P, const, B, C, C) \ + GLM_SWIZZLE_GEN_VEC3_ENTRY(T, P, const, B, C, D) \ + GLM_SWIZZLE_GEN_VEC3_ENTRY(T, P, const, B, D, A) \ + GLM_SWIZZLE_GEN_VEC3_ENTRY(T, P, const, B, D, B) \ + GLM_SWIZZLE_GEN_VEC3_ENTRY(T, P, const, B, D, C) \ + GLM_SWIZZLE_GEN_VEC3_ENTRY(T, P, const, B, D, D) \ + GLM_SWIZZLE_GEN_VEC3_ENTRY(T, P, const, C, A, A) \ + GLM_SWIZZLE_GEN_VEC3_ENTRY(T, P, const, C, A, B) \ + GLM_SWIZZLE_GEN_VEC3_ENTRY(T, P, const, C, A, C) \ + GLM_SWIZZLE_GEN_VEC3_ENTRY(T, P, const, C, A, D) \ + GLM_SWIZZLE_GEN_VEC3_ENTRY(T, P, const, C, B, A) \ + GLM_SWIZZLE_GEN_VEC3_ENTRY(T, P, const, C, B, B) \ + GLM_SWIZZLE_GEN_VEC3_ENTRY(T, P, const, C, B, C) \ + GLM_SWIZZLE_GEN_VEC3_ENTRY(T, P, const, C, B, D) \ + GLM_SWIZZLE_GEN_VEC3_ENTRY(T, P, const, C, C, A) \ + GLM_SWIZZLE_GEN_VEC3_ENTRY(T, P, const, C, C, B) \ + GLM_SWIZZLE_GEN_VEC3_ENTRY(T, P, const, C, C, C) \ + GLM_SWIZZLE_GEN_VEC3_ENTRY(T, P, const, C, C, D) \ + GLM_SWIZZLE_GEN_VEC3_ENTRY(T, P, const, C, D, A) \ + GLM_SWIZZLE_GEN_VEC3_ENTRY(T, P, const, C, D, B) \ + GLM_SWIZZLE_GEN_VEC3_ENTRY(T, P, const, C, D, C) \ + GLM_SWIZZLE_GEN_VEC3_ENTRY(T, P, const, C, D, D) \ + GLM_SWIZZLE_GEN_VEC3_ENTRY(T, P, const, D, A, A) \ + GLM_SWIZZLE_GEN_VEC3_ENTRY(T, P, const, D, A, B) \ + GLM_SWIZZLE_GEN_VEC3_ENTRY(T, P, const, D, A, C) \ + GLM_SWIZZLE_GEN_VEC3_ENTRY(T, P, const, D, A, D) \ + GLM_SWIZZLE_GEN_VEC3_ENTRY(T, P, const, D, B, A) \ + GLM_SWIZZLE_GEN_VEC3_ENTRY(T, P, const, D, B, B) \ + GLM_SWIZZLE_GEN_VEC3_ENTRY(T, P, const, D, B, C) \ + GLM_SWIZZLE_GEN_VEC3_ENTRY(T, P, const, D, B, D) \ + GLM_SWIZZLE_GEN_VEC3_ENTRY(T, P, const, D, C, A) \ + GLM_SWIZZLE_GEN_VEC3_ENTRY(T, P, const, D, C, B) \ + GLM_SWIZZLE_GEN_VEC3_ENTRY(T, P, const, D, C, C) \ + GLM_SWIZZLE_GEN_VEC3_ENTRY(T, P, const, D, C, D) \ + GLM_SWIZZLE_GEN_VEC3_ENTRY(T, P, const, D, D, A) \ + GLM_SWIZZLE_GEN_VEC3_ENTRY(T, P, const, D, D, B) \ + GLM_SWIZZLE_GEN_VEC3_ENTRY(T, P, const, D, D, C) \ + GLM_SWIZZLE_GEN_VEC3_ENTRY(T, P, const, D, D, D) + +#define GLM_SWIZZLE_GEN_VEC4_FROM_VEC4_SWIZZLE(T, P, A, B, C, D) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, A, A, A, A) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, A, A, A, B) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, A, A, A, C) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, A, A, A, D) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, A, A, B, A) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, A, A, B, B) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, A, A, B, C) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, A, A, B, D) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, A, A, C, A) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, A, A, C, B) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, A, A, C, C) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, A, A, C, D) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, A, A, D, A) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, A, A, D, B) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, A, A, D, C) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, A, A, D, D) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, A, B, A, A) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, A, B, A, B) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, A, B, A, C) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, A, B, A, D) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, A, B, B, A) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, A, B, B, B) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, A, B, B, C) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, A, B, B, D) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, A, B, C, A) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, A, B, C, B) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, A, B, C, C) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, A, B, C, D) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, A, B, D, A) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, A, B, D, B) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, A, B, D, C) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, A, B, D, D) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, A, C, A, A) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, A, C, A, B) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, A, C, A, C) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, A, C, A, D) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, A, C, B, A) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, A, C, B, B) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, A, C, B, C) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, A, C, B, D) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, A, C, C, A) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, A, C, C, B) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, A, C, C, C) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, A, C, C, D) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, A, C, D, A) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, A, C, D, B) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, A, C, D, C) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, A, C, D, D) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, A, D, A, A) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, A, D, A, B) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, A, D, A, C) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, A, D, A, D) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, A, D, B, A) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, A, D, B, B) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, A, D, B, C) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, A, D, B, D) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, A, D, C, A) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, A, D, C, B) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, A, D, C, C) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, A, D, C, D) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, A, D, D, A) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, A, D, D, B) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, A, D, D, C) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, A, D, D, D) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, B, A, A, A) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, B, A, A, B) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, B, A, A, C) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, B, A, A, D) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, B, A, B, A) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, B, A, B, B) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, B, A, B, C) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, B, A, B, D) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, B, A, C, A) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, B, A, C, B) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, B, A, C, C) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, B, A, C, D) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, B, A, D, A) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, B, A, D, B) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, B, A, D, C) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, B, A, D, D) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, B, B, A, A) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, B, B, A, B) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, B, B, A, C) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, B, B, A, D) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, B, B, B, A) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, B, B, B, B) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, B, B, B, C) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, B, B, B, D) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, B, B, C, A) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, B, B, C, B) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, B, B, C, C) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, B, B, C, D) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, B, B, D, A) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, B, B, D, B) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, B, B, D, C) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, B, B, D, D) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, B, C, A, A) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, B, C, A, B) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, B, C, A, C) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, B, C, A, D) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, B, C, B, A) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, B, C, B, B) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, B, C, B, C) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, B, C, B, D) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, B, C, C, A) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, B, C, C, B) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, B, C, C, C) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, B, C, C, D) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, B, C, D, A) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, B, C, D, B) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, B, C, D, C) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, B, C, D, D) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, B, D, A, A) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, B, D, A, B) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, B, D, A, C) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, B, D, A, D) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, B, D, B, A) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, B, D, B, B) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, B, D, B, C) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, B, D, B, D) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, B, D, C, A) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, B, D, C, B) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, B, D, C, C) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, B, D, C, D) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, B, D, D, A) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, B, D, D, B) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, B, D, D, C) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, B, D, D, D) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, C, A, A, A) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, C, A, A, B) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, C, A, A, C) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, C, A, A, D) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, C, A, B, A) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, C, A, B, B) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, C, A, B, C) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, C, A, B, D) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, C, A, C, A) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, C, A, C, B) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, C, A, C, C) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, C, A, C, D) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, C, A, D, A) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, C, A, D, B) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, C, A, D, C) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, C, A, D, D) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, C, B, A, A) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, C, B, A, B) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, C, B, A, C) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, C, B, A, D) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, C, B, B, A) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, C, B, B, B) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, C, B, B, C) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, C, B, B, D) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, C, B, C, A) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, C, B, C, B) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, C, B, C, C) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, C, B, C, D) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, C, B, D, A) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, C, B, D, B) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, C, B, D, C) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, C, B, D, D) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, C, C, A, A) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, C, C, A, B) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, C, C, A, C) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, C, C, A, D) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, C, C, B, A) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, C, C, B, B) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, C, C, B, C) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, C, C, B, D) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, C, C, C, A) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, C, C, C, B) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, C, C, C, C) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, C, C, C, D) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, C, C, D, A) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, C, C, D, B) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, C, C, D, C) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, C, C, D, D) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, C, D, A, A) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, C, D, A, B) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, C, D, A, C) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, C, D, A, D) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, C, D, B, A) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, C, D, B, B) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, C, D, B, C) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, C, D, B, D) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, C, D, C, A) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, C, D, C, B) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, C, D, C, C) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, C, D, C, D) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, C, D, D, A) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, C, D, D, B) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, C, D, D, C) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, C, D, D, D) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, D, A, A, A) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, D, A, A, B) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, D, A, A, C) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, D, A, A, D) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, D, A, B, A) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, D, A, B, B) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, D, A, B, C) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, D, A, B, D) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, D, A, C, A) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, D, A, C, B) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, D, A, C, C) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, D, A, C, D) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, D, A, D, A) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, D, A, D, B) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, D, A, D, C) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, D, A, D, D) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, D, B, A, A) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, D, B, A, B) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, D, B, A, C) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, D, B, A, D) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, D, B, B, A) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, D, B, B, B) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, D, B, B, C) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, D, B, B, D) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, D, B, C, A) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, D, B, C, B) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, D, B, C, C) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, D, B, C, D) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, D, B, D, A) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, D, B, D, B) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, D, B, D, C) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, D, B, D, D) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, D, C, A, A) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, D, C, A, B) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, D, C, A, C) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, D, C, A, D) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, D, C, B, A) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, D, C, B, B) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, D, C, B, C) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, D, C, B, D) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, D, C, C, A) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, D, C, C, B) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, D, C, C, C) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, D, C, C, D) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, D, C, D, A) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, D, C, D, B) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, D, C, D, C) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, D, C, D, D) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, D, D, A, A) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, D, D, A, B) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, D, D, A, C) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, D, D, A, D) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, D, D, B, A) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, D, D, B, B) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, D, D, B, C) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, D, D, B, D) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, D, D, C, A) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, D, D, C, B) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, D, D, C, C) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, D, D, C, D) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, D, D, D, A) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, D, D, D, B) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, D, D, D, C) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, D, D, D, D) + +#define GLM_SWIZZLE_GEN_VEC_FROM_VEC4_COMP(T, P, A, B, C, D) \ + GLM_SWIZZLE_GEN_VEC2_FROM_VEC4_SWIZZLE(T, P, A, B, C, D) \ + GLM_SWIZZLE_GEN_VEC3_FROM_VEC4_SWIZZLE(T, P, A, B, C, D) \ + GLM_SWIZZLE_GEN_VEC4_FROM_VEC4_SWIZZLE(T, P, A, B, C, D) + +#define GLM_SWIZZLE_GEN_VEC_FROM_VEC4(T, P) \ + GLM_SWIZZLE_GEN_VEC_FROM_VEC4_COMP(T, P, x, y, z, w) \ + GLM_SWIZZLE_GEN_VEC_FROM_VEC4_COMP(T, P, r, g, b, a) \ + GLM_SWIZZLE_GEN_VEC_FROM_VEC4_COMP(T, P, s, t, p, q) + diff --git a/src/GLMath/glm/detail/_vectorize.hpp b/src/GLMath/glm/detail/_vectorize.hpp new file mode 100644 index 000000000..ba7fd85c8 --- /dev/null +++ b/src/GLMath/glm/detail/_vectorize.hpp @@ -0,0 +1,123 @@ +#pragma once + +namespace glm{ +namespace detail +{ + template class vec, length_t L, typename R, typename T, qualifier Q> + struct functor1{}; + + template class vec, typename R, typename T, qualifier Q> + struct functor1 + { + GLM_FUNC_QUALIFIER GLM_CONSTEXPR static vec<1, R, Q> call(R (*Func) (T x), vec<1, T, Q> const& v) + { + return vec<1, R, Q>(Func(v.x)); + } + }; + + template class vec, typename R, typename T, qualifier Q> + struct functor1 + { + GLM_FUNC_QUALIFIER GLM_CONSTEXPR static vec<2, R, Q> call(R (*Func) (T x), vec<2, T, Q> const& v) + { + return vec<2, R, Q>(Func(v.x), Func(v.y)); + } + }; + + template class vec, typename R, typename T, qualifier Q> + struct functor1 + { + GLM_FUNC_QUALIFIER GLM_CONSTEXPR static vec<3, R, Q> call(R (*Func) (T x), vec<3, T, Q> const& v) + { + return vec<3, R, Q>(Func(v.x), Func(v.y), Func(v.z)); + } + }; + + template class vec, typename R, typename T, qualifier Q> + struct functor1 + { + GLM_FUNC_QUALIFIER GLM_CONSTEXPR static vec<4, R, Q> call(R (*Func) (T x), vec<4, T, Q> const& v) + { + return vec<4, R, Q>(Func(v.x), Func(v.y), Func(v.z), Func(v.w)); + } + }; + + template class vec, length_t L, typename T, qualifier Q> + struct functor2{}; + + template class vec, typename T, qualifier Q> + struct functor2 + { + GLM_FUNC_QUALIFIER static vec<1, T, Q> call(T (*Func) (T x, T y), vec<1, T, Q> const& a, vec<1, T, Q> const& b) + { + return vec<1, T, Q>(Func(a.x, b.x)); + } + }; + + template class vec, typename T, qualifier Q> + struct functor2 + { + GLM_FUNC_QUALIFIER static vec<2, T, Q> call(T (*Func) (T x, T y), vec<2, T, Q> const& a, vec<2, T, Q> const& b) + { + return vec<2, T, Q>(Func(a.x, b.x), Func(a.y, b.y)); + } + }; + + template class vec, typename T, qualifier Q> + struct functor2 + { + GLM_FUNC_QUALIFIER static vec<3, T, Q> call(T (*Func) (T x, T y), vec<3, T, Q> const& a, vec<3, T, Q> const& b) + { + return vec<3, T, Q>(Func(a.x, b.x), Func(a.y, b.y), Func(a.z, b.z)); + } + }; + + template class vec, typename T, qualifier Q> + struct functor2 + { + GLM_FUNC_QUALIFIER static vec<4, T, Q> call(T (*Func) (T x, T y), vec<4, T, Q> const& a, vec<4, T, Q> const& b) + { + return vec<4, T, Q>(Func(a.x, b.x), Func(a.y, b.y), Func(a.z, b.z), Func(a.w, b.w)); + } + }; + + template class vec, length_t L, typename T, qualifier Q> + struct functor2_vec_sca{}; + + template class vec, typename T, qualifier Q> + struct functor2_vec_sca + { + GLM_FUNC_QUALIFIER static vec<1, T, Q> call(T (*Func) (T x, T y), vec<1, T, Q> const& a, T b) + { + return vec<1, T, Q>(Func(a.x, b)); + } + }; + + template class vec, typename T, qualifier Q> + struct functor2_vec_sca + { + GLM_FUNC_QUALIFIER static vec<2, T, Q> call(T (*Func) (T x, T y), vec<2, T, Q> const& a, T b) + { + return vec<2, T, Q>(Func(a.x, b), Func(a.y, b)); + } + }; + + template class vec, typename T, qualifier Q> + struct functor2_vec_sca + { + GLM_FUNC_QUALIFIER static vec<3, T, Q> call(T (*Func) (T x, T y), vec<3, T, Q> const& a, T b) + { + return vec<3, T, Q>(Func(a.x, b), Func(a.y, b), Func(a.z, b)); + } + }; + + template class vec, typename T, qualifier Q> + struct functor2_vec_sca + { + GLM_FUNC_QUALIFIER static vec<4, T, Q> call(T (*Func) (T x, T y), vec<4, T, Q> const& a, T b) + { + return vec<4, T, Q>(Func(a.x, b), Func(a.y, b), Func(a.z, b), Func(a.w, b)); + } + }; +}//namespace detail +}//namespace glm diff --git a/src/GLMath/glm/detail/compute_common.hpp b/src/GLMath/glm/detail/compute_common.hpp new file mode 100644 index 000000000..cc24b9e62 --- /dev/null +++ b/src/GLMath/glm/detail/compute_common.hpp @@ -0,0 +1,50 @@ +#pragma once + +#include "setup.hpp" +#include + +namespace glm{ +namespace detail +{ + template + struct compute_abs + {}; + + template + struct compute_abs + { + GLM_FUNC_QUALIFIER GLM_CONSTEXPR static genFIType call(genFIType x) + { + GLM_STATIC_ASSERT( + std::numeric_limits::is_iec559 || std::numeric_limits::is_signed, + "'abs' only accept floating-point and integer scalar or vector inputs"); + + return x >= genFIType(0) ? x : -x; + // TODO, perf comp with: *(((int *) &x) + 1) &= 0x7fffffff; + } + }; + +#if GLM_COMPILER & GLM_COMPILER_CUDA + template<> + struct compute_abs + { + GLM_FUNC_QUALIFIER GLM_CONSTEXPR static float call(float x) + { + return fabsf(x); + } + }; +#endif + + template + struct compute_abs + { + GLM_FUNC_QUALIFIER GLM_CONSTEXPR static genFIType call(genFIType x) + { + GLM_STATIC_ASSERT( + (!std::numeric_limits::is_signed && std::numeric_limits::is_integer), + "'abs' only accept floating-point and integer scalar or vector inputs"); + return x; + } + }; +}//namespace detail +}//namespace glm diff --git a/src/GLMath/glm/detail/compute_vector_relational.hpp b/src/GLMath/glm/detail/compute_vector_relational.hpp new file mode 100644 index 000000000..167b6345d --- /dev/null +++ b/src/GLMath/glm/detail/compute_vector_relational.hpp @@ -0,0 +1,30 @@ +#pragma once + +//#include "compute_common.hpp" +#include "setup.hpp" +#include + +namespace glm{ +namespace detail +{ + template + struct compute_equal + { + GLM_FUNC_QUALIFIER GLM_CONSTEXPR static bool call(T a, T b) + { + return a == b; + } + }; +/* + template + struct compute_equal + { + GLM_FUNC_QUALIFIER GLM_CONSTEXPR static bool call(T a, T b) + { + return detail::compute_abs::is_signed>::call(b - a) <= static_cast(0); + //return std::memcmp(&a, &b, sizeof(T)) == 0; + } + }; +*/ +}//namespace detail +}//namespace glm diff --git a/src/GLMath/glm/detail/func_common.inl b/src/GLMath/glm/detail/func_common.inl new file mode 100644 index 000000000..b987de63f --- /dev/null +++ b/src/GLMath/glm/detail/func_common.inl @@ -0,0 +1,787 @@ +/// @ref core +/// @file glm/detail/func_common.inl + +#include "../vector_relational.hpp" +#include "compute_common.hpp" +#include "type_vec1.hpp" +#include "type_vec2.hpp" +#include "type_vec3.hpp" +#include "type_vec4.hpp" +#include "_vectorize.hpp" +#include + +namespace glm +{ + // min + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR genType min(genType x, genType y) + { + GLM_STATIC_ASSERT(std::numeric_limits::is_iec559 || std::numeric_limits::is_integer, "'min' only accept floating-point or integer inputs"); + return (y < x) ? y : x; + } + + // max + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR genType max(genType x, genType y) + { + GLM_STATIC_ASSERT(std::numeric_limits::is_iec559 || std::numeric_limits::is_integer, "'max' only accept floating-point or integer inputs"); + + return (x < y) ? y : x; + } + + // abs + template<> + GLM_FUNC_QUALIFIER GLM_CONSTEXPR int abs(int x) + { + int const y = x >> (sizeof(int) * 8 - 1); + return (x ^ y) - y; + } + + // round +# if GLM_HAS_CXX11_STL + using ::std::round; +# else + template + GLM_FUNC_QUALIFIER genType round(genType x) + { + GLM_STATIC_ASSERT(std::numeric_limits::is_iec559, "'round' only accept floating-point inputs"); + + return x < static_cast(0) ? static_cast(int(x - static_cast(0.5))) : static_cast(int(x + static_cast(0.5))); + } +# endif + + // trunc +# if GLM_HAS_CXX11_STL + using ::std::trunc; +# else + template + GLM_FUNC_QUALIFIER genType trunc(genType x) + { + GLM_STATIC_ASSERT(std::numeric_limits::is_iec559, "'trunc' only accept floating-point inputs"); + + return x < static_cast(0) ? -std::floor(-x) : std::floor(x); + } +# endif + +}//namespace glm + +namespace glm{ +namespace detail +{ + template + struct compute_abs_vector + { + GLM_FUNC_QUALIFIER GLM_CONSTEXPR static vec call(vec const& x) + { + return detail::functor1::call(abs, x); + } + }; + + template + struct compute_mix_vector + { + GLM_FUNC_QUALIFIER static vec call(vec const& x, vec const& y, vec const& a) + { + GLM_STATIC_ASSERT(std::numeric_limits::is_iec559 || GLM_CONFIG_UNRESTRICTED_GENTYPE, "'mix' only accept floating-point inputs for the interpolator a"); + + return vec(vec(x) * (static_cast(1) - a) + vec(y) * a); + } + }; + + template + struct compute_mix_vector + { + GLM_FUNC_QUALIFIER static vec call(vec const& x, vec const& y, vec const& a) + { + vec Result; + for(length_t i = 0; i < x.length(); ++i) + Result[i] = a[i] ? y[i] : x[i]; + return Result; + } + }; + + template + struct compute_mix_scalar + { + GLM_FUNC_QUALIFIER static vec call(vec const& x, vec const& y, U const& a) + { + GLM_STATIC_ASSERT(std::numeric_limits::is_iec559 || GLM_CONFIG_UNRESTRICTED_GENTYPE, "'mix' only accept floating-point inputs for the interpolator a"); + + return vec(vec(x) * (static_cast(1) - a) + vec(y) * a); + } + }; + + template + struct compute_mix_scalar + { + GLM_FUNC_QUALIFIER static vec call(vec const& x, vec const& y, bool const& a) + { + return a ? y : x; + } + }; + + template + struct compute_mix + { + GLM_FUNC_QUALIFIER static T call(T const& x, T const& y, U const& a) + { + GLM_STATIC_ASSERT(std::numeric_limits::is_iec559 || GLM_CONFIG_UNRESTRICTED_GENTYPE, "'mix' only accept floating-point inputs for the interpolator a"); + + return static_cast(static_cast(x) * (static_cast(1) - a) + static_cast(y) * a); + } + }; + + template + struct compute_mix + { + GLM_FUNC_QUALIFIER static T call(T const& x, T const& y, bool const& a) + { + return a ? y : x; + } + }; + + template + struct compute_sign + { + GLM_FUNC_QUALIFIER static vec call(vec const& x) + { + return vec(glm::lessThan(vec(0), x)) - vec(glm::lessThan(x, vec(0))); + } + }; + +# if GLM_ARCH == GLM_ARCH_X86 + template + struct compute_sign + { + GLM_FUNC_QUALIFIER static vec call(vec const& x) + { + T const Shift(static_cast(sizeof(T) * 8 - 1)); + vec const y(vec::type, Q>(-x) >> typename detail::make_unsigned::type(Shift)); + + return (x >> Shift) | y; + } + }; +# endif + + template + struct compute_floor + { + GLM_FUNC_QUALIFIER static vec call(vec const& x) + { + return detail::functor1::call(std::floor, x); + } + }; + + template + struct compute_ceil + { + GLM_FUNC_QUALIFIER static vec call(vec const& x) + { + return detail::functor1::call(std::ceil, x); + } + }; + + template + struct compute_fract + { + GLM_FUNC_QUALIFIER static vec call(vec const& x) + { + return x - floor(x); + } + }; + + template + struct compute_trunc + { + GLM_FUNC_QUALIFIER static vec call(vec const& x) + { + return detail::functor1::call(trunc, x); + } + }; + + template + struct compute_round + { + GLM_FUNC_QUALIFIER static vec call(vec const& x) + { + return detail::functor1::call(round, x); + } + }; + + template + struct compute_mod + { + GLM_FUNC_QUALIFIER static vec call(vec const& a, vec const& b) + { + GLM_STATIC_ASSERT(std::numeric_limits::is_iec559, "'mod' only accept floating-point inputs. Include for integer inputs."); + return a - b * floor(a / b); + } + }; + + template + struct compute_min_vector + { + GLM_FUNC_QUALIFIER static vec call(vec const& x, vec const& y) + { + return detail::functor2::call(min, x, y); + } + }; + + template + struct compute_max_vector + { + GLM_FUNC_QUALIFIER static vec call(vec const& x, vec const& y) + { + return detail::functor2::call(max, x, y); + } + }; + + template + struct compute_clamp_vector + { + GLM_FUNC_QUALIFIER static vec call(vec const& x, vec const& minVal, vec const& maxVal) + { + return min(max(x, minVal), maxVal); + } + }; + + template + struct compute_step_vector + { + GLM_FUNC_QUALIFIER static vec call(vec const& edge, vec const& x) + { + return mix(vec(1), vec(0), glm::lessThan(x, edge)); + } + }; + + template + struct compute_smoothstep_vector + { + GLM_FUNC_QUALIFIER static vec call(vec const& edge0, vec const& edge1, vec const& x) + { + GLM_STATIC_ASSERT(std::numeric_limits::is_iec559 || GLM_CONFIG_UNRESTRICTED_GENTYPE, "'step' only accept floating-point inputs"); + vec const tmp(clamp((x - edge0) / (edge1 - edge0), static_cast(0), static_cast(1))); + return tmp * tmp * (static_cast(3) - static_cast(2) * tmp); + } + }; +}//namespace detail + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR genFIType abs(genFIType x) + { + return detail::compute_abs::is_signed>::call(x); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec abs(vec const& x) + { + return detail::compute_abs_vector::value>::call(x); + } + + // sign + // fast and works for any type + template + GLM_FUNC_QUALIFIER genFIType sign(genFIType x) + { + GLM_STATIC_ASSERT( + std::numeric_limits::is_iec559 || (std::numeric_limits::is_signed && std::numeric_limits::is_integer), + "'sign' only accept signed inputs"); + + return detail::compute_sign<1, genFIType, defaultp, std::numeric_limits::is_iec559, highp>::call(vec<1, genFIType>(x)).x; + } + + template + GLM_FUNC_QUALIFIER vec sign(vec const& x) + { + GLM_STATIC_ASSERT( + std::numeric_limits::is_iec559 || (std::numeric_limits::is_signed && std::numeric_limits::is_integer), + "'sign' only accept signed inputs"); + + return detail::compute_sign::is_iec559, detail::is_aligned::value>::call(x); + } + + // floor + using ::std::floor; + template + GLM_FUNC_QUALIFIER vec floor(vec const& x) + { + GLM_STATIC_ASSERT(std::numeric_limits::is_iec559, "'floor' only accept floating-point inputs."); + return detail::compute_floor::value>::call(x); + } + + template + GLM_FUNC_QUALIFIER vec trunc(vec const& x) + { + GLM_STATIC_ASSERT(std::numeric_limits::is_iec559, "'trunc' only accept floating-point inputs"); + return detail::compute_trunc::value>::call(x); + } + + template + GLM_FUNC_QUALIFIER vec round(vec const& x) + { + GLM_STATIC_ASSERT(std::numeric_limits::is_iec559, "'round' only accept floating-point inputs"); + return detail::compute_round::value>::call(x); + } + +/* + // roundEven + template + GLM_FUNC_QUALIFIER genType roundEven(genType const& x) + { + GLM_STATIC_ASSERT(std::numeric_limits::is_iec559, "'roundEven' only accept floating-point inputs"); + + return genType(int(x + genType(int(x) % 2))); + } +*/ + + // roundEven + template + GLM_FUNC_QUALIFIER genType roundEven(genType x) + { + GLM_STATIC_ASSERT(std::numeric_limits::is_iec559, "'roundEven' only accept floating-point inputs"); + + int Integer = static_cast(x); + genType IntegerPart = static_cast(Integer); + genType FractionalPart = fract(x); + + if(FractionalPart > static_cast(0.5) || FractionalPart < static_cast(0.5)) + { + return round(x); + } + else if((Integer % 2) == 0) + { + return IntegerPart; + } + else if(x <= static_cast(0)) // Work around... + { + return IntegerPart - static_cast(1); + } + else + { + return IntegerPart + static_cast(1); + } + //else // Bug on MinGW 4.5.2 + //{ + // return mix(IntegerPart + genType(-1), IntegerPart + genType(1), x <= genType(0)); + //} + } + + template + GLM_FUNC_QUALIFIER vec roundEven(vec const& x) + { + GLM_STATIC_ASSERT(std::numeric_limits::is_iec559, "'roundEven' only accept floating-point inputs"); + return detail::functor1::call(roundEven, x); + } + + // ceil + using ::std::ceil; + template + GLM_FUNC_QUALIFIER vec ceil(vec const& x) + { + GLM_STATIC_ASSERT(std::numeric_limits::is_iec559, "'ceil' only accept floating-point inputs"); + return detail::compute_ceil::value>::call(x); + } + + // fract + template + GLM_FUNC_QUALIFIER genType fract(genType x) + { + return fract(vec<1, genType>(x)).x; + } + + template + GLM_FUNC_QUALIFIER vec fract(vec const& x) + { + GLM_STATIC_ASSERT(std::numeric_limits::is_iec559, "'fract' only accept floating-point inputs"); + return detail::compute_fract::value>::call(x); + } + + // mod + template + GLM_FUNC_QUALIFIER genType mod(genType x, genType y) + { +# if GLM_COMPILER & GLM_COMPILER_CUDA + // Another Cuda compiler bug https://github.com/g-truc/glm/issues/530 + vec<1, genType, defaultp> Result(mod(vec<1, genType, defaultp>(x), y)); + return Result.x; +# else + return mod(vec<1, genType, defaultp>(x), y).x; +# endif + } + + template + GLM_FUNC_QUALIFIER vec mod(vec const& x, T y) + { + return detail::compute_mod::value>::call(x, vec(y)); + } + + template + GLM_FUNC_QUALIFIER vec mod(vec const& x, vec const& y) + { + return detail::compute_mod::value>::call(x, y); + } + + // modf + template + GLM_FUNC_QUALIFIER genType modf(genType x, genType & i) + { + GLM_STATIC_ASSERT(std::numeric_limits::is_iec559, "'modf' only accept floating-point inputs"); + return std::modf(x, &i); + } + + template + GLM_FUNC_QUALIFIER vec<1, T, Q> modf(vec<1, T, Q> const& x, vec<1, T, Q> & i) + { + return vec<1, T, Q>( + modf(x.x, i.x)); + } + + template + GLM_FUNC_QUALIFIER vec<2, T, Q> modf(vec<2, T, Q> const& x, vec<2, T, Q> & i) + { + return vec<2, T, Q>( + modf(x.x, i.x), + modf(x.y, i.y)); + } + + template + GLM_FUNC_QUALIFIER vec<3, T, Q> modf(vec<3, T, Q> const& x, vec<3, T, Q> & i) + { + return vec<3, T, Q>( + modf(x.x, i.x), + modf(x.y, i.y), + modf(x.z, i.z)); + } + + template + GLM_FUNC_QUALIFIER vec<4, T, Q> modf(vec<4, T, Q> const& x, vec<4, T, Q> & i) + { + return vec<4, T, Q>( + modf(x.x, i.x), + modf(x.y, i.y), + modf(x.z, i.z), + modf(x.w, i.w)); + } + + //// Only valid if (INT_MIN <= x-y <= INT_MAX) + //// min(x,y) + //r = y + ((x - y) & ((x - y) >> (sizeof(int) * + //CHAR_BIT - 1))); + //// max(x,y) + //r = x - ((x - y) & ((x - y) >> (sizeof(int) * + //CHAR_BIT - 1))); + + // min + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec min(vec const& a, T b) + { + GLM_STATIC_ASSERT(std::numeric_limits::is_iec559 || std::numeric_limits::is_integer, "'min' only accept floating-point or integer inputs"); + return detail::compute_min_vector::value>::call(a, vec(b)); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec min(vec const& a, vec const& b) + { + return detail::compute_min_vector::value>::call(a, b); + } + + // max + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec max(vec const& a, T b) + { + GLM_STATIC_ASSERT(std::numeric_limits::is_iec559 || std::numeric_limits::is_integer, "'max' only accept floating-point or integer inputs"); + return detail::compute_max_vector::value>::call(a, vec(b)); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec max(vec const& a, vec const& b) + { + return detail::compute_max_vector::value>::call(a, b); + } + + // clamp + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR genType clamp(genType x, genType minVal, genType maxVal) + { + GLM_STATIC_ASSERT(std::numeric_limits::is_iec559 || std::numeric_limits::is_integer, "'clamp' only accept floating-point or integer inputs"); + return min(max(x, minVal), maxVal); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec clamp(vec const& x, T minVal, T maxVal) + { + GLM_STATIC_ASSERT(std::numeric_limits::is_iec559 || std::numeric_limits::is_integer, "'clamp' only accept floating-point or integer inputs"); + return detail::compute_clamp_vector::value>::call(x, vec(minVal), vec(maxVal)); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec clamp(vec const& x, vec const& minVal, vec const& maxVal) + { + GLM_STATIC_ASSERT(std::numeric_limits::is_iec559 || std::numeric_limits::is_integer, "'clamp' only accept floating-point or integer inputs"); + return detail::compute_clamp_vector::value>::call(x, minVal, maxVal); + } + + template + GLM_FUNC_QUALIFIER genTypeT mix(genTypeT x, genTypeT y, genTypeU a) + { + return detail::compute_mix::call(x, y, a); + } + + template + GLM_FUNC_QUALIFIER vec mix(vec const& x, vec const& y, U a) + { + return detail::compute_mix_scalar::value>::call(x, y, a); + } + + template + GLM_FUNC_QUALIFIER vec mix(vec const& x, vec const& y, vec const& a) + { + return detail::compute_mix_vector::value>::call(x, y, a); + } + + // step + template + GLM_FUNC_QUALIFIER genType step(genType edge, genType x) + { + return mix(static_cast(1), static_cast(0), x < edge); + } + + template + GLM_FUNC_QUALIFIER vec step(T edge, vec const& x) + { + return detail::compute_step_vector::value>::call(vec(edge), x); + } + + template + GLM_FUNC_QUALIFIER vec step(vec const& edge, vec const& x) + { + return detail::compute_step_vector::value>::call(edge, x); + } + + // smoothstep + template + GLM_FUNC_QUALIFIER genType smoothstep(genType edge0, genType edge1, genType x) + { + GLM_STATIC_ASSERT(std::numeric_limits::is_iec559 || GLM_CONFIG_UNRESTRICTED_GENTYPE, "'smoothstep' only accept floating-point inputs"); + + genType const tmp(clamp((x - edge0) / (edge1 - edge0), genType(0), genType(1))); + return tmp * tmp * (genType(3) - genType(2) * tmp); + } + + template + GLM_FUNC_QUALIFIER vec smoothstep(T edge0, T edge1, vec const& x) + { + return detail::compute_smoothstep_vector::value>::call(vec(edge0), vec(edge1), x); + } + + template + GLM_FUNC_QUALIFIER vec smoothstep(vec const& edge0, vec const& edge1, vec const& x) + { + return detail::compute_smoothstep_vector::value>::call(edge0, edge1, x); + } + +# if GLM_HAS_CXX11_STL + using std::isnan; +# else + template + GLM_FUNC_QUALIFIER bool isnan(genType x) + { + GLM_STATIC_ASSERT(std::numeric_limits::is_iec559, "'isnan' only accept floating-point inputs"); + +# if GLM_HAS_CXX11_STL + return std::isnan(x); +# elif GLM_COMPILER & GLM_COMPILER_VC + return _isnan(x) != 0; +# elif GLM_COMPILER & GLM_COMPILER_INTEL +# if GLM_PLATFORM & GLM_PLATFORM_WINDOWS + return _isnan(x) != 0; +# else + return ::isnan(x) != 0; +# endif +# elif (GLM_COMPILER & (GLM_COMPILER_GCC | GLM_COMPILER_CLANG)) && (GLM_PLATFORM & GLM_PLATFORM_ANDROID) && __cplusplus < 201103L + return _isnan(x) != 0; +# elif GLM_COMPILER & GLM_COMPILER_CUDA + return ::isnan(x) != 0; +# else + return std::isnan(x); +# endif + } +# endif + + template + GLM_FUNC_QUALIFIER vec isnan(vec const& v) + { + GLM_STATIC_ASSERT(std::numeric_limits::is_iec559, "'isnan' only accept floating-point inputs"); + + vec Result; + for (length_t l = 0; l < v.length(); ++l) + Result[l] = glm::isnan(v[l]); + return Result; + } + +# if GLM_HAS_CXX11_STL + using std::isinf; +# else + template + GLM_FUNC_QUALIFIER bool isinf(genType x) + { + GLM_STATIC_ASSERT(std::numeric_limits::is_iec559, "'isinf' only accept floating-point inputs"); + +# if GLM_HAS_CXX11_STL + return std::isinf(x); +# elif GLM_COMPILER & (GLM_COMPILER_INTEL | GLM_COMPILER_VC) +# if(GLM_PLATFORM & GLM_PLATFORM_WINDOWS) + return _fpclass(x) == _FPCLASS_NINF || _fpclass(x) == _FPCLASS_PINF; +# else + return ::isinf(x); +# endif +# elif GLM_COMPILER & (GLM_COMPILER_GCC | GLM_COMPILER_CLANG) +# if(GLM_PLATFORM & GLM_PLATFORM_ANDROID && __cplusplus < 201103L) + return _isinf(x) != 0; +# else + return std::isinf(x); +# endif +# elif GLM_COMPILER & GLM_COMPILER_CUDA + // http://developer.download.nvidia.com/compute/cuda/4_2/rel/toolkit/docs/online/group__CUDA__MATH__DOUBLE_g13431dd2b40b51f9139cbb7f50c18fab.html#g13431dd2b40b51f9139cbb7f50c18fab + return ::isinf(double(x)) != 0; +# else + return std::isinf(x); +# endif + } +# endif + + template + GLM_FUNC_QUALIFIER vec isinf(vec const& v) + { + GLM_STATIC_ASSERT(std::numeric_limits::is_iec559, "'isinf' only accept floating-point inputs"); + + vec Result; + for (length_t l = 0; l < v.length(); ++l) + Result[l] = glm::isinf(v[l]); + return Result; + } + + GLM_FUNC_QUALIFIER int floatBitsToInt(float const& v) + { + union + { + float in; + int out; + } u; + + u.in = v; + + return u.out; + } + + template + GLM_FUNC_QUALIFIER vec floatBitsToInt(vec const& v) + { + return reinterpret_cast&>(const_cast&>(v)); + } + + GLM_FUNC_QUALIFIER uint floatBitsToUint(float const& v) + { + union + { + float in; + uint out; + } u; + + u.in = v; + + return u.out; + } + + template + GLM_FUNC_QUALIFIER vec floatBitsToUint(vec const& v) + { + return reinterpret_cast&>(const_cast&>(v)); + } + + GLM_FUNC_QUALIFIER float intBitsToFloat(int const& v) + { + union + { + int in; + float out; + } u; + + u.in = v; + + return u.out; + } + + template + GLM_FUNC_QUALIFIER vec intBitsToFloat(vec const& v) + { + return reinterpret_cast&>(const_cast&>(v)); + } + + GLM_FUNC_QUALIFIER float uintBitsToFloat(uint const& v) + { + union + { + uint in; + float out; + } u; + + u.in = v; + + return u.out; + } + + template + GLM_FUNC_QUALIFIER vec uintBitsToFloat(vec const& v) + { + return reinterpret_cast&>(const_cast&>(v)); + } + + template + GLM_FUNC_QUALIFIER genType fma(genType const& a, genType const& b, genType const& c) + { + return a * b + c; + } + + template + GLM_FUNC_QUALIFIER genType frexp(genType x, int& exp) + { + GLM_STATIC_ASSERT(std::numeric_limits::is_iec559, "'frexp' only accept floating-point inputs"); + + return std::frexp(x, &exp); + } + + template + GLM_FUNC_QUALIFIER vec frexp(vec const& v, vec& exp) + { + GLM_STATIC_ASSERT(std::numeric_limits::is_iec559, "'frexp' only accept floating-point inputs"); + + vec Result; + for (length_t l = 0; l < v.length(); ++l) + Result[l] = std::frexp(v[l], &exp[l]); + return Result; + } + + template + GLM_FUNC_QUALIFIER genType ldexp(genType const& x, int const& exp) + { + GLM_STATIC_ASSERT(std::numeric_limits::is_iec559, "'ldexp' only accept floating-point inputs"); + + return std::ldexp(x, exp); + } + + template + GLM_FUNC_QUALIFIER vec ldexp(vec const& v, vec const& exp) + { + GLM_STATIC_ASSERT(std::numeric_limits::is_iec559, "'ldexp' only accept floating-point inputs"); + + vec Result; + for (length_t l = 0; l < v.length(); ++l) + Result[l] = std::ldexp(v[l], exp[l]); + return Result; + } +}//namespace glm + +#if GLM_CONFIG_SIMD == GLM_ENABLE +# include "func_common_simd.inl" +#endif diff --git a/src/GLMath/glm/detail/func_common_simd.inl b/src/GLMath/glm/detail/func_common_simd.inl new file mode 100644 index 000000000..ce0032d33 --- /dev/null +++ b/src/GLMath/glm/detail/func_common_simd.inl @@ -0,0 +1,231 @@ +/// @ref core +/// @file glm/detail/func_common_simd.inl + +#if GLM_ARCH & GLM_ARCH_SSE2_BIT + +#include "../simd/common.h" + +#include + +namespace glm{ +namespace detail +{ + template + struct compute_abs_vector<4, float, Q, true> + { + GLM_FUNC_QUALIFIER static vec<4, float, Q> call(vec<4, float, Q> const& v) + { + vec<4, float, Q> result; + result.data = glm_vec4_abs(v.data); + return result; + } + }; + + template + struct compute_abs_vector<4, int, Q, true> + { + GLM_FUNC_QUALIFIER static vec<4, int, Q> call(vec<4, int, Q> const& v) + { + vec<4, int, Q> result; + result.data = glm_ivec4_abs(v.data); + return result; + } + }; + + template + struct compute_floor<4, float, Q, true> + { + GLM_FUNC_QUALIFIER static vec<4, float, Q> call(vec<4, float, Q> const& v) + { + vec<4, float, Q> result; + result.data = glm_vec4_floor(v.data); + return result; + } + }; + + template + struct compute_ceil<4, float, Q, true> + { + GLM_FUNC_QUALIFIER static vec<4, float, Q> call(vec<4, float, Q> const& v) + { + vec<4, float, Q> result; + result.data = glm_vec4_ceil(v.data); + return result; + } + }; + + template + struct compute_fract<4, float, Q, true> + { + GLM_FUNC_QUALIFIER static vec<4, float, Q> call(vec<4, float, Q> const& v) + { + vec<4, float, Q> result; + result.data = glm_vec4_fract(v.data); + return result; + } + }; + + template + struct compute_round<4, float, Q, true> + { + GLM_FUNC_QUALIFIER static vec<4, float, Q> call(vec<4, float, Q> const& v) + { + vec<4, float, Q> result; + result.data = glm_vec4_round(v.data); + return result; + } + }; + + template + struct compute_mod<4, float, Q, true> + { + GLM_FUNC_QUALIFIER static vec<4, float, Q> call(vec<4, float, Q> const& x, vec<4, float, Q> const& y) + { + vec<4, float, Q> result; + result.data = glm_vec4_mod(x.data, y.data); + return result; + } + }; + + template + struct compute_min_vector<4, float, Q, true> + { + GLM_FUNC_QUALIFIER static vec<4, float, Q> call(vec<4, float, Q> const& v1, vec<4, float, Q> const& v2) + { + vec<4, float, Q> result; + result.data = _mm_min_ps(v1.data, v2.data); + return result; + } + }; + + template + struct compute_min_vector<4, int, Q, true> + { + GLM_FUNC_QUALIFIER static vec<4, int, Q> call(vec<4, int, Q> const& v1, vec<4, int, Q> const& v2) + { + vec<4, int, Q> result; + result.data = _mm_min_epi32(v1.data, v2.data); + return result; + } + }; + + template + struct compute_min_vector<4, uint, Q, true> + { + GLM_FUNC_QUALIFIER static vec<4, uint, Q> call(vec<4, uint, Q> const& v1, vec<4, uint, Q> const& v2) + { + vec<4, uint, Q> result; + result.data = _mm_min_epu32(v1.data, v2.data); + return result; + } + }; + + template + struct compute_max_vector<4, float, Q, true> + { + GLM_FUNC_QUALIFIER static vec<4, float, Q> call(vec<4, float, Q> const& v1, vec<4, float, Q> const& v2) + { + vec<4, float, Q> result; + result.data = _mm_max_ps(v1.data, v2.data); + return result; + } + }; + + template + struct compute_max_vector<4, int, Q, true> + { + GLM_FUNC_QUALIFIER static vec<4, int, Q> call(vec<4, int, Q> const& v1, vec<4, int, Q> const& v2) + { + vec<4, int, Q> result; + result.data = _mm_max_epi32(v1.data, v2.data); + return result; + } + }; + + template + struct compute_max_vector<4, uint, Q, true> + { + GLM_FUNC_QUALIFIER static vec<4, uint, Q> call(vec<4, uint, Q> const& v1, vec<4, uint, Q> const& v2) + { + vec<4, uint, Q> result; + result.data = _mm_max_epu32(v1.data, v2.data); + return result; + } + }; + + template + struct compute_clamp_vector<4, float, Q, true> + { + GLM_FUNC_QUALIFIER static vec<4, float, Q> call(vec<4, float, Q> const& x, vec<4, float, Q> const& minVal, vec<4, float, Q> const& maxVal) + { + vec<4, float, Q> result; + result.data = _mm_min_ps(_mm_max_ps(x.data, minVal.data), maxVal.data); + return result; + } + }; + + template + struct compute_clamp_vector<4, int, Q, true> + { + GLM_FUNC_QUALIFIER static vec<4, int, Q> call(vec<4, int, Q> const& x, vec<4, int, Q> const& minVal, vec<4, int, Q> const& maxVal) + { + vec<4, int, Q> result; + result.data = _mm_min_epi32(_mm_max_epi32(x.data, minVal.data), maxVal.data); + return result; + } + }; + + template + struct compute_clamp_vector<4, uint, Q, true> + { + GLM_FUNC_QUALIFIER static vec<4, uint, Q> call(vec<4, uint, Q> const& x, vec<4, uint, Q> const& minVal, vec<4, uint, Q> const& maxVal) + { + vec<4, uint, Q> result; + result.data = _mm_min_epu32(_mm_max_epu32(x.data, minVal.data), maxVal.data); + return result; + } + }; + + template + struct compute_mix_vector<4, float, bool, Q, true> + { + GLM_FUNC_QUALIFIER static vec<4, float, Q> call(vec<4, float, Q> const& x, vec<4, float, Q> const& y, vec<4, bool, Q> const& a) + { + __m128i const Load = _mm_set_epi32(-static_cast(a.w), -static_cast(a.z), -static_cast(a.y), -static_cast(a.x)); + __m128 const Mask = _mm_castsi128_ps(Load); + + vec<4, float, Q> Result; +# if 0 && GLM_ARCH & GLM_ARCH_AVX + Result.data = _mm_blendv_ps(x.data, y.data, Mask); +# else + Result.data = _mm_or_ps(_mm_and_ps(Mask, y.data), _mm_andnot_ps(Mask, x.data)); +# endif + return Result; + } + }; +/* FIXME + template + struct compute_step_vector + { + GLM_FUNC_QUALIFIER static vec<4, float, Q> call(vec<4, float, Q> const& edge, vec<4, float, Q> const& x) + { + vec<4, float, Q> Result; + result.data = glm_vec4_step(edge.data, x.data); + return result; + } + }; +*/ + template + struct compute_smoothstep_vector<4, float, Q, true> + { + GLM_FUNC_QUALIFIER static vec<4, float, Q> call(vec<4, float, Q> const& edge0, vec<4, float, Q> const& edge1, vec<4, float, Q> const& x) + { + vec<4, float, Q> Result; + Result.data = glm_vec4_smoothstep(edge0.data, edge1.data, x.data); + return Result; + } + }; +}//namespace detail +}//namespace glm + +#endif//GLM_ARCH & GLM_ARCH_SSE2_BIT diff --git a/src/GLMath/glm/detail/func_exponential.inl b/src/GLMath/glm/detail/func_exponential.inl new file mode 100644 index 000000000..2040d41f8 --- /dev/null +++ b/src/GLMath/glm/detail/func_exponential.inl @@ -0,0 +1,152 @@ +/// @ref core +/// @file glm/detail/func_exponential.inl + +#include "../vector_relational.hpp" +#include "_vectorize.hpp" +#include +#include +#include + +namespace glm{ +namespace detail +{ +# if GLM_HAS_CXX11_STL + using std::log2; +# else + template + genType log2(genType Value) + { + return std::log(Value) * static_cast(1.4426950408889634073599246810019); + } +# endif + + template + struct compute_log2 + { + GLM_FUNC_QUALIFIER static vec call(vec const& v) + { + GLM_STATIC_ASSERT(std::numeric_limits::is_iec559, "'log2' only accept floating-point inputs. Include for integer inputs."); + + return detail::functor1::call(log2, v); + } + }; + + template + struct compute_sqrt + { + GLM_FUNC_QUALIFIER static vec call(vec const& x) + { + return detail::functor1::call(std::sqrt, x); + } + }; + + template + struct compute_inversesqrt + { + GLM_FUNC_QUALIFIER static vec call(vec const& x) + { + return static_cast(1) / sqrt(x); + } + }; + + template + struct compute_inversesqrt + { + GLM_FUNC_QUALIFIER static vec call(vec const& x) + { + vec tmp(x); + vec xhalf(tmp * 0.5f); + vec* p = reinterpret_cast*>(const_cast*>(&x)); + vec i = vec(0x5f375a86) - (*p >> vec(1)); + vec* ptmp = reinterpret_cast*>(&i); + tmp = *ptmp; + tmp = tmp * (1.5f - xhalf * tmp * tmp); + return tmp; + } + }; +}//namespace detail + + // pow + using std::pow; + template + GLM_FUNC_QUALIFIER vec pow(vec const& base, vec const& exponent) + { + return detail::functor2::call(pow, base, exponent); + } + + // exp + using std::exp; + template + GLM_FUNC_QUALIFIER vec exp(vec const& x) + { + return detail::functor1::call(exp, x); + } + + // log + using std::log; + template + GLM_FUNC_QUALIFIER vec log(vec const& x) + { + return detail::functor1::call(log, x); + } + +# if GLM_HAS_CXX11_STL + using std::exp2; +# else + //exp2, ln2 = 0.69314718055994530941723212145818f + template + GLM_FUNC_QUALIFIER genType exp2(genType x) + { + GLM_STATIC_ASSERT(std::numeric_limits::is_iec559, "'exp2' only accept floating-point inputs"); + + return std::exp(static_cast(0.69314718055994530941723212145818) * x); + } +# endif + + template + GLM_FUNC_QUALIFIER vec exp2(vec const& x) + { + return detail::functor1::call(exp2, x); + } + + // log2, ln2 = 0.69314718055994530941723212145818f + template + GLM_FUNC_QUALIFIER genType log2(genType x) + { + return log2(vec<1, genType>(x)).x; + } + + template + GLM_FUNC_QUALIFIER vec log2(vec const& x) + { + return detail::compute_log2::is_iec559, detail::is_aligned::value>::call(x); + } + + // sqrt + using std::sqrt; + template + GLM_FUNC_QUALIFIER vec sqrt(vec const& x) + { + GLM_STATIC_ASSERT(std::numeric_limits::is_iec559, "'sqrt' only accept floating-point inputs"); + return detail::compute_sqrt::value>::call(x); + } + + // inversesqrt + template + GLM_FUNC_QUALIFIER genType inversesqrt(genType x) + { + return static_cast(1) / sqrt(x); + } + + template + GLM_FUNC_QUALIFIER vec inversesqrt(vec const& x) + { + GLM_STATIC_ASSERT(std::numeric_limits::is_iec559, "'inversesqrt' only accept floating-point inputs"); + return detail::compute_inversesqrt::value>::call(x); + } +}//namespace glm + +#if GLM_CONFIG_SIMD == GLM_ENABLE +# include "func_exponential_simd.inl" +#endif + diff --git a/src/GLMath/glm/detail/func_exponential_simd.inl b/src/GLMath/glm/detail/func_exponential_simd.inl new file mode 100644 index 000000000..fb7895172 --- /dev/null +++ b/src/GLMath/glm/detail/func_exponential_simd.inl @@ -0,0 +1,37 @@ +/// @ref core +/// @file glm/detail/func_exponential_simd.inl + +#include "../simd/exponential.h" + +#if GLM_ARCH & GLM_ARCH_SSE2_BIT + +namespace glm{ +namespace detail +{ + template + struct compute_sqrt<4, float, Q, true> + { + GLM_FUNC_QUALIFIER static vec<4, float, Q> call(vec<4, float, Q> const& v) + { + vec<4, float, Q> Result; + Result.data = _mm_sqrt_ps(v.data); + return Result; + } + }; + +# if GLM_CONFIG_ALIGNED_GENTYPES == GLM_ENABLE + template<> + struct compute_sqrt<4, float, aligned_lowp, true> + { + GLM_FUNC_QUALIFIER static vec<4, float, aligned_lowp> call(vec<4, float, aligned_lowp> const& v) + { + vec<4, float, aligned_lowp> Result; + Result.data = glm_vec4_sqrt_lowp(v.data); + return Result; + } + }; +# endif +}//namespace detail +}//namespace glm + +#endif//GLM_ARCH & GLM_ARCH_SSE2_BIT diff --git a/src/GLMath/glm/detail/func_geometric.inl b/src/GLMath/glm/detail/func_geometric.inl new file mode 100644 index 000000000..9cde28fed --- /dev/null +++ b/src/GLMath/glm/detail/func_geometric.inl @@ -0,0 +1,243 @@ +#include "../exponential.hpp" +#include "../common.hpp" + +namespace glm{ +namespace detail +{ + template + struct compute_length + { + GLM_FUNC_QUALIFIER static T call(vec const& v) + { + return sqrt(dot(v, v)); + } + }; + + template + struct compute_distance + { + GLM_FUNC_QUALIFIER static T call(vec const& p0, vec const& p1) + { + return length(p1 - p0); + } + }; + + template + struct compute_dot{}; + + template + struct compute_dot, T, Aligned> + { + GLM_FUNC_QUALIFIER static T call(vec<1, T, Q> const& a, vec<1, T, Q> const& b) + { + return a.x * b.x; + } + }; + + template + struct compute_dot, T, Aligned> + { + GLM_FUNC_QUALIFIER static T call(vec<2, T, Q> const& a, vec<2, T, Q> const& b) + { + vec<2, T, Q> tmp(a * b); + return tmp.x + tmp.y; + } + }; + + template + struct compute_dot, T, Aligned> + { + GLM_FUNC_QUALIFIER static T call(vec<3, T, Q> const& a, vec<3, T, Q> const& b) + { + vec<3, T, Q> tmp(a * b); + return tmp.x + tmp.y + tmp.z; + } + }; + + template + struct compute_dot, T, Aligned> + { + GLM_FUNC_QUALIFIER static T call(vec<4, T, Q> const& a, vec<4, T, Q> const& b) + { + vec<4, T, Q> tmp(a * b); + return (tmp.x + tmp.y) + (tmp.z + tmp.w); + } + }; + + template + struct compute_cross + { + GLM_FUNC_QUALIFIER static vec<3, T, Q> call(vec<3, T, Q> const& x, vec<3, T, Q> const& y) + { + GLM_STATIC_ASSERT(std::numeric_limits::is_iec559, "'cross' accepts only floating-point inputs"); + + return vec<3, T, Q>( + x.y * y.z - y.y * x.z, + x.z * y.x - y.z * x.x, + x.x * y.y - y.x * x.y); + } + }; + + template + struct compute_normalize + { + GLM_FUNC_QUALIFIER static vec call(vec const& v) + { + GLM_STATIC_ASSERT(std::numeric_limits::is_iec559, "'normalize' accepts only floating-point inputs"); + + return v * inversesqrt(dot(v, v)); + } + }; + + template + struct compute_faceforward + { + GLM_FUNC_QUALIFIER static vec call(vec const& N, vec const& I, vec const& Nref) + { + GLM_STATIC_ASSERT(std::numeric_limits::is_iec559, "'normalize' accepts only floating-point inputs"); + + return dot(Nref, I) < static_cast(0) ? N : -N; + } + }; + + template + struct compute_reflect + { + GLM_FUNC_QUALIFIER static vec call(vec const& I, vec const& N) + { + return I - N * dot(N, I) * static_cast(2); + } + }; + + template + struct compute_refract + { + GLM_FUNC_QUALIFIER static vec call(vec const& I, vec const& N, T eta) + { + T const dotValue(dot(N, I)); + T const k(static_cast(1) - eta * eta * (static_cast(1) - dotValue * dotValue)); + vec const Result = + (k >= static_cast(0)) ? (eta * I - (eta * dotValue + std::sqrt(k)) * N) : vec(0); + return Result; + } + }; +}//namespace detail + + // length + template + GLM_FUNC_QUALIFIER genType length(genType x) + { + GLM_STATIC_ASSERT(std::numeric_limits::is_iec559, "'length' accepts only floating-point inputs"); + + return abs(x); + } + + template + GLM_FUNC_QUALIFIER T length(vec const& v) + { + GLM_STATIC_ASSERT(std::numeric_limits::is_iec559, "'length' accepts only floating-point inputs"); + + return detail::compute_length::value>::call(v); + } + + // distance + template + GLM_FUNC_QUALIFIER genType distance(genType const& p0, genType const& p1) + { + GLM_STATIC_ASSERT(std::numeric_limits::is_iec559, "'distance' accepts only floating-point inputs"); + + return length(p1 - p0); + } + + template + GLM_FUNC_QUALIFIER T distance(vec const& p0, vec const& p1) + { + return detail::compute_distance::value>::call(p0, p1); + } + + // dot + template + GLM_FUNC_QUALIFIER T dot(T x, T y) + { + GLM_STATIC_ASSERT(std::numeric_limits::is_iec559, "'dot' accepts only floating-point inputs"); + return x * y; + } + + template + GLM_FUNC_QUALIFIER T dot(vec const& x, vec const& y) + { + GLM_STATIC_ASSERT(std::numeric_limits::is_iec559, "'dot' accepts only floating-point inputs"); + return detail::compute_dot, T, detail::is_aligned::value>::call(x, y); + } + + // cross + template + GLM_FUNC_QUALIFIER vec<3, T, Q> cross(vec<3, T, Q> const& x, vec<3, T, Q> const& y) + { + return detail::compute_cross::value>::call(x, y); + } +/* + // normalize + template + GLM_FUNC_QUALIFIER genType normalize(genType const& x) + { + GLM_STATIC_ASSERT(std::numeric_limits::is_iec559, "'normalize' accepts only floating-point inputs"); + + return x < genType(0) ? genType(-1) : genType(1); + } +*/ + template + GLM_FUNC_QUALIFIER vec normalize(vec const& x) + { + GLM_STATIC_ASSERT(std::numeric_limits::is_iec559, "'normalize' accepts only floating-point inputs"); + + return detail::compute_normalize::value>::call(x); + } + + // faceforward + template + GLM_FUNC_QUALIFIER genType faceforward(genType const& N, genType const& I, genType const& Nref) + { + return dot(Nref, I) < static_cast(0) ? N : -N; + } + + template + GLM_FUNC_QUALIFIER vec faceforward(vec const& N, vec const& I, vec const& Nref) + { + return detail::compute_faceforward::value>::call(N, I, Nref); + } + + // reflect + template + GLM_FUNC_QUALIFIER genType reflect(genType const& I, genType const& N) + { + return I - N * dot(N, I) * genType(2); + } + + template + GLM_FUNC_QUALIFIER vec reflect(vec const& I, vec const& N) + { + return detail::compute_reflect::value>::call(I, N); + } + + // refract + template + GLM_FUNC_QUALIFIER genType refract(genType const& I, genType const& N, genType eta) + { + GLM_STATIC_ASSERT(std::numeric_limits::is_iec559, "'refract' accepts only floating-point inputs"); + genType const dotValue(dot(N, I)); + genType const k(static_cast(1) - eta * eta * (static_cast(1) - dotValue * dotValue)); + return (eta * I - (eta * dotValue + sqrt(k)) * N) * static_cast(k >= static_cast(0)); + } + + template + GLM_FUNC_QUALIFIER vec refract(vec const& I, vec const& N, T eta) + { + GLM_STATIC_ASSERT(std::numeric_limits::is_iec559, "'refract' accepts only floating-point inputs"); + return detail::compute_refract::value>::call(I, N, eta); + } +}//namespace glm + +#if GLM_CONFIG_SIMD == GLM_ENABLE +# include "func_geometric_simd.inl" +#endif diff --git a/src/GLMath/glm/detail/func_geometric_simd.inl b/src/GLMath/glm/detail/func_geometric_simd.inl new file mode 100644 index 000000000..e6c8d85f2 --- /dev/null +++ b/src/GLMath/glm/detail/func_geometric_simd.inl @@ -0,0 +1,99 @@ +/// @ref core +/// @file glm/detail/func_geometric_simd.inl + +#include "../simd/geometric.h" + +#if GLM_ARCH & GLM_ARCH_SSE2_BIT + +namespace glm{ +namespace detail +{ + template + struct compute_length<4, float, Q, true> + { + GLM_FUNC_QUALIFIER static float call(vec<4, float, Q> const& v) + { + return _mm_cvtss_f32(glm_vec4_length(v.data)); + } + }; + + template + struct compute_distance<4, float, Q, true> + { + GLM_FUNC_QUALIFIER static float call(vec<4, float, Q> const& p0, vec<4, float, Q> const& p1) + { + return _mm_cvtss_f32(glm_vec4_distance(p0.data, p1.data)); + } + }; + + template + struct compute_dot, float, true> + { + GLM_FUNC_QUALIFIER static float call(vec<4, float, Q> const& x, vec<4, float, Q> const& y) + { + return _mm_cvtss_f32(glm_vec1_dot(x.data, y.data)); + } + }; + + template + struct compute_cross + { + GLM_FUNC_QUALIFIER static vec<3, float, Q> call(vec<3, float, Q> const& a, vec<3, float, Q> const& b) + { + __m128 const set0 = _mm_set_ps(0.0f, a.z, a.y, a.x); + __m128 const set1 = _mm_set_ps(0.0f, b.z, b.y, b.x); + __m128 const xpd0 = glm_vec4_cross(set0, set1); + + vec<4, float, Q> Result; + Result.data = xpd0; + return vec<3, float, Q>(Result); + } + }; + + template + struct compute_normalize<4, float, Q, true> + { + GLM_FUNC_QUALIFIER static vec<4, float, Q> call(vec<4, float, Q> const& v) + { + vec<4, float, Q> Result; + Result.data = glm_vec4_normalize(v.data); + return Result; + } + }; + + template + struct compute_faceforward<4, float, Q, true> + { + GLM_FUNC_QUALIFIER static vec<4, float, Q> call(vec<4, float, Q> const& N, vec<4, float, Q> const& I, vec<4, float, Q> const& Nref) + { + vec<4, float, Q> Result; + Result.data = glm_vec4_faceforward(N.data, I.data, Nref.data); + return Result; + } + }; + + template + struct compute_reflect<4, float, Q, true> + { + GLM_FUNC_QUALIFIER static vec<4, float, Q> call(vec<4, float, Q> const& I, vec<4, float, Q> const& N) + { + vec<4, float, Q> Result; + Result.data = glm_vec4_reflect(I.data, N.data); + return Result; + } + }; + + template + struct compute_refract<4, float, Q, true> + { + GLM_FUNC_QUALIFIER static vec<4, float, Q> call(vec<4, float, Q> const& I, vec<4, float, Q> const& N, float eta) + { + vec<4, float, Q> Result; + Result.data = glm_vec4_refract(I.data, N.data, _mm_set1_ps(eta)); + return Result; + } + }; +}//namespace detail +}//namespace glm + +#endif//GLM_ARCH & GLM_ARCH_SSE2_BIT diff --git a/src/GLMath/glm/detail/func_integer.inl b/src/GLMath/glm/detail/func_integer.inl new file mode 100644 index 000000000..f4067ffcc --- /dev/null +++ b/src/GLMath/glm/detail/func_integer.inl @@ -0,0 +1,362 @@ +/// @ref core + +#include "_vectorize.hpp" +#if(GLM_ARCH & GLM_ARCH_X86 && GLM_COMPILER & GLM_COMPILER_VC) +# include +# pragma intrinsic(_BitScanReverse) +#endif//(GLM_ARCH & GLM_ARCH_X86 && GLM_COMPILER & GLM_COMPILER_VC) +#include + +#if !GLM_HAS_EXTENDED_INTEGER_TYPE +# if GLM_COMPILER & GLM_COMPILER_GCC +# pragma GCC diagnostic ignored "-Wlong-long" +# endif +# if (GLM_COMPILER & GLM_COMPILER_CLANG) +# pragma clang diagnostic ignored "-Wc++11-long-long" +# endif +#endif + +namespace glm{ +namespace detail +{ + template + GLM_FUNC_QUALIFIER T mask(T Bits) + { + return Bits >= static_cast(sizeof(T) * 8) ? ~static_cast(0) : (static_cast(1) << Bits) - static_cast(1); + } + + template + struct compute_bitfieldReverseStep + { + GLM_FUNC_QUALIFIER static vec call(vec const& v, T, T) + { + return v; + } + }; + + template + struct compute_bitfieldReverseStep + { + GLM_FUNC_QUALIFIER static vec call(vec const& v, T Mask, T Shift) + { + return (v & Mask) << Shift | (v & (~Mask)) >> Shift; + } + }; + + template + struct compute_bitfieldBitCountStep + { + GLM_FUNC_QUALIFIER static vec call(vec const& v, T, T) + { + return v; + } + }; + + template + struct compute_bitfieldBitCountStep + { + GLM_FUNC_QUALIFIER static vec call(vec const& v, T Mask, T Shift) + { + return (v & Mask) + ((v >> Shift) & Mask); + } + }; + + template + struct compute_findLSB + { + GLM_FUNC_QUALIFIER static int call(genIUType Value) + { + if(Value == 0) + return -1; + + return glm::bitCount(~Value & (Value - static_cast(1))); + } + }; + +# if GLM_HAS_BITSCAN_WINDOWS + template + struct compute_findLSB + { + GLM_FUNC_QUALIFIER static int call(genIUType Value) + { + unsigned long Result(0); + unsigned char IsNotNull = _BitScanForward(&Result, *reinterpret_cast(&Value)); + return IsNotNull ? int(Result) : -1; + } + }; + +# if !((GLM_COMPILER & GLM_COMPILER_VC) && (GLM_MODEL == GLM_MODEL_32)) + template + struct compute_findLSB + { + GLM_FUNC_QUALIFIER static int call(genIUType Value) + { + unsigned long Result(0); + unsigned char IsNotNull = _BitScanForward64(&Result, *reinterpret_cast(&Value)); + return IsNotNull ? int(Result) : -1; + } + }; +# endif +# endif//GLM_HAS_BITSCAN_WINDOWS + + template + struct compute_findMSB_step_vec + { + GLM_FUNC_QUALIFIER static vec call(vec const& x, T Shift) + { + return x | (x >> Shift); + } + }; + + template + struct compute_findMSB_step_vec + { + GLM_FUNC_QUALIFIER static vec call(vec const& x, T) + { + return x; + } + }; + + template + struct compute_findMSB_vec + { + GLM_FUNC_QUALIFIER static vec call(vec const& v) + { + vec x(v); + x = compute_findMSB_step_vec= 8>::call(x, static_cast( 1)); + x = compute_findMSB_step_vec= 8>::call(x, static_cast( 2)); + x = compute_findMSB_step_vec= 8>::call(x, static_cast( 4)); + x = compute_findMSB_step_vec= 16>::call(x, static_cast( 8)); + x = compute_findMSB_step_vec= 32>::call(x, static_cast(16)); + x = compute_findMSB_step_vec= 64>::call(x, static_cast(32)); + return vec(sizeof(T) * 8 - 1) - glm::bitCount(~x); + } + }; + +# if GLM_HAS_BITSCAN_WINDOWS + template + GLM_FUNC_QUALIFIER int compute_findMSB_32(genIUType Value) + { + unsigned long Result(0); + unsigned char IsNotNull = _BitScanReverse(&Result, *reinterpret_cast(&Value)); + return IsNotNull ? int(Result) : -1; + } + + template + struct compute_findMSB_vec + { + GLM_FUNC_QUALIFIER static vec call(vec const& x) + { + return detail::functor1::call(compute_findMSB_32, x); + } + }; + +# if !((GLM_COMPILER & GLM_COMPILER_VC) && (GLM_MODEL == GLM_MODEL_32)) + template + GLM_FUNC_QUALIFIER int compute_findMSB_64(genIUType Value) + { + unsigned long Result(0); + unsigned char IsNotNull = _BitScanReverse64(&Result, *reinterpret_cast(&Value)); + return IsNotNull ? int(Result) : -1; + } + + template + struct compute_findMSB_vec + { + GLM_FUNC_QUALIFIER static vec call(vec const& x) + { + return detail::functor1::call(compute_findMSB_64, x); + } + }; +# endif +# endif//GLM_HAS_BITSCAN_WINDOWS +}//namespace detail + + // uaddCarry + GLM_FUNC_QUALIFIER uint uaddCarry(uint const& x, uint const& y, uint & Carry) + { + detail::uint64 const Value64(static_cast(x) + static_cast(y)); + detail::uint64 const Max32((static_cast(1) << static_cast(32)) - static_cast(1)); + Carry = Value64 > Max32 ? 1u : 0u; + return static_cast(Value64 % (Max32 + static_cast(1))); + } + + template + GLM_FUNC_QUALIFIER vec uaddCarry(vec const& x, vec const& y, vec& Carry) + { + vec Value64(vec(x) + vec(y)); + vec Max32((static_cast(1) << static_cast(32)) - static_cast(1)); + Carry = mix(vec(0), vec(1), greaterThan(Value64, Max32)); + return vec(Value64 % (Max32 + static_cast(1))); + } + + // usubBorrow + GLM_FUNC_QUALIFIER uint usubBorrow(uint const& x, uint const& y, uint & Borrow) + { + Borrow = x >= y ? static_cast(0) : static_cast(1); + if(y >= x) + return y - x; + else + return static_cast((static_cast(1) << static_cast(32)) + (static_cast(y) - static_cast(x))); + } + + template + GLM_FUNC_QUALIFIER vec usubBorrow(vec const& x, vec const& y, vec& Borrow) + { + Borrow = mix(vec(1), vec(0), greaterThanEqual(x, y)); + vec const YgeX(y - x); + vec const XgeY(vec((static_cast(1) << static_cast(32)) + (vec(y) - vec(x)))); + return mix(XgeY, YgeX, greaterThanEqual(y, x)); + } + + // umulExtended + GLM_FUNC_QUALIFIER void umulExtended(uint const& x, uint const& y, uint & msb, uint & lsb) + { + detail::uint64 Value64 = static_cast(x) * static_cast(y); + msb = static_cast(Value64 >> static_cast(32)); + lsb = static_cast(Value64); + } + + template + GLM_FUNC_QUALIFIER void umulExtended(vec const& x, vec const& y, vec& msb, vec& lsb) + { + vec Value64(vec(x) * vec(y)); + msb = vec(Value64 >> static_cast(32)); + lsb = vec(Value64); + } + + // imulExtended + GLM_FUNC_QUALIFIER void imulExtended(int x, int y, int& msb, int& lsb) + { + detail::int64 Value64 = static_cast(x) * static_cast(y); + msb = static_cast(Value64 >> static_cast(32)); + lsb = static_cast(Value64); + } + + template + GLM_FUNC_QUALIFIER void imulExtended(vec const& x, vec const& y, vec& msb, vec& lsb) + { + vec Value64(vec(x) * vec(y)); + lsb = vec(Value64 & static_cast(0xFFFFFFFF)); + msb = vec((Value64 >> static_cast(32)) & static_cast(0xFFFFFFFF)); + } + + // bitfieldExtract + template + GLM_FUNC_QUALIFIER genIUType bitfieldExtract(genIUType Value, int Offset, int Bits) + { + return bitfieldExtract(vec<1, genIUType>(Value), Offset, Bits).x; + } + + template + GLM_FUNC_QUALIFIER vec bitfieldExtract(vec const& Value, int Offset, int Bits) + { + GLM_STATIC_ASSERT(std::numeric_limits::is_integer, "'bitfieldExtract' only accept integer inputs"); + + return (Value >> static_cast(Offset)) & static_cast(detail::mask(Bits)); + } + + // bitfieldInsert + template + GLM_FUNC_QUALIFIER genIUType bitfieldInsert(genIUType const& Base, genIUType const& Insert, int Offset, int Bits) + { + return bitfieldInsert(vec<1, genIUType>(Base), vec<1, genIUType>(Insert), Offset, Bits).x; + } + + template + GLM_FUNC_QUALIFIER vec bitfieldInsert(vec const& Base, vec const& Insert, int Offset, int Bits) + { + GLM_STATIC_ASSERT(std::numeric_limits::is_integer, "'bitfieldInsert' only accept integer values"); + + T const Mask = static_cast(detail::mask(Bits) << Offset); + return (Base & ~Mask) | ((Insert << static_cast(Offset)) & Mask); + } + + // bitfieldReverse + template + GLM_FUNC_QUALIFIER genType bitfieldReverse(genType x) + { + return bitfieldReverse(glm::vec<1, genType, glm::defaultp>(x)).x; + } + + template + GLM_FUNC_QUALIFIER vec bitfieldReverse(vec const& v) + { + vec x(v); + x = detail::compute_bitfieldReverseStep::value, sizeof(T) * 8>= 2>::call(x, static_cast(0x5555555555555555ull), static_cast( 1)); + x = detail::compute_bitfieldReverseStep::value, sizeof(T) * 8>= 4>::call(x, static_cast(0x3333333333333333ull), static_cast( 2)); + x = detail::compute_bitfieldReverseStep::value, sizeof(T) * 8>= 8>::call(x, static_cast(0x0F0F0F0F0F0F0F0Full), static_cast( 4)); + x = detail::compute_bitfieldReverseStep::value, sizeof(T) * 8>= 16>::call(x, static_cast(0x00FF00FF00FF00FFull), static_cast( 8)); + x = detail::compute_bitfieldReverseStep::value, sizeof(T) * 8>= 32>::call(x, static_cast(0x0000FFFF0000FFFFull), static_cast(16)); + x = detail::compute_bitfieldReverseStep::value, sizeof(T) * 8>= 64>::call(x, static_cast(0x00000000FFFFFFFFull), static_cast(32)); + return x; + } + + // bitCount + template + GLM_FUNC_QUALIFIER int bitCount(genType x) + { + return bitCount(glm::vec<1, genType, glm::defaultp>(x)).x; + } + + template + GLM_FUNC_QUALIFIER vec bitCount(vec const& v) + { +# if GLM_COMPILER & GLM_COMPILER_VC +# pragma warning(push) +# pragma warning(disable : 4310) //cast truncates constant value +# endif + + vec::type, Q> x(*reinterpret_cast::type, Q> const *>(&v)); + x = detail::compute_bitfieldBitCountStep::type, Q, detail::is_aligned::value, sizeof(T) * 8>= 2>::call(x, typename detail::make_unsigned::type(0x5555555555555555ull), typename detail::make_unsigned::type( 1)); + x = detail::compute_bitfieldBitCountStep::type, Q, detail::is_aligned::value, sizeof(T) * 8>= 4>::call(x, typename detail::make_unsigned::type(0x3333333333333333ull), typename detail::make_unsigned::type( 2)); + x = detail::compute_bitfieldBitCountStep::type, Q, detail::is_aligned::value, sizeof(T) * 8>= 8>::call(x, typename detail::make_unsigned::type(0x0F0F0F0F0F0F0F0Full), typename detail::make_unsigned::type( 4)); + x = detail::compute_bitfieldBitCountStep::type, Q, detail::is_aligned::value, sizeof(T) * 8>= 16>::call(x, typename detail::make_unsigned::type(0x00FF00FF00FF00FFull), typename detail::make_unsigned::type( 8)); + x = detail::compute_bitfieldBitCountStep::type, Q, detail::is_aligned::value, sizeof(T) * 8>= 32>::call(x, typename detail::make_unsigned::type(0x0000FFFF0000FFFFull), typename detail::make_unsigned::type(16)); + x = detail::compute_bitfieldBitCountStep::type, Q, detail::is_aligned::value, sizeof(T) * 8>= 64>::call(x, typename detail::make_unsigned::type(0x00000000FFFFFFFFull), typename detail::make_unsigned::type(32)); + return vec(x); + +# if GLM_COMPILER & GLM_COMPILER_VC +# pragma warning(pop) +# endif + } + + // findLSB + template + GLM_FUNC_QUALIFIER int findLSB(genIUType Value) + { + GLM_STATIC_ASSERT(std::numeric_limits::is_integer, "'findLSB' only accept integer values"); + + return detail::compute_findLSB::call(Value); + } + + template + GLM_FUNC_QUALIFIER vec findLSB(vec const& x) + { + GLM_STATIC_ASSERT(std::numeric_limits::is_integer, "'findLSB' only accept integer values"); + + return detail::functor1::call(findLSB, x); + } + + // findMSB + template + GLM_FUNC_QUALIFIER int findMSB(genIUType v) + { + GLM_STATIC_ASSERT(std::numeric_limits::is_integer, "'findMSB' only accept integer values"); + + return findMSB(vec<1, genIUType>(v)).x; + } + + template + GLM_FUNC_QUALIFIER vec findMSB(vec const& v) + { + GLM_STATIC_ASSERT(std::numeric_limits::is_integer, "'findMSB' only accept integer values"); + + return detail::compute_findMSB_vec::call(v); + } +}//namespace glm + +#if GLM_CONFIG_SIMD == GLM_ENABLE +# include "func_integer_simd.inl" +#endif + diff --git a/src/GLMath/glm/detail/func_integer_simd.inl b/src/GLMath/glm/detail/func_integer_simd.inl new file mode 100644 index 000000000..8be6c9ce4 --- /dev/null +++ b/src/GLMath/glm/detail/func_integer_simd.inl @@ -0,0 +1,65 @@ +#include "../simd/integer.h" + +#if GLM_ARCH & GLM_ARCH_SSE2_BIT + +namespace glm{ +namespace detail +{ + template + struct compute_bitfieldReverseStep<4, uint, Q, true, true> + { + GLM_FUNC_QUALIFIER static vec<4, uint, Q> call(vec<4, uint, Q> const& v, uint Mask, uint Shift) + { + __m128i const set0 = v.data; + + __m128i const set1 = _mm_set1_epi32(static_cast(Mask)); + __m128i const and1 = _mm_and_si128(set0, set1); + __m128i const sft1 = _mm_slli_epi32(and1, Shift); + + __m128i const set2 = _mm_andnot_si128(set0, _mm_set1_epi32(-1)); + __m128i const and2 = _mm_and_si128(set0, set2); + __m128i const sft2 = _mm_srai_epi32(and2, Shift); + + __m128i const or0 = _mm_or_si128(sft1, sft2); + + return or0; + } + }; + + template + struct compute_bitfieldBitCountStep<4, uint, Q, true, true> + { + GLM_FUNC_QUALIFIER static vec<4, uint, Q> call(vec<4, uint, Q> const& v, uint Mask, uint Shift) + { + __m128i const set0 = v.data; + + __m128i const set1 = _mm_set1_epi32(static_cast(Mask)); + __m128i const and0 = _mm_and_si128(set0, set1); + __m128i const sft0 = _mm_slli_epi32(set0, Shift); + __m128i const and1 = _mm_and_si128(sft0, set1); + __m128i const add0 = _mm_add_epi32(and0, and1); + + return add0; + } + }; +}//namespace detail + +# if GLM_ARCH & GLM_ARCH_AVX_BIT + template<> + GLM_FUNC_QUALIFIER int bitCount(uint x) + { + return _mm_popcnt_u32(x); + } + +# if(GLM_MODEL == GLM_MODEL_64) + template<> + GLM_FUNC_QUALIFIER int bitCount(detail::uint64 x) + { + return static_cast(_mm_popcnt_u64(x)); + } +# endif//GLM_MODEL +# endif//GLM_ARCH + +}//namespace glm + +#endif//GLM_ARCH & GLM_ARCH_SSE2_BIT diff --git a/src/GLMath/glm/detail/func_matrix.inl b/src/GLMath/glm/detail/func_matrix.inl new file mode 100644 index 000000000..d980c6d38 --- /dev/null +++ b/src/GLMath/glm/detail/func_matrix.inl @@ -0,0 +1,398 @@ +#include "../geometric.hpp" +#include + +namespace glm{ +namespace detail +{ + template + struct compute_matrixCompMult + { + GLM_FUNC_QUALIFIER static mat call(mat const& x, mat const& y) + { + mat Result; + for(length_t i = 0; i < Result.length(); ++i) + Result[i] = x[i] * y[i]; + return Result; + } + }; + + template + struct compute_transpose{}; + + template + struct compute_transpose<2, 2, T, Q, Aligned> + { + GLM_FUNC_QUALIFIER static mat<2, 2, T, Q> call(mat<2, 2, T, Q> const& m) + { + mat<2, 2, T, Q> Result; + Result[0][0] = m[0][0]; + Result[0][1] = m[1][0]; + Result[1][0] = m[0][1]; + Result[1][1] = m[1][1]; + return Result; + } + }; + + template + struct compute_transpose<2, 3, T, Q, Aligned> + { + GLM_FUNC_QUALIFIER static mat<3, 2, T, Q> call(mat<2, 3, T, Q> const& m) + { + mat<3,2, T, Q> Result; + Result[0][0] = m[0][0]; + Result[0][1] = m[1][0]; + Result[1][0] = m[0][1]; + Result[1][1] = m[1][1]; + Result[2][0] = m[0][2]; + Result[2][1] = m[1][2]; + return Result; + } + }; + + template + struct compute_transpose<2, 4, T, Q, Aligned> + { + GLM_FUNC_QUALIFIER static mat<4, 2, T, Q> call(mat<2, 4, T, Q> const& m) + { + mat<4, 2, T, Q> Result; + Result[0][0] = m[0][0]; + Result[0][1] = m[1][0]; + Result[1][0] = m[0][1]; + Result[1][1] = m[1][1]; + Result[2][0] = m[0][2]; + Result[2][1] = m[1][2]; + Result[3][0] = m[0][3]; + Result[3][1] = m[1][3]; + return Result; + } + }; + + template + struct compute_transpose<3, 2, T, Q, Aligned> + { + GLM_FUNC_QUALIFIER static mat<2, 3, T, Q> call(mat<3, 2, T, Q> const& m) + { + mat<2, 3, T, Q> Result; + Result[0][0] = m[0][0]; + Result[0][1] = m[1][0]; + Result[0][2] = m[2][0]; + Result[1][0] = m[0][1]; + Result[1][1] = m[1][1]; + Result[1][2] = m[2][1]; + return Result; + } + }; + + template + struct compute_transpose<3, 3, T, Q, Aligned> + { + GLM_FUNC_QUALIFIER static mat<3, 3, T, Q> call(mat<3, 3, T, Q> const& m) + { + mat<3, 3, T, Q> Result; + Result[0][0] = m[0][0]; + Result[0][1] = m[1][0]; + Result[0][2] = m[2][0]; + + Result[1][0] = m[0][1]; + Result[1][1] = m[1][1]; + Result[1][2] = m[2][1]; + + Result[2][0] = m[0][2]; + Result[2][1] = m[1][2]; + Result[2][2] = m[2][2]; + return Result; + } + }; + + template + struct compute_transpose<3, 4, T, Q, Aligned> + { + GLM_FUNC_QUALIFIER static mat<4, 3, T, Q> call(mat<3, 4, T, Q> const& m) + { + mat<4, 3, T, Q> Result; + Result[0][0] = m[0][0]; + Result[0][1] = m[1][0]; + Result[0][2] = m[2][0]; + Result[1][0] = m[0][1]; + Result[1][1] = m[1][1]; + Result[1][2] = m[2][1]; + Result[2][0] = m[0][2]; + Result[2][1] = m[1][2]; + Result[2][2] = m[2][2]; + Result[3][0] = m[0][3]; + Result[3][1] = m[1][3]; + Result[3][2] = m[2][3]; + return Result; + } + }; + + template + struct compute_transpose<4, 2, T, Q, Aligned> + { + GLM_FUNC_QUALIFIER static mat<2, 4, T, Q> call(mat<4, 2, T, Q> const& m) + { + mat<2, 4, T, Q> Result; + Result[0][0] = m[0][0]; + Result[0][1] = m[1][0]; + Result[0][2] = m[2][0]; + Result[0][3] = m[3][0]; + Result[1][0] = m[0][1]; + Result[1][1] = m[1][1]; + Result[1][2] = m[2][1]; + Result[1][3] = m[3][1]; + return Result; + } + }; + + template + struct compute_transpose<4, 3, T, Q, Aligned> + { + GLM_FUNC_QUALIFIER static mat<3, 4, T, Q> call(mat<4, 3, T, Q> const& m) + { + mat<3, 4, T, Q> Result; + Result[0][0] = m[0][0]; + Result[0][1] = m[1][0]; + Result[0][2] = m[2][0]; + Result[0][3] = m[3][0]; + Result[1][0] = m[0][1]; + Result[1][1] = m[1][1]; + Result[1][2] = m[2][1]; + Result[1][3] = m[3][1]; + Result[2][0] = m[0][2]; + Result[2][1] = m[1][2]; + Result[2][2] = m[2][2]; + Result[2][3] = m[3][2]; + return Result; + } + }; + + template + struct compute_transpose<4, 4, T, Q, Aligned> + { + GLM_FUNC_QUALIFIER static mat<4, 4, T, Q> call(mat<4, 4, T, Q> const& m) + { + mat<4, 4, T, Q> Result; + Result[0][0] = m[0][0]; + Result[0][1] = m[1][0]; + Result[0][2] = m[2][0]; + Result[0][3] = m[3][0]; + + Result[1][0] = m[0][1]; + Result[1][1] = m[1][1]; + Result[1][2] = m[2][1]; + Result[1][3] = m[3][1]; + + Result[2][0] = m[0][2]; + Result[2][1] = m[1][2]; + Result[2][2] = m[2][2]; + Result[2][3] = m[3][2]; + + Result[3][0] = m[0][3]; + Result[3][1] = m[1][3]; + Result[3][2] = m[2][3]; + Result[3][3] = m[3][3]; + return Result; + } + }; + + template + struct compute_determinant{}; + + template + struct compute_determinant<2, 2, T, Q, Aligned> + { + GLM_FUNC_QUALIFIER static T call(mat<2, 2, T, Q> const& m) + { + return m[0][0] * m[1][1] - m[1][0] * m[0][1]; + } + }; + + template + struct compute_determinant<3, 3, T, Q, Aligned> + { + GLM_FUNC_QUALIFIER static T call(mat<3, 3, T, Q> const& m) + { + return + + m[0][0] * (m[1][1] * m[2][2] - m[2][1] * m[1][2]) + - m[1][0] * (m[0][1] * m[2][2] - m[2][1] * m[0][2]) + + m[2][0] * (m[0][1] * m[1][2] - m[1][1] * m[0][2]); + } + }; + + template + struct compute_determinant<4, 4, T, Q, Aligned> + { + GLM_FUNC_QUALIFIER static T call(mat<4, 4, T, Q> const& m) + { + T SubFactor00 = m[2][2] * m[3][3] - m[3][2] * m[2][3]; + T SubFactor01 = m[2][1] * m[3][3] - m[3][1] * m[2][3]; + T SubFactor02 = m[2][1] * m[3][2] - m[3][1] * m[2][2]; + T SubFactor03 = m[2][0] * m[3][3] - m[3][0] * m[2][3]; + T SubFactor04 = m[2][0] * m[3][2] - m[3][0] * m[2][2]; + T SubFactor05 = m[2][0] * m[3][1] - m[3][0] * m[2][1]; + + vec<4, T, Q> DetCof( + + (m[1][1] * SubFactor00 - m[1][2] * SubFactor01 + m[1][3] * SubFactor02), + - (m[1][0] * SubFactor00 - m[1][2] * SubFactor03 + m[1][3] * SubFactor04), + + (m[1][0] * SubFactor01 - m[1][1] * SubFactor03 + m[1][3] * SubFactor05), + - (m[1][0] * SubFactor02 - m[1][1] * SubFactor04 + m[1][2] * SubFactor05)); + + return + m[0][0] * DetCof[0] + m[0][1] * DetCof[1] + + m[0][2] * DetCof[2] + m[0][3] * DetCof[3]; + } + }; + + template + struct compute_inverse{}; + + template + struct compute_inverse<2, 2, T, Q, Aligned> + { + GLM_FUNC_QUALIFIER static mat<2, 2, T, Q> call(mat<2, 2, T, Q> const& m) + { + T OneOverDeterminant = static_cast(1) / ( + + m[0][0] * m[1][1] + - m[1][0] * m[0][1]); + + mat<2, 2, T, Q> Inverse( + + m[1][1] * OneOverDeterminant, + - m[0][1] * OneOverDeterminant, + - m[1][0] * OneOverDeterminant, + + m[0][0] * OneOverDeterminant); + + return Inverse; + } + }; + + template + struct compute_inverse<3, 3, T, Q, Aligned> + { + GLM_FUNC_QUALIFIER static mat<3, 3, T, Q> call(mat<3, 3, T, Q> const& m) + { + T OneOverDeterminant = static_cast(1) / ( + + m[0][0] * (m[1][1] * m[2][2] - m[2][1] * m[1][2]) + - m[1][0] * (m[0][1] * m[2][2] - m[2][1] * m[0][2]) + + m[2][0] * (m[0][1] * m[1][2] - m[1][1] * m[0][2])); + + mat<3, 3, T, Q> Inverse; + Inverse[0][0] = + (m[1][1] * m[2][2] - m[2][1] * m[1][2]) * OneOverDeterminant; + Inverse[1][0] = - (m[1][0] * m[2][2] - m[2][0] * m[1][2]) * OneOverDeterminant; + Inverse[2][0] = + (m[1][0] * m[2][1] - m[2][0] * m[1][1]) * OneOverDeterminant; + Inverse[0][1] = - (m[0][1] * m[2][2] - m[2][1] * m[0][2]) * OneOverDeterminant; + Inverse[1][1] = + (m[0][0] * m[2][2] - m[2][0] * m[0][2]) * OneOverDeterminant; + Inverse[2][1] = - (m[0][0] * m[2][1] - m[2][0] * m[0][1]) * OneOverDeterminant; + Inverse[0][2] = + (m[0][1] * m[1][2] - m[1][1] * m[0][2]) * OneOverDeterminant; + Inverse[1][2] = - (m[0][0] * m[1][2] - m[1][0] * m[0][2]) * OneOverDeterminant; + Inverse[2][2] = + (m[0][0] * m[1][1] - m[1][0] * m[0][1]) * OneOverDeterminant; + + return Inverse; + } + }; + + template + struct compute_inverse<4, 4, T, Q, Aligned> + { + GLM_FUNC_QUALIFIER static mat<4, 4, T, Q> call(mat<4, 4, T, Q> const& m) + { + T Coef00 = m[2][2] * m[3][3] - m[3][2] * m[2][3]; + T Coef02 = m[1][2] * m[3][3] - m[3][2] * m[1][3]; + T Coef03 = m[1][2] * m[2][3] - m[2][2] * m[1][3]; + + T Coef04 = m[2][1] * m[3][3] - m[3][1] * m[2][3]; + T Coef06 = m[1][1] * m[3][3] - m[3][1] * m[1][3]; + T Coef07 = m[1][1] * m[2][3] - m[2][1] * m[1][3]; + + T Coef08 = m[2][1] * m[3][2] - m[3][1] * m[2][2]; + T Coef10 = m[1][1] * m[3][2] - m[3][1] * m[1][2]; + T Coef11 = m[1][1] * m[2][2] - m[2][1] * m[1][2]; + + T Coef12 = m[2][0] * m[3][3] - m[3][0] * m[2][3]; + T Coef14 = m[1][0] * m[3][3] - m[3][0] * m[1][3]; + T Coef15 = m[1][0] * m[2][3] - m[2][0] * m[1][3]; + + T Coef16 = m[2][0] * m[3][2] - m[3][0] * m[2][2]; + T Coef18 = m[1][0] * m[3][2] - m[3][0] * m[1][2]; + T Coef19 = m[1][0] * m[2][2] - m[2][0] * m[1][2]; + + T Coef20 = m[2][0] * m[3][1] - m[3][0] * m[2][1]; + T Coef22 = m[1][0] * m[3][1] - m[3][0] * m[1][1]; + T Coef23 = m[1][0] * m[2][1] - m[2][0] * m[1][1]; + + vec<4, T, Q> Fac0(Coef00, Coef00, Coef02, Coef03); + vec<4, T, Q> Fac1(Coef04, Coef04, Coef06, Coef07); + vec<4, T, Q> Fac2(Coef08, Coef08, Coef10, Coef11); + vec<4, T, Q> Fac3(Coef12, Coef12, Coef14, Coef15); + vec<4, T, Q> Fac4(Coef16, Coef16, Coef18, Coef19); + vec<4, T, Q> Fac5(Coef20, Coef20, Coef22, Coef23); + + vec<4, T, Q> Vec0(m[1][0], m[0][0], m[0][0], m[0][0]); + vec<4, T, Q> Vec1(m[1][1], m[0][1], m[0][1], m[0][1]); + vec<4, T, Q> Vec2(m[1][2], m[0][2], m[0][2], m[0][2]); + vec<4, T, Q> Vec3(m[1][3], m[0][3], m[0][3], m[0][3]); + + vec<4, T, Q> Inv0(Vec1 * Fac0 - Vec2 * Fac1 + Vec3 * Fac2); + vec<4, T, Q> Inv1(Vec0 * Fac0 - Vec2 * Fac3 + Vec3 * Fac4); + vec<4, T, Q> Inv2(Vec0 * Fac1 - Vec1 * Fac3 + Vec3 * Fac5); + vec<4, T, Q> Inv3(Vec0 * Fac2 - Vec1 * Fac4 + Vec2 * Fac5); + + vec<4, T, Q> SignA(+1, -1, +1, -1); + vec<4, T, Q> SignB(-1, +1, -1, +1); + mat<4, 4, T, Q> Inverse(Inv0 * SignA, Inv1 * SignB, Inv2 * SignA, Inv3 * SignB); + + vec<4, T, Q> Row0(Inverse[0][0], Inverse[1][0], Inverse[2][0], Inverse[3][0]); + + vec<4, T, Q> Dot0(m[0] * Row0); + T Dot1 = (Dot0.x + Dot0.y) + (Dot0.z + Dot0.w); + + T OneOverDeterminant = static_cast(1) / Dot1; + + return Inverse * OneOverDeterminant; + } + }; +}//namespace detail + + template + GLM_FUNC_QUALIFIER mat matrixCompMult(mat const& x, mat const& y) + { + GLM_STATIC_ASSERT(std::numeric_limits::is_iec559 || GLM_CONFIG_UNRESTRICTED_GENTYPE, "'matrixCompMult' only accept floating-point inputs"); + return detail::compute_matrixCompMult::value>::call(x, y); + } + + template + GLM_FUNC_QUALIFIER typename detail::outerProduct_trait::type outerProduct(vec const& c, vec const& r) + { + GLM_STATIC_ASSERT(std::numeric_limits::is_iec559 || GLM_CONFIG_UNRESTRICTED_GENTYPE, "'outerProduct' only accept floating-point inputs"); + + typename detail::outerProduct_trait::type m; + for(length_t i = 0; i < m.length(); ++i) + m[i] = c * r[i]; + return m; + } + + template + GLM_FUNC_QUALIFIER typename mat::transpose_type transpose(mat const& m) + { + GLM_STATIC_ASSERT(std::numeric_limits::is_iec559 || GLM_CONFIG_UNRESTRICTED_GENTYPE, "'transpose' only accept floating-point inputs"); + return detail::compute_transpose::value>::call(m); + } + + template + GLM_FUNC_QUALIFIER T determinant(mat const& m) + { + GLM_STATIC_ASSERT(std::numeric_limits::is_iec559 || GLM_CONFIG_UNRESTRICTED_GENTYPE, "'determinant' only accept floating-point inputs"); + return detail::compute_determinant::value>::call(m); + } + + template + GLM_FUNC_QUALIFIER mat inverse(mat const& m) + { + GLM_STATIC_ASSERT(std::numeric_limits::is_iec559 || GLM_CONFIG_UNRESTRICTED_GENTYPE, "'inverse' only accept floating-point inputs"); + return detail::compute_inverse::value>::call(m); + } +}//namespace glm + +#if GLM_CONFIG_SIMD == GLM_ENABLE +# include "func_matrix_simd.inl" +#endif + diff --git a/src/GLMath/glm/detail/func_matrix_simd.inl b/src/GLMath/glm/detail/func_matrix_simd.inl new file mode 100644 index 000000000..f7337fe75 --- /dev/null +++ b/src/GLMath/glm/detail/func_matrix_simd.inl @@ -0,0 +1,94 @@ +#if GLM_ARCH & GLM_ARCH_SSE2_BIT + +#include "type_mat4x4.hpp" +#include "../geometric.hpp" +#include "../simd/matrix.h" +#include + +namespace glm{ +namespace detail +{ +# if GLM_CONFIG_ALIGNED_GENTYPES == GLM_ENABLE + template + struct compute_matrixCompMult<4, 4, float, Q, true> + { + GLM_STATIC_ASSERT(detail::is_aligned::value, "Specialization requires aligned"); + + GLM_FUNC_QUALIFIER static mat<4, 4, float, Q> call(mat<4, 4, float, Q> const& x, mat<4, 4, float, Q> const& y) + { + mat<4, 4, float, Q> Result; + glm_mat4_matrixCompMult( + *static_cast(&x[0].data), + *static_cast(&y[0].data), + *static_cast(&Result[0].data)); + return Result; + } + }; +# endif + + template + struct compute_transpose<4, 4, float, Q, true> + { + GLM_FUNC_QUALIFIER static mat<4, 4, float, Q> call(mat<4, 4, float, Q> const& m) + { + mat<4, 4, float, Q> Result; + glm_mat4_transpose(&m[0].data, &Result[0].data); + return Result; + } + }; + + template + struct compute_determinant<4, 4, float, Q, true> + { + GLM_FUNC_QUALIFIER static float call(mat<4, 4, float, Q> const& m) + { + return _mm_cvtss_f32(glm_mat4_determinant(&m[0].data)); + } + }; + + template + struct compute_inverse<4, 4, float, Q, true> + { + GLM_FUNC_QUALIFIER static mat<4, 4, float, Q> call(mat<4, 4, float, Q> const& m) + { + mat<4, 4, float, Q> Result; + glm_mat4_inverse(&m[0].data, &Result[0].data); + return Result; + } + }; +}//namespace detail + +# if GLM_CONFIG_ALIGNED_GENTYPES == GLM_ENABLE + template<> + GLM_FUNC_QUALIFIER mat<4, 4, float, aligned_lowp> outerProduct<4, 4, float, aligned_lowp>(vec<4, float, aligned_lowp> const& c, vec<4, float, aligned_lowp> const& r) + { + __m128 NativeResult[4]; + glm_mat4_outerProduct(c.data, r.data, NativeResult); + mat<4, 4, float, aligned_lowp> Result; + std::memcpy(&Result[0], &NativeResult[0], sizeof(Result)); + return Result; + } + + template<> + GLM_FUNC_QUALIFIER mat<4, 4, float, aligned_mediump> outerProduct<4, 4, float, aligned_mediump>(vec<4, float, aligned_mediump> const& c, vec<4, float, aligned_mediump> const& r) + { + __m128 NativeResult[4]; + glm_mat4_outerProduct(c.data, r.data, NativeResult); + mat<4, 4, float, aligned_mediump> Result; + std::memcpy(&Result[0], &NativeResult[0], sizeof(Result)); + return Result; + } + + template<> + GLM_FUNC_QUALIFIER mat<4, 4, float, aligned_highp> outerProduct<4, 4, float, aligned_highp>(vec<4, float, aligned_highp> const& c, vec<4, float, aligned_highp> const& r) + { + __m128 NativeResult[4]; + glm_mat4_outerProduct(c.data, r.data, NativeResult); + mat<4, 4, float, aligned_highp> Result; + std::memcpy(&Result[0], &NativeResult[0], sizeof(Result)); + return Result; + } +# endif +}//namespace glm + +#endif diff --git a/src/GLMath/glm/detail/func_packing.inl b/src/GLMath/glm/detail/func_packing.inl new file mode 100644 index 000000000..234b093c0 --- /dev/null +++ b/src/GLMath/glm/detail/func_packing.inl @@ -0,0 +1,189 @@ +/// @ref core +/// @file glm/detail/func_packing.inl + +#include "../common.hpp" +#include "type_half.hpp" + +namespace glm +{ + GLM_FUNC_QUALIFIER uint packUnorm2x16(vec2 const& v) + { + union + { + unsigned short in[2]; + uint out; + } u; + + vec<2, unsigned short, defaultp> result(round(clamp(v, 0.0f, 1.0f) * 65535.0f)); + + u.in[0] = result[0]; + u.in[1] = result[1]; + + return u.out; + } + + GLM_FUNC_QUALIFIER vec2 unpackUnorm2x16(uint p) + { + union + { + uint in; + unsigned short out[2]; + } u; + + u.in = p; + + return vec2(u.out[0], u.out[1]) * 1.5259021896696421759365224689097e-5f; + } + + GLM_FUNC_QUALIFIER uint packSnorm2x16(vec2 const& v) + { + union + { + signed short in[2]; + uint out; + } u; + + vec<2, short, defaultp> result(round(clamp(v, -1.0f, 1.0f) * 32767.0f)); + + u.in[0] = result[0]; + u.in[1] = result[1]; + + return u.out; + } + + GLM_FUNC_QUALIFIER vec2 unpackSnorm2x16(uint p) + { + union + { + uint in; + signed short out[2]; + } u; + + u.in = p; + + return clamp(vec2(u.out[0], u.out[1]) * 3.0518509475997192297128208258309e-5f, -1.0f, 1.0f); + } + + GLM_FUNC_QUALIFIER uint packUnorm4x8(vec4 const& v) + { + union + { + unsigned char in[4]; + uint out; + } u; + + vec<4, unsigned char, defaultp> result(round(clamp(v, 0.0f, 1.0f) * 255.0f)); + + u.in[0] = result[0]; + u.in[1] = result[1]; + u.in[2] = result[2]; + u.in[3] = result[3]; + + return u.out; + } + + GLM_FUNC_QUALIFIER vec4 unpackUnorm4x8(uint p) + { + union + { + uint in; + unsigned char out[4]; + } u; + + u.in = p; + + return vec4(u.out[0], u.out[1], u.out[2], u.out[3]) * 0.0039215686274509803921568627451f; + } + + GLM_FUNC_QUALIFIER uint packSnorm4x8(vec4 const& v) + { + union + { + signed char in[4]; + uint out; + } u; + + vec<4, signed char, defaultp> result(round(clamp(v, -1.0f, 1.0f) * 127.0f)); + + u.in[0] = result[0]; + u.in[1] = result[1]; + u.in[2] = result[2]; + u.in[3] = result[3]; + + return u.out; + } + + GLM_FUNC_QUALIFIER glm::vec4 unpackSnorm4x8(uint p) + { + union + { + uint in; + signed char out[4]; + } u; + + u.in = p; + + return clamp(vec4(u.out[0], u.out[1], u.out[2], u.out[3]) * 0.0078740157480315f, -1.0f, 1.0f); + } + + GLM_FUNC_QUALIFIER double packDouble2x32(uvec2 const& v) + { + union + { + uint in[2]; + double out; + } u; + + u.in[0] = v[0]; + u.in[1] = v[1]; + + return u.out; + } + + GLM_FUNC_QUALIFIER uvec2 unpackDouble2x32(double v) + { + union + { + double in; + uint out[2]; + } u; + + u.in = v; + + return uvec2(u.out[0], u.out[1]); + } + + GLM_FUNC_QUALIFIER uint packHalf2x16(vec2 const& v) + { + union + { + signed short in[2]; + uint out; + } u; + + u.in[0] = detail::toFloat16(v.x); + u.in[1] = detail::toFloat16(v.y); + + return u.out; + } + + GLM_FUNC_QUALIFIER vec2 unpackHalf2x16(uint v) + { + union + { + uint in; + signed short out[2]; + } u; + + u.in = v; + + return vec2( + detail::toFloat32(u.out[0]), + detail::toFloat32(u.out[1])); + } +}//namespace glm + +#if GLM_CONFIG_SIMD == GLM_ENABLE +# include "func_packing_simd.inl" +#endif + diff --git a/src/GLMath/glm/detail/func_packing_simd.inl b/src/GLMath/glm/detail/func_packing_simd.inl new file mode 100644 index 000000000..fd0fe8b7d --- /dev/null +++ b/src/GLMath/glm/detail/func_packing_simd.inl @@ -0,0 +1,6 @@ +namespace glm{ +namespace detail +{ + +}//namespace detail +}//namespace glm diff --git a/src/GLMath/glm/detail/func_trigonometric.inl b/src/GLMath/glm/detail/func_trigonometric.inl new file mode 100644 index 000000000..e129dceac --- /dev/null +++ b/src/GLMath/glm/detail/func_trigonometric.inl @@ -0,0 +1,197 @@ +#include "_vectorize.hpp" +#include +#include + +namespace glm +{ + // radians + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR genType radians(genType degrees) + { + GLM_STATIC_ASSERT(std::numeric_limits::is_iec559, "'radians' only accept floating-point input"); + + return degrees * static_cast(0.01745329251994329576923690768489); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec radians(vec const& v) + { + return detail::functor1::call(radians, v); + } + + // degrees + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR genType degrees(genType radians) + { + GLM_STATIC_ASSERT(std::numeric_limits::is_iec559, "'degrees' only accept floating-point input"); + + return radians * static_cast(57.295779513082320876798154814105); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec degrees(vec const& v) + { + return detail::functor1::call(degrees, v); + } + + // sin + using ::std::sin; + + template + GLM_FUNC_QUALIFIER vec sin(vec const& v) + { + return detail::functor1::call(sin, v); + } + + // cos + using std::cos; + + template + GLM_FUNC_QUALIFIER vec cos(vec const& v) + { + return detail::functor1::call(cos, v); + } + + // tan + using std::tan; + + template + GLM_FUNC_QUALIFIER vec tan(vec const& v) + { + return detail::functor1::call(tan, v); + } + + // asin + using std::asin; + + template + GLM_FUNC_QUALIFIER vec asin(vec const& v) + { + return detail::functor1::call(asin, v); + } + + // acos + using std::acos; + + template + GLM_FUNC_QUALIFIER vec acos(vec const& v) + { + return detail::functor1::call(acos, v); + } + + // atan + template + GLM_FUNC_QUALIFIER genType atan(genType y, genType x) + { + GLM_STATIC_ASSERT(std::numeric_limits::is_iec559, "'atan' only accept floating-point input"); + + return ::std::atan2(y, x); + } + + template + GLM_FUNC_QUALIFIER vec atan(vec const& a, vec const& b) + { + return detail::functor2::call(::std::atan2, a, b); + } + + using std::atan; + + template + GLM_FUNC_QUALIFIER vec atan(vec const& v) + { + return detail::functor1::call(atan, v); + } + + // sinh + using std::sinh; + + template + GLM_FUNC_QUALIFIER vec sinh(vec const& v) + { + return detail::functor1::call(sinh, v); + } + + // cosh + using std::cosh; + + template + GLM_FUNC_QUALIFIER vec cosh(vec const& v) + { + return detail::functor1::call(cosh, v); + } + + // tanh + using std::tanh; + + template + GLM_FUNC_QUALIFIER vec tanh(vec const& v) + { + return detail::functor1::call(tanh, v); + } + + // asinh +# if GLM_HAS_CXX11_STL + using std::asinh; +# else + template + GLM_FUNC_QUALIFIER genType asinh(genType x) + { + GLM_STATIC_ASSERT(std::numeric_limits::is_iec559, "'asinh' only accept floating-point input"); + + return (x < static_cast(0) ? static_cast(-1) : (x > static_cast(0) ? static_cast(1) : static_cast(0))) * log(std::abs(x) + sqrt(static_cast(1) + x * x)); + } +# endif + + template + GLM_FUNC_QUALIFIER vec asinh(vec const& v) + { + return detail::functor1::call(asinh, v); + } + + // acosh +# if GLM_HAS_CXX11_STL + using std::acosh; +# else + template + GLM_FUNC_QUALIFIER genType acosh(genType x) + { + GLM_STATIC_ASSERT(std::numeric_limits::is_iec559, "'acosh' only accept floating-point input"); + + if(x < static_cast(1)) + return static_cast(0); + return log(x + sqrt(x * x - static_cast(1))); + } +# endif + + template + GLM_FUNC_QUALIFIER vec acosh(vec const& v) + { + return detail::functor1::call(acosh, v); + } + + // atanh +# if GLM_HAS_CXX11_STL + using std::atanh; +# else + template + GLM_FUNC_QUALIFIER genType atanh(genType x) + { + GLM_STATIC_ASSERT(std::numeric_limits::is_iec559, "'atanh' only accept floating-point input"); + + if(std::abs(x) >= static_cast(1)) + return 0; + return static_cast(0.5) * log((static_cast(1) + x) / (static_cast(1) - x)); + } +# endif + + template + GLM_FUNC_QUALIFIER vec atanh(vec const& v) + { + return detail::functor1::call(atanh, v); + } +}//namespace glm + +#if GLM_CONFIG_SIMD == GLM_ENABLE +# include "func_trigonometric_simd.inl" +#endif + diff --git a/src/GLMath/glm/detail/func_trigonometric_simd.inl b/src/GLMath/glm/detail/func_trigonometric_simd.inl new file mode 100644 index 000000000..e69de29bb diff --git a/src/GLMath/glm/detail/func_vector_relational.inl b/src/GLMath/glm/detail/func_vector_relational.inl new file mode 100644 index 000000000..80c9e87fc --- /dev/null +++ b/src/GLMath/glm/detail/func_vector_relational.inl @@ -0,0 +1,87 @@ +namespace glm +{ + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec lessThan(vec const& x, vec const& y) + { + vec Result(true); + for(length_t i = 0; i < L; ++i) + Result[i] = x[i] < y[i]; + return Result; + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec lessThanEqual(vec const& x, vec const& y) + { + vec Result(true); + for(length_t i = 0; i < L; ++i) + Result[i] = x[i] <= y[i]; + return Result; + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec greaterThan(vec const& x, vec const& y) + { + vec Result(true); + for(length_t i = 0; i < L; ++i) + Result[i] = x[i] > y[i]; + return Result; + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec greaterThanEqual(vec const& x, vec const& y) + { + vec Result(true); + for(length_t i = 0; i < L; ++i) + Result[i] = x[i] >= y[i]; + return Result; + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec equal(vec const& x, vec const& y) + { + vec Result(true); + for(length_t i = 0; i < L; ++i) + Result[i] = x[i] == y[i]; + return Result; + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec notEqual(vec const& x, vec const& y) + { + vec Result(true); + for(length_t i = 0; i < L; ++i) + Result[i] = x[i] != y[i]; + return Result; + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR bool any(vec const& v) + { + bool Result = false; + for(length_t i = 0; i < L; ++i) + Result = Result || v[i]; + return Result; + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR bool all(vec const& v) + { + bool Result = true; + for(length_t i = 0; i < L; ++i) + Result = Result && v[i]; + return Result; + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec not_(vec const& v) + { + vec Result(true); + for(length_t i = 0; i < L; ++i) + Result[i] = !v[i]; + return Result; + } +}//namespace glm + +#if GLM_CONFIG_SIMD == GLM_ENABLE +# include "func_vector_relational_simd.inl" +#endif diff --git a/src/GLMath/glm/detail/func_vector_relational_simd.inl b/src/GLMath/glm/detail/func_vector_relational_simd.inl new file mode 100644 index 000000000..fd0fe8b7d --- /dev/null +++ b/src/GLMath/glm/detail/func_vector_relational_simd.inl @@ -0,0 +1,6 @@ +namespace glm{ +namespace detail +{ + +}//namespace detail +}//namespace glm diff --git a/src/GLMath/glm/detail/glm.cpp b/src/GLMath/glm/detail/glm.cpp new file mode 100644 index 000000000..e0755bd65 --- /dev/null +++ b/src/GLMath/glm/detail/glm.cpp @@ -0,0 +1,263 @@ +/// @ref core +/// @file glm/glm.cpp + +#ifndef GLM_ENABLE_EXPERIMENTAL +#define GLM_ENABLE_EXPERIMENTAL +#endif +#include +#include +#include +#include +#include +#include + +namespace glm +{ +// tvec1 type explicit instantiation +template struct vec<1, uint8, lowp>; +template struct vec<1, uint16, lowp>; +template struct vec<1, uint32, lowp>; +template struct vec<1, uint64, lowp>; +template struct vec<1, int8, lowp>; +template struct vec<1, int16, lowp>; +template struct vec<1, int32, lowp>; +template struct vec<1, int64, lowp>; +template struct vec<1, float32, lowp>; +template struct vec<1, float64, lowp>; + +template struct vec<1, uint8, mediump>; +template struct vec<1, uint16, mediump>; +template struct vec<1, uint32, mediump>; +template struct vec<1, uint64, mediump>; +template struct vec<1, int8, mediump>; +template struct vec<1, int16, mediump>; +template struct vec<1, int32, mediump>; +template struct vec<1, int64, mediump>; +template struct vec<1, float32, mediump>; +template struct vec<1, float64, mediump>; + +template struct vec<1, uint8, highp>; +template struct vec<1, uint16, highp>; +template struct vec<1, uint32, highp>; +template struct vec<1, uint64, highp>; +template struct vec<1, int8, highp>; +template struct vec<1, int16, highp>; +template struct vec<1, int32, highp>; +template struct vec<1, int64, highp>; +template struct vec<1, float32, highp>; +template struct vec<1, float64, highp>; + +// tvec2 type explicit instantiation +template struct vec<2, uint8, lowp>; +template struct vec<2, uint16, lowp>; +template struct vec<2, uint32, lowp>; +template struct vec<2, uint64, lowp>; +template struct vec<2, int8, lowp>; +template struct vec<2, int16, lowp>; +template struct vec<2, int32, lowp>; +template struct vec<2, int64, lowp>; +template struct vec<2, float32, lowp>; +template struct vec<2, float64, lowp>; + +template struct vec<2, uint8, mediump>; +template struct vec<2, uint16, mediump>; +template struct vec<2, uint32, mediump>; +template struct vec<2, uint64, mediump>; +template struct vec<2, int8, mediump>; +template struct vec<2, int16, mediump>; +template struct vec<2, int32, mediump>; +template struct vec<2, int64, mediump>; +template struct vec<2, float32, mediump>; +template struct vec<2, float64, mediump>; + +template struct vec<2, uint8, highp>; +template struct vec<2, uint16, highp>; +template struct vec<2, uint32, highp>; +template struct vec<2, uint64, highp>; +template struct vec<2, int8, highp>; +template struct vec<2, int16, highp>; +template struct vec<2, int32, highp>; +template struct vec<2, int64, highp>; +template struct vec<2, float32, highp>; +template struct vec<2, float64, highp>; + +// tvec3 type explicit instantiation +template struct vec<3, uint8, lowp>; +template struct vec<3, uint16, lowp>; +template struct vec<3, uint32, lowp>; +template struct vec<3, uint64, lowp>; +template struct vec<3, int8, lowp>; +template struct vec<3, int16, lowp>; +template struct vec<3, int32, lowp>; +template struct vec<3, int64, lowp>; +template struct vec<3, float32, lowp>; +template struct vec<3, float64, lowp>; + +template struct vec<3, uint8, mediump>; +template struct vec<3, uint16, mediump>; +template struct vec<3, uint32, mediump>; +template struct vec<3, uint64, mediump>; +template struct vec<3, int8, mediump>; +template struct vec<3, int16, mediump>; +template struct vec<3, int32, mediump>; +template struct vec<3, int64, mediump>; +template struct vec<3, float32, mediump>; +template struct vec<3, float64, mediump>; + +template struct vec<3, uint8, highp>; +template struct vec<3, uint16, highp>; +template struct vec<3, uint32, highp>; +template struct vec<3, uint64, highp>; +template struct vec<3, int8, highp>; +template struct vec<3, int16, highp>; +template struct vec<3, int32, highp>; +template struct vec<3, int64, highp>; +template struct vec<3, float32, highp>; +template struct vec<3, float64, highp>; + +// tvec4 type explicit instantiation +template struct vec<4, uint8, lowp>; +template struct vec<4, uint16, lowp>; +template struct vec<4, uint32, lowp>; +template struct vec<4, uint64, lowp>; +template struct vec<4, int8, lowp>; +template struct vec<4, int16, lowp>; +template struct vec<4, int32, lowp>; +template struct vec<4, int64, lowp>; +template struct vec<4, float32, lowp>; +template struct vec<4, float64, lowp>; + +template struct vec<4, uint8, mediump>; +template struct vec<4, uint16, mediump>; +template struct vec<4, uint32, mediump>; +template struct vec<4, uint64, mediump>; +template struct vec<4, int8, mediump>; +template struct vec<4, int16, mediump>; +template struct vec<4, int32, mediump>; +template struct vec<4, int64, mediump>; +template struct vec<4, float32, mediump>; +template struct vec<4, float64, mediump>; + +template struct vec<4, uint8, highp>; +template struct vec<4, uint16, highp>; +template struct vec<4, uint32, highp>; +template struct vec<4, uint64, highp>; +template struct vec<4, int8, highp>; +template struct vec<4, int16, highp>; +template struct vec<4, int32, highp>; +template struct vec<4, int64, highp>; +template struct vec<4, float32, highp>; +template struct vec<4, float64, highp>; + +// tmat2x2 type explicit instantiation +template struct mat<2, 2, float32, lowp>; +template struct mat<2, 2, float64, lowp>; + +template struct mat<2, 2, float32, mediump>; +template struct mat<2, 2, float64, mediump>; + +template struct mat<2, 2, float32, highp>; +template struct mat<2, 2, float64, highp>; + +// tmat2x3 type explicit instantiation +template struct mat<2, 3, float32, lowp>; +template struct mat<2, 3, float64, lowp>; + +template struct mat<2, 3, float32, mediump>; +template struct mat<2, 3, float64, mediump>; + +template struct mat<2, 3, float32, highp>; +template struct mat<2, 3, float64, highp>; + +// tmat2x4 type explicit instantiation +template struct mat<2, 4, float32, lowp>; +template struct mat<2, 4, float64, lowp>; + +template struct mat<2, 4, float32, mediump>; +template struct mat<2, 4, float64, mediump>; + +template struct mat<2, 4, float32, highp>; +template struct mat<2, 4, float64, highp>; + +// tmat3x2 type explicit instantiation +template struct mat<3, 2, float32, lowp>; +template struct mat<3, 2, float64, lowp>; + +template struct mat<3, 2, float32, mediump>; +template struct mat<3, 2, float64, mediump>; + +template struct mat<3, 2, float32, highp>; +template struct mat<3, 2, float64, highp>; + +// tmat3x3 type explicit instantiation +template struct mat<3, 3, float32, lowp>; +template struct mat<3, 3, float64, lowp>; + +template struct mat<3, 3, float32, mediump>; +template struct mat<3, 3, float64, mediump>; + +template struct mat<3, 3, float32, highp>; +template struct mat<3, 3, float64, highp>; + +// tmat3x4 type explicit instantiation +template struct mat<3, 4, float32, lowp>; +template struct mat<3, 4, float64, lowp>; + +template struct mat<3, 4, float32, mediump>; +template struct mat<3, 4, float64, mediump>; + +template struct mat<3, 4, float32, highp>; +template struct mat<3, 4, float64, highp>; + +// tmat4x2 type explicit instantiation +template struct mat<4, 2, float32, lowp>; +template struct mat<4, 2, float64, lowp>; + +template struct mat<4, 2, float32, mediump>; +template struct mat<4, 2, float64, mediump>; + +template struct mat<4, 2, float32, highp>; +template struct mat<4, 2, float64, highp>; + +// tmat4x3 type explicit instantiation +template struct mat<4, 3, float32, lowp>; +template struct mat<4, 3, float64, lowp>; + +template struct mat<4, 3, float32, mediump>; +template struct mat<4, 3, float64, mediump>; + +template struct mat<4, 3, float32, highp>; +template struct mat<4, 3, float64, highp>; + +// tmat4x4 type explicit instantiation +template struct mat<4, 4, float32, lowp>; +template struct mat<4, 4, float64, lowp>; + +template struct mat<4, 4, float32, mediump>; +template struct mat<4, 4, float64, mediump>; + +template struct mat<4, 4, float32, highp>; +template struct mat<4, 4, float64, highp>; + +// tquat type explicit instantiation +template struct qua; +template struct qua; + +template struct qua; +template struct qua; + +template struct qua; +template struct qua; + +//tdualquat type explicit instantiation +template struct tdualquat; +template struct tdualquat; + +template struct tdualquat; +template struct tdualquat; + +template struct tdualquat; +template struct tdualquat; + +}//namespace glm + diff --git a/src/GLMath/glm/detail/qualifier.hpp b/src/GLMath/glm/detail/qualifier.hpp new file mode 100644 index 000000000..115817f19 --- /dev/null +++ b/src/GLMath/glm/detail/qualifier.hpp @@ -0,0 +1,210 @@ +#pragma once + +#include "setup.hpp" + +namespace glm +{ + /// Qualify GLM types in term of alignment (packed, aligned) and precision in term of ULPs (lowp, mediump, highp) + enum qualifier + { + packed_highp, ///< Typed data is tightly packed in memory and operations are executed with high precision in term of ULPs + packed_mediump, ///< Typed data is tightly packed in memory and operations are executed with medium precision in term of ULPs for higher performance + packed_lowp, ///< Typed data is tightly packed in memory and operations are executed with low precision in term of ULPs to maximize performance + +# if GLM_CONFIG_ALIGNED_GENTYPES == GLM_ENABLE + aligned_highp, ///< Typed data is aligned in memory allowing SIMD optimizations and operations are executed with high precision in term of ULPs + aligned_mediump, ///< Typed data is aligned in memory allowing SIMD optimizations and operations are executed with high precision in term of ULPs for higher performance + aligned_lowp, // ///< Typed data is aligned in memory allowing SIMD optimizations and operations are executed with high precision in term of ULPs to maximize performance + aligned = aligned_highp, ///< By default aligned qualifier is also high precision +# endif + + highp = packed_highp, ///< By default highp qualifier is also packed + mediump = packed_mediump, ///< By default mediump qualifier is also packed + lowp = packed_lowp, ///< By default lowp qualifier is also packed + packed = packed_highp, ///< By default packed qualifier is also high precision + +# if GLM_CONFIG_ALIGNED_GENTYPES == GLM_ENABLE && defined(GLM_FORCE_DEFAULT_ALIGNED_GENTYPES) + defaultp = aligned_highp +# else + defaultp = highp +# endif + }; + + typedef qualifier precision; + + template struct vec; + template struct mat; + template struct qua; + +# if GLM_HAS_TEMPLATE_ALIASES + template using tvec1 = vec<1, T, Q>; + template using tvec2 = vec<2, T, Q>; + template using tvec3 = vec<3, T, Q>; + template using tvec4 = vec<4, T, Q>; + template using tmat2x2 = mat<2, 2, T, Q>; + template using tmat2x3 = mat<2, 3, T, Q>; + template using tmat2x4 = mat<2, 4, T, Q>; + template using tmat3x2 = mat<3, 2, T, Q>; + template using tmat3x3 = mat<3, 3, T, Q>; + template using tmat3x4 = mat<3, 4, T, Q>; + template using tmat4x2 = mat<4, 2, T, Q>; + template using tmat4x3 = mat<4, 3, T, Q>; + template using tmat4x4 = mat<4, 4, T, Q>; + template using tquat = qua; +# endif + +namespace detail +{ + template + struct is_aligned + { + static const bool value = false; + }; + +# if GLM_CONFIG_ALIGNED_GENTYPES == GLM_ENABLE + template<> + struct is_aligned + { + static const bool value = true; + }; + + template<> + struct is_aligned + { + static const bool value = true; + }; + + template<> + struct is_aligned + { + static const bool value = true; + }; +# endif + + template + struct storage + { + typedef struct type { + T data[L]; + } type; + }; + +# if GLM_HAS_ALIGNOF + template + struct storage + { + typedef struct alignas(L * sizeof(T)) type { + T data[L]; + } type; + }; + + template + struct storage<3, T, true> + { + typedef struct alignas(4 * sizeof(T)) type { + T data[4]; + } type; + }; +# endif + +# if GLM_ARCH & GLM_ARCH_SSE2_BIT + template<> + struct storage<4, float, true> + { + typedef glm_f32vec4 type; + }; + + template<> + struct storage<4, int, true> + { + typedef glm_i32vec4 type; + }; + + template<> + struct storage<4, unsigned int, true> + { + typedef glm_u32vec4 type; + }; + + template<> + struct storage<2, double, true> + { + typedef glm_f64vec2 type; + }; + + template<> + struct storage<2, detail::int64, true> + { + typedef glm_i64vec2 type; + }; + + template<> + struct storage<2, detail::uint64, true> + { + typedef glm_u64vec2 type; + }; +# endif + +# if (GLM_ARCH & GLM_ARCH_AVX_BIT) + template<> + struct storage<4, double, true> + { + typedef glm_f64vec4 type; + }; +# endif + +# if (GLM_ARCH & GLM_ARCH_AVX2_BIT) + template<> + struct storage<4, detail::int64, true> + { + typedef glm_i64vec4 type; + }; + + template<> + struct storage<4, detail::uint64, true> + { + typedef glm_u64vec4 type; + }; +# endif + + enum genTypeEnum + { + GENTYPE_VEC, + GENTYPE_MAT, + GENTYPE_QUAT + }; + + template + struct genTypeTrait + {}; + + template + struct genTypeTrait > + { + static const genTypeEnum GENTYPE = GENTYPE_MAT; + }; + + template + struct init_gentype + { + }; + + template + struct init_gentype + { + GLM_FUNC_QUALIFIER GLM_CONSTEXPR static genType identity() + { + return genType(1, 0, 0, 0); + } + }; + + template + struct init_gentype + { + GLM_FUNC_QUALIFIER GLM_CONSTEXPR static genType identity() + { + return genType(1); + } + }; +}//namespace detail +}//namespace glm diff --git a/src/GLMath/glm/detail/setup.hpp b/src/GLMath/glm/detail/setup.hpp new file mode 100644 index 000000000..2983e7197 --- /dev/null +++ b/src/GLMath/glm/detail/setup.hpp @@ -0,0 +1,1086 @@ +#ifndef GLM_SETUP_INCLUDED + +#include +#include + +#define GLM_VERSION_MAJOR 0 +#define GLM_VERSION_MINOR 9 +#define GLM_VERSION_PATCH 9 +#define GLM_VERSION_REVISION 4 +#define GLM_VERSION 995 +#define GLM_VERSION_MESSAGE "GLM: version 0.9.9.5" + +#define GLM_SETUP_INCLUDED GLM_VERSION + +/////////////////////////////////////////////////////////////////////////////////// +// Active states + +#define GLM_DISABLE 0 +#define GLM_ENABLE 1 + +/////////////////////////////////////////////////////////////////////////////////// +// Messages + +#if defined(GLM_FORCE_MESSAGES) +# define GLM_MESSAGES GLM_ENABLE +#else +# define GLM_MESSAGES GLM_DISABLE +#endif + +/////////////////////////////////////////////////////////////////////////////////// +// Detect the platform + +#include "../simd/platform.h" + +/////////////////////////////////////////////////////////////////////////////////// +// Build model + +#if defined(__arch64__) || defined(__LP64__) || defined(_M_X64) || defined(__ppc64__) || defined(__x86_64__) +# define GLM_MODEL GLM_MODEL_64 +#elif defined(__i386__) || defined(__ppc__) +# define GLM_MODEL GLM_MODEL_32 +#else +# define GLM_MODEL GLM_MODEL_32 +#endif// + +#if !defined(GLM_MODEL) && GLM_COMPILER != 0 +# error "GLM_MODEL undefined, your compiler may not be supported by GLM. Add #define GLM_MODEL 0 to ignore this message." +#endif//GLM_MODEL + +/////////////////////////////////////////////////////////////////////////////////// +// C++ Version + +// User defines: GLM_FORCE_CXX98, GLM_FORCE_CXX03, GLM_FORCE_CXX11, GLM_FORCE_CXX14, GLM_FORCE_CXX17, GLM_FORCE_CXX2A + +#define GLM_LANG_CXX98_FLAG (1 << 1) +#define GLM_LANG_CXX03_FLAG (1 << 2) +#define GLM_LANG_CXX0X_FLAG (1 << 3) +#define GLM_LANG_CXX11_FLAG (1 << 4) +#define GLM_LANG_CXX14_FLAG (1 << 5) +#define GLM_LANG_CXX17_FLAG (1 << 6) +#define GLM_LANG_CXX2A_FLAG (1 << 7) +#define GLM_LANG_CXXMS_FLAG (1 << 8) +#define GLM_LANG_CXXGNU_FLAG (1 << 9) + +#define GLM_LANG_CXX98 GLM_LANG_CXX98_FLAG +#define GLM_LANG_CXX03 (GLM_LANG_CXX98 | GLM_LANG_CXX03_FLAG) +#define GLM_LANG_CXX0X (GLM_LANG_CXX03 | GLM_LANG_CXX0X_FLAG) +#define GLM_LANG_CXX11 (GLM_LANG_CXX0X | GLM_LANG_CXX11_FLAG) +#define GLM_LANG_CXX14 (GLM_LANG_CXX11 | GLM_LANG_CXX14_FLAG) +#define GLM_LANG_CXX17 (GLM_LANG_CXX14 | GLM_LANG_CXX17_FLAG) +#define GLM_LANG_CXX2A (GLM_LANG_CXX17 | GLM_LANG_CXX2A_FLAG) +#define GLM_LANG_CXXMS GLM_LANG_CXXMS_FLAG +#define GLM_LANG_CXXGNU GLM_LANG_CXXGNU_FLAG + +#if (defined(_MSC_EXTENSIONS)) +# define GLM_LANG_EXT GLM_LANG_CXXMS_FLAG +#elif ((GLM_COMPILER & (GLM_COMPILER_CLANG | GLM_COMPILER_GCC)) && (GLM_ARCH & GLM_ARCH_SIMD_BIT)) +# define GLM_LANG_EXT GLM_LANG_CXXMS_FLAG +#else +# define GLM_LANG_EXT 0 +#endif + +#if (defined(GLM_FORCE_CXX_UNKNOWN)) +# define GLM_LANG 0 +#elif defined(GLM_FORCE_CXX2A) +# define GLM_LANG (GLM_LANG_CXX2A | GLM_LANG_EXT) +# define GLM_LANG_STL11_FORCED +#elif defined(GLM_FORCE_CXX17) +# define GLM_LANG (GLM_LANG_CXX17 | GLM_LANG_EXT) +# define GLM_LANG_STL11_FORCED +#elif defined(GLM_FORCE_CXX14) +# define GLM_LANG (GLM_LANG_CXX14 | GLM_LANG_EXT) +# define GLM_LANG_STL11_FORCED +#elif defined(GLM_FORCE_CXX11) +# define GLM_LANG (GLM_LANG_CXX11 | GLM_LANG_EXT) +# define GLM_LANG_STL11_FORCED +#elif defined(GLM_FORCE_CXX03) +# define GLM_LANG (GLM_LANG_CXX03 | GLM_LANG_EXT) +#elif defined(GLM_FORCE_CXX98) +# define GLM_LANG (GLM_LANG_CXX98 | GLM_LANG_EXT) +#else +# if GLM_COMPILER & GLM_COMPILER_VC && defined(_MSVC_LANG) +# if GLM_COMPILER >= GLM_COMPILER_VC15_7 +# define GLM_LANG_PLATFORM _MSVC_LANG +# elif GLM_COMPILER >= GLM_COMPILER_VC15 +# if _MSVC_LANG > 201402L +# define GLM_LANG_PLATFORM 201402L +# else +# define GLM_LANG_PLATFORM _MSVC_LANG +# endif +# else +# define GLM_LANG_PLATFORM 0 +# endif +# else +# define GLM_LANG_PLATFORM 0 +# endif + +# if __cplusplus > 201703L || GLM_LANG_PLATFORM > 201703L +# define GLM_LANG (GLM_LANG_CXX2A | GLM_LANG_EXT) +# elif __cplusplus == 201703L || GLM_LANG_PLATFORM == 201703L +# define GLM_LANG (GLM_LANG_CXX17 | GLM_LANG_EXT) +# elif __cplusplus == 201402L || GLM_LANG_PLATFORM == 201402L +# define GLM_LANG (GLM_LANG_CXX14 | GLM_LANG_EXT) +# elif __cplusplus == 201103L || GLM_LANG_PLATFORM == 201103L +# define GLM_LANG (GLM_LANG_CXX11 | GLM_LANG_EXT) +# elif defined(__INTEL_CXX11_MODE__) || defined(_MSC_VER) || defined(__GXX_EXPERIMENTAL_CXX0X__) +# define GLM_LANG (GLM_LANG_CXX0X | GLM_LANG_EXT) +# elif __cplusplus == 199711L +# define GLM_LANG (GLM_LANG_CXX98 | GLM_LANG_EXT) +# else +# define GLM_LANG (0 | GLM_LANG_EXT) +# endif +#endif + +/////////////////////////////////////////////////////////////////////////////////// +// Has of C++ features + +// http://clang.llvm.org/cxx_status.html +// http://gcc.gnu.org/projects/cxx0x.html +// http://msdn.microsoft.com/en-us/library/vstudio/hh567368(v=vs.120).aspx + +// Android has multiple STLs but C++11 STL detection doesn't always work #284 #564 +#if GLM_PLATFORM == GLM_PLATFORM_ANDROID && !defined(GLM_LANG_STL11_FORCED) +# define GLM_HAS_CXX11_STL 0 +#elif GLM_COMPILER & GLM_COMPILER_CLANG +# if ((defined(_LIBCPP_VERSION) || defined(_MSC_VER)) && GLM_LANG & GLM_LANG_CXX11_FLAG) || \ + defined(GLM_LANG_STL11_FORCED) +# define GLM_HAS_CXX11_STL 1 +# else +# define GLM_HAS_CXX11_STL 0 +# endif +#elif GLM_LANG & GLM_LANG_CXX11_FLAG +# define GLM_HAS_CXX11_STL 1 +#else +# define GLM_HAS_CXX11_STL ((GLM_LANG & GLM_LANG_CXX0X_FLAG) && (\ + ((GLM_COMPILER & GLM_COMPILER_GCC) && (GLM_COMPILER >= GLM_COMPILER_GCC48)) || \ + ((GLM_COMPILER & GLM_COMPILER_VC) && (GLM_COMPILER >= GLM_COMPILER_VC12)) || \ + ((GLM_PLATFORM != GLM_PLATFORM_WINDOWS) && (GLM_COMPILER & GLM_COMPILER_INTEL) && (GLM_COMPILER >= GLM_COMPILER_INTEL15)))) +#endif + +// N1720 +#if GLM_COMPILER & GLM_COMPILER_CLANG +# define GLM_HAS_STATIC_ASSERT __has_feature(cxx_static_assert) +#elif GLM_LANG & GLM_LANG_CXX11_FLAG +# define GLM_HAS_STATIC_ASSERT 1 +#else +# define GLM_HAS_STATIC_ASSERT ((GLM_LANG & GLM_LANG_CXX0X_FLAG) && (\ + ((GLM_COMPILER & GLM_COMPILER_CUDA)) || \ + ((GLM_COMPILER & GLM_COMPILER_VC)))) +#endif + +// N1988 +#if GLM_LANG & GLM_LANG_CXX11_FLAG +# define GLM_HAS_EXTENDED_INTEGER_TYPE 1 +#else +# define GLM_HAS_EXTENDED_INTEGER_TYPE (\ + ((GLM_LANG & GLM_LANG_CXX0X_FLAG) && (GLM_COMPILER & GLM_COMPILER_VC)) || \ + ((GLM_LANG & GLM_LANG_CXX0X_FLAG) && (GLM_COMPILER & GLM_COMPILER_CUDA)) || \ + ((GLM_LANG & GLM_LANG_CXX0X_FLAG) && (GLM_COMPILER & GLM_COMPILER_CLANG))) +#endif + +// N2672 Initializer lists http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2008/n2672.htm +#if GLM_COMPILER & GLM_COMPILER_CLANG +# define GLM_HAS_INITIALIZER_LISTS __has_feature(cxx_generalized_initializers) +#elif GLM_LANG & GLM_LANG_CXX11_FLAG +# define GLM_HAS_INITIALIZER_LISTS 1 +#else +# define GLM_HAS_INITIALIZER_LISTS ((GLM_LANG & GLM_LANG_CXX0X_FLAG) && (\ + ((GLM_COMPILER & GLM_COMPILER_VC) && (GLM_COMPILER >= GLM_COMPILER_VC15)) || \ + ((GLM_COMPILER & GLM_COMPILER_INTEL) && (GLM_COMPILER >= GLM_COMPILER_INTEL14)) || \ + ((GLM_COMPILER & GLM_COMPILER_CUDA) && (GLM_COMPILER >= GLM_COMPILER_CUDA75)))) +#endif + +// N2544 Unrestricted unions http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2008/n2544.pdf +#if GLM_COMPILER & GLM_COMPILER_CLANG +# define GLM_HAS_UNRESTRICTED_UNIONS __has_feature(cxx_unrestricted_unions) +#elif GLM_LANG & GLM_LANG_CXX11_FLAG +# define GLM_HAS_UNRESTRICTED_UNIONS 1 +#else +# define GLM_HAS_UNRESTRICTED_UNIONS (GLM_LANG & GLM_LANG_CXX0X_FLAG) && (\ + (GLM_COMPILER & GLM_COMPILER_VC) || \ + ((GLM_COMPILER & GLM_COMPILER_CUDA) && (GLM_COMPILER >= GLM_COMPILER_CUDA75))) +#endif + +// N2346 +#if GLM_COMPILER & GLM_COMPILER_CLANG +# define GLM_HAS_DEFAULTED_FUNCTIONS __has_feature(cxx_defaulted_functions) +#elif GLM_LANG & GLM_LANG_CXX11_FLAG +# define GLM_HAS_DEFAULTED_FUNCTIONS 1 +#else +# define GLM_HAS_DEFAULTED_FUNCTIONS ((GLM_LANG & GLM_LANG_CXX0X_FLAG) && (\ + ((GLM_COMPILER & GLM_COMPILER_VC) && (GLM_COMPILER >= GLM_COMPILER_VC12)) || \ + ((GLM_COMPILER & GLM_COMPILER_INTEL)) || \ + (GLM_COMPILER & GLM_COMPILER_CUDA))) +#endif + +// N2118 +#if GLM_COMPILER & GLM_COMPILER_CLANG +# define GLM_HAS_RVALUE_REFERENCES __has_feature(cxx_rvalue_references) +#elif GLM_LANG & GLM_LANG_CXX11_FLAG +# define GLM_HAS_RVALUE_REFERENCES 1 +#else +# define GLM_HAS_RVALUE_REFERENCES ((GLM_LANG & GLM_LANG_CXX0X_FLAG) && (\ + ((GLM_COMPILER & GLM_COMPILER_VC)) || \ + ((GLM_COMPILER & GLM_COMPILER_CUDA)))) +#endif + +// N2437 http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2007/n2437.pdf +#if GLM_COMPILER & GLM_COMPILER_CLANG +# define GLM_HAS_EXPLICIT_CONVERSION_OPERATORS __has_feature(cxx_explicit_conversions) +#elif GLM_LANG & GLM_LANG_CXX11_FLAG +# define GLM_HAS_EXPLICIT_CONVERSION_OPERATORS 1 +#else +# define GLM_HAS_EXPLICIT_CONVERSION_OPERATORS ((GLM_LANG & GLM_LANG_CXX0X_FLAG) && (\ + ((GLM_COMPILER & GLM_COMPILER_INTEL) && (GLM_COMPILER >= GLM_COMPILER_INTEL14)) || \ + ((GLM_COMPILER & GLM_COMPILER_VC) && (GLM_COMPILER >= GLM_COMPILER_VC12)) || \ + ((GLM_COMPILER & GLM_COMPILER_CUDA)))) +#endif + +// N2258 http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2007/n2258.pdf +#if GLM_COMPILER & GLM_COMPILER_CLANG +# define GLM_HAS_TEMPLATE_ALIASES __has_feature(cxx_alias_templates) +#elif GLM_LANG & GLM_LANG_CXX11_FLAG +# define GLM_HAS_TEMPLATE_ALIASES 1 +#else +# define GLM_HAS_TEMPLATE_ALIASES ((GLM_LANG & GLM_LANG_CXX0X_FLAG) && (\ + ((GLM_COMPILER & GLM_COMPILER_INTEL)) || \ + ((GLM_COMPILER & GLM_COMPILER_VC) && (GLM_COMPILER >= GLM_COMPILER_VC12)) || \ + ((GLM_COMPILER & GLM_COMPILER_CUDA)))) +#endif + +// N2930 http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2009/n2930.html +#if GLM_COMPILER & GLM_COMPILER_CLANG +# define GLM_HAS_RANGE_FOR __has_feature(cxx_range_for) +#elif GLM_LANG & GLM_LANG_CXX11_FLAG +# define GLM_HAS_RANGE_FOR 1 +#else +# define GLM_HAS_RANGE_FOR ((GLM_LANG & GLM_LANG_CXX0X_FLAG) && (\ + ((GLM_COMPILER & GLM_COMPILER_INTEL)) || \ + ((GLM_COMPILER & GLM_COMPILER_VC)) || \ + ((GLM_COMPILER & GLM_COMPILER_CUDA)))) +#endif + +// N2341 http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2007/n2341.pdf +#if GLM_COMPILER & GLM_COMPILER_CLANG +# define GLM_HAS_ALIGNOF __has_feature(cxx_alignas) +#elif GLM_LANG & GLM_LANG_CXX11_FLAG +# define GLM_HAS_ALIGNOF 1 +#else +# define GLM_HAS_ALIGNOF ((GLM_LANG & GLM_LANG_CXX0X_FLAG) && (\ + ((GLM_COMPILER & GLM_COMPILER_INTEL) && (GLM_COMPILER >= GLM_COMPILER_INTEL15)) || \ + ((GLM_COMPILER & GLM_COMPILER_VC) && (GLM_COMPILER >= GLM_COMPILER_VC14)) || \ + ((GLM_COMPILER & GLM_COMPILER_CUDA) && (GLM_COMPILER >= GLM_COMPILER_CUDA70)))) +#endif + +// N2235 Generalized Constant Expressions http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2007/n2235.pdf +// N3652 Extended Constant Expressions http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2013/n3652.html +#if (GLM_ARCH & GLM_ARCH_SIMD_BIT) // Compiler SIMD intrinsics don't support constexpr... +# define GLM_HAS_CONSTEXPR 0 +#elif (GLM_COMPILER & GLM_COMPILER_CLANG) +# define GLM_HAS_CONSTEXPR __has_feature(cxx_relaxed_constexpr) +#elif (GLM_LANG & GLM_LANG_CXX14_FLAG) +# define GLM_HAS_CONSTEXPR 1 +#else +# define GLM_HAS_CONSTEXPR ((GLM_LANG & GLM_LANG_CXX0X_FLAG) && GLM_HAS_INITIALIZER_LISTS && (\ + ((GLM_COMPILER & GLM_COMPILER_INTEL) && (GLM_COMPILER >= GLM_COMPILER_INTEL17)) || \ + ((GLM_COMPILER & GLM_COMPILER_VC) && (GLM_COMPILER >= GLM_COMPILER_VC15)))) +#endif + +#if GLM_HAS_CONSTEXPR +# define GLM_CONSTEXPR constexpr +#else +# define GLM_CONSTEXPR +#endif + +// +#if GLM_HAS_CONSTEXPR +# if (GLM_COMPILER & GLM_COMPILER_CLANG) +# define GLM_HAS_IF_CONSTEXPR __has_feature(cxx_if_constexpr) +# elif (GLM_COMPILER & GLM_COMPILER_GCC) +# define GLM_HAS_IF_CONSTEXPR GLM_COMPILER >= GLM_COMPILER_GCC7 +# elif (GLM_LANG & GLM_LANG_CXX17_FLAG) +# define GLM_HAS_IF_CONSTEXPR 1 +# else +# define GLM_HAS_IF_CONSTEXPR 0 +# endif +#else +# define GLM_HAS_IF_CONSTEXPR 0 +#endif + +#if GLM_HAS_IF_CONSTEXPR +# define GLM_IF_CONSTEXPR if constexpr +#else +# define GLM_IF_CONSTEXPR if +#endif + +// +#if GLM_LANG & GLM_LANG_CXX11_FLAG +# define GLM_HAS_ASSIGNABLE 1 +#else +# define GLM_HAS_ASSIGNABLE ((GLM_LANG & GLM_LANG_CXX0X_FLAG) && (\ + ((GLM_COMPILER & GLM_COMPILER_VC) && (GLM_COMPILER >= GLM_COMPILER_VC15)) || \ + ((GLM_COMPILER & GLM_COMPILER_GCC) && (GLM_COMPILER >= GLM_COMPILER_GCC49)))) +#endif + +// +#define GLM_HAS_TRIVIAL_QUERIES 0 + +// +#if GLM_LANG & GLM_LANG_CXX11_FLAG +# define GLM_HAS_MAKE_SIGNED 1 +#else +# define GLM_HAS_MAKE_SIGNED ((GLM_LANG & GLM_LANG_CXX0X_FLAG) && (\ + ((GLM_COMPILER & GLM_COMPILER_VC) && (GLM_COMPILER >= GLM_COMPILER_VC12)) || \ + ((GLM_COMPILER & GLM_COMPILER_CUDA)))) +#endif + +// +#if defined(GLM_FORCE_INTRINSICS) +# define GLM_HAS_BITSCAN_WINDOWS ((GLM_PLATFORM & GLM_PLATFORM_WINDOWS) && (\ + ((GLM_COMPILER & GLM_COMPILER_INTEL)) || \ + ((GLM_COMPILER & GLM_COMPILER_VC) && (GLM_COMPILER >= GLM_COMPILER_VC14) && (GLM_ARCH & GLM_ARCH_X86_BIT)))) +#else +# define GLM_HAS_BITSCAN_WINDOWS 0 +#endif + +/////////////////////////////////////////////////////////////////////////////////// +// OpenMP +#ifdef _OPENMP +# if GLM_COMPILER & GLM_COMPILER_GCC +# if GLM_COMPILER >= GLM_COMPILER_GCC61 +# define GLM_HAS_OPENMP 45 +# elif GLM_COMPILER >= GLM_COMPILER_GCC49 +# define GLM_HAS_OPENMP 40 +# elif GLM_COMPILER >= GLM_COMPILER_GCC47 +# define GLM_HAS_OPENMP 31 +# else +# define GLM_HAS_OPENMP 0 +# endif +# elif GLM_COMPILER & GLM_COMPILER_CLANG +# if GLM_COMPILER >= GLM_COMPILER_CLANG38 +# define GLM_HAS_OPENMP 31 +# else +# define GLM_HAS_OPENMP 0 +# endif +# elif GLM_COMPILER & GLM_COMPILER_VC +# define GLM_HAS_OPENMP 20 +# elif GLM_COMPILER & GLM_COMPILER_INTEL +# if GLM_COMPILER >= GLM_COMPILER_INTEL16 +# define GLM_HAS_OPENMP 40 +# else +# define GLM_HAS_OPENMP 0 +# endif +# else +# define GLM_HAS_OPENMP 0 +# endif +#else +# define GLM_HAS_OPENMP 0 +#endif + +/////////////////////////////////////////////////////////////////////////////////// +// nullptr + +#if GLM_LANG & GLM_LANG_CXX0X_FLAG +# define GLM_CONFIG_NULLPTR GLM_ENABLE +#else +# define GLM_CONFIG_NULLPTR GLM_DISABLE +#endif + +#if GLM_CONFIG_NULLPTR == GLM_ENABLE +# define GLM_NULLPTR nullptr +#else +# define GLM_NULLPTR 0 +#endif + +/////////////////////////////////////////////////////////////////////////////////// +// Static assert + +#if GLM_HAS_STATIC_ASSERT +# define GLM_STATIC_ASSERT(x, message) static_assert(x, message) +#elif GLM_COMPILER & GLM_COMPILER_VC +# define GLM_STATIC_ASSERT(x, message) typedef char __CASSERT__##__LINE__[(x) ? 1 : -1] +#else +# define GLM_STATIC_ASSERT(x, message) assert(x) +#endif//GLM_LANG + +/////////////////////////////////////////////////////////////////////////////////// +// Qualifiers + +#if GLM_COMPILER & GLM_COMPILER_CUDA +# define GLM_CUDA_FUNC_DEF __device__ __host__ +# define GLM_CUDA_FUNC_DECL __device__ __host__ +#else +# define GLM_CUDA_FUNC_DEF +# define GLM_CUDA_FUNC_DECL +#endif + +#if defined(GLM_FORCE_INLINE) +# if GLM_COMPILER & GLM_COMPILER_VC +# define GLM_INLINE __forceinline +# define GLM_NEVER_INLINE __declspec((noinline)) +# elif GLM_COMPILER & (GLM_COMPILER_GCC | GLM_COMPILER_CLANG) +# define GLM_INLINE inline __attribute__((__always_inline__)) +# define GLM_NEVER_INLINE __attribute__((__noinline__)) +# elif GLM_COMPILER & GLM_COMPILER_CUDA +# define GLM_INLINE __forceinline__ +# define GLM_NEVER_INLINE __noinline__ +# else +# define GLM_INLINE inline +# define GLM_NEVER_INLINE +# endif//GLM_COMPILER +#else +# define GLM_INLINE inline +# define GLM_NEVER_INLINE +#endif//defined(GLM_FORCE_INLINE) + +#define GLM_FUNC_DECL GLM_CUDA_FUNC_DECL +#define GLM_FUNC_QUALIFIER GLM_CUDA_FUNC_DEF GLM_INLINE + +/////////////////////////////////////////////////////////////////////////////////// +// Swizzle operators + +// User defines: GLM_FORCE_SWIZZLE + +#define GLM_SWIZZLE_DISABLED 0 +#define GLM_SWIZZLE_OPERATOR 1 +#define GLM_SWIZZLE_FUNCTION 2 + +#if defined(GLM_FORCE_XYZW_ONLY) +# undef GLM_FORCE_SWIZZLE +#endif + +#if defined(GLM_SWIZZLE) +# pragma message("GLM: GLM_SWIZZLE is deprecated, use GLM_FORCE_SWIZZLE instead.") +# define GLM_FORCE_SWIZZLE +#endif + +#if defined(GLM_FORCE_SWIZZLE) && (GLM_LANG & GLM_LANG_CXXMS_FLAG) +# define GLM_CONFIG_SWIZZLE GLM_SWIZZLE_OPERATOR +#elif defined(GLM_FORCE_SWIZZLE) +# define GLM_CONFIG_SWIZZLE GLM_SWIZZLE_FUNCTION +#else +# define GLM_CONFIG_SWIZZLE GLM_SWIZZLE_DISABLED +#endif + +/////////////////////////////////////////////////////////////////////////////////// +// Allows using not basic types as genType + +// #define GLM_FORCE_UNRESTRICTED_GENTYPE + +#ifdef GLM_FORCE_UNRESTRICTED_GENTYPE +# define GLM_CONFIG_UNRESTRICTED_GENTYPE GLM_ENABLE +#else +# define GLM_CONFIG_UNRESTRICTED_GENTYPE GLM_DISABLE +#endif + +/////////////////////////////////////////////////////////////////////////////////// +// Clip control, define GLM_FORCE_DEPTH_ZERO_TO_ONE before including GLM +// to use a clip space between 0 to 1. +// Coordinate system, define GLM_FORCE_LEFT_HANDED before including GLM +// to use left handed coordinate system by default. + +#define GLM_CLIP_CONTROL_ZO_BIT (1 << 0) // ZERO_TO_ONE +#define GLM_CLIP_CONTROL_NO_BIT (1 << 1) // NEGATIVE_ONE_TO_ONE +#define GLM_CLIP_CONTROL_LH_BIT (1 << 2) // LEFT_HANDED, For DirectX, Metal, Vulkan +#define GLM_CLIP_CONTROL_RH_BIT (1 << 3) // RIGHT_HANDED, For OpenGL, default in GLM + +#define GLM_CLIP_CONTROL_LH_ZO (GLM_CLIP_CONTROL_LH_BIT | GLM_CLIP_CONTROL_ZO_BIT) +#define GLM_CLIP_CONTROL_LH_NO (GLM_CLIP_CONTROL_LH_BIT | GLM_CLIP_CONTROL_NO_BIT) +#define GLM_CLIP_CONTROL_RH_ZO (GLM_CLIP_CONTROL_RH_BIT | GLM_CLIP_CONTROL_ZO_BIT) +#define GLM_CLIP_CONTROL_RH_NO (GLM_CLIP_CONTROL_RH_BIT | GLM_CLIP_CONTROL_NO_BIT) + +#ifdef GLM_FORCE_DEPTH_ZERO_TO_ONE +# ifdef GLM_FORCE_LEFT_HANDED +# define GLM_CONFIG_CLIP_CONTROL GLM_CLIP_CONTROL_LH_ZO +# else +# define GLM_CONFIG_CLIP_CONTROL GLM_CLIP_CONTROL_RH_ZO +# endif +#else +# ifdef GLM_FORCE_LEFT_HANDED +# define GLM_CONFIG_CLIP_CONTROL GLM_CLIP_CONTROL_LH_NO +# else +# define GLM_CONFIG_CLIP_CONTROL GLM_CLIP_CONTROL_RH_NO +# endif +#endif + +/////////////////////////////////////////////////////////////////////////////////// +// Qualifiers + +#if (GLM_COMPILER & GLM_COMPILER_VC) || ((GLM_COMPILER & GLM_COMPILER_INTEL) && (GLM_PLATFORM & GLM_PLATFORM_WINDOWS)) +# define GLM_DEPRECATED __declspec(deprecated) +# define GLM_ALIGNED_TYPEDEF(type, name, alignment) typedef __declspec(align(alignment)) type name +#elif GLM_COMPILER & (GLM_COMPILER_GCC | GLM_COMPILER_CLANG | GLM_COMPILER_INTEL) +# define GLM_DEPRECATED __attribute__((__deprecated__)) +# define GLM_ALIGNED_TYPEDEF(type, name, alignment) typedef type name __attribute__((aligned(alignment))) +#elif GLM_COMPILER & GLM_COMPILER_CUDA +# define GLM_DEPRECATED +# define GLM_ALIGNED_TYPEDEF(type, name, alignment) typedef type name __align__(x) +#else +# define GLM_DEPRECATED +# define GLM_ALIGNED_TYPEDEF(type, name, alignment) typedef type name +#endif + +/////////////////////////////////////////////////////////////////////////////////// + +#ifdef GLM_FORCE_EXPLICIT_CTOR +# define GLM_EXPLICIT explicit +#else +# define GLM_EXPLICIT +#endif + +/////////////////////////////////////////////////////////////////////////////////// +// Length type: all length functions returns a length_t type. +// When GLM_FORCE_SIZE_T_LENGTH is defined, length_t is a typedef of size_t otherwise +// length_t is a typedef of int like GLSL defines it. + +#define GLM_LENGTH_INT 1 +#define GLM_LENGTH_SIZE_T 2 + +#ifdef GLM_FORCE_SIZE_T_LENGTH +# define GLM_CONFIG_LENGTH_TYPE GLM_LENGTH_SIZE_T +#else +# define GLM_CONFIG_LENGTH_TYPE GLM_LENGTH_INT +#endif + +namespace glm +{ + using std::size_t; +# if GLM_CONFIG_LENGTH_TYPE == GLM_LENGTH_SIZE_T + typedef size_t length_t; +# else + typedef int length_t; +# endif +}//namespace glm + +/////////////////////////////////////////////////////////////////////////////////// +// constexpr + +#if GLM_HAS_CONSTEXPR +# define GLM_CONFIG_CONSTEXP GLM_ENABLE + + namespace glm + { + template + constexpr std::size_t countof(T const (&)[N]) + { + return N; + } + }//namespace glm +# define GLM_COUNTOF(arr) glm::countof(arr) +#elif defined(_MSC_VER) +# define GLM_CONFIG_CONSTEXP GLM_DISABLE + +# define GLM_COUNTOF(arr) _countof(arr) +#else +# define GLM_CONFIG_CONSTEXP GLM_DISABLE + +# define GLM_COUNTOF(arr) sizeof(arr) / sizeof(arr[0]) +#endif + +/////////////////////////////////////////////////////////////////////////////////// +// uint + +namespace glm{ +namespace detail +{ + template + struct is_int + { + enum test {value = 0}; + }; + + template<> + struct is_int + { + enum test {value = ~0}; + }; + + template<> + struct is_int + { + enum test {value = ~0}; + }; +}//namespace detail + + typedef unsigned int uint; +}//namespace glm + +/////////////////////////////////////////////////////////////////////////////////// +// 64-bit int + +#if GLM_HAS_EXTENDED_INTEGER_TYPE +# include +#endif + +namespace glm{ +namespace detail +{ +# if GLM_HAS_EXTENDED_INTEGER_TYPE + typedef std::uint64_t uint64; + typedef std::int64_t int64; +# elif (defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L)) // C99 detected, 64 bit types available + typedef uint64_t uint64; + typedef int64_t int64; +# elif GLM_COMPILER & GLM_COMPILER_VC + typedef unsigned __int64 uint64; + typedef signed __int64 int64; +# elif GLM_COMPILER & GLM_COMPILER_GCC +# pragma GCC diagnostic ignored "-Wlong-long" + __extension__ typedef unsigned long long uint64; + __extension__ typedef signed long long int64; +# elif (GLM_COMPILER & GLM_COMPILER_CLANG) +# pragma clang diagnostic ignored "-Wc++11-long-long" + typedef unsigned long long uint64; + typedef signed long long int64; +# else//unknown compiler + typedef unsigned long long uint64; + typedef signed long long int64; +# endif +}//namespace detail +}//namespace glm + +/////////////////////////////////////////////////////////////////////////////////// +// make_unsigned + +#if GLM_HAS_MAKE_SIGNED +# include + +namespace glm{ +namespace detail +{ + using std::make_unsigned; +}//namespace detail +}//namespace glm + +#else + +namespace glm{ +namespace detail +{ + template + struct make_unsigned + {}; + + template<> + struct make_unsigned + { + typedef unsigned char type; + }; + + template<> + struct make_unsigned + { + typedef unsigned short type; + }; + + template<> + struct make_unsigned + { + typedef unsigned int type; + }; + + template<> + struct make_unsigned + { + typedef unsigned long type; + }; + + template<> + struct make_unsigned + { + typedef uint64 type; + }; + + template<> + struct make_unsigned + { + typedef unsigned char type; + }; + + template<> + struct make_unsigned + { + typedef unsigned short type; + }; + + template<> + struct make_unsigned + { + typedef unsigned int type; + }; + + template<> + struct make_unsigned + { + typedef unsigned long type; + }; + + template<> + struct make_unsigned + { + typedef uint64 type; + }; +}//namespace detail +}//namespace glm +#endif + +/////////////////////////////////////////////////////////////////////////////////// +// Only use x, y, z, w as vector type components + +#ifdef GLM_FORCE_XYZW_ONLY +# define GLM_CONFIG_XYZW_ONLY GLM_ENABLE +#else +# define GLM_CONFIG_XYZW_ONLY GLM_DISABLE +#endif + +/////////////////////////////////////////////////////////////////////////////////// +// Configure the use of defaulted initialized types + +#define GLM_CTOR_INIT_DISABLE 0 +#define GLM_CTOR_INITIALIZER_LIST 1 +#define GLM_CTOR_INITIALISATION 2 + +#if defined(GLM_FORCE_CTOR_INIT) && GLM_HAS_INITIALIZER_LISTS +# define GLM_CONFIG_CTOR_INIT GLM_CTOR_INITIALIZER_LIST +#elif defined(GLM_FORCE_CTOR_INIT) && !GLM_HAS_INITIALIZER_LISTS +# define GLM_CONFIG_CTOR_INIT GLM_CTOR_INITIALISATION +#else +# define GLM_CONFIG_CTOR_INIT GLM_CTOR_INIT_DISABLE +#endif + +/////////////////////////////////////////////////////////////////////////////////// +// Use SIMD instruction sets + +#if GLM_HAS_ALIGNOF && (GLM_LANG & GLM_LANG_CXXMS_FLAG) && (GLM_ARCH & GLM_ARCH_SIMD_BIT) +# define GLM_CONFIG_SIMD GLM_ENABLE +#else +# define GLM_CONFIG_SIMD GLM_DISABLE +#endif + +/////////////////////////////////////////////////////////////////////////////////// +// Configure the use of defaulted function + +#if GLM_HAS_DEFAULTED_FUNCTIONS && GLM_CONFIG_CTOR_INIT == GLM_CTOR_INIT_DISABLE +# define GLM_CONFIG_DEFAULTED_FUNCTIONS GLM_ENABLE +# define GLM_DEFAULT = default +#else +# define GLM_CONFIG_DEFAULTED_FUNCTIONS GLM_DISABLE +# define GLM_DEFAULT +#endif + +/////////////////////////////////////////////////////////////////////////////////// +// Configure the use of aligned gentypes + +#ifdef GLM_FORCE_ALIGNED // Legacy define +# define GLM_FORCE_DEFAULT_ALIGNED_GENTYPES +#endif + +#ifdef GLM_FORCE_DEFAULT_ALIGNED_GENTYPES +# define GLM_FORCE_ALIGNED_GENTYPES +#endif + +#if GLM_HAS_ALIGNOF && (GLM_LANG & GLM_LANG_CXXMS_FLAG) && (defined(GLM_FORCE_ALIGNED_GENTYPES) || (GLM_CONFIG_SIMD == GLM_ENABLE)) +# define GLM_CONFIG_ALIGNED_GENTYPES GLM_ENABLE +#else +# define GLM_CONFIG_ALIGNED_GENTYPES GLM_DISABLE +#endif + +/////////////////////////////////////////////////////////////////////////////////// +// Configure the use of anonymous structure as implementation detail + +#if ((GLM_CONFIG_SIMD == GLM_ENABLE) || (GLM_CONFIG_SWIZZLE == GLM_SWIZZLE_OPERATOR) || (GLM_CONFIG_ALIGNED_GENTYPES == GLM_ENABLE)) +# define GLM_CONFIG_ANONYMOUS_STRUCT GLM_ENABLE +#else +# define GLM_CONFIG_ANONYMOUS_STRUCT GLM_DISABLE +#endif + +/////////////////////////////////////////////////////////////////////////////////// +// Silent warnings + +#ifdef GLM_FORCE_SILENT_WARNINGS +# define GLM_SILENT_WARNINGS GLM_ENABLE +#else +# define GLM_SILENT_WARNINGS GLM_DISABLE +#endif + +/////////////////////////////////////////////////////////////////////////////////// +// Precision + +#define GLM_HIGHP 1 +#define GLM_MEDIUMP 2 +#define GLM_LOWP 3 + +#if defined(GLM_FORCE_PRECISION_HIGHP_BOOL) || defined(GLM_PRECISION_HIGHP_BOOL) +# define GLM_CONFIG_PRECISION_BOOL GLM_HIGHP +#elif defined(GLM_FORCE_PRECISION_MEDIUMP_BOOL) || defined(GLM_PRECISION_MEDIUMP_BOOL) +# define GLM_CONFIG_PRECISION_BOOL GLM_MEDIUMP +#elif defined(GLM_FORCE_PRECISION_LOWP_BOOL) || defined(GLM_PRECISION_LOWP_BOOL) +# define GLM_CONFIG_PRECISION_BOOL GLM_LOWP +#else +# define GLM_CONFIG_PRECISION_BOOL GLM_HIGHP +#endif + +#if defined(GLM_FORCE_PRECISION_HIGHP_INT) || defined(GLM_PRECISION_HIGHP_INT) +# define GLM_CONFIG_PRECISION_INT GLM_HIGHP +#elif defined(GLM_FORCE_PRECISION_MEDIUMP_INT) || defined(GLM_PRECISION_MEDIUMP_INT) +# define GLM_CONFIG_PRECISION_INT GLM_MEDIUMP +#elif defined(GLM_FORCE_PRECISION_LOWP_INT) || defined(GLM_PRECISION_LOWP_INT) +# define GLM_CONFIG_PRECISION_INT GLM_LOWP +#else +# define GLM_CONFIG_PRECISION_INT GLM_HIGHP +#endif + +#if defined(GLM_FORCE_PRECISION_HIGHP_UINT) || defined(GLM_PRECISION_HIGHP_UINT) +# define GLM_CONFIG_PRECISION_UINT GLM_HIGHP +#elif defined(GLM_FORCE_PRECISION_MEDIUMP_UINT) || defined(GLM_PRECISION_MEDIUMP_UINT) +# define GLM_CONFIG_PRECISION_UINT GLM_MEDIUMP +#elif defined(GLM_FORCE_PRECISION_LOWP_UINT) || defined(GLM_PRECISION_LOWP_UINT) +# define GLM_CONFIG_PRECISION_UINT GLM_LOWP +#else +# define GLM_CONFIG_PRECISION_UINT GLM_HIGHP +#endif + +#if defined(GLM_FORCE_PRECISION_HIGHP_FLOAT) || defined(GLM_PRECISION_HIGHP_FLOAT) +# define GLM_CONFIG_PRECISION_FLOAT GLM_HIGHP +#elif defined(GLM_FORCE_PRECISION_MEDIUMP_FLOAT) || defined(GLM_PRECISION_MEDIUMP_FLOAT) +# define GLM_CONFIG_PRECISION_FLOAT GLM_MEDIUMP +#elif defined(GLM_FORCE_PRECISION_LOWP_FLOAT) || defined(GLM_PRECISION_LOWP_FLOAT) +# define GLM_CONFIG_PRECISION_FLOAT GLM_LOWP +#else +# define GLM_CONFIG_PRECISION_FLOAT GLM_HIGHP +#endif + +#if defined(GLM_FORCE_PRECISION_HIGHP_DOUBLE) || defined(GLM_PRECISION_HIGHP_DOUBLE) +# define GLM_CONFIG_PRECISION_DOUBLE GLM_HIGHP +#elif defined(GLM_FORCE_PRECISION_MEDIUMP_DOUBLE) || defined(GLM_PRECISION_MEDIUMP_DOUBLE) +# define GLM_CONFIG_PRECISION_DOUBLE GLM_MEDIUMP +#elif defined(GLM_FORCE_PRECISION_LOWP_DOUBLE) || defined(GLM_PRECISION_LOWP_DOUBLE) +# define GLM_CONFIG_PRECISION_DOUBLE GLM_LOWP +#else +# define GLM_CONFIG_PRECISION_DOUBLE GLM_HIGHP +#endif + +/////////////////////////////////////////////////////////////////////////////////// +// Check inclusions of different versions of GLM + +#elif ((GLM_SETUP_INCLUDED != GLM_VERSION) && !defined(GLM_FORCE_IGNORE_VERSION)) +# error "GLM error: A different version of GLM is already included. Define GLM_FORCE_IGNORE_VERSION before including GLM headers to ignore this error." +#elif GLM_SETUP_INCLUDED == GLM_VERSION + +/////////////////////////////////////////////////////////////////////////////////// +// Messages + +#if GLM_MESSAGES == GLM_ENABLE && !defined(GLM_MESSAGE_DISPLAYED) +# define GLM_MESSAGE_DISPLAYED +# define GLM_STR_HELPER(x) #x +# define GLM_STR(x) GLM_STR_HELPER(x) + + // Report GLM version +# pragma message (GLM_STR(GLM_VERSION_MESSAGE)) + + // Report C++ language +# if (GLM_LANG & GLM_LANG_CXX2A_FLAG) && (GLM_LANG & GLM_LANG_EXT) +# pragma message("GLM: C++ 2A with extensions") +# elif (GLM_LANG & GLM_LANG_CXX2A_FLAG) +# pragma message("GLM: C++ 2A") +# elif (GLM_LANG & GLM_LANG_CXX17_FLAG) && (GLM_LANG & GLM_LANG_EXT) +# pragma message("GLM: C++ 17 with extensions") +# elif (GLM_LANG & GLM_LANG_CXX17_FLAG) +# pragma message("GLM: C++ 17") +# elif (GLM_LANG & GLM_LANG_CXX14_FLAG) && (GLM_LANG & GLM_LANG_EXT) +# pragma message("GLM: C++ 14 with extensions") +# elif (GLM_LANG & GLM_LANG_CXX14_FLAG) +# pragma message("GLM: C++ 14") +# elif (GLM_LANG & GLM_LANG_CXX11_FLAG) && (GLM_LANG & GLM_LANG_EXT) +# pragma message("GLM: C++ 11 with extensions") +# elif (GLM_LANG & GLM_LANG_CXX11_FLAG) +# pragma message("GLM: C++ 11") +# elif (GLM_LANG & GLM_LANG_CXX0X_FLAG) && (GLM_LANG & GLM_LANG_EXT) +# pragma message("GLM: C++ 0x with extensions") +# elif (GLM_LANG & GLM_LANG_CXX0X_FLAG) +# pragma message("GLM: C++ 0x") +# elif (GLM_LANG & GLM_LANG_CXX03_FLAG) && (GLM_LANG & GLM_LANG_EXT) +# pragma message("GLM: C++ 03 with extensions") +# elif (GLM_LANG & GLM_LANG_CXX03_FLAG) +# pragma message("GLM: C++ 03") +# elif (GLM_LANG & GLM_LANG_CXX98_FLAG) && (GLM_LANG & GLM_LANG_EXT) +# pragma message("GLM: C++ 98 with extensions") +# elif (GLM_LANG & GLM_LANG_CXX98_FLAG) +# pragma message("GLM: C++ 98") +# else +# pragma message("GLM: C++ language undetected") +# endif//GLM_LANG + + // Report compiler detection +# if GLM_COMPILER & GLM_COMPILER_CUDA +# pragma message("GLM: CUDA compiler detected") +# elif GLM_COMPILER & GLM_COMPILER_VC +# pragma message("GLM: Visual C++ compiler detected") +# elif GLM_COMPILER & GLM_COMPILER_CLANG +# pragma message("GLM: Clang compiler detected") +# elif GLM_COMPILER & GLM_COMPILER_INTEL +# pragma message("GLM: Intel Compiler detected") +# elif GLM_COMPILER & GLM_COMPILER_GCC +# pragma message("GLM: GCC compiler detected") +# else +# pragma message("GLM: Compiler not detected") +# endif + + // Report build target +# if (GLM_ARCH & GLM_ARCH_AVX2_BIT) && (GLM_MODEL == GLM_MODEL_64) +# pragma message("GLM: x86 64 bits with AVX2 instruction set build target") +# elif (GLM_ARCH & GLM_ARCH_AVX2_BIT) && (GLM_MODEL == GLM_MODEL_32) +# pragma message("GLM: x86 32 bits with AVX2 instruction set build target") + +# elif (GLM_ARCH & GLM_ARCH_AVX_BIT) && (GLM_MODEL == GLM_MODEL_64) +# pragma message("GLM: x86 64 bits with AVX instruction set build target") +# elif (GLM_ARCH & GLM_ARCH_AVX_BIT) && (GLM_MODEL == GLM_MODEL_32) +# pragma message("GLM: x86 32 bits with AVX instruction set build target") + +# elif (GLM_ARCH & GLM_ARCH_SSE42_BIT) && (GLM_MODEL == GLM_MODEL_64) +# pragma message("GLM: x86 64 bits with SSE4.2 instruction set build target") +# elif (GLM_ARCH & GLM_ARCH_SSE42_BIT) && (GLM_MODEL == GLM_MODEL_32) +# pragma message("GLM: x86 32 bits with SSE4.2 instruction set build target") + +# elif (GLM_ARCH & GLM_ARCH_SSE41_BIT) && (GLM_MODEL == GLM_MODEL_64) +# pragma message("GLM: x86 64 bits with SSE4.1 instruction set build target") +# elif (GLM_ARCH & GLM_ARCH_SSE41_BIT) && (GLM_MODEL == GLM_MODEL_32) +# pragma message("GLM: x86 32 bits with SSE4.1 instruction set build target") + +# elif (GLM_ARCH & GLM_ARCH_SSSE3_BIT) && (GLM_MODEL == GLM_MODEL_64) +# pragma message("GLM: x86 64 bits with SSSE3 instruction set build target") +# elif (GLM_ARCH & GLM_ARCH_SSSE3_BIT) && (GLM_MODEL == GLM_MODEL_32) +# pragma message("GLM: x86 32 bits with SSSE3 instruction set build target") + +# elif (GLM_ARCH & GLM_ARCH_SSE3_BIT) && (GLM_MODEL == GLM_MODEL_64) +# pragma message("GLM: x86 64 bits with SSE3 instruction set build target") +# elif (GLM_ARCH & GLM_ARCH_SSE3_BIT) && (GLM_MODEL == GLM_MODEL_32) +# pragma message("GLM: x86 32 bits with SSE3 instruction set build target") + +# elif (GLM_ARCH & GLM_ARCH_SSE2_BIT) && (GLM_MODEL == GLM_MODEL_64) +# pragma message("GLM: x86 64 bits with SSE2 instruction set build target") +# elif (GLM_ARCH & GLM_ARCH_SSE2_BIT) && (GLM_MODEL == GLM_MODEL_32) +# pragma message("GLM: x86 32 bits with SSE2 instruction set build target") + +# elif (GLM_ARCH & GLM_ARCH_X86_BIT) && (GLM_MODEL == GLM_MODEL_64) +# pragma message("GLM: x86 64 bits build target") +# elif (GLM_ARCH & GLM_ARCH_X86_BIT) && (GLM_MODEL == GLM_MODEL_32) +# pragma message("GLM: x86 32 bits build target") + +# elif (GLM_ARCH & GLM_ARCH_NEON_BIT) && (GLM_MODEL == GLM_MODEL_64) +# pragma message("GLM: ARM 64 bits with Neon instruction set build target") +# elif (GLM_ARCH & GLM_ARCH_NEON_BIT) && (GLM_MODEL == GLM_MODEL_32) +# pragma message("GLM: ARM 32 bits with Neon instruction set build target") + +# elif (GLM_ARCH & GLM_ARCH_ARM_BIT) && (GLM_MODEL == GLM_MODEL_64) +# pragma message("GLM: ARM 64 bits build target") +# elif (GLM_ARCH & GLM_ARCH_ARM_BIT) && (GLM_MODEL == GLM_MODEL_32) +# pragma message("GLM: ARM 32 bits build target") + +# elif (GLM_ARCH & GLM_ARCH_MIPS_BIT) && (GLM_MODEL == GLM_MODEL_64) +# pragma message("GLM: MIPS 64 bits build target") +# elif (GLM_ARCH & GLM_ARCH_MIPS_BIT) && (GLM_MODEL == GLM_MODEL_32) +# pragma message("GLM: MIPS 32 bits build target") + +# elif (GLM_ARCH & GLM_ARCH_PPC_BIT) && (GLM_MODEL == GLM_MODEL_64) +# pragma message("GLM: PowerPC 64 bits build target") +# elif (GLM_ARCH & GLM_ARCH_PPC_BIT) && (GLM_MODEL == GLM_MODEL_32) +# pragma message("GLM: PowerPC 32 bits build target") +# else +# pragma message("GLM: Unknown build target") +# endif//GLM_ARCH + + // Report platform name +# if(GLM_PLATFORM & GLM_PLATFORM_QNXNTO) +# pragma message("GLM: QNX platform detected") +//# elif(GLM_PLATFORM & GLM_PLATFORM_IOS) +//# pragma message("GLM: iOS platform detected") +# elif(GLM_PLATFORM & GLM_PLATFORM_APPLE) +# pragma message("GLM: Apple platform detected") +# elif(GLM_PLATFORM & GLM_PLATFORM_WINCE) +# pragma message("GLM: WinCE platform detected") +# elif(GLM_PLATFORM & GLM_PLATFORM_WINDOWS) +# pragma message("GLM: Windows platform detected") +# elif(GLM_PLATFORM & GLM_PLATFORM_CHROME_NACL) +# pragma message("GLM: Native Client detected") +# elif(GLM_PLATFORM & GLM_PLATFORM_ANDROID) +# pragma message("GLM: Android platform detected") +# elif(GLM_PLATFORM & GLM_PLATFORM_LINUX) +# pragma message("GLM: Linux platform detected") +# elif(GLM_PLATFORM & GLM_PLATFORM_UNIX) +# pragma message("GLM: UNIX platform detected") +# elif(GLM_PLATFORM & GLM_PLATFORM_UNKNOWN) +# pragma message("GLM: platform unknown") +# else +# pragma message("GLM: platform not detected") +# endif + + // Report whether only xyzw component are used +# if defined GLM_FORCE_XYZW_ONLY +# pragma message("GLM: GLM_FORCE_XYZW_ONLY is defined. Only x, y, z and w component are available in vector type. This define disables swizzle operators and SIMD instruction sets.") +# endif + + // Report swizzle operator support +# if GLM_CONFIG_SWIZZLE == GLM_SWIZZLE_OPERATOR +# pragma message("GLM: GLM_FORCE_SWIZZLE is defined, swizzling operators enabled.") +# elif GLM_CONFIG_SWIZZLE == GLM_SWIZZLE_FUNCTION +# pragma message("GLM: GLM_FORCE_SWIZZLE is defined, swizzling functions enabled. Enable compiler C++ language extensions to enable swizzle operators.") +# else +# pragma message("GLM: GLM_FORCE_SWIZZLE is undefined. swizzling functions or operators are disabled.") +# endif + + // Report .length() type +# if GLM_CONFIG_LENGTH_TYPE == GLM_LENGTH_SIZE_T +# pragma message("GLM: GLM_FORCE_SIZE_T_LENGTH is defined. .length() returns a glm::length_t, a typedef of std::size_t.") +# else +# pragma message("GLM: GLM_FORCE_SIZE_T_LENGTH is undefined. .length() returns a glm::length_t, a typedef of int following GLSL.") +# endif + +# if GLM_CONFIG_UNRESTRICTED_GENTYPE == GLM_ENABLE +# pragma message("GLM: GLM_FORCE_UNRESTRICTED_GENTYPE is defined. Removes GLSL restrictions on valid function genTypes.") +# else +# pragma message("GLM: GLM_FORCE_UNRESTRICTED_GENTYPE is undefined. Follows strictly GLSL on valid function genTypes.") +# endif + +# if GLM_SILENT_WARNINGS == GLM_ENABLE +# pragma message("GLM: GLM_FORCE_SILENT_WARNINGS is defined. Ignores C++ warnings from using C++ language extensions.") +# else +# pragma message("GLM: GLM_FORCE_SILENT_WARNINGS is undefined. Shows C++ warnings from using C++ language extensions.") +# endif + +# ifdef GLM_FORCE_SINGLE_ONLY +# pragma message("GLM: GLM_FORCE_SINGLE_ONLY is defined. Using only single precision floating-point types.") +# endif + +# if defined(GLM_FORCE_ALIGNED_GENTYPES) && (GLM_CONFIG_ALIGNED_GENTYPES == GLM_ENABLE) +# undef GLM_FORCE_ALIGNED_GENTYPES +# pragma message("GLM: GLM_FORCE_ALIGNED_GENTYPES is defined, allowing aligned types. This prevents the use of C++ constexpr.") +# elif defined(GLM_FORCE_ALIGNED_GENTYPES) && (GLM_CONFIG_ALIGNED_GENTYPES == GLM_DISABLE) +# undef GLM_FORCE_ALIGNED_GENTYPES +# pragma message("GLM: GLM_FORCE_ALIGNED_GENTYPES is defined but is disabled. It requires C++11 and language extensions.") +# endif + +# if defined(GLM_FORCE_DEFAULT_ALIGNED_GENTYPES) +# if GLM_CONFIG_ALIGNED_GENTYPES == GLM_DISABLE +# undef GLM_FORCE_DEFAULT_ALIGNED_GENTYPES +# pragma message("GLM: GLM_FORCE_DEFAULT_ALIGNED_GENTYPES is defined but is disabled. It requires C++11 and language extensions.") +# elif GLM_CONFIG_ALIGNED_GENTYPES == GLM_ENABLE +# pragma message("GLM: GLM_FORCE_DEFAULT_ALIGNED_GENTYPES is defined. All gentypes (e.g. vec3) will be aligned and padded by default.") +# endif +# endif + +# if GLM_CONFIG_CLIP_CONTROL & GLM_CLIP_CONTROL_ZO_BIT +# pragma message("GLM: GLM_FORCE_DEPTH_ZERO_TO_ONE is defined. Using zero to one depth clip space.") +# else +# pragma message("GLM: GLM_FORCE_DEPTH_ZERO_TO_ONE is undefined. Using negative one to one depth clip space.") +# endif + +# if GLM_CONFIG_CLIP_CONTROL & GLM_CLIP_CONTROL_LH_BIT +# pragma message("GLM: GLM_FORCE_LEFT_HANDED is defined. Using left handed coordinate system.") +# else +# pragma message("GLM: GLM_FORCE_LEFT_HANDED is undefined. Using right handed coordinate system.") +# endif +#endif//GLM_MESSAGES + +#endif//GLM_SETUP_INCLUDED diff --git a/src/GLMath/glm/detail/type_float.hpp b/src/GLMath/glm/detail/type_float.hpp new file mode 100644 index 000000000..c8037ebd7 --- /dev/null +++ b/src/GLMath/glm/detail/type_float.hpp @@ -0,0 +1,68 @@ +#pragma once + +#include "setup.hpp" + +#if GLM_COMPILER == GLM_COMPILER_VC12 +# pragma warning(push) +# pragma warning(disable: 4512) // assignment operator could not be generated +#endif + +namespace glm{ +namespace detail +{ + template + union float_t + {}; + + // https://randomascii.wordpress.com/2012/02/25/comparing-floating-point-numbers-2012-edition/ + template <> + union float_t + { + typedef int int_type; + typedef float float_type; + + GLM_CONSTEXPR float_t(float_type Num = 0.0f) : f(Num) {} + + GLM_CONSTEXPR float_t& operator=(float_t const& x) + { + f = x.f; + return *this; + } + + // Portable extraction of components. + GLM_CONSTEXPR bool negative() const { return i < 0; } + GLM_CONSTEXPR int_type mantissa() const { return i & ((1 << 23) - 1); } + GLM_CONSTEXPR int_type exponent() const { return (i >> 23) & ((1 << 8) - 1); } + + int_type i; + float_type f; + }; + + template <> + union float_t + { + typedef detail::int64 int_type; + typedef double float_type; + + GLM_CONSTEXPR float_t(float_type Num = static_cast(0)) : f(Num) {} + + GLM_CONSTEXPR float_t& operator=(float_t const& x) + { + f = x.f; + return *this; + } + + // Portable extraction of components. + GLM_CONSTEXPR bool negative() const { return i < 0; } + GLM_CONSTEXPR int_type mantissa() const { return i & ((int_type(1) << 52) - 1); } + GLM_CONSTEXPR int_type exponent() const { return (i >> 52) & ((int_type(1) << 11) - 1); } + + int_type i; + float_type f; + }; +}//namespace detail +}//namespace glm + +#if GLM_COMPILER == GLM_COMPILER_VC12 +# pragma warning(pop) +#endif diff --git a/src/GLMath/glm/detail/type_half.hpp b/src/GLMath/glm/detail/type_half.hpp new file mode 100644 index 000000000..40b8bec00 --- /dev/null +++ b/src/GLMath/glm/detail/type_half.hpp @@ -0,0 +1,16 @@ +#pragma once + +#include "setup.hpp" + +namespace glm{ +namespace detail +{ + typedef short hdata; + + GLM_FUNC_DECL float toFloat32(hdata value); + GLM_FUNC_DECL hdata toFloat16(float const& value); + +}//namespace detail +}//namespace glm + +#include "type_half.inl" diff --git a/src/GLMath/glm/detail/type_half.inl b/src/GLMath/glm/detail/type_half.inl new file mode 100644 index 000000000..b0723e362 --- /dev/null +++ b/src/GLMath/glm/detail/type_half.inl @@ -0,0 +1,241 @@ +namespace glm{ +namespace detail +{ + GLM_FUNC_QUALIFIER float overflow() + { + volatile float f = 1e10; + + for(int i = 0; i < 10; ++i) + f *= f; // this will overflow before the for loop terminates + return f; + } + + union uif32 + { + GLM_FUNC_QUALIFIER uif32() : + i(0) + {} + + GLM_FUNC_QUALIFIER uif32(float f_) : + f(f_) + {} + + GLM_FUNC_QUALIFIER uif32(unsigned int i_) : + i(i_) + {} + + float f; + unsigned int i; + }; + + GLM_FUNC_QUALIFIER float toFloat32(hdata value) + { + int s = (value >> 15) & 0x00000001; + int e = (value >> 10) & 0x0000001f; + int m = value & 0x000003ff; + + if(e == 0) + { + if(m == 0) + { + // + // Plus or minus zero + // + + detail::uif32 result; + result.i = static_cast(s << 31); + return result.f; + } + else + { + // + // Denormalized number -- renormalize it + // + + while(!(m & 0x00000400)) + { + m <<= 1; + e -= 1; + } + + e += 1; + m &= ~0x00000400; + } + } + else if(e == 31) + { + if(m == 0) + { + // + // Positive or negative infinity + // + + uif32 result; + result.i = static_cast((s << 31) | 0x7f800000); + return result.f; + } + else + { + // + // Nan -- preserve sign and significand bits + // + + uif32 result; + result.i = static_cast((s << 31) | 0x7f800000 | (m << 13)); + return result.f; + } + } + + // + // Normalized number + // + + e = e + (127 - 15); + m = m << 13; + + // + // Assemble s, e and m. + // + + uif32 Result; + Result.i = static_cast((s << 31) | (e << 23) | m); + return Result.f; + } + + GLM_FUNC_QUALIFIER hdata toFloat16(float const& f) + { + uif32 Entry; + Entry.f = f; + int i = static_cast(Entry.i); + + // + // Our floating point number, f, is represented by the bit + // pattern in integer i. Disassemble that bit pattern into + // the sign, s, the exponent, e, and the significand, m. + // Shift s into the position where it will go in the + // resulting half number. + // Adjust e, accounting for the different exponent bias + // of float and half (127 versus 15). + // + + int s = (i >> 16) & 0x00008000; + int e = ((i >> 23) & 0x000000ff) - (127 - 15); + int m = i & 0x007fffff; + + // + // Now reassemble s, e and m into a half: + // + + if(e <= 0) + { + if(e < -10) + { + // + // E is less than -10. The absolute value of f is + // less than half_MIN (f may be a small normalized + // float, a denormalized float or a zero). + // + // We convert f to a half zero. + // + + return hdata(s); + } + + // + // E is between -10 and 0. F is a normalized float, + // whose magnitude is less than __half_NRM_MIN. + // + // We convert f to a denormalized half. + // + + m = (m | 0x00800000) >> (1 - e); + + // + // Round to nearest, round "0.5" up. + // + // Rounding may cause the significand to overflow and make + // our number normalized. Because of the way a half's bits + // are laid out, we don't have to treat this case separately; + // the code below will handle it correctly. + // + + if(m & 0x00001000) + m += 0x00002000; + + // + // Assemble the half from s, e (zero) and m. + // + + return hdata(s | (m >> 13)); + } + else if(e == 0xff - (127 - 15)) + { + if(m == 0) + { + // + // F is an infinity; convert f to a half + // infinity with the same sign as f. + // + + return hdata(s | 0x7c00); + } + else + { + // + // F is a NAN; we produce a half NAN that preserves + // the sign bit and the 10 leftmost bits of the + // significand of f, with one exception: If the 10 + // leftmost bits are all zero, the NAN would turn + // into an infinity, so we have to set at least one + // bit in the significand. + // + + m >>= 13; + + return hdata(s | 0x7c00 | m | (m == 0)); + } + } + else + { + // + // E is greater than zero. F is a normalized float. + // We try to convert f to a normalized half. + // + + // + // Round to nearest, round "0.5" up + // + + if(m & 0x00001000) + { + m += 0x00002000; + + if(m & 0x00800000) + { + m = 0; // overflow in significand, + e += 1; // adjust exponent + } + } + + // + // Handle exponent overflow + // + + if (e > 30) + { + overflow(); // Cause a hardware floating point overflow; + + return hdata(s | 0x7c00); + // if this returns, the half becomes an + } // infinity with the same sign as f. + + // + // Assemble the half from s, e and m. + // + + return hdata(s | (e << 10) | (m >> 13)); + } + } + +}//namespace detail +}//namespace glm diff --git a/src/GLMath/glm/detail/type_mat2x2.hpp b/src/GLMath/glm/detail/type_mat2x2.hpp new file mode 100644 index 000000000..033908f44 --- /dev/null +++ b/src/GLMath/glm/detail/type_mat2x2.hpp @@ -0,0 +1,177 @@ +/// @ref core +/// @file glm/detail/type_mat2x2.hpp + +#pragma once + +#include "type_vec2.hpp" +#include +#include + +namespace glm +{ + template + struct mat<2, 2, T, Q> + { + typedef vec<2, T, Q> col_type; + typedef vec<2, T, Q> row_type; + typedef mat<2, 2, T, Q> type; + typedef mat<2, 2, T, Q> transpose_type; + typedef T value_type; + + private: + col_type value[2]; + + public: + // -- Accesses -- + + typedef length_t length_type; + GLM_FUNC_DECL static GLM_CONSTEXPR length_type length() { return 2; } + + GLM_FUNC_DECL col_type & operator[](length_type i); + GLM_FUNC_DECL GLM_CONSTEXPR col_type const& operator[](length_type i) const; + + // -- Constructors -- + + GLM_FUNC_DECL GLM_CONSTEXPR mat() GLM_DEFAULT; + template + GLM_FUNC_DECL GLM_CONSTEXPR mat(mat<2, 2, T, P> const& m); + + GLM_FUNC_DECL explicit GLM_CONSTEXPR mat(T scalar); + GLM_FUNC_DECL GLM_CONSTEXPR mat( + T const& x1, T const& y1, + T const& x2, T const& y2); + GLM_FUNC_DECL GLM_CONSTEXPR mat( + col_type const& v1, + col_type const& v2); + + // -- Conversions -- + + template + GLM_FUNC_DECL GLM_CONSTEXPR mat( + U const& x1, V const& y1, + M const& x2, N const& y2); + + template + GLM_FUNC_DECL GLM_CONSTEXPR mat( + vec<2, U, Q> const& v1, + vec<2, V, Q> const& v2); + + // -- Matrix conversions -- + + template + GLM_FUNC_DECL GLM_EXPLICIT GLM_CONSTEXPR mat(mat<2, 2, U, P> const& m); + + GLM_FUNC_DECL GLM_EXPLICIT GLM_CONSTEXPR mat(mat<3, 3, T, Q> const& x); + GLM_FUNC_DECL GLM_EXPLICIT GLM_CONSTEXPR mat(mat<4, 4, T, Q> const& x); + GLM_FUNC_DECL GLM_EXPLICIT GLM_CONSTEXPR mat(mat<2, 3, T, Q> const& x); + GLM_FUNC_DECL GLM_EXPLICIT GLM_CONSTEXPR mat(mat<3, 2, T, Q> const& x); + GLM_FUNC_DECL GLM_EXPLICIT GLM_CONSTEXPR mat(mat<2, 4, T, Q> const& x); + GLM_FUNC_DECL GLM_EXPLICIT GLM_CONSTEXPR mat(mat<4, 2, T, Q> const& x); + GLM_FUNC_DECL GLM_EXPLICIT GLM_CONSTEXPR mat(mat<3, 4, T, Q> const& x); + GLM_FUNC_DECL GLM_EXPLICIT GLM_CONSTEXPR mat(mat<4, 3, T, Q> const& x); + + // -- Unary arithmetic operators -- + + template + GLM_FUNC_DECL mat<2, 2, T, Q> & operator=(mat<2, 2, U, Q> const& m); + template + GLM_FUNC_DECL mat<2, 2, T, Q> & operator+=(U s); + template + GLM_FUNC_DECL mat<2, 2, T, Q> & operator+=(mat<2, 2, U, Q> const& m); + template + GLM_FUNC_DECL mat<2, 2, T, Q> & operator-=(U s); + template + GLM_FUNC_DECL mat<2, 2, T, Q> & operator-=(mat<2, 2, U, Q> const& m); + template + GLM_FUNC_DECL mat<2, 2, T, Q> & operator*=(U s); + template + GLM_FUNC_DECL mat<2, 2, T, Q> & operator*=(mat<2, 2, U, Q> const& m); + template + GLM_FUNC_DECL mat<2, 2, T, Q> & operator/=(U s); + template + GLM_FUNC_DECL mat<2, 2, T, Q> & operator/=(mat<2, 2, U, Q> const& m); + + // -- Increment and decrement operators -- + + GLM_FUNC_DECL mat<2, 2, T, Q> & operator++ (); + GLM_FUNC_DECL mat<2, 2, T, Q> & operator-- (); + GLM_FUNC_DECL mat<2, 2, T, Q> operator++(int); + GLM_FUNC_DECL mat<2, 2, T, Q> operator--(int); + }; + + // -- Unary operators -- + + template + GLM_FUNC_DECL mat<2, 2, T, Q> operator+(mat<2, 2, T, Q> const& m); + + template + GLM_FUNC_DECL mat<2, 2, T, Q> operator-(mat<2, 2, T, Q> const& m); + + // -- Binary operators -- + + template + GLM_FUNC_DECL mat<2, 2, T, Q> operator+(mat<2, 2, T, Q> const& m, T scalar); + + template + GLM_FUNC_DECL mat<2, 2, T, Q> operator+(T scalar, mat<2, 2, T, Q> const& m); + + template + GLM_FUNC_DECL mat<2, 2, T, Q> operator+(mat<2, 2, T, Q> const& m1, mat<2, 2, T, Q> const& m2); + + template + GLM_FUNC_DECL mat<2, 2, T, Q> operator-(mat<2, 2, T, Q> const& m, T scalar); + + template + GLM_FUNC_DECL mat<2, 2, T, Q> operator-(T scalar, mat<2, 2, T, Q> const& m); + + template + GLM_FUNC_DECL mat<2, 2, T, Q> operator-(mat<2, 2, T, Q> const& m1, mat<2, 2, T, Q> const& m2); + + template + GLM_FUNC_DECL mat<2, 2, T, Q> operator*(mat<2, 2, T, Q> const& m, T scalar); + + template + GLM_FUNC_DECL mat<2, 2, T, Q> operator*(T scalar, mat<2, 2, T, Q> const& m); + + template + GLM_FUNC_DECL typename mat<2, 2, T, Q>::col_type operator*(mat<2, 2, T, Q> const& m, typename mat<2, 2, T, Q>::row_type const& v); + + template + GLM_FUNC_DECL typename mat<2, 2, T, Q>::row_type operator*(typename mat<2, 2, T, Q>::col_type const& v, mat<2, 2, T, Q> const& m); + + template + GLM_FUNC_DECL mat<2, 2, T, Q> operator*(mat<2, 2, T, Q> const& m1, mat<2, 2, T, Q> const& m2); + + template + GLM_FUNC_DECL mat<3, 2, T, Q> operator*(mat<2, 2, T, Q> const& m1, mat<3, 2, T, Q> const& m2); + + template + GLM_FUNC_DECL mat<4, 2, T, Q> operator*(mat<2, 2, T, Q> const& m1, mat<4, 2, T, Q> const& m2); + + template + GLM_FUNC_DECL mat<2, 2, T, Q> operator/(mat<2, 2, T, Q> const& m, T scalar); + + template + GLM_FUNC_DECL mat<2, 2, T, Q> operator/(T scalar, mat<2, 2, T, Q> const& m); + + template + GLM_FUNC_DECL typename mat<2, 2, T, Q>::col_type operator/(mat<2, 2, T, Q> const& m, typename mat<2, 2, T, Q>::row_type const& v); + + template + GLM_FUNC_DECL typename mat<2, 2, T, Q>::row_type operator/(typename mat<2, 2, T, Q>::col_type const& v, mat<2, 2, T, Q> const& m); + + template + GLM_FUNC_DECL mat<2, 2, T, Q> operator/(mat<2, 2, T, Q> const& m1, mat<2, 2, T, Q> const& m2); + + // -- Boolean operators -- + + template + GLM_FUNC_DECL bool operator==(mat<2, 2, T, Q> const& m1, mat<2, 2, T, Q> const& m2); + + template + GLM_FUNC_DECL bool operator!=(mat<2, 2, T, Q> const& m1, mat<2, 2, T, Q> const& m2); +} //namespace glm + +#ifndef GLM_EXTERNAL_TEMPLATE +#include "type_mat2x2.inl" +#endif diff --git a/src/GLMath/glm/detail/type_mat2x2.inl b/src/GLMath/glm/detail/type_mat2x2.inl new file mode 100644 index 000000000..fe5d1aa31 --- /dev/null +++ b/src/GLMath/glm/detail/type_mat2x2.inl @@ -0,0 +1,536 @@ +#include "../matrix.hpp" + +namespace glm +{ + // -- Constructors -- + +# if GLM_CONFIG_DEFAULTED_FUNCTIONS == GLM_DISABLE + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<2, 2, T, Q>::mat() +# if GLM_CONFIG_CTOR_INIT == GLM_CTOR_INITIALIZER_LIST + : value{col_type(1, 0), col_type(0, 1)} +# endif + { +# if GLM_CONFIG_CTOR_INIT == GLM_CTOR_INITIALISATION + this->value[0] = col_type(1, 0); + this->value[1] = col_type(0, 1); +# endif + } +# endif + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<2, 2, T, Q>::mat(mat<2, 2, T, P> const& m) +# if GLM_HAS_INITIALIZER_LISTS + : value{m[0], m[1]} +# endif + { +# if !GLM_HAS_INITIALIZER_LISTS + this->value[0] = m[0]; + this->value[1] = m[1]; +# endif + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<2, 2, T, Q>::mat(T scalar) +# if GLM_HAS_INITIALIZER_LISTS + : value{col_type(scalar, 0), col_type(0, scalar)} +# endif + { +# if !GLM_HAS_INITIALIZER_LISTS + this->value[0] = col_type(scalar, 0); + this->value[1] = col_type(0, scalar); +# endif + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<2, 2, T, Q>::mat + ( + T const& x0, T const& y0, + T const& x1, T const& y1 + ) +# if GLM_HAS_INITIALIZER_LISTS + : value{col_type(x0, y0), col_type(x1, y1)} +# endif + { +# if !GLM_HAS_INITIALIZER_LISTS + this->value[0] = col_type(x0, y0); + this->value[1] = col_type(x1, y1); +# endif + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<2, 2, T, Q>::mat(col_type const& v0, col_type const& v1) +# if GLM_HAS_INITIALIZER_LISTS + : value{v0, v1} +# endif + { +# if !GLM_HAS_INITIALIZER_LISTS + this->value[0] = v0; + this->value[1] = v1; +# endif + } + + // -- Conversion constructors -- + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<2, 2, T, Q>::mat + ( + X1 const& x1, Y1 const& y1, + X2 const& x2, Y2 const& y2 + ) +# if GLM_HAS_INITIALIZER_LISTS + : value{col_type(static_cast(x1), value_type(y1)), col_type(static_cast(x2), value_type(y2)) } +# endif + { +# if !GLM_HAS_INITIALIZER_LISTS + this->value[0] = col_type(static_cast(x1), value_type(y1)); + this->value[1] = col_type(static_cast(x2), value_type(y2)); +# endif + } + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<2, 2, T, Q>::mat(vec<2, V1, Q> const& v1, vec<2, V2, Q> const& v2) +# if GLM_HAS_INITIALIZER_LISTS + : value{col_type(v1), col_type(v2)} +# endif + { +# if !GLM_HAS_INITIALIZER_LISTS + this->value[0] = col_type(v1); + this->value[1] = col_type(v2); +# endif + } + + // -- mat2x2 matrix conversions -- + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<2, 2, T, Q>::mat(mat<2, 2, U, P> const& m) +# if GLM_HAS_INITIALIZER_LISTS + : value{col_type(m[0]), col_type(m[1])} +# endif + { +# if !GLM_HAS_INITIALIZER_LISTS + this->value[0] = col_type(m[0]); + this->value[1] = col_type(m[1]); +# endif + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<2, 2, T, Q>::mat(mat<3, 3, T, Q> const& m) +# if GLM_HAS_INITIALIZER_LISTS + : value{col_type(m[0]), col_type(m[1])} +# endif + { +# if !GLM_HAS_INITIALIZER_LISTS + this->value[0] = col_type(m[0]); + this->value[1] = col_type(m[1]); +# endif + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<2, 2, T, Q>::mat(mat<4, 4, T, Q> const& m) +# if GLM_HAS_INITIALIZER_LISTS + : value{col_type(m[0]), col_type(m[1])} +# endif + { +# if !GLM_HAS_INITIALIZER_LISTS + this->value[0] = col_type(m[0]); + this->value[1] = col_type(m[1]); +# endif + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<2, 2, T, Q>::mat(mat<2, 3, T, Q> const& m) +# if GLM_HAS_INITIALIZER_LISTS + : value{col_type(m[0]), col_type(m[1])} +# endif + { +# if !GLM_HAS_INITIALIZER_LISTS + this->value[0] = col_type(m[0]); + this->value[1] = col_type(m[1]); +# endif + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<2, 2, T, Q>::mat(mat<3, 2, T, Q> const& m) +# if GLM_HAS_INITIALIZER_LISTS + : value{col_type(m[0]), col_type(m[1])} +# endif + { +# if !GLM_HAS_INITIALIZER_LISTS + this->value[0] = col_type(m[0]); + this->value[1] = col_type(m[1]); +# endif + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<2, 2, T, Q>::mat(mat<2, 4, T, Q> const& m) +# if GLM_HAS_INITIALIZER_LISTS + : value{col_type(m[0]), col_type(m[1])} +# endif + { +# if !GLM_HAS_INITIALIZER_LISTS + this->value[0] = col_type(m[0]); + this->value[1] = col_type(m[1]); +# endif + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<2, 2, T, Q>::mat(mat<4, 2, T, Q> const& m) +# if GLM_HAS_INITIALIZER_LISTS + : value{col_type(m[0]), col_type(m[1])} +# endif + { +# if !GLM_HAS_INITIALIZER_LISTS + this->value[0] = col_type(m[0]); + this->value[1] = col_type(m[1]); +# endif + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<2, 2, T, Q>::mat(mat<3, 4, T, Q> const& m) +# if GLM_HAS_INITIALIZER_LISTS + : value{col_type(m[0]), col_type(m[1])} +# endif + { +# if !GLM_HAS_INITIALIZER_LISTS + this->value[0] = col_type(m[0]); + this->value[1] = col_type(m[1]); +# endif + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<2, 2, T, Q>::mat(mat<4, 3, T, Q> const& m) +# if GLM_HAS_INITIALIZER_LISTS + : value{col_type(m[0]), col_type(m[1])} +# endif + { +# if !GLM_HAS_INITIALIZER_LISTS + this->value[0] = col_type(m[0]); + this->value[1] = col_type(m[1]); +# endif + } + + // -- Accesses -- + + template + GLM_FUNC_QUALIFIER typename mat<2, 2, T, Q>::col_type& mat<2, 2, T, Q>::operator[](typename mat<2, 2, T, Q>::length_type i) + { + assert(i < this->length()); + return this->value[i]; + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR typename mat<2, 2, T, Q>::col_type const& mat<2, 2, T, Q>::operator[](typename mat<2, 2, T, Q>::length_type i) const + { + assert(i < this->length()); + return this->value[i]; + } + + // -- Unary updatable operators -- + + template + template + GLM_FUNC_QUALIFIER mat<2, 2, T, Q>& mat<2, 2, T, Q>::operator=(mat<2, 2, U, Q> const& m) + { + this->value[0] = m[0]; + this->value[1] = m[1]; + return *this; + } + + template + template + GLM_FUNC_QUALIFIER mat<2, 2, T, Q>& mat<2, 2, T, Q>::operator+=(U scalar) + { + this->value[0] += scalar; + this->value[1] += scalar; + return *this; + } + + template + template + GLM_FUNC_QUALIFIER mat<2, 2, T, Q>& mat<2, 2, T, Q>::operator+=(mat<2, 2, U, Q> const& m) + { + this->value[0] += m[0]; + this->value[1] += m[1]; + return *this; + } + + template + template + GLM_FUNC_QUALIFIER mat<2, 2, T, Q>& mat<2, 2, T, Q>::operator-=(U scalar) + { + this->value[0] -= scalar; + this->value[1] -= scalar; + return *this; + } + + template + template + GLM_FUNC_QUALIFIER mat<2, 2, T, Q>& mat<2, 2, T, Q>::operator-=(mat<2, 2, U, Q> const& m) + { + this->value[0] -= m[0]; + this->value[1] -= m[1]; + return *this; + } + + template + template + GLM_FUNC_QUALIFIER mat<2, 2, T, Q>& mat<2, 2, T, Q>::operator*=(U scalar) + { + this->value[0] *= scalar; + this->value[1] *= scalar; + return *this; + } + + template + template + GLM_FUNC_QUALIFIER mat<2, 2, T, Q>& mat<2, 2, T, Q>::operator*=(mat<2, 2, U, Q> const& m) + { + return (*this = *this * m); + } + + template + template + GLM_FUNC_QUALIFIER mat<2, 2, T, Q>& mat<2, 2, T, Q>::operator/=(U scalar) + { + this->value[0] /= scalar; + this->value[1] /= scalar; + return *this; + } + + template + template + GLM_FUNC_QUALIFIER mat<2, 2, T, Q>& mat<2, 2, T, Q>::operator/=(mat<2, 2, U, Q> const& m) + { + return *this *= inverse(m); + } + + // -- Increment and decrement operators -- + + template + GLM_FUNC_QUALIFIER mat<2, 2, T, Q>& mat<2, 2, T, Q>::operator++() + { + ++this->value[0]; + ++this->value[1]; + return *this; + } + + template + GLM_FUNC_QUALIFIER mat<2, 2, T, Q>& mat<2, 2, T, Q>::operator--() + { + --this->value[0]; + --this->value[1]; + return *this; + } + + template + GLM_FUNC_QUALIFIER mat<2, 2, T, Q> mat<2, 2, T, Q>::operator++(int) + { + mat<2, 2, T, Q> Result(*this); + ++*this; + return Result; + } + + template + GLM_FUNC_QUALIFIER mat<2, 2, T, Q> mat<2, 2, T, Q>::operator--(int) + { + mat<2, 2, T, Q> Result(*this); + --*this; + return Result; + } + + // -- Unary arithmetic operators -- + + template + GLM_FUNC_QUALIFIER mat<2, 2, T, Q> operator+(mat<2, 2, T, Q> const& m) + { + return m; + } + + template + GLM_FUNC_QUALIFIER mat<2, 2, T, Q> operator-(mat<2, 2, T, Q> const& m) + { + return mat<2, 2, T, Q>( + -m[0], + -m[1]); + } + + // -- Binary arithmetic operators -- + + template + GLM_FUNC_QUALIFIER mat<2, 2, T, Q> operator+(mat<2, 2, T, Q> const& m, T scalar) + { + return mat<2, 2, T, Q>( + m[0] + scalar, + m[1] + scalar); + } + + template + GLM_FUNC_QUALIFIER mat<2, 2, T, Q> operator+(T scalar, mat<2, 2, T, Q> const& m) + { + return mat<2, 2, T, Q>( + m[0] + scalar, + m[1] + scalar); + } + + template + GLM_FUNC_QUALIFIER mat<2, 2, T, Q> operator+(mat<2, 2, T, Q> const& m1, mat<2, 2, T, Q> const& m2) + { + return mat<2, 2, T, Q>( + m1[0] + m2[0], + m1[1] + m2[1]); + } + + template + GLM_FUNC_QUALIFIER mat<2, 2, T, Q> operator-(mat<2, 2, T, Q> const& m, T scalar) + { + return mat<2, 2, T, Q>( + m[0] - scalar, + m[1] - scalar); + } + + template + GLM_FUNC_QUALIFIER mat<2, 2, T, Q> operator-(T scalar, mat<2, 2, T, Q> const& m) + { + return mat<2, 2, T, Q>( + scalar - m[0], + scalar - m[1]); + } + + template + GLM_FUNC_QUALIFIER mat<2, 2, T, Q> operator-(mat<2, 2, T, Q> const& m1, mat<2, 2, T, Q> const& m2) + { + return mat<2, 2, T, Q>( + m1[0] - m2[0], + m1[1] - m2[1]); + } + + template + GLM_FUNC_QUALIFIER mat<2, 2, T, Q> operator*(mat<2, 2, T, Q> const& m, T scalar) + { + return mat<2, 2, T, Q>( + m[0] * scalar, + m[1] * scalar); + } + + template + GLM_FUNC_QUALIFIER mat<2, 2, T, Q> operator*(T scalar, mat<2, 2, T, Q> const& m) + { + return mat<2, 2, T, Q>( + m[0] * scalar, + m[1] * scalar); + } + + template + GLM_FUNC_QUALIFIER typename mat<2, 2, T, Q>::col_type operator* + ( + mat<2, 2, T, Q> const& m, + typename mat<2, 2, T, Q>::row_type const& v + ) + { + return vec<2, T, Q>( + m[0][0] * v.x + m[1][0] * v.y, + m[0][1] * v.x + m[1][1] * v.y); + } + + template + GLM_FUNC_QUALIFIER typename mat<2, 2, T, Q>::row_type operator* + ( + typename mat<2, 2, T, Q>::col_type const& v, + mat<2, 2, T, Q> const& m + ) + { + return vec<2, T, Q>( + v.x * m[0][0] + v.y * m[0][1], + v.x * m[1][0] + v.y * m[1][1]); + } + + template + GLM_FUNC_QUALIFIER mat<2, 2, T, Q> operator*(mat<2, 2, T, Q> const& m1, mat<2, 2, T, Q> const& m2) + { + return mat<2, 2, T, Q>( + m1[0][0] * m2[0][0] + m1[1][0] * m2[0][1], + m1[0][1] * m2[0][0] + m1[1][1] * m2[0][1], + m1[0][0] * m2[1][0] + m1[1][0] * m2[1][1], + m1[0][1] * m2[1][0] + m1[1][1] * m2[1][1]); + } + + template + GLM_FUNC_QUALIFIER mat<3, 2, T, Q> operator*(mat<2, 2, T, Q> const& m1, mat<3, 2, T, Q> const& m2) + { + return mat<3, 2, T, Q>( + m1[0][0] * m2[0][0] + m1[1][0] * m2[0][1], + m1[0][1] * m2[0][0] + m1[1][1] * m2[0][1], + m1[0][0] * m2[1][0] + m1[1][0] * m2[1][1], + m1[0][1] * m2[1][0] + m1[1][1] * m2[1][1], + m1[0][0] * m2[2][0] + m1[1][0] * m2[2][1], + m1[0][1] * m2[2][0] + m1[1][1] * m2[2][1]); + } + + template + GLM_FUNC_QUALIFIER mat<4, 2, T, Q> operator*(mat<2, 2, T, Q> const& m1, mat<4, 2, T, Q> const& m2) + { + return mat<4, 2, T, Q>( + m1[0][0] * m2[0][0] + m1[1][0] * m2[0][1], + m1[0][1] * m2[0][0] + m1[1][1] * m2[0][1], + m1[0][0] * m2[1][0] + m1[1][0] * m2[1][1], + m1[0][1] * m2[1][0] + m1[1][1] * m2[1][1], + m1[0][0] * m2[2][0] + m1[1][0] * m2[2][1], + m1[0][1] * m2[2][0] + m1[1][1] * m2[2][1], + m1[0][0] * m2[3][0] + m1[1][0] * m2[3][1], + m1[0][1] * m2[3][0] + m1[1][1] * m2[3][1]); + } + + template + GLM_FUNC_QUALIFIER mat<2, 2, T, Q> operator/(mat<2, 2, T, Q> const& m, T scalar) + { + return mat<2, 2, T, Q>( + m[0] / scalar, + m[1] / scalar); + } + + template + GLM_FUNC_QUALIFIER mat<2, 2, T, Q> operator/(T scalar, mat<2, 2, T, Q> const& m) + { + return mat<2, 2, T, Q>( + scalar / m[0], + scalar / m[1]); + } + + template + GLM_FUNC_QUALIFIER typename mat<2, 2, T, Q>::col_type operator/(mat<2, 2, T, Q> const& m, typename mat<2, 2, T, Q>::row_type const& v) + { + return inverse(m) * v; + } + + template + GLM_FUNC_QUALIFIER typename mat<2, 2, T, Q>::row_type operator/(typename mat<2, 2, T, Q>::col_type const& v, mat<2, 2, T, Q> const& m) + { + return v * inverse(m); + } + + template + GLM_FUNC_QUALIFIER mat<2, 2, T, Q> operator/(mat<2, 2, T, Q> const& m1, mat<2, 2, T, Q> const& m2) + { + mat<2, 2, T, Q> m1_copy(m1); + return m1_copy /= m2; + } + + // -- Boolean operators -- + + template + GLM_FUNC_QUALIFIER bool operator==(mat<2, 2, T, Q> const& m1, mat<2, 2, T, Q> const& m2) + { + return (m1[0] == m2[0]) && (m1[1] == m2[1]); + } + + template + GLM_FUNC_QUALIFIER bool operator!=(mat<2, 2, T, Q> const& m1, mat<2, 2, T, Q> const& m2) + { + return (m1[0] != m2[0]) || (m1[1] != m2[1]); + } +} //namespace glm diff --git a/src/GLMath/glm/detail/type_mat2x3.hpp b/src/GLMath/glm/detail/type_mat2x3.hpp new file mode 100644 index 000000000..d6596e469 --- /dev/null +++ b/src/GLMath/glm/detail/type_mat2x3.hpp @@ -0,0 +1,159 @@ +/// @ref core +/// @file glm/detail/type_mat2x3.hpp + +#pragma once + +#include "type_vec2.hpp" +#include "type_vec3.hpp" +#include +#include + +namespace glm +{ + template + struct mat<2, 3, T, Q> + { + typedef vec<3, T, Q> col_type; + typedef vec<2, T, Q> row_type; + typedef mat<2, 3, T, Q> type; + typedef mat<3, 2, T, Q> transpose_type; + typedef T value_type; + + private: + col_type value[2]; + + public: + // -- Accesses -- + + typedef length_t length_type; + GLM_FUNC_DECL static GLM_CONSTEXPR length_type length() { return 2; } + + GLM_FUNC_DECL col_type & operator[](length_type i); + GLM_FUNC_DECL GLM_CONSTEXPR col_type const& operator[](length_type i) const; + + // -- Constructors -- + + GLM_FUNC_DECL GLM_CONSTEXPR mat() GLM_DEFAULT; + template + GLM_FUNC_DECL GLM_CONSTEXPR mat(mat<2, 3, T, P> const& m); + + GLM_FUNC_DECL explicit GLM_CONSTEXPR mat(T scalar); + GLM_FUNC_DECL GLM_CONSTEXPR mat( + T x0, T y0, T z0, + T x1, T y1, T z1); + GLM_FUNC_DECL GLM_CONSTEXPR mat( + col_type const& v0, + col_type const& v1); + + // -- Conversions -- + + template + GLM_FUNC_DECL GLM_CONSTEXPR mat( + X1 x1, Y1 y1, Z1 z1, + X2 x2, Y2 y2, Z2 z2); + + template + GLM_FUNC_DECL GLM_CONSTEXPR mat( + vec<3, U, Q> const& v1, + vec<3, V, Q> const& v2); + + // -- Matrix conversions -- + + template + GLM_FUNC_DECL GLM_EXPLICIT GLM_CONSTEXPR mat(mat<2, 3, U, P> const& m); + + GLM_FUNC_DECL GLM_EXPLICIT GLM_CONSTEXPR mat(mat<2, 2, T, Q> const& x); + GLM_FUNC_DECL GLM_EXPLICIT GLM_CONSTEXPR mat(mat<3, 3, T, Q> const& x); + GLM_FUNC_DECL GLM_EXPLICIT GLM_CONSTEXPR mat(mat<4, 4, T, Q> const& x); + GLM_FUNC_DECL GLM_EXPLICIT GLM_CONSTEXPR mat(mat<2, 4, T, Q> const& x); + GLM_FUNC_DECL GLM_EXPLICIT GLM_CONSTEXPR mat(mat<3, 2, T, Q> const& x); + GLM_FUNC_DECL GLM_EXPLICIT GLM_CONSTEXPR mat(mat<3, 4, T, Q> const& x); + GLM_FUNC_DECL GLM_EXPLICIT GLM_CONSTEXPR mat(mat<4, 2, T, Q> const& x); + GLM_FUNC_DECL GLM_EXPLICIT GLM_CONSTEXPR mat(mat<4, 3, T, Q> const& x); + + // -- Unary arithmetic operators -- + + template + GLM_FUNC_DECL mat<2, 3, T, Q> & operator=(mat<2, 3, U, Q> const& m); + template + GLM_FUNC_DECL mat<2, 3, T, Q> & operator+=(U s); + template + GLM_FUNC_DECL mat<2, 3, T, Q> & operator+=(mat<2, 3, U, Q> const& m); + template + GLM_FUNC_DECL mat<2, 3, T, Q> & operator-=(U s); + template + GLM_FUNC_DECL mat<2, 3, T, Q> & operator-=(mat<2, 3, U, Q> const& m); + template + GLM_FUNC_DECL mat<2, 3, T, Q> & operator*=(U s); + template + GLM_FUNC_DECL mat<2, 3, T, Q> & operator/=(U s); + + // -- Increment and decrement operators -- + + GLM_FUNC_DECL mat<2, 3, T, Q> & operator++ (); + GLM_FUNC_DECL mat<2, 3, T, Q> & operator-- (); + GLM_FUNC_DECL mat<2, 3, T, Q> operator++(int); + GLM_FUNC_DECL mat<2, 3, T, Q> operator--(int); + }; + + // -- Unary operators -- + + template + GLM_FUNC_DECL mat<2, 3, T, Q> operator+(mat<2, 3, T, Q> const& m); + + template + GLM_FUNC_DECL mat<2, 3, T, Q> operator-(mat<2, 3, T, Q> const& m); + + // -- Binary operators -- + + template + GLM_FUNC_DECL mat<2, 3, T, Q> operator+(mat<2, 3, T, Q> const& m, T scalar); + + template + GLM_FUNC_DECL mat<2, 3, T, Q> operator+(mat<2, 3, T, Q> const& m1, mat<2, 3, T, Q> const& m2); + + template + GLM_FUNC_DECL mat<2, 3, T, Q> operator-(mat<2, 3, T, Q> const& m, T scalar); + + template + GLM_FUNC_DECL mat<2, 3, T, Q> operator-(mat<2, 3, T, Q> const& m1, mat<2, 3, T, Q> const& m2); + + template + GLM_FUNC_DECL mat<2, 3, T, Q> operator*(mat<2, 3, T, Q> const& m, T scalar); + + template + GLM_FUNC_DECL mat<2, 3, T, Q> operator*(T scalar, mat<2, 3, T, Q> const& m); + + template + GLM_FUNC_DECL typename mat<2, 3, T, Q>::col_type operator*(mat<2, 3, T, Q> const& m, typename mat<2, 3, T, Q>::row_type const& v); + + template + GLM_FUNC_DECL typename mat<2, 3, T, Q>::row_type operator*(typename mat<2, 3, T, Q>::col_type const& v, mat<2, 3, T, Q> const& m); + + template + GLM_FUNC_DECL mat<2, 3, T, Q> operator*(mat<2, 3, T, Q> const& m1, mat<2, 2, T, Q> const& m2); + + template + GLM_FUNC_DECL mat<3, 3, T, Q> operator*(mat<2, 3, T, Q> const& m1, mat<3, 2, T, Q> const& m2); + + template + GLM_FUNC_DECL mat<4, 3, T, Q> operator*(mat<2, 3, T, Q> const& m1, mat<4, 2, T, Q> const& m2); + + template + GLM_FUNC_DECL mat<2, 3, T, Q> operator/(mat<2, 3, T, Q> const& m, T scalar); + + template + GLM_FUNC_DECL mat<2, 3, T, Q> operator/(T scalar, mat<2, 3, T, Q> const& m); + + // -- Boolean operators -- + + template + GLM_FUNC_DECL bool operator==(mat<2, 3, T, Q> const& m1, mat<2, 3, T, Q> const& m2); + + template + GLM_FUNC_DECL bool operator!=(mat<2, 3, T, Q> const& m1, mat<2, 3, T, Q> const& m2); +}//namespace glm + +#ifndef GLM_EXTERNAL_TEMPLATE +#include "type_mat2x3.inl" +#endif diff --git a/src/GLMath/glm/detail/type_mat2x3.inl b/src/GLMath/glm/detail/type_mat2x3.inl new file mode 100644 index 000000000..5fec17e5d --- /dev/null +++ b/src/GLMath/glm/detail/type_mat2x3.inl @@ -0,0 +1,510 @@ +namespace glm +{ + // -- Constructors -- + +# if GLM_CONFIG_DEFAULTED_FUNCTIONS == GLM_DISABLE + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<2, 3, T, Q>::mat() +# if GLM_CONFIG_CTOR_INIT == GLM_CTOR_INITIALIZER_LIST + : value{col_type(1, 0, 0), col_type(0, 1, 0)} +# endif + { +# if GLM_CONFIG_CTOR_INIT == GLM_CTOR_INITIALISATION + this->value[0] = col_type(1, 0, 0); + this->value[1] = col_type(0, 1, 0); +# endif + } +# endif + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<2, 3, T, Q>::mat(mat<2, 3, T, P> const& m) +# if GLM_HAS_INITIALIZER_LISTS + : value{m.value[0], m.value[1]} +# endif + { +# if !GLM_HAS_INITIALIZER_LISTS + this->value[0] = m.value[0]; + this->value[1] = m.value[1]; +# endif + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<2, 3, T, Q>::mat(T scalar) +# if GLM_HAS_INITIALIZER_LISTS + : value{col_type(scalar, 0, 0), col_type(0, scalar, 0)} +# endif + { +# if !GLM_HAS_INITIALIZER_LISTS + this->value[0] = col_type(scalar, 0, 0); + this->value[1] = col_type(0, scalar, 0); +# endif + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<2, 3, T, Q>::mat + ( + T x0, T y0, T z0, + T x1, T y1, T z1 + ) +# if GLM_HAS_INITIALIZER_LISTS + : value{col_type(x0, y0, z0), col_type(x1, y1, z1)} +# endif + { +# if !GLM_HAS_INITIALIZER_LISTS + this->value[0] = col_type(x0, y0, z0); + this->value[1] = col_type(x1, y1, z1); +# endif + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<2, 3, T, Q>::mat(col_type const& v0, col_type const& v1) +# if GLM_HAS_INITIALIZER_LISTS + : value{col_type(v0), col_type(v1)} +# endif + { +# if !GLM_HAS_INITIALIZER_LISTS + this->value[0] = col_type(v0); + this->value[1] = col_type(v1); +# endif + } + + // -- Conversion constructors -- + + template + template< + typename X1, typename Y1, typename Z1, + typename X2, typename Y2, typename Z2> + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<2, 3, T, Q>::mat + ( + X1 x1, Y1 y1, Z1 z1, + X2 x2, Y2 y2, Z2 z2 + ) +# if GLM_HAS_INITIALIZER_LISTS + : value{col_type(x1, y1, z1), col_type(x2, y2, z2)} +# endif + { +# if !GLM_HAS_INITIALIZER_LISTS + this->value[0] = col_type(x1, y1, z1); + this->value[1] = col_type(x2, y2, z2); +# endif + } + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<2, 3, T, Q>::mat(vec<3, V1, Q> const& v1, vec<3, V2, Q> const& v2) +# if GLM_HAS_INITIALIZER_LISTS + : value{col_type(v1), col_type(v2)} +# endif + { +# if !GLM_HAS_INITIALIZER_LISTS + this->value[0] = col_type(v1); + this->value[1] = col_type(v2); +# endif + } + + // -- Matrix conversions -- + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<2, 3, T, Q>::mat(mat<2, 3, U, P> const& m) +# if GLM_HAS_INITIALIZER_LISTS + : value{col_type(m[0]), col_type(m[1])} +# endif + { +# if !GLM_HAS_INITIALIZER_LISTS + this->value[0] = col_type(m[0]); + this->value[1] = col_type(m[1]); +# endif + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<2, 3, T, Q>::mat(mat<2, 2, T, Q> const& m) +# if GLM_HAS_INITIALIZER_LISTS + : value{col_type(m[0], 0), col_type(m[1], 0)} +# endif + { +# if !GLM_HAS_INITIALIZER_LISTS + this->value[0] = col_type(m[0], 0); + this->value[1] = col_type(m[1], 0); +# endif + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<2, 3, T, Q>::mat(mat<3, 3, T, Q> const& m) +# if GLM_HAS_INITIALIZER_LISTS + : value{col_type(m[0]), col_type(m[1])} +# endif + { +# if !GLM_HAS_INITIALIZER_LISTS + this->value[0] = col_type(m[0]); + this->value[1] = col_type(m[1]); +# endif + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<2, 3, T, Q>::mat(mat<4, 4, T, Q> const& m) +# if GLM_HAS_INITIALIZER_LISTS + : value{col_type(m[0]), col_type(m[1])} +# endif + { +# if !GLM_HAS_INITIALIZER_LISTS + this->value[0] = col_type(m[0]); + this->value[1] = col_type(m[1]); +# endif + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<2, 3, T, Q>::mat(mat<2, 4, T, Q> const& m) +# if GLM_HAS_INITIALIZER_LISTS + : value{col_type(m[0]), col_type(m[1])} +# endif + { +# if !GLM_HAS_INITIALIZER_LISTS + this->value[0] = col_type(m[0]); + this->value[1] = col_type(m[1]); +# endif + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<2, 3, T, Q>::mat(mat<3, 2, T, Q> const& m) +# if GLM_HAS_INITIALIZER_LISTS + : value{col_type(m[0], 0), col_type(m[1], 0)} +# endif + { +# if !GLM_HAS_INITIALIZER_LISTS + this->value[0] = col_type(m[0], 0); + this->value[1] = col_type(m[1], 0); +# endif + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<2, 3, T, Q>::mat(mat<3, 4, T, Q> const& m) +# if GLM_HAS_INITIALIZER_LISTS + : value{col_type(m[0]), col_type(m[1])} +# endif + { +# if !GLM_HAS_INITIALIZER_LISTS + this->value[0] = col_type(m[0]); + this->value[1] = col_type(m[1]); +# endif + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<2, 3, T, Q>::mat(mat<4, 2, T, Q> const& m) +# if GLM_HAS_INITIALIZER_LISTS + : value{col_type(m[0], 0), col_type(m[1], 0)} +# endif + { +# if !GLM_HAS_INITIALIZER_LISTS + this->value[0] = col_type(m[0], 0); + this->value[1] = col_type(m[1], 0); +# endif + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<2, 3, T, Q>::mat(mat<4, 3, T, Q> const& m) +# if GLM_HAS_INITIALIZER_LISTS + : value{col_type(m[0]), col_type(m[1])} +# endif + { +# if !GLM_HAS_INITIALIZER_LISTS + this->value[0] = col_type(m[0]); + this->value[1] = col_type(m[1]); +# endif + } + + // -- Accesses -- + + template + GLM_FUNC_QUALIFIER typename mat<2, 3, T, Q>::col_type & mat<2, 3, T, Q>::operator[](typename mat<2, 3, T, Q>::length_type i) + { + assert(i < this->length()); + return this->value[i]; + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR typename mat<2, 3, T, Q>::col_type const& mat<2, 3, T, Q>::operator[](typename mat<2, 3, T, Q>::length_type i) const + { + assert(i < this->length()); + return this->value[i]; + } + + // -- Unary updatable operators -- + + template + template + GLM_FUNC_QUALIFIER mat<2, 3, T, Q>& mat<2, 3, T, Q>::operator=(mat<2, 3, U, Q> const& m) + { + this->value[0] = m[0]; + this->value[1] = m[1]; + return *this; + } + + template + template + GLM_FUNC_QUALIFIER mat<2, 3, T, Q> & mat<2, 3, T, Q>::operator+=(U s) + { + this->value[0] += s; + this->value[1] += s; + return *this; + } + + template + template + GLM_FUNC_QUALIFIER mat<2, 3, T, Q>& mat<2, 3, T, Q>::operator+=(mat<2, 3, U, Q> const& m) + { + this->value[0] += m[0]; + this->value[1] += m[1]; + return *this; + } + + template + template + GLM_FUNC_QUALIFIER mat<2, 3, T, Q>& mat<2, 3, T, Q>::operator-=(U s) + { + this->value[0] -= s; + this->value[1] -= s; + return *this; + } + + template + template + GLM_FUNC_QUALIFIER mat<2, 3, T, Q>& mat<2, 3, T, Q>::operator-=(mat<2, 3, U, Q> const& m) + { + this->value[0] -= m[0]; + this->value[1] -= m[1]; + return *this; + } + + template + template + GLM_FUNC_QUALIFIER mat<2, 3, T, Q>& mat<2, 3, T, Q>::operator*=(U s) + { + this->value[0] *= s; + this->value[1] *= s; + return *this; + } + + template + template + GLM_FUNC_QUALIFIER mat<2, 3, T, Q> & mat<2, 3, T, Q>::operator/=(U s) + { + this->value[0] /= s; + this->value[1] /= s; + return *this; + } + + // -- Increment and decrement operators -- + + template + GLM_FUNC_QUALIFIER mat<2, 3, T, Q> & mat<2, 3, T, Q>::operator++() + { + ++this->value[0]; + ++this->value[1]; + return *this; + } + + template + GLM_FUNC_QUALIFIER mat<2, 3, T, Q> & mat<2, 3, T, Q>::operator--() + { + --this->value[0]; + --this->value[1]; + return *this; + } + + template + GLM_FUNC_QUALIFIER mat<2, 3, T, Q> mat<2, 3, T, Q>::operator++(int) + { + mat<2, 3, T, Q> Result(*this); + ++*this; + return Result; + } + + template + GLM_FUNC_QUALIFIER mat<2, 3, T, Q> mat<2, 3, T, Q>::operator--(int) + { + mat<2, 3, T, Q> Result(*this); + --*this; + return Result; + } + + // -- Unary arithmetic operators -- + + template + GLM_FUNC_QUALIFIER mat<2, 3, T, Q> operator+(mat<2, 3, T, Q> const& m) + { + return m; + } + + template + GLM_FUNC_QUALIFIER mat<2, 3, T, Q> operator-(mat<2, 3, T, Q> const& m) + { + return mat<2, 3, T, Q>( + -m[0], + -m[1]); + } + + // -- Binary arithmetic operators -- + + template + GLM_FUNC_QUALIFIER mat<2, 3, T, Q> operator+(mat<2, 3, T, Q> const& m, T scalar) + { + return mat<2, 3, T, Q>( + m[0] + scalar, + m[1] + scalar); + } + + template + GLM_FUNC_QUALIFIER mat<2, 3, T, Q> operator+(mat<2, 3, T, Q> const& m1, mat<2, 3, T, Q> const& m2) + { + return mat<2, 3, T, Q>( + m1[0] + m2[0], + m1[1] + m2[1]); + } + + template + GLM_FUNC_QUALIFIER mat<2, 3, T, Q> operator-(mat<2, 3, T, Q> const& m, T scalar) + { + return mat<2, 3, T, Q>( + m[0] - scalar, + m[1] - scalar); + } + + template + GLM_FUNC_QUALIFIER mat<2, 3, T, Q> operator-(mat<2, 3, T, Q> const& m1, mat<2, 3, T, Q> const& m2) + { + return mat<2, 3, T, Q>( + m1[0] - m2[0], + m1[1] - m2[1]); + } + + template + GLM_FUNC_QUALIFIER mat<2, 3, T, Q> operator*(mat<2, 3, T, Q> const& m, T scalar) + { + return mat<2, 3, T, Q>( + m[0] * scalar, + m[1] * scalar); + } + + template + GLM_FUNC_QUALIFIER mat<2, 3, T, Q> operator*(T scalar, mat<2, 3, T, Q> const& m) + { + return mat<2, 3, T, Q>( + m[0] * scalar, + m[1] * scalar); + } + + template + GLM_FUNC_QUALIFIER typename mat<2, 3, T, Q>::col_type operator* + ( + mat<2, 3, T, Q> const& m, + typename mat<2, 3, T, Q>::row_type const& v) + { + return typename mat<2, 3, T, Q>::col_type( + m[0][0] * v.x + m[1][0] * v.y, + m[0][1] * v.x + m[1][1] * v.y, + m[0][2] * v.x + m[1][2] * v.y); + } + + template + GLM_FUNC_QUALIFIER typename mat<2, 3, T, Q>::row_type operator* + ( + typename mat<2, 3, T, Q>::col_type const& v, + mat<2, 3, T, Q> const& m) + { + return typename mat<2, 3, T, Q>::row_type( + v.x * m[0][0] + v.y * m[0][1] + v.z * m[0][2], + v.x * m[1][0] + v.y * m[1][1] + v.z * m[1][2]); + } + + template + GLM_FUNC_QUALIFIER mat<2, 3, T, Q> operator*(mat<2, 3, T, Q> const& m1, mat<2, 2, T, Q> const& m2) + { + return mat<2, 3, T, Q>( + m1[0][0] * m2[0][0] + m1[1][0] * m2[0][1], + m1[0][1] * m2[0][0] + m1[1][1] * m2[0][1], + m1[0][2] * m2[0][0] + m1[1][2] * m2[0][1], + m1[0][0] * m2[1][0] + m1[1][0] * m2[1][1], + m1[0][1] * m2[1][0] + m1[1][1] * m2[1][1], + m1[0][2] * m2[1][0] + m1[1][2] * m2[1][1]); + } + + template + GLM_FUNC_QUALIFIER mat<3, 3, T, Q> operator*(mat<2, 3, T, Q> const& m1, mat<3, 2, T, Q> const& m2) + { + T SrcA00 = m1[0][0]; + T SrcA01 = m1[0][1]; + T SrcA02 = m1[0][2]; + T SrcA10 = m1[1][0]; + T SrcA11 = m1[1][1]; + T SrcA12 = m1[1][2]; + + T SrcB00 = m2[0][0]; + T SrcB01 = m2[0][1]; + T SrcB10 = m2[1][0]; + T SrcB11 = m2[1][1]; + T SrcB20 = m2[2][0]; + T SrcB21 = m2[2][1]; + + mat<3, 3, T, Q> Result; + Result[0][0] = SrcA00 * SrcB00 + SrcA10 * SrcB01; + Result[0][1] = SrcA01 * SrcB00 + SrcA11 * SrcB01; + Result[0][2] = SrcA02 * SrcB00 + SrcA12 * SrcB01; + Result[1][0] = SrcA00 * SrcB10 + SrcA10 * SrcB11; + Result[1][1] = SrcA01 * SrcB10 + SrcA11 * SrcB11; + Result[1][2] = SrcA02 * SrcB10 + SrcA12 * SrcB11; + Result[2][0] = SrcA00 * SrcB20 + SrcA10 * SrcB21; + Result[2][1] = SrcA01 * SrcB20 + SrcA11 * SrcB21; + Result[2][2] = SrcA02 * SrcB20 + SrcA12 * SrcB21; + return Result; + } + + template + GLM_FUNC_QUALIFIER mat<4, 3, T, Q> operator*(mat<2, 3, T, Q> const& m1, mat<4, 2, T, Q> const& m2) + { + return mat<4, 3, T, Q>( + m1[0][0] * m2[0][0] + m1[1][0] * m2[0][1], + m1[0][1] * m2[0][0] + m1[1][1] * m2[0][1], + m1[0][2] * m2[0][0] + m1[1][2] * m2[0][1], + m1[0][0] * m2[1][0] + m1[1][0] * m2[1][1], + m1[0][1] * m2[1][0] + m1[1][1] * m2[1][1], + m1[0][2] * m2[1][0] + m1[1][2] * m2[1][1], + m1[0][0] * m2[2][0] + m1[1][0] * m2[2][1], + m1[0][1] * m2[2][0] + m1[1][1] * m2[2][1], + m1[0][2] * m2[2][0] + m1[1][2] * m2[2][1], + m1[0][0] * m2[3][0] + m1[1][0] * m2[3][1], + m1[0][1] * m2[3][0] + m1[1][1] * m2[3][1], + m1[0][2] * m2[3][0] + m1[1][2] * m2[3][1]); + } + + template + GLM_FUNC_QUALIFIER mat<2, 3, T, Q> operator/(mat<2, 3, T, Q> const& m, T scalar) + { + return mat<2, 3, T, Q>( + m[0] / scalar, + m[1] / scalar); + } + + template + GLM_FUNC_QUALIFIER mat<2, 3, T, Q> operator/(T scalar, mat<2, 3, T, Q> const& m) + { + return mat<2, 3, T, Q>( + scalar / m[0], + scalar / m[1]); + } + + // -- Boolean operators -- + + template + GLM_FUNC_QUALIFIER bool operator==(mat<2, 3, T, Q> const& m1, mat<2, 3, T, Q> const& m2) + { + return (m1[0] == m2[0]) && (m1[1] == m2[1]); + } + + template + GLM_FUNC_QUALIFIER bool operator!=(mat<2, 3, T, Q> const& m1, mat<2, 3, T, Q> const& m2) + { + return (m1[0] != m2[0]) || (m1[1] != m2[1]); + } +} //namespace glm diff --git a/src/GLMath/glm/detail/type_mat2x4.hpp b/src/GLMath/glm/detail/type_mat2x4.hpp new file mode 100644 index 000000000..ff03e215e --- /dev/null +++ b/src/GLMath/glm/detail/type_mat2x4.hpp @@ -0,0 +1,161 @@ +/// @ref core +/// @file glm/detail/type_mat2x4.hpp + +#pragma once + +#include "type_vec2.hpp" +#include "type_vec4.hpp" +#include +#include + +namespace glm +{ + template + struct mat<2, 4, T, Q> + { + typedef vec<4, T, Q> col_type; + typedef vec<2, T, Q> row_type; + typedef mat<2, 4, T, Q> type; + typedef mat<4, 2, T, Q> transpose_type; + typedef T value_type; + + private: + col_type value[2]; + + public: + // -- Accesses -- + + typedef length_t length_type; + GLM_FUNC_DECL static GLM_CONSTEXPR length_type length() { return 2; } + + GLM_FUNC_DECL col_type & operator[](length_type i); + GLM_FUNC_DECL GLM_CONSTEXPR col_type const& operator[](length_type i) const; + + // -- Constructors -- + + GLM_FUNC_DECL GLM_CONSTEXPR mat() GLM_DEFAULT; + template + GLM_FUNC_DECL GLM_CONSTEXPR mat(mat<2, 4, T, P> const& m); + + GLM_FUNC_DECL explicit GLM_CONSTEXPR mat(T scalar); + GLM_FUNC_DECL GLM_CONSTEXPR mat( + T x0, T y0, T z0, T w0, + T x1, T y1, T z1, T w1); + GLM_FUNC_DECL GLM_CONSTEXPR mat( + col_type const& v0, + col_type const& v1); + + // -- Conversions -- + + template< + typename X1, typename Y1, typename Z1, typename W1, + typename X2, typename Y2, typename Z2, typename W2> + GLM_FUNC_DECL GLM_CONSTEXPR mat( + X1 x1, Y1 y1, Z1 z1, W1 w1, + X2 x2, Y2 y2, Z2 z2, W2 w2); + + template + GLM_FUNC_DECL GLM_CONSTEXPR mat( + vec<4, U, Q> const& v1, + vec<4, V, Q> const& v2); + + // -- Matrix conversions -- + + template + GLM_FUNC_DECL GLM_EXPLICIT GLM_CONSTEXPR mat(mat<2, 4, U, P> const& m); + + GLM_FUNC_DECL GLM_EXPLICIT GLM_CONSTEXPR mat(mat<2, 2, T, Q> const& x); + GLM_FUNC_DECL GLM_EXPLICIT GLM_CONSTEXPR mat(mat<3, 3, T, Q> const& x); + GLM_FUNC_DECL GLM_EXPLICIT GLM_CONSTEXPR mat(mat<4, 4, T, Q> const& x); + GLM_FUNC_DECL GLM_EXPLICIT GLM_CONSTEXPR mat(mat<2, 3, T, Q> const& x); + GLM_FUNC_DECL GLM_EXPLICIT GLM_CONSTEXPR mat(mat<3, 2, T, Q> const& x); + GLM_FUNC_DECL GLM_EXPLICIT GLM_CONSTEXPR mat(mat<3, 4, T, Q> const& x); + GLM_FUNC_DECL GLM_EXPLICIT GLM_CONSTEXPR mat(mat<4, 2, T, Q> const& x); + GLM_FUNC_DECL GLM_EXPLICIT GLM_CONSTEXPR mat(mat<4, 3, T, Q> const& x); + + // -- Unary arithmetic operators -- + + template + GLM_FUNC_DECL mat<2, 4, T, Q> & operator=(mat<2, 4, U, Q> const& m); + template + GLM_FUNC_DECL mat<2, 4, T, Q> & operator+=(U s); + template + GLM_FUNC_DECL mat<2, 4, T, Q> & operator+=(mat<2, 4, U, Q> const& m); + template + GLM_FUNC_DECL mat<2, 4, T, Q> & operator-=(U s); + template + GLM_FUNC_DECL mat<2, 4, T, Q> & operator-=(mat<2, 4, U, Q> const& m); + template + GLM_FUNC_DECL mat<2, 4, T, Q> & operator*=(U s); + template + GLM_FUNC_DECL mat<2, 4, T, Q> & operator/=(U s); + + // -- Increment and decrement operators -- + + GLM_FUNC_DECL mat<2, 4, T, Q> & operator++ (); + GLM_FUNC_DECL mat<2, 4, T, Q> & operator-- (); + GLM_FUNC_DECL mat<2, 4, T, Q> operator++(int); + GLM_FUNC_DECL mat<2, 4, T, Q> operator--(int); + }; + + // -- Unary operators -- + + template + GLM_FUNC_DECL mat<2, 4, T, Q> operator+(mat<2, 4, T, Q> const& m); + + template + GLM_FUNC_DECL mat<2, 4, T, Q> operator-(mat<2, 4, T, Q> const& m); + + // -- Binary operators -- + + template + GLM_FUNC_DECL mat<2, 4, T, Q> operator+(mat<2, 4, T, Q> const& m, T scalar); + + template + GLM_FUNC_DECL mat<2, 4, T, Q> operator+(mat<2, 4, T, Q> const& m1, mat<2, 4, T, Q> const& m2); + + template + GLM_FUNC_DECL mat<2, 4, T, Q> operator-(mat<2, 4, T, Q> const& m, T scalar); + + template + GLM_FUNC_DECL mat<2, 4, T, Q> operator-(mat<2, 4, T, Q> const& m1, mat<2, 4, T, Q> const& m2); + + template + GLM_FUNC_DECL mat<2, 4, T, Q> operator*(mat<2, 4, T, Q> const& m, T scalar); + + template + GLM_FUNC_DECL mat<2, 4, T, Q> operator*(T scalar, mat<2, 4, T, Q> const& m); + + template + GLM_FUNC_DECL typename mat<2, 4, T, Q>::col_type operator*(mat<2, 4, T, Q> const& m, typename mat<2, 4, T, Q>::row_type const& v); + + template + GLM_FUNC_DECL typename mat<2, 4, T, Q>::row_type operator*(typename mat<2, 4, T, Q>::col_type const& v, mat<2, 4, T, Q> const& m); + + template + GLM_FUNC_DECL mat<4, 4, T, Q> operator*(mat<2, 4, T, Q> const& m1, mat<4, 2, T, Q> const& m2); + + template + GLM_FUNC_DECL mat<2, 4, T, Q> operator*(mat<2, 4, T, Q> const& m1, mat<2, 2, T, Q> const& m2); + + template + GLM_FUNC_DECL mat<3, 4, T, Q> operator*(mat<2, 4, T, Q> const& m1, mat<3, 2, T, Q> const& m2); + + template + GLM_FUNC_DECL mat<2, 4, T, Q> operator/(mat<2, 4, T, Q> const& m, T scalar); + + template + GLM_FUNC_DECL mat<2, 4, T, Q> operator/(T scalar, mat<2, 4, T, Q> const& m); + + // -- Boolean operators -- + + template + GLM_FUNC_DECL bool operator==(mat<2, 4, T, Q> const& m1, mat<2, 4, T, Q> const& m2); + + template + GLM_FUNC_DECL bool operator!=(mat<2, 4, T, Q> const& m1, mat<2, 4, T, Q> const& m2); +}//namespace glm + +#ifndef GLM_EXTERNAL_TEMPLATE +#include "type_mat2x4.inl" +#endif diff --git a/src/GLMath/glm/detail/type_mat2x4.inl b/src/GLMath/glm/detail/type_mat2x4.inl new file mode 100644 index 000000000..b6d2b9ddf --- /dev/null +++ b/src/GLMath/glm/detail/type_mat2x4.inl @@ -0,0 +1,520 @@ +namespace glm +{ + // -- Constructors -- + +# if GLM_CONFIG_DEFAULTED_FUNCTIONS == GLM_DISABLE + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<2, 4, T, Q>::mat() +# if GLM_CONFIG_CTOR_INIT == GLM_CTOR_INITIALIZER_LIST + : value{col_type(1, 0, 0, 0), col_type(0, 1, 0, 0)} +# endif + { +# if GLM_CONFIG_CTOR_INIT == GLM_CTOR_INITIALISATION + this->value[0] = col_type(1, 0, 0, 0); + this->value[1] = col_type(0, 1, 0, 0); +# endif + } +# endif + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<2, 4, T, Q>::mat(mat<2, 4, T, P> const& m) +# if GLM_HAS_INITIALIZER_LISTS + : value{m[0], m[1]} +# endif + { +# if !GLM_HAS_INITIALIZER_LISTS + this->value[0] = m[0]; + this->value[1] = m[1]; +# endif + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<2, 4, T, Q>::mat(T s) +# if GLM_HAS_INITIALIZER_LISTS + : value{col_type(s, 0, 0, 0), col_type(0, s, 0, 0)} +# endif + { +# if !GLM_HAS_INITIALIZER_LISTS + this->value[0] = col_type(s, 0, 0, 0); + this->value[1] = col_type(0, s, 0, 0); +# endif + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<2, 4, T, Q>::mat + ( + T x0, T y0, T z0, T w0, + T x1, T y1, T z1, T w1 + ) +# if GLM_HAS_INITIALIZER_LISTS + : value{col_type(x0, y0, z0, w0), col_type(x1, y1, z1, w1)} +# endif + { +# if !GLM_HAS_INITIALIZER_LISTS + this->value[0] = col_type(x0, y0, z0, w0); + this->value[1] = col_type(x1, y1, z1, w1); +# endif + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<2, 4, T, Q>::mat(col_type const& v0, col_type const& v1) +# if GLM_HAS_INITIALIZER_LISTS + : value{col_type(v0), col_type(v1)} +# endif + { +# if !GLM_HAS_INITIALIZER_LISTS + this->value[0] = v0; + this->value[1] = v1; +# endif + } + + // -- Conversion constructors -- + + template + template< + typename X1, typename Y1, typename Z1, typename W1, + typename X2, typename Y2, typename Z2, typename W2> + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<2, 4, T, Q>::mat + ( + X1 x1, Y1 y1, Z1 z1, W1 w1, + X2 x2, Y2 y2, Z2 z2, W2 w2 + ) +# if GLM_HAS_INITIALIZER_LISTS + : value{ + col_type(x1, y1, z1, w1), + col_type(x2, y2, z2, w2)} +# endif + { +# if !GLM_HAS_INITIALIZER_LISTS + this->value[0] = col_type(x1, y1, z1, w1); + this->value[1] = col_type(x2, y2, z2, w2); +# endif + } + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<2, 4, T, Q>::mat(vec<4, V1, Q> const& v1, vec<4, V2, Q> const& v2) +# if GLM_HAS_INITIALIZER_LISTS + : value{col_type(v1), col_type(v2)} +# endif + { +# if !GLM_HAS_INITIALIZER_LISTS + this->value[0] = col_type(v1); + this->value[1] = col_type(v2); +# endif + } + + // -- Matrix conversions -- + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<2, 4, T, Q>::mat(mat<2, 4, U, P> const& m) +# if GLM_HAS_INITIALIZER_LISTS + : value{col_type(m[0]), col_type(m[1])} +# endif + { +# if !GLM_HAS_INITIALIZER_LISTS + this->value[0] = col_type(m[0]); + this->value[1] = col_type(m[1]); +# endif + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<2, 4, T, Q>::mat(mat<2, 2, T, Q> const& m) +# if GLM_HAS_INITIALIZER_LISTS + : value{col_type(m[0], 0, 0), col_type(m[1], 0, 0)} +# endif + { +# if !GLM_HAS_INITIALIZER_LISTS + this->value[0] = col_type(m[0], 0, 0); + this->value[1] = col_type(m[1], 0, 0); +# endif + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<2, 4, T, Q>::mat(mat<3, 3, T, Q> const& m) +# if GLM_HAS_INITIALIZER_LISTS + : value{col_type(m[0], 0), col_type(m[1], 0)} +# endif + { +# if !GLM_HAS_INITIALIZER_LISTS + this->value[0] = col_type(m[0], 0); + this->value[1] = col_type(m[1], 0); +# endif + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<2, 4, T, Q>::mat(mat<4, 4, T, Q> const& m) +# if GLM_HAS_INITIALIZER_LISTS + : value{col_type(m[0]), col_type(m[1])} +# endif + { +# if !GLM_HAS_INITIALIZER_LISTS + this->value[0] = col_type(m[0]); + this->value[1] = col_type(m[1]); +# endif + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<2, 4, T, Q>::mat(mat<2, 3, T, Q> const& m) +# if GLM_HAS_INITIALIZER_LISTS + : value{col_type(m[0], 0), col_type(m[1], 0)} +# endif + { +# if !GLM_HAS_INITIALIZER_LISTS + this->value[0] = col_type(m[0], 0); + this->value[1] = col_type(m[1], 0); +# endif + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<2, 4, T, Q>::mat(mat<3, 2, T, Q> const& m) +# if GLM_HAS_INITIALIZER_LISTS + : value{col_type(m[0], 0, 0), col_type(m[1], 0, 0)} +# endif + { +# if !GLM_HAS_INITIALIZER_LISTS + this->value[0] = col_type(m[0], 0, 0); + this->value[1] = col_type(m[1], 0, 0); +# endif + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<2, 4, T, Q>::mat(mat<3, 4, T, Q> const& m) +# if GLM_HAS_INITIALIZER_LISTS + : value{col_type(m[0]), col_type(m[1])} +# endif + { +# if !GLM_HAS_INITIALIZER_LISTS + this->value[0] = col_type(m[0]); + this->value[1] = col_type(m[1]); +# endif + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<2, 4, T, Q>::mat(mat<4, 2, T, Q> const& m) +# if GLM_HAS_INITIALIZER_LISTS + : value{col_type(m[0], 0, 0), col_type(m[1], 0, 0)} +# endif + { +# if !GLM_HAS_INITIALIZER_LISTS + this->value[0] = col_type(m[0], 0, 0); + this->value[1] = col_type(m[1], 0, 0); +# endif + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<2, 4, T, Q>::mat(mat<4, 3, T, Q> const& m) +# if GLM_HAS_INITIALIZER_LISTS + : value{col_type(m[0], 0), col_type(m[1], 0)} +# endif + { +# if !GLM_HAS_INITIALIZER_LISTS + this->value[0] = col_type(m[0], 0); + this->value[1] = col_type(m[1], 0); +# endif + } + + // -- Accesses -- + + template + GLM_FUNC_QUALIFIER typename mat<2, 4, T, Q>::col_type & mat<2, 4, T, Q>::operator[](typename mat<2, 4, T, Q>::length_type i) + { + assert(i < this->length()); + return this->value[i]; + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR typename mat<2, 4, T, Q>::col_type const& mat<2, 4, T, Q>::operator[](typename mat<2, 4, T, Q>::length_type i) const + { + assert(i < this->length()); + return this->value[i]; + } + + // -- Unary updatable operators -- + + template + template + GLM_FUNC_QUALIFIER mat<2, 4, T, Q>& mat<2, 4, T, Q>::operator=(mat<2, 4, U, Q> const& m) + { + this->value[0] = m[0]; + this->value[1] = m[1]; + return *this; + } + + template + template + GLM_FUNC_QUALIFIER mat<2, 4, T, Q>& mat<2, 4, T, Q>::operator+=(U s) + { + this->value[0] += s; + this->value[1] += s; + return *this; + } + + template + template + GLM_FUNC_QUALIFIER mat<2, 4, T, Q>& mat<2, 4, T, Q>::operator+=(mat<2, 4, U, Q> const& m) + { + this->value[0] += m[0]; + this->value[1] += m[1]; + return *this; + } + + template + template + GLM_FUNC_QUALIFIER mat<2, 4, T, Q>& mat<2, 4, T, Q>::operator-=(U s) + { + this->value[0] -= s; + this->value[1] -= s; + return *this; + } + + template + template + GLM_FUNC_QUALIFIER mat<2, 4, T, Q>& mat<2, 4, T, Q>::operator-=(mat<2, 4, U, Q> const& m) + { + this->value[0] -= m[0]; + this->value[1] -= m[1]; + return *this; + } + + template + template + GLM_FUNC_QUALIFIER mat<2, 4, T, Q>& mat<2, 4, T, Q>::operator*=(U s) + { + this->value[0] *= s; + this->value[1] *= s; + return *this; + } + + template + template + GLM_FUNC_QUALIFIER mat<2, 4, T, Q> & mat<2, 4, T, Q>::operator/=(U s) + { + this->value[0] /= s; + this->value[1] /= s; + return *this; + } + + // -- Increment and decrement operators -- + + template + GLM_FUNC_QUALIFIER mat<2, 4, T, Q>& mat<2, 4, T, Q>::operator++() + { + ++this->value[0]; + ++this->value[1]; + return *this; + } + + template + GLM_FUNC_QUALIFIER mat<2, 4, T, Q>& mat<2, 4, T, Q>::operator--() + { + --this->value[0]; + --this->value[1]; + return *this; + } + + template + GLM_FUNC_QUALIFIER mat<2, 4, T, Q> mat<2, 4, T, Q>::operator++(int) + { + mat<2, 4, T, Q> Result(*this); + ++*this; + return Result; + } + + template + GLM_FUNC_QUALIFIER mat<2, 4, T, Q> mat<2, 4, T, Q>::operator--(int) + { + mat<2, 4, T, Q> Result(*this); + --*this; + return Result; + } + + // -- Unary arithmetic operators -- + + template + GLM_FUNC_QUALIFIER mat<2, 4, T, Q> operator+(mat<2, 4, T, Q> const& m) + { + return m; + } + + template + GLM_FUNC_QUALIFIER mat<2, 4, T, Q> operator-(mat<2, 4, T, Q> const& m) + { + return mat<2, 4, T, Q>( + -m[0], + -m[1]); + } + + // -- Binary arithmetic operators -- + + template + GLM_FUNC_QUALIFIER mat<2, 4, T, Q> operator+(mat<2, 4, T, Q> const& m, T scalar) + { + return mat<2, 4, T, Q>( + m[0] + scalar, + m[1] + scalar); + } + + template + GLM_FUNC_QUALIFIER mat<2, 4, T, Q> operator+(mat<2, 4, T, Q> const& m1, mat<2, 4, T, Q> const& m2) + { + return mat<2, 4, T, Q>( + m1[0] + m2[0], + m1[1] + m2[1]); + } + + template + GLM_FUNC_QUALIFIER mat<2, 4, T, Q> operator-(mat<2, 4, T, Q> const& m, T scalar) + { + return mat<2, 4, T, Q>( + m[0] - scalar, + m[1] - scalar); + } + + template + GLM_FUNC_QUALIFIER mat<2, 4, T, Q> operator-(mat<2, 4, T, Q> const& m1, mat<2, 4, T, Q> const& m2) + { + return mat<2, 4, T, Q>( + m1[0] - m2[0], + m1[1] - m2[1]); + } + + template + GLM_FUNC_QUALIFIER mat<2, 4, T, Q> operator*(mat<2, 4, T, Q> const& m, T scalar) + { + return mat<2, 4, T, Q>( + m[0] * scalar, + m[1] * scalar); + } + + template + GLM_FUNC_QUALIFIER mat<2, 4, T, Q> operator*(T scalar, mat<2, 4, T, Q> const& m) + { + return mat<2, 4, T, Q>( + m[0] * scalar, + m[1] * scalar); + } + + template + GLM_FUNC_QUALIFIER typename mat<2, 4, T, Q>::col_type operator*(mat<2, 4, T, Q> const& m, typename mat<2, 4, T, Q>::row_type const& v) + { + return typename mat<2, 4, T, Q>::col_type( + m[0][0] * v.x + m[1][0] * v.y, + m[0][1] * v.x + m[1][1] * v.y, + m[0][2] * v.x + m[1][2] * v.y, + m[0][3] * v.x + m[1][3] * v.y); + } + + template + GLM_FUNC_QUALIFIER typename mat<2, 4, T, Q>::row_type operator*(typename mat<2, 4, T, Q>::col_type const& v, mat<2, 4, T, Q> const& m) + { + return typename mat<2, 4, T, Q>::row_type( + v.x * m[0][0] + v.y * m[0][1] + v.z * m[0][2] + v.w * m[0][3], + v.x * m[1][0] + v.y * m[1][1] + v.z * m[1][2] + v.w * m[1][3]); + } + + template + GLM_FUNC_QUALIFIER mat<4, 4, T, Q> operator*(mat<2, 4, T, Q> const& m1, mat<4, 2, T, Q> const& m2) + { + T SrcA00 = m1[0][0]; + T SrcA01 = m1[0][1]; + T SrcA02 = m1[0][2]; + T SrcA03 = m1[0][3]; + T SrcA10 = m1[1][0]; + T SrcA11 = m1[1][1]; + T SrcA12 = m1[1][2]; + T SrcA13 = m1[1][3]; + + T SrcB00 = m2[0][0]; + T SrcB01 = m2[0][1]; + T SrcB10 = m2[1][0]; + T SrcB11 = m2[1][1]; + T SrcB20 = m2[2][0]; + T SrcB21 = m2[2][1]; + T SrcB30 = m2[3][0]; + T SrcB31 = m2[3][1]; + + mat<4, 4, T, Q> Result; + Result[0][0] = SrcA00 * SrcB00 + SrcA10 * SrcB01; + Result[0][1] = SrcA01 * SrcB00 + SrcA11 * SrcB01; + Result[0][2] = SrcA02 * SrcB00 + SrcA12 * SrcB01; + Result[0][3] = SrcA03 * SrcB00 + SrcA13 * SrcB01; + Result[1][0] = SrcA00 * SrcB10 + SrcA10 * SrcB11; + Result[1][1] = SrcA01 * SrcB10 + SrcA11 * SrcB11; + Result[1][2] = SrcA02 * SrcB10 + SrcA12 * SrcB11; + Result[1][3] = SrcA03 * SrcB10 + SrcA13 * SrcB11; + Result[2][0] = SrcA00 * SrcB20 + SrcA10 * SrcB21; + Result[2][1] = SrcA01 * SrcB20 + SrcA11 * SrcB21; + Result[2][2] = SrcA02 * SrcB20 + SrcA12 * SrcB21; + Result[2][3] = SrcA03 * SrcB20 + SrcA13 * SrcB21; + Result[3][0] = SrcA00 * SrcB30 + SrcA10 * SrcB31; + Result[3][1] = SrcA01 * SrcB30 + SrcA11 * SrcB31; + Result[3][2] = SrcA02 * SrcB30 + SrcA12 * SrcB31; + Result[3][3] = SrcA03 * SrcB30 + SrcA13 * SrcB31; + return Result; + } + + template + GLM_FUNC_QUALIFIER mat<2, 4, T, Q> operator*(mat<2, 4, T, Q> const& m1, mat<2, 2, T, Q> const& m2) + { + return mat<2, 4, T, Q>( + m1[0][0] * m2[0][0] + m1[1][0] * m2[0][1], + m1[0][1] * m2[0][0] + m1[1][1] * m2[0][1], + m1[0][2] * m2[0][0] + m1[1][2] * m2[0][1], + m1[0][3] * m2[0][0] + m1[1][3] * m2[0][1], + m1[0][0] * m2[1][0] + m1[1][0] * m2[1][1], + m1[0][1] * m2[1][0] + m1[1][1] * m2[1][1], + m1[0][2] * m2[1][0] + m1[1][2] * m2[1][1], + m1[0][3] * m2[1][0] + m1[1][3] * m2[1][1]); + } + + template + GLM_FUNC_QUALIFIER mat<3, 4, T, Q> operator*(mat<2, 4, T, Q> const& m1, mat<3, 2, T, Q> const& m2) + { + return mat<3, 4, T, Q>( + m1[0][0] * m2[0][0] + m1[1][0] * m2[0][1], + m1[0][1] * m2[0][0] + m1[1][1] * m2[0][1], + m1[0][2] * m2[0][0] + m1[1][2] * m2[0][1], + m1[0][3] * m2[0][0] + m1[1][3] * m2[0][1], + m1[0][0] * m2[1][0] + m1[1][0] * m2[1][1], + m1[0][1] * m2[1][0] + m1[1][1] * m2[1][1], + m1[0][2] * m2[1][0] + m1[1][2] * m2[1][1], + m1[0][3] * m2[1][0] + m1[1][3] * m2[1][1], + m1[0][0] * m2[2][0] + m1[1][0] * m2[2][1], + m1[0][1] * m2[2][0] + m1[1][1] * m2[2][1], + m1[0][2] * m2[2][0] + m1[1][2] * m2[2][1], + m1[0][3] * m2[2][0] + m1[1][3] * m2[2][1]); + } + + template + GLM_FUNC_QUALIFIER mat<2, 4, T, Q> operator/(mat<2, 4, T, Q> const& m, T scalar) + { + return mat<2, 4, T, Q>( + m[0] / scalar, + m[1] / scalar); + } + + template + GLM_FUNC_QUALIFIER mat<2, 4, T, Q> operator/(T scalar, mat<2, 4, T, Q> const& m) + { + return mat<2, 4, T, Q>( + scalar / m[0], + scalar / m[1]); + } + + // -- Boolean operators -- + + template + GLM_FUNC_QUALIFIER bool operator==(mat<2, 4, T, Q> const& m1, mat<2, 4, T, Q> const& m2) + { + return (m1[0] == m2[0]) && (m1[1] == m2[1]); + } + + template + GLM_FUNC_QUALIFIER bool operator!=(mat<2, 4, T, Q> const& m1, mat<2, 4, T, Q> const& m2) + { + return (m1[0] != m2[0]) || (m1[1] != m2[1]); + } +} //namespace glm diff --git a/src/GLMath/glm/detail/type_mat3x2.hpp b/src/GLMath/glm/detail/type_mat3x2.hpp new file mode 100644 index 000000000..e16658131 --- /dev/null +++ b/src/GLMath/glm/detail/type_mat3x2.hpp @@ -0,0 +1,167 @@ +/// @ref core +/// @file glm/detail/type_mat3x2.hpp + +#pragma once + +#include "type_vec2.hpp" +#include "type_vec3.hpp" +#include +#include + +namespace glm +{ + template + struct mat<3, 2, T, Q> + { + typedef vec<2, T, Q> col_type; + typedef vec<3, T, Q> row_type; + typedef mat<3, 2, T, Q> type; + typedef mat<2, 3, T, Q> transpose_type; + typedef T value_type; + + private: + col_type value[3]; + + public: + // -- Accesses -- + + typedef length_t length_type; + GLM_FUNC_DECL static GLM_CONSTEXPR length_type length() { return 3; } + + GLM_FUNC_DECL col_type & operator[](length_type i); + GLM_FUNC_DECL GLM_CONSTEXPR col_type const& operator[](length_type i) const; + + // -- Constructors -- + + GLM_FUNC_DECL GLM_CONSTEXPR mat() GLM_DEFAULT; + template + GLM_FUNC_DECL GLM_CONSTEXPR mat(mat<3, 2, T, P> const& m); + + GLM_FUNC_DECL explicit GLM_CONSTEXPR mat(T scalar); + GLM_FUNC_DECL GLM_CONSTEXPR mat( + T x0, T y0, + T x1, T y1, + T x2, T y2); + GLM_FUNC_DECL GLM_CONSTEXPR mat( + col_type const& v0, + col_type const& v1, + col_type const& v2); + + // -- Conversions -- + + template< + typename X1, typename Y1, + typename X2, typename Y2, + typename X3, typename Y3> + GLM_FUNC_DECL GLM_CONSTEXPR mat( + X1 x1, Y1 y1, + X2 x2, Y2 y2, + X3 x3, Y3 y3); + + template + GLM_FUNC_DECL GLM_CONSTEXPR mat( + vec<2, V1, Q> const& v1, + vec<2, V2, Q> const& v2, + vec<2, V3, Q> const& v3); + + // -- Matrix conversions -- + + template + GLM_FUNC_DECL GLM_EXPLICIT GLM_CONSTEXPR mat(mat<3, 2, U, P> const& m); + + GLM_FUNC_DECL GLM_EXPLICIT GLM_CONSTEXPR mat(mat<2, 2, T, Q> const& x); + GLM_FUNC_DECL GLM_EXPLICIT GLM_CONSTEXPR mat(mat<3, 3, T, Q> const& x); + GLM_FUNC_DECL GLM_EXPLICIT GLM_CONSTEXPR mat(mat<4, 4, T, Q> const& x); + GLM_FUNC_DECL GLM_EXPLICIT GLM_CONSTEXPR mat(mat<2, 3, T, Q> const& x); + GLM_FUNC_DECL GLM_EXPLICIT GLM_CONSTEXPR mat(mat<2, 4, T, Q> const& x); + GLM_FUNC_DECL GLM_EXPLICIT GLM_CONSTEXPR mat(mat<3, 4, T, Q> const& x); + GLM_FUNC_DECL GLM_EXPLICIT GLM_CONSTEXPR mat(mat<4, 2, T, Q> const& x); + GLM_FUNC_DECL GLM_EXPLICIT GLM_CONSTEXPR mat(mat<4, 3, T, Q> const& x); + + // -- Unary arithmetic operators -- + + template + GLM_FUNC_DECL mat<3, 2, T, Q> & operator=(mat<3, 2, U, Q> const& m); + template + GLM_FUNC_DECL mat<3, 2, T, Q> & operator+=(U s); + template + GLM_FUNC_DECL mat<3, 2, T, Q> & operator+=(mat<3, 2, U, Q> const& m); + template + GLM_FUNC_DECL mat<3, 2, T, Q> & operator-=(U s); + template + GLM_FUNC_DECL mat<3, 2, T, Q> & operator-=(mat<3, 2, U, Q> const& m); + template + GLM_FUNC_DECL mat<3, 2, T, Q> & operator*=(U s); + template + GLM_FUNC_DECL mat<3, 2, T, Q> & operator/=(U s); + + // -- Increment and decrement operators -- + + GLM_FUNC_DECL mat<3, 2, T, Q> & operator++ (); + GLM_FUNC_DECL mat<3, 2, T, Q> & operator-- (); + GLM_FUNC_DECL mat<3, 2, T, Q> operator++(int); + GLM_FUNC_DECL mat<3, 2, T, Q> operator--(int); + }; + + // -- Unary operators -- + + template + GLM_FUNC_DECL mat<3, 2, T, Q> operator+(mat<3, 2, T, Q> const& m); + + template + GLM_FUNC_DECL mat<3, 2, T, Q> operator-(mat<3, 2, T, Q> const& m); + + // -- Binary operators -- + + template + GLM_FUNC_DECL mat<3, 2, T, Q> operator+(mat<3, 2, T, Q> const& m, T scalar); + + template + GLM_FUNC_DECL mat<3, 2, T, Q> operator+(mat<3, 2, T, Q> const& m1, mat<3, 2, T, Q> const& m2); + + template + GLM_FUNC_DECL mat<3, 2, T, Q> operator-(mat<3, 2, T, Q> const& m, T scalar); + + template + GLM_FUNC_DECL mat<3, 2, T, Q> operator-(mat<3, 2, T, Q> const& m1, mat<3, 2, T, Q> const& m2); + + template + GLM_FUNC_DECL mat<3, 2, T, Q> operator*(mat<3, 2, T, Q> const& m, T scalar); + + template + GLM_FUNC_DECL mat<3, 2, T, Q> operator*(T scalar, mat<3, 2, T, Q> const& m); + + template + GLM_FUNC_DECL typename mat<3, 2, T, Q>::col_type operator*(mat<3, 2, T, Q> const& m, typename mat<3, 2, T, Q>::row_type const& v); + + template + GLM_FUNC_DECL typename mat<3, 2, T, Q>::row_type operator*(typename mat<3, 2, T, Q>::col_type const& v, mat<3, 2, T, Q> const& m); + + template + GLM_FUNC_DECL mat<2, 2, T, Q> operator*(mat<3, 2, T, Q> const& m1, mat<2, 3, T, Q> const& m2); + + template + GLM_FUNC_DECL mat<3, 2, T, Q> operator*(mat<3, 2, T, Q> const& m1, mat<3, 3, T, Q> const& m2); + + template + GLM_FUNC_DECL mat<4, 2, T, Q> operator*(mat<3, 2, T, Q> const& m1, mat<4, 3, T, Q> const& m2); + + template + GLM_FUNC_DECL mat<3, 2, T, Q> operator/(mat<3, 2, T, Q> const& m, T scalar); + + template + GLM_FUNC_DECL mat<3, 2, T, Q> operator/(T scalar, mat<3, 2, T, Q> const& m); + + // -- Boolean operators -- + + template + GLM_FUNC_DECL bool operator==(mat<3, 2, T, Q> const& m1, mat<3, 2, T, Q> const& m2); + + template + GLM_FUNC_DECL bool operator!=(mat<3, 2, T, Q> const& m1, mat<3, 2, T, Q> const& m2); + +}//namespace glm + +#ifndef GLM_EXTERNAL_TEMPLATE +#include "type_mat3x2.inl" +#endif diff --git a/src/GLMath/glm/detail/type_mat3x2.inl b/src/GLMath/glm/detail/type_mat3x2.inl new file mode 100644 index 000000000..b4b948b72 --- /dev/null +++ b/src/GLMath/glm/detail/type_mat3x2.inl @@ -0,0 +1,532 @@ +namespace glm +{ + // -- Constructors -- + +# if GLM_CONFIG_DEFAULTED_FUNCTIONS == GLM_DISABLE + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<3, 2, T, Q>::mat() +# if GLM_CONFIG_CTOR_INIT == GLM_CTOR_INITIALIZER_LIST + : value{col_type(1, 0), col_type(0, 1), col_type(0, 0)} +# endif + { +# if GLM_CONFIG_CTOR_INIT == GLM_CTOR_INITIALISATION + this->value[0] = col_type(1, 0); + this->value[1] = col_type(0, 1); + this->value[2] = col_type(0, 0); +# endif + } +# endif + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<3, 2, T, Q>::mat(mat<3, 2, T, P> const& m) +# if GLM_HAS_INITIALIZER_LISTS + : value{col_type(m[0]), col_type(m[1]), col_type(m[2])} +# endif + { +# if !GLM_HAS_INITIALIZER_LISTS + this->value[0] = m[0]; + this->value[1] = m[1]; + this->value[2] = m[2]; +# endif + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<3, 2, T, Q>::mat(T s) +# if GLM_HAS_INITIALIZER_LISTS + : value{col_type(s, 0), col_type(0, s), col_type(0, 0)} +# endif + { +# if !GLM_HAS_INITIALIZER_LISTS + this->value[0] = col_type(s, 0); + this->value[1] = col_type(0, s); + this->value[2] = col_type(0, 0); +# endif + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<3, 2, T, Q>::mat + ( + T x0, T y0, + T x1, T y1, + T x2, T y2 + ) +# if GLM_HAS_INITIALIZER_LISTS + : value{col_type(x0, y0), col_type(x1, y1), col_type(x2, y2)} +# endif + { +# if !GLM_HAS_INITIALIZER_LISTS + this->value[0] = col_type(x0, y0); + this->value[1] = col_type(x1, y1); + this->value[2] = col_type(x2, y2); +# endif + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<3, 2, T, Q>::mat(col_type const& v0, col_type const& v1, col_type const& v2) +# if GLM_HAS_INITIALIZER_LISTS + : value{col_type(v0), col_type(v1), col_type(v2)} +# endif + { +# if !GLM_HAS_INITIALIZER_LISTS + this->value[0] = v0; + this->value[1] = v1; + this->value[2] = v2; +# endif + } + + // -- Conversion constructors -- + + template + template< + typename X0, typename Y0, + typename X1, typename Y1, + typename X2, typename Y2> + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<3, 2, T, Q>::mat + ( + X0 x0, Y0 y0, + X1 x1, Y1 y1, + X2 x2, Y2 y2 + ) +# if GLM_HAS_INITIALIZER_LISTS + : value{col_type(x0, y0), col_type(x1, y1), col_type(x2, y2)} +# endif + { +# if !GLM_HAS_INITIALIZER_LISTS + this->value[0] = col_type(x0, y0); + this->value[1] = col_type(x1, y1); + this->value[2] = col_type(x2, y2); +# endif + } + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<3, 2, T, Q>::mat(vec<2, V0, Q> const& v0, vec<2, V1, Q> const& v1, vec<2, V2, Q> const& v2) +# if GLM_HAS_INITIALIZER_LISTS + : value{col_type(v0), col_type(v1), col_type(v2)} +# endif + { +# if !GLM_HAS_INITIALIZER_LISTS + this->value[0] = col_type(v0); + this->value[1] = col_type(v1); + this->value[2] = col_type(v2); +# endif + } + + // -- Matrix conversions -- + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<3, 2, T, Q>::mat(mat<3, 2, U, P> const& m) +# if GLM_HAS_INITIALIZER_LISTS + : value{col_type(m[0]), col_type(m[1]), col_type(m[2])} +# endif + { +# if !GLM_HAS_INITIALIZER_LISTS + this->value[0] = col_type(m[0]); + this->value[1] = col_type(m[1]); + this->value[2] = col_type(m[2]); +# endif + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<3, 2, T, Q>::mat(mat<2, 2, T, Q> const& m) +# if GLM_HAS_INITIALIZER_LISTS + : value{col_type(m[0]), col_type(m[1]), col_type(0)} +# endif + { +# if !GLM_HAS_INITIALIZER_LISTS + this->value[0] = m[0]; + this->value[1] = m[1]; + this->value[2] = col_type(0); +# endif + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<3, 2, T, Q>::mat(mat<3, 3, T, Q> const& m) +# if GLM_HAS_INITIALIZER_LISTS + : value{col_type(m[0]), col_type(m[1]), col_type(m[2])} +# endif + { +# if !GLM_HAS_INITIALIZER_LISTS + this->value[0] = col_type(m[0]); + this->value[1] = col_type(m[1]); + this->value[2] = col_type(m[2]); +# endif + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<3, 2, T, Q>::mat(mat<4, 4, T, Q> const& m) +# if GLM_HAS_INITIALIZER_LISTS + : value{col_type(m[0]), col_type(m[1]), col_type(m[2])} +# endif + { +# if !GLM_HAS_INITIALIZER_LISTS + this->value[0] = col_type(m[0]); + this->value[1] = col_type(m[1]); + this->value[2] = col_type(m[2]); +# endif + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<3, 2, T, Q>::mat(mat<2, 3, T, Q> const& m) +# if GLM_HAS_INITIALIZER_LISTS + : value{col_type(m[0]), col_type(m[1]), col_type(0)} +# endif + { +# if !GLM_HAS_INITIALIZER_LISTS + this->value[0] = col_type(m[0]); + this->value[1] = col_type(m[1]); + this->value[2] = col_type(0); +# endif + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<3, 2, T, Q>::mat(mat<2, 4, T, Q> const& m) +# if GLM_HAS_INITIALIZER_LISTS + : value{col_type(m[0]), col_type(m[1]), col_type(0)} +# endif + { +# if !GLM_HAS_INITIALIZER_LISTS + this->value[0] = col_type(m[0]); + this->value[1] = col_type(m[1]); + this->value[2] = col_type(0); +# endif + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<3, 2, T, Q>::mat(mat<3, 4, T, Q> const& m) +# if GLM_HAS_INITIALIZER_LISTS + : value{col_type(m[0]), col_type(m[1]), col_type(m[2])} +# endif + { +# if !GLM_HAS_INITIALIZER_LISTS + this->value[0] = col_type(m[0]); + this->value[1] = col_type(m[1]); + this->value[2] = col_type(m[2]); +# endif + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<3, 2, T, Q>::mat(mat<4, 2, T, Q> const& m) +# if GLM_HAS_INITIALIZER_LISTS + : value{col_type(m[0]), col_type(m[1]), col_type(m[2])} +# endif + { +# if !GLM_HAS_INITIALIZER_LISTS + this->value[0] = m[0]; + this->value[1] = m[1]; + this->value[2] = m[2]; +# endif + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<3, 2, T, Q>::mat(mat<4, 3, T, Q> const& m) +# if GLM_HAS_INITIALIZER_LISTS + : value{col_type(m[0]), col_type(m[1]), col_type(m[2])} +# endif + { +# if !GLM_HAS_INITIALIZER_LISTS + this->value[0] = col_type(m[0]); + this->value[1] = col_type(m[1]); + this->value[2] = col_type(m[2]); +# endif + } + + // -- Accesses -- + + template + GLM_FUNC_QUALIFIER typename mat<3, 2, T, Q>::col_type & mat<3, 2, T, Q>::operator[](typename mat<3, 2, T, Q>::length_type i) + { + assert(i < this->length()); + return this->value[i]; + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR typename mat<3, 2, T, Q>::col_type const& mat<3, 2, T, Q>::operator[](typename mat<3, 2, T, Q>::length_type i) const + { + assert(i < this->length()); + return this->value[i]; + } + + // -- Unary updatable operators -- + + template + template + GLM_FUNC_QUALIFIER mat<3, 2, T, Q>& mat<3, 2, T, Q>::operator=(mat<3, 2, U, Q> const& m) + { + this->value[0] = m[0]; + this->value[1] = m[1]; + this->value[2] = m[2]; + return *this; + } + + template + template + GLM_FUNC_QUALIFIER mat<3, 2, T, Q>& mat<3, 2, T, Q>::operator+=(U s) + { + this->value[0] += s; + this->value[1] += s; + this->value[2] += s; + return *this; + } + + template + template + GLM_FUNC_QUALIFIER mat<3, 2, T, Q>& mat<3, 2, T, Q>::operator+=(mat<3, 2, U, Q> const& m) + { + this->value[0] += m[0]; + this->value[1] += m[1]; + this->value[2] += m[2]; + return *this; + } + + template + template + GLM_FUNC_QUALIFIER mat<3, 2, T, Q>& mat<3, 2, T, Q>::operator-=(U s) + { + this->value[0] -= s; + this->value[1] -= s; + this->value[2] -= s; + return *this; + } + + template + template + GLM_FUNC_QUALIFIER mat<3, 2, T, Q>& mat<3, 2, T, Q>::operator-=(mat<3, 2, U, Q> const& m) + { + this->value[0] -= m[0]; + this->value[1] -= m[1]; + this->value[2] -= m[2]; + return *this; + } + + template + template + GLM_FUNC_QUALIFIER mat<3, 2, T, Q>& mat<3, 2, T, Q>::operator*=(U s) + { + this->value[0] *= s; + this->value[1] *= s; + this->value[2] *= s; + return *this; + } + + template + template + GLM_FUNC_QUALIFIER mat<3, 2, T, Q> & mat<3, 2, T, Q>::operator/=(U s) + { + this->value[0] /= s; + this->value[1] /= s; + this->value[2] /= s; + return *this; + } + + // -- Increment and decrement operators -- + + template + GLM_FUNC_QUALIFIER mat<3, 2, T, Q>& mat<3, 2, T, Q>::operator++() + { + ++this->value[0]; + ++this->value[1]; + ++this->value[2]; + return *this; + } + + template + GLM_FUNC_QUALIFIER mat<3, 2, T, Q>& mat<3, 2, T, Q>::operator--() + { + --this->value[0]; + --this->value[1]; + --this->value[2]; + return *this; + } + + template + GLM_FUNC_QUALIFIER mat<3, 2, T, Q> mat<3, 2, T, Q>::operator++(int) + { + mat<3, 2, T, Q> Result(*this); + ++*this; + return Result; + } + + template + GLM_FUNC_QUALIFIER mat<3, 2, T, Q> mat<3, 2, T, Q>::operator--(int) + { + mat<3, 2, T, Q> Result(*this); + --*this; + return Result; + } + + // -- Unary arithmetic operators -- + + template + GLM_FUNC_QUALIFIER mat<3, 2, T, Q> operator+(mat<3, 2, T, Q> const& m) + { + return m; + } + + template + GLM_FUNC_QUALIFIER mat<3, 2, T, Q> operator-(mat<3, 2, T, Q> const& m) + { + return mat<3, 2, T, Q>( + -m[0], + -m[1], + -m[2]); + } + + // -- Binary arithmetic operators -- + + template + GLM_FUNC_QUALIFIER mat<3, 2, T, Q> operator+(mat<3, 2, T, Q> const& m, T scalar) + { + return mat<3, 2, T, Q>( + m[0] + scalar, + m[1] + scalar, + m[2] + scalar); + } + + template + GLM_FUNC_QUALIFIER mat<3, 2, T, Q> operator+(mat<3, 2, T, Q> const& m1, mat<3, 2, T, Q> const& m2) + { + return mat<3, 2, T, Q>( + m1[0] + m2[0], + m1[1] + m2[1], + m1[2] + m2[2]); + } + + template + GLM_FUNC_QUALIFIER mat<3, 2, T, Q> operator-(mat<3, 2, T, Q> const& m, T scalar) + { + return mat<3, 2, T, Q>( + m[0] - scalar, + m[1] - scalar, + m[2] - scalar); + } + + template + GLM_FUNC_QUALIFIER mat<3, 2, T, Q> operator-(mat<3, 2, T, Q> const& m1, mat<3, 2, T, Q> const& m2) + { + return mat<3, 2, T, Q>( + m1[0] - m2[0], + m1[1] - m2[1], + m1[2] - m2[2]); + } + + template + GLM_FUNC_QUALIFIER mat<3, 2, T, Q> operator*(mat<3, 2, T, Q> const& m, T scalar) + { + return mat<3, 2, T, Q>( + m[0] * scalar, + m[1] * scalar, + m[2] * scalar); + } + + template + GLM_FUNC_QUALIFIER mat<3, 2, T, Q> operator*(T scalar, mat<3, 2, T, Q> const& m) + { + return mat<3, 2, T, Q>( + m[0] * scalar, + m[1] * scalar, + m[2] * scalar); + } + + template + GLM_FUNC_QUALIFIER typename mat<3, 2, T, Q>::col_type operator*(mat<3, 2, T, Q> const& m, typename mat<3, 2, T, Q>::row_type const& v) + { + return typename mat<3, 2, T, Q>::col_type( + m[0][0] * v.x + m[1][0] * v.y + m[2][0] * v.z, + m[0][1] * v.x + m[1][1] * v.y + m[2][1] * v.z); + } + + template + GLM_FUNC_QUALIFIER typename mat<3, 2, T, Q>::row_type operator*(typename mat<3, 2, T, Q>::col_type const& v, mat<3, 2, T, Q> const& m) + { + return typename mat<3, 2, T, Q>::row_type( + v.x * m[0][0] + v.y * m[0][1], + v.x * m[1][0] + v.y * m[1][1], + v.x * m[2][0] + v.y * m[2][1]); + } + + template + GLM_FUNC_QUALIFIER mat<2, 2, T, Q> operator*(mat<3, 2, T, Q> const& m1, mat<2, 3, T, Q> const& m2) + { + const T SrcA00 = m1[0][0]; + const T SrcA01 = m1[0][1]; + const T SrcA10 = m1[1][0]; + const T SrcA11 = m1[1][1]; + const T SrcA20 = m1[2][0]; + const T SrcA21 = m1[2][1]; + + const T SrcB00 = m2[0][0]; + const T SrcB01 = m2[0][1]; + const T SrcB02 = m2[0][2]; + const T SrcB10 = m2[1][0]; + const T SrcB11 = m2[1][1]; + const T SrcB12 = m2[1][2]; + + mat<2, 2, T, Q> Result; + Result[0][0] = SrcA00 * SrcB00 + SrcA10 * SrcB01 + SrcA20 * SrcB02; + Result[0][1] = SrcA01 * SrcB00 + SrcA11 * SrcB01 + SrcA21 * SrcB02; + Result[1][0] = SrcA00 * SrcB10 + SrcA10 * SrcB11 + SrcA20 * SrcB12; + Result[1][1] = SrcA01 * SrcB10 + SrcA11 * SrcB11 + SrcA21 * SrcB12; + return Result; + } + + template + GLM_FUNC_QUALIFIER mat<3, 2, T, Q> operator*(mat<3, 2, T, Q> const& m1, mat<3, 3, T, Q> const& m2) + { + return mat<3, 2, T, Q>( + m1[0][0] * m2[0][0] + m1[1][0] * m2[0][1] + m1[2][0] * m2[0][2], + m1[0][1] * m2[0][0] + m1[1][1] * m2[0][1] + m1[2][1] * m2[0][2], + m1[0][0] * m2[1][0] + m1[1][0] * m2[1][1] + m1[2][0] * m2[1][2], + m1[0][1] * m2[1][0] + m1[1][1] * m2[1][1] + m1[2][1] * m2[1][2], + m1[0][0] * m2[2][0] + m1[1][0] * m2[2][1] + m1[2][0] * m2[2][2], + m1[0][1] * m2[2][0] + m1[1][1] * m2[2][1] + m1[2][1] * m2[2][2]); + } + + template + GLM_FUNC_QUALIFIER mat<4, 2, T, Q> operator*(mat<3, 2, T, Q> const& m1, mat<4, 3, T, Q> const& m2) + { + return mat<4, 2, T, Q>( + m1[0][0] * m2[0][0] + m1[1][0] * m2[0][1] + m1[2][0] * m2[0][2], + m1[0][1] * m2[0][0] + m1[1][1] * m2[0][1] + m1[2][1] * m2[0][2], + m1[0][0] * m2[1][0] + m1[1][0] * m2[1][1] + m1[2][0] * m2[1][2], + m1[0][1] * m2[1][0] + m1[1][1] * m2[1][1] + m1[2][1] * m2[1][2], + m1[0][0] * m2[2][0] + m1[1][0] * m2[2][1] + m1[2][0] * m2[2][2], + m1[0][1] * m2[2][0] + m1[1][1] * m2[2][1] + m1[2][1] * m2[2][2], + m1[0][0] * m2[3][0] + m1[1][0] * m2[3][1] + m1[2][0] * m2[3][2], + m1[0][1] * m2[3][0] + m1[1][1] * m2[3][1] + m1[2][1] * m2[3][2]); + } + + template + GLM_FUNC_QUALIFIER mat<3, 2, T, Q> operator/(mat<3, 2, T, Q> const& m, T scalar) + { + return mat<3, 2, T, Q>( + m[0] / scalar, + m[1] / scalar, + m[2] / scalar); + } + + template + GLM_FUNC_QUALIFIER mat<3, 2, T, Q> operator/(T scalar, mat<3, 2, T, Q> const& m) + { + return mat<3, 2, T, Q>( + scalar / m[0], + scalar / m[1], + scalar / m[2]); + } + + // -- Boolean operators -- + + template + GLM_FUNC_QUALIFIER bool operator==(mat<3, 2, T, Q> const& m1, mat<3, 2, T, Q> const& m2) + { + return (m1[0] == m2[0]) && (m1[1] == m2[1]) && (m1[2] == m2[2]); + } + + template + GLM_FUNC_QUALIFIER bool operator!=(mat<3, 2, T, Q> const& m1, mat<3, 2, T, Q> const& m2) + { + return (m1[0] != m2[0]) || (m1[1] != m2[1]) || (m1[2] != m2[2]); + } +} //namespace glm diff --git a/src/GLMath/glm/detail/type_mat3x3.hpp b/src/GLMath/glm/detail/type_mat3x3.hpp new file mode 100644 index 000000000..3174872eb --- /dev/null +++ b/src/GLMath/glm/detail/type_mat3x3.hpp @@ -0,0 +1,184 @@ +/// @ref core +/// @file glm/detail/type_mat3x3.hpp + +#pragma once + +#include "type_vec3.hpp" +#include +#include + +namespace glm +{ + template + struct mat<3, 3, T, Q> + { + typedef vec<3, T, Q> col_type; + typedef vec<3, T, Q> row_type; + typedef mat<3, 3, T, Q> type; + typedef mat<3, 3, T, Q> transpose_type; + typedef T value_type; + + private: + col_type value[3]; + + public: + // -- Accesses -- + + typedef length_t length_type; + GLM_FUNC_DECL static GLM_CONSTEXPR length_type length() { return 3; } + + GLM_FUNC_DECL col_type & operator[](length_type i); + GLM_FUNC_DECL GLM_CONSTEXPR col_type const& operator[](length_type i) const; + + // -- Constructors -- + + GLM_FUNC_DECL GLM_CONSTEXPR mat() GLM_DEFAULT; + template + GLM_FUNC_DECL GLM_CONSTEXPR mat(mat<3, 3, T, P> const& m); + + GLM_FUNC_DECL explicit GLM_CONSTEXPR mat(T scalar); + GLM_FUNC_DECL GLM_CONSTEXPR mat( + T x0, T y0, T z0, + T x1, T y1, T z1, + T x2, T y2, T z2); + GLM_FUNC_DECL GLM_CONSTEXPR mat( + col_type const& v0, + col_type const& v1, + col_type const& v2); + + // -- Conversions -- + + template< + typename X1, typename Y1, typename Z1, + typename X2, typename Y2, typename Z2, + typename X3, typename Y3, typename Z3> + GLM_FUNC_DECL GLM_CONSTEXPR mat( + X1 x1, Y1 y1, Z1 z1, + X2 x2, Y2 y2, Z2 z2, + X3 x3, Y3 y3, Z3 z3); + + template + GLM_FUNC_DECL GLM_CONSTEXPR mat( + vec<3, V1, Q> const& v1, + vec<3, V2, Q> const& v2, + vec<3, V3, Q> const& v3); + + // -- Matrix conversions -- + + template + GLM_FUNC_DECL GLM_EXPLICIT GLM_CONSTEXPR mat(mat<3, 3, U, P> const& m); + + GLM_FUNC_DECL GLM_EXPLICIT GLM_CONSTEXPR mat(mat<2, 2, T, Q> const& x); + GLM_FUNC_DECL GLM_EXPLICIT GLM_CONSTEXPR mat(mat<4, 4, T, Q> const& x); + GLM_FUNC_DECL GLM_EXPLICIT GLM_CONSTEXPR mat(mat<2, 3, T, Q> const& x); + GLM_FUNC_DECL GLM_EXPLICIT GLM_CONSTEXPR mat(mat<3, 2, T, Q> const& x); + GLM_FUNC_DECL GLM_EXPLICIT GLM_CONSTEXPR mat(mat<2, 4, T, Q> const& x); + GLM_FUNC_DECL GLM_EXPLICIT GLM_CONSTEXPR mat(mat<4, 2, T, Q> const& x); + GLM_FUNC_DECL GLM_EXPLICIT GLM_CONSTEXPR mat(mat<3, 4, T, Q> const& x); + GLM_FUNC_DECL GLM_EXPLICIT GLM_CONSTEXPR mat(mat<4, 3, T, Q> const& x); + + // -- Unary arithmetic operators -- + + template + GLM_FUNC_DECL mat<3, 3, T, Q> & operator=(mat<3, 3, U, Q> const& m); + template + GLM_FUNC_DECL mat<3, 3, T, Q> & operator+=(U s); + template + GLM_FUNC_DECL mat<3, 3, T, Q> & operator+=(mat<3, 3, U, Q> const& m); + template + GLM_FUNC_DECL mat<3, 3, T, Q> & operator-=(U s); + template + GLM_FUNC_DECL mat<3, 3, T, Q> & operator-=(mat<3, 3, U, Q> const& m); + template + GLM_FUNC_DECL mat<3, 3, T, Q> & operator*=(U s); + template + GLM_FUNC_DECL mat<3, 3, T, Q> & operator*=(mat<3, 3, U, Q> const& m); + template + GLM_FUNC_DECL mat<3, 3, T, Q> & operator/=(U s); + template + GLM_FUNC_DECL mat<3, 3, T, Q> & operator/=(mat<3, 3, U, Q> const& m); + + // -- Increment and decrement operators -- + + GLM_FUNC_DECL mat<3, 3, T, Q> & operator++(); + GLM_FUNC_DECL mat<3, 3, T, Q> & operator--(); + GLM_FUNC_DECL mat<3, 3, T, Q> operator++(int); + GLM_FUNC_DECL mat<3, 3, T, Q> operator--(int); + }; + + // -- Unary operators -- + + template + GLM_FUNC_DECL mat<3, 3, T, Q> operator+(mat<3, 3, T, Q> const& m); + + template + GLM_FUNC_DECL mat<3, 3, T, Q> operator-(mat<3, 3, T, Q> const& m); + + // -- Binary operators -- + + template + GLM_FUNC_DECL mat<3, 3, T, Q> operator+(mat<3, 3, T, Q> const& m, T scalar); + + template + GLM_FUNC_DECL mat<3, 3, T, Q> operator+(T scalar, mat<3, 3, T, Q> const& m); + + template + GLM_FUNC_DECL mat<3, 3, T, Q> operator+(mat<3, 3, T, Q> const& m1, mat<3, 3, T, Q> const& m2); + + template + GLM_FUNC_DECL mat<3, 3, T, Q> operator-(mat<3, 3, T, Q> const& m, T scalar); + + template + GLM_FUNC_DECL mat<3, 3, T, Q> operator-(T scalar, mat<3, 3, T, Q> const& m); + + template + GLM_FUNC_DECL mat<3, 3, T, Q> operator-(mat<3, 3, T, Q> const& m1, mat<3, 3, T, Q> const& m2); + + template + GLM_FUNC_DECL mat<3, 3, T, Q> operator*(mat<3, 3, T, Q> const& m, T scalar); + + template + GLM_FUNC_DECL mat<3, 3, T, Q> operator*(T scalar, mat<3, 3, T, Q> const& m); + + template + GLM_FUNC_DECL typename mat<3, 3, T, Q>::col_type operator*(mat<3, 3, T, Q> const& m, typename mat<3, 3, T, Q>::row_type const& v); + + template + GLM_FUNC_DECL typename mat<3, 3, T, Q>::row_type operator*(typename mat<3, 3, T, Q>::col_type const& v, mat<3, 3, T, Q> const& m); + + template + GLM_FUNC_DECL mat<3, 3, T, Q> operator*(mat<3, 3, T, Q> const& m1, mat<3, 3, T, Q> const& m2); + + template + GLM_FUNC_DECL mat<2, 3, T, Q> operator*(mat<3, 3, T, Q> const& m1, mat<2, 3, T, Q> const& m2); + + template + GLM_FUNC_DECL mat<4, 3, T, Q> operator*(mat<3, 3, T, Q> const& m1, mat<4, 3, T, Q> const& m2); + + template + GLM_FUNC_DECL mat<3, 3, T, Q> operator/(mat<3, 3, T, Q> const& m, T scalar); + + template + GLM_FUNC_DECL mat<3, 3, T, Q> operator/(T scalar, mat<3, 3, T, Q> const& m); + + template + GLM_FUNC_DECL typename mat<3, 3, T, Q>::col_type operator/(mat<3, 3, T, Q> const& m, typename mat<3, 3, T, Q>::row_type const& v); + + template + GLM_FUNC_DECL typename mat<3, 3, T, Q>::row_type operator/(typename mat<3, 3, T, Q>::col_type const& v, mat<3, 3, T, Q> const& m); + + template + GLM_FUNC_DECL mat<3, 3, T, Q> operator/(mat<3, 3, T, Q> const& m1, mat<3, 3, T, Q> const& m2); + + // -- Boolean operators -- + + template + GLM_FUNC_DECL GLM_CONSTEXPR bool operator==(mat<3, 3, T, Q> const& m1, mat<3, 3, T, Q> const& m2); + + template + GLM_FUNC_DECL bool operator!=(mat<3, 3, T, Q> const& m1, mat<3, 3, T, Q> const& m2); +}//namespace glm + +#ifndef GLM_EXTERNAL_TEMPLATE +#include "type_mat3x3.inl" +#endif diff --git a/src/GLMath/glm/detail/type_mat3x3.inl b/src/GLMath/glm/detail/type_mat3x3.inl new file mode 100644 index 000000000..1ddaf99d5 --- /dev/null +++ b/src/GLMath/glm/detail/type_mat3x3.inl @@ -0,0 +1,601 @@ +#include "../matrix.hpp" + +namespace glm +{ + // -- Constructors -- + +# if GLM_CONFIG_DEFAULTED_FUNCTIONS == GLM_DISABLE + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<3, 3, T, Q>::mat() +# if GLM_CONFIG_CTOR_INIT == GLM_CTOR_INITIALIZER_LIST + : value{col_type(1, 0, 0), col_type(0, 1, 0), col_type(0, 0, 1)} +# endif + { +# if GLM_CONFIG_CTOR_INIT == GLM_CTOR_INITIALISATION + this->value[0] = col_type(1, 0, 0); + this->value[1] = col_type(0, 1, 0); + this->value[2] = col_type(0, 0, 1); +# endif + } +# endif + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<3, 3, T, Q>::mat(mat<3, 3, T, P> const& m) +# if GLM_HAS_INITIALIZER_LISTS + : value{col_type(m[0]), col_type(m[1]), col_type(m[2])} +# endif + { +# if !GLM_HAS_INITIALIZER_LISTS + this->value[0] = col_type(m[0]); + this->value[1] = col_type(m[1]); + this->value[2] = col_type(m[2]); +# endif + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<3, 3, T, Q>::mat(T s) +# if GLM_HAS_INITIALIZER_LISTS + : value{col_type(s, 0, 0), col_type(0, s, 0), col_type(0, 0, s)} +# endif + { +# if !GLM_HAS_INITIALIZER_LISTS + this->value[0] = col_type(s, 0, 0); + this->value[1] = col_type(0, s, 0); + this->value[2] = col_type(0, 0, s); +# endif + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<3, 3, T, Q>::mat + ( + T x0, T y0, T z0, + T x1, T y1, T z1, + T x2, T y2, T z2 + ) +# if GLM_HAS_INITIALIZER_LISTS + : value{col_type(x0, y0, z0), col_type(x1, y1, z1), col_type(x2, y2, z2)} +# endif + { +# if !GLM_HAS_INITIALIZER_LISTS + this->value[0] = col_type(x0, y0, z0); + this->value[1] = col_type(x1, y1, z1); + this->value[2] = col_type(x2, y2, z2); +# endif + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<3, 3, T, Q>::mat(col_type const& v0, col_type const& v1, col_type const& v2) +# if GLM_HAS_INITIALIZER_LISTS + : value{col_type(v0), col_type(v1), col_type(v2)} +# endif + { +# if !GLM_HAS_INITIALIZER_LISTS + this->value[0] = col_type(v0); + this->value[1] = col_type(v1); + this->value[2] = col_type(v2); +# endif + } + + // -- Conversion constructors -- + + template + template< + typename X1, typename Y1, typename Z1, + typename X2, typename Y2, typename Z2, + typename X3, typename Y3, typename Z3> + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<3, 3, T, Q>::mat + ( + X1 x1, Y1 y1, Z1 z1, + X2 x2, Y2 y2, Z2 z2, + X3 x3, Y3 y3, Z3 z3 + ) +# if GLM_HAS_INITIALIZER_LISTS + : value{col_type(x1, y1, z1), col_type(x2, y2, z2), col_type(x3, y3, z3)} +# endif + { +# if !GLM_HAS_INITIALIZER_LISTS + this->value[0] = col_type(x1, y1, z1); + this->value[1] = col_type(x2, y2, z2); + this->value[2] = col_type(x3, y3, z3); +# endif + } + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<3, 3, T, Q>::mat(vec<3, V1, Q> const& v1, vec<3, V2, Q> const& v2, vec<3, V3, Q> const& v3) +# if GLM_HAS_INITIALIZER_LISTS + : value{col_type(v1), col_type(v2), col_type(v3)} +# endif + { +# if !GLM_HAS_INITIALIZER_LISTS + this->value[0] = col_type(v1); + this->value[1] = col_type(v2); + this->value[2] = col_type(v3); +# endif + } + + // -- Matrix conversions -- + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<3, 3, T, Q>::mat(mat<3, 3, U, P> const& m) +# if GLM_HAS_INITIALIZER_LISTS + : value{col_type(m[0]), col_type(m[1]), col_type(m[2])} +# endif + { +# if !GLM_HAS_INITIALIZER_LISTS + this->value[0] = col_type(m[0]); + this->value[1] = col_type(m[1]); + this->value[2] = col_type(m[2]); +# endif + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<3, 3, T, Q>::mat(mat<2, 2, T, Q> const& m) +# if GLM_HAS_INITIALIZER_LISTS + : value{col_type(m[0], 0), col_type(m[1], 0), col_type(0, 0, 1)} +# endif + { +# if !GLM_HAS_INITIALIZER_LISTS + this->value[0] = col_type(m[0], 0); + this->value[1] = col_type(m[1], 0); + this->value[2] = col_type(0, 0, 1); +# endif + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<3, 3, T, Q>::mat(mat<4, 4, T, Q> const& m) +# if GLM_HAS_INITIALIZER_LISTS + : value{col_type(m[0]), col_type(m[1]), col_type(m[2])} +# endif + { +# if !GLM_HAS_INITIALIZER_LISTS + this->value[0] = col_type(m[0]); + this->value[1] = col_type(m[1]); + this->value[2] = col_type(m[2]); +# endif + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<3, 3, T, Q>::mat(mat<2, 3, T, Q> const& m) +# if GLM_HAS_INITIALIZER_LISTS + : value{col_type(m[0]), col_type(m[1]), col_type(0, 0, 1)} +# endif + { +# if !GLM_HAS_INITIALIZER_LISTS + this->value[0] = col_type(m[0]); + this->value[1] = col_type(m[1]); + this->value[2] = col_type(0, 0, 1); +# endif + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<3, 3, T, Q>::mat(mat<3, 2, T, Q> const& m) +# if GLM_HAS_INITIALIZER_LISTS + : value{col_type(m[0], 0), col_type(m[1], 0), col_type(m[2], 1)} +# endif + { +# if !GLM_HAS_INITIALIZER_LISTS + this->value[0] = col_type(m[0], 0); + this->value[1] = col_type(m[1], 0); + this->value[2] = col_type(m[2], 1); +# endif + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<3, 3, T, Q>::mat(mat<2, 4, T, Q> const& m) +# if GLM_HAS_INITIALIZER_LISTS + : value{col_type(m[0]), col_type(m[1]), col_type(0, 0, 1)} +# endif + { +# if !GLM_HAS_INITIALIZER_LISTS + this->value[0] = col_type(m[0]); + this->value[1] = col_type(m[1]); + this->value[2] = col_type(0, 0, 1); +# endif + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<3, 3, T, Q>::mat(mat<4, 2, T, Q> const& m) +# if GLM_HAS_INITIALIZER_LISTS + : value{col_type(m[0], 0), col_type(m[1], 0), col_type(m[2], 1)} +# endif + { +# if !GLM_HAS_INITIALIZER_LISTS + this->value[0] = col_type(m[0], 0); + this->value[1] = col_type(m[1], 0); + this->value[2] = col_type(m[2], 1); +# endif + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<3, 3, T, Q>::mat(mat<3, 4, T, Q> const& m) +# if GLM_HAS_INITIALIZER_LISTS + : value{col_type(m[0]), col_type(m[1]), col_type(m[2])} +# endif + { +# if !GLM_HAS_INITIALIZER_LISTS + this->value[0] = col_type(m[0]); + this->value[1] = col_type(m[1]); + this->value[2] = col_type(m[2]); +# endif + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<3, 3, T, Q>::mat(mat<4, 3, T, Q> const& m) +# if GLM_HAS_INITIALIZER_LISTS + : value{col_type(m[0]), col_type(m[1]), col_type(m[2])} +# endif + { +# if !GLM_HAS_INITIALIZER_LISTS + this->value[0] = col_type(m[0]); + this->value[1] = col_type(m[1]); + this->value[2] = col_type(m[2]); +# endif + } + + // -- Accesses -- + + template + GLM_FUNC_QUALIFIER typename mat<3, 3, T, Q>::col_type & mat<3, 3, T, Q>::operator[](typename mat<3, 3, T, Q>::length_type i) + { + assert(i < this->length()); + return this->value[i]; + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR typename mat<3, 3, T, Q>::col_type const& mat<3, 3, T, Q>::operator[](typename mat<3, 3, T, Q>::length_type i) const + { + assert(i < this->length()); + return this->value[i]; + } + + // -- Unary updatable operators -- + + template + template + GLM_FUNC_QUALIFIER mat<3, 3, T, Q> & mat<3, 3, T, Q>::operator=(mat<3, 3, U, Q> const& m) + { + this->value[0] = m[0]; + this->value[1] = m[1]; + this->value[2] = m[2]; + return *this; + } + + template + template + GLM_FUNC_QUALIFIER mat<3, 3, T, Q> & mat<3, 3, T, Q>::operator+=(U s) + { + this->value[0] += s; + this->value[1] += s; + this->value[2] += s; + return *this; + } + + template + template + GLM_FUNC_QUALIFIER mat<3, 3, T, Q> & mat<3, 3, T, Q>::operator+=(mat<3, 3, U, Q> const& m) + { + this->value[0] += m[0]; + this->value[1] += m[1]; + this->value[2] += m[2]; + return *this; + } + + template + template + GLM_FUNC_QUALIFIER mat<3, 3, T, Q> & mat<3, 3, T, Q>::operator-=(U s) + { + this->value[0] -= s; + this->value[1] -= s; + this->value[2] -= s; + return *this; + } + + template + template + GLM_FUNC_QUALIFIER mat<3, 3, T, Q> & mat<3, 3, T, Q>::operator-=(mat<3, 3, U, Q> const& m) + { + this->value[0] -= m[0]; + this->value[1] -= m[1]; + this->value[2] -= m[2]; + return *this; + } + + template + template + GLM_FUNC_QUALIFIER mat<3, 3, T, Q> & mat<3, 3, T, Q>::operator*=(U s) + { + this->value[0] *= s; + this->value[1] *= s; + this->value[2] *= s; + return *this; + } + + template + template + GLM_FUNC_QUALIFIER mat<3, 3, T, Q> & mat<3, 3, T, Q>::operator*=(mat<3, 3, U, Q> const& m) + { + return (*this = *this * m); + } + + template + template + GLM_FUNC_QUALIFIER mat<3, 3, T, Q> & mat<3, 3, T, Q>::operator/=(U s) + { + this->value[0] /= s; + this->value[1] /= s; + this->value[2] /= s; + return *this; + } + + template + template + GLM_FUNC_QUALIFIER mat<3, 3, T, Q> & mat<3, 3, T, Q>::operator/=(mat<3, 3, U, Q> const& m) + { + return *this *= inverse(m); + } + + // -- Increment and decrement operators -- + + template + GLM_FUNC_QUALIFIER mat<3, 3, T, Q> & mat<3, 3, T, Q>::operator++() + { + ++this->value[0]; + ++this->value[1]; + ++this->value[2]; + return *this; + } + + template + GLM_FUNC_QUALIFIER mat<3, 3, T, Q> & mat<3, 3, T, Q>::operator--() + { + --this->value[0]; + --this->value[1]; + --this->value[2]; + return *this; + } + + template + GLM_FUNC_QUALIFIER mat<3, 3, T, Q> mat<3, 3, T, Q>::operator++(int) + { + mat<3, 3, T, Q> Result(*this); + ++*this; + return Result; + } + + template + GLM_FUNC_QUALIFIER mat<3, 3, T, Q> mat<3, 3, T, Q>::operator--(int) + { + mat<3, 3, T, Q> Result(*this); + --*this; + return Result; + } + + // -- Unary arithmetic operators -- + + template + GLM_FUNC_QUALIFIER mat<3, 3, T, Q> operator+(mat<3, 3, T, Q> const& m) + { + return m; + } + + template + GLM_FUNC_QUALIFIER mat<3, 3, T, Q> operator-(mat<3, 3, T, Q> const& m) + { + return mat<3, 3, T, Q>( + -m[0], + -m[1], + -m[2]); + } + + // -- Binary arithmetic operators -- + + template + GLM_FUNC_QUALIFIER mat<3, 3, T, Q> operator+(mat<3, 3, T, Q> const& m, T scalar) + { + return mat<3, 3, T, Q>( + m[0] + scalar, + m[1] + scalar, + m[2] + scalar); + } + + template + GLM_FUNC_QUALIFIER mat<3, 3, T, Q> operator+(T scalar, mat<3, 3, T, Q> const& m) + { + return mat<3, 3, T, Q>( + m[0] + scalar, + m[1] + scalar, + m[2] + scalar); + } + + template + GLM_FUNC_QUALIFIER mat<3, 3, T, Q> operator+(mat<3, 3, T, Q> const& m1, mat<3, 3, T, Q> const& m2) + { + return mat<3, 3, T, Q>( + m1[0] + m2[0], + m1[1] + m2[1], + m1[2] + m2[2]); + } + + template + GLM_FUNC_QUALIFIER mat<3, 3, T, Q> operator-(mat<3, 3, T, Q> const& m, T scalar) + { + return mat<3, 3, T, Q>( + m[0] - scalar, + m[1] - scalar, + m[2] - scalar); + } + + template + GLM_FUNC_QUALIFIER mat<3, 3, T, Q> operator-(T scalar, mat<3, 3, T, Q> const& m) + { + return mat<3, 3, T, Q>( + scalar - m[0], + scalar - m[1], + scalar - m[2]); + } + + template + GLM_FUNC_QUALIFIER mat<3, 3, T, Q> operator-(mat<3, 3, T, Q> const& m1, mat<3, 3, T, Q> const& m2) + { + return mat<3, 3, T, Q>( + m1[0] - m2[0], + m1[1] - m2[1], + m1[2] - m2[2]); + } + + template + GLM_FUNC_QUALIFIER mat<3, 3, T, Q> operator*(mat<3, 3, T, Q> const& m, T scalar) + { + return mat<3, 3, T, Q>( + m[0] * scalar, + m[1] * scalar, + m[2] * scalar); + } + + template + GLM_FUNC_QUALIFIER mat<3, 3, T, Q> operator*(T scalar, mat<3, 3, T, Q> const& m) + { + return mat<3, 3, T, Q>( + m[0] * scalar, + m[1] * scalar, + m[2] * scalar); + } + + template + GLM_FUNC_QUALIFIER typename mat<3, 3, T, Q>::col_type operator*(mat<3, 3, T, Q> const& m, typename mat<3, 3, T, Q>::row_type const& v) + { + return typename mat<3, 3, T, Q>::col_type( + m[0][0] * v.x + m[1][0] * v.y + m[2][0] * v.z, + m[0][1] * v.x + m[1][1] * v.y + m[2][1] * v.z, + m[0][2] * v.x + m[1][2] * v.y + m[2][2] * v.z); + } + + template + GLM_FUNC_QUALIFIER typename mat<3, 3, T, Q>::row_type operator*(typename mat<3, 3, T, Q>::col_type const& v, mat<3, 3, T, Q> const& m) + { + return typename mat<3, 3, T, Q>::row_type( + m[0][0] * v.x + m[0][1] * v.y + m[0][2] * v.z, + m[1][0] * v.x + m[1][1] * v.y + m[1][2] * v.z, + m[2][0] * v.x + m[2][1] * v.y + m[2][2] * v.z); + } + + template + GLM_FUNC_QUALIFIER mat<3, 3, T, Q> operator*(mat<3, 3, T, Q> const& m1, mat<3, 3, T, Q> const& m2) + { + T const SrcA00 = m1[0][0]; + T const SrcA01 = m1[0][1]; + T const SrcA02 = m1[0][2]; + T const SrcA10 = m1[1][0]; + T const SrcA11 = m1[1][1]; + T const SrcA12 = m1[1][2]; + T const SrcA20 = m1[2][0]; + T const SrcA21 = m1[2][1]; + T const SrcA22 = m1[2][2]; + + T const SrcB00 = m2[0][0]; + T const SrcB01 = m2[0][1]; + T const SrcB02 = m2[0][2]; + T const SrcB10 = m2[1][0]; + T const SrcB11 = m2[1][1]; + T const SrcB12 = m2[1][2]; + T const SrcB20 = m2[2][0]; + T const SrcB21 = m2[2][1]; + T const SrcB22 = m2[2][2]; + + mat<3, 3, T, Q> Result; + Result[0][0] = SrcA00 * SrcB00 + SrcA10 * SrcB01 + SrcA20 * SrcB02; + Result[0][1] = SrcA01 * SrcB00 + SrcA11 * SrcB01 + SrcA21 * SrcB02; + Result[0][2] = SrcA02 * SrcB00 + SrcA12 * SrcB01 + SrcA22 * SrcB02; + Result[1][0] = SrcA00 * SrcB10 + SrcA10 * SrcB11 + SrcA20 * SrcB12; + Result[1][1] = SrcA01 * SrcB10 + SrcA11 * SrcB11 + SrcA21 * SrcB12; + Result[1][2] = SrcA02 * SrcB10 + SrcA12 * SrcB11 + SrcA22 * SrcB12; + Result[2][0] = SrcA00 * SrcB20 + SrcA10 * SrcB21 + SrcA20 * SrcB22; + Result[2][1] = SrcA01 * SrcB20 + SrcA11 * SrcB21 + SrcA21 * SrcB22; + Result[2][2] = SrcA02 * SrcB20 + SrcA12 * SrcB21 + SrcA22 * SrcB22; + return Result; + } + + template + GLM_FUNC_QUALIFIER mat<2, 3, T, Q> operator*(mat<3, 3, T, Q> const& m1, mat<2, 3, T, Q> const& m2) + { + return mat<2, 3, T, Q>( + m1[0][0] * m2[0][0] + m1[1][0] * m2[0][1] + m1[2][0] * m2[0][2], + m1[0][1] * m2[0][0] + m1[1][1] * m2[0][1] + m1[2][1] * m2[0][2], + m1[0][2] * m2[0][0] + m1[1][2] * m2[0][1] + m1[2][2] * m2[0][2], + m1[0][0] * m2[1][0] + m1[1][0] * m2[1][1] + m1[2][0] * m2[1][2], + m1[0][1] * m2[1][0] + m1[1][1] * m2[1][1] + m1[2][1] * m2[1][2], + m1[0][2] * m2[1][0] + m1[1][2] * m2[1][1] + m1[2][2] * m2[1][2]); + } + + template + GLM_FUNC_QUALIFIER mat<4, 3, T, Q> operator*(mat<3, 3, T, Q> const& m1, mat<4, 3, T, Q> const& m2) + { + return mat<4, 3, T, Q>( + m1[0][0] * m2[0][0] + m1[1][0] * m2[0][1] + m1[2][0] * m2[0][2], + m1[0][1] * m2[0][0] + m1[1][1] * m2[0][1] + m1[2][1] * m2[0][2], + m1[0][2] * m2[0][0] + m1[1][2] * m2[0][1] + m1[2][2] * m2[0][2], + m1[0][0] * m2[1][0] + m1[1][0] * m2[1][1] + m1[2][0] * m2[1][2], + m1[0][1] * m2[1][0] + m1[1][1] * m2[1][1] + m1[2][1] * m2[1][2], + m1[0][2] * m2[1][0] + m1[1][2] * m2[1][1] + m1[2][2] * m2[1][2], + m1[0][0] * m2[2][0] + m1[1][0] * m2[2][1] + m1[2][0] * m2[2][2], + m1[0][1] * m2[2][0] + m1[1][1] * m2[2][1] + m1[2][1] * m2[2][2], + m1[0][2] * m2[2][0] + m1[1][2] * m2[2][1] + m1[2][2] * m2[2][2], + m1[0][0] * m2[3][0] + m1[1][0] * m2[3][1] + m1[2][0] * m2[3][2], + m1[0][1] * m2[3][0] + m1[1][1] * m2[3][1] + m1[2][1] * m2[3][2], + m1[0][2] * m2[3][0] + m1[1][2] * m2[3][1] + m1[2][2] * m2[3][2]); + } + + template + GLM_FUNC_QUALIFIER mat<3, 3, T, Q> operator/(mat<3, 3, T, Q> const& m, T scalar) + { + return mat<3, 3, T, Q>( + m[0] / scalar, + m[1] / scalar, + m[2] / scalar); + } + + template + GLM_FUNC_QUALIFIER mat<3, 3, T, Q> operator/(T scalar, mat<3, 3, T, Q> const& m) + { + return mat<3, 3, T, Q>( + scalar / m[0], + scalar / m[1], + scalar / m[2]); + } + + template + GLM_FUNC_QUALIFIER typename mat<3, 3, T, Q>::col_type operator/(mat<3, 3, T, Q> const& m, typename mat<3, 3, T, Q>::row_type const& v) + { + return inverse(m) * v; + } + + template + GLM_FUNC_QUALIFIER typename mat<3, 3, T, Q>::row_type operator/(typename mat<3, 3, T, Q>::col_type const& v, mat<3, 3, T, Q> const& m) + { + return v * inverse(m); + } + + template + GLM_FUNC_QUALIFIER mat<3, 3, T, Q> operator/(mat<3, 3, T, Q> const& m1, mat<3, 3, T, Q> const& m2) + { + mat<3, 3, T, Q> m1_copy(m1); + return m1_copy /= m2; + } + + // -- Boolean operators -- + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR bool operator==(mat<3, 3, T, Q> const& m1, mat<3, 3, T, Q> const& m2) + { + return (m1[0] == m2[0]) && (m1[1] == m2[1]) && (m1[2] == m2[2]); + } + + template + GLM_FUNC_QUALIFIER bool operator!=(mat<3, 3, T, Q> const& m1, mat<3, 3, T, Q> const& m2) + { + return (m1[0] != m2[0]) || (m1[1] != m2[1]) || (m1[2] != m2[2]); + } +} //namespace glm diff --git a/src/GLMath/glm/detail/type_mat3x4.hpp b/src/GLMath/glm/detail/type_mat3x4.hpp new file mode 100644 index 000000000..6e40b9030 --- /dev/null +++ b/src/GLMath/glm/detail/type_mat3x4.hpp @@ -0,0 +1,166 @@ +/// @ref core +/// @file glm/detail/type_mat3x4.hpp + +#pragma once + +#include "type_vec3.hpp" +#include "type_vec4.hpp" +#include +#include + +namespace glm +{ + template + struct mat<3, 4, T, Q> + { + typedef vec<4, T, Q> col_type; + typedef vec<3, T, Q> row_type; + typedef mat<3, 4, T, Q> type; + typedef mat<4, 3, T, Q> transpose_type; + typedef T value_type; + + private: + col_type value[3]; + + public: + // -- Accesses -- + + typedef length_t length_type; + GLM_FUNC_DECL static GLM_CONSTEXPR length_type length() { return 3; } + + GLM_FUNC_DECL col_type & operator[](length_type i); + GLM_FUNC_DECL GLM_CONSTEXPR col_type const& operator[](length_type i) const; + + // -- Constructors -- + + GLM_FUNC_DECL GLM_CONSTEXPR mat() GLM_DEFAULT; + template + GLM_FUNC_DECL GLM_CONSTEXPR mat(mat<3, 4, T, P> const& m); + + GLM_FUNC_DECL explicit GLM_CONSTEXPR mat(T scalar); + GLM_FUNC_DECL GLM_CONSTEXPR mat( + T x0, T y0, T z0, T w0, + T x1, T y1, T z1, T w1, + T x2, T y2, T z2, T w2); + GLM_FUNC_DECL GLM_CONSTEXPR mat( + col_type const& v0, + col_type const& v1, + col_type const& v2); + + // -- Conversions -- + + template< + typename X1, typename Y1, typename Z1, typename W1, + typename X2, typename Y2, typename Z2, typename W2, + typename X3, typename Y3, typename Z3, typename W3> + GLM_FUNC_DECL GLM_CONSTEXPR mat( + X1 x1, Y1 y1, Z1 z1, W1 w1, + X2 x2, Y2 y2, Z2 z2, W2 w2, + X3 x3, Y3 y3, Z3 z3, W3 w3); + + template + GLM_FUNC_DECL GLM_CONSTEXPR mat( + vec<4, V1, Q> const& v1, + vec<4, V2, Q> const& v2, + vec<4, V3, Q> const& v3); + + // -- Matrix conversions -- + + template + GLM_FUNC_DECL GLM_EXPLICIT GLM_CONSTEXPR mat(mat<3, 4, U, P> const& m); + + GLM_FUNC_DECL GLM_EXPLICIT GLM_CONSTEXPR mat(mat<2, 2, T, Q> const& x); + GLM_FUNC_DECL GLM_EXPLICIT GLM_CONSTEXPR mat(mat<3, 3, T, Q> const& x); + GLM_FUNC_DECL GLM_EXPLICIT GLM_CONSTEXPR mat(mat<4, 4, T, Q> const& x); + GLM_FUNC_DECL GLM_EXPLICIT GLM_CONSTEXPR mat(mat<2, 3, T, Q> const& x); + GLM_FUNC_DECL GLM_EXPLICIT GLM_CONSTEXPR mat(mat<3, 2, T, Q> const& x); + GLM_FUNC_DECL GLM_EXPLICIT GLM_CONSTEXPR mat(mat<2, 4, T, Q> const& x); + GLM_FUNC_DECL GLM_EXPLICIT GLM_CONSTEXPR mat(mat<4, 2, T, Q> const& x); + GLM_FUNC_DECL GLM_EXPLICIT GLM_CONSTEXPR mat(mat<4, 3, T, Q> const& x); + + // -- Unary arithmetic operators -- + + template + GLM_FUNC_DECL mat<3, 4, T, Q> & operator=(mat<3, 4, U, Q> const& m); + template + GLM_FUNC_DECL mat<3, 4, T, Q> & operator+=(U s); + template + GLM_FUNC_DECL mat<3, 4, T, Q> & operator+=(mat<3, 4, U, Q> const& m); + template + GLM_FUNC_DECL mat<3, 4, T, Q> & operator-=(U s); + template + GLM_FUNC_DECL mat<3, 4, T, Q> & operator-=(mat<3, 4, U, Q> const& m); + template + GLM_FUNC_DECL mat<3, 4, T, Q> & operator*=(U s); + template + GLM_FUNC_DECL mat<3, 4, T, Q> & operator/=(U s); + + // -- Increment and decrement operators -- + + GLM_FUNC_DECL mat<3, 4, T, Q> & operator++(); + GLM_FUNC_DECL mat<3, 4, T, Q> & operator--(); + GLM_FUNC_DECL mat<3, 4, T, Q> operator++(int); + GLM_FUNC_DECL mat<3, 4, T, Q> operator--(int); + }; + + // -- Unary operators -- + + template + GLM_FUNC_DECL mat<3, 4, T, Q> operator+(mat<3, 4, T, Q> const& m); + + template + GLM_FUNC_DECL mat<3, 4, T, Q> operator-(mat<3, 4, T, Q> const& m); + + // -- Binary operators -- + + template + GLM_FUNC_DECL mat<3, 4, T, Q> operator+(mat<3, 4, T, Q> const& m, T scalar); + + template + GLM_FUNC_DECL mat<3, 4, T, Q> operator+(mat<3, 4, T, Q> const& m1, mat<3, 4, T, Q> const& m2); + + template + GLM_FUNC_DECL mat<3, 4, T, Q> operator-(mat<3, 4, T, Q> const& m, T scalar); + + template + GLM_FUNC_DECL mat<3, 4, T, Q> operator-(mat<3, 4, T, Q> const& m1, mat<3, 4, T, Q> const& m2); + + template + GLM_FUNC_DECL mat<3, 4, T, Q> operator*(mat<3, 4, T, Q> const& m, T scalar); + + template + GLM_FUNC_DECL mat<3, 4, T, Q> operator*(T scalar, mat<3, 4, T, Q> const& m); + + template + GLM_FUNC_DECL typename mat<3, 4, T, Q>::col_type operator*(mat<3, 4, T, Q> const& m, typename mat<3, 4, T, Q>::row_type const& v); + + template + GLM_FUNC_DECL typename mat<3, 4, T, Q>::row_type operator*(typename mat<3, 4, T, Q>::col_type const& v, mat<3, 4, T, Q> const& m); + + template + GLM_FUNC_DECL mat<4, 4, T, Q> operator*(mat<3, 4, T, Q> const& m1, mat<4, 3, T, Q> const& m2); + + template + GLM_FUNC_DECL mat<2, 4, T, Q> operator*(mat<3, 4, T, Q> const& m1, mat<2, 3, T, Q> const& m2); + + template + GLM_FUNC_DECL mat<3, 4, T, Q> operator*(mat<3, 4, T, Q> const& m1, mat<3, 3, T, Q> const& m2); + + template + GLM_FUNC_DECL mat<3, 4, T, Q> operator/(mat<3, 4, T, Q> const& m, T scalar); + + template + GLM_FUNC_DECL mat<3, 4, T, Q> operator/(T scalar, mat<3, 4, T, Q> const& m); + + // -- Boolean operators -- + + template + GLM_FUNC_DECL bool operator==(mat<3, 4, T, Q> const& m1, mat<3, 4, T, Q> const& m2); + + template + GLM_FUNC_DECL bool operator!=(mat<3, 4, T, Q> const& m1, mat<3, 4, T, Q> const& m2); +}//namespace glm + +#ifndef GLM_EXTERNAL_TEMPLATE +#include "type_mat3x4.inl" +#endif diff --git a/src/GLMath/glm/detail/type_mat3x4.inl b/src/GLMath/glm/detail/type_mat3x4.inl new file mode 100644 index 000000000..6ee416cfd --- /dev/null +++ b/src/GLMath/glm/detail/type_mat3x4.inl @@ -0,0 +1,578 @@ +namespace glm +{ + // -- Constructors -- + +# if GLM_CONFIG_DEFAULTED_FUNCTIONS == GLM_DISABLE + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<3, 4, T, Q>::mat() +# if GLM_CONFIG_CTOR_INIT == GLM_CTOR_INITIALIZER_LIST + : value{col_type(1, 0, 0, 0), col_type(0, 1, 0, 0), col_type(0, 0, 1, 0)} +# endif + { +# if GLM_CONFIG_CTOR_INIT == GLM_CTOR_INITIALISATION + this->value[0] = col_type(1, 0, 0, 0); + this->value[1] = col_type(0, 1, 0, 0); + this->value[2] = col_type(0, 0, 1, 0); +# endif + } +# endif + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<3, 4, T, Q>::mat(mat<3, 4, T, P> const& m) +# if GLM_HAS_INITIALIZER_LISTS + : value{col_type(m[0]), col_type(m[1]), col_type(m[2])} +# endif + { +# if !GLM_HAS_INITIALIZER_LISTS + this->value[0] = m[0]; + this->value[1] = m[1]; + this->value[2] = m[2]; +# endif + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<3, 4, T, Q>::mat(T s) +# if GLM_HAS_INITIALIZER_LISTS + : value{col_type(s, 0, 0, 0), col_type(0, s, 0, 0), col_type(0, 0, s, 0)} +# endif + { +# if !GLM_HAS_INITIALIZER_LISTS + this->value[0] = col_type(s, 0, 0, 0); + this->value[1] = col_type(0, s, 0, 0); + this->value[2] = col_type(0, 0, s, 0); +# endif + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<3, 4, T, Q>::mat + ( + T x0, T y0, T z0, T w0, + T x1, T y1, T z1, T w1, + T x2, T y2, T z2, T w2 + ) +# if GLM_HAS_INITIALIZER_LISTS + : value{ + col_type(x0, y0, z0, w0), + col_type(x1, y1, z1, w1), + col_type(x2, y2, z2, w2)} +# endif + { +# if !GLM_HAS_INITIALIZER_LISTS + this->value[0] = col_type(x0, y0, z0, w0); + this->value[1] = col_type(x1, y1, z1, w1); + this->value[2] = col_type(x2, y2, z2, w2); +# endif + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<3, 4, T, Q>::mat(col_type const& v0, col_type const& v1, col_type const& v2) +# if GLM_HAS_INITIALIZER_LISTS + : value{col_type(v0), col_type(v1), col_type(v2)} +# endif + { +# if !GLM_HAS_INITIALIZER_LISTS + this->value[0] = v0; + this->value[1] = v1; + this->value[2] = v2; +# endif + } + + // -- Conversion constructors -- + + template + template< + typename X0, typename Y0, typename Z0, typename W0, + typename X1, typename Y1, typename Z1, typename W1, + typename X2, typename Y2, typename Z2, typename W2> + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<3, 4, T, Q>::mat + ( + X0 x0, Y0 y0, Z0 z0, W0 w0, + X1 x1, Y1 y1, Z1 z1, W1 w1, + X2 x2, Y2 y2, Z2 z2, W2 w2 + ) +# if GLM_HAS_INITIALIZER_LISTS + : value{ + col_type(x0, y0, z0, w0), + col_type(x1, y1, z1, w1), + col_type(x2, y2, z2, w2)} +# endif + { +# if !GLM_HAS_INITIALIZER_LISTS + this->value[0] = col_type(x0, y0, z0, w0); + this->value[1] = col_type(x1, y1, z1, w1); + this->value[2] = col_type(x2, y2, z2, w2); +# endif + } + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<3, 4, T, Q>::mat(vec<4, V1, Q> const& v0, vec<4, V2, Q> const& v1, vec<4, V3, Q> const& v2) +# if GLM_HAS_INITIALIZER_LISTS + : value{col_type(v0), col_type(v1), col_type(v2)} +# endif + { +# if !GLM_HAS_INITIALIZER_LISTS + this->value[0] = col_type(v0); + this->value[1] = col_type(v1); + this->value[2] = col_type(v2); +# endif + } + + // -- Matrix conversions -- + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<3, 4, T, Q>::mat(mat<3, 4, U, P> const& m) +# if GLM_HAS_INITIALIZER_LISTS + : value{col_type(m[0]), col_type(m[1]), col_type(m[2])} +# endif + { +# if !GLM_HAS_INITIALIZER_LISTS + this->value[0] = col_type(m[0]); + this->value[1] = col_type(m[1]); + this->value[2] = col_type(m[2]); +# endif + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<3, 4, T, Q>::mat(mat<2, 2, T, Q> const& m) +# if GLM_HAS_INITIALIZER_LISTS + : value{col_type(m[0], 0, 0), col_type(m[1], 0, 0), col_type(0, 0, 1, 0)} +# endif + { +# if !GLM_HAS_INITIALIZER_LISTS + this->value[0] = col_type(m[0], 0, 0); + this->value[1] = col_type(m[1], 0, 0); + this->value[2] = col_type(0, 0, 1, 0); +# endif + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<3, 4, T, Q>::mat(mat<3, 3, T, Q> const& m) +# if GLM_HAS_INITIALIZER_LISTS + : value{col_type(m[0], 0), col_type(m[1], 0), col_type(m[2], 0)} +# endif + { +# if !GLM_HAS_INITIALIZER_LISTS + this->value[0] = col_type(m[0], 0); + this->value[1] = col_type(m[1], 0); + this->value[2] = col_type(m[2], 0); +# endif + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<3, 4, T, Q>::mat(mat<4, 4, T, Q> const& m) +# if GLM_HAS_INITIALIZER_LISTS + : value{col_type(m[0]), col_type(m[1]), col_type(m[2])} +# endif + { +# if !GLM_HAS_INITIALIZER_LISTS + this->value[0] = col_type(m[0]); + this->value[1] = col_type(m[1]); + this->value[2] = col_type(m[2]); +# endif + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<3, 4, T, Q>::mat(mat<2, 3, T, Q> const& m) +# if GLM_HAS_INITIALIZER_LISTS + : value{col_type(m[0], 0), col_type(m[1], 0), col_type(0, 0, 1, 0)} +# endif + { +# if !GLM_HAS_INITIALIZER_LISTS + this->value[0] = col_type(m[0], 0); + this->value[1] = col_type(m[1], 0); + this->value[2] = col_type(0, 0, 1, 0); +# endif + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<3, 4, T, Q>::mat(mat<3, 2, T, Q> const& m) +# if GLM_HAS_INITIALIZER_LISTS + : value{col_type(m[0], 0, 0), col_type(m[1], 0, 0), col_type(m[2], 1, 0)} +# endif + { +# if !GLM_HAS_INITIALIZER_LISTS + this->value[0] = col_type(m[0], 0, 0); + this->value[1] = col_type(m[1], 0, 0); + this->value[2] = col_type(m[2], 1, 0); +# endif + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<3, 4, T, Q>::mat(mat<2, 4, T, Q> const& m) +# if GLM_HAS_INITIALIZER_LISTS + : value{col_type(m[0]), col_type(m[1]), col_type(0, 0, 1, 0)} +# endif + { +# if !GLM_HAS_INITIALIZER_LISTS + this->value[0] = col_type(m[0]); + this->value[1] = col_type(m[1]); + this->value[2] = col_type(0, 0, 1, 0); +# endif + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<3, 4, T, Q>::mat(mat<4, 2, T, Q> const& m) +# if GLM_HAS_INITIALIZER_LISTS + : value{col_type(m[0], 0, 0), col_type(m[1], 0, 0), col_type(m[2], 1, 0)} +# endif + { +# if !GLM_HAS_INITIALIZER_LISTS + this->value[0] = col_type(m[0], 0, 0); + this->value[1] = col_type(m[1], 0, 0); + this->value[2] = col_type(m[2], 1, 0); +# endif + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<3, 4, T, Q>::mat(mat<4, 3, T, Q> const& m) +# if GLM_HAS_INITIALIZER_LISTS + : value{col_type(m[0], 0), col_type(m[1], 0), col_type(m[2], 0)} +# endif + { +# if !GLM_HAS_INITIALIZER_LISTS + this->value[0] = col_type(m[0], 0); + this->value[1] = col_type(m[1], 0); + this->value[2] = col_type(m[2], 0); +# endif + } + + // -- Accesses -- + + template + GLM_FUNC_QUALIFIER typename mat<3, 4, T, Q>::col_type & mat<3, 4, T, Q>::operator[](typename mat<3, 4, T, Q>::length_type i) + { + assert(i < this->length()); + return this->value[i]; + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR typename mat<3, 4, T, Q>::col_type const& mat<3, 4, T, Q>::operator[](typename mat<3, 4, T, Q>::length_type i) const + { + assert(i < this->length()); + return this->value[i]; + } + + // -- Unary updatable operators -- + + template + template + GLM_FUNC_QUALIFIER mat<3, 4, T, Q>& mat<3, 4, T, Q>::operator=(mat<3, 4, U, Q> const& m) + { + this->value[0] = m[0]; + this->value[1] = m[1]; + this->value[2] = m[2]; + return *this; + } + + template + template + GLM_FUNC_QUALIFIER mat<3, 4, T, Q>& mat<3, 4, T, Q>::operator+=(U s) + { + this->value[0] += s; + this->value[1] += s; + this->value[2] += s; + return *this; + } + + template + template + GLM_FUNC_QUALIFIER mat<3, 4, T, Q>& mat<3, 4, T, Q>::operator+=(mat<3, 4, U, Q> const& m) + { + this->value[0] += m[0]; + this->value[1] += m[1]; + this->value[2] += m[2]; + return *this; + } + + template + template + GLM_FUNC_QUALIFIER mat<3, 4, T, Q>& mat<3, 4, T, Q>::operator-=(U s) + { + this->value[0] -= s; + this->value[1] -= s; + this->value[2] -= s; + return *this; + } + + template + template + GLM_FUNC_QUALIFIER mat<3, 4, T, Q>& mat<3, 4, T, Q>::operator-=(mat<3, 4, U, Q> const& m) + { + this->value[0] -= m[0]; + this->value[1] -= m[1]; + this->value[2] -= m[2]; + return *this; + } + + template + template + GLM_FUNC_QUALIFIER mat<3, 4, T, Q>& mat<3, 4, T, Q>::operator*=(U s) + { + this->value[0] *= s; + this->value[1] *= s; + this->value[2] *= s; + return *this; + } + + template + template + GLM_FUNC_QUALIFIER mat<3, 4, T, Q> & mat<3, 4, T, Q>::operator/=(U s) + { + this->value[0] /= s; + this->value[1] /= s; + this->value[2] /= s; + return *this; + } + + // -- Increment and decrement operators -- + + template + GLM_FUNC_QUALIFIER mat<3, 4, T, Q>& mat<3, 4, T, Q>::operator++() + { + ++this->value[0]; + ++this->value[1]; + ++this->value[2]; + return *this; + } + + template + GLM_FUNC_QUALIFIER mat<3, 4, T, Q>& mat<3, 4, T, Q>::operator--() + { + --this->value[0]; + --this->value[1]; + --this->value[2]; + return *this; + } + + template + GLM_FUNC_QUALIFIER mat<3, 4, T, Q> mat<3, 4, T, Q>::operator++(int) + { + mat<3, 4, T, Q> Result(*this); + ++*this; + return Result; + } + + template + GLM_FUNC_QUALIFIER mat<3, 4, T, Q> mat<3, 4, T, Q>::operator--(int) + { + mat<3, 4, T, Q> Result(*this); + --*this; + return Result; + } + + // -- Unary arithmetic operators -- + + template + GLM_FUNC_QUALIFIER mat<3, 4, T, Q> operator+(mat<3, 4, T, Q> const& m) + { + return m; + } + + template + GLM_FUNC_QUALIFIER mat<3, 4, T, Q> operator-(mat<3, 4, T, Q> const& m) + { + return mat<3, 4, T, Q>( + -m[0], + -m[1], + -m[2]); + } + + // -- Binary arithmetic operators -- + + template + GLM_FUNC_QUALIFIER mat<3, 4, T, Q> operator+(mat<3, 4, T, Q> const& m, T scalar) + { + return mat<3, 4, T, Q>( + m[0] + scalar, + m[1] + scalar, + m[2] + scalar); + } + + template + GLM_FUNC_QUALIFIER mat<3, 4, T, Q> operator+(mat<3, 4, T, Q> const& m1, mat<3, 4, T, Q> const& m2) + { + return mat<3, 4, T, Q>( + m1[0] + m2[0], + m1[1] + m2[1], + m1[2] + m2[2]); + } + + template + GLM_FUNC_QUALIFIER mat<3, 4, T, Q> operator-(mat<3, 4, T, Q> const& m, T scalar) + { + return mat<3, 4, T, Q>( + m[0] - scalar, + m[1] - scalar, + m[2] - scalar); + } + + template + GLM_FUNC_QUALIFIER mat<3, 4, T, Q> operator-(mat<3, 4, T, Q> const& m1, mat<3, 4, T, Q> const& m2) + { + return mat<3, 4, T, Q>( + m1[0] - m2[0], + m1[1] - m2[1], + m1[2] - m2[2]); + } + + template + GLM_FUNC_QUALIFIER mat<3, 4, T, Q> operator*(mat<3, 4, T, Q> const& m, T scalar) + { + return mat<3, 4, T, Q>( + m[0] * scalar, + m[1] * scalar, + m[2] * scalar); + } + + template + GLM_FUNC_QUALIFIER mat<3, 4, T, Q> operator*(T scalar, mat<3, 4, T, Q> const& m) + { + return mat<3, 4, T, Q>( + m[0] * scalar, + m[1] * scalar, + m[2] * scalar); + } + + template + GLM_FUNC_QUALIFIER typename mat<3, 4, T, Q>::col_type operator* + ( + mat<3, 4, T, Q> const& m, + typename mat<3, 4, T, Q>::row_type const& v + ) + { + return typename mat<3, 4, T, Q>::col_type( + m[0][0] * v.x + m[1][0] * v.y + m[2][0] * v.z, + m[0][1] * v.x + m[1][1] * v.y + m[2][1] * v.z, + m[0][2] * v.x + m[1][2] * v.y + m[2][2] * v.z, + m[0][3] * v.x + m[1][3] * v.y + m[2][3] * v.z); + } + + template + GLM_FUNC_QUALIFIER typename mat<3, 4, T, Q>::row_type operator* + ( + typename mat<3, 4, T, Q>::col_type const& v, + mat<3, 4, T, Q> const& m + ) + { + return typename mat<3, 4, T, Q>::row_type( + v.x * m[0][0] + v.y * m[0][1] + v.z * m[0][2] + v.w * m[0][3], + v.x * m[1][0] + v.y * m[1][1] + v.z * m[1][2] + v.w * m[1][3], + v.x * m[2][0] + v.y * m[2][1] + v.z * m[2][2] + v.w * m[2][3]); + } + + template + GLM_FUNC_QUALIFIER mat<4, 4, T, Q> operator*(mat<3, 4, T, Q> const& m1, mat<4, 3, T, Q> const& m2) + { + const T SrcA00 = m1[0][0]; + const T SrcA01 = m1[0][1]; + const T SrcA02 = m1[0][2]; + const T SrcA03 = m1[0][3]; + const T SrcA10 = m1[1][0]; + const T SrcA11 = m1[1][1]; + const T SrcA12 = m1[1][2]; + const T SrcA13 = m1[1][3]; + const T SrcA20 = m1[2][0]; + const T SrcA21 = m1[2][1]; + const T SrcA22 = m1[2][2]; + const T SrcA23 = m1[2][3]; + + const T SrcB00 = m2[0][0]; + const T SrcB01 = m2[0][1]; + const T SrcB02 = m2[0][2]; + const T SrcB10 = m2[1][0]; + const T SrcB11 = m2[1][1]; + const T SrcB12 = m2[1][2]; + const T SrcB20 = m2[2][0]; + const T SrcB21 = m2[2][1]; + const T SrcB22 = m2[2][2]; + const T SrcB30 = m2[3][0]; + const T SrcB31 = m2[3][1]; + const T SrcB32 = m2[3][2]; + + mat<4, 4, T, Q> Result; + Result[0][0] = SrcA00 * SrcB00 + SrcA10 * SrcB01 + SrcA20 * SrcB02; + Result[0][1] = SrcA01 * SrcB00 + SrcA11 * SrcB01 + SrcA21 * SrcB02; + Result[0][2] = SrcA02 * SrcB00 + SrcA12 * SrcB01 + SrcA22 * SrcB02; + Result[0][3] = SrcA03 * SrcB00 + SrcA13 * SrcB01 + SrcA23 * SrcB02; + Result[1][0] = SrcA00 * SrcB10 + SrcA10 * SrcB11 + SrcA20 * SrcB12; + Result[1][1] = SrcA01 * SrcB10 + SrcA11 * SrcB11 + SrcA21 * SrcB12; + Result[1][2] = SrcA02 * SrcB10 + SrcA12 * SrcB11 + SrcA22 * SrcB12; + Result[1][3] = SrcA03 * SrcB10 + SrcA13 * SrcB11 + SrcA23 * SrcB12; + Result[2][0] = SrcA00 * SrcB20 + SrcA10 * SrcB21 + SrcA20 * SrcB22; + Result[2][1] = SrcA01 * SrcB20 + SrcA11 * SrcB21 + SrcA21 * SrcB22; + Result[2][2] = SrcA02 * SrcB20 + SrcA12 * SrcB21 + SrcA22 * SrcB22; + Result[2][3] = SrcA03 * SrcB20 + SrcA13 * SrcB21 + SrcA23 * SrcB22; + Result[3][0] = SrcA00 * SrcB30 + SrcA10 * SrcB31 + SrcA20 * SrcB32; + Result[3][1] = SrcA01 * SrcB30 + SrcA11 * SrcB31 + SrcA21 * SrcB32; + Result[3][2] = SrcA02 * SrcB30 + SrcA12 * SrcB31 + SrcA22 * SrcB32; + Result[3][3] = SrcA03 * SrcB30 + SrcA13 * SrcB31 + SrcA23 * SrcB32; + return Result; + } + + template + GLM_FUNC_QUALIFIER mat<2, 4, T, Q> operator*(mat<3, 4, T, Q> const& m1, mat<2, 3, T, Q> const& m2) + { + return mat<2, 4, T, Q>( + m1[0][0] * m2[0][0] + m1[1][0] * m2[0][1] + m1[2][0] * m2[0][2], + m1[0][1] * m2[0][0] + m1[1][1] * m2[0][1] + m1[2][1] * m2[0][2], + m1[0][2] * m2[0][0] + m1[1][2] * m2[0][1] + m1[2][2] * m2[0][2], + m1[0][3] * m2[0][0] + m1[1][3] * m2[0][1] + m1[2][3] * m2[0][2], + m1[0][0] * m2[1][0] + m1[1][0] * m2[1][1] + m1[2][0] * m2[1][2], + m1[0][1] * m2[1][0] + m1[1][1] * m2[1][1] + m1[2][1] * m2[1][2], + m1[0][2] * m2[1][0] + m1[1][2] * m2[1][1] + m1[2][2] * m2[1][2], + m1[0][3] * m2[1][0] + m1[1][3] * m2[1][1] + m1[2][3] * m2[1][2]); + } + + template + GLM_FUNC_QUALIFIER mat<3, 4, T, Q> operator*(mat<3, 4, T, Q> const& m1, mat<3, 3, T, Q> const& m2) + { + return mat<3, 4, T, Q>( + m1[0][0] * m2[0][0] + m1[1][0] * m2[0][1] + m1[2][0] * m2[0][2], + m1[0][1] * m2[0][0] + m1[1][1] * m2[0][1] + m1[2][1] * m2[0][2], + m1[0][2] * m2[0][0] + m1[1][2] * m2[0][1] + m1[2][2] * m2[0][2], + m1[0][3] * m2[0][0] + m1[1][3] * m2[0][1] + m1[2][3] * m2[0][2], + m1[0][0] * m2[1][0] + m1[1][0] * m2[1][1] + m1[2][0] * m2[1][2], + m1[0][1] * m2[1][0] + m1[1][1] * m2[1][1] + m1[2][1] * m2[1][2], + m1[0][2] * m2[1][0] + m1[1][2] * m2[1][1] + m1[2][2] * m2[1][2], + m1[0][3] * m2[1][0] + m1[1][3] * m2[1][1] + m1[2][3] * m2[1][2], + m1[0][0] * m2[2][0] + m1[1][0] * m2[2][1] + m1[2][0] * m2[2][2], + m1[0][1] * m2[2][0] + m1[1][1] * m2[2][1] + m1[2][1] * m2[2][2], + m1[0][2] * m2[2][0] + m1[1][2] * m2[2][1] + m1[2][2] * m2[2][2], + m1[0][3] * m2[2][0] + m1[1][3] * m2[2][1] + m1[2][3] * m2[2][2]); + } + + template + GLM_FUNC_QUALIFIER mat<3, 4, T, Q> operator/(mat<3, 4, T, Q> const& m, T scalar) + { + return mat<3, 4, T, Q>( + m[0] / scalar, + m[1] / scalar, + m[2] / scalar); + } + + template + GLM_FUNC_QUALIFIER mat<3, 4, T, Q> operator/(T scalar, mat<3, 4, T, Q> const& m) + { + return mat<3, 4, T, Q>( + scalar / m[0], + scalar / m[1], + scalar / m[2]); + } + + // -- Boolean operators -- + + template + GLM_FUNC_QUALIFIER bool operator==(mat<3, 4, T, Q> const& m1, mat<3, 4, T, Q> const& m2) + { + return (m1[0] == m2[0]) && (m1[1] == m2[1]) && (m1[2] == m2[2]); + } + + template + GLM_FUNC_QUALIFIER bool operator!=(mat<3, 4, T, Q> const& m1, mat<3, 4, T, Q> const& m2) + { + return (m1[0] != m2[0]) || (m1[1] != m2[1]) || (m1[2] != m2[2]); + } +} //namespace glm diff --git a/src/GLMath/glm/detail/type_mat4x2.hpp b/src/GLMath/glm/detail/type_mat4x2.hpp new file mode 100644 index 000000000..8d3435271 --- /dev/null +++ b/src/GLMath/glm/detail/type_mat4x2.hpp @@ -0,0 +1,171 @@ +/// @ref core +/// @file glm/detail/type_mat4x2.hpp + +#pragma once + +#include "type_vec2.hpp" +#include "type_vec4.hpp" +#include +#include + +namespace glm +{ + template + struct mat<4, 2, T, Q> + { + typedef vec<2, T, Q> col_type; + typedef vec<4, T, Q> row_type; + typedef mat<4, 2, T, Q> type; + typedef mat<2, 4, T, Q> transpose_type; + typedef T value_type; + + private: + col_type value[4]; + + public: + // -- Accesses -- + + typedef length_t length_type; + GLM_FUNC_DECL static GLM_CONSTEXPR length_type length() { return 4; } + + GLM_FUNC_DECL col_type & operator[](length_type i); + GLM_FUNC_DECL GLM_CONSTEXPR col_type const& operator[](length_type i) const; + + // -- Constructors -- + + GLM_FUNC_DECL GLM_CONSTEXPR mat() GLM_DEFAULT; + template + GLM_FUNC_DECL GLM_CONSTEXPR mat(mat<4, 2, T, P> const& m); + + GLM_FUNC_DECL explicit GLM_CONSTEXPR mat(T scalar); + GLM_FUNC_DECL GLM_CONSTEXPR mat( + T x0, T y0, + T x1, T y1, + T x2, T y2, + T x3, T y3); + GLM_FUNC_DECL GLM_CONSTEXPR mat( + col_type const& v0, + col_type const& v1, + col_type const& v2, + col_type const& v3); + + // -- Conversions -- + + template< + typename X0, typename Y0, + typename X1, typename Y1, + typename X2, typename Y2, + typename X3, typename Y3> + GLM_FUNC_DECL GLM_CONSTEXPR mat( + X0 x0, Y0 y0, + X1 x1, Y1 y1, + X2 x2, Y2 y2, + X3 x3, Y3 y3); + + template + GLM_FUNC_DECL GLM_CONSTEXPR mat( + vec<2, V1, Q> const& v1, + vec<2, V2, Q> const& v2, + vec<2, V3, Q> const& v3, + vec<2, V4, Q> const& v4); + + // -- Matrix conversions -- + + template + GLM_FUNC_DECL GLM_EXPLICIT GLM_CONSTEXPR mat(mat<4, 2, U, P> const& m); + + GLM_FUNC_DECL GLM_EXPLICIT GLM_CONSTEXPR mat(mat<2, 2, T, Q> const& x); + GLM_FUNC_DECL GLM_EXPLICIT GLM_CONSTEXPR mat(mat<3, 3, T, Q> const& x); + GLM_FUNC_DECL GLM_EXPLICIT GLM_CONSTEXPR mat(mat<4, 4, T, Q> const& x); + GLM_FUNC_DECL GLM_EXPLICIT GLM_CONSTEXPR mat(mat<2, 3, T, Q> const& x); + GLM_FUNC_DECL GLM_EXPLICIT GLM_CONSTEXPR mat(mat<3, 2, T, Q> const& x); + GLM_FUNC_DECL GLM_EXPLICIT GLM_CONSTEXPR mat(mat<2, 4, T, Q> const& x); + GLM_FUNC_DECL GLM_EXPLICIT GLM_CONSTEXPR mat(mat<4, 3, T, Q> const& x); + GLM_FUNC_DECL GLM_EXPLICIT GLM_CONSTEXPR mat(mat<3, 4, T, Q> const& x); + + // -- Unary arithmetic operators -- + + template + GLM_FUNC_DECL mat<4, 2, T, Q> & operator=(mat<4, 2, U, Q> const& m); + template + GLM_FUNC_DECL mat<4, 2, T, Q> & operator+=(U s); + template + GLM_FUNC_DECL mat<4, 2, T, Q> & operator+=(mat<4, 2, U, Q> const& m); + template + GLM_FUNC_DECL mat<4, 2, T, Q> & operator-=(U s); + template + GLM_FUNC_DECL mat<4, 2, T, Q> & operator-=(mat<4, 2, U, Q> const& m); + template + GLM_FUNC_DECL mat<4, 2, T, Q> & operator*=(U s); + template + GLM_FUNC_DECL mat<4, 2, T, Q> & operator/=(U s); + + // -- Increment and decrement operators -- + + GLM_FUNC_DECL mat<4, 2, T, Q> & operator++ (); + GLM_FUNC_DECL mat<4, 2, T, Q> & operator-- (); + GLM_FUNC_DECL mat<4, 2, T, Q> operator++(int); + GLM_FUNC_DECL mat<4, 2, T, Q> operator--(int); + }; + + // -- Unary operators -- + + template + GLM_FUNC_DECL mat<4, 2, T, Q> operator+(mat<4, 2, T, Q> const& m); + + template + GLM_FUNC_DECL mat<4, 2, T, Q> operator-(mat<4, 2, T, Q> const& m); + + // -- Binary operators -- + + template + GLM_FUNC_DECL mat<4, 2, T, Q> operator+(mat<4, 2, T, Q> const& m, T scalar); + + template + GLM_FUNC_DECL mat<4, 2, T, Q> operator+(mat<4, 2, T, Q> const& m1, mat<4, 2, T, Q> const& m2); + + template + GLM_FUNC_DECL mat<4, 2, T, Q> operator-(mat<4, 2, T, Q> const& m, T scalar); + + template + GLM_FUNC_DECL mat<4, 2, T, Q> operator-(mat<4, 2, T, Q> const& m1, mat<4, 2, T, Q> const& m2); + + template + GLM_FUNC_DECL mat<4, 2, T, Q> operator*(mat<4, 2, T, Q> const& m, T scalar); + + template + GLM_FUNC_DECL mat<4, 2, T, Q> operator*(T scalar, mat<4, 2, T, Q> const& m); + + template + GLM_FUNC_DECL typename mat<4, 2, T, Q>::col_type operator*(mat<4, 2, T, Q> const& m, typename mat<4, 2, T, Q>::row_type const& v); + + template + GLM_FUNC_DECL typename mat<4, 2, T, Q>::row_type operator*(typename mat<4, 2, T, Q>::col_type const& v, mat<4, 2, T, Q> const& m); + + template + GLM_FUNC_DECL mat<2, 2, T, Q> operator*(mat<4, 2, T, Q> const& m1, mat<2, 4, T, Q> const& m2); + + template + GLM_FUNC_DECL mat<3, 2, T, Q> operator*(mat<4, 2, T, Q> const& m1, mat<3, 4, T, Q> const& m2); + + template + GLM_FUNC_DECL mat<4, 2, T, Q> operator*(mat<4, 2, T, Q> const& m1, mat<4, 4, T, Q> const& m2); + + template + GLM_FUNC_DECL mat<4, 2, T, Q> operator/(mat<4, 2, T, Q> const& m, T scalar); + + template + GLM_FUNC_DECL mat<4, 2, T, Q> operator/(T scalar, mat<4, 2, T, Q> const& m); + + // -- Boolean operators -- + + template + GLM_FUNC_DECL bool operator==(mat<4, 2, T, Q> const& m1, mat<4, 2, T, Q> const& m2); + + template + GLM_FUNC_DECL bool operator!=(mat<4, 2, T, Q> const& m1, mat<4, 2, T, Q> const& m2); +}//namespace glm + +#ifndef GLM_EXTERNAL_TEMPLATE +#include "type_mat4x2.inl" +#endif diff --git a/src/GLMath/glm/detail/type_mat4x2.inl b/src/GLMath/glm/detail/type_mat4x2.inl new file mode 100644 index 000000000..419c80c42 --- /dev/null +++ b/src/GLMath/glm/detail/type_mat4x2.inl @@ -0,0 +1,574 @@ +namespace glm +{ + // -- Constructors -- + +# if GLM_CONFIG_DEFAULTED_FUNCTIONS == GLM_DISABLE + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<4, 2, T, Q>::mat() +# if GLM_CONFIG_CTOR_INIT == GLM_CTOR_INITIALIZER_LIST + : value{col_type(1, 0), col_type(0, 1), col_type(0, 0), col_type(0, 0)} +# endif + { +# if GLM_CONFIG_CTOR_INIT == GLM_CTOR_INITIALISATION + this->value[0] = col_type(1, 0); + this->value[1] = col_type(0, 1); + this->value[2] = col_type(0, 0); + this->value[3] = col_type(0, 0); +# endif + } +# endif + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<4, 2, T, Q>::mat(mat<4, 2, T, P> const& m) +# if GLM_HAS_INITIALIZER_LISTS + : value{col_type(m[0]), col_type(m[1]), col_type(m[2]), col_type(m[3])} +# endif + { +# if !GLM_HAS_INITIALIZER_LISTS + this->value[0] = m[0]; + this->value[1] = m[1]; + this->value[2] = m[2]; + this->value[3] = m[3]; +# endif + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<4, 2, T, Q>::mat(T s) +# if GLM_HAS_INITIALIZER_LISTS + : value{col_type(s, 0), col_type(0, s), col_type(0, 0), col_type(0, 0)} +# endif + { +# if !GLM_HAS_INITIALIZER_LISTS + this->value[0] = col_type(s, 0); + this->value[1] = col_type(0, s); + this->value[2] = col_type(0, 0); + this->value[3] = col_type(0, 0); +# endif + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<4, 2, T, Q>::mat + ( + T x0, T y0, + T x1, T y1, + T x2, T y2, + T x3, T y3 + ) +# if GLM_HAS_INITIALIZER_LISTS + : value{col_type(x0, y0), col_type(x1, y1), col_type(x2, y2), col_type(x3, y3)} +# endif + { +# if !GLM_HAS_INITIALIZER_LISTS + this->value[0] = col_type(x0, y0); + this->value[1] = col_type(x1, y1); + this->value[2] = col_type(x2, y2); + this->value[3] = col_type(x3, y3); +# endif + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<4, 2, T, Q>::mat(col_type const& v0, col_type const& v1, col_type const& v2, col_type const& v3) +# if GLM_HAS_INITIALIZER_LISTS + : value{col_type(v0), col_type(v1), col_type(v2), col_type(v3)} +# endif + { +# if !GLM_HAS_INITIALIZER_LISTS + this->value[0] = v0; + this->value[1] = v1; + this->value[2] = v2; + this->value[3] = v3; +# endif + } + + // -- Conversion constructors -- + + template + template< + typename X0, typename Y0, + typename X1, typename Y1, + typename X2, typename Y2, + typename X3, typename Y3> + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<4, 2, T, Q>::mat + ( + X0 x0, Y0 y0, + X1 x1, Y1 y1, + X2 x2, Y2 y2, + X3 x3, Y3 y3 + ) +# if GLM_HAS_INITIALIZER_LISTS + : value{col_type(x0, y0), col_type(x1, y1), col_type(x2, y2), col_type(x3, y3)} +# endif + { +# if !GLM_HAS_INITIALIZER_LISTS + this->value[0] = col_type(x0, y0); + this->value[1] = col_type(x1, y1); + this->value[2] = col_type(x2, y2); + this->value[3] = col_type(x3, y3); +# endif + } + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<4, 2, T, Q>::mat(vec<2, V0, Q> const& v0, vec<2, V1, Q> const& v1, vec<2, V2, Q> const& v2, vec<2, V3, Q> const& v3) +# if GLM_HAS_INITIALIZER_LISTS + : value{col_type(v0), col_type(v1), col_type(v2), col_type(v3)} +# endif + { +# if !GLM_HAS_INITIALIZER_LISTS + this->value[0] = col_type(v0); + this->value[1] = col_type(v1); + this->value[2] = col_type(v2); + this->value[3] = col_type(v3); +# endif + } + + // -- Conversion -- + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<4, 2, T, Q>::mat(mat<4, 2, U, P> const& m) +# if GLM_HAS_INITIALIZER_LISTS + : value{col_type(m[0]), col_type(m[1]), col_type(m[2]), col_type(m[3])} +# endif + { +# if !GLM_HAS_INITIALIZER_LISTS + this->value[0] = col_type(m[0]); + this->value[1] = col_type(m[1]); + this->value[2] = col_type(m[2]); + this->value[3] = col_type(m[3]); +# endif + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<4, 2, T, Q>::mat(mat<2, 2, T, Q> const& m) +# if GLM_HAS_INITIALIZER_LISTS + : value{col_type(m[0]), col_type(m[1]), col_type(0), col_type(0)} +# endif + { +# if !GLM_HAS_INITIALIZER_LISTS + this->value[0] = col_type(m[0]); + this->value[1] = col_type(m[1]); + this->value[2] = col_type(0); + this->value[3] = col_type(0); +# endif + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<4, 2, T, Q>::mat(mat<3, 3, T, Q> const& m) +# if GLM_HAS_INITIALIZER_LISTS + : value{col_type(m[0]), col_type(m[1]), col_type(m[2]), col_type(0)} +# endif + { +# if !GLM_HAS_INITIALIZER_LISTS + this->value[0] = col_type(m[0]); + this->value[1] = col_type(m[1]); + this->value[2] = col_type(m[2]); + this->value[3] = col_type(0); +# endif + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<4, 2, T, Q>::mat(mat<4, 4, T, Q> const& m) +# if GLM_HAS_INITIALIZER_LISTS + : value{col_type(m[0]), col_type(m[1]), col_type(m[2]), col_type(m[3])} +# endif + { +# if !GLM_HAS_INITIALIZER_LISTS + this->value[0] = col_type(m[0]); + this->value[1] = col_type(m[1]); + this->value[2] = col_type(m[2]); + this->value[3] = col_type(m[3]); +# endif + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<4, 2, T, Q>::mat(mat<2, 3, T, Q> const& m) +# if GLM_HAS_INITIALIZER_LISTS + : value{col_type(m[0]), col_type(m[1]), col_type(0), col_type(0)} +# endif + { +# if !GLM_HAS_INITIALIZER_LISTS + this->value[0] = col_type(m[0]); + this->value[1] = col_type(m[1]); + this->value[2] = col_type(0); + this->value[3] = col_type(0); +# endif + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<4, 2, T, Q>::mat(mat<3, 2, T, Q> const& m) +# if GLM_HAS_INITIALIZER_LISTS + : value{col_type(m[0]), col_type(m[1]), col_type(m[2]), col_type(0)} +# endif + { +# if !GLM_HAS_INITIALIZER_LISTS + this->value[0] = col_type(m[0]); + this->value[1] = col_type(m[1]); + this->value[2] = col_type(m[2]); + this->value[3] = col_type(0); +# endif + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<4, 2, T, Q>::mat(mat<2, 4, T, Q> const& m) +# if GLM_HAS_INITIALIZER_LISTS + : value{col_type(m[0]), col_type(m[1]), col_type(0), col_type(0)} +# endif + { +# if !GLM_HAS_INITIALIZER_LISTS + this->value[0] = col_type(m[0]); + this->value[1] = col_type(m[1]); + this->value[2] = col_type(0); + this->value[3] = col_type(0); +# endif + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<4, 2, T, Q>::mat(mat<4, 3, T, Q> const& m) +# if GLM_HAS_INITIALIZER_LISTS + : value{col_type(m[0]), col_type(m[1]), col_type(m[2]), col_type(m[3])} +# endif + { +# if !GLM_HAS_INITIALIZER_LISTS + this->value[0] = col_type(m[0]); + this->value[1] = col_type(m[1]); + this->value[2] = col_type(m[2]); + this->value[3] = col_type(m[3]); +# endif + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<4, 2, T, Q>::mat(mat<3, 4, T, Q> const& m) +# if GLM_HAS_INITIALIZER_LISTS + : value{col_type(m[0]), col_type(m[1]), col_type(m[2]), col_type(0)} +# endif + { +# if !GLM_HAS_INITIALIZER_LISTS + this->value[0] = col_type(m[0]); + this->value[1] = col_type(m[1]); + this->value[2] = col_type(m[2]); + this->value[3] = col_type(0); +# endif + } + + // -- Accesses -- + + template + GLM_FUNC_QUALIFIER typename mat<4, 2, T, Q>::col_type & mat<4, 2, T, Q>::operator[](typename mat<4, 2, T, Q>::length_type i) + { + assert(i < this->length()); + return this->value[i]; + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR typename mat<4, 2, T, Q>::col_type const& mat<4, 2, T, Q>::operator[](typename mat<4, 2, T, Q>::length_type i) const + { + assert(i < this->length()); + return this->value[i]; + } + + // -- Unary updatable operators -- + + template + template + GLM_FUNC_QUALIFIER mat<4, 2, T, Q>& mat<4, 2, T, Q>::operator=(mat<4, 2, U, Q> const& m) + { + this->value[0] = m[0]; + this->value[1] = m[1]; + this->value[2] = m[2]; + this->value[3] = m[3]; + return *this; + } + + template + template + GLM_FUNC_QUALIFIER mat<4, 2, T, Q> & mat<4, 2, T, Q>::operator+=(U s) + { + this->value[0] += s; + this->value[1] += s; + this->value[2] += s; + this->value[3] += s; + return *this; + } + + template + template + GLM_FUNC_QUALIFIER mat<4, 2, T, Q> & mat<4, 2, T, Q>::operator+=(mat<4, 2, U, Q> const& m) + { + this->value[0] += m[0]; + this->value[1] += m[1]; + this->value[2] += m[2]; + this->value[3] += m[3]; + return *this; + } + + template + template + GLM_FUNC_QUALIFIER mat<4, 2, T, Q> & mat<4, 2, T, Q>::operator-=(U s) + { + this->value[0] -= s; + this->value[1] -= s; + this->value[2] -= s; + this->value[3] -= s; + return *this; + } + + template + template + GLM_FUNC_QUALIFIER mat<4, 2, T, Q> & mat<4, 2, T, Q>::operator-=(mat<4, 2, U, Q> const& m) + { + this->value[0] -= m[0]; + this->value[1] -= m[1]; + this->value[2] -= m[2]; + this->value[3] -= m[3]; + return *this; + } + + template + template + GLM_FUNC_QUALIFIER mat<4, 2, T, Q> & mat<4, 2, T, Q>::operator*=(U s) + { + this->value[0] *= s; + this->value[1] *= s; + this->value[2] *= s; + this->value[3] *= s; + return *this; + } + + template + template + GLM_FUNC_QUALIFIER mat<4, 2, T, Q> & mat<4, 2, T, Q>::operator/=(U s) + { + this->value[0] /= s; + this->value[1] /= s; + this->value[2] /= s; + this->value[3] /= s; + return *this; + } + + // -- Increment and decrement operators -- + + template + GLM_FUNC_QUALIFIER mat<4, 2, T, Q> & mat<4, 2, T, Q>::operator++() + { + ++this->value[0]; + ++this->value[1]; + ++this->value[2]; + ++this->value[3]; + return *this; + } + + template + GLM_FUNC_QUALIFIER mat<4, 2, T, Q> & mat<4, 2, T, Q>::operator--() + { + --this->value[0]; + --this->value[1]; + --this->value[2]; + --this->value[3]; + return *this; + } + + template + GLM_FUNC_QUALIFIER mat<4, 2, T, Q> mat<4, 2, T, Q>::operator++(int) + { + mat<4, 2, T, Q> Result(*this); + ++*this; + return Result; + } + + template + GLM_FUNC_QUALIFIER mat<4, 2, T, Q> mat<4, 2, T, Q>::operator--(int) + { + mat<4, 2, T, Q> Result(*this); + --*this; + return Result; + } + + // -- Unary arithmetic operators -- + + template + GLM_FUNC_QUALIFIER mat<4, 2, T, Q> operator+(mat<4, 2, T, Q> const& m) + { + return m; + } + + template + GLM_FUNC_QUALIFIER mat<4, 2, T, Q> operator-(mat<4, 2, T, Q> const& m) + { + return mat<4, 2, T, Q>( + -m[0], + -m[1], + -m[2], + -m[3]); + } + + // -- Binary arithmetic operators -- + + template + GLM_FUNC_QUALIFIER mat<4, 2, T, Q> operator+(mat<4, 2, T, Q> const& m, T scalar) + { + return mat<4, 2, T, Q>( + m[0] + scalar, + m[1] + scalar, + m[2] + scalar, + m[3] + scalar); + } + + template + GLM_FUNC_QUALIFIER mat<4, 2, T, Q> operator+(mat<4, 2, T, Q> const& m1, mat<4, 2, T, Q> const& m2) + { + return mat<4, 2, T, Q>( + m1[0] + m2[0], + m1[1] + m2[1], + m1[2] + m2[2], + m1[3] + m2[3]); + } + + template + GLM_FUNC_QUALIFIER mat<4, 2, T, Q> operator-(mat<4, 2, T, Q> const& m, T scalar) + { + return mat<4, 2, T, Q>( + m[0] - scalar, + m[1] - scalar, + m[2] - scalar, + m[3] - scalar); + } + + template + GLM_FUNC_QUALIFIER mat<4, 2, T, Q> operator-(mat<4, 2, T, Q> const& m1, mat<4, 2, T, Q> const& m2) + { + return mat<4, 2, T, Q>( + m1[0] - m2[0], + m1[1] - m2[1], + m1[2] - m2[2], + m1[3] - m2[3]); + } + + template + GLM_FUNC_QUALIFIER mat<4, 2, T, Q> operator*(mat<4, 2, T, Q> const& m, T scalar) + { + return mat<4, 2, T, Q>( + m[0] * scalar, + m[1] * scalar, + m[2] * scalar, + m[3] * scalar); + } + + template + GLM_FUNC_QUALIFIER mat<4, 2, T, Q> operator*(T scalar, mat<4, 2, T, Q> const& m) + { + return mat<4, 2, T, Q>( + m[0] * scalar, + m[1] * scalar, + m[2] * scalar, + m[3] * scalar); + } + + template + GLM_FUNC_QUALIFIER typename mat<4, 2, T, Q>::col_type operator*(mat<4, 2, T, Q> const& m, typename mat<4, 2, T, Q>::row_type const& v) + { + return typename mat<4, 2, T, Q>::col_type( + m[0][0] * v.x + m[1][0] * v.y + m[2][0] * v.z + m[3][0] * v.w, + m[0][1] * v.x + m[1][1] * v.y + m[2][1] * v.z + m[3][1] * v.w); + } + + template + GLM_FUNC_QUALIFIER typename mat<4, 2, T, Q>::row_type operator*(typename mat<4, 2, T, Q>::col_type const& v, mat<4, 2, T, Q> const& m) + { + return typename mat<4, 2, T, Q>::row_type( + v.x * m[0][0] + v.y * m[0][1], + v.x * m[1][0] + v.y * m[1][1], + v.x * m[2][0] + v.y * m[2][1], + v.x * m[3][0] + v.y * m[3][1]); + } + + template + GLM_FUNC_QUALIFIER mat<2, 2, T, Q> operator*(mat<4, 2, T, Q> const& m1, mat<2, 4, T, Q> const& m2) + { + T const SrcA00 = m1[0][0]; + T const SrcA01 = m1[0][1]; + T const SrcA10 = m1[1][0]; + T const SrcA11 = m1[1][1]; + T const SrcA20 = m1[2][0]; + T const SrcA21 = m1[2][1]; + T const SrcA30 = m1[3][0]; + T const SrcA31 = m1[3][1]; + + T const SrcB00 = m2[0][0]; + T const SrcB01 = m2[0][1]; + T const SrcB02 = m2[0][2]; + T const SrcB03 = m2[0][3]; + T const SrcB10 = m2[1][0]; + T const SrcB11 = m2[1][1]; + T const SrcB12 = m2[1][2]; + T const SrcB13 = m2[1][3]; + + mat<2, 2, T, Q> Result; + Result[0][0] = SrcA00 * SrcB00 + SrcA10 * SrcB01 + SrcA20 * SrcB02 + SrcA30 * SrcB03; + Result[0][1] = SrcA01 * SrcB00 + SrcA11 * SrcB01 + SrcA21 * SrcB02 + SrcA31 * SrcB03; + Result[1][0] = SrcA00 * SrcB10 + SrcA10 * SrcB11 + SrcA20 * SrcB12 + SrcA30 * SrcB13; + Result[1][1] = SrcA01 * SrcB10 + SrcA11 * SrcB11 + SrcA21 * SrcB12 + SrcA31 * SrcB13; + return Result; + } + + template + GLM_FUNC_QUALIFIER mat<3, 2, T, Q> operator*(mat<4, 2, T, Q> const& m1, mat<3, 4, T, Q> const& m2) + { + return mat<3, 2, T, Q>( + m1[0][0] * m2[0][0] + m1[1][0] * m2[0][1] + m1[2][0] * m2[0][2] + m1[3][0] * m2[0][3], + m1[0][1] * m2[0][0] + m1[1][1] * m2[0][1] + m1[2][1] * m2[0][2] + m1[3][1] * m2[0][3], + m1[0][0] * m2[1][0] + m1[1][0] * m2[1][1] + m1[2][0] * m2[1][2] + m1[3][0] * m2[1][3], + m1[0][1] * m2[1][0] + m1[1][1] * m2[1][1] + m1[2][1] * m2[1][2] + m1[3][1] * m2[1][3], + m1[0][0] * m2[2][0] + m1[1][0] * m2[2][1] + m1[2][0] * m2[2][2] + m1[3][0] * m2[2][3], + m1[0][1] * m2[2][0] + m1[1][1] * m2[2][1] + m1[2][1] * m2[2][2] + m1[3][1] * m2[2][3]); + } + + template + GLM_FUNC_QUALIFIER mat<4, 2, T, Q> operator*(mat<4, 2, T, Q> const& m1, mat<4, 4, T, Q> const& m2) + { + return mat<4, 2, T, Q>( + m1[0][0] * m2[0][0] + m1[1][0] * m2[0][1] + m1[2][0] * m2[0][2] + m1[3][0] * m2[0][3], + m1[0][1] * m2[0][0] + m1[1][1] * m2[0][1] + m1[2][1] * m2[0][2] + m1[3][1] * m2[0][3], + m1[0][0] * m2[1][0] + m1[1][0] * m2[1][1] + m1[2][0] * m2[1][2] + m1[3][0] * m2[1][3], + m1[0][1] * m2[1][0] + m1[1][1] * m2[1][1] + m1[2][1] * m2[1][2] + m1[3][1] * m2[1][3], + m1[0][0] * m2[2][0] + m1[1][0] * m2[2][1] + m1[2][0] * m2[2][2] + m1[3][0] * m2[2][3], + m1[0][1] * m2[2][0] + m1[1][1] * m2[2][1] + m1[2][1] * m2[2][2] + m1[3][1] * m2[2][3], + m1[0][0] * m2[3][0] + m1[1][0] * m2[3][1] + m1[2][0] * m2[3][2] + m1[3][0] * m2[3][3], + m1[0][1] * m2[3][0] + m1[1][1] * m2[3][1] + m1[2][1] * m2[3][2] + m1[3][1] * m2[3][3]); + } + + template + GLM_FUNC_QUALIFIER mat<4, 2, T, Q> operator/(mat<4, 2, T, Q> const& m, T scalar) + { + return mat<4, 2, T, Q>( + m[0] / scalar, + m[1] / scalar, + m[2] / scalar, + m[3] / scalar); + } + + template + GLM_FUNC_QUALIFIER mat<4, 2, T, Q> operator/(T scalar, mat<4, 2, T, Q> const& m) + { + return mat<4, 2, T, Q>( + scalar / m[0], + scalar / m[1], + scalar / m[2], + scalar / m[3]); + } + + // -- Boolean operators -- + + template + GLM_FUNC_QUALIFIER bool operator==(mat<4, 2, T, Q> const& m1, mat<4, 2, T, Q> const& m2) + { + return (m1[0] == m2[0]) && (m1[1] == m2[1]) && (m1[2] == m2[2]) && (m1[3] == m2[3]); + } + + template + GLM_FUNC_QUALIFIER bool operator!=(mat<4, 2, T, Q> const& m1, mat<4, 2, T, Q> const& m2) + { + return (m1[0] != m2[0]) || (m1[1] != m2[1]) || (m1[2] != m2[2]) || (m1[3] != m2[3]); + } +} //namespace glm diff --git a/src/GLMath/glm/detail/type_mat4x3.hpp b/src/GLMath/glm/detail/type_mat4x3.hpp new file mode 100644 index 000000000..16e427051 --- /dev/null +++ b/src/GLMath/glm/detail/type_mat4x3.hpp @@ -0,0 +1,171 @@ +/// @ref core +/// @file glm/detail/type_mat4x3.hpp + +#pragma once + +#include "type_vec3.hpp" +#include "type_vec4.hpp" +#include +#include + +namespace glm +{ + template + struct mat<4, 3, T, Q> + { + typedef vec<3, T, Q> col_type; + typedef vec<4, T, Q> row_type; + typedef mat<4, 3, T, Q> type; + typedef mat<3, 4, T, Q> transpose_type; + typedef T value_type; + + private: + col_type value[4]; + + public: + // -- Accesses -- + + typedef length_t length_type; + GLM_FUNC_DECL static GLM_CONSTEXPR length_type length() { return 4; } + + GLM_FUNC_DECL col_type & operator[](length_type i); + GLM_FUNC_DECL GLM_CONSTEXPR col_type const& operator[](length_type i) const; + + // -- Constructors -- + + GLM_FUNC_DECL GLM_CONSTEXPR mat() GLM_DEFAULT; + template + GLM_FUNC_DECL GLM_CONSTEXPR mat(mat<4, 3, T, P> const& m); + + GLM_FUNC_DECL explicit GLM_CONSTEXPR mat(T const& x); + GLM_FUNC_DECL GLM_CONSTEXPR mat( + T const& x0, T const& y0, T const& z0, + T const& x1, T const& y1, T const& z1, + T const& x2, T const& y2, T const& z2, + T const& x3, T const& y3, T const& z3); + GLM_FUNC_DECL GLM_CONSTEXPR mat( + col_type const& v0, + col_type const& v1, + col_type const& v2, + col_type const& v3); + + // -- Conversions -- + + template< + typename X1, typename Y1, typename Z1, + typename X2, typename Y2, typename Z2, + typename X3, typename Y3, typename Z3, + typename X4, typename Y4, typename Z4> + GLM_FUNC_DECL GLM_CONSTEXPR mat( + X1 const& x1, Y1 const& y1, Z1 const& z1, + X2 const& x2, Y2 const& y2, Z2 const& z2, + X3 const& x3, Y3 const& y3, Z3 const& z3, + X4 const& x4, Y4 const& y4, Z4 const& z4); + + template + GLM_FUNC_DECL GLM_CONSTEXPR mat( + vec<3, V1, Q> const& v1, + vec<3, V2, Q> const& v2, + vec<3, V3, Q> const& v3, + vec<3, V4, Q> const& v4); + + // -- Matrix conversions -- + + template + GLM_FUNC_DECL GLM_EXPLICIT GLM_CONSTEXPR mat(mat<4, 3, U, P> const& m); + + GLM_FUNC_DECL GLM_EXPLICIT GLM_CONSTEXPR mat(mat<2, 2, T, Q> const& x); + GLM_FUNC_DECL GLM_EXPLICIT GLM_CONSTEXPR mat(mat<3, 3, T, Q> const& x); + GLM_FUNC_DECL GLM_EXPLICIT GLM_CONSTEXPR mat(mat<4, 4, T, Q> const& x); + GLM_FUNC_DECL GLM_EXPLICIT GLM_CONSTEXPR mat(mat<2, 3, T, Q> const& x); + GLM_FUNC_DECL GLM_EXPLICIT GLM_CONSTEXPR mat(mat<3, 2, T, Q> const& x); + GLM_FUNC_DECL GLM_EXPLICIT GLM_CONSTEXPR mat(mat<2, 4, T, Q> const& x); + GLM_FUNC_DECL GLM_EXPLICIT GLM_CONSTEXPR mat(mat<4, 2, T, Q> const& x); + GLM_FUNC_DECL GLM_EXPLICIT GLM_CONSTEXPR mat(mat<3, 4, T, Q> const& x); + + // -- Unary arithmetic operators -- + + template + GLM_FUNC_DECL mat<4, 3, T, Q> & operator=(mat<4, 3, U, Q> const& m); + template + GLM_FUNC_DECL mat<4, 3, T, Q> & operator+=(U s); + template + GLM_FUNC_DECL mat<4, 3, T, Q> & operator+=(mat<4, 3, U, Q> const& m); + template + GLM_FUNC_DECL mat<4, 3, T, Q> & operator-=(U s); + template + GLM_FUNC_DECL mat<4, 3, T, Q> & operator-=(mat<4, 3, U, Q> const& m); + template + GLM_FUNC_DECL mat<4, 3, T, Q> & operator*=(U s); + template + GLM_FUNC_DECL mat<4, 3, T, Q> & operator/=(U s); + + // -- Increment and decrement operators -- + + GLM_FUNC_DECL mat<4, 3, T, Q>& operator++(); + GLM_FUNC_DECL mat<4, 3, T, Q>& operator--(); + GLM_FUNC_DECL mat<4, 3, T, Q> operator++(int); + GLM_FUNC_DECL mat<4, 3, T, Q> operator--(int); + }; + + // -- Unary operators -- + + template + GLM_FUNC_DECL mat<4, 3, T, Q> operator+(mat<4, 3, T, Q> const& m); + + template + GLM_FUNC_DECL mat<4, 3, T, Q> operator-(mat<4, 3, T, Q> const& m); + + // -- Binary operators -- + + template + GLM_FUNC_DECL mat<4, 3, T, Q> operator+(mat<4, 3, T, Q> const& m, T const& s); + + template + GLM_FUNC_DECL mat<4, 3, T, Q> operator+(mat<4, 3, T, Q> const& m1, mat<4, 3, T, Q> const& m2); + + template + GLM_FUNC_DECL mat<4, 3, T, Q> operator-(mat<4, 3, T, Q> const& m, T const& s); + + template + GLM_FUNC_DECL mat<4, 3, T, Q> operator-(mat<4, 3, T, Q> const& m1, mat<4, 3, T, Q> const& m2); + + template + GLM_FUNC_DECL mat<4, 3, T, Q> operator*(mat<4, 3, T, Q> const& m, T const& s); + + template + GLM_FUNC_DECL mat<4, 3, T, Q> operator*(T const& s, mat<4, 3, T, Q> const& m); + + template + GLM_FUNC_DECL typename mat<4, 3, T, Q>::col_type operator*(mat<4, 3, T, Q> const& m, typename mat<4, 3, T, Q>::row_type const& v); + + template + GLM_FUNC_DECL typename mat<4, 3, T, Q>::row_type operator*(typename mat<4, 3, T, Q>::col_type const& v, mat<4, 3, T, Q> const& m); + + template + GLM_FUNC_DECL mat<2, 3, T, Q> operator*(mat<4, 3, T, Q> const& m1, mat<2, 4, T, Q> const& m2); + + template + GLM_FUNC_DECL mat<3, 3, T, Q> operator*(mat<4, 3, T, Q> const& m1, mat<3, 4, T, Q> const& m2); + + template + GLM_FUNC_DECL mat<4, 3, T, Q> operator*(mat<4, 3, T, Q> const& m1, mat<4, 4, T, Q> const& m2); + + template + GLM_FUNC_DECL mat<4, 3, T, Q> operator/(mat<4, 3, T, Q> const& m, T const& s); + + template + GLM_FUNC_DECL mat<4, 3, T, Q> operator/(T const& s, mat<4, 3, T, Q> const& m); + + // -- Boolean operators -- + + template + GLM_FUNC_DECL bool operator==(mat<4, 3, T, Q> const& m1, mat<4, 3, T, Q> const& m2); + + template + GLM_FUNC_DECL bool operator!=(mat<4, 3, T, Q> const& m1, mat<4, 3, T, Q> const& m2); +}//namespace glm + +#ifndef GLM_EXTERNAL_TEMPLATE +#include "type_mat4x3.inl" +#endif //GLM_EXTERNAL_TEMPLATE diff --git a/src/GLMath/glm/detail/type_mat4x3.inl b/src/GLMath/glm/detail/type_mat4x3.inl new file mode 100644 index 000000000..11b1ee35d --- /dev/null +++ b/src/GLMath/glm/detail/type_mat4x3.inl @@ -0,0 +1,598 @@ +namespace glm +{ + // -- Constructors -- + +# if GLM_CONFIG_DEFAULTED_FUNCTIONS == GLM_DISABLE + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<4, 3, T, Q>::mat() +# if GLM_CONFIG_CTOR_INIT == GLM_CTOR_INITIALIZER_LIST + : value{col_type(1, 0, 0), col_type(0, 1, 0), col_type(0, 0, 1), col_type(0, 0, 0)} +# endif + { +# if GLM_CONFIG_CTOR_INIT == GLM_CTOR_INITIALISATION + this->value[0] = col_type(1, 0, 0); + this->value[1] = col_type(0, 1, 0); + this->value[2] = col_type(0, 0, 1); + this->value[3] = col_type(0, 0, 0); +# endif + } +# endif + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<4, 3, T, Q>::mat(mat<4, 3, T, P> const& m) +# if GLM_HAS_INITIALIZER_LISTS + : value{col_type(m[0]), col_type(m[1]), col_type(m[2]), col_type(m[3])} +# endif + { +# if !GLM_HAS_INITIALIZER_LISTS + this->value[0] = m[0]; + this->value[1] = m[1]; + this->value[2] = m[2]; + this->value[3] = m[3]; +# endif + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<4, 3, T, Q>::mat(T const& s) +# if GLM_HAS_INITIALIZER_LISTS + : value{col_type(s, 0, 0), col_type(0, s, 0), col_type(0, 0, s), col_type(0, 0, 0)} +# endif + { +# if !GLM_HAS_INITIALIZER_LISTS + this->value[0] = col_type(s, 0, 0); + this->value[1] = col_type(0, s, 0); + this->value[2] = col_type(0, 0, s); + this->value[3] = col_type(0, 0, 0); +# endif + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<4, 3, T, Q>::mat + ( + T const& x0, T const& y0, T const& z0, + T const& x1, T const& y1, T const& z1, + T const& x2, T const& y2, T const& z2, + T const& x3, T const& y3, T const& z3 + ) +# if GLM_HAS_INITIALIZER_LISTS + : value{col_type(x0, y0, z0), col_type(x1, y1, z1), col_type(x2, y2, z2), col_type(x3, y3, z3)} +# endif + { +# if !GLM_HAS_INITIALIZER_LISTS + this->value[0] = col_type(x0, y0, z0); + this->value[1] = col_type(x1, y1, z1); + this->value[2] = col_type(x2, y2, z2); + this->value[3] = col_type(x3, y3, z3); +# endif + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<4, 3, T, Q>::mat(col_type const& v0, col_type const& v1, col_type const& v2, col_type const& v3) +# if GLM_HAS_INITIALIZER_LISTS + : value{col_type(v0), col_type(v1), col_type(v2), col_type(v3)} +# endif + { +# if !GLM_HAS_INITIALIZER_LISTS + this->value[0] = v0; + this->value[1] = v1; + this->value[2] = v2; + this->value[3] = v3; +# endif + } + + // -- Conversion constructors -- + + template + template< + typename X0, typename Y0, typename Z0, + typename X1, typename Y1, typename Z1, + typename X2, typename Y2, typename Z2, + typename X3, typename Y3, typename Z3> + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<4, 3, T, Q>::mat + ( + X0 const& x0, Y0 const& y0, Z0 const& z0, + X1 const& x1, Y1 const& y1, Z1 const& z1, + X2 const& x2, Y2 const& y2, Z2 const& z2, + X3 const& x3, Y3 const& y3, Z3 const& z3 + ) +# if GLM_HAS_INITIALIZER_LISTS + : value{col_type(x0, y0, z0), col_type(x1, y1, z1), col_type(x2, y2, z2), col_type(x3, y3, z3)} +# endif + { +# if !GLM_HAS_INITIALIZER_LISTS + this->value[0] = col_type(x0, y0, z0); + this->value[1] = col_type(x1, y1, z1); + this->value[2] = col_type(x2, y2, z2); + this->value[3] = col_type(x3, y3, z3); +# endif + } + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<4, 3, T, Q>::mat(vec<3, V1, Q> const& v1, vec<3, V2, Q> const& v2, vec<3, V3, Q> const& v3, vec<3, V4, Q> const& v4) +# if GLM_HAS_INITIALIZER_LISTS + : value{col_type(v1), col_type(v2), col_type(v3), col_type(v4)} +# endif + { +# if !GLM_HAS_INITIALIZER_LISTS + this->value[0] = col_type(v1); + this->value[1] = col_type(v2); + this->value[2] = col_type(v3); + this->value[3] = col_type(v4); +# endif + } + + // -- Matrix conversions -- + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<4, 3, T, Q>::mat(mat<4, 3, U, P> const& m) +# if GLM_HAS_INITIALIZER_LISTS + : value{col_type(m[0]), col_type(m[1]), col_type(m[2]), col_type(m[3])} +# endif + { +# if !GLM_HAS_INITIALIZER_LISTS + this->value[0] = col_type(m[0]); + this->value[1] = col_type(m[1]); + this->value[2] = col_type(m[2]); + this->value[3] = col_type(m[3]); +# endif + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<4, 3, T, Q>::mat(mat<2, 2, T, Q> const& m) +# if GLM_HAS_INITIALIZER_LISTS + : value{col_type(m[0], 0), col_type(m[1], 0), col_type(0, 0, 1), col_type(0)} +# endif + { +# if !GLM_HAS_INITIALIZER_LISTS + this->value[0] = col_type(m[0], 0); + this->value[1] = col_type(m[1], 0); + this->value[2] = col_type(0, 0, 1); + this->value[3] = col_type(0); +# endif + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<4, 3, T, Q>::mat(mat<3, 3, T, Q> const& m) +# if GLM_HAS_INITIALIZER_LISTS + : value{col_type(m[0]), col_type(m[1]), col_type(m[2]), col_type(0)} +# endif + { +# if !GLM_HAS_INITIALIZER_LISTS + this->value[0] = col_type(m[0]); + this->value[1] = col_type(m[1]); + this->value[2] = col_type(m[2]); + this->value[3] = col_type(0); +# endif + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<4, 3, T, Q>::mat(mat<4, 4, T, Q> const& m) +# if GLM_HAS_INITIALIZER_LISTS + : value{col_type(m[0]), col_type(m[1]), col_type(m[2]), col_type(m[3])} +# endif + { +# if !GLM_HAS_INITIALIZER_LISTS + this->value[0] = col_type(m[0]); + this->value[1] = col_type(m[1]); + this->value[2] = col_type(m[2]); + this->value[3] = col_type(m[3]); +# endif + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<4, 3, T, Q>::mat(mat<2, 3, T, Q> const& m) +# if GLM_HAS_INITIALIZER_LISTS + : value{col_type(m[0]), col_type(m[1]), col_type(0, 0, 1), col_type(0)} +# endif + { +# if !GLM_HAS_INITIALIZER_LISTS + this->value[0] = col_type(m[0]); + this->value[1] = col_type(m[1]); + this->value[2] = col_type(0, 0, 1); + this->value[3] = col_type(0); +# endif + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<4, 3, T, Q>::mat(mat<3, 2, T, Q> const& m) +# if GLM_HAS_INITIALIZER_LISTS + : value{col_type(m[0], 0), col_type(m[1], 0), col_type(m[2], 1), col_type(0)} +# endif + { +# if !GLM_HAS_INITIALIZER_LISTS + this->value[0] = col_type(m[0], 0); + this->value[1] = col_type(m[1], 0); + this->value[2] = col_type(m[2], 1); + this->value[3] = col_type(0); +# endif + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<4, 3, T, Q>::mat(mat<2, 4, T, Q> const& m) +# if GLM_HAS_INITIALIZER_LISTS + : value{col_type(m[0]), col_type(m[1]), col_type(0, 0, 1), col_type(0)} +# endif + { +# if !GLM_HAS_INITIALIZER_LISTS + this->value[0] = col_type(m[0]); + this->value[1] = col_type(m[1]); + this->value[2] = col_type(0, 0, 1); + this->value[3] = col_type(0); +# endif + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<4, 3, T, Q>::mat(mat<4, 2, T, Q> const& m) +# if GLM_HAS_INITIALIZER_LISTS + : value{col_type(m[0], 0), col_type(m[1], 0), col_type(m[2], 1), col_type(m[3], 0)} +# endif + { +# if !GLM_HAS_INITIALIZER_LISTS + this->value[0] = col_type(m[0], 0); + this->value[1] = col_type(m[1], 0); + this->value[2] = col_type(m[2], 1); + this->value[3] = col_type(m[3], 0); +# endif + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<4, 3, T, Q>::mat(mat<3, 4, T, Q> const& m) +# if GLM_HAS_INITIALIZER_LISTS + : value{col_type(m[0]), col_type(m[1]), col_type(m[2]), col_type(0)} +# endif + { +# if !GLM_HAS_INITIALIZER_LISTS + this->value[0] = col_type(m[0]); + this->value[1] = col_type(m[1]); + this->value[2] = col_type(m[2]); + this->value[3] = col_type(0); +# endif + } + + // -- Accesses -- + + template + GLM_FUNC_QUALIFIER typename mat<4, 3, T, Q>::col_type & mat<4, 3, T, Q>::operator[](typename mat<4, 3, T, Q>::length_type i) + { + assert(i < this->length()); + return this->value[i]; + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR typename mat<4, 3, T, Q>::col_type const& mat<4, 3, T, Q>::operator[](typename mat<4, 3, T, Q>::length_type i) const + { + assert(i < this->length()); + return this->value[i]; + } + + // -- Unary updatable operators -- + + template + template + GLM_FUNC_QUALIFIER mat<4, 3, T, Q>& mat<4, 3, T, Q>::operator=(mat<4, 3, U, Q> const& m) + { + this->value[0] = m[0]; + this->value[1] = m[1]; + this->value[2] = m[2]; + this->value[3] = m[3]; + return *this; + } + + template + template + GLM_FUNC_QUALIFIER mat<4, 3, T, Q> & mat<4, 3, T, Q>::operator+=(U s) + { + this->value[0] += s; + this->value[1] += s; + this->value[2] += s; + this->value[3] += s; + return *this; + } + + template + template + GLM_FUNC_QUALIFIER mat<4, 3, T, Q> & mat<4, 3, T, Q>::operator+=(mat<4, 3, U, Q> const& m) + { + this->value[0] += m[0]; + this->value[1] += m[1]; + this->value[2] += m[2]; + this->value[3] += m[3]; + return *this; + } + + template + template + GLM_FUNC_QUALIFIER mat<4, 3, T, Q> & mat<4, 3, T, Q>::operator-=(U s) + { + this->value[0] -= s; + this->value[1] -= s; + this->value[2] -= s; + this->value[3] -= s; + return *this; + } + + template + template + GLM_FUNC_QUALIFIER mat<4, 3, T, Q> & mat<4, 3, T, Q>::operator-=(mat<4, 3, U, Q> const& m) + { + this->value[0] -= m[0]; + this->value[1] -= m[1]; + this->value[2] -= m[2]; + this->value[3] -= m[3]; + return *this; + } + + template + template + GLM_FUNC_QUALIFIER mat<4, 3, T, Q> & mat<4, 3, T, Q>::operator*=(U s) + { + this->value[0] *= s; + this->value[1] *= s; + this->value[2] *= s; + this->value[3] *= s; + return *this; + } + + template + template + GLM_FUNC_QUALIFIER mat<4, 3, T, Q> & mat<4, 3, T, Q>::operator/=(U s) + { + this->value[0] /= s; + this->value[1] /= s; + this->value[2] /= s; + this->value[3] /= s; + return *this; + } + + // -- Increment and decrement operators -- + + template + GLM_FUNC_QUALIFIER mat<4, 3, T, Q> & mat<4, 3, T, Q>::operator++() + { + ++this->value[0]; + ++this->value[1]; + ++this->value[2]; + ++this->value[3]; + return *this; + } + + template + GLM_FUNC_QUALIFIER mat<4, 3, T, Q> & mat<4, 3, T, Q>::operator--() + { + --this->value[0]; + --this->value[1]; + --this->value[2]; + --this->value[3]; + return *this; + } + + template + GLM_FUNC_QUALIFIER mat<4, 3, T, Q> mat<4, 3, T, Q>::operator++(int) + { + mat<4, 3, T, Q> Result(*this); + ++*this; + return Result; + } + + template + GLM_FUNC_QUALIFIER mat<4, 3, T, Q> mat<4, 3, T, Q>::operator--(int) + { + mat<4, 3, T, Q> Result(*this); + --*this; + return Result; + } + + // -- Unary arithmetic operators -- + + template + GLM_FUNC_QUALIFIER mat<4, 3, T, Q> operator+(mat<4, 3, T, Q> const& m) + { + return m; + } + + template + GLM_FUNC_QUALIFIER mat<4, 3, T, Q> operator-(mat<4, 3, T, Q> const& m) + { + return mat<4, 3, T, Q>( + -m[0], + -m[1], + -m[2], + -m[3]); + } + + // -- Binary arithmetic operators -- + + template + GLM_FUNC_QUALIFIER mat<4, 3, T, Q> operator+(mat<4, 3, T, Q> const& m, T const& s) + { + return mat<4, 3, T, Q>( + m[0] + s, + m[1] + s, + m[2] + s, + m[3] + s); + } + + template + GLM_FUNC_QUALIFIER mat<4, 3, T, Q> operator+(mat<4, 3, T, Q> const& m1, mat<4, 3, T, Q> const& m2) + { + return mat<4, 3, T, Q>( + m1[0] + m2[0], + m1[1] + m2[1], + m1[2] + m2[2], + m1[3] + m2[3]); + } + + template + GLM_FUNC_QUALIFIER mat<4, 3, T, Q> operator-(mat<4, 3, T, Q> const& m, T const& s) + { + return mat<4, 3, T, Q>( + m[0] - s, + m[1] - s, + m[2] - s, + m[3] - s); + } + + template + GLM_FUNC_QUALIFIER mat<4, 3, T, Q> operator-(mat<4, 3, T, Q> const& m1, mat<4, 3, T, Q> const& m2) + { + return mat<4, 3, T, Q>( + m1[0] - m2[0], + m1[1] - m2[1], + m1[2] - m2[2], + m1[3] - m2[3]); + } + + template + GLM_FUNC_QUALIFIER mat<4, 3, T, Q> operator*(mat<4, 3, T, Q> const& m, T const& s) + { + return mat<4, 3, T, Q>( + m[0] * s, + m[1] * s, + m[2] * s, + m[3] * s); + } + + template + GLM_FUNC_QUALIFIER mat<4, 3, T, Q> operator*(T const& s, mat<4, 3, T, Q> const& m) + { + return mat<4, 3, T, Q>( + m[0] * s, + m[1] * s, + m[2] * s, + m[3] * s); + } + + template + GLM_FUNC_QUALIFIER typename mat<4, 3, T, Q>::col_type operator* + ( + mat<4, 3, T, Q> const& m, + typename mat<4, 3, T, Q>::row_type const& v) + { + return typename mat<4, 3, T, Q>::col_type( + m[0][0] * v.x + m[1][0] * v.y + m[2][0] * v.z + m[3][0] * v.w, + m[0][1] * v.x + m[1][1] * v.y + m[2][1] * v.z + m[3][1] * v.w, + m[0][2] * v.x + m[1][2] * v.y + m[2][2] * v.z + m[3][2] * v.w); + } + + template + GLM_FUNC_QUALIFIER typename mat<4, 3, T, Q>::row_type operator* + ( + typename mat<4, 3, T, Q>::col_type const& v, + mat<4, 3, T, Q> const& m) + { + return typename mat<4, 3, T, Q>::row_type( + v.x * m[0][0] + v.y * m[0][1] + v.z * m[0][2], + v.x * m[1][0] + v.y * m[1][1] + v.z * m[1][2], + v.x * m[2][0] + v.y * m[2][1] + v.z * m[2][2], + v.x * m[3][0] + v.y * m[3][1] + v.z * m[3][2]); + } + + template + GLM_FUNC_QUALIFIER mat<2, 3, T, Q> operator*(mat<4, 3, T, Q> const& m1, mat<2, 4, T, Q> const& m2) + { + return mat<2, 3, T, Q>( + m1[0][0] * m2[0][0] + m1[1][0] * m2[0][1] + m1[2][0] * m2[0][2] + m1[3][0] * m2[0][3], + m1[0][1] * m2[0][0] + m1[1][1] * m2[0][1] + m1[2][1] * m2[0][2] + m1[3][1] * m2[0][3], + m1[0][2] * m2[0][0] + m1[1][2] * m2[0][1] + m1[2][2] * m2[0][2] + m1[3][2] * m2[0][3], + m1[0][0] * m2[1][0] + m1[1][0] * m2[1][1] + m1[2][0] * m2[1][2] + m1[3][0] * m2[1][3], + m1[0][1] * m2[1][0] + m1[1][1] * m2[1][1] + m1[2][1] * m2[1][2] + m1[3][1] * m2[1][3], + m1[0][2] * m2[1][0] + m1[1][2] * m2[1][1] + m1[2][2] * m2[1][2] + m1[3][2] * m2[1][3]); + } + + template + GLM_FUNC_QUALIFIER mat<3, 3, T, Q> operator*(mat<4, 3, T, Q> const& m1, mat<3, 4, T, Q> const& m2) + { + T const SrcA00 = m1[0][0]; + T const SrcA01 = m1[0][1]; + T const SrcA02 = m1[0][2]; + T const SrcA10 = m1[1][0]; + T const SrcA11 = m1[1][1]; + T const SrcA12 = m1[1][2]; + T const SrcA20 = m1[2][0]; + T const SrcA21 = m1[2][1]; + T const SrcA22 = m1[2][2]; + T const SrcA30 = m1[3][0]; + T const SrcA31 = m1[3][1]; + T const SrcA32 = m1[3][2]; + + T const SrcB00 = m2[0][0]; + T const SrcB01 = m2[0][1]; + T const SrcB02 = m2[0][2]; + T const SrcB03 = m2[0][3]; + T const SrcB10 = m2[1][0]; + T const SrcB11 = m2[1][1]; + T const SrcB12 = m2[1][2]; + T const SrcB13 = m2[1][3]; + T const SrcB20 = m2[2][0]; + T const SrcB21 = m2[2][1]; + T const SrcB22 = m2[2][2]; + T const SrcB23 = m2[2][3]; + + mat<3, 3, T, Q> Result; + Result[0][0] = SrcA00 * SrcB00 + SrcA10 * SrcB01 + SrcA20 * SrcB02 + SrcA30 * SrcB03; + Result[0][1] = SrcA01 * SrcB00 + SrcA11 * SrcB01 + SrcA21 * SrcB02 + SrcA31 * SrcB03; + Result[0][2] = SrcA02 * SrcB00 + SrcA12 * SrcB01 + SrcA22 * SrcB02 + SrcA32 * SrcB03; + Result[1][0] = SrcA00 * SrcB10 + SrcA10 * SrcB11 + SrcA20 * SrcB12 + SrcA30 * SrcB13; + Result[1][1] = SrcA01 * SrcB10 + SrcA11 * SrcB11 + SrcA21 * SrcB12 + SrcA31 * SrcB13; + Result[1][2] = SrcA02 * SrcB10 + SrcA12 * SrcB11 + SrcA22 * SrcB12 + SrcA32 * SrcB13; + Result[2][0] = SrcA00 * SrcB20 + SrcA10 * SrcB21 + SrcA20 * SrcB22 + SrcA30 * SrcB23; + Result[2][1] = SrcA01 * SrcB20 + SrcA11 * SrcB21 + SrcA21 * SrcB22 + SrcA31 * SrcB23; + Result[2][2] = SrcA02 * SrcB20 + SrcA12 * SrcB21 + SrcA22 * SrcB22 + SrcA32 * SrcB23; + return Result; + } + + template + GLM_FUNC_QUALIFIER mat<4, 3, T, Q> operator*(mat<4, 3, T, Q> const& m1, mat<4, 4, T, Q> const& m2) + { + return mat<4, 3, T, Q>( + m1[0][0] * m2[0][0] + m1[1][0] * m2[0][1] + m1[2][0] * m2[0][2] + m1[3][0] * m2[0][3], + m1[0][1] * m2[0][0] + m1[1][1] * m2[0][1] + m1[2][1] * m2[0][2] + m1[3][1] * m2[0][3], + m1[0][2] * m2[0][0] + m1[1][2] * m2[0][1] + m1[2][2] * m2[0][2] + m1[3][2] * m2[0][3], + m1[0][0] * m2[1][0] + m1[1][0] * m2[1][1] + m1[2][0] * m2[1][2] + m1[3][0] * m2[1][3], + m1[0][1] * m2[1][0] + m1[1][1] * m2[1][1] + m1[2][1] * m2[1][2] + m1[3][1] * m2[1][3], + m1[0][2] * m2[1][0] + m1[1][2] * m2[1][1] + m1[2][2] * m2[1][2] + m1[3][2] * m2[1][3], + m1[0][0] * m2[2][0] + m1[1][0] * m2[2][1] + m1[2][0] * m2[2][2] + m1[3][0] * m2[2][3], + m1[0][1] * m2[2][0] + m1[1][1] * m2[2][1] + m1[2][1] * m2[2][2] + m1[3][1] * m2[2][3], + m1[0][2] * m2[2][0] + m1[1][2] * m2[2][1] + m1[2][2] * m2[2][2] + m1[3][2] * m2[2][3], + m1[0][0] * m2[3][0] + m1[1][0] * m2[3][1] + m1[2][0] * m2[3][2] + m1[3][0] * m2[3][3], + m1[0][1] * m2[3][0] + m1[1][1] * m2[3][1] + m1[2][1] * m2[3][2] + m1[3][1] * m2[3][3], + m1[0][2] * m2[3][0] + m1[1][2] * m2[3][1] + m1[2][2] * m2[3][2] + m1[3][2] * m2[3][3]); + } + + template + GLM_FUNC_QUALIFIER mat<4, 3, T, Q> operator/(mat<4, 3, T, Q> const& m, T const& s) + { + return mat<4, 3, T, Q>( + m[0] / s, + m[1] / s, + m[2] / s, + m[3] / s); + } + + template + GLM_FUNC_QUALIFIER mat<4, 3, T, Q> operator/(T const& s, mat<4, 3, T, Q> const& m) + { + return mat<4, 3, T, Q>( + s / m[0], + s / m[1], + s / m[2], + s / m[3]); + } + + // -- Boolean operators -- + + template + GLM_FUNC_QUALIFIER bool operator==(mat<4, 3, T, Q> const& m1, mat<4, 3, T, Q> const& m2) + { + return (m1[0] == m2[0]) && (m1[1] == m2[1]) && (m1[2] == m2[2]) && (m1[3] == m2[3]); + } + + template + GLM_FUNC_QUALIFIER bool operator!=(mat<4, 3, T, Q> const& m1, mat<4, 3, T, Q> const& m2) + { + return (m1[0] != m2[0]) || (m1[1] != m2[1]) || (m1[2] != m2[2]) || (m1[3] != m2[3]); + } +} //namespace glm diff --git a/src/GLMath/glm/detail/type_mat4x4.hpp b/src/GLMath/glm/detail/type_mat4x4.hpp new file mode 100644 index 000000000..3517f9f52 --- /dev/null +++ b/src/GLMath/glm/detail/type_mat4x4.hpp @@ -0,0 +1,189 @@ +/// @ref core +/// @file glm/detail/type_mat4x4.hpp + +#pragma once + +#include "type_vec4.hpp" +#include +#include + +namespace glm +{ + template + struct mat<4, 4, T, Q> + { + typedef vec<4, T, Q> col_type; + typedef vec<4, T, Q> row_type; + typedef mat<4, 4, T, Q> type; + typedef mat<4, 4, T, Q> transpose_type; + typedef T value_type; + + private: + col_type value[4]; + + public: + // -- Accesses -- + + typedef length_t length_type; + GLM_FUNC_DECL static GLM_CONSTEXPR length_type length(){return 4;} + + GLM_FUNC_DECL col_type & operator[](length_type i); + GLM_FUNC_DECL GLM_CONSTEXPR col_type const& operator[](length_type i) const; + + // -- Constructors -- + + GLM_FUNC_DECL GLM_CONSTEXPR mat() GLM_DEFAULT; + template + GLM_FUNC_DECL GLM_CONSTEXPR mat(mat<4, 4, T, P> const& m); + + GLM_FUNC_DECL explicit GLM_CONSTEXPR mat(T const& x); + GLM_FUNC_DECL GLM_CONSTEXPR mat( + T const& x0, T const& y0, T const& z0, T const& w0, + T const& x1, T const& y1, T const& z1, T const& w1, + T const& x2, T const& y2, T const& z2, T const& w2, + T const& x3, T const& y3, T const& z3, T const& w3); + GLM_FUNC_DECL GLM_CONSTEXPR mat( + col_type const& v0, + col_type const& v1, + col_type const& v2, + col_type const& v3); + + // -- Conversions -- + + template< + typename X1, typename Y1, typename Z1, typename W1, + typename X2, typename Y2, typename Z2, typename W2, + typename X3, typename Y3, typename Z3, typename W3, + typename X4, typename Y4, typename Z4, typename W4> + GLM_FUNC_DECL GLM_CONSTEXPR mat( + X1 const& x1, Y1 const& y1, Z1 const& z1, W1 const& w1, + X2 const& x2, Y2 const& y2, Z2 const& z2, W2 const& w2, + X3 const& x3, Y3 const& y3, Z3 const& z3, W3 const& w3, + X4 const& x4, Y4 const& y4, Z4 const& z4, W4 const& w4); + + template + GLM_FUNC_DECL GLM_CONSTEXPR mat( + vec<4, V1, Q> const& v1, + vec<4, V2, Q> const& v2, + vec<4, V3, Q> const& v3, + vec<4, V4, Q> const& v4); + + // -- Matrix conversions -- + + template + GLM_FUNC_DECL GLM_EXPLICIT GLM_CONSTEXPR mat(mat<4, 4, U, P> const& m); + + GLM_FUNC_DECL GLM_EXPLICIT GLM_CONSTEXPR mat(mat<2, 2, T, Q> const& x); + GLM_FUNC_DECL GLM_EXPLICIT GLM_CONSTEXPR mat(mat<3, 3, T, Q> const& x); + GLM_FUNC_DECL GLM_EXPLICIT GLM_CONSTEXPR mat(mat<2, 3, T, Q> const& x); + GLM_FUNC_DECL GLM_EXPLICIT GLM_CONSTEXPR mat(mat<3, 2, T, Q> const& x); + GLM_FUNC_DECL GLM_EXPLICIT GLM_CONSTEXPR mat(mat<2, 4, T, Q> const& x); + GLM_FUNC_DECL GLM_EXPLICIT GLM_CONSTEXPR mat(mat<4, 2, T, Q> const& x); + GLM_FUNC_DECL GLM_EXPLICIT GLM_CONSTEXPR mat(mat<3, 4, T, Q> const& x); + GLM_FUNC_DECL GLM_EXPLICIT GLM_CONSTEXPR mat(mat<4, 3, T, Q> const& x); + + // -- Unary arithmetic operators -- + + template + GLM_FUNC_DECL mat<4, 4, T, Q> & operator=(mat<4, 4, U, Q> const& m); + template + GLM_FUNC_DECL mat<4, 4, T, Q> & operator+=(U s); + template + GLM_FUNC_DECL mat<4, 4, T, Q> & operator+=(mat<4, 4, U, Q> const& m); + template + GLM_FUNC_DECL mat<4, 4, T, Q> & operator-=(U s); + template + GLM_FUNC_DECL mat<4, 4, T, Q> & operator-=(mat<4, 4, U, Q> const& m); + template + GLM_FUNC_DECL mat<4, 4, T, Q> & operator*=(U s); + template + GLM_FUNC_DECL mat<4, 4, T, Q> & operator*=(mat<4, 4, U, Q> const& m); + template + GLM_FUNC_DECL mat<4, 4, T, Q> & operator/=(U s); + template + GLM_FUNC_DECL mat<4, 4, T, Q> & operator/=(mat<4, 4, U, Q> const& m); + + // -- Increment and decrement operators -- + + GLM_FUNC_DECL mat<4, 4, T, Q> & operator++(); + GLM_FUNC_DECL mat<4, 4, T, Q> & operator--(); + GLM_FUNC_DECL mat<4, 4, T, Q> operator++(int); + GLM_FUNC_DECL mat<4, 4, T, Q> operator--(int); + }; + + // -- Unary operators -- + + template + GLM_FUNC_DECL mat<4, 4, T, Q> operator+(mat<4, 4, T, Q> const& m); + + template + GLM_FUNC_DECL mat<4, 4, T, Q> operator-(mat<4, 4, T, Q> const& m); + + // -- Binary operators -- + + template + GLM_FUNC_DECL mat<4, 4, T, Q> operator+(mat<4, 4, T, Q> const& m, T const& s); + + template + GLM_FUNC_DECL mat<4, 4, T, Q> operator+(T const& s, mat<4, 4, T, Q> const& m); + + template + GLM_FUNC_DECL mat<4, 4, T, Q> operator+(mat<4, 4, T, Q> const& m1, mat<4, 4, T, Q> const& m2); + + template + GLM_FUNC_DECL mat<4, 4, T, Q> operator-(mat<4, 4, T, Q> const& m, T const& s); + + template + GLM_FUNC_DECL mat<4, 4, T, Q> operator-(T const& s, mat<4, 4, T, Q> const& m); + + template + GLM_FUNC_DECL mat<4, 4, T, Q> operator-(mat<4, 4, T, Q> const& m1, mat<4, 4, T, Q> const& m2); + + template + GLM_FUNC_DECL mat<4, 4, T, Q> operator*(mat<4, 4, T, Q> const& m, T const& s); + + template + GLM_FUNC_DECL mat<4, 4, T, Q> operator*(T const& s, mat<4, 4, T, Q> const& m); + + template + GLM_FUNC_DECL typename mat<4, 4, T, Q>::col_type operator*(mat<4, 4, T, Q> const& m, typename mat<4, 4, T, Q>::row_type const& v); + + template + GLM_FUNC_DECL typename mat<4, 4, T, Q>::row_type operator*(typename mat<4, 4, T, Q>::col_type const& v, mat<4, 4, T, Q> const& m); + + template + GLM_FUNC_DECL mat<2, 4, T, Q> operator*(mat<4, 4, T, Q> const& m1, mat<2, 4, T, Q> const& m2); + + template + GLM_FUNC_DECL mat<3, 4, T, Q> operator*(mat<4, 4, T, Q> const& m1, mat<3, 4, T, Q> const& m2); + + template + GLM_FUNC_DECL mat<4, 4, T, Q> operator*(mat<4, 4, T, Q> const& m1, mat<4, 4, T, Q> const& m2); + + template + GLM_FUNC_DECL mat<4, 4, T, Q> operator/(mat<4, 4, T, Q> const& m, T const& s); + + template + GLM_FUNC_DECL mat<4, 4, T, Q> operator/(T const& s, mat<4, 4, T, Q> const& m); + + template + GLM_FUNC_DECL typename mat<4, 4, T, Q>::col_type operator/(mat<4, 4, T, Q> const& m, typename mat<4, 4, T, Q>::row_type const& v); + + template + GLM_FUNC_DECL typename mat<4, 4, T, Q>::row_type operator/(typename mat<4, 4, T, Q>::col_type const& v, mat<4, 4, T, Q> const& m); + + template + GLM_FUNC_DECL mat<4, 4, T, Q> operator/(mat<4, 4, T, Q> const& m1, mat<4, 4, T, Q> const& m2); + + // -- Boolean operators -- + + template + GLM_FUNC_DECL bool operator==(mat<4, 4, T, Q> const& m1, mat<4, 4, T, Q> const& m2); + + template + GLM_FUNC_DECL bool operator!=(mat<4, 4, T, Q> const& m1, mat<4, 4, T, Q> const& m2); +}//namespace glm + +#ifndef GLM_EXTERNAL_TEMPLATE +#include "type_mat4x4.inl" +#endif//GLM_EXTERNAL_TEMPLATE diff --git a/src/GLMath/glm/detail/type_mat4x4.inl b/src/GLMath/glm/detail/type_mat4x4.inl new file mode 100644 index 000000000..e38b87f77 --- /dev/null +++ b/src/GLMath/glm/detail/type_mat4x4.inl @@ -0,0 +1,706 @@ +#include "../matrix.hpp" + +namespace glm +{ + // -- Constructors -- + +# if GLM_CONFIG_DEFAULTED_FUNCTIONS == GLM_DISABLE + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<4, 4, T, Q>::mat() +# if GLM_CONFIG_CTOR_INIT == GLM_CTOR_INITIALIZER_LIST + : value{col_type(1, 0, 0, 0), col_type(0, 1, 0, 0), col_type(0, 0, 1, 0), col_type(0, 0, 0, 1)} +# endif + { +# if GLM_CONFIG_CTOR_INIT == GLM_CTOR_INITIALISATION + this->value[0] = col_type(1, 0, 0, 0); + this->value[1] = col_type(0, 1, 0, 0); + this->value[2] = col_type(0, 0, 1, 0); + this->value[3] = col_type(0, 0, 0, 1); +# endif + } +# endif + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<4, 4, T, Q>::mat(mat<4, 4, T, P> const& m) +# if GLM_HAS_INITIALIZER_LISTS + : value{col_type(m[0]), col_type(m[1]), col_type(m[2]), col_type(m[3])} +# endif + { +# if !GLM_HAS_INITIALIZER_LISTS + this->value[0] = m[0]; + this->value[1] = m[1]; + this->value[2] = m[2]; + this->value[3] = m[3]; +# endif + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<4, 4, T, Q>::mat(T const& s) +# if GLM_HAS_INITIALIZER_LISTS + : value{col_type(s, 0, 0, 0), col_type(0, s, 0, 0), col_type(0, 0, s, 0), col_type(0, 0, 0, s)} +# endif + { +# if !GLM_HAS_INITIALIZER_LISTS + this->value[0] = col_type(s, 0, 0, 0); + this->value[1] = col_type(0, s, 0, 0); + this->value[2] = col_type(0, 0, s, 0); + this->value[3] = col_type(0, 0, 0, s); +# endif + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<4, 4, T, Q>::mat + ( + T const& x0, T const& y0, T const& z0, T const& w0, + T const& x1, T const& y1, T const& z1, T const& w1, + T const& x2, T const& y2, T const& z2, T const& w2, + T const& x3, T const& y3, T const& z3, T const& w3 + ) +# if GLM_HAS_INITIALIZER_LISTS + : value{ + col_type(x0, y0, z0, w0), + col_type(x1, y1, z1, w1), + col_type(x2, y2, z2, w2), + col_type(x3, y3, z3, w3)} +# endif + { +# if !GLM_HAS_INITIALIZER_LISTS + this->value[0] = col_type(x0, y0, z0, w0); + this->value[1] = col_type(x1, y1, z1, w1); + this->value[2] = col_type(x2, y2, z2, w2); + this->value[3] = col_type(x3, y3, z3, w3); +# endif + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<4, 4, T, Q>::mat(col_type const& v0, col_type const& v1, col_type const& v2, col_type const& v3) +# if GLM_HAS_INITIALIZER_LISTS + : value{col_type(v0), col_type(v1), col_type(v2), col_type(v3)} +# endif + { +# if !GLM_HAS_INITIALIZER_LISTS + this->value[0] = v0; + this->value[1] = v1; + this->value[2] = v2; + this->value[3] = v3; +# endif + } + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<4, 4, T, Q>::mat(mat<4, 4, U, P> const& m) +# if GLM_HAS_INITIALIZER_LISTS + : value{col_type(m[0]), col_type(m[1]), col_type(m[2]), col_type(m[3])} +# endif + { +# if !GLM_HAS_INITIALIZER_LISTS + this->value[0] = col_type(m[0]); + this->value[1] = col_type(m[1]); + this->value[2] = col_type(m[2]); + this->value[3] = col_type(m[3]); +# endif + } + + // -- Conversions -- + + template + template< + typename X1, typename Y1, typename Z1, typename W1, + typename X2, typename Y2, typename Z2, typename W2, + typename X3, typename Y3, typename Z3, typename W3, + typename X4, typename Y4, typename Z4, typename W4> + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<4, 4, T, Q>::mat + ( + X1 const& x1, Y1 const& y1, Z1 const& z1, W1 const& w1, + X2 const& x2, Y2 const& y2, Z2 const& z2, W2 const& w2, + X3 const& x3, Y3 const& y3, Z3 const& z3, W3 const& w3, + X4 const& x4, Y4 const& y4, Z4 const& z4, W4 const& w4 + ) +# if GLM_HAS_INITIALIZER_LISTS + : value{col_type(x1, y1, z1, w1), col_type(x2, y2, z2, w2), col_type(x3, y3, z3, w3), col_type(x4, y4, z4, w4)} +# endif + { + GLM_STATIC_ASSERT(std::numeric_limits::is_iec559 || std::numeric_limits::is_integer || GLM_CONFIG_UNRESTRICTED_GENTYPE, "*mat4x4 constructor only takes float and integer types, 1st parameter type invalid."); + GLM_STATIC_ASSERT(std::numeric_limits::is_iec559 || std::numeric_limits::is_integer || GLM_CONFIG_UNRESTRICTED_GENTYPE, "*mat4x4 constructor only takes float and integer types, 2nd parameter type invalid."); + GLM_STATIC_ASSERT(std::numeric_limits::is_iec559 || std::numeric_limits::is_integer || GLM_CONFIG_UNRESTRICTED_GENTYPE, "*mat4x4 constructor only takes float and integer types, 3rd parameter type invalid."); + GLM_STATIC_ASSERT(std::numeric_limits::is_iec559 || std::numeric_limits::is_integer || GLM_CONFIG_UNRESTRICTED_GENTYPE, "*mat4x4 constructor only takes float and integer types, 4th parameter type invalid."); + + GLM_STATIC_ASSERT(std::numeric_limits::is_iec559 || std::numeric_limits::is_integer || GLM_CONFIG_UNRESTRICTED_GENTYPE, "*mat4x4 constructor only takes float and integer types, 5th parameter type invalid."); + GLM_STATIC_ASSERT(std::numeric_limits::is_iec559 || std::numeric_limits::is_integer || GLM_CONFIG_UNRESTRICTED_GENTYPE, "*mat4x4 constructor only takes float and integer types, 6th parameter type invalid."); + GLM_STATIC_ASSERT(std::numeric_limits::is_iec559 || std::numeric_limits::is_integer || GLM_CONFIG_UNRESTRICTED_GENTYPE, "*mat4x4 constructor only takes float and integer types, 7th parameter type invalid."); + GLM_STATIC_ASSERT(std::numeric_limits::is_iec559 || std::numeric_limits::is_integer || GLM_CONFIG_UNRESTRICTED_GENTYPE, "*mat4x4 constructor only takes float and integer types, 8th parameter type invalid."); + + GLM_STATIC_ASSERT(std::numeric_limits::is_iec559 || std::numeric_limits::is_integer || GLM_CONFIG_UNRESTRICTED_GENTYPE, "*mat4x4 constructor only takes float and integer types, 9th parameter type invalid."); + GLM_STATIC_ASSERT(std::numeric_limits::is_iec559 || std::numeric_limits::is_integer || GLM_CONFIG_UNRESTRICTED_GENTYPE, "*mat4x4 constructor only takes float and integer types, 10th parameter type invalid."); + GLM_STATIC_ASSERT(std::numeric_limits::is_iec559 || std::numeric_limits::is_integer || GLM_CONFIG_UNRESTRICTED_GENTYPE, "*mat4x4 constructor only takes float and integer types, 11th parameter type invalid."); + GLM_STATIC_ASSERT(std::numeric_limits::is_iec559 || std::numeric_limits::is_integer || GLM_CONFIG_UNRESTRICTED_GENTYPE, "*mat4x4 constructor only takes float and integer types, 12th parameter type invalid."); + + GLM_STATIC_ASSERT(std::numeric_limits::is_iec559 || std::numeric_limits::is_integer || GLM_CONFIG_UNRESTRICTED_GENTYPE, "*mat4x4 constructor only takes float and integer types, 13th parameter type invalid."); + GLM_STATIC_ASSERT(std::numeric_limits::is_iec559 || std::numeric_limits::is_integer || GLM_CONFIG_UNRESTRICTED_GENTYPE, "*mat4x4 constructor only takes float and integer types, 14th parameter type invalid."); + GLM_STATIC_ASSERT(std::numeric_limits::is_iec559 || std::numeric_limits::is_integer || GLM_CONFIG_UNRESTRICTED_GENTYPE, "*mat4x4 constructor only takes float and integer types, 15th parameter type invalid."); + GLM_STATIC_ASSERT(std::numeric_limits::is_iec559 || std::numeric_limits::is_integer || GLM_CONFIG_UNRESTRICTED_GENTYPE, "*mat4x4 constructor only takes float and integer types, 16th parameter type invalid."); + +# if !GLM_HAS_INITIALIZER_LISTS + this->value[0] = col_type(x1, y1, z1, w1); + this->value[1] = col_type(x2, y2, z2, w2); + this->value[2] = col_type(x3, y3, z3, w3); + this->value[3] = col_type(x4, y4, z4, w4); +# endif + } + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<4, 4, T, Q>::mat(vec<4, V1, Q> const& v1, vec<4, V2, Q> const& v2, vec<4, V3, Q> const& v3, vec<4, V4, Q> const& v4) +# if GLM_HAS_INITIALIZER_LISTS + : value{col_type(v1), col_type(v2), col_type(v3), col_type(v4)} +# endif + { + GLM_STATIC_ASSERT(std::numeric_limits::is_iec559 || std::numeric_limits::is_integer || GLM_CONFIG_UNRESTRICTED_GENTYPE, "*mat4x4 constructor only takes float and integer types, 1st parameter type invalid."); + GLM_STATIC_ASSERT(std::numeric_limits::is_iec559 || std::numeric_limits::is_integer || GLM_CONFIG_UNRESTRICTED_GENTYPE, "*mat4x4 constructor only takes float and integer types, 2nd parameter type invalid."); + GLM_STATIC_ASSERT(std::numeric_limits::is_iec559 || std::numeric_limits::is_integer || GLM_CONFIG_UNRESTRICTED_GENTYPE, "*mat4x4 constructor only takes float and integer types, 3rd parameter type invalid."); + GLM_STATIC_ASSERT(std::numeric_limits::is_iec559 || std::numeric_limits::is_integer || GLM_CONFIG_UNRESTRICTED_GENTYPE, "*mat4x4 constructor only takes float and integer types, 4th parameter type invalid."); + +# if !GLM_HAS_INITIALIZER_LISTS + this->value[0] = col_type(v1); + this->value[1] = col_type(v2); + this->value[2] = col_type(v3); + this->value[3] = col_type(v4); +# endif + } + + // -- Matrix conversions -- + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<4, 4, T, Q>::mat(mat<2, 2, T, Q> const& m) +# if GLM_HAS_INITIALIZER_LISTS + : value{col_type(m[0], 0, 0), col_type(m[1], 0, 0), col_type(0, 0, 1, 0), col_type(0, 0, 0, 1)} +# endif + { +# if !GLM_HAS_INITIALIZER_LISTS + this->value[0] = col_type(m[0], 0, 0); + this->value[1] = col_type(m[1], 0, 0); + this->value[2] = col_type(0, 0, 1, 0); + this->value[3] = col_type(0, 0, 0, 1); +# endif + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<4, 4, T, Q>::mat(mat<3, 3, T, Q> const& m) +# if GLM_HAS_INITIALIZER_LISTS + : value{col_type(m[0], 0), col_type(m[1], 0), col_type(m[2], 0), col_type(0, 0, 0, 1)} +# endif + { +# if !GLM_HAS_INITIALIZER_LISTS + this->value[0] = col_type(m[0], 0); + this->value[1] = col_type(m[1], 0); + this->value[2] = col_type(m[2], 0); + this->value[3] = col_type(0, 0, 0, 1); +# endif + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<4, 4, T, Q>::mat(mat<2, 3, T, Q> const& m) +# if GLM_HAS_INITIALIZER_LISTS + : value{col_type(m[0], 0), col_type(m[1], 0), col_type(0, 0, 1, 0), col_type(0, 0, 0, 1)} +# endif + { +# if !GLM_HAS_INITIALIZER_LISTS + this->value[0] = col_type(m[0], 0); + this->value[1] = col_type(m[1], 0); + this->value[2] = col_type(0, 0, 1, 0); + this->value[3] = col_type(0, 0, 0, 1); +# endif + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<4, 4, T, Q>::mat(mat<3, 2, T, Q> const& m) +# if GLM_HAS_INITIALIZER_LISTS + : value{col_type(m[0], 0, 0), col_type(m[1], 0, 0), col_type(m[2], 1, 0), col_type(0, 0, 0, 1)} +# endif + { +# if !GLM_HAS_INITIALIZER_LISTS + this->value[0] = col_type(m[0], 0, 0); + this->value[1] = col_type(m[1], 0, 0); + this->value[2] = col_type(m[2], 1, 0); + this->value[3] = col_type(0, 0, 0, 1); +# endif + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<4, 4, T, Q>::mat(mat<2, 4, T, Q> const& m) +# if GLM_HAS_INITIALIZER_LISTS + : value{col_type(m[0]), col_type(m[1]), col_type(0, 0, 1, 0), col_type(0, 0, 0, 1)} +# endif + { +# if !GLM_HAS_INITIALIZER_LISTS + this->value[0] = m[0]; + this->value[1] = m[1]; + this->value[2] = col_type(0, 0, 1, 0); + this->value[3] = col_type(0, 0, 0, 1); +# endif + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<4, 4, T, Q>::mat(mat<4, 2, T, Q> const& m) +# if GLM_HAS_INITIALIZER_LISTS + : value{col_type(m[0], 0, 0), col_type(m[1], 0, 0), col_type(0, 0, 1, 0), col_type(0, 0, 0, 1)} +# endif + { +# if !GLM_HAS_INITIALIZER_LISTS + this->value[0] = col_type(m[0], 0, 0); + this->value[1] = col_type(m[1], 0, 0); + this->value[2] = col_type(0, 0, 1, 0); + this->value[3] = col_type(0, 0, 0, 1); +# endif + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<4, 4, T, Q>::mat(mat<3, 4, T, Q> const& m) +# if GLM_HAS_INITIALIZER_LISTS + : value{col_type(m[0]), col_type(m[1]), col_type(m[2]), col_type(0, 0, 0, 1)} +# endif + { +# if !GLM_HAS_INITIALIZER_LISTS + this->value[0] = m[0]; + this->value[1] = m[1]; + this->value[2] = m[2]; + this->value[3] = col_type(0, 0, 0, 1); +# endif + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<4, 4, T, Q>::mat(mat<4, 3, T, Q> const& m) +# if GLM_HAS_INITIALIZER_LISTS + : value{col_type(m[0], 0), col_type(m[1], 0), col_type(m[2], 0), col_type(m[3], 1)} +# endif + { +# if !GLM_HAS_INITIALIZER_LISTS + this->value[0] = col_type(m[0], 0); + this->value[1] = col_type(m[1], 0); + this->value[2] = col_type(m[2], 0); + this->value[3] = col_type(m[3], 1); +# endif + } + + // -- Accesses -- + + template + GLM_FUNC_QUALIFIER typename mat<4, 4, T, Q>::col_type & mat<4, 4, T, Q>::operator[](typename mat<4, 4, T, Q>::length_type i) + { + assert(i < this->length()); + return this->value[i]; + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR typename mat<4, 4, T, Q>::col_type const& mat<4, 4, T, Q>::operator[](typename mat<4, 4, T, Q>::length_type i) const + { + assert(i < this->length()); + return this->value[i]; + } + + // -- Unary arithmetic operators -- + + template + template + GLM_FUNC_QUALIFIER mat<4, 4, T, Q>& mat<4, 4, T, Q>::operator=(mat<4, 4, U, Q> const& m) + { + //memcpy could be faster + //memcpy(&this->value, &m.value, 16 * sizeof(valType)); + this->value[0] = m[0]; + this->value[1] = m[1]; + this->value[2] = m[2]; + this->value[3] = m[3]; + return *this; + } + + template + template + GLM_FUNC_QUALIFIER mat<4, 4, T, Q>& mat<4, 4, T, Q>::operator+=(U s) + { + this->value[0] += s; + this->value[1] += s; + this->value[2] += s; + this->value[3] += s; + return *this; + } + + template + template + GLM_FUNC_QUALIFIER mat<4, 4, T, Q>& mat<4, 4, T, Q>::operator+=(mat<4, 4, U, Q> const& m) + { + this->value[0] += m[0]; + this->value[1] += m[1]; + this->value[2] += m[2]; + this->value[3] += m[3]; + return *this; + } + + template + template + GLM_FUNC_QUALIFIER mat<4, 4, T, Q> & mat<4, 4, T, Q>::operator-=(U s) + { + this->value[0] -= s; + this->value[1] -= s; + this->value[2] -= s; + this->value[3] -= s; + return *this; + } + + template + template + GLM_FUNC_QUALIFIER mat<4, 4, T, Q> & mat<4, 4, T, Q>::operator-=(mat<4, 4, U, Q> const& m) + { + this->value[0] -= m[0]; + this->value[1] -= m[1]; + this->value[2] -= m[2]; + this->value[3] -= m[3]; + return *this; + } + + template + template + GLM_FUNC_QUALIFIER mat<4, 4, T, Q> & mat<4, 4, T, Q>::operator*=(U s) + { + this->value[0] *= s; + this->value[1] *= s; + this->value[2] *= s; + this->value[3] *= s; + return *this; + } + + template + template + GLM_FUNC_QUALIFIER mat<4, 4, T, Q> & mat<4, 4, T, Q>::operator*=(mat<4, 4, U, Q> const& m) + { + return (*this = *this * m); + } + + template + template + GLM_FUNC_QUALIFIER mat<4, 4, T, Q> & mat<4, 4, T, Q>::operator/=(U s) + { + this->value[0] /= s; + this->value[1] /= s; + this->value[2] /= s; + this->value[3] /= s; + return *this; + } + + template + template + GLM_FUNC_QUALIFIER mat<4, 4, T, Q> & mat<4, 4, T, Q>::operator/=(mat<4, 4, U, Q> const& m) + { + return *this *= inverse(m); + } + + // -- Increment and decrement operators -- + + template + GLM_FUNC_QUALIFIER mat<4, 4, T, Q> & mat<4, 4, T, Q>::operator++() + { + ++this->value[0]; + ++this->value[1]; + ++this->value[2]; + ++this->value[3]; + return *this; + } + + template + GLM_FUNC_QUALIFIER mat<4, 4, T, Q> & mat<4, 4, T, Q>::operator--() + { + --this->value[0]; + --this->value[1]; + --this->value[2]; + --this->value[3]; + return *this; + } + + template + GLM_FUNC_QUALIFIER mat<4, 4, T, Q> mat<4, 4, T, Q>::operator++(int) + { + mat<4, 4, T, Q> Result(*this); + ++*this; + return Result; + } + + template + GLM_FUNC_QUALIFIER mat<4, 4, T, Q> mat<4, 4, T, Q>::operator--(int) + { + mat<4, 4, T, Q> Result(*this); + --*this; + return Result; + } + + // -- Unary constant operators -- + + template + GLM_FUNC_QUALIFIER mat<4, 4, T, Q> operator+(mat<4, 4, T, Q> const& m) + { + return m; + } + + template + GLM_FUNC_QUALIFIER mat<4, 4, T, Q> operator-(mat<4, 4, T, Q> const& m) + { + return mat<4, 4, T, Q>( + -m[0], + -m[1], + -m[2], + -m[3]); + } + + // -- Binary arithmetic operators -- + + template + GLM_FUNC_QUALIFIER mat<4, 4, T, Q> operator+(mat<4, 4, T, Q> const& m, T const& s) + { + return mat<4, 4, T, Q>( + m[0] + s, + m[1] + s, + m[2] + s, + m[3] + s); + } + + template + GLM_FUNC_QUALIFIER mat<4, 4, T, Q> operator+(T const& s, mat<4, 4, T, Q> const& m) + { + return mat<4, 4, T, Q>( + m[0] + s, + m[1] + s, + m[2] + s, + m[3] + s); + } + + template + GLM_FUNC_QUALIFIER mat<4, 4, T, Q> operator+(mat<4, 4, T, Q> const& m1, mat<4, 4, T, Q> const& m2) + { + return mat<4, 4, T, Q>( + m1[0] + m2[0], + m1[1] + m2[1], + m1[2] + m2[2], + m1[3] + m2[3]); + } + + template + GLM_FUNC_QUALIFIER mat<4, 4, T, Q> operator-(mat<4, 4, T, Q> const& m, T const& s) + { + return mat<4, 4, T, Q>( + m[0] - s, + m[1] - s, + m[2] - s, + m[3] - s); + } + + template + GLM_FUNC_QUALIFIER mat<4, 4, T, Q> operator-(T const& s, mat<4, 4, T, Q> const& m) + { + return mat<4, 4, T, Q>( + s - m[0], + s - m[1], + s - m[2], + s - m[3]); + } + + template + GLM_FUNC_QUALIFIER mat<4, 4, T, Q> operator-(mat<4, 4, T, Q> const& m1, mat<4, 4, T, Q> const& m2) + { + return mat<4, 4, T, Q>( + m1[0] - m2[0], + m1[1] - m2[1], + m1[2] - m2[2], + m1[3] - m2[3]); + } + + template + GLM_FUNC_QUALIFIER mat<4, 4, T, Q> operator*(mat<4, 4, T, Q> const& m, T const & s) + { + return mat<4, 4, T, Q>( + m[0] * s, + m[1] * s, + m[2] * s, + m[3] * s); + } + + template + GLM_FUNC_QUALIFIER mat<4, 4, T, Q> operator*(T const& s, mat<4, 4, T, Q> const& m) + { + return mat<4, 4, T, Q>( + m[0] * s, + m[1] * s, + m[2] * s, + m[3] * s); + } + + template + GLM_FUNC_QUALIFIER typename mat<4, 4, T, Q>::col_type operator* + ( + mat<4, 4, T, Q> const& m, + typename mat<4, 4, T, Q>::row_type const& v + ) + { +/* + __m128 v0 = _mm_shuffle_ps(v.data, v.data, _MM_SHUFFLE(0, 0, 0, 0)); + __m128 v1 = _mm_shuffle_ps(v.data, v.data, _MM_SHUFFLE(1, 1, 1, 1)); + __m128 v2 = _mm_shuffle_ps(v.data, v.data, _MM_SHUFFLE(2, 2, 2, 2)); + __m128 v3 = _mm_shuffle_ps(v.data, v.data, _MM_SHUFFLE(3, 3, 3, 3)); + + __m128 m0 = _mm_mul_ps(m[0].data, v0); + __m128 m1 = _mm_mul_ps(m[1].data, v1); + __m128 a0 = _mm_add_ps(m0, m1); + + __m128 m2 = _mm_mul_ps(m[2].data, v2); + __m128 m3 = _mm_mul_ps(m[3].data, v3); + __m128 a1 = _mm_add_ps(m2, m3); + + __m128 a2 = _mm_add_ps(a0, a1); + + return typename mat<4, 4, T, Q>::col_type(a2); +*/ + + typename mat<4, 4, T, Q>::col_type const Mov0(v[0]); + typename mat<4, 4, T, Q>::col_type const Mov1(v[1]); + typename mat<4, 4, T, Q>::col_type const Mul0 = m[0] * Mov0; + typename mat<4, 4, T, Q>::col_type const Mul1 = m[1] * Mov1; + typename mat<4, 4, T, Q>::col_type const Add0 = Mul0 + Mul1; + typename mat<4, 4, T, Q>::col_type const Mov2(v[2]); + typename mat<4, 4, T, Q>::col_type const Mov3(v[3]); + typename mat<4, 4, T, Q>::col_type const Mul2 = m[2] * Mov2; + typename mat<4, 4, T, Q>::col_type const Mul3 = m[3] * Mov3; + typename mat<4, 4, T, Q>::col_type const Add1 = Mul2 + Mul3; + typename mat<4, 4, T, Q>::col_type const Add2 = Add0 + Add1; + return Add2; + +/* + return typename mat<4, 4, T, Q>::col_type( + m[0][0] * v[0] + m[1][0] * v[1] + m[2][0] * v[2] + m[3][0] * v[3], + m[0][1] * v[0] + m[1][1] * v[1] + m[2][1] * v[2] + m[3][1] * v[3], + m[0][2] * v[0] + m[1][2] * v[1] + m[2][2] * v[2] + m[3][2] * v[3], + m[0][3] * v[0] + m[1][3] * v[1] + m[2][3] * v[2] + m[3][3] * v[3]); +*/ + } + + template + GLM_FUNC_QUALIFIER typename mat<4, 4, T, Q>::row_type operator* + ( + typename mat<4, 4, T, Q>::col_type const& v, + mat<4, 4, T, Q> const& m + ) + { + return typename mat<4, 4, T, Q>::row_type( + m[0][0] * v[0] + m[0][1] * v[1] + m[0][2] * v[2] + m[0][3] * v[3], + m[1][0] * v[0] + m[1][1] * v[1] + m[1][2] * v[2] + m[1][3] * v[3], + m[2][0] * v[0] + m[2][1] * v[1] + m[2][2] * v[2] + m[2][3] * v[3], + m[3][0] * v[0] + m[3][1] * v[1] + m[3][2] * v[2] + m[3][3] * v[3]); + } + + template + GLM_FUNC_QUALIFIER mat<2, 4, T, Q> operator*(mat<4, 4, T, Q> const& m1, mat<2, 4, T, Q> const& m2) + { + return mat<2, 4, T, Q>( + m1[0][0] * m2[0][0] + m1[1][0] * m2[0][1] + m1[2][0] * m2[0][2] + m1[3][0] * m2[0][3], + m1[0][1] * m2[0][0] + m1[1][1] * m2[0][1] + m1[2][1] * m2[0][2] + m1[3][1] * m2[0][3], + m1[0][2] * m2[0][0] + m1[1][2] * m2[0][1] + m1[2][2] * m2[0][2] + m1[3][2] * m2[0][3], + m1[0][3] * m2[0][0] + m1[1][3] * m2[0][1] + m1[2][3] * m2[0][2] + m1[3][3] * m2[0][3], + m1[0][0] * m2[1][0] + m1[1][0] * m2[1][1] + m1[2][0] * m2[1][2] + m1[3][0] * m2[1][3], + m1[0][1] * m2[1][0] + m1[1][1] * m2[1][1] + m1[2][1] * m2[1][2] + m1[3][1] * m2[1][3], + m1[0][2] * m2[1][0] + m1[1][2] * m2[1][1] + m1[2][2] * m2[1][2] + m1[3][2] * m2[1][3], + m1[0][3] * m2[1][0] + m1[1][3] * m2[1][1] + m1[2][3] * m2[1][2] + m1[3][3] * m2[1][3]); + } + + template + GLM_FUNC_QUALIFIER mat<3, 4, T, Q> operator*(mat<4, 4, T, Q> const& m1, mat<3, 4, T, Q> const& m2) + { + return mat<3, 4, T, Q>( + m1[0][0] * m2[0][0] + m1[1][0] * m2[0][1] + m1[2][0] * m2[0][2] + m1[3][0] * m2[0][3], + m1[0][1] * m2[0][0] + m1[1][1] * m2[0][1] + m1[2][1] * m2[0][2] + m1[3][1] * m2[0][3], + m1[0][2] * m2[0][0] + m1[1][2] * m2[0][1] + m1[2][2] * m2[0][2] + m1[3][2] * m2[0][3], + m1[0][3] * m2[0][0] + m1[1][3] * m2[0][1] + m1[2][3] * m2[0][2] + m1[3][3] * m2[0][3], + m1[0][0] * m2[1][0] + m1[1][0] * m2[1][1] + m1[2][0] * m2[1][2] + m1[3][0] * m2[1][3], + m1[0][1] * m2[1][0] + m1[1][1] * m2[1][1] + m1[2][1] * m2[1][2] + m1[3][1] * m2[1][3], + m1[0][2] * m2[1][0] + m1[1][2] * m2[1][1] + m1[2][2] * m2[1][2] + m1[3][2] * m2[1][3], + m1[0][3] * m2[1][0] + m1[1][3] * m2[1][1] + m1[2][3] * m2[1][2] + m1[3][3] * m2[1][3], + m1[0][0] * m2[2][0] + m1[1][0] * m2[2][1] + m1[2][0] * m2[2][2] + m1[3][0] * m2[2][3], + m1[0][1] * m2[2][0] + m1[1][1] * m2[2][1] + m1[2][1] * m2[2][2] + m1[3][1] * m2[2][3], + m1[0][2] * m2[2][0] + m1[1][2] * m2[2][1] + m1[2][2] * m2[2][2] + m1[3][2] * m2[2][3], + m1[0][3] * m2[2][0] + m1[1][3] * m2[2][1] + m1[2][3] * m2[2][2] + m1[3][3] * m2[2][3]); + } + + template + GLM_FUNC_QUALIFIER mat<4, 4, T, Q> operator*(mat<4, 4, T, Q> const& m1, mat<4, 4, T, Q> const& m2) + { + typename mat<4, 4, T, Q>::col_type const SrcA0 = m1[0]; + typename mat<4, 4, T, Q>::col_type const SrcA1 = m1[1]; + typename mat<4, 4, T, Q>::col_type const SrcA2 = m1[2]; + typename mat<4, 4, T, Q>::col_type const SrcA3 = m1[3]; + + typename mat<4, 4, T, Q>::col_type const SrcB0 = m2[0]; + typename mat<4, 4, T, Q>::col_type const SrcB1 = m2[1]; + typename mat<4, 4, T, Q>::col_type const SrcB2 = m2[2]; + typename mat<4, 4, T, Q>::col_type const SrcB3 = m2[3]; + + mat<4, 4, T, Q> Result; + Result[0] = SrcA0 * SrcB0[0] + SrcA1 * SrcB0[1] + SrcA2 * SrcB0[2] + SrcA3 * SrcB0[3]; + Result[1] = SrcA0 * SrcB1[0] + SrcA1 * SrcB1[1] + SrcA2 * SrcB1[2] + SrcA3 * SrcB1[3]; + Result[2] = SrcA0 * SrcB2[0] + SrcA1 * SrcB2[1] + SrcA2 * SrcB2[2] + SrcA3 * SrcB2[3]; + Result[3] = SrcA0 * SrcB3[0] + SrcA1 * SrcB3[1] + SrcA2 * SrcB3[2] + SrcA3 * SrcB3[3]; + return Result; + } + + template + GLM_FUNC_QUALIFIER mat<4, 4, T, Q> operator/(mat<4, 4, T, Q> const& m, T const& s) + { + return mat<4, 4, T, Q>( + m[0] / s, + m[1] / s, + m[2] / s, + m[3] / s); + } + + template + GLM_FUNC_QUALIFIER mat<4, 4, T, Q> operator/(T const& s, mat<4, 4, T, Q> const& m) + { + return mat<4, 4, T, Q>( + s / m[0], + s / m[1], + s / m[2], + s / m[3]); + } + + template + GLM_FUNC_QUALIFIER typename mat<4, 4, T, Q>::col_type operator/(mat<4, 4, T, Q> const& m, typename mat<4, 4, T, Q>::row_type const& v) + { + return inverse(m) * v; + } + + template + GLM_FUNC_QUALIFIER typename mat<4, 4, T, Q>::row_type operator/(typename mat<4, 4, T, Q>::col_type const& v, mat<4, 4, T, Q> const& m) + { + return v * inverse(m); + } + + template + GLM_FUNC_QUALIFIER mat<4, 4, T, Q> operator/(mat<4, 4, T, Q> const& m1, mat<4, 4, T, Q> const& m2) + { + mat<4, 4, T, Q> m1_copy(m1); + return m1_copy /= m2; + } + + // -- Boolean operators -- + + template + GLM_FUNC_QUALIFIER bool operator==(mat<4, 4, T, Q> const& m1, mat<4, 4, T, Q> const& m2) + { + return (m1[0] == m2[0]) && (m1[1] == m2[1]) && (m1[2] == m2[2]) && (m1[3] == m2[3]); + } + + template + GLM_FUNC_QUALIFIER bool operator!=(mat<4, 4, T, Q> const& m1, mat<4, 4, T, Q> const& m2) + { + return (m1[0] != m2[0]) || (m1[1] != m2[1]) || (m1[2] != m2[2]) || (m1[3] != m2[3]); + } +}//namespace glm + +#if GLM_CONFIG_SIMD == GLM_ENABLE +# include "type_mat4x4_simd.inl" +#endif diff --git a/src/GLMath/glm/detail/type_mat4x4_simd.inl b/src/GLMath/glm/detail/type_mat4x4_simd.inl new file mode 100644 index 000000000..fb3a16f06 --- /dev/null +++ b/src/GLMath/glm/detail/type_mat4x4_simd.inl @@ -0,0 +1,6 @@ +/// @ref core + +namespace glm +{ + +}//namespace glm diff --git a/src/GLMath/glm/detail/type_quat.hpp b/src/GLMath/glm/detail/type_quat.hpp new file mode 100644 index 000000000..b49c25348 --- /dev/null +++ b/src/GLMath/glm/detail/type_quat.hpp @@ -0,0 +1,190 @@ +/// @ref gtc_quaternion +/// @file glm/gtc/quaternion.hpp +/// +/// @see core (dependence) +/// @see gtc_constants (dependence) +/// +/// @defgroup gtc_quaternion GLM_GTC_quaternion +/// @ingroup gtc +/// +/// Include to use the features of this extension. +/// +/// Defines a templated quaternion type and several quaternion operations. + +#pragma once + +// Dependency: +#include "../detail/type_mat3x3.hpp" +#include "../detail/type_mat4x4.hpp" +#include "../detail/type_vec3.hpp" +#include "../detail/type_vec4.hpp" +#include "../ext/vector_relational.hpp" +#include "../ext/quaternion_relational.hpp" +#include "../gtc/constants.hpp" +#include "../gtc/matrix_transform.hpp" + +namespace glm +{ + /// @addtogroup gtc_quaternion + /// @{ + + template + struct qua + { + // -- Implementation detail -- + + typedef qua type; + typedef T value_type; + + // -- Data -- + +# if GLM_SILENT_WARNINGS == GLM_ENABLE +# if GLM_COMPILER & GLM_COMPILER_GCC +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Wpedantic" +# elif GLM_COMPILER & GLM_COMPILER_CLANG +# pragma clang diagnostic push +# pragma clang diagnostic ignored "-Wgnu-anonymous-struct" +# pragma clang diagnostic ignored "-Wnested-anon-types" +# elif GLM_COMPILER & GLM_COMPILER_VC +# pragma warning(push) +# pragma warning(disable: 4201) // nonstandard extension used : nameless struct/union +# endif +# endif + +# if GLM_LANG & GLM_LANG_CXXMS_FLAG + union + { + struct { T x, y, z, w;}; + + typename detail::storage<4, T, detail::is_aligned::value>::type data; + }; +# else + T x, y, z, w; +# endif + +# if GLM_SILENT_WARNINGS == GLM_ENABLE +# if GLM_COMPILER & GLM_COMPILER_CLANG +# pragma clang diagnostic pop +# elif GLM_COMPILER & GLM_COMPILER_GCC +# pragma GCC diagnostic pop +# elif GLM_COMPILER & GLM_COMPILER_VC +# pragma warning(pop) +# endif +# endif + + // -- Component accesses -- + + typedef length_t length_type; + /// Return the count of components of a quaternion + GLM_FUNC_DECL static GLM_CONSTEXPR length_type length(){return 4;} + + GLM_FUNC_DECL GLM_CONSTEXPR T & operator[](length_type i); + GLM_FUNC_DECL GLM_CONSTEXPR T const& operator[](length_type i) const; + + // -- Implicit basic constructors -- + + GLM_FUNC_DECL GLM_CONSTEXPR qua() GLM_DEFAULT; + GLM_FUNC_DECL GLM_CONSTEXPR qua(qua const& q) GLM_DEFAULT; + template + GLM_FUNC_DECL GLM_CONSTEXPR qua(qua const& q); + + // -- Explicit basic constructors -- + + GLM_FUNC_DECL GLM_CONSTEXPR qua(T s, vec<3, T, Q> const& v); + GLM_FUNC_DECL GLM_CONSTEXPR qua(T w, T x, T y, T z); + + // -- Conversion constructors -- + + template + GLM_FUNC_DECL GLM_CONSTEXPR GLM_EXPLICIT qua(qua const& q); + + /// Explicit conversion operators +# if GLM_HAS_EXPLICIT_CONVERSION_OPERATORS + GLM_FUNC_DECL explicit operator mat<3, 3, T, Q>(); + GLM_FUNC_DECL explicit operator mat<4, 4, T, Q>(); +# endif + + /// Create a quaternion from two normalized axis + /// + /// @param u A first normalized axis + /// @param v A second normalized axis + /// @see gtc_quaternion + /// @see http://lolengine.net/blog/2013/09/18/beautiful-maths-quaternion-from-vectors + GLM_FUNC_DECL qua(vec<3, T, Q> const& u, vec<3, T, Q> const& v); + + /// Build a quaternion from euler angles (pitch, yaw, roll), in radians. + GLM_FUNC_DECL GLM_EXPLICIT qua(vec<3, T, Q> const& eulerAngles); + GLM_FUNC_DECL GLM_EXPLICIT qua(mat<3, 3, T, Q> const& q); + GLM_FUNC_DECL GLM_EXPLICIT qua(mat<4, 4, T, Q> const& q); + + // -- Unary arithmetic operators -- + + GLM_FUNC_DECL qua& operator=(qua const& q) GLM_DEFAULT; + + template + GLM_FUNC_DECL qua& operator=(qua const& q); + template + GLM_FUNC_DECL qua& operator+=(qua const& q); + template + GLM_FUNC_DECL qua& operator-=(qua const& q); + template + GLM_FUNC_DECL qua& operator*=(qua const& q); + template + GLM_FUNC_DECL qua& operator*=(U s); + template + GLM_FUNC_DECL qua& operator/=(U s); + }; + + // -- Unary bit operators -- + + template + GLM_FUNC_DECL qua operator+(qua const& q); + + template + GLM_FUNC_DECL qua operator-(qua const& q); + + // -- Binary operators -- + + template + GLM_FUNC_DECL qua operator+(qua const& q, qua const& p); + + template + GLM_FUNC_DECL qua operator-(qua const& q, qua const& p); + + template + GLM_FUNC_DECL qua operator*(qua const& q, qua const& p); + + template + GLM_FUNC_DECL vec<3, T, Q> operator*(qua const& q, vec<3, T, Q> const& v); + + template + GLM_FUNC_DECL vec<3, T, Q> operator*(vec<3, T, Q> const& v, qua const& q); + + template + GLM_FUNC_DECL vec<4, T, Q> operator*(qua const& q, vec<4, T, Q> const& v); + + template + GLM_FUNC_DECL vec<4, T, Q> operator*(vec<4, T, Q> const& v, qua const& q); + + template + GLM_FUNC_DECL qua operator*(qua const& q, T const& s); + + template + GLM_FUNC_DECL qua operator*(T const& s, qua const& q); + + template + GLM_FUNC_DECL qua operator/(qua const& q, T const& s); + + // -- Boolean operators -- + + template + GLM_FUNC_DECL GLM_CONSTEXPR bool operator==(qua const& q1, qua const& q2); + + template + GLM_FUNC_DECL GLM_CONSTEXPR bool operator!=(qua const& q1, qua const& q2); + + /// @} +} //namespace glm + +#include "type_quat.inl" diff --git a/src/GLMath/glm/detail/type_quat.inl b/src/GLMath/glm/detail/type_quat.inl new file mode 100644 index 000000000..d9f63c674 --- /dev/null +++ b/src/GLMath/glm/detail/type_quat.inl @@ -0,0 +1,379 @@ +#include "../trigonometric.hpp" +#include "../exponential.hpp" +#include "../ext/quaternion_geometric.hpp" +#include + +namespace glm{ +namespace detail +{ + template + struct genTypeTrait > + { + static const genTypeEnum GENTYPE = GENTYPE_QUAT; + }; + + template + struct compute_dot, T, Aligned> + { + static GLM_FUNC_QUALIFIER T call(qua const& a, qua const& b) + { + vec<4, T, Q> tmp(a.x * b.x, a.y * b.y, a.z * b.z, a.w * b.w); + return (tmp.x + tmp.y) + (tmp.z + tmp.w); + } + }; + + template + struct compute_quat_add + { + static qua call(qua const& q, qua const& p) + { + return qua(q.w + p.w, q.x + p.x, q.y + p.y, q.z + p.z); + } + }; + + template + struct compute_quat_sub + { + static qua call(qua const& q, qua const& p) + { + return qua(q.w - p.w, q.x - p.x, q.y - p.y, q.z - p.z); + } + }; + + template + struct compute_quat_mul_scalar + { + static qua call(qua const& q, T s) + { + return qua(q.w * s, q.x * s, q.y * s, q.z * s); + } + }; + + template + struct compute_quat_div_scalar + { + static qua call(qua const& q, T s) + { + return qua(q.w / s, q.x / s, q.y / s, q.z / s); + } + }; + + template + struct compute_quat_mul_vec4 + { + static vec<4, T, Q> call(qua const& q, vec<4, T, Q> const& v) + { + return vec<4, T, Q>(q * vec<3, T, Q>(v), v.w); + } + }; +}//namespace detail + + // -- Component accesses -- + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR T & qua::operator[](typename qua::length_type i) + { + assert(i >= 0 && i < this->length()); + return (&x)[i]; + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR T const& qua::operator[](typename qua::length_type i) const + { + assert(i >= 0 && i < this->length()); + return (&x)[i]; + } + + // -- Implicit basic constructors -- + +# if GLM_CONFIG_DEFAULTED_FUNCTIONS == GLM_DISABLE + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR qua::qua() +# if GLM_CONFIG_CTOR_INIT != GLM_CTOR_INIT_DISABLE + : x(0), y(0), z(0), w(1) +# endif + {} + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR qua::qua(qua const& q) + : x(q.x), y(q.y), z(q.z), w(q.w) + {} +# endif + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR qua::qua(qua const& q) + : x(q.x), y(q.y), z(q.z), w(q.w) + {} + + // -- Explicit basic constructors -- + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR qua::qua(T s, vec<3, T, Q> const& v) + : x(v.x), y(v.y), z(v.z), w(s) + {} + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR qua::qua(T _w, T _x, T _y, T _z) + : x(_x), y(_y), z(_z), w(_w) + {} + + // -- Conversion constructors -- + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR qua::qua(qua const& q) + : x(static_cast(q.x)) + , y(static_cast(q.y)) + , z(static_cast(q.z)) + , w(static_cast(q.w)) + {} + + //template + //GLM_FUNC_QUALIFIER qua::qua + //( + // valType const& pitch, + // valType const& yaw, + // valType const& roll + //) + //{ + // vec<3, valType> eulerAngle(pitch * valType(0.5), yaw * valType(0.5), roll * valType(0.5)); + // vec<3, valType> c = glm::cos(eulerAngle * valType(0.5)); + // vec<3, valType> s = glm::sin(eulerAngle * valType(0.5)); + // + // this->w = c.x * c.y * c.z + s.x * s.y * s.z; + // this->x = s.x * c.y * c.z - c.x * s.y * s.z; + // this->y = c.x * s.y * c.z + s.x * c.y * s.z; + // this->z = c.x * c.y * s.z - s.x * s.y * c.z; + //} + + template + GLM_FUNC_QUALIFIER qua::qua(vec<3, T, Q> const& u, vec<3, T, Q> const& v) + { + T norm_u_norm_v = sqrt(dot(u, u) * dot(v, v)); + T real_part = norm_u_norm_v + dot(u, v); + vec<3, T, Q> t; + + if(real_part < static_cast(1.e-6f) * norm_u_norm_v) + { + // If u and v are exactly opposite, rotate 180 degrees + // around an arbitrary orthogonal axis. Axis normalisation + // can happen later, when we normalise the quaternion. + real_part = static_cast(0); + t = abs(u.x) > abs(u.z) ? vec<3, T, Q>(-u.y, u.x, static_cast(0)) : vec<3, T, Q>(static_cast(0), -u.z, u.y); + } + else + { + // Otherwise, build quaternion the standard way. + t = cross(u, v); + } + + *this = normalize(qua(real_part, t.x, t.y, t.z)); + } + + template + GLM_FUNC_QUALIFIER qua::qua(vec<3, T, Q> const& eulerAngle) + { + vec<3, T, Q> c = glm::cos(eulerAngle * T(0.5)); + vec<3, T, Q> s = glm::sin(eulerAngle * T(0.5)); + + this->w = c.x * c.y * c.z + s.x * s.y * s.z; + this->x = s.x * c.y * c.z - c.x * s.y * s.z; + this->y = c.x * s.y * c.z + s.x * c.y * s.z; + this->z = c.x * c.y * s.z - s.x * s.y * c.z; + } + + template + GLM_FUNC_QUALIFIER qua::qua(mat<3, 3, T, Q> const& m) + { + *this = quat_cast(m); + } + + template + GLM_FUNC_QUALIFIER qua::qua(mat<4, 4, T, Q> const& m) + { + *this = quat_cast(m); + } + +# if GLM_HAS_EXPLICIT_CONVERSION_OPERATORS + template + GLM_FUNC_QUALIFIER qua::operator mat<3, 3, T, Q>() + { + return mat3_cast(*this); + } + + template + GLM_FUNC_QUALIFIER qua::operator mat<4, 4, T, Q>() + { + return mat4_cast(*this); + } +# endif//GLM_HAS_EXPLICIT_CONVERSION_OPERATORS + + // -- Unary arithmetic operators -- + +# if GLM_CONFIG_DEFAULTED_FUNCTIONS == GLM_DISABLE + template + GLM_FUNC_QUALIFIER qua & qua::operator=(qua const& q) + { + this->w = q.w; + this->x = q.x; + this->y = q.y; + this->z = q.z; + return *this; + } +# endif + + template + template + GLM_FUNC_QUALIFIER qua & qua::operator=(qua const& q) + { + this->w = static_cast(q.w); + this->x = static_cast(q.x); + this->y = static_cast(q.y); + this->z = static_cast(q.z); + return *this; + } + + template + template + GLM_FUNC_QUALIFIER qua & qua::operator+=(qua const& q) + { + return (*this = detail::compute_quat_add::value>::call(*this, qua(q))); + } + + template + template + GLM_FUNC_QUALIFIER qua & qua::operator-=(qua const& q) + { + return (*this = detail::compute_quat_sub::value>::call(*this, qua(q))); + } + + template + template + GLM_FUNC_QUALIFIER qua & qua::operator*=(qua const& r) + { + qua const p(*this); + qua const q(r); + + this->w = p.w * q.w - p.x * q.x - p.y * q.y - p.z * q.z; + this->x = p.w * q.x + p.x * q.w + p.y * q.z - p.z * q.y; + this->y = p.w * q.y + p.y * q.w + p.z * q.x - p.x * q.z; + this->z = p.w * q.z + p.z * q.w + p.x * q.y - p.y * q.x; + return *this; + } + + template + template + GLM_FUNC_QUALIFIER qua & qua::operator*=(U s) + { + return (*this = detail::compute_quat_mul_scalar::value>::call(*this, static_cast(s))); + } + + template + template + GLM_FUNC_QUALIFIER qua & qua::operator/=(U s) + { + return (*this = detail::compute_quat_div_scalar::value>::call(*this, static_cast(s))); + } + + // -- Unary bit operators -- + + template + GLM_FUNC_QUALIFIER qua operator+(qua const& q) + { + return q; + } + + template + GLM_FUNC_QUALIFIER qua operator-(qua const& q) + { + return qua(-q.w, -q.x, -q.y, -q.z); + } + + // -- Binary operators -- + + template + GLM_FUNC_QUALIFIER qua operator+(qua const& q, qua const& p) + { + return qua(q) += p; + } + + template + GLM_FUNC_QUALIFIER qua operator-(qua const& q, qua const& p) + { + return qua(q) -= p; + } + + template + GLM_FUNC_QUALIFIER qua operator*(qua const& q, qua const& p) + { + return qua(q) *= p; + } + + template + GLM_FUNC_QUALIFIER vec<3, T, Q> operator*(qua const& q, vec<3, T, Q> const& v) + { + vec<3, T, Q> const QuatVector(q.x, q.y, q.z); + vec<3, T, Q> const uv(glm::cross(QuatVector, v)); + vec<3, T, Q> const uuv(glm::cross(QuatVector, uv)); + + return v + ((uv * q.w) + uuv) * static_cast(2); + } + + template + GLM_FUNC_QUALIFIER vec<3, T, Q> operator*(vec<3, T, Q> const& v, qua const& q) + { + return glm::inverse(q) * v; + } + + template + GLM_FUNC_QUALIFIER vec<4, T, Q> operator*(qua const& q, vec<4, T, Q> const& v) + { + return detail::compute_quat_mul_vec4::value>::call(q, v); + } + + template + GLM_FUNC_QUALIFIER vec<4, T, Q> operator*(vec<4, T, Q> const& v, qua const& q) + { + return glm::inverse(q) * v; + } + + template + GLM_FUNC_QUALIFIER qua operator*(qua const& q, T const& s) + { + return qua( + q.w * s, q.x * s, q.y * s, q.z * s); + } + + template + GLM_FUNC_QUALIFIER qua operator*(T const& s, qua const& q) + { + return q * s; + } + + template + GLM_FUNC_QUALIFIER qua operator/(qua const& q, T const& s) + { + return qua( + q.w / s, q.x / s, q.y / s, q.z / s); + } + + // -- Boolean operators -- + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR bool operator==(qua const& q1, qua const& q2) + { + return q1.x == q2.x && q1.y == q2.y && q1.z == q2.z && q1.w == q2.w; + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR bool operator!=(qua const& q1, qua const& q2) + { + return q1.x != q2.x || q1.y != q2.y || q1.z != q2.z || q1.w != q2.w; + } +}//namespace glm + +#if GLM_CONFIG_SIMD == GLM_ENABLE +# include "type_quat_simd.inl" +#endif + diff --git a/src/GLMath/glm/detail/type_quat_simd.inl b/src/GLMath/glm/detail/type_quat_simd.inl new file mode 100644 index 000000000..3333e59f1 --- /dev/null +++ b/src/GLMath/glm/detail/type_quat_simd.inl @@ -0,0 +1,188 @@ +/// @ref core + +#if GLM_ARCH & GLM_ARCH_SSE2_BIT + +namespace glm{ +namespace detail +{ +/* + template + struct compute_quat_mul + { + static qua call(qua const& q1, qua const& q2) + { + // SSE2 STATS: 11 shuffle, 8 mul, 8 add + // SSE4 STATS: 3 shuffle, 4 mul, 4 dpps + + __m128 const mul0 = _mm_mul_ps(q1.Data, _mm_shuffle_ps(q2.Data, q2.Data, _MM_SHUFFLE(0, 1, 2, 3))); + __m128 const mul1 = _mm_mul_ps(q1.Data, _mm_shuffle_ps(q2.Data, q2.Data, _MM_SHUFFLE(1, 0, 3, 2))); + __m128 const mul2 = _mm_mul_ps(q1.Data, _mm_shuffle_ps(q2.Data, q2.Data, _MM_SHUFFLE(2, 3, 0, 1))); + __m128 const mul3 = _mm_mul_ps(q1.Data, q2.Data); + +# if GLM_ARCH & GLM_ARCH_SSE41_BIT + __m128 const add0 = _mm_dp_ps(mul0, _mm_set_ps(1.0f, -1.0f, 1.0f, 1.0f), 0xff); + __m128 const add1 = _mm_dp_ps(mul1, _mm_set_ps(1.0f, 1.0f, 1.0f, -1.0f), 0xff); + __m128 const add2 = _mm_dp_ps(mul2, _mm_set_ps(1.0f, 1.0f, -1.0f, 1.0f), 0xff); + __m128 const add3 = _mm_dp_ps(mul3, _mm_set_ps(1.0f, -1.0f, -1.0f, -1.0f), 0xff); +# else + __m128 const mul4 = _mm_mul_ps(mul0, _mm_set_ps(1.0f, -1.0f, 1.0f, 1.0f)); + __m128 const add0 = _mm_add_ps(mul0, _mm_movehl_ps(mul4, mul4)); + __m128 const add4 = _mm_add_ss(add0, _mm_shuffle_ps(add0, add0, 1)); + + __m128 const mul5 = _mm_mul_ps(mul1, _mm_set_ps(1.0f, 1.0f, 1.0f, -1.0f)); + __m128 const add1 = _mm_add_ps(mul1, _mm_movehl_ps(mul5, mul5)); + __m128 const add5 = _mm_add_ss(add1, _mm_shuffle_ps(add1, add1, 1)); + + __m128 const mul6 = _mm_mul_ps(mul2, _mm_set_ps(1.0f, 1.0f, -1.0f, 1.0f)); + __m128 const add2 = _mm_add_ps(mul6, _mm_movehl_ps(mul6, mul6)); + __m128 const add6 = _mm_add_ss(add2, _mm_shuffle_ps(add2, add2, 1)); + + __m128 const mul7 = _mm_mul_ps(mul3, _mm_set_ps(1.0f, -1.0f, -1.0f, -1.0f)); + __m128 const add3 = _mm_add_ps(mul3, _mm_movehl_ps(mul7, mul7)); + __m128 const add7 = _mm_add_ss(add3, _mm_shuffle_ps(add3, add3, 1)); + #endif + + // This SIMD code is a politically correct way of doing this, but in every test I've tried it has been slower than + // the final code below. I'll keep this here for reference - maybe somebody else can do something better... + // + //__m128 xxyy = _mm_shuffle_ps(add4, add5, _MM_SHUFFLE(0, 0, 0, 0)); + //__m128 zzww = _mm_shuffle_ps(add6, add7, _MM_SHUFFLE(0, 0, 0, 0)); + // + //return _mm_shuffle_ps(xxyy, zzww, _MM_SHUFFLE(2, 0, 2, 0)); + + qua Result; + _mm_store_ss(&Result.x, add4); + _mm_store_ss(&Result.y, add5); + _mm_store_ss(&Result.z, add6); + _mm_store_ss(&Result.w, add7); + return Result; + } + }; +*/ + + template + struct compute_quat_add + { + static qua call(qua const& q, qua const& p) + { + qua Result; + Result.data = _mm_add_ps(q.data, p.data); + return Result; + } + }; + +# if GLM_ARCH & GLM_ARCH_AVX_BIT + template + struct compute_quat_add + { + static qua call(qua const& a, qua const& b) + { + qua Result; + Result.data = _mm256_add_pd(a.data, b.data); + return Result; + } + }; +# endif + + template + struct compute_quat_sub + { + static qua call(qua const& q, qua const& p) + { + vec<4, float, Q> Result; + Result.data = _mm_sub_ps(q.data, p.data); + return Result; + } + }; + +# if GLM_ARCH & GLM_ARCH_AVX_BIT + template + struct compute_quat_sub + { + static qua call(qua const& a, qua const& b) + { + qua Result; + Result.data = _mm256_sub_pd(a.data, b.data); + return Result; + } + }; +# endif + + template + struct compute_quat_mul_scalar + { + static qua call(qua const& q, float s) + { + vec<4, float, Q> Result; + Result.data = _mm_mul_ps(q.data, _mm_set_ps1(s)); + return Result; + } + }; + +# if GLM_ARCH & GLM_ARCH_AVX_BIT + template + struct compute_quat_mul_scalar + { + static qua call(qua const& q, double s) + { + qua Result; + Result.data = _mm256_mul_pd(q.data, _mm_set_ps1(s)); + return Result; + } + }; +# endif + + template + struct compute_quat_div_scalar + { + static qua call(qua const& q, float s) + { + vec<4, float, Q> Result; + Result.data = _mm_div_ps(q.data, _mm_set_ps1(s)); + return Result; + } + }; + +# if GLM_ARCH & GLM_ARCH_AVX_BIT + template + struct compute_quat_div_scalar + { + static qua call(qua const& q, double s) + { + qua Result; + Result.data = _mm256_div_pd(q.data, _mm_set_ps1(s)); + return Result; + } + }; +# endif + + template + struct compute_quat_mul_vec4 + { + static vec<4, float, Q> call(qua const& q, vec<4, float, Q> const& v) + { + __m128 const q_wwww = _mm_shuffle_ps(q.data, q.data, _MM_SHUFFLE(3, 3, 3, 3)); + __m128 const q_swp0 = _mm_shuffle_ps(q.data, q.data, _MM_SHUFFLE(3, 0, 2, 1)); + __m128 const q_swp1 = _mm_shuffle_ps(q.data, q.data, _MM_SHUFFLE(3, 1, 0, 2)); + __m128 const v_swp0 = _mm_shuffle_ps(v.data, v.data, _MM_SHUFFLE(3, 0, 2, 1)); + __m128 const v_swp1 = _mm_shuffle_ps(v.data, v.data, _MM_SHUFFLE(3, 1, 0, 2)); + + __m128 uv = _mm_sub_ps(_mm_mul_ps(q_swp0, v_swp1), _mm_mul_ps(q_swp1, v_swp0)); + __m128 uv_swp0 = _mm_shuffle_ps(uv, uv, _MM_SHUFFLE(3, 0, 2, 1)); + __m128 uv_swp1 = _mm_shuffle_ps(uv, uv, _MM_SHUFFLE(3, 1, 0, 2)); + __m128 uuv = _mm_sub_ps(_mm_mul_ps(q_swp0, uv_swp1), _mm_mul_ps(q_swp1, uv_swp0)); + + __m128 const two = _mm_set1_ps(2.0f); + uv = _mm_mul_ps(uv, _mm_mul_ps(q_wwww, two)); + uuv = _mm_mul_ps(uuv, two); + + vec<4, float, Q> Result; + Result.data = _mm_add_ps(v.Data, _mm_add_ps(uv, uuv)); + return Result; + } + }; +}//namespace detail +}//namespace glm + +#endif//GLM_ARCH & GLM_ARCH_SSE2_BIT + diff --git a/src/GLMath/glm/detail/type_vec1.hpp b/src/GLMath/glm/detail/type_vec1.hpp new file mode 100644 index 000000000..51163f14b --- /dev/null +++ b/src/GLMath/glm/detail/type_vec1.hpp @@ -0,0 +1,308 @@ +/// @ref core +/// @file glm/detail/type_vec1.hpp + +#pragma once + +#include "qualifier.hpp" +#if GLM_CONFIG_SWIZZLE == GLM_SWIZZLE_OPERATOR +# include "_swizzle.hpp" +#elif GLM_CONFIG_SWIZZLE == GLM_SWIZZLE_FUNCTION +# include "_swizzle_func.hpp" +#endif +#include + +namespace glm +{ + template + struct vec<1, T, Q> + { + // -- Implementation detail -- + + typedef T value_type; + typedef vec<1, T, Q> type; + typedef vec<1, bool, Q> bool_type; + + // -- Data -- + +# if GLM_SILENT_WARNINGS == GLM_ENABLE +# if GLM_COMPILER & GLM_COMPILER_GCC +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Wpedantic" +# elif GLM_COMPILER & GLM_COMPILER_CLANG +# pragma clang diagnostic push +# pragma clang diagnostic ignored "-Wgnu-anonymous-struct" +# pragma clang diagnostic ignored "-Wnested-anon-types" +# elif GLM_COMPILER & GLM_COMPILER_VC +# pragma warning(push) +# pragma warning(disable: 4201) // nonstandard extension used : nameless struct/union +# endif +# endif + +# if GLM_CONFIG_XYZW_ONLY + T x; +# elif GLM_CONFIG_ANONYMOUS_STRUCT == GLM_ENABLE + union + { + T x; + T r; + T s; + + typename detail::storage<1, T, detail::is_aligned::value>::type data; +/* +# if GLM_CONFIG_SWIZZLE == GLM_SWIZZLE_OPERATOR + _GLM_SWIZZLE1_2_MEMBERS(T, Q, x) + _GLM_SWIZZLE1_2_MEMBERS(T, Q, r) + _GLM_SWIZZLE1_2_MEMBERS(T, Q, s) + _GLM_SWIZZLE1_3_MEMBERS(T, Q, x) + _GLM_SWIZZLE1_3_MEMBERS(T, Q, r) + _GLM_SWIZZLE1_3_MEMBERS(T, Q, s) + _GLM_SWIZZLE1_4_MEMBERS(T, Q, x) + _GLM_SWIZZLE1_4_MEMBERS(T, Q, r) + _GLM_SWIZZLE1_4_MEMBERS(T, Q, s) +# endif +*/ + }; +# else + union {T x, r, s;}; +/* +# if GLM_CONFIG_SWIZZLE == GLM_SWIZZLE_FUNCTION + GLM_SWIZZLE_GEN_VEC_FROM_VEC1(T, Q) +# endif +*/ +# endif + +# if GLM_SILENT_WARNINGS == GLM_ENABLE +# if GLM_COMPILER & GLM_COMPILER_CLANG +# pragma clang diagnostic pop +# elif GLM_COMPILER & GLM_COMPILER_GCC +# pragma GCC diagnostic pop +# elif GLM_COMPILER & GLM_COMPILER_VC +# pragma warning(pop) +# endif +# endif + + // -- Component accesses -- + + /// Return the count of components of the vector + typedef length_t length_type; + GLM_FUNC_DECL static GLM_CONSTEXPR length_type length(){return 1;} + + GLM_FUNC_DECL GLM_CONSTEXPR T & operator[](length_type i); + GLM_FUNC_DECL GLM_CONSTEXPR T const& operator[](length_type i) const; + + // -- Implicit basic constructors -- + + GLM_FUNC_DECL GLM_CONSTEXPR vec() GLM_DEFAULT; + GLM_FUNC_DECL GLM_CONSTEXPR vec(vec const& v) GLM_DEFAULT; + template + GLM_FUNC_DECL GLM_CONSTEXPR vec(vec<1, T, P> const& v); + + // -- Explicit basic constructors -- + + GLM_FUNC_DECL GLM_CONSTEXPR explicit vec(T scalar); + + // -- Conversion vector constructors -- + + /// Explicit conversions (From section 5.4.1 Conversion and scalar constructors of GLSL 1.30.08 specification) + template + GLM_FUNC_DECL GLM_CONSTEXPR GLM_EXPLICIT vec(vec<2, U, P> const& v); + /// Explicit conversions (From section 5.4.1 Conversion and scalar constructors of GLSL 1.30.08 specification) + template + GLM_FUNC_DECL GLM_CONSTEXPR GLM_EXPLICIT vec(vec<3, U, P> const& v); + /// Explicit conversions (From section 5.4.1 Conversion and scalar constructors of GLSL 1.30.08 specification) + template + GLM_FUNC_DECL GLM_CONSTEXPR GLM_EXPLICIT vec(vec<4, U, P> const& v); + + /// Explicit conversions (From section 5.4.1 Conversion and scalar constructors of GLSL 1.30.08 specification) + template + GLM_FUNC_DECL GLM_CONSTEXPR GLM_EXPLICIT vec(vec<1, U, P> const& v); + + // -- Swizzle constructors -- +/* +# if GLM_CONFIG_SWIZZLE == GLM_SWIZZLE_OPERATOR + template + GLM_FUNC_DECL GLM_CONSTEXPR vec(detail::_swizzle<1, T, Q, E0, -1,-2,-3> const& that) + { + *this = that(); + } +# endif//GLM_CONFIG_SWIZZLE == GLM_SWIZZLE_OPERATOR +*/ + // -- Unary arithmetic operators -- + + GLM_FUNC_DECL GLM_CONSTEXPR vec<1, T, Q> & operator=(vec const& v) GLM_DEFAULT; + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<1, T, Q> & operator=(vec<1, U, Q> const& v); + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<1, T, Q> & operator+=(U scalar); + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<1, T, Q> & operator+=(vec<1, U, Q> const& v); + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<1, T, Q> & operator-=(U scalar); + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<1, T, Q> & operator-=(vec<1, U, Q> const& v); + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<1, T, Q> & operator*=(U scalar); + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<1, T, Q> & operator*=(vec<1, U, Q> const& v); + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<1, T, Q> & operator/=(U scalar); + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<1, T, Q> & operator/=(vec<1, U, Q> const& v); + + // -- Increment and decrement operators -- + + GLM_FUNC_DECL GLM_CONSTEXPR vec<1, T, Q> & operator++(); + GLM_FUNC_DECL GLM_CONSTEXPR vec<1, T, Q> & operator--(); + GLM_FUNC_DECL GLM_CONSTEXPR vec<1, T, Q> operator++(int); + GLM_FUNC_DECL GLM_CONSTEXPR vec<1, T, Q> operator--(int); + + // -- Unary bit operators -- + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<1, T, Q> & operator%=(U scalar); + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<1, T, Q> & operator%=(vec<1, U, Q> const& v); + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<1, T, Q> & operator&=(U scalar); + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<1, T, Q> & operator&=(vec<1, U, Q> const& v); + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<1, T, Q> & operator|=(U scalar); + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<1, T, Q> & operator|=(vec<1, U, Q> const& v); + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<1, T, Q> & operator^=(U scalar); + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<1, T, Q> & operator^=(vec<1, U, Q> const& v); + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<1, T, Q> & operator<<=(U scalar); + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<1, T, Q> & operator<<=(vec<1, U, Q> const& v); + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<1, T, Q> & operator>>=(U scalar); + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<1, T, Q> & operator>>=(vec<1, U, Q> const& v); + }; + + // -- Unary operators -- + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<1, T, Q> operator+(vec<1, T, Q> const& v); + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<1, T, Q> operator-(vec<1, T, Q> const& v); + + // -- Binary operators -- + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<1, T, Q> operator+(vec<1, T, Q> const& v, T scalar); + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<1, T, Q> operator+(T scalar, vec<1, T, Q> const& v); + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<1, T, Q> operator+(vec<1, T, Q> const& v1, vec<1, T, Q> const& v2); + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<1, T, Q> operator-(vec<1, T, Q> const& v, T scalar); + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<1, T, Q> operator-(T scalar, vec<1, T, Q> const& v); + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<1, T, Q> operator-(vec<1, T, Q> const& v1, vec<1, T, Q> const& v2); + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<1, T, Q> operator*(vec<1, T, Q> const& v, T scalar); + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<1, T, Q> operator*(T scalar, vec<1, T, Q> const& v); + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<1, T, Q> operator*(vec<1, T, Q> const& v1, vec<1, T, Q> const& v2); + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<1, T, Q> operator/(vec<1, T, Q> const& v, T scalar); + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<1, T, Q> operator/(T scalar, vec<1, T, Q> const& v); + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<1, T, Q> operator/(vec<1, T, Q> const& v1, vec<1, T, Q> const& v2); + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<1, T, Q> operator%(vec<1, T, Q> const& v, T scalar); + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<1, T, Q> operator%(T scalar, vec<1, T, Q> const& v); + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<1, T, Q> operator%(vec<1, T, Q> const& v1, vec<1, T, Q> const& v2); + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<1, T, Q> operator&(vec<1, T, Q> const& v, T scalar); + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<1, T, Q> operator&(T scalar, vec<1, T, Q> const& v); + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<1, T, Q> operator&(vec<1, T, Q> const& v1, vec<1, T, Q> const& v2); + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<1, T, Q> operator|(vec<1, T, Q> const& v, T scalar); + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<1, T, Q> operator|(T scalar, vec<1, T, Q> const& v); + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<1, T, Q> operator|(vec<1, T, Q> const& v1, vec<1, T, Q> const& v2); + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<1, T, Q> operator^(vec<1, T, Q> const& v, T scalar); + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<1, T, Q> operator^(T scalar, vec<1, T, Q> const& v); + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<1, T, Q> operator^(vec<1, T, Q> const& v1, vec<1, T, Q> const& v2); + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<1, T, Q> operator<<(vec<1, T, Q> const& v, T scalar); + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<1, T, Q> operator<<(T scalar, vec<1, T, Q> const& v); + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<1, T, Q> operator<<(vec<1, T, Q> const& v1, vec<1, T, Q> const& v2); + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<1, T, Q> operator>>(vec<1, T, Q> const& v, T scalar); + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<1, T, Q> operator>>(T scalar, vec<1, T, Q> const& v); + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<1, T, Q> operator>>(vec<1, T, Q> const& v1, vec<1, T, Q> const& v2); + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<1, T, Q> operator~(vec<1, T, Q> const& v); + + // -- Boolean operators -- + + template + GLM_FUNC_DECL GLM_CONSTEXPR bool operator==(vec<1, T, Q> const& v1, vec<1, T, Q> const& v2); + + template + GLM_FUNC_DECL GLM_CONSTEXPR bool operator!=(vec<1, T, Q> const& v1, vec<1, T, Q> const& v2); + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<1, bool, Q> operator&&(vec<1, bool, Q> const& v1, vec<1, bool, Q> const& v2); + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<1, bool, Q> operator||(vec<1, bool, Q> const& v1, vec<1, bool, Q> const& v2); +}//namespace glm + +#ifndef GLM_EXTERNAL_TEMPLATE +#include "type_vec1.inl" +#endif//GLM_EXTERNAL_TEMPLATE diff --git a/src/GLMath/glm/detail/type_vec1.inl b/src/GLMath/glm/detail/type_vec1.inl new file mode 100644 index 000000000..d0f49fd35 --- /dev/null +++ b/src/GLMath/glm/detail/type_vec1.inl @@ -0,0 +1,551 @@ +/// @ref core + +#include "./compute_vector_relational.hpp" + +namespace glm +{ + // -- Implicit basic constructors -- + +# if GLM_CONFIG_DEFAULTED_FUNCTIONS == GLM_DISABLE + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<1, T, Q>::vec() +# if GLM_CONFIG_CTOR_INIT != GLM_CTOR_INIT_DISABLE + : x(0) +# endif + {} + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<1, T, Q>::vec(vec<1, T, Q> const& v) + : x(v.x) + {} +# endif + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<1, T, Q>::vec(vec<1, T, P> const& v) + : x(v.x) + {} + + // -- Explicit basic constructors -- + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<1, T, Q>::vec(T scalar) + : x(scalar) + {} + + // -- Conversion vector constructors -- + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<1, T, Q>::vec(vec<1, U, P> const& v) + : x(static_cast(v.x)) + {} + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<1, T, Q>::vec(vec<2, U, P> const& v) + : x(static_cast(v.x)) + {} + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<1, T, Q>::vec(vec<3, U, P> const& v) + : x(static_cast(v.x)) + {} + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<1, T, Q>::vec(vec<4, U, P> const& v) + : x(static_cast(v.x)) + {} + + // -- Component accesses -- + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR T & vec<1, T, Q>::operator[](typename vec<1, T, Q>::length_type) + { + return x; + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR T const& vec<1, T, Q>::operator[](typename vec<1, T, Q>::length_type) const + { + return x; + } + + // -- Unary arithmetic operators -- + +# if GLM_CONFIG_DEFAULTED_FUNCTIONS == GLM_DISABLE + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<1, T, Q> & vec<1, T, Q>::operator=(vec<1, T, Q> const& v) + { + this->x = v.x; + return *this; + } +# endif + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<1, T, Q> & vec<1, T, Q>::operator=(vec<1, U, Q> const& v) + { + this->x = static_cast(v.x); + return *this; + } + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<1, T, Q> & vec<1, T, Q>::operator+=(U scalar) + { + this->x += static_cast(scalar); + return *this; + } + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<1, T, Q> & vec<1, T, Q>::operator+=(vec<1, U, Q> const& v) + { + this->x += static_cast(v.x); + return *this; + } + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<1, T, Q> & vec<1, T, Q>::operator-=(U scalar) + { + this->x -= static_cast(scalar); + return *this; + } + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<1, T, Q> & vec<1, T, Q>::operator-=(vec<1, U, Q> const& v) + { + this->x -= static_cast(v.x); + return *this; + } + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<1, T, Q> & vec<1, T, Q>::operator*=(U scalar) + { + this->x *= static_cast(scalar); + return *this; + } + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<1, T, Q> & vec<1, T, Q>::operator*=(vec<1, U, Q> const& v) + { + this->x *= static_cast(v.x); + return *this; + } + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<1, T, Q> & vec<1, T, Q>::operator/=(U scalar) + { + this->x /= static_cast(scalar); + return *this; + } + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<1, T, Q> & vec<1, T, Q>::operator/=(vec<1, U, Q> const& v) + { + this->x /= static_cast(v.x); + return *this; + } + + // -- Increment and decrement operators -- + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<1, T, Q> & vec<1, T, Q>::operator++() + { + ++this->x; + return *this; + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<1, T, Q> & vec<1, T, Q>::operator--() + { + --this->x; + return *this; + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<1, T, Q> vec<1, T, Q>::operator++(int) + { + vec<1, T, Q> Result(*this); + ++*this; + return Result; + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<1, T, Q> vec<1, T, Q>::operator--(int) + { + vec<1, T, Q> Result(*this); + --*this; + return Result; + } + + // -- Unary bit operators -- + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<1, T, Q> & vec<1, T, Q>::operator%=(U scalar) + { + this->x %= static_cast(scalar); + return *this; + } + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<1, T, Q> & vec<1, T, Q>::operator%=(vec<1, U, Q> const& v) + { + this->x %= static_cast(v.x); + return *this; + } + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<1, T, Q> & vec<1, T, Q>::operator&=(U scalar) + { + this->x &= static_cast(scalar); + return *this; + } + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<1, T, Q> & vec<1, T, Q>::operator&=(vec<1, U, Q> const& v) + { + this->x &= static_cast(v.x); + return *this; + } + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<1, T, Q> & vec<1, T, Q>::operator|=(U scalar) + { + this->x |= static_cast(scalar); + return *this; + } + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<1, T, Q> & vec<1, T, Q>::operator|=(vec<1, U, Q> const& v) + { + this->x |= U(v.x); + return *this; + } + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<1, T, Q> & vec<1, T, Q>::operator^=(U scalar) + { + this->x ^= static_cast(scalar); + return *this; + } + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<1, T, Q> & vec<1, T, Q>::operator^=(vec<1, U, Q> const& v) + { + this->x ^= static_cast(v.x); + return *this; + } + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<1, T, Q> & vec<1, T, Q>::operator<<=(U scalar) + { + this->x <<= static_cast(scalar); + return *this; + } + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<1, T, Q> & vec<1, T, Q>::operator<<=(vec<1, U, Q> const& v) + { + this->x <<= static_cast(v.x); + return *this; + } + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<1, T, Q> & vec<1, T, Q>::operator>>=(U scalar) + { + this->x >>= static_cast(scalar); + return *this; + } + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<1, T, Q> & vec<1, T, Q>::operator>>=(vec<1, U, Q> const& v) + { + this->x >>= static_cast(v.x); + return *this; + } + + // -- Unary constant operators -- + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<1, T, Q> operator+(vec<1, T, Q> const& v) + { + return v; + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<1, T, Q> operator-(vec<1, T, Q> const& v) + { + return vec<1, T, Q>( + -v.x); + } + + // -- Binary arithmetic operators -- + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<1, T, Q> operator+(vec<1, T, Q> const& v, T scalar) + { + return vec<1, T, Q>( + v.x + scalar); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<1, T, Q> operator+(T scalar, vec<1, T, Q> const& v) + { + return vec<1, T, Q>( + scalar + v.x); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<1, T, Q> operator+(vec<1, T, Q> const& v1, vec<1, T, Q> const& v2) + { + return vec<1, T, Q>( + v1.x + v2.x); + } + + //operator- + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<1, T, Q> operator-(vec<1, T, Q> const& v, T scalar) + { + return vec<1, T, Q>( + v.x - scalar); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<1, T, Q> operator-(T scalar, vec<1, T, Q> const& v) + { + return vec<1, T, Q>( + scalar - v.x); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<1, T, Q> operator-(vec<1, T, Q> const& v1, vec<1, T, Q> const& v2) + { + return vec<1, T, Q>( + v1.x - v2.x); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<1, T, Q> operator*(vec<1, T, Q> const& v, T scalar) + { + return vec<1, T, Q>( + v.x * scalar); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<1, T, Q> operator*(T scalar, vec<1, T, Q> const& v) + { + return vec<1, T, Q>( + scalar * v.x); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<1, T, Q> operator*(vec<1, T, Q> const& v1, vec<1, T, Q> const& v2) + { + return vec<1, T, Q>( + v1.x * v2.x); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<1, T, Q> operator/(vec<1, T, Q> const& v, T scalar) + { + return vec<1, T, Q>( + v.x / scalar); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<1, T, Q> operator/(T scalar, vec<1, T, Q> const& v) + { + return vec<1, T, Q>( + scalar / v.x); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<1, T, Q> operator/(vec<1, T, Q> const& v1, vec<1, T, Q> const& v2) + { + return vec<1, T, Q>( + v1.x / v2.x); + } + + // -- Binary bit operators -- + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<1, T, Q> operator%(vec<1, T, Q> const& v, T scalar) + { + return vec<1, T, Q>( + v.x % scalar); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<1, T, Q> operator%(T scalar, vec<1, T, Q> const& v) + { + return vec<1, T, Q>( + scalar % v.x); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<1, T, Q> operator%(vec<1, T, Q> const& v1, vec<1, T, Q> const& v2) + { + return vec<1, T, Q>( + v1.x % v2.x); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<1, T, Q> operator&(vec<1, T, Q> const& v, T scalar) + { + return vec<1, T, Q>( + v.x & scalar); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<1, T, Q> operator&(T scalar, vec<1, T, Q> const& v) + { + return vec<1, T, Q>( + scalar & v.x); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<1, T, Q> operator&(vec<1, T, Q> const& v1, vec<1, T, Q> const& v2) + { + return vec<1, T, Q>( + v1.x & v2.x); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<1, T, Q> operator|(vec<1, T, Q> const& v, T scalar) + { + return vec<1, T, Q>( + v.x | scalar); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<1, T, Q> operator|(T scalar, vec<1, T, Q> const& v) + { + return vec<1, T, Q>( + scalar | v.x); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<1, T, Q> operator|(vec<1, T, Q> const& v1, vec<1, T, Q> const& v2) + { + return vec<1, T, Q>( + v1.x | v2.x); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<1, T, Q> operator^(vec<1, T, Q> const& v, T scalar) + { + return vec<1, T, Q>( + v.x ^ scalar); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<1, T, Q> operator^(T scalar, vec<1, T, Q> const& v) + { + return vec<1, T, Q>( + scalar ^ v.x); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<1, T, Q> operator^(vec<1, T, Q> const& v1, vec<1, T, Q> const& v2) + { + return vec<1, T, Q>( + v1.x ^ v2.x); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<1, T, Q> operator<<(vec<1, T, Q> const& v, T scalar) + { + return vec<1, T, Q>( + static_cast(v.x << scalar)); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<1, T, Q> operator<<(T scalar, vec<1, T, Q> const& v) + { + return vec<1, T, Q>( + scalar << v.x); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<1, T, Q> operator<<(vec<1, T, Q> const& v1, vec<1, T, Q> const& v2) + { + return vec<1, T, Q>( + v1.x << v2.x); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<1, T, Q> operator>>(vec<1, T, Q> const& v, T scalar) + { + return vec<1, T, Q>( + v.x >> scalar); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<1, T, Q> operator>>(T scalar, vec<1, T, Q> const& v) + { + return vec<1, T, Q>( + scalar >> v.x); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<1, T, Q> operator>>(vec<1, T, Q> const& v1, vec<1, T, Q> const& v2) + { + return vec<1, T, Q>( + v1.x >> v2.x); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<1, T, Q> operator~(vec<1, T, Q> const& v) + { + return vec<1, T, Q>( + ~v.x); + } + + // -- Boolean operators -- + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR bool operator==(vec<1, T, Q> const& v1, vec<1, T, Q> const& v2) + { + return detail::compute_equal::is_iec559>::call(v1.x, v2.x); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR bool operator!=(vec<1, T, Q> const& v1, vec<1, T, Q> const& v2) + { + return !(v1 == v2); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<1, bool, Q> operator&&(vec<1, bool, Q> const& v1, vec<1, bool, Q> const& v2) + { + return vec<1, bool, Q>(v1.x && v2.x); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<1, bool, Q> operator||(vec<1, bool, Q> const& v1, vec<1, bool, Q> const& v2) + { + return vec<1, bool, Q>(v1.x || v2.x); + } +}//namespace glm diff --git a/src/GLMath/glm/detail/type_vec2.hpp b/src/GLMath/glm/detail/type_vec2.hpp new file mode 100644 index 000000000..52ef408e5 --- /dev/null +++ b/src/GLMath/glm/detail/type_vec2.hpp @@ -0,0 +1,399 @@ +/// @ref core +/// @file glm/detail/type_vec2.hpp + +#pragma once + +#include "qualifier.hpp" +#if GLM_CONFIG_SWIZZLE == GLM_SWIZZLE_OPERATOR +# include "_swizzle.hpp" +#elif GLM_CONFIG_SWIZZLE == GLM_SWIZZLE_FUNCTION +# include "_swizzle_func.hpp" +#endif +#include + +namespace glm +{ + template + struct vec<2, T, Q> + { + // -- Implementation detail -- + + typedef T value_type; + typedef vec<2, T, Q> type; + typedef vec<2, bool, Q> bool_type; + + // -- Data -- + +# if GLM_SILENT_WARNINGS == GLM_ENABLE +# if GLM_COMPILER & GLM_COMPILER_GCC +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Wpedantic" +# elif GLM_COMPILER & GLM_COMPILER_CLANG +# pragma clang diagnostic push +# pragma clang diagnostic ignored "-Wgnu-anonymous-struct" +# pragma clang diagnostic ignored "-Wnested-anon-types" +# elif GLM_COMPILER & GLM_COMPILER_VC +# pragma warning(push) +# pragma warning(disable: 4201) // nonstandard extension used : nameless struct/union +# endif +# endif + +# if GLM_CONFIG_XYZW_ONLY + T x, y; +# elif GLM_CONFIG_ANONYMOUS_STRUCT == GLM_ENABLE + union + { + struct{ T x, y; }; + struct{ T r, g; }; + struct{ T s, t; }; + + typename detail::storage<2, T, detail::is_aligned::value>::type data; + +# if GLM_CONFIG_SWIZZLE == GLM_SWIZZLE_OPERATOR + GLM_SWIZZLE2_2_MEMBERS(T, Q, x, y) + GLM_SWIZZLE2_2_MEMBERS(T, Q, r, g) + GLM_SWIZZLE2_2_MEMBERS(T, Q, s, t) + GLM_SWIZZLE2_3_MEMBERS(T, Q, x, y) + GLM_SWIZZLE2_3_MEMBERS(T, Q, r, g) + GLM_SWIZZLE2_3_MEMBERS(T, Q, s, t) + GLM_SWIZZLE2_4_MEMBERS(T, Q, x, y) + GLM_SWIZZLE2_4_MEMBERS(T, Q, r, g) + GLM_SWIZZLE2_4_MEMBERS(T, Q, s, t) +# endif + }; +# else + union {T x, r, s;}; + union {T y, g, t;}; + +# if GLM_CONFIG_SWIZZLE == GLM_SWIZZLE_FUNCTION + GLM_SWIZZLE_GEN_VEC_FROM_VEC2(T, Q) +# endif//GLM_CONFIG_SWIZZLE +# endif + +# if GLM_SILENT_WARNINGS == GLM_ENABLE +# if GLM_COMPILER & GLM_COMPILER_CLANG +# pragma clang diagnostic pop +# elif GLM_COMPILER & GLM_COMPILER_GCC +# pragma GCC diagnostic pop +# elif GLM_COMPILER & GLM_COMPILER_VC +# pragma warning(pop) +# endif +# endif + + // -- Component accesses -- + + /// Return the count of components of the vector + typedef length_t length_type; + GLM_FUNC_DECL static GLM_CONSTEXPR length_type length(){return 2;} + + GLM_FUNC_DECL GLM_CONSTEXPR T& operator[](length_type i); + GLM_FUNC_DECL GLM_CONSTEXPR T const& operator[](length_type i) const; + + // -- Implicit basic constructors -- + + GLM_FUNC_DECL GLM_CONSTEXPR vec() GLM_DEFAULT; + GLM_FUNC_DECL GLM_CONSTEXPR vec(vec const& v) GLM_DEFAULT; + template + GLM_FUNC_DECL GLM_CONSTEXPR vec(vec<2, T, P> const& v); + + // -- Explicit basic constructors -- + + GLM_FUNC_DECL GLM_CONSTEXPR explicit vec(T scalar); + GLM_FUNC_DECL GLM_CONSTEXPR vec(T x, T y); + + // -- Conversion constructors -- + + template + GLM_FUNC_DECL GLM_CONSTEXPR explicit vec(vec<1, U, P> const& v); + + /// Explicit conversions (From section 5.4.1 Conversion and scalar constructors of GLSL 1.30.08 specification) + template + GLM_FUNC_DECL GLM_CONSTEXPR vec(A x, B y); + template + GLM_FUNC_DECL GLM_CONSTEXPR vec(vec<1, A, Q> const& x, B y); + template + GLM_FUNC_DECL GLM_CONSTEXPR vec(A x, vec<1, B, Q> const& y); + template + GLM_FUNC_DECL GLM_CONSTEXPR vec(vec<1, A, Q> const& x, vec<1, B, Q> const& y); + + // -- Conversion vector constructors -- + + /// Explicit conversions (From section 5.4.1 Conversion and scalar constructors of GLSL 1.30.08 specification) + template + GLM_FUNC_DECL GLM_CONSTEXPR GLM_EXPLICIT vec(vec<3, U, P> const& v); + /// Explicit conversions (From section 5.4.1 Conversion and scalar constructors of GLSL 1.30.08 specification) + template + GLM_FUNC_DECL GLM_CONSTEXPR GLM_EXPLICIT vec(vec<4, U, P> const& v); + + /// Explicit conversions (From section 5.4.1 Conversion and scalar constructors of GLSL 1.30.08 specification) + template + GLM_FUNC_DECL GLM_CONSTEXPR GLM_EXPLICIT vec(vec<2, U, P> const& v); + + // -- Swizzle constructors -- +# if GLM_CONFIG_SWIZZLE == GLM_SWIZZLE_OPERATOR + template + GLM_FUNC_DECL GLM_CONSTEXPR vec(detail::_swizzle<2, T, Q, E0, E1,-1,-2> const& that) + { + *this = that(); + } +# endif//GLM_CONFIG_SWIZZLE == GLM_SWIZZLE_OPERATOR + + // -- Unary arithmetic operators -- + + GLM_FUNC_DECL GLM_CONSTEXPR vec<2, T, Q> & operator=(vec const& v) GLM_DEFAULT; + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<2, T, Q> & operator=(vec<2, U, Q> const& v); + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<2, T, Q> & operator+=(U scalar); + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<2, T, Q> & operator+=(vec<1, U, Q> const& v); + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<2, T, Q> & operator+=(vec<2, U, Q> const& v); + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<2, T, Q> & operator-=(U scalar); + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<2, T, Q> & operator-=(vec<1, U, Q> const& v); + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<2, T, Q> & operator-=(vec<2, U, Q> const& v); + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<2, T, Q> & operator*=(U scalar); + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<2, T, Q> & operator*=(vec<1, U, Q> const& v); + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<2, T, Q> & operator*=(vec<2, U, Q> const& v); + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<2, T, Q> & operator/=(U scalar); + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<2, T, Q> & operator/=(vec<1, U, Q> const& v); + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<2, T, Q> & operator/=(vec<2, U, Q> const& v); + + // -- Increment and decrement operators -- + + GLM_FUNC_DECL GLM_CONSTEXPR vec<2, T, Q> & operator++(); + GLM_FUNC_DECL GLM_CONSTEXPR vec<2, T, Q> & operator--(); + GLM_FUNC_DECL GLM_CONSTEXPR vec<2, T, Q> operator++(int); + GLM_FUNC_DECL GLM_CONSTEXPR vec<2, T, Q> operator--(int); + + // -- Unary bit operators -- + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<2, T, Q> & operator%=(U scalar); + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<2, T, Q> & operator%=(vec<1, U, Q> const& v); + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<2, T, Q> & operator%=(vec<2, U, Q> const& v); + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<2, T, Q> & operator&=(U scalar); + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<2, T, Q> & operator&=(vec<1, U, Q> const& v); + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<2, T, Q> & operator&=(vec<2, U, Q> const& v); + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<2, T, Q> & operator|=(U scalar); + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<2, T, Q> & operator|=(vec<1, U, Q> const& v); + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<2, T, Q> & operator|=(vec<2, U, Q> const& v); + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<2, T, Q> & operator^=(U scalar); + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<2, T, Q> & operator^=(vec<1, U, Q> const& v); + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<2, T, Q> & operator^=(vec<2, U, Q> const& v); + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<2, T, Q> & operator<<=(U scalar); + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<2, T, Q> & operator<<=(vec<1, U, Q> const& v); + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<2, T, Q> & operator<<=(vec<2, U, Q> const& v); + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<2, T, Q> & operator>>=(U scalar); + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<2, T, Q> & operator>>=(vec<1, U, Q> const& v); + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<2, T, Q> & operator>>=(vec<2, U, Q> const& v); + }; + + // -- Unary operators -- + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<2, T, Q> operator+(vec<2, T, Q> const& v); + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<2, T, Q> operator-(vec<2, T, Q> const& v); + + // -- Binary operators -- + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<2, T, Q> operator+(vec<2, T, Q> const& v, T scalar); + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<2, T, Q> operator+(vec<2, T, Q> const& v1, vec<1, T, Q> const& v2); + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<2, T, Q> operator+(T scalar, vec<2, T, Q> const& v); + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<2, T, Q> operator+(vec<1, T, Q> const& v1, vec<2, T, Q> const& v2); + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<2, T, Q> operator+(vec<2, T, Q> const& v1, vec<2, T, Q> const& v2); + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<2, T, Q> operator-(vec<2, T, Q> const& v, T scalar); + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<2, T, Q> operator-(vec<2, T, Q> const& v1, vec<1, T, Q> const& v2); + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<2, T, Q> operator-(T scalar, vec<2, T, Q> const& v); + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<2, T, Q> operator-(vec<1, T, Q> const& v1, vec<2, T, Q> const& v2); + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<2, T, Q> operator-(vec<2, T, Q> const& v1, vec<2, T, Q> const& v2); + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<2, T, Q> operator*(vec<2, T, Q> const& v, T scalar); + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<2, T, Q> operator*(vec<2, T, Q> const& v1, vec<1, T, Q> const& v2); + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<2, T, Q> operator*(T scalar, vec<2, T, Q> const& v); + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<2, T, Q> operator*(vec<1, T, Q> const& v1, vec<2, T, Q> const& v2); + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<2, T, Q> operator*(vec<2, T, Q> const& v1, vec<2, T, Q> const& v2); + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<2, T, Q> operator/(vec<2, T, Q> const& v, T scalar); + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<2, T, Q> operator/(vec<2, T, Q> const& v1, vec<1, T, Q> const& v2); + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<2, T, Q> operator/(T scalar, vec<2, T, Q> const& v); + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<2, T, Q> operator/(vec<1, T, Q> const& v1, vec<2, T, Q> const& v2); + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<2, T, Q> operator/(vec<2, T, Q> const& v1, vec<2, T, Q> const& v2); + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<2, T, Q> operator%(vec<2, T, Q> const& v, T scalar); + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<2, T, Q> operator%(vec<2, T, Q> const& v1, vec<1, T, Q> const& v2); + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<2, T, Q> operator%(T scalar, vec<2, T, Q> const& v); + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<2, T, Q> operator%(vec<1, T, Q> const& v1, vec<2, T, Q> const& v2); + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<2, T, Q> operator%(vec<2, T, Q> const& v1, vec<2, T, Q> const& v2); + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<2, T, Q> operator&(vec<2, T, Q> const& v, T scalar); + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<2, T, Q> operator&(vec<2, T, Q> const& v1, vec<1, T, Q> const& v2); + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<2, T, Q> operator&(T scalar, vec<2, T, Q> const& v); + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<2, T, Q> operator&(vec<1, T, Q> const& v1, vec<2, T, Q> const& v2); + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<2, T, Q> operator&(vec<2, T, Q> const& v1, vec<2, T, Q> const& v2); + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<2, T, Q> operator|(vec<2, T, Q> const& v, T scalar); + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<2, T, Q> operator|(vec<2, T, Q> const& v1, vec<1, T, Q> const& v2); + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<2, T, Q> operator|(T scalar, vec<2, T, Q> const& v); + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<2, T, Q> operator|(vec<1, T, Q> const& v1, vec<2, T, Q> const& v2); + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<2, T, Q> operator|(vec<2, T, Q> const& v1, vec<2, T, Q> const& v2); + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<2, T, Q> operator^(vec<2, T, Q> const& v, T scalar); + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<2, T, Q> operator^(vec<2, T, Q> const& v1, vec<1, T, Q> const& v2); + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<2, T, Q> operator^(T scalar, vec<2, T, Q> const& v); + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<2, T, Q> operator^(vec<1, T, Q> const& v1, vec<2, T, Q> const& v2); + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<2, T, Q> operator^(vec<2, T, Q> const& v1, vec<2, T, Q> const& v2); + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<2, T, Q> operator<<(vec<2, T, Q> const& v, T scalar); + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<2, T, Q> operator<<(vec<2, T, Q> const& v1, vec<1, T, Q> const& v2); + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<2, T, Q> operator<<(T scalar, vec<2, T, Q> const& v); + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<2, T, Q> operator<<(vec<1, T, Q> const& v1, vec<2, T, Q> const& v2); + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<2, T, Q> operator<<(vec<2, T, Q> const& v1, vec<2, T, Q> const& v2); + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<2, T, Q> operator>>(vec<2, T, Q> const& v, T scalar); + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<2, T, Q> operator>>(vec<2, T, Q> const& v1, vec<1, T, Q> const& v2); + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<2, T, Q> operator>>(T scalar, vec<2, T, Q> const& v); + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<2, T, Q> operator>>(vec<1, T, Q> const& v1, vec<2, T, Q> const& v2); + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<2, T, Q> operator>>(vec<2, T, Q> const& v1, vec<2, T, Q> const& v2); + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<2, T, Q> operator~(vec<2, T, Q> const& v); + + // -- Boolean operators -- + + template + GLM_FUNC_DECL GLM_CONSTEXPR bool operator==(vec<2, T, Q> const& v1, vec<2, T, Q> const& v2); + + template + GLM_FUNC_DECL GLM_CONSTEXPR bool operator!=(vec<2, T, Q> const& v1, vec<2, T, Q> const& v2); + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<2, bool, Q> operator&&(vec<2, bool, Q> const& v1, vec<2, bool, Q> const& v2); + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<2, bool, Q> operator||(vec<2, bool, Q> const& v1, vec<2, bool, Q> const& v2); +}//namespace glm + +#ifndef GLM_EXTERNAL_TEMPLATE +#include "type_vec2.inl" +#endif//GLM_EXTERNAL_TEMPLATE diff --git a/src/GLMath/glm/detail/type_vec2.inl b/src/GLMath/glm/detail/type_vec2.inl new file mode 100644 index 000000000..8e65d6bb9 --- /dev/null +++ b/src/GLMath/glm/detail/type_vec2.inl @@ -0,0 +1,913 @@ +/// @ref core + +#include "./compute_vector_relational.hpp" + +namespace glm +{ + // -- Implicit basic constructors -- + +# if GLM_CONFIG_DEFAULTED_FUNCTIONS == GLM_DISABLE + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<2, T, Q>::vec() +# if GLM_CONFIG_CTOR_INIT != GLM_CTOR_INIT_DISABLE + : x(0), y(0) +# endif + {} + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<2, T, Q>::vec(vec<2, T, Q> const& v) + : x(v.x), y(v.y) + {} +# endif + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<2, T, Q>::vec(vec<2, T, P> const& v) + : x(v.x), y(v.y) + {} + + // -- Explicit basic constructors -- + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<2, T, Q>::vec(T scalar) + : x(scalar), y(scalar) + {} + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<2, T, Q>::vec(T _x, T _y) + : x(_x), y(_y) + {} + + // -- Conversion scalar constructors -- + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<2, T, Q>::vec(vec<1, U, P> const& v) + : x(static_cast(v.x)) + , y(static_cast(v.x)) + {} + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<2, T, Q>::vec(A _x, B _y) + : x(static_cast(_x)) + , y(static_cast(_y)) + {} + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<2, T, Q>::vec(vec<1, A, Q> const& _x, B _y) + : x(static_cast(_x.x)) + , y(static_cast(_y)) + {} + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<2, T, Q>::vec(A _x, vec<1, B, Q> const& _y) + : x(static_cast(_x)) + , y(static_cast(_y.x)) + {} + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<2, T, Q>::vec(vec<1, A, Q> const& _x, vec<1, B, Q> const& _y) + : x(static_cast(_x.x)) + , y(static_cast(_y.x)) + {} + + // -- Conversion vector constructors -- + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<2, T, Q>::vec(vec<2, U, P> const& v) + : x(static_cast(v.x)) + , y(static_cast(v.y)) + {} + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<2, T, Q>::vec(vec<3, U, P> const& v) + : x(static_cast(v.x)) + , y(static_cast(v.y)) + {} + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<2, T, Q>::vec(vec<4, U, P> const& v) + : x(static_cast(v.x)) + , y(static_cast(v.y)) + {} + + // -- Component accesses -- + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR T & vec<2, T, Q>::operator[](typename vec<2, T, Q>::length_type i) + { + assert(i >= 0 && i < this->length()); + switch(i) + { + default: + case 0: + return x; + case 1: + return y; + } + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR T const& vec<2, T, Q>::operator[](typename vec<2, T, Q>::length_type i) const + { + assert(i >= 0 && i < this->length()); + switch(i) + { + default: + case 0: + return x; + case 1: + return y; + } + } + + // -- Unary arithmetic operators -- + +# if GLM_CONFIG_DEFAULTED_FUNCTIONS == GLM_DISABLE + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<2, T, Q> & vec<2, T, Q>::operator=(vec<2, T, Q> const& v) + { + this->x = v.x; + this->y = v.y; + return *this; + } +# endif + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<2, T, Q> & vec<2, T, Q>::operator=(vec<2, U, Q> const& v) + { + this->x = static_cast(v.x); + this->y = static_cast(v.y); + return *this; + } + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<2, T, Q> & vec<2, T, Q>::operator+=(U scalar) + { + this->x += static_cast(scalar); + this->y += static_cast(scalar); + return *this; + } + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<2, T, Q> & vec<2, T, Q>::operator+=(vec<1, U, Q> const& v) + { + this->x += static_cast(v.x); + this->y += static_cast(v.x); + return *this; + } + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<2, T, Q> & vec<2, T, Q>::operator+=(vec<2, U, Q> const& v) + { + this->x += static_cast(v.x); + this->y += static_cast(v.y); + return *this; + } + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<2, T, Q> & vec<2, T, Q>::operator-=(U scalar) + { + this->x -= static_cast(scalar); + this->y -= static_cast(scalar); + return *this; + } + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<2, T, Q> & vec<2, T, Q>::operator-=(vec<1, U, Q> const& v) + { + this->x -= static_cast(v.x); + this->y -= static_cast(v.x); + return *this; + } + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<2, T, Q> & vec<2, T, Q>::operator-=(vec<2, U, Q> const& v) + { + this->x -= static_cast(v.x); + this->y -= static_cast(v.y); + return *this; + } + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<2, T, Q> & vec<2, T, Q>::operator*=(U scalar) + { + this->x *= static_cast(scalar); + this->y *= static_cast(scalar); + return *this; + } + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<2, T, Q> & vec<2, T, Q>::operator*=(vec<1, U, Q> const& v) + { + this->x *= static_cast(v.x); + this->y *= static_cast(v.x); + return *this; + } + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<2, T, Q> & vec<2, T, Q>::operator*=(vec<2, U, Q> const& v) + { + this->x *= static_cast(v.x); + this->y *= static_cast(v.y); + return *this; + } + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<2, T, Q> & vec<2, T, Q>::operator/=(U scalar) + { + this->x /= static_cast(scalar); + this->y /= static_cast(scalar); + return *this; + } + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<2, T, Q> & vec<2, T, Q>::operator/=(vec<1, U, Q> const& v) + { + this->x /= static_cast(v.x); + this->y /= static_cast(v.x); + return *this; + } + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<2, T, Q> & vec<2, T, Q>::operator/=(vec<2, U, Q> const& v) + { + this->x /= static_cast(v.x); + this->y /= static_cast(v.y); + return *this; + } + + // -- Increment and decrement operators -- + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<2, T, Q> & vec<2, T, Q>::operator++() + { + ++this->x; + ++this->y; + return *this; + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<2, T, Q> & vec<2, T, Q>::operator--() + { + --this->x; + --this->y; + return *this; + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<2, T, Q> vec<2, T, Q>::operator++(int) + { + vec<2, T, Q> Result(*this); + ++*this; + return Result; + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<2, T, Q> vec<2, T, Q>::operator--(int) + { + vec<2, T, Q> Result(*this); + --*this; + return Result; + } + + // -- Unary bit operators -- + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<2, T, Q> & vec<2, T, Q>::operator%=(U scalar) + { + this->x %= static_cast(scalar); + this->y %= static_cast(scalar); + return *this; + } + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<2, T, Q> & vec<2, T, Q>::operator%=(vec<1, U, Q> const& v) + { + this->x %= static_cast(v.x); + this->y %= static_cast(v.x); + return *this; + } + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<2, T, Q> & vec<2, T, Q>::operator%=(vec<2, U, Q> const& v) + { + this->x %= static_cast(v.x); + this->y %= static_cast(v.y); + return *this; + } + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<2, T, Q> & vec<2, T, Q>::operator&=(U scalar) + { + this->x &= static_cast(scalar); + this->y &= static_cast(scalar); + return *this; + } + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<2, T, Q> & vec<2, T, Q>::operator&=(vec<1, U, Q> const& v) + { + this->x &= static_cast(v.x); + this->y &= static_cast(v.x); + return *this; + } + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<2, T, Q> & vec<2, T, Q>::operator&=(vec<2, U, Q> const& v) + { + this->x &= static_cast(v.x); + this->y &= static_cast(v.y); + return *this; + } + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<2, T, Q> & vec<2, T, Q>::operator|=(U scalar) + { + this->x |= static_cast(scalar); + this->y |= static_cast(scalar); + return *this; + } + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<2, T, Q> & vec<2, T, Q>::operator|=(vec<1, U, Q> const& v) + { + this->x |= static_cast(v.x); + this->y |= static_cast(v.x); + return *this; + } + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<2, T, Q> & vec<2, T, Q>::operator|=(vec<2, U, Q> const& v) + { + this->x |= static_cast(v.x); + this->y |= static_cast(v.y); + return *this; + } + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<2, T, Q> & vec<2, T, Q>::operator^=(U scalar) + { + this->x ^= static_cast(scalar); + this->y ^= static_cast(scalar); + return *this; + } + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<2, T, Q> & vec<2, T, Q>::operator^=(vec<1, U, Q> const& v) + { + this->x ^= static_cast(v.x); + this->y ^= static_cast(v.x); + return *this; + } + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<2, T, Q> & vec<2, T, Q>::operator^=(vec<2, U, Q> const& v) + { + this->x ^= static_cast(v.x); + this->y ^= static_cast(v.y); + return *this; + } + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<2, T, Q> & vec<2, T, Q>::operator<<=(U scalar) + { + this->x <<= static_cast(scalar); + this->y <<= static_cast(scalar); + return *this; + } + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<2, T, Q> & vec<2, T, Q>::operator<<=(vec<1, U, Q> const& v) + { + this->x <<= static_cast(v.x); + this->y <<= static_cast(v.x); + return *this; + } + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<2, T, Q> & vec<2, T, Q>::operator<<=(vec<2, U, Q> const& v) + { + this->x <<= static_cast(v.x); + this->y <<= static_cast(v.y); + return *this; + } + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<2, T, Q> & vec<2, T, Q>::operator>>=(U scalar) + { + this->x >>= static_cast(scalar); + this->y >>= static_cast(scalar); + return *this; + } + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<2, T, Q> & vec<2, T, Q>::operator>>=(vec<1, U, Q> const& v) + { + this->x >>= static_cast(v.x); + this->y >>= static_cast(v.x); + return *this; + } + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<2, T, Q> & vec<2, T, Q>::operator>>=(vec<2, U, Q> const& v) + { + this->x >>= static_cast(v.x); + this->y >>= static_cast(v.y); + return *this; + } + + // -- Unary arithmetic operators -- + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<2, T, Q> operator+(vec<2, T, Q> const& v) + { + return v; + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<2, T, Q> operator-(vec<2, T, Q> const& v) + { + return vec<2, T, Q>( + -v.x, + -v.y); + } + + // -- Binary arithmetic operators -- + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<2, T, Q> operator+(vec<2, T, Q> const& v, T scalar) + { + return vec<2, T, Q>( + v.x + scalar, + v.y + scalar); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<2, T, Q> operator+(vec<2, T, Q> const& v1, vec<1, T, Q> const& v2) + { + return vec<2, T, Q>( + v1.x + v2.x, + v1.y + v2.x); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<2, T, Q> operator+(T scalar, vec<2, T, Q> const& v) + { + return vec<2, T, Q>( + scalar + v.x, + scalar + v.y); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<2, T, Q> operator+(vec<1, T, Q> const& v1, vec<2, T, Q> const& v2) + { + return vec<2, T, Q>( + v1.x + v2.x, + v1.x + v2.y); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<2, T, Q> operator+(vec<2, T, Q> const& v1, vec<2, T, Q> const& v2) + { + return vec<2, T, Q>( + v1.x + v2.x, + v1.y + v2.y); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<2, T, Q> operator-(vec<2, T, Q> const& v, T scalar) + { + return vec<2, T, Q>( + v.x - scalar, + v.y - scalar); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<2, T, Q> operator-(vec<2, T, Q> const& v1, vec<1, T, Q> const& v2) + { + return vec<2, T, Q>( + v1.x - v2.x, + v1.y - v2.x); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<2, T, Q> operator-(T scalar, vec<2, T, Q> const& v) + { + return vec<2, T, Q>( + scalar - v.x, + scalar - v.y); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<2, T, Q> operator-(vec<1, T, Q> const& v1, vec<2, T, Q> const& v2) + { + return vec<2, T, Q>( + v1.x - v2.x, + v1.x - v2.y); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<2, T, Q> operator-(vec<2, T, Q> const& v1, vec<2, T, Q> const& v2) + { + return vec<2, T, Q>( + v1.x - v2.x, + v1.y - v2.y); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<2, T, Q> operator*(vec<2, T, Q> const& v, T scalar) + { + return vec<2, T, Q>( + v.x * scalar, + v.y * scalar); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<2, T, Q> operator*(vec<2, T, Q> const& v1, vec<1, T, Q> const& v2) + { + return vec<2, T, Q>( + v1.x * v2.x, + v1.y * v2.x); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<2, T, Q> operator*(T scalar, vec<2, T, Q> const& v) + { + return vec<2, T, Q>( + scalar * v.x, + scalar * v.y); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<2, T, Q> operator*(vec<1, T, Q> const& v1, vec<2, T, Q> const& v2) + { + return vec<2, T, Q>( + v1.x * v2.x, + v1.x * v2.y); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<2, T, Q> operator*(vec<2, T, Q> const& v1, vec<2, T, Q> const& v2) + { + return vec<2, T, Q>( + v1.x * v2.x, + v1.y * v2.y); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<2, T, Q> operator/(vec<2, T, Q> const& v, T scalar) + { + return vec<2, T, Q>( + v.x / scalar, + v.y / scalar); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<2, T, Q> operator/(vec<2, T, Q> const& v1, vec<1, T, Q> const& v2) + { + return vec<2, T, Q>( + v1.x / v2.x, + v1.y / v2.x); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<2, T, Q> operator/(T scalar, vec<2, T, Q> const& v) + { + return vec<2, T, Q>( + scalar / v.x, + scalar / v.y); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<2, T, Q> operator/(vec<1, T, Q> const& v1, vec<2, T, Q> const& v2) + { + return vec<2, T, Q>( + v1.x / v2.x, + v1.x / v2.y); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<2, T, Q> operator/(vec<2, T, Q> const& v1, vec<2, T, Q> const& v2) + { + return vec<2, T, Q>( + v1.x / v2.x, + v1.y / v2.y); + } + + // -- Binary bit operators -- + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<2, T, Q> operator%(vec<2, T, Q> const& v, T scalar) + { + return vec<2, T, Q>( + v.x % scalar, + v.y % scalar); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<2, T, Q> operator%(vec<2, T, Q> const& v1, vec<1, T, Q> const& v2) + { + return vec<2, T, Q>( + v1.x % v2.x, + v1.y % v2.x); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<2, T, Q> operator%(T scalar, vec<2, T, Q> const& v) + { + return vec<2, T, Q>( + scalar % v.x, + scalar % v.y); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<2, T, Q> operator%(vec<1, T, Q> const& v1, vec<2, T, Q> const& v2) + { + return vec<2, T, Q>( + v1.x % v2.x, + v1.x % v2.y); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<2, T, Q> operator%(vec<2, T, Q> const& v1, vec<2, T, Q> const& v2) + { + return vec<2, T, Q>( + v1.x % v2.x, + v1.y % v2.y); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<2, T, Q> operator&(vec<2, T, Q> const& v, T scalar) + { + return vec<2, T, Q>( + v.x & scalar, + v.y & scalar); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<2, T, Q> operator&(vec<2, T, Q> const& v1, vec<1, T, Q> const& v2) + { + return vec<2, T, Q>( + v1.x & v2.x, + v1.y & v2.x); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<2, T, Q> operator&(T scalar, vec<2, T, Q> const& v) + { + return vec<2, T, Q>( + scalar & v.x, + scalar & v.y); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<2, T, Q> operator&(vec<1, T, Q> const& v1, vec<2, T, Q> const& v2) + { + return vec<2, T, Q>( + v1.x & v2.x, + v1.x & v2.y); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<2, T, Q> operator&(vec<2, T, Q> const& v1, vec<2, T, Q> const& v2) + { + return vec<2, T, Q>( + v1.x & v2.x, + v1.y & v2.y); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<2, T, Q> operator|(vec<2, T, Q> const& v, T scalar) + { + return vec<2, T, Q>( + v.x | scalar, + v.y | scalar); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<2, T, Q> operator|(vec<2, T, Q> const& v1, vec<1, T, Q> const& v2) + { + return vec<2, T, Q>( + v1.x | v2.x, + v1.y | v2.x); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<2, T, Q> operator|(T scalar, vec<2, T, Q> const& v) + { + return vec<2, T, Q>( + scalar | v.x, + scalar | v.y); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<2, T, Q> operator|(vec<1, T, Q> const& v1, vec<2, T, Q> const& v2) + { + return vec<2, T, Q>( + v1.x | v2.x, + v1.x | v2.y); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<2, T, Q> operator|(vec<2, T, Q> const& v1, vec<2, T, Q> const& v2) + { + return vec<2, T, Q>( + v1.x | v2.x, + v1.y | v2.y); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<2, T, Q> operator^(vec<2, T, Q> const& v, T scalar) + { + return vec<2, T, Q>( + v.x ^ scalar, + v.y ^ scalar); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<2, T, Q> operator^(vec<2, T, Q> const& v1, vec<1, T, Q> const& v2) + { + return vec<2, T, Q>( + v1.x ^ v2.x, + v1.y ^ v2.x); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<2, T, Q> operator^(T scalar, vec<2, T, Q> const& v) + { + return vec<2, T, Q>( + scalar ^ v.x, + scalar ^ v.y); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<2, T, Q> operator^(vec<1, T, Q> const& v1, vec<2, T, Q> const& v2) + { + return vec<2, T, Q>( + v1.x ^ v2.x, + v1.x ^ v2.y); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<2, T, Q> operator^(vec<2, T, Q> const& v1, vec<2, T, Q> const& v2) + { + return vec<2, T, Q>( + v1.x ^ v2.x, + v1.y ^ v2.y); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<2, T, Q> operator<<(vec<2, T, Q> const& v, T scalar) + { + return vec<2, T, Q>( + v.x << scalar, + v.y << scalar); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<2, T, Q> operator<<(vec<2, T, Q> const& v1, vec<1, T, Q> const& v2) + { + return vec<2, T, Q>( + v1.x << v2.x, + v1.y << v2.x); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<2, T, Q> operator<<(T scalar, vec<2, T, Q> const& v) + { + return vec<2, T, Q>( + scalar << v.x, + scalar << v.y); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<2, T, Q> operator<<(vec<1, T, Q> const& v1, vec<2, T, Q> const& v2) + { + return vec<2, T, Q>( + v1.x << v2.x, + v1.x << v2.y); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<2, T, Q> operator<<(vec<2, T, Q> const& v1, vec<2, T, Q> const& v2) + { + return vec<2, T, Q>( + v1.x << v2.x, + v1.y << v2.y); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<2, T, Q> operator>>(vec<2, T, Q> const& v, T scalar) + { + return vec<2, T, Q>( + v.x >> scalar, + v.y >> scalar); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<2, T, Q> operator>>(vec<2, T, Q> const& v1, vec<1, T, Q> const& v2) + { + return vec<2, T, Q>( + v1.x >> v2.x, + v1.y >> v2.x); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<2, T, Q> operator>>(T scalar, vec<2, T, Q> const& v) + { + return vec<2, T, Q>( + scalar >> v.x, + scalar >> v.y); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<2, T, Q> operator>>(vec<1, T, Q> const& v1, vec<2, T, Q> const& v2) + { + return vec<2, T, Q>( + v1.x >> v2.x, + v1.x >> v2.y); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<2, T, Q> operator>>(vec<2, T, Q> const& v1, vec<2, T, Q> const& v2) + { + return vec<2, T, Q>( + v1.x >> v2.x, + v1.y >> v2.y); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<2, T, Q> operator~(vec<2, T, Q> const& v) + { + return vec<2, T, Q>( + ~v.x, + ~v.y); + } + + // -- Boolean operators -- + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR bool operator==(vec<2, T, Q> const& v1, vec<2, T, Q> const& v2) + { + return + detail::compute_equal::is_iec559>::call(v1.x, v2.x) && + detail::compute_equal::is_iec559>::call(v1.y, v2.y); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR bool operator!=(vec<2, T, Q> const& v1, vec<2, T, Q> const& v2) + { + return !(v1 == v2); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<2, bool, Q> operator&&(vec<2, bool, Q> const& v1, vec<2, bool, Q> const& v2) + { + return vec<2, bool, Q>(v1.x && v2.x, v1.y && v2.y); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<2, bool, Q> operator||(vec<2, bool, Q> const& v1, vec<2, bool, Q> const& v2) + { + return vec<2, bool, Q>(v1.x || v2.x, v1.y || v2.y); + } +}//namespace glm diff --git a/src/GLMath/glm/detail/type_vec3.hpp b/src/GLMath/glm/detail/type_vec3.hpp new file mode 100644 index 000000000..d83cde678 --- /dev/null +++ b/src/GLMath/glm/detail/type_vec3.hpp @@ -0,0 +1,432 @@ +/// @ref core +/// @file glm/detail/type_vec3.hpp + +#pragma once + +#include "qualifier.hpp" +#if GLM_CONFIG_SWIZZLE == GLM_SWIZZLE_OPERATOR +# include "_swizzle.hpp" +#elif GLM_CONFIG_SWIZZLE == GLM_SWIZZLE_FUNCTION +# include "_swizzle_func.hpp" +#endif +#include + +namespace glm +{ + template + struct vec<3, T, Q> + { + // -- Implementation detail -- + + typedef T value_type; + typedef vec<3, T, Q> type; + typedef vec<3, bool, Q> bool_type; + + // -- Data -- + +# if GLM_SILENT_WARNINGS == GLM_ENABLE +# if GLM_COMPILER & GLM_COMPILER_GCC +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Wpedantic" +# elif GLM_COMPILER & GLM_COMPILER_CLANG +# pragma clang diagnostic push +# pragma clang diagnostic ignored "-Wgnu-anonymous-struct" +# pragma clang diagnostic ignored "-Wnested-anon-types" +# elif GLM_COMPILER & GLM_COMPILER_VC +# pragma warning(push) +# pragma warning(disable: 4201) // nonstandard extension used : nameless struct/union +# if GLM_CONFIG_ALIGNED_GENTYPES == GLM_ENABLE +# pragma warning(disable: 4324) // structure was padded due to alignment specifier +# endif +# endif +# endif + +# if GLM_CONFIG_XYZW_ONLY + T x, y, z; +# elif GLM_CONFIG_ANONYMOUS_STRUCT == GLM_ENABLE + union + { + struct{ T x, y, z; }; + struct{ T r, g, b; }; + struct{ T s, t, p; }; + + typename detail::storage<3, T, detail::is_aligned::value>::type data; + +# if GLM_CONFIG_SWIZZLE == GLM_SWIZZLE_OPERATOR + GLM_SWIZZLE3_2_MEMBERS(T, Q, x, y, z) + GLM_SWIZZLE3_2_MEMBERS(T, Q, r, g, b) + GLM_SWIZZLE3_2_MEMBERS(T, Q, s, t, p) + GLM_SWIZZLE3_3_MEMBERS(T, Q, x, y, z) + GLM_SWIZZLE3_3_MEMBERS(T, Q, r, g, b) + GLM_SWIZZLE3_3_MEMBERS(T, Q, s, t, p) + GLM_SWIZZLE3_4_MEMBERS(T, Q, x, y, z) + GLM_SWIZZLE3_4_MEMBERS(T, Q, r, g, b) + GLM_SWIZZLE3_4_MEMBERS(T, Q, s, t, p) +# endif + }; +# else + union { T x, r, s; }; + union { T y, g, t; }; + union { T z, b, p; }; + +# if GLM_CONFIG_SWIZZLE == GLM_SWIZZLE_FUNCTION + GLM_SWIZZLE_GEN_VEC_FROM_VEC3(T, Q) +# endif//GLM_CONFIG_SWIZZLE +# endif//GLM_LANG + +# if GLM_SILENT_WARNINGS == GLM_ENABLE +# if GLM_COMPILER & GLM_COMPILER_CLANG +# pragma clang diagnostic pop +# elif GLM_COMPILER & GLM_COMPILER_GCC +# pragma GCC diagnostic pop +# elif GLM_COMPILER & GLM_COMPILER_VC +# pragma warning(pop) +# endif +# endif + + // -- Component accesses -- + + /// Return the count of components of the vector + typedef length_t length_type; + GLM_FUNC_DECL static GLM_CONSTEXPR length_type length(){return 3;} + + GLM_FUNC_DECL GLM_CONSTEXPR T & operator[](length_type i); + GLM_FUNC_DECL GLM_CONSTEXPR T const& operator[](length_type i) const; + + // -- Implicit basic constructors -- + + GLM_FUNC_DECL GLM_CONSTEXPR vec() GLM_DEFAULT; + GLM_FUNC_DECL GLM_CONSTEXPR vec(vec const& v) GLM_DEFAULT; + template + GLM_FUNC_DECL GLM_CONSTEXPR vec(vec<3, T, P> const& v); + + // -- Explicit basic constructors -- + + GLM_FUNC_DECL GLM_CONSTEXPR explicit vec(T scalar); + GLM_FUNC_DECL GLM_CONSTEXPR vec(T a, T b, T c); + + // -- Conversion scalar constructors -- + + template + GLM_FUNC_DECL GLM_CONSTEXPR explicit vec(vec<1, U, P> const& v); + + /// Explicit conversions (From section 5.4.1 Conversion and scalar constructors of GLSL 1.30.08 specification) + template + GLM_FUNC_DECL GLM_CONSTEXPR vec(X x, Y y, Z z); + template + GLM_FUNC_DECL GLM_CONSTEXPR vec(vec<1, X, Q> const& _x, Y _y, Z _z); + template + GLM_FUNC_DECL GLM_CONSTEXPR vec(X _x, vec<1, Y, Q> const& _y, Z _z); + template + GLM_FUNC_DECL GLM_CONSTEXPR vec(vec<1, X, Q> const& _x, vec<1, Y, Q> const& _y, Z _z); + template + GLM_FUNC_DECL GLM_CONSTEXPR vec(X _x, Y _y, vec<1, Z, Q> const& _z); + template + GLM_FUNC_DECL GLM_CONSTEXPR vec(vec<1, X, Q> const& _x, Y _y, vec<1, Z, Q> const& _z); + template + GLM_FUNC_DECL GLM_CONSTEXPR vec(X _x, vec<1, Y, Q> const& _y, vec<1, Z, Q> const& _z); + template + GLM_FUNC_DECL GLM_CONSTEXPR vec(vec<1, X, Q> const& _x, vec<1, Y, Q> const& _y, vec<1, Z, Q> const& _z); + + // -- Conversion vector constructors -- + + /// Explicit conversions (From section 5.4.1 Conversion and scalar constructors of GLSL 1.30.08 specification) + template + GLM_FUNC_DECL GLM_CONSTEXPR vec(vec<2, A, P> const& _xy, B _z); + /// Explicit conversions (From section 5.4.1 Conversion and scalar constructors of GLSL 1.30.08 specification) + template + GLM_FUNC_DECL GLM_CONSTEXPR vec(vec<2, A, P> const& _xy, vec<1, B, P> const& _z); + /// Explicit conversions (From section 5.4.1 Conversion and scalar constructors of GLSL 1.30.08 specification) + template + GLM_FUNC_DECL GLM_CONSTEXPR vec(A _x, vec<2, B, P> const& _yz); + /// Explicit conversions (From section 5.4.1 Conversion and scalar constructors of GLSL 1.30.08 specification) + template + GLM_FUNC_DECL GLM_CONSTEXPR vec(vec<1, A, P> const& _x, vec<2, B, P> const& _yz); + /// Explicit conversions (From section 5.4.1 Conversion and scalar constructors of GLSL 1.30.08 specification) + template + GLM_FUNC_DECL GLM_CONSTEXPR GLM_EXPLICIT vec(vec<4, U, P> const& v); + + /// Explicit conversions (From section 5.4.1 Conversion and scalar constructors of GLSL 1.30.08 specification) + template + GLM_FUNC_DECL GLM_CONSTEXPR GLM_EXPLICIT vec(vec<3, U, P> const& v); + + // -- Swizzle constructors -- +# if GLM_CONFIG_SWIZZLE == GLM_SWIZZLE_OPERATOR + template + GLM_FUNC_DECL GLM_CONSTEXPR vec(detail::_swizzle<3, T, Q, E0, E1, E2, -1> const& that) + { + *this = that(); + } + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec(detail::_swizzle<2, T, Q, E0, E1, -1, -2> const& v, T const& scalar) + { + *this = vec(v(), scalar); + } + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec(T const& scalar, detail::_swizzle<2, T, Q, E0, E1, -1, -2> const& v) + { + *this = vec(scalar, v()); + } +# endif//GLM_CONFIG_SWIZZLE == GLM_SWIZZLE_OPERATOR + + // -- Unary arithmetic operators -- + + GLM_FUNC_DECL GLM_CONSTEXPR vec<3, T, Q>& operator=(vec<3, T, Q> const& v) GLM_DEFAULT; + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<3, T, Q> & operator=(vec<3, U, Q> const& v); + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<3, T, Q> & operator+=(U scalar); + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<3, T, Q> & operator+=(vec<1, U, Q> const& v); + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<3, T, Q> & operator+=(vec<3, U, Q> const& v); + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<3, T, Q> & operator-=(U scalar); + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<3, T, Q> & operator-=(vec<1, U, Q> const& v); + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<3, T, Q> & operator-=(vec<3, U, Q> const& v); + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<3, T, Q> & operator*=(U scalar); + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<3, T, Q> & operator*=(vec<1, U, Q> const& v); + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<3, T, Q> & operator*=(vec<3, U, Q> const& v); + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<3, T, Q> & operator/=(U scalar); + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<3, T, Q> & operator/=(vec<1, U, Q> const& v); + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<3, T, Q> & operator/=(vec<3, U, Q> const& v); + + // -- Increment and decrement operators -- + + GLM_FUNC_DECL GLM_CONSTEXPR vec<3, T, Q> & operator++(); + GLM_FUNC_DECL GLM_CONSTEXPR vec<3, T, Q> & operator--(); + GLM_FUNC_DECL GLM_CONSTEXPR vec<3, T, Q> operator++(int); + GLM_FUNC_DECL GLM_CONSTEXPR vec<3, T, Q> operator--(int); + + // -- Unary bit operators -- + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<3, T, Q> & operator%=(U scalar); + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<3, T, Q> & operator%=(vec<1, U, Q> const& v); + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<3, T, Q> & operator%=(vec<3, U, Q> const& v); + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<3, T, Q> & operator&=(U scalar); + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<3, T, Q> & operator&=(vec<1, U, Q> const& v); + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<3, T, Q> & operator&=(vec<3, U, Q> const& v); + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<3, T, Q> & operator|=(U scalar); + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<3, T, Q> & operator|=(vec<1, U, Q> const& v); + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<3, T, Q> & operator|=(vec<3, U, Q> const& v); + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<3, T, Q> & operator^=(U scalar); + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<3, T, Q> & operator^=(vec<1, U, Q> const& v); + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<3, T, Q> & operator^=(vec<3, U, Q> const& v); + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<3, T, Q> & operator<<=(U scalar); + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<3, T, Q> & operator<<=(vec<1, U, Q> const& v); + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<3, T, Q> & operator<<=(vec<3, U, Q> const& v); + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<3, T, Q> & operator>>=(U scalar); + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<3, T, Q> & operator>>=(vec<1, U, Q> const& v); + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<3, T, Q> & operator>>=(vec<3, U, Q> const& v); + }; + + // -- Unary operators -- + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<3, T, Q> operator+(vec<3, T, Q> const& v); + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<3, T, Q> operator-(vec<3, T, Q> const& v); + + // -- Binary operators -- + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<3, T, Q> operator+(vec<3, T, Q> const& v, T scalar); + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<3, T, Q> operator+(vec<3, T, Q> const& v, vec<1, T, Q> const& scalar); + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<3, T, Q> operator+(T scalar, vec<3, T, Q> const& v); + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<3, T, Q> operator+(vec<1, T, Q> const& v1, vec<3, T, Q> const& v2); + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<3, T, Q> operator+(vec<3, T, Q> const& v1, vec<3, T, Q> const& v2); + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<3, T, Q> operator-(vec<3, T, Q> const& v, T scalar); + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<3, T, Q> operator-(vec<3, T, Q> const& v1, vec<1, T, Q> const& v2); + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<3, T, Q> operator-(T scalar, vec<3, T, Q> const& v); + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<3, T, Q> operator-(vec<1, T, Q> const& v1, vec<3, T, Q> const& v2); + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<3, T, Q> operator-(vec<3, T, Q> const& v1, vec<3, T, Q> const& v2); + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<3, T, Q> operator*(vec<3, T, Q> const& v, T scalar); + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<3, T, Q> operator*(vec<3, T, Q> const& v1, vec<1, T, Q> const& v2); + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<3, T, Q> operator*(T scalar, vec<3, T, Q> const& v); + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<3, T, Q> operator*(vec<1, T, Q> const& v1, vec<3, T, Q> const& v2); + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<3, T, Q> operator*(vec<3, T, Q> const& v1, vec<3, T, Q> const& v2); + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<3, T, Q> operator/(vec<3, T, Q> const& v, T scalar); + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<3, T, Q> operator/(vec<3, T, Q> const& v1, vec<1, T, Q> const& v2); + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<3, T, Q> operator/(T scalar, vec<3, T, Q> const& v); + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<3, T, Q> operator/(vec<1, T, Q> const& v1, vec<3, T, Q> const& v2); + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<3, T, Q> operator/(vec<3, T, Q> const& v1, vec<3, T, Q> const& v2); + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<3, T, Q> operator%(vec<3, T, Q> const& v, T scalar); + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<3, T, Q> operator%(vec<3, T, Q> const& v1, vec<1, T, Q> const& v2); + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<3, T, Q> operator%(T scalar, vec<3, T, Q> const& v); + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<3, T, Q> operator%(vec<1, T, Q> const& v1, vec<3, T, Q> const& v2); + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<3, T, Q> operator%(vec<3, T, Q> const& v1, vec<3, T, Q> const& v2); + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<3, T, Q> operator&(vec<3, T, Q> const& v1, T scalar); + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<3, T, Q> operator&(vec<3, T, Q> const& v1, vec<1, T, Q> const& v2); + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<3, T, Q> operator&(T scalar, vec<3, T, Q> const& v); + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<3, T, Q> operator&(vec<1, T, Q> const& v1, vec<3, T, Q> const& v2); + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<3, T, Q> operator&(vec<3, T, Q> const& v1, vec<3, T, Q> const& v2); + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<3, T, Q> operator|(vec<3, T, Q> const& v, T scalar); + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<3, T, Q> operator|(vec<3, T, Q> const& v1, vec<1, T, Q> const& v2); + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<3, T, Q> operator|(T scalar, vec<3, T, Q> const& v); + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<3, T, Q> operator|(vec<1, T, Q> const& v1, vec<3, T, Q> const& v2); + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<3, T, Q> operator|(vec<3, T, Q> const& v1, vec<3, T, Q> const& v2); + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<3, T, Q> operator^(vec<3, T, Q> const& v, T scalar); + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<3, T, Q> operator^(vec<3, T, Q> const& v1, vec<1, T, Q> const& v2); + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<3, T, Q> operator^(T scalar, vec<3, T, Q> const& v); + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<3, T, Q> operator^(vec<1, T, Q> const& v1, vec<3, T, Q> const& v2); + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<3, T, Q> operator^(vec<3, T, Q> const& v1, vec<3, T, Q> const& v2); + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<3, T, Q> operator<<(vec<3, T, Q> const& v, T scalar); + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<3, T, Q> operator<<(vec<3, T, Q> const& v1, vec<1, T, Q> const& v2); + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<3, T, Q> operator<<(T scalar, vec<3, T, Q> const& v); + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<3, T, Q> operator<<(vec<1, T, Q> const& v1, vec<3, T, Q> const& v2); + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<3, T, Q> operator<<(vec<3, T, Q> const& v1, vec<3, T, Q> const& v2); + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<3, T, Q> operator>>(vec<3, T, Q> const& v, T scalar); + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<3, T, Q> operator>>(vec<3, T, Q> const& v1, vec<1, T, Q> const& v2); + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<3, T, Q> operator>>(T scalar, vec<3, T, Q> const& v); + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<3, T, Q> operator>>(vec<1, T, Q> const& v1, vec<3, T, Q> const& v2); + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<3, T, Q> operator>>(vec<3, T, Q> const& v1, vec<3, T, Q> const& v2); + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<3, T, Q> operator~(vec<3, T, Q> const& v); + + // -- Boolean operators -- + + template + GLM_FUNC_DECL GLM_CONSTEXPR bool operator==(vec<3, T, Q> const& v1, vec<3, T, Q> const& v2); + + template + GLM_FUNC_DECL GLM_CONSTEXPR bool operator!=(vec<3, T, Q> const& v1, vec<3, T, Q> const& v2); + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<3, bool, Q> operator&&(vec<3, bool, Q> const& v1, vec<3, bool, Q> const& v2); + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<3, bool, Q> operator||(vec<3, bool, Q> const& v1, vec<3, bool, Q> const& v2); +}//namespace glm + +#ifndef GLM_EXTERNAL_TEMPLATE +#include "type_vec3.inl" +#endif//GLM_EXTERNAL_TEMPLATE diff --git a/src/GLMath/glm/detail/type_vec3.inl b/src/GLMath/glm/detail/type_vec3.inl new file mode 100644 index 000000000..6532c9e6e --- /dev/null +++ b/src/GLMath/glm/detail/type_vec3.inl @@ -0,0 +1,1068 @@ +/// @ref core + +#include "compute_vector_relational.hpp" + +namespace glm +{ + // -- Implicit basic constructors -- + +# if GLM_CONFIG_DEFAULTED_FUNCTIONS == GLM_DISABLE + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<3, T, Q>::vec() +# if GLM_CONFIG_CTOR_INIT != GLM_CTOR_INIT_DISABLE + : x(0), y(0), z(0) +# endif + {} + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<3, T, Q>::vec(vec<3, T, Q> const& v) + : x(v.x), y(v.y), z(v.z) + {} +# endif + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<3, T, Q>::vec(vec<3, T, P> const& v) + : x(v.x), y(v.y), z(v.z) + {} + + // -- Explicit basic constructors -- + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<3, T, Q>::vec(T scalar) + : x(scalar), y(scalar), z(scalar) + {} + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<3, T, Q>::vec(T _x, T _y, T _z) + : x(_x), y(_y), z(_z) + {} + + // -- Conversion scalar constructors -- + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<3, T, Q>::vec(vec<1, U, P> const& v) + : x(static_cast(v.x)) + , y(static_cast(v.x)) + , z(static_cast(v.x)) + {} + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<3, T, Q>::vec(X _x, Y _y, Z _z) + : x(static_cast(_x)) + , y(static_cast(_y)) + , z(static_cast(_z)) + {} + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<3, T, Q>::vec(vec<1, X, Q> const& _x, Y _y, Z _z) + : x(static_cast(_x.x)) + , y(static_cast(_y)) + , z(static_cast(_z)) + {} + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<3, T, Q>::vec(X _x, vec<1, Y, Q> const& _y, Z _z) + : x(static_cast(_x)) + , y(static_cast(_y.x)) + , z(static_cast(_z)) + {} + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<3, T, Q>::vec(vec<1, X, Q> const& _x, vec<1, Y, Q> const& _y, Z _z) + : x(static_cast(_x.x)) + , y(static_cast(_y.x)) + , z(static_cast(_z)) + {} + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<3, T, Q>::vec(X _x, Y _y, vec<1, Z, Q> const& _z) + : x(static_cast(_x)) + , y(static_cast(_y)) + , z(static_cast(_z.x)) + {} + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<3, T, Q>::vec(vec<1, X, Q> const& _x, Y _y, vec<1, Z, Q> const& _z) + : x(static_cast(_x.x)) + , y(static_cast(_y)) + , z(static_cast(_z.x)) + {} + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<3, T, Q>::vec(X _x, vec<1, Y, Q> const& _y, vec<1, Z, Q> const& _z) + : x(static_cast(_x)) + , y(static_cast(_y.x)) + , z(static_cast(_z.x)) + {} + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<3, T, Q>::vec(vec<1, X, Q> const& _x, vec<1, Y, Q> const& _y, vec<1, Z, Q> const& _z) + : x(static_cast(_x.x)) + , y(static_cast(_y.x)) + , z(static_cast(_z.x)) + {} + + // -- Conversion vector constructors -- + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<3, T, Q>::vec(vec<2, A, P> const& _xy, B _z) + : x(static_cast(_xy.x)) + , y(static_cast(_xy.y)) + , z(static_cast(_z)) + {} + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<3, T, Q>::vec(vec<2, A, P> const& _xy, vec<1, B, P> const& _z) + : x(static_cast(_xy.x)) + , y(static_cast(_xy.y)) + , z(static_cast(_z.x)) + {} + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<3, T, Q>::vec(A _x, vec<2, B, P> const& _yz) + : x(static_cast(_x)) + , y(static_cast(_yz.x)) + , z(static_cast(_yz.y)) + {} + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<3, T, Q>::vec(vec<1, A, P> const& _x, vec<2, B, P> const& _yz) + : x(static_cast(_x.x)) + , y(static_cast(_yz.x)) + , z(static_cast(_yz.y)) + {} + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<3, T, Q>::vec(vec<3, U, P> const& v) + : x(static_cast(v.x)) + , y(static_cast(v.y)) + , z(static_cast(v.z)) + {} + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<3, T, Q>::vec(vec<4, U, P> const& v) + : x(static_cast(v.x)) + , y(static_cast(v.y)) + , z(static_cast(v.z)) + {} + + // -- Component accesses -- + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR T & vec<3, T, Q>::operator[](typename vec<3, T, Q>::length_type i) + { + assert(i >= 0 && i < this->length()); + switch(i) + { + default: + case 0: + return x; + case 1: + return y; + case 2: + return z; + } + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR T const& vec<3, T, Q>::operator[](typename vec<3, T, Q>::length_type i) const + { + assert(i >= 0 && i < this->length()); + switch(i) + { + default: + case 0: + return x; + case 1: + return y; + case 2: + return z; + } + } + + // -- Unary arithmetic operators -- + +# if GLM_CONFIG_DEFAULTED_FUNCTIONS == GLM_DISABLE + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<3, T, Q>& vec<3, T, Q>::operator=(vec<3, T, Q> const& v) + { + this->x = v.x; + this->y = v.y; + this->z = v.z; + return *this; + } +# endif + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<3, T, Q>& vec<3, T, Q>::operator=(vec<3, U, Q> const& v) + { + this->x = static_cast(v.x); + this->y = static_cast(v.y); + this->z = static_cast(v.z); + return *this; + } + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<3, T, Q> & vec<3, T, Q>::operator+=(U scalar) + { + this->x += static_cast(scalar); + this->y += static_cast(scalar); + this->z += static_cast(scalar); + return *this; + } + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<3, T, Q> & vec<3, T, Q>::operator+=(vec<1, U, Q> const& v) + { + this->x += static_cast(v.x); + this->y += static_cast(v.x); + this->z += static_cast(v.x); + return *this; + } + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<3, T, Q> & vec<3, T, Q>::operator+=(vec<3, U, Q> const& v) + { + this->x += static_cast(v.x); + this->y += static_cast(v.y); + this->z += static_cast(v.z); + return *this; + } + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<3, T, Q> & vec<3, T, Q>::operator-=(U scalar) + { + this->x -= static_cast(scalar); + this->y -= static_cast(scalar); + this->z -= static_cast(scalar); + return *this; + } + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<3, T, Q> & vec<3, T, Q>::operator-=(vec<1, U, Q> const& v) + { + this->x -= static_cast(v.x); + this->y -= static_cast(v.x); + this->z -= static_cast(v.x); + return *this; + } + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<3, T, Q> & vec<3, T, Q>::operator-=(vec<3, U, Q> const& v) + { + this->x -= static_cast(v.x); + this->y -= static_cast(v.y); + this->z -= static_cast(v.z); + return *this; + } + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<3, T, Q> & vec<3, T, Q>::operator*=(U scalar) + { + this->x *= static_cast(scalar); + this->y *= static_cast(scalar); + this->z *= static_cast(scalar); + return *this; + } + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<3, T, Q> & vec<3, T, Q>::operator*=(vec<1, U, Q> const& v) + { + this->x *= static_cast(v.x); + this->y *= static_cast(v.x); + this->z *= static_cast(v.x); + return *this; + } + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<3, T, Q> & vec<3, T, Q>::operator*=(vec<3, U, Q> const& v) + { + this->x *= static_cast(v.x); + this->y *= static_cast(v.y); + this->z *= static_cast(v.z); + return *this; + } + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<3, T, Q> & vec<3, T, Q>::operator/=(U v) + { + this->x /= static_cast(v); + this->y /= static_cast(v); + this->z /= static_cast(v); + return *this; + } + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<3, T, Q> & vec<3, T, Q>::operator/=(vec<1, U, Q> const& v) + { + this->x /= static_cast(v.x); + this->y /= static_cast(v.x); + this->z /= static_cast(v.x); + return *this; + } + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<3, T, Q> & vec<3, T, Q>::operator/=(vec<3, U, Q> const& v) + { + this->x /= static_cast(v.x); + this->y /= static_cast(v.y); + this->z /= static_cast(v.z); + return *this; + } + + // -- Increment and decrement operators -- + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<3, T, Q> & vec<3, T, Q>::operator++() + { + ++this->x; + ++this->y; + ++this->z; + return *this; + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<3, T, Q> & vec<3, T, Q>::operator--() + { + --this->x; + --this->y; + --this->z; + return *this; + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<3, T, Q> vec<3, T, Q>::operator++(int) + { + vec<3, T, Q> Result(*this); + ++*this; + return Result; + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<3, T, Q> vec<3, T, Q>::operator--(int) + { + vec<3, T, Q> Result(*this); + --*this; + return Result; + } + + // -- Unary bit operators -- + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<3, T, Q> & vec<3, T, Q>::operator%=(U scalar) + { + this->x %= scalar; + this->y %= scalar; + this->z %= scalar; + return *this; + } + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<3, T, Q> & vec<3, T, Q>::operator%=(vec<1, U, Q> const& v) + { + this->x %= v.x; + this->y %= v.x; + this->z %= v.x; + return *this; + } + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<3, T, Q> & vec<3, T, Q>::operator%=(vec<3, U, Q> const& v) + { + this->x %= v.x; + this->y %= v.y; + this->z %= v.z; + return *this; + } + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<3, T, Q> & vec<3, T, Q>::operator&=(U scalar) + { + this->x &= scalar; + this->y &= scalar; + this->z &= scalar; + return *this; + } + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<3, T, Q> & vec<3, T, Q>::operator&=(vec<1, U, Q> const& v) + { + this->x &= v.x; + this->y &= v.x; + this->z &= v.x; + return *this; + } + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<3, T, Q> & vec<3, T, Q>::operator&=(vec<3, U, Q> const& v) + { + this->x &= v.x; + this->y &= v.y; + this->z &= v.z; + return *this; + } + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<3, T, Q> & vec<3, T, Q>::operator|=(U scalar) + { + this->x |= scalar; + this->y |= scalar; + this->z |= scalar; + return *this; + } + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<3, T, Q> & vec<3, T, Q>::operator|=(vec<1, U, Q> const& v) + { + this->x |= v.x; + this->y |= v.x; + this->z |= v.x; + return *this; + } + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<3, T, Q> & vec<3, T, Q>::operator|=(vec<3, U, Q> const& v) + { + this->x |= v.x; + this->y |= v.y; + this->z |= v.z; + return *this; + } + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<3, T, Q> & vec<3, T, Q>::operator^=(U scalar) + { + this->x ^= scalar; + this->y ^= scalar; + this->z ^= scalar; + return *this; + } + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<3, T, Q> & vec<3, T, Q>::operator^=(vec<1, U, Q> const& v) + { + this->x ^= v.x; + this->y ^= v.x; + this->z ^= v.x; + return *this; + } + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<3, T, Q> & vec<3, T, Q>::operator^=(vec<3, U, Q> const& v) + { + this->x ^= v.x; + this->y ^= v.y; + this->z ^= v.z; + return *this; + } + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<3, T, Q> & vec<3, T, Q>::operator<<=(U scalar) + { + this->x <<= scalar; + this->y <<= scalar; + this->z <<= scalar; + return *this; + } + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<3, T, Q> & vec<3, T, Q>::operator<<=(vec<1, U, Q> const& v) + { + this->x <<= static_cast(v.x); + this->y <<= static_cast(v.x); + this->z <<= static_cast(v.x); + return *this; + } + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<3, T, Q> & vec<3, T, Q>::operator<<=(vec<3, U, Q> const& v) + { + this->x <<= static_cast(v.x); + this->y <<= static_cast(v.y); + this->z <<= static_cast(v.z); + return *this; + } + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<3, T, Q> & vec<3, T, Q>::operator>>=(U scalar) + { + this->x >>= static_cast(scalar); + this->y >>= static_cast(scalar); + this->z >>= static_cast(scalar); + return *this; + } + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<3, T, Q> & vec<3, T, Q>::operator>>=(vec<1, U, Q> const& v) + { + this->x >>= static_cast(v.x); + this->y >>= static_cast(v.x); + this->z >>= static_cast(v.x); + return *this; + } + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<3, T, Q> & vec<3, T, Q>::operator>>=(vec<3, U, Q> const& v) + { + this->x >>= static_cast(v.x); + this->y >>= static_cast(v.y); + this->z >>= static_cast(v.z); + return *this; + } + + // -- Unary arithmetic operators -- + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<3, T, Q> operator+(vec<3, T, Q> const& v) + { + return v; + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<3, T, Q> operator-(vec<3, T, Q> const& v) + { + return vec<3, T, Q>( + -v.x, + -v.y, + -v.z); + } + + // -- Binary arithmetic operators -- + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<3, T, Q> operator+(vec<3, T, Q> const& v, T scalar) + { + return vec<3, T, Q>( + v.x + scalar, + v.y + scalar, + v.z + scalar); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<3, T, Q> operator+(vec<3, T, Q> const& v, vec<1, T, Q> const& scalar) + { + return vec<3, T, Q>( + v.x + scalar.x, + v.y + scalar.x, + v.z + scalar.x); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<3, T, Q> operator+(T scalar, vec<3, T, Q> const& v) + { + return vec<3, T, Q>( + scalar + v.x, + scalar + v.y, + scalar + v.z); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<3, T, Q> operator+(vec<1, T, Q> const& scalar, vec<3, T, Q> const& v) + { + return vec<3, T, Q>( + scalar.x + v.x, + scalar.x + v.y, + scalar.x + v.z); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<3, T, Q> operator+(vec<3, T, Q> const& v1, vec<3, T, Q> const& v2) + { + return vec<3, T, Q>( + v1.x + v2.x, + v1.y + v2.y, + v1.z + v2.z); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<3, T, Q> operator-(vec<3, T, Q> const& v, T scalar) + { + return vec<3, T, Q>( + v.x - scalar, + v.y - scalar, + v.z - scalar); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<3, T, Q> operator-(vec<3, T, Q> const& v, vec<1, T, Q> const& scalar) + { + return vec<3, T, Q>( + v.x - scalar.x, + v.y - scalar.x, + v.z - scalar.x); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<3, T, Q> operator-(T scalar, vec<3, T, Q> const& v) + { + return vec<3, T, Q>( + scalar - v.x, + scalar - v.y, + scalar - v.z); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<3, T, Q> operator-(vec<1, T, Q> const& scalar, vec<3, T, Q> const& v) + { + return vec<3, T, Q>( + scalar.x - v.x, + scalar.x - v.y, + scalar.x - v.z); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<3, T, Q> operator-(vec<3, T, Q> const& v1, vec<3, T, Q> const& v2) + { + return vec<3, T, Q>( + v1.x - v2.x, + v1.y - v2.y, + v1.z - v2.z); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<3, T, Q> operator*(vec<3, T, Q> const& v, T scalar) + { + return vec<3, T, Q>( + v.x * scalar, + v.y * scalar, + v.z * scalar); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<3, T, Q> operator*(vec<3, T, Q> const& v, vec<1, T, Q> const& scalar) + { + return vec<3, T, Q>( + v.x * scalar.x, + v.y * scalar.x, + v.z * scalar.x); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<3, T, Q> operator*(T scalar, vec<3, T, Q> const& v) + { + return vec<3, T, Q>( + scalar * v.x, + scalar * v.y, + scalar * v.z); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<3, T, Q> operator*(vec<1, T, Q> const& scalar, vec<3, T, Q> const& v) + { + return vec<3, T, Q>( + scalar.x * v.x, + scalar.x * v.y, + scalar.x * v.z); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<3, T, Q> operator*(vec<3, T, Q> const& v1, vec<3, T, Q> const& v2) + { + return vec<3, T, Q>( + v1.x * v2.x, + v1.y * v2.y, + v1.z * v2.z); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<3, T, Q> operator/(vec<3, T, Q> const& v, T scalar) + { + return vec<3, T, Q>( + v.x / scalar, + v.y / scalar, + v.z / scalar); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<3, T, Q> operator/(vec<3, T, Q> const& v, vec<1, T, Q> const& scalar) + { + return vec<3, T, Q>( + v.x / scalar.x, + v.y / scalar.x, + v.z / scalar.x); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<3, T, Q> operator/(T scalar, vec<3, T, Q> const& v) + { + return vec<3, T, Q>( + scalar / v.x, + scalar / v.y, + scalar / v.z); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<3, T, Q> operator/(vec<1, T, Q> const& scalar, vec<3, T, Q> const& v) + { + return vec<3, T, Q>( + scalar.x / v.x, + scalar.x / v.y, + scalar.x / v.z); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<3, T, Q> operator/(vec<3, T, Q> const& v1, vec<3, T, Q> const& v2) + { + return vec<3, T, Q>( + v1.x / v2.x, + v1.y / v2.y, + v1.z / v2.z); + } + + // -- Binary bit operators -- + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<3, T, Q> operator%(vec<3, T, Q> const& v, T scalar) + { + return vec<3, T, Q>( + v.x % scalar, + v.y % scalar, + v.z % scalar); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<3, T, Q> operator%(vec<3, T, Q> const& v, vec<1, T, Q> const& scalar) + { + return vec<3, T, Q>( + v.x % scalar.x, + v.y % scalar.x, + v.z % scalar.x); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<3, T, Q> operator%(T scalar, vec<3, T, Q> const& v) + { + return vec<3, T, Q>( + scalar % v.x, + scalar % v.y, + scalar % v.z); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<3, T, Q> operator%(vec<1, T, Q> const& scalar, vec<3, T, Q> const& v) + { + return vec<3, T, Q>( + scalar.x % v.x, + scalar.x % v.y, + scalar.x % v.z); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<3, T, Q> operator%(vec<3, T, Q> const& v1, vec<3, T, Q> const& v2) + { + return vec<3, T, Q>( + v1.x % v2.x, + v1.y % v2.y, + v1.z % v2.z); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<3, T, Q> operator&(vec<3, T, Q> const& v, T scalar) + { + return vec<3, T, Q>( + v.x & scalar, + v.y & scalar, + v.z & scalar); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<3, T, Q> operator&(vec<3, T, Q> const& v, vec<1, T, Q> const& scalar) + { + return vec<3, T, Q>( + v.x & scalar.x, + v.y & scalar.x, + v.z & scalar.x); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<3, T, Q> operator&(T scalar, vec<3, T, Q> const& v) + { + return vec<3, T, Q>( + scalar & v.x, + scalar & v.y, + scalar & v.z); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<3, T, Q> operator&(vec<1, T, Q> const& scalar, vec<3, T, Q> const& v) + { + return vec<3, T, Q>( + scalar.x & v.x, + scalar.x & v.y, + scalar.x & v.z); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<3, T, Q> operator&(vec<3, T, Q> const& v1, vec<3, T, Q> const& v2) + { + return vec<3, T, Q>( + v1.x & v2.x, + v1.y & v2.y, + v1.z & v2.z); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<3, T, Q> operator|(vec<3, T, Q> const& v, T scalar) + { + return vec<3, T, Q>( + v.x | scalar, + v.y | scalar, + v.z | scalar); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<3, T, Q> operator|(vec<3, T, Q> const& v, vec<1, T, Q> const& scalar) + { + return vec<3, T, Q>( + v.x | scalar.x, + v.y | scalar.x, + v.z | scalar.x); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<3, T, Q> operator|(T scalar, vec<3, T, Q> const& v) + { + return vec<3, T, Q>( + scalar | v.x, + scalar | v.y, + scalar | v.z); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<3, T, Q> operator|(vec<1, T, Q> const& scalar, vec<3, T, Q> const& v) + { + return vec<3, T, Q>( + scalar.x | v.x, + scalar.x | v.y, + scalar.x | v.z); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<3, T, Q> operator|(vec<3, T, Q> const& v1, vec<3, T, Q> const& v2) + { + return vec<3, T, Q>( + v1.x | v2.x, + v1.y | v2.y, + v1.z | v2.z); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<3, T, Q> operator^(vec<3, T, Q> const& v, T scalar) + { + return vec<3, T, Q>( + v.x ^ scalar, + v.y ^ scalar, + v.z ^ scalar); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<3, T, Q> operator^(vec<3, T, Q> const& v, vec<1, T, Q> const& scalar) + { + return vec<3, T, Q>( + v.x ^ scalar.x, + v.y ^ scalar.x, + v.z ^ scalar.x); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<3, T, Q> operator^(T scalar, vec<3, T, Q> const& v) + { + return vec<3, T, Q>( + scalar ^ v.x, + scalar ^ v.y, + scalar ^ v.z); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<3, T, Q> operator^(vec<1, T, Q> const& scalar, vec<3, T, Q> const& v) + { + return vec<3, T, Q>( + scalar.x ^ v.x, + scalar.x ^ v.y, + scalar.x ^ v.z); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<3, T, Q> operator^(vec<3, T, Q> const& v1, vec<3, T, Q> const& v2) + { + return vec<3, T, Q>( + v1.x ^ v2.x, + v1.y ^ v2.y, + v1.z ^ v2.z); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<3, T, Q> operator<<(vec<3, T, Q> const& v, T scalar) + { + return vec<3, T, Q>( + v.x << scalar, + v.y << scalar, + v.z << scalar); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<3, T, Q> operator<<(vec<3, T, Q> const& v, vec<1, T, Q> const& scalar) + { + return vec<3, T, Q>( + v.x << scalar.x, + v.y << scalar.x, + v.z << scalar.x); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<3, T, Q> operator<<(T scalar, vec<3, T, Q> const& v) + { + return vec<3, T, Q>( + scalar << v.x, + scalar << v.y, + scalar << v.z); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<3, T, Q> operator<<(vec<1, T, Q> const& scalar, vec<3, T, Q> const& v) + { + return vec<3, T, Q>( + scalar.x << v.x, + scalar.x << v.y, + scalar.x << v.z); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<3, T, Q> operator<<(vec<3, T, Q> const& v1, vec<3, T, Q> const& v2) + { + return vec<3, T, Q>( + v1.x << v2.x, + v1.y << v2.y, + v1.z << v2.z); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<3, T, Q> operator>>(vec<3, T, Q> const& v, T scalar) + { + return vec<3, T, Q>( + v.x >> scalar, + v.y >> scalar, + v.z >> scalar); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<3, T, Q> operator>>(vec<3, T, Q> const& v, vec<1, T, Q> const& scalar) + { + return vec<3, T, Q>( + v.x >> scalar.x, + v.y >> scalar.x, + v.z >> scalar.x); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<3, T, Q> operator>>(T scalar, vec<3, T, Q> const& v) + { + return vec<3, T, Q>( + scalar >> v.x, + scalar >> v.y, + scalar >> v.z); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<3, T, Q> operator>>(vec<1, T, Q> const& scalar, vec<3, T, Q> const& v) + { + return vec<3, T, Q>( + scalar.x >> v.x, + scalar.x >> v.y, + scalar.x >> v.z); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<3, T, Q> operator>>(vec<3, T, Q> const& v1, vec<3, T, Q> const& v2) + { + return vec<3, T, Q>( + v1.x >> v2.x, + v1.y >> v2.y, + v1.z >> v2.z); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<3, T, Q> operator~(vec<3, T, Q> const& v) + { + return vec<3, T, Q>( + ~v.x, + ~v.y, + ~v.z); + } + + // -- Boolean operators -- + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR bool operator==(vec<3, T, Q> const& v1, vec<3, T, Q> const& v2) + { + return + detail::compute_equal::is_iec559>::call(v1.x, v2.x) && + detail::compute_equal::is_iec559>::call(v1.y, v2.y) && + detail::compute_equal::is_iec559>::call(v1.z, v2.z); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR bool operator!=(vec<3, T, Q> const& v1, vec<3, T, Q> const& v2) + { + return !(v1 == v2); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<3, bool, Q> operator&&(vec<3, bool, Q> const& v1, vec<3, bool, Q> const& v2) + { + return vec<3, bool, Q>(v1.x && v2.x, v1.y && v2.y, v1.z && v2.z); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<3, bool, Q> operator||(vec<3, bool, Q> const& v1, vec<3, bool, Q> const& v2) + { + return vec<3, bool, Q>(v1.x || v2.x, v1.y || v2.y, v1.z || v2.z); + } +}//namespace glm diff --git a/src/GLMath/glm/detail/type_vec4.hpp b/src/GLMath/glm/detail/type_vec4.hpp new file mode 100644 index 000000000..15fabe12a --- /dev/null +++ b/src/GLMath/glm/detail/type_vec4.hpp @@ -0,0 +1,504 @@ +/// @ref core +/// @file glm/detail/type_vec4.hpp + +#pragma once + +#include "qualifier.hpp" +#if GLM_CONFIG_SWIZZLE == GLM_SWIZZLE_OPERATOR +# include "_swizzle.hpp" +#elif GLM_CONFIG_SWIZZLE == GLM_SWIZZLE_FUNCTION +# include "_swizzle_func.hpp" +#endif +#include + +namespace glm +{ + template + struct vec<4, T, Q> + { + // -- Implementation detail -- + + typedef T value_type; + typedef vec<4, T, Q> type; + typedef vec<4, bool, Q> bool_type; + + // -- Data -- + +# if GLM_SILENT_WARNINGS == GLM_ENABLE +# if GLM_COMPILER & GLM_COMPILER_GCC +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Wpedantic" +# elif GLM_COMPILER & GLM_COMPILER_CLANG +# pragma clang diagnostic push +# pragma clang diagnostic ignored "-Wgnu-anonymous-struct" +# pragma clang diagnostic ignored "-Wnested-anon-types" +# elif GLM_COMPILER & GLM_COMPILER_VC +# pragma warning(push) +# pragma warning(disable: 4201) // nonstandard extension used : nameless struct/union +# endif +# endif + +# if GLM_CONFIG_XYZW_ONLY + T x, y, z, w; +# elif GLM_CONFIG_ANONYMOUS_STRUCT == GLM_ENABLE + union + { + struct { T x, y, z, w; }; + struct { T r, g, b, a; }; + struct { T s, t, p, q; }; + + typename detail::storage<4, T, detail::is_aligned::value>::type data; + +# if GLM_CONFIG_SWIZZLE == GLM_SWIZZLE_OPERATOR + GLM_SWIZZLE4_2_MEMBERS(T, Q, x, y, z, w) + GLM_SWIZZLE4_2_MEMBERS(T, Q, r, g, b, a) + GLM_SWIZZLE4_2_MEMBERS(T, Q, s, t, p, q) + GLM_SWIZZLE4_3_MEMBERS(T, Q, x, y, z, w) + GLM_SWIZZLE4_3_MEMBERS(T, Q, r, g, b, a) + GLM_SWIZZLE4_3_MEMBERS(T, Q, s, t, p, q) + GLM_SWIZZLE4_4_MEMBERS(T, Q, x, y, z, w) + GLM_SWIZZLE4_4_MEMBERS(T, Q, r, g, b, a) + GLM_SWIZZLE4_4_MEMBERS(T, Q, s, t, p, q) +# endif + }; +# else + union { T x, r, s; }; + union { T y, g, t; }; + union { T z, b, p; }; + union { T w, a, q; }; + +# if GLM_CONFIG_SWIZZLE == GLM_SWIZZLE_FUNCTION + GLM_SWIZZLE_GEN_VEC_FROM_VEC4(T, Q) +# endif +# endif + +# if GLM_SILENT_WARNINGS == GLM_ENABLE +# if GLM_COMPILER & GLM_COMPILER_CLANG +# pragma clang diagnostic pop +# elif GLM_COMPILER & GLM_COMPILER_GCC +# pragma GCC diagnostic pop +# elif GLM_COMPILER & GLM_COMPILER_VC +# pragma warning(pop) +# endif +# endif + + // -- Component accesses -- + + /// Return the count of components of the vector + typedef length_t length_type; + GLM_FUNC_DECL static GLM_CONSTEXPR length_type length(){return 4;} + + GLM_FUNC_DECL GLM_CONSTEXPR T & operator[](length_type i); + GLM_FUNC_DECL GLM_CONSTEXPR T const& operator[](length_type i) const; + + // -- Implicit basic constructors -- + + GLM_FUNC_DECL GLM_CONSTEXPR vec() GLM_DEFAULT; + GLM_FUNC_DECL GLM_CONSTEXPR vec(vec<4, T, Q> const& v) GLM_DEFAULT; + template + GLM_FUNC_DECL GLM_CONSTEXPR vec(vec<4, T, P> const& v); + + // -- Explicit basic constructors -- + + GLM_FUNC_DECL GLM_CONSTEXPR explicit vec(T scalar); + GLM_FUNC_DECL GLM_CONSTEXPR vec(T x, T y, T z, T w); + + // -- Conversion scalar constructors -- + + template + GLM_FUNC_DECL GLM_CONSTEXPR explicit vec(vec<1, U, P> const& v); + + /// Explicit conversions (From section 5.4.1 Conversion and scalar constructors of GLSL 1.30.08 specification) + template + GLM_FUNC_DECL GLM_CONSTEXPR vec(X _x, Y _y, Z _z, W _w); + template + GLM_FUNC_DECL GLM_CONSTEXPR vec(vec<1, X, Q> const& _x, Y _y, Z _z, W _w); + template + GLM_FUNC_DECL GLM_CONSTEXPR vec(X _x, vec<1, Y, Q> const& _y, Z _z, W _w); + template + GLM_FUNC_DECL GLM_CONSTEXPR vec(vec<1, X, Q> const& _x, vec<1, Y, Q> const& _y, Z _z, W _w); + template + GLM_FUNC_DECL GLM_CONSTEXPR vec(X _x, Y _y, vec<1, Z, Q> const& _z, W _w); + template + GLM_FUNC_DECL GLM_CONSTEXPR vec(vec<1, X, Q> const& _x, Y _y, vec<1, Z, Q> const& _z, W _w); + template + GLM_FUNC_DECL GLM_CONSTEXPR vec(X _x, vec<1, Y, Q> const& _y, vec<1, Z, Q> const& _z, W _w); + template + GLM_FUNC_DECL GLM_CONSTEXPR vec(vec<1, X, Q> const& _x, vec<1, Y, Q> const& _y, vec<1, Z, Q> const& _z, W _w); + template + GLM_FUNC_DECL GLM_CONSTEXPR vec(vec<1, X, Q> const& _x, Y _y, Z _z, vec<1, W, Q> const& _w); + template + GLM_FUNC_DECL GLM_CONSTEXPR vec(X _x, vec<1, Y, Q> const& _y, Z _z, vec<1, W, Q> const& _w); + template + GLM_FUNC_DECL GLM_CONSTEXPR vec(vec<1, X, Q> const& _x, vec<1, Y, Q> const& _y, Z _z, vec<1, W, Q> const& _w); + template + GLM_FUNC_DECL GLM_CONSTEXPR vec(X _x, Y _y, vec<1, Z, Q> const& _z, vec<1, W, Q> const& _w); + template + GLM_FUNC_DECL GLM_CONSTEXPR vec(vec<1, X, Q> const& _x, Y _y, vec<1, Z, Q> const& _z, vec<1, W, Q> const& _w); + template + GLM_FUNC_DECL GLM_CONSTEXPR vec(X _x, vec<1, Y, Q> const& _y, vec<1, Z, Q> const& _z, vec<1, W, Q> const& _w); + template + GLM_FUNC_DECL GLM_CONSTEXPR vec(vec<1, X, Q> const& _x, vec<1, Y, Q> const& _Y, vec<1, Z, Q> const& _z, vec<1, W, Q> const& _w); + + // -- Conversion vector constructors -- + + /// Explicit conversions (From section 5.4.1 Conversion and scalar constructors of GLSL 1.30.08 specification) + template + GLM_FUNC_DECL GLM_CONSTEXPR vec(vec<2, A, P> const& _xy, B _z, C _w); + /// Explicit conversions (From section 5.4.1 Conversion and scalar constructors of GLSL 1.30.08 specification) + template + GLM_FUNC_DECL GLM_CONSTEXPR vec(vec<2, A, P> const& _xy, vec<1, B, P> const& _z, C _w); + /// Explicit conversions (From section 5.4.1 Conversion and scalar constructors of GLSL 1.30.08 specification) + template + GLM_FUNC_DECL GLM_CONSTEXPR vec(vec<2, A, P> const& _xy, B _z, vec<1, C, P> const& _w); + /// Explicit conversions (From section 5.4.1 Conversion and scalar constructors of GLSL 1.30.08 specification) + template + GLM_FUNC_DECL GLM_CONSTEXPR vec(vec<2, A, P> const& _xy, vec<1, B, P> const& _z, vec<1, C, P> const& _w); + /// Explicit conversions (From section 5.4.1 Conversion and scalar constructors of GLSL 1.30.08 specification) + template + GLM_FUNC_DECL GLM_CONSTEXPR vec(A _x, vec<2, B, P> const& _yz, C _w); + /// Explicit conversions (From section 5.4.1 Conversion and scalar constructors of GLSL 1.30.08 specification) + template + GLM_FUNC_DECL GLM_CONSTEXPR vec(vec<1, A, P> const& _x, vec<2, B, P> const& _yz, C _w); + /// Explicit conversions (From section 5.4.1 Conversion and scalar constructors of GLSL 1.30.08 specification) + template + GLM_FUNC_DECL GLM_CONSTEXPR vec(A _x, vec<2, B, P> const& _yz, vec<1, C, P> const& _w); + /// Explicit conversions (From section 5.4.1 Conversion and scalar constructors of GLSL 1.30.08 specification) + template + GLM_FUNC_DECL GLM_CONSTEXPR vec(vec<1, A, P> const& _x, vec<2, B, P> const& _yz, vec<1, C, P> const& _w); + /// Explicit conversions (From section 5.4.1 Conversion and scalar constructors of GLSL 1.30.08 specification) + template + GLM_FUNC_DECL GLM_CONSTEXPR vec(A _x, B _y, vec<2, C, P> const& _zw); + /// Explicit conversions (From section 5.4.1 Conversion and scalar constructors of GLSL 1.30.08 specification) + template + GLM_FUNC_DECL GLM_CONSTEXPR vec(vec<1, A, P> const& _x, B _y, vec<2, C, P> const& _zw); + /// Explicit conversions (From section 5.4.1 Conversion and scalar constructors of GLSL 1.30.08 specification) + template + GLM_FUNC_DECL GLM_CONSTEXPR vec(A _x, vec<1, B, P> const& _y, vec<2, C, P> const& _zw); + /// Explicit conversions (From section 5.4.1 Conversion and scalar constructors of GLSL 1.30.08 specification) + template + GLM_FUNC_DECL GLM_CONSTEXPR vec(vec<1, A, P> const& _x, vec<1, B, P> const& _y, vec<2, C, P> const& _zw); + /// Explicit conversions (From section 5.4.1 Conversion and scalar constructors of GLSL 1.30.08 specification) + template + GLM_FUNC_DECL GLM_CONSTEXPR vec(vec<3, A, P> const& _xyz, B _w); + /// Explicit conversions (From section 5.4.1 Conversion and scalar constructors of GLSL 1.30.08 specification) + template + GLM_FUNC_DECL GLM_CONSTEXPR vec(vec<3, A, P> const& _xyz, vec<1, B, P> const& _w); + /// Explicit conversions (From section 5.4.1 Conversion and scalar constructors of GLSL 1.30.08 specification) + template + GLM_FUNC_DECL GLM_CONSTEXPR vec(A _x, vec<3, B, P> const& _yzw); + /// Explicit conversions (From section 5.4.1 Conversion and scalar constructors of GLSL 1.30.08 specification) + template + GLM_FUNC_DECL GLM_CONSTEXPR vec(vec<1, A, P> const& _x, vec<3, B, P> const& _yzw); + /// Explicit conversions (From section 5.4.1 Conversion and scalar constructors of GLSL 1.30.08 specification) + template + GLM_FUNC_DECL GLM_CONSTEXPR vec(vec<2, A, P> const& _xy, vec<2, B, P> const& _zw); + + /// Explicit conversions (From section 5.4.1 Conversion and scalar constructors of GLSL 1.30.08 specification) + template + GLM_FUNC_DECL GLM_CONSTEXPR GLM_EXPLICIT vec(vec<4, U, P> const& v); + + // -- Swizzle constructors -- +# if GLM_CONFIG_SWIZZLE == GLM_SWIZZLE_OPERATOR + template + GLM_FUNC_DECL GLM_CONSTEXPR vec(detail::_swizzle<4, T, Q, E0, E1, E2, E3> const& that) + { + *this = that(); + } + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec(detail::_swizzle<2, T, Q, E0, E1, -1, -2> const& v, detail::_swizzle<2, T, Q, F0, F1, -1, -2> const& u) + { + *this = vec<4, T, Q>(v(), u()); + } + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec(T const& x, T const& y, detail::_swizzle<2, T, Q, E0, E1, -1, -2> const& v) + { + *this = vec<4, T, Q>(x, y, v()); + } + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec(T const& x, detail::_swizzle<2, T, Q, E0, E1, -1, -2> const& v, T const& w) + { + *this = vec<4, T, Q>(x, v(), w); + } + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec(detail::_swizzle<2, T, Q, E0, E1, -1, -2> const& v, T const& z, T const& w) + { + *this = vec<4, T, Q>(v(), z, w); + } + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec(detail::_swizzle<3, T, Q, E0, E1, E2, -1> const& v, T const& w) + { + *this = vec<4, T, Q>(v(), w); + } + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec(T const& x, detail::_swizzle<3, T, Q, E0, E1, E2, -1> const& v) + { + *this = vec<4, T, Q>(x, v()); + } +# endif//GLM_CONFIG_SWIZZLE == GLM_SWIZZLE_OPERATOR + + // -- Unary arithmetic operators -- + + GLM_FUNC_DECL GLM_CONSTEXPR vec<4, T, Q>& operator=(vec<4, T, Q> const& v) GLM_DEFAULT; + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<4, T, Q>& operator=(vec<4, U, Q> const& v); + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<4, T, Q>& operator+=(U scalar); + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<4, T, Q>& operator+=(vec<1, U, Q> const& v); + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<4, T, Q>& operator+=(vec<4, U, Q> const& v); + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<4, T, Q>& operator-=(U scalar); + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<4, T, Q>& operator-=(vec<1, U, Q> const& v); + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<4, T, Q>& operator-=(vec<4, U, Q> const& v); + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<4, T, Q>& operator*=(U scalar); + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<4, T, Q>& operator*=(vec<1, U, Q> const& v); + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<4, T, Q>& operator*=(vec<4, U, Q> const& v); + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<4, T, Q>& operator/=(U scalar); + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<4, T, Q>& operator/=(vec<1, U, Q> const& v); + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<4, T, Q>& operator/=(vec<4, U, Q> const& v); + + // -- Increment and decrement operators -- + + GLM_FUNC_DECL GLM_CONSTEXPR vec<4, T, Q> & operator++(); + GLM_FUNC_DECL GLM_CONSTEXPR vec<4, T, Q> & operator--(); + GLM_FUNC_DECL GLM_CONSTEXPR vec<4, T, Q> operator++(int); + GLM_FUNC_DECL GLM_CONSTEXPR vec<4, T, Q> operator--(int); + + // -- Unary bit operators -- + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<4, T, Q> & operator%=(U scalar); + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<4, T, Q> & operator%=(vec<1, U, Q> const& v); + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<4, T, Q> & operator%=(vec<4, U, Q> const& v); + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<4, T, Q> & operator&=(U scalar); + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<4, T, Q> & operator&=(vec<1, U, Q> const& v); + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<4, T, Q> & operator&=(vec<4, U, Q> const& v); + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<4, T, Q> & operator|=(U scalar); + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<4, T, Q> & operator|=(vec<1, U, Q> const& v); + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<4, T, Q> & operator|=(vec<4, U, Q> const& v); + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<4, T, Q> & operator^=(U scalar); + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<4, T, Q> & operator^=(vec<1, U, Q> const& v); + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<4, T, Q> & operator^=(vec<4, U, Q> const& v); + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<4, T, Q> & operator<<=(U scalar); + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<4, T, Q> & operator<<=(vec<1, U, Q> const& v); + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<4, T, Q> & operator<<=(vec<4, U, Q> const& v); + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<4, T, Q> & operator>>=(U scalar); + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<4, T, Q> & operator>>=(vec<1, U, Q> const& v); + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<4, T, Q> & operator>>=(vec<4, U, Q> const& v); + }; + + // -- Unary operators -- + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<4, T, Q> operator+(vec<4, T, Q> const& v); + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<4, T, Q> operator-(vec<4, T, Q> const& v); + + // -- Binary operators -- + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<4, T, Q> operator+(vec<4, T, Q> const& v, T const & scalar); + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<4, T, Q> operator+(vec<4, T, Q> const& v1, vec<1, T, Q> const& v2); + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<4, T, Q> operator+(T scalar, vec<4, T, Q> const& v); + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<4, T, Q> operator+(vec<1, T, Q> const& v1, vec<4, T, Q> const& v2); + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<4, T, Q> operator+(vec<4, T, Q> const& v1, vec<4, T, Q> const& v2); + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<4, T, Q> operator-(vec<4, T, Q> const& v, T const & scalar); + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<4, T, Q> operator-(vec<4, T, Q> const& v1, vec<1, T, Q> const& v2); + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<4, T, Q> operator-(T scalar, vec<4, T, Q> const& v); + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<4, T, Q> operator-(vec<1, T, Q> const& v1, vec<4, T, Q> const& v2); + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<4, T, Q> operator-(vec<4, T, Q> const& v1, vec<4, T, Q> const& v2); + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<4, T, Q> operator*(vec<4, T, Q> const& v, T const & scalar); + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<4, T, Q> operator*(vec<4, T, Q> const& v1, vec<1, T, Q> const& v2); + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<4, T, Q> operator*(T scalar, vec<4, T, Q> const& v); + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<4, T, Q> operator*(vec<1, T, Q> const& v1, vec<4, T, Q> const& v2); + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<4, T, Q> operator*(vec<4, T, Q> const& v1, vec<4, T, Q> const& v2); + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<4, T, Q> operator/(vec<4, T, Q> const& v, T const & scalar); + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<4, T, Q> operator/(vec<4, T, Q> const& v1, vec<1, T, Q> const& v2); + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<4, T, Q> operator/(T scalar, vec<4, T, Q> const& v); + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<4, T, Q> operator/(vec<1, T, Q> const& v1, vec<4, T, Q> const& v2); + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<4, T, Q> operator/(vec<4, T, Q> const& v1, vec<4, T, Q> const& v2); + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<4, T, Q> operator%(vec<4, T, Q> const& v, T scalar); + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<4, T, Q> operator%(vec<4, T, Q> const& v, vec<1, T, Q> const& scalar); + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<4, T, Q> operator%(T scalar, vec<4, T, Q> const& v); + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<4, T, Q> operator%(vec<1, T, Q> const& scalar, vec<4, T, Q> const& v); + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<4, T, Q> operator%(vec<4, T, Q> const& v1, vec<4, T, Q> const& v2); + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<4, T, Q> operator&(vec<4, T, Q> const& v, T scalar); + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<4, T, Q> operator&(vec<4, T, Q> const& v, vec<1, T, Q> const& scalar); + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<4, T, Q> operator&(T scalar, vec<4, T, Q> const& v); + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<4, T, Q> operator&(vec<1, T, Q> const& scalar, vec<4, T, Q> const& v); + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<4, T, Q> operator&(vec<4, T, Q> const& v1, vec<4, T, Q> const& v2); + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<4, T, Q> operator|(vec<4, T, Q> const& v, T scalar); + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<4, T, Q> operator|(vec<4, T, Q> const& v, vec<1, T, Q> const& scalar); + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<4, T, Q> operator|(T scalar, vec<4, T, Q> const& v); + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<4, T, Q> operator|(vec<1, T, Q> const& scalar, vec<4, T, Q> const& v); + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<4, T, Q> operator|(vec<4, T, Q> const& v1, vec<4, T, Q> const& v2); + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<4, T, Q> operator^(vec<4, T, Q> const& v, T scalar); + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<4, T, Q> operator^(vec<4, T, Q> const& v, vec<1, T, Q> const& scalar); + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<4, T, Q> operator^(T scalar, vec<4, T, Q> const& v); + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<4, T, Q> operator^(vec<1, T, Q> const& scalar, vec<4, T, Q> const& v); + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<4, T, Q> operator^(vec<4, T, Q> const& v1, vec<4, T, Q> const& v2); + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<4, T, Q> operator<<(vec<4, T, Q> const& v, T scalar); + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<4, T, Q> operator<<(vec<4, T, Q> const& v, vec<1, T, Q> const& scalar); + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<4, T, Q> operator<<(T scalar, vec<4, T, Q> const& v); + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<4, T, Q> operator<<(vec<1, T, Q> const& scalar, vec<4, T, Q> const& v); + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<4, T, Q> operator<<(vec<4, T, Q> const& v1, vec<4, T, Q> const& v2); + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<4, T, Q> operator>>(vec<4, T, Q> const& v, T scalar); + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<4, T, Q> operator>>(vec<4, T, Q> const& v, vec<1, T, Q> const& scalar); + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<4, T, Q> operator>>(T scalar, vec<4, T, Q> const& v); + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<4, T, Q> operator>>(vec<1, T, Q> const& scalar, vec<4, T, Q> const& v); + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<4, T, Q> operator>>(vec<4, T, Q> const& v1, vec<4, T, Q> const& v2); + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<4, T, Q> operator~(vec<4, T, Q> const& v); + + // -- Boolean operators -- + + template + GLM_FUNC_DECL GLM_CONSTEXPR bool operator==(vec<4, T, Q> const& v1, vec<4, T, Q> const& v2); + + template + GLM_FUNC_DECL GLM_CONSTEXPR bool operator!=(vec<4, T, Q> const& v1, vec<4, T, Q> const& v2); + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<4, bool, Q> operator&&(vec<4, bool, Q> const& v1, vec<4, bool, Q> const& v2); + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<4, bool, Q> operator||(vec<4, bool, Q> const& v1, vec<4, bool, Q> const& v2); +}//namespace glm + +#ifndef GLM_EXTERNAL_TEMPLATE +#include "type_vec4.inl" +#endif//GLM_EXTERNAL_TEMPLATE diff --git a/src/GLMath/glm/detail/type_vec4.inl b/src/GLMath/glm/detail/type_vec4.inl new file mode 100644 index 000000000..3c212d98b --- /dev/null +++ b/src/GLMath/glm/detail/type_vec4.inl @@ -0,0 +1,1140 @@ +/// @ref core + +#include "compute_vector_relational.hpp" + +namespace glm{ +namespace detail +{ + template + struct compute_vec4_add + { + GLM_FUNC_QUALIFIER GLM_CONSTEXPR static vec<4, T, Q> call(vec<4, T, Q> const& a, vec<4, T, Q> const& b) + { + return vec<4, T, Q>(a.x + b.x, a.y + b.y, a.z + b.z, a.w + b.w); + } + }; + + template + struct compute_vec4_sub + { + GLM_FUNC_QUALIFIER GLM_CONSTEXPR static vec<4, T, Q> call(vec<4, T, Q> const& a, vec<4, T, Q> const& b) + { + return vec<4, T, Q>(a.x - b.x, a.y - b.y, a.z - b.z, a.w - b.w); + } + }; + + template + struct compute_vec4_mul + { + GLM_FUNC_QUALIFIER GLM_CONSTEXPR static vec<4, T, Q> call(vec<4, T, Q> const& a, vec<4, T, Q> const& b) + { + return vec<4, T, Q>(a.x * b.x, a.y * b.y, a.z * b.z, a.w * b.w); + } + }; + + template + struct compute_vec4_div + { + GLM_FUNC_QUALIFIER GLM_CONSTEXPR static vec<4, T, Q> call(vec<4, T, Q> const& a, vec<4, T, Q> const& b) + { + return vec<4, T, Q>(a.x / b.x, a.y / b.y, a.z / b.z, a.w / b.w); + } + }; + + template + struct compute_vec4_mod + { + GLM_FUNC_QUALIFIER GLM_CONSTEXPR static vec<4, T, Q> call(vec<4, T, Q> const& a, vec<4, T, Q> const& b) + { + return vec<4, T, Q>(a.x % b.x, a.y % b.y, a.z % b.z, a.w % b.w); + } + }; + + template + struct compute_vec4_and + { + GLM_FUNC_QUALIFIER GLM_CONSTEXPR static vec<4, T, Q> call(vec<4, T, Q> const& a, vec<4, T, Q> const& b) + { + return vec<4, T, Q>(a.x & b.x, a.y & b.y, a.z & b.z, a.w & b.w); + } + }; + + template + struct compute_vec4_or + { + GLM_FUNC_QUALIFIER GLM_CONSTEXPR static vec<4, T, Q> call(vec<4, T, Q> const& a, vec<4, T, Q> const& b) + { + return vec<4, T, Q>(a.x | b.x, a.y | b.y, a.z | b.z, a.w | b.w); + } + }; + + template + struct compute_vec4_xor + { + GLM_FUNC_QUALIFIER GLM_CONSTEXPR static vec<4, T, Q> call(vec<4, T, Q> const& a, vec<4, T, Q> const& b) + { + return vec<4, T, Q>(a.x ^ b.x, a.y ^ b.y, a.z ^ b.z, a.w ^ b.w); + } + }; + + template + struct compute_vec4_shift_left + { + GLM_FUNC_QUALIFIER GLM_CONSTEXPR static vec<4, T, Q> call(vec<4, T, Q> const& a, vec<4, T, Q> const& b) + { + return vec<4, T, Q>(a.x << b.x, a.y << b.y, a.z << b.z, a.w << b.w); + } + }; + + template + struct compute_vec4_shift_right + { + GLM_FUNC_QUALIFIER GLM_CONSTEXPR static vec<4, T, Q> call(vec<4, T, Q> const& a, vec<4, T, Q> const& b) + { + return vec<4, T, Q>(a.x >> b.x, a.y >> b.y, a.z >> b.z, a.w >> b.w); + } + }; + + template + struct compute_vec4_equal + { + GLM_FUNC_QUALIFIER GLM_CONSTEXPR static bool call(vec<4, T, Q> const& v1, vec<4, T, Q> const& v2) + { + return + detail::compute_equal::is_iec559>::call(v1.x, v2.x) && + detail::compute_equal::is_iec559>::call(v1.y, v2.y) && + detail::compute_equal::is_iec559>::call(v1.z, v2.z) && + detail::compute_equal::is_iec559>::call(v1.w, v2.w); + } + }; + + template + struct compute_vec4_nequal + { + GLM_FUNC_QUALIFIER GLM_CONSTEXPR static bool call(vec<4, T, Q> const& v1, vec<4, T, Q> const& v2) + { + return !compute_vec4_equal::value, sizeof(T) * 8, detail::is_aligned::value>::call(v1, v2); + } + }; + + template + struct compute_vec4_bitwise_not + { + GLM_FUNC_QUALIFIER GLM_CONSTEXPR static vec<4, T, Q> call(vec<4, T, Q> const& v) + { + return vec<4, T, Q>(~v.x, ~v.y, ~v.z, ~v.w); + } + }; +}//namespace detail + + // -- Implicit basic constructors -- + +# if GLM_CONFIG_DEFAULTED_FUNCTIONS == GLM_DISABLE + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<4, T, Q>::vec() +# if GLM_CONFIG_CTOR_INIT != GLM_CTOR_INIT_DISABLE + : x(0), y(0), z(0), w(0) +# endif + {} + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<4, T, Q>::vec(vec<4, T, Q> const& v) + : x(v.x), y(v.y), z(v.z), w(v.w) + {} +# endif + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<4, T, Q>::vec(vec<4, T, P> const& v) + : x(v.x), y(v.y), z(v.z), w(v.w) + {} + + // -- Explicit basic constructors -- + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<4, T, Q>::vec(T scalar) + : x(scalar), y(scalar), z(scalar), w(scalar) + {} + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<4, T, Q>::vec(T _x, T _y, T _z, T _w) + : x(_x), y(_y), z(_z), w(_w) + {} + + // -- Conversion scalar constructors -- + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<4, T, Q>::vec(vec<1, U, P> const& v) + : x(static_cast(v.x)) + , y(static_cast(v.x)) + , z(static_cast(v.x)) + , w(static_cast(v.x)) + {} + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<4, T, Q>::vec(X _x, Y _y, Z _z, W _w) + : x(static_cast(_x)) + , y(static_cast(_y)) + , z(static_cast(_z)) + , w(static_cast(_w)) + {} + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<4, T, Q>::vec(vec<1, X, Q> const& _x, Y _y, Z _z, W _w) + : x(static_cast(_x.x)) + , y(static_cast(_y)) + , z(static_cast(_z)) + , w(static_cast(_w)) + {} + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<4, T, Q>::vec(X _x, vec<1, Y, Q> const& _y, Z _z, W _w) + : x(static_cast(_x)) + , y(static_cast(_y.x)) + , z(static_cast(_z)) + , w(static_cast(_w)) + {} + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<4, T, Q>::vec(vec<1, X, Q> const& _x, vec<1, Y, Q> const& _y, Z _z, W _w) + : x(static_cast(_x.x)) + , y(static_cast(_y.x)) + , z(static_cast(_z)) + , w(static_cast(_w)) + {} + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<4, T, Q>::vec(X _x, Y _y, vec<1, Z, Q> const& _z, W _w) + : x(static_cast(_x)) + , y(static_cast(_y)) + , z(static_cast(_z.x)) + , w(static_cast(_w)) + {} + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<4, T, Q>::vec(vec<1, X, Q> const& _x, Y _y, vec<1, Z, Q> const& _z, W _w) + : x(static_cast(_x.x)) + , y(static_cast(_y)) + , z(static_cast(_z.x)) + , w(static_cast(_w)) + {} + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<4, T, Q>::vec(X _x, vec<1, Y, Q> const& _y, vec<1, Z, Q> const& _z, W _w) + : x(static_cast(_x)) + , y(static_cast(_y.x)) + , z(static_cast(_z.x)) + , w(static_cast(_w)) + {} + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<4, T, Q>::vec(vec<1, X, Q> const& _x, vec<1, Y, Q> const& _y, vec<1, Z, Q> const& _z, W _w) + : x(static_cast(_x.x)) + , y(static_cast(_y.x)) + , z(static_cast(_z.x)) + , w(static_cast(_w)) + {} + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<4, T, Q>::vec(vec<1, X, Q> const& _x, Y _y, Z _z, vec<1, W, Q> const& _w) + : x(static_cast(_x.x)) + , y(static_cast(_y)) + , z(static_cast(_z)) + , w(static_cast(_w.x)) + {} + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<4, T, Q>::vec(X _x, vec<1, Y, Q> const& _y, Z _z, vec<1, W, Q> const& _w) + : x(static_cast(_x)) + , y(static_cast(_y.x)) + , z(static_cast(_z)) + , w(static_cast(_w.x)) + {} + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<4, T, Q>::vec(vec<1, X, Q> const& _x, vec<1, Y, Q> const& _y, Z _z, vec<1, W, Q> const& _w) + : x(static_cast(_x.x)) + , y(static_cast(_y.x)) + , z(static_cast(_z)) + , w(static_cast(_w.x)) + {} + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<4, T, Q>::vec(X _x, Y _y, vec<1, Z, Q> const& _z, vec<1, W, Q> const& _w) + : x(static_cast(_x)) + , y(static_cast(_y)) + , z(static_cast(_z.x)) + , w(static_cast(_w.x)) + {} + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<4, T, Q>::vec(vec<1, X, Q> const& _x, Y _y, vec<1, Z, Q> const& _z, vec<1, W, Q> const& _w) + : x(static_cast(_x.x)) + , y(static_cast(_y)) + , z(static_cast(_z.x)) + , w(static_cast(_w.x)) + {} + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<4, T, Q>::vec(X _x, vec<1, Y, Q> const& _y, vec<1, Z, Q> const& _z, vec<1, W, Q> const& _w) + : x(static_cast(_x)) + , y(static_cast(_y.x)) + , z(static_cast(_z.x)) + , w(static_cast(_w.x)) + {} + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<4, T, Q>::vec(vec<1, X, Q> const& _x, vec<1, Y, Q> const& _y, vec<1, Z, Q> const& _z, vec<1, W, Q> const& _w) + : x(static_cast(_x.x)) + , y(static_cast(_y.x)) + , z(static_cast(_z.x)) + , w(static_cast(_w.x)) + {} + + // -- Conversion vector constructors -- + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<4, T, Q>::vec(vec<2, A, P> const& _xy, B _z, C _w) + : x(static_cast(_xy.x)) + , y(static_cast(_xy.y)) + , z(static_cast(_z)) + , w(static_cast(_w)) + {} + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<4, T, Q>::vec(vec<2, A, P> const& _xy, vec<1, B, P> const& _z, C _w) + : x(static_cast(_xy.x)) + , y(static_cast(_xy.y)) + , z(static_cast(_z.x)) + , w(static_cast(_w)) + {} + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<4, T, Q>::vec(vec<2, A, P> const& _xy, B _z, vec<1, C, P> const& _w) + : x(static_cast(_xy.x)) + , y(static_cast(_xy.y)) + , z(static_cast(_z)) + , w(static_cast(_w.x)) + {} + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<4, T, Q>::vec(vec<2, A, P> const& _xy, vec<1, B, P> const& _z, vec<1, C, P> const& _w) + : x(static_cast(_xy.x)) + , y(static_cast(_xy.y)) + , z(static_cast(_z.x)) + , w(static_cast(_w.x)) + {} + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<4, T, Q>::vec(A _x, vec<2, B, P> const& _yz, C _w) + : x(static_cast(_x)) + , y(static_cast(_yz.x)) + , z(static_cast(_yz.y)) + , w(static_cast(_w)) + {} + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<4, T, Q>::vec(vec<1, A, P> const& _x, vec<2, B, P> const& _yz, C _w) + : x(static_cast(_x.x)) + , y(static_cast(_yz.x)) + , z(static_cast(_yz.y)) + , w(static_cast(_w)) + {} + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<4, T, Q>::vec(A _x, vec<2, B, P> const& _yz, vec<1, C, P> const& _w) + : x(static_cast(_x)) + , y(static_cast(_yz.x)) + , z(static_cast(_yz.y)) + , w(static_cast(_w.x)) + {} + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<4, T, Q>::vec(vec<1, A, P> const& _x, vec<2, B, P> const& _yz, vec<1, C, P> const& _w) + : x(static_cast(_x.x)) + , y(static_cast(_yz.x)) + , z(static_cast(_yz.y)) + , w(static_cast(_w.x)) + {} + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<4, T, Q>::vec(A _x, B _y, vec<2, C, P> const& _zw) + : x(static_cast(_x)) + , y(static_cast(_y)) + , z(static_cast(_zw.x)) + , w(static_cast(_zw.y)) + {} + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<4, T, Q>::vec(vec<1, A, P> const& _x, B _y, vec<2, C, P> const& _zw) + : x(static_cast(_x.x)) + , y(static_cast(_y)) + , z(static_cast(_zw.x)) + , w(static_cast(_zw.y)) + {} + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<4, T, Q>::vec(A _x, vec<1, B, P> const& _y, vec<2, C, P> const& _zw) + : x(static_cast(_x)) + , y(static_cast(_y.x)) + , z(static_cast(_zw.x)) + , w(static_cast(_zw.y)) + {} + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<4, T, Q>::vec(vec<1, A, P> const& _x, vec<1, B, P> const& _y, vec<2, C, P> const& _zw) + : x(static_cast(_x.x)) + , y(static_cast(_y.x)) + , z(static_cast(_zw.x)) + , w(static_cast(_zw.y)) + {} + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<4, T, Q>::vec(vec<3, A, P> const& _xyz, B _w) + : x(static_cast(_xyz.x)) + , y(static_cast(_xyz.y)) + , z(static_cast(_xyz.z)) + , w(static_cast(_w)) + {} + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<4, T, Q>::vec(vec<3, A, P> const& _xyz, vec<1, B, P> const& _w) + : x(static_cast(_xyz.x)) + , y(static_cast(_xyz.y)) + , z(static_cast(_xyz.z)) + , w(static_cast(_w.x)) + {} + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<4, T, Q>::vec(A _x, vec<3, B, P> const& _yzw) + : x(static_cast(_x)) + , y(static_cast(_yzw.x)) + , z(static_cast(_yzw.y)) + , w(static_cast(_yzw.z)) + {} + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<4, T, Q>::vec(vec<1, A, P> const& _x, vec<3, B, P> const& _yzw) + : x(static_cast(_x.x)) + , y(static_cast(_yzw.x)) + , z(static_cast(_yzw.y)) + , w(static_cast(_yzw.z)) + {} + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<4, T, Q>::vec(vec<2, A, P> const& _xy, vec<2, B, P> const& _zw) + : x(static_cast(_xy.x)) + , y(static_cast(_xy.y)) + , z(static_cast(_zw.x)) + , w(static_cast(_zw.y)) + {} + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<4, T, Q>::vec(vec<4, U, P> const& v) + : x(static_cast(v.x)) + , y(static_cast(v.y)) + , z(static_cast(v.z)) + , w(static_cast(v.w)) + {} + + // -- Component accesses -- + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR T& vec<4, T, Q>::operator[](typename vec<4, T, Q>::length_type i) + { + assert(i >= 0 && i < this->length()); + switch(i) + { + default: + case 0: + return x; + case 1: + return y; + case 2: + return z; + case 3: + return w; + } + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR T const& vec<4, T, Q>::operator[](typename vec<4, T, Q>::length_type i) const + { + assert(i >= 0 && i < this->length()); + switch(i) + { + default: + case 0: + return x; + case 1: + return y; + case 2: + return z; + case 3: + return w; + } + } + + // -- Unary arithmetic operators -- + +# if GLM_CONFIG_DEFAULTED_FUNCTIONS == GLM_DISABLE + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<4, T, Q>& vec<4, T, Q>::operator=(vec<4, T, Q> const& v) + { + this->x = v.x; + this->y = v.y; + this->z = v.z; + this->w = v.w; + return *this; + } +# endif + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<4, T, Q>& vec<4, T, Q>::operator=(vec<4, U, Q> const& v) + { + this->x = static_cast(v.x); + this->y = static_cast(v.y); + this->z = static_cast(v.z); + this->w = static_cast(v.w); + return *this; + } + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<4, T, Q> & vec<4, T, Q>::operator+=(U scalar) + { + return (*this = detail::compute_vec4_add::value>::call(*this, vec<4, T, Q>(scalar))); + } + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<4, T, Q> & vec<4, T, Q>::operator+=(vec<1, U, Q> const& v) + { + return (*this = detail::compute_vec4_add::value>::call(*this, vec<4, T, Q>(v.x))); + } + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<4, T, Q> & vec<4, T, Q>::operator+=(vec<4, U, Q> const& v) + { + return (*this = detail::compute_vec4_add::value>::call(*this, vec<4, T, Q>(v))); + } + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<4, T, Q> & vec<4, T, Q>::operator-=(U scalar) + { + return (*this = detail::compute_vec4_sub::value>::call(*this, vec<4, T, Q>(scalar))); + } + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<4, T, Q> & vec<4, T, Q>::operator-=(vec<1, U, Q> const& v) + { + return (*this = detail::compute_vec4_sub::value>::call(*this, vec<4, T, Q>(v.x))); + } + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<4, T, Q> & vec<4, T, Q>::operator-=(vec<4, U, Q> const& v) + { + return (*this = detail::compute_vec4_sub::value>::call(*this, vec<4, T, Q>(v))); + } + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<4, T, Q> & vec<4, T, Q>::operator*=(U scalar) + { + return (*this = detail::compute_vec4_mul::value>::call(*this, vec<4, T, Q>(scalar))); + } + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<4, T, Q> & vec<4, T, Q>::operator*=(vec<1, U, Q> const& v) + { + return (*this = detail::compute_vec4_mul::value>::call(*this, vec<4, T, Q>(v.x))); + } + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<4, T, Q> & vec<4, T, Q>::operator*=(vec<4, U, Q> const& v) + { + return (*this = detail::compute_vec4_mul::value>::call(*this, vec<4, T, Q>(v))); + } + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<4, T, Q> & vec<4, T, Q>::operator/=(U scalar) + { + return (*this = detail::compute_vec4_div::value>::call(*this, vec<4, T, Q>(scalar))); + } + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<4, T, Q> & vec<4, T, Q>::operator/=(vec<1, U, Q> const& v) + { + return (*this = detail::compute_vec4_div::value>::call(*this, vec<4, T, Q>(v.x))); + } + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<4, T, Q> & vec<4, T, Q>::operator/=(vec<4, U, Q> const& v) + { + return (*this = detail::compute_vec4_div::value>::call(*this, vec<4, T, Q>(v))); + } + + // -- Increment and decrement operators -- + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<4, T, Q> & vec<4, T, Q>::operator++() + { + ++this->x; + ++this->y; + ++this->z; + ++this->w; + return *this; + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<4, T, Q> & vec<4, T, Q>::operator--() + { + --this->x; + --this->y; + --this->z; + --this->w; + return *this; + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<4, T, Q> vec<4, T, Q>::operator++(int) + { + vec<4, T, Q> Result(*this); + ++*this; + return Result; + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<4, T, Q> vec<4, T, Q>::operator--(int) + { + vec<4, T, Q> Result(*this); + --*this; + return Result; + } + + // -- Unary bit operators -- + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<4, T, Q> & vec<4, T, Q>::operator%=(U scalar) + { + return (*this = detail::compute_vec4_mod::value>::call(*this, vec<4, T, Q>(scalar))); + } + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<4, T, Q> & vec<4, T, Q>::operator%=(vec<1, U, Q> const& v) + { + return (*this = detail::compute_vec4_mod::value>::call(*this, vec<4, T, Q>(v))); + } + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<4, T, Q> & vec<4, T, Q>::operator%=(vec<4, U, Q> const& v) + { + return (*this = detail::compute_vec4_mod::value>::call(*this, vec<4, T, Q>(v))); + } + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<4, T, Q> & vec<4, T, Q>::operator&=(U scalar) + { + return (*this = detail::compute_vec4_and::value, sizeof(T) * 8, detail::is_aligned::value>::call(*this, vec<4, T, Q>(scalar))); + } + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<4, T, Q> & vec<4, T, Q>::operator&=(vec<1, U, Q> const& v) + { + return (*this = detail::compute_vec4_and::value, sizeof(T) * 8, detail::is_aligned::value>::call(*this, vec<4, T, Q>(v))); + } + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<4, T, Q> & vec<4, T, Q>::operator&=(vec<4, U, Q> const& v) + { + return (*this = detail::compute_vec4_and::value, sizeof(T) * 8, detail::is_aligned::value>::call(*this, vec<4, T, Q>(v))); + } + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<4, T, Q> & vec<4, T, Q>::operator|=(U scalar) + { + return (*this = detail::compute_vec4_or::value, sizeof(T) * 8, detail::is_aligned::value>::call(*this, vec<4, T, Q>(scalar))); + } + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<4, T, Q> & vec<4, T, Q>::operator|=(vec<1, U, Q> const& v) + { + return (*this = detail::compute_vec4_or::value, sizeof(T) * 8, detail::is_aligned::value>::call(*this, vec<4, T, Q>(v))); + } + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<4, T, Q> & vec<4, T, Q>::operator|=(vec<4, U, Q> const& v) + { + return (*this = detail::compute_vec4_or::value, sizeof(T) * 8, detail::is_aligned::value>::call(*this, vec<4, T, Q>(v))); + } + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<4, T, Q> & vec<4, T, Q>::operator^=(U scalar) + { + return (*this = detail::compute_vec4_xor::value, sizeof(T) * 8, detail::is_aligned::value>::call(*this, vec<4, T, Q>(scalar))); + } + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<4, T, Q> & vec<4, T, Q>::operator^=(vec<1, U, Q> const& v) + { + return (*this = detail::compute_vec4_xor::value, sizeof(T) * 8, detail::is_aligned::value>::call(*this, vec<4, T, Q>(v))); + } + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<4, T, Q> & vec<4, T, Q>::operator^=(vec<4, U, Q> const& v) + { + return (*this = detail::compute_vec4_xor::value, sizeof(T) * 8, detail::is_aligned::value>::call(*this, vec<4, T, Q>(v))); + } + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<4, T, Q> & vec<4, T, Q>::operator<<=(U scalar) + { + return (*this = detail::compute_vec4_shift_left::value, sizeof(T) * 8, detail::is_aligned::value>::call(*this, vec<4, T, Q>(scalar))); + } + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<4, T, Q> & vec<4, T, Q>::operator<<=(vec<1, U, Q> const& v) + { + return (*this = detail::compute_vec4_shift_left::value, sizeof(T) * 8, detail::is_aligned::value>::call(*this, vec<4, T, Q>(v))); + } + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<4, T, Q> & vec<4, T, Q>::operator<<=(vec<4, U, Q> const& v) + { + return (*this = detail::compute_vec4_shift_left::value, sizeof(T) * 8, detail::is_aligned::value>::call(*this, vec<4, T, Q>(v))); + } + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<4, T, Q> & vec<4, T, Q>::operator>>=(U scalar) + { + return (*this = detail::compute_vec4_shift_right::value, sizeof(T) * 8, detail::is_aligned::value>::call(*this, vec<4, T, Q>(scalar))); + } + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<4, T, Q> & vec<4, T, Q>::operator>>=(vec<1, U, Q> const& v) + { + return (*this = detail::compute_vec4_shift_right::value, sizeof(T) * 8, detail::is_aligned::value>::call(*this, vec<4, T, Q>(v))); + } + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<4, T, Q> & vec<4, T, Q>::operator>>=(vec<4, U, Q> const& v) + { + return (*this = detail::compute_vec4_shift_right::value, sizeof(T) * 8, detail::is_aligned::value>::call(*this, vec<4, T, Q>(v))); + } + + // -- Unary constant operators -- + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<4, T, Q> operator+(vec<4, T, Q> const& v) + { + return v; + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<4, T, Q> operator-(vec<4, T, Q> const& v) + { + return vec<4, T, Q>(0) -= v; + } + + // -- Binary arithmetic operators -- + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<4, T, Q> operator+(vec<4, T, Q> const& v, T const & scalar) + { + return vec<4, T, Q>(v) += scalar; + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<4, T, Q> operator+(vec<4, T, Q> const& v1, vec<1, T, Q> const& v2) + { + return vec<4, T, Q>(v1) += v2; + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<4, T, Q> operator+(T scalar, vec<4, T, Q> const& v) + { + return vec<4, T, Q>(v) += scalar; + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<4, T, Q> operator+(vec<1, T, Q> const& v1, vec<4, T, Q> const& v2) + { + return vec<4, T, Q>(v2) += v1; + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<4, T, Q> operator+(vec<4, T, Q> const& v1, vec<4, T, Q> const& v2) + { + return vec<4, T, Q>(v1) += v2; + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<4, T, Q> operator-(vec<4, T, Q> const& v, T const & scalar) + { + return vec<4, T, Q>(v) -= scalar; + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<4, T, Q> operator-(vec<4, T, Q> const& v1, vec<1, T, Q> const& v2) + { + return vec<4, T, Q>(v1) -= v2; + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<4, T, Q> operator-(T scalar, vec<4, T, Q> const& v) + { + return vec<4, T, Q>(scalar) -= v; + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<4, T, Q> operator-(vec<1, T, Q> const& v1, vec<4, T, Q> const& v2) + { + return vec<4, T, Q>(v1.x) -= v2; + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<4, T, Q> operator-(vec<4, T, Q> const& v1, vec<4, T, Q> const& v2) + { + return vec<4, T, Q>(v1) -= v2; + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<4, T, Q> operator*(vec<4, T, Q> const& v, T const & scalar) + { + return vec<4, T, Q>(v) *= scalar; + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<4, T, Q> operator*(vec<4, T, Q> const& v1, vec<1, T, Q> const& v2) + { + return vec<4, T, Q>(v1) *= v2; + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<4, T, Q> operator*(T scalar, vec<4, T, Q> const& v) + { + return vec<4, T, Q>(v) *= scalar; + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<4, T, Q> operator*(vec<1, T, Q> const& v1, vec<4, T, Q> const& v2) + { + return vec<4, T, Q>(v2) *= v1; + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<4, T, Q> operator*(vec<4, T, Q> const& v1, vec<4, T, Q> const& v2) + { + return vec<4, T, Q>(v1) *= v2; + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<4, T, Q> operator/(vec<4, T, Q> const& v, T const & scalar) + { + return vec<4, T, Q>(v) /= scalar; + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<4, T, Q> operator/(vec<4, T, Q> const& v1, vec<1, T, Q> const& v2) + { + return vec<4, T, Q>(v1) /= v2; + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<4, T, Q> operator/(T scalar, vec<4, T, Q> const& v) + { + return vec<4, T, Q>(scalar) /= v; + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<4, T, Q> operator/(vec<1, T, Q> const& v1, vec<4, T, Q> const& v2) + { + return vec<4, T, Q>(v1.x) /= v2; + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<4, T, Q> operator/(vec<4, T, Q> const& v1, vec<4, T, Q> const& v2) + { + return vec<4, T, Q>(v1) /= v2; + } + + // -- Binary bit operators -- + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<4, T, Q> operator%(vec<4, T, Q> const& v, T scalar) + { + return vec<4, T, Q>(v) %= scalar; + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<4, T, Q> operator%(vec<4, T, Q> const& v1, vec<1, T, Q> const& v2) + { + return vec<4, T, Q>(v1) %= v2.x; + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<4, T, Q> operator%(T scalar, vec<4, T, Q> const& v) + { + return vec<4, T, Q>(scalar) %= v; + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<4, T, Q> operator%(vec<1, T, Q> const& scalar, vec<4, T, Q> const& v) + { + return vec<4, T, Q>(scalar.x) %= v; + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<4, T, Q> operator%(vec<4, T, Q> const& v1, vec<4, T, Q> const& v2) + { + return vec<4, T, Q>(v1) %= v2; + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<4, T, Q> operator&(vec<4, T, Q> const& v, T scalar) + { + return vec<4, T, Q>(v) &= scalar; + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<4, T, Q> operator&(vec<4, T, Q> const& v, vec<1, T, Q> const& scalar) + { + return vec<4, T, Q>(v) &= scalar; + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<4, T, Q> operator&(T scalar, vec<4, T, Q> const& v) + { + return vec<4, T, Q>(scalar) &= v; + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<4, T, Q> operator&(vec<1, T, Q> const& v1, vec<4, T, Q> const& v2) + { + return vec<4, T, Q>(v1.x) &= v2; + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<4, T, Q> operator&(vec<4, T, Q> const& v1, vec<4, T, Q> const& v2) + { + return vec<4, T, Q>(v1) &= v2; + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<4, T, Q> operator|(vec<4, T, Q> const& v, T scalar) + { + return vec<4, T, Q>(v) |= scalar; + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<4, T, Q> operator|(vec<4, T, Q> const& v1, vec<1, T, Q> const& v2) + { + return vec<4, T, Q>(v1) |= v2.x; + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<4, T, Q> operator|(T scalar, vec<4, T, Q> const& v) + { + return vec<4, T, Q>(scalar) |= v; + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<4, T, Q> operator|(vec<1, T, Q> const& v1, vec<4, T, Q> const& v2) + { + return vec<4, T, Q>(v1.x) |= v2; + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<4, T, Q> operator|(vec<4, T, Q> const& v1, vec<4, T, Q> const& v2) + { + return vec<4, T, Q>(v1) |= v2; + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<4, T, Q> operator^(vec<4, T, Q> const& v, T scalar) + { + return vec<4, T, Q>(v) ^= scalar; + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<4, T, Q> operator^(vec<4, T, Q> const& v1, vec<1, T, Q> const& v2) + { + return vec<4, T, Q>(v1) ^= v2.x; + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<4, T, Q> operator^(T scalar, vec<4, T, Q> const& v) + { + return vec<4, T, Q>(scalar) ^= v; + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<4, T, Q> operator^(vec<1, T, Q> const& v1, vec<4, T, Q> const& v2) + { + return vec<4, T, Q>(v1.x) ^= v2; + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<4, T, Q> operator^(vec<4, T, Q> const& v1, vec<4, T, Q> const& v2) + { + return vec<4, T, Q>(v1) ^= v2; + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<4, T, Q> operator<<(vec<4, T, Q> const& v, T scalar) + { + return vec<4, T, Q>(v) <<= scalar; + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<4, T, Q> operator<<(vec<4, T, Q> const& v1, vec<1, T, Q> const& v2) + { + return vec<4, T, Q>(v1) <<= v2.x; + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<4, T, Q> operator<<(T scalar, vec<4, T, Q> const& v) + { + return vec<4, T, Q>(scalar) <<= v; + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<4, T, Q> operator<<(vec<1, T, Q> const& v1, vec<4, T, Q> const& v2) + { + return vec<4, T, Q>(v1.x) <<= v2; + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<4, T, Q> operator<<(vec<4, T, Q> const& v1, vec<4, T, Q> const& v2) + { + return vec<4, T, Q>(v1) <<= v2; + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<4, T, Q> operator>>(vec<4, T, Q> const& v, T scalar) + { + return vec<4, T, Q>(v) >>= scalar; + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<4, T, Q> operator>>(vec<4, T, Q> const& v1, vec<1, T, Q> const& v2) + { + return vec<4, T, Q>(v1) >>= v2.x; + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<4, T, Q> operator>>(T scalar, vec<4, T, Q> const& v) + { + return vec<4, T, Q>(scalar) >>= v; + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<4, T, Q> operator>>(vec<1, T, Q> const& v1, vec<4, T, Q> const& v2) + { + return vec<4, T, Q>(v1.x) >>= v2; + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<4, T, Q> operator>>(vec<4, T, Q> const& v1, vec<4, T, Q> const& v2) + { + return vec<4, T, Q>(v1) >>= v2; + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<4, T, Q> operator~(vec<4, T, Q> const& v) + { + return detail::compute_vec4_bitwise_not::value, sizeof(T) * 8, detail::is_aligned::value>::call(v); + } + + // -- Boolean operators -- + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR bool operator==(vec<4, T, Q> const& v1, vec<4, T, Q> const& v2) + { + return detail::compute_vec4_equal::value, sizeof(T) * 8, detail::is_aligned::value>::call(v1, v2); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR bool operator!=(vec<4, T, Q> const& v1, vec<4, T, Q> const& v2) + { + return detail::compute_vec4_nequal::value, sizeof(T) * 8, detail::is_aligned::value>::call(v1, v2); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<4, bool, Q> operator&&(vec<4, bool, Q> const& v1, vec<4, bool, Q> const& v2) + { + return vec<4, bool, Q>(v1.x && v2.x, v1.y && v2.y, v1.z && v2.z, v1.w && v2.w); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<4, bool, Q> operator||(vec<4, bool, Q> const& v1, vec<4, bool, Q> const& v2) + { + return vec<4, bool, Q>(v1.x || v2.x, v1.y || v2.y, v1.z || v2.z, v1.w || v2.w); + } +}//namespace glm + +#if GLM_CONFIG_SIMD == GLM_ENABLE +# include "type_vec4_simd.inl" +#endif diff --git a/src/GLMath/glm/detail/type_vec4_simd.inl b/src/GLMath/glm/detail/type_vec4_simd.inl new file mode 100644 index 000000000..415b73cb7 --- /dev/null +++ b/src/GLMath/glm/detail/type_vec4_simd.inl @@ -0,0 +1,463 @@ +#if GLM_ARCH & GLM_ARCH_SSE2_BIT + +namespace glm{ +namespace detail +{ +# if GLM_CONFIG_SWIZZLE == GLM_SWIZZLE_OPERATOR + template + struct _swizzle_base1<4, float, Q, E0,E1,E2,E3, true> : public _swizzle_base0 + { + GLM_FUNC_QUALIFIER vec<4, float, Q> operator ()() const + { + __m128 data = *reinterpret_cast<__m128 const*>(&this->_buffer); + + vec<4, float, Q> Result; +# if GLM_ARCH & GLM_ARCH_AVX_BIT + Result.data = _mm_permute_ps(data, _MM_SHUFFLE(E3, E2, E1, E0)); +# else + Result.data = _mm_shuffle_ps(data, data, _MM_SHUFFLE(E3, E2, E1, E0)); +# endif + return Result; + } + }; + + template + struct _swizzle_base1<4, int, Q, E0,E1,E2,E3, true> : public _swizzle_base0 + { + GLM_FUNC_QUALIFIER vec<4, int, Q> operator ()() const + { + __m128i data = *reinterpret_cast<__m128i const*>(&this->_buffer); + + vec<4, int, Q> Result; + Result.data = _mm_shuffle_epi32(data, _MM_SHUFFLE(E3, E2, E1, E0)); + return Result; + } + }; + + template + struct _swizzle_base1<4, uint, Q, E0,E1,E2,E3, true> : public _swizzle_base0 + { + GLM_FUNC_QUALIFIER vec<4, uint, Q> operator ()() const + { + __m128i data = *reinterpret_cast<__m128i const*>(&this->_buffer); + + vec<4, uint, Q> Result; + Result.data = _mm_shuffle_epi32(data, _MM_SHUFFLE(E3, E2, E1, E0)); + return Result; + } + }; +# endif// GLM_CONFIG_SWIZZLE == GLM_SWIZZLE_OPERATOR + + template + struct compute_vec4_add + { + static vec<4, float, Q> call(vec<4, float, Q> const& a, vec<4, float, Q> const& b) + { + vec<4, float, Q> Result; + Result.data = _mm_add_ps(a.data, b.data); + return Result; + } + }; + +# if GLM_ARCH & GLM_ARCH_AVX_BIT + template + struct compute_vec4_add + { + static vec<4, double, Q> call(vec<4, double, Q> const& a, vec<4, double, Q> const& b) + { + vec<4, double, Q> Result; + Result.data = _mm256_add_pd(a.data, b.data); + return Result; + } + }; +# endif + + template + struct compute_vec4_sub + { + static vec<4, float, Q> call(vec<4, float, Q> const& a, vec<4, float, Q> const& b) + { + vec<4, float, Q> Result; + Result.data = _mm_sub_ps(a.data, b.data); + return Result; + } + }; + +# if GLM_ARCH & GLM_ARCH_AVX_BIT + template + struct compute_vec4_sub + { + static vec<4, double, Q> call(vec<4, double, Q> const& a, vec<4, double, Q> const& b) + { + vec<4, double, Q> Result; + Result.data = _mm256_sub_pd(a.data, b.data); + return Result; + } + }; +# endif + + template + struct compute_vec4_mul + { + static vec<4, float, Q> call(vec<4, float, Q> const& a, vec<4, float, Q> const& b) + { + vec<4, float, Q> Result; + Result.data = _mm_mul_ps(a.data, b.data); + return Result; + } + }; + +# if GLM_ARCH & GLM_ARCH_AVX_BIT + template + struct compute_vec4_mul + { + static vec<4, double, Q> call(vec<4, double, Q> const& a, vec<4, double, Q> const& b) + { + vec<4, double, Q> Result; + Result.data = _mm256_mul_pd(a.data, b.data); + return Result; + } + }; +# endif + + template + struct compute_vec4_div + { + static vec<4, float, Q> call(vec<4, float, Q> const& a, vec<4, float, Q> const& b) + { + vec<4, float, Q> Result; + Result.data = _mm_div_ps(a.data, b.data); + return Result; + } + }; + + # if GLM_ARCH & GLM_ARCH_AVX_BIT + template + struct compute_vec4_div + { + static vec<4, double, Q> call(vec<4, double, Q> const& a, vec<4, double, Q> const& b) + { + vec<4, double, Q> Result; + Result.data = _mm256_div_pd(a.data, b.data); + return Result; + } + }; +# endif + + template<> + struct compute_vec4_div + { + static vec<4, float, aligned_lowp> call(vec<4, float, aligned_lowp> const& a, vec<4, float, aligned_lowp> const& b) + { + vec<4, float, aligned_lowp> Result; + Result.data = _mm_mul_ps(a.data, _mm_rcp_ps(b.data)); + return Result; + } + }; + + template + struct compute_vec4_and + { + static vec<4, T, Q> call(vec<4, T, Q> const& a, vec<4, T, Q> const& b) + { + vec<4, T, Q> Result; + Result.data = _mm_and_si128(a.data, b.data); + return Result; + } + }; + +# if GLM_ARCH & GLM_ARCH_AVX2_BIT + template + struct compute_vec4_and + { + static vec<4, T, Q> call(vec<4, T, Q> const& a, vec<4, T, Q> const& b) + { + vec<4, T, Q> Result; + Result.data = _mm256_and_si256(a.data, b.data); + return Result; + } + }; +# endif + + template + struct compute_vec4_or + { + static vec<4, T, Q> call(vec<4, T, Q> const& a, vec<4, T, Q> const& b) + { + vec<4, T, Q> Result; + Result.data = _mm_or_si128(a.data, b.data); + return Result; + } + }; + +# if GLM_ARCH & GLM_ARCH_AVX2_BIT + template + struct compute_vec4_or + { + static vec<4, T, Q> call(vec<4, T, Q> const& a, vec<4, T, Q> const& b) + { + vec<4, T, Q> Result; + Result.data = _mm256_or_si256(a.data, b.data); + return Result; + } + }; +# endif + + template + struct compute_vec4_xor + { + static vec<4, T, Q> call(vec<4, T, Q> const& a, vec<4, T, Q> const& b) + { + vec<4, T, Q> Result; + Result.data = _mm_xor_si128(a.data, b.data); + return Result; + } + }; + +# if GLM_ARCH & GLM_ARCH_AVX2_BIT + template + struct compute_vec4_xor + { + static vec<4, T, Q> call(vec<4, T, Q> const& a, vec<4, T, Q> const& b) + { + vec<4, T, Q> Result; + Result.data = _mm256_xor_si256(a.data, b.data); + return Result; + } + }; +# endif + + template + struct compute_vec4_shift_left + { + static vec<4, T, Q> call(vec<4, T, Q> const& a, vec<4, T, Q> const& b) + { + vec<4, T, Q> Result; + Result.data = _mm_sll_epi32(a.data, b.data); + return Result; + } + }; + +# if GLM_ARCH & GLM_ARCH_AVX2_BIT + template + struct compute_vec4_shift_left + { + static vec<4, T, Q> call(vec<4, T, Q> const& a, vec<4, T, Q> const& b) + { + vec<4, T, Q> Result; + Result.data = _mm256_sll_epi64(a.data, b.data); + return Result; + } + }; +# endif + + template + struct compute_vec4_shift_right + { + static vec<4, T, Q> call(vec<4, T, Q> const& a, vec<4, T, Q> const& b) + { + vec<4, T, Q> Result; + Result.data = _mm_srl_epi32(a.data, b.data); + return Result; + } + }; + +# if GLM_ARCH & GLM_ARCH_AVX2_BIT + template + struct compute_vec4_shift_right + { + static vec<4, T, Q> call(vec<4, T, Q> const& a, vec<4, T, Q> const& b) + { + vec<4, T, Q> Result; + Result.data = _mm256_srl_epi64(a.data, b.data); + return Result; + } + }; +# endif + + template + struct compute_vec4_bitwise_not + { + static vec<4, T, Q> call(vec<4, T, Q> const& v) + { + vec<4, T, Q> Result; + Result.data = _mm_xor_si128(v.data, _mm_set1_epi32(-1)); + return Result; + } + }; + +# if GLM_ARCH & GLM_ARCH_AVX2_BIT + template + struct compute_vec4_bitwise_not + { + static vec<4, T, Q> call(vec<4, T, Q> const& v) + { + vec<4, T, Q> Result; + Result.data = _mm256_xor_si256(v.data, _mm_set1_epi32(-1)); + return Result; + } + }; +# endif + + template + struct compute_vec4_equal + { + static bool call(vec<4, float, Q> const& v1, vec<4, float, Q> const& v2) + { + return _mm_movemask_ps(_mm_cmpeq_ps(v1.data, v2.data)) != 0; + } + }; + +# if GLM_ARCH & GLM_ARCH_SSE41_BIT + template + struct compute_vec4_equal + { + static bool call(vec<4, int, Q> const& v1, vec<4, int, Q> const& v2) + { + //return _mm_movemask_epi8(_mm_cmpeq_epi32(v1.data, v2.data)) != 0; + __m128i neq = _mm_xor_si128(v1.data, v2.data); + return _mm_test_all_zeros(neq, neq) == 0; + } + }; +# endif + + template + struct compute_vec4_nequal + { + static bool call(vec<4, float, Q> const& v1, vec<4, float, Q> const& v2) + { + return _mm_movemask_ps(_mm_cmpneq_ps(v1.data, v2.data)) != 0; + } + }; + +# if GLM_ARCH & GLM_ARCH_SSE41_BIT + template + struct compute_vec4_nequal + { + static bool call(vec<4, int, Q> const& v1, vec<4, int, Q> const& v2) + { + //return _mm_movemask_epi8(_mm_cmpneq_epi32(v1.data, v2.data)) != 0; + __m128i neq = _mm_xor_si128(v1.data, v2.data); + return _mm_test_all_zeros(neq, neq) != 0; + } + }; +# endif +}//namespace detail + + template<> + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<4, float, aligned_lowp>::vec(float _s) : + data(_mm_set1_ps(_s)) + {} + + template<> + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<4, float, aligned_mediump>::vec(float _s) : + data(_mm_set1_ps(_s)) + {} + + template<> + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<4, float, aligned_highp>::vec(float _s) : + data(_mm_set1_ps(_s)) + {} + +# if GLM_ARCH & GLM_ARCH_AVX_BIT + template<> + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<4, double, aligned_lowp>::vec(double _s) : + data(_mm256_set1_pd(_s)) + {} + + template<> + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<4, double, aligned_mediump>::vec(double _s) : + data(_mm256_set1_pd(_s)) + {} + + template<> + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<4, double, aligned_highp>::vec(double _s) : + data(_mm256_set1_pd(_s)) + {} +# endif + + template<> + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<4, int, aligned_lowp>::vec(int _s) : + data(_mm_set1_epi32(_s)) + {} + + template<> + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<4, int, aligned_mediump>::vec(int _s) : + data(_mm_set1_epi32(_s)) + {} + + template<> + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<4, int, aligned_highp>::vec(int _s) : + data(_mm_set1_epi32(_s)) + {} + +# if GLM_ARCH & GLM_ARCH_AVX2_BIT + template<> + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<4, detail::int64, aligned_lowp>::vec(detail::int64 _s) : + data(_mm256_set1_epi64x(_s)) + {} + + template<> + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<4, detail::int64, aligned_mediump>::vec(detail::int64 _s) : + data(_mm256_set1_epi64x(_s)) + {} + + template<> + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<4, detail::int64, aligned_highp>::vec(detail::int64 _s) : + data(_mm256_set1_epi64x(_s)) + {} +# endif + + template<> + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<4, float, aligned_lowp>::vec(float _x, float _y, float _z, float _w) : + data(_mm_set_ps(_w, _z, _y, _x)) + {} + + template<> + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<4, float, aligned_mediump>::vec(float _x, float _y, float _z, float _w) : + data(_mm_set_ps(_w, _z, _y, _x)) + {} + + template<> + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<4, float, aligned_highp>::vec(float _x, float _y, float _z, float _w) : + data(_mm_set_ps(_w, _z, _y, _x)) + {} + + template<> + template<> + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<4, int, aligned_lowp>::vec(int _x, int _y, int _z, int _w) : + data(_mm_set_epi32(_w, _z, _y, _x)) + {} + + template<> + template<> + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<4, int, aligned_mediump>::vec(int _x, int _y, int _z, int _w) : + data(_mm_set_epi32(_w, _z, _y, _x)) + {} + + template<> + template<> + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<4, int, aligned_highp>::vec(int _x, int _y, int _z, int _w) : + data(_mm_set_epi32(_w, _z, _y, _x)) + {} + + template<> + template<> + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<4, float, aligned_lowp>::vec(int _x, int _y, int _z, int _w) : + data(_mm_cvtepi32_ps(_mm_set_epi32(_w, _z, _y, _x))) + {} + + template<> + template<> + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<4, float, aligned_mediump>::vec(int _x, int _y, int _z, int _w) : + data(_mm_cvtepi32_ps(_mm_set_epi32(_w, _z, _y, _x))) + {} + + template<> + template<> + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<4, float, aligned_highp>::vec(int _x, int _y, int _z, int _w) : + data(_mm_cvtepi32_ps(_mm_set_epi32(_w, _z, _y, _x))) + {} +}//namespace glm + +#endif//GLM_ARCH & GLM_ARCH_SSE2_BIT diff --git a/src/GLMath/glm/exponential.hpp b/src/GLMath/glm/exponential.hpp new file mode 100644 index 000000000..f8fb886f6 --- /dev/null +++ b/src/GLMath/glm/exponential.hpp @@ -0,0 +1,110 @@ +/// @ref core +/// @file glm/exponential.hpp +/// +/// @see GLSL 4.20.8 specification, section 8.2 Exponential Functions +/// +/// @defgroup core_func_exponential Exponential functions +/// @ingroup core +/// +/// Provides GLSL exponential functions +/// +/// These all operate component-wise. The description is per component. +/// +/// Include to use these core features. + +#pragma once + +#include "detail/type_vec1.hpp" +#include "detail/type_vec2.hpp" +#include "detail/type_vec3.hpp" +#include "detail/type_vec4.hpp" +#include + +namespace glm +{ + /// @addtogroup core_func_exponential + /// @{ + + /// Returns 'base' raised to the power 'exponent'. + /// + /// @param base Floating point value. pow function is defined for input values of 'base' defined in the range (inf-, inf+) in the limit of the type qualifier. + /// @param exponent Floating point value representing the 'exponent'. + /// + /// @see GLSL pow man page + /// @see GLSL 4.20.8 specification, section 8.2 Exponential Functions + template + GLM_FUNC_DECL vec pow(vec const& base, vec const& exponent); + + /// Returns the natural exponentiation of x, i.e., e^x. + /// + /// @param v exp function is defined for input values of v defined in the range (inf-, inf+) in the limit of the type qualifier. + /// @tparam L An integer between 1 and 4 included that qualify the dimension of the vector. + /// @tparam T Floating-point scalar types. + /// + /// @see GLSL exp man page + /// @see GLSL 4.20.8 specification, section 8.2 Exponential Functions + template + GLM_FUNC_DECL vec exp(vec const& v); + + /// Returns the natural logarithm of v, i.e., + /// returns the value y which satisfies the equation x = e^y. + /// Results are undefined if v <= 0. + /// + /// @param v log function is defined for input values of v defined in the range (0, inf+) in the limit of the type qualifier. + /// @tparam L An integer between 1 and 4 included that qualify the dimension of the vector. + /// @tparam T Floating-point scalar types. + /// + /// @see GLSL log man page + /// @see GLSL 4.20.8 specification, section 8.2 Exponential Functions + template + GLM_FUNC_DECL vec log(vec const& v); + + /// Returns 2 raised to the v power. + /// + /// @param v exp2 function is defined for input values of v defined in the range (inf-, inf+) in the limit of the type qualifier. + /// @tparam L An integer between 1 and 4 included that qualify the dimension of the vector. + /// @tparam T Floating-point scalar types. + /// + /// @see GLSL exp2 man page + /// @see GLSL 4.20.8 specification, section 8.2 Exponential Functions + template + GLM_FUNC_DECL vec exp2(vec const& v); + + /// Returns the base 2 log of x, i.e., returns the value y, + /// which satisfies the equation x = 2 ^ y. + /// + /// @param v log2 function is defined for input values of v defined in the range (0, inf+) in the limit of the type qualifier. + /// @tparam L An integer between 1 and 4 included that qualify the dimension of the vector. + /// @tparam T Floating-point scalar types. + /// + /// @see GLSL log2 man page + /// @see GLSL 4.20.8 specification, section 8.2 Exponential Functions + template + GLM_FUNC_DECL vec log2(vec const& v); + + /// Returns the positive square root of v. + /// + /// @param v sqrt function is defined for input values of v defined in the range [0, inf+) in the limit of the type qualifier. + /// @tparam L An integer between 1 and 4 included that qualify the dimension of the vector. + /// @tparam T Floating-point scalar types. + /// + /// @see GLSL sqrt man page + /// @see GLSL 4.20.8 specification, section 8.2 Exponential Functions + template + GLM_FUNC_DECL vec sqrt(vec const& v); + + /// Returns the reciprocal of the positive square root of v. + /// + /// @param v inversesqrt function is defined for input values of v defined in the range [0, inf+) in the limit of the type qualifier. + /// @tparam L An integer between 1 and 4 included that qualify the dimension of the vector. + /// @tparam T Floating-point scalar types. + /// + /// @see GLSL inversesqrt man page + /// @see GLSL 4.20.8 specification, section 8.2 Exponential Functions + template + GLM_FUNC_DECL vec inversesqrt(vec const& v); + + /// @} +}//namespace glm + +#include "detail/func_exponential.inl" diff --git a/src/GLMath/glm/ext.hpp b/src/GLMath/glm/ext.hpp new file mode 100644 index 000000000..3bc8db27d --- /dev/null +++ b/src/GLMath/glm/ext.hpp @@ -0,0 +1,196 @@ +/// @file glm/ext.hpp +/// +/// @ref core (Dependence) + +#include "detail/setup.hpp" + +#pragma once + +#include "glm.hpp" + +#if GLM_MESSAGES == GLM_ENABLE && !defined(GLM_MESSAGE_EXT_INCLUDED_DISPLAYED) +# define GLM_MESSAGE_EXT_INCLUDED_DISPLAYED +# pragma message("GLM: All extensions included (not recommended)") +#endif//GLM_MESSAGES + +#include "./ext/matrix_double2x2.hpp" +#include "./ext/matrix_double2x2_precision.hpp" +#include "./ext/matrix_double2x3.hpp" +#include "./ext/matrix_double2x3_precision.hpp" +#include "./ext/matrix_double2x4.hpp" +#include "./ext/matrix_double2x4_precision.hpp" +#include "./ext/matrix_double3x2.hpp" +#include "./ext/matrix_double3x2_precision.hpp" +#include "./ext/matrix_double3x3.hpp" +#include "./ext/matrix_double3x3_precision.hpp" +#include "./ext/matrix_double3x4.hpp" +#include "./ext/matrix_double3x4_precision.hpp" +#include "./ext/matrix_double4x2.hpp" +#include "./ext/matrix_double4x2_precision.hpp" +#include "./ext/matrix_double4x3.hpp" +#include "./ext/matrix_double4x3_precision.hpp" +#include "./ext/matrix_double4x4.hpp" +#include "./ext/matrix_double4x4_precision.hpp" + +#include "./ext/matrix_float2x2.hpp" +#include "./ext/matrix_float2x2_precision.hpp" +#include "./ext/matrix_float2x3.hpp" +#include "./ext/matrix_float2x3_precision.hpp" +#include "./ext/matrix_float2x4.hpp" +#include "./ext/matrix_float2x4_precision.hpp" +#include "./ext/matrix_float3x2.hpp" +#include "./ext/matrix_float3x2_precision.hpp" +#include "./ext/matrix_float3x3.hpp" +#include "./ext/matrix_float3x3_precision.hpp" +#include "./ext/matrix_float3x4.hpp" +#include "./ext/matrix_float3x4_precision.hpp" +#include "./ext/matrix_float4x2.hpp" +#include "./ext/matrix_float4x2_precision.hpp" +#include "./ext/matrix_float4x3.hpp" +#include "./ext/matrix_float4x3_precision.hpp" +#include "./ext/matrix_float4x4.hpp" +#include "./ext/matrix_float4x4_precision.hpp" + +#include "./ext/matrix_relational.hpp" + +#include "./ext/quaternion_double.hpp" +#include "./ext/quaternion_double_precision.hpp" +#include "./ext/quaternion_float.hpp" +#include "./ext/quaternion_float_precision.hpp" +#include "./ext/quaternion_geometric.hpp" +#include "./ext/quaternion_relational.hpp" + +#include "./ext/scalar_constants.hpp" +#include "./ext/scalar_int_sized.hpp" +#include "./ext/scalar_relational.hpp" + +#include "./ext/vector_bool1.hpp" +#include "./ext/vector_bool1_precision.hpp" +#include "./ext/vector_bool2.hpp" +#include "./ext/vector_bool2_precision.hpp" +#include "./ext/vector_bool3.hpp" +#include "./ext/vector_bool3_precision.hpp" +#include "./ext/vector_bool4.hpp" +#include "./ext/vector_bool4_precision.hpp" + +#include "./ext/vector_double1.hpp" +#include "./ext/vector_double1_precision.hpp" +#include "./ext/vector_double2.hpp" +#include "./ext/vector_double2_precision.hpp" +#include "./ext/vector_double3.hpp" +#include "./ext/vector_double3_precision.hpp" +#include "./ext/vector_double4.hpp" +#include "./ext/vector_double4_precision.hpp" + +#include "./ext/vector_float1.hpp" +#include "./ext/vector_float1_precision.hpp" +#include "./ext/vector_float2.hpp" +#include "./ext/vector_float2_precision.hpp" +#include "./ext/vector_float3.hpp" +#include "./ext/vector_float3_precision.hpp" +#include "./ext/vector_float4.hpp" +#include "./ext/vector_float4_precision.hpp" + +#include "./ext/vector_int1.hpp" +#include "./ext/vector_int1_precision.hpp" +#include "./ext/vector_int2.hpp" +#include "./ext/vector_int2_precision.hpp" +#include "./ext/vector_int3.hpp" +#include "./ext/vector_int3_precision.hpp" +#include "./ext/vector_int4.hpp" +#include "./ext/vector_int4_precision.hpp" + +#include "./ext/vector_relational.hpp" + +#include "./ext/vector_uint1.hpp" +#include "./ext/vector_uint1_precision.hpp" +#include "./ext/vector_uint2.hpp" +#include "./ext/vector_uint2_precision.hpp" +#include "./ext/vector_uint3.hpp" +#include "./ext/vector_uint3_precision.hpp" +#include "./ext/vector_uint4.hpp" +#include "./ext/vector_uint4_precision.hpp" + +#include "./gtc/bitfield.hpp" +#include "./gtc/color_space.hpp" +#include "./gtc/constants.hpp" +#include "./gtc/epsilon.hpp" +#include "./gtc/integer.hpp" +#include "./gtc/matrix_access.hpp" +#include "./gtc/matrix_integer.hpp" +#include "./gtc/matrix_inverse.hpp" +#include "./gtc/matrix_transform.hpp" +#include "./gtc/noise.hpp" +#include "./gtc/packing.hpp" +#include "./gtc/quaternion.hpp" +#include "./gtc/random.hpp" +#include "./gtc/reciprocal.hpp" +#include "./gtc/round.hpp" +#include "./gtc/type_precision.hpp" +#include "./gtc/type_ptr.hpp" +#include "./gtc/ulp.hpp" +#include "./gtc/vec1.hpp" +#if GLM_CONFIG_ALIGNED_GENTYPES == GLM_ENABLE +# include "./gtc/type_aligned.hpp" +#endif + +#ifdef GLM_ENABLE_EXPERIMENTAL +#include "./gtx/associated_min_max.hpp" +#include "./gtx/bit.hpp" +#include "./gtx/closest_point.hpp" +#include "./gtx/color_encoding.hpp" +#include "./gtx/color_space.hpp" +#include "./gtx/color_space_YCoCg.hpp" +#include "./gtx/compatibility.hpp" +#include "./gtx/component_wise.hpp" +#include "./gtx/dual_quaternion.hpp" +#include "./gtx/euler_angles.hpp" +#include "./gtx/extend.hpp" +#include "./gtx/extended_min_max.hpp" +#include "./gtx/fast_exponential.hpp" +#include "./gtx/fast_square_root.hpp" +#include "./gtx/fast_trigonometry.hpp" +#include "./gtx/functions.hpp" +#include "./gtx/gradient_paint.hpp" +#include "./gtx/handed_coordinate_space.hpp" +#include "./gtx/integer.hpp" +#include "./gtx/intersect.hpp" +#include "./gtx/log_base.hpp" +#include "./gtx/matrix_cross_product.hpp" +#include "./gtx/matrix_interpolation.hpp" +#include "./gtx/matrix_major_storage.hpp" +#include "./gtx/matrix_operation.hpp" +#include "./gtx/matrix_query.hpp" +#include "./gtx/mixed_product.hpp" +#include "./gtx/norm.hpp" +#include "./gtx/normal.hpp" +#include "./gtx/normalize_dot.hpp" +#include "./gtx/number_precision.hpp" +#include "./gtx/optimum_pow.hpp" +#include "./gtx/orthonormalize.hpp" +#include "./gtx/perpendicular.hpp" +#include "./gtx/polar_coordinates.hpp" +#include "./gtx/projection.hpp" +#include "./gtx/quaternion.hpp" +#include "./gtx/raw_data.hpp" +#include "./gtx/rotate_vector.hpp" +#include "./gtx/spline.hpp" +#include "./gtx/std_based_type.hpp" +#if !(GLM_COMPILER & GLM_COMPILER_CUDA) +# include "./gtx/string_cast.hpp" +#endif +#include "./gtx/transform.hpp" +#include "./gtx/transform2.hpp" +#include "./gtx/vec_swizzle.hpp" +#include "./gtx/vector_angle.hpp" +#include "./gtx/vector_query.hpp" +#include "./gtx/wrap.hpp" + +#if GLM_HAS_TEMPLATE_ALIASES +# include "./gtx/scalar_multiplication.hpp" +#endif + +#if GLM_HAS_RANGE_FOR +# include "./gtx/range.hpp" +#endif +#endif//GLM_ENABLE_EXPERIMENTAL diff --git a/src/GLMath/glm/ext/matrix_clip_space.hpp b/src/GLMath/glm/ext/matrix_clip_space.hpp new file mode 100644 index 000000000..c3874f2f8 --- /dev/null +++ b/src/GLMath/glm/ext/matrix_clip_space.hpp @@ -0,0 +1,522 @@ +/// @ref ext_matrix_clip_space +/// @file glm/ext/matrix_clip_space.hpp +/// +/// @defgroup ext_matrix_clip_space GLM_EXT_matrix_clip_space +/// @ingroup ext +/// +/// Defines functions that generate clip space transformation matrices. +/// +/// The matrices generated by this extension use standard OpenGL fixed-function +/// conventions. For example, the lookAt function generates a transform from world +/// space into the specific eye space that the projective matrix functions +/// (perspective, ortho, etc) are designed to expect. The OpenGL compatibility +/// specifications defines the particular layout of this eye space. +/// +/// Include to use the features of this extension. +/// +/// @see ext_matrix_transform +/// @see ext_matrix_projection + +#pragma once + +// Dependencies +#include "../ext/scalar_constants.hpp" +#include "../geometric.hpp" +#include "../trigonometric.hpp" + +#if GLM_MESSAGES == GLM_ENABLE && !defined(GLM_EXT_INCLUDED) +# pragma message("GLM: GLM_EXT_matrix_clip_space extension included") +#endif + +namespace glm +{ + /// @addtogroup ext_matrix_clip_space + /// @{ + + /// Creates a matrix for projecting two-dimensional coordinates onto the screen. + /// + /// @tparam T A floating-point scalar type + /// + /// @see - glm::ortho(T const& left, T const& right, T const& bottom, T const& top, T const& zNear, T const& zFar) + /// @see gluOrtho2D man page + template + GLM_FUNC_DECL mat<4, 4, T, defaultp> ortho( + T left, T right, T bottom, T top); + + /// Creates a matrix for an orthographic parallel viewing volume, using left-handed coordinates. + /// The near and far clip planes correspond to z normalized device coordinates of 0 and +1 respectively. (Direct3D clip volume definition) + /// + /// @tparam T A floating-point scalar type + /// + /// @see - glm::ortho(T const& left, T const& right, T const& bottom, T const& top) + template + GLM_FUNC_DECL mat<4, 4, T, defaultp> orthoLH_ZO( + T left, T right, T bottom, T top, T zNear, T zFar); + + /// Creates a matrix for an orthographic parallel viewing volume using right-handed coordinates. + /// The near and far clip planes correspond to z normalized device coordinates of -1 and +1 respectively. (OpenGL clip volume definition) + /// + /// @tparam T A floating-point scalar type + /// + /// @see - glm::ortho(T const& left, T const& right, T const& bottom, T const& top) + template + GLM_FUNC_DECL mat<4, 4, T, defaultp> orthoLH_NO( + T left, T right, T bottom, T top, T zNear, T zFar); + + /// Creates a matrix for an orthographic parallel viewing volume, using left-handed coordinates. + /// The near and far clip planes correspond to z normalized device coordinates of 0 and +1 respectively. (Direct3D clip volume definition) + /// + /// @tparam T A floating-point scalar type + /// + /// @see - glm::ortho(T const& left, T const& right, T const& bottom, T const& top) + template + GLM_FUNC_DECL mat<4, 4, T, defaultp> orthoRH_ZO( + T left, T right, T bottom, T top, T zNear, T zFar); + + /// Creates a matrix for an orthographic parallel viewing volume, using right-handed coordinates. + /// The near and far clip planes correspond to z normalized device coordinates of -1 and +1 respectively. (OpenGL clip volume definition) + /// + /// @tparam T A floating-point scalar type + /// + /// @see - glm::ortho(T const& left, T const& right, T const& bottom, T const& top) + template + GLM_FUNC_DECL mat<4, 4, T, defaultp> orthoRH_NO( + T left, T right, T bottom, T top, T zNear, T zFar); + + /// Creates a matrix for an orthographic parallel viewing volume, using left-handed coordinates. + /// The near and far clip planes correspond to z normalized device coordinates of 0 and +1 respectively. (Direct3D clip volume definition) + /// + /// @tparam T A floating-point scalar type + /// + /// @see - glm::ortho(T const& left, T const& right, T const& bottom, T const& top) + template + GLM_FUNC_DECL mat<4, 4, T, defaultp> orthoZO( + T left, T right, T bottom, T top, T zNear, T zFar); + + /// Creates a matrix for an orthographic parallel viewing volume, using left-handed coordinates if GLM_FORCE_LEFT_HANDED if defined or right-handed coordinates otherwise. + /// The near and far clip planes correspond to z normalized device coordinates of -1 and +1 respectively. (OpenGL clip volume definition) + /// + /// @tparam T A floating-point scalar type + /// + /// @see - glm::ortho(T const& left, T const& right, T const& bottom, T const& top) + template + GLM_FUNC_DECL mat<4, 4, T, defaultp> orthoNO( + T left, T right, T bottom, T top, T zNear, T zFar); + + /// Creates a matrix for an orthographic parallel viewing volume, using left-handed coordinates. + /// If GLM_FORCE_DEPTH_ZERO_TO_ONE is defined, the near and far clip planes correspond to z normalized device coordinates of 0 and +1 respectively. (Direct3D clip volume definition) + /// Otherwise, the near and far clip planes correspond to z normalized device coordinates of -1 and +1 respectively. (OpenGL clip volume definition) + /// + /// @tparam T A floating-point scalar type + /// + /// @see - glm::ortho(T const& left, T const& right, T const& bottom, T const& top) + template + GLM_FUNC_DECL mat<4, 4, T, defaultp> orthoLH( + T left, T right, T bottom, T top, T zNear, T zFar); + + /// Creates a matrix for an orthographic parallel viewing volume, using right-handed coordinates. + /// If GLM_FORCE_DEPTH_ZERO_TO_ONE is defined, the near and far clip planes correspond to z normalized device coordinates of 0 and +1 respectively. (Direct3D clip volume definition) + /// Otherwise, the near and far clip planes correspond to z normalized device coordinates of -1 and +1 respectively. (OpenGL clip volume definition) + /// + /// @tparam T A floating-point scalar type + /// + /// @see - glm::ortho(T const& left, T const& right, T const& bottom, T const& top) + template + GLM_FUNC_DECL mat<4, 4, T, defaultp> orthoRH( + T left, T right, T bottom, T top, T zNear, T zFar); + + /// Creates a matrix for an orthographic parallel viewing volume, using the default handedness and default near and far clip planes definition. + /// To change default handedness use GLM_FORCE_LEFT_HANDED. To change default near and far clip planes definition use GLM_FORCE_DEPTH_ZERO_TO_ONE. + /// + /// @tparam T A floating-point scalar type + /// + /// @see - glm::ortho(T const& left, T const& right, T const& bottom, T const& top) + /// @see glOrtho man page + template + GLM_FUNC_DECL mat<4, 4, T, defaultp> ortho( + T left, T right, T bottom, T top, T zNear, T zFar); + + /// Creates a left handed frustum matrix. + /// The near and far clip planes correspond to z normalized device coordinates of 0 and +1 respectively. (Direct3D clip volume definition) + /// + /// @tparam T A floating-point scalar type + template + GLM_FUNC_DECL mat<4, 4, T, defaultp> frustumLH_ZO( + T left, T right, T bottom, T top, T near, T far); + + /// Creates a left handed frustum matrix. + /// The near and far clip planes correspond to z normalized device coordinates of -1 and +1 respectively. (OpenGL clip volume definition) + /// + /// @tparam T A floating-point scalar type + template + GLM_FUNC_DECL mat<4, 4, T, defaultp> frustumLH_NO( + T left, T right, T bottom, T top, T near, T far); + + /// Creates a right handed frustum matrix. + /// The near and far clip planes correspond to z normalized device coordinates of 0 and +1 respectively. (Direct3D clip volume definition) + /// + /// @tparam T A floating-point scalar type + template + GLM_FUNC_DECL mat<4, 4, T, defaultp> frustumRH_ZO( + T left, T right, T bottom, T top, T near, T far); + + /// Creates a right handed frustum matrix. + /// The near and far clip planes correspond to z normalized device coordinates of -1 and +1 respectively. (OpenGL clip volume definition) + /// + /// @tparam T A floating-point scalar type + template + GLM_FUNC_DECL mat<4, 4, T, defaultp> frustumRH_NO( + T left, T right, T bottom, T top, T near, T far); + + /// Creates a frustum matrix using left-handed coordinates if GLM_FORCE_LEFT_HANDED if defined or right-handed coordinates otherwise. + /// The near and far clip planes correspond to z normalized device coordinates of 0 and +1 respectively. (Direct3D clip volume definition) + /// + /// @tparam T A floating-point scalar type + template + GLM_FUNC_DECL mat<4, 4, T, defaultp> frustumZO( + T left, T right, T bottom, T top, T near, T far); + + /// Creates a frustum matrix using left-handed coordinates if GLM_FORCE_LEFT_HANDED if defined or right-handed coordinates otherwise. + /// The near and far clip planes correspond to z normalized device coordinates of -1 and +1 respectively. (OpenGL clip volume definition) + /// + /// @tparam T A floating-point scalar type + template + GLM_FUNC_DECL mat<4, 4, T, defaultp> frustumNO( + T left, T right, T bottom, T top, T near, T far); + + /// Creates a left handed frustum matrix. + /// If GLM_FORCE_DEPTH_ZERO_TO_ONE is defined, the near and far clip planes correspond to z normalized device coordinates of 0 and +1 respectively. (Direct3D clip volume definition) + /// Otherwise, the near and far clip planes correspond to z normalized device coordinates of -1 and +1 respectively. (OpenGL clip volume definition) + /// + /// @tparam T A floating-point scalar type + template + GLM_FUNC_DECL mat<4, 4, T, defaultp> frustumLH( + T left, T right, T bottom, T top, T near, T far); + + /// Creates a right handed frustum matrix. + /// If GLM_FORCE_DEPTH_ZERO_TO_ONE is defined, the near and far clip planes correspond to z normalized device coordinates of 0 and +1 respectively. (Direct3D clip volume definition) + /// Otherwise, the near and far clip planes correspond to z normalized device coordinates of -1 and +1 respectively. (OpenGL clip volume definition) + /// + /// @tparam T A floating-point scalar type + template + GLM_FUNC_DECL mat<4, 4, T, defaultp> frustumRH( + T left, T right, T bottom, T top, T near, T far); + + /// Creates a frustum matrix with default handedness, using the default handedness and default near and far clip planes definition. + /// To change default handedness use GLM_FORCE_LEFT_HANDED. To change default near and far clip planes definition use GLM_FORCE_DEPTH_ZERO_TO_ONE. + /// + /// @tparam T A floating-point scalar type + /// @see glFrustum man page + template + GLM_FUNC_DECL mat<4, 4, T, defaultp> frustum( + T left, T right, T bottom, T top, T near, T far); + + + /// Creates a matrix for a right handed, symetric perspective-view frustum. + /// The near and far clip planes correspond to z normalized device coordinates of 0 and +1 respectively. (Direct3D clip volume definition) + /// + /// @param fovy Specifies the field of view angle, in degrees, in the y direction. Expressed in radians. + /// @param aspect Specifies the aspect ratio that determines the field of view in the x direction. The aspect ratio is the ratio of x (width) to y (height). + /// @param near Specifies the distance from the viewer to the near clipping plane (always positive). + /// @param far Specifies the distance from the viewer to the far clipping plane (always positive). + /// + /// @tparam T A floating-point scalar type + template + GLM_FUNC_DECL mat<4, 4, T, defaultp> perspectiveRH_ZO( + T fovy, T aspect, T near, T far); + + /// Creates a matrix for a right handed, symetric perspective-view frustum. + /// The near and far clip planes correspond to z normalized device coordinates of -1 and +1 respectively. (OpenGL clip volume definition) + /// + /// @param fovy Specifies the field of view angle, in degrees, in the y direction. Expressed in radians. + /// @param aspect Specifies the aspect ratio that determines the field of view in the x direction. The aspect ratio is the ratio of x (width) to y (height). + /// @param near Specifies the distance from the viewer to the near clipping plane (always positive). + /// @param far Specifies the distance from the viewer to the far clipping plane (always positive). + /// + /// @tparam T A floating-point scalar type + template + GLM_FUNC_DECL mat<4, 4, T, defaultp> perspectiveRH_NO( + T fovy, T aspect, T near, T far); + + /// Creates a matrix for a left handed, symetric perspective-view frustum. + /// The near and far clip planes correspond to z normalized device coordinates of 0 and +1 respectively. (Direct3D clip volume definition) + /// + /// @param fovy Specifies the field of view angle, in degrees, in the y direction. Expressed in radians. + /// @param aspect Specifies the aspect ratio that determines the field of view in the x direction. The aspect ratio is the ratio of x (width) to y (height). + /// @param near Specifies the distance from the viewer to the near clipping plane (always positive). + /// @param far Specifies the distance from the viewer to the far clipping plane (always positive). + /// + /// @tparam T A floating-point scalar type + template + GLM_FUNC_DECL mat<4, 4, T, defaultp> perspectiveLH_ZO( + T fovy, T aspect, T near, T far); + + /// Creates a matrix for a left handed, symetric perspective-view frustum. + /// The near and far clip planes correspond to z normalized device coordinates of -1 and +1 respectively. (OpenGL clip volume definition) + /// + /// @param fovy Specifies the field of view angle, in degrees, in the y direction. Expressed in radians. + /// @param aspect Specifies the aspect ratio that determines the field of view in the x direction. The aspect ratio is the ratio of x (width) to y (height). + /// @param near Specifies the distance from the viewer to the near clipping plane (always positive). + /// @param far Specifies the distance from the viewer to the far clipping plane (always positive). + /// + /// @tparam T A floating-point scalar type + template + GLM_FUNC_DECL mat<4, 4, T, defaultp> perspectiveLH_NO( + T fovy, T aspect, T near, T far); + + /// Creates a matrix for a symetric perspective-view frustum using left-handed coordinates if GLM_FORCE_LEFT_HANDED if defined or right-handed coordinates otherwise. + /// The near and far clip planes correspond to z normalized device coordinates of 0 and +1 respectively. (Direct3D clip volume definition) + /// + /// @param fovy Specifies the field of view angle, in degrees, in the y direction. Expressed in radians. + /// @param aspect Specifies the aspect ratio that determines the field of view in the x direction. The aspect ratio is the ratio of x (width) to y (height). + /// @param near Specifies the distance from the viewer to the near clipping plane (always positive). + /// @param far Specifies the distance from the viewer to the far clipping plane (always positive). + /// + /// @tparam T A floating-point scalar type + template + GLM_FUNC_DECL mat<4, 4, T, defaultp> perspectiveZO( + T fovy, T aspect, T near, T far); + + /// Creates a matrix for a symetric perspective-view frustum using left-handed coordinates if GLM_FORCE_LEFT_HANDED if defined or right-handed coordinates otherwise. + /// The near and far clip planes correspond to z normalized device coordinates of -1 and +1 respectively. (OpenGL clip volume definition) + /// + /// @param fovy Specifies the field of view angle, in degrees, in the y direction. Expressed in radians. + /// @param aspect Specifies the aspect ratio that determines the field of view in the x direction. The aspect ratio is the ratio of x (width) to y (height). + /// @param near Specifies the distance from the viewer to the near clipping plane (always positive). + /// @param far Specifies the distance from the viewer to the far clipping plane (always positive). + /// + /// @tparam T A floating-point scalar type + template + GLM_FUNC_DECL mat<4, 4, T, defaultp> perspectiveNO( + T fovy, T aspect, T near, T far); + + /// Creates a matrix for a right handed, symetric perspective-view frustum. + /// If GLM_FORCE_DEPTH_ZERO_TO_ONE is defined, the near and far clip planes correspond to z normalized device coordinates of 0 and +1 respectively. (Direct3D clip volume definition) + /// Otherwise, the near and far clip planes correspond to z normalized device coordinates of -1 and +1 respectively. (OpenGL clip volume definition) + /// + /// @param fovy Specifies the field of view angle, in degrees, in the y direction. Expressed in radians. + /// @param aspect Specifies the aspect ratio that determines the field of view in the x direction. The aspect ratio is the ratio of x (width) to y (height). + /// @param near Specifies the distance from the viewer to the near clipping plane (always positive). + /// @param far Specifies the distance from the viewer to the far clipping plane (always positive). + /// + /// @tparam T A floating-point scalar type + template + GLM_FUNC_DECL mat<4, 4, T, defaultp> perspectiveRH( + T fovy, T aspect, T near, T far); + + /// Creates a matrix for a left handed, symetric perspective-view frustum. + /// If GLM_FORCE_DEPTH_ZERO_TO_ONE is defined, the near and far clip planes correspond to z normalized device coordinates of 0 and +1 respectively. (Direct3D clip volume definition) + /// Otherwise, the near and far clip planes correspond to z normalized device coordinates of -1 and +1 respectively. (OpenGL clip volume definition) + /// + /// @param fovy Specifies the field of view angle, in degrees, in the y direction. Expressed in radians. + /// @param aspect Specifies the aspect ratio that determines the field of view in the x direction. The aspect ratio is the ratio of x (width) to y (height). + /// @param near Specifies the distance from the viewer to the near clipping plane (always positive). + /// @param far Specifies the distance from the viewer to the far clipping plane (always positive). + /// + /// @tparam T A floating-point scalar type + template + GLM_FUNC_DECL mat<4, 4, T, defaultp> perspectiveLH( + T fovy, T aspect, T near, T far); + + /// Creates a matrix for a symetric perspective-view frustum based on the default handedness and default near and far clip planes definition. + /// To change default handedness use GLM_FORCE_LEFT_HANDED. To change default near and far clip planes definition use GLM_FORCE_DEPTH_ZERO_TO_ONE. + /// + /// @param fovy Specifies the field of view angle in the y direction. Expressed in radians. + /// @param aspect Specifies the aspect ratio that determines the field of view in the x direction. The aspect ratio is the ratio of x (width) to y (height). + /// @param near Specifies the distance from the viewer to the near clipping plane (always positive). + /// @param far Specifies the distance from the viewer to the far clipping plane (always positive). + /// + /// @tparam T A floating-point scalar type + /// @see gluPerspective man page + template + GLM_FUNC_DECL mat<4, 4, T, defaultp> perspective( + T fovy, T aspect, T near, T far); + + /// Builds a perspective projection matrix based on a field of view using right-handed coordinates. + /// The near and far clip planes correspond to z normalized device coordinates of 0 and +1 respectively. (Direct3D clip volume definition) + /// + /// @param fov Expressed in radians. + /// @param width Width of the viewport + /// @param height Height of the viewport + /// @param near Specifies the distance from the viewer to the near clipping plane (always positive). + /// @param far Specifies the distance from the viewer to the far clipping plane (always positive). + /// + /// @tparam T A floating-point scalar type + template + GLM_FUNC_DECL mat<4, 4, T, defaultp> perspectiveFovRH_ZO( + T fov, T width, T height, T near, T far); + + /// Builds a perspective projection matrix based on a field of view using right-handed coordinates. + /// The near and far clip planes correspond to z normalized device coordinates of -1 and +1 respectively. (OpenGL clip volume definition) + /// + /// @param fov Expressed in radians. + /// @param width Width of the viewport + /// @param height Height of the viewport + /// @param near Specifies the distance from the viewer to the near clipping plane (always positive). + /// @param far Specifies the distance from the viewer to the far clipping plane (always positive). + /// + /// @tparam T A floating-point scalar type + template + GLM_FUNC_DECL mat<4, 4, T, defaultp> perspectiveFovRH_NO( + T fov, T width, T height, T near, T far); + + /// Builds a perspective projection matrix based on a field of view using left-handed coordinates. + /// The near and far clip planes correspond to z normalized device coordinates of 0 and +1 respectively. (Direct3D clip volume definition) + /// + /// @param fov Expressed in radians. + /// @param width Width of the viewport + /// @param height Height of the viewport + /// @param near Specifies the distance from the viewer to the near clipping plane (always positive). + /// @param far Specifies the distance from the viewer to the far clipping plane (always positive). + /// + /// @tparam T A floating-point scalar type + template + GLM_FUNC_DECL mat<4, 4, T, defaultp> perspectiveFovLH_ZO( + T fov, T width, T height, T near, T far); + + /// Builds a perspective projection matrix based on a field of view using left-handed coordinates. + /// The near and far clip planes correspond to z normalized device coordinates of -1 and +1 respectively. (OpenGL clip volume definition) + /// + /// @param fov Expressed in radians. + /// @param width Width of the viewport + /// @param height Height of the viewport + /// @param near Specifies the distance from the viewer to the near clipping plane (always positive). + /// @param far Specifies the distance from the viewer to the far clipping plane (always positive). + /// + /// @tparam T A floating-point scalar type + template + GLM_FUNC_DECL mat<4, 4, T, defaultp> perspectiveFovLH_NO( + T fov, T width, T height, T near, T far); + + /// Builds a perspective projection matrix based on a field of view using left-handed coordinates if GLM_FORCE_LEFT_HANDED if defined or right-handed coordinates otherwise. + /// The near and far clip planes correspond to z normalized device coordinates of 0 and +1 respectively. (Direct3D clip volume definition) + /// + /// @param fov Expressed in radians. + /// @param width Width of the viewport + /// @param height Height of the viewport + /// @param near Specifies the distance from the viewer to the near clipping plane (always positive). + /// @param far Specifies the distance from the viewer to the far clipping plane (always positive). + /// + /// @tparam T A floating-point scalar type + template + GLM_FUNC_DECL mat<4, 4, T, defaultp> perspectiveFovZO( + T fov, T width, T height, T near, T far); + + /// Builds a perspective projection matrix based on a field of view using left-handed coordinates if GLM_FORCE_LEFT_HANDED if defined or right-handed coordinates otherwise. + /// The near and far clip planes correspond to z normalized device coordinates of -1 and +1 respectively. (OpenGL clip volume definition) + /// + /// @param fov Expressed in radians. + /// @param width Width of the viewport + /// @param height Height of the viewport + /// @param near Specifies the distance from the viewer to the near clipping plane (always positive). + /// @param far Specifies the distance from the viewer to the far clipping plane (always positive). + /// + /// @tparam T A floating-point scalar type + template + GLM_FUNC_DECL mat<4, 4, T, defaultp> perspectiveFovNO( + T fov, T width, T height, T near, T far); + + /// Builds a right handed perspective projection matrix based on a field of view. + /// If GLM_FORCE_DEPTH_ZERO_TO_ONE is defined, the near and far clip planes correspond to z normalized device coordinates of 0 and +1 respectively. (Direct3D clip volume definition) + /// Otherwise, the near and far clip planes correspond to z normalized device coordinates of -1 and +1 respectively. (OpenGL clip volume definition) + /// + /// @param fov Expressed in radians. + /// @param width Width of the viewport + /// @param height Height of the viewport + /// @param near Specifies the distance from the viewer to the near clipping plane (always positive). + /// @param far Specifies the distance from the viewer to the far clipping plane (always positive). + /// + /// @tparam T A floating-point scalar type + template + GLM_FUNC_DECL mat<4, 4, T, defaultp> perspectiveFovRH( + T fov, T width, T height, T near, T far); + + /// Builds a left handed perspective projection matrix based on a field of view. + /// If GLM_FORCE_DEPTH_ZERO_TO_ONE is defined, the near and far clip planes correspond to z normalized device coordinates of 0 and +1 respectively. (Direct3D clip volume definition) + /// Otherwise, the near and far clip planes correspond to z normalized device coordinates of -1 and +1 respectively. (OpenGL clip volume definition) + /// + /// @param fov Expressed in radians. + /// @param width Width of the viewport + /// @param height Height of the viewport + /// @param near Specifies the distance from the viewer to the near clipping plane (always positive). + /// @param far Specifies the distance from the viewer to the far clipping plane (always positive). + /// + /// @tparam T A floating-point scalar type + template + GLM_FUNC_DECL mat<4, 4, T, defaultp> perspectiveFovLH( + T fov, T width, T height, T near, T far); + + /// Builds a perspective projection matrix based on a field of view and the default handedness and default near and far clip planes definition. + /// To change default handedness use GLM_FORCE_LEFT_HANDED. To change default near and far clip planes definition use GLM_FORCE_DEPTH_ZERO_TO_ONE. + /// + /// @param fov Expressed in radians. + /// @param width Width of the viewport + /// @param height Height of the viewport + /// @param near Specifies the distance from the viewer to the near clipping plane (always positive). + /// @param far Specifies the distance from the viewer to the far clipping plane (always positive). + /// + /// @tparam T A floating-point scalar type + template + GLM_FUNC_DECL mat<4, 4, T, defaultp> perspectiveFov( + T fov, T width, T height, T near, T far); + + /// Creates a matrix for a left handed, symmetric perspective-view frustum with far plane at infinite. + /// + /// @param fovy Specifies the field of view angle, in degrees, in the y direction. Expressed in radians. + /// @param aspect Specifies the aspect ratio that determines the field of view in the x direction. The aspect ratio is the ratio of x (width) to y (height). + /// @param near Specifies the distance from the viewer to the near clipping plane (always positive). + /// + /// @tparam T A floating-point scalar type + template + GLM_FUNC_DECL mat<4, 4, T, defaultp> infinitePerspectiveLH( + T fovy, T aspect, T near); + + /// Creates a matrix for a right handed, symmetric perspective-view frustum with far plane at infinite. + /// + /// @param fovy Specifies the field of view angle, in degrees, in the y direction. Expressed in radians. + /// @param aspect Specifies the aspect ratio that determines the field of view in the x direction. The aspect ratio is the ratio of x (width) to y (height). + /// @param near Specifies the distance from the viewer to the near clipping plane (always positive). + /// + /// @tparam T A floating-point scalar type + template + GLM_FUNC_DECL mat<4, 4, T, defaultp> infinitePerspectiveRH( + T fovy, T aspect, T near); + + /// Creates a matrix for a symmetric perspective-view frustum with far plane at infinite with default handedness. + /// + /// @param fovy Specifies the field of view angle, in degrees, in the y direction. Expressed in radians. + /// @param aspect Specifies the aspect ratio that determines the field of view in the x direction. The aspect ratio is the ratio of x (width) to y (height). + /// @param near Specifies the distance from the viewer to the near clipping plane (always positive). + /// + /// @tparam T A floating-point scalar type + template + GLM_FUNC_DECL mat<4, 4, T, defaultp> infinitePerspective( + T fovy, T aspect, T near); + + /// Creates a matrix for a symmetric perspective-view frustum with far plane at infinite for graphics hardware that doesn't support depth clamping. + /// + /// @param fovy Specifies the field of view angle, in degrees, in the y direction. Expressed in radians. + /// @param aspect Specifies the aspect ratio that determines the field of view in the x direction. The aspect ratio is the ratio of x (width) to y (height). + /// @param near Specifies the distance from the viewer to the near clipping plane (always positive). + /// + /// @tparam T A floating-point scalar type + template + GLM_FUNC_DECL mat<4, 4, T, defaultp> tweakedInfinitePerspective( + T fovy, T aspect, T near); + + /// Creates a matrix for a symmetric perspective-view frustum with far plane at infinite for graphics hardware that doesn't support depth clamping. + /// + /// @param fovy Specifies the field of view angle, in degrees, in the y direction. Expressed in radians. + /// @param aspect Specifies the aspect ratio that determines the field of view in the x direction. The aspect ratio is the ratio of x (width) to y (height). + /// @param near Specifies the distance from the viewer to the near clipping plane (always positive). + /// @param ep Epsilon + /// + /// @tparam T A floating-point scalar type + template + GLM_FUNC_DECL mat<4, 4, T, defaultp> tweakedInfinitePerspective( + T fovy, T aspect, T near, T ep); + + /// @} +}//namespace glm + +#include "matrix_clip_space.inl" diff --git a/src/GLMath/glm/ext/matrix_clip_space.inl b/src/GLMath/glm/ext/matrix_clip_space.inl new file mode 100644 index 000000000..baf68ccab --- /dev/null +++ b/src/GLMath/glm/ext/matrix_clip_space.inl @@ -0,0 +1,534 @@ +namespace glm +{ + template + GLM_FUNC_QUALIFIER mat<4, 4, T, defaultp> ortho(T left, T right, T bottom, T top) + { + mat<4, 4, T, defaultp> Result(static_cast(1)); + Result[0][0] = static_cast(2) / (right - left); + Result[1][1] = static_cast(2) / (top - bottom); + Result[2][2] = - static_cast(1); + Result[3][0] = - (right + left) / (right - left); + Result[3][1] = - (top + bottom) / (top - bottom); + return Result; + } + + template + GLM_FUNC_QUALIFIER mat<4, 4, T, defaultp> orthoLH_ZO(T left, T right, T bottom, T top, T zNear, T zFar) + { + mat<4, 4, T, defaultp> Result(1); + Result[0][0] = static_cast(2) / (right - left); + Result[1][1] = static_cast(2) / (top - bottom); + Result[2][2] = static_cast(1) / (zFar - zNear); + Result[3][0] = - (right + left) / (right - left); + Result[3][1] = - (top + bottom) / (top - bottom); + Result[3][2] = - zNear / (zFar - zNear); + return Result; + } + + template + GLM_FUNC_QUALIFIER mat<4, 4, T, defaultp> orthoLH_NO(T left, T right, T bottom, T top, T zNear, T zFar) + { + mat<4, 4, T, defaultp> Result(1); + Result[0][0] = static_cast(2) / (right - left); + Result[1][1] = static_cast(2) / (top - bottom); + Result[2][2] = static_cast(2) / (zFar - zNear); + Result[3][0] = - (right + left) / (right - left); + Result[3][1] = - (top + bottom) / (top - bottom); + Result[3][2] = - (zFar + zNear) / (zFar - zNear); + return Result; + } + + template + GLM_FUNC_QUALIFIER mat<4, 4, T, defaultp> orthoRH_ZO(T left, T right, T bottom, T top, T zNear, T zFar) + { + mat<4, 4, T, defaultp> Result(1); + Result[0][0] = static_cast(2) / (right - left); + Result[1][1] = static_cast(2) / (top - bottom); + Result[2][2] = - static_cast(1) / (zFar - zNear); + Result[3][0] = - (right + left) / (right - left); + Result[3][1] = - (top + bottom) / (top - bottom); + Result[3][2] = - zNear / (zFar - zNear); + return Result; + } + + template + GLM_FUNC_QUALIFIER mat<4, 4, T, defaultp> orthoRH_NO(T left, T right, T bottom, T top, T zNear, T zFar) + { + mat<4, 4, T, defaultp> Result(1); + Result[0][0] = static_cast(2) / (right - left); + Result[1][1] = static_cast(2) / (top - bottom); + Result[2][2] = - static_cast(2) / (zFar - zNear); + Result[3][0] = - (right + left) / (right - left); + Result[3][1] = - (top + bottom) / (top - bottom); + Result[3][2] = - (zFar + zNear) / (zFar - zNear); + return Result; + } + + template + GLM_FUNC_QUALIFIER mat<4, 4, T, defaultp> orthoZO(T left, T right, T bottom, T top, T zNear, T zFar) + { + if(GLM_CONFIG_CLIP_CONTROL & GLM_CLIP_CONTROL_LH_BIT) + return orthoLH_ZO(left, right, bottom, top, zNear, zFar); + else + return orthoRH_ZO(left, right, bottom, top, zNear, zFar); + } + + template + GLM_FUNC_QUALIFIER mat<4, 4, T, defaultp> orthoNO(T left, T right, T bottom, T top, T zNear, T zFar) + { + if(GLM_CONFIG_CLIP_CONTROL & GLM_CLIP_CONTROL_LH_BIT) + return orthoLH_NO(left, right, bottom, top, zNear, zFar); + else + return orthoRH_NO(left, right, bottom, top, zNear, zFar); + } + + template + GLM_FUNC_QUALIFIER mat<4, 4, T, defaultp> orthoLH(T left, T right, T bottom, T top, T zNear, T zFar) + { + if(GLM_CONFIG_CLIP_CONTROL & GLM_CLIP_CONTROL_ZO_BIT) + return orthoLH_ZO(left, right, bottom, top, zNear, zFar); + else + return orthoLH_NO(left, right, bottom, top, zNear, zFar); + + } + + template + GLM_FUNC_QUALIFIER mat<4, 4, T, defaultp> orthoRH(T left, T right, T bottom, T top, T zNear, T zFar) + { + if(GLM_CONFIG_CLIP_CONTROL & GLM_CLIP_CONTROL_ZO_BIT) + return orthoRH_ZO(left, right, bottom, top, zNear, zFar); + else + return orthoRH_NO(left, right, bottom, top, zNear, zFar); + } + + template + GLM_FUNC_QUALIFIER mat<4, 4, T, defaultp> ortho(T left, T right, T bottom, T top, T zNear, T zFar) + { + if(GLM_CONFIG_CLIP_CONTROL == GLM_CLIP_CONTROL_LH_ZO) + return orthoLH_ZO(left, right, bottom, top, zNear, zFar); + else if(GLM_CONFIG_CLIP_CONTROL == GLM_CLIP_CONTROL_LH_NO) + return orthoLH_NO(left, right, bottom, top, zNear, zFar); + else if(GLM_CONFIG_CLIP_CONTROL == GLM_CLIP_CONTROL_RH_ZO) + return orthoRH_ZO(left, right, bottom, top, zNear, zFar); + else if(GLM_CONFIG_CLIP_CONTROL == GLM_CLIP_CONTROL_RH_NO) + return orthoRH_NO(left, right, bottom, top, zNear, zFar); + } + + template + GLM_FUNC_QUALIFIER mat<4, 4, T, defaultp> frustumLH_ZO(T left, T right, T bottom, T top, T nearVal, T farVal) + { + mat<4, 4, T, defaultp> Result(0); + Result[0][0] = (static_cast(2) * nearVal) / (right - left); + Result[1][1] = (static_cast(2) * nearVal) / (top - bottom); + Result[2][0] = (right + left) / (right - left); + Result[2][1] = (top + bottom) / (top - bottom); + Result[2][2] = farVal / (farVal - nearVal); + Result[2][3] = static_cast(1); + Result[3][2] = -(farVal * nearVal) / (farVal - nearVal); + return Result; + } + + template + GLM_FUNC_QUALIFIER mat<4, 4, T, defaultp> frustumLH_NO(T left, T right, T bottom, T top, T nearVal, T farVal) + { + mat<4, 4, T, defaultp> Result(0); + Result[0][0] = (static_cast(2) * nearVal) / (right - left); + Result[1][1] = (static_cast(2) * nearVal) / (top - bottom); + Result[2][0] = (right + left) / (right - left); + Result[2][1] = (top + bottom) / (top - bottom); + Result[2][2] = (farVal + nearVal) / (farVal - nearVal); + Result[2][3] = static_cast(1); + Result[3][2] = - (static_cast(2) * farVal * nearVal) / (farVal - nearVal); + return Result; + } + + template + GLM_FUNC_QUALIFIER mat<4, 4, T, defaultp> frustumRH_ZO(T left, T right, T bottom, T top, T nearVal, T farVal) + { + mat<4, 4, T, defaultp> Result(0); + Result[0][0] = (static_cast(2) * nearVal) / (right - left); + Result[1][1] = (static_cast(2) * nearVal) / (top - bottom); + Result[2][0] = (right + left) / (right - left); + Result[2][1] = (top + bottom) / (top - bottom); + Result[2][2] = farVal / (nearVal - farVal); + Result[2][3] = static_cast(-1); + Result[3][2] = -(farVal * nearVal) / (farVal - nearVal); + return Result; + } + + template + GLM_FUNC_QUALIFIER mat<4, 4, T, defaultp> frustumRH_NO(T left, T right, T bottom, T top, T nearVal, T farVal) + { + mat<4, 4, T, defaultp> Result(0); + Result[0][0] = (static_cast(2) * nearVal) / (right - left); + Result[1][1] = (static_cast(2) * nearVal) / (top - bottom); + Result[2][0] = (right + left) / (right - left); + Result[2][1] = (top + bottom) / (top - bottom); + Result[2][2] = - (farVal + nearVal) / (farVal - nearVal); + Result[2][3] = static_cast(-1); + Result[3][2] = - (static_cast(2) * farVal * nearVal) / (farVal - nearVal); + return Result; + } + + template + GLM_FUNC_QUALIFIER mat<4, 4, T, defaultp> frustumZO(T left, T right, T bottom, T top, T nearVal, T farVal) + { + if(GLM_CONFIG_CLIP_CONTROL & GLM_CLIP_CONTROL_LH_BIT) + return frustumLH_ZO(left, right, bottom, top, nearVal, farVal); + else + return frustumRH_ZO(left, right, bottom, top, nearVal, farVal); + } + + template + GLM_FUNC_QUALIFIER mat<4, 4, T, defaultp> frustumNO(T left, T right, T bottom, T top, T nearVal, T farVal) + { + if(GLM_CONFIG_CLIP_CONTROL & GLM_CLIP_CONTROL_LH_BIT) + return frustumLH_NO(left, right, bottom, top, nearVal, farVal); + else + return frustumRH_NO(left, right, bottom, top, nearVal, farVal); + } + + template + GLM_FUNC_QUALIFIER mat<4, 4, T, defaultp> frustumLH(T left, T right, T bottom, T top, T nearVal, T farVal) + { + if(GLM_CONFIG_CLIP_CONTROL & GLM_CLIP_CONTROL_ZO_BIT) + return frustumLH_ZO(left, right, bottom, top, nearVal, farVal); + else + return frustumLH_NO(left, right, bottom, top, nearVal, farVal); + } + + template + GLM_FUNC_QUALIFIER mat<4, 4, T, defaultp> frustumRH(T left, T right, T bottom, T top, T nearVal, T farVal) + { + if(GLM_CONFIG_CLIP_CONTROL & GLM_CLIP_CONTROL_ZO_BIT) + return frustumRH_ZO(left, right, bottom, top, nearVal, farVal); + else + return frustumRH_NO(left, right, bottom, top, nearVal, farVal); + } + + template + GLM_FUNC_QUALIFIER mat<4, 4, T, defaultp> frustum(T left, T right, T bottom, T top, T nearVal, T farVal) + { + if(GLM_CONFIG_CLIP_CONTROL == GLM_CLIP_CONTROL_LH_ZO) + return frustumLH_ZO(left, right, bottom, top, nearVal, farVal); + else if(GLM_CONFIG_CLIP_CONTROL == GLM_CLIP_CONTROL_LH_NO) + return frustumLH_NO(left, right, bottom, top, nearVal, farVal); + else if(GLM_CONFIG_CLIP_CONTROL == GLM_CLIP_CONTROL_RH_ZO) + return frustumRH_ZO(left, right, bottom, top, nearVal, farVal); + else if(GLM_CONFIG_CLIP_CONTROL == GLM_CLIP_CONTROL_RH_NO) + return frustumRH_NO(left, right, bottom, top, nearVal, farVal); + } + + template + GLM_FUNC_QUALIFIER mat<4, 4, T, defaultp> perspectiveRH_ZO(T fovy, T aspect, T zNear, T zFar) + { + assert(abs(aspect - std::numeric_limits::epsilon()) > static_cast(0)); + + T const tanHalfFovy = tan(fovy / static_cast(2)); + + mat<4, 4, T, defaultp> Result(static_cast(0)); + Result[0][0] = static_cast(1) / (aspect * tanHalfFovy); + Result[1][1] = static_cast(1) / (tanHalfFovy); + Result[2][2] = zFar / (zNear - zFar); + Result[2][3] = - static_cast(1); + Result[3][2] = -(zFar * zNear) / (zFar - zNear); + return Result; + } + + template + GLM_FUNC_QUALIFIER mat<4, 4, T, defaultp> perspectiveRH_NO(T fovy, T aspect, T zNear, T zFar) + { + assert(abs(aspect - std::numeric_limits::epsilon()) > static_cast(0)); + + T const tanHalfFovy = tan(fovy / static_cast(2)); + + mat<4, 4, T, defaultp> Result(static_cast(0)); + Result[0][0] = static_cast(1) / (aspect * tanHalfFovy); + Result[1][1] = static_cast(1) / (tanHalfFovy); + Result[2][2] = - (zFar + zNear) / (zFar - zNear); + Result[2][3] = - static_cast(1); + Result[3][2] = - (static_cast(2) * zFar * zNear) / (zFar - zNear); + return Result; + } + + template + GLM_FUNC_QUALIFIER mat<4, 4, T, defaultp> perspectiveLH_ZO(T fovy, T aspect, T zNear, T zFar) + { + assert(abs(aspect - std::numeric_limits::epsilon()) > static_cast(0)); + + T const tanHalfFovy = tan(fovy / static_cast(2)); + + mat<4, 4, T, defaultp> Result(static_cast(0)); + Result[0][0] = static_cast(1) / (aspect * tanHalfFovy); + Result[1][1] = static_cast(1) / (tanHalfFovy); + Result[2][2] = zFar / (zFar - zNear); + Result[2][3] = static_cast(1); + Result[3][2] = -(zFar * zNear) / (zFar - zNear); + return Result; + } + + template + GLM_FUNC_QUALIFIER mat<4, 4, T, defaultp> perspectiveLH_NO(T fovy, T aspect, T zNear, T zFar) + { + assert(abs(aspect - std::numeric_limits::epsilon()) > static_cast(0)); + + T const tanHalfFovy = tan(fovy / static_cast(2)); + + mat<4, 4, T, defaultp> Result(static_cast(0)); + Result[0][0] = static_cast(1) / (aspect * tanHalfFovy); + Result[1][1] = static_cast(1) / (tanHalfFovy); + Result[2][2] = (zFar + zNear) / (zFar - zNear); + Result[2][3] = static_cast(1); + Result[3][2] = - (static_cast(2) * zFar * zNear) / (zFar - zNear); + return Result; + } + + template + GLM_FUNC_QUALIFIER mat<4, 4, T, defaultp> perspectiveZO(T fovy, T aspect, T zNear, T zFar) + { + if(GLM_CONFIG_CLIP_CONTROL & GLM_CLIP_CONTROL_LH_BIT) + return perspectiveLH_ZO(fovy, aspect, zNear, zFar); + else + return perspectiveRH_ZO(fovy, aspect, zNear, zFar); + } + + template + GLM_FUNC_QUALIFIER mat<4, 4, T, defaultp> perspectiveNO(T fovy, T aspect, T zNear, T zFar) + { + if(GLM_CONFIG_CLIP_CONTROL & GLM_CLIP_CONTROL_LH_BIT) + return perspectiveLH_NO(fovy, aspect, zNear, zFar); + else + return perspectiveRH_NO(fovy, aspect, zNear, zFar); + } + + template + GLM_FUNC_QUALIFIER mat<4, 4, T, defaultp> perspectiveLH(T fovy, T aspect, T zNear, T zFar) + { + if(GLM_CONFIG_CLIP_CONTROL & GLM_CLIP_CONTROL_ZO_BIT) + return perspectiveLH_ZO(fovy, aspect, zNear, zFar); + else + return perspectiveLH_NO(fovy, aspect, zNear, zFar); + + } + + template + GLM_FUNC_QUALIFIER mat<4, 4, T, defaultp> perspectiveRH(T fovy, T aspect, T zNear, T zFar) + { + if(GLM_CONFIG_CLIP_CONTROL & GLM_CLIP_CONTROL_ZO_BIT) + return perspectiveRH_ZO(fovy, aspect, zNear, zFar); + else + return perspectiveRH_NO(fovy, aspect, zNear, zFar); + } + + template + GLM_FUNC_QUALIFIER mat<4, 4, T, defaultp> perspective(T fovy, T aspect, T zNear, T zFar) + { + GLM_IF_CONSTEXPR(GLM_CONFIG_CLIP_CONTROL == GLM_CLIP_CONTROL_LH_ZO) + return perspectiveLH_ZO(fovy, aspect, zNear, zFar); + else GLM_IF_CONSTEXPR(GLM_CONFIG_CLIP_CONTROL == GLM_CLIP_CONTROL_LH_NO) + return perspectiveLH_NO(fovy, aspect, zNear, zFar); + else GLM_IF_CONSTEXPR(GLM_CONFIG_CLIP_CONTROL == GLM_CLIP_CONTROL_RH_ZO) + return perspectiveRH_ZO(fovy, aspect, zNear, zFar); + else GLM_IF_CONSTEXPR(GLM_CONFIG_CLIP_CONTROL == GLM_CLIP_CONTROL_RH_NO) + return perspectiveRH_NO(fovy, aspect, zNear, zFar); + } + + template + GLM_FUNC_QUALIFIER mat<4, 4, T, defaultp> perspectiveFovRH_ZO(T fov, T width, T height, T zNear, T zFar) + { + assert(width > static_cast(0)); + assert(height > static_cast(0)); + assert(fov > static_cast(0)); + + T const rad = fov; + T const h = glm::cos(static_cast(0.5) * rad) / glm::sin(static_cast(0.5) * rad); + T const w = h * height / width; ///todo max(width , Height) / min(width , Height)? + + mat<4, 4, T, defaultp> Result(static_cast(0)); + Result[0][0] = w; + Result[1][1] = h; + Result[2][2] = zFar / (zNear - zFar); + Result[2][3] = - static_cast(1); + Result[3][2] = -(zFar * zNear) / (zFar - zNear); + return Result; + } + + template + GLM_FUNC_QUALIFIER mat<4, 4, T, defaultp> perspectiveFovRH_NO(T fov, T width, T height, T zNear, T zFar) + { + assert(width > static_cast(0)); + assert(height > static_cast(0)); + assert(fov > static_cast(0)); + + T const rad = fov; + T const h = glm::cos(static_cast(0.5) * rad) / glm::sin(static_cast(0.5) * rad); + T const w = h * height / width; ///todo max(width , Height) / min(width , Height)? + + mat<4, 4, T, defaultp> Result(static_cast(0)); + Result[0][0] = w; + Result[1][1] = h; + Result[2][2] = - (zFar + zNear) / (zFar - zNear); + Result[2][3] = - static_cast(1); + Result[3][2] = - (static_cast(2) * zFar * zNear) / (zFar - zNear); + return Result; + } + + template + GLM_FUNC_QUALIFIER mat<4, 4, T, defaultp> perspectiveFovLH_ZO(T fov, T width, T height, T zNear, T zFar) + { + assert(width > static_cast(0)); + assert(height > static_cast(0)); + assert(fov > static_cast(0)); + + T const rad = fov; + T const h = glm::cos(static_cast(0.5) * rad) / glm::sin(static_cast(0.5) * rad); + T const w = h * height / width; ///todo max(width , Height) / min(width , Height)? + + mat<4, 4, T, defaultp> Result(static_cast(0)); + Result[0][0] = w; + Result[1][1] = h; + Result[2][2] = zFar / (zFar - zNear); + Result[2][3] = static_cast(1); + Result[3][2] = -(zFar * zNear) / (zFar - zNear); + return Result; + } + + template + GLM_FUNC_QUALIFIER mat<4, 4, T, defaultp> perspectiveFovLH_NO(T fov, T width, T height, T zNear, T zFar) + { + assert(width > static_cast(0)); + assert(height > static_cast(0)); + assert(fov > static_cast(0)); + + T const rad = fov; + T const h = glm::cos(static_cast(0.5) * rad) / glm::sin(static_cast(0.5) * rad); + T const w = h * height / width; ///todo max(width , Height) / min(width , Height)? + + mat<4, 4, T, defaultp> Result(static_cast(0)); + Result[0][0] = w; + Result[1][1] = h; + Result[2][2] = (zFar + zNear) / (zFar - zNear); + Result[2][3] = static_cast(1); + Result[3][2] = - (static_cast(2) * zFar * zNear) / (zFar - zNear); + return Result; + } + + template + GLM_FUNC_QUALIFIER mat<4, 4, T, defaultp> perspectiveFovZO(T fov, T width, T height, T zNear, T zFar) + { + if(GLM_CONFIG_CLIP_CONTROL & GLM_CLIP_CONTROL_LH_BIT) + return perspectiveFovLH_ZO(fov, width, height, zNear, zFar); + else + return perspectiveFovRH_ZO(fov, width, height, zNear, zFar); + } + + template + GLM_FUNC_QUALIFIER mat<4, 4, T, defaultp> perspectiveFovNO(T fov, T width, T height, T zNear, T zFar) + { + if(GLM_CONFIG_CLIP_CONTROL & GLM_CLIP_CONTROL_LH_BIT) + return perspectiveFovLH_NO(fov, width, height, zNear, zFar); + else + return perspectiveFovRH_NO(fov, width, height, zNear, zFar); + } + + template + GLM_FUNC_QUALIFIER mat<4, 4, T, defaultp> perspectiveFovLH(T fov, T width, T height, T zNear, T zFar) + { + if(GLM_CONFIG_CLIP_CONTROL & GLM_CLIP_CONTROL_ZO_BIT) + return perspectiveFovLH_ZO(fov, width, height, zNear, zFar); + else + return perspectiveFovLH_NO(fov, width, height, zNear, zFar); + } + + template + GLM_FUNC_QUALIFIER mat<4, 4, T, defaultp> perspectiveFovRH(T fov, T width, T height, T zNear, T zFar) + { + if(GLM_CONFIG_CLIP_CONTROL & GLM_CLIP_CONTROL_ZO_BIT) + return perspectiveFovRH_ZO(fov, width, height, zNear, zFar); + else + return perspectiveFovRH_NO(fov, width, height, zNear, zFar); + } + + template + GLM_FUNC_QUALIFIER mat<4, 4, T, defaultp> perspectiveFov(T fov, T width, T height, T zNear, T zFar) + { + if(GLM_CONFIG_CLIP_CONTROL == GLM_CLIP_CONTROL_LH_ZO) + return perspectiveFovLH_ZO(fov, width, height, zNear, zFar); + else if(GLM_CONFIG_CLIP_CONTROL == GLM_CLIP_CONTROL_LH_NO) + return perspectiveFovLH_NO(fov, width, height, zNear, zFar); + else if(GLM_CONFIG_CLIP_CONTROL == GLM_CLIP_CONTROL_RH_ZO) + return perspectiveFovRH_ZO(fov, width, height, zNear, zFar); + else if(GLM_CONFIG_CLIP_CONTROL == GLM_CLIP_CONTROL_RH_NO) + return perspectiveFovRH_NO(fov, width, height, zNear, zFar); + } + + template + GLM_FUNC_QUALIFIER mat<4, 4, T, defaultp> infinitePerspectiveRH(T fovy, T aspect, T zNear) + { + T const range = tan(fovy / static_cast(2)) * zNear; + T const left = -range * aspect; + T const right = range * aspect; + T const bottom = -range; + T const top = range; + + mat<4, 4, T, defaultp> Result(static_cast(0)); + Result[0][0] = (static_cast(2) * zNear) / (right - left); + Result[1][1] = (static_cast(2) * zNear) / (top - bottom); + Result[2][2] = - static_cast(1); + Result[2][3] = - static_cast(1); + Result[3][2] = - static_cast(2) * zNear; + return Result; + } + + template + GLM_FUNC_QUALIFIER mat<4, 4, T, defaultp> infinitePerspectiveLH(T fovy, T aspect, T zNear) + { + T const range = tan(fovy / static_cast(2)) * zNear; + T const left = -range * aspect; + T const right = range * aspect; + T const bottom = -range; + T const top = range; + + mat<4, 4, T, defaultp> Result(T(0)); + Result[0][0] = (static_cast(2) * zNear) / (right - left); + Result[1][1] = (static_cast(2) * zNear) / (top - bottom); + Result[2][2] = static_cast(1); + Result[2][3] = static_cast(1); + Result[3][2] = - static_cast(2) * zNear; + return Result; + } + + template + GLM_FUNC_QUALIFIER mat<4, 4, T, defaultp> infinitePerspective(T fovy, T aspect, T zNear) + { + if(GLM_CONFIG_CLIP_CONTROL & GLM_CLIP_CONTROL_LH_BIT) + return infinitePerspectiveLH(fovy, aspect, zNear); + else + return infinitePerspectiveRH(fovy, aspect, zNear); + } + + // Infinite projection matrix: http://www.terathon.com/gdc07_lengyel.pdf + template + GLM_FUNC_QUALIFIER mat<4, 4, T, defaultp> tweakedInfinitePerspective(T fovy, T aspect, T zNear, T ep) + { + T const range = tan(fovy / static_cast(2)) * zNear; + T const left = -range * aspect; + T const right = range * aspect; + T const bottom = -range; + T const top = range; + + mat<4, 4, T, defaultp> Result(static_cast(0)); + Result[0][0] = (static_cast(2) * zNear) / (right - left); + Result[1][1] = (static_cast(2) * zNear) / (top - bottom); + Result[2][2] = ep - static_cast(1); + Result[2][3] = static_cast(-1); + Result[3][2] = (ep - static_cast(2)) * zNear; + return Result; + } + + template + GLM_FUNC_QUALIFIER mat<4, 4, T, defaultp> tweakedInfinitePerspective(T fovy, T aspect, T zNear) + { + return tweakedInfinitePerspective(fovy, aspect, zNear, epsilon()); + } +}//namespace glm diff --git a/src/GLMath/glm/ext/matrix_common.hpp b/src/GLMath/glm/ext/matrix_common.hpp new file mode 100644 index 000000000..05c37991c --- /dev/null +++ b/src/GLMath/glm/ext/matrix_common.hpp @@ -0,0 +1,36 @@ +/// @ref ext_matrix_common +/// @file glm/ext/matrix_common.hpp +/// +/// @defgroup ext_matrix_common GLM_EXT_matrix_common +/// @ingroup ext +/// +/// Defines functions for common matrix operations. +/// +/// Include to use the features of this extension. +/// +/// @see ext_matrix_common + +#pragma once + +#include "../detail/qualifier.hpp" +#include "../detail/_fixes.hpp" + +#if GLM_MESSAGES == GLM_ENABLE && !defined(GLM_EXT_INCLUDED) +# pragma message("GLM: GLM_EXT_matrix_transform extension included") +#endif + +namespace glm +{ + /// @addtogroup ext_matrix_common + /// @{ + + template + GLM_FUNC_DECL mat mix(mat const& x, mat const& y, mat const& a); + + template + GLM_FUNC_DECL mat mix(mat const& x, mat const& y, U a); + + /// @} +}//namespace glm + +#include "matrix_common.inl" diff --git a/src/GLMath/glm/ext/matrix_common.inl b/src/GLMath/glm/ext/matrix_common.inl new file mode 100644 index 000000000..9d508485b --- /dev/null +++ b/src/GLMath/glm/ext/matrix_common.inl @@ -0,0 +1,16 @@ +#include "../matrix.hpp" + +namespace glm +{ + template + GLM_FUNC_QUALIFIER mat mix(mat const& x, mat const& y, U a) + { + return mat(x) * (static_cast(1) - a) + mat(y) * a; + } + + template + GLM_FUNC_QUALIFIER mat mix(mat const& x, mat const& y, mat const& a) + { + return matrixCompMult(mat(x), static_cast(1) - a) + matrixCompMult(mat(y), a); + } +}//namespace glm diff --git a/src/GLMath/glm/ext/matrix_double2x2.hpp b/src/GLMath/glm/ext/matrix_double2x2.hpp new file mode 100644 index 000000000..94dca54b5 --- /dev/null +++ b/src/GLMath/glm/ext/matrix_double2x2.hpp @@ -0,0 +1,23 @@ +/// @ref core +/// @file glm/ext/matrix_double2x2.hpp + +#pragma once +#include "../detail/type_mat2x2.hpp" + +namespace glm +{ + /// @addtogroup core_matrix + /// @{ + + /// 2 columns of 2 components matrix of double-precision floating-point numbers. + /// + /// @see GLSL 4.20.8 specification, section 4.1.6 Matrices + typedef mat<2, 2, double, defaultp> dmat2x2; + + /// 2 columns of 2 components matrix of double-precision floating-point numbers. + /// + /// @see GLSL 4.20.8 specification, section 4.1.6 Matrices + typedef mat<2, 2, double, defaultp> dmat2; + + /// @} +}//namespace glm diff --git a/src/GLMath/glm/ext/matrix_double2x2_precision.hpp b/src/GLMath/glm/ext/matrix_double2x2_precision.hpp new file mode 100644 index 000000000..9e2c174e4 --- /dev/null +++ b/src/GLMath/glm/ext/matrix_double2x2_precision.hpp @@ -0,0 +1,49 @@ +/// @ref core +/// @file glm/ext/matrix_double2x2_precision.hpp + +#pragma once +#include "../detail/type_mat2x2.hpp" + +namespace glm +{ + /// @addtogroup core_matrix_precision + /// @{ + + /// 2 columns of 2 components matrix of double-precision floating-point numbers using low precision arithmetic in term of ULPs. + /// + /// @see GLSL 4.20.8 specification, section 4.1.6 Matrices + /// @see GLSL 4.20.8 specification, section 4.7.2 Precision Qualifier + typedef mat<2, 2, double, lowp> lowp_dmat2; + + /// 2 columns of 2 components matrix of double-precision floating-point numbers using medium precision arithmetic in term of ULPs. + /// + /// @see GLSL 4.20.8 specification, section 4.1.6 Matrices + /// @see GLSL 4.20.8 specification, section 4.7.2 Precision Qualifier + typedef mat<2, 2, double, mediump> mediump_dmat2; + + /// 2 columns of 2 components matrix of double-precision floating-point numbers using medium precision arithmetic in term of ULPs. + /// + /// @see GLSL 4.20.8 specification, section 4.1.6 Matrices + /// @see GLSL 4.20.8 specification, section 4.7.2 Precision Qualifier + typedef mat<2, 2, double, highp> highp_dmat2; + + /// 2 columns of 2 components matrix of double-precision floating-point numbers using low precision arithmetic in term of ULPs. + /// + /// @see GLSL 4.20.8 specification, section 4.1.6 Matrices + /// @see GLSL 4.20.8 specification, section 4.7.2 Precision Qualifier + typedef mat<2, 2, double, lowp> lowp_dmat2x2; + + /// 2 columns of 2 components matrix of double-precision floating-point numbers using medium precision arithmetic in term of ULPs. + /// + /// @see GLSL 4.20.8 specification, section 4.1.6 Matrices + /// @see GLSL 4.20.8 specification, section 4.7.2 Precision Qualifier + typedef mat<2, 2, double, mediump> mediump_dmat2x2; + + /// 2 columns of 2 components matrix of double-precision floating-point numbers using medium precision arithmetic in term of ULPs. + /// + /// @see GLSL 4.20.8 specification, section 4.1.6 Matrices + /// @see GLSL 4.20.8 specification, section 4.7.2 Precision Qualifier + typedef mat<2, 2, double, highp> highp_dmat2x2; + + /// @} +}//namespace glm diff --git a/src/GLMath/glm/ext/matrix_double2x3.hpp b/src/GLMath/glm/ext/matrix_double2x3.hpp new file mode 100644 index 000000000..bfef87a66 --- /dev/null +++ b/src/GLMath/glm/ext/matrix_double2x3.hpp @@ -0,0 +1,18 @@ +/// @ref core +/// @file glm/ext/matrix_double2x3.hpp + +#pragma once +#include "../detail/type_mat2x3.hpp" + +namespace glm +{ + /// @addtogroup core_matrix + /// @{ + + /// 2 columns of 3 components matrix of double-precision floating-point numbers. + /// + /// @see GLSL 4.20.8 specification, section 4.1.6 Matrices + typedef mat<2, 3, double, defaultp> dmat2x3; + + /// @} +}//namespace glm diff --git a/src/GLMath/glm/ext/matrix_double2x3_precision.hpp b/src/GLMath/glm/ext/matrix_double2x3_precision.hpp new file mode 100644 index 000000000..098fb6046 --- /dev/null +++ b/src/GLMath/glm/ext/matrix_double2x3_precision.hpp @@ -0,0 +1,31 @@ +/// @ref core +/// @file glm/ext/matrix_double2x3_precision.hpp + +#pragma once +#include "../detail/type_mat2x3.hpp" + +namespace glm +{ + /// @addtogroup core_matrix_precision + /// @{ + + /// 2 columns of 3 components matrix of double-precision floating-point numbers using low precision arithmetic in term of ULPs. + /// + /// @see GLSL 4.20.8 specification, section 4.1.6 Matrices + /// @see GLSL 4.20.8 specification, section 4.7.2 Precision Qualifier + typedef mat<2, 3, double, lowp> lowp_dmat2x3; + + /// 2 columns of 3 components matrix of double-precision floating-point numbers using medium precision arithmetic in term of ULPs. + /// + /// @see GLSL 4.20.8 specification, section 4.1.6 Matrices + /// @see GLSL 4.20.8 specification, section 4.7.2 Precision Qualifier + typedef mat<2, 3, double, mediump> mediump_dmat2x3; + + /// 2 columns of 3 components matrix of double-precision floating-point numbers using medium precision arithmetic in term of ULPs. + /// + /// @see GLSL 4.20.8 specification, section 4.1.6 Matrices + /// @see GLSL 4.20.8 specification, section 4.7.2 Precision Qualifier + typedef mat<2, 3, double, highp> highp_dmat2x3; + + /// @} +}//namespace glm diff --git a/src/GLMath/glm/ext/matrix_double2x4.hpp b/src/GLMath/glm/ext/matrix_double2x4.hpp new file mode 100644 index 000000000..499284bce --- /dev/null +++ b/src/GLMath/glm/ext/matrix_double2x4.hpp @@ -0,0 +1,18 @@ +/// @ref core +/// @file glm/ext/matrix_double2x4.hpp + +#pragma once +#include "../detail/type_mat2x4.hpp" + +namespace glm +{ + /// @addtogroup core_matrix + /// @{ + + /// 2 columns of 4 components matrix of double-precision floating-point numbers. + /// + /// @see GLSL 4.20.8 specification, section 4.1.6 Matrices + typedef mat<2, 4, double, defaultp> dmat2x4; + + /// @} +}//namespace glm diff --git a/src/GLMath/glm/ext/matrix_double2x4_precision.hpp b/src/GLMath/glm/ext/matrix_double2x4_precision.hpp new file mode 100644 index 000000000..9b61ebcee --- /dev/null +++ b/src/GLMath/glm/ext/matrix_double2x4_precision.hpp @@ -0,0 +1,31 @@ +/// @ref core +/// @file glm/ext/matrix_double2x4_precision.hpp + +#pragma once +#include "../detail/type_mat2x4.hpp" + +namespace glm +{ + /// @addtogroup core_matrix_precision + /// @{ + + /// 2 columns of 4 components matrix of double-precision floating-point numbers using low precision arithmetic in term of ULPs. + /// + /// @see GLSL 4.20.8 specification, section 4.1.6 Matrices + /// @see GLSL 4.20.8 specification, section 4.7.2 Precision Qualifier + typedef mat<2, 4, double, lowp> lowp_dmat2x4; + + /// 2 columns of 4 components matrix of double-precision floating-point numbers using medium precision arithmetic in term of ULPs. + /// + /// @see GLSL 4.20.8 specification, section 4.1.6 Matrices + /// @see GLSL 4.20.8 specification, section 4.7.2 Precision Qualifier + typedef mat<2, 4, double, mediump> mediump_dmat2x4; + + /// 2 columns of 4 components matrix of double-precision floating-point numbers using medium precision arithmetic in term of ULPs. + /// + /// @see GLSL 4.20.8 specification, section 4.1.6 Matrices + /// @see GLSL 4.20.8 specification, section 4.7.2 Precision Qualifier + typedef mat<2, 4, double, highp> highp_dmat2x4; + + /// @} +}//namespace glm diff --git a/src/GLMath/glm/ext/matrix_double3x2.hpp b/src/GLMath/glm/ext/matrix_double3x2.hpp new file mode 100644 index 000000000..dd23f36cd --- /dev/null +++ b/src/GLMath/glm/ext/matrix_double3x2.hpp @@ -0,0 +1,18 @@ +/// @ref core +/// @file glm/ext/matrix_double3x2.hpp + +#pragma once +#include "../detail/type_mat3x2.hpp" + +namespace glm +{ + /// @addtogroup core_matrix + /// @{ + + /// 3 columns of 2 components matrix of double-precision floating-point numbers. + /// + /// @see GLSL 4.20.8 specification, section 4.1.6 Matrices + typedef mat<3, 2, double, defaultp> dmat3x2; + + /// @} +}//namespace glm diff --git a/src/GLMath/glm/ext/matrix_double3x2_precision.hpp b/src/GLMath/glm/ext/matrix_double3x2_precision.hpp new file mode 100644 index 000000000..068d9e911 --- /dev/null +++ b/src/GLMath/glm/ext/matrix_double3x2_precision.hpp @@ -0,0 +1,31 @@ +/// @ref core +/// @file glm/ext/matrix_double3x2_precision.hpp + +#pragma once +#include "../detail/type_mat3x2.hpp" + +namespace glm +{ + /// @addtogroup core_matrix_precision + /// @{ + + /// 3 columns of 2 components matrix of double-precision floating-point numbers using low precision arithmetic in term of ULPs. + /// + /// @see GLSL 4.20.8 specification, section 4.1.6 Matrices + /// @see GLSL 4.20.8 specification, section 4.7.2 Precision Qualifier + typedef mat<3, 2, double, lowp> lowp_dmat3x2; + + /// 3 columns of 2 components matrix of double-precision floating-point numbers using medium precision arithmetic in term of ULPs. + /// + /// @see GLSL 4.20.8 specification, section 4.1.6 Matrices + /// @see GLSL 4.20.8 specification, section 4.7.2 Precision Qualifier + typedef mat<3, 2, double, mediump> mediump_dmat3x2; + + /// 3 columns of 2 components matrix of double-precision floating-point numbers using medium precision arithmetic in term of ULPs. + /// + /// @see GLSL 4.20.8 specification, section 4.1.6 Matrices + /// @see GLSL 4.20.8 specification, section 4.7.2 Precision Qualifier + typedef mat<3, 2, double, highp> highp_dmat3x2; + + /// @} +}//namespace glm diff --git a/src/GLMath/glm/ext/matrix_double3x3.hpp b/src/GLMath/glm/ext/matrix_double3x3.hpp new file mode 100644 index 000000000..53572b735 --- /dev/null +++ b/src/GLMath/glm/ext/matrix_double3x3.hpp @@ -0,0 +1,23 @@ +/// @ref core +/// @file glm/ext/matrix_double3x3.hpp + +#pragma once +#include "../detail/type_mat3x3.hpp" + +namespace glm +{ + /// @addtogroup core_matrix + /// @{ + + /// 3 columns of 3 components matrix of double-precision floating-point numbers. + /// + /// @see GLSL 4.20.8 specification, section 4.1.6 Matrices + typedef mat<3, 3, double, defaultp> dmat3x3; + + /// 3 columns of 3 components matrix of double-precision floating-point numbers. + /// + /// @see GLSL 4.20.8 specification, section 4.1.6 Matrices + typedef mat<3, 3, double, defaultp> dmat3; + + /// @} +}//namespace glm diff --git a/src/GLMath/glm/ext/matrix_double3x3_precision.hpp b/src/GLMath/glm/ext/matrix_double3x3_precision.hpp new file mode 100644 index 000000000..8691e7808 --- /dev/null +++ b/src/GLMath/glm/ext/matrix_double3x3_precision.hpp @@ -0,0 +1,49 @@ +/// @ref core +/// @file glm/ext/matrix_double3x3_precision.hpp + +#pragma once +#include "../detail/type_mat3x3.hpp" + +namespace glm +{ + /// @addtogroup core_matrix_precision + /// @{ + + /// 3 columns of 3 components matrix of double-precision floating-point numbers using low precision arithmetic in term of ULPs. + /// + /// @see GLSL 4.20.8 specification, section 4.1.6 Matrices + /// @see GLSL 4.20.8 specification, section 4.7.2 Precision Qualifier + typedef mat<3, 3, double, lowp> lowp_dmat3; + + /// 3 columns of 3 components matrix of double-precision floating-point numbers using medium precision arithmetic in term of ULPs. + /// + /// @see GLSL 4.20.8 specification, section 4.1.6 Matrices + /// @see GLSL 4.20.8 specification, section 4.7.2 Precision Qualifier + typedef mat<3, 3, double, mediump> mediump_dmat3; + + /// 3 columns of 3 components matrix of double-precision floating-point numbers using medium precision arithmetic in term of ULPs. + /// + /// @see GLSL 4.20.8 specification, section 4.1.6 Matrices + /// @see GLSL 4.20.8 specification, section 4.7.2 Precision Qualifier + typedef mat<3, 3, double, highp> highp_dmat3; + + /// 3 columns of 3 components matrix of double-precision floating-point numbers using low precision arithmetic in term of ULPs. + /// + /// @see GLSL 4.20.8 specification, section 4.1.6 Matrices + /// @see GLSL 4.20.8 specification, section 4.7.2 Precision Qualifier + typedef mat<3, 3, double, lowp> lowp_dmat3x3; + + /// 3 columns of 3 components matrix of double-precision floating-point numbers using medium precision arithmetic in term of ULPs. + /// + /// @see GLSL 4.20.8 specification, section 4.1.6 Matrices + /// @see GLSL 4.20.8 specification, section 4.7.2 Precision Qualifier + typedef mat<3, 3, double, mediump> mediump_dmat3x3; + + /// 3 columns of 3 components matrix of double-precision floating-point numbers using medium precision arithmetic in term of ULPs. + /// + /// @see GLSL 4.20.8 specification, section 4.1.6 Matrices + /// @see GLSL 4.20.8 specification, section 4.7.2 Precision Qualifier + typedef mat<3, 3, double, highp> highp_dmat3x3; + + /// @} +}//namespace glm diff --git a/src/GLMath/glm/ext/matrix_double3x4.hpp b/src/GLMath/glm/ext/matrix_double3x4.hpp new file mode 100644 index 000000000..c572d637c --- /dev/null +++ b/src/GLMath/glm/ext/matrix_double3x4.hpp @@ -0,0 +1,18 @@ +/// @ref core +/// @file glm/ext/matrix_double3x4.hpp + +#pragma once +#include "../detail/type_mat3x4.hpp" + +namespace glm +{ + /// @addtogroup core_matrix + /// @{ + + /// 3 columns of 4 components matrix of double-precision floating-point numbers. + /// + /// @see GLSL 4.20.8 specification, section 4.1.6 Matrices + typedef mat<3, 4, double, defaultp> dmat3x4; + + /// @} +}//namespace glm diff --git a/src/GLMath/glm/ext/matrix_double3x4_precision.hpp b/src/GLMath/glm/ext/matrix_double3x4_precision.hpp new file mode 100644 index 000000000..f040217e7 --- /dev/null +++ b/src/GLMath/glm/ext/matrix_double3x4_precision.hpp @@ -0,0 +1,31 @@ +/// @ref core +/// @file glm/ext/matrix_double3x4_precision.hpp + +#pragma once +#include "../detail/type_mat3x4.hpp" + +namespace glm +{ + /// @addtogroup core_matrix_precision + /// @{ + + /// 3 columns of 4 components matrix of double-precision floating-point numbers using low precision arithmetic in term of ULPs. + /// + /// @see GLSL 4.20.8 specification, section 4.1.6 Matrices + /// @see GLSL 4.20.8 specification, section 4.7.2 Precision Qualifier + typedef mat<3, 4, double, lowp> lowp_dmat3x4; + + /// 3 columns of 4 components matrix of double-precision floating-point numbers using medium precision arithmetic in term of ULPs. + /// + /// @see GLSL 4.20.8 specification, section 4.1.6 Matrices + /// @see GLSL 4.20.8 specification, section 4.7.2 Precision Qualifier + typedef mat<3, 4, double, mediump> mediump_dmat3x4; + + /// 3 columns of 4 components matrix of double-precision floating-point numbers using medium precision arithmetic in term of ULPs. + /// + /// @see GLSL 4.20.8 specification, section 4.1.6 Matrices + /// @see GLSL 4.20.8 specification, section 4.7.2 Precision Qualifier + typedef mat<3, 4, double, highp> highp_dmat3x4; + + /// @} +}//namespace glm diff --git a/src/GLMath/glm/ext/matrix_double4x2.hpp b/src/GLMath/glm/ext/matrix_double4x2.hpp new file mode 100644 index 000000000..9b229f471 --- /dev/null +++ b/src/GLMath/glm/ext/matrix_double4x2.hpp @@ -0,0 +1,18 @@ +/// @ref core +/// @file glm/ext/matrix_double4x2.hpp + +#pragma once +#include "../detail/type_mat4x2.hpp" + +namespace glm +{ + /// @addtogroup core_matrix + /// @{ + + /// 4 columns of 2 components matrix of double-precision floating-point numbers. + /// + /// @see GLSL 4.20.8 specification, section 4.1.6 Matrices + typedef mat<4, 2, double, defaultp> dmat4x2; + + /// @} +}//namespace glm diff --git a/src/GLMath/glm/ext/matrix_double4x2_precision.hpp b/src/GLMath/glm/ext/matrix_double4x2_precision.hpp new file mode 100644 index 000000000..6ad18ba9e --- /dev/null +++ b/src/GLMath/glm/ext/matrix_double4x2_precision.hpp @@ -0,0 +1,31 @@ +/// @ref core +/// @file glm/ext/matrix_double4x2_precision.hpp + +#pragma once +#include "../detail/type_mat4x2.hpp" + +namespace glm +{ + /// @addtogroup core_matrix_precision + /// @{ + + /// 4 columns of 2 components matrix of double-precision floating-point numbers using low precision arithmetic in term of ULPs. + /// + /// @see GLSL 4.20.8 specification, section 4.1.6 Matrices + /// @see GLSL 4.20.8 specification, section 4.7.2 Precision Qualifier + typedef mat<4, 2, double, lowp> lowp_dmat4x2; + + /// 4 columns of 2 components matrix of double-precision floating-point numbers using medium precision arithmetic in term of ULPs. + /// + /// @see GLSL 4.20.8 specification, section 4.1.6 Matrices + /// @see GLSL 4.20.8 specification, section 4.7.2 Precision Qualifier + typedef mat<4, 2, double, mediump> mediump_dmat4x2; + + /// 4 columns of 2 components matrix of double-precision floating-point numbers using medium precision arithmetic in term of ULPs. + /// + /// @see GLSL 4.20.8 specification, section 4.1.6 Matrices + /// @see GLSL 4.20.8 specification, section 4.7.2 Precision Qualifier + typedef mat<4, 2, double, highp> highp_dmat4x2; + + /// @} +}//namespace glm diff --git a/src/GLMath/glm/ext/matrix_double4x3.hpp b/src/GLMath/glm/ext/matrix_double4x3.hpp new file mode 100644 index 000000000..dca4cf956 --- /dev/null +++ b/src/GLMath/glm/ext/matrix_double4x3.hpp @@ -0,0 +1,18 @@ +/// @ref core +/// @file glm/ext/matrix_double4x3.hpp + +#pragma once +#include "../detail/type_mat4x3.hpp" + +namespace glm +{ + /// @addtogroup core_matrix + /// @{ + + /// 4 columns of 3 components matrix of double-precision floating-point numbers. + /// + /// @see GLSL 4.20.8 specification, section 4.1.6 Matrices + typedef mat<4, 3, double, defaultp> dmat4x3; + + /// @} +}//namespace glm diff --git a/src/GLMath/glm/ext/matrix_double4x3_precision.hpp b/src/GLMath/glm/ext/matrix_double4x3_precision.hpp new file mode 100644 index 000000000..f7371de84 --- /dev/null +++ b/src/GLMath/glm/ext/matrix_double4x3_precision.hpp @@ -0,0 +1,31 @@ +/// @ref core +/// @file glm/ext/matrix_double4x3_precision.hpp + +#pragma once +#include "../detail/type_mat4x3.hpp" + +namespace glm +{ + /// @addtogroup core_matrix_precision + /// @{ + + /// 4 columns of 3 components matrix of double-precision floating-point numbers using low precision arithmetic in term of ULPs. + /// + /// @see GLSL 4.20.8 specification, section 4.1.6 Matrices + /// @see GLSL 4.20.8 specification, section 4.7.2 Precision Qualifier + typedef mat<4, 3, double, lowp> lowp_dmat4x3; + + /// 4 columns of 3 components matrix of double-precision floating-point numbers using medium precision arithmetic in term of ULPs. + /// + /// @see GLSL 4.20.8 specification, section 4.1.6 Matrices + /// @see GLSL 4.20.8 specification, section 4.7.2 Precision Qualifier + typedef mat<4, 3, double, mediump> mediump_dmat4x3; + + /// 4 columns of 3 components matrix of double-precision floating-point numbers using medium precision arithmetic in term of ULPs. + /// + /// @see GLSL 4.20.8 specification, section 4.1.6 Matrices + /// @see GLSL 4.20.8 specification, section 4.7.2 Precision Qualifier + typedef mat<4, 3, double, highp> highp_dmat4x3; + + /// @} +}//namespace glm diff --git a/src/GLMath/glm/ext/matrix_double4x4.hpp b/src/GLMath/glm/ext/matrix_double4x4.hpp new file mode 100644 index 000000000..81e1bf65c --- /dev/null +++ b/src/GLMath/glm/ext/matrix_double4x4.hpp @@ -0,0 +1,23 @@ +/// @ref core +/// @file glm/ext/matrix_double4x4.hpp + +#pragma once +#include "../detail/type_mat4x4.hpp" + +namespace glm +{ + /// @addtogroup core_matrix + /// @{ + + /// 4 columns of 4 components matrix of double-precision floating-point numbers. + /// + /// @see GLSL 4.20.8 specification, section 4.1.6 Matrices + typedef mat<4, 4, double, defaultp> dmat4x4; + + /// 4 columns of 4 components matrix of double-precision floating-point numbers. + /// + /// @see GLSL 4.20.8 specification, section 4.1.6 Matrices + typedef mat<4, 4, double, defaultp> dmat4; + + /// @} +}//namespace glm diff --git a/src/GLMath/glm/ext/matrix_double4x4_precision.hpp b/src/GLMath/glm/ext/matrix_double4x4_precision.hpp new file mode 100644 index 000000000..4c36a8486 --- /dev/null +++ b/src/GLMath/glm/ext/matrix_double4x4_precision.hpp @@ -0,0 +1,49 @@ +/// @ref core +/// @file glm/ext/matrix_double4x4_precision.hpp + +#pragma once +#include "../detail/type_mat4x4.hpp" + +namespace glm +{ + /// @addtogroup core_matrix_precision + /// @{ + + /// 4 columns of 4 components matrix of double-precision floating-point numbers using low precision arithmetic in term of ULPs. + /// + /// @see GLSL 4.20.8 specification, section 4.1.6 Matrices + /// @see GLSL 4.20.8 specification, section 4.7.2 Precision Qualifier + typedef mat<4, 4, double, lowp> lowp_dmat4; + + /// 4 columns of 4 components matrix of double-precision floating-point numbers using medium precision arithmetic in term of ULPs. + /// + /// @see GLSL 4.20.8 specification, section 4.1.6 Matrices + /// @see GLSL 4.20.8 specification, section 4.7.2 Precision Qualifier + typedef mat<4, 4, double, mediump> mediump_dmat4; + + /// 4 columns of 4 components matrix of double-precision floating-point numbers using medium precision arithmetic in term of ULPs. + /// + /// @see GLSL 4.20.8 specification, section 4.1.6 Matrices + /// @see GLSL 4.20.8 specification, section 4.7.2 Precision Qualifier + typedef mat<4, 4, double, highp> highp_dmat4; + + /// 4 columns of 4 components matrix of double-precision floating-point numbers using low precision arithmetic in term of ULPs. + /// + /// @see GLSL 4.20.8 specification, section 4.1.6 Matrices + /// @see GLSL 4.20.8 specification, section 4.7.2 Precision Qualifier + typedef mat<4, 4, double, lowp> lowp_dmat4x4; + + /// 4 columns of 4 components matrix of double-precision floating-point numbers using medium precision arithmetic in term of ULPs. + /// + /// @see GLSL 4.20.8 specification, section 4.1.6 Matrices + /// @see GLSL 4.20.8 specification, section 4.7.2 Precision Qualifier + typedef mat<4, 4, double, mediump> mediump_dmat4x4; + + /// 4 columns of 4 components matrix of double-precision floating-point numbers using medium precision arithmetic in term of ULPs. + /// + /// @see GLSL 4.20.8 specification, section 4.1.6 Matrices + /// @see GLSL 4.20.8 specification, section 4.7.2 Precision Qualifier + typedef mat<4, 4, double, highp> highp_dmat4x4; + + /// @} +}//namespace glm diff --git a/src/GLMath/glm/ext/matrix_float2x2.hpp b/src/GLMath/glm/ext/matrix_float2x2.hpp new file mode 100644 index 000000000..53df921fe --- /dev/null +++ b/src/GLMath/glm/ext/matrix_float2x2.hpp @@ -0,0 +1,23 @@ +/// @ref core +/// @file glm/ext/matrix_float2x2.hpp + +#pragma once +#include "../detail/type_mat2x2.hpp" + +namespace glm +{ + /// @addtogroup core_matrix + /// @{ + + /// 2 columns of 2 components matrix of single-precision floating-point numbers. + /// + /// @see GLSL 4.20.8 specification, section 4.1.6 Matrices + typedef mat<2, 2, float, defaultp> mat2x2; + + /// 2 columns of 2 components matrix of single-precision floating-point numbers. + /// + /// @see GLSL 4.20.8 specification, section 4.1.6 Matrices + typedef mat<2, 2, float, defaultp> mat2; + + /// @} +}//namespace glm diff --git a/src/GLMath/glm/ext/matrix_float2x2_precision.hpp b/src/GLMath/glm/ext/matrix_float2x2_precision.hpp new file mode 100644 index 000000000..898b6db71 --- /dev/null +++ b/src/GLMath/glm/ext/matrix_float2x2_precision.hpp @@ -0,0 +1,49 @@ +/// @ref core +/// @file glm/ext/matrix_float2x2_precision.hpp + +#pragma once +#include "../detail/type_mat2x2.hpp" + +namespace glm +{ + /// @addtogroup core_matrix_precision + /// @{ + + /// 2 columns of 2 components matrix of single-precision floating-point numbers using low precision arithmetic in term of ULPs. + /// + /// @see GLSL 4.20.8 specification, section 4.1.6 Matrices + /// @see GLSL 4.20.8 specification, section 4.7.2 Precision Qualifier + typedef mat<2, 2, float, lowp> lowp_mat2; + + /// 2 columns of 2 components matrix of single-precision floating-point numbers using medium precision arithmetic in term of ULPs. + /// + /// @see GLSL 4.20.8 specification, section 4.1.6 Matrices + /// @see GLSL 4.20.8 specification, section 4.7.2 Precision Qualifier + typedef mat<2, 2, float, mediump> mediump_mat2; + + /// 2 columns of 2 components matrix of single-precision floating-point numbers using high precision arithmetic in term of ULPs. + /// + /// @see GLSL 4.20.8 specification, section 4.1.6 Matrices + /// @see GLSL 4.20.8 specification, section 4.7.2 Precision Qualifier + typedef mat<2, 2, float, highp> highp_mat2; + + /// 2 columns of 2 components matrix of single-precision floating-point numbers using low precision arithmetic in term of ULPs. + /// + /// @see GLSL 4.20.8 specification, section 4.1.6 Matrices + /// @see GLSL 4.20.8 specification, section 4.7.2 Precision Qualifier + typedef mat<2, 2, float, lowp> lowp_mat2x2; + + /// 2 columns of 2 components matrix of single-precision floating-point numbers using medium precision arithmetic in term of ULPs. + /// + /// @see GLSL 4.20.8 specification, section 4.1.6 Matrices + /// @see GLSL 4.20.8 specification, section 4.7.2 Precision Qualifier + typedef mat<2, 2, float, mediump> mediump_mat2x2; + + /// 2 columns of 2 components matrix of single-precision floating-point numbers using high precision arithmetic in term of ULPs. + /// + /// @see GLSL 4.20.8 specification, section 4.1.6 Matrices + /// @see GLSL 4.20.8 specification, section 4.7.2 Precision Qualifier + typedef mat<2, 2, float, highp> highp_mat2x2; + + /// @} +}//namespace glm diff --git a/src/GLMath/glm/ext/matrix_float2x3.hpp b/src/GLMath/glm/ext/matrix_float2x3.hpp new file mode 100644 index 000000000..6f68822db --- /dev/null +++ b/src/GLMath/glm/ext/matrix_float2x3.hpp @@ -0,0 +1,18 @@ +/// @ref core +/// @file glm/ext/matrix_float2x3.hpp + +#pragma once +#include "../detail/type_mat2x3.hpp" + +namespace glm +{ + /// @addtogroup core_matrix + /// @{ + + /// 2 columns of 3 components matrix of single-precision floating-point numbers. + /// + /// @see GLSL 4.20.8 specification, section 4.1.6 Matrices + typedef mat<2, 3, float, defaultp> mat2x3; + + /// @} +}//namespace glm diff --git a/src/GLMath/glm/ext/matrix_float2x3_precision.hpp b/src/GLMath/glm/ext/matrix_float2x3_precision.hpp new file mode 100644 index 000000000..50c103245 --- /dev/null +++ b/src/GLMath/glm/ext/matrix_float2x3_precision.hpp @@ -0,0 +1,31 @@ +/// @ref core +/// @file glm/ext/matrix_float2x3_precision.hpp + +#pragma once +#include "../detail/type_mat2x3.hpp" + +namespace glm +{ + /// @addtogroup core_matrix_precision + /// @{ + + /// 2 columns of 3 components matrix of single-precision floating-point numbers using low precision arithmetic in term of ULPs. + /// + /// @see GLSL 4.20.8 specification, section 4.1.6 Matrices + /// @see GLSL 4.20.8 specification, section 4.7.2 Precision Qualifier + typedef mat<2, 3, float, lowp> lowp_mat2x3; + + /// 2 columns of 3 components matrix of single-precision floating-point numbers using medium precision arithmetic in term of ULPs. + /// + /// @see GLSL 4.20.8 specification, section 4.1.6 Matrices + /// @see GLSL 4.20.8 specification, section 4.7.2 Precision Qualifier + typedef mat<2, 3, float, mediump> mediump_mat2x3; + + /// 2 columns of 3 components matrix of single-precision floating-point numbers using high precision arithmetic in term of ULPs. + /// + /// @see GLSL 4.20.8 specification, section 4.1.6 Matrices + /// @see GLSL 4.20.8 specification, section 4.7.2 Precision Qualifier + typedef mat<2, 3, float, highp> highp_mat2x3; + + /// @} +}//namespace glm diff --git a/src/GLMath/glm/ext/matrix_float2x4.hpp b/src/GLMath/glm/ext/matrix_float2x4.hpp new file mode 100644 index 000000000..30f30de3c --- /dev/null +++ b/src/GLMath/glm/ext/matrix_float2x4.hpp @@ -0,0 +1,18 @@ +/// @ref core +/// @file glm/ext/matrix_float2x4.hpp + +#pragma once +#include "../detail/type_mat2x4.hpp" + +namespace glm +{ + /// @addtogroup core_matrix + /// @{ + + /// 2 columns of 4 components matrix of single-precision floating-point numbers. + /// + /// @see GLSL 4.20.8 specification, section 4.1.6 Matrices + typedef mat<2, 4, float, defaultp> mat2x4; + + /// @} +}//namespace glm diff --git a/src/GLMath/glm/ext/matrix_float2x4_precision.hpp b/src/GLMath/glm/ext/matrix_float2x4_precision.hpp new file mode 100644 index 000000000..079d63828 --- /dev/null +++ b/src/GLMath/glm/ext/matrix_float2x4_precision.hpp @@ -0,0 +1,31 @@ +/// @ref core +/// @file glm/ext/matrix_float2x4_precision.hpp + +#pragma once +#include "../detail/type_mat2x4.hpp" + +namespace glm +{ + /// @addtogroup core_matrix_precision + /// @{ + + /// 2 columns of 4 components matrix of single-precision floating-point numbers using low precision arithmetic in term of ULPs. + /// + /// @see GLSL 4.20.8 specification, section 4.1.6 Matrices + /// @see GLSL 4.20.8 specification, section 4.7.2 Precision Qualifier + typedef mat<2, 4, float, lowp> lowp_mat2x4; + + /// 2 columns of 4 components matrix of single-precision floating-point numbers using medium precision arithmetic in term of ULPs. + /// + /// @see GLSL 4.20.8 specification, section 4.1.6 Matrices + /// @see GLSL 4.20.8 specification, section 4.7.2 Precision Qualifier + typedef mat<2, 4, float, mediump> mediump_mat2x4; + + /// 2 columns of 4 components matrix of single-precision floating-point numbers using high precision arithmetic in term of ULPs. + /// + /// @see GLSL 4.20.8 specification, section 4.1.6 Matrices + /// @see GLSL 4.20.8 specification, section 4.7.2 Precision Qualifier + typedef mat<2, 4, float, highp> highp_mat2x4; + + /// @} +}//namespace glm diff --git a/src/GLMath/glm/ext/matrix_float3x2.hpp b/src/GLMath/glm/ext/matrix_float3x2.hpp new file mode 100644 index 000000000..d39dd2fed --- /dev/null +++ b/src/GLMath/glm/ext/matrix_float3x2.hpp @@ -0,0 +1,18 @@ +/// @ref core +/// @file glm/ext/matrix_float3x2.hpp + +#pragma once +#include "../detail/type_mat3x2.hpp" + +namespace glm +{ + /// @addtogroup core + /// @{ + + /// 3 columns of 2 components matrix of single-precision floating-point numbers. + /// + /// @see GLSL 4.20.8 specification, section 4.1.6 Matrices + typedef mat<3, 2, float, defaultp> mat3x2; + + /// @} +}//namespace glm diff --git a/src/GLMath/glm/ext/matrix_float3x2_precision.hpp b/src/GLMath/glm/ext/matrix_float3x2_precision.hpp new file mode 100644 index 000000000..8572c2a1b --- /dev/null +++ b/src/GLMath/glm/ext/matrix_float3x2_precision.hpp @@ -0,0 +1,31 @@ +/// @ref core +/// @file glm/ext/matrix_float3x2_precision.hpp + +#pragma once +#include "../detail/type_mat3x2.hpp" + +namespace glm +{ + /// @addtogroup core_matrix_precision + /// @{ + + /// 3 columns of 2 components matrix of single-precision floating-point numbers using low precision arithmetic in term of ULPs. + /// + /// @see GLSL 4.20.8 specification, section 4.1.6 Matrices + /// @see GLSL 4.20.8 specification, section 4.7.2 Precision Qualifier + typedef mat<3, 2, float, lowp> lowp_mat3x2; + + /// 3 columns of 2 components matrix of single-precision floating-point numbers using medium precision arithmetic in term of ULPs. + /// + /// @see GLSL 4.20.8 specification, section 4.1.6 Matrices + /// @see GLSL 4.20.8 specification, section 4.7.2 Precision Qualifier + typedef mat<3, 2, float, mediump> mediump_mat3x2; + + /// 3 columns of 2 components matrix of single-precision floating-point numbers using high precision arithmetic in term of ULPs. + /// + /// @see GLSL 4.20.8 specification, section 4.1.6 Matrices + /// @see GLSL 4.20.8 specification, section 4.7.2 Precision Qualifier + typedef mat<3, 2, float, highp> highp_mat3x2; + + /// @} +}//namespace glm diff --git a/src/GLMath/glm/ext/matrix_float3x3.hpp b/src/GLMath/glm/ext/matrix_float3x3.hpp new file mode 100644 index 000000000..177d809ff --- /dev/null +++ b/src/GLMath/glm/ext/matrix_float3x3.hpp @@ -0,0 +1,23 @@ +/// @ref core +/// @file glm/ext/matrix_float3x3.hpp + +#pragma once +#include "../detail/type_mat3x3.hpp" + +namespace glm +{ + /// @addtogroup core_matrix + /// @{ + + /// 3 columns of 3 components matrix of single-precision floating-point numbers. + /// + /// @see GLSL 4.20.8 specification, section 4.1.6 Matrices + typedef mat<3, 3, float, defaultp> mat3x3; + + /// 3 columns of 3 components matrix of single-precision floating-point numbers. + /// + /// @see GLSL 4.20.8 specification, section 4.1.6 Matrices + typedef mat<3, 3, float, defaultp> mat3; + + /// @} +}//namespace glm diff --git a/src/GLMath/glm/ext/matrix_float3x3_precision.hpp b/src/GLMath/glm/ext/matrix_float3x3_precision.hpp new file mode 100644 index 000000000..8a900c164 --- /dev/null +++ b/src/GLMath/glm/ext/matrix_float3x3_precision.hpp @@ -0,0 +1,49 @@ +/// @ref core +/// @file glm/ext/matrix_float3x3_precision.hpp + +#pragma once +#include "../detail/type_mat3x3.hpp" + +namespace glm +{ + /// @addtogroup core_matrix_precision + /// @{ + + /// 3 columns of 3 components matrix of single-precision floating-point numbers using low precision arithmetic in term of ULPs. + /// + /// @see GLSL 4.20.8 specification, section 4.1.6 Matrices + /// @see GLSL 4.20.8 specification, section 4.7.2 Precision Qualifier + typedef mat<3, 3, float, lowp> lowp_mat3; + + /// 3 columns of 3 components matrix of single-precision floating-point numbers using medium precision arithmetic in term of ULPs. + /// + /// @see GLSL 4.20.8 specification, section 4.1.6 Matrices + /// @see GLSL 4.20.8 specification, section 4.7.2 Precision Qualifier + typedef mat<3, 3, float, mediump> mediump_mat3; + + /// 3 columns of 3 components matrix of single-precision floating-point numbers using high precision arithmetic in term of ULPs. + /// + /// @see GLSL 4.20.8 specification, section 4.1.6 Matrices + /// @see GLSL 4.20.8 specification, section 4.7.2 Precision Qualifier + typedef mat<3, 3, float, highp> highp_mat3; + + /// 3 columns of 3 components matrix of single-precision floating-point numbers using low precision arithmetic in term of ULPs. + /// + /// @see GLSL 4.20.8 specification, section 4.1.6 Matrices + /// @see GLSL 4.20.8 specification, section 4.7.2 Precision Qualifier + typedef mat<3, 3, float, lowp> lowp_mat3x3; + + /// 3 columns of 3 components matrix of single-precision floating-point numbers using medium precision arithmetic in term of ULPs. + /// + /// @see GLSL 4.20.8 specification, section 4.1.6 Matrices + /// @see GLSL 4.20.8 specification, section 4.7.2 Precision Qualifier + typedef mat<3, 3, float, mediump> mediump_mat3x3; + + /// 3 columns of 3 components matrix of single-precision floating-point numbers using high precision arithmetic in term of ULPs. + /// + /// @see GLSL 4.20.8 specification, section 4.1.6 Matrices + /// @see GLSL 4.20.8 specification, section 4.7.2 Precision Qualifier + typedef mat<3, 3, float, highp> highp_mat3x3; + + /// @} +}//namespace glm diff --git a/src/GLMath/glm/ext/matrix_float3x4.hpp b/src/GLMath/glm/ext/matrix_float3x4.hpp new file mode 100644 index 000000000..64b8459dc --- /dev/null +++ b/src/GLMath/glm/ext/matrix_float3x4.hpp @@ -0,0 +1,18 @@ +/// @ref core +/// @file glm/ext/matrix_float3x4.hpp + +#pragma once +#include "../detail/type_mat3x4.hpp" + +namespace glm +{ + /// @addtogroup core_matrix + /// @{ + + /// 3 columns of 4 components matrix of single-precision floating-point numbers. + /// + /// @see GLSL 4.20.8 specification, section 4.1.6 Matrices + typedef mat<3, 4, float, defaultp> mat3x4; + + /// @} +}//namespace glm diff --git a/src/GLMath/glm/ext/matrix_float3x4_precision.hpp b/src/GLMath/glm/ext/matrix_float3x4_precision.hpp new file mode 100644 index 000000000..bc36bf13a --- /dev/null +++ b/src/GLMath/glm/ext/matrix_float3x4_precision.hpp @@ -0,0 +1,31 @@ +/// @ref core +/// @file glm/ext/matrix_float3x4_precision.hpp + +#pragma once +#include "../detail/type_mat3x4.hpp" + +namespace glm +{ + /// @addtogroup core_matrix_precision + /// @{ + + /// 3 columns of 4 components matrix of single-precision floating-point numbers using low precision arithmetic in term of ULPs. + /// + /// @see GLSL 4.20.8 specification, section 4.1.6 Matrices + /// @see GLSL 4.20.8 specification, section 4.7.2 Precision Qualifier + typedef mat<3, 4, float, lowp> lowp_mat3x4; + + /// 3 columns of 4 components matrix of single-precision floating-point numbers using medium precision arithmetic in term of ULPs. + /// + /// @see GLSL 4.20.8 specification, section 4.1.6 Matrices + /// @see GLSL 4.20.8 specification, section 4.7.2 Precision Qualifier + typedef mat<3, 4, float, mediump> mediump_mat3x4; + + /// 3 columns of 4 components matrix of single-precision floating-point numbers using high precision arithmetic in term of ULPs. + /// + /// @see GLSL 4.20.8 specification, section 4.1.6 Matrices + /// @see GLSL 4.20.8 specification, section 4.7.2 Precision Qualifier + typedef mat<3, 4, float, highp> highp_mat3x4; + + /// @} +}//namespace glm diff --git a/src/GLMath/glm/ext/matrix_float4x2.hpp b/src/GLMath/glm/ext/matrix_float4x2.hpp new file mode 100644 index 000000000..1ed5227bf --- /dev/null +++ b/src/GLMath/glm/ext/matrix_float4x2.hpp @@ -0,0 +1,18 @@ +/// @ref core +/// @file glm/ext/matrix_float4x2.hpp + +#pragma once +#include "../detail/type_mat4x2.hpp" + +namespace glm +{ + /// @addtogroup core_matrix + /// @{ + + /// 4 columns of 2 components matrix of single-precision floating-point numbers. + /// + /// @see GLSL 4.20.8 specification, section 4.1.6 Matrices + typedef mat<4, 2, float, defaultp> mat4x2; + + /// @} +}//namespace glm diff --git a/src/GLMath/glm/ext/matrix_float4x2_precision.hpp b/src/GLMath/glm/ext/matrix_float4x2_precision.hpp new file mode 100644 index 000000000..88fd06963 --- /dev/null +++ b/src/GLMath/glm/ext/matrix_float4x2_precision.hpp @@ -0,0 +1,31 @@ +/// @ref core +/// @file glm/ext/matrix_float2x2_precision.hpp + +#pragma once +#include "../detail/type_mat2x2.hpp" + +namespace glm +{ + /// @addtogroup core_matrix_precision + /// @{ + + /// 4 columns of 2 components matrix of single-precision floating-point numbers using low precision arithmetic in term of ULPs. + /// + /// @see GLSL 4.20.8 specification, section 4.1.6 Matrices + /// @see GLSL 4.20.8 specification, section 4.7.2 Precision Qualifier + typedef mat<4, 2, float, lowp> lowp_mat4x2; + + /// 4 columns of 2 components matrix of single-precision floating-point numbers using medium precision arithmetic in term of ULPs. + /// + /// @see GLSL 4.20.8 specification, section 4.1.6 Matrices + /// @see GLSL 4.20.8 specification, section 4.7.2 Precision Qualifier + typedef mat<4, 2, float, mediump> mediump_mat4x2; + + /// 4 columns of 2 components matrix of single-precision floating-point numbers using high precision arithmetic in term of ULPs. + /// + /// @see GLSL 4.20.8 specification, section 4.1.6 Matrices + /// @see GLSL 4.20.8 specification, section 4.7.2 Precision Qualifier + typedef mat<4, 2, float, highp> highp_mat4x2; + + /// @} +}//namespace glm diff --git a/src/GLMath/glm/ext/matrix_float4x3.hpp b/src/GLMath/glm/ext/matrix_float4x3.hpp new file mode 100644 index 000000000..5dbe76570 --- /dev/null +++ b/src/GLMath/glm/ext/matrix_float4x3.hpp @@ -0,0 +1,18 @@ +/// @ref core +/// @file glm/ext/matrix_float4x3.hpp + +#pragma once +#include "../detail/type_mat4x3.hpp" + +namespace glm +{ + /// @addtogroup core_matrix + /// @{ + + /// 4 columns of 3 components matrix of single-precision floating-point numbers. + /// + /// @see GLSL 4.20.8 specification, section 4.1.6 Matrices + typedef mat<4, 3, float, defaultp> mat4x3; + + /// @} +}//namespace glm diff --git a/src/GLMath/glm/ext/matrix_float4x3_precision.hpp b/src/GLMath/glm/ext/matrix_float4x3_precision.hpp new file mode 100644 index 000000000..846ed4fc8 --- /dev/null +++ b/src/GLMath/glm/ext/matrix_float4x3_precision.hpp @@ -0,0 +1,31 @@ +/// @ref core +/// @file glm/ext/matrix_float4x3_precision.hpp + +#pragma once +#include "../detail/type_mat4x3.hpp" + +namespace glm +{ + /// @addtogroup core_matrix_precision + /// @{ + + /// 4 columns of 3 components matrix of single-precision floating-point numbers using low precision arithmetic in term of ULPs. + /// + /// @see GLSL 4.20.8 specification, section 4.1.6 Matrices + /// @see GLSL 4.20.8 specification, section 4.7.2 Precision Qualifier + typedef mat<4, 3, float, lowp> lowp_mat4x3; + + /// 4 columns of 3 components matrix of single-precision floating-point numbers using medium precision arithmetic in term of ULPs. + /// + /// @see GLSL 4.20.8 specification, section 4.1.6 Matrices + /// @see GLSL 4.20.8 specification, section 4.7.2 Precision Qualifier + typedef mat<4, 3, float, mediump> mediump_mat4x3; + + /// 4 columns of 3 components matrix of single-precision floating-point numbers using high precision arithmetic in term of ULPs. + /// + /// @see GLSL 4.20.8 specification, section 4.1.6 Matrices + /// @see GLSL 4.20.8 specification, section 4.7.2 Precision Qualifier + typedef mat<4, 3, float, highp> highp_mat4x3; + + /// @} +}//namespace glm diff --git a/src/GLMath/glm/ext/matrix_float4x4.hpp b/src/GLMath/glm/ext/matrix_float4x4.hpp new file mode 100644 index 000000000..5ba111de0 --- /dev/null +++ b/src/GLMath/glm/ext/matrix_float4x4.hpp @@ -0,0 +1,23 @@ +/// @ref core +/// @file glm/ext/matrix_float4x4.hpp + +#pragma once +#include "../detail/type_mat4x4.hpp" + +namespace glm +{ + /// @ingroup core_matrix + /// @{ + + /// 4 columns of 4 components matrix of single-precision floating-point numbers. + /// + /// @see GLSL 4.20.8 specification, section 4.1.6 Matrices + typedef mat<4, 4, float, defaultp> mat4x4; + + /// 4 columns of 4 components matrix of single-precision floating-point numbers. + /// + /// @see GLSL 4.20.8 specification, section 4.1.6 Matrices + typedef mat<4, 4, float, defaultp> mat4; + + /// @} +}//namespace glm diff --git a/src/GLMath/glm/ext/matrix_float4x4_precision.hpp b/src/GLMath/glm/ext/matrix_float4x4_precision.hpp new file mode 100644 index 000000000..597149bcf --- /dev/null +++ b/src/GLMath/glm/ext/matrix_float4x4_precision.hpp @@ -0,0 +1,49 @@ +/// @ref core +/// @file glm/ext/matrix_float4x4_precision.hpp + +#pragma once +#include "../detail/type_mat4x4.hpp" + +namespace glm +{ + /// @addtogroup core_matrix_precision + /// @{ + + /// 4 columns of 4 components matrix of single-precision floating-point numbers using low precision arithmetic in term of ULPs. + /// + /// @see GLSL 4.20.8 specification, section 4.1.6 Matrices + /// @see GLSL 4.20.8 specification, section 4.7.2 Precision Qualifier + typedef mat<4, 4, float, lowp> lowp_mat4; + + /// 4 columns of 4 components matrix of single-precision floating-point numbers using medium precision arithmetic in term of ULPs. + /// + /// @see GLSL 4.20.8 specification, section 4.1.6 Matrices + /// @see GLSL 4.20.8 specification, section 4.7.2 Precision Qualifier + typedef mat<4, 4, float, mediump> mediump_mat4; + + /// 4 columns of 4 components matrix of single-precision floating-point numbers using high precision arithmetic in term of ULPs. + /// + /// @see GLSL 4.20.8 specification, section 4.1.6 Matrices + /// @see GLSL 4.20.8 specification, section 4.7.2 Precision Qualifier + typedef mat<4, 4, float, highp> highp_mat4; + + /// 4 columns of 4 components matrix of single-precision floating-point numbers using low precision arithmetic in term of ULPs. + /// + /// @see GLSL 4.20.8 specification, section 4.1.6 Matrices + /// @see GLSL 4.20.8 specification, section 4.7.2 Precision Qualifier + typedef mat<4, 4, float, lowp> lowp_mat4x4; + + /// 4 columns of 4 components matrix of single-precision floating-point numbers using medium precision arithmetic in term of ULPs. + /// + /// @see GLSL 4.20.8 specification, section 4.1.6 Matrices + /// @see GLSL 4.20.8 specification, section 4.7.2 Precision Qualifier + typedef mat<4, 4, float, mediump> mediump_mat4x4; + + /// 4 columns of 4 components matrix of single-precision floating-point numbers using high precision arithmetic in term of ULPs. + /// + /// @see GLSL 4.20.8 specification, section 4.1.6 Matrices + /// @see GLSL 4.20.8 specification, section 4.7.2 Precision Qualifier + typedef mat<4, 4, float, highp> highp_mat4x4; + + /// @} +}//namespace glm diff --git a/src/GLMath/glm/ext/matrix_projection.hpp b/src/GLMath/glm/ext/matrix_projection.hpp new file mode 100644 index 000000000..51fd01bd8 --- /dev/null +++ b/src/GLMath/glm/ext/matrix_projection.hpp @@ -0,0 +1,149 @@ +/// @ref ext_matrix_projection +/// @file glm/ext/matrix_projection.hpp +/// +/// @defgroup ext_matrix_projection GLM_EXT_matrix_projection +/// @ingroup ext +/// +/// Functions that generate common projection transformation matrices. +/// +/// The matrices generated by this extension use standard OpenGL fixed-function +/// conventions. For example, the lookAt function generates a transform from world +/// space into the specific eye space that the projective matrix functions +/// (perspective, ortho, etc) are designed to expect. The OpenGL compatibility +/// specifications defines the particular layout of this eye space. +/// +/// Include to use the features of this extension. +/// +/// @see ext_matrix_transform +/// @see ext_matrix_clip_space + +#pragma once + +// Dependencies +#include "../gtc/constants.hpp" +#include "../geometric.hpp" +#include "../trigonometric.hpp" +#include "../matrix.hpp" + +#if GLM_MESSAGES == GLM_ENABLE && !defined(GLM_EXT_INCLUDED) +# pragma message("GLM: GLM_EXT_matrix_projection extension included") +#endif + +namespace glm +{ + /// @addtogroup ext_matrix_projection + /// @{ + + /// Map the specified object coordinates (obj.x, obj.y, obj.z) into window coordinates. + /// The near and far clip planes correspond to z normalized device coordinates of 0 and +1 respectively. (Direct3D clip volume definition) + /// + /// @param obj Specify the object coordinates. + /// @param model Specifies the current modelview matrix + /// @param proj Specifies the current projection matrix + /// @param viewport Specifies the current viewport + /// @return Return the computed window coordinates. + /// @tparam T Native type used for the computation. Currently supported: half (not recommended), float or double. + /// @tparam U Currently supported: Floating-point types and integer types. + /// + /// @see gluProject man page + template + GLM_FUNC_DECL vec<3, T, Q> projectZO( + vec<3, T, Q> const& obj, mat<4, 4, T, Q> const& model, mat<4, 4, T, Q> const& proj, vec<4, U, Q> const& viewport); + + /// Map the specified object coordinates (obj.x, obj.y, obj.z) into window coordinates. + /// The near and far clip planes correspond to z normalized device coordinates of -1 and +1 respectively. (OpenGL clip volume definition) + /// + /// @param obj Specify the object coordinates. + /// @param model Specifies the current modelview matrix + /// @param proj Specifies the current projection matrix + /// @param viewport Specifies the current viewport + /// @return Return the computed window coordinates. + /// @tparam T Native type used for the computation. Currently supported: half (not recommended), float or double. + /// @tparam U Currently supported: Floating-point types and integer types. + /// + /// @see gluProject man page + template + GLM_FUNC_DECL vec<3, T, Q> projectNO( + vec<3, T, Q> const& obj, mat<4, 4, T, Q> const& model, mat<4, 4, T, Q> const& proj, vec<4, U, Q> const& viewport); + + /// Map the specified object coordinates (obj.x, obj.y, obj.z) into window coordinates using default near and far clip planes definition. + /// To change default near and far clip planes definition use GLM_FORCE_DEPTH_ZERO_TO_ONE. + /// + /// @param obj Specify the object coordinates. + /// @param model Specifies the current modelview matrix + /// @param proj Specifies the current projection matrix + /// @param viewport Specifies the current viewport + /// @return Return the computed window coordinates. + /// @tparam T Native type used for the computation. Currently supported: half (not recommended), float or double. + /// @tparam U Currently supported: Floating-point types and integer types. + /// + /// @see gluProject man page + template + GLM_FUNC_DECL vec<3, T, Q> project( + vec<3, T, Q> const& obj, mat<4, 4, T, Q> const& model, mat<4, 4, T, Q> const& proj, vec<4, U, Q> const& viewport); + + /// Map the specified window coordinates (win.x, win.y, win.z) into object coordinates. + /// The near and far clip planes correspond to z normalized device coordinates of 0 and +1 respectively. (Direct3D clip volume definition) + /// + /// @param win Specify the window coordinates to be mapped. + /// @param model Specifies the modelview matrix + /// @param proj Specifies the projection matrix + /// @param viewport Specifies the viewport + /// @return Returns the computed object coordinates. + /// @tparam T Native type used for the computation. Currently supported: half (not recommended), float or double. + /// @tparam U Currently supported: Floating-point types and integer types. + /// + /// @see gluUnProject man page + template + GLM_FUNC_DECL vec<3, T, Q> unProjectZO( + vec<3, T, Q> const& win, mat<4, 4, T, Q> const& model, mat<4, 4, T, Q> const& proj, vec<4, U, Q> const& viewport); + + /// Map the specified window coordinates (win.x, win.y, win.z) into object coordinates. + /// The near and far clip planes correspond to z normalized device coordinates of -1 and +1 respectively. (OpenGL clip volume definition) + /// + /// @param win Specify the window coordinates to be mapped. + /// @param model Specifies the modelview matrix + /// @param proj Specifies the projection matrix + /// @param viewport Specifies the viewport + /// @return Returns the computed object coordinates. + /// @tparam T Native type used for the computation. Currently supported: half (not recommended), float or double. + /// @tparam U Currently supported: Floating-point types and integer types. + /// + /// @see gluUnProject man page + template + GLM_FUNC_DECL vec<3, T, Q> unProjectNO( + vec<3, T, Q> const& win, mat<4, 4, T, Q> const& model, mat<4, 4, T, Q> const& proj, vec<4, U, Q> const& viewport); + + /// Map the specified window coordinates (win.x, win.y, win.z) into object coordinates using default near and far clip planes definition. + /// To change default near and far clip planes definition use GLM_FORCE_DEPTH_ZERO_TO_ONE. + /// + /// @param win Specify the window coordinates to be mapped. + /// @param model Specifies the modelview matrix + /// @param proj Specifies the projection matrix + /// @param viewport Specifies the viewport + /// @return Returns the computed object coordinates. + /// @tparam T Native type used for the computation. Currently supported: half (not recommended), float or double. + /// @tparam U Currently supported: Floating-point types and integer types. + /// + /// @see gluUnProject man page + template + GLM_FUNC_DECL vec<3, T, Q> unProject( + vec<3, T, Q> const& win, mat<4, 4, T, Q> const& model, mat<4, 4, T, Q> const& proj, vec<4, U, Q> const& viewport); + + /// Define a picking region + /// + /// @param center Specify the center of a picking region in window coordinates. + /// @param delta Specify the width and height, respectively, of the picking region in window coordinates. + /// @param viewport Rendering viewport + /// @tparam T Native type used for the computation. Currently supported: half (not recommended), float or double. + /// @tparam U Currently supported: Floating-point types and integer types. + /// + /// @see gluPickMatrix man page + template + GLM_FUNC_DECL mat<4, 4, T, Q> pickMatrix( + vec<2, T, Q> const& center, vec<2, T, Q> const& delta, vec<4, U, Q> const& viewport); + + /// @} +}//namespace glm + +#include "matrix_projection.inl" diff --git a/src/GLMath/glm/ext/matrix_projection.inl b/src/GLMath/glm/ext/matrix_projection.inl new file mode 100644 index 000000000..8b4eea986 --- /dev/null +++ b/src/GLMath/glm/ext/matrix_projection.inl @@ -0,0 +1,104 @@ +namespace glm +{ + template + GLM_FUNC_QUALIFIER vec<3, T, Q> projectZO(vec<3, T, Q> const& obj, mat<4, 4, T, Q> const& model, mat<4, 4, T, Q> const& proj, vec<4, U, Q> const& viewport) + { + vec<4, T, Q> tmp = vec<4, T, Q>(obj, static_cast(1)); + tmp = model * tmp; + tmp = proj * tmp; + + tmp /= tmp.w; + tmp.x = tmp.x * static_cast(0.5) + static_cast(0.5); + tmp.y = tmp.y * static_cast(0.5) + static_cast(0.5); + + tmp[0] = tmp[0] * T(viewport[2]) + T(viewport[0]); + tmp[1] = tmp[1] * T(viewport[3]) + T(viewport[1]); + + return vec<3, T, Q>(tmp); + } + + template + GLM_FUNC_QUALIFIER vec<3, T, Q> projectNO(vec<3, T, Q> const& obj, mat<4, 4, T, Q> const& model, mat<4, 4, T, Q> const& proj, vec<4, U, Q> const& viewport) + { + vec<4, T, Q> tmp = vec<4, T, Q>(obj, static_cast(1)); + tmp = model * tmp; + tmp = proj * tmp; + + tmp /= tmp.w; + tmp = tmp * static_cast(0.5) + static_cast(0.5); + tmp[0] = tmp[0] * T(viewport[2]) + T(viewport[0]); + tmp[1] = tmp[1] * T(viewport[3]) + T(viewport[1]); + + return vec<3, T, Q>(tmp); + } + + template + GLM_FUNC_QUALIFIER vec<3, T, Q> project(vec<3, T, Q> const& obj, mat<4, 4, T, Q> const& model, mat<4, 4, T, Q> const& proj, vec<4, U, Q> const& viewport) + { + if(GLM_CONFIG_CLIP_CONTROL & GLM_CLIP_CONTROL_ZO_BIT) + return projectZO(obj, model, proj, viewport); + else + return projectNO(obj, model, proj, viewport); + } + + template + GLM_FUNC_QUALIFIER vec<3, T, Q> unProjectZO(vec<3, T, Q> const& win, mat<4, 4, T, Q> const& model, mat<4, 4, T, Q> const& proj, vec<4, U, Q> const& viewport) + { + mat<4, 4, T, Q> Inverse = inverse(proj * model); + + vec<4, T, Q> tmp = vec<4, T, Q>(win, T(1)); + tmp.x = (tmp.x - T(viewport[0])) / T(viewport[2]); + tmp.y = (tmp.y - T(viewport[1])) / T(viewport[3]); + tmp.x = tmp.x * static_cast(2) - static_cast(1); + tmp.y = tmp.y * static_cast(2) - static_cast(1); + + vec<4, T, Q> obj = Inverse * tmp; + obj /= obj.w; + + return vec<3, T, Q>(obj); + } + + template + GLM_FUNC_QUALIFIER vec<3, T, Q> unProjectNO(vec<3, T, Q> const& win, mat<4, 4, T, Q> const& model, mat<4, 4, T, Q> const& proj, vec<4, U, Q> const& viewport) + { + mat<4, 4, T, Q> Inverse = inverse(proj * model); + + vec<4, T, Q> tmp = vec<4, T, Q>(win, T(1)); + tmp.x = (tmp.x - T(viewport[0])) / T(viewport[2]); + tmp.y = (tmp.y - T(viewport[1])) / T(viewport[3]); + tmp = tmp * static_cast(2) - static_cast(1); + + vec<4, T, Q> obj = Inverse * tmp; + obj /= obj.w; + + return vec<3, T, Q>(obj); + } + + template + GLM_FUNC_QUALIFIER vec<3, T, Q> unProject(vec<3, T, Q> const& win, mat<4, 4, T, Q> const& model, mat<4, 4, T, Q> const& proj, vec<4, U, Q> const& viewport) + { + if(GLM_CONFIG_CLIP_CONTROL & GLM_CLIP_CONTROL_ZO_BIT) + return unProjectZO(win, model, proj, viewport); + else + return unProjectNO(win, model, proj, viewport); + } + + template + GLM_FUNC_QUALIFIER mat<4, 4, T, Q> pickMatrix(vec<2, T, Q> const& center, vec<2, T, Q> const& delta, vec<4, U, Q> const& viewport) + { + assert(delta.x > static_cast(0) && delta.y > static_cast(0)); + mat<4, 4, T, Q> Result(static_cast(1)); + + if(!(delta.x > static_cast(0) && delta.y > static_cast(0))) + return Result; // Error + + vec<3, T, Q> Temp( + (static_cast(viewport[2]) - static_cast(2) * (center.x - static_cast(viewport[0]))) / delta.x, + (static_cast(viewport[3]) - static_cast(2) * (center.y - static_cast(viewport[1]))) / delta.y, + static_cast(0)); + + // Translate and scale the picked region to the entire window + Result = translate(Result, Temp); + return scale(Result, vec<3, T, Q>(static_cast(viewport[2]) / delta.x, static_cast(viewport[3]) / delta.y, static_cast(1))); + } +}//namespace glm diff --git a/src/GLMath/glm/ext/matrix_relational.hpp b/src/GLMath/glm/ext/matrix_relational.hpp new file mode 100644 index 000000000..20023ad89 --- /dev/null +++ b/src/GLMath/glm/ext/matrix_relational.hpp @@ -0,0 +1,132 @@ +/// @ref ext_matrix_relational +/// @file glm/ext/matrix_relational.hpp +/// +/// @defgroup ext_matrix_relational GLM_EXT_matrix_relational +/// @ingroup ext +/// +/// Exposes comparison functions for matrix types that take a user defined epsilon values. +/// +/// Include to use the features of this extension. +/// +/// @see ext_vector_relational +/// @see ext_scalar_relational +/// @see ext_quaternion_relational + +#pragma once + +// Dependencies +#include "../detail/qualifier.hpp" + +#if GLM_MESSAGES == GLM_ENABLE && !defined(GLM_EXT_INCLUDED) +# pragma message("GLM: GLM_EXT_matrix_relational extension included") +#endif + +namespace glm +{ + /// @addtogroup ext_matrix_relational + /// @{ + + /// Perform a component-wise equal-to comparison of two matrices. + /// Return a boolean vector which components value is True if this expression is satisfied per column of the matrices. + /// + /// @tparam C Integer between 1 and 4 included that qualify the number of columns of the matrix + /// @tparam R Integer between 1 and 4 included that qualify the number of rows of the matrix + /// @tparam T Floating-point or integer scalar types + /// @tparam Q Value from qualifier enum + template + GLM_FUNC_DECL GLM_CONSTEXPR vec equal(mat const& x, mat const& y); + + /// Perform a component-wise not-equal-to comparison of two matrices. + /// Return a boolean vector which components value is True if this expression is satisfied per column of the matrices. + /// + /// @tparam C Integer between 1 and 4 included that qualify the number of columns of the matrix + /// @tparam R Integer between 1 and 4 included that qualify the number of rows of the matrix + /// @tparam T Floating-point or integer scalar types + /// @tparam Q Value from qualifier enum + template + GLM_FUNC_DECL GLM_CONSTEXPR vec notEqual(mat const& x, mat const& y); + + /// Returns the component-wise comparison of |x - y| < epsilon. + /// True if this expression is satisfied. + /// + /// @tparam C Integer between 1 and 4 included that qualify the number of columns of the matrix + /// @tparam R Integer between 1 and 4 included that qualify the number of rows of the matrix + /// @tparam T Floating-point or integer scalar types + /// @tparam Q Value from qualifier enum + template + GLM_FUNC_DECL GLM_CONSTEXPR vec equal(mat const& x, mat const& y, T epsilon); + + /// Returns the component-wise comparison of |x - y| < epsilon. + /// True if this expression is satisfied. + /// + /// @tparam C Integer between 1 and 4 included that qualify the number of columns of the matrix + /// @tparam R Integer between 1 and 4 included that qualify the number of rows of the matrix + /// @tparam T Floating-point or integer scalar types + /// @tparam Q Value from qualifier enum + template + GLM_FUNC_DECL GLM_CONSTEXPR vec equal(mat const& x, mat const& y, vec const& epsilon); + + /// Returns the component-wise comparison of |x - y| < epsilon. + /// True if this expression is not satisfied. + /// + /// @tparam C Integer between 1 and 4 included that qualify the number of columns of the matrix + /// @tparam R Integer between 1 and 4 included that qualify the number of rows of the matrix + /// @tparam T Floating-point or integer scalar types + /// @tparam Q Value from qualifier enum + template + GLM_FUNC_DECL GLM_CONSTEXPR vec notEqual(mat const& x, mat const& y, T epsilon); + + /// Returns the component-wise comparison of |x - y| >= epsilon. + /// True if this expression is not satisfied. + /// + /// @tparam C Integer between 1 and 4 included that qualify the number of columns of the matrix + /// @tparam R Integer between 1 and 4 included that qualify the number of rows of the matrix + /// @tparam T Floating-point or integer scalar types + /// @tparam Q Value from qualifier enum + template + GLM_FUNC_DECL GLM_CONSTEXPR vec notEqual(mat const& x, mat const& y, vec const& epsilon); + + /// Returns the component-wise comparison between two vectors in term of ULPs. + /// True if this expression is satisfied. + /// + /// @tparam C Integer between 1 and 4 included that qualify the number of columns of the matrix + /// @tparam R Integer between 1 and 4 included that qualify the number of rows of the matrix + /// @tparam T Floating-point + /// @tparam Q Value from qualifier enum + template + GLM_FUNC_DECL GLM_CONSTEXPR vec equal(mat const& x, mat const& y, int ULPs); + + /// Returns the component-wise comparison between two vectors in term of ULPs. + /// True if this expression is satisfied. + /// + /// @tparam C Integer between 1 and 4 included that qualify the number of columns of the matrix + /// @tparam R Integer between 1 and 4 included that qualify the number of rows of the matrix + /// @tparam T Floating-point + /// @tparam Q Value from qualifier enum + template + GLM_FUNC_DECL GLM_CONSTEXPR vec equal(mat const& x, mat const& y, vec const& ULPs); + + /// Returns the component-wise comparison between two vectors in term of ULPs. + /// True if this expression is not satisfied. + /// + /// @tparam C Integer between 1 and 4 included that qualify the number of columns of the matrix + /// @tparam R Integer between 1 and 4 included that qualify the number of rows of the matrix + /// @tparam T Floating-point + /// @tparam Q Value from qualifier enum + template + GLM_FUNC_DECL GLM_CONSTEXPR vec notEqual(mat const& x, mat const& y, int ULPs); + + /// Returns the component-wise comparison between two vectors in term of ULPs. + /// True if this expression is not satisfied. + /// + /// @tparam C Integer between 1 and 4 included that qualify the number of columns of the matrix + /// @tparam R Integer between 1 and 4 included that qualify the number of rows of the matrix + /// @tparam T Floating-point + /// @tparam Q Value from qualifier enum + template + GLM_FUNC_DECL GLM_CONSTEXPR vec notEqual(mat const& x, mat const& y, vec const& ULPs); + + /// @} +}//namespace glm + +#include "matrix_relational.inl" diff --git a/src/GLMath/glm/ext/matrix_relational.inl b/src/GLMath/glm/ext/matrix_relational.inl new file mode 100644 index 000000000..b2b875309 --- /dev/null +++ b/src/GLMath/glm/ext/matrix_relational.inl @@ -0,0 +1,82 @@ +/// @ref ext_vector_relational +/// @file glm/ext/vector_relational.inl + +// Dependency: +#include "../ext/vector_relational.hpp" +#include "../common.hpp" + +namespace glm +{ + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec equal(mat const& a, mat const& b) + { + return equal(a, b, static_cast(0)); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec equal(mat const& a, mat const& b, T Epsilon) + { + return equal(a, b, vec(Epsilon)); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec equal(mat const& a, mat const& b, vec const& Epsilon) + { + vec Result(true); + for(length_t i = 0; i < C; ++i) + Result[i] = all(equal(a[i], b[i], Epsilon[i])); + return Result; + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec notEqual(mat const& x, mat const& y) + { + return notEqual(x, y, static_cast(0)); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec notEqual(mat const& x, mat const& y, T Epsilon) + { + return notEqual(x, y, vec(Epsilon)); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec notEqual(mat const& a, mat const& b, vec const& Epsilon) + { + vec Result(true); + for(length_t i = 0; i < C; ++i) + Result[i] = any(notEqual(a[i], b[i], Epsilon[i])); + return Result; + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec equal(mat const& a, mat const& b, int MaxULPs) + { + return equal(a, b, vec(MaxULPs)); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec equal(mat const& a, mat const& b, vec const& MaxULPs) + { + vec Result(true); + for(length_t i = 0; i < C; ++i) + Result[i] = all(equal(a[i], b[i], MaxULPs[i])); + return Result; + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec notEqual(mat const& x, mat const& y, int MaxULPs) + { + return notEqual(x, y, vec(MaxULPs)); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec notEqual(mat const& a, mat const& b, vec const& MaxULPs) + { + vec Result(true); + for(length_t i = 0; i < C; ++i) + Result[i] = any(notEqual(a[i], b[i], MaxULPs[i])); + return Result; + } + +}//namespace glm diff --git a/src/GLMath/glm/ext/matrix_transform.hpp b/src/GLMath/glm/ext/matrix_transform.hpp new file mode 100644 index 000000000..cbd187efd --- /dev/null +++ b/src/GLMath/glm/ext/matrix_transform.hpp @@ -0,0 +1,144 @@ +/// @ref ext_matrix_transform +/// @file glm/ext/matrix_transform.hpp +/// +/// @defgroup ext_matrix_transform GLM_EXT_matrix_transform +/// @ingroup ext +/// +/// Defines functions that generate common transformation matrices. +/// +/// The matrices generated by this extension use standard OpenGL fixed-function +/// conventions. For example, the lookAt function generates a transform from world +/// space into the specific eye space that the projective matrix functions +/// (perspective, ortho, etc) are designed to expect. The OpenGL compatibility +/// specifications defines the particular layout of this eye space. +/// +/// Include to use the features of this extension. +/// +/// @see ext_matrix_projection +/// @see ext_matrix_clip_space + +#pragma once + +// Dependencies +#include "../gtc/constants.hpp" +#include "../geometric.hpp" +#include "../trigonometric.hpp" +#include "../matrix.hpp" + +#if GLM_MESSAGES == GLM_ENABLE && !defined(GLM_EXT_INCLUDED) +# pragma message("GLM: GLM_EXT_matrix_transform extension included") +#endif + +namespace glm +{ + /// @addtogroup ext_matrix_transform + /// @{ + + /// Builds an identity matrix. + template + GLM_FUNC_DECL GLM_CONSTEXPR genType identity(); + + /// Builds a translation 4 * 4 matrix created from a vector of 3 components. + /// + /// @param m Input matrix multiplied by this translation matrix. + /// @param v Coordinates of a translation vector. + /// + /// @tparam T A floating-point scalar type + /// @tparam Q A value from qualifier enum + /// + /// @code + /// #include + /// #include + /// ... + /// glm::mat4 m = glm::translate(glm::mat4(1.0f), glm::vec3(1.0f)); + /// // m[0][0] == 1.0f, m[0][1] == 0.0f, m[0][2] == 0.0f, m[0][3] == 0.0f + /// // m[1][0] == 0.0f, m[1][1] == 1.0f, m[1][2] == 0.0f, m[1][3] == 0.0f + /// // m[2][0] == 0.0f, m[2][1] == 0.0f, m[2][2] == 1.0f, m[2][3] == 0.0f + /// // m[3][0] == 1.0f, m[3][1] == 1.0f, m[3][2] == 1.0f, m[3][3] == 1.0f + /// @endcode + /// + /// @see - translate(mat<4, 4, T, Q> const& m, T x, T y, T z) + /// @see - translate(vec<3, T, Q> const& v) + /// @see glTranslate man page + template + GLM_FUNC_DECL mat<4, 4, T, Q> translate( + mat<4, 4, T, Q> const& m, vec<3, T, Q> const& v); + + /// Builds a rotation 4 * 4 matrix created from an axis vector and an angle. + /// + /// @param m Input matrix multiplied by this rotation matrix. + /// @param angle Rotation angle expressed in radians. + /// @param axis Rotation axis, recommended to be normalized. + /// + /// @tparam T A floating-point scalar type + /// @tparam Q A value from qualifier enum + /// + /// @see - rotate(mat<4, 4, T, Q> const& m, T angle, T x, T y, T z) + /// @see - rotate(T angle, vec<3, T, Q> const& v) + /// @see glRotate man page + template + GLM_FUNC_DECL mat<4, 4, T, Q> rotate( + mat<4, 4, T, Q> const& m, T angle, vec<3, T, Q> const& axis); + + /// Builds a scale 4 * 4 matrix created from 3 scalars. + /// + /// @param m Input matrix multiplied by this scale matrix. + /// @param v Ratio of scaling for each axis. + /// + /// @tparam T A floating-point scalar type + /// @tparam Q A value from qualifier enum + /// + /// @see - scale(mat<4, 4, T, Q> const& m, T x, T y, T z) + /// @see - scale(vec<3, T, Q> const& v) + /// @see glScale man page + template + GLM_FUNC_DECL mat<4, 4, T, Q> scale( + mat<4, 4, T, Q> const& m, vec<3, T, Q> const& v); + + /// Build a right handed look at view matrix. + /// + /// @param eye Position of the camera + /// @param center Position where the camera is looking at + /// @param up Normalized up vector, how the camera is oriented. Typically (0, 0, 1) + /// + /// @tparam T A floating-point scalar type + /// @tparam Q A value from qualifier enum + /// + /// @see - frustum(T const& left, T const& right, T const& bottom, T const& top, T const& nearVal, T const& farVal) frustum(T const& left, T const& right, T const& bottom, T const& top, T const& nearVal, T const& farVal) + template + GLM_FUNC_DECL mat<4, 4, T, Q> lookAtRH( + vec<3, T, Q> const& eye, vec<3, T, Q> const& center, vec<3, T, Q> const& up); + + /// Build a left handed look at view matrix. + /// + /// @param eye Position of the camera + /// @param center Position where the camera is looking at + /// @param up Normalized up vector, how the camera is oriented. Typically (0, 0, 1) + /// + /// @tparam T A floating-point scalar type + /// @tparam Q A value from qualifier enum + /// + /// @see - frustum(T const& left, T const& right, T const& bottom, T const& top, T const& nearVal, T const& farVal) frustum(T const& left, T const& right, T const& bottom, T const& top, T const& nearVal, T const& farVal) + template + GLM_FUNC_DECL mat<4, 4, T, Q> lookAtLH( + vec<3, T, Q> const& eye, vec<3, T, Q> const& center, vec<3, T, Q> const& up); + + /// Build a look at view matrix based on the default handedness. + /// + /// @param eye Position of the camera + /// @param center Position where the camera is looking at + /// @param up Normalized up vector, how the camera is oriented. Typically (0, 0, 1) + /// + /// @tparam T A floating-point scalar type + /// @tparam Q A value from qualifier enum + /// + /// @see - frustum(T const& left, T const& right, T const& bottom, T const& top, T const& nearVal, T const& farVal) frustum(T const& left, T const& right, T const& bottom, T const& top, T const& nearVal, T const& farVal) + /// @see gluLookAt man page + template + GLM_FUNC_DECL mat<4, 4, T, Q> lookAt( + vec<3, T, Q> const& eye, vec<3, T, Q> const& center, vec<3, T, Q> const& up); + + /// @} +}//namespace glm + +#include "matrix_transform.inl" diff --git a/src/GLMath/glm/ext/matrix_transform.inl b/src/GLMath/glm/ext/matrix_transform.inl new file mode 100644 index 000000000..a415157e0 --- /dev/null +++ b/src/GLMath/glm/ext/matrix_transform.inl @@ -0,0 +1,152 @@ +namespace glm +{ + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR genType identity() + { + return detail::init_gentype::GENTYPE>::identity(); + } + + template + GLM_FUNC_QUALIFIER mat<4, 4, T, Q> translate(mat<4, 4, T, Q> const& m, vec<3, T, Q> const& v) + { + mat<4, 4, T, Q> Result(m); + Result[3] = m[0] * v[0] + m[1] * v[1] + m[2] * v[2] + m[3]; + return Result; + } + + template + GLM_FUNC_QUALIFIER mat<4, 4, T, Q> rotate(mat<4, 4, T, Q> const& m, T angle, vec<3, T, Q> const& v) + { + T const a = angle; + T const c = cos(a); + T const s = sin(a); + + vec<3, T, Q> axis(normalize(v)); + vec<3, T, Q> temp((T(1) - c) * axis); + + mat<4, 4, T, Q> Rotate; + Rotate[0][0] = c + temp[0] * axis[0]; + Rotate[0][1] = temp[0] * axis[1] + s * axis[2]; + Rotate[0][2] = temp[0] * axis[2] - s * axis[1]; + + Rotate[1][0] = temp[1] * axis[0] - s * axis[2]; + Rotate[1][1] = c + temp[1] * axis[1]; + Rotate[1][2] = temp[1] * axis[2] + s * axis[0]; + + Rotate[2][0] = temp[2] * axis[0] + s * axis[1]; + Rotate[2][1] = temp[2] * axis[1] - s * axis[0]; + Rotate[2][2] = c + temp[2] * axis[2]; + + mat<4, 4, T, Q> Result; + Result[0] = m[0] * Rotate[0][0] + m[1] * Rotate[0][1] + m[2] * Rotate[0][2]; + Result[1] = m[0] * Rotate[1][0] + m[1] * Rotate[1][1] + m[2] * Rotate[1][2]; + Result[2] = m[0] * Rotate[2][0] + m[1] * Rotate[2][1] + m[2] * Rotate[2][2]; + Result[3] = m[3]; + return Result; + } + + template + GLM_FUNC_QUALIFIER mat<4, 4, T, Q> rotate_slow(mat<4, 4, T, Q> const& m, T angle, vec<3, T, Q> const& v) + { + T const a = angle; + T const c = cos(a); + T const s = sin(a); + mat<4, 4, T, Q> Result; + + vec<3, T, Q> axis = normalize(v); + + Result[0][0] = c + (static_cast(1) - c) * axis.x * axis.x; + Result[0][1] = (static_cast(1) - c) * axis.x * axis.y + s * axis.z; + Result[0][2] = (static_cast(1) - c) * axis.x * axis.z - s * axis.y; + Result[0][3] = static_cast(0); + + Result[1][0] = (static_cast(1) - c) * axis.y * axis.x - s * axis.z; + Result[1][1] = c + (static_cast(1) - c) * axis.y * axis.y; + Result[1][2] = (static_cast(1) - c) * axis.y * axis.z + s * axis.x; + Result[1][3] = static_cast(0); + + Result[2][0] = (static_cast(1) - c) * axis.z * axis.x + s * axis.y; + Result[2][1] = (static_cast(1) - c) * axis.z * axis.y - s * axis.x; + Result[2][2] = c + (static_cast(1) - c) * axis.z * axis.z; + Result[2][3] = static_cast(0); + + Result[3] = vec<4, T, Q>(0, 0, 0, 1); + return m * Result; + } + + template + GLM_FUNC_QUALIFIER mat<4, 4, T, Q> scale(mat<4, 4, T, Q> const& m, vec<3, T, Q> const& v) + { + mat<4, 4, T, Q> Result; + Result[0] = m[0] * v[0]; + Result[1] = m[1] * v[1]; + Result[2] = m[2] * v[2]; + Result[3] = m[3]; + return Result; + } + + template + GLM_FUNC_QUALIFIER mat<4, 4, T, Q> scale_slow(mat<4, 4, T, Q> const& m, vec<3, T, Q> const& v) + { + mat<4, 4, T, Q> Result(T(1)); + Result[0][0] = v.x; + Result[1][1] = v.y; + Result[2][2] = v.z; + return m * Result; + } + + template + GLM_FUNC_QUALIFIER mat<4, 4, T, Q> lookAtRH(vec<3, T, Q> const& eye, vec<3, T, Q> const& center, vec<3, T, Q> const& up) + { + vec<3, T, Q> const f(normalize(center - eye)); + vec<3, T, Q> const s(normalize(cross(f, up))); + vec<3, T, Q> const u(cross(s, f)); + + mat<4, 4, T, Q> Result(1); + Result[0][0] = s.x; + Result[1][0] = s.y; + Result[2][0] = s.z; + Result[0][1] = u.x; + Result[1][1] = u.y; + Result[2][1] = u.z; + Result[0][2] =-f.x; + Result[1][2] =-f.y; + Result[2][2] =-f.z; + Result[3][0] =-dot(s, eye); + Result[3][1] =-dot(u, eye); + Result[3][2] = dot(f, eye); + return Result; + } + + template + GLM_FUNC_QUALIFIER mat<4, 4, T, Q> lookAtLH(vec<3, T, Q> const& eye, vec<3, T, Q> const& center, vec<3, T, Q> const& up) + { + vec<3, T, Q> const f(normalize(center - eye)); + vec<3, T, Q> const s(normalize(cross(up, f))); + vec<3, T, Q> const u(cross(f, s)); + + mat<4, 4, T, Q> Result(1); + Result[0][0] = s.x; + Result[1][0] = s.y; + Result[2][0] = s.z; + Result[0][1] = u.x; + Result[1][1] = u.y; + Result[2][1] = u.z; + Result[0][2] = f.x; + Result[1][2] = f.y; + Result[2][2] = f.z; + Result[3][0] = -dot(s, eye); + Result[3][1] = -dot(u, eye); + Result[3][2] = -dot(f, eye); + return Result; + } + + template + GLM_FUNC_QUALIFIER mat<4, 4, T, Q> lookAt(vec<3, T, Q> const& eye, vec<3, T, Q> const& center, vec<3, T, Q> const& up) + { + GLM_IF_CONSTEXPR(GLM_CONFIG_CLIP_CONTROL & GLM_CLIP_CONTROL_LH_BIT) + return lookAtLH(eye, center, up); + else + return lookAtRH(eye, center, up); + } +}//namespace glm diff --git a/src/GLMath/glm/ext/quaternion_common.hpp b/src/GLMath/glm/ext/quaternion_common.hpp new file mode 100644 index 000000000..2980ed4d4 --- /dev/null +++ b/src/GLMath/glm/ext/quaternion_common.hpp @@ -0,0 +1,120 @@ +/// @ref ext_quaternion_common +/// @file glm/ext/quaternion_common.hpp +/// +/// @defgroup ext_quaternion_common GLM_EXT_quaternion_common +/// @ingroup ext +/// +/// Provides common functions for quaternion types +/// +/// Include to use the features of this extension. +/// +/// @see ext_scalar_common +/// @see ext_vector_common +/// @see ext_quaternion_float +/// @see ext_quaternion_double +/// @see ext_quaternion_exponential +/// @see ext_quaternion_geometric +/// @see ext_quaternion_relational +/// @see ext_quaternion_trigonometric +/// @see ext_quaternion_transform + +#pragma once + +// Dependency: +#include "../ext/scalar_constants.hpp" +#include "../ext/quaternion_geometric.hpp" +#include "../common.hpp" +#include "../trigonometric.hpp" +#include "../exponential.hpp" +#include + +#if GLM_MESSAGES == GLM_ENABLE && !defined(GLM_EXT_INCLUDED) +# pragma message("GLM: GLM_EXT_quaternion_common extension included") +#endif + +namespace glm +{ + /// @addtogroup ext_quaternion_common + /// @{ + + /// Spherical linear interpolation of two quaternions. + /// The interpolation is oriented and the rotation is performed at constant speed. + /// For short path spherical linear interpolation, use the slerp function. + /// + /// @param x A quaternion + /// @param y A quaternion + /// @param a Interpolation factor. The interpolation is defined beyond the range [0, 1]. + /// + /// @tparam T A floating-point scalar type + /// @tparam Q A value from qualifier enum + /// + /// @see - slerp(qua const& x, qua const& y, T const& a) + template + GLM_FUNC_DECL qua mix(qua const& x, qua const& y, T a); + + /// Linear interpolation of two quaternions. + /// The interpolation is oriented. + /// + /// @param x A quaternion + /// @param y A quaternion + /// @param a Interpolation factor. The interpolation is defined in the range [0, 1]. + /// + /// @tparam T A floating-point scalar type + /// @tparam Q A value from qualifier enum + template + GLM_FUNC_DECL qua lerp(qua const& x, qua const& y, T a); + + /// Spherical linear interpolation of two quaternions. + /// The interpolation always take the short path and the rotation is performed at constant speed. + /// + /// @param x A quaternion + /// @param y A quaternion + /// @param a Interpolation factor. The interpolation is defined beyond the range [0, 1]. + /// + /// @tparam T A floating-point scalar type + /// @tparam Q A value from qualifier enum + template + GLM_FUNC_DECL qua slerp(qua const& x, qua const& y, T a); + + /// Returns the q conjugate. + /// + /// @tparam T A floating-point scalar type + /// @tparam Q A value from qualifier enum + template + GLM_FUNC_DECL qua conjugate(qua const& q); + + /// Returns the q inverse. + /// + /// @tparam T A floating-point scalar type + /// @tparam Q A value from qualifier enum + template + GLM_FUNC_DECL qua inverse(qua const& q); + + /// Returns true if x holds a NaN (not a number) + /// representation in the underlying implementation's set of + /// floating point representations. Returns false otherwise, + /// including for implementations with no NaN + /// representations. + /// + /// /!\ When using compiler fast math, this function may fail. + /// + /// @tparam T A floating-point scalar type + /// @tparam Q A value from qualifier enum + template + GLM_FUNC_DECL vec<4, bool, Q> isnan(qua const& x); + + /// Returns true if x holds a positive infinity or negative + /// infinity representation in the underlying implementation's + /// set of floating point representations. Returns false + /// otherwise, including for implementations with no infinity + /// representations. + /// + /// @tparam T A floating-point scalar type + /// @tparam Q A value from qualifier enum + template + GLM_FUNC_DECL vec<4, bool, Q> isinf(qua const& x); + + /// @} +} //namespace glm + +#include "quaternion_common.inl" diff --git a/src/GLMath/glm/ext/quaternion_common.inl b/src/GLMath/glm/ext/quaternion_common.inl new file mode 100644 index 000000000..3b2846f36 --- /dev/null +++ b/src/GLMath/glm/ext/quaternion_common.inl @@ -0,0 +1,107 @@ +namespace glm +{ + template + GLM_FUNC_QUALIFIER qua mix(qua const& x, qua const& y, T a) + { + GLM_STATIC_ASSERT(std::numeric_limits::is_iec559, "'mix' only accept floating-point inputs"); + + T const cosTheta = dot(x, y); + + // Perform a linear interpolation when cosTheta is close to 1 to avoid side effect of sin(angle) becoming a zero denominator + if(cosTheta > static_cast(1) - epsilon()) + { + // Linear interpolation + return qua( + mix(x.w, y.w, a), + mix(x.x, y.x, a), + mix(x.y, y.y, a), + mix(x.z, y.z, a)); + } + else + { + // Essential Mathematics, page 467 + T angle = acos(cosTheta); + return (sin((static_cast(1) - a) * angle) * x + sin(a * angle) * y) / sin(angle); + } + } + + template + GLM_FUNC_QUALIFIER qua lerp(qua const& x, qua const& y, T a) + { + GLM_STATIC_ASSERT(std::numeric_limits::is_iec559, "'lerp' only accept floating-point inputs"); + + // Lerp is only defined in [0, 1] + assert(a >= static_cast(0)); + assert(a <= static_cast(1)); + + return x * (static_cast(1) - a) + (y * a); + } + + template + GLM_FUNC_QUALIFIER qua slerp(qua const& x, qua const& y, T a) + { + GLM_STATIC_ASSERT(std::numeric_limits::is_iec559, "'slerp' only accept floating-point inputs"); + + qua z = y; + + T cosTheta = dot(x, y); + + // If cosTheta < 0, the interpolation will take the long way around the sphere. + // To fix this, one quat must be negated. + if(cosTheta < static_cast(0)) + { + z = -y; + cosTheta = -cosTheta; + } + + // Perform a linear interpolation when cosTheta is close to 1 to avoid side effect of sin(angle) becoming a zero denominator + if(cosTheta > static_cast(1) - epsilon()) + { + // Linear interpolation + return qua( + mix(x.w, z.w, a), + mix(x.x, z.x, a), + mix(x.y, z.y, a), + mix(x.z, z.z, a)); + } + else + { + // Essential Mathematics, page 467 + T angle = acos(cosTheta); + return (sin((static_cast(1) - a) * angle) * x + sin(a * angle) * z) / sin(angle); + } + } + + template + GLM_FUNC_QUALIFIER qua conjugate(qua const& q) + { + return qua(q.w, -q.x, -q.y, -q.z); + } + + template + GLM_FUNC_QUALIFIER qua inverse(qua const& q) + { + return conjugate(q) / dot(q, q); + } + + template + GLM_FUNC_QUALIFIER vec<4, bool, Q> isnan(qua const& q) + { + GLM_STATIC_ASSERT(std::numeric_limits::is_iec559, "'isnan' only accept floating-point inputs"); + + return vec<4, bool, Q>(isnan(q.x), isnan(q.y), isnan(q.z), isnan(q.w)); + } + + template + GLM_FUNC_QUALIFIER vec<4, bool, Q> isinf(qua const& q) + { + GLM_STATIC_ASSERT(std::numeric_limits::is_iec559, "'isinf' only accept floating-point inputs"); + + return vec<4, bool, Q>(isinf(q.x), isinf(q.y), isinf(q.z), isinf(q.w)); + } +}//namespace glm + +#if GLM_CONFIG_SIMD == GLM_ENABLE +# include "quaternion_common_simd.inl" +#endif + diff --git a/src/GLMath/glm/ext/quaternion_common_simd.inl b/src/GLMath/glm/ext/quaternion_common_simd.inl new file mode 100644 index 000000000..ddfc8a44f --- /dev/null +++ b/src/GLMath/glm/ext/quaternion_common_simd.inl @@ -0,0 +1,18 @@ +#if GLM_ARCH & GLM_ARCH_SSE2_BIT + +namespace glm{ +namespace detail +{ + template + struct compute_dot, float, true> + { + static GLM_FUNC_QUALIFIER float call(qua const& x, qua const& y) + { + return _mm_cvtss_f32(glm_vec1_dot(x.data, y.data)); + } + }; +}//namespace detail +}//namespace glm + +#endif//GLM_ARCH & GLM_ARCH_SSE2_BIT + diff --git a/src/GLMath/glm/ext/quaternion_double.hpp b/src/GLMath/glm/ext/quaternion_double.hpp new file mode 100644 index 000000000..63b24de4d --- /dev/null +++ b/src/GLMath/glm/ext/quaternion_double.hpp @@ -0,0 +1,39 @@ +/// @ref ext_quaternion_double +/// @file glm/ext/quaternion_double.hpp +/// +/// @defgroup ext_quaternion_double GLM_EXT_quaternion_double +/// @ingroup ext +/// +/// Exposes double-precision floating point quaternion type. +/// +/// Include to use the features of this extension. +/// +/// @see ext_quaternion_float +/// @see ext_quaternion_double_precision +/// @see ext_quaternion_common +/// @see ext_quaternion_exponential +/// @see ext_quaternion_geometric +/// @see ext_quaternion_relational +/// @see ext_quaternion_transform +/// @see ext_quaternion_trigonometric + +#pragma once + +// Dependency: +#include "../detail/type_quat.hpp" + +#if GLM_MESSAGES == GLM_ENABLE && !defined(GLM_EXT_INCLUDED) +# pragma message("GLM: GLM_EXT_quaternion_double extension included") +#endif + +namespace glm +{ + /// @addtogroup ext_quaternion_double + /// @{ + + /// Quaternion of double-precision floating-point numbers. + typedef qua dquat; + + /// @} +} //namespace glm + diff --git a/src/GLMath/glm/ext/quaternion_double_precision.hpp b/src/GLMath/glm/ext/quaternion_double_precision.hpp new file mode 100644 index 000000000..8aa24a177 --- /dev/null +++ b/src/GLMath/glm/ext/quaternion_double_precision.hpp @@ -0,0 +1,42 @@ +/// @ref ext_quaternion_double_precision +/// @file glm/ext/quaternion_double_precision.hpp +/// +/// @defgroup ext_quaternion_double_precision GLM_EXT_quaternion_double_precision +/// @ingroup ext +/// +/// Exposes double-precision floating point quaternion type with various precision in term of ULPs. +/// +/// Include to use the features of this extension. + +#pragma once + +// Dependency: +#include "../detail/type_quat.hpp" + +#if GLM_MESSAGES == GLM_ENABLE && !defined(GLM_EXT_INCLUDED) +# pragma message("GLM: GLM_EXT_quaternion_double_precision extension included") +#endif + +namespace glm +{ + /// @addtogroup ext_quaternion_double_precision + /// @{ + + /// Quaternion of double-precision floating-point numbers using high precision arithmetic in term of ULPs. + /// + /// @see ext_quaternion_double_precision + typedef qua lowp_dquat; + + /// Quaternion of medium double-qualifier floating-point numbers using high precision arithmetic in term of ULPs. + /// + /// @see ext_quaternion_double_precision + typedef qua mediump_dquat; + + /// Quaternion of high double-qualifier floating-point numbers using high precision arithmetic in term of ULPs. + /// + /// @see ext_quaternion_double_precision + typedef qua highp_dquat; + + /// @} +} //namespace glm + diff --git a/src/GLMath/glm/ext/quaternion_exponential.hpp b/src/GLMath/glm/ext/quaternion_exponential.hpp new file mode 100644 index 000000000..affe2979a --- /dev/null +++ b/src/GLMath/glm/ext/quaternion_exponential.hpp @@ -0,0 +1,63 @@ +/// @ref ext_quaternion_exponential +/// @file glm/ext/quaternion_exponential.hpp +/// +/// @defgroup ext_quaternion_exponential GLM_EXT_quaternion_exponential +/// @ingroup ext +/// +/// Provides exponential functions for quaternion types +/// +/// Include to use the features of this extension. +/// +/// @see core_exponential +/// @see ext_quaternion_float +/// @see ext_quaternion_double + +#pragma once + +// Dependency: +#include "../common.hpp" +#include "../trigonometric.hpp" +#include "../geometric.hpp" +#include "../ext/scalar_constants.hpp" + +#if GLM_MESSAGES == GLM_ENABLE && !defined(GLM_EXT_INCLUDED) +# pragma message("GLM: GLM_EXT_quaternion_exponential extension included") +#endif + +namespace glm +{ + /// @addtogroup ext_quaternion_transform + /// @{ + + /// Returns a exponential of a quaternion. + /// + /// @tparam T A floating-point scalar type + /// @tparam Q A value from qualifier enum + template + GLM_FUNC_DECL qua exp(qua const& q); + + /// Returns a logarithm of a quaternion + /// + /// @tparam T A floating-point scalar type + /// @tparam Q A value from qualifier enum + template + GLM_FUNC_DECL qua log(qua const& q); + + /// Returns a quaternion raised to a power. + /// + /// @tparam T A floating-point scalar type + /// @tparam Q A value from qualifier enum + template + GLM_FUNC_DECL qua pow(qua const& q, T y); + + /// Returns the square root of a quaternion + /// + /// @tparam T A floating-point scalar type + /// @tparam Q A value from qualifier enum + template + GLM_FUNC_DECL qua sqrt(qua const& q); + + /// @} +} //namespace glm + +#include "quaternion_exponential.inl" diff --git a/src/GLMath/glm/ext/quaternion_exponential.inl b/src/GLMath/glm/ext/quaternion_exponential.inl new file mode 100644 index 000000000..1365d0a96 --- /dev/null +++ b/src/GLMath/glm/ext/quaternion_exponential.inl @@ -0,0 +1,69 @@ +namespace glm +{ + template + GLM_FUNC_QUALIFIER qua exp(qua const& q) + { + vec<3, T, Q> u(q.x, q.y, q.z); + T const Angle = glm::length(u); + if (Angle < epsilon()) + return qua(); + + vec<3, T, Q> const v(u / Angle); + return qua(cos(Angle), sin(Angle) * v); + } + + template + GLM_FUNC_QUALIFIER qua log(qua const& q) + { + vec<3, T, Q> u(q.x, q.y, q.z); + T Vec3Len = length(u); + + if (Vec3Len < epsilon()) + { + if(q.w > static_cast(0)) + return qua(log(q.w), static_cast(0), static_cast(0), static_cast(0)); + else if(q.w < static_cast(0)) + return qua(log(-q.w), pi(), static_cast(0), static_cast(0)); + else + return qua(std::numeric_limits::infinity(), std::numeric_limits::infinity(), std::numeric_limits::infinity(), std::numeric_limits::infinity()); + } + else + { + T t = atan(Vec3Len, T(q.w)) / Vec3Len; + T QuatLen2 = Vec3Len * Vec3Len + q.w * q.w; + return qua(static_cast(0.5) * log(QuatLen2), t * q.x, t * q.y, t * q.z); + } + } + + template + GLM_FUNC_QUALIFIER qua pow(qua const& x, T y) + { + //Raising to the power of 0 should yield 1 + //Needed to prevent a division by 0 error later on + if(y > -epsilon() && y < epsilon()) + return qua(1,0,0,0); + + //To deal with non-unit quaternions + T magnitude = sqrt(x.x * x.x + x.y * x.y + x.z * x.z + x.w *x.w); + + //Equivalent to raising a real number to a power + //Needed to prevent a division by 0 error later on + if(abs(x.w / magnitude) > static_cast(1) - epsilon() && abs(x.w / magnitude) < static_cast(1) + epsilon()) + return qua(pow(x.w, y), 0, 0, 0); + + T Angle = acos(x.w / magnitude); + T NewAngle = Angle * y; + T Div = sin(NewAngle) / sin(Angle); + T Mag = pow(magnitude, y - static_cast(1)); + + return qua(cos(NewAngle) * magnitude * Mag, x.x * Div * Mag, x.y * Div * Mag, x.z * Div * Mag); + } + + template + GLM_FUNC_QUALIFIER qua sqrt(qua const& x) + { + return pow(x, static_cast(0.5)); + } +}//namespace glm + + diff --git a/src/GLMath/glm/ext/quaternion_float.hpp b/src/GLMath/glm/ext/quaternion_float.hpp new file mode 100644 index 000000000..ca42a6059 --- /dev/null +++ b/src/GLMath/glm/ext/quaternion_float.hpp @@ -0,0 +1,39 @@ +/// @ref ext_quaternion_float +/// @file glm/ext/quaternion_float.hpp +/// +/// @defgroup ext_quaternion_float GLM_EXT_quaternion_float +/// @ingroup ext +/// +/// Exposes single-precision floating point quaternion type. +/// +/// Include to use the features of this extension. +/// +/// @see ext_quaternion_double +/// @see ext_quaternion_float_precision +/// @see ext_quaternion_common +/// @see ext_quaternion_exponential +/// @see ext_quaternion_geometric +/// @see ext_quaternion_relational +/// @see ext_quaternion_transform +/// @see ext_quaternion_trigonometric + +#pragma once + +// Dependency: +#include "../detail/type_quat.hpp" + +#if GLM_MESSAGES == GLM_ENABLE && !defined(GLM_EXT_INCLUDED) +# pragma message("GLM: GLM_EXT_quaternion_float extension included") +#endif + +namespace glm +{ + /// @addtogroup ext_quaternion_float + /// @{ + + /// Quaternion of single-precision floating-point numbers. + typedef qua quat; + + /// @} +} //namespace glm + diff --git a/src/GLMath/glm/ext/quaternion_float_precision.hpp b/src/GLMath/glm/ext/quaternion_float_precision.hpp new file mode 100644 index 000000000..f9e4f5c21 --- /dev/null +++ b/src/GLMath/glm/ext/quaternion_float_precision.hpp @@ -0,0 +1,36 @@ +/// @ref ext_quaternion_float_precision +/// @file glm/ext/quaternion_float_precision.hpp +/// +/// @defgroup ext_quaternion_float_precision GLM_EXT_quaternion_float_precision +/// @ingroup ext +/// +/// Exposes single-precision floating point quaternion type with various precision in term of ULPs. +/// +/// Include to use the features of this extension. + +#pragma once + +// Dependency: +#include "../detail/type_quat.hpp" + +#if GLM_MESSAGES == GLM_ENABLE && !defined(GLM_EXT_INCLUDED) +# pragma message("GLM: GLM_EXT_quaternion_float_precision extension included") +#endif + +namespace glm +{ + /// @addtogroup ext_quaternion_float_precision + /// @{ + + /// Quaternion of single-precision floating-point numbers using high precision arithmetic in term of ULPs. + typedef qua lowp_quat; + + /// Quaternion of single-precision floating-point numbers using high precision arithmetic in term of ULPs. + typedef qua mediump_quat; + + /// Quaternion of single-precision floating-point numbers using high precision arithmetic in term of ULPs. + typedef qua highp_quat; + + /// @} +} //namespace glm + diff --git a/src/GLMath/glm/ext/quaternion_geometric.hpp b/src/GLMath/glm/ext/quaternion_geometric.hpp new file mode 100644 index 000000000..6d98bbe93 --- /dev/null +++ b/src/GLMath/glm/ext/quaternion_geometric.hpp @@ -0,0 +1,70 @@ +/// @ref ext_quaternion_geometric +/// @file glm/ext/quaternion_geometric.hpp +/// +/// @defgroup ext_quaternion_geometric GLM_EXT_quaternion_geometric +/// @ingroup ext +/// +/// Provides geometric functions for quaternion types +/// +/// Include to use the features of this extension. +/// +/// @see core_geometric +/// @see ext_quaternion_float +/// @see ext_quaternion_double + +#pragma once + +// Dependency: +#include "../geometric.hpp" +#include "../exponential.hpp" +#include "../ext/vector_relational.hpp" + +#if GLM_MESSAGES == GLM_ENABLE && !defined(GLM_EXT_INCLUDED) +# pragma message("GLM: GLM_EXT_quaternion_geometric extension included") +#endif + +namespace glm +{ + /// @addtogroup ext_quaternion_geometric + /// @{ + + /// Returns the norm of a quaternions + /// + /// @tparam T Floating-point scalar types + /// @tparam Q Value from qualifier enum + /// + /// @see ext_quaternion_geometric + template + GLM_FUNC_DECL T length(qua const& q); + + /// Returns the normalized quaternion. + /// + /// @tparam T Floating-point scalar types + /// @tparam Q Value from qualifier enum + /// + /// @see ext_quaternion_geometric + template + GLM_FUNC_DECL qua normalize(qua const& q); + + /// Returns dot product of q1 and q2, i.e., q1[0] * q2[0] + q1[1] * q2[1] + ... + /// + /// @tparam T Floating-point scalar types. + /// @tparam Q Value from qualifier enum + /// + /// @see ext_quaternion_geometric + template + GLM_FUNC_DECL T dot(qua const& x, qua const& y); + + /// Compute a cross product. + /// + /// @tparam T Floating-point scalar types + /// @tparam Q Value from qualifier enum + /// + /// @see ext_quaternion_geometric + template + GLM_FUNC_QUALIFIER qua cross(qua const& q1, qua const& q2); + + /// @} +} //namespace glm + +#include "quaternion_geometric.inl" diff --git a/src/GLMath/glm/ext/quaternion_geometric.inl b/src/GLMath/glm/ext/quaternion_geometric.inl new file mode 100644 index 000000000..e155ac521 --- /dev/null +++ b/src/GLMath/glm/ext/quaternion_geometric.inl @@ -0,0 +1,36 @@ +namespace glm +{ + template + GLM_FUNC_QUALIFIER T dot(qua const& x, qua const& y) + { + GLM_STATIC_ASSERT(std::numeric_limits::is_iec559, "'dot' accepts only floating-point inputs"); + return detail::compute_dot, T, detail::is_aligned::value>::call(x, y); + } + + template + GLM_FUNC_QUALIFIER T length(qua const& q) + { + return glm::sqrt(dot(q, q)); + } + + template + GLM_FUNC_QUALIFIER qua normalize(qua const& q) + { + T len = length(q); + if(len <= static_cast(0)) // Problem + return qua(static_cast(1), static_cast(0), static_cast(0), static_cast(0)); + T oneOverLen = static_cast(1) / len; + return qua(q.w * oneOverLen, q.x * oneOverLen, q.y * oneOverLen, q.z * oneOverLen); + } + + template + GLM_FUNC_QUALIFIER qua cross(qua const& q1, qua const& q2) + { + return qua( + q1.w * q2.w - q1.x * q2.x - q1.y * q2.y - q1.z * q2.z, + q1.w * q2.x + q1.x * q2.w + q1.y * q2.z - q1.z * q2.y, + q1.w * q2.y + q1.y * q2.w + q1.z * q2.x - q1.x * q2.z, + q1.w * q2.z + q1.z * q2.w + q1.x * q2.y - q1.y * q2.x); + } +}//namespace glm + diff --git a/src/GLMath/glm/ext/quaternion_relational.hpp b/src/GLMath/glm/ext/quaternion_relational.hpp new file mode 100644 index 000000000..7aa121da0 --- /dev/null +++ b/src/GLMath/glm/ext/quaternion_relational.hpp @@ -0,0 +1,62 @@ +/// @ref ext_quaternion_relational +/// @file glm/ext/quaternion_relational.hpp +/// +/// @defgroup ext_quaternion_relational GLM_EXT_quaternion_relational +/// @ingroup ext +/// +/// Exposes comparison functions for quaternion types that take a user defined epsilon values. +/// +/// Include to use the features of this extension. +/// +/// @see core_vector_relational +/// @see ext_vector_relational +/// @see ext_matrix_relational +/// @see ext_quaternion_float +/// @see ext_quaternion_double + +#pragma once + +// Dependency: +#include "../vector_relational.hpp" + +#if GLM_MESSAGES == GLM_ENABLE && !defined(GLM_EXT_INCLUDED) +# pragma message("GLM: GLM_EXT_quaternion_relational extension included") +#endif + +namespace glm +{ + /// @addtogroup ext_quaternion_relational + /// @{ + + /// Returns the component-wise comparison of result x == y. + /// + /// @tparam T Floating-point scalar types + /// @tparam Q Value from qualifier enum + template + GLM_FUNC_DECL vec<4, bool, Q> equal(qua const& x, qua const& y); + + /// Returns the component-wise comparison of |x - y| < epsilon. + /// + /// @tparam T Floating-point scalar types + /// @tparam Q Value from qualifier enum + template + GLM_FUNC_DECL vec<4, bool, Q> equal(qua const& x, qua const& y, T epsilon); + + /// Returns the component-wise comparison of result x != y. + /// + /// @tparam T Floating-point scalar types + /// @tparam Q Value from qualifier enum + template + GLM_FUNC_DECL vec<4, bool, Q> notEqual(qua const& x, qua const& y); + + /// Returns the component-wise comparison of |x - y| >= epsilon. + /// + /// @tparam T Floating-point scalar types + /// @tparam Q Value from qualifier enum + template + GLM_FUNC_DECL vec<4, bool, Q> notEqual(qua const& x, qua const& y, T epsilon); + + /// @} +} //namespace glm + +#include "quaternion_relational.inl" diff --git a/src/GLMath/glm/ext/quaternion_relational.inl b/src/GLMath/glm/ext/quaternion_relational.inl new file mode 100644 index 000000000..b1713e95c --- /dev/null +++ b/src/GLMath/glm/ext/quaternion_relational.inl @@ -0,0 +1,35 @@ +namespace glm +{ + template + GLM_FUNC_QUALIFIER vec<4, bool, Q> equal(qua const& x, qua const& y) + { + vec<4, bool, Q> Result; + for(length_t i = 0; i < x.length(); ++i) + Result[i] = x[i] == y[i]; + return Result; + } + + template + GLM_FUNC_QUALIFIER vec<4, bool, Q> equal(qua const& x, qua const& y, T epsilon) + { + vec<4, T, Q> v(x.x - y.x, x.y - y.y, x.z - y.z, x.w - y.w); + return lessThan(abs(v), vec<4, T, Q>(epsilon)); + } + + template + GLM_FUNC_QUALIFIER vec<4, bool, Q> notEqual(qua const& x, qua const& y) + { + vec<4, bool, Q> Result; + for(length_t i = 0; i < x.length(); ++i) + Result[i] = x[i] != y[i]; + return Result; + } + + template + GLM_FUNC_QUALIFIER vec<4, bool, Q> notEqual(qua const& x, qua const& y, T epsilon) + { + vec<4, T, Q> v(x.x - y.x, x.y - y.y, x.z - y.z, x.w - y.w); + return greaterThanEqual(abs(v), vec<4, T, Q>(epsilon)); + } +}//namespace glm + diff --git a/src/GLMath/glm/ext/quaternion_transform.hpp b/src/GLMath/glm/ext/quaternion_transform.hpp new file mode 100644 index 000000000..a9cc5c2b5 --- /dev/null +++ b/src/GLMath/glm/ext/quaternion_transform.hpp @@ -0,0 +1,47 @@ +/// @ref ext_quaternion_transform +/// @file glm/ext/quaternion_transform.hpp +/// +/// @defgroup ext_quaternion_transform GLM_EXT_quaternion_transform +/// @ingroup ext +/// +/// Provides transformation functions for quaternion types +/// +/// Include to use the features of this extension. +/// +/// @see ext_quaternion_float +/// @see ext_quaternion_double +/// @see ext_quaternion_exponential +/// @see ext_quaternion_geometric +/// @see ext_quaternion_relational +/// @see ext_quaternion_trigonometric + +#pragma once + +// Dependency: +#include "../common.hpp" +#include "../trigonometric.hpp" +#include "../geometric.hpp" + +#if GLM_MESSAGES == GLM_ENABLE && !defined(GLM_EXT_INCLUDED) +# pragma message("GLM: GLM_EXT_quaternion_transform extension included") +#endif + +namespace glm +{ + /// @addtogroup ext_quaternion_transform + /// @{ + + /// Rotates a quaternion from a vector of 3 components axis and an angle. + /// + /// @param q Source orientation + /// @param angle Angle expressed in radians. + /// @param axis Axis of the rotation + /// + /// @tparam T Floating-point scalar types + /// @tparam Q Value from qualifier enum + template + GLM_FUNC_DECL qua rotate(qua const& q, T const& angle, vec<3, T, Q> const& axis); + /// @} +} //namespace glm + +#include "quaternion_transform.inl" diff --git a/src/GLMath/glm/ext/quaternion_transform.inl b/src/GLMath/glm/ext/quaternion_transform.inl new file mode 100644 index 000000000..b87ecb65d --- /dev/null +++ b/src/GLMath/glm/ext/quaternion_transform.inl @@ -0,0 +1,24 @@ +namespace glm +{ + template + GLM_FUNC_QUALIFIER qua rotate(qua const& q, T const& angle, vec<3, T, Q> const& v) + { + vec<3, T, Q> Tmp = v; + + // Axis of rotation must be normalised + T len = glm::length(Tmp); + if(abs(len - static_cast(1)) > static_cast(0.001)) + { + T oneOverLen = static_cast(1) / len; + Tmp.x *= oneOverLen; + Tmp.y *= oneOverLen; + Tmp.z *= oneOverLen; + } + + T const AngleRad(angle); + T const Sin = sin(AngleRad * static_cast(0.5)); + + return q * qua(cos(AngleRad * static_cast(0.5)), Tmp.x * Sin, Tmp.y * Sin, Tmp.z * Sin); + } +}//namespace glm + diff --git a/src/GLMath/glm/ext/quaternion_trigonometric.hpp b/src/GLMath/glm/ext/quaternion_trigonometric.hpp new file mode 100644 index 000000000..76cea27ad --- /dev/null +++ b/src/GLMath/glm/ext/quaternion_trigonometric.hpp @@ -0,0 +1,63 @@ +/// @ref ext_quaternion_trigonometric +/// @file glm/ext/quaternion_trigonometric.hpp +/// +/// @defgroup ext_quaternion_trigonometric GLM_EXT_quaternion_trigonometric +/// @ingroup ext +/// +/// Provides trigonometric functions for quaternion types +/// +/// Include to use the features of this extension. +/// +/// @see ext_quaternion_float +/// @see ext_quaternion_double +/// @see ext_quaternion_exponential +/// @see ext_quaternion_geometric +/// @see ext_quaternion_relational +/// @see ext_quaternion_transform + +#pragma once + +// Dependency: +#include "../trigonometric.hpp" +#include "../exponential.hpp" +#include "scalar_constants.hpp" +#include "vector_relational.hpp" +#include + +#if GLM_MESSAGES == GLM_ENABLE && !defined(GLM_EXT_INCLUDED) +# pragma message("GLM: GLM_EXT_quaternion_trigonometric extension included") +#endif + +namespace glm +{ + /// @addtogroup ext_quaternion_trigonometric + /// @{ + + /// Returns the quaternion rotation angle. + /// + /// @tparam T A floating-point scalar type + /// @tparam Q A value from qualifier enum + template + GLM_FUNC_DECL T angle(qua const& x); + + /// Returns the q rotation axis. + /// + /// @tparam T A floating-point scalar type + /// @tparam Q A value from qualifier enum + template + GLM_FUNC_DECL vec<3, T, Q> axis(qua const& x); + + /// Build a quaternion from an angle and a normalized axis. + /// + /// @param angle Angle expressed in radians. + /// @param axis Axis of the quaternion, must be normalized. + /// + /// @tparam T A floating-point scalar type + /// @tparam Q A value from qualifier enum + template + GLM_FUNC_DECL qua angleAxis(T const& angle, vec<3, T, Q> const& axis); + + /// @} +} //namespace glm + +#include "quaternion_trigonometric.inl" diff --git a/src/GLMath/glm/ext/quaternion_trigonometric.inl b/src/GLMath/glm/ext/quaternion_trigonometric.inl new file mode 100644 index 000000000..0c0bf63f5 --- /dev/null +++ b/src/GLMath/glm/ext/quaternion_trigonometric.inl @@ -0,0 +1,27 @@ +namespace glm +{ + template + GLM_FUNC_QUALIFIER T angle(qua const& x) + { + return acos(x.w) * static_cast(2); + } + + template + GLM_FUNC_QUALIFIER vec<3, T, Q> axis(qua const& x) + { + T const tmp1 = static_cast(1) - x.w * x.w; + if(tmp1 <= static_cast(0)) + return vec<3, T, Q>(0, 0, 1); + T const tmp2 = static_cast(1) / sqrt(tmp1); + return vec<3, T, Q>(x.x * tmp2, x.y * tmp2, x.z * tmp2); + } + + template + GLM_FUNC_QUALIFIER qua angleAxis(T const& angle, vec<3, T, Q> const& v) + { + T const a(angle); + T const s = glm::sin(a * static_cast(0.5)); + + return qua(glm::cos(a * static_cast(0.5)), v * s); + } +}//namespace glm diff --git a/src/GLMath/glm/ext/scalar_common.hpp b/src/GLMath/glm/ext/scalar_common.hpp new file mode 100644 index 000000000..4ab0f88be --- /dev/null +++ b/src/GLMath/glm/ext/scalar_common.hpp @@ -0,0 +1,103 @@ +/// @ref ext_scalar_common +/// @file glm/ext/scalar_common.hpp +/// +/// @defgroup ext_scalar_common GLM_EXT_scalar_common +/// @ingroup ext +/// +/// Exposes min and max functions for 3 to 4 scalar parameters. +/// +/// Include to use the features of this extension. +/// +/// @see core_func_common +/// @see ext_vector_common + +#pragma once + +// Dependency: +#include "../common.hpp" + +#if GLM_MESSAGES == GLM_ENABLE && !defined(GLM_EXT_INCLUDED) +# pragma message("GLM: GLM_EXT_scalar_common extension included") +#endif + +namespace glm +{ + /// @addtogroup ext_scalar_common + /// @{ + + /// Returns the minimum component-wise values of 3 inputs + /// + /// @tparam T A floating-point scalar type. + template + GLM_FUNC_DECL T min(T a, T b, T c); + + /// Returns the minimum component-wise values of 4 inputs + /// + /// @tparam T A floating-point scalar type. + template + GLM_FUNC_DECL T min(T a, T b, T c, T d); + + /// Returns the maximum component-wise values of 3 inputs + /// + /// @tparam T A floating-point scalar type. + template + GLM_FUNC_DECL T max(T a, T b, T c); + + /// Returns the maximum component-wise values of 4 inputs + /// + /// @tparam T A floating-point scalar type. + template + GLM_FUNC_DECL T max(T a, T b, T c, T d); + + /// Returns the minimum component-wise values of 2 inputs. If one of the two arguments is NaN, the value of the other argument is returned. + /// + /// @tparam T A floating-point scalar type. + /// + /// @see std::fmin documentation + template + GLM_FUNC_DECL T fmin(T a, T b); + + /// Returns the minimum component-wise values of 3 inputs. If one of the two arguments is NaN, the value of the other argument is returned. + /// + /// @tparam T A floating-point scalar type. + /// + /// @see std::fmin documentation + template + GLM_FUNC_DECL T fmin(T a, T b, T c); + + /// Returns the minimum component-wise values of 4 inputs. If one of the two arguments is NaN, the value of the other argument is returned. + /// + /// @tparam T A floating-point scalar type. + /// + /// @see std::fmin documentation + template + GLM_FUNC_DECL T fmin(T a, T b, T c, T d); + + /// Returns the maximum component-wise values of 2 inputs. If one of the two arguments is NaN, the value of the other argument is returned. + /// + /// @tparam T A floating-point scalar type. + /// + /// @see std::fmax documentation + template + GLM_FUNC_DECL T fmax(T a, T b); + + /// Returns the maximum component-wise values of 3 inputs. If one of the two arguments is NaN, the value of the other argument is returned. + /// + /// @tparam T A floating-point scalar type. + /// + /// @see std::fmax documentation + template + GLM_FUNC_DECL T fmax(T a, T b, T C); + + /// Returns the maximum component-wise values of 4 inputs. If one of the two arguments is NaN, the value of the other argument is returned. + /// + /// @tparam T A floating-point scalar type. + /// + /// @see std::fmax documentation + template + GLM_FUNC_DECL T fmax(T a, T b, T C, T D); + + /// @} +}//namespace glm + +#include "scalar_common.inl" diff --git a/src/GLMath/glm/ext/scalar_common.inl b/src/GLMath/glm/ext/scalar_common.inl new file mode 100644 index 000000000..118a670ea --- /dev/null +++ b/src/GLMath/glm/ext/scalar_common.inl @@ -0,0 +1,115 @@ +namespace glm +{ + template + GLM_FUNC_QUALIFIER T min(T a, T b, T c) + { + return glm::min(glm::min(a, b), c); + } + + template + GLM_FUNC_QUALIFIER T min(T a, T b, T c, T d) + { + return glm::min(glm::min(a, b), glm::min(c, d)); + } + + template + GLM_FUNC_QUALIFIER T max(T a, T b, T c) + { + return glm::max(glm::max(a, b), c); + } + + template + GLM_FUNC_QUALIFIER T max(T a, T b, T c, T d) + { + return glm::max(glm::max(a, b), glm::max(c, d)); + } + +# if GLM_HAS_CXX11_STL + using std::fmin; +# else + template + GLM_FUNC_QUALIFIER T fmin(T a, T b) + { + GLM_STATIC_ASSERT(std::numeric_limits::is_iec559, "'fmin' only accept floating-point input"); + + if (isnan(a)) + return b; + return min(a, b); + } +# endif + + template + GLM_FUNC_QUALIFIER T fmin(T a, T b, T c) + { + GLM_STATIC_ASSERT(std::numeric_limits::is_iec559, "'fmin' only accept floating-point input"); + + if (isnan(a)) + return fmin(b, c); + if (isnan(b)) + return fmin(a, c); + if (isnan(c)) + return min(a, b); + return min(a, b, c); + } + + template + GLM_FUNC_QUALIFIER T fmin(T a, T b, T c, T d) + { + GLM_STATIC_ASSERT(std::numeric_limits::is_iec559, "'fmin' only accept floating-point input"); + + if (isnan(a)) + return fmin(b, c, d); + if (isnan(b)) + return min(a, fmin(c, d)); + if (isnan(c)) + return fmin(min(a, b), d); + if (isnan(d)) + return min(a, b, c); + return min(a, b, c, d); + } + + +# if GLM_HAS_CXX11_STL + using std::fmax; +# else + template + GLM_FUNC_QUALIFIER T fmax(T a, T b) + { + GLM_STATIC_ASSERT(std::numeric_limits::is_iec559, "'fmax' only accept floating-point input"); + + if (isnan(a)) + return b; + return max(a, b); + } +# endif + + template + GLM_FUNC_QUALIFIER T fmax(T a, T b, T c) + { + GLM_STATIC_ASSERT(std::numeric_limits::is_iec559, "'fmax' only accept floating-point input"); + + if (isnan(a)) + return fmax(b, c); + if (isnan(b)) + return fmax(a, c); + if (isnan(c)) + return max(a, b); + return max(a, b, c); + } + + template + GLM_FUNC_QUALIFIER T fmax(T a, T b, T c, T d) + { + GLM_STATIC_ASSERT(std::numeric_limits::is_iec559, "'fmax' only accept floating-point input"); + + if (isnan(a)) + return fmax(b, c, d); + if (isnan(b)) + return max(a, fmax(c, d)); + if (isnan(c)) + return fmax(max(a, b), d); + if (isnan(d)) + return max(a, b, c); + return max(a, b, c, d); + } +}//namespace glm diff --git a/src/GLMath/glm/ext/scalar_constants.hpp b/src/GLMath/glm/ext/scalar_constants.hpp new file mode 100644 index 000000000..4a61faa5f --- /dev/null +++ b/src/GLMath/glm/ext/scalar_constants.hpp @@ -0,0 +1,36 @@ +/// @ref ext_scalar_constants +/// @file glm/ext/scalar_constants.hpp +/// +/// @defgroup ext_scalar_constants GLM_EXT_scalar_constants +/// @ingroup ext +/// +/// Provides a list of constants and precomputed useful values. +/// +/// Include to use the features of this extension. + +#pragma once + +// Dependencies +#include "../detail/setup.hpp" + +#if GLM_MESSAGES == GLM_ENABLE && !defined(GLM_EXT_INCLUDED) +# pragma message("GLM: GLM_EXT_scalar_constants extension included") +#endif + +namespace glm +{ + /// @addtogroup ext_scalar_constants + /// @{ + + /// Return the epsilon constant for floating point types. + template + GLM_FUNC_DECL GLM_CONSTEXPR genType epsilon(); + + /// Return the pi constant for floating point types. + template + GLM_FUNC_DECL GLM_CONSTEXPR genType pi(); + + /// @} +} //namespace glm + +#include "scalar_constants.inl" diff --git a/src/GLMath/glm/ext/scalar_constants.inl b/src/GLMath/glm/ext/scalar_constants.inl new file mode 100644 index 000000000..c075fbc7d --- /dev/null +++ b/src/GLMath/glm/ext/scalar_constants.inl @@ -0,0 +1,18 @@ +#include + +namespace glm +{ + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR genType epsilon() + { + GLM_STATIC_ASSERT(std::numeric_limits::is_iec559, "'epsilon' only accepts floating-point inputs"); + return std::numeric_limits::epsilon(); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR genType pi() + { + GLM_STATIC_ASSERT(std::numeric_limits::is_iec559, "'epsilon' only accepts floating-point inputs"); + return static_cast(3.14159265358979323846264338327950288); + } +} //namespace glm diff --git a/src/GLMath/glm/ext/scalar_int_sized.hpp b/src/GLMath/glm/ext/scalar_int_sized.hpp new file mode 100644 index 000000000..8e9c511c9 --- /dev/null +++ b/src/GLMath/glm/ext/scalar_int_sized.hpp @@ -0,0 +1,70 @@ +/// @ref ext_scalar_int_sized +/// @file glm/ext/scalar_int_sized.hpp +/// +/// @defgroup ext_scalar_int_sized GLM_EXT_scalar_int_sized +/// @ingroup ext +/// +/// Exposes sized signed integer scalar types. +/// +/// Include to use the features of this extension. +/// +/// @see ext_scalar_uint_sized + +#pragma once + +#include "../detail/setup.hpp" + +#if GLM_MESSAGES == GLM_ENABLE && !defined(GLM_EXT_INCLUDED) +# pragma message("GLM: GLM_EXT_scalar_int_sized extension included") +#endif + +namespace glm{ +namespace detail +{ +# if GLM_HAS_EXTENDED_INTEGER_TYPE + typedef std::int8_t int8; + typedef std::int16_t int16; + typedef std::int32_t int32; +# else + typedef signed char int8; + typedef signed short int16; + typedef signed int int32; +#endif// + + template<> + struct is_int + { + enum test {value = ~0}; + }; + + template<> + struct is_int + { + enum test {value = ~0}; + }; + + template<> + struct is_int + { + enum test {value = ~0}; + }; +}//namespace detail + + + /// @addtogroup ext_scalar_int_sized + /// @{ + + /// 8 bit signed integer type. + typedef detail::int8 int8; + + /// 16 bit signed integer type. + typedef detail::int16 int16; + + /// 32 bit signed integer type. + typedef detail::int32 int32; + + /// 64 bit signed integer type. + typedef detail::int64 int64; + + /// @} +}//namespace glm diff --git a/src/GLMath/glm/ext/scalar_relational.hpp b/src/GLMath/glm/ext/scalar_relational.hpp new file mode 100644 index 000000000..3076a5e63 --- /dev/null +++ b/src/GLMath/glm/ext/scalar_relational.hpp @@ -0,0 +1,65 @@ +/// @ref ext_scalar_relational +/// @file glm/ext/scalar_relational.hpp +/// +/// @defgroup ext_scalar_relational GLM_EXT_scalar_relational +/// @ingroup ext +/// +/// Exposes comparison functions for scalar types that take a user defined epsilon values. +/// +/// Include to use the features of this extension. +/// +/// @see core_vector_relational +/// @see ext_vector_relational +/// @see ext_matrix_relational + +#pragma once + +// Dependencies +#include "../detail/qualifier.hpp" + +#if GLM_MESSAGES == GLM_ENABLE && !defined(GLM_EXT_INCLUDED) +# pragma message("GLM: GLM_EXT_scalar_relational extension included") +#endif + +namespace glm +{ + /// Returns the component-wise comparison of |x - y| < epsilon. + /// True if this expression is satisfied. + /// + /// @tparam genType Floating-point or integer scalar types + template + GLM_FUNC_DECL GLM_CONSTEXPR bool equal(genType const& x, genType const& y, genType const& epsilon); + + /// Returns the component-wise comparison of |x - y| >= epsilon. + /// True if this expression is not satisfied. + /// + /// @tparam genType Floating-point or integer scalar types + template + GLM_FUNC_DECL GLM_CONSTEXPR bool notEqual(genType const& x, genType const& y, genType const& epsilon); + + /// Returns the component-wise comparison between two scalars in term of ULPs. + /// True if this expression is satisfied. + /// + /// @param x First operand. + /// @param y Second operand. + /// @param ULPs Maximum difference in ULPs between the two operators to consider them equal. + /// + /// @tparam genType Floating-point or integer scalar types + template + GLM_FUNC_DECL GLM_CONSTEXPR bool equal(genType const& x, genType const& y, int ULPs); + + /// Returns the component-wise comparison between two scalars in term of ULPs. + /// True if this expression is not satisfied. + /// + /// @param x First operand. + /// @param y Second operand. + /// @param ULPs Maximum difference in ULPs between the two operators to consider them not equal. + /// + /// @tparam genType Floating-point or integer scalar types + template + GLM_FUNC_DECL GLM_CONSTEXPR bool notEqual(genType const& x, genType const& y, int ULPs); + + /// @} +}//namespace glm + +#include "scalar_relational.inl" diff --git a/src/GLMath/glm/ext/scalar_relational.inl b/src/GLMath/glm/ext/scalar_relational.inl new file mode 100644 index 000000000..27370e144 --- /dev/null +++ b/src/GLMath/glm/ext/scalar_relational.inl @@ -0,0 +1,43 @@ +#include "../common.hpp" +#include "../ext/scalar_int_sized.hpp" +#include "../ext/scalar_uint_sized.hpp" +#include "../detail/type_float.hpp" + +namespace glm +{ + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR bool equal(genType const& x, genType const& y, genType const& epsilon) + { + return abs(x - y) <= epsilon; + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR bool notEqual(genType const& x, genType const& y, genType const& epsilon) + { + return abs(x - y) > epsilon; + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR bool equal(genType const& x, genType const& y, int MaxULPs) + { + detail::float_t const a(x); + detail::float_t const b(y); + + // Different signs means they do not match. + if(a.negative() != b.negative()) + { + // Check for equality to make sure +0==-0 + return a.mantissa() == b.mantissa() && a.exponent() == b.exponent(); + } + + // Find the difference in ULPs. + typename detail::float_t::int_type const DiffULPs = abs(a.i - b.i); + return DiffULPs <= MaxULPs; + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR bool notEqual(genType const& x, genType const& y, int ULPs) + { + return !equal(x, y, ULPs); + } +}//namespace glm diff --git a/src/GLMath/glm/ext/scalar_uint_sized.hpp b/src/GLMath/glm/ext/scalar_uint_sized.hpp new file mode 100644 index 000000000..fd5267fad --- /dev/null +++ b/src/GLMath/glm/ext/scalar_uint_sized.hpp @@ -0,0 +1,70 @@ +/// @ref ext_scalar_uint_sized +/// @file glm/ext/scalar_uint_sized.hpp +/// +/// @defgroup ext_scalar_uint_sized GLM_EXT_scalar_uint_sized +/// @ingroup ext +/// +/// Exposes sized unsigned integer scalar types. +/// +/// Include to use the features of this extension. +/// +/// @see ext_scalar_int_sized + +#pragma once + +#include "../detail/setup.hpp" + +#if GLM_MESSAGES == GLM_ENABLE && !defined(GLM_EXT_INCLUDED) +# pragma message("GLM: GLM_EXT_scalar_uint_sized extension included") +#endif + +namespace glm{ +namespace detail +{ +# if GLM_HAS_EXTENDED_INTEGER_TYPE + typedef std::uint8_t uint8; + typedef std::uint16_t uint16; + typedef std::uint32_t uint32; +# else + typedef unsigned char uint8; + typedef unsigned short uint16; + typedef unsigned int uint32; +#endif + + template<> + struct is_int + { + enum test {value = ~0}; + }; + + template<> + struct is_int + { + enum test {value = ~0}; + }; + + template<> + struct is_int + { + enum test {value = ~0}; + }; +}//namespace detail + + + /// @addtogroup ext_scalar_uint_sized + /// @{ + + /// 8 bit unsigned integer type. + typedef detail::uint8 uint8; + + /// 16 bit unsigned integer type. + typedef detail::uint16 uint16; + + /// 32 bit unsigned integer type. + typedef detail::uint32 uint32; + + /// 64 bit unsigned integer type. + typedef detail::uint64 uint64; + + /// @} +}//namespace glm diff --git a/src/GLMath/glm/ext/scalar_ulp.hpp b/src/GLMath/glm/ext/scalar_ulp.hpp new file mode 100644 index 000000000..90e177da1 --- /dev/null +++ b/src/GLMath/glm/ext/scalar_ulp.hpp @@ -0,0 +1,74 @@ +/// @ref ext_scalar_ulp +/// @file glm/ext/scalar_ulp.hpp +/// +/// @defgroup ext_scalar_ulp GLM_EXT_scalar_ulp +/// @ingroup ext +/// +/// Allow the measurement of the accuracy of a function against a reference +/// implementation. This extension works on floating-point data and provide results +/// in ULP. +/// +/// Include to use the features of this extension. +/// +/// @see ext_vector_ulp +/// @see ext_scalar_relational + +#pragma once + +// Dependencies +#include "../ext/scalar_int_sized.hpp" +#include "../common.hpp" +#include "../detail/qualifier.hpp" + +#if GLM_MESSAGES == GLM_ENABLE && !defined(GLM_EXT_INCLUDED) +# pragma message("GLM: GLM_EXT_scalar_ulp extension included") +#endif + +namespace glm +{ + /// Return the next ULP value(s) after the input value(s). + /// + /// @tparam genType A floating-point scalar type. + /// + /// @see ext_scalar_ulp + template + GLM_FUNC_DECL genType next_float(genType x); + + /// Return the previous ULP value(s) before the input value(s). + /// + /// @tparam genType A floating-point scalar type. + /// + /// @see ext_scalar_ulp + template + GLM_FUNC_DECL genType prev_float(genType x); + + /// Return the value(s) ULP distance after the input value(s). + /// + /// @tparam genType A floating-point scalar type. + /// + /// @see ext_scalar_ulp + template + GLM_FUNC_DECL genType next_float(genType x, int ULPs); + + /// Return the value(s) ULP distance before the input value(s). + /// + /// @tparam genType A floating-point scalar type. + /// + /// @see ext_scalar_ulp + template + GLM_FUNC_DECL genType prev_float(genType x, int ULPs); + + /// Return the distance in the number of ULP between 2 single-precision floating-point scalars. + /// + /// @see ext_scalar_ulp + GLM_FUNC_DECL int float_distance(float x, float y); + + /// Return the distance in the number of ULP between 2 double-precision floating-point scalars. + /// + /// @see ext_scalar_ulp + GLM_FUNC_DECL int64 float_distance(double x, double y); + + /// @} +}//namespace glm + +#include "scalar_ulp.inl" diff --git a/src/GLMath/glm/ext/scalar_ulp.inl b/src/GLMath/glm/ext/scalar_ulp.inl new file mode 100644 index 000000000..8fc3759b9 --- /dev/null +++ b/src/GLMath/glm/ext/scalar_ulp.inl @@ -0,0 +1,284 @@ +/// Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. +/// +/// Developed at SunPro, a Sun Microsystems, Inc. business. +/// Permission to use, copy, modify, and distribute this +/// software is freely granted, provided that this notice +/// is preserved. + +#include "../detail/type_float.hpp" +#include "../ext/scalar_constants.hpp" +#include +#include + +#if(GLM_COMPILER & GLM_COMPILER_VC) +# pragma warning(push) +# pragma warning(disable : 4127) +#endif + +typedef union +{ + float value; + /* FIXME: Assumes 32 bit int. */ + unsigned int word; +} ieee_float_shape_type; + +typedef union +{ + double value; + struct + { + int lsw; + int msw; + } parts; +} ieee_double_shape_type; + +#define GLM_EXTRACT_WORDS(ix0,ix1,d) \ + do { \ + ieee_double_shape_type ew_u; \ + ew_u.value = (d); \ + (ix0) = ew_u.parts.msw; \ + (ix1) = ew_u.parts.lsw; \ + } while (0) + +#define GLM_GET_FLOAT_WORD(i,d) \ + do { \ + ieee_float_shape_type gf_u; \ + gf_u.value = (d); \ + (i) = gf_u.word; \ + } while (0) + +#define GLM_SET_FLOAT_WORD(d,i) \ + do { \ + ieee_float_shape_type sf_u; \ + sf_u.word = (i); \ + (d) = sf_u.value; \ + } while (0) + +#define GLM_INSERT_WORDS(d,ix0,ix1) \ + do { \ + ieee_double_shape_type iw_u; \ + iw_u.parts.msw = (ix0); \ + iw_u.parts.lsw = (ix1); \ + (d) = iw_u.value; \ + } while (0) + +namespace glm{ +namespace detail +{ + GLM_FUNC_QUALIFIER float nextafterf(float x, float y) + { + volatile float t; + int hx, hy, ix, iy; + + GLM_GET_FLOAT_WORD(hx, x); + GLM_GET_FLOAT_WORD(hy, y); + ix = hx & 0x7fffffff; // |x| + iy = hy & 0x7fffffff; // |y| + + if((ix > 0x7f800000) || // x is nan + (iy > 0x7f800000)) // y is nan + return x + y; + if(abs(y - x) <= epsilon()) + return y; // x=y, return y + if(ix == 0) + { // x == 0 + GLM_SET_FLOAT_WORD(x, (hy & 0x80000000) | 1);// return +-minsubnormal + t = x * x; + if(abs(t - x) <= epsilon()) + return t; + else + return x; // raise underflow flag + } + if(hx >= 0) + { // x > 0 + if(hx > hy) // x > y, x -= ulp + hx -= 1; + else // x < y, x += ulp + hx += 1; + } + else + { // x < 0 + if(hy >= 0 || hx > hy) // x < y, x -= ulp + hx -= 1; + else // x > y, x += ulp + hx += 1; + } + hy = hx & 0x7f800000; + if(hy >= 0x7f800000) + return x + x; // overflow + if(hy < 0x00800000) // underflow + { + t = x * x; + if(abs(t - x) > epsilon()) + { // raise underflow flag + GLM_SET_FLOAT_WORD(y, hx); + return y; + } + } + GLM_SET_FLOAT_WORD(x, hx); + return x; + } + + GLM_FUNC_QUALIFIER double nextafter(double x, double y) + { + volatile double t; + int hx, hy, ix, iy; + unsigned int lx, ly; + + GLM_EXTRACT_WORDS(hx, lx, x); + GLM_EXTRACT_WORDS(hy, ly, y); + ix = hx & 0x7fffffff; // |x| + iy = hy & 0x7fffffff; // |y| + + if(((ix >= 0x7ff00000) && ((ix - 0x7ff00000) | lx) != 0) || // x is nan + ((iy >= 0x7ff00000) && ((iy - 0x7ff00000) | ly) != 0)) // y is nan + return x + y; + if(abs(y - x) <= epsilon()) + return y; // x=y, return y + if((ix | lx) == 0) + { // x == 0 + GLM_INSERT_WORDS(x, hy & 0x80000000, 1); // return +-minsubnormal + t = x * x; + if(abs(t - x) <= epsilon()) + return t; + else + return x; // raise underflow flag + } + if(hx >= 0) { // x > 0 + if(hx > hy || ((hx == hy) && (lx > ly))) { // x > y, x -= ulp + if(lx == 0) hx -= 1; + lx -= 1; + } + else { // x < y, x += ulp + lx += 1; + if(lx == 0) hx += 1; + } + } + else { // x < 0 + if(hy >= 0 || hx > hy || ((hx == hy) && (lx > ly))){// x < y, x -= ulp + if(lx == 0) hx -= 1; + lx -= 1; + } + else { // x > y, x += ulp + lx += 1; + if(lx == 0) hx += 1; + } + } + hy = hx & 0x7ff00000; + if(hy >= 0x7ff00000) + return x + x; // overflow + if(hy < 0x00100000) + { // underflow + t = x * x; + if(abs(t - x) > epsilon()) + { // raise underflow flag + GLM_INSERT_WORDS(y, hx, lx); + return y; + } + } + GLM_INSERT_WORDS(x, hx, lx); + return x; + } +}//namespace detail +}//namespace glm + +#if(GLM_COMPILER & GLM_COMPILER_VC) +# pragma warning(pop) +#endif + +namespace glm +{ + template<> + GLM_FUNC_QUALIFIER float next_float(float x) + { +# if GLM_HAS_CXX11_STL + return std::nextafter(x, std::numeric_limits::max()); +# elif((GLM_COMPILER & GLM_COMPILER_VC) || ((GLM_COMPILER & GLM_COMPILER_INTEL) && (GLM_PLATFORM & GLM_PLATFORM_WINDOWS))) + return detail::nextafterf(x, FLT_MAX); +# elif(GLM_PLATFORM & GLM_PLATFORM_ANDROID) + return __builtin_nextafterf(x, FLT_MAX); +# else + return nextafterf(x, FLT_MAX); +# endif + } + + template<> + GLM_FUNC_QUALIFIER double next_float(double x) + { +# if GLM_HAS_CXX11_STL + return std::nextafter(x, std::numeric_limits::max()); +# elif((GLM_COMPILER & GLM_COMPILER_VC) || ((GLM_COMPILER & GLM_COMPILER_INTEL) && (GLM_PLATFORM & GLM_PLATFORM_WINDOWS))) + return detail::nextafter(x, std::numeric_limits::max()); +# elif(GLM_PLATFORM & GLM_PLATFORM_ANDROID) + return __builtin_nextafter(x, DBL_MAX); +# else + return nextafter(x, DBL_MAX); +# endif + } + + template + GLM_FUNC_QUALIFIER T next_float(T x, int ULPs) + { + GLM_STATIC_ASSERT(std::numeric_limits::is_iec559, "'next_float' only accept floating-point input"); + assert(ULPs >= 0); + + T temp = x; + for(int i = 0; i < ULPs; ++i) + temp = next_float(temp); + return temp; + } + + GLM_FUNC_QUALIFIER float prev_float(float x) + { +# if GLM_HAS_CXX11_STL + return std::nextafter(x, std::numeric_limits::min()); +# elif((GLM_COMPILER & GLM_COMPILER_VC) || ((GLM_COMPILER & GLM_COMPILER_INTEL) && (GLM_PLATFORM & GLM_PLATFORM_WINDOWS))) + return detail::nextafterf(x, FLT_MIN); +# elif(GLM_PLATFORM & GLM_PLATFORM_ANDROID) + return __builtin_nextafterf(x, FLT_MIN); +# else + return nextafterf(x, FLT_MIN); +# endif + } + + GLM_FUNC_QUALIFIER double prev_float(double x) + { +# if GLM_HAS_CXX11_STL + return std::nextafter(x, std::numeric_limits::min()); +# elif((GLM_COMPILER & GLM_COMPILER_VC) || ((GLM_COMPILER & GLM_COMPILER_INTEL) && (GLM_PLATFORM & GLM_PLATFORM_WINDOWS))) + return _nextafter(x, DBL_MIN); +# elif(GLM_PLATFORM & GLM_PLATFORM_ANDROID) + return __builtin_nextafter(x, DBL_MIN); +# else + return nextafter(x, DBL_MIN); +# endif + } + + template + GLM_FUNC_QUALIFIER T prev_float(T x, int ULPs) + { + GLM_STATIC_ASSERT(std::numeric_limits::is_iec559, "'prev_float' only accept floating-point input"); + assert(ULPs >= 0); + + T temp = x; + for(int i = 0; i < ULPs; ++i) + temp = prev_float(temp); + return temp; + } + + GLM_FUNC_QUALIFIER int float_distance(float x, float y) + { + detail::float_t const a(x); + detail::float_t const b(y); + + return abs(a.i - b.i); + } + + GLM_FUNC_QUALIFIER int64 float_distance(double x, double y) + { + detail::float_t const a(x); + detail::float_t const b(y); + + return abs(a.i - b.i); + } +}//namespace glm diff --git a/src/GLMath/glm/ext/vector_bool1.hpp b/src/GLMath/glm/ext/vector_bool1.hpp new file mode 100644 index 000000000..002c3202a --- /dev/null +++ b/src/GLMath/glm/ext/vector_bool1.hpp @@ -0,0 +1,30 @@ +/// @ref ext_vector_bool1 +/// @file glm/ext/vector_bool1.hpp +/// +/// @defgroup ext_vector_bool1 GLM_EXT_vector_bool1 +/// @ingroup ext +/// +/// Exposes bvec1 vector type. +/// +/// Include to use the features of this extension. +/// +/// @see ext_vector_bool1_precision extension. + +#pragma once + +#include "../detail/type_vec1.hpp" + +#if GLM_MESSAGES == GLM_ENABLE && !defined(GLM_EXT_INCLUDED) +# pragma message("GLM: GLM_EXT_vector_bool1 extension included") +#endif + +namespace glm +{ + /// @addtogroup ext_vector_bool1 + /// @{ + + /// 1 components vector of boolean. + typedef vec<1, bool, defaultp> bvec1; + + /// @} +}//namespace glm diff --git a/src/GLMath/glm/ext/vector_bool1_precision.hpp b/src/GLMath/glm/ext/vector_bool1_precision.hpp new file mode 100644 index 000000000..e62d3cfb5 --- /dev/null +++ b/src/GLMath/glm/ext/vector_bool1_precision.hpp @@ -0,0 +1,34 @@ +/// @ref ext_vector_bool1_precision +/// @file glm/ext/vector_bool1_precision.hpp +/// +/// @defgroup ext_vector_bool1_precision GLM_EXT_vector_bool1_precision +/// @ingroup ext +/// +/// Exposes highp_bvec1, mediump_bvec1 and lowp_bvec1 types. +/// +/// Include to use the features of this extension. + +#pragma once + +#include "../detail/type_vec1.hpp" + +#if GLM_MESSAGES == GLM_ENABLE && !defined(GLM_EXT_INCLUDED) +# pragma message("GLM: GLM_EXT_vector_bool1_precision extension included") +#endif + +namespace glm +{ + /// @addtogroup ext_vector_bool1_precision + /// @{ + + /// 1 component vector of bool values. + typedef vec<1, bool, highp> highp_bvec1; + + /// 1 component vector of bool values. + typedef vec<1, bool, mediump> mediump_bvec1; + + /// 1 component vector of bool values. + typedef vec<1, bool, lowp> lowp_bvec1; + + /// @} +}//namespace glm diff --git a/src/GLMath/glm/ext/vector_bool2.hpp b/src/GLMath/glm/ext/vector_bool2.hpp new file mode 100644 index 000000000..52288b75c --- /dev/null +++ b/src/GLMath/glm/ext/vector_bool2.hpp @@ -0,0 +1,18 @@ +/// @ref core +/// @file glm/ext/vector_bool2.hpp + +#pragma once +#include "../detail/type_vec2.hpp" + +namespace glm +{ + /// @addtogroup core_vector + /// @{ + + /// 2 components vector of boolean. + /// + /// @see GLSL 4.20.8 specification, section 4.1.5 Vectors + typedef vec<2, bool, defaultp> bvec2; + + /// @} +}//namespace glm diff --git a/src/GLMath/glm/ext/vector_bool2_precision.hpp b/src/GLMath/glm/ext/vector_bool2_precision.hpp new file mode 100644 index 000000000..43709332c --- /dev/null +++ b/src/GLMath/glm/ext/vector_bool2_precision.hpp @@ -0,0 +1,31 @@ +/// @ref core +/// @file glm/ext/vector_bool2_precision.hpp + +#pragma once +#include "../detail/type_vec2.hpp" + +namespace glm +{ + /// @addtogroup core_vector_precision + /// @{ + + /// 2 components vector of high qualifier bool numbers. + /// + /// @see GLSL 4.20.8 specification, section 4.1.5 Vectors + /// @see GLSL 4.20.8 specification, section 4.7.2 Precision Qualifier + typedef vec<2, bool, highp> highp_bvec2; + + /// 2 components vector of medium qualifier bool numbers. + /// + /// @see GLSL 4.20.8 specification, section 4.1.5 Vectors + /// @see GLSL 4.20.8 specification, section 4.7.2 Precision Qualifier + typedef vec<2, bool, mediump> mediump_bvec2; + + /// 2 components vector of low qualifier bool numbers. + /// + /// @see GLSL 4.20.8 specification, section 4.1.5 Vectors + /// @see GLSL 4.20.8 specification, section 4.7.2 Precision Qualifier + typedef vec<2, bool, lowp> lowp_bvec2; + + /// @} +}//namespace glm diff --git a/src/GLMath/glm/ext/vector_bool3.hpp b/src/GLMath/glm/ext/vector_bool3.hpp new file mode 100644 index 000000000..90a0b7ea5 --- /dev/null +++ b/src/GLMath/glm/ext/vector_bool3.hpp @@ -0,0 +1,18 @@ +/// @ref core +/// @file glm/ext/vector_bool3.hpp + +#pragma once +#include "../detail/type_vec3.hpp" + +namespace glm +{ + /// @addtogroup core_vector + /// @{ + + /// 3 components vector of boolean. + /// + /// @see GLSL 4.20.8 specification, section 4.1.5 Vectors + typedef vec<3, bool, defaultp> bvec3; + + /// @} +}//namespace glm diff --git a/src/GLMath/glm/ext/vector_bool3_precision.hpp b/src/GLMath/glm/ext/vector_bool3_precision.hpp new file mode 100644 index 000000000..89cd2d320 --- /dev/null +++ b/src/GLMath/glm/ext/vector_bool3_precision.hpp @@ -0,0 +1,31 @@ +/// @ref core +/// @file glm/ext/vector_bool3_precision.hpp + +#pragma once +#include "../detail/type_vec3.hpp" + +namespace glm +{ + /// @addtogroup core_vector_precision + /// @{ + + /// 3 components vector of high qualifier bool numbers. + /// + /// @see GLSL 4.20.8 specification, section 4.1.5 Vectors + /// @see GLSL 4.20.8 specification, section 4.7.2 Precision Qualifier + typedef vec<3, bool, highp> highp_bvec3; + + /// 3 components vector of medium qualifier bool numbers. + /// + /// @see GLSL 4.20.8 specification, section 4.1.5 Vectors + /// @see GLSL 4.20.8 specification, section 4.7.2 Precision Qualifier + typedef vec<3, bool, mediump> mediump_bvec3; + + /// 3 components vector of low qualifier bool numbers. + /// + /// @see GLSL 4.20.8 specification, section 4.1.5 Vectors + /// @see GLSL 4.20.8 specification, section 4.7.2 Precision Qualifier + typedef vec<3, bool, lowp> lowp_bvec3; + + /// @} +}//namespace glm diff --git a/src/GLMath/glm/ext/vector_bool4.hpp b/src/GLMath/glm/ext/vector_bool4.hpp new file mode 100644 index 000000000..18aa71bd0 --- /dev/null +++ b/src/GLMath/glm/ext/vector_bool4.hpp @@ -0,0 +1,18 @@ +/// @ref core +/// @file glm/ext/vector_bool4.hpp + +#pragma once +#include "../detail/type_vec4.hpp" + +namespace glm +{ + /// @addtogroup core_vector + /// @{ + + /// 4 components vector of boolean. + /// + /// @see GLSL 4.20.8 specification, section 4.1.5 Vectors + typedef vec<4, bool, defaultp> bvec4; + + /// @} +}//namespace glm diff --git a/src/GLMath/glm/ext/vector_bool4_precision.hpp b/src/GLMath/glm/ext/vector_bool4_precision.hpp new file mode 100644 index 000000000..79786e542 --- /dev/null +++ b/src/GLMath/glm/ext/vector_bool4_precision.hpp @@ -0,0 +1,31 @@ +/// @ref core +/// @file glm/ext/vector_bool4_precision.hpp + +#pragma once +#include "../detail/type_vec4.hpp" + +namespace glm +{ + /// @addtogroup core_vector_precision + /// @{ + + /// 4 components vector of high qualifier bool numbers. + /// + /// @see GLSL 4.20.8 specification, section 4.1.5 Vectors + /// @see GLSL 4.20.8 specification, section 4.7.2 Precision Qualifier + typedef vec<4, bool, highp> highp_bvec4; + + /// 4 components vector of medium qualifier bool numbers. + /// + /// @see GLSL 4.20.8 specification, section 4.1.5 Vectors + /// @see GLSL 4.20.8 specification, section 4.7.2 Precision Qualifier + typedef vec<4, bool, mediump> mediump_bvec4; + + /// 4 components vector of low qualifier bool numbers. + /// + /// @see GLSL 4.20.8 specification, section 4.1.5 Vectors + /// @see GLSL 4.20.8 specification, section 4.7.2 Precision Qualifier + typedef vec<4, bool, lowp> lowp_bvec4; + + /// @} +}//namespace glm diff --git a/src/GLMath/glm/ext/vector_common.hpp b/src/GLMath/glm/ext/vector_common.hpp new file mode 100644 index 000000000..324fe1c70 --- /dev/null +++ b/src/GLMath/glm/ext/vector_common.hpp @@ -0,0 +1,144 @@ +/// @ref ext_vector_common +/// @file glm/ext/vector_common.hpp +/// +/// @defgroup ext_vector_common GLM_EXT_vector_common +/// @ingroup ext +/// +/// Exposes min and max functions for 3 to 4 vector parameters. +/// +/// Include to use the features of this extension. +/// +/// @see core_common +/// @see ext_scalar_common + +#pragma once + +// Dependency: +#include "../ext/scalar_common.hpp" +#include "../common.hpp" + +#if GLM_MESSAGES == GLM_ENABLE && !defined(GLM_EXT_INCLUDED) +# pragma message("GLM: GLM_EXT_vector_common extension included") +#endif + +namespace glm +{ + /// @addtogroup ext_vector_common + /// @{ + + /// Return the minimum component-wise values of 3 inputs + /// + /// @tparam L Integer between 1 and 4 included that qualify the dimension of the vector + /// @tparam T Floating-point or integer scalar types + /// @tparam Q Value from qualifier enum + template + GLM_FUNC_DECL GLM_CONSTEXPR vec min(vec const& a, vec const& b, vec const& c); + + /// Return the minimum component-wise values of 4 inputs + /// + /// @tparam L Integer between 1 and 4 included that qualify the dimension of the vector + /// @tparam T Floating-point or integer scalar types + /// @tparam Q Value from qualifier enum + template + GLM_FUNC_DECL GLM_CONSTEXPR vec min(vec const& a, vec const& b, vec const& c, vec const& d); + + /// Return the maximum component-wise values of 3 inputs + /// + /// @tparam L Integer between 1 and 4 included that qualify the dimension of the vector + /// @tparam T Floating-point or integer scalar types + /// @tparam Q Value from qualifier enum + template + GLM_FUNC_DECL GLM_CONSTEXPR vec max(vec const& x, vec const& y, vec const& z); + + /// Return the maximum component-wise values of 4 inputs + /// + /// @tparam L Integer between 1 and 4 included that qualify the dimension of the vector + /// @tparam T Floating-point or integer scalar types + /// @tparam Q Value from qualifier enum + template + GLM_FUNC_DECL GLM_CONSTEXPR vec max( vec const& x, vec const& y, vec const& z, vec const& w); + + /// Returns y if y < x; otherwise, it returns x. If one of the two arguments is NaN, the value of the other argument is returned. + /// + /// @tparam L Integer between 1 and 4 included that qualify the dimension of the vector + /// @tparam T Floating-point scalar types + /// @tparam Q Value from qualifier enum + /// + /// @see std::fmin documentation + template + GLM_FUNC_DECL vec fmin(vec const& x, T y); + + /// Returns y if y < x; otherwise, it returns x. If one of the two arguments is NaN, the value of the other argument is returned. + /// + /// @tparam L Integer between 1 and 4 included that qualify the dimension of the vector + /// @tparam T Floating-point scalar types + /// @tparam Q Value from qualifier enum + /// + /// @see std::fmin documentation + template + GLM_FUNC_DECL vec fmin(vec const& x, vec const& y); + + /// Returns y if y < x; otherwise, it returns x. If one of the two arguments is NaN, the value of the other argument is returned. + /// + /// @tparam L Integer between 1 and 4 included that qualify the dimension of the vector + /// @tparam T Floating-point scalar types + /// @tparam Q Value from qualifier enum + /// + /// @see std::fmin documentation + template + GLM_FUNC_DECL vec fmin(vec const& a, vec const& b, vec const& c); + + /// Returns y if y < x; otherwise, it returns x. If one of the two arguments is NaN, the value of the other argument is returned. + /// + /// @tparam L Integer between 1 and 4 included that qualify the dimension of the vector + /// @tparam T Floating-point scalar types + /// @tparam Q Value from qualifier enum + /// + /// @see std::fmin documentation + template + GLM_FUNC_DECL vec fmin(vec const& a, vec const& b, vec const& c, vec const& d); + + /// Returns y if x < y; otherwise, it returns x. If one of the two arguments is NaN, the value of the other argument is returned. + /// + /// @tparam L Integer between 1 and 4 included that qualify the dimension of the vector + /// @tparam T Floating-point scalar types + /// @tparam Q Value from qualifier enum + /// + /// @see std::fmax documentation + template + GLM_FUNC_DECL vec fmax(vec const& a, T b); + + /// Returns y if x < y; otherwise, it returns x. If one of the two arguments is NaN, the value of the other argument is returned. + /// + /// @tparam L Integer between 1 and 4 included that qualify the dimension of the vector + /// @tparam T Floating-point scalar types + /// @tparam Q Value from qualifier enum + /// + /// @see std::fmax documentation + template + GLM_FUNC_DECL vec fmax(vec const& a, vec const& b); + + /// Returns y if x < y; otherwise, it returns x. If one of the two arguments is NaN, the value of the other argument is returned. + /// + /// @tparam L Integer between 1 and 4 included that qualify the dimension of the vector + /// @tparam T Floating-point scalar types + /// @tparam Q Value from qualifier enum + /// + /// @see std::fmax documentation + template + GLM_FUNC_DECL vec fmax(vec const& a, vec const& b, vec const& c); + + /// Returns y if x < y; otherwise, it returns x. If one of the two arguments is NaN, the value of the other argument is returned. + /// + /// @tparam L Integer between 1 and 4 included that qualify the dimension of the vector + /// @tparam T Floating-point scalar types + /// @tparam Q Value from qualifier enum + /// + /// @see std::fmax documentation + template + GLM_FUNC_DECL vec fmax(vec const& a, vec const& b, vec const& c, vec const& d); + + /// @} +}//namespace glm + +#include "vector_common.inl" diff --git a/src/GLMath/glm/ext/vector_common.inl b/src/GLMath/glm/ext/vector_common.inl new file mode 100644 index 000000000..71f38093a --- /dev/null +++ b/src/GLMath/glm/ext/vector_common.inl @@ -0,0 +1,88 @@ +#include "../detail/_vectorize.hpp" + +namespace glm +{ + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec min(vec const& x, vec const& y, vec const& z) + { + GLM_STATIC_ASSERT(std::numeric_limits::is_iec559, "'min' only accept floating-point or integer inputs"); + return glm::min(glm::min(x, y), z); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec min(vec const& x, vec const& y, vec const& z, vec const& w) + { + GLM_STATIC_ASSERT(std::numeric_limits::is_iec559, "'min' only accept floating-point or integer inputs"); + return glm::min(glm::min(x, y), glm::min(z, w)); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec max(vec const& x, vec const& y, vec const& z) + { + GLM_STATIC_ASSERT(std::numeric_limits::is_iec559, "'max' only accept floating-point or integer inputs"); + return glm::max(glm::max(x, y), z); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec max(vec const& x, vec const& y, vec const& z, vec const& w) + { + GLM_STATIC_ASSERT(std::numeric_limits::is_iec559, "'max' only accept floating-point or integer inputs"); + return glm::max(glm::max(x, y), glm::max(z, w)); + } + + template + GLM_FUNC_QUALIFIER vec fmin(vec const& a, T b) + { + GLM_STATIC_ASSERT(std::numeric_limits::is_iec559, "'fmin' only accept floating-point inputs"); + return detail::functor2::call(fmin, a, vec(b)); + } + + template + GLM_FUNC_QUALIFIER vec fmin(vec const& a, vec const& b) + { + GLM_STATIC_ASSERT(std::numeric_limits::is_iec559, "'fmin' only accept floating-point inputs"); + return detail::functor2::call(fmin, a, b); + } + + template + GLM_FUNC_QUALIFIER vec fmin(vec const& a, vec const& b, vec const& c) + { + GLM_STATIC_ASSERT(std::numeric_limits::is_iec559, "'fmin' only accept floating-point inputs"); + return fmin(fmin(a, b), c); + } + + template + GLM_FUNC_QUALIFIER vec fmin(vec const& a, vec const& b, vec const& c, vec const& d) + { + GLM_STATIC_ASSERT(std::numeric_limits::is_iec559, "'fmin' only accept floating-point inputs"); + return fmin(fmin(a, b), fmin(c, d)); + } + + template + GLM_FUNC_QUALIFIER vec fmax(vec const& a, T b) + { + GLM_STATIC_ASSERT(std::numeric_limits::is_iec559, "'fmax' only accept floating-point inputs"); + return detail::functor2::call(fmax, a, vec(b)); + } + + template + GLM_FUNC_QUALIFIER vec fmax(vec const& a, vec const& b) + { + GLM_STATIC_ASSERT(std::numeric_limits::is_iec559, "'fmax' only accept floating-point inputs"); + return detail::functor2::call(fmax, a, b); + } + + template + GLM_FUNC_QUALIFIER vec fmax(vec const& a, vec const& b, vec const& c) + { + GLM_STATIC_ASSERT(std::numeric_limits::is_iec559, "'fmax' only accept floating-point inputs"); + return fmax(fmax(a, b), c); + } + + template + GLM_FUNC_QUALIFIER vec fmax(vec const& a, vec const& b, vec const& c, vec const& d) + { + GLM_STATIC_ASSERT(std::numeric_limits::is_iec559, "'fmax' only accept floating-point inputs"); + return fmax(fmax(a, b), fmax(c, d)); + } +}//namespace glm diff --git a/src/GLMath/glm/ext/vector_double1.hpp b/src/GLMath/glm/ext/vector_double1.hpp new file mode 100644 index 000000000..ef12def42 --- /dev/null +++ b/src/GLMath/glm/ext/vector_double1.hpp @@ -0,0 +1,31 @@ +/// @ref ext_vector_double1 +/// @file glm/ext/vector_double1.hpp +/// +/// @defgroup ext_vector_double1 GLM_EXT_vector_double1 +/// @ingroup ext +/// +/// Exposes double-precision floating point vector type with one component. +/// +/// Include to use the features of this extension. +/// +/// @see ext_vector_double1_precision extension. +/// @see ext_vector_float1 extension. + +#pragma once + +#include "../detail/type_vec1.hpp" + +#if GLM_MESSAGES == GLM_ENABLE && !defined(GLM_EXT_INCLUDED) +# pragma message("GLM: GLM_EXT_vector_dvec1 extension included") +#endif + +namespace glm +{ + /// @addtogroup ext_vector_double1 + /// @{ + + /// 1 components vector of double-precision floating-point numbers. + typedef vec<1, double, defaultp> dvec1; + + /// @} +}//namespace glm diff --git a/src/GLMath/glm/ext/vector_double1_precision.hpp b/src/GLMath/glm/ext/vector_double1_precision.hpp new file mode 100644 index 000000000..1d4719595 --- /dev/null +++ b/src/GLMath/glm/ext/vector_double1_precision.hpp @@ -0,0 +1,36 @@ +/// @ref ext_vector_double1_precision +/// @file glm/ext/vector_double1_precision.hpp +/// +/// @defgroup ext_vector_double1_precision GLM_EXT_vector_double1_precision +/// @ingroup ext +/// +/// Exposes highp_dvec1, mediump_dvec1 and lowp_dvec1 types. +/// +/// Include to use the features of this extension. +/// +/// @see ext_vector_double1 + +#pragma once + +#include "../detail/type_vec1.hpp" + +#if GLM_MESSAGES == GLM_ENABLE && !defined(GLM_EXT_INCLUDED) +# pragma message("GLM: GLM_EXT_vector_double1_precision extension included") +#endif + +namespace glm +{ + /// @addtogroup ext_vector_double1_precision + /// @{ + + /// 1 component vector of double-precision floating-point numbers using high precision arithmetic in term of ULPs. + typedef vec<1, double, highp> highp_dvec1; + + /// 1 component vector of double-precision floating-point numbers using medium precision arithmetic in term of ULPs. + typedef vec<1, double, mediump> mediump_dvec1; + + /// 1 component vector of double-precision floating-point numbers using low precision arithmetic in term of ULPs. + typedef vec<1, double, lowp> lowp_dvec1; + + /// @} +}//namespace glm diff --git a/src/GLMath/glm/ext/vector_double2.hpp b/src/GLMath/glm/ext/vector_double2.hpp new file mode 100644 index 000000000..60e357750 --- /dev/null +++ b/src/GLMath/glm/ext/vector_double2.hpp @@ -0,0 +1,18 @@ +/// @ref core +/// @file glm/ext/vector_double2.hpp + +#pragma once +#include "../detail/type_vec2.hpp" + +namespace glm +{ + /// @addtogroup core_vector + /// @{ + + /// 2 components vector of double-precision floating-point numbers. + /// + /// @see GLSL 4.20.8 specification, section 4.1.5 Vectors + typedef vec<2, double, defaultp> dvec2; + + /// @} +}//namespace glm diff --git a/src/GLMath/glm/ext/vector_double2_precision.hpp b/src/GLMath/glm/ext/vector_double2_precision.hpp new file mode 100644 index 000000000..fa53940f6 --- /dev/null +++ b/src/GLMath/glm/ext/vector_double2_precision.hpp @@ -0,0 +1,31 @@ +/// @ref core +/// @file glm/ext/vector_double2_precision.hpp + +#pragma once +#include "../detail/type_vec2.hpp" + +namespace glm +{ + /// @addtogroup core_vector_precision + /// @{ + + /// 2 components vector of high double-qualifier floating-point numbers. + /// + /// @see GLSL 4.20.8 specification, section 4.1.5 Vectors + /// @see GLSL 4.20.8 specification, section 4.7.2 Precision Qualifier + typedef vec<2, double, highp> highp_dvec2; + + /// 2 components vector of medium double-qualifier floating-point numbers. + /// + /// @see GLSL 4.20.8 specification, section 4.1.5 Vectors + /// @see GLSL 4.20.8 specification, section 4.7.2 Precision Qualifier + typedef vec<2, double, mediump> mediump_dvec2; + + /// 2 components vector of low double-qualifier floating-point numbers. + /// + /// @see GLSL 4.20.8 specification, section 4.1.5 Vectors + /// @see GLSL 4.20.8 specification, section 4.7.2 Precision Qualifier + typedef vec<2, double, lowp> lowp_dvec2; + + /// @} +}//namespace glm diff --git a/src/GLMath/glm/ext/vector_double3.hpp b/src/GLMath/glm/ext/vector_double3.hpp new file mode 100644 index 000000000..6dfe4c675 --- /dev/null +++ b/src/GLMath/glm/ext/vector_double3.hpp @@ -0,0 +1,18 @@ +/// @ref core +/// @file glm/ext/vector_double3.hpp + +#pragma once +#include "../detail/type_vec3.hpp" + +namespace glm +{ + /// @addtogroup core_vector + /// @{ + + /// 3 components vector of double-precision floating-point numbers. + /// + /// @see GLSL 4.20.8 specification, section 4.1.5 Vectors + typedef vec<3, double, defaultp> dvec3; + + /// @} +}//namespace glm diff --git a/src/GLMath/glm/ext/vector_double3_precision.hpp b/src/GLMath/glm/ext/vector_double3_precision.hpp new file mode 100644 index 000000000..a8cfa37a8 --- /dev/null +++ b/src/GLMath/glm/ext/vector_double3_precision.hpp @@ -0,0 +1,34 @@ +/// @ref core +/// @file glm/ext/vector_double3_precision.hpp + +#pragma once +#include "../detail/type_vec3.hpp" + +namespace glm +{ + /// @addtogroup core_vector_precision + /// @{ + + /// 3 components vector of high double-qualifier floating-point numbers. + /// There is no guarantee on the actual qualifier. + /// + /// @see GLSL 4.20.8 specification, section 4.1.5 Vectors + /// @see GLSL 4.20.8 specification, section 4.7.2 Precision Qualifier + typedef vec<3, double, highp> highp_dvec3; + + /// 3 components vector of medium double-qualifier floating-point numbers. + /// There is no guarantee on the actual qualifier. + /// + /// @see GLSL 4.20.8 specification, section 4.1.5 Vectors + /// @see GLSL 4.20.8 specification, section 4.7.2 Precision Qualifier + typedef vec<3, double, mediump> mediump_dvec3; + + /// 3 components vector of low double-qualifier floating-point numbers. + /// There is no guarantee on the actual qualifier. + /// + /// @see GLSL 4.20.8 specification, section 4.1.5 Vectors + /// @see GLSL 4.20.8 specification, section 4.7.2 Precision Qualifier + typedef vec<3, double, lowp> lowp_dvec3; + + /// @} +}//namespace glm diff --git a/src/GLMath/glm/ext/vector_double4.hpp b/src/GLMath/glm/ext/vector_double4.hpp new file mode 100644 index 000000000..87f225f64 --- /dev/null +++ b/src/GLMath/glm/ext/vector_double4.hpp @@ -0,0 +1,18 @@ +/// @ref core +/// @file glm/ext/vector_double4.hpp + +#pragma once +#include "../detail/type_vec4.hpp" + +namespace glm +{ + /// @addtogroup core_vector + /// @{ + + /// 4 components vector of double-precision floating-point numbers. + /// + /// @see GLSL 4.20.8 specification, section 4.1.5 Vectors + typedef vec<4, double, defaultp> dvec4; + + /// @} +}//namespace glm diff --git a/src/GLMath/glm/ext/vector_double4_precision.hpp b/src/GLMath/glm/ext/vector_double4_precision.hpp new file mode 100644 index 000000000..09cafa1eb --- /dev/null +++ b/src/GLMath/glm/ext/vector_double4_precision.hpp @@ -0,0 +1,35 @@ +/// @ref core +/// @file glm/ext/vector_double4_precision.hpp + +#pragma once +#include "../detail/setup.hpp" +#include "../detail/type_vec4.hpp" + +namespace glm +{ + /// @addtogroup core_vector_precision + /// @{ + + /// 4 components vector of high double-qualifier floating-point numbers. + /// There is no guarantee on the actual qualifier. + /// + /// @see GLSL 4.20.8 specification, section 4.1.5 Vectors + /// @see GLSL 4.20.8 specification, section 4.7.2 Precision Qualifier + typedef vec<4, double, highp> highp_dvec4; + + /// 4 components vector of medium double-qualifier floating-point numbers. + /// There is no guarantee on the actual qualifier. + /// + /// @see GLSL 4.20.8 specification, section 4.1.5 Vectors + /// @see GLSL 4.20.8 specification, section 4.7.2 Precision Qualifier + typedef vec<4, double, mediump> mediump_dvec4; + + /// 4 components vector of low double-qualifier floating-point numbers. + /// There is no guarantee on the actual qualifier. + /// + /// @see GLSL 4.20.8 specification, section 4.1.5 Vectors + /// @see GLSL 4.20.8 specification, section 4.7.2 Precision Qualifier + typedef vec<4, double, lowp> lowp_dvec4; + + /// @} +}//namespace glm diff --git a/src/GLMath/glm/ext/vector_float1.hpp b/src/GLMath/glm/ext/vector_float1.hpp new file mode 100644 index 000000000..28acc2c9c --- /dev/null +++ b/src/GLMath/glm/ext/vector_float1.hpp @@ -0,0 +1,31 @@ +/// @ref ext_vector_float1 +/// @file glm/ext/vector_float1.hpp +/// +/// @defgroup ext_vector_float1 GLM_EXT_vector_float1 +/// @ingroup ext +/// +/// Exposes single-precision floating point vector type with one component. +/// +/// Include to use the features of this extension. +/// +/// @see ext_vector_float1_precision extension. +/// @see ext_vector_double1 extension. + +#pragma once + +#include "../detail/type_vec1.hpp" + +#if GLM_MESSAGES == GLM_ENABLE && !defined(GLM_EXT_INCLUDED) +# pragma message("GLM: GLM_EXT_vector_float1 extension included") +#endif + +namespace glm +{ + /// @addtogroup ext_vector_float1 + /// @{ + + /// 1 components vector of single-precision floating-point numbers. + typedef vec<1, float, defaultp> vec1; + + /// @} +}//namespace glm diff --git a/src/GLMath/glm/ext/vector_float1_precision.hpp b/src/GLMath/glm/ext/vector_float1_precision.hpp new file mode 100644 index 000000000..6e8dad8d1 --- /dev/null +++ b/src/GLMath/glm/ext/vector_float1_precision.hpp @@ -0,0 +1,36 @@ +/// @ref ext_vector_float1_precision +/// @file glm/ext/vector_float1_precision.hpp +/// +/// @defgroup ext_vector_float1_precision GLM_EXT_vector_float1_precision +/// @ingroup ext +/// +/// Exposes highp_vec1, mediump_vec1 and lowp_vec1 types. +/// +/// Include to use the features of this extension. +/// +/// @see ext_vector_float1 extension. + +#pragma once + +#include "../detail/type_vec1.hpp" + +#if GLM_MESSAGES == GLM_ENABLE && !defined(GLM_EXT_INCLUDED) +# pragma message("GLM: GLM_EXT_vector_float1_precision extension included") +#endif + +namespace glm +{ + /// @addtogroup ext_vector_float1_precision + /// @{ + + /// 1 component vector of single-precision floating-point numbers using high precision arithmetic in term of ULPs. + typedef vec<1, float, highp> highp_vec1; + + /// 1 component vector of single-precision floating-point numbers using medium precision arithmetic in term of ULPs. + typedef vec<1, float, mediump> mediump_vec1; + + /// 1 component vector of single-precision floating-point numbers using low precision arithmetic in term of ULPs. + typedef vec<1, float, lowp> lowp_vec1; + + /// @} +}//namespace glm diff --git a/src/GLMath/glm/ext/vector_float2.hpp b/src/GLMath/glm/ext/vector_float2.hpp new file mode 100644 index 000000000..d31545dcc --- /dev/null +++ b/src/GLMath/glm/ext/vector_float2.hpp @@ -0,0 +1,18 @@ +/// @ref core +/// @file glm/ext/vector_float2.hpp + +#pragma once +#include "../detail/type_vec2.hpp" + +namespace glm +{ + /// @addtogroup core_vector + /// @{ + + /// 2 components vector of single-precision floating-point numbers. + /// + /// @see GLSL 4.20.8 specification, section 4.1.5 Vectors + typedef vec<2, float, defaultp> vec2; + + /// @} +}//namespace glm diff --git a/src/GLMath/glm/ext/vector_float2_precision.hpp b/src/GLMath/glm/ext/vector_float2_precision.hpp new file mode 100644 index 000000000..23c0820d0 --- /dev/null +++ b/src/GLMath/glm/ext/vector_float2_precision.hpp @@ -0,0 +1,31 @@ +/// @ref core +/// @file glm/ext/vector_float2_precision.hpp + +#pragma once +#include "../detail/type_vec2.hpp" + +namespace glm +{ + /// @addtogroup core_vector_precision + /// @{ + + /// 2 components vector of high single-qualifier floating-point numbers. + /// + /// @see GLSL 4.20.8 specification, section 4.1.5 Vectors + /// @see GLSL 4.20.8 specification, section 4.7.2 Precision Qualifier + typedef vec<2, float, highp> highp_vec2; + + /// 2 components vector of medium single-qualifier floating-point numbers. + /// + /// @see GLSL 4.20.8 specification, section 4.1.5 Vectors + /// @see GLSL 4.20.8 specification, section 4.7.2 Precision Qualifier + typedef vec<2, float, mediump> mediump_vec2; + + /// 2 components vector of low single-qualifier floating-point numbers. + /// + /// @see GLSL 4.20.8 specification, section 4.1.5 Vectors + /// @see GLSL 4.20.8 specification, section 4.7.2 Precision Qualifier + typedef vec<2, float, lowp> lowp_vec2; + + /// @} +}//namespace glm diff --git a/src/GLMath/glm/ext/vector_float3.hpp b/src/GLMath/glm/ext/vector_float3.hpp new file mode 100644 index 000000000..cd79a6200 --- /dev/null +++ b/src/GLMath/glm/ext/vector_float3.hpp @@ -0,0 +1,18 @@ +/// @ref core +/// @file glm/ext/vector_float3.hpp + +#pragma once +#include "../detail/type_vec3.hpp" + +namespace glm +{ + /// @addtogroup core_vector + /// @{ + + /// 3 components vector of single-precision floating-point numbers. + /// + /// @see GLSL 4.20.8 specification, section 4.1.5 Vectors + typedef vec<3, float, defaultp> vec3; + + /// @} +}//namespace glm diff --git a/src/GLMath/glm/ext/vector_float3_precision.hpp b/src/GLMath/glm/ext/vector_float3_precision.hpp new file mode 100644 index 000000000..be640b531 --- /dev/null +++ b/src/GLMath/glm/ext/vector_float3_precision.hpp @@ -0,0 +1,31 @@ +/// @ref core +/// @file glm/ext/vector_float3_precision.hpp + +#pragma once +#include "../detail/type_vec3.hpp" + +namespace glm +{ + /// @addtogroup core_vector_precision + /// @{ + + /// 3 components vector of high single-qualifier floating-point numbers. + /// + /// @see GLSL 4.20.8 specification, section 4.1.5 Vectors + /// @see GLSL 4.20.8 specification, section 4.7.2 Precision Qualifier + typedef vec<3, float, highp> highp_vec3; + + /// 3 components vector of medium single-qualifier floating-point numbers. + /// + /// @see GLSL 4.20.8 specification, section 4.1.5 Vectors + /// @see GLSL 4.20.8 specification, section 4.7.2 Precision Qualifier + typedef vec<3, float, mediump> mediump_vec3; + + /// 3 components vector of low single-qualifier floating-point numbers. + /// + /// @see GLSL 4.20.8 specification, section 4.1.5 Vectors + /// @see GLSL 4.20.8 specification, section 4.7.2 Precision Qualifier + typedef vec<3, float, lowp> lowp_vec3; + + /// @} +}//namespace glm diff --git a/src/GLMath/glm/ext/vector_float4.hpp b/src/GLMath/glm/ext/vector_float4.hpp new file mode 100644 index 000000000..d84adcc22 --- /dev/null +++ b/src/GLMath/glm/ext/vector_float4.hpp @@ -0,0 +1,18 @@ +/// @ref core +/// @file glm/ext/vector_float4.hpp + +#pragma once +#include "../detail/type_vec4.hpp" + +namespace glm +{ + /// @addtogroup core_vector + /// @{ + + /// 4 components vector of single-precision floating-point numbers. + /// + /// @see GLSL 4.20.8 specification, section 4.1.5 Vectors + typedef vec<4, float, defaultp> vec4; + + /// @} +}//namespace glm diff --git a/src/GLMath/glm/ext/vector_float4_precision.hpp b/src/GLMath/glm/ext/vector_float4_precision.hpp new file mode 100644 index 000000000..aede83882 --- /dev/null +++ b/src/GLMath/glm/ext/vector_float4_precision.hpp @@ -0,0 +1,31 @@ +/// @ref core +/// @file glm/ext/vector_float4_precision.hpp + +#pragma once +#include "../detail/type_vec4.hpp" + +namespace glm +{ + /// @addtogroup core_vector_precision + /// @{ + + /// 4 components vector of high single-qualifier floating-point numbers. + /// + /// @see GLSL 4.20.8 specification, section 4.1.5 Vectors + /// @see GLSL 4.20.8 specification, section 4.7.2 Precision Qualifier + typedef vec<4, float, highp> highp_vec4; + + /// 4 components vector of medium single-qualifier floating-point numbers. + /// + /// @see GLSL 4.20.8 specification, section 4.1.5 Vectors + /// @see GLSL 4.20.8 specification, section 4.7.2 Precision Qualifier + typedef vec<4, float, mediump> mediump_vec4; + + /// 4 components vector of low single-qualifier floating-point numbers. + /// + /// @see GLSL 4.20.8 specification, section 4.1.5 Vectors + /// @see GLSL 4.20.8 specification, section 4.7.2 Precision Qualifier + typedef vec<4, float, lowp> lowp_vec4; + + /// @} +}//namespace glm diff --git a/src/GLMath/glm/ext/vector_int1.hpp b/src/GLMath/glm/ext/vector_int1.hpp new file mode 100644 index 000000000..dc8603891 --- /dev/null +++ b/src/GLMath/glm/ext/vector_int1.hpp @@ -0,0 +1,32 @@ +/// @ref ext_vector_int1 +/// @file glm/ext/vector_int1.hpp +/// +/// @defgroup ext_vector_int1 GLM_EXT_vector_int1 +/// @ingroup ext +/// +/// Exposes ivec1 vector type. +/// +/// Include to use the features of this extension. +/// +/// @see ext_vector_uint1 extension. +/// @see ext_vector_int1_precision extension. + +#pragma once + +#include "../detail/type_vec1.hpp" + +#if GLM_MESSAGES == GLM_ENABLE && !defined(GLM_EXT_INCLUDED) +# pragma message("GLM: GLM_EXT_vector_int1 extension included") +#endif + +namespace glm +{ + /// @addtogroup ext_vector_int1 + /// @{ + + /// 1 component vector of signed integer numbers. + typedef vec<1, int, defaultp> ivec1; + + /// @} +}//namespace glm + diff --git a/src/GLMath/glm/ext/vector_int1_precision.hpp b/src/GLMath/glm/ext/vector_int1_precision.hpp new file mode 100644 index 000000000..3323954b5 --- /dev/null +++ b/src/GLMath/glm/ext/vector_int1_precision.hpp @@ -0,0 +1,34 @@ +/// @ref ext_vector_int1_precision +/// @file glm/ext/vector_int1_precision.hpp +/// +/// @defgroup ext_vector_int1_precision GLM_EXT_vector_int1_precision +/// @ingroup ext +/// +/// Exposes highp_ivec1, mediump_ivec1 and lowp_ivec1 types. +/// +/// Include to use the features of this extension. + +#pragma once + +#include "../detail/type_vec1.hpp" + +#if GLM_MESSAGES == GLM_ENABLE && !defined(GLM_EXT_INCLUDED) +# pragma message("GLM: GLM_EXT_vector_int1_precision extension included") +#endif + +namespace glm +{ + /// @addtogroup ext_vector_int1_precision + /// @{ + + /// 1 component vector of signed integer values. + typedef vec<1, int, highp> highp_ivec1; + + /// 1 component vector of signed integer values. + typedef vec<1, int, mediump> mediump_ivec1; + + /// 1 component vector of signed integer values. + typedef vec<1, int, lowp> lowp_ivec1; + + /// @} +}//namespace glm diff --git a/src/GLMath/glm/ext/vector_int2.hpp b/src/GLMath/glm/ext/vector_int2.hpp new file mode 100644 index 000000000..aef803e91 --- /dev/null +++ b/src/GLMath/glm/ext/vector_int2.hpp @@ -0,0 +1,18 @@ +/// @ref core +/// @file glm/ext/vector_int2.hpp + +#pragma once +#include "../detail/type_vec2.hpp" + +namespace glm +{ + /// @addtogroup core_vector + /// @{ + + /// 2 components vector of signed integer numbers. + /// + /// @see GLSL 4.20.8 specification, section 4.1.5 Vectors + typedef vec<2, int, defaultp> ivec2; + + /// @} +}//namespace glm diff --git a/src/GLMath/glm/ext/vector_int2_precision.hpp b/src/GLMath/glm/ext/vector_int2_precision.hpp new file mode 100644 index 000000000..97315fc31 --- /dev/null +++ b/src/GLMath/glm/ext/vector_int2_precision.hpp @@ -0,0 +1,31 @@ +/// @ref core +/// @file glm/ext/vector_int2_precision.hpp + +#pragma once +#include "../detail/type_vec2.hpp" + +namespace glm +{ + /// @addtogroup core_vector_precision + /// @{ + + /// 2 components vector of high qualifier signed integer numbers. + /// + /// @see GLSL 4.20.8 specification, section 4.1.5 Vectors + /// @see GLSL 4.20.8 specification, section 4.7.2 Precision Qualifier + typedef vec<2, int, highp> highp_ivec2; + + /// 2 components vector of medium qualifier signed integer numbers. + /// + /// @see GLSL 4.20.8 specification, section 4.1.5 Vectors + /// @see GLSL 4.20.8 specification, section 4.7.2 Precision Qualifier + typedef vec<2, int, mediump> mediump_ivec2; + + /// 2 components vector of low qualifier signed integer numbers. + /// + /// @see GLSL 4.20.8 specification, section 4.1.5 Vectors + /// @see GLSL 4.20.8 specification, section 4.7.2 Precision Qualifier + typedef vec<2, int, lowp> lowp_ivec2; + + /// @} +}//namespace glm diff --git a/src/GLMath/glm/ext/vector_int3.hpp b/src/GLMath/glm/ext/vector_int3.hpp new file mode 100644 index 000000000..4767e61e8 --- /dev/null +++ b/src/GLMath/glm/ext/vector_int3.hpp @@ -0,0 +1,18 @@ +/// @ref core +/// @file glm/ext/vector_int3.hpp + +#pragma once +#include "../detail/type_vec3.hpp" + +namespace glm +{ + /// @addtogroup core_vector + /// @{ + + /// 3 components vector of signed integer numbers. + /// + /// @see GLSL 4.20.8 specification, section 4.1.5 Vectors + typedef vec<3, int, defaultp> ivec3; + + /// @} +}//namespace glm diff --git a/src/GLMath/glm/ext/vector_int3_precision.hpp b/src/GLMath/glm/ext/vector_int3_precision.hpp new file mode 100644 index 000000000..2cd3f5ff8 --- /dev/null +++ b/src/GLMath/glm/ext/vector_int3_precision.hpp @@ -0,0 +1,31 @@ +/// @ref core +/// @file glm/ext/vector_int3_precision.hpp + +#pragma once +#include "../detail/type_vec3.hpp" + +namespace glm +{ + /// @addtogroup core_vector_precision + /// @{ + + /// 3 components vector of high qualifier signed integer numbers. + /// + /// @see GLSL 4.20.8 specification, section 4.1.5 Vectors + /// @see GLSL 4.20.8 specification, section 4.7.2 Precision Qualifier + typedef vec<3, int, highp> highp_ivec3; + + /// 3 components vector of medium qualifier signed integer numbers. + /// + /// @see GLSL 4.20.8 specification, section 4.1.5 Vectors + /// @see GLSL 4.20.8 specification, section 4.7.2 Precision Qualifier + typedef vec<3, int, mediump> mediump_ivec3; + + /// 3 components vector of low qualifier signed integer numbers. + /// + /// @see GLSL 4.20.8 specification, section 4.1.5 Vectors + /// @see GLSL 4.20.8 specification, section 4.7.2 Precision Qualifier + typedef vec<3, int, lowp> lowp_ivec3; + + /// @} +}//namespace glm diff --git a/src/GLMath/glm/ext/vector_int4.hpp b/src/GLMath/glm/ext/vector_int4.hpp new file mode 100644 index 000000000..bb23adf70 --- /dev/null +++ b/src/GLMath/glm/ext/vector_int4.hpp @@ -0,0 +1,18 @@ +/// @ref core +/// @file glm/ext/vector_int4.hpp + +#pragma once +#include "../detail/type_vec4.hpp" + +namespace glm +{ + /// @addtogroup core_vector + /// @{ + + /// 4 components vector of signed integer numbers. + /// + /// @see GLSL 4.20.8 specification, section 4.1.5 Vectors + typedef vec<4, int, defaultp> ivec4; + + /// @} +}//namespace glm diff --git a/src/GLMath/glm/ext/vector_int4_precision.hpp b/src/GLMath/glm/ext/vector_int4_precision.hpp new file mode 100644 index 000000000..4fcd79169 --- /dev/null +++ b/src/GLMath/glm/ext/vector_int4_precision.hpp @@ -0,0 +1,31 @@ +/// @ref core +/// @file glm/ext/vector_int4_precision.hpp + +#pragma once +#include "../detail/type_vec4.hpp" + +namespace glm +{ + /// @addtogroup core_vector_precision + /// @{ + + /// 4 components vector of high qualifier signed integer numbers. + /// + /// @see GLSL 4.20.8 specification, section 4.1.5 Vectors + /// @see GLSL 4.20.8 specification, section 4.7.2 Precision Qualifier + typedef vec<4, int, highp> highp_ivec4; + + /// 4 components vector of medium qualifier signed integer numbers. + /// + /// @see GLSL 4.20.8 specification, section 4.1.5 Vectors + /// @see GLSL 4.20.8 specification, section 4.7.2 Precision Qualifier + typedef vec<4, int, mediump> mediump_ivec4; + + /// 4 components vector of low qualifier signed integer numbers. + /// + /// @see GLSL 4.20.8 specification, section 4.1.5 Vectors + /// @see GLSL 4.20.8 specification, section 4.7.2 Precision Qualifier + typedef vec<4, int, lowp> lowp_ivec4; + + /// @} +}//namespace glm diff --git a/src/GLMath/glm/ext/vector_relational.hpp b/src/GLMath/glm/ext/vector_relational.hpp new file mode 100644 index 000000000..a556bdfa9 --- /dev/null +++ b/src/GLMath/glm/ext/vector_relational.hpp @@ -0,0 +1,104 @@ +/// @ref ext_vector_relational +/// @file glm/ext/vector_relational.hpp +/// +/// @defgroup ext_vector_relational GLM_EXT_vector_relational +/// @ingroup ext +/// +/// Exposes comparison functions for vector types that take a user defined epsilon values. +/// +/// Include to use the features of this extension. +/// +/// @see core_vector_relational +/// @see ext_scalar_relational +/// @see ext_matrix_relational + +#pragma once + +// Dependencies +#include "../detail/qualifier.hpp" + +#if GLM_MESSAGES == GLM_ENABLE && !defined(GLM_EXT_INCLUDED) +# pragma message("GLM: GLM_EXT_vector_relational extension included") +#endif + +namespace glm +{ + /// @addtogroup ext_vector_relational + /// @{ + + /// Returns the component-wise comparison of |x - y| < epsilon. + /// True if this expression is satisfied. + /// + /// @tparam L Integer between 1 and 4 included that qualify the dimension of the vector + /// @tparam T Floating-point or integer scalar types + /// @tparam Q Value from qualifier enum + template + GLM_FUNC_DECL GLM_CONSTEXPR vec equal(vec const& x, vec const& y, T epsilon); + + /// Returns the component-wise comparison of |x - y| < epsilon. + /// True if this expression is satisfied. + /// + /// @tparam L Integer between 1 and 4 included that qualify the dimension of the vector + /// @tparam T Floating-point or integer scalar types + /// @tparam Q Value from qualifier enum + template + GLM_FUNC_DECL GLM_CONSTEXPR vec equal(vec const& x, vec const& y, vec const& epsilon); + + /// Returns the component-wise comparison of |x - y| >= epsilon. + /// True if this expression is not satisfied. + /// + /// @tparam L Integer between 1 and 4 included that qualify the dimension of the vector + /// @tparam T Floating-point or integer scalar types + /// @tparam Q Value from qualifier enum + template + GLM_FUNC_DECL GLM_CONSTEXPR vec notEqual(vec const& x, vec const& y, T epsilon); + + /// Returns the component-wise comparison of |x - y| >= epsilon. + /// True if this expression is not satisfied. + /// + /// @tparam L Integer between 1 and 4 included that qualify the dimension of the vector + /// @tparam T Floating-point or integer scalar types + /// @tparam Q Value from qualifier enum + template + GLM_FUNC_DECL GLM_CONSTEXPR vec notEqual(vec const& x, vec const& y, vec const& epsilon); + + /// Returns the component-wise comparison between two vectors in term of ULPs. + /// True if this expression is satisfied. + /// + /// @tparam L Integer between 1 and 4 included that qualify the dimension of the vector + /// @tparam T Floating-point + /// @tparam Q Value from qualifier enum + template + GLM_FUNC_DECL GLM_CONSTEXPR vec equal(vec const& x, vec const& y, int ULPs); + + /// Returns the component-wise comparison between two vectors in term of ULPs. + /// True if this expression is satisfied. + /// + /// @tparam L Integer between 1 and 4 included that qualify the dimension of the vector + /// @tparam T Floating-point + /// @tparam Q Value from qualifier enum + template + GLM_FUNC_DECL GLM_CONSTEXPR vec equal(vec const& x, vec const& y, vec const& ULPs); + + /// Returns the component-wise comparison between two vectors in term of ULPs. + /// True if this expression is not satisfied. + /// + /// @tparam L Integer between 1 and 4 included that qualify the dimension of the vector + /// @tparam T Floating-point + /// @tparam Q Value from qualifier enum + template + GLM_FUNC_DECL GLM_CONSTEXPR vec notEqual(vec const& x, vec const& y, int ULPs); + + /// Returns the component-wise comparison between two vectors in term of ULPs. + /// True if this expression is not satisfied. + /// + /// @tparam L Integer between 1 and 4 included that qualify the dimension of the vector + /// @tparam T Floating-point + /// @tparam Q Value from qualifier enum + template + GLM_FUNC_DECL GLM_CONSTEXPR vec notEqual(vec const& x, vec const& y, vec const& ULPs); + + /// @} +}//namespace glm + +#include "vector_relational.inl" diff --git a/src/GLMath/glm/ext/vector_relational.inl b/src/GLMath/glm/ext/vector_relational.inl new file mode 100644 index 000000000..7a39ab508 --- /dev/null +++ b/src/GLMath/glm/ext/vector_relational.inl @@ -0,0 +1,75 @@ +#include "../vector_relational.hpp" +#include "../common.hpp" +#include "../detail/qualifier.hpp" +#include "../detail/type_float.hpp" + +namespace glm +{ + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec equal(vec const& x, vec const& y, T Epsilon) + { + return equal(x, y, vec(Epsilon)); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec equal(vec const& x, vec const& y, vec const& Epsilon) + { + return lessThanEqual(abs(x - y), Epsilon); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec notEqual(vec const& x, vec const& y, T Epsilon) + { + return notEqual(x, y, vec(Epsilon)); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec notEqual(vec const& x, vec const& y, vec const& Epsilon) + { + return greaterThan(abs(x - y), Epsilon); + } + + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec equal(vec const& x, vec const& y, int MaxULPs) + { + return equal(x, y, vec(MaxULPs)); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec equal(vec const& x, vec const& y, vec const& MaxULPs) + { + vec Result(false); + for(length_t i = 0; i < L; ++i) + { + detail::float_t const a(x[i]); + detail::float_t const b(y[i]); + + // Different signs means they do not match. + if(a.negative() != b.negative()) + { + // Check for equality to make sure +0==-0 + Result[i] = a.mantissa() == b.mantissa() && a.exponent() == b.exponent(); + } + else + { + // Find the difference in ULPs. + typename detail::float_t::int_type const DiffULPs = abs(a.i - b.i); + Result[i] = DiffULPs <= MaxULPs[i]; + } + } + return Result; + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec notEqual(vec const& x, vec const& y, int MaxULPs) + { + return notEqual(x, y, vec(MaxULPs)); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec notEqual(vec const& x, vec const& y, vec const& MaxULPs) + { + return not_(equal(x, y, MaxULPs)); + } +}//namespace glm diff --git a/src/GLMath/glm/ext/vector_uint1.hpp b/src/GLMath/glm/ext/vector_uint1.hpp new file mode 100644 index 000000000..eb8a70497 --- /dev/null +++ b/src/GLMath/glm/ext/vector_uint1.hpp @@ -0,0 +1,32 @@ +/// @ref ext_vector_uint1 +/// @file glm/ext/vector_uint1.hpp +/// +/// @defgroup ext_vector_uint1 GLM_EXT_vector_uint1 +/// @ingroup ext +/// +/// Exposes uvec1 vector type. +/// +/// Include to use the features of this extension. +/// +/// @see ext_vector_int1 extension. +/// @see ext_vector_uint1_precision extension. + +#pragma once + +#include "../detail/type_vec1.hpp" + +#if GLM_MESSAGES == GLM_ENABLE && !defined(GLM_EXT_INCLUDED) +# pragma message("GLM: GLM_EXT_vector_uint1 extension included") +#endif + +namespace glm +{ + /// @addtogroup ext_vector_uint1 + /// @{ + + /// 1 component vector of unsigned integer numbers. + typedef vec<1, unsigned int, defaultp> uvec1; + + /// @} +}//namespace glm + diff --git a/src/GLMath/glm/ext/vector_uint1_precision.hpp b/src/GLMath/glm/ext/vector_uint1_precision.hpp new file mode 100644 index 000000000..30daa5b66 --- /dev/null +++ b/src/GLMath/glm/ext/vector_uint1_precision.hpp @@ -0,0 +1,40 @@ +/// @ref ext_vector_uint1_precision +/// @file glm/ext/vector_uint1_precision.hpp +/// +/// @defgroup ext_vector_uint1_precision GLM_EXT_vector_uint1_precision +/// @ingroup ext +/// +/// Exposes highp_uvec1, mediump_uvec1 and lowp_uvec1 types. +/// +/// Include to use the features of this extension. + +#pragma once + +#include "../detail/type_vec1.hpp" + +#if GLM_MESSAGES == GLM_ENABLE && !defined(GLM_EXT_INCLUDED) +# pragma message("GLM: GLM_EXT_vector_uint1_precision extension included") +#endif + +namespace glm +{ + /// @addtogroup ext_vector_uint1_precision + /// @{ + + /// 1 component vector of unsigned integer values. + /// + /// @see ext_vector_uint1_precision + typedef vec<1, unsigned int, highp> highp_uvec1; + + /// 1 component vector of unsigned integer values. + /// + /// @see ext_vector_uint1_precision + typedef vec<1, unsigned int, mediump> mediump_uvec1; + + /// 1 component vector of unsigned integer values. + /// + /// @see ext_vector_uint1_precision + typedef vec<1, unsigned int, lowp> lowp_uvec1; + + /// @} +}//namespace glm diff --git a/src/GLMath/glm/ext/vector_uint2.hpp b/src/GLMath/glm/ext/vector_uint2.hpp new file mode 100644 index 000000000..03c00f5ff --- /dev/null +++ b/src/GLMath/glm/ext/vector_uint2.hpp @@ -0,0 +1,18 @@ +/// @ref core +/// @file glm/ext/vector_uint2.hpp + +#pragma once +#include "../detail/type_vec2.hpp" + +namespace glm +{ + /// @addtogroup core_vector + /// @{ + + /// 2 components vector of unsigned integer numbers. + /// + /// @see GLSL 4.20.8 specification, section 4.1.5 Vectors + typedef vec<2, unsigned int, defaultp> uvec2; + + /// @} +}//namespace glm diff --git a/src/GLMath/glm/ext/vector_uint2_precision.hpp b/src/GLMath/glm/ext/vector_uint2_precision.hpp new file mode 100644 index 000000000..2ba7b0d43 --- /dev/null +++ b/src/GLMath/glm/ext/vector_uint2_precision.hpp @@ -0,0 +1,31 @@ +/// @ref core +/// @file glm/ext/vector_uint2_precision.hpp + +#pragma once +#include "../detail/type_vec2.hpp" + +namespace glm +{ + /// @addtogroup core_vector_precision + /// @{ + + /// 2 components vector of high qualifier unsigned integer numbers. + /// + /// @see GLSL 4.20.8 specification, section 4.1.5 Vectors + /// @see GLSL 4.20.8 specification, section 4.7.2 Precision Qualifier + typedef vec<2, unsigned int, highp> highp_uvec2; + + /// 2 components vector of medium qualifier unsigned integer numbers. + /// + /// @see GLSL 4.20.8 specification, section 4.1.5 Vectors + /// @see GLSL 4.20.8 specification, section 4.7.2 Precision Qualifier + typedef vec<2, unsigned int, mediump> mediump_uvec2; + + /// 2 components vector of low qualifier unsigned integer numbers. + /// + /// @see GLSL 4.20.8 specification, section 4.1.5 Vectors + /// @see GLSL 4.20.8 specification, section 4.7.2 Precision Qualifier + typedef vec<2, unsigned int, lowp> lowp_uvec2; + + /// @} +}//namespace glm diff --git a/src/GLMath/glm/ext/vector_uint3.hpp b/src/GLMath/glm/ext/vector_uint3.hpp new file mode 100644 index 000000000..f5b41c408 --- /dev/null +++ b/src/GLMath/glm/ext/vector_uint3.hpp @@ -0,0 +1,18 @@ +/// @ref core +/// @file glm/ext/vector_uint3.hpp + +#pragma once +#include "../detail/type_vec3.hpp" + +namespace glm +{ + /// @addtogroup core_vector + /// @{ + + /// 3 components vector of unsigned integer numbers. + /// + /// @see GLSL 4.20.8 specification, section 4.1.5 Vectors + typedef vec<3, unsigned int, defaultp> uvec3; + + /// @} +}//namespace glm diff --git a/src/GLMath/glm/ext/vector_uint3_precision.hpp b/src/GLMath/glm/ext/vector_uint3_precision.hpp new file mode 100644 index 000000000..125191c77 --- /dev/null +++ b/src/GLMath/glm/ext/vector_uint3_precision.hpp @@ -0,0 +1,31 @@ +/// @ref core +/// @file glm/ext/vector_uint3_precision.hpp + +#pragma once +#include "../detail/type_vec3.hpp" + +namespace glm +{ + /// @addtogroup core_vector_precision + /// @{ + + /// 3 components vector of high qualifier unsigned integer numbers. + /// + /// @see GLSL 4.20.8 specification, section 4.1.5 Vectors + /// @see GLSL 4.20.8 specification, section 4.7.2 Precision Qualifier + typedef vec<3, unsigned int, highp> highp_uvec3; + + /// 3 components vector of medium qualifier unsigned integer numbers. + /// + /// @see GLSL 4.20.8 specification, section 4.1.5 Vectors + /// @see GLSL 4.20.8 specification, section 4.7.2 Precision Qualifier + typedef vec<3, unsigned int, mediump> mediump_uvec3; + + /// 3 components vector of low qualifier unsigned integer numbers. + /// + /// @see GLSL 4.20.8 specification, section 4.1.5 Vectors + /// @see GLSL 4.20.8 specification, section 4.7.2 Precision Qualifier + typedef vec<3, unsigned int, lowp> lowp_uvec3; + + /// @} +}//namespace glm diff --git a/src/GLMath/glm/ext/vector_uint4.hpp b/src/GLMath/glm/ext/vector_uint4.hpp new file mode 100644 index 000000000..32ced58a8 --- /dev/null +++ b/src/GLMath/glm/ext/vector_uint4.hpp @@ -0,0 +1,18 @@ +/// @ref core +/// @file glm/ext/vector_uint4.hpp + +#pragma once +#include "../detail/type_vec4.hpp" + +namespace glm +{ + /// @addtogroup core_vector + /// @{ + + /// 4 components vector of unsigned integer numbers. + /// + /// @see GLSL 4.20.8 specification, section 4.1.5 Vectors + typedef vec<4, unsigned int, defaultp> uvec4; + + /// @} +}//namespace glm diff --git a/src/GLMath/glm/ext/vector_uint4_precision.hpp b/src/GLMath/glm/ext/vector_uint4_precision.hpp new file mode 100644 index 000000000..cf4097ce9 --- /dev/null +++ b/src/GLMath/glm/ext/vector_uint4_precision.hpp @@ -0,0 +1,31 @@ +/// @ref core +/// @file glm/ext/vector_uint4_precision.hpp + +#pragma once +#include "../detail/type_vec4.hpp" + +namespace glm +{ + /// @addtogroup core_vector_precision + /// @{ + + /// 4 components vector of high qualifier unsigned integer numbers. + /// + /// @see GLSL 4.20.8 specification, section 4.1.5 Vectors + /// @see GLSL 4.20.8 specification, section 4.7.2 Precision Qualifier + typedef vec<4, unsigned int, highp> highp_uvec4; + + /// 4 components vector of medium qualifier unsigned integer numbers. + /// + /// @see GLSL 4.20.8 specification, section 4.1.5 Vectors + /// @see GLSL 4.20.8 specification, section 4.7.2 Precision Qualifier + typedef vec<4, unsigned int, mediump> mediump_uvec4; + + /// 4 components vector of low qualifier unsigned integer numbers. + /// + /// @see GLSL 4.20.8 specification, section 4.1.5 Vectors + /// @see GLSL 4.20.8 specification, section 4.7.2 Precision Qualifier + typedef vec<4, unsigned int, lowp> lowp_uvec4; + + /// @} +}//namespace glm diff --git a/src/GLMath/glm/ext/vector_ulp.hpp b/src/GLMath/glm/ext/vector_ulp.hpp new file mode 100644 index 000000000..3883758f0 --- /dev/null +++ b/src/GLMath/glm/ext/vector_ulp.hpp @@ -0,0 +1,109 @@ +/// @ref ext_vector_ulp +/// @file glm/ext/vector_ulp.hpp +/// +/// @defgroup ext_vector_ulp GLM_EXT_vector_ulp +/// @ingroup ext +/// +/// Allow the measurement of the accuracy of a function against a reference +/// implementation. This extension works on floating-point data and provide results +/// in ULP. +/// +/// Include to use the features of this extension. +/// +/// @see ext_scalar_ulp +/// @see ext_scalar_relational +/// @see ext_vector_relational + +#pragma once + +// Dependencies +#include "../ext/scalar_ulp.hpp" + +#if GLM_MESSAGES == GLM_ENABLE && !defined(GLM_EXT_INCLUDED) +# pragma message("GLM: GLM_EXT_vector_ulp extension included") +#endif + +namespace glm +{ + /// Return the next ULP value(s) after the input value(s). + /// + /// @tparam L Integer between 1 and 4 included that qualify the dimension of the vector + /// @tparam T Floating-point + /// @tparam Q Value from qualifier enum + /// + /// @see ext_scalar_ulp + template + GLM_FUNC_DECL vec next_float(vec const& x); + + /// Return the value(s) ULP distance after the input value(s). + /// + /// @tparam L Integer between 1 and 4 included that qualify the dimension of the vector + /// @tparam T Floating-point + /// @tparam Q Value from qualifier enum + /// + /// @see ext_scalar_ulp + template + GLM_FUNC_DECL vec next_float(vec const& x, int ULPs); + + /// Return the value(s) ULP distance after the input value(s). + /// + /// @tparam L Integer between 1 and 4 included that qualify the dimension of the vector + /// @tparam T Floating-point + /// @tparam Q Value from qualifier enum + /// + /// @see ext_scalar_ulp + template + GLM_FUNC_DECL vec next_float(vec const& x, vec const& ULPs); + + /// Return the previous ULP value(s) before the input value(s). + /// + /// @tparam L Integer between 1 and 4 included that qualify the dimension of the vector + /// @tparam T Floating-point + /// @tparam Q Value from qualifier enum + /// + /// @see ext_scalar_ulp + template + GLM_FUNC_DECL vec prev_float(vec const& x); + + /// Return the value(s) ULP distance before the input value(s). + /// + /// @tparam L Integer between 1 and 4 included that qualify the dimension of the vector + /// @tparam T Floating-point + /// @tparam Q Value from qualifier enum + /// + /// @see ext_scalar_ulp + template + GLM_FUNC_DECL vec prev_float(vec const& x, int ULPs); + + /// Return the value(s) ULP distance before the input value(s). + /// + /// @tparam L Integer between 1 and 4 included that qualify the dimension of the vector + /// @tparam T Floating-point + /// @tparam Q Value from qualifier enum + /// + /// @see ext_scalar_ulp + template + GLM_FUNC_DECL vec prev_float(vec const& x, vec const& ULPs); + + /// Return the distance in the number of ULP between 2 single-precision floating-point scalars. + /// + /// @tparam L Integer between 1 and 4 included that qualify the dimension of the vector + /// @tparam Q Value from qualifier enum + /// + /// @see ext_scalar_ulp + template + GLM_FUNC_DECL vec float_distance(vec const& x, vec const& y); + + /// Return the distance in the number of ULP between 2 double-precision floating-point scalars. + /// + /// @tparam L Integer between 1 and 4 included that qualify the dimension of the vector + /// @tparam Q Value from qualifier enum + /// + /// @see ext_scalar_ulp + template + GLM_FUNC_DECL vec float_distance(vec const& x, vec const& y); + + /// @} +}//namespace glm + +#include "vector_ulp.inl" diff --git a/src/GLMath/glm/ext/vector_ulp.inl b/src/GLMath/glm/ext/vector_ulp.inl new file mode 100644 index 000000000..864653431 --- /dev/null +++ b/src/GLMath/glm/ext/vector_ulp.inl @@ -0,0 +1,74 @@ +namespace glm +{ + template + GLM_FUNC_QUALIFIER vec next_float(vec const& x) + { + vec Result; + for(length_t i = 0, n = Result.length(); i < n; ++i) + Result[i] = next_float(x[i]); + return Result; + } + + template + GLM_FUNC_QUALIFIER vec next_float(vec const& x, int ULPs) + { + vec Result; + for(length_t i = 0, n = Result.length(); i < n; ++i) + Result[i] = next_float(x[i], ULPs); + return Result; + } + + template + GLM_FUNC_QUALIFIER vec next_float(vec const& x, vec const& ULPs) + { + vec Result; + for(length_t i = 0, n = Result.length(); i < n; ++i) + Result[i] = next_float(x[i], ULPs[i]); + return Result; + } + + template + GLM_FUNC_QUALIFIER vec prev_float(vec const& x) + { + vec Result; + for(length_t i = 0, n = Result.length(); i < n; ++i) + Result[i] = prev_float(x[i]); + return Result; + } + + template + GLM_FUNC_QUALIFIER vec prev_float(vec const& x, int ULPs) + { + vec Result; + for(length_t i = 0, n = Result.length(); i < n; ++i) + Result[i] = prev_float(x[i], ULPs); + return Result; + } + + template + GLM_FUNC_QUALIFIER vec prev_float(vec const& x, vec const& ULPs) + { + vec Result; + for(length_t i = 0, n = Result.length(); i < n; ++i) + Result[i] = prev_float(x[i], ULPs[i]); + return Result; + } + + template + GLM_FUNC_QUALIFIER vec float_distance(vec const& x, vec const& y) + { + vec Result; + for(length_t i = 0, n = Result.length(); i < n; ++i) + Result[i] = float_distance(x[i], y[i]); + return Result; + } + + template + GLM_FUNC_QUALIFIER vec float_distance(vec const& x, vec const& y) + { + vec Result; + for(length_t i = 0, n = Result.length(); i < n; ++i) + Result[i] = float_distance(x[i], y[i]); + return Result; + } +}//namespace glm diff --git a/src/GLMath/glm/fwd.hpp b/src/GLMath/glm/fwd.hpp new file mode 100644 index 000000000..474d44f76 --- /dev/null +++ b/src/GLMath/glm/fwd.hpp @@ -0,0 +1,818 @@ +#pragma once + +#include "detail/qualifier.hpp" + +namespace glm +{ +#if GLM_HAS_EXTENDED_INTEGER_TYPE + typedef std::int8_t int8; + typedef std::int16_t int16; + typedef std::int32_t int32; + typedef std::int64_t int64; + + typedef std::uint8_t uint8; + typedef std::uint16_t uint16; + typedef std::uint32_t uint32; + typedef std::uint64_t uint64; +#else + typedef signed char int8; + typedef signed short int16; + typedef signed int int32; + typedef detail::int64 int64; + + typedef unsigned char uint8; + typedef unsigned short uint16; + typedef unsigned int uint32; + typedef detail::uint64 uint64; +#endif + + // Scalar int + + typedef int8 lowp_i8; + typedef int8 mediump_i8; + typedef int8 highp_i8; + typedef int8 i8; + + typedef int8 lowp_int8; + typedef int8 mediump_int8; + typedef int8 highp_int8; + + typedef int8 lowp_int8_t; + typedef int8 mediump_int8_t; + typedef int8 highp_int8_t; + typedef int8 int8_t; + + typedef int16 lowp_i16; + typedef int16 mediump_i16; + typedef int16 highp_i16; + typedef int16 i16; + + typedef int16 lowp_int16; + typedef int16 mediump_int16; + typedef int16 highp_int16; + + typedef int16 lowp_int16_t; + typedef int16 mediump_int16_t; + typedef int16 highp_int16_t; + typedef int16 int16_t; + + typedef int32 lowp_i32; + typedef int32 mediump_i32; + typedef int32 highp_i32; + typedef int32 i32; + + typedef int32 lowp_int32; + typedef int32 mediump_int32; + typedef int32 highp_int32; + + typedef int32 lowp_int32_t; + typedef int32 mediump_int32_t; + typedef int32 highp_int32_t; + typedef int32 int32_t; + + typedef int64 lowp_i64; + typedef int64 mediump_i64; + typedef int64 highp_i64; + typedef int64 i64; + + typedef int64 lowp_int64; + typedef int64 mediump_int64; + typedef int64 highp_int64; + + typedef int64 lowp_int64_t; + typedef int64 mediump_int64_t; + typedef int64 highp_int64_t; + typedef int64 int64_t; + + // Scalar uint + + typedef uint8 lowp_u8; + typedef uint8 mediump_u8; + typedef uint8 highp_u8; + typedef uint8 u8; + + typedef uint8 lowp_uint8; + typedef uint8 mediump_uint8; + typedef uint8 highp_uint8; + + typedef uint8 lowp_uint8_t; + typedef uint8 mediump_uint8_t; + typedef uint8 highp_uint8_t; + typedef uint8 uint8_t; + + typedef uint16 lowp_u16; + typedef uint16 mediump_u16; + typedef uint16 highp_u16; + typedef uint16 u16; + + typedef uint16 lowp_uint16; + typedef uint16 mediump_uint16; + typedef uint16 highp_uint16; + + typedef uint16 lowp_uint16_t; + typedef uint16 mediump_uint16_t; + typedef uint16 highp_uint16_t; + typedef uint16 uint16_t; + + typedef uint32 lowp_u32; + typedef uint32 mediump_u32; + typedef uint32 highp_u32; + typedef uint32 u32; + + typedef uint32 lowp_uint32; + typedef uint32 mediump_uint32; + typedef uint32 highp_uint32; + + typedef uint32 lowp_uint32_t; + typedef uint32 mediump_uint32_t; + typedef uint32 highp_uint32_t; + typedef uint32 uint32_t; + + typedef uint64 lowp_u64; + typedef uint64 mediump_u64; + typedef uint64 highp_u64; + typedef uint64 u64; + + typedef uint64 lowp_uint64; + typedef uint64 mediump_uint64; + typedef uint64 highp_uint64; + + typedef uint64 lowp_uint64_t; + typedef uint64 mediump_uint64_t; + typedef uint64 highp_uint64_t; + typedef uint64 uint64_t; + + // Scalar float + + typedef float lowp_f32; + typedef float mediump_f32; + typedef float highp_f32; + typedef float f32; + + typedef float lowp_float32; + typedef float mediump_float32; + typedef float highp_float32; + typedef float float32; + + typedef float lowp_float32_t; + typedef float mediump_float32_t; + typedef float highp_float32_t; + typedef float float32_t; + + + typedef double lowp_f64; + typedef double mediump_f64; + typedef double highp_f64; + typedef double f64; + + typedef double lowp_float64; + typedef double mediump_float64; + typedef double highp_float64; + typedef double float64; + + typedef double lowp_float64_t; + typedef double mediump_float64_t; + typedef double highp_float64_t; + typedef double float64_t; + + // Vector bool + + typedef vec<1, bool, lowp> lowp_bvec1; + typedef vec<2, bool, lowp> lowp_bvec2; + typedef vec<3, bool, lowp> lowp_bvec3; + typedef vec<4, bool, lowp> lowp_bvec4; + + typedef vec<1, bool, mediump> mediump_bvec1; + typedef vec<2, bool, mediump> mediump_bvec2; + typedef vec<3, bool, mediump> mediump_bvec3; + typedef vec<4, bool, mediump> mediump_bvec4; + + typedef vec<1, bool, highp> highp_bvec1; + typedef vec<2, bool, highp> highp_bvec2; + typedef vec<3, bool, highp> highp_bvec3; + typedef vec<4, bool, highp> highp_bvec4; + + typedef vec<1, bool, defaultp> bvec1; + typedef vec<2, bool, defaultp> bvec2; + typedef vec<3, bool, defaultp> bvec3; + typedef vec<4, bool, defaultp> bvec4; + + // Vector int + + typedef vec<1, i32, lowp> lowp_ivec1; + typedef vec<2, i32, lowp> lowp_ivec2; + typedef vec<3, i32, lowp> lowp_ivec3; + typedef vec<4, i32, lowp> lowp_ivec4; + + typedef vec<1, i32, mediump> mediump_ivec1; + typedef vec<2, i32, mediump> mediump_ivec2; + typedef vec<3, i32, mediump> mediump_ivec3; + typedef vec<4, i32, mediump> mediump_ivec4; + + typedef vec<1, i32, highp> highp_ivec1; + typedef vec<2, i32, highp> highp_ivec2; + typedef vec<3, i32, highp> highp_ivec3; + typedef vec<4, i32, highp> highp_ivec4; + + typedef vec<1, i32, defaultp> ivec1; + typedef vec<2, i32, defaultp> ivec2; + typedef vec<3, i32, defaultp> ivec3; + typedef vec<4, i32, defaultp> ivec4; + + typedef vec<1, i8, lowp> lowp_i8vec1; + typedef vec<2, i8, lowp> lowp_i8vec2; + typedef vec<3, i8, lowp> lowp_i8vec3; + typedef vec<4, i8, lowp> lowp_i8vec4; + + typedef vec<1, i8, mediump> mediump_i8vec1; + typedef vec<2, i8, mediump> mediump_i8vec2; + typedef vec<3, i8, mediump> mediump_i8vec3; + typedef vec<4, i8, mediump> mediump_i8vec4; + + typedef vec<1, i8, highp> highp_i8vec1; + typedef vec<2, i8, highp> highp_i8vec2; + typedef vec<3, i8, highp> highp_i8vec3; + typedef vec<4, i8, highp> highp_i8vec4; + + typedef vec<1, i8, defaultp> i8vec1; + typedef vec<2, i8, defaultp> i8vec2; + typedef vec<3, i8, defaultp> i8vec3; + typedef vec<4, i8, defaultp> i8vec4; + + typedef vec<1, i16, lowp> lowp_i16vec1; + typedef vec<2, i16, lowp> lowp_i16vec2; + typedef vec<3, i16, lowp> lowp_i16vec3; + typedef vec<4, i16, lowp> lowp_i16vec4; + + typedef vec<1, i16, mediump> mediump_i16vec1; + typedef vec<2, i16, mediump> mediump_i16vec2; + typedef vec<3, i16, mediump> mediump_i16vec3; + typedef vec<4, i16, mediump> mediump_i16vec4; + + typedef vec<1, i16, highp> highp_i16vec1; + typedef vec<2, i16, highp> highp_i16vec2; + typedef vec<3, i16, highp> highp_i16vec3; + typedef vec<4, i16, highp> highp_i16vec4; + + typedef vec<1, i16, defaultp> i16vec1; + typedef vec<2, i16, defaultp> i16vec2; + typedef vec<3, i16, defaultp> i16vec3; + typedef vec<4, i16, defaultp> i16vec4; + + typedef vec<1, i32, lowp> lowp_i32vec1; + typedef vec<2, i32, lowp> lowp_i32vec2; + typedef vec<3, i32, lowp> lowp_i32vec3; + typedef vec<4, i32, lowp> lowp_i32vec4; + + typedef vec<1, i32, mediump> mediump_i32vec1; + typedef vec<2, i32, mediump> mediump_i32vec2; + typedef vec<3, i32, mediump> mediump_i32vec3; + typedef vec<4, i32, mediump> mediump_i32vec4; + + typedef vec<1, i32, highp> highp_i32vec1; + typedef vec<2, i32, highp> highp_i32vec2; + typedef vec<3, i32, highp> highp_i32vec3; + typedef vec<4, i32, highp> highp_i32vec4; + + typedef vec<1, i32, defaultp> i32vec1; + typedef vec<2, i32, defaultp> i32vec2; + typedef vec<3, i32, defaultp> i32vec3; + typedef vec<4, i32, defaultp> i32vec4; + + typedef vec<1, i64, lowp> lowp_i64vec1; + typedef vec<2, i64, lowp> lowp_i64vec2; + typedef vec<3, i64, lowp> lowp_i64vec3; + typedef vec<4, i64, lowp> lowp_i64vec4; + + typedef vec<1, i64, mediump> mediump_i64vec1; + typedef vec<2, i64, mediump> mediump_i64vec2; + typedef vec<3, i64, mediump> mediump_i64vec3; + typedef vec<4, i64, mediump> mediump_i64vec4; + + typedef vec<1, i64, highp> highp_i64vec1; + typedef vec<2, i64, highp> highp_i64vec2; + typedef vec<3, i64, highp> highp_i64vec3; + typedef vec<4, i64, highp> highp_i64vec4; + + typedef vec<1, i64, defaultp> i64vec1; + typedef vec<2, i64, defaultp> i64vec2; + typedef vec<3, i64, defaultp> i64vec3; + typedef vec<4, i64, defaultp> i64vec4; + + // Vector uint + + typedef vec<1, u32, lowp> lowp_uvec1; + typedef vec<2, u32, lowp> lowp_uvec2; + typedef vec<3, u32, lowp> lowp_uvec3; + typedef vec<4, u32, lowp> lowp_uvec4; + + typedef vec<1, u32, mediump> mediump_uvec1; + typedef vec<2, u32, mediump> mediump_uvec2; + typedef vec<3, u32, mediump> mediump_uvec3; + typedef vec<4, u32, mediump> mediump_uvec4; + + typedef vec<1, u32, highp> highp_uvec1; + typedef vec<2, u32, highp> highp_uvec2; + typedef vec<3, u32, highp> highp_uvec3; + typedef vec<4, u32, highp> highp_uvec4; + + typedef vec<1, u32, defaultp> uvec1; + typedef vec<2, u32, defaultp> uvec2; + typedef vec<3, u32, defaultp> uvec3; + typedef vec<4, u32, defaultp> uvec4; + + typedef vec<1, u8, lowp> lowp_u8vec1; + typedef vec<2, u8, lowp> lowp_u8vec2; + typedef vec<3, u8, lowp> lowp_u8vec3; + typedef vec<4, u8, lowp> lowp_u8vec4; + + typedef vec<1, u8, mediump> mediump_u8vec1; + typedef vec<2, u8, mediump> mediump_u8vec2; + typedef vec<3, u8, mediump> mediump_u8vec3; + typedef vec<4, u8, mediump> mediump_u8vec4; + + typedef vec<1, u8, highp> highp_u8vec1; + typedef vec<2, u8, highp> highp_u8vec2; + typedef vec<3, u8, highp> highp_u8vec3; + typedef vec<4, u8, highp> highp_u8vec4; + + typedef vec<1, u8, defaultp> u8vec1; + typedef vec<2, u8, defaultp> u8vec2; + typedef vec<3, u8, defaultp> u8vec3; + typedef vec<4, u8, defaultp> u8vec4; + + typedef vec<1, u16, lowp> lowp_u16vec1; + typedef vec<2, u16, lowp> lowp_u16vec2; + typedef vec<3, u16, lowp> lowp_u16vec3; + typedef vec<4, u16, lowp> lowp_u16vec4; + + typedef vec<1, u16, mediump> mediump_u16vec1; + typedef vec<2, u16, mediump> mediump_u16vec2; + typedef vec<3, u16, mediump> mediump_u16vec3; + typedef vec<4, u16, mediump> mediump_u16vec4; + + typedef vec<1, u16, highp> highp_u16vec1; + typedef vec<2, u16, highp> highp_u16vec2; + typedef vec<3, u16, highp> highp_u16vec3; + typedef vec<4, u16, highp> highp_u16vec4; + + typedef vec<1, u16, defaultp> u16vec1; + typedef vec<2, u16, defaultp> u16vec2; + typedef vec<3, u16, defaultp> u16vec3; + typedef vec<4, u16, defaultp> u16vec4; + + typedef vec<1, u32, lowp> lowp_u32vec1; + typedef vec<2, u32, lowp> lowp_u32vec2; + typedef vec<3, u32, lowp> lowp_u32vec3; + typedef vec<4, u32, lowp> lowp_u32vec4; + + typedef vec<1, u32, mediump> mediump_u32vec1; + typedef vec<2, u32, mediump> mediump_u32vec2; + typedef vec<3, u32, mediump> mediump_u32vec3; + typedef vec<4, u32, mediump> mediump_u32vec4; + + typedef vec<1, u32, highp> highp_u32vec1; + typedef vec<2, u32, highp> highp_u32vec2; + typedef vec<3, u32, highp> highp_u32vec3; + typedef vec<4, u32, highp> highp_u32vec4; + + typedef vec<1, u32, defaultp> u32vec1; + typedef vec<2, u32, defaultp> u32vec2; + typedef vec<3, u32, defaultp> u32vec3; + typedef vec<4, u32, defaultp> u32vec4; + + typedef vec<1, u64, lowp> lowp_u64vec1; + typedef vec<2, u64, lowp> lowp_u64vec2; + typedef vec<3, u64, lowp> lowp_u64vec3; + typedef vec<4, u64, lowp> lowp_u64vec4; + + typedef vec<1, u64, mediump> mediump_u64vec1; + typedef vec<2, u64, mediump> mediump_u64vec2; + typedef vec<3, u64, mediump> mediump_u64vec3; + typedef vec<4, u64, mediump> mediump_u64vec4; + + typedef vec<1, u64, highp> highp_u64vec1; + typedef vec<2, u64, highp> highp_u64vec2; + typedef vec<3, u64, highp> highp_u64vec3; + typedef vec<4, u64, highp> highp_u64vec4; + + typedef vec<1, u64, defaultp> u64vec1; + typedef vec<2, u64, defaultp> u64vec2; + typedef vec<3, u64, defaultp> u64vec3; + typedef vec<4, u64, defaultp> u64vec4; + + // Vector float + + typedef vec<1, float, lowp> lowp_vec1; + typedef vec<2, float, lowp> lowp_vec2; + typedef vec<3, float, lowp> lowp_vec3; + typedef vec<4, float, lowp> lowp_vec4; + + typedef vec<1, float, mediump> mediump_vec1; + typedef vec<2, float, mediump> mediump_vec2; + typedef vec<3, float, mediump> mediump_vec3; + typedef vec<4, float, mediump> mediump_vec4; + + typedef vec<1, float, highp> highp_vec1; + typedef vec<2, float, highp> highp_vec2; + typedef vec<3, float, highp> highp_vec3; + typedef vec<4, float, highp> highp_vec4; + + typedef vec<1, float, defaultp> vec1; + typedef vec<2, float, defaultp> vec2; + typedef vec<3, float, defaultp> vec3; + typedef vec<4, float, defaultp> vec4; + + typedef vec<1, float, lowp> lowp_fvec1; + typedef vec<2, float, lowp> lowp_fvec2; + typedef vec<3, float, lowp> lowp_fvec3; + typedef vec<4, float, lowp> lowp_fvec4; + + typedef vec<1, float, mediump> mediump_fvec1; + typedef vec<2, float, mediump> mediump_fvec2; + typedef vec<3, float, mediump> mediump_fvec3; + typedef vec<4, float, mediump> mediump_fvec4; + + typedef vec<1, float, highp> highp_fvec1; + typedef vec<2, float, highp> highp_fvec2; + typedef vec<3, float, highp> highp_fvec3; + typedef vec<4, float, highp> highp_fvec4; + + typedef vec<1, f32, defaultp> fvec1; + typedef vec<2, f32, defaultp> fvec2; + typedef vec<3, f32, defaultp> fvec3; + typedef vec<4, f32, defaultp> fvec4; + + typedef vec<1, f32, lowp> lowp_f32vec1; + typedef vec<2, f32, lowp> lowp_f32vec2; + typedef vec<3, f32, lowp> lowp_f32vec3; + typedef vec<4, f32, lowp> lowp_f32vec4; + + typedef vec<1, f32, mediump> mediump_f32vec1; + typedef vec<2, f32, mediump> mediump_f32vec2; + typedef vec<3, f32, mediump> mediump_f32vec3; + typedef vec<4, f32, mediump> mediump_f32vec4; + + typedef vec<1, f32, highp> highp_f32vec1; + typedef vec<2, f32, highp> highp_f32vec2; + typedef vec<3, f32, highp> highp_f32vec3; + typedef vec<4, f32, highp> highp_f32vec4; + + typedef vec<1, f32, defaultp> f32vec1; + typedef vec<2, f32, defaultp> f32vec2; + typedef vec<3, f32, defaultp> f32vec3; + typedef vec<4, f32, defaultp> f32vec4; + + typedef vec<1, f64, lowp> lowp_dvec1; + typedef vec<2, f64, lowp> lowp_dvec2; + typedef vec<3, f64, lowp> lowp_dvec3; + typedef vec<4, f64, lowp> lowp_dvec4; + + typedef vec<1, f64, mediump> mediump_dvec1; + typedef vec<2, f64, mediump> mediump_dvec2; + typedef vec<3, f64, mediump> mediump_dvec3; + typedef vec<4, f64, mediump> mediump_dvec4; + + typedef vec<1, f64, highp> highp_dvec1; + typedef vec<2, f64, highp> highp_dvec2; + typedef vec<3, f64, highp> highp_dvec3; + typedef vec<4, f64, highp> highp_dvec4; + + typedef vec<1, f64, defaultp> dvec1; + typedef vec<2, f64, defaultp> dvec2; + typedef vec<3, f64, defaultp> dvec3; + typedef vec<4, f64, defaultp> dvec4; + + typedef vec<1, f64, lowp> lowp_f64vec1; + typedef vec<2, f64, lowp> lowp_f64vec2; + typedef vec<3, f64, lowp> lowp_f64vec3; + typedef vec<4, f64, lowp> lowp_f64vec4; + + typedef vec<1, f64, mediump> mediump_f64vec1; + typedef vec<2, f64, mediump> mediump_f64vec2; + typedef vec<3, f64, mediump> mediump_f64vec3; + typedef vec<4, f64, mediump> mediump_f64vec4; + + typedef vec<1, f64, highp> highp_f64vec1; + typedef vec<2, f64, highp> highp_f64vec2; + typedef vec<3, f64, highp> highp_f64vec3; + typedef vec<4, f64, highp> highp_f64vec4; + + typedef vec<1, f64, defaultp> f64vec1; + typedef vec<2, f64, defaultp> f64vec2; + typedef vec<3, f64, defaultp> f64vec3; + typedef vec<4, f64, defaultp> f64vec4; + + // Matrix NxN + + typedef mat<2, 2, f32, lowp> lowp_mat2; + typedef mat<3, 3, f32, lowp> lowp_mat3; + typedef mat<4, 4, f32, lowp> lowp_mat4; + + typedef mat<2, 2, f32, mediump> mediump_mat2; + typedef mat<3, 3, f32, mediump> mediump_mat3; + typedef mat<4, 4, f32, mediump> mediump_mat4; + + typedef mat<2, 2, f32, highp> highp_mat2; + typedef mat<3, 3, f32, highp> highp_mat3; + typedef mat<4, 4, f32, highp> highp_mat4; + + typedef mat<2, 2, f32, defaultp> mat2; + typedef mat<3, 3, f32, defaultp> mat3; + typedef mat<4, 4, f32, defaultp> mat4; + + typedef mat<2, 2, f32, lowp> lowp_fmat2; + typedef mat<3, 3, f32, lowp> lowp_fmat3; + typedef mat<4, 4, f32, lowp> lowp_fmat4; + + typedef mat<2, 2, f32, mediump> mediump_fmat2; + typedef mat<3, 3, f32, mediump> mediump_fmat3; + typedef mat<4, 4, f32, mediump> mediump_fmat4; + + typedef mat<2, 2, f32, highp> highp_fmat2; + typedef mat<3, 3, f32, highp> highp_fmat3; + typedef mat<4, 4, f32, highp> highp_fmat4; + + typedef mat<2, 2, f32, defaultp> fmat2; + typedef mat<3, 3, f32, defaultp> fmat3; + typedef mat<4, 4, f32, defaultp> fmat4; + + typedef mat<2, 2, f32, lowp> lowp_f32mat2; + typedef mat<3, 3, f32, lowp> lowp_f32mat3; + typedef mat<4, 4, f32, lowp> lowp_f32mat4; + + typedef mat<2, 2, f32, mediump> mediump_f32mat2; + typedef mat<3, 3, f32, mediump> mediump_f32mat3; + typedef mat<4, 4, f32, mediump> mediump_f32mat4; + + typedef mat<2, 2, f32, highp> highp_f32mat2; + typedef mat<3, 3, f32, highp> highp_f32mat3; + typedef mat<4, 4, f32, highp> highp_f32mat4; + + typedef mat<2, 2, f32, defaultp> f32mat2; + typedef mat<3, 3, f32, defaultp> f32mat3; + typedef mat<4, 4, f32, defaultp> f32mat4; + + typedef mat<2, 2, f64, lowp> lowp_dmat2; + typedef mat<3, 3, f64, lowp> lowp_dmat3; + typedef mat<4, 4, f64, lowp> lowp_dmat4; + + typedef mat<2, 2, f64, mediump> mediump_dmat2; + typedef mat<3, 3, f64, mediump> mediump_dmat3; + typedef mat<4, 4, f64, mediump> mediump_dmat4; + + typedef mat<2, 2, f64, highp> highp_dmat2; + typedef mat<3, 3, f64, highp> highp_dmat3; + typedef mat<4, 4, f64, highp> highp_dmat4; + + typedef mat<2, 2, f64, defaultp> dmat2; + typedef mat<3, 3, f64, defaultp> dmat3; + typedef mat<4, 4, f64, defaultp> dmat4; + + typedef mat<2, 2, f64, lowp> lowp_f64mat2; + typedef mat<3, 3, f64, lowp> lowp_f64mat3; + typedef mat<4, 4, f64, lowp> lowp_f64mat4; + + typedef mat<2, 2, f64, mediump> mediump_f64mat2; + typedef mat<3, 3, f64, mediump> mediump_f64mat3; + typedef mat<4, 4, f64, mediump> mediump_f64mat4; + + typedef mat<2, 2, f64, highp> highp_f64mat2; + typedef mat<3, 3, f64, highp> highp_f64mat3; + typedef mat<4, 4, f64, highp> highp_f64mat4; + + typedef mat<2, 2, f64, defaultp> f64mat2; + typedef mat<3, 3, f64, defaultp> f64mat3; + typedef mat<4, 4, f64, defaultp> f64mat4; + + // Matrix MxN + + typedef mat<2, 2, f32, lowp> lowp_mat2x2; + typedef mat<2, 3, f32, lowp> lowp_mat2x3; + typedef mat<2, 4, f32, lowp> lowp_mat2x4; + typedef mat<3, 2, f32, lowp> lowp_mat3x2; + typedef mat<3, 3, f32, lowp> lowp_mat3x3; + typedef mat<3, 4, f32, lowp> lowp_mat3x4; + typedef mat<4, 2, f32, lowp> lowp_mat4x2; + typedef mat<4, 3, f32, lowp> lowp_mat4x3; + typedef mat<4, 4, f32, lowp> lowp_mat4x4; + + typedef mat<2, 2, f32, mediump> mediump_mat2x2; + typedef mat<2, 3, f32, mediump> mediump_mat2x3; + typedef mat<2, 4, f32, mediump> mediump_mat2x4; + typedef mat<3, 2, f32, mediump> mediump_mat3x2; + typedef mat<3, 3, f32, mediump> mediump_mat3x3; + typedef mat<3, 4, f32, mediump> mediump_mat3x4; + typedef mat<4, 2, f32, mediump> mediump_mat4x2; + typedef mat<4, 3, f32, mediump> mediump_mat4x3; + typedef mat<4, 4, f32, mediump> mediump_mat4x4; + + typedef mat<2, 2, f32, highp> highp_mat2x2; + typedef mat<2, 3, f32, highp> highp_mat2x3; + typedef mat<2, 4, f32, highp> highp_mat2x4; + typedef mat<3, 2, f32, highp> highp_mat3x2; + typedef mat<3, 3, f32, highp> highp_mat3x3; + typedef mat<3, 4, f32, highp> highp_mat3x4; + typedef mat<4, 2, f32, highp> highp_mat4x2; + typedef mat<4, 3, f32, highp> highp_mat4x3; + typedef mat<4, 4, f32, highp> highp_mat4x4; + + typedef mat<2, 2, f32, defaultp> mat2x2; + typedef mat<3, 2, f32, defaultp> mat3x2; + typedef mat<4, 2, f32, defaultp> mat4x2; + typedef mat<2, 3, f32, defaultp> mat2x3; + typedef mat<3, 3, f32, defaultp> mat3x3; + typedef mat<4, 3, f32, defaultp> mat4x3; + typedef mat<2, 4, f32, defaultp> mat2x4; + typedef mat<3, 4, f32, defaultp> mat3x4; + typedef mat<4, 4, f32, defaultp> mat4x4; + + typedef mat<2, 2, f32, lowp> lowp_fmat2x2; + typedef mat<2, 3, f32, lowp> lowp_fmat2x3; + typedef mat<2, 4, f32, lowp> lowp_fmat2x4; + typedef mat<3, 2, f32, lowp> lowp_fmat3x2; + typedef mat<3, 3, f32, lowp> lowp_fmat3x3; + typedef mat<3, 4, f32, lowp> lowp_fmat3x4; + typedef mat<4, 2, f32, lowp> lowp_fmat4x2; + typedef mat<4, 3, f32, lowp> lowp_fmat4x3; + typedef mat<4, 4, f32, lowp> lowp_fmat4x4; + + typedef mat<2, 2, f32, mediump> mediump_fmat2x2; + typedef mat<2, 3, f32, mediump> mediump_fmat2x3; + typedef mat<2, 4, f32, mediump> mediump_fmat2x4; + typedef mat<3, 2, f32, mediump> mediump_fmat3x2; + typedef mat<3, 3, f32, mediump> mediump_fmat3x3; + typedef mat<3, 4, f32, mediump> mediump_fmat3x4; + typedef mat<4, 2, f32, mediump> mediump_fmat4x2; + typedef mat<4, 3, f32, mediump> mediump_fmat4x3; + typedef mat<4, 4, f32, mediump> mediump_fmat4x4; + + typedef mat<2, 2, f32, highp> highp_fmat2x2; + typedef mat<2, 3, f32, highp> highp_fmat2x3; + typedef mat<2, 4, f32, highp> highp_fmat2x4; + typedef mat<3, 2, f32, highp> highp_fmat3x2; + typedef mat<3, 3, f32, highp> highp_fmat3x3; + typedef mat<3, 4, f32, highp> highp_fmat3x4; + typedef mat<4, 2, f32, highp> highp_fmat4x2; + typedef mat<4, 3, f32, highp> highp_fmat4x3; + typedef mat<4, 4, f32, highp> highp_fmat4x4; + + typedef mat<2, 2, f32, defaultp> fmat2x2; + typedef mat<3, 2, f32, defaultp> fmat3x2; + typedef mat<4, 2, f32, defaultp> fmat4x2; + typedef mat<2, 3, f32, defaultp> fmat2x3; + typedef mat<3, 3, f32, defaultp> fmat3x3; + typedef mat<4, 3, f32, defaultp> fmat4x3; + typedef mat<2, 4, f32, defaultp> fmat2x4; + typedef mat<3, 4, f32, defaultp> fmat3x4; + typedef mat<4, 4, f32, defaultp> fmat4x4; + + typedef mat<2, 2, f32, lowp> lowp_f32mat2x2; + typedef mat<2, 3, f32, lowp> lowp_f32mat2x3; + typedef mat<2, 4, f32, lowp> lowp_f32mat2x4; + typedef mat<3, 2, f32, lowp> lowp_f32mat3x2; + typedef mat<3, 3, f32, lowp> lowp_f32mat3x3; + typedef mat<3, 4, f32, lowp> lowp_f32mat3x4; + typedef mat<4, 2, f32, lowp> lowp_f32mat4x2; + typedef mat<4, 3, f32, lowp> lowp_f32mat4x3; + typedef mat<4, 4, f32, lowp> lowp_f32mat4x4; + + typedef mat<2, 2, f32, mediump> mediump_f32mat2x2; + typedef mat<2, 3, f32, mediump> mediump_f32mat2x3; + typedef mat<2, 4, f32, mediump> mediump_f32mat2x4; + typedef mat<3, 2, f32, mediump> mediump_f32mat3x2; + typedef mat<3, 3, f32, mediump> mediump_f32mat3x3; + typedef mat<3, 4, f32, mediump> mediump_f32mat3x4; + typedef mat<4, 2, f32, mediump> mediump_f32mat4x2; + typedef mat<4, 3, f32, mediump> mediump_f32mat4x3; + typedef mat<4, 4, f32, mediump> mediump_f32mat4x4; + + typedef mat<2, 2, f32, highp> highp_f32mat2x2; + typedef mat<2, 3, f32, highp> highp_f32mat2x3; + typedef mat<2, 4, f32, highp> highp_f32mat2x4; + typedef mat<3, 2, f32, highp> highp_f32mat3x2; + typedef mat<3, 3, f32, highp> highp_f32mat3x3; + typedef mat<3, 4, f32, highp> highp_f32mat3x4; + typedef mat<4, 2, f32, highp> highp_f32mat4x2; + typedef mat<4, 3, f32, highp> highp_f32mat4x3; + typedef mat<4, 4, f32, highp> highp_f32mat4x4; + + typedef mat<2, 2, f32, defaultp> f32mat2x2; + typedef mat<3, 2, f32, defaultp> f32mat3x2; + typedef mat<4, 2, f32, defaultp> f32mat4x2; + typedef mat<2, 3, f32, defaultp> f32mat2x3; + typedef mat<3, 3, f32, defaultp> f32mat3x3; + typedef mat<4, 3, f32, defaultp> f32mat4x3; + typedef mat<2, 4, f32, defaultp> f32mat2x4; + typedef mat<3, 4, f32, defaultp> f32mat3x4; + typedef mat<4, 4, f32, defaultp> f32mat4x4; + + typedef mat<2, 2, double, lowp> lowp_dmat2x2; + typedef mat<2, 3, double, lowp> lowp_dmat2x3; + typedef mat<2, 4, double, lowp> lowp_dmat2x4; + typedef mat<3, 2, double, lowp> lowp_dmat3x2; + typedef mat<3, 3, double, lowp> lowp_dmat3x3; + typedef mat<3, 4, double, lowp> lowp_dmat3x4; + typedef mat<4, 2, double, lowp> lowp_dmat4x2; + typedef mat<4, 3, double, lowp> lowp_dmat4x3; + typedef mat<4, 4, double, lowp> lowp_dmat4x4; + + typedef mat<2, 2, double, mediump> mediump_dmat2x2; + typedef mat<2, 3, double, mediump> mediump_dmat2x3; + typedef mat<2, 4, double, mediump> mediump_dmat2x4; + typedef mat<3, 2, double, mediump> mediump_dmat3x2; + typedef mat<3, 3, double, mediump> mediump_dmat3x3; + typedef mat<3, 4, double, mediump> mediump_dmat3x4; + typedef mat<4, 2, double, mediump> mediump_dmat4x2; + typedef mat<4, 3, double, mediump> mediump_dmat4x3; + typedef mat<4, 4, double, mediump> mediump_dmat4x4; + + typedef mat<2, 2, double, highp> highp_dmat2x2; + typedef mat<2, 3, double, highp> highp_dmat2x3; + typedef mat<2, 4, double, highp> highp_dmat2x4; + typedef mat<3, 2, double, highp> highp_dmat3x2; + typedef mat<3, 3, double, highp> highp_dmat3x3; + typedef mat<3, 4, double, highp> highp_dmat3x4; + typedef mat<4, 2, double, highp> highp_dmat4x2; + typedef mat<4, 3, double, highp> highp_dmat4x3; + typedef mat<4, 4, double, highp> highp_dmat4x4; + + typedef mat<2, 2, double, defaultp> dmat2x2; + typedef mat<3, 2, double, defaultp> dmat3x2; + typedef mat<4, 2, double, defaultp> dmat4x2; + typedef mat<2, 3, double, defaultp> dmat2x3; + typedef mat<3, 3, double, defaultp> dmat3x3; + typedef mat<4, 3, double, defaultp> dmat4x3; + typedef mat<2, 4, double, defaultp> dmat2x4; + typedef mat<3, 4, double, defaultp> dmat3x4; + typedef mat<4, 4, double, defaultp> dmat4x4; + + typedef mat<2, 2, f64, lowp> lowp_f64mat2x2; + typedef mat<2, 3, f64, lowp> lowp_f64mat2x3; + typedef mat<2, 4, f64, lowp> lowp_f64mat2x4; + typedef mat<3, 2, f64, lowp> lowp_f64mat3x2; + typedef mat<3, 3, f64, lowp> lowp_f64mat3x3; + typedef mat<3, 4, f64, lowp> lowp_f64mat3x4; + typedef mat<4, 2, f64, lowp> lowp_f64mat4x2; + typedef mat<4, 3, f64, lowp> lowp_f64mat4x3; + typedef mat<4, 4, f64, lowp> lowp_f64mat4x4; + + typedef mat<2, 2, f64, mediump> mediump_f64mat2x2; + typedef mat<2, 3, f64, mediump> mediump_f64mat2x3; + typedef mat<2, 4, f64, mediump> mediump_f64mat2x4; + typedef mat<3, 2, f64, mediump> mediump_f64mat3x2; + typedef mat<3, 3, f64, mediump> mediump_f64mat3x3; + typedef mat<3, 4, f64, mediump> mediump_f64mat3x4; + typedef mat<4, 2, f64, mediump> mediump_f64mat4x2; + typedef mat<4, 3, f64, mediump> mediump_f64mat4x3; + typedef mat<4, 4, f64, mediump> mediump_f64mat4x4; + + typedef mat<2, 2, f64, highp> highp_f64mat2x2; + typedef mat<2, 3, f64, highp> highp_f64mat2x3; + typedef mat<2, 4, f64, highp> highp_f64mat2x4; + typedef mat<3, 2, f64, highp> highp_f64mat3x2; + typedef mat<3, 3, f64, highp> highp_f64mat3x3; + typedef mat<3, 4, f64, highp> highp_f64mat3x4; + typedef mat<4, 2, f64, highp> highp_f64mat4x2; + typedef mat<4, 3, f64, highp> highp_f64mat4x3; + typedef mat<4, 4, f64, highp> highp_f64mat4x4; + + typedef mat<2, 2, f64, defaultp> f64mat2x2; + typedef mat<3, 2, f64, defaultp> f64mat3x2; + typedef mat<4, 2, f64, defaultp> f64mat4x2; + typedef mat<2, 3, f64, defaultp> f64mat2x3; + typedef mat<3, 3, f64, defaultp> f64mat3x3; + typedef mat<4, 3, f64, defaultp> f64mat4x3; + typedef mat<2, 4, f64, defaultp> f64mat2x4; + typedef mat<3, 4, f64, defaultp> f64mat3x4; + typedef mat<4, 4, f64, defaultp> f64mat4x4; + + // Quaternion + + typedef qua lowp_quat; + typedef qua mediump_quat; + typedef qua highp_quat; + typedef qua quat; + + typedef qua lowp_fquat; + typedef qua mediump_fquat; + typedef qua highp_fquat; + typedef qua fquat; + + typedef qua lowp_f32quat; + typedef qua mediump_f32quat; + typedef qua highp_f32quat; + typedef qua f32quat; + + typedef qua lowp_dquat; + typedef qua mediump_dquat; + typedef qua highp_dquat; + typedef qua dquat; + + typedef qua lowp_f64quat; + typedef qua mediump_f64quat; + typedef qua highp_f64quat; + typedef qua f64quat; +}//namespace glm + + diff --git a/src/GLMath/glm/geometric.hpp b/src/GLMath/glm/geometric.hpp new file mode 100644 index 000000000..c068a3cbd --- /dev/null +++ b/src/GLMath/glm/geometric.hpp @@ -0,0 +1,116 @@ +/// @ref core +/// @file glm/geometric.hpp +/// +/// @see GLSL 4.20.8 specification, section 8.5 Geometric Functions +/// +/// @defgroup core_func_geometric Geometric functions +/// @ingroup core +/// +/// These operate on vectors as vectors, not component-wise. +/// +/// Include to use these core features. + +#pragma once + +#include "detail/type_vec3.hpp" + +namespace glm +{ + /// @addtogroup core_func_geometric + /// @{ + + /// Returns the length of x, i.e., sqrt(x * x). + /// + /// @tparam L An integer between 1 and 4 included that qualify the dimension of the vector. + /// @tparam T Floating-point scalar types. + /// + /// @see GLSL length man page + /// @see GLSL 4.20.8 specification, section 8.5 Geometric Functions + template + GLM_FUNC_DECL T length(vec const& x); + + /// Returns the distance betwwen p0 and p1, i.e., length(p0 - p1). + /// + /// @tparam L An integer between 1 and 4 included that qualify the dimension of the vector. + /// @tparam T Floating-point scalar types. + /// + /// @see GLSL distance man page + /// @see GLSL 4.20.8 specification, section 8.5 Geometric Functions + template + GLM_FUNC_DECL T distance(vec const& p0, vec const& p1); + + /// Returns the dot product of x and y, i.e., result = x * y. + /// + /// @tparam L An integer between 1 and 4 included that qualify the dimension of the vector. + /// @tparam T Floating-point scalar types. + /// + /// @see GLSL dot man page + /// @see GLSL 4.20.8 specification, section 8.5 Geometric Functions + template + GLM_FUNC_DECL T dot(vec const& x, vec const& y); + + /// Returns the cross product of x and y. + /// + /// @tparam T Floating-point scalar types. + /// + /// @see GLSL cross man page + /// @see GLSL 4.20.8 specification, section 8.5 Geometric Functions + template + GLM_FUNC_DECL vec<3, T, Q> cross(vec<3, T, Q> const& x, vec<3, T, Q> const& y); + + /// Returns a vector in the same direction as x but with length of 1. + /// According to issue 10 GLSL 1.10 specification, if length(x) == 0 then result is undefined and generate an error. + /// + /// @tparam L An integer between 1 and 4 included that qualify the dimension of the vector. + /// @tparam T Floating-point scalar types. + /// + /// @see GLSL normalize man page + /// @see GLSL 4.20.8 specification, section 8.5 Geometric Functions + template + GLM_FUNC_DECL vec normalize(vec const& x); + + /// If dot(Nref, I) < 0.0, return N, otherwise, return -N. + /// + /// @tparam L An integer between 1 and 4 included that qualify the dimension of the vector. + /// @tparam T Floating-point scalar types. + /// + /// @see GLSL faceforward man page + /// @see GLSL 4.20.8 specification, section 8.5 Geometric Functions + template + GLM_FUNC_DECL vec faceforward( + vec const& N, + vec const& I, + vec const& Nref); + + /// For the incident vector I and surface orientation N, + /// returns the reflection direction : result = I - 2.0 * dot(N, I) * N. + /// + /// @tparam L An integer between 1 and 4 included that qualify the dimension of the vector. + /// @tparam T Floating-point scalar types. + /// + /// @see GLSL reflect man page + /// @see GLSL 4.20.8 specification, section 8.5 Geometric Functions + template + GLM_FUNC_DECL vec reflect( + vec const& I, + vec const& N); + + /// For the incident vector I and surface normal N, + /// and the ratio of indices of refraction eta, + /// return the refraction vector. + /// + /// @tparam L An integer between 1 and 4 included that qualify the dimension of the vector. + /// @tparam T Floating-point scalar types. + /// + /// @see GLSL refract man page + /// @see GLSL 4.20.8 specification, section 8.5 Geometric Functions + template + GLM_FUNC_DECL vec refract( + vec const& I, + vec const& N, + T eta); + + /// @} +}//namespace glm + +#include "detail/func_geometric.inl" diff --git a/src/GLMath/glm/glm.hpp b/src/GLMath/glm/glm.hpp new file mode 100644 index 000000000..8b6106496 --- /dev/null +++ b/src/GLMath/glm/glm.hpp @@ -0,0 +1,136 @@ +/// @ref core +/// @file glm/glm.hpp +/// +/// @defgroup core Core features +/// +/// @brief Features that implement in C++ the GLSL specification as closely as possible. +/// +/// The GLM core consists of C++ types that mirror GLSL types and +/// C++ functions that mirror the GLSL functions. +/// +/// The best documentation for GLM Core is the current GLSL specification, +/// version 4.2 +/// (pdf file). +/// +/// GLM core functionalities require to be included to be used. +/// +/// +/// @defgroup core_vector Vector types +/// +/// Vector types of two to four components with an exhaustive set of operators. +/// +/// @ingroup core +/// +/// +/// @defgroup core_vector_precision Vector types with precision qualifiers +/// +/// @brief Vector types with precision qualifiers which may result in various precision in term of ULPs +/// +/// GLSL allows defining qualifiers for particular variables. +/// With OpenGL's GLSL, these qualifiers have no effect; they are there for compatibility, +/// with OpenGL ES's GLSL, these qualifiers do have an effect. +/// +/// C++ has no language equivalent to qualifier qualifiers. So GLM provides the next-best thing: +/// a number of typedefs that use a particular qualifier. +/// +/// None of these types make any guarantees about the actual qualifier used. +/// +/// @ingroup core +/// +/// +/// @defgroup core_matrix Matrix types +/// +/// Matrix types of with C columns and R rows where C and R are values between 2 to 4 included. +/// These types have exhaustive sets of operators. +/// +/// @ingroup core +/// +/// +/// @defgroup core_matrix_precision Matrix types with precision qualifiers +/// +/// @brief Matrix types with precision qualifiers which may result in various precision in term of ULPs +/// +/// GLSL allows defining qualifiers for particular variables. +/// With OpenGL's GLSL, these qualifiers have no effect; they are there for compatibility, +/// with OpenGL ES's GLSL, these qualifiers do have an effect. +/// +/// C++ has no language equivalent to qualifier qualifiers. So GLM provides the next-best thing: +/// a number of typedefs that use a particular qualifier. +/// +/// None of these types make any guarantees about the actual qualifier used. +/// +/// @ingroup core +/// +/// +/// @defgroup ext Stable extensions +/// +/// @brief Additional features not specified by GLSL specification. +/// +/// EXT extensions are fully tested and documented. +/// +/// Even if it's highly unrecommended, it's possible to include all the extensions at once by +/// including . Otherwise, each extension needs to be included a specific file. +/// +/// +/// @defgroup gtc Recommended extensions +/// +/// @brief Additional features not specified by GLSL specification. +/// +/// GTC extensions aim to be stable with tests and documentation. +/// +/// Even if it's highly unrecommended, it's possible to include all the extensions at once by +/// including . Otherwise, each extension needs to be included a specific file. +/// +/// +/// @defgroup gtx Experimental extensions +/// +/// @brief Experimental features not specified by GLSL specification. +/// +/// Experimental extensions are useful functions and types, but the development of +/// their API and functionality is not necessarily stable. They can change +/// substantially between versions. Backwards compatibility is not much of an issue +/// for them. +/// +/// Even if it's highly unrecommended, it's possible to include all the extensions +/// at once by including . Otherwise, each extension needs to be +/// included a specific file. +/// +/// @mainpage OpenGL Mathematics (GLM) +/// - Website: glm.g-truc.net +/// - GLM API documentation +/// - GLM Manual + +#include "detail/_fixes.hpp" + +#include "detail/setup.hpp" + +#pragma once + +#include +#include +#include +#include +#include +#include "fwd.hpp" + +#include "vec2.hpp" +#include "vec3.hpp" +#include "vec4.hpp" +#include "mat2x2.hpp" +#include "mat2x3.hpp" +#include "mat2x4.hpp" +#include "mat3x2.hpp" +#include "mat3x3.hpp" +#include "mat3x4.hpp" +#include "mat4x2.hpp" +#include "mat4x3.hpp" +#include "mat4x4.hpp" + +#include "trigonometric.hpp" +#include "exponential.hpp" +#include "common.hpp" +#include "packing.hpp" +#include "geometric.hpp" +#include "matrix.hpp" +#include "vector_relational.hpp" +#include "integer.hpp" diff --git a/src/GLMath/glm/gtc/bitfield.hpp b/src/GLMath/glm/gtc/bitfield.hpp new file mode 100644 index 000000000..084fbe75f --- /dev/null +++ b/src/GLMath/glm/gtc/bitfield.hpp @@ -0,0 +1,266 @@ +/// @ref gtc_bitfield +/// @file glm/gtc/bitfield.hpp +/// +/// @see core (dependence) +/// @see gtc_bitfield (dependence) +/// +/// @defgroup gtc_bitfield GLM_GTC_bitfield +/// @ingroup gtc +/// +/// Include to use the features of this extension. +/// +/// Allow to perform bit operations on integer values + +#include "../detail/setup.hpp" + +#pragma once + +// Dependencies +#include "../ext/scalar_int_sized.hpp" +#include "../ext/scalar_uint_sized.hpp" +#include "../detail/qualifier.hpp" +#include "../detail/_vectorize.hpp" +#include "type_precision.hpp" +#include + +#if GLM_MESSAGES == GLM_ENABLE && !defined(GLM_EXT_INCLUDED) +# pragma message("GLM: GLM_GTC_bitfield extension included") +#endif + +namespace glm +{ + /// @addtogroup gtc_bitfield + /// @{ + + /// Build a mask of 'count' bits + /// + /// @see gtc_bitfield + template + GLM_FUNC_DECL genIUType mask(genIUType Bits); + + /// Build a mask of 'count' bits + /// + /// @tparam L Integer between 1 and 4 included that qualify the dimension of the vector + /// @tparam T Signed and unsigned integer scalar types + /// @tparam Q Value from qualifier enum + /// + /// @see gtc_bitfield + template + GLM_FUNC_DECL vec mask(vec const& v); + + /// Rotate all bits to the right. All the bits dropped in the right side are inserted back on the left side. + /// + /// @see gtc_bitfield + template + GLM_FUNC_DECL genIUType bitfieldRotateRight(genIUType In, int Shift); + + /// Rotate all bits to the right. All the bits dropped in the right side are inserted back on the left side. + /// + /// @tparam L Integer between 1 and 4 included that qualify the dimension of the vector + /// @tparam T Signed and unsigned integer scalar types + /// @tparam Q Value from qualifier enum + /// + /// @see gtc_bitfield + template + GLM_FUNC_DECL vec bitfieldRotateRight(vec const& In, int Shift); + + /// Rotate all bits to the left. All the bits dropped in the left side are inserted back on the right side. + /// + /// @see gtc_bitfield + template + GLM_FUNC_DECL genIUType bitfieldRotateLeft(genIUType In, int Shift); + + /// Rotate all bits to the left. All the bits dropped in the left side are inserted back on the right side. + /// + /// @tparam L Integer between 1 and 4 included that qualify the dimension of the vector + /// @tparam T Signed and unsigned integer scalar types + /// @tparam Q Value from qualifier enum + /// + /// @see gtc_bitfield + template + GLM_FUNC_DECL vec bitfieldRotateLeft(vec const& In, int Shift); + + /// Set to 1 a range of bits. + /// + /// @see gtc_bitfield + template + GLM_FUNC_DECL genIUType bitfieldFillOne(genIUType Value, int FirstBit, int BitCount); + + /// Set to 1 a range of bits. + /// + /// @tparam L Integer between 1 and 4 included that qualify the dimension of the vector + /// @tparam T Signed and unsigned integer scalar types + /// @tparam Q Value from qualifier enum + /// + /// @see gtc_bitfield + template + GLM_FUNC_DECL vec bitfieldFillOne(vec const& Value, int FirstBit, int BitCount); + + /// Set to 0 a range of bits. + /// + /// @see gtc_bitfield + template + GLM_FUNC_DECL genIUType bitfieldFillZero(genIUType Value, int FirstBit, int BitCount); + + /// Set to 0 a range of bits. + /// + /// @tparam L Integer between 1 and 4 included that qualify the dimension of the vector + /// @tparam T Signed and unsigned integer scalar types + /// @tparam Q Value from qualifier enum + /// + /// @see gtc_bitfield + template + GLM_FUNC_DECL vec bitfieldFillZero(vec const& Value, int FirstBit, int BitCount); + + /// Interleaves the bits of x and y. + /// The first bit is the first bit of x followed by the first bit of y. + /// The other bits are interleaved following the previous sequence. + /// + /// @see gtc_bitfield + GLM_FUNC_DECL int16 bitfieldInterleave(int8 x, int8 y); + + /// Interleaves the bits of x and y. + /// The first bit is the first bit of x followed by the first bit of y. + /// The other bits are interleaved following the previous sequence. + /// + /// @see gtc_bitfield + GLM_FUNC_DECL uint16 bitfieldInterleave(uint8 x, uint8 y); + + /// Interleaves the bits of x and y. + /// The first bit is the first bit of v.x followed by the first bit of v.y. + /// The other bits are interleaved following the previous sequence. + /// + /// @see gtc_bitfield + GLM_FUNC_DECL uint16 bitfieldInterleave(u8vec2 const& v); + + /// Deinterleaves the bits of x. + /// + /// @see gtc_bitfield + GLM_FUNC_DECL glm::u8vec2 bitfieldDeinterleave(glm::uint16 x); + + /// Interleaves the bits of x and y. + /// The first bit is the first bit of x followed by the first bit of y. + /// The other bits are interleaved following the previous sequence. + /// + /// @see gtc_bitfield + GLM_FUNC_DECL int32 bitfieldInterleave(int16 x, int16 y); + + /// Interleaves the bits of x and y. + /// The first bit is the first bit of x followed by the first bit of y. + /// The other bits are interleaved following the previous sequence. + /// + /// @see gtc_bitfield + GLM_FUNC_DECL uint32 bitfieldInterleave(uint16 x, uint16 y); + + /// Interleaves the bits of x and y. + /// The first bit is the first bit of v.x followed by the first bit of v.y. + /// The other bits are interleaved following the previous sequence. + /// + /// @see gtc_bitfield + GLM_FUNC_DECL uint32 bitfieldInterleave(u16vec2 const& v); + + /// Deinterleaves the bits of x. + /// + /// @see gtc_bitfield + GLM_FUNC_DECL glm::u16vec2 bitfieldDeinterleave(glm::uint32 x); + + /// Interleaves the bits of x and y. + /// The first bit is the first bit of x followed by the first bit of y. + /// The other bits are interleaved following the previous sequence. + /// + /// @see gtc_bitfield + GLM_FUNC_DECL int64 bitfieldInterleave(int32 x, int32 y); + + /// Interleaves the bits of x and y. + /// The first bit is the first bit of x followed by the first bit of y. + /// The other bits are interleaved following the previous sequence. + /// + /// @see gtc_bitfield + GLM_FUNC_DECL uint64 bitfieldInterleave(uint32 x, uint32 y); + + /// Interleaves the bits of x and y. + /// The first bit is the first bit of v.x followed by the first bit of v.y. + /// The other bits are interleaved following the previous sequence. + /// + /// @see gtc_bitfield + GLM_FUNC_DECL uint64 bitfieldInterleave(u32vec2 const& v); + + /// Deinterleaves the bits of x. + /// + /// @see gtc_bitfield + GLM_FUNC_DECL glm::u32vec2 bitfieldDeinterleave(glm::uint64 x); + + /// Interleaves the bits of x, y and z. + /// The first bit is the first bit of x followed by the first bit of y and the first bit of z. + /// The other bits are interleaved following the previous sequence. + /// + /// @see gtc_bitfield + GLM_FUNC_DECL int32 bitfieldInterleave(int8 x, int8 y, int8 z); + + /// Interleaves the bits of x, y and z. + /// The first bit is the first bit of x followed by the first bit of y and the first bit of z. + /// The other bits are interleaved following the previous sequence. + /// + /// @see gtc_bitfield + GLM_FUNC_DECL uint32 bitfieldInterleave(uint8 x, uint8 y, uint8 z); + + /// Interleaves the bits of x, y and z. + /// The first bit is the first bit of x followed by the first bit of y and the first bit of z. + /// The other bits are interleaved following the previous sequence. + /// + /// @see gtc_bitfield + GLM_FUNC_DECL int64 bitfieldInterleave(int16 x, int16 y, int16 z); + + /// Interleaves the bits of x, y and z. + /// The first bit is the first bit of x followed by the first bit of y and the first bit of z. + /// The other bits are interleaved following the previous sequence. + /// + /// @see gtc_bitfield + GLM_FUNC_DECL uint64 bitfieldInterleave(uint16 x, uint16 y, uint16 z); + + /// Interleaves the bits of x, y and z. + /// The first bit is the first bit of x followed by the first bit of y and the first bit of z. + /// The other bits are interleaved following the previous sequence. + /// + /// @see gtc_bitfield + GLM_FUNC_DECL int64 bitfieldInterleave(int32 x, int32 y, int32 z); + + /// Interleaves the bits of x, y and z. + /// The first bit is the first bit of x followed by the first bit of y and the first bit of z. + /// The other bits are interleaved following the previous sequence. + /// + /// @see gtc_bitfield + GLM_FUNC_DECL uint64 bitfieldInterleave(uint32 x, uint32 y, uint32 z); + + /// Interleaves the bits of x, y, z and w. + /// The first bit is the first bit of x followed by the first bit of y, the first bit of z and finally the first bit of w. + /// The other bits are interleaved following the previous sequence. + /// + /// @see gtc_bitfield + GLM_FUNC_DECL int32 bitfieldInterleave(int8 x, int8 y, int8 z, int8 w); + + /// Interleaves the bits of x, y, z and w. + /// The first bit is the first bit of x followed by the first bit of y, the first bit of z and finally the first bit of w. + /// The other bits are interleaved following the previous sequence. + /// + /// @see gtc_bitfield + GLM_FUNC_DECL uint32 bitfieldInterleave(uint8 x, uint8 y, uint8 z, uint8 w); + + /// Interleaves the bits of x, y, z and w. + /// The first bit is the first bit of x followed by the first bit of y, the first bit of z and finally the first bit of w. + /// The other bits are interleaved following the previous sequence. + /// + /// @see gtc_bitfield + GLM_FUNC_DECL int64 bitfieldInterleave(int16 x, int16 y, int16 z, int16 w); + + /// Interleaves the bits of x, y, z and w. + /// The first bit is the first bit of x followed by the first bit of y, the first bit of z and finally the first bit of w. + /// The other bits are interleaved following the previous sequence. + /// + /// @see gtc_bitfield + GLM_FUNC_DECL uint64 bitfieldInterleave(uint16 x, uint16 y, uint16 z, uint16 w); + + /// @} +} //namespace glm + +#include "bitfield.inl" diff --git a/src/GLMath/glm/gtc/bitfield.inl b/src/GLMath/glm/gtc/bitfield.inl new file mode 100644 index 000000000..06cf1889c --- /dev/null +++ b/src/GLMath/glm/gtc/bitfield.inl @@ -0,0 +1,626 @@ +/// @ref gtc_bitfield + +#include "../simd/integer.h" + +namespace glm{ +namespace detail +{ + template + GLM_FUNC_DECL RET bitfieldInterleave(PARAM x, PARAM y); + + template + GLM_FUNC_DECL RET bitfieldInterleave(PARAM x, PARAM y, PARAM z); + + template + GLM_FUNC_DECL RET bitfieldInterleave(PARAM x, PARAM y, PARAM z, PARAM w); + + template<> + GLM_FUNC_QUALIFIER glm::uint16 bitfieldInterleave(glm::uint8 x, glm::uint8 y) + { + glm::uint16 REG1(x); + glm::uint16 REG2(y); + + REG1 = ((REG1 << 4) | REG1) & static_cast(0x0F0F); + REG2 = ((REG2 << 4) | REG2) & static_cast(0x0F0F); + + REG1 = ((REG1 << 2) | REG1) & static_cast(0x3333); + REG2 = ((REG2 << 2) | REG2) & static_cast(0x3333); + + REG1 = ((REG1 << 1) | REG1) & static_cast(0x5555); + REG2 = ((REG2 << 1) | REG2) & static_cast(0x5555); + + return REG1 | static_cast(REG2 << 1); + } + + template<> + GLM_FUNC_QUALIFIER glm::uint32 bitfieldInterleave(glm::uint16 x, glm::uint16 y) + { + glm::uint32 REG1(x); + glm::uint32 REG2(y); + + REG1 = ((REG1 << 8) | REG1) & static_cast(0x00FF00FF); + REG2 = ((REG2 << 8) | REG2) & static_cast(0x00FF00FF); + + REG1 = ((REG1 << 4) | REG1) & static_cast(0x0F0F0F0F); + REG2 = ((REG2 << 4) | REG2) & static_cast(0x0F0F0F0F); + + REG1 = ((REG1 << 2) | REG1) & static_cast(0x33333333); + REG2 = ((REG2 << 2) | REG2) & static_cast(0x33333333); + + REG1 = ((REG1 << 1) | REG1) & static_cast(0x55555555); + REG2 = ((REG2 << 1) | REG2) & static_cast(0x55555555); + + return REG1 | (REG2 << 1); + } + + template<> + GLM_FUNC_QUALIFIER glm::uint64 bitfieldInterleave(glm::uint32 x, glm::uint32 y) + { + glm::uint64 REG1(x); + glm::uint64 REG2(y); + + REG1 = ((REG1 << 16) | REG1) & static_cast(0x0000FFFF0000FFFFull); + REG2 = ((REG2 << 16) | REG2) & static_cast(0x0000FFFF0000FFFFull); + + REG1 = ((REG1 << 8) | REG1) & static_cast(0x00FF00FF00FF00FFull); + REG2 = ((REG2 << 8) | REG2) & static_cast(0x00FF00FF00FF00FFull); + + REG1 = ((REG1 << 4) | REG1) & static_cast(0x0F0F0F0F0F0F0F0Full); + REG2 = ((REG2 << 4) | REG2) & static_cast(0x0F0F0F0F0F0F0F0Full); + + REG1 = ((REG1 << 2) | REG1) & static_cast(0x3333333333333333ull); + REG2 = ((REG2 << 2) | REG2) & static_cast(0x3333333333333333ull); + + REG1 = ((REG1 << 1) | REG1) & static_cast(0x5555555555555555ull); + REG2 = ((REG2 << 1) | REG2) & static_cast(0x5555555555555555ull); + + return REG1 | (REG2 << 1); + } + + template<> + GLM_FUNC_QUALIFIER glm::uint32 bitfieldInterleave(glm::uint8 x, glm::uint8 y, glm::uint8 z) + { + glm::uint32 REG1(x); + glm::uint32 REG2(y); + glm::uint32 REG3(z); + + REG1 = ((REG1 << 16) | REG1) & static_cast(0xFF0000FFu); + REG2 = ((REG2 << 16) | REG2) & static_cast(0xFF0000FFu); + REG3 = ((REG3 << 16) | REG3) & static_cast(0xFF0000FFu); + + REG1 = ((REG1 << 8) | REG1) & static_cast(0x0F00F00Fu); + REG2 = ((REG2 << 8) | REG2) & static_cast(0x0F00F00Fu); + REG3 = ((REG3 << 8) | REG3) & static_cast(0x0F00F00Fu); + + REG1 = ((REG1 << 4) | REG1) & static_cast(0xC30C30C3u); + REG2 = ((REG2 << 4) | REG2) & static_cast(0xC30C30C3u); + REG3 = ((REG3 << 4) | REG3) & static_cast(0xC30C30C3u); + + REG1 = ((REG1 << 2) | REG1) & static_cast(0x49249249u); + REG2 = ((REG2 << 2) | REG2) & static_cast(0x49249249u); + REG3 = ((REG3 << 2) | REG3) & static_cast(0x49249249u); + + return REG1 | (REG2 << 1) | (REG3 << 2); + } + + template<> + GLM_FUNC_QUALIFIER glm::uint64 bitfieldInterleave(glm::uint16 x, glm::uint16 y, glm::uint16 z) + { + glm::uint64 REG1(x); + glm::uint64 REG2(y); + glm::uint64 REG3(z); + + REG1 = ((REG1 << 32) | REG1) & static_cast(0xFFFF00000000FFFFull); + REG2 = ((REG2 << 32) | REG2) & static_cast(0xFFFF00000000FFFFull); + REG3 = ((REG3 << 32) | REG3) & static_cast(0xFFFF00000000FFFFull); + + REG1 = ((REG1 << 16) | REG1) & static_cast(0x00FF0000FF0000FFull); + REG2 = ((REG2 << 16) | REG2) & static_cast(0x00FF0000FF0000FFull); + REG3 = ((REG3 << 16) | REG3) & static_cast(0x00FF0000FF0000FFull); + + REG1 = ((REG1 << 8) | REG1) & static_cast(0xF00F00F00F00F00Full); + REG2 = ((REG2 << 8) | REG2) & static_cast(0xF00F00F00F00F00Full); + REG3 = ((REG3 << 8) | REG3) & static_cast(0xF00F00F00F00F00Full); + + REG1 = ((REG1 << 4) | REG1) & static_cast(0x30C30C30C30C30C3ull); + REG2 = ((REG2 << 4) | REG2) & static_cast(0x30C30C30C30C30C3ull); + REG3 = ((REG3 << 4) | REG3) & static_cast(0x30C30C30C30C30C3ull); + + REG1 = ((REG1 << 2) | REG1) & static_cast(0x9249249249249249ull); + REG2 = ((REG2 << 2) | REG2) & static_cast(0x9249249249249249ull); + REG3 = ((REG3 << 2) | REG3) & static_cast(0x9249249249249249ull); + + return REG1 | (REG2 << 1) | (REG3 << 2); + } + + template<> + GLM_FUNC_QUALIFIER glm::uint64 bitfieldInterleave(glm::uint32 x, glm::uint32 y, glm::uint32 z) + { + glm::uint64 REG1(x); + glm::uint64 REG2(y); + glm::uint64 REG3(z); + + REG1 = ((REG1 << 32) | REG1) & static_cast(0xFFFF00000000FFFFull); + REG2 = ((REG2 << 32) | REG2) & static_cast(0xFFFF00000000FFFFull); + REG3 = ((REG3 << 32) | REG3) & static_cast(0xFFFF00000000FFFFull); + + REG1 = ((REG1 << 16) | REG1) & static_cast(0x00FF0000FF0000FFull); + REG2 = ((REG2 << 16) | REG2) & static_cast(0x00FF0000FF0000FFull); + REG3 = ((REG3 << 16) | REG3) & static_cast(0x00FF0000FF0000FFull); + + REG1 = ((REG1 << 8) | REG1) & static_cast(0xF00F00F00F00F00Full); + REG2 = ((REG2 << 8) | REG2) & static_cast(0xF00F00F00F00F00Full); + REG3 = ((REG3 << 8) | REG3) & static_cast(0xF00F00F00F00F00Full); + + REG1 = ((REG1 << 4) | REG1) & static_cast(0x30C30C30C30C30C3ull); + REG2 = ((REG2 << 4) | REG2) & static_cast(0x30C30C30C30C30C3ull); + REG3 = ((REG3 << 4) | REG3) & static_cast(0x30C30C30C30C30C3ull); + + REG1 = ((REG1 << 2) | REG1) & static_cast(0x9249249249249249ull); + REG2 = ((REG2 << 2) | REG2) & static_cast(0x9249249249249249ull); + REG3 = ((REG3 << 2) | REG3) & static_cast(0x9249249249249249ull); + + return REG1 | (REG2 << 1) | (REG3 << 2); + } + + template<> + GLM_FUNC_QUALIFIER glm::uint32 bitfieldInterleave(glm::uint8 x, glm::uint8 y, glm::uint8 z, glm::uint8 w) + { + glm::uint32 REG1(x); + glm::uint32 REG2(y); + glm::uint32 REG3(z); + glm::uint32 REG4(w); + + REG1 = ((REG1 << 12) | REG1) & static_cast(0x000F000Fu); + REG2 = ((REG2 << 12) | REG2) & static_cast(0x000F000Fu); + REG3 = ((REG3 << 12) | REG3) & static_cast(0x000F000Fu); + REG4 = ((REG4 << 12) | REG4) & static_cast(0x000F000Fu); + + REG1 = ((REG1 << 6) | REG1) & static_cast(0x03030303u); + REG2 = ((REG2 << 6) | REG2) & static_cast(0x03030303u); + REG3 = ((REG3 << 6) | REG3) & static_cast(0x03030303u); + REG4 = ((REG4 << 6) | REG4) & static_cast(0x03030303u); + + REG1 = ((REG1 << 3) | REG1) & static_cast(0x11111111u); + REG2 = ((REG2 << 3) | REG2) & static_cast(0x11111111u); + REG3 = ((REG3 << 3) | REG3) & static_cast(0x11111111u); + REG4 = ((REG4 << 3) | REG4) & static_cast(0x11111111u); + + return REG1 | (REG2 << 1) | (REG3 << 2) | (REG4 << 3); + } + + template<> + GLM_FUNC_QUALIFIER glm::uint64 bitfieldInterleave(glm::uint16 x, glm::uint16 y, glm::uint16 z, glm::uint16 w) + { + glm::uint64 REG1(x); + glm::uint64 REG2(y); + glm::uint64 REG3(z); + glm::uint64 REG4(w); + + REG1 = ((REG1 << 24) | REG1) & static_cast(0x000000FF000000FFull); + REG2 = ((REG2 << 24) | REG2) & static_cast(0x000000FF000000FFull); + REG3 = ((REG3 << 24) | REG3) & static_cast(0x000000FF000000FFull); + REG4 = ((REG4 << 24) | REG4) & static_cast(0x000000FF000000FFull); + + REG1 = ((REG1 << 12) | REG1) & static_cast(0x000F000F000F000Full); + REG2 = ((REG2 << 12) | REG2) & static_cast(0x000F000F000F000Full); + REG3 = ((REG3 << 12) | REG3) & static_cast(0x000F000F000F000Full); + REG4 = ((REG4 << 12) | REG4) & static_cast(0x000F000F000F000Full); + + REG1 = ((REG1 << 6) | REG1) & static_cast(0x0303030303030303ull); + REG2 = ((REG2 << 6) | REG2) & static_cast(0x0303030303030303ull); + REG3 = ((REG3 << 6) | REG3) & static_cast(0x0303030303030303ull); + REG4 = ((REG4 << 6) | REG4) & static_cast(0x0303030303030303ull); + + REG1 = ((REG1 << 3) | REG1) & static_cast(0x1111111111111111ull); + REG2 = ((REG2 << 3) | REG2) & static_cast(0x1111111111111111ull); + REG3 = ((REG3 << 3) | REG3) & static_cast(0x1111111111111111ull); + REG4 = ((REG4 << 3) | REG4) & static_cast(0x1111111111111111ull); + + return REG1 | (REG2 << 1) | (REG3 << 2) | (REG4 << 3); + } +}//namespace detail + + template + GLM_FUNC_QUALIFIER genIUType mask(genIUType Bits) + { + GLM_STATIC_ASSERT(std::numeric_limits::is_integer, "'mask' accepts only integer values"); + + return Bits >= sizeof(genIUType) * 8 ? ~static_cast(0) : (static_cast(1) << Bits) - static_cast(1); + } + + template + GLM_FUNC_QUALIFIER vec mask(vec const& v) + { + GLM_STATIC_ASSERT(std::numeric_limits::is_integer, "'mask' accepts only integer values"); + + return detail::functor1::call(mask, v); + } + + template + GLM_FUNC_QUALIFIER genIType bitfieldRotateRight(genIType In, int Shift) + { + GLM_STATIC_ASSERT(std::numeric_limits::is_integer, "'bitfieldRotateRight' accepts only integer values"); + + int const BitSize = static_cast(sizeof(genIType) * 8); + return (In << static_cast(Shift)) | (In >> static_cast(BitSize - Shift)); + } + + template + GLM_FUNC_QUALIFIER vec bitfieldRotateRight(vec const& In, int Shift) + { + GLM_STATIC_ASSERT(std::numeric_limits::is_integer, "'bitfieldRotateRight' accepts only integer values"); + + int const BitSize = static_cast(sizeof(T) * 8); + return (In << static_cast(Shift)) | (In >> static_cast(BitSize - Shift)); + } + + template + GLM_FUNC_QUALIFIER genIType bitfieldRotateLeft(genIType In, int Shift) + { + GLM_STATIC_ASSERT(std::numeric_limits::is_integer, "'bitfieldRotateLeft' accepts only integer values"); + + int const BitSize = static_cast(sizeof(genIType) * 8); + return (In >> static_cast(Shift)) | (In << static_cast(BitSize - Shift)); + } + + template + GLM_FUNC_QUALIFIER vec bitfieldRotateLeft(vec const& In, int Shift) + { + GLM_STATIC_ASSERT(std::numeric_limits::is_integer, "'bitfieldRotateLeft' accepts only integer values"); + + int const BitSize = static_cast(sizeof(T) * 8); + return (In >> static_cast(Shift)) | (In << static_cast(BitSize - Shift)); + } + + template + GLM_FUNC_QUALIFIER genIUType bitfieldFillOne(genIUType Value, int FirstBit, int BitCount) + { + return Value | static_cast(mask(BitCount) << FirstBit); + } + + template + GLM_FUNC_QUALIFIER vec bitfieldFillOne(vec const& Value, int FirstBit, int BitCount) + { + return Value | static_cast(mask(BitCount) << FirstBit); + } + + template + GLM_FUNC_QUALIFIER genIUType bitfieldFillZero(genIUType Value, int FirstBit, int BitCount) + { + return Value & static_cast(~(mask(BitCount) << FirstBit)); + } + + template + GLM_FUNC_QUALIFIER vec bitfieldFillZero(vec const& Value, int FirstBit, int BitCount) + { + return Value & static_cast(~(mask(BitCount) << FirstBit)); + } + + GLM_FUNC_QUALIFIER int16 bitfieldInterleave(int8 x, int8 y) + { + union sign8 + { + int8 i; + uint8 u; + } sign_x, sign_y; + + union sign16 + { + int16 i; + uint16 u; + } result; + + sign_x.i = x; + sign_y.i = y; + result.u = bitfieldInterleave(sign_x.u, sign_y.u); + + return result.i; + } + + GLM_FUNC_QUALIFIER uint16 bitfieldInterleave(uint8 x, uint8 y) + { + return detail::bitfieldInterleave(x, y); + } + + GLM_FUNC_QUALIFIER uint16 bitfieldInterleave(u8vec2 const& v) + { + return detail::bitfieldInterleave(v.x, v.y); + } + + GLM_FUNC_QUALIFIER u8vec2 bitfieldDeinterleave(glm::uint16 x) + { + uint16 REG1(x); + uint16 REG2(x >>= 1); + + REG1 = REG1 & static_cast(0x5555); + REG2 = REG2 & static_cast(0x5555); + + REG1 = ((REG1 >> 1) | REG1) & static_cast(0x3333); + REG2 = ((REG2 >> 1) | REG2) & static_cast(0x3333); + + REG1 = ((REG1 >> 2) | REG1) & static_cast(0x0F0F); + REG2 = ((REG2 >> 2) | REG2) & static_cast(0x0F0F); + + REG1 = ((REG1 >> 4) | REG1) & static_cast(0x00FF); + REG2 = ((REG2 >> 4) | REG2) & static_cast(0x00FF); + + REG1 = ((REG1 >> 8) | REG1) & static_cast(0xFFFF); + REG2 = ((REG2 >> 8) | REG2) & static_cast(0xFFFF); + + return glm::u8vec2(REG1, REG2); + } + + GLM_FUNC_QUALIFIER int32 bitfieldInterleave(int16 x, int16 y) + { + union sign16 + { + int16 i; + uint16 u; + } sign_x, sign_y; + + union sign32 + { + int32 i; + uint32 u; + } result; + + sign_x.i = x; + sign_y.i = y; + result.u = bitfieldInterleave(sign_x.u, sign_y.u); + + return result.i; + } + + GLM_FUNC_QUALIFIER uint32 bitfieldInterleave(uint16 x, uint16 y) + { + return detail::bitfieldInterleave(x, y); + } + + GLM_FUNC_QUALIFIER glm::uint32 bitfieldInterleave(u16vec2 const& v) + { + return detail::bitfieldInterleave(v.x, v.y); + } + + GLM_FUNC_QUALIFIER glm::u16vec2 bitfieldDeinterleave(glm::uint32 x) + { + glm::uint32 REG1(x); + glm::uint32 REG2(x >>= 1); + + REG1 = REG1 & static_cast(0x55555555); + REG2 = REG2 & static_cast(0x55555555); + + REG1 = ((REG1 >> 1) | REG1) & static_cast(0x33333333); + REG2 = ((REG2 >> 1) | REG2) & static_cast(0x33333333); + + REG1 = ((REG1 >> 2) | REG1) & static_cast(0x0F0F0F0F); + REG2 = ((REG2 >> 2) | REG2) & static_cast(0x0F0F0F0F); + + REG1 = ((REG1 >> 4) | REG1) & static_cast(0x00FF00FF); + REG2 = ((REG2 >> 4) | REG2) & static_cast(0x00FF00FF); + + REG1 = ((REG1 >> 8) | REG1) & static_cast(0x0000FFFF); + REG2 = ((REG2 >> 8) | REG2) & static_cast(0x0000FFFF); + + return glm::u16vec2(REG1, REG2); + } + + GLM_FUNC_QUALIFIER int64 bitfieldInterleave(int32 x, int32 y) + { + union sign32 + { + int32 i; + uint32 u; + } sign_x, sign_y; + + union sign64 + { + int64 i; + uint64 u; + } result; + + sign_x.i = x; + sign_y.i = y; + result.u = bitfieldInterleave(sign_x.u, sign_y.u); + + return result.i; + } + + GLM_FUNC_QUALIFIER uint64 bitfieldInterleave(uint32 x, uint32 y) + { + return detail::bitfieldInterleave(x, y); + } + + GLM_FUNC_QUALIFIER glm::uint64 bitfieldInterleave(u32vec2 const& v) + { + return detail::bitfieldInterleave(v.x, v.y); + } + + GLM_FUNC_QUALIFIER glm::u32vec2 bitfieldDeinterleave(glm::uint64 x) + { + glm::uint64 REG1(x); + glm::uint64 REG2(x >>= 1); + + REG1 = REG1 & static_cast(0x5555555555555555ull); + REG2 = REG2 & static_cast(0x5555555555555555ull); + + REG1 = ((REG1 >> 1) | REG1) & static_cast(0x3333333333333333ull); + REG2 = ((REG2 >> 1) | REG2) & static_cast(0x3333333333333333ull); + + REG1 = ((REG1 >> 2) | REG1) & static_cast(0x0F0F0F0F0F0F0F0Full); + REG2 = ((REG2 >> 2) | REG2) & static_cast(0x0F0F0F0F0F0F0F0Full); + + REG1 = ((REG1 >> 4) | REG1) & static_cast(0x00FF00FF00FF00FFull); + REG2 = ((REG2 >> 4) | REG2) & static_cast(0x00FF00FF00FF00FFull); + + REG1 = ((REG1 >> 8) | REG1) & static_cast(0x0000FFFF0000FFFFull); + REG2 = ((REG2 >> 8) | REG2) & static_cast(0x0000FFFF0000FFFFull); + + REG1 = ((REG1 >> 16) | REG1) & static_cast(0x00000000FFFFFFFFull); + REG2 = ((REG2 >> 16) | REG2) & static_cast(0x00000000FFFFFFFFull); + + return glm::u32vec2(REG1, REG2); + } + + GLM_FUNC_QUALIFIER int32 bitfieldInterleave(int8 x, int8 y, int8 z) + { + union sign8 + { + int8 i; + uint8 u; + } sign_x, sign_y, sign_z; + + union sign32 + { + int32 i; + uint32 u; + } result; + + sign_x.i = x; + sign_y.i = y; + sign_z.i = z; + result.u = bitfieldInterleave(sign_x.u, sign_y.u, sign_z.u); + + return result.i; + } + + GLM_FUNC_QUALIFIER uint32 bitfieldInterleave(uint8 x, uint8 y, uint8 z) + { + return detail::bitfieldInterleave(x, y, z); + } + + GLM_FUNC_QUALIFIER uint32 bitfieldInterleave(u8vec3 const& v) + { + return detail::bitfieldInterleave(v.x, v.y, v.z); + } + + GLM_FUNC_QUALIFIER int64 bitfieldInterleave(int16 x, int16 y, int16 z) + { + union sign16 + { + int16 i; + uint16 u; + } sign_x, sign_y, sign_z; + + union sign64 + { + int64 i; + uint64 u; + } result; + + sign_x.i = x; + sign_y.i = y; + sign_z.i = z; + result.u = bitfieldInterleave(sign_x.u, sign_y.u, sign_z.u); + + return result.i; + } + + GLM_FUNC_QUALIFIER uint64 bitfieldInterleave(uint16 x, uint16 y, uint16 z) + { + return detail::bitfieldInterleave(x, y, z); + } + + GLM_FUNC_QUALIFIER uint64 bitfieldInterleave(u16vec3 const& v) + { + return detail::bitfieldInterleave(v.x, v.y, v.z); + } + + GLM_FUNC_QUALIFIER int64 bitfieldInterleave(int32 x, int32 y, int32 z) + { + union sign16 + { + int32 i; + uint32 u; + } sign_x, sign_y, sign_z; + + union sign64 + { + int64 i; + uint64 u; + } result; + + sign_x.i = x; + sign_y.i = y; + sign_z.i = z; + result.u = bitfieldInterleave(sign_x.u, sign_y.u, sign_z.u); + + return result.i; + } + + GLM_FUNC_QUALIFIER uint64 bitfieldInterleave(uint32 x, uint32 y, uint32 z) + { + return detail::bitfieldInterleave(x, y, z); + } + + GLM_FUNC_QUALIFIER uint64 bitfieldInterleave(u32vec3 const& v) + { + return detail::bitfieldInterleave(v.x, v.y, v.z); + } + + GLM_FUNC_QUALIFIER int32 bitfieldInterleave(int8 x, int8 y, int8 z, int8 w) + { + union sign8 + { + int8 i; + uint8 u; + } sign_x, sign_y, sign_z, sign_w; + + union sign32 + { + int32 i; + uint32 u; + } result; + + sign_x.i = x; + sign_y.i = y; + sign_z.i = z; + sign_w.i = w; + result.u = bitfieldInterleave(sign_x.u, sign_y.u, sign_z.u, sign_w.u); + + return result.i; + } + + GLM_FUNC_QUALIFIER uint32 bitfieldInterleave(uint8 x, uint8 y, uint8 z, uint8 w) + { + return detail::bitfieldInterleave(x, y, z, w); + } + + GLM_FUNC_QUALIFIER uint32 bitfieldInterleave(u8vec4 const& v) + { + return detail::bitfieldInterleave(v.x, v.y, v.z, v.w); + } + + GLM_FUNC_QUALIFIER int64 bitfieldInterleave(int16 x, int16 y, int16 z, int16 w) + { + union sign16 + { + int16 i; + uint16 u; + } sign_x, sign_y, sign_z, sign_w; + + union sign64 + { + int64 i; + uint64 u; + } result; + + sign_x.i = x; + sign_y.i = y; + sign_z.i = z; + sign_w.i = w; + result.u = bitfieldInterleave(sign_x.u, sign_y.u, sign_z.u, sign_w.u); + + return result.i; + } + + GLM_FUNC_QUALIFIER uint64 bitfieldInterleave(uint16 x, uint16 y, uint16 z, uint16 w) + { + return detail::bitfieldInterleave(x, y, z, w); + } + + GLM_FUNC_QUALIFIER uint64 bitfieldInterleave(u16vec4 const& v) + { + return detail::bitfieldInterleave(v.x, v.y, v.z, v.w); + } +}//namespace glm diff --git a/src/GLMath/glm/gtc/color_space.hpp b/src/GLMath/glm/gtc/color_space.hpp new file mode 100644 index 000000000..cffd9f093 --- /dev/null +++ b/src/GLMath/glm/gtc/color_space.hpp @@ -0,0 +1,56 @@ +/// @ref gtc_color_space +/// @file glm/gtc/color_space.hpp +/// +/// @see core (dependence) +/// @see gtc_color_space (dependence) +/// +/// @defgroup gtc_color_space GLM_GTC_color_space +/// @ingroup gtc +/// +/// Include to use the features of this extension. +/// +/// Allow to perform bit operations on integer values + +#pragma once + +// Dependencies +#include "../detail/setup.hpp" +#include "../detail/qualifier.hpp" +#include "../exponential.hpp" +#include "../vec3.hpp" +#include "../vec4.hpp" +#include + +#if GLM_MESSAGES == GLM_ENABLE && !defined(GLM_EXT_INCLUDED) +# pragma message("GLM: GLM_GTC_color_space extension included") +#endif + +namespace glm +{ + /// @addtogroup gtc_color_space + /// @{ + + /// Convert a linear color to sRGB color using a standard gamma correction. + /// IEC 61966-2-1:1999 / Rec. 709 specification https://www.w3.org/Graphics/Color/srgb + template + GLM_FUNC_DECL vec convertLinearToSRGB(vec const& ColorLinear); + + /// Convert a linear color to sRGB color using a custom gamma correction. + /// IEC 61966-2-1:1999 / Rec. 709 specification https://www.w3.org/Graphics/Color/srgb + template + GLM_FUNC_DECL vec convertLinearToSRGB(vec const& ColorLinear, T Gamma); + + /// Convert a sRGB color to linear color using a standard gamma correction. + /// IEC 61966-2-1:1999 / Rec. 709 specification https://www.w3.org/Graphics/Color/srgb + template + GLM_FUNC_DECL vec convertSRGBToLinear(vec const& ColorSRGB); + + /// Convert a sRGB color to linear color using a custom gamma correction. + // IEC 61966-2-1:1999 / Rec. 709 specification https://www.w3.org/Graphics/Color/srgb + template + GLM_FUNC_DECL vec convertSRGBToLinear(vec const& ColorSRGB, T Gamma); + + /// @} +} //namespace glm + +#include "color_space.inl" diff --git a/src/GLMath/glm/gtc/color_space.inl b/src/GLMath/glm/gtc/color_space.inl new file mode 100644 index 000000000..2a900044e --- /dev/null +++ b/src/GLMath/glm/gtc/color_space.inl @@ -0,0 +1,84 @@ +/// @ref gtc_color_space + +namespace glm{ +namespace detail +{ + template + struct compute_rgbToSrgb + { + GLM_FUNC_QUALIFIER static vec call(vec const& ColorRGB, T GammaCorrection) + { + vec const ClampedColor(clamp(ColorRGB, static_cast(0), static_cast(1))); + + return mix( + pow(ClampedColor, vec(GammaCorrection)) * static_cast(1.055) - static_cast(0.055), + ClampedColor * static_cast(12.92), + lessThan(ClampedColor, vec(static_cast(0.0031308)))); + } + }; + + template + struct compute_rgbToSrgb<4, T, Q> + { + GLM_FUNC_QUALIFIER static vec<4, T, Q> call(vec<4, T, Q> const& ColorRGB, T GammaCorrection) + { + return vec<4, T, Q>(compute_rgbToSrgb<3, T, Q>::call(vec<3, T, Q>(ColorRGB), GammaCorrection), ColorRGB.w); + } + }; + + template + struct compute_srgbToRgb + { + GLM_FUNC_QUALIFIER static vec call(vec const& ColorSRGB, T Gamma) + { + return mix( + pow((ColorSRGB + static_cast(0.055)) * static_cast(0.94786729857819905213270142180095), vec(Gamma)), + ColorSRGB * static_cast(0.07739938080495356037151702786378), + lessThanEqual(ColorSRGB, vec(static_cast(0.04045)))); + } + }; + + template + struct compute_srgbToRgb<4, T, Q> + { + GLM_FUNC_QUALIFIER static vec<4, T, Q> call(vec<4, T, Q> const& ColorSRGB, T Gamma) + { + return vec<4, T, Q>(compute_srgbToRgb<3, T, Q>::call(vec<3, T, Q>(ColorSRGB), Gamma), ColorSRGB.w); + } + }; +}//namespace detail + + template + GLM_FUNC_QUALIFIER vec convertLinearToSRGB(vec const& ColorLinear) + { + return detail::compute_rgbToSrgb::call(ColorLinear, static_cast(0.41666)); + } + + // Based on Ian Taylor http://chilliant.blogspot.fr/2012/08/srgb-approximations-for-hlsl.html + template<> + GLM_FUNC_QUALIFIER vec<3, float, lowp> convertLinearToSRGB(vec<3, float, lowp> const& ColorLinear) + { + vec<3, float, lowp> S1 = sqrt(ColorLinear); + vec<3, float, lowp> S2 = sqrt(S1); + vec<3, float, lowp> S3 = sqrt(S2); + return 0.662002687f * S1 + 0.684122060f * S2 - 0.323583601f * S3 - 0.0225411470f * ColorLinear; + } + + template + GLM_FUNC_QUALIFIER vec convertLinearToSRGB(vec const& ColorLinear, T Gamma) + { + return detail::compute_rgbToSrgb::call(ColorLinear, static_cast(1) / Gamma); + } + + template + GLM_FUNC_QUALIFIER vec convertSRGBToLinear(vec const& ColorSRGB) + { + return detail::compute_srgbToRgb::call(ColorSRGB, static_cast(2.4)); + } + + template + GLM_FUNC_QUALIFIER vec convertSRGBToLinear(vec const& ColorSRGB, T Gamma) + { + return detail::compute_srgbToRgb::call(ColorSRGB, Gamma); + } +}//namespace glm diff --git a/src/GLMath/glm/gtc/constants.hpp b/src/GLMath/glm/gtc/constants.hpp new file mode 100644 index 000000000..99f212869 --- /dev/null +++ b/src/GLMath/glm/gtc/constants.hpp @@ -0,0 +1,165 @@ +/// @ref gtc_constants +/// @file glm/gtc/constants.hpp +/// +/// @see core (dependence) +/// +/// @defgroup gtc_constants GLM_GTC_constants +/// @ingroup gtc +/// +/// Include to use the features of this extension. +/// +/// Provide a list of constants and precomputed useful values. + +#pragma once + +// Dependencies +#include "../ext/scalar_constants.hpp" + +#if GLM_MESSAGES == GLM_ENABLE && !defined(GLM_EXT_INCLUDED) +# pragma message("GLM: GLM_GTC_constants extension included") +#endif + +namespace glm +{ + /// @addtogroup gtc_constants + /// @{ + + /// Return 0. + /// @see gtc_constants + template + GLM_FUNC_DECL GLM_CONSTEXPR genType zero(); + + /// Return 1. + /// @see gtc_constants + template + GLM_FUNC_DECL GLM_CONSTEXPR genType one(); + + /// Return pi * 2. + /// @see gtc_constants + template + GLM_FUNC_DECL GLM_CONSTEXPR genType two_pi(); + + /// Return square root of pi. + /// @see gtc_constants + template + GLM_FUNC_DECL GLM_CONSTEXPR genType root_pi(); + + /// Return pi / 2. + /// @see gtc_constants + template + GLM_FUNC_DECL GLM_CONSTEXPR genType half_pi(); + + /// Return pi / 2 * 3. + /// @see gtc_constants + template + GLM_FUNC_DECL GLM_CONSTEXPR genType three_over_two_pi(); + + /// Return pi / 4. + /// @see gtc_constants + template + GLM_FUNC_DECL GLM_CONSTEXPR genType quarter_pi(); + + /// Return 1 / pi. + /// @see gtc_constants + template + GLM_FUNC_DECL GLM_CONSTEXPR genType one_over_pi(); + + /// Return 1 / (pi * 2). + /// @see gtc_constants + template + GLM_FUNC_DECL GLM_CONSTEXPR genType one_over_two_pi(); + + /// Return 2 / pi. + /// @see gtc_constants + template + GLM_FUNC_DECL GLM_CONSTEXPR genType two_over_pi(); + + /// Return 4 / pi. + /// @see gtc_constants + template + GLM_FUNC_DECL GLM_CONSTEXPR genType four_over_pi(); + + /// Return 2 / sqrt(pi). + /// @see gtc_constants + template + GLM_FUNC_DECL GLM_CONSTEXPR genType two_over_root_pi(); + + /// Return 1 / sqrt(2). + /// @see gtc_constants + template + GLM_FUNC_DECL GLM_CONSTEXPR genType one_over_root_two(); + + /// Return sqrt(pi / 2). + /// @see gtc_constants + template + GLM_FUNC_DECL GLM_CONSTEXPR genType root_half_pi(); + + /// Return sqrt(2 * pi). + /// @see gtc_constants + template + GLM_FUNC_DECL GLM_CONSTEXPR genType root_two_pi(); + + /// Return sqrt(ln(4)). + /// @see gtc_constants + template + GLM_FUNC_DECL GLM_CONSTEXPR genType root_ln_four(); + + /// Return e constant. + /// @see gtc_constants + template + GLM_FUNC_DECL GLM_CONSTEXPR genType e(); + + /// Return Euler's constant. + /// @see gtc_constants + template + GLM_FUNC_DECL GLM_CONSTEXPR genType euler(); + + /// Return sqrt(2). + /// @see gtc_constants + template + GLM_FUNC_DECL GLM_CONSTEXPR genType root_two(); + + /// Return sqrt(3). + /// @see gtc_constants + template + GLM_FUNC_DECL GLM_CONSTEXPR genType root_three(); + + /// Return sqrt(5). + /// @see gtc_constants + template + GLM_FUNC_DECL GLM_CONSTEXPR genType root_five(); + + /// Return ln(2). + /// @see gtc_constants + template + GLM_FUNC_DECL GLM_CONSTEXPR genType ln_two(); + + /// Return ln(10). + /// @see gtc_constants + template + GLM_FUNC_DECL GLM_CONSTEXPR genType ln_ten(); + + /// Return ln(ln(2)). + /// @see gtc_constants + template + GLM_FUNC_DECL GLM_CONSTEXPR genType ln_ln_two(); + + /// Return 1 / 3. + /// @see gtc_constants + template + GLM_FUNC_DECL GLM_CONSTEXPR genType third(); + + /// Return 2 / 3. + /// @see gtc_constants + template + GLM_FUNC_DECL GLM_CONSTEXPR genType two_thirds(); + + /// Return the golden ratio constant. + /// @see gtc_constants + template + GLM_FUNC_DECL GLM_CONSTEXPR genType golden_ratio(); + + /// @} +} //namespace glm + +#include "constants.inl" diff --git a/src/GLMath/glm/gtc/constants.inl b/src/GLMath/glm/gtc/constants.inl new file mode 100644 index 000000000..87f5c860f --- /dev/null +++ b/src/GLMath/glm/gtc/constants.inl @@ -0,0 +1,166 @@ +/// @ref gtc_constants + +namespace glm +{ + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR genType zero() + { + return genType(0); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR genType one() + { + return genType(1); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR genType two_pi() + { + return genType(6.28318530717958647692528676655900576); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR genType root_pi() + { + return genType(1.772453850905516027); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR genType half_pi() + { + return genType(1.57079632679489661923132169163975144); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR genType three_over_two_pi() + { + return genType(4.71238898038468985769396507491925432); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR genType quarter_pi() + { + return genType(0.785398163397448309615660845819875721); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR genType one_over_pi() + { + return genType(0.318309886183790671537767526745028724); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR genType one_over_two_pi() + { + return genType(0.159154943091895335768883763372514362); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR genType two_over_pi() + { + return genType(0.636619772367581343075535053490057448); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR genType four_over_pi() + { + return genType(1.273239544735162686151070106980114898); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR genType two_over_root_pi() + { + return genType(1.12837916709551257389615890312154517); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR genType one_over_root_two() + { + return genType(0.707106781186547524400844362104849039); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR genType root_half_pi() + { + return genType(1.253314137315500251); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR genType root_two_pi() + { + return genType(2.506628274631000502); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR genType root_ln_four() + { + return genType(1.17741002251547469); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR genType e() + { + return genType(2.71828182845904523536); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR genType euler() + { + return genType(0.577215664901532860606); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR genType root_two() + { + return genType(1.41421356237309504880168872420969808); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR genType root_three() + { + return genType(1.73205080756887729352744634150587236); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR genType root_five() + { + return genType(2.23606797749978969640917366873127623); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR genType ln_two() + { + return genType(0.693147180559945309417232121458176568); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR genType ln_ten() + { + return genType(2.30258509299404568401799145468436421); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR genType ln_ln_two() + { + return genType(-0.3665129205816643); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR genType third() + { + return genType(0.3333333333333333333333333333333333333333); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR genType two_thirds() + { + return genType(0.666666666666666666666666666666666666667); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR genType golden_ratio() + { + return genType(1.61803398874989484820458683436563811); + } +} //namespace glm diff --git a/src/GLMath/glm/gtc/epsilon.hpp b/src/GLMath/glm/gtc/epsilon.hpp new file mode 100644 index 000000000..640439b11 --- /dev/null +++ b/src/GLMath/glm/gtc/epsilon.hpp @@ -0,0 +1,60 @@ +/// @ref gtc_epsilon +/// @file glm/gtc/epsilon.hpp +/// +/// @see core (dependence) +/// @see gtc_quaternion (dependence) +/// +/// @defgroup gtc_epsilon GLM_GTC_epsilon +/// @ingroup gtc +/// +/// Include to use the features of this extension. +/// +/// Comparison functions for a user defined epsilon values. + +#pragma once + +// Dependencies +#include "../detail/setup.hpp" +#include "../detail/qualifier.hpp" + +#if GLM_MESSAGES == GLM_ENABLE && !defined(GLM_EXT_INCLUDED) +# pragma message("GLM: GLM_GTC_epsilon extension included") +#endif + +namespace glm +{ + /// @addtogroup gtc_epsilon + /// @{ + + /// Returns the component-wise comparison of |x - y| < epsilon. + /// True if this expression is satisfied. + /// + /// @see gtc_epsilon + template + GLM_FUNC_DECL vec epsilonEqual(vec const& x, vec const& y, T const& epsilon); + + /// Returns the component-wise comparison of |x - y| < epsilon. + /// True if this expression is satisfied. + /// + /// @see gtc_epsilon + template + GLM_FUNC_DECL bool epsilonEqual(genType const& x, genType const& y, genType const& epsilon); + + /// Returns the component-wise comparison of |x - y| < epsilon. + /// True if this expression is not satisfied. + /// + /// @see gtc_epsilon + template + GLM_FUNC_DECL vec epsilonNotEqual(vec const& x, vec const& y, T const& epsilon); + + /// Returns the component-wise comparison of |x - y| >= epsilon. + /// True if this expression is not satisfied. + /// + /// @see gtc_epsilon + template + GLM_FUNC_DECL bool epsilonNotEqual(genType const& x, genType const& y, genType const& epsilon); + + /// @} +}//namespace glm + +#include "epsilon.inl" diff --git a/src/GLMath/glm/gtc/epsilon.inl b/src/GLMath/glm/gtc/epsilon.inl new file mode 100644 index 000000000..508b9f896 --- /dev/null +++ b/src/GLMath/glm/gtc/epsilon.inl @@ -0,0 +1,80 @@ +/// @ref gtc_epsilon + +// Dependency: +#include "../vector_relational.hpp" +#include "../common.hpp" + +namespace glm +{ + template<> + GLM_FUNC_QUALIFIER bool epsilonEqual + ( + float const& x, + float const& y, + float const& epsilon + ) + { + return abs(x - y) < epsilon; + } + + template<> + GLM_FUNC_QUALIFIER bool epsilonEqual + ( + double const& x, + double const& y, + double const& epsilon + ) + { + return abs(x - y) < epsilon; + } + + template + GLM_FUNC_QUALIFIER vec epsilonEqual(vec const& x, vec const& y, T const& epsilon) + { + return lessThan(abs(x - y), vec(epsilon)); + } + + template + GLM_FUNC_QUALIFIER vec epsilonEqual(vec const& x, vec const& y, vec const& epsilon) + { + return lessThan(abs(x - y), vec(epsilon)); + } + + template<> + GLM_FUNC_QUALIFIER bool epsilonNotEqual(float const& x, float const& y, float const& epsilon) + { + return abs(x - y) >= epsilon; + } + + template<> + GLM_FUNC_QUALIFIER bool epsilonNotEqual(double const& x, double const& y, double const& epsilon) + { + return abs(x - y) >= epsilon; + } + + template + GLM_FUNC_QUALIFIER vec epsilonNotEqual(vec const& x, vec const& y, T const& epsilon) + { + return greaterThanEqual(abs(x - y), vec(epsilon)); + } + + template + GLM_FUNC_QUALIFIER vec epsilonNotEqual(vec const& x, vec const& y, vec const& epsilon) + { + return greaterThanEqual(abs(x - y), vec(epsilon)); + } + + template + GLM_FUNC_QUALIFIER vec<4, bool, Q> epsilonEqual(qua const& x, qua const& y, T const& epsilon) + { + vec<4, T, Q> v(x.x - y.x, x.y - y.y, x.z - y.z, x.w - y.w); + return lessThan(abs(v), vec<4, T, Q>(epsilon)); + } + + template + GLM_FUNC_QUALIFIER vec<4, bool, Q> epsilonNotEqual(qua const& x, qua const& y, T const& epsilon) + { + vec<4, T, Q> v(x.x - y.x, x.y - y.y, x.z - y.z, x.w - y.w); + return greaterThanEqual(abs(v), vec<4, T, Q>(epsilon)); + } +}//namespace glm diff --git a/src/GLMath/glm/gtc/integer.hpp b/src/GLMath/glm/gtc/integer.hpp new file mode 100644 index 000000000..4c3f28f65 --- /dev/null +++ b/src/GLMath/glm/gtc/integer.hpp @@ -0,0 +1,65 @@ +/// @ref gtc_integer +/// @file glm/gtc/integer.hpp +/// +/// @see core (dependence) +/// @see gtc_integer (dependence) +/// +/// @defgroup gtc_integer GLM_GTC_integer +/// @ingroup gtc +/// +/// Include to use the features of this extension. +/// +/// @brief Allow to perform bit operations on integer values + +#pragma once + +// Dependencies +#include "../detail/setup.hpp" +#include "../detail/qualifier.hpp" +#include "../common.hpp" +#include "../integer.hpp" +#include "../exponential.hpp" +#include + +#if GLM_MESSAGES == GLM_ENABLE && !defined(GLM_EXT_INCLUDED) +# pragma message("GLM: GLM_GTC_integer extension included") +#endif + +namespace glm +{ + /// @addtogroup gtc_integer + /// @{ + + /// Returns the log2 of x for integer values. Can be reliably using to compute mipmap count from the texture size. + /// @see gtc_integer + template + GLM_FUNC_DECL genIUType log2(genIUType x); + + /// Returns a value equal to the nearest integer to x. + /// The fraction 0.5 will round in a direction chosen by the + /// implementation, presumably the direction that is fastest. + /// + /// @param x The values of the argument must be greater or equal to zero. + /// @tparam T floating point scalar types. + /// + /// @see GLSL round man page + /// @see gtc_integer + template + GLM_FUNC_DECL vec iround(vec const& x); + + /// Returns a value equal to the nearest integer to x. + /// The fraction 0.5 will round in a direction chosen by the + /// implementation, presumably the direction that is fastest. + /// + /// @param x The values of the argument must be greater or equal to zero. + /// @tparam T floating point scalar types. + /// + /// @see GLSL round man page + /// @see gtc_integer + template + GLM_FUNC_DECL vec uround(vec const& x); + + /// @} +} //namespace glm + +#include "integer.inl" diff --git a/src/GLMath/glm/gtc/integer.inl b/src/GLMath/glm/gtc/integer.inl new file mode 100644 index 000000000..f0a8b4f25 --- /dev/null +++ b/src/GLMath/glm/gtc/integer.inl @@ -0,0 +1,68 @@ +/// @ref gtc_integer + +namespace glm{ +namespace detail +{ + template + struct compute_log2 + { + GLM_FUNC_QUALIFIER static vec call(vec const& v) + { + //Equivalent to return findMSB(vec); but save one function call in ASM with VC + //return findMSB(vec); + return vec(detail::compute_findMSB_vec::call(v)); + } + }; + +# if GLM_HAS_BITSCAN_WINDOWS + template + struct compute_log2<4, int, Q, false, Aligned> + { + GLM_FUNC_QUALIFIER static vec<4, int, Q> call(vec<4, int, Q> const& v) + { + vec<4, int, Q> Result; + _BitScanReverse(reinterpret_cast(&Result.x), v.x); + _BitScanReverse(reinterpret_cast(&Result.y), v.y); + _BitScanReverse(reinterpret_cast(&Result.z), v.z); + _BitScanReverse(reinterpret_cast(&Result.w), v.w); + return Result; + } + }; +# endif//GLM_HAS_BITSCAN_WINDOWS +}//namespace detail + template + GLM_FUNC_QUALIFIER int iround(genType x) + { + GLM_STATIC_ASSERT(std::numeric_limits::is_iec559, "'iround' only accept floating-point inputs"); + assert(static_cast(0.0) <= x); + + return static_cast(x + static_cast(0.5)); + } + + template + GLM_FUNC_QUALIFIER vec iround(vec const& x) + { + GLM_STATIC_ASSERT(std::numeric_limits::is_iec559, "'iround' only accept floating-point inputs"); + assert(all(lessThanEqual(vec(0), x))); + + return vec(x + static_cast(0.5)); + } + + template + GLM_FUNC_QUALIFIER uint uround(genType x) + { + GLM_STATIC_ASSERT(std::numeric_limits::is_iec559, "'uround' only accept floating-point inputs"); + assert(static_cast(0.0) <= x); + + return static_cast(x + static_cast(0.5)); + } + + template + GLM_FUNC_QUALIFIER vec uround(vec const& x) + { + GLM_STATIC_ASSERT(std::numeric_limits::is_iec559, "'uround' only accept floating-point inputs"); + assert(all(lessThanEqual(vec(0), x))); + + return vec(x + static_cast(0.5)); + } +}//namespace glm diff --git a/src/GLMath/glm/gtc/matrix_access.hpp b/src/GLMath/glm/gtc/matrix_access.hpp new file mode 100644 index 000000000..4935ba755 --- /dev/null +++ b/src/GLMath/glm/gtc/matrix_access.hpp @@ -0,0 +1,60 @@ +/// @ref gtc_matrix_access +/// @file glm/gtc/matrix_access.hpp +/// +/// @see core (dependence) +/// +/// @defgroup gtc_matrix_access GLM_GTC_matrix_access +/// @ingroup gtc +/// +/// Include to use the features of this extension. +/// +/// Defines functions to access rows or columns of a matrix easily. + +#pragma once + +// Dependency: +#include "../detail/setup.hpp" + +#if GLM_MESSAGES == GLM_ENABLE && !defined(GLM_EXT_INCLUDED) +# pragma message("GLM: GLM_GTC_matrix_access extension included") +#endif + +namespace glm +{ + /// @addtogroup gtc_matrix_access + /// @{ + + /// Get a specific row of a matrix. + /// @see gtc_matrix_access + template + GLM_FUNC_DECL typename genType::row_type row( + genType const& m, + length_t index); + + /// Set a specific row to a matrix. + /// @see gtc_matrix_access + template + GLM_FUNC_DECL genType row( + genType const& m, + length_t index, + typename genType::row_type const& x); + + /// Get a specific column of a matrix. + /// @see gtc_matrix_access + template + GLM_FUNC_DECL typename genType::col_type column( + genType const& m, + length_t index); + + /// Set a specific column to a matrix. + /// @see gtc_matrix_access + template + GLM_FUNC_DECL genType column( + genType const& m, + length_t index, + typename genType::col_type const& x); + + /// @} +}//namespace glm + +#include "matrix_access.inl" diff --git a/src/GLMath/glm/gtc/matrix_access.inl b/src/GLMath/glm/gtc/matrix_access.inl new file mode 100644 index 000000000..09fcc10d3 --- /dev/null +++ b/src/GLMath/glm/gtc/matrix_access.inl @@ -0,0 +1,62 @@ +/// @ref gtc_matrix_access + +namespace glm +{ + template + GLM_FUNC_QUALIFIER genType row + ( + genType const& m, + length_t index, + typename genType::row_type const& x + ) + { + assert(index >= 0 && index < m[0].length()); + + genType Result = m; + for(length_t i = 0; i < m.length(); ++i) + Result[i][index] = x[i]; + return Result; + } + + template + GLM_FUNC_QUALIFIER typename genType::row_type row + ( + genType const& m, + length_t index + ) + { + assert(index >= 0 && index < m[0].length()); + + typename genType::row_type Result(0); + for(length_t i = 0; i < m.length(); ++i) + Result[i] = m[i][index]; + return Result; + } + + template + GLM_FUNC_QUALIFIER genType column + ( + genType const& m, + length_t index, + typename genType::col_type const& x + ) + { + assert(index >= 0 && index < m.length()); + + genType Result = m; + Result[index] = x; + return Result; + } + + template + GLM_FUNC_QUALIFIER typename genType::col_type column + ( + genType const& m, + length_t index + ) + { + assert(index >= 0 && index < m.length()); + + return m[index]; + } +}//namespace glm diff --git a/src/GLMath/glm/gtc/matrix_integer.hpp b/src/GLMath/glm/gtc/matrix_integer.hpp new file mode 100644 index 000000000..557a9775b --- /dev/null +++ b/src/GLMath/glm/gtc/matrix_integer.hpp @@ -0,0 +1,487 @@ +/// @ref gtc_matrix_integer +/// @file glm/gtc/matrix_integer.hpp +/// +/// @see core (dependence) +/// +/// @defgroup gtc_matrix_integer GLM_GTC_matrix_integer +/// @ingroup gtc +/// +/// Include to use the features of this extension. +/// +/// Defines a number of matrices with integer types. + +#pragma once + +// Dependency: +#include "../mat2x2.hpp" +#include "../mat2x3.hpp" +#include "../mat2x4.hpp" +#include "../mat3x2.hpp" +#include "../mat3x3.hpp" +#include "../mat3x4.hpp" +#include "../mat4x2.hpp" +#include "../mat4x3.hpp" +#include "../mat4x4.hpp" + +#if GLM_MESSAGES == GLM_ENABLE && !defined(GLM_EXT_INCLUDED) +# pragma message("GLM: GLM_GTC_matrix_integer extension included") +#endif + +namespace glm +{ + /// @addtogroup gtc_matrix_integer + /// @{ + + /// High-qualifier signed integer 2x2 matrix. + /// @see gtc_matrix_integer + typedef mat<2, 2, int, highp> highp_imat2; + + /// High-qualifier signed integer 3x3 matrix. + /// @see gtc_matrix_integer + typedef mat<3, 3, int, highp> highp_imat3; + + /// High-qualifier signed integer 4x4 matrix. + /// @see gtc_matrix_integer + typedef mat<4, 4, int, highp> highp_imat4; + + /// High-qualifier signed integer 2x2 matrix. + /// @see gtc_matrix_integer + typedef mat<2, 2, int, highp> highp_imat2x2; + + /// High-qualifier signed integer 2x3 matrix. + /// @see gtc_matrix_integer + typedef mat<2, 3, int, highp> highp_imat2x3; + + /// High-qualifier signed integer 2x4 matrix. + /// @see gtc_matrix_integer + typedef mat<2, 4, int, highp> highp_imat2x4; + + /// High-qualifier signed integer 3x2 matrix. + /// @see gtc_matrix_integer + typedef mat<3, 2, int, highp> highp_imat3x2; + + /// High-qualifier signed integer 3x3 matrix. + /// @see gtc_matrix_integer + typedef mat<3, 3, int, highp> highp_imat3x3; + + /// High-qualifier signed integer 3x4 matrix. + /// @see gtc_matrix_integer + typedef mat<3, 4, int, highp> highp_imat3x4; + + /// High-qualifier signed integer 4x2 matrix. + /// @see gtc_matrix_integer + typedef mat<4, 2, int, highp> highp_imat4x2; + + /// High-qualifier signed integer 4x3 matrix. + /// @see gtc_matrix_integer + typedef mat<4, 3, int, highp> highp_imat4x3; + + /// High-qualifier signed integer 4x4 matrix. + /// @see gtc_matrix_integer + typedef mat<4, 4, int, highp> highp_imat4x4; + + + /// Medium-qualifier signed integer 2x2 matrix. + /// @see gtc_matrix_integer + typedef mat<2, 2, int, mediump> mediump_imat2; + + /// Medium-qualifier signed integer 3x3 matrix. + /// @see gtc_matrix_integer + typedef mat<3, 3, int, mediump> mediump_imat3; + + /// Medium-qualifier signed integer 4x4 matrix. + /// @see gtc_matrix_integer + typedef mat<4, 4, int, mediump> mediump_imat4; + + + /// Medium-qualifier signed integer 2x2 matrix. + /// @see gtc_matrix_integer + typedef mat<2, 2, int, mediump> mediump_imat2x2; + + /// Medium-qualifier signed integer 2x3 matrix. + /// @see gtc_matrix_integer + typedef mat<2, 3, int, mediump> mediump_imat2x3; + + /// Medium-qualifier signed integer 2x4 matrix. + /// @see gtc_matrix_integer + typedef mat<2, 4, int, mediump> mediump_imat2x4; + + /// Medium-qualifier signed integer 3x2 matrix. + /// @see gtc_matrix_integer + typedef mat<3, 2, int, mediump> mediump_imat3x2; + + /// Medium-qualifier signed integer 3x3 matrix. + /// @see gtc_matrix_integer + typedef mat<3, 3, int, mediump> mediump_imat3x3; + + /// Medium-qualifier signed integer 3x4 matrix. + /// @see gtc_matrix_integer + typedef mat<3, 4, int, mediump> mediump_imat3x4; + + /// Medium-qualifier signed integer 4x2 matrix. + /// @see gtc_matrix_integer + typedef mat<4, 2, int, mediump> mediump_imat4x2; + + /// Medium-qualifier signed integer 4x3 matrix. + /// @see gtc_matrix_integer + typedef mat<4, 3, int, mediump> mediump_imat4x3; + + /// Medium-qualifier signed integer 4x4 matrix. + /// @see gtc_matrix_integer + typedef mat<4, 4, int, mediump> mediump_imat4x4; + + + /// Low-qualifier signed integer 2x2 matrix. + /// @see gtc_matrix_integer + typedef mat<2, 2, int, lowp> lowp_imat2; + + /// Low-qualifier signed integer 3x3 matrix. + /// @see gtc_matrix_integer + typedef mat<3, 3, int, lowp> lowp_imat3; + + /// Low-qualifier signed integer 4x4 matrix. + /// @see gtc_matrix_integer + typedef mat<4, 4, int, lowp> lowp_imat4; + + + /// Low-qualifier signed integer 2x2 matrix. + /// @see gtc_matrix_integer + typedef mat<2, 2, int, lowp> lowp_imat2x2; + + /// Low-qualifier signed integer 2x3 matrix. + /// @see gtc_matrix_integer + typedef mat<2, 3, int, lowp> lowp_imat2x3; + + /// Low-qualifier signed integer 2x4 matrix. + /// @see gtc_matrix_integer + typedef mat<2, 4, int, lowp> lowp_imat2x4; + + /// Low-qualifier signed integer 3x2 matrix. + /// @see gtc_matrix_integer + typedef mat<3, 2, int, lowp> lowp_imat3x2; + + /// Low-qualifier signed integer 3x3 matrix. + /// @see gtc_matrix_integer + typedef mat<3, 3, int, lowp> lowp_imat3x3; + + /// Low-qualifier signed integer 3x4 matrix. + /// @see gtc_matrix_integer + typedef mat<3, 4, int, lowp> lowp_imat3x4; + + /// Low-qualifier signed integer 4x2 matrix. + /// @see gtc_matrix_integer + typedef mat<4, 2, int, lowp> lowp_imat4x2; + + /// Low-qualifier signed integer 4x3 matrix. + /// @see gtc_matrix_integer + typedef mat<4, 3, int, lowp> lowp_imat4x3; + + /// Low-qualifier signed integer 4x4 matrix. + /// @see gtc_matrix_integer + typedef mat<4, 4, int, lowp> lowp_imat4x4; + + + /// High-qualifier unsigned integer 2x2 matrix. + /// @see gtc_matrix_integer + typedef mat<2, 2, uint, highp> highp_umat2; + + /// High-qualifier unsigned integer 3x3 matrix. + /// @see gtc_matrix_integer + typedef mat<3, 3, uint, highp> highp_umat3; + + /// High-qualifier unsigned integer 4x4 matrix. + /// @see gtc_matrix_integer + typedef mat<4, 4, uint, highp> highp_umat4; + + /// High-qualifier unsigned integer 2x2 matrix. + /// @see gtc_matrix_integer + typedef mat<2, 2, uint, highp> highp_umat2x2; + + /// High-qualifier unsigned integer 2x3 matrix. + /// @see gtc_matrix_integer + typedef mat<2, 3, uint, highp> highp_umat2x3; + + /// High-qualifier unsigned integer 2x4 matrix. + /// @see gtc_matrix_integer + typedef mat<2, 4, uint, highp> highp_umat2x4; + + /// High-qualifier unsigned integer 3x2 matrix. + /// @see gtc_matrix_integer + typedef mat<3, 2, uint, highp> highp_umat3x2; + + /// High-qualifier unsigned integer 3x3 matrix. + /// @see gtc_matrix_integer + typedef mat<3, 3, uint, highp> highp_umat3x3; + + /// High-qualifier unsigned integer 3x4 matrix. + /// @see gtc_matrix_integer + typedef mat<3, 4, uint, highp> highp_umat3x4; + + /// High-qualifier unsigned integer 4x2 matrix. + /// @see gtc_matrix_integer + typedef mat<4, 2, uint, highp> highp_umat4x2; + + /// High-qualifier unsigned integer 4x3 matrix. + /// @see gtc_matrix_integer + typedef mat<4, 3, uint, highp> highp_umat4x3; + + /// High-qualifier unsigned integer 4x4 matrix. + /// @see gtc_matrix_integer + typedef mat<4, 4, uint, highp> highp_umat4x4; + + + /// Medium-qualifier unsigned integer 2x2 matrix. + /// @see gtc_matrix_integer + typedef mat<2, 2, uint, mediump> mediump_umat2; + + /// Medium-qualifier unsigned integer 3x3 matrix. + /// @see gtc_matrix_integer + typedef mat<3, 3, uint, mediump> mediump_umat3; + + /// Medium-qualifier unsigned integer 4x4 matrix. + /// @see gtc_matrix_integer + typedef mat<4, 4, uint, mediump> mediump_umat4; + + + /// Medium-qualifier unsigned integer 2x2 matrix. + /// @see gtc_matrix_integer + typedef mat<2, 2, uint, mediump> mediump_umat2x2; + + /// Medium-qualifier unsigned integer 2x3 matrix. + /// @see gtc_matrix_integer + typedef mat<2, 3, uint, mediump> mediump_umat2x3; + + /// Medium-qualifier unsigned integer 2x4 matrix. + /// @see gtc_matrix_integer + typedef mat<2, 4, uint, mediump> mediump_umat2x4; + + /// Medium-qualifier unsigned integer 3x2 matrix. + /// @see gtc_matrix_integer + typedef mat<3, 2, uint, mediump> mediump_umat3x2; + + /// Medium-qualifier unsigned integer 3x3 matrix. + /// @see gtc_matrix_integer + typedef mat<3, 3, uint, mediump> mediump_umat3x3; + + /// Medium-qualifier unsigned integer 3x4 matrix. + /// @see gtc_matrix_integer + typedef mat<3, 4, uint, mediump> mediump_umat3x4; + + /// Medium-qualifier unsigned integer 4x2 matrix. + /// @see gtc_matrix_integer + typedef mat<4, 2, uint, mediump> mediump_umat4x2; + + /// Medium-qualifier unsigned integer 4x3 matrix. + /// @see gtc_matrix_integer + typedef mat<4, 3, uint, mediump> mediump_umat4x3; + + /// Medium-qualifier unsigned integer 4x4 matrix. + /// @see gtc_matrix_integer + typedef mat<4, 4, uint, mediump> mediump_umat4x4; + + + /// Low-qualifier unsigned integer 2x2 matrix. + /// @see gtc_matrix_integer + typedef mat<2, 2, uint, lowp> lowp_umat2; + + /// Low-qualifier unsigned integer 3x3 matrix. + /// @see gtc_matrix_integer + typedef mat<3, 3, uint, lowp> lowp_umat3; + + /// Low-qualifier unsigned integer 4x4 matrix. + /// @see gtc_matrix_integer + typedef mat<4, 4, uint, lowp> lowp_umat4; + + + /// Low-qualifier unsigned integer 2x2 matrix. + /// @see gtc_matrix_integer + typedef mat<2, 2, uint, lowp> lowp_umat2x2; + + /// Low-qualifier unsigned integer 2x3 matrix. + /// @see gtc_matrix_integer + typedef mat<2, 3, uint, lowp> lowp_umat2x3; + + /// Low-qualifier unsigned integer 2x4 matrix. + /// @see gtc_matrix_integer + typedef mat<2, 4, uint, lowp> lowp_umat2x4; + + /// Low-qualifier unsigned integer 3x2 matrix. + /// @see gtc_matrix_integer + typedef mat<3, 2, uint, lowp> lowp_umat3x2; + + /// Low-qualifier unsigned integer 3x3 matrix. + /// @see gtc_matrix_integer + typedef mat<3, 3, uint, lowp> lowp_umat3x3; + + /// Low-qualifier unsigned integer 3x4 matrix. + /// @see gtc_matrix_integer + typedef mat<3, 4, uint, lowp> lowp_umat3x4; + + /// Low-qualifier unsigned integer 4x2 matrix. + /// @see gtc_matrix_integer + typedef mat<4, 2, uint, lowp> lowp_umat4x2; + + /// Low-qualifier unsigned integer 4x3 matrix. + /// @see gtc_matrix_integer + typedef mat<4, 3, uint, lowp> lowp_umat4x3; + + /// Low-qualifier unsigned integer 4x4 matrix. + /// @see gtc_matrix_integer + typedef mat<4, 4, uint, lowp> lowp_umat4x4; + +#if(defined(GLM_PRECISION_HIGHP_INT)) + typedef highp_imat2 imat2; + typedef highp_imat3 imat3; + typedef highp_imat4 imat4; + typedef highp_imat2x2 imat2x2; + typedef highp_imat2x3 imat2x3; + typedef highp_imat2x4 imat2x4; + typedef highp_imat3x2 imat3x2; + typedef highp_imat3x3 imat3x3; + typedef highp_imat3x4 imat3x4; + typedef highp_imat4x2 imat4x2; + typedef highp_imat4x3 imat4x3; + typedef highp_imat4x4 imat4x4; +#elif(defined(GLM_PRECISION_LOWP_INT)) + typedef lowp_imat2 imat2; + typedef lowp_imat3 imat3; + typedef lowp_imat4 imat4; + typedef lowp_imat2x2 imat2x2; + typedef lowp_imat2x3 imat2x3; + typedef lowp_imat2x4 imat2x4; + typedef lowp_imat3x2 imat3x2; + typedef lowp_imat3x3 imat3x3; + typedef lowp_imat3x4 imat3x4; + typedef lowp_imat4x2 imat4x2; + typedef lowp_imat4x3 imat4x3; + typedef lowp_imat4x4 imat4x4; +#else //if(defined(GLM_PRECISION_MEDIUMP_INT)) + + /// Signed integer 2x2 matrix. + /// @see gtc_matrix_integer + typedef mediump_imat2 imat2; + + /// Signed integer 3x3 matrix. + /// @see gtc_matrix_integer + typedef mediump_imat3 imat3; + + /// Signed integer 4x4 matrix. + /// @see gtc_matrix_integer + typedef mediump_imat4 imat4; + + /// Signed integer 2x2 matrix. + /// @see gtc_matrix_integer + typedef mediump_imat2x2 imat2x2; + + /// Signed integer 2x3 matrix. + /// @see gtc_matrix_integer + typedef mediump_imat2x3 imat2x3; + + /// Signed integer 2x4 matrix. + /// @see gtc_matrix_integer + typedef mediump_imat2x4 imat2x4; + + /// Signed integer 3x2 matrix. + /// @see gtc_matrix_integer + typedef mediump_imat3x2 imat3x2; + + /// Signed integer 3x3 matrix. + /// @see gtc_matrix_integer + typedef mediump_imat3x3 imat3x3; + + /// Signed integer 3x4 matrix. + /// @see gtc_matrix_integer + typedef mediump_imat3x4 imat3x4; + + /// Signed integer 4x2 matrix. + /// @see gtc_matrix_integer + typedef mediump_imat4x2 imat4x2; + + /// Signed integer 4x3 matrix. + /// @see gtc_matrix_integer + typedef mediump_imat4x3 imat4x3; + + /// Signed integer 4x4 matrix. + /// @see gtc_matrix_integer + typedef mediump_imat4x4 imat4x4; +#endif//GLM_PRECISION + +#if(defined(GLM_PRECISION_HIGHP_UINT)) + typedef highp_umat2 umat2; + typedef highp_umat3 umat3; + typedef highp_umat4 umat4; + typedef highp_umat2x2 umat2x2; + typedef highp_umat2x3 umat2x3; + typedef highp_umat2x4 umat2x4; + typedef highp_umat3x2 umat3x2; + typedef highp_umat3x3 umat3x3; + typedef highp_umat3x4 umat3x4; + typedef highp_umat4x2 umat4x2; + typedef highp_umat4x3 umat4x3; + typedef highp_umat4x4 umat4x4; +#elif(defined(GLM_PRECISION_LOWP_UINT)) + typedef lowp_umat2 umat2; + typedef lowp_umat3 umat3; + typedef lowp_umat4 umat4; + typedef lowp_umat2x2 umat2x2; + typedef lowp_umat2x3 umat2x3; + typedef lowp_umat2x4 umat2x4; + typedef lowp_umat3x2 umat3x2; + typedef lowp_umat3x3 umat3x3; + typedef lowp_umat3x4 umat3x4; + typedef lowp_umat4x2 umat4x2; + typedef lowp_umat4x3 umat4x3; + typedef lowp_umat4x4 umat4x4; +#else //if(defined(GLM_PRECISION_MEDIUMP_UINT)) + + /// Unsigned integer 2x2 matrix. + /// @see gtc_matrix_integer + typedef mediump_umat2 umat2; + + /// Unsigned integer 3x3 matrix. + /// @see gtc_matrix_integer + typedef mediump_umat3 umat3; + + /// Unsigned integer 4x4 matrix. + /// @see gtc_matrix_integer + typedef mediump_umat4 umat4; + + /// Unsigned integer 2x2 matrix. + /// @see gtc_matrix_integer + typedef mediump_umat2x2 umat2x2; + + /// Unsigned integer 2x3 matrix. + /// @see gtc_matrix_integer + typedef mediump_umat2x3 umat2x3; + + /// Unsigned integer 2x4 matrix. + /// @see gtc_matrix_integer + typedef mediump_umat2x4 umat2x4; + + /// Unsigned integer 3x2 matrix. + /// @see gtc_matrix_integer + typedef mediump_umat3x2 umat3x2; + + /// Unsigned integer 3x3 matrix. + /// @see gtc_matrix_integer + typedef mediump_umat3x3 umat3x3; + + /// Unsigned integer 3x4 matrix. + /// @see gtc_matrix_integer + typedef mediump_umat3x4 umat3x4; + + /// Unsigned integer 4x2 matrix. + /// @see gtc_matrix_integer + typedef mediump_umat4x2 umat4x2; + + /// Unsigned integer 4x3 matrix. + /// @see gtc_matrix_integer + typedef mediump_umat4x3 umat4x3; + + /// Unsigned integer 4x4 matrix. + /// @see gtc_matrix_integer + typedef mediump_umat4x4 umat4x4; +#endif//GLM_PRECISION + + /// @} +}//namespace glm diff --git a/src/GLMath/glm/gtc/matrix_inverse.hpp b/src/GLMath/glm/gtc/matrix_inverse.hpp new file mode 100644 index 000000000..a1900adcb --- /dev/null +++ b/src/GLMath/glm/gtc/matrix_inverse.hpp @@ -0,0 +1,50 @@ +/// @ref gtc_matrix_inverse +/// @file glm/gtc/matrix_inverse.hpp +/// +/// @see core (dependence) +/// +/// @defgroup gtc_matrix_inverse GLM_GTC_matrix_inverse +/// @ingroup gtc +/// +/// Include to use the features of this extension. +/// +/// Defines additional matrix inverting functions. + +#pragma once + +// Dependencies +#include "../detail/setup.hpp" +#include "../matrix.hpp" +#include "../mat2x2.hpp" +#include "../mat3x3.hpp" +#include "../mat4x4.hpp" + +#if GLM_MESSAGES == GLM_ENABLE && !defined(GLM_EXT_INCLUDED) +# pragma message("GLM: GLM_GTC_matrix_inverse extension included") +#endif + +namespace glm +{ + /// @addtogroup gtc_matrix_inverse + /// @{ + + /// Fast matrix inverse for affine matrix. + /// + /// @param m Input matrix to invert. + /// @tparam genType Squared floating-point matrix: half, float or double. Inverse of matrix based of half-qualifier floating point value is highly innacurate. + /// @see gtc_matrix_inverse + template + GLM_FUNC_DECL genType affineInverse(genType const& m); + + /// Compute the inverse transpose of a matrix. + /// + /// @param m Input matrix to invert transpose. + /// @tparam genType Squared floating-point matrix: half, float or double. Inverse of matrix based of half-qualifier floating point value is highly innacurate. + /// @see gtc_matrix_inverse + template + GLM_FUNC_DECL genType inverseTranspose(genType const& m); + + /// @} +}//namespace glm + +#include "matrix_inverse.inl" diff --git a/src/GLMath/glm/gtc/matrix_inverse.inl b/src/GLMath/glm/gtc/matrix_inverse.inl new file mode 100644 index 000000000..c004b9e14 --- /dev/null +++ b/src/GLMath/glm/gtc/matrix_inverse.inl @@ -0,0 +1,118 @@ +/// @ref gtc_matrix_inverse + +namespace glm +{ + template + GLM_FUNC_QUALIFIER mat<3, 3, T, Q> affineInverse(mat<3, 3, T, Q> const& m) + { + mat<2, 2, T, Q> const Inv(inverse(mat<2, 2, T, Q>(m))); + + return mat<3, 3, T, Q>( + vec<3, T, Q>(Inv[0], static_cast(0)), + vec<3, T, Q>(Inv[1], static_cast(0)), + vec<3, T, Q>(-Inv * vec<2, T, Q>(m[2]), static_cast(1))); + } + + template + GLM_FUNC_QUALIFIER mat<4, 4, T, Q> affineInverse(mat<4, 4, T, Q> const& m) + { + mat<3, 3, T, Q> const Inv(inverse(mat<3, 3, T, Q>(m))); + + return mat<4, 4, T, Q>( + vec<4, T, Q>(Inv[0], static_cast(0)), + vec<4, T, Q>(Inv[1], static_cast(0)), + vec<4, T, Q>(Inv[2], static_cast(0)), + vec<4, T, Q>(-Inv * vec<3, T, Q>(m[3]), static_cast(1))); + } + + template + GLM_FUNC_QUALIFIER mat<2, 2, T, Q> inverseTranspose(mat<2, 2, T, Q> const& m) + { + T Determinant = m[0][0] * m[1][1] - m[1][0] * m[0][1]; + + mat<2, 2, T, Q> Inverse( + + m[1][1] / Determinant, + - m[0][1] / Determinant, + - m[1][0] / Determinant, + + m[0][0] / Determinant); + + return Inverse; + } + + template + GLM_FUNC_QUALIFIER mat<3, 3, T, Q> inverseTranspose(mat<3, 3, T, Q> const& m) + { + T Determinant = + + m[0][0] * (m[1][1] * m[2][2] - m[1][2] * m[2][1]) + - m[0][1] * (m[1][0] * m[2][2] - m[1][2] * m[2][0]) + + m[0][2] * (m[1][0] * m[2][1] - m[1][1] * m[2][0]); + + mat<3, 3, T, Q> Inverse; + Inverse[0][0] = + (m[1][1] * m[2][2] - m[2][1] * m[1][2]); + Inverse[0][1] = - (m[1][0] * m[2][2] - m[2][0] * m[1][2]); + Inverse[0][2] = + (m[1][0] * m[2][1] - m[2][0] * m[1][1]); + Inverse[1][0] = - (m[0][1] * m[2][2] - m[2][1] * m[0][2]); + Inverse[1][1] = + (m[0][0] * m[2][2] - m[2][0] * m[0][2]); + Inverse[1][2] = - (m[0][0] * m[2][1] - m[2][0] * m[0][1]); + Inverse[2][0] = + (m[0][1] * m[1][2] - m[1][1] * m[0][2]); + Inverse[2][1] = - (m[0][0] * m[1][2] - m[1][0] * m[0][2]); + Inverse[2][2] = + (m[0][0] * m[1][1] - m[1][0] * m[0][1]); + Inverse /= Determinant; + + return Inverse; + } + + template + GLM_FUNC_QUALIFIER mat<4, 4, T, Q> inverseTranspose(mat<4, 4, T, Q> const& m) + { + T SubFactor00 = m[2][2] * m[3][3] - m[3][2] * m[2][3]; + T SubFactor01 = m[2][1] * m[3][3] - m[3][1] * m[2][3]; + T SubFactor02 = m[2][1] * m[3][2] - m[3][1] * m[2][2]; + T SubFactor03 = m[2][0] * m[3][3] - m[3][0] * m[2][3]; + T SubFactor04 = m[2][0] * m[3][2] - m[3][0] * m[2][2]; + T SubFactor05 = m[2][0] * m[3][1] - m[3][0] * m[2][1]; + T SubFactor06 = m[1][2] * m[3][3] - m[3][2] * m[1][3]; + T SubFactor07 = m[1][1] * m[3][3] - m[3][1] * m[1][3]; + T SubFactor08 = m[1][1] * m[3][2] - m[3][1] * m[1][2]; + T SubFactor09 = m[1][0] * m[3][3] - m[3][0] * m[1][3]; + T SubFactor10 = m[1][0] * m[3][2] - m[3][0] * m[1][2]; + T SubFactor11 = m[1][0] * m[3][1] - m[3][0] * m[1][1]; + T SubFactor12 = m[1][2] * m[2][3] - m[2][2] * m[1][3]; + T SubFactor13 = m[1][1] * m[2][3] - m[2][1] * m[1][3]; + T SubFactor14 = m[1][1] * m[2][2] - m[2][1] * m[1][2]; + T SubFactor15 = m[1][0] * m[2][3] - m[2][0] * m[1][3]; + T SubFactor16 = m[1][0] * m[2][2] - m[2][0] * m[1][2]; + T SubFactor17 = m[1][0] * m[2][1] - m[2][0] * m[1][1]; + + mat<4, 4, T, Q> Inverse; + Inverse[0][0] = + (m[1][1] * SubFactor00 - m[1][2] * SubFactor01 + m[1][3] * SubFactor02); + Inverse[0][1] = - (m[1][0] * SubFactor00 - m[1][2] * SubFactor03 + m[1][3] * SubFactor04); + Inverse[0][2] = + (m[1][0] * SubFactor01 - m[1][1] * SubFactor03 + m[1][3] * SubFactor05); + Inverse[0][3] = - (m[1][0] * SubFactor02 - m[1][1] * SubFactor04 + m[1][2] * SubFactor05); + + Inverse[1][0] = - (m[0][1] * SubFactor00 - m[0][2] * SubFactor01 + m[0][3] * SubFactor02); + Inverse[1][1] = + (m[0][0] * SubFactor00 - m[0][2] * SubFactor03 + m[0][3] * SubFactor04); + Inverse[1][2] = - (m[0][0] * SubFactor01 - m[0][1] * SubFactor03 + m[0][3] * SubFactor05); + Inverse[1][3] = + (m[0][0] * SubFactor02 - m[0][1] * SubFactor04 + m[0][2] * SubFactor05); + + Inverse[2][0] = + (m[0][1] * SubFactor06 - m[0][2] * SubFactor07 + m[0][3] * SubFactor08); + Inverse[2][1] = - (m[0][0] * SubFactor06 - m[0][2] * SubFactor09 + m[0][3] * SubFactor10); + Inverse[2][2] = + (m[0][0] * SubFactor07 - m[0][1] * SubFactor09 + m[0][3] * SubFactor11); + Inverse[2][3] = - (m[0][0] * SubFactor08 - m[0][1] * SubFactor10 + m[0][2] * SubFactor11); + + Inverse[3][0] = - (m[0][1] * SubFactor12 - m[0][2] * SubFactor13 + m[0][3] * SubFactor14); + Inverse[3][1] = + (m[0][0] * SubFactor12 - m[0][2] * SubFactor15 + m[0][3] * SubFactor16); + Inverse[3][2] = - (m[0][0] * SubFactor13 - m[0][1] * SubFactor15 + m[0][3] * SubFactor17); + Inverse[3][3] = + (m[0][0] * SubFactor14 - m[0][1] * SubFactor16 + m[0][2] * SubFactor17); + + T Determinant = + + m[0][0] * Inverse[0][0] + + m[0][1] * Inverse[0][1] + + m[0][2] * Inverse[0][2] + + m[0][3] * Inverse[0][3]; + + Inverse /= Determinant; + + return Inverse; + } +}//namespace glm diff --git a/src/GLMath/glm/gtc/matrix_transform.hpp b/src/GLMath/glm/gtc/matrix_transform.hpp new file mode 100644 index 000000000..612418fa5 --- /dev/null +++ b/src/GLMath/glm/gtc/matrix_transform.hpp @@ -0,0 +1,36 @@ +/// @ref gtc_matrix_transform +/// @file glm/gtc/matrix_transform.hpp +/// +/// @see core (dependence) +/// @see gtx_transform +/// @see gtx_transform2 +/// +/// @defgroup gtc_matrix_transform GLM_GTC_matrix_transform +/// @ingroup gtc +/// +/// Include to use the features of this extension. +/// +/// Defines functions that generate common transformation matrices. +/// +/// The matrices generated by this extension use standard OpenGL fixed-function +/// conventions. For example, the lookAt function generates a transform from world +/// space into the specific eye space that the projective matrix functions +/// (perspective, ortho, etc) are designed to expect. The OpenGL compatibility +/// specifications defines the particular layout of this eye space. + +#pragma once + +// Dependencies +#include "../mat4x4.hpp" +#include "../vec2.hpp" +#include "../vec3.hpp" +#include "../vec4.hpp" +#include "../ext/matrix_projection.hpp" +#include "../ext/matrix_clip_space.hpp" +#include "../ext/matrix_transform.hpp" + +#if GLM_MESSAGES == GLM_ENABLE && !defined(GLM_EXT_INCLUDED) +# pragma message("GLM: GLM_GTC_matrix_transform extension included") +#endif + +#include "matrix_transform.inl" diff --git a/src/GLMath/glm/gtc/matrix_transform.inl b/src/GLMath/glm/gtc/matrix_transform.inl new file mode 100644 index 000000000..15b46bc9d --- /dev/null +++ b/src/GLMath/glm/gtc/matrix_transform.inl @@ -0,0 +1,3 @@ +#include "../geometric.hpp" +#include "../trigonometric.hpp" +#include "../matrix.hpp" diff --git a/src/GLMath/glm/gtc/noise.hpp b/src/GLMath/glm/gtc/noise.hpp new file mode 100644 index 000000000..ab1772e78 --- /dev/null +++ b/src/GLMath/glm/gtc/noise.hpp @@ -0,0 +1,61 @@ +/// @ref gtc_noise +/// @file glm/gtc/noise.hpp +/// +/// @see core (dependence) +/// +/// @defgroup gtc_noise GLM_GTC_noise +/// @ingroup gtc +/// +/// Include to use the features of this extension. +/// +/// Defines 2D, 3D and 4D procedural noise functions +/// Based on the work of Stefan Gustavson and Ashima Arts on "webgl-noise": +/// https://github.com/ashima/webgl-noise +/// Following Stefan Gustavson's paper "Simplex noise demystified": +/// http://www.itn.liu.se/~stegu/simplexnoise/simplexnoise.pdf + +#pragma once + +// Dependencies +#include "../detail/setup.hpp" +#include "../detail/qualifier.hpp" +#include "../detail/_noise.hpp" +#include "../geometric.hpp" +#include "../common.hpp" +#include "../vector_relational.hpp" +#include "../vec2.hpp" +#include "../vec3.hpp" +#include "../vec4.hpp" + +#if GLM_MESSAGES == GLM_ENABLE && !defined(GLM_EXT_INCLUDED) +# pragma message("GLM: GLM_GTC_noise extension included") +#endif + +namespace glm +{ + /// @addtogroup gtc_noise + /// @{ + + /// Classic perlin noise. + /// @see gtc_noise + template + GLM_FUNC_DECL T perlin( + vec const& p); + + /// Periodic perlin noise. + /// @see gtc_noise + template + GLM_FUNC_DECL T perlin( + vec const& p, + vec const& rep); + + /// Simplex noise. + /// @see gtc_noise + template + GLM_FUNC_DECL T simplex( + vec const& p); + + /// @} +}//namespace glm + +#include "noise.inl" diff --git a/src/GLMath/glm/gtc/noise.inl b/src/GLMath/glm/gtc/noise.inl new file mode 100644 index 000000000..30d0b274d --- /dev/null +++ b/src/GLMath/glm/gtc/noise.inl @@ -0,0 +1,807 @@ +/// @ref gtc_noise +/// +// Based on the work of Stefan Gustavson and Ashima Arts on "webgl-noise": +// https://github.com/ashima/webgl-noise +// Following Stefan Gustavson's paper "Simplex noise demystified": +// http://www.itn.liu.se/~stegu/simplexnoise/simplexnoise.pdf + +namespace glm{ +namespace gtc +{ + template + GLM_FUNC_QUALIFIER vec<4, T, Q> grad4(T const& j, vec<4, T, Q> const& ip) + { + vec<3, T, Q> pXYZ = floor(fract(vec<3, T, Q>(j) * vec<3, T, Q>(ip)) * T(7)) * ip[2] - T(1); + T pW = static_cast(1.5) - dot(abs(pXYZ), vec<3, T, Q>(1)); + vec<4, T, Q> s = vec<4, T, Q>(lessThan(vec<4, T, Q>(pXYZ, pW), vec<4, T, Q>(0.0))); + pXYZ = pXYZ + (vec<3, T, Q>(s) * T(2) - T(1)) * s.w; + return vec<4, T, Q>(pXYZ, pW); + } +}//namespace gtc + + // Classic Perlin noise + template + GLM_FUNC_QUALIFIER T perlin(vec<2, T, Q> const& Position) + { + vec<4, T, Q> Pi = glm::floor(vec<4, T, Q>(Position.x, Position.y, Position.x, Position.y)) + vec<4, T, Q>(0.0, 0.0, 1.0, 1.0); + vec<4, T, Q> Pf = glm::fract(vec<4, T, Q>(Position.x, Position.y, Position.x, Position.y)) - vec<4, T, Q>(0.0, 0.0, 1.0, 1.0); + Pi = mod(Pi, vec<4, T, Q>(289)); // To avoid truncation effects in permutation + vec<4, T, Q> ix(Pi.x, Pi.z, Pi.x, Pi.z); + vec<4, T, Q> iy(Pi.y, Pi.y, Pi.w, Pi.w); + vec<4, T, Q> fx(Pf.x, Pf.z, Pf.x, Pf.z); + vec<4, T, Q> fy(Pf.y, Pf.y, Pf.w, Pf.w); + + vec<4, T, Q> i = detail::permute(detail::permute(ix) + iy); + + vec<4, T, Q> gx = static_cast(2) * glm::fract(i / T(41)) - T(1); + vec<4, T, Q> gy = glm::abs(gx) - T(0.5); + vec<4, T, Q> tx = glm::floor(gx + T(0.5)); + gx = gx - tx; + + vec<2, T, Q> g00(gx.x, gy.x); + vec<2, T, Q> g10(gx.y, gy.y); + vec<2, T, Q> g01(gx.z, gy.z); + vec<2, T, Q> g11(gx.w, gy.w); + + vec<4, T, Q> norm = detail::taylorInvSqrt(vec<4, T, Q>(dot(g00, g00), dot(g01, g01), dot(g10, g10), dot(g11, g11))); + g00 *= norm.x; + g01 *= norm.y; + g10 *= norm.z; + g11 *= norm.w; + + T n00 = dot(g00, vec<2, T, Q>(fx.x, fy.x)); + T n10 = dot(g10, vec<2, T, Q>(fx.y, fy.y)); + T n01 = dot(g01, vec<2, T, Q>(fx.z, fy.z)); + T n11 = dot(g11, vec<2, T, Q>(fx.w, fy.w)); + + vec<2, T, Q> fade_xy = detail::fade(vec<2, T, Q>(Pf.x, Pf.y)); + vec<2, T, Q> n_x = mix(vec<2, T, Q>(n00, n01), vec<2, T, Q>(n10, n11), fade_xy.x); + T n_xy = mix(n_x.x, n_x.y, fade_xy.y); + return T(2.3) * n_xy; + } + + // Classic Perlin noise + template + GLM_FUNC_QUALIFIER T perlin(vec<3, T, Q> const& Position) + { + vec<3, T, Q> Pi0 = floor(Position); // Integer part for indexing + vec<3, T, Q> Pi1 = Pi0 + T(1); // Integer part + 1 + Pi0 = detail::mod289(Pi0); + Pi1 = detail::mod289(Pi1); + vec<3, T, Q> Pf0 = fract(Position); // Fractional part for interpolation + vec<3, T, Q> Pf1 = Pf0 - T(1); // Fractional part - 1.0 + vec<4, T, Q> ix(Pi0.x, Pi1.x, Pi0.x, Pi1.x); + vec<4, T, Q> iy = vec<4, T, Q>(vec<2, T, Q>(Pi0.y), vec<2, T, Q>(Pi1.y)); + vec<4, T, Q> iz0(Pi0.z); + vec<4, T, Q> iz1(Pi1.z); + + vec<4, T, Q> ixy = detail::permute(detail::permute(ix) + iy); + vec<4, T, Q> ixy0 = detail::permute(ixy + iz0); + vec<4, T, Q> ixy1 = detail::permute(ixy + iz1); + + vec<4, T, Q> gx0 = ixy0 * T(1.0 / 7.0); + vec<4, T, Q> gy0 = fract(floor(gx0) * T(1.0 / 7.0)) - T(0.5); + gx0 = fract(gx0); + vec<4, T, Q> gz0 = vec<4, T, Q>(0.5) - abs(gx0) - abs(gy0); + vec<4, T, Q> sz0 = step(gz0, vec<4, T, Q>(0.0)); + gx0 -= sz0 * (step(T(0), gx0) - T(0.5)); + gy0 -= sz0 * (step(T(0), gy0) - T(0.5)); + + vec<4, T, Q> gx1 = ixy1 * T(1.0 / 7.0); + vec<4, T, Q> gy1 = fract(floor(gx1) * T(1.0 / 7.0)) - T(0.5); + gx1 = fract(gx1); + vec<4, T, Q> gz1 = vec<4, T, Q>(0.5) - abs(gx1) - abs(gy1); + vec<4, T, Q> sz1 = step(gz1, vec<4, T, Q>(0.0)); + gx1 -= sz1 * (step(T(0), gx1) - T(0.5)); + gy1 -= sz1 * (step(T(0), gy1) - T(0.5)); + + vec<3, T, Q> g000(gx0.x, gy0.x, gz0.x); + vec<3, T, Q> g100(gx0.y, gy0.y, gz0.y); + vec<3, T, Q> g010(gx0.z, gy0.z, gz0.z); + vec<3, T, Q> g110(gx0.w, gy0.w, gz0.w); + vec<3, T, Q> g001(gx1.x, gy1.x, gz1.x); + vec<3, T, Q> g101(gx1.y, gy1.y, gz1.y); + vec<3, T, Q> g011(gx1.z, gy1.z, gz1.z); + vec<3, T, Q> g111(gx1.w, gy1.w, gz1.w); + + vec<4, T, Q> norm0 = detail::taylorInvSqrt(vec<4, T, Q>(dot(g000, g000), dot(g010, g010), dot(g100, g100), dot(g110, g110))); + g000 *= norm0.x; + g010 *= norm0.y; + g100 *= norm0.z; + g110 *= norm0.w; + vec<4, T, Q> norm1 = detail::taylorInvSqrt(vec<4, T, Q>(dot(g001, g001), dot(g011, g011), dot(g101, g101), dot(g111, g111))); + g001 *= norm1.x; + g011 *= norm1.y; + g101 *= norm1.z; + g111 *= norm1.w; + + T n000 = dot(g000, Pf0); + T n100 = dot(g100, vec<3, T, Q>(Pf1.x, Pf0.y, Pf0.z)); + T n010 = dot(g010, vec<3, T, Q>(Pf0.x, Pf1.y, Pf0.z)); + T n110 = dot(g110, vec<3, T, Q>(Pf1.x, Pf1.y, Pf0.z)); + T n001 = dot(g001, vec<3, T, Q>(Pf0.x, Pf0.y, Pf1.z)); + T n101 = dot(g101, vec<3, T, Q>(Pf1.x, Pf0.y, Pf1.z)); + T n011 = dot(g011, vec<3, T, Q>(Pf0.x, Pf1.y, Pf1.z)); + T n111 = dot(g111, Pf1); + + vec<3, T, Q> fade_xyz = detail::fade(Pf0); + vec<4, T, Q> n_z = mix(vec<4, T, Q>(n000, n100, n010, n110), vec<4, T, Q>(n001, n101, n011, n111), fade_xyz.z); + vec<2, T, Q> n_yz = mix(vec<2, T, Q>(n_z.x, n_z.y), vec<2, T, Q>(n_z.z, n_z.w), fade_xyz.y); + T n_xyz = mix(n_yz.x, n_yz.y, fade_xyz.x); + return T(2.2) * n_xyz; + } + /* + // Classic Perlin noise + template + GLM_FUNC_QUALIFIER T perlin(vec<3, T, Q> const& P) + { + vec<3, T, Q> Pi0 = floor(P); // Integer part for indexing + vec<3, T, Q> Pi1 = Pi0 + T(1); // Integer part + 1 + Pi0 = mod(Pi0, T(289)); + Pi1 = mod(Pi1, T(289)); + vec<3, T, Q> Pf0 = fract(P); // Fractional part for interpolation + vec<3, T, Q> Pf1 = Pf0 - T(1); // Fractional part - 1.0 + vec<4, T, Q> ix(Pi0.x, Pi1.x, Pi0.x, Pi1.x); + vec<4, T, Q> iy(Pi0.y, Pi0.y, Pi1.y, Pi1.y); + vec<4, T, Q> iz0(Pi0.z); + vec<4, T, Q> iz1(Pi1.z); + + vec<4, T, Q> ixy = permute(permute(ix) + iy); + vec<4, T, Q> ixy0 = permute(ixy + iz0); + vec<4, T, Q> ixy1 = permute(ixy + iz1); + + vec<4, T, Q> gx0 = ixy0 / T(7); + vec<4, T, Q> gy0 = fract(floor(gx0) / T(7)) - T(0.5); + gx0 = fract(gx0); + vec<4, T, Q> gz0 = vec<4, T, Q>(0.5) - abs(gx0) - abs(gy0); + vec<4, T, Q> sz0 = step(gz0, vec<4, T, Q>(0.0)); + gx0 -= sz0 * (step(0.0, gx0) - T(0.5)); + gy0 -= sz0 * (step(0.0, gy0) - T(0.5)); + + vec<4, T, Q> gx1 = ixy1 / T(7); + vec<4, T, Q> gy1 = fract(floor(gx1) / T(7)) - T(0.5); + gx1 = fract(gx1); + vec<4, T, Q> gz1 = vec<4, T, Q>(0.5) - abs(gx1) - abs(gy1); + vec<4, T, Q> sz1 = step(gz1, vec<4, T, Q>(0.0)); + gx1 -= sz1 * (step(T(0), gx1) - T(0.5)); + gy1 -= sz1 * (step(T(0), gy1) - T(0.5)); + + vec<3, T, Q> g000(gx0.x, gy0.x, gz0.x); + vec<3, T, Q> g100(gx0.y, gy0.y, gz0.y); + vec<3, T, Q> g010(gx0.z, gy0.z, gz0.z); + vec<3, T, Q> g110(gx0.w, gy0.w, gz0.w); + vec<3, T, Q> g001(gx1.x, gy1.x, gz1.x); + vec<3, T, Q> g101(gx1.y, gy1.y, gz1.y); + vec<3, T, Q> g011(gx1.z, gy1.z, gz1.z); + vec<3, T, Q> g111(gx1.w, gy1.w, gz1.w); + + vec<4, T, Q> norm0 = taylorInvSqrt(vec<4, T, Q>(dot(g000, g000), dot(g010, g010), dot(g100, g100), dot(g110, g110))); + g000 *= norm0.x; + g010 *= norm0.y; + g100 *= norm0.z; + g110 *= norm0.w; + vec<4, T, Q> norm1 = taylorInvSqrt(vec<4, T, Q>(dot(g001, g001), dot(g011, g011), dot(g101, g101), dot(g111, g111))); + g001 *= norm1.x; + g011 *= norm1.y; + g101 *= norm1.z; + g111 *= norm1.w; + + T n000 = dot(g000, Pf0); + T n100 = dot(g100, vec<3, T, Q>(Pf1.x, Pf0.y, Pf0.z)); + T n010 = dot(g010, vec<3, T, Q>(Pf0.x, Pf1.y, Pf0.z)); + T n110 = dot(g110, vec<3, T, Q>(Pf1.x, Pf1.y, Pf0.z)); + T n001 = dot(g001, vec<3, T, Q>(Pf0.x, Pf0.y, Pf1.z)); + T n101 = dot(g101, vec<3, T, Q>(Pf1.x, Pf0.y, Pf1.z)); + T n011 = dot(g011, vec<3, T, Q>(Pf0.x, Pf1.y, Pf1.z)); + T n111 = dot(g111, Pf1); + + vec<3, T, Q> fade_xyz = fade(Pf0); + vec<4, T, Q> n_z = mix(vec<4, T, Q>(n000, n100, n010, n110), vec<4, T, Q>(n001, n101, n011, n111), fade_xyz.z); + vec<2, T, Q> n_yz = mix( + vec<2, T, Q>(n_z.x, n_z.y), + vec<2, T, Q>(n_z.z, n_z.w), fade_xyz.y); + T n_xyz = mix(n_yz.x, n_yz.y, fade_xyz.x); + return T(2.2) * n_xyz; + } + */ + // Classic Perlin noise + template + GLM_FUNC_QUALIFIER T perlin(vec<4, T, Q> const& Position) + { + vec<4, T, Q> Pi0 = floor(Position); // Integer part for indexing + vec<4, T, Q> Pi1 = Pi0 + T(1); // Integer part + 1 + Pi0 = mod(Pi0, vec<4, T, Q>(289)); + Pi1 = mod(Pi1, vec<4, T, Q>(289)); + vec<4, T, Q> Pf0 = fract(Position); // Fractional part for interpolation + vec<4, T, Q> Pf1 = Pf0 - T(1); // Fractional part - 1.0 + vec<4, T, Q> ix(Pi0.x, Pi1.x, Pi0.x, Pi1.x); + vec<4, T, Q> iy(Pi0.y, Pi0.y, Pi1.y, Pi1.y); + vec<4, T, Q> iz0(Pi0.z); + vec<4, T, Q> iz1(Pi1.z); + vec<4, T, Q> iw0(Pi0.w); + vec<4, T, Q> iw1(Pi1.w); + + vec<4, T, Q> ixy = detail::permute(detail::permute(ix) + iy); + vec<4, T, Q> ixy0 = detail::permute(ixy + iz0); + vec<4, T, Q> ixy1 = detail::permute(ixy + iz1); + vec<4, T, Q> ixy00 = detail::permute(ixy0 + iw0); + vec<4, T, Q> ixy01 = detail::permute(ixy0 + iw1); + vec<4, T, Q> ixy10 = detail::permute(ixy1 + iw0); + vec<4, T, Q> ixy11 = detail::permute(ixy1 + iw1); + + vec<4, T, Q> gx00 = ixy00 / T(7); + vec<4, T, Q> gy00 = floor(gx00) / T(7); + vec<4, T, Q> gz00 = floor(gy00) / T(6); + gx00 = fract(gx00) - T(0.5); + gy00 = fract(gy00) - T(0.5); + gz00 = fract(gz00) - T(0.5); + vec<4, T, Q> gw00 = vec<4, T, Q>(0.75) - abs(gx00) - abs(gy00) - abs(gz00); + vec<4, T, Q> sw00 = step(gw00, vec<4, T, Q>(0.0)); + gx00 -= sw00 * (step(T(0), gx00) - T(0.5)); + gy00 -= sw00 * (step(T(0), gy00) - T(0.5)); + + vec<4, T, Q> gx01 = ixy01 / T(7); + vec<4, T, Q> gy01 = floor(gx01) / T(7); + vec<4, T, Q> gz01 = floor(gy01) / T(6); + gx01 = fract(gx01) - T(0.5); + gy01 = fract(gy01) - T(0.5); + gz01 = fract(gz01) - T(0.5); + vec<4, T, Q> gw01 = vec<4, T, Q>(0.75) - abs(gx01) - abs(gy01) - abs(gz01); + vec<4, T, Q> sw01 = step(gw01, vec<4, T, Q>(0.0)); + gx01 -= sw01 * (step(T(0), gx01) - T(0.5)); + gy01 -= sw01 * (step(T(0), gy01) - T(0.5)); + + vec<4, T, Q> gx10 = ixy10 / T(7); + vec<4, T, Q> gy10 = floor(gx10) / T(7); + vec<4, T, Q> gz10 = floor(gy10) / T(6); + gx10 = fract(gx10) - T(0.5); + gy10 = fract(gy10) - T(0.5); + gz10 = fract(gz10) - T(0.5); + vec<4, T, Q> gw10 = vec<4, T, Q>(0.75) - abs(gx10) - abs(gy10) - abs(gz10); + vec<4, T, Q> sw10 = step(gw10, vec<4, T, Q>(0)); + gx10 -= sw10 * (step(T(0), gx10) - T(0.5)); + gy10 -= sw10 * (step(T(0), gy10) - T(0.5)); + + vec<4, T, Q> gx11 = ixy11 / T(7); + vec<4, T, Q> gy11 = floor(gx11) / T(7); + vec<4, T, Q> gz11 = floor(gy11) / T(6); + gx11 = fract(gx11) - T(0.5); + gy11 = fract(gy11) - T(0.5); + gz11 = fract(gz11) - T(0.5); + vec<4, T, Q> gw11 = vec<4, T, Q>(0.75) - abs(gx11) - abs(gy11) - abs(gz11); + vec<4, T, Q> sw11 = step(gw11, vec<4, T, Q>(0.0)); + gx11 -= sw11 * (step(T(0), gx11) - T(0.5)); + gy11 -= sw11 * (step(T(0), gy11) - T(0.5)); + + vec<4, T, Q> g0000(gx00.x, gy00.x, gz00.x, gw00.x); + vec<4, T, Q> g1000(gx00.y, gy00.y, gz00.y, gw00.y); + vec<4, T, Q> g0100(gx00.z, gy00.z, gz00.z, gw00.z); + vec<4, T, Q> g1100(gx00.w, gy00.w, gz00.w, gw00.w); + vec<4, T, Q> g0010(gx10.x, gy10.x, gz10.x, gw10.x); + vec<4, T, Q> g1010(gx10.y, gy10.y, gz10.y, gw10.y); + vec<4, T, Q> g0110(gx10.z, gy10.z, gz10.z, gw10.z); + vec<4, T, Q> g1110(gx10.w, gy10.w, gz10.w, gw10.w); + vec<4, T, Q> g0001(gx01.x, gy01.x, gz01.x, gw01.x); + vec<4, T, Q> g1001(gx01.y, gy01.y, gz01.y, gw01.y); + vec<4, T, Q> g0101(gx01.z, gy01.z, gz01.z, gw01.z); + vec<4, T, Q> g1101(gx01.w, gy01.w, gz01.w, gw01.w); + vec<4, T, Q> g0011(gx11.x, gy11.x, gz11.x, gw11.x); + vec<4, T, Q> g1011(gx11.y, gy11.y, gz11.y, gw11.y); + vec<4, T, Q> g0111(gx11.z, gy11.z, gz11.z, gw11.z); + vec<4, T, Q> g1111(gx11.w, gy11.w, gz11.w, gw11.w); + + vec<4, T, Q> norm00 = detail::taylorInvSqrt(vec<4, T, Q>(dot(g0000, g0000), dot(g0100, g0100), dot(g1000, g1000), dot(g1100, g1100))); + g0000 *= norm00.x; + g0100 *= norm00.y; + g1000 *= norm00.z; + g1100 *= norm00.w; + + vec<4, T, Q> norm01 = detail::taylorInvSqrt(vec<4, T, Q>(dot(g0001, g0001), dot(g0101, g0101), dot(g1001, g1001), dot(g1101, g1101))); + g0001 *= norm01.x; + g0101 *= norm01.y; + g1001 *= norm01.z; + g1101 *= norm01.w; + + vec<4, T, Q> norm10 = detail::taylorInvSqrt(vec<4, T, Q>(dot(g0010, g0010), dot(g0110, g0110), dot(g1010, g1010), dot(g1110, g1110))); + g0010 *= norm10.x; + g0110 *= norm10.y; + g1010 *= norm10.z; + g1110 *= norm10.w; + + vec<4, T, Q> norm11 = detail::taylorInvSqrt(vec<4, T, Q>(dot(g0011, g0011), dot(g0111, g0111), dot(g1011, g1011), dot(g1111, g1111))); + g0011 *= norm11.x; + g0111 *= norm11.y; + g1011 *= norm11.z; + g1111 *= norm11.w; + + T n0000 = dot(g0000, Pf0); + T n1000 = dot(g1000, vec<4, T, Q>(Pf1.x, Pf0.y, Pf0.z, Pf0.w)); + T n0100 = dot(g0100, vec<4, T, Q>(Pf0.x, Pf1.y, Pf0.z, Pf0.w)); + T n1100 = dot(g1100, vec<4, T, Q>(Pf1.x, Pf1.y, Pf0.z, Pf0.w)); + T n0010 = dot(g0010, vec<4, T, Q>(Pf0.x, Pf0.y, Pf1.z, Pf0.w)); + T n1010 = dot(g1010, vec<4, T, Q>(Pf1.x, Pf0.y, Pf1.z, Pf0.w)); + T n0110 = dot(g0110, vec<4, T, Q>(Pf0.x, Pf1.y, Pf1.z, Pf0.w)); + T n1110 = dot(g1110, vec<4, T, Q>(Pf1.x, Pf1.y, Pf1.z, Pf0.w)); + T n0001 = dot(g0001, vec<4, T, Q>(Pf0.x, Pf0.y, Pf0.z, Pf1.w)); + T n1001 = dot(g1001, vec<4, T, Q>(Pf1.x, Pf0.y, Pf0.z, Pf1.w)); + T n0101 = dot(g0101, vec<4, T, Q>(Pf0.x, Pf1.y, Pf0.z, Pf1.w)); + T n1101 = dot(g1101, vec<4, T, Q>(Pf1.x, Pf1.y, Pf0.z, Pf1.w)); + T n0011 = dot(g0011, vec<4, T, Q>(Pf0.x, Pf0.y, Pf1.z, Pf1.w)); + T n1011 = dot(g1011, vec<4, T, Q>(Pf1.x, Pf0.y, Pf1.z, Pf1.w)); + T n0111 = dot(g0111, vec<4, T, Q>(Pf0.x, Pf1.y, Pf1.z, Pf1.w)); + T n1111 = dot(g1111, Pf1); + + vec<4, T, Q> fade_xyzw = detail::fade(Pf0); + vec<4, T, Q> n_0w = mix(vec<4, T, Q>(n0000, n1000, n0100, n1100), vec<4, T, Q>(n0001, n1001, n0101, n1101), fade_xyzw.w); + vec<4, T, Q> n_1w = mix(vec<4, T, Q>(n0010, n1010, n0110, n1110), vec<4, T, Q>(n0011, n1011, n0111, n1111), fade_xyzw.w); + vec<4, T, Q> n_zw = mix(n_0w, n_1w, fade_xyzw.z); + vec<2, T, Q> n_yzw = mix(vec<2, T, Q>(n_zw.x, n_zw.y), vec<2, T, Q>(n_zw.z, n_zw.w), fade_xyzw.y); + T n_xyzw = mix(n_yzw.x, n_yzw.y, fade_xyzw.x); + return T(2.2) * n_xyzw; + } + + // Classic Perlin noise, periodic variant + template + GLM_FUNC_QUALIFIER T perlin(vec<2, T, Q> const& Position, vec<2, T, Q> const& rep) + { + vec<4, T, Q> Pi = floor(vec<4, T, Q>(Position.x, Position.y, Position.x, Position.y)) + vec<4, T, Q>(0.0, 0.0, 1.0, 1.0); + vec<4, T, Q> Pf = fract(vec<4, T, Q>(Position.x, Position.y, Position.x, Position.y)) - vec<4, T, Q>(0.0, 0.0, 1.0, 1.0); + Pi = mod(Pi, vec<4, T, Q>(rep.x, rep.y, rep.x, rep.y)); // To create noise with explicit period + Pi = mod(Pi, vec<4, T, Q>(289)); // To avoid truncation effects in permutation + vec<4, T, Q> ix(Pi.x, Pi.z, Pi.x, Pi.z); + vec<4, T, Q> iy(Pi.y, Pi.y, Pi.w, Pi.w); + vec<4, T, Q> fx(Pf.x, Pf.z, Pf.x, Pf.z); + vec<4, T, Q> fy(Pf.y, Pf.y, Pf.w, Pf.w); + + vec<4, T, Q> i = detail::permute(detail::permute(ix) + iy); + + vec<4, T, Q> gx = static_cast(2) * fract(i / T(41)) - T(1); + vec<4, T, Q> gy = abs(gx) - T(0.5); + vec<4, T, Q> tx = floor(gx + T(0.5)); + gx = gx - tx; + + vec<2, T, Q> g00(gx.x, gy.x); + vec<2, T, Q> g10(gx.y, gy.y); + vec<2, T, Q> g01(gx.z, gy.z); + vec<2, T, Q> g11(gx.w, gy.w); + + vec<4, T, Q> norm = detail::taylorInvSqrt(vec<4, T, Q>(dot(g00, g00), dot(g01, g01), dot(g10, g10), dot(g11, g11))); + g00 *= norm.x; + g01 *= norm.y; + g10 *= norm.z; + g11 *= norm.w; + + T n00 = dot(g00, vec<2, T, Q>(fx.x, fy.x)); + T n10 = dot(g10, vec<2, T, Q>(fx.y, fy.y)); + T n01 = dot(g01, vec<2, T, Q>(fx.z, fy.z)); + T n11 = dot(g11, vec<2, T, Q>(fx.w, fy.w)); + + vec<2, T, Q> fade_xy = detail::fade(vec<2, T, Q>(Pf.x, Pf.y)); + vec<2, T, Q> n_x = mix(vec<2, T, Q>(n00, n01), vec<2, T, Q>(n10, n11), fade_xy.x); + T n_xy = mix(n_x.x, n_x.y, fade_xy.y); + return T(2.3) * n_xy; + } + + // Classic Perlin noise, periodic variant + template + GLM_FUNC_QUALIFIER T perlin(vec<3, T, Q> const& Position, vec<3, T, Q> const& rep) + { + vec<3, T, Q> Pi0 = mod(floor(Position), rep); // Integer part, modulo period + vec<3, T, Q> Pi1 = mod(Pi0 + vec<3, T, Q>(T(1)), rep); // Integer part + 1, mod period + Pi0 = mod(Pi0, vec<3, T, Q>(289)); + Pi1 = mod(Pi1, vec<3, T, Q>(289)); + vec<3, T, Q> Pf0 = fract(Position); // Fractional part for interpolation + vec<3, T, Q> Pf1 = Pf0 - vec<3, T, Q>(T(1)); // Fractional part - 1.0 + vec<4, T, Q> ix = vec<4, T, Q>(Pi0.x, Pi1.x, Pi0.x, Pi1.x); + vec<4, T, Q> iy = vec<4, T, Q>(Pi0.y, Pi0.y, Pi1.y, Pi1.y); + vec<4, T, Q> iz0(Pi0.z); + vec<4, T, Q> iz1(Pi1.z); + + vec<4, T, Q> ixy = detail::permute(detail::permute(ix) + iy); + vec<4, T, Q> ixy0 = detail::permute(ixy + iz0); + vec<4, T, Q> ixy1 = detail::permute(ixy + iz1); + + vec<4, T, Q> gx0 = ixy0 / T(7); + vec<4, T, Q> gy0 = fract(floor(gx0) / T(7)) - T(0.5); + gx0 = fract(gx0); + vec<4, T, Q> gz0 = vec<4, T, Q>(0.5) - abs(gx0) - abs(gy0); + vec<4, T, Q> sz0 = step(gz0, vec<4, T, Q>(0)); + gx0 -= sz0 * (step(T(0), gx0) - T(0.5)); + gy0 -= sz0 * (step(T(0), gy0) - T(0.5)); + + vec<4, T, Q> gx1 = ixy1 / T(7); + vec<4, T, Q> gy1 = fract(floor(gx1) / T(7)) - T(0.5); + gx1 = fract(gx1); + vec<4, T, Q> gz1 = vec<4, T, Q>(0.5) - abs(gx1) - abs(gy1); + vec<4, T, Q> sz1 = step(gz1, vec<4, T, Q>(T(0))); + gx1 -= sz1 * (step(T(0), gx1) - T(0.5)); + gy1 -= sz1 * (step(T(0), gy1) - T(0.5)); + + vec<3, T, Q> g000 = vec<3, T, Q>(gx0.x, gy0.x, gz0.x); + vec<3, T, Q> g100 = vec<3, T, Q>(gx0.y, gy0.y, gz0.y); + vec<3, T, Q> g010 = vec<3, T, Q>(gx0.z, gy0.z, gz0.z); + vec<3, T, Q> g110 = vec<3, T, Q>(gx0.w, gy0.w, gz0.w); + vec<3, T, Q> g001 = vec<3, T, Q>(gx1.x, gy1.x, gz1.x); + vec<3, T, Q> g101 = vec<3, T, Q>(gx1.y, gy1.y, gz1.y); + vec<3, T, Q> g011 = vec<3, T, Q>(gx1.z, gy1.z, gz1.z); + vec<3, T, Q> g111 = vec<3, T, Q>(gx1.w, gy1.w, gz1.w); + + vec<4, T, Q> norm0 = detail::taylorInvSqrt(vec<4, T, Q>(dot(g000, g000), dot(g010, g010), dot(g100, g100), dot(g110, g110))); + g000 *= norm0.x; + g010 *= norm0.y; + g100 *= norm0.z; + g110 *= norm0.w; + vec<4, T, Q> norm1 = detail::taylorInvSqrt(vec<4, T, Q>(dot(g001, g001), dot(g011, g011), dot(g101, g101), dot(g111, g111))); + g001 *= norm1.x; + g011 *= norm1.y; + g101 *= norm1.z; + g111 *= norm1.w; + + T n000 = dot(g000, Pf0); + T n100 = dot(g100, vec<3, T, Q>(Pf1.x, Pf0.y, Pf0.z)); + T n010 = dot(g010, vec<3, T, Q>(Pf0.x, Pf1.y, Pf0.z)); + T n110 = dot(g110, vec<3, T, Q>(Pf1.x, Pf1.y, Pf0.z)); + T n001 = dot(g001, vec<3, T, Q>(Pf0.x, Pf0.y, Pf1.z)); + T n101 = dot(g101, vec<3, T, Q>(Pf1.x, Pf0.y, Pf1.z)); + T n011 = dot(g011, vec<3, T, Q>(Pf0.x, Pf1.y, Pf1.z)); + T n111 = dot(g111, Pf1); + + vec<3, T, Q> fade_xyz = detail::fade(Pf0); + vec<4, T, Q> n_z = mix(vec<4, T, Q>(n000, n100, n010, n110), vec<4, T, Q>(n001, n101, n011, n111), fade_xyz.z); + vec<2, T, Q> n_yz = mix(vec<2, T, Q>(n_z.x, n_z.y), vec<2, T, Q>(n_z.z, n_z.w), fade_xyz.y); + T n_xyz = mix(n_yz.x, n_yz.y, fade_xyz.x); + return T(2.2) * n_xyz; + } + + // Classic Perlin noise, periodic version + template + GLM_FUNC_QUALIFIER T perlin(vec<4, T, Q> const& Position, vec<4, T, Q> const& rep) + { + vec<4, T, Q> Pi0 = mod(floor(Position), rep); // Integer part modulo rep + vec<4, T, Q> Pi1 = mod(Pi0 + T(1), rep); // Integer part + 1 mod rep + vec<4, T, Q> Pf0 = fract(Position); // Fractional part for interpolation + vec<4, T, Q> Pf1 = Pf0 - T(1); // Fractional part - 1.0 + vec<4, T, Q> ix = vec<4, T, Q>(Pi0.x, Pi1.x, Pi0.x, Pi1.x); + vec<4, T, Q> iy = vec<4, T, Q>(Pi0.y, Pi0.y, Pi1.y, Pi1.y); + vec<4, T, Q> iz0(Pi0.z); + vec<4, T, Q> iz1(Pi1.z); + vec<4, T, Q> iw0(Pi0.w); + vec<4, T, Q> iw1(Pi1.w); + + vec<4, T, Q> ixy = detail::permute(detail::permute(ix) + iy); + vec<4, T, Q> ixy0 = detail::permute(ixy + iz0); + vec<4, T, Q> ixy1 = detail::permute(ixy + iz1); + vec<4, T, Q> ixy00 = detail::permute(ixy0 + iw0); + vec<4, T, Q> ixy01 = detail::permute(ixy0 + iw1); + vec<4, T, Q> ixy10 = detail::permute(ixy1 + iw0); + vec<4, T, Q> ixy11 = detail::permute(ixy1 + iw1); + + vec<4, T, Q> gx00 = ixy00 / T(7); + vec<4, T, Q> gy00 = floor(gx00) / T(7); + vec<4, T, Q> gz00 = floor(gy00) / T(6); + gx00 = fract(gx00) - T(0.5); + gy00 = fract(gy00) - T(0.5); + gz00 = fract(gz00) - T(0.5); + vec<4, T, Q> gw00 = vec<4, T, Q>(0.75) - abs(gx00) - abs(gy00) - abs(gz00); + vec<4, T, Q> sw00 = step(gw00, vec<4, T, Q>(0)); + gx00 -= sw00 * (step(T(0), gx00) - T(0.5)); + gy00 -= sw00 * (step(T(0), gy00) - T(0.5)); + + vec<4, T, Q> gx01 = ixy01 / T(7); + vec<4, T, Q> gy01 = floor(gx01) / T(7); + vec<4, T, Q> gz01 = floor(gy01) / T(6); + gx01 = fract(gx01) - T(0.5); + gy01 = fract(gy01) - T(0.5); + gz01 = fract(gz01) - T(0.5); + vec<4, T, Q> gw01 = vec<4, T, Q>(0.75) - abs(gx01) - abs(gy01) - abs(gz01); + vec<4, T, Q> sw01 = step(gw01, vec<4, T, Q>(0.0)); + gx01 -= sw01 * (step(T(0), gx01) - T(0.5)); + gy01 -= sw01 * (step(T(0), gy01) - T(0.5)); + + vec<4, T, Q> gx10 = ixy10 / T(7); + vec<4, T, Q> gy10 = floor(gx10) / T(7); + vec<4, T, Q> gz10 = floor(gy10) / T(6); + gx10 = fract(gx10) - T(0.5); + gy10 = fract(gy10) - T(0.5); + gz10 = fract(gz10) - T(0.5); + vec<4, T, Q> gw10 = vec<4, T, Q>(0.75) - abs(gx10) - abs(gy10) - abs(gz10); + vec<4, T, Q> sw10 = step(gw10, vec<4, T, Q>(0.0)); + gx10 -= sw10 * (step(T(0), gx10) - T(0.5)); + gy10 -= sw10 * (step(T(0), gy10) - T(0.5)); + + vec<4, T, Q> gx11 = ixy11 / T(7); + vec<4, T, Q> gy11 = floor(gx11) / T(7); + vec<4, T, Q> gz11 = floor(gy11) / T(6); + gx11 = fract(gx11) - T(0.5); + gy11 = fract(gy11) - T(0.5); + gz11 = fract(gz11) - T(0.5); + vec<4, T, Q> gw11 = vec<4, T, Q>(0.75) - abs(gx11) - abs(gy11) - abs(gz11); + vec<4, T, Q> sw11 = step(gw11, vec<4, T, Q>(T(0))); + gx11 -= sw11 * (step(T(0), gx11) - T(0.5)); + gy11 -= sw11 * (step(T(0), gy11) - T(0.5)); + + vec<4, T, Q> g0000(gx00.x, gy00.x, gz00.x, gw00.x); + vec<4, T, Q> g1000(gx00.y, gy00.y, gz00.y, gw00.y); + vec<4, T, Q> g0100(gx00.z, gy00.z, gz00.z, gw00.z); + vec<4, T, Q> g1100(gx00.w, gy00.w, gz00.w, gw00.w); + vec<4, T, Q> g0010(gx10.x, gy10.x, gz10.x, gw10.x); + vec<4, T, Q> g1010(gx10.y, gy10.y, gz10.y, gw10.y); + vec<4, T, Q> g0110(gx10.z, gy10.z, gz10.z, gw10.z); + vec<4, T, Q> g1110(gx10.w, gy10.w, gz10.w, gw10.w); + vec<4, T, Q> g0001(gx01.x, gy01.x, gz01.x, gw01.x); + vec<4, T, Q> g1001(gx01.y, gy01.y, gz01.y, gw01.y); + vec<4, T, Q> g0101(gx01.z, gy01.z, gz01.z, gw01.z); + vec<4, T, Q> g1101(gx01.w, gy01.w, gz01.w, gw01.w); + vec<4, T, Q> g0011(gx11.x, gy11.x, gz11.x, gw11.x); + vec<4, T, Q> g1011(gx11.y, gy11.y, gz11.y, gw11.y); + vec<4, T, Q> g0111(gx11.z, gy11.z, gz11.z, gw11.z); + vec<4, T, Q> g1111(gx11.w, gy11.w, gz11.w, gw11.w); + + vec<4, T, Q> norm00 = detail::taylorInvSqrt(vec<4, T, Q>(dot(g0000, g0000), dot(g0100, g0100), dot(g1000, g1000), dot(g1100, g1100))); + g0000 *= norm00.x; + g0100 *= norm00.y; + g1000 *= norm00.z; + g1100 *= norm00.w; + + vec<4, T, Q> norm01 = detail::taylorInvSqrt(vec<4, T, Q>(dot(g0001, g0001), dot(g0101, g0101), dot(g1001, g1001), dot(g1101, g1101))); + g0001 *= norm01.x; + g0101 *= norm01.y; + g1001 *= norm01.z; + g1101 *= norm01.w; + + vec<4, T, Q> norm10 = detail::taylorInvSqrt(vec<4, T, Q>(dot(g0010, g0010), dot(g0110, g0110), dot(g1010, g1010), dot(g1110, g1110))); + g0010 *= norm10.x; + g0110 *= norm10.y; + g1010 *= norm10.z; + g1110 *= norm10.w; + + vec<4, T, Q> norm11 = detail::taylorInvSqrt(vec<4, T, Q>(dot(g0011, g0011), dot(g0111, g0111), dot(g1011, g1011), dot(g1111, g1111))); + g0011 *= norm11.x; + g0111 *= norm11.y; + g1011 *= norm11.z; + g1111 *= norm11.w; + + T n0000 = dot(g0000, Pf0); + T n1000 = dot(g1000, vec<4, T, Q>(Pf1.x, Pf0.y, Pf0.z, Pf0.w)); + T n0100 = dot(g0100, vec<4, T, Q>(Pf0.x, Pf1.y, Pf0.z, Pf0.w)); + T n1100 = dot(g1100, vec<4, T, Q>(Pf1.x, Pf1.y, Pf0.z, Pf0.w)); + T n0010 = dot(g0010, vec<4, T, Q>(Pf0.x, Pf0.y, Pf1.z, Pf0.w)); + T n1010 = dot(g1010, vec<4, T, Q>(Pf1.x, Pf0.y, Pf1.z, Pf0.w)); + T n0110 = dot(g0110, vec<4, T, Q>(Pf0.x, Pf1.y, Pf1.z, Pf0.w)); + T n1110 = dot(g1110, vec<4, T, Q>(Pf1.x, Pf1.y, Pf1.z, Pf0.w)); + T n0001 = dot(g0001, vec<4, T, Q>(Pf0.x, Pf0.y, Pf0.z, Pf1.w)); + T n1001 = dot(g1001, vec<4, T, Q>(Pf1.x, Pf0.y, Pf0.z, Pf1.w)); + T n0101 = dot(g0101, vec<4, T, Q>(Pf0.x, Pf1.y, Pf0.z, Pf1.w)); + T n1101 = dot(g1101, vec<4, T, Q>(Pf1.x, Pf1.y, Pf0.z, Pf1.w)); + T n0011 = dot(g0011, vec<4, T, Q>(Pf0.x, Pf0.y, Pf1.z, Pf1.w)); + T n1011 = dot(g1011, vec<4, T, Q>(Pf1.x, Pf0.y, Pf1.z, Pf1.w)); + T n0111 = dot(g0111, vec<4, T, Q>(Pf0.x, Pf1.y, Pf1.z, Pf1.w)); + T n1111 = dot(g1111, Pf1); + + vec<4, T, Q> fade_xyzw = detail::fade(Pf0); + vec<4, T, Q> n_0w = mix(vec<4, T, Q>(n0000, n1000, n0100, n1100), vec<4, T, Q>(n0001, n1001, n0101, n1101), fade_xyzw.w); + vec<4, T, Q> n_1w = mix(vec<4, T, Q>(n0010, n1010, n0110, n1110), vec<4, T, Q>(n0011, n1011, n0111, n1111), fade_xyzw.w); + vec<4, T, Q> n_zw = mix(n_0w, n_1w, fade_xyzw.z); + vec<2, T, Q> n_yzw = mix(vec<2, T, Q>(n_zw.x, n_zw.y), vec<2, T, Q>(n_zw.z, n_zw.w), fade_xyzw.y); + T n_xyzw = mix(n_yzw.x, n_yzw.y, fade_xyzw.x); + return T(2.2) * n_xyzw; + } + + template + GLM_FUNC_QUALIFIER T simplex(glm::vec<2, T, Q> const& v) + { + vec<4, T, Q> const C = vec<4, T, Q>( + T( 0.211324865405187), // (3.0 - sqrt(3.0)) / 6.0 + T( 0.366025403784439), // 0.5 * (sqrt(3.0) - 1.0) + T(-0.577350269189626), // -1.0 + 2.0 * C.x + T( 0.024390243902439)); // 1.0 / 41.0 + + // First corner + vec<2, T, Q> i = floor(v + dot(v, vec<2, T, Q>(C[1]))); + vec<2, T, Q> x0 = v - i + dot(i, vec<2, T, Q>(C[0])); + + // Other corners + //i1.x = step( x0.y, x0.x ); // x0.x > x0.y ? 1.0 : 0.0 + //i1.y = 1.0 - i1.x; + vec<2, T, Q> i1 = (x0.x > x0.y) ? vec<2, T, Q>(1, 0) : vec<2, T, Q>(0, 1); + // x0 = x0 - 0.0 + 0.0 * C.xx ; + // x1 = x0 - i1 + 1.0 * C.xx ; + // x2 = x0 - 1.0 + 2.0 * C.xx ; + vec<4, T, Q> x12 = vec<4, T, Q>(x0.x, x0.y, x0.x, x0.y) + vec<4, T, Q>(C.x, C.x, C.z, C.z); + x12 = vec<4, T, Q>(vec<2, T, Q>(x12) - i1, x12.z, x12.w); + + // Permutations + i = mod(i, vec<2, T, Q>(289)); // Avoid truncation effects in permutation + vec<3, T, Q> p = detail::permute( + detail::permute(i.y + vec<3, T, Q>(T(0), i1.y, T(1))) + + i.x + vec<3, T, Q>(T(0), i1.x, T(1))); + + vec<3, T, Q> m = max(vec<3, T, Q>(0.5) - vec<3, T, Q>( + dot(x0, x0), + dot(vec<2, T, Q>(x12.x, x12.y), vec<2, T, Q>(x12.x, x12.y)), + dot(vec<2, T, Q>(x12.z, x12.w), vec<2, T, Q>(x12.z, x12.w))), vec<3, T, Q>(0)); + m = m * m ; + m = m * m ; + + // Gradients: 41 points uniformly over a line, mapped onto a diamond. + // The ring size 17*17 = 289 is close to a multiple of 41 (41*7 = 287) + + vec<3, T, Q> x = static_cast(2) * fract(p * C.w) - T(1); + vec<3, T, Q> h = abs(x) - T(0.5); + vec<3, T, Q> ox = floor(x + T(0.5)); + vec<3, T, Q> a0 = x - ox; + + // Normalise gradients implicitly by scaling m + // Inlined for speed: m *= taylorInvSqrt( a0*a0 + h*h ); + m *= static_cast(1.79284291400159) - T(0.85373472095314) * (a0 * a0 + h * h); + + // Compute final noise value at P + vec<3, T, Q> g; + g.x = a0.x * x0.x + h.x * x0.y; + //g.yz = a0.yz * x12.xz + h.yz * x12.yw; + g.y = a0.y * x12.x + h.y * x12.y; + g.z = a0.z * x12.z + h.z * x12.w; + return T(130) * dot(m, g); + } + + template + GLM_FUNC_QUALIFIER T simplex(vec<3, T, Q> const& v) + { + vec<2, T, Q> const C(1.0 / 6.0, 1.0 / 3.0); + vec<4, T, Q> const D(0.0, 0.5, 1.0, 2.0); + + // First corner + vec<3, T, Q> i(floor(v + dot(v, vec<3, T, Q>(C.y)))); + vec<3, T, Q> x0(v - i + dot(i, vec<3, T, Q>(C.x))); + + // Other corners + vec<3, T, Q> g(step(vec<3, T, Q>(x0.y, x0.z, x0.x), x0)); + vec<3, T, Q> l(T(1) - g); + vec<3, T, Q> i1(min(g, vec<3, T, Q>(l.z, l.x, l.y))); + vec<3, T, Q> i2(max(g, vec<3, T, Q>(l.z, l.x, l.y))); + + // x0 = x0 - 0.0 + 0.0 * C.xxx; + // x1 = x0 - i1 + 1.0 * C.xxx; + // x2 = x0 - i2 + 2.0 * C.xxx; + // x3 = x0 - 1.0 + 3.0 * C.xxx; + vec<3, T, Q> x1(x0 - i1 + C.x); + vec<3, T, Q> x2(x0 - i2 + C.y); // 2.0*C.x = 1/3 = C.y + vec<3, T, Q> x3(x0 - D.y); // -1.0+3.0*C.x = -0.5 = -D.y + + // Permutations + i = detail::mod289(i); + vec<4, T, Q> p(detail::permute(detail::permute(detail::permute( + i.z + vec<4, T, Q>(T(0), i1.z, i2.z, T(1))) + + i.y + vec<4, T, Q>(T(0), i1.y, i2.y, T(1))) + + i.x + vec<4, T, Q>(T(0), i1.x, i2.x, T(1)))); + + // Gradients: 7x7 points over a square, mapped onto an octahedron. + // The ring size 17*17 = 289 is close to a multiple of 49 (49*6 = 294) + T n_ = static_cast(0.142857142857); // 1.0/7.0 + vec<3, T, Q> ns(n_ * vec<3, T, Q>(D.w, D.y, D.z) - vec<3, T, Q>(D.x, D.z, D.x)); + + vec<4, T, Q> j(p - T(49) * floor(p * ns.z * ns.z)); // mod(p,7*7) + + vec<4, T, Q> x_(floor(j * ns.z)); + vec<4, T, Q> y_(floor(j - T(7) * x_)); // mod(j,N) + + vec<4, T, Q> x(x_ * ns.x + ns.y); + vec<4, T, Q> y(y_ * ns.x + ns.y); + vec<4, T, Q> h(T(1) - abs(x) - abs(y)); + + vec<4, T, Q> b0(x.x, x.y, y.x, y.y); + vec<4, T, Q> b1(x.z, x.w, y.z, y.w); + + // vec4 s0 = vec4(lessThan(b0,0.0))*2.0 - 1.0; + // vec4 s1 = vec4(lessThan(b1,0.0))*2.0 - 1.0; + vec<4, T, Q> s0(floor(b0) * T(2) + T(1)); + vec<4, T, Q> s1(floor(b1) * T(2) + T(1)); + vec<4, T, Q> sh(-step(h, vec<4, T, Q>(0.0))); + + vec<4, T, Q> a0 = vec<4, T, Q>(b0.x, b0.z, b0.y, b0.w) + vec<4, T, Q>(s0.x, s0.z, s0.y, s0.w) * vec<4, T, Q>(sh.x, sh.x, sh.y, sh.y); + vec<4, T, Q> a1 = vec<4, T, Q>(b1.x, b1.z, b1.y, b1.w) + vec<4, T, Q>(s1.x, s1.z, s1.y, s1.w) * vec<4, T, Q>(sh.z, sh.z, sh.w, sh.w); + + vec<3, T, Q> p0(a0.x, a0.y, h.x); + vec<3, T, Q> p1(a0.z, a0.w, h.y); + vec<3, T, Q> p2(a1.x, a1.y, h.z); + vec<3, T, Q> p3(a1.z, a1.w, h.w); + + // Normalise gradients + vec<4, T, Q> norm = detail::taylorInvSqrt(vec<4, T, Q>(dot(p0, p0), dot(p1, p1), dot(p2, p2), dot(p3, p3))); + p0 *= norm.x; + p1 *= norm.y; + p2 *= norm.z; + p3 *= norm.w; + + // Mix final noise value + vec<4, T, Q> m = max(T(0.6) - vec<4, T, Q>(dot(x0, x0), dot(x1, x1), dot(x2, x2), dot(x3, x3)), vec<4, T, Q>(0)); + m = m * m; + return T(42) * dot(m * m, vec<4, T, Q>(dot(p0, x0), dot(p1, x1), dot(p2, x2), dot(p3, x3))); + } + + template + GLM_FUNC_QUALIFIER T simplex(vec<4, T, Q> const& v) + { + vec<4, T, Q> const C( + 0.138196601125011, // (5 - sqrt(5))/20 G4 + 0.276393202250021, // 2 * G4 + 0.414589803375032, // 3 * G4 + -0.447213595499958); // -1 + 4 * G4 + + // (sqrt(5) - 1)/4 = F4, used once below + T const F4 = static_cast(0.309016994374947451); + + // First corner + vec<4, T, Q> i = floor(v + dot(v, vec<4, T, Q>(F4))); + vec<4, T, Q> x0 = v - i + dot(i, vec<4, T, Q>(C.x)); + + // Other corners + + // Rank sorting originally contributed by Bill Licea-Kane, AMD (formerly ATI) + vec<4, T, Q> i0; + vec<3, T, Q> isX = step(vec<3, T, Q>(x0.y, x0.z, x0.w), vec<3, T, Q>(x0.x)); + vec<3, T, Q> isYZ = step(vec<3, T, Q>(x0.z, x0.w, x0.w), vec<3, T, Q>(x0.y, x0.y, x0.z)); + // i0.x = dot(isX, vec3(1.0)); + //i0.x = isX.x + isX.y + isX.z; + //i0.yzw = static_cast(1) - isX; + i0 = vec<4, T, Q>(isX.x + isX.y + isX.z, T(1) - isX); + // i0.y += dot(isYZ.xy, vec2(1.0)); + i0.y += isYZ.x + isYZ.y; + //i0.zw += 1.0 - vec<2, T, Q>(isYZ.x, isYZ.y); + i0.z += static_cast(1) - isYZ.x; + i0.w += static_cast(1) - isYZ.y; + i0.z += isYZ.z; + i0.w += static_cast(1) - isYZ.z; + + // i0 now contains the unique values 0,1,2,3 in each channel + vec<4, T, Q> i3 = clamp(i0, T(0), T(1)); + vec<4, T, Q> i2 = clamp(i0 - T(1), T(0), T(1)); + vec<4, T, Q> i1 = clamp(i0 - T(2), T(0), T(1)); + + // x0 = x0 - 0.0 + 0.0 * C.xxxx + // x1 = x0 - i1 + 0.0 * C.xxxx + // x2 = x0 - i2 + 0.0 * C.xxxx + // x3 = x0 - i3 + 0.0 * C.xxxx + // x4 = x0 - 1.0 + 4.0 * C.xxxx + vec<4, T, Q> x1 = x0 - i1 + C.x; + vec<4, T, Q> x2 = x0 - i2 + C.y; + vec<4, T, Q> x3 = x0 - i3 + C.z; + vec<4, T, Q> x4 = x0 + C.w; + + // Permutations + i = mod(i, vec<4, T, Q>(289)); + T j0 = detail::permute(detail::permute(detail::permute(detail::permute(i.w) + i.z) + i.y) + i.x); + vec<4, T, Q> j1 = detail::permute(detail::permute(detail::permute(detail::permute( + i.w + vec<4, T, Q>(i1.w, i2.w, i3.w, T(1))) + + i.z + vec<4, T, Q>(i1.z, i2.z, i3.z, T(1))) + + i.y + vec<4, T, Q>(i1.y, i2.y, i3.y, T(1))) + + i.x + vec<4, T, Q>(i1.x, i2.x, i3.x, T(1))); + + // Gradients: 7x7x6 points over a cube, mapped onto a 4-cross polytope + // 7*7*6 = 294, which is close to the ring size 17*17 = 289. + vec<4, T, Q> ip = vec<4, T, Q>(T(1) / T(294), T(1) / T(49), T(1) / T(7), T(0)); + + vec<4, T, Q> p0 = gtc::grad4(j0, ip); + vec<4, T, Q> p1 = gtc::grad4(j1.x, ip); + vec<4, T, Q> p2 = gtc::grad4(j1.y, ip); + vec<4, T, Q> p3 = gtc::grad4(j1.z, ip); + vec<4, T, Q> p4 = gtc::grad4(j1.w, ip); + + // Normalise gradients + vec<4, T, Q> norm = detail::taylorInvSqrt(vec<4, T, Q>(dot(p0, p0), dot(p1, p1), dot(p2, p2), dot(p3, p3))); + p0 *= norm.x; + p1 *= norm.y; + p2 *= norm.z; + p3 *= norm.w; + p4 *= detail::taylorInvSqrt(dot(p4, p4)); + + // Mix contributions from the five corners + vec<3, T, Q> m0 = max(T(0.6) - vec<3, T, Q>(dot(x0, x0), dot(x1, x1), dot(x2, x2)), vec<3, T, Q>(0)); + vec<2, T, Q> m1 = max(T(0.6) - vec<2, T, Q>(dot(x3, x3), dot(x4, x4) ), vec<2, T, Q>(0)); + m0 = m0 * m0; + m1 = m1 * m1; + return T(49) * + (dot(m0 * m0, vec<3, T, Q>(dot(p0, x0), dot(p1, x1), dot(p2, x2))) + + dot(m1 * m1, vec<2, T, Q>(dot(p3, x3), dot(p4, x4)))); + } +}//namespace glm diff --git a/src/GLMath/glm/gtc/packing.hpp b/src/GLMath/glm/gtc/packing.hpp new file mode 100644 index 000000000..285239522 --- /dev/null +++ b/src/GLMath/glm/gtc/packing.hpp @@ -0,0 +1,728 @@ +/// @ref gtc_packing +/// @file glm/gtc/packing.hpp +/// +/// @see core (dependence) +/// +/// @defgroup gtc_packing GLM_GTC_packing +/// @ingroup gtc +/// +/// Include to use the features of this extension. +/// +/// This extension provides a set of function to convert vertors to packed +/// formats. + +#pragma once + +// Dependency: +#include "type_precision.hpp" + +#if GLM_MESSAGES == GLM_ENABLE && !defined(GLM_EXT_INCLUDED) +# pragma message("GLM: GLM_GTC_packing extension included") +#endif + +namespace glm +{ + /// @addtogroup gtc_packing + /// @{ + + /// First, converts the normalized floating-point value v into a 8-bit integer value. + /// Then, the results are packed into the returned 8-bit unsigned integer. + /// + /// The conversion for component c of v to fixed point is done as follows: + /// packUnorm1x8: round(clamp(c, 0, +1) * 255.0) + /// + /// @see gtc_packing + /// @see uint16 packUnorm2x8(vec2 const& v) + /// @see uint32 packUnorm4x8(vec4 const& v) + /// @see GLSL packUnorm4x8 man page + /// @see GLSL 4.20.8 specification, section 8.4 Floating-Point Pack and Unpack Functions + GLM_FUNC_DECL uint8 packUnorm1x8(float v); + + /// Convert a single 8-bit integer to a normalized floating-point value. + /// + /// The conversion for unpacked fixed-point value f to floating point is done as follows: + /// unpackUnorm4x8: f / 255.0 + /// + /// @see gtc_packing + /// @see vec2 unpackUnorm2x8(uint16 p) + /// @see vec4 unpackUnorm4x8(uint32 p) + /// @see GLSL unpackUnorm4x8 man page + /// @see GLSL 4.20.8 specification, section 8.4 Floating-Point Pack and Unpack Functions + GLM_FUNC_DECL float unpackUnorm1x8(uint8 p); + + /// First, converts each component of the normalized floating-point value v into 8-bit integer values. + /// Then, the results are packed into the returned 16-bit unsigned integer. + /// + /// The conversion for component c of v to fixed point is done as follows: + /// packUnorm2x8: round(clamp(c, 0, +1) * 255.0) + /// + /// The first component of the vector will be written to the least significant bits of the output; + /// the last component will be written to the most significant bits. + /// + /// @see gtc_packing + /// @see uint8 packUnorm1x8(float const& v) + /// @see uint32 packUnorm4x8(vec4 const& v) + /// @see GLSL packUnorm4x8 man page + /// @see GLSL 4.20.8 specification, section 8.4 Floating-Point Pack and Unpack Functions + GLM_FUNC_DECL uint16 packUnorm2x8(vec2 const& v); + + /// First, unpacks a single 16-bit unsigned integer p into a pair of 8-bit unsigned integers. + /// Then, each component is converted to a normalized floating-point value to generate the returned two-component vector. + /// + /// The conversion for unpacked fixed-point value f to floating point is done as follows: + /// unpackUnorm4x8: f / 255.0 + /// + /// The first component of the returned vector will be extracted from the least significant bits of the input; + /// the last component will be extracted from the most significant bits. + /// + /// @see gtc_packing + /// @see float unpackUnorm1x8(uint8 v) + /// @see vec4 unpackUnorm4x8(uint32 p) + /// @see GLSL unpackUnorm4x8 man page + /// @see GLSL 4.20.8 specification, section 8.4 Floating-Point Pack and Unpack Functions + GLM_FUNC_DECL vec2 unpackUnorm2x8(uint16 p); + + /// First, converts the normalized floating-point value v into 8-bit integer value. + /// Then, the results are packed into the returned 8-bit unsigned integer. + /// + /// The conversion to fixed point is done as follows: + /// packSnorm1x8: round(clamp(s, -1, +1) * 127.0) + /// + /// @see gtc_packing + /// @see uint16 packSnorm2x8(vec2 const& v) + /// @see uint32 packSnorm4x8(vec4 const& v) + /// @see GLSL packSnorm4x8 man page + /// @see GLSL 4.20.8 specification, section 8.4 Floating-Point Pack and Unpack Functions + GLM_FUNC_DECL uint8 packSnorm1x8(float s); + + /// First, unpacks a single 8-bit unsigned integer p into a single 8-bit signed integers. + /// Then, the value is converted to a normalized floating-point value to generate the returned scalar. + /// + /// The conversion for unpacked fixed-point value f to floating point is done as follows: + /// unpackSnorm1x8: clamp(f / 127.0, -1, +1) + /// + /// @see gtc_packing + /// @see vec2 unpackSnorm2x8(uint16 p) + /// @see vec4 unpackSnorm4x8(uint32 p) + /// @see GLSL unpackSnorm4x8 man page + /// @see GLSL 4.20.8 specification, section 8.4 Floating-Point Pack and Unpack Functions + GLM_FUNC_DECL float unpackSnorm1x8(uint8 p); + + /// First, converts each component of the normalized floating-point value v into 8-bit integer values. + /// Then, the results are packed into the returned 16-bit unsigned integer. + /// + /// The conversion for component c of v to fixed point is done as follows: + /// packSnorm2x8: round(clamp(c, -1, +1) * 127.0) + /// + /// The first component of the vector will be written to the least significant bits of the output; + /// the last component will be written to the most significant bits. + /// + /// @see gtc_packing + /// @see uint8 packSnorm1x8(float const& v) + /// @see uint32 packSnorm4x8(vec4 const& v) + /// @see GLSL packSnorm4x8 man page + /// @see GLSL 4.20.8 specification, section 8.4 Floating-Point Pack and Unpack Functions + GLM_FUNC_DECL uint16 packSnorm2x8(vec2 const& v); + + /// First, unpacks a single 16-bit unsigned integer p into a pair of 8-bit signed integers. + /// Then, each component is converted to a normalized floating-point value to generate the returned two-component vector. + /// + /// The conversion for unpacked fixed-point value f to floating point is done as follows: + /// unpackSnorm2x8: clamp(f / 127.0, -1, +1) + /// + /// The first component of the returned vector will be extracted from the least significant bits of the input; + /// the last component will be extracted from the most significant bits. + /// + /// @see gtc_packing + /// @see float unpackSnorm1x8(uint8 p) + /// @see vec4 unpackSnorm4x8(uint32 p) + /// @see GLSL unpackSnorm4x8 man page + /// @see GLSL 4.20.8 specification, section 8.4 Floating-Point Pack and Unpack Functions + GLM_FUNC_DECL vec2 unpackSnorm2x8(uint16 p); + + /// First, converts the normalized floating-point value v into a 16-bit integer value. + /// Then, the results are packed into the returned 16-bit unsigned integer. + /// + /// The conversion for component c of v to fixed point is done as follows: + /// packUnorm1x16: round(clamp(c, 0, +1) * 65535.0) + /// + /// @see gtc_packing + /// @see uint16 packSnorm1x16(float const& v) + /// @see uint64 packSnorm4x16(vec4 const& v) + /// @see GLSL packUnorm4x8 man page + /// @see GLSL 4.20.8 specification, section 8.4 Floating-Point Pack and Unpack Functions + GLM_FUNC_DECL uint16 packUnorm1x16(float v); + + /// First, unpacks a single 16-bit unsigned integer p into a of 16-bit unsigned integers. + /// Then, the value is converted to a normalized floating-point value to generate the returned scalar. + /// + /// The conversion for unpacked fixed-point value f to floating point is done as follows: + /// unpackUnorm1x16: f / 65535.0 + /// + /// @see gtc_packing + /// @see vec2 unpackUnorm2x16(uint32 p) + /// @see vec4 unpackUnorm4x16(uint64 p) + /// @see GLSL unpackUnorm2x16 man page + /// @see GLSL 4.20.8 specification, section 8.4 Floating-Point Pack and Unpack Functions + GLM_FUNC_DECL float unpackUnorm1x16(uint16 p); + + /// First, converts each component of the normalized floating-point value v into 16-bit integer values. + /// Then, the results are packed into the returned 64-bit unsigned integer. + /// + /// The conversion for component c of v to fixed point is done as follows: + /// packUnorm4x16: round(clamp(c, 0, +1) * 65535.0) + /// + /// The first component of the vector will be written to the least significant bits of the output; + /// the last component will be written to the most significant bits. + /// + /// @see gtc_packing + /// @see uint16 packUnorm1x16(float const& v) + /// @see uint32 packUnorm2x16(vec2 const& v) + /// @see GLSL packUnorm4x8 man page + /// @see GLSL 4.20.8 specification, section 8.4 Floating-Point Pack and Unpack Functions + GLM_FUNC_DECL uint64 packUnorm4x16(vec4 const& v); + + /// First, unpacks a single 64-bit unsigned integer p into four 16-bit unsigned integers. + /// Then, each component is converted to a normalized floating-point value to generate the returned four-component vector. + /// + /// The conversion for unpacked fixed-point value f to floating point is done as follows: + /// unpackUnormx4x16: f / 65535.0 + /// + /// The first component of the returned vector will be extracted from the least significant bits of the input; + /// the last component will be extracted from the most significant bits. + /// + /// @see gtc_packing + /// @see float unpackUnorm1x16(uint16 p) + /// @see vec2 unpackUnorm2x16(uint32 p) + /// @see GLSL unpackUnorm2x16 man page + /// @see GLSL 4.20.8 specification, section 8.4 Floating-Point Pack and Unpack Functions + GLM_FUNC_DECL vec4 unpackUnorm4x16(uint64 p); + + /// First, converts the normalized floating-point value v into 16-bit integer value. + /// Then, the results are packed into the returned 16-bit unsigned integer. + /// + /// The conversion to fixed point is done as follows: + /// packSnorm1x8: round(clamp(s, -1, +1) * 32767.0) + /// + /// @see gtc_packing + /// @see uint32 packSnorm2x16(vec2 const& v) + /// @see uint64 packSnorm4x16(vec4 const& v) + /// @see GLSL packSnorm4x8 man page + /// @see GLSL 4.20.8 specification, section 8.4 Floating-Point Pack and Unpack Functions + GLM_FUNC_DECL uint16 packSnorm1x16(float v); + + /// First, unpacks a single 16-bit unsigned integer p into a single 16-bit signed integers. + /// Then, each component is converted to a normalized floating-point value to generate the returned scalar. + /// + /// The conversion for unpacked fixed-point value f to floating point is done as follows: + /// unpackSnorm1x16: clamp(f / 32767.0, -1, +1) + /// + /// @see gtc_packing + /// @see vec2 unpackSnorm2x16(uint32 p) + /// @see vec4 unpackSnorm4x16(uint64 p) + /// @see GLSL unpackSnorm4x8 man page + /// @see GLSL 4.20.8 specification, section 8.4 Floating-Point Pack and Unpack Functions + GLM_FUNC_DECL float unpackSnorm1x16(uint16 p); + + /// First, converts each component of the normalized floating-point value v into 16-bit integer values. + /// Then, the results are packed into the returned 64-bit unsigned integer. + /// + /// The conversion for component c of v to fixed point is done as follows: + /// packSnorm2x8: round(clamp(c, -1, +1) * 32767.0) + /// + /// The first component of the vector will be written to the least significant bits of the output; + /// the last component will be written to the most significant bits. + /// + /// @see gtc_packing + /// @see uint16 packSnorm1x16(float const& v) + /// @see uint32 packSnorm2x16(vec2 const& v) + /// @see GLSL packSnorm4x8 man page + /// @see GLSL 4.20.8 specification, section 8.4 Floating-Point Pack and Unpack Functions + GLM_FUNC_DECL uint64 packSnorm4x16(vec4 const& v); + + /// First, unpacks a single 64-bit unsigned integer p into four 16-bit signed integers. + /// Then, each component is converted to a normalized floating-point value to generate the returned four-component vector. + /// + /// The conversion for unpacked fixed-point value f to floating point is done as follows: + /// unpackSnorm4x16: clamp(f / 32767.0, -1, +1) + /// + /// The first component of the returned vector will be extracted from the least significant bits of the input; + /// the last component will be extracted from the most significant bits. + /// + /// @see gtc_packing + /// @see float unpackSnorm1x16(uint16 p) + /// @see vec2 unpackSnorm2x16(uint32 p) + /// @see GLSL unpackSnorm4x8 man page + /// @see GLSL 4.20.8 specification, section 8.4 Floating-Point Pack and Unpack Functions + GLM_FUNC_DECL vec4 unpackSnorm4x16(uint64 p); + + /// Returns an unsigned integer obtained by converting the components of a floating-point scalar + /// to the 16-bit floating-point representation found in the OpenGL Specification, + /// and then packing this 16-bit value into a 16-bit unsigned integer. + /// + /// @see gtc_packing + /// @see uint32 packHalf2x16(vec2 const& v) + /// @see uint64 packHalf4x16(vec4 const& v) + /// @see GLSL packHalf2x16 man page + /// @see GLSL 4.20.8 specification, section 8.4 Floating-Point Pack and Unpack Functions + GLM_FUNC_DECL uint16 packHalf1x16(float v); + + /// Returns a floating-point scalar with components obtained by unpacking a 16-bit unsigned integer into a 16-bit value, + /// interpreted as a 16-bit floating-point number according to the OpenGL Specification, + /// and converting it to 32-bit floating-point values. + /// + /// @see gtc_packing + /// @see vec2 unpackHalf2x16(uint32 const& v) + /// @see vec4 unpackHalf4x16(uint64 const& v) + /// @see GLSL unpackHalf2x16 man page + /// @see GLSL 4.20.8 specification, section 8.4 Floating-Point Pack and Unpack Functions + GLM_FUNC_DECL float unpackHalf1x16(uint16 v); + + /// Returns an unsigned integer obtained by converting the components of a four-component floating-point vector + /// to the 16-bit floating-point representation found in the OpenGL Specification, + /// and then packing these four 16-bit values into a 64-bit unsigned integer. + /// The first vector component specifies the 16 least-significant bits of the result; + /// the forth component specifies the 16 most-significant bits. + /// + /// @see gtc_packing + /// @see uint16 packHalf1x16(float const& v) + /// @see uint32 packHalf2x16(vec2 const& v) + /// @see GLSL packHalf2x16 man page + /// @see GLSL 4.20.8 specification, section 8.4 Floating-Point Pack and Unpack Functions + GLM_FUNC_DECL uint64 packHalf4x16(vec4 const& v); + + /// Returns a four-component floating-point vector with components obtained by unpacking a 64-bit unsigned integer into four 16-bit values, + /// interpreting those values as 16-bit floating-point numbers according to the OpenGL Specification, + /// and converting them to 32-bit floating-point values. + /// The first component of the vector is obtained from the 16 least-significant bits of v; + /// the forth component is obtained from the 16 most-significant bits of v. + /// + /// @see gtc_packing + /// @see float unpackHalf1x16(uint16 const& v) + /// @see vec2 unpackHalf2x16(uint32 const& v) + /// @see GLSL unpackHalf2x16 man page + /// @see GLSL 4.20.8 specification, section 8.4 Floating-Point Pack and Unpack Functions + GLM_FUNC_DECL vec4 unpackHalf4x16(uint64 p); + + /// Returns an unsigned integer obtained by converting the components of a four-component signed integer vector + /// to the 10-10-10-2-bit signed integer representation found in the OpenGL Specification, + /// and then packing these four values into a 32-bit unsigned integer. + /// The first vector component specifies the 10 least-significant bits of the result; + /// the forth component specifies the 2 most-significant bits. + /// + /// @see gtc_packing + /// @see uint32 packI3x10_1x2(uvec4 const& v) + /// @see uint32 packSnorm3x10_1x2(vec4 const& v) + /// @see uint32 packUnorm3x10_1x2(vec4 const& v) + /// @see ivec4 unpackI3x10_1x2(uint32 const& p) + GLM_FUNC_DECL uint32 packI3x10_1x2(ivec4 const& v); + + /// Unpacks a single 32-bit unsigned integer p into three 10-bit and one 2-bit signed integers. + /// + /// The first component of the returned vector will be extracted from the least significant bits of the input; + /// the last component will be extracted from the most significant bits. + /// + /// @see gtc_packing + /// @see uint32 packU3x10_1x2(uvec4 const& v) + /// @see vec4 unpackSnorm3x10_1x2(uint32 const& p); + /// @see uvec4 unpackI3x10_1x2(uint32 const& p); + GLM_FUNC_DECL ivec4 unpackI3x10_1x2(uint32 p); + + /// Returns an unsigned integer obtained by converting the components of a four-component unsigned integer vector + /// to the 10-10-10-2-bit unsigned integer representation found in the OpenGL Specification, + /// and then packing these four values into a 32-bit unsigned integer. + /// The first vector component specifies the 10 least-significant bits of the result; + /// the forth component specifies the 2 most-significant bits. + /// + /// @see gtc_packing + /// @see uint32 packI3x10_1x2(ivec4 const& v) + /// @see uint32 packSnorm3x10_1x2(vec4 const& v) + /// @see uint32 packUnorm3x10_1x2(vec4 const& v) + /// @see ivec4 unpackU3x10_1x2(uint32 const& p) + GLM_FUNC_DECL uint32 packU3x10_1x2(uvec4 const& v); + + /// Unpacks a single 32-bit unsigned integer p into three 10-bit and one 2-bit unsigned integers. + /// + /// The first component of the returned vector will be extracted from the least significant bits of the input; + /// the last component will be extracted from the most significant bits. + /// + /// @see gtc_packing + /// @see uint32 packU3x10_1x2(uvec4 const& v) + /// @see vec4 unpackSnorm3x10_1x2(uint32 const& p); + /// @see uvec4 unpackI3x10_1x2(uint32 const& p); + GLM_FUNC_DECL uvec4 unpackU3x10_1x2(uint32 p); + + /// First, converts the first three components of the normalized floating-point value v into 10-bit signed integer values. + /// Then, converts the forth component of the normalized floating-point value v into 2-bit signed integer values. + /// Then, the results are packed into the returned 32-bit unsigned integer. + /// + /// The conversion for component c of v to fixed point is done as follows: + /// packSnorm3x10_1x2(xyz): round(clamp(c, -1, +1) * 511.0) + /// packSnorm3x10_1x2(w): round(clamp(c, -1, +1) * 1.0) + /// + /// The first vector component specifies the 10 least-significant bits of the result; + /// the forth component specifies the 2 most-significant bits. + /// + /// @see gtc_packing + /// @see vec4 unpackSnorm3x10_1x2(uint32 const& p) + /// @see uint32 packUnorm3x10_1x2(vec4 const& v) + /// @see uint32 packU3x10_1x2(uvec4 const& v) + /// @see uint32 packI3x10_1x2(ivec4 const& v) + GLM_FUNC_DECL uint32 packSnorm3x10_1x2(vec4 const& v); + + /// First, unpacks a single 32-bit unsigned integer p into four 16-bit signed integers. + /// Then, each component is converted to a normalized floating-point value to generate the returned four-component vector. + /// + /// The conversion for unpacked fixed-point value f to floating point is done as follows: + /// unpackSnorm3x10_1x2(xyz): clamp(f / 511.0, -1, +1) + /// unpackSnorm3x10_1x2(w): clamp(f / 511.0, -1, +1) + /// + /// The first component of the returned vector will be extracted from the least significant bits of the input; + /// the last component will be extracted from the most significant bits. + /// + /// @see gtc_packing + /// @see uint32 packSnorm3x10_1x2(vec4 const& v) + /// @see vec4 unpackUnorm3x10_1x2(uint32 const& p)) + /// @see uvec4 unpackI3x10_1x2(uint32 const& p) + /// @see uvec4 unpackU3x10_1x2(uint32 const& p) + GLM_FUNC_DECL vec4 unpackSnorm3x10_1x2(uint32 p); + + /// First, converts the first three components of the normalized floating-point value v into 10-bit unsigned integer values. + /// Then, converts the forth component of the normalized floating-point value v into 2-bit signed uninteger values. + /// Then, the results are packed into the returned 32-bit unsigned integer. + /// + /// The conversion for component c of v to fixed point is done as follows: + /// packUnorm3x10_1x2(xyz): round(clamp(c, 0, +1) * 1023.0) + /// packUnorm3x10_1x2(w): round(clamp(c, 0, +1) * 3.0) + /// + /// The first vector component specifies the 10 least-significant bits of the result; + /// the forth component specifies the 2 most-significant bits. + /// + /// @see gtc_packing + /// @see vec4 unpackUnorm3x10_1x2(uint32 const& p) + /// @see uint32 packUnorm3x10_1x2(vec4 const& v) + /// @see uint32 packU3x10_1x2(uvec4 const& v) + /// @see uint32 packI3x10_1x2(ivec4 const& v) + GLM_FUNC_DECL uint32 packUnorm3x10_1x2(vec4 const& v); + + /// First, unpacks a single 32-bit unsigned integer p into four 16-bit signed integers. + /// Then, each component is converted to a normalized floating-point value to generate the returned four-component vector. + /// + /// The conversion for unpacked fixed-point value f to floating point is done as follows: + /// unpackSnorm3x10_1x2(xyz): clamp(f / 1023.0, 0, +1) + /// unpackSnorm3x10_1x2(w): clamp(f / 3.0, 0, +1) + /// + /// The first component of the returned vector will be extracted from the least significant bits of the input; + /// the last component will be extracted from the most significant bits. + /// + /// @see gtc_packing + /// @see uint32 packSnorm3x10_1x2(vec4 const& v) + /// @see vec4 unpackInorm3x10_1x2(uint32 const& p)) + /// @see uvec4 unpackI3x10_1x2(uint32 const& p) + /// @see uvec4 unpackU3x10_1x2(uint32 const& p) + GLM_FUNC_DECL vec4 unpackUnorm3x10_1x2(uint32 p); + + /// First, converts the first two components of the normalized floating-point value v into 11-bit signless floating-point values. + /// Then, converts the third component of the normalized floating-point value v into a 10-bit signless floating-point value. + /// Then, the results are packed into the returned 32-bit unsigned integer. + /// + /// The first vector component specifies the 11 least-significant bits of the result; + /// the last component specifies the 10 most-significant bits. + /// + /// @see gtc_packing + /// @see vec3 unpackF2x11_1x10(uint32 const& p) + GLM_FUNC_DECL uint32 packF2x11_1x10(vec3 const& v); + + /// First, unpacks a single 32-bit unsigned integer p into two 11-bit signless floating-point values and one 10-bit signless floating-point value . + /// Then, each component is converted to a normalized floating-point value to generate the returned three-component vector. + /// + /// The first component of the returned vector will be extracted from the least significant bits of the input; + /// the last component will be extracted from the most significant bits. + /// + /// @see gtc_packing + /// @see uint32 packF2x11_1x10(vec3 const& v) + GLM_FUNC_DECL vec3 unpackF2x11_1x10(uint32 p); + + + /// First, converts the first two components of the normalized floating-point value v into 11-bit signless floating-point values. + /// Then, converts the third component of the normalized floating-point value v into a 10-bit signless floating-point value. + /// Then, the results are packed into the returned 32-bit unsigned integer. + /// + /// The first vector component specifies the 11 least-significant bits of the result; + /// the last component specifies the 10 most-significant bits. + /// + /// packF3x9_E1x5 allows encoding into RGBE / RGB9E5 format + /// + /// @see gtc_packing + /// @see vec3 unpackF3x9_E1x5(uint32 const& p) + GLM_FUNC_DECL uint32 packF3x9_E1x5(vec3 const& v); + + /// First, unpacks a single 32-bit unsigned integer p into two 11-bit signless floating-point values and one 10-bit signless floating-point value . + /// Then, each component is converted to a normalized floating-point value to generate the returned three-component vector. + /// + /// The first component of the returned vector will be extracted from the least significant bits of the input; + /// the last component will be extracted from the most significant bits. + /// + /// unpackF3x9_E1x5 allows decoding RGBE / RGB9E5 data + /// + /// @see gtc_packing + /// @see uint32 packF3x9_E1x5(vec3 const& v) + GLM_FUNC_DECL vec3 unpackF3x9_E1x5(uint32 p); + + /// Returns an unsigned integer vector obtained by converting the components of a floating-point vector + /// to the 16-bit floating-point representation found in the OpenGL Specification. + /// The first vector component specifies the 16 least-significant bits of the result; + /// the forth component specifies the 16 most-significant bits. + /// + /// @see gtc_packing + /// @see vec<3, T, Q> unpackRGBM(vec<4, T, Q> const& p) + /// @see GLSL 4.20.8 specification, section 8.4 Floating-Point Pack and Unpack Functions + template + GLM_FUNC_DECL vec<4, T, Q> packRGBM(vec<3, T, Q> const& rgb); + + /// Returns a floating-point vector with components obtained by reinterpreting an integer vector as 16-bit floating-point numbers and converting them to 32-bit floating-point values. + /// The first component of the vector is obtained from the 16 least-significant bits of v; + /// the forth component is obtained from the 16 most-significant bits of v. + /// + /// @see gtc_packing + /// @see vec<4, T, Q> packRGBM(vec<3, float, Q> const& v) + /// @see GLSL 4.20.8 specification, section 8.4 Floating-Point Pack and Unpack Functions + template + GLM_FUNC_DECL vec<3, T, Q> unpackRGBM(vec<4, T, Q> const& rgbm); + + /// Returns an unsigned integer vector obtained by converting the components of a floating-point vector + /// to the 16-bit floating-point representation found in the OpenGL Specification. + /// The first vector component specifies the 16 least-significant bits of the result; + /// the forth component specifies the 16 most-significant bits. + /// + /// @see gtc_packing + /// @see vec unpackHalf(vec const& p) + /// @see GLSL 4.20.8 specification, section 8.4 Floating-Point Pack and Unpack Functions + template + GLM_FUNC_DECL vec packHalf(vec const& v); + + /// Returns a floating-point vector with components obtained by reinterpreting an integer vector as 16-bit floating-point numbers and converting them to 32-bit floating-point values. + /// The first component of the vector is obtained from the 16 least-significant bits of v; + /// the forth component is obtained from the 16 most-significant bits of v. + /// + /// @see gtc_packing + /// @see vec packHalf(vec const& v) + /// @see GLSL 4.20.8 specification, section 8.4 Floating-Point Pack and Unpack Functions + template + GLM_FUNC_DECL vec unpackHalf(vec const& p); + + /// Convert each component of the normalized floating-point vector into unsigned integer values. + /// + /// @see gtc_packing + /// @see vec unpackUnorm(vec const& p); + template + GLM_FUNC_DECL vec packUnorm(vec const& v); + + /// Convert a packed integer to a normalized floating-point vector. + /// + /// @see gtc_packing + /// @see vec packUnorm(vec const& v) + template + GLM_FUNC_DECL vec unpackUnorm(vec const& v); + + /// Convert each component of the normalized floating-point vector into signed integer values. + /// + /// @see gtc_packing + /// @see vec unpackSnorm(vec const& p); + template + GLM_FUNC_DECL vec packSnorm(vec const& v); + + /// Convert a packed integer to a normalized floating-point vector. + /// + /// @see gtc_packing + /// @see vec packSnorm(vec const& v) + template + GLM_FUNC_DECL vec unpackSnorm(vec const& v); + + /// Convert each component of the normalized floating-point vector into unsigned integer values. + /// + /// @see gtc_packing + /// @see vec2 unpackUnorm2x4(uint8 p) + GLM_FUNC_DECL uint8 packUnorm2x4(vec2 const& v); + + /// Convert a packed integer to a normalized floating-point vector. + /// + /// @see gtc_packing + /// @see uint8 packUnorm2x4(vec2 const& v) + GLM_FUNC_DECL vec2 unpackUnorm2x4(uint8 p); + + /// Convert each component of the normalized floating-point vector into unsigned integer values. + /// + /// @see gtc_packing + /// @see vec4 unpackUnorm4x4(uint16 p) + GLM_FUNC_DECL uint16 packUnorm4x4(vec4 const& v); + + /// Convert a packed integer to a normalized floating-point vector. + /// + /// @see gtc_packing + /// @see uint16 packUnorm4x4(vec4 const& v) + GLM_FUNC_DECL vec4 unpackUnorm4x4(uint16 p); + + /// Convert each component of the normalized floating-point vector into unsigned integer values. + /// + /// @see gtc_packing + /// @see vec3 unpackUnorm1x5_1x6_1x5(uint16 p) + GLM_FUNC_DECL uint16 packUnorm1x5_1x6_1x5(vec3 const& v); + + /// Convert a packed integer to a normalized floating-point vector. + /// + /// @see gtc_packing + /// @see uint16 packUnorm1x5_1x6_1x5(vec3 const& v) + GLM_FUNC_DECL vec3 unpackUnorm1x5_1x6_1x5(uint16 p); + + /// Convert each component of the normalized floating-point vector into unsigned integer values. + /// + /// @see gtc_packing + /// @see vec4 unpackUnorm3x5_1x1(uint16 p) + GLM_FUNC_DECL uint16 packUnorm3x5_1x1(vec4 const& v); + + /// Convert a packed integer to a normalized floating-point vector. + /// + /// @see gtc_packing + /// @see uint16 packUnorm3x5_1x1(vec4 const& v) + GLM_FUNC_DECL vec4 unpackUnorm3x5_1x1(uint16 p); + + /// Convert each component of the normalized floating-point vector into unsigned integer values. + /// + /// @see gtc_packing + /// @see vec3 unpackUnorm2x3_1x2(uint8 p) + GLM_FUNC_DECL uint8 packUnorm2x3_1x2(vec3 const& v); + + /// Convert a packed integer to a normalized floating-point vector. + /// + /// @see gtc_packing + /// @see uint8 packUnorm2x3_1x2(vec3 const& v) + GLM_FUNC_DECL vec3 unpackUnorm2x3_1x2(uint8 p); + + + + /// Convert each component from an integer vector into a packed unsigned integer. + /// + /// @see gtc_packing + /// @see i8vec2 unpackInt2x8(int16 p) + GLM_FUNC_DECL int16 packInt2x8(i8vec2 const& v); + + /// Convert a packed integer into an integer vector. + /// + /// @see gtc_packing + /// @see int16 packInt2x8(i8vec2 const& v) + GLM_FUNC_DECL i8vec2 unpackInt2x8(int16 p); + + /// Convert each component from an integer vector into a packed unsigned integer. + /// + /// @see gtc_packing + /// @see u8vec2 unpackInt2x8(uint16 p) + GLM_FUNC_DECL uint16 packUint2x8(u8vec2 const& v); + + /// Convert a packed integer into an integer vector. + /// + /// @see gtc_packing + /// @see uint16 packInt2x8(u8vec2 const& v) + GLM_FUNC_DECL u8vec2 unpackUint2x8(uint16 p); + + /// Convert each component from an integer vector into a packed unsigned integer. + /// + /// @see gtc_packing + /// @see i8vec4 unpackInt4x8(int32 p) + GLM_FUNC_DECL int32 packInt4x8(i8vec4 const& v); + + /// Convert a packed integer into an integer vector. + /// + /// @see gtc_packing + /// @see int32 packInt2x8(i8vec4 const& v) + GLM_FUNC_DECL i8vec4 unpackInt4x8(int32 p); + + /// Convert each component from an integer vector into a packed unsigned integer. + /// + /// @see gtc_packing + /// @see u8vec4 unpackUint4x8(uint32 p) + GLM_FUNC_DECL uint32 packUint4x8(u8vec4 const& v); + + /// Convert a packed integer into an integer vector. + /// + /// @see gtc_packing + /// @see uint32 packUint4x8(u8vec2 const& v) + GLM_FUNC_DECL u8vec4 unpackUint4x8(uint32 p); + + /// Convert each component from an integer vector into a packed unsigned integer. + /// + /// @see gtc_packing + /// @see i16vec2 unpackInt2x16(int p) + GLM_FUNC_DECL int packInt2x16(i16vec2 const& v); + + /// Convert a packed integer into an integer vector. + /// + /// @see gtc_packing + /// @see int packInt2x16(i16vec2 const& v) + GLM_FUNC_DECL i16vec2 unpackInt2x16(int p); + + /// Convert each component from an integer vector into a packed unsigned integer. + /// + /// @see gtc_packing + /// @see i16vec4 unpackInt4x16(int64 p) + GLM_FUNC_DECL int64 packInt4x16(i16vec4 const& v); + + /// Convert a packed integer into an integer vector. + /// + /// @see gtc_packing + /// @see int64 packInt4x16(i16vec4 const& v) + GLM_FUNC_DECL i16vec4 unpackInt4x16(int64 p); + + /// Convert each component from an integer vector into a packed unsigned integer. + /// + /// @see gtc_packing + /// @see u16vec2 unpackUint2x16(uint p) + GLM_FUNC_DECL uint packUint2x16(u16vec2 const& v); + + /// Convert a packed integer into an integer vector. + /// + /// @see gtc_packing + /// @see uint packUint2x16(u16vec2 const& v) + GLM_FUNC_DECL u16vec2 unpackUint2x16(uint p); + + /// Convert each component from an integer vector into a packed unsigned integer. + /// + /// @see gtc_packing + /// @see u16vec4 unpackUint4x16(uint64 p) + GLM_FUNC_DECL uint64 packUint4x16(u16vec4 const& v); + + /// Convert a packed integer into an integer vector. + /// + /// @see gtc_packing + /// @see uint64 packUint4x16(u16vec4 const& v) + GLM_FUNC_DECL u16vec4 unpackUint4x16(uint64 p); + + /// Convert each component from an integer vector into a packed unsigned integer. + /// + /// @see gtc_packing + /// @see i32vec2 unpackInt2x32(int p) + GLM_FUNC_DECL int64 packInt2x32(i32vec2 const& v); + + /// Convert a packed integer into an integer vector. + /// + /// @see gtc_packing + /// @see int packInt2x16(i32vec2 const& v) + GLM_FUNC_DECL i32vec2 unpackInt2x32(int64 p); + + /// Convert each component from an integer vector into a packed unsigned integer. + /// + /// @see gtc_packing + /// @see u32vec2 unpackUint2x32(int p) + GLM_FUNC_DECL uint64 packUint2x32(u32vec2 const& v); + + /// Convert a packed integer into an integer vector. + /// + /// @see gtc_packing + /// @see int packUint2x16(u32vec2 const& v) + GLM_FUNC_DECL u32vec2 unpackUint2x32(uint64 p); + + + /// @} +}// namespace glm + +#include "packing.inl" diff --git a/src/GLMath/glm/gtc/packing.inl b/src/GLMath/glm/gtc/packing.inl new file mode 100644 index 000000000..8c906e16c --- /dev/null +++ b/src/GLMath/glm/gtc/packing.inl @@ -0,0 +1,938 @@ +/// @ref gtc_packing + +#include "../ext/scalar_relational.hpp" +#include "../ext/vector_relational.hpp" +#include "../common.hpp" +#include "../vec2.hpp" +#include "../vec3.hpp" +#include "../vec4.hpp" +#include "../detail/type_half.hpp" +#include +#include + +namespace glm{ +namespace detail +{ + GLM_FUNC_QUALIFIER glm::uint16 float2half(glm::uint32 f) + { + // 10 bits => EE EEEFFFFF + // 11 bits => EEE EEFFFFFF + // Half bits => SEEEEEFF FFFFFFFF + // Float bits => SEEEEEEE EFFFFFFF FFFFFFFF FFFFFFFF + + // 0x00007c00 => 00000000 00000000 01111100 00000000 + // 0x000003ff => 00000000 00000000 00000011 11111111 + // 0x38000000 => 00111000 00000000 00000000 00000000 + // 0x7f800000 => 01111111 10000000 00000000 00000000 + // 0x00008000 => 00000000 00000000 10000000 00000000 + return + ((f >> 16) & 0x8000) | // sign + ((((f & 0x7f800000) - 0x38000000) >> 13) & 0x7c00) | // exponential + ((f >> 13) & 0x03ff); // Mantissa + } + + GLM_FUNC_QUALIFIER glm::uint32 float2packed11(glm::uint32 f) + { + // 10 bits => EE EEEFFFFF + // 11 bits => EEE EEFFFFFF + // Half bits => SEEEEEFF FFFFFFFF + // Float bits => SEEEEEEE EFFFFFFF FFFFFFFF FFFFFFFF + + // 0x000007c0 => 00000000 00000000 00000111 11000000 + // 0x00007c00 => 00000000 00000000 01111100 00000000 + // 0x000003ff => 00000000 00000000 00000011 11111111 + // 0x38000000 => 00111000 00000000 00000000 00000000 + // 0x7f800000 => 01111111 10000000 00000000 00000000 + // 0x00008000 => 00000000 00000000 10000000 00000000 + return + ((((f & 0x7f800000) - 0x38000000) >> 17) & 0x07c0) | // exponential + ((f >> 17) & 0x003f); // Mantissa + } + + GLM_FUNC_QUALIFIER glm::uint32 packed11ToFloat(glm::uint32 p) + { + // 10 bits => EE EEEFFFFF + // 11 bits => EEE EEFFFFFF + // Half bits => SEEEEEFF FFFFFFFF + // Float bits => SEEEEEEE EFFFFFFF FFFFFFFF FFFFFFFF + + // 0x000007c0 => 00000000 00000000 00000111 11000000 + // 0x00007c00 => 00000000 00000000 01111100 00000000 + // 0x000003ff => 00000000 00000000 00000011 11111111 + // 0x38000000 => 00111000 00000000 00000000 00000000 + // 0x7f800000 => 01111111 10000000 00000000 00000000 + // 0x00008000 => 00000000 00000000 10000000 00000000 + return + ((((p & 0x07c0) << 17) + 0x38000000) & 0x7f800000) | // exponential + ((p & 0x003f) << 17); // Mantissa + } + + GLM_FUNC_QUALIFIER glm::uint32 float2packed10(glm::uint32 f) + { + // 10 bits => EE EEEFFFFF + // 11 bits => EEE EEFFFFFF + // Half bits => SEEEEEFF FFFFFFFF + // Float bits => SEEEEEEE EFFFFFFF FFFFFFFF FFFFFFFF + + // 0x0000001F => 00000000 00000000 00000000 00011111 + // 0x0000003F => 00000000 00000000 00000000 00111111 + // 0x000003E0 => 00000000 00000000 00000011 11100000 + // 0x000007C0 => 00000000 00000000 00000111 11000000 + // 0x00007C00 => 00000000 00000000 01111100 00000000 + // 0x000003FF => 00000000 00000000 00000011 11111111 + // 0x38000000 => 00111000 00000000 00000000 00000000 + // 0x7f800000 => 01111111 10000000 00000000 00000000 + // 0x00008000 => 00000000 00000000 10000000 00000000 + return + ((((f & 0x7f800000) - 0x38000000) >> 18) & 0x03E0) | // exponential + ((f >> 18) & 0x001f); // Mantissa + } + + GLM_FUNC_QUALIFIER glm::uint32 packed10ToFloat(glm::uint32 p) + { + // 10 bits => EE EEEFFFFF + // 11 bits => EEE EEFFFFFF + // Half bits => SEEEEEFF FFFFFFFF + // Float bits => SEEEEEEE EFFFFFFF FFFFFFFF FFFFFFFF + + // 0x0000001F => 00000000 00000000 00000000 00011111 + // 0x0000003F => 00000000 00000000 00000000 00111111 + // 0x000003E0 => 00000000 00000000 00000011 11100000 + // 0x000007C0 => 00000000 00000000 00000111 11000000 + // 0x00007C00 => 00000000 00000000 01111100 00000000 + // 0x000003FF => 00000000 00000000 00000011 11111111 + // 0x38000000 => 00111000 00000000 00000000 00000000 + // 0x7f800000 => 01111111 10000000 00000000 00000000 + // 0x00008000 => 00000000 00000000 10000000 00000000 + return + ((((p & 0x03E0) << 18) + 0x38000000) & 0x7f800000) | // exponential + ((p & 0x001f) << 18); // Mantissa + } + + GLM_FUNC_QUALIFIER glm::uint half2float(glm::uint h) + { + return ((h & 0x8000) << 16) | ((( h & 0x7c00) + 0x1C000) << 13) | ((h & 0x03FF) << 13); + } + + GLM_FUNC_QUALIFIER glm::uint floatTo11bit(float x) + { + if(x == 0.0f) + return 0u; + else if(glm::isnan(x)) + return ~0u; + else if(glm::isinf(x)) + return 0x1Fu << 6u; + + uint Pack = 0u; + memcpy(&Pack, &x, sizeof(Pack)); + return float2packed11(Pack); + } + + GLM_FUNC_QUALIFIER float packed11bitToFloat(glm::uint x) + { + if(x == 0) + return 0.0f; + else if(x == ((1 << 11) - 1)) + return ~0;//NaN + else if(x == (0x1f << 6)) + return ~0;//Inf + + uint Result = packed11ToFloat(x); + + float Temp = 0; + memcpy(&Temp, &Result, sizeof(Temp)); + return Temp; + } + + GLM_FUNC_QUALIFIER glm::uint floatTo10bit(float x) + { + if(x == 0.0f) + return 0u; + else if(glm::isnan(x)) + return ~0u; + else if(glm::isinf(x)) + return 0x1Fu << 5u; + + uint Pack = 0; + memcpy(&Pack, &x, sizeof(Pack)); + return float2packed10(Pack); + } + + GLM_FUNC_QUALIFIER float packed10bitToFloat(glm::uint x) + { + if(x == 0) + return 0.0f; + else if(x == ((1 << 10) - 1)) + return ~0;//NaN + else if(x == (0x1f << 5)) + return ~0;//Inf + + uint Result = packed10ToFloat(x); + + float Temp = 0; + memcpy(&Temp, &Result, sizeof(Temp)); + return Temp; + } + +// GLM_FUNC_QUALIFIER glm::uint f11_f11_f10(float x, float y, float z) +// { +// return ((floatTo11bit(x) & ((1 << 11) - 1)) << 0) | ((floatTo11bit(y) & ((1 << 11) - 1)) << 11) | ((floatTo10bit(z) & ((1 << 10) - 1)) << 22); +// } + + union u3u3u2 + { + struct + { + uint x : 3; + uint y : 3; + uint z : 2; + } data; + uint8 pack; + }; + + union u4u4 + { + struct + { + uint x : 4; + uint y : 4; + } data; + uint8 pack; + }; + + union u4u4u4u4 + { + struct + { + uint x : 4; + uint y : 4; + uint z : 4; + uint w : 4; + } data; + uint16 pack; + }; + + union u5u6u5 + { + struct + { + uint x : 5; + uint y : 6; + uint z : 5; + } data; + uint16 pack; + }; + + union u5u5u5u1 + { + struct + { + uint x : 5; + uint y : 5; + uint z : 5; + uint w : 1; + } data; + uint16 pack; + }; + + union u10u10u10u2 + { + struct + { + uint x : 10; + uint y : 10; + uint z : 10; + uint w : 2; + } data; + uint32 pack; + }; + + union i10i10i10i2 + { + struct + { + int x : 10; + int y : 10; + int z : 10; + int w : 2; + } data; + uint32 pack; + }; + + union u9u9u9e5 + { + struct + { + uint x : 9; + uint y : 9; + uint z : 9; + uint w : 5; + } data; + uint32 pack; + }; + + template + struct compute_half + {}; + + template + struct compute_half<1, Q> + { + GLM_FUNC_QUALIFIER static vec<1, uint16, Q> pack(vec<1, float, Q> const& v) + { + int16 const Unpack(detail::toFloat16(v.x)); + u16vec1 Packed; + memcpy(&Packed, &Unpack, sizeof(Packed)); + return Packed; + } + + GLM_FUNC_QUALIFIER static vec<1, float, Q> unpack(vec<1, uint16, Q> const& v) + { + i16vec1 Unpack; + memcpy(&Unpack, &v, sizeof(Unpack)); + return vec<1, float, Q>(detail::toFloat32(v.x)); + } + }; + + template + struct compute_half<2, Q> + { + GLM_FUNC_QUALIFIER static vec<2, uint16, Q> pack(vec<2, float, Q> const& v) + { + vec<2, int16, Q> const Unpack(detail::toFloat16(v.x), detail::toFloat16(v.y)); + u16vec2 Packed; + memcpy(&Packed, &Unpack, sizeof(Packed)); + return Packed; + } + + GLM_FUNC_QUALIFIER static vec<2, float, Q> unpack(vec<2, uint16, Q> const& v) + { + i16vec2 Unpack; + memcpy(&Unpack, &v, sizeof(Unpack)); + return vec<2, float, Q>(detail::toFloat32(v.x), detail::toFloat32(v.y)); + } + }; + + template + struct compute_half<3, Q> + { + GLM_FUNC_QUALIFIER static vec<3, uint16, Q> pack(vec<3, float, Q> const& v) + { + vec<3, int16, Q> const Unpack(detail::toFloat16(v.x), detail::toFloat16(v.y), detail::toFloat16(v.z)); + u16vec3 Packed; + memcpy(&Packed, &Unpack, sizeof(Packed)); + return Packed; + } + + GLM_FUNC_QUALIFIER static vec<3, float, Q> unpack(vec<3, uint16, Q> const& v) + { + i16vec3 Unpack; + memcpy(&Unpack, &v, sizeof(Unpack)); + return vec<3, float, Q>(detail::toFloat32(v.x), detail::toFloat32(v.y), detail::toFloat32(v.z)); + } + }; + + template + struct compute_half<4, Q> + { + GLM_FUNC_QUALIFIER static vec<4, uint16, Q> pack(vec<4, float, Q> const& v) + { + vec<4, int16, Q> const Unpack(detail::toFloat16(v.x), detail::toFloat16(v.y), detail::toFloat16(v.z), detail::toFloat16(v.w)); + u16vec4 Packed; + memcpy(&Packed, &Unpack, sizeof(Packed)); + return Packed; + } + + GLM_FUNC_QUALIFIER static vec<4, float, Q> unpack(vec<4, uint16, Q> const& v) + { + i16vec4 Unpack; + memcpy(&Unpack, &v, sizeof(Unpack)); + return vec<4, float, Q>(detail::toFloat32(v.x), detail::toFloat32(v.y), detail::toFloat32(v.z), detail::toFloat32(v.w)); + } + }; +}//namespace detail + + GLM_FUNC_QUALIFIER uint8 packUnorm1x8(float v) + { + return static_cast(round(clamp(v, 0.0f, 1.0f) * 255.0f)); + } + + GLM_FUNC_QUALIFIER float unpackUnorm1x8(uint8 p) + { + float const Unpack(p); + return Unpack * static_cast(0.0039215686274509803921568627451); // 1 / 255 + } + + GLM_FUNC_QUALIFIER uint16 packUnorm2x8(vec2 const& v) + { + u8vec2 const Topack(round(clamp(v, 0.0f, 1.0f) * 255.0f)); + + uint16 Unpack = 0; + memcpy(&Unpack, &Topack, sizeof(Unpack)); + return Unpack; + } + + GLM_FUNC_QUALIFIER vec2 unpackUnorm2x8(uint16 p) + { + u8vec2 Unpack; + memcpy(&Unpack, &p, sizeof(Unpack)); + return vec2(Unpack) * float(0.0039215686274509803921568627451); // 1 / 255 + } + + GLM_FUNC_QUALIFIER uint8 packSnorm1x8(float v) + { + int8 const Topack(static_cast(round(clamp(v ,-1.0f, 1.0f) * 127.0f))); + uint8 Packed = 0; + memcpy(&Packed, &Topack, sizeof(Packed)); + return Packed; + } + + GLM_FUNC_QUALIFIER float unpackSnorm1x8(uint8 p) + { + int8 Unpack = 0; + memcpy(&Unpack, &p, sizeof(Unpack)); + return clamp( + static_cast(Unpack) * 0.00787401574803149606299212598425f, // 1.0f / 127.0f + -1.0f, 1.0f); + } + + GLM_FUNC_QUALIFIER uint16 packSnorm2x8(vec2 const& v) + { + i8vec2 const Topack(round(clamp(v, -1.0f, 1.0f) * 127.0f)); + uint16 Packed = 0; + memcpy(&Packed, &Topack, sizeof(Packed)); + return Packed; + } + + GLM_FUNC_QUALIFIER vec2 unpackSnorm2x8(uint16 p) + { + i8vec2 Unpack; + memcpy(&Unpack, &p, sizeof(Unpack)); + return clamp( + vec2(Unpack) * 0.00787401574803149606299212598425f, // 1.0f / 127.0f + -1.0f, 1.0f); + } + + GLM_FUNC_QUALIFIER uint16 packUnorm1x16(float s) + { + return static_cast(round(clamp(s, 0.0f, 1.0f) * 65535.0f)); + } + + GLM_FUNC_QUALIFIER float unpackUnorm1x16(uint16 p) + { + float const Unpack(p); + return Unpack * 1.5259021896696421759365224689097e-5f; // 1.0 / 65535.0 + } + + GLM_FUNC_QUALIFIER uint64 packUnorm4x16(vec4 const& v) + { + u16vec4 const Topack(round(clamp(v , 0.0f, 1.0f) * 65535.0f)); + uint64 Packed = 0; + memcpy(&Packed, &Topack, sizeof(Packed)); + return Packed; + } + + GLM_FUNC_QUALIFIER vec4 unpackUnorm4x16(uint64 p) + { + u16vec4 Unpack; + memcpy(&Unpack, &p, sizeof(Unpack)); + return vec4(Unpack) * 1.5259021896696421759365224689097e-5f; // 1.0 / 65535.0 + } + + GLM_FUNC_QUALIFIER uint16 packSnorm1x16(float v) + { + int16 const Topack = static_cast(round(clamp(v ,-1.0f, 1.0f) * 32767.0f)); + uint16 Packed = 0; + memcpy(&Packed, &Topack, sizeof(Packed)); + return Packed; + } + + GLM_FUNC_QUALIFIER float unpackSnorm1x16(uint16 p) + { + int16 Unpack = 0; + memcpy(&Unpack, &p, sizeof(Unpack)); + return clamp( + static_cast(Unpack) * 3.0518509475997192297128208258309e-5f, //1.0f / 32767.0f, + -1.0f, 1.0f); + } + + GLM_FUNC_QUALIFIER uint64 packSnorm4x16(vec4 const& v) + { + i16vec4 const Topack(round(clamp(v ,-1.0f, 1.0f) * 32767.0f)); + uint64 Packed = 0; + memcpy(&Packed, &Topack, sizeof(Packed)); + return Packed; + } + + GLM_FUNC_QUALIFIER vec4 unpackSnorm4x16(uint64 p) + { + i16vec4 Unpack; + memcpy(&Unpack, &p, sizeof(Unpack)); + return clamp( + vec4(Unpack) * 3.0518509475997192297128208258309e-5f, //1.0f / 32767.0f, + -1.0f, 1.0f); + } + + GLM_FUNC_QUALIFIER uint16 packHalf1x16(float v) + { + int16 const Topack(detail::toFloat16(v)); + uint16 Packed = 0; + memcpy(&Packed, &Topack, sizeof(Packed)); + return Packed; + } + + GLM_FUNC_QUALIFIER float unpackHalf1x16(uint16 v) + { + int16 Unpack = 0; + memcpy(&Unpack, &v, sizeof(Unpack)); + return detail::toFloat32(Unpack); + } + + GLM_FUNC_QUALIFIER uint64 packHalf4x16(glm::vec4 const& v) + { + i16vec4 const Unpack( + detail::toFloat16(v.x), + detail::toFloat16(v.y), + detail::toFloat16(v.z), + detail::toFloat16(v.w)); + uint64 Packed = 0; + memcpy(&Packed, &Unpack, sizeof(Packed)); + return Packed; + } + + GLM_FUNC_QUALIFIER glm::vec4 unpackHalf4x16(uint64 v) + { + i16vec4 Unpack; + memcpy(&Unpack, &v, sizeof(Unpack)); + return vec4( + detail::toFloat32(Unpack.x), + detail::toFloat32(Unpack.y), + detail::toFloat32(Unpack.z), + detail::toFloat32(Unpack.w)); + } + + GLM_FUNC_QUALIFIER uint32 packI3x10_1x2(ivec4 const& v) + { + detail::i10i10i10i2 Result; + Result.data.x = v.x; + Result.data.y = v.y; + Result.data.z = v.z; + Result.data.w = v.w; + return Result.pack; + } + + GLM_FUNC_QUALIFIER ivec4 unpackI3x10_1x2(uint32 v) + { + detail::i10i10i10i2 Unpack; + Unpack.pack = v; + return ivec4( + Unpack.data.x, + Unpack.data.y, + Unpack.data.z, + Unpack.data.w); + } + + GLM_FUNC_QUALIFIER uint32 packU3x10_1x2(uvec4 const& v) + { + detail::u10u10u10u2 Result; + Result.data.x = v.x; + Result.data.y = v.y; + Result.data.z = v.z; + Result.data.w = v.w; + return Result.pack; + } + + GLM_FUNC_QUALIFIER uvec4 unpackU3x10_1x2(uint32 v) + { + detail::u10u10u10u2 Unpack; + Unpack.pack = v; + return uvec4( + Unpack.data.x, + Unpack.data.y, + Unpack.data.z, + Unpack.data.w); + } + + GLM_FUNC_QUALIFIER uint32 packSnorm3x10_1x2(vec4 const& v) + { + ivec4 const Pack(round(clamp(v,-1.0f, 1.0f) * vec4(511.f, 511.f, 511.f, 1.f))); + + detail::i10i10i10i2 Result; + Result.data.x = Pack.x; + Result.data.y = Pack.y; + Result.data.z = Pack.z; + Result.data.w = Pack.w; + return Result.pack; + } + + GLM_FUNC_QUALIFIER vec4 unpackSnorm3x10_1x2(uint32 v) + { + detail::i10i10i10i2 Unpack; + Unpack.pack = v; + + vec4 const Result(Unpack.data.x, Unpack.data.y, Unpack.data.z, Unpack.data.w); + + return clamp(Result * vec4(1.f / 511.f, 1.f / 511.f, 1.f / 511.f, 1.f), -1.0f, 1.0f); + } + + GLM_FUNC_QUALIFIER uint32 packUnorm3x10_1x2(vec4 const& v) + { + uvec4 const Unpack(round(clamp(v, 0.0f, 1.0f) * vec4(1023.f, 1023.f, 1023.f, 3.f))); + + detail::u10u10u10u2 Result; + Result.data.x = Unpack.x; + Result.data.y = Unpack.y; + Result.data.z = Unpack.z; + Result.data.w = Unpack.w; + return Result.pack; + } + + GLM_FUNC_QUALIFIER vec4 unpackUnorm3x10_1x2(uint32 v) + { + vec4 const ScaleFactors(1.0f / 1023.f, 1.0f / 1023.f, 1.0f / 1023.f, 1.0f / 3.f); + + detail::u10u10u10u2 Unpack; + Unpack.pack = v; + return vec4(Unpack.data.x, Unpack.data.y, Unpack.data.z, Unpack.data.w) * ScaleFactors; + } + + GLM_FUNC_QUALIFIER uint32 packF2x11_1x10(vec3 const& v) + { + return + ((detail::floatTo11bit(v.x) & ((1 << 11) - 1)) << 0) | + ((detail::floatTo11bit(v.y) & ((1 << 11) - 1)) << 11) | + ((detail::floatTo10bit(v.z) & ((1 << 10) - 1)) << 22); + } + + GLM_FUNC_QUALIFIER vec3 unpackF2x11_1x10(uint32 v) + { + return vec3( + detail::packed11bitToFloat(v >> 0), + detail::packed11bitToFloat(v >> 11), + detail::packed10bitToFloat(v >> 22)); + } + + GLM_FUNC_QUALIFIER uint32 packF3x9_E1x5(vec3 const& v) + { + float const SharedExpMax = (pow(2.0f, 9.0f - 1.0f) / pow(2.0f, 9.0f)) * pow(2.0f, 31.f - 15.f); + vec3 const Color = clamp(v, 0.0f, SharedExpMax); + float const MaxColor = max(Color.x, max(Color.y, Color.z)); + + float const ExpSharedP = max(-15.f - 1.f, floor(log2(MaxColor))) + 1.0f + 15.f; + float const MaxShared = floor(MaxColor / pow(2.0f, (ExpSharedP - 15.f - 9.f)) + 0.5f); + float const ExpShared = equal(MaxShared, pow(2.0f, 9.0f), epsilon()) ? ExpSharedP + 1.0f : ExpSharedP; + + uvec3 const ColorComp(floor(Color / pow(2.f, (ExpShared - 15.f - 9.f)) + 0.5f)); + + detail::u9u9u9e5 Unpack; + Unpack.data.x = ColorComp.x; + Unpack.data.y = ColorComp.y; + Unpack.data.z = ColorComp.z; + Unpack.data.w = uint(ExpShared); + return Unpack.pack; + } + + GLM_FUNC_QUALIFIER vec3 unpackF3x9_E1x5(uint32 v) + { + detail::u9u9u9e5 Unpack; + Unpack.pack = v; + + return vec3(Unpack.data.x, Unpack.data.y, Unpack.data.z) * pow(2.0f, Unpack.data.w - 15.f - 9.f); + } + + // Based on Brian Karis http://graphicrants.blogspot.fr/2009/04/rgbm-color-encoding.html + template + GLM_FUNC_QUALIFIER vec<4, T, Q> packRGBM(vec<3, T, Q> const& rgb) + { + vec<3, T, Q> const Color(rgb * static_cast(1.0 / 6.0)); + T Alpha = clamp(max(max(Color.x, Color.y), max(Color.z, static_cast(1e-6))), static_cast(0), static_cast(1)); + Alpha = ceil(Alpha * static_cast(255.0)) / static_cast(255.0); + return vec<4, T, Q>(Color / Alpha, Alpha); + } + + template + GLM_FUNC_QUALIFIER vec<3, T, Q> unpackRGBM(vec<4, T, Q> const& rgbm) + { + return vec<3, T, Q>(rgbm.x, rgbm.y, rgbm.z) * rgbm.w * static_cast(6); + } + + template + GLM_FUNC_QUALIFIER vec packHalf(vec const& v) + { + return detail::compute_half::pack(v); + } + + template + GLM_FUNC_QUALIFIER vec unpackHalf(vec const& v) + { + return detail::compute_half::unpack(v); + } + + template + GLM_FUNC_QUALIFIER vec packUnorm(vec const& v) + { + GLM_STATIC_ASSERT(std::numeric_limits::is_integer, "uintType must be an integer type"); + GLM_STATIC_ASSERT(std::numeric_limits::is_iec559, "floatType must be a floating point type"); + + return vec(round(clamp(v, static_cast(0), static_cast(1)) * static_cast(std::numeric_limits::max()))); + } + + template + GLM_FUNC_QUALIFIER vec unpackUnorm(vec const& v) + { + GLM_STATIC_ASSERT(std::numeric_limits::is_integer, "uintType must be an integer type"); + GLM_STATIC_ASSERT(std::numeric_limits::is_iec559, "floatType must be a floating point type"); + + return vec(v) * (static_cast(1) / static_cast(std::numeric_limits::max())); + } + + template + GLM_FUNC_QUALIFIER vec packSnorm(vec const& v) + { + GLM_STATIC_ASSERT(std::numeric_limits::is_integer, "uintType must be an integer type"); + GLM_STATIC_ASSERT(std::numeric_limits::is_iec559, "floatType must be a floating point type"); + + return vec(round(clamp(v , static_cast(-1), static_cast(1)) * static_cast(std::numeric_limits::max()))); + } + + template + GLM_FUNC_QUALIFIER vec unpackSnorm(vec const& v) + { + GLM_STATIC_ASSERT(std::numeric_limits::is_integer, "uintType must be an integer type"); + GLM_STATIC_ASSERT(std::numeric_limits::is_iec559, "floatType must be a floating point type"); + + return clamp(vec(v) * (static_cast(1) / static_cast(std::numeric_limits::max())), static_cast(-1), static_cast(1)); + } + + GLM_FUNC_QUALIFIER uint8 packUnorm2x4(vec2 const& v) + { + u32vec2 const Unpack(round(clamp(v, 0.0f, 1.0f) * 15.0f)); + detail::u4u4 Result; + Result.data.x = Unpack.x; + Result.data.y = Unpack.y; + return Result.pack; + } + + GLM_FUNC_QUALIFIER vec2 unpackUnorm2x4(uint8 v) + { + float const ScaleFactor(1.f / 15.f); + detail::u4u4 Unpack; + Unpack.pack = v; + return vec2(Unpack.data.x, Unpack.data.y) * ScaleFactor; + } + + GLM_FUNC_QUALIFIER uint16 packUnorm4x4(vec4 const& v) + { + u32vec4 const Unpack(round(clamp(v, 0.0f, 1.0f) * 15.0f)); + detail::u4u4u4u4 Result; + Result.data.x = Unpack.x; + Result.data.y = Unpack.y; + Result.data.z = Unpack.z; + Result.data.w = Unpack.w; + return Result.pack; + } + + GLM_FUNC_QUALIFIER vec4 unpackUnorm4x4(uint16 v) + { + float const ScaleFactor(1.f / 15.f); + detail::u4u4u4u4 Unpack; + Unpack.pack = v; + return vec4(Unpack.data.x, Unpack.data.y, Unpack.data.z, Unpack.data.w) * ScaleFactor; + } + + GLM_FUNC_QUALIFIER uint16 packUnorm1x5_1x6_1x5(vec3 const& v) + { + u32vec3 const Unpack(round(clamp(v, 0.0f, 1.0f) * vec3(31.f, 63.f, 31.f))); + detail::u5u6u5 Result; + Result.data.x = Unpack.x; + Result.data.y = Unpack.y; + Result.data.z = Unpack.z; + return Result.pack; + } + + GLM_FUNC_QUALIFIER vec3 unpackUnorm1x5_1x6_1x5(uint16 v) + { + vec3 const ScaleFactor(1.f / 31.f, 1.f / 63.f, 1.f / 31.f); + detail::u5u6u5 Unpack; + Unpack.pack = v; + return vec3(Unpack.data.x, Unpack.data.y, Unpack.data.z) * ScaleFactor; + } + + GLM_FUNC_QUALIFIER uint16 packUnorm3x5_1x1(vec4 const& v) + { + u32vec4 const Unpack(round(clamp(v, 0.0f, 1.0f) * vec4(31.f, 31.f, 31.f, 1.f))); + detail::u5u5u5u1 Result; + Result.data.x = Unpack.x; + Result.data.y = Unpack.y; + Result.data.z = Unpack.z; + Result.data.w = Unpack.w; + return Result.pack; + } + + GLM_FUNC_QUALIFIER vec4 unpackUnorm3x5_1x1(uint16 v) + { + vec4 const ScaleFactor(1.f / 31.f, 1.f / 31.f, 1.f / 31.f, 1.f); + detail::u5u5u5u1 Unpack; + Unpack.pack = v; + return vec4(Unpack.data.x, Unpack.data.y, Unpack.data.z, Unpack.data.w) * ScaleFactor; + } + + GLM_FUNC_QUALIFIER uint8 packUnorm2x3_1x2(vec3 const& v) + { + u32vec3 const Unpack(round(clamp(v, 0.0f, 1.0f) * vec3(7.f, 7.f, 3.f))); + detail::u3u3u2 Result; + Result.data.x = Unpack.x; + Result.data.y = Unpack.y; + Result.data.z = Unpack.z; + return Result.pack; + } + + GLM_FUNC_QUALIFIER vec3 unpackUnorm2x3_1x2(uint8 v) + { + vec3 const ScaleFactor(1.f / 7.f, 1.f / 7.f, 1.f / 3.f); + detail::u3u3u2 Unpack; + Unpack.pack = v; + return vec3(Unpack.data.x, Unpack.data.y, Unpack.data.z) * ScaleFactor; + } + + GLM_FUNC_QUALIFIER int16 packInt2x8(i8vec2 const& v) + { + int16 Pack = 0; + memcpy(&Pack, &v, sizeof(Pack)); + return Pack; + } + + GLM_FUNC_QUALIFIER i8vec2 unpackInt2x8(int16 p) + { + i8vec2 Unpack; + memcpy(&Unpack, &p, sizeof(Unpack)); + return Unpack; + } + + GLM_FUNC_QUALIFIER uint16 packUint2x8(u8vec2 const& v) + { + uint16 Pack = 0; + memcpy(&Pack, &v, sizeof(Pack)); + return Pack; + } + + GLM_FUNC_QUALIFIER u8vec2 unpackUint2x8(uint16 p) + { + u8vec2 Unpack; + memcpy(&Unpack, &p, sizeof(Unpack)); + return Unpack; + } + + GLM_FUNC_QUALIFIER int32 packInt4x8(i8vec4 const& v) + { + int32 Pack = 0; + memcpy(&Pack, &v, sizeof(Pack)); + return Pack; + } + + GLM_FUNC_QUALIFIER i8vec4 unpackInt4x8(int32 p) + { + i8vec4 Unpack; + memcpy(&Unpack, &p, sizeof(Unpack)); + return Unpack; + } + + GLM_FUNC_QUALIFIER uint32 packUint4x8(u8vec4 const& v) + { + uint32 Pack = 0; + memcpy(&Pack, &v, sizeof(Pack)); + return Pack; + } + + GLM_FUNC_QUALIFIER u8vec4 unpackUint4x8(uint32 p) + { + u8vec4 Unpack; + memcpy(&Unpack, &p, sizeof(Unpack)); + return Unpack; + } + + GLM_FUNC_QUALIFIER int packInt2x16(i16vec2 const& v) + { + int Pack = 0; + memcpy(&Pack, &v, sizeof(Pack)); + return Pack; + } + + GLM_FUNC_QUALIFIER i16vec2 unpackInt2x16(int p) + { + i16vec2 Unpack; + memcpy(&Unpack, &p, sizeof(Unpack)); + return Unpack; + } + + GLM_FUNC_QUALIFIER int64 packInt4x16(i16vec4 const& v) + { + int64 Pack = 0; + memcpy(&Pack, &v, sizeof(Pack)); + return Pack; + } + + GLM_FUNC_QUALIFIER i16vec4 unpackInt4x16(int64 p) + { + i16vec4 Unpack; + memcpy(&Unpack, &p, sizeof(Unpack)); + return Unpack; + } + + GLM_FUNC_QUALIFIER uint packUint2x16(u16vec2 const& v) + { + uint Pack = 0; + memcpy(&Pack, &v, sizeof(Pack)); + return Pack; + } + + GLM_FUNC_QUALIFIER u16vec2 unpackUint2x16(uint p) + { + u16vec2 Unpack; + memcpy(&Unpack, &p, sizeof(Unpack)); + return Unpack; + } + + GLM_FUNC_QUALIFIER uint64 packUint4x16(u16vec4 const& v) + { + uint64 Pack = 0; + memcpy(&Pack, &v, sizeof(Pack)); + return Pack; + } + + GLM_FUNC_QUALIFIER u16vec4 unpackUint4x16(uint64 p) + { + u16vec4 Unpack; + memcpy(&Unpack, &p, sizeof(Unpack)); + return Unpack; + } + + GLM_FUNC_QUALIFIER int64 packInt2x32(i32vec2 const& v) + { + int64 Pack = 0; + memcpy(&Pack, &v, sizeof(Pack)); + return Pack; + } + + GLM_FUNC_QUALIFIER i32vec2 unpackInt2x32(int64 p) + { + i32vec2 Unpack; + memcpy(&Unpack, &p, sizeof(Unpack)); + return Unpack; + } + + GLM_FUNC_QUALIFIER uint64 packUint2x32(u32vec2 const& v) + { + uint64 Pack = 0; + memcpy(&Pack, &v, sizeof(Pack)); + return Pack; + } + + GLM_FUNC_QUALIFIER u32vec2 unpackUint2x32(uint64 p) + { + u32vec2 Unpack; + memcpy(&Unpack, &p, sizeof(Unpack)); + return Unpack; + } +}//namespace glm + diff --git a/src/GLMath/glm/gtc/quaternion.hpp b/src/GLMath/glm/gtc/quaternion.hpp new file mode 100644 index 000000000..359e072b9 --- /dev/null +++ b/src/GLMath/glm/gtc/quaternion.hpp @@ -0,0 +1,173 @@ +/// @ref gtc_quaternion +/// @file glm/gtc/quaternion.hpp +/// +/// @see core (dependence) +/// @see gtc_constants (dependence) +/// +/// @defgroup gtc_quaternion GLM_GTC_quaternion +/// @ingroup gtc +/// +/// Include to use the features of this extension. +/// +/// Defines a templated quaternion type and several quaternion operations. + +#pragma once + +// Dependency: +#include "../gtc/constants.hpp" +#include "../gtc/matrix_transform.hpp" +#include "../ext/vector_relational.hpp" +#include "../ext/quaternion_common.hpp" +#include "../ext/quaternion_float.hpp" +#include "../ext/quaternion_float_precision.hpp" +#include "../ext/quaternion_double.hpp" +#include "../ext/quaternion_double_precision.hpp" +#include "../ext/quaternion_relational.hpp" +#include "../ext/quaternion_geometric.hpp" +#include "../ext/quaternion_trigonometric.hpp" +#include "../ext/quaternion_transform.hpp" +#include "../detail/type_mat3x3.hpp" +#include "../detail/type_mat4x4.hpp" +#include "../detail/type_vec3.hpp" +#include "../detail/type_vec4.hpp" + +#if GLM_MESSAGES == GLM_ENABLE && !defined(GLM_EXT_INCLUDED) +# pragma message("GLM: GLM_GTC_quaternion extension included") +#endif + +namespace glm +{ + /// @addtogroup gtc_quaternion + /// @{ + + /// Returns euler angles, pitch as x, yaw as y, roll as z. + /// The result is expressed in radians. + /// + /// @tparam T Floating-point scalar types. + /// + /// @see gtc_quaternion + template + GLM_FUNC_DECL vec<3, T, Q> eulerAngles(qua const& x); + + /// Returns roll value of euler angles expressed in radians. + /// + /// @tparam T Floating-point scalar types. + /// + /// @see gtc_quaternion + template + GLM_FUNC_DECL T roll(qua const& x); + + /// Returns pitch value of euler angles expressed in radians. + /// + /// @tparam T Floating-point scalar types. + /// + /// @see gtc_quaternion + template + GLM_FUNC_DECL T pitch(qua const& x); + + /// Returns yaw value of euler angles expressed in radians. + /// + /// @tparam T Floating-point scalar types. + /// + /// @see gtc_quaternion + template + GLM_FUNC_DECL T yaw(qua const& x); + + /// Converts a quaternion to a 3 * 3 matrix. + /// + /// @tparam T Floating-point scalar types. + /// + /// @see gtc_quaternion + template + GLM_FUNC_DECL mat<3, 3, T, Q> mat3_cast(qua const& x); + + /// Converts a quaternion to a 4 * 4 matrix. + /// + /// @tparam T Floating-point scalar types. + /// + /// @see gtc_quaternion + template + GLM_FUNC_DECL mat<4, 4, T, Q> mat4_cast(qua const& x); + + /// Converts a pure rotation 3 * 3 matrix to a quaternion. + /// + /// @tparam T Floating-point scalar types. + /// + /// @see gtc_quaternion + template + GLM_FUNC_DECL qua quat_cast(mat<3, 3, T, Q> const& x); + + /// Converts a pure rotation 4 * 4 matrix to a quaternion. + /// + /// @tparam T Floating-point scalar types. + /// + /// @see gtc_quaternion + template + GLM_FUNC_DECL qua quat_cast(mat<4, 4, T, Q> const& x); + + /// Returns the component-wise comparison result of x < y. + /// + /// @tparam T Floating-point scalar types + /// @tparam Q Value from qualifier enum + /// + /// @see ext_quaternion_relational + template + GLM_FUNC_DECL vec<4, bool, Q> lessThan(qua const& x, qua const& y); + + /// Returns the component-wise comparison of result x <= y. + /// + /// @tparam T Floating-point scalar types + /// @tparam Q Value from qualifier enum + /// + /// @see ext_quaternion_relational + template + GLM_FUNC_DECL vec<4, bool, Q> lessThanEqual(qua const& x, qua const& y); + + /// Returns the component-wise comparison of result x > y. + /// + /// @tparam T Floating-point scalar types + /// @tparam Q Value from qualifier enum + /// + /// @see ext_quaternion_relational + template + GLM_FUNC_DECL vec<4, bool, Q> greaterThan(qua const& x, qua const& y); + + /// Returns the component-wise comparison of result x >= y. + /// + /// @tparam T Floating-point scalar types + /// @tparam Q Value from qualifier enum + /// + /// @see ext_quaternion_relational + template + GLM_FUNC_DECL vec<4, bool, Q> greaterThanEqual(qua const& x, qua const& y); + + /// Build a look at quaternion based on the default handedness. + /// + /// @param direction Desired forward direction. Needs to be normalized. + /// @param up Up vector, how the camera is oriented. Typically (0, 1, 0). + template + GLM_FUNC_DECL qua quatLookAt( + vec<3, T, Q> const& direction, + vec<3, T, Q> const& up); + + /// Build a right-handed look at quaternion. + /// + /// @param direction Desired forward direction onto which the -z-axis gets mapped. Needs to be normalized. + /// @param up Up vector, how the camera is oriented. Typically (0, 1, 0). + template + GLM_FUNC_DECL qua quatLookAtRH( + vec<3, T, Q> const& direction, + vec<3, T, Q> const& up); + + /// Build a left-handed look at quaternion. + /// + /// @param direction Desired forward direction onto which the +z-axis gets mapped. Needs to be normalized. + /// @param up Up vector, how the camera is oriented. Typically (0, 1, 0). + template + GLM_FUNC_DECL qua quatLookAtLH( + vec<3, T, Q> const& direction, + vec<3, T, Q> const& up); + /// @} +} //namespace glm + +#include "quaternion.inl" diff --git a/src/GLMath/glm/gtc/quaternion.inl b/src/GLMath/glm/gtc/quaternion.inl new file mode 100644 index 000000000..9dd037ee8 --- /dev/null +++ b/src/GLMath/glm/gtc/quaternion.inl @@ -0,0 +1,200 @@ +#include "../trigonometric.hpp" +#include "../geometric.hpp" +#include "../exponential.hpp" +#include "epsilon.hpp" +#include + +namespace glm +{ + template + GLM_FUNC_QUALIFIER vec<3, T, Q> eulerAngles(qua const& x) + { + return vec<3, T, Q>(pitch(x), yaw(x), roll(x)); + } + + template + GLM_FUNC_QUALIFIER T roll(qua const& q) + { + return static_cast(atan(static_cast(2) * (q.x * q.y + q.w * q.z), q.w * q.w + q.x * q.x - q.y * q.y - q.z * q.z)); + } + + template + GLM_FUNC_QUALIFIER T pitch(qua const& q) + { + //return T(atan(T(2) * (q.y * q.z + q.w * q.x), q.w * q.w - q.x * q.x - q.y * q.y + q.z * q.z)); + T const y = static_cast(2) * (q.y * q.z + q.w * q.x); + T const x = q.w * q.w - q.x * q.x - q.y * q.y + q.z * q.z; + + if(all(equal(vec<2, T, Q>(x, y), vec<2, T, Q>(0), epsilon()))) //avoid atan2(0,0) - handle singularity - Matiis + return static_cast(static_cast(2) * atan(q.x, q.w)); + + return static_cast(atan(y, x)); + } + + template + GLM_FUNC_QUALIFIER T yaw(qua const& q) + { + return asin(clamp(static_cast(-2) * (q.x * q.z - q.w * q.y), static_cast(-1), static_cast(1))); + } + + template + GLM_FUNC_QUALIFIER mat<3, 3, T, Q> mat3_cast(qua const& q) + { + mat<3, 3, T, Q> Result(T(1)); + T qxx(q.x * q.x); + T qyy(q.y * q.y); + T qzz(q.z * q.z); + T qxz(q.x * q.z); + T qxy(q.x * q.y); + T qyz(q.y * q.z); + T qwx(q.w * q.x); + T qwy(q.w * q.y); + T qwz(q.w * q.z); + + Result[0][0] = T(1) - T(2) * (qyy + qzz); + Result[0][1] = T(2) * (qxy + qwz); + Result[0][2] = T(2) * (qxz - qwy); + + Result[1][0] = T(2) * (qxy - qwz); + Result[1][1] = T(1) - T(2) * (qxx + qzz); + Result[1][2] = T(2) * (qyz + qwx); + + Result[2][0] = T(2) * (qxz + qwy); + Result[2][1] = T(2) * (qyz - qwx); + Result[2][2] = T(1) - T(2) * (qxx + qyy); + return Result; + } + + template + GLM_FUNC_QUALIFIER mat<4, 4, T, Q> mat4_cast(qua const& q) + { + return mat<4, 4, T, Q>(mat3_cast(q)); + } + + template + GLM_FUNC_QUALIFIER qua quat_cast(mat<3, 3, T, Q> const& m) + { + T fourXSquaredMinus1 = m[0][0] - m[1][1] - m[2][2]; + T fourYSquaredMinus1 = m[1][1] - m[0][0] - m[2][2]; + T fourZSquaredMinus1 = m[2][2] - m[0][0] - m[1][1]; + T fourWSquaredMinus1 = m[0][0] + m[1][1] + m[2][2]; + + int biggestIndex = 0; + T fourBiggestSquaredMinus1 = fourWSquaredMinus1; + if(fourXSquaredMinus1 > fourBiggestSquaredMinus1) + { + fourBiggestSquaredMinus1 = fourXSquaredMinus1; + biggestIndex = 1; + } + if(fourYSquaredMinus1 > fourBiggestSquaredMinus1) + { + fourBiggestSquaredMinus1 = fourYSquaredMinus1; + biggestIndex = 2; + } + if(fourZSquaredMinus1 > fourBiggestSquaredMinus1) + { + fourBiggestSquaredMinus1 = fourZSquaredMinus1; + biggestIndex = 3; + } + + T biggestVal = sqrt(fourBiggestSquaredMinus1 + static_cast(1)) * static_cast(0.5); + T mult = static_cast(0.25) / biggestVal; + + switch(biggestIndex) + { + case 0: + return qua(biggestVal, (m[1][2] - m[2][1]) * mult, (m[2][0] - m[0][2]) * mult, (m[0][1] - m[1][0]) * mult); + case 1: + return qua((m[1][2] - m[2][1]) * mult, biggestVal, (m[0][1] + m[1][0]) * mult, (m[2][0] + m[0][2]) * mult); + case 2: + return qua((m[2][0] - m[0][2]) * mult, (m[0][1] + m[1][0]) * mult, biggestVal, (m[1][2] + m[2][1]) * mult); + case 3: + return qua((m[0][1] - m[1][0]) * mult, (m[2][0] + m[0][2]) * mult, (m[1][2] + m[2][1]) * mult, biggestVal); + default: // Silence a -Wswitch-default warning in GCC. Should never actually get here. Assert is just for sanity. + assert(false); + return qua(1, 0, 0, 0); + } + } + + template + GLM_FUNC_QUALIFIER qua quat_cast(mat<4, 4, T, Q> const& m4) + { + return quat_cast(mat<3, 3, T, Q>(m4)); + } + + template + GLM_FUNC_QUALIFIER vec<4, bool, Q> lessThan(qua const& x, qua const& y) + { + vec<4, bool, Q> Result; + for(length_t i = 0; i < x.length(); ++i) + Result[i] = x[i] < y[i]; + return Result; + } + + template + GLM_FUNC_QUALIFIER vec<4, bool, Q> lessThanEqual(qua const& x, qua const& y) + { + vec<4, bool, Q> Result; + for(length_t i = 0; i < x.length(); ++i) + Result[i] = x[i] <= y[i]; + return Result; + } + + template + GLM_FUNC_QUALIFIER vec<4, bool, Q> greaterThan(qua const& x, qua const& y) + { + vec<4, bool, Q> Result; + for(length_t i = 0; i < x.length(); ++i) + Result[i] = x[i] > y[i]; + return Result; + } + + template + GLM_FUNC_QUALIFIER vec<4, bool, Q> greaterThanEqual(qua const& x, qua const& y) + { + vec<4, bool, Q> Result; + for(length_t i = 0; i < x.length(); ++i) + Result[i] = x[i] >= y[i]; + return Result; + } + + + template + GLM_FUNC_QUALIFIER qua quatLookAt(vec<3, T, Q> const& direction, vec<3, T, Q> const& up) + { +# if GLM_CONFIG_CLIP_CONTROL & GLM_CLIP_CONTROL_LH_BIT + return quatLookAtLH(direction, up); +# else + return quatLookAtRH(direction, up); +# endif + } + + template + GLM_FUNC_QUALIFIER qua quatLookAtRH(vec<3, T, Q> const& direction, vec<3, T, Q> const& up) + { + mat<3, 3, T, Q> Result; + + Result[2] = -direction; + Result[0] = normalize(cross(up, Result[2])); + Result[1] = cross(Result[2], Result[0]); + + return quat_cast(Result); + } + + template + GLM_FUNC_QUALIFIER qua quatLookAtLH(vec<3, T, Q> const& direction, vec<3, T, Q> const& up) + { + mat<3, 3, T, Q> Result; + + Result[2] = direction; + Result[0] = normalize(cross(up, Result[2])); + Result[1] = cross(Result[2], Result[0]); + + return quat_cast(Result); + } +}//namespace glm + +#if GLM_CONFIG_SIMD == GLM_ENABLE +# include "quaternion_simd.inl" +#endif + diff --git a/src/GLMath/glm/gtc/quaternion_simd.inl b/src/GLMath/glm/gtc/quaternion_simd.inl new file mode 100644 index 000000000..e69de29bb diff --git a/src/GLMath/glm/gtc/random.hpp b/src/GLMath/glm/gtc/random.hpp new file mode 100644 index 000000000..9a859580e --- /dev/null +++ b/src/GLMath/glm/gtc/random.hpp @@ -0,0 +1,82 @@ +/// @ref gtc_random +/// @file glm/gtc/random.hpp +/// +/// @see core (dependence) +/// @see gtx_random (extended) +/// +/// @defgroup gtc_random GLM_GTC_random +/// @ingroup gtc +/// +/// Include to use the features of this extension. +/// +/// Generate random number from various distribution methods. + +#pragma once + +// Dependency: +#include "../ext/scalar_int_sized.hpp" +#include "../ext/scalar_uint_sized.hpp" +#include "../detail/qualifier.hpp" + +#if GLM_MESSAGES == GLM_ENABLE && !defined(GLM_EXT_INCLUDED) +# pragma message("GLM: GLM_GTC_random extension included") +#endif + +namespace glm +{ + /// @addtogroup gtc_random + /// @{ + + /// Generate random numbers in the interval [Min, Max], according a linear distribution + /// + /// @param Min Minimum value included in the sampling + /// @param Max Maximum value included in the sampling + /// @tparam genType Value type. Currently supported: float or double scalars. + /// @see gtc_random + template + GLM_FUNC_DECL genType linearRand(genType Min, genType Max); + + /// Generate random numbers in the interval [Min, Max], according a linear distribution + /// + /// @param Min Minimum value included in the sampling + /// @param Max Maximum value included in the sampling + /// @tparam T Value type. Currently supported: float or double. + /// + /// @see gtc_random + template + GLM_FUNC_DECL vec linearRand(vec const& Min, vec const& Max); + + /// Generate random numbers in the interval [Min, Max], according a gaussian distribution + /// + /// @see gtc_random + template + GLM_FUNC_DECL genType gaussRand(genType Mean, genType Deviation); + + /// Generate a random 2D vector which coordinates are regulary distributed on a circle of a given radius + /// + /// @see gtc_random + template + GLM_FUNC_DECL vec<2, T, defaultp> circularRand(T Radius); + + /// Generate a random 3D vector which coordinates are regulary distributed on a sphere of a given radius + /// + /// @see gtc_random + template + GLM_FUNC_DECL vec<3, T, defaultp> sphericalRand(T Radius); + + /// Generate a random 2D vector which coordinates are regulary distributed within the area of a disk of a given radius + /// + /// @see gtc_random + template + GLM_FUNC_DECL vec<2, T, defaultp> diskRand(T Radius); + + /// Generate a random 3D vector which coordinates are regulary distributed within the volume of a ball of a given radius + /// + /// @see gtc_random + template + GLM_FUNC_DECL vec<3, T, defaultp> ballRand(T Radius); + + /// @} +}//namespace glm + +#include "random.inl" diff --git a/src/GLMath/glm/gtc/random.inl b/src/GLMath/glm/gtc/random.inl new file mode 100644 index 000000000..de10a409d --- /dev/null +++ b/src/GLMath/glm/gtc/random.inl @@ -0,0 +1,303 @@ +#include "../geometric.hpp" +#include "../exponential.hpp" +#include "../trigonometric.hpp" +#include "../detail/type_vec1.hpp" +#include +#include +#include +#include + +namespace glm{ +namespace detail +{ + template + struct compute_rand + { + GLM_FUNC_QUALIFIER static vec call(); + }; + + template + struct compute_rand<1, uint8, P> + { + GLM_FUNC_QUALIFIER static vec<1, uint8, P> call() + { + return vec<1, uint8, P>( + std::rand() % std::numeric_limits::max()); + } + }; + + template + struct compute_rand<2, uint8, P> + { + GLM_FUNC_QUALIFIER static vec<2, uint8, P> call() + { + return vec<2, uint8, P>( + std::rand() % std::numeric_limits::max(), + std::rand() % std::numeric_limits::max()); + } + }; + + template + struct compute_rand<3, uint8, P> + { + GLM_FUNC_QUALIFIER static vec<3, uint8, P> call() + { + return vec<3, uint8, P>( + std::rand() % std::numeric_limits::max(), + std::rand() % std::numeric_limits::max(), + std::rand() % std::numeric_limits::max()); + } + }; + + template + struct compute_rand<4, uint8, P> + { + GLM_FUNC_QUALIFIER static vec<4, uint8, P> call() + { + return vec<4, uint8, P>( + std::rand() % std::numeric_limits::max(), + std::rand() % std::numeric_limits::max(), + std::rand() % std::numeric_limits::max(), + std::rand() % std::numeric_limits::max()); + } + }; + + template + struct compute_rand + { + GLM_FUNC_QUALIFIER static vec call() + { + return + (vec(compute_rand::call()) << static_cast(8)) | + (vec(compute_rand::call()) << static_cast(0)); + } + }; + + template + struct compute_rand + { + GLM_FUNC_QUALIFIER static vec call() + { + return + (vec(compute_rand::call()) << static_cast(16)) | + (vec(compute_rand::call()) << static_cast(0)); + } + }; + + template + struct compute_rand + { + GLM_FUNC_QUALIFIER static vec call() + { + return + (vec(compute_rand::call()) << static_cast(32)) | + (vec(compute_rand::call()) << static_cast(0)); + } + }; + + template + struct compute_linearRand + { + GLM_FUNC_QUALIFIER static vec call(vec const& Min, vec const& Max); + }; + + template + struct compute_linearRand + { + GLM_FUNC_QUALIFIER static vec call(vec const& Min, vec const& Max) + { + return (vec(compute_rand::call() % vec(Max + static_cast(1) - Min))) + Min; + } + }; + + template + struct compute_linearRand + { + GLM_FUNC_QUALIFIER static vec call(vec const& Min, vec const& Max) + { + return (compute_rand::call() % (Max + static_cast(1) - Min)) + Min; + } + }; + + template + struct compute_linearRand + { + GLM_FUNC_QUALIFIER static vec call(vec const& Min, vec const& Max) + { + return (vec(compute_rand::call() % vec(Max + static_cast(1) - Min))) + Min; + } + }; + + template + struct compute_linearRand + { + GLM_FUNC_QUALIFIER static vec call(vec const& Min, vec const& Max) + { + return (compute_rand::call() % (Max + static_cast(1) - Min)) + Min; + } + }; + + template + struct compute_linearRand + { + GLM_FUNC_QUALIFIER static vec call(vec const& Min, vec const& Max) + { + return (vec(compute_rand::call() % vec(Max + static_cast(1) - Min))) + Min; + } + }; + + template + struct compute_linearRand + { + GLM_FUNC_QUALIFIER static vec call(vec const& Min, vec const& Max) + { + return (compute_rand::call() % (Max + static_cast(1) - Min)) + Min; + } + }; + + template + struct compute_linearRand + { + GLM_FUNC_QUALIFIER static vec call(vec const& Min, vec const& Max) + { + return (vec(compute_rand::call() % vec(Max + static_cast(1) - Min))) + Min; + } + }; + + template + struct compute_linearRand + { + GLM_FUNC_QUALIFIER static vec call(vec const& Min, vec const& Max) + { + return (compute_rand::call() % (Max + static_cast(1) - Min)) + Min; + } + }; + + template + struct compute_linearRand + { + GLM_FUNC_QUALIFIER static vec call(vec const& Min, vec const& Max) + { + return vec(compute_rand::call()) / static_cast(std::numeric_limits::max()) * (Max - Min) + Min; + } + }; + + template + struct compute_linearRand + { + GLM_FUNC_QUALIFIER static vec call(vec const& Min, vec const& Max) + { + return vec(compute_rand::call()) / static_cast(std::numeric_limits::max()) * (Max - Min) + Min; + } + }; + + template + struct compute_linearRand + { + GLM_FUNC_QUALIFIER static vec call(vec const& Min, vec const& Max) + { + return vec(compute_rand::call()) / static_cast(std::numeric_limits::max()) * (Max - Min) + Min; + } + }; +}//namespace detail + + template + GLM_FUNC_QUALIFIER genType linearRand(genType Min, genType Max) + { + return detail::compute_linearRand<1, genType, highp>::call( + vec<1, genType, highp>(Min), + vec<1, genType, highp>(Max)).x; + } + + template + GLM_FUNC_QUALIFIER vec linearRand(vec const& Min, vec const& Max) + { + return detail::compute_linearRand::call(Min, Max); + } + + template + GLM_FUNC_QUALIFIER genType gaussRand(genType Mean, genType Deviation) + { + genType w, x1, x2; + + do + { + x1 = linearRand(genType(-1), genType(1)); + x2 = linearRand(genType(-1), genType(1)); + + w = x1 * x1 + x2 * x2; + } while(w > genType(1)); + + return x2 * Deviation * Deviation * sqrt((genType(-2) * log(w)) / w) + Mean; + } + + template + GLM_FUNC_QUALIFIER vec gaussRand(vec const& Mean, vec const& Deviation) + { + return detail::functor2::call(gaussRand, Mean, Deviation); + } + + template + GLM_FUNC_QUALIFIER vec<2, T, defaultp> diskRand(T Radius) + { + assert(Radius > static_cast(0)); + + vec<2, T, defaultp> Result(T(0)); + T LenRadius(T(0)); + + do + { + Result = linearRand( + vec<2, T, defaultp>(-Radius), + vec<2, T, defaultp>(Radius)); + LenRadius = length(Result); + } + while(LenRadius > Radius); + + return Result; + } + + template + GLM_FUNC_QUALIFIER vec<3, T, defaultp> ballRand(T Radius) + { + assert(Radius > static_cast(0)); + + vec<3, T, defaultp> Result(T(0)); + T LenRadius(T(0)); + + do + { + Result = linearRand( + vec<3, T, defaultp>(-Radius), + vec<3, T, defaultp>(Radius)); + LenRadius = length(Result); + } + while(LenRadius > Radius); + + return Result; + } + + template + GLM_FUNC_QUALIFIER vec<2, T, defaultp> circularRand(T Radius) + { + assert(Radius > static_cast(0)); + + T a = linearRand(T(0), static_cast(6.283185307179586476925286766559)); + return vec<2, T, defaultp>(glm::cos(a), glm::sin(a)) * Radius; + } + + template + GLM_FUNC_QUALIFIER vec<3, T, defaultp> sphericalRand(T Radius) + { + assert(Radius > static_cast(0)); + + T theta = linearRand(T(0), T(6.283185307179586476925286766559f)); + T phi = std::acos(linearRand(T(-1.0f), T(1.0f))); + + T x = std::sin(phi) * std::cos(theta); + T y = std::sin(phi) * std::sin(theta); + T z = std::cos(phi); + + return vec<3, T, defaultp>(x, y, z) * Radius; + } +}//namespace glm diff --git a/src/GLMath/glm/gtc/reciprocal.hpp b/src/GLMath/glm/gtc/reciprocal.hpp new file mode 100644 index 000000000..c7d133038 --- /dev/null +++ b/src/GLMath/glm/gtc/reciprocal.hpp @@ -0,0 +1,135 @@ +/// @ref gtc_reciprocal +/// @file glm/gtc/reciprocal.hpp +/// +/// @see core (dependence) +/// +/// @defgroup gtc_reciprocal GLM_GTC_reciprocal +/// @ingroup gtc +/// +/// Include to use the features of this extension. +/// +/// Define secant, cosecant and cotangent functions. + +#pragma once + +// Dependencies +#include "../detail/setup.hpp" + +#if GLM_MESSAGES == GLM_ENABLE && !defined(GLM_EXT_INCLUDED) +# pragma message("GLM: GLM_GTC_reciprocal extension included") +#endif + +namespace glm +{ + /// @addtogroup gtc_reciprocal + /// @{ + + /// Secant function. + /// hypotenuse / adjacent or 1 / cos(x) + /// + /// @tparam genType Floating-point scalar or vector types. + /// + /// @see gtc_reciprocal + template + GLM_FUNC_DECL genType sec(genType angle); + + /// Cosecant function. + /// hypotenuse / opposite or 1 / sin(x) + /// + /// @tparam genType Floating-point scalar or vector types. + /// + /// @see gtc_reciprocal + template + GLM_FUNC_DECL genType csc(genType angle); + + /// Cotangent function. + /// adjacent / opposite or 1 / tan(x) + /// + /// @tparam genType Floating-point scalar or vector types. + /// + /// @see gtc_reciprocal + template + GLM_FUNC_DECL genType cot(genType angle); + + /// Inverse secant function. + /// + /// @return Return an angle expressed in radians. + /// @tparam genType Floating-point scalar or vector types. + /// + /// @see gtc_reciprocal + template + GLM_FUNC_DECL genType asec(genType x); + + /// Inverse cosecant function. + /// + /// @return Return an angle expressed in radians. + /// @tparam genType Floating-point scalar or vector types. + /// + /// @see gtc_reciprocal + template + GLM_FUNC_DECL genType acsc(genType x); + + /// Inverse cotangent function. + /// + /// @return Return an angle expressed in radians. + /// @tparam genType Floating-point scalar or vector types. + /// + /// @see gtc_reciprocal + template + GLM_FUNC_DECL genType acot(genType x); + + /// Secant hyperbolic function. + /// + /// @tparam genType Floating-point scalar or vector types. + /// + /// @see gtc_reciprocal + template + GLM_FUNC_DECL genType sech(genType angle); + + /// Cosecant hyperbolic function. + /// + /// @tparam genType Floating-point scalar or vector types. + /// + /// @see gtc_reciprocal + template + GLM_FUNC_DECL genType csch(genType angle); + + /// Cotangent hyperbolic function. + /// + /// @tparam genType Floating-point scalar or vector types. + /// + /// @see gtc_reciprocal + template + GLM_FUNC_DECL genType coth(genType angle); + + /// Inverse secant hyperbolic function. + /// + /// @return Return an angle expressed in radians. + /// @tparam genType Floating-point scalar or vector types. + /// + /// @see gtc_reciprocal + template + GLM_FUNC_DECL genType asech(genType x); + + /// Inverse cosecant hyperbolic function. + /// + /// @return Return an angle expressed in radians. + /// @tparam genType Floating-point scalar or vector types. + /// + /// @see gtc_reciprocal + template + GLM_FUNC_DECL genType acsch(genType x); + + /// Inverse cotangent hyperbolic function. + /// + /// @return Return an angle expressed in radians. + /// @tparam genType Floating-point scalar or vector types. + /// + /// @see gtc_reciprocal + template + GLM_FUNC_DECL genType acoth(genType x); + + /// @} +}//namespace glm + +#include "reciprocal.inl" diff --git a/src/GLMath/glm/gtc/reciprocal.inl b/src/GLMath/glm/gtc/reciprocal.inl new file mode 100644 index 000000000..d88729e88 --- /dev/null +++ b/src/GLMath/glm/gtc/reciprocal.inl @@ -0,0 +1,191 @@ +/// @ref gtc_reciprocal + +#include "../trigonometric.hpp" +#include + +namespace glm +{ + // sec + template + GLM_FUNC_QUALIFIER genType sec(genType angle) + { + GLM_STATIC_ASSERT(std::numeric_limits::is_iec559, "'sec' only accept floating-point values"); + return genType(1) / glm::cos(angle); + } + + template + GLM_FUNC_QUALIFIER vec sec(vec const& x) + { + GLM_STATIC_ASSERT(std::numeric_limits::is_iec559, "'sec' only accept floating-point inputs"); + return detail::functor1::call(sec, x); + } + + // csc + template + GLM_FUNC_QUALIFIER genType csc(genType angle) + { + GLM_STATIC_ASSERT(std::numeric_limits::is_iec559, "'csc' only accept floating-point values"); + return genType(1) / glm::sin(angle); + } + + template + GLM_FUNC_QUALIFIER vec csc(vec const& x) + { + GLM_STATIC_ASSERT(std::numeric_limits::is_iec559, "'csc' only accept floating-point inputs"); + return detail::functor1::call(csc, x); + } + + // cot + template + GLM_FUNC_QUALIFIER genType cot(genType angle) + { + GLM_STATIC_ASSERT(std::numeric_limits::is_iec559, "'cot' only accept floating-point values"); + + genType const pi_over_2 = genType(3.1415926535897932384626433832795 / 2.0); + return glm::tan(pi_over_2 - angle); + } + + template + GLM_FUNC_QUALIFIER vec cot(vec const& x) + { + GLM_STATIC_ASSERT(std::numeric_limits::is_iec559, "'cot' only accept floating-point inputs"); + return detail::functor1::call(cot, x); + } + + // asec + template + GLM_FUNC_QUALIFIER genType asec(genType x) + { + GLM_STATIC_ASSERT(std::numeric_limits::is_iec559, "'asec' only accept floating-point values"); + return acos(genType(1) / x); + } + + template + GLM_FUNC_QUALIFIER vec asec(vec const& x) + { + GLM_STATIC_ASSERT(std::numeric_limits::is_iec559, "'asec' only accept floating-point inputs"); + return detail::functor1::call(asec, x); + } + + // acsc + template + GLM_FUNC_QUALIFIER genType acsc(genType x) + { + GLM_STATIC_ASSERT(std::numeric_limits::is_iec559, "'acsc' only accept floating-point values"); + return asin(genType(1) / x); + } + + template + GLM_FUNC_QUALIFIER vec acsc(vec const& x) + { + GLM_STATIC_ASSERT(std::numeric_limits::is_iec559, "'acsc' only accept floating-point inputs"); + return detail::functor1::call(acsc, x); + } + + // acot + template + GLM_FUNC_QUALIFIER genType acot(genType x) + { + GLM_STATIC_ASSERT(std::numeric_limits::is_iec559, "'acot' only accept floating-point values"); + + genType const pi_over_2 = genType(3.1415926535897932384626433832795 / 2.0); + return pi_over_2 - atan(x); + } + + template + GLM_FUNC_QUALIFIER vec acot(vec const& x) + { + GLM_STATIC_ASSERT(std::numeric_limits::is_iec559, "'acot' only accept floating-point inputs"); + return detail::functor1::call(acot, x); + } + + // sech + template + GLM_FUNC_QUALIFIER genType sech(genType angle) + { + GLM_STATIC_ASSERT(std::numeric_limits::is_iec559, "'sech' only accept floating-point values"); + return genType(1) / glm::cosh(angle); + } + + template + GLM_FUNC_QUALIFIER vec sech(vec const& x) + { + GLM_STATIC_ASSERT(std::numeric_limits::is_iec559, "'sech' only accept floating-point inputs"); + return detail::functor1::call(sech, x); + } + + // csch + template + GLM_FUNC_QUALIFIER genType csch(genType angle) + { + GLM_STATIC_ASSERT(std::numeric_limits::is_iec559, "'csch' only accept floating-point values"); + return genType(1) / glm::sinh(angle); + } + + template + GLM_FUNC_QUALIFIER vec csch(vec const& x) + { + GLM_STATIC_ASSERT(std::numeric_limits::is_iec559, "'csch' only accept floating-point inputs"); + return detail::functor1::call(csch, x); + } + + // coth + template + GLM_FUNC_QUALIFIER genType coth(genType angle) + { + GLM_STATIC_ASSERT(std::numeric_limits::is_iec559, "'coth' only accept floating-point values"); + return glm::cosh(angle) / glm::sinh(angle); + } + + template + GLM_FUNC_QUALIFIER vec coth(vec const& x) + { + GLM_STATIC_ASSERT(std::numeric_limits::is_iec559, "'coth' only accept floating-point inputs"); + return detail::functor1::call(coth, x); + } + + // asech + template + GLM_FUNC_QUALIFIER genType asech(genType x) + { + GLM_STATIC_ASSERT(std::numeric_limits::is_iec559, "'asech' only accept floating-point values"); + return acosh(genType(1) / x); + } + + template + GLM_FUNC_QUALIFIER vec asech(vec const& x) + { + GLM_STATIC_ASSERT(std::numeric_limits::is_iec559, "'asech' only accept floating-point inputs"); + return detail::functor1::call(asech, x); + } + + // acsch + template + GLM_FUNC_QUALIFIER genType acsch(genType x) + { + GLM_STATIC_ASSERT(std::numeric_limits::is_iec559, "'acsch' only accept floating-point values"); + return asinh(genType(1) / x); + } + + template + GLM_FUNC_QUALIFIER vec acsch(vec const& x) + { + GLM_STATIC_ASSERT(std::numeric_limits::is_iec559, "'acsch' only accept floating-point inputs"); + return detail::functor1::call(acsch, x); + } + + // acoth + template + GLM_FUNC_QUALIFIER genType acoth(genType x) + { + GLM_STATIC_ASSERT(std::numeric_limits::is_iec559, "'acoth' only accept floating-point values"); + return atanh(genType(1) / x); + } + + template + GLM_FUNC_QUALIFIER vec acoth(vec const& x) + { + GLM_STATIC_ASSERT(std::numeric_limits::is_iec559, "'acoth' only accept floating-point inputs"); + return detail::functor1::call(acoth, x); + } +}//namespace glm diff --git a/src/GLMath/glm/gtc/round.hpp b/src/GLMath/glm/gtc/round.hpp new file mode 100644 index 000000000..fbbcdeb55 --- /dev/null +++ b/src/GLMath/glm/gtc/round.hpp @@ -0,0 +1,202 @@ +/// @ref gtc_round +/// @file glm/gtc/round.hpp +/// +/// @see core (dependence) +/// @see gtc_round (dependence) +/// +/// @defgroup gtc_round GLM_GTC_round +/// @ingroup gtc +/// +/// Include to use the features of this extension. +/// +/// Rounding value to specific boundings + +#pragma once + +// Dependencies +#include "../detail/setup.hpp" +#include "../detail/qualifier.hpp" +#include "../detail/_vectorize.hpp" +#include "../vector_relational.hpp" +#include "../common.hpp" +#include + +#if GLM_MESSAGES == GLM_ENABLE && !defined(GLM_EXT_INCLUDED) +# pragma message("GLM: GLM_GTC_integer extension included") +#endif + +namespace glm +{ + /// @addtogroup gtc_round + /// @{ + + /// Return true if the value is a power of two number. + /// + /// @see gtc_round + template + GLM_FUNC_DECL bool isPowerOfTwo(genIUType v); + + /// Return true if the value is a power of two number. + /// + /// @tparam L Integer between 1 and 4 included that qualify the dimension of the vector + /// @tparam T Floating-point or integer scalar types + /// @tparam Q Value from qualifier enum + /// + /// @see gtc_round + template + GLM_FUNC_DECL vec isPowerOfTwo(vec const& v); + + /// Return the power of two number which value is just higher the input value, + /// round up to a power of two. + /// + /// @see gtc_round + template + GLM_FUNC_DECL genIUType ceilPowerOfTwo(genIUType v); + + /// Return the power of two number which value is just higher the input value, + /// round up to a power of two. + /// + /// @tparam L Integer between 1 and 4 included that qualify the dimension of the vector + /// @tparam T Floating-point or integer scalar types + /// @tparam Q Value from qualifier enum + /// + /// @see gtc_round + template + GLM_FUNC_DECL vec ceilPowerOfTwo(vec const& v); + + /// Return the power of two number which value is just lower the input value, + /// round down to a power of two. + /// + /// @see gtc_round + template + GLM_FUNC_DECL genIUType floorPowerOfTwo(genIUType v); + + /// Return the power of two number which value is just lower the input value, + /// round down to a power of two. + /// + /// @tparam L Integer between 1 and 4 included that qualify the dimension of the vector + /// @tparam T Floating-point or integer scalar types + /// @tparam Q Value from qualifier enum + /// + /// @see gtc_round + template + GLM_FUNC_DECL vec floorPowerOfTwo(vec const& v); + + /// Return the power of two number which value is the closet to the input value. + /// + /// @see gtc_round + template + GLM_FUNC_DECL genIUType roundPowerOfTwo(genIUType v); + + /// Return the power of two number which value is the closet to the input value. + /// + /// @tparam L Integer between 1 and 4 included that qualify the dimension of the vector + /// @tparam T Floating-point or integer scalar types + /// @tparam Q Value from qualifier enum + /// + /// @see gtc_round + template + GLM_FUNC_DECL vec roundPowerOfTwo(vec const& v); + + /// Return true if the 'Value' is a multiple of 'Multiple'. + /// + /// @see gtc_round + template + GLM_FUNC_DECL bool isMultiple(genIUType v, genIUType Multiple); + + /// Return true if the 'Value' is a multiple of 'Multiple'. + /// + /// @tparam L Integer between 1 and 4 included that qualify the dimension of the vector + /// @tparam T Floating-point or integer scalar types + /// @tparam Q Value from qualifier enum + /// + /// @see gtc_round + template + GLM_FUNC_DECL vec isMultiple(vec const& v, T Multiple); + + /// Return true if the 'Value' is a multiple of 'Multiple'. + /// + /// @tparam L Integer between 1 and 4 included that qualify the dimension of the vector + /// @tparam T Floating-point or integer scalar types + /// @tparam Q Value from qualifier enum + /// + /// @see gtc_round + template + GLM_FUNC_DECL vec isMultiple(vec const& v, vec const& Multiple); + + /// Higher multiple number of Source. + /// + /// @tparam genType Floating-point or integer scalar or vector types. + /// + /// @param v Source value to which is applied the function + /// @param Multiple Must be a null or positive value + /// + /// @see gtc_round + template + GLM_FUNC_DECL genType ceilMultiple(genType v, genType Multiple); + + /// Higher multiple number of Source. + /// + /// @tparam L Integer between 1 and 4 included that qualify the dimension of the vector + /// @tparam T Floating-point or integer scalar types + /// @tparam Q Value from qualifier enum + /// + /// @param v Source values to which is applied the function + /// @param Multiple Must be a null or positive value + /// + /// @see gtc_round + template + GLM_FUNC_DECL vec ceilMultiple(vec const& v, vec const& Multiple); + + /// Lower multiple number of Source. + /// + /// @tparam genType Floating-point or integer scalar or vector types. + /// + /// @param v Source value to which is applied the function + /// @param Multiple Must be a null or positive value + /// + /// @see gtc_round + template + GLM_FUNC_DECL genType floorMultiple(genType v, genType Multiple); + + /// Lower multiple number of Source. + /// + /// @tparam L Integer between 1 and 4 included that qualify the dimension of the vector + /// @tparam T Floating-point or integer scalar types + /// @tparam Q Value from qualifier enum + /// + /// @param v Source values to which is applied the function + /// @param Multiple Must be a null or positive value + /// + /// @see gtc_round + template + GLM_FUNC_DECL vec floorMultiple(vec const& v, vec const& Multiple); + + /// Lower multiple number of Source. + /// + /// @tparam genType Floating-point or integer scalar or vector types. + /// + /// @param v Source value to which is applied the function + /// @param Multiple Must be a null or positive value + /// + /// @see gtc_round + template + GLM_FUNC_DECL genType roundMultiple(genType v, genType Multiple); + + /// Lower multiple number of Source. + /// + /// @tparam L Integer between 1 and 4 included that qualify the dimension of the vector + /// @tparam T Floating-point or integer scalar types + /// @tparam Q Value from qualifier enum + /// + /// @param v Source values to which is applied the function + /// @param Multiple Must be a null or positive value + /// + /// @see gtc_round + template + GLM_FUNC_DECL vec roundMultiple(vec const& v, vec const& Multiple); + + /// @} +} //namespace glm + +#include "round.inl" diff --git a/src/GLMath/glm/gtc/round.inl b/src/GLMath/glm/gtc/round.inl new file mode 100644 index 000000000..0d4d7d67d --- /dev/null +++ b/src/GLMath/glm/gtc/round.inl @@ -0,0 +1,343 @@ +/// @ref gtc_round + +#include "../integer.hpp" + +namespace glm{ +namespace detail +{ + template + struct compute_ceilShift + { + GLM_FUNC_QUALIFIER static vec call(vec const& v, T) + { + return v; + } + }; + + template + struct compute_ceilShift + { + GLM_FUNC_QUALIFIER static vec call(vec const& v, T Shift) + { + return v | (v >> Shift); + } + }; + + template + struct compute_ceilPowerOfTwo + { + GLM_FUNC_QUALIFIER static vec call(vec const& x) + { + GLM_STATIC_ASSERT(!std::numeric_limits::is_iec559, "'ceilPowerOfTwo' only accept integer scalar or vector inputs"); + + vec const Sign(sign(x)); + + vec v(abs(x)); + + v = v - static_cast(1); + v = v | (v >> static_cast(1)); + v = v | (v >> static_cast(2)); + v = v | (v >> static_cast(4)); + v = compute_ceilShift= 2>::call(v, 8); + v = compute_ceilShift= 4>::call(v, 16); + v = compute_ceilShift= 8>::call(v, 32); + return (v + static_cast(1)) * Sign; + } + }; + + template + struct compute_ceilPowerOfTwo + { + GLM_FUNC_QUALIFIER static vec call(vec const& x) + { + GLM_STATIC_ASSERT(!std::numeric_limits::is_iec559, "'ceilPowerOfTwo' only accept integer scalar or vector inputs"); + + vec v(x); + + v = v - static_cast(1); + v = v | (v >> static_cast(1)); + v = v | (v >> static_cast(2)); + v = v | (v >> static_cast(4)); + v = compute_ceilShift= 2>::call(v, 8); + v = compute_ceilShift= 4>::call(v, 16); + v = compute_ceilShift= 8>::call(v, 32); + return v + static_cast(1); + } + }; + + template + struct compute_ceilMultiple{}; + + template<> + struct compute_ceilMultiple + { + template + GLM_FUNC_QUALIFIER static genType call(genType Source, genType Multiple) + { + if(Source > genType(0)) + return Source + (Multiple - std::fmod(Source, Multiple)); + else + return Source + std::fmod(-Source, Multiple); + } + }; + + template<> + struct compute_ceilMultiple + { + template + GLM_FUNC_QUALIFIER static genType call(genType Source, genType Multiple) + { + genType Tmp = Source - genType(1); + return Tmp + (Multiple - (Tmp % Multiple)); + } + }; + + template<> + struct compute_ceilMultiple + { + template + GLM_FUNC_QUALIFIER static genType call(genType Source, genType Multiple) + { + if(Source > genType(0)) + { + genType Tmp = Source - genType(1); + return Tmp + (Multiple - (Tmp % Multiple)); + } + else + return Source + (-Source % Multiple); + } + }; + + template + struct compute_floorMultiple{}; + + template<> + struct compute_floorMultiple + { + template + GLM_FUNC_QUALIFIER static genType call(genType Source, genType Multiple) + { + if(Source >= genType(0)) + return Source - std::fmod(Source, Multiple); + else + return Source - std::fmod(Source, Multiple) - Multiple; + } + }; + + template<> + struct compute_floorMultiple + { + template + GLM_FUNC_QUALIFIER static genType call(genType Source, genType Multiple) + { + if(Source >= genType(0)) + return Source - Source % Multiple; + else + { + genType Tmp = Source + genType(1); + return Tmp - Tmp % Multiple - Multiple; + } + } + }; + + template<> + struct compute_floorMultiple + { + template + GLM_FUNC_QUALIFIER static genType call(genType Source, genType Multiple) + { + if(Source >= genType(0)) + return Source - Source % Multiple; + else + { + genType Tmp = Source + genType(1); + return Tmp - Tmp % Multiple - Multiple; + } + } + }; + + template + struct compute_roundMultiple{}; + + template<> + struct compute_roundMultiple + { + template + GLM_FUNC_QUALIFIER static genType call(genType Source, genType Multiple) + { + if(Source >= genType(0)) + return Source - std::fmod(Source, Multiple); + else + { + genType Tmp = Source + genType(1); + return Tmp - std::fmod(Tmp, Multiple) - Multiple; + } + } + }; + + template<> + struct compute_roundMultiple + { + template + GLM_FUNC_QUALIFIER static genType call(genType Source, genType Multiple) + { + if(Source >= genType(0)) + return Source - Source % Multiple; + else + { + genType Tmp = Source + genType(1); + return Tmp - Tmp % Multiple - Multiple; + } + } + }; + + template<> + struct compute_roundMultiple + { + template + GLM_FUNC_QUALIFIER static genType call(genType Source, genType Multiple) + { + if(Source >= genType(0)) + return Source - Source % Multiple; + else + { + genType Tmp = Source + genType(1); + return Tmp - Tmp % Multiple - Multiple; + } + } + }; +}//namespace detail + + //////////////// + // isPowerOfTwo + + template + GLM_FUNC_QUALIFIER bool isPowerOfTwo(genType Value) + { + genType const Result = glm::abs(Value); + return !(Result & (Result - 1)); + } + + template + GLM_FUNC_QUALIFIER vec isPowerOfTwo(vec const& Value) + { + vec const Result(abs(Value)); + return equal(Result & (Result - 1), vec(0)); + } + + ////////////////// + // ceilPowerOfTwo + + template + GLM_FUNC_QUALIFIER genType ceilPowerOfTwo(genType value) + { + return detail::compute_ceilPowerOfTwo<1, genType, defaultp, std::numeric_limits::is_signed>::call(vec<1, genType, defaultp>(value)).x; + } + + template + GLM_FUNC_QUALIFIER vec ceilPowerOfTwo(vec const& v) + { + return detail::compute_ceilPowerOfTwo::is_signed>::call(v); + } + + /////////////////// + // floorPowerOfTwo + + template + GLM_FUNC_QUALIFIER genType floorPowerOfTwo(genType value) + { + return isPowerOfTwo(value) ? value : static_cast(1) << findMSB(value); + } + + template + GLM_FUNC_QUALIFIER vec floorPowerOfTwo(vec const& v) + { + return detail::functor1::call(floorPowerOfTwo, v); + } + + /////////////////// + // roundPowerOfTwo + + template + GLM_FUNC_QUALIFIER genIUType roundPowerOfTwo(genIUType value) + { + if(isPowerOfTwo(value)) + return value; + + genIUType const prev = static_cast(1) << findMSB(value); + genIUType const next = prev << static_cast(1); + return (next - value) < (value - prev) ? next : prev; + } + + template + GLM_FUNC_QUALIFIER vec roundPowerOfTwo(vec const& v) + { + return detail::functor1::call(roundPowerOfTwo, v); + } + + //////////////// + // isMultiple + + template + GLM_FUNC_QUALIFIER bool isMultiple(genType Value, genType Multiple) + { + return isMultiple(vec<1, genType>(Value), vec<1, genType>(Multiple)).x; + } + + template + GLM_FUNC_QUALIFIER vec isMultiple(vec const& Value, T Multiple) + { + return (Value % Multiple) == vec(0); + } + + template + GLM_FUNC_QUALIFIER vec isMultiple(vec const& Value, vec const& Multiple) + { + return (Value % Multiple) == vec(0); + } + + ////////////////////// + // ceilMultiple + + template + GLM_FUNC_QUALIFIER genType ceilMultiple(genType Source, genType Multiple) + { + return detail::compute_ceilMultiple::is_iec559, std::numeric_limits::is_signed>::call(Source, Multiple); + } + + template + GLM_FUNC_QUALIFIER vec ceilMultiple(vec const& Source, vec const& Multiple) + { + return detail::functor2::call(ceilMultiple, Source, Multiple); + } + + ////////////////////// + // floorMultiple + + template + GLM_FUNC_QUALIFIER genType floorMultiple(genType Source, genType Multiple) + { + return detail::compute_floorMultiple::is_iec559, std::numeric_limits::is_signed>::call(Source, Multiple); + } + + template + GLM_FUNC_QUALIFIER vec floorMultiple(vec const& Source, vec const& Multiple) + { + return detail::functor2::call(floorMultiple, Source, Multiple); + } + + ////////////////////// + // roundMultiple + + template + GLM_FUNC_QUALIFIER genType roundMultiple(genType Source, genType Multiple) + { + return detail::compute_roundMultiple::is_iec559, std::numeric_limits::is_signed>::call(Source, Multiple); + } + + template + GLM_FUNC_QUALIFIER vec roundMultiple(vec const& Source, vec const& Multiple) + { + return detail::functor2::call(roundMultiple, Source, Multiple); + } +}//namespace glm diff --git a/src/GLMath/glm/gtc/type_aligned.hpp b/src/GLMath/glm/gtc/type_aligned.hpp new file mode 100644 index 000000000..5403abf67 --- /dev/null +++ b/src/GLMath/glm/gtc/type_aligned.hpp @@ -0,0 +1,1315 @@ +/// @ref gtc_type_aligned +/// @file glm/gtc/type_aligned.hpp +/// +/// @see core (dependence) +/// +/// @defgroup gtc_type_aligned GLM_GTC_type_aligned +/// @ingroup gtc +/// +/// Include to use the features of this extension. +/// +/// Aligned types allowing SIMD optimizations of vectors and matrices types + +#pragma once + +#if (GLM_CONFIG_ALIGNED_GENTYPES == GLM_DISABLE) +# error "GLM: Aligned gentypes require to enable C++ language extensions. Define GLM_FORCE_ALIGNED_GENTYPES before including GLM headers to use aligned types." +#endif + +#if GLM_MESSAGES == GLM_ENABLE && !defined(GLM_EXT_INCLUDED) +# pragma message("GLM: GLM_GTC_type_aligned extension included") +#endif + +#include "../mat4x4.hpp" +#include "../mat4x3.hpp" +#include "../mat4x2.hpp" +#include "../mat3x4.hpp" +#include "../mat3x3.hpp" +#include "../mat3x2.hpp" +#include "../mat2x4.hpp" +#include "../mat2x3.hpp" +#include "../mat2x2.hpp" +#include "../gtc/vec1.hpp" +#include "../vec2.hpp" +#include "../vec3.hpp" +#include "../vec4.hpp" + +namespace glm +{ + /// @addtogroup gtc_type_aligned + /// @{ + + // -- *vec1 -- + + /// 1 component vector aligned in memory of single-precision floating-point numbers using high precision arithmetic in term of ULPs. + typedef vec<1, float, aligned_highp> aligned_highp_vec1; + + /// 1 component vector aligned in memory of single-precision floating-point numbers using medium precision arithmetic in term of ULPs. + typedef vec<1, float, aligned_mediump> aligned_mediump_vec1; + + /// 1 component vector aligned in memory of single-precision floating-point numbers using low precision arithmetic in term of ULPs. + typedef vec<1, float, aligned_lowp> aligned_lowp_vec1; + + /// 1 component vector aligned in memory of double-precision floating-point numbers using high precision arithmetic in term of ULPs. + typedef vec<1, double, aligned_highp> aligned_highp_dvec1; + + /// 1 component vector aligned in memory of double-precision floating-point numbers using medium precision arithmetic in term of ULPs. + typedef vec<1, double, aligned_mediump> aligned_mediump_dvec1; + + /// 1 component vector aligned in memory of double-precision floating-point numbers using low precision arithmetic in term of ULPs. + typedef vec<1, double, aligned_lowp> aligned_lowp_dvec1; + + /// 1 component vector aligned in memory of signed integer numbers. + typedef vec<1, int, aligned_highp> aligned_highp_ivec1; + + /// 1 component vector aligned in memory of signed integer numbers. + typedef vec<1, int, aligned_mediump> aligned_mediump_ivec1; + + /// 1 component vector aligned in memory of signed integer numbers. + typedef vec<1, int, aligned_lowp> aligned_lowp_ivec1; + + /// 1 component vector aligned in memory of unsigned integer numbers. + typedef vec<1, uint, aligned_highp> aligned_highp_uvec1; + + /// 1 component vector aligned in memory of unsigned integer numbers. + typedef vec<1, uint, aligned_mediump> aligned_mediump_uvec1; + + /// 1 component vector aligned in memory of unsigned integer numbers. + typedef vec<1, uint, aligned_lowp> aligned_lowp_uvec1; + + /// 1 component vector aligned in memory of bool values. + typedef vec<1, bool, aligned_highp> aligned_highp_bvec1; + + /// 1 component vector aligned in memory of bool values. + typedef vec<1, bool, aligned_mediump> aligned_mediump_bvec1; + + /// 1 component vector aligned in memory of bool values. + typedef vec<1, bool, aligned_lowp> aligned_lowp_bvec1; + + /// 1 component vector tightly packed in memory of single-precision floating-point numbers using high precision arithmetic in term of ULPs. + typedef vec<1, float, packed_highp> packed_highp_vec1; + + /// 1 component vector tightly packed in memory of single-precision floating-point numbers using medium precision arithmetic in term of ULPs. + typedef vec<1, float, packed_mediump> packed_mediump_vec1; + + /// 1 component vector tightly packed in memory of single-precision floating-point numbers using low precision arithmetic in term of ULPs. + typedef vec<1, float, packed_lowp> packed_lowp_vec1; + + /// 1 component vector tightly packed in memory of double-precision floating-point numbers using high precision arithmetic in term of ULPs. + typedef vec<1, double, packed_highp> packed_highp_dvec1; + + /// 1 component vector tightly packed in memory of double-precision floating-point numbers using medium precision arithmetic in term of ULPs. + typedef vec<1, double, packed_mediump> packed_mediump_dvec1; + + /// 1 component vector tightly packed in memory of double-precision floating-point numbers using low precision arithmetic in term of ULPs. + typedef vec<1, double, packed_lowp> packed_lowp_dvec1; + + /// 1 component vector tightly packed in memory of signed integer numbers. + typedef vec<1, int, packed_highp> packed_highp_ivec1; + + /// 1 component vector tightly packed in memory of signed integer numbers. + typedef vec<1, int, packed_mediump> packed_mediump_ivec1; + + /// 1 component vector tightly packed in memory of signed integer numbers. + typedef vec<1, int, packed_lowp> packed_lowp_ivec1; + + /// 1 component vector tightly packed in memory of unsigned integer numbers. + typedef vec<1, uint, packed_highp> packed_highp_uvec1; + + /// 1 component vector tightly packed in memory of unsigned integer numbers. + typedef vec<1, uint, packed_mediump> packed_mediump_uvec1; + + /// 1 component vector tightly packed in memory of unsigned integer numbers. + typedef vec<1, uint, packed_lowp> packed_lowp_uvec1; + + /// 1 component vector tightly packed in memory of bool values. + typedef vec<1, bool, packed_highp> packed_highp_bvec1; + + /// 1 component vector tightly packed in memory of bool values. + typedef vec<1, bool, packed_mediump> packed_mediump_bvec1; + + /// 1 component vector tightly packed in memory of bool values. + typedef vec<1, bool, packed_lowp> packed_lowp_bvec1; + + // -- *vec2 -- + + /// 2 components vector aligned in memory of single-precision floating-point numbers using high precision arithmetic in term of ULPs. + typedef vec<2, float, aligned_highp> aligned_highp_vec2; + + /// 2 components vector aligned in memory of single-precision floating-point numbers using medium precision arithmetic in term of ULPs. + typedef vec<2, float, aligned_mediump> aligned_mediump_vec2; + + /// 2 components vector aligned in memory of single-precision floating-point numbers using low precision arithmetic in term of ULPs. + typedef vec<2, float, aligned_lowp> aligned_lowp_vec2; + + /// 2 components vector aligned in memory of double-precision floating-point numbers using high precision arithmetic in term of ULPs. + typedef vec<2, double, aligned_highp> aligned_highp_dvec2; + + /// 2 components vector aligned in memory of double-precision floating-point numbers using medium precision arithmetic in term of ULPs. + typedef vec<2, double, aligned_mediump> aligned_mediump_dvec2; + + /// 2 components vector aligned in memory of double-precision floating-point numbers using low precision arithmetic in term of ULPs. + typedef vec<2, double, aligned_lowp> aligned_lowp_dvec2; + + /// 2 components vector aligned in memory of signed integer numbers. + typedef vec<2, int, aligned_highp> aligned_highp_ivec2; + + /// 2 components vector aligned in memory of signed integer numbers. + typedef vec<2, int, aligned_mediump> aligned_mediump_ivec2; + + /// 2 components vector aligned in memory of signed integer numbers. + typedef vec<2, int, aligned_lowp> aligned_lowp_ivec2; + + /// 2 components vector aligned in memory of unsigned integer numbers. + typedef vec<2, uint, aligned_highp> aligned_highp_uvec2; + + /// 2 components vector aligned in memory of unsigned integer numbers. + typedef vec<2, uint, aligned_mediump> aligned_mediump_uvec2; + + /// 2 components vector aligned in memory of unsigned integer numbers. + typedef vec<2, uint, aligned_lowp> aligned_lowp_uvec2; + + /// 2 components vector aligned in memory of bool values. + typedef vec<2, bool, aligned_highp> aligned_highp_bvec2; + + /// 2 components vector aligned in memory of bool values. + typedef vec<2, bool, aligned_mediump> aligned_mediump_bvec2; + + /// 2 components vector aligned in memory of bool values. + typedef vec<2, bool, aligned_lowp> aligned_lowp_bvec2; + + /// 2 components vector tightly packed in memory of single-precision floating-point numbers using high precision arithmetic in term of ULPs. + typedef vec<2, float, packed_highp> packed_highp_vec2; + + /// 2 components vector tightly packed in memory of single-precision floating-point numbers using medium precision arithmetic in term of ULPs. + typedef vec<2, float, packed_mediump> packed_mediump_vec2; + + /// 2 components vector tightly packed in memory of single-precision floating-point numbers using low precision arithmetic in term of ULPs. + typedef vec<2, float, packed_lowp> packed_lowp_vec2; + + /// 2 components vector tightly packed in memory of double-precision floating-point numbers using high precision arithmetic in term of ULPs. + typedef vec<2, double, packed_highp> packed_highp_dvec2; + + /// 2 components vector tightly packed in memory of double-precision floating-point numbers using medium precision arithmetic in term of ULPs. + typedef vec<2, double, packed_mediump> packed_mediump_dvec2; + + /// 2 components vector tightly packed in memory of double-precision floating-point numbers using low precision arithmetic in term of ULPs. + typedef vec<2, double, packed_lowp> packed_lowp_dvec2; + + /// 2 components vector tightly packed in memory of signed integer numbers. + typedef vec<2, int, packed_highp> packed_highp_ivec2; + + /// 2 components vector tightly packed in memory of signed integer numbers. + typedef vec<2, int, packed_mediump> packed_mediump_ivec2; + + /// 2 components vector tightly packed in memory of signed integer numbers. + typedef vec<2, int, packed_lowp> packed_lowp_ivec2; + + /// 2 components vector tightly packed in memory of unsigned integer numbers. + typedef vec<2, uint, packed_highp> packed_highp_uvec2; + + /// 2 components vector tightly packed in memory of unsigned integer numbers. + typedef vec<2, uint, packed_mediump> packed_mediump_uvec2; + + /// 2 components vector tightly packed in memory of unsigned integer numbers. + typedef vec<2, uint, packed_lowp> packed_lowp_uvec2; + + /// 2 components vector tightly packed in memory of bool values. + typedef vec<2, bool, packed_highp> packed_highp_bvec2; + + /// 2 components vector tightly packed in memory of bool values. + typedef vec<2, bool, packed_mediump> packed_mediump_bvec2; + + /// 2 components vector tightly packed in memory of bool values. + typedef vec<2, bool, packed_lowp> packed_lowp_bvec2; + + // -- *vec3 -- + + /// 3 components vector aligned in memory of single-precision floating-point numbers using high precision arithmetic in term of ULPs. + typedef vec<3, float, aligned_highp> aligned_highp_vec3; + + /// 3 components vector aligned in memory of single-precision floating-point numbers using medium precision arithmetic in term of ULPs. + typedef vec<3, float, aligned_mediump> aligned_mediump_vec3; + + /// 3 components vector aligned in memory of single-precision floating-point numbers using low precision arithmetic in term of ULPs. + typedef vec<3, float, aligned_lowp> aligned_lowp_vec3; + + /// 3 components vector aligned in memory of double-precision floating-point numbers using high precision arithmetic in term of ULPs. + typedef vec<3, double, aligned_highp> aligned_highp_dvec3; + + /// 3 components vector aligned in memory of double-precision floating-point numbers using medium precision arithmetic in term of ULPs. + typedef vec<3, double, aligned_mediump> aligned_mediump_dvec3; + + /// 3 components vector aligned in memory of double-precision floating-point numbers using low precision arithmetic in term of ULPs. + typedef vec<3, double, aligned_lowp> aligned_lowp_dvec3; + + /// 3 components vector aligned in memory of signed integer numbers. + typedef vec<3, int, aligned_highp> aligned_highp_ivec3; + + /// 3 components vector aligned in memory of signed integer numbers. + typedef vec<3, int, aligned_mediump> aligned_mediump_ivec3; + + /// 3 components vector aligned in memory of signed integer numbers. + typedef vec<3, int, aligned_lowp> aligned_lowp_ivec3; + + /// 3 components vector aligned in memory of unsigned integer numbers. + typedef vec<3, uint, aligned_highp> aligned_highp_uvec3; + + /// 3 components vector aligned in memory of unsigned integer numbers. + typedef vec<3, uint, aligned_mediump> aligned_mediump_uvec3; + + /// 3 components vector aligned in memory of unsigned integer numbers. + typedef vec<3, uint, aligned_lowp> aligned_lowp_uvec3; + + /// 3 components vector aligned in memory of bool values. + typedef vec<3, bool, aligned_highp> aligned_highp_bvec3; + + /// 3 components vector aligned in memory of bool values. + typedef vec<3, bool, aligned_mediump> aligned_mediump_bvec3; + + /// 3 components vector aligned in memory of bool values. + typedef vec<3, bool, aligned_lowp> aligned_lowp_bvec3; + + /// 3 components vector tightly packed in memory of single-precision floating-point numbers using high precision arithmetic in term of ULPs. + typedef vec<3, float, packed_highp> packed_highp_vec3; + + /// 3 components vector tightly packed in memory of single-precision floating-point numbers using medium precision arithmetic in term of ULPs. + typedef vec<3, float, packed_mediump> packed_mediump_vec3; + + /// 3 components vector tightly packed in memory of single-precision floating-point numbers using low precision arithmetic in term of ULPs. + typedef vec<3, float, packed_lowp> packed_lowp_vec3; + + /// 3 components vector tightly packed in memory of double-precision floating-point numbers using high precision arithmetic in term of ULPs. + typedef vec<3, double, packed_highp> packed_highp_dvec3; + + /// 3 components vector tightly packed in memory of double-precision floating-point numbers using medium precision arithmetic in term of ULPs. + typedef vec<3, double, packed_mediump> packed_mediump_dvec3; + + /// 3 components vector tightly packed in memory of double-precision floating-point numbers using low precision arithmetic in term of ULPs. + typedef vec<3, double, packed_lowp> packed_lowp_dvec3; + + /// 3 components vector tightly packed in memory of signed integer numbers. + typedef vec<3, int, packed_highp> packed_highp_ivec3; + + /// 3 components vector tightly packed in memory of signed integer numbers. + typedef vec<3, int, packed_mediump> packed_mediump_ivec3; + + /// 3 components vector tightly packed in memory of signed integer numbers. + typedef vec<3, int, packed_lowp> packed_lowp_ivec3; + + /// 3 components vector tightly packed in memory of unsigned integer numbers. + typedef vec<3, uint, packed_highp> packed_highp_uvec3; + + /// 3 components vector tightly packed in memory of unsigned integer numbers. + typedef vec<3, uint, packed_mediump> packed_mediump_uvec3; + + /// 3 components vector tightly packed in memory of unsigned integer numbers. + typedef vec<3, uint, packed_lowp> packed_lowp_uvec3; + + /// 3 components vector tightly packed in memory of bool values. + typedef vec<3, bool, packed_highp> packed_highp_bvec3; + + /// 3 components vector tightly packed in memory of bool values. + typedef vec<3, bool, packed_mediump> packed_mediump_bvec3; + + /// 3 components vector tightly packed in memory of bool values. + typedef vec<3, bool, packed_lowp> packed_lowp_bvec3; + + // -- *vec4 -- + + /// 4 components vector aligned in memory of single-precision floating-point numbers using high precision arithmetic in term of ULPs. + typedef vec<4, float, aligned_highp> aligned_highp_vec4; + + /// 4 components vector aligned in memory of single-precision floating-point numbers using medium precision arithmetic in term of ULPs. + typedef vec<4, float, aligned_mediump> aligned_mediump_vec4; + + /// 4 components vector aligned in memory of single-precision floating-point numbers using low precision arithmetic in term of ULPs. + typedef vec<4, float, aligned_lowp> aligned_lowp_vec4; + + /// 4 components vector aligned in memory of double-precision floating-point numbers using high precision arithmetic in term of ULPs. + typedef vec<4, double, aligned_highp> aligned_highp_dvec4; + + /// 4 components vector aligned in memory of double-precision floating-point numbers using medium precision arithmetic in term of ULPs. + typedef vec<4, double, aligned_mediump> aligned_mediump_dvec4; + + /// 4 components vector aligned in memory of double-precision floating-point numbers using low precision arithmetic in term of ULPs. + typedef vec<4, double, aligned_lowp> aligned_lowp_dvec4; + + /// 4 components vector aligned in memory of signed integer numbers. + typedef vec<4, int, aligned_highp> aligned_highp_ivec4; + + /// 4 components vector aligned in memory of signed integer numbers. + typedef vec<4, int, aligned_mediump> aligned_mediump_ivec4; + + /// 4 components vector aligned in memory of signed integer numbers. + typedef vec<4, int, aligned_lowp> aligned_lowp_ivec4; + + /// 4 components vector aligned in memory of unsigned integer numbers. + typedef vec<4, uint, aligned_highp> aligned_highp_uvec4; + + /// 4 components vector aligned in memory of unsigned integer numbers. + typedef vec<4, uint, aligned_mediump> aligned_mediump_uvec4; + + /// 4 components vector aligned in memory of unsigned integer numbers. + typedef vec<4, uint, aligned_lowp> aligned_lowp_uvec4; + + /// 4 components vector aligned in memory of bool values. + typedef vec<4, bool, aligned_highp> aligned_highp_bvec4; + + /// 4 components vector aligned in memory of bool values. + typedef vec<4, bool, aligned_mediump> aligned_mediump_bvec4; + + /// 4 components vector aligned in memory of bool values. + typedef vec<4, bool, aligned_lowp> aligned_lowp_bvec4; + + /// 4 components vector tightly packed in memory of single-precision floating-point numbers using high precision arithmetic in term of ULPs. + typedef vec<4, float, packed_highp> packed_highp_vec4; + + /// 4 components vector tightly packed in memory of single-precision floating-point numbers using medium precision arithmetic in term of ULPs. + typedef vec<4, float, packed_mediump> packed_mediump_vec4; + + /// 4 components vector tightly packed in memory of single-precision floating-point numbers using low precision arithmetic in term of ULPs. + typedef vec<4, float, packed_lowp> packed_lowp_vec4; + + /// 4 components vector tightly packed in memory of double-precision floating-point numbers using high precision arithmetic in term of ULPs. + typedef vec<4, double, packed_highp> packed_highp_dvec4; + + /// 4 components vector tightly packed in memory of double-precision floating-point numbers using medium precision arithmetic in term of ULPs. + typedef vec<4, double, packed_mediump> packed_mediump_dvec4; + + /// 4 components vector tightly packed in memory of double-precision floating-point numbers using low precision arithmetic in term of ULPs. + typedef vec<4, double, packed_lowp> packed_lowp_dvec4; + + /// 4 components vector tightly packed in memory of signed integer numbers. + typedef vec<4, int, packed_highp> packed_highp_ivec4; + + /// 4 components vector tightly packed in memory of signed integer numbers. + typedef vec<4, int, packed_mediump> packed_mediump_ivec4; + + /// 4 components vector tightly packed in memory of signed integer numbers. + typedef vec<4, int, packed_lowp> packed_lowp_ivec4; + + /// 4 components vector tightly packed in memory of unsigned integer numbers. + typedef vec<4, uint, packed_highp> packed_highp_uvec4; + + /// 4 components vector tightly packed in memory of unsigned integer numbers. + typedef vec<4, uint, packed_mediump> packed_mediump_uvec4; + + /// 4 components vector tightly packed in memory of unsigned integer numbers. + typedef vec<4, uint, packed_lowp> packed_lowp_uvec4; + + /// 4 components vector tightly packed in memory of bool values. + typedef vec<4, bool, packed_highp> packed_highp_bvec4; + + /// 4 components vector tightly packed in memory of bool values. + typedef vec<4, bool, packed_mediump> packed_mediump_bvec4; + + /// 4 components vector tightly packed in memory of bool values. + typedef vec<4, bool, packed_lowp> packed_lowp_bvec4; + + // -- *mat2 -- + + /// 2 by 2 matrix aligned in memory of single-precision floating-point numbers using high precision arithmetic in term of ULPs. + typedef mat<2, 2, float, aligned_highp> aligned_highp_mat2; + + /// 2 by 2 matrix aligned in memory of single-precision floating-point numbers using medium precision arithmetic in term of ULPs. + typedef mat<2, 2, float, aligned_mediump> aligned_mediump_mat2; + + /// 2 by 2 matrix aligned in memory of single-precision floating-point numbers using low precision arithmetic in term of ULPs. + typedef mat<2, 2, float, aligned_lowp> aligned_lowp_mat2; + + /// 2 by 2 matrix aligned in memory of double-precision floating-point numbers using high precision arithmetic in term of ULPs. + typedef mat<2, 2, double, aligned_highp> aligned_highp_dmat2; + + /// 2 by 2 matrix aligned in memory of double-precision floating-point numbers using medium precision arithmetic in term of ULPs. + typedef mat<2, 2, double, aligned_mediump> aligned_mediump_dmat2; + + /// 2 by 2 matrix aligned in memory of double-precision floating-point numbers using low precision arithmetic in term of ULPs. + typedef mat<2, 2, double, aligned_lowp> aligned_lowp_dmat2; + + /// 2 by 2 matrix tightly packed in memory of single-precision floating-point numbers using high precision arithmetic in term of ULPs. + typedef mat<2, 2, float, packed_highp> packed_highp_mat2; + + /// 2 by 2 matrix tightly packed in memory of single-precision floating-point numbers using medium precision arithmetic in term of ULPs. + typedef mat<2, 2, float, packed_mediump> packed_mediump_mat2; + + /// 2 by 2 matrix tightly packed in memory of single-precision floating-point numbers using low precision arithmetic in term of ULPs. + typedef mat<2, 2, float, packed_lowp> packed_lowp_mat2; + + /// 2 by 2 matrix tightly packed in memory of double-precision floating-point numbers using high precision arithmetic in term of ULPs. + typedef mat<2, 2, double, packed_highp> packed_highp_dmat2; + + /// 2 by 2 matrix tightly packed in memory of double-precision floating-point numbers using medium precision arithmetic in term of ULPs. + typedef mat<2, 2, double, packed_mediump> packed_mediump_dmat2; + + /// 2 by 2 matrix tightly packed in memory of double-precision floating-point numbers using low precision arithmetic in term of ULPs. + typedef mat<2, 2, double, packed_lowp> packed_lowp_dmat2; + + // -- *mat3 -- + + /// 3 by 3 matrix aligned in memory of single-precision floating-point numbers using high precision arithmetic in term of ULPs. + typedef mat<3, 3, float, aligned_highp> aligned_highp_mat3; + + /// 3 by 3 matrix aligned in memory of single-precision floating-point numbers using medium precision arithmetic in term of ULPs. + typedef mat<3, 3, float, aligned_mediump> aligned_mediump_mat3; + + /// 3 by 3 matrix aligned in memory of single-precision floating-point numbers using low precision arithmetic in term of ULPs. + typedef mat<3, 3, float, aligned_lowp> aligned_lowp_mat3; + + /// 3 by 3 matrix aligned in memory of double-precision floating-point numbers using high precision arithmetic in term of ULPs. + typedef mat<3, 3, double, aligned_highp> aligned_highp_dmat3; + + /// 3 by 3 matrix aligned in memory of double-precision floating-point numbers using medium precision arithmetic in term of ULPs. + typedef mat<3, 3, double, aligned_mediump> aligned_mediump_dmat3; + + /// 3 by 3 matrix aligned in memory of double-precision floating-point numbers using low precision arithmetic in term of ULPs. + typedef mat<3, 3, double, aligned_lowp> aligned_lowp_dmat3; + + /// 3 by 3 matrix tightly packed in memory of single-precision floating-point numbers using high precision arithmetic in term of ULPs. + typedef mat<3, 3, float, packed_highp> packed_highp_mat3; + + /// 3 by 3 matrix tightly packed in memory of single-precision floating-point numbers using medium precision arithmetic in term of ULPs. + typedef mat<3, 3, float, packed_mediump> packed_mediump_mat3; + + /// 3 by 3 matrix tightly packed in memory of single-precision floating-point numbers using low precision arithmetic in term of ULPs. + typedef mat<3, 3, float, packed_lowp> packed_lowp_mat3; + + /// 3 by 3 matrix tightly packed in memory of double-precision floating-point numbers using high precision arithmetic in term of ULPs. + typedef mat<3, 3, double, packed_highp> packed_highp_dmat3; + + /// 3 by 3 matrix tightly packed in memory of double-precision floating-point numbers using medium precision arithmetic in term of ULPs. + typedef mat<3, 3, double, packed_mediump> packed_mediump_dmat3; + + /// 3 by 3 matrix tightly packed in memory of double-precision floating-point numbers using low precision arithmetic in term of ULPs. + typedef mat<3, 3, double, packed_lowp> packed_lowp_dmat3; + + // -- *mat4 -- + + /// 4 by 4 matrix aligned in memory of single-precision floating-point numbers using high precision arithmetic in term of ULPs. + typedef mat<4, 4, float, aligned_highp> aligned_highp_mat4; + + /// 4 by 4 matrix aligned in memory of single-precision floating-point numbers using medium precision arithmetic in term of ULPs. + typedef mat<4, 4, float, aligned_mediump> aligned_mediump_mat4; + + /// 4 by 4 matrix aligned in memory of single-precision floating-point numbers using low precision arithmetic in term of ULPs. + typedef mat<4, 4, float, aligned_lowp> aligned_lowp_mat4; + + /// 4 by 4 matrix aligned in memory of double-precision floating-point numbers using high precision arithmetic in term of ULPs. + typedef mat<4, 4, double, aligned_highp> aligned_highp_dmat4; + + /// 4 by 4 matrix aligned in memory of double-precision floating-point numbers using medium precision arithmetic in term of ULPs. + typedef mat<4, 4, double, aligned_mediump> aligned_mediump_dmat4; + + /// 4 by 4 matrix aligned in memory of double-precision floating-point numbers using low precision arithmetic in term of ULPs. + typedef mat<4, 4, double, aligned_lowp> aligned_lowp_dmat4; + + /// 4 by 4 matrix tightly packed in memory of single-precision floating-point numbers using high precision arithmetic in term of ULPs. + typedef mat<4, 4, float, packed_highp> packed_highp_mat4; + + /// 4 by 4 matrix tightly packed in memory of single-precision floating-point numbers using medium precision arithmetic in term of ULPs. + typedef mat<4, 4, float, packed_mediump> packed_mediump_mat4; + + /// 4 by 4 matrix tightly packed in memory of single-precision floating-point numbers using low precision arithmetic in term of ULPs. + typedef mat<4, 4, float, packed_lowp> packed_lowp_mat4; + + /// 4 by 4 matrix tightly packed in memory of double-precision floating-point numbers using high precision arithmetic in term of ULPs. + typedef mat<4, 4, double, packed_highp> packed_highp_dmat4; + + /// 4 by 4 matrix tightly packed in memory of double-precision floating-point numbers using medium precision arithmetic in term of ULPs. + typedef mat<4, 4, double, packed_mediump> packed_mediump_dmat4; + + /// 4 by 4 matrix tightly packed in memory of double-precision floating-point numbers using low precision arithmetic in term of ULPs. + typedef mat<4, 4, double, packed_lowp> packed_lowp_dmat4; + + // -- *mat2x2 -- + + /// 2 by 2 matrix aligned in memory of single-precision floating-point numbers using high precision arithmetic in term of ULPs. + typedef mat<2, 2, float, aligned_highp> aligned_highp_mat2x2; + + /// 2 by 2 matrix aligned in memory of single-precision floating-point numbers using medium precision arithmetic in term of ULPs. + typedef mat<2, 2, float, aligned_mediump> aligned_mediump_mat2x2; + + /// 2 by 2 matrix aligned in memory of single-precision floating-point numbers using low precision arithmetic in term of ULPs. + typedef mat<2, 2, float, aligned_lowp> aligned_lowp_mat2x2; + + /// 2 by 2 matrix aligned in memory of double-precision floating-point numbers using high precision arithmetic in term of ULPs. + typedef mat<2, 2, double, aligned_highp> aligned_highp_dmat2x2; + + /// 2 by 2 matrix aligned in memory of double-precision floating-point numbers using medium precision arithmetic in term of ULPs. + typedef mat<2, 2, double, aligned_mediump> aligned_mediump_dmat2x2; + + /// 2 by 2 matrix aligned in memory of double-precision floating-point numbers using low precision arithmetic in term of ULPs. + typedef mat<2, 2, double, aligned_lowp> aligned_lowp_dmat2x2; + + /// 2 by 2 matrix tightly packed in memory of single-precision floating-point numbers using high precision arithmetic in term of ULPs. + typedef mat<2, 2, float, packed_highp> packed_highp_mat2x2; + + /// 2 by 2 matrix tightly packed in memory of single-precision floating-point numbers using medium precision arithmetic in term of ULPs. + typedef mat<2, 2, float, packed_mediump> packed_mediump_mat2x2; + + /// 2 by 2 matrix tightly packed in memory of single-precision floating-point numbers using low precision arithmetic in term of ULPs. + typedef mat<2, 2, float, packed_lowp> packed_lowp_mat2x2; + + /// 2 by 2 matrix tightly packed in memory of double-precision floating-point numbers using high precision arithmetic in term of ULPs. + typedef mat<2, 2, double, packed_highp> packed_highp_dmat2x2; + + /// 2 by 2 matrix tightly packed in memory of double-precision floating-point numbers using medium precision arithmetic in term of ULPs. + typedef mat<2, 2, double, packed_mediump> packed_mediump_dmat2x2; + + /// 2 by 2 matrix tightly packed in memory of double-precision floating-point numbers using low precision arithmetic in term of ULPs. + typedef mat<2, 2, double, packed_lowp> packed_lowp_dmat2x2; + + // -- *mat2x3 -- + + /// 2 by 3 matrix aligned in memory of single-precision floating-point numbers using high precision arithmetic in term of ULPs. + typedef mat<2, 3, float, aligned_highp> aligned_highp_mat2x3; + + /// 2 by 3 matrix aligned in memory of single-precision floating-point numbers using medium precision arithmetic in term of ULPs. + typedef mat<2, 3, float, aligned_mediump> aligned_mediump_mat2x3; + + /// 2 by 3 matrix aligned in memory of single-precision floating-point numbers using low precision arithmetic in term of ULPs. + typedef mat<2, 3, float, aligned_lowp> aligned_lowp_mat2x3; + + /// 2 by 3 matrix aligned in memory of double-precision floating-point numbers using high precision arithmetic in term of ULPs. + typedef mat<2, 3, double, aligned_highp> aligned_highp_dmat2x3; + + /// 2 by 3 matrix aligned in memory of double-precision floating-point numbers using medium precision arithmetic in term of ULPs. + typedef mat<2, 3, double, aligned_mediump> aligned_mediump_dmat2x3; + + /// 2 by 3 matrix aligned in memory of double-precision floating-point numbers using low precision arithmetic in term of ULPs. + typedef mat<2, 3, double, aligned_lowp> aligned_lowp_dmat2x3; + + /// 2 by 3 matrix tightly packed in memory of single-precision floating-point numbers using high precision arithmetic in term of ULPs. + typedef mat<2, 3, float, packed_highp> packed_highp_mat2x3; + + /// 2 by 3 matrix tightly packed in memory of single-precision floating-point numbers using medium precision arithmetic in term of ULPs. + typedef mat<2, 3, float, packed_mediump> packed_mediump_mat2x3; + + /// 2 by 3 matrix tightly packed in memory of single-precision floating-point numbers using low precision arithmetic in term of ULPs. + typedef mat<2, 3, float, packed_lowp> packed_lowp_mat2x3; + + /// 2 by 3 matrix tightly packed in memory of double-precision floating-point numbers using high precision arithmetic in term of ULPs. + typedef mat<2, 3, double, packed_highp> packed_highp_dmat2x3; + + /// 2 by 3 matrix tightly packed in memory of double-precision floating-point numbers using medium precision arithmetic in term of ULPs. + typedef mat<2, 3, double, packed_mediump> packed_mediump_dmat2x3; + + /// 2 by 3 matrix tightly packed in memory of double-precision floating-point numbers using low precision arithmetic in term of ULPs. + typedef mat<2, 3, double, packed_lowp> packed_lowp_dmat2x3; + + // -- *mat2x4 -- + + /// 2 by 4 matrix aligned in memory of single-precision floating-point numbers using high precision arithmetic in term of ULPs. + typedef mat<2, 4, float, aligned_highp> aligned_highp_mat2x4; + + /// 2 by 4 matrix aligned in memory of single-precision floating-point numbers using medium precision arithmetic in term of ULPs. + typedef mat<2, 4, float, aligned_mediump> aligned_mediump_mat2x4; + + /// 2 by 4 matrix aligned in memory of single-precision floating-point numbers using low precision arithmetic in term of ULPs. + typedef mat<2, 4, float, aligned_lowp> aligned_lowp_mat2x4; + + /// 2 by 4 matrix aligned in memory of double-precision floating-point numbers using high precision arithmetic in term of ULPs. + typedef mat<2, 4, double, aligned_highp> aligned_highp_dmat2x4; + + /// 2 by 4 matrix aligned in memory of double-precision floating-point numbers using medium precision arithmetic in term of ULPs. + typedef mat<2, 4, double, aligned_mediump> aligned_mediump_dmat2x4; + + /// 2 by 4 matrix aligned in memory of double-precision floating-point numbers using low precision arithmetic in term of ULPs. + typedef mat<2, 4, double, aligned_lowp> aligned_lowp_dmat2x4; + + /// 2 by 4 matrix tightly packed in memory of single-precision floating-point numbers using high precision arithmetic in term of ULPs. + typedef mat<2, 4, float, packed_highp> packed_highp_mat2x4; + + /// 2 by 4 matrix tightly packed in memory of single-precision floating-point numbers using medium precision arithmetic in term of ULPs. + typedef mat<2, 4, float, packed_mediump> packed_mediump_mat2x4; + + /// 2 by 4 matrix tightly packed in memory of single-precision floating-point numbers using low precision arithmetic in term of ULPs. + typedef mat<2, 4, float, packed_lowp> packed_lowp_mat2x4; + + /// 2 by 4 matrix tightly packed in memory of double-precision floating-point numbers using high precision arithmetic in term of ULPs. + typedef mat<2, 4, double, packed_highp> packed_highp_dmat2x4; + + /// 2 by 4 matrix tightly packed in memory of double-precision floating-point numbers using medium precision arithmetic in term of ULPs. + typedef mat<2, 4, double, packed_mediump> packed_mediump_dmat2x4; + + /// 2 by 4 matrix tightly packed in memory of double-precision floating-point numbers using low precision arithmetic in term of ULPs. + typedef mat<2, 4, double, packed_lowp> packed_lowp_dmat2x4; + + // -- *mat3x2 -- + + /// 3 by 2 matrix aligned in memory of single-precision floating-point numbers using high precision arithmetic in term of ULPs. + typedef mat<3, 2, float, aligned_highp> aligned_highp_mat3x2; + + /// 3 by 2 matrix aligned in memory of single-precision floating-point numbers using medium precision arithmetic in term of ULPs. + typedef mat<3, 2, float, aligned_mediump> aligned_mediump_mat3x2; + + /// 3 by 2 matrix aligned in memory of single-precision floating-point numbers using low precision arithmetic in term of ULPs. + typedef mat<3, 2, float, aligned_lowp> aligned_lowp_mat3x2; + + /// 3 by 2 matrix aligned in memory of double-precision floating-point numbers using high precision arithmetic in term of ULPs. + typedef mat<3, 2, double, aligned_highp> aligned_highp_dmat3x2; + + /// 3 by 2 matrix aligned in memory of double-precision floating-point numbers using medium precision arithmetic in term of ULPs. + typedef mat<3, 2, double, aligned_mediump> aligned_mediump_dmat3x2; + + /// 3 by 2 matrix aligned in memory of double-precision floating-point numbers using low precision arithmetic in term of ULPs. + typedef mat<3, 2, double, aligned_lowp> aligned_lowp_dmat3x2; + + /// 3 by 2 matrix tightly packed in memory of single-precision floating-point numbers using high precision arithmetic in term of ULPs. + typedef mat<3, 2, float, packed_highp> packed_highp_mat3x2; + + /// 3 by 2 matrix tightly packed in memory of single-precision floating-point numbers using medium precision arithmetic in term of ULPs. + typedef mat<3, 2, float, packed_mediump> packed_mediump_mat3x2; + + /// 3 by 2 matrix tightly packed in memory of single-precision floating-point numbers using low precision arithmetic in term of ULPs. + typedef mat<3, 2, float, packed_lowp> packed_lowp_mat3x2; + + /// 3 by 2 matrix tightly packed in memory of double-precision floating-point numbers using high precision arithmetic in term of ULPs. + typedef mat<3, 2, double, packed_highp> packed_highp_dmat3x2; + + /// 3 by 2 matrix tightly packed in memory of double-precision floating-point numbers using medium precision arithmetic in term of ULPs. + typedef mat<3, 2, double, packed_mediump> packed_mediump_dmat3x2; + + /// 3 by 2 matrix tightly packed in memory of double-precision floating-point numbers using low precision arithmetic in term of ULPs. + typedef mat<3, 2, double, packed_lowp> packed_lowp_dmat3x2; + + // -- *mat3x3 -- + + /// 3 by 3 matrix aligned in memory of single-precision floating-point numbers using high precision arithmetic in term of ULPs. + typedef mat<3, 3, float, aligned_highp> aligned_highp_mat3x3; + + /// 3 by 3 matrix aligned in memory of single-precision floating-point numbers using medium precision arithmetic in term of ULPs. + typedef mat<3, 3, float, aligned_mediump> aligned_mediump_mat3x3; + + /// 3 by 3 matrix aligned in memory of single-precision floating-point numbers using low precision arithmetic in term of ULPs. + typedef mat<3, 3, float, aligned_lowp> aligned_lowp_mat3x3; + + /// 3 by 3 matrix aligned in memory of double-precision floating-point numbers using high precision arithmetic in term of ULPs. + typedef mat<3, 3, double, aligned_highp> aligned_highp_dmat3x3; + + /// 3 by 3 matrix aligned in memory of double-precision floating-point numbers using medium precision arithmetic in term of ULPs. + typedef mat<3, 3, double, aligned_mediump> aligned_mediump_dmat3x3; + + /// 3 by 3 matrix aligned in memory of double-precision floating-point numbers using low precision arithmetic in term of ULPs. + typedef mat<3, 3, double, aligned_lowp> aligned_lowp_dmat3x3; + + /// 3 by 3 matrix tightly packed in memory of single-precision floating-point numbers using high precision arithmetic in term of ULPs. + typedef mat<3, 3, float, packed_highp> packed_highp_mat3x3; + + /// 3 by 3 matrix tightly packed in memory of single-precision floating-point numbers using medium precision arithmetic in term of ULPs. + typedef mat<3, 3, float, packed_mediump> packed_mediump_mat3x3; + + /// 3 by 3 matrix tightly packed in memory of single-precision floating-point numbers using low precision arithmetic in term of ULPs. + typedef mat<3, 3, float, packed_lowp> packed_lowp_mat3x3; + + /// 3 by 3 matrix tightly packed in memory of double-precision floating-point numbers using high precision arithmetic in term of ULPs. + typedef mat<3, 3, double, packed_highp> packed_highp_dmat3x3; + + /// 3 by 3 matrix tightly packed in memory of double-precision floating-point numbers using medium precision arithmetic in term of ULPs. + typedef mat<3, 3, double, packed_mediump> packed_mediump_dmat3x3; + + /// 3 by 3 matrix tightly packed in memory of double-precision floating-point numbers using low precision arithmetic in term of ULPs. + typedef mat<3, 3, double, packed_lowp> packed_lowp_dmat3x3; + + // -- *mat3x4 -- + + /// 3 by 4 matrix aligned in memory of single-precision floating-point numbers using high precision arithmetic in term of ULPs. + typedef mat<3, 4, float, aligned_highp> aligned_highp_mat3x4; + + /// 3 by 4 matrix aligned in memory of single-precision floating-point numbers using medium precision arithmetic in term of ULPs. + typedef mat<3, 4, float, aligned_mediump> aligned_mediump_mat3x4; + + /// 3 by 4 matrix aligned in memory of single-precision floating-point numbers using low precision arithmetic in term of ULPs. + typedef mat<3, 4, float, aligned_lowp> aligned_lowp_mat3x4; + + /// 3 by 4 matrix aligned in memory of double-precision floating-point numbers using high precision arithmetic in term of ULPs. + typedef mat<3, 4, double, aligned_highp> aligned_highp_dmat3x4; + + /// 3 by 4 matrix aligned in memory of double-precision floating-point numbers using medium precision arithmetic in term of ULPs. + typedef mat<3, 4, double, aligned_mediump> aligned_mediump_dmat3x4; + + /// 3 by 4 matrix aligned in memory of double-precision floating-point numbers using low precision arithmetic in term of ULPs. + typedef mat<3, 4, double, aligned_lowp> aligned_lowp_dmat3x4; + + /// 3 by 4 matrix tightly packed in memory of single-precision floating-point numbers using high precision arithmetic in term of ULPs. + typedef mat<3, 4, float, packed_highp> packed_highp_mat3x4; + + /// 3 by 4 matrix tightly packed in memory of single-precision floating-point numbers using medium precision arithmetic in term of ULPs. + typedef mat<3, 4, float, packed_mediump> packed_mediump_mat3x4; + + /// 3 by 4 matrix tightly packed in memory of single-precision floating-point numbers using low precision arithmetic in term of ULPs. + typedef mat<3, 4, float, packed_lowp> packed_lowp_mat3x4; + + /// 3 by 4 matrix tightly packed in memory of double-precision floating-point numbers using high precision arithmetic in term of ULPs. + typedef mat<3, 4, double, packed_highp> packed_highp_dmat3x4; + + /// 3 by 4 matrix tightly packed in memory of double-precision floating-point numbers using medium precision arithmetic in term of ULPs. + typedef mat<3, 4, double, packed_mediump> packed_mediump_dmat3x4; + + /// 3 by 4 matrix tightly packed in memory of double-precision floating-point numbers using low precision arithmetic in term of ULPs. + typedef mat<3, 4, double, packed_lowp> packed_lowp_dmat3x4; + + // -- *mat4x2 -- + + /// 4 by 2 matrix aligned in memory of single-precision floating-point numbers using high precision arithmetic in term of ULPs. + typedef mat<4, 2, float, aligned_highp> aligned_highp_mat4x2; + + /// 4 by 2 matrix aligned in memory of single-precision floating-point numbers using medium precision arithmetic in term of ULPs. + typedef mat<4, 2, float, aligned_mediump> aligned_mediump_mat4x2; + + /// 4 by 2 matrix aligned in memory of single-precision floating-point numbers using low precision arithmetic in term of ULPs. + typedef mat<4, 2, float, aligned_lowp> aligned_lowp_mat4x2; + + /// 4 by 2 matrix aligned in memory of double-precision floating-point numbers using high precision arithmetic in term of ULPs. + typedef mat<4, 2, double, aligned_highp> aligned_highp_dmat4x2; + + /// 4 by 2 matrix aligned in memory of double-precision floating-point numbers using medium precision arithmetic in term of ULPs. + typedef mat<4, 2, double, aligned_mediump> aligned_mediump_dmat4x2; + + /// 4 by 2 matrix aligned in memory of double-precision floating-point numbers using low precision arithmetic in term of ULPs. + typedef mat<4, 2, double, aligned_lowp> aligned_lowp_dmat4x2; + + /// 4 by 2 matrix tightly packed in memory of single-precision floating-point numbers using high precision arithmetic in term of ULPs. + typedef mat<4, 2, float, packed_highp> packed_highp_mat4x2; + + /// 4 by 2 matrix tightly packed in memory of single-precision floating-point numbers using medium precision arithmetic in term of ULPs. + typedef mat<4, 2, float, packed_mediump> packed_mediump_mat4x2; + + /// 4 by 2 matrix tightly packed in memory of single-precision floating-point numbers using low precision arithmetic in term of ULPs. + typedef mat<4, 2, float, packed_lowp> packed_lowp_mat4x2; + + /// 4 by 2 matrix tightly packed in memory of double-precision floating-point numbers using high precision arithmetic in term of ULPs. + typedef mat<4, 2, double, packed_highp> packed_highp_dmat4x2; + + /// 4 by 2 matrix tightly packed in memory of double-precision floating-point numbers using medium precision arithmetic in term of ULPs. + typedef mat<4, 2, double, packed_mediump> packed_mediump_dmat4x2; + + /// 4 by 2 matrix tightly packed in memory of double-precision floating-point numbers using low precision arithmetic in term of ULPs. + typedef mat<4, 2, double, packed_lowp> packed_lowp_dmat4x2; + + // -- *mat4x3 -- + + /// 4 by 3 matrix aligned in memory of single-precision floating-point numbers using high precision arithmetic in term of ULPs. + typedef mat<4, 3, float, aligned_highp> aligned_highp_mat4x3; + + /// 4 by 3 matrix aligned in memory of single-precision floating-point numbers using medium precision arithmetic in term of ULPs. + typedef mat<4, 3, float, aligned_mediump> aligned_mediump_mat4x3; + + /// 4 by 3 matrix aligned in memory of single-precision floating-point numbers using low precision arithmetic in term of ULPs. + typedef mat<4, 3, float, aligned_lowp> aligned_lowp_mat4x3; + + /// 4 by 3 matrix aligned in memory of double-precision floating-point numbers using high precision arithmetic in term of ULPs. + typedef mat<4, 3, double, aligned_highp> aligned_highp_dmat4x3; + + /// 4 by 3 matrix aligned in memory of double-precision floating-point numbers using medium precision arithmetic in term of ULPs. + typedef mat<4, 3, double, aligned_mediump> aligned_mediump_dmat4x3; + + /// 4 by 3 matrix aligned in memory of double-precision floating-point numbers using low precision arithmetic in term of ULPs. + typedef mat<4, 3, double, aligned_lowp> aligned_lowp_dmat4x3; + + /// 4 by 3 matrix tightly packed in memory of single-precision floating-point numbers using high precision arithmetic in term of ULPs. + typedef mat<4, 3, float, packed_highp> packed_highp_mat4x3; + + /// 4 by 3 matrix tightly packed in memory of single-precision floating-point numbers using medium precision arithmetic in term of ULPs. + typedef mat<4, 3, float, packed_mediump> packed_mediump_mat4x3; + + /// 4 by 3 matrix tightly packed in memory of single-precision floating-point numbers using low precision arithmetic in term of ULPs. + typedef mat<4, 3, float, packed_lowp> packed_lowp_mat4x3; + + /// 4 by 3 matrix tightly packed in memory of double-precision floating-point numbers using high precision arithmetic in term of ULPs. + typedef mat<4, 3, double, packed_highp> packed_highp_dmat4x3; + + /// 4 by 3 matrix tightly packed in memory of double-precision floating-point numbers using medium precision arithmetic in term of ULPs. + typedef mat<4, 3, double, packed_mediump> packed_mediump_dmat4x3; + + /// 4 by 3 matrix tightly packed in memory of double-precision floating-point numbers using low precision arithmetic in term of ULPs. + typedef mat<4, 3, double, packed_lowp> packed_lowp_dmat4x3; + + // -- *mat4x4 -- + + /// 4 by 4 matrix aligned in memory of single-precision floating-point numbers using high precision arithmetic in term of ULPs. + typedef mat<4, 4, float, aligned_highp> aligned_highp_mat4x4; + + /// 4 by 4 matrix aligned in memory of single-precision floating-point numbers using medium precision arithmetic in term of ULPs. + typedef mat<4, 4, float, aligned_mediump> aligned_mediump_mat4x4; + + /// 4 by 4 matrix aligned in memory of single-precision floating-point numbers using low precision arithmetic in term of ULPs. + typedef mat<4, 4, float, aligned_lowp> aligned_lowp_mat4x4; + + /// 4 by 4 matrix aligned in memory of double-precision floating-point numbers using high precision arithmetic in term of ULPs. + typedef mat<4, 4, double, aligned_highp> aligned_highp_dmat4x4; + + /// 4 by 4 matrix aligned in memory of double-precision floating-point numbers using medium precision arithmetic in term of ULPs. + typedef mat<4, 4, double, aligned_mediump> aligned_mediump_dmat4x4; + + /// 4 by 4 matrix aligned in memory of double-precision floating-point numbers using low precision arithmetic in term of ULPs. + typedef mat<4, 4, double, aligned_lowp> aligned_lowp_dmat4x4; + + /// 4 by 4 matrix tightly packed in memory of single-precision floating-point numbers using high precision arithmetic in term of ULPs. + typedef mat<4, 4, float, packed_highp> packed_highp_mat4x4; + + /// 4 by 4 matrix tightly packed in memory of single-precision floating-point numbers using medium precision arithmetic in term of ULPs. + typedef mat<4, 4, float, packed_mediump> packed_mediump_mat4x4; + + /// 4 by 4 matrix tightly packed in memory of single-precision floating-point numbers using low precision arithmetic in term of ULPs. + typedef mat<4, 4, float, packed_lowp> packed_lowp_mat4x4; + + /// 4 by 4 matrix tightly packed in memory of double-precision floating-point numbers using high precision arithmetic in term of ULPs. + typedef mat<4, 4, double, packed_highp> packed_highp_dmat4x4; + + /// 4 by 4 matrix tightly packed in memory of double-precision floating-point numbers using medium precision arithmetic in term of ULPs. + typedef mat<4, 4, double, packed_mediump> packed_mediump_dmat4x4; + + /// 4 by 4 matrix tightly packed in memory of double-precision floating-point numbers using low precision arithmetic in term of ULPs. + typedef mat<4, 4, double, packed_lowp> packed_lowp_dmat4x4; + + // -- default -- + +#if(defined(GLM_PRECISION_LOWP_FLOAT)) + typedef aligned_lowp_vec1 aligned_vec1; + typedef aligned_lowp_vec2 aligned_vec2; + typedef aligned_lowp_vec3 aligned_vec3; + typedef aligned_lowp_vec4 aligned_vec4; + typedef packed_lowp_vec1 packed_vec1; + typedef packed_lowp_vec2 packed_vec2; + typedef packed_lowp_vec3 packed_vec3; + typedef packed_lowp_vec4 packed_vec4; + + typedef aligned_lowp_mat2 aligned_mat2; + typedef aligned_lowp_mat3 aligned_mat3; + typedef aligned_lowp_mat4 aligned_mat4; + typedef packed_lowp_mat2 packed_mat2; + typedef packed_lowp_mat3 packed_mat3; + typedef packed_lowp_mat4 packed_mat4; + + typedef aligned_lowp_mat2x2 aligned_mat2x2; + typedef aligned_lowp_mat2x3 aligned_mat2x3; + typedef aligned_lowp_mat2x4 aligned_mat2x4; + typedef aligned_lowp_mat3x2 aligned_mat3x2; + typedef aligned_lowp_mat3x3 aligned_mat3x3; + typedef aligned_lowp_mat3x4 aligned_mat3x4; + typedef aligned_lowp_mat4x2 aligned_mat4x2; + typedef aligned_lowp_mat4x3 aligned_mat4x3; + typedef aligned_lowp_mat4x4 aligned_mat4x4; + typedef packed_lowp_mat2x2 packed_mat2x2; + typedef packed_lowp_mat2x3 packed_mat2x3; + typedef packed_lowp_mat2x4 packed_mat2x4; + typedef packed_lowp_mat3x2 packed_mat3x2; + typedef packed_lowp_mat3x3 packed_mat3x3; + typedef packed_lowp_mat3x4 packed_mat3x4; + typedef packed_lowp_mat4x2 packed_mat4x2; + typedef packed_lowp_mat4x3 packed_mat4x3; + typedef packed_lowp_mat4x4 packed_mat4x4; +#elif(defined(GLM_PRECISION_MEDIUMP_FLOAT)) + typedef aligned_mediump_vec1 aligned_vec1; + typedef aligned_mediump_vec2 aligned_vec2; + typedef aligned_mediump_vec3 aligned_vec3; + typedef aligned_mediump_vec4 aligned_vec4; + typedef packed_mediump_vec1 packed_vec1; + typedef packed_mediump_vec2 packed_vec2; + typedef packed_mediump_vec3 packed_vec3; + typedef packed_mediump_vec4 packed_vec4; + + typedef aligned_mediump_mat2 aligned_mat2; + typedef aligned_mediump_mat3 aligned_mat3; + typedef aligned_mediump_mat4 aligned_mat4; + typedef packed_mediump_mat2 packed_mat2; + typedef packed_mediump_mat3 packed_mat3; + typedef packed_mediump_mat4 packed_mat4; + + typedef aligned_mediump_mat2x2 aligned_mat2x2; + typedef aligned_mediump_mat2x3 aligned_mat2x3; + typedef aligned_mediump_mat2x4 aligned_mat2x4; + typedef aligned_mediump_mat3x2 aligned_mat3x2; + typedef aligned_mediump_mat3x3 aligned_mat3x3; + typedef aligned_mediump_mat3x4 aligned_mat3x4; + typedef aligned_mediump_mat4x2 aligned_mat4x2; + typedef aligned_mediump_mat4x3 aligned_mat4x3; + typedef aligned_mediump_mat4x4 aligned_mat4x4; + typedef packed_mediump_mat2x2 packed_mat2x2; + typedef packed_mediump_mat2x3 packed_mat2x3; + typedef packed_mediump_mat2x4 packed_mat2x4; + typedef packed_mediump_mat3x2 packed_mat3x2; + typedef packed_mediump_mat3x3 packed_mat3x3; + typedef packed_mediump_mat3x4 packed_mat3x4; + typedef packed_mediump_mat4x2 packed_mat4x2; + typedef packed_mediump_mat4x3 packed_mat4x3; + typedef packed_mediump_mat4x4 packed_mat4x4; +#else //defined(GLM_PRECISION_HIGHP_FLOAT) + /// 1 component vector aligned in memory of single-precision floating-point numbers. + typedef aligned_highp_vec1 aligned_vec1; + + /// 2 components vector aligned in memory of single-precision floating-point numbers. + typedef aligned_highp_vec2 aligned_vec2; + + /// 3 components vector aligned in memory of single-precision floating-point numbers. + typedef aligned_highp_vec3 aligned_vec3; + + /// 4 components vector aligned in memory of single-precision floating-point numbers. + typedef aligned_highp_vec4 aligned_vec4; + + /// 1 component vector tightly packed in memory of single-precision floating-point numbers. + typedef packed_highp_vec1 packed_vec1; + + /// 2 components vector tightly packed in memory of single-precision floating-point numbers. + typedef packed_highp_vec2 packed_vec2; + + /// 3 components vector tightly packed in memory of single-precision floating-point numbers. + typedef packed_highp_vec3 packed_vec3; + + /// 4 components vector tightly packed in memory of single-precision floating-point numbers. + typedef packed_highp_vec4 packed_vec4; + + /// 2 by 2 matrix tightly aligned in memory of single-precision floating-point numbers. + typedef aligned_highp_mat2 aligned_mat2; + + /// 3 by 3 matrix tightly aligned in memory of single-precision floating-point numbers. + typedef aligned_highp_mat3 aligned_mat3; + + /// 4 by 4 matrix tightly aligned in memory of single-precision floating-point numbers. + typedef aligned_highp_mat4 aligned_mat4; + + /// 2 by 2 matrix tightly packed in memory of single-precision floating-point numbers. + typedef packed_highp_mat2 packed_mat2; + + /// 3 by 3 matrix tightly packed in memory of single-precision floating-point numbers. + typedef packed_highp_mat3 packed_mat3; + + /// 4 by 4 matrix tightly packed in memory of single-precision floating-point numbers. + typedef packed_highp_mat4 packed_mat4; + + /// 2 by 2 matrix tightly aligned in memory of single-precision floating-point numbers. + typedef aligned_highp_mat2x2 aligned_mat2x2; + + /// 2 by 3 matrix tightly aligned in memory of single-precision floating-point numbers. + typedef aligned_highp_mat2x3 aligned_mat2x3; + + /// 2 by 4 matrix tightly aligned in memory of single-precision floating-point numbers. + typedef aligned_highp_mat2x4 aligned_mat2x4; + + /// 3 by 2 matrix tightly aligned in memory of single-precision floating-point numbers. + typedef aligned_highp_mat3x2 aligned_mat3x2; + + /// 3 by 3 matrix tightly aligned in memory of single-precision floating-point numbers. + typedef aligned_highp_mat3x3 aligned_mat3x3; + + /// 3 by 4 matrix tightly aligned in memory of single-precision floating-point numbers. + typedef aligned_highp_mat3x4 aligned_mat3x4; + + /// 4 by 2 matrix tightly aligned in memory of single-precision floating-point numbers. + typedef aligned_highp_mat4x2 aligned_mat4x2; + + /// 4 by 3 matrix tightly aligned in memory of single-precision floating-point numbers. + typedef aligned_highp_mat4x3 aligned_mat4x3; + + /// 4 by 4 matrix tightly aligned in memory of single-precision floating-point numbers. + typedef aligned_highp_mat4x4 aligned_mat4x4; + + /// 2 by 2 matrix tightly packed in memory of single-precision floating-point numbers. + typedef packed_highp_mat2x2 packed_mat2x2; + + /// 2 by 3 matrix tightly packed in memory of single-precision floating-point numbers. + typedef packed_highp_mat2x3 packed_mat2x3; + + /// 2 by 4 matrix tightly packed in memory of single-precision floating-point numbers. + typedef packed_highp_mat2x4 packed_mat2x4; + + /// 3 by 2 matrix tightly packed in memory of single-precision floating-point numbers. + typedef packed_highp_mat3x2 packed_mat3x2; + + /// 3 by 3 matrix tightly packed in memory of single-precision floating-point numbers. + typedef packed_highp_mat3x3 packed_mat3x3; + + /// 3 by 4 matrix tightly packed in memory of single-precision floating-point numbers. + typedef packed_highp_mat3x4 packed_mat3x4; + + /// 4 by 2 matrix tightly packed in memory of single-precision floating-point numbers. + typedef packed_highp_mat4x2 packed_mat4x2; + + /// 4 by 3 matrix tightly packed in memory of single-precision floating-point numbers. + typedef packed_highp_mat4x3 packed_mat4x3; + + /// 4 by 4 matrix tightly packed in memory of single-precision floating-point numbers. + typedef packed_highp_mat4x4 packed_mat4x4; +#endif//GLM_PRECISION + +#if(defined(GLM_PRECISION_LOWP_DOUBLE)) + typedef aligned_lowp_dvec1 aligned_dvec1; + typedef aligned_lowp_dvec2 aligned_dvec2; + typedef aligned_lowp_dvec3 aligned_dvec3; + typedef aligned_lowp_dvec4 aligned_dvec4; + typedef packed_lowp_dvec1 packed_dvec1; + typedef packed_lowp_dvec2 packed_dvec2; + typedef packed_lowp_dvec3 packed_dvec3; + typedef packed_lowp_dvec4 packed_dvec4; + + typedef aligned_lowp_dmat2 aligned_dmat2; + typedef aligned_lowp_dmat3 aligned_dmat3; + typedef aligned_lowp_dmat4 aligned_dmat4; + typedef packed_lowp_dmat2 packed_dmat2; + typedef packed_lowp_dmat3 packed_dmat3; + typedef packed_lowp_dmat4 packed_dmat4; + + typedef aligned_lowp_dmat2x2 aligned_dmat2x2; + typedef aligned_lowp_dmat2x3 aligned_dmat2x3; + typedef aligned_lowp_dmat2x4 aligned_dmat2x4; + typedef aligned_lowp_dmat3x2 aligned_dmat3x2; + typedef aligned_lowp_dmat3x3 aligned_dmat3x3; + typedef aligned_lowp_dmat3x4 aligned_dmat3x4; + typedef aligned_lowp_dmat4x2 aligned_dmat4x2; + typedef aligned_lowp_dmat4x3 aligned_dmat4x3; + typedef aligned_lowp_dmat4x4 aligned_dmat4x4; + typedef packed_lowp_dmat2x2 packed_dmat2x2; + typedef packed_lowp_dmat2x3 packed_dmat2x3; + typedef packed_lowp_dmat2x4 packed_dmat2x4; + typedef packed_lowp_dmat3x2 packed_dmat3x2; + typedef packed_lowp_dmat3x3 packed_dmat3x3; + typedef packed_lowp_dmat3x4 packed_dmat3x4; + typedef packed_lowp_dmat4x2 packed_dmat4x2; + typedef packed_lowp_dmat4x3 packed_dmat4x3; + typedef packed_lowp_dmat4x4 packed_dmat4x4; +#elif(defined(GLM_PRECISION_MEDIUMP_DOUBLE)) + typedef aligned_mediump_dvec1 aligned_dvec1; + typedef aligned_mediump_dvec2 aligned_dvec2; + typedef aligned_mediump_dvec3 aligned_dvec3; + typedef aligned_mediump_dvec4 aligned_dvec4; + typedef packed_mediump_dvec1 packed_dvec1; + typedef packed_mediump_dvec2 packed_dvec2; + typedef packed_mediump_dvec3 packed_dvec3; + typedef packed_mediump_dvec4 packed_dvec4; + + typedef aligned_mediump_dmat2 aligned_dmat2; + typedef aligned_mediump_dmat3 aligned_dmat3; + typedef aligned_mediump_dmat4 aligned_dmat4; + typedef packed_mediump_dmat2 packed_dmat2; + typedef packed_mediump_dmat3 packed_dmat3; + typedef packed_mediump_dmat4 packed_dmat4; + + typedef aligned_mediump_dmat2x2 aligned_dmat2x2; + typedef aligned_mediump_dmat2x3 aligned_dmat2x3; + typedef aligned_mediump_dmat2x4 aligned_dmat2x4; + typedef aligned_mediump_dmat3x2 aligned_dmat3x2; + typedef aligned_mediump_dmat3x3 aligned_dmat3x3; + typedef aligned_mediump_dmat3x4 aligned_dmat3x4; + typedef aligned_mediump_dmat4x2 aligned_dmat4x2; + typedef aligned_mediump_dmat4x3 aligned_dmat4x3; + typedef aligned_mediump_dmat4x4 aligned_dmat4x4; + typedef packed_mediump_dmat2x2 packed_dmat2x2; + typedef packed_mediump_dmat2x3 packed_dmat2x3; + typedef packed_mediump_dmat2x4 packed_dmat2x4; + typedef packed_mediump_dmat3x2 packed_dmat3x2; + typedef packed_mediump_dmat3x3 packed_dmat3x3; + typedef packed_mediump_dmat3x4 packed_dmat3x4; + typedef packed_mediump_dmat4x2 packed_dmat4x2; + typedef packed_mediump_dmat4x3 packed_dmat4x3; + typedef packed_mediump_dmat4x4 packed_dmat4x4; +#else //defined(GLM_PRECISION_HIGHP_DOUBLE) + /// 1 component vector aligned in memory of double-precision floating-point numbers. + typedef aligned_highp_dvec1 aligned_dvec1; + + /// 2 components vector aligned in memory of double-precision floating-point numbers. + typedef aligned_highp_dvec2 aligned_dvec2; + + /// 3 components vector aligned in memory of double-precision floating-point numbers. + typedef aligned_highp_dvec3 aligned_dvec3; + + /// 4 components vector aligned in memory of double-precision floating-point numbers. + typedef aligned_highp_dvec4 aligned_dvec4; + + /// 1 component vector tightly packed in memory of double-precision floating-point numbers. + typedef packed_highp_dvec1 packed_dvec1; + + /// 2 components vector tightly packed in memory of double-precision floating-point numbers. + typedef packed_highp_dvec2 packed_dvec2; + + /// 3 components vector tightly packed in memory of double-precision floating-point numbers. + typedef packed_highp_dvec3 packed_dvec3; + + /// 4 components vector tightly packed in memory of double-precision floating-point numbers. + typedef packed_highp_dvec4 packed_dvec4; + + /// 2 by 2 matrix tightly aligned in memory of double-precision floating-point numbers. + typedef aligned_highp_dmat2 aligned_dmat2; + + /// 3 by 3 matrix tightly aligned in memory of double-precision floating-point numbers. + typedef aligned_highp_dmat3 aligned_dmat3; + + /// 4 by 4 matrix tightly aligned in memory of double-precision floating-point numbers. + typedef aligned_highp_dmat4 aligned_dmat4; + + /// 2 by 2 matrix tightly packed in memory of double-precision floating-point numbers. + typedef packed_highp_dmat2 packed_dmat2; + + /// 3 by 3 matrix tightly packed in memory of double-precision floating-point numbers. + typedef packed_highp_dmat3 packed_dmat3; + + /// 4 by 4 matrix tightly packed in memory of double-precision floating-point numbers. + typedef packed_highp_dmat4 packed_dmat4; + + /// 2 by 2 matrix tightly aligned in memory of double-precision floating-point numbers. + typedef aligned_highp_dmat2x2 aligned_dmat2x2; + + /// 2 by 3 matrix tightly aligned in memory of double-precision floating-point numbers. + typedef aligned_highp_dmat2x3 aligned_dmat2x3; + + /// 2 by 4 matrix tightly aligned in memory of double-precision floating-point numbers. + typedef aligned_highp_dmat2x4 aligned_dmat2x4; + + /// 3 by 2 matrix tightly aligned in memory of double-precision floating-point numbers. + typedef aligned_highp_dmat3x2 aligned_dmat3x2; + + /// 3 by 3 matrix tightly aligned in memory of double-precision floating-point numbers. + typedef aligned_highp_dmat3x3 aligned_dmat3x3; + + /// 3 by 4 matrix tightly aligned in memory of double-precision floating-point numbers. + typedef aligned_highp_dmat3x4 aligned_dmat3x4; + + /// 4 by 2 matrix tightly aligned in memory of double-precision floating-point numbers. + typedef aligned_highp_dmat4x2 aligned_dmat4x2; + + /// 4 by 3 matrix tightly aligned in memory of double-precision floating-point numbers. + typedef aligned_highp_dmat4x3 aligned_dmat4x3; + + /// 4 by 4 matrix tightly aligned in memory of double-precision floating-point numbers. + typedef aligned_highp_dmat4x4 aligned_dmat4x4; + + /// 2 by 2 matrix tightly packed in memory of double-precision floating-point numbers. + typedef packed_highp_dmat2x2 packed_dmat2x2; + + /// 2 by 3 matrix tightly packed in memory of double-precision floating-point numbers. + typedef packed_highp_dmat2x3 packed_dmat2x3; + + /// 2 by 4 matrix tightly packed in memory of double-precision floating-point numbers. + typedef packed_highp_dmat2x4 packed_dmat2x4; + + /// 3 by 2 matrix tightly packed in memory of double-precision floating-point numbers. + typedef packed_highp_dmat3x2 packed_dmat3x2; + + /// 3 by 3 matrix tightly packed in memory of double-precision floating-point numbers. + typedef packed_highp_dmat3x3 packed_dmat3x3; + + /// 3 by 4 matrix tightly packed in memory of double-precision floating-point numbers. + typedef packed_highp_dmat3x4 packed_dmat3x4; + + /// 4 by 2 matrix tightly packed in memory of double-precision floating-point numbers. + typedef packed_highp_dmat4x2 packed_dmat4x2; + + /// 4 by 3 matrix tightly packed in memory of double-precision floating-point numbers. + typedef packed_highp_dmat4x3 packed_dmat4x3; + + /// 4 by 4 matrix tightly packed in memory of double-precision floating-point numbers. + typedef packed_highp_dmat4x4 packed_dmat4x4; +#endif//GLM_PRECISION + +#if(defined(GLM_PRECISION_LOWP_INT)) + typedef aligned_lowp_ivec1 aligned_ivec1; + typedef aligned_lowp_ivec2 aligned_ivec2; + typedef aligned_lowp_ivec3 aligned_ivec3; + typedef aligned_lowp_ivec4 aligned_ivec4; +#elif(defined(GLM_PRECISION_MEDIUMP_INT)) + typedef aligned_mediump_ivec1 aligned_ivec1; + typedef aligned_mediump_ivec2 aligned_ivec2; + typedef aligned_mediump_ivec3 aligned_ivec3; + typedef aligned_mediump_ivec4 aligned_ivec4; +#else //defined(GLM_PRECISION_HIGHP_INT) + /// 1 component vector aligned in memory of signed integer numbers. + typedef aligned_highp_ivec1 aligned_ivec1; + + /// 2 components vector aligned in memory of signed integer numbers. + typedef aligned_highp_ivec2 aligned_ivec2; + + /// 3 components vector aligned in memory of signed integer numbers. + typedef aligned_highp_ivec3 aligned_ivec3; + + /// 4 components vector aligned in memory of signed integer numbers. + typedef aligned_highp_ivec4 aligned_ivec4; + + /// 1 component vector tightly packed in memory of signed integer numbers. + typedef packed_highp_ivec1 packed_ivec1; + + /// 2 components vector tightly packed in memory of signed integer numbers. + typedef packed_highp_ivec2 packed_ivec2; + + /// 3 components vector tightly packed in memory of signed integer numbers. + typedef packed_highp_ivec3 packed_ivec3; + + /// 4 components vector tightly packed in memory of signed integer numbers. + typedef packed_highp_ivec4 packed_ivec4; +#endif//GLM_PRECISION + + // -- Unsigned integer definition -- + +#if(defined(GLM_PRECISION_LOWP_UINT)) + typedef aligned_lowp_uvec1 aligned_uvec1; + typedef aligned_lowp_uvec2 aligned_uvec2; + typedef aligned_lowp_uvec3 aligned_uvec3; + typedef aligned_lowp_uvec4 aligned_uvec4; +#elif(defined(GLM_PRECISION_MEDIUMP_UINT)) + typedef aligned_mediump_uvec1 aligned_uvec1; + typedef aligned_mediump_uvec2 aligned_uvec2; + typedef aligned_mediump_uvec3 aligned_uvec3; + typedef aligned_mediump_uvec4 aligned_uvec4; +#else //defined(GLM_PRECISION_HIGHP_UINT) + /// 1 component vector aligned in memory of unsigned integer numbers. + typedef aligned_highp_uvec1 aligned_uvec1; + + /// 2 components vector aligned in memory of unsigned integer numbers. + typedef aligned_highp_uvec2 aligned_uvec2; + + /// 3 components vector aligned in memory of unsigned integer numbers. + typedef aligned_highp_uvec3 aligned_uvec3; + + /// 4 components vector aligned in memory of unsigned integer numbers. + typedef aligned_highp_uvec4 aligned_uvec4; + + /// 1 component vector tightly packed in memory of unsigned integer numbers. + typedef packed_highp_uvec1 packed_uvec1; + + /// 2 components vector tightly packed in memory of unsigned integer numbers. + typedef packed_highp_uvec2 packed_uvec2; + + /// 3 components vector tightly packed in memory of unsigned integer numbers. + typedef packed_highp_uvec3 packed_uvec3; + + /// 4 components vector tightly packed in memory of unsigned integer numbers. + typedef packed_highp_uvec4 packed_uvec4; +#endif//GLM_PRECISION + +#if(defined(GLM_PRECISION_LOWP_BOOL)) + typedef aligned_lowp_bvec1 aligned_bvec1; + typedef aligned_lowp_bvec2 aligned_bvec2; + typedef aligned_lowp_bvec3 aligned_bvec3; + typedef aligned_lowp_bvec4 aligned_bvec4; +#elif(defined(GLM_PRECISION_MEDIUMP_BOOL)) + typedef aligned_mediump_bvec1 aligned_bvec1; + typedef aligned_mediump_bvec2 aligned_bvec2; + typedef aligned_mediump_bvec3 aligned_bvec3; + typedef aligned_mediump_bvec4 aligned_bvec4; +#else //defined(GLM_PRECISION_HIGHP_BOOL) + /// 1 component vector aligned in memory of bool values. + typedef aligned_highp_bvec1 aligned_bvec1; + + /// 2 components vector aligned in memory of bool values. + typedef aligned_highp_bvec2 aligned_bvec2; + + /// 3 components vector aligned in memory of bool values. + typedef aligned_highp_bvec3 aligned_bvec3; + + /// 4 components vector aligned in memory of bool values. + typedef aligned_highp_bvec4 aligned_bvec4; + + /// 1 components vector tightly packed in memory of bool values. + typedef packed_highp_bvec1 packed_bvec1; + + /// 2 components vector tightly packed in memory of bool values. + typedef packed_highp_bvec2 packed_bvec2; + + /// 3 components vector tightly packed in memory of bool values. + typedef packed_highp_bvec3 packed_bvec3; + + /// 4 components vector tightly packed in memory of bool values. + typedef packed_highp_bvec4 packed_bvec4; +#endif//GLM_PRECISION + + /// @} +}//namespace glm diff --git a/src/GLMath/glm/gtc/type_precision.hpp b/src/GLMath/glm/gtc/type_precision.hpp new file mode 100644 index 000000000..250bc4f96 --- /dev/null +++ b/src/GLMath/glm/gtc/type_precision.hpp @@ -0,0 +1,2138 @@ +/// @ref gtc_type_precision +/// @file glm/gtc/type_precision.hpp +/// +/// @see core (dependence) +/// @see gtc_quaternion (dependence) +/// +/// @defgroup gtc_type_precision GLM_GTC_type_precision +/// @ingroup gtc +/// +/// Include to use the features of this extension. +/// +/// Defines specific C++-based qualifier types. + +#pragma once + +// Dependency: +#include "../gtc/quaternion.hpp" +#include "../gtc/vec1.hpp" +#include "../ext/scalar_int_sized.hpp" +#include "../ext/scalar_uint_sized.hpp" +#include "../detail/type_vec2.hpp" +#include "../detail/type_vec3.hpp" +#include "../detail/type_vec4.hpp" +#include "../detail/type_mat2x2.hpp" +#include "../detail/type_mat2x3.hpp" +#include "../detail/type_mat2x4.hpp" +#include "../detail/type_mat3x2.hpp" +#include "../detail/type_mat3x3.hpp" +#include "../detail/type_mat3x4.hpp" +#include "../detail/type_mat4x2.hpp" +#include "../detail/type_mat4x3.hpp" +#include "../detail/type_mat4x4.hpp" + +#if GLM_MESSAGES == GLM_ENABLE && !defined(GLM_EXT_INCLUDED) +# pragma message("GLM: GLM_GTC_type_precision extension included") +#endif + +namespace glm +{ + /////////////////////////// + // Signed int vector types + + /// @addtogroup gtc_type_precision + /// @{ + + /// Low qualifier 8 bit signed integer type. + /// @see gtc_type_precision + typedef detail::int8 lowp_int8; + + /// Low qualifier 16 bit signed integer type. + /// @see gtc_type_precision + typedef detail::int16 lowp_int16; + + /// Low qualifier 32 bit signed integer type. + /// @see gtc_type_precision + typedef detail::int32 lowp_int32; + + /// Low qualifier 64 bit signed integer type. + /// @see gtc_type_precision + typedef detail::int64 lowp_int64; + + /// Low qualifier 8 bit signed integer type. + /// @see gtc_type_precision + typedef detail::int8 lowp_int8_t; + + /// Low qualifier 16 bit signed integer type. + /// @see gtc_type_precision + typedef detail::int16 lowp_int16_t; + + /// Low qualifier 32 bit signed integer type. + /// @see gtc_type_precision + typedef detail::int32 lowp_int32_t; + + /// Low qualifier 64 bit signed integer type. + /// @see gtc_type_precision + typedef detail::int64 lowp_int64_t; + + /// Low qualifier 8 bit signed integer type. + /// @see gtc_type_precision + typedef detail::int8 lowp_i8; + + /// Low qualifier 16 bit signed integer type. + /// @see gtc_type_precision + typedef detail::int16 lowp_i16; + + /// Low qualifier 32 bit signed integer type. + /// @see gtc_type_precision + typedef detail::int32 lowp_i32; + + /// Low qualifier 64 bit signed integer type. + /// @see gtc_type_precision + typedef detail::int64 lowp_i64; + + /// Medium qualifier 8 bit signed integer type. + /// @see gtc_type_precision + typedef detail::int8 mediump_int8; + + /// Medium qualifier 16 bit signed integer type. + /// @see gtc_type_precision + typedef detail::int16 mediump_int16; + + /// Medium qualifier 32 bit signed integer type. + /// @see gtc_type_precision + typedef detail::int32 mediump_int32; + + /// Medium qualifier 64 bit signed integer type. + /// @see gtc_type_precision + typedef detail::int64 mediump_int64; + + /// Medium qualifier 8 bit signed integer type. + /// @see gtc_type_precision + typedef detail::int8 mediump_int8_t; + + /// Medium qualifier 16 bit signed integer type. + /// @see gtc_type_precision + typedef detail::int16 mediump_int16_t; + + /// Medium qualifier 32 bit signed integer type. + /// @see gtc_type_precision + typedef detail::int32 mediump_int32_t; + + /// Medium qualifier 64 bit signed integer type. + /// @see gtc_type_precision + typedef detail::int64 mediump_int64_t; + + /// Medium qualifier 8 bit signed integer type. + /// @see gtc_type_precision + typedef detail::int8 mediump_i8; + + /// Medium qualifier 16 bit signed integer type. + /// @see gtc_type_precision + typedef detail::int16 mediump_i16; + + /// Medium qualifier 32 bit signed integer type. + /// @see gtc_type_precision + typedef detail::int32 mediump_i32; + + /// Medium qualifier 64 bit signed integer type. + /// @see gtc_type_precision + typedef detail::int64 mediump_i64; + + /// High qualifier 8 bit signed integer type. + /// @see gtc_type_precision + typedef detail::int8 highp_int8; + + /// High qualifier 16 bit signed integer type. + /// @see gtc_type_precision + typedef detail::int16 highp_int16; + + /// High qualifier 32 bit signed integer type. + /// @see gtc_type_precision + typedef detail::int32 highp_int32; + + /// High qualifier 64 bit signed integer type. + /// @see gtc_type_precision + typedef detail::int64 highp_int64; + + /// High qualifier 8 bit signed integer type. + /// @see gtc_type_precision + typedef detail::int8 highp_int8_t; + + /// High qualifier 16 bit signed integer type. + /// @see gtc_type_precision + typedef detail::int16 highp_int16_t; + + /// 32 bit signed integer type. + /// @see gtc_type_precision + typedef detail::int32 highp_int32_t; + + /// High qualifier 64 bit signed integer type. + /// @see gtc_type_precision + typedef detail::int64 highp_int64_t; + + /// High qualifier 8 bit signed integer type. + /// @see gtc_type_precision + typedef detail::int8 highp_i8; + + /// High qualifier 16 bit signed integer type. + /// @see gtc_type_precision + typedef detail::int16 highp_i16; + + /// High qualifier 32 bit signed integer type. + /// @see gtc_type_precision + typedef detail::int32 highp_i32; + + /// High qualifier 64 bit signed integer type. + /// @see gtc_type_precision + typedef detail::int64 highp_i64; + + +#if GLM_HAS_EXTENDED_INTEGER_TYPE + using std::int8_t; + using std::int16_t; + using std::int32_t; + using std::int64_t; +#else + /// 8 bit signed integer type. + /// @see gtc_type_precision + typedef detail::int8 int8_t; + + /// 16 bit signed integer type. + /// @see gtc_type_precision + typedef detail::int16 int16_t; + + /// 32 bit signed integer type. + /// @see gtc_type_precision + typedef detail::int32 int32_t; + + /// 64 bit signed integer type. + /// @see gtc_type_precision + typedef detail::int64 int64_t; +#endif + + /// 8 bit signed integer type. + /// @see gtc_type_precision + typedef detail::int8 i8; + + /// 16 bit signed integer type. + /// @see gtc_type_precision + typedef detail::int16 i16; + + /// 32 bit signed integer type. + /// @see gtc_type_precision + typedef detail::int32 i32; + + /// 64 bit signed integer type. + /// @see gtc_type_precision + typedef detail::int64 i64; + + + + /// Low qualifier 8 bit signed integer scalar type. + /// @see gtc_type_precision + typedef vec<1, i8, lowp> lowp_i8vec1; + + /// Low qualifier 8 bit signed integer vector of 2 components type. + /// @see gtc_type_precision + typedef vec<2, i8, lowp> lowp_i8vec2; + + /// Low qualifier 8 bit signed integer vector of 3 components type. + /// @see gtc_type_precision + typedef vec<3, i8, lowp> lowp_i8vec3; + + /// Low qualifier 8 bit signed integer vector of 4 components type. + /// @see gtc_type_precision + typedef vec<4, i8, lowp> lowp_i8vec4; + + + /// Medium qualifier 8 bit signed integer scalar type. + /// @see gtc_type_precision + typedef vec<1, i8, mediump> mediump_i8vec1; + + /// Medium qualifier 8 bit signed integer vector of 2 components type. + /// @see gtc_type_precision + typedef vec<2, i8, mediump> mediump_i8vec2; + + /// Medium qualifier 8 bit signed integer vector of 3 components type. + /// @see gtc_type_precision + typedef vec<3, i8, mediump> mediump_i8vec3; + + /// Medium qualifier 8 bit signed integer vector of 4 components type. + /// @see gtc_type_precision + typedef vec<4, i8, mediump> mediump_i8vec4; + + + /// High qualifier 8 bit signed integer scalar type. + /// @see gtc_type_precision + typedef vec<1, i8, highp> highp_i8vec1; + + /// High qualifier 8 bit signed integer vector of 2 components type. + /// @see gtc_type_precision + typedef vec<2, i8, highp> highp_i8vec2; + + /// High qualifier 8 bit signed integer vector of 3 components type. + /// @see gtc_type_precision + typedef vec<3, i8, highp> highp_i8vec3; + + /// High qualifier 8 bit signed integer vector of 4 components type. + /// @see gtc_type_precision + typedef vec<4, i8, highp> highp_i8vec4; + + + + /// 8 bit signed integer scalar type. + /// @see gtc_type_precision + typedef vec<1, i8, defaultp> i8vec1; + + /// 8 bit signed integer vector of 2 components type. + /// @see gtc_type_precision + typedef vec<2, i8, defaultp> i8vec2; + + /// 8 bit signed integer vector of 3 components type. + /// @see gtc_type_precision + typedef vec<3, i8, defaultp> i8vec3; + + /// 8 bit signed integer vector of 4 components type. + /// @see gtc_type_precision + typedef vec<4, i8, defaultp> i8vec4; + + + + + + /// Low qualifier 16 bit signed integer scalar type. + /// @see gtc_type_precision + typedef vec<1, i16, lowp> lowp_i16vec1; + + /// Low qualifier 16 bit signed integer vector of 2 components type. + /// @see gtc_type_precision + typedef vec<2, i16, lowp> lowp_i16vec2; + + /// Low qualifier 16 bit signed integer vector of 3 components type. + /// @see gtc_type_precision + typedef vec<3, i16, lowp> lowp_i16vec3; + + /// Low qualifier 16 bit signed integer vector of 4 components type. + /// @see gtc_type_precision + typedef vec<4, i16, lowp> lowp_i16vec4; + + + /// Medium qualifier 16 bit signed integer scalar type. + /// @see gtc_type_precision + typedef vec<1, i16, mediump> mediump_i16vec1; + + /// Medium qualifier 16 bit signed integer vector of 2 components type. + /// @see gtc_type_precision + typedef vec<2, i16, mediump> mediump_i16vec2; + + /// Medium qualifier 16 bit signed integer vector of 3 components type. + /// @see gtc_type_precision + typedef vec<3, i16, mediump> mediump_i16vec3; + + /// Medium qualifier 16 bit signed integer vector of 4 components type. + /// @see gtc_type_precision + typedef vec<4, i16, mediump> mediump_i16vec4; + + + /// High qualifier 16 bit signed integer scalar type. + /// @see gtc_type_precision + typedef vec<1, i16, highp> highp_i16vec1; + + /// High qualifier 16 bit signed integer vector of 2 components type. + /// @see gtc_type_precision + typedef vec<2, i16, highp> highp_i16vec2; + + /// High qualifier 16 bit signed integer vector of 3 components type. + /// @see gtc_type_precision + typedef vec<3, i16, highp> highp_i16vec3; + + /// High qualifier 16 bit signed integer vector of 4 components type. + /// @see gtc_type_precision + typedef vec<4, i16, highp> highp_i16vec4; + + + + + /// 16 bit signed integer scalar type. + /// @see gtc_type_precision + typedef vec<1, i16, defaultp> i16vec1; + + /// 16 bit signed integer vector of 2 components type. + /// @see gtc_type_precision + typedef vec<2, i16, defaultp> i16vec2; + + /// 16 bit signed integer vector of 3 components type. + /// @see gtc_type_precision + typedef vec<3, i16, defaultp> i16vec3; + + /// 16 bit signed integer vector of 4 components type. + /// @see gtc_type_precision + typedef vec<4, i16, defaultp> i16vec4; + + + + /// Low qualifier 32 bit signed integer scalar type. + /// @see gtc_type_precision + typedef vec<1, i32, lowp> lowp_i32vec1; + + /// Low qualifier 32 bit signed integer vector of 2 components type. + /// @see gtc_type_precision + typedef vec<2, i32, lowp> lowp_i32vec2; + + /// Low qualifier 32 bit signed integer vector of 3 components type. + /// @see gtc_type_precision + typedef vec<3, i32, lowp> lowp_i32vec3; + + /// Low qualifier 32 bit signed integer vector of 4 components type. + /// @see gtc_type_precision + typedef vec<4, i32, lowp> lowp_i32vec4; + + + /// Medium qualifier 32 bit signed integer scalar type. + /// @see gtc_type_precision + typedef vec<1, i32, mediump> mediump_i32vec1; + + /// Medium qualifier 32 bit signed integer vector of 2 components type. + /// @see gtc_type_precision + typedef vec<2, i32, mediump> mediump_i32vec2; + + /// Medium qualifier 32 bit signed integer vector of 3 components type. + /// @see gtc_type_precision + typedef vec<3, i32, mediump> mediump_i32vec3; + + /// Medium qualifier 32 bit signed integer vector of 4 components type. + /// @see gtc_type_precision + typedef vec<4, i32, mediump> mediump_i32vec4; + + + /// High qualifier 32 bit signed integer scalar type. + /// @see gtc_type_precision + typedef vec<1, i32, highp> highp_i32vec1; + + /// High qualifier 32 bit signed integer vector of 2 components type. + /// @see gtc_type_precision + typedef vec<2, i32, highp> highp_i32vec2; + + /// High qualifier 32 bit signed integer vector of 3 components type. + /// @see gtc_type_precision + typedef vec<3, i32, highp> highp_i32vec3; + + /// High qualifier 32 bit signed integer vector of 4 components type. + /// @see gtc_type_precision + typedef vec<4, i32, highp> highp_i32vec4; + + + /// 32 bit signed integer scalar type. + /// @see gtc_type_precision + typedef vec<1, i32, defaultp> i32vec1; + + /// 32 bit signed integer vector of 2 components type. + /// @see gtc_type_precision + typedef vec<2, i32, defaultp> i32vec2; + + /// 32 bit signed integer vector of 3 components type. + /// @see gtc_type_precision + typedef vec<3, i32, defaultp> i32vec3; + + /// 32 bit signed integer vector of 4 components type. + /// @see gtc_type_precision + typedef vec<4, i32, defaultp> i32vec4; + + + + + /// Low qualifier 64 bit signed integer scalar type. + /// @see gtc_type_precision + typedef vec<1, i64, lowp> lowp_i64vec1; + + /// Low qualifier 64 bit signed integer vector of 2 components type. + /// @see gtc_type_precision + typedef vec<2, i64, lowp> lowp_i64vec2; + + /// Low qualifier 64 bit signed integer vector of 3 components type. + /// @see gtc_type_precision + typedef vec<3, i64, lowp> lowp_i64vec3; + + /// Low qualifier 64 bit signed integer vector of 4 components type. + /// @see gtc_type_precision + typedef vec<4, i64, lowp> lowp_i64vec4; + + + /// Medium qualifier 64 bit signed integer scalar type. + /// @see gtc_type_precision + typedef vec<1, i64, mediump> mediump_i64vec1; + + /// Medium qualifier 64 bit signed integer vector of 2 components type. + /// @see gtc_type_precision + typedef vec<2, i64, mediump> mediump_i64vec2; + + /// Medium qualifier 64 bit signed integer vector of 3 components type. + /// @see gtc_type_precision + typedef vec<3, i64, mediump> mediump_i64vec3; + + /// Medium qualifier 64 bit signed integer vector of 4 components type. + /// @see gtc_type_precision + typedef vec<4, i64, mediump> mediump_i64vec4; + + + /// High qualifier 64 bit signed integer scalar type. + /// @see gtc_type_precision + typedef vec<1, i64, highp> highp_i64vec1; + + /// High qualifier 64 bit signed integer vector of 2 components type. + /// @see gtc_type_precision + typedef vec<2, i64, highp> highp_i64vec2; + + /// High qualifier 64 bit signed integer vector of 3 components type. + /// @see gtc_type_precision + typedef vec<3, i64, highp> highp_i64vec3; + + /// High qualifier 64 bit signed integer vector of 4 components type. + /// @see gtc_type_precision + typedef vec<4, i64, highp> highp_i64vec4; + + + /// 64 bit signed integer scalar type. + /// @see gtc_type_precision + typedef vec<1, i64, defaultp> i64vec1; + + /// 64 bit signed integer vector of 2 components type. + /// @see gtc_type_precision + typedef vec<2, i64, defaultp> i64vec2; + + /// 64 bit signed integer vector of 3 components type. + /// @see gtc_type_precision + typedef vec<3, i64, defaultp> i64vec3; + + /// 64 bit signed integer vector of 4 components type. + /// @see gtc_type_precision + typedef vec<4, i64, defaultp> i64vec4; + + + ///////////////////////////// + // Unsigned int vector types + + /// Low qualifier 8 bit unsigned integer type. + /// @see gtc_type_precision + typedef detail::uint8 lowp_uint8; + + /// Low qualifier 16 bit unsigned integer type. + /// @see gtc_type_precision + typedef detail::uint16 lowp_uint16; + + /// Low qualifier 32 bit unsigned integer type. + /// @see gtc_type_precision + typedef detail::uint32 lowp_uint32; + + /// Low qualifier 64 bit unsigned integer type. + /// @see gtc_type_precision + typedef detail::uint64 lowp_uint64; + + /// Low qualifier 8 bit unsigned integer type. + /// @see gtc_type_precision + typedef detail::uint8 lowp_uint8_t; + + /// Low qualifier 16 bit unsigned integer type. + /// @see gtc_type_precision + typedef detail::uint16 lowp_uint16_t; + + /// Low qualifier 32 bit unsigned integer type. + /// @see gtc_type_precision + typedef detail::uint32 lowp_uint32_t; + + /// Low qualifier 64 bit unsigned integer type. + /// @see gtc_type_precision + typedef detail::uint64 lowp_uint64_t; + + /// Low qualifier 8 bit unsigned integer type. + /// @see gtc_type_precision + typedef detail::uint8 lowp_u8; + + /// Low qualifier 16 bit unsigned integer type. + /// @see gtc_type_precision + typedef detail::uint16 lowp_u16; + + /// Low qualifier 32 bit unsigned integer type. + /// @see gtc_type_precision + typedef detail::uint32 lowp_u32; + + /// Low qualifier 64 bit unsigned integer type. + /// @see gtc_type_precision + typedef detail::uint64 lowp_u64; + + /// Medium qualifier 8 bit unsigned integer type. + /// @see gtc_type_precision + typedef detail::uint8 mediump_uint8; + + /// Medium qualifier 16 bit unsigned integer type. + /// @see gtc_type_precision + typedef detail::uint16 mediump_uint16; + + /// Medium qualifier 32 bit unsigned integer type. + /// @see gtc_type_precision + typedef detail::uint32 mediump_uint32; + + /// Medium qualifier 64 bit unsigned integer type. + /// @see gtc_type_precision + typedef detail::uint64 mediump_uint64; + + /// Medium qualifier 8 bit unsigned integer type. + /// @see gtc_type_precision + typedef detail::uint8 mediump_uint8_t; + + /// Medium qualifier 16 bit unsigned integer type. + /// @see gtc_type_precision + typedef detail::uint16 mediump_uint16_t; + + /// Medium qualifier 32 bit unsigned integer type. + /// @see gtc_type_precision + typedef detail::uint32 mediump_uint32_t; + + /// Medium qualifier 64 bit unsigned integer type. + /// @see gtc_type_precision + typedef detail::uint64 mediump_uint64_t; + + /// Medium qualifier 8 bit unsigned integer type. + /// @see gtc_type_precision + typedef detail::uint8 mediump_u8; + + /// Medium qualifier 16 bit unsigned integer type. + /// @see gtc_type_precision + typedef detail::uint16 mediump_u16; + + /// Medium qualifier 32 bit unsigned integer type. + /// @see gtc_type_precision + typedef detail::uint32 mediump_u32; + + /// Medium qualifier 64 bit unsigned integer type. + /// @see gtc_type_precision + typedef detail::uint64 mediump_u64; + + /// High qualifier 8 bit unsigned integer type. + /// @see gtc_type_precision + typedef detail::uint8 highp_uint8; + + /// High qualifier 16 bit unsigned integer type. + /// @see gtc_type_precision + typedef detail::uint16 highp_uint16; + + /// High qualifier 32 bit unsigned integer type. + /// @see gtc_type_precision + typedef detail::uint32 highp_uint32; + + /// High qualifier 64 bit unsigned integer type. + /// @see gtc_type_precision + typedef detail::uint64 highp_uint64; + + /// High qualifier 8 bit unsigned integer type. + /// @see gtc_type_precision + typedef detail::uint8 highp_uint8_t; + + /// High qualifier 16 bit unsigned integer type. + /// @see gtc_type_precision + typedef detail::uint16 highp_uint16_t; + + /// High qualifier 32 bit unsigned integer type. + /// @see gtc_type_precision + typedef detail::uint32 highp_uint32_t; + + /// High qualifier 64 bit unsigned integer type. + /// @see gtc_type_precision + typedef detail::uint64 highp_uint64_t; + + /// High qualifier 8 bit unsigned integer type. + /// @see gtc_type_precision + typedef detail::uint8 highp_u8; + + /// High qualifier 16 bit unsigned integer type. + /// @see gtc_type_precision + typedef detail::uint16 highp_u16; + + /// High qualifier 32 bit unsigned integer type. + /// @see gtc_type_precision + typedef detail::uint32 highp_u32; + + /// High qualifier 64 bit unsigned integer type. + /// @see gtc_type_precision + typedef detail::uint64 highp_u64; + +#if GLM_HAS_EXTENDED_INTEGER_TYPE + using std::uint8_t; + using std::uint16_t; + using std::uint32_t; + using std::uint64_t; +#else + /// Default qualifier 8 bit unsigned integer type. + /// @see gtc_type_precision + typedef detail::uint8 uint8_t; + + /// Default qualifier 16 bit unsigned integer type. + /// @see gtc_type_precision + typedef detail::uint16 uint16_t; + + /// Default qualifier 32 bit unsigned integer type. + /// @see gtc_type_precision + typedef detail::uint32 uint32_t; + + /// Default qualifier 64 bit unsigned integer type. + /// @see gtc_type_precision + typedef detail::uint64 uint64_t; +#endif + + /// Default qualifier 8 bit unsigned integer type. + /// @see gtc_type_precision + typedef detail::uint8 u8; + + /// Default qualifier 16 bit unsigned integer type. + /// @see gtc_type_precision + typedef detail::uint16 u16; + + /// Default qualifier 32 bit unsigned integer type. + /// @see gtc_type_precision + typedef detail::uint32 u32; + + /// Default qualifier 64 bit unsigned integer type. + /// @see gtc_type_precision + typedef detail::uint64 u64; + + + + + + ////////////////////// + // Float vector types + + /// Single-qualifier floating-point scalar. + /// @see gtc_type_precision + typedef float float32; + + /// Double-qualifier floating-point scalar. + /// @see gtc_type_precision + typedef double float64; + + /// Low 32 bit single-qualifier floating-point scalar. + /// @see gtc_type_precision + typedef float32 lowp_float32; + + /// Low 64 bit double-qualifier floating-point scalar. + /// @see gtc_type_precision + typedef float64 lowp_float64; + + /// Low 32 bit single-qualifier floating-point scalar. + /// @see gtc_type_precision + typedef float32 lowp_float32_t; + + /// Low 64 bit double-qualifier floating-point scalar. + /// @see gtc_type_precision + typedef float64 lowp_float64_t; + + /// Low 32 bit single-qualifier floating-point scalar. + /// @see gtc_type_precision + typedef float32 lowp_f32; + + /// Low 64 bit double-qualifier floating-point scalar. + /// @see gtc_type_precision + typedef float64 lowp_f64; + + /// Low 32 bit single-qualifier floating-point scalar. + /// @see gtc_type_precision + typedef float32 lowp_float32; + + /// Low 64 bit double-qualifier floating-point scalar. + /// @see gtc_type_precision + typedef float64 lowp_float64; + + /// Low 32 bit single-qualifier floating-point scalar. + /// @see gtc_type_precision + typedef float32 lowp_float32_t; + + /// Low 64 bit double-qualifier floating-point scalar. + /// @see gtc_type_precision + typedef float64 lowp_float64_t; + + /// Low 32 bit single-qualifier floating-point scalar. + /// @see gtc_type_precision + typedef float32 lowp_f32; + + /// Low 64 bit double-qualifier floating-point scalar. + /// @see gtc_type_precision + typedef float64 lowp_f64; + + + /// Low 32 bit single-qualifier floating-point scalar. + /// @see gtc_type_precision + typedef float32 lowp_float32; + + /// Low 64 bit double-qualifier floating-point scalar. + /// @see gtc_type_precision + typedef float64 lowp_float64; + + /// Low 32 bit single-qualifier floating-point scalar. + /// @see gtc_type_precision + typedef float32 lowp_float32_t; + + /// Low 64 bit double-qualifier floating-point scalar. + /// @see gtc_type_precision + typedef float64 lowp_float64_t; + + /// Low 32 bit single-qualifier floating-point scalar. + /// @see gtc_type_precision + typedef float32 lowp_f32; + + /// Low 64 bit double-qualifier floating-point scalar. + /// @see gtc_type_precision + typedef float64 lowp_f64; + + + /// Medium 32 bit single-qualifier floating-point scalar. + /// @see gtc_type_precision + typedef float32 mediump_float32; + + /// Medium 64 bit double-qualifier floating-point scalar. + /// @see gtc_type_precision + typedef float64 mediump_float64; + + /// Medium 32 bit single-qualifier floating-point scalar. + /// @see gtc_type_precision + typedef float32 mediump_float32_t; + + /// Medium 64 bit double-qualifier floating-point scalar. + /// @see gtc_type_precision + typedef float64 mediump_float64_t; + + /// Medium 32 bit single-qualifier floating-point scalar. + /// @see gtc_type_precision + typedef float32 mediump_f32; + + /// Medium 64 bit double-qualifier floating-point scalar. + /// @see gtc_type_precision + typedef float64 mediump_f64; + + + /// High 32 bit single-qualifier floating-point scalar. + /// @see gtc_type_precision + typedef float32 highp_float32; + + /// High 64 bit double-qualifier floating-point scalar. + /// @see gtc_type_precision + typedef float64 highp_float64; + + /// High 32 bit single-qualifier floating-point scalar. + /// @see gtc_type_precision + typedef float32 highp_float32_t; + + /// High 64 bit double-qualifier floating-point scalar. + /// @see gtc_type_precision + typedef float64 highp_float64_t; + + /// High 32 bit single-qualifier floating-point scalar. + /// @see gtc_type_precision + typedef float32 highp_f32; + + /// High 64 bit double-qualifier floating-point scalar. + /// @see gtc_type_precision + typedef float64 highp_f64; + + +#if(defined(GLM_PRECISION_LOWP_FLOAT)) + /// Default 32 bit single-qualifier floating-point scalar. + /// @see gtc_type_precision + typedef lowp_float32_t float32_t; + + /// Default 64 bit double-qualifier floating-point scalar. + /// @see gtc_type_precision + typedef lowp_float64_t float64_t; + + /// Default 32 bit single-qualifier floating-point scalar. + /// @see gtc_type_precision + typedef lowp_f32 f32; + + /// Default 64 bit double-qualifier floating-point scalar. + /// @see gtc_type_precision + typedef lowp_f64 f64; + +#elif(defined(GLM_PRECISION_MEDIUMP_FLOAT)) + /// Default 32 bit single-qualifier floating-point scalar. + /// @see gtc_type_precision + typedef mediump_float32 float32_t; + + /// Default 64 bit double-qualifier floating-point scalar. + /// @see gtc_type_precision + typedef mediump_float64 float64_t; + + /// Default 32 bit single-qualifier floating-point scalar. + /// @see gtc_type_precision + typedef mediump_float32 f32; + + /// Default 64 bit double-qualifier floating-point scalar. + /// @see gtc_type_precision + typedef mediump_float64 f64; + +#else//(defined(GLM_PRECISION_HIGHP_FLOAT)) + + /// Default 32 bit single-qualifier floating-point scalar. + /// @see gtc_type_precision + typedef highp_float32_t float32_t; + + /// Default 64 bit double-qualifier floating-point scalar. + /// @see gtc_type_precision + typedef highp_float64_t float64_t; + + /// Default 32 bit single-qualifier floating-point scalar. + /// @see gtc_type_precision + typedef highp_float32_t f32; + + /// Default 64 bit double-qualifier floating-point scalar. + /// @see gtc_type_precision + typedef highp_float64_t f64; +#endif + + + /// Low single-qualifier floating-point vector of 1 component. + /// @see gtc_type_precision + typedef vec<1, float, lowp> lowp_fvec1; + + /// Low single-qualifier floating-point vector of 2 components. + /// @see gtc_type_precision + typedef vec<2, float, lowp> lowp_fvec2; + + /// Low single-qualifier floating-point vector of 3 components. + /// @see gtc_type_precision + typedef vec<3, float, lowp> lowp_fvec3; + + /// Low single-qualifier floating-point vector of 4 components. + /// @see gtc_type_precision + typedef vec<4, float, lowp> lowp_fvec4; + + + /// Medium single-qualifier floating-point vector of 1 component. + /// @see gtc_type_precision + typedef vec<1, float, mediump> mediump_fvec1; + + /// Medium Single-qualifier floating-point vector of 2 components. + /// @see gtc_type_precision + typedef vec<2, float, mediump> mediump_fvec2; + + /// Medium Single-qualifier floating-point vector of 3 components. + /// @see gtc_type_precision + typedef vec<3, float, mediump> mediump_fvec3; + + /// Medium Single-qualifier floating-point vector of 4 components. + /// @see gtc_type_precision + typedef vec<4, float, mediump> mediump_fvec4; + + + /// High single-qualifier floating-point vector of 1 component. + /// @see gtc_type_precision + typedef vec<1, float, highp> highp_fvec1; + + /// High Single-qualifier floating-point vector of 2 components. + /// @see core_precision + typedef vec<2, float, highp> highp_fvec2; + + /// High Single-qualifier floating-point vector of 3 components. + /// @see core_precision + typedef vec<3, float, highp> highp_fvec3; + + /// High Single-qualifier floating-point vector of 4 components. + /// @see core_precision + typedef vec<4, float, highp> highp_fvec4; + + + /// Low single-qualifier floating-point vector of 1 component. + /// @see gtc_type_precision + typedef vec<1, f32, lowp> lowp_f32vec1; + + /// Low single-qualifier floating-point vector of 2 components. + /// @see core_precision + typedef vec<2, f32, lowp> lowp_f32vec2; + + /// Low single-qualifier floating-point vector of 3 components. + /// @see core_precision + typedef vec<3, f32, lowp> lowp_f32vec3; + + /// Low single-qualifier floating-point vector of 4 components. + /// @see core_precision + typedef vec<4, f32, lowp> lowp_f32vec4; + + /// Medium single-qualifier floating-point vector of 1 component. + /// @see gtc_type_precision + typedef vec<1, f32, mediump> mediump_f32vec1; + + /// Medium single-qualifier floating-point vector of 2 components. + /// @see core_precision + typedef vec<2, f32, mediump> mediump_f32vec2; + + /// Medium single-qualifier floating-point vector of 3 components. + /// @see core_precision + typedef vec<3, f32, mediump> mediump_f32vec3; + + /// Medium single-qualifier floating-point vector of 4 components. + /// @see core_precision + typedef vec<4, f32, mediump> mediump_f32vec4; + + /// High single-qualifier floating-point vector of 1 component. + /// @see gtc_type_precision + typedef vec<1, f32, highp> highp_f32vec1; + + /// High single-qualifier floating-point vector of 2 components. + /// @see gtc_type_precision + typedef vec<2, f32, highp> highp_f32vec2; + + /// High single-qualifier floating-point vector of 3 components. + /// @see gtc_type_precision + typedef vec<3, f32, highp> highp_f32vec3; + + /// High single-qualifier floating-point vector of 4 components. + /// @see gtc_type_precision + typedef vec<4, f32, highp> highp_f32vec4; + + + /// Low double-qualifier floating-point vector of 1 component. + /// @see gtc_type_precision + typedef vec<1, f64, lowp> lowp_f64vec1; + + /// Low double-qualifier floating-point vector of 2 components. + /// @see gtc_type_precision + typedef vec<2, f64, lowp> lowp_f64vec2; + + /// Low double-qualifier floating-point vector of 3 components. + /// @see gtc_type_precision + typedef vec<3, f64, lowp> lowp_f64vec3; + + /// Low double-qualifier floating-point vector of 4 components. + /// @see gtc_type_precision + typedef vec<4, f64, lowp> lowp_f64vec4; + + /// Medium double-qualifier floating-point vector of 1 component. + /// @see gtc_type_precision + typedef vec<1, f64, mediump> mediump_f64vec1; + + /// Medium double-qualifier floating-point vector of 2 components. + /// @see gtc_type_precision + typedef vec<2, f64, mediump> mediump_f64vec2; + + /// Medium double-qualifier floating-point vector of 3 components. + /// @see gtc_type_precision + typedef vec<3, f64, mediump> mediump_f64vec3; + + /// Medium double-qualifier floating-point vector of 4 components. + /// @see gtc_type_precision + typedef vec<4, f64, mediump> mediump_f64vec4; + + /// High double-qualifier floating-point vector of 1 component. + /// @see gtc_type_precision + typedef vec<1, f64, highp> highp_f64vec1; + + /// High double-qualifier floating-point vector of 2 components. + /// @see gtc_type_precision + typedef vec<2, f64, highp> highp_f64vec2; + + /// High double-qualifier floating-point vector of 3 components. + /// @see gtc_type_precision + typedef vec<3, f64, highp> highp_f64vec3; + + /// High double-qualifier floating-point vector of 4 components. + /// @see gtc_type_precision + typedef vec<4, f64, highp> highp_f64vec4; + + + + ////////////////////// + // Float matrix types + + /// Low single-qualifier floating-point 1x1 matrix. + /// @see gtc_type_precision + //typedef lowp_f32 lowp_fmat1x1; + + /// Low single-qualifier floating-point 2x2 matrix. + /// @see gtc_type_precision + typedef mat<2, 2, f32, lowp> lowp_fmat2x2; + + /// Low single-qualifier floating-point 2x3 matrix. + /// @see gtc_type_precision + typedef mat<2, 3, f32, lowp> lowp_fmat2x3; + + /// Low single-qualifier floating-point 2x4 matrix. + /// @see gtc_type_precision + typedef mat<2, 4, f32, lowp> lowp_fmat2x4; + + /// Low single-qualifier floating-point 3x2 matrix. + /// @see gtc_type_precision + typedef mat<3, 2, f32, lowp> lowp_fmat3x2; + + /// Low single-qualifier floating-point 3x3 matrix. + /// @see gtc_type_precision + typedef mat<3, 3, f32, lowp> lowp_fmat3x3; + + /// Low single-qualifier floating-point 3x4 matrix. + /// @see gtc_type_precision + typedef mat<3, 4, f32, lowp> lowp_fmat3x4; + + /// Low single-qualifier floating-point 4x2 matrix. + /// @see gtc_type_precision + typedef mat<4, 2, f32, lowp> lowp_fmat4x2; + + /// Low single-qualifier floating-point 4x3 matrix. + /// @see gtc_type_precision + typedef mat<4, 3, f32, lowp> lowp_fmat4x3; + + /// Low single-qualifier floating-point 4x4 matrix. + /// @see gtc_type_precision + typedef mat<4, 4, f32, lowp> lowp_fmat4x4; + + /// Low single-qualifier floating-point 1x1 matrix. + /// @see gtc_type_precision + //typedef lowp_fmat1x1 lowp_fmat1; + + /// Low single-qualifier floating-point 2x2 matrix. + /// @see gtc_type_precision + typedef lowp_fmat2x2 lowp_fmat2; + + /// Low single-qualifier floating-point 3x3 matrix. + /// @see gtc_type_precision + typedef lowp_fmat3x3 lowp_fmat3; + + /// Low single-qualifier floating-point 4x4 matrix. + /// @see gtc_type_precision + typedef lowp_fmat4x4 lowp_fmat4; + + + /// Medium single-qualifier floating-point 1x1 matrix. + /// @see gtc_type_precision + //typedef mediump_f32 mediump_fmat1x1; + + /// Medium single-qualifier floating-point 2x2 matrix. + /// @see gtc_type_precision + typedef mat<2, 2, f32, mediump> mediump_fmat2x2; + + /// Medium single-qualifier floating-point 2x3 matrix. + /// @see gtc_type_precision + typedef mat<2, 3, f32, mediump> mediump_fmat2x3; + + /// Medium single-qualifier floating-point 2x4 matrix. + /// @see gtc_type_precision + typedef mat<2, 4, f32, mediump> mediump_fmat2x4; + + /// Medium single-qualifier floating-point 3x2 matrix. + /// @see gtc_type_precision + typedef mat<3, 2, f32, mediump> mediump_fmat3x2; + + /// Medium single-qualifier floating-point 3x3 matrix. + /// @see gtc_type_precision + typedef mat<3, 3, f32, mediump> mediump_fmat3x3; + + /// Medium single-qualifier floating-point 3x4 matrix. + /// @see gtc_type_precision + typedef mat<3, 4, f32, mediump> mediump_fmat3x4; + + /// Medium single-qualifier floating-point 4x2 matrix. + /// @see gtc_type_precision + typedef mat<4, 2, f32, mediump> mediump_fmat4x2; + + /// Medium single-qualifier floating-point 4x3 matrix. + /// @see gtc_type_precision + typedef mat<4, 3, f32, mediump> mediump_fmat4x3; + + /// Medium single-qualifier floating-point 4x4 matrix. + /// @see gtc_type_precision + typedef mat<4, 4, f32, mediump> mediump_fmat4x4; + + /// Medium single-qualifier floating-point 1x1 matrix. + /// @see gtc_type_precision + //typedef mediump_fmat1x1 mediump_fmat1; + + /// Medium single-qualifier floating-point 2x2 matrix. + /// @see gtc_type_precision + typedef mediump_fmat2x2 mediump_fmat2; + + /// Medium single-qualifier floating-point 3x3 matrix. + /// @see gtc_type_precision + typedef mediump_fmat3x3 mediump_fmat3; + + /// Medium single-qualifier floating-point 4x4 matrix. + /// @see gtc_type_precision + typedef mediump_fmat4x4 mediump_fmat4; + + + /// High single-qualifier floating-point 1x1 matrix. + /// @see gtc_type_precision + //typedef highp_f32 highp_fmat1x1; + + /// High single-qualifier floating-point 2x2 matrix. + /// @see gtc_type_precision + typedef mat<2, 2, f32, highp> highp_fmat2x2; + + /// High single-qualifier floating-point 2x3 matrix. + /// @see gtc_type_precision + typedef mat<2, 3, f32, highp> highp_fmat2x3; + + /// High single-qualifier floating-point 2x4 matrix. + /// @see gtc_type_precision + typedef mat<2, 4, f32, highp> highp_fmat2x4; + + /// High single-qualifier floating-point 3x2 matrix. + /// @see gtc_type_precision + typedef mat<3, 2, f32, highp> highp_fmat3x2; + + /// High single-qualifier floating-point 3x3 matrix. + /// @see gtc_type_precision + typedef mat<3, 3, f32, highp> highp_fmat3x3; + + /// High single-qualifier floating-point 3x4 matrix. + /// @see gtc_type_precision + typedef mat<3, 4, f32, highp> highp_fmat3x4; + + /// High single-qualifier floating-point 4x2 matrix. + /// @see gtc_type_precision + typedef mat<4, 2, f32, highp> highp_fmat4x2; + + /// High single-qualifier floating-point 4x3 matrix. + /// @see gtc_type_precision + typedef mat<4, 3, f32, highp> highp_fmat4x3; + + /// High single-qualifier floating-point 4x4 matrix. + /// @see gtc_type_precision + typedef mat<4, 4, f32, highp> highp_fmat4x4; + + /// High single-qualifier floating-point 1x1 matrix. + /// @see gtc_type_precision + //typedef highp_fmat1x1 highp_fmat1; + + /// High single-qualifier floating-point 2x2 matrix. + /// @see gtc_type_precision + typedef highp_fmat2x2 highp_fmat2; + + /// High single-qualifier floating-point 3x3 matrix. + /// @see gtc_type_precision + typedef highp_fmat3x3 highp_fmat3; + + /// High single-qualifier floating-point 4x4 matrix. + /// @see gtc_type_precision + typedef highp_fmat4x4 highp_fmat4; + + + /// Low single-qualifier floating-point 1x1 matrix. + /// @see gtc_type_precision + //typedef f32 lowp_f32mat1x1; + + /// Low single-qualifier floating-point 2x2 matrix. + /// @see gtc_type_precision + typedef mat<2, 2, f32, lowp> lowp_f32mat2x2; + + /// Low single-qualifier floating-point 2x3 matrix. + /// @see gtc_type_precision + typedef mat<2, 3, f32, lowp> lowp_f32mat2x3; + + /// Low single-qualifier floating-point 2x4 matrix. + /// @see gtc_type_precision + typedef mat<2, 4, f32, lowp> lowp_f32mat2x4; + + /// Low single-qualifier floating-point 3x2 matrix. + /// @see gtc_type_precision + typedef mat<3, 2, f32, lowp> lowp_f32mat3x2; + + /// Low single-qualifier floating-point 3x3 matrix. + /// @see gtc_type_precision + typedef mat<3, 3, f32, lowp> lowp_f32mat3x3; + + /// Low single-qualifier floating-point 3x4 matrix. + /// @see gtc_type_precision + typedef mat<3, 4, f32, lowp> lowp_f32mat3x4; + + /// Low single-qualifier floating-point 4x2 matrix. + /// @see gtc_type_precision + typedef mat<4, 2, f32, lowp> lowp_f32mat4x2; + + /// Low single-qualifier floating-point 4x3 matrix. + /// @see gtc_type_precision + typedef mat<4, 3, f32, lowp> lowp_f32mat4x3; + + /// Low single-qualifier floating-point 4x4 matrix. + /// @see gtc_type_precision + typedef mat<4, 4, f32, lowp> lowp_f32mat4x4; + + /// Low single-qualifier floating-point 1x1 matrix. + /// @see gtc_type_precision + //typedef detail::tmat1x1 lowp_f32mat1; + + /// Low single-qualifier floating-point 2x2 matrix. + /// @see gtc_type_precision + typedef lowp_f32mat2x2 lowp_f32mat2; + + /// Low single-qualifier floating-point 3x3 matrix. + /// @see gtc_type_precision + typedef lowp_f32mat3x3 lowp_f32mat3; + + /// Low single-qualifier floating-point 4x4 matrix. + /// @see gtc_type_precision + typedef lowp_f32mat4x4 lowp_f32mat4; + + + /// High single-qualifier floating-point 1x1 matrix. + /// @see gtc_type_precision + //typedef f32 mediump_f32mat1x1; + + /// Low single-qualifier floating-point 2x2 matrix. + /// @see gtc_type_precision + typedef mat<2, 2, f32, mediump> mediump_f32mat2x2; + + /// Medium single-qualifier floating-point 2x3 matrix. + /// @see gtc_type_precision + typedef mat<2, 3, f32, mediump> mediump_f32mat2x3; + + /// Medium single-qualifier floating-point 2x4 matrix. + /// @see gtc_type_precision + typedef mat<2, 4, f32, mediump> mediump_f32mat2x4; + + /// Medium single-qualifier floating-point 3x2 matrix. + /// @see gtc_type_precision + typedef mat<3, 2, f32, mediump> mediump_f32mat3x2; + + /// Medium single-qualifier floating-point 3x3 matrix. + /// @see gtc_type_precision + typedef mat<3, 3, f32, mediump> mediump_f32mat3x3; + + /// Medium single-qualifier floating-point 3x4 matrix. + /// @see gtc_type_precision + typedef mat<3, 4, f32, mediump> mediump_f32mat3x4; + + /// Medium single-qualifier floating-point 4x2 matrix. + /// @see gtc_type_precision + typedef mat<4, 2, f32, mediump> mediump_f32mat4x2; + + /// Medium single-qualifier floating-point 4x3 matrix. + /// @see gtc_type_precision + typedef mat<4, 3, f32, mediump> mediump_f32mat4x3; + + /// Medium single-qualifier floating-point 4x4 matrix. + /// @see gtc_type_precision + typedef mat<4, 4, f32, mediump> mediump_f32mat4x4; + + /// Medium single-qualifier floating-point 1x1 matrix. + /// @see gtc_type_precision + //typedef detail::tmat1x1 f32mat1; + + /// Medium single-qualifier floating-point 2x2 matrix. + /// @see gtc_type_precision + typedef mediump_f32mat2x2 mediump_f32mat2; + + /// Medium single-qualifier floating-point 3x3 matrix. + /// @see gtc_type_precision + typedef mediump_f32mat3x3 mediump_f32mat3; + + /// Medium single-qualifier floating-point 4x4 matrix. + /// @see gtc_type_precision + typedef mediump_f32mat4x4 mediump_f32mat4; + + + /// High single-qualifier floating-point 1x1 matrix. + /// @see gtc_type_precision + //typedef f32 highp_f32mat1x1; + + /// High single-qualifier floating-point 2x2 matrix. + /// @see gtc_type_precision + typedef mat<2, 2, f32, highp> highp_f32mat2x2; + + /// High single-qualifier floating-point 2x3 matrix. + /// @see gtc_type_precision + typedef mat<2, 3, f32, highp> highp_f32mat2x3; + + /// High single-qualifier floating-point 2x4 matrix. + /// @see gtc_type_precision + typedef mat<2, 4, f32, highp> highp_f32mat2x4; + + /// High single-qualifier floating-point 3x2 matrix. + /// @see gtc_type_precision + typedef mat<3, 2, f32, highp> highp_f32mat3x2; + + /// High single-qualifier floating-point 3x3 matrix. + /// @see gtc_type_precision + typedef mat<3, 3, f32, highp> highp_f32mat3x3; + + /// High single-qualifier floating-point 3x4 matrix. + /// @see gtc_type_precision + typedef mat<3, 4, f32, highp> highp_f32mat3x4; + + /// High single-qualifier floating-point 4x2 matrix. + /// @see gtc_type_precision + typedef mat<4, 2, f32, highp> highp_f32mat4x2; + + /// High single-qualifier floating-point 4x3 matrix. + /// @see gtc_type_precision + typedef mat<4, 3, f32, highp> highp_f32mat4x3; + + /// High single-qualifier floating-point 4x4 matrix. + /// @see gtc_type_precision + typedef mat<4, 4, f32, highp> highp_f32mat4x4; + + /// High single-qualifier floating-point 1x1 matrix. + /// @see gtc_type_precision + //typedef detail::tmat1x1 f32mat1; + + /// High single-qualifier floating-point 2x2 matrix. + /// @see gtc_type_precision + typedef highp_f32mat2x2 highp_f32mat2; + + /// High single-qualifier floating-point 3x3 matrix. + /// @see gtc_type_precision + typedef highp_f32mat3x3 highp_f32mat3; + + /// High single-qualifier floating-point 4x4 matrix. + /// @see gtc_type_precision + typedef highp_f32mat4x4 highp_f32mat4; + + + /// Low double-qualifier floating-point 1x1 matrix. + /// @see gtc_type_precision + //typedef f64 lowp_f64mat1x1; + + /// Low double-qualifier floating-point 2x2 matrix. + /// @see gtc_type_precision + typedef mat<2, 2, f64, lowp> lowp_f64mat2x2; + + /// Low double-qualifier floating-point 2x3 matrix. + /// @see gtc_type_precision + typedef mat<2, 3, f64, lowp> lowp_f64mat2x3; + + /// Low double-qualifier floating-point 2x4 matrix. + /// @see gtc_type_precision + typedef mat<2, 4, f64, lowp> lowp_f64mat2x4; + + /// Low double-qualifier floating-point 3x2 matrix. + /// @see gtc_type_precision + typedef mat<3, 2, f64, lowp> lowp_f64mat3x2; + + /// Low double-qualifier floating-point 3x3 matrix. + /// @see gtc_type_precision + typedef mat<3, 3, f64, lowp> lowp_f64mat3x3; + + /// Low double-qualifier floating-point 3x4 matrix. + /// @see gtc_type_precision + typedef mat<3, 4, f64, lowp> lowp_f64mat3x4; + + /// Low double-qualifier floating-point 4x2 matrix. + /// @see gtc_type_precision + typedef mat<4, 2, f64, lowp> lowp_f64mat4x2; + + /// Low double-qualifier floating-point 4x3 matrix. + /// @see gtc_type_precision + typedef mat<4, 3, f64, lowp> lowp_f64mat4x3; + + /// Low double-qualifier floating-point 4x4 matrix. + /// @see gtc_type_precision + typedef mat<4, 4, f64, lowp> lowp_f64mat4x4; + + /// Low double-qualifier floating-point 1x1 matrix. + /// @see gtc_type_precision + //typedef lowp_f64mat1x1 lowp_f64mat1; + + /// Low double-qualifier floating-point 2x2 matrix. + /// @see gtc_type_precision + typedef lowp_f64mat2x2 lowp_f64mat2; + + /// Low double-qualifier floating-point 3x3 matrix. + /// @see gtc_type_precision + typedef lowp_f64mat3x3 lowp_f64mat3; + + /// Low double-qualifier floating-point 4x4 matrix. + /// @see gtc_type_precision + typedef lowp_f64mat4x4 lowp_f64mat4; + + + /// Medium double-qualifier floating-point 1x1 matrix. + /// @see gtc_type_precision + //typedef f64 Highp_f64mat1x1; + + /// Medium double-qualifier floating-point 2x2 matrix. + /// @see gtc_type_precision + typedef mat<2, 2, f64, mediump> mediump_f64mat2x2; + + /// Medium double-qualifier floating-point 2x3 matrix. + /// @see gtc_type_precision + typedef mat<2, 3, f64, mediump> mediump_f64mat2x3; + + /// Medium double-qualifier floating-point 2x4 matrix. + /// @see gtc_type_precision + typedef mat<2, 4, f64, mediump> mediump_f64mat2x4; + + /// Medium double-qualifier floating-point 3x2 matrix. + /// @see gtc_type_precision + typedef mat<3, 2, f64, mediump> mediump_f64mat3x2; + + /// Medium double-qualifier floating-point 3x3 matrix. + /// @see gtc_type_precision + typedef mat<3, 3, f64, mediump> mediump_f64mat3x3; + + /// Medium double-qualifier floating-point 3x4 matrix. + /// @see gtc_type_precision + typedef mat<3, 4, f64, mediump> mediump_f64mat3x4; + + /// Medium double-qualifier floating-point 4x2 matrix. + /// @see gtc_type_precision + typedef mat<4, 2, f64, mediump> mediump_f64mat4x2; + + /// Medium double-qualifier floating-point 4x3 matrix. + /// @see gtc_type_precision + typedef mat<4, 3, f64, mediump> mediump_f64mat4x3; + + /// Medium double-qualifier floating-point 4x4 matrix. + /// @see gtc_type_precision + typedef mat<4, 4, f64, mediump> mediump_f64mat4x4; + + /// Medium double-qualifier floating-point 1x1 matrix. + /// @see gtc_type_precision + //typedef mediump_f64mat1x1 mediump_f64mat1; + + /// Medium double-qualifier floating-point 2x2 matrix. + /// @see gtc_type_precision + typedef mediump_f64mat2x2 mediump_f64mat2; + + /// Medium double-qualifier floating-point 3x3 matrix. + /// @see gtc_type_precision + typedef mediump_f64mat3x3 mediump_f64mat3; + + /// Medium double-qualifier floating-point 4x4 matrix. + /// @see gtc_type_precision + typedef mediump_f64mat4x4 mediump_f64mat4; + + /// High double-qualifier floating-point 1x1 matrix. + /// @see gtc_type_precision + //typedef f64 highp_f64mat1x1; + + /// High double-qualifier floating-point 2x2 matrix. + /// @see gtc_type_precision + typedef mat<2, 2, f64, highp> highp_f64mat2x2; + + /// High double-qualifier floating-point 2x3 matrix. + /// @see gtc_type_precision + typedef mat<2, 3, f64, highp> highp_f64mat2x3; + + /// High double-qualifier floating-point 2x4 matrix. + /// @see gtc_type_precision + typedef mat<2, 4, f64, highp> highp_f64mat2x4; + + /// High double-qualifier floating-point 3x2 matrix. + /// @see gtc_type_precision + typedef mat<3, 2, f64, highp> highp_f64mat3x2; + + /// High double-qualifier floating-point 3x3 matrix. + /// @see gtc_type_precision + typedef mat<3, 3, f64, highp> highp_f64mat3x3; + + /// High double-qualifier floating-point 3x4 matrix. + /// @see gtc_type_precision + typedef mat<3, 4, f64, highp> highp_f64mat3x4; + + /// High double-qualifier floating-point 4x2 matrix. + /// @see gtc_type_precision + typedef mat<4, 2, f64, highp> highp_f64mat4x2; + + /// High double-qualifier floating-point 4x3 matrix. + /// @see gtc_type_precision + typedef mat<4, 3, f64, highp> highp_f64mat4x3; + + /// High double-qualifier floating-point 4x4 matrix. + /// @see gtc_type_precision + typedef mat<4, 4, f64, highp> highp_f64mat4x4; + + /// High double-qualifier floating-point 1x1 matrix. + /// @see gtc_type_precision + //typedef highp_f64mat1x1 highp_f64mat1; + + /// High double-qualifier floating-point 2x2 matrix. + /// @see gtc_type_precision + typedef highp_f64mat2x2 highp_f64mat2; + + /// High double-qualifier floating-point 3x3 matrix. + /// @see gtc_type_precision + typedef highp_f64mat3x3 highp_f64mat3; + + /// High double-qualifier floating-point 4x4 matrix. + /// @see gtc_type_precision + typedef highp_f64mat4x4 highp_f64mat4; + + + + + /// Low qualifier 8 bit unsigned integer scalar type. + /// @see gtc_type_precision + typedef vec<1, u8, lowp> lowp_u8vec1; + + /// Low qualifier 8 bit unsigned integer vector of 2 components type. + /// @see gtc_type_precision + typedef vec<2, u8, lowp> lowp_u8vec2; + + /// Low qualifier 8 bit unsigned integer vector of 3 components type. + /// @see gtc_type_precision + typedef vec<3, u8, lowp> lowp_u8vec3; + + /// Low qualifier 8 bit unsigned integer vector of 4 components type. + /// @see gtc_type_precision + typedef vec<4, u8, lowp> lowp_u8vec4; + + + /// Medium qualifier 8 bit unsigned integer scalar type. + /// @see gtc_type_precision + typedef vec<1, u8, mediump> mediump_u8vec1; + + /// Medium qualifier 8 bit unsigned integer vector of 2 components type. + /// @see gtc_type_precision + typedef vec<2, u8, mediump> mediump_u8vec2; + + /// Medium qualifier 8 bit unsigned integer vector of 3 components type. + /// @see gtc_type_precision + typedef vec<3, u8, mediump> mediump_u8vec3; + + /// Medium qualifier 8 bit unsigned integer vector of 4 components type. + /// @see gtc_type_precision + typedef vec<4, u8, mediump> mediump_u8vec4; + + + /// High qualifier 8 bit unsigned integer scalar type. + /// @see gtc_type_precision + typedef vec<1, u8, highp> highp_u8vec1; + + /// High qualifier 8 bit unsigned integer vector of 2 components type. + /// @see gtc_type_precision + typedef vec<2, u8, highp> highp_u8vec2; + + /// High qualifier 8 bit unsigned integer vector of 3 components type. + /// @see gtc_type_precision + typedef vec<3, u8, highp> highp_u8vec3; + + /// High qualifier 8 bit unsigned integer vector of 4 components type. + /// @see gtc_type_precision + typedef vec<4, u8, highp> highp_u8vec4; + + + + /// Default qualifier 8 bit unsigned integer scalar type. + /// @see gtc_type_precision + typedef vec<1, u8, defaultp> u8vec1; + + /// Default qualifier 8 bit unsigned integer vector of 2 components type. + /// @see gtc_type_precision + typedef vec<2, u8, defaultp> u8vec2; + + /// Default qualifier 8 bit unsigned integer vector of 3 components type. + /// @see gtc_type_precision + typedef vec<3, u8, defaultp> u8vec3; + + /// Default qualifier 8 bit unsigned integer vector of 4 components type. + /// @see gtc_type_precision + typedef vec<4, u8, defaultp> u8vec4; + + + + + /// Low qualifier 16 bit unsigned integer scalar type. + /// @see gtc_type_precision + typedef vec<1, u16, lowp> lowp_u16vec1; + + /// Low qualifier 16 bit unsigned integer vector of 2 components type. + /// @see gtc_type_precision + typedef vec<2, u16, lowp> lowp_u16vec2; + + /// Low qualifier 16 bit unsigned integer vector of 3 components type. + /// @see gtc_type_precision + typedef vec<3, u16, lowp> lowp_u16vec3; + + /// Low qualifier 16 bit unsigned integer vector of 4 components type. + /// @see gtc_type_precision + typedef vec<4, u16, lowp> lowp_u16vec4; + + + /// Medium qualifier 16 bit unsigned integer scalar type. + /// @see gtc_type_precision + typedef vec<1, u16, mediump> mediump_u16vec1; + + /// Medium qualifier 16 bit unsigned integer vector of 2 components type. + /// @see gtc_type_precision + typedef vec<2, u16, mediump> mediump_u16vec2; + + /// Medium qualifier 16 bit unsigned integer vector of 3 components type. + /// @see gtc_type_precision + typedef vec<3, u16, mediump> mediump_u16vec3; + + /// Medium qualifier 16 bit unsigned integer vector of 4 components type. + /// @see gtc_type_precision + typedef vec<4, u16, mediump> mediump_u16vec4; + + + /// High qualifier 16 bit unsigned integer scalar type. + /// @see gtc_type_precision + typedef vec<1, u16, highp> highp_u16vec1; + + /// High qualifier 16 bit unsigned integer vector of 2 components type. + /// @see gtc_type_precision + typedef vec<2, u16, highp> highp_u16vec2; + + /// High qualifier 16 bit unsigned integer vector of 3 components type. + /// @see gtc_type_precision + typedef vec<3, u16, highp> highp_u16vec3; + + /// High qualifier 16 bit unsigned integer vector of 4 components type. + /// @see gtc_type_precision + typedef vec<4, u16, highp> highp_u16vec4; + + + + + /// Default qualifier 16 bit unsigned integer scalar type. + /// @see gtc_type_precision + typedef vec<1, u16, defaultp> u16vec1; + + /// Default qualifier 16 bit unsigned integer vector of 2 components type. + /// @see gtc_type_precision + typedef vec<2, u16, defaultp> u16vec2; + + /// Default qualifier 16 bit unsigned integer vector of 3 components type. + /// @see gtc_type_precision + typedef vec<3, u16, defaultp> u16vec3; + + /// Default qualifier 16 bit unsigned integer vector of 4 components type. + /// @see gtc_type_precision + typedef vec<4, u16, defaultp> u16vec4; + + + + /// Low qualifier 32 bit unsigned integer scalar type. + /// @see gtc_type_precision + typedef vec<1, u32, lowp> lowp_u32vec1; + + /// Low qualifier 32 bit unsigned integer vector of 2 components type. + /// @see gtc_type_precision + typedef vec<2, u32, lowp> lowp_u32vec2; + + /// Low qualifier 32 bit unsigned integer vector of 3 components type. + /// @see gtc_type_precision + typedef vec<3, u32, lowp> lowp_u32vec3; + + /// Low qualifier 32 bit unsigned integer vector of 4 components type. + /// @see gtc_type_precision + typedef vec<4, u32, lowp> lowp_u32vec4; + + + /// Medium qualifier 32 bit unsigned integer scalar type. + /// @see gtc_type_precision + typedef vec<1, u32, mediump> mediump_u32vec1; + + /// Medium qualifier 32 bit unsigned integer vector of 2 components type. + /// @see gtc_type_precision + typedef vec<2, u32, mediump> mediump_u32vec2; + + /// Medium qualifier 32 bit unsigned integer vector of 3 components type. + /// @see gtc_type_precision + typedef vec<3, u32, mediump> mediump_u32vec3; + + /// Medium qualifier 32 bit unsigned integer vector of 4 components type. + /// @see gtc_type_precision + typedef vec<4, u32, mediump> mediump_u32vec4; + + + /// High qualifier 32 bit unsigned integer scalar type. + /// @see gtc_type_precision + typedef vec<1, u32, highp> highp_u32vec1; + + /// High qualifier 32 bit unsigned integer vector of 2 components type. + /// @see gtc_type_precision + typedef vec<2, u32, highp> highp_u32vec2; + + /// High qualifier 32 bit unsigned integer vector of 3 components type. + /// @see gtc_type_precision + typedef vec<3, u32, highp> highp_u32vec3; + + /// High qualifier 32 bit unsigned integer vector of 4 components type. + /// @see gtc_type_precision + typedef vec<4, u32, highp> highp_u32vec4; + + + + /// Default qualifier 32 bit unsigned integer scalar type. + /// @see gtc_type_precision + typedef vec<1, u32, defaultp> u32vec1; + + /// Default qualifier 32 bit unsigned integer vector of 2 components type. + /// @see gtc_type_precision + typedef vec<2, u32, defaultp> u32vec2; + + /// Default qualifier 32 bit unsigned integer vector of 3 components type. + /// @see gtc_type_precision + typedef vec<3, u32, defaultp> u32vec3; + + /// Default qualifier 32 bit unsigned integer vector of 4 components type. + /// @see gtc_type_precision + typedef vec<4, u32, defaultp> u32vec4; + + + + + /// Low qualifier 64 bit unsigned integer scalar type. + /// @see gtc_type_precision + typedef vec<1, u64, lowp> lowp_u64vec1; + + /// Low qualifier 64 bit unsigned integer vector of 2 components type. + /// @see gtc_type_precision + typedef vec<2, u64, lowp> lowp_u64vec2; + + /// Low qualifier 64 bit unsigned integer vector of 3 components type. + /// @see gtc_type_precision + typedef vec<3, u64, lowp> lowp_u64vec3; + + /// Low qualifier 64 bit unsigned integer vector of 4 components type. + /// @see gtc_type_precision + typedef vec<4, u64, lowp> lowp_u64vec4; + + + /// Medium qualifier 64 bit unsigned integer scalar type. + /// @see gtc_type_precision + typedef vec<1, u64, mediump> mediump_u64vec1; + + /// Medium qualifier 64 bit unsigned integer vector of 2 components type. + /// @see gtc_type_precision + typedef vec<2, u64, mediump> mediump_u64vec2; + + /// Medium qualifier 64 bit unsigned integer vector of 3 components type. + /// @see gtc_type_precision + typedef vec<3, u64, mediump> mediump_u64vec3; + + /// Medium qualifier 64 bit unsigned integer vector of 4 components type. + /// @see gtc_type_precision + typedef vec<4, u64, mediump> mediump_u64vec4; + + + /// High qualifier 64 bit unsigned integer scalar type. + /// @see gtc_type_precision + typedef vec<1, u64, highp> highp_u64vec1; + + /// High qualifier 64 bit unsigned integer vector of 2 components type. + /// @see gtc_type_precision + typedef vec<2, u64, highp> highp_u64vec2; + + /// High qualifier 64 bit unsigned integer vector of 3 components type. + /// @see gtc_type_precision + typedef vec<3, u64, highp> highp_u64vec3; + + /// High qualifier 64 bit unsigned integer vector of 4 components type. + /// @see gtc_type_precision + typedef vec<4, u64, highp> highp_u64vec4; + + + + + /// Default qualifier 64 bit unsigned integer scalar type. + /// @see gtc_type_precision + typedef vec<1, u64, defaultp> u64vec1; + + /// Default qualifier 64 bit unsigned integer vector of 2 components type. + /// @see gtc_type_precision + typedef vec<2, u64, defaultp> u64vec2; + + /// Default qualifier 64 bit unsigned integer vector of 3 components type. + /// @see gtc_type_precision + typedef vec<3, u64, defaultp> u64vec3; + + /// Default qualifier 64 bit unsigned integer vector of 4 components type. + /// @see gtc_type_precision + typedef vec<4, u64, defaultp> u64vec4; + + + ////////////////////// + // Float vector types + + /// 32 bit single-qualifier floating-point scalar. + /// @see gtc_type_precision + typedef float32 float32_t; + + /// 32 bit single-qualifier floating-point scalar. + /// @see gtc_type_precision + typedef float32 f32; + +# ifndef GLM_FORCE_SINGLE_ONLY + + /// 64 bit double-qualifier floating-point scalar. + /// @see gtc_type_precision + typedef float64 float64_t; + + /// 64 bit double-qualifier floating-point scalar. + /// @see gtc_type_precision + typedef float64 f64; +# endif//GLM_FORCE_SINGLE_ONLY + + /// Single-qualifier floating-point vector of 1 component. + /// @see gtc_type_precision + typedef vec<1, float, defaultp> fvec1; + + /// Single-qualifier floating-point vector of 2 components. + /// @see gtc_type_precision + typedef vec<2, float, defaultp> fvec2; + + /// Single-qualifier floating-point vector of 3 components. + /// @see gtc_type_precision + typedef vec<3, float, defaultp> fvec3; + + /// Single-qualifier floating-point vector of 4 components. + /// @see gtc_type_precision + typedef vec<4, float, defaultp> fvec4; + + + /// Single-qualifier floating-point vector of 1 component. + /// @see gtc_type_precision + typedef vec<1, f32, defaultp> f32vec1; + + /// Single-qualifier floating-point vector of 2 components. + /// @see gtc_type_precision + typedef vec<2, f32, defaultp> f32vec2; + + /// Single-qualifier floating-point vector of 3 components. + /// @see gtc_type_precision + typedef vec<3, f32, defaultp> f32vec3; + + /// Single-qualifier floating-point vector of 4 components. + /// @see gtc_type_precision + typedef vec<4, f32, defaultp> f32vec4; + +# ifndef GLM_FORCE_SINGLE_ONLY + /// Double-qualifier floating-point vector of 1 component. + /// @see gtc_type_precision + typedef vec<1, f64, defaultp> f64vec1; + + /// Double-qualifier floating-point vector of 2 components. + /// @see gtc_type_precision + typedef vec<2, f64, defaultp> f64vec2; + + /// Double-qualifier floating-point vector of 3 components. + /// @see gtc_type_precision + typedef vec<3, f64, defaultp> f64vec3; + + /// Double-qualifier floating-point vector of 4 components. + /// @see gtc_type_precision + typedef vec<4, f64, defaultp> f64vec4; +# endif//GLM_FORCE_SINGLE_ONLY + + + ////////////////////// + // Float matrix types + + /// Single-qualifier floating-point 1x1 matrix. + /// @see gtc_type_precision + //typedef detail::tmat1x1 fmat1; + + /// Single-qualifier floating-point 2x2 matrix. + /// @see gtc_type_precision + typedef mat<2, 2, f32, defaultp> fmat2; + + /// Single-qualifier floating-point 3x3 matrix. + /// @see gtc_type_precision + typedef mat<3, 3, f32, defaultp> fmat3; + + /// Single-qualifier floating-point 4x4 matrix. + /// @see gtc_type_precision + typedef mat<4, 4, f32, defaultp> fmat4; + + + /// Single-qualifier floating-point 1x1 matrix. + /// @see gtc_type_precision + //typedef f32 fmat1x1; + + /// Single-qualifier floating-point 2x2 matrix. + /// @see gtc_type_precision + typedef mat<2, 2, f32, defaultp> fmat2x2; + + /// Single-qualifier floating-point 2x3 matrix. + /// @see gtc_type_precision + typedef mat<2, 3, f32, defaultp> fmat2x3; + + /// Single-qualifier floating-point 2x4 matrix. + /// @see gtc_type_precision + typedef mat<2, 4, f32, defaultp> fmat2x4; + + /// Single-qualifier floating-point 3x2 matrix. + /// @see gtc_type_precision + typedef mat<3, 2, f32, defaultp> fmat3x2; + + /// Single-qualifier floating-point 3x3 matrix. + /// @see gtc_type_precision + typedef mat<3, 3, f32, defaultp> fmat3x3; + + /// Single-qualifier floating-point 3x4 matrix. + /// @see gtc_type_precision + typedef mat<3, 4, f32, defaultp> fmat3x4; + + /// Single-qualifier floating-point 4x2 matrix. + /// @see gtc_type_precision + typedef mat<4, 2, f32, defaultp> fmat4x2; + + /// Single-qualifier floating-point 4x3 matrix. + /// @see gtc_type_precision + typedef mat<4, 3, f32, defaultp> fmat4x3; + + /// Single-qualifier floating-point 4x4 matrix. + /// @see gtc_type_precision + typedef mat<4, 4, f32, defaultp> fmat4x4; + + + /// Single-qualifier floating-point 1x1 matrix. + /// @see gtc_type_precision + //typedef detail::tmat1x1 f32mat1; + + /// Single-qualifier floating-point 2x2 matrix. + /// @see gtc_type_precision + typedef mat<2, 2, f32, defaultp> f32mat2; + + /// Single-qualifier floating-point 3x3 matrix. + /// @see gtc_type_precision + typedef mat<3, 3, f32, defaultp> f32mat3; + + /// Single-qualifier floating-point 4x4 matrix. + /// @see gtc_type_precision + typedef mat<4, 4, f32, defaultp> f32mat4; + + + /// Single-qualifier floating-point 1x1 matrix. + /// @see gtc_type_precision + //typedef f32 f32mat1x1; + + /// Single-qualifier floating-point 2x2 matrix. + /// @see gtc_type_precision + typedef mat<2, 2, f32, defaultp> f32mat2x2; + + /// Single-qualifier floating-point 2x3 matrix. + /// @see gtc_type_precision + typedef mat<2, 3, f32, defaultp> f32mat2x3; + + /// Single-qualifier floating-point 2x4 matrix. + /// @see gtc_type_precision + typedef mat<2, 4, f32, defaultp> f32mat2x4; + + /// Single-qualifier floating-point 3x2 matrix. + /// @see gtc_type_precision + typedef mat<3, 2, f32, defaultp> f32mat3x2; + + /// Single-qualifier floating-point 3x3 matrix. + /// @see gtc_type_precision + typedef mat<3, 3, f32, defaultp> f32mat3x3; + + /// Single-qualifier floating-point 3x4 matrix. + /// @see gtc_type_precision + typedef mat<3, 4, f32, defaultp> f32mat3x4; + + /// Single-qualifier floating-point 4x2 matrix. + /// @see gtc_type_precision + typedef mat<4, 2, f32, defaultp> f32mat4x2; + + /// Single-qualifier floating-point 4x3 matrix. + /// @see gtc_type_precision + typedef mat<4, 3, f32, defaultp> f32mat4x3; + + /// Single-qualifier floating-point 4x4 matrix. + /// @see gtc_type_precision + typedef mat<4, 4, f32, defaultp> f32mat4x4; + + +# ifndef GLM_FORCE_SINGLE_ONLY + + /// Double-qualifier floating-point 1x1 matrix. + /// @see gtc_type_precision + //typedef detail::tmat1x1 f64mat1; + + /// Double-qualifier floating-point 2x2 matrix. + /// @see gtc_type_precision + typedef mat<2, 2, f64, defaultp> f64mat2; + + /// Double-qualifier floating-point 3x3 matrix. + /// @see gtc_type_precision + typedef mat<3, 3, f64, defaultp> f64mat3; + + /// Double-qualifier floating-point 4x4 matrix. + /// @see gtc_type_precision + typedef mat<4, 4, f64, defaultp> f64mat4; + + + /// Double-qualifier floating-point 1x1 matrix. + /// @see gtc_type_precision + //typedef f64 f64mat1x1; + + /// Double-qualifier floating-point 2x2 matrix. + /// @see gtc_type_precision + typedef mat<2, 2, f64, defaultp> f64mat2x2; + + /// Double-qualifier floating-point 2x3 matrix. + /// @see gtc_type_precision + typedef mat<2, 3, f64, defaultp> f64mat2x3; + + /// Double-qualifier floating-point 2x4 matrix. + /// @see gtc_type_precision + typedef mat<2, 4, f64, defaultp> f64mat2x4; + + /// Double-qualifier floating-point 3x2 matrix. + /// @see gtc_type_precision + typedef mat<3, 2, f64, defaultp> f64mat3x2; + + /// Double-qualifier floating-point 3x3 matrix. + /// @see gtc_type_precision + typedef mat<3, 3, f64, defaultp> f64mat3x3; + + /// Double-qualifier floating-point 3x4 matrix. + /// @see gtc_type_precision + typedef mat<3, 4, f64, defaultp> f64mat3x4; + + /// Double-qualifier floating-point 4x2 matrix. + /// @see gtc_type_precision + typedef mat<4, 2, f64, defaultp> f64mat4x2; + + /// Double-qualifier floating-point 4x3 matrix. + /// @see gtc_type_precision + typedef mat<4, 3, f64, defaultp> f64mat4x3; + + /// Double-qualifier floating-point 4x4 matrix. + /// @see gtc_type_precision + typedef mat<4, 4, f64, defaultp> f64mat4x4; + +# endif//GLM_FORCE_SINGLE_ONLY + + ////////////////////////// + // Quaternion types + + /// Single-qualifier floating-point quaternion. + /// @see gtc_type_precision + typedef qua f32quat; + + /// Low single-qualifier floating-point quaternion. + /// @see gtc_type_precision + typedef qua lowp_f32quat; + + /// Low double-qualifier floating-point quaternion. + /// @see gtc_type_precision + typedef qua lowp_f64quat; + + /// Medium single-qualifier floating-point quaternion. + /// @see gtc_type_precision + typedef qua mediump_f32quat; + +# ifndef GLM_FORCE_SINGLE_ONLY + + /// Medium double-qualifier floating-point quaternion. + /// @see gtc_type_precision + typedef qua mediump_f64quat; + + /// High single-qualifier floating-point quaternion. + /// @see gtc_type_precision + typedef qua highp_f32quat; + + /// High double-qualifier floating-point quaternion. + /// @see gtc_type_precision + typedef qua highp_f64quat; + + /// Double-qualifier floating-point quaternion. + /// @see gtc_type_precision + typedef qua f64quat; + +# endif//GLM_FORCE_SINGLE_ONLY + + /// @} +}//namespace glm + +#include "type_precision.inl" diff --git a/src/GLMath/glm/gtc/type_precision.inl b/src/GLMath/glm/gtc/type_precision.inl new file mode 100644 index 000000000..ae8091206 --- /dev/null +++ b/src/GLMath/glm/gtc/type_precision.inl @@ -0,0 +1,6 @@ +/// @ref gtc_precision + +namespace glm +{ + +} diff --git a/src/GLMath/glm/gtc/type_ptr.hpp b/src/GLMath/glm/gtc/type_ptr.hpp new file mode 100644 index 000000000..d7e625aa5 --- /dev/null +++ b/src/GLMath/glm/gtc/type_ptr.hpp @@ -0,0 +1,230 @@ +/// @ref gtc_type_ptr +/// @file glm/gtc/type_ptr.hpp +/// +/// @see core (dependence) +/// @see gtc_quaternion (dependence) +/// +/// @defgroup gtc_type_ptr GLM_GTC_type_ptr +/// @ingroup gtc +/// +/// Include to use the features of this extension. +/// +/// Handles the interaction between pointers and vector, matrix types. +/// +/// This extension defines an overloaded function, glm::value_ptr. It returns +/// a pointer to the memory layout of the object. Matrix types store their values +/// in column-major order. +/// +/// This is useful for uploading data to matrices or copying data to buffer objects. +/// +/// Example: +/// @code +/// #include +/// #include +/// +/// glm::vec3 aVector(3); +/// glm::mat4 someMatrix(1.0); +/// +/// glUniform3fv(uniformLoc, 1, glm::value_ptr(aVector)); +/// glUniformMatrix4fv(uniformMatrixLoc, 1, GL_FALSE, glm::value_ptr(someMatrix)); +/// @endcode +/// +/// need to be included to use the features of this extension. + +#pragma once + +// Dependency: +#include "../gtc/quaternion.hpp" +#include "../gtc/vec1.hpp" +#include "../vec2.hpp" +#include "../vec3.hpp" +#include "../vec4.hpp" +#include "../mat2x2.hpp" +#include "../mat2x3.hpp" +#include "../mat2x4.hpp" +#include "../mat3x2.hpp" +#include "../mat3x3.hpp" +#include "../mat3x4.hpp" +#include "../mat4x2.hpp" +#include "../mat4x3.hpp" +#include "../mat4x4.hpp" +#include + +#if GLM_MESSAGES == GLM_ENABLE && !defined(GLM_EXT_INCLUDED) +# pragma message("GLM: GLM_GTC_type_ptr extension included") +#endif + +namespace glm +{ + /// @addtogroup gtc_type_ptr + /// @{ + + /// Return the constant address to the data of the input parameter. + /// @see gtc_type_ptr + template + GLM_FUNC_DECL typename genType::value_type const * value_ptr(genType const& v); + + /// Build a vector from a pointer. + /// @see gtc_type_ptr + template + GLM_FUNC_DECL vec<1, T, Q> make_vec1(vec<1, T, Q> const& v); + + /// Build a vector from a pointer. + /// @see gtc_type_ptr + template + GLM_FUNC_DECL vec<1, T, Q> make_vec1(vec<2, T, Q> const& v); + + /// Build a vector from a pointer. + /// @see gtc_type_ptr + template + GLM_FUNC_DECL vec<1, T, Q> make_vec1(vec<3, T, Q> const& v); + + /// Build a vector from a pointer. + /// @see gtc_type_ptr + template + GLM_FUNC_DECL vec<1, T, Q> make_vec1(vec<4, T, Q> const& v); + + /// Build a vector from a pointer. + /// @see gtc_type_ptr + template + GLM_FUNC_DECL vec<2, T, Q> make_vec2(vec<1, T, Q> const& v); + + /// Build a vector from a pointer. + /// @see gtc_type_ptr + template + GLM_FUNC_DECL vec<2, T, Q> make_vec2(vec<2, T, Q> const& v); + + /// Build a vector from a pointer. + /// @see gtc_type_ptr + template + GLM_FUNC_DECL vec<2, T, Q> make_vec2(vec<3, T, Q> const& v); + + /// Build a vector from a pointer. + /// @see gtc_type_ptr + template + GLM_FUNC_DECL vec<2, T, Q> make_vec2(vec<4, T, Q> const& v); + + /// Build a vector from a pointer. + /// @see gtc_type_ptr + template + GLM_FUNC_DECL vec<3, T, Q> make_vec3(vec<1, T, Q> const& v); + + /// Build a vector from a pointer. + /// @see gtc_type_ptr + template + GLM_FUNC_DECL vec<3, T, Q> make_vec3(vec<2, T, Q> const& v); + + /// Build a vector from a pointer. + /// @see gtc_type_ptr + template + GLM_FUNC_DECL vec<3, T, Q> make_vec3(vec<3, T, Q> const& v); + + /// Build a vector from a pointer. + /// @see gtc_type_ptr + template + GLM_FUNC_DECL vec<3, T, Q> make_vec3(vec<4, T, Q> const& v); + + /// Build a vector from a pointer. + /// @see gtc_type_ptr + template + GLM_FUNC_DECL vec<4, T, Q> make_vec4(vec<1, T, Q> const& v); + + /// Build a vector from a pointer. + /// @see gtc_type_ptr + template + GLM_FUNC_DECL vec<4, T, Q> make_vec4(vec<2, T, Q> const& v); + + /// Build a vector from a pointer. + /// @see gtc_type_ptr + template + GLM_FUNC_DECL vec<4, T, Q> make_vec4(vec<3, T, Q> const& v); + + /// Build a vector from a pointer. + /// @see gtc_type_ptr + template + GLM_FUNC_DECL vec<4, T, Q> make_vec4(vec<4, T, Q> const& v); + + /// Build a vector from a pointer. + /// @see gtc_type_ptr + template + GLM_FUNC_DECL vec<2, T, defaultp> make_vec2(T const * const ptr); + + /// Build a vector from a pointer. + /// @see gtc_type_ptr + template + GLM_FUNC_DECL vec<3, T, defaultp> make_vec3(T const * const ptr); + + /// Build a vector from a pointer. + /// @see gtc_type_ptr + template + GLM_FUNC_DECL vec<4, T, defaultp> make_vec4(T const * const ptr); + + /// Build a matrix from a pointer. + /// @see gtc_type_ptr + template + GLM_FUNC_DECL mat<2, 2, T, defaultp> make_mat2x2(T const * const ptr); + + /// Build a matrix from a pointer. + /// @see gtc_type_ptr + template + GLM_FUNC_DECL mat<2, 3, T, defaultp> make_mat2x3(T const * const ptr); + + /// Build a matrix from a pointer. + /// @see gtc_type_ptr + template + GLM_FUNC_DECL mat<2, 4, T, defaultp> make_mat2x4(T const * const ptr); + + /// Build a matrix from a pointer. + /// @see gtc_type_ptr + template + GLM_FUNC_DECL mat<3, 2, T, defaultp> make_mat3x2(T const * const ptr); + + /// Build a matrix from a pointer. + /// @see gtc_type_ptr + template + GLM_FUNC_DECL mat<3, 3, T, defaultp> make_mat3x3(T const * const ptr); + + /// Build a matrix from a pointer. + /// @see gtc_type_ptr + template + GLM_FUNC_DECL mat<3, 4, T, defaultp> make_mat3x4(T const * const ptr); + + /// Build a matrix from a pointer. + /// @see gtc_type_ptr + template + GLM_FUNC_DECL mat<4, 2, T, defaultp> make_mat4x2(T const * const ptr); + + /// Build a matrix from a pointer. + /// @see gtc_type_ptr + template + GLM_FUNC_DECL mat<4, 3, T, defaultp> make_mat4x3(T const * const ptr); + + /// Build a matrix from a pointer. + /// @see gtc_type_ptr + template + GLM_FUNC_DECL mat<4, 4, T, defaultp> make_mat4x4(T const * const ptr); + + /// Build a matrix from a pointer. + /// @see gtc_type_ptr + template + GLM_FUNC_DECL mat<2, 2, T, defaultp> make_mat2(T const * const ptr); + + /// Build a matrix from a pointer. + /// @see gtc_type_ptr + template + GLM_FUNC_DECL mat<3, 3, T, defaultp> make_mat3(T const * const ptr); + + /// Build a matrix from a pointer. + /// @see gtc_type_ptr + template + GLM_FUNC_DECL mat<4, 4, T, defaultp> make_mat4(T const * const ptr); + + /// Build a quaternion from a pointer. + /// @see gtc_type_ptr + template + GLM_FUNC_DECL qua make_quat(T const * const ptr); + + /// @} +}//namespace glm + +#include "type_ptr.inl" diff --git a/src/GLMath/glm/gtc/type_ptr.inl b/src/GLMath/glm/gtc/type_ptr.inl new file mode 100644 index 000000000..71df4d30d --- /dev/null +++ b/src/GLMath/glm/gtc/type_ptr.inl @@ -0,0 +1,386 @@ +/// @ref gtc_type_ptr + +#include + +namespace glm +{ + /// @addtogroup gtc_type_ptr + /// @{ + + template + GLM_FUNC_QUALIFIER T const* value_ptr(vec<2, T, Q> const& v) + { + return &(v.x); + } + + template + GLM_FUNC_QUALIFIER T* value_ptr(vec<2, T, Q>& v) + { + return &(v.x); + } + + template + GLM_FUNC_QUALIFIER T const * value_ptr(vec<3, T, Q> const& v) + { + return &(v.x); + } + + template + GLM_FUNC_QUALIFIER T* value_ptr(vec<3, T, Q>& v) + { + return &(v.x); + } + + template + GLM_FUNC_QUALIFIER T const* value_ptr(vec<4, T, Q> const& v) + { + return &(v.x); + } + + template + GLM_FUNC_QUALIFIER T* value_ptr(vec<4, T, Q>& v) + { + return &(v.x); + } + + template + GLM_FUNC_QUALIFIER T const* value_ptr(mat<2, 2, T, Q> const& m) + { + return &(m[0].x); + } + + template + GLM_FUNC_QUALIFIER T* value_ptr(mat<2, 2, T, Q>& m) + { + return &(m[0].x); + } + + template + GLM_FUNC_QUALIFIER T const* value_ptr(mat<3, 3, T, Q> const& m) + { + return &(m[0].x); + } + + template + GLM_FUNC_QUALIFIER T* value_ptr(mat<3, 3, T, Q>& m) + { + return &(m[0].x); + } + + template + GLM_FUNC_QUALIFIER T const* value_ptr(mat<4, 4, T, Q> const& m) + { + return &(m[0].x); + } + + template + GLM_FUNC_QUALIFIER T* value_ptr(mat<4, 4, T, Q>& m) + { + return &(m[0].x); + } + + template + GLM_FUNC_QUALIFIER T const* value_ptr(mat<2, 3, T, Q> const& m) + { + return &(m[0].x); + } + + template + GLM_FUNC_QUALIFIER T* value_ptr(mat<2, 3, T, Q>& m) + { + return &(m[0].x); + } + + template + GLM_FUNC_QUALIFIER T const* value_ptr(mat<3, 2, T, Q> const& m) + { + return &(m[0].x); + } + + template + GLM_FUNC_QUALIFIER T* value_ptr(mat<3, 2, T, Q>& m) + { + return &(m[0].x); + } + + template + GLM_FUNC_QUALIFIER T const* value_ptr(mat<2, 4, T, Q> const& m) + { + return &(m[0].x); + } + + template + GLM_FUNC_QUALIFIER T* value_ptr(mat<2, 4, T, Q>& m) + { + return &(m[0].x); + } + + template + GLM_FUNC_QUALIFIER T const* value_ptr(mat<4, 2, T, Q> const& m) + { + return &(m[0].x); + } + + template + GLM_FUNC_QUALIFIER T* value_ptr(mat<4, 2, T, Q>& m) + { + return &(m[0].x); + } + + template + GLM_FUNC_QUALIFIER T const* value_ptr(mat<3, 4, T, Q> const& m) + { + return &(m[0].x); + } + + template + GLM_FUNC_QUALIFIER T* value_ptr(mat<3, 4, T, Q>& m) + { + return &(m[0].x); + } + + template + GLM_FUNC_QUALIFIER T const* value_ptr(mat<4, 3, T, Q> const& m) + { + return &(m[0].x); + } + + template + GLM_FUNC_QUALIFIER T * value_ptr(mat<4, 3, T, Q>& m) + { + return &(m[0].x); + } + + template + GLM_FUNC_QUALIFIER T const * value_ptr(qua const& q) + { + return &(q[0]); + } + + template + GLM_FUNC_QUALIFIER T* value_ptr(qua& q) + { + return &(q[0]); + } + + template + inline vec<1, T, Q> make_vec1(vec<1, T, Q> const& v) + { + return v; + } + + template + inline vec<1, T, Q> make_vec1(vec<2, T, Q> const& v) + { + return vec<1, T, Q>(v); + } + + template + inline vec<1, T, Q> make_vec1(vec<3, T, Q> const& v) + { + return vec<1, T, Q>(v); + } + + template + inline vec<1, T, Q> make_vec1(vec<4, T, Q> const& v) + { + return vec<1, T, Q>(v); + } + + template + inline vec<2, T, Q> make_vec2(vec<1, T, Q> const& v) + { + return vec<2, T, Q>(v.x, static_cast(0)); + } + + template + inline vec<2, T, Q> make_vec2(vec<2, T, Q> const& v) + { + return v; + } + + template + inline vec<2, T, Q> make_vec2(vec<3, T, Q> const& v) + { + return vec<2, T, Q>(v); + } + + template + inline vec<2, T, Q> make_vec2(vec<4, T, Q> const& v) + { + return vec<2, T, Q>(v); + } + + template + inline vec<3, T, Q> make_vec3(vec<1, T, Q> const& v) + { + return vec<3, T, Q>(v.x, static_cast(0), static_cast(0)); + } + + template + inline vec<3, T, Q> make_vec3(vec<2, T, Q> const& v) + { + return vec<3, T, Q>(v.x, v.y, static_cast(0)); + } + + template + inline vec<3, T, Q> make_vec3(vec<3, T, Q> const& v) + { + return v; + } + + template + inline vec<3, T, Q> make_vec3(vec<4, T, Q> const& v) + { + return vec<3, T, Q>(v); + } + + template + inline vec<4, T, Q> make_vec4(vec<1, T, Q> const& v) + { + return vec<4, T, Q>(v.x, static_cast(0), static_cast(0), static_cast(1)); + } + + template + inline vec<4, T, Q> make_vec4(vec<2, T, Q> const& v) + { + return vec<4, T, Q>(v.x, v.y, static_cast(0), static_cast(1)); + } + + template + inline vec<4, T, Q> make_vec4(vec<3, T, Q> const& v) + { + return vec<4, T, Q>(v.x, v.y, v.z, static_cast(1)); + } + + template + inline vec<4, T, Q> make_vec4(vec<4, T, Q> const& v) + { + return v; + } + + template + GLM_FUNC_QUALIFIER vec<2, T, defaultp> make_vec2(T const *const ptr) + { + vec<2, T, defaultp> Result; + memcpy(value_ptr(Result), ptr, sizeof(vec<2, T, defaultp>)); + return Result; + } + + template + GLM_FUNC_QUALIFIER vec<3, T, defaultp> make_vec3(T const *const ptr) + { + vec<3, T, defaultp> Result; + memcpy(value_ptr(Result), ptr, sizeof(vec<3, T, defaultp>)); + return Result; + } + + template + GLM_FUNC_QUALIFIER vec<4, T, defaultp> make_vec4(T const *const ptr) + { + vec<4, T, defaultp> Result; + memcpy(value_ptr(Result), ptr, sizeof(vec<4, T, defaultp>)); + return Result; + } + + template + GLM_FUNC_QUALIFIER mat<2, 2, T, defaultp> make_mat2x2(T const *const ptr) + { + mat<2, 2, T, defaultp> Result; + memcpy(value_ptr(Result), ptr, sizeof(mat<2, 2, T, defaultp>)); + return Result; + } + + template + GLM_FUNC_QUALIFIER mat<2, 3, T, defaultp> make_mat2x3(T const *const ptr) + { + mat<2, 3, T, defaultp> Result; + memcpy(value_ptr(Result), ptr, sizeof(mat<2, 3, T, defaultp>)); + return Result; + } + + template + GLM_FUNC_QUALIFIER mat<2, 4, T, defaultp> make_mat2x4(T const *const ptr) + { + mat<2, 4, T, defaultp> Result; + memcpy(value_ptr(Result), ptr, sizeof(mat<2, 4, T, defaultp>)); + return Result; + } + + template + GLM_FUNC_QUALIFIER mat<3, 2, T, defaultp> make_mat3x2(T const *const ptr) + { + mat<3, 2, T, defaultp> Result; + memcpy(value_ptr(Result), ptr, sizeof(mat<3, 2, T, defaultp>)); + return Result; + } + + template + GLM_FUNC_QUALIFIER mat<3, 3, T, defaultp> make_mat3x3(T const *const ptr) + { + mat<3, 3, T, defaultp> Result; + memcpy(value_ptr(Result), ptr, sizeof(mat<3, 3, T, defaultp>)); + return Result; + } + + template + GLM_FUNC_QUALIFIER mat<3, 4, T, defaultp> make_mat3x4(T const *const ptr) + { + mat<3, 4, T, defaultp> Result; + memcpy(value_ptr(Result), ptr, sizeof(mat<3, 4, T, defaultp>)); + return Result; + } + + template + GLM_FUNC_QUALIFIER mat<4, 2, T, defaultp> make_mat4x2(T const *const ptr) + { + mat<4, 2, T, defaultp> Result; + memcpy(value_ptr(Result), ptr, sizeof(mat<4, 2, T, defaultp>)); + return Result; + } + + template + GLM_FUNC_QUALIFIER mat<4, 3, T, defaultp> make_mat4x3(T const *const ptr) + { + mat<4, 3, T, defaultp> Result; + memcpy(value_ptr(Result), ptr, sizeof(mat<4, 3, T, defaultp>)); + return Result; + } + + template + GLM_FUNC_QUALIFIER mat<4, 4, T, defaultp> make_mat4x4(T const *const ptr) + { + mat<4, 4, T, defaultp> Result; + memcpy(value_ptr(Result), ptr, sizeof(mat<4, 4, T, defaultp>)); + return Result; + } + + template + GLM_FUNC_QUALIFIER mat<2, 2, T, defaultp> make_mat2(T const *const ptr) + { + return make_mat2x2(ptr); + } + + template + GLM_FUNC_QUALIFIER mat<3, 3, T, defaultp> make_mat3(T const *const ptr) + { + return make_mat3x3(ptr); + } + + template + GLM_FUNC_QUALIFIER mat<4, 4, T, defaultp> make_mat4(T const *const ptr) + { + return make_mat4x4(ptr); + } + + template + GLM_FUNC_QUALIFIER qua make_quat(T const *const ptr) + { + qua Result; + memcpy(value_ptr(Result), ptr, sizeof(qua)); + return Result; + } + + /// @} +}//namespace glm + diff --git a/src/GLMath/glm/gtc/ulp.hpp b/src/GLMath/glm/gtc/ulp.hpp new file mode 100644 index 000000000..01f4f11e1 --- /dev/null +++ b/src/GLMath/glm/gtc/ulp.hpp @@ -0,0 +1,24 @@ +/// @ref gtc_ulp +/// @file glm/gtc/ulp.hpp +/// +/// @see core (dependence) +/// +/// @defgroup gtc_ulp GLM_GTC_ulp +/// @ingroup gtc +/// +/// Include to use the features of this extension. +/// +/// Allow the measurement of the accuracy of a function against a reference +/// implementation. This extension works on floating-point data and provide results +/// in ULP. + +#pragma once + +// Dependencies +#include "../ext/scalar_ulp.hpp" +#include "../ext/vector_ulp.hpp" + +#if GLM_MESSAGES == GLM_ENABLE && !defined(GLM_EXT_INCLUDED) +# pragma message("GLM: GLM_GTC_ulp extension included") +#endif + diff --git a/src/GLMath/glm/gtc/ulp.inl b/src/GLMath/glm/gtc/ulp.inl new file mode 100644 index 000000000..53f4950a9 --- /dev/null +++ b/src/GLMath/glm/gtc/ulp.inl @@ -0,0 +1,3 @@ +/// @ref gtc_ulp +/// + diff --git a/src/GLMath/glm/gtc/vec1.hpp b/src/GLMath/glm/gtc/vec1.hpp new file mode 100644 index 000000000..c20be8747 --- /dev/null +++ b/src/GLMath/glm/gtc/vec1.hpp @@ -0,0 +1,30 @@ +/// @ref gtc_vec1 +/// @file glm/gtc/vec1.hpp +/// +/// @see core (dependence) +/// +/// @defgroup gtc_vec1 GLM_GTC_vec1 +/// @ingroup gtc +/// +/// Include to use the features of this extension. +/// +/// Add vec1, ivec1, uvec1 and bvec1 types. + +#pragma once + +// Dependency: +#include "../ext/vector_bool1.hpp" +#include "../ext/vector_bool1_precision.hpp" +#include "../ext/vector_float1.hpp" +#include "../ext/vector_float1_precision.hpp" +#include "../ext/vector_double1.hpp" +#include "../ext/vector_double1_precision.hpp" +#include "../ext/vector_int1.hpp" +#include "../ext/vector_int1_precision.hpp" +#include "../ext/vector_uint1.hpp" +#include "../ext/vector_uint1_precision.hpp" + +#if GLM_MESSAGES == GLM_ENABLE && !defined(GLM_EXT_INCLUDED) +# pragma message("GLM: GLM_GTC_vec1 extension included") +#endif + diff --git a/src/GLMath/glm/gtx/associated_min_max.hpp b/src/GLMath/glm/gtx/associated_min_max.hpp new file mode 100644 index 000000000..d1a41c06b --- /dev/null +++ b/src/GLMath/glm/gtx/associated_min_max.hpp @@ -0,0 +1,207 @@ +/// @ref gtx_associated_min_max +/// @file glm/gtx/associated_min_max.hpp +/// +/// @see core (dependence) +/// @see gtx_extented_min_max (dependence) +/// +/// @defgroup gtx_associated_min_max GLM_GTX_associated_min_max +/// @ingroup gtx +/// +/// Include to use the features of this extension. +/// +/// @brief Min and max functions that return associated values not the compared onces. + +#pragma once + +// Dependency: +#include "../glm.hpp" + +#if GLM_MESSAGES == GLM_ENABLE && !defined(GLM_EXT_INCLUDED) +# ifndef GLM_ENABLE_EXPERIMENTAL +# pragma message("GLM: GLM_GTX_associated_min_max is an experimental extension and may change in the future. Use #define GLM_ENABLE_EXPERIMENTAL before including it, if you really want to use it.") +# else +# pragma message("GLM: GLM_GTX_associated_min_max extension included") +# endif +#endif + +namespace glm +{ + /// @addtogroup gtx_associated_min_max + /// @{ + + /// Minimum comparison between 2 variables and returns 2 associated variable values + /// @see gtx_associated_min_max + template + GLM_FUNC_DECL U associatedMin(T x, U a, T y, U b); + + /// Minimum comparison between 2 variables and returns 2 associated variable values + /// @see gtx_associated_min_max + template + GLM_FUNC_DECL vec<2, U, Q> associatedMin( + vec const& x, vec const& a, + vec const& y, vec const& b); + + /// Minimum comparison between 2 variables and returns 2 associated variable values + /// @see gtx_associated_min_max + template + GLM_FUNC_DECL vec associatedMin( + T x, const vec& a, + T y, const vec& b); + + /// Minimum comparison between 2 variables and returns 2 associated variable values + /// @see gtx_associated_min_max + template + GLM_FUNC_DECL vec associatedMin( + vec const& x, U a, + vec const& y, U b); + + /// Minimum comparison between 3 variables and returns 3 associated variable values + /// @see gtx_associated_min_max + template + GLM_FUNC_DECL U associatedMin( + T x, U a, + T y, U b, + T z, U c); + + /// Minimum comparison between 3 variables and returns 3 associated variable values + /// @see gtx_associated_min_max + template + GLM_FUNC_DECL vec associatedMin( + vec const& x, vec const& a, + vec const& y, vec const& b, + vec const& z, vec const& c); + + /// Minimum comparison between 4 variables and returns 4 associated variable values + /// @see gtx_associated_min_max + template + GLM_FUNC_DECL U associatedMin( + T x, U a, + T y, U b, + T z, U c, + T w, U d); + + /// Minimum comparison between 4 variables and returns 4 associated variable values + /// @see gtx_associated_min_max + template + GLM_FUNC_DECL vec associatedMin( + vec const& x, vec const& a, + vec const& y, vec const& b, + vec const& z, vec const& c, + vec const& w, vec const& d); + + /// Minimum comparison between 4 variables and returns 4 associated variable values + /// @see gtx_associated_min_max + template + GLM_FUNC_DECL vec associatedMin( + T x, vec const& a, + T y, vec const& b, + T z, vec const& c, + T w, vec const& d); + + /// Minimum comparison between 4 variables and returns 4 associated variable values + /// @see gtx_associated_min_max + template + GLM_FUNC_DECL vec associatedMin( + vec const& x, U a, + vec const& y, U b, + vec const& z, U c, + vec const& w, U d); + + /// Maximum comparison between 2 variables and returns 2 associated variable values + /// @see gtx_associated_min_max + template + GLM_FUNC_DECL U associatedMax(T x, U a, T y, U b); + + /// Maximum comparison between 2 variables and returns 2 associated variable values + /// @see gtx_associated_min_max + template + GLM_FUNC_DECL vec<2, U, Q> associatedMax( + vec const& x, vec const& a, + vec const& y, vec const& b); + + /// Maximum comparison between 2 variables and returns 2 associated variable values + /// @see gtx_associated_min_max + template + GLM_FUNC_DECL vec associatedMax( + T x, vec const& a, + T y, vec const& b); + + /// Maximum comparison between 2 variables and returns 2 associated variable values + /// @see gtx_associated_min_max + template + GLM_FUNC_DECL vec associatedMax( + vec const& x, U a, + vec const& y, U b); + + /// Maximum comparison between 3 variables and returns 3 associated variable values + /// @see gtx_associated_min_max + template + GLM_FUNC_DECL U associatedMax( + T x, U a, + T y, U b, + T z, U c); + + /// Maximum comparison between 3 variables and returns 3 associated variable values + /// @see gtx_associated_min_max + template + GLM_FUNC_DECL vec associatedMax( + vec const& x, vec const& a, + vec const& y, vec const& b, + vec const& z, vec const& c); + + /// Maximum comparison between 3 variables and returns 3 associated variable values + /// @see gtx_associated_min_max + template + GLM_FUNC_DECL vec associatedMax( + T x, vec const& a, + T y, vec const& b, + T z, vec const& c); + + /// Maximum comparison between 3 variables and returns 3 associated variable values + /// @see gtx_associated_min_max + template + GLM_FUNC_DECL vec associatedMax( + vec const& x, U a, + vec const& y, U b, + vec const& z, U c); + + /// Maximum comparison between 4 variables and returns 4 associated variable values + /// @see gtx_associated_min_max + template + GLM_FUNC_DECL U associatedMax( + T x, U a, + T y, U b, + T z, U c, + T w, U d); + + /// Maximum comparison between 4 variables and returns 4 associated variable values + /// @see gtx_associated_min_max + template + GLM_FUNC_DECL vec associatedMax( + vec const& x, vec const& a, + vec const& y, vec const& b, + vec const& z, vec const& c, + vec const& w, vec const& d); + + /// Maximum comparison between 4 variables and returns 4 associated variable values + /// @see gtx_associated_min_max + template + GLM_FUNC_DECL vec associatedMax( + T x, vec const& a, + T y, vec const& b, + T z, vec const& c, + T w, vec const& d); + + /// Maximum comparison between 4 variables and returns 4 associated variable values + /// @see gtx_associated_min_max + template + GLM_FUNC_DECL vec associatedMax( + vec const& x, U a, + vec const& y, U b, + vec const& z, U c, + vec const& w, U d); + + /// @} +} //namespace glm + +#include "associated_min_max.inl" diff --git a/src/GLMath/glm/gtx/associated_min_max.inl b/src/GLMath/glm/gtx/associated_min_max.inl new file mode 100644 index 000000000..5186c471c --- /dev/null +++ b/src/GLMath/glm/gtx/associated_min_max.inl @@ -0,0 +1,354 @@ +/// @ref gtx_associated_min_max + +namespace glm{ + +// Min comparison between 2 variables +template +GLM_FUNC_QUALIFIER U associatedMin(T x, U a, T y, U b) +{ + return x < y ? a : b; +} + +template +GLM_FUNC_QUALIFIER vec<2, U, Q> associatedMin +( + vec const& x, vec const& a, + vec const& y, vec const& b +) +{ + vec Result; + for(length_t i = 0, n = Result.length(); i < n; ++i) + Result[i] = x[i] < y[i] ? a[i] : b[i]; + return Result; +} + +template +GLM_FUNC_QUALIFIER vec associatedMin +( + T x, const vec& a, + T y, const vec& b +) +{ + vec Result; + for(length_t i = 0, n = Result.length(); i < n; ++i) + Result[i] = x < y ? a[i] : b[i]; + return Result; +} + +template +GLM_FUNC_QUALIFIER vec associatedMin +( + vec const& x, U a, + vec const& y, U b +) +{ + vec Result; + for(length_t i = 0, n = Result.length(); i < n; ++i) + Result[i] = x[i] < y[i] ? a : b; + return Result; +} + +// Min comparison between 3 variables +template +GLM_FUNC_QUALIFIER U associatedMin +( + T x, U a, + T y, U b, + T z, U c +) +{ + U Result = x < y ? (x < z ? a : c) : (y < z ? b : c); + return Result; +} + +template +GLM_FUNC_QUALIFIER vec associatedMin +( + vec const& x, vec const& a, + vec const& y, vec const& b, + vec const& z, vec const& c +) +{ + vec Result; + for(length_t i = 0, n = Result.length(); i < n; ++i) + Result[i] = x[i] < y[i] ? (x[i] < z[i] ? a[i] : c[i]) : (y[i] < z[i] ? b[i] : c[i]); + return Result; +} + +// Min comparison between 4 variables +template +GLM_FUNC_QUALIFIER U associatedMin +( + T x, U a, + T y, U b, + T z, U c, + T w, U d +) +{ + T Test1 = min(x, y); + T Test2 = min(z, w); + U Result1 = x < y ? a : b; + U Result2 = z < w ? c : d; + U Result = Test1 < Test2 ? Result1 : Result2; + return Result; +} + +// Min comparison between 4 variables +template +GLM_FUNC_QUALIFIER vec associatedMin +( + vec const& x, vec const& a, + vec const& y, vec const& b, + vec const& z, vec const& c, + vec const& w, vec const& d +) +{ + vec Result; + for(length_t i = 0, n = Result.length(); i < n; ++i) + { + T Test1 = min(x[i], y[i]); + T Test2 = min(z[i], w[i]); + U Result1 = x[i] < y[i] ? a[i] : b[i]; + U Result2 = z[i] < w[i] ? c[i] : d[i]; + Result[i] = Test1 < Test2 ? Result1 : Result2; + } + return Result; +} + +// Min comparison between 4 variables +template +GLM_FUNC_QUALIFIER vec associatedMin +( + T x, vec const& a, + T y, vec const& b, + T z, vec const& c, + T w, vec const& d +) +{ + T Test1 = min(x, y); + T Test2 = min(z, w); + + vec Result; + for(length_t i = 0, n = Result.length(); i < n; ++i) + { + U Result1 = x < y ? a[i] : b[i]; + U Result2 = z < w ? c[i] : d[i]; + Result[i] = Test1 < Test2 ? Result1 : Result2; + } + return Result; +} + +// Min comparison between 4 variables +template +GLM_FUNC_QUALIFIER vec associatedMin +( + vec const& x, U a, + vec const& y, U b, + vec const& z, U c, + vec const& w, U d +) +{ + vec Result; + for(length_t i = 0, n = Result.length(); i < n; ++i) + { + T Test1 = min(x[i], y[i]); + T Test2 = min(z[i], w[i]); + U Result1 = x[i] < y[i] ? a : b; + U Result2 = z[i] < w[i] ? c : d; + Result[i] = Test1 < Test2 ? Result1 : Result2; + } + return Result; +} + +// Max comparison between 2 variables +template +GLM_FUNC_QUALIFIER U associatedMax(T x, U a, T y, U b) +{ + return x > y ? a : b; +} + +// Max comparison between 2 variables +template +GLM_FUNC_QUALIFIER vec<2, U, Q> associatedMax +( + vec const& x, vec const& a, + vec const& y, vec const& b +) +{ + vec Result; + for(length_t i = 0, n = Result.length(); i < n; ++i) + Result[i] = x[i] > y[i] ? a[i] : b[i]; + return Result; +} + +// Max comparison between 2 variables +template +GLM_FUNC_QUALIFIER vec associatedMax +( + T x, vec const& a, + T y, vec const& b +) +{ + vec Result; + for(length_t i = 0, n = Result.length(); i < n; ++i) + Result[i] = x > y ? a[i] : b[i]; + return Result; +} + +// Max comparison between 2 variables +template +GLM_FUNC_QUALIFIER vec associatedMax +( + vec const& x, U a, + vec const& y, U b +) +{ + vec Result; + for(length_t i = 0, n = Result.length(); i < n; ++i) + Result[i] = x[i] > y[i] ? a : b; + return Result; +} + +// Max comparison between 3 variables +template +GLM_FUNC_QUALIFIER U associatedMax +( + T x, U a, + T y, U b, + T z, U c +) +{ + U Result = x > y ? (x > z ? a : c) : (y > z ? b : c); + return Result; +} + +// Max comparison between 3 variables +template +GLM_FUNC_QUALIFIER vec associatedMax +( + vec const& x, vec const& a, + vec const& y, vec const& b, + vec const& z, vec const& c +) +{ + vec Result; + for(length_t i = 0, n = Result.length(); i < n; ++i) + Result[i] = x[i] > y[i] ? (x[i] > z[i] ? a[i] : c[i]) : (y[i] > z[i] ? b[i] : c[i]); + return Result; +} + +// Max comparison between 3 variables +template +GLM_FUNC_QUALIFIER vec associatedMax +( + T x, vec const& a, + T y, vec const& b, + T z, vec const& c +) +{ + vec Result; + for(length_t i = 0, n = Result.length(); i < n; ++i) + Result[i] = x > y ? (x > z ? a[i] : c[i]) : (y > z ? b[i] : c[i]); + return Result; +} + +// Max comparison between 3 variables +template +GLM_FUNC_QUALIFIER vec associatedMax +( + vec const& x, U a, + vec const& y, U b, + vec const& z, U c +) +{ + vec Result; + for(length_t i = 0, n = Result.length(); i < n; ++i) + Result[i] = x[i] > y[i] ? (x[i] > z[i] ? a : c) : (y[i] > z[i] ? b : c); + return Result; +} + +// Max comparison between 4 variables +template +GLM_FUNC_QUALIFIER U associatedMax +( + T x, U a, + T y, U b, + T z, U c, + T w, U d +) +{ + T Test1 = max(x, y); + T Test2 = max(z, w); + U Result1 = x > y ? a : b; + U Result2 = z > w ? c : d; + U Result = Test1 > Test2 ? Result1 : Result2; + return Result; +} + +// Max comparison between 4 variables +template +GLM_FUNC_QUALIFIER vec associatedMax +( + vec const& x, vec const& a, + vec const& y, vec const& b, + vec const& z, vec const& c, + vec const& w, vec const& d +) +{ + vec Result; + for(length_t i = 0, n = Result.length(); i < n; ++i) + { + T Test1 = max(x[i], y[i]); + T Test2 = max(z[i], w[i]); + U Result1 = x[i] > y[i] ? a[i] : b[i]; + U Result2 = z[i] > w[i] ? c[i] : d[i]; + Result[i] = Test1 > Test2 ? Result1 : Result2; + } + return Result; +} + +// Max comparison between 4 variables +template +GLM_FUNC_QUALIFIER vec associatedMax +( + T x, vec const& a, + T y, vec const& b, + T z, vec const& c, + T w, vec const& d +) +{ + T Test1 = max(x, y); + T Test2 = max(z, w); + + vec Result; + for(length_t i = 0, n = Result.length(); i < n; ++i) + { + U Result1 = x > y ? a[i] : b[i]; + U Result2 = z > w ? c[i] : d[i]; + Result[i] = Test1 > Test2 ? Result1 : Result2; + } + return Result; +} + +// Max comparison between 4 variables +template +GLM_FUNC_QUALIFIER vec associatedMax +( + vec const& x, U a, + vec const& y, U b, + vec const& z, U c, + vec const& w, U d +) +{ + vec Result; + for(length_t i = 0, n = Result.length(); i < n; ++i) + { + T Test1 = max(x[i], y[i]); + T Test2 = max(z[i], w[i]); + U Result1 = x[i] > y[i] ? a : b; + U Result2 = z[i] > w[i] ? c : d; + Result[i] = Test1 > Test2 ? Result1 : Result2; + } + return Result; +} +}//namespace glm diff --git a/src/GLMath/glm/gtx/bit.hpp b/src/GLMath/glm/gtx/bit.hpp new file mode 100644 index 000000000..60a7aef1b --- /dev/null +++ b/src/GLMath/glm/gtx/bit.hpp @@ -0,0 +1,98 @@ +/// @ref gtx_bit +/// @file glm/gtx/bit.hpp +/// +/// @see core (dependence) +/// +/// @defgroup gtx_bit GLM_GTX_bit +/// @ingroup gtx +/// +/// Include to use the features of this extension. +/// +/// Allow to perform bit operations on integer values + +#pragma once + +// Dependencies +#include "../gtc/bitfield.hpp" + +#if GLM_MESSAGES == GLM_ENABLE && !defined(GLM_EXT_INCLUDED) +# ifndef GLM_ENABLE_EXPERIMENTAL +# pragma message("GLM: GLM_GTX_bit is an experimental extension and may change in the future. Use #define GLM_ENABLE_EXPERIMENTAL before including it, if you really want to use it.") +# else +# pragma message("GLM: GLM_GTX_bit extension included") +# endif +#endif + +namespace glm +{ + /// @addtogroup gtx_bit + /// @{ + + /// @see gtx_bit + template + GLM_FUNC_DECL genIUType highestBitValue(genIUType Value); + + /// @see gtx_bit + template + GLM_FUNC_DECL genIUType lowestBitValue(genIUType Value); + + /// Find the highest bit set to 1 in a integer variable and return its value. + /// + /// @see gtx_bit + template + GLM_FUNC_DECL vec highestBitValue(vec const& value); + + /// Return the power of two number which value is just higher the input value. + /// Deprecated, use ceilPowerOfTwo from GTC_round instead + /// + /// @see gtc_round + /// @see gtx_bit + template + GLM_DEPRECATED GLM_FUNC_DECL genIUType powerOfTwoAbove(genIUType Value); + + /// Return the power of two number which value is just higher the input value. + /// Deprecated, use ceilPowerOfTwo from GTC_round instead + /// + /// @see gtc_round + /// @see gtx_bit + template + GLM_DEPRECATED GLM_FUNC_DECL vec powerOfTwoAbove(vec const& value); + + /// Return the power of two number which value is just lower the input value. + /// Deprecated, use floorPowerOfTwo from GTC_round instead + /// + /// @see gtc_round + /// @see gtx_bit + template + GLM_DEPRECATED GLM_FUNC_DECL genIUType powerOfTwoBelow(genIUType Value); + + /// Return the power of two number which value is just lower the input value. + /// Deprecated, use floorPowerOfTwo from GTC_round instead + /// + /// @see gtc_round + /// @see gtx_bit + template + GLM_DEPRECATED GLM_FUNC_DECL vec powerOfTwoBelow(vec const& value); + + /// Return the power of two number which value is the closet to the input value. + /// Deprecated, use roundPowerOfTwo from GTC_round instead + /// + /// @see gtc_round + /// @see gtx_bit + template + GLM_DEPRECATED GLM_FUNC_DECL genIUType powerOfTwoNearest(genIUType Value); + + /// Return the power of two number which value is the closet to the input value. + /// Deprecated, use roundPowerOfTwo from GTC_round instead + /// + /// @see gtc_round + /// @see gtx_bit + template + GLM_DEPRECATED GLM_FUNC_DECL vec powerOfTwoNearest(vec const& value); + + /// @} +} //namespace glm + + +#include "bit.inl" + diff --git a/src/GLMath/glm/gtx/bit.inl b/src/GLMath/glm/gtx/bit.inl new file mode 100644 index 000000000..621b62624 --- /dev/null +++ b/src/GLMath/glm/gtx/bit.inl @@ -0,0 +1,92 @@ +/// @ref gtx_bit + +namespace glm +{ + /////////////////// + // highestBitValue + + template + GLM_FUNC_QUALIFIER genIUType highestBitValue(genIUType Value) + { + genIUType tmp = Value; + genIUType result = genIUType(0); + while(tmp) + { + result = (tmp & (~tmp + 1)); // grab lowest bit + tmp &= ~result; // clear lowest bit + } + return result; + } + + template + GLM_FUNC_QUALIFIER vec highestBitValue(vec const& v) + { + return detail::functor1::call(highestBitValue, v); + } + + /////////////////// + // lowestBitValue + + template + GLM_FUNC_QUALIFIER genIUType lowestBitValue(genIUType Value) + { + return (Value & (~Value + 1)); + } + + template + GLM_FUNC_QUALIFIER vec lowestBitValue(vec const& v) + { + return detail::functor1::call(lowestBitValue, v); + } + + /////////////////// + // powerOfTwoAbove + + template + GLM_FUNC_QUALIFIER genType powerOfTwoAbove(genType value) + { + return isPowerOfTwo(value) ? value : highestBitValue(value) << 1; + } + + template + GLM_FUNC_QUALIFIER vec powerOfTwoAbove(vec const& v) + { + return detail::functor1::call(powerOfTwoAbove, v); + } + + /////////////////// + // powerOfTwoBelow + + template + GLM_FUNC_QUALIFIER genType powerOfTwoBelow(genType value) + { + return isPowerOfTwo(value) ? value : highestBitValue(value); + } + + template + GLM_FUNC_QUALIFIER vec powerOfTwoBelow(vec const& v) + { + return detail::functor1::call(powerOfTwoBelow, v); + } + + ///////////////////// + // powerOfTwoNearest + + template + GLM_FUNC_QUALIFIER genType powerOfTwoNearest(genType value) + { + if(isPowerOfTwo(value)) + return value; + + genType const prev = highestBitValue(value); + genType const next = prev << 1; + return (next - value) < (value - prev) ? next : prev; + } + + template + GLM_FUNC_QUALIFIER vec powerOfTwoNearest(vec const& v) + { + return detail::functor1::call(powerOfTwoNearest, v); + } + +}//namespace glm diff --git a/src/GLMath/glm/gtx/closest_point.hpp b/src/GLMath/glm/gtx/closest_point.hpp new file mode 100644 index 000000000..de6dbbff9 --- /dev/null +++ b/src/GLMath/glm/gtx/closest_point.hpp @@ -0,0 +1,49 @@ +/// @ref gtx_closest_point +/// @file glm/gtx/closest_point.hpp +/// +/// @see core (dependence) +/// +/// @defgroup gtx_closest_point GLM_GTX_closest_point +/// @ingroup gtx +/// +/// Include to use the features of this extension. +/// +/// Find the point on a straight line which is the closet of a point. + +#pragma once + +// Dependency: +#include "../glm.hpp" + +#if GLM_MESSAGES == GLM_ENABLE && !defined(GLM_EXT_INCLUDED) +# ifndef GLM_ENABLE_EXPERIMENTAL +# pragma message("GLM: GLM_GTX_closest_point is an experimental extension and may change in the future. Use #define GLM_ENABLE_EXPERIMENTAL before including it, if you really want to use it.") +# else +# pragma message("GLM: GLM_GTX_closest_point extension included") +# endif +#endif + +namespace glm +{ + /// @addtogroup gtx_closest_point + /// @{ + + /// Find the point on a straight line which is the closet of a point. + /// @see gtx_closest_point + template + GLM_FUNC_DECL vec<3, T, Q> closestPointOnLine( + vec<3, T, Q> const& point, + vec<3, T, Q> const& a, + vec<3, T, Q> const& b); + + /// 2d lines work as well + template + GLM_FUNC_DECL vec<2, T, Q> closestPointOnLine( + vec<2, T, Q> const& point, + vec<2, T, Q> const& a, + vec<2, T, Q> const& b); + + /// @} +}// namespace glm + +#include "closest_point.inl" diff --git a/src/GLMath/glm/gtx/closest_point.inl b/src/GLMath/glm/gtx/closest_point.inl new file mode 100644 index 000000000..0a39b042b --- /dev/null +++ b/src/GLMath/glm/gtx/closest_point.inl @@ -0,0 +1,45 @@ +/// @ref gtx_closest_point + +namespace glm +{ + template + GLM_FUNC_QUALIFIER vec<3, T, Q> closestPointOnLine + ( + vec<3, T, Q> const& point, + vec<3, T, Q> const& a, + vec<3, T, Q> const& b + ) + { + T LineLength = distance(a, b); + vec<3, T, Q> Vector = point - a; + vec<3, T, Q> LineDirection = (b - a) / LineLength; + + // Project Vector to LineDirection to get the distance of point from a + T Distance = dot(Vector, LineDirection); + + if(Distance <= T(0)) return a; + if(Distance >= LineLength) return b; + return a + LineDirection * Distance; + } + + template + GLM_FUNC_QUALIFIER vec<2, T, Q> closestPointOnLine + ( + vec<2, T, Q> const& point, + vec<2, T, Q> const& a, + vec<2, T, Q> const& b + ) + { + T LineLength = distance(a, b); + vec<2, T, Q> Vector = point - a; + vec<2, T, Q> LineDirection = (b - a) / LineLength; + + // Project Vector to LineDirection to get the distance of point from a + T Distance = dot(Vector, LineDirection); + + if(Distance <= T(0)) return a; + if(Distance >= LineLength) return b; + return a + LineDirection * Distance; + } + +}//namespace glm diff --git a/src/GLMath/glm/gtx/color_encoding.hpp b/src/GLMath/glm/gtx/color_encoding.hpp new file mode 100644 index 000000000..96ded2a27 --- /dev/null +++ b/src/GLMath/glm/gtx/color_encoding.hpp @@ -0,0 +1,54 @@ +/// @ref gtx_color_encoding +/// @file glm/gtx/color_encoding.hpp +/// +/// @see core (dependence) +/// @see gtx_color_encoding (dependence) +/// +/// @defgroup gtx_color_encoding GLM_GTX_color_encoding +/// @ingroup gtx +/// +/// Include to use the features of this extension. +/// +/// @brief Allow to perform bit operations on integer values + +#pragma once + +// Dependencies +#include "../detail/setup.hpp" +#include "../detail/qualifier.hpp" +#include "../vec3.hpp" +#include + +#if GLM_MESSAGES == GLM_ENABLE && !defined(GLM_EXT_INCLUDED) +# ifndef GLM_ENABLE_EXPERIMENTAL +# pragma message("GLM: GLM_GTC_color_encoding is an experimental extension and may change in the future. Use #define GLM_ENABLE_EXPERIMENTAL before including it, if you really want to use it.") +# else +# pragma message("GLM: GLM_GTC_color_encoding extension included") +# endif +#endif + +namespace glm +{ + /// @addtogroup gtx_color_encoding + /// @{ + + /// Convert a linear sRGB color to D65 YUV. + template + GLM_FUNC_DECL vec<3, T, Q> convertLinearSRGBToD65XYZ(vec<3, T, Q> const& ColorLinearSRGB); + + /// Convert a linear sRGB color to D50 YUV. + template + GLM_FUNC_DECL vec<3, T, Q> convertLinearSRGBToD50XYZ(vec<3, T, Q> const& ColorLinearSRGB); + + /// Convert a D65 YUV color to linear sRGB. + template + GLM_FUNC_DECL vec<3, T, Q> convertD65XYZToLinearSRGB(vec<3, T, Q> const& ColorD65XYZ); + + /// Convert a D65 YUV color to D50 YUV. + template + GLM_FUNC_DECL vec<3, T, Q> convertD65XYZToD50XYZ(vec<3, T, Q> const& ColorD65XYZ); + + /// @} +} //namespace glm + +#include "color_encoding.inl" diff --git a/src/GLMath/glm/gtx/color_encoding.inl b/src/GLMath/glm/gtx/color_encoding.inl new file mode 100644 index 000000000..e50fa3efa --- /dev/null +++ b/src/GLMath/glm/gtx/color_encoding.inl @@ -0,0 +1,45 @@ +/// @ref gtx_color_encoding + +namespace glm +{ + template + GLM_FUNC_QUALIFIER vec<3, T, Q> convertLinearSRGBToD65XYZ(vec<3, T, Q> const& ColorLinearSRGB) + { + vec<3, T, Q> const M(0.490f, 0.17697f, 0.2f); + vec<3, T, Q> const N(0.31f, 0.8124f, 0.01063f); + vec<3, T, Q> const O(0.490f, 0.01f, 0.99f); + + return (M * ColorLinearSRGB + N * ColorLinearSRGB + O * ColorLinearSRGB) * static_cast(5.650675255693055f); + } + + template + GLM_FUNC_QUALIFIER vec<3, T, Q> convertLinearSRGBToD50XYZ(vec<3, T, Q> const& ColorLinearSRGB) + { + vec<3, T, Q> const M(0.436030342570117f, 0.222438466210245f, 0.013897440074263f); + vec<3, T, Q> const N(0.385101860087134f, 0.716942745571917f, 0.097076381494207f); + vec<3, T, Q> const O(0.143067806654203f, 0.060618777416563f, 0.713926257896652f); + + return M * ColorLinearSRGB + N * ColorLinearSRGB + O * ColorLinearSRGB; + } + + template + GLM_FUNC_QUALIFIER vec<3, T, Q> convertD65XYZToLinearSRGB(vec<3, T, Q> const& ColorD65XYZ) + { + vec<3, T, Q> const M(0.41847f, -0.091169f, 0.0009209f); + vec<3, T, Q> const N(-0.15866f, 0.25243f, 0.015708f); + vec<3, T, Q> const O(0.0009209f, -0.0025498f, 0.1786f); + + return M * ColorD65XYZ + N * ColorD65XYZ + O * ColorD65XYZ; + } + + template + GLM_FUNC_QUALIFIER vec<3, T, Q> convertD65XYZToD50XYZ(vec<3, T, Q> const& ColorD65XYZ) + { + vec<3, T, Q> const M(+1.047844353856414f, +0.029549007606644f, -0.009250984365223f); + vec<3, T, Q> const N(+0.022898981050086f, +0.990508028941971f, +0.015072338237051f); + vec<3, T, Q> const O(-0.050206647741605f, -0.017074711360960f, +0.751717835079977f); + + return M * ColorD65XYZ + N * ColorD65XYZ + O * ColorD65XYZ; + } + +}//namespace glm diff --git a/src/GLMath/glm/gtx/color_space.hpp b/src/GLMath/glm/gtx/color_space.hpp new file mode 100644 index 000000000..a63439214 --- /dev/null +++ b/src/GLMath/glm/gtx/color_space.hpp @@ -0,0 +1,72 @@ +/// @ref gtx_color_space +/// @file glm/gtx/color_space.hpp +/// +/// @see core (dependence) +/// +/// @defgroup gtx_color_space GLM_GTX_color_space +/// @ingroup gtx +/// +/// Include to use the features of this extension. +/// +/// Related to RGB to HSV conversions and operations. + +#pragma once + +// Dependency: +#include "../glm.hpp" + +#if GLM_MESSAGES == GLM_ENABLE && !defined(GLM_EXT_INCLUDED) +# ifndef GLM_ENABLE_EXPERIMENTAL +# pragma message("GLM: GLM_GTX_color_space is an experimental extension and may change in the future. Use #define GLM_ENABLE_EXPERIMENTAL before including it, if you really want to use it.") +# else +# pragma message("GLM: GLM_GTX_color_space extension included") +# endif +#endif + +namespace glm +{ + /// @addtogroup gtx_color_space + /// @{ + + /// Converts a color from HSV color space to its color in RGB color space. + /// @see gtx_color_space + template + GLM_FUNC_DECL vec<3, T, Q> rgbColor( + vec<3, T, Q> const& hsvValue); + + /// Converts a color from RGB color space to its color in HSV color space. + /// @see gtx_color_space + template + GLM_FUNC_DECL vec<3, T, Q> hsvColor( + vec<3, T, Q> const& rgbValue); + + /// Build a saturation matrix. + /// @see gtx_color_space + template + GLM_FUNC_DECL mat<4, 4, T, defaultp> saturation( + T const s); + + /// Modify the saturation of a color. + /// @see gtx_color_space + template + GLM_FUNC_DECL vec<3, T, Q> saturation( + T const s, + vec<3, T, Q> const& color); + + /// Modify the saturation of a color. + /// @see gtx_color_space + template + GLM_FUNC_DECL vec<4, T, Q> saturation( + T const s, + vec<4, T, Q> const& color); + + /// Compute color luminosity associating ratios (0.33, 0.59, 0.11) to RGB canals. + /// @see gtx_color_space + template + GLM_FUNC_DECL T luminosity( + vec<3, T, Q> const& color); + + /// @} +}//namespace glm + +#include "color_space.inl" diff --git a/src/GLMath/glm/gtx/color_space.inl b/src/GLMath/glm/gtx/color_space.inl new file mode 100644 index 000000000..f698afe1e --- /dev/null +++ b/src/GLMath/glm/gtx/color_space.inl @@ -0,0 +1,141 @@ +/// @ref gtx_color_space + +namespace glm +{ + template + GLM_FUNC_QUALIFIER vec<3, T, Q> rgbColor(const vec<3, T, Q>& hsvColor) + { + vec<3, T, Q> hsv = hsvColor; + vec<3, T, Q> rgbColor; + + if(hsv.y == static_cast(0)) + // achromatic (grey) + rgbColor = vec<3, T, Q>(hsv.z); + else + { + T sector = floor(hsv.x * (T(1) / T(60))); + T frac = (hsv.x * (T(1) / T(60))) - sector; + // factorial part of h + T o = hsv.z * (T(1) - hsv.y); + T p = hsv.z * (T(1) - hsv.y * frac); + T q = hsv.z * (T(1) - hsv.y * (T(1) - frac)); + + switch(int(sector)) + { + default: + case 0: + rgbColor.r = hsv.z; + rgbColor.g = q; + rgbColor.b = o; + break; + case 1: + rgbColor.r = p; + rgbColor.g = hsv.z; + rgbColor.b = o; + break; + case 2: + rgbColor.r = o; + rgbColor.g = hsv.z; + rgbColor.b = q; + break; + case 3: + rgbColor.r = o; + rgbColor.g = p; + rgbColor.b = hsv.z; + break; + case 4: + rgbColor.r = q; + rgbColor.g = o; + rgbColor.b = hsv.z; + break; + case 5: + rgbColor.r = hsv.z; + rgbColor.g = o; + rgbColor.b = p; + break; + } + } + + return rgbColor; + } + + template + GLM_FUNC_QUALIFIER vec<3, T, Q> hsvColor(const vec<3, T, Q>& rgbColor) + { + vec<3, T, Q> hsv = rgbColor; + float Min = min(min(rgbColor.r, rgbColor.g), rgbColor.b); + float Max = max(max(rgbColor.r, rgbColor.g), rgbColor.b); + float Delta = Max - Min; + + hsv.z = Max; + + if(Max != static_cast(0)) + { + hsv.y = Delta / hsv.z; + T h = static_cast(0); + + if(rgbColor.r == Max) + // between yellow & magenta + h = static_cast(0) + T(60) * (rgbColor.g - rgbColor.b) / Delta; + else if(rgbColor.g == Max) + // between cyan & yellow + h = static_cast(120) + T(60) * (rgbColor.b - rgbColor.r) / Delta; + else + // between magenta & cyan + h = static_cast(240) + T(60) * (rgbColor.r - rgbColor.g) / Delta; + + if(h < T(0)) + hsv.x = h + T(360); + else + hsv.x = h; + } + else + { + // If r = g = b = 0 then s = 0, h is undefined + hsv.y = static_cast(0); + hsv.x = static_cast(0); + } + + return hsv; + } + + template + GLM_FUNC_QUALIFIER mat<4, 4, T, defaultp> saturation(T const s) + { + vec<3, T, defaultp> rgbw = vec<3, T, defaultp>(T(0.2126), T(0.7152), T(0.0722)); + + vec<3, T, defaultp> const col((T(1) - s) * rgbw); + + mat<4, 4, T, defaultp> result(T(1)); + result[0][0] = col.x + s; + result[0][1] = col.x; + result[0][2] = col.x; + result[1][0] = col.y; + result[1][1] = col.y + s; + result[1][2] = col.y; + result[2][0] = col.z; + result[2][1] = col.z; + result[2][2] = col.z + s; + + return result; + } + + template + GLM_FUNC_QUALIFIER vec<3, T, Q> saturation(const T s, const vec<3, T, Q>& color) + { + return vec<3, T, Q>(saturation(s) * vec<4, T, Q>(color, T(0))); + } + + template + GLM_FUNC_QUALIFIER vec<4, T, Q> saturation(const T s, const vec<4, T, Q>& color) + { + return saturation(s) * color; + } + + template + GLM_FUNC_QUALIFIER T luminosity(const vec<3, T, Q>& color) + { + const vec<3, T, Q> tmp = vec<3, T, Q>(0.33, 0.59, 0.11); + return dot(color, tmp); + } +}//namespace glm diff --git a/src/GLMath/glm/gtx/color_space_YCoCg.hpp b/src/GLMath/glm/gtx/color_space_YCoCg.hpp new file mode 100644 index 000000000..dd2b77169 --- /dev/null +++ b/src/GLMath/glm/gtx/color_space_YCoCg.hpp @@ -0,0 +1,60 @@ +/// @ref gtx_color_space_YCoCg +/// @file glm/gtx/color_space_YCoCg.hpp +/// +/// @see core (dependence) +/// +/// @defgroup gtx_color_space_YCoCg GLM_GTX_color_space_YCoCg +/// @ingroup gtx +/// +/// Include to use the features of this extension. +/// +/// RGB to YCoCg conversions and operations + +#pragma once + +// Dependency: +#include "../glm.hpp" + +#if GLM_MESSAGES == GLM_ENABLE && !defined(GLM_EXT_INCLUDED) +# ifndef GLM_ENABLE_EXPERIMENTAL +# pragma message("GLM: GLM_GTX_color_space_YCoCg is an experimental extension and may change in the future. Use #define GLM_ENABLE_EXPERIMENTAL before including it, if you really want to use it.") +# else +# pragma message("GLM: GLM_GTX_color_space_YCoCg extension included") +# endif +#endif + +namespace glm +{ + /// @addtogroup gtx_color_space_YCoCg + /// @{ + + /// Convert a color from RGB color space to YCoCg color space. + /// @see gtx_color_space_YCoCg + template + GLM_FUNC_DECL vec<3, T, Q> rgb2YCoCg( + vec<3, T, Q> const& rgbColor); + + /// Convert a color from YCoCg color space to RGB color space. + /// @see gtx_color_space_YCoCg + template + GLM_FUNC_DECL vec<3, T, Q> YCoCg2rgb( + vec<3, T, Q> const& YCoCgColor); + + /// Convert a color from RGB color space to YCoCgR color space. + /// @see "YCoCg-R: A Color Space with RGB Reversibility and Low Dynamic Range" + /// @see gtx_color_space_YCoCg + template + GLM_FUNC_DECL vec<3, T, Q> rgb2YCoCgR( + vec<3, T, Q> const& rgbColor); + + /// Convert a color from YCoCgR color space to RGB color space. + /// @see "YCoCg-R: A Color Space with RGB Reversibility and Low Dynamic Range" + /// @see gtx_color_space_YCoCg + template + GLM_FUNC_DECL vec<3, T, Q> YCoCgR2rgb( + vec<3, T, Q> const& YCoCgColor); + + /// @} +}//namespace glm + +#include "color_space_YCoCg.inl" diff --git a/src/GLMath/glm/gtx/color_space_YCoCg.inl b/src/GLMath/glm/gtx/color_space_YCoCg.inl new file mode 100644 index 000000000..83ba857c0 --- /dev/null +++ b/src/GLMath/glm/gtx/color_space_YCoCg.inl @@ -0,0 +1,107 @@ +/// @ref gtx_color_space_YCoCg + +namespace glm +{ + template + GLM_FUNC_QUALIFIER vec<3, T, Q> rgb2YCoCg + ( + vec<3, T, Q> const& rgbColor + ) + { + vec<3, T, Q> result; + result.x/*Y */ = rgbColor.r / T(4) + rgbColor.g / T(2) + rgbColor.b / T(4); + result.y/*Co*/ = rgbColor.r / T(2) + rgbColor.g * T(0) - rgbColor.b / T(2); + result.z/*Cg*/ = - rgbColor.r / T(4) + rgbColor.g / T(2) - rgbColor.b / T(4); + return result; + } + + template + GLM_FUNC_QUALIFIER vec<3, T, Q> YCoCg2rgb + ( + vec<3, T, Q> const& YCoCgColor + ) + { + vec<3, T, Q> result; + result.r = YCoCgColor.x + YCoCgColor.y - YCoCgColor.z; + result.g = YCoCgColor.x + YCoCgColor.z; + result.b = YCoCgColor.x - YCoCgColor.y - YCoCgColor.z; + return result; + } + + template + class compute_YCoCgR { + public: + static GLM_FUNC_QUALIFIER vec<3, T, Q> rgb2YCoCgR + ( + vec<3, T, Q> const& rgbColor + ) + { + vec<3, T, Q> result; + result.x/*Y */ = rgbColor.g * static_cast(0.5) + (rgbColor.r + rgbColor.b) * static_cast(0.25); + result.y/*Co*/ = rgbColor.r - rgbColor.b; + result.z/*Cg*/ = rgbColor.g - (rgbColor.r + rgbColor.b) * static_cast(0.5); + return result; + } + + static GLM_FUNC_QUALIFIER vec<3, T, Q> YCoCgR2rgb + ( + vec<3, T, Q> const& YCoCgRColor + ) + { + vec<3, T, Q> result; + T tmp = YCoCgRColor.x - (YCoCgRColor.z * static_cast(0.5)); + result.g = YCoCgRColor.z + tmp; + result.b = tmp - (YCoCgRColor.y * static_cast(0.5)); + result.r = result.b + YCoCgRColor.y; + return result; + } + }; + + template + class compute_YCoCgR { + public: + static GLM_FUNC_QUALIFIER vec<3, T, Q> rgb2YCoCgR + ( + vec<3, T, Q> const& rgbColor + ) + { + vec<3, T, Q> result; + result.y/*Co*/ = rgbColor.r - rgbColor.b; + T tmp = rgbColor.b + (result.y >> 1); + result.z/*Cg*/ = rgbColor.g - tmp; + result.x/*Y */ = tmp + (result.z >> 1); + return result; + } + + static GLM_FUNC_QUALIFIER vec<3, T, Q> YCoCgR2rgb + ( + vec<3, T, Q> const& YCoCgRColor + ) + { + vec<3, T, Q> result; + T tmp = YCoCgRColor.x - (YCoCgRColor.z >> 1); + result.g = YCoCgRColor.z + tmp; + result.b = tmp - (YCoCgRColor.y >> 1); + result.r = result.b + YCoCgRColor.y; + return result; + } + }; + + template + GLM_FUNC_QUALIFIER vec<3, T, Q> rgb2YCoCgR + ( + vec<3, T, Q> const& rgbColor + ) + { + return compute_YCoCgR::is_integer>::rgb2YCoCgR(rgbColor); + } + + template + GLM_FUNC_QUALIFIER vec<3, T, Q> YCoCgR2rgb + ( + vec<3, T, Q> const& YCoCgRColor + ) + { + return compute_YCoCgR::is_integer>::YCoCgR2rgb(YCoCgRColor); + } +}//namespace glm diff --git a/src/GLMath/glm/gtx/common.hpp b/src/GLMath/glm/gtx/common.hpp new file mode 100644 index 000000000..254ada2d7 --- /dev/null +++ b/src/GLMath/glm/gtx/common.hpp @@ -0,0 +1,76 @@ +/// @ref gtx_common +/// @file glm/gtx/common.hpp +/// +/// @see core (dependence) +/// +/// @defgroup gtx_common GLM_GTX_common +/// @ingroup gtx +/// +/// Include to use the features of this extension. +/// +/// @brief Provide functions to increase the compatibility with Cg and HLSL languages + +#pragma once + +// Dependencies: +#include "../vec2.hpp" +#include "../vec3.hpp" +#include "../vec4.hpp" +#include "../gtc/vec1.hpp" + +#if GLM_MESSAGES == GLM_ENABLE && !defined(GLM_EXT_INCLUDED) +# ifndef GLM_ENABLE_EXPERIMENTAL +# pragma message("GLM: GLM_GTX_common is an experimental extension and may change in the future. Use #define GLM_ENABLE_EXPERIMENTAL before including it, if you really want to use it.") +# else +# pragma message("GLM: GLM_GTX_common extension included") +# endif +#endif + +namespace glm +{ + /// @addtogroup gtx_common + /// @{ + + /// Returns true if x is a denormalized number + /// Numbers whose absolute value is too small to be represented in the normal format are represented in an alternate, denormalized format. + /// This format is less precise but can represent values closer to zero. + /// + /// @tparam genType Floating-point scalar or vector types. + /// + /// @see GLSL isnan man page + /// @see GLSL 4.20.8 specification, section 8.3 Common Functions + template + GLM_FUNC_DECL typename genType::bool_type isdenormal(genType const& x); + + /// Similar to 'mod' but with a different rounding and integer support. + /// Returns 'x - y * trunc(x/y)' instead of 'x - y * floor(x/y)' + /// + /// @see GLSL mod vs HLSL fmod + /// @see GLSL mod man page + template + GLM_FUNC_DECL vec fmod(vec const& v); + + /// Returns whether vector components values are within an interval. A open interval excludes its endpoints, and is denoted with square brackets. + /// + /// @tparam L Integer between 1 and 4 included that qualify the dimension of the vector + /// @tparam T Floating-point or integer scalar types + /// @tparam Q Value from qualifier enum + /// + /// @see ext_vector_relational + template + GLM_FUNC_DECL vec openBounded(vec const& Value, vec const& Min, vec const& Max); + + /// Returns whether vector components values are within an interval. A closed interval includes its endpoints, and is denoted with square brackets. + /// + /// @tparam L Integer between 1 and 4 included that qualify the dimension of the vector + /// @tparam T Floating-point or integer scalar types + /// @tparam Q Value from qualifier enum + /// + /// @see ext_vector_relational + template + GLM_FUNC_DECL vec closeBounded(vec const& Value, vec const& Min, vec const& Max); + + /// @} +}//namespace glm + +#include "common.inl" diff --git a/src/GLMath/glm/gtx/common.inl b/src/GLMath/glm/gtx/common.inl new file mode 100644 index 000000000..4ad2126d9 --- /dev/null +++ b/src/GLMath/glm/gtx/common.inl @@ -0,0 +1,125 @@ +/// @ref gtx_common + +#include +#include "../gtc/epsilon.hpp" +#include "../gtc/constants.hpp" + +namespace glm{ +namespace detail +{ + template + struct compute_fmod + { + GLM_FUNC_QUALIFIER static vec call(vec const& a, vec const& b) + { + return detail::functor2::call(std::fmod, a, b); + } + }; + + template + struct compute_fmod + { + GLM_FUNC_QUALIFIER static vec call(vec const& a, vec const& b) + { + return a % b; + } + }; +}//namespace detail + + template + GLM_FUNC_QUALIFIER bool isdenormal(T const& x) + { + GLM_STATIC_ASSERT(std::numeric_limits::is_iec559, "'isdenormal' only accept floating-point inputs"); + +# if GLM_HAS_CXX11_STL + return std::fpclassify(x) == FP_SUBNORMAL; +# else + return epsilonNotEqual(x, static_cast(0), epsilon()) && std::fabs(x) < std::numeric_limits::min(); +# endif + } + + template + GLM_FUNC_QUALIFIER typename vec<1, T, Q>::bool_type isdenormal + ( + vec<1, T, Q> const& x + ) + { + GLM_STATIC_ASSERT(std::numeric_limits::is_iec559, "'isdenormal' only accept floating-point inputs"); + + return typename vec<1, T, Q>::bool_type( + isdenormal(x.x)); + } + + template + GLM_FUNC_QUALIFIER typename vec<2, T, Q>::bool_type isdenormal + ( + vec<2, T, Q> const& x + ) + { + GLM_STATIC_ASSERT(std::numeric_limits::is_iec559, "'isdenormal' only accept floating-point inputs"); + + return typename vec<2, T, Q>::bool_type( + isdenormal(x.x), + isdenormal(x.y)); + } + + template + GLM_FUNC_QUALIFIER typename vec<3, T, Q>::bool_type isdenormal + ( + vec<3, T, Q> const& x + ) + { + GLM_STATIC_ASSERT(std::numeric_limits::is_iec559, "'isdenormal' only accept floating-point inputs"); + + return typename vec<3, T, Q>::bool_type( + isdenormal(x.x), + isdenormal(x.y), + isdenormal(x.z)); + } + + template + GLM_FUNC_QUALIFIER typename vec<4, T, Q>::bool_type isdenormal + ( + vec<4, T, Q> const& x + ) + { + GLM_STATIC_ASSERT(std::numeric_limits::is_iec559, "'isdenormal' only accept floating-point inputs"); + + return typename vec<4, T, Q>::bool_type( + isdenormal(x.x), + isdenormal(x.y), + isdenormal(x.z), + isdenormal(x.w)); + } + + // fmod + template + GLM_FUNC_QUALIFIER genType fmod(genType x, genType y) + { + return fmod(vec<1, genType>(x), y).x; + } + + template + GLM_FUNC_QUALIFIER vec fmod(vec const& x, T y) + { + return detail::compute_fmod::is_iec559>::call(x, vec(y)); + } + + template + GLM_FUNC_QUALIFIER vec fmod(vec const& x, vec const& y) + { + return detail::compute_fmod::is_iec559>::call(x, y); + } + + template + GLM_FUNC_QUALIFIER vec openBounded(vec const& Value, vec const& Min, vec const& Max) + { + return greaterThan(Value, Min) && lessThan(Value, Max); + } + + template + GLM_FUNC_QUALIFIER vec closeBounded(vec const& Value, vec const& Min, vec const& Max) + { + return greaterThanEqual(Value, Min) && lessThanEqual(Value, Max); + } +}//namespace glm diff --git a/src/GLMath/glm/gtx/compatibility.hpp b/src/GLMath/glm/gtx/compatibility.hpp new file mode 100644 index 000000000..f1b00a6b3 --- /dev/null +++ b/src/GLMath/glm/gtx/compatibility.hpp @@ -0,0 +1,133 @@ +/// @ref gtx_compatibility +/// @file glm/gtx/compatibility.hpp +/// +/// @see core (dependence) +/// +/// @defgroup gtx_compatibility GLM_GTX_compatibility +/// @ingroup gtx +/// +/// Include to use the features of this extension. +/// +/// Provide functions to increase the compatibility with Cg and HLSL languages + +#pragma once + +// Dependency: +#include "../glm.hpp" +#include "../gtc/quaternion.hpp" + +#if GLM_MESSAGES == GLM_ENABLE && !defined(GLM_EXT_INCLUDED) +# ifndef GLM_ENABLE_EXPERIMENTAL +# pragma message("GLM: GLM_GTX_compatibility is an experimental extension and may change in the future. Use #define GLM_ENABLE_EXPERIMENTAL before including it, if you really want to use it.") +# else +# pragma message("GLM: GLM_GTX_compatibility extension included") +# endif +#endif + +#if GLM_COMPILER & GLM_COMPILER_VC +# include +#elif GLM_COMPILER & GLM_COMPILER_GCC +# include +# if(GLM_PLATFORM & GLM_PLATFORM_ANDROID) +# undef isfinite +# endif +#endif//GLM_COMPILER + +namespace glm +{ + /// @addtogroup gtx_compatibility + /// @{ + + template GLM_FUNC_QUALIFIER T lerp(T x, T y, T a){return mix(x, y, a);} //!< \brief Returns x * (1.0 - a) + y * a, i.e., the linear blend of x and y using the floating-point value a. The value for a is not restricted to the range [0, 1]. (From GLM_GTX_compatibility) + template GLM_FUNC_QUALIFIER vec<2, T, Q> lerp(const vec<2, T, Q>& x, const vec<2, T, Q>& y, T a){return mix(x, y, a);} //!< \brief Returns x * (1.0 - a) + y * a, i.e., the linear blend of x and y using the floating-point value a. The value for a is not restricted to the range [0, 1]. (From GLM_GTX_compatibility) + + template GLM_FUNC_QUALIFIER vec<3, T, Q> lerp(const vec<3, T, Q>& x, const vec<3, T, Q>& y, T a){return mix(x, y, a);} //!< \brief Returns x * (1.0 - a) + y * a, i.e., the linear blend of x and y using the floating-point value a. The value for a is not restricted to the range [0, 1]. (From GLM_GTX_compatibility) + template GLM_FUNC_QUALIFIER vec<4, T, Q> lerp(const vec<4, T, Q>& x, const vec<4, T, Q>& y, T a){return mix(x, y, a);} //!< \brief Returns x * (1.0 - a) + y * a, i.e., the linear blend of x and y using the floating-point value a. The value for a is not restricted to the range [0, 1]. (From GLM_GTX_compatibility) + template GLM_FUNC_QUALIFIER vec<2, T, Q> lerp(const vec<2, T, Q>& x, const vec<2, T, Q>& y, const vec<2, T, Q>& a){return mix(x, y, a);} //!< \brief Returns the component-wise result of x * (1.0 - a) + y * a, i.e., the linear blend of x and y using vector a. The value for a is not restricted to the range [0, 1]. (From GLM_GTX_compatibility) + template GLM_FUNC_QUALIFIER vec<3, T, Q> lerp(const vec<3, T, Q>& x, const vec<3, T, Q>& y, const vec<3, T, Q>& a){return mix(x, y, a);} //!< \brief Returns the component-wise result of x * (1.0 - a) + y * a, i.e., the linear blend of x and y using vector a. The value for a is not restricted to the range [0, 1]. (From GLM_GTX_compatibility) + template GLM_FUNC_QUALIFIER vec<4, T, Q> lerp(const vec<4, T, Q>& x, const vec<4, T, Q>& y, const vec<4, T, Q>& a){return mix(x, y, a);} //!< \brief Returns the component-wise result of x * (1.0 - a) + y * a, i.e., the linear blend of x and y using vector a. The value for a is not restricted to the range [0, 1]. (From GLM_GTX_compatibility) + + template GLM_FUNC_QUALIFIER T saturate(T x){return clamp(x, T(0), T(1));} //!< \brief Returns clamp(x, 0, 1) for each component in x. (From GLM_GTX_compatibility) + template GLM_FUNC_QUALIFIER vec<2, T, Q> saturate(const vec<2, T, Q>& x){return clamp(x, T(0), T(1));} //!< \brief Returns clamp(x, 0, 1) for each component in x. (From GLM_GTX_compatibility) + template GLM_FUNC_QUALIFIER vec<3, T, Q> saturate(const vec<3, T, Q>& x){return clamp(x, T(0), T(1));} //!< \brief Returns clamp(x, 0, 1) for each component in x. (From GLM_GTX_compatibility) + template GLM_FUNC_QUALIFIER vec<4, T, Q> saturate(const vec<4, T, Q>& x){return clamp(x, T(0), T(1));} //!< \brief Returns clamp(x, 0, 1) for each component in x. (From GLM_GTX_compatibility) + + template GLM_FUNC_QUALIFIER T atan2(T x, T y){return atan(x, y);} //!< \brief Arc tangent. Returns an angle whose tangent is y/x. The signs of x and y are used to determine what quadrant the angle is in. The range of values returned by this function is [-PI, PI]. Results are undefined if x and y are both 0. (From GLM_GTX_compatibility) + template GLM_FUNC_QUALIFIER vec<2, T, Q> atan2(const vec<2, T, Q>& x, const vec<2, T, Q>& y){return atan(x, y);} //!< \brief Arc tangent. Returns an angle whose tangent is y/x. The signs of x and y are used to determine what quadrant the angle is in. The range of values returned by this function is [-PI, PI]. Results are undefined if x and y are both 0. (From GLM_GTX_compatibility) + template GLM_FUNC_QUALIFIER vec<3, T, Q> atan2(const vec<3, T, Q>& x, const vec<3, T, Q>& y){return atan(x, y);} //!< \brief Arc tangent. Returns an angle whose tangent is y/x. The signs of x and y are used to determine what quadrant the angle is in. The range of values returned by this function is [-PI, PI]. Results are undefined if x and y are both 0. (From GLM_GTX_compatibility) + template GLM_FUNC_QUALIFIER vec<4, T, Q> atan2(const vec<4, T, Q>& x, const vec<4, T, Q>& y){return atan(x, y);} //!< \brief Arc tangent. Returns an angle whose tangent is y/x. The signs of x and y are used to determine what quadrant the angle is in. The range of values returned by this function is [-PI, PI]. Results are undefined if x and y are both 0. (From GLM_GTX_compatibility) + + template GLM_FUNC_DECL bool isfinite(genType const& x); //!< \brief Test whether or not a scalar or each vector component is a finite value. (From GLM_GTX_compatibility) + template GLM_FUNC_DECL vec<1, bool, Q> isfinite(const vec<1, T, Q>& x); //!< \brief Test whether or not a scalar or each vector component is a finite value. (From GLM_GTX_compatibility) + template GLM_FUNC_DECL vec<2, bool, Q> isfinite(const vec<2, T, Q>& x); //!< \brief Test whether or not a scalar or each vector component is a finite value. (From GLM_GTX_compatibility) + template GLM_FUNC_DECL vec<3, bool, Q> isfinite(const vec<3, T, Q>& x); //!< \brief Test whether or not a scalar or each vector component is a finite value. (From GLM_GTX_compatibility) + template GLM_FUNC_DECL vec<4, bool, Q> isfinite(const vec<4, T, Q>& x); //!< \brief Test whether or not a scalar or each vector component is a finite value. (From GLM_GTX_compatibility) + + typedef bool bool1; //!< \brief boolean type with 1 component. (From GLM_GTX_compatibility extension) + typedef vec<2, bool, highp> bool2; //!< \brief boolean type with 2 components. (From GLM_GTX_compatibility extension) + typedef vec<3, bool, highp> bool3; //!< \brief boolean type with 3 components. (From GLM_GTX_compatibility extension) + typedef vec<4, bool, highp> bool4; //!< \brief boolean type with 4 components. (From GLM_GTX_compatibility extension) + + typedef bool bool1x1; //!< \brief boolean matrix with 1 x 1 component. (From GLM_GTX_compatibility extension) + typedef mat<2, 2, bool, highp> bool2x2; //!< \brief boolean matrix with 2 x 2 components. (From GLM_GTX_compatibility extension) + typedef mat<2, 3, bool, highp> bool2x3; //!< \brief boolean matrix with 2 x 3 components. (From GLM_GTX_compatibility extension) + typedef mat<2, 4, bool, highp> bool2x4; //!< \brief boolean matrix with 2 x 4 components. (From GLM_GTX_compatibility extension) + typedef mat<3, 2, bool, highp> bool3x2; //!< \brief boolean matrix with 3 x 2 components. (From GLM_GTX_compatibility extension) + typedef mat<3, 3, bool, highp> bool3x3; //!< \brief boolean matrix with 3 x 3 components. (From GLM_GTX_compatibility extension) + typedef mat<3, 4, bool, highp> bool3x4; //!< \brief boolean matrix with 3 x 4 components. (From GLM_GTX_compatibility extension) + typedef mat<4, 2, bool, highp> bool4x2; //!< \brief boolean matrix with 4 x 2 components. (From GLM_GTX_compatibility extension) + typedef mat<4, 3, bool, highp> bool4x3; //!< \brief boolean matrix with 4 x 3 components. (From GLM_GTX_compatibility extension) + typedef mat<4, 4, bool, highp> bool4x4; //!< \brief boolean matrix with 4 x 4 components. (From GLM_GTX_compatibility extension) + + typedef int int1; //!< \brief integer vector with 1 component. (From GLM_GTX_compatibility extension) + typedef vec<2, int, highp> int2; //!< \brief integer vector with 2 components. (From GLM_GTX_compatibility extension) + typedef vec<3, int, highp> int3; //!< \brief integer vector with 3 components. (From GLM_GTX_compatibility extension) + typedef vec<4, int, highp> int4; //!< \brief integer vector with 4 components. (From GLM_GTX_compatibility extension) + + typedef int int1x1; //!< \brief integer matrix with 1 component. (From GLM_GTX_compatibility extension) + typedef mat<2, 2, int, highp> int2x2; //!< \brief integer matrix with 2 x 2 components. (From GLM_GTX_compatibility extension) + typedef mat<2, 3, int, highp> int2x3; //!< \brief integer matrix with 2 x 3 components. (From GLM_GTX_compatibility extension) + typedef mat<2, 4, int, highp> int2x4; //!< \brief integer matrix with 2 x 4 components. (From GLM_GTX_compatibility extension) + typedef mat<3, 2, int, highp> int3x2; //!< \brief integer matrix with 3 x 2 components. (From GLM_GTX_compatibility extension) + typedef mat<3, 3, int, highp> int3x3; //!< \brief integer matrix with 3 x 3 components. (From GLM_GTX_compatibility extension) + typedef mat<3, 4, int, highp> int3x4; //!< \brief integer matrix with 3 x 4 components. (From GLM_GTX_compatibility extension) + typedef mat<4, 2, int, highp> int4x2; //!< \brief integer matrix with 4 x 2 components. (From GLM_GTX_compatibility extension) + typedef mat<4, 3, int, highp> int4x3; //!< \brief integer matrix with 4 x 3 components. (From GLM_GTX_compatibility extension) + typedef mat<4, 4, int, highp> int4x4; //!< \brief integer matrix with 4 x 4 components. (From GLM_GTX_compatibility extension) + + typedef float float1; //!< \brief single-qualifier floating-point vector with 1 component. (From GLM_GTX_compatibility extension) + typedef vec<2, float, highp> float2; //!< \brief single-qualifier floating-point vector with 2 components. (From GLM_GTX_compatibility extension) + typedef vec<3, float, highp> float3; //!< \brief single-qualifier floating-point vector with 3 components. (From GLM_GTX_compatibility extension) + typedef vec<4, float, highp> float4; //!< \brief single-qualifier floating-point vector with 4 components. (From GLM_GTX_compatibility extension) + + typedef float float1x1; //!< \brief single-qualifier floating-point matrix with 1 component. (From GLM_GTX_compatibility extension) + typedef mat<2, 2, float, highp> float2x2; //!< \brief single-qualifier floating-point matrix with 2 x 2 components. (From GLM_GTX_compatibility extension) + typedef mat<2, 3, float, highp> float2x3; //!< \brief single-qualifier floating-point matrix with 2 x 3 components. (From GLM_GTX_compatibility extension) + typedef mat<2, 4, float, highp> float2x4; //!< \brief single-qualifier floating-point matrix with 2 x 4 components. (From GLM_GTX_compatibility extension) + typedef mat<3, 2, float, highp> float3x2; //!< \brief single-qualifier floating-point matrix with 3 x 2 components. (From GLM_GTX_compatibility extension) + typedef mat<3, 3, float, highp> float3x3; //!< \brief single-qualifier floating-point matrix with 3 x 3 components. (From GLM_GTX_compatibility extension) + typedef mat<3, 4, float, highp> float3x4; //!< \brief single-qualifier floating-point matrix with 3 x 4 components. (From GLM_GTX_compatibility extension) + typedef mat<4, 2, float, highp> float4x2; //!< \brief single-qualifier floating-point matrix with 4 x 2 components. (From GLM_GTX_compatibility extension) + typedef mat<4, 3, float, highp> float4x3; //!< \brief single-qualifier floating-point matrix with 4 x 3 components. (From GLM_GTX_compatibility extension) + typedef mat<4, 4, float, highp> float4x4; //!< \brief single-qualifier floating-point matrix with 4 x 4 components. (From GLM_GTX_compatibility extension) + + typedef double double1; //!< \brief double-qualifier floating-point vector with 1 component. (From GLM_GTX_compatibility extension) + typedef vec<2, double, highp> double2; //!< \brief double-qualifier floating-point vector with 2 components. (From GLM_GTX_compatibility extension) + typedef vec<3, double, highp> double3; //!< \brief double-qualifier floating-point vector with 3 components. (From GLM_GTX_compatibility extension) + typedef vec<4, double, highp> double4; //!< \brief double-qualifier floating-point vector with 4 components. (From GLM_GTX_compatibility extension) + + typedef double double1x1; //!< \brief double-qualifier floating-point matrix with 1 component. (From GLM_GTX_compatibility extension) + typedef mat<2, 2, double, highp> double2x2; //!< \brief double-qualifier floating-point matrix with 2 x 2 components. (From GLM_GTX_compatibility extension) + typedef mat<2, 3, double, highp> double2x3; //!< \brief double-qualifier floating-point matrix with 2 x 3 components. (From GLM_GTX_compatibility extension) + typedef mat<2, 4, double, highp> double2x4; //!< \brief double-qualifier floating-point matrix with 2 x 4 components. (From GLM_GTX_compatibility extension) + typedef mat<3, 2, double, highp> double3x2; //!< \brief double-qualifier floating-point matrix with 3 x 2 components. (From GLM_GTX_compatibility extension) + typedef mat<3, 3, double, highp> double3x3; //!< \brief double-qualifier floating-point matrix with 3 x 3 components. (From GLM_GTX_compatibility extension) + typedef mat<3, 4, double, highp> double3x4; //!< \brief double-qualifier floating-point matrix with 3 x 4 components. (From GLM_GTX_compatibility extension) + typedef mat<4, 2, double, highp> double4x2; //!< \brief double-qualifier floating-point matrix with 4 x 2 components. (From GLM_GTX_compatibility extension) + typedef mat<4, 3, double, highp> double4x3; //!< \brief double-qualifier floating-point matrix with 4 x 3 components. (From GLM_GTX_compatibility extension) + typedef mat<4, 4, double, highp> double4x4; //!< \brief double-qualifier floating-point matrix with 4 x 4 components. (From GLM_GTX_compatibility extension) + + /// @} +}//namespace glm + +#include "compatibility.inl" diff --git a/src/GLMath/glm/gtx/compatibility.inl b/src/GLMath/glm/gtx/compatibility.inl new file mode 100644 index 000000000..1d49496b6 --- /dev/null +++ b/src/GLMath/glm/gtx/compatibility.inl @@ -0,0 +1,62 @@ +#include + +namespace glm +{ + // isfinite + template + GLM_FUNC_QUALIFIER bool isfinite( + genType const& x) + { +# if GLM_HAS_CXX11_STL + return std::isfinite(x) != 0; +# elif GLM_COMPILER & GLM_COMPILER_VC + return _finite(x) != 0; +# elif GLM_COMPILER & GLM_COMPILER_GCC && GLM_PLATFORM & GLM_PLATFORM_ANDROID + return _isfinite(x) != 0; +# else + if (std::numeric_limits::is_integer || std::denorm_absent == std::numeric_limits::has_denorm) + return std::numeric_limits::min() <= x && std::numeric_limits::max() >= x; + else + return -std::numeric_limits::max() <= x && std::numeric_limits::max() >= x; +# endif + } + + template + GLM_FUNC_QUALIFIER vec<1, bool, Q> isfinite( + vec<1, T, Q> const& x) + { + return vec<1, bool, Q>( + isfinite(x.x)); + } + + template + GLM_FUNC_QUALIFIER vec<2, bool, Q> isfinite( + vec<2, T, Q> const& x) + { + return vec<2, bool, Q>( + isfinite(x.x), + isfinite(x.y)); + } + + template + GLM_FUNC_QUALIFIER vec<3, bool, Q> isfinite( + vec<3, T, Q> const& x) + { + return vec<3, bool, Q>( + isfinite(x.x), + isfinite(x.y), + isfinite(x.z)); + } + + template + GLM_FUNC_QUALIFIER vec<4, bool, Q> isfinite( + vec<4, T, Q> const& x) + { + return vec<4, bool, Q>( + isfinite(x.x), + isfinite(x.y), + isfinite(x.z), + isfinite(x.w)); + } + +}//namespace glm diff --git a/src/GLMath/glm/gtx/component_wise.hpp b/src/GLMath/glm/gtx/component_wise.hpp new file mode 100644 index 000000000..34a2b0a37 --- /dev/null +++ b/src/GLMath/glm/gtx/component_wise.hpp @@ -0,0 +1,69 @@ +/// @ref gtx_component_wise +/// @file glm/gtx/component_wise.hpp +/// @date 2007-05-21 / 2011-06-07 +/// @author Christophe Riccio +/// +/// @see core (dependence) +/// +/// @defgroup gtx_component_wise GLM_GTX_component_wise +/// @ingroup gtx +/// +/// Include to use the features of this extension. +/// +/// Operations between components of a type + +#pragma once + +// Dependencies +#include "../detail/setup.hpp" +#include "../detail/qualifier.hpp" + +#if GLM_MESSAGES == GLM_ENABLE && !defined(GLM_EXT_INCLUDED) +# ifndef GLM_ENABLE_EXPERIMENTAL +# pragma message("GLM: GLM_GTX_component_wise is an experimental extension and may change in the future. Use #define GLM_ENABLE_EXPERIMENTAL before including it, if you really want to use it.") +# else +# pragma message("GLM: GLM_GTX_component_wise extension included") +# endif +#endif + +namespace glm +{ + /// @addtogroup gtx_component_wise + /// @{ + + /// Convert an integer vector to a normalized float vector. + /// If the parameter value type is already a floating qualifier type, the value is passed through. + /// @see gtx_component_wise + template + GLM_FUNC_DECL vec compNormalize(vec const& v); + + /// Convert a normalized float vector to an integer vector. + /// If the parameter value type is already a floating qualifier type, the value is passed through. + /// @see gtx_component_wise + template + GLM_FUNC_DECL vec compScale(vec const& v); + + /// Add all vector components together. + /// @see gtx_component_wise + template + GLM_FUNC_DECL typename genType::value_type compAdd(genType const& v); + + /// Multiply all vector components together. + /// @see gtx_component_wise + template + GLM_FUNC_DECL typename genType::value_type compMul(genType const& v); + + /// Find the minimum value between single vector components. + /// @see gtx_component_wise + template + GLM_FUNC_DECL typename genType::value_type compMin(genType const& v); + + /// Find the maximum value between single vector components. + /// @see gtx_component_wise + template + GLM_FUNC_DECL typename genType::value_type compMax(genType const& v); + + /// @} +}//namespace glm + +#include "component_wise.inl" diff --git a/src/GLMath/glm/gtx/component_wise.inl b/src/GLMath/glm/gtx/component_wise.inl new file mode 100644 index 000000000..cbbc7d41e --- /dev/null +++ b/src/GLMath/glm/gtx/component_wise.inl @@ -0,0 +1,127 @@ +/// @ref gtx_component_wise + +#include + +namespace glm{ +namespace detail +{ + template + struct compute_compNormalize + {}; + + template + struct compute_compNormalize + { + GLM_FUNC_QUALIFIER static vec call(vec const& v) + { + floatType const Min = static_cast(std::numeric_limits::min()); + floatType const Max = static_cast(std::numeric_limits::max()); + return (vec(v) - Min) / (Max - Min) * static_cast(2) - static_cast(1); + } + }; + + template + struct compute_compNormalize + { + GLM_FUNC_QUALIFIER static vec call(vec const& v) + { + return vec(v) / static_cast(std::numeric_limits::max()); + } + }; + + template + struct compute_compNormalize + { + GLM_FUNC_QUALIFIER static vec call(vec const& v) + { + return v; + } + }; + + template + struct compute_compScale + {}; + + template + struct compute_compScale + { + GLM_FUNC_QUALIFIER static vec call(vec const& v) + { + floatType const Max = static_cast(std::numeric_limits::max()) + static_cast(0.5); + vec const Scaled(v * Max); + vec const Result(Scaled - static_cast(0.5)); + return Result; + } + }; + + template + struct compute_compScale + { + GLM_FUNC_QUALIFIER static vec call(vec const& v) + { + return vec(vec(v) * static_cast(std::numeric_limits::max())); + } + }; + + template + struct compute_compScale + { + GLM_FUNC_QUALIFIER static vec call(vec const& v) + { + return v; + } + }; +}//namespace detail + + template + GLM_FUNC_QUALIFIER vec compNormalize(vec const& v) + { + GLM_STATIC_ASSERT(std::numeric_limits::is_iec559, "'compNormalize' accepts only floating-point types for 'floatType' template parameter"); + + return detail::compute_compNormalize::is_integer, std::numeric_limits::is_signed>::call(v); + } + + template + GLM_FUNC_QUALIFIER vec compScale(vec const& v) + { + GLM_STATIC_ASSERT(std::numeric_limits::is_iec559, "'compScale' accepts only floating-point types for 'floatType' template parameter"); + + return detail::compute_compScale::is_integer, std::numeric_limits::is_signed>::call(v); + } + + template + GLM_FUNC_QUALIFIER T compAdd(vec const& v) + { + T Result(0); + for(length_t i = 0, n = v.length(); i < n; ++i) + Result += v[i]; + return Result; + } + + template + GLM_FUNC_QUALIFIER T compMul(vec const& v) + { + T Result(1); + for(length_t i = 0, n = v.length(); i < n; ++i) + Result *= v[i]; + return Result; + } + + template + GLM_FUNC_QUALIFIER T compMin(vec const& v) + { + T Result(v[0]); + for(length_t i = 1, n = v.length(); i < n; ++i) + Result = min(Result, v[i]); + return Result; + } + + template + GLM_FUNC_QUALIFIER T compMax(vec const& v) + { + T Result(v[0]); + for(length_t i = 1, n = v.length(); i < n; ++i) + Result = max(Result, v[i]); + return Result; + } +}//namespace glm diff --git a/src/GLMath/glm/gtx/dual_quaternion.hpp b/src/GLMath/glm/gtx/dual_quaternion.hpp new file mode 100644 index 000000000..6a51ab7d3 --- /dev/null +++ b/src/GLMath/glm/gtx/dual_quaternion.hpp @@ -0,0 +1,274 @@ +/// @ref gtx_dual_quaternion +/// @file glm/gtx/dual_quaternion.hpp +/// @author Maksim Vorobiev (msomeone@gmail.com) +/// +/// @see core (dependence) +/// @see gtc_constants (dependence) +/// @see gtc_quaternion (dependence) +/// +/// @defgroup gtx_dual_quaternion GLM_GTX_dual_quaternion +/// @ingroup gtx +/// +/// Include to use the features of this extension. +/// +/// Defines a templated dual-quaternion type and several dual-quaternion operations. + +#pragma once + +// Dependency: +#include "../glm.hpp" +#include "../gtc/constants.hpp" +#include "../gtc/quaternion.hpp" + +#if GLM_MESSAGES == GLM_ENABLE && !defined(GLM_EXT_INCLUDED) +# ifndef GLM_ENABLE_EXPERIMENTAL +# pragma message("GLM: GLM_GTX_dual_quaternion is an experimental extension and may change in the future. Use #define GLM_ENABLE_EXPERIMENTAL before including it, if you really want to use it.") +# else +# pragma message("GLM: GLM_GTX_dual_quaternion extension included") +# endif +#endif + +namespace glm +{ + /// @addtogroup gtx_dual_quaternion + /// @{ + + template + struct tdualquat + { + // -- Implementation detail -- + + typedef T value_type; + typedef qua part_type; + + // -- Data -- + + qua real, dual; + + // -- Component accesses -- + + typedef length_t length_type; + /// Return the count of components of a dual quaternion + GLM_FUNC_DECL static GLM_CONSTEXPR length_type length(){return 2;} + + GLM_FUNC_DECL part_type & operator[](length_type i); + GLM_FUNC_DECL part_type const& operator[](length_type i) const; + + // -- Implicit basic constructors -- + + GLM_FUNC_DECL GLM_CONSTEXPR tdualquat() GLM_DEFAULT; + GLM_FUNC_DECL GLM_CONSTEXPR tdualquat(tdualquat const& d) GLM_DEFAULT; + template + GLM_FUNC_DECL GLM_CONSTEXPR tdualquat(tdualquat const& d); + + // -- Explicit basic constructors -- + + GLM_FUNC_DECL GLM_CONSTEXPR tdualquat(qua const& real); + GLM_FUNC_DECL GLM_CONSTEXPR tdualquat(qua const& orientation, vec<3, T, Q> const& translation); + GLM_FUNC_DECL GLM_CONSTEXPR tdualquat(qua const& real, qua const& dual); + + // -- Conversion constructors -- + + template + GLM_FUNC_DECL GLM_CONSTEXPR GLM_EXPLICIT tdualquat(tdualquat const& q); + + GLM_FUNC_DECL GLM_EXPLICIT GLM_CONSTEXPR tdualquat(mat<2, 4, T, Q> const& holder_mat); + GLM_FUNC_DECL GLM_EXPLICIT GLM_CONSTEXPR tdualquat(mat<3, 4, T, Q> const& aug_mat); + + // -- Unary arithmetic operators -- + + GLM_FUNC_DECL tdualquat & operator=(tdualquat const& m) GLM_DEFAULT; + + template + GLM_FUNC_DECL tdualquat & operator=(tdualquat const& m); + template + GLM_FUNC_DECL tdualquat & operator*=(U s); + template + GLM_FUNC_DECL tdualquat & operator/=(U s); + }; + + // -- Unary bit operators -- + + template + GLM_FUNC_DECL tdualquat operator+(tdualquat const& q); + + template + GLM_FUNC_DECL tdualquat operator-(tdualquat const& q); + + // -- Binary operators -- + + template + GLM_FUNC_DECL tdualquat operator+(tdualquat const& q, tdualquat const& p); + + template + GLM_FUNC_DECL tdualquat operator*(tdualquat const& q, tdualquat const& p); + + template + GLM_FUNC_DECL vec<3, T, Q> operator*(tdualquat const& q, vec<3, T, Q> const& v); + + template + GLM_FUNC_DECL vec<3, T, Q> operator*(vec<3, T, Q> const& v, tdualquat const& q); + + template + GLM_FUNC_DECL vec<4, T, Q> operator*(tdualquat const& q, vec<4, T, Q> const& v); + + template + GLM_FUNC_DECL vec<4, T, Q> operator*(vec<4, T, Q> const& v, tdualquat const& q); + + template + GLM_FUNC_DECL tdualquat operator*(tdualquat const& q, T const& s); + + template + GLM_FUNC_DECL tdualquat operator*(T const& s, tdualquat const& q); + + template + GLM_FUNC_DECL tdualquat operator/(tdualquat const& q, T const& s); + + // -- Boolean operators -- + + template + GLM_FUNC_DECL bool operator==(tdualquat const& q1, tdualquat const& q2); + + template + GLM_FUNC_DECL bool operator!=(tdualquat const& q1, tdualquat const& q2); + + /// Creates an identity dual quaternion. + /// + /// @see gtx_dual_quaternion + template + GLM_FUNC_DECL tdualquat dual_quat_identity(); + + /// Returns the normalized quaternion. + /// + /// @see gtx_dual_quaternion + template + GLM_FUNC_DECL tdualquat normalize(tdualquat const& q); + + /// Returns the linear interpolation of two dual quaternion. + /// + /// @see gtc_dual_quaternion + template + GLM_FUNC_DECL tdualquat lerp(tdualquat const& x, tdualquat const& y, T const& a); + + /// Returns the q inverse. + /// + /// @see gtx_dual_quaternion + template + GLM_FUNC_DECL tdualquat inverse(tdualquat const& q); + + /// Converts a quaternion to a 2 * 4 matrix. + /// + /// @see gtx_dual_quaternion + template + GLM_FUNC_DECL mat<2, 4, T, Q> mat2x4_cast(tdualquat const& x); + + /// Converts a quaternion to a 3 * 4 matrix. + /// + /// @see gtx_dual_quaternion + template + GLM_FUNC_DECL mat<3, 4, T, Q> mat3x4_cast(tdualquat const& x); + + /// Converts a 2 * 4 matrix (matrix which holds real and dual parts) to a quaternion. + /// + /// @see gtx_dual_quaternion + template + GLM_FUNC_DECL tdualquat dualquat_cast(mat<2, 4, T, Q> const& x); + + /// Converts a 3 * 4 matrix (augmented matrix rotation + translation) to a quaternion. + /// + /// @see gtx_dual_quaternion + template + GLM_FUNC_DECL tdualquat dualquat_cast(mat<3, 4, T, Q> const& x); + + + /// Dual-quaternion of low single-qualifier floating-point numbers. + /// + /// @see gtx_dual_quaternion + typedef tdualquat lowp_dualquat; + + /// Dual-quaternion of medium single-qualifier floating-point numbers. + /// + /// @see gtx_dual_quaternion + typedef tdualquat mediump_dualquat; + + /// Dual-quaternion of high single-qualifier floating-point numbers. + /// + /// @see gtx_dual_quaternion + typedef tdualquat highp_dualquat; + + + /// Dual-quaternion of low single-qualifier floating-point numbers. + /// + /// @see gtx_dual_quaternion + typedef tdualquat lowp_fdualquat; + + /// Dual-quaternion of medium single-qualifier floating-point numbers. + /// + /// @see gtx_dual_quaternion + typedef tdualquat mediump_fdualquat; + + /// Dual-quaternion of high single-qualifier floating-point numbers. + /// + /// @see gtx_dual_quaternion + typedef tdualquat highp_fdualquat; + + + /// Dual-quaternion of low double-qualifier floating-point numbers. + /// + /// @see gtx_dual_quaternion + typedef tdualquat lowp_ddualquat; + + /// Dual-quaternion of medium double-qualifier floating-point numbers. + /// + /// @see gtx_dual_quaternion + typedef tdualquat mediump_ddualquat; + + /// Dual-quaternion of high double-qualifier floating-point numbers. + /// + /// @see gtx_dual_quaternion + typedef tdualquat highp_ddualquat; + + +#if(!defined(GLM_PRECISION_HIGHP_FLOAT) && !defined(GLM_PRECISION_MEDIUMP_FLOAT) && !defined(GLM_PRECISION_LOWP_FLOAT)) + /// Dual-quaternion of floating-point numbers. + /// + /// @see gtx_dual_quaternion + typedef highp_fdualquat dualquat; + + /// Dual-quaternion of single-qualifier floating-point numbers. + /// + /// @see gtx_dual_quaternion + typedef highp_fdualquat fdualquat; +#elif(defined(GLM_PRECISION_HIGHP_FLOAT) && !defined(GLM_PRECISION_MEDIUMP_FLOAT) && !defined(GLM_PRECISION_LOWP_FLOAT)) + typedef highp_fdualquat dualquat; + typedef highp_fdualquat fdualquat; +#elif(!defined(GLM_PRECISION_HIGHP_FLOAT) && defined(GLM_PRECISION_MEDIUMP_FLOAT) && !defined(GLM_PRECISION_LOWP_FLOAT)) + typedef mediump_fdualquat dualquat; + typedef mediump_fdualquat fdualquat; +#elif(!defined(GLM_PRECISION_HIGHP_FLOAT) && !defined(GLM_PRECISION_MEDIUMP_FLOAT) && defined(GLM_PRECISION_LOWP_FLOAT)) + typedef lowp_fdualquat dualquat; + typedef lowp_fdualquat fdualquat; +#else +# error "GLM error: multiple default precision requested for single-precision floating-point types" +#endif + + +#if(!defined(GLM_PRECISION_HIGHP_DOUBLE) && !defined(GLM_PRECISION_MEDIUMP_DOUBLE) && !defined(GLM_PRECISION_LOWP_DOUBLE)) + /// Dual-quaternion of default double-qualifier floating-point numbers. + /// + /// @see gtx_dual_quaternion + typedef highp_ddualquat ddualquat; +#elif(defined(GLM_PRECISION_HIGHP_DOUBLE) && !defined(GLM_PRECISION_MEDIUMP_DOUBLE) && !defined(GLM_PRECISION_LOWP_DOUBLE)) + typedef highp_ddualquat ddualquat; +#elif(!defined(GLM_PRECISION_HIGHP_DOUBLE) && defined(GLM_PRECISION_MEDIUMP_DOUBLE) && !defined(GLM_PRECISION_LOWP_DOUBLE)) + typedef mediump_ddualquat ddualquat; +#elif(!defined(GLM_PRECISION_HIGHP_DOUBLE) && !defined(GLM_PRECISION_MEDIUMP_DOUBLE) && defined(GLM_PRECISION_LOWP_DOUBLE)) + typedef lowp_ddualquat ddualquat; +#else +# error "GLM error: Multiple default precision requested for double-precision floating-point types" +#endif + + /// @} +} //namespace glm + +#include "dual_quaternion.inl" diff --git a/src/GLMath/glm/gtx/dual_quaternion.inl b/src/GLMath/glm/gtx/dual_quaternion.inl new file mode 100644 index 000000000..fad07ea84 --- /dev/null +++ b/src/GLMath/glm/gtx/dual_quaternion.inl @@ -0,0 +1,352 @@ +/// @ref gtx_dual_quaternion + +#include "../geometric.hpp" +#include + +namespace glm +{ + // -- Component accesses -- + + template + GLM_FUNC_QUALIFIER typename tdualquat::part_type & tdualquat::operator[](typename tdualquat::length_type i) + { + assert(i >= 0 && i < this->length()); + return (&real)[i]; + } + + template + GLM_FUNC_QUALIFIER typename tdualquat::part_type const& tdualquat::operator[](typename tdualquat::length_type i) const + { + assert(i >= 0 && i < this->length()); + return (&real)[i]; + } + + // -- Implicit basic constructors -- + +# if GLM_CONFIG_DEFAULTED_FUNCTIONS == GLM_DISABLE + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR tdualquat::tdualquat() +# if GLM_CONFIG_DEFAULTED_FUNCTIONS != GLM_DISABLE + : real(qua()) + , dual(qua(0, 0, 0, 0)) +# endif + {} + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR tdualquat::tdualquat(tdualquat const& d) + : real(d.real) + , dual(d.dual) + {} +# endif + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR tdualquat::tdualquat(tdualquat const& d) + : real(d.real) + , dual(d.dual) + {} + + // -- Explicit basic constructors -- + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR tdualquat::tdualquat(qua const& r) + : real(r), dual(qua(0, 0, 0, 0)) + {} + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR tdualquat::tdualquat(qua const& q, vec<3, T, Q> const& p) + : real(q), dual( + T(-0.5) * ( p.x*q.x + p.y*q.y + p.z*q.z), + T(+0.5) * ( p.x*q.w + p.y*q.z - p.z*q.y), + T(+0.5) * (-p.x*q.z + p.y*q.w + p.z*q.x), + T(+0.5) * ( p.x*q.y - p.y*q.x + p.z*q.w)) + {} + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR tdualquat::tdualquat(qua const& r, qua const& d) + : real(r), dual(d) + {} + + // -- Conversion constructors -- + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR tdualquat::tdualquat(tdualquat const& q) + : real(q.real) + , dual(q.dual) + {} + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR tdualquat::tdualquat(mat<2, 4, T, Q> const& m) + { + *this = dualquat_cast(m); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR tdualquat::tdualquat(mat<3, 4, T, Q> const& m) + { + *this = dualquat_cast(m); + } + + // -- Unary arithmetic operators -- + +# if GLM_CONFIG_DEFAULTED_FUNCTIONS == GLM_DISABLE + template + GLM_FUNC_QUALIFIER tdualquat & tdualquat::operator=(tdualquat const& q) + { + this->real = q.real; + this->dual = q.dual; + return *this; + } +# endif + + template + template + GLM_FUNC_QUALIFIER tdualquat & tdualquat::operator=(tdualquat const& q) + { + this->real = q.real; + this->dual = q.dual; + return *this; + } + + template + template + GLM_FUNC_QUALIFIER tdualquat & tdualquat::operator*=(U s) + { + this->real *= static_cast(s); + this->dual *= static_cast(s); + return *this; + } + + template + template + GLM_FUNC_QUALIFIER tdualquat & tdualquat::operator/=(U s) + { + this->real /= static_cast(s); + this->dual /= static_cast(s); + return *this; + } + + // -- Unary bit operators -- + + template + GLM_FUNC_QUALIFIER tdualquat operator+(tdualquat const& q) + { + return q; + } + + template + GLM_FUNC_QUALIFIER tdualquat operator-(tdualquat const& q) + { + return tdualquat(-q.real, -q.dual); + } + + // -- Binary operators -- + + template + GLM_FUNC_QUALIFIER tdualquat operator+(tdualquat const& q, tdualquat const& p) + { + return tdualquat(q.real + p.real,q.dual + p.dual); + } + + template + GLM_FUNC_QUALIFIER tdualquat operator*(tdualquat const& p, tdualquat const& o) + { + return tdualquat(p.real * o.real,p.real * o.dual + p.dual * o.real); + } + + template + GLM_FUNC_QUALIFIER vec<3, T, Q> operator*(tdualquat const& q, vec<3, T, Q> const& v) + { + vec<3, T, Q> const real_v3(q.real.x,q.real.y,q.real.z); + vec<3, T, Q> const dual_v3(q.dual.x,q.dual.y,q.dual.z); + return (cross(real_v3, cross(real_v3,v) + v * q.real.w + dual_v3) + dual_v3 * q.real.w - real_v3 * q.dual.w) * T(2) + v; + } + + template + GLM_FUNC_QUALIFIER vec<3, T, Q> operator*(vec<3, T, Q> const& v, tdualquat const& q) + { + return glm::inverse(q) * v; + } + + template + GLM_FUNC_QUALIFIER vec<4, T, Q> operator*(tdualquat const& q, vec<4, T, Q> const& v) + { + return vec<4, T, Q>(q * vec<3, T, Q>(v), v.w); + } + + template + GLM_FUNC_QUALIFIER vec<4, T, Q> operator*(vec<4, T, Q> const& v, tdualquat const& q) + { + return glm::inverse(q) * v; + } + + template + GLM_FUNC_QUALIFIER tdualquat operator*(tdualquat const& q, T const& s) + { + return tdualquat(q.real * s, q.dual * s); + } + + template + GLM_FUNC_QUALIFIER tdualquat operator*(T const& s, tdualquat const& q) + { + return q * s; + } + + template + GLM_FUNC_QUALIFIER tdualquat operator/(tdualquat const& q, T const& s) + { + return tdualquat(q.real / s, q.dual / s); + } + + // -- Boolean operators -- + + template + GLM_FUNC_QUALIFIER bool operator==(tdualquat const& q1, tdualquat const& q2) + { + return (q1.real == q2.real) && (q1.dual == q2.dual); + } + + template + GLM_FUNC_QUALIFIER bool operator!=(tdualquat const& q1, tdualquat const& q2) + { + return (q1.real != q2.real) || (q1.dual != q2.dual); + } + + // -- Operations -- + + template + GLM_FUNC_QUALIFIER tdualquat dual_quat_identity() + { + return tdualquat( + qua(static_cast(1), static_cast(0), static_cast(0), static_cast(0)), + qua(static_cast(0), static_cast(0), static_cast(0), static_cast(0))); + } + + template + GLM_FUNC_QUALIFIER tdualquat normalize(tdualquat const& q) + { + return q / length(q.real); + } + + template + GLM_FUNC_QUALIFIER tdualquat lerp(tdualquat const& x, tdualquat const& y, T const& a) + { + // Dual Quaternion Linear blend aka DLB: + // Lerp is only defined in [0, 1] + assert(a >= static_cast(0)); + assert(a <= static_cast(1)); + T const k = dot(x.real,y.real) < static_cast(0) ? -a : a; + T const one(1); + return tdualquat(x * (one - a) + y * k); + } + + template + GLM_FUNC_QUALIFIER tdualquat inverse(tdualquat const& q) + { + const glm::qua real = conjugate(q.real); + const glm::qua dual = conjugate(q.dual); + return tdualquat(real, dual + (real * (-2.0f * dot(real,dual)))); + } + + template + GLM_FUNC_QUALIFIER mat<2, 4, T, Q> mat2x4_cast(tdualquat const& x) + { + return mat<2, 4, T, Q>( x[0].x, x[0].y, x[0].z, x[0].w, x[1].x, x[1].y, x[1].z, x[1].w ); + } + + template + GLM_FUNC_QUALIFIER mat<3, 4, T, Q> mat3x4_cast(tdualquat const& x) + { + qua r = x.real / length2(x.real); + + qua const rr(r.w * x.real.w, r.x * x.real.x, r.y * x.real.y, r.z * x.real.z); + r *= static_cast(2); + + T const xy = r.x * x.real.y; + T const xz = r.x * x.real.z; + T const yz = r.y * x.real.z; + T const wx = r.w * x.real.x; + T const wy = r.w * x.real.y; + T const wz = r.w * x.real.z; + + vec<4, T, Q> const a( + rr.w + rr.x - rr.y - rr.z, + xy - wz, + xz + wy, + -(x.dual.w * r.x - x.dual.x * r.w + x.dual.y * r.z - x.dual.z * r.y)); + + vec<4, T, Q> const b( + xy + wz, + rr.w + rr.y - rr.x - rr.z, + yz - wx, + -(x.dual.w * r.y - x.dual.x * r.z - x.dual.y * r.w + x.dual.z * r.x)); + + vec<4, T, Q> const c( + xz - wy, + yz + wx, + rr.w + rr.z - rr.x - rr.y, + -(x.dual.w * r.z + x.dual.x * r.y - x.dual.y * r.x - x.dual.z * r.w)); + + return mat<3, 4, T, Q>(a, b, c); + } + + template + GLM_FUNC_QUALIFIER tdualquat dualquat_cast(mat<2, 4, T, Q> const& x) + { + return tdualquat( + qua( x[0].w, x[0].x, x[0].y, x[0].z ), + qua( x[1].w, x[1].x, x[1].y, x[1].z )); + } + + template + GLM_FUNC_QUALIFIER tdualquat dualquat_cast(mat<3, 4, T, Q> const& x) + { + qua real; + + T const trace = x[0].x + x[1].y + x[2].z; + if(trace > static_cast(0)) + { + T const r = sqrt(T(1) + trace); + T const invr = static_cast(0.5) / r; + real.w = static_cast(0.5) * r; + real.x = (x[2].y - x[1].z) * invr; + real.y = (x[0].z - x[2].x) * invr; + real.z = (x[1].x - x[0].y) * invr; + } + else if(x[0].x > x[1].y && x[0].x > x[2].z) + { + T const r = sqrt(T(1) + x[0].x - x[1].y - x[2].z); + T const invr = static_cast(0.5) / r; + real.x = static_cast(0.5)*r; + real.y = (x[1].x + x[0].y) * invr; + real.z = (x[0].z + x[2].x) * invr; + real.w = (x[2].y - x[1].z) * invr; + } + else if(x[1].y > x[2].z) + { + T const r = sqrt(T(1) + x[1].y - x[0].x - x[2].z); + T const invr = static_cast(0.5) / r; + real.x = (x[1].x + x[0].y) * invr; + real.y = static_cast(0.5) * r; + real.z = (x[2].y + x[1].z) * invr; + real.w = (x[0].z - x[2].x) * invr; + } + else + { + T const r = sqrt(T(1) + x[2].z - x[0].x - x[1].y); + T const invr = static_cast(0.5) / r; + real.x = (x[0].z + x[2].x) * invr; + real.y = (x[2].y + x[1].z) * invr; + real.z = static_cast(0.5) * r; + real.w = (x[1].x - x[0].y) * invr; + } + + qua dual; + dual.x = static_cast(0.5) * ( x[0].w * real.w + x[1].w * real.z - x[2].w * real.y); + dual.y = static_cast(0.5) * (-x[0].w * real.z + x[1].w * real.w + x[2].w * real.x); + dual.z = static_cast(0.5) * ( x[0].w * real.y - x[1].w * real.x + x[2].w * real.w); + dual.w = -static_cast(0.5) * ( x[0].w * real.x + x[1].w * real.y + x[2].w * real.z); + return tdualquat(real, dual); + } +}//namespace glm diff --git a/src/GLMath/glm/gtx/easing.hpp b/src/GLMath/glm/gtx/easing.hpp new file mode 100644 index 000000000..57f3d61b1 --- /dev/null +++ b/src/GLMath/glm/gtx/easing.hpp @@ -0,0 +1,219 @@ +/// @ref gtx_easing +/// @file glm/gtx/easing.hpp +/// @author Robert Chisholm +/// +/// @see core (dependence) +/// +/// @defgroup gtx_easing GLM_GTX_easing +/// @ingroup gtx +/// +/// Include to use the features of this extension. +/// +/// Easing functions for animations and transitons +/// All functions take a parameter x in the range [0.0,1.0] +/// +/// Based on the AHEasing project of Warren Moore (https://github.com/warrenm/AHEasing) + +#pragma once + +// Dependency: +#include "../glm.hpp" +#include "../gtc/constants.hpp" +#include "../detail/qualifier.hpp" + +#if GLM_MESSAGES == GLM_ENABLE && !defined(GLM_EXT_INCLUDED) +# ifndef GLM_ENABLE_EXPERIMENTAL +# pragma message("GLM: GLM_GTX_easing is an experimental extension and may change in the future. Use #define GLM_ENABLE_EXPERIMENTAL before including it, if you really want to use it.") +# else +# pragma message("GLM: GLM_GTX_easing extension included") +# endif +#endif + +namespace glm{ + /// @addtogroup gtx_easing + /// @{ + + /// Modelled after the line y = x + /// @see gtx_easing + template + GLM_FUNC_DECL genType linearInterpolation(genType const & a); + + /// Modelled after the parabola y = x^2 + /// @see gtx_easing + template + GLM_FUNC_DECL genType quadraticEaseIn(genType const & a); + + /// Modelled after the parabola y = -x^2 + 2x + /// @see gtx_easing + template + GLM_FUNC_DECL genType quadraticEaseOut(genType const & a); + + /// Modelled after the piecewise quadratic + /// y = (1/2)((2x)^2) ; [0, 0.5) + /// y = -(1/2)((2x-1)*(2x-3) - 1) ; [0.5, 1] + /// @see gtx_easing + template + GLM_FUNC_DECL genType quadraticEaseInOut(genType const & a); + + /// Modelled after the cubic y = x^3 + template + GLM_FUNC_DECL genType cubicEaseIn(genType const & a); + + /// Modelled after the cubic y = (x - 1)^3 + 1 + /// @see gtx_easing + template + GLM_FUNC_DECL genType cubicEaseOut(genType const & a); + + /// Modelled after the piecewise cubic + /// y = (1/2)((2x)^3) ; [0, 0.5) + /// y = (1/2)((2x-2)^3 + 2) ; [0.5, 1] + /// @see gtx_easing + template + GLM_FUNC_DECL genType cubicEaseInOut(genType const & a); + + /// Modelled after the quartic x^4 + /// @see gtx_easing + template + GLM_FUNC_DECL genType quarticEaseIn(genType const & a); + + /// Modelled after the quartic y = 1 - (x - 1)^4 + /// @see gtx_easing + template + GLM_FUNC_DECL genType quarticEaseOut(genType const & a); + + /// Modelled after the piecewise quartic + /// y = (1/2)((2x)^4) ; [0, 0.5) + /// y = -(1/2)((2x-2)^4 - 2) ; [0.5, 1] + /// @see gtx_easing + template + GLM_FUNC_DECL genType quarticEaseInOut(genType const & a); + + /// Modelled after the quintic y = x^5 + /// @see gtx_easing + template + GLM_FUNC_DECL genType quinticEaseIn(genType const & a); + + /// Modelled after the quintic y = (x - 1)^5 + 1 + /// @see gtx_easing + template + GLM_FUNC_DECL genType quinticEaseOut(genType const & a); + + /// Modelled after the piecewise quintic + /// y = (1/2)((2x)^5) ; [0, 0.5) + /// y = (1/2)((2x-2)^5 + 2) ; [0.5, 1] + /// @see gtx_easing + template + GLM_FUNC_DECL genType quinticEaseInOut(genType const & a); + + /// Modelled after quarter-cycle of sine wave + /// @see gtx_easing + template + GLM_FUNC_DECL genType sineEaseIn(genType const & a); + + /// Modelled after quarter-cycle of sine wave (different phase) + /// @see gtx_easing + template + GLM_FUNC_DECL genType sineEaseOut(genType const & a); + + /// Modelled after half sine wave + /// @see gtx_easing + template + GLM_FUNC_DECL genType sineEaseInOut(genType const & a); + + /// Modelled after shifted quadrant IV of unit circle + /// @see gtx_easing + template + GLM_FUNC_DECL genType circularEaseIn(genType const & a); + + /// Modelled after shifted quadrant II of unit circle + /// @see gtx_easing + template + GLM_FUNC_DECL genType circularEaseOut(genType const & a); + + /// Modelled after the piecewise circular function + /// y = (1/2)(1 - sqrt(1 - 4x^2)) ; [0, 0.5) + /// y = (1/2)(sqrt(-(2x - 3)*(2x - 1)) + 1) ; [0.5, 1] + /// @see gtx_easing + template + GLM_FUNC_DECL genType circularEaseInOut(genType const & a); + + /// Modelled after the exponential function y = 2^(10(x - 1)) + /// @see gtx_easing + template + GLM_FUNC_DECL genType exponentialEaseIn(genType const & a); + + /// Modelled after the exponential function y = -2^(-10x) + 1 + /// @see gtx_easing + template + GLM_FUNC_DECL genType exponentialEaseOut(genType const & a); + + /// Modelled after the piecewise exponential + /// y = (1/2)2^(10(2x - 1)) ; [0,0.5) + /// y = -(1/2)*2^(-10(2x - 1))) + 1 ; [0.5,1] + /// @see gtx_easing + template + GLM_FUNC_DECL genType exponentialEaseInOut(genType const & a); + + /// Modelled after the damped sine wave y = sin(13pi/2*x)*pow(2, 10 * (x - 1)) + /// @see gtx_easing + template + GLM_FUNC_DECL genType elasticEaseIn(genType const & a); + + /// Modelled after the damped sine wave y = sin(-13pi/2*(x + 1))*pow(2, -10x) + 1 + /// @see gtx_easing + template + GLM_FUNC_DECL genType elasticEaseOut(genType const & a); + + /// Modelled after the piecewise exponentially-damped sine wave: + /// y = (1/2)*sin(13pi/2*(2*x))*pow(2, 10 * ((2*x) - 1)) ; [0,0.5) + /// y = (1/2)*(sin(-13pi/2*((2x-1)+1))*pow(2,-10(2*x-1)) + 2) ; [0.5, 1] + /// @see gtx_easing + template + GLM_FUNC_DECL genType elasticEaseInOut(genType const & a); + + /// @see gtx_easing + template + GLM_FUNC_DECL genType backEaseIn(genType const& a); + + /// @see gtx_easing + template + GLM_FUNC_DECL genType backEaseOut(genType const& a); + + /// @see gtx_easing + template + GLM_FUNC_DECL genType backEaseInOut(genType const& a); + + /// @param a parameter + /// @param o Optional overshoot modifier + /// @see gtx_easing + template + GLM_FUNC_DECL genType backEaseIn(genType const& a, genType const& o); + + /// @param a parameter + /// @param o Optional overshoot modifier + /// @see gtx_easing + template + GLM_FUNC_DECL genType backEaseOut(genType const& a, genType const& o); + + /// @param a parameter + /// @param o Optional overshoot modifier + /// @see gtx_easing + template + GLM_FUNC_DECL genType backEaseInOut(genType const& a, genType const& o); + + /// @see gtx_easing + template + GLM_FUNC_DECL genType bounceEaseIn(genType const& a); + + /// @see gtx_easing + template + GLM_FUNC_DECL genType bounceEaseOut(genType const& a); + + /// @see gtx_easing + template + GLM_FUNC_DECL genType bounceEaseInOut(genType const& a); + + /// @} +}//namespace glm + +#include "easing.inl" diff --git a/src/GLMath/glm/gtx/easing.inl b/src/GLMath/glm/gtx/easing.inl new file mode 100644 index 000000000..4b7d05b71 --- /dev/null +++ b/src/GLMath/glm/gtx/easing.inl @@ -0,0 +1,436 @@ +/// @ref gtx_easing + +#include + +namespace glm{ + + template + GLM_FUNC_QUALIFIER genType linearInterpolation(genType const& a) + { + // Only defined in [0, 1] + assert(a >= zero()); + assert(a <= one()); + + return a; + } + + template + GLM_FUNC_QUALIFIER genType quadraticEaseIn(genType const& a) + { + // Only defined in [0, 1] + assert(a >= zero()); + assert(a <= one()); + + return a * a; + } + + template + GLM_FUNC_QUALIFIER genType quadraticEaseOut(genType const& a) + { + // Only defined in [0, 1] + assert(a >= zero()); + assert(a <= one()); + + return -(a * (a - static_cast(2))); + } + + template + GLM_FUNC_QUALIFIER genType quadraticEaseInOut(genType const& a) + { + // Only defined in [0, 1] + assert(a >= zero()); + assert(a <= one()); + + if(a < static_cast(0.5)) + { + return static_cast(2) * a * a; + } + else + { + return (-static_cast(2) * a * a) + (4 * a) - one(); + } + } + + template + GLM_FUNC_QUALIFIER genType cubicEaseIn(genType const& a) + { + // Only defined in [0, 1] + assert(a >= zero()); + assert(a <= one()); + + return a * a * a; + } + + template + GLM_FUNC_QUALIFIER genType cubicEaseOut(genType const& a) + { + // Only defined in [0, 1] + assert(a >= zero()); + assert(a <= one()); + + genType const f = a - one(); + return f * f * f + one(); + } + + template + GLM_FUNC_QUALIFIER genType cubicEaseInOut(genType const& a) + { + // Only defined in [0, 1] + assert(a >= zero()); + assert(a <= one()); + + if (a < static_cast(0.5)) + { + return static_cast(4) * a * a * a; + } + else + { + genType const f = ((static_cast(2) * a) - static_cast(2)); + return static_cast(0.5) * f * f * f + one(); + } + } + + template + GLM_FUNC_QUALIFIER genType quarticEaseIn(genType const& a) + { + // Only defined in [0, 1] + assert(a >= zero()); + assert(a <= one()); + + return a * a * a * a; + } + + template + GLM_FUNC_QUALIFIER genType quarticEaseOut(genType const& a) + { + // Only defined in [0, 1] + assert(a >= zero()); + assert(a <= one()); + + genType const f = (a - one()); + return f * f * f * (one() - a) + one(); + } + + template + GLM_FUNC_QUALIFIER genType quarticEaseInOut(genType const& a) + { + // Only defined in [0, 1] + assert(a >= zero()); + assert(a <= one()); + + if(a < static_cast(0.5)) + { + return static_cast(8) * a * a * a * a; + } + else + { + genType const f = (a - one()); + return -static_cast(8) * f * f * f * f + one(); + } + } + + template + GLM_FUNC_QUALIFIER genType quinticEaseIn(genType const& a) + { + // Only defined in [0, 1] + assert(a >= zero()); + assert(a <= one()); + + return a * a * a * a * a; + } + + template + GLM_FUNC_QUALIFIER genType quinticEaseOut(genType const& a) + { + // Only defined in [0, 1] + assert(a >= zero()); + assert(a <= one()); + + genType const f = (a - one()); + return f * f * f * f * f + one(); + } + + template + GLM_FUNC_QUALIFIER genType quinticEaseInOut(genType const& a) + { + // Only defined in [0, 1] + assert(a >= zero()); + assert(a <= one()); + + if(a < static_cast(0.5)) + { + return static_cast(16) * a * a * a * a * a; + } + else + { + genType const f = ((static_cast(2) * a) - static_cast(2)); + return static_cast(0.5) * f * f * f * f * f + one(); + } + } + + template + GLM_FUNC_QUALIFIER genType sineEaseIn(genType const& a) + { + // Only defined in [0, 1] + assert(a >= zero()); + assert(a <= one()); + + return sin((a - one()) * half_pi()) + one(); + } + + template + GLM_FUNC_QUALIFIER genType sineEaseOut(genType const& a) + { + // Only defined in [0, 1] + assert(a >= zero()); + assert(a <= one()); + + return sin(a * half_pi()); + } + + template + GLM_FUNC_QUALIFIER genType sineEaseInOut(genType const& a) + { + // Only defined in [0, 1] + assert(a >= zero()); + assert(a <= one()); + + return static_cast(0.5) * (one() - cos(a * pi())); + } + + template + GLM_FUNC_QUALIFIER genType circularEaseIn(genType const& a) + { + // Only defined in [0, 1] + assert(a >= zero()); + assert(a <= one()); + + return one() - sqrt(one() - (a * a)); + } + + template + GLM_FUNC_QUALIFIER genType circularEaseOut(genType const& a) + { + // Only defined in [0, 1] + assert(a >= zero()); + assert(a <= one()); + + return sqrt((static_cast(2) - a) * a); + } + + template + GLM_FUNC_QUALIFIER genType circularEaseInOut(genType const& a) + { + // Only defined in [0, 1] + assert(a >= zero()); + assert(a <= one()); + + if(a < static_cast(0.5)) + { + return static_cast(0.5) * (one() - std::sqrt(one() - static_cast(4) * (a * a))); + } + else + { + return static_cast(0.5) * (std::sqrt(-((static_cast(2) * a) - static_cast(3)) * ((static_cast(2) * a) - one())) + one()); + } + } + + template + GLM_FUNC_QUALIFIER genType exponentialEaseIn(genType const& a) + { + // Only defined in [0, 1] + assert(a >= zero()); + assert(a <= one()); + + if(a <= zero()) + return a; + else + { + genType const Complementary = a - one(); + genType const Two = static_cast(2); + + return glm::pow(Two, Complementary * static_cast(10)); + } + } + + template + GLM_FUNC_QUALIFIER genType exponentialEaseOut(genType const& a) + { + // Only defined in [0, 1] + assert(a >= zero()); + assert(a <= one()); + + if(a >= one()) + return a; + else + { + return one() - glm::pow(static_cast(2), -static_cast(10) * a); + } + } + + template + GLM_FUNC_QUALIFIER genType exponentialEaseInOut(genType const& a) + { + // Only defined in [0, 1] + assert(a >= zero()); + assert(a <= one()); + + if(a < static_cast(0.5)) + return static_cast(0.5) * glm::pow(static_cast(2), (static_cast(20) * a) - static_cast(10)); + else + return -static_cast(0.5) * glm::pow(static_cast(2), (-static_cast(20) * a) + static_cast(10)) + one(); + } + + template + GLM_FUNC_QUALIFIER genType elasticEaseIn(genType const& a) + { + // Only defined in [0, 1] + assert(a >= zero()); + assert(a <= one()); + + return std::sin(static_cast(13) * half_pi() * a) * glm::pow(static_cast(2), static_cast(10) * (a - one())); + } + + template + GLM_FUNC_QUALIFIER genType elasticEaseOut(genType const& a) + { + // Only defined in [0, 1] + assert(a >= zero()); + assert(a <= one()); + + return std::sin(-static_cast(13) * half_pi() * (a + one())) * glm::pow(static_cast(2), -static_cast(10) * a) + one(); + } + + template + GLM_FUNC_QUALIFIER genType elasticEaseInOut(genType const& a) + { + // Only defined in [0, 1] + assert(a >= zero()); + assert(a <= one()); + + if(a < static_cast(0.5)) + return static_cast(0.5) * std::sin(static_cast(13) * half_pi() * (static_cast(2) * a)) * glm::pow(static_cast(2), static_cast(10) * ((static_cast(2) * a) - one())); + else + return static_cast(0.5) * (std::sin(-static_cast(13) * half_pi() * ((static_cast(2) * a - one()) + one())) * glm::pow(static_cast(2), -static_cast(10) * (static_cast(2) * a - one())) + static_cast(2)); + } + + template + GLM_FUNC_QUALIFIER genType backEaseIn(genType const& a, genType const& o) + { + // Only defined in [0, 1] + assert(a >= zero()); + assert(a <= one()); + + genType z = ((o + one()) * a) - o; + return (a * a * z); + } + + template + GLM_FUNC_QUALIFIER genType backEaseOut(genType const& a, genType const& o) + { + // Only defined in [0, 1] + assert(a >= zero()); + assert(a <= one()); + + genType n = a - one(); + genType z = ((o + one()) * n) + o; + return (n * n * z) + one(); + } + + template + GLM_FUNC_QUALIFIER genType backEaseInOut(genType const& a, genType const& o) + { + // Only defined in [0, 1] + assert(a >= zero()); + assert(a <= one()); + + genType s = o * static_cast(1.525); + genType x = static_cast(0.5); + genType n = a / static_cast(0.5); + + if (n < static_cast(1)) + { + genType z = ((s + static_cast(1)) * n) - s; + genType m = n * n * z; + return x * m; + } + else + { + n -= static_cast(2); + genType z = ((s + static_cast(1)) * n) + s; + genType m = (n*n*z) + static_cast(2); + return x * m; + } + } + + template + GLM_FUNC_QUALIFIER genType backEaseIn(genType const& a) + { + return backEaseIn(a, static_cast(1.70158)); + } + + template + GLM_FUNC_QUALIFIER genType backEaseOut(genType const& a) + { + return backEaseOut(a, static_cast(1.70158)); + } + + template + GLM_FUNC_QUALIFIER genType backEaseInOut(genType const& a) + { + return backEaseInOut(a, static_cast(1.70158)); + } + + template + GLM_FUNC_QUALIFIER genType bounceEaseOut(genType const& a) + { + // Only defined in [0, 1] + assert(a >= zero()); + assert(a <= one()); + + if(a < static_cast(4.0 / 11.0)) + { + return (static_cast(121) * a * a) / static_cast(16); + } + else if(a < static_cast(8.0 / 11.0)) + { + return (static_cast(363.0 / 40.0) * a * a) - (static_cast(99.0 / 10.0) * a) + static_cast(17.0 / 5.0); + } + else if(a < static_cast(9.0 / 10.0)) + { + return (static_cast(4356.0 / 361.0) * a * a) - (static_cast(35442.0 / 1805.0) * a) + static_cast(16061.0 / 1805.0); + } + else + { + return (static_cast(54.0 / 5.0) * a * a) - (static_cast(513.0 / 25.0) * a) + static_cast(268.0 / 25.0); + } + } + + template + GLM_FUNC_QUALIFIER genType bounceEaseIn(genType const& a) + { + // Only defined in [0, 1] + assert(a >= zero()); + assert(a <= one()); + + return one() - bounceEaseOut(one() - a); + } + + template + GLM_FUNC_QUALIFIER genType bounceEaseInOut(genType const& a) + { + // Only defined in [0, 1] + assert(a >= zero()); + assert(a <= one()); + + if(a < static_cast(0.5)) + { + return static_cast(0.5) * (one() - bounceEaseOut(a * static_cast(2))); + } + else + { + return static_cast(0.5) * bounceEaseOut(a * static_cast(2) - one()) + static_cast(0.5); + } + } + +}//namespace glm diff --git a/src/GLMath/glm/gtx/euler_angles.hpp b/src/GLMath/glm/gtx/euler_angles.hpp new file mode 100644 index 000000000..27236973a --- /dev/null +++ b/src/GLMath/glm/gtx/euler_angles.hpp @@ -0,0 +1,335 @@ +/// @ref gtx_euler_angles +/// @file glm/gtx/euler_angles.hpp +/// +/// @see core (dependence) +/// +/// @defgroup gtx_euler_angles GLM_GTX_euler_angles +/// @ingroup gtx +/// +/// Include to use the features of this extension. +/// +/// Build matrices from Euler angles. +/// +/// Extraction of Euler angles from rotation matrix. +/// Based on the original paper 2014 Mike Day - Extracting Euler Angles from a Rotation Matrix. + +#pragma once + +// Dependency: +#include "../glm.hpp" + +#if GLM_MESSAGES == GLM_ENABLE && !defined(GLM_EXT_INCLUDED) +# ifndef GLM_ENABLE_EXPERIMENTAL +# pragma message("GLM: GLM_GTX_euler_angles is an experimental extension and may change in the future. Use #define GLM_ENABLE_EXPERIMENTAL before including it, if you really want to use it.") +# else +# pragma message("GLM: GLM_GTX_euler_angles extension included") +# endif +#endif + +namespace glm +{ + /// @addtogroup gtx_euler_angles + /// @{ + + /// Creates a 3D 4 * 4 homogeneous rotation matrix from an euler angle X. + /// @see gtx_euler_angles + template + GLM_FUNC_DECL mat<4, 4, T, defaultp> eulerAngleX( + T const& angleX); + + /// Creates a 3D 4 * 4 homogeneous rotation matrix from an euler angle Y. + /// @see gtx_euler_angles + template + GLM_FUNC_DECL mat<4, 4, T, defaultp> eulerAngleY( + T const& angleY); + + /// Creates a 3D 4 * 4 homogeneous rotation matrix from an euler angle Z. + /// @see gtx_euler_angles + template + GLM_FUNC_DECL mat<4, 4, T, defaultp> eulerAngleZ( + T const& angleZ); + + /// Creates a 3D 4 * 4 homogeneous derived matrix from the rotation matrix about X-axis. + /// @see gtx_euler_angles + template + GLM_FUNC_DECL mat<4, 4, T, defaultp> derivedEulerAngleX( + T const & angleX, T const & angularVelocityX); + + /// Creates a 3D 4 * 4 homogeneous derived matrix from the rotation matrix about Y-axis. + /// @see gtx_euler_angles + template + GLM_FUNC_DECL mat<4, 4, T, defaultp> derivedEulerAngleY( + T const & angleY, T const & angularVelocityY); + + /// Creates a 3D 4 * 4 homogeneous derived matrix from the rotation matrix about Z-axis. + /// @see gtx_euler_angles + template + GLM_FUNC_DECL mat<4, 4, T, defaultp> derivedEulerAngleZ( + T const & angleZ, T const & angularVelocityZ); + + /// Creates a 3D 4 * 4 homogeneous rotation matrix from euler angles (X * Y). + /// @see gtx_euler_angles + template + GLM_FUNC_DECL mat<4, 4, T, defaultp> eulerAngleXY( + T const& angleX, + T const& angleY); + + /// Creates a 3D 4 * 4 homogeneous rotation matrix from euler angles (Y * X). + /// @see gtx_euler_angles + template + GLM_FUNC_DECL mat<4, 4, T, defaultp> eulerAngleYX( + T const& angleY, + T const& angleX); + + /// Creates a 3D 4 * 4 homogeneous rotation matrix from euler angles (X * Z). + /// @see gtx_euler_angles + template + GLM_FUNC_DECL mat<4, 4, T, defaultp> eulerAngleXZ( + T const& angleX, + T const& angleZ); + + /// Creates a 3D 4 * 4 homogeneous rotation matrix from euler angles (Z * X). + /// @see gtx_euler_angles + template + GLM_FUNC_DECL mat<4, 4, T, defaultp> eulerAngleZX( + T const& angle, + T const& angleX); + + /// Creates a 3D 4 * 4 homogeneous rotation matrix from euler angles (Y * Z). + /// @see gtx_euler_angles + template + GLM_FUNC_DECL mat<4, 4, T, defaultp> eulerAngleYZ( + T const& angleY, + T const& angleZ); + + /// Creates a 3D 4 * 4 homogeneous rotation matrix from euler angles (Z * Y). + /// @see gtx_euler_angles + template + GLM_FUNC_DECL mat<4, 4, T, defaultp> eulerAngleZY( + T const& angleZ, + T const& angleY); + + /// Creates a 3D 4 * 4 homogeneous rotation matrix from euler angles (X * Y * Z). + /// @see gtx_euler_angles + template + GLM_FUNC_DECL mat<4, 4, T, defaultp> eulerAngleXYZ( + T const& t1, + T const& t2, + T const& t3); + + /// Creates a 3D 4 * 4 homogeneous rotation matrix from euler angles (Y * X * Z). + /// @see gtx_euler_angles + template + GLM_FUNC_DECL mat<4, 4, T, defaultp> eulerAngleYXZ( + T const& yaw, + T const& pitch, + T const& roll); + + /// Creates a 3D 4 * 4 homogeneous rotation matrix from euler angles (X * Z * X). + /// @see gtx_euler_angles + template + GLM_FUNC_DECL mat<4, 4, T, defaultp> eulerAngleXZX( + T const & t1, + T const & t2, + T const & t3); + + /// Creates a 3D 4 * 4 homogeneous rotation matrix from euler angles (X * Y * X). + /// @see gtx_euler_angles + template + GLM_FUNC_DECL mat<4, 4, T, defaultp> eulerAngleXYX( + T const & t1, + T const & t2, + T const & t3); + + /// Creates a 3D 4 * 4 homogeneous rotation matrix from euler angles (Y * X * Y). + /// @see gtx_euler_angles + template + GLM_FUNC_DECL mat<4, 4, T, defaultp> eulerAngleYXY( + T const & t1, + T const & t2, + T const & t3); + + /// Creates a 3D 4 * 4 homogeneous rotation matrix from euler angles (Y * Z * Y). + /// @see gtx_euler_angles + template + GLM_FUNC_DECL mat<4, 4, T, defaultp> eulerAngleYZY( + T const & t1, + T const & t2, + T const & t3); + + /// Creates a 3D 4 * 4 homogeneous rotation matrix from euler angles (Z * Y * Z). + /// @see gtx_euler_angles + template + GLM_FUNC_DECL mat<4, 4, T, defaultp> eulerAngleZYZ( + T const & t1, + T const & t2, + T const & t3); + + /// Creates a 3D 4 * 4 homogeneous rotation matrix from euler angles (Z * X * Z). + /// @see gtx_euler_angles + template + GLM_FUNC_DECL mat<4, 4, T, defaultp> eulerAngleZXZ( + T const & t1, + T const & t2, + T const & t3); + + /// Creates a 3D 4 * 4 homogeneous rotation matrix from euler angles (X * Z * Y). + /// @see gtx_euler_angles + template + GLM_FUNC_DECL mat<4, 4, T, defaultp> eulerAngleXZY( + T const & t1, + T const & t2, + T const & t3); + + /// Creates a 3D 4 * 4 homogeneous rotation matrix from euler angles (Y * Z * X). + /// @see gtx_euler_angles + template + GLM_FUNC_DECL mat<4, 4, T, defaultp> eulerAngleYZX( + T const & t1, + T const & t2, + T const & t3); + + /// Creates a 3D 4 * 4 homogeneous rotation matrix from euler angles (Z * Y * X). + /// @see gtx_euler_angles + template + GLM_FUNC_DECL mat<4, 4, T, defaultp> eulerAngleZYX( + T const & t1, + T const & t2, + T const & t3); + + /// Creates a 3D 4 * 4 homogeneous rotation matrix from euler angles (Z * X * Y). + /// @see gtx_euler_angles + template + GLM_FUNC_DECL mat<4, 4, T, defaultp> eulerAngleZXY( + T const & t1, + T const & t2, + T const & t3); + + /// Creates a 3D 4 * 4 homogeneous rotation matrix from euler angles (Y * X * Z). + /// @see gtx_euler_angles + template + GLM_FUNC_DECL mat<4, 4, T, defaultp> yawPitchRoll( + T const& yaw, + T const& pitch, + T const& roll); + + /// Creates a 2D 2 * 2 rotation matrix from an euler angle. + /// @see gtx_euler_angles + template + GLM_FUNC_DECL mat<2, 2, T, defaultp> orientate2(T const& angle); + + /// Creates a 2D 4 * 4 homogeneous rotation matrix from an euler angle. + /// @see gtx_euler_angles + template + GLM_FUNC_DECL mat<3, 3, T, defaultp> orientate3(T const& angle); + + /// Creates a 3D 3 * 3 rotation matrix from euler angles (Y * X * Z). + /// @see gtx_euler_angles + template + GLM_FUNC_DECL mat<3, 3, T, Q> orientate3(vec<3, T, Q> const& angles); + + /// Creates a 3D 4 * 4 homogeneous rotation matrix from euler angles (Y * X * Z). + /// @see gtx_euler_angles + template + GLM_FUNC_DECL mat<4, 4, T, Q> orientate4(vec<3, T, Q> const& angles); + + /// Extracts the (X * Y * Z) Euler angles from the rotation matrix M + /// @see gtx_euler_angles + template + GLM_FUNC_DECL void extractEulerAngleXYZ(mat<4, 4, T, defaultp> const& M, + T & t1, + T & t2, + T & t3); + + /// Extracts the (Y * X * Z) Euler angles from the rotation matrix M + /// @see gtx_euler_angles + template + GLM_FUNC_DECL void extractEulerAngleYXZ(mat<4, 4, T, defaultp> const & M, + T & t1, + T & t2, + T & t3); + + /// Extracts the (X * Z * X) Euler angles from the rotation matrix M + /// @see gtx_euler_angles + template + GLM_FUNC_DECL void extractEulerAngleXZX(mat<4, 4, T, defaultp> const & M, + T & t1, + T & t2, + T & t3); + + /// Extracts the (X * Y * X) Euler angles from the rotation matrix M + /// @see gtx_euler_angles + template + GLM_FUNC_DECL void extractEulerAngleXYX(mat<4, 4, T, defaultp> const & M, + T & t1, + T & t2, + T & t3); + + /// Extracts the (Y * X * Y) Euler angles from the rotation matrix M + /// @see gtx_euler_angles + template + GLM_FUNC_DECL void extractEulerAngleYXY(mat<4, 4, T, defaultp> const & M, + T & t1, + T & t2, + T & t3); + + /// Extracts the (Y * Z * Y) Euler angles from the rotation matrix M + /// @see gtx_euler_angles + template + GLM_FUNC_DECL void extractEulerAngleYZY(mat<4, 4, T, defaultp> const & M, + T & t1, + T & t2, + T & t3); + + /// Extracts the (Z * Y * Z) Euler angles from the rotation matrix M + /// @see gtx_euler_angles + template + GLM_FUNC_DECL void extractEulerAngleZYZ(mat<4, 4, T, defaultp> const & M, + T & t1, + T & t2, + T & t3); + + /// Extracts the (Z * X * Z) Euler angles from the rotation matrix M + /// @see gtx_euler_angles + template + GLM_FUNC_DECL void extractEulerAngleZXZ(mat<4, 4, T, defaultp> const & M, + T & t1, + T & t2, + T & t3); + + /// Extracts the (X * Z * Y) Euler angles from the rotation matrix M + /// @see gtx_euler_angles + template + GLM_FUNC_DECL void extractEulerAngleXZY(mat<4, 4, T, defaultp> const & M, + T & t1, + T & t2, + T & t3); + + /// Extracts the (Y * Z * X) Euler angles from the rotation matrix M + /// @see gtx_euler_angles + template + GLM_FUNC_DECL void extractEulerAngleYZX(mat<4, 4, T, defaultp> const & M, + T & t1, + T & t2, + T & t3); + + /// Extracts the (Z * Y * X) Euler angles from the rotation matrix M + /// @see gtx_euler_angles + template + GLM_FUNC_DECL void extractEulerAngleZYX(mat<4, 4, T, defaultp> const & M, + T & t1, + T & t2, + T & t3); + + /// Extracts the (Z * X * Y) Euler angles from the rotation matrix M + /// @see gtx_euler_angles + template + GLM_FUNC_DECL void extractEulerAngleZXY(mat<4, 4, T, defaultp> const & M, + T & t1, + T & t2, + T & t3); + + /// @} +}//namespace glm + +#include "euler_angles.inl" diff --git a/src/GLMath/glm/gtx/euler_angles.inl b/src/GLMath/glm/gtx/euler_angles.inl new file mode 100644 index 000000000..68c50124e --- /dev/null +++ b/src/GLMath/glm/gtx/euler_angles.inl @@ -0,0 +1,899 @@ +/// @ref gtx_euler_angles + +#include "compatibility.hpp" // glm::atan2 + +namespace glm +{ + template + GLM_FUNC_QUALIFIER mat<4, 4, T, defaultp> eulerAngleX + ( + T const& angleX + ) + { + T cosX = glm::cos(angleX); + T sinX = glm::sin(angleX); + + return mat<4, 4, T, defaultp>( + T(1), T(0), T(0), T(0), + T(0), cosX, sinX, T(0), + T(0),-sinX, cosX, T(0), + T(0), T(0), T(0), T(1)); + } + + template + GLM_FUNC_QUALIFIER mat<4, 4, T, defaultp> eulerAngleY + ( + T const& angleY + ) + { + T cosY = glm::cos(angleY); + T sinY = glm::sin(angleY); + + return mat<4, 4, T, defaultp>( + cosY, T(0), -sinY, T(0), + T(0), T(1), T(0), T(0), + sinY, T(0), cosY, T(0), + T(0), T(0), T(0), T(1)); + } + + template + GLM_FUNC_QUALIFIER mat<4, 4, T, defaultp> eulerAngleZ + ( + T const& angleZ + ) + { + T cosZ = glm::cos(angleZ); + T sinZ = glm::sin(angleZ); + + return mat<4, 4, T, defaultp>( + cosZ, sinZ, T(0), T(0), + -sinZ, cosZ, T(0), T(0), + T(0), T(0), T(1), T(0), + T(0), T(0), T(0), T(1)); + } + + template + GLM_FUNC_QUALIFIER mat<4, 4, T, defaultp> derivedEulerAngleX + ( + T const & angleX, + T const & angularVelocityX + ) + { + T cosX = glm::cos(angleX) * angularVelocityX; + T sinX = glm::sin(angleX) * angularVelocityX; + + return mat<4, 4, T, defaultp>( + T(0), T(0), T(0), T(0), + T(0),-sinX, cosX, T(0), + T(0),-cosX,-sinX, T(0), + T(0), T(0), T(0), T(0)); + } + + template + GLM_FUNC_QUALIFIER mat<4, 4, T, defaultp> derivedEulerAngleY + ( + T const & angleY, + T const & angularVelocityY + ) + { + T cosY = glm::cos(angleY) * angularVelocityY; + T sinY = glm::sin(angleY) * angularVelocityY; + + return mat<4, 4, T, defaultp>( + -sinY, T(0), -cosY, T(0), + T(0), T(0), T(0), T(0), + cosY, T(0), -sinY, T(0), + T(0), T(0), T(0), T(0)); + } + + template + GLM_FUNC_QUALIFIER mat<4, 4, T, defaultp> derivedEulerAngleZ + ( + T const & angleZ, + T const & angularVelocityZ + ) + { + T cosZ = glm::cos(angleZ) * angularVelocityZ; + T sinZ = glm::sin(angleZ) * angularVelocityZ; + + return mat<4, 4, T, defaultp>( + -sinZ, cosZ, T(0), T(0), + -cosZ, -sinZ, T(0), T(0), + T(0), T(0), T(0), T(0), + T(0), T(0), T(0), T(0)); + } + + template + GLM_FUNC_QUALIFIER mat<4, 4, T, defaultp> eulerAngleXY + ( + T const& angleX, + T const& angleY + ) + { + T cosX = glm::cos(angleX); + T sinX = glm::sin(angleX); + T cosY = glm::cos(angleY); + T sinY = glm::sin(angleY); + + return mat<4, 4, T, defaultp>( + cosY, -sinX * -sinY, cosX * -sinY, T(0), + T(0), cosX, sinX, T(0), + sinY, -sinX * cosY, cosX * cosY, T(0), + T(0), T(0), T(0), T(1)); + } + + template + GLM_FUNC_QUALIFIER mat<4, 4, T, defaultp> eulerAngleYX + ( + T const& angleY, + T const& angleX + ) + { + T cosX = glm::cos(angleX); + T sinX = glm::sin(angleX); + T cosY = glm::cos(angleY); + T sinY = glm::sin(angleY); + + return mat<4, 4, T, defaultp>( + cosY, 0, -sinY, T(0), + sinY * sinX, cosX, cosY * sinX, T(0), + sinY * cosX, -sinX, cosY * cosX, T(0), + T(0), T(0), T(0), T(1)); + } + + template + GLM_FUNC_QUALIFIER mat<4, 4, T, defaultp> eulerAngleXZ + ( + T const& angleX, + T const& angleZ + ) + { + return eulerAngleX(angleX) * eulerAngleZ(angleZ); + } + + template + GLM_FUNC_QUALIFIER mat<4, 4, T, defaultp> eulerAngleZX + ( + T const& angleZ, + T const& angleX + ) + { + return eulerAngleZ(angleZ) * eulerAngleX(angleX); + } + + template + GLM_FUNC_QUALIFIER mat<4, 4, T, defaultp> eulerAngleYZ + ( + T const& angleY, + T const& angleZ + ) + { + return eulerAngleY(angleY) * eulerAngleZ(angleZ); + } + + template + GLM_FUNC_QUALIFIER mat<4, 4, T, defaultp> eulerAngleZY + ( + T const& angleZ, + T const& angleY + ) + { + return eulerAngleZ(angleZ) * eulerAngleY(angleY); + } + + template + GLM_FUNC_QUALIFIER mat<4, 4, T, defaultp> eulerAngleXYZ + ( + T const& t1, + T const& t2, + T const& t3 + ) + { + T c1 = glm::cos(-t1); + T c2 = glm::cos(-t2); + T c3 = glm::cos(-t3); + T s1 = glm::sin(-t1); + T s2 = glm::sin(-t2); + T s3 = glm::sin(-t3); + + mat<4, 4, T, defaultp> Result; + Result[0][0] = c2 * c3; + Result[0][1] =-c1 * s3 + s1 * s2 * c3; + Result[0][2] = s1 * s3 + c1 * s2 * c3; + Result[0][3] = static_cast(0); + Result[1][0] = c2 * s3; + Result[1][1] = c1 * c3 + s1 * s2 * s3; + Result[1][2] =-s1 * c3 + c1 * s2 * s3; + Result[1][3] = static_cast(0); + Result[2][0] =-s2; + Result[2][1] = s1 * c2; + Result[2][2] = c1 * c2; + Result[2][3] = static_cast(0); + Result[3][0] = static_cast(0); + Result[3][1] = static_cast(0); + Result[3][2] = static_cast(0); + Result[3][3] = static_cast(1); + return Result; + } + + template + GLM_FUNC_QUALIFIER mat<4, 4, T, defaultp> eulerAngleYXZ + ( + T const& yaw, + T const& pitch, + T const& roll + ) + { + T tmp_ch = glm::cos(yaw); + T tmp_sh = glm::sin(yaw); + T tmp_cp = glm::cos(pitch); + T tmp_sp = glm::sin(pitch); + T tmp_cb = glm::cos(roll); + T tmp_sb = glm::sin(roll); + + mat<4, 4, T, defaultp> Result; + Result[0][0] = tmp_ch * tmp_cb + tmp_sh * tmp_sp * tmp_sb; + Result[0][1] = tmp_sb * tmp_cp; + Result[0][2] = -tmp_sh * tmp_cb + tmp_ch * tmp_sp * tmp_sb; + Result[0][3] = static_cast(0); + Result[1][0] = -tmp_ch * tmp_sb + tmp_sh * tmp_sp * tmp_cb; + Result[1][1] = tmp_cb * tmp_cp; + Result[1][2] = tmp_sb * tmp_sh + tmp_ch * tmp_sp * tmp_cb; + Result[1][3] = static_cast(0); + Result[2][0] = tmp_sh * tmp_cp; + Result[2][1] = -tmp_sp; + Result[2][2] = tmp_ch * tmp_cp; + Result[2][3] = static_cast(0); + Result[3][0] = static_cast(0); + Result[3][1] = static_cast(0); + Result[3][2] = static_cast(0); + Result[3][3] = static_cast(1); + return Result; + } + + template + GLM_FUNC_QUALIFIER mat<4, 4, T, defaultp> eulerAngleXZX + ( + T const & t1, + T const & t2, + T const & t3 + ) + { + T c1 = glm::cos(t1); + T s1 = glm::sin(t1); + T c2 = glm::cos(t2); + T s2 = glm::sin(t2); + T c3 = glm::cos(t3); + T s3 = glm::sin(t3); + + mat<4, 4, T, defaultp> Result; + Result[0][0] = c2; + Result[0][1] = c1 * s2; + Result[0][2] = s1 * s2; + Result[0][3] = static_cast(0); + Result[1][0] =-c3 * s2; + Result[1][1] = c1 * c2 * c3 - s1 * s3; + Result[1][2] = c1 * s3 + c2 * c3 * s1; + Result[1][3] = static_cast(0); + Result[2][0] = s2 * s3; + Result[2][1] =-c3 * s1 - c1 * c2 * s3; + Result[2][2] = c1 * c3 - c2 * s1 * s3; + Result[2][3] = static_cast(0); + Result[3][0] = static_cast(0); + Result[3][1] = static_cast(0); + Result[3][2] = static_cast(0); + Result[3][3] = static_cast(1); + return Result; + } + + template + GLM_FUNC_QUALIFIER mat<4, 4, T, defaultp> eulerAngleXYX + ( + T const & t1, + T const & t2, + T const & t3 + ) + { + T c1 = glm::cos(t1); + T s1 = glm::sin(t1); + T c2 = glm::cos(t2); + T s2 = glm::sin(t2); + T c3 = glm::cos(t3); + T s3 = glm::sin(t3); + + mat<4, 4, T, defaultp> Result; + Result[0][0] = c2; + Result[0][1] = s1 * s2; + Result[0][2] =-c1 * s2; + Result[0][3] = static_cast(0); + Result[1][0] = s2 * s3; + Result[1][1] = c1 * c3 - c2 * s1 * s3; + Result[1][2] = c3 * s1 + c1 * c2 * s3; + Result[1][3] = static_cast(0); + Result[2][0] = c3 * s2; + Result[2][1] =-c1 * s3 - c2 * c3 * s1; + Result[2][2] = c1 * c2 * c3 - s1 * s3; + Result[2][3] = static_cast(0); + Result[3][0] = static_cast(0); + Result[3][1] = static_cast(0); + Result[3][2] = static_cast(0); + Result[3][3] = static_cast(1); + return Result; + } + + template + GLM_FUNC_QUALIFIER mat<4, 4, T, defaultp> eulerAngleYXY + ( + T const & t1, + T const & t2, + T const & t3 + ) + { + T c1 = glm::cos(t1); + T s1 = glm::sin(t1); + T c2 = glm::cos(t2); + T s2 = glm::sin(t2); + T c3 = glm::cos(t3); + T s3 = glm::sin(t3); + + mat<4, 4, T, defaultp> Result; + Result[0][0] = c1 * c3 - c2 * s1 * s3; + Result[0][1] = s2* s3; + Result[0][2] =-c3 * s1 - c1 * c2 * s3; + Result[0][3] = static_cast(0); + Result[1][0] = s1 * s2; + Result[1][1] = c2; + Result[1][2] = c1 * s2; + Result[1][3] = static_cast(0); + Result[2][0] = c1 * s3 + c2 * c3 * s1; + Result[2][1] =-c3 * s2; + Result[2][2] = c1 * c2 * c3 - s1 * s3; + Result[2][3] = static_cast(0); + Result[3][0] = static_cast(0); + Result[3][1] = static_cast(0); + Result[3][2] = static_cast(0); + Result[3][3] = static_cast(1); + return Result; + } + + template + GLM_FUNC_QUALIFIER mat<4, 4, T, defaultp> eulerAngleYZY + ( + T const & t1, + T const & t2, + T const & t3 + ) + { + T c1 = glm::cos(t1); + T s1 = glm::sin(t1); + T c2 = glm::cos(t2); + T s2 = glm::sin(t2); + T c3 = glm::cos(t3); + T s3 = glm::sin(t3); + + mat<4, 4, T, defaultp> Result; + Result[0][0] = c1 * c2 * c3 - s1 * s3; + Result[0][1] = c3 * s2; + Result[0][2] =-c1 * s3 - c2 * c3 * s1; + Result[0][3] = static_cast(0); + Result[1][0] =-c1 * s2; + Result[1][1] = c2; + Result[1][2] = s1 * s2; + Result[1][3] = static_cast(0); + Result[2][0] = c3 * s1 + c1 * c2 * s3; + Result[2][1] = s2 * s3; + Result[2][2] = c1 * c3 - c2 * s1 * s3; + Result[2][3] = static_cast(0); + Result[3][0] = static_cast(0); + Result[3][1] = static_cast(0); + Result[3][2] = static_cast(0); + Result[3][3] = static_cast(1); + return Result; + } + + template + GLM_FUNC_QUALIFIER mat<4, 4, T, defaultp> eulerAngleZYZ + ( + T const & t1, + T const & t2, + T const & t3 + ) + { + T c1 = glm::cos(t1); + T s1 = glm::sin(t1); + T c2 = glm::cos(t2); + T s2 = glm::sin(t2); + T c3 = glm::cos(t3); + T s3 = glm::sin(t3); + + mat<4, 4, T, defaultp> Result; + Result[0][0] = c1 * c2 * c3 - s1 * s3; + Result[0][1] = c1 * s3 + c2 * c3 * s1; + Result[0][2] =-c3 * s2; + Result[0][3] = static_cast(0); + Result[1][0] =-c3 * s1 - c1 * c2 * s3; + Result[1][1] = c1 * c3 - c2 * s1 * s3; + Result[1][2] = s2 * s3; + Result[1][3] = static_cast(0); + Result[2][0] = c1 * s2; + Result[2][1] = s1 * s2; + Result[2][2] = c2; + Result[2][3] = static_cast(0); + Result[3][0] = static_cast(0); + Result[3][1] = static_cast(0); + Result[3][2] = static_cast(0); + Result[3][3] = static_cast(1); + return Result; + } + + template + GLM_FUNC_QUALIFIER mat<4, 4, T, defaultp> eulerAngleZXZ + ( + T const & t1, + T const & t2, + T const & t3 + ) + { + T c1 = glm::cos(t1); + T s1 = glm::sin(t1); + T c2 = glm::cos(t2); + T s2 = glm::sin(t2); + T c3 = glm::cos(t3); + T s3 = glm::sin(t3); + + mat<4, 4, T, defaultp> Result; + Result[0][0] = c1 * c3 - c2 * s1 * s3; + Result[0][1] = c3 * s1 + c1 * c2 * s3; + Result[0][2] = s2 *s3; + Result[0][3] = static_cast(0); + Result[1][0] =-c1 * s3 - c2 * c3 * s1; + Result[1][1] = c1 * c2 * c3 - s1 * s3; + Result[1][2] = c3 * s2; + Result[1][3] = static_cast(0); + Result[2][0] = s1 * s2; + Result[2][1] =-c1 * s2; + Result[2][2] = c2; + Result[2][3] = static_cast(0); + Result[3][0] = static_cast(0); + Result[3][1] = static_cast(0); + Result[3][2] = static_cast(0); + Result[3][3] = static_cast(1); + return Result; + } + + template + GLM_FUNC_QUALIFIER mat<4, 4, T, defaultp> eulerAngleXZY + ( + T const & t1, + T const & t2, + T const & t3 + ) + { + T c1 = glm::cos(t1); + T s1 = glm::sin(t1); + T c2 = glm::cos(t2); + T s2 = glm::sin(t2); + T c3 = glm::cos(t3); + T s3 = glm::sin(t3); + + mat<4, 4, T, defaultp> Result; + Result[0][0] = c2 * c3; + Result[0][1] = s1 * s3 + c1 * c3 * s2; + Result[0][2] = c3 * s1 * s2 - c1 * s3; + Result[0][3] = static_cast(0); + Result[1][0] =-s2; + Result[1][1] = c1 * c2; + Result[1][2] = c2 * s1; + Result[1][3] = static_cast(0); + Result[2][0] = c2 * s3; + Result[2][1] = c1 * s2 * s3 - c3 * s1; + Result[2][2] = c1 * c3 + s1 * s2 *s3; + Result[2][3] = static_cast(0); + Result[3][0] = static_cast(0); + Result[3][1] = static_cast(0); + Result[3][2] = static_cast(0); + Result[3][3] = static_cast(1); + return Result; + } + + template + GLM_FUNC_QUALIFIER mat<4, 4, T, defaultp> eulerAngleYZX + ( + T const & t1, + T const & t2, + T const & t3 + ) + { + T c1 = glm::cos(t1); + T s1 = glm::sin(t1); + T c2 = glm::cos(t2); + T s2 = glm::sin(t2); + T c3 = glm::cos(t3); + T s3 = glm::sin(t3); + + mat<4, 4, T, defaultp> Result; + Result[0][0] = c1 * c2; + Result[0][1] = s2; + Result[0][2] =-c2 * s1; + Result[0][3] = static_cast(0); + Result[1][0] = s1 * s3 - c1 * c3 * s2; + Result[1][1] = c2 * c3; + Result[1][2] = c1 * s3 + c3 * s1 * s2; + Result[1][3] = static_cast(0); + Result[2][0] = c3 * s1 + c1 * s2 * s3; + Result[2][1] =-c2 * s3; + Result[2][2] = c1 * c3 - s1 * s2 * s3; + Result[2][3] = static_cast(0); + Result[3][0] = static_cast(0); + Result[3][1] = static_cast(0); + Result[3][2] = static_cast(0); + Result[3][3] = static_cast(1); + return Result; + } + + template + GLM_FUNC_QUALIFIER mat<4, 4, T, defaultp> eulerAngleZYX + ( + T const & t1, + T const & t2, + T const & t3 + ) + { + T c1 = glm::cos(t1); + T s1 = glm::sin(t1); + T c2 = glm::cos(t2); + T s2 = glm::sin(t2); + T c3 = glm::cos(t3); + T s3 = glm::sin(t3); + + mat<4, 4, T, defaultp> Result; + Result[0][0] = c1 * c2; + Result[0][1] = c2 * s1; + Result[0][2] =-s2; + Result[0][3] = static_cast(0); + Result[1][0] = c1 * s2 * s3 - c3 * s1; + Result[1][1] = c1 * c3 + s1 * s2 * s3; + Result[1][2] = c2 * s3; + Result[1][3] = static_cast(0); + Result[2][0] = s1 * s3 + c1 * c3 * s2; + Result[2][1] = c3 * s1 * s2 - c1 * s3; + Result[2][2] = c2 * c3; + Result[2][3] = static_cast(0); + Result[3][0] = static_cast(0); + Result[3][1] = static_cast(0); + Result[3][2] = static_cast(0); + Result[3][3] = static_cast(1); + return Result; + } + + template + GLM_FUNC_QUALIFIER mat<4, 4, T, defaultp> eulerAngleZXY + ( + T const & t1, + T const & t2, + T const & t3 + ) + { + T c1 = glm::cos(t1); + T s1 = glm::sin(t1); + T c2 = glm::cos(t2); + T s2 = glm::sin(t2); + T c3 = glm::cos(t3); + T s3 = glm::sin(t3); + + mat<4, 4, T, defaultp> Result; + Result[0][0] = c1 * c3 - s1 * s2 * s3; + Result[0][1] = c3 * s1 + c1 * s2 * s3; + Result[0][2] =-c2 * s3; + Result[0][3] = static_cast(0); + Result[1][0] =-c2 * s1; + Result[1][1] = c1 * c2; + Result[1][2] = s2; + Result[1][3] = static_cast(0); + Result[2][0] = c1 * s3 + c3 * s1 * s2; + Result[2][1] = s1 * s3 - c1 * c3 * s2; + Result[2][2] = c2 * c3; + Result[2][3] = static_cast(0); + Result[3][0] = static_cast(0); + Result[3][1] = static_cast(0); + Result[3][2] = static_cast(0); + Result[3][3] = static_cast(1); + return Result; + } + + template + GLM_FUNC_QUALIFIER mat<4, 4, T, defaultp> yawPitchRoll + ( + T const& yaw, + T const& pitch, + T const& roll + ) + { + T tmp_ch = glm::cos(yaw); + T tmp_sh = glm::sin(yaw); + T tmp_cp = glm::cos(pitch); + T tmp_sp = glm::sin(pitch); + T tmp_cb = glm::cos(roll); + T tmp_sb = glm::sin(roll); + + mat<4, 4, T, defaultp> Result; + Result[0][0] = tmp_ch * tmp_cb + tmp_sh * tmp_sp * tmp_sb; + Result[0][1] = tmp_sb * tmp_cp; + Result[0][2] = -tmp_sh * tmp_cb + tmp_ch * tmp_sp * tmp_sb; + Result[0][3] = static_cast(0); + Result[1][0] = -tmp_ch * tmp_sb + tmp_sh * tmp_sp * tmp_cb; + Result[1][1] = tmp_cb * tmp_cp; + Result[1][2] = tmp_sb * tmp_sh + tmp_ch * tmp_sp * tmp_cb; + Result[1][3] = static_cast(0); + Result[2][0] = tmp_sh * tmp_cp; + Result[2][1] = -tmp_sp; + Result[2][2] = tmp_ch * tmp_cp; + Result[2][3] = static_cast(0); + Result[3][0] = static_cast(0); + Result[3][1] = static_cast(0); + Result[3][2] = static_cast(0); + Result[3][3] = static_cast(1); + return Result; + } + + template + GLM_FUNC_QUALIFIER mat<2, 2, T, defaultp> orientate2 + ( + T const& angle + ) + { + T c = glm::cos(angle); + T s = glm::sin(angle); + + mat<2, 2, T, defaultp> Result; + Result[0][0] = c; + Result[0][1] = s; + Result[1][0] = -s; + Result[1][1] = c; + return Result; + } + + template + GLM_FUNC_QUALIFIER mat<3, 3, T, defaultp> orientate3 + ( + T const& angle + ) + { + T c = glm::cos(angle); + T s = glm::sin(angle); + + mat<3, 3, T, defaultp> Result; + Result[0][0] = c; + Result[0][1] = s; + Result[0][2] = 0.0f; + Result[1][0] = -s; + Result[1][1] = c; + Result[1][2] = 0.0f; + Result[2][0] = 0.0f; + Result[2][1] = 0.0f; + Result[2][2] = 1.0f; + return Result; + } + + template + GLM_FUNC_QUALIFIER mat<3, 3, T, Q> orientate3 + ( + vec<3, T, Q> const& angles + ) + { + return mat<3, 3, T, Q>(yawPitchRoll(angles.z, angles.x, angles.y)); + } + + template + GLM_FUNC_QUALIFIER mat<4, 4, T, Q> orientate4 + ( + vec<3, T, Q> const& angles + ) + { + return yawPitchRoll(angles.z, angles.x, angles.y); + } + + template + GLM_FUNC_DECL void extractEulerAngleXYZ(mat<4, 4, T, defaultp> const& M, + T & t1, + T & t2, + T & t3) + { + T T1 = glm::atan2(M[2][1], M[2][2]); + T C2 = glm::sqrt(M[0][0]*M[0][0] + M[1][0]*M[1][0]); + T T2 = glm::atan2(-M[2][0], C2); + T S1 = glm::sin(T1); + T C1 = glm::cos(T1); + T T3 = glm::atan2(S1*M[0][2] - C1*M[0][1], C1*M[1][1] - S1*M[1][2 ]); + t1 = -T1; + t2 = -T2; + t3 = -T3; + } + + template + GLM_FUNC_QUALIFIER void extractEulerAngleYXZ(mat<4, 4, T, defaultp> const & M, + T & t1, + T & t2, + T & t3) + { + T T1 = glm::atan2(M[2][0], M[2][2]); + T C2 = glm::sqrt(M[0][1]*M[0][1] + M[1][1]*M[1][1]); + T T2 = glm::atan2(-M[2][1], C2); + T S1 = glm::sin(T1); + T C1 = glm::cos(T1); + T T3 = glm::atan2(S1*M[1][2] - C1*M[1][0], C1*M[0][0] - S1*M[0][2]); + t1 = T1; + t2 = T2; + t3 = T3; + } + + template + GLM_FUNC_QUALIFIER void extractEulerAngleXZX(mat<4, 4, T, defaultp> const & M, + T & t1, + T & t2, + T & t3) + { + T T1 = glm::atan2(M[0][2], M[0][1]); + T S2 = glm::sqrt(M[1][0]*M[1][0] + M[2][0]*M[2][0]); + T T2 = glm::atan2(S2, M[0][0]); + T S1 = glm::sin(T1); + T C1 = glm::cos(T1); + T T3 = glm::atan2(C1*M[1][2] - S1*M[1][1], C1*M[2][2] - S1*M[2][1]); + t1 = T1; + t2 = T2; + t3 = T3; + } + + template + GLM_FUNC_QUALIFIER void extractEulerAngleXYX(mat<4, 4, T, defaultp> const & M, + T & t1, + T & t2, + T & t3) + { + T T1 = glm::atan2(M[0][1], -M[0][2]); + T S2 = glm::sqrt(M[1][0]*M[1][0] + M[2][0]*M[2][0]); + T T2 = glm::atan2(S2, M[0][0]); + T S1 = glm::sin(T1); + T C1 = glm::cos(T1); + T T3 = glm::atan2(-C1*M[2][1] - S1*M[2][2], C1*M[1][1] + S1*M[1][2]); + t1 = T1; + t2 = T2; + t3 = T3; + } + + template + GLM_FUNC_QUALIFIER void extractEulerAngleYXY(mat<4, 4, T, defaultp> const & M, + T & t1, + T & t2, + T & t3) + { + T T1 = glm::atan2(M[1][0], M[1][2]); + T S2 = glm::sqrt(M[0][1]*M[0][1] + M[2][1]*M[2][1]); + T T2 = glm::atan2(S2, M[1][1]); + T S1 = glm::sin(T1); + T C1 = glm::cos(T1); + T T3 = glm::atan2(C1*M[2][0] - S1*M[2][2], C1*M[0][0] - S1*M[0][2]); + t1 = T1; + t2 = T2; + t3 = T3; + } + + template + GLM_FUNC_QUALIFIER void extractEulerAngleYZY(mat<4, 4, T, defaultp> const & M, + T & t1, + T & t2, + T & t3) + { + T T1 = glm::atan2(M[1][2], -M[1][0]); + T S2 = glm::sqrt(M[0][1]*M[0][1] + M[2][1]*M[2][1]); + T T2 = glm::atan2(S2, M[1][1]); + T S1 = glm::sin(T1); + T C1 = glm::cos(T1); + T T3 = glm::atan2(-S1*M[0][0] - C1*M[0][2], S1*M[2][0] + C1*M[2][2]); + t1 = T1; + t2 = T2; + t3 = T3; + } + + template + GLM_FUNC_QUALIFIER void extractEulerAngleZYZ(mat<4, 4, T, defaultp> const & M, + T & t1, + T & t2, + T & t3) + { + T T1 = glm::atan2(M[2][1], M[2][0]); + T S2 = glm::sqrt(M[0][2]*M[0][2] + M[1][2]*M[1][2]); + T T2 = glm::atan2(S2, M[2][2]); + T S1 = glm::sin(T1); + T C1 = glm::cos(T1); + T T3 = glm::atan2(C1*M[0][1] - S1*M[0][0], C1*M[1][1] - S1*M[1][0]); + t1 = T1; + t2 = T2; + t3 = T3; + } + + template + GLM_FUNC_QUALIFIER void extractEulerAngleZXZ(mat<4, 4, T, defaultp> const & M, + T & t1, + T & t2, + T & t3) + { + T T1 = glm::atan2(M[2][0], -M[2][1]); + T S2 = glm::sqrt(M[0][2]*M[0][2] + M[1][2]*M[1][2]); + T T2 = glm::atan2(S2, M[2][2]); + T S1 = glm::sin(T1); + T C1 = glm::cos(T1); + T T3 = glm::atan2(-C1*M[1][0] - S1*M[1][1], C1*M[0][0] + S1*M[0][1]); + t1 = T1; + t2 = T2; + t3 = T3; + } + + template + GLM_FUNC_QUALIFIER void extractEulerAngleXZY(mat<4, 4, T, defaultp> const & M, + T & t1, + T & t2, + T & t3) + { + T T1 = glm::atan2(M[1][2], M[1][1]); + T C2 = glm::sqrt(M[0][0]*M[0][0] + M[2][0]*M[2][0]); + T T2 = glm::atan2(-M[1][0], C2); + T S1 = glm::sin(T1); + T C1 = glm::cos(T1); + T T3 = glm::atan2(S1*M[0][1] - C1*M[0][2], C1*M[2][2] - S1*M[2][1]); + t1 = T1; + t2 = T2; + t3 = T3; + } + + template + GLM_FUNC_QUALIFIER void extractEulerAngleYZX(mat<4, 4, T, defaultp> const & M, + T & t1, + T & t2, + T & t3) + { + T T1 = glm::atan2(-M[0][2], M[0][0]); + T C2 = glm::sqrt(M[1][1]*M[1][1] + M[2][1]*M[2][1]); + T T2 = glm::atan2(M[0][1], C2); + T S1 = glm::sin(T1); + T C1 = glm::cos(T1); + T T3 = glm::atan2(S1*M[1][0] + C1*M[1][2], S1*M[2][0] + C1*M[2][2]); + t1 = T1; + t2 = T2; + t3 = T3; + } + + template + GLM_FUNC_QUALIFIER void extractEulerAngleZYX(mat<4, 4, T, defaultp> const & M, + T & t1, + T & t2, + T & t3) + { + T T1 = glm::atan2(M[0][1], M[0][0]); + T C2 = glm::sqrt(M[1][2]*M[1][2] + M[2][2]*M[2][2]); + T T2 = glm::atan2(-M[0][2], C2); + T S1 = glm::sin(T1); + T C1 = glm::cos(T1); + T T3 = glm::atan2(S1*M[2][0] - C1*M[2][1], C1*M[1][1] - S1*M[1][0]); + t1 = T1; + t2 = T2; + t3 = T3; + } + + template + GLM_FUNC_QUALIFIER void extractEulerAngleZXY(mat<4, 4, T, defaultp> const & M, + T & t1, + T & t2, + T & t3) + { + T T1 = glm::atan2(-M[1][0], M[1][1]); + T C2 = glm::sqrt(M[0][2]*M[0][2] + M[2][2]*M[2][2]); + T T2 = glm::atan2(M[1][2], C2); + T S1 = glm::sin(T1); + T C1 = glm::cos(T1); + T T3 = glm::atan2(C1*M[2][0] + S1*M[2][1], C1*M[0][0] + S1*M[0][1]); + t1 = T1; + t2 = T2; + t3 = T3; + } +}//namespace glm diff --git a/src/GLMath/glm/gtx/extend.hpp b/src/GLMath/glm/gtx/extend.hpp new file mode 100644 index 000000000..28b7c5c01 --- /dev/null +++ b/src/GLMath/glm/gtx/extend.hpp @@ -0,0 +1,42 @@ +/// @ref gtx_extend +/// @file glm/gtx/extend.hpp +/// +/// @see core (dependence) +/// +/// @defgroup gtx_extend GLM_GTX_extend +/// @ingroup gtx +/// +/// Include to use the features of this extension. +/// +/// Extend a position from a source to a position at a defined length. + +#pragma once + +// Dependency: +#include "../glm.hpp" + +#if GLM_MESSAGES == GLM_ENABLE && !defined(GLM_EXT_INCLUDED) +# ifndef GLM_ENABLE_EXPERIMENTAL +# pragma message("GLM: GLM_GTX_extend is an experimental extension and may change in the future. Use #define GLM_ENABLE_EXPERIMENTAL before including it, if you really want to use it.") +# else +# pragma message("GLM: GLM_GTX_extend extension included") +# endif +#endif + +namespace glm +{ + /// @addtogroup gtx_extend + /// @{ + + /// Extends of Length the Origin position using the (Source - Origin) direction. + /// @see gtx_extend + template + GLM_FUNC_DECL genType extend( + genType const& Origin, + genType const& Source, + typename genType::value_type const Length); + + /// @} +}//namespace glm + +#include "extend.inl" diff --git a/src/GLMath/glm/gtx/extend.inl b/src/GLMath/glm/gtx/extend.inl new file mode 100644 index 000000000..32128eb20 --- /dev/null +++ b/src/GLMath/glm/gtx/extend.inl @@ -0,0 +1,48 @@ +/// @ref gtx_extend + +namespace glm +{ + template + GLM_FUNC_QUALIFIER genType extend + ( + genType const& Origin, + genType const& Source, + genType const& Distance + ) + { + return Origin + (Source - Origin) * Distance; + } + + template + GLM_FUNC_QUALIFIER vec<2, T, Q> extend + ( + vec<2, T, Q> const& Origin, + vec<2, T, Q> const& Source, + T const& Distance + ) + { + return Origin + (Source - Origin) * Distance; + } + + template + GLM_FUNC_QUALIFIER vec<3, T, Q> extend + ( + vec<3, T, Q> const& Origin, + vec<3, T, Q> const& Source, + T const& Distance + ) + { + return Origin + (Source - Origin) * Distance; + } + + template + GLM_FUNC_QUALIFIER vec<4, T, Q> extend + ( + vec<4, T, Q> const& Origin, + vec<4, T, Q> const& Source, + T const& Distance + ) + { + return Origin + (Source - Origin) * Distance; + } +}//namespace glm diff --git a/src/GLMath/glm/gtx/extended_min_max.hpp b/src/GLMath/glm/gtx/extended_min_max.hpp new file mode 100644 index 000000000..ad23a91dc --- /dev/null +++ b/src/GLMath/glm/gtx/extended_min_max.hpp @@ -0,0 +1,182 @@ +/// @ref gtx_extended_min_max +/// @file glm/gtx/extended_min_max.hpp +/// +/// @see core (dependence) +/// +/// @defgroup gtx_extended_min_max GLM_GTX_extented_min_max +/// @ingroup gtx +/// +/// Include to use the features of this extension. +/// +/// Min and max functions for 3 to 4 parameters. + +#pragma once + +// Dependency: +#include "../glm.hpp" + +#if GLM_MESSAGES == GLM_ENABLE && !defined(GLM_EXT_INCLUDED) +# ifndef GLM_ENABLE_EXPERIMENTAL +# pragma message("GLM: GLM_GTX_extented_min_max is an experimental extension and may change in the future. Use #define GLM_ENABLE_EXPERIMENTAL before including it, if you really want to use it.") +# else +# pragma message("GLM: GLM_GTX_extented_min_max extension included") +# endif +#endif + +namespace glm +{ + /// @addtogroup gtx_extended_min_max + /// @{ + + /// Return the minimum component-wise values of 3 inputs + /// @see gtx_extented_min_max + template + GLM_FUNC_DECL T min( + T const& x, + T const& y, + T const& z); + + /// Return the minimum component-wise values of 3 inputs + /// @see gtx_extented_min_max + template class C> + GLM_FUNC_DECL C min( + C const& x, + typename C::T const& y, + typename C::T const& z); + + /// Return the minimum component-wise values of 3 inputs + /// @see gtx_extented_min_max + template class C> + GLM_FUNC_DECL C min( + C const& x, + C const& y, + C const& z); + + /// Return the minimum component-wise values of 4 inputs + /// @see gtx_extented_min_max + template + GLM_FUNC_DECL T min( + T const& x, + T const& y, + T const& z, + T const& w); + + /// Return the minimum component-wise values of 4 inputs + /// @see gtx_extented_min_max + template class C> + GLM_FUNC_DECL C min( + C const& x, + typename C::T const& y, + typename C::T const& z, + typename C::T const& w); + + /// Return the minimum component-wise values of 4 inputs + /// @see gtx_extented_min_max + template class C> + GLM_FUNC_DECL C min( + C const& x, + C const& y, + C const& z, + C const& w); + + /// Return the maximum component-wise values of 3 inputs + /// @see gtx_extented_min_max + template + GLM_FUNC_DECL T max( + T const& x, + T const& y, + T const& z); + + /// Return the maximum component-wise values of 3 inputs + /// @see gtx_extented_min_max + template class C> + GLM_FUNC_DECL C max( + C const& x, + typename C::T const& y, + typename C::T const& z); + + /// Return the maximum component-wise values of 3 inputs + /// @see gtx_extented_min_max + template class C> + GLM_FUNC_DECL C max( + C const& x, + C const& y, + C const& z); + + /// Return the maximum component-wise values of 4 inputs + /// @see gtx_extented_min_max + template + GLM_FUNC_DECL T max( + T const& x, + T const& y, + T const& z, + T const& w); + + /// Return the maximum component-wise values of 4 inputs + /// @see gtx_extented_min_max + template class C> + GLM_FUNC_DECL C max( + C const& x, + typename C::T const& y, + typename C::T const& z, + typename C::T const& w); + + /// Return the maximum component-wise values of 4 inputs + /// @see gtx_extented_min_max + template class C> + GLM_FUNC_DECL C max( + C const& x, + C const& y, + C const& z, + C const& w); + + /// Returns y if y < x; otherwise, it returns x. If one of the two arguments is NaN, the value of the other argument is returned. + /// + /// @tparam genType Floating-point or integer; scalar or vector types. + /// + /// @see gtx_extented_min_max + template + GLM_FUNC_DECL genType fmin(genType x, genType y); + + /// Returns y if x < y; otherwise, it returns x. If one of the two arguments is NaN, the value of the other argument is returned. + /// + /// @tparam genType Floating-point; scalar or vector types. + /// + /// @see gtx_extented_min_max + /// @see std::fmax documentation + template + GLM_FUNC_DECL genType fmax(genType x, genType y); + + /// Returns min(max(x, minVal), maxVal) for each component in x. If one of the two arguments is NaN, the value of the other argument is returned. + /// + /// @tparam genType Floating-point scalar or vector types. + /// + /// @see gtx_extented_min_max + template + GLM_FUNC_DECL genType fclamp(genType x, genType minVal, genType maxVal); + + /// Returns min(max(x, minVal), maxVal) for each component in x. If one of the two arguments is NaN, the value of the other argument is returned. + /// + /// @tparam L Integer between 1 and 4 included that qualify the dimension of the vector + /// @tparam T Floating-point scalar types + /// @tparam Q Value from qualifier enum + /// + /// @see gtx_extented_min_max + template + GLM_FUNC_DECL vec fclamp(vec const& x, T minVal, T maxVal); + + /// Returns min(max(x, minVal), maxVal) for each component in x. If one of the two arguments is NaN, the value of the other argument is returned. + /// + /// @tparam L Integer between 1 and 4 included that qualify the dimension of the vector + /// @tparam T Floating-point scalar types + /// @tparam Q Value from qualifier enum + /// + /// @see gtx_extented_min_max + template + GLM_FUNC_DECL vec fclamp(vec const& x, vec const& minVal, vec const& maxVal); + + + /// @} +}//namespace glm + +#include "extended_min_max.inl" diff --git a/src/GLMath/glm/gtx/extended_min_max.inl b/src/GLMath/glm/gtx/extended_min_max.inl new file mode 100644 index 000000000..e72d1ccf1 --- /dev/null +++ b/src/GLMath/glm/gtx/extended_min_max.inl @@ -0,0 +1,218 @@ +/// @ref gtx_extended_min_max + +namespace glm +{ + template + GLM_FUNC_QUALIFIER T min( + T const& x, + T const& y, + T const& z) + { + return glm::min(glm::min(x, y), z); + } + + template class C> + GLM_FUNC_QUALIFIER C min + ( + C const& x, + typename C::T const& y, + typename C::T const& z + ) + { + return glm::min(glm::min(x, y), z); + } + + template class C> + GLM_FUNC_QUALIFIER C min + ( + C const& x, + C const& y, + C const& z + ) + { + return glm::min(glm::min(x, y), z); + } + + template + GLM_FUNC_QUALIFIER T min + ( + T const& x, + T const& y, + T const& z, + T const& w + ) + { + return glm::min(glm::min(x, y), glm::min(z, w)); + } + + template class C> + GLM_FUNC_QUALIFIER C min + ( + C const& x, + typename C::T const& y, + typename C::T const& z, + typename C::T const& w + ) + { + return glm::min(glm::min(x, y), glm::min(z, w)); + } + + template class C> + GLM_FUNC_QUALIFIER C min + ( + C const& x, + C const& y, + C const& z, + C const& w + ) + { + return glm::min(glm::min(x, y), glm::min(z, w)); + } + + template + GLM_FUNC_QUALIFIER T max( + T const& x, + T const& y, + T const& z) + { + return glm::max(glm::max(x, y), z); + } + + template class C> + GLM_FUNC_QUALIFIER C max + ( + C const& x, + typename C::T const& y, + typename C::T const& z + ) + { + return glm::max(glm::max(x, y), z); + } + + template class C> + GLM_FUNC_QUALIFIER C max + ( + C const& x, + C const& y, + C const& z + ) + { + return glm::max(glm::max(x, y), z); + } + + template + GLM_FUNC_QUALIFIER T max + ( + T const& x, + T const& y, + T const& z, + T const& w + ) + { + return glm::max(glm::max(x, y), glm::max(z, w)); + } + + template class C> + GLM_FUNC_QUALIFIER C max + ( + C const& x, + typename C::T const& y, + typename C::T const& z, + typename C::T const& w + ) + { + return glm::max(glm::max(x, y), glm::max(z, w)); + } + + template class C> + GLM_FUNC_QUALIFIER C max + ( + C const& x, + C const& y, + C const& z, + C const& w + ) + { + return glm::max(glm::max(x, y), glm::max(z, w)); + } + + // fmin +# if GLM_HAS_CXX11_STL + using std::fmin; +# else + template + GLM_FUNC_QUALIFIER genType fmin(genType x, genType y) + { + GLM_STATIC_ASSERT(std::numeric_limits::is_iec559, "'fmin' only accept floating-point input"); + + if (isnan(x)) + return y; + if (isnan(y)) + return x; + + return min(x, y); + } +# endif + + template + GLM_FUNC_QUALIFIER vec fmin(vec const& a, T b) + { + return detail::functor2::call(fmin, a, vec(b)); + } + + template + GLM_FUNC_QUALIFIER vec fmin(vec const& a, vec const& b) + { + return detail::functor2::call(fmin, a, b); + } + + // fmax +# if GLM_HAS_CXX11_STL + using std::fmax; +# else + template + GLM_FUNC_QUALIFIER genType fmax(genType x, genType y) + { + GLM_STATIC_ASSERT(std::numeric_limits::is_iec559, "'fmax' only accept floating-point input"); + + if (isnan(x)) + return y; + if (isnan(y)) + return x; + + return max(x, y); + } +# endif + + template + GLM_FUNC_QUALIFIER vec fmax(vec const& a, T b) + { + return detail::functor2::call(fmax, a, vec(b)); + } + + template + GLM_FUNC_QUALIFIER vec fmax(vec const& a, vec const& b) + { + return detail::functor2::call(fmax, a, b); + } + + // fclamp + template + GLM_FUNC_QUALIFIER genType fclamp(genType x, genType minVal, genType maxVal) + { + GLM_STATIC_ASSERT(std::numeric_limits::is_iec559, "'fclamp' only accept floating-point or integer inputs"); + return fmin(fmax(x, minVal), maxVal); + } + + template + GLM_FUNC_QUALIFIER vec fclamp(vec const& x, T minVal, T maxVal) + { + return fmin(fmax(x, vec(minVal)), vec(maxVal)); + } + + template + GLM_FUNC_QUALIFIER vec fclamp(vec const& x, vec const& minVal, vec const& maxVal) + { + return fmin(fmax(x, minVal), maxVal); + } +}//namespace glm diff --git a/src/GLMath/glm/gtx/exterior_product.hpp b/src/GLMath/glm/gtx/exterior_product.hpp new file mode 100644 index 000000000..5522df788 --- /dev/null +++ b/src/GLMath/glm/gtx/exterior_product.hpp @@ -0,0 +1,45 @@ +/// @ref gtx_exterior_product +/// @file glm/gtx/exterior_product.hpp +/// +/// @see core (dependence) +/// @see gtx_exterior_product (dependence) +/// +/// @defgroup gtx_exterior_product GLM_GTX_exterior_product +/// @ingroup gtx +/// +/// Include to use the features of this extension. +/// +/// @brief Allow to perform bit operations on integer values + +#pragma once + +// Dependencies +#include "../detail/setup.hpp" +#include "../detail/qualifier.hpp" + +#if GLM_MESSAGES == GLM_ENABLE && !defined(GLM_EXT_INCLUDED) +# ifndef GLM_ENABLE_EXPERIMENTAL +# pragma message("GLM: GLM_GTX_exterior_product is an experimental extension and may change in the future. Use #define GLM_ENABLE_EXPERIMENTAL before including it, if you really want to use it.") +# else +# pragma message("GLM: GLM_GTX_exterior_product extension included") +# endif +#endif + +namespace glm +{ + /// @addtogroup gtx_exterior_product + /// @{ + + /// Returns the cross product of x and y. + /// + /// @tparam T Floating-point scalar types + /// @tparam Q Value from qualifier enum + /// + /// @see Exterior product + template + GLM_FUNC_DECL T cross(vec<2, T, Q> const& v, vec<2, T, Q> const& u); + + /// @} +} //namespace glm + +#include "exterior_product.inl" diff --git a/src/GLMath/glm/gtx/exterior_product.inl b/src/GLMath/glm/gtx/exterior_product.inl new file mode 100644 index 000000000..93661fd3f --- /dev/null +++ b/src/GLMath/glm/gtx/exterior_product.inl @@ -0,0 +1,26 @@ +/// @ref gtx_exterior_product + +#include + +namespace glm { +namespace detail +{ + template + struct compute_cross_vec2 + { + GLM_FUNC_QUALIFIER static T call(vec<2, T, Q> const& v, vec<2, T, Q> const& u) + { + GLM_STATIC_ASSERT(std::numeric_limits::is_iec559, "'cross' accepts only floating-point inputs"); + + return v.x * u.y - u.x * v.y; + } + }; +}//namespace detail + + template + GLM_FUNC_QUALIFIER T cross(vec<2, T, Q> const& x, vec<2, T, Q> const& y) + { + return detail::compute_cross_vec2::value>::call(x, y); + } +}//namespace glm + diff --git a/src/GLMath/glm/gtx/fast_exponential.hpp b/src/GLMath/glm/gtx/fast_exponential.hpp new file mode 100644 index 000000000..6fb728652 --- /dev/null +++ b/src/GLMath/glm/gtx/fast_exponential.hpp @@ -0,0 +1,95 @@ +/// @ref gtx_fast_exponential +/// @file glm/gtx/fast_exponential.hpp +/// +/// @see core (dependence) +/// @see gtx_half_float (dependence) +/// +/// @defgroup gtx_fast_exponential GLM_GTX_fast_exponential +/// @ingroup gtx +/// +/// Include to use the features of this extension. +/// +/// Fast but less accurate implementations of exponential based functions. + +#pragma once + +// Dependency: +#include "../glm.hpp" + +#if GLM_MESSAGES == GLM_ENABLE && !defined(GLM_EXT_INCLUDED) +# ifndef GLM_ENABLE_EXPERIMENTAL +# pragma message("GLM: GLM_GTX_fast_exponential is an experimental extension and may change in the future. Use #define GLM_ENABLE_EXPERIMENTAL before including it, if you really want to use it.") +# else +# pragma message("GLM: GLM_GTX_fast_exponential extension included") +# endif +#endif + +namespace glm +{ + /// @addtogroup gtx_fast_exponential + /// @{ + + /// Faster than the common pow function but less accurate. + /// @see gtx_fast_exponential + template + GLM_FUNC_DECL genType fastPow(genType x, genType y); + + /// Faster than the common pow function but less accurate. + /// @see gtx_fast_exponential + template + GLM_FUNC_DECL vec fastPow(vec const& x, vec const& y); + + /// Faster than the common pow function but less accurate. + /// @see gtx_fast_exponential + template + GLM_FUNC_DECL genTypeT fastPow(genTypeT x, genTypeU y); + + /// Faster than the common pow function but less accurate. + /// @see gtx_fast_exponential + template + GLM_FUNC_DECL vec fastPow(vec const& x); + + /// Faster than the common exp function but less accurate. + /// @see gtx_fast_exponential + template + GLM_FUNC_DECL T fastExp(T x); + + /// Faster than the common exp function but less accurate. + /// @see gtx_fast_exponential + template + GLM_FUNC_DECL vec fastExp(vec const& x); + + /// Faster than the common log function but less accurate. + /// @see gtx_fast_exponential + template + GLM_FUNC_DECL T fastLog(T x); + + /// Faster than the common exp2 function but less accurate. + /// @see gtx_fast_exponential + template + GLM_FUNC_DECL vec fastLog(vec const& x); + + /// Faster than the common exp2 function but less accurate. + /// @see gtx_fast_exponential + template + GLM_FUNC_DECL T fastExp2(T x); + + /// Faster than the common exp2 function but less accurate. + /// @see gtx_fast_exponential + template + GLM_FUNC_DECL vec fastExp2(vec const& x); + + /// Faster than the common log2 function but less accurate. + /// @see gtx_fast_exponential + template + GLM_FUNC_DECL T fastLog2(T x); + + /// Faster than the common log2 function but less accurate. + /// @see gtx_fast_exponential + template + GLM_FUNC_DECL vec fastLog2(vec const& x); + + /// @} +}//namespace glm + +#include "fast_exponential.inl" diff --git a/src/GLMath/glm/gtx/fast_exponential.inl b/src/GLMath/glm/gtx/fast_exponential.inl new file mode 100644 index 000000000..f139e5056 --- /dev/null +++ b/src/GLMath/glm/gtx/fast_exponential.inl @@ -0,0 +1,136 @@ +/// @ref gtx_fast_exponential + +namespace glm +{ + // fastPow: + template + GLM_FUNC_QUALIFIER genType fastPow(genType x, genType y) + { + return exp(y * log(x)); + } + + template + GLM_FUNC_QUALIFIER vec fastPow(vec const& x, vec const& y) + { + return exp(y * log(x)); + } + + template + GLM_FUNC_QUALIFIER T fastPow(T x, int y) + { + T f = static_cast(1); + for(int i = 0; i < y; ++i) + f *= x; + return f; + } + + template + GLM_FUNC_QUALIFIER vec fastPow(vec const& x, vec const& y) + { + vec Result; + for(length_t i = 0, n = x.length(); i < n; ++i) + Result[i] = fastPow(x[i], y[i]); + return Result; + } + + // fastExp + // Note: This function provides accurate results only for value between -1 and 1, else avoid it. + template + GLM_FUNC_QUALIFIER T fastExp(T x) + { + // This has a better looking and same performance in release mode than the following code. However, in debug mode it's slower. + // return 1.0f + x * (1.0f + x * 0.5f * (1.0f + x * 0.3333333333f * (1.0f + x * 0.25 * (1.0f + x * 0.2f)))); + T x2 = x * x; + T x3 = x2 * x; + T x4 = x3 * x; + T x5 = x4 * x; + return T(1) + x + (x2 * T(0.5)) + (x3 * T(0.1666666667)) + (x4 * T(0.041666667)) + (x5 * T(0.008333333333)); + } + /* // Try to handle all values of float... but often shower than std::exp, glm::floor and the loop kill the performance + GLM_FUNC_QUALIFIER float fastExp(float x) + { + const float e = 2.718281828f; + const float IntegerPart = floor(x); + const float FloatPart = x - IntegerPart; + float z = 1.f; + + for(int i = 0; i < int(IntegerPart); ++i) + z *= e; + + const float x2 = FloatPart * FloatPart; + const float x3 = x2 * FloatPart; + const float x4 = x3 * FloatPart; + const float x5 = x4 * FloatPart; + return z * (1.0f + FloatPart + (x2 * 0.5f) + (x3 * 0.1666666667f) + (x4 * 0.041666667f) + (x5 * 0.008333333333f)); + } + + // Increase accuracy on number bigger that 1 and smaller than -1 but it's not enough for high and negative numbers + GLM_FUNC_QUALIFIER float fastExp(float x) + { + // This has a better looking and same performance in release mode than the following code. However, in debug mode it's slower. + // return 1.0f + x * (1.0f + x * 0.5f * (1.0f + x * 0.3333333333f * (1.0f + x * 0.25 * (1.0f + x * 0.2f)))); + float x2 = x * x; + float x3 = x2 * x; + float x4 = x3 * x; + float x5 = x4 * x; + float x6 = x5 * x; + float x7 = x6 * x; + float x8 = x7 * x; + return 1.0f + x + (x2 * 0.5f) + (x3 * 0.1666666667f) + (x4 * 0.041666667f) + (x5 * 0.008333333333f)+ (x6 * 0.00138888888888f) + (x7 * 0.000198412698f) + (x8 * 0.0000248015873f);; + } + */ + + template + GLM_FUNC_QUALIFIER vec fastExp(vec const& x) + { + return detail::functor1::call(fastExp, x); + } + + // fastLog + template + GLM_FUNC_QUALIFIER genType fastLog(genType x) + { + return std::log(x); + } + + /* Slower than the VC7.1 function... + GLM_FUNC_QUALIFIER float fastLog(float x) + { + float y1 = (x - 1.0f) / (x + 1.0f); + float y2 = y1 * y1; + return 2.0f * y1 * (1.0f + y2 * (0.3333333333f + y2 * (0.2f + y2 * 0.1428571429f))); + } + */ + + template + GLM_FUNC_QUALIFIER vec fastLog(vec const& x) + { + return detail::functor1::call(fastLog, x); + } + + //fastExp2, ln2 = 0.69314718055994530941723212145818f + template + GLM_FUNC_QUALIFIER genType fastExp2(genType x) + { + return fastExp(0.69314718055994530941723212145818f * x); + } + + template + GLM_FUNC_QUALIFIER vec fastExp2(vec const& x) + { + return detail::functor1::call(fastExp2, x); + } + + // fastLog2, ln2 = 0.69314718055994530941723212145818f + template + GLM_FUNC_QUALIFIER genType fastLog2(genType x) + { + return fastLog(x) / 0.69314718055994530941723212145818f; + } + + template + GLM_FUNC_QUALIFIER vec fastLog2(vec const& x) + { + return detail::functor1::call(fastLog2, x); + } +}//namespace glm diff --git a/src/GLMath/glm/gtx/fast_square_root.hpp b/src/GLMath/glm/gtx/fast_square_root.hpp new file mode 100644 index 000000000..9fb3f2fce --- /dev/null +++ b/src/GLMath/glm/gtx/fast_square_root.hpp @@ -0,0 +1,92 @@ +/// @ref gtx_fast_square_root +/// @file glm/gtx/fast_square_root.hpp +/// +/// @see core (dependence) +/// +/// @defgroup gtx_fast_square_root GLM_GTX_fast_square_root +/// @ingroup gtx +/// +/// Include to use the features of this extension. +/// +/// Fast but less accurate implementations of square root based functions. +/// - Sqrt optimisation based on Newton's method, +/// www.gamedev.net/community/forums/topic.asp?topic id=139956 + +#pragma once + +// Dependency: +#include "../common.hpp" +#include "../exponential.hpp" +#include "../geometric.hpp" + +#if GLM_MESSAGES == GLM_ENABLE && !defined(GLM_EXT_INCLUDED) +# ifndef GLM_ENABLE_EXPERIMENTAL +# pragma message("GLM: GLM_GTX_fast_square_root is an experimental extension and may change in the future. Use #define GLM_ENABLE_EXPERIMENTAL before including it, if you really want to use it.") +# else +# pragma message("GLM: GLM_GTX_fast_square_root extension included") +# endif +#endif + +namespace glm +{ + /// @addtogroup gtx_fast_square_root + /// @{ + + /// Faster than the common sqrt function but less accurate. + /// + /// @see gtx_fast_square_root extension. + template + GLM_FUNC_DECL genType fastSqrt(genType x); + + /// Faster than the common sqrt function but less accurate. + /// + /// @see gtx_fast_square_root extension. + template + GLM_FUNC_DECL vec fastSqrt(vec const& x); + + /// Faster than the common inversesqrt function but less accurate. + /// + /// @see gtx_fast_square_root extension. + template + GLM_FUNC_DECL genType fastInverseSqrt(genType x); + + /// Faster than the common inversesqrt function but less accurate. + /// + /// @see gtx_fast_square_root extension. + template + GLM_FUNC_DECL vec fastInverseSqrt(vec const& x); + + /// Faster than the common length function but less accurate. + /// + /// @see gtx_fast_square_root extension. + template + GLM_FUNC_DECL genType fastLength(genType x); + + /// Faster than the common length function but less accurate. + /// + /// @see gtx_fast_square_root extension. + template + GLM_FUNC_DECL T fastLength(vec const& x); + + /// Faster than the common distance function but less accurate. + /// + /// @see gtx_fast_square_root extension. + template + GLM_FUNC_DECL genType fastDistance(genType x, genType y); + + /// Faster than the common distance function but less accurate. + /// + /// @see gtx_fast_square_root extension. + template + GLM_FUNC_DECL T fastDistance(vec const& x, vec const& y); + + /// Faster than the common normalize function but less accurate. + /// + /// @see gtx_fast_square_root extension. + template + GLM_FUNC_DECL genType fastNormalize(genType const& x); + + /// @} +}// namespace glm + +#include "fast_square_root.inl" diff --git a/src/GLMath/glm/gtx/fast_square_root.inl b/src/GLMath/glm/gtx/fast_square_root.inl new file mode 100644 index 000000000..0bc359002 --- /dev/null +++ b/src/GLMath/glm/gtx/fast_square_root.inl @@ -0,0 +1,80 @@ +/// @ref gtx_fast_square_root + +namespace glm +{ + // fastSqrt + template + GLM_FUNC_QUALIFIER genType fastSqrt(genType x) + { + GLM_STATIC_ASSERT(std::numeric_limits::is_iec559, "'fastSqrt' only accept floating-point input"); + + return genType(1) / fastInverseSqrt(x); + } + + template + GLM_FUNC_QUALIFIER vec fastSqrt(vec const& x) + { + return detail::functor1::call(fastSqrt, x); + } + + // fastInversesqrt + template + GLM_FUNC_QUALIFIER genType fastInverseSqrt(genType x) + { +# ifdef __CUDACC__ // Wordaround for a CUDA compiler bug up to CUDA6 + vec<1, T, Q> tmp(detail::compute_inversesqrt::value>::call(vec<1, genType, lowp>(x))); + return tmp.x; +# else + return detail::compute_inversesqrt<1, genType, lowp, detail::is_aligned::value>::call(vec<1, genType, lowp>(x)).x; +# endif + } + + template + GLM_FUNC_QUALIFIER vec fastInverseSqrt(vec const& x) + { + return detail::compute_inversesqrt::value>::call(x); + } + + // fastLength + template + GLM_FUNC_QUALIFIER genType fastLength(genType x) + { + GLM_STATIC_ASSERT(std::numeric_limits::is_iec559, "'fastLength' only accept floating-point inputs"); + + return abs(x); + } + + template + GLM_FUNC_QUALIFIER T fastLength(vec const& x) + { + GLM_STATIC_ASSERT(std::numeric_limits::is_iec559, "'fastLength' only accept floating-point inputs"); + + return fastSqrt(dot(x, x)); + } + + // fastDistance + template + GLM_FUNC_QUALIFIER genType fastDistance(genType x, genType y) + { + return fastLength(y - x); + } + + template + GLM_FUNC_QUALIFIER T fastDistance(vec const& x, vec const& y) + { + return fastLength(y - x); + } + + // fastNormalize + template + GLM_FUNC_QUALIFIER genType fastNormalize(genType x) + { + return x > genType(0) ? genType(1) : -genType(1); + } + + template + GLM_FUNC_QUALIFIER vec fastNormalize(vec const& x) + { + return x * fastInverseSqrt(dot(x, x)); + } +}//namespace glm diff --git a/src/GLMath/glm/gtx/fast_trigonometry.hpp b/src/GLMath/glm/gtx/fast_trigonometry.hpp new file mode 100644 index 000000000..2650d6e4d --- /dev/null +++ b/src/GLMath/glm/gtx/fast_trigonometry.hpp @@ -0,0 +1,79 @@ +/// @ref gtx_fast_trigonometry +/// @file glm/gtx/fast_trigonometry.hpp +/// +/// @see core (dependence) +/// +/// @defgroup gtx_fast_trigonometry GLM_GTX_fast_trigonometry +/// @ingroup gtx +/// +/// Include to use the features of this extension. +/// +/// Fast but less accurate implementations of trigonometric functions. + +#pragma once + +// Dependency: +#include "../gtc/constants.hpp" + +#if GLM_MESSAGES == GLM_ENABLE && !defined(GLM_EXT_INCLUDED) +# ifndef GLM_ENABLE_EXPERIMENTAL +# pragma message("GLM: GLM_GTX_fast_trigonometry is an experimental extension and may change in the future. Use #define GLM_ENABLE_EXPERIMENTAL before including it, if you really want to use it.") +# else +# pragma message("GLM: GLM_GTX_fast_trigonometry extension included") +# endif +#endif + +namespace glm +{ + /// @addtogroup gtx_fast_trigonometry + /// @{ + + /// Wrap an angle to [0 2pi[ + /// From GLM_GTX_fast_trigonometry extension. + template + GLM_FUNC_DECL T wrapAngle(T angle); + + /// Faster than the common sin function but less accurate. + /// From GLM_GTX_fast_trigonometry extension. + template + GLM_FUNC_DECL T fastSin(T angle); + + /// Faster than the common cos function but less accurate. + /// From GLM_GTX_fast_trigonometry extension. + template + GLM_FUNC_DECL T fastCos(T angle); + + /// Faster than the common tan function but less accurate. + /// Defined between -2pi and 2pi. + /// From GLM_GTX_fast_trigonometry extension. + template + GLM_FUNC_DECL T fastTan(T angle); + + /// Faster than the common asin function but less accurate. + /// Defined between -2pi and 2pi. + /// From GLM_GTX_fast_trigonometry extension. + template + GLM_FUNC_DECL T fastAsin(T angle); + + /// Faster than the common acos function but less accurate. + /// Defined between -2pi and 2pi. + /// From GLM_GTX_fast_trigonometry extension. + template + GLM_FUNC_DECL T fastAcos(T angle); + + /// Faster than the common atan function but less accurate. + /// Defined between -2pi and 2pi. + /// From GLM_GTX_fast_trigonometry extension. + template + GLM_FUNC_DECL T fastAtan(T y, T x); + + /// Faster than the common atan function but less accurate. + /// Defined between -2pi and 2pi. + /// From GLM_GTX_fast_trigonometry extension. + template + GLM_FUNC_DECL T fastAtan(T angle); + + /// @} +}//namespace glm + +#include "fast_trigonometry.inl" diff --git a/src/GLMath/glm/gtx/fast_trigonometry.inl b/src/GLMath/glm/gtx/fast_trigonometry.inl new file mode 100644 index 000000000..1a710cbcd --- /dev/null +++ b/src/GLMath/glm/gtx/fast_trigonometry.inl @@ -0,0 +1,142 @@ +/// @ref gtx_fast_trigonometry + +namespace glm{ +namespace detail +{ + template + GLM_FUNC_QUALIFIER vec taylorCos(vec const& x) + { + return static_cast(1) + - (x * x) * (1.f / 2.f) + + ((x * x) * (x * x)) * (1.f / 24.f) + - (((x * x) * (x * x)) * (x * x)) * (1.f / 720.f) + + (((x * x) * (x * x)) * ((x * x) * (x * x))) * (1.f / 40320.f); + } + + template + GLM_FUNC_QUALIFIER T cos_52s(T x) + { + T const xx(x * x); + return (T(0.9999932946) + xx * (T(-0.4999124376) + xx * (T(0.0414877472) + xx * T(-0.0012712095)))); + } + + template + GLM_FUNC_QUALIFIER vec cos_52s(vec const& x) + { + return detail::functor1::call(cos_52s, x); + } +}//namespace detail + + // wrapAngle + template + GLM_FUNC_QUALIFIER T wrapAngle(T angle) + { + return abs(mod(angle, two_pi())); + } + + template + GLM_FUNC_QUALIFIER vec wrapAngle(vec const& x) + { + return detail::functor1::call(wrapAngle, x); + } + + // cos + template + GLM_FUNC_QUALIFIER T fastCos(T x) + { + T const angle(wrapAngle(x)); + + if(angle < half_pi()) + return detail::cos_52s(angle); + if(angle < pi()) + return -detail::cos_52s(pi() - angle); + if(angle < (T(3) * half_pi())) + return -detail::cos_52s(angle - pi()); + + return detail::cos_52s(two_pi() - angle); + } + + template + GLM_FUNC_QUALIFIER vec fastCos(vec const& x) + { + return detail::functor1::call(fastCos, x); + } + + // sin + template + GLM_FUNC_QUALIFIER T fastSin(T x) + { + return fastCos(half_pi() - x); + } + + template + GLM_FUNC_QUALIFIER vec fastSin(vec const& x) + { + return detail::functor1::call(fastSin, x); + } + + // tan + template + GLM_FUNC_QUALIFIER T fastTan(T x) + { + return x + (x * x * x * T(0.3333333333)) + (x * x * x * x * x * T(0.1333333333333)) + (x * x * x * x * x * x * x * T(0.0539682539)); + } + + template + GLM_FUNC_QUALIFIER vec fastTan(vec const& x) + { + return detail::functor1::call(fastTan, x); + } + + // asin + template + GLM_FUNC_QUALIFIER T fastAsin(T x) + { + return x + (x * x * x * T(0.166666667)) + (x * x * x * x * x * T(0.075)) + (x * x * x * x * x * x * x * T(0.0446428571)) + (x * x * x * x * x * x * x * x * x * T(0.0303819444));// + (x * x * x * x * x * x * x * x * x * x * x * T(0.022372159)); + } + + template + GLM_FUNC_QUALIFIER vec fastAsin(vec const& x) + { + return detail::functor1::call(fastAsin, x); + } + + // acos + template + GLM_FUNC_QUALIFIER T fastAcos(T x) + { + return T(1.5707963267948966192313216916398) - fastAsin(x); //(PI / 2) + } + + template + GLM_FUNC_QUALIFIER vec fastAcos(vec const& x) + { + return detail::functor1::call(fastAcos, x); + } + + // atan + template + GLM_FUNC_QUALIFIER T fastAtan(T y, T x) + { + T sgn = sign(y) * sign(x); + return abs(fastAtan(y / x)) * sgn; + } + + template + GLM_FUNC_QUALIFIER vec fastAtan(vec const& y, vec const& x) + { + return detail::functor2::call(fastAtan, y, x); + } + + template + GLM_FUNC_QUALIFIER T fastAtan(T x) + { + return x - (x * x * x * T(0.333333333333)) + (x * x * x * x * x * T(0.2)) - (x * x * x * x * x * x * x * T(0.1428571429)) + (x * x * x * x * x * x * x * x * x * T(0.111111111111)) - (x * x * x * x * x * x * x * x * x * x * x * T(0.0909090909)); + } + + template + GLM_FUNC_QUALIFIER vec fastAtan(vec const& x) + { + return detail::functor1::call(fastAtan, x); + } +}//namespace glm diff --git a/src/GLMath/glm/gtx/float_notmalize.inl b/src/GLMath/glm/gtx/float_notmalize.inl new file mode 100644 index 000000000..8cdbc5aaa --- /dev/null +++ b/src/GLMath/glm/gtx/float_notmalize.inl @@ -0,0 +1,13 @@ +/// @ref gtx_float_normalize + +#include + +namespace glm +{ + template + GLM_FUNC_QUALIFIER vec floatNormalize(vec const& v) + { + return vec(v) / static_cast(std::numeric_limits::max()); + } + +}//namespace glm diff --git a/src/GLMath/glm/gtx/functions.hpp b/src/GLMath/glm/gtx/functions.hpp new file mode 100644 index 000000000..9f4166c4c --- /dev/null +++ b/src/GLMath/glm/gtx/functions.hpp @@ -0,0 +1,56 @@ +/// @ref gtx_functions +/// @file glm/gtx/functions.hpp +/// +/// @see core (dependence) +/// @see gtc_quaternion (dependence) +/// +/// @defgroup gtx_functions GLM_GTX_functions +/// @ingroup gtx +/// +/// Include to use the features of this extension. +/// +/// List of useful common functions. + +#pragma once + +// Dependencies +#include "../detail/setup.hpp" +#include "../detail/qualifier.hpp" +#include "../detail/type_vec2.hpp" + +#if GLM_MESSAGES == GLM_ENABLE && !defined(GLM_EXT_INCLUDED) +# ifndef GLM_ENABLE_EXPERIMENTAL +# pragma message("GLM: GLM_GTX_functions is an experimental extension and may change in the future. Use #define GLM_ENABLE_EXPERIMENTAL before including it, if you really want to use it.") +# else +# pragma message("GLM: GLM_GTX_functions extension included") +# endif +#endif + +namespace glm +{ + /// @addtogroup gtx_functions + /// @{ + + /// 1D gauss function + /// + /// @see gtc_epsilon + template + GLM_FUNC_DECL T gauss( + T x, + T ExpectedValue, + T StandardDeviation); + + /// 2D gauss function + /// + /// @see gtc_epsilon + template + GLM_FUNC_DECL T gauss( + vec<2, T, Q> const& Coord, + vec<2, T, Q> const& ExpectedValue, + vec<2, T, Q> const& StandardDeviation); + + /// @} +}//namespace glm + +#include "functions.inl" + diff --git a/src/GLMath/glm/gtx/functions.inl b/src/GLMath/glm/gtx/functions.inl new file mode 100644 index 000000000..29cbb20b8 --- /dev/null +++ b/src/GLMath/glm/gtx/functions.inl @@ -0,0 +1,30 @@ +/// @ref gtx_functions + +#include "../exponential.hpp" + +namespace glm +{ + template + GLM_FUNC_QUALIFIER T gauss + ( + T x, + T ExpectedValue, + T StandardDeviation + ) + { + return exp(-((x - ExpectedValue) * (x - ExpectedValue)) / (static_cast(2) * StandardDeviation * StandardDeviation)) / (StandardDeviation * sqrt(static_cast(6.28318530717958647692528676655900576))); + } + + template + GLM_FUNC_QUALIFIER T gauss + ( + vec<2, T, Q> const& Coord, + vec<2, T, Q> const& ExpectedValue, + vec<2, T, Q> const& StandardDeviation + ) + { + vec<2, T, Q> const Squared = ((Coord - ExpectedValue) * (Coord - ExpectedValue)) / (static_cast(2) * StandardDeviation * StandardDeviation); + return exp(-(Squared.x + Squared.y)); + } +}//namespace glm + diff --git a/src/GLMath/glm/gtx/gradient_paint.hpp b/src/GLMath/glm/gtx/gradient_paint.hpp new file mode 100644 index 000000000..6f85bf482 --- /dev/null +++ b/src/GLMath/glm/gtx/gradient_paint.hpp @@ -0,0 +1,53 @@ +/// @ref gtx_gradient_paint +/// @file glm/gtx/gradient_paint.hpp +/// +/// @see core (dependence) +/// @see gtx_optimum_pow (dependence) +/// +/// @defgroup gtx_gradient_paint GLM_GTX_gradient_paint +/// @ingroup gtx +/// +/// Include to use the features of this extension. +/// +/// Functions that return the color of procedural gradient for specific coordinates. + +#pragma once + +// Dependency: +#include "../glm.hpp" +#include "../gtx/optimum_pow.hpp" + +#if GLM_MESSAGES == GLM_ENABLE && !defined(GLM_EXT_INCLUDED) +# ifndef GLM_ENABLE_EXPERIMENTAL +# pragma message("GLM: GLM_GTX_gradient_paint is an experimental extension and may change in the future. Use #define GLM_ENABLE_EXPERIMENTAL before including it, if you really want to use it.") +# else +# pragma message("GLM: GLM_GTX_gradient_paint extension included") +# endif +#endif + +namespace glm +{ + /// @addtogroup gtx_gradient_paint + /// @{ + + /// Return a color from a radial gradient. + /// @see - gtx_gradient_paint + template + GLM_FUNC_DECL T radialGradient( + vec<2, T, Q> const& Center, + T const& Radius, + vec<2, T, Q> const& Focal, + vec<2, T, Q> const& Position); + + /// Return a color from a linear gradient. + /// @see - gtx_gradient_paint + template + GLM_FUNC_DECL T linearGradient( + vec<2, T, Q> const& Point0, + vec<2, T, Q> const& Point1, + vec<2, T, Q> const& Position); + + /// @} +}// namespace glm + +#include "gradient_paint.inl" diff --git a/src/GLMath/glm/gtx/gradient_paint.inl b/src/GLMath/glm/gtx/gradient_paint.inl new file mode 100644 index 000000000..4c495e62c --- /dev/null +++ b/src/GLMath/glm/gtx/gradient_paint.inl @@ -0,0 +1,36 @@ +/// @ref gtx_gradient_paint + +namespace glm +{ + template + GLM_FUNC_QUALIFIER T radialGradient + ( + vec<2, T, Q> const& Center, + T const& Radius, + vec<2, T, Q> const& Focal, + vec<2, T, Q> const& Position + ) + { + vec<2, T, Q> F = Focal - Center; + vec<2, T, Q> D = Position - Focal; + T Radius2 = pow2(Radius); + T Fx2 = pow2(F.x); + T Fy2 = pow2(F.y); + + T Numerator = (D.x * F.x + D.y * F.y) + sqrt(Radius2 * (pow2(D.x) + pow2(D.y)) - pow2(D.x * F.y - D.y * F.x)); + T Denominator = Radius2 - (Fx2 + Fy2); + return Numerator / Denominator; + } + + template + GLM_FUNC_QUALIFIER T linearGradient + ( + vec<2, T, Q> const& Point0, + vec<2, T, Q> const& Point1, + vec<2, T, Q> const& Position + ) + { + vec<2, T, Q> Dist = Point1 - Point0; + return (Dist.x * (Position.x - Point0.x) + Dist.y * (Position.y - Point0.y)) / glm::dot(Dist, Dist); + } +}//namespace glm diff --git a/src/GLMath/glm/gtx/handed_coordinate_space.hpp b/src/GLMath/glm/gtx/handed_coordinate_space.hpp new file mode 100644 index 000000000..3c8596892 --- /dev/null +++ b/src/GLMath/glm/gtx/handed_coordinate_space.hpp @@ -0,0 +1,50 @@ +/// @ref gtx_handed_coordinate_space +/// @file glm/gtx/handed_coordinate_space.hpp +/// +/// @see core (dependence) +/// +/// @defgroup gtx_handed_coordinate_space GLM_GTX_handed_coordinate_space +/// @ingroup gtx +/// +/// Include to use the features of this extension. +/// +/// To know if a set of three basis vectors defines a right or left-handed coordinate system. + +#pragma once + +// Dependency: +#include "../glm.hpp" + +#if GLM_MESSAGES == GLM_ENABLE && !defined(GLM_EXT_INCLUDED) +# ifndef GLM_ENABLE_EXPERIMENTAL +# pragma message("GLM: GLM_GTX_handed_coordinate_space is an experimental extension and may change in the future. Use #define GLM_ENABLE_EXPERIMENTAL before including it, if you really want to use it.") +# else +# pragma message("GLM: GLM_GTX_handed_coordinate_space extension included") +# endif +#endif + +namespace glm +{ + /// @addtogroup gtx_handed_coordinate_space + /// @{ + + //! Return if a trihedron right handed or not. + //! From GLM_GTX_handed_coordinate_space extension. + template + GLM_FUNC_DECL bool rightHanded( + vec<3, T, Q> const& tangent, + vec<3, T, Q> const& binormal, + vec<3, T, Q> const& normal); + + //! Return if a trihedron left handed or not. + //! From GLM_GTX_handed_coordinate_space extension. + template + GLM_FUNC_DECL bool leftHanded( + vec<3, T, Q> const& tangent, + vec<3, T, Q> const& binormal, + vec<3, T, Q> const& normal); + + /// @} +}// namespace glm + +#include "handed_coordinate_space.inl" diff --git a/src/GLMath/glm/gtx/handed_coordinate_space.inl b/src/GLMath/glm/gtx/handed_coordinate_space.inl new file mode 100644 index 000000000..e43c17bd3 --- /dev/null +++ b/src/GLMath/glm/gtx/handed_coordinate_space.inl @@ -0,0 +1,26 @@ +/// @ref gtx_handed_coordinate_space + +namespace glm +{ + template + GLM_FUNC_QUALIFIER bool rightHanded + ( + vec<3, T, Q> const& tangent, + vec<3, T, Q> const& binormal, + vec<3, T, Q> const& normal + ) + { + return dot(cross(normal, tangent), binormal) > T(0); + } + + template + GLM_FUNC_QUALIFIER bool leftHanded + ( + vec<3, T, Q> const& tangent, + vec<3, T, Q> const& binormal, + vec<3, T, Q> const& normal + ) + { + return dot(cross(normal, tangent), binormal) < T(0); + } +}//namespace glm diff --git a/src/GLMath/glm/gtx/hash.hpp b/src/GLMath/glm/gtx/hash.hpp new file mode 100644 index 000000000..93b1bc2dc --- /dev/null +++ b/src/GLMath/glm/gtx/hash.hpp @@ -0,0 +1,142 @@ +/// @ref gtx_hash +/// @file glm/gtx/hash.hpp +/// +/// @see core (dependence) +/// +/// @defgroup gtx_hash GLM_GTX_hash +/// @ingroup gtx +/// +/// Include to use the features of this extension. +/// +/// Add std::hash support for glm types + +#pragma once + +#if GLM_MESSAGES == GLM_ENABLE && !defined(GLM_EXT_INCLUDED) +# ifndef GLM_ENABLE_EXPERIMENTAL +# pragma message("GLM: GLM_GTX_hash is an experimental extension and may change in the future. Use #define GLM_ENABLE_EXPERIMENTAL before including it, if you really want to use it.") +# else +# pragma message("GLM: GLM_GTX_hash extension included") +# endif +#endif + +#include + +#include "../vec2.hpp" +#include "../vec3.hpp" +#include "../vec4.hpp" +#include "../gtc/vec1.hpp" + +#include "../gtc/quaternion.hpp" +#include "../gtx/dual_quaternion.hpp" + +#include "../mat2x2.hpp" +#include "../mat2x3.hpp" +#include "../mat2x4.hpp" + +#include "../mat3x2.hpp" +#include "../mat3x3.hpp" +#include "../mat3x4.hpp" + +#include "../mat4x2.hpp" +#include "../mat4x3.hpp" +#include "../mat4x4.hpp" + +#if !GLM_HAS_CXX11_STL +# error "GLM_GTX_hash requires C++11 standard library support" +#endif + +namespace std +{ + template + struct hash > + { + GLM_FUNC_DECL size_t operator()(glm::vec<1, T, Q> const& v) const; + }; + + template + struct hash > + { + GLM_FUNC_DECL size_t operator()(glm::vec<2, T, Q> const& v) const; + }; + + template + struct hash > + { + GLM_FUNC_DECL size_t operator()(glm::vec<3, T, Q> const& v) const; + }; + + template + struct hash > + { + GLM_FUNC_DECL size_t operator()(glm::vec<4, T, Q> const& v) const; + }; + + template + struct hash> + { + GLM_FUNC_DECL size_t operator()(glm::tquat const& q) const; + }; + + template + struct hash > + { + GLM_FUNC_DECL size_t operator()(glm::tdualquat const& q) const; + }; + + template + struct hash > + { + GLM_FUNC_DECL size_t operator()(glm::mat<2, 2, T,Q> const& m) const; + }; + + template + struct hash > + { + GLM_FUNC_DECL size_t operator()(glm::mat<2, 3, T,Q> const& m) const; + }; + + template + struct hash > + { + GLM_FUNC_DECL size_t operator()(glm::mat<2, 4, T,Q> const& m) const; + }; + + template + struct hash > + { + GLM_FUNC_DECL size_t operator()(glm::mat<3, 2, T,Q> const& m) const; + }; + + template + struct hash > + { + GLM_FUNC_DECL size_t operator()(glm::mat<3, 3, T,Q> const& m) const; + }; + + template + struct hash > + { + GLM_FUNC_DECL size_t operator()(glm::mat<3, 4, T,Q> const& m) const; + }; + + template + struct hash > + { + GLM_FUNC_DECL size_t operator()(glm::mat<4, 2, T,Q> const& m) const; + }; + + template + struct hash > + { + GLM_FUNC_DECL size_t operator()(glm::mat<4, 3, T,Q> const& m) const; + }; + + template + struct hash > + { + GLM_FUNC_DECL size_t operator()(glm::mat<4, 4, T,Q> const& m) const; + }; +} // namespace std + +#include "hash.inl" diff --git a/src/GLMath/glm/gtx/hash.inl b/src/GLMath/glm/gtx/hash.inl new file mode 100644 index 000000000..64443ef8c --- /dev/null +++ b/src/GLMath/glm/gtx/hash.inl @@ -0,0 +1,184 @@ +/// @ref gtx_hash +/// +/// @see core (dependence) +/// +/// @defgroup gtx_hash GLM_GTX_hash +/// @ingroup gtx +/// +/// @brief Add std::hash support for glm types +/// +/// need to be included to use the features of this extension. + +namespace glm { +namespace detail +{ + GLM_INLINE void hash_combine(size_t &seed, size_t hash) + { + hash += 0x9e3779b9 + (seed << 6) + (seed >> 2); + seed ^= hash; + } +}} + +namespace std +{ + template + GLM_FUNC_QUALIFIER size_t hash>::operator()(glm::vec<1, T, Q> const& v) const + { + hash hasher; + return hasher(v.x); + } + + template + GLM_FUNC_QUALIFIER size_t hash>::operator()(glm::vec<2, T, Q> const& v) const + { + size_t seed = 0; + hash hasher; + glm::detail::hash_combine(seed, hasher(v.x)); + glm::detail::hash_combine(seed, hasher(v.y)); + return seed; + } + + template + GLM_FUNC_QUALIFIER size_t hash>::operator()(glm::vec<3, T, Q> const& v) const + { + size_t seed = 0; + hash hasher; + glm::detail::hash_combine(seed, hasher(v.x)); + glm::detail::hash_combine(seed, hasher(v.y)); + glm::detail::hash_combine(seed, hasher(v.z)); + return seed; + } + + template + GLM_FUNC_QUALIFIER size_t hash>::operator()(glm::vec<4, T, Q> const& v) const + { + size_t seed = 0; + hash hasher; + glm::detail::hash_combine(seed, hasher(v.x)); + glm::detail::hash_combine(seed, hasher(v.y)); + glm::detail::hash_combine(seed, hasher(v.z)); + glm::detail::hash_combine(seed, hasher(v.w)); + return seed; + } + + template + GLM_FUNC_QUALIFIER size_t hash>::operator()(glm::tquat const& q) const + { + size_t seed = 0; + hash hasher; + glm::detail::hash_combine(seed, hasher(q.x)); + glm::detail::hash_combine(seed, hasher(q.y)); + glm::detail::hash_combine(seed, hasher(q.z)); + glm::detail::hash_combine(seed, hasher(q.w)); + return seed; + } + + template + GLM_FUNC_QUALIFIER size_t hash>::operator()(glm::tdualquat const& q) const + { + size_t seed = 0; + hash> hasher; + glm::detail::hash_combine(seed, hasher(q.real)); + glm::detail::hash_combine(seed, hasher(q.dual)); + return seed; + } + + template + GLM_FUNC_QUALIFIER size_t hash>::operator()(glm::mat<2, 2, T, Q> const& m) const + { + size_t seed = 0; + hash> hasher; + glm::detail::hash_combine(seed, hasher(m[0])); + glm::detail::hash_combine(seed, hasher(m[1])); + return seed; + } + + template + GLM_FUNC_QUALIFIER size_t hash>::operator()(glm::mat<2, 3, T, Q> const& m) const + { + size_t seed = 0; + hash> hasher; + glm::detail::hash_combine(seed, hasher(m[0])); + glm::detail::hash_combine(seed, hasher(m[1])); + return seed; + } + + template + GLM_FUNC_QUALIFIER size_t hash>::operator()(glm::mat<2, 4, T, Q> const& m) const + { + size_t seed = 0; + hash> hasher; + glm::detail::hash_combine(seed, hasher(m[0])); + glm::detail::hash_combine(seed, hasher(m[1])); + return seed; + } + + template + GLM_FUNC_QUALIFIER size_t hash>::operator()(glm::mat<3, 2, T, Q> const& m) const + { + size_t seed = 0; + hash> hasher; + glm::detail::hash_combine(seed, hasher(m[0])); + glm::detail::hash_combine(seed, hasher(m[1])); + glm::detail::hash_combine(seed, hasher(m[2])); + return seed; + } + + template + GLM_FUNC_QUALIFIER size_t hash>::operator()(glm::mat<3, 3, T, Q> const& m) const + { + size_t seed = 0; + hash> hasher; + glm::detail::hash_combine(seed, hasher(m[0])); + glm::detail::hash_combine(seed, hasher(m[1])); + glm::detail::hash_combine(seed, hasher(m[2])); + return seed; + } + + template + GLM_FUNC_QUALIFIER size_t hash>::operator()(glm::mat<3, 4, T, Q> const& m) const + { + size_t seed = 0; + hash> hasher; + glm::detail::hash_combine(seed, hasher(m[0])); + glm::detail::hash_combine(seed, hasher(m[1])); + glm::detail::hash_combine(seed, hasher(m[2])); + return seed; + } + + template + GLM_FUNC_QUALIFIER size_t hash>::operator()(glm::mat<4, 2, T,Q> const& m) const + { + size_t seed = 0; + hash> hasher; + glm::detail::hash_combine(seed, hasher(m[0])); + glm::detail::hash_combine(seed, hasher(m[1])); + glm::detail::hash_combine(seed, hasher(m[2])); + glm::detail::hash_combine(seed, hasher(m[3])); + return seed; + } + + template + GLM_FUNC_QUALIFIER size_t hash>::operator()(glm::mat<4, 3, T,Q> const& m) const + { + size_t seed = 0; + hash> hasher; + glm::detail::hash_combine(seed, hasher(m[0])); + glm::detail::hash_combine(seed, hasher(m[1])); + glm::detail::hash_combine(seed, hasher(m[2])); + glm::detail::hash_combine(seed, hasher(m[3])); + return seed; + } + + template + GLM_FUNC_QUALIFIER size_t hash>::operator()(glm::mat<4, 4, T, Q> const& m) const + { + size_t seed = 0; + hash> hasher; + glm::detail::hash_combine(seed, hasher(m[0])); + glm::detail::hash_combine(seed, hasher(m[1])); + glm::detail::hash_combine(seed, hasher(m[2])); + glm::detail::hash_combine(seed, hasher(m[3])); + return seed; + } +} diff --git a/src/GLMath/glm/gtx/integer.hpp b/src/GLMath/glm/gtx/integer.hpp new file mode 100644 index 000000000..d0b4c61a3 --- /dev/null +++ b/src/GLMath/glm/gtx/integer.hpp @@ -0,0 +1,76 @@ +/// @ref gtx_integer +/// @file glm/gtx/integer.hpp +/// +/// @see core (dependence) +/// +/// @defgroup gtx_integer GLM_GTX_integer +/// @ingroup gtx +/// +/// Include to use the features of this extension. +/// +/// Add support for integer for core functions + +#pragma once + +// Dependency: +#include "../glm.hpp" +#include "../gtc/integer.hpp" + +#if GLM_MESSAGES == GLM_ENABLE && !defined(GLM_EXT_INCLUDED) +# ifndef GLM_ENABLE_EXPERIMENTAL +# pragma message("GLM: GLM_GTX_integer is an experimental extension and may change in the future. Use #define GLM_ENABLE_EXPERIMENTAL before including it, if you really want to use it.") +# else +# pragma message("GLM: GLM_GTX_integer extension included") +# endif +#endif + +namespace glm +{ + /// @addtogroup gtx_integer + /// @{ + + //! Returns x raised to the y power. + //! From GLM_GTX_integer extension. + GLM_FUNC_DECL int pow(int x, uint y); + + //! Returns the positive square root of x. + //! From GLM_GTX_integer extension. + GLM_FUNC_DECL int sqrt(int x); + + //! Returns the floor log2 of x. + //! From GLM_GTX_integer extension. + GLM_FUNC_DECL unsigned int floor_log2(unsigned int x); + + //! Modulus. Returns x - y * floor(x / y) for each component in x using the floating point value y. + //! From GLM_GTX_integer extension. + GLM_FUNC_DECL int mod(int x, int y); + + //! Return the factorial value of a number (!12 max, integer only) + //! From GLM_GTX_integer extension. + template + GLM_FUNC_DECL genType factorial(genType const& x); + + //! 32bit signed integer. + //! From GLM_GTX_integer extension. + typedef signed int sint; + + //! Returns x raised to the y power. + //! From GLM_GTX_integer extension. + GLM_FUNC_DECL uint pow(uint x, uint y); + + //! Returns the positive square root of x. + //! From GLM_GTX_integer extension. + GLM_FUNC_DECL uint sqrt(uint x); + + //! Modulus. Returns x - y * floor(x / y) for each component in x using the floating point value y. + //! From GLM_GTX_integer extension. + GLM_FUNC_DECL uint mod(uint x, uint y); + + //! Returns the number of leading zeros. + //! From GLM_GTX_integer extension. + GLM_FUNC_DECL uint nlz(uint x); + + /// @} +}//namespace glm + +#include "integer.inl" diff --git a/src/GLMath/glm/gtx/integer.inl b/src/GLMath/glm/gtx/integer.inl new file mode 100644 index 000000000..956366b25 --- /dev/null +++ b/src/GLMath/glm/gtx/integer.inl @@ -0,0 +1,185 @@ +/// @ref gtx_integer + +namespace glm +{ + // pow + GLM_FUNC_QUALIFIER int pow(int x, uint y) + { + if(y == 0) + return x >= 0 ? 1 : -1; + + int result = x; + for(uint i = 1; i < y; ++i) + result *= x; + return result; + } + + // sqrt: From Christopher J. Musial, An integer square root, Graphics Gems, 1990, page 387 + GLM_FUNC_QUALIFIER int sqrt(int x) + { + if(x <= 1) return x; + + int NextTrial = x >> 1; + int CurrentAnswer; + + do + { + CurrentAnswer = NextTrial; + NextTrial = (NextTrial + x / NextTrial) >> 1; + } while(NextTrial < CurrentAnswer); + + return CurrentAnswer; + } + +// Henry Gordon Dietz: http://aggregate.org/MAGIC/ +namespace detail +{ + GLM_FUNC_QUALIFIER unsigned int ones32(unsigned int x) + { + /* 32-bit recursive reduction using SWAR... + but first step is mapping 2-bit values + into sum of 2 1-bit values in sneaky way + */ + x -= ((x >> 1) & 0x55555555); + x = (((x >> 2) & 0x33333333) + (x & 0x33333333)); + x = (((x >> 4) + x) & 0x0f0f0f0f); + x += (x >> 8); + x += (x >> 16); + return(x & 0x0000003f); + } +}//namespace detail + + // Henry Gordon Dietz: http://aggregate.org/MAGIC/ +/* + GLM_FUNC_QUALIFIER unsigned int floor_log2(unsigned int x) + { + x |= (x >> 1); + x |= (x >> 2); + x |= (x >> 4); + x |= (x >> 8); + x |= (x >> 16); + + return _detail::ones32(x) >> 1; + } +*/ + // mod + GLM_FUNC_QUALIFIER int mod(int x, int y) + { + return ((x % y) + y) % y; + } + + // factorial (!12 max, integer only) + template + GLM_FUNC_QUALIFIER genType factorial(genType const& x) + { + genType Temp = x; + genType Result; + for(Result = 1; Temp > 1; --Temp) + Result *= Temp; + return Result; + } + + template + GLM_FUNC_QUALIFIER vec<2, T, Q> factorial( + vec<2, T, Q> const& x) + { + return vec<2, T, Q>( + factorial(x.x), + factorial(x.y)); + } + + template + GLM_FUNC_QUALIFIER vec<3, T, Q> factorial( + vec<3, T, Q> const& x) + { + return vec<3, T, Q>( + factorial(x.x), + factorial(x.y), + factorial(x.z)); + } + + template + GLM_FUNC_QUALIFIER vec<4, T, Q> factorial( + vec<4, T, Q> const& x) + { + return vec<4, T, Q>( + factorial(x.x), + factorial(x.y), + factorial(x.z), + factorial(x.w)); + } + + GLM_FUNC_QUALIFIER uint pow(uint x, uint y) + { + if (y == 0) + return 1u; + + uint result = x; + for(uint i = 1; i < y; ++i) + result *= x; + return result; + } + + GLM_FUNC_QUALIFIER uint sqrt(uint x) + { + if(x <= 1) return x; + + uint NextTrial = x >> 1; + uint CurrentAnswer; + + do + { + CurrentAnswer = NextTrial; + NextTrial = (NextTrial + x / NextTrial) >> 1; + } while(NextTrial < CurrentAnswer); + + return CurrentAnswer; + } + + GLM_FUNC_QUALIFIER uint mod(uint x, uint y) + { + return x - y * (x / y); + } + +#if(GLM_COMPILER & (GLM_COMPILER_VC | GLM_COMPILER_GCC)) + + GLM_FUNC_QUALIFIER unsigned int nlz(unsigned int x) + { + return 31u - findMSB(x); + } + +#else + + // Hackers Delight: http://www.hackersdelight.org/HDcode/nlz.c.txt + GLM_FUNC_QUALIFIER unsigned int nlz(unsigned int x) + { + int y, m, n; + + y = -int(x >> 16); // If left half of x is 0, + m = (y >> 16) & 16; // set n = 16. If left half + n = 16 - m; // is nonzero, set n = 0 and + x = x >> m; // shift x right 16. + // Now x is of the form 0000xxxx. + y = x - 0x100; // If positions 8-15 are 0, + m = (y >> 16) & 8; // add 8 to n and shift x left 8. + n = n + m; + x = x << m; + + y = x - 0x1000; // If positions 12-15 are 0, + m = (y >> 16) & 4; // add 4 to n and shift x left 4. + n = n + m; + x = x << m; + + y = x - 0x4000; // If positions 14-15 are 0, + m = (y >> 16) & 2; // add 2 to n and shift x left 2. + n = n + m; + x = x << m; + + y = x >> 14; // Set y = 0, 1, 2, or 3. + m = y & ~(y >> 1); // Set m = 0, 1, 2, or 2 resp. + return unsigned(n + 2 - m); + } + +#endif//(GLM_COMPILER) + +}//namespace glm diff --git a/src/GLMath/glm/gtx/intersect.hpp b/src/GLMath/glm/gtx/intersect.hpp new file mode 100644 index 000000000..3c78f2b8e --- /dev/null +++ b/src/GLMath/glm/gtx/intersect.hpp @@ -0,0 +1,92 @@ +/// @ref gtx_intersect +/// @file glm/gtx/intersect.hpp +/// +/// @see core (dependence) +/// @see gtx_closest_point (dependence) +/// +/// @defgroup gtx_intersect GLM_GTX_intersect +/// @ingroup gtx +/// +/// Include to use the features of this extension. +/// +/// Add intersection functions + +#pragma once + +// Dependency: +#include +#include +#include "../glm.hpp" +#include "../geometric.hpp" +#include "../gtx/closest_point.hpp" +#include "../gtx/vector_query.hpp" + +#if GLM_MESSAGES == GLM_ENABLE && !defined(GLM_EXT_INCLUDED) +# ifndef GLM_ENABLE_EXPERIMENTAL +# pragma message("GLM: GLM_GTX_closest_point is an experimental extension and may change in the future. Use #define GLM_ENABLE_EXPERIMENTAL before including it, if you really want to use it.") +# else +# pragma message("GLM: GLM_GTX_closest_point extension included") +# endif +#endif + +namespace glm +{ + /// @addtogroup gtx_intersect + /// @{ + + //! Compute the intersection of a ray and a plane. + //! Ray direction and plane normal must be unit length. + //! From GLM_GTX_intersect extension. + template + GLM_FUNC_DECL bool intersectRayPlane( + genType const& orig, genType const& dir, + genType const& planeOrig, genType const& planeNormal, + typename genType::value_type & intersectionDistance); + + //! Compute the intersection of a ray and a triangle. + /// Based om Tomas Möller implementation http://fileadmin.cs.lth.se/cs/Personal/Tomas_Akenine-Moller/raytri/ + //! From GLM_GTX_intersect extension. + template + GLM_FUNC_DECL bool intersectRayTriangle( + vec<3, T, Q> const& orig, vec<3, T, Q> const& dir, + vec<3, T, Q> const& v0, vec<3, T, Q> const& v1, vec<3, T, Q> const& v2, + vec<2, T, Q>& baryPosition, T& distance); + + //! Compute the intersection of a line and a triangle. + //! From GLM_GTX_intersect extension. + template + GLM_FUNC_DECL bool intersectLineTriangle( + genType const& orig, genType const& dir, + genType const& vert0, genType const& vert1, genType const& vert2, + genType & position); + + //! Compute the intersection distance of a ray and a sphere. + //! The ray direction vector is unit length. + //! From GLM_GTX_intersect extension. + template + GLM_FUNC_DECL bool intersectRaySphere( + genType const& rayStarting, genType const& rayNormalizedDirection, + genType const& sphereCenter, typename genType::value_type const sphereRadiusSquered, + typename genType::value_type & intersectionDistance); + + //! Compute the intersection of a ray and a sphere. + //! From GLM_GTX_intersect extension. + template + GLM_FUNC_DECL bool intersectRaySphere( + genType const& rayStarting, genType const& rayNormalizedDirection, + genType const& sphereCenter, const typename genType::value_type sphereRadius, + genType & intersectionPosition, genType & intersectionNormal); + + //! Compute the intersection of a line and a sphere. + //! From GLM_GTX_intersect extension + template + GLM_FUNC_DECL bool intersectLineSphere( + genType const& point0, genType const& point1, + genType const& sphereCenter, typename genType::value_type sphereRadius, + genType & intersectionPosition1, genType & intersectionNormal1, + genType & intersectionPosition2 = genType(), genType & intersectionNormal2 = genType()); + + /// @} +}//namespace glm + +#include "intersect.inl" diff --git a/src/GLMath/glm/gtx/intersect.inl b/src/GLMath/glm/gtx/intersect.inl new file mode 100644 index 000000000..e76fd628b --- /dev/null +++ b/src/GLMath/glm/gtx/intersect.inl @@ -0,0 +1,197 @@ +/// @ref gtx_intersect + +namespace glm +{ + template + GLM_FUNC_QUALIFIER bool intersectRayPlane + ( + genType const& orig, genType const& dir, + genType const& planeOrig, genType const& planeNormal, + typename genType::value_type & intersectionDistance + ) + { + typename genType::value_type d = glm::dot(dir, planeNormal); + typename genType::value_type Epsilon = std::numeric_limits::epsilon(); + + if(d < -Epsilon) + { + intersectionDistance = glm::dot(planeOrig - orig, planeNormal) / d; + return true; + } + + return false; + } + + template + GLM_FUNC_QUALIFIER bool intersectRayTriangle + ( + vec<3, T, Q> const& orig, vec<3, T, Q> const& dir, + vec<3, T, Q> const& vert0, vec<3, T, Q> const& vert1, vec<3, T, Q> const& vert2, + vec<2, T, Q>& baryPosition, T& distance + ) + { + // find vectors for two edges sharing vert0 + vec<3, T, Q> const edge1 = vert1 - vert0; + vec<3, T, Q> const edge2 = vert2 - vert0; + + // begin calculating determinant - also used to calculate U parameter + vec<3, T, Q> const p = glm::cross(dir, edge2); + + // if determinant is near zero, ray lies in plane of triangle + T const det = glm::dot(edge1, p); + + vec<3, T, Q> Perpendicular(0); + + if(det > std::numeric_limits::epsilon()) + { + // calculate distance from vert0 to ray origin + vec<3, T, Q> const dist = orig - vert0; + + // calculate U parameter and test bounds + baryPosition.x = glm::dot(dist, p); + if(baryPosition.x < static_cast(0) || baryPosition.x > det) + return false; + + // prepare to test V parameter + Perpendicular = glm::cross(dist, edge1); + + // calculate V parameter and test bounds + baryPosition.y = glm::dot(dir, Perpendicular); + if((baryPosition.y < static_cast(0)) || ((baryPosition.x + baryPosition.y) > det)) + return false; + } + else if(det < -std::numeric_limits::epsilon()) + { + // calculate distance from vert0 to ray origin + vec<3, T, Q> const dist = orig - vert0; + + // calculate U parameter and test bounds + baryPosition.x = glm::dot(dist, p); + if((baryPosition.x > static_cast(0)) || (baryPosition.x < det)) + return false; + + // prepare to test V parameter + Perpendicular = glm::cross(dist, edge1); + + // calculate V parameter and test bounds + baryPosition.y = glm::dot(dir, Perpendicular); + if((baryPosition.y > static_cast(0)) || (baryPosition.x + baryPosition.y < det)) + return false; + } + else + return false; // ray is parallel to the plane of the triangle + + T inv_det = static_cast(1) / det; + + // calculate distance, ray intersects triangle + distance = glm::dot(edge2, Perpendicular) * inv_det; + baryPosition *= inv_det; + + return true; + } + + template + GLM_FUNC_QUALIFIER bool intersectLineTriangle + ( + genType const& orig, genType const& dir, + genType const& vert0, genType const& vert1, genType const& vert2, + genType & position + ) + { + typename genType::value_type Epsilon = std::numeric_limits::epsilon(); + + genType edge1 = vert1 - vert0; + genType edge2 = vert2 - vert0; + + genType Perpendicular = cross(dir, edge2); + + float det = dot(edge1, Perpendicular); + + if (det > -Epsilon && det < Epsilon) + return false; + typename genType::value_type inv_det = typename genType::value_type(1) / det; + + genType Tengant = orig - vert0; + + position.y = dot(Tengant, Perpendicular) * inv_det; + if (position.y < typename genType::value_type(0) || position.y > typename genType::value_type(1)) + return false; + + genType Cotengant = cross(Tengant, edge1); + + position.z = dot(dir, Cotengant) * inv_det; + if (position.z < typename genType::value_type(0) || position.y + position.z > typename genType::value_type(1)) + return false; + + position.x = dot(edge2, Cotengant) * inv_det; + + return true; + } + + template + GLM_FUNC_QUALIFIER bool intersectRaySphere + ( + genType const& rayStarting, genType const& rayNormalizedDirection, + genType const& sphereCenter, const typename genType::value_type sphereRadiusSquered, + typename genType::value_type & intersectionDistance + ) + { + typename genType::value_type Epsilon = std::numeric_limits::epsilon(); + genType diff = sphereCenter - rayStarting; + typename genType::value_type t0 = dot(diff, rayNormalizedDirection); + typename genType::value_type dSquared = dot(diff, diff) - t0 * t0; + if( dSquared > sphereRadiusSquered ) + { + return false; + } + typename genType::value_type t1 = sqrt( sphereRadiusSquered - dSquared ); + intersectionDistance = t0 > t1 + Epsilon ? t0 - t1 : t0 + t1; + return intersectionDistance > Epsilon; + } + + template + GLM_FUNC_QUALIFIER bool intersectRaySphere + ( + genType const& rayStarting, genType const& rayNormalizedDirection, + genType const& sphereCenter, const typename genType::value_type sphereRadius, + genType & intersectionPosition, genType & intersectionNormal + ) + { + typename genType::value_type distance; + if( intersectRaySphere( rayStarting, rayNormalizedDirection, sphereCenter, sphereRadius * sphereRadius, distance ) ) + { + intersectionPosition = rayStarting + rayNormalizedDirection * distance; + intersectionNormal = (intersectionPosition - sphereCenter) / sphereRadius; + return true; + } + return false; + } + + template + GLM_FUNC_QUALIFIER bool intersectLineSphere + ( + genType const& point0, genType const& point1, + genType const& sphereCenter, typename genType::value_type sphereRadius, + genType & intersectionPoint1, genType & intersectionNormal1, + genType & intersectionPoint2, genType & intersectionNormal2 + ) + { + typename genType::value_type Epsilon = std::numeric_limits::epsilon(); + genType dir = normalize(point1 - point0); + genType diff = sphereCenter - point0; + typename genType::value_type t0 = dot(diff, dir); + typename genType::value_type dSquared = dot(diff, diff) - t0 * t0; + if( dSquared > sphereRadius * sphereRadius ) + { + return false; + } + typename genType::value_type t1 = sqrt( sphereRadius * sphereRadius - dSquared ); + if( t0 < t1 + Epsilon ) + t1 = -t1; + intersectionPoint1 = point0 + dir * (t0 - t1); + intersectionNormal1 = (intersectionPoint1 - sphereCenter) / sphereRadius; + intersectionPoint2 = point0 + dir * (t0 + t1); + intersectionNormal2 = (intersectionPoint2 - sphereCenter) / sphereRadius; + return true; + } +}//namespace glm diff --git a/src/GLMath/glm/gtx/io.hpp b/src/GLMath/glm/gtx/io.hpp new file mode 100644 index 000000000..8d974f004 --- /dev/null +++ b/src/GLMath/glm/gtx/io.hpp @@ -0,0 +1,201 @@ +/// @ref gtx_io +/// @file glm/gtx/io.hpp +/// @author Jan P Springer (regnirpsj@gmail.com) +/// +/// @see core (dependence) +/// @see gtc_matrix_access (dependence) +/// @see gtc_quaternion (dependence) +/// +/// @defgroup gtx_io GLM_GTX_io +/// @ingroup gtx +/// +/// Include to use the features of this extension. +/// +/// std::[w]ostream support for glm types +/// +/// std::[w]ostream support for glm types + qualifier/width/etc. manipulators +/// based on howard hinnant's std::chrono io proposal +/// [http://home.roadrunner.com/~hinnant/bloomington/chrono_io.html] + +#pragma once + +// Dependency: +#include "../glm.hpp" +#include "../gtx/quaternion.hpp" + +#if GLM_MESSAGES == GLM_ENABLE && !defined(GLM_EXT_INCLUDED) +# ifndef GLM_ENABLE_EXPERIMENTAL +# pragma message("GLM: GLM_GTX_io is an experimental extension and may change in the future. Use #define GLM_ENABLE_EXPERIMENTAL before including it, if you really want to use it.") +# else +# pragma message("GLM: GLM_GTX_io extension included") +# endif +#endif + +#include // std::basic_ostream<> (fwd) +#include // std::locale, std::locale::facet, std::locale::id +#include // std::pair<> + +namespace glm +{ + /// @addtogroup gtx_io + /// @{ + + namespace io + { + enum order_type { column_major, row_major}; + + template + class format_punct : public std::locale::facet + { + typedef CTy char_type; + + public: + + static std::locale::id id; + + bool formatted; + unsigned precision; + unsigned width; + char_type separator; + char_type delim_left; + char_type delim_right; + char_type space; + char_type newline; + order_type order; + + GLM_FUNC_DECL explicit format_punct(size_t a = 0); + GLM_FUNC_DECL explicit format_punct(format_punct const&); + }; + + template > + class basic_state_saver { + + public: + + GLM_FUNC_DECL explicit basic_state_saver(std::basic_ios&); + GLM_FUNC_DECL ~basic_state_saver(); + + private: + + typedef ::std::basic_ios state_type; + typedef typename state_type::char_type char_type; + typedef ::std::ios_base::fmtflags flags_type; + typedef ::std::streamsize streamsize_type; + typedef ::std::locale const locale_type; + + state_type& state_; + flags_type flags_; + streamsize_type precision_; + streamsize_type width_; + char_type fill_; + locale_type locale_; + + GLM_FUNC_DECL basic_state_saver& operator=(basic_state_saver const&); + }; + + typedef basic_state_saver state_saver; + typedef basic_state_saver wstate_saver; + + template > + class basic_format_saver + { + public: + + GLM_FUNC_DECL explicit basic_format_saver(std::basic_ios&); + GLM_FUNC_DECL ~basic_format_saver(); + + private: + + basic_state_saver const bss_; + + GLM_FUNC_DECL basic_format_saver& operator=(basic_format_saver const&); + }; + + typedef basic_format_saver format_saver; + typedef basic_format_saver wformat_saver; + + struct precision + { + unsigned value; + + GLM_FUNC_DECL explicit precision(unsigned); + }; + + struct width + { + unsigned value; + + GLM_FUNC_DECL explicit width(unsigned); + }; + + template + struct delimeter + { + CTy value[3]; + + GLM_FUNC_DECL explicit delimeter(CTy /* left */, CTy /* right */, CTy /* separator */ = ','); + }; + + struct order + { + order_type value; + + GLM_FUNC_DECL explicit order(order_type); + }; + + // functions, inlined (inline) + + template + FTy const& get_facet(std::basic_ios&); + template + std::basic_ios& formatted(std::basic_ios&); + template + std::basic_ios& unformattet(std::basic_ios&); + + template + std::basic_ostream& operator<<(std::basic_ostream&, precision const&); + template + std::basic_ostream& operator<<(std::basic_ostream&, width const&); + template + std::basic_ostream& operator<<(std::basic_ostream&, delimeter const&); + template + std::basic_ostream& operator<<(std::basic_ostream&, order const&); + }//namespace io + + template + GLM_FUNC_DECL std::basic_ostream& operator<<(std::basic_ostream&, qua const&); + template + GLM_FUNC_DECL std::basic_ostream& operator<<(std::basic_ostream&, vec<1, T, Q> const&); + template + GLM_FUNC_DECL std::basic_ostream& operator<<(std::basic_ostream&, vec<2, T, Q> const&); + template + GLM_FUNC_DECL std::basic_ostream& operator<<(std::basic_ostream&, vec<3, T, Q> const&); + template + GLM_FUNC_DECL std::basic_ostream& operator<<(std::basic_ostream&, vec<4, T, Q> const&); + template + GLM_FUNC_DECL std::basic_ostream& operator<<(std::basic_ostream&, mat<2, 2, T, Q> const&); + template + GLM_FUNC_DECL std::basic_ostream& operator<<(std::basic_ostream&, mat<2, 3, T, Q> const&); + template + GLM_FUNC_DECL std::basic_ostream& operator<<(std::basic_ostream&, mat<2, 4, T, Q> const&); + template + GLM_FUNC_DECL std::basic_ostream& operator<<(std::basic_ostream&, mat<3, 2, T, Q> const&); + template + GLM_FUNC_DECL std::basic_ostream& operator<<(std::basic_ostream&, mat<3, 3, T, Q> const&); + template + GLM_FUNC_DECL std::basic_ostream& operator<<(std::basic_ostream&, mat<3, 4, T, Q> const&); + template + GLM_FUNC_DECL std::basic_ostream& operator<<(std::basic_ostream&, mat<4, 2, T, Q> const&); + template + GLM_FUNC_DECL std::basic_ostream& operator<<(std::basic_ostream&, mat<4, 3, T, Q> const&); + template + GLM_FUNC_DECL std::basic_ostream& operator<<(std::basic_ostream&, mat<4, 4, T, Q> const&); + + template + GLM_FUNC_DECL std::basic_ostream & operator<<(std::basic_ostream &, + std::pair const, mat<4, 4, T, Q> const> const&); + + /// @} +}//namespace glm + +#include "io.inl" diff --git a/src/GLMath/glm/gtx/io.inl b/src/GLMath/glm/gtx/io.inl new file mode 100644 index 000000000..a3a1bb6c2 --- /dev/null +++ b/src/GLMath/glm/gtx/io.inl @@ -0,0 +1,440 @@ +/// @ref gtx_io +/// @author Jan P Springer (regnirpsj@gmail.com) + +#include // std::fixed, std::setfill<>, std::setprecision, std::right, std::setw +#include // std::basic_ostream<> +#include "../gtc/matrix_access.hpp" // glm::col, glm::row +#include "../gtx/type_trait.hpp" // glm::type<> + +namespace glm{ +namespace io +{ + template + GLM_FUNC_QUALIFIER format_punct::format_punct(size_t a) + : std::locale::facet(a) + , formatted(true) + , precision(3) + , width(1 + 4 + 1 + precision) + , separator(',') + , delim_left('[') + , delim_right(']') + , space(' ') + , newline('\n') + , order(column_major) + {} + + template + GLM_FUNC_QUALIFIER format_punct::format_punct(format_punct const& a) + : std::locale::facet(0) + , formatted(a.formatted) + , precision(a.precision) + , width(a.width) + , separator(a.separator) + , delim_left(a.delim_left) + , delim_right(a.delim_right) + , space(a.space) + , newline(a.newline) + , order(a.order) + {} + + template std::locale::id format_punct::id; + + template + GLM_FUNC_QUALIFIER basic_state_saver::basic_state_saver(std::basic_ios& a) + : state_(a) + , flags_(a.flags()) + , precision_(a.precision()) + , width_(a.width()) + , fill_(a.fill()) + , locale_(a.getloc()) + {} + + template + GLM_FUNC_QUALIFIER basic_state_saver::~basic_state_saver() + { + state_.imbue(locale_); + state_.fill(fill_); + state_.width(width_); + state_.precision(precision_); + state_.flags(flags_); + } + + template + GLM_FUNC_QUALIFIER basic_format_saver::basic_format_saver(std::basic_ios& a) + : bss_(a) + { + a.imbue(std::locale(a.getloc(), new format_punct(get_facet >(a)))); + } + + template + GLM_FUNC_QUALIFIER + basic_format_saver::~basic_format_saver() + {} + + GLM_FUNC_QUALIFIER precision::precision(unsigned a) + : value(a) + {} + + GLM_FUNC_QUALIFIER width::width(unsigned a) + : value(a) + {} + + template + GLM_FUNC_QUALIFIER delimeter::delimeter(CTy a, CTy b, CTy c) + : value() + { + value[0] = a; + value[1] = b; + value[2] = c; + } + + GLM_FUNC_QUALIFIER order::order(order_type a) + : value(a) + {} + + template + GLM_FUNC_QUALIFIER FTy const& get_facet(std::basic_ios& ios) + { + if(!std::has_facet(ios.getloc())) + ios.imbue(std::locale(ios.getloc(), new FTy)); + + return std::use_facet(ios.getloc()); + } + + template + GLM_FUNC_QUALIFIER std::basic_ios& formatted(std::basic_ios& ios) + { + const_cast&>(get_facet >(ios)).formatted = true; + return ios; + } + + template + GLM_FUNC_QUALIFIER std::basic_ios& unformatted(std::basic_ios& ios) + { + const_cast&>(get_facet >(ios)).formatted = false; + return ios; + } + + template + GLM_FUNC_QUALIFIER std::basic_ostream& operator<<(std::basic_ostream& os, precision const& a) + { + const_cast&>(get_facet >(os)).precision = a.value; + return os; + } + + template + GLM_FUNC_QUALIFIER std::basic_ostream& operator<<(std::basic_ostream& os, width const& a) + { + const_cast&>(get_facet >(os)).width = a.value; + return os; + } + + template + GLM_FUNC_QUALIFIER std::basic_ostream& operator<<(std::basic_ostream& os, delimeter const& a) + { + format_punct & fmt(const_cast&>(get_facet >(os))); + + fmt.delim_left = a.value[0]; + fmt.delim_right = a.value[1]; + fmt.separator = a.value[2]; + + return os; + } + + template + GLM_FUNC_QUALIFIER std::basic_ostream& operator<<(std::basic_ostream& os, order const& a) + { + const_cast&>(get_facet >(os)).order = a.value; + return os; + } +} // namespace io + +namespace detail +{ + template + GLM_FUNC_QUALIFIER std::basic_ostream& + print_vector_on(std::basic_ostream& os, V const& a) + { + typename std::basic_ostream::sentry const cerberus(os); + + if(cerberus) + { + io::format_punct const& fmt(io::get_facet >(os)); + + length_t const& components(type::components); + + if(fmt.formatted) + { + io::basic_state_saver const bss(os); + + os << std::fixed << std::right << std::setprecision(fmt.precision) << std::setfill(fmt.space) << fmt.delim_left; + + for(length_t i(0); i < components; ++i) + { + os << std::setw(fmt.width) << a[i]; + if(components-1 != i) + os << fmt.separator; + } + + os << fmt.delim_right; + } + else + { + for(length_t i(0); i < components; ++i) + { + os << a[i]; + + if(components-1 != i) + os << fmt.space; + } + } + } + + return os; + } +}//namespace detail + + template + GLM_FUNC_QUALIFIER std::basic_ostream& operator<<(std::basic_ostream& os, qua const& a) + { + return detail::print_vector_on(os, a); + } + + template + GLM_FUNC_QUALIFIER std::basic_ostream& operator<<(std::basic_ostream& os, vec<1, T, Q> const& a) + { + return detail::print_vector_on(os, a); + } + + template + GLM_FUNC_QUALIFIER std::basic_ostream& operator<<(std::basic_ostream& os, vec<2, T, Q> const& a) + { + return detail::print_vector_on(os, a); + } + + template + GLM_FUNC_QUALIFIER std::basic_ostream& operator<<(std::basic_ostream& os, vec<3, T, Q> const& a) + { + return detail::print_vector_on(os, a); + } + + template + GLM_FUNC_QUALIFIER std::basic_ostream& operator<<(std::basic_ostream& os, vec<4, T, Q> const& a) + { + return detail::print_vector_on(os, a); + } + +namespace detail +{ + template class M, length_t C, length_t R, typename T, qualifier Q> + GLM_FUNC_QUALIFIER std::basic_ostream& print_matrix_on(std::basic_ostream& os, M const& a) + { + typename std::basic_ostream::sentry const cerberus(os); + + if(cerberus) + { + io::format_punct const& fmt(io::get_facet >(os)); + + length_t const& cols(type >::cols); + length_t const& rows(type >::rows); + + if(fmt.formatted) + { + os << fmt.newline << fmt.delim_left; + + switch(fmt.order) + { + case io::column_major: + { + for(length_t i(0); i < rows; ++i) + { + if (0 != i) + os << fmt.space; + + os << row(a, i); + + if(rows-1 != i) + os << fmt.newline; + } + } + break; + + case io::row_major: + { + for(length_t i(0); i < cols; ++i) + { + if(0 != i) + os << fmt.space; + + os << column(a, i); + + if(cols-1 != i) + os << fmt.newline; + } + } + break; + } + + os << fmt.delim_right; + } + else + { + switch (fmt.order) + { + case io::column_major: + { + for(length_t i(0); i < cols; ++i) + { + os << column(a, i); + + if(cols - 1 != i) + os << fmt.space; + } + } + break; + + case io::row_major: + { + for (length_t i(0); i < rows; ++i) + { + os << row(a, i); + + if (rows-1 != i) + os << fmt.space; + } + } + break; + } + } + } + + return os; + } +}//namespace detail + + template + GLM_FUNC_QUALIFIER std::basic_ostream& operator<<(std::basic_ostream& os, mat<2, 2, T, Q> const& a) + { + return detail::print_matrix_on(os, a); + } + + template + GLM_FUNC_QUALIFIER std::basic_ostream& operator<<(std::basic_ostream& os, mat<2, 3, T, Q> const& a) + { + return detail::print_matrix_on(os, a); + } + + template + GLM_FUNC_QUALIFIER std::basic_ostream& operator<<(std::basic_ostream& os, mat<2, 4, T, Q> const& a) + { + return detail::print_matrix_on(os, a); + } + + template + GLM_FUNC_QUALIFIER std::basic_ostream& operator<<(std::basic_ostream& os, mat<3, 2, T, Q> const& a) + { + return detail::print_matrix_on(os, a); + } + + template + GLM_FUNC_QUALIFIER std::basic_ostream& operator<<(std::basic_ostream& os, mat<3, 3, T, Q> const& a) + { + return detail::print_matrix_on(os, a); + } + + template + GLM_FUNC_QUALIFIER std::basic_ostream & operator<<(std::basic_ostream& os, mat<3, 4, T, Q> const& a) + { + return detail::print_matrix_on(os, a); + } + + template + GLM_FUNC_QUALIFIER std::basic_ostream & operator<<(std::basic_ostream& os, mat<4, 2, T, Q> const& a) + { + return detail::print_matrix_on(os, a); + } + + template + GLM_FUNC_QUALIFIER std::basic_ostream & operator<<(std::basic_ostream& os, mat<4, 3, T, Q> const& a) + { + return detail::print_matrix_on(os, a); + } + + template + GLM_FUNC_QUALIFIER std::basic_ostream & operator<<(std::basic_ostream& os, mat<4, 4, T, Q> const& a) + { + return detail::print_matrix_on(os, a); + } + +namespace detail +{ + template class M, length_t C, length_t R, typename T, qualifier Q> + GLM_FUNC_QUALIFIER std::basic_ostream& print_matrix_pair_on(std::basic_ostream& os, std::pair const, M const> const& a) + { + typename std::basic_ostream::sentry const cerberus(os); + + if(cerberus) + { + io::format_punct const& fmt(io::get_facet >(os)); + M const& ml(a.first); + M const& mr(a.second); + length_t const& cols(type >::cols); + length_t const& rows(type >::rows); + + if(fmt.formatted) + { + os << fmt.newline << fmt.delim_left; + + switch(fmt.order) + { + case io::column_major: + { + for(length_t i(0); i < rows; ++i) + { + if(0 != i) + os << fmt.space; + + os << row(ml, i) << ((rows-1 != i) ? fmt.space : fmt.delim_right) << fmt.space << ((0 != i) ? fmt.space : fmt.delim_left) << row(mr, i); + + if(rows-1 != i) + os << fmt.newline; + } + } + break; + case io::row_major: + { + for(length_t i(0); i < cols; ++i) + { + if(0 != i) + os << fmt.space; + + os << column(ml, i) << ((cols-1 != i) ? fmt.space : fmt.delim_right) << fmt.space << ((0 != i) ? fmt.space : fmt.delim_left) << column(mr, i); + + if(cols-1 != i) + os << fmt.newline; + } + } + break; + } + + os << fmt.delim_right; + } + else + { + os << ml << fmt.space << mr; + } + } + + return os; + } +}//namespace detail + + template + GLM_FUNC_QUALIFIER std::basic_ostream& operator<<( + std::basic_ostream & os, + std::pair const, + mat<4, 4, T, Q> const> const& a) + { + return detail::print_matrix_pair_on(os, a); + } +}//namespace glm diff --git a/src/GLMath/glm/gtx/log_base.hpp b/src/GLMath/glm/gtx/log_base.hpp new file mode 100644 index 000000000..ba28c9d7b --- /dev/null +++ b/src/GLMath/glm/gtx/log_base.hpp @@ -0,0 +1,48 @@ +/// @ref gtx_log_base +/// @file glm/gtx/log_base.hpp +/// +/// @see core (dependence) +/// +/// @defgroup gtx_log_base GLM_GTX_log_base +/// @ingroup gtx +/// +/// Include to use the features of this extension. +/// +/// Logarithm for any base. base can be a vector or a scalar. + +#pragma once + +// Dependency: +#include "../glm.hpp" + +#if GLM_MESSAGES == GLM_ENABLE && !defined(GLM_EXT_INCLUDED) +# ifndef GLM_ENABLE_EXPERIMENTAL +# pragma message("GLM: GLM_GTX_log_base is an experimental extension and may change in the future. Use #define GLM_ENABLE_EXPERIMENTAL before including it, if you really want to use it.") +# else +# pragma message("GLM: GLM_GTX_log_base extension included") +# endif +#endif + +namespace glm +{ + /// @addtogroup gtx_log_base + /// @{ + + /// Logarithm for any base. + /// From GLM_GTX_log_base. + template + GLM_FUNC_DECL genType log( + genType const& x, + genType const& base); + + /// Logarithm for any base. + /// From GLM_GTX_log_base. + template + GLM_FUNC_DECL vec sign( + vec const& x, + vec const& base); + + /// @} +}//namespace glm + +#include "log_base.inl" diff --git a/src/GLMath/glm/gtx/log_base.inl b/src/GLMath/glm/gtx/log_base.inl new file mode 100644 index 000000000..4bbb8e895 --- /dev/null +++ b/src/GLMath/glm/gtx/log_base.inl @@ -0,0 +1,16 @@ +/// @ref gtx_log_base + +namespace glm +{ + template + GLM_FUNC_QUALIFIER genType log(genType const& x, genType const& base) + { + return glm::log(x) / glm::log(base); + } + + template + GLM_FUNC_QUALIFIER vec log(vec const& x, vec const& base) + { + return glm::log(x) / glm::log(base); + } +}//namespace glm diff --git a/src/GLMath/glm/gtx/matrix_cross_product.hpp b/src/GLMath/glm/gtx/matrix_cross_product.hpp new file mode 100644 index 000000000..1e585f9a4 --- /dev/null +++ b/src/GLMath/glm/gtx/matrix_cross_product.hpp @@ -0,0 +1,47 @@ +/// @ref gtx_matrix_cross_product +/// @file glm/gtx/matrix_cross_product.hpp +/// +/// @see core (dependence) +/// @see gtx_extented_min_max (dependence) +/// +/// @defgroup gtx_matrix_cross_product GLM_GTX_matrix_cross_product +/// @ingroup gtx +/// +/// Include to use the features of this extension. +/// +/// Build cross product matrices + +#pragma once + +// Dependency: +#include "../glm.hpp" + +#if GLM_MESSAGES == GLM_ENABLE && !defined(GLM_EXT_INCLUDED) +# ifndef GLM_ENABLE_EXPERIMENTAL +# pragma message("GLM: GLM_GTX_matrix_cross_product is an experimental extension and may change in the future. Use #define GLM_ENABLE_EXPERIMENTAL before including it, if you really want to use it.") +# else +# pragma message("GLM: GLM_GTX_matrix_cross_product extension included") +# endif +#endif + +namespace glm +{ + /// @addtogroup gtx_matrix_cross_product + /// @{ + + //! Build a cross product matrix. + //! From GLM_GTX_matrix_cross_product extension. + template + GLM_FUNC_DECL mat<3, 3, T, Q> matrixCross3( + vec<3, T, Q> const& x); + + //! Build a cross product matrix. + //! From GLM_GTX_matrix_cross_product extension. + template + GLM_FUNC_DECL mat<4, 4, T, Q> matrixCross4( + vec<3, T, Q> const& x); + + /// @} +}//namespace glm + +#include "matrix_cross_product.inl" diff --git a/src/GLMath/glm/gtx/matrix_cross_product.inl b/src/GLMath/glm/gtx/matrix_cross_product.inl new file mode 100644 index 000000000..3a153977c --- /dev/null +++ b/src/GLMath/glm/gtx/matrix_cross_product.inl @@ -0,0 +1,37 @@ +/// @ref gtx_matrix_cross_product + +namespace glm +{ + template + GLM_FUNC_QUALIFIER mat<3, 3, T, Q> matrixCross3 + ( + vec<3, T, Q> const& x + ) + { + mat<3, 3, T, Q> Result(T(0)); + Result[0][1] = x.z; + Result[1][0] = -x.z; + Result[0][2] = -x.y; + Result[2][0] = x.y; + Result[1][2] = x.x; + Result[2][1] = -x.x; + return Result; + } + + template + GLM_FUNC_QUALIFIER mat<4, 4, T, Q> matrixCross4 + ( + vec<3, T, Q> const& x + ) + { + mat<4, 4, T, Q> Result(T(0)); + Result[0][1] = x.z; + Result[1][0] = -x.z; + Result[0][2] = -x.y; + Result[2][0] = x.y; + Result[1][2] = x.x; + Result[2][1] = -x.x; + return Result; + } + +}//namespace glm diff --git a/src/GLMath/glm/gtx/matrix_decompose.hpp b/src/GLMath/glm/gtx/matrix_decompose.hpp new file mode 100644 index 000000000..acd7a7f06 --- /dev/null +++ b/src/GLMath/glm/gtx/matrix_decompose.hpp @@ -0,0 +1,46 @@ +/// @ref gtx_matrix_decompose +/// @file glm/gtx/matrix_decompose.hpp +/// +/// @see core (dependence) +/// +/// @defgroup gtx_matrix_decompose GLM_GTX_matrix_decompose +/// @ingroup gtx +/// +/// Include to use the features of this extension. +/// +/// Decomposes a model matrix to translations, rotation and scale components + +#pragma once + +// Dependencies +#include "../mat4x4.hpp" +#include "../vec3.hpp" +#include "../vec4.hpp" +#include "../geometric.hpp" +#include "../gtc/quaternion.hpp" +#include "../gtc/matrix_transform.hpp" + +#if GLM_MESSAGES == GLM_ENABLE && !defined(GLM_EXT_INCLUDED) +# ifndef GLM_ENABLE_EXPERIMENTAL +# pragma message("GLM: GLM_GTX_matrix_decompose is an experimental extension and may change in the future. Use #define GLM_ENABLE_EXPERIMENTAL before including it, if you really want to use it.") +# else +# pragma message("GLM: GLM_GTX_matrix_decompose extension included") +# endif +#endif + +namespace glm +{ + /// @addtogroup gtx_matrix_decompose + /// @{ + + /// Decomposes a model matrix to translations, rotation and scale components + /// @see gtx_matrix_decompose + template + GLM_FUNC_DECL bool decompose( + mat<4, 4, T, Q> const& modelMatrix, + vec<3, T, Q> & scale, qua & orientation, vec<3, T, Q> & translation, vec<3, T, Q> & skew, vec<4, T, Q> & perspective); + + /// @} +}//namespace glm + +#include "matrix_decompose.inl" diff --git a/src/GLMath/glm/gtx/matrix_decompose.inl b/src/GLMath/glm/gtx/matrix_decompose.inl new file mode 100644 index 000000000..694f5eca7 --- /dev/null +++ b/src/GLMath/glm/gtx/matrix_decompose.inl @@ -0,0 +1,186 @@ +/// @ref gtx_matrix_decompose + +#include "../gtc/constants.hpp" +#include "../gtc/epsilon.hpp" + +namespace glm{ +namespace detail +{ + /// Make a linear combination of two vectors and return the result. + // result = (a * ascl) + (b * bscl) + template + GLM_FUNC_QUALIFIER vec<3, T, Q> combine( + vec<3, T, Q> const& a, + vec<3, T, Q> const& b, + T ascl, T bscl) + { + return (a * ascl) + (b * bscl); + } + + template + GLM_FUNC_QUALIFIER vec<3, T, Q> scale(vec<3, T, Q> const& v, T desiredLength) + { + return v * desiredLength / length(v); + } +}//namespace detail + + // Matrix decompose + // http://www.opensource.apple.com/source/WebCore/WebCore-514/platform/graphics/transforms/TransformationMatrix.cpp + // Decomposes the mode matrix to translations,rotation scale components + + template + GLM_FUNC_QUALIFIER bool decompose(mat<4, 4, T, Q> const& ModelMatrix, vec<3, T, Q> & Scale, qua & Orientation, vec<3, T, Q> & Translation, vec<3, T, Q> & Skew, vec<4, T, Q> & Perspective) + { + mat<4, 4, T, Q> LocalMatrix(ModelMatrix); + + // Normalize the matrix. + if(epsilonEqual(LocalMatrix[3][3], static_cast(0), epsilon())) + return false; + + for(length_t i = 0; i < 4; ++i) + for(length_t j = 0; j < 4; ++j) + LocalMatrix[i][j] /= LocalMatrix[3][3]; + + // perspectiveMatrix is used to solve for perspective, but it also provides + // an easy way to test for singularity of the upper 3x3 component. + mat<4, 4, T, Q> PerspectiveMatrix(LocalMatrix); + + for(length_t i = 0; i < 3; i++) + PerspectiveMatrix[i][3] = static_cast(0); + PerspectiveMatrix[3][3] = static_cast(1); + + /// TODO: Fixme! + if(epsilonEqual(determinant(PerspectiveMatrix), static_cast(0), epsilon())) + return false; + + // First, isolate perspective. This is the messiest. + if( + epsilonNotEqual(LocalMatrix[0][3], static_cast(0), epsilon()) || + epsilonNotEqual(LocalMatrix[1][3], static_cast(0), epsilon()) || + epsilonNotEqual(LocalMatrix[2][3], static_cast(0), epsilon())) + { + // rightHandSide is the right hand side of the equation. + vec<4, T, Q> RightHandSide; + RightHandSide[0] = LocalMatrix[0][3]; + RightHandSide[1] = LocalMatrix[1][3]; + RightHandSide[2] = LocalMatrix[2][3]; + RightHandSide[3] = LocalMatrix[3][3]; + + // Solve the equation by inverting PerspectiveMatrix and multiplying + // rightHandSide by the inverse. (This is the easiest way, not + // necessarily the best.) + mat<4, 4, T, Q> InversePerspectiveMatrix = glm::inverse(PerspectiveMatrix);// inverse(PerspectiveMatrix, inversePerspectiveMatrix); + mat<4, 4, T, Q> TransposedInversePerspectiveMatrix = glm::transpose(InversePerspectiveMatrix);// transposeMatrix4(inversePerspectiveMatrix, transposedInversePerspectiveMatrix); + + Perspective = TransposedInversePerspectiveMatrix * RightHandSide; + // v4MulPointByMatrix(rightHandSide, transposedInversePerspectiveMatrix, perspectivePoint); + + // Clear the perspective partition + LocalMatrix[0][3] = LocalMatrix[1][3] = LocalMatrix[2][3] = static_cast(0); + LocalMatrix[3][3] = static_cast(1); + } + else + { + // No perspective. + Perspective = vec<4, T, Q>(0, 0, 0, 1); + } + + // Next take care of translation (easy). + Translation = vec<3, T, Q>(LocalMatrix[3]); + LocalMatrix[3] = vec<4, T, Q>(0, 0, 0, LocalMatrix[3].w); + + vec<3, T, Q> Row[3], Pdum3; + + // Now get scale and shear. + for(length_t i = 0; i < 3; ++i) + for(length_t j = 0; j < 3; ++j) + Row[i][j] = LocalMatrix[i][j]; + + // Compute X scale factor and normalize first row. + Scale.x = length(Row[0]);// v3Length(Row[0]); + + Row[0] = detail::scale(Row[0], static_cast(1)); + + // Compute XY shear factor and make 2nd row orthogonal to 1st. + Skew.z = dot(Row[0], Row[1]); + Row[1] = detail::combine(Row[1], Row[0], static_cast(1), -Skew.z); + + // Now, compute Y scale and normalize 2nd row. + Scale.y = length(Row[1]); + Row[1] = detail::scale(Row[1], static_cast(1)); + Skew.z /= Scale.y; + + // Compute XZ and YZ shears, orthogonalize 3rd row. + Skew.y = glm::dot(Row[0], Row[2]); + Row[2] = detail::combine(Row[2], Row[0], static_cast(1), -Skew.y); + Skew.x = glm::dot(Row[1], Row[2]); + Row[2] = detail::combine(Row[2], Row[1], static_cast(1), -Skew.x); + + // Next, get Z scale and normalize 3rd row. + Scale.z = length(Row[2]); + Row[2] = detail::scale(Row[2], static_cast(1)); + Skew.y /= Scale.z; + Skew.x /= Scale.z; + + // At this point, the matrix (in rows[]) is orthonormal. + // Check for a coordinate system flip. If the determinant + // is -1, then negate the matrix and the scaling factors. + Pdum3 = cross(Row[1], Row[2]); // v3Cross(row[1], row[2], Pdum3); + if(dot(Row[0], Pdum3) < 0) + { + for(length_t i = 0; i < 3; i++) + { + Scale[i] *= static_cast(-1); + Row[i] *= static_cast(-1); + } + } + + // Now, get the rotations out, as described in the gem. + + // FIXME - Add the ability to return either quaternions (which are + // easier to recompose with) or Euler angles (rx, ry, rz), which + // are easier for authors to deal with. The latter will only be useful + // when we fix https://bugs.webkit.org/show_bug.cgi?id=23799, so I + // will leave the Euler angle code here for now. + + // ret.rotateY = asin(-Row[0][2]); + // if (cos(ret.rotateY) != 0) { + // ret.rotateX = atan2(Row[1][2], Row[2][2]); + // ret.rotateZ = atan2(Row[0][1], Row[0][0]); + // } else { + // ret.rotateX = atan2(-Row[2][0], Row[1][1]); + // ret.rotateZ = 0; + // } + + int i, j, k = 0; + T root, trace = Row[0].x + Row[1].y + Row[2].z; + if(trace > static_cast(0)) + { + root = sqrt(trace + static_cast(1.0)); + Orientation.w = static_cast(0.5) * root; + root = static_cast(0.5) / root; + Orientation.x = root * (Row[1].z - Row[2].y); + Orientation.y = root * (Row[2].x - Row[0].z); + Orientation.z = root * (Row[0].y - Row[1].x); + } // End if > 0 + else + { + static int Next[3] = {1, 2, 0}; + i = 0; + if(Row[1].y > Row[0].x) i = 1; + if(Row[2].z > Row[i][i]) i = 2; + j = Next[i]; + k = Next[j]; + + root = sqrt(Row[i][i] - Row[j][j] - Row[k][k] + static_cast(1.0)); + + Orientation[i] = static_cast(0.5) * root; + root = static_cast(0.5) / root; + Orientation[j] = root * (Row[i][j] + Row[j][i]); + Orientation[k] = root * (Row[i][k] + Row[k][i]); + Orientation.w = root * (Row[j][k] - Row[k][j]); + } // End if <= 0 + + return true; + } +}//namespace glm diff --git a/src/GLMath/glm/gtx/matrix_factorisation.hpp b/src/GLMath/glm/gtx/matrix_factorisation.hpp new file mode 100644 index 000000000..5a975d60b --- /dev/null +++ b/src/GLMath/glm/gtx/matrix_factorisation.hpp @@ -0,0 +1,69 @@ +/// @ref gtx_matrix_factorisation +/// @file glm/gtx/matrix_factorisation.hpp +/// +/// @see core (dependence) +/// +/// @defgroup gtx_matrix_factorisation GLM_GTX_matrix_factorisation +/// @ingroup gtx +/// +/// Include to use the features of this extension. +/// +/// Functions to factor matrices in various forms + +#pragma once + +// Dependency: +#include "../glm.hpp" + +#if GLM_MESSAGES == GLM_ENABLE && !defined(GLM_EXT_INCLUDED) +# ifndef GLM_ENABLE_EXPERIMENTAL +# pragma message("GLM: GLM_GTX_matrix_factorisation is an experimental extension and may change in the future. Use #define GLM_ENABLE_EXPERIMENTAL before including it, if you really want to use it.") +# else +# pragma message("GLM: GLM_GTX_matrix_factorisation extension included") +# endif +#endif + +/* +Suggestions: + - Move helper functions flipud and fliplr to another file: They may be helpful in more general circumstances. + - Implement other types of matrix factorisation, such as: QL and LQ, L(D)U, eigendecompositions, etc... +*/ + +namespace glm +{ + /// @addtogroup gtx_matrix_factorisation + /// @{ + + /// Flips the matrix rows up and down. + /// + /// From GLM_GTX_matrix_factorisation extension. + template + GLM_FUNC_DECL mat flipud(mat const& in); + + /// Flips the matrix columns right and left. + /// + /// From GLM_GTX_matrix_factorisation extension. + template + GLM_FUNC_DECL mat fliplr(mat const& in); + + /// Performs QR factorisation of a matrix. + /// Returns 2 matrices, q and r, such that the columns of q are orthonormal and span the same subspace than those of the input matrix, r is an upper triangular matrix, and q*r=in. + /// Given an n-by-m input matrix, q has dimensions min(n,m)-by-m, and r has dimensions n-by-min(n,m). + /// + /// From GLM_GTX_matrix_factorisation extension. + template + GLM_FUNC_DECL void qr_decompose(mat const& in, mat<(C < R ? C : R), R, T, Q>& q, mat& r); + + /// Performs RQ factorisation of a matrix. + /// Returns 2 matrices, r and q, such that r is an upper triangular matrix, the rows of q are orthonormal and span the same subspace than those of the input matrix, and r*q=in. + /// Note that in the context of RQ factorisation, the diagonal is seen as starting in the lower-right corner of the matrix, instead of the usual upper-left. + /// Given an n-by-m input matrix, r has dimensions min(n,m)-by-m, and q has dimensions n-by-min(n,m). + /// + /// From GLM_GTX_matrix_factorisation extension. + template + GLM_FUNC_DECL void rq_decompose(mat const& in, mat<(C < R ? C : R), R, T, Q>& r, mat& q); + + /// @} +} + +#include "matrix_factorisation.inl" diff --git a/src/GLMath/glm/gtx/matrix_factorisation.inl b/src/GLMath/glm/gtx/matrix_factorisation.inl new file mode 100644 index 000000000..c479b8ad9 --- /dev/null +++ b/src/GLMath/glm/gtx/matrix_factorisation.inl @@ -0,0 +1,84 @@ +/// @ref gtx_matrix_factorisation + +namespace glm +{ + template + GLM_FUNC_QUALIFIER mat flipud(mat const& in) + { + mat tin = transpose(in); + tin = fliplr(tin); + mat out = transpose(tin); + + return out; + } + + template + GLM_FUNC_QUALIFIER mat fliplr(mat const& in) + { + mat out; + for (length_t i = 0; i < C; i++) + { + out[i] = in[(C - i) - 1]; + } + + return out; + } + + template + GLM_FUNC_QUALIFIER void qr_decompose(mat const& in, mat<(C < R ? C : R), R, T, Q>& q, mat& r) + { + // Uses modified Gram-Schmidt method + // Source: https://en.wikipedia.org/wiki/Gram–Schmidt_process + // And https://en.wikipedia.org/wiki/QR_decomposition + + //For all the linearly independs columns of the input... + // (there can be no more linearly independents columns than there are rows.) + for (length_t i = 0; i < (C < R ? C : R); i++) + { + //Copy in Q the input's i-th column. + q[i] = in[i]; + + //j = [0,i[ + // Make that column orthogonal to all the previous ones by substracting to it the non-orthogonal projection of all the previous columns. + // Also: Fill the zero elements of R + for (length_t j = 0; j < i; j++) + { + q[i] -= dot(q[i], q[j])*q[j]; + r[j][i] = 0; + } + + //Now, Q i-th column is orthogonal to all the previous columns. Normalize it. + q[i] = normalize(q[i]); + + //j = [i,C[ + //Finally, compute the corresponding coefficients of R by computing the projection of the resulting column on the other columns of the input. + for (length_t j = i; j < C; j++) + { + r[j][i] = dot(in[j], q[i]); + } + } + } + + template + GLM_FUNC_QUALIFIER void rq_decompose(mat const& in, mat<(C < R ? C : R), R, T, Q>& r, mat& q) + { + // From https://en.wikipedia.org/wiki/QR_decomposition: + // The RQ decomposition transforms a matrix A into the product of an upper triangular matrix R (also known as right-triangular) and an orthogonal matrix Q. The only difference from QR decomposition is the order of these matrices. + // QR decomposition is Gram–Schmidt orthogonalization of columns of A, started from the first column. + // RQ decomposition is Gram–Schmidt orthogonalization of rows of A, started from the last row. + + mat tin = transpose(in); + tin = fliplr(tin); + + mat tr; + mat<(C < R ? C : R), C, T, Q> tq; + qr_decompose(tin, tq, tr); + + tr = fliplr(tr); + r = transpose(tr); + r = fliplr(r); + + tq = fliplr(tq); + q = transpose(tq); + } +} //namespace glm diff --git a/src/GLMath/glm/gtx/matrix_interpolation.hpp b/src/GLMath/glm/gtx/matrix_interpolation.hpp new file mode 100644 index 000000000..7d5ad4cd9 --- /dev/null +++ b/src/GLMath/glm/gtx/matrix_interpolation.hpp @@ -0,0 +1,60 @@ +/// @ref gtx_matrix_interpolation +/// @file glm/gtx/matrix_interpolation.hpp +/// @author Ghenadii Ursachi (the.asteroth@gmail.com) +/// +/// @see core (dependence) +/// +/// @defgroup gtx_matrix_interpolation GLM_GTX_matrix_interpolation +/// @ingroup gtx +/// +/// Include to use the features of this extension. +/// +/// Allows to directly interpolate two matrices. + +#pragma once + +// Dependency: +#include "../glm.hpp" + +#if GLM_MESSAGES == GLM_ENABLE && !defined(GLM_EXT_INCLUDED) +# ifndef GLM_ENABLE_EXPERIMENTAL +# pragma message("GLM: GLM_GTX_matrix_interpolation is an experimental extension and may change in the future. Use #define GLM_ENABLE_EXPERIMENTAL before including it, if you really want to use it.") +# else +# pragma message("GLM: GLM_GTX_matrix_interpolation extension included") +# endif +#endif + +namespace glm +{ + /// @addtogroup gtx_matrix_interpolation + /// @{ + + /// Get the axis and angle of the rotation from a matrix. + /// From GLM_GTX_matrix_interpolation extension. + template + GLM_FUNC_DECL void axisAngle( + mat<4, 4, T, Q> const& Mat, vec<3, T, Q> & Axis, T & Angle); + + /// Build a matrix from axis and angle. + /// From GLM_GTX_matrix_interpolation extension. + template + GLM_FUNC_DECL mat<4, 4, T, Q> axisAngleMatrix( + vec<3, T, Q> const& Axis, T const Angle); + + /// Extracts the rotation part of a matrix. + /// From GLM_GTX_matrix_interpolation extension. + template + GLM_FUNC_DECL mat<4, 4, T, Q> extractMatrixRotation( + mat<4, 4, T, Q> const& Mat); + + /// Build a interpolation of 4 * 4 matrixes. + /// From GLM_GTX_matrix_interpolation extension. + /// Warning! works only with rotation and/or translation matrixes, scale will generate unexpected results. + template + GLM_FUNC_DECL mat<4, 4, T, Q> interpolate( + mat<4, 4, T, Q> const& m1, mat<4, 4, T, Q> const& m2, T const Delta); + + /// @} +}//namespace glm + +#include "matrix_interpolation.inl" diff --git a/src/GLMath/glm/gtx/matrix_interpolation.inl b/src/GLMath/glm/gtx/matrix_interpolation.inl new file mode 100644 index 000000000..de40b7d74 --- /dev/null +++ b/src/GLMath/glm/gtx/matrix_interpolation.inl @@ -0,0 +1,129 @@ +/// @ref gtx_matrix_interpolation + +#include "../gtc/constants.hpp" + +namespace glm +{ + template + GLM_FUNC_QUALIFIER void axisAngle(mat<4, 4, T, Q> const& m, vec<3, T, Q> & axis, T& angle) + { + T epsilon = static_cast(0.01); + T epsilon2 = static_cast(0.1); + + if((abs(m[1][0] - m[0][1]) < epsilon) && (abs(m[2][0] - m[0][2]) < epsilon) && (abs(m[2][1] - m[1][2]) < epsilon)) + { + if ((abs(m[1][0] + m[0][1]) < epsilon2) && (abs(m[2][0] + m[0][2]) < epsilon2) && (abs(m[2][1] + m[1][2]) < epsilon2) && (abs(m[0][0] + m[1][1] + m[2][2] - static_cast(3.0)) < epsilon2)) + { + angle = static_cast(0.0); + axis.x = static_cast(1.0); + axis.y = static_cast(0.0); + axis.z = static_cast(0.0); + return; + } + angle = static_cast(3.1415926535897932384626433832795); + T xx = (m[0][0] + static_cast(1.0)) * static_cast(0.5); + T yy = (m[1][1] + static_cast(1.0)) * static_cast(0.5); + T zz = (m[2][2] + static_cast(1.0)) * static_cast(0.5); + T xy = (m[1][0] + m[0][1]) * static_cast(0.25); + T xz = (m[2][0] + m[0][2]) * static_cast(0.25); + T yz = (m[2][1] + m[1][2]) * static_cast(0.25); + if((xx > yy) && (xx > zz)) + { + if(xx < epsilon) + { + axis.x = static_cast(0.0); + axis.y = static_cast(0.7071); + axis.z = static_cast(0.7071); + } + else + { + axis.x = sqrt(xx); + axis.y = xy / axis.x; + axis.z = xz / axis.x; + } + } + else if (yy > zz) + { + if(yy < epsilon) + { + axis.x = static_cast(0.7071); + axis.y = static_cast(0.0); + axis.z = static_cast(0.7071); + } + else + { + axis.y = sqrt(yy); + axis.x = xy / axis.y; + axis.z = yz / axis.y; + } + } + else + { + if (zz < epsilon) + { + axis.x = static_cast(0.7071); + axis.y = static_cast(0.7071); + axis.z = static_cast(0.0); + } + else + { + axis.z = sqrt(zz); + axis.x = xz / axis.z; + axis.y = yz / axis.z; + } + } + return; + } + T s = sqrt((m[2][1] - m[1][2]) * (m[2][1] - m[1][2]) + (m[2][0] - m[0][2]) * (m[2][0] - m[0][2]) + (m[1][0] - m[0][1]) * (m[1][0] - m[0][1])); + if (glm::abs(s) < T(0.001)) + s = static_cast(1); + T const angleCos = (m[0][0] + m[1][1] + m[2][2] - static_cast(1)) * static_cast(0.5); + if(angleCos - static_cast(1) < epsilon) + angle = pi() * static_cast(0.25); + else + angle = acos(angleCos); + axis.x = (m[1][2] - m[2][1]) / s; + axis.y = (m[2][0] - m[0][2]) / s; + axis.z = (m[0][1] - m[1][0]) / s; + } + + template + GLM_FUNC_QUALIFIER mat<4, 4, T, Q> axisAngleMatrix(vec<3, T, Q> const& axis, T const angle) + { + T c = cos(angle); + T s = sin(angle); + T t = static_cast(1) - c; + vec<3, T, Q> n = normalize(axis); + + return mat<4, 4, T, Q>( + t * n.x * n.x + c, t * n.x * n.y + n.z * s, t * n.x * n.z - n.y * s, static_cast(0.0), + t * n.x * n.y - n.z * s, t * n.y * n.y + c, t * n.y * n.z + n.x * s, static_cast(0.0), + t * n.x * n.z + n.y * s, t * n.y * n.z - n.x * s, t * n.z * n.z + c, static_cast(0.0), + static_cast(0.0), static_cast(0.0), static_cast(0.0), static_cast(1.0)); + } + + template + GLM_FUNC_QUALIFIER mat<4, 4, T, Q> extractMatrixRotation(mat<4, 4, T, Q> const& m) + { + return mat<4, 4, T, Q>( + m[0][0], m[0][1], m[0][2], static_cast(0.0), + m[1][0], m[1][1], m[1][2], static_cast(0.0), + m[2][0], m[2][1], m[2][2], static_cast(0.0), + static_cast(0.0), static_cast(0.0), static_cast(0.0), static_cast(1.0)); + } + + template + GLM_FUNC_QUALIFIER mat<4, 4, T, Q> interpolate(mat<4, 4, T, Q> const& m1, mat<4, 4, T, Q> const& m2, T const delta) + { + mat<4, 4, T, Q> m1rot = extractMatrixRotation(m1); + mat<4, 4, T, Q> dltRotation = m2 * transpose(m1rot); + vec<3, T, Q> dltAxis; + T dltAngle; + axisAngle(dltRotation, dltAxis, dltAngle); + mat<4, 4, T, Q> out = axisAngleMatrix(dltAxis, dltAngle * delta) * m1rot; + out[3][0] = m1[3][0] + delta * (m2[3][0] - m1[3][0]); + out[3][1] = m1[3][1] + delta * (m2[3][1] - m1[3][1]); + out[3][2] = m1[3][2] + delta * (m2[3][2] - m1[3][2]); + return out; + } +}//namespace glm diff --git a/src/GLMath/glm/gtx/matrix_major_storage.hpp b/src/GLMath/glm/gtx/matrix_major_storage.hpp new file mode 100644 index 000000000..8c6bc22d1 --- /dev/null +++ b/src/GLMath/glm/gtx/matrix_major_storage.hpp @@ -0,0 +1,119 @@ +/// @ref gtx_matrix_major_storage +/// @file glm/gtx/matrix_major_storage.hpp +/// +/// @see core (dependence) +/// @see gtx_extented_min_max (dependence) +/// +/// @defgroup gtx_matrix_major_storage GLM_GTX_matrix_major_storage +/// @ingroup gtx +/// +/// Include to use the features of this extension. +/// +/// Build matrices with specific matrix order, row or column + +#pragma once + +// Dependency: +#include "../glm.hpp" + +#if GLM_MESSAGES == GLM_ENABLE && !defined(GLM_EXT_INCLUDED) +# ifndef GLM_ENABLE_EXPERIMENTAL +# pragma message("GLM: GLM_GTX_matrix_major_storage is an experimental extension and may change in the future. Use #define GLM_ENABLE_EXPERIMENTAL before including it, if you really want to use it.") +# else +# pragma message("GLM: GLM_GTX_matrix_major_storage extension included") +# endif +#endif + +namespace glm +{ + /// @addtogroup gtx_matrix_major_storage + /// @{ + + //! Build a row major matrix from row vectors. + //! From GLM_GTX_matrix_major_storage extension. + template + GLM_FUNC_DECL mat<2, 2, T, Q> rowMajor2( + vec<2, T, Q> const& v1, + vec<2, T, Q> const& v2); + + //! Build a row major matrix from other matrix. + //! From GLM_GTX_matrix_major_storage extension. + template + GLM_FUNC_DECL mat<2, 2, T, Q> rowMajor2( + mat<2, 2, T, Q> const& m); + + //! Build a row major matrix from row vectors. + //! From GLM_GTX_matrix_major_storage extension. + template + GLM_FUNC_DECL mat<3, 3, T, Q> rowMajor3( + vec<3, T, Q> const& v1, + vec<3, T, Q> const& v2, + vec<3, T, Q> const& v3); + + //! Build a row major matrix from other matrix. + //! From GLM_GTX_matrix_major_storage extension. + template + GLM_FUNC_DECL mat<3, 3, T, Q> rowMajor3( + mat<3, 3, T, Q> const& m); + + //! Build a row major matrix from row vectors. + //! From GLM_GTX_matrix_major_storage extension. + template + GLM_FUNC_DECL mat<4, 4, T, Q> rowMajor4( + vec<4, T, Q> const& v1, + vec<4, T, Q> const& v2, + vec<4, T, Q> const& v3, + vec<4, T, Q> const& v4); + + //! Build a row major matrix from other matrix. + //! From GLM_GTX_matrix_major_storage extension. + template + GLM_FUNC_DECL mat<4, 4, T, Q> rowMajor4( + mat<4, 4, T, Q> const& m); + + //! Build a column major matrix from column vectors. + //! From GLM_GTX_matrix_major_storage extension. + template + GLM_FUNC_DECL mat<2, 2, T, Q> colMajor2( + vec<2, T, Q> const& v1, + vec<2, T, Q> const& v2); + + //! Build a column major matrix from other matrix. + //! From GLM_GTX_matrix_major_storage extension. + template + GLM_FUNC_DECL mat<2, 2, T, Q> colMajor2( + mat<2, 2, T, Q> const& m); + + //! Build a column major matrix from column vectors. + //! From GLM_GTX_matrix_major_storage extension. + template + GLM_FUNC_DECL mat<3, 3, T, Q> colMajor3( + vec<3, T, Q> const& v1, + vec<3, T, Q> const& v2, + vec<3, T, Q> const& v3); + + //! Build a column major matrix from other matrix. + //! From GLM_GTX_matrix_major_storage extension. + template + GLM_FUNC_DECL mat<3, 3, T, Q> colMajor3( + mat<3, 3, T, Q> const& m); + + //! Build a column major matrix from column vectors. + //! From GLM_GTX_matrix_major_storage extension. + template + GLM_FUNC_DECL mat<4, 4, T, Q> colMajor4( + vec<4, T, Q> const& v1, + vec<4, T, Q> const& v2, + vec<4, T, Q> const& v3, + vec<4, T, Q> const& v4); + + //! Build a column major matrix from other matrix. + //! From GLM_GTX_matrix_major_storage extension. + template + GLM_FUNC_DECL mat<4, 4, T, Q> colMajor4( + mat<4, 4, T, Q> const& m); + + /// @} +}//namespace glm + +#include "matrix_major_storage.inl" diff --git a/src/GLMath/glm/gtx/matrix_major_storage.inl b/src/GLMath/glm/gtx/matrix_major_storage.inl new file mode 100644 index 000000000..279dd3433 --- /dev/null +++ b/src/GLMath/glm/gtx/matrix_major_storage.inl @@ -0,0 +1,166 @@ +/// @ref gtx_matrix_major_storage + +namespace glm +{ + template + GLM_FUNC_QUALIFIER mat<2, 2, T, Q> rowMajor2 + ( + vec<2, T, Q> const& v1, + vec<2, T, Q> const& v2 + ) + { + mat<2, 2, T, Q> Result; + Result[0][0] = v1.x; + Result[1][0] = v1.y; + Result[0][1] = v2.x; + Result[1][1] = v2.y; + return Result; + } + + template + GLM_FUNC_QUALIFIER mat<2, 2, T, Q> rowMajor2( + const mat<2, 2, T, Q>& m) + { + mat<2, 2, T, Q> Result; + Result[0][0] = m[0][0]; + Result[0][1] = m[1][0]; + Result[1][0] = m[0][1]; + Result[1][1] = m[1][1]; + return Result; + } + + template + GLM_FUNC_QUALIFIER mat<3, 3, T, Q> rowMajor3( + const vec<3, T, Q>& v1, + const vec<3, T, Q>& v2, + const vec<3, T, Q>& v3) + { + mat<3, 3, T, Q> Result; + Result[0][0] = v1.x; + Result[1][0] = v1.y; + Result[2][0] = v1.z; + Result[0][1] = v2.x; + Result[1][1] = v2.y; + Result[2][1] = v2.z; + Result[0][2] = v3.x; + Result[1][2] = v3.y; + Result[2][2] = v3.z; + return Result; + } + + template + GLM_FUNC_QUALIFIER mat<3, 3, T, Q> rowMajor3( + const mat<3, 3, T, Q>& m) + { + mat<3, 3, T, Q> Result; + Result[0][0] = m[0][0]; + Result[0][1] = m[1][0]; + Result[0][2] = m[2][0]; + Result[1][0] = m[0][1]; + Result[1][1] = m[1][1]; + Result[1][2] = m[2][1]; + Result[2][0] = m[0][2]; + Result[2][1] = m[1][2]; + Result[2][2] = m[2][2]; + return Result; + } + + template + GLM_FUNC_QUALIFIER mat<4, 4, T, Q> rowMajor4( + const vec<4, T, Q>& v1, + const vec<4, T, Q>& v2, + const vec<4, T, Q>& v3, + const vec<4, T, Q>& v4) + { + mat<4, 4, T, Q> Result; + Result[0][0] = v1.x; + Result[1][0] = v1.y; + Result[2][0] = v1.z; + Result[3][0] = v1.w; + Result[0][1] = v2.x; + Result[1][1] = v2.y; + Result[2][1] = v2.z; + Result[3][1] = v2.w; + Result[0][2] = v3.x; + Result[1][2] = v3.y; + Result[2][2] = v3.z; + Result[3][2] = v3.w; + Result[0][3] = v4.x; + Result[1][3] = v4.y; + Result[2][3] = v4.z; + Result[3][3] = v4.w; + return Result; + } + + template + GLM_FUNC_QUALIFIER mat<4, 4, T, Q> rowMajor4( + const mat<4, 4, T, Q>& m) + { + mat<4, 4, T, Q> Result; + Result[0][0] = m[0][0]; + Result[0][1] = m[1][0]; + Result[0][2] = m[2][0]; + Result[0][3] = m[3][0]; + Result[1][0] = m[0][1]; + Result[1][1] = m[1][1]; + Result[1][2] = m[2][1]; + Result[1][3] = m[3][1]; + Result[2][0] = m[0][2]; + Result[2][1] = m[1][2]; + Result[2][2] = m[2][2]; + Result[2][3] = m[3][2]; + Result[3][0] = m[0][3]; + Result[3][1] = m[1][3]; + Result[3][2] = m[2][3]; + Result[3][3] = m[3][3]; + return Result; + } + + template + GLM_FUNC_QUALIFIER mat<2, 2, T, Q> colMajor2( + const vec<2, T, Q>& v1, + const vec<2, T, Q>& v2) + { + return mat<2, 2, T, Q>(v1, v2); + } + + template + GLM_FUNC_QUALIFIER mat<2, 2, T, Q> colMajor2( + const mat<2, 2, T, Q>& m) + { + return mat<2, 2, T, Q>(m); + } + + template + GLM_FUNC_QUALIFIER mat<3, 3, T, Q> colMajor3( + const vec<3, T, Q>& v1, + const vec<3, T, Q>& v2, + const vec<3, T, Q>& v3) + { + return mat<3, 3, T, Q>(v1, v2, v3); + } + + template + GLM_FUNC_QUALIFIER mat<3, 3, T, Q> colMajor3( + const mat<3, 3, T, Q>& m) + { + return mat<3, 3, T, Q>(m); + } + + template + GLM_FUNC_QUALIFIER mat<4, 4, T, Q> colMajor4( + const vec<4, T, Q>& v1, + const vec<4, T, Q>& v2, + const vec<4, T, Q>& v3, + const vec<4, T, Q>& v4) + { + return mat<4, 4, T, Q>(v1, v2, v3, v4); + } + + template + GLM_FUNC_QUALIFIER mat<4, 4, T, Q> colMajor4( + const mat<4, 4, T, Q>& m) + { + return mat<4, 4, T, Q>(m); + } +}//namespace glm diff --git a/src/GLMath/glm/gtx/matrix_operation.hpp b/src/GLMath/glm/gtx/matrix_operation.hpp new file mode 100644 index 000000000..de6ff1f86 --- /dev/null +++ b/src/GLMath/glm/gtx/matrix_operation.hpp @@ -0,0 +1,103 @@ +/// @ref gtx_matrix_operation +/// @file glm/gtx/matrix_operation.hpp +/// +/// @see core (dependence) +/// +/// @defgroup gtx_matrix_operation GLM_GTX_matrix_operation +/// @ingroup gtx +/// +/// Include to use the features of this extension. +/// +/// Build diagonal matrices from vectors. + +#pragma once + +// Dependency: +#include "../glm.hpp" + +#if GLM_MESSAGES == GLM_ENABLE && !defined(GLM_EXT_INCLUDED) +# ifndef GLM_ENABLE_EXPERIMENTAL +# pragma message("GLM: GLM_GTX_matrix_operation is an experimental extension and may change in the future. Use #define GLM_ENABLE_EXPERIMENTAL before including it, if you really want to use it.") +# else +# pragma message("GLM: GLM_GTX_matrix_operation extension included") +# endif +#endif + +namespace glm +{ + /// @addtogroup gtx_matrix_operation + /// @{ + + //! Build a diagonal matrix. + //! From GLM_GTX_matrix_operation extension. + template + GLM_FUNC_DECL mat<2, 2, T, Q> diagonal2x2( + vec<2, T, Q> const& v); + + //! Build a diagonal matrix. + //! From GLM_GTX_matrix_operation extension. + template + GLM_FUNC_DECL mat<2, 3, T, Q> diagonal2x3( + vec<2, T, Q> const& v); + + //! Build a diagonal matrix. + //! From GLM_GTX_matrix_operation extension. + template + GLM_FUNC_DECL mat<2, 4, T, Q> diagonal2x4( + vec<2, T, Q> const& v); + + //! Build a diagonal matrix. + //! From GLM_GTX_matrix_operation extension. + template + GLM_FUNC_DECL mat<3, 2, T, Q> diagonal3x2( + vec<2, T, Q> const& v); + + //! Build a diagonal matrix. + //! From GLM_GTX_matrix_operation extension. + template + GLM_FUNC_DECL mat<3, 3, T, Q> diagonal3x3( + vec<3, T, Q> const& v); + + //! Build a diagonal matrix. + //! From GLM_GTX_matrix_operation extension. + template + GLM_FUNC_DECL mat<3, 4, T, Q> diagonal3x4( + vec<3, T, Q> const& v); + + //! Build a diagonal matrix. + //! From GLM_GTX_matrix_operation extension. + template + GLM_FUNC_DECL mat<4, 2, T, Q> diagonal4x2( + vec<2, T, Q> const& v); + + //! Build a diagonal matrix. + //! From GLM_GTX_matrix_operation extension. + template + GLM_FUNC_DECL mat<4, 3, T, Q> diagonal4x3( + vec<3, T, Q> const& v); + + //! Build a diagonal matrix. + //! From GLM_GTX_matrix_operation extension. + template + GLM_FUNC_DECL mat<4, 4, T, Q> diagonal4x4( + vec<4, T, Q> const& v); + + /// Build an adjugate matrix. + /// From GLM_GTX_matrix_operation extension. + template + GLM_FUNC_DECL mat<2, 2, T, Q> adjugate(mat<2, 2, T, Q> const& m); + + /// Build an adjugate matrix. + /// From GLM_GTX_matrix_operation extension. + template + GLM_FUNC_DECL mat<3, 3, T, Q> adjugate(mat<3, 3, T, Q> const& m); + + /// Build an adjugate matrix. + /// From GLM_GTX_matrix_operation extension. + template + GLM_FUNC_DECL mat<4, 4, T, Q> adjugate(mat<4, 4, T, Q> const& m); + + /// @} +}//namespace glm + +#include "matrix_operation.inl" diff --git a/src/GLMath/glm/gtx/matrix_operation.inl b/src/GLMath/glm/gtx/matrix_operation.inl new file mode 100644 index 000000000..9de83f823 --- /dev/null +++ b/src/GLMath/glm/gtx/matrix_operation.inl @@ -0,0 +1,176 @@ +/// @ref gtx_matrix_operation + +namespace glm +{ + template + GLM_FUNC_QUALIFIER mat<2, 2, T, Q> diagonal2x2 + ( + vec<2, T, Q> const& v + ) + { + mat<2, 2, T, Q> Result(static_cast(1)); + Result[0][0] = v[0]; + Result[1][1] = v[1]; + return Result; + } + + template + GLM_FUNC_QUALIFIER mat<2, 3, T, Q> diagonal2x3 + ( + vec<2, T, Q> const& v + ) + { + mat<2, 3, T, Q> Result(static_cast(1)); + Result[0][0] = v[0]; + Result[1][1] = v[1]; + return Result; + } + + template + GLM_FUNC_QUALIFIER mat<2, 4, T, Q> diagonal2x4 + ( + vec<2, T, Q> const& v + ) + { + mat<2, 4, T, Q> Result(static_cast(1)); + Result[0][0] = v[0]; + Result[1][1] = v[1]; + return Result; + } + + template + GLM_FUNC_QUALIFIER mat<3, 2, T, Q> diagonal3x2 + ( + vec<2, T, Q> const& v + ) + { + mat<3, 2, T, Q> Result(static_cast(1)); + Result[0][0] = v[0]; + Result[1][1] = v[1]; + return Result; + } + + template + GLM_FUNC_QUALIFIER mat<3, 3, T, Q> diagonal3x3 + ( + vec<3, T, Q> const& v + ) + { + mat<3, 3, T, Q> Result(static_cast(1)); + Result[0][0] = v[0]; + Result[1][1] = v[1]; + Result[2][2] = v[2]; + return Result; + } + + template + GLM_FUNC_QUALIFIER mat<3, 4, T, Q> diagonal3x4 + ( + vec<3, T, Q> const& v + ) + { + mat<3, 4, T, Q> Result(static_cast(1)); + Result[0][0] = v[0]; + Result[1][1] = v[1]; + Result[2][2] = v[2]; + return Result; + } + + template + GLM_FUNC_QUALIFIER mat<4, 4, T, Q> diagonal4x4 + ( + vec<4, T, Q> const& v + ) + { + mat<4, 4, T, Q> Result(static_cast(1)); + Result[0][0] = v[0]; + Result[1][1] = v[1]; + Result[2][2] = v[2]; + Result[3][3] = v[3]; + return Result; + } + + template + GLM_FUNC_QUALIFIER mat<4, 3, T, Q> diagonal4x3 + ( + vec<3, T, Q> const& v + ) + { + mat<4, 3, T, Q> Result(static_cast(1)); + Result[0][0] = v[0]; + Result[1][1] = v[1]; + Result[2][2] = v[2]; + return Result; + } + + template + GLM_FUNC_QUALIFIER mat<4, 2, T, Q> diagonal4x2 + ( + vec<2, T, Q> const& v + ) + { + mat<4, 2, T, Q> Result(static_cast(1)); + Result[0][0] = v[0]; + Result[1][1] = v[1]; + return Result; + } + + template + GLM_FUNC_QUALIFIER mat<2, 2, T, Q> adjugate(mat<2, 2, T, Q> const& m) + { + return mat<2, 2, T, Q>( + +m[1][1], -m[1][0], + -m[0][1], +m[0][0]); + } + + template + GLM_FUNC_QUALIFIER mat<3, 3, T, Q> adjugate(mat<3, 3, T, Q> const& m) + { + T const m00 = determinant(mat<2, 2, T, Q>(m[1][1], m[2][1], m[1][2], m[2][2])); + T const m01 = determinant(mat<2, 2, T, Q>(m[0][1], m[2][1], m[0][2], m[2][2])); + T const m02 = determinant(mat<2, 2, T, Q>(m[0][1], m[1][1], m[0][2], m[1][2])); + + T const m10 = determinant(mat<2, 2, T, Q>(m[1][0], m[2][0], m[1][2], m[2][2])); + T const m11 = determinant(mat<2, 2, T, Q>(m[0][0], m[2][0], m[0][2], m[2][2])); + T const m12 = determinant(mat<2, 2, T, Q>(m[0][0], m[1][0], m[0][2], m[1][2])); + + T const m20 = determinant(mat<2, 2, T, Q>(m[1][0], m[2][0], m[1][1], m[2][1])); + T const m21 = determinant(mat<2, 2, T, Q>(m[0][0], m[2][0], m[0][1], m[2][1])); + T const m22 = determinant(mat<2, 2, T, Q>(m[0][0], m[1][0], m[0][1], m[1][1])); + + return mat<3, 3, T, Q>( + +m00, -m01, +m02, + -m10, +m11, -m12, + +m20, -m21, +m22); + } + + template + GLM_FUNC_QUALIFIER mat<4, 4, T, Q> adjugate(mat<4, 4, T, Q> const& m) + { + T const m00 = determinant(mat<3, 3, T, Q>(m[1][1], m[1][2], m[1][3], m[2][1], m[2][2], m[2][3], m[3][1], m[3][2], m[3][3])); + T const m01 = determinant(mat<3, 3, T, Q>(m[1][0], m[1][2], m[1][3], m[2][0], m[2][2], m[2][3], m[3][0], m[3][2], m[3][3])); + T const m02 = determinant(mat<3, 3, T, Q>(m[1][0], m[1][1], m[1][3], m[2][0], m[2][2], m[2][3], m[3][0], m[3][1], m[3][3])); + T const m03 = determinant(mat<3, 3, T, Q>(m[1][0], m[1][1], m[1][2], m[2][0], m[2][1], m[2][2], m[3][0], m[3][1], m[3][2])); + + T const m10 = determinant(mat<3, 3, T, Q>(m[0][1], m[0][2], m[0][3], m[2][1], m[2][2], m[2][3], m[3][1], m[3][2], m[3][3])); + T const m11 = determinant(mat<3, 3, T, Q>(m[0][0], m[0][2], m[0][3], m[2][0], m[2][2], m[2][3], m[3][0], m[3][2], m[3][3])); + T const m12 = determinant(mat<3, 3, T, Q>(m[0][0], m[0][1], m[0][3], m[2][0], m[2][1], m[2][3], m[3][0], m[3][1], m[3][3])); + T const m13 = determinant(mat<3, 3, T, Q>(m[0][0], m[0][1], m[0][2], m[2][0], m[2][1], m[2][2], m[3][0], m[3][1], m[3][2])); + + T const m20 = determinant(mat<3, 3, T, Q>(m[0][1], m[0][2], m[0][3], m[1][1], m[1][2], m[1][3], m[3][1], m[3][2], m[3][3])); + T const m21 = determinant(mat<3, 3, T, Q>(m[0][0], m[0][2], m[0][3], m[1][0], m[1][2], m[1][3], m[3][0], m[3][2], m[3][3])); + T const m22 = determinant(mat<3, 3, T, Q>(m[0][0], m[0][1], m[0][3], m[1][0], m[1][1], m[1][3], m[3][0], m[3][1], m[3][3])); + T const m23 = determinant(mat<3, 3, T, Q>(m[0][0], m[0][1], m[0][2], m[1][0], m[1][1], m[1][2], m[3][0], m[3][1], m[3][2])); + + T const m30 = determinant(mat<3, 3, T, Q>(m[0][1], m[0][2], m[0][3], m[1][1], m[1][2], m[1][3], m[2][1], m[2][2], m[2][3])); + T const m31 = determinant(mat<3, 3, T, Q>(m[0][0], m[0][2], m[0][3], m[1][0], m[1][2], m[1][3], m[2][0], m[2][2], m[2][3])); + T const m32 = determinant(mat<3, 3, T, Q>(m[0][0], m[0][1], m[0][3], m[1][0], m[1][1], m[1][3], m[2][0], m[2][1], m[2][3])); + T const m33 = determinant(mat<3, 3, T, Q>(m[0][0], m[0][1], m[0][2], m[1][0], m[1][1], m[1][2], m[2][0], m[2][1], m[2][2])); + + return mat<4, 4, T, Q>( + +m00, -m01, +m02, -m03, + -m10, +m11, -m12, +m13, + +m20, -m21, +m22, -m23, + -m30, +m31, -m32, +m33); + } +}//namespace glm diff --git a/src/GLMath/glm/gtx/matrix_query.hpp b/src/GLMath/glm/gtx/matrix_query.hpp new file mode 100644 index 000000000..8011b2b1d --- /dev/null +++ b/src/GLMath/glm/gtx/matrix_query.hpp @@ -0,0 +1,77 @@ +/// @ref gtx_matrix_query +/// @file glm/gtx/matrix_query.hpp +/// +/// @see core (dependence) +/// @see gtx_vector_query (dependence) +/// +/// @defgroup gtx_matrix_query GLM_GTX_matrix_query +/// @ingroup gtx +/// +/// Include to use the features of this extension. +/// +/// Query to evaluate matrix properties + +#pragma once + +// Dependency: +#include "../glm.hpp" +#include "../gtx/vector_query.hpp" +#include + +#if GLM_MESSAGES == GLM_ENABLE && !defined(GLM_EXT_INCLUDED) +# ifndef GLM_ENABLE_EXPERIMENTAL +# pragma message("GLM: GLM_GTX_matrix_query is an experimental extension and may change in the future. Use #define GLM_ENABLE_EXPERIMENTAL before including it, if you really want to use it.") +# else +# pragma message("GLM: GLM_GTX_matrix_query extension included") +# endif +#endif + +namespace glm +{ + /// @addtogroup gtx_matrix_query + /// @{ + + /// Return whether a matrix a null matrix. + /// From GLM_GTX_matrix_query extension. + template + GLM_FUNC_DECL bool isNull(mat<2, 2, T, Q> const& m, T const& epsilon); + + /// Return whether a matrix a null matrix. + /// From GLM_GTX_matrix_query extension. + template + GLM_FUNC_DECL bool isNull(mat<3, 3, T, Q> const& m, T const& epsilon); + + /// Return whether a matrix is a null matrix. + /// From GLM_GTX_matrix_query extension. + template + GLM_FUNC_DECL bool isNull(mat<4, 4, T, Q> const& m, T const& epsilon); + + /// Return whether a matrix is an identity matrix. + /// From GLM_GTX_matrix_query extension. + template class matType> + GLM_FUNC_DECL bool isIdentity(matType const& m, T const& epsilon); + + /// Return whether a matrix is a normalized matrix. + /// From GLM_GTX_matrix_query extension. + template + GLM_FUNC_DECL bool isNormalized(mat<2, 2, T, Q> const& m, T const& epsilon); + + /// Return whether a matrix is a normalized matrix. + /// From GLM_GTX_matrix_query extension. + template + GLM_FUNC_DECL bool isNormalized(mat<3, 3, T, Q> const& m, T const& epsilon); + + /// Return whether a matrix is a normalized matrix. + /// From GLM_GTX_matrix_query extension. + template + GLM_FUNC_DECL bool isNormalized(mat<4, 4, T, Q> const& m, T const& epsilon); + + /// Return whether a matrix is an orthonormalized matrix. + /// From GLM_GTX_matrix_query extension. + template class matType> + GLM_FUNC_DECL bool isOrthogonal(matType const& m, T const& epsilon); + + /// @} +}//namespace glm + +#include "matrix_query.inl" diff --git a/src/GLMath/glm/gtx/matrix_query.inl b/src/GLMath/glm/gtx/matrix_query.inl new file mode 100644 index 000000000..77bd23108 --- /dev/null +++ b/src/GLMath/glm/gtx/matrix_query.inl @@ -0,0 +1,113 @@ +/// @ref gtx_matrix_query + +namespace glm +{ + template + GLM_FUNC_QUALIFIER bool isNull(mat<2, 2, T, Q> const& m, T const& epsilon) + { + bool result = true; + for(length_t i = 0; result && i < m.length() ; ++i) + result = isNull(m[i], epsilon); + return result; + } + + template + GLM_FUNC_QUALIFIER bool isNull(mat<3, 3, T, Q> const& m, T const& epsilon) + { + bool result = true; + for(length_t i = 0; result && i < m.length() ; ++i) + result = isNull(m[i], epsilon); + return result; + } + + template + GLM_FUNC_QUALIFIER bool isNull(mat<4, 4, T, Q> const& m, T const& epsilon) + { + bool result = true; + for(length_t i = 0; result && i < m.length() ; ++i) + result = isNull(m[i], epsilon); + return result; + } + + template + GLM_FUNC_QUALIFIER bool isIdentity(mat const& m, T const& epsilon) + { + bool result = true; + for(length_t i = 0; result && i < m[0].length() ; ++i) + { + for(length_t j = 0; result && j < i ; ++j) + result = abs(m[i][j]) <= epsilon; + if(result) + result = abs(m[i][i] - 1) <= epsilon; + for(length_t j = i + 1; result && j < m.length(); ++j) + result = abs(m[i][j]) <= epsilon; + } + return result; + } + + template + GLM_FUNC_QUALIFIER bool isNormalized(mat<2, 2, T, Q> const& m, T const& epsilon) + { + bool result(true); + for(length_t i = 0; result && i < m.length(); ++i) + result = isNormalized(m[i], epsilon); + for(length_t i = 0; result && i < m.length(); ++i) + { + typename mat<2, 2, T, Q>::col_type v; + for(length_t j = 0; j < m.length(); ++j) + v[j] = m[j][i]; + result = isNormalized(v, epsilon); + } + return result; + } + + template + GLM_FUNC_QUALIFIER bool isNormalized(mat<3, 3, T, Q> const& m, T const& epsilon) + { + bool result(true); + for(length_t i = 0; result && i < m.length(); ++i) + result = isNormalized(m[i], epsilon); + for(length_t i = 0; result && i < m.length(); ++i) + { + typename mat<3, 3, T, Q>::col_type v; + for(length_t j = 0; j < m.length(); ++j) + v[j] = m[j][i]; + result = isNormalized(v, epsilon); + } + return result; + } + + template + GLM_FUNC_QUALIFIER bool isNormalized(mat<4, 4, T, Q> const& m, T const& epsilon) + { + bool result(true); + for(length_t i = 0; result && i < m.length(); ++i) + result = isNormalized(m[i], epsilon); + for(length_t i = 0; result && i < m.length(); ++i) + { + typename mat<4, 4, T, Q>::col_type v; + for(length_t j = 0; j < m.length(); ++j) + v[j] = m[j][i]; + result = isNormalized(v, epsilon); + } + return result; + } + + template + GLM_FUNC_QUALIFIER bool isOrthogonal(mat const& m, T const& epsilon) + { + bool result = true; + for(length_t i(0); result && i < m.length() - 1; ++i) + for(length_t j(i + 1); result && j < m.length(); ++j) + result = areOrthogonal(m[i], m[j], epsilon); + + if(result) + { + mat tmp = transpose(m); + for(length_t i(0); result && i < m.length() - 1 ; ++i) + for(length_t j(i + 1); result && j < m.length(); ++j) + result = areOrthogonal(tmp[i], tmp[j], epsilon); + } + return result; + } +}//namespace glm diff --git a/src/GLMath/glm/gtx/matrix_transform_2d.hpp b/src/GLMath/glm/gtx/matrix_transform_2d.hpp new file mode 100644 index 000000000..5f9c54021 --- /dev/null +++ b/src/GLMath/glm/gtx/matrix_transform_2d.hpp @@ -0,0 +1,81 @@ +/// @ref gtx_matrix_transform_2d +/// @file glm/gtx/matrix_transform_2d.hpp +/// @author Miguel Ãngel Pérez Martínez +/// +/// @see core (dependence) +/// +/// @defgroup gtx_matrix_transform_2d GLM_GTX_matrix_transform_2d +/// @ingroup gtx +/// +/// Include to use the features of this extension. +/// +/// Defines functions that generate common 2d transformation matrices. + +#pragma once + +// Dependency: +#include "../mat3x3.hpp" +#include "../vec2.hpp" + +#if GLM_MESSAGES == GLM_ENABLE && !defined(GLM_EXT_INCLUDED) +# ifndef GLM_ENABLE_EXPERIMENTAL +# pragma message("GLM: GLM_GTX_matrix_transform_2d is an experimental extension and may change in the future. Use #define GLM_ENABLE_EXPERIMENTAL before including it, if you really want to use it.") +# else +# pragma message("GLM: GLM_GTX_matrix_transform_2d extension included") +# endif +#endif + +namespace glm +{ + /// @addtogroup gtx_matrix_transform_2d + /// @{ + + /// Builds a translation 3 * 3 matrix created from a vector of 2 components. + /// + /// @param m Input matrix multiplied by this translation matrix. + /// @param v Coordinates of a translation vector. + template + GLM_FUNC_QUALIFIER mat<3, 3, T, Q> translate( + mat<3, 3, T, Q> const& m, + vec<2, T, Q> const& v); + + /// Builds a rotation 3 * 3 matrix created from an angle. + /// + /// @param m Input matrix multiplied by this translation matrix. + /// @param angle Rotation angle expressed in radians. + template + GLM_FUNC_QUALIFIER mat<3, 3, T, Q> rotate( + mat<3, 3, T, Q> const& m, + T angle); + + /// Builds a scale 3 * 3 matrix created from a vector of 2 components. + /// + /// @param m Input matrix multiplied by this translation matrix. + /// @param v Coordinates of a scale vector. + template + GLM_FUNC_QUALIFIER mat<3, 3, T, Q> scale( + mat<3, 3, T, Q> const& m, + vec<2, T, Q> const& v); + + /// Builds an horizontal (parallel to the x axis) shear 3 * 3 matrix. + /// + /// @param m Input matrix multiplied by this translation matrix. + /// @param y Shear factor. + template + GLM_FUNC_QUALIFIER mat<3, 3, T, Q> shearX( + mat<3, 3, T, Q> const& m, + T y); + + /// Builds a vertical (parallel to the y axis) shear 3 * 3 matrix. + /// + /// @param m Input matrix multiplied by this translation matrix. + /// @param x Shear factor. + template + GLM_FUNC_QUALIFIER mat<3, 3, T, Q> shearY( + mat<3, 3, T, Q> const& m, + T x); + + /// @} +}//namespace glm + +#include "matrix_transform_2d.inl" diff --git a/src/GLMath/glm/gtx/matrix_transform_2d.inl b/src/GLMath/glm/gtx/matrix_transform_2d.inl new file mode 100644 index 000000000..a68d24dc9 --- /dev/null +++ b/src/GLMath/glm/gtx/matrix_transform_2d.inl @@ -0,0 +1,68 @@ +/// @ref gtx_matrix_transform_2d +/// @author Miguel Ãngel Pérez Martínez + +#include "../trigonometric.hpp" + +namespace glm +{ + + template + GLM_FUNC_QUALIFIER mat<3, 3, T, Q> translate( + mat<3, 3, T, Q> const& m, + vec<2, T, Q> const& v) + { + mat<3, 3, T, Q> Result(m); + Result[2] = m[0] * v[0] + m[1] * v[1] + m[2]; + return Result; + } + + + template + GLM_FUNC_QUALIFIER mat<3, 3, T, Q> rotate( + mat<3, 3, T, Q> const& m, + T angle) + { + T const a = angle; + T const c = cos(a); + T const s = sin(a); + + mat<3, 3, T, Q> Result; + Result[0] = m[0] * c + m[1] * s; + Result[1] = m[0] * -s + m[1] * c; + Result[2] = m[2]; + return Result; + } + + template + GLM_FUNC_QUALIFIER mat<3, 3, T, Q> scale( + mat<3, 3, T, Q> const& m, + vec<2, T, Q> const& v) + { + mat<3, 3, T, Q> Result; + Result[0] = m[0] * v[0]; + Result[1] = m[1] * v[1]; + Result[2] = m[2]; + return Result; + } + + template + GLM_FUNC_QUALIFIER mat<3, 3, T, Q> shearX( + mat<3, 3, T, Q> const& m, + T y) + { + mat<3, 3, T, Q> Result(1); + Result[0][1] = y; + return m * Result; + } + + template + GLM_FUNC_QUALIFIER mat<3, 3, T, Q> shearY( + mat<3, 3, T, Q> const& m, + T x) + { + mat<3, 3, T, Q> Result(1); + Result[1][0] = x; + return m * Result; + } + +}//namespace glm diff --git a/src/GLMath/glm/gtx/mixed_product.hpp b/src/GLMath/glm/gtx/mixed_product.hpp new file mode 100644 index 000000000..b242e357e --- /dev/null +++ b/src/GLMath/glm/gtx/mixed_product.hpp @@ -0,0 +1,41 @@ +/// @ref gtx_mixed_product +/// @file glm/gtx/mixed_product.hpp +/// +/// @see core (dependence) +/// +/// @defgroup gtx_mixed_product GLM_GTX_mixed_producte +/// @ingroup gtx +/// +/// Include to use the features of this extension. +/// +/// Mixed product of 3 vectors. + +#pragma once + +// Dependency: +#include "../glm.hpp" + +#if GLM_MESSAGES == GLM_ENABLE && !defined(GLM_EXT_INCLUDED) +# ifndef GLM_ENABLE_EXPERIMENTAL +# pragma message("GLM: GLM_GTX_mixed_product is an experimental extension and may change in the future. Use #define GLM_ENABLE_EXPERIMENTAL before including it, if you really want to use it.") +# else +# pragma message("GLM: GLM_GTX_mixed_product extension included") +# endif +#endif + +namespace glm +{ + /// @addtogroup gtx_mixed_product + /// @{ + + /// @brief Mixed product of 3 vectors (from GLM_GTX_mixed_product extension) + template + GLM_FUNC_DECL T mixedProduct( + vec<3, T, Q> const& v1, + vec<3, T, Q> const& v2, + vec<3, T, Q> const& v3); + + /// @} +}// namespace glm + +#include "mixed_product.inl" diff --git a/src/GLMath/glm/gtx/mixed_product.inl b/src/GLMath/glm/gtx/mixed_product.inl new file mode 100644 index 000000000..e5cdbdb49 --- /dev/null +++ b/src/GLMath/glm/gtx/mixed_product.inl @@ -0,0 +1,15 @@ +/// @ref gtx_mixed_product + +namespace glm +{ + template + GLM_FUNC_QUALIFIER T mixedProduct + ( + vec<3, T, Q> const& v1, + vec<3, T, Q> const& v2, + vec<3, T, Q> const& v3 + ) + { + return dot(cross(v1, v2), v3); + } +}//namespace glm diff --git a/src/GLMath/glm/gtx/norm.hpp b/src/GLMath/glm/gtx/norm.hpp new file mode 100644 index 000000000..cdf60e05b --- /dev/null +++ b/src/GLMath/glm/gtx/norm.hpp @@ -0,0 +1,76 @@ +/// @ref gtx_norm +/// @file glm/gtx/norm.hpp +/// +/// @see core (dependence) +/// @see gtx_quaternion (dependence) +/// +/// @defgroup gtx_norm GLM_GTX_norm +/// @ingroup gtx +/// +/// Include to use the features of this extension. +/// +/// Various ways to compute vector norms. + +#pragma once + +// Dependency: +#include "../geometric.hpp" +#include "../gtx/quaternion.hpp" + +#if GLM_MESSAGES == GLM_ENABLE && !defined(GLM_EXT_INCLUDED) +# ifndef GLM_ENABLE_EXPERIMENTAL +# pragma message("GLM: GLM_GTX_norm is an experimental extension and may change in the future. Use #define GLM_ENABLE_EXPERIMENTAL before including it, if you really want to use it.") +# else +# pragma message("GLM: GLM_GTX_norm extension included") +# endif +#endif + +namespace glm +{ + /// @addtogroup gtx_norm + /// @{ + + /// Returns the squared length of x. + /// From GLM_GTX_norm extension. + template + GLM_FUNC_DECL T length2(vec const& x); + + /// Returns the squared distance between p0 and p1, i.e., length2(p0 - p1). + /// From GLM_GTX_norm extension. + template + GLM_FUNC_DECL T distance2(vec const& p0, vec const& p1); + + //! Returns the L1 norm between x and y. + //! From GLM_GTX_norm extension. + template + GLM_FUNC_DECL T l1Norm(vec<3, T, Q> const& x, vec<3, T, Q> const& y); + + //! Returns the L1 norm of v. + //! From GLM_GTX_norm extension. + template + GLM_FUNC_DECL T l1Norm(vec<3, T, Q> const& v); + + //! Returns the L2 norm between x and y. + //! From GLM_GTX_norm extension. + template + GLM_FUNC_DECL T l2Norm(vec<3, T, Q> const& x, vec<3, T, Q> const& y); + + //! Returns the L2 norm of v. + //! From GLM_GTX_norm extension. + template + GLM_FUNC_DECL T l2Norm(vec<3, T, Q> const& x); + + //! Returns the L norm between x and y. + //! From GLM_GTX_norm extension. + template + GLM_FUNC_DECL T lxNorm(vec<3, T, Q> const& x, vec<3, T, Q> const& y, unsigned int Depth); + + //! Returns the L norm of v. + //! From GLM_GTX_norm extension. + template + GLM_FUNC_DECL T lxNorm(vec<3, T, Q> const& x, unsigned int Depth); + + /// @} +}//namespace glm + +#include "norm.inl" diff --git a/src/GLMath/glm/gtx/norm.inl b/src/GLMath/glm/gtx/norm.inl new file mode 100644 index 000000000..c3f468c3a --- /dev/null +++ b/src/GLMath/glm/gtx/norm.inl @@ -0,0 +1,83 @@ +/// @ref gtx_norm + +#include "../detail/qualifier.hpp" + +namespace glm{ +namespace detail +{ + template + struct compute_length2 + { + GLM_FUNC_QUALIFIER static T call(vec const& v) + { + return dot(v, v); + } + }; +}//namespace detail + + template + GLM_FUNC_QUALIFIER genType length2(genType x) + { + GLM_STATIC_ASSERT(std::numeric_limits::is_iec559, "'length2' accepts only floating-point inputs"); + return x * x; + } + + template + GLM_FUNC_QUALIFIER T length2(vec const& v) + { + GLM_STATIC_ASSERT(std::numeric_limits::is_iec559, "'length2' accepts only floating-point inputs"); + return detail::compute_length2::value>::call(v); + } + + template + GLM_FUNC_QUALIFIER T distance2(T p0, T p1) + { + GLM_STATIC_ASSERT(std::numeric_limits::is_iec559, "'distance2' accepts only floating-point inputs"); + return length2(p1 - p0); + } + + template + GLM_FUNC_QUALIFIER T distance2(vec const& p0, vec const& p1) + { + GLM_STATIC_ASSERT(std::numeric_limits::is_iec559, "'distance2' accepts only floating-point inputs"); + return length2(p1 - p0); + } + + template + GLM_FUNC_QUALIFIER T l1Norm(vec<3, T, Q> const& a, vec<3, T, Q> const& b) + { + return abs(b.x - a.x) + abs(b.y - a.y) + abs(b.z - a.z); + } + + template + GLM_FUNC_QUALIFIER T l1Norm(vec<3, T, Q> const& v) + { + return abs(v.x) + abs(v.y) + abs(v.z); + } + + template + GLM_FUNC_QUALIFIER T l2Norm(vec<3, T, Q> const& a, vec<3, T, Q> const& b + ) + { + return length(b - a); + } + + template + GLM_FUNC_QUALIFIER T l2Norm(vec<3, T, Q> const& v) + { + return length(v); + } + + template + GLM_FUNC_QUALIFIER T lxNorm(vec<3, T, Q> const& x, vec<3, T, Q> const& y, unsigned int Depth) + { + return pow(pow(y.x - x.x, T(Depth)) + pow(y.y - x.y, T(Depth)) + pow(y.z - x.z, T(Depth)), T(1) / T(Depth)); + } + + template + GLM_FUNC_QUALIFIER T lxNorm(vec<3, T, Q> const& v, unsigned int Depth) + { + return pow(pow(v.x, T(Depth)) + pow(v.y, T(Depth)) + pow(v.z, T(Depth)), T(1) / T(Depth)); + } + +}//namespace glm diff --git a/src/GLMath/glm/gtx/normal.hpp b/src/GLMath/glm/gtx/normal.hpp new file mode 100644 index 000000000..068682f75 --- /dev/null +++ b/src/GLMath/glm/gtx/normal.hpp @@ -0,0 +1,41 @@ +/// @ref gtx_normal +/// @file glm/gtx/normal.hpp +/// +/// @see core (dependence) +/// @see gtx_extented_min_max (dependence) +/// +/// @defgroup gtx_normal GLM_GTX_normal +/// @ingroup gtx +/// +/// Include to use the features of this extension. +/// +/// Compute the normal of a triangle. + +#pragma once + +// Dependency: +#include "../glm.hpp" + +#if GLM_MESSAGES == GLM_ENABLE && !defined(GLM_EXT_INCLUDED) +# ifndef GLM_ENABLE_EXPERIMENTAL +# pragma message("GLM: GLM_GTX_normal is an experimental extension and may change in the future. Use #define GLM_ENABLE_EXPERIMENTAL before including it, if you really want to use it.") +# else +# pragma message("GLM: GLM_GTX_normal extension included") +# endif +#endif + +namespace glm +{ + /// @addtogroup gtx_normal + /// @{ + + /// Computes triangle normal from triangle points. + /// + /// @see gtx_normal + template + GLM_FUNC_DECL vec<3, T, Q> triangleNormal(vec<3, T, Q> const& p1, vec<3, T, Q> const& p2, vec<3, T, Q> const& p3); + + /// @} +}//namespace glm + +#include "normal.inl" diff --git a/src/GLMath/glm/gtx/normal.inl b/src/GLMath/glm/gtx/normal.inl new file mode 100644 index 000000000..74f9fc994 --- /dev/null +++ b/src/GLMath/glm/gtx/normal.inl @@ -0,0 +1,15 @@ +/// @ref gtx_normal + +namespace glm +{ + template + GLM_FUNC_QUALIFIER vec<3, T, Q> triangleNormal + ( + vec<3, T, Q> const& p1, + vec<3, T, Q> const& p2, + vec<3, T, Q> const& p3 + ) + { + return normalize(cross(p1 - p2, p1 - p3)); + } +}//namespace glm diff --git a/src/GLMath/glm/gtx/normalize_dot.hpp b/src/GLMath/glm/gtx/normalize_dot.hpp new file mode 100644 index 000000000..51958023f --- /dev/null +++ b/src/GLMath/glm/gtx/normalize_dot.hpp @@ -0,0 +1,49 @@ +/// @ref gtx_normalize_dot +/// @file glm/gtx/normalize_dot.hpp +/// +/// @see core (dependence) +/// @see gtx_fast_square_root (dependence) +/// +/// @defgroup gtx_normalize_dot GLM_GTX_normalize_dot +/// @ingroup gtx +/// +/// Include to use the features of this extension. +/// +/// Dot product of vectors that need to be normalize with a single square root. + +#pragma once + +// Dependency: +#include "../gtx/fast_square_root.hpp" + +#if GLM_MESSAGES == GLM_ENABLE && !defined(GLM_EXT_INCLUDED) +# ifndef GLM_ENABLE_EXPERIMENTAL +# pragma message("GLM: GLM_GTX_normalize_dot is an experimental extension and may change in the future. Use #define GLM_ENABLE_EXPERIMENTAL before including it, if you really want to use it.") +# else +# pragma message("GLM: GLM_GTX_normalize_dot extension included") +# endif +#endif + +namespace glm +{ + /// @addtogroup gtx_normalize_dot + /// @{ + + /// Normalize parameters and returns the dot product of x and y. + /// It's faster that dot(normalize(x), normalize(y)). + /// + /// @see gtx_normalize_dot extension. + template + GLM_FUNC_DECL T normalizeDot(vec const& x, vec const& y); + + /// Normalize parameters and returns the dot product of x and y. + /// Faster that dot(fastNormalize(x), fastNormalize(y)). + /// + /// @see gtx_normalize_dot extension. + template + GLM_FUNC_DECL T fastNormalizeDot(vec const& x, vec const& y); + + /// @} +}//namespace glm + +#include "normalize_dot.inl" diff --git a/src/GLMath/glm/gtx/normalize_dot.inl b/src/GLMath/glm/gtx/normalize_dot.inl new file mode 100644 index 000000000..7bcd9a534 --- /dev/null +++ b/src/GLMath/glm/gtx/normalize_dot.inl @@ -0,0 +1,16 @@ +/// @ref gtx_normalize_dot + +namespace glm +{ + template + GLM_FUNC_QUALIFIER T normalizeDot(vec const& x, vec const& y) + { + return glm::dot(x, y) * glm::inversesqrt(glm::dot(x, x) * glm::dot(y, y)); + } + + template + GLM_FUNC_QUALIFIER T fastNormalizeDot(vec const& x, vec const& y) + { + return glm::dot(x, y) * glm::fastInverseSqrt(glm::dot(x, x) * glm::dot(y, y)); + } +}//namespace glm diff --git a/src/GLMath/glm/gtx/number_precision.hpp b/src/GLMath/glm/gtx/number_precision.hpp new file mode 100644 index 000000000..3a606bda0 --- /dev/null +++ b/src/GLMath/glm/gtx/number_precision.hpp @@ -0,0 +1,61 @@ +/// @ref gtx_number_precision +/// @file glm/gtx/number_precision.hpp +/// +/// @see core (dependence) +/// @see gtc_type_precision (dependence) +/// @see gtc_quaternion (dependence) +/// +/// @defgroup gtx_number_precision GLM_GTX_number_precision +/// @ingroup gtx +/// +/// Include to use the features of this extension. +/// +/// Defined size types. + +#pragma once + +// Dependency: +#include "../glm.hpp" +#include "../gtc/type_precision.hpp" + +#if GLM_MESSAGES == GLM_ENABLE && !defined(GLM_EXT_INCLUDED) +# ifndef GLM_ENABLE_EXPERIMENTAL +# pragma message("GLM: GLM_GTX_number_precision is an experimental extension and may change in the future. Use #define GLM_ENABLE_EXPERIMENTAL before including it, if you really want to use it.") +# else +# pragma message("GLM: GLM_GTX_number_precision extension included") +# endif +#endif + +namespace glm{ +namespace gtx +{ + ///////////////////////////// + // Unsigned int vector types + + /// @addtogroup gtx_number_precision + /// @{ + + typedef u8 u8vec1; //!< \brief 8bit unsigned integer scalar. (from GLM_GTX_number_precision extension) + typedef u16 u16vec1; //!< \brief 16bit unsigned integer scalar. (from GLM_GTX_number_precision extension) + typedef u32 u32vec1; //!< \brief 32bit unsigned integer scalar. (from GLM_GTX_number_precision extension) + typedef u64 u64vec1; //!< \brief 64bit unsigned integer scalar. (from GLM_GTX_number_precision extension) + + ////////////////////// + // Float vector types + + typedef f32 f32vec1; //!< \brief Single-qualifier floating-point scalar. (from GLM_GTX_number_precision extension) + typedef f64 f64vec1; //!< \brief Single-qualifier floating-point scalar. (from GLM_GTX_number_precision extension) + + ////////////////////// + // Float matrix types + + typedef f32 f32mat1; //!< \brief Single-qualifier floating-point scalar. (from GLM_GTX_number_precision extension) + typedef f32 f32mat1x1; //!< \brief Single-qualifier floating-point scalar. (from GLM_GTX_number_precision extension) + typedef f64 f64mat1; //!< \brief Double-qualifier floating-point scalar. (from GLM_GTX_number_precision extension) + typedef f64 f64mat1x1; //!< \brief Double-qualifier floating-point scalar. (from GLM_GTX_number_precision extension) + + /// @} +}//namespace gtx +}//namespace glm + +#include "number_precision.inl" diff --git a/src/GLMath/glm/gtx/number_precision.inl b/src/GLMath/glm/gtx/number_precision.inl new file mode 100644 index 000000000..b39d71c3b --- /dev/null +++ b/src/GLMath/glm/gtx/number_precision.inl @@ -0,0 +1,6 @@ +/// @ref gtx_number_precision + +namespace glm +{ + +} diff --git a/src/GLMath/glm/gtx/optimum_pow.hpp b/src/GLMath/glm/gtx/optimum_pow.hpp new file mode 100644 index 000000000..9284a474d --- /dev/null +++ b/src/GLMath/glm/gtx/optimum_pow.hpp @@ -0,0 +1,54 @@ +/// @ref gtx_optimum_pow +/// @file glm/gtx/optimum_pow.hpp +/// +/// @see core (dependence) +/// +/// @defgroup gtx_optimum_pow GLM_GTX_optimum_pow +/// @ingroup gtx +/// +/// Include to use the features of this extension. +/// +/// Integer exponentiation of power functions. + +#pragma once + +// Dependency: +#include "../glm.hpp" + +#if GLM_MESSAGES == GLM_ENABLE && !defined(GLM_EXT_INCLUDED) +# ifndef GLM_ENABLE_EXPERIMENTAL +# pragma message("GLM: GLM_GTX_optimum_pow is an experimental extension and may change in the future. Use #define GLM_ENABLE_EXPERIMENTAL before including it, if you really want to use it.") +# else +# pragma message("GLM: GLM_GTX_optimum_pow extension included") +# endif +#endif + +namespace glm{ +namespace gtx +{ + /// @addtogroup gtx_optimum_pow + /// @{ + + /// Returns x raised to the power of 2. + /// + /// @see gtx_optimum_pow + template + GLM_FUNC_DECL genType pow2(genType const& x); + + /// Returns x raised to the power of 3. + /// + /// @see gtx_optimum_pow + template + GLM_FUNC_DECL genType pow3(genType const& x); + + /// Returns x raised to the power of 4. + /// + /// @see gtx_optimum_pow + template + GLM_FUNC_DECL genType pow4(genType const& x); + + /// @} +}//namespace gtx +}//namespace glm + +#include "optimum_pow.inl" diff --git a/src/GLMath/glm/gtx/optimum_pow.inl b/src/GLMath/glm/gtx/optimum_pow.inl new file mode 100644 index 000000000..a26c19c18 --- /dev/null +++ b/src/GLMath/glm/gtx/optimum_pow.inl @@ -0,0 +1,22 @@ +/// @ref gtx_optimum_pow + +namespace glm +{ + template + GLM_FUNC_QUALIFIER genType pow2(genType const& x) + { + return x * x; + } + + template + GLM_FUNC_QUALIFIER genType pow3(genType const& x) + { + return x * x * x; + } + + template + GLM_FUNC_QUALIFIER genType pow4(genType const& x) + { + return (x * x) * (x * x); + } +}//namespace glm diff --git a/src/GLMath/glm/gtx/orthonormalize.hpp b/src/GLMath/glm/gtx/orthonormalize.hpp new file mode 100644 index 000000000..3e004fb06 --- /dev/null +++ b/src/GLMath/glm/gtx/orthonormalize.hpp @@ -0,0 +1,49 @@ +/// @ref gtx_orthonormalize +/// @file glm/gtx/orthonormalize.hpp +/// +/// @see core (dependence) +/// @see gtx_extented_min_max (dependence) +/// +/// @defgroup gtx_orthonormalize GLM_GTX_orthonormalize +/// @ingroup gtx +/// +/// Include to use the features of this extension. +/// +/// Orthonormalize matrices. + +#pragma once + +// Dependency: +#include "../vec3.hpp" +#include "../mat3x3.hpp" +#include "../geometric.hpp" + +#if GLM_MESSAGES == GLM_ENABLE && !defined(GLM_EXT_INCLUDED) +# ifndef GLM_ENABLE_EXPERIMENTAL +# pragma message("GLM: GLM_GTX_orthonormalize is an experimental extension and may change in the future. Use #define GLM_ENABLE_EXPERIMENTAL before including it, if you really want to use it.") +# else +# pragma message("GLM: GLM_GTX_orthonormalize extension included") +# endif +#endif + +namespace glm +{ + /// @addtogroup gtx_orthonormalize + /// @{ + + /// Returns the orthonormalized matrix of m. + /// + /// @see gtx_orthonormalize + template + GLM_FUNC_DECL mat<3, 3, T, Q> orthonormalize(mat<3, 3, T, Q> const& m); + + /// Orthonormalizes x according y. + /// + /// @see gtx_orthonormalize + template + GLM_FUNC_DECL vec<3, T, Q> orthonormalize(vec<3, T, Q> const& x, vec<3, T, Q> const& y); + + /// @} +}//namespace glm + +#include "orthonormalize.inl" diff --git a/src/GLMath/glm/gtx/orthonormalize.inl b/src/GLMath/glm/gtx/orthonormalize.inl new file mode 100644 index 000000000..cb553ba62 --- /dev/null +++ b/src/GLMath/glm/gtx/orthonormalize.inl @@ -0,0 +1,29 @@ +/// @ref gtx_orthonormalize + +namespace glm +{ + template + GLM_FUNC_QUALIFIER mat<3, 3, T, Q> orthonormalize(mat<3, 3, T, Q> const& m) + { + mat<3, 3, T, Q> r = m; + + r[0] = normalize(r[0]); + + T d0 = dot(r[0], r[1]); + r[1] -= r[0] * d0; + r[1] = normalize(r[1]); + + T d1 = dot(r[1], r[2]); + d0 = dot(r[0], r[2]); + r[2] -= r[0] * d0 + r[1] * d1; + r[2] = normalize(r[2]); + + return r; + } + + template + GLM_FUNC_QUALIFIER vec<3, T, Q> orthonormalize(vec<3, T, Q> const& x, vec<3, T, Q> const& y) + { + return normalize(x - y * dot(y, x)); + } +}//namespace glm diff --git a/src/GLMath/glm/gtx/perpendicular.hpp b/src/GLMath/glm/gtx/perpendicular.hpp new file mode 100644 index 000000000..72b77b6e2 --- /dev/null +++ b/src/GLMath/glm/gtx/perpendicular.hpp @@ -0,0 +1,41 @@ +/// @ref gtx_perpendicular +/// @file glm/gtx/perpendicular.hpp +/// +/// @see core (dependence) +/// @see gtx_projection (dependence) +/// +/// @defgroup gtx_perpendicular GLM_GTX_perpendicular +/// @ingroup gtx +/// +/// Include to use the features of this extension. +/// +/// Perpendicular of a vector from other one + +#pragma once + +// Dependency: +#include "../glm.hpp" +#include "../gtx/projection.hpp" + +#if GLM_MESSAGES == GLM_ENABLE && !defined(GLM_EXT_INCLUDED) +# ifndef GLM_ENABLE_EXPERIMENTAL +# pragma message("GLM: GLM_GTX_perpendicular is an experimental extension and may change in the future. Use #define GLM_ENABLE_EXPERIMENTAL before including it, if you really want to use it.") +# else +# pragma message("GLM: GLM_GTX_perpendicular extension included") +# endif +#endif + +namespace glm +{ + /// @addtogroup gtx_perpendicular + /// @{ + + //! Projects x a perpendicular axis of Normal. + //! From GLM_GTX_perpendicular extension. + template + GLM_FUNC_DECL genType perp(genType const& x, genType const& Normal); + + /// @} +}//namespace glm + +#include "perpendicular.inl" diff --git a/src/GLMath/glm/gtx/perpendicular.inl b/src/GLMath/glm/gtx/perpendicular.inl new file mode 100644 index 000000000..1e72f3342 --- /dev/null +++ b/src/GLMath/glm/gtx/perpendicular.inl @@ -0,0 +1,10 @@ +/// @ref gtx_perpendicular + +namespace glm +{ + template + GLM_FUNC_QUALIFIER genType perp(genType const& x, genType const& Normal) + { + return x - proj(x, Normal); + } +}//namespace glm diff --git a/src/GLMath/glm/gtx/polar_coordinates.hpp b/src/GLMath/glm/gtx/polar_coordinates.hpp new file mode 100644 index 000000000..b39911257 --- /dev/null +++ b/src/GLMath/glm/gtx/polar_coordinates.hpp @@ -0,0 +1,48 @@ +/// @ref gtx_polar_coordinates +/// @file glm/gtx/polar_coordinates.hpp +/// +/// @see core (dependence) +/// +/// @defgroup gtx_polar_coordinates GLM_GTX_polar_coordinates +/// @ingroup gtx +/// +/// Include to use the features of this extension. +/// +/// Conversion from Euclidean space to polar space and revert. + +#pragma once + +// Dependency: +#include "../glm.hpp" + +#if GLM_MESSAGES == GLM_ENABLE && !defined(GLM_EXT_INCLUDED) +# ifndef GLM_ENABLE_EXPERIMENTAL +# pragma message("GLM: GLM_GTX_polar_coordinates is an experimental extension and may change in the future. Use #define GLM_ENABLE_EXPERIMENTAL before including it, if you really want to use it.") +# else +# pragma message("GLM: GLM_GTX_polar_coordinates extension included") +# endif +#endif + +namespace glm +{ + /// @addtogroup gtx_polar_coordinates + /// @{ + + /// Convert Euclidean to Polar coordinates, x is the xz distance, y, the latitude and z the longitude. + /// + /// @see gtx_polar_coordinates + template + GLM_FUNC_DECL vec<3, T, Q> polar( + vec<3, T, Q> const& euclidean); + + /// Convert Polar to Euclidean coordinates. + /// + /// @see gtx_polar_coordinates + template + GLM_FUNC_DECL vec<3, T, Q> euclidean( + vec<2, T, Q> const& polar); + + /// @} +}//namespace glm + +#include "polar_coordinates.inl" diff --git a/src/GLMath/glm/gtx/polar_coordinates.inl b/src/GLMath/glm/gtx/polar_coordinates.inl new file mode 100644 index 000000000..371c8ddde --- /dev/null +++ b/src/GLMath/glm/gtx/polar_coordinates.inl @@ -0,0 +1,36 @@ +/// @ref gtx_polar_coordinates + +namespace glm +{ + template + GLM_FUNC_QUALIFIER vec<3, T, Q> polar + ( + vec<3, T, Q> const& euclidean + ) + { + T const Length(length(euclidean)); + vec<3, T, Q> const tmp(euclidean / Length); + T const xz_dist(sqrt(tmp.x * tmp.x + tmp.z * tmp.z)); + + return vec<3, T, Q>( + asin(tmp.y), // latitude + atan(tmp.x, tmp.z), // longitude + xz_dist); // xz distance + } + + template + GLM_FUNC_QUALIFIER vec<3, T, Q> euclidean + ( + vec<2, T, Q> const& polar + ) + { + T const latitude(polar.x); + T const longitude(polar.y); + + return vec<3, T, Q>( + cos(latitude) * sin(longitude), + sin(latitude), + cos(latitude) * cos(longitude)); + } + +}//namespace glm diff --git a/src/GLMath/glm/gtx/projection.hpp b/src/GLMath/glm/gtx/projection.hpp new file mode 100644 index 000000000..678f3ad5a --- /dev/null +++ b/src/GLMath/glm/gtx/projection.hpp @@ -0,0 +1,43 @@ +/// @ref gtx_projection +/// @file glm/gtx/projection.hpp +/// +/// @see core (dependence) +/// +/// @defgroup gtx_projection GLM_GTX_projection +/// @ingroup gtx +/// +/// Include to use the features of this extension. +/// +/// Projection of a vector to other one + +#pragma once + +// Dependency: +#include "../geometric.hpp" + +#if GLM_MESSAGES == GLM_ENABLE && !defined(GLM_EXT_INCLUDED) +# ifndef GLM_ENABLE_EXPERIMENTAL +# pragma message("GLM: GLM_GTX_projection is an experimental extension and may change in the future. Use #define GLM_ENABLE_EXPERIMENTAL before including it, if you really want to use it.") +# else +# pragma message("GLM: GLM_GTX_projection extension included") +# endif +#endif + +namespace glm +{ + /// @addtogroup gtx_projection + /// @{ + + /// Projects x on Normal. + /// + /// @param[in] x A vector to project + /// @param[in] Normal A normal that doesn't need to be of unit length. + /// + /// @see gtx_projection + template + GLM_FUNC_DECL genType proj(genType const& x, genType const& Normal); + + /// @} +}//namespace glm + +#include "projection.inl" diff --git a/src/GLMath/glm/gtx/projection.inl b/src/GLMath/glm/gtx/projection.inl new file mode 100644 index 000000000..f23f884fb --- /dev/null +++ b/src/GLMath/glm/gtx/projection.inl @@ -0,0 +1,10 @@ +/// @ref gtx_projection + +namespace glm +{ + template + GLM_FUNC_QUALIFIER genType proj(genType const& x, genType const& Normal) + { + return glm::dot(x, Normal) / glm::dot(Normal, Normal) * Normal; + } +}//namespace glm diff --git a/src/GLMath/glm/gtx/quaternion.hpp b/src/GLMath/glm/gtx/quaternion.hpp new file mode 100644 index 000000000..05a71dfad --- /dev/null +++ b/src/GLMath/glm/gtx/quaternion.hpp @@ -0,0 +1,174 @@ +/// @ref gtx_quaternion +/// @file glm/gtx/quaternion.hpp +/// +/// @see core (dependence) +/// @see gtx_extented_min_max (dependence) +/// +/// @defgroup gtx_quaternion GLM_GTX_quaternion +/// @ingroup gtx +/// +/// Include to use the features of this extension. +/// +/// Extented quaternion types and functions + +#pragma once + +// Dependency: +#include "../glm.hpp" +#include "../gtc/constants.hpp" +#include "../gtc/quaternion.hpp" +#include "../ext/quaternion_exponential.hpp" +#include "../gtx/norm.hpp" + +#if GLM_MESSAGES == GLM_ENABLE && !defined(GLM_EXT_INCLUDED) +# ifndef GLM_ENABLE_EXPERIMENTAL +# pragma message("GLM: GLM_GTX_quaternion is an experimental extension and may change in the future. Use #define GLM_ENABLE_EXPERIMENTAL before including it, if you really want to use it.") +# else +# pragma message("GLM: GLM_GTX_quaternion extension included") +# endif +#endif + +namespace glm +{ + /// @addtogroup gtx_quaternion + /// @{ + + /// Create an identity quaternion. + /// + /// @see gtx_quaternion + template + GLM_FUNC_DECL qua quat_identity(); + + /// Compute a cross product between a quaternion and a vector. + /// + /// @see gtx_quaternion + template + GLM_FUNC_DECL vec<3, T, Q> cross( + qua const& q, + vec<3, T, Q> const& v); + + //! Compute a cross product between a vector and a quaternion. + /// + /// @see gtx_quaternion + template + GLM_FUNC_DECL vec<3, T, Q> cross( + vec<3, T, Q> const& v, + qua const& q); + + //! Compute a point on a path according squad equation. + //! q1 and q2 are control points; s1 and s2 are intermediate control points. + /// + /// @see gtx_quaternion + template + GLM_FUNC_DECL qua squad( + qua const& q1, + qua const& q2, + qua const& s1, + qua const& s2, + T const& h); + + //! Returns an intermediate control point for squad interpolation. + /// + /// @see gtx_quaternion + template + GLM_FUNC_DECL qua intermediate( + qua const& prev, + qua const& curr, + qua const& next); + + //! Returns quarternion square root. + /// + /// @see gtx_quaternion + //template + //qua sqrt( + // qua const& q); + + //! Rotates a 3 components vector by a quaternion. + /// + /// @see gtx_quaternion + template + GLM_FUNC_DECL vec<3, T, Q> rotate( + qua const& q, + vec<3, T, Q> const& v); + + /// Rotates a 4 components vector by a quaternion. + /// + /// @see gtx_quaternion + template + GLM_FUNC_DECL vec<4, T, Q> rotate( + qua const& q, + vec<4, T, Q> const& v); + + /// Extract the real component of a quaternion. + /// + /// @see gtx_quaternion + template + GLM_FUNC_DECL T extractRealComponent( + qua const& q); + + /// Converts a quaternion to a 3 * 3 matrix. + /// + /// @see gtx_quaternion + template + GLM_FUNC_DECL mat<3, 3, T, Q> toMat3( + qua const& x){return mat3_cast(x);} + + /// Converts a quaternion to a 4 * 4 matrix. + /// + /// @see gtx_quaternion + template + GLM_FUNC_DECL mat<4, 4, T, Q> toMat4( + qua const& x){return mat4_cast(x);} + + /// Converts a 3 * 3 matrix to a quaternion. + /// + /// @see gtx_quaternion + template + GLM_FUNC_DECL qua toQuat( + mat<3, 3, T, Q> const& x){return quat_cast(x);} + + /// Converts a 4 * 4 matrix to a quaternion. + /// + /// @see gtx_quaternion + template + GLM_FUNC_DECL qua toQuat( + mat<4, 4, T, Q> const& x){return quat_cast(x);} + + /// Quaternion interpolation using the rotation short path. + /// + /// @see gtx_quaternion + template + GLM_FUNC_DECL qua shortMix( + qua const& x, + qua const& y, + T const& a); + + /// Quaternion normalized linear interpolation. + /// + /// @see gtx_quaternion + template + GLM_FUNC_DECL qua fastMix( + qua const& x, + qua const& y, + T const& a); + + /// Compute the rotation between two vectors. + /// param orig vector, needs to be normalized + /// param dest vector, needs to be normalized + /// + /// @see gtx_quaternion + template + GLM_FUNC_DECL qua rotation( + vec<3, T, Q> const& orig, + vec<3, T, Q> const& dest); + + /// Returns the squared length of x. + /// + /// @see gtx_quaternion + template + GLM_FUNC_DECL T length2(qua const& q); + + /// @} +}//namespace glm + +#include "quaternion.inl" diff --git a/src/GLMath/glm/gtx/quaternion.inl b/src/GLMath/glm/gtx/quaternion.inl new file mode 100644 index 000000000..679b39f1e --- /dev/null +++ b/src/GLMath/glm/gtx/quaternion.inl @@ -0,0 +1,159 @@ +/// @ref gtx_quaternion + +#include +#include "../gtc/constants.hpp" + +namespace glm +{ + template + GLM_FUNC_QUALIFIER qua quat_identity() + { + return qua(static_cast(1), static_cast(0), static_cast(0), static_cast(0)); + } + + template + GLM_FUNC_QUALIFIER vec<3, T, Q> cross(vec<3, T, Q> const& v, qua const& q) + { + return inverse(q) * v; + } + + template + GLM_FUNC_QUALIFIER vec<3, T, Q> cross(qua const& q, vec<3, T, Q> const& v) + { + return q * v; + } + + template + GLM_FUNC_QUALIFIER qua squad + ( + qua const& q1, + qua const& q2, + qua const& s1, + qua const& s2, + T const& h) + { + return mix(mix(q1, q2, h), mix(s1, s2, h), static_cast(2) * (static_cast(1) - h) * h); + } + + template + GLM_FUNC_QUALIFIER qua intermediate + ( + qua const& prev, + qua const& curr, + qua const& next + ) + { + qua invQuat = inverse(curr); + return exp((log(next * invQuat) + log(prev * invQuat)) / static_cast(-4)) * curr; + } + + template + GLM_FUNC_QUALIFIER vec<3, T, Q> rotate(qua const& q, vec<3, T, Q> const& v) + { + return q * v; + } + + template + GLM_FUNC_QUALIFIER vec<4, T, Q> rotate(qua const& q, vec<4, T, Q> const& v) + { + return q * v; + } + + template + GLM_FUNC_QUALIFIER T extractRealComponent(qua const& q) + { + T w = static_cast(1) - q.x * q.x - q.y * q.y - q.z * q.z; + if(w < T(0)) + return T(0); + else + return -sqrt(w); + } + + template + GLM_FUNC_QUALIFIER T length2(qua const& q) + { + return q.x * q.x + q.y * q.y + q.z * q.z + q.w * q.w; + } + + template + GLM_FUNC_QUALIFIER qua shortMix(qua const& x, qua const& y, T const& a) + { + if(a <= static_cast(0)) return x; + if(a >= static_cast(1)) return y; + + T fCos = dot(x, y); + qua y2(y); //BUG!!! qua y2; + if(fCos < static_cast(0)) + { + y2 = -y; + fCos = -fCos; + } + + //if(fCos > 1.0f) // problem + T k0, k1; + if(fCos > (static_cast(1) - epsilon())) + { + k0 = static_cast(1) - a; + k1 = static_cast(0) + a; //BUG!!! 1.0f + a; + } + else + { + T fSin = sqrt(T(1) - fCos * fCos); + T fAngle = atan(fSin, fCos); + T fOneOverSin = static_cast(1) / fSin; + k0 = sin((static_cast(1) - a) * fAngle) * fOneOverSin; + k1 = sin((static_cast(0) + a) * fAngle) * fOneOverSin; + } + + return qua( + k0 * x.w + k1 * y2.w, + k0 * x.x + k1 * y2.x, + k0 * x.y + k1 * y2.y, + k0 * x.z + k1 * y2.z); + } + + template + GLM_FUNC_QUALIFIER qua fastMix(qua const& x, qua const& y, T const& a) + { + return glm::normalize(x * (static_cast(1) - a) + (y * a)); + } + + template + GLM_FUNC_QUALIFIER qua rotation(vec<3, T, Q> const& orig, vec<3, T, Q> const& dest) + { + T cosTheta = dot(orig, dest); + vec<3, T, Q> rotationAxis; + + if(cosTheta >= static_cast(1) - epsilon()) { + // orig and dest point in the same direction + return quat_identity(); + } + + if(cosTheta < static_cast(-1) + epsilon()) + { + // special case when vectors in opposite directions : + // there is no "ideal" rotation axis + // So guess one; any will do as long as it's perpendicular to start + // This implementation favors a rotation around the Up axis (Y), + // since it's often what you want to do. + rotationAxis = cross(vec<3, T, Q>(0, 0, 1), orig); + if(length2(rotationAxis) < epsilon()) // bad luck, they were parallel, try again! + rotationAxis = cross(vec<3, T, Q>(1, 0, 0), orig); + + rotationAxis = normalize(rotationAxis); + return angleAxis(pi(), rotationAxis); + } + + // Implementation from Stan Melax's Game Programming Gems 1 article + rotationAxis = cross(orig, dest); + + T s = sqrt((T(1) + cosTheta) * static_cast(2)); + T invs = static_cast(1) / s; + + return qua( + s * static_cast(0.5f), + rotationAxis.x * invs, + rotationAxis.y * invs, + rotationAxis.z * invs); + } +}//namespace glm diff --git a/src/GLMath/glm/gtx/range.hpp b/src/GLMath/glm/gtx/range.hpp new file mode 100644 index 000000000..93bcb9a65 --- /dev/null +++ b/src/GLMath/glm/gtx/range.hpp @@ -0,0 +1,98 @@ +/// @ref gtx_range +/// @file glm/gtx/range.hpp +/// @author Joshua Moerman +/// +/// @defgroup gtx_range GLM_GTX_range +/// @ingroup gtx +/// +/// Include to use the features of this extension. +/// +/// Defines begin and end for vectors and matrices. Useful for range-based for loop. +/// The range is defined over the elements, not over columns or rows (e.g. mat4 has 16 elements). + +#pragma once + +// Dependencies +#include "../detail/setup.hpp" + +#if GLM_MESSAGES == GLM_ENABLE && !defined(GLM_EXT_INCLUDED) +# ifndef GLM_ENABLE_EXPERIMENTAL +# pragma message("GLM: GLM_GTX_range is an experimental extension and may change in the future. Use #define GLM_ENABLE_EXPERIMENTAL before including it, if you really want to use it.") +# else +# pragma message("GLM: GLM_GTX_range extension included") +# endif +#endif + +#include "../gtc/type_ptr.hpp" +#include "../gtc/vec1.hpp" + +namespace glm +{ + /// @addtogroup gtx_range + /// @{ + +# if GLM_COMPILER & GLM_COMPILER_VC +# pragma warning(push) +# pragma warning(disable : 4100) // unreferenced formal parameter +# endif + + template + inline length_t components(vec<1, T, Q> const& v) + { + return v.length(); + } + + template + inline length_t components(vec<2, T, Q> const& v) + { + return v.length(); + } + + template + inline length_t components(vec<3, T, Q> const& v) + { + return v.length(); + } + + template + inline length_t components(vec<4, T, Q> const& v) + { + return v.length(); + } + + template + inline length_t components(genType const& m) + { + return m.length() * m[0].length(); + } + + template + inline typename genType::value_type const * begin(genType const& v) + { + return value_ptr(v); + } + + template + inline typename genType::value_type const * end(genType const& v) + { + return begin(v) + components(v); + } + + template + inline typename genType::value_type * begin(genType& v) + { + return value_ptr(v); + } + + template + inline typename genType::value_type * end(genType& v) + { + return begin(v) + components(v); + } + +# if GLM_COMPILER & GLM_COMPILER_VC +# pragma warning(pop) +# endif + + /// @} +}//namespace glm diff --git a/src/GLMath/glm/gtx/raw_data.hpp b/src/GLMath/glm/gtx/raw_data.hpp new file mode 100644 index 000000000..86cbe77d9 --- /dev/null +++ b/src/GLMath/glm/gtx/raw_data.hpp @@ -0,0 +1,51 @@ +/// @ref gtx_raw_data +/// @file glm/gtx/raw_data.hpp +/// +/// @see core (dependence) +/// +/// @defgroup gtx_raw_data GLM_GTX_raw_data +/// @ingroup gtx +/// +/// Include to use the features of this extension. +/// +/// Projection of a vector to other one + +#pragma once + +// Dependencies +#include "../ext/scalar_uint_sized.hpp" +#include "../detail/setup.hpp" + +#if GLM_MESSAGES == GLM_ENABLE && !defined(GLM_EXT_INCLUDED) +# ifndef GLM_ENABLE_EXPERIMENTAL +# pragma message("GLM: GLM_GTX_raw_data is an experimental extension and may change in the future. Use #define GLM_ENABLE_EXPERIMENTAL before including it, if you really want to use it.") +# else +# pragma message("GLM: GLM_GTX_raw_data extension included") +# endif +#endif + +namespace glm +{ + /// @addtogroup gtx_raw_data + /// @{ + + //! Type for byte numbers. + //! From GLM_GTX_raw_data extension. + typedef detail::uint8 byte; + + //! Type for word numbers. + //! From GLM_GTX_raw_data extension. + typedef detail::uint16 word; + + //! Type for dword numbers. + //! From GLM_GTX_raw_data extension. + typedef detail::uint32 dword; + + //! Type for qword numbers. + //! From GLM_GTX_raw_data extension. + typedef detail::uint64 qword; + + /// @} +}// namespace glm + +#include "raw_data.inl" diff --git a/src/GLMath/glm/gtx/raw_data.inl b/src/GLMath/glm/gtx/raw_data.inl new file mode 100644 index 000000000..c740317d3 --- /dev/null +++ b/src/GLMath/glm/gtx/raw_data.inl @@ -0,0 +1,2 @@ +/// @ref gtx_raw_data + diff --git a/src/GLMath/glm/gtx/rotate_normalized_axis.hpp b/src/GLMath/glm/gtx/rotate_normalized_axis.hpp new file mode 100644 index 000000000..2103ca08f --- /dev/null +++ b/src/GLMath/glm/gtx/rotate_normalized_axis.hpp @@ -0,0 +1,68 @@ +/// @ref gtx_rotate_normalized_axis +/// @file glm/gtx/rotate_normalized_axis.hpp +/// +/// @see core (dependence) +/// @see gtc_matrix_transform +/// @see gtc_quaternion +/// +/// @defgroup gtx_rotate_normalized_axis GLM_GTX_rotate_normalized_axis +/// @ingroup gtx +/// +/// Include to use the features of this extension. +/// +/// Quaternions and matrices rotations around normalized axis. + +#pragma once + +// Dependency: +#include "../glm.hpp" +#include "../gtc/epsilon.hpp" +#include "../gtc/quaternion.hpp" + +#if GLM_MESSAGES == GLM_ENABLE && !defined(GLM_EXT_INCLUDED) +# ifndef GLM_ENABLE_EXPERIMENTAL +# pragma message("GLM: GLM_GTX_rotate_normalized_axis is an experimental extension and may change in the future. Use #define GLM_ENABLE_EXPERIMENTAL before including it, if you really want to use it.") +# else +# pragma message("GLM: GLM_GTX_rotate_normalized_axis extension included") +# endif +#endif + +namespace glm +{ + /// @addtogroup gtx_rotate_normalized_axis + /// @{ + + /// Builds a rotation 4 * 4 matrix created from a normalized axis and an angle. + /// + /// @param m Input matrix multiplied by this rotation matrix. + /// @param angle Rotation angle expressed in radians. + /// @param axis Rotation axis, must be normalized. + /// @tparam T Value type used to build the matrix. Currently supported: half (not recommended), float or double. + /// + /// @see gtx_rotate_normalized_axis + /// @see - rotate(T angle, T x, T y, T z) + /// @see - rotate(mat<4, 4, T, Q> const& m, T angle, T x, T y, T z) + /// @see - rotate(T angle, vec<3, T, Q> const& v) + template + GLM_FUNC_DECL mat<4, 4, T, Q> rotateNormalizedAxis( + mat<4, 4, T, Q> const& m, + T const& angle, + vec<3, T, Q> const& axis); + + /// Rotates a quaternion from a vector of 3 components normalized axis and an angle. + /// + /// @param q Source orientation + /// @param angle Angle expressed in radians. + /// @param axis Normalized axis of the rotation, must be normalized. + /// + /// @see gtx_rotate_normalized_axis + template + GLM_FUNC_DECL qua rotateNormalizedAxis( + qua const& q, + T const& angle, + vec<3, T, Q> const& axis); + + /// @} +}//namespace glm + +#include "rotate_normalized_axis.inl" diff --git a/src/GLMath/glm/gtx/rotate_normalized_axis.inl b/src/GLMath/glm/gtx/rotate_normalized_axis.inl new file mode 100644 index 000000000..b2e9278c0 --- /dev/null +++ b/src/GLMath/glm/gtx/rotate_normalized_axis.inl @@ -0,0 +1,58 @@ +/// @ref gtx_rotate_normalized_axis + +namespace glm +{ + template + GLM_FUNC_QUALIFIER mat<4, 4, T, Q> rotateNormalizedAxis + ( + mat<4, 4, T, Q> const& m, + T const& angle, + vec<3, T, Q> const& v + ) + { + T const a = angle; + T const c = cos(a); + T const s = sin(a); + + vec<3, T, Q> const axis(v); + + vec<3, T, Q> const temp((static_cast(1) - c) * axis); + + mat<4, 4, T, Q> Rotate; + Rotate[0][0] = c + temp[0] * axis[0]; + Rotate[0][1] = 0 + temp[0] * axis[1] + s * axis[2]; + Rotate[0][2] = 0 + temp[0] * axis[2] - s * axis[1]; + + Rotate[1][0] = 0 + temp[1] * axis[0] - s * axis[2]; + Rotate[1][1] = c + temp[1] * axis[1]; + Rotate[1][2] = 0 + temp[1] * axis[2] + s * axis[0]; + + Rotate[2][0] = 0 + temp[2] * axis[0] + s * axis[1]; + Rotate[2][1] = 0 + temp[2] * axis[1] - s * axis[0]; + Rotate[2][2] = c + temp[2] * axis[2]; + + mat<4, 4, T, Q> Result; + Result[0] = m[0] * Rotate[0][0] + m[1] * Rotate[0][1] + m[2] * Rotate[0][2]; + Result[1] = m[0] * Rotate[1][0] + m[1] * Rotate[1][1] + m[2] * Rotate[1][2]; + Result[2] = m[0] * Rotate[2][0] + m[1] * Rotate[2][1] + m[2] * Rotate[2][2]; + Result[3] = m[3]; + return Result; + } + + template + GLM_FUNC_QUALIFIER qua rotateNormalizedAxis + ( + qua const& q, + T const& angle, + vec<3, T, Q> const& v + ) + { + vec<3, T, Q> const Tmp(v); + + T const AngleRad(angle); + T const Sin = sin(AngleRad * T(0.5)); + + return q * qua(cos(AngleRad * static_cast(0.5)), Tmp.x * Sin, Tmp.y * Sin, Tmp.z * Sin); + //return gtc::quaternion::cross(q, tquat(cos(AngleRad * T(0.5)), Tmp.x * fSin, Tmp.y * fSin, Tmp.z * fSin)); + } +}//namespace glm diff --git a/src/GLMath/glm/gtx/rotate_vector.hpp b/src/GLMath/glm/gtx/rotate_vector.hpp new file mode 100644 index 000000000..dcd5b95a6 --- /dev/null +++ b/src/GLMath/glm/gtx/rotate_vector.hpp @@ -0,0 +1,123 @@ +/// @ref gtx_rotate_vector +/// @file glm/gtx/rotate_vector.hpp +/// +/// @see core (dependence) +/// @see gtx_transform (dependence) +/// +/// @defgroup gtx_rotate_vector GLM_GTX_rotate_vector +/// @ingroup gtx +/// +/// Include to use the features of this extension. +/// +/// Function to directly rotate a vector + +#pragma once + +// Dependency: +#include "../gtx/transform.hpp" +#include "../gtc/epsilon.hpp" +#include "../ext/vector_relational.hpp" +#include "../glm.hpp" + +#if GLM_MESSAGES == GLM_ENABLE && !defined(GLM_EXT_INCLUDED) +# ifndef GLM_ENABLE_EXPERIMENTAL +# pragma message("GLM: GLM_GTX_rotate_vector is an experimental extension and may change in the future. Use #define GLM_ENABLE_EXPERIMENTAL before including it, if you really want to use it.") +# else +# pragma message("GLM: GLM_GTX_rotate_vector extension included") +# endif +#endif + +namespace glm +{ + /// @addtogroup gtx_rotate_vector + /// @{ + + /// Returns Spherical interpolation between two vectors + /// + /// @param x A first vector + /// @param y A second vector + /// @param a Interpolation factor. The interpolation is defined beyond the range [0, 1]. + /// + /// @see gtx_rotate_vector + template + GLM_FUNC_DECL vec<3, T, Q> slerp( + vec<3, T, Q> const& x, + vec<3, T, Q> const& y, + T const& a); + + //! Rotate a two dimensional vector. + //! From GLM_GTX_rotate_vector extension. + template + GLM_FUNC_DECL vec<2, T, Q> rotate( + vec<2, T, Q> const& v, + T const& angle); + + //! Rotate a three dimensional vector around an axis. + //! From GLM_GTX_rotate_vector extension. + template + GLM_FUNC_DECL vec<3, T, Q> rotate( + vec<3, T, Q> const& v, + T const& angle, + vec<3, T, Q> const& normal); + + //! Rotate a four dimensional vector around an axis. + //! From GLM_GTX_rotate_vector extension. + template + GLM_FUNC_DECL vec<4, T, Q> rotate( + vec<4, T, Q> const& v, + T const& angle, + vec<3, T, Q> const& normal); + + //! Rotate a three dimensional vector around the X axis. + //! From GLM_GTX_rotate_vector extension. + template + GLM_FUNC_DECL vec<3, T, Q> rotateX( + vec<3, T, Q> const& v, + T const& angle); + + //! Rotate a three dimensional vector around the Y axis. + //! From GLM_GTX_rotate_vector extension. + template + GLM_FUNC_DECL vec<3, T, Q> rotateY( + vec<3, T, Q> const& v, + T const& angle); + + //! Rotate a three dimensional vector around the Z axis. + //! From GLM_GTX_rotate_vector extension. + template + GLM_FUNC_DECL vec<3, T, Q> rotateZ( + vec<3, T, Q> const& v, + T const& angle); + + //! Rotate a four dimensional vector around the X axis. + //! From GLM_GTX_rotate_vector extension. + template + GLM_FUNC_DECL vec<4, T, Q> rotateX( + vec<4, T, Q> const& v, + T const& angle); + + //! Rotate a four dimensional vector around the Y axis. + //! From GLM_GTX_rotate_vector extension. + template + GLM_FUNC_DECL vec<4, T, Q> rotateY( + vec<4, T, Q> const& v, + T const& angle); + + //! Rotate a four dimensional vector around the Z axis. + //! From GLM_GTX_rotate_vector extension. + template + GLM_FUNC_DECL vec<4, T, Q> rotateZ( + vec<4, T, Q> const& v, + T const& angle); + + //! Build a rotation matrix from a normal and a up vector. + //! From GLM_GTX_rotate_vector extension. + template + GLM_FUNC_DECL mat<4, 4, T, Q> orientation( + vec<3, T, Q> const& Normal, + vec<3, T, Q> const& Up); + + /// @} +}//namespace glm + +#include "rotate_vector.inl" diff --git a/src/GLMath/glm/gtx/rotate_vector.inl b/src/GLMath/glm/gtx/rotate_vector.inl new file mode 100644 index 000000000..f8136e765 --- /dev/null +++ b/src/GLMath/glm/gtx/rotate_vector.inl @@ -0,0 +1,187 @@ +/// @ref gtx_rotate_vector + +namespace glm +{ + template + GLM_FUNC_QUALIFIER vec<3, T, Q> slerp + ( + vec<3, T, Q> const& x, + vec<3, T, Q> const& y, + T const& a + ) + { + // get cosine of angle between vectors (-1 -> 1) + T CosAlpha = dot(x, y); + // get angle (0 -> pi) + T Alpha = acos(CosAlpha); + // get sine of angle between vectors (0 -> 1) + T SinAlpha = sin(Alpha); + // this breaks down when SinAlpha = 0, i.e. Alpha = 0 or pi + T t1 = sin((static_cast(1) - a) * Alpha) / SinAlpha; + T t2 = sin(a * Alpha) / SinAlpha; + + // interpolate src vectors + return x * t1 + y * t2; + } + + template + GLM_FUNC_QUALIFIER vec<2, T, Q> rotate + ( + vec<2, T, Q> const& v, + T const& angle + ) + { + vec<2, T, Q> Result; + T const Cos(cos(angle)); + T const Sin(sin(angle)); + + Result.x = v.x * Cos - v.y * Sin; + Result.y = v.x * Sin + v.y * Cos; + return Result; + } + + template + GLM_FUNC_QUALIFIER vec<3, T, Q> rotate + ( + vec<3, T, Q> const& v, + T const& angle, + vec<3, T, Q> const& normal + ) + { + return mat<3, 3, T, Q>(glm::rotate(angle, normal)) * v; + } + /* + template + GLM_FUNC_QUALIFIER vec<3, T, Q> rotateGTX( + const vec<3, T, Q>& x, + T angle, + const vec<3, T, Q>& normal) + { + const T Cos = cos(radians(angle)); + const T Sin = sin(radians(angle)); + return x * Cos + ((x * normal) * (T(1) - Cos)) * normal + cross(x, normal) * Sin; + } + */ + template + GLM_FUNC_QUALIFIER vec<4, T, Q> rotate + ( + vec<4, T, Q> const& v, + T const& angle, + vec<3, T, Q> const& normal + ) + { + return rotate(angle, normal) * v; + } + + template + GLM_FUNC_QUALIFIER vec<3, T, Q> rotateX + ( + vec<3, T, Q> const& v, + T const& angle + ) + { + vec<3, T, Q> Result(v); + T const Cos(cos(angle)); + T const Sin(sin(angle)); + + Result.y = v.y * Cos - v.z * Sin; + Result.z = v.y * Sin + v.z * Cos; + return Result; + } + + template + GLM_FUNC_QUALIFIER vec<3, T, Q> rotateY + ( + vec<3, T, Q> const& v, + T const& angle + ) + { + vec<3, T, Q> Result = v; + T const Cos(cos(angle)); + T const Sin(sin(angle)); + + Result.x = v.x * Cos + v.z * Sin; + Result.z = -v.x * Sin + v.z * Cos; + return Result; + } + + template + GLM_FUNC_QUALIFIER vec<3, T, Q> rotateZ + ( + vec<3, T, Q> const& v, + T const& angle + ) + { + vec<3, T, Q> Result = v; + T const Cos(cos(angle)); + T const Sin(sin(angle)); + + Result.x = v.x * Cos - v.y * Sin; + Result.y = v.x * Sin + v.y * Cos; + return Result; + } + + template + GLM_FUNC_QUALIFIER vec<4, T, Q> rotateX + ( + vec<4, T, Q> const& v, + T const& angle + ) + { + vec<4, T, Q> Result = v; + T const Cos(cos(angle)); + T const Sin(sin(angle)); + + Result.y = v.y * Cos - v.z * Sin; + Result.z = v.y * Sin + v.z * Cos; + return Result; + } + + template + GLM_FUNC_QUALIFIER vec<4, T, Q> rotateY + ( + vec<4, T, Q> const& v, + T const& angle + ) + { + vec<4, T, Q> Result = v; + T const Cos(cos(angle)); + T const Sin(sin(angle)); + + Result.x = v.x * Cos + v.z * Sin; + Result.z = -v.x * Sin + v.z * Cos; + return Result; + } + + template + GLM_FUNC_QUALIFIER vec<4, T, Q> rotateZ + ( + vec<4, T, Q> const& v, + T const& angle + ) + { + vec<4, T, Q> Result = v; + T const Cos(cos(angle)); + T const Sin(sin(angle)); + + Result.x = v.x * Cos - v.y * Sin; + Result.y = v.x * Sin + v.y * Cos; + return Result; + } + + template + GLM_FUNC_QUALIFIER mat<4, 4, T, Q> orientation + ( + vec<3, T, Q> const& Normal, + vec<3, T, Q> const& Up + ) + { + if(all(equal(Normal, Up, epsilon()))) + return mat<4, 4, T, Q>(static_cast(1)); + + vec<3, T, Q> RotationAxis = cross(Up, Normal); + T Angle = acos(dot(Normal, Up)); + + return rotate(Angle, RotationAxis); + } +}//namespace glm diff --git a/src/GLMath/glm/gtx/scalar_multiplication.hpp b/src/GLMath/glm/gtx/scalar_multiplication.hpp new file mode 100644 index 000000000..f391f8deb --- /dev/null +++ b/src/GLMath/glm/gtx/scalar_multiplication.hpp @@ -0,0 +1,75 @@ +/// @ref gtx +/// @file glm/gtx/scalar_multiplication.hpp +/// @author Joshua Moerman +/// +/// Include to use the features of this extension. +/// +/// Enables scalar multiplication for all types +/// +/// Since GLSL is very strict about types, the following (often used) combinations do not work: +/// double * vec4 +/// int * vec4 +/// vec4 / int +/// So we'll fix that! Of course "float * vec4" should remain the same (hence the enable_if magic) + +#pragma once + +#include "../detail/setup.hpp" + +#if GLM_MESSAGES == GLM_ENABLE && !defined(GLM_EXT_INCLUDED) +# ifndef GLM_ENABLE_EXPERIMENTAL +# pragma message("GLM: GLM_GTX_scalar_multiplication is an experimental extension and may change in the future. Use #define GLM_ENABLE_EXPERIMENTAL before including it, if you really want to use it.") +# else +# pragma message("GLM: GLM_GTX_scalar_multiplication extension included") +# endif +#endif + +#include "../vec2.hpp" +#include "../vec3.hpp" +#include "../vec4.hpp" +#include "../mat2x2.hpp" +#include + +namespace glm +{ + template + using return_type_scalar_multiplication = typename std::enable_if< + !std::is_same::value // T may not be a float + && std::is_arithmetic::value, Vec // But it may be an int or double (no vec3 or mat3, ...) + >::type; + +#define GLM_IMPLEMENT_SCAL_MULT(Vec) \ + template \ + return_type_scalar_multiplication \ + operator*(T const& s, Vec rh){ \ + return rh *= static_cast(s); \ + } \ + \ + template \ + return_type_scalar_multiplication \ + operator*(Vec lh, T const& s){ \ + return lh *= static_cast(s); \ + } \ + \ + template \ + return_type_scalar_multiplication \ + operator/(Vec lh, T const& s){ \ + return lh *= 1.0f / s; \ + } + +GLM_IMPLEMENT_SCAL_MULT(vec2) +GLM_IMPLEMENT_SCAL_MULT(vec3) +GLM_IMPLEMENT_SCAL_MULT(vec4) + +GLM_IMPLEMENT_SCAL_MULT(mat2) +GLM_IMPLEMENT_SCAL_MULT(mat2x3) +GLM_IMPLEMENT_SCAL_MULT(mat2x4) +GLM_IMPLEMENT_SCAL_MULT(mat3x2) +GLM_IMPLEMENT_SCAL_MULT(mat3) +GLM_IMPLEMENT_SCAL_MULT(mat3x4) +GLM_IMPLEMENT_SCAL_MULT(mat4x2) +GLM_IMPLEMENT_SCAL_MULT(mat4x3) +GLM_IMPLEMENT_SCAL_MULT(mat4) + +#undef GLM_IMPLEMENT_SCAL_MULT +} // namespace glm diff --git a/src/GLMath/glm/gtx/scalar_relational.hpp b/src/GLMath/glm/gtx/scalar_relational.hpp new file mode 100644 index 000000000..8be9c57b8 --- /dev/null +++ b/src/GLMath/glm/gtx/scalar_relational.hpp @@ -0,0 +1,36 @@ +/// @ref gtx_scalar_relational +/// @file glm/gtx/scalar_relational.hpp +/// +/// @see core (dependence) +/// +/// @defgroup gtx_scalar_relational GLM_GTX_scalar_relational +/// @ingroup gtx +/// +/// Include to use the features of this extension. +/// +/// Extend a position from a source to a position at a defined length. + +#pragma once + +// Dependency: +#include "../glm.hpp" + +#if GLM_MESSAGES == GLM_ENABLE && !defined(GLM_EXT_INCLUDED) +# ifndef GLM_ENABLE_EXPERIMENTAL +# pragma message("GLM: GLM_GTX_extend is an experimental extension and may change in the future. Use #define GLM_ENABLE_EXPERIMENTAL before including it, if you really want to use it.") +# else +# pragma message("GLM: GLM_GTX_extend extension included") +# endif +#endif + +namespace glm +{ + /// @addtogroup gtx_scalar_relational + /// @{ + + + + /// @} +}//namespace glm + +#include "scalar_relational.inl" diff --git a/src/GLMath/glm/gtx/scalar_relational.inl b/src/GLMath/glm/gtx/scalar_relational.inl new file mode 100644 index 000000000..c2a121cff --- /dev/null +++ b/src/GLMath/glm/gtx/scalar_relational.inl @@ -0,0 +1,88 @@ +/// @ref gtx_scalar_relational + +namespace glm +{ + template + GLM_FUNC_QUALIFIER bool lessThan + ( + T const& x, + T const& y + ) + { + return x < y; + } + + template + GLM_FUNC_QUALIFIER bool lessThanEqual + ( + T const& x, + T const& y + ) + { + return x <= y; + } + + template + GLM_FUNC_QUALIFIER bool greaterThan + ( + T const& x, + T const& y + ) + { + return x > y; + } + + template + GLM_FUNC_QUALIFIER bool greaterThanEqual + ( + T const& x, + T const& y + ) + { + return x >= y; + } + + template + GLM_FUNC_QUALIFIER bool equal + ( + T const& x, + T const& y + ) + { + return detail::compute_equal::is_iec559>::call(x, y); + } + + template + GLM_FUNC_QUALIFIER bool notEqual + ( + T const& x, + T const& y + ) + { + return !detail::compute_equal::is_iec559>::call(x, y); + } + + GLM_FUNC_QUALIFIER bool any + ( + bool const& x + ) + { + return x; + } + + GLM_FUNC_QUALIFIER bool all + ( + bool const& x + ) + { + return x; + } + + GLM_FUNC_QUALIFIER bool not_ + ( + bool const& x + ) + { + return !x; + } +}//namespace glm diff --git a/src/GLMath/glm/gtx/spline.hpp b/src/GLMath/glm/gtx/spline.hpp new file mode 100644 index 000000000..731c979e3 --- /dev/null +++ b/src/GLMath/glm/gtx/spline.hpp @@ -0,0 +1,65 @@ +/// @ref gtx_spline +/// @file glm/gtx/spline.hpp +/// +/// @see core (dependence) +/// +/// @defgroup gtx_spline GLM_GTX_spline +/// @ingroup gtx +/// +/// Include to use the features of this extension. +/// +/// Spline functions + +#pragma once + +// Dependency: +#include "../glm.hpp" +#include "../gtx/optimum_pow.hpp" + +#if GLM_MESSAGES == GLM_ENABLE && !defined(GLM_EXT_INCLUDED) +# ifndef GLM_ENABLE_EXPERIMENTAL +# pragma message("GLM: GLM_GTX_spline is an experimental extension and may change in the future. Use #define GLM_ENABLE_EXPERIMENTAL before including it, if you really want to use it.") +# else +# pragma message("GLM: GLM_GTX_spline extension included") +# endif +#endif + +namespace glm +{ + /// @addtogroup gtx_spline + /// @{ + + /// Return a point from a catmull rom curve. + /// @see gtx_spline extension. + template + GLM_FUNC_DECL genType catmullRom( + genType const& v1, + genType const& v2, + genType const& v3, + genType const& v4, + typename genType::value_type const& s); + + /// Return a point from a hermite curve. + /// @see gtx_spline extension. + template + GLM_FUNC_DECL genType hermite( + genType const& v1, + genType const& t1, + genType const& v2, + genType const& t2, + typename genType::value_type const& s); + + /// Return a point from a cubic curve. + /// @see gtx_spline extension. + template + GLM_FUNC_DECL genType cubic( + genType const& v1, + genType const& v2, + genType const& v3, + genType const& v4, + typename genType::value_type const& s); + + /// @} +}//namespace glm + +#include "spline.inl" diff --git a/src/GLMath/glm/gtx/spline.inl b/src/GLMath/glm/gtx/spline.inl new file mode 100644 index 000000000..c3fd05656 --- /dev/null +++ b/src/GLMath/glm/gtx/spline.inl @@ -0,0 +1,60 @@ +/// @ref gtx_spline + +namespace glm +{ + template + GLM_FUNC_QUALIFIER genType catmullRom + ( + genType const& v1, + genType const& v2, + genType const& v3, + genType const& v4, + typename genType::value_type const& s + ) + { + typename genType::value_type s2 = pow2(s); + typename genType::value_type s3 = pow3(s); + + typename genType::value_type f1 = -s3 + typename genType::value_type(2) * s2 - s; + typename genType::value_type f2 = typename genType::value_type(3) * s3 - typename genType::value_type(5) * s2 + typename genType::value_type(2); + typename genType::value_type f3 = typename genType::value_type(-3) * s3 + typename genType::value_type(4) * s2 + s; + typename genType::value_type f4 = s3 - s2; + + return (f1 * v1 + f2 * v2 + f3 * v3 + f4 * v4) / typename genType::value_type(2); + + } + + template + GLM_FUNC_QUALIFIER genType hermite + ( + genType const& v1, + genType const& t1, + genType const& v2, + genType const& t2, + typename genType::value_type const& s + ) + { + typename genType::value_type s2 = pow2(s); + typename genType::value_type s3 = pow3(s); + + typename genType::value_type f1 = typename genType::value_type(2) * s3 - typename genType::value_type(3) * s2 + typename genType::value_type(1); + typename genType::value_type f2 = typename genType::value_type(-2) * s3 + typename genType::value_type(3) * s2; + typename genType::value_type f3 = s3 - typename genType::value_type(2) * s2 + s; + typename genType::value_type f4 = s3 - s2; + + return f1 * v1 + f2 * v2 + f3 * t1 + f4 * t2; + } + + template + GLM_FUNC_QUALIFIER genType cubic + ( + genType const& v1, + genType const& v2, + genType const& v3, + genType const& v4, + typename genType::value_type const& s + ) + { + return ((v1 * s + v2) * s + v3) * s + v4; + } +}//namespace glm diff --git a/src/GLMath/glm/gtx/std_based_type.hpp b/src/GLMath/glm/gtx/std_based_type.hpp new file mode 100644 index 000000000..cd3be8cb7 --- /dev/null +++ b/src/GLMath/glm/gtx/std_based_type.hpp @@ -0,0 +1,68 @@ +/// @ref gtx_std_based_type +/// @file glm/gtx/std_based_type.hpp +/// +/// @see core (dependence) +/// @see gtx_extented_min_max (dependence) +/// +/// @defgroup gtx_std_based_type GLM_GTX_std_based_type +/// @ingroup gtx +/// +/// Include to use the features of this extension. +/// +/// Adds vector types based on STL value types. + +#pragma once + +// Dependency: +#include "../glm.hpp" +#include + +#if GLM_MESSAGES == GLM_ENABLE && !defined(GLM_EXT_INCLUDED) +# ifndef GLM_ENABLE_EXPERIMENTAL +# pragma message("GLM: GLM_GTX_std_based_type is an experimental extension and may change in the future. Use #define GLM_ENABLE_EXPERIMENTAL before including it, if you really want to use it.") +# else +# pragma message("GLM: GLM_GTX_std_based_type extension included") +# endif +#endif + +namespace glm +{ + /// @addtogroup gtx_std_based_type + /// @{ + + /// Vector type based of one std::size_t component. + /// @see GLM_GTX_std_based_type + typedef vec<1, std::size_t, defaultp> size1; + + /// Vector type based of two std::size_t components. + /// @see GLM_GTX_std_based_type + typedef vec<2, std::size_t, defaultp> size2; + + /// Vector type based of three std::size_t components. + /// @see GLM_GTX_std_based_type + typedef vec<3, std::size_t, defaultp> size3; + + /// Vector type based of four std::size_t components. + /// @see GLM_GTX_std_based_type + typedef vec<4, std::size_t, defaultp> size4; + + /// Vector type based of one std::size_t component. + /// @see GLM_GTX_std_based_type + typedef vec<1, std::size_t, defaultp> size1_t; + + /// Vector type based of two std::size_t components. + /// @see GLM_GTX_std_based_type + typedef vec<2, std::size_t, defaultp> size2_t; + + /// Vector type based of three std::size_t components. + /// @see GLM_GTX_std_based_type + typedef vec<3, std::size_t, defaultp> size3_t; + + /// Vector type based of four std::size_t components. + /// @see GLM_GTX_std_based_type + typedef vec<4, std::size_t, defaultp> size4_t; + + /// @} +}//namespace glm + +#include "std_based_type.inl" diff --git a/src/GLMath/glm/gtx/std_based_type.inl b/src/GLMath/glm/gtx/std_based_type.inl new file mode 100644 index 000000000..9c34bdb6e --- /dev/null +++ b/src/GLMath/glm/gtx/std_based_type.inl @@ -0,0 +1,6 @@ +/// @ref gtx_std_based_type + +namespace glm +{ + +} diff --git a/src/GLMath/glm/gtx/string_cast.hpp b/src/GLMath/glm/gtx/string_cast.hpp new file mode 100644 index 000000000..27846bf89 --- /dev/null +++ b/src/GLMath/glm/gtx/string_cast.hpp @@ -0,0 +1,52 @@ +/// @ref gtx_string_cast +/// @file glm/gtx/string_cast.hpp +/// +/// @see core (dependence) +/// @see gtx_integer (dependence) +/// @see gtx_quaternion (dependence) +/// +/// @defgroup gtx_string_cast GLM_GTX_string_cast +/// @ingroup gtx +/// +/// Include to use the features of this extension. +/// +/// Setup strings for GLM type values +/// +/// This extension is not supported with CUDA + +#pragma once + +// Dependency: +#include "../glm.hpp" +#include "../gtc/type_precision.hpp" +#include "../gtc/quaternion.hpp" +#include "../gtx/dual_quaternion.hpp" +#include +#include + +#if GLM_MESSAGES == GLM_ENABLE && !defined(GLM_EXT_INCLUDED) +# ifndef GLM_ENABLE_EXPERIMENTAL +# pragma message("GLM: GLM_GTX_string_cast is an experimental extension and may change in the future. Use #define GLM_ENABLE_EXPERIMENTAL before including it, if you really want to use it.") +# else +# pragma message("GLM: GLM_GTX_string_cast extension included") +# endif +#endif + +#if(GLM_COMPILER & GLM_COMPILER_CUDA) +# error "GLM_GTX_string_cast is not supported on CUDA compiler" +#endif + +namespace glm +{ + /// @addtogroup gtx_string_cast + /// @{ + + /// Create a string from a GLM vector or matrix typed variable. + /// @see gtx_string_cast extension. + template + GLM_FUNC_DECL std::string to_string(genType const& x); + + /// @} +}//namespace glm + +#include "string_cast.inl" diff --git a/src/GLMath/glm/gtx/string_cast.inl b/src/GLMath/glm/gtx/string_cast.inl new file mode 100644 index 000000000..4ba7da3be --- /dev/null +++ b/src/GLMath/glm/gtx/string_cast.inl @@ -0,0 +1,492 @@ +/// @ref gtx_string_cast + +#include +#include + +namespace glm{ +namespace detail +{ + template + struct cast + { + typedef T value_type; + }; + + template <> + struct cast + { + typedef double value_type; + }; + + GLM_FUNC_QUALIFIER std::string format(const char* msg, ...) + { + std::size_t const STRING_BUFFER(4096); + char text[STRING_BUFFER]; + va_list list; + + if(msg == GLM_NULLPTR) + return std::string(); + + va_start(list, msg); +# if(GLM_COMPILER & GLM_COMPILER_VC) + vsprintf_s(text, STRING_BUFFER, msg, list); +# else// + vsprintf(text, msg, list); +# endif// + va_end(list); + + return std::string(text); + } + + static const char* LabelTrue = "true"; + static const char* LabelFalse = "false"; + + template + struct literal + { + GLM_FUNC_QUALIFIER static char const * value() {return "%d";} + }; + + template + struct literal + { + GLM_FUNC_QUALIFIER static char const * value() {return "%f";} + }; + +# if GLM_MODEL == GLM_MODEL_32 && GLM_COMPILER && GLM_COMPILER_VC + template<> + struct literal + { + GLM_FUNC_QUALIFIER static char const * value() {return "%lld";} + }; + + template<> + struct literal + { + GLM_FUNC_QUALIFIER static char const * value() {return "%lld";} + }; +# endif//GLM_MODEL == GLM_MODEL_32 && GLM_COMPILER && GLM_COMPILER_VC + + template + struct prefix{}; + + template<> + struct prefix + { + GLM_FUNC_QUALIFIER static char const * value() {return "";} + }; + + template<> + struct prefix + { + GLM_FUNC_QUALIFIER static char const * value() {return "d";} + }; + + template<> + struct prefix + { + GLM_FUNC_QUALIFIER static char const * value() {return "b";} + }; + + template<> + struct prefix + { + GLM_FUNC_QUALIFIER static char const * value() {return "u8";} + }; + + template<> + struct prefix + { + GLM_FUNC_QUALIFIER static char const * value() {return "i8";} + }; + + template<> + struct prefix + { + GLM_FUNC_QUALIFIER static char const * value() {return "u16";} + }; + + template<> + struct prefix + { + GLM_FUNC_QUALIFIER static char const * value() {return "i16";} + }; + + template<> + struct prefix + { + GLM_FUNC_QUALIFIER static char const * value() {return "u";} + }; + + template<> + struct prefix + { + GLM_FUNC_QUALIFIER static char const * value() {return "i";} + }; + + template<> + struct prefix + { + GLM_FUNC_QUALIFIER static char const * value() {return "u64";} + }; + + template<> + struct prefix + { + GLM_FUNC_QUALIFIER static char const * value() {return "i64";} + }; + + template + struct compute_to_string + {}; + + template + struct compute_to_string > + { + GLM_FUNC_QUALIFIER static std::string call(vec<1, bool, Q> const& x) + { + return detail::format("bvec1(%s)", + x[0] ? detail::LabelTrue : detail::LabelFalse); + } + }; + + template + struct compute_to_string > + { + GLM_FUNC_QUALIFIER static std::string call(vec<2, bool, Q> const& x) + { + return detail::format("bvec2(%s, %s)", + x[0] ? detail::LabelTrue : detail::LabelFalse, + x[1] ? detail::LabelTrue : detail::LabelFalse); + } + }; + + template + struct compute_to_string > + { + GLM_FUNC_QUALIFIER static std::string call(vec<3, bool, Q> const& x) + { + return detail::format("bvec3(%s, %s, %s)", + x[0] ? detail::LabelTrue : detail::LabelFalse, + x[1] ? detail::LabelTrue : detail::LabelFalse, + x[2] ? detail::LabelTrue : detail::LabelFalse); + } + }; + + template + struct compute_to_string > + { + GLM_FUNC_QUALIFIER static std::string call(vec<4, bool, Q> const& x) + { + return detail::format("bvec4(%s, %s, %s, %s)", + x[0] ? detail::LabelTrue : detail::LabelFalse, + x[1] ? detail::LabelTrue : detail::LabelFalse, + x[2] ? detail::LabelTrue : detail::LabelFalse, + x[3] ? detail::LabelTrue : detail::LabelFalse); + } + }; + + template + struct compute_to_string > + { + GLM_FUNC_QUALIFIER static std::string call(vec<1, T, Q> const& x) + { + char const * PrefixStr = prefix::value(); + char const * LiteralStr = literal::is_iec559>::value(); + std::string FormatStr(detail::format("%svec1(%s)", + PrefixStr, + LiteralStr)); + + return detail::format(FormatStr.c_str(), + static_cast::value_type>(x[0])); + } + }; + + template + struct compute_to_string > + { + GLM_FUNC_QUALIFIER static std::string call(vec<2, T, Q> const& x) + { + char const * PrefixStr = prefix::value(); + char const * LiteralStr = literal::is_iec559>::value(); + std::string FormatStr(detail::format("%svec2(%s, %s)", + PrefixStr, + LiteralStr, LiteralStr)); + + return detail::format(FormatStr.c_str(), + static_cast::value_type>(x[0]), + static_cast::value_type>(x[1])); + } + }; + + template + struct compute_to_string > + { + GLM_FUNC_QUALIFIER static std::string call(vec<3, T, Q> const& x) + { + char const * PrefixStr = prefix::value(); + char const * LiteralStr = literal::is_iec559>::value(); + std::string FormatStr(detail::format("%svec3(%s, %s, %s)", + PrefixStr, + LiteralStr, LiteralStr, LiteralStr)); + + return detail::format(FormatStr.c_str(), + static_cast::value_type>(x[0]), + static_cast::value_type>(x[1]), + static_cast::value_type>(x[2])); + } + }; + + template + struct compute_to_string > + { + GLM_FUNC_QUALIFIER static std::string call(vec<4, T, Q> const& x) + { + char const * PrefixStr = prefix::value(); + char const * LiteralStr = literal::is_iec559>::value(); + std::string FormatStr(detail::format("%svec4(%s, %s, %s, %s)", + PrefixStr, + LiteralStr, LiteralStr, LiteralStr, LiteralStr)); + + return detail::format(FormatStr.c_str(), + static_cast::value_type>(x[0]), + static_cast::value_type>(x[1]), + static_cast::value_type>(x[2]), + static_cast::value_type>(x[3])); + } + }; + + + template + struct compute_to_string > + { + GLM_FUNC_QUALIFIER static std::string call(mat<2, 2, T, Q> const& x) + { + char const * PrefixStr = prefix::value(); + char const * LiteralStr = literal::is_iec559>::value(); + std::string FormatStr(detail::format("%smat2x2((%s, %s), (%s, %s))", + PrefixStr, + LiteralStr, LiteralStr, + LiteralStr, LiteralStr)); + + return detail::format(FormatStr.c_str(), + static_cast::value_type>(x[0][0]), static_cast::value_type>(x[0][1]), + static_cast::value_type>(x[1][0]), static_cast::value_type>(x[1][1])); + } + }; + + template + struct compute_to_string > + { + GLM_FUNC_QUALIFIER static std::string call(mat<2, 3, T, Q> const& x) + { + char const * PrefixStr = prefix::value(); + char const * LiteralStr = literal::is_iec559>::value(); + std::string FormatStr(detail::format("%smat2x3((%s, %s, %s), (%s, %s, %s))", + PrefixStr, + LiteralStr, LiteralStr, LiteralStr, + LiteralStr, LiteralStr, LiteralStr)); + + return detail::format(FormatStr.c_str(), + static_cast::value_type>(x[0][0]), static_cast::value_type>(x[0][1]), static_cast::value_type>(x[0][2]), + static_cast::value_type>(x[1][0]), static_cast::value_type>(x[1][1]), static_cast::value_type>(x[1][2])); + } + }; + + template + struct compute_to_string > + { + GLM_FUNC_QUALIFIER static std::string call(mat<2, 4, T, Q> const& x) + { + char const * PrefixStr = prefix::value(); + char const * LiteralStr = literal::is_iec559>::value(); + std::string FormatStr(detail::format("%smat2x4((%s, %s, %s, %s), (%s, %s, %s, %s))", + PrefixStr, + LiteralStr, LiteralStr, LiteralStr, LiteralStr, + LiteralStr, LiteralStr, LiteralStr, LiteralStr)); + + return detail::format(FormatStr.c_str(), + static_cast::value_type>(x[0][0]), static_cast::value_type>(x[0][1]), static_cast::value_type>(x[0][2]), static_cast::value_type>(x[0][3]), + static_cast::value_type>(x[1][0]), static_cast::value_type>(x[1][1]), static_cast::value_type>(x[1][2]), static_cast::value_type>(x[1][3])); + } + }; + + template + struct compute_to_string > + { + GLM_FUNC_QUALIFIER static std::string call(mat<3, 2, T, Q> const& x) + { + char const * PrefixStr = prefix::value(); + char const * LiteralStr = literal::is_iec559>::value(); + std::string FormatStr(detail::format("%smat3x2((%s, %s), (%s, %s), (%s, %s))", + PrefixStr, + LiteralStr, LiteralStr, + LiteralStr, LiteralStr, + LiteralStr, LiteralStr)); + + return detail::format(FormatStr.c_str(), + static_cast::value_type>(x[0][0]), static_cast::value_type>(x[0][1]), + static_cast::value_type>(x[1][0]), static_cast::value_type>(x[1][1]), + static_cast::value_type>(x[2][0]), static_cast::value_type>(x[2][1])); + } + }; + + template + struct compute_to_string > + { + GLM_FUNC_QUALIFIER static std::string call(mat<3, 3, T, Q> const& x) + { + char const * PrefixStr = prefix::value(); + char const * LiteralStr = literal::is_iec559>::value(); + std::string FormatStr(detail::format("%smat3x3((%s, %s, %s), (%s, %s, %s), (%s, %s, %s))", + PrefixStr, + LiteralStr, LiteralStr, LiteralStr, + LiteralStr, LiteralStr, LiteralStr, + LiteralStr, LiteralStr, LiteralStr)); + + return detail::format(FormatStr.c_str(), + static_cast::value_type>(x[0][0]), static_cast::value_type>(x[0][1]), static_cast::value_type>(x[0][2]), + static_cast::value_type>(x[1][0]), static_cast::value_type>(x[1][1]), static_cast::value_type>(x[1][2]), + static_cast::value_type>(x[2][0]), static_cast::value_type>(x[2][1]), static_cast::value_type>(x[2][2])); + } + }; + + template + struct compute_to_string > + { + GLM_FUNC_QUALIFIER static std::string call(mat<3, 4, T, Q> const& x) + { + char const * PrefixStr = prefix::value(); + char const * LiteralStr = literal::is_iec559>::value(); + std::string FormatStr(detail::format("%smat3x4((%s, %s, %s, %s), (%s, %s, %s, %s), (%s, %s, %s, %s))", + PrefixStr, + LiteralStr, LiteralStr, LiteralStr, LiteralStr, + LiteralStr, LiteralStr, LiteralStr, LiteralStr, + LiteralStr, LiteralStr, LiteralStr, LiteralStr)); + + return detail::format(FormatStr.c_str(), + static_cast::value_type>(x[0][0]), static_cast::value_type>(x[0][1]), static_cast::value_type>(x[0][2]), static_cast::value_type>(x[0][3]), + static_cast::value_type>(x[1][0]), static_cast::value_type>(x[1][1]), static_cast::value_type>(x[1][2]), static_cast::value_type>(x[1][3]), + static_cast::value_type>(x[2][0]), static_cast::value_type>(x[2][1]), static_cast::value_type>(x[2][2]), static_cast::value_type>(x[2][3])); + } + }; + + template + struct compute_to_string > + { + GLM_FUNC_QUALIFIER static std::string call(mat<4, 2, T, Q> const& x) + { + char const * PrefixStr = prefix::value(); + char const * LiteralStr = literal::is_iec559>::value(); + std::string FormatStr(detail::format("%smat4x2((%s, %s), (%s, %s), (%s, %s), (%s, %s))", + PrefixStr, + LiteralStr, LiteralStr, + LiteralStr, LiteralStr, + LiteralStr, LiteralStr, + LiteralStr, LiteralStr)); + + return detail::format(FormatStr.c_str(), + static_cast::value_type>(x[0][0]), static_cast::value_type>(x[0][1]), + static_cast::value_type>(x[1][0]), static_cast::value_type>(x[1][1]), + static_cast::value_type>(x[2][0]), static_cast::value_type>(x[2][1]), + static_cast::value_type>(x[3][0]), static_cast::value_type>(x[3][1])); + } + }; + + template + struct compute_to_string > + { + GLM_FUNC_QUALIFIER static std::string call(mat<4, 3, T, Q> const& x) + { + char const * PrefixStr = prefix::value(); + char const * LiteralStr = literal::is_iec559>::value(); + std::string FormatStr(detail::format("%smat4x3((%s, %s, %s), (%s, %s, %s), (%s, %s, %s), (%s, %s, %s))", + PrefixStr, + LiteralStr, LiteralStr, LiteralStr, + LiteralStr, LiteralStr, LiteralStr, + LiteralStr, LiteralStr, LiteralStr, + LiteralStr, LiteralStr, LiteralStr)); + + return detail::format(FormatStr.c_str(), + static_cast::value_type>(x[0][0]), static_cast::value_type>(x[0][1]), static_cast::value_type>(x[0][2]), + static_cast::value_type>(x[1][0]), static_cast::value_type>(x[1][1]), static_cast::value_type>(x[1][2]), + static_cast::value_type>(x[2][0]), static_cast::value_type>(x[2][1]), static_cast::value_type>(x[2][2]), + static_cast::value_type>(x[3][0]), static_cast::value_type>(x[3][1]), static_cast::value_type>(x[3][2])); + } + }; + + template + struct compute_to_string > + { + GLM_FUNC_QUALIFIER static std::string call(mat<4, 4, T, Q> const& x) + { + char const * PrefixStr = prefix::value(); + char const * LiteralStr = literal::is_iec559>::value(); + std::string FormatStr(detail::format("%smat4x4((%s, %s, %s, %s), (%s, %s, %s, %s), (%s, %s, %s, %s), (%s, %s, %s, %s))", + PrefixStr, + LiteralStr, LiteralStr, LiteralStr, LiteralStr, + LiteralStr, LiteralStr, LiteralStr, LiteralStr, + LiteralStr, LiteralStr, LiteralStr, LiteralStr, + LiteralStr, LiteralStr, LiteralStr, LiteralStr)); + + return detail::format(FormatStr.c_str(), + static_cast::value_type>(x[0][0]), static_cast::value_type>(x[0][1]), static_cast::value_type>(x[0][2]), static_cast::value_type>(x[0][3]), + static_cast::value_type>(x[1][0]), static_cast::value_type>(x[1][1]), static_cast::value_type>(x[1][2]), static_cast::value_type>(x[1][3]), + static_cast::value_type>(x[2][0]), static_cast::value_type>(x[2][1]), static_cast::value_type>(x[2][2]), static_cast::value_type>(x[2][3]), + static_cast::value_type>(x[3][0]), static_cast::value_type>(x[3][1]), static_cast::value_type>(x[3][2]), static_cast::value_type>(x[3][3])); + } + }; + + + template + struct compute_to_string > + { + GLM_FUNC_QUALIFIER static std::string call(qua const& x) + { + char const * PrefixStr = prefix::value(); + char const * LiteralStr = literal::is_iec559>::value(); + std::string FormatStr(detail::format("%squat(%s, {%s, %s, %s})", + PrefixStr, + LiteralStr, LiteralStr, LiteralStr, LiteralStr)); + + return detail::format(FormatStr.c_str(), + static_cast::value_type>(x[3]), + static_cast::value_type>(x[0]), + static_cast::value_type>(x[1]), + static_cast::value_type>(x[2])); + } + }; + + template + struct compute_to_string > + { + GLM_FUNC_QUALIFIER static std::string call(tdualquat const& x) + { + char const * PrefixStr = prefix::value(); + char const * LiteralStr = literal::is_iec559>::value(); + std::string FormatStr(detail::format("%sdualquat((%s, {%s, %s, %s}), (%s, {%s, %s, %s}))", + PrefixStr, + LiteralStr, LiteralStr, LiteralStr, LiteralStr, + LiteralStr, LiteralStr, LiteralStr, LiteralStr)); + + return detail::format(FormatStr.c_str(), + static_cast::value_type>(x.real[3]), + static_cast::value_type>(x.real[0]), + static_cast::value_type>(x.real[1]), + static_cast::value_type>(x.real[2]), + static_cast::value_type>(x.dual[3]), + static_cast::value_type>(x.dual[0]), + static_cast::value_type>(x.dual[1]), + static_cast::value_type>(x.dual[2])); + } + }; + +}//namespace detail + +template +GLM_FUNC_QUALIFIER std::string to_string(matType const& x) +{ + return detail::compute_to_string::call(x); +} + +}//namespace glm diff --git a/src/GLMath/glm/gtx/texture.hpp b/src/GLMath/glm/gtx/texture.hpp new file mode 100644 index 000000000..20585e68c --- /dev/null +++ b/src/GLMath/glm/gtx/texture.hpp @@ -0,0 +1,46 @@ +/// @ref gtx_texture +/// @file glm/gtx/texture.hpp +/// +/// @see core (dependence) +/// +/// @defgroup gtx_texture GLM_GTX_texture +/// @ingroup gtx +/// +/// Include to use the features of this extension. +/// +/// Wrapping mode of texture coordinates. + +#pragma once + +// Dependency: +#include "../glm.hpp" +#include "../gtc/integer.hpp" +#include "../gtx/component_wise.hpp" + +#if GLM_MESSAGES == GLM_ENABLE && !defined(GLM_EXT_INCLUDED) +# ifndef GLM_ENABLE_EXPERIMENTAL +# pragma message("GLM: GLM_GTX_texture is an experimental extension and may change in the future. Use #define GLM_ENABLE_EXPERIMENTAL before including it, if you really want to use it.") +# else +# pragma message("GLM: GLM_GTX_texture extension included") +# endif +#endif + +namespace glm +{ + /// @addtogroup gtx_texture + /// @{ + + /// Compute the number of mipmaps levels necessary to create a mipmap complete texture + /// + /// @param Extent Extent of the texture base level mipmap + /// @tparam L Integer between 1 and 4 included that qualify the dimension of the vector + /// @tparam T Floating-point or signed integer scalar types + /// @tparam Q Value from qualifier enum + template + T levels(vec const& Extent); + + /// @} +}// namespace glm + +#include "texture.inl" + diff --git a/src/GLMath/glm/gtx/texture.inl b/src/GLMath/glm/gtx/texture.inl new file mode 100644 index 000000000..593c82614 --- /dev/null +++ b/src/GLMath/glm/gtx/texture.inl @@ -0,0 +1,17 @@ +/// @ref gtx_texture + +namespace glm +{ + template + inline T levels(vec const& Extent) + { + return glm::log2(compMax(Extent)) + static_cast(1); + } + + template + inline T levels(T Extent) + { + return vec<1, T, defaultp>(Extent).x; + } +}//namespace glm + diff --git a/src/GLMath/glm/gtx/transform.hpp b/src/GLMath/glm/gtx/transform.hpp new file mode 100644 index 000000000..0279fc8bd --- /dev/null +++ b/src/GLMath/glm/gtx/transform.hpp @@ -0,0 +1,60 @@ +/// @ref gtx_transform +/// @file glm/gtx/transform.hpp +/// +/// @see core (dependence) +/// @see gtc_matrix_transform (dependence) +/// @see gtx_transform +/// @see gtx_transform2 +/// +/// @defgroup gtx_transform GLM_GTX_transform +/// @ingroup gtx +/// +/// Include to use the features of this extension. +/// +/// Add transformation matrices + +#pragma once + +// Dependency: +#include "../glm.hpp" +#include "../gtc/matrix_transform.hpp" + +#if GLM_MESSAGES == GLM_ENABLE && !defined(GLM_EXT_INCLUDED) +# ifndef GLM_ENABLE_EXPERIMENTAL +# pragma message("GLM: GLM_GTX_transform is an experimental extension and may change in the future. Use #define GLM_ENABLE_EXPERIMENTAL before including it, if you really want to use it.") +# else +# pragma message("GLM: GLM_GTX_transform extension included") +# endif +#endif + +namespace glm +{ + /// @addtogroup gtx_transform + /// @{ + + /// Transforms a matrix with a translation 4 * 4 matrix created from 3 scalars. + /// @see gtc_matrix_transform + /// @see gtx_transform + template + GLM_FUNC_DECL mat<4, 4, T, Q> translate( + vec<3, T, Q> const& v); + + /// Builds a rotation 4 * 4 matrix created from an axis of 3 scalars and an angle expressed in radians. + /// @see gtc_matrix_transform + /// @see gtx_transform + template + GLM_FUNC_DECL mat<4, 4, T, Q> rotate( + T angle, + vec<3, T, Q> const& v); + + /// Transforms a matrix with a scale 4 * 4 matrix created from a vector of 3 components. + /// @see gtc_matrix_transform + /// @see gtx_transform + template + GLM_FUNC_DECL mat<4, 4, T, Q> scale( + vec<3, T, Q> const& v); + + /// @} +}// namespace glm + +#include "transform.inl" diff --git a/src/GLMath/glm/gtx/transform.inl b/src/GLMath/glm/gtx/transform.inl new file mode 100644 index 000000000..48ee6801b --- /dev/null +++ b/src/GLMath/glm/gtx/transform.inl @@ -0,0 +1,23 @@ +/// @ref gtx_transform + +namespace glm +{ + template + GLM_FUNC_QUALIFIER mat<4, 4, T, Q> translate(vec<3, T, Q> const& v) + { + return translate(mat<4, 4, T, Q>(static_cast(1)), v); + } + + template + GLM_FUNC_QUALIFIER mat<4, 4, T, Q> rotate(T angle, vec<3, T, Q> const& v) + { + return rotate(mat<4, 4, T, Q>(static_cast(1)), angle, v); + } + + template + GLM_FUNC_QUALIFIER mat<4, 4, T, Q> scale(vec<3, T, Q> const& v) + { + return scale(mat<4, 4, T, Q>(static_cast(1)), v); + } + +}//namespace glm diff --git a/src/GLMath/glm/gtx/transform2.hpp b/src/GLMath/glm/gtx/transform2.hpp new file mode 100644 index 000000000..0d8ba9d90 --- /dev/null +++ b/src/GLMath/glm/gtx/transform2.hpp @@ -0,0 +1,89 @@ +/// @ref gtx_transform2 +/// @file glm/gtx/transform2.hpp +/// +/// @see core (dependence) +/// @see gtx_transform (dependence) +/// +/// @defgroup gtx_transform2 GLM_GTX_transform2 +/// @ingroup gtx +/// +/// Include to use the features of this extension. +/// +/// Add extra transformation matrices + +#pragma once + +// Dependency: +#include "../glm.hpp" +#include "../gtx/transform.hpp" + +#if GLM_MESSAGES == GLM_ENABLE && !defined(GLM_EXT_INCLUDED) +# ifndef GLM_ENABLE_EXPERIMENTAL +# pragma message("GLM: GLM_GTX_transform2 is an experimental extension and may change in the future. Use #define GLM_ENABLE_EXPERIMENTAL before including it, if you really want to use it.") +# else +# pragma message("GLM: GLM_GTX_transform2 extension included") +# endif +#endif + +namespace glm +{ + /// @addtogroup gtx_transform2 + /// @{ + + //! Transforms a matrix with a shearing on X axis. + //! From GLM_GTX_transform2 extension. + template + GLM_FUNC_DECL mat<3, 3, T, Q> shearX2D(mat<3, 3, T, Q> const& m, T y); + + //! Transforms a matrix with a shearing on Y axis. + //! From GLM_GTX_transform2 extension. + template + GLM_FUNC_DECL mat<3, 3, T, Q> shearY2D(mat<3, 3, T, Q> const& m, T x); + + //! Transforms a matrix with a shearing on X axis + //! From GLM_GTX_transform2 extension. + template + GLM_FUNC_DECL mat<4, 4, T, Q> shearX3D(mat<4, 4, T, Q> const& m, T y, T z); + + //! Transforms a matrix with a shearing on Y axis. + //! From GLM_GTX_transform2 extension. + template + GLM_FUNC_DECL mat<4, 4, T, Q> shearY3D(mat<4, 4, T, Q> const& m, T x, T z); + + //! Transforms a matrix with a shearing on Z axis. + //! From GLM_GTX_transform2 extension. + template + GLM_FUNC_DECL mat<4, 4, T, Q> shearZ3D(mat<4, 4, T, Q> const& m, T x, T y); + + //template GLM_FUNC_QUALIFIER mat<4, 4, T, Q> shear(const mat<4, 4, T, Q> & m, shearPlane, planePoint, angle) + // Identity + tan(angle) * cross(Normal, OnPlaneVector) 0 + // - dot(PointOnPlane, normal) * OnPlaneVector 1 + + // Reflect functions seem to don't work + //template mat<3, 3, T, Q> reflect2D(const mat<3, 3, T, Q> & m, const vec<3, T, Q>& normal){return reflect2DGTX(m, normal);} //!< \brief Build a reflection matrix (from GLM_GTX_transform2 extension) + //template mat<4, 4, T, Q> reflect3D(const mat<4, 4, T, Q> & m, const vec<3, T, Q>& normal){return reflect3DGTX(m, normal);} //!< \brief Build a reflection matrix (from GLM_GTX_transform2 extension) + + //! Build planar projection matrix along normal axis. + //! From GLM_GTX_transform2 extension. + template + GLM_FUNC_DECL mat<3, 3, T, Q> proj2D(mat<3, 3, T, Q> const& m, vec<3, T, Q> const& normal); + + //! Build planar projection matrix along normal axis. + //! From GLM_GTX_transform2 extension. + template + GLM_FUNC_DECL mat<4, 4, T, Q> proj3D(mat<4, 4, T, Q> const & m, vec<3, T, Q> const& normal); + + //! Build a scale bias matrix. + //! From GLM_GTX_transform2 extension. + template + GLM_FUNC_DECL mat<4, 4, T, Q> scaleBias(T scale, T bias); + + //! Build a scale bias matrix. + //! From GLM_GTX_transform2 extension. + template + GLM_FUNC_DECL mat<4, 4, T, Q> scaleBias(mat<4, 4, T, Q> const& m, T scale, T bias); + + /// @} +}// namespace glm + +#include "transform2.inl" diff --git a/src/GLMath/glm/gtx/transform2.inl b/src/GLMath/glm/gtx/transform2.inl new file mode 100644 index 000000000..2b53198b3 --- /dev/null +++ b/src/GLMath/glm/gtx/transform2.inl @@ -0,0 +1,125 @@ +/// @ref gtx_transform2 + +namespace glm +{ + template + GLM_FUNC_QUALIFIER mat<3, 3, T, Q> shearX2D(mat<3, 3, T, Q> const& m, T s) + { + mat<3, 3, T, Q> r(1); + r[1][0] = s; + return m * r; + } + + template + GLM_FUNC_QUALIFIER mat<3, 3, T, Q> shearY2D(mat<3, 3, T, Q> const& m, T s) + { + mat<3, 3, T, Q> r(1); + r[0][1] = s; + return m * r; + } + + template + GLM_FUNC_QUALIFIER mat<4, 4, T, Q> shearX3D(mat<4, 4, T, Q> const& m, T s, T t) + { + mat<4, 4, T, Q> r(1); + r[0][1] = s; + r[0][2] = t; + return m * r; + } + + template + GLM_FUNC_QUALIFIER mat<4, 4, T, Q> shearY3D(mat<4, 4, T, Q> const& m, T s, T t) + { + mat<4, 4, T, Q> r(1); + r[1][0] = s; + r[1][2] = t; + return m * r; + } + + template + GLM_FUNC_QUALIFIER mat<4, 4, T, Q> shearZ3D(mat<4, 4, T, Q> const& m, T s, T t) + { + mat<4, 4, T, Q> r(1); + r[2][0] = s; + r[2][1] = t; + return m * r; + } + + template + GLM_FUNC_QUALIFIER mat<3, 3, T, Q> reflect2D(mat<3, 3, T, Q> const& m, vec<3, T, Q> const& normal) + { + mat<3, 3, T, Q> r(static_cast(1)); + r[0][0] = static_cast(1) - static_cast(2) * normal.x * normal.x; + r[0][1] = -static_cast(2) * normal.x * normal.y; + r[1][0] = -static_cast(2) * normal.x * normal.y; + r[1][1] = static_cast(1) - static_cast(2) * normal.y * normal.y; + return m * r; + } + + template + GLM_FUNC_QUALIFIER mat<4, 4, T, Q> reflect3D(mat<4, 4, T, Q> const& m, vec<3, T, Q> const& normal) + { + mat<4, 4, T, Q> r(static_cast(1)); + r[0][0] = static_cast(1) - static_cast(2) * normal.x * normal.x; + r[0][1] = -static_cast(2) * normal.x * normal.y; + r[0][2] = -static_cast(2) * normal.x * normal.z; + + r[1][0] = -static_cast(2) * normal.x * normal.y; + r[1][1] = static_cast(1) - static_cast(2) * normal.y * normal.y; + r[1][2] = -static_cast(2) * normal.y * normal.z; + + r[2][0] = -static_cast(2) * normal.x * normal.z; + r[2][1] = -static_cast(2) * normal.y * normal.z; + r[2][2] = static_cast(1) - static_cast(2) * normal.z * normal.z; + return m * r; + } + + template + GLM_FUNC_QUALIFIER mat<3, 3, T, Q> proj2D( + const mat<3, 3, T, Q>& m, + const vec<3, T, Q>& normal) + { + mat<3, 3, T, Q> r(static_cast(1)); + r[0][0] = static_cast(1) - normal.x * normal.x; + r[0][1] = - normal.x * normal.y; + r[1][0] = - normal.x * normal.y; + r[1][1] = static_cast(1) - normal.y * normal.y; + return m * r; + } + + template + GLM_FUNC_QUALIFIER mat<4, 4, T, Q> proj3D( + const mat<4, 4, T, Q>& m, + const vec<3, T, Q>& normal) + { + mat<4, 4, T, Q> r(static_cast(1)); + r[0][0] = static_cast(1) - normal.x * normal.x; + r[0][1] = - normal.x * normal.y; + r[0][2] = - normal.x * normal.z; + r[1][0] = - normal.x * normal.y; + r[1][1] = static_cast(1) - normal.y * normal.y; + r[1][2] = - normal.y * normal.z; + r[2][0] = - normal.x * normal.z; + r[2][1] = - normal.y * normal.z; + r[2][2] = static_cast(1) - normal.z * normal.z; + return m * r; + } + + template + GLM_FUNC_QUALIFIER mat<4, 4, T, Q> scaleBias(T scale, T bias) + { + mat<4, 4, T, Q> result; + result[3] = vec<4, T, Q>(vec<3, T, Q>(bias), static_cast(1)); + result[0][0] = scale; + result[1][1] = scale; + result[2][2] = scale; + return result; + } + + template + GLM_FUNC_QUALIFIER mat<4, 4, T, Q> scaleBias(mat<4, 4, T, Q> const& m, T scale, T bias) + { + return m * scaleBias(scale, bias); + } +}//namespace glm + diff --git a/src/GLMath/glm/gtx/type_aligned.hpp b/src/GLMath/glm/gtx/type_aligned.hpp new file mode 100644 index 000000000..2ae522c1f --- /dev/null +++ b/src/GLMath/glm/gtx/type_aligned.hpp @@ -0,0 +1,982 @@ +/// @ref gtx_type_aligned +/// @file glm/gtx/type_aligned.hpp +/// +/// @see core (dependence) +/// @see gtc_quaternion (dependence) +/// +/// @defgroup gtx_type_aligned GLM_GTX_type_aligned +/// @ingroup gtx +/// +/// Include to use the features of this extension. +/// +/// Defines aligned types. + +#pragma once + +// Dependency: +#include "../gtc/type_precision.hpp" +#include "../gtc/quaternion.hpp" + +#if GLM_MESSAGES == GLM_ENABLE && !defined(GLM_EXT_INCLUDED) +# ifndef GLM_ENABLE_EXPERIMENTAL +# pragma message("GLM: GLM_GTX_type_aligned is an experimental extension and may change in the future. Use #define GLM_ENABLE_EXPERIMENTAL before including it, if you really want to use it.") +# else +# pragma message("GLM: GLM_GTX_type_aligned extension included") +# endif +#endif + +namespace glm +{ + /////////////////////////// + // Signed int vector types + + /// @addtogroup gtx_type_aligned + /// @{ + + /// Low qualifier 8 bit signed integer aligned scalar type. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(lowp_int8, aligned_lowp_int8, 1); + + /// Low qualifier 16 bit signed integer aligned scalar type. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(lowp_int16, aligned_lowp_int16, 2); + + /// Low qualifier 32 bit signed integer aligned scalar type. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(lowp_int32, aligned_lowp_int32, 4); + + /// Low qualifier 64 bit signed integer aligned scalar type. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(lowp_int64, aligned_lowp_int64, 8); + + + /// Low qualifier 8 bit signed integer aligned scalar type. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(lowp_int8_t, aligned_lowp_int8_t, 1); + + /// Low qualifier 16 bit signed integer aligned scalar type. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(lowp_int16_t, aligned_lowp_int16_t, 2); + + /// Low qualifier 32 bit signed integer aligned scalar type. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(lowp_int32_t, aligned_lowp_int32_t, 4); + + /// Low qualifier 64 bit signed integer aligned scalar type. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(lowp_int64_t, aligned_lowp_int64_t, 8); + + + /// Low qualifier 8 bit signed integer aligned scalar type. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(lowp_i8, aligned_lowp_i8, 1); + + /// Low qualifier 16 bit signed integer aligned scalar type. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(lowp_i16, aligned_lowp_i16, 2); + + /// Low qualifier 32 bit signed integer aligned scalar type. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(lowp_i32, aligned_lowp_i32, 4); + + /// Low qualifier 64 bit signed integer aligned scalar type. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(lowp_i64, aligned_lowp_i64, 8); + + + /// Medium qualifier 8 bit signed integer aligned scalar type. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(mediump_int8, aligned_mediump_int8, 1); + + /// Medium qualifier 16 bit signed integer aligned scalar type. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(mediump_int16, aligned_mediump_int16, 2); + + /// Medium qualifier 32 bit signed integer aligned scalar type. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(mediump_int32, aligned_mediump_int32, 4); + + /// Medium qualifier 64 bit signed integer aligned scalar type. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(mediump_int64, aligned_mediump_int64, 8); + + + /// Medium qualifier 8 bit signed integer aligned scalar type. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(mediump_int8_t, aligned_mediump_int8_t, 1); + + /// Medium qualifier 16 bit signed integer aligned scalar type. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(mediump_int16_t, aligned_mediump_int16_t, 2); + + /// Medium qualifier 32 bit signed integer aligned scalar type. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(mediump_int32_t, aligned_mediump_int32_t, 4); + + /// Medium qualifier 64 bit signed integer aligned scalar type. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(mediump_int64_t, aligned_mediump_int64_t, 8); + + + /// Medium qualifier 8 bit signed integer aligned scalar type. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(mediump_i8, aligned_mediump_i8, 1); + + /// Medium qualifier 16 bit signed integer aligned scalar type. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(mediump_i16, aligned_mediump_i16, 2); + + /// Medium qualifier 32 bit signed integer aligned scalar type. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(mediump_i32, aligned_mediump_i32, 4); + + /// Medium qualifier 64 bit signed integer aligned scalar type. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(mediump_i64, aligned_mediump_i64, 8); + + + /// High qualifier 8 bit signed integer aligned scalar type. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(highp_int8, aligned_highp_int8, 1); + + /// High qualifier 16 bit signed integer aligned scalar type. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(highp_int16, aligned_highp_int16, 2); + + /// High qualifier 32 bit signed integer aligned scalar type. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(highp_int32, aligned_highp_int32, 4); + + /// High qualifier 64 bit signed integer aligned scalar type. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(highp_int64, aligned_highp_int64, 8); + + + /// High qualifier 8 bit signed integer aligned scalar type. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(highp_int8_t, aligned_highp_int8_t, 1); + + /// High qualifier 16 bit signed integer aligned scalar type. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(highp_int16_t, aligned_highp_int16_t, 2); + + /// High qualifier 32 bit signed integer aligned scalar type. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(highp_int32_t, aligned_highp_int32_t, 4); + + /// High qualifier 64 bit signed integer aligned scalar type. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(highp_int64_t, aligned_highp_int64_t, 8); + + + /// High qualifier 8 bit signed integer aligned scalar type. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(highp_i8, aligned_highp_i8, 1); + + /// High qualifier 16 bit signed integer aligned scalar type. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(highp_i16, aligned_highp_i16, 2); + + /// High qualifier 32 bit signed integer aligned scalar type. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(highp_i32, aligned_highp_i32, 4); + + /// High qualifier 64 bit signed integer aligned scalar type. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(highp_i64, aligned_highp_i64, 8); + + + /// Default qualifier 8 bit signed integer aligned scalar type. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(int8, aligned_int8, 1); + + /// Default qualifier 16 bit signed integer aligned scalar type. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(int16, aligned_int16, 2); + + /// Default qualifier 32 bit signed integer aligned scalar type. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(int32, aligned_int32, 4); + + /// Default qualifier 64 bit signed integer aligned scalar type. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(int64, aligned_int64, 8); + + + /// Default qualifier 8 bit signed integer aligned scalar type. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(int8_t, aligned_int8_t, 1); + + /// Default qualifier 16 bit signed integer aligned scalar type. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(int16_t, aligned_int16_t, 2); + + /// Default qualifier 32 bit signed integer aligned scalar type. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(int32_t, aligned_int32_t, 4); + + /// Default qualifier 64 bit signed integer aligned scalar type. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(int64_t, aligned_int64_t, 8); + + + /// Default qualifier 8 bit signed integer aligned scalar type. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(i8, aligned_i8, 1); + + /// Default qualifier 16 bit signed integer aligned scalar type. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(i16, aligned_i16, 2); + + /// Default qualifier 32 bit signed integer aligned scalar type. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(i32, aligned_i32, 4); + + /// Default qualifier 64 bit signed integer aligned scalar type. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(i64, aligned_i64, 8); + + + /// Default qualifier 32 bit signed integer aligned scalar type. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(ivec1, aligned_ivec1, 4); + + /// Default qualifier 32 bit signed integer aligned vector of 2 components type. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(ivec2, aligned_ivec2, 8); + + /// Default qualifier 32 bit signed integer aligned vector of 3 components type. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(ivec3, aligned_ivec3, 16); + + /// Default qualifier 32 bit signed integer aligned vector of 4 components type. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(ivec4, aligned_ivec4, 16); + + + /// Default qualifier 8 bit signed integer aligned scalar type. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(i8vec1, aligned_i8vec1, 1); + + /// Default qualifier 8 bit signed integer aligned vector of 2 components type. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(i8vec2, aligned_i8vec2, 2); + + /// Default qualifier 8 bit signed integer aligned vector of 3 components type. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(i8vec3, aligned_i8vec3, 4); + + /// Default qualifier 8 bit signed integer aligned vector of 4 components type. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(i8vec4, aligned_i8vec4, 4); + + + /// Default qualifier 16 bit signed integer aligned scalar type. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(i16vec1, aligned_i16vec1, 2); + + /// Default qualifier 16 bit signed integer aligned vector of 2 components type. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(i16vec2, aligned_i16vec2, 4); + + /// Default qualifier 16 bit signed integer aligned vector of 3 components type. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(i16vec3, aligned_i16vec3, 8); + + /// Default qualifier 16 bit signed integer aligned vector of 4 components type. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(i16vec4, aligned_i16vec4, 8); + + + /// Default qualifier 32 bit signed integer aligned scalar type. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(i32vec1, aligned_i32vec1, 4); + + /// Default qualifier 32 bit signed integer aligned vector of 2 components type. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(i32vec2, aligned_i32vec2, 8); + + /// Default qualifier 32 bit signed integer aligned vector of 3 components type. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(i32vec3, aligned_i32vec3, 16); + + /// Default qualifier 32 bit signed integer aligned vector of 4 components type. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(i32vec4, aligned_i32vec4, 16); + + + /// Default qualifier 64 bit signed integer aligned scalar type. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(i64vec1, aligned_i64vec1, 8); + + /// Default qualifier 64 bit signed integer aligned vector of 2 components type. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(i64vec2, aligned_i64vec2, 16); + + /// Default qualifier 64 bit signed integer aligned vector of 3 components type. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(i64vec3, aligned_i64vec3, 32); + + /// Default qualifier 64 bit signed integer aligned vector of 4 components type. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(i64vec4, aligned_i64vec4, 32); + + + ///////////////////////////// + // Unsigned int vector types + + /// Low qualifier 8 bit unsigned integer aligned scalar type. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(lowp_uint8, aligned_lowp_uint8, 1); + + /// Low qualifier 16 bit unsigned integer aligned scalar type. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(lowp_uint16, aligned_lowp_uint16, 2); + + /// Low qualifier 32 bit unsigned integer aligned scalar type. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(lowp_uint32, aligned_lowp_uint32, 4); + + /// Low qualifier 64 bit unsigned integer aligned scalar type. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(lowp_uint64, aligned_lowp_uint64, 8); + + + /// Low qualifier 8 bit unsigned integer aligned scalar type. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(lowp_uint8_t, aligned_lowp_uint8_t, 1); + + /// Low qualifier 16 bit unsigned integer aligned scalar type. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(lowp_uint16_t, aligned_lowp_uint16_t, 2); + + /// Low qualifier 32 bit unsigned integer aligned scalar type. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(lowp_uint32_t, aligned_lowp_uint32_t, 4); + + /// Low qualifier 64 bit unsigned integer aligned scalar type. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(lowp_uint64_t, aligned_lowp_uint64_t, 8); + + + /// Low qualifier 8 bit unsigned integer aligned scalar type. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(lowp_u8, aligned_lowp_u8, 1); + + /// Low qualifier 16 bit unsigned integer aligned scalar type. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(lowp_u16, aligned_lowp_u16, 2); + + /// Low qualifier 32 bit unsigned integer aligned scalar type. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(lowp_u32, aligned_lowp_u32, 4); + + /// Low qualifier 64 bit unsigned integer aligned scalar type. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(lowp_u64, aligned_lowp_u64, 8); + + + /// Medium qualifier 8 bit unsigned integer aligned scalar type. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(mediump_uint8, aligned_mediump_uint8, 1); + + /// Medium qualifier 16 bit unsigned integer aligned scalar type. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(mediump_uint16, aligned_mediump_uint16, 2); + + /// Medium qualifier 32 bit unsigned integer aligned scalar type. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(mediump_uint32, aligned_mediump_uint32, 4); + + /// Medium qualifier 64 bit unsigned integer aligned scalar type. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(mediump_uint64, aligned_mediump_uint64, 8); + + + /// Medium qualifier 8 bit unsigned integer aligned scalar type. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(mediump_uint8_t, aligned_mediump_uint8_t, 1); + + /// Medium qualifier 16 bit unsigned integer aligned scalar type. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(mediump_uint16_t, aligned_mediump_uint16_t, 2); + + /// Medium qualifier 32 bit unsigned integer aligned scalar type. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(mediump_uint32_t, aligned_mediump_uint32_t, 4); + + /// Medium qualifier 64 bit unsigned integer aligned scalar type. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(mediump_uint64_t, aligned_mediump_uint64_t, 8); + + + /// Medium qualifier 8 bit unsigned integer aligned scalar type. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(mediump_u8, aligned_mediump_u8, 1); + + /// Medium qualifier 16 bit unsigned integer aligned scalar type. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(mediump_u16, aligned_mediump_u16, 2); + + /// Medium qualifier 32 bit unsigned integer aligned scalar type. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(mediump_u32, aligned_mediump_u32, 4); + + /// Medium qualifier 64 bit unsigned integer aligned scalar type. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(mediump_u64, aligned_mediump_u64, 8); + + + /// High qualifier 8 bit unsigned integer aligned scalar type. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(highp_uint8, aligned_highp_uint8, 1); + + /// High qualifier 16 bit unsigned integer aligned scalar type. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(highp_uint16, aligned_highp_uint16, 2); + + /// High qualifier 32 bit unsigned integer aligned scalar type. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(highp_uint32, aligned_highp_uint32, 4); + + /// High qualifier 64 bit unsigned integer aligned scalar type. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(highp_uint64, aligned_highp_uint64, 8); + + + /// High qualifier 8 bit unsigned integer aligned scalar type. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(highp_uint8_t, aligned_highp_uint8_t, 1); + + /// High qualifier 16 bit unsigned integer aligned scalar type. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(highp_uint16_t, aligned_highp_uint16_t, 2); + + /// High qualifier 32 bit unsigned integer aligned scalar type. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(highp_uint32_t, aligned_highp_uint32_t, 4); + + /// High qualifier 64 bit unsigned integer aligned scalar type. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(highp_uint64_t, aligned_highp_uint64_t, 8); + + + /// High qualifier 8 bit unsigned integer aligned scalar type. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(highp_u8, aligned_highp_u8, 1); + + /// High qualifier 16 bit unsigned integer aligned scalar type. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(highp_u16, aligned_highp_u16, 2); + + /// High qualifier 32 bit unsigned integer aligned scalar type. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(highp_u32, aligned_highp_u32, 4); + + /// High qualifier 64 bit unsigned integer aligned scalar type. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(highp_u64, aligned_highp_u64, 8); + + + /// Default qualifier 8 bit unsigned integer aligned scalar type. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(uint8, aligned_uint8, 1); + + /// Default qualifier 16 bit unsigned integer aligned scalar type. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(uint16, aligned_uint16, 2); + + /// Default qualifier 32 bit unsigned integer aligned scalar type. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(uint32, aligned_uint32, 4); + + /// Default qualifier 64 bit unsigned integer aligned scalar type. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(uint64, aligned_uint64, 8); + + + /// Default qualifier 8 bit unsigned integer aligned scalar type. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(uint8_t, aligned_uint8_t, 1); + + /// Default qualifier 16 bit unsigned integer aligned scalar type. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(uint16_t, aligned_uint16_t, 2); + + /// Default qualifier 32 bit unsigned integer aligned scalar type. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(uint32_t, aligned_uint32_t, 4); + + /// Default qualifier 64 bit unsigned integer aligned scalar type. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(uint64_t, aligned_uint64_t, 8); + + + /// Default qualifier 8 bit unsigned integer aligned scalar type. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(u8, aligned_u8, 1); + + /// Default qualifier 16 bit unsigned integer aligned scalar type. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(u16, aligned_u16, 2); + + /// Default qualifier 32 bit unsigned integer aligned scalar type. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(u32, aligned_u32, 4); + + /// Default qualifier 64 bit unsigned integer aligned scalar type. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(u64, aligned_u64, 8); + + + /// Default qualifier 32 bit unsigned integer aligned scalar type. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(uvec1, aligned_uvec1, 4); + + /// Default qualifier 32 bit unsigned integer aligned vector of 2 components type. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(uvec2, aligned_uvec2, 8); + + /// Default qualifier 32 bit unsigned integer aligned vector of 3 components type. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(uvec3, aligned_uvec3, 16); + + /// Default qualifier 32 bit unsigned integer aligned vector of 4 components type. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(uvec4, aligned_uvec4, 16); + + + /// Default qualifier 8 bit unsigned integer aligned scalar type. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(u8vec1, aligned_u8vec1, 1); + + /// Default qualifier 8 bit unsigned integer aligned vector of 2 components type. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(u8vec2, aligned_u8vec2, 2); + + /// Default qualifier 8 bit unsigned integer aligned vector of 3 components type. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(u8vec3, aligned_u8vec3, 4); + + /// Default qualifier 8 bit unsigned integer aligned vector of 4 components type. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(u8vec4, aligned_u8vec4, 4); + + + /// Default qualifier 16 bit unsigned integer aligned scalar type. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(u16vec1, aligned_u16vec1, 2); + + /// Default qualifier 16 bit unsigned integer aligned vector of 2 components type. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(u16vec2, aligned_u16vec2, 4); + + /// Default qualifier 16 bit unsigned integer aligned vector of 3 components type. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(u16vec3, aligned_u16vec3, 8); + + /// Default qualifier 16 bit unsigned integer aligned vector of 4 components type. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(u16vec4, aligned_u16vec4, 8); + + + /// Default qualifier 32 bit unsigned integer aligned scalar type. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(u32vec1, aligned_u32vec1, 4); + + /// Default qualifier 32 bit unsigned integer aligned vector of 2 components type. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(u32vec2, aligned_u32vec2, 8); + + /// Default qualifier 32 bit unsigned integer aligned vector of 3 components type. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(u32vec3, aligned_u32vec3, 16); + + /// Default qualifier 32 bit unsigned integer aligned vector of 4 components type. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(u32vec4, aligned_u32vec4, 16); + + + /// Default qualifier 64 bit unsigned integer aligned scalar type. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(u64vec1, aligned_u64vec1, 8); + + /// Default qualifier 64 bit unsigned integer aligned vector of 2 components type. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(u64vec2, aligned_u64vec2, 16); + + /// Default qualifier 64 bit unsigned integer aligned vector of 3 components type. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(u64vec3, aligned_u64vec3, 32); + + /// Default qualifier 64 bit unsigned integer aligned vector of 4 components type. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(u64vec4, aligned_u64vec4, 32); + + + ////////////////////// + // Float vector types + + /// 32 bit single-qualifier floating-point aligned scalar. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(float32, aligned_float32, 4); + + /// 32 bit single-qualifier floating-point aligned scalar. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(float32_t, aligned_float32_t, 4); + + /// 32 bit single-qualifier floating-point aligned scalar. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(float32, aligned_f32, 4); + +# ifndef GLM_FORCE_SINGLE_ONLY + + /// 64 bit double-qualifier floating-point aligned scalar. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(float64, aligned_float64, 8); + + /// 64 bit double-qualifier floating-point aligned scalar. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(float64_t, aligned_float64_t, 8); + + /// 64 bit double-qualifier floating-point aligned scalar. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(float64, aligned_f64, 8); + +# endif//GLM_FORCE_SINGLE_ONLY + + + /// Single-qualifier floating-point aligned vector of 1 component. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(vec1, aligned_vec1, 4); + + /// Single-qualifier floating-point aligned vector of 2 components. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(vec2, aligned_vec2, 8); + + /// Single-qualifier floating-point aligned vector of 3 components. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(vec3, aligned_vec3, 16); + + /// Single-qualifier floating-point aligned vector of 4 components. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(vec4, aligned_vec4, 16); + + + /// Single-qualifier floating-point aligned vector of 1 component. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(fvec1, aligned_fvec1, 4); + + /// Single-qualifier floating-point aligned vector of 2 components. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(fvec2, aligned_fvec2, 8); + + /// Single-qualifier floating-point aligned vector of 3 components. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(fvec3, aligned_fvec3, 16); + + /// Single-qualifier floating-point aligned vector of 4 components. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(fvec4, aligned_fvec4, 16); + + + /// Single-qualifier floating-point aligned vector of 1 component. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(f32vec1, aligned_f32vec1, 4); + + /// Single-qualifier floating-point aligned vector of 2 components. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(f32vec2, aligned_f32vec2, 8); + + /// Single-qualifier floating-point aligned vector of 3 components. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(f32vec3, aligned_f32vec3, 16); + + /// Single-qualifier floating-point aligned vector of 4 components. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(f32vec4, aligned_f32vec4, 16); + + + /// Double-qualifier floating-point aligned vector of 1 component. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(dvec1, aligned_dvec1, 8); + + /// Double-qualifier floating-point aligned vector of 2 components. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(dvec2, aligned_dvec2, 16); + + /// Double-qualifier floating-point aligned vector of 3 components. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(dvec3, aligned_dvec3, 32); + + /// Double-qualifier floating-point aligned vector of 4 components. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(dvec4, aligned_dvec4, 32); + + +# ifndef GLM_FORCE_SINGLE_ONLY + + /// Double-qualifier floating-point aligned vector of 1 component. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(f64vec1, aligned_f64vec1, 8); + + /// Double-qualifier floating-point aligned vector of 2 components. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(f64vec2, aligned_f64vec2, 16); + + /// Double-qualifier floating-point aligned vector of 3 components. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(f64vec3, aligned_f64vec3, 32); + + /// Double-qualifier floating-point aligned vector of 4 components. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(f64vec4, aligned_f64vec4, 32); + +# endif//GLM_FORCE_SINGLE_ONLY + + ////////////////////// + // Float matrix types + + /// Single-qualifier floating-point aligned 1x1 matrix. + /// @see gtx_type_aligned + //typedef detail::tmat1 mat1; + + /// Single-qualifier floating-point aligned 2x2 matrix. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(mat2, aligned_mat2, 16); + + /// Single-qualifier floating-point aligned 3x3 matrix. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(mat3, aligned_mat3, 16); + + /// Single-qualifier floating-point aligned 4x4 matrix. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(mat4, aligned_mat4, 16); + + + /// Single-qualifier floating-point aligned 1x1 matrix. + /// @see gtx_type_aligned + //typedef detail::tmat1x1 mat1; + + /// Single-qualifier floating-point aligned 2x2 matrix. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(mat2x2, aligned_mat2x2, 16); + + /// Single-qualifier floating-point aligned 3x3 matrix. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(mat3x3, aligned_mat3x3, 16); + + /// Single-qualifier floating-point aligned 4x4 matrix. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(mat4x4, aligned_mat4x4, 16); + + + /// Single-qualifier floating-point aligned 1x1 matrix. + /// @see gtx_type_aligned + //typedef detail::tmat1x1 fmat1; + + /// Single-qualifier floating-point aligned 2x2 matrix. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(fmat2x2, aligned_fmat2, 16); + + /// Single-qualifier floating-point aligned 3x3 matrix. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(fmat3x3, aligned_fmat3, 16); + + /// Single-qualifier floating-point aligned 4x4 matrix. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(fmat4x4, aligned_fmat4, 16); + + + /// Single-qualifier floating-point aligned 1x1 matrix. + /// @see gtx_type_aligned + //typedef f32 fmat1x1; + + /// Single-qualifier floating-point aligned 2x2 matrix. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(fmat2x2, aligned_fmat2x2, 16); + + /// Single-qualifier floating-point aligned 2x3 matrix. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(fmat2x3, aligned_fmat2x3, 16); + + /// Single-qualifier floating-point aligned 2x4 matrix. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(fmat2x4, aligned_fmat2x4, 16); + + /// Single-qualifier floating-point aligned 3x2 matrix. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(fmat3x2, aligned_fmat3x2, 16); + + /// Single-qualifier floating-point aligned 3x3 matrix. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(fmat3x3, aligned_fmat3x3, 16); + + /// Single-qualifier floating-point aligned 3x4 matrix. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(fmat3x4, aligned_fmat3x4, 16); + + /// Single-qualifier floating-point aligned 4x2 matrix. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(fmat4x2, aligned_fmat4x2, 16); + + /// Single-qualifier floating-point aligned 4x3 matrix. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(fmat4x3, aligned_fmat4x3, 16); + + /// Single-qualifier floating-point aligned 4x4 matrix. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(fmat4x4, aligned_fmat4x4, 16); + + + /// Single-qualifier floating-point aligned 1x1 matrix. + /// @see gtx_type_aligned + //typedef detail::tmat1x1 f32mat1; + + /// Single-qualifier floating-point aligned 2x2 matrix. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(f32mat2x2, aligned_f32mat2, 16); + + /// Single-qualifier floating-point aligned 3x3 matrix. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(f32mat3x3, aligned_f32mat3, 16); + + /// Single-qualifier floating-point aligned 4x4 matrix. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(f32mat4x4, aligned_f32mat4, 16); + + + /// Single-qualifier floating-point aligned 1x1 matrix. + /// @see gtx_type_aligned + //typedef f32 f32mat1x1; + + /// Single-qualifier floating-point aligned 2x2 matrix. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(f32mat2x2, aligned_f32mat2x2, 16); + + /// Single-qualifier floating-point aligned 2x3 matrix. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(f32mat2x3, aligned_f32mat2x3, 16); + + /// Single-qualifier floating-point aligned 2x4 matrix. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(f32mat2x4, aligned_f32mat2x4, 16); + + /// Single-qualifier floating-point aligned 3x2 matrix. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(f32mat3x2, aligned_f32mat3x2, 16); + + /// Single-qualifier floating-point aligned 3x3 matrix. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(f32mat3x3, aligned_f32mat3x3, 16); + + /// Single-qualifier floating-point aligned 3x4 matrix. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(f32mat3x4, aligned_f32mat3x4, 16); + + /// Single-qualifier floating-point aligned 4x2 matrix. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(f32mat4x2, aligned_f32mat4x2, 16); + + /// Single-qualifier floating-point aligned 4x3 matrix. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(f32mat4x3, aligned_f32mat4x3, 16); + + /// Single-qualifier floating-point aligned 4x4 matrix. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(f32mat4x4, aligned_f32mat4x4, 16); + + +# ifndef GLM_FORCE_SINGLE_ONLY + + /// Double-qualifier floating-point aligned 1x1 matrix. + /// @see gtx_type_aligned + //typedef detail::tmat1x1 f64mat1; + + /// Double-qualifier floating-point aligned 2x2 matrix. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(f64mat2x2, aligned_f64mat2, 32); + + /// Double-qualifier floating-point aligned 3x3 matrix. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(f64mat3x3, aligned_f64mat3, 32); + + /// Double-qualifier floating-point aligned 4x4 matrix. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(f64mat4x4, aligned_f64mat4, 32); + + + /// Double-qualifier floating-point aligned 1x1 matrix. + /// @see gtx_type_aligned + //typedef f64 f64mat1x1; + + /// Double-qualifier floating-point aligned 2x2 matrix. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(f64mat2x2, aligned_f64mat2x2, 32); + + /// Double-qualifier floating-point aligned 2x3 matrix. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(f64mat2x3, aligned_f64mat2x3, 32); + + /// Double-qualifier floating-point aligned 2x4 matrix. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(f64mat2x4, aligned_f64mat2x4, 32); + + /// Double-qualifier floating-point aligned 3x2 matrix. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(f64mat3x2, aligned_f64mat3x2, 32); + + /// Double-qualifier floating-point aligned 3x3 matrix. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(f64mat3x3, aligned_f64mat3x3, 32); + + /// Double-qualifier floating-point aligned 3x4 matrix. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(f64mat3x4, aligned_f64mat3x4, 32); + + /// Double-qualifier floating-point aligned 4x2 matrix. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(f64mat4x2, aligned_f64mat4x2, 32); + + /// Double-qualifier floating-point aligned 4x3 matrix. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(f64mat4x3, aligned_f64mat4x3, 32); + + /// Double-qualifier floating-point aligned 4x4 matrix. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(f64mat4x4, aligned_f64mat4x4, 32); + +# endif//GLM_FORCE_SINGLE_ONLY + + + ////////////////////////// + // Quaternion types + + /// Single-qualifier floating-point aligned quaternion. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(quat, aligned_quat, 16); + + /// Single-qualifier floating-point aligned quaternion. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(quat, aligned_fquat, 16); + + /// Double-qualifier floating-point aligned quaternion. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(dquat, aligned_dquat, 32); + + /// Single-qualifier floating-point aligned quaternion. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(f32quat, aligned_f32quat, 16); + +# ifndef GLM_FORCE_SINGLE_ONLY + + /// Double-qualifier floating-point aligned quaternion. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(f64quat, aligned_f64quat, 32); + +# endif//GLM_FORCE_SINGLE_ONLY + + /// @} +}//namespace glm + +#include "type_aligned.inl" diff --git a/src/GLMath/glm/gtx/type_aligned.inl b/src/GLMath/glm/gtx/type_aligned.inl new file mode 100644 index 000000000..54c1b818b --- /dev/null +++ b/src/GLMath/glm/gtx/type_aligned.inl @@ -0,0 +1,6 @@ +/// @ref gtc_type_aligned + +namespace glm +{ + +} diff --git a/src/GLMath/glm/gtx/type_trait.hpp b/src/GLMath/glm/gtx/type_trait.hpp new file mode 100644 index 000000000..56685c8cb --- /dev/null +++ b/src/GLMath/glm/gtx/type_trait.hpp @@ -0,0 +1,85 @@ +/// @ref gtx_type_trait +/// @file glm/gtx/type_trait.hpp +/// +/// @see core (dependence) +/// +/// @defgroup gtx_type_trait GLM_GTX_type_trait +/// @ingroup gtx +/// +/// Include to use the features of this extension. +/// +/// Defines traits for each type. + +#pragma once + +#if GLM_MESSAGES == GLM_ENABLE && !defined(GLM_EXT_INCLUDED) +# ifndef GLM_ENABLE_EXPERIMENTAL +# pragma message("GLM: GLM_GTX_type_trait is an experimental extension and may change in the future. Use #define GLM_ENABLE_EXPERIMENTAL before including it, if you really want to use it.") +# else +# pragma message("GLM: GLM_GTX_type_trait extension included") +# endif +#endif + +// Dependency: +#include "../detail/qualifier.hpp" +#include "../gtc/quaternion.hpp" +#include "../gtx/dual_quaternion.hpp" + +namespace glm +{ + /// @addtogroup gtx_type_trait + /// @{ + + template + struct type + { + static bool const is_vec = false; + static bool const is_mat = false; + static bool const is_quat = false; + static length_t const components = 0; + static length_t const cols = 0; + static length_t const rows = 0; + }; + + template + struct type > + { + static bool const is_vec = true; + static bool const is_mat = false; + static bool const is_quat = false; + static length_t const components = L; + }; + + template + struct type > + { + static bool const is_vec = false; + static bool const is_mat = true; + static bool const is_quat = false; + static length_t const components = C; + static length_t const cols = C; + static length_t const rows = R; + }; + + template + struct type > + { + static bool const is_vec = false; + static bool const is_mat = false; + static bool const is_quat = true; + static length_t const components = 4; + }; + + template + struct type > + { + static bool const is_vec = false; + static bool const is_mat = false; + static bool const is_quat = true; + static length_t const components = 8; + }; + + /// @} +}//namespace glm + +#include "type_trait.inl" diff --git a/src/GLMath/glm/gtx/type_trait.inl b/src/GLMath/glm/gtx/type_trait.inl new file mode 100644 index 000000000..045de959c --- /dev/null +++ b/src/GLMath/glm/gtx/type_trait.inl @@ -0,0 +1,61 @@ +/// @ref gtx_type_trait + +namespace glm +{ + template + bool const type::is_vec; + template + bool const type::is_mat; + template + bool const type::is_quat; + template + length_t const type::components; + template + length_t const type::cols; + template + length_t const type::rows; + + // vec + template + bool const type >::is_vec; + template + bool const type >::is_mat; + template + bool const type >::is_quat; + template + length_t const type >::components; + + // mat + template + bool const type >::is_vec; + template + bool const type >::is_mat; + template + bool const type >::is_quat; + template + length_t const type >::components; + template + length_t const type >::cols; + template + length_t const type >::rows; + + // tquat + template + bool const type >::is_vec; + template + bool const type >::is_mat; + template + bool const type >::is_quat; + template + length_t const type >::components; + + // tdualquat + template + bool const type >::is_vec; + template + bool const type >::is_mat; + template + bool const type >::is_quat; + template + length_t const type >::components; +}//namespace glm diff --git a/src/GLMath/glm/gtx/vec_swizzle.hpp b/src/GLMath/glm/gtx/vec_swizzle.hpp new file mode 100644 index 000000000..1c49abcb6 --- /dev/null +++ b/src/GLMath/glm/gtx/vec_swizzle.hpp @@ -0,0 +1,2782 @@ +/// @ref gtx_vec_swizzle +/// @file glm/gtx/vec_swizzle.hpp +/// +/// @see core (dependence) +/// +/// @defgroup gtx_vec_swizzle GLM_GTX_vec_swizzle +/// @ingroup gtx +/// +/// Include to use the features of this extension. +/// +/// Functions to perform swizzle operation. + +#pragma once + +#include "../glm.hpp" + +#if GLM_MESSAGES == GLM_ENABLE && !defined(GLM_EXT_INCLUDED) +# ifndef GLM_ENABLE_EXPERIMENTAL +# pragma message("GLM: GLM_GTX_vec_swizzle is an experimental extension and may change in the future. Use #define GLM_ENABLE_EXPERIMENTAL before including it, if you really want to use it.") +# else +# pragma message("GLM: GLM_GTX_vec_swizzle extension included") +# endif +#endif + +namespace glm { + // xx + template + GLM_INLINE glm::vec<2, T, Q> xx(const glm::vec<1, T, Q> &v) { + return glm::vec<2, T, Q>(v.x, v.x); + } + + template + GLM_INLINE glm::vec<2, T, Q> xx(const glm::vec<2, T, Q> &v) { + return glm::vec<2, T, Q>(v.x, v.x); + } + + template + GLM_INLINE glm::vec<2, T, Q> xx(const glm::vec<3, T, Q> &v) { + return glm::vec<2, T, Q>(v.x, v.x); + } + + template + GLM_INLINE glm::vec<2, T, Q> xx(const glm::vec<4, T, Q> &v) { + return glm::vec<2, T, Q>(v.x, v.x); + } + + // xy + template + GLM_INLINE glm::vec<2, T, Q> xy(const glm::vec<2, T, Q> &v) { + return glm::vec<2, T, Q>(v.x, v.y); + } + + template + GLM_INLINE glm::vec<2, T, Q> xy(const glm::vec<3, T, Q> &v) { + return glm::vec<2, T, Q>(v.x, v.y); + } + + template + GLM_INLINE glm::vec<2, T, Q> xy(const glm::vec<4, T, Q> &v) { + return glm::vec<2, T, Q>(v.x, v.y); + } + + // xz + template + GLM_INLINE glm::vec<2, T, Q> xz(const glm::vec<3, T, Q> &v) { + return glm::vec<2, T, Q>(v.x, v.z); + } + + template + GLM_INLINE glm::vec<2, T, Q> xz(const glm::vec<4, T, Q> &v) { + return glm::vec<2, T, Q>(v.x, v.z); + } + + // xw + template + GLM_INLINE glm::vec<2, T, Q> xw(const glm::vec<4, T, Q> &v) { + return glm::vec<2, T, Q>(v.x, v.w); + } + + // yx + template + GLM_INLINE glm::vec<2, T, Q> yx(const glm::vec<2, T, Q> &v) { + return glm::vec<2, T, Q>(v.y, v.x); + } + + template + GLM_INLINE glm::vec<2, T, Q> yx(const glm::vec<3, T, Q> &v) { + return glm::vec<2, T, Q>(v.y, v.x); + } + + template + GLM_INLINE glm::vec<2, T, Q> yx(const glm::vec<4, T, Q> &v) { + return glm::vec<2, T, Q>(v.y, v.x); + } + + // yy + template + GLM_INLINE glm::vec<2, T, Q> yy(const glm::vec<2, T, Q> &v) { + return glm::vec<2, T, Q>(v.y, v.y); + } + + template + GLM_INLINE glm::vec<2, T, Q> yy(const glm::vec<3, T, Q> &v) { + return glm::vec<2, T, Q>(v.y, v.y); + } + + template + GLM_INLINE glm::vec<2, T, Q> yy(const glm::vec<4, T, Q> &v) { + return glm::vec<2, T, Q>(v.y, v.y); + } + + // yz + template + GLM_INLINE glm::vec<2, T, Q> yz(const glm::vec<3, T, Q> &v) { + return glm::vec<2, T, Q>(v.y, v.z); + } + + template + GLM_INLINE glm::vec<2, T, Q> yz(const glm::vec<4, T, Q> &v) { + return glm::vec<2, T, Q>(v.y, v.z); + } + + // yw + template + GLM_INLINE glm::vec<2, T, Q> yw(const glm::vec<4, T, Q> &v) { + return glm::vec<2, T, Q>(v.y, v.w); + } + + // zx + template + GLM_INLINE glm::vec<2, T, Q> zx(const glm::vec<3, T, Q> &v) { + return glm::vec<2, T, Q>(v.z, v.x); + } + + template + GLM_INLINE glm::vec<2, T, Q> zx(const glm::vec<4, T, Q> &v) { + return glm::vec<2, T, Q>(v.z, v.x); + } + + // zy + template + GLM_INLINE glm::vec<2, T, Q> zy(const glm::vec<3, T, Q> &v) { + return glm::vec<2, T, Q>(v.z, v.y); + } + + template + GLM_INLINE glm::vec<2, T, Q> zy(const glm::vec<4, T, Q> &v) { + return glm::vec<2, T, Q>(v.z, v.y); + } + + // zz + template + GLM_INLINE glm::vec<2, T, Q> zz(const glm::vec<3, T, Q> &v) { + return glm::vec<2, T, Q>(v.z, v.z); + } + + template + GLM_INLINE glm::vec<2, T, Q> zz(const glm::vec<4, T, Q> &v) { + return glm::vec<2, T, Q>(v.z, v.z); + } + + // zw + template + GLM_INLINE glm::vec<2, T, Q> zw(const glm::vec<4, T, Q> &v) { + return glm::vec<2, T, Q>(v.z, v.w); + } + + // wx + template + GLM_INLINE glm::vec<2, T, Q> wx(const glm::vec<4, T, Q> &v) { + return glm::vec<2, T, Q>(v.w, v.x); + } + + // wy + template + GLM_INLINE glm::vec<2, T, Q> wy(const glm::vec<4, T, Q> &v) { + return glm::vec<2, T, Q>(v.w, v.y); + } + + // wz + template + GLM_INLINE glm::vec<2, T, Q> wz(const glm::vec<4, T, Q> &v) { + return glm::vec<2, T, Q>(v.w, v.z); + } + + // ww + template + GLM_INLINE glm::vec<2, T, Q> ww(const glm::vec<4, T, Q> &v) { + return glm::vec<2, T, Q>(v.w, v.w); + } + + // xxx + template + GLM_INLINE glm::vec<3, T, Q> xxx(const glm::vec<1, T, Q> &v) { + return glm::vec<3, T, Q>(v.x, v.x, v.x); + } + + template + GLM_INLINE glm::vec<3, T, Q> xxx(const glm::vec<2, T, Q> &v) { + return glm::vec<3, T, Q>(v.x, v.x, v.x); + } + + template + GLM_INLINE glm::vec<3, T, Q> xxx(const glm::vec<3, T, Q> &v) { + return glm::vec<3, T, Q>(v.x, v.x, v.x); + } + + template + GLM_INLINE glm::vec<3, T, Q> xxx(const glm::vec<4, T, Q> &v) { + return glm::vec<3, T, Q>(v.x, v.x, v.x); + } + + // xxy + template + GLM_INLINE glm::vec<3, T, Q> xxy(const glm::vec<2, T, Q> &v) { + return glm::vec<3, T, Q>(v.x, v.x, v.y); + } + + template + GLM_INLINE glm::vec<3, T, Q> xxy(const glm::vec<3, T, Q> &v) { + return glm::vec<3, T, Q>(v.x, v.x, v.y); + } + + template + GLM_INLINE glm::vec<3, T, Q> xxy(const glm::vec<4, T, Q> &v) { + return glm::vec<3, T, Q>(v.x, v.x, v.y); + } + + // xxz + template + GLM_INLINE glm::vec<3, T, Q> xxz(const glm::vec<3, T, Q> &v) { + return glm::vec<3, T, Q>(v.x, v.x, v.z); + } + + template + GLM_INLINE glm::vec<3, T, Q> xxz(const glm::vec<4, T, Q> &v) { + return glm::vec<3, T, Q>(v.x, v.x, v.z); + } + + // xxw + template + GLM_INLINE glm::vec<3, T, Q> xxw(const glm::vec<4, T, Q> &v) { + return glm::vec<3, T, Q>(v.x, v.x, v.w); + } + + // xyx + template + GLM_INLINE glm::vec<3, T, Q> xyx(const glm::vec<2, T, Q> &v) { + return glm::vec<3, T, Q>(v.x, v.y, v.x); + } + + template + GLM_INLINE glm::vec<3, T, Q> xyx(const glm::vec<3, T, Q> &v) { + return glm::vec<3, T, Q>(v.x, v.y, v.x); + } + + template + GLM_INLINE glm::vec<3, T, Q> xyx(const glm::vec<4, T, Q> &v) { + return glm::vec<3, T, Q>(v.x, v.y, v.x); + } + + // xyy + template + GLM_INLINE glm::vec<3, T, Q> xyy(const glm::vec<2, T, Q> &v) { + return glm::vec<3, T, Q>(v.x, v.y, v.y); + } + + template + GLM_INLINE glm::vec<3, T, Q> xyy(const glm::vec<3, T, Q> &v) { + return glm::vec<3, T, Q>(v.x, v.y, v.y); + } + + template + GLM_INLINE glm::vec<3, T, Q> xyy(const glm::vec<4, T, Q> &v) { + return glm::vec<3, T, Q>(v.x, v.y, v.y); + } + + // xyz + template + GLM_INLINE glm::vec<3, T, Q> xyz(const glm::vec<3, T, Q> &v) { + return glm::vec<3, T, Q>(v.x, v.y, v.z); + } + + template + GLM_INLINE glm::vec<3, T, Q> xyz(const glm::vec<4, T, Q> &v) { + return glm::vec<3, T, Q>(v.x, v.y, v.z); + } + + // xyw + template + GLM_INLINE glm::vec<3, T, Q> xyw(const glm::vec<4, T, Q> &v) { + return glm::vec<3, T, Q>(v.x, v.y, v.w); + } + + // xzx + template + GLM_INLINE glm::vec<3, T, Q> xzx(const glm::vec<3, T, Q> &v) { + return glm::vec<3, T, Q>(v.x, v.z, v.x); + } + + template + GLM_INLINE glm::vec<3, T, Q> xzx(const glm::vec<4, T, Q> &v) { + return glm::vec<3, T, Q>(v.x, v.z, v.x); + } + + // xzy + template + GLM_INLINE glm::vec<3, T, Q> xzy(const glm::vec<3, T, Q> &v) { + return glm::vec<3, T, Q>(v.x, v.z, v.y); + } + + template + GLM_INLINE glm::vec<3, T, Q> xzy(const glm::vec<4, T, Q> &v) { + return glm::vec<3, T, Q>(v.x, v.z, v.y); + } + + // xzz + template + GLM_INLINE glm::vec<3, T, Q> xzz(const glm::vec<3, T, Q> &v) { + return glm::vec<3, T, Q>(v.x, v.z, v.z); + } + + template + GLM_INLINE glm::vec<3, T, Q> xzz(const glm::vec<4, T, Q> &v) { + return glm::vec<3, T, Q>(v.x, v.z, v.z); + } + + // xzw + template + GLM_INLINE glm::vec<3, T, Q> xzw(const glm::vec<4, T, Q> &v) { + return glm::vec<3, T, Q>(v.x, v.z, v.w); + } + + // xwx + template + GLM_INLINE glm::vec<3, T, Q> xwx(const glm::vec<4, T, Q> &v) { + return glm::vec<3, T, Q>(v.x, v.w, v.x); + } + + // xwy + template + GLM_INLINE glm::vec<3, T, Q> xwy(const glm::vec<4, T, Q> &v) { + return glm::vec<3, T, Q>(v.x, v.w, v.y); + } + + // xwz + template + GLM_INLINE glm::vec<3, T, Q> xwz(const glm::vec<4, T, Q> &v) { + return glm::vec<3, T, Q>(v.x, v.w, v.z); + } + + // xww + template + GLM_INLINE glm::vec<3, T, Q> xww(const glm::vec<4, T, Q> &v) { + return glm::vec<3, T, Q>(v.x, v.w, v.w); + } + + // yxx + template + GLM_INLINE glm::vec<3, T, Q> yxx(const glm::vec<2, T, Q> &v) { + return glm::vec<3, T, Q>(v.y, v.x, v.x); + } + + template + GLM_INLINE glm::vec<3, T, Q> yxx(const glm::vec<3, T, Q> &v) { + return glm::vec<3, T, Q>(v.y, v.x, v.x); + } + + template + GLM_INLINE glm::vec<3, T, Q> yxx(const glm::vec<4, T, Q> &v) { + return glm::vec<3, T, Q>(v.y, v.x, v.x); + } + + // yxy + template + GLM_INLINE glm::vec<3, T, Q> yxy(const glm::vec<2, T, Q> &v) { + return glm::vec<3, T, Q>(v.y, v.x, v.y); + } + + template + GLM_INLINE glm::vec<3, T, Q> yxy(const glm::vec<3, T, Q> &v) { + return glm::vec<3, T, Q>(v.y, v.x, v.y); + } + + template + GLM_INLINE glm::vec<3, T, Q> yxy(const glm::vec<4, T, Q> &v) { + return glm::vec<3, T, Q>(v.y, v.x, v.y); + } + + // yxz + template + GLM_INLINE glm::vec<3, T, Q> yxz(const glm::vec<3, T, Q> &v) { + return glm::vec<3, T, Q>(v.y, v.x, v.z); + } + + template + GLM_INLINE glm::vec<3, T, Q> yxz(const glm::vec<4, T, Q> &v) { + return glm::vec<3, T, Q>(v.y, v.x, v.z); + } + + // yxw + template + GLM_INLINE glm::vec<3, T, Q> yxw(const glm::vec<4, T, Q> &v) { + return glm::vec<3, T, Q>(v.y, v.x, v.w); + } + + // yyx + template + GLM_INLINE glm::vec<3, T, Q> yyx(const glm::vec<2, T, Q> &v) { + return glm::vec<3, T, Q>(v.y, v.y, v.x); + } + + template + GLM_INLINE glm::vec<3, T, Q> yyx(const glm::vec<3, T, Q> &v) { + return glm::vec<3, T, Q>(v.y, v.y, v.x); + } + + template + GLM_INLINE glm::vec<3, T, Q> yyx(const glm::vec<4, T, Q> &v) { + return glm::vec<3, T, Q>(v.y, v.y, v.x); + } + + // yyy + template + GLM_INLINE glm::vec<3, T, Q> yyy(const glm::vec<2, T, Q> &v) { + return glm::vec<3, T, Q>(v.y, v.y, v.y); + } + + template + GLM_INLINE glm::vec<3, T, Q> yyy(const glm::vec<3, T, Q> &v) { + return glm::vec<3, T, Q>(v.y, v.y, v.y); + } + + template + GLM_INLINE glm::vec<3, T, Q> yyy(const glm::vec<4, T, Q> &v) { + return glm::vec<3, T, Q>(v.y, v.y, v.y); + } + + // yyz + template + GLM_INLINE glm::vec<3, T, Q> yyz(const glm::vec<3, T, Q> &v) { + return glm::vec<3, T, Q>(v.y, v.y, v.z); + } + + template + GLM_INLINE glm::vec<3, T, Q> yyz(const glm::vec<4, T, Q> &v) { + return glm::vec<3, T, Q>(v.y, v.y, v.z); + } + + // yyw + template + GLM_INLINE glm::vec<3, T, Q> yyw(const glm::vec<4, T, Q> &v) { + return glm::vec<3, T, Q>(v.y, v.y, v.w); + } + + // yzx + template + GLM_INLINE glm::vec<3, T, Q> yzx(const glm::vec<3, T, Q> &v) { + return glm::vec<3, T, Q>(v.y, v.z, v.x); + } + + template + GLM_INLINE glm::vec<3, T, Q> yzx(const glm::vec<4, T, Q> &v) { + return glm::vec<3, T, Q>(v.y, v.z, v.x); + } + + // yzy + template + GLM_INLINE glm::vec<3, T, Q> yzy(const glm::vec<3, T, Q> &v) { + return glm::vec<3, T, Q>(v.y, v.z, v.y); + } + + template + GLM_INLINE glm::vec<3, T, Q> yzy(const glm::vec<4, T, Q> &v) { + return glm::vec<3, T, Q>(v.y, v.z, v.y); + } + + // yzz + template + GLM_INLINE glm::vec<3, T, Q> yzz(const glm::vec<3, T, Q> &v) { + return glm::vec<3, T, Q>(v.y, v.z, v.z); + } + + template + GLM_INLINE glm::vec<3, T, Q> yzz(const glm::vec<4, T, Q> &v) { + return glm::vec<3, T, Q>(v.y, v.z, v.z); + } + + // yzw + template + GLM_INLINE glm::vec<3, T, Q> yzw(const glm::vec<4, T, Q> &v) { + return glm::vec<3, T, Q>(v.y, v.z, v.w); + } + + // ywx + template + GLM_INLINE glm::vec<3, T, Q> ywx(const glm::vec<4, T, Q> &v) { + return glm::vec<3, T, Q>(v.y, v.w, v.x); + } + + // ywy + template + GLM_INLINE glm::vec<3, T, Q> ywy(const glm::vec<4, T, Q> &v) { + return glm::vec<3, T, Q>(v.y, v.w, v.y); + } + + // ywz + template + GLM_INLINE glm::vec<3, T, Q> ywz(const glm::vec<4, T, Q> &v) { + return glm::vec<3, T, Q>(v.y, v.w, v.z); + } + + // yww + template + GLM_INLINE glm::vec<3, T, Q> yww(const glm::vec<4, T, Q> &v) { + return glm::vec<3, T, Q>(v.y, v.w, v.w); + } + + // zxx + template + GLM_INLINE glm::vec<3, T, Q> zxx(const glm::vec<3, T, Q> &v) { + return glm::vec<3, T, Q>(v.z, v.x, v.x); + } + + template + GLM_INLINE glm::vec<3, T, Q> zxx(const glm::vec<4, T, Q> &v) { + return glm::vec<3, T, Q>(v.z, v.x, v.x); + } + + // zxy + template + GLM_INLINE glm::vec<3, T, Q> zxy(const glm::vec<3, T, Q> &v) { + return glm::vec<3, T, Q>(v.z, v.x, v.y); + } + + template + GLM_INLINE glm::vec<3, T, Q> zxy(const glm::vec<4, T, Q> &v) { + return glm::vec<3, T, Q>(v.z, v.x, v.y); + } + + // zxz + template + GLM_INLINE glm::vec<3, T, Q> zxz(const glm::vec<3, T, Q> &v) { + return glm::vec<3, T, Q>(v.z, v.x, v.z); + } + + template + GLM_INLINE glm::vec<3, T, Q> zxz(const glm::vec<4, T, Q> &v) { + return glm::vec<3, T, Q>(v.z, v.x, v.z); + } + + // zxw + template + GLM_INLINE glm::vec<3, T, Q> zxw(const glm::vec<4, T, Q> &v) { + return glm::vec<3, T, Q>(v.z, v.x, v.w); + } + + // zyx + template + GLM_INLINE glm::vec<3, T, Q> zyx(const glm::vec<3, T, Q> &v) { + return glm::vec<3, T, Q>(v.z, v.y, v.x); + } + + template + GLM_INLINE glm::vec<3, T, Q> zyx(const glm::vec<4, T, Q> &v) { + return glm::vec<3, T, Q>(v.z, v.y, v.x); + } + + // zyy + template + GLM_INLINE glm::vec<3, T, Q> zyy(const glm::vec<3, T, Q> &v) { + return glm::vec<3, T, Q>(v.z, v.y, v.y); + } + + template + GLM_INLINE glm::vec<3, T, Q> zyy(const glm::vec<4, T, Q> &v) { + return glm::vec<3, T, Q>(v.z, v.y, v.y); + } + + // zyz + template + GLM_INLINE glm::vec<3, T, Q> zyz(const glm::vec<3, T, Q> &v) { + return glm::vec<3, T, Q>(v.z, v.y, v.z); + } + + template + GLM_INLINE glm::vec<3, T, Q> zyz(const glm::vec<4, T, Q> &v) { + return glm::vec<3, T, Q>(v.z, v.y, v.z); + } + + // zyw + template + GLM_INLINE glm::vec<3, T, Q> zyw(const glm::vec<4, T, Q> &v) { + return glm::vec<3, T, Q>(v.z, v.y, v.w); + } + + // zzx + template + GLM_INLINE glm::vec<3, T, Q> zzx(const glm::vec<3, T, Q> &v) { + return glm::vec<3, T, Q>(v.z, v.z, v.x); + } + + template + GLM_INLINE glm::vec<3, T, Q> zzx(const glm::vec<4, T, Q> &v) { + return glm::vec<3, T, Q>(v.z, v.z, v.x); + } + + // zzy + template + GLM_INLINE glm::vec<3, T, Q> zzy(const glm::vec<3, T, Q> &v) { + return glm::vec<3, T, Q>(v.z, v.z, v.y); + } + + template + GLM_INLINE glm::vec<3, T, Q> zzy(const glm::vec<4, T, Q> &v) { + return glm::vec<3, T, Q>(v.z, v.z, v.y); + } + + // zzz + template + GLM_INLINE glm::vec<3, T, Q> zzz(const glm::vec<3, T, Q> &v) { + return glm::vec<3, T, Q>(v.z, v.z, v.z); + } + + template + GLM_INLINE glm::vec<3, T, Q> zzz(const glm::vec<4, T, Q> &v) { + return glm::vec<3, T, Q>(v.z, v.z, v.z); + } + + // zzw + template + GLM_INLINE glm::vec<3, T, Q> zzw(const glm::vec<4, T, Q> &v) { + return glm::vec<3, T, Q>(v.z, v.z, v.w); + } + + // zwx + template + GLM_INLINE glm::vec<3, T, Q> zwx(const glm::vec<4, T, Q> &v) { + return glm::vec<3, T, Q>(v.z, v.w, v.x); + } + + // zwy + template + GLM_INLINE glm::vec<3, T, Q> zwy(const glm::vec<4, T, Q> &v) { + return glm::vec<3, T, Q>(v.z, v.w, v.y); + } + + // zwz + template + GLM_INLINE glm::vec<3, T, Q> zwz(const glm::vec<4, T, Q> &v) { + return glm::vec<3, T, Q>(v.z, v.w, v.z); + } + + // zww + template + GLM_INLINE glm::vec<3, T, Q> zww(const glm::vec<4, T, Q> &v) { + return glm::vec<3, T, Q>(v.z, v.w, v.w); + } + + // wxx + template + GLM_INLINE glm::vec<3, T, Q> wxx(const glm::vec<4, T, Q> &v) { + return glm::vec<3, T, Q>(v.w, v.x, v.x); + } + + // wxy + template + GLM_INLINE glm::vec<3, T, Q> wxy(const glm::vec<4, T, Q> &v) { + return glm::vec<3, T, Q>(v.w, v.x, v.y); + } + + // wxz + template + GLM_INLINE glm::vec<3, T, Q> wxz(const glm::vec<4, T, Q> &v) { + return glm::vec<3, T, Q>(v.w, v.x, v.z); + } + + // wxw + template + GLM_INLINE glm::vec<3, T, Q> wxw(const glm::vec<4, T, Q> &v) { + return glm::vec<3, T, Q>(v.w, v.x, v.w); + } + + // wyx + template + GLM_INLINE glm::vec<3, T, Q> wyx(const glm::vec<4, T, Q> &v) { + return glm::vec<3, T, Q>(v.w, v.y, v.x); + } + + // wyy + template + GLM_INLINE glm::vec<3, T, Q> wyy(const glm::vec<4, T, Q> &v) { + return glm::vec<3, T, Q>(v.w, v.y, v.y); + } + + // wyz + template + GLM_INLINE glm::vec<3, T, Q> wyz(const glm::vec<4, T, Q> &v) { + return glm::vec<3, T, Q>(v.w, v.y, v.z); + } + + // wyw + template + GLM_INLINE glm::vec<3, T, Q> wyw(const glm::vec<4, T, Q> &v) { + return glm::vec<3, T, Q>(v.w, v.y, v.w); + } + + // wzx + template + GLM_INLINE glm::vec<3, T, Q> wzx(const glm::vec<4, T, Q> &v) { + return glm::vec<3, T, Q>(v.w, v.z, v.x); + } + + // wzy + template + GLM_INLINE glm::vec<3, T, Q> wzy(const glm::vec<4, T, Q> &v) { + return glm::vec<3, T, Q>(v.w, v.z, v.y); + } + + // wzz + template + GLM_INLINE glm::vec<3, T, Q> wzz(const glm::vec<4, T, Q> &v) { + return glm::vec<3, T, Q>(v.w, v.z, v.z); + } + + // wzw + template + GLM_INLINE glm::vec<3, T, Q> wzw(const glm::vec<4, T, Q> &v) { + return glm::vec<3, T, Q>(v.w, v.z, v.w); + } + + // wwx + template + GLM_INLINE glm::vec<3, T, Q> wwx(const glm::vec<4, T, Q> &v) { + return glm::vec<3, T, Q>(v.w, v.w, v.x); + } + + // wwy + template + GLM_INLINE glm::vec<3, T, Q> wwy(const glm::vec<4, T, Q> &v) { + return glm::vec<3, T, Q>(v.w, v.w, v.y); + } + + // wwz + template + GLM_INLINE glm::vec<3, T, Q> wwz(const glm::vec<4, T, Q> &v) { + return glm::vec<3, T, Q>(v.w, v.w, v.z); + } + + // www + template + GLM_INLINE glm::vec<3, T, Q> www(const glm::vec<4, T, Q> &v) { + return glm::vec<3, T, Q>(v.w, v.w, v.w); + } + + // xxxx + template + GLM_INLINE glm::vec<4, T, Q> xxxx(const glm::vec<1, T, Q> &v) { + return glm::vec<4, T, Q>(v.x, v.x, v.x, v.x); + } + + template + GLM_INLINE glm::vec<4, T, Q> xxxx(const glm::vec<2, T, Q> &v) { + return glm::vec<4, T, Q>(v.x, v.x, v.x, v.x); + } + + template + GLM_INLINE glm::vec<4, T, Q> xxxx(const glm::vec<3, T, Q> &v) { + return glm::vec<4, T, Q>(v.x, v.x, v.x, v.x); + } + + template + GLM_INLINE glm::vec<4, T, Q> xxxx(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.x, v.x, v.x, v.x); + } + + // xxxy + template + GLM_INLINE glm::vec<4, T, Q> xxxy(const glm::vec<2, T, Q> &v) { + return glm::vec<4, T, Q>(v.x, v.x, v.x, v.y); + } + + template + GLM_INLINE glm::vec<4, T, Q> xxxy(const glm::vec<3, T, Q> &v) { + return glm::vec<4, T, Q>(v.x, v.x, v.x, v.y); + } + + template + GLM_INLINE glm::vec<4, T, Q> xxxy(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.x, v.x, v.x, v.y); + } + + // xxxz + template + GLM_INLINE glm::vec<4, T, Q> xxxz(const glm::vec<3, T, Q> &v) { + return glm::vec<4, T, Q>(v.x, v.x, v.x, v.z); + } + + template + GLM_INLINE glm::vec<4, T, Q> xxxz(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.x, v.x, v.x, v.z); + } + + // xxxw + template + GLM_INLINE glm::vec<4, T, Q> xxxw(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.x, v.x, v.x, v.w); + } + + // xxyx + template + GLM_INLINE glm::vec<4, T, Q> xxyx(const glm::vec<2, T, Q> &v) { + return glm::vec<4, T, Q>(v.x, v.x, v.y, v.x); + } + + template + GLM_INLINE glm::vec<4, T, Q> xxyx(const glm::vec<3, T, Q> &v) { + return glm::vec<4, T, Q>(v.x, v.x, v.y, v.x); + } + + template + GLM_INLINE glm::vec<4, T, Q> xxyx(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.x, v.x, v.y, v.x); + } + + // xxyy + template + GLM_INLINE glm::vec<4, T, Q> xxyy(const glm::vec<2, T, Q> &v) { + return glm::vec<4, T, Q>(v.x, v.x, v.y, v.y); + } + + template + GLM_INLINE glm::vec<4, T, Q> xxyy(const glm::vec<3, T, Q> &v) { + return glm::vec<4, T, Q>(v.x, v.x, v.y, v.y); + } + + template + GLM_INLINE glm::vec<4, T, Q> xxyy(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.x, v.x, v.y, v.y); + } + + // xxyz + template + GLM_INLINE glm::vec<4, T, Q> xxyz(const glm::vec<3, T, Q> &v) { + return glm::vec<4, T, Q>(v.x, v.x, v.y, v.z); + } + + template + GLM_INLINE glm::vec<4, T, Q> xxyz(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.x, v.x, v.y, v.z); + } + + // xxyw + template + GLM_INLINE glm::vec<4, T, Q> xxyw(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.x, v.x, v.y, v.w); + } + + // xxzx + template + GLM_INLINE glm::vec<4, T, Q> xxzx(const glm::vec<3, T, Q> &v) { + return glm::vec<4, T, Q>(v.x, v.x, v.z, v.x); + } + + template + GLM_INLINE glm::vec<4, T, Q> xxzx(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.x, v.x, v.z, v.x); + } + + // xxzy + template + GLM_INLINE glm::vec<4, T, Q> xxzy(const glm::vec<3, T, Q> &v) { + return glm::vec<4, T, Q>(v.x, v.x, v.z, v.y); + } + + template + GLM_INLINE glm::vec<4, T, Q> xxzy(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.x, v.x, v.z, v.y); + } + + // xxzz + template + GLM_INLINE glm::vec<4, T, Q> xxzz(const glm::vec<3, T, Q> &v) { + return glm::vec<4, T, Q>(v.x, v.x, v.z, v.z); + } + + template + GLM_INLINE glm::vec<4, T, Q> xxzz(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.x, v.x, v.z, v.z); + } + + // xxzw + template + GLM_INLINE glm::vec<4, T, Q> xxzw(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.x, v.x, v.z, v.w); + } + + // xxwx + template + GLM_INLINE glm::vec<4, T, Q> xxwx(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.x, v.x, v.w, v.x); + } + + // xxwy + template + GLM_INLINE glm::vec<4, T, Q> xxwy(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.x, v.x, v.w, v.y); + } + + // xxwz + template + GLM_INLINE glm::vec<4, T, Q> xxwz(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.x, v.x, v.w, v.z); + } + + // xxww + template + GLM_INLINE glm::vec<4, T, Q> xxww(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.x, v.x, v.w, v.w); + } + + // xyxx + template + GLM_INLINE glm::vec<4, T, Q> xyxx(const glm::vec<2, T, Q> &v) { + return glm::vec<4, T, Q>(v.x, v.y, v.x, v.x); + } + + template + GLM_INLINE glm::vec<4, T, Q> xyxx(const glm::vec<3, T, Q> &v) { + return glm::vec<4, T, Q>(v.x, v.y, v.x, v.x); + } + + template + GLM_INLINE glm::vec<4, T, Q> xyxx(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.x, v.y, v.x, v.x); + } + + // xyxy + template + GLM_INLINE glm::vec<4, T, Q> xyxy(const glm::vec<2, T, Q> &v) { + return glm::vec<4, T, Q>(v.x, v.y, v.x, v.y); + } + + template + GLM_INLINE glm::vec<4, T, Q> xyxy(const glm::vec<3, T, Q> &v) { + return glm::vec<4, T, Q>(v.x, v.y, v.x, v.y); + } + + template + GLM_INLINE glm::vec<4, T, Q> xyxy(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.x, v.y, v.x, v.y); + } + + // xyxz + template + GLM_INLINE glm::vec<4, T, Q> xyxz(const glm::vec<3, T, Q> &v) { + return glm::vec<4, T, Q>(v.x, v.y, v.x, v.z); + } + + template + GLM_INLINE glm::vec<4, T, Q> xyxz(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.x, v.y, v.x, v.z); + } + + // xyxw + template + GLM_INLINE glm::vec<4, T, Q> xyxw(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.x, v.y, v.x, v.w); + } + + // xyyx + template + GLM_INLINE glm::vec<4, T, Q> xyyx(const glm::vec<2, T, Q> &v) { + return glm::vec<4, T, Q>(v.x, v.y, v.y, v.x); + } + + template + GLM_INLINE glm::vec<4, T, Q> xyyx(const glm::vec<3, T, Q> &v) { + return glm::vec<4, T, Q>(v.x, v.y, v.y, v.x); + } + + template + GLM_INLINE glm::vec<4, T, Q> xyyx(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.x, v.y, v.y, v.x); + } + + // xyyy + template + GLM_INLINE glm::vec<4, T, Q> xyyy(const glm::vec<2, T, Q> &v) { + return glm::vec<4, T, Q>(v.x, v.y, v.y, v.y); + } + + template + GLM_INLINE glm::vec<4, T, Q> xyyy(const glm::vec<3, T, Q> &v) { + return glm::vec<4, T, Q>(v.x, v.y, v.y, v.y); + } + + template + GLM_INLINE glm::vec<4, T, Q> xyyy(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.x, v.y, v.y, v.y); + } + + // xyyz + template + GLM_INLINE glm::vec<4, T, Q> xyyz(const glm::vec<3, T, Q> &v) { + return glm::vec<4, T, Q>(v.x, v.y, v.y, v.z); + } + + template + GLM_INLINE glm::vec<4, T, Q> xyyz(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.x, v.y, v.y, v.z); + } + + // xyyw + template + GLM_INLINE glm::vec<4, T, Q> xyyw(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.x, v.y, v.y, v.w); + } + + // xyzx + template + GLM_INLINE glm::vec<4, T, Q> xyzx(const glm::vec<3, T, Q> &v) { + return glm::vec<4, T, Q>(v.x, v.y, v.z, v.x); + } + + template + GLM_INLINE glm::vec<4, T, Q> xyzx(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.x, v.y, v.z, v.x); + } + + // xyzy + template + GLM_INLINE glm::vec<4, T, Q> xyzy(const glm::vec<3, T, Q> &v) { + return glm::vec<4, T, Q>(v.x, v.y, v.z, v.y); + } + + template + GLM_INLINE glm::vec<4, T, Q> xyzy(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.x, v.y, v.z, v.y); + } + + // xyzz + template + GLM_INLINE glm::vec<4, T, Q> xyzz(const glm::vec<3, T, Q> &v) { + return glm::vec<4, T, Q>(v.x, v.y, v.z, v.z); + } + + template + GLM_INLINE glm::vec<4, T, Q> xyzz(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.x, v.y, v.z, v.z); + } + + // xyzw + template + GLM_INLINE glm::vec<4, T, Q> xyzw(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.x, v.y, v.z, v.w); + } + + // xywx + template + GLM_INLINE glm::vec<4, T, Q> xywx(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.x, v.y, v.w, v.x); + } + + // xywy + template + GLM_INLINE glm::vec<4, T, Q> xywy(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.x, v.y, v.w, v.y); + } + + // xywz + template + GLM_INLINE glm::vec<4, T, Q> xywz(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.x, v.y, v.w, v.z); + } + + // xyww + template + GLM_INLINE glm::vec<4, T, Q> xyww(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.x, v.y, v.w, v.w); + } + + // xzxx + template + GLM_INLINE glm::vec<4, T, Q> xzxx(const glm::vec<3, T, Q> &v) { + return glm::vec<4, T, Q>(v.x, v.z, v.x, v.x); + } + + template + GLM_INLINE glm::vec<4, T, Q> xzxx(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.x, v.z, v.x, v.x); + } + + // xzxy + template + GLM_INLINE glm::vec<4, T, Q> xzxy(const glm::vec<3, T, Q> &v) { + return glm::vec<4, T, Q>(v.x, v.z, v.x, v.y); + } + + template + GLM_INLINE glm::vec<4, T, Q> xzxy(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.x, v.z, v.x, v.y); + } + + // xzxz + template + GLM_INLINE glm::vec<4, T, Q> xzxz(const glm::vec<3, T, Q> &v) { + return glm::vec<4, T, Q>(v.x, v.z, v.x, v.z); + } + + template + GLM_INLINE glm::vec<4, T, Q> xzxz(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.x, v.z, v.x, v.z); + } + + // xzxw + template + GLM_INLINE glm::vec<4, T, Q> xzxw(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.x, v.z, v.x, v.w); + } + + // xzyx + template + GLM_INLINE glm::vec<4, T, Q> xzyx(const glm::vec<3, T, Q> &v) { + return glm::vec<4, T, Q>(v.x, v.z, v.y, v.x); + } + + template + GLM_INLINE glm::vec<4, T, Q> xzyx(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.x, v.z, v.y, v.x); + } + + // xzyy + template + GLM_INLINE glm::vec<4, T, Q> xzyy(const glm::vec<3, T, Q> &v) { + return glm::vec<4, T, Q>(v.x, v.z, v.y, v.y); + } + + template + GLM_INLINE glm::vec<4, T, Q> xzyy(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.x, v.z, v.y, v.y); + } + + // xzyz + template + GLM_INLINE glm::vec<4, T, Q> xzyz(const glm::vec<3, T, Q> &v) { + return glm::vec<4, T, Q>(v.x, v.z, v.y, v.z); + } + + template + GLM_INLINE glm::vec<4, T, Q> xzyz(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.x, v.z, v.y, v.z); + } + + // xzyw + template + GLM_INLINE glm::vec<4, T, Q> xzyw(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.x, v.z, v.y, v.w); + } + + // xzzx + template + GLM_INLINE glm::vec<4, T, Q> xzzx(const glm::vec<3, T, Q> &v) { + return glm::vec<4, T, Q>(v.x, v.z, v.z, v.x); + } + + template + GLM_INLINE glm::vec<4, T, Q> xzzx(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.x, v.z, v.z, v.x); + } + + // xzzy + template + GLM_INLINE glm::vec<4, T, Q> xzzy(const glm::vec<3, T, Q> &v) { + return glm::vec<4, T, Q>(v.x, v.z, v.z, v.y); + } + + template + GLM_INLINE glm::vec<4, T, Q> xzzy(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.x, v.z, v.z, v.y); + } + + // xzzz + template + GLM_INLINE glm::vec<4, T, Q> xzzz(const glm::vec<3, T, Q> &v) { + return glm::vec<4, T, Q>(v.x, v.z, v.z, v.z); + } + + template + GLM_INLINE glm::vec<4, T, Q> xzzz(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.x, v.z, v.z, v.z); + } + + // xzzw + template + GLM_INLINE glm::vec<4, T, Q> xzzw(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.x, v.z, v.z, v.w); + } + + // xzwx + template + GLM_INLINE glm::vec<4, T, Q> xzwx(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.x, v.z, v.w, v.x); + } + + // xzwy + template + GLM_INLINE glm::vec<4, T, Q> xzwy(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.x, v.z, v.w, v.y); + } + + // xzwz + template + GLM_INLINE glm::vec<4, T, Q> xzwz(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.x, v.z, v.w, v.z); + } + + // xzww + template + GLM_INLINE glm::vec<4, T, Q> xzww(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.x, v.z, v.w, v.w); + } + + // xwxx + template + GLM_INLINE glm::vec<4, T, Q> xwxx(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.x, v.w, v.x, v.x); + } + + // xwxy + template + GLM_INLINE glm::vec<4, T, Q> xwxy(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.x, v.w, v.x, v.y); + } + + // xwxz + template + GLM_INLINE glm::vec<4, T, Q> xwxz(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.x, v.w, v.x, v.z); + } + + // xwxw + template + GLM_INLINE glm::vec<4, T, Q> xwxw(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.x, v.w, v.x, v.w); + } + + // xwyx + template + GLM_INLINE glm::vec<4, T, Q> xwyx(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.x, v.w, v.y, v.x); + } + + // xwyy + template + GLM_INLINE glm::vec<4, T, Q> xwyy(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.x, v.w, v.y, v.y); + } + + // xwyz + template + GLM_INLINE glm::vec<4, T, Q> xwyz(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.x, v.w, v.y, v.z); + } + + // xwyw + template + GLM_INLINE glm::vec<4, T, Q> xwyw(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.x, v.w, v.y, v.w); + } + + // xwzx + template + GLM_INLINE glm::vec<4, T, Q> xwzx(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.x, v.w, v.z, v.x); + } + + // xwzy + template + GLM_INLINE glm::vec<4, T, Q> xwzy(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.x, v.w, v.z, v.y); + } + + // xwzz + template + GLM_INLINE glm::vec<4, T, Q> xwzz(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.x, v.w, v.z, v.z); + } + + // xwzw + template + GLM_INLINE glm::vec<4, T, Q> xwzw(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.x, v.w, v.z, v.w); + } + + // xwwx + template + GLM_INLINE glm::vec<4, T, Q> xwwx(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.x, v.w, v.w, v.x); + } + + // xwwy + template + GLM_INLINE glm::vec<4, T, Q> xwwy(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.x, v.w, v.w, v.y); + } + + // xwwz + template + GLM_INLINE glm::vec<4, T, Q> xwwz(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.x, v.w, v.w, v.z); + } + + // xwww + template + GLM_INLINE glm::vec<4, T, Q> xwww(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.x, v.w, v.w, v.w); + } + + // yxxx + template + GLM_INLINE glm::vec<4, T, Q> yxxx(const glm::vec<2, T, Q> &v) { + return glm::vec<4, T, Q>(v.y, v.x, v.x, v.x); + } + + template + GLM_INLINE glm::vec<4, T, Q> yxxx(const glm::vec<3, T, Q> &v) { + return glm::vec<4, T, Q>(v.y, v.x, v.x, v.x); + } + + template + GLM_INLINE glm::vec<4, T, Q> yxxx(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.y, v.x, v.x, v.x); + } + + // yxxy + template + GLM_INLINE glm::vec<4, T, Q> yxxy(const glm::vec<2, T, Q> &v) { + return glm::vec<4, T, Q>(v.y, v.x, v.x, v.y); + } + + template + GLM_INLINE glm::vec<4, T, Q> yxxy(const glm::vec<3, T, Q> &v) { + return glm::vec<4, T, Q>(v.y, v.x, v.x, v.y); + } + + template + GLM_INLINE glm::vec<4, T, Q> yxxy(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.y, v.x, v.x, v.y); + } + + // yxxz + template + GLM_INLINE glm::vec<4, T, Q> yxxz(const glm::vec<3, T, Q> &v) { + return glm::vec<4, T, Q>(v.y, v.x, v.x, v.z); + } + + template + GLM_INLINE glm::vec<4, T, Q> yxxz(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.y, v.x, v.x, v.z); + } + + // yxxw + template + GLM_INLINE glm::vec<4, T, Q> yxxw(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.y, v.x, v.x, v.w); + } + + // yxyx + template + GLM_INLINE glm::vec<4, T, Q> yxyx(const glm::vec<2, T, Q> &v) { + return glm::vec<4, T, Q>(v.y, v.x, v.y, v.x); + } + + template + GLM_INLINE glm::vec<4, T, Q> yxyx(const glm::vec<3, T, Q> &v) { + return glm::vec<4, T, Q>(v.y, v.x, v.y, v.x); + } + + template + GLM_INLINE glm::vec<4, T, Q> yxyx(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.y, v.x, v.y, v.x); + } + + // yxyy + template + GLM_INLINE glm::vec<4, T, Q> yxyy(const glm::vec<2, T, Q> &v) { + return glm::vec<4, T, Q>(v.y, v.x, v.y, v.y); + } + + template + GLM_INLINE glm::vec<4, T, Q> yxyy(const glm::vec<3, T, Q> &v) { + return glm::vec<4, T, Q>(v.y, v.x, v.y, v.y); + } + + template + GLM_INLINE glm::vec<4, T, Q> yxyy(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.y, v.x, v.y, v.y); + } + + // yxyz + template + GLM_INLINE glm::vec<4, T, Q> yxyz(const glm::vec<3, T, Q> &v) { + return glm::vec<4, T, Q>(v.y, v.x, v.y, v.z); + } + + template + GLM_INLINE glm::vec<4, T, Q> yxyz(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.y, v.x, v.y, v.z); + } + + // yxyw + template + GLM_INLINE glm::vec<4, T, Q> yxyw(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.y, v.x, v.y, v.w); + } + + // yxzx + template + GLM_INLINE glm::vec<4, T, Q> yxzx(const glm::vec<3, T, Q> &v) { + return glm::vec<4, T, Q>(v.y, v.x, v.z, v.x); + } + + template + GLM_INLINE glm::vec<4, T, Q> yxzx(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.y, v.x, v.z, v.x); + } + + // yxzy + template + GLM_INLINE glm::vec<4, T, Q> yxzy(const glm::vec<3, T, Q> &v) { + return glm::vec<4, T, Q>(v.y, v.x, v.z, v.y); + } + + template + GLM_INLINE glm::vec<4, T, Q> yxzy(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.y, v.x, v.z, v.y); + } + + // yxzz + template + GLM_INLINE glm::vec<4, T, Q> yxzz(const glm::vec<3, T, Q> &v) { + return glm::vec<4, T, Q>(v.y, v.x, v.z, v.z); + } + + template + GLM_INLINE glm::vec<4, T, Q> yxzz(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.y, v.x, v.z, v.z); + } + + // yxzw + template + GLM_INLINE glm::vec<4, T, Q> yxzw(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.y, v.x, v.z, v.w); + } + + // yxwx + template + GLM_INLINE glm::vec<4, T, Q> yxwx(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.y, v.x, v.w, v.x); + } + + // yxwy + template + GLM_INLINE glm::vec<4, T, Q> yxwy(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.y, v.x, v.w, v.y); + } + + // yxwz + template + GLM_INLINE glm::vec<4, T, Q> yxwz(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.y, v.x, v.w, v.z); + } + + // yxww + template + GLM_INLINE glm::vec<4, T, Q> yxww(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.y, v.x, v.w, v.w); + } + + // yyxx + template + GLM_INLINE glm::vec<4, T, Q> yyxx(const glm::vec<2, T, Q> &v) { + return glm::vec<4, T, Q>(v.y, v.y, v.x, v.x); + } + + template + GLM_INLINE glm::vec<4, T, Q> yyxx(const glm::vec<3, T, Q> &v) { + return glm::vec<4, T, Q>(v.y, v.y, v.x, v.x); + } + + template + GLM_INLINE glm::vec<4, T, Q> yyxx(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.y, v.y, v.x, v.x); + } + + // yyxy + template + GLM_INLINE glm::vec<4, T, Q> yyxy(const glm::vec<2, T, Q> &v) { + return glm::vec<4, T, Q>(v.y, v.y, v.x, v.y); + } + + template + GLM_INLINE glm::vec<4, T, Q> yyxy(const glm::vec<3, T, Q> &v) { + return glm::vec<4, T, Q>(v.y, v.y, v.x, v.y); + } + + template + GLM_INLINE glm::vec<4, T, Q> yyxy(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.y, v.y, v.x, v.y); + } + + // yyxz + template + GLM_INLINE glm::vec<4, T, Q> yyxz(const glm::vec<3, T, Q> &v) { + return glm::vec<4, T, Q>(v.y, v.y, v.x, v.z); + } + + template + GLM_INLINE glm::vec<4, T, Q> yyxz(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.y, v.y, v.x, v.z); + } + + // yyxw + template + GLM_INLINE glm::vec<4, T, Q> yyxw(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.y, v.y, v.x, v.w); + } + + // yyyx + template + GLM_INLINE glm::vec<4, T, Q> yyyx(const glm::vec<2, T, Q> &v) { + return glm::vec<4, T, Q>(v.y, v.y, v.y, v.x); + } + + template + GLM_INLINE glm::vec<4, T, Q> yyyx(const glm::vec<3, T, Q> &v) { + return glm::vec<4, T, Q>(v.y, v.y, v.y, v.x); + } + + template + GLM_INLINE glm::vec<4, T, Q> yyyx(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.y, v.y, v.y, v.x); + } + + // yyyy + template + GLM_INLINE glm::vec<4, T, Q> yyyy(const glm::vec<2, T, Q> &v) { + return glm::vec<4, T, Q>(v.y, v.y, v.y, v.y); + } + + template + GLM_INLINE glm::vec<4, T, Q> yyyy(const glm::vec<3, T, Q> &v) { + return glm::vec<4, T, Q>(v.y, v.y, v.y, v.y); + } + + template + GLM_INLINE glm::vec<4, T, Q> yyyy(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.y, v.y, v.y, v.y); + } + + // yyyz + template + GLM_INLINE glm::vec<4, T, Q> yyyz(const glm::vec<3, T, Q> &v) { + return glm::vec<4, T, Q>(v.y, v.y, v.y, v.z); + } + + template + GLM_INLINE glm::vec<4, T, Q> yyyz(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.y, v.y, v.y, v.z); + } + + // yyyw + template + GLM_INLINE glm::vec<4, T, Q> yyyw(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.y, v.y, v.y, v.w); + } + + // yyzx + template + GLM_INLINE glm::vec<4, T, Q> yyzx(const glm::vec<3, T, Q> &v) { + return glm::vec<4, T, Q>(v.y, v.y, v.z, v.x); + } + + template + GLM_INLINE glm::vec<4, T, Q> yyzx(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.y, v.y, v.z, v.x); + } + + // yyzy + template + GLM_INLINE glm::vec<4, T, Q> yyzy(const glm::vec<3, T, Q> &v) { + return glm::vec<4, T, Q>(v.y, v.y, v.z, v.y); + } + + template + GLM_INLINE glm::vec<4, T, Q> yyzy(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.y, v.y, v.z, v.y); + } + + // yyzz + template + GLM_INLINE glm::vec<4, T, Q> yyzz(const glm::vec<3, T, Q> &v) { + return glm::vec<4, T, Q>(v.y, v.y, v.z, v.z); + } + + template + GLM_INLINE glm::vec<4, T, Q> yyzz(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.y, v.y, v.z, v.z); + } + + // yyzw + template + GLM_INLINE glm::vec<4, T, Q> yyzw(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.y, v.y, v.z, v.w); + } + + // yywx + template + GLM_INLINE glm::vec<4, T, Q> yywx(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.y, v.y, v.w, v.x); + } + + // yywy + template + GLM_INLINE glm::vec<4, T, Q> yywy(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.y, v.y, v.w, v.y); + } + + // yywz + template + GLM_INLINE glm::vec<4, T, Q> yywz(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.y, v.y, v.w, v.z); + } + + // yyww + template + GLM_INLINE glm::vec<4, T, Q> yyww(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.y, v.y, v.w, v.w); + } + + // yzxx + template + GLM_INLINE glm::vec<4, T, Q> yzxx(const glm::vec<3, T, Q> &v) { + return glm::vec<4, T, Q>(v.y, v.z, v.x, v.x); + } + + template + GLM_INLINE glm::vec<4, T, Q> yzxx(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.y, v.z, v.x, v.x); + } + + // yzxy + template + GLM_INLINE glm::vec<4, T, Q> yzxy(const glm::vec<3, T, Q> &v) { + return glm::vec<4, T, Q>(v.y, v.z, v.x, v.y); + } + + template + GLM_INLINE glm::vec<4, T, Q> yzxy(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.y, v.z, v.x, v.y); + } + + // yzxz + template + GLM_INLINE glm::vec<4, T, Q> yzxz(const glm::vec<3, T, Q> &v) { + return glm::vec<4, T, Q>(v.y, v.z, v.x, v.z); + } + + template + GLM_INLINE glm::vec<4, T, Q> yzxz(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.y, v.z, v.x, v.z); + } + + // yzxw + template + GLM_INLINE glm::vec<4, T, Q> yzxw(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.y, v.z, v.x, v.w); + } + + // yzyx + template + GLM_INLINE glm::vec<4, T, Q> yzyx(const glm::vec<3, T, Q> &v) { + return glm::vec<4, T, Q>(v.y, v.z, v.y, v.x); + } + + template + GLM_INLINE glm::vec<4, T, Q> yzyx(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.y, v.z, v.y, v.x); + } + + // yzyy + template + GLM_INLINE glm::vec<4, T, Q> yzyy(const glm::vec<3, T, Q> &v) { + return glm::vec<4, T, Q>(v.y, v.z, v.y, v.y); + } + + template + GLM_INLINE glm::vec<4, T, Q> yzyy(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.y, v.z, v.y, v.y); + } + + // yzyz + template + GLM_INLINE glm::vec<4, T, Q> yzyz(const glm::vec<3, T, Q> &v) { + return glm::vec<4, T, Q>(v.y, v.z, v.y, v.z); + } + + template + GLM_INLINE glm::vec<4, T, Q> yzyz(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.y, v.z, v.y, v.z); + } + + // yzyw + template + GLM_INLINE glm::vec<4, T, Q> yzyw(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.y, v.z, v.y, v.w); + } + + // yzzx + template + GLM_INLINE glm::vec<4, T, Q> yzzx(const glm::vec<3, T, Q> &v) { + return glm::vec<4, T, Q>(v.y, v.z, v.z, v.x); + } + + template + GLM_INLINE glm::vec<4, T, Q> yzzx(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.y, v.z, v.z, v.x); + } + + // yzzy + template + GLM_INLINE glm::vec<4, T, Q> yzzy(const glm::vec<3, T, Q> &v) { + return glm::vec<4, T, Q>(v.y, v.z, v.z, v.y); + } + + template + GLM_INLINE glm::vec<4, T, Q> yzzy(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.y, v.z, v.z, v.y); + } + + // yzzz + template + GLM_INLINE glm::vec<4, T, Q> yzzz(const glm::vec<3, T, Q> &v) { + return glm::vec<4, T, Q>(v.y, v.z, v.z, v.z); + } + + template + GLM_INLINE glm::vec<4, T, Q> yzzz(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.y, v.z, v.z, v.z); + } + + // yzzw + template + GLM_INLINE glm::vec<4, T, Q> yzzw(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.y, v.z, v.z, v.w); + } + + // yzwx + template + GLM_INLINE glm::vec<4, T, Q> yzwx(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.y, v.z, v.w, v.x); + } + + // yzwy + template + GLM_INLINE glm::vec<4, T, Q> yzwy(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.y, v.z, v.w, v.y); + } + + // yzwz + template + GLM_INLINE glm::vec<4, T, Q> yzwz(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.y, v.z, v.w, v.z); + } + + // yzww + template + GLM_INLINE glm::vec<4, T, Q> yzww(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.y, v.z, v.w, v.w); + } + + // ywxx + template + GLM_INLINE glm::vec<4, T, Q> ywxx(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.y, v.w, v.x, v.x); + } + + // ywxy + template + GLM_INLINE glm::vec<4, T, Q> ywxy(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.y, v.w, v.x, v.y); + } + + // ywxz + template + GLM_INLINE glm::vec<4, T, Q> ywxz(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.y, v.w, v.x, v.z); + } + + // ywxw + template + GLM_INLINE glm::vec<4, T, Q> ywxw(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.y, v.w, v.x, v.w); + } + + // ywyx + template + GLM_INLINE glm::vec<4, T, Q> ywyx(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.y, v.w, v.y, v.x); + } + + // ywyy + template + GLM_INLINE glm::vec<4, T, Q> ywyy(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.y, v.w, v.y, v.y); + } + + // ywyz + template + GLM_INLINE glm::vec<4, T, Q> ywyz(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.y, v.w, v.y, v.z); + } + + // ywyw + template + GLM_INLINE glm::vec<4, T, Q> ywyw(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.y, v.w, v.y, v.w); + } + + // ywzx + template + GLM_INLINE glm::vec<4, T, Q> ywzx(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.y, v.w, v.z, v.x); + } + + // ywzy + template + GLM_INLINE glm::vec<4, T, Q> ywzy(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.y, v.w, v.z, v.y); + } + + // ywzz + template + GLM_INLINE glm::vec<4, T, Q> ywzz(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.y, v.w, v.z, v.z); + } + + // ywzw + template + GLM_INLINE glm::vec<4, T, Q> ywzw(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.y, v.w, v.z, v.w); + } + + // ywwx + template + GLM_INLINE glm::vec<4, T, Q> ywwx(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.y, v.w, v.w, v.x); + } + + // ywwy + template + GLM_INLINE glm::vec<4, T, Q> ywwy(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.y, v.w, v.w, v.y); + } + + // ywwz + template + GLM_INLINE glm::vec<4, T, Q> ywwz(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.y, v.w, v.w, v.z); + } + + // ywww + template + GLM_INLINE glm::vec<4, T, Q> ywww(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.y, v.w, v.w, v.w); + } + + // zxxx + template + GLM_INLINE glm::vec<4, T, Q> zxxx(const glm::vec<3, T, Q> &v) { + return glm::vec<4, T, Q>(v.z, v.x, v.x, v.x); + } + + template + GLM_INLINE glm::vec<4, T, Q> zxxx(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.z, v.x, v.x, v.x); + } + + // zxxy + template + GLM_INLINE glm::vec<4, T, Q> zxxy(const glm::vec<3, T, Q> &v) { + return glm::vec<4, T, Q>(v.z, v.x, v.x, v.y); + } + + template + GLM_INLINE glm::vec<4, T, Q> zxxy(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.z, v.x, v.x, v.y); + } + + // zxxz + template + GLM_INLINE glm::vec<4, T, Q> zxxz(const glm::vec<3, T, Q> &v) { + return glm::vec<4, T, Q>(v.z, v.x, v.x, v.z); + } + + template + GLM_INLINE glm::vec<4, T, Q> zxxz(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.z, v.x, v.x, v.z); + } + + // zxxw + template + GLM_INLINE glm::vec<4, T, Q> zxxw(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.z, v.x, v.x, v.w); + } + + // zxyx + template + GLM_INLINE glm::vec<4, T, Q> zxyx(const glm::vec<3, T, Q> &v) { + return glm::vec<4, T, Q>(v.z, v.x, v.y, v.x); + } + + template + GLM_INLINE glm::vec<4, T, Q> zxyx(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.z, v.x, v.y, v.x); + } + + // zxyy + template + GLM_INLINE glm::vec<4, T, Q> zxyy(const glm::vec<3, T, Q> &v) { + return glm::vec<4, T, Q>(v.z, v.x, v.y, v.y); + } + + template + GLM_INLINE glm::vec<4, T, Q> zxyy(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.z, v.x, v.y, v.y); + } + + // zxyz + template + GLM_INLINE glm::vec<4, T, Q> zxyz(const glm::vec<3, T, Q> &v) { + return glm::vec<4, T, Q>(v.z, v.x, v.y, v.z); + } + + template + GLM_INLINE glm::vec<4, T, Q> zxyz(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.z, v.x, v.y, v.z); + } + + // zxyw + template + GLM_INLINE glm::vec<4, T, Q> zxyw(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.z, v.x, v.y, v.w); + } + + // zxzx + template + GLM_INLINE glm::vec<4, T, Q> zxzx(const glm::vec<3, T, Q> &v) { + return glm::vec<4, T, Q>(v.z, v.x, v.z, v.x); + } + + template + GLM_INLINE glm::vec<4, T, Q> zxzx(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.z, v.x, v.z, v.x); + } + + // zxzy + template + GLM_INLINE glm::vec<4, T, Q> zxzy(const glm::vec<3, T, Q> &v) { + return glm::vec<4, T, Q>(v.z, v.x, v.z, v.y); + } + + template + GLM_INLINE glm::vec<4, T, Q> zxzy(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.z, v.x, v.z, v.y); + } + + // zxzz + template + GLM_INLINE glm::vec<4, T, Q> zxzz(const glm::vec<3, T, Q> &v) { + return glm::vec<4, T, Q>(v.z, v.x, v.z, v.z); + } + + template + GLM_INLINE glm::vec<4, T, Q> zxzz(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.z, v.x, v.z, v.z); + } + + // zxzw + template + GLM_INLINE glm::vec<4, T, Q> zxzw(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.z, v.x, v.z, v.w); + } + + // zxwx + template + GLM_INLINE glm::vec<4, T, Q> zxwx(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.z, v.x, v.w, v.x); + } + + // zxwy + template + GLM_INLINE glm::vec<4, T, Q> zxwy(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.z, v.x, v.w, v.y); + } + + // zxwz + template + GLM_INLINE glm::vec<4, T, Q> zxwz(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.z, v.x, v.w, v.z); + } + + // zxww + template + GLM_INLINE glm::vec<4, T, Q> zxww(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.z, v.x, v.w, v.w); + } + + // zyxx + template + GLM_INLINE glm::vec<4, T, Q> zyxx(const glm::vec<3, T, Q> &v) { + return glm::vec<4, T, Q>(v.z, v.y, v.x, v.x); + } + + template + GLM_INLINE glm::vec<4, T, Q> zyxx(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.z, v.y, v.x, v.x); + } + + // zyxy + template + GLM_INLINE glm::vec<4, T, Q> zyxy(const glm::vec<3, T, Q> &v) { + return glm::vec<4, T, Q>(v.z, v.y, v.x, v.y); + } + + template + GLM_INLINE glm::vec<4, T, Q> zyxy(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.z, v.y, v.x, v.y); + } + + // zyxz + template + GLM_INLINE glm::vec<4, T, Q> zyxz(const glm::vec<3, T, Q> &v) { + return glm::vec<4, T, Q>(v.z, v.y, v.x, v.z); + } + + template + GLM_INLINE glm::vec<4, T, Q> zyxz(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.z, v.y, v.x, v.z); + } + + // zyxw + template + GLM_INLINE glm::vec<4, T, Q> zyxw(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.z, v.y, v.x, v.w); + } + + // zyyx + template + GLM_INLINE glm::vec<4, T, Q> zyyx(const glm::vec<3, T, Q> &v) { + return glm::vec<4, T, Q>(v.z, v.y, v.y, v.x); + } + + template + GLM_INLINE glm::vec<4, T, Q> zyyx(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.z, v.y, v.y, v.x); + } + + // zyyy + template + GLM_INLINE glm::vec<4, T, Q> zyyy(const glm::vec<3, T, Q> &v) { + return glm::vec<4, T, Q>(v.z, v.y, v.y, v.y); + } + + template + GLM_INLINE glm::vec<4, T, Q> zyyy(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.z, v.y, v.y, v.y); + } + + // zyyz + template + GLM_INLINE glm::vec<4, T, Q> zyyz(const glm::vec<3, T, Q> &v) { + return glm::vec<4, T, Q>(v.z, v.y, v.y, v.z); + } + + template + GLM_INLINE glm::vec<4, T, Q> zyyz(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.z, v.y, v.y, v.z); + } + + // zyyw + template + GLM_INLINE glm::vec<4, T, Q> zyyw(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.z, v.y, v.y, v.w); + } + + // zyzx + template + GLM_INLINE glm::vec<4, T, Q> zyzx(const glm::vec<3, T, Q> &v) { + return glm::vec<4, T, Q>(v.z, v.y, v.z, v.x); + } + + template + GLM_INLINE glm::vec<4, T, Q> zyzx(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.z, v.y, v.z, v.x); + } + + // zyzy + template + GLM_INLINE glm::vec<4, T, Q> zyzy(const glm::vec<3, T, Q> &v) { + return glm::vec<4, T, Q>(v.z, v.y, v.z, v.y); + } + + template + GLM_INLINE glm::vec<4, T, Q> zyzy(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.z, v.y, v.z, v.y); + } + + // zyzz + template + GLM_INLINE glm::vec<4, T, Q> zyzz(const glm::vec<3, T, Q> &v) { + return glm::vec<4, T, Q>(v.z, v.y, v.z, v.z); + } + + template + GLM_INLINE glm::vec<4, T, Q> zyzz(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.z, v.y, v.z, v.z); + } + + // zyzw + template + GLM_INLINE glm::vec<4, T, Q> zyzw(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.z, v.y, v.z, v.w); + } + + // zywx + template + GLM_INLINE glm::vec<4, T, Q> zywx(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.z, v.y, v.w, v.x); + } + + // zywy + template + GLM_INLINE glm::vec<4, T, Q> zywy(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.z, v.y, v.w, v.y); + } + + // zywz + template + GLM_INLINE glm::vec<4, T, Q> zywz(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.z, v.y, v.w, v.z); + } + + // zyww + template + GLM_INLINE glm::vec<4, T, Q> zyww(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.z, v.y, v.w, v.w); + } + + // zzxx + template + GLM_INLINE glm::vec<4, T, Q> zzxx(const glm::vec<3, T, Q> &v) { + return glm::vec<4, T, Q>(v.z, v.z, v.x, v.x); + } + + template + GLM_INLINE glm::vec<4, T, Q> zzxx(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.z, v.z, v.x, v.x); + } + + // zzxy + template + GLM_INLINE glm::vec<4, T, Q> zzxy(const glm::vec<3, T, Q> &v) { + return glm::vec<4, T, Q>(v.z, v.z, v.x, v.y); + } + + template + GLM_INLINE glm::vec<4, T, Q> zzxy(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.z, v.z, v.x, v.y); + } + + // zzxz + template + GLM_INLINE glm::vec<4, T, Q> zzxz(const glm::vec<3, T, Q> &v) { + return glm::vec<4, T, Q>(v.z, v.z, v.x, v.z); + } + + template + GLM_INLINE glm::vec<4, T, Q> zzxz(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.z, v.z, v.x, v.z); + } + + // zzxw + template + GLM_INLINE glm::vec<4, T, Q> zzxw(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.z, v.z, v.x, v.w); + } + + // zzyx + template + GLM_INLINE glm::vec<4, T, Q> zzyx(const glm::vec<3, T, Q> &v) { + return glm::vec<4, T, Q>(v.z, v.z, v.y, v.x); + } + + template + GLM_INLINE glm::vec<4, T, Q> zzyx(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.z, v.z, v.y, v.x); + } + + // zzyy + template + GLM_INLINE glm::vec<4, T, Q> zzyy(const glm::vec<3, T, Q> &v) { + return glm::vec<4, T, Q>(v.z, v.z, v.y, v.y); + } + + template + GLM_INLINE glm::vec<4, T, Q> zzyy(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.z, v.z, v.y, v.y); + } + + // zzyz + template + GLM_INLINE glm::vec<4, T, Q> zzyz(const glm::vec<3, T, Q> &v) { + return glm::vec<4, T, Q>(v.z, v.z, v.y, v.z); + } + + template + GLM_INLINE glm::vec<4, T, Q> zzyz(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.z, v.z, v.y, v.z); + } + + // zzyw + template + GLM_INLINE glm::vec<4, T, Q> zzyw(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.z, v.z, v.y, v.w); + } + + // zzzx + template + GLM_INLINE glm::vec<4, T, Q> zzzx(const glm::vec<3, T, Q> &v) { + return glm::vec<4, T, Q>(v.z, v.z, v.z, v.x); + } + + template + GLM_INLINE glm::vec<4, T, Q> zzzx(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.z, v.z, v.z, v.x); + } + + // zzzy + template + GLM_INLINE glm::vec<4, T, Q> zzzy(const glm::vec<3, T, Q> &v) { + return glm::vec<4, T, Q>(v.z, v.z, v.z, v.y); + } + + template + GLM_INLINE glm::vec<4, T, Q> zzzy(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.z, v.z, v.z, v.y); + } + + // zzzz + template + GLM_INLINE glm::vec<4, T, Q> zzzz(const glm::vec<3, T, Q> &v) { + return glm::vec<4, T, Q>(v.z, v.z, v.z, v.z); + } + + template + GLM_INLINE glm::vec<4, T, Q> zzzz(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.z, v.z, v.z, v.z); + } + + // zzzw + template + GLM_INLINE glm::vec<4, T, Q> zzzw(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.z, v.z, v.z, v.w); + } + + // zzwx + template + GLM_INLINE glm::vec<4, T, Q> zzwx(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.z, v.z, v.w, v.x); + } + + // zzwy + template + GLM_INLINE glm::vec<4, T, Q> zzwy(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.z, v.z, v.w, v.y); + } + + // zzwz + template + GLM_INLINE glm::vec<4, T, Q> zzwz(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.z, v.z, v.w, v.z); + } + + // zzww + template + GLM_INLINE glm::vec<4, T, Q> zzww(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.z, v.z, v.w, v.w); + } + + // zwxx + template + GLM_INLINE glm::vec<4, T, Q> zwxx(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.z, v.w, v.x, v.x); + } + + // zwxy + template + GLM_INLINE glm::vec<4, T, Q> zwxy(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.z, v.w, v.x, v.y); + } + + // zwxz + template + GLM_INLINE glm::vec<4, T, Q> zwxz(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.z, v.w, v.x, v.z); + } + + // zwxw + template + GLM_INLINE glm::vec<4, T, Q> zwxw(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.z, v.w, v.x, v.w); + } + + // zwyx + template + GLM_INLINE glm::vec<4, T, Q> zwyx(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.z, v.w, v.y, v.x); + } + + // zwyy + template + GLM_INLINE glm::vec<4, T, Q> zwyy(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.z, v.w, v.y, v.y); + } + + // zwyz + template + GLM_INLINE glm::vec<4, T, Q> zwyz(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.z, v.w, v.y, v.z); + } + + // zwyw + template + GLM_INLINE glm::vec<4, T, Q> zwyw(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.z, v.w, v.y, v.w); + } + + // zwzx + template + GLM_INLINE glm::vec<4, T, Q> zwzx(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.z, v.w, v.z, v.x); + } + + // zwzy + template + GLM_INLINE glm::vec<4, T, Q> zwzy(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.z, v.w, v.z, v.y); + } + + // zwzz + template + GLM_INLINE glm::vec<4, T, Q> zwzz(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.z, v.w, v.z, v.z); + } + + // zwzw + template + GLM_INLINE glm::vec<4, T, Q> zwzw(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.z, v.w, v.z, v.w); + } + + // zwwx + template + GLM_INLINE glm::vec<4, T, Q> zwwx(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.z, v.w, v.w, v.x); + } + + // zwwy + template + GLM_INLINE glm::vec<4, T, Q> zwwy(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.z, v.w, v.w, v.y); + } + + // zwwz + template + GLM_INLINE glm::vec<4, T, Q> zwwz(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.z, v.w, v.w, v.z); + } + + // zwww + template + GLM_INLINE glm::vec<4, T, Q> zwww(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.z, v.w, v.w, v.w); + } + + // wxxx + template + GLM_INLINE glm::vec<4, T, Q> wxxx(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.w, v.x, v.x, v.x); + } + + // wxxy + template + GLM_INLINE glm::vec<4, T, Q> wxxy(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.w, v.x, v.x, v.y); + } + + // wxxz + template + GLM_INLINE glm::vec<4, T, Q> wxxz(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.w, v.x, v.x, v.z); + } + + // wxxw + template + GLM_INLINE glm::vec<4, T, Q> wxxw(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.w, v.x, v.x, v.w); + } + + // wxyx + template + GLM_INLINE glm::vec<4, T, Q> wxyx(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.w, v.x, v.y, v.x); + } + + // wxyy + template + GLM_INLINE glm::vec<4, T, Q> wxyy(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.w, v.x, v.y, v.y); + } + + // wxyz + template + GLM_INLINE glm::vec<4, T, Q> wxyz(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.w, v.x, v.y, v.z); + } + + // wxyw + template + GLM_INLINE glm::vec<4, T, Q> wxyw(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.w, v.x, v.y, v.w); + } + + // wxzx + template + GLM_INLINE glm::vec<4, T, Q> wxzx(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.w, v.x, v.z, v.x); + } + + // wxzy + template + GLM_INLINE glm::vec<4, T, Q> wxzy(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.w, v.x, v.z, v.y); + } + + // wxzz + template + GLM_INLINE glm::vec<4, T, Q> wxzz(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.w, v.x, v.z, v.z); + } + + // wxzw + template + GLM_INLINE glm::vec<4, T, Q> wxzw(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.w, v.x, v.z, v.w); + } + + // wxwx + template + GLM_INLINE glm::vec<4, T, Q> wxwx(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.w, v.x, v.w, v.x); + } + + // wxwy + template + GLM_INLINE glm::vec<4, T, Q> wxwy(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.w, v.x, v.w, v.y); + } + + // wxwz + template + GLM_INLINE glm::vec<4, T, Q> wxwz(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.w, v.x, v.w, v.z); + } + + // wxww + template + GLM_INLINE glm::vec<4, T, Q> wxww(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.w, v.x, v.w, v.w); + } + + // wyxx + template + GLM_INLINE glm::vec<4, T, Q> wyxx(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.w, v.y, v.x, v.x); + } + + // wyxy + template + GLM_INLINE glm::vec<4, T, Q> wyxy(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.w, v.y, v.x, v.y); + } + + // wyxz + template + GLM_INLINE glm::vec<4, T, Q> wyxz(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.w, v.y, v.x, v.z); + } + + // wyxw + template + GLM_INLINE glm::vec<4, T, Q> wyxw(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.w, v.y, v.x, v.w); + } + + // wyyx + template + GLM_INLINE glm::vec<4, T, Q> wyyx(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.w, v.y, v.y, v.x); + } + + // wyyy + template + GLM_INLINE glm::vec<4, T, Q> wyyy(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.w, v.y, v.y, v.y); + } + + // wyyz + template + GLM_INLINE glm::vec<4, T, Q> wyyz(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.w, v.y, v.y, v.z); + } + + // wyyw + template + GLM_INLINE glm::vec<4, T, Q> wyyw(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.w, v.y, v.y, v.w); + } + + // wyzx + template + GLM_INLINE glm::vec<4, T, Q> wyzx(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.w, v.y, v.z, v.x); + } + + // wyzy + template + GLM_INLINE glm::vec<4, T, Q> wyzy(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.w, v.y, v.z, v.y); + } + + // wyzz + template + GLM_INLINE glm::vec<4, T, Q> wyzz(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.w, v.y, v.z, v.z); + } + + // wyzw + template + GLM_INLINE glm::vec<4, T, Q> wyzw(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.w, v.y, v.z, v.w); + } + + // wywx + template + GLM_INLINE glm::vec<4, T, Q> wywx(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.w, v.y, v.w, v.x); + } + + // wywy + template + GLM_INLINE glm::vec<4, T, Q> wywy(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.w, v.y, v.w, v.y); + } + + // wywz + template + GLM_INLINE glm::vec<4, T, Q> wywz(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.w, v.y, v.w, v.z); + } + + // wyww + template + GLM_INLINE glm::vec<4, T, Q> wyww(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.w, v.y, v.w, v.w); + } + + // wzxx + template + GLM_INLINE glm::vec<4, T, Q> wzxx(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.w, v.z, v.x, v.x); + } + + // wzxy + template + GLM_INLINE glm::vec<4, T, Q> wzxy(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.w, v.z, v.x, v.y); + } + + // wzxz + template + GLM_INLINE glm::vec<4, T, Q> wzxz(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.w, v.z, v.x, v.z); + } + + // wzxw + template + GLM_INLINE glm::vec<4, T, Q> wzxw(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.w, v.z, v.x, v.w); + } + + // wzyx + template + GLM_INLINE glm::vec<4, T, Q> wzyx(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.w, v.z, v.y, v.x); + } + + // wzyy + template + GLM_INLINE glm::vec<4, T, Q> wzyy(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.w, v.z, v.y, v.y); + } + + // wzyz + template + GLM_INLINE glm::vec<4, T, Q> wzyz(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.w, v.z, v.y, v.z); + } + + // wzyw + template + GLM_INLINE glm::vec<4, T, Q> wzyw(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.w, v.z, v.y, v.w); + } + + // wzzx + template + GLM_INLINE glm::vec<4, T, Q> wzzx(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.w, v.z, v.z, v.x); + } + + // wzzy + template + GLM_INLINE glm::vec<4, T, Q> wzzy(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.w, v.z, v.z, v.y); + } + + // wzzz + template + GLM_INLINE glm::vec<4, T, Q> wzzz(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.w, v.z, v.z, v.z); + } + + // wzzw + template + GLM_INLINE glm::vec<4, T, Q> wzzw(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.w, v.z, v.z, v.w); + } + + // wzwx + template + GLM_INLINE glm::vec<4, T, Q> wzwx(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.w, v.z, v.w, v.x); + } + + // wzwy + template + GLM_INLINE glm::vec<4, T, Q> wzwy(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.w, v.z, v.w, v.y); + } + + // wzwz + template + GLM_INLINE glm::vec<4, T, Q> wzwz(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.w, v.z, v.w, v.z); + } + + // wzww + template + GLM_INLINE glm::vec<4, T, Q> wzww(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.w, v.z, v.w, v.w); + } + + // wwxx + template + GLM_INLINE glm::vec<4, T, Q> wwxx(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.w, v.w, v.x, v.x); + } + + // wwxy + template + GLM_INLINE glm::vec<4, T, Q> wwxy(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.w, v.w, v.x, v.y); + } + + // wwxz + template + GLM_INLINE glm::vec<4, T, Q> wwxz(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.w, v.w, v.x, v.z); + } + + // wwxw + template + GLM_INLINE glm::vec<4, T, Q> wwxw(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.w, v.w, v.x, v.w); + } + + // wwyx + template + GLM_INLINE glm::vec<4, T, Q> wwyx(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.w, v.w, v.y, v.x); + } + + // wwyy + template + GLM_INLINE glm::vec<4, T, Q> wwyy(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.w, v.w, v.y, v.y); + } + + // wwyz + template + GLM_INLINE glm::vec<4, T, Q> wwyz(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.w, v.w, v.y, v.z); + } + + // wwyw + template + GLM_INLINE glm::vec<4, T, Q> wwyw(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.w, v.w, v.y, v.w); + } + + // wwzx + template + GLM_INLINE glm::vec<4, T, Q> wwzx(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.w, v.w, v.z, v.x); + } + + // wwzy + template + GLM_INLINE glm::vec<4, T, Q> wwzy(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.w, v.w, v.z, v.y); + } + + // wwzz + template + GLM_INLINE glm::vec<4, T, Q> wwzz(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.w, v.w, v.z, v.z); + } + + // wwzw + template + GLM_INLINE glm::vec<4, T, Q> wwzw(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.w, v.w, v.z, v.w); + } + + // wwwx + template + GLM_INLINE glm::vec<4, T, Q> wwwx(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.w, v.w, v.w, v.x); + } + + // wwwy + template + GLM_INLINE glm::vec<4, T, Q> wwwy(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.w, v.w, v.w, v.y); + } + + // wwwz + template + GLM_INLINE glm::vec<4, T, Q> wwwz(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.w, v.w, v.w, v.z); + } + + // wwww + template + GLM_INLINE glm::vec<4, T, Q> wwww(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.w, v.w, v.w, v.w); + } + +} diff --git a/src/GLMath/glm/gtx/vector_angle.hpp b/src/GLMath/glm/gtx/vector_angle.hpp new file mode 100644 index 000000000..9ae437126 --- /dev/null +++ b/src/GLMath/glm/gtx/vector_angle.hpp @@ -0,0 +1,57 @@ +/// @ref gtx_vector_angle +/// @file glm/gtx/vector_angle.hpp +/// +/// @see core (dependence) +/// @see gtx_quaternion (dependence) +/// @see gtx_epsilon (dependence) +/// +/// @defgroup gtx_vector_angle GLM_GTX_vector_angle +/// @ingroup gtx +/// +/// Include to use the features of this extension. +/// +/// Compute angle between vectors + +#pragma once + +// Dependency: +#include "../glm.hpp" +#include "../gtc/epsilon.hpp" +#include "../gtx/quaternion.hpp" +#include "../gtx/rotate_vector.hpp" + +#if GLM_MESSAGES == GLM_ENABLE && !defined(GLM_EXT_INCLUDED) +# ifndef GLM_ENABLE_EXPERIMENTAL +# pragma message("GLM: GLM_GTX_vector_angle is an experimental extension and may change in the future. Use #define GLM_ENABLE_EXPERIMENTAL before including it, if you really want to use it.") +# else +# pragma message("GLM: GLM_GTX_vector_angle extension included") +# endif +#endif + +namespace glm +{ + /// @addtogroup gtx_vector_angle + /// @{ + + //! Returns the absolute angle between two vectors. + //! Parameters need to be normalized. + /// @see gtx_vector_angle extension. + template + GLM_FUNC_DECL T angle(vec const& x, vec const& y); + + //! Returns the oriented angle between two 2d vectors. + //! Parameters need to be normalized. + /// @see gtx_vector_angle extension. + template + GLM_FUNC_DECL T orientedAngle(vec<2, T, Q> const& x, vec<2, T, Q> const& y); + + //! Returns the oriented angle between two 3d vectors based from a reference axis. + //! Parameters need to be normalized. + /// @see gtx_vector_angle extension. + template + GLM_FUNC_DECL T orientedAngle(vec<3, T, Q> const& x, vec<3, T, Q> const& y, vec<3, T, Q> const& ref); + + /// @} +}// namespace glm + +#include "vector_angle.inl" diff --git a/src/GLMath/glm/gtx/vector_angle.inl b/src/GLMath/glm/gtx/vector_angle.inl new file mode 100644 index 000000000..a1f957a59 --- /dev/null +++ b/src/GLMath/glm/gtx/vector_angle.inl @@ -0,0 +1,44 @@ +/// @ref gtx_vector_angle + +namespace glm +{ + template + GLM_FUNC_QUALIFIER genType angle + ( + genType const& x, + genType const& y + ) + { + GLM_STATIC_ASSERT(std::numeric_limits::is_iec559, "'angle' only accept floating-point inputs"); + return acos(clamp(dot(x, y), genType(-1), genType(1))); + } + + template + GLM_FUNC_QUALIFIER T angle(vec const& x, vec const& y) + { + GLM_STATIC_ASSERT(std::numeric_limits::is_iec559, "'angle' only accept floating-point inputs"); + return acos(clamp(dot(x, y), T(-1), T(1))); + } + + //! \todo epsilon is hard coded to 0.01 + template + GLM_FUNC_QUALIFIER T orientedAngle(vec<2, T, Q> const& x, vec<2, T, Q> const& y) + { + GLM_STATIC_ASSERT(std::numeric_limits::is_iec559, "'orientedAngle' only accept floating-point inputs"); + T const Angle(acos(clamp(dot(x, y), T(-1), T(1)))); + + if(all(epsilonEqual(y, glm::rotate(x, Angle), T(0.0001)))) + return Angle; + else + return -Angle; + } + + template + GLM_FUNC_QUALIFIER T orientedAngle(vec<3, T, Q> const& x, vec<3, T, Q> const& y, vec<3, T, Q> const& ref) + { + GLM_STATIC_ASSERT(std::numeric_limits::is_iec559, "'orientedAngle' only accept floating-point inputs"); + + T const Angle(acos(clamp(dot(x, y), T(-1), T(1)))); + return mix(Angle, -Angle, dot(ref, cross(x, y)) < T(0)); + } +}//namespace glm diff --git a/src/GLMath/glm/gtx/vector_query.hpp b/src/GLMath/glm/gtx/vector_query.hpp new file mode 100644 index 000000000..77c7b974b --- /dev/null +++ b/src/GLMath/glm/gtx/vector_query.hpp @@ -0,0 +1,66 @@ +/// @ref gtx_vector_query +/// @file glm/gtx/vector_query.hpp +/// +/// @see core (dependence) +/// +/// @defgroup gtx_vector_query GLM_GTX_vector_query +/// @ingroup gtx +/// +/// Include to use the features of this extension. +/// +/// Query informations of vector types + +#pragma once + +// Dependency: +#include "../glm.hpp" +#include +#include + +#if GLM_MESSAGES == GLM_ENABLE && !defined(GLM_EXT_INCLUDED) +# ifndef GLM_ENABLE_EXPERIMENTAL +# pragma message("GLM: GLM_GTX_vector_query is an experimental extension and may change in the future. Use #define GLM_ENABLE_EXPERIMENTAL before including it, if you really want to use it.") +# else +# pragma message("GLM: GLM_GTX_vector_query extension included") +# endif +#endif + +namespace glm +{ + /// @addtogroup gtx_vector_query + /// @{ + + //! Check whether two vectors are collinears. + /// @see gtx_vector_query extensions. + template + GLM_FUNC_DECL bool areCollinear(vec const& v0, vec const& v1, T const& epsilon); + + //! Check whether two vectors are orthogonals. + /// @see gtx_vector_query extensions. + template + GLM_FUNC_DECL bool areOrthogonal(vec const& v0, vec const& v1, T const& epsilon); + + //! Check whether a vector is normalized. + /// @see gtx_vector_query extensions. + template + GLM_FUNC_DECL bool isNormalized(vec const& v, T const& epsilon); + + //! Check whether a vector is null. + /// @see gtx_vector_query extensions. + template + GLM_FUNC_DECL bool isNull(vec const& v, T const& epsilon); + + //! Check whether a each component of a vector is null. + /// @see gtx_vector_query extensions. + template + GLM_FUNC_DECL vec isCompNull(vec const& v, T const& epsilon); + + //! Check whether two vectors are orthonormal. + /// @see gtx_vector_query extensions. + template + GLM_FUNC_DECL bool areOrthonormal(vec const& v0, vec const& v1, T const& epsilon); + + /// @} +}// namespace glm + +#include "vector_query.inl" diff --git a/src/GLMath/glm/gtx/vector_query.inl b/src/GLMath/glm/gtx/vector_query.inl new file mode 100644 index 000000000..d1a5c9be4 --- /dev/null +++ b/src/GLMath/glm/gtx/vector_query.inl @@ -0,0 +1,154 @@ +/// @ref gtx_vector_query + +#include + +namespace glm{ +namespace detail +{ + template + struct compute_areCollinear{}; + + template + struct compute_areCollinear<2, T, Q> + { + GLM_FUNC_QUALIFIER static bool call(vec<2, T, Q> const& v0, vec<2, T, Q> const& v1, T const& epsilon) + { + return length(cross(vec<3, T, Q>(v0, static_cast(0)), vec<3, T, Q>(v1, static_cast(0)))) < epsilon; + } + }; + + template + struct compute_areCollinear<3, T, Q> + { + GLM_FUNC_QUALIFIER static bool call(vec<3, T, Q> const& v0, vec<3, T, Q> const& v1, T const& epsilon) + { + return length(cross(v0, v1)) < epsilon; + } + }; + + template + struct compute_areCollinear<4, T, Q> + { + GLM_FUNC_QUALIFIER static bool call(vec<4, T, Q> const& v0, vec<4, T, Q> const& v1, T const& epsilon) + { + return length(cross(vec<3, T, Q>(v0), vec<3, T, Q>(v1))) < epsilon; + } + }; + + template + struct compute_isCompNull{}; + + template + struct compute_isCompNull<2, T, Q> + { + GLM_FUNC_QUALIFIER static vec<2, bool, Q> call(vec<2, T, Q> const& v, T const& epsilon) + { + return vec<2, bool, Q>( + (abs(v.x) < epsilon), + (abs(v.y) < epsilon)); + } + }; + + template + struct compute_isCompNull<3, T, Q> + { + GLM_FUNC_QUALIFIER static vec<3, bool, Q> call(vec<3, T, Q> const& v, T const& epsilon) + { + return vec<3, bool, Q>( + (abs(v.x) < epsilon), + (abs(v.y) < epsilon), + (abs(v.z) < epsilon)); + } + }; + + template + struct compute_isCompNull<4, T, Q> + { + GLM_FUNC_QUALIFIER static vec<4, bool, Q> call(vec<4, T, Q> const& v, T const& epsilon) + { + return vec<4, bool, Q>( + (abs(v.x) < epsilon), + (abs(v.y) < epsilon), + (abs(v.z) < epsilon), + (abs(v.w) < epsilon)); + } + }; + +}//namespace detail + + template + GLM_FUNC_QUALIFIER bool areCollinear(vec const& v0, vec const& v1, T const& epsilon) + { + GLM_STATIC_ASSERT(std::numeric_limits::is_iec559, "'areCollinear' only accept floating-point inputs"); + + return detail::compute_areCollinear::call(v0, v1, epsilon); + } + + template + GLM_FUNC_QUALIFIER bool areOrthogonal(vec const& v0, vec const& v1, T const& epsilon) + { + GLM_STATIC_ASSERT(std::numeric_limits::is_iec559, "'areOrthogonal' only accept floating-point inputs"); + + return abs(dot(v0, v1)) <= max( + static_cast(1), + length(v0)) * max(static_cast(1), length(v1)) * epsilon; + } + + template + GLM_FUNC_QUALIFIER bool isNormalized(vec const& v, T const& epsilon) + { + GLM_STATIC_ASSERT(std::numeric_limits::is_iec559, "'isNormalized' only accept floating-point inputs"); + + return abs(length(v) - static_cast(1)) <= static_cast(2) * epsilon; + } + + template + GLM_FUNC_QUALIFIER bool isNull(vec const& v, T const& epsilon) + { + GLM_STATIC_ASSERT(std::numeric_limits::is_iec559, "'isNull' only accept floating-point inputs"); + + return length(v) <= epsilon; + } + + template + GLM_FUNC_QUALIFIER vec isCompNull(vec const& v, T const& epsilon) + { + GLM_STATIC_ASSERT(std::numeric_limits::is_iec559, "'isCompNull' only accept floating-point inputs"); + + return detail::compute_isCompNull::call(v, epsilon); + } + + template + GLM_FUNC_QUALIFIER vec<2, bool, Q> isCompNull(vec<2, T, Q> const& v, T const& epsilon) + { + return vec<2, bool, Q>( + abs(v.x) < epsilon, + abs(v.y) < epsilon); + } + + template + GLM_FUNC_QUALIFIER vec<3, bool, Q> isCompNull(vec<3, T, Q> const& v, T const& epsilon) + { + return vec<3, bool, Q>( + abs(v.x) < epsilon, + abs(v.y) < epsilon, + abs(v.z) < epsilon); + } + + template + GLM_FUNC_QUALIFIER vec<4, bool, Q> isCompNull(vec<4, T, Q> const& v, T const& epsilon) + { + return vec<4, bool, Q>( + abs(v.x) < epsilon, + abs(v.y) < epsilon, + abs(v.z) < epsilon, + abs(v.w) < epsilon); + } + + template + GLM_FUNC_QUALIFIER bool areOrthonormal(vec const& v0, vec const& v1, T const& epsilon) + { + return isNormalized(v0, epsilon) && isNormalized(v1, epsilon) && (abs(dot(v0, v1)) <= epsilon); + } + +}//namespace glm diff --git a/src/GLMath/glm/gtx/wrap.hpp b/src/GLMath/glm/gtx/wrap.hpp new file mode 100644 index 000000000..02c51965c --- /dev/null +++ b/src/GLMath/glm/gtx/wrap.hpp @@ -0,0 +1,55 @@ +/// @ref gtx_wrap +/// @file glm/gtx/wrap.hpp +/// +/// @see core (dependence) +/// +/// @defgroup gtx_wrap GLM_GTX_wrap +/// @ingroup gtx +/// +/// Include to use the features of this extension. +/// +/// Wrapping mode of texture coordinates. + +#pragma once + +// Dependency: +#include "../glm.hpp" +#include "../gtc/vec1.hpp" + +#if GLM_MESSAGES == GLM_ENABLE && !defined(GLM_EXT_INCLUDED) +# ifndef GLM_ENABLE_EXPERIMENTAL +# pragma message("GLM: GLM_GTX_wrap is an experimental extension and may change in the future. Use #define GLM_ENABLE_EXPERIMENTAL before including it, if you really want to use it.") +# else +# pragma message("GLM: GLM_GTX_wrap extension included") +# endif +#endif + +namespace glm +{ + /// @addtogroup gtx_wrap + /// @{ + + /// Simulate GL_CLAMP OpenGL wrap mode + /// @see gtx_wrap extension. + template + GLM_FUNC_DECL genType clamp(genType const& Texcoord); + + /// Simulate GL_REPEAT OpenGL wrap mode + /// @see gtx_wrap extension. + template + GLM_FUNC_DECL genType repeat(genType const& Texcoord); + + /// Simulate GL_MIRRORED_REPEAT OpenGL wrap mode + /// @see gtx_wrap extension. + template + GLM_FUNC_DECL genType mirrorClamp(genType const& Texcoord); + + /// Simulate GL_MIRROR_REPEAT OpenGL wrap mode + /// @see gtx_wrap extension. + template + GLM_FUNC_DECL genType mirrorRepeat(genType const& Texcoord); + + /// @} +}// namespace glm + +#include "wrap.inl" diff --git a/src/GLMath/glm/gtx/wrap.inl b/src/GLMath/glm/gtx/wrap.inl new file mode 100644 index 000000000..409a316ab --- /dev/null +++ b/src/GLMath/glm/gtx/wrap.inl @@ -0,0 +1,57 @@ +/// @ref gtx_wrap + +namespace glm +{ + template + GLM_FUNC_QUALIFIER vec clamp(vec const& Texcoord) + { + return glm::clamp(Texcoord, vec(0), vec(1)); + } + + template + GLM_FUNC_QUALIFIER genType clamp(genType const& Texcoord) + { + return clamp(vec<1, genType, defaultp>(Texcoord)).x; + } + + template + GLM_FUNC_QUALIFIER vec repeat(vec const& Texcoord) + { + return glm::fract(Texcoord); + } + + template + GLM_FUNC_QUALIFIER genType repeat(genType const& Texcoord) + { + return repeat(vec<1, genType, defaultp>(Texcoord)).x; + } + + template + GLM_FUNC_QUALIFIER vec mirrorClamp(vec const& Texcoord) + { + return glm::fract(glm::abs(Texcoord)); + } + + template + GLM_FUNC_QUALIFIER genType mirrorClamp(genType const& Texcoord) + { + return mirrorClamp(vec<1, genType, defaultp>(Texcoord)).x; + } + + template + GLM_FUNC_QUALIFIER vec mirrorRepeat(vec const& Texcoord) + { + vec const Abs = glm::abs(Texcoord); + vec const Clamp = glm::mod(glm::floor(Abs), vec(2)); + vec const Floor = glm::floor(Abs); + vec const Rest = Abs - Floor; + vec const Mirror = Clamp + Rest; + return mix(Rest, vec(1) - Rest, glm::greaterThanEqual(Mirror, vec(1))); + } + + template + GLM_FUNC_QUALIFIER genType mirrorRepeat(genType const& Texcoord) + { + return mirrorRepeat(vec<1, genType, defaultp>(Texcoord)).x; + } +}//namespace glm diff --git a/src/GLMath/glm/integer.hpp b/src/GLMath/glm/integer.hpp new file mode 100644 index 000000000..8817db3f0 --- /dev/null +++ b/src/GLMath/glm/integer.hpp @@ -0,0 +1,212 @@ +/// @ref core +/// @file glm/integer.hpp +/// +/// @see GLSL 4.20.8 specification, section 8.8 Integer Functions +/// +/// @defgroup core_func_integer Integer functions +/// @ingroup core +/// +/// Provides GLSL functions on integer types +/// +/// These all operate component-wise. The description is per component. +/// The notation [a, b] means the set of bits from bit-number a through bit-number +/// b, inclusive. The lowest-order bit is bit 0. +/// +/// Include to use these core features. + +#pragma once + +#include "detail/qualifier.hpp" +#include "common.hpp" +#include "vector_relational.hpp" + +namespace glm +{ + /// @addtogroup core_func_integer + /// @{ + + /// Adds 32-bit unsigned integer x and y, returning the sum + /// modulo pow(2, 32). The value carry is set to 0 if the sum was + /// less than pow(2, 32), or to 1 otherwise. + /// + /// @tparam L An integer between 1 and 4 included that qualify the dimension of the vector. + /// + /// @see GLSL uaddCarry man page + /// @see GLSL 4.20.8 specification, section 8.8 Integer Functions + template + GLM_FUNC_DECL vec uaddCarry( + vec const& x, + vec const& y, + vec & carry); + + /// Subtracts the 32-bit unsigned integer y from x, returning + /// the difference if non-negative, or pow(2, 32) plus the difference + /// otherwise. The value borrow is set to 0 if x >= y, or to 1 otherwise. + /// + /// @tparam L An integer between 1 and 4 included that qualify the dimension of the vector. + /// + /// @see GLSL usubBorrow man page + /// @see GLSL 4.20.8 specification, section 8.8 Integer Functions + template + GLM_FUNC_DECL vec usubBorrow( + vec const& x, + vec const& y, + vec & borrow); + + /// Multiplies 32-bit integers x and y, producing a 64-bit + /// result. The 32 least-significant bits are returned in lsb. + /// The 32 most-significant bits are returned in msb. + /// + /// @tparam L An integer between 1 and 4 included that qualify the dimension of the vector. + /// + /// @see GLSL umulExtended man page + /// @see GLSL 4.20.8 specification, section 8.8 Integer Functions + template + GLM_FUNC_DECL void umulExtended( + vec const& x, + vec const& y, + vec & msb, + vec & lsb); + + /// Multiplies 32-bit integers x and y, producing a 64-bit + /// result. The 32 least-significant bits are returned in lsb. + /// The 32 most-significant bits are returned in msb. + /// + /// @tparam L An integer between 1 and 4 included that qualify the dimension of the vector. + /// + /// @see GLSL imulExtended man page + /// @see GLSL 4.20.8 specification, section 8.8 Integer Functions + template + GLM_FUNC_DECL void imulExtended( + vec const& x, + vec const& y, + vec & msb, + vec & lsb); + + /// Extracts bits [offset, offset + bits - 1] from value, + /// returning them in the least significant bits of the result. + /// For unsigned data types, the most significant bits of the + /// result will be set to zero. For signed data types, the + /// most significant bits will be set to the value of bit offset + base - 1. + /// + /// If bits is zero, the result will be zero. The result will be + /// undefined if offset or bits is negative, or if the sum of + /// offset and bits is greater than the number of bits used + /// to store the operand. + /// + /// @tparam L An integer between 1 and 4 included that qualify the dimension of the vector. + /// @tparam T Signed or unsigned integer scalar types. + /// + /// @see GLSL bitfieldExtract man page + /// @see GLSL 4.20.8 specification, section 8.8 Integer Functions + template + GLM_FUNC_DECL vec bitfieldExtract( + vec const& Value, + int Offset, + int Bits); + + /// Returns the insertion the bits least-significant bits of insert into base. + /// + /// The result will have bits [offset, offset + bits - 1] taken + /// from bits [0, bits - 1] of insert, and all other bits taken + /// directly from the corresponding bits of base. If bits is + /// zero, the result will simply be base. The result will be + /// undefined if offset or bits is negative, or if the sum of + /// offset and bits is greater than the number of bits used to + /// store the operand. + /// + /// @tparam L An integer between 1 and 4 included that qualify the dimension of the vector. + /// @tparam T Signed or unsigned integer scalar or vector types. + /// + /// @see GLSL bitfieldInsert man page + /// @see GLSL 4.20.8 specification, section 8.8 Integer Functions + template + GLM_FUNC_DECL vec bitfieldInsert( + vec const& Base, + vec const& Insert, + int Offset, + int Bits); + + /// Returns the reversal of the bits of value. + /// The bit numbered n of the result will be taken from bit (bits - 1) - n of value, + /// where bits is the total number of bits used to represent value. + /// + /// @tparam L An integer between 1 and 4 included that qualify the dimension of the vector. + /// @tparam T Signed or unsigned integer scalar or vector types. + /// + /// @see GLSL bitfieldReverse man page + /// @see GLSL 4.20.8 specification, section 8.8 Integer Functions + template + GLM_FUNC_DECL vec bitfieldReverse(vec const& v); + + /// Returns the number of bits set to 1 in the binary representation of value. + /// + /// @tparam genType Signed or unsigned integer scalar or vector types. + /// + /// @see GLSL bitCount man page + /// @see GLSL 4.20.8 specification, section 8.8 Integer Functions + template + GLM_FUNC_DECL int bitCount(genType v); + + /// Returns the number of bits set to 1 in the binary representation of value. + /// + /// @tparam L An integer between 1 and 4 included that qualify the dimension of the vector. + /// @tparam T Signed or unsigned integer scalar or vector types. + /// + /// @see GLSL bitCount man page + /// @see GLSL 4.20.8 specification, section 8.8 Integer Functions + template + GLM_FUNC_DECL vec bitCount(vec const& v); + + /// Returns the bit number of the least significant bit set to + /// 1 in the binary representation of value. + /// If value is zero, -1 will be returned. + /// + /// @tparam genIUType Signed or unsigned integer scalar types. + /// + /// @see GLSL findLSB man page + /// @see GLSL 4.20.8 specification, section 8.8 Integer Functions + template + GLM_FUNC_DECL int findLSB(genIUType x); + + /// Returns the bit number of the least significant bit set to + /// 1 in the binary representation of value. + /// If value is zero, -1 will be returned. + /// + /// @tparam L An integer between 1 and 4 included that qualify the dimension of the vector. + /// @tparam T Signed or unsigned integer scalar types. + /// + /// @see GLSL findLSB man page + /// @see GLSL 4.20.8 specification, section 8.8 Integer Functions + template + GLM_FUNC_DECL vec findLSB(vec const& v); + + /// Returns the bit number of the most significant bit in the binary representation of value. + /// For positive integers, the result will be the bit number of the most significant bit set to 1. + /// For negative integers, the result will be the bit number of the most significant + /// bit set to 0. For a value of zero or negative one, -1 will be returned. + /// + /// @tparam genIUType Signed or unsigned integer scalar types. + /// + /// @see GLSL findMSB man page + /// @see GLSL 4.20.8 specification, section 8.8 Integer Functions + template + GLM_FUNC_DECL int findMSB(genIUType x); + + /// Returns the bit number of the most significant bit in the binary representation of value. + /// For positive integers, the result will be the bit number of the most significant bit set to 1. + /// For negative integers, the result will be the bit number of the most significant + /// bit set to 0. For a value of zero or negative one, -1 will be returned. + /// + /// @tparam L An integer between 1 and 4 included that qualify the dimension of the vector. + /// @tparam T Signed or unsigned integer scalar types. + /// + /// @see GLSL findMSB man page + /// @see GLSL 4.20.8 specification, section 8.8 Integer Functions + template + GLM_FUNC_DECL vec findMSB(vec const& v); + + /// @} +}//namespace glm + +#include "detail/func_integer.inl" diff --git a/src/GLMath/glm/mat2x2.hpp b/src/GLMath/glm/mat2x2.hpp new file mode 100644 index 000000000..96bec96b9 --- /dev/null +++ b/src/GLMath/glm/mat2x2.hpp @@ -0,0 +1,9 @@ +/// @ref core +/// @file glm/mat2x2.hpp + +#pragma once +#include "./ext/matrix_double2x2.hpp" +#include "./ext/matrix_double2x2_precision.hpp" +#include "./ext/matrix_float2x2.hpp" +#include "./ext/matrix_float2x2_precision.hpp" + diff --git a/src/GLMath/glm/mat2x3.hpp b/src/GLMath/glm/mat2x3.hpp new file mode 100644 index 000000000..d68dc25ed --- /dev/null +++ b/src/GLMath/glm/mat2x3.hpp @@ -0,0 +1,9 @@ +/// @ref core +/// @file glm/mat2x3.hpp + +#pragma once +#include "./ext/matrix_double2x3.hpp" +#include "./ext/matrix_double2x3_precision.hpp" +#include "./ext/matrix_float2x3.hpp" +#include "./ext/matrix_float2x3_precision.hpp" + diff --git a/src/GLMath/glm/mat2x4.hpp b/src/GLMath/glm/mat2x4.hpp new file mode 100644 index 000000000..b04b7387b --- /dev/null +++ b/src/GLMath/glm/mat2x4.hpp @@ -0,0 +1,9 @@ +/// @ref core +/// @file glm/mat2x4.hpp + +#pragma once +#include "./ext/matrix_double2x4.hpp" +#include "./ext/matrix_double2x4_precision.hpp" +#include "./ext/matrix_float2x4.hpp" +#include "./ext/matrix_float2x4_precision.hpp" + diff --git a/src/GLMath/glm/mat3x2.hpp b/src/GLMath/glm/mat3x2.hpp new file mode 100644 index 000000000..c85315372 --- /dev/null +++ b/src/GLMath/glm/mat3x2.hpp @@ -0,0 +1,9 @@ +/// @ref core +/// @file glm/mat3x2.hpp + +#pragma once +#include "./ext/matrix_double3x2.hpp" +#include "./ext/matrix_double3x2_precision.hpp" +#include "./ext/matrix_float3x2.hpp" +#include "./ext/matrix_float3x2_precision.hpp" + diff --git a/src/GLMath/glm/mat3x3.hpp b/src/GLMath/glm/mat3x3.hpp new file mode 100644 index 000000000..fd4fa31cd --- /dev/null +++ b/src/GLMath/glm/mat3x3.hpp @@ -0,0 +1,8 @@ +/// @ref core +/// @file glm/mat3x3.hpp + +#pragma once +#include "./ext/matrix_double3x3.hpp" +#include "./ext/matrix_double3x3_precision.hpp" +#include "./ext/matrix_float3x3.hpp" +#include "./ext/matrix_float3x3_precision.hpp" diff --git a/src/GLMath/glm/mat3x4.hpp b/src/GLMath/glm/mat3x4.hpp new file mode 100644 index 000000000..6342bf5b9 --- /dev/null +++ b/src/GLMath/glm/mat3x4.hpp @@ -0,0 +1,8 @@ +/// @ref core +/// @file glm/mat3x4.hpp + +#pragma once +#include "./ext/matrix_double3x4.hpp" +#include "./ext/matrix_double3x4_precision.hpp" +#include "./ext/matrix_float3x4.hpp" +#include "./ext/matrix_float3x4_precision.hpp" diff --git a/src/GLMath/glm/mat4x2.hpp b/src/GLMath/glm/mat4x2.hpp new file mode 100644 index 000000000..e013e46b9 --- /dev/null +++ b/src/GLMath/glm/mat4x2.hpp @@ -0,0 +1,9 @@ +/// @ref core +/// @file glm/mat4x2.hpp + +#pragma once +#include "./ext/matrix_double4x2.hpp" +#include "./ext/matrix_double4x2_precision.hpp" +#include "./ext/matrix_float4x2.hpp" +#include "./ext/matrix_float4x2_precision.hpp" + diff --git a/src/GLMath/glm/mat4x3.hpp b/src/GLMath/glm/mat4x3.hpp new file mode 100644 index 000000000..205725abd --- /dev/null +++ b/src/GLMath/glm/mat4x3.hpp @@ -0,0 +1,8 @@ +/// @ref core +/// @file glm/mat4x3.hpp + +#pragma once +#include "./ext/matrix_double4x3.hpp" +#include "./ext/matrix_double4x3_precision.hpp" +#include "./ext/matrix_float4x3.hpp" +#include "./ext/matrix_float4x3_precision.hpp" diff --git a/src/GLMath/glm/mat4x4.hpp b/src/GLMath/glm/mat4x4.hpp new file mode 100644 index 000000000..3515f7f37 --- /dev/null +++ b/src/GLMath/glm/mat4x4.hpp @@ -0,0 +1,9 @@ +/// @ref core +/// @file glm/mat4x4.hpp + +#pragma once +#include "./ext/matrix_double4x4.hpp" +#include "./ext/matrix_double4x4_precision.hpp" +#include "./ext/matrix_float4x4.hpp" +#include "./ext/matrix_float4x4_precision.hpp" + diff --git a/src/GLMath/glm/matrix.hpp b/src/GLMath/glm/matrix.hpp new file mode 100644 index 000000000..6badf5385 --- /dev/null +++ b/src/GLMath/glm/matrix.hpp @@ -0,0 +1,161 @@ +/// @ref core +/// @file glm/matrix.hpp +/// +/// @see GLSL 4.20.8 specification, section 8.6 Matrix Functions +/// +/// @defgroup core_func_matrix Matrix functions +/// @ingroup core +/// +/// Provides GLSL matrix functions. +/// +/// Include to use these core features. + +#pragma once + +// Dependencies +#include "detail/qualifier.hpp" +#include "detail/setup.hpp" +#include "vec2.hpp" +#include "vec3.hpp" +#include "vec4.hpp" +#include "mat2x2.hpp" +#include "mat2x3.hpp" +#include "mat2x4.hpp" +#include "mat3x2.hpp" +#include "mat3x3.hpp" +#include "mat3x4.hpp" +#include "mat4x2.hpp" +#include "mat4x3.hpp" +#include "mat4x4.hpp" + +namespace glm { +namespace detail +{ + template + struct outerProduct_trait{}; + + template + struct outerProduct_trait<2, 2, T, Q> + { + typedef mat<2, 2, T, Q> type; + }; + + template + struct outerProduct_trait<2, 3, T, Q> + { + typedef mat<3, 2, T, Q> type; + }; + + template + struct outerProduct_trait<2, 4, T, Q> + { + typedef mat<4, 2, T, Q> type; + }; + + template + struct outerProduct_trait<3, 2, T, Q> + { + typedef mat<2, 3, T, Q> type; + }; + + template + struct outerProduct_trait<3, 3, T, Q> + { + typedef mat<3, 3, T, Q> type; + }; + + template + struct outerProduct_trait<3, 4, T, Q> + { + typedef mat<4, 3, T, Q> type; + }; + + template + struct outerProduct_trait<4, 2, T, Q> + { + typedef mat<2, 4, T, Q> type; + }; + + template + struct outerProduct_trait<4, 3, T, Q> + { + typedef mat<3, 4, T, Q> type; + }; + + template + struct outerProduct_trait<4, 4, T, Q> + { + typedef mat<4, 4, T, Q> type; + }; +}//namespace detail + + /// @addtogroup core_func_matrix + /// @{ + + /// Multiply matrix x by matrix y component-wise, i.e., + /// result[i][j] is the scalar product of x[i][j] and y[i][j]. + /// + /// @tparam C Integer between 1 and 4 included that qualify the number a column + /// @tparam R Integer between 1 and 4 included that qualify the number a row + /// @tparam T Floating-point or signed integer scalar types + /// @tparam Q Value from qualifier enum + /// + /// @see GLSL matrixCompMult man page + /// @see GLSL 4.20.8 specification, section 8.6 Matrix Functions + template + GLM_FUNC_DECL mat matrixCompMult(mat const& x, mat const& y); + + /// Treats the first parameter c as a column vector + /// and the second parameter r as a row vector + /// and does a linear algebraic matrix multiply c * r. + /// + /// @tparam C Integer between 1 and 4 included that qualify the number a column + /// @tparam R Integer between 1 and 4 included that qualify the number a row + /// @tparam T Floating-point or signed integer scalar types + /// @tparam Q Value from qualifier enum + /// + /// @see GLSL outerProduct man page + /// @see GLSL 4.20.8 specification, section 8.6 Matrix Functions + template + GLM_FUNC_DECL typename detail::outerProduct_trait::type outerProduct(vec const& c, vec const& r); + + /// Returns the transposed matrix of x + /// + /// @tparam C Integer between 1 and 4 included that qualify the number a column + /// @tparam R Integer between 1 and 4 included that qualify the number a row + /// @tparam T Floating-point or signed integer scalar types + /// @tparam Q Value from qualifier enum + /// + /// @see GLSL transpose man page + /// @see GLSL 4.20.8 specification, section 8.6 Matrix Functions + template + GLM_FUNC_DECL typename mat::transpose_type transpose(mat const& x); + + /// Return the determinant of a squared matrix. + /// + /// @tparam C Integer between 1 and 4 included that qualify the number a column + /// @tparam R Integer between 1 and 4 included that qualify the number a row + /// @tparam T Floating-point or signed integer scalar types + /// @tparam Q Value from qualifier enum + /// + /// @see GLSL determinant man page + /// @see GLSL 4.20.8 specification, section 8.6 Matrix Functions + template + GLM_FUNC_DECL T determinant(mat const& m); + + /// Return the inverse of a squared matrix. + /// + /// @tparam C Integer between 1 and 4 included that qualify the number a column + /// @tparam R Integer between 1 and 4 included that qualify the number a row + /// @tparam T Floating-point or signed integer scalar types + /// @tparam Q Value from qualifier enum + /// + /// @see GLSL inverse man page + /// @see GLSL 4.20.8 specification, section 8.6 Matrix Functions + template + GLM_FUNC_DECL mat inverse(mat const& m); + + /// @} +}//namespace glm + +#include "detail/func_matrix.inl" diff --git a/src/GLMath/glm/packing.hpp b/src/GLMath/glm/packing.hpp new file mode 100644 index 000000000..ca83ac1de --- /dev/null +++ b/src/GLMath/glm/packing.hpp @@ -0,0 +1,173 @@ +/// @ref core +/// @file glm/packing.hpp +/// +/// @see GLSL 4.20.8 specification, section 8.4 Floating-Point Pack and Unpack Functions +/// @see gtc_packing +/// +/// @defgroup core_func_packing Floating-Point Pack and Unpack Functions +/// @ingroup core +/// +/// Provides GLSL functions to pack and unpack half, single and double-precision floating point values into more compact integer types. +/// +/// These functions do not operate component-wise, rather as described in each case. +/// +/// Include to use these core features. + +#pragma once + +#include "./ext/vector_uint2.hpp" +#include "./ext/vector_float2.hpp" +#include "./ext/vector_float4.hpp" + +namespace glm +{ + /// @addtogroup core_func_packing + /// @{ + + /// First, converts each component of the normalized floating-point value v into 8- or 16-bit integer values. + /// Then, the results are packed into the returned 32-bit unsigned integer. + /// + /// The conversion for component c of v to fixed point is done as follows: + /// packUnorm2x16: round(clamp(c, 0, +1) * 65535.0) + /// + /// The first component of the vector will be written to the least significant bits of the output; + /// the last component will be written to the most significant bits. + /// + /// @see GLSL packUnorm2x16 man page + /// @see GLSL 4.20.8 specification, section 8.4 Floating-Point Pack and Unpack Functions + GLM_FUNC_DECL uint packUnorm2x16(vec2 const& v); + + /// First, converts each component of the normalized floating-point value v into 8- or 16-bit integer values. + /// Then, the results are packed into the returned 32-bit unsigned integer. + /// + /// The conversion for component c of v to fixed point is done as follows: + /// packSnorm2x16: round(clamp(v, -1, +1) * 32767.0) + /// + /// The first component of the vector will be written to the least significant bits of the output; + /// the last component will be written to the most significant bits. + /// + /// @see GLSL packSnorm2x16 man page + /// @see GLSL 4.20.8 specification, section 8.4 Floating-Point Pack and Unpack Functions + GLM_FUNC_DECL uint packSnorm2x16(vec2 const& v); + + /// First, converts each component of the normalized floating-point value v into 8- or 16-bit integer values. + /// Then, the results are packed into the returned 32-bit unsigned integer. + /// + /// The conversion for component c of v to fixed point is done as follows: + /// packUnorm4x8: round(clamp(c, 0, +1) * 255.0) + /// + /// The first component of the vector will be written to the least significant bits of the output; + /// the last component will be written to the most significant bits. + /// + /// @see GLSL packUnorm4x8 man page + /// @see GLSL 4.20.8 specification, section 8.4 Floating-Point Pack and Unpack Functions + GLM_FUNC_DECL uint packUnorm4x8(vec4 const& v); + + /// First, converts each component of the normalized floating-point value v into 8- or 16-bit integer values. + /// Then, the results are packed into the returned 32-bit unsigned integer. + /// + /// The conversion for component c of v to fixed point is done as follows: + /// packSnorm4x8: round(clamp(c, -1, +1) * 127.0) + /// + /// The first component of the vector will be written to the least significant bits of the output; + /// the last component will be written to the most significant bits. + /// + /// @see GLSL packSnorm4x8 man page + /// @see GLSL 4.20.8 specification, section 8.4 Floating-Point Pack and Unpack Functions + GLM_FUNC_DECL uint packSnorm4x8(vec4 const& v); + + /// First, unpacks a single 32-bit unsigned integer p into a pair of 16-bit unsigned integers, four 8-bit unsigned integers, or four 8-bit signed integers. + /// Then, each component is converted to a normalized floating-point value to generate the returned two- or four-component vector. + /// + /// The conversion for unpacked fixed-point value f to floating point is done as follows: + /// unpackUnorm2x16: f / 65535.0 + /// + /// The first component of the returned vector will be extracted from the least significant bits of the input; + /// the last component will be extracted from the most significant bits. + /// + /// @see GLSL unpackUnorm2x16 man page + /// @see GLSL 4.20.8 specification, section 8.4 Floating-Point Pack and Unpack Functions + GLM_FUNC_DECL vec2 unpackUnorm2x16(uint p); + + /// First, unpacks a single 32-bit unsigned integer p into a pair of 16-bit unsigned integers, four 8-bit unsigned integers, or four 8-bit signed integers. + /// Then, each component is converted to a normalized floating-point value to generate the returned two- or four-component vector. + /// + /// The conversion for unpacked fixed-point value f to floating point is done as follows: + /// unpackSnorm2x16: clamp(f / 32767.0, -1, +1) + /// + /// The first component of the returned vector will be extracted from the least significant bits of the input; + /// the last component will be extracted from the most significant bits. + /// + /// @see GLSL unpackSnorm2x16 man page + /// @see GLSL 4.20.8 specification, section 8.4 Floating-Point Pack and Unpack Functions + GLM_FUNC_DECL vec2 unpackSnorm2x16(uint p); + + /// First, unpacks a single 32-bit unsigned integer p into a pair of 16-bit unsigned integers, four 8-bit unsigned integers, or four 8-bit signed integers. + /// Then, each component is converted to a normalized floating-point value to generate the returned two- or four-component vector. + /// + /// The conversion for unpacked fixed-point value f to floating point is done as follows: + /// unpackUnorm4x8: f / 255.0 + /// + /// The first component of the returned vector will be extracted from the least significant bits of the input; + /// the last component will be extracted from the most significant bits. + /// + /// @see GLSL unpackUnorm4x8 man page + /// @see GLSL 4.20.8 specification, section 8.4 Floating-Point Pack and Unpack Functions + GLM_FUNC_DECL vec4 unpackUnorm4x8(uint p); + + /// First, unpacks a single 32-bit unsigned integer p into a pair of 16-bit unsigned integers, four 8-bit unsigned integers, or four 8-bit signed integers. + /// Then, each component is converted to a normalized floating-point value to generate the returned two- or four-component vector. + /// + /// The conversion for unpacked fixed-point value f to floating point is done as follows: + /// unpackSnorm4x8: clamp(f / 127.0, -1, +1) + /// + /// The first component of the returned vector will be extracted from the least significant bits of the input; + /// the last component will be extracted from the most significant bits. + /// + /// @see GLSL unpackSnorm4x8 man page + /// @see GLSL 4.20.8 specification, section 8.4 Floating-Point Pack and Unpack Functions + GLM_FUNC_DECL vec4 unpackSnorm4x8(uint p); + + /// Returns a double-qualifier value obtained by packing the components of v into a 64-bit value. + /// If an IEEE 754 Inf or NaN is created, it will not signal, and the resulting floating point value is unspecified. + /// Otherwise, the bit- level representation of v is preserved. + /// The first vector component specifies the 32 least significant bits; + /// the second component specifies the 32 most significant bits. + /// + /// @see GLSL packDouble2x32 man page + /// @see GLSL 4.20.8 specification, section 8.4 Floating-Point Pack and Unpack Functions + GLM_FUNC_DECL double packDouble2x32(uvec2 const& v); + + /// Returns a two-component unsigned integer vector representation of v. + /// The bit-level representation of v is preserved. + /// The first component of the vector contains the 32 least significant bits of the double; + /// the second component consists the 32 most significant bits. + /// + /// @see GLSL unpackDouble2x32 man page + /// @see GLSL 4.20.8 specification, section 8.4 Floating-Point Pack and Unpack Functions + GLM_FUNC_DECL uvec2 unpackDouble2x32(double v); + + /// Returns an unsigned integer obtained by converting the components of a two-component floating-point vector + /// to the 16-bit floating-point representation found in the OpenGL Specification, + /// and then packing these two 16- bit integers into a 32-bit unsigned integer. + /// The first vector component specifies the 16 least-significant bits of the result; + /// the second component specifies the 16 most-significant bits. + /// + /// @see GLSL packHalf2x16 man page + /// @see GLSL 4.20.8 specification, section 8.4 Floating-Point Pack and Unpack Functions + GLM_FUNC_DECL uint packHalf2x16(vec2 const& v); + + /// Returns a two-component floating-point vector with components obtained by unpacking a 32-bit unsigned integer into a pair of 16-bit values, + /// interpreting those values as 16-bit floating-point numbers according to the OpenGL Specification, + /// and converting them to 32-bit floating-point values. + /// The first component of the vector is obtained from the 16 least-significant bits of v; + /// the second component is obtained from the 16 most-significant bits of v. + /// + /// @see GLSL unpackHalf2x16 man page + /// @see GLSL 4.20.8 specification, section 8.4 Floating-Point Pack and Unpack Functions + GLM_FUNC_DECL vec2 unpackHalf2x16(uint v); + + /// @} +}//namespace glm + +#include "detail/func_packing.inl" diff --git a/src/GLMath/glm/simd/common.h b/src/GLMath/glm/simd/common.h new file mode 100644 index 000000000..9b017cb42 --- /dev/null +++ b/src/GLMath/glm/simd/common.h @@ -0,0 +1,240 @@ +/// @ref simd +/// @file glm/simd/common.h + +#pragma once + +#include "platform.h" + +#if GLM_ARCH & GLM_ARCH_SSE2_BIT + +GLM_FUNC_QUALIFIER glm_f32vec4 glm_vec4_add(glm_f32vec4 a, glm_f32vec4 b) +{ + return _mm_add_ps(a, b); +} + +GLM_FUNC_QUALIFIER glm_f32vec4 glm_vec1_add(glm_f32vec4 a, glm_f32vec4 b) +{ + return _mm_add_ss(a, b); +} + +GLM_FUNC_QUALIFIER glm_f32vec4 glm_vec4_sub(glm_f32vec4 a, glm_f32vec4 b) +{ + return _mm_sub_ps(a, b); +} + +GLM_FUNC_QUALIFIER glm_f32vec4 glm_vec1_sub(glm_f32vec4 a, glm_f32vec4 b) +{ + return _mm_sub_ss(a, b); +} + +GLM_FUNC_QUALIFIER glm_f32vec4 glm_vec4_mul(glm_f32vec4 a, glm_f32vec4 b) +{ + return _mm_mul_ps(a, b); +} + +GLM_FUNC_QUALIFIER glm_f32vec4 glm_vec1_mul(glm_f32vec4 a, glm_f32vec4 b) +{ + return _mm_mul_ss(a, b); +} + +GLM_FUNC_QUALIFIER glm_f32vec4 glm_vec4_div(glm_f32vec4 a, glm_f32vec4 b) +{ + return _mm_div_ps(a, b); +} + +GLM_FUNC_QUALIFIER glm_f32vec4 glm_vec1_div(glm_f32vec4 a, glm_f32vec4 b) +{ + return _mm_div_ss(a, b); +} + +GLM_FUNC_QUALIFIER glm_f32vec4 glm_vec4_div_lowp(glm_f32vec4 a, glm_f32vec4 b) +{ + return glm_vec4_mul(a, _mm_rcp_ps(b)); +} + +GLM_FUNC_QUALIFIER glm_f32vec4 glm_vec4_swizzle_xyzw(glm_f32vec4 a) +{ +# if GLM_ARCH & GLM_ARCH_AVX2_BIT + return _mm_permute_ps(a, _MM_SHUFFLE(3, 2, 1, 0)); +# else + return _mm_shuffle_ps(a, a, _MM_SHUFFLE(3, 2, 1, 0)); +# endif +} + +GLM_FUNC_QUALIFIER glm_f32vec4 glm_vec1_fma(glm_f32vec4 a, glm_f32vec4 b, glm_f32vec4 c) +{ +# if (GLM_ARCH & GLM_ARCH_AVX2_BIT) && !(GLM_COMPILER & GLM_COMPILER_CLANG) + return _mm_fmadd_ss(a, b, c); +# else + return _mm_add_ss(_mm_mul_ss(a, b), c); +# endif +} + +GLM_FUNC_QUALIFIER glm_f32vec4 glm_vec4_fma(glm_f32vec4 a, glm_f32vec4 b, glm_f32vec4 c) +{ +# if (GLM_ARCH & GLM_ARCH_AVX2_BIT) && !(GLM_COMPILER & GLM_COMPILER_CLANG) + return _mm_fmadd_ps(a, b, c); +# else + return glm_vec4_add(glm_vec4_mul(a, b), c); +# endif +} + +GLM_FUNC_QUALIFIER glm_f32vec4 glm_vec4_abs(glm_f32vec4 x) +{ + return _mm_and_ps(x, _mm_castsi128_ps(_mm_set1_epi32(0x7FFFFFFF))); +} + +GLM_FUNC_QUALIFIER glm_ivec4 glm_ivec4_abs(glm_ivec4 x) +{ +# if GLM_ARCH & GLM_ARCH_SSSE3_BIT + return _mm_sign_epi32(x, x); +# else + glm_ivec4 const sgn0 = _mm_srai_epi32(x, 31); + glm_ivec4 const inv0 = _mm_xor_si128(x, sgn0); + glm_ivec4 const sub0 = _mm_sub_epi32(inv0, sgn0); + return sub0; +# endif +} + +GLM_FUNC_QUALIFIER glm_vec4 glm_vec4_sign(glm_vec4 x) +{ + glm_vec4 const zro0 = _mm_setzero_ps(); + glm_vec4 const cmp0 = _mm_cmplt_ps(x, zro0); + glm_vec4 const cmp1 = _mm_cmpgt_ps(x, zro0); + glm_vec4 const and0 = _mm_and_ps(cmp0, _mm_set1_ps(-1.0f)); + glm_vec4 const and1 = _mm_and_ps(cmp1, _mm_set1_ps(1.0f)); + glm_vec4 const or0 = _mm_or_ps(and0, and1); + return or0; +} + +GLM_FUNC_QUALIFIER glm_vec4 glm_vec4_round(glm_vec4 x) +{ +# if GLM_ARCH & GLM_ARCH_SSE41_BIT + return _mm_round_ps(x, _MM_FROUND_TO_NEAREST_INT); +# else + glm_vec4 const sgn0 = _mm_castsi128_ps(_mm_set1_epi32(int(0x80000000))); + glm_vec4 const and0 = _mm_and_ps(sgn0, x); + glm_vec4 const or0 = _mm_or_ps(and0, _mm_set_ps1(8388608.0f)); + glm_vec4 const add0 = glm_vec4_add(x, or0); + glm_vec4 const sub0 = glm_vec4_sub(add0, or0); + return sub0; +# endif +} + +GLM_FUNC_QUALIFIER glm_vec4 glm_vec4_floor(glm_vec4 x) +{ +# if GLM_ARCH & GLM_ARCH_SSE41_BIT + return _mm_floor_ps(x); +# else + glm_vec4 const rnd0 = glm_vec4_round(x); + glm_vec4 const cmp0 = _mm_cmplt_ps(x, rnd0); + glm_vec4 const and0 = _mm_and_ps(cmp0, _mm_set1_ps(1.0f)); + glm_vec4 const sub0 = glm_vec4_sub(rnd0, and0); + return sub0; +# endif +} + +/* trunc TODO +GLM_FUNC_QUALIFIER glm_vec4 glm_vec4_trunc(glm_vec4 x) +{ + return glm_vec4(); +} +*/ + +//roundEven +GLM_FUNC_QUALIFIER glm_vec4 glm_vec4_roundEven(glm_vec4 x) +{ + glm_vec4 const sgn0 = _mm_castsi128_ps(_mm_set1_epi32(int(0x80000000))); + glm_vec4 const and0 = _mm_and_ps(sgn0, x); + glm_vec4 const or0 = _mm_or_ps(and0, _mm_set_ps1(8388608.0f)); + glm_vec4 const add0 = glm_vec4_add(x, or0); + glm_vec4 const sub0 = glm_vec4_sub(add0, or0); + return sub0; +} + +GLM_FUNC_QUALIFIER glm_vec4 glm_vec4_ceil(glm_vec4 x) +{ +# if GLM_ARCH & GLM_ARCH_SSE41_BIT + return _mm_ceil_ps(x); +# else + glm_vec4 const rnd0 = glm_vec4_round(x); + glm_vec4 const cmp0 = _mm_cmpgt_ps(x, rnd0); + glm_vec4 const and0 = _mm_and_ps(cmp0, _mm_set1_ps(1.0f)); + glm_vec4 const add0 = glm_vec4_add(rnd0, and0); + return add0; +# endif +} + +GLM_FUNC_QUALIFIER glm_vec4 glm_vec4_fract(glm_vec4 x) +{ + glm_vec4 const flr0 = glm_vec4_floor(x); + glm_vec4 const sub0 = glm_vec4_sub(x, flr0); + return sub0; +} + +GLM_FUNC_QUALIFIER glm_vec4 glm_vec4_mod(glm_vec4 x, glm_vec4 y) +{ + glm_vec4 const div0 = glm_vec4_div(x, y); + glm_vec4 const flr0 = glm_vec4_floor(div0); + glm_vec4 const mul0 = glm_vec4_mul(y, flr0); + glm_vec4 const sub0 = glm_vec4_sub(x, mul0); + return sub0; +} + +GLM_FUNC_QUALIFIER glm_vec4 glm_vec4_clamp(glm_vec4 v, glm_vec4 minVal, glm_vec4 maxVal) +{ + glm_vec4 const min0 = _mm_min_ps(v, maxVal); + glm_vec4 const max0 = _mm_max_ps(min0, minVal); + return max0; +} + +GLM_FUNC_QUALIFIER glm_vec4 glm_vec4_mix(glm_vec4 v1, glm_vec4 v2, glm_vec4 a) +{ + glm_vec4 const sub0 = glm_vec4_sub(_mm_set1_ps(1.0f), a); + glm_vec4 const mul0 = glm_vec4_mul(v1, sub0); + glm_vec4 const mad0 = glm_vec4_fma(v2, a, mul0); + return mad0; +} + +GLM_FUNC_QUALIFIER glm_vec4 glm_vec4_step(glm_vec4 edge, glm_vec4 x) +{ + glm_vec4 const cmp = _mm_cmple_ps(x, edge); + return _mm_movemask_ps(cmp) == 0 ? _mm_set1_ps(1.0f) : _mm_setzero_ps(); +} + +GLM_FUNC_QUALIFIER glm_vec4 glm_vec4_smoothstep(glm_vec4 edge0, glm_vec4 edge1, glm_vec4 x) +{ + glm_vec4 const sub0 = glm_vec4_sub(x, edge0); + glm_vec4 const sub1 = glm_vec4_sub(edge1, edge0); + glm_vec4 const div0 = glm_vec4_sub(sub0, sub1); + glm_vec4 const clp0 = glm_vec4_clamp(div0, _mm_setzero_ps(), _mm_set1_ps(1.0f)); + glm_vec4 const mul0 = glm_vec4_mul(_mm_set1_ps(2.0f), clp0); + glm_vec4 const sub2 = glm_vec4_sub(_mm_set1_ps(3.0f), mul0); + glm_vec4 const mul1 = glm_vec4_mul(clp0, clp0); + glm_vec4 const mul2 = glm_vec4_mul(mul1, sub2); + return mul2; +} + +// Agner Fog method +GLM_FUNC_QUALIFIER glm_vec4 glm_vec4_nan(glm_vec4 x) +{ + glm_ivec4 const t1 = _mm_castps_si128(x); // reinterpret as 32-bit integer + glm_ivec4 const t2 = _mm_sll_epi32(t1, _mm_cvtsi32_si128(1)); // shift out sign bit + glm_ivec4 const t3 = _mm_set1_epi32(int(0xFF000000)); // exponent mask + glm_ivec4 const t4 = _mm_and_si128(t2, t3); // exponent + glm_ivec4 const t5 = _mm_andnot_si128(t3, t2); // fraction + glm_ivec4 const Equal = _mm_cmpeq_epi32(t3, t4); + glm_ivec4 const Nequal = _mm_cmpeq_epi32(t5, _mm_setzero_si128()); + glm_ivec4 const And = _mm_and_si128(Equal, Nequal); + return _mm_castsi128_ps(And); // exponent = all 1s and fraction != 0 +} + +// Agner Fog method +GLM_FUNC_QUALIFIER glm_vec4 glm_vec4_inf(glm_vec4 x) +{ + glm_ivec4 const t1 = _mm_castps_si128(x); // reinterpret as 32-bit integer + glm_ivec4 const t2 = _mm_sll_epi32(t1, _mm_cvtsi32_si128(1)); // shift out sign bit + return _mm_castsi128_ps(_mm_cmpeq_epi32(t2, _mm_set1_epi32(int(0xFF000000)))); // exponent is all 1s, fraction is 0 +} + +#endif//GLM_ARCH & GLM_ARCH_SSE2_BIT diff --git a/src/GLMath/glm/simd/exponential.h b/src/GLMath/glm/simd/exponential.h new file mode 100644 index 000000000..bc351d011 --- /dev/null +++ b/src/GLMath/glm/simd/exponential.h @@ -0,0 +1,20 @@ +/// @ref simd +/// @file glm/simd/experimental.h + +#pragma once + +#include "platform.h" + +#if GLM_ARCH & GLM_ARCH_SSE2_BIT + +GLM_FUNC_QUALIFIER glm_f32vec4 glm_vec1_sqrt_lowp(glm_f32vec4 x) +{ + return _mm_mul_ss(_mm_rsqrt_ss(x), x); +} + +GLM_FUNC_QUALIFIER glm_f32vec4 glm_vec4_sqrt_lowp(glm_f32vec4 x) +{ + return _mm_mul_ps(_mm_rsqrt_ps(x), x); +} + +#endif//GLM_ARCH & GLM_ARCH_SSE2_BIT diff --git a/src/GLMath/glm/simd/geometric.h b/src/GLMath/glm/simd/geometric.h new file mode 100644 index 000000000..07d7cbcc4 --- /dev/null +++ b/src/GLMath/glm/simd/geometric.h @@ -0,0 +1,124 @@ +/// @ref simd +/// @file glm/simd/geometric.h + +#pragma once + +#include "common.h" + +#if GLM_ARCH & GLM_ARCH_SSE2_BIT + +GLM_FUNC_DECL glm_vec4 glm_vec4_dot(glm_vec4 v1, glm_vec4 v2); +GLM_FUNC_DECL glm_vec4 glm_vec1_dot(glm_vec4 v1, glm_vec4 v2); + +GLM_FUNC_QUALIFIER glm_vec4 glm_vec4_length(glm_vec4 x) +{ + glm_vec4 const dot0 = glm_vec4_dot(x, x); + glm_vec4 const sqt0 = _mm_sqrt_ps(dot0); + return sqt0; +} + +GLM_FUNC_QUALIFIER glm_vec4 glm_vec4_distance(glm_vec4 p0, glm_vec4 p1) +{ + glm_vec4 const sub0 = _mm_sub_ps(p0, p1); + glm_vec4 const len0 = glm_vec4_length(sub0); + return len0; +} + +GLM_FUNC_QUALIFIER glm_vec4 glm_vec4_dot(glm_vec4 v1, glm_vec4 v2) +{ +# if GLM_ARCH & GLM_ARCH_AVX_BIT + return _mm_dp_ps(v1, v2, 0xff); +# elif GLM_ARCH & GLM_ARCH_SSE3_BIT + glm_vec4 const mul0 = _mm_mul_ps(v1, v2); + glm_vec4 const hadd0 = _mm_hadd_ps(mul0, mul0); + glm_vec4 const hadd1 = _mm_hadd_ps(hadd0, hadd0); + return hadd1; +# else + glm_vec4 const mul0 = _mm_mul_ps(v1, v2); + glm_vec4 const swp0 = _mm_shuffle_ps(mul0, mul0, _MM_SHUFFLE(2, 3, 0, 1)); + glm_vec4 const add0 = _mm_add_ps(mul0, swp0); + glm_vec4 const swp1 = _mm_shuffle_ps(add0, add0, _MM_SHUFFLE(0, 1, 2, 3)); + glm_vec4 const add1 = _mm_add_ps(add0, swp1); + return add1; +# endif +} + +GLM_FUNC_QUALIFIER glm_vec4 glm_vec1_dot(glm_vec4 v1, glm_vec4 v2) +{ +# if GLM_ARCH & GLM_ARCH_AVX_BIT + return _mm_dp_ps(v1, v2, 0xff); +# elif GLM_ARCH & GLM_ARCH_SSE3_BIT + glm_vec4 const mul0 = _mm_mul_ps(v1, v2); + glm_vec4 const had0 = _mm_hadd_ps(mul0, mul0); + glm_vec4 const had1 = _mm_hadd_ps(had0, had0); + return had1; +# else + glm_vec4 const mul0 = _mm_mul_ps(v1, v2); + glm_vec4 const mov0 = _mm_movehl_ps(mul0, mul0); + glm_vec4 const add0 = _mm_add_ps(mov0, mul0); + glm_vec4 const swp1 = _mm_shuffle_ps(add0, add0, 1); + glm_vec4 const add1 = _mm_add_ss(add0, swp1); + return add1; +# endif +} + +GLM_FUNC_QUALIFIER glm_vec4 glm_vec4_cross(glm_vec4 v1, glm_vec4 v2) +{ + glm_vec4 const swp0 = _mm_shuffle_ps(v1, v1, _MM_SHUFFLE(3, 0, 2, 1)); + glm_vec4 const swp1 = _mm_shuffle_ps(v1, v1, _MM_SHUFFLE(3, 1, 0, 2)); + glm_vec4 const swp2 = _mm_shuffle_ps(v2, v2, _MM_SHUFFLE(3, 0, 2, 1)); + glm_vec4 const swp3 = _mm_shuffle_ps(v2, v2, _MM_SHUFFLE(3, 1, 0, 2)); + glm_vec4 const mul0 = _mm_mul_ps(swp0, swp3); + glm_vec4 const mul1 = _mm_mul_ps(swp1, swp2); + glm_vec4 const sub0 = _mm_sub_ps(mul0, mul1); + return sub0; +} + +GLM_FUNC_QUALIFIER glm_vec4 glm_vec4_normalize(glm_vec4 v) +{ + glm_vec4 const dot0 = glm_vec4_dot(v, v); + glm_vec4 const isr0 = _mm_rsqrt_ps(dot0); + glm_vec4 const mul0 = _mm_mul_ps(v, isr0); + return mul0; +} + +GLM_FUNC_QUALIFIER glm_vec4 glm_vec4_faceforward(glm_vec4 N, glm_vec4 I, glm_vec4 Nref) +{ + glm_vec4 const dot0 = glm_vec4_dot(Nref, I); + glm_vec4 const sgn0 = glm_vec4_sign(dot0); + glm_vec4 const mul0 = _mm_mul_ps(sgn0, _mm_set1_ps(-1.0f)); + glm_vec4 const mul1 = _mm_mul_ps(N, mul0); + return mul1; +} + +GLM_FUNC_QUALIFIER glm_vec4 glm_vec4_reflect(glm_vec4 I, glm_vec4 N) +{ + glm_vec4 const dot0 = glm_vec4_dot(N, I); + glm_vec4 const mul0 = _mm_mul_ps(N, dot0); + glm_vec4 const mul1 = _mm_mul_ps(mul0, _mm_set1_ps(2.0f)); + glm_vec4 const sub0 = _mm_sub_ps(I, mul1); + return sub0; +} + +GLM_FUNC_QUALIFIER __m128 glm_vec4_refract(glm_vec4 I, glm_vec4 N, glm_vec4 eta) +{ + glm_vec4 const dot0 = glm_vec4_dot(N, I); + glm_vec4 const mul0 = _mm_mul_ps(eta, eta); + glm_vec4 const mul1 = _mm_mul_ps(dot0, dot0); + glm_vec4 const sub0 = _mm_sub_ps(_mm_set1_ps(1.0f), mul0); + glm_vec4 const sub1 = _mm_sub_ps(_mm_set1_ps(1.0f), mul1); + glm_vec4 const mul2 = _mm_mul_ps(sub0, sub1); + + if(_mm_movemask_ps(_mm_cmplt_ss(mul2, _mm_set1_ps(0.0f))) == 0) + return _mm_set1_ps(0.0f); + + glm_vec4 const sqt0 = _mm_sqrt_ps(mul2); + glm_vec4 const mad0 = glm_vec4_fma(eta, dot0, sqt0); + glm_vec4 const mul4 = _mm_mul_ps(mad0, N); + glm_vec4 const mul5 = _mm_mul_ps(eta, I); + glm_vec4 const sub2 = _mm_sub_ps(mul5, mul4); + + return sub2; +} + +#endif//GLM_ARCH & GLM_ARCH_SSE2_BIT diff --git a/src/GLMath/glm/simd/integer.h b/src/GLMath/glm/simd/integer.h new file mode 100644 index 000000000..93814183f --- /dev/null +++ b/src/GLMath/glm/simd/integer.h @@ -0,0 +1,115 @@ +/// @ref simd +/// @file glm/simd/integer.h + +#pragma once + +#if GLM_ARCH & GLM_ARCH_SSE2_BIT + +GLM_FUNC_QUALIFIER glm_uvec4 glm_i128_interleave(glm_uvec4 x) +{ + glm_uvec4 const Mask4 = _mm_set1_epi32(0x0000FFFF); + glm_uvec4 const Mask3 = _mm_set1_epi32(0x00FF00FF); + glm_uvec4 const Mask2 = _mm_set1_epi32(0x0F0F0F0F); + glm_uvec4 const Mask1 = _mm_set1_epi32(0x33333333); + glm_uvec4 const Mask0 = _mm_set1_epi32(0x55555555); + + glm_uvec4 Reg1; + glm_uvec4 Reg2; + + // REG1 = x; + // REG2 = y; + //Reg1 = _mm_unpacklo_epi64(x, y); + Reg1 = x; + + //REG1 = ((REG1 << 16) | REG1) & glm::uint64(0x0000FFFF0000FFFF); + //REG2 = ((REG2 << 16) | REG2) & glm::uint64(0x0000FFFF0000FFFF); + Reg2 = _mm_slli_si128(Reg1, 2); + Reg1 = _mm_or_si128(Reg2, Reg1); + Reg1 = _mm_and_si128(Reg1, Mask4); + + //REG1 = ((REG1 << 8) | REG1) & glm::uint64(0x00FF00FF00FF00FF); + //REG2 = ((REG2 << 8) | REG2) & glm::uint64(0x00FF00FF00FF00FF); + Reg2 = _mm_slli_si128(Reg1, 1); + Reg1 = _mm_or_si128(Reg2, Reg1); + Reg1 = _mm_and_si128(Reg1, Mask3); + + //REG1 = ((REG1 << 4) | REG1) & glm::uint64(0x0F0F0F0F0F0F0F0F); + //REG2 = ((REG2 << 4) | REG2) & glm::uint64(0x0F0F0F0F0F0F0F0F); + Reg2 = _mm_slli_epi32(Reg1, 4); + Reg1 = _mm_or_si128(Reg2, Reg1); + Reg1 = _mm_and_si128(Reg1, Mask2); + + //REG1 = ((REG1 << 2) | REG1) & glm::uint64(0x3333333333333333); + //REG2 = ((REG2 << 2) | REG2) & glm::uint64(0x3333333333333333); + Reg2 = _mm_slli_epi32(Reg1, 2); + Reg1 = _mm_or_si128(Reg2, Reg1); + Reg1 = _mm_and_si128(Reg1, Mask1); + + //REG1 = ((REG1 << 1) | REG1) & glm::uint64(0x5555555555555555); + //REG2 = ((REG2 << 1) | REG2) & glm::uint64(0x5555555555555555); + Reg2 = _mm_slli_epi32(Reg1, 1); + Reg1 = _mm_or_si128(Reg2, Reg1); + Reg1 = _mm_and_si128(Reg1, Mask0); + + //return REG1 | (REG2 << 1); + Reg2 = _mm_slli_epi32(Reg1, 1); + Reg2 = _mm_srli_si128(Reg2, 8); + Reg1 = _mm_or_si128(Reg1, Reg2); + + return Reg1; +} + +GLM_FUNC_QUALIFIER glm_uvec4 glm_i128_interleave2(glm_uvec4 x, glm_uvec4 y) +{ + glm_uvec4 const Mask4 = _mm_set1_epi32(0x0000FFFF); + glm_uvec4 const Mask3 = _mm_set1_epi32(0x00FF00FF); + glm_uvec4 const Mask2 = _mm_set1_epi32(0x0F0F0F0F); + glm_uvec4 const Mask1 = _mm_set1_epi32(0x33333333); + glm_uvec4 const Mask0 = _mm_set1_epi32(0x55555555); + + glm_uvec4 Reg1; + glm_uvec4 Reg2; + + // REG1 = x; + // REG2 = y; + Reg1 = _mm_unpacklo_epi64(x, y); + + //REG1 = ((REG1 << 16) | REG1) & glm::uint64(0x0000FFFF0000FFFF); + //REG2 = ((REG2 << 16) | REG2) & glm::uint64(0x0000FFFF0000FFFF); + Reg2 = _mm_slli_si128(Reg1, 2); + Reg1 = _mm_or_si128(Reg2, Reg1); + Reg1 = _mm_and_si128(Reg1, Mask4); + + //REG1 = ((REG1 << 8) | REG1) & glm::uint64(0x00FF00FF00FF00FF); + //REG2 = ((REG2 << 8) | REG2) & glm::uint64(0x00FF00FF00FF00FF); + Reg2 = _mm_slli_si128(Reg1, 1); + Reg1 = _mm_or_si128(Reg2, Reg1); + Reg1 = _mm_and_si128(Reg1, Mask3); + + //REG1 = ((REG1 << 4) | REG1) & glm::uint64(0x0F0F0F0F0F0F0F0F); + //REG2 = ((REG2 << 4) | REG2) & glm::uint64(0x0F0F0F0F0F0F0F0F); + Reg2 = _mm_slli_epi32(Reg1, 4); + Reg1 = _mm_or_si128(Reg2, Reg1); + Reg1 = _mm_and_si128(Reg1, Mask2); + + //REG1 = ((REG1 << 2) | REG1) & glm::uint64(0x3333333333333333); + //REG2 = ((REG2 << 2) | REG2) & glm::uint64(0x3333333333333333); + Reg2 = _mm_slli_epi32(Reg1, 2); + Reg1 = _mm_or_si128(Reg2, Reg1); + Reg1 = _mm_and_si128(Reg1, Mask1); + + //REG1 = ((REG1 << 1) | REG1) & glm::uint64(0x5555555555555555); + //REG2 = ((REG2 << 1) | REG2) & glm::uint64(0x5555555555555555); + Reg2 = _mm_slli_epi32(Reg1, 1); + Reg1 = _mm_or_si128(Reg2, Reg1); + Reg1 = _mm_and_si128(Reg1, Mask0); + + //return REG1 | (REG2 << 1); + Reg2 = _mm_slli_epi32(Reg1, 1); + Reg2 = _mm_srli_si128(Reg2, 8); + Reg1 = _mm_or_si128(Reg1, Reg2); + + return Reg1; +} + +#endif//GLM_ARCH & GLM_ARCH_SSE2_BIT diff --git a/src/GLMath/glm/simd/matrix.h b/src/GLMath/glm/simd/matrix.h new file mode 100644 index 000000000..b6c42ea4c --- /dev/null +++ b/src/GLMath/glm/simd/matrix.h @@ -0,0 +1,1028 @@ +/// @ref simd +/// @file glm/simd/matrix.h + +#pragma once + +#include "geometric.h" + +#if GLM_ARCH & GLM_ARCH_SSE2_BIT + +GLM_FUNC_QUALIFIER void glm_mat4_matrixCompMult(glm_vec4 const in1[4], glm_vec4 const in2[4], glm_vec4 out[4]) +{ + out[0] = _mm_mul_ps(in1[0], in2[0]); + out[1] = _mm_mul_ps(in1[1], in2[1]); + out[2] = _mm_mul_ps(in1[2], in2[2]); + out[3] = _mm_mul_ps(in1[3], in2[3]); +} + +GLM_FUNC_QUALIFIER void glm_mat4_add(glm_vec4 const in1[4], glm_vec4 const in2[4], glm_vec4 out[4]) +{ + out[0] = _mm_add_ps(in1[0], in2[0]); + out[1] = _mm_add_ps(in1[1], in2[1]); + out[2] = _mm_add_ps(in1[2], in2[2]); + out[3] = _mm_add_ps(in1[3], in2[3]); +} + +GLM_FUNC_QUALIFIER void glm_mat4_sub(glm_vec4 const in1[4], glm_vec4 const in2[4], glm_vec4 out[4]) +{ + out[0] = _mm_sub_ps(in1[0], in2[0]); + out[1] = _mm_sub_ps(in1[1], in2[1]); + out[2] = _mm_sub_ps(in1[2], in2[2]); + out[3] = _mm_sub_ps(in1[3], in2[3]); +} + +GLM_FUNC_QUALIFIER glm_vec4 glm_mat4_mul_vec4(glm_vec4 const m[4], glm_vec4 v) +{ + __m128 v0 = _mm_shuffle_ps(v, v, _MM_SHUFFLE(0, 0, 0, 0)); + __m128 v1 = _mm_shuffle_ps(v, v, _MM_SHUFFLE(1, 1, 1, 1)); + __m128 v2 = _mm_shuffle_ps(v, v, _MM_SHUFFLE(2, 2, 2, 2)); + __m128 v3 = _mm_shuffle_ps(v, v, _MM_SHUFFLE(3, 3, 3, 3)); + + __m128 m0 = _mm_mul_ps(m[0], v0); + __m128 m1 = _mm_mul_ps(m[1], v1); + __m128 m2 = _mm_mul_ps(m[2], v2); + __m128 m3 = _mm_mul_ps(m[3], v3); + + __m128 a0 = _mm_add_ps(m0, m1); + __m128 a1 = _mm_add_ps(m2, m3); + __m128 a2 = _mm_add_ps(a0, a1); + + return a2; +} + +GLM_FUNC_QUALIFIER __m128 glm_vec4_mul_mat4(glm_vec4 v, glm_vec4 const m[4]) +{ + __m128 i0 = m[0]; + __m128 i1 = m[1]; + __m128 i2 = m[2]; + __m128 i3 = m[3]; + + __m128 m0 = _mm_mul_ps(v, i0); + __m128 m1 = _mm_mul_ps(v, i1); + __m128 m2 = _mm_mul_ps(v, i2); + __m128 m3 = _mm_mul_ps(v, i3); + + __m128 u0 = _mm_unpacklo_ps(m0, m1); + __m128 u1 = _mm_unpackhi_ps(m0, m1); + __m128 a0 = _mm_add_ps(u0, u1); + + __m128 u2 = _mm_unpacklo_ps(m2, m3); + __m128 u3 = _mm_unpackhi_ps(m2, m3); + __m128 a1 = _mm_add_ps(u2, u3); + + __m128 f0 = _mm_movelh_ps(a0, a1); + __m128 f1 = _mm_movehl_ps(a1, a0); + __m128 f2 = _mm_add_ps(f0, f1); + + return f2; +} + +GLM_FUNC_QUALIFIER void glm_mat4_mul(glm_vec4 const in1[4], glm_vec4 const in2[4], glm_vec4 out[4]) +{ + { + __m128 e0 = _mm_shuffle_ps(in2[0], in2[0], _MM_SHUFFLE(0, 0, 0, 0)); + __m128 e1 = _mm_shuffle_ps(in2[0], in2[0], _MM_SHUFFLE(1, 1, 1, 1)); + __m128 e2 = _mm_shuffle_ps(in2[0], in2[0], _MM_SHUFFLE(2, 2, 2, 2)); + __m128 e3 = _mm_shuffle_ps(in2[0], in2[0], _MM_SHUFFLE(3, 3, 3, 3)); + + __m128 m0 = _mm_mul_ps(in1[0], e0); + __m128 m1 = _mm_mul_ps(in1[1], e1); + __m128 m2 = _mm_mul_ps(in1[2], e2); + __m128 m3 = _mm_mul_ps(in1[3], e3); + + __m128 a0 = _mm_add_ps(m0, m1); + __m128 a1 = _mm_add_ps(m2, m3); + __m128 a2 = _mm_add_ps(a0, a1); + + out[0] = a2; + } + + { + __m128 e0 = _mm_shuffle_ps(in2[1], in2[1], _MM_SHUFFLE(0, 0, 0, 0)); + __m128 e1 = _mm_shuffle_ps(in2[1], in2[1], _MM_SHUFFLE(1, 1, 1, 1)); + __m128 e2 = _mm_shuffle_ps(in2[1], in2[1], _MM_SHUFFLE(2, 2, 2, 2)); + __m128 e3 = _mm_shuffle_ps(in2[1], in2[1], _MM_SHUFFLE(3, 3, 3, 3)); + + __m128 m0 = _mm_mul_ps(in1[0], e0); + __m128 m1 = _mm_mul_ps(in1[1], e1); + __m128 m2 = _mm_mul_ps(in1[2], e2); + __m128 m3 = _mm_mul_ps(in1[3], e3); + + __m128 a0 = _mm_add_ps(m0, m1); + __m128 a1 = _mm_add_ps(m2, m3); + __m128 a2 = _mm_add_ps(a0, a1); + + out[1] = a2; + } + + { + __m128 e0 = _mm_shuffle_ps(in2[2], in2[2], _MM_SHUFFLE(0, 0, 0, 0)); + __m128 e1 = _mm_shuffle_ps(in2[2], in2[2], _MM_SHUFFLE(1, 1, 1, 1)); + __m128 e2 = _mm_shuffle_ps(in2[2], in2[2], _MM_SHUFFLE(2, 2, 2, 2)); + __m128 e3 = _mm_shuffle_ps(in2[2], in2[2], _MM_SHUFFLE(3, 3, 3, 3)); + + __m128 m0 = _mm_mul_ps(in1[0], e0); + __m128 m1 = _mm_mul_ps(in1[1], e1); + __m128 m2 = _mm_mul_ps(in1[2], e2); + __m128 m3 = _mm_mul_ps(in1[3], e3); + + __m128 a0 = _mm_add_ps(m0, m1); + __m128 a1 = _mm_add_ps(m2, m3); + __m128 a2 = _mm_add_ps(a0, a1); + + out[2] = a2; + } + + { + //(__m128&)_mm_shuffle_epi32(__m128i&)in2[0], _MM_SHUFFLE(3, 3, 3, 3)) + __m128 e0 = _mm_shuffle_ps(in2[3], in2[3], _MM_SHUFFLE(0, 0, 0, 0)); + __m128 e1 = _mm_shuffle_ps(in2[3], in2[3], _MM_SHUFFLE(1, 1, 1, 1)); + __m128 e2 = _mm_shuffle_ps(in2[3], in2[3], _MM_SHUFFLE(2, 2, 2, 2)); + __m128 e3 = _mm_shuffle_ps(in2[3], in2[3], _MM_SHUFFLE(3, 3, 3, 3)); + + __m128 m0 = _mm_mul_ps(in1[0], e0); + __m128 m1 = _mm_mul_ps(in1[1], e1); + __m128 m2 = _mm_mul_ps(in1[2], e2); + __m128 m3 = _mm_mul_ps(in1[3], e3); + + __m128 a0 = _mm_add_ps(m0, m1); + __m128 a1 = _mm_add_ps(m2, m3); + __m128 a2 = _mm_add_ps(a0, a1); + + out[3] = a2; + } +} + +GLM_FUNC_QUALIFIER void glm_mat4_transpose(glm_vec4 const in[4], glm_vec4 out[4]) +{ + __m128 tmp0 = _mm_shuffle_ps(in[0], in[1], 0x44); + __m128 tmp2 = _mm_shuffle_ps(in[0], in[1], 0xEE); + __m128 tmp1 = _mm_shuffle_ps(in[2], in[3], 0x44); + __m128 tmp3 = _mm_shuffle_ps(in[2], in[3], 0xEE); + + out[0] = _mm_shuffle_ps(tmp0, tmp1, 0x88); + out[1] = _mm_shuffle_ps(tmp0, tmp1, 0xDD); + out[2] = _mm_shuffle_ps(tmp2, tmp3, 0x88); + out[3] = _mm_shuffle_ps(tmp2, tmp3, 0xDD); +} + +GLM_FUNC_QUALIFIER glm_vec4 glm_mat4_determinant_highp(glm_vec4 const in[4]) +{ + __m128 Fac0; + { + // valType SubFactor00 = m[2][2] * m[3][3] - m[3][2] * m[2][3]; + // valType SubFactor00 = m[2][2] * m[3][3] - m[3][2] * m[2][3]; + // valType SubFactor06 = m[1][2] * m[3][3] - m[3][2] * m[1][3]; + // valType SubFactor13 = m[1][2] * m[2][3] - m[2][2] * m[1][3]; + + __m128 Swp0a = _mm_shuffle_ps(in[3], in[2], _MM_SHUFFLE(3, 3, 3, 3)); + __m128 Swp0b = _mm_shuffle_ps(in[3], in[2], _MM_SHUFFLE(2, 2, 2, 2)); + + __m128 Swp00 = _mm_shuffle_ps(in[2], in[1], _MM_SHUFFLE(2, 2, 2, 2)); + __m128 Swp01 = _mm_shuffle_ps(Swp0a, Swp0a, _MM_SHUFFLE(2, 0, 0, 0)); + __m128 Swp02 = _mm_shuffle_ps(Swp0b, Swp0b, _MM_SHUFFLE(2, 0, 0, 0)); + __m128 Swp03 = _mm_shuffle_ps(in[2], in[1], _MM_SHUFFLE(3, 3, 3, 3)); + + __m128 Mul00 = _mm_mul_ps(Swp00, Swp01); + __m128 Mul01 = _mm_mul_ps(Swp02, Swp03); + Fac0 = _mm_sub_ps(Mul00, Mul01); + } + + __m128 Fac1; + { + // valType SubFactor01 = m[2][1] * m[3][3] - m[3][1] * m[2][3]; + // valType SubFactor01 = m[2][1] * m[3][3] - m[3][1] * m[2][3]; + // valType SubFactor07 = m[1][1] * m[3][3] - m[3][1] * m[1][3]; + // valType SubFactor14 = m[1][1] * m[2][3] - m[2][1] * m[1][3]; + + __m128 Swp0a = _mm_shuffle_ps(in[3], in[2], _MM_SHUFFLE(3, 3, 3, 3)); + __m128 Swp0b = _mm_shuffle_ps(in[3], in[2], _MM_SHUFFLE(1, 1, 1, 1)); + + __m128 Swp00 = _mm_shuffle_ps(in[2], in[1], _MM_SHUFFLE(1, 1, 1, 1)); + __m128 Swp01 = _mm_shuffle_ps(Swp0a, Swp0a, _MM_SHUFFLE(2, 0, 0, 0)); + __m128 Swp02 = _mm_shuffle_ps(Swp0b, Swp0b, _MM_SHUFFLE(2, 0, 0, 0)); + __m128 Swp03 = _mm_shuffle_ps(in[2], in[1], _MM_SHUFFLE(3, 3, 3, 3)); + + __m128 Mul00 = _mm_mul_ps(Swp00, Swp01); + __m128 Mul01 = _mm_mul_ps(Swp02, Swp03); + Fac1 = _mm_sub_ps(Mul00, Mul01); + } + + + __m128 Fac2; + { + // valType SubFactor02 = m[2][1] * m[3][2] - m[3][1] * m[2][2]; + // valType SubFactor02 = m[2][1] * m[3][2] - m[3][1] * m[2][2]; + // valType SubFactor08 = m[1][1] * m[3][2] - m[3][1] * m[1][2]; + // valType SubFactor15 = m[1][1] * m[2][2] - m[2][1] * m[1][2]; + + __m128 Swp0a = _mm_shuffle_ps(in[3], in[2], _MM_SHUFFLE(2, 2, 2, 2)); + __m128 Swp0b = _mm_shuffle_ps(in[3], in[2], _MM_SHUFFLE(1, 1, 1, 1)); + + __m128 Swp00 = _mm_shuffle_ps(in[2], in[1], _MM_SHUFFLE(1, 1, 1, 1)); + __m128 Swp01 = _mm_shuffle_ps(Swp0a, Swp0a, _MM_SHUFFLE(2, 0, 0, 0)); + __m128 Swp02 = _mm_shuffle_ps(Swp0b, Swp0b, _MM_SHUFFLE(2, 0, 0, 0)); + __m128 Swp03 = _mm_shuffle_ps(in[2], in[1], _MM_SHUFFLE(2, 2, 2, 2)); + + __m128 Mul00 = _mm_mul_ps(Swp00, Swp01); + __m128 Mul01 = _mm_mul_ps(Swp02, Swp03); + Fac2 = _mm_sub_ps(Mul00, Mul01); + } + + __m128 Fac3; + { + // valType SubFactor03 = m[2][0] * m[3][3] - m[3][0] * m[2][3]; + // valType SubFactor03 = m[2][0] * m[3][3] - m[3][0] * m[2][3]; + // valType SubFactor09 = m[1][0] * m[3][3] - m[3][0] * m[1][3]; + // valType SubFactor16 = m[1][0] * m[2][3] - m[2][0] * m[1][3]; + + __m128 Swp0a = _mm_shuffle_ps(in[3], in[2], _MM_SHUFFLE(3, 3, 3, 3)); + __m128 Swp0b = _mm_shuffle_ps(in[3], in[2], _MM_SHUFFLE(0, 0, 0, 0)); + + __m128 Swp00 = _mm_shuffle_ps(in[2], in[1], _MM_SHUFFLE(0, 0, 0, 0)); + __m128 Swp01 = _mm_shuffle_ps(Swp0a, Swp0a, _MM_SHUFFLE(2, 0, 0, 0)); + __m128 Swp02 = _mm_shuffle_ps(Swp0b, Swp0b, _MM_SHUFFLE(2, 0, 0, 0)); + __m128 Swp03 = _mm_shuffle_ps(in[2], in[1], _MM_SHUFFLE(3, 3, 3, 3)); + + __m128 Mul00 = _mm_mul_ps(Swp00, Swp01); + __m128 Mul01 = _mm_mul_ps(Swp02, Swp03); + Fac3 = _mm_sub_ps(Mul00, Mul01); + } + + __m128 Fac4; + { + // valType SubFactor04 = m[2][0] * m[3][2] - m[3][0] * m[2][2]; + // valType SubFactor04 = m[2][0] * m[3][2] - m[3][0] * m[2][2]; + // valType SubFactor10 = m[1][0] * m[3][2] - m[3][0] * m[1][2]; + // valType SubFactor17 = m[1][0] * m[2][2] - m[2][0] * m[1][2]; + + __m128 Swp0a = _mm_shuffle_ps(in[3], in[2], _MM_SHUFFLE(2, 2, 2, 2)); + __m128 Swp0b = _mm_shuffle_ps(in[3], in[2], _MM_SHUFFLE(0, 0, 0, 0)); + + __m128 Swp00 = _mm_shuffle_ps(in[2], in[1], _MM_SHUFFLE(0, 0, 0, 0)); + __m128 Swp01 = _mm_shuffle_ps(Swp0a, Swp0a, _MM_SHUFFLE(2, 0, 0, 0)); + __m128 Swp02 = _mm_shuffle_ps(Swp0b, Swp0b, _MM_SHUFFLE(2, 0, 0, 0)); + __m128 Swp03 = _mm_shuffle_ps(in[2], in[1], _MM_SHUFFLE(2, 2, 2, 2)); + + __m128 Mul00 = _mm_mul_ps(Swp00, Swp01); + __m128 Mul01 = _mm_mul_ps(Swp02, Swp03); + Fac4 = _mm_sub_ps(Mul00, Mul01); + } + + __m128 Fac5; + { + // valType SubFactor05 = m[2][0] * m[3][1] - m[3][0] * m[2][1]; + // valType SubFactor05 = m[2][0] * m[3][1] - m[3][0] * m[2][1]; + // valType SubFactor12 = m[1][0] * m[3][1] - m[3][0] * m[1][1]; + // valType SubFactor18 = m[1][0] * m[2][1] - m[2][0] * m[1][1]; + + __m128 Swp0a = _mm_shuffle_ps(in[3], in[2], _MM_SHUFFLE(1, 1, 1, 1)); + __m128 Swp0b = _mm_shuffle_ps(in[3], in[2], _MM_SHUFFLE(0, 0, 0, 0)); + + __m128 Swp00 = _mm_shuffle_ps(in[2], in[1], _MM_SHUFFLE(0, 0, 0, 0)); + __m128 Swp01 = _mm_shuffle_ps(Swp0a, Swp0a, _MM_SHUFFLE(2, 0, 0, 0)); + __m128 Swp02 = _mm_shuffle_ps(Swp0b, Swp0b, _MM_SHUFFLE(2, 0, 0, 0)); + __m128 Swp03 = _mm_shuffle_ps(in[2], in[1], _MM_SHUFFLE(1, 1, 1, 1)); + + __m128 Mul00 = _mm_mul_ps(Swp00, Swp01); + __m128 Mul01 = _mm_mul_ps(Swp02, Swp03); + Fac5 = _mm_sub_ps(Mul00, Mul01); + } + + __m128 SignA = _mm_set_ps( 1.0f,-1.0f, 1.0f,-1.0f); + __m128 SignB = _mm_set_ps(-1.0f, 1.0f,-1.0f, 1.0f); + + // m[1][0] + // m[0][0] + // m[0][0] + // m[0][0] + __m128 Temp0 = _mm_shuffle_ps(in[1], in[0], _MM_SHUFFLE(0, 0, 0, 0)); + __m128 Vec0 = _mm_shuffle_ps(Temp0, Temp0, _MM_SHUFFLE(2, 2, 2, 0)); + + // m[1][1] + // m[0][1] + // m[0][1] + // m[0][1] + __m128 Temp1 = _mm_shuffle_ps(in[1], in[0], _MM_SHUFFLE(1, 1, 1, 1)); + __m128 Vec1 = _mm_shuffle_ps(Temp1, Temp1, _MM_SHUFFLE(2, 2, 2, 0)); + + // m[1][2] + // m[0][2] + // m[0][2] + // m[0][2] + __m128 Temp2 = _mm_shuffle_ps(in[1], in[0], _MM_SHUFFLE(2, 2, 2, 2)); + __m128 Vec2 = _mm_shuffle_ps(Temp2, Temp2, _MM_SHUFFLE(2, 2, 2, 0)); + + // m[1][3] + // m[0][3] + // m[0][3] + // m[0][3] + __m128 Temp3 = _mm_shuffle_ps(in[1], in[0], _MM_SHUFFLE(3, 3, 3, 3)); + __m128 Vec3 = _mm_shuffle_ps(Temp3, Temp3, _MM_SHUFFLE(2, 2, 2, 0)); + + // col0 + // + (Vec1[0] * Fac0[0] - Vec2[0] * Fac1[0] + Vec3[0] * Fac2[0]), + // - (Vec1[1] * Fac0[1] - Vec2[1] * Fac1[1] + Vec3[1] * Fac2[1]), + // + (Vec1[2] * Fac0[2] - Vec2[2] * Fac1[2] + Vec3[2] * Fac2[2]), + // - (Vec1[3] * Fac0[3] - Vec2[3] * Fac1[3] + Vec3[3] * Fac2[3]), + __m128 Mul00 = _mm_mul_ps(Vec1, Fac0); + __m128 Mul01 = _mm_mul_ps(Vec2, Fac1); + __m128 Mul02 = _mm_mul_ps(Vec3, Fac2); + __m128 Sub00 = _mm_sub_ps(Mul00, Mul01); + __m128 Add00 = _mm_add_ps(Sub00, Mul02); + __m128 Inv0 = _mm_mul_ps(SignB, Add00); + + // col1 + // - (Vec0[0] * Fac0[0] - Vec2[0] * Fac3[0] + Vec3[0] * Fac4[0]), + // + (Vec0[0] * Fac0[1] - Vec2[1] * Fac3[1] + Vec3[1] * Fac4[1]), + // - (Vec0[0] * Fac0[2] - Vec2[2] * Fac3[2] + Vec3[2] * Fac4[2]), + // + (Vec0[0] * Fac0[3] - Vec2[3] * Fac3[3] + Vec3[3] * Fac4[3]), + __m128 Mul03 = _mm_mul_ps(Vec0, Fac0); + __m128 Mul04 = _mm_mul_ps(Vec2, Fac3); + __m128 Mul05 = _mm_mul_ps(Vec3, Fac4); + __m128 Sub01 = _mm_sub_ps(Mul03, Mul04); + __m128 Add01 = _mm_add_ps(Sub01, Mul05); + __m128 Inv1 = _mm_mul_ps(SignA, Add01); + + // col2 + // + (Vec0[0] * Fac1[0] - Vec1[0] * Fac3[0] + Vec3[0] * Fac5[0]), + // - (Vec0[0] * Fac1[1] - Vec1[1] * Fac3[1] + Vec3[1] * Fac5[1]), + // + (Vec0[0] * Fac1[2] - Vec1[2] * Fac3[2] + Vec3[2] * Fac5[2]), + // - (Vec0[0] * Fac1[3] - Vec1[3] * Fac3[3] + Vec3[3] * Fac5[3]), + __m128 Mul06 = _mm_mul_ps(Vec0, Fac1); + __m128 Mul07 = _mm_mul_ps(Vec1, Fac3); + __m128 Mul08 = _mm_mul_ps(Vec3, Fac5); + __m128 Sub02 = _mm_sub_ps(Mul06, Mul07); + __m128 Add02 = _mm_add_ps(Sub02, Mul08); + __m128 Inv2 = _mm_mul_ps(SignB, Add02); + + // col3 + // - (Vec1[0] * Fac2[0] - Vec1[0] * Fac4[0] + Vec2[0] * Fac5[0]), + // + (Vec1[0] * Fac2[1] - Vec1[1] * Fac4[1] + Vec2[1] * Fac5[1]), + // - (Vec1[0] * Fac2[2] - Vec1[2] * Fac4[2] + Vec2[2] * Fac5[2]), + // + (Vec1[0] * Fac2[3] - Vec1[3] * Fac4[3] + Vec2[3] * Fac5[3])); + __m128 Mul09 = _mm_mul_ps(Vec0, Fac2); + __m128 Mul10 = _mm_mul_ps(Vec1, Fac4); + __m128 Mul11 = _mm_mul_ps(Vec2, Fac5); + __m128 Sub03 = _mm_sub_ps(Mul09, Mul10); + __m128 Add03 = _mm_add_ps(Sub03, Mul11); + __m128 Inv3 = _mm_mul_ps(SignA, Add03); + + __m128 Row0 = _mm_shuffle_ps(Inv0, Inv1, _MM_SHUFFLE(0, 0, 0, 0)); + __m128 Row1 = _mm_shuffle_ps(Inv2, Inv3, _MM_SHUFFLE(0, 0, 0, 0)); + __m128 Row2 = _mm_shuffle_ps(Row0, Row1, _MM_SHUFFLE(2, 0, 2, 0)); + + // valType Determinant = m[0][0] * Inverse[0][0] + // + m[0][1] * Inverse[1][0] + // + m[0][2] * Inverse[2][0] + // + m[0][3] * Inverse[3][0]; + __m128 Det0 = glm_vec4_dot(in[0], Row2); + return Det0; +} + +GLM_FUNC_QUALIFIER glm_vec4 glm_mat4_determinant_lowp(glm_vec4 const m[4]) +{ + // _mm_castsi128_ps(_mm_shuffle_epi32(_mm_castps_si128( + + //T SubFactor00 = m[2][2] * m[3][3] - m[3][2] * m[2][3]; + //T SubFactor01 = m[2][1] * m[3][3] - m[3][1] * m[2][3]; + //T SubFactor02 = m[2][1] * m[3][2] - m[3][1] * m[2][2]; + //T SubFactor03 = m[2][0] * m[3][3] - m[3][0] * m[2][3]; + //T SubFactor04 = m[2][0] * m[3][2] - m[3][0] * m[2][2]; + //T SubFactor05 = m[2][0] * m[3][1] - m[3][0] * m[2][1]; + + // First 2 columns + __m128 Swp2A = _mm_castsi128_ps(_mm_shuffle_epi32(_mm_castps_si128(m[2]), _MM_SHUFFLE(0, 1, 1, 2))); + __m128 Swp3A = _mm_castsi128_ps(_mm_shuffle_epi32(_mm_castps_si128(m[3]), _MM_SHUFFLE(3, 2, 3, 3))); + __m128 MulA = _mm_mul_ps(Swp2A, Swp3A); + + // Second 2 columns + __m128 Swp2B = _mm_castsi128_ps(_mm_shuffle_epi32(_mm_castps_si128(m[2]), _MM_SHUFFLE(3, 2, 3, 3))); + __m128 Swp3B = _mm_castsi128_ps(_mm_shuffle_epi32(_mm_castps_si128(m[3]), _MM_SHUFFLE(0, 1, 1, 2))); + __m128 MulB = _mm_mul_ps(Swp2B, Swp3B); + + // Columns subtraction + __m128 SubE = _mm_sub_ps(MulA, MulB); + + // Last 2 rows + __m128 Swp2C = _mm_castsi128_ps(_mm_shuffle_epi32(_mm_castps_si128(m[2]), _MM_SHUFFLE(0, 0, 1, 2))); + __m128 Swp3C = _mm_castsi128_ps(_mm_shuffle_epi32(_mm_castps_si128(m[3]), _MM_SHUFFLE(1, 2, 0, 0))); + __m128 MulC = _mm_mul_ps(Swp2C, Swp3C); + __m128 SubF = _mm_sub_ps(_mm_movehl_ps(MulC, MulC), MulC); + + //vec<4, T, Q> DetCof( + // + (m[1][1] * SubFactor00 - m[1][2] * SubFactor01 + m[1][3] * SubFactor02), + // - (m[1][0] * SubFactor00 - m[1][2] * SubFactor03 + m[1][3] * SubFactor04), + // + (m[1][0] * SubFactor01 - m[1][1] * SubFactor03 + m[1][3] * SubFactor05), + // - (m[1][0] * SubFactor02 - m[1][1] * SubFactor04 + m[1][2] * SubFactor05)); + + __m128 SubFacA = _mm_castsi128_ps(_mm_shuffle_epi32(_mm_castps_si128(SubE), _MM_SHUFFLE(2, 1, 0, 0))); + __m128 SwpFacA = _mm_castsi128_ps(_mm_shuffle_epi32(_mm_castps_si128(m[1]), _MM_SHUFFLE(0, 0, 0, 1))); + __m128 MulFacA = _mm_mul_ps(SwpFacA, SubFacA); + + __m128 SubTmpB = _mm_shuffle_ps(SubE, SubF, _MM_SHUFFLE(0, 0, 3, 1)); + __m128 SubFacB = _mm_castsi128_ps(_mm_shuffle_epi32(_mm_castps_si128(SubTmpB), _MM_SHUFFLE(3, 1, 1, 0)));//SubF[0], SubE[3], SubE[3], SubE[1]; + __m128 SwpFacB = _mm_castsi128_ps(_mm_shuffle_epi32(_mm_castps_si128(m[1]), _MM_SHUFFLE(1, 1, 2, 2))); + __m128 MulFacB = _mm_mul_ps(SwpFacB, SubFacB); + + __m128 SubRes = _mm_sub_ps(MulFacA, MulFacB); + + __m128 SubTmpC = _mm_shuffle_ps(SubE, SubF, _MM_SHUFFLE(1, 0, 2, 2)); + __m128 SubFacC = _mm_castsi128_ps(_mm_shuffle_epi32(_mm_castps_si128(SubTmpC), _MM_SHUFFLE(3, 3, 2, 0))); + __m128 SwpFacC = _mm_castsi128_ps(_mm_shuffle_epi32(_mm_castps_si128(m[1]), _MM_SHUFFLE(2, 3, 3, 3))); + __m128 MulFacC = _mm_mul_ps(SwpFacC, SubFacC); + + __m128 AddRes = _mm_add_ps(SubRes, MulFacC); + __m128 DetCof = _mm_mul_ps(AddRes, _mm_setr_ps( 1.0f,-1.0f, 1.0f,-1.0f)); + + //return m[0][0] * DetCof[0] + // + m[0][1] * DetCof[1] + // + m[0][2] * DetCof[2] + // + m[0][3] * DetCof[3]; + + return glm_vec4_dot(m[0], DetCof); +} + +GLM_FUNC_QUALIFIER glm_vec4 glm_mat4_determinant(glm_vec4 const m[4]) +{ + // _mm_castsi128_ps(_mm_shuffle_epi32(_mm_castps_si128(add) + + //T SubFactor00 = m[2][2] * m[3][3] - m[3][2] * m[2][3]; + //T SubFactor01 = m[2][1] * m[3][3] - m[3][1] * m[2][3]; + //T SubFactor02 = m[2][1] * m[3][2] - m[3][1] * m[2][2]; + //T SubFactor03 = m[2][0] * m[3][3] - m[3][0] * m[2][3]; + //T SubFactor04 = m[2][0] * m[3][2] - m[3][0] * m[2][2]; + //T SubFactor05 = m[2][0] * m[3][1] - m[3][0] * m[2][1]; + + // First 2 columns + __m128 Swp2A = _mm_shuffle_ps(m[2], m[2], _MM_SHUFFLE(0, 1, 1, 2)); + __m128 Swp3A = _mm_shuffle_ps(m[3], m[3], _MM_SHUFFLE(3, 2, 3, 3)); + __m128 MulA = _mm_mul_ps(Swp2A, Swp3A); + + // Second 2 columns + __m128 Swp2B = _mm_shuffle_ps(m[2], m[2], _MM_SHUFFLE(3, 2, 3, 3)); + __m128 Swp3B = _mm_shuffle_ps(m[3], m[3], _MM_SHUFFLE(0, 1, 1, 2)); + __m128 MulB = _mm_mul_ps(Swp2B, Swp3B); + + // Columns subtraction + __m128 SubE = _mm_sub_ps(MulA, MulB); + + // Last 2 rows + __m128 Swp2C = _mm_shuffle_ps(m[2], m[2], _MM_SHUFFLE(0, 0, 1, 2)); + __m128 Swp3C = _mm_shuffle_ps(m[3], m[3], _MM_SHUFFLE(1, 2, 0, 0)); + __m128 MulC = _mm_mul_ps(Swp2C, Swp3C); + __m128 SubF = _mm_sub_ps(_mm_movehl_ps(MulC, MulC), MulC); + + //vec<4, T, Q> DetCof( + // + (m[1][1] * SubFactor00 - m[1][2] * SubFactor01 + m[1][3] * SubFactor02), + // - (m[1][0] * SubFactor00 - m[1][2] * SubFactor03 + m[1][3] * SubFactor04), + // + (m[1][0] * SubFactor01 - m[1][1] * SubFactor03 + m[1][3] * SubFactor05), + // - (m[1][0] * SubFactor02 - m[1][1] * SubFactor04 + m[1][2] * SubFactor05)); + + __m128 SubFacA = _mm_shuffle_ps(SubE, SubE, _MM_SHUFFLE(2, 1, 0, 0)); + __m128 SwpFacA = _mm_shuffle_ps(m[1], m[1], _MM_SHUFFLE(0, 0, 0, 1)); + __m128 MulFacA = _mm_mul_ps(SwpFacA, SubFacA); + + __m128 SubTmpB = _mm_shuffle_ps(SubE, SubF, _MM_SHUFFLE(0, 0, 3, 1)); + __m128 SubFacB = _mm_shuffle_ps(SubTmpB, SubTmpB, _MM_SHUFFLE(3, 1, 1, 0));//SubF[0], SubE[3], SubE[3], SubE[1]; + __m128 SwpFacB = _mm_shuffle_ps(m[1], m[1], _MM_SHUFFLE(1, 1, 2, 2)); + __m128 MulFacB = _mm_mul_ps(SwpFacB, SubFacB); + + __m128 SubRes = _mm_sub_ps(MulFacA, MulFacB); + + __m128 SubTmpC = _mm_shuffle_ps(SubE, SubF, _MM_SHUFFLE(1, 0, 2, 2)); + __m128 SubFacC = _mm_shuffle_ps(SubTmpC, SubTmpC, _MM_SHUFFLE(3, 3, 2, 0)); + __m128 SwpFacC = _mm_shuffle_ps(m[1], m[1], _MM_SHUFFLE(2, 3, 3, 3)); + __m128 MulFacC = _mm_mul_ps(SwpFacC, SubFacC); + + __m128 AddRes = _mm_add_ps(SubRes, MulFacC); + __m128 DetCof = _mm_mul_ps(AddRes, _mm_setr_ps( 1.0f,-1.0f, 1.0f,-1.0f)); + + //return m[0][0] * DetCof[0] + // + m[0][1] * DetCof[1] + // + m[0][2] * DetCof[2] + // + m[0][3] * DetCof[3]; + + return glm_vec4_dot(m[0], DetCof); +} + +GLM_FUNC_QUALIFIER void glm_mat4_inverse(glm_vec4 const in[4], glm_vec4 out[4]) +{ + __m128 Fac0; + { + // valType SubFactor00 = m[2][2] * m[3][3] - m[3][2] * m[2][3]; + // valType SubFactor00 = m[2][2] * m[3][3] - m[3][2] * m[2][3]; + // valType SubFactor06 = m[1][2] * m[3][3] - m[3][2] * m[1][3]; + // valType SubFactor13 = m[1][2] * m[2][3] - m[2][2] * m[1][3]; + + __m128 Swp0a = _mm_shuffle_ps(in[3], in[2], _MM_SHUFFLE(3, 3, 3, 3)); + __m128 Swp0b = _mm_shuffle_ps(in[3], in[2], _MM_SHUFFLE(2, 2, 2, 2)); + + __m128 Swp00 = _mm_shuffle_ps(in[2], in[1], _MM_SHUFFLE(2, 2, 2, 2)); + __m128 Swp01 = _mm_shuffle_ps(Swp0a, Swp0a, _MM_SHUFFLE(2, 0, 0, 0)); + __m128 Swp02 = _mm_shuffle_ps(Swp0b, Swp0b, _MM_SHUFFLE(2, 0, 0, 0)); + __m128 Swp03 = _mm_shuffle_ps(in[2], in[1], _MM_SHUFFLE(3, 3, 3, 3)); + + __m128 Mul00 = _mm_mul_ps(Swp00, Swp01); + __m128 Mul01 = _mm_mul_ps(Swp02, Swp03); + Fac0 = _mm_sub_ps(Mul00, Mul01); + } + + __m128 Fac1; + { + // valType SubFactor01 = m[2][1] * m[3][3] - m[3][1] * m[2][3]; + // valType SubFactor01 = m[2][1] * m[3][3] - m[3][1] * m[2][3]; + // valType SubFactor07 = m[1][1] * m[3][3] - m[3][1] * m[1][3]; + // valType SubFactor14 = m[1][1] * m[2][3] - m[2][1] * m[1][3]; + + __m128 Swp0a = _mm_shuffle_ps(in[3], in[2], _MM_SHUFFLE(3, 3, 3, 3)); + __m128 Swp0b = _mm_shuffle_ps(in[3], in[2], _MM_SHUFFLE(1, 1, 1, 1)); + + __m128 Swp00 = _mm_shuffle_ps(in[2], in[1], _MM_SHUFFLE(1, 1, 1, 1)); + __m128 Swp01 = _mm_shuffle_ps(Swp0a, Swp0a, _MM_SHUFFLE(2, 0, 0, 0)); + __m128 Swp02 = _mm_shuffle_ps(Swp0b, Swp0b, _MM_SHUFFLE(2, 0, 0, 0)); + __m128 Swp03 = _mm_shuffle_ps(in[2], in[1], _MM_SHUFFLE(3, 3, 3, 3)); + + __m128 Mul00 = _mm_mul_ps(Swp00, Swp01); + __m128 Mul01 = _mm_mul_ps(Swp02, Swp03); + Fac1 = _mm_sub_ps(Mul00, Mul01); + } + + + __m128 Fac2; + { + // valType SubFactor02 = m[2][1] * m[3][2] - m[3][1] * m[2][2]; + // valType SubFactor02 = m[2][1] * m[3][2] - m[3][1] * m[2][2]; + // valType SubFactor08 = m[1][1] * m[3][2] - m[3][1] * m[1][2]; + // valType SubFactor15 = m[1][1] * m[2][2] - m[2][1] * m[1][2]; + + __m128 Swp0a = _mm_shuffle_ps(in[3], in[2], _MM_SHUFFLE(2, 2, 2, 2)); + __m128 Swp0b = _mm_shuffle_ps(in[3], in[2], _MM_SHUFFLE(1, 1, 1, 1)); + + __m128 Swp00 = _mm_shuffle_ps(in[2], in[1], _MM_SHUFFLE(1, 1, 1, 1)); + __m128 Swp01 = _mm_shuffle_ps(Swp0a, Swp0a, _MM_SHUFFLE(2, 0, 0, 0)); + __m128 Swp02 = _mm_shuffle_ps(Swp0b, Swp0b, _MM_SHUFFLE(2, 0, 0, 0)); + __m128 Swp03 = _mm_shuffle_ps(in[2], in[1], _MM_SHUFFLE(2, 2, 2, 2)); + + __m128 Mul00 = _mm_mul_ps(Swp00, Swp01); + __m128 Mul01 = _mm_mul_ps(Swp02, Swp03); + Fac2 = _mm_sub_ps(Mul00, Mul01); + } + + __m128 Fac3; + { + // valType SubFactor03 = m[2][0] * m[3][3] - m[3][0] * m[2][3]; + // valType SubFactor03 = m[2][0] * m[3][3] - m[3][0] * m[2][3]; + // valType SubFactor09 = m[1][0] * m[3][3] - m[3][0] * m[1][3]; + // valType SubFactor16 = m[1][0] * m[2][3] - m[2][0] * m[1][3]; + + __m128 Swp0a = _mm_shuffle_ps(in[3], in[2], _MM_SHUFFLE(3, 3, 3, 3)); + __m128 Swp0b = _mm_shuffle_ps(in[3], in[2], _MM_SHUFFLE(0, 0, 0, 0)); + + __m128 Swp00 = _mm_shuffle_ps(in[2], in[1], _MM_SHUFFLE(0, 0, 0, 0)); + __m128 Swp01 = _mm_shuffle_ps(Swp0a, Swp0a, _MM_SHUFFLE(2, 0, 0, 0)); + __m128 Swp02 = _mm_shuffle_ps(Swp0b, Swp0b, _MM_SHUFFLE(2, 0, 0, 0)); + __m128 Swp03 = _mm_shuffle_ps(in[2], in[1], _MM_SHUFFLE(3, 3, 3, 3)); + + __m128 Mul00 = _mm_mul_ps(Swp00, Swp01); + __m128 Mul01 = _mm_mul_ps(Swp02, Swp03); + Fac3 = _mm_sub_ps(Mul00, Mul01); + } + + __m128 Fac4; + { + // valType SubFactor04 = m[2][0] * m[3][2] - m[3][0] * m[2][2]; + // valType SubFactor04 = m[2][0] * m[3][2] - m[3][0] * m[2][2]; + // valType SubFactor10 = m[1][0] * m[3][2] - m[3][0] * m[1][2]; + // valType SubFactor17 = m[1][0] * m[2][2] - m[2][0] * m[1][2]; + + __m128 Swp0a = _mm_shuffle_ps(in[3], in[2], _MM_SHUFFLE(2, 2, 2, 2)); + __m128 Swp0b = _mm_shuffle_ps(in[3], in[2], _MM_SHUFFLE(0, 0, 0, 0)); + + __m128 Swp00 = _mm_shuffle_ps(in[2], in[1], _MM_SHUFFLE(0, 0, 0, 0)); + __m128 Swp01 = _mm_shuffle_ps(Swp0a, Swp0a, _MM_SHUFFLE(2, 0, 0, 0)); + __m128 Swp02 = _mm_shuffle_ps(Swp0b, Swp0b, _MM_SHUFFLE(2, 0, 0, 0)); + __m128 Swp03 = _mm_shuffle_ps(in[2], in[1], _MM_SHUFFLE(2, 2, 2, 2)); + + __m128 Mul00 = _mm_mul_ps(Swp00, Swp01); + __m128 Mul01 = _mm_mul_ps(Swp02, Swp03); + Fac4 = _mm_sub_ps(Mul00, Mul01); + } + + __m128 Fac5; + { + // valType SubFactor05 = m[2][0] * m[3][1] - m[3][0] * m[2][1]; + // valType SubFactor05 = m[2][0] * m[3][1] - m[3][0] * m[2][1]; + // valType SubFactor12 = m[1][0] * m[3][1] - m[3][0] * m[1][1]; + // valType SubFactor18 = m[1][0] * m[2][1] - m[2][0] * m[1][1]; + + __m128 Swp0a = _mm_shuffle_ps(in[3], in[2], _MM_SHUFFLE(1, 1, 1, 1)); + __m128 Swp0b = _mm_shuffle_ps(in[3], in[2], _MM_SHUFFLE(0, 0, 0, 0)); + + __m128 Swp00 = _mm_shuffle_ps(in[2], in[1], _MM_SHUFFLE(0, 0, 0, 0)); + __m128 Swp01 = _mm_shuffle_ps(Swp0a, Swp0a, _MM_SHUFFLE(2, 0, 0, 0)); + __m128 Swp02 = _mm_shuffle_ps(Swp0b, Swp0b, _MM_SHUFFLE(2, 0, 0, 0)); + __m128 Swp03 = _mm_shuffle_ps(in[2], in[1], _MM_SHUFFLE(1, 1, 1, 1)); + + __m128 Mul00 = _mm_mul_ps(Swp00, Swp01); + __m128 Mul01 = _mm_mul_ps(Swp02, Swp03); + Fac5 = _mm_sub_ps(Mul00, Mul01); + } + + __m128 SignA = _mm_set_ps( 1.0f,-1.0f, 1.0f,-1.0f); + __m128 SignB = _mm_set_ps(-1.0f, 1.0f,-1.0f, 1.0f); + + // m[1][0] + // m[0][0] + // m[0][0] + // m[0][0] + __m128 Temp0 = _mm_shuffle_ps(in[1], in[0], _MM_SHUFFLE(0, 0, 0, 0)); + __m128 Vec0 = _mm_shuffle_ps(Temp0, Temp0, _MM_SHUFFLE(2, 2, 2, 0)); + + // m[1][1] + // m[0][1] + // m[0][1] + // m[0][1] + __m128 Temp1 = _mm_shuffle_ps(in[1], in[0], _MM_SHUFFLE(1, 1, 1, 1)); + __m128 Vec1 = _mm_shuffle_ps(Temp1, Temp1, _MM_SHUFFLE(2, 2, 2, 0)); + + // m[1][2] + // m[0][2] + // m[0][2] + // m[0][2] + __m128 Temp2 = _mm_shuffle_ps(in[1], in[0], _MM_SHUFFLE(2, 2, 2, 2)); + __m128 Vec2 = _mm_shuffle_ps(Temp2, Temp2, _MM_SHUFFLE(2, 2, 2, 0)); + + // m[1][3] + // m[0][3] + // m[0][3] + // m[0][3] + __m128 Temp3 = _mm_shuffle_ps(in[1], in[0], _MM_SHUFFLE(3, 3, 3, 3)); + __m128 Vec3 = _mm_shuffle_ps(Temp3, Temp3, _MM_SHUFFLE(2, 2, 2, 0)); + + // col0 + // + (Vec1[0] * Fac0[0] - Vec2[0] * Fac1[0] + Vec3[0] * Fac2[0]), + // - (Vec1[1] * Fac0[1] - Vec2[1] * Fac1[1] + Vec3[1] * Fac2[1]), + // + (Vec1[2] * Fac0[2] - Vec2[2] * Fac1[2] + Vec3[2] * Fac2[2]), + // - (Vec1[3] * Fac0[3] - Vec2[3] * Fac1[3] + Vec3[3] * Fac2[3]), + __m128 Mul00 = _mm_mul_ps(Vec1, Fac0); + __m128 Mul01 = _mm_mul_ps(Vec2, Fac1); + __m128 Mul02 = _mm_mul_ps(Vec3, Fac2); + __m128 Sub00 = _mm_sub_ps(Mul00, Mul01); + __m128 Add00 = _mm_add_ps(Sub00, Mul02); + __m128 Inv0 = _mm_mul_ps(SignB, Add00); + + // col1 + // - (Vec0[0] * Fac0[0] - Vec2[0] * Fac3[0] + Vec3[0] * Fac4[0]), + // + (Vec0[0] * Fac0[1] - Vec2[1] * Fac3[1] + Vec3[1] * Fac4[1]), + // - (Vec0[0] * Fac0[2] - Vec2[2] * Fac3[2] + Vec3[2] * Fac4[2]), + // + (Vec0[0] * Fac0[3] - Vec2[3] * Fac3[3] + Vec3[3] * Fac4[3]), + __m128 Mul03 = _mm_mul_ps(Vec0, Fac0); + __m128 Mul04 = _mm_mul_ps(Vec2, Fac3); + __m128 Mul05 = _mm_mul_ps(Vec3, Fac4); + __m128 Sub01 = _mm_sub_ps(Mul03, Mul04); + __m128 Add01 = _mm_add_ps(Sub01, Mul05); + __m128 Inv1 = _mm_mul_ps(SignA, Add01); + + // col2 + // + (Vec0[0] * Fac1[0] - Vec1[0] * Fac3[0] + Vec3[0] * Fac5[0]), + // - (Vec0[0] * Fac1[1] - Vec1[1] * Fac3[1] + Vec3[1] * Fac5[1]), + // + (Vec0[0] * Fac1[2] - Vec1[2] * Fac3[2] + Vec3[2] * Fac5[2]), + // - (Vec0[0] * Fac1[3] - Vec1[3] * Fac3[3] + Vec3[3] * Fac5[3]), + __m128 Mul06 = _mm_mul_ps(Vec0, Fac1); + __m128 Mul07 = _mm_mul_ps(Vec1, Fac3); + __m128 Mul08 = _mm_mul_ps(Vec3, Fac5); + __m128 Sub02 = _mm_sub_ps(Mul06, Mul07); + __m128 Add02 = _mm_add_ps(Sub02, Mul08); + __m128 Inv2 = _mm_mul_ps(SignB, Add02); + + // col3 + // - (Vec1[0] * Fac2[0] - Vec1[0] * Fac4[0] + Vec2[0] * Fac5[0]), + // + (Vec1[0] * Fac2[1] - Vec1[1] * Fac4[1] + Vec2[1] * Fac5[1]), + // - (Vec1[0] * Fac2[2] - Vec1[2] * Fac4[2] + Vec2[2] * Fac5[2]), + // + (Vec1[0] * Fac2[3] - Vec1[3] * Fac4[3] + Vec2[3] * Fac5[3])); + __m128 Mul09 = _mm_mul_ps(Vec0, Fac2); + __m128 Mul10 = _mm_mul_ps(Vec1, Fac4); + __m128 Mul11 = _mm_mul_ps(Vec2, Fac5); + __m128 Sub03 = _mm_sub_ps(Mul09, Mul10); + __m128 Add03 = _mm_add_ps(Sub03, Mul11); + __m128 Inv3 = _mm_mul_ps(SignA, Add03); + + __m128 Row0 = _mm_shuffle_ps(Inv0, Inv1, _MM_SHUFFLE(0, 0, 0, 0)); + __m128 Row1 = _mm_shuffle_ps(Inv2, Inv3, _MM_SHUFFLE(0, 0, 0, 0)); + __m128 Row2 = _mm_shuffle_ps(Row0, Row1, _MM_SHUFFLE(2, 0, 2, 0)); + + // valType Determinant = m[0][0] * Inverse[0][0] + // + m[0][1] * Inverse[1][0] + // + m[0][2] * Inverse[2][0] + // + m[0][3] * Inverse[3][0]; + __m128 Det0 = glm_vec4_dot(in[0], Row2); + __m128 Rcp0 = _mm_div_ps(_mm_set1_ps(1.0f), Det0); + //__m128 Rcp0 = _mm_rcp_ps(Det0); + + // Inverse /= Determinant; + out[0] = _mm_mul_ps(Inv0, Rcp0); + out[1] = _mm_mul_ps(Inv1, Rcp0); + out[2] = _mm_mul_ps(Inv2, Rcp0); + out[3] = _mm_mul_ps(Inv3, Rcp0); +} + +GLM_FUNC_QUALIFIER void glm_mat4_inverse_lowp(glm_vec4 const in[4], glm_vec4 out[4]) +{ + __m128 Fac0; + { + // valType SubFactor00 = m[2][2] * m[3][3] - m[3][2] * m[2][3]; + // valType SubFactor00 = m[2][2] * m[3][3] - m[3][2] * m[2][3]; + // valType SubFactor06 = m[1][2] * m[3][3] - m[3][2] * m[1][3]; + // valType SubFactor13 = m[1][2] * m[2][3] - m[2][2] * m[1][3]; + + __m128 Swp0a = _mm_shuffle_ps(in[3], in[2], _MM_SHUFFLE(3, 3, 3, 3)); + __m128 Swp0b = _mm_shuffle_ps(in[3], in[2], _MM_SHUFFLE(2, 2, 2, 2)); + + __m128 Swp00 = _mm_shuffle_ps(in[2], in[1], _MM_SHUFFLE(2, 2, 2, 2)); + __m128 Swp01 = _mm_shuffle_ps(Swp0a, Swp0a, _MM_SHUFFLE(2, 0, 0, 0)); + __m128 Swp02 = _mm_shuffle_ps(Swp0b, Swp0b, _MM_SHUFFLE(2, 0, 0, 0)); + __m128 Swp03 = _mm_shuffle_ps(in[2], in[1], _MM_SHUFFLE(3, 3, 3, 3)); + + __m128 Mul00 = _mm_mul_ps(Swp00, Swp01); + __m128 Mul01 = _mm_mul_ps(Swp02, Swp03); + Fac0 = _mm_sub_ps(Mul00, Mul01); + } + + __m128 Fac1; + { + // valType SubFactor01 = m[2][1] * m[3][3] - m[3][1] * m[2][3]; + // valType SubFactor01 = m[2][1] * m[3][3] - m[3][1] * m[2][3]; + // valType SubFactor07 = m[1][1] * m[3][3] - m[3][1] * m[1][3]; + // valType SubFactor14 = m[1][1] * m[2][3] - m[2][1] * m[1][3]; + + __m128 Swp0a = _mm_shuffle_ps(in[3], in[2], _MM_SHUFFLE(3, 3, 3, 3)); + __m128 Swp0b = _mm_shuffle_ps(in[3], in[2], _MM_SHUFFLE(1, 1, 1, 1)); + + __m128 Swp00 = _mm_shuffle_ps(in[2], in[1], _MM_SHUFFLE(1, 1, 1, 1)); + __m128 Swp01 = _mm_shuffle_ps(Swp0a, Swp0a, _MM_SHUFFLE(2, 0, 0, 0)); + __m128 Swp02 = _mm_shuffle_ps(Swp0b, Swp0b, _MM_SHUFFLE(2, 0, 0, 0)); + __m128 Swp03 = _mm_shuffle_ps(in[2], in[1], _MM_SHUFFLE(3, 3, 3, 3)); + + __m128 Mul00 = _mm_mul_ps(Swp00, Swp01); + __m128 Mul01 = _mm_mul_ps(Swp02, Swp03); + Fac1 = _mm_sub_ps(Mul00, Mul01); + } + + + __m128 Fac2; + { + // valType SubFactor02 = m[2][1] * m[3][2] - m[3][1] * m[2][2]; + // valType SubFactor02 = m[2][1] * m[3][2] - m[3][1] * m[2][2]; + // valType SubFactor08 = m[1][1] * m[3][2] - m[3][1] * m[1][2]; + // valType SubFactor15 = m[1][1] * m[2][2] - m[2][1] * m[1][2]; + + __m128 Swp0a = _mm_shuffle_ps(in[3], in[2], _MM_SHUFFLE(2, 2, 2, 2)); + __m128 Swp0b = _mm_shuffle_ps(in[3], in[2], _MM_SHUFFLE(1, 1, 1, 1)); + + __m128 Swp00 = _mm_shuffle_ps(in[2], in[1], _MM_SHUFFLE(1, 1, 1, 1)); + __m128 Swp01 = _mm_shuffle_ps(Swp0a, Swp0a, _MM_SHUFFLE(2, 0, 0, 0)); + __m128 Swp02 = _mm_shuffle_ps(Swp0b, Swp0b, _MM_SHUFFLE(2, 0, 0, 0)); + __m128 Swp03 = _mm_shuffle_ps(in[2], in[1], _MM_SHUFFLE(2, 2, 2, 2)); + + __m128 Mul00 = _mm_mul_ps(Swp00, Swp01); + __m128 Mul01 = _mm_mul_ps(Swp02, Swp03); + Fac2 = _mm_sub_ps(Mul00, Mul01); + } + + __m128 Fac3; + { + // valType SubFactor03 = m[2][0] * m[3][3] - m[3][0] * m[2][3]; + // valType SubFactor03 = m[2][0] * m[3][3] - m[3][0] * m[2][3]; + // valType SubFactor09 = m[1][0] * m[3][3] - m[3][0] * m[1][3]; + // valType SubFactor16 = m[1][0] * m[2][3] - m[2][0] * m[1][3]; + + __m128 Swp0a = _mm_shuffle_ps(in[3], in[2], _MM_SHUFFLE(3, 3, 3, 3)); + __m128 Swp0b = _mm_shuffle_ps(in[3], in[2], _MM_SHUFFLE(0, 0, 0, 0)); + + __m128 Swp00 = _mm_shuffle_ps(in[2], in[1], _MM_SHUFFLE(0, 0, 0, 0)); + __m128 Swp01 = _mm_shuffle_ps(Swp0a, Swp0a, _MM_SHUFFLE(2, 0, 0, 0)); + __m128 Swp02 = _mm_shuffle_ps(Swp0b, Swp0b, _MM_SHUFFLE(2, 0, 0, 0)); + __m128 Swp03 = _mm_shuffle_ps(in[2], in[1], _MM_SHUFFLE(3, 3, 3, 3)); + + __m128 Mul00 = _mm_mul_ps(Swp00, Swp01); + __m128 Mul01 = _mm_mul_ps(Swp02, Swp03); + Fac3 = _mm_sub_ps(Mul00, Mul01); + } + + __m128 Fac4; + { + // valType SubFactor04 = m[2][0] * m[3][2] - m[3][0] * m[2][2]; + // valType SubFactor04 = m[2][0] * m[3][2] - m[3][0] * m[2][2]; + // valType SubFactor10 = m[1][0] * m[3][2] - m[3][0] * m[1][2]; + // valType SubFactor17 = m[1][0] * m[2][2] - m[2][0] * m[1][2]; + + __m128 Swp0a = _mm_shuffle_ps(in[3], in[2], _MM_SHUFFLE(2, 2, 2, 2)); + __m128 Swp0b = _mm_shuffle_ps(in[3], in[2], _MM_SHUFFLE(0, 0, 0, 0)); + + __m128 Swp00 = _mm_shuffle_ps(in[2], in[1], _MM_SHUFFLE(0, 0, 0, 0)); + __m128 Swp01 = _mm_shuffle_ps(Swp0a, Swp0a, _MM_SHUFFLE(2, 0, 0, 0)); + __m128 Swp02 = _mm_shuffle_ps(Swp0b, Swp0b, _MM_SHUFFLE(2, 0, 0, 0)); + __m128 Swp03 = _mm_shuffle_ps(in[2], in[1], _MM_SHUFFLE(2, 2, 2, 2)); + + __m128 Mul00 = _mm_mul_ps(Swp00, Swp01); + __m128 Mul01 = _mm_mul_ps(Swp02, Swp03); + Fac4 = _mm_sub_ps(Mul00, Mul01); + } + + __m128 Fac5; + { + // valType SubFactor05 = m[2][0] * m[3][1] - m[3][0] * m[2][1]; + // valType SubFactor05 = m[2][0] * m[3][1] - m[3][0] * m[2][1]; + // valType SubFactor12 = m[1][0] * m[3][1] - m[3][0] * m[1][1]; + // valType SubFactor18 = m[1][0] * m[2][1] - m[2][0] * m[1][1]; + + __m128 Swp0a = _mm_shuffle_ps(in[3], in[2], _MM_SHUFFLE(1, 1, 1, 1)); + __m128 Swp0b = _mm_shuffle_ps(in[3], in[2], _MM_SHUFFLE(0, 0, 0, 0)); + + __m128 Swp00 = _mm_shuffle_ps(in[2], in[1], _MM_SHUFFLE(0, 0, 0, 0)); + __m128 Swp01 = _mm_shuffle_ps(Swp0a, Swp0a, _MM_SHUFFLE(2, 0, 0, 0)); + __m128 Swp02 = _mm_shuffle_ps(Swp0b, Swp0b, _MM_SHUFFLE(2, 0, 0, 0)); + __m128 Swp03 = _mm_shuffle_ps(in[2], in[1], _MM_SHUFFLE(1, 1, 1, 1)); + + __m128 Mul00 = _mm_mul_ps(Swp00, Swp01); + __m128 Mul01 = _mm_mul_ps(Swp02, Swp03); + Fac5 = _mm_sub_ps(Mul00, Mul01); + } + + __m128 SignA = _mm_set_ps( 1.0f,-1.0f, 1.0f,-1.0f); + __m128 SignB = _mm_set_ps(-1.0f, 1.0f,-1.0f, 1.0f); + + // m[1][0] + // m[0][0] + // m[0][0] + // m[0][0] + __m128 Temp0 = _mm_shuffle_ps(in[1], in[0], _MM_SHUFFLE(0, 0, 0, 0)); + __m128 Vec0 = _mm_shuffle_ps(Temp0, Temp0, _MM_SHUFFLE(2, 2, 2, 0)); + + // m[1][1] + // m[0][1] + // m[0][1] + // m[0][1] + __m128 Temp1 = _mm_shuffle_ps(in[1], in[0], _MM_SHUFFLE(1, 1, 1, 1)); + __m128 Vec1 = _mm_shuffle_ps(Temp1, Temp1, _MM_SHUFFLE(2, 2, 2, 0)); + + // m[1][2] + // m[0][2] + // m[0][2] + // m[0][2] + __m128 Temp2 = _mm_shuffle_ps(in[1], in[0], _MM_SHUFFLE(2, 2, 2, 2)); + __m128 Vec2 = _mm_shuffle_ps(Temp2, Temp2, _MM_SHUFFLE(2, 2, 2, 0)); + + // m[1][3] + // m[0][3] + // m[0][3] + // m[0][3] + __m128 Temp3 = _mm_shuffle_ps(in[1], in[0], _MM_SHUFFLE(3, 3, 3, 3)); + __m128 Vec3 = _mm_shuffle_ps(Temp3, Temp3, _MM_SHUFFLE(2, 2, 2, 0)); + + // col0 + // + (Vec1[0] * Fac0[0] - Vec2[0] * Fac1[0] + Vec3[0] * Fac2[0]), + // - (Vec1[1] * Fac0[1] - Vec2[1] * Fac1[1] + Vec3[1] * Fac2[1]), + // + (Vec1[2] * Fac0[2] - Vec2[2] * Fac1[2] + Vec3[2] * Fac2[2]), + // - (Vec1[3] * Fac0[3] - Vec2[3] * Fac1[3] + Vec3[3] * Fac2[3]), + __m128 Mul00 = _mm_mul_ps(Vec1, Fac0); + __m128 Mul01 = _mm_mul_ps(Vec2, Fac1); + __m128 Mul02 = _mm_mul_ps(Vec3, Fac2); + __m128 Sub00 = _mm_sub_ps(Mul00, Mul01); + __m128 Add00 = _mm_add_ps(Sub00, Mul02); + __m128 Inv0 = _mm_mul_ps(SignB, Add00); + + // col1 + // - (Vec0[0] * Fac0[0] - Vec2[0] * Fac3[0] + Vec3[0] * Fac4[0]), + // + (Vec0[0] * Fac0[1] - Vec2[1] * Fac3[1] + Vec3[1] * Fac4[1]), + // - (Vec0[0] * Fac0[2] - Vec2[2] * Fac3[2] + Vec3[2] * Fac4[2]), + // + (Vec0[0] * Fac0[3] - Vec2[3] * Fac3[3] + Vec3[3] * Fac4[3]), + __m128 Mul03 = _mm_mul_ps(Vec0, Fac0); + __m128 Mul04 = _mm_mul_ps(Vec2, Fac3); + __m128 Mul05 = _mm_mul_ps(Vec3, Fac4); + __m128 Sub01 = _mm_sub_ps(Mul03, Mul04); + __m128 Add01 = _mm_add_ps(Sub01, Mul05); + __m128 Inv1 = _mm_mul_ps(SignA, Add01); + + // col2 + // + (Vec0[0] * Fac1[0] - Vec1[0] * Fac3[0] + Vec3[0] * Fac5[0]), + // - (Vec0[0] * Fac1[1] - Vec1[1] * Fac3[1] + Vec3[1] * Fac5[1]), + // + (Vec0[0] * Fac1[2] - Vec1[2] * Fac3[2] + Vec3[2] * Fac5[2]), + // - (Vec0[0] * Fac1[3] - Vec1[3] * Fac3[3] + Vec3[3] * Fac5[3]), + __m128 Mul06 = _mm_mul_ps(Vec0, Fac1); + __m128 Mul07 = _mm_mul_ps(Vec1, Fac3); + __m128 Mul08 = _mm_mul_ps(Vec3, Fac5); + __m128 Sub02 = _mm_sub_ps(Mul06, Mul07); + __m128 Add02 = _mm_add_ps(Sub02, Mul08); + __m128 Inv2 = _mm_mul_ps(SignB, Add02); + + // col3 + // - (Vec1[0] * Fac2[0] - Vec1[0] * Fac4[0] + Vec2[0] * Fac5[0]), + // + (Vec1[0] * Fac2[1] - Vec1[1] * Fac4[1] + Vec2[1] * Fac5[1]), + // - (Vec1[0] * Fac2[2] - Vec1[2] * Fac4[2] + Vec2[2] * Fac5[2]), + // + (Vec1[0] * Fac2[3] - Vec1[3] * Fac4[3] + Vec2[3] * Fac5[3])); + __m128 Mul09 = _mm_mul_ps(Vec0, Fac2); + __m128 Mul10 = _mm_mul_ps(Vec1, Fac4); + __m128 Mul11 = _mm_mul_ps(Vec2, Fac5); + __m128 Sub03 = _mm_sub_ps(Mul09, Mul10); + __m128 Add03 = _mm_add_ps(Sub03, Mul11); + __m128 Inv3 = _mm_mul_ps(SignA, Add03); + + __m128 Row0 = _mm_shuffle_ps(Inv0, Inv1, _MM_SHUFFLE(0, 0, 0, 0)); + __m128 Row1 = _mm_shuffle_ps(Inv2, Inv3, _MM_SHUFFLE(0, 0, 0, 0)); + __m128 Row2 = _mm_shuffle_ps(Row0, Row1, _MM_SHUFFLE(2, 0, 2, 0)); + + // valType Determinant = m[0][0] * Inverse[0][0] + // + m[0][1] * Inverse[1][0] + // + m[0][2] * Inverse[2][0] + // + m[0][3] * Inverse[3][0]; + __m128 Det0 = glm_vec4_dot(in[0], Row2); + __m128 Rcp0 = _mm_rcp_ps(Det0); + //__m128 Rcp0 = _mm_div_ps(one, Det0); + // Inverse /= Determinant; + out[0] = _mm_mul_ps(Inv0, Rcp0); + out[1] = _mm_mul_ps(Inv1, Rcp0); + out[2] = _mm_mul_ps(Inv2, Rcp0); + out[3] = _mm_mul_ps(Inv3, Rcp0); +} +/* +GLM_FUNC_QUALIFIER void glm_mat4_rotate(__m128 const in[4], float Angle, float const v[3], __m128 out[4]) +{ + float a = glm::radians(Angle); + float c = cos(a); + float s = sin(a); + + glm::vec4 AxisA(v[0], v[1], v[2], float(0)); + __m128 AxisB = _mm_set_ps(AxisA.w, AxisA.z, AxisA.y, AxisA.x); + __m128 AxisC = detail::sse_nrm_ps(AxisB); + + __m128 Cos0 = _mm_set_ss(c); + __m128 CosA = _mm_shuffle_ps(Cos0, Cos0, _MM_SHUFFLE(0, 0, 0, 0)); + __m128 Sin0 = _mm_set_ss(s); + __m128 SinA = _mm_shuffle_ps(Sin0, Sin0, _MM_SHUFFLE(0, 0, 0, 0)); + + // vec<3, T, Q> temp = (valType(1) - c) * axis; + __m128 Temp0 = _mm_sub_ps(one, CosA); + __m128 Temp1 = _mm_mul_ps(Temp0, AxisC); + + //Rotate[0][0] = c + temp[0] * axis[0]; + //Rotate[0][1] = 0 + temp[0] * axis[1] + s * axis[2]; + //Rotate[0][2] = 0 + temp[0] * axis[2] - s * axis[1]; + __m128 Axis0 = _mm_shuffle_ps(AxisC, AxisC, _MM_SHUFFLE(0, 0, 0, 0)); + __m128 TmpA0 = _mm_mul_ps(Axis0, AxisC); + __m128 CosA0 = _mm_shuffle_ps(Cos0, Cos0, _MM_SHUFFLE(1, 1, 1, 0)); + __m128 TmpA1 = _mm_add_ps(CosA0, TmpA0); + __m128 SinA0 = SinA;//_mm_set_ps(0.0f, s, -s, 0.0f); + __m128 TmpA2 = _mm_shuffle_ps(AxisC, AxisC, _MM_SHUFFLE(3, 1, 2, 3)); + __m128 TmpA3 = _mm_mul_ps(SinA0, TmpA2); + __m128 TmpA4 = _mm_add_ps(TmpA1, TmpA3); + + //Rotate[1][0] = 0 + temp[1] * axis[0] - s * axis[2]; + //Rotate[1][1] = c + temp[1] * axis[1]; + //Rotate[1][2] = 0 + temp[1] * axis[2] + s * axis[0]; + __m128 Axis1 = _mm_shuffle_ps(AxisC, AxisC, _MM_SHUFFLE(1, 1, 1, 1)); + __m128 TmpB0 = _mm_mul_ps(Axis1, AxisC); + __m128 CosA1 = _mm_shuffle_ps(Cos0, Cos0, _MM_SHUFFLE(1, 1, 0, 1)); + __m128 TmpB1 = _mm_add_ps(CosA1, TmpB0); + __m128 SinB0 = SinA;//_mm_set_ps(-s, 0.0f, s, 0.0f); + __m128 TmpB2 = _mm_shuffle_ps(AxisC, AxisC, _MM_SHUFFLE(3, 0, 3, 2)); + __m128 TmpB3 = _mm_mul_ps(SinA0, TmpB2); + __m128 TmpB4 = _mm_add_ps(TmpB1, TmpB3); + + //Rotate[2][0] = 0 + temp[2] * axis[0] + s * axis[1]; + //Rotate[2][1] = 0 + temp[2] * axis[1] - s * axis[0]; + //Rotate[2][2] = c + temp[2] * axis[2]; + __m128 Axis2 = _mm_shuffle_ps(AxisC, AxisC, _MM_SHUFFLE(2, 2, 2, 2)); + __m128 TmpC0 = _mm_mul_ps(Axis2, AxisC); + __m128 CosA2 = _mm_shuffle_ps(Cos0, Cos0, _MM_SHUFFLE(1, 0, 1, 1)); + __m128 TmpC1 = _mm_add_ps(CosA2, TmpC0); + __m128 SinC0 = SinA;//_mm_set_ps(s, -s, 0.0f, 0.0f); + __m128 TmpC2 = _mm_shuffle_ps(AxisC, AxisC, _MM_SHUFFLE(3, 3, 0, 1)); + __m128 TmpC3 = _mm_mul_ps(SinA0, TmpC2); + __m128 TmpC4 = _mm_add_ps(TmpC1, TmpC3); + + __m128 Result[4]; + Result[0] = TmpA4; + Result[1] = TmpB4; + Result[2] = TmpC4; + Result[3] = _mm_set_ps(1, 0, 0, 0); + + //mat<4, 4, valType> Result; + //Result[0] = m[0] * Rotate[0][0] + m[1] * Rotate[0][1] + m[2] * Rotate[0][2]; + //Result[1] = m[0] * Rotate[1][0] + m[1] * Rotate[1][1] + m[2] * Rotate[1][2]; + //Result[2] = m[0] * Rotate[2][0] + m[1] * Rotate[2][1] + m[2] * Rotate[2][2]; + //Result[3] = m[3]; + //return Result; + sse_mul_ps(in, Result, out); +} +*/ +GLM_FUNC_QUALIFIER void glm_mat4_outerProduct(__m128 const& c, __m128 const& r, __m128 out[4]) +{ + out[0] = _mm_mul_ps(c, _mm_shuffle_ps(r, r, _MM_SHUFFLE(0, 0, 0, 0))); + out[1] = _mm_mul_ps(c, _mm_shuffle_ps(r, r, _MM_SHUFFLE(1, 1, 1, 1))); + out[2] = _mm_mul_ps(c, _mm_shuffle_ps(r, r, _MM_SHUFFLE(2, 2, 2, 2))); + out[3] = _mm_mul_ps(c, _mm_shuffle_ps(r, r, _MM_SHUFFLE(3, 3, 3, 3))); +} + +#endif//GLM_ARCH & GLM_ARCH_SSE2_BIT diff --git a/src/GLMath/glm/simd/packing.h b/src/GLMath/glm/simd/packing.h new file mode 100644 index 000000000..609163eb0 --- /dev/null +++ b/src/GLMath/glm/simd/packing.h @@ -0,0 +1,8 @@ +/// @ref simd +/// @file glm/simd/packing.h + +#pragma once + +#if GLM_ARCH & GLM_ARCH_SSE2_BIT + +#endif//GLM_ARCH & GLM_ARCH_SSE2_BIT diff --git a/src/GLMath/glm/simd/platform.h b/src/GLMath/glm/simd/platform.h new file mode 100644 index 000000000..c7b6afd2b --- /dev/null +++ b/src/GLMath/glm/simd/platform.h @@ -0,0 +1,366 @@ +#pragma once + +/////////////////////////////////////////////////////////////////////////////////// +// Platform + +#define GLM_PLATFORM_UNKNOWN 0x00000000 +#define GLM_PLATFORM_WINDOWS 0x00010000 +#define GLM_PLATFORM_LINUX 0x00020000 +#define GLM_PLATFORM_APPLE 0x00040000 +//#define GLM_PLATFORM_IOS 0x00080000 +#define GLM_PLATFORM_ANDROID 0x00100000 +#define GLM_PLATFORM_CHROME_NACL 0x00200000 +#define GLM_PLATFORM_UNIX 0x00400000 +#define GLM_PLATFORM_QNXNTO 0x00800000 +#define GLM_PLATFORM_WINCE 0x01000000 +#define GLM_PLATFORM_CYGWIN 0x02000000 + +#ifdef GLM_FORCE_PLATFORM_UNKNOWN +# define GLM_PLATFORM GLM_PLATFORM_UNKNOWN +#elif defined(__CYGWIN__) +# define GLM_PLATFORM GLM_PLATFORM_CYGWIN +#elif defined(__QNXNTO__) +# define GLM_PLATFORM GLM_PLATFORM_QNXNTO +#elif defined(__APPLE__) +# define GLM_PLATFORM GLM_PLATFORM_APPLE +#elif defined(WINCE) +# define GLM_PLATFORM GLM_PLATFORM_WINCE +#elif defined(_WIN32) +# define GLM_PLATFORM GLM_PLATFORM_WINDOWS +#elif defined(__native_client__) +# define GLM_PLATFORM GLM_PLATFORM_CHROME_NACL +#elif defined(__ANDROID__) +# define GLM_PLATFORM GLM_PLATFORM_ANDROID +#elif defined(__linux) +# define GLM_PLATFORM GLM_PLATFORM_LINUX +#elif defined(__unix) +# define GLM_PLATFORM GLM_PLATFORM_UNIX +#else +# define GLM_PLATFORM GLM_PLATFORM_UNKNOWN +#endif// + +/////////////////////////////////////////////////////////////////////////////////// +// Compiler + +#define GLM_COMPILER_UNKNOWN 0x00000000 + +// Intel +#define GLM_COMPILER_INTEL 0x00100000 +#define GLM_COMPILER_INTEL14 0x00100040 +#define GLM_COMPILER_INTEL15 0x00100050 +#define GLM_COMPILER_INTEL16 0x00100060 +#define GLM_COMPILER_INTEL17 0x00100070 + +// Visual C++ defines +#define GLM_COMPILER_VC 0x01000000 +#define GLM_COMPILER_VC12 0x01000001 +#define GLM_COMPILER_VC14 0x01000002 +#define GLM_COMPILER_VC15 0x01000003 +#define GLM_COMPILER_VC15_3 0x01000004 +#define GLM_COMPILER_VC15_5 0x01000005 +#define GLM_COMPILER_VC15_6 0x01000006 +#define GLM_COMPILER_VC15_7 0x01000007 + +// GCC defines +#define GLM_COMPILER_GCC 0x02000000 +#define GLM_COMPILER_GCC46 0x020000D0 +#define GLM_COMPILER_GCC47 0x020000E0 +#define GLM_COMPILER_GCC48 0x020000F0 +#define GLM_COMPILER_GCC49 0x02000100 +#define GLM_COMPILER_GCC5 0x02000200 +#define GLM_COMPILER_GCC6 0x02000300 +#define GLM_COMPILER_GCC7 0x02000400 +#define GLM_COMPILER_GCC8 0x02000500 + +// CUDA +#define GLM_COMPILER_CUDA 0x10000000 +#define GLM_COMPILER_CUDA70 0x100000A0 +#define GLM_COMPILER_CUDA75 0x100000B0 +#define GLM_COMPILER_CUDA80 0x100000C0 + +// Clang +#define GLM_COMPILER_CLANG 0x20000000 +#define GLM_COMPILER_CLANG34 0x20000050 +#define GLM_COMPILER_CLANG35 0x20000060 +#define GLM_COMPILER_CLANG36 0x20000070 +#define GLM_COMPILER_CLANG37 0x20000080 +#define GLM_COMPILER_CLANG38 0x20000090 +#define GLM_COMPILER_CLANG39 0x200000A0 +#define GLM_COMPILER_CLANG40 0x200000B0 +#define GLM_COMPILER_CLANG41 0x200000C0 +#define GLM_COMPILER_CLANG42 0x200000D0 + +// Build model +#define GLM_MODEL_32 0x00000010 +#define GLM_MODEL_64 0x00000020 + +// Force generic C++ compiler +#ifdef GLM_FORCE_COMPILER_UNKNOWN +# define GLM_COMPILER GLM_COMPILER_UNKNOWN + +#elif defined(__INTEL_COMPILER) +# if (__INTEL_COMPILER < 1400) +# error "GLM requires ICC 2013 SP1 or newer" +# elif __INTEL_COMPILER == 1400 +# define GLM_COMPILER GLM_COMPILER_INTEL14 +# elif __INTEL_COMPILER == 1500 +# define GLM_COMPILER GLM_COMPILER_INTEL15 +# elif __INTEL_COMPILER == 1600 +# define GLM_COMPILER GLM_COMPILER_INTEL16 +# elif __INTEL_COMPILER >= 1700 +# define GLM_COMPILER GLM_COMPILER_INTEL17 +# endif + +// CUDA +#elif defined(__CUDACC__) +# if !defined(CUDA_VERSION) && !defined(GLM_FORCE_CUDA) +# include // make sure version is defined since nvcc does not define it itself! +# endif +# if CUDA_VERSION < 7000 +# error "GLM requires CUDA 7.0 or higher" +# elif (CUDA_VERSION >= 7000 && CUDA_VERSION < 7500) +# define GLM_COMPILER GLM_COMPILER_CUDA70 +# elif (CUDA_VERSION >= 7500 && CUDA_VERSION < 8000) +# define GLM_COMPILER GLM_COMPILER_CUDA75 +# elif (CUDA_VERSION >= 8000) +# define GLM_COMPILER GLM_COMPILER_CUDA80 +# endif + +// Clang +#elif defined(__clang__) +# if defined(__apple_build_version__) +# if (__clang_major__ < 6) +# error "GLM requires Clang 3.4 / Apple Clang 6.0 or higher" +# elif __clang_major__ == 6 && __clang_minor__ == 0 +# define GLM_COMPILER GLM_COMPILER_CLANG35 +# elif __clang_major__ == 6 && __clang_minor__ >= 1 +# define GLM_COMPILER GLM_COMPILER_CLANG36 +# elif __clang_major__ >= 7 +# define GLM_COMPILER GLM_COMPILER_CLANG37 +# endif +# else +# if ((__clang_major__ == 3) && (__clang_minor__ < 4)) || (__clang_major__ < 3) +# error "GLM requires Clang 3.4 or higher" +# elif __clang_major__ == 3 && __clang_minor__ == 4 +# define GLM_COMPILER GLM_COMPILER_CLANG34 +# elif __clang_major__ == 3 && __clang_minor__ == 5 +# define GLM_COMPILER GLM_COMPILER_CLANG35 +# elif __clang_major__ == 3 && __clang_minor__ == 6 +# define GLM_COMPILER GLM_COMPILER_CLANG36 +# elif __clang_major__ == 3 && __clang_minor__ == 7 +# define GLM_COMPILER GLM_COMPILER_CLANG37 +# elif __clang_major__ == 3 && __clang_minor__ == 8 +# define GLM_COMPILER GLM_COMPILER_CLANG38 +# elif __clang_major__ == 3 && __clang_minor__ >= 9 +# define GLM_COMPILER GLM_COMPILER_CLANG39 +# elif __clang_major__ == 4 && __clang_minor__ == 0 +# define GLM_COMPILER GLM_COMPILER_CLANG40 +# elif __clang_major__ == 4 && __clang_minor__ == 1 +# define GLM_COMPILER GLM_COMPILER_CLANG41 +# elif __clang_major__ == 4 && __clang_minor__ >= 2 +# define GLM_COMPILER GLM_COMPILER_CLANG42 +# elif __clang_major__ >= 4 +# define GLM_COMPILER GLM_COMPILER_CLANG42 +# endif +# endif + +// Visual C++ +#elif defined(_MSC_VER) +# if _MSC_VER < 1800 +# error "GLM requires Visual C++ 12 - 2013 or higher" +# elif _MSC_VER == 1800 +# define GLM_COMPILER GLM_COMPILER_VC12 +# elif _MSC_VER == 1900 +# define GLM_COMPILER GLM_COMPILER_VC14 +# elif _MSC_VER == 1910 +# define GLM_COMPILER GLM_COMPILER_VC15 +# elif _MSC_VER == 1911 +# define GLM_COMPILER GLM_COMPILER_VC15_3 +# elif _MSC_VER == 1912 +# define GLM_COMPILER GLM_COMPILER_VC15_5 +# elif _MSC_VER == 1913 +# define GLM_COMPILER GLM_COMPILER_VC15_6 +# elif _MSC_VER >= 1914 +# define GLM_COMPILER GLM_COMPILER_VC15_7 +# endif//_MSC_VER + +// G++ +#elif defined(__GNUC__) || defined(__MINGW32__) +# if ((__GNUC__ == 4) && (__GNUC_MINOR__ < 6)) || (__GNUC__ < 4) +# error "GLM requires GCC 4.7 or higher" +# elif (__GNUC__ == 4) && (__GNUC_MINOR__ == 6) +# define GLM_COMPILER (GLM_COMPILER_GCC46) +# elif (__GNUC__ == 4) && (__GNUC_MINOR__ == 7) +# define GLM_COMPILER (GLM_COMPILER_GCC47) +# elif (__GNUC__ == 4) && (__GNUC_MINOR__ == 8) +# define GLM_COMPILER (GLM_COMPILER_GCC48) +# elif (__GNUC__ == 4) && (__GNUC_MINOR__ >= 9) +# define GLM_COMPILER (GLM_COMPILER_GCC49) +# elif (__GNUC__ == 5) +# define GLM_COMPILER (GLM_COMPILER_GCC5) +# elif (__GNUC__ == 6) +# define GLM_COMPILER (GLM_COMPILER_GCC6) +# elif (__GNUC__ == 7) +# define GLM_COMPILER (GLM_COMPILER_GCC7) +# elif (__GNUC__ >= 8) +# define GLM_COMPILER (GLM_COMPILER_GCC8) +# endif + +#else +# define GLM_COMPILER GLM_COMPILER_UNKNOWN +#endif + +#ifndef GLM_COMPILER +# error "GLM_COMPILER undefined, your compiler may not be supported by GLM. Add #define GLM_COMPILER 0 to ignore this message." +#endif//GLM_COMPILER + +/////////////////////////////////////////////////////////////////////////////////// +// Instruction sets + +// User defines: GLM_FORCE_PURE GLM_FORCE_INTRINSICS GLM_FORCE_SSE2 GLM_FORCE_SSE3 GLM_FORCE_AVX GLM_FORCE_AVX2 GLM_FORCE_AVX2 + +#define GLM_ARCH_MIPS_BIT (0x10000000) +#define GLM_ARCH_PPC_BIT (0x20000000) +#define GLM_ARCH_ARM_BIT (0x40000000) +#define GLM_ARCH_X86_BIT (0x80000000) + +#define GLM_ARCH_SIMD_BIT (0x00001000) + +#define GLM_ARCH_NEON_BIT (0x00000001) +#define GLM_ARCH_SSE_BIT (0x00000002) +#define GLM_ARCH_SSE2_BIT (0x00000004) +#define GLM_ARCH_SSE3_BIT (0x00000008) +#define GLM_ARCH_SSSE3_BIT (0x00000010) +#define GLM_ARCH_SSE41_BIT (0x00000020) +#define GLM_ARCH_SSE42_BIT (0x00000040) +#define GLM_ARCH_AVX_BIT (0x00000080) +#define GLM_ARCH_AVX2_BIT (0x00000100) + +#define GLM_ARCH_UNKNOWN (0) +#define GLM_ARCH_X86 (GLM_ARCH_X86_BIT) +#define GLM_ARCH_SSE (GLM_ARCH_SSE_BIT | GLM_ARCH_SIMD_BIT | GLM_ARCH_X86) +#define GLM_ARCH_SSE2 (GLM_ARCH_SSE2_BIT | GLM_ARCH_SSE) +#define GLM_ARCH_SSE3 (GLM_ARCH_SSE3_BIT | GLM_ARCH_SSE2) +#define GLM_ARCH_SSSE3 (GLM_ARCH_SSSE3_BIT | GLM_ARCH_SSE3) +#define GLM_ARCH_SSE41 (GLM_ARCH_SSE41_BIT | GLM_ARCH_SSSE3) +#define GLM_ARCH_SSE42 (GLM_ARCH_SSE42_BIT | GLM_ARCH_SSE41) +#define GLM_ARCH_AVX (GLM_ARCH_AVX_BIT | GLM_ARCH_SSE42) +#define GLM_ARCH_AVX2 (GLM_ARCH_AVX2_BIT | GLM_ARCH_AVX) +#define GLM_ARCH_ARM (GLM_ARCH_ARM_BIT) +#define GLM_ARCH_NEON (GLM_ARCH_NEON_BIT | GLM_ARCH_SIMD_BIT | GLM_ARCH_ARM) +#define GLM_ARCH_MIPS (GLM_ARCH_MIPS_BIT) +#define GLM_ARCH_PPC (GLM_ARCH_PPC_BIT) + +#if defined(GLM_FORCE_ARCH_UNKNOWN) || defined(GLM_FORCE_PURE) +# define GLM_ARCH GLM_ARCH_UNKNOWN +#elif defined(GLM_FORCE_NEON) +# define GLM_ARCH (GLM_ARCH_NEON) +# define GLM_FORCE_INTRINSICS +#elif defined(GLM_FORCE_AVX2) +# define GLM_ARCH (GLM_ARCH_AVX2) +# define GLM_FORCE_INTRINSICS +#elif defined(GLM_FORCE_AVX) +# define GLM_ARCH (GLM_ARCH_AVX) +# define GLM_FORCE_INTRINSICS +#elif defined(GLM_FORCE_SSE42) +# define GLM_ARCH (GLM_ARCH_SSE42) +# define GLM_FORCE_INTRINSICS +#elif defined(GLM_FORCE_SSE41) +# define GLM_ARCH (GLM_ARCH_SSE41) +# define GLM_FORCE_INTRINSICS +#elif defined(GLM_FORCE_SSSE3) +# define GLM_ARCH (GLM_ARCH_SSSE3) +# define GLM_FORCE_INTRINSICS +#elif defined(GLM_FORCE_SSE3) +# define GLM_ARCH (GLM_ARCH_SSE3) +# define GLM_FORCE_INTRINSICS +#elif defined(GLM_FORCE_SSE2) +# define GLM_ARCH (GLM_ARCH_SSE2) +# define GLM_FORCE_INTRINSICS +#elif defined(GLM_FORCE_SSE) +# define GLM_ARCH (GLM_ARCH_SSE) +# define GLM_FORCE_INTRINSICS +#elif defined(GLM_FORCE_INTRINSICS) && !defined(GLM_FORCE_XYZW_ONLY) +# if defined(__AVX2__) +# define GLM_ARCH (GLM_ARCH_AVX2) +# elif defined(__AVX__) +# define GLM_ARCH (GLM_ARCH_AVX) +# elif defined(__SSE4_2__) +# define GLM_ARCH (GLM_ARCH_SSE42) +# elif defined(__SSE4_1__) +# define GLM_ARCH (GLM_ARCH_SSE41) +# elif defined(__SSSE3__) +# define GLM_ARCH (GLM_ARCH_SSSE3) +# elif defined(__SSE3__) +# define GLM_ARCH (GLM_ARCH_SSE3) +# elif defined(__SSE2__) || defined(__x86_64__) || defined(_M_X64) || defined(_M_IX86_FP) +# define GLM_ARCH (GLM_ARCH_SSE2) +# elif defined(__i386__) +# define GLM_ARCH (GLM_ARCH_X86) +# elif defined(__ARM_NEON) +# define GLM_ARCH (GLM_ARCH_ARM | GLM_ARCH_NEON) +# elif defined(__arm__ ) || defined(_M_ARM) +# define GLM_ARCH (GLM_ARCH_ARM) +# elif defined(__mips__ ) +# define GLM_ARCH (GLM_ARCH_MIPS) +# elif defined(__powerpc__ ) || defined(_M_PPC) +# define GLM_ARCH (GLM_ARCH_PPC) +# else +# define GLM_ARCH (GLM_ARCH_UNKNOWN) +# endif +#else +# if defined(__x86_64__) || defined(_M_X64) || defined(_M_IX86) || defined(__i386__) +# define GLM_ARCH (GLM_ARCH_X86) +# elif defined(__arm__) || defined(_M_ARM) +# define GLM_ARCH (GLM_ARCH_ARM) +# elif defined(__powerpc__) || defined(_M_PPC) +# define GLM_ARCH (GLM_ARCH_PPC) +# elif defined(__mips__) +# define GLM_ARCH (GLM_ARCH_MIPS) +# else +# define GLM_ARCH (GLM_ARCH_UNKNOWN) +# endif +#endif + +#if GLM_ARCH & GLM_ARCH_AVX2_BIT +# include +#elif GLM_ARCH & GLM_ARCH_AVX_BIT +# include +#elif GLM_ARCH & GLM_ARCH_SSE42_BIT +# if GLM_COMPILER & GLM_COMPILER_CLANG +# include +# endif +# include +#elif GLM_ARCH & GLM_ARCH_SSE41_BIT +# include +#elif GLM_ARCH & GLM_ARCH_SSSE3_BIT +# include +#elif GLM_ARCH & GLM_ARCH_SSE3_BIT +# include +#elif GLM_ARCH & GLM_ARCH_SSE2_BIT +# include +#endif//GLM_ARCH + +#if GLM_ARCH & GLM_ARCH_SSE2_BIT + typedef __m128 glm_f32vec4; + typedef __m128i glm_i32vec4; + typedef __m128i glm_u32vec4; + typedef __m128d glm_f64vec2; + typedef __m128i glm_i64vec2; + typedef __m128i glm_u64vec2; + + typedef glm_f32vec4 glm_vec4; + typedef glm_i32vec4 glm_ivec4; + typedef glm_u32vec4 glm_uvec4; + typedef glm_f64vec2 glm_dvec2; +#endif + +#if GLM_ARCH & GLM_ARCH_AVX_BIT + typedef __m256d glm_f64vec4; + typedef glm_f64vec4 glm_dvec4; +#endif + +#if GLM_ARCH & GLM_ARCH_AVX2_BIT + typedef __m256i glm_i64vec4; + typedef __m256i glm_u64vec4; +#endif diff --git a/src/GLMath/glm/simd/trigonometric.h b/src/GLMath/glm/simd/trigonometric.h new file mode 100644 index 000000000..739b796e7 --- /dev/null +++ b/src/GLMath/glm/simd/trigonometric.h @@ -0,0 +1,9 @@ +/// @ref simd +/// @file glm/simd/trigonometric.h + +#pragma once + +#if GLM_ARCH & GLM_ARCH_SSE2_BIT + +#endif//GLM_ARCH & GLM_ARCH_SSE2_BIT + diff --git a/src/GLMath/glm/simd/vector_relational.h b/src/GLMath/glm/simd/vector_relational.h new file mode 100644 index 000000000..f7385e974 --- /dev/null +++ b/src/GLMath/glm/simd/vector_relational.h @@ -0,0 +1,8 @@ +/// @ref simd +/// @file glm/simd/vector_relational.h + +#pragma once + +#if GLM_ARCH & GLM_ARCH_SSE2_BIT + +#endif//GLM_ARCH & GLM_ARCH_SSE2_BIT diff --git a/src/GLMath/glm/trigonometric.hpp b/src/GLMath/glm/trigonometric.hpp new file mode 100644 index 000000000..fcf07f899 --- /dev/null +++ b/src/GLMath/glm/trigonometric.hpp @@ -0,0 +1,210 @@ +/// @ref core +/// @file glm/trigonometric.hpp +/// +/// @see GLSL 4.20.8 specification, section 8.1 Angle and Trigonometry Functions +/// +/// @defgroup core_func_trigonometric Angle and Trigonometry Functions +/// @ingroup core +/// +/// Function parameters specified as angle are assumed to be in units of radians. +/// In no case will any of these functions result in a divide by zero error. If +/// the divisor of a ratio is 0, then results will be undefined. +/// +/// These all operate component-wise. The description is per component. +/// +/// Include to use these core features. +/// +/// @see ext_vector_trigonometric + +#pragma once + +#include "detail/setup.hpp" +#include "detail/qualifier.hpp" + +namespace glm +{ + /// @addtogroup core_func_trigonometric + /// @{ + + /// Converts degrees to radians and returns the result. + /// + /// @tparam L Integer between 1 and 4 included that qualify the dimension of the vector + /// @tparam T Floating-point scalar types + /// @tparam Q Value from qualifier enum + /// + /// @see GLSL radians man page + /// @see GLSL 4.20.8 specification, section 8.1 Angle and Trigonometry Functions + template + GLM_FUNC_DECL GLM_CONSTEXPR vec radians(vec const& degrees); + + /// Converts radians to degrees and returns the result. + /// + /// @tparam L Integer between 1 and 4 included that qualify the dimension of the vector + /// @tparam T Floating-point scalar types + /// @tparam Q Value from qualifier enum + /// + /// @see GLSL degrees man page + /// @see GLSL 4.20.8 specification, section 8.1 Angle and Trigonometry Functions + template + GLM_FUNC_DECL GLM_CONSTEXPR vec degrees(vec const& radians); + + /// The standard trigonometric sine function. + /// The values returned by this function will range from [-1, 1]. + /// + /// @tparam L Integer between 1 and 4 included that qualify the dimension of the vector + /// @tparam T Floating-point scalar types + /// @tparam Q Value from qualifier enum + /// + /// @see GLSL sin man page + /// @see GLSL 4.20.8 specification, section 8.1 Angle and Trigonometry Functions + template + GLM_FUNC_DECL vec sin(vec const& angle); + + /// The standard trigonometric cosine function. + /// The values returned by this function will range from [-1, 1]. + /// + /// @tparam L Integer between 1 and 4 included that qualify the dimension of the vector + /// @tparam T Floating-point scalar types + /// @tparam Q Value from qualifier enum + /// + /// @see GLSL cos man page + /// @see GLSL 4.20.8 specification, section 8.1 Angle and Trigonometry Functions + template + GLM_FUNC_DECL vec cos(vec const& angle); + + /// The standard trigonometric tangent function. + /// + /// @tparam L Integer between 1 and 4 included that qualify the dimension of the vector + /// @tparam T Floating-point scalar types + /// @tparam Q Value from qualifier enum + /// + /// @see GLSL tan man page + /// @see GLSL 4.20.8 specification, section 8.1 Angle and Trigonometry Functions + template + GLM_FUNC_DECL vec tan(vec const& angle); + + /// Arc sine. Returns an angle whose sine is x. + /// The range of values returned by this function is [-PI/2, PI/2]. + /// Results are undefined if |x| > 1. + /// + /// @tparam L Integer between 1 and 4 included that qualify the dimension of the vector + /// @tparam T Floating-point scalar types + /// @tparam Q Value from qualifier enum + /// + /// @see GLSL asin man page + /// @see GLSL 4.20.8 specification, section 8.1 Angle and Trigonometry Functions + template + GLM_FUNC_DECL vec asin(vec const& x); + + /// Arc cosine. Returns an angle whose sine is x. + /// The range of values returned by this function is [0, PI]. + /// Results are undefined if |x| > 1. + /// + /// @tparam L Integer between 1 and 4 included that qualify the dimension of the vector + /// @tparam T Floating-point scalar types + /// @tparam Q Value from qualifier enum + /// + /// @see GLSL acos man page + /// @see GLSL 4.20.8 specification, section 8.1 Angle and Trigonometry Functions + template + GLM_FUNC_DECL vec acos(vec const& x); + + /// Arc tangent. Returns an angle whose tangent is y/x. + /// The signs of x and y are used to determine what + /// quadrant the angle is in. The range of values returned + /// by this function is [-PI, PI]. Results are undefined + /// if x and y are both 0. + /// + /// @tparam L Integer between 1 and 4 included that qualify the dimension of the vector + /// @tparam T Floating-point scalar types + /// @tparam Q Value from qualifier enum + /// + /// @see GLSL atan man page + /// @see GLSL 4.20.8 specification, section 8.1 Angle and Trigonometry Functions + template + GLM_FUNC_DECL vec atan(vec const& y, vec const& x); + + /// Arc tangent. Returns an angle whose tangent is y_over_x. + /// The range of values returned by this function is [-PI/2, PI/2]. + /// + /// @tparam L Integer between 1 and 4 included that qualify the dimension of the vector + /// @tparam T Floating-point scalar types + /// @tparam Q Value from qualifier enum + /// + /// @see GLSL atan man page + /// @see GLSL 4.20.8 specification, section 8.1 Angle and Trigonometry Functions + template + GLM_FUNC_DECL vec atan(vec const& y_over_x); + + /// Returns the hyperbolic sine function, (exp(x) - exp(-x)) / 2 + /// + /// @tparam L Integer between 1 and 4 included that qualify the dimension of the vector + /// @tparam T Floating-point scalar types + /// @tparam Q Value from qualifier enum + /// + /// @see GLSL sinh man page + /// @see GLSL 4.20.8 specification, section 8.1 Angle and Trigonometry Functions + template + GLM_FUNC_DECL vec sinh(vec const& angle); + + /// Returns the hyperbolic cosine function, (exp(x) + exp(-x)) / 2 + /// + /// @tparam L Integer between 1 and 4 included that qualify the dimension of the vector + /// @tparam T Floating-point scalar types + /// @tparam Q Value from qualifier enum + /// + /// @see GLSL cosh man page + /// @see GLSL 4.20.8 specification, section 8.1 Angle and Trigonometry Functions + template + GLM_FUNC_DECL vec cosh(vec const& angle); + + /// Returns the hyperbolic tangent function, sinh(angle) / cosh(angle) + /// + /// @tparam L Integer between 1 and 4 included that qualify the dimension of the vector + /// @tparam T Floating-point scalar types + /// @tparam Q Value from qualifier enum + /// + /// @see GLSL tanh man page + /// @see GLSL 4.20.8 specification, section 8.1 Angle and Trigonometry Functions + template + GLM_FUNC_DECL vec tanh(vec const& angle); + + /// Arc hyperbolic sine; returns the inverse of sinh. + /// + /// @tparam L Integer between 1 and 4 included that qualify the dimension of the vector + /// @tparam T Floating-point scalar types + /// @tparam Q Value from qualifier enum + /// + /// @see GLSL asinh man page + /// @see GLSL 4.20.8 specification, section 8.1 Angle and Trigonometry Functions + template + GLM_FUNC_DECL vec asinh(vec const& x); + + /// Arc hyperbolic cosine; returns the non-negative inverse + /// of cosh. Results are undefined if x < 1. + /// + /// @tparam L Integer between 1 and 4 included that qualify the dimension of the vector + /// @tparam T Floating-point scalar types + /// @tparam Q Value from qualifier enum + /// + /// @see GLSL acosh man page + /// @see GLSL 4.20.8 specification, section 8.1 Angle and Trigonometry Functions + template + GLM_FUNC_DECL vec acosh(vec const& x); + + /// Arc hyperbolic tangent; returns the inverse of tanh. + /// Results are undefined if abs(x) >= 1. + /// + /// @tparam L Integer between 1 and 4 included that qualify the dimension of the vector + /// @tparam T Floating-point scalar types + /// @tparam Q Value from qualifier enum + /// + /// @see GLSL atanh man page + /// @see GLSL 4.20.8 specification, section 8.1 Angle and Trigonometry Functions + template + GLM_FUNC_DECL vec atanh(vec const& x); + + /// @} +}//namespace glm + +#include "detail/func_trigonometric.inl" diff --git a/src/GLMath/glm/vec2.hpp b/src/GLMath/glm/vec2.hpp new file mode 100644 index 000000000..be768bf5f --- /dev/null +++ b/src/GLMath/glm/vec2.hpp @@ -0,0 +1,14 @@ +/// @ref core +/// @file glm/vec2.hpp + +#pragma once +#include "./ext/vector_bool2.hpp" +#include "./ext/vector_bool2_precision.hpp" +#include "./ext/vector_float2.hpp" +#include "./ext/vector_float2_precision.hpp" +#include "./ext/vector_double2.hpp" +#include "./ext/vector_double2_precision.hpp" +#include "./ext/vector_int2.hpp" +#include "./ext/vector_int2_precision.hpp" +#include "./ext/vector_uint2.hpp" +#include "./ext/vector_uint2_precision.hpp" diff --git a/src/GLMath/glm/vec3.hpp b/src/GLMath/glm/vec3.hpp new file mode 100644 index 000000000..f57072239 --- /dev/null +++ b/src/GLMath/glm/vec3.hpp @@ -0,0 +1,14 @@ +/// @ref core +/// @file glm/vec3.hpp + +#pragma once +#include "./ext/vector_bool3.hpp" +#include "./ext/vector_bool3_precision.hpp" +#include "./ext/vector_float3.hpp" +#include "./ext/vector_float3_precision.hpp" +#include "./ext/vector_double3.hpp" +#include "./ext/vector_double3_precision.hpp" +#include "./ext/vector_int3.hpp" +#include "./ext/vector_int3_precision.hpp" +#include "./ext/vector_uint3.hpp" +#include "./ext/vector_uint3_precision.hpp" diff --git a/src/GLMath/glm/vec4.hpp b/src/GLMath/glm/vec4.hpp new file mode 100644 index 000000000..911702072 --- /dev/null +++ b/src/GLMath/glm/vec4.hpp @@ -0,0 +1,15 @@ +/// @ref core +/// @file glm/vec4.hpp + +#pragma once +#include "./ext/vector_bool4.hpp" +#include "./ext/vector_bool4_precision.hpp" +#include "./ext/vector_float4.hpp" +#include "./ext/vector_float4_precision.hpp" +#include "./ext/vector_double4.hpp" +#include "./ext/vector_double4_precision.hpp" +#include "./ext/vector_int4.hpp" +#include "./ext/vector_int4_precision.hpp" +#include "./ext/vector_uint4.hpp" +#include "./ext/vector_uint4_precision.hpp" + diff --git a/src/GLMath/glm/vector_relational.hpp b/src/GLMath/glm/vector_relational.hpp new file mode 100644 index 000000000..a0fe17eb7 --- /dev/null +++ b/src/GLMath/glm/vector_relational.hpp @@ -0,0 +1,121 @@ +/// @ref core +/// @file glm/vector_relational.hpp +/// +/// @see GLSL 4.20.8 specification, section 8.7 Vector Relational Functions +/// +/// @defgroup core_func_vector_relational Vector Relational Functions +/// @ingroup core +/// +/// Relational and equality operators (<, <=, >, >=, ==, !=) are defined to +/// operate on scalars and produce scalar Boolean results. For vector results, +/// use the following built-in functions. +/// +/// In all cases, the sizes of all the input and return vectors for any particular +/// call must match. +/// +/// Include to use these core features. +/// +/// @see ext_vector_relational + +#pragma once + +#include "detail/qualifier.hpp" +#include "detail/setup.hpp" + +namespace glm +{ + /// @addtogroup core_func_vector_relational + /// @{ + + /// Returns the component-wise comparison result of x < y. + /// + /// @tparam L An integer between 1 and 4 included that qualify the dimension of the vector. + /// @tparam T A floating-point or integer scalar type. + /// + /// @see GLSL lessThan man page + /// @see GLSL 4.20.8 specification, section 8.7 Vector Relational Functions + template + GLM_FUNC_DECL GLM_CONSTEXPR vec lessThan(vec const& x, vec const& y); + + /// Returns the component-wise comparison of result x <= y. + /// + /// @tparam L An integer between 1 and 4 included that qualify the dimension of the vector. + /// @tparam T A floating-point or integer scalar type. + /// + /// @see GLSL lessThanEqual man page + /// @see GLSL 4.20.8 specification, section 8.7 Vector Relational Functions + template + GLM_FUNC_DECL GLM_CONSTEXPR vec lessThanEqual(vec const& x, vec const& y); + + /// Returns the component-wise comparison of result x > y. + /// + /// @tparam L An integer between 1 and 4 included that qualify the dimension of the vector. + /// @tparam T A floating-point or integer scalar type. + /// + /// @see GLSL greaterThan man page + /// @see GLSL 4.20.8 specification, section 8.7 Vector Relational Functions + template + GLM_FUNC_DECL GLM_CONSTEXPR vec greaterThan(vec const& x, vec const& y); + + /// Returns the component-wise comparison of result x >= y. + /// + /// @tparam L An integer between 1 and 4 included that qualify the dimension of the vector. + /// @tparam T A floating-point or integer scalar type. + /// + /// @see GLSL greaterThanEqual man page + /// @see GLSL 4.20.8 specification, section 8.7 Vector Relational Functions + template + GLM_FUNC_DECL GLM_CONSTEXPR vec greaterThanEqual(vec const& x, vec const& y); + + /// Returns the component-wise comparison of result x == y. + /// + /// @tparam L An integer between 1 and 4 included that qualify the dimension of the vector. + /// @tparam T A floating-point, integer or bool scalar type. + /// + /// @see GLSL equal man page + /// @see GLSL 4.20.8 specification, section 8.7 Vector Relational Functions + template + GLM_FUNC_DECL GLM_CONSTEXPR vec equal(vec const& x, vec const& y); + + /// Returns the component-wise comparison of result x != y. + /// + /// @tparam L An integer between 1 and 4 included that qualify the dimension of the vector. + /// @tparam T A floating-point, integer or bool scalar type. + /// + /// @see GLSL notEqual man page + /// @see GLSL 4.20.8 specification, section 8.7 Vector Relational Functions + template + GLM_FUNC_DECL GLM_CONSTEXPR vec notEqual(vec const& x, vec const& y); + + /// Returns true if any component of x is true. + /// + /// @tparam L An integer between 1 and 4 included that qualify the dimension of the vector. + /// + /// @see GLSL any man page + /// @see GLSL 4.20.8 specification, section 8.7 Vector Relational Functions + template + GLM_FUNC_DECL GLM_CONSTEXPR bool any(vec const& v); + + /// Returns true if all components of x are true. + /// + /// @tparam L An integer between 1 and 4 included that qualify the dimension of the vector. + /// + /// @see GLSL all man page + /// @see GLSL 4.20.8 specification, section 8.7 Vector Relational Functions + template + GLM_FUNC_DECL GLM_CONSTEXPR bool all(vec const& v); + + /// Returns the component-wise logical complement of x. + /// /!\ Because of language incompatibilities between C++ and GLSL, GLM defines the function not but not_ instead. + /// + /// @tparam L An integer between 1 and 4 included that qualify the dimension of the vector. + /// + /// @see GLSL not man page + /// @see GLSL 4.20.8 specification, section 8.7 Vector Relational Functions + template + GLM_FUNC_DECL GLM_CONSTEXPR vec not_(vec const& v); + + /// @} +}//namespace glm + +#include "detail/func_vector_relational.inl" -- GitLab From 3ad1deab18b8f9b08ed81e07f2fe0fe506dea0ec Mon Sep 17 00:00:00 2001 From: John Harwell Date: Tue, 25 Jun 2019 11:54:30 -0500 Subject: [PATCH 375/427] For the experimental texture volume drawing, more enhancements including addition of a developer flag to smooth voxel drawing (remove aliasing artifacts) and use of the GLM library to replace some GLU functions. --- .../BrainOpenGLVolumeTextureSliceDrawing.cxx | 426 +++++++++++++----- .../BrainOpenGLVolumeTextureSliceDrawing.h | 15 +- src/Common/DeveloperFlagsEnum.cxx | 5 + src/Common/DeveloperFlagsEnum.h | 3 +- 4 files changed, 334 insertions(+), 115 deletions(-) diff --git a/src/Brain/BrainOpenGLVolumeTextureSliceDrawing.cxx b/src/Brain/BrainOpenGLVolumeTextureSliceDrawing.cxx index 3ed236325..89c1444a2 100644 --- a/src/Brain/BrainOpenGLVolumeTextureSliceDrawing.cxx +++ b/src/Brain/BrainOpenGLVolumeTextureSliceDrawing.cxx @@ -20,6 +20,9 @@ /*LICENSE_END*/ #include +#include +#include +#include #define __BRAIN_OPEN_GL_VOLUME_TEXTURE_SLICE_DRAWING_DECLARE__ #include "BrainOpenGLVolumeTextureSliceDrawing.h" @@ -134,6 +137,59 @@ BrainOpenGLVolumeTextureSliceDrawing::draw(BrainOpenGLFixedPipeline* fixedPipeli return; } + glMatrixMode(GL_PROJECTION); + glPushMatrix(); + + glMatrixMode(GL_MODELVIEW); + glPushMatrix(); + + drawPrivate(fixedPipelineDrawing, + browserTabContent, + volumeDrawInfo, + sliceDrawingType, + sliceProjectionType, + obliqueSliceMaskingType, + viewport); + + glMatrixMode(GL_PROJECTION); + glPopMatrix(); + + glMatrixMode(GL_MODELVIEW); + glPopMatrix(); +} + +/** + * Draw Volume Slices or slices for ALL Stuctures View. + * + * @param fixedPipelineDrawing + * The OpenGL drawing. + * @param browserTabContent + * Content of browser tab that is to be drawn. + * @param volumeDrawInfo + * Info on each volume layers for drawing. + * @param sliceDrawingType + * Type of slice drawing (montage, single) + * @param sliceProjectionType + * Type of projection for the slice drawing (oblique, orthogonal) + * @param obliqueSliceMaskingType + * Masking for oblique slice drawing + * @param viewport + * The viewport (region of graphics area) for drawing slices. + */ +void +BrainOpenGLVolumeTextureSliceDrawing::drawPrivate(BrainOpenGLFixedPipeline* fixedPipelineDrawing, + BrowserTabContent* browserTabContent, + std::vector& volumeDrawInfo, + const VolumeSliceDrawingTypeEnum::Enum sliceDrawingType, + const VolumeSliceProjectionTypeEnum::Enum sliceProjectionType, + const VolumeSliceInterpolationEdgeEffectsMaskingEnum::Enum obliqueSliceMaskingType, + const int32_t viewport[4]) +{ + // CaretAssert(sliceProjectionType == VolumeSliceProjectionTypeEnum::VOLUME_SLICE_PROJECTION_OBLIQUE); + + if (volumeDrawInfo.empty()) { + return; + } CaretAssert(fixedPipelineDrawing); CaretAssert(browserTabContent); m_browserTabContent = browserTabContent; @@ -303,7 +359,6 @@ BrainOpenGLVolumeTextureSliceDrawing::drawVolumeSliceViewPlane(const VolumeSlice drawOrientationAxes(allVP); break; case VolumeSliceProjectionTypeEnum::VOLUME_SLICE_PROJECTION_ORTHOGONAL: - drawOrientationAxes(allVP); break; } } @@ -783,23 +838,25 @@ BrainOpenGLVolumeTextureSliceDrawing::drawVolumeSliceViewProjection(const BrainO */ glDisable(GL_CULL_FACE); + Matrix4x4 obliqueTransformationMatrix; switch (sliceProjectionType) { case VolumeSliceProjectionTypeEnum::VOLUME_SLICE_PROJECTION_ORTHOGONAL: +// break; case VolumeSliceProjectionTypeEnum::VOLUME_SLICE_PROJECTION_OBLIQUE: { /* * Create the oblique slice transformation matrix */ - Matrix4x4 obliqueTransformationMatrix; - createObliqueTransformationMatrix(sliceCoordinates, + createObliqueTransformationMatrix(sliceProjectionType, + sliceCoordinates, obliqueTransformationMatrix); - drawObliqueSliceWithOutlines(sliceViewPlane, - sliceProjectionType, - obliqueTransformationMatrix); } break; } + drawObliqueSliceWithOutlines(sliceViewPlane, + sliceProjectionType, + obliqueTransformationMatrix); } if ( ! m_identificationModeFlag) { @@ -919,6 +976,152 @@ BrainOpenGLVolumeTextureSliceDrawing::createSlicePlaneEquation(const VolumeSlice m_lookAtCenter[2] = sliceCoordinates[2]; } +///** +// * Set the volume slice viewing transformation. This sets the position and +// * orientation of the camera. +// * +// * @param sliceProjectionType +// * Type of projection for the slice drawing (oblique, orthogonal) +// * @param sliceViewPlane +// * View plane that is displayed. +// * @param plane +// * Plane equation of selected slice. +// * @param sliceCoordinates +// * Coordinates of the selected slices. +// */ +//void +//BrainOpenGLVolumeTextureSliceDrawing::setVolumeSliceViewingAndModelingTransformations(const VolumeSliceProjectionTypeEnum::Enum sliceProjectionType, +// const VolumeSliceViewPlaneEnum::Enum sliceViewPlane, +// const Plane& plane, +// const float sliceCoordinates[3]) +//{ +// /* +// * Initialize the modelview matrix to the identity matrix +// * This places the camera at the origin, pointing down the +// * negative-Z axis with the up vector set to (0,1,0 => +// * positive-Y is up). +// */ +// glMatrixMode(GL_MODELVIEW); +// glLoadIdentity(); +// +// const float* userTranslation = m_browserTabContent->getTranslation(); +// +// /* +// * Move the camera with the user's translation +// */ +// float viewTranslationX = 0.0; +// float viewTranslationY = 0.0; +// float viewTranslationZ = 0.0; +// +// switch (sliceViewPlane) { +// case VolumeSliceViewPlaneEnum::ALL: +// case VolumeSliceViewPlaneEnum::AXIAL: +// viewTranslationX = sliceCoordinates[0] + userTranslation[0]; +// viewTranslationY = sliceCoordinates[1] + userTranslation[1]; +// break; +// case VolumeSliceViewPlaneEnum::CORONAL: +// viewTranslationX = sliceCoordinates[0] + userTranslation[0]; +// viewTranslationY = sliceCoordinates[2] + userTranslation[2]; +// break; +// case VolumeSliceViewPlaneEnum::PARASAGITTAL: +// viewTranslationX = -(sliceCoordinates[1] + userTranslation[1]); +// viewTranslationY = sliceCoordinates[2] + userTranslation[2]; +// break; +// } +// +// glTranslatef(viewTranslationX, +// viewTranslationY, +// viewTranslationZ); +// +// +// +// +// glGetDoublev(GL_MODELVIEW_MATRIX, +// m_viewingMatrix); +// +// /* +// * Since an orthographic projection is used, the camera only needs +// * to be a little bit from the center along the plane's normal vector. +// */ +// double planeNormal[3]; +// plane.getNormalVector(planeNormal); +// double cameraXYZ[3] = { +// m_lookAtCenter[0] + planeNormal[0] * BrainOpenGLFixedPipeline::s_gluLookAtCenterFromEyeOffsetDistance, +// m_lookAtCenter[1] + planeNormal[1] * BrainOpenGLFixedPipeline::s_gluLookAtCenterFromEyeOffsetDistance, +// m_lookAtCenter[2] + planeNormal[2] * BrainOpenGLFixedPipeline::s_gluLookAtCenterFromEyeOffsetDistance, +// }; +// +// /* +// * Set the up vector which indices which way is up (screen Y) +// */ +// float up[3] = { 0.0, 0.0, 0.0 }; +// switch (sliceViewPlane) { +// case VolumeSliceViewPlaneEnum::ALL: +// case VolumeSliceViewPlaneEnum::AXIAL: +// up[1] = 1.0; +// break; +// case VolumeSliceViewPlaneEnum::CORONAL: +// up[2] = 1.0; +// break; +// case VolumeSliceViewPlaneEnum::PARASAGITTAL: +// up[2] = 1.0; +// break; +// } +// +// /* +// * For oblique viewing, the up vector needs to be rotated by the +// * oblique rotation matrix. +// */ +// switch (sliceProjectionType) { +// case VolumeSliceProjectionTypeEnum::VOLUME_SLICE_PROJECTION_OBLIQUE: +// m_browserTabContent->getObliqueVolumeRotationMatrix().multiplyPoint3(up); +// break; +// case VolumeSliceProjectionTypeEnum::VOLUME_SLICE_PROJECTION_ORTHOGONAL: +// break; +// } +// +// /* +// * Now set the camera to look at the selected coordinate (center) +// * with the camera offset a little bit from the center. +// * This allows the slice's voxels to be drawn in the actual coordinates. +// */ +// gluLookAt(cameraXYZ[0], cameraXYZ[1], cameraXYZ[2], +// m_lookAtCenter[0], m_lookAtCenter[1], m_lookAtCenter[2], +// up[0], up[1], up[2]); +//} + +/** + * Convert a Matrix4x4 to a glm::mat4 matrix + */ +static glm::mat4 +convertMatrix4x4toGlmMat4(const Matrix4x4& matrix) +{ + float m[16]; + matrix.getMatrixForOpenGL(m); + + glm::mat4 out(m[0], m[1], m[2], m[3], + m[4], m[5], m[6], m[7], + m[8], m[9], m[10], m[11], + m[12], m[13], m[14], m[15]); + return out; +} + +/** + * Convert a glm::mat4 matrix to an OpenGL matrix + */ +static void +mat4ToOpenGLMatrix(const glm::mat4& matrixIn, + float matrixOut[16]) +{ + int32_t indx = 0; + for (int32_t iRow = 0; iRow < 4; iRow++) { + for (int jCol = 0; jCol < 4; jCol++) { + matrixOut[indx] = matrixIn[iRow][jCol]; + indx++; + } + } +} + /** * Set the volume slice viewing transformation. This sets the position and * orientation of the camera. @@ -938,66 +1141,42 @@ BrainOpenGLVolumeTextureSliceDrawing::setVolumeSliceViewingAndModelingTransforma const Plane& plane, const float sliceCoordinates[3]) { - /* - * Initialize the modelview matrix to the identity matrix - * This places the camera at the origin, pointing down the - * negative-Z axis with the up vector set to (0,1,0 => - * positive-Y is up). - */ - glMatrixMode(GL_MODELVIEW); - glLoadIdentity(); - - const float* userTranslation = m_browserTabContent->getTranslation(); /* * Move the camera with the user's translation */ - float viewTranslationX = 0.0; - float viewTranslationY = 0.0; - float viewTranslationZ = 0.0; - + const float* userTranslation = m_browserTabContent->getTranslation(); + glm::vec3 translation(0.0); switch (sliceViewPlane) { case VolumeSliceViewPlaneEnum::ALL: case VolumeSliceViewPlaneEnum::AXIAL: - viewTranslationX = sliceCoordinates[0] + userTranslation[0]; - viewTranslationY = sliceCoordinates[1] + userTranslation[1]; + translation[0] = sliceCoordinates[0] + userTranslation[0]; + translation[1] = sliceCoordinates[1] + userTranslation[1]; break; case VolumeSliceViewPlaneEnum::CORONAL: - viewTranslationX = sliceCoordinates[0] + userTranslation[0]; - viewTranslationY = sliceCoordinates[2] + userTranslation[2]; + translation[0] = sliceCoordinates[0] + userTranslation[0]; + translation[1] = sliceCoordinates[2] + userTranslation[2]; break; case VolumeSliceViewPlaneEnum::PARASAGITTAL: - viewTranslationX = -(sliceCoordinates[1] + userTranslation[1]); - viewTranslationY = sliceCoordinates[2] + userTranslation[2]; + translation[0] = -(sliceCoordinates[1] + userTranslation[1]); + translation[1] = sliceCoordinates[2] + userTranslation[2]; break; } - - glTranslatef(viewTranslationX, - viewTranslationY, - viewTranslationZ); - - - - - glGetDoublev(GL_MODELVIEW_MATRIX, - m_viewingMatrix); /* - * Since an orthographic projection is used, the camera only needs + * Since an orthographic projection is used, the eye only needs * to be a little bit from the center along the plane's normal vector. */ double planeNormal[3]; plane.getNormalVector(planeNormal); - double cameraXYZ[3] = { - m_lookAtCenter[0] + planeNormal[0] * BrainOpenGLFixedPipeline::s_gluLookAtCenterFromEyeOffsetDistance, - m_lookAtCenter[1] + planeNormal[1] * BrainOpenGLFixedPipeline::s_gluLookAtCenterFromEyeOffsetDistance, - m_lookAtCenter[2] + planeNormal[2] * BrainOpenGLFixedPipeline::s_gluLookAtCenterFromEyeOffsetDistance, - }; + glm::vec3 eye(m_lookAtCenter[0] + planeNormal[0] * BrainOpenGLFixedPipeline::s_gluLookAtCenterFromEyeOffsetDistance, + m_lookAtCenter[1] + planeNormal[1] * BrainOpenGLFixedPipeline::s_gluLookAtCenterFromEyeOffsetDistance, + m_lookAtCenter[2] + planeNormal[2] * BrainOpenGLFixedPipeline::s_gluLookAtCenterFromEyeOffsetDistance); /* * Set the up vector which indices which way is up (screen Y) */ - float up[3] = { 0.0, 0.0, 0.0 }; + glm::vec4 up(0.0, 0.0, 0.0, 1.0); switch (sliceViewPlane) { case VolumeSliceViewPlaneEnum::ALL: case VolumeSliceViewPlaneEnum::AXIAL: @@ -1011,13 +1190,27 @@ BrainOpenGLVolumeTextureSliceDrawing::setVolumeSliceViewingAndModelingTransforma break; } - /* - * For oblique viewing, the up vector needs to be rotated by the - * oblique rotation matrix. - */ switch (sliceProjectionType) { case VolumeSliceProjectionTypeEnum::VOLUME_SLICE_PROJECTION_OBLIQUE: - m_browserTabContent->getObliqueVolumeRotationMatrix().multiplyPoint3(up); + { + /* + * For oblique viewing, the up vector needs to be rotated by the + * oblique rotation matrix. + */ + const Matrix4x4 obMat = m_browserTabContent->getObliqueVolumeRotationMatrix(); + const glm::mat4 matrix = convertMatrix4x4toGlmMat4(obMat); + up = matrix * up; + + if (debugFlag) { + float upTemp[3] = { up[0], up[1], up[2] }; + m_browserTabContent->getObliqueVolumeRotationMatrix().multiplyPoint3(upTemp); + float upCopy[3] = { up[0], up[1], up[2] }; + const float dist = MathFunctions::distance3D(upTemp, upCopy); + if (dist >= 0.01) { + std::cout << "Up vectors different by distance: " << dist << std::endl; + } + } + } break; case VolumeSliceProjectionTypeEnum::VOLUME_SLICE_PROJECTION_ORTHOGONAL: break; @@ -1028,9 +1221,18 @@ BrainOpenGLVolumeTextureSliceDrawing::setVolumeSliceViewingAndModelingTransforma * with the camera offset a little bit from the center. * This allows the slice's voxels to be drawn in the actual coordinates. */ - gluLookAt(cameraXYZ[0], cameraXYZ[1], cameraXYZ[2], - m_lookAtCenter[0], m_lookAtCenter[1], m_lookAtCenter[2], - up[0], up[1], up[2]); +// gluLookAt(eye[0], eye[1], eye[2], +// m_lookAtCenter[0], m_lookAtCenter[1], m_lookAtCenter[2], +// up[0], up[1], up[2]); + glm::vec3 lookAt(m_lookAtCenter[0], m_lookAtCenter[1], m_lookAtCenter[2]); + glm::mat4 lookAtMatrix = glm::lookAt(eye, lookAt, glm::vec3(up)); + + glm::mat4 translationMatrix = glm::translate(glm::mat4(1.0), translation); + + glm::mat4 projMatrix = translationMatrix * lookAtMatrix; + + glMatrixMode(GL_MODELVIEW); + glLoadMatrixf(glm::value_ptr(projMatrix)); } /** @@ -1931,10 +2133,14 @@ BrainOpenGLVolumeTextureSliceDrawing::drawOrientationAxes(const int viewport[4]) */ glMatrixMode(GL_PROJECTION); glPushMatrix(); - glLoadIdentity(); - glOrtho(left, right, - bottom, top, - nearDepth, farDepth); +// glLoadIdentity(); +// glOrtho(left, right, +// bottom, top, +// nearDepth, farDepth); + glm::mat4 orthoMatrix = glm::ortho(left, right, + bottom, top, + nearDepth, farDepth); + glLoadMatrixf(glm::value_ptr(orthoMatrix)); glMatrixMode(GL_MODELVIEW); @@ -1945,26 +2151,21 @@ BrainOpenGLVolumeTextureSliceDrawing::drawOrientationAxes(const int viewport[4]) * Set the viewing transformation, places 'eye' so that it looks * at the 'model' which is, in this case, the axes */ - double eyeX = 0.0; - double eyeY = 0.0; - double eyeZ = BrainOpenGLFixedPipeline::s_gluLookAtCenterFromEyeOffsetDistance; //100.0; - const double centerX = 0; - const double centerY = 0; - const double centerZ = 0; - const double upX = 0; - const double upY = 1; - const double upZ = 0; - gluLookAt(eyeX, eyeY, eyeZ, - centerX, centerY, centerZ, - upX, upY, upZ); - + const glm::vec3 eyeXYZ(0.0, 0.0, BrainOpenGLFixedPipeline::s_gluLookAtCenterFromEyeOffsetDistance); + const glm::vec3 lookAtXYZ(0.0, 0.0, 0.0); + const glm::vec3 upVector(0.0, 1.0, 0.0); + glm::mat4 lookAtMatrix = glm::lookAt(eyeXYZ, + lookAtXYZ, + upVector); + /* * Set the modeling transformation */ const Matrix4x4 obliqueRotationMatrix = m_browserTabContent->getObliqueVolumeRotationMatrix(); - double rotationMatrix[16]; - obliqueRotationMatrix.getMatrixForOpenGL(rotationMatrix); - glMultMatrixd(rotationMatrix); + const glm::mat4 obliqueMat4 = convertMatrix4x4toGlmMat4(obliqueRotationMatrix); + const glm::mat4 modelMatrix = lookAtMatrix * obliqueMat4; + glLoadMatrixf(glm::value_ptr(modelMatrix)); + /* * Disable depth buffer. Otherwise, when volume slices are drawn @@ -2312,13 +2513,16 @@ BrainOpenGLVolumeTextureSliceDrawing::getVoxelCoordinateBoundsAndSpacing(float b /** * Create the oblique transformation matrix. * + * @parm sliceProjectionType + * The slice projection type * @param sliceCoordinates * Slice that is being drawn. * @param obliqueTransformationMatrixOut * OUTPUT transformation matrix for oblique viewing. */ void -BrainOpenGLVolumeTextureSliceDrawing::createObliqueTransformationMatrix(const float sliceCoordinates[3], +BrainOpenGLVolumeTextureSliceDrawing::createObliqueTransformationMatrix(const VolumeSliceProjectionTypeEnum::Enum sliceProjectionType, + const float sliceCoordinates[3], Matrix4x4& obliqueTransformationMatrixOut) { /* @@ -2326,15 +2530,24 @@ BrainOpenGLVolumeTextureSliceDrawing::createObliqueTransformationMatrix(const fl */ obliqueTransformationMatrixOut.identity(); - /* - * Get the oblique rotation matrix - */ - Matrix4x4 obliqueRotationMatrix = m_browserTabContent->getObliqueVolumeRotationMatrix(); - - /* - * Create the transformation matrix - */ - obliqueTransformationMatrixOut.postmultiply(obliqueRotationMatrix); + switch (sliceProjectionType) { + case VolumeSliceProjectionTypeEnum::VOLUME_SLICE_PROJECTION_OBLIQUE: + { + /* + * Get the oblique rotation matrix + */ + Matrix4x4 obliqueRotationMatrix = m_browserTabContent->getObliqueVolumeRotationMatrix(); + + /* + * Create the transformation matrix + */ + obliqueTransformationMatrixOut.postmultiply(obliqueRotationMatrix); + + } + break; + case VolumeSliceProjectionTypeEnum::VOLUME_SLICE_PROJECTION_ORTHOGONAL: + break; + } /* * Translate to selected coordinate @@ -2690,6 +2903,16 @@ BrainOpenGLVolumeTextureSliceDrawing::createTextureName(const VolumeMappableInte glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER); glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_BORDER); + const bool enableMipMapsFlag(false); + if (enableMipMapsFlag) { + /* + * Generate mip-maps + * However does not improve quality or remove aliasing in oblique views (rotation + * around multiple axes) + */ + glTexParameteri(GL_TEXTURE_3D, GL_GENERATE_MIPMAP, GL_TRUE); + } + /* * Compression works but appears lossy for palette mapped data * but may be okay for label mapped data @@ -2823,35 +3046,6 @@ BrainOpenGLVolumeTextureSliceDrawing::drawObliqueSliceWithOutlines(const VolumeS float actualOrigin[3]; m_volumeDrawInfo[0].volumeFile->indexToSpace(originIJK, actualOrigin); - float screenOffsetX = 0.0; - float screenOffsetY = 0.0; - float originOffsetX = 0.0; - float originOffsetY = 0.0; - switch (sliceViewPlane) { - case VolumeSliceViewPlaneEnum::ALL: - CaretAssert(0); - break; - case VolumeSliceViewPlaneEnum::AXIAL: - screenOffsetX = m_lookAtCenter[0]; - screenOffsetY = m_lookAtCenter[1]; - originOffsetX = actualOrigin[0]; - originOffsetY = actualOrigin[1]; - break; - case VolumeSliceViewPlaneEnum::CORONAL: - screenOffsetX = m_lookAtCenter[0]; - screenOffsetY = m_lookAtCenter[2]; - originOffsetX = actualOrigin[0]; - originOffsetY = actualOrigin[2]; - break; - case VolumeSliceViewPlaneEnum::PARASAGITTAL: - screenOffsetX = m_lookAtCenter[1]; - screenOffsetY = m_lookAtCenter[2]; - originOffsetX = actualOrigin[1]; - originOffsetY = actualOrigin[2]; - break; - } - - /* * Set the corners of the screen for the respective view */ @@ -3317,6 +3511,7 @@ BrainOpenGLVolumeTextureSliceDrawing::setupTextureFiltering(const CaretMappableD const VolumeSliceProjectionTypeEnum::Enum sliceProjectionType) { if (mapFile->isMappedWithPalette()) { + switch (sliceProjectionType) { case caret::VolumeSliceProjectionTypeEnum::VOLUME_SLICE_PROJECTION_OBLIQUE: { @@ -3346,7 +3541,7 @@ BrainOpenGLVolumeTextureSliceDrawing::setupTextureFiltering(const CaretMappableD * Thus, Pixel contains more than one texel */ glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - + /* * GL_TEXTURE_MAG_FILTER - The texture magnification function * is used when the pixel being textured maps to an area less than or @@ -3365,6 +3560,7 @@ BrainOpenGLVolumeTextureSliceDrawing::setupTextureFiltering(const CaretMappableD * If GL_LINEAR is used the voxel "blockiness" is smoothed out */ glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + } break; case caret::VolumeSliceProjectionTypeEnum::VOLUME_SLICE_PROJECTION_ORTHOGONAL: @@ -3378,6 +3574,14 @@ BrainOpenGLVolumeTextureSliceDrawing::setupTextureFiltering(const CaretMappableD break; } + /* + * Option to smooth voxels that removes all "blocki-ness" + */ + if (DeveloperFlagsEnum::isFlag(DeveloperFlagsEnum::DELELOPER_FLAG_VOXEL_SMOOTH)) { + glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + } + GLfloat borderColor[4] = { 0.0, 0.0, 0.0, 0.0 }; glTexParameterfv(GL_TEXTURE_3D, GL_TEXTURE_BORDER_COLOR, borderColor); } diff --git a/src/Brain/BrainOpenGLVolumeTextureSliceDrawing.h b/src/Brain/BrainOpenGLVolumeTextureSliceDrawing.h index cad8f5fe4..3f9a3df11 100644 --- a/src/Brain/BrainOpenGLVolumeTextureSliceDrawing.h +++ b/src/Brain/BrainOpenGLVolumeTextureSliceDrawing.h @@ -66,7 +66,15 @@ namespace caret { BrainOpenGLVolumeTextureSliceDrawing(const BrainOpenGLVolumeTextureSliceDrawing&); BrainOpenGLVolumeTextureSliceDrawing& operator=(const BrainOpenGLVolumeTextureSliceDrawing&); - + + void drawPrivate(BrainOpenGLFixedPipeline* fixedPipelineDrawing, + BrowserTabContent* browserTabContent, + std::vector& volumeDrawInfo, + const VolumeSliceDrawingTypeEnum::Enum sliceDrawingType, + const VolumeSliceProjectionTypeEnum::Enum sliceProjectionType, + const VolumeSliceInterpolationEdgeEffectsMaskingEnum::Enum obliqueSliceMaskingType, + const int32_t viewport[4]); + void drawVolumeSlicesForAllStructuresView(const VolumeSliceProjectionTypeEnum::Enum sliceProjectionType, const int32_t viewport[4]); @@ -149,7 +157,8 @@ namespace caret { bool getVoxelCoordinateBoundsAndSpacing(float boundsOut[6], float spacingOut[3]); - void createObliqueTransformationMatrix(const float sliceCoordinates[3], + void createObliqueTransformationMatrix(const VolumeSliceProjectionTypeEnum::Enum sliceProjectionType, + const float sliceCoordinates[3], Matrix4x4& obliqueTransformationMatrixOut); bool getVolumeDrawingViewDependentCulling(const VolumeSliceViewPlaneEnum::Enum sliceViewPlane, @@ -219,7 +228,7 @@ namespace caret { double m_lookAtCenter[3]; - double m_viewingMatrix[16]; +// double m_viewingMatrix[16]; double m_orthographicBounds[6]; diff --git a/src/Common/DeveloperFlagsEnum.cxx b/src/Common/DeveloperFlagsEnum.cxx index 87103bdf1..cb8bbddc8 100644 --- a/src/Common/DeveloperFlagsEnum.cxx +++ b/src/Common/DeveloperFlagsEnum.cxx @@ -129,6 +129,11 @@ DeveloperFlagsEnum::initialize() "Texture Volume Drawing", CheckableEnum::YES, false)); + checkableItems.push_back(DeveloperFlagsEnum(DELELOPER_FLAG_VOXEL_SMOOTH, + "DELELOPER_FLAG_VOXEL_SMOOTH", + "Smooth Texture Volume Voxels", + CheckableEnum::YES, + false)); std::vector notCheckableItems; diff --git a/src/Common/DeveloperFlagsEnum.h b/src/Common/DeveloperFlagsEnum.h index 4f4ff9069..42c431c83 100644 --- a/src/Common/DeveloperFlagsEnum.h +++ b/src/Common/DeveloperFlagsEnum.h @@ -37,7 +37,8 @@ public: enum Enum { DEVELOPER_FLAG_UNUSED, DEVELOPER_FLAG_FLIP_PALETTE_NOT_DATA, - DEVELOPER_FLAG_TEXTURE_VOLUME + DEVELOPER_FLAG_TEXTURE_VOLUME, + DELELOPER_FLAG_VOXEL_SMOOTH }; ~DeveloperFlagsEnum(); -- GitLab From e846e142b80153e6d39384b4d346557254a0e316 Mon Sep 17 00:00:00 2001 From: John Harwell Date: Wed, 26 Jun 2019 10:10:51 -0500 Subject: [PATCH 376/427] WB-860 NIFTI Volume Timeseries Issues: VolumeFile was always reporting NIFTI_UNITS_UNKNOWN as it failed to override CaretMappableDataFile::getMapIntervalUnits(). VolumeFile now overrides getMapIntervalUnits() and it returns NIFTI_UNITS_SEC if the time dimension is greater than one and NiftiHeader::getTimeStep() is greater than zero. --- src/Files/VolumeFile.cxx | 26 ++++++++++++++++++++++++++ src/Files/VolumeFile.h | 2 ++ 2 files changed, 28 insertions(+) diff --git a/src/Files/VolumeFile.cxx b/src/Files/VolumeFile.cxx index 746eea8d2..5e204284d 100644 --- a/src/Files/VolumeFile.cxx +++ b/src/Files/VolumeFile.cxx @@ -2402,4 +2402,30 @@ VolumeFile::getVolumeVoxelIdentificationForMaps(const std::vector& mapI } +/** + * @return The units for the 'interval' between two consecutive maps. + */ +NiftiTimeUnitsEnum::Enum +VolumeFile::getMapIntervalUnits() const +{ + NiftiTimeUnitsEnum::Enum units = NiftiTimeUnitsEnum::NIFTI_UNITS_UNKNOWN; + + if (m_header != NULL && m_header->getType() == AbstractHeader::NIFTI) { + const NiftiHeader& myHeader = *((NiftiHeader*)m_header.getPointer()); + + std::vector dims; + getDimensions(dims); + if (dims.size() >= 4) { + if (dims[3] > 1) { + const float timeStep = myHeader.getTimeStep(); + if (timeStep > 0.0) { + units = NiftiTimeUnitsEnum::NIFTI_UNITS_SEC; + } + } + } + } + + return units; +} + diff --git a/src/Files/VolumeFile.h b/src/Files/VolumeFile.h index bb8fb16d9..8ce504030 100644 --- a/src/Files/VolumeFile.h +++ b/src/Files/VolumeFile.h @@ -375,6 +375,8 @@ namespace caret { int64_t ijkOut[3], AString& textOut) const; + virtual NiftiTimeUnitsEnum::Enum getMapIntervalUnits() const override; + }; } -- GitLab From a8f2e51ab1dd628ed2b4d29920b4ed7bc824ad08 Mon Sep 17 00:00:00 2001 From: John Harwell Date: Thu, 27 Jun 2019 09:50:08 -0500 Subject: [PATCH 377/427] WB-860 NIFTI Volume Time Series Issues: Volume File was not overriding getMapIntervalStartAndStep() to report the time step and this has been fixed so that time-series line charts display correctly. For any previously created line-series charts, one must reselect the brainordinate to correct the chart. --- src/Files/VolumeFile.cxx | 40 ++++++++++++++++++++++++++++++++++++++++ src/Files/VolumeFile.h | 2 ++ 2 files changed, 42 insertions(+) diff --git a/src/Files/VolumeFile.cxx b/src/Files/VolumeFile.cxx index 5e204284d..bfd188e7d 100644 --- a/src/Files/VolumeFile.cxx +++ b/src/Files/VolumeFile.cxx @@ -2417,6 +2417,9 @@ VolumeFile::getMapIntervalUnits() const getDimensions(dims); if (dims.size() >= 4) { if (dims[3] > 1) { + /* + * Timestep from NiftiHeader is always seconds + */ const float timeStep = myHeader.getTimeStep(); if (timeStep > 0.0) { units = NiftiTimeUnitsEnum::NIFTI_UNITS_SEC; @@ -2428,4 +2431,41 @@ VolumeFile::getMapIntervalUnits() const return units; } +/** + * Get the units value for the first map and the + * quantity of units between consecutive maps. If the + * units for the maps is unknown, value of one (1) are + * returned for both output values. + * + * @param firstMapUnitsValueOut + * Output containing units value for first map. + * @param mapIntervalStepValueOut + * Output containing number of units between consecutive maps. + */ +void +VolumeFile::getMapIntervalStartAndStep(float& firstMapUnitsValueOut, + float& mapIntervalStepValueOut) const +{ + firstMapUnitsValueOut = 0.0; + mapIntervalStepValueOut = 1.0; + + if (m_header != NULL && m_header->getType() == AbstractHeader::NIFTI) { + const NiftiHeader& myHeader = *((NiftiHeader*)m_header.getPointer()); + + std::vector dims; + getDimensions(dims); + if (dims.size() >= 4) { + if (dims[3] > 1) { + /* + * Timestep from NiftiHeader is always seconds + */ + const float timeStep = myHeader.getTimeStep(); + if (timeStep > 0.0) { + mapIntervalStepValueOut = timeStep; + } + } + } + } +} + diff --git a/src/Files/VolumeFile.h b/src/Files/VolumeFile.h index 8ce504030..dce6b8350 100644 --- a/src/Files/VolumeFile.h +++ b/src/Files/VolumeFile.h @@ -377,6 +377,8 @@ namespace caret { virtual NiftiTimeUnitsEnum::Enum getMapIntervalUnits() const override; + virtual void getMapIntervalStartAndStep(float& firstMapUnitsValueOut, + float& mapIntervalStepValueOut) const override; }; } -- GitLab From b55bbfa286ef6b6955c05b527d3dc010353cf7b2 Mon Sep 17 00:00:00 2001 From: Tim Coalson Date: Fri, 5 Jul 2019 17:21:33 -0500 Subject: [PATCH 378/427] add option to allow changing the number of timeseries stored in a sdscalar when converting --- src/Operations/OperationCiftiConvert.cxx | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/Operations/OperationCiftiConvert.cxx b/src/Operations/OperationCiftiConvert.cxx index 8b101e66e..9865cf074 100644 --- a/src/Operations/OperationCiftiConvert.cxx +++ b/src/Operations/OperationCiftiConvert.cxx @@ -69,6 +69,7 @@ OperationParameters* OperationCiftiConvert::getParameters() OptionalParameter* fgresetTimeunitsOpt = fgresetTimeOpt->createOptionalParameter(3, "-unit", "use a unit other than time"); fgresetTimeunitsOpt->addStringParameter(1, "unit", "unit identifier (default SECOND)"); fromGiftiExt->createOptionalParameter(4, "-reset-scalars", "reset mapping along rows to scalars, taking length from the gifti file"); + fromGiftiExt->createOptionalParameter(6, "-column-reset-scalars", "reset mapping along columns to scalar (useful for changing number of sers in a sdseries file)"); OptionalParameter* fromGiftiReplace = fromGiftiExt->createOptionalParameter(5, "-replace-binary", "replace data with a binary file"); fromGiftiReplace->addStringParameter(1, "binary-in", "the binary file that contains replacement data"); fromGiftiReplace->createOptionalParameter(2, "-flip-endian", "byteswap the binary file"); @@ -247,12 +248,18 @@ void OperationCiftiConvert::useParameters(OperationParameters* myParams, Progres myXML.getSeriesMap(CiftiXML::ALONG_COLUMN).setLength(numRows); } if (fromGiftiExt->getOptionalParameter(4)->m_present) - { + {//-reset-scalars if (fgresetTimeOpt->m_present) throw OperationException("only one of -reset-timepoints and -reset-scalars may be specified"); CiftiScalarsMap newMap; newMap.setLength(numCols); myXML.setMap(CiftiXML::ALONG_ROW, newMap); } + if (fromGiftiExt->getOptionalParameter(6)->m_present) + {//-column-reset-scalars + CiftiScalarsMap newMap; + newMap.setLength(numCols); + myXML.setMap(CiftiXML::ALONG_COLUMN, newMap); + } if (myXML.getDimensionLength(CiftiXML::ALONG_ROW) != numCols || myXML.getDimensionLength(CiftiXML::ALONG_COLUMN) != numRows) { throw OperationException("dimensions of input gifti array (" + AString::number(numRows) + " rows, " + AString::number(numCols) + " columns)" + -- GitLab From 33aa493fe9470d7c12905111fdb637566c473f92 Mon Sep 17 00:00:00 2001 From: Tim Coalson Date: Fri, 5 Jul 2019 17:29:06 -0500 Subject: [PATCH 379/427] fix wrong dimension use --- src/Operations/OperationCiftiConvert.cxx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Operations/OperationCiftiConvert.cxx b/src/Operations/OperationCiftiConvert.cxx index 9865cf074..bbf8ae00e 100644 --- a/src/Operations/OperationCiftiConvert.cxx +++ b/src/Operations/OperationCiftiConvert.cxx @@ -257,7 +257,7 @@ void OperationCiftiConvert::useParameters(OperationParameters* myParams, Progres if (fromGiftiExt->getOptionalParameter(6)->m_present) {//-column-reset-scalars CiftiScalarsMap newMap; - newMap.setLength(numCols); + newMap.setLength(numRows); myXML.setMap(CiftiXML::ALONG_COLUMN, newMap); } if (myXML.getDimensionLength(CiftiXML::ALONG_ROW) != numCols || myXML.getDimensionLength(CiftiXML::ALONG_COLUMN) != numRows) -- GitLab From cef8e811b5bf267db9cb329e852dc7b45dfdb6d2 Mon Sep 17 00:00:00 2001 From: Tim Coalson Date: Fri, 5 Jul 2019 18:04:22 -0500 Subject: [PATCH 380/427] fix typo in option description, add comments --- src/Operations/OperationCiftiConvert.cxx | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Operations/OperationCiftiConvert.cxx b/src/Operations/OperationCiftiConvert.cxx index bbf8ae00e..166daeb7f 100644 --- a/src/Operations/OperationCiftiConvert.cxx +++ b/src/Operations/OperationCiftiConvert.cxx @@ -69,7 +69,7 @@ OperationParameters* OperationCiftiConvert::getParameters() OptionalParameter* fgresetTimeunitsOpt = fgresetTimeOpt->createOptionalParameter(3, "-unit", "use a unit other than time"); fgresetTimeunitsOpt->addStringParameter(1, "unit", "unit identifier (default SECOND)"); fromGiftiExt->createOptionalParameter(4, "-reset-scalars", "reset mapping along rows to scalars, taking length from the gifti file"); - fromGiftiExt->createOptionalParameter(6, "-column-reset-scalars", "reset mapping along columns to scalar (useful for changing number of sers in a sdseries file)"); + fromGiftiExt->createOptionalParameter(6, "-column-reset-scalars", "reset mapping along columns to scalar (useful for changing number of series in a sdseries file)"); OptionalParameter* fromGiftiReplace = fromGiftiExt->createOptionalParameter(5, "-replace-binary", "replace data with a binary file"); fromGiftiReplace->addStringParameter(1, "binary-in", "the binary file that contains replacement data"); fromGiftiReplace->createOptionalParameter(2, "-flip-endian", "byteswap the binary file"); @@ -227,7 +227,7 @@ void OperationCiftiConvert::useParameters(OperationParameters* myParams, Progres int64_t numRows = dataArrayRef->getNumberOfRows(); OptionalParameter* fgresetTimeOpt = fromGiftiExt->getOptionalParameter(3); if (fgresetTimeOpt->m_present) - { + {//-reset-timepoints CiftiSeriesMap::Unit myUnit = CiftiSeriesMap::SECOND; OptionalParameter* fgresetTimeunitsOpt = fgresetTimeOpt->getOptionalParameter(3); if (fgresetTimeunitsOpt->m_present) @@ -415,7 +415,7 @@ void OperationCiftiConvert::useParameters(OperationParameters* myParams, Progres if (outXML.getNumberOfDimensions() != 2) throw OperationException("conversion only supported for 2D cifti"); OptionalParameter* fnresetTimeOpt = fromNifti->getOptionalParameter(4); if (fnresetTimeOpt->m_present) - { + {//-reset-timepoints CiftiSeriesMap::Unit myUnit = CiftiSeriesMap::SECOND; OptionalParameter* fnresetTimeunitsOpt = fnresetTimeOpt->getOptionalParameter(3); if (fnresetTimeunitsOpt->m_present) @@ -427,7 +427,7 @@ void OperationCiftiConvert::useParameters(OperationParameters* myParams, Progres outXML.setMap(CiftiXML::ALONG_ROW, CiftiSeriesMap(myDims[3], fnresetTimeOpt->getDouble(2), fnresetTimeOpt->getDouble(1), myUnit)); } if (fromNifti->getOptionalParameter(5)->m_present) - { + {//-reset-scalars if (fnresetTimeOpt->m_present) throw OperationException("only one of -reset-timepoints and -reset-scalars may be specified"); CiftiScalarsMap newMap; newMap.setLength(myDims[3]); -- GitLab From 9a522f73ec6bedeafd31dd47e3c1fd0c7584e243 Mon Sep 17 00:00:00 2001 From: John Harwell Date: Thu, 27 Jun 2019 14:56:29 -0500 Subject: [PATCH 381/427] WB-448 handling of files with same names but different directories: This was never implemented correctly as it was treating all files in overlay as if they had the same name. As a result, if any files were in different directories, the relative paths were displayed. This has been corrected so that uniquely named files have no path displayed, and files with same name but different paths show a relative path. --- src/Files/FilePathNamePrefixCompactor.cxx | 129 +++++++++++++--------- src/Files/FilePathNamePrefixCompactor.h | 5 +- 2 files changed, 80 insertions(+), 54 deletions(-) diff --git a/src/Files/FilePathNamePrefixCompactor.cxx b/src/Files/FilePathNamePrefixCompactor.cxx index d1b225c33..a4ecdf6de 100644 --- a/src/Files/FilePathNamePrefixCompactor.cxx +++ b/src/Files/FilePathNamePrefixCompactor.cxx @@ -102,18 +102,6 @@ FilePathNamePrefixCompactor::removeMatchingPathPrefixFromCaretDataFiles(const st } removeMatchingPathPrefixFromCaretDataFiles(caretDataFiles, prefixRemovedNamesOut); - -// std::vector fileNames; -// for (std::vector::const_iterator iter = caretMappableDataFiles.begin(); -// iter != caretMappableDataFiles.end(); -// iter++) { -// const CaretDataFile* cdf = *iter; -// CaretAssert(cdf); -// fileNames.push_back(cdf->getFileName()); -// } -// -// removeMatchingPathPrefixFromFileNames(fileNames, -// prefixRemovedNamesOut); } /** @@ -137,6 +125,7 @@ void FilePathNamePrefixCompactor::removeMatchingPathPrefixFromCaretDataFiles(const std::vector& caretDataFiles, std::vector& prefixRemovedNamesOut) { + std::vector filePathNames; std::vector fileNames; std::vector specialPrefixes; for (std::vector::const_iterator iter = caretDataFiles.begin(); @@ -146,20 +135,93 @@ FilePathNamePrefixCompactor::removeMatchingPathPrefixFromCaretDataFiles(const st CaretAssert(cdf); if (cdf->getDataFileType() == DataFileTypeEnum::CONNECTIVITY_DENSE_DYNAMIC) { -// CiftiConnectivityMatrixDenseDynamicFile* denseDynFile = dynamic_cast(cdf); -// cdf = denseDynFile->getParentBrainordinateDataSeriesFile(); specialPrefixes.push_back("dynconn - "); } else { specialPrefixes.push_back(""); } - fileNames.push_back(cdf->getFileName()); + filePathNames.push_back(cdf->getFileName()); + fileNames.push_back(cdf->getFileNameNoPath()); } - removeMatchingPathPrefixFromFileNames(fileNames, - prefixRemovedNamesOut); + CaretAssert(filePathNames.size() == fileNames.size()); + CaretAssert(filePathNames.size() == specialPrefixes.size()); + const bool onlyFilesWithMatchingFileNamesFlag(true); + if (onlyFilesWithMatchingFileNamesFlag) { + const int32_t numFiles = static_cast(filePathNames.size()); + std::vector cleanedFlag(numFiles, false); + + /* + * Initialize output to just file names (no paths) + */ + prefixRemovedNamesOut = fileNames; + + for (int32_t i = 0; i < (numFiles - 1); i++) { + CaretAssertVectorIndex(cleanedFlag, i); + if (cleanedFlag[i]) { + continue; + } + + CaretAssertVectorIndex(fileNames, i); + const AString& name = fileNames[i]; + + std::vector duplicateFileIndices; + std::vector duplicateNameFiles; + duplicateFileIndices.push_back(i); + CaretAssertVectorIndex(filePathNames, i); + duplicateNameFiles.push_back(filePathNames[i]); + + /* + * Find files with same name (excluding path) + */ + for (int32_t j = i + 1; j < numFiles; j++) { + CaretAssertVectorIndex(cleanedFlag, j); + if (cleanedFlag[j]) { + continue; + } + + CaretAssertVectorIndex(fileNames, j); + if (name == fileNames[j]) { + duplicateFileIndices.push_back(j); + CaretAssertVectorIndex(filePathNames, j); + duplicateNameFiles.push_back(filePathNames[j]); + } + } + + /* + * If multiple files with same same, remove the matching paths + */ + std::vector cleanedFileNames; + if (duplicateNameFiles.size() > 1) { + removeMatchingPathPrefixFromFileNames(duplicateNameFiles, + cleanedFileNames); + + /* + * Update the name with prefix removed for output + */ + CaretAssert(duplicateFileIndices.size() == cleanedFileNames.size()); + const int32_t numCleanedFiles = static_cast(cleanedFileNames.size()); + for (int32_t m = 0; m < numCleanedFiles; m++) { + CaretAssertVectorIndex(duplicateFileIndices, m); + const int32_t indx = duplicateFileIndices[m]; + CaretAssertVectorIndex(cleanedFileNames, m); + prefixRemovedNamesOut[indx] = cleanedFileNames[m]; + CaretAssertVectorIndex(cleanedFlag, indx); + cleanedFlag[indx] = true; + } + } + } + } + else { + /* + * Running this will operate on all files as if all of the them have the same file name + */ + removeMatchingPathPrefixFromFileNames(filePathNames, + prefixRemovedNamesOut); + } + const int32_t numFiles = static_cast(prefixRemovedNamesOut.size()); CaretAssert(numFiles == static_cast(specialPrefixes.size())); for (int32_t i = 0; i < numFiles; i++) { @@ -167,39 +229,6 @@ FilePathNamePrefixCompactor::removeMatchingPathPrefixFromCaretDataFiles(const st } } -/** - * Create names that show the filename followed by the path BUT remove - * any matching prefix from the paths for a group of CaretDataFiles. - * - * Example Input File Name: - * /mnt/myelin/data/subject2/rsfmri/activity.dscalar.nii - * /mnt/myelin/data/subject1/rsfmri/activity.dscalar.nii - * Output: - * actitivity.dscalar.nii (../subject2/rsfmri) - * actitivity.dscalar.nii (../subject1/rsfmri) - * - * @param caretDataFiles - * The caret data files from which names are obtained. - * @param prefixRemovedNamesOut - * Names of files with matching prefixes removed. Number of elements - * will match the number of elements in caretDataFiles. - */ -void -FilePathNamePrefixCompactor::removeMatchingPathPrefixFromCaretDataFile(const CaretDataFile* caretDataFile, - AString& prefixRemovedNameOut) -{ - std::vector nameVector; - nameVector.push_back(caretDataFile->getFileName()); - - std::vector prefixVector; - removeMatchingPathPrefixFromFileNames(nameVector, - prefixVector); - - CaretAssert(nameVector.size() == prefixVector.size()); - CaretAssert(prefixVector.size() == 1); - prefixRemovedNameOut = prefixVector[0]; -} - /** * Create names that show the filename followed by the path BUT remove * any matching prefix from the paths for a group of file names. diff --git a/src/Files/FilePathNamePrefixCompactor.h b/src/Files/FilePathNamePrefixCompactor.h index 2f8814e7b..7c0ee16c1 100644 --- a/src/Files/FilePathNamePrefixCompactor.h +++ b/src/Files/FilePathNamePrefixCompactor.h @@ -37,10 +37,7 @@ namespace caret { static void removeMatchingPathPrefixFromCaretDataFiles(const std::vector& caretDataFiles, std::vector& prefixRemovedNamesOut); - - static void removeMatchingPathPrefixFromCaretDataFile(const CaretDataFile* caretDataFile, - AString& prefixRemovedNameOut); - + private: FilePathNamePrefixCompactor(); -- GitLab From 2b393d40b06fef81c7f8872c6aea564fe1c3186c Mon Sep 17 00:00:00 2001 From: John Harwell Date: Wed, 3 Jul 2019 15:19:07 -0500 Subject: [PATCH 382/427] WB-860 NIFTI Volume Timeseries Issues: Mostly implemented dynamic connectivity for volume time-series file. --- src/Brain/Brain.cxx | 55 ++- src/Brain/Brain.h | 5 + src/Brain/IdentificationTextGenerator.cxx | 11 + src/Common/CMakeLists.txt | 2 + src/Common/ConnectivityCorrelation.cxx | 338 ++++++++++++++ src/Common/ConnectivityCorrelation.h | 128 ++++++ src/Files/CMakeLists.txt | 2 + ...iftiMappableConnectivityMatrixDataFile.cxx | 2 +- src/Files/VolumeDynamicConnectivityFile.cxx | 412 ++++++++++++++++++ src/Files/VolumeDynamicConnectivityFile.h | 146 +++++++ src/Files/VolumeFile.cxx | 157 +++++++ src/Files/VolumeFile.h | 18 +- .../BrainBrowserWindowOrientedToolBox.cxx | 5 + .../CiftiConnectivityMatrixViewController.cxx | 60 ++- .../CiftiConnectivityMatrixViewController.h | 4 +- src/GuiQt/GuiManager.cxx | 15 + 16 files changed, 1344 insertions(+), 16 deletions(-) create mode 100644 src/Common/ConnectivityCorrelation.cxx create mode 100644 src/Common/ConnectivityCorrelation.h create mode 100644 src/Files/VolumeDynamicConnectivityFile.cxx create mode 100644 src/Files/VolumeDynamicConnectivityFile.h diff --git a/src/Brain/Brain.cxx b/src/Brain/Brain.cxx index 5205c7c54..fb638d073 100644 --- a/src/Brain/Brain.cxx +++ b/src/Brain/Brain.cxx @@ -119,6 +119,7 @@ #include "Surface.h" #include "SurfaceProjectedItem.h" #include "SystemUtilities.h" +#include "VolumeDynamicConnectivityFile.h" #include "VolumeFile.h" #include "VolumeSurfaceOutlineSetModel.h" @@ -1562,6 +1563,8 @@ Brain::addReadOrReloadVolumeFile(const FileModeAddReadReload fileMode, m_volumeFiles.push_back(vf); } + initializeVolumeFile(vf); + return vf; } @@ -1588,6 +1591,47 @@ Brain::getVolumeFile(const int32_t volumeFileIndex) return m_volumeFiles[volumeFileIndex]; } +/** + * Get the volume dynamic connecivity files + * + * @param volumeDynamicConnectivityFilesOut + * Output with volume dynamic connectivity files + */ +void +Brain::getVolumeDynamicConnectivityFiles(std::vector& volumeDynamicConnectivityFilesOut) const +{ + volumeDynamicConnectivityFilesOut.clear(); + + for (auto vf : m_volumeFiles) { + CaretAssert(vf); + VolumeDynamicConnectivityFile* volDynConn = vf->getVolumeDynamicConnectivityFile(); + if (volDynConn != NULL) { + if (volDynConn->isDataValid()) { + volumeDynamicConnectivityFilesOut.push_back(volDynConn); + } + } + } +} + +/** + * Initialize a volume file. If it is functional data and contains more than one timepoint + * setup its volume dynamic connectivity file. + */ +void +Brain::initializeVolumeFile(VolumeFile* volumeFile) +{ + CaretAssert(volumeFile); + /* + * Enable dynamic connectivity using preferences + */ + CaretPreferences* prefs = SessionManager::get()->getCaretPreferences(); + VolumeDynamicConnectivityFile* volDynConn = volumeFile->getVolumeDynamicConnectivityFile(); + if (volDynConn != NULL) { + volDynConn->setEnabledAsLayer(prefs->isDynamicConnectivityDefaultedOn()); + } +} + + /** * Get the volume file at the given index. * @param volumeFileIndex @@ -4495,6 +4539,7 @@ Brain::addDataFile(CaretDataFile* caretDataFile) { VolumeFile* file = dynamic_cast(caretDataFile); CaretAssert(file); + initializeVolumeFile(file); m_volumeFiles.push_back(file); } break; @@ -6553,9 +6598,13 @@ Brain::getAllDataFiles(std::vector& allDataFilesOut, m_sceneFiles.begin(), m_sceneFiles.end()); - allDataFilesOut.insert(allDataFilesOut.end(), - m_volumeFiles.begin(), - m_volumeFiles.end()); + for (auto vf : m_volumeFiles) { + allDataFilesOut.push_back(vf); + VolumeDynamicConnectivityFile* volDynConnFile = vf->getVolumeDynamicConnectivityFile(); + if (volDynConnFile != NULL) { + allDataFilesOut.push_back(volDynConnFile); + } + } } /** diff --git a/src/Brain/Brain.h b/src/Brain/Brain.h index 233092112..61ff6d2ca 100644 --- a/src/Brain/Brain.h +++ b/src/Brain/Brain.h @@ -101,6 +101,7 @@ namespace caret { class Surface; class SurfaceFile; class SurfaceProjectedItem; + class VolumeDynamicConnectivityFile; class VolumeFile; class Brain : public CaretObject, public EventListenerInterface, public SceneableInterface { @@ -188,6 +189,8 @@ namespace caret { const VolumeFile* getVolumeFile(const int32_t volumeFileIndex) const; + void getVolumeDynamicConnectivityFiles(std::vector& volumeDynamicConnectivityFilesOut) const; + void resetBrain(); void resetBrainKeepSceneFiles(); @@ -712,6 +715,8 @@ namespace caret { void initializeDenseDataSeriesFile(CiftiBrainordinateDataSeriesFile* dataSeriesFile); + void initializeVolumeFile(VolumeFile* volumeFile); + void updateChartModel(); void updateVolumeSliceModel(); diff --git a/src/Brain/IdentificationTextGenerator.cxx b/src/Brain/IdentificationTextGenerator.cxx index 98e52a0b5..899f83b5e 100644 --- a/src/Brain/IdentificationTextGenerator.cxx +++ b/src/Brain/IdentificationTextGenerator.cxx @@ -73,6 +73,7 @@ #include "LabelFile.h" #include "MetricFile.h" #include "Surface.h" +#include "VolumeDynamicConnectivityFile.h" #include "SurfaceProjectedItem.h" #include "SurfaceProjectionBarycentric.h" #include "SurfaceProjectionVanEssen.h" @@ -276,6 +277,13 @@ IdentificationTextGenerator::generateVolumeIdentificationText(IdentificationStri for (int32_t i = 0; i < numVolumeFiles; i++) { const VolumeFile* vf = brain->getVolumeFile(i); volumeInterfaces.push_back(vf); + + const VolumeDynamicConnectivityFile* volDynConnFile = vf->getVolumeDynamicConnectivityFile(); + if (volDynConnFile != NULL) { + if (volDynConnFile->isDataValid()) { + volumeInterfaces.push_back(volDynConnFile); + } + } } /* @@ -383,6 +391,9 @@ IdentificationTextGenerator::generateVolumeIdentificationText(IdentificationStri } } + if (dynamic_cast(volumeFile) != NULL) { + boldText.insert(0, "VOLUME CONNECTIVITY DYNAMIC "); + } idText.addLine(true, boldText, text); diff --git a/src/Common/CMakeLists.txt b/src/Common/CMakeLists.txt index 19662195b..a1f410e20 100644 --- a/src/Common/CMakeLists.txt +++ b/src/Common/CMakeLists.txt @@ -83,6 +83,7 @@ CaretTemporaryFile.h CaretUndoCommand.h CaretUndoStack.h CaretUnitsTypeEnum.h +ConnectivityCorrelation.h CubicSpline.h DataCompressZLib.h DataFile.h @@ -211,6 +212,7 @@ CaretTemporaryFile.cxx CaretUndoCommand.cxx CaretUndoStack.cxx CaretUnitsTypeEnum.cxx +ConnectivityCorrelation.cxx CubicSpline.cxx DataCompressZLib.cxx DataFile.cxx diff --git a/src/Common/ConnectivityCorrelation.cxx b/src/Common/ConnectivityCorrelation.cxx new file mode 100644 index 000000000..f5d8d7800 --- /dev/null +++ b/src/Common/ConnectivityCorrelation.cxx @@ -0,0 +1,338 @@ + +/*LICENSE_START*/ +/* + * Copyright (C) 2019 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + +#include + +#define __CONNECTIVITY_CORRELATION_DECLARE__ +#include "ConnectivityCorrelation.h" +#undef __CONNECTIVITY_CORRELATION_DECLARE__ + +#include "CaretAssert.h" +#include "CaretOMP.h" +#include "dot_wrapper.h" + +using namespace caret; + + + +/** + * \class caret::ConnectivityCorrelation + * \brief Computes connectivity correlations + * \ingroup Common + */ + +/** + * Constructor for data in one 'chunk' of memory + * + * @param data + * Points to first data element in first group + * @param groupCount + * Number of groups + * @param groupStride + * Element offset between two consecutive groups (from first element in first + * group to first element in second group, and so on) + * @param groupDataCount + * Number of data elements in a group + * @param groupDataStride + * Element offset between to consecutive data values within a group + * Use a value of "1" for contiguous data + * + * Example: Cifti file with data in a matrix. Each row contains + * data (timepoints) for one group + * data = pointer to data + * groupCount = number of rows + * groupStride = number of columns + * groupDataCount = number of columns + * groupDataStride = 1 + * + * Example: Nifti file with time-series data. Each 'brick' contains one timepoint + * for all groups (voxels). 4D matrix + * data = pointer to data + * groupCount = dim[0] * dim[1] * dim[2] (sizeof a 'brick') + * groupStride = 1 + * groupDataCount = number of timepoints + * groupDataStride = dim[0] * dim[1] * dim[3] (sizeof a 'brick') + */ +ConnectivityCorrelation::ConnectivityCorrelation(const float* data, + const int64_t groupCount, + const int64_t groupStride, + const int64_t groupDataCount, + const int64_t groupDataStride) +: m_dataCount(groupDataCount) +{ + CaretAssert(data); + CaretAssert(groupStride >= 1); + CaretAssert(groupDataStride >= 1); + + for (int64_t i = 0; i < groupCount; i++) { + const float* dataPointer = data + (i * groupStride); + addDataGroup(dataPointer, + groupDataCount, + groupDataStride); + } + CaretAssert(static_cast(m_groups.size()) == groupCount); + + m_contiguousDataFlag = (groupDataStride == 1); +} + +/** + * Constructor for data in separate 'chunk' for each data group + * + * @param dataGroups + * Each element is pointer to first element in each group + * @param groupCount + * Number of groups (number of elements in 'dataGroups') + * @param groupDataCount + * Number of data elements in a group + * @param groupDataStrides + * Each element is offset between consecutive data values within each group + * Must contain 'groupCount' elements. + * OR may also be NULL which indicates elements are contigous in all groups + * + * Example: GIFTI file that stores each 'map' in a separate chunk of memory + * dataGroups = pointers to data in each GIFTI data array + * groupCount = number of rows + * groupStride = number of columns + * groupDataCount = number of columns + * groupDataStrides = NULL (data elements are consecutive) + */ +ConnectivityCorrelation::ConnectivityCorrelation(const float* dataGroups[], + const int64_t groupCount, + const int64_t groupDataCount, + const int64_t groupDataStrides[]) +: m_dataCount(groupDataCount) +{ + CaretAssert(dataGroups); + m_contiguousDataFlag = true; + + const bool haveStride(groupDataStrides != NULL); + for (int64_t i = 0; i < groupCount; i++) { + const int64_t dataStride(haveStride + ? groupDataStrides[i] + : 1); + CaretAssert(dataStride >= 1); + CaretAssert(dataGroups[i]); + + addDataGroup(dataGroups[i], + groupDataCount, + dataStride); + + if (dataStride != 1) { + m_contiguousDataFlag = false; + } + } + CaretAssert(static_cast(m_groups.size()) == groupCount); +} + +/** + * Destructor. + */ +ConnectivityCorrelation::~ConnectivityCorrelation() +{ +} + +/** + * Compute data's mean and sum-squared + * + * @param data + * Data on which mean and sum-squared are calculated + * @param dataCount + * Number of elements in data. + * @param dataStride + * Element offset between consecutive data values + */ +void +ConnectivityCorrelation::addDataGroup(const float* data, + const int64_t dataCount, + const int64_t dataStride) +{ + CaretAssert(data); + + float mean(0.0f); + float sqrtSumSquared(0.0f); + computeDataMeanAndSumSquared(data, + dataCount, + dataStride, + mean, + sqrtSumSquared); + + std::unique_ptr gd(new GroupData(data, + dataStride, + mean, + sqrtSumSquared)); + m_groups.push_back(std::move(gd)); +} + +/** + * Get correlation from the given group to all other groups + * + * @param groupIndex + * Index of group to correlate to all other groups + * @param dataOut + * Output with correlation values. + */ +void +ConnectivityCorrelation::getCorrelationForGroup(const int64_t groupIndex, + std::vector& dataOut) +{ + CaretAssertVectorIndex(m_groups, groupIndex); + + if (m_dataCount > 0) { + const int64_t numGroups = static_cast(m_groups.size()); + dataOut.resize(numGroups); +#pragma omp CARET_PARFOR schedule(dynamic) + for (int64_t iGroup = 0; iGroup < numGroups; iGroup++) { + CaretAssertVectorIndex(dataOut, iGroup); + if (m_contiguousDataFlag) { + dataOut[iGroup] = correlationContiguousData(groupIndex, + iGroup); + } + else { + dataOut[iGroup] = correlationNonContiguousData(groupIndex, + iGroup); + } + } + } +} + +/** + * Get the correlation coefficient for the two given groups + * that contain CONTIGOUS data + * + * @param fromGroupIndex + * Index of a group + * @param toGroupIndex + * Index of a second group. + */ +float +ConnectivityCorrelation::correlationContiguousData(const int64_t fromGroupIndex, + const int64_t toGroupIndex) const +{ + CaretAssertVectorIndex(m_groups, fromGroupIndex); + CaretAssertVectorIndex(m_groups, toGroupIndex); + + const GroupData* fromGroup = m_groups[fromGroupIndex].get(); + const GroupData* toGroup = m_groups[toGroupIndex].get(); + + double xySum = dsdot(fromGroup->m_data, + toGroup->m_data, + m_dataCount); + + const double ssxy = xySum - (m_dataCount * fromGroup->m_mean * toGroup->m_mean); + + float correlationCoefficient = 0.0; + if ((fromGroup->m_sqrt_ssxx > 0.0) + && (toGroup->m_sqrt_ssxx > 0.0)) { + correlationCoefficient = (ssxy / (fromGroup->m_sqrt_ssxx * toGroup->m_sqrt_ssxx)); + } + return correlationCoefficient; +} + +/** + * Get the correlation coefficient for the two given groups + * that contain NON-CONTIGOUS data + * + * @param fromGroupIndex + * Index of a group + * @param toGroupIndex + * Index of a second group. + */ +float +ConnectivityCorrelation::correlationNonContiguousData(const int64_t fromGroupIndex, + const int64_t toGroupIndex) const +{ + CaretAssertVectorIndex(m_groups, fromGroupIndex); + CaretAssertVectorIndex(m_groups, toGroupIndex); + + const GroupData* fromGroup = m_groups[fromGroupIndex].get(); + const GroupData* toGroup = m_groups[toGroupIndex].get(); + + const float* fromData = fromGroup->m_data; + const int64_t fromStride = fromGroup->m_dataStride; + const float* toData = toGroup->m_data; + const int64_t toStride = toGroup->m_dataStride; + + int64_t fromOffset(0); + int64_t toOffset(0); + double xySum(0.0); + for (int32_t i = 0; i < m_dataCount; i++) { + xySum += (fromData[fromOffset] * toData[toOffset]); + fromOffset += fromStride; + toOffset += toStride; + } + + const double ssxy = xySum - (m_dataCount * fromGroup->m_mean * toGroup->m_mean); + + float correlationCoefficient = 0.0; + if ((fromGroup->m_sqrt_ssxx > 0.0) + && (toGroup->m_sqrt_ssxx > 0.0)) { + correlationCoefficient = (ssxy / (fromGroup->m_sqrt_ssxx * toGroup->m_sqrt_ssxx)); + } + return correlationCoefficient; +} + +/** + * Compute data's mean and sum-squared + * + * @param data + * Data on which mean and sum-squared are calculated + * @param dataCount + * Number of elements in data. + * @param dataStride + * Element offset between consecutive data values + * @param meanOut + * Output with mean of data. + * @param sqrtSquaredOut + * Output with square root of (sum-squared). + */ +void +ConnectivityCorrelation::computeDataMeanAndSumSquared(const float* data, + const int64_t dataCount, + const int64_t dataStride, + float& meanOut, + float& sqrtSquaredOut) const +{ + CaretAssert(data); + CaretAssert(dataStride >= 1); + + meanOut = 0.0; + sqrtSquaredOut = 0.0; + if (dataCount <= 0) { + return; + } + + double sum = 0.0; + double sumSquared = 0.0; + + int64_t offset = 0; + for (int64_t i = 0; i < dataCount; i++) { +// const float d = data[i]; + const float d = data[offset]; + sum += d; + sumSquared += (d * d); + offset += dataStride; + } + + meanOut = (sum / dataCount); + const float ssxx = (sumSquared - (dataCount * meanOut * meanOut)); + sqrtSquaredOut = std::sqrt(ssxx); +} + diff --git a/src/Common/ConnectivityCorrelation.h b/src/Common/ConnectivityCorrelation.h new file mode 100644 index 000000000..1f3f17ccc --- /dev/null +++ b/src/Common/ConnectivityCorrelation.h @@ -0,0 +1,128 @@ +#ifndef __CONNECTIVITY_CORRELATION_H__ +#define __CONNECTIVITY_CORRELATION_H__ + +/*LICENSE_START*/ +/* + * Copyright (C) 2019 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + + + +#include + +#include "CaretObject.h" + + + +namespace caret { + + class ConnectivityCorrelation : public CaretObject { + + public: + ConnectivityCorrelation(const float* data, + const int64_t groupCount, + const int64_t groupStride, + const int64_t groupDataCount, + const int64_t groupDataStride); + + ConnectivityCorrelation(const float* dataGroups[], + const int64_t groupCount, + const int64_t groupDataCount, + const int64_t groupDataStrides[]); + + /** + * Destructor + */ + ~ConnectivityCorrelation(); + + ConnectivityCorrelation(const ConnectivityCorrelation&) = delete; + + ConnectivityCorrelation& operator=(const ConnectivityCorrelation&) = delete; + + void getCorrelationForGroup(const int64_t groupIndex, + std::vector& dataOut); + + + + // ADD_NEW_METHODS_HERE + + private: + class GroupData { + public: + /** + * Constructor. + * + * @param data + * Pointer to data + * @param dataStride + * Offset between consecutive elements (1 for contiguous data) + * @param mean + * Mean value of 'data' + * @param sqrtSumSquared + * Square root of sum-squared of data + */ + GroupData(const float* data, + const int64_t dataStride, + const float mean, + const float sqrtSumSquared) + : m_data(data), + m_dataStride(dataStride), + m_mean(mean), + m_sqrt_ssxx(sqrtSumSquared) { } + + const float* m_data; + + const int64_t m_dataStride; + + const float m_mean; + + const float m_sqrt_ssxx; + }; + + void addDataGroup(const float* data, + const int64_t dataCount, + const int64_t dataStride); + + void computeDataMeanAndSumSquared(const float* data, + const int64_t dataCount, + const int64_t dataStride, + float& meanOut, + float& sumSquaredOut) const; + + float correlationContiguousData(const int64_t fromGroupIndex, + const int64_t toGroupIndex) const; + + float correlationNonContiguousData(const int64_t fromGroupIndex, + const int64_t toGroupIndex) const; + + const float m_dataCount; + + std::vector> m_groups; + + bool m_contiguousDataFlag = false; + + // ADD_NEW_MEMBERS_HERE + + }; + +#ifdef __CONNECTIVITY_CORRELATION_DECLARE__ + // +#endif // __CONNECTIVITY_CORRELATION_DECLARE__ + +} // namespace +#endif //__CONNECTIVITY_CORRELATION_H__ diff --git a/src/Files/CMakeLists.txt b/src/Files/CMakeLists.txt index bf381d784..249943fea 100755 --- a/src/Files/CMakeLists.txt +++ b/src/Files/CMakeLists.txt @@ -146,6 +146,7 @@ SurfaceResamplingMethodEnum.h SurfaceTypeEnum.h TextFile.h TopologyHelper.h +VolumeDynamicConnectivityFile.h VolumeEditingModeEnum.h VolumeFile.h VolumeFileEditorDelegate.h @@ -279,6 +280,7 @@ SurfaceResamplingMethodEnum.cxx SurfaceTypeEnum.cxx TextFile.cxx TopologyHelper.cxx +VolumeDynamicConnectivityFile.cxx VolumeEditingModeEnum.cxx VolumeFile.cxx VolumeFileEditorDelegate.cxx diff --git a/src/Files/CiftiMappableConnectivityMatrixDataFile.cxx b/src/Files/CiftiMappableConnectivityMatrixDataFile.cxx index 0b6a2dd90..6205019d2 100644 --- a/src/Files/CiftiMappableConnectivityMatrixDataFile.cxx +++ b/src/Files/CiftiMappableConnectivityMatrixDataFile.cxx @@ -62,7 +62,7 @@ CiftiMappableConnectivityMatrixDataFile::CiftiMappableConnectivityMatrixDataFile m_sceneAssistant->add("m_connectivityDataLoaded", "ConnectivityDataLoaded", m_connectivityDataLoaded); - m_sceneAssistant->add("m_dataLoadingEnabled", + m_sceneAssistant->add("+", &m_dataLoadingEnabled); } diff --git a/src/Files/VolumeDynamicConnectivityFile.cxx b/src/Files/VolumeDynamicConnectivityFile.cxx new file mode 100644 index 000000000..83cbd05c2 --- /dev/null +++ b/src/Files/VolumeDynamicConnectivityFile.cxx @@ -0,0 +1,412 @@ + +/*LICENSE_START*/ +/* + * Copyright (C) 2019 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + +#include + +#define __VOLUME_DYNN_CONN_FILE_DECLARE__ +#include "VolumeDynamicConnectivityFile.h" +#undef __VOLUME_DYNN_CONN_FILE_DECLARE__ + +#include "CaretAssert.h" +#include "ConnectivityCorrelation.h" +#include "DataFileException.h" +#include "FileInformation.h" +#include "SceneClass.h" +#include "SceneClassAssistant.h" + +using namespace caret; + + + +/** + * \class caret::VolumeDynamicConnectivityFile + * \brief Dynamic connectivity volume file + * \ingroup Files + */ + +/** + * Constructor. + */ +VolumeDynamicConnectivityFile::VolumeDynamicConnectivityFile(const VolumeFile* parentVolumeFile) +: VolumeFile(), +m_parentVolumeFile(parentVolumeFile) +{ + CaretAssert(m_parentVolumeFile); + + m_sceneAssistant = std::unique_ptr(new SceneClassAssistant()); + m_sceneAssistant->add("m_enabledAsLayer", + &m_enabledAsLayer); +} + +/** + * Destructor. + */ +VolumeDynamicConnectivityFile::~VolumeDynamicConnectivityFile() +{ +} + +/** + * Clear the file. + */ +void +VolumeDynamicConnectivityFile::clear() +{ + VolumeFile::clear(); + m_voxelData = NULL; + m_numberOfVoxels = 0; + m_validDataFlag = false; + m_enabledAsLayer = false; + m_connectivityCorrelation.reset(); +} + +/** + * @return True if enabled as a layer. + */ +bool +VolumeDynamicConnectivityFile::isEnabledAsLayer() const +{ + return m_enabledAsLayer; +} + +/** + * Set enabled as a layer. + * + * @param True if enabled as a layer. + */ +void +VolumeDynamicConnectivityFile::setEnabledAsLayer(const bool enabled) +{ + m_enabledAsLayer = enabled; +} + +/** + * @return True if data loading enabled. + */ +bool +VolumeDynamicConnectivityFile::isDataLoadingEnabled() const +{ + return m_dataLoadingEnabledFlag; +} + +/** + * Set data loading enabled. + * + * @param True if data loading enabled. + */ +void +VolumeDynamicConnectivityFile::setDataLoadingEnabled(const bool enabled) +{ + m_dataLoadingEnabledFlag = enabled; +} + +/** + * Initialize the file using information from parent volume file + */ +void +VolumeDynamicConnectivityFile::initializeFile() +{ + CaretAssert(m_parentVolumeFile); + const int64_t numberOfFrames(1); + const int32_t numberOfComponents(1); + + reinitialize(m_parentVolumeFile->getVolumeSpace(), + numberOfFrames, + numberOfComponents, + SubvolumeAttributes::FUNCTIONAL, + m_parentVolumeFile->m_header); + + AString path, nameNoExt, ext; + FileInformation fileInfo(m_parentVolumeFile->getFileName()); + fileInfo.getFileComponents(path, nameNoExt, ext); + setFileName(FileInformation::assembleFileComponents(path, + nameNoExt, + "dynconn.nii.gz")); //DataFileTypeEnum::toFileExtension(DataFileTypeEnum::CONNECTIVITY_DENSE_DYNAMIC))); + + std::vector dims; + getDimensions(dims); + m_numberOfVoxels = (dims[0] * dims[1] * dims[2]); + m_dimI = dims[0]; + m_dimJ = dims[1]; + m_dimK = dims[2]; + m_dimTime = dims[3]; + CaretAssert(m_dimTime == 1); + if (m_numberOfVoxels > 0) { + m_voxelData = const_cast(getFrame(0)); + m_sliceStride = m_dimI * m_dimJ; + m_timePointIndexStride = m_numberOfVoxels; + } + + clearVoxels(); + + m_validDataFlag = true; +} + +/** + * @return True if this file type supports writing, else false. + * + * Dense files do NOT support writing. + */ +bool +VolumeDynamicConnectivityFile::supportsWriting() const +{ + return false; +} + +/** + * @return The parent volume file + */ +VolumeFile* +VolumeDynamicConnectivityFile::getParentVolumeFile() +{ + return const_cast(m_parentVolumeFile); +} + +/** + * @return The parent volume file (const method) + */ +const VolumeFile* +VolumeDynamicConnectivityFile::getParentVolumeFile() const +{ + return m_parentVolumeFile; +} + +/** + * @return True if the data is valid + */ +bool +VolumeDynamicConnectivityFile::isDataValid() const +{ + return m_validDataFlag; +} + +/** + * Add information about the file to the data file information. + * + * @param dataFileInformation + * Consolidates information about a data file. + */ +void +VolumeDynamicConnectivityFile::addToDataFileContentInformation(DataFileContentInformation& dataFileInformation) +{ + VolumeFile::addToDataFileContentInformation(dataFileInformation); +} + +/** + * Read the file with the given name. + * + * @param filename + * Name of file + * @throws DataFileException + * If error occurs + */ +void +VolumeDynamicConnectivityFile::readFile(const AString& /*filename*/) +{ + throw DataFileException("Read of Volume Dynamic Connectivity File is not allowed"); +} + +/** + * Read the file with the given name. + * + * @param filename + * Name of file + * @throws DataFileException + * If error occurs + */ +void +VolumeDynamicConnectivityFile::writeFile(const AString& /*filename*/) +{ + throw DataFileException("Writing of Volume Dynamic Connectivity File is not allowed"); +} + +/** + * Save subclass data to the scene. + * + * @param sceneAttributes + * Attributes for the scene. Scenes may be of different types + * (full, generic, etc) and the attributes should be checked when + * restoring the scene. + * + * @param sceneClass + * sceneClass to which data members should be added. Will always + * be valid (non-NULL). + */ +void +VolumeDynamicConnectivityFile::saveSubClassDataToScene(const SceneAttributes* sceneAttributes, + SceneClass* sceneClass) +{ + m_sceneAssistant->saveMembers(sceneAttributes, + sceneClass); +} + +/** + * Restore file data from the scene. + * + * @param sceneAttributes + * Attributes for the scene. Scenes may be of different types + * (full, generic, etc) and the attributes should be checked when + * restoring the scene. + * + * @param sceneClass + * sceneClass for the instance of a class that implements + * this interface. Will NEVER be NULL. + */ +void +VolumeDynamicConnectivityFile::restoreSubClassDataFromScene(const SceneAttributes* sceneAttributes, + const SceneClass* sceneClass) +{ + m_sceneAssistant->restoreMembers(sceneAttributes, + sceneClass); +} + +/** + * Clear voxels in this volume + */ +void +VolumeDynamicConnectivityFile::clearVoxels() +{ + if (m_voxelData != NULL) { + std::fill(m_voxelData, m_voxelData + m_numberOfVoxels, 0.0f); + updateScalarColoringForMap(0); + } +} + +/** + * Get the timepoints for a given voxel + * + * @param i + * index "I" + * @param j + * index "J" + * @param k + * index "K" + * @param dataOut + * Output with time points + */ +void +VolumeDynamicConnectivityFile::getTimePointsForVoxel(const int64_t i, + const int64_t j, + const int64_t k, + std::vector& dataOut) const +{ + CaretAssert(indexValid(i, j, k)); + + const int64_t ijk[3] { i, j, k }; + const int64_t componentIndex(0); + + dataOut.resize(m_dimTime); + for (int64_t iTime = 0; iTime < m_dimTime; iTime++) { + dataOut[iTime] = m_parentVolumeFile->getValue(ijk, + iTime, + componentIndex); + } +} + +/** + * Load the connectivity for the voxel at the given coordinate. + * The loaded data will be in the voxels inside this volume. + * If the voxel index at the coordinate is invalid, zeros are loaded into all voxels. + * + * @param xyz + * The voxel XYZ. + */ +void +VolumeDynamicConnectivityFile::loadConnectivityForVoxelXYZ(const float xyz[3]) +{ + float indicesFloat[3]; + spaceToIndex(xyz, + indicesFloat); + const int64_t ijk[3] { + static_cast(indicesFloat[0]), + static_cast(indicesFloat[1]), + static_cast(indicesFloat[2]) + }; + loadConnectivityForVoxelIndex(ijk); +} + + +/** + * Load the connectivity for the given voxel. + * The loaded data will be in the voxels inside this volume. + * If the voxel index is invalid, zeros are loaded into all voxels. + * + * @param ijk + * The voxel index. + */ +void +VolumeDynamicConnectivityFile::loadConnectivityForVoxelIndex(const int64_t ijk[3]) +{ + if ( ! isDataValid()) { + return; + } + if ( ! m_dataLoadingEnabledFlag) { + return; + } + + clearVoxels(); + + if (m_voxelData != NULL) { + if (indexValid(ijk)) { + const int64_t voxelCount(m_dimI * m_dimJ * m_dimK); + if (m_connectivityCorrelation == NULL) { + /* + * Need data and timepoint count from parent volume + * IJK dimensions are same in this and parent volume + */ + CaretAssert(m_parentVolumeFile); + const float* parentVoxels = m_parentVolumeFile->getFrame(0); + CaretAssert(parentVoxels); + std::vector parentVolumeDimensions; + m_parentVolumeFile->getDimensions(parentVolumeDimensions); + CaretAssert(parentVolumeDimensions.size() >= 4); + const int64_t timePointCount = parentVolumeDimensions[3]; + + const int64_t groupCount(voxelCount); // Each IJK voxel is a group + const int64_t groupStride(1); // All groups have one element in each 'brick' + const int64_t groupDataCount(timePointCount); // Each group contains timepoints + const int64_t groupDataStride(voxelCount); // Each group element is in separate IJK 'brick' + m_connectivityCorrelation.reset(new ConnectivityCorrelation(parentVoxels, + groupCount, + groupStride, + groupDataCount, + groupDataStride)); + } + + const int64_t myTimePointOffset = getVoxelOffset(ijk[0], ijk[1], ijk[2], 0); + std::vector coefficients; + m_connectivityCorrelation->getCorrelationForGroup(myTimePointOffset, + coefficients); + CaretAssert(voxelCount == static_cast(coefficients.size())); + std::copy(coefficients.begin(), + coefficients.end(), + m_voxelData); + + setMapName(0, + ("Voxel (" + + AString::fromNumbers(ijk, 3, ", ") + + ")")); + } + } + + const int32_t mapIndex(0); + updateScalarColoringForMap(mapIndex); +} + diff --git a/src/Files/VolumeDynamicConnectivityFile.h b/src/Files/VolumeDynamicConnectivityFile.h new file mode 100644 index 000000000..6e7657ebe --- /dev/null +++ b/src/Files/VolumeDynamicConnectivityFile.h @@ -0,0 +1,146 @@ +#ifndef __VOLUME_DYNN_CONN_FILE_H__ +#define __VOLUME_DYNN_CONN_FILE_H__ + +/*LICENSE_START*/ +/* + * Copyright (C) 2019 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + + + +#include + +#include "VolumeFile.h" + + + +namespace caret { + class ConnectivityCorrelation; + + class VolumeDynamicConnectivityFile : public VolumeFile { + + public: + VolumeDynamicConnectivityFile(const VolumeFile* parentVolumeFile); + + virtual ~VolumeDynamicConnectivityFile(); + + VolumeDynamicConnectivityFile(const VolumeDynamicConnectivityFile&) = delete; + + VolumeDynamicConnectivityFile& operator=(const VolumeDynamicConnectivityFile&) = delete; + + void initializeFile(); + + virtual void clear() override; + + virtual void addToDataFileContentInformation(DataFileContentInformation& dataFileInformation) override; + + virtual void readFile(const AString& filename) override; + + virtual void writeFile(const AString& filename) override; + + virtual bool supportsWriting() const override; + + VolumeFile* getParentVolumeFile(); + + const VolumeFile* getParentVolumeFile() const; + + bool isDataValid() const; + + bool isEnabledAsLayer() const; + + void setEnabledAsLayer(const bool enabled); + + void loadConnectivityForVoxelIndex(const int64_t ijk[3]); + + void loadConnectivityForVoxelXYZ(const float xyz[3]); + + bool isDataLoadingEnabled() const; + + void setDataLoadingEnabled(const bool enabled); + + // ADD_NEW_METHODS_HERE + + + + + + + protected: + virtual void saveSubClassDataToScene(const SceneAttributes* sceneAttributes, + SceneClass* sceneClass); + + virtual void restoreSubClassDataFromScene(const SceneAttributes* sceneAttributes, + const SceneClass* sceneClass); + + private: + void clearVoxels(); + + void getTimePointsForVoxel(const int64_t i, + const int64_t j, + const int64_t k, + std::vector& dataOut) const; + + inline int64_t getVoxelOffset(const int64_t i, + const int64_t j, + const int64_t k, + const int64_t timePointIndex) const { + const int64_t offset = (i + + (j * m_dimI) + + (k * m_sliceStride) + + (timePointIndex * m_timePointIndexStride)); + return offset; + } + + const VolumeFile* m_parentVolumeFile; + + std::unique_ptr m_sceneAssistant; + + std::unique_ptr m_connectivityCorrelation; + + float* m_voxelData = NULL; + + int64_t m_numberOfVoxels = 0; + + int64_t m_sliceStride = 0; + + int64_t m_timePointIndexStride = 0; + + int64_t m_dimI = 0; + + int64_t m_dimJ = 0; + + int64_t m_dimK = 0; + + int64_t m_dimTime = 0; + + bool m_validDataFlag = false; + + bool m_enabledAsLayer = true; + + bool m_dataLoadingEnabledFlag = true; + + // ADD_NEW_MEMBERS_HERE + + }; + +#ifdef __VOLUME_DYNN_CONN_FILE_DECLARE__ + // +#endif // __VOLUME_DYNN_CONN_FILE_DECLARE__ + +} // namespace +#endif //__VOLUME_DYNN_CONN_FILE_H__ diff --git a/src/Files/VolumeFile.cxx b/src/Files/VolumeFile.cxx index bfd188e7d..afec444e3 100644 --- a/src/Files/VolumeFile.cxx +++ b/src/Files/VolumeFile.cxx @@ -17,6 +17,8 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ /*LICENSE_END*/ + +#include #include #include #include @@ -42,6 +44,7 @@ #include "NiftiIO.h" #include "Palette.h" #include "SceneClass.h" +#include "VolumeDynamicConnectivityFile.h" #include "VolumeFile.h" #include "VolumeFileEditorDelegate.h" #include "VolumeFileVoxelColorizer.h" @@ -54,6 +57,7 @@ using namespace std; const float VolumeFile::INVALID_INTERP_VALUE = 0.0f;//we may want NaN or something more obvious bool VolumeFile::s_voxelColoringEnabled = true; +const AString VolumeFile::s_paletteColorMappingNameInMetaData = "__DYNAMIC_FILE_PALETTE_COLOR_MAPPING__"; /** * Static method that sets the status of voxel coloring. Coloring may take @@ -197,6 +201,7 @@ VolumeFile::clear() VolumeBase::clear(); m_volumeFileEditorDelegate->clear(); + m_lazyInitializedDynamicConnectivityFile.reset(); } void VolumeFile::readFile(const AString& filename) @@ -333,6 +338,22 @@ VolumeFile::writeFile(const AString& filename) throw DataFileException(filename, "writing multi-component volumes is not currently supported");//its a hassle, and uncommon, and there is only one 3-component type, restricted to 0-255 } + + /* + * Put the child dynamic data-series file's palette in the file's metadata. + */ + if (m_lazyInitializedDynamicConnectivityFile != NULL) { + GiftiMetaData* fileMetaData = m_lazyInitializedDynamicConnectivityFile->getCiftiXML().getFileMetaData(); + CaretAssert(fileMetaData); + if (m_lazyInitializedDynamicConnectivityFile->getNumberOfMaps() > 0) { + fileMetaData->set(s_paletteColorMappingNameInMetaData, + m_lazyInitializedDynamicConnectivityFile->getMapPaletteColorMapping(0)->encodeInXML()); + } + else { + fileMetaData->remove(s_paletteColorMappingNameInMetaData); + } + } + updateCaretExtension(); NiftiHeader outHeader;//begin nifti-specific code @@ -776,6 +797,10 @@ VolumeFile::clearModified() for (int32_t i = 0; i < numMaps; i++) { getMapMetaData(i)->clearModified(); } + + if (m_lazyInitializedDynamicConnectivityFile != NULL) { + m_lazyInitializedDynamicConnectivityFile->clearModified(); + } } /** @@ -1934,6 +1959,10 @@ VolumeFile::saveFileDataToScene(const SceneAttributes* sceneAttributes, sceneClass->addClass(m_classNameHierarchy->saveToScene(sceneAttributes, "m_classNameHierarchy")); } + if (m_lazyInitializedDynamicConnectivityFile != NULL) { + sceneClass->addClass(m_lazyInitializedDynamicConnectivityFile->saveToScene(sceneAttributes, + "m_lazyInitializedDynamicConnectivityFile")); + } } /** @@ -1975,6 +2004,13 @@ VolumeFile::restoreFileDataFromScene(const SceneAttributes* sceneAttributes, sc); m_forceUpdateOfGroupAndNameHierarchy = false; } + + const SceneClass* dynamicFileSceneClass = sceneClass->getClass("m_lazyInitializedDynamicConnectivityFile"); + if (dynamicFileSceneClass != NULL) { + VolumeDynamicConnectivityFile* denseDynamicFile = getVolumeDynamicConnectivityFile(); + denseDynamicFile->restoreFromScene(sceneAttributes, + dynamicFileSceneClass); + } } /** @@ -2468,4 +2504,125 @@ VolumeFile::getMapIntervalStartAndStep(float& firstMapUnitsValueOut, } } +/** + * @return The volume dynamic connectivity file for a data-series (functional) file + * that contains at least two time points. Note that some files may + * have type anatomy but still contain functional data. + * Will return NULL for other types. + */ +const VolumeDynamicConnectivityFile* +VolumeFile::getVolumeDynamicConnectivityFile() const +{ + VolumeFile* nonConstThis = const_cast(this); + return nonConstThis->getVolumeDynamicConnectivityFile(); +} + +/** + * @return The volume dynamic connectivity file for a data-series (functional) file + * that contains at least two time points. Note that some files may + * have type anatomy but still contain functional data. + * Will return NULL for other types. + */ +VolumeDynamicConnectivityFile* +VolumeFile::getVolumeDynamicConnectivityFile() +{ + if (m_lazyInitializedDynamicConnectivityFile == NULL) { + if ((getType() == SubvolumeAttributes::ANATOMY) + || (getType() == SubvolumeAttributes::FUNCTIONAL)) { + std::vector dims; + getDimensions(dims); + if (dims.size() >= 4) { + if (dims[3] > 1) { + m_lazyInitializedDynamicConnectivityFile.reset(new VolumeDynamicConnectivityFile(this)); + + m_lazyInitializedDynamicConnectivityFile->initializeFile(); + + /* + * Palette for dynamic file is in file metadata + */ + GiftiMetaData* fileMetaData = getFileMetaData(); + const AString encodedPaletteColorMappingString = fileMetaData->get(s_paletteColorMappingNameInMetaData); + if ( ! encodedPaletteColorMappingString.isEmpty()) { + if (m_lazyInitializedDynamicConnectivityFile->getNumberOfMaps() > 0) { + PaletteColorMapping* pcm = m_lazyInitializedDynamicConnectivityFile->getMapPaletteColorMapping(0); + CaretAssert(pcm); + pcm->decodeFromStringXML(encodedPaletteColorMappingString); + } + } + + m_lazyInitializedDynamicConnectivityFile->clearModified(); + } + } + } + } + + return m_lazyInitializedDynamicConnectivityFile.get(); +} + +/** + * @return True if any of the maps in this file contain a + * color mapping that possesses a modified status. + */ +bool +VolumeFile::isModifiedPaletteColorMapping() const +{ + /* + * This method is override because we need to know if the + * encapsulated dynamic dense file has a modified palette. + * When restoring a scene, a file with any type of modification + * must be reloaded to remove any modifications. Note that + * when a scene is restored, files that are not modified and + * are in the new scene are NOT reloaded to save time. + */ + if (CaretMappableDataFile::isModifiedPaletteColorMapping()) { + return true; + } + + if (m_lazyInitializedDynamicConnectivityFile != NULL) { + if (m_lazyInitializedDynamicConnectivityFile->isModifiedPaletteColorMapping()) { + return true; + } + } + + return false; +} + +/** + * @return The modified status for aall palettes in this file. + * Note that 'modified' overrides any 'modified by show scene'. + */ +PaletteModifiedStatusEnum::Enum +VolumeFile::getPaletteColorMappingModifiedStatus() const +{ + const std::array fileModStatus = { { + CaretMappableDataFile::getPaletteColorMappingModifiedStatus(), + ((m_lazyInitializedDynamicConnectivityFile != NULL) + ? m_lazyInitializedDynamicConnectivityFile->getPaletteColorMappingModifiedStatus() + : PaletteModifiedStatusEnum::UNMODIFIED) + } }; + + PaletteModifiedStatusEnum::Enum modStatus = PaletteModifiedStatusEnum::UNMODIFIED; + for (auto status : fileModStatus) { + switch (status) { + case PaletteModifiedStatusEnum::MODIFIED: + modStatus = PaletteModifiedStatusEnum::MODIFIED; + break; + case PaletteModifiedStatusEnum::MODIFIED_BY_SHOW_SCENE: + modStatus = PaletteModifiedStatusEnum::MODIFIED_BY_SHOW_SCENE; + break; + case PaletteModifiedStatusEnum::UNMODIFIED: + break; + } + + if (modStatus == PaletteModifiedStatusEnum::MODIFIED) { + /* + * 'MODIFIED' overrides 'MODIFIED_BY_SHOW_SCENE' + * so no need to continue loop + */ + break; + } + } + + return modStatus; +} diff --git a/src/Files/VolumeFile.h b/src/Files/VolumeFile.h index dce6b8350..ee1256942 100644 --- a/src/Files/VolumeFile.h +++ b/src/Files/VolumeFile.h @@ -37,6 +37,7 @@ namespace caret { class GroupAndNameHierarchyModel; + class VolumeDynamicConnectivityFile; class VolumeFileEditorDelegate; class VolumeFileVoxelColorizer; class VolumeSpline; @@ -96,6 +97,8 @@ namespace caret { /** Performs coloring of voxels. Will be NULL if coloring is disabled. */ CaretPointer m_voxelColorizer; + std::unique_ptr m_lazyInitializedDynamicConnectivityFile; + /** True if the volume is a single slice, needed by interpolateValue() methods */ bool m_singleSliceFlag; @@ -123,6 +126,8 @@ namespace caret { CaretPointer m_volumeFileEditorDelegate; + static const AString s_paletteColorMappingNameInMetaData; + protected: virtual void saveFileDataToScene(const SceneAttributes* sceneAttributes, SceneClass* sceneClass); @@ -189,9 +194,9 @@ namespace caret { ///returns true if volume space matches in spatial dimensions and sform bool matchesVolumeSpace(const int64_t dims[3], const std::vector >& sform) const; - void readFile(const AString& filename); + virtual void readFile(const AString& filename); - void writeFile(const AString& filename); + virtual void writeFile(const AString& filename); bool isEmpty() const { return VolumeBase::isEmpty(); } @@ -379,6 +384,15 @@ namespace caret { virtual void getMapIntervalStartAndStep(float& firstMapUnitsValueOut, float& mapIntervalStepValueOut) const override; + + VolumeDynamicConnectivityFile* getVolumeDynamicConnectivityFile(); + + const VolumeDynamicConnectivityFile* getVolumeDynamicConnectivityFile() const; + + virtual bool isModifiedPaletteColorMapping() const override; + + virtual PaletteModifiedStatusEnum::Enum getPaletteColorMappingModifiedStatus() const override; + }; } diff --git a/src/GuiQt/BrainBrowserWindowOrientedToolBox.cxx b/src/GuiQt/BrainBrowserWindowOrientedToolBox.cxx index e74290b2e..9ea622644 100644 --- a/src/GuiQt/BrainBrowserWindowOrientedToolBox.cxx +++ b/src/GuiQt/BrainBrowserWindowOrientedToolBox.cxx @@ -57,6 +57,7 @@ #include "SceneClass.h" #include "SceneWindowGeometry.h" #include "SessionManager.h" +#include "VolumeDynamicConnectivityFile.h" #include "VolumeFile.h" #include "VolumeSurfaceOutlineSetViewController.h" #include "WuQMacroManager.h" @@ -754,6 +755,10 @@ BrainBrowserWindowOrientedToolBox::receiveEvent(Event* event) if (vf->isMappedWithLabelTable()) { haveLabels = true; } + const VolumeDynamicConnectivityFile* volDynConnFile = vf->getVolumeDynamicConnectivityFile(); + if (volDynConnFile != NULL) { + haveConnFiles = true; + } } break; } diff --git a/src/GuiQt/CiftiConnectivityMatrixViewController.cxx b/src/GuiQt/CiftiConnectivityMatrixViewController.cxx index 4969b7dca..77b71f7ac 100644 --- a/src/GuiQt/CiftiConnectivityMatrixViewController.cxx +++ b/src/GuiQt/CiftiConnectivityMatrixViewController.cxx @@ -49,6 +49,7 @@ #include "FiberTrajectoryMapProperties.h" #include "FilePathNamePrefixCompactor.h" #include "GuiManager.h" +#include "VolumeDynamicConnectivityFile.h" #include "WuQMacroManager.h" #include "WuQMessageBox.h" #include "WuQtUtilities.h" @@ -156,6 +157,11 @@ CiftiConnectivityMatrixViewController::updateViewController() files.push_back(*matrixIter); } + std::vector volumeDynConnFiles; + brain->getVolumeDynamicConnectivityFiles(volumeDynConnFiles); + files.insert(files.end(), + volumeDynConnFiles.begin(), volumeDynConnFiles.end()); + WuQMacroManager* macroManager = WuQMacroManager::instance(); const int32_t numFiles = static_cast(files.size()); @@ -257,6 +263,7 @@ CiftiConnectivityMatrixViewController::updateViewController() const CiftiMappableConnectivityMatrixDataFile* matrixFile = dynamic_cast(files[i]); const CiftiFiberTrajectoryFile* trajFile = dynamic_cast(files[i]); + const VolumeDynamicConnectivityFile* volDynConnFile = dynamic_cast(files[i]); bool checkStatus = false; if (matrixFile != NULL) { @@ -265,6 +272,9 @@ CiftiConnectivityMatrixViewController::updateViewController() else if (trajFile != NULL) { checkStatus = trajFile->isDataLoadingEnabled(); } + else if (volDynConnFile != NULL) { + checkStatus = volDynConnFile->isDataLoadingEnabled(); + } else { CaretAssertMessage(0, "Has a new file type been added?"); } @@ -277,6 +287,9 @@ CiftiConnectivityMatrixViewController::updateViewController() if (dynConnFile != NULL) { layerCheckBox->setChecked(dynConnFile->isEnabledAsLayer()); } + else if (volDynConnFile != NULL) { + layerCheckBox->setChecked(volDynConnFile->isEnabledAsLayer()); + } else { layerCheckBox->setChecked(false); } @@ -299,6 +312,9 @@ CiftiConnectivityMatrixViewController::updateViewController() if (dynamic_cast(files[i]) != NULL) { layerCheckBoxValid = true; } + else if (dynamic_cast(files[i]) != NULL) { + layerCheckBoxValid = true; + } } m_fileEnableCheckBoxes[i]->setVisible(showRow); @@ -328,11 +344,13 @@ CiftiConnectivityMatrixViewController::updateFiberOrientationComboBoxes() QComboBox* comboBox = m_fiberOrientationFileComboBoxes[i]; CiftiMappableConnectivityMatrixDataFile* matrixFile = NULL; CiftiFiberTrajectoryFile* trajFile = NULL; + VolumeDynamicConnectivityFile* volDynConnFile = NULL; if (comboBox->isEnabled()) { getFileAtIndex(i, matrixFile, - trajFile); + trajFile, + volDynConnFile); } if (trajFile != NULL) { @@ -387,10 +405,12 @@ CiftiConnectivityMatrixViewController::enabledCheckBoxClicked(int indx) CiftiMappableConnectivityMatrixDataFile* matrixFile = NULL; CiftiFiberTrajectoryFile* trajFile = NULL; - + VolumeDynamicConnectivityFile* volDynConnFile(NULL); + getFileAtIndex(indx, matrixFile, - trajFile); + trajFile, + volDynConnFile); if (matrixFile != NULL) { matrixFile->setMapDataLoadingEnabled(0, @@ -399,6 +419,9 @@ CiftiConnectivityMatrixViewController::enabledCheckBoxClicked(int indx) else if (trajFile != NULL) { trajFile->setDataLoadingEnabled(newStatus); } + else if (volDynConnFile != NULL) { + volDynConnFile->setDataLoadingEnabled(newStatus); + } else { CaretAssertMessage(0, "Has a new file type been added?"); } @@ -420,10 +443,12 @@ CiftiConnectivityMatrixViewController::layerCheckBoxClicked(int indx) CiftiMappableConnectivityMatrixDataFile* matrixFile = NULL; CiftiFiberTrajectoryFile* trajFile = NULL; - + VolumeDynamicConnectivityFile* volDynConnFile(NULL); + getFileAtIndex(indx, matrixFile, - trajFile); + trajFile, + volDynConnFile); if (matrixFile != NULL) { CiftiConnectivityMatrixDenseDynamicFile* dynConnFile = dynamic_cast(matrixFile); @@ -431,6 +456,9 @@ CiftiConnectivityMatrixViewController::layerCheckBoxClicked(int indx) dynConnFile->setEnabledAsLayer(newStatus); } } + else if (volDynConnFile != NULL) { + volDynConnFile->setEnabledAsLayer(newStatus); + } else if (trajFile != NULL) { CaretAssertMessage(0, "Should never get caled for fiber trajectory file"); } @@ -454,11 +482,14 @@ CiftiConnectivityMatrixViewController::layerCheckBoxClicked(int indx) * If there is a CIFTI matrix file at the given index, this will be non-NULL. * @param ciftiTrajFileOut * If there is a CIFTI trajectory file at the given index, this will be non-NULL. + * @param volDynConnFileOut + * If there is a volume dynamnic connectivity files at the given index, this will be non-NULL */ void CiftiConnectivityMatrixViewController::getFileAtIndex(const int32_t indx, CiftiMappableConnectivityMatrixDataFile* &ciftiMatrixFileOut, - CiftiFiberTrajectoryFile* &ciftiTrajFileOut) + CiftiFiberTrajectoryFile* &ciftiTrajFileOut, + VolumeDynamicConnectivityFile* &volDynConnFileOut) { CaretAssertVectorIndex(m_fileEnableCheckBoxes, indx); void* ptr = m_fileEnableCheckBoxes[indx]->property(FILE_POINTER_PROPERTY_NAME).value(); @@ -466,6 +497,7 @@ CiftiConnectivityMatrixViewController::getFileAtIndex(const int32_t indx, ciftiMatrixFileOut = dynamic_cast(mapFilePointer); ciftiTrajFileOut = dynamic_cast(mapFilePointer); + volDynConnFileOut = dynamic_cast(mapFilePointer); AString name = ""; if (mapFilePointer != NULL) { @@ -488,6 +520,9 @@ CiftiConnectivityMatrixViewController::getFileAtIndex(const int32_t indx, else if (ciftiTrajFileOut != NULL) { /* OK */ } + else if (volDynConnFileOut != NULL) { + /* OK */ + } else { CaretAssertMessage(0, "Has a new file type been added?"); @@ -507,10 +542,12 @@ CiftiConnectivityMatrixViewController::fiberOrientationFileComboBoxActivated(int CiftiMappableConnectivityMatrixDataFile* matrixFile = NULL; CiftiFiberTrajectoryFile* trajFile = NULL; + VolumeDynamicConnectivityFile* volDynConnFile(NULL); getFileAtIndex(indx, matrixFile, - trajFile); + trajFile, + volDynConnFile); CaretAssertMessage(trajFile, "Selected orientation file but trajectory file is invalid."); @@ -552,10 +589,12 @@ CiftiConnectivityMatrixViewController::copyToolButtonClicked(int indx) CiftiMappableConnectivityMatrixDataFile* matrixFile = NULL; CiftiFiberTrajectoryFile* trajFile = NULL; - + VolumeDynamicConnectivityFile* volDynConnFile(NULL); + getFileAtIndex(indx, matrixFile, - trajFile); + trajFile, + volDynConnFile); bool errorFlag = false; @@ -579,6 +618,9 @@ CiftiConnectivityMatrixViewController::copyToolButtonClicked(int indx) else { errorFlag = true; } + } + else if (volDynConnFile != NULL) { + } else if (trajFile != NULL) { CiftiFiberTrajectoryFile* newTrajFile = trajFile->newFiberTrajectoryFileFromLoadedRowData(directoryName, diff --git a/src/GuiQt/CiftiConnectivityMatrixViewController.h b/src/GuiQt/CiftiConnectivityMatrixViewController.h index 59d7e1152..26260da7e 100644 --- a/src/GuiQt/CiftiConnectivityMatrixViewController.h +++ b/src/GuiQt/CiftiConnectivityMatrixViewController.h @@ -38,6 +38,7 @@ namespace caret { class CiftiMappableConnectivityMatrixDataFile; class CiftiFiberTrajectoryFile; + class VolumeDynamicConnectivityFile; class CiftiConnectivityMatrixViewController : public QWidget, EventListenerInterface { @@ -75,7 +76,8 @@ namespace caret { void getFileAtIndex(const int32_t indx, CiftiMappableConnectivityMatrixDataFile* &ciftiMatrixFileOut, - CiftiFiberTrajectoryFile* &ciftiTrajFileOut); + CiftiFiberTrajectoryFile* &ciftiTrajFileOut, + VolumeDynamicConnectivityFile* &volDynConnFileOut); const QString m_objectNamePrefix; diff --git a/src/GuiQt/GuiManager.cxx b/src/GuiQt/GuiManager.cxx index ecab154ab..3e0ad6281 100644 --- a/src/GuiQt/GuiManager.cxx +++ b/src/GuiQt/GuiManager.cxx @@ -123,6 +123,7 @@ #include "SurfacePropertiesEditorDialog.h" #include "Surface.h" #include "TileTabsConfigurationDialog.h" +#include "VolumeDynamicConnectivityFile.h" #include "VolumeMappableInterface.h" #include "VolumePropertiesEditorDialog.h" #include "WbMacroCustomOperationManager.h" @@ -3157,6 +3158,20 @@ GuiManager::processIdentification(const int32_t tabIndex, } } + if (idVoxel->isValid()) { + std::vector volumeDynConnFiles; + brain->getVolumeDynamicConnectivityFiles(volumeDynConnFiles); + + for (auto vdc : volumeDynConnFiles) { + if (vdc->isEnabledAsLayer()) { + double xyzDouble[3]; + idVoxel->getModelXYZ(xyzDouble); + float xyz[3] { static_cast(xyzDouble[0]), static_cast(xyzDouble[1]), static_cast(xyzDouble[2]) }; + vdc->loadConnectivityForVoxelXYZ(xyz); + } + } + } + SelectionItemChartMatrix* idChartOneMatrix = selectionManager->getChartMatrixIdentification(); if (idChartOneMatrix->isValid()) { ChartableMatrixInterface* chartMatrixInterface = idChartOneMatrix->getChartableMatrixInterface(); -- GitLab From a1f6b3ca74a90e8d18e6e3a82e1786de4605073b Mon Sep 17 00:00:00 2001 From: John Harwell Date: Tue, 9 Jul 2019 09:55:58 -0500 Subject: [PATCH 383/427] Allow inclusion of non-writable file extensions when calling DataFileTypeEnum::getFilesExtensionsForEveryFile() --- src/Common/DataFileTypeEnum.cxx | 85 +++++++++++++++++++++++++++++---- src/Common/DataFileTypeEnum.h | 6 ++- 2 files changed, 81 insertions(+), 10 deletions(-) diff --git a/src/Common/DataFileTypeEnum.cxx b/src/Common/DataFileTypeEnum.cxx index cc40d27b0..635bd14ef 100644 --- a/src/Common/DataFileTypeEnum.cxx +++ b/src/Common/DataFileTypeEnum.cxx @@ -324,6 +324,12 @@ DataFileTypeEnum::initialize() false, "nii", "nii.gz")); + enumData.push_back(DataFileTypeEnum(VOLUME_DYNAMIC, + "VOLUME_DYNAMIC", + "Volume - Dynamic", + "VOLUME DYNAMIC", + false, + "vol_dynconn")); // this file is never written } /** @@ -612,23 +618,86 @@ DataFileTypeEnum::getAllFileExtensions(const Enum enumValue) /** * @return All valid file extensions for all file types except UNKNOWN - * and CONNECTIVITY_DENSE_DYNAMIC + * and dynanmic connectivity files + * + * @param includeNonWritableFileTypesFlag + * If true, include non-writable files such as dynamic connectvity files */ std::vector -DataFileTypeEnum::getFilesExtensionsForEveryFile() +DataFileTypeEnum::getFilesExtensionsForEveryFile(const bool includeNonWritableFileTypesFlag) { std::vector allExtensions; for (std::vector::iterator enumIter = enumData.begin(); enumIter != enumData.end(); enumIter++) { - if (enumIter->enumValue == DataFileTypeEnum::CONNECTIVITY_DENSE_DYNAMIC) { - /* nothing */ - } - else if (enumIter->enumValue == DataFileTypeEnum::UNKNOWN) { - /* nothing */ + bool validFlag(true); + + switch (enumIter->enumValue) { + case DataFileTypeEnum::ANNOTATION: + break; + case DataFileTypeEnum::ANNOTATION_TEXT_SUBSTITUTION: + break; + case DataFileTypeEnum::BORDER: + break; + case DataFileTypeEnum::CONNECTIVITY_DENSE: + break; + case DataFileTypeEnum::CONNECTIVITY_DENSE_DYNAMIC: + validFlag = includeNonWritableFileTypesFlag; + break; + case DataFileTypeEnum::CONNECTIVITY_DENSE_LABEL: + break; + case DataFileTypeEnum::CONNECTIVITY_DENSE_PARCEL: + break; + case DataFileTypeEnum::CONNECTIVITY_PARCEL: + break; + case DataFileTypeEnum::CONNECTIVITY_PARCEL_DENSE: + break; + case DataFileTypeEnum::CONNECTIVITY_PARCEL_LABEL: + break; + case DataFileTypeEnum::CONNECTIVITY_PARCEL_SCALAR: + break; + case DataFileTypeEnum::CONNECTIVITY_PARCEL_SERIES: + break; + case DataFileTypeEnum::CONNECTIVITY_DENSE_SCALAR: + break; + case DataFileTypeEnum::CONNECTIVITY_DENSE_TIME_SERIES: + break; + case DataFileTypeEnum::CONNECTIVITY_FIBER_ORIENTATIONS_TEMPORARY: + break; + case DataFileTypeEnum::CONNECTIVITY_FIBER_TRAJECTORY_TEMPORARY: + break; + case DataFileTypeEnum::CONNECTIVITY_SCALAR_DATA_SERIES: + break; + case DataFileTypeEnum::FOCI: + break; + case DataFileTypeEnum::IMAGE: + break; + case DataFileTypeEnum::LABEL: + break; + case DataFileTypeEnum::METRIC: + break; + case DataFileTypeEnum::PALETTE: + break; + case DataFileTypeEnum::RGBA: + break; + case DataFileTypeEnum::SCENE: + break; + case DataFileTypeEnum::SPECIFICATION: + break; + case DataFileTypeEnum::SURFACE: + break; + case DataFileTypeEnum::UNKNOWN: + validFlag = false; + break; + case DataFileTypeEnum::VOLUME: + break; + case DataFileTypeEnum::VOLUME_DYNAMIC: + validFlag = includeNonWritableFileTypesFlag; + break; } - else { + + if (validFlag) { allExtensions.insert(allExtensions.end(), enumIter->fileExtensions.begin(), enumIter->fileExtensions.end()); diff --git a/src/Common/DataFileTypeEnum.h b/src/Common/DataFileTypeEnum.h index a2a6a8016..71aae2320 100644 --- a/src/Common/DataFileTypeEnum.h +++ b/src/Common/DataFileTypeEnum.h @@ -90,7 +90,9 @@ public: /** Unknown */ UNKNOWN, /** Volume */ - VOLUME + VOLUME, + /** Volume */ + VOLUME_DYNAMIC }; /** @@ -140,7 +142,7 @@ public: static std::vector getAllFileExtensions(const Enum enumValue); - static std::vector getFilesExtensionsForEveryFile(); + static std::vector getFilesExtensionsForEveryFile(const bool includeNonWritableFileTypesFlag = false); static bool isFileUsedWithOneStructure(const Enum enumValue); -- GitLab From 7d05e960f5f966e822f9398e715078ee35151545 Mon Sep 17 00:00:00 2001 From: John Harwell Date: Tue, 9 Jul 2019 09:57:54 -0500 Subject: [PATCH 384/427] WB-860 NIFTI Volume Timeseries Issues: Initial implementation of Volume Dynamic Connectivity File (from a volume data-series file). --- src/Brain/Brain.cxx | 78 ++- src/Brain/BrowserTabContent.cxx | 78 ++- src/Brain/ChartTwoOverlay.cxx | 2 + src/Brain/IdentificationTextGenerator.cxx | 7 +- src/Brain/Overlay.cxx | 74 ++- src/Brain/SurfaceNodeColoring.cxx | 2 + src/Common/FileInformation.cxx | 3 +- src/Files/CaretDataFileHelper.cxx | 3 + src/Files/CaretMappableDataFile.h | 4 +- ...etMappableDataFileAndMapSelectionModel.cxx | 3 + src/Files/ChartableTwoFileDelegate.cxx | 4 +- src/Files/ChartableTwoFileMatrixChart.cxx | 2 + .../CiftiMappableConnectivityMatrixDataFile.h | 2 +- src/Files/CiftiMappableDataFile.cxx | 7 + src/Files/FilePathNamePrefixCompactor.cxx | 71 ++- src/Files/GiftiTypeFile.cxx | 4 + src/Files/PaletteFile.cxx | 2 + src/Files/SceneFile.cxx | 4 + src/Files/SpecFile.cxx | 67 ++- src/Files/VolumeDynamicConnectivityFile.cxx | 443 +++++++++++++++--- src/Files/VolumeDynamicConnectivityFile.h | 34 +- src/Files/VolumeFile.cxx | 13 + src/Files/VolumeFile.h | 2 + .../BrainBrowserWindowOrientedToolBox.cxx | 12 +- .../CiftiConnectivityMatrixViewController.cxx | 19 +- src/GuiQt/GuiManager.cxx | 1 + src/GuiQt/IdentifyBrainordinateDialog.cxx | 2 + src/GuiQt/OverlayViewController.cxx | 68 ++- src/GuiQt/UserInputModeViewContextMenu.cxx | 51 +- src/GuiQt/UserInputModeViewContextMenu.h | 2 +- 30 files changed, 944 insertions(+), 120 deletions(-) diff --git a/src/Brain/Brain.cxx b/src/Brain/Brain.cxx index fb638d073..309f8801b 100644 --- a/src/Brain/Brain.cxx +++ b/src/Brain/Brain.cxx @@ -853,6 +853,8 @@ Brain::resetBrainKeepSceneFiles() break; case DataFileTypeEnum::VOLUME: break; + case DataFileTypeEnum::VOLUME_DYNAMIC: + break; } if (keepFileFlag) { @@ -4543,6 +4545,9 @@ Brain::addDataFile(CaretDataFile* caretDataFile) m_volumeFiles.push_back(file); } break; + case DataFileTypeEnum::VOLUME_DYNAMIC: + CaretAssertMessage(0, "Volume Dynamic files are never added to the brain"); + break; } m_specFile->addCaretDataFile(caretDataFile); @@ -5403,6 +5408,9 @@ Brain::addReadOrReloadDataFile(const FileModeAddReadReload fileMode, caretDataFile, dataFileName); break; + case DataFileTypeEnum::VOLUME_DYNAMIC: + CaretAssertMessage(0, "Volume dynamic files are never read by the Brain"); + break; } if (caretDataFileRead != NULL) { @@ -6824,6 +6832,8 @@ Brain::writeDataFile(CaretDataFile* caretDataFile) break; case DataFileTypeEnum::VOLUME: break; + case DataFileTypeEnum::VOLUME_DYNAMIC: + break; } } @@ -6845,10 +6855,72 @@ Brain::removeWithoutDeleteDataFile(const CaretDataFile* caretDataFile) } /* - * Dense dynamic files are encapsulated in a dense-series file - * so they do not get removed. + * dynamic files are not removable. */ - if (caretDataFile->getDataFileType() == DataFileTypeEnum::CONNECTIVITY_DENSE_DYNAMIC) { + bool canBeRemovedFlag(true); + switch (caretDataFile->getDataFileType()) { + case DataFileTypeEnum::ANNOTATION: + break; + case DataFileTypeEnum::ANNOTATION_TEXT_SUBSTITUTION: + break; + case DataFileTypeEnum::BORDER: + break; + case DataFileTypeEnum::CONNECTIVITY_DENSE: + break; + case DataFileTypeEnum::CONNECTIVITY_DENSE_DYNAMIC: + canBeRemovedFlag = false; + break; + case DataFileTypeEnum::CONNECTIVITY_DENSE_LABEL: + break; + case DataFileTypeEnum::CONNECTIVITY_DENSE_PARCEL: + break; + case DataFileTypeEnum::CONNECTIVITY_PARCEL: + break; + case DataFileTypeEnum::CONNECTIVITY_PARCEL_DENSE: + break; + case DataFileTypeEnum::CONNECTIVITY_PARCEL_LABEL: + break; + case DataFileTypeEnum::CONNECTIVITY_PARCEL_SCALAR: + break; + case DataFileTypeEnum::CONNECTIVITY_PARCEL_SERIES: + break; + case DataFileTypeEnum::CONNECTIVITY_DENSE_SCALAR: + break; + case DataFileTypeEnum::CONNECTIVITY_DENSE_TIME_SERIES: + break; + case DataFileTypeEnum::CONNECTIVITY_FIBER_ORIENTATIONS_TEMPORARY: + break; + case DataFileTypeEnum::CONNECTIVITY_FIBER_TRAJECTORY_TEMPORARY: + break; + case DataFileTypeEnum::CONNECTIVITY_SCALAR_DATA_SERIES: + break; + case DataFileTypeEnum::FOCI: + break; + case DataFileTypeEnum::IMAGE: + break; + case DataFileTypeEnum::LABEL: + break; + case DataFileTypeEnum::METRIC: + break; + case DataFileTypeEnum::PALETTE: + break; + case DataFileTypeEnum::RGBA: + break; + case DataFileTypeEnum::SCENE: + break; + case DataFileTypeEnum::SPECIFICATION: + break; + case DataFileTypeEnum::SURFACE: + break; + case DataFileTypeEnum::UNKNOWN: + break; + case DataFileTypeEnum::VOLUME: + break; + case DataFileTypeEnum::VOLUME_DYNAMIC: + canBeRemovedFlag = false; + break; + } + if ( ! canBeRemovedFlag) { return false; } diff --git a/src/Brain/BrowserTabContent.cxx b/src/Brain/BrowserTabContent.cxx index 8b784f509..bb3c00dbb 100644 --- a/src/Brain/BrowserTabContent.cxx +++ b/src/Brain/BrowserTabContent.cxx @@ -88,6 +88,7 @@ #include "ViewingTransformations.h" #include "ViewingTransformationsCerebellum.h" #include "ViewingTransformationsVolume.h" +#include "VolumeDynamicConnectivityFile.h" #include "VolumeSliceSettings.h" #include "VolumeSurfaceOutlineModel.h" #include "VolumeSurfaceOutlineSetModel.h" @@ -2064,14 +2065,75 @@ BrowserTabContent::getFilesDisplayedInTab(std::vector& displayed mapIndex); if (overlayDataFile != NULL) { - /* - * Dense dynamic is encapsulated within its parent data-series - * file so include both files. - */ - if (overlayDataFile->getDataFileType() == DataFileTypeEnum::CONNECTIVITY_DENSE_DYNAMIC) { - CiftiConnectivityMatrixDenseDynamicFile* dynFile = dynamic_cast(overlayDataFile); - CaretAssert(dynFile); - displayedDataFiles.insert(dynFile->getParentBrainordinateDataSeriesFile()); + switch (overlayDataFile->getDataFileType()) { + case DataFileTypeEnum::ANNOTATION: + break; + case DataFileTypeEnum::ANNOTATION_TEXT_SUBSTITUTION: + break; + case DataFileTypeEnum::BORDER: + break; + case DataFileTypeEnum::CONNECTIVITY_DENSE: + break; + case DataFileTypeEnum::CONNECTIVITY_DENSE_DYNAMIC: + { + CiftiConnectivityMatrixDenseDynamicFile* dynFile = dynamic_cast(overlayDataFile); + CaretAssert(dynFile); + displayedDataFiles.insert(dynFile->getParentBrainordinateDataSeriesFile()); + } + break; + case DataFileTypeEnum::CONNECTIVITY_DENSE_LABEL: + break; + case DataFileTypeEnum::CONNECTIVITY_DENSE_PARCEL: + break; + case DataFileTypeEnum::CONNECTIVITY_PARCEL: + break; + case DataFileTypeEnum::CONNECTIVITY_PARCEL_DENSE: + break; + case DataFileTypeEnum::CONNECTIVITY_PARCEL_LABEL: + break; + case DataFileTypeEnum::CONNECTIVITY_PARCEL_SCALAR: + break; + case DataFileTypeEnum::CONNECTIVITY_PARCEL_SERIES: + break; + case DataFileTypeEnum::CONNECTIVITY_DENSE_SCALAR: + break; + case DataFileTypeEnum::CONNECTIVITY_DENSE_TIME_SERIES: + break; + case DataFileTypeEnum::CONNECTIVITY_FIBER_ORIENTATIONS_TEMPORARY: + break; + case DataFileTypeEnum::CONNECTIVITY_FIBER_TRAJECTORY_TEMPORARY: + break; + case DataFileTypeEnum::CONNECTIVITY_SCALAR_DATA_SERIES: + break; + case DataFileTypeEnum::FOCI: + break; + case DataFileTypeEnum::IMAGE: + break; + case DataFileTypeEnum::LABEL: + break; + case DataFileTypeEnum::METRIC: + break; + case DataFileTypeEnum::PALETTE: + break; + case DataFileTypeEnum::RGBA: + break; + case DataFileTypeEnum::SCENE: + break; + case DataFileTypeEnum::SPECIFICATION: + break; + case DataFileTypeEnum::SURFACE: + break; + case DataFileTypeEnum::UNKNOWN: + break; + case DataFileTypeEnum::VOLUME: + break; + case DataFileTypeEnum::VOLUME_DYNAMIC: + { + VolumeDynamicConnectivityFile* volDynFile = dynamic_cast(overlayDataFile); + CaretAssert(volDynFile); + displayedDataFiles.insert(volDynFile->getParentVolumeFile()); + } + break; } displayedDataFiles.insert(overlayDataFile); diff --git a/src/Brain/ChartTwoOverlay.cxx b/src/Brain/ChartTwoOverlay.cxx index 56f551505..16a55074c 100644 --- a/src/Brain/ChartTwoOverlay.cxx +++ b/src/Brain/ChartTwoOverlay.cxx @@ -1172,6 +1172,8 @@ ChartTwoOverlay::isAllMapsSupported() const break; case DataFileTypeEnum::VOLUME: break; + case DataFileTypeEnum::VOLUME_DYNAMIC: + break; } } } diff --git a/src/Brain/IdentificationTextGenerator.cxx b/src/Brain/IdentificationTextGenerator.cxx index 899f83b5e..b2a60c3c2 100644 --- a/src/Brain/IdentificationTextGenerator.cxx +++ b/src/Brain/IdentificationTextGenerator.cxx @@ -392,7 +392,8 @@ IdentificationTextGenerator::generateVolumeIdentificationText(IdentificationStri } if (dynamic_cast(volumeFile) != NULL) { - boldText.insert(0, "VOLUME CONNECTIVITY DYNAMIC "); + boldText.insert(0, + (DataFileTypeEnum::toOverlayTypeName(DataFileTypeEnum::VOLUME_DYNAMIC) + " ")); } idText.addLine(true, boldText, @@ -474,6 +475,8 @@ IdentificationTextGenerator::generateVolumeIdentificationText(IdentificationStri break; case DataFileTypeEnum::VOLUME: break; + case DataFileTypeEnum::VOLUME_DYNAMIC: + break; } if (limitMapIndicesFlag) { getMapIndicesOfFileUsedInOverlays(ciftiFile, @@ -626,6 +629,8 @@ IdentificationTextGenerator::generateSurfaceIdentificationText(IdentificationStr break; case DataFileTypeEnum::VOLUME: break; + case DataFileTypeEnum::VOLUME_DYNAMIC: + break; } if (limitMapIndicesFlag) { getMapIndicesOfFileUsedInOverlays(cmdf, diff --git a/src/Brain/Overlay.cxx b/src/Brain/Overlay.cxx index 2144afe10..c4b638028 100644 --- a/src/Brain/Overlay.cxx +++ b/src/Brain/Overlay.cxx @@ -40,6 +40,7 @@ #include "SceneClass.h" #include "SceneClassAssistant.h" #include "Surface.h" +#include "VolumeDynamicConnectivityFile.h" #include "VolumeFile.h" using namespace caret; @@ -385,10 +386,75 @@ Overlay::getSelectionData(std::vector& mapFilesOut, } if (useIt) { - if (mapFile->getDataFileType() == DataFileTypeEnum::CONNECTIVITY_DENSE_DYNAMIC) { - CiftiConnectivityMatrixDenseDynamicFile* dynConnFile = dynamic_cast(mapFile); - CaretAssert(dynConnFile); - useIt = dynConnFile->isEnabledAsLayer(); + switch (mapFile->getDataFileType()) { + case DataFileTypeEnum::ANNOTATION: + break; + case DataFileTypeEnum::ANNOTATION_TEXT_SUBSTITUTION: + break; + case DataFileTypeEnum::BORDER: + break; + case DataFileTypeEnum::CONNECTIVITY_DENSE: + break; + case DataFileTypeEnum::CONNECTIVITY_DENSE_DYNAMIC: + { + CiftiConnectivityMatrixDenseDynamicFile* dynConnFile = dynamic_cast(mapFile); + CaretAssert(dynConnFile); + useIt = dynConnFile->isEnabledAsLayer(); + } + break; + case DataFileTypeEnum::CONNECTIVITY_DENSE_LABEL: + break; + case DataFileTypeEnum::CONNECTIVITY_DENSE_PARCEL: + break; + case DataFileTypeEnum::CONNECTIVITY_PARCEL: + break; + case DataFileTypeEnum::CONNECTIVITY_PARCEL_DENSE: + break; + case DataFileTypeEnum::CONNECTIVITY_PARCEL_LABEL: + break; + case DataFileTypeEnum::CONNECTIVITY_PARCEL_SCALAR: + break; + case DataFileTypeEnum::CONNECTIVITY_PARCEL_SERIES: + break; + case DataFileTypeEnum::CONNECTIVITY_DENSE_SCALAR: + break; + case DataFileTypeEnum::CONNECTIVITY_DENSE_TIME_SERIES: + break; + case DataFileTypeEnum::CONNECTIVITY_FIBER_ORIENTATIONS_TEMPORARY: + break; + case DataFileTypeEnum::CONNECTIVITY_FIBER_TRAJECTORY_TEMPORARY: + break; + case DataFileTypeEnum::CONNECTIVITY_SCALAR_DATA_SERIES: + break; + case DataFileTypeEnum::FOCI: + break; + case DataFileTypeEnum::IMAGE: + break; + case DataFileTypeEnum::LABEL: + break; + case DataFileTypeEnum::METRIC: + break; + case DataFileTypeEnum::PALETTE: + break; + case DataFileTypeEnum::RGBA: + break; + case DataFileTypeEnum::SCENE: + break; + case DataFileTypeEnum::SPECIFICATION: + break; + case DataFileTypeEnum::SURFACE: + break; + case DataFileTypeEnum::UNKNOWN: + break; + case DataFileTypeEnum::VOLUME: + break; + case DataFileTypeEnum::VOLUME_DYNAMIC: + { + VolumeDynamicConnectivityFile* volDynFile = dynamic_cast(mapFile); + CaretAssert(volDynFile); + useIt = volDynFile->isEnabledAsLayer(); + } + break; } } diff --git a/src/Brain/SurfaceNodeColoring.cxx b/src/Brain/SurfaceNodeColoring.cxx index db8dc967c..d56b7c77d 100644 --- a/src/Brain/SurfaceNodeColoring.cxx +++ b/src/Brain/SurfaceNodeColoring.cxx @@ -490,6 +490,8 @@ SurfaceNodeColoring::colorSurfaceNodes(const DisplayPropertiesLabels* displayPro break; case DataFileTypeEnum::VOLUME: break; + case DataFileTypeEnum::VOLUME_DYNAMIC: + break; case DataFileTypeEnum::UNKNOWN: break; } diff --git a/src/Common/FileInformation.cxx b/src/Common/FileInformation.cxx index fdfd25c5a..b67393f22 100644 --- a/src/Common/FileInformation.cxx +++ b/src/Common/FileInformation.cxx @@ -488,7 +488,8 @@ FileInformation::getCanonicalPath() const AString FileInformation::getFileExtension() const { - const std::vector workbenchExtensions = DataFileTypeEnum::getFilesExtensionsForEveryFile(); + const bool includeNonWritableFileTypesFlag(true); + const std::vector workbenchExtensions = DataFileTypeEnum::getFilesExtensionsForEveryFile(includeNonWritableFileTypesFlag); for (std::vector::const_iterator extIter = workbenchExtensions.begin(); extIter != workbenchExtensions.end(); diff --git a/src/Files/CaretDataFileHelper.cxx b/src/Files/CaretDataFileHelper.cxx index d6817ab73..775b5808c 100644 --- a/src/Files/CaretDataFileHelper.cxx +++ b/src/Files/CaretDataFileHelper.cxx @@ -389,6 +389,9 @@ CaretDataFileHelper::createCaretDataFileForFileType(const DataFileTypeEnum::Enum case DataFileTypeEnum::VOLUME: caretDataFile = new VolumeFile(); break; + case DataFileTypeEnum::VOLUME_DYNAMIC: + CaretAssertMessage(0, "Never create a Volume Dynamic file"); + break; } return caretDataFile; diff --git a/src/Files/CaretMappableDataFile.h b/src/Files/CaretMappableDataFile.h index 7afc6eb44..a1b44eec6 100644 --- a/src/Files/CaretMappableDataFile.h +++ b/src/Files/CaretMappableDataFile.h @@ -516,6 +516,8 @@ namespace caret { AString& textOut) const; + void updateAfterFileDataChanges(); + protected: CaretMappableDataFile(const CaretMappableDataFile&); @@ -525,8 +527,6 @@ namespace caret { void helpGetSupportedLineSeriesChartDataTypes(std::vector& chartDataTypesOut) const; - void updateAfterFileDataChanges(); - virtual void saveFileDataToScene(const SceneAttributes* sceneAttributes, SceneClass* sceneClass); diff --git a/src/Files/CaretMappableDataFileAndMapSelectionModel.cxx b/src/Files/CaretMappableDataFileAndMapSelectionModel.cxx index 76aa917e8..f71f7827e 100644 --- a/src/Files/CaretMappableDataFileAndMapSelectionModel.cxx +++ b/src/Files/CaretMappableDataFileAndMapSelectionModel.cxx @@ -182,6 +182,9 @@ CaretMappableDataFileAndMapSelectionModel::validateDataFileTypes() case DataFileTypeEnum::VOLUME: isMappableFile = true; break; + case DataFileTypeEnum::VOLUME_DYNAMIC: + isMappableFile = true; + break; } CaretAssert(isMappableFile); diff --git a/src/Files/ChartableTwoFileDelegate.cxx b/src/Files/ChartableTwoFileDelegate.cxx index ec6f826a3..68b0104de 100644 --- a/src/Files/ChartableTwoFileDelegate.cxx +++ b/src/Files/ChartableTwoFileDelegate.cxx @@ -174,7 +174,9 @@ ChartableTwoFileDelegate::updateAfterFileChanged() lineSeriesType = ChartTwoLineSeriesContentTypeEnum::LINE_SERIES_CONTENT_BRAINORDINATE_DATA; } break; - + case DataFileTypeEnum::VOLUME_DYNAMIC: + histogramType = ChartTwoHistogramContentTypeEnum::HISTOGRAM_CONTENT_TYPE_MAP_DATA; + break; } if (m_histogramCharting) { diff --git a/src/Files/ChartableTwoFileMatrixChart.cxx b/src/Files/ChartableTwoFileMatrixChart.cxx index 1a93586ed..66e4a3984 100644 --- a/src/Files/ChartableTwoFileMatrixChart.cxx +++ b/src/Files/ChartableTwoFileMatrixChart.cxx @@ -148,6 +148,8 @@ m_validRowColumnSelectionDimensions(validRowColumnSelectionDimensions) break; case DataFileTypeEnum::VOLUME: break; + case DataFileTypeEnum::VOLUME_DYNAMIC: + break; } bool hasColumnParcelsFlag = false; diff --git a/src/Files/CiftiMappableConnectivityMatrixDataFile.h b/src/Files/CiftiMappableConnectivityMatrixDataFile.h index a14e5fb88..4b795411b 100644 --- a/src/Files/CiftiMappableConnectivityMatrixDataFile.h +++ b/src/Files/CiftiMappableConnectivityMatrixDataFile.h @@ -67,7 +67,7 @@ namespace caret { virtual bool loadMapAverageDataForVoxelIndices(const int32_t mapIndex, const int64_t volumeDimensionIJK[3], const std::vector& voxelIndices); - + void loadDataForRowIndex(const int64_t rowIndex); void loadDataForColumnIndex(const int64_t rowIndex); diff --git a/src/Files/CiftiMappableDataFile.cxx b/src/Files/CiftiMappableDataFile.cxx index 2ba8541d9..7745bc5e0 100644 --- a/src/Files/CiftiMappableDataFile.cxx +++ b/src/Files/CiftiMappableDataFile.cxx @@ -238,6 +238,7 @@ CiftiMappableDataFile::CiftiMappableDataFile(const DataFileTypeEnum::Enum dataFi case DataFileTypeEnum::SURFACE: case DataFileTypeEnum::UNKNOWN: case DataFileTypeEnum::VOLUME: + case DataFileTypeEnum::VOLUME_DYNAMIC: CaretAssertMessage(0, (DataFileTypeEnum::toGuiName(dataFileType) + " is not a CIFTI Mappable Data File.")); break; @@ -799,6 +800,7 @@ CiftiMappableDataFile::validateMappingTypes(const AString& filename) case DataFileTypeEnum::SURFACE: case DataFileTypeEnum::UNKNOWN: case DataFileTypeEnum::VOLUME: + case DataFileTypeEnum::VOLUME_DYNAMIC: throw DataFileException(filename, DataFileTypeEnum::toGuiName(dataFileType) + " is not a CIFTI Mappable Data File."); @@ -1936,6 +1938,8 @@ CiftiMappableDataFile::getMatrixForChartingRGBA(int32_t& numberOfRowsOut, break; case DataFileTypeEnum::VOLUME: break; + case DataFileTypeEnum::VOLUME_DYNAMIC: + break; } if (( ! useMapFileHelperFlag) @@ -4808,6 +4812,9 @@ CiftiMappableDataFile::getSurfaceNodeIdentificationForMaps(const std::vector(mapIndices.size()); diff --git a/src/Files/FilePathNamePrefixCompactor.cxx b/src/Files/FilePathNamePrefixCompactor.cxx index a4ecdf6de..f148c96a5 100644 --- a/src/Files/FilePathNamePrefixCompactor.cxx +++ b/src/Files/FilePathNamePrefixCompactor.cxx @@ -134,13 +134,72 @@ FilePathNamePrefixCompactor::removeMatchingPathPrefixFromCaretDataFiles(const st CaretDataFile* cdf = *iter; CaretAssert(cdf); - if (cdf->getDataFileType() == DataFileTypeEnum::CONNECTIVITY_DENSE_DYNAMIC) { - specialPrefixes.push_back("dynconn - "); - } - else { - specialPrefixes.push_back(""); - } + AString fileSpecialPrefix; + switch (cdf->getDataFileType()) { + case DataFileTypeEnum::ANNOTATION: + break; + case DataFileTypeEnum::ANNOTATION_TEXT_SUBSTITUTION: + break; + case DataFileTypeEnum::BORDER: + break; + case DataFileTypeEnum::CONNECTIVITY_DENSE: + break; + case DataFileTypeEnum::CONNECTIVITY_DENSE_DYNAMIC: + fileSpecialPrefix = "dynconn - "; + break; + case DataFileTypeEnum::CONNECTIVITY_DENSE_LABEL: + break; + case DataFileTypeEnum::CONNECTIVITY_DENSE_PARCEL: + break; + case DataFileTypeEnum::CONNECTIVITY_PARCEL: + break; + case DataFileTypeEnum::CONNECTIVITY_PARCEL_DENSE: + break; + case DataFileTypeEnum::CONNECTIVITY_PARCEL_LABEL: + break; + case DataFileTypeEnum::CONNECTIVITY_PARCEL_SCALAR: + break; + case DataFileTypeEnum::CONNECTIVITY_PARCEL_SERIES: + break; + case DataFileTypeEnum::CONNECTIVITY_DENSE_SCALAR: + break; + case DataFileTypeEnum::CONNECTIVITY_DENSE_TIME_SERIES: + break; + case DataFileTypeEnum::CONNECTIVITY_FIBER_ORIENTATIONS_TEMPORARY: + break; + case DataFileTypeEnum::CONNECTIVITY_FIBER_TRAJECTORY_TEMPORARY: + break; + case DataFileTypeEnum::CONNECTIVITY_SCALAR_DATA_SERIES: + break; + case DataFileTypeEnum::FOCI: + break; + case DataFileTypeEnum::IMAGE: + break; + case DataFileTypeEnum::LABEL: + break; + case DataFileTypeEnum::METRIC: + break; + case DataFileTypeEnum::PALETTE: + break; + case DataFileTypeEnum::RGBA: + break; + case DataFileTypeEnum::SCENE: + break; + case DataFileTypeEnum::SPECIFICATION: + break; + case DataFileTypeEnum::SURFACE: + break; + case DataFileTypeEnum::UNKNOWN: + break; + case DataFileTypeEnum::VOLUME: + break; + case DataFileTypeEnum::VOLUME_DYNAMIC: + fileSpecialPrefix = "voldynconn - "; + break; + } + specialPrefixes.push_back(fileSpecialPrefix); + filePathNames.push_back(cdf->getFileName()); fileNames.push_back(cdf->getFileNameNoPath()); } diff --git a/src/Files/GiftiTypeFile.cxx b/src/Files/GiftiTypeFile.cxx index c51d8e76c..b599fedff 100644 --- a/src/Files/GiftiTypeFile.cxx +++ b/src/Files/GiftiTypeFile.cxx @@ -1154,6 +1154,8 @@ GiftiTypeFile::getBrainordinateMappingMatch(const CaretMappableDataFile* mapFile break; case DataFileTypeEnum::VOLUME: break; + case DataFileTypeEnum::VOLUME_DYNAMIC: + break; } if (giftiFlag) { @@ -1287,6 +1289,8 @@ GiftiTypeFile::getSurfaceNodeIdentificationForMaps(const std::vector& m break; case DataFileTypeEnum::VOLUME: break; + case DataFileTypeEnum::VOLUME_DYNAMIC: + break; } } diff --git a/src/Files/PaletteFile.cxx b/src/Files/PaletteFile.cxx index fc9540670..df910e927 100644 --- a/src/Files/PaletteFile.cxx +++ b/src/Files/PaletteFile.cxx @@ -1700,6 +1700,8 @@ PaletteFile::setDefaultPaletteColorMapping(PaletteColorMapping* paletteColorMapp case DataFileTypeEnum::VOLUME: checkVolume = true; break; + case DataFileTypeEnum::VOLUME_DYNAMIC: + break; } if (invalid) { diff --git a/src/Files/SceneFile.cxx b/src/Files/SceneFile.cxx index cafd92062..52d5a51b9 100644 --- a/src/Files/SceneFile.cxx +++ b/src/Files/SceneFile.cxx @@ -1356,7 +1356,11 @@ SceneFile::getAllDataFileNamesFromAllScenes() const break; case DataFileTypeEnum::VOLUME: break; + case DataFileTypeEnum::VOLUME_DYNAMIC: + validDiskFileFlag = false; + break; case DataFileTypeEnum::UNKNOWN: + validDiskFileFlag = false; break; } } diff --git a/src/Files/SpecFile.cxx b/src/Files/SpecFile.cxx index e5ec85863..850e2dd41 100644 --- a/src/Files/SpecFile.cxx +++ b/src/Files/SpecFile.cxx @@ -1775,11 +1775,72 @@ SpecFile::addToDataFileContentInformation(DataFileContentInformation& dataFileIn bool SpecFile::isDataFileTypeAllowedInSpecFile(const DataFileTypeEnum::Enum dataFileType) { - if (dataFileType == DataFileTypeEnum::CONNECTIVITY_DENSE_DYNAMIC) { - return false; + bool allowedFlag(true); + + switch (dataFileType) { + case DataFileTypeEnum::ANNOTATION: + break; + case DataFileTypeEnum::ANNOTATION_TEXT_SUBSTITUTION: + break; + case DataFileTypeEnum::BORDER: + break; + case DataFileTypeEnum::CONNECTIVITY_DENSE: + break; + case DataFileTypeEnum::CONNECTIVITY_DENSE_DYNAMIC: + allowedFlag = false; + break; + case DataFileTypeEnum::CONNECTIVITY_DENSE_LABEL: + break; + case DataFileTypeEnum::CONNECTIVITY_DENSE_PARCEL: + break; + case DataFileTypeEnum::CONNECTIVITY_PARCEL: + break; + case DataFileTypeEnum::CONNECTIVITY_PARCEL_DENSE: + break; + case DataFileTypeEnum::CONNECTIVITY_PARCEL_LABEL: + break; + case DataFileTypeEnum::CONNECTIVITY_PARCEL_SCALAR: + break; + case DataFileTypeEnum::CONNECTIVITY_PARCEL_SERIES: + break; + case DataFileTypeEnum::CONNECTIVITY_DENSE_SCALAR: + break; + case DataFileTypeEnum::CONNECTIVITY_DENSE_TIME_SERIES: + break; + case DataFileTypeEnum::CONNECTIVITY_FIBER_ORIENTATIONS_TEMPORARY: + break; + case DataFileTypeEnum::CONNECTIVITY_FIBER_TRAJECTORY_TEMPORARY: + break; + case DataFileTypeEnum::CONNECTIVITY_SCALAR_DATA_SERIES: + break; + case DataFileTypeEnum::FOCI: + break; + case DataFileTypeEnum::IMAGE: + break; + case DataFileTypeEnum::LABEL: + break; + case DataFileTypeEnum::METRIC: + break; + case DataFileTypeEnum::PALETTE: + break; + case DataFileTypeEnum::RGBA: + break; + case DataFileTypeEnum::SCENE: + break; + case DataFileTypeEnum::SPECIFICATION: + break; + case DataFileTypeEnum::SURFACE: + break; + case DataFileTypeEnum::UNKNOWN: + break; + case DataFileTypeEnum::VOLUME: + break; + case DataFileTypeEnum::VOLUME_DYNAMIC: + allowedFlag = false; + break; } - return true; + return allowedFlag; } diff --git a/src/Files/VolumeDynamicConnectivityFile.cxx b/src/Files/VolumeDynamicConnectivityFile.cxx index 83cbd05c2..5f0f14441 100644 --- a/src/Files/VolumeDynamicConnectivityFile.cxx +++ b/src/Files/VolumeDynamicConnectivityFile.cxx @@ -27,6 +27,7 @@ #include "CaretAssert.h" #include "ConnectivityCorrelation.h" +#include "ConnectivityDataLoaded.h" #include "DataFileException.h" #include "FileInformation.h" #include "SceneClass.h" @@ -46,14 +47,21 @@ using namespace caret; * Constructor. */ VolumeDynamicConnectivityFile::VolumeDynamicConnectivityFile(const VolumeFile* parentVolumeFile) -: VolumeFile(), +: VolumeFile(DataFileTypeEnum::VOLUME_DYNAMIC), m_parentVolumeFile(parentVolumeFile) { CaretAssert(m_parentVolumeFile); + m_connectivityDataLoaded.reset(new ConnectivityDataLoaded()); + m_sceneAssistant = std::unique_ptr(new SceneClassAssistant()); + m_sceneAssistant->add("m_dataLoadingEnabledFlag", + &m_dataLoadingEnabledFlag); m_sceneAssistant->add("m_enabledAsLayer", &m_enabledAsLayer); + m_sceneAssistant->add("m_connectivityDataLoaded", + "ConnectivityDataLoaded", + m_connectivityDataLoaded.get()); } /** @@ -70,11 +78,30 @@ void VolumeDynamicConnectivityFile::clear() { VolumeFile::clear(); + clearPrivateData(); +} + +/** + * Clear the file. + */ +void +VolumeDynamicConnectivityFile::clearPrivateData() +{ m_voxelData = NULL; m_numberOfVoxels = 0; m_validDataFlag = false; m_enabledAsLayer = false; m_connectivityCorrelation.reset(); + m_connectivityDataLoaded->reset(); +} + +/** + * @return Pointer to the information about last loaded connectivity data. + */ +const ConnectivityDataLoaded* +VolumeDynamicConnectivityFile::getConnectivityDataLoaded() const +{ + return m_connectivityDataLoaded.get(); } /** @@ -123,6 +150,8 @@ VolumeDynamicConnectivityFile::setDataLoadingEnabled(const bool enabled) void VolumeDynamicConnectivityFile::initializeFile() { + clearPrivateData(); + CaretAssert(m_parentVolumeFile); const int64_t numberOfFrames(1); const int32_t numberOfComponents(1); @@ -138,7 +167,7 @@ VolumeDynamicConnectivityFile::initializeFile() fileInfo.getFileComponents(path, nameNoExt, ext); setFileName(FileInformation::assembleFileComponents(path, nameNoExt, - "dynconn.nii.gz")); //DataFileTypeEnum::toFileExtension(DataFileTypeEnum::CONNECTIVITY_DENSE_DYNAMIC))); + DataFileTypeEnum::toFileExtension(DataFileTypeEnum::VOLUME_DYNAMIC))); std::vector dims; getDimensions(dims); @@ -237,46 +266,6 @@ VolumeDynamicConnectivityFile::writeFile(const AString& /*filename*/) throw DataFileException("Writing of Volume Dynamic Connectivity File is not allowed"); } -/** - * Save subclass data to the scene. - * - * @param sceneAttributes - * Attributes for the scene. Scenes may be of different types - * (full, generic, etc) and the attributes should be checked when - * restoring the scene. - * - * @param sceneClass - * sceneClass to which data members should be added. Will always - * be valid (non-NULL). - */ -void -VolumeDynamicConnectivityFile::saveSubClassDataToScene(const SceneAttributes* sceneAttributes, - SceneClass* sceneClass) -{ - m_sceneAssistant->saveMembers(sceneAttributes, - sceneClass); -} - -/** - * Restore file data from the scene. - * - * @param sceneAttributes - * Attributes for the scene. Scenes may be of different types - * (full, generic, etc) and the attributes should be checked when - * restoring the scene. - * - * @param sceneClass - * sceneClass for the instance of a class that implements - * this interface. Will NEVER be NULL. - */ -void -VolumeDynamicConnectivityFile::restoreSubClassDataFromScene(const SceneAttributes* sceneAttributes, - const SceneClass* sceneClass) -{ - m_sceneAssistant->restoreMembers(sceneAttributes, - sceneClass); -} - /** * Clear voxels in this volume */ @@ -286,7 +275,9 @@ VolumeDynamicConnectivityFile::clearVoxels() if (m_voxelData != NULL) { std::fill(m_voxelData, m_voxelData + m_numberOfVoxels, 0.0f); updateScalarColoringForMap(0); + m_connectivityDataLoaded->reset(); } + m_dataLoadedName = ""; } /** @@ -320,6 +311,74 @@ VolumeDynamicConnectivityFile::getTimePointsForVoxel(const int64_t i, } } +/** + * Load connectivity data for the voxel indices and then average the data. + * + * @param volumeDimensionIJK + * Dimensions of the volume. + * @param voxelIndices + * Indices of voxels. + * @return + * True if data was loaded, else false + */ +bool +VolumeDynamicConnectivityFile::loadMapAverageDataForVoxelIndices(const int64_t volumeDimensionIJK[3], + const std::vector& voxelIndices) +{ + /* + * Loading of data disabled? + */ + if ( ! isDataValid()) { + return false; + } + if ( ! isDataLoadingEnabled()) { + + } + + if ( ! matchesDimensions(volumeDimensionIJK[0], + volumeDimensionIJK[1], + volumeDimensionIJK[2])) { + return false; + } + + /* + * Zero out here so that data only gets cleared when data + * is to be loaded. + */ + clearVoxels(); + + std::vector averageData(m_numberOfVoxels, 0.0f); + std::vector data(m_numberOfVoxels); + float validDataCount(0.0f); + for (auto voxel : voxelIndices) { + if (getConnectivityForVoxelIndex(voxel.m_ijk, + data)) { + for (int64_t i = 0; i < m_numberOfVoxels; i++) { + averageData[i] += data[i]; + } + validDataCount += 1.0f; + } + } + if (validDataCount >= 1.0) { + for (int64_t i = 0; i < m_numberOfVoxels; i++) { + m_voxelData[i] = averageData[i] / validDataCount; + } + + const int32_t mapIndex(0); + updateScalarColoringForMap(mapIndex); + + setMapName(mapIndex, + ("Average Voxel Count: " + + AString::number(validDataCount, 'f', 0))); + m_dataLoadedName = ("Average_Voxel_Count_" + + AString::number(validDataCount, 'f', 0)); + + return true; + } + + return false; +} + /** * Load the connectivity for the voxel at the given coordinate. * The loaded data will be in the voxels inside this volume. @@ -327,8 +386,10 @@ VolumeDynamicConnectivityFile::getTimePointsForVoxel(const int64_t i, * * @param xyz * The voxel XYZ. + * @return + * True if data was loaded, else false. */ -void +bool VolumeDynamicConnectivityFile::loadConnectivityForVoxelXYZ(const float xyz[3]) { float indicesFloat[3]; @@ -339,7 +400,28 @@ VolumeDynamicConnectivityFile::loadConnectivityForVoxelXYZ(const float xyz[3]) static_cast(indicesFloat[1]), static_cast(indicesFloat[2]) }; - loadConnectivityForVoxelIndex(ijk); + + if (loadConnectivityForVoxelIndex(ijk)) { + const int32_t invalidRowColumnIndex(-1); + m_connectivityDataLoaded->setVolumeXYZLoading(xyz, + invalidRowColumnIndex, + invalidRowColumnIndex); + setMapName(0, + ("Voxel XYZ: (" + + AString::fromNumbers(xyz, 3, ",") + + ")")); + m_dataLoadedName = ("Voxel_x" + + AString::number(static_cast(xyz[0])) + + "_y" + + AString::number(static_cast(xyz[1])) + + "_z" + + AString::number(static_cast(xyz[2]))); + m_dataLoadedName = m_dataLoadedName.replace('-', 'n'); + + return true; + } + + return false; } @@ -351,18 +433,53 @@ VolumeDynamicConnectivityFile::loadConnectivityForVoxelXYZ(const float xyz[3]) * @param ijk * The voxel index. */ -void +bool VolumeDynamicConnectivityFile::loadConnectivityForVoxelIndex(const int64_t ijk[3]) { + bool validFlag(false); + if ( ! isDataValid()) { - return; + return validFlag; } if ( ! m_dataLoadingEnabledFlag) { - return; + return validFlag; } clearVoxels(); + std::vector data; + if (getConnectivityForVoxelIndex(ijk, data)) { + CaretAssert(m_numberOfVoxels == static_cast(data.size())); + std::copy(data.begin(), + data.end(), + m_voxelData); + + validFlag = true; + } + + const int32_t mapIndex(0); + updateScalarColoringForMap(mapIndex); + + return validFlag; +} + +/** + * Get the connectivity for the given voxel. + * If the voxel index is invalid, zeros are loaded into all voxels. + * + * @param ijk + * The voxel index. + * @param voxelsOut + * Output containing voxel data + * @return + * True if data was loaded. + */ + +bool +VolumeDynamicConnectivityFile::getConnectivityForVoxelIndex(const int64_t ijk[3], + std::vector& voxelsOut) +{ + bool validFlag(false); if (m_voxelData != NULL) { if (indexValid(ijk)) { const int64_t voxelCount(m_dimI * m_dimJ * m_dimK); @@ -391,22 +508,228 @@ VolumeDynamicConnectivityFile::loadConnectivityForVoxelIndex(const int64_t ijk[3 } const int64_t myTimePointOffset = getVoxelOffset(ijk[0], ijk[1], ijk[2], 0); - std::vector coefficients; m_connectivityCorrelation->getCorrelationForGroup(myTimePointOffset, - coefficients); - CaretAssert(voxelCount == static_cast(coefficients.size())); - std::copy(coefficients.begin(), - coefficients.end(), - m_voxelData); - - setMapName(0, - ("Voxel (" - + AString::fromNumbers(ijk, 3, ", ") - + ")")); + voxelsOut); + CaretAssert(voxelCount == static_cast(voxelsOut.size())); + validFlag = true; } } + + return validFlag; +} + +/** + * @return True if this matches the given dimensions + * + * @param dimI + * I dimension + * @param dimJ + * J dimension + * @param dimK + * K dimension + */ +bool +VolumeDynamicConnectivityFile::matchesDimensions(const int64_t dimI, + const int64_t dimJ, + const int64_t dimK) const +{ + if ((dimI == m_dimI) + && (dimJ == m_dimJ) + && (dimK == m_dimK)) { + return true; + } - const int32_t mapIndex(0); - updateScalarColoringForMap(mapIndex); + return false; +} + +/** + * @return A volume file using the loaded data (will return NULL if there is an error). + * + * @param directoryName + * @param errorMessageOut + */ +VolumeFile* +VolumeDynamicConnectivityFile::newVolumeFileFromLoadedData(const AString& directoryName, + AString& errorMessageOut) +{ + errorMessageOut.clear(); + + bool validDataFlag(false); + switch (m_connectivityDataLoaded->getMode()) { + case ConnectivityDataLoaded::MODE_COLUMN: + break; + case ConnectivityDataLoaded::MODE_NONE: + break; + case ConnectivityDataLoaded::MODE_ROW: + break; + case ConnectivityDataLoaded::MODE_SURFACE_NODE: + break; + case ConnectivityDataLoaded::MODE_SURFACE_NODE_AVERAGE: + break; + case ConnectivityDataLoaded::MODE_VOXEL_IJK_AVERAGE: + validDataFlag = true; + break; + case ConnectivityDataLoaded::MODE_VOXEL_XYZ: + validDataFlag = true; + break; + } + + if ( ! validDataFlag) { + errorMessageOut = "No voxel connectivity data is loaded"; + return NULL; + } + + VolumeFile* vf(NULL); + + try { + std::vector dimensions; + dimensions.push_back(m_dimI); + dimensions.push_back(m_dimJ); + dimensions.push_back(m_dimK); + dimensions.push_back(1); + dimensions.push_back(1); + + const VolumeSpace vs = getVolumeSpace(); + vf = new VolumeFile(dimensions, + vs.getSform(), + 1, + SubvolumeAttributes::FUNCTIONAL, + m_header); + + float* voxelData = const_cast(vf->getFrame(0)); + std::copy(m_voxelData, m_voxelData + m_numberOfVoxels, + voxelData); + + /* + * May need to convert a remote path to a local path + */ + FileInformation fileNameInfo(getFileName()); + const AString volumeFileName = fileNameInfo.getAsLocalAbsoluteFilePath(directoryName, + vf->getDataFileType()); + + /* + * Create name of volume file data loaded information + */ + FileInformation volumeFileInfo(volumeFileName); + AString thePath, theName, theExtension; + volumeFileInfo.getFileComponents(thePath, + theName, + theExtension); + theName.append("_" + m_dataLoadedName); + AString newFileName = FileInformation::assembleFileComponents(thePath, + theName, + theExtension); + if (newFileName.endsWith(".nii")) { + newFileName.append(".gz"); + } + vf->setFileName(newFileName); + + + /* + * Need to copy color palette since it may be the default + */ + PaletteColorMapping* volumePalette = vf->getMapPaletteColorMapping(0); + CaretAssert(volumePalette); + const PaletteColorMapping* myPalette = getMapPaletteColorMapping(0); + CaretAssert(myPalette); + volumePalette->copy(*myPalette, + true); + + vf->updateAfterFileDataChanges(); + vf->updateScalarColoringForMap(0); + vf->setModified(); + } + catch (const DataFileException& dfe) { + errorMessageOut = dfe.whatString(); + if (vf != NULL) { + delete vf; + vf = NULL; + } + } + + return vf; +} + + +/** + * Save data to the scene. + * + * @param sceneAttributes + * Attributes for the scene. Scenes may be of different types + * (full, generic, etc) and the attributes should be checked when + * restoring the scene. + * + * @param sceneClass + * sceneClass to which data members should be added. Will always + * be valid (non-NULL). + */ +void +VolumeDynamicConnectivityFile::saveFileDataToScene(const SceneAttributes* sceneAttributes, + SceneClass* sceneClass) +{ + VolumeFile::saveFileDataToScene(sceneAttributes, + sceneClass); + m_sceneAssistant->saveMembers(sceneAttributes, + sceneClass); +} + +/** + * Restore file data from the scene. + * + * @param sceneAttributes + * Attributes for the scene. Scenes may be of different types + * (full, generic, etc) and the attributes should be checked when + * restoring the scene. + * + * @param sceneClass + * sceneClass for the instance of a class that implements + * this interface. Will NEVER be NULL. + */ +void +VolumeDynamicConnectivityFile::restoreFileDataFromScene(const SceneAttributes* sceneAttributes, + const SceneClass* sceneClass) +{ + m_connectivityDataLoaded->reset(); + + VolumeFile::restoreFileDataFromScene(sceneAttributes, + sceneClass); + m_sceneAssistant->restoreMembers(sceneAttributes, + sceneClass); + + + switch (m_connectivityDataLoaded->getMode()) { + case ConnectivityDataLoaded::MODE_COLUMN: + break; + case ConnectivityDataLoaded::MODE_NONE: + break; + case ConnectivityDataLoaded::MODE_ROW: + break; + case ConnectivityDataLoaded::MODE_SURFACE_NODE: + break; + case ConnectivityDataLoaded::MODE_SURFACE_NODE_AVERAGE: + break; + case ConnectivityDataLoaded::MODE_VOXEL_IJK_AVERAGE: + { + int64_t dimIJK[3]; + std::vector voxelIJKs; + m_connectivityDataLoaded->getVolumeAverageVoxelLoading(dimIJK, + voxelIJKs); + loadMapAverageDataForVoxelIndices(dimIJK, + voxelIJKs); + } + break; + case ConnectivityDataLoaded::MODE_VOXEL_XYZ: + { + float xyz[3]; + int64_t rowIndex(-1); + int64_t columnIndex(-1); + + m_connectivityDataLoaded->getVolumeXYZLoading(xyz, + rowIndex, + columnIndex); + loadConnectivityForVoxelXYZ(xyz); + } + break; + } } diff --git a/src/Files/VolumeDynamicConnectivityFile.h b/src/Files/VolumeDynamicConnectivityFile.h index 6e7657ebe..fb3124523 100644 --- a/src/Files/VolumeDynamicConnectivityFile.h +++ b/src/Files/VolumeDynamicConnectivityFile.h @@ -31,6 +31,7 @@ namespace caret { class ConnectivityCorrelation; + class ConnectivityDataLoaded; class VolumeDynamicConnectivityFile : public VolumeFile { @@ -65,14 +66,24 @@ namespace caret { void setEnabledAsLayer(const bool enabled); - void loadConnectivityForVoxelIndex(const int64_t ijk[3]); + bool loadConnectivityForVoxelXYZ(const float xyz[3]); - void loadConnectivityForVoxelXYZ(const float xyz[3]); + bool loadMapAverageDataForVoxelIndices(const int64_t volumeDimensionIJK[3], + const std::vector& voxelIndices); bool isDataLoadingEnabled() const; void setDataLoadingEnabled(const bool enabled); + const ConnectivityDataLoaded* getConnectivityDataLoaded() const; + + bool matchesDimensions(const int64_t dimI, + const int64_t dimJ, + const int64_t dimK) const; + + VolumeFile* newVolumeFileFromLoadedData(const AString& directoryName, + AString& errorMessageOut); + // ADD_NEW_METHODS_HERE @@ -81,13 +92,15 @@ namespace caret { protected: - virtual void saveSubClassDataToScene(const SceneAttributes* sceneAttributes, - SceneClass* sceneClass); + virtual void saveFileDataToScene(const SceneAttributes* sceneAttributes, + SceneClass* sceneClass) override; - virtual void restoreSubClassDataFromScene(const SceneAttributes* sceneAttributes, - const SceneClass* sceneClass); + virtual void restoreFileDataFromScene(const SceneAttributes* sceneAttributes, + const SceneClass* sceneClass) override; private: + void clearPrivateData(); + void clearVoxels(); void getTimePointsForVoxel(const int64_t i, @@ -106,6 +119,11 @@ namespace caret { return offset; } + bool loadConnectivityForVoxelIndex(const int64_t ijk[3]); + + bool getConnectivityForVoxelIndex(const int64_t ijk[3], + std::vector& voxelsOut) ; + const VolumeFile* m_parentVolumeFile; std::unique_ptr m_sceneAssistant; @@ -128,12 +146,16 @@ namespace caret { int64_t m_dimTime = 0; + AString m_dataLoadedName; + bool m_validDataFlag = false; bool m_enabledAsLayer = true; bool m_dataLoadingEnabledFlag = true; + std::unique_ptr m_connectivityDataLoaded; + // ADD_NEW_MEMBERS_HERE }; diff --git a/src/Files/VolumeFile.cxx b/src/Files/VolumeFile.cxx index afec444e3..05c7a9236 100644 --- a/src/Files/VolumeFile.cxx +++ b/src/Files/VolumeFile.cxx @@ -79,6 +79,19 @@ VolumeFile::setVoxelColoringEnabled(const bool enabled) : "Volume coloring is disabled.")); } +/** protected, used by dynamic volume file */ +VolumeFile::VolumeFile(const DataFileTypeEnum::Enum dataFileType) +: VolumeBase(), CaretMappableDataFile(dataFileType) +{//CaretPointers initialize to NULL, and this isn't an operator= + CaretAssert((dataFileType == DataFileTypeEnum::VOLUME) + || (dataFileType == DataFileTypeEnum::VOLUME_DYNAMIC)); + m_forceUpdateOfGroupAndNameHierarchy = true; + for (int32_t i = 0; i < BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS; i++) { + m_chartingEnabledForTab[i] = false; + } + validateMembers(); +} + VolumeFile::VolumeFile() : VolumeBase(), CaretMappableDataFile(DataFileTypeEnum::VOLUME) diff --git a/src/Files/VolumeFile.h b/src/Files/VolumeFile.h index ee1256942..3a8e488c6 100644 --- a/src/Files/VolumeFile.h +++ b/src/Files/VolumeFile.h @@ -129,6 +129,8 @@ namespace caret { static const AString s_paletteColorMappingNameInMetaData; protected: + VolumeFile(const DataFileTypeEnum::Enum dataFileType); + virtual void saveFileDataToScene(const SceneAttributes* sceneAttributes, SceneClass* sceneClass); diff --git a/src/GuiQt/BrainBrowserWindowOrientedToolBox.cxx b/src/GuiQt/BrainBrowserWindowOrientedToolBox.cxx index 9ea622644..23c8c1569 100644 --- a/src/GuiQt/BrainBrowserWindowOrientedToolBox.cxx +++ b/src/GuiQt/BrainBrowserWindowOrientedToolBox.cxx @@ -755,12 +755,16 @@ BrainBrowserWindowOrientedToolBox::receiveEvent(Event* event) if (vf->isMappedWithLabelTable()) { haveLabels = true; } - const VolumeDynamicConnectivityFile* volDynConnFile = vf->getVolumeDynamicConnectivityFile(); - if (volDynConnFile != NULL) { - haveConnFiles = true; - } } break; + case DataFileTypeEnum::VOLUME_DYNAMIC: + haveConnFiles = true; +// haveVolumes = true; +// const VolumeDynamicConnectivityFile* volDynConnFile = vf->getVolumeDynamicConnectivityFile(); +// if (volDynConnFile != NULL) { +// haveConnFiles = true; +// } + break; } } diff --git a/src/GuiQt/CiftiConnectivityMatrixViewController.cxx b/src/GuiQt/CiftiConnectivityMatrixViewController.cxx index 77b71f7ac..ef74b7bbb 100644 --- a/src/GuiQt/CiftiConnectivityMatrixViewController.cxx +++ b/src/GuiQt/CiftiConnectivityMatrixViewController.cxx @@ -217,12 +217,12 @@ CiftiConnectivityMatrixViewController::updateViewController() copyToolButton = new QToolButton(); copyToolButton->setText("Copy"); - copyToolButton->setToolTip("Copy loaded row data to a new CIFTI Scalar File"); + copyToolButton->setToolTip("Copy loaded connectivity data into a writable file that is added to layers"); m_fileCopyToolButtons.push_back(copyToolButton); copyToolButton->setObjectName(objectNamePrefix + "CopyButton"); macroManager->addMacroSupportToObject(copyToolButton, - "Copy load row to new CIFTI scalar file for " + descriptivePrefix); + "Copy load row to new CIFTI scalar or Volume file for " + descriptivePrefix); comboBox = new QComboBox(); m_fiberOrientationFileComboBoxes.push_back(comboBox); @@ -620,7 +620,20 @@ CiftiConnectivityMatrixViewController::copyToolButtonClicked(int indx) } } else if (volDynConnFile != NULL) { - + VolumeFile* newVolumeFile = volDynConnFile->newVolumeFileFromLoadedData(directoryName, + errorMessage); + if (newVolumeFile != NULL) { + EventDataFileAdd dataFileAdd(newVolumeFile); + EventManager::get()->sendEvent(dataFileAdd.getPointer()); + + if (dataFileAdd.isError()) { + errorMessage = dataFileAdd.getErrorMessage(); + errorFlag = true; + } + } + else { + errorFlag = true; + } } else if (trajFile != NULL) { CiftiFiberTrajectoryFile* newTrajFile = trajFile->newFiberTrajectoryFileFromLoadedRowData(directoryName, diff --git a/src/GuiQt/GuiManager.cxx b/src/GuiQt/GuiManager.cxx index 3e0ad6281..08379763d 100644 --- a/src/GuiQt/GuiManager.cxx +++ b/src/GuiQt/GuiManager.cxx @@ -786,6 +786,7 @@ GuiManager::testForModifiedFiles(const TestModifiedMode testModifiedMode, dataFileTypesToExclude.push_back(DataFileTypeEnum::CONNECTIVITY_DENSE_DYNAMIC); dataFileTypesToExclude.push_back(DataFileTypeEnum::CONNECTIVITY_FIBER_ORIENTATIONS_TEMPORARY); dataFileTypesToExclude.push_back(DataFileTypeEnum::CONNECTIVITY_FIBER_TRAJECTORY_TEMPORARY); + dataFileTypesToExclude.push_back(DataFileTypeEnum::VOLUME_DYNAMIC); switch (testModifiedMode) { case TEST_FOR_MODIFIED_FILES_MODE_FOR_EXIT: diff --git a/src/GuiQt/IdentifyBrainordinateDialog.cxx b/src/GuiQt/IdentifyBrainordinateDialog.cxx index 8fb622d2d..8e46197cb 100644 --- a/src/GuiQt/IdentifyBrainordinateDialog.cxx +++ b/src/GuiQt/IdentifyBrainordinateDialog.cxx @@ -183,6 +183,8 @@ IdentifyBrainordinateDialog::IdentifyBrainordinateDialog(QWidget* parent) break; case DataFileTypeEnum::VOLUME: break; + case DataFileTypeEnum::VOLUME_DYNAMIC: + break; } if (parcelSourceDimension != PARCEL_SOURCE_INVALID_DIMENSION) { diff --git a/src/GuiQt/OverlayViewController.cxx b/src/GuiQt/OverlayViewController.cxx index 8466ee3a9..c303ef4ae 100644 --- a/src/GuiQt/OverlayViewController.cxx +++ b/src/GuiQt/OverlayViewController.cxx @@ -989,9 +989,71 @@ OverlayViewController::menuConstructionAboutToShow() menuText += suffix; } - const bool notDynConnFileFlag = (caretDataFile->getDataFileType() != DataFileTypeEnum::CONNECTIVITY_DENSE_DYNAMIC); - m_constructionReloadFileAction->setEnabled(notDynConnFileFlag); - m_copyPathAndFileNameToClipboardAction->setEnabled(notDynConnFileFlag); + bool dynConnFlag(false); + switch (caretDataFile->getDataFileType()) { + case DataFileTypeEnum::ANNOTATION: + break; + case DataFileTypeEnum::ANNOTATION_TEXT_SUBSTITUTION: + break; + case DataFileTypeEnum::BORDER: + break; + case DataFileTypeEnum::CONNECTIVITY_DENSE: + break; + case DataFileTypeEnum::CONNECTIVITY_DENSE_DYNAMIC: + dynConnFlag = true; + break; + case DataFileTypeEnum::CONNECTIVITY_DENSE_LABEL: + break; + case DataFileTypeEnum::CONNECTIVITY_DENSE_PARCEL: + break; + case DataFileTypeEnum::CONNECTIVITY_PARCEL: + break; + case DataFileTypeEnum::CONNECTIVITY_PARCEL_DENSE: + break; + case DataFileTypeEnum::CONNECTIVITY_PARCEL_LABEL: + break; + case DataFileTypeEnum::CONNECTIVITY_PARCEL_SCALAR: + break; + case DataFileTypeEnum::CONNECTIVITY_PARCEL_SERIES: + break; + case DataFileTypeEnum::CONNECTIVITY_DENSE_SCALAR: + break; + case DataFileTypeEnum::CONNECTIVITY_DENSE_TIME_SERIES: + break; + case DataFileTypeEnum::CONNECTIVITY_FIBER_ORIENTATIONS_TEMPORARY: + break; + case DataFileTypeEnum::CONNECTIVITY_FIBER_TRAJECTORY_TEMPORARY: + break; + case DataFileTypeEnum::CONNECTIVITY_SCALAR_DATA_SERIES: + break; + case DataFileTypeEnum::FOCI: + break; + case DataFileTypeEnum::IMAGE: + break; + case DataFileTypeEnum::LABEL: + break; + case DataFileTypeEnum::METRIC: + break; + case DataFileTypeEnum::PALETTE: + break; + case DataFileTypeEnum::RGBA: + break; + case DataFileTypeEnum::SCENE: + break; + case DataFileTypeEnum::SPECIFICATION: + break; + case DataFileTypeEnum::SURFACE: + break; + case DataFileTypeEnum::UNKNOWN: + break; + case DataFileTypeEnum::VOLUME: + break; + case DataFileTypeEnum::VOLUME_DYNAMIC: + dynConnFlag = true; + break; + } + m_constructionReloadFileAction->setEnabled( ! dynConnFlag); + m_copyPathAndFileNameToClipboardAction->setEnabled( ! dynConnFlag); } m_constructionReloadFileAction->setText(menuText); diff --git a/src/GuiQt/UserInputModeViewContextMenu.cxx b/src/GuiQt/UserInputModeViewContextMenu.cxx index 7a040a652..81dec53ac 100644 --- a/src/GuiQt/UserInputModeViewContextMenu.cxx +++ b/src/GuiQt/UserInputModeViewContextMenu.cxx @@ -76,6 +76,7 @@ #include "Surface.h" #include "UserInputModeFociWidget.h" #include "UserInputTileTabsContextMenu.h" +#include "VolumeDynamicConnectivityFile.h" #include "VolumeFile.h" #include "WuQDataEntryDialog.h" #include "WuQMessageBox.h" @@ -671,7 +672,7 @@ UserInputModeViewContextMenu::addLabelRegionOfInterestActions() std::vector ciftiMatrixFiles; std::vector ciftiFiberTrajectoryFiles; std::vector chartableFiles; - + std::vector volDynConnFiles; /* * Get all files in displayed overlays */ @@ -700,18 +701,24 @@ UserInputModeViewContextMenu::addLabelRegionOfInterestActions() if (lineSeriesFile != NULL) { chartableFiles.push_back(lineSeriesFile); } + + VolumeDynamicConnectivityFile* volDynnFile = dynamic_cast(mapFile); + if (volDynnFile != NULL) { + volDynConnFiles.push_back(volDynnFile); + } } - const bool hasCiftiConnectivity = (ciftiMatrixFiles.empty() == false); - const bool haveCiftiFiberTrajectoryFiles = (ciftiFiberTrajectoryFiles.empty() == false); - const bool haveChartableFiles = (chartableFiles.empty() == false); + const bool hasDynamicConnectivity = ( ( ! ciftiMatrixFiles.empty()) + || ( ! volDynConnFiles.empty())); + const bool haveCiftiFiberTrajectoryFiles = ( ! ciftiFiberTrajectoryFiles.empty()); + const bool haveChartableFiles = ( ! chartableFiles.empty()); /* * Actions for each file type */ - QList ciftiConnectivityActions; - QActionGroup* ciftiConnectivityActionGroup = new QActionGroup(this); - QObject::connect(ciftiConnectivityActionGroup, SIGNAL(triggered(QAction*)), - this, SLOT(parcelCiftiConnectivityActionSelected(QAction*))); + QList connectivityActions; + QActionGroup* connectivityActionGroup = new QActionGroup(this); + QObject::connect(connectivityActionGroup, SIGNAL(triggered(QAction*)), + this, SLOT(connectivityActionSelected(QAction*))); QList ciftiFiberTrajectoryActions; QActionGroup* ciftiFiberTrajectoryActionGroup = new QActionGroup(this); @@ -730,7 +737,7 @@ UserInputModeViewContextMenu::addLabelRegionOfInterestActions() + "\" from map \"" + parcelConnectivity->mapName + "\""); - if (hasCiftiConnectivity) { + if (hasDynamicConnectivity) { bool matchFlag = false; if (parcelType == ParcelType::PARCEL_TYPE_SURFACE_NODES) { matchFlag = true; @@ -747,14 +754,23 @@ UserInputModeViewContextMenu::addLabelRegionOfInterestActions() break; } } + + for (auto volDynFile : volDynConnFiles) { + if (volDynFile->matchesDimensions(parcelConnectivity->volumeDimensions[0], + parcelConnectivity->volumeDimensions[1], + parcelConnectivity->volumeDimensions[2])) { + matchFlag = true; + break; + } + } } if (matchFlag) { const AString actionName("Show Connectivity for " + sourceLabelName); - QAction* action = ciftiConnectivityActionGroup->addAction(actionName); + QAction* action = connectivityActionGroup->addAction(actionName); action->setData(qVariantFromValue((void*)parcelConnectivity)); - ciftiConnectivityActions.push_back(action); + connectivityActions.push_back(action); } } @@ -802,7 +818,7 @@ UserInputModeViewContextMenu::addLabelRegionOfInterestActions() } } - addActionsToMenu(ciftiConnectivityActions, + addActionsToMenu(connectivityActions, true); addActionsToMenu(ciftiFiberTrajectoryActions, true); @@ -981,7 +997,7 @@ UserInputModeViewContextMenu::addFociActions() * Action that was selected. */ void -UserInputModeViewContextMenu::parcelCiftiConnectivityActionSelected(QAction* action) +UserInputModeViewContextMenu::connectivityActionSelected(QAction* action) { void* pointer = action->data().value(); ParcelConnectivity* pc = (ParcelConnectivity*)pointer; @@ -1043,6 +1059,15 @@ UserInputModeViewContextMenu::parcelCiftiConnectivityActionSelected(QAction* act pc->ciftiConnectivityManager->loadAverageDataForVoxelIndices(pc->brain, pc->volumeDimensions, voxelIndices); + + { + std::vector volDynConnFiles; + pc->brain->getVolumeDynamicConnectivityFiles(volDynConnFiles); + for (auto vdcf : volDynConnFiles) { + vdcf->loadMapAverageDataForVoxelIndices(pc->volumeDimensions, + voxelIndices); + } + } break; } } diff --git a/src/GuiQt/UserInputModeViewContextMenu.h b/src/GuiQt/UserInputModeViewContextMenu.h index f915e087a..1f2021948 100644 --- a/src/GuiQt/UserInputModeViewContextMenu.h +++ b/src/GuiQt/UserInputModeViewContextMenu.h @@ -84,7 +84,7 @@ namespace caret { void parcelCiftiFiberTrajectoryActionSelected(QAction* action); - void parcelCiftiConnectivityActionSelected(QAction* action); + void connectivityActionSelected(QAction* action); void parcelChartableDataActionSelected(QAction* action); -- GitLab From a40e23a237a3b839b12c01817bc640f4a32c45fc Mon Sep 17 00:00:00 2001 From: John Harwell Date: Fri, 12 Jul 2019 15:15:30 -0500 Subject: [PATCH 385/427] WB-860 NIFTI Volume Timeseries Issues: Implemented dynamic connectivity for Metric Files including redesign of ConnectivityCorrelation different memory organizations of brainordinates and timepoints. --- src/Brain/Brain.cxx | 40 ++ src/Brain/Brain.h | 3 + src/Brain/BrainStructure.cxx | 13 +- src/Brain/BrowserTabContent.cxx | 8 + src/Brain/ChartTwoOverlay.cxx | 2 + src/Brain/IdentificationTextGenerator.cxx | 26 +- src/Brain/Overlay.cxx | 8 + src/Brain/SurfaceNodeColoring.cxx | 3 +- src/Common/ConnectivityCorrelation.cxx | 618 +++++++++++----- src/Common/ConnectivityCorrelation.h | 142 +++- src/Common/DataFileTypeEnum.cxx | 13 +- src/Common/DataFileTypeEnum.h | 4 +- src/Files/CMakeLists.txt | 2 + src/Files/CaretDataFileHelper.cxx | 3 + ...etMappableDataFileAndMapSelectionModel.cxx | 3 + src/Files/ChartableTwoFileDelegate.cxx | 3 + src/Files/ChartableTwoFileMatrixChart.cxx | 2 + src/Files/CiftiMappableDataFile.cxx | 7 + src/Files/FilePathNamePrefixCompactor.cxx | 3 + src/Files/GiftiTypeFile.cxx | 80 ++- src/Files/MetricDynamicConnectivityFile.cxx | 671 ++++++++++++++++++ src/Files/MetricDynamicConnectivityFile.h | 138 ++++ src/Files/MetricFile.cxx | 90 +++ src/Files/MetricFile.h | 14 + src/Files/PaletteFile.cxx | 2 + src/Files/SceneFile.cxx | 3 + src/Files/SpecFile.cxx | 3 + src/Files/VolumeDynamicConnectivityFile.cxx | 97 ++- src/Files/VolumeDynamicConnectivityFile.h | 2 + src/Gifti/GiftiDataArray.cxx | 16 +- src/Gifti/GiftiDataArray.h | 4 + .../BrainBrowserWindowOrientedToolBox.cxx | 3 + .../CiftiConnectivityMatrixViewController.cxx | 70 +- .../CiftiConnectivityMatrixViewController.h | 2 + src/GuiQt/GuiManager.cxx | 14 + src/GuiQt/IdentifyBrainordinateDialog.cxx | 2 + src/GuiQt/OverlayViewController.cxx | 3 + src/GuiQt/UserInputModeViewContextMenu.cxx | 46 +- 38 files changed, 1871 insertions(+), 292 deletions(-) create mode 100644 src/Files/MetricDynamicConnectivityFile.cxx create mode 100644 src/Files/MetricDynamicConnectivityFile.h diff --git a/src/Brain/Brain.cxx b/src/Brain/Brain.cxx index 309f8801b..e0dd7ae87 100644 --- a/src/Brain/Brain.cxx +++ b/src/Brain/Brain.cxx @@ -93,6 +93,7 @@ #include "IdentificationManager.h" #include "ImageFile.h" #include "MathFunctions.h" +#include "MetricDynamicConnectivityFile.h" #include "MetricFile.h" #include "ModelChart.h" #include "ModelChartTwo.h" @@ -835,6 +836,8 @@ Brain::resetBrainKeepSceneFiles() break; case DataFileTypeEnum::METRIC: break; + case DataFileTypeEnum::METRIC_DYNAMIC: + break; case DataFileTypeEnum::PALETTE: keepFileFlag = false; break; @@ -1615,6 +1618,32 @@ Brain::getVolumeDynamicConnectivityFiles(std::vector& metricDynamicConnectivityFilesOut) const +{ + metricDynamicConnectivityFilesOut.clear(); + + for (auto bs : m_brainStructures) { + std::vector metricFiles; + bs->getMetricFiles(metricFiles); + + for (auto mf : metricFiles) { + MetricDynamicConnectivityFile* metricDynConn = mf->getMetricDynamicConnectivityFile(); + if (metricDynConn != NULL) { + if (metricDynConn->isDataValid()) { + metricDynamicConnectivityFilesOut.push_back(metricDynConn); + } + } + } + } +} + /** * Initialize a volume file. If it is functional data and contains more than one timepoint * setup its volume dynamic connectivity file. @@ -4479,6 +4508,9 @@ Brain::addDataFile(CaretDataFile* caretDataFile) true); } break; + case DataFileTypeEnum::METRIC_DYNAMIC: + CaretAssertMessage(0, "Metric dynamic files should never be added to brain"); + break; case DataFileTypeEnum::PALETTE: { throw DataFileException(caretDataFile->getFileName(), @@ -5370,6 +5402,9 @@ Brain::addReadOrReloadDataFile(const FileModeAddReadReload fileMode, structure, markDataFileAsModified); break; + case DataFileTypeEnum::METRIC_DYNAMIC: + CaretAssertMessage(0, "Metric dynamic files are never read by Brain"); + break; case DataFileTypeEnum::PALETTE: caretDataFileRead = addReadOrReloadPaletteFile(fileMode, caretDataFile, @@ -6811,6 +6846,8 @@ Brain::writeDataFile(CaretDataFile* caretDataFile) break; case DataFileTypeEnum::METRIC: break; + case DataFileTypeEnum::METRIC_DYNAMIC: + break; case DataFileTypeEnum::PALETTE: break; case DataFileTypeEnum::RGBA: @@ -6902,6 +6939,9 @@ Brain::removeWithoutDeleteDataFile(const CaretDataFile* caretDataFile) break; case DataFileTypeEnum::METRIC: break; + case DataFileTypeEnum::METRIC_DYNAMIC: + canBeRemovedFlag = false; + break; case DataFileTypeEnum::PALETTE: break; case DataFileTypeEnum::RGBA: diff --git a/src/Brain/Brain.h b/src/Brain/Brain.h index 61ff6d2ca..a136fa8cd 100644 --- a/src/Brain/Brain.h +++ b/src/Brain/Brain.h @@ -86,6 +86,7 @@ namespace caret { class ImageFile; class LabelFile; class MetricFile; + class MetricDynamicConnectivityFile; class ModelChart; class ModelChartTwo; class ModelSurfaceMontage; @@ -191,6 +192,8 @@ namespace caret { void getVolumeDynamicConnectivityFiles(std::vector& volumeDynamicConnectivityFilesOut) const; + void getMetricDynamicConnectivityFiles(std::vector& metricDynamicConnectivityFilesOut) const; + void resetBrain(); void resetBrainKeepSceneFiles(); diff --git a/src/Brain/BrainStructure.cxx b/src/Brain/BrainStructure.cxx index 700b053aa..14df5afa2 100644 --- a/src/Brain/BrainStructure.cxx +++ b/src/Brain/BrainStructure.cxx @@ -45,6 +45,7 @@ #include "SelectionManager.h" #include "LabelFile.h" #include "MathFunctions.h" +#include "MetricDynamicConnectivityFile.h" #include "MetricFile.h" #include "ModelSurface.h" #include "OverlaySet.h" @@ -1136,9 +1137,15 @@ BrainStructure::getAllDataFiles(std::vector& allDataFilesOut) co allDataFilesOut.insert(allDataFilesOut.end(), m_labelFiles.begin(), m_labelFiles.end()); - allDataFilesOut.insert(allDataFilesOut.end(), - m_metricFiles.begin(), - m_metricFiles.end()); + for (auto mf : m_metricFiles) { + allDataFilesOut.push_back(mf); + MetricDynamicConnectivityFile* dynFile = mf->getMetricDynamicConnectivityFile(); + if (dynFile != NULL) { + if (dynFile->isDataValid()) { + allDataFilesOut.push_back(dynFile); + } + } + } allDataFilesOut.insert(allDataFilesOut.end(), m_rgbaFiles.begin(), m_rgbaFiles.end()); diff --git a/src/Brain/BrowserTabContent.cxx b/src/Brain/BrowserTabContent.cxx index bb3c00dbb..b578eee2e 100644 --- a/src/Brain/BrowserTabContent.cxx +++ b/src/Brain/BrowserTabContent.cxx @@ -63,6 +63,7 @@ #include "LabelFile.h" #include "MathFunctions.h" #include "Matrix4x4.h" +#include "MetricDynamicConnectivityFile.h" #include "ModelChart.h" #include "ModelChartTwo.h" #include "ModelSurface.h" @@ -2113,6 +2114,13 @@ BrowserTabContent::getFilesDisplayedInTab(std::vector& displayed break; case DataFileTypeEnum::METRIC: break; + case DataFileTypeEnum::METRIC_DYNAMIC: + { + MetricDynamicConnectivityFile* metricDynFile = dynamic_cast(overlayDataFile); + CaretAssert(metricDynFile); + displayedDataFiles.insert(metricDynFile); + } + break; case DataFileTypeEnum::PALETTE: break; case DataFileTypeEnum::RGBA: diff --git a/src/Brain/ChartTwoOverlay.cxx b/src/Brain/ChartTwoOverlay.cxx index 16a55074c..ff59dbf95 100644 --- a/src/Brain/ChartTwoOverlay.cxx +++ b/src/Brain/ChartTwoOverlay.cxx @@ -1158,6 +1158,8 @@ ChartTwoOverlay::isAllMapsSupported() const break; case DataFileTypeEnum::METRIC: break; + case DataFileTypeEnum::METRIC_DYNAMIC: + break; case DataFileTypeEnum::PALETTE: break; case DataFileTypeEnum::RGBA: diff --git a/src/Brain/IdentificationTextGenerator.cxx b/src/Brain/IdentificationTextGenerator.cxx index b2a60c3c2..fe5ec45c7 100644 --- a/src/Brain/IdentificationTextGenerator.cxx +++ b/src/Brain/IdentificationTextGenerator.cxx @@ -53,6 +53,7 @@ #include "Histogram.h" #include "ImageFile.h" #include "MapFileDataSelector.h" +#include "MetricDynamicConnectivityFile.h" #include "OverlaySet.h" #include "SelectionItemBorderSurface.h" #include "SelectionItemChartDataSeries.h" @@ -460,6 +461,8 @@ IdentificationTextGenerator::generateVolumeIdentificationText(IdentificationStri break; case DataFileTypeEnum::METRIC: break; + case DataFileTypeEnum::METRIC_DYNAMIC: + break; case DataFileTypeEnum::PALETTE: break; case DataFileTypeEnum::RGBA: @@ -614,6 +617,8 @@ IdentificationTextGenerator::generateSurfaceIdentificationText(IdentificationStr break; case DataFileTypeEnum::METRIC: break; + case DataFileTypeEnum::METRIC_DYNAMIC: + break; case DataFileTypeEnum::PALETTE: break; case DataFileTypeEnum::RGBA: @@ -654,7 +659,7 @@ IdentificationTextGenerator::generateSurfaceIdentificationText(IdentificationStr const int32_t numLabelFiles = brainStructure->getNumberOfLabelFiles(); for (int32_t i = 0; i < numLabelFiles; i++) { const LabelFile* lf = brainStructure->getLabelFile(i); - AString boldText = "LABEL " + lf->getFileNameNoPath() + ":"; + AString boldText = "LABEL " + lf->getFileNameNoPath(); AString text; const int numMaps = lf->getNumberOfMaps(); for (int32_t j = 0; j < numMaps; j++) { @@ -667,19 +672,34 @@ IdentificationTextGenerator::generateSurfaceIdentificationText(IdentificationStr idText.addLine(true, boldText, text); } + std::vector metricDynConFiles; const int32_t numMetricFiles = brainStructure->getNumberOfMetricFiles(); for (int32_t i = 0; i < numMetricFiles; i++) { const MetricFile* mf = brainStructure->getMetricFile(i); - AString boldText = "METRIC " + mf->getFileNameNoPath() + ":"; + AString boldText = "METRIC " + mf->getFileNameNoPath(); AString text; const int numMaps = mf->getNumberOfMaps(); for (int32_t j = 0; j < numMaps; j++) { text += (" " + AString::number(mf->getValue(nodeNumber, j))); } idText.addLine(true, boldText, text); + + const MetricDynamicConnectivityFile* mdcf = mf->getMetricDynamicConnectivityFile(); + if (mdcf != NULL) { + if (mdcf->isDataValid()) { + if (mdcf->isEnabledAsLayer()) { + AString boldText = "METRIC DYNAMIC " + mdcf->getFileNameNoPath(); + AString text; + const int numMaps = mdcf->getNumberOfMaps(); + for (int32_t j = 0; j < numMaps; j++) { + text += (" " + AString::number(mdcf->getValue(nodeNumber, j))); + } + idText.addLine(true, boldText, text); + } + } + } } } - } /** diff --git a/src/Brain/Overlay.cxx b/src/Brain/Overlay.cxx index c4b638028..fdf86cc0b 100644 --- a/src/Brain/Overlay.cxx +++ b/src/Brain/Overlay.cxx @@ -34,6 +34,7 @@ #include "EventManager.h" #include "EventOverlayValidate.h" #include "LabelFile.h" +#include "MetricDynamicConnectivityFile.h" #include "MetricFile.h" #include "PlainTextStringBuilder.h" #include "RgbaFile.h" @@ -434,6 +435,13 @@ Overlay::getSelectionData(std::vector& mapFilesOut, break; case DataFileTypeEnum::METRIC: break; + case DataFileTypeEnum::METRIC_DYNAMIC: + { + MetricDynamicConnectivityFile* metricDynFile = dynamic_cast(mapFile); + CaretAssert(metricDynFile); + useIt = metricDynFile->isEnabledAsLayer(); + } + break; case DataFileTypeEnum::PALETTE: break; case DataFileTypeEnum::RGBA: diff --git a/src/Brain/SurfaceNodeColoring.cxx b/src/Brain/SurfaceNodeColoring.cxx index d56b7c77d..41912950b 100644 --- a/src/Brain/SurfaceNodeColoring.cxx +++ b/src/Brain/SurfaceNodeColoring.cxx @@ -467,7 +467,8 @@ SurfaceNodeColoring::colorSurfaceNodes(const DisplayPropertiesLabels* displayPro overlayRGBV); break; case DataFileTypeEnum::METRIC: - isColoringValid = this->assignMetricColoring(brainStructure, + case DataFileTypeEnum::METRIC_DYNAMIC: // same as metric + isColoringValid = this->assignMetricColoring(brainStructure, dynamic_cast(selectedMapFile), selectedMapIndex, numNodes, diff --git a/src/Common/ConnectivityCorrelation.cxx b/src/Common/ConnectivityCorrelation.cxx index f5d8d7800..c049cde56 100644 --- a/src/Common/ConnectivityCorrelation.cxx +++ b/src/Common/ConnectivityCorrelation.cxx @@ -37,109 +37,162 @@ using namespace caret; * \class caret::ConnectivityCorrelation * \brief Computes connectivity correlations * \ingroup Common + * + * Correlation from https://en.wikipedia.org/wiki/Pearson_product-moment_correlation_coefficient */ /** - * Constructor for data in one 'chunk' of memory + * Create new instance for data that is in one contiguous "chunk" of memory * * @param data - * Points to first data element in first group - * @param groupCount - * Number of groups - * @param groupStride - * Element offset between two consecutive groups (from first element in first - * group to first element in second group, and so on) - * @param groupDataCount - * Number of data elements in a group - * @param groupDataStride - * Element offset between to consecutive data values within a group + * Points to first timepoint for first brainordinate + * @param numberOfBrainordinates + * Number of brainordinates + * @param nextBrainordinateStride + * Element offset between two consecutive brainordinates (eg: offset from first value for FIRST brainordinate + * to first value for SECOND brainordinate, and so on) + * @param numberOfTimePoints + * Number of timepoints for each brainordinate + * @param nextTimePointStride + * Offset between two consecutive timepoints for a brainordinate * Use a value of "1" for contiguous data - * - * Example: Cifti file with data in a matrix. Each row contains - * data (timepoints) for one group - * data = pointer to data - * groupCount = number of rows - * groupStride = number of columns - * groupDataCount = number of columns - * groupDataStride = 1 + * @param errorMessageOut + * Contains error information if NULL is returned + * @return + * Pointer to new instance or NULL if there is an error. * * Example: Nifti file with time-series data. Each 'brick' contains one timepoint - * for all groups (voxels). 4D matrix - * data = pointer to data - * groupCount = dim[0] * dim[1] * dim[2] (sizeof a 'brick') - * groupStride = 1 - * groupDataCount = number of timepoints - * groupDataStride = dim[0] * dim[1] * dim[3] (sizeof a 'brick') + * for all brainordinates. Brainordinates are "interleaved" + * data = pointer to data + * numberOfBrainordinates = dim[0] * dim[1] * dim[2] (sizeof a 'brick') + * nextBrainordinateStride = 1 + * numberOfTimePoints = number of timepoints + * nextTimePointStride = dim[0] * dim[1] * dim[3] (sizeof a 'brick') */ -ConnectivityCorrelation::ConnectivityCorrelation(const float* data, - const int64_t groupCount, - const int64_t groupStride, - const int64_t groupDataCount, - const int64_t groupDataStride) -: m_dataCount(groupDataCount) +ConnectivityCorrelation* +ConnectivityCorrelation::newInstance(const float* data, + const int64_t numberOfBrainordinates, + const int64_t nextBrainordinateStride, + const int64_t numberOfTimePoints, + const int64_t nextTimePointStride, + AString& errorMessageOut) { - CaretAssert(data); - CaretAssert(groupStride >= 1); - CaretAssert(groupDataStride >= 1); - - for (int64_t i = 0; i < groupCount; i++) { - const float* dataPointer = data + (i * groupStride); - addDataGroup(dataPointer, - groupDataCount, - groupDataStride); + errorMessageOut.clear(); + + std::vector brainordinateDataPointers; + for (int64_t i = 0; i < numberOfBrainordinates; i++) { + const int64_t offset = i * nextBrainordinateStride; + brainordinateDataPointers.push_back(data + + offset); } - CaretAssert(static_cast(m_groups.size()) == groupCount); - m_contiguousDataFlag = (groupDataStride == 1); + CaretAssert(numberOfBrainordinates == static_cast(brainordinateDataPointers.size())); + + /* + * While either of the other two factory methods can be called, the new instance + * for brainordinates is faster when each brainordinates timepoint's are in a + * contigous section of memory (nextTimePointStride == 1). + */ + ConnectivityCorrelation* instanceOut = newInstanceBrainordinates(brainordinateDataPointers, + numberOfTimePoints, + nextTimePointStride, + errorMessageOut); + + return instanceOut; } /** - * Constructor for data in separate 'chunk' for each data group + * Create a new instance where each chunk of memory contains all timepoints for one brainordinate. * - * @param dataGroups - * Each element is pointer to first element in each group - * @param groupCount - * Number of groups (number of elements in 'dataGroups') - * @param groupDataCount - * Number of data elements in a group - * @param groupDataStrides - * Each element is offset between consecutive data values within each group - * Must contain 'groupCount' elements. - * OR may also be NULL which indicates elements are contigous in all groups + * @param brainordinateDataPointers + * Each element points to all timepoints for one brainordinate + * @param numberOfTimePoints + * Number of timepoints for each brainordinate + * @param nextTimePointStride + * Offset between two consecutive timepoints for a brainordinate + * Use a value of "1" for contiguous data + * @param errorMessageOut + * Contains error information if NULL is returned + * @return + * Pointer to new instance or NULL if there is an error. + */ +ConnectivityCorrelation* +ConnectivityCorrelation::newInstanceBrainordinates(const std::vector& brainordinateDataPointers, + const int64_t numberOfTimePoints, + const int64_t nextTimePointStride, + AString& errorMessageOut) +{ + errorMessageOut.clear(); + + ConnectivityCorrelation* instanceOut = ((nextTimePointStride == 1) + ? new ConnectivityCorrelation(DataTypeEnum::BRAINORDINATES_CONTIGUOUS_DATA) + : new ConnectivityCorrelation(DataTypeEnum::BRAINORDINATES_NON_CONTIGUOUS_DATA)); + const bool validFlag = instanceOut->initializeWithBrainordinates(brainordinateDataPointers, + numberOfTimePoints, + nextTimePointStride, + errorMessageOut); + if ( ! validFlag) { + delete instanceOut; + instanceOut = NULL; + } + + return instanceOut; +} + +/** + * Create a new instance where each chunk of memory contains one timepoint for all brainordinates + * + * @param timePointDataPointers + * Each element points to all brainordinates for one timepoint + * @param numberOfBrainordinates + * Number of brainordinates + * @param nextBrainordinateStride + * Element offset between two consecutive brainordinates (eg: offset from first value for FIRST brainordinate + * to first value for SECOND brainordinate, and so on) + * @param errorMessageOut + * Contains error information if NULL is returned + * @return + * Pointer to new instance or NULL if there is an error. * - * Example: GIFTI file that stores each 'map' in a separate chunk of memory - * dataGroups = pointers to data in each GIFTI data array - * groupCount = number of rows - * groupStride = number of columns - * groupDataCount = number of columns - * groupDataStrides = NULL (data elements are consecutive) + * Example: GIFTI functional file (Metric) with time-series data. Each map contains one timepoint + * for all brainordinates. + * timePointDataPointers = pointer to each map's data + * numberOfBrainordinates = Number of vertices in the GIFTI file + * nextBrainordinateStride = 1 + * numberOfTimePoints = number of maps in file (each map is one time point) */ -ConnectivityCorrelation::ConnectivityCorrelation(const float* dataGroups[], - const int64_t groupCount, - const int64_t groupDataCount, - const int64_t groupDataStrides[]) -: m_dataCount(groupDataCount) +ConnectivityCorrelation* +ConnectivityCorrelation::newInstanceTimePoints(const std::vector& timePointDataPointers, + const int64_t numberOfBrainordinates, + const int64_t nextBrainordinateStride, + AString& errorMessageOut) { - CaretAssert(dataGroups); - m_contiguousDataFlag = true; - - const bool haveStride(groupDataStrides != NULL); - for (int64_t i = 0; i < groupCount; i++) { - const int64_t dataStride(haveStride - ? groupDataStrides[i] - : 1); - CaretAssert(dataStride >= 1); - CaretAssert(dataGroups[i]); - - addDataGroup(dataGroups[i], - groupDataCount, - dataStride); - - if (dataStride != 1) { - m_contiguousDataFlag = false; - } + errorMessageOut.clear(); + + ConnectivityCorrelation* instanceOut = new ConnectivityCorrelation(DataTypeEnum::TIMEPOINTS); + const bool validFlag = instanceOut->initializeWithTimePoints(timePointDataPointers, + numberOfBrainordinates, + nextBrainordinateStride, + errorMessageOut); + if ( ! validFlag) { + delete instanceOut; + instanceOut = NULL; } - CaretAssert(static_cast(m_groups.size()) == groupCount); + + return instanceOut; +} + +/** + * Constructor + * + * @param dataType + * Type of data (brainordinate or timepoint) + * + */ +ConnectivityCorrelation::ConnectivityCorrelation(const DataTypeEnum dataType) +: m_dataType(dataType) +{ + } /** @@ -150,120 +203,235 @@ ConnectivityCorrelation::~ConnectivityCorrelation() } /** - * Compute data's mean and sum-squared + * Initialize a new instance where a chunk of memory contains all timepoints for one brainordinate. * - * @param data - * Data on which mean and sum-squared are calculated - * @param dataCount - * Number of elements in data. - * @param dataStride - * Element offset between consecutive data values + * @param brainordinateDataPointers + * Each element points to all timepoints for one brainordinate + * @param numberOfTimePoints + * Number of timepoints for each brainordinate + * @param nextTimePointStride + * Offset between two consecutive timepoints for a brainordinate + * Use a value of "1" for contiguous data + * @param errorMessageOut + * Contains error information if NULL is returned + * @return + * Pointer to new instance or NULL if there is an error. */ -void -ConnectivityCorrelation::addDataGroup(const float* data, - const int64_t dataCount, - const int64_t dataStride) +bool +ConnectivityCorrelation::initializeWithBrainordinates(const std::vector& brainordinateDataPointers, + const int64_t numberOfTimePoints, + const int64_t nextTimePointStride, + AString& errorMessageOut) { - CaretAssert(data); - - float mean(0.0f); - float sqrtSumSquared(0.0f); - computeDataMeanAndSumSquared(data, - dataCount, - dataStride, - mean, - sqrtSumSquared); - - std::unique_ptr gd(new GroupData(data, - dataStride, - mean, - sqrtSumSquared)); - m_groups.push_back(std::move(gd)); + switch (m_dataType) { + case DataTypeEnum::BRAINORDINATES_CONTIGUOUS_DATA: + break; + case DataTypeEnum::BRAINORDINATES_NON_CONTIGUOUS_DATA: + break; + case DataTypeEnum::INVALID: + CaretAssert(0); + break; + case DataTypeEnum::TIMEPOINTS: + CaretAssert(0); + break; + } + + m_numberOfTimePoints = numberOfTimePoints; + + CaretAssert(brainordinateDataPointers.size() >= 2); + CaretAssert(m_numberOfTimePoints >= 2); + CaretAssert(nextTimePointStride >= 1); + + m_numberOfBrainordinates = static_cast(brainordinateDataPointers.size()); + if (m_numberOfBrainordinates < 2) { + errorMessageOut.appendWithNewLine("There must be at least two brainordinates"); + } + if (numberOfTimePoints < 2) { + errorMessageOut.appendWithNewLine("There must be at least two time points"); + } + if (nextTimePointStride < 1) { + errorMessageOut.appendWithNewLine("TimePoint stride must be at least one"); + } + if ( ! errorMessageOut.isEmpty()) { + return false; + } + + for (int64_t i = 0; i < m_numberOfBrainordinates; i++) { + CaretAssertVectorIndex(brainordinateDataPointers, i); + const float* dataPointer = brainordinateDataPointers[i]; + CaretAssert(dataPointer); + std::unique_ptr gd(new BrainordinateData(dataPointer, + nextTimePointStride)); + m_brainordinateData.push_back(std::move(gd)); + } + CaretAssert(static_cast(m_brainordinateData.size()) == m_numberOfBrainordinates); + + computeBrainordinateMeanAndSumSquared(); + + return true; } /** - * Get correlation from the given group to all other groups + * Initialize a new instance where a chunk of memory contains all timepoints for one brainordinate. * - * @param groupIndex - * Index of group to correlate to all other groups + * @param timePointDataPointers + * Each element points to all brainordinates for one timepoint + * @param numberOfBrainordinates + * Number of brainordinates + * @param nextBrainordinateStride + * Element offset between two consecutive brainordinates (eg: offset from first value for FIRST brainordinate + * to first value for SECOND brainordinate, and so on) + * @param errorMessageOut + * Contains error information if NULL is returned + * @return + * Pointer to new instance or NULL if there is an error. + */ +bool +ConnectivityCorrelation::initializeWithTimePoints(const std::vector& timePointDataPointers, + const int64_t numberOfBrainordinates, + const int64_t nextBrainordinateStride, + AString& errorMessageOut) +{ + CaretAssert(m_dataType == DataTypeEnum::TIMEPOINTS); + m_numberOfBrainordinates = numberOfBrainordinates; + m_numberOfTimePoints = static_cast(timePointDataPointers.size()); + CaretAssert(m_numberOfTimePoints >= 2); + CaretAssert(nextBrainordinateStride >= 1); + + if (m_numberOfBrainordinates < 2) { + errorMessageOut.appendWithNewLine("There must be at least two brainordinates"); + } + if (m_numberOfTimePoints < 2) { + errorMessageOut.appendWithNewLine("There must be at least two time points"); + } + if (nextBrainordinateStride < 1) { + errorMessageOut.appendWithNewLine("Brainordinate stride must be at least one"); + } + if ( ! errorMessageOut.isEmpty()) { + return false; + } + + for (int64_t i = 0; i < m_numberOfTimePoints; i++) { + CaretAssertVectorIndex(timePointDataPointers, i); + const float* dataPointer = timePointDataPointers[i]; + CaretAssert(dataPointer); + + std::unique_ptr td(new TimePointData(dataPointer, + nextBrainordinateStride)); + m_timePointData.push_back(std::move(td)); + } + + computeBrainordinateMeanAndSumSquared(); + + return true; +} + +/** + * Get correlation from the given brainordinate to all other brainordinates + * + * @param brainordinateIndex + * Index of brainordinate to correlate to all other brainordinates * @param dataOut * Output with correlation values. */ void -ConnectivityCorrelation::getCorrelationForGroup(const int64_t groupIndex, +ConnectivityCorrelation::getCorrelationForBrainordinate(const int64_t brainordinateIndex, std::vector& dataOut) { - CaretAssertVectorIndex(m_groups, groupIndex); - - if (m_dataCount > 0) { - const int64_t numGroups = static_cast(m_groups.size()); - dataOut.resize(numGroups); + CaretAssert(m_numberOfBrainordinates > 1); + CaretAssert(m_numberOfTimePoints > 1); + if (m_numberOfTimePoints > 0) { + if (m_numberOfBrainordinates > 0) { + dataOut.resize(m_numberOfBrainordinates); #pragma omp CARET_PARFOR schedule(dynamic) - for (int64_t iGroup = 0; iGroup < numGroups; iGroup++) { - CaretAssertVectorIndex(dataOut, iGroup); - if (m_contiguousDataFlag) { - dataOut[iGroup] = correlationContiguousData(groupIndex, - iGroup); - } - else { - dataOut[iGroup] = correlationNonContiguousData(groupIndex, - iGroup); + for (int64_t i = 0; i < m_numberOfBrainordinates; i++) { + CaretAssertVectorIndex(dataOut, i); + switch (m_dataType) { + case DataTypeEnum::BRAINORDINATES_CONTIGUOUS_DATA: + dataOut[i] = correlationBrainordinateContiguousData(brainordinateIndex, + i); + break; + case DataTypeEnum::BRAINORDINATES_NON_CONTIGUOUS_DATA: + dataOut[i] = correlationBrainordinateNonContiguousData(brainordinateIndex, + i); + break; + case DataTypeEnum::INVALID: + CaretAssert(0); + break; + case DataTypeEnum::TIMEPOINTS: + dataOut[i] = correlationTimePointData(brainordinateIndex, + i); + break; + } } } } } /** - * Get the correlation coefficient for the two given groups + * Get the correlation coefficient for the two given brainordinates * that contain CONTIGOUS data * - * @param fromGroupIndex - * Index of a group - * @param toGroupIndex - * Index of a second group. + * @param fromBrainordinateIndex + * Index of a brainordinate + * @param toBrainordinateIndex + * Index of a second brainordinate. */ float -ConnectivityCorrelation::correlationContiguousData(const int64_t fromGroupIndex, - const int64_t toGroupIndex) const +ConnectivityCorrelation::correlationBrainordinateContiguousData(const int64_t fromBrainordinateIndex, + const int64_t toBrainordinateIndex) const { - CaretAssertVectorIndex(m_groups, fromGroupIndex); - CaretAssertVectorIndex(m_groups, toGroupIndex); + CaretAssert(m_dataType == DataTypeEnum::BRAINORDINATES_CONTIGUOUS_DATA); - const GroupData* fromGroup = m_groups[fromGroupIndex].get(); - const GroupData* toGroup = m_groups[toGroupIndex].get(); + CaretAssertVectorIndex(m_brainordinateData, fromBrainordinateIndex); + CaretAssertVectorIndex(m_brainordinateData, toBrainordinateIndex); + const BrainordinateData* fromData = m_brainordinateData[fromBrainordinateIndex].get(); + const BrainordinateData* toGroup = m_brainordinateData[toBrainordinateIndex].get(); - double xySum = dsdot(fromGroup->m_data, + CaretAssertVectorIndex(m_meanSSData, fromBrainordinateIndex); + CaretAssertVectorIndex(m_meanSSData, toBrainordinateIndex); + const BrainordinateMeanSS* fromMeanSS = m_meanSSData[fromBrainordinateIndex].get(); + const BrainordinateMeanSS* toMeanSS = m_meanSSData[toBrainordinateIndex].get(); + + double xySum = dsdot(fromData->m_data, toGroup->m_data, - m_dataCount); + m_numberOfTimePoints); - const double ssxy = xySum - (m_dataCount * fromGroup->m_mean * toGroup->m_mean); + const double ssxy = xySum - (m_numberOfTimePoints * fromMeanSS->m_mean * toMeanSS->m_mean); float correlationCoefficient = 0.0; - if ((fromGroup->m_sqrt_ssxx > 0.0) - && (toGroup->m_sqrt_ssxx > 0.0)) { - correlationCoefficient = (ssxy / (fromGroup->m_sqrt_ssxx * toGroup->m_sqrt_ssxx)); + if ((fromMeanSS->m_sqrt_ssxx > 0.0) + && (toMeanSS->m_sqrt_ssxx > 0.0)) { + correlationCoefficient = (ssxy / (fromMeanSS->m_sqrt_ssxx * toMeanSS->m_sqrt_ssxx)); } return correlationCoefficient; } /** - * Get the correlation coefficient for the two given groups + * Get the correlation coefficient for the two given brainordinates * that contain NON-CONTIGOUS data * - * @param fromGroupIndex - * Index of a group - * @param toGroupIndex - * Index of a second group. + * @param fromBrainordinateIndex + * Index of a brainordinate + * @param toBrainordinateIndex + * Index of a second brainordinate. */ float -ConnectivityCorrelation::correlationNonContiguousData(const int64_t fromGroupIndex, - const int64_t toGroupIndex) const +ConnectivityCorrelation::correlationBrainordinateNonContiguousData(const int64_t fromBrainordinateIndex, + const int64_t toBrainordinateIndex) const { - CaretAssertVectorIndex(m_groups, fromGroupIndex); - CaretAssertVectorIndex(m_groups, toGroupIndex); + CaretAssert(m_dataType == DataTypeEnum::BRAINORDINATES_NON_CONTIGUOUS_DATA); + + CaretAssertVectorIndex(m_brainordinateData, fromBrainordinateIndex); + CaretAssertVectorIndex(m_brainordinateData, toBrainordinateIndex); + + const BrainordinateData* fromGroup = m_brainordinateData[fromBrainordinateIndex].get(); + const BrainordinateData* toGroup = m_brainordinateData[toBrainordinateIndex].get(); - const GroupData* fromGroup = m_groups[fromGroupIndex].get(); - const GroupData* toGroup = m_groups[toGroupIndex].get(); + CaretAssertVectorIndex(m_meanSSData, fromBrainordinateIndex); + CaretAssertVectorIndex(m_meanSSData, toBrainordinateIndex); + const BrainordinateMeanSS* fromMeanSS = m_meanSSData[fromBrainordinateIndex].get(); + const BrainordinateMeanSS* toMeanSS = m_meanSSData[toBrainordinateIndex].get(); const float* fromData = fromGroup->m_data; const int64_t fromStride = fromGroup->m_dataStride; @@ -273,66 +441,126 @@ ConnectivityCorrelation::correlationNonContiguousData(const int64_t fromGroupInd int64_t fromOffset(0); int64_t toOffset(0); double xySum(0.0); - for (int32_t i = 0; i < m_dataCount; i++) { + for (int32_t i = 0; i < m_numberOfTimePoints; i++) { xySum += (fromData[fromOffset] * toData[toOffset]); fromOffset += fromStride; toOffset += toStride; } - const double ssxy = xySum - (m_dataCount * fromGroup->m_mean * toGroup->m_mean); + const double ssxy = xySum - (m_numberOfTimePoints * fromMeanSS->m_mean * toMeanSS->m_mean); float correlationCoefficient = 0.0; - if ((fromGroup->m_sqrt_ssxx > 0.0) - && (toGroup->m_sqrt_ssxx > 0.0)) { - correlationCoefficient = (ssxy / (fromGroup->m_sqrt_ssxx * toGroup->m_sqrt_ssxx)); + if ((fromMeanSS->m_sqrt_ssxx > 0.0) + && (toMeanSS->m_sqrt_ssxx > 0.0)) { + correlationCoefficient = (ssxy / (fromMeanSS->m_sqrt_ssxx * toMeanSS->m_sqrt_ssxx)); } return correlationCoefficient; } /** - * Compute data's mean and sum-squared + * Get the correlation coefficient for the two given brainordinates + * that contain NON-CONTIGOUS data * - * @param data - * Data on which mean and sum-squared are calculated - * @param dataCount - * Number of elements in data. - * @param dataStride - * Element offset between consecutive data values - * @param meanOut - * Output with mean of data. - * @param sqrtSquaredOut - * Output with square root of (sum-squared). + * @param fromBrainordinateIndex + * Index of a brainordinate + * @param toBrainordinateIndex + * Index of a second brainordinate. */ -void -ConnectivityCorrelation::computeDataMeanAndSumSquared(const float* data, - const int64_t dataCount, - const int64_t dataStride, - float& meanOut, - float& sqrtSquaredOut) const +float +ConnectivityCorrelation::correlationTimePointData(const int64_t fromBrainordinateIndex, + const int64_t toBrainordinateIndex) const { - CaretAssert(data); - CaretAssert(dataStride >= 1); + CaretAssert(m_dataType == DataTypeEnum::TIMEPOINTS); + + CaretAssert((fromBrainordinateIndex >= 0) && (fromBrainordinateIndex < m_numberOfBrainordinates)); + CaretAssert((toBrainordinateIndex >= 0) && (toBrainordinateIndex < m_numberOfBrainordinates)); - meanOut = 0.0; - sqrtSquaredOut = 0.0; - if (dataCount <= 0) { - return; + CaretAssertVectorIndex(m_meanSSData, fromBrainordinateIndex); + CaretAssertVectorIndex(m_meanSSData, toBrainordinateIndex); + const BrainordinateMeanSS* fromMeanSS = m_meanSSData[fromBrainordinateIndex].get(); + const BrainordinateMeanSS* toMeanSS = m_meanSSData[toBrainordinateIndex].get(); + + double xySum(0.0); + for (int32_t i = 0; i < m_numberOfTimePoints; i++) { + CaretAssertVectorIndex(m_timePointData, i); + const TimePointData* tpd = m_timePointData[i].get(); + const int64_t fromOffset = (fromBrainordinateIndex * tpd->m_dataStride); + const int64_t toOffset = (toBrainordinateIndex * tpd->m_dataStride); + + xySum += (tpd->m_data[fromOffset] * tpd->m_data[toOffset]); } - double sum = 0.0; - double sumSquared = 0.0; + const double ssxy = xySum - (m_numberOfTimePoints * fromMeanSS->m_mean * toMeanSS->m_mean); + + float correlationCoefficient = 0.0; + if ((fromMeanSS->m_sqrt_ssxx > 0.0) + && (toMeanSS->m_sqrt_ssxx > 0.0)) { + correlationCoefficient = (ssxy / (fromMeanSS->m_sqrt_ssxx * toMeanSS->m_sqrt_ssxx)); + } + return correlationCoefficient; +} + +/** + * Compute the mean and sum-squared for all brainordinates + */ +void +ConnectivityCorrelation::computeBrainordinateMeanAndSumSquared() +{ + const float numTimePointsFloat(m_numberOfTimePoints); + + /* + * Set the size of the vector so that loop can run in parallel + */ + m_meanSSData.resize(m_numberOfBrainordinates); - int64_t offset = 0; - for (int64_t i = 0; i < dataCount; i++) { -// const float d = data[i]; - const float d = data[offset]; - sum += d; - sumSquared += (d * d); - offset += dataStride; +#pragma omp CARET_PARFOR schedule(dynamic) + for (int64_t i = 0; i < m_numberOfBrainordinates; i++) { + double sum(0.0); + double sumSquared(0.0); + + switch (m_dataType) { + case DataTypeEnum::BRAINORDINATES_CONTIGUOUS_DATA: + case DataTypeEnum::BRAINORDINATES_NON_CONTIGUOUS_DATA: + { + CaretAssertVectorIndex(m_brainordinateData, i); + const float* data = m_brainordinateData[i]->m_data; + CaretAssert(data); + const int64_t stride = m_brainordinateData[i]->m_dataStride; + for (int64_t j = 0; j < m_numberOfTimePoints; j++) { + const int64_t offset = (j * stride); + const float d = data[offset]; + sum += d; + sumSquared += (d * d); + } + } + break; + case DataTypeEnum::INVALID: + CaretAssert(0); + break; + case DataTypeEnum::TIMEPOINTS: + { + for (int64_t j = 0; j < m_numberOfTimePoints; j++) { + CaretAssertVectorIndex(m_timePointData, j); + const int64_t offset = (i * m_timePointData[j]->m_dataStride); + const float d = m_timePointData[j]->m_data[offset]; + sum += d; + sumSquared += (d * d); + } + } + break; + } + + + const float mean = (sum / numTimePointsFloat); + const float ssxxSquared = (sumSquared - (numTimePointsFloat * mean * mean)); + const float ssxx = std::sqrt(ssxxSquared); + + BrainordinateMeanSS* bmss = new BrainordinateMeanSS(mean, + ssxx); + CaretAssertVectorIndex(m_meanSSData, i); + m_meanSSData[i].reset(bmss); } - meanOut = (sum / dataCount); - const float ssxx = (sumSquared - (dataCount * meanOut * meanOut)); - sqrtSquaredOut = std::sqrt(ssxx); + CaretAssert(static_cast(m_meanSSData.size()) == m_numberOfBrainordinates); } diff --git a/src/Common/ConnectivityCorrelation.h b/src/Common/ConnectivityCorrelation.h index 1f3f17ccc..4c9266308 100644 --- a/src/Common/ConnectivityCorrelation.h +++ b/src/Common/ConnectivityCorrelation.h @@ -34,16 +34,22 @@ namespace caret { class ConnectivityCorrelation : public CaretObject { public: - ConnectivityCorrelation(const float* data, - const int64_t groupCount, - const int64_t groupStride, - const int64_t groupDataCount, - const int64_t groupDataStride); - - ConnectivityCorrelation(const float* dataGroups[], - const int64_t groupCount, - const int64_t groupDataCount, - const int64_t groupDataStrides[]); + static ConnectivityCorrelation* newInstance(const float* data, + const int64_t numberOfBrainordinates, + const int64_t nextBrainordinateStride, + const int64_t numberOfTimePoints, + const int64_t nextTimePointStride, + AString& errorMessageOut); + + static ConnectivityCorrelation* newInstanceBrainordinates(const std::vector& brainordinateDataPointers, + const int64_t numberOfTimePoints, + const int64_t nextTimePointStride, + AString& errorMessageOut); + + static ConnectivityCorrelation* newInstanceTimePoints(const std::vector& timePointDataPointers, + const int64_t numberOfBrainordinates, + const int64_t nextBrainordinateStride, + AString& errorMessageOut); /** * Destructor @@ -54,15 +60,24 @@ namespace caret { ConnectivityCorrelation& operator=(const ConnectivityCorrelation&) = delete; - void getCorrelationForGroup(const int64_t groupIndex, - std::vector& dataOut); - + void getCorrelationForBrainordinate(const int64_t brainordinateIndex, + std::vector& dataOut); + // ADD_NEW_METHODS_HERE private: - class GroupData { + enum class DataTypeEnum { + INVALID, + BRAINORDINATES_NON_CONTIGUOUS_DATA, + BRAINORDINATES_CONTIGUOUS_DATA, + TIMEPOINTS + }; + + ConnectivityCorrelation(const DataTypeEnum dataType); + + class BrainordinateData { public: /** * Constructor. @@ -76,45 +91,100 @@ namespace caret { * @param sqrtSumSquared * Square root of sum-squared of data */ - GroupData(const float* data, - const int64_t dataStride, - const float mean, - const float sqrtSumSquared) + BrainordinateData(const float* data, + const int64_t dataStride) : m_data(data), - m_dataStride(dataStride), - m_mean(mean), - m_sqrt_ssxx(sqrtSumSquared) { } + m_dataStride(dataStride) { } const float* m_data; const int64_t m_dataStride; + }; + + class BrainordinateMeanSS { + public: + /* + * Constructor + * + * @param mean + * Mean value of 'data' + * @param sqrtSumSquared + * Square root of sum-squared of data + */ + BrainordinateMeanSS(const float mean, + const float sqrtSumSquared) + : m_mean(mean), + m_sqrt_ssxx(sqrtSumSquared) { } const float m_mean; const float m_sqrt_ssxx; }; - void addDataGroup(const float* data, - const int64_t dataCount, - const int64_t dataStride); + class TimePointData { + public: + /** + * Constructor. + * + * @param data + * Pointer to data + * @param dataStride + * Offset between consecutive elements (1 for contiguous data) + */ + TimePointData(const float* data, + const int64_t dataStride) + : m_data(data), + m_dataStride(dataStride) + { + } + + const float* m_data; + + const int64_t m_dataStride; + }; + + bool initializeWithBrainordinates(const std::vector& brainordinateDataPointers, + const int64_t numberOfTimePoints, + const int64_t nextTimePointStride, + AString& errorMessageOut); + + bool initializeWithTimePoints(const std::vector& timePointDataPointers, + const int64_t numberOfBrainordinates, + const int64_t nextBrainordinateStride, + AString& errorMessageOut); + + void computeBrainordinateMeanAndSumSquared(); + +// void computeDataMeanAndSumSquared(const float* data, +// const int64_t dataCount, +// const int64_t dataStride, +// float& meanOut, +// float& ssxxOut) const; +// +// void computeDataMeanAndSumSquared(const int64_t brainordinateIndex, +// float& meanOut, +// float& sqrtSquaredOut) const; + + float correlationBrainordinateContiguousData(const int64_t fromBrainordinateIndex, + const int64_t toBrainordinateIndex) const; + + float correlationBrainordinateNonContiguousData(const int64_t fromBrainordinateIndex, + const int64_t toBrainordinateIndex) const; + + float correlationTimePointData(const int64_t fromBrainordinateIndex, + const int64_t toBrainordinateIndex) const; - void computeDataMeanAndSumSquared(const float* data, - const int64_t dataCount, - const int64_t dataStride, - float& meanOut, - float& sumSquaredOut) const; + DataTypeEnum m_dataType = DataTypeEnum::INVALID; - float correlationContiguousData(const int64_t fromGroupIndex, - const int64_t toGroupIndex) const; + int64_t m_numberOfBrainordinates = -1; - float correlationNonContiguousData(const int64_t fromGroupIndex, - const int64_t toGroupIndex) const; + int64_t m_numberOfTimePoints = -1; - const float m_dataCount; + std::vector> m_brainordinateData; - std::vector> m_groups; + std::vector> m_timePointData; - bool m_contiguousDataFlag = false; + std::vector> m_meanSSData; // ADD_NEW_MEMBERS_HERE diff --git a/src/Common/DataFileTypeEnum.cxx b/src/Common/DataFileTypeEnum.cxx index 635bd14ef..5efd2a2e9 100644 --- a/src/Common/DataFileTypeEnum.cxx +++ b/src/Common/DataFileTypeEnum.cxx @@ -272,7 +272,14 @@ DataFileTypeEnum::initialize() "func.gii", "shape.gii")); - enumData.push_back(DataFileTypeEnum(PALETTE, + enumData.push_back(DataFileTypeEnum(METRIC_DYNAMIC, + "METRIC_DYNAMIC", + "Metric - Dynamic", + "METRIC_DYNAMIC", + true, + "func_dynconn")); // this file is never written + + enumData.push_back(DataFileTypeEnum(PALETTE, "PALETTE", "Palette", "PALETTE", @@ -324,6 +331,7 @@ DataFileTypeEnum::initialize() false, "nii", "nii.gz")); + enumData.push_back(DataFileTypeEnum(VOLUME_DYNAMIC, "VOLUME_DYNAMIC", "Volume - Dynamic", @@ -677,6 +685,9 @@ DataFileTypeEnum::getFilesExtensionsForEveryFile(const bool includeNonWritableFi break; case DataFileTypeEnum::METRIC: break; + case DataFileTypeEnum::METRIC_DYNAMIC: + validFlag = includeNonWritableFileTypesFlag; + break; case DataFileTypeEnum::PALETTE: break; case DataFileTypeEnum::RGBA: diff --git a/src/Common/DataFileTypeEnum.h b/src/Common/DataFileTypeEnum.h index 71aae2320..93c7cb8dc 100644 --- a/src/Common/DataFileTypeEnum.h +++ b/src/Common/DataFileTypeEnum.h @@ -77,6 +77,8 @@ public: LABEL, /** Metric */ METRIC, + /** Metric Dynamic Connectivity */ + METRIC_DYNAMIC, /** Palette */ PALETTE, /** RGBA */ @@ -91,7 +93,7 @@ public: UNKNOWN, /** Volume */ VOLUME, - /** Volume */ + /** Volume Dynamic Connectivity*/ VOLUME_DYNAMIC }; diff --git a/src/Files/CMakeLists.txt b/src/Files/CMakeLists.txt index 249943fea..0520e6a59 100755 --- a/src/Files/CMakeLists.txt +++ b/src/Files/CMakeLists.txt @@ -109,6 +109,7 @@ LabelDrawingProperties.h LabelDrawingTypeEnum.h LabelFile.h MapYokingGroupEnum.h +MetricDynamicConnectivityFile.h MetricFile.h MetricSmoothingObject.h NodeAndVoxelColoring.h @@ -243,6 +244,7 @@ LabelDrawingProperties.cxx LabelDrawingTypeEnum.cxx LabelFile.cxx MapYokingGroupEnum.cxx +MetricDynamicConnectivityFile.cxx MetricFile.cxx MetricSmoothingObject.cxx NodeAndVoxelColoring.cxx diff --git a/src/Files/CaretDataFileHelper.cxx b/src/Files/CaretDataFileHelper.cxx index 775b5808c..452551072 100644 --- a/src/Files/CaretDataFileHelper.cxx +++ b/src/Files/CaretDataFileHelper.cxx @@ -368,6 +368,9 @@ CaretDataFileHelper::createCaretDataFileForFileType(const DataFileTypeEnum::Enum case DataFileTypeEnum::METRIC: caretDataFile = new MetricFile(); break; + case DataFileTypeEnum::METRIC_DYNAMIC: + CaretAssertMessage(0, "Never create a metric dynamic file"); + break; case DataFileTypeEnum::PALETTE: caretDataFile = new PaletteFile(); break; diff --git a/src/Files/CaretMappableDataFileAndMapSelectionModel.cxx b/src/Files/CaretMappableDataFileAndMapSelectionModel.cxx index f71f7827e..d7ae9f173 100644 --- a/src/Files/CaretMappableDataFileAndMapSelectionModel.cxx +++ b/src/Files/CaretMappableDataFileAndMapSelectionModel.cxx @@ -166,6 +166,9 @@ CaretMappableDataFileAndMapSelectionModel::validateDataFileTypes() case DataFileTypeEnum::METRIC: isMappableFile = true; break; + case DataFileTypeEnum::METRIC_DYNAMIC: + isMappableFile = true;; + break; case DataFileTypeEnum::PALETTE: break; case DataFileTypeEnum::RGBA: diff --git a/src/Files/ChartableTwoFileDelegate.cxx b/src/Files/ChartableTwoFileDelegate.cxx index 68b0104de..03faab330 100644 --- a/src/Files/ChartableTwoFileDelegate.cxx +++ b/src/Files/ChartableTwoFileDelegate.cxx @@ -156,6 +156,9 @@ ChartableTwoFileDelegate::updateAfterFileChanged() histogramType = ChartTwoHistogramContentTypeEnum::HISTOGRAM_CONTENT_TYPE_MAP_DATA; lineSeriesType = ChartTwoLineSeriesContentTypeEnum::LINE_SERIES_CONTENT_BRAINORDINATE_DATA; break; + case DataFileTypeEnum::METRIC_DYNAMIC: + histogramType = ChartTwoHistogramContentTypeEnum::HISTOGRAM_CONTENT_TYPE_MAP_DATA; + break; case DataFileTypeEnum::PALETTE: break; case DataFileTypeEnum::RGBA: diff --git a/src/Files/ChartableTwoFileMatrixChart.cxx b/src/Files/ChartableTwoFileMatrixChart.cxx index 66e4a3984..af1b89252 100644 --- a/src/Files/ChartableTwoFileMatrixChart.cxx +++ b/src/Files/ChartableTwoFileMatrixChart.cxx @@ -134,6 +134,8 @@ m_validRowColumnSelectionDimensions(validRowColumnSelectionDimensions) break; case DataFileTypeEnum::METRIC: break; + case DataFileTypeEnum::METRIC_DYNAMIC: + break; case DataFileTypeEnum::PALETTE: break; case DataFileTypeEnum::RGBA: diff --git a/src/Files/CiftiMappableDataFile.cxx b/src/Files/CiftiMappableDataFile.cxx index 7745bc5e0..248efdfa7 100644 --- a/src/Files/CiftiMappableDataFile.cxx +++ b/src/Files/CiftiMappableDataFile.cxx @@ -231,6 +231,7 @@ CiftiMappableDataFile::CiftiMappableDataFile(const DataFileTypeEnum::Enum dataFi case DataFileTypeEnum::IMAGE: case DataFileTypeEnum::LABEL: case DataFileTypeEnum::METRIC: + case DataFileTypeEnum::METRIC_DYNAMIC: case DataFileTypeEnum::PALETTE: case DataFileTypeEnum::RGBA: case DataFileTypeEnum::SCENE: @@ -793,6 +794,7 @@ CiftiMappableDataFile::validateMappingTypes(const AString& filename) case DataFileTypeEnum::IMAGE: case DataFileTypeEnum::LABEL: case DataFileTypeEnum::METRIC: + case DataFileTypeEnum::METRIC_DYNAMIC: case DataFileTypeEnum::PALETTE: case DataFileTypeEnum::RGBA: case DataFileTypeEnum::SCENE: @@ -1924,6 +1926,8 @@ CiftiMappableDataFile::getMatrixForChartingRGBA(int32_t& numberOfRowsOut, break; case DataFileTypeEnum::METRIC: break; + case DataFileTypeEnum::METRIC_DYNAMIC: + break; case DataFileTypeEnum::PALETTE: break; case DataFileTypeEnum::RGBA: @@ -4791,6 +4795,9 @@ CiftiMappableDataFile::getSurfaceNodeIdentificationForMaps(const std::vectorgetDataFileType() == DataFileTypeEnum::METRIC) { - return true; + bool paletteFlag(false); + + switch (getDataFileType()) { + case DataFileTypeEnum::ANNOTATION: + break; + case DataFileTypeEnum::ANNOTATION_TEXT_SUBSTITUTION: + break; + case DataFileTypeEnum::BORDER: + break; + case DataFileTypeEnum::CONNECTIVITY_DENSE: + break; + case DataFileTypeEnum::CONNECTIVITY_DENSE_DYNAMIC: + break; + case DataFileTypeEnum::CONNECTIVITY_DENSE_LABEL: + break; + case DataFileTypeEnum::CONNECTIVITY_DENSE_PARCEL: + break; + case DataFileTypeEnum::CONNECTIVITY_PARCEL: + break; + case DataFileTypeEnum::CONNECTIVITY_PARCEL_DENSE: + break; + case DataFileTypeEnum::CONNECTIVITY_PARCEL_LABEL: + break; + case DataFileTypeEnum::CONNECTIVITY_PARCEL_SCALAR: + break; + case DataFileTypeEnum::CONNECTIVITY_PARCEL_SERIES: + break; + case DataFileTypeEnum::CONNECTIVITY_DENSE_SCALAR: + break; + case DataFileTypeEnum::CONNECTIVITY_DENSE_TIME_SERIES: + break; + case DataFileTypeEnum::CONNECTIVITY_FIBER_ORIENTATIONS_TEMPORARY: + break; + case DataFileTypeEnum::CONNECTIVITY_FIBER_TRAJECTORY_TEMPORARY: + break; + case DataFileTypeEnum::CONNECTIVITY_SCALAR_DATA_SERIES: + break; + case DataFileTypeEnum::FOCI: + break; + case DataFileTypeEnum::IMAGE: + break; + case DataFileTypeEnum::LABEL: + break; + case DataFileTypeEnum::METRIC: + paletteFlag = true; + break; + case DataFileTypeEnum::METRIC_DYNAMIC: + paletteFlag = true; + break; + case DataFileTypeEnum::PALETTE: + break; + case DataFileTypeEnum::RGBA: + break; + case DataFileTypeEnum::SCENE: + break; + case DataFileTypeEnum::SPECIFICATION: + break; + case DataFileTypeEnum::SURFACE: + break; + case DataFileTypeEnum::UNKNOWN: + break; + case DataFileTypeEnum::VOLUME: + break; + case DataFileTypeEnum::VOLUME_DYNAMIC: + break; } - return false; + + return paletteFlag; } /** @@ -968,9 +1032,13 @@ GiftiTypeFile::getMapIndexFromUniqueID(const AString& uniqueID) const * Palette file containing palettes. */ void -GiftiTypeFile::updateScalarColoringForMap(const int32_t /*mapIndex*/) +GiftiTypeFile::updateScalarColoringForMap(const int32_t mapIndex) { invalidateHistogramChartColoring(); + if ((mapIndex >= 0) + && (mapIndex < getNumberOfMaps())) { + this->giftiFile->getDataArray(mapIndex)->invalidateHistograms(); + } } /** @@ -1138,6 +1206,9 @@ GiftiTypeFile::getBrainordinateMappingMatch(const CaretMappableDataFile* mapFile case DataFileTypeEnum::METRIC: giftiFlag = true; break; + case DataFileTypeEnum::METRIC_DYNAMIC: + giftiFlag = true; + break; case DataFileTypeEnum::PALETTE: break; case DataFileTypeEnum::RGBA: @@ -1257,6 +1328,7 @@ GiftiTypeFile::getSurfaceNodeIdentificationForMaps(const std::vector& m } break; case DataFileTypeEnum::METRIC: + case DataFileTypeEnum::METRIC_DYNAMIC: // subclass of METRIC { const MetricFile* mf = dynamic_cast(this); CaretAssert(mf); diff --git a/src/Files/MetricDynamicConnectivityFile.cxx b/src/Files/MetricDynamicConnectivityFile.cxx new file mode 100644 index 000000000..73dad9eea --- /dev/null +++ b/src/Files/MetricDynamicConnectivityFile.cxx @@ -0,0 +1,671 @@ + +/*LICENSE_START*/ +/* + * Copyright (C) 2019 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + +#define __METRIC_DYNAMIC_CONNECTIVITY_FILE_DECLARE__ +#include "MetricDynamicConnectivityFile.h" +#undef __METRIC_DYNAMIC_CONNECTIVITY_FILE_DECLARE__ + +#include "CaretAssert.h" +#include "CaretLogger.h" +#include "ConnectivityCorrelation.h" +#include "ConnectivityDataLoaded.h" +#include "DataFileException.h" +#include "FileInformation.h" +#include "SceneClassAssistant.h" +using namespace caret; + + + +/** + * \class caret::MetricDynamicConnectivityFile + * \brief Dynamic connectivity from metric file + * \ingroup Files + */ + +/** + * Constructor. + * @param parentMetricFile + * The parent metric file. + */ +MetricDynamicConnectivityFile::MetricDynamicConnectivityFile(MetricFile* parentMetricFile) +: MetricFile(DataFileTypeEnum::METRIC_DYNAMIC), +m_parentMetricFile(parentMetricFile) +{ + CaretAssert(m_parentMetricFile); + + m_connectivityDataLoaded.reset(new ConnectivityDataLoaded()); + + m_sceneAssistant = std::unique_ptr(new SceneClassAssistant()); + m_sceneAssistant->add("m_dataLoadingEnabledFlag", + &m_dataLoadingEnabledFlag); + m_sceneAssistant->add("m_enabledAsLayer", + &m_enabledAsLayer); + m_sceneAssistant->add("m_connectivityDataLoaded", + "ConnectivityDataLoaded", + m_connectivityDataLoaded.get()); +} + +/** + * Destructor. + */ +MetricDynamicConnectivityFile::~MetricDynamicConnectivityFile() +{ +} + +/** + * Clear the file. + */ +void +MetricDynamicConnectivityFile::clear() +{ + MetricFile::clear(); + clearPrivateData(); +} + +/** + * Clear the file. + */ +void +MetricDynamicConnectivityFile::clearPrivateData() +{ + m_numberOfVertices = 0; + m_validDataFlag = false; + m_enabledAsLayer = false; + m_connectivityCorrelation.reset(); + m_connectivityDataLoaded->reset(); +} + +/** + * @return Pointer to the information about last loaded connectivity data. + */ +const ConnectivityDataLoaded* +MetricDynamicConnectivityFile::getConnectivityDataLoaded() const +{ + return m_connectivityDataLoaded.get(); +} + +/** + * @return True if enabled as a layer. + */ +bool +MetricDynamicConnectivityFile::isEnabledAsLayer() const +{ + return m_enabledAsLayer; +} + +/** + * Set enabled as a layer. + * + * @param True if enabled as a layer. + */ +void +MetricDynamicConnectivityFile::setEnabledAsLayer(const bool enabled) +{ + m_enabledAsLayer = enabled; +} + +/** + * @return True if data loading enabled. + */ +bool +MetricDynamicConnectivityFile::isDataLoadingEnabled() const +{ + return m_dataLoadingEnabledFlag; +} + +/** + * Set data loading enabled. + * + * @param True if data loading enabled. + */ +void +MetricDynamicConnectivityFile::setDataLoadingEnabled(const bool enabled) +{ + m_dataLoadingEnabledFlag = enabled; +} + +/** + * Initialize the file using information from parent volume file + */ +void +MetricDynamicConnectivityFile::initializeFile() +{ + clearPrivateData(); + + CaretAssert(m_parentMetricFile); + m_numberOfVertices = m_parentMetricFile->getNumberOfNodes(); + const int32_t numberOfMaps = 1; + setNumberOfNodesAndColumns(m_numberOfVertices, + numberOfMaps); + setStructure(m_parentMetricFile->getStructure()); + + CaretAssert(getNumberOfNodes() == m_numberOfVertices); + CaretAssert(getNumberOfMaps() == numberOfMaps); + + AString path, nameNoExt, ext; + FileInformation fileInfo(m_parentMetricFile->getFileName()); + fileInfo.getFileComponents(path, nameNoExt, ext); + setFileName(FileInformation::assembleFileComponents(path, + nameNoExt, + DataFileTypeEnum::toFileExtension(DataFileTypeEnum::METRIC_DYNAMIC))); + clearVertexValues(); + clearModified(); + + m_validDataFlag = true; +} + +/** + * @return True if this file type supports writing, else false. + * + * Dense files do NOT support writing. + */ +bool +MetricDynamicConnectivityFile::supportsWriting() const +{ + return false; +} + +/** + * @return The parent volume file + */ +MetricFile* +MetricDynamicConnectivityFile::getParentMetricFile() +{ + return const_cast(m_parentMetricFile); +} + +/** + * @return The parent metric file (const method) + */ +const MetricFile* +MetricDynamicConnectivityFile::getParentMetricFile() const +{ + return m_parentMetricFile; +} + +/** + * @return True if the data is valid + */ +bool +MetricDynamicConnectivityFile::isDataValid() const +{ + return m_validDataFlag; +} + +/** + * Add information about the file to the data file information. + * + * @param dataFileInformation + * Consolidates information about a data file. + */ +void +MetricDynamicConnectivityFile::addToDataFileContentInformation(DataFileContentInformation& dataFileInformation) +{ + MetricFile::addToDataFileContentInformation(dataFileInformation); +} + +/** + * Read the file with the given name. + * + * @param filename + * Name of file + * @throws DataFileException + * If error occurs + */ +void +MetricDynamicConnectivityFile::readFile(const AString& /*filename*/) +{ + throw DataFileException("Read of Metric Dynamic Connectivity File is not allowed"); +} + +/** + * Read the file with the given name. + * + * @param filename + * Name of file + * @throws DataFileException + * If error occurs + */ +void +MetricDynamicConnectivityFile::writeFile(const AString& /*filename*/) +{ + throw DataFileException("Writing of Metric Dynamic Connectivity File is not allowed"); +} + +/** + * Clear voxels in this volume + */ +void +MetricDynamicConnectivityFile::clearVertexValues() +{ + CaretAssert(getNumberOfMaps() == 1); + const int32_t mapIndex(0); + const float value(0.0f); + initializeColumn(mapIndex, + value); + m_dataLoadedName = ""; +} + +/** + * Load connectivity data for the surface's node. + * + * @param surfaceNumberOfNodes + * Number of nodes in surface. + * @param structure + * Surface's structure. + * @param nodeIndex + * Index of node number. + * @return + * True if data was loaded, else false. + */ +bool +MetricDynamicConnectivityFile::loadDataForSurfaceNode(const int32_t surfaceNumberOfNodes, + const StructureEnum::Enum structure, + const int32_t nodeIndex) +{ + bool validFlag(false); + + if ( ! isDataValid()) { + return validFlag; + } + if ( ! m_dataLoadingEnabledFlag) { + return validFlag; + } + if (getStructure() != structure) { + return validFlag; + } + if (getNumberOfNodes() != surfaceNumberOfNodes) { + return validFlag; + } + + clearVertexValues(); + m_connectivityDataLoaded->reset(); + + std::vector data; + if (getConnectivityForVertexIndex(nodeIndex, data)) { + CaretAssert(m_numberOfVertices == static_cast(data.size())); + float* dataPointer = const_cast(getValuePointerForColumn(0)); + CaretAssert(dataPointer); + std::copy(data.begin(), + data.end(), + dataPointer); + + validFlag = true; + + m_connectivityDataLoaded->setSurfaceNodeLoading(getStructure(), + getNumberOfNodes(), + nodeIndex, + -1, - + 1); + } + + const int32_t mapIndex(0); + const AString mapName("Vertex_Index_" + + AString::number(nodeIndex) + + "_Structure_" + + StructureEnum::toGuiName(structure)); + setMapName(mapIndex, + mapName); + m_dataLoadedName = mapName; + + updateAfterFileDataChanges(); + updateScalarColoringForMap(0); + + return validFlag; +} + + + +/** + * Load connectivity data for the surface's nodes and then average the data. + * + * @param surfaceNumberOfNodes + * Number of nodes in surface. + * @param structure + * Surface's structure. + * @param nodeIndices + * Indices of nodes. + * @return + * True if data was loaded, else false. + */ +bool +MetricDynamicConnectivityFile::loadAverageDataForSurfaceNodes(const int32_t surfaceNumberOfNodes, + const StructureEnum::Enum structure, + const std::vector& nodeIndices) +{ + bool validFlag(false); + + if ( ! isDataValid()) { + return validFlag; + } + if ( ! m_dataLoadingEnabledFlag) { + return validFlag; + } + if (getStructure() != structure) { + return validFlag; + } + if (getNumberOfNodes() != surfaceNumberOfNodes) { + return validFlag; + } + + clearVertexValues(); + m_connectivityDataLoaded->reset(); + + std::vector dataSum(m_numberOfVertices, 0.0); + const int64_t vertexCount = static_cast(nodeIndices.size()); + for (auto nodeIndex : nodeIndices) { + std::vector data; + if (getConnectivityForVertexIndex(nodeIndex, data)) { + CaretAssert(m_numberOfVertices == static_cast(data.size())); + for (int32_t i = 0; i < m_numberOfVertices; i++) { + dataSum[i] += data[i]; + } + } + } + + if (vertexCount >= 1.0) { + float* dataPointer = const_cast(getValuePointerForColumn(0)); + CaretAssert(dataPointer); + for (int32_t i = 0; i < m_numberOfVertices; i++) { + dataPointer[i] = static_cast(dataSum[i] / vertexCount); + } + + validFlag = true; + + m_connectivityDataLoaded->setSurfaceAverageNodeLoading(getStructure(), + getNumberOfNodes(), + nodeIndices); + } + const AString mapName("Average_Vertex_Count_" + + AString::number(vertexCount, 'f', 0)); + m_dataLoadedName = mapName; + const int32_t mapIndex(0); + setMapName(mapIndex, + mapName); + + updateAfterFileDataChanges(); + updateScalarColoringForMap(0); + + return validFlag; +} + +/** + * Get the connectivity for the given vertex index. + * If the vertex index is invalid, zeros are loaded into all voxels. + * + * @param vertexIndex + * The vertex index. + * @param vertexDataOut + * Output containing vertex data + * @return + * True if data was loaded. + */ +bool +MetricDynamicConnectivityFile::getConnectivityForVertexIndex(const int32_t vertexIndex, + std::vector& vertexDataOut) +{ + bool validFlag(false); + if ( ! m_connectivityCorrelationFailedFlag) { + if ((vertexIndex >= 0) + && (vertexIndex < getNumberOfNodes())) { + if (m_connectivityCorrelation == NULL) { + /* + * Need data and timepoint count from parent volume + * IJK dimensions are same in this and parent volume + */ + CaretAssert(m_parentMetricFile); + const int32_t numberOfTimePoints = m_parentMetricFile->getNumberOfMaps(); + CaretAssert(numberOfTimePoints >= 2); + std::vector timePointData; + for (int32_t i = 0; i < numberOfTimePoints; i++) { + const float* dataPtr = m_parentMetricFile->getValuePointerForColumn(i); + CaretAssert(dataPtr); + timePointData.push_back(dataPtr); + } + const int64_t brainordinateStride(1); + AString errorMessage; + ConnectivityCorrelation* cc = ConnectivityCorrelation::newInstanceTimePoints(timePointData, + m_numberOfVertices, + brainordinateStride, + errorMessage); + if (cc != NULL) { + m_connectivityCorrelation.reset(cc); + } + else { + m_connectivityCorrelationFailedFlag = true; + CaretLogSevere("Failed to create connectvity correlation for " + + m_parentMetricFile->getFileNameNoPath()); + } + } + + if (m_connectivityCorrelation) { + m_connectivityCorrelation->getCorrelationForBrainordinate(vertexIndex, + vertexDataOut); + CaretAssert(m_numberOfVertices == static_cast(vertexDataOut.size())); + validFlag = true; + } + } + } + + if ( ! validFlag) { + vertexDataOut.resize(m_numberOfVertices); + std::fill(vertexDataOut.begin(), vertexDataOut.end(), + 0.0f); + } + + updateAfterFileDataChanges(); + invalidateHistogramChartColoring(); + + return validFlag; +} + + +/** + * @return A metric file using the loaded data (will return NULL if there is an error). + * + * @param directoryName + * Directory for file + * @param errorMessageOut + * Contains error information + */ +MetricFile* +MetricDynamicConnectivityFile::newMetricFileFromLoadedData(const AString& directoryName, + AString& errorMessageOut) +{ + errorMessageOut.clear(); + + bool validDataFlag(false); + switch (m_connectivityDataLoaded->getMode()) { + case ConnectivityDataLoaded::MODE_COLUMN: + break; + case ConnectivityDataLoaded::MODE_NONE: + break; + case ConnectivityDataLoaded::MODE_ROW: + break; + case ConnectivityDataLoaded::MODE_SURFACE_NODE: + validDataFlag = true; + break; + case ConnectivityDataLoaded::MODE_SURFACE_NODE_AVERAGE: + validDataFlag = true; + break; + case ConnectivityDataLoaded::MODE_VOXEL_IJK_AVERAGE: + break; + case ConnectivityDataLoaded::MODE_VOXEL_XYZ: + break; + } + + if ( ! validDataFlag) { + errorMessageOut = "No metric connectivity data is loaded"; + return NULL; + } + + MetricFile* mf(NULL); + + try { + const int32_t numVertices = getNumberOfNodes(); + mf = new MetricFile(); + mf->setStructure(getStructure()); + mf->setNumberOfNodesAndColumns(numVertices, + 1); + mf->setValuesForColumn(0, this->getValuePointerForColumn(0)); + + /* + * May need to convert a remote path to a local path + */ + FileInformation fileNameInfo(getFileName()); + const AString metricFileName = fileNameInfo.getAsLocalAbsoluteFilePath(directoryName, + mf->getDataFileType()); + + /* + * Create name of metric file data loaded information + */ + FileInformation metricFileInfo(metricFileName); + AString thePath, theName, theExtension; + metricFileInfo.getFileComponents(thePath, + theName, + theExtension); + theName.append("_" + m_dataLoadedName); + AString newFileName = FileInformation::assembleFileComponents(thePath, + theName, + theExtension); + mf->setFileName(newFileName); + mf->setMapName(0, m_dataLoadedName); + + /* + * Need to copy color palette since it may be the default + */ + PaletteColorMapping* metricPalette = mf->getMapPaletteColorMapping(0); + CaretAssert(metricPalette); + const PaletteColorMapping* myPalette = getMapPaletteColorMapping(0); + CaretAssert(myPalette); + metricPalette->copy(*myPalette, + true); + + mf->updateAfterFileDataChanges(); + mf->updateScalarColoringForMap(0); + mf->setModified(); + } + catch (const DataFileException& dfe) { + errorMessageOut = dfe.whatString(); + if (mf != NULL) { + delete mf; + mf = NULL; + } + } + + return mf; +} + + +/** + * Save data to the scene. + * + * @param sceneAttributes + * Attributes for the scene. Scenes may be of different types + * (full, generic, etc) and the attributes should be checked when + * restoring the scene. + * + * @param sceneClass + * sceneClass to which data members should be added. Will always + * be valid (non-NULL). + */ +void +MetricDynamicConnectivityFile::saveFileDataToScene(const SceneAttributes* sceneAttributes, + SceneClass* sceneClass) +{ + MetricFile::saveFileDataToScene(sceneAttributes, + sceneClass); + m_sceneAssistant->saveMembers(sceneAttributes, + sceneClass); +} + +/** + * Restore file data from the scene. + * + * @param sceneAttributes + * Attributes for the scene. Scenes may be of different types + * (full, generic, etc) and the attributes should be checked when + * restoring the scene. + * + * @param sceneClass + * sceneClass for the instance of a class that implements + * this interface. Will NEVER be NULL. + */ +void +MetricDynamicConnectivityFile::restoreFileDataFromScene(const SceneAttributes* sceneAttributes, + const SceneClass* sceneClass) +{ + m_connectivityDataLoaded->reset(); + + MetricFile::restoreFileDataFromScene(sceneAttributes, + sceneClass); + m_sceneAssistant->restoreMembers(sceneAttributes, + sceneClass); + + + switch (m_connectivityDataLoaded->getMode()) { + case ConnectivityDataLoaded::MODE_COLUMN: + break; + case ConnectivityDataLoaded::MODE_NONE: + break; + case ConnectivityDataLoaded::MODE_ROW: + break; + case ConnectivityDataLoaded::MODE_SURFACE_NODE: + { + StructureEnum::Enum structure = StructureEnum::INVALID; + int32_t surfaceNumberOfVertices(-1); + int32_t vertexIndex(-1); + int64_t rowIndex(-1); + int64_t columnIndex(-1); + m_connectivityDataLoaded->getSurfaceNodeLoading(structure, + surfaceNumberOfVertices, + vertexIndex, + rowIndex, + columnIndex); + if (vertexIndex >= 0) { + loadDataForSurfaceNode(surfaceNumberOfVertices, + structure, + vertexIndex); + } + } + break; + case ConnectivityDataLoaded::MODE_SURFACE_NODE_AVERAGE: + { + StructureEnum::Enum structure = StructureEnum::INVALID; + int32_t surfaceNumberOfVertices(-1); + std::vector vertexIndices; + m_connectivityDataLoaded->getSurfaceAverageNodeLoading(structure, + surfaceNumberOfVertices, + vertexIndices); + if ( ! vertexIndices.empty()) { + loadAverageDataForSurfaceNodes(surfaceNumberOfVertices, + structure, + vertexIndices); + } + } + break; + case ConnectivityDataLoaded::MODE_VOXEL_IJK_AVERAGE: + break; + case ConnectivityDataLoaded::MODE_VOXEL_XYZ: + break; + } +} + diff --git a/src/Files/MetricDynamicConnectivityFile.h b/src/Files/MetricDynamicConnectivityFile.h new file mode 100644 index 000000000..b0dc344a7 --- /dev/null +++ b/src/Files/MetricDynamicConnectivityFile.h @@ -0,0 +1,138 @@ +#ifndef __METRIC_DYNAMIC_CONNECTIVITY_FILE_H__ +#define __METRIC_DYNAMIC_CONNECTIVITY_FILE_H__ + +/*LICENSE_START*/ +/* + * Copyright (C) 2019 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + + + +#include + +#include "MetricFile.h" + + + +namespace caret { + + class ConnectivityCorrelation; + class ConnectivityDataLoaded; + + class MetricDynamicConnectivityFile : public MetricFile { + + public: + MetricDynamicConnectivityFile(MetricFile* parentMetricFile); + + virtual ~MetricDynamicConnectivityFile(); + + MetricDynamicConnectivityFile(const MetricDynamicConnectivityFile&) = delete; + + MetricDynamicConnectivityFile& operator=(const MetricDynamicConnectivityFile&) = delete; + + void initializeFile(); + + virtual void clear() override; + + virtual void addToDataFileContentInformation(DataFileContentInformation& dataFileInformation) override; + + virtual void readFile(const AString& filename) override; + + virtual void writeFile(const AString& filename) override; + + virtual bool supportsWriting() const override; + + MetricFile* getParentMetricFile(); + + const MetricFile* getParentMetricFile() const; + + bool isDataValid() const; + + bool isEnabledAsLayer() const; + + void setEnabledAsLayer(const bool enabled); + + bool loadConnectivityForVoxelXYZ(const float xyz[3]); + + bool loadMapAverageDataForVoxelIndices(const int64_t volumeDimensionIJK[3], + const std::vector& voxelIndices); + + bool isDataLoadingEnabled() const; + + void setDataLoadingEnabled(const bool enabled); + + const ConnectivityDataLoaded* getConnectivityDataLoaded() const; + + bool loadDataForSurfaceNode(const int32_t surfaceNumberOfNodes, + const StructureEnum::Enum structure, + const int32_t nodeIndex); + + bool loadAverageDataForSurfaceNodes(const int32_t surfaceNumberOfNodes, + const StructureEnum::Enum structure, + const std::vector& nodeIndices); + + MetricFile* newMetricFileFromLoadedData(const AString& directoryName, + AString& errorMessageOut); + + // ADD_NEW_METHODS_HERE + + protected: + virtual void saveFileDataToScene(const SceneAttributes* sceneAttributes, + SceneClass* sceneClass) override; + + virtual void restoreFileDataFromScene(const SceneAttributes* sceneAttributes, + const SceneClass* sceneClass) override; + + private: + void clearPrivateData(); + + void clearVertexValues(); + + bool getConnectivityForVertexIndex(const int32_t vertexIndex, + std::vector& vertexDataOut); + + const MetricFile* m_parentMetricFile; + + std::unique_ptr m_sceneAssistant; + + std::unique_ptr m_connectivityCorrelation; + + AString m_dataLoadedName; + + int32_t m_numberOfVertices = 0; + + bool m_validDataFlag = false; + + bool m_enabledAsLayer = true; + + bool m_dataLoadingEnabledFlag = true; + + std::unique_ptr m_connectivityDataLoaded; + + bool m_connectivityCorrelationFailedFlag = false; + + // ADD_NEW_MEMBERS_HERE + + }; + +#ifdef __METRIC_DYNAMIC_CONNECTIVITY_FILE_DECLARE__ + // +#endif // __METRIC_DYNAMIC_CONNECTIVITY_FILE_DECLARE__ + +} // namespace +#endif //__METRIC_DYNAMIC_CONNECTIVITY_FILE_H__ diff --git a/src/Files/MetricFile.cxx b/src/Files/MetricFile.cxx index c7892b114..3cb6687ac 100644 --- a/src/Files/MetricFile.cxx +++ b/src/Files/MetricFile.cxx @@ -27,6 +27,7 @@ #include "GiftiFile.h" #include "MapFileDataSelector.h" #include "MathFunctions.h" +#include "MetricDynamicConnectivityFile.h" #include "MetricFile.h" #include "NiftiEnums.h" #include "PaletteColorMapping.h" @@ -36,6 +37,8 @@ using namespace caret; +const AString MetricFile::s_paletteColorMappingNameInMetaData = "__DYNAMIC_FILE_PALETTE_COLOR_MAPPING__"; + /** * Constructor. */ @@ -45,6 +48,18 @@ MetricFile::MetricFile() this->initializeMembersMetricFile(); } +/** + * Constructor for subclasses + * + * @param dataFileType + * Filetype for subclass + */ +MetricFile::MetricFile(const DataFileTypeEnum::Enum dataFileType) +: GiftiTypeFile(dataFileType) +{ + this->initializeMembersMetricFile(); +} + /** * Copy constructor. * @@ -92,6 +107,21 @@ void MetricFile::writeFile(const AString& filename) { CaretLogWarning("metric file '" + filename + "' should be saved ending in .func.gii or .shape.gii, see wb_command -gifti-help"); } + /* + * Put the child dynamic data-series file's palette in the file's metadata. + */ + if (m_lazyInitializedDynamicConnectivityFile != NULL) { + GiftiMetaData* fileMetaData = m_lazyInitializedDynamicConnectivityFile->getCiftiXML().getFileMetaData(); + CaretAssert(fileMetaData); + if (m_lazyInitializedDynamicConnectivityFile->getNumberOfMaps() > 0) { + fileMetaData->set(s_paletteColorMappingNameInMetaData, + m_lazyInitializedDynamicConnectivityFile->getMapPaletteColorMapping(0)->encodeInXML()); + } + else { + fileMetaData->remove(s_paletteColorMappingNameInMetaData); + } + } + caret::GiftiTypeFile::writeFile(filename); } @@ -740,6 +770,11 @@ MetricFile::saveFileDataToScene(const SceneAttributes* sceneAttributes, sceneClass->addBooleanArray("m_chartingEnabledForTab", m_chartingEnabledForTab, BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS); + + if (m_lazyInitializedDynamicConnectivityFile != NULL) { + sceneClass->addClass(m_lazyInitializedDynamicConnectivityFile->saveToScene(sceneAttributes, + "m_lazyInitializedDynamicConnectivityFile")); + } } /** @@ -774,6 +809,61 @@ MetricFile::restoreFileDataFromScene(const SceneAttributes* sceneAttributes, m_chartingEnabledForTab[i] = false; } } + + const SceneClass* dynamicFileSceneClass = sceneClass->getClass("m_lazyInitializedDynamicConnectivityFile"); + if (dynamicFileSceneClass != NULL) { + MetricDynamicConnectivityFile* denseDynamicFile = getMetricDynamicConnectivityFile(); + denseDynamicFile->restoreFromScene(sceneAttributes, + dynamicFileSceneClass); + } +} + +/** + * @return The volume dynamic connectivity file for a data-series (functional) file + * that contains at least two time points. Note that some files may + * have type anatomy but still contain functional data. + * Will return NULL for other types. + */ +const MetricDynamicConnectivityFile* +MetricFile::getMetricDynamicConnectivityFile() const +{ + MetricFile* nonConstThis = const_cast(this); + return nonConstThis->getMetricDynamicConnectivityFile(); +} + +/** + * @return The volume dynamic connectivity file for a data-series (functional) file + * that contains at least two time points. Note that some files may + * have type anatomy but still contain functional data. + * Will return NULL for other types. + */ +MetricDynamicConnectivityFile* +MetricFile::getMetricDynamicConnectivityFile() +{ + if (m_lazyInitializedDynamicConnectivityFile == NULL) { + if (getNumberOfMaps() > 1) { + m_lazyInitializedDynamicConnectivityFile.reset(new MetricDynamicConnectivityFile(this)); + + m_lazyInitializedDynamicConnectivityFile->initializeFile(); + + /* + * Palette for dynamic file is in file metadata + */ + GiftiMetaData* fileMetaData = getFileMetaData(); + const AString encodedPaletteColorMappingString = fileMetaData->get(s_paletteColorMappingNameInMetaData); + if ( ! encodedPaletteColorMappingString.isEmpty()) { + if (m_lazyInitializedDynamicConnectivityFile->getNumberOfMaps() > 0) { + PaletteColorMapping* pcm = m_lazyInitializedDynamicConnectivityFile->getMapPaletteColorMapping(0); + CaretAssert(pcm); + pcm->decodeFromStringXML(encodedPaletteColorMappingString); + } + } + + m_lazyInitializedDynamicConnectivityFile->clearModified(); + } + } + + return m_lazyInitializedDynamicConnectivityFile.get(); } diff --git a/src/Files/MetricFile.h b/src/Files/MetricFile.h index b22298dda..dc00ae2a7 100644 --- a/src/Files/MetricFile.h +++ b/src/Files/MetricFile.h @@ -22,6 +22,8 @@ */ /*LICENSE_END*/ +#include + #include #include @@ -32,6 +34,7 @@ namespace caret { class GiftiDataArray; + class MetricDynamicConnectivityFile; /** * \brief A Metric data file. @@ -99,7 +102,13 @@ namespace caret { //override writeFile in order to check filename against type of file virtual void writeFile(const AString& filename); + MetricDynamicConnectivityFile* getMetricDynamicConnectivityFile(); + + const MetricDynamicConnectivityFile* getMetricDynamicConnectivityFile() const; + protected: + MetricFile(const DataFileTypeEnum::Enum dataFileType); + /** * Validate the contents of the file after it * has been read such as correct number of @@ -121,7 +130,12 @@ namespace caret { /** Points to actual data in each Gifti Data Array */ std::vector columnDataPointers; + std::unique_ptr m_lazyInitializedDynamicConnectivityFile; + bool m_chartingEnabledForTab[BrainConstants::MAXIMUM_NUMBER_OF_BROWSER_TABS]; + + static const AString s_paletteColorMappingNameInMetaData; + }; } // namespace diff --git a/src/Files/PaletteFile.cxx b/src/Files/PaletteFile.cxx index df910e927..06f12de84 100644 --- a/src/Files/PaletteFile.cxx +++ b/src/Files/PaletteFile.cxx @@ -1679,6 +1679,8 @@ PaletteFile::setDefaultPaletteColorMapping(PaletteColorMapping* paletteColorMapp case DataFileTypeEnum::METRIC: checkShapeFile = true; break; + case DataFileTypeEnum::METRIC_DYNAMIC: + break; case DataFileTypeEnum::PALETTE: invalid = true; break; diff --git a/src/Files/SceneFile.cxx b/src/Files/SceneFile.cxx index 52d5a51b9..e41f7901e 100644 --- a/src/Files/SceneFile.cxx +++ b/src/Files/SceneFile.cxx @@ -1344,6 +1344,9 @@ SceneFile::getAllDataFileNamesFromAllScenes() const break; case DataFileTypeEnum::METRIC: break; + case DataFileTypeEnum::METRIC_DYNAMIC: + validDiskFileFlag = false; + break; case DataFileTypeEnum::PALETTE: break; case DataFileTypeEnum::RGBA: diff --git a/src/Files/SpecFile.cxx b/src/Files/SpecFile.cxx index 850e2dd41..bd98ddceb 100644 --- a/src/Files/SpecFile.cxx +++ b/src/Files/SpecFile.cxx @@ -1821,6 +1821,9 @@ SpecFile::isDataFileTypeAllowedInSpecFile(const DataFileTypeEnum::Enum dataFileT break; case DataFileTypeEnum::METRIC: break; + case DataFileTypeEnum::METRIC_DYNAMIC: + allowedFlag = false; + break; case DataFileTypeEnum::PALETTE: break; case DataFileTypeEnum::RGBA: diff --git a/src/Files/VolumeDynamicConnectivityFile.cxx b/src/Files/VolumeDynamicConnectivityFile.cxx index 5f0f14441..51a7a350d 100644 --- a/src/Files/VolumeDynamicConnectivityFile.cxx +++ b/src/Files/VolumeDynamicConnectivityFile.cxx @@ -26,6 +26,7 @@ #undef __VOLUME_DYNN_CONN_FILE_DECLARE__ #include "CaretAssert.h" +#include "CaretLogger.h" #include "ConnectivityCorrelation.h" #include "ConnectivityDataLoaded.h" #include "DataFileException.h" @@ -474,47 +475,77 @@ VolumeDynamicConnectivityFile::loadConnectivityForVoxelIndex(const int64_t ijk[3 * @return * True if data was loaded. */ - bool VolumeDynamicConnectivityFile::getConnectivityForVoxelIndex(const int64_t ijk[3], - std::vector& voxelsOut) + std::vector& voxelsOut) { bool validFlag(false); - if (m_voxelData != NULL) { - if (indexValid(ijk)) { - const int64_t voxelCount(m_dimI * m_dimJ * m_dimK); - if (m_connectivityCorrelation == NULL) { - /* - * Need data and timepoint count from parent volume - * IJK dimensions are same in this and parent volume - */ - CaretAssert(m_parentVolumeFile); - const float* parentVoxels = m_parentVolumeFile->getFrame(0); - CaretAssert(parentVoxels); - std::vector parentVolumeDimensions; - m_parentVolumeFile->getDimensions(parentVolumeDimensions); - CaretAssert(parentVolumeDimensions.size() >= 4); - const int64_t timePointCount = parentVolumeDimensions[3]; + + if ( ! m_connectivityCorrelationFailedFlag) { + if (m_voxelData != NULL) { + if (indexValid(ijk)) { + const int64_t voxelCount(m_dimI * m_dimJ * m_dimK); + if (m_connectivityCorrelation == NULL) { + /* + * Need data and timepoint count from parent volume + * IJK dimensions are same in this and parent volume + */ + CaretAssert(m_parentVolumeFile); + const float* parentVoxels = m_parentVolumeFile->getFrame(0); + CaretAssert(parentVoxels); + std::vector parentVolumeDimensions; + m_parentVolumeFile->getDimensions(parentVolumeDimensions); + CaretAssert(parentVolumeDimensions.size() >= 4); + const int64_t timePointCount = parentVolumeDimensions[3]; + + const int64_t numberOfBrainordinates(voxelCount); // Each IJK voxel is a group + const int64_t nextBrainordinateStride(1); // All groups have one element in each 'brick' + const int64_t numberOfTimePoints(timePointCount); // Each group contains timepoints + const int64_t nextTimePointStride(voxelCount); // Each group element is in separate IJK 'brick' + + AString errorMessage; + ConnectivityCorrelation* cc = ConnectivityCorrelation::newInstance(parentVoxels, + numberOfBrainordinates, + nextBrainordinateStride, + numberOfTimePoints, + nextTimePointStride, + errorMessage); + + { + + } + if (cc != NULL) { + m_connectivityCorrelation.reset(cc); + } + else { + m_connectivityCorrelationFailedFlag = true; + CaretLogSevere("Failed to create connectvity correlation for " + + m_parentVolumeFile->getFileNameNoPath()); + } + // m_connectivityCorrelation.reset(new ConnectivityCorrelation(parentVoxels, + // groupCount, + // groupStride, + // groupDataCount, + // groupDataStride)); + } - const int64_t groupCount(voxelCount); // Each IJK voxel is a group - const int64_t groupStride(1); // All groups have one element in each 'brick' - const int64_t groupDataCount(timePointCount); // Each group contains timepoints - const int64_t groupDataStride(voxelCount); // Each group element is in separate IJK 'brick' - m_connectivityCorrelation.reset(new ConnectivityCorrelation(parentVoxels, - groupCount, - groupStride, - groupDataCount, - groupDataStride)); + if (m_connectivityCorrelation) { + const int64_t myTimePointOffset = getVoxelOffset(ijk[0], ijk[1], ijk[2], 0); + m_connectivityCorrelation->getCorrelationForBrainordinate(myTimePointOffset, + voxelsOut); + CaretAssert(voxelCount == static_cast(voxelsOut.size())); + validFlag = true; + } } - - const int64_t myTimePointOffset = getVoxelOffset(ijk[0], ijk[1], ijk[2], 0); - m_connectivityCorrelation->getCorrelationForGroup(myTimePointOffset, - voxelsOut); - CaretAssert(voxelCount == static_cast(voxelsOut.size())); - validFlag = true; } } + if ( ! validFlag) { + voxelsOut.resize(m_numberOfVoxels); + std::fill(voxelsOut.begin(), voxelsOut.end(), + 0.0f); + } + return validFlag; } @@ -546,7 +577,9 @@ VolumeDynamicConnectivityFile::matchesDimensions(const int64_t dimI, * @return A volume file using the loaded data (will return NULL if there is an error). * * @param directoryName + * Directory for file * @param errorMessageOut + * Contains error information */ VolumeFile* VolumeDynamicConnectivityFile::newVolumeFileFromLoadedData(const AString& directoryName, diff --git a/src/Files/VolumeDynamicConnectivityFile.h b/src/Files/VolumeDynamicConnectivityFile.h index fb3124523..a515f3b12 100644 --- a/src/Files/VolumeDynamicConnectivityFile.h +++ b/src/Files/VolumeDynamicConnectivityFile.h @@ -129,6 +129,8 @@ namespace caret { std::unique_ptr m_sceneAssistant; std::unique_ptr m_connectivityCorrelation; + + bool m_connectivityCorrelationFailedFlag = false; float* m_voxelData = NULL; diff --git a/src/Gifti/GiftiDataArray.cxx b/src/Gifti/GiftiDataArray.cxx index a8fb0e34a..74f15186e 100644 --- a/src/Gifti/GiftiDataArray.cxx +++ b/src/Gifti/GiftiDataArray.cxx @@ -1723,10 +1723,21 @@ const FastStatistics* GiftiDataArray::getFastStatistics() const return m_fastStatistics; } +/** + * Invalidate the histograms + */ +void +GiftiDataArray::invalidateHistograms() +{ + m_histogramNeedsUpdateFlag = true; + m_histogramLimitedValuesNeedsUpdateFlag = true; +} + const Histogram* GiftiDataArray::getHistogram(const int32_t numberOfBuckets) const { if (this->getDataType() == NiftiDataTypeEnum::NIFTI_TYPE_FLOAT32) { - bool updateHistogramFlag = false; + bool updateHistogramFlag = m_histogramNeedsUpdateFlag; + m_histogramNeedsUpdateFlag = false; if (m_histogram == NULL) { m_histogram.grabNew(new Histogram(numberOfBuckets)); updateHistogramFlag = true; @@ -1788,7 +1799,8 @@ const Histogram* GiftiDataArray::getHistogram(const int32_t numberOfBuckets, const bool includeZeroValues) const { if (this->getDataType() == NiftiDataTypeEnum::NIFTI_TYPE_FLOAT32) { - bool updateHistogramFlag = false; + bool updateHistogramFlag = m_histogramLimitedValuesNeedsUpdateFlag; + m_histogramLimitedValuesNeedsUpdateFlag = false; if (m_histogramLimitedValues == NULL) { m_histogramLimitedValues.grabNew(new Histogram(numberOfBuckets)); diff --git a/src/Gifti/GiftiDataArray.h b/src/Gifti/GiftiDataArray.h index e6898e4d3..b09c4d68d 100644 --- a/src/Gifti/GiftiDataArray.h +++ b/src/Gifti/GiftiDataArray.h @@ -304,6 +304,8 @@ namespace caret { const float mostNegativeValueInclusive, const bool includeZeroValues) const; + void invalidateHistograms(); + protected: //validate the array @@ -412,6 +414,7 @@ namespace caret { mutable CaretPointer m_histogram; mutable int32_t m_histogramNumberOfBuckets = 100; + mutable bool m_histogramNeedsUpdateFlag = false; mutable CaretPointer m_histogramLimitedValues; mutable int32_t m_histogramLimitedValuesNumberOfBuckets = 100; @@ -420,6 +423,7 @@ namespace caret { mutable float m_histogramLimitedValuesLeastNegativeValueInclusive; mutable float m_histogramLimitedValuesMostNegativeValueInclusive; mutable bool m_histogramLimitedValuesIncludeZeroValues; + mutable bool m_histogramLimitedValuesNeedsUpdateFlag = false; /// statistics about data (DO NOT COPY) mutable DescriptiveStatistics* descriptiveStatisticsLimitedValues; diff --git a/src/GuiQt/BrainBrowserWindowOrientedToolBox.cxx b/src/GuiQt/BrainBrowserWindowOrientedToolBox.cxx index 23c8c1569..f7ee1e2c7 100644 --- a/src/GuiQt/BrainBrowserWindowOrientedToolBox.cxx +++ b/src/GuiQt/BrainBrowserWindowOrientedToolBox.cxx @@ -733,6 +733,9 @@ BrainBrowserWindowOrientedToolBox::receiveEvent(Event* event) break; case DataFileTypeEnum::METRIC: break; + case DataFileTypeEnum::METRIC_DYNAMIC: + haveConnFiles = true; + break; case DataFileTypeEnum::PALETTE: break; case DataFileTypeEnum::RGBA: diff --git a/src/GuiQt/CiftiConnectivityMatrixViewController.cxx b/src/GuiQt/CiftiConnectivityMatrixViewController.cxx index ef74b7bbb..4ef6a8ec3 100644 --- a/src/GuiQt/CiftiConnectivityMatrixViewController.cxx +++ b/src/GuiQt/CiftiConnectivityMatrixViewController.cxx @@ -49,6 +49,7 @@ #include "FiberTrajectoryMapProperties.h" #include "FilePathNamePrefixCompactor.h" #include "GuiManager.h" +#include "MetricDynamicConnectivityFile.h" #include "VolumeDynamicConnectivityFile.h" #include "WuQMacroManager.h" #include "WuQMessageBox.h" @@ -157,6 +158,11 @@ CiftiConnectivityMatrixViewController::updateViewController() files.push_back(*matrixIter); } + std::vector metricDynConnFiles; + brain->getMetricDynamicConnectivityFiles(metricDynConnFiles); + files.insert(files.end(), + metricDynConnFiles.begin(), metricDynConnFiles.end()); + std::vector volumeDynConnFiles; brain->getVolumeDynamicConnectivityFiles(volumeDynConnFiles); files.insert(files.end(), @@ -196,13 +202,11 @@ CiftiConnectivityMatrixViewController::updateViewController() macroManager->addMacroSupportToObject(checkBox, "Enable " + descriptivePrefix); - const AString dynToolTip("This option is enabled only for .dynconn.nii (dynamic connectivity) files. " + const AString dynToolTip("This option is enabled only for dynamic connectivity files. " "When checked, this dynamic connectivity file will appear in the Overlay Layers' File selection combo box. " "Dynamic connectivity files do not explicitly exist but allow dynamic " - "computation of connectivity from a dense data series (.dtseries) file. " - "Dynamic connectivity allows one to view connectivity for a brainordinate without creation of an " - "extremely large dense connectivity (.dconn.nii) file. " - "In Preferences, one may set the default to show/hide .dynconn.nii files."); + "computation of connectivity from a CIFTI data-series, metric, or volume file. " + "In Preferences, one may set the default to show/hide dynamic connectivity files."); layerCheckBox = new QCheckBox(""); WuQtUtilities::setWordWrappedToolTip(layerCheckBox, dynToolTip); m_layerCheckBoxes.push_back(layerCheckBox); @@ -264,6 +268,7 @@ CiftiConnectivityMatrixViewController::updateViewController() const CiftiMappableConnectivityMatrixDataFile* matrixFile = dynamic_cast(files[i]); const CiftiFiberTrajectoryFile* trajFile = dynamic_cast(files[i]); const VolumeDynamicConnectivityFile* volDynConnFile = dynamic_cast(files[i]); + const MetricDynamicConnectivityFile* metricDynConnFile = dynamic_cast(files[i]); bool checkStatus = false; if (matrixFile != NULL) { @@ -275,6 +280,9 @@ CiftiConnectivityMatrixViewController::updateViewController() else if (volDynConnFile != NULL) { checkStatus = volDynConnFile->isDataLoadingEnabled(); } + else if (metricDynConnFile != NULL) { + checkStatus = metricDynConnFile->isDataLoadingEnabled(); + } else { CaretAssertMessage(0, "Has a new file type been added?"); } @@ -290,6 +298,9 @@ CiftiConnectivityMatrixViewController::updateViewController() else if (volDynConnFile != NULL) { layerCheckBox->setChecked(volDynConnFile->isEnabledAsLayer()); } + else if (metricDynConnFile != NULL) { + layerCheckBox->setChecked(metricDynConnFile->isEnabledAsLayer()); + } else { layerCheckBox->setChecked(false); } @@ -308,10 +319,12 @@ CiftiConnectivityMatrixViewController::updateViewController() if (dynamic_cast(files[i]) != NULL) { showOrientationComboBox = true; } - if (dynamic_cast(files[i]) != NULL) { layerCheckBoxValid = true; } + else if (dynamic_cast(files[i]) != NULL) { + layerCheckBoxValid = true; + } else if (dynamic_cast(files[i]) != NULL) { layerCheckBoxValid = true; } @@ -344,12 +357,14 @@ CiftiConnectivityMatrixViewController::updateFiberOrientationComboBoxes() QComboBox* comboBox = m_fiberOrientationFileComboBoxes[i]; CiftiMappableConnectivityMatrixDataFile* matrixFile = NULL; CiftiFiberTrajectoryFile* trajFile = NULL; + MetricDynamicConnectivityFile* metricDynConnFile(NULL); VolumeDynamicConnectivityFile* volDynConnFile = NULL; if (comboBox->isEnabled()) { getFileAtIndex(i, matrixFile, trajFile, + metricDynConnFile, volDynConnFile); } @@ -406,16 +421,21 @@ CiftiConnectivityMatrixViewController::enabledCheckBoxClicked(int indx) CiftiMappableConnectivityMatrixDataFile* matrixFile = NULL; CiftiFiberTrajectoryFile* trajFile = NULL; VolumeDynamicConnectivityFile* volDynConnFile(NULL); + MetricDynamicConnectivityFile* metricDynConnFile(NULL); getFileAtIndex(indx, matrixFile, trajFile, + metricDynConnFile, volDynConnFile); if (matrixFile != NULL) { matrixFile->setMapDataLoadingEnabled(0, newStatus); } + else if (metricDynConnFile != NULL) { + metricDynConnFile->setDataLoadingEnabled(newStatus); + } else if (trajFile != NULL) { trajFile->setDataLoadingEnabled(newStatus); } @@ -443,11 +463,13 @@ CiftiConnectivityMatrixViewController::layerCheckBoxClicked(int indx) CiftiMappableConnectivityMatrixDataFile* matrixFile = NULL; CiftiFiberTrajectoryFile* trajFile = NULL; + MetricDynamicConnectivityFile* metricDynConnFile(NULL); VolumeDynamicConnectivityFile* volDynConnFile(NULL); getFileAtIndex(indx, matrixFile, trajFile, + metricDynConnFile, volDynConnFile); if (matrixFile != NULL) { @@ -456,6 +478,9 @@ CiftiConnectivityMatrixViewController::layerCheckBoxClicked(int indx) dynConnFile->setEnabledAsLayer(newStatus); } } + else if (metricDynConnFile != NULL) { + metricDynConnFile->setEnabledAsLayer(newStatus); + } else if (volDynConnFile != NULL) { volDynConnFile->setEnabledAsLayer(newStatus); } @@ -482,6 +507,8 @@ CiftiConnectivityMatrixViewController::layerCheckBoxClicked(int indx) * If there is a CIFTI matrix file at the given index, this will be non-NULL. * @param ciftiTrajFileOut * If there is a CIFTI trajectory file at the given index, this will be non-NULL. + * @param metricDynConnFileOut + * If there is a Metric Dynamic file at the given index, this will be non-NULL * @param volDynConnFileOut * If there is a volume dynamnic connectivity files at the given index, this will be non-NULL */ @@ -489,15 +516,17 @@ void CiftiConnectivityMatrixViewController::getFileAtIndex(const int32_t indx, CiftiMappableConnectivityMatrixDataFile* &ciftiMatrixFileOut, CiftiFiberTrajectoryFile* &ciftiTrajFileOut, + MetricDynamicConnectivityFile* &metricDynConnFileOut, VolumeDynamicConnectivityFile* &volDynConnFileOut) { CaretAssertVectorIndex(m_fileEnableCheckBoxes, indx); void* ptr = m_fileEnableCheckBoxes[indx]->property(FILE_POINTER_PROPERTY_NAME).value(); CaretMappableDataFile* mapFilePointer = (CaretMappableDataFile*)ptr; - ciftiMatrixFileOut = dynamic_cast(mapFilePointer); - ciftiTrajFileOut = dynamic_cast(mapFilePointer); - volDynConnFileOut = dynamic_cast(mapFilePointer); + ciftiMatrixFileOut = dynamic_cast(mapFilePointer); + ciftiTrajFileOut = dynamic_cast(mapFilePointer); + metricDynConnFileOut = dynamic_cast(mapFilePointer); + volDynConnFileOut = dynamic_cast(mapFilePointer); AString name = ""; if (mapFilePointer != NULL) { @@ -520,6 +549,9 @@ CiftiConnectivityMatrixViewController::getFileAtIndex(const int32_t indx, else if (ciftiTrajFileOut != NULL) { /* OK */ } + else if (metricDynConnFileOut != NULL) { + /* OK */ + } else if (volDynConnFileOut != NULL) { /* OK */ } @@ -542,11 +574,13 @@ CiftiConnectivityMatrixViewController::fiberOrientationFileComboBoxActivated(int CiftiMappableConnectivityMatrixDataFile* matrixFile = NULL; CiftiFiberTrajectoryFile* trajFile = NULL; + MetricDynamicConnectivityFile* metricDynConnFile(NULL); VolumeDynamicConnectivityFile* volDynConnFile(NULL); getFileAtIndex(indx, matrixFile, trajFile, + metricDynConnFile, volDynConnFile); CaretAssertMessage(trajFile, @@ -589,11 +623,13 @@ CiftiConnectivityMatrixViewController::copyToolButtonClicked(int indx) CiftiMappableConnectivityMatrixDataFile* matrixFile = NULL; CiftiFiberTrajectoryFile* trajFile = NULL; + MetricDynamicConnectivityFile* metricDynConnFile(NULL); VolumeDynamicConnectivityFile* volDynConnFile(NULL); getFileAtIndex(indx, matrixFile, trajFile, + metricDynConnFile, volDynConnFile); @@ -635,6 +671,22 @@ CiftiConnectivityMatrixViewController::copyToolButtonClicked(int indx) errorFlag = true; } } + else if (metricDynConnFile != NULL) { + MetricFile* newMetricFile = metricDynConnFile->newMetricFileFromLoadedData(directoryName, + errorMessage); + if (newMetricFile != NULL) { + EventDataFileAdd dataFileAdd(newMetricFile); + EventManager::get()->sendEvent(dataFileAdd.getPointer()); + + if (dataFileAdd.isError()) { + errorMessage = dataFileAdd.getErrorMessage(); + errorFlag = true; + } + } + else { + errorFlag = true; + } + } else if (trajFile != NULL) { CiftiFiberTrajectoryFile* newTrajFile = trajFile->newFiberTrajectoryFileFromLoadedRowData(directoryName, errorMessage); diff --git a/src/GuiQt/CiftiConnectivityMatrixViewController.h b/src/GuiQt/CiftiConnectivityMatrixViewController.h index 26260da7e..d0bdd3da1 100644 --- a/src/GuiQt/CiftiConnectivityMatrixViewController.h +++ b/src/GuiQt/CiftiConnectivityMatrixViewController.h @@ -38,6 +38,7 @@ namespace caret { class CiftiMappableConnectivityMatrixDataFile; class CiftiFiberTrajectoryFile; + class MetricDynamicConnectivityFile; class VolumeDynamicConnectivityFile; class CiftiConnectivityMatrixViewController : public QWidget, EventListenerInterface { @@ -77,6 +78,7 @@ namespace caret { void getFileAtIndex(const int32_t indx, CiftiMappableConnectivityMatrixDataFile* &ciftiMatrixFileOut, CiftiFiberTrajectoryFile* &ciftiTrajFileOut, + MetricDynamicConnectivityFile* &metricDynConnFileOut, VolumeDynamicConnectivityFile* &volDynConnFileOut); const QString m_objectNamePrefix; diff --git a/src/GuiQt/GuiManager.cxx b/src/GuiQt/GuiManager.cxx index 08379763d..03c444401 100644 --- a/src/GuiQt/GuiManager.cxx +++ b/src/GuiQt/GuiManager.cxx @@ -95,6 +95,7 @@ #include "ImageFile.h" #include "ImageCaptureDialog.h" #include "InformationDisplayDialog.h" +#include "MetricDynamicConnectivityFile.h" #include "ModelChartTwo.h" #include "OverlaySettingsEditorDialog.h" #include "MacDockMenu.h" @@ -786,6 +787,7 @@ GuiManager::testForModifiedFiles(const TestModifiedMode testModifiedMode, dataFileTypesToExclude.push_back(DataFileTypeEnum::CONNECTIVITY_DENSE_DYNAMIC); dataFileTypesToExclude.push_back(DataFileTypeEnum::CONNECTIVITY_FIBER_ORIENTATIONS_TEMPORARY); dataFileTypesToExclude.push_back(DataFileTypeEnum::CONNECTIVITY_FIBER_TRAJECTORY_TEMPORARY); + dataFileTypesToExclude.push_back(DataFileTypeEnum::METRIC_DYNAMIC); dataFileTypesToExclude.push_back(DataFileTypeEnum::VOLUME_DYNAMIC); switch (testModifiedMode) { @@ -3103,6 +3105,18 @@ GuiManager::processIdentification(const int32_t tabIndex, ciftiLoadingInfo); chartingDataManager->loadChartForSurfaceNode(surface, nodeIndex); + + std::vector metricDynConnFiles; + brain->getMetricDynamicConnectivityFiles(metricDynConnFiles); + + for (auto mdc : metricDynConnFiles) { + if (mdc->isEnabledAsLayer()) { + mdc->loadDataForSurfaceNode(surface->getNumberOfNodes(), + surface->getStructure(), + nodeIndex); + } + } + updateGraphicsFlag = true; } catch (const DataFileException& e) { diff --git a/src/GuiQt/IdentifyBrainordinateDialog.cxx b/src/GuiQt/IdentifyBrainordinateDialog.cxx index 8e46197cb..4a63cf2fd 100644 --- a/src/GuiQt/IdentifyBrainordinateDialog.cxx +++ b/src/GuiQt/IdentifyBrainordinateDialog.cxx @@ -169,6 +169,8 @@ IdentifyBrainordinateDialog::IdentifyBrainordinateDialog(QWidget* parent) break; case DataFileTypeEnum::METRIC: break; + case DataFileTypeEnum::METRIC_DYNAMIC: + break; case DataFileTypeEnum::PALETTE: break; case DataFileTypeEnum::RGBA: diff --git a/src/GuiQt/OverlayViewController.cxx b/src/GuiQt/OverlayViewController.cxx index c303ef4ae..2e006084e 100644 --- a/src/GuiQt/OverlayViewController.cxx +++ b/src/GuiQt/OverlayViewController.cxx @@ -1034,6 +1034,9 @@ OverlayViewController::menuConstructionAboutToShow() break; case DataFileTypeEnum::METRIC: break; + case DataFileTypeEnum::METRIC_DYNAMIC: + dynConnFlag = true; + break; case DataFileTypeEnum::PALETTE: break; case DataFileTypeEnum::RGBA: diff --git a/src/GuiQt/UserInputModeViewContextMenu.cxx b/src/GuiQt/UserInputModeViewContextMenu.cxx index 81dec53ac..141684efb 100644 --- a/src/GuiQt/UserInputModeViewContextMenu.cxx +++ b/src/GuiQt/UserInputModeViewContextMenu.cxx @@ -62,6 +62,7 @@ #include "MapFileDataSelector.h" #include "Overlay.h" #include "OverlaySet.h" +#include "MetricDynamicConnectivityFile.h" #include "Model.h" #include "ProgressReportingDialog.h" #include "SelectionItemBorderSurface.h" @@ -368,13 +369,21 @@ UserInputModeViewContextMenu::addBorderRegionOfInterestActions() Brain* brain = borderID->getBrain(); std::vector ciftiMatrixFiles; brain->getAllCiftiConnectivityMatrixFiles(ciftiMatrixFiles); - bool hasCiftiConnectivity = (ciftiMatrixFiles.empty() == false); + bool hasConnectivityFile = (ciftiMatrixFiles.empty() == false); + + std::vector metricDynConnFiles; + brain->getMetricDynamicConnectivityFiles(metricDynConnFiles); + for (auto mdc : metricDynConnFiles) { + if (mdc->isDataLoadingEnabled()) { + hasConnectivityFile = true; + } + } /* * Connectivity actions for borders */ - if (hasCiftiConnectivity) { - const QString text = ("Show CIFTI Connectivity for Nodes Inside Border " + if (hasConnectivityFile) { + const QString text = ("Show Connectivity for Vertices Inside Border " + borderID->getBorder()->getName()); QAction* action = WuQtUtilities::createAction(text, "", @@ -388,7 +397,7 @@ UserInputModeViewContextMenu::addBorderRegionOfInterestActions() brain->getAllChartableBrainordinateDataFiles(chartableFiles); if (chartableFiles.empty() == false) { - const QString text = ("Show Charts for Nodes Inside Border " + const QString text = ("Show Charts for Vertices Inside Border " + borderID->getBorder()->getName()); QAction* action = WuQtUtilities::createAction(text, "", @@ -672,6 +681,7 @@ UserInputModeViewContextMenu::addLabelRegionOfInterestActions() std::vector ciftiMatrixFiles; std::vector ciftiFiberTrajectoryFiles; std::vector chartableFiles; + std::vector metricDynConnFiles; std::vector volDynConnFiles; /* * Get all files in displayed overlays @@ -702,13 +712,19 @@ UserInputModeViewContextMenu::addLabelRegionOfInterestActions() chartableFiles.push_back(lineSeriesFile); } + MetricDynamicConnectivityFile* metricDynConnFile = dynamic_cast(mapFile); + if (metricDynConnFile != NULL) { + metricDynConnFiles.push_back(metricDynConnFile); + } + VolumeDynamicConnectivityFile* volDynnFile = dynamic_cast(mapFile); if (volDynnFile != NULL) { volDynConnFiles.push_back(volDynnFile); } } const bool hasDynamicConnectivity = ( ( ! ciftiMatrixFiles.empty()) - || ( ! volDynConnFiles.empty())); + || ( ! metricDynConnFiles.empty()) + || ( ! volDynConnFiles.empty()) ); const bool haveCiftiFiberTrajectoryFiles = ( ! ciftiFiberTrajectoryFiles.empty()); const bool haveChartableFiles = ( ! chartableFiles.empty()); @@ -1054,6 +1070,15 @@ UserInputModeViewContextMenu::connectivityActionSelected(QAction* action) pc->ciftiConnectivityManager->loadAverageDataForSurfaceNodes(pc->brain, pc->surface, nodeIndices); + { + std::vector metricDynConnFiles; + pc->brain->getMetricDynamicConnectivityFiles(metricDynConnFiles); + for (auto mdcf : metricDynConnFiles) { + mdcf->loadAverageDataForSurfaceNodes(pc->surface->getNumberOfNodes(), + pc->surface->getStructure(), + nodeIndices); + } + } break; case ParcelType::PARCEL_TYPE_VOLUME_VOXELS: pc->ciftiConnectivityManager->loadAverageDataForVoxelIndices(pc->brain, @@ -1203,6 +1228,17 @@ UserInputModeViewContextMenu::borderCiftiConnectivitySelected() ciftiConnMann->loadAverageDataForSurfaceNodes(borderID->getBrain(), surface, nodeIndices); + + { + Brain* brain = GuiManager::get()->getBrain(); + std::vector metricDynConnFiles; + brain->getMetricDynamicConnectivityFiles(metricDynConnFiles); + for (auto mdcf : metricDynConnFiles) { + mdcf->loadAverageDataForSurfaceNodes(surface->getNumberOfNodes(), + surface->getStructure(), + nodeIndices); + } + } } catch (const DataFileException& e) { cursor.restoreCursor(); -- GitLab From 0cae1ae9ef26181358475350b8a4541d43dcf762 Mon Sep 17 00:00:00 2001 From: John Harwell Date: Mon, 15 Jul 2019 14:15:05 -0500 Subject: [PATCH 386/427] Rename some Tile Tabs classes for Grid layouts to include 'Grid' in their name in preparation of new Manual Tile Tab layouts. --- src/Brain/BrainOpenGLViewportContent.cxx | 12 +- src/Brain/BrowserWindowContent.cxx | 28 ++--- src/Brain/BrowserWindowContent.h | 8 +- src/Common/CMakeLists.txt | 16 +-- src/Common/TileTabsConfiguration.cxx | 66 +++++------ src/Common/TileTabsConfiguration.h | 22 ++-- ...nModeEnum.cxx => TileTabsGridModeEnum.cxx} | 108 +++++++++--------- ...ationModeEnum.h => TileTabsGridModeEnum.h} | 26 ++--- ... TileTabsGridRowColumnContentTypeEnum.cxx} | 108 +++++++++--------- ...=> TileTabsGridRowColumnContentTypeEnum.h} | 26 ++--- ...t.cxx => TileTabsGridRowColumnElement.cxx} | 60 +++++----- ...ement.h => TileTabsGridRowColumnElement.h} | 38 +++--- ... TileTabsGridRowColumnStretchTypeEnum.cxx} | 108 +++++++++--------- ...=> TileTabsGridRowColumnStretchTypeEnum.h} | 26 ++--- src/GuiQt/BrainBrowserWindow.cxx | 24 ++-- src/GuiQt/BrainBrowserWindow.h | 6 +- src/GuiQt/BrainBrowserWindowToolBar.cxx | 4 +- src/GuiQt/TileTabsConfigurationDialog.cxx | 40 +++---- src/GuiQt/TileTabsConfigurationDialog.h | 10 +- src/GuiQt/TileTabsConfigurationModifier.cxx | 16 +-- src/GuiQt/TileTabsConfigurationModifier.h | 4 +- 21 files changed, 378 insertions(+), 378 deletions(-) rename src/Common/{TileTabsConfigurationModeEnum.cxx => TileTabsGridModeEnum.cxx} (64%) rename src/Common/{TileTabsConfigurationModeEnum.h => TileTabsGridModeEnum.h} (77%) rename src/Common/{TileTabsRowColumnContentTypeEnum.cxx => TileTabsGridRowColumnContentTypeEnum.cxx} (61%) rename src/Common/{TileTabsRowColumnContentTypeEnum.h => TileTabsGridRowColumnContentTypeEnum.h} (74%) rename src/Common/{TileTabsRowColumnElement.cxx => TileTabsGridRowColumnElement.cxx} (59%) rename src/Common/{TileTabsRowColumnElement.h => TileTabsGridRowColumnElement.h} (54%) rename src/Common/{TileTabsRowColumnStretchTypeEnum.cxx => TileTabsGridRowColumnStretchTypeEnum.cxx} (61%) rename src/Common/{TileTabsRowColumnStretchTypeEnum.h => TileTabsGridRowColumnStretchTypeEnum.h} (74%) diff --git a/src/Brain/BrainOpenGLViewportContent.cxx b/src/Brain/BrainOpenGLViewportContent.cxx index ae7842ed4..48f376820 100644 --- a/src/Brain/BrainOpenGLViewportContent.cxx +++ b/src/Brain/BrainOpenGLViewportContent.cxx @@ -654,21 +654,21 @@ BrainOpenGLViewportContent::createViewportContentForTileTabs(std::vectorgetRow(iRowFromTop)->getContentType(); + const TileTabsGridRowColumnContentTypeEnum::Enum rowContentType = tileTabsConfiguration->getRow(iRowFromTop)->getContentType(); switch (rowContentType) { - case TileTabsRowColumnContentTypeEnum::SPACE: + case TileTabsGridRowColumnContentTypeEnum::SPACE: spacerTabFlag = true; break; - case TileTabsRowColumnContentTypeEnum::TAB: + case TileTabsGridRowColumnContentTypeEnum::TAB: break; } - const TileTabsRowColumnContentTypeEnum::Enum tabContentType = tileTabsConfiguration->getColumn(jCol)->getContentType(); + const TileTabsGridRowColumnContentTypeEnum::Enum tabContentType = tileTabsConfiguration->getColumn(jCol)->getContentType(); switch (tabContentType) { - case TileTabsRowColumnContentTypeEnum::SPACE: + case TileTabsGridRowColumnContentTypeEnum::SPACE: spacerTabFlag = true; break; - case TileTabsRowColumnContentTypeEnum::TAB: + case TileTabsGridRowColumnContentTypeEnum::TAB: break; } diff --git a/src/Brain/BrowserWindowContent.cxx b/src/Brain/BrowserWindowContent.cxx index 3463f7c53..8c097401f 100644 --- a/src/Brain/BrowserWindowContent.cxx +++ b/src/Brain/BrowserWindowContent.cxx @@ -58,7 +58,7 @@ m_windowIndex(windowIndex) m_sceneAssistant->add("m_windowAspectLockedRatio", &m_windowAspectLockedRatio); m_sceneAssistant->add("m_allTabsInWindowAspectRatioLocked", &m_allTabsInWindowAspectRatioLocked); m_sceneAssistant->add("m_tileTabsEnabled", &m_tileTabsEnabled); - m_sceneAssistant->add("m_tileTabsConfigurationMode", + m_sceneAssistant->add("m_tileTabsConfigurationMode", &m_tileTabsConfigurationMode); m_sceneAssistant->add("m_sceneGraphicsWidth", &m_sceneGraphicsWidth); m_sceneAssistant->add("m_sceneGraphicsHeight", &m_sceneGraphicsHeight); @@ -108,7 +108,7 @@ BrowserWindowContent::reset() m_tileTabsEnabled = false; m_sceneGraphicsHeight = 0; m_sceneGraphicsWidth = 0; - m_tileTabsConfigurationMode = TileTabsConfigurationModeEnum::AUTOMATIC; + m_tileTabsConfigurationMode = TileTabsGridModeEnum::AUTOMATIC; m_automaticTileTabsConfiguration->updateAutomaticConfigurationRowsAndColumns(1); /* sets rows/columns/factors to defaults */ m_customTileTabsConfiguration->updateAutomaticConfigurationRowsAndColumns(1); @@ -222,10 +222,10 @@ BrowserWindowContent::getSelectedTileTabsConfiguration() TileTabsConfiguration* configMode = NULL; switch (m_tileTabsConfigurationMode) { - case TileTabsConfigurationModeEnum::AUTOMATIC: + case TileTabsGridModeEnum::AUTOMATIC: configMode = m_automaticTileTabsConfiguration.get(); break; - case TileTabsConfigurationModeEnum::CUSTOM: + case TileTabsGridModeEnum::CUSTOM: configMode = m_customTileTabsConfiguration.get(); break; } @@ -245,10 +245,10 @@ BrowserWindowContent::getSelectedTileTabsConfiguration() const TileTabsConfiguration* configMode = NULL; switch (m_tileTabsConfigurationMode) { - case TileTabsConfigurationModeEnum::AUTOMATIC: + case TileTabsGridModeEnum::AUTOMATIC: configMode = m_automaticTileTabsConfiguration.get(); break; - case TileTabsConfigurationModeEnum::CUSTOM: + case TileTabsGridModeEnum::CUSTOM: configMode = m_customTileTabsConfiguration.get(); break; } @@ -296,7 +296,7 @@ BrowserWindowContent::getCustomTileTabsConfiguration() const /** * @return The tile tabs configuration mode. */ -TileTabsConfigurationModeEnum::Enum +TileTabsGridModeEnum::Enum BrowserWindowContent::getTileTabsConfigurationMode() const { return m_tileTabsConfigurationMode; @@ -309,7 +309,7 @@ BrowserWindowContent::getTileTabsConfigurationMode() const * New value for configuration mode. */ void -BrowserWindowContent::setTileTabsConfigurationMode(const TileTabsConfigurationModeEnum::Enum configMode) +BrowserWindowContent::setTileTabsConfigurationMode(const TileTabsGridModeEnum::Enum configMode) { m_tileTabsConfigurationMode = configMode; } @@ -548,10 +548,10 @@ BrowserWindowContent::restoreFromScene(const SceneAttributes* sceneAttributes, if (oldTileTabsAutoPrimitive != NULL) { const bool autoModeSelected = oldTileTabsAutoPrimitive->booleanValue(); if (autoModeSelected) { - m_tileTabsConfigurationMode = TileTabsConfigurationModeEnum::AUTOMATIC; + m_tileTabsConfigurationMode = TileTabsGridModeEnum::AUTOMATIC; } else { - m_tileTabsConfigurationMode = TileTabsConfigurationModeEnum::CUSTOM; + m_tileTabsConfigurationMode = TileTabsGridModeEnum::CUSTOM; } } @@ -561,10 +561,10 @@ BrowserWindowContent::restoreFromScene(const SceneAttributes* sceneAttributes, * If tile tabs was enabled, use CUSTOM, otherwise AUTOMATIC */ if (m_tileTabsEnabled) { - m_tileTabsConfigurationMode = TileTabsConfigurationModeEnum::CUSTOM; + m_tileTabsConfigurationMode = TileTabsGridModeEnum::CUSTOM; } else { - m_tileTabsConfigurationMode = TileTabsConfigurationModeEnum::AUTOMATIC; + m_tileTabsConfigurationMode = TileTabsGridModeEnum::AUTOMATIC; } } @@ -650,10 +650,10 @@ BrowserWindowContent::restoreFromOldBrainBrowserWindowScene(const SceneAttribute * If tile tabs was enabled, use CUSTOM, otherwise AUTOMATIC */ if (m_tileTabsEnabled) { - m_tileTabsConfigurationMode = TileTabsConfigurationModeEnum::CUSTOM; + m_tileTabsConfigurationMode = TileTabsGridModeEnum::CUSTOM; } else { - m_tileTabsConfigurationMode = TileTabsConfigurationModeEnum::AUTOMATIC; + m_tileTabsConfigurationMode = TileTabsGridModeEnum::AUTOMATIC; } } diff --git a/src/Brain/BrowserWindowContent.h b/src/Brain/BrowserWindowContent.h index 9b4c5763a..06567a3a7 100644 --- a/src/Brain/BrowserWindowContent.h +++ b/src/Brain/BrowserWindowContent.h @@ -28,7 +28,7 @@ #include "CaretObject.h" #include "SceneableInterface.h" -#include "TileTabsConfigurationModeEnum.h" +#include "TileTabsGridModeEnum.h" namespace caret { class SceneClassAssistant; @@ -77,9 +77,9 @@ namespace caret { const TileTabsConfiguration* getCustomTileTabsConfiguration() const; - TileTabsConfigurationModeEnum::Enum getTileTabsConfigurationMode() const; + TileTabsGridModeEnum::Enum getTileTabsConfigurationMode() const; - void setTileTabsConfigurationMode(const TileTabsConfigurationModeEnum::Enum configMode); + void setTileTabsConfigurationMode(const TileTabsGridModeEnum::Enum configMode); int32_t getSceneGraphicsWidth() const; @@ -142,7 +142,7 @@ namespace caret { bool m_tileTabsEnabled = false; - TileTabsConfigurationModeEnum::Enum m_tileTabsConfigurationMode = TileTabsConfigurationModeEnum::AUTOMATIC; + TileTabsGridModeEnum::Enum m_tileTabsConfigurationMode = TileTabsGridModeEnum::AUTOMATIC; int32_t m_sceneGraphicsWidth = 0; diff --git a/src/Common/CMakeLists.txt b/src/Common/CMakeLists.txt index a1f410e20..cf43445d8 100644 --- a/src/Common/CMakeLists.txt +++ b/src/Common/CMakeLists.txt @@ -153,10 +153,10 @@ StringTableModel.h StructureEnum.h SystemUtilities.h TileTabsConfiguration.h -TileTabsConfigurationModeEnum.h -TileTabsRowColumnContentTypeEnum.h -TileTabsRowColumnElement.h -TileTabsRowColumnStretchTypeEnum.h +TileTabsGridModeEnum.h +TileTabsGridRowColumnContentTypeEnum.h +TileTabsGridRowColumnElement.h +TileTabsGridRowColumnStretchTypeEnum.h TracksModificationInterface.h TriStateSelectionStatusEnum.h Vector3D.h @@ -275,10 +275,10 @@ StringTableModel.cxx StructureEnum.cxx SystemUtilities.cxx TileTabsConfiguration.cxx -TileTabsConfigurationModeEnum.cxx -TileTabsRowColumnContentTypeEnum.cxx -TileTabsRowColumnElement.cxx -TileTabsRowColumnStretchTypeEnum.cxx +TileTabsGridModeEnum.cxx +TileTabsGridRowColumnContentTypeEnum.cxx +TileTabsGridRowColumnElement.cxx +TileTabsGridRowColumnStretchTypeEnum.cxx TriStateSelectionStatusEnum.cxx Vector3D.cxx VectorOperation.cxx diff --git a/src/Common/TileTabsConfiguration.cxx b/src/Common/TileTabsConfiguration.cxx index 79c773921..01db9f5cb 100644 --- a/src/Common/TileTabsConfiguration.cxx +++ b/src/Common/TileTabsConfiguration.cxx @@ -162,7 +162,7 @@ TileTabsConfiguration::copy(const TileTabsConfiguration& rhs) * * @param Information for element. */ -TileTabsRowColumnElement* +TileTabsGridRowColumnElement* TileTabsConfiguration::getColumn(const int32_t columnIndex) { CaretAssertVectorIndex(m_columns, columnIndex); @@ -174,7 +174,7 @@ TileTabsConfiguration::getColumn(const int32_t columnIndex) * * @param Information for element. */ -const TileTabsRowColumnElement* +const TileTabsGridRowColumnElement* TileTabsConfiguration::getColumn(const int32_t columnIndex) const { CaretAssertVectorIndex(m_columns, columnIndex); @@ -186,7 +186,7 @@ TileTabsConfiguration::getColumn(const int32_t columnIndex) const * * @param Information for element. */ -TileTabsRowColumnElement* +TileTabsGridRowColumnElement* TileTabsConfiguration::getRow(const int32_t rowIndex) { CaretAssertVectorIndex(m_rows, rowIndex); @@ -198,7 +198,7 @@ TileTabsConfiguration::getRow(const int32_t rowIndex) * * @param Information for element. */ -const TileTabsRowColumnElement* +const TileTabsGridRowColumnElement* TileTabsConfiguration::getRow(const int32_t rowIndex) const { CaretAssertVectorIndex(m_rows, rowIndex); @@ -228,7 +228,7 @@ bool TileTabsConfiguration::getRowHeightsAndColumnWidthsForWindowSize(const int32_t windowWidth, const int32_t windowHeight, const int32_t numberOfModelsToDraw, - const TileTabsConfigurationModeEnum::Enum configurationMode, + const TileTabsGridModeEnum::Enum configurationMode, std::vector& rowHeightsOut, std::vector& columnWidthsOut) { @@ -247,7 +247,7 @@ TileTabsConfiguration::getRowHeightsAndColumnWidthsForWindowSize(const int32_t w columnWidthsOut.clear(); switch (configurationMode) { - case TileTabsConfigurationModeEnum::AUTOMATIC: + case TileTabsGridModeEnum::AUTOMATIC: { /* * Update number of rows/columns in the default configuration @@ -265,7 +265,7 @@ TileTabsConfiguration::getRowHeightsAndColumnWidthsForWindowSize(const int32_t w columnWidthsOut.push_back(windowWidth / numCols); } } break; - case TileTabsConfigurationModeEnum::CUSTOM: + case TileTabsGridModeEnum::CUSTOM: { /* * Rows/columns from user configuration @@ -279,12 +279,12 @@ TileTabsConfiguration::getRowHeightsAndColumnWidthsForWindowSize(const int32_t w float rowPercentTotal = 0.0; float rowStretchTotal = 0.0; for (int32_t i = 0; i < numRows; i++) { - const TileTabsRowColumnElement* e = getRow(i); + const TileTabsGridRowColumnElement* e = getRow(i); switch (e->getStretchType()) { - case TileTabsRowColumnStretchTypeEnum::PERCENT: + case TileTabsGridRowColumnStretchTypeEnum::PERCENT: rowPercentTotal += (e->getPercentStretch() / 100.0); break; - case TileTabsRowColumnStretchTypeEnum::WEIGHT: + case TileTabsGridRowColumnStretchTypeEnum::WEIGHT: rowStretchTotal += e->getWeightStretch(); break; } @@ -301,12 +301,12 @@ TileTabsConfiguration::getRowHeightsAndColumnWidthsForWindowSize(const int32_t w } for (int32_t i = 0; i < numRows; i++) { int32_t h = 0; - const TileTabsRowColumnElement* e = getRow(i); + const TileTabsGridRowColumnElement* e = getRow(i); switch (e->getStretchType()) { - case TileTabsRowColumnStretchTypeEnum::PERCENT: + case TileTabsGridRowColumnStretchTypeEnum::PERCENT: h = (e->getPercentStretch() / 100.0) * windowHeight; break; - case TileTabsRowColumnStretchTypeEnum::WEIGHT: + case TileTabsGridRowColumnStretchTypeEnum::WEIGHT: if (rowStretchTotal > 0.0) { h = static_cast((e->getWeightStretch() / rowStretchTotal) * windowWeightHeight); @@ -323,12 +323,12 @@ TileTabsConfiguration::getRowHeightsAndColumnWidthsForWindowSize(const int32_t w float columnPercentTotal = 0.0; float columnStretchTotal = 0.0; for (int32_t i = 0; i < numCols; i++) { - const TileTabsRowColumnElement* e = getColumn(i); + const TileTabsGridRowColumnElement* e = getColumn(i); switch (e->getStretchType()) { - case TileTabsRowColumnStretchTypeEnum::PERCENT: + case TileTabsGridRowColumnStretchTypeEnum::PERCENT: columnPercentTotal += (e->getPercentStretch() / 100.0); break; - case TileTabsRowColumnStretchTypeEnum::WEIGHT: + case TileTabsGridRowColumnStretchTypeEnum::WEIGHT: columnStretchTotal += e->getWeightStretch(); break; } @@ -346,12 +346,12 @@ TileTabsConfiguration::getRowHeightsAndColumnWidthsForWindowSize(const int32_t w for (int32_t i = 0; i < numCols; i++) { int32_t w = 0; - const TileTabsRowColumnElement* e = getColumn(i); + const TileTabsGridRowColumnElement* e = getColumn(i); switch (e->getStretchType()) { - case TileTabsRowColumnStretchTypeEnum::PERCENT: + case TileTabsGridRowColumnStretchTypeEnum::PERCENT: w = (e->getPercentStretch() / 100.0) * windowWidth; break; - case TileTabsRowColumnStretchTypeEnum::WEIGHT: + case TileTabsGridRowColumnStretchTypeEnum::WEIGHT: if (columnStretchTotal > 0.0) { w = static_cast((e->getWeightStretch() / columnStretchTotal) * windowWeightWidth); @@ -669,14 +669,14 @@ TileTabsConfiguration::encodeInXMLWithStreamWriterVersionTwo() const void TileTabsConfiguration::encodeRowColumnElement(QXmlStreamWriter& writer, const AString tagName, - const std::vector& elements) const + const std::vector& elements) const { writer.writeStartElement(tagName); for (const auto e : elements) { writer.writeStartElement(s_v2_elementTagName); - writer.writeAttribute(s_v2_contentTypeAttributeName, TileTabsRowColumnContentTypeEnum::toName(e.getContentType())); - writer.writeAttribute(s_v2_stretchTypeAttributeName, TileTabsRowColumnStretchTypeEnum::toName(e.getStretchType())); + writer.writeAttribute(s_v2_contentTypeAttributeName, TileTabsGridRowColumnContentTypeEnum::toName(e.getContentType())); + writer.writeAttribute(s_v2_stretchTypeAttributeName, TileTabsGridRowColumnStretchTypeEnum::toName(e.getStretchType())); writer.writeAttribute(s_v2_percentStretchAttributeName, AString::number(e.getPercentStretch(), 'f', 2)); writer.writeAttribute(s_v2_weightStretchAttributeName, AString::number(e.getWeightStretch(), 'f', 2)); writer.writeEndElement(); @@ -879,21 +879,21 @@ TileTabsConfiguration::decodeFromXMLWithStreamReaderVersionOne(QXmlStreamReader& m_columns.clear(); for (int32_t i = 0; i < numberOfRows; i++) { - TileTabsRowColumnElement element; + TileTabsGridRowColumnElement element; CaretAssertVectorIndex(rowStretchFactors, i); element.setWeightStretch(rowStretchFactors[i]); - element.setContentType(TileTabsRowColumnContentTypeEnum::TAB); - element.setStretchType(TileTabsRowColumnStretchTypeEnum::WEIGHT); + element.setContentType(TileTabsGridRowColumnContentTypeEnum::TAB); + element.setStretchType(TileTabsGridRowColumnStretchTypeEnum::WEIGHT); m_rows.push_back(element); } CaretAssert(numberOfRows == static_cast(m_rows.size())); for (int32_t i = 0; i < numberOfColumns; i++) { - TileTabsRowColumnElement element; + TileTabsGridRowColumnElement element; CaretAssertVectorIndex(columnStretchFactors, i); element.setWeightStretch(columnStretchFactors[i]); - element.setContentType(TileTabsRowColumnContentTypeEnum::TAB); - element.setStretchType(TileTabsRowColumnStretchTypeEnum::WEIGHT); + element.setContentType(TileTabsGridRowColumnContentTypeEnum::TAB); + element.setStretchType(TileTabsGridRowColumnStretchTypeEnum::WEIGHT); m_columns.push_back(element); } CaretAssert(numberOfColumns == static_cast(m_columns.size())); @@ -961,7 +961,7 @@ TileTabsConfiguration::decodeFromXMLWithStreamReaderVersionTwo(QXmlStreamReader& case ReadMode::COLUMNS: { AString errorMessage; - TileTabsRowColumnElement e; + TileTabsGridRowColumnElement e; if (decodeRowColumnElement(xml, e, errorMessage)) { m_columns.push_back(e); } @@ -973,7 +973,7 @@ TileTabsConfiguration::decodeFromXMLWithStreamReaderVersionTwo(QXmlStreamReader& case ReadMode::ROWS: { AString errorMessage; - TileTabsRowColumnElement e; + TileTabsGridRowColumnElement e; if (decodeRowColumnElement(xml, e, errorMessage)) { m_rows.push_back(e); } @@ -1054,7 +1054,7 @@ TileTabsConfiguration::decodeFromXMLWithStreamReaderVersionTwo(QXmlStreamReader& */ bool TileTabsConfiguration::decodeRowColumnElement(QXmlStreamReader& reader, - TileTabsRowColumnElement& element, + TileTabsGridRowColumnElement& element, AString& errorMessageOut) { const QXmlStreamAttributes atts = reader.attributes(); @@ -1064,7 +1064,7 @@ TileTabsConfiguration::decodeRowColumnElement(QXmlStreamReader& reader, if (atts.hasAttribute(s_v2_contentTypeAttributeName)) { bool validFlag(false); const AString s = atts.value(s_v2_contentTypeAttributeName).toString(); - element.setContentType(TileTabsRowColumnContentTypeEnum::fromName(s, &validFlag)); + element.setContentType(TileTabsGridRowColumnContentTypeEnum::fromName(s, &validFlag)); if ( ! validFlag) { errorMessageOut.append("Content type \"" + s + "\" is not valid. "); } @@ -1076,7 +1076,7 @@ TileTabsConfiguration::decodeRowColumnElement(QXmlStreamReader& reader, if (atts.hasAttribute(s_v2_stretchTypeAttributeName)) { bool validFlag(false); const AString s = atts.value(s_v2_stretchTypeAttributeName).toString(); - element.setStretchType(TileTabsRowColumnStretchTypeEnum::fromName(s, &validFlag)); + element.setStretchType(TileTabsGridRowColumnStretchTypeEnum::fromName(s, &validFlag)); if ( ! validFlag) { errorMessageOut.append("Stretch type \"" + s + "\" is not valid. "); } diff --git a/src/Common/TileTabsConfiguration.h b/src/Common/TileTabsConfiguration.h index a28bf5792..87661333f 100644 --- a/src/Common/TileTabsConfiguration.h +++ b/src/Common/TileTabsConfiguration.h @@ -23,8 +23,8 @@ #include "CaretException.h" #include "CaretObject.h" -#include "TileTabsConfigurationModeEnum.h" -#include "TileTabsRowColumnElement.h" +#include "TileTabsGridModeEnum.h" +#include "TileTabsGridRowColumnElement.h" class QXmlStreamReader; class QXmlStreamWriter; @@ -49,7 +49,7 @@ namespace caret { bool getRowHeightsAndColumnWidthsForWindowSize(const int32_t windowWidth, const int32_t windowHeight, const int32_t numberOfModelsToDraw, - const TileTabsConfigurationModeEnum::Enum configurationMode, + const TileTabsGridModeEnum::Enum configurationMode, std::vector& rowHeightsOut, std::vector& columnWidthsOut); @@ -67,13 +67,13 @@ namespace caret { void setNumberOfColumns(const int32_t numberOfColumns); - TileTabsRowColumnElement* getColumn(const int32_t columnIndex); + TileTabsGridRowColumnElement* getColumn(const int32_t columnIndex); - const TileTabsRowColumnElement* getColumn(const int32_t columnIndex) const; + const TileTabsGridRowColumnElement* getColumn(const int32_t columnIndex) const; - TileTabsRowColumnElement* getRow(const int32_t rowIndex); + TileTabsGridRowColumnElement* getRow(const int32_t rowIndex); - const TileTabsRowColumnElement* getRow(const int32_t rowIndex) const; + const TileTabsGridRowColumnElement* getRow(const int32_t rowIndex) const; AString encodeInXML() const; @@ -115,10 +115,10 @@ namespace caret { void encodeRowColumnElement(QXmlStreamWriter& writer, const AString tagName, - const std::vector& elements) const; + const std::vector& elements) const; bool decodeRowColumnElement(QXmlStreamReader& reader, - TileTabsRowColumnElement& element, + TileTabsGridRowColumnElement& element, AString& errorMessageOut); void initialize(); @@ -130,9 +130,9 @@ namespace caret { /** Unique identifier does not get copied */ AString m_uniqueIdentifier; - std::vector m_columns; + std::vector m_columns; - std::vector m_rows; + std::vector m_rows; bool m_centeringCorrectionEnabled = false; diff --git a/src/Common/TileTabsConfigurationModeEnum.cxx b/src/Common/TileTabsGridModeEnum.cxx similarity index 64% rename from src/Common/TileTabsConfigurationModeEnum.cxx rename to src/Common/TileTabsGridModeEnum.cxx index ace7e4b90..15547e3df 100644 --- a/src/Common/TileTabsConfigurationModeEnum.cxx +++ b/src/Common/TileTabsGridModeEnum.cxx @@ -20,9 +20,9 @@ /*LICENSE_END*/ #include -#define __TILE_TABS_CONFIGURATION_MODE_ENUM_DECLARE__ -#include "TileTabsConfigurationModeEnum.h" -#undef __TILE_TABS_CONFIGURATION_MODE_ENUM_DECLARE__ +#define __TILE_TABS_GRID_MODE_ENUM_DECLARE__ +#include "TileTabsGridModeEnum.h" +#undef __TILE_TABS_GRID_MODE_ENUM_DECLARE__ #include "CaretAssert.h" @@ -30,8 +30,8 @@ using namespace caret; /** - * \class caret::TileTabsConfigurationModeEnum - * \brief Enumerated type for Tile Tab Configuration Types + * \class caret::TileTabsGridModeEnum + * \brief Enumerated type for Grid Tile Tab Configuration Mode (auto, custom) * * Using this enumerated type in the GUI with an EnumComboBoxTemplate * @@ -40,30 +40,30 @@ using namespace caret; * class EnumComboBoxTemplate; * * Declare the member: - * EnumComboBoxTemplate* m_tileTabsConfigurationModeEnumComboBox; + * EnumComboBoxTemplate* m_TileTabsGridModeEnumComboBox; * * Declare a slot that is called when user changes selection * private slots: - * void tileTabsConfigurationModeEnumComboBoxItemActivated(); + * void TileTabsGridModeEnumComboBoxItemActivated(); * * Implementation File (.cxx) * Include the header files * #include "EnumComboBoxTemplate.h" - * #include "TileTabsConfigurationModeEnum.h" + * #include "TileTabsGridModeEnum.h" * * Instatiate: - * m_tileTabsConfigurationModeEnumComboBox = new EnumComboBoxTemplate(this); - * m_tileTabsConfigurationModeEnumComboBox->setup(); + * m_TileTabsGridModeEnumComboBox = new EnumComboBoxTemplate(this); + * m_TileTabsGridModeEnumComboBox->setup(); * * Get notified when the user changes the selection: - * QObject::connect(m_tileTabsConfigurationModeEnumComboBox, SIGNAL(itemActivated()), - * this, SLOT(tileTabsConfigurationModeEnumComboBoxItemActivated())); + * QObject::connect(m_TileTabsGridModeEnumComboBox, SIGNAL(itemActivated()), + * this, SLOT(TileTabsGridModeEnumComboBoxItemActivated())); * * Update the selection: - * m_tileTabsConfigurationModeEnumComboBox->setSelectedItem(NEW_VALUE); + * m_TileTabsGridModeEnumComboBox->setSelectedItem(NEW_VALUE); * * Read the selection: - * const TileTabsConfigurationModeEnum::Enum VARIABLE = m_tileTabsConfigurationModeEnumComboBox->getSelectedItem(); + * const TileTabsGridModeEnum::Enum VARIABLE = m_TileTabsGridModeEnumComboBox->getSelectedItem(); * */ @@ -78,7 +78,7 @@ using namespace caret; * @param guiName * User-friendly name for use in user-interface. */ -TileTabsConfigurationModeEnum::TileTabsConfigurationModeEnum(const Enum enumValue, +TileTabsGridModeEnum::TileTabsGridModeEnum(const Enum enumValue, const AString& name, const AString& guiName) { @@ -91,7 +91,7 @@ TileTabsConfigurationModeEnum::TileTabsConfigurationModeEnum(const Enum enumValu /** * Destructor. */ -TileTabsConfigurationModeEnum::~TileTabsConfigurationModeEnum() +TileTabsGridModeEnum::~TileTabsGridModeEnum() { } @@ -99,18 +99,18 @@ TileTabsConfigurationModeEnum::~TileTabsConfigurationModeEnum() * Initialize the enumerated metadata. */ void -TileTabsConfigurationModeEnum::initialize() +TileTabsGridModeEnum::initialize() { if (initializedFlag) { return; } initializedFlag = true; - enumData.push_back(TileTabsConfigurationModeEnum(AUTOMATIC, + enumData.push_back(TileTabsGridModeEnum(AUTOMATIC, "AUTOMATIC", "Automatic")); - enumData.push_back(TileTabsConfigurationModeEnum(CUSTOM, + enumData.push_back(TileTabsGridModeEnum(CUSTOM, "CUSTOM", "Custom")); @@ -123,14 +123,14 @@ TileTabsConfigurationModeEnum::initialize() * @return Pointer to data for this enumerated type * or NULL if no data for type or if type is invalid. */ -const TileTabsConfigurationModeEnum* -TileTabsConfigurationModeEnum::findData(const Enum enumValue) +const TileTabsGridModeEnum* +TileTabsGridModeEnum::findData(const Enum enumValue) { if (initializedFlag == false) initialize(); size_t num = enumData.size(); for (size_t i = 0; i < num; i++) { - const TileTabsConfigurationModeEnum* d = &enumData[i]; + const TileTabsGridModeEnum* d = &enumData[i]; if (d->enumValue == enumValue) { return d; } @@ -147,10 +147,10 @@ TileTabsConfigurationModeEnum::findData(const Enum enumValue) * String representing enumerated value. */ AString -TileTabsConfigurationModeEnum::toName(Enum enumValue) { +TileTabsGridModeEnum::toName(Enum enumValue) { if (initializedFlag == false) initialize(); - const TileTabsConfigurationModeEnum* enumInstance = findData(enumValue); + const TileTabsGridModeEnum* enumInstance = findData(enumValue); return enumInstance->name; } @@ -164,18 +164,18 @@ TileTabsConfigurationModeEnum::toName(Enum enumValue) { * @return * Enumerated value. */ -TileTabsConfigurationModeEnum::Enum -TileTabsConfigurationModeEnum::fromName(const AString& name, bool* isValidOut) +TileTabsGridModeEnum::Enum +TileTabsGridModeEnum::fromName(const AString& name, bool* isValidOut) { if (initializedFlag == false) initialize(); bool validFlag = false; - Enum enumValue = TileTabsConfigurationModeEnum::enumData[0].enumValue; + Enum enumValue = TileTabsGridModeEnum::enumData[0].enumValue; - for (std::vector::iterator iter = enumData.begin(); + for (std::vector::iterator iter = enumData.begin(); iter != enumData.end(); iter++) { - const TileTabsConfigurationModeEnum& d = *iter; + const TileTabsGridModeEnum& d = *iter; if (d.name == name) { enumValue = d.enumValue; validFlag = true; @@ -187,7 +187,7 @@ TileTabsConfigurationModeEnum::fromName(const AString& name, bool* isValidOut) *isValidOut = validFlag; } else if (validFlag == false) { - CaretAssertMessage(0, AString("Name " + name + "failed to match enumerated value for type TileTabsConfigurationModeEnum")); + CaretAssertMessage(0, AString("Name " + name + "failed to match enumerated value for type TileTabsGridModeEnum")); } return enumValue; } @@ -200,10 +200,10 @@ TileTabsConfigurationModeEnum::fromName(const AString& name, bool* isValidOut) * String representing enumerated value. */ AString -TileTabsConfigurationModeEnum::toGuiName(Enum enumValue) { +TileTabsGridModeEnum::toGuiName(Enum enumValue) { if (initializedFlag == false) initialize(); - const TileTabsConfigurationModeEnum* enumInstance = findData(enumValue); + const TileTabsGridModeEnum* enumInstance = findData(enumValue); return enumInstance->guiName; } @@ -217,18 +217,18 @@ TileTabsConfigurationModeEnum::toGuiName(Enum enumValue) { * @return * Enumerated value. */ -TileTabsConfigurationModeEnum::Enum -TileTabsConfigurationModeEnum::fromGuiName(const AString& guiName, bool* isValidOut) +TileTabsGridModeEnum::Enum +TileTabsGridModeEnum::fromGuiName(const AString& guiName, bool* isValidOut) { if (initializedFlag == false) initialize(); bool validFlag = false; - Enum enumValue = TileTabsConfigurationModeEnum::enumData[0].enumValue; + Enum enumValue = TileTabsGridModeEnum::enumData[0].enumValue; - for (std::vector::iterator iter = enumData.begin(); + for (std::vector::iterator iter = enumData.begin(); iter != enumData.end(); iter++) { - const TileTabsConfigurationModeEnum& d = *iter; + const TileTabsGridModeEnum& d = *iter; if (d.guiName == guiName) { enumValue = d.enumValue; validFlag = true; @@ -240,7 +240,7 @@ TileTabsConfigurationModeEnum::fromGuiName(const AString& guiName, bool* isValid *isValidOut = validFlag; } else if (validFlag == false) { - CaretAssertMessage(0, AString("guiName " + guiName + "failed to match enumerated value for type TileTabsConfigurationModeEnum")); + CaretAssertMessage(0, AString("guiName " + guiName + "failed to match enumerated value for type TileTabsGridModeEnum")); } return enumValue; } @@ -252,10 +252,10 @@ TileTabsConfigurationModeEnum::fromGuiName(const AString& guiName, bool* isValid * Integer code for data type. */ int32_t -TileTabsConfigurationModeEnum::toIntegerCode(Enum enumValue) +TileTabsGridModeEnum::toIntegerCode(Enum enumValue) { if (initializedFlag == false) initialize(); - const TileTabsConfigurationModeEnum* enumInstance = findData(enumValue); + const TileTabsGridModeEnum* enumInstance = findData(enumValue); return enumInstance->integerCode; } @@ -270,18 +270,18 @@ TileTabsConfigurationModeEnum::toIntegerCode(Enum enumValue) * @return * Enum for integer code. */ -TileTabsConfigurationModeEnum::Enum -TileTabsConfigurationModeEnum::fromIntegerCode(const int32_t integerCode, bool* isValidOut) +TileTabsGridModeEnum::Enum +TileTabsGridModeEnum::fromIntegerCode(const int32_t integerCode, bool* isValidOut) { if (initializedFlag == false) initialize(); bool validFlag = false; - Enum enumValue = TileTabsConfigurationModeEnum::enumData[0].enumValue; + Enum enumValue = TileTabsGridModeEnum::enumData[0].enumValue; - for (std::vector::iterator iter = enumData.begin(); + for (std::vector::iterator iter = enumData.begin(); iter != enumData.end(); iter++) { - const TileTabsConfigurationModeEnum& enumInstance = *iter; + const TileTabsGridModeEnum& enumInstance = *iter; if (enumInstance.integerCode == integerCode) { enumValue = enumInstance.enumValue; validFlag = true; @@ -293,7 +293,7 @@ TileTabsConfigurationModeEnum::fromIntegerCode(const int32_t integerCode, bool* *isValidOut = validFlag; } else if (validFlag == false) { - CaretAssertMessage(0, AString("Integer code " + AString::number(integerCode) + "failed to match enumerated value for type TileTabsConfigurationModeEnum")); + CaretAssertMessage(0, AString("Integer code " + AString::number(integerCode) + "failed to match enumerated value for type TileTabsGridModeEnum")); } return enumValue; } @@ -306,13 +306,13 @@ TileTabsConfigurationModeEnum::fromIntegerCode(const int32_t integerCode, bool* * A vector that is OUTPUT containing all of the enumerated values. */ void -TileTabsConfigurationModeEnum::getAllEnums(std::vector& allEnums) +TileTabsGridModeEnum::getAllEnums(std::vector& allEnums) { if (initializedFlag == false) initialize(); allEnums.clear(); - for (std::vector::iterator iter = enumData.begin(); + for (std::vector::iterator iter = enumData.begin(); iter != enumData.end(); iter++) { allEnums.push_back(iter->enumValue); @@ -328,16 +328,16 @@ TileTabsConfigurationModeEnum::getAllEnums(std::vector& allNames, const bool isSorted) +TileTabsGridModeEnum::getAllNames(std::vector& allNames, const bool isSorted) { if (initializedFlag == false) initialize(); allNames.clear(); - for (std::vector::iterator iter = enumData.begin(); + for (std::vector::iterator iter = enumData.begin(); iter != enumData.end(); iter++) { - allNames.push_back(TileTabsConfigurationModeEnum::toName(iter->enumValue)); + allNames.push_back(TileTabsGridModeEnum::toName(iter->enumValue)); } if (isSorted) { @@ -354,16 +354,16 @@ TileTabsConfigurationModeEnum::getAllNames(std::vector& allNames, const * If true, the names are sorted in alphabetical order. */ void -TileTabsConfigurationModeEnum::getAllGuiNames(std::vector& allGuiNames, const bool isSorted) +TileTabsGridModeEnum::getAllGuiNames(std::vector& allGuiNames, const bool isSorted) { if (initializedFlag == false) initialize(); allGuiNames.clear(); - for (std::vector::iterator iter = enumData.begin(); + for (std::vector::iterator iter = enumData.begin(); iter != enumData.end(); iter++) { - allGuiNames.push_back(TileTabsConfigurationModeEnum::toGuiName(iter->enumValue)); + allGuiNames.push_back(TileTabsGridModeEnum::toGuiName(iter->enumValue)); } if (isSorted) { diff --git a/src/Common/TileTabsConfigurationModeEnum.h b/src/Common/TileTabsGridModeEnum.h similarity index 77% rename from src/Common/TileTabsConfigurationModeEnum.h rename to src/Common/TileTabsGridModeEnum.h index 11704af4f..4478b4fb3 100644 --- a/src/Common/TileTabsConfigurationModeEnum.h +++ b/src/Common/TileTabsGridModeEnum.h @@ -1,5 +1,5 @@ -#ifndef __TILE_TABS_CONFIGURATION_MODE_ENUM_H__ -#define __TILE_TABS_CONFIGURATION_MODE_ENUM_H__ +#ifndef __TILE_TABS_GRID_MODE_ENUM_H__ +#define __TILE_TABS_GRID_MODE_ENUM_H__ /*LICENSE_START*/ /* @@ -28,7 +28,7 @@ namespace caret { -class TileTabsConfigurationModeEnum { +class TileTabsGridModeEnum { public: /** @@ -42,7 +42,7 @@ public: }; - ~TileTabsConfigurationModeEnum(); + ~TileTabsGridModeEnum(); static AString toName(Enum enumValue); @@ -63,14 +63,14 @@ public: static void getAllGuiNames(std::vector& allGuiNames, const bool isSorted); private: - TileTabsConfigurationModeEnum(const Enum enumValue, + TileTabsGridModeEnum(const Enum enumValue, const AString& name, const AString& guiName); - static const TileTabsConfigurationModeEnum* findData(const Enum enumValue); + static const TileTabsGridModeEnum* findData(const Enum enumValue); /** Holds all instance of enum values and associated metadata */ - static std::vector enumData; + static std::vector enumData; /** Initialize instances that contain the enum values and metadata */ static void initialize(); @@ -94,11 +94,11 @@ private: AString guiName; }; -#ifdef __TILE_TABS_CONFIGURATION_MODE_ENUM_DECLARE__ -std::vector TileTabsConfigurationModeEnum::enumData; -bool TileTabsConfigurationModeEnum::initializedFlag = false; -int32_t TileTabsConfigurationModeEnum::integerCodeCounter = 0; -#endif // __TILE_TABS_CONFIGURATION_MODE_ENUM_DECLARE__ +#ifdef __TILE_TABS_GRID_MODE_ENUM_DECLARE__ +std::vector TileTabsGridModeEnum::enumData; +bool TileTabsGridModeEnum::initializedFlag = false; +int32_t TileTabsGridModeEnum::integerCodeCounter = 0; +#endif // __TILE_TABS_GRID_MODE_ENUM_DECLARE__ } // namespace -#endif //__TILE_TABS_CONFIGURATION_MODE_ENUM_H__ +#endif //__TILE_TABS_GRID_MODE_ENUM_H__ diff --git a/src/Common/TileTabsRowColumnContentTypeEnum.cxx b/src/Common/TileTabsGridRowColumnContentTypeEnum.cxx similarity index 61% rename from src/Common/TileTabsRowColumnContentTypeEnum.cxx rename to src/Common/TileTabsGridRowColumnContentTypeEnum.cxx index 5b8327f79..4008e22ec 100644 --- a/src/Common/TileTabsRowColumnContentTypeEnum.cxx +++ b/src/Common/TileTabsGridRowColumnContentTypeEnum.cxx @@ -20,9 +20,9 @@ /*LICENSE_END*/ #include -#define __TILE_TABS_ROW_COLUMN_CONTENT_TYPE_ENUM_DECLARE__ -#include "TileTabsRowColumnContentTypeEnum.h" -#undef __TILE_TABS_ROW_COLUMN_CONTENT_TYPE_ENUM_DECLARE__ +#define __TILE_TABS_GRID_ROW_COLUMN_CONTENT_TYPE_ENUM_DECLARE__ +#include "TileTabsGridRowColumnContentTypeEnum.h" +#undef __TILE_TABS_GRID_ROW_COLUMN_CONTENT_TYPE_ENUM_DECLARE__ #include "CaretAssert.h" @@ -30,8 +30,8 @@ using namespace caret; /** - * \class caret::TileTabsRowColumnContentTypeEnum - * \brief Content type for a row or column in a tile tabs + * \class caret::TileTabsGridRowColumnContentTypeEnum + * \brief Content type for a row or column in a tile tabs grid * * Using this enumerated type in the GUI with an EnumComboBoxTemplate * @@ -40,30 +40,30 @@ using namespace caret; * class EnumComboBoxTemplate; * * Declare the member: - * EnumComboBoxTemplate* m_tileTabsRowColumnContentTypeEnumComboBox; + * EnumComboBoxTemplate* m_TileTabsGridRowColumnContentTypeEnumComboBox; * * Declare a slot that is called when user changes selection * private slots: - * void tileTabsRowColumnContentTypeEnumComboBoxItemActivated(); + * void TileTabsGridRowColumnContentTypeEnumComboBoxItemActivated(); * * Implementation File (.cxx) * Include the header files * #include "EnumComboBoxTemplate.h" - * #include "TileTabsRowColumnContentTypeEnum.h" + * #include "TileTabsGridRowColumnContentTypeEnum.h" * * Instatiate: - * m_tileTabsRowColumnContentTypeEnumComboBox = new EnumComboBoxTemplate(this); - * m_tileTabsRowColumnContentTypeEnumComboBox->setup(); + * m_TileTabsGridRowColumnContentTypeEnumComboBox = new EnumComboBoxTemplate(this); + * m_TileTabsGridRowColumnContentTypeEnumComboBox->setup(); * * Get notified when the user changes the selection: - * QObject::connect(m_tileTabsRowColumnContentTypeEnumComboBox, SIGNAL(itemActivated()), - * this, SLOT(tileTabsRowColumnContentTypeEnumComboBoxItemActivated())); + * QObject::connect(m_TileTabsGridRowColumnContentTypeEnumComboBox, SIGNAL(itemActivated()), + * this, SLOT(TileTabsGridRowColumnContentTypeEnumComboBoxItemActivated())); * * Update the selection: - * m_tileTabsRowColumnContentTypeEnumComboBox->setSelectedItem(NEW_VALUE); + * m_TileTabsGridRowColumnContentTypeEnumComboBox->setSelectedItem(NEW_VALUE); * * Read the selection: - * const TileTabsRowColumnContentTypeEnum::Enum VARIABLE = m_tileTabsRowColumnContentTypeEnumComboBox->getSelectedItem(); + * const TileTabsGridRowColumnContentTypeEnum::Enum VARIABLE = m_TileTabsGridRowColumnContentTypeEnumComboBox->getSelectedItem(); * */ @@ -78,7 +78,7 @@ using namespace caret; * @param guiName * User-friendly name for use in user-interface. */ -TileTabsRowColumnContentTypeEnum::TileTabsRowColumnContentTypeEnum(const Enum enumValue, +TileTabsGridRowColumnContentTypeEnum::TileTabsGridRowColumnContentTypeEnum(const Enum enumValue, const AString& name, const AString& guiName) { @@ -91,7 +91,7 @@ TileTabsRowColumnContentTypeEnum::TileTabsRowColumnContentTypeEnum(const Enum en /** * Destructor. */ -TileTabsRowColumnContentTypeEnum::~TileTabsRowColumnContentTypeEnum() +TileTabsGridRowColumnContentTypeEnum::~TileTabsGridRowColumnContentTypeEnum() { } @@ -99,18 +99,18 @@ TileTabsRowColumnContentTypeEnum::~TileTabsRowColumnContentTypeEnum() * Initialize the enumerated metadata. */ void -TileTabsRowColumnContentTypeEnum::initialize() +TileTabsGridRowColumnContentTypeEnum::initialize() { if (initializedFlag) { return; } initializedFlag = true; - enumData.push_back(TileTabsRowColumnContentTypeEnum(SPACE, + enumData.push_back(TileTabsGridRowColumnContentTypeEnum(SPACE, "SPACE", "Space")); - enumData.push_back(TileTabsRowColumnContentTypeEnum(TAB, + enumData.push_back(TileTabsGridRowColumnContentTypeEnum(TAB, "TAB", "Tab")); @@ -123,14 +123,14 @@ TileTabsRowColumnContentTypeEnum::initialize() * @return Pointer to data for this enumerated type * or NULL if no data for type or if type is invalid. */ -const TileTabsRowColumnContentTypeEnum* -TileTabsRowColumnContentTypeEnum::findData(const Enum enumValue) +const TileTabsGridRowColumnContentTypeEnum* +TileTabsGridRowColumnContentTypeEnum::findData(const Enum enumValue) { if (initializedFlag == false) initialize(); size_t num = enumData.size(); for (size_t i = 0; i < num; i++) { - const TileTabsRowColumnContentTypeEnum* d = &enumData[i]; + const TileTabsGridRowColumnContentTypeEnum* d = &enumData[i]; if (d->enumValue == enumValue) { return d; } @@ -147,10 +147,10 @@ TileTabsRowColumnContentTypeEnum::findData(const Enum enumValue) * String representing enumerated value. */ AString -TileTabsRowColumnContentTypeEnum::toName(Enum enumValue) { +TileTabsGridRowColumnContentTypeEnum::toName(Enum enumValue) { if (initializedFlag == false) initialize(); - const TileTabsRowColumnContentTypeEnum* enumInstance = findData(enumValue); + const TileTabsGridRowColumnContentTypeEnum* enumInstance = findData(enumValue); return enumInstance->name; } @@ -164,18 +164,18 @@ TileTabsRowColumnContentTypeEnum::toName(Enum enumValue) { * @return * Enumerated value. */ -TileTabsRowColumnContentTypeEnum::Enum -TileTabsRowColumnContentTypeEnum::fromName(const AString& name, bool* isValidOut) +TileTabsGridRowColumnContentTypeEnum::Enum +TileTabsGridRowColumnContentTypeEnum::fromName(const AString& name, bool* isValidOut) { if (initializedFlag == false) initialize(); bool validFlag = false; - Enum enumValue = TileTabsRowColumnContentTypeEnum::enumData[0].enumValue; + Enum enumValue = TileTabsGridRowColumnContentTypeEnum::enumData[0].enumValue; - for (std::vector::iterator iter = enumData.begin(); + for (std::vector::iterator iter = enumData.begin(); iter != enumData.end(); iter++) { - const TileTabsRowColumnContentTypeEnum& d = *iter; + const TileTabsGridRowColumnContentTypeEnum& d = *iter; if (d.name == name) { enumValue = d.enumValue; validFlag = true; @@ -187,7 +187,7 @@ TileTabsRowColumnContentTypeEnum::fromName(const AString& name, bool* isValidOut *isValidOut = validFlag; } else if (validFlag == false) { - CaretAssertMessage(0, AString("Name " + name + "failed to match enumerated value for type TileTabsRowColumnContentTypeEnum")); + CaretAssertMessage(0, AString("Name " + name + "failed to match enumerated value for type TileTabsGridRowColumnContentTypeEnum")); } return enumValue; } @@ -200,10 +200,10 @@ TileTabsRowColumnContentTypeEnum::fromName(const AString& name, bool* isValidOut * String representing enumerated value. */ AString -TileTabsRowColumnContentTypeEnum::toGuiName(Enum enumValue) { +TileTabsGridRowColumnContentTypeEnum::toGuiName(Enum enumValue) { if (initializedFlag == false) initialize(); - const TileTabsRowColumnContentTypeEnum* enumInstance = findData(enumValue); + const TileTabsGridRowColumnContentTypeEnum* enumInstance = findData(enumValue); return enumInstance->guiName; } @@ -217,18 +217,18 @@ TileTabsRowColumnContentTypeEnum::toGuiName(Enum enumValue) { * @return * Enumerated value. */ -TileTabsRowColumnContentTypeEnum::Enum -TileTabsRowColumnContentTypeEnum::fromGuiName(const AString& guiName, bool* isValidOut) +TileTabsGridRowColumnContentTypeEnum::Enum +TileTabsGridRowColumnContentTypeEnum::fromGuiName(const AString& guiName, bool* isValidOut) { if (initializedFlag == false) initialize(); bool validFlag = false; - Enum enumValue = TileTabsRowColumnContentTypeEnum::enumData[0].enumValue; + Enum enumValue = TileTabsGridRowColumnContentTypeEnum::enumData[0].enumValue; - for (std::vector::iterator iter = enumData.begin(); + for (std::vector::iterator iter = enumData.begin(); iter != enumData.end(); iter++) { - const TileTabsRowColumnContentTypeEnum& d = *iter; + const TileTabsGridRowColumnContentTypeEnum& d = *iter; if (d.guiName == guiName) { enumValue = d.enumValue; validFlag = true; @@ -240,7 +240,7 @@ TileTabsRowColumnContentTypeEnum::fromGuiName(const AString& guiName, bool* isVa *isValidOut = validFlag; } else if (validFlag == false) { - CaretAssertMessage(0, AString("guiName " + guiName + "failed to match enumerated value for type TileTabsRowColumnContentTypeEnum")); + CaretAssertMessage(0, AString("guiName " + guiName + "failed to match enumerated value for type TileTabsGridRowColumnContentTypeEnum")); } return enumValue; } @@ -252,10 +252,10 @@ TileTabsRowColumnContentTypeEnum::fromGuiName(const AString& guiName, bool* isVa * Integer code for data type. */ int32_t -TileTabsRowColumnContentTypeEnum::toIntegerCode(Enum enumValue) +TileTabsGridRowColumnContentTypeEnum::toIntegerCode(Enum enumValue) { if (initializedFlag == false) initialize(); - const TileTabsRowColumnContentTypeEnum* enumInstance = findData(enumValue); + const TileTabsGridRowColumnContentTypeEnum* enumInstance = findData(enumValue); return enumInstance->integerCode; } @@ -270,18 +270,18 @@ TileTabsRowColumnContentTypeEnum::toIntegerCode(Enum enumValue) * @return * Enum for integer code. */ -TileTabsRowColumnContentTypeEnum::Enum -TileTabsRowColumnContentTypeEnum::fromIntegerCode(const int32_t integerCode, bool* isValidOut) +TileTabsGridRowColumnContentTypeEnum::Enum +TileTabsGridRowColumnContentTypeEnum::fromIntegerCode(const int32_t integerCode, bool* isValidOut) { if (initializedFlag == false) initialize(); bool validFlag = false; - Enum enumValue = TileTabsRowColumnContentTypeEnum::enumData[0].enumValue; + Enum enumValue = TileTabsGridRowColumnContentTypeEnum::enumData[0].enumValue; - for (std::vector::iterator iter = enumData.begin(); + for (std::vector::iterator iter = enumData.begin(); iter != enumData.end(); iter++) { - const TileTabsRowColumnContentTypeEnum& enumInstance = *iter; + const TileTabsGridRowColumnContentTypeEnum& enumInstance = *iter; if (enumInstance.integerCode == integerCode) { enumValue = enumInstance.enumValue; validFlag = true; @@ -293,7 +293,7 @@ TileTabsRowColumnContentTypeEnum::fromIntegerCode(const int32_t integerCode, boo *isValidOut = validFlag; } else if (validFlag == false) { - CaretAssertMessage(0, AString("Integer code " + AString::number(integerCode) + "failed to match enumerated value for type TileTabsRowColumnContentTypeEnum")); + CaretAssertMessage(0, AString("Integer code " + AString::number(integerCode) + "failed to match enumerated value for type TileTabsGridRowColumnContentTypeEnum")); } return enumValue; } @@ -306,13 +306,13 @@ TileTabsRowColumnContentTypeEnum::fromIntegerCode(const int32_t integerCode, boo * A vector that is OUTPUT containing all of the enumerated values. */ void -TileTabsRowColumnContentTypeEnum::getAllEnums(std::vector& allEnums) +TileTabsGridRowColumnContentTypeEnum::getAllEnums(std::vector& allEnums) { if (initializedFlag == false) initialize(); allEnums.clear(); - for (std::vector::iterator iter = enumData.begin(); + for (std::vector::iterator iter = enumData.begin(); iter != enumData.end(); iter++) { allEnums.push_back(iter->enumValue); @@ -328,16 +328,16 @@ TileTabsRowColumnContentTypeEnum::getAllEnums(std::vector& allNames, const bool isSorted) +TileTabsGridRowColumnContentTypeEnum::getAllNames(std::vector& allNames, const bool isSorted) { if (initializedFlag == false) initialize(); allNames.clear(); - for (std::vector::iterator iter = enumData.begin(); + for (std::vector::iterator iter = enumData.begin(); iter != enumData.end(); iter++) { - allNames.push_back(TileTabsRowColumnContentTypeEnum::toName(iter->enumValue)); + allNames.push_back(TileTabsGridRowColumnContentTypeEnum::toName(iter->enumValue)); } if (isSorted) { @@ -354,16 +354,16 @@ TileTabsRowColumnContentTypeEnum::getAllNames(std::vector& allNames, co * If true, the names are sorted in alphabetical order. */ void -TileTabsRowColumnContentTypeEnum::getAllGuiNames(std::vector& allGuiNames, const bool isSorted) +TileTabsGridRowColumnContentTypeEnum::getAllGuiNames(std::vector& allGuiNames, const bool isSorted) { if (initializedFlag == false) initialize(); allGuiNames.clear(); - for (std::vector::iterator iter = enumData.begin(); + for (std::vector::iterator iter = enumData.begin(); iter != enumData.end(); iter++) { - allGuiNames.push_back(TileTabsRowColumnContentTypeEnum::toGuiName(iter->enumValue)); + allGuiNames.push_back(TileTabsGridRowColumnContentTypeEnum::toGuiName(iter->enumValue)); } if (isSorted) { diff --git a/src/Common/TileTabsRowColumnContentTypeEnum.h b/src/Common/TileTabsGridRowColumnContentTypeEnum.h similarity index 74% rename from src/Common/TileTabsRowColumnContentTypeEnum.h rename to src/Common/TileTabsGridRowColumnContentTypeEnum.h index e1e25d3f5..fabf3fc9a 100644 --- a/src/Common/TileTabsRowColumnContentTypeEnum.h +++ b/src/Common/TileTabsGridRowColumnContentTypeEnum.h @@ -1,5 +1,5 @@ -#ifndef __TILE_TABS_ROW_COLUMN_CONTENT_TYPE_ENUM_H__ -#define __TILE_TABS_ROW_COLUMN_CONTENT_TYPE_ENUM_H__ +#ifndef __TILE_TABS_GRID_ROW_COLUMN_CONTENT_TYPE_ENUM_H__ +#define __TILE_TABS_GRID_ROW_COLUMN_CONTENT_TYPE_ENUM_H__ /*LICENSE_START*/ /* @@ -28,7 +28,7 @@ namespace caret { -class TileTabsRowColumnContentTypeEnum { +class TileTabsGridRowColumnContentTypeEnum { public: /** @@ -42,7 +42,7 @@ public: }; - ~TileTabsRowColumnContentTypeEnum(); + ~TileTabsGridRowColumnContentTypeEnum(); static AString toName(Enum enumValue); @@ -63,14 +63,14 @@ public: static void getAllGuiNames(std::vector& allGuiNames, const bool isSorted); private: - TileTabsRowColumnContentTypeEnum(const Enum enumValue, + TileTabsGridRowColumnContentTypeEnum(const Enum enumValue, const AString& name, const AString& guiName); - static const TileTabsRowColumnContentTypeEnum* findData(const Enum enumValue); + static const TileTabsGridRowColumnContentTypeEnum* findData(const Enum enumValue); /** Holds all instance of enum values and associated metadata */ - static std::vector enumData; + static std::vector enumData; /** Initialize instances that contain the enum values and metadata */ static void initialize(); @@ -94,11 +94,11 @@ private: AString guiName; }; -#ifdef __TILE_TABS_ROW_COLUMN_CONTENT_TYPE_ENUM_DECLARE__ -std::vector TileTabsRowColumnContentTypeEnum::enumData; -bool TileTabsRowColumnContentTypeEnum::initializedFlag = false; -int32_t TileTabsRowColumnContentTypeEnum::integerCodeCounter = 0; -#endif // __TILE_TABS_ROW_COLUMN_CONTENT_TYPE_ENUM_DECLARE__ +#ifdef __TILE_TABS_GRID_ROW_COLUMN_CONTENT_TYPE_ENUM_DECLARE__ +std::vector TileTabsGridRowColumnContentTypeEnum::enumData; +bool TileTabsGridRowColumnContentTypeEnum::initializedFlag = false; +int32_t TileTabsGridRowColumnContentTypeEnum::integerCodeCounter = 0; +#endif // __TILE_TABS_GRID_ROW_COLUMN_CONTENT_TYPE_ENUM_DECLARE__ } // namespace -#endif //__TILE_TABS_ROW_COLUMN_CONTENT_TYPE_ENUM_H__ +#endif //__TILE_TABS_GRID_ROW_COLUMN_CONTENT_TYPE_ENUM_H__ diff --git a/src/Common/TileTabsRowColumnElement.cxx b/src/Common/TileTabsGridRowColumnElement.cxx similarity index 59% rename from src/Common/TileTabsRowColumnElement.cxx rename to src/Common/TileTabsGridRowColumnElement.cxx index 7a761106a..4c7573524 100644 --- a/src/Common/TileTabsRowColumnElement.cxx +++ b/src/Common/TileTabsGridRowColumnElement.cxx @@ -19,9 +19,9 @@ */ /*LICENSE_END*/ -#define __TILE_TABS_ROW_COLUMN_ELEMENT_DECLARE__ -#include "TileTabsRowColumnElement.h" -#undef __TILE_TABS_ROW_COLUMN_ELEMENT_DECLARE__ +#define __TILE_TABS_GRID_ROW_COLUMN_ELEMENT_DECLARE__ +#include "TileTabsGridRowColumnElement.h" +#undef __TILE_TABS_GRID_ROW_COLUMN_ELEMENT_DECLARE__ #include "CaretAssert.h" using namespace caret; @@ -29,15 +29,15 @@ using namespace caret; /** - * \class caret::TileTabsRowColumnElement - * \brief Contents on an element in a Tile Tabs Configuration row or column. + * \class caret::TileTabsGridRowColumnElement + * \brief Contents on an element in a Tile Tabs Configuration grid row or column. * \ingroup Common */ /** * Constructor. */ -TileTabsRowColumnElement::TileTabsRowColumnElement() +TileTabsGridRowColumnElement::TileTabsGridRowColumnElement() : CaretObject() { clear(); @@ -46,7 +46,7 @@ TileTabsRowColumnElement::TileTabsRowColumnElement() /** * Destructor. */ -TileTabsRowColumnElement::~TileTabsRowColumnElement() +TileTabsGridRowColumnElement::~TileTabsGridRowColumnElement() { } @@ -55,10 +55,10 @@ TileTabsRowColumnElement::~TileTabsRowColumnElement() * @param obj * Object that is copied. */ -TileTabsRowColumnElement::TileTabsRowColumnElement(const TileTabsRowColumnElement& obj) +TileTabsGridRowColumnElement::TileTabsGridRowColumnElement(const TileTabsGridRowColumnElement& obj) : CaretObject(obj) { - this->copyHelperTileTabsRowColumnElement(obj); + this->copyHelperTileTabsGridRowColumnElement(obj); } /** @@ -68,12 +68,12 @@ TileTabsRowColumnElement::TileTabsRowColumnElement(const TileTabsRowColumnElemen * @return * Reference to this object. */ -TileTabsRowColumnElement& -TileTabsRowColumnElement::operator=(const TileTabsRowColumnElement& obj) +TileTabsGridRowColumnElement& +TileTabsGridRowColumnElement::operator=(const TileTabsGridRowColumnElement& obj) { if (this != &obj) { CaretObject::operator=(obj); - this->copyHelperTileTabsRowColumnElement(obj); + this->copyHelperTileTabsGridRowColumnElement(obj); } return *this; } @@ -84,7 +84,7 @@ TileTabsRowColumnElement::operator=(const TileTabsRowColumnElement& obj) * Object that is copied. */ void -TileTabsRowColumnElement::copyHelperTileTabsRowColumnElement(const TileTabsRowColumnElement& obj) +TileTabsGridRowColumnElement::copyHelperTileTabsGridRowColumnElement(const TileTabsGridRowColumnElement& obj) { m_contentType = obj.m_contentType; m_stretchType = obj.m_stretchType; @@ -96,10 +96,10 @@ TileTabsRowColumnElement::copyHelperTileTabsRowColumnElement(const TileTabsRowCo * Clear this instance. */ void -TileTabsRowColumnElement::clear() +TileTabsGridRowColumnElement::clear() { - m_contentType = TileTabsRowColumnContentTypeEnum::TAB; - m_stretchType = TileTabsRowColumnStretchTypeEnum::WEIGHT; + m_contentType = TileTabsGridRowColumnContentTypeEnum::TAB; + m_stretchType = TileTabsGridRowColumnStretchTypeEnum::WEIGHT; m_percentStretch = 20.0; m_weightStretch = 1.0; } @@ -107,8 +107,8 @@ TileTabsRowColumnElement::clear() /** * @return Content type (spacer or tab) */ -TileTabsRowColumnContentTypeEnum::Enum -TileTabsRowColumnElement::getContentType() const +TileTabsGridRowColumnContentTypeEnum::Enum +TileTabsGridRowColumnElement::getContentType() const { return m_contentType; } @@ -120,7 +120,7 @@ TileTabsRowColumnElement::getContentType() const * New value for content type. */ void -TileTabsRowColumnElement::setContentType(const TileTabsRowColumnContentTypeEnum::Enum contentType) +TileTabsGridRowColumnElement::setContentType(const TileTabsGridRowColumnContentTypeEnum::Enum contentType) { m_contentType = contentType; } @@ -128,8 +128,8 @@ TileTabsRowColumnElement::setContentType(const TileTabsRowColumnContentTypeEnum: /** * @return The stretch type (percent or weight) */ -TileTabsRowColumnStretchTypeEnum::Enum -TileTabsRowColumnElement::getStretchType() const +TileTabsGridRowColumnStretchTypeEnum::Enum +TileTabsGridRowColumnElement::getStretchType() const { return m_stretchType; } @@ -141,7 +141,7 @@ TileTabsRowColumnElement::getStretchType() const * New value for stretch type. */ void -TileTabsRowColumnElement::setStretchType(const TileTabsRowColumnStretchTypeEnum::Enum stretchType) +TileTabsGridRowColumnElement::setStretchType(const TileTabsGridRowColumnStretchTypeEnum::Enum stretchType) { m_stretchType = stretchType; } @@ -150,7 +150,7 @@ TileTabsRowColumnElement::setStretchType(const TileTabsRowColumnStretchTypeEnum: * @return The percent stretch value. */ float -TileTabsRowColumnElement::getPercentStretch() const +TileTabsGridRowColumnElement::getPercentStretch() const { return m_percentStretch; } @@ -162,7 +162,7 @@ TileTabsRowColumnElement::getPercentStretch() const * New value for percent stretch. */ void -TileTabsRowColumnElement::setPercentStretch(const float percentStretch) +TileTabsGridRowColumnElement::setPercentStretch(const float percentStretch) { m_percentStretch = percentStretch; } @@ -171,7 +171,7 @@ TileTabsRowColumnElement::setPercentStretch(const float percentStretch) * @return The weight stretch value. */ float -TileTabsRowColumnElement::getWeightStretch() const +TileTabsGridRowColumnElement::getWeightStretch() const { return m_weightStretch; } @@ -183,7 +183,7 @@ TileTabsRowColumnElement::getWeightStretch() const * New value for weight stretch. */ void -TileTabsRowColumnElement::setWeightStretch(const float weightStretch) +TileTabsGridRowColumnElement::setWeightStretch(const float weightStretch) { m_weightStretch = weightStretch; } @@ -194,11 +194,11 @@ TileTabsRowColumnElement::setWeightStretch(const float weightStretch) * @return String describing this object's content. */ AString -TileTabsRowColumnElement::toString() const +TileTabsGridRowColumnElement::toString() const { - AString s("TileTabsRowColumnElement: "); - s.append("ContentType=" + TileTabsRowColumnContentTypeEnum::toGuiName(m_contentType)); - s.append(" StretchType=" + TileTabsRowColumnStretchTypeEnum::toGuiName(m_stretchType)); + AString s("TileTabsGridRowColumnElement: "); + s.append("ContentType=" + TileTabsGridRowColumnContentTypeEnum::toGuiName(m_contentType)); + s.append(" StretchType=" + TileTabsGridRowColumnStretchTypeEnum::toGuiName(m_stretchType)); s.append(" PercentStretch=" + AString::number(m_percentStretch)); s.append(" WeightStretch=" + AString::number(m_weightStretch)); return s; diff --git a/src/Common/TileTabsRowColumnElement.h b/src/Common/TileTabsGridRowColumnElement.h similarity index 54% rename from src/Common/TileTabsRowColumnElement.h rename to src/Common/TileTabsGridRowColumnElement.h index 9b7158bdb..5cb80c183 100644 --- a/src/Common/TileTabsRowColumnElement.h +++ b/src/Common/TileTabsGridRowColumnElement.h @@ -1,5 +1,5 @@ -#ifndef __TILE_TABS_ROW_COLUMN_ELEMENT_H__ -#define __TILE_TABS_ROW_COLUMN_ELEMENT_H__ +#ifndef __TILE_TABS_GRID_ROW_COLUMN_ELEMENT_H__ +#define __TILE_TABS_GRID_ROW_COLUMN_ELEMENT_H__ /*LICENSE_START*/ /* @@ -27,31 +27,31 @@ #include "CaretObject.h" -#include "TileTabsRowColumnContentTypeEnum.h" -#include "TileTabsRowColumnStretchTypeEnum.h" +#include "TileTabsGridRowColumnContentTypeEnum.h" +#include "TileTabsGridRowColumnStretchTypeEnum.h" namespace caret { - class TileTabsRowColumnElement : public CaretObject { + class TileTabsGridRowColumnElement : public CaretObject { public: - TileTabsRowColumnElement(); + TileTabsGridRowColumnElement(); - virtual ~TileTabsRowColumnElement(); + virtual ~TileTabsGridRowColumnElement(); - TileTabsRowColumnElement(const TileTabsRowColumnElement& obj); + TileTabsGridRowColumnElement(const TileTabsGridRowColumnElement& obj); - TileTabsRowColumnElement& operator=(const TileTabsRowColumnElement& obj); + TileTabsGridRowColumnElement& operator=(const TileTabsGridRowColumnElement& obj); void clear(); - TileTabsRowColumnContentTypeEnum::Enum getContentType() const; + TileTabsGridRowColumnContentTypeEnum::Enum getContentType() const; - void setContentType(const TileTabsRowColumnContentTypeEnum::Enum contentType); + void setContentType(const TileTabsGridRowColumnContentTypeEnum::Enum contentType); - TileTabsRowColumnStretchTypeEnum::Enum getStretchType() const; + TileTabsGridRowColumnStretchTypeEnum::Enum getStretchType() const; - void setStretchType(const TileTabsRowColumnStretchTypeEnum::Enum stretchType); + void setStretchType(const TileTabsGridRowColumnStretchTypeEnum::Enum stretchType); float getPercentStretch() const; @@ -66,11 +66,11 @@ namespace caret { virtual AString toString() const; private: - void copyHelperTileTabsRowColumnElement(const TileTabsRowColumnElement& obj); + void copyHelperTileTabsGridRowColumnElement(const TileTabsGridRowColumnElement& obj); - TileTabsRowColumnContentTypeEnum::Enum m_contentType = TileTabsRowColumnContentTypeEnum::TAB; + TileTabsGridRowColumnContentTypeEnum::Enum m_contentType = TileTabsGridRowColumnContentTypeEnum::TAB; - TileTabsRowColumnStretchTypeEnum::Enum m_stretchType = TileTabsRowColumnStretchTypeEnum::WEIGHT; + TileTabsGridRowColumnStretchTypeEnum::Enum m_stretchType = TileTabsGridRowColumnStretchTypeEnum::WEIGHT; float m_percentStretch = 20.0; @@ -80,9 +80,9 @@ namespace caret { }; -#ifdef __TILE_TABS_ROW_COLUMN_ELEMENT_DECLARE__ +#ifdef __TILE_TABS_GRID_ROW_COLUMN_ELEMENT_DECLARE__ // -#endif // __TILE_TABS_ROW_COLUMN_ELEMENT_DECLARE__ +#endif // __TILE_TABS_GRID_ROW_COLUMN_ELEMENT_DECLARE__ } // namespace -#endif //__TILE_TABS_ROW_COLUMN_ELEMENT_H__ +#endif //__TILE_TABS_GRID_ROW_COLUMN_ELEMENT_H__ diff --git a/src/Common/TileTabsRowColumnStretchTypeEnum.cxx b/src/Common/TileTabsGridRowColumnStretchTypeEnum.cxx similarity index 61% rename from src/Common/TileTabsRowColumnStretchTypeEnum.cxx rename to src/Common/TileTabsGridRowColumnStretchTypeEnum.cxx index 4e6b16162..156938d1d 100644 --- a/src/Common/TileTabsRowColumnStretchTypeEnum.cxx +++ b/src/Common/TileTabsGridRowColumnStretchTypeEnum.cxx @@ -20,9 +20,9 @@ /*LICENSE_END*/ #include -#define __TILE_TABS_ROW_COLUMN_STRETCH_TYPE_ENUM_DECLARE__ -#include "TileTabsRowColumnStretchTypeEnum.h" -#undef __TILE_TABS_ROW_COLUMN_STRETCH_TYPE_ENUM_DECLARE__ +#define __TILE_TABS_GRID_ROW_COLUMN_STRETCH_TYPE_ENUM_DECLARE__ +#include "TileTabsGridRowColumnStretchTypeEnum.h" +#undef __TILE_TABS_GRID_ROW_COLUMN_STRETCH_TYPE_ENUM_DECLARE__ #include "CaretAssert.h" @@ -30,8 +30,8 @@ using namespace caret; /** - * \class caret::TileTabsRowColumnStretchTypeEnum - * \brief Stretch type for tile tabs configuration. + * \class caret::TileTabsGridRowColumnStretchTypeEnum + * \brief Stretch type for tile tabs grid configuration. * * Using this enumerated type in the GUI with an EnumComboBoxTemplate * @@ -40,30 +40,30 @@ using namespace caret; * class EnumComboBoxTemplate; * * Declare the member: - * EnumComboBoxTemplate* m_tileTabsRowColumnStretchTypeEnumComboBox; + * EnumComboBoxTemplate* m_TileTabsGridRowColumnStretchTypeEnumComboBox; * * Declare a slot that is called when user changes selection * private slots: - * void tileTabsRowColumnStretchTypeEnumComboBoxItemActivated(); + * void TileTabsGridRowColumnStretchTypeEnumComboBoxItemActivated(); * * Implementation File (.cxx) * Include the header files * #include "EnumComboBoxTemplate.h" - * #include "TileTabsRowColumnStretchTypeEnum.h" + * #include "TileTabsGridRowColumnStretchTypeEnum.h" * * Instatiate: - * m_tileTabsRowColumnStretchTypeEnumComboBox = new EnumComboBoxTemplate(this); - * m_tileTabsRowColumnStretchTypeEnumComboBox->setup(); + * m_TileTabsGridRowColumnStretchTypeEnumComboBox = new EnumComboBoxTemplate(this); + * m_TileTabsGridRowColumnStretchTypeEnumComboBox->setup(); * * Get notified when the user changes the selection: - * QObject::connect(m_tileTabsRowColumnStretchTypeEnumComboBox, SIGNAL(itemActivated()), - * this, SLOT(tileTabsRowColumnStretchTypeEnumComboBoxItemActivated())); + * QObject::connect(m_TileTabsGridRowColumnStretchTypeEnumComboBox, SIGNAL(itemActivated()), + * this, SLOT(TileTabsGridRowColumnStretchTypeEnumComboBoxItemActivated())); * * Update the selection: - * m_tileTabsRowColumnStretchTypeEnumComboBox->setSelectedItem(NEW_VALUE); + * m_TileTabsGridRowColumnStretchTypeEnumComboBox->setSelectedItem(NEW_VALUE); * * Read the selection: - * const TileTabsRowColumnStretchTypeEnum::Enum VARIABLE = m_tileTabsRowColumnStretchTypeEnumComboBox->getSelectedItem(); + * const TileTabsGridRowColumnStretchTypeEnum::Enum VARIABLE = m_TileTabsGridRowColumnStretchTypeEnumComboBox->getSelectedItem(); * */ @@ -78,7 +78,7 @@ using namespace caret; * @param guiName * User-friendly name for use in user-interface. */ -TileTabsRowColumnStretchTypeEnum::TileTabsRowColumnStretchTypeEnum(const Enum enumValue, +TileTabsGridRowColumnStretchTypeEnum::TileTabsGridRowColumnStretchTypeEnum(const Enum enumValue, const AString& name, const AString& guiName) { @@ -91,7 +91,7 @@ TileTabsRowColumnStretchTypeEnum::TileTabsRowColumnStretchTypeEnum(const Enum en /** * Destructor. */ -TileTabsRowColumnStretchTypeEnum::~TileTabsRowColumnStretchTypeEnum() +TileTabsGridRowColumnStretchTypeEnum::~TileTabsGridRowColumnStretchTypeEnum() { } @@ -99,18 +99,18 @@ TileTabsRowColumnStretchTypeEnum::~TileTabsRowColumnStretchTypeEnum() * Initialize the enumerated metadata. */ void -TileTabsRowColumnStretchTypeEnum::initialize() +TileTabsGridRowColumnStretchTypeEnum::initialize() { if (initializedFlag) { return; } initializedFlag = true; - enumData.push_back(TileTabsRowColumnStretchTypeEnum(PERCENT, + enumData.push_back(TileTabsGridRowColumnStretchTypeEnum(PERCENT, "PERCENT", "Percent")); - enumData.push_back(TileTabsRowColumnStretchTypeEnum(WEIGHT, + enumData.push_back(TileTabsGridRowColumnStretchTypeEnum(WEIGHT, "WEIGHT", "Weight")); @@ -123,14 +123,14 @@ TileTabsRowColumnStretchTypeEnum::initialize() * @return Pointer to data for this enumerated type * or NULL if no data for type or if type is invalid. */ -const TileTabsRowColumnStretchTypeEnum* -TileTabsRowColumnStretchTypeEnum::findData(const Enum enumValue) +const TileTabsGridRowColumnStretchTypeEnum* +TileTabsGridRowColumnStretchTypeEnum::findData(const Enum enumValue) { if (initializedFlag == false) initialize(); size_t num = enumData.size(); for (size_t i = 0; i < num; i++) { - const TileTabsRowColumnStretchTypeEnum* d = &enumData[i]; + const TileTabsGridRowColumnStretchTypeEnum* d = &enumData[i]; if (d->enumValue == enumValue) { return d; } @@ -147,10 +147,10 @@ TileTabsRowColumnStretchTypeEnum::findData(const Enum enumValue) * String representing enumerated value. */ AString -TileTabsRowColumnStretchTypeEnum::toName(Enum enumValue) { +TileTabsGridRowColumnStretchTypeEnum::toName(Enum enumValue) { if (initializedFlag == false) initialize(); - const TileTabsRowColumnStretchTypeEnum* enumInstance = findData(enumValue); + const TileTabsGridRowColumnStretchTypeEnum* enumInstance = findData(enumValue); return enumInstance->name; } @@ -164,18 +164,18 @@ TileTabsRowColumnStretchTypeEnum::toName(Enum enumValue) { * @return * Enumerated value. */ -TileTabsRowColumnStretchTypeEnum::Enum -TileTabsRowColumnStretchTypeEnum::fromName(const AString& name, bool* isValidOut) +TileTabsGridRowColumnStretchTypeEnum::Enum +TileTabsGridRowColumnStretchTypeEnum::fromName(const AString& name, bool* isValidOut) { if (initializedFlag == false) initialize(); bool validFlag = false; - Enum enumValue = TileTabsRowColumnStretchTypeEnum::enumData[0].enumValue; + Enum enumValue = TileTabsGridRowColumnStretchTypeEnum::enumData[0].enumValue; - for (std::vector::iterator iter = enumData.begin(); + for (std::vector::iterator iter = enumData.begin(); iter != enumData.end(); iter++) { - const TileTabsRowColumnStretchTypeEnum& d = *iter; + const TileTabsGridRowColumnStretchTypeEnum& d = *iter; if (d.name == name) { enumValue = d.enumValue; validFlag = true; @@ -187,7 +187,7 @@ TileTabsRowColumnStretchTypeEnum::fromName(const AString& name, bool* isValidOut *isValidOut = validFlag; } else if (validFlag == false) { - CaretAssertMessage(0, AString("Name " + name + "failed to match enumerated value for type TileTabsRowColumnStretchTypeEnum")); + CaretAssertMessage(0, AString("Name " + name + "failed to match enumerated value for type TileTabsGridRowColumnStretchTypeEnum")); } return enumValue; } @@ -200,10 +200,10 @@ TileTabsRowColumnStretchTypeEnum::fromName(const AString& name, bool* isValidOut * String representing enumerated value. */ AString -TileTabsRowColumnStretchTypeEnum::toGuiName(Enum enumValue) { +TileTabsGridRowColumnStretchTypeEnum::toGuiName(Enum enumValue) { if (initializedFlag == false) initialize(); - const TileTabsRowColumnStretchTypeEnum* enumInstance = findData(enumValue); + const TileTabsGridRowColumnStretchTypeEnum* enumInstance = findData(enumValue); return enumInstance->guiName; } @@ -217,18 +217,18 @@ TileTabsRowColumnStretchTypeEnum::toGuiName(Enum enumValue) { * @return * Enumerated value. */ -TileTabsRowColumnStretchTypeEnum::Enum -TileTabsRowColumnStretchTypeEnum::fromGuiName(const AString& guiName, bool* isValidOut) +TileTabsGridRowColumnStretchTypeEnum::Enum +TileTabsGridRowColumnStretchTypeEnum::fromGuiName(const AString& guiName, bool* isValidOut) { if (initializedFlag == false) initialize(); bool validFlag = false; - Enum enumValue = TileTabsRowColumnStretchTypeEnum::enumData[0].enumValue; + Enum enumValue = TileTabsGridRowColumnStretchTypeEnum::enumData[0].enumValue; - for (std::vector::iterator iter = enumData.begin(); + for (std::vector::iterator iter = enumData.begin(); iter != enumData.end(); iter++) { - const TileTabsRowColumnStretchTypeEnum& d = *iter; + const TileTabsGridRowColumnStretchTypeEnum& d = *iter; if (d.guiName == guiName) { enumValue = d.enumValue; validFlag = true; @@ -240,7 +240,7 @@ TileTabsRowColumnStretchTypeEnum::fromGuiName(const AString& guiName, bool* isVa *isValidOut = validFlag; } else if (validFlag == false) { - CaretAssertMessage(0, AString("guiName " + guiName + "failed to match enumerated value for type TileTabsRowColumnStretchTypeEnum")); + CaretAssertMessage(0, AString("guiName " + guiName + "failed to match enumerated value for type TileTabsGridRowColumnStretchTypeEnum")); } return enumValue; } @@ -252,10 +252,10 @@ TileTabsRowColumnStretchTypeEnum::fromGuiName(const AString& guiName, bool* isVa * Integer code for data type. */ int32_t -TileTabsRowColumnStretchTypeEnum::toIntegerCode(Enum enumValue) +TileTabsGridRowColumnStretchTypeEnum::toIntegerCode(Enum enumValue) { if (initializedFlag == false) initialize(); - const TileTabsRowColumnStretchTypeEnum* enumInstance = findData(enumValue); + const TileTabsGridRowColumnStretchTypeEnum* enumInstance = findData(enumValue); return enumInstance->integerCode; } @@ -270,18 +270,18 @@ TileTabsRowColumnStretchTypeEnum::toIntegerCode(Enum enumValue) * @return * Enum for integer code. */ -TileTabsRowColumnStretchTypeEnum::Enum -TileTabsRowColumnStretchTypeEnum::fromIntegerCode(const int32_t integerCode, bool* isValidOut) +TileTabsGridRowColumnStretchTypeEnum::Enum +TileTabsGridRowColumnStretchTypeEnum::fromIntegerCode(const int32_t integerCode, bool* isValidOut) { if (initializedFlag == false) initialize(); bool validFlag = false; - Enum enumValue = TileTabsRowColumnStretchTypeEnum::enumData[0].enumValue; + Enum enumValue = TileTabsGridRowColumnStretchTypeEnum::enumData[0].enumValue; - for (std::vector::iterator iter = enumData.begin(); + for (std::vector::iterator iter = enumData.begin(); iter != enumData.end(); iter++) { - const TileTabsRowColumnStretchTypeEnum& enumInstance = *iter; + const TileTabsGridRowColumnStretchTypeEnum& enumInstance = *iter; if (enumInstance.integerCode == integerCode) { enumValue = enumInstance.enumValue; validFlag = true; @@ -293,7 +293,7 @@ TileTabsRowColumnStretchTypeEnum::fromIntegerCode(const int32_t integerCode, boo *isValidOut = validFlag; } else if (validFlag == false) { - CaretAssertMessage(0, AString("Integer code " + AString::number(integerCode) + "failed to match enumerated value for type TileTabsRowColumnStretchTypeEnum")); + CaretAssertMessage(0, AString("Integer code " + AString::number(integerCode) + "failed to match enumerated value for type TileTabsGridRowColumnStretchTypeEnum")); } return enumValue; } @@ -306,13 +306,13 @@ TileTabsRowColumnStretchTypeEnum::fromIntegerCode(const int32_t integerCode, boo * A vector that is OUTPUT containing all of the enumerated values. */ void -TileTabsRowColumnStretchTypeEnum::getAllEnums(std::vector& allEnums) +TileTabsGridRowColumnStretchTypeEnum::getAllEnums(std::vector& allEnums) { if (initializedFlag == false) initialize(); allEnums.clear(); - for (std::vector::iterator iter = enumData.begin(); + for (std::vector::iterator iter = enumData.begin(); iter != enumData.end(); iter++) { allEnums.push_back(iter->enumValue); @@ -328,16 +328,16 @@ TileTabsRowColumnStretchTypeEnum::getAllEnums(std::vector& allNames, const bool isSorted) +TileTabsGridRowColumnStretchTypeEnum::getAllNames(std::vector& allNames, const bool isSorted) { if (initializedFlag == false) initialize(); allNames.clear(); - for (std::vector::iterator iter = enumData.begin(); + for (std::vector::iterator iter = enumData.begin(); iter != enumData.end(); iter++) { - allNames.push_back(TileTabsRowColumnStretchTypeEnum::toName(iter->enumValue)); + allNames.push_back(TileTabsGridRowColumnStretchTypeEnum::toName(iter->enumValue)); } if (isSorted) { @@ -354,16 +354,16 @@ TileTabsRowColumnStretchTypeEnum::getAllNames(std::vector& allNames, co * If true, the names are sorted in alphabetical order. */ void -TileTabsRowColumnStretchTypeEnum::getAllGuiNames(std::vector& allGuiNames, const bool isSorted) +TileTabsGridRowColumnStretchTypeEnum::getAllGuiNames(std::vector& allGuiNames, const bool isSorted) { if (initializedFlag == false) initialize(); allGuiNames.clear(); - for (std::vector::iterator iter = enumData.begin(); + for (std::vector::iterator iter = enumData.begin(); iter != enumData.end(); iter++) { - allGuiNames.push_back(TileTabsRowColumnStretchTypeEnum::toGuiName(iter->enumValue)); + allGuiNames.push_back(TileTabsGridRowColumnStretchTypeEnum::toGuiName(iter->enumValue)); } if (isSorted) { diff --git a/src/Common/TileTabsRowColumnStretchTypeEnum.h b/src/Common/TileTabsGridRowColumnStretchTypeEnum.h similarity index 74% rename from src/Common/TileTabsRowColumnStretchTypeEnum.h rename to src/Common/TileTabsGridRowColumnStretchTypeEnum.h index c99fb0cca..bdea3d730 100644 --- a/src/Common/TileTabsRowColumnStretchTypeEnum.h +++ b/src/Common/TileTabsGridRowColumnStretchTypeEnum.h @@ -1,5 +1,5 @@ -#ifndef __TILE_TABS_ROW_COLUMN_STRETCH_TYPE_ENUM_H__ -#define __TILE_TABS_ROW_COLUMN_STRETCH_TYPE_ENUM_H__ +#ifndef __TILE_TABS_GRID_ROW_COLUMN_STRETCH_TYPE_ENUM_H__ +#define __TILE_TABS_GRID_ROW_COLUMN_STRETCH_TYPE_ENUM_H__ /*LICENSE_START*/ /* @@ -28,7 +28,7 @@ namespace caret { -class TileTabsRowColumnStretchTypeEnum { +class TileTabsGridRowColumnStretchTypeEnum { public: /** @@ -42,7 +42,7 @@ public: }; - ~TileTabsRowColumnStretchTypeEnum(); + ~TileTabsGridRowColumnStretchTypeEnum(); static AString toName(Enum enumValue); @@ -63,14 +63,14 @@ public: static void getAllGuiNames(std::vector& allGuiNames, const bool isSorted); private: - TileTabsRowColumnStretchTypeEnum(const Enum enumValue, + TileTabsGridRowColumnStretchTypeEnum(const Enum enumValue, const AString& name, const AString& guiName); - static const TileTabsRowColumnStretchTypeEnum* findData(const Enum enumValue); + static const TileTabsGridRowColumnStretchTypeEnum* findData(const Enum enumValue); /** Holds all instance of enum values and associated metadata */ - static std::vector enumData; + static std::vector enumData; /** Initialize instances that contain the enum values and metadata */ static void initialize(); @@ -94,11 +94,11 @@ private: AString guiName; }; -#ifdef __TILE_TABS_ROW_COLUMN_STRETCH_TYPE_ENUM_DECLARE__ -std::vector TileTabsRowColumnStretchTypeEnum::enumData; -bool TileTabsRowColumnStretchTypeEnum::initializedFlag = false; -int32_t TileTabsRowColumnStretchTypeEnum::integerCodeCounter = 0; -#endif // __TILE_TABS_ROW_COLUMN_STRETCH_TYPE_ENUM_DECLARE__ +#ifdef __TILE_TABS_GRID_ROW_COLUMN_STRETCH_TYPE_ENUM_DECLARE__ +std::vector TileTabsGridRowColumnStretchTypeEnum::enumData; +bool TileTabsGridRowColumnStretchTypeEnum::initializedFlag = false; +int32_t TileTabsGridRowColumnStretchTypeEnum::integerCodeCounter = 0; +#endif // __TILE_TABS_GRID_ROW_COLUMN_STRETCH_TYPE_ENUM_DECLARE__ } // namespace -#endif //__TILE_TABS_ROW_COLUMN_STRETCH_TYPE_ENUM_H__ +#endif //__TILE_TABS_GRID_ROW_COLUMN_STRETCH_TYPE_ENUM_H__ diff --git a/src/GuiQt/BrainBrowserWindow.cxx b/src/GuiQt/BrainBrowserWindow.cxx index 5525de30c..0c8064380 100644 --- a/src/GuiQt/BrainBrowserWindow.cxx +++ b/src/GuiQt/BrainBrowserWindow.cxx @@ -2426,17 +2426,17 @@ BrainBrowserWindow::processViewMenuAboutToShow() m_viewTileTabsAction->setText("Enter Tile Tabs"); } - m_viewAutomaticTileTabsConfigurationAction->setText(getTileTabsConfigurationLabelText(TileTabsConfigurationModeEnum::AUTOMATIC, + m_viewAutomaticTileTabsConfigurationAction->setText(getTileTabsConfigurationLabelText(TileTabsGridModeEnum::AUTOMATIC, true)); - m_viewCustomTileTabsConfigurationAction->setText(getTileTabsConfigurationLabelText(TileTabsConfigurationModeEnum::CUSTOM, + m_viewCustomTileTabsConfigurationAction->setText(getTileTabsConfigurationLabelText(TileTabsGridModeEnum::CUSTOM, true)); BrowserWindowContent* bwc = getBrowerWindowContent(); switch (bwc->getTileTabsConfigurationMode()) { - case TileTabsConfigurationModeEnum::AUTOMATIC: + case TileTabsGridModeEnum::AUTOMATIC: m_viewAutomaticTileTabsConfigurationAction->setChecked(true); break; - case TileTabsConfigurationModeEnum::CUSTOM: + case TileTabsGridModeEnum::CUSTOM: m_viewCustomTileTabsConfigurationAction->setChecked(true); break; } @@ -2452,16 +2452,16 @@ BrainBrowserWindow::processViewMenuAboutToShow() * Include the number of rows and columns. */ AString -BrainBrowserWindow::getTileTabsConfigurationLabelText(const TileTabsConfigurationModeEnum::Enum configurationMode, +BrainBrowserWindow::getTileTabsConfigurationLabelText(const TileTabsGridModeEnum::Enum configurationMode, const bool includeRowsAndColumnsIn) const { bool includeRowsAndColumns = includeRowsAndColumnsIn; AString modeLabel; switch (configurationMode) { - case TileTabsConfigurationModeEnum::AUTOMATIC: + case TileTabsGridModeEnum::AUTOMATIC: modeLabel = "Automatic"; break; - case TileTabsConfigurationModeEnum::CUSTOM: + case TileTabsGridModeEnum::CUSTOM: modeLabel = "Custom"; break; } @@ -2473,12 +2473,12 @@ BrainBrowserWindow::getTileTabsConfigurationLabelText(const TileTabsConfiguratio const int32_t windowTabCount = static_cast(windowTabIndices.size()); AString errorText; switch (configurationMode) { - case TileTabsConfigurationModeEnum::AUTOMATIC: + case TileTabsGridModeEnum::AUTOMATIC: TileTabsConfiguration::getRowsAndColumnsForNumberOfTabs(windowTabCount, configRowCount, configColCount); break; - case TileTabsConfigurationModeEnum::CUSTOM: + case TileTabsGridModeEnum::CUSTOM: { const TileTabsConfiguration* customConfig = getBrowerWindowContent()->getCustomTileTabsConfiguration(); configRowCount = customConfig->getNumberOfRows(); @@ -2581,10 +2581,10 @@ BrainBrowserWindow::processViewTileTabsAutomaticCustomTriggered(QAction* action) { BrowserWindowContent* bwc = getBrowerWindowContent(); if (action == m_viewAutomaticTileTabsConfigurationAction) { - bwc->setTileTabsConfigurationMode(TileTabsConfigurationModeEnum::AUTOMATIC); + bwc->setTileTabsConfigurationMode(TileTabsGridModeEnum::AUTOMATIC); } else if (action == m_viewCustomTileTabsConfigurationAction) { - bwc->setTileTabsConfigurationMode(TileTabsConfigurationModeEnum::CUSTOM); + bwc->setTileTabsConfigurationMode(TileTabsGridModeEnum::CUSTOM); } else { CaretAssert(0); @@ -2603,7 +2603,7 @@ BrainBrowserWindow::processViewTileTabsLoadUserConfigurationMenuItemTriggered(QA CaretAssert(action); BrowserWindowContent* bwc = getBrowerWindowContent(); - bwc->setTileTabsConfigurationMode(TileTabsConfigurationModeEnum::CUSTOM); + bwc->setTileTabsConfigurationMode(TileTabsGridModeEnum::CUSTOM); TileTabsConfiguration* tileTabsConfig = NULL; for (auto& config : m_viewCustomTileTabsConfigurationActions) { diff --git a/src/GuiQt/BrainBrowserWindow.h b/src/GuiQt/BrainBrowserWindow.h index a4363d4ad..67dfd4ece 100644 --- a/src/GuiQt/BrainBrowserWindow.h +++ b/src/GuiQt/BrainBrowserWindow.h @@ -32,7 +32,7 @@ #include "DataFileTypeEnum.h" #include "EventListenerInterface.h" #include "SceneableInterface.h" -#include "TileTabsConfigurationModeEnum.h" +#include "TileTabsGridModeEnum.h" class QAction; class QActionGroup; @@ -150,8 +150,8 @@ namespace caret { bool isOpenGLContextSharingValid() const; - AString getTileTabsConfigurationLabelText(const TileTabsConfigurationModeEnum::Enum configurationMode, - const bool includeRowsAndColumns) const; + AString getTileTabsConfigurationLabelText(const TileTabsGridModeEnum::Enum configurationMode, + const bool includeRowsAndColumns) const; void resizeDockWidgets(const QList &docks, const QList &sizes, Qt::Orientation orientation); diff --git a/src/GuiQt/BrainBrowserWindowToolBar.cxx b/src/GuiQt/BrainBrowserWindowToolBar.cxx index b4333d6f6..f1fffa402 100644 --- a/src/GuiQt/BrainBrowserWindowToolBar.cxx +++ b/src/GuiQt/BrainBrowserWindowToolBar.cxx @@ -959,10 +959,10 @@ BrainBrowserWindowToolBar::allowAddingNewTab() * Automatic configuration always shows all tabs */ switch (browserWindowContent->getTileTabsConfigurationMode()) { - case TileTabsConfigurationModeEnum::AUTOMATIC: + case TileTabsGridModeEnum::AUTOMATIC: return true; break; - case TileTabsConfigurationModeEnum::CUSTOM: + case TileTabsGridModeEnum::CUSTOM: break; } diff --git a/src/GuiQt/TileTabsConfigurationDialog.cxx b/src/GuiQt/TileTabsConfigurationDialog.cxx index e97420c9f..cc2bc54ee 100644 --- a/src/GuiQt/TileTabsConfigurationDialog.cxx +++ b/src/GuiQt/TileTabsConfigurationDialog.cxx @@ -54,7 +54,7 @@ #include "GuiManager.h" #include "SessionManager.h" #include "TileTabsConfiguration.h" -#include "TileTabsRowColumnElement.h" +#include "TileTabsGridRowColumnElement.h" #include "WuQDataEntryDialog.h" #include "WuQFactory.h" #include "WuQGridLayoutGroup.h" @@ -474,7 +474,7 @@ TileTabsConfigurationDialog::updateRowColumnStretchWidgets(TileTabsConfiguration */ numRowElements = static_cast(m_rowElements.size()); for (int32_t iRow = 0; iRow < numRowElements; iRow++) { - TileTabsRowColumnElement* element(NULL); + TileTabsGridRowColumnElement* element(NULL); if (iRow < numRows) { element = configuration->getRow(iRow); } @@ -508,7 +508,7 @@ TileTabsConfigurationDialog::updateRowColumnStretchWidgets(TileTabsConfiguration */ numColumnElements = static_cast(m_columnElements.size()); for (int32_t iColumn = 0; iColumn < numColumnElements; iColumn++) { - TileTabsRowColumnElement* element(NULL); + TileTabsGridRowColumnElement* element(NULL); if (iColumn < numColumns) { element = configuration->getColumn(iColumn); } @@ -681,10 +681,10 @@ TileTabsConfigurationDialog::automaticCustomButtonClicked(QAbstractButton* butto { BrowserWindowContent* browserWindowContent = getBrowserWindowContent(); if (button == m_automaticConfigurationRadioButton) { - browserWindowContent->setTileTabsConfigurationMode(TileTabsConfigurationModeEnum::AUTOMATIC); + browserWindowContent->setTileTabsConfigurationMode(TileTabsGridModeEnum::AUTOMATIC); } else if (button == m_customConfigurationRadioButton) { - browserWindowContent->setTileTabsConfigurationMode(TileTabsConfigurationModeEnum::CUSTOM); + browserWindowContent->setTileTabsConfigurationMode(TileTabsGridModeEnum::CUSTOM); } else { CaretAssert(0); @@ -739,10 +739,10 @@ TileTabsConfigurationDialog::updateDialog() } switch (browserWindowContent->getTileTabsConfigurationMode()) { - case TileTabsConfigurationModeEnum::AUTOMATIC: + case TileTabsGridModeEnum::AUTOMATIC: m_automaticConfigurationRadioButton->setChecked(true); break; - case TileTabsConfigurationModeEnum::CUSTOM: + case TileTabsGridModeEnum::CUSTOM: m_customConfigurationRadioButton->setChecked(true); break; } @@ -797,9 +797,9 @@ void TileTabsConfigurationDialog::updateStretchFactors() { BrainBrowserWindow* browserWindow = getBrowserWindow(); - m_automaticConfigurationRadioButton->setText(browserWindow->getTileTabsConfigurationLabelText(TileTabsConfigurationModeEnum::AUTOMATIC, + m_automaticConfigurationRadioButton->setText(browserWindow->getTileTabsConfigurationLabelText(TileTabsGridModeEnum::AUTOMATIC, true)); - m_customConfigurationRadioButton->setText(browserWindow->getTileTabsConfigurationLabelText(TileTabsConfigurationModeEnum::CUSTOM, + m_customConfigurationRadioButton->setText(browserWindow->getTileTabsConfigurationLabelText(TileTabsGridModeEnum::CUSTOM, false)); const TileTabsConfiguration* configuration = getCustomTileTabsConfiguration(); if (configuration != NULL) { @@ -1153,7 +1153,7 @@ m_element(NULL) * Content type combo box */ m_contentTypeComboBox = new EnumComboBoxTemplate(this); - m_contentTypeComboBox->setup(); + m_contentTypeComboBox->setup(); QObject::connect(m_contentTypeComboBox, &EnumComboBoxTemplate::itemActivated, this, &TileTabElementWidgets::contentTypeActivated); m_contentTypeComboBox->getComboBox()->setFixedWidth(m_contentTypeComboBox->getComboBox()->sizeHint().width()); @@ -1163,7 +1163,7 @@ m_element(NULL) * Stretch type combo box */ m_stretchTypeComboBox = new EnumComboBoxTemplate(this); - m_stretchTypeComboBox->setup(); + m_stretchTypeComboBox->setup(); QObject::connect(m_stretchTypeComboBox, &EnumComboBoxTemplate::itemActivated, this, &TileTabElementWidgets::stretchTypeActivated); m_stretchTypeComboBox->getComboBox()->setFixedWidth(m_stretchTypeComboBox->getComboBox()->sizeHint().width()); @@ -1275,23 +1275,23 @@ TileTabElementWidgets::createConstructionMenu(QToolButton* toolButton) * Update with the given row/column element. */ void -TileTabElementWidgets::updateContent(TileTabsRowColumnElement* element) +TileTabElementWidgets::updateContent(TileTabsGridRowColumnElement* element) { m_element = element; const bool showFlag(m_element != NULL); if (showFlag) { - m_contentTypeComboBox->setSelectedItem(element->getContentType()); - m_stretchTypeComboBox->setSelectedItem(element->getStretchType()); + m_contentTypeComboBox->setSelectedItem(element->getContentType()); + m_stretchTypeComboBox->setSelectedItem(element->getStretchType()); QSignalBlocker valueBlocker(m_stretchValueSpinBox); switch (m_element->getStretchType()) { - case TileTabsRowColumnStretchTypeEnum::PERCENT: + case TileTabsGridRowColumnStretchTypeEnum::PERCENT: m_stretchValueSpinBox->setRange(0.0, 100.0); m_stretchValueSpinBox->setSingleStep(1.0); m_stretchValueSpinBox->setValue(m_element->getPercentStretch()); m_stretchValueSpinBox->setSuffix("%"); break; - case TileTabsRowColumnStretchTypeEnum::WEIGHT: + case TileTabsGridRowColumnStretchTypeEnum::WEIGHT: m_stretchValueSpinBox->setRange(0.0, 1000.0); m_stretchValueSpinBox->setSingleStep(0.1); m_stretchValueSpinBox->setValue(m_element->getWeightStretch()); @@ -1382,7 +1382,7 @@ void TileTabElementWidgets::contentTypeActivated() { if (m_element != NULL) { - m_element->setContentType(m_contentTypeComboBox->getSelectedItem()); + m_element->setContentType(m_contentTypeComboBox->getSelectedItem()); emit itemChanged(); } } @@ -1394,7 +1394,7 @@ void TileTabElementWidgets::stretchTypeActivated() { if (m_element != NULL) { - m_element->setStretchType(m_stretchTypeComboBox->getSelectedItem()); + m_element->setStretchType(m_stretchTypeComboBox->getSelectedItem()); emit itemChanged(); } } @@ -1407,10 +1407,10 @@ TileTabElementWidgets::stretchValueChanged(double) { if (m_element != NULL) { switch (m_element->getStretchType()) { - case TileTabsRowColumnStretchTypeEnum::PERCENT: + case TileTabsGridRowColumnStretchTypeEnum::PERCENT: m_element->setPercentStretch(m_stretchValueSpinBox->value()); break; - case TileTabsRowColumnStretchTypeEnum::WEIGHT: + case TileTabsGridRowColumnStretchTypeEnum::WEIGHT: m_element->setWeightStretch(m_stretchValueSpinBox->value()); break; } diff --git a/src/GuiQt/TileTabsConfigurationDialog.h b/src/GuiQt/TileTabsConfigurationDialog.h index f42f5d433..29e31cff2 100644 --- a/src/GuiQt/TileTabsConfigurationDialog.h +++ b/src/GuiQt/TileTabsConfigurationDialog.h @@ -23,8 +23,8 @@ #include "EventListenerInterface.h" #include "EventTileTabsConfigurationModification.h" -#include "TileTabsRowColumnContentTypeEnum.h" -#include "TileTabsRowColumnStretchTypeEnum.h" +#include "TileTabsGridRowColumnContentTypeEnum.h" +#include "TileTabsGridRowColumnStretchTypeEnum.h" #include "WuQDialogNonModal.h" class QCheckBox; @@ -46,7 +46,7 @@ namespace caret { class EnumComboBoxTemplate; class TileTabsConfiguration; class TileTabElementWidgets; - class TileTabsRowColumnElement; + class TileTabsGridRowColumnElement; class WuQGridLayoutGroup; class WuQListWidget; @@ -210,7 +210,7 @@ namespace caret { virtual ~TileTabElementWidgets(); - void updateContent(TileTabsRowColumnElement* element); + void updateContent(TileTabsGridRowColumnElement* element); signals: void itemChanged(); @@ -234,7 +234,7 @@ namespace caret { TileTabsConfigurationDialog* m_tileTabsConfigurationDialog; const EventTileTabsConfigurationModification::RowColumnType m_rowColumnType; const int32_t m_index; - TileTabsRowColumnElement* m_element; + TileTabsGridRowColumnElement* m_element; QLabel* m_indexLabel; QAction* m_constructionAction; diff --git a/src/GuiQt/TileTabsConfigurationModifier.cxx b/src/GuiQt/TileTabsConfigurationModifier.cxx index 3fe8be2a3..3b04e14c4 100644 --- a/src/GuiQt/TileTabsConfigurationModifier.cxx +++ b/src/GuiQt/TileTabsConfigurationModifier.cxx @@ -342,7 +342,7 @@ TileTabsConfigurationModifier::loadRowColumnsIntoTileTabsConfiguration(AString& } for (int32_t jCol = 0; jCol < numColumns; jCol++) { - TileTabsRowColumnElement* rce = newConfiguration.getColumn(jCol); + TileTabsGridRowColumnElement* rce = newConfiguration.getColumn(jCol); CaretAssertVectorIndex(m_rowColumns, jCol); CaretAssert(m_rowColumns[jCol]->m_stretching); *rce = *m_rowColumns[jCol]->m_stretching; @@ -371,7 +371,7 @@ TileTabsConfigurationModifier::loadRowColumnsIntoTileTabsConfiguration(AString& } for (int32_t iRow = 0; iRow < numRows; iRow++) { - TileTabsRowColumnElement* rce = newConfiguration.getRow(iRow); + TileTabsGridRowColumnElement* rce = newConfiguration.getRow(iRow); CaretAssertVectorIndex(m_rowColumns, iRow); CaretAssert(m_rowColumns[iRow]->m_stretching); *rce = *m_rowColumns[iRow]->m_stretching; @@ -491,7 +491,7 @@ TileTabsConfigurationModifier::RowColumnContent::RowColumnContent(const std::vec browserTabContent)); } - m_stretching = new TileTabsRowColumnElement(*tileTabsConfiguration->getRow(rowColumnIndex)); + m_stretching = new TileTabsGridRowColumnElement(*tileTabsConfiguration->getRow(rowColumnIndex)); } else { for (int32_t iRow = 0; iRow < numRows; iRow++) { @@ -507,7 +507,7 @@ TileTabsConfigurationModifier::RowColumnContent::RowColumnContent(const std::vec browserTabContent)); } - m_stretching = new TileTabsRowColumnElement(*tileTabsConfiguration->getColumn(rowColumnIndex)); + m_stretching = new TileTabsGridRowColumnElement(*tileTabsConfiguration->getColumn(rowColumnIndex)); } } @@ -522,7 +522,7 @@ TileTabsConfigurationModifier::RowColumnContent::RowColumnContent(const int32_t for (int32_t i = 0; i < numberOfElements; i++) { m_tabElements.push_back(new Element(i, i, NULL)); } - m_stretching = new TileTabsRowColumnElement(); + m_stretching = new TileTabsGridRowColumnElement(); } /** @@ -536,8 +536,8 @@ TileTabsConfigurationModifier::RowColumnContent::newInstanceContainingSpacers(co { RowColumnContent* content = new RowColumnContent(numberOfElements); - content->m_stretching->setContentType(TileTabsRowColumnContentTypeEnum::SPACE); - content->m_stretching->setStretchType(TileTabsRowColumnStretchTypeEnum::WEIGHT); + content->m_stretching->setContentType(TileTabsGridRowColumnContentTypeEnum::SPACE); + content->m_stretching->setStretchType(TileTabsGridRowColumnStretchTypeEnum::WEIGHT); content->m_stretching->setWeightStretch(1.0); return content; @@ -570,7 +570,7 @@ TileTabsConfigurationModifier::RowColumnContent::RowColumnContent(const RowColum m_tabElements.push_back(new Element(*te)); } - m_stretching = new TileTabsRowColumnElement(*obj.m_stretching); + m_stretching = new TileTabsGridRowColumnElement(*obj.m_stretching); } /** diff --git a/src/GuiQt/TileTabsConfigurationModifier.h b/src/GuiQt/TileTabsConfigurationModifier.h index 5f01e846d..c0b57e106 100644 --- a/src/GuiQt/TileTabsConfigurationModifier.h +++ b/src/GuiQt/TileTabsConfigurationModifier.h @@ -36,7 +36,7 @@ namespace caret { class EventTileTabsConfigurationModification; class SpacerTabContent; class TileTabsConfiguration; - class TileTabsRowColumnElement; + class TileTabsGridRowColumnElement; class TileTabsConfigurationModifier : public CaretObject { @@ -99,7 +99,7 @@ namespace caret { std::vector m_tabElements; - TileTabsRowColumnElement* m_stretching; + TileTabsGridRowColumnElement* m_stretching; private: RowColumnContent(const int32_t numberOfElements); -- GitLab From 47d15ebf78fba191d237e6f61bf9dd77951472b9 Mon Sep 17 00:00:00 2001 From: John Harwell Date: Mon, 15 Jul 2019 15:04:27 -0500 Subject: [PATCH 387/427] WB-860 NIFTI Volume Timeseries Issues: Correction to use preferences for defaulting "as a layer" for Metric Dynamic Connectivity File. --- src/Brain/BrainStructure.cxx | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/Brain/BrainStructure.cxx b/src/Brain/BrainStructure.cxx index 14df5afa2..33409222a 100644 --- a/src/Brain/BrainStructure.cxx +++ b/src/Brain/BrainStructure.cxx @@ -250,6 +250,15 @@ BrainStructure::addMetricFile(MetricFile* metricFile, if (addFileToBrainStructure) { m_metricFiles.push_back(metricFile); + + /* + * Enable dynamic connectivity using preferences + */ + CaretPreferences* prefs = SessionManager::get()->getCaretPreferences(); + MetricDynamicConnectivityFile* metricDynConn = metricFile->getMetricDynamicConnectivityFile(); + if (metricDynConn != NULL) { + metricDynConn->setEnabledAsLayer(prefs->isDynamicConnectivityDefaultedOn()); + } } } -- GitLab From e6da629705293ad71b3a86d8ec9a7f84a9a04201 Mon Sep 17 00:00:00 2001 From: John Harwell Date: Tue, 16 Jul 2019 14:12:04 -0500 Subject: [PATCH 388/427] WB-859 Manual Tile Tabs Layout System: Added some classes that will be used with upcoming manual tab layout system. --- src/Common/CMakeLists.txt | 6 + src/Common/TileTabsBaseConfiguration.cxx | 328 +++++ src/Common/TileTabsBaseConfiguration.h | 120 ++ .../TileTabsConfigurationLayoutTypeEnum.cxx | 373 ++++++ .../TileTabsConfigurationLayoutTypeEnum.h | 104 ++ .../TileTabsGridLayoutConfiguration.cxx | 1092 +++++++++++++++++ src/Common/TileTabsGridLayoutConfiguration.h | 178 +++ 7 files changed, 2201 insertions(+) create mode 100644 src/Common/TileTabsBaseConfiguration.cxx create mode 100644 src/Common/TileTabsBaseConfiguration.h create mode 100644 src/Common/TileTabsConfigurationLayoutTypeEnum.cxx create mode 100644 src/Common/TileTabsConfigurationLayoutTypeEnum.h create mode 100644 src/Common/TileTabsGridLayoutConfiguration.cxx create mode 100644 src/Common/TileTabsGridLayoutConfiguration.h diff --git a/src/Common/CMakeLists.txt b/src/Common/CMakeLists.txt index cf43445d8..61a63d0ed 100644 --- a/src/Common/CMakeLists.txt +++ b/src/Common/CMakeLists.txt @@ -153,6 +153,9 @@ StringTableModel.h StructureEnum.h SystemUtilities.h TileTabsConfiguration.h +TileTabsConfigurationLayoutTypeEnum.h +TileTabsBaseConfiguration.h +TileTabsGridLayoutConfiguration.h TileTabsGridModeEnum.h TileTabsGridRowColumnContentTypeEnum.h TileTabsGridRowColumnElement.h @@ -275,6 +278,9 @@ StringTableModel.cxx StructureEnum.cxx SystemUtilities.cxx TileTabsConfiguration.cxx +TileTabsConfigurationLayoutTypeEnum.cxx +TileTabsBaseConfiguration.cxx +TileTabsGridLayoutConfiguration.cxx TileTabsGridModeEnum.cxx TileTabsGridRowColumnContentTypeEnum.cxx TileTabsGridRowColumnElement.cxx diff --git a/src/Common/TileTabsBaseConfiguration.cxx b/src/Common/TileTabsBaseConfiguration.cxx new file mode 100644 index 000000000..e22e23a4b --- /dev/null +++ b/src/Common/TileTabsBaseConfiguration.cxx @@ -0,0 +1,328 @@ + +/*LICENSE_START*/ +/* + * Copyright (C) 2014 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + +#include +#include + +#define __TILE_TABS_BASE_CONFIGURATION_DECLARE__ +#include "TileTabsBaseConfiguration.h" +#undef __TILE_TABS_BASE_CONFIGURATION_DECLARE__ + +#include +#include + +#include "CaretAssert.h" +#include "CaretLogger.h" +#include "SystemUtilities.h" +#include "TileTabsGridLayoutConfiguration.h" + +using namespace caret; + + + +/** + * \class caret::TileTabsBaseConfiguration + * \brief Defines a tile tabs configuration + * \ingroup Common + */ + +/** + * Constructor that creates a 2 by 2 configuration. + */ +TileTabsBaseConfiguration::TileTabsBaseConfiguration(const TileTabsConfigurationLayoutTypeEnum::Enum layoutType) +: CaretObject(), +m_layoutType(layoutType) +{ + initializeTileTabsBaseConfiguration(); +} + +/** + * Destructor. + */ +TileTabsBaseConfiguration::~TileTabsBaseConfiguration() +{ +} + +/** + * Copy constructor. + * + * NOTE: Unique identifier remains the same ! See also: newCopyWithNewUniqueIdentifier() + * + * @param obj + * Object that is copied. + */ +TileTabsBaseConfiguration::TileTabsBaseConfiguration(const TileTabsBaseConfiguration& obj) +: CaretObject(obj), +m_layoutType(obj.m_layoutType) +{ + const AString savedUniqueID = m_uniqueIdentifier; + initializeTileTabsBaseConfiguration(); + m_uniqueIdentifier = savedUniqueID; + this->copyHelperTileTabsBaseConfiguration(obj); +} + +/** + * Assignment operator. + * + * NOTE: Unique identifier remains the same ! See also: newCopyWithNewUniqueIdentifier() + * + * @param obj + * Data copied from obj to this. + * @return + * Reference to this object. + */ +TileTabsBaseConfiguration& +TileTabsBaseConfiguration::operator=(const TileTabsBaseConfiguration& obj) +{ + if (this != &obj) { + CaretObject::operator=(obj); + this->copyHelperTileTabsBaseConfiguration(obj); + } + return *this; +} + +/** + * Initialize an instance of a tile tabs configuration. + */ +void +TileTabsBaseConfiguration::initializeTileTabsBaseConfiguration() +{ + m_name.clear(); + m_uniqueIdentifier = SystemUtilities::createUniqueID(); +} + +/** + * Helps with copying an object of this type. + * @param obj + * Object that is copied. + */ +void +TileTabsBaseConfiguration::copyHelperTileTabsBaseConfiguration(const TileTabsBaseConfiguration& obj) +{ + if (this == &obj) { + return; + } + + m_name = obj.m_name; + //DO NOT CHANGE THE UNIQUE IDENTIFIER: m_uniqueIdentifier +} + +/** + * Copies the tile tabs configuration rows, columns, and + * stretch factors. Name is NOT copied. + */ +void +TileTabsBaseConfiguration::copy(const TileTabsBaseConfiguration& rhs) +{ + CaretAssertToDoFatal(); // need to do this a different way + AString savedName = m_name; + copyHelperTileTabsBaseConfiguration(rhs); + m_name = savedName; +} + +/** + * @return Type of layout for the configuration + */ +TileTabsConfigurationLayoutTypeEnum::Enum +TileTabsBaseConfiguration::getLayoutType() const +{ + return m_layoutType; +} + +/** + * @return the name of the tile tabs configuration. + */ +AString +TileTabsBaseConfiguration::getName() const +{ + return m_name; +} + +/** + * @return Get the unique identifier that uniquely identifies each configuration. + */ +AString +TileTabsBaseConfiguration::getUniqueIdentifier() const +{ + return m_uniqueIdentifier; +} + +/** + * Set the name of the tile tabs configuration. + * + * @param name + * New name for configuration. + */ +void +TileTabsBaseConfiguration::setName(const AString& name) +{ + m_name = name; +} + +/** + * Set the unique identifier of the tile tabs configuration. + * + * @param uniqueID + * New unique identifier for configuration. + */ +void +TileTabsBaseConfiguration::setUniqueIdentifierProtected(const AString& uniqueID) +{ + m_uniqueIdentifier = uniqueID; +} + + +/** + * @return Encoded tile tabs configuration in XML + */ +AString +TileTabsBaseConfiguration::encodeInXML() const +{ + AString s; + encodeInXML(s); + return s; +} + +/** + * Decode the tile tabs configuration from XML + * + * @param xmlString + * String containing XML. + * @param errorMessageOut + * Contains error information if decoding fails. + * @return + * Pointer to the configuration or NULL if there was an error. + */ +TileTabsBaseConfiguration* +TileTabsBaseConfiguration::decodeFromXML(const AString& xmlString, + AString& errorMessageOut) +{ + errorMessageOut.clear(); + TileTabsBaseConfiguration* configurationOut(NULL); + + QXmlStreamReader xml(xmlString); + + if (xml.readNextStartElement()) { + const QStringRef tagName(xml.name()); + if (tagName == TileTabsGridLayoutConfiguration::s_v1_rootTagName) { + TileTabsGridLayoutConfiguration* v1 = new TileTabsGridLayoutConfiguration(); + v1->decodeFromXML(xml, + tagName.toString()); + configurationOut = v1; + } + else if (tagName == TileTabsGridLayoutConfiguration::s_v2_rootTagName) { + TileTabsGridLayoutConfiguration* v2 = new TileTabsGridLayoutConfiguration(); + v2->decodeFromXML(xml, + tagName.toString()); + configurationOut = v2; + } + else { + xml.raiseError("TileTabsBaseConfiguration first element is " + + xml.name().toString() + + " but should be " + + TileTabsGridLayoutConfiguration::s_v1_rootTagName + + " or " + + TileTabsGridLayoutConfiguration::s_v2_rootTagName); + } + } + else { + xml.raiseError("TileTabsBaseConfiguration failed to find start elemnent."); + } + + if (xml.hasError()) { + errorMessageOut = ("Tile Tabs Configuration Read Error at line number=" + + AString::number(xml.lineNumber()) + + " column number=" + + AString::number(xml.columnNumber()) + + " description=" + + xml.errorString()); + if (configurationOut != NULL) { + delete configurationOut; + configurationOut = NULL; + } + return configurationOut; + } + + const bool debugFlag(false); + if (debugFlag) { +// AString xmlText = encodeInXMLWithStreamWriterVersionTwo(); +// std::cout << std::endl << "NEW: " << xmlText << std::endl << std::endl; +// AString em; +// TileTabsBaseConfiguration temp; +// QXmlStreamReader tempReader(xmlText); +// tempReader.readNextStartElement(); +// temp.decodeFromXMLWithStreamReaderVersionTwo(tempReader); +// if (tempReader.hasError()) { +// std::cout << "Decode error: " << tempReader.errorString() << std::endl; +// } +// else { +// std::cout << "Decoded: " << temp.toString() << std::endl; +// } +// +// std::cout << std::endl; + } + return configurationOut; +} + + +/** + * @return String version of an instance. + */ +AString +TileTabsBaseConfiguration::toString() const +{ + AString s("Name: %1, Unique ID: %2\n"); + s = s.arg(m_name).arg(m_uniqueIdentifier); +// +// int32_t indx(0); +// for (const auto item : m_columns) { +// s.append(" Column " + AString::number(indx) + ": " + item.toString() + "\n"); +// indx++; +// } +// indx = 0; +// for (const auto item : m_rows) { +// s.append(" Row " + AString::number(indx) + ": " + item.toString() + "\n"); +// indx++; +// } + + return s; +} + +/** + * Compare two tile tabs configurations by name. + * + * @param ttc1 + * First tile tab configuration. + * @param ttc2 + * Second tile tab configuration. + * @return + * True if ttc1 is "less than" when compared by name, else false. + */ +bool +TileTabsBaseConfiguration::lessThanComparisonByName(const TileTabsBaseConfiguration* ttc1, + const TileTabsBaseConfiguration* ttc2) +{ + if (ttc1->getName() < ttc2->getName()) { + return true; + } + return false; +} + diff --git a/src/Common/TileTabsBaseConfiguration.h b/src/Common/TileTabsBaseConfiguration.h new file mode 100644 index 000000000..8e5ed2571 --- /dev/null +++ b/src/Common/TileTabsBaseConfiguration.h @@ -0,0 +1,120 @@ +#ifndef __TILE_TABS_BASE_CONFIGURATION_H__ +#define __TILE_TABS_BASE_CONFIGURATION_H__ + +/*LICENSE_START*/ +/* + * Copyright (C) 2014 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + +#include "CaretException.h" +#include "CaretObject.h" +#include "TileTabsConfigurationLayoutTypeEnum.h" +#include "TileTabsGridModeEnum.h" +#include "TileTabsGridRowColumnElement.h" + +class QXmlStreamReader; +class QXmlStreamWriter; + +namespace caret { + + class TileTabsBaseConfiguration : public CaretObject { + + protected: + TileTabsBaseConfiguration(const TileTabsConfigurationLayoutTypeEnum::Enum layoutType); + + public: + virtual ~TileTabsBaseConfiguration(); + + TileTabsBaseConfiguration(const TileTabsBaseConfiguration& obj); + + TileTabsBaseConfiguration& operator=(const TileTabsBaseConfiguration& obj); + + void copy(const TileTabsBaseConfiguration& rhs); + + virtual TileTabsBaseConfiguration* newCopyWithNewUniqueIdentifier() const = 0; + + TileTabsConfigurationLayoutTypeEnum::Enum getLayoutType() const; + + AString getName() const; + + void setName(const AString& name); + + AString getUniqueIdentifier() const; + + + AString encodeInXML() const; + + static TileTabsBaseConfiguration* decodeFromXML(const AString& xmlString, + AString& errorMessageOut); + + AString toString() const override; + + static bool lessThanComparisonByName(const TileTabsBaseConfiguration* ttc1, + const TileTabsBaseConfiguration* ttc2); + + // ADD_NEW_METHODS_HERE + + + protected: + /** + * Decode the configuration using the given XML stream reader and root element + * If there is an error, xml.raiseError() should be used to specify the error + * and caller of this method can test for the error using xml.isError(). + * + * @param xml + * The XML stream reader. + * @param rootElement + * The root element. + */ + virtual void decodeFromXML(QXmlStreamReader& xml, + const QString& rootElementText) = 0; + + /** + * Encode the configuration in XML. + * + * @param xmlTextOut + * Contains XML representation of configuration. + */ + virtual void encodeInXML(AString& xmlTextOut) const = 0; + + void setUniqueIdentifierProtected(const AString& uniqueID); + + void copyHelperTileTabsBaseConfiguration(const TileTabsBaseConfiguration& obj); + + private: + + void initializeTileTabsBaseConfiguration(); + + // ADD_NEW_MEMBERS_HERE + + const TileTabsConfigurationLayoutTypeEnum::Enum m_layoutType; + + AString m_name; + + /** Unique identifier does not get copied */ + AString m_uniqueIdentifier; + + + }; + +#ifdef __TILE_TABS_BASE_CONFIGURATION_DECLARE__ + +#endif // __TILE_TABS_BASE_CONFIGURATION_DECLARE__ + +} // namespace +#endif //__TILE_TABS_BASE_CONFIGURATION_H__ diff --git a/src/Common/TileTabsConfigurationLayoutTypeEnum.cxx b/src/Common/TileTabsConfigurationLayoutTypeEnum.cxx new file mode 100644 index 000000000..fe45e41cb --- /dev/null +++ b/src/Common/TileTabsConfigurationLayoutTypeEnum.cxx @@ -0,0 +1,373 @@ + +/*LICENSE_START*/ +/* + * Copyright (C) 2019 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + +#include +#define __TILE_TABS_CONFIGURATION_LAYOUT_TYPE_ENUM_DECLARE__ +#include "TileTabsConfigurationLayoutTypeEnum.h" +#undef __TILE_TABS_CONFIGURATION_LAYOUT_TYPE_ENUM_DECLARE__ + +#include "CaretAssert.h" + +using namespace caret; + + +/** + * \class caret::TileTabsConfigurationLayoutTypeEnum + * \brief Types of tile tabs layouts + * + * Using this enumerated type in the GUI with an EnumComboBoxTemplate + * + * Header File (.h) + * Forward declare the data type: + * class EnumComboBoxTemplate; + * + * Declare the member: + * EnumComboBoxTemplate* m_tileTabsConfigurationLayoutTypeEnumComboBox; + * + * Declare a slot that is called when user changes selection + * private slots: + * void tileTabsConfigurationLayoutTypeEnumComboBoxItemActivated(); + * + * Implementation File (.cxx) + * Include the header files + * #include "EnumComboBoxTemplate.h" + * #include "TileTabsConfigurationLayoutTypeEnum.h" + * + * Instatiate: + * m_tileTabsConfigurationLayoutTypeEnumComboBox = new EnumComboBoxTemplate(this); + * m_tileTabsConfigurationLayoutTypeEnumComboBox->setup(); + * + * Get notified when the user changes the selection: + * QObject::connect(m_tileTabsConfigurationLayoutTypeEnumComboBox, SIGNAL(itemActivated()), + * this, SLOT(tileTabsConfigurationLayoutTypeEnumComboBoxItemActivated())); + * + * Update the selection: + * m_tileTabsConfigurationLayoutTypeEnumComboBox->setSelectedItem(NEW_VALUE); + * + * Read the selection: + * const TileTabsConfigurationLayoutTypeEnum::Enum VARIABLE = m_tileTabsConfigurationLayoutTypeEnumComboBox->getSelectedItem(); + * + */ + +/** + * Constructor. + * + * @param enumValue + * An enumerated value. + * @param name + * Name of enumerated value. + * + * @param guiName + * User-friendly name for use in user-interface. + */ +TileTabsConfigurationLayoutTypeEnum::TileTabsConfigurationLayoutTypeEnum(const Enum enumValue, + const AString& name, + const AString& guiName) +{ + this->enumValue = enumValue; + this->integerCode = integerCodeCounter++; + this->name = name; + this->guiName = guiName; +} + +/** + * Destructor. + */ +TileTabsConfigurationLayoutTypeEnum::~TileTabsConfigurationLayoutTypeEnum() +{ +} + +/** + * Initialize the enumerated metadata. + */ +void +TileTabsConfigurationLayoutTypeEnum::initialize() +{ + if (initializedFlag) { + return; + } + initializedFlag = true; + + enumData.push_back(TileTabsConfigurationLayoutTypeEnum(GRID, + "GRID", + "Grid")); + + enumData.push_back(TileTabsConfigurationLayoutTypeEnum(MANUAL, + "MANUAL", + "Manual")); + +} + +/** + * Find the data for and enumerated value. + * @param enumValue + * The enumerated value. + * @return Pointer to data for this enumerated type + * or NULL if no data for type or if type is invalid. + */ +const TileTabsConfigurationLayoutTypeEnum* +TileTabsConfigurationLayoutTypeEnum::findData(const Enum enumValue) +{ + if (initializedFlag == false) initialize(); + + size_t num = enumData.size(); + for (size_t i = 0; i < num; i++) { + const TileTabsConfigurationLayoutTypeEnum* d = &enumData[i]; + if (d->enumValue == enumValue) { + return d; + } + } + + return NULL; +} + +/** + * Get a string representation of the enumerated type. + * @param enumValue + * Enumerated value. + * @return + * String representing enumerated value. + */ +AString +TileTabsConfigurationLayoutTypeEnum::toName(Enum enumValue) { + if (initializedFlag == false) initialize(); + + const TileTabsConfigurationLayoutTypeEnum* enumInstance = findData(enumValue); + return enumInstance->name; +} + +/** + * Get an enumerated value corresponding to its name. + * @param name + * Name of enumerated value. + * @param isValidOut + * If not NULL, it is set indicating that a + * enum value exists for the input name. + * @return + * Enumerated value. + */ +TileTabsConfigurationLayoutTypeEnum::Enum +TileTabsConfigurationLayoutTypeEnum::fromName(const AString& name, bool* isValidOut) +{ + if (initializedFlag == false) initialize(); + + bool validFlag = false; + Enum enumValue = TileTabsConfigurationLayoutTypeEnum::enumData[0].enumValue; + + for (std::vector::iterator iter = enumData.begin(); + iter != enumData.end(); + iter++) { + const TileTabsConfigurationLayoutTypeEnum& d = *iter; + if (d.name == name) { + enumValue = d.enumValue; + validFlag = true; + break; + } + } + + if (isValidOut != 0) { + *isValidOut = validFlag; + } + else if (validFlag == false) { + CaretAssertMessage(0, AString("Name " + name + "failed to match enumerated value for type TileTabsConfigurationLayoutTypeEnum")); + } + return enumValue; +} + +/** + * Get a GUI string representation of the enumerated type. + * @param enumValue + * Enumerated value. + * @return + * String representing enumerated value. + */ +AString +TileTabsConfigurationLayoutTypeEnum::toGuiName(Enum enumValue) { + if (initializedFlag == false) initialize(); + + const TileTabsConfigurationLayoutTypeEnum* enumInstance = findData(enumValue); + return enumInstance->guiName; +} + +/** + * Get an enumerated value corresponding to its GUI name. + * @param s + * Name of enumerated value. + * @param isValidOut + * If not NULL, it is set indicating that a + * enum value exists for the input name. + * @return + * Enumerated value. + */ +TileTabsConfigurationLayoutTypeEnum::Enum +TileTabsConfigurationLayoutTypeEnum::fromGuiName(const AString& guiName, bool* isValidOut) +{ + if (initializedFlag == false) initialize(); + + bool validFlag = false; + Enum enumValue = TileTabsConfigurationLayoutTypeEnum::enumData[0].enumValue; + + for (std::vector::iterator iter = enumData.begin(); + iter != enumData.end(); + iter++) { + const TileTabsConfigurationLayoutTypeEnum& d = *iter; + if (d.guiName == guiName) { + enumValue = d.enumValue; + validFlag = true; + break; + } + } + + if (isValidOut != 0) { + *isValidOut = validFlag; + } + else if (validFlag == false) { + CaretAssertMessage(0, AString("guiName " + guiName + "failed to match enumerated value for type TileTabsConfigurationLayoutTypeEnum")); + } + return enumValue; +} + +/** + * Get the integer code for a data type. + * + * @return + * Integer code for data type. + */ +int32_t +TileTabsConfigurationLayoutTypeEnum::toIntegerCode(Enum enumValue) +{ + if (initializedFlag == false) initialize(); + const TileTabsConfigurationLayoutTypeEnum* enumInstance = findData(enumValue); + return enumInstance->integerCode; +} + +/** + * Find the data type corresponding to an integer code. + * + * @param integerCode + * Integer code for enum. + * @param isValidOut + * If not NULL, on exit isValidOut will indicate if + * integer code is valid. + * @return + * Enum for integer code. + */ +TileTabsConfigurationLayoutTypeEnum::Enum +TileTabsConfigurationLayoutTypeEnum::fromIntegerCode(const int32_t integerCode, bool* isValidOut) +{ + if (initializedFlag == false) initialize(); + + bool validFlag = false; + Enum enumValue = TileTabsConfigurationLayoutTypeEnum::enumData[0].enumValue; + + for (std::vector::iterator iter = enumData.begin(); + iter != enumData.end(); + iter++) { + const TileTabsConfigurationLayoutTypeEnum& enumInstance = *iter; + if (enumInstance.integerCode == integerCode) { + enumValue = enumInstance.enumValue; + validFlag = true; + break; + } + } + + if (isValidOut != 0) { + *isValidOut = validFlag; + } + else if (validFlag == false) { + CaretAssertMessage(0, AString("Integer code " + AString::number(integerCode) + "failed to match enumerated value for type TileTabsConfigurationLayoutTypeEnum")); + } + return enumValue; +} + +/** + * Get all of the enumerated type values. The values can be used + * as parameters to toXXX() methods to get associated metadata. + * + * @param allEnums + * A vector that is OUTPUT containing all of the enumerated values. + */ +void +TileTabsConfigurationLayoutTypeEnum::getAllEnums(std::vector& allEnums) +{ + if (initializedFlag == false) initialize(); + + allEnums.clear(); + + for (std::vector::iterator iter = enumData.begin(); + iter != enumData.end(); + iter++) { + allEnums.push_back(iter->enumValue); + } +} + +/** + * Get all of the names of the enumerated type values. + * + * @param allNames + * A vector that is OUTPUT containing all of the names of the enumerated values. + * @param isSorted + * If true, the names are sorted in alphabetical order. + */ +void +TileTabsConfigurationLayoutTypeEnum::getAllNames(std::vector& allNames, const bool isSorted) +{ + if (initializedFlag == false) initialize(); + + allNames.clear(); + + for (std::vector::iterator iter = enumData.begin(); + iter != enumData.end(); + iter++) { + allNames.push_back(TileTabsConfigurationLayoutTypeEnum::toName(iter->enumValue)); + } + + if (isSorted) { + std::sort(allNames.begin(), allNames.end()); + } +} + +/** + * Get all of the GUI names of the enumerated type values. + * + * @param allNames + * A vector that is OUTPUT containing all of the GUI names of the enumerated values. + * @param isSorted + * If true, the names are sorted in alphabetical order. + */ +void +TileTabsConfigurationLayoutTypeEnum::getAllGuiNames(std::vector& allGuiNames, const bool isSorted) +{ + if (initializedFlag == false) initialize(); + + allGuiNames.clear(); + + for (std::vector::iterator iter = enumData.begin(); + iter != enumData.end(); + iter++) { + allGuiNames.push_back(TileTabsConfigurationLayoutTypeEnum::toGuiName(iter->enumValue)); + } + + if (isSorted) { + std::sort(allGuiNames.begin(), allGuiNames.end()); + } +} + diff --git a/src/Common/TileTabsConfigurationLayoutTypeEnum.h b/src/Common/TileTabsConfigurationLayoutTypeEnum.h new file mode 100644 index 000000000..8a57604bd --- /dev/null +++ b/src/Common/TileTabsConfigurationLayoutTypeEnum.h @@ -0,0 +1,104 @@ +#ifndef __TILE_TABS_CONFIGURATION_LAYOUT_TYPE_ENUM_H__ +#define __TILE_TABS_CONFIGURATION_LAYOUT_TYPE_ENUM_H__ + +/*LICENSE_START*/ +/* + * Copyright (C) 2019 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + + +#include +#include +#include "AString.h" + +namespace caret { + +class TileTabsConfigurationLayoutTypeEnum { + +public: + /** + * Enumerated values. + */ + enum Enum { + /** */ + GRID, + /** */ + MANUAL + }; + + + ~TileTabsConfigurationLayoutTypeEnum(); + + static AString toName(Enum enumValue); + + static Enum fromName(const AString& name, bool* isValidOut); + + static AString toGuiName(Enum enumValue); + + static Enum fromGuiName(const AString& guiName, bool* isValidOut); + + static int32_t toIntegerCode(Enum enumValue); + + static Enum fromIntegerCode(const int32_t integerCode, bool* isValidOut); + + static void getAllEnums(std::vector& allEnums); + + static void getAllNames(std::vector& allNames, const bool isSorted); + + static void getAllGuiNames(std::vector& allGuiNames, const bool isSorted); + +private: + TileTabsConfigurationLayoutTypeEnum(const Enum enumValue, + const AString& name, + const AString& guiName); + + static const TileTabsConfigurationLayoutTypeEnum* findData(const Enum enumValue); + + /** Holds all instance of enum values and associated metadata */ + static std::vector enumData; + + /** Initialize instances that contain the enum values and metadata */ + static void initialize(); + + /** Indicates instance of enum values and metadata have been initialized */ + static bool initializedFlag; + + /** Auto generated integer codes */ + static int32_t integerCodeCounter; + + /** The enumerated type value for an instance */ + Enum enumValue; + + /** The integer code associated with an enumerated value */ + int32_t integerCode; + + /** The name, a text string that is identical to the enumerated value */ + AString name; + + /** A user-friendly name that is displayed in the GUI */ + AString guiName; +}; + +#ifdef __TILE_TABS_CONFIGURATION_LAYOUT_TYPE_ENUM_DECLARE__ +std::vector TileTabsConfigurationLayoutTypeEnum::enumData; +bool TileTabsConfigurationLayoutTypeEnum::initializedFlag = false; +int32_t TileTabsConfigurationLayoutTypeEnum::integerCodeCounter = 0; +#endif // __TILE_TABS_CONFIGURATION_LAYOUT_TYPE_ENUM_DECLARE__ + +} // namespace +#endif //__TILE_TABS_CONFIGURATION_LAYOUT_TYPE_ENUM_H__ diff --git a/src/Common/TileTabsGridLayoutConfiguration.cxx b/src/Common/TileTabsGridLayoutConfiguration.cxx new file mode 100644 index 000000000..dbe571bf6 --- /dev/null +++ b/src/Common/TileTabsGridLayoutConfiguration.cxx @@ -0,0 +1,1092 @@ + +/*LICENSE_START*/ +/* + * Copyright (C) 2014 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + +#include +#include + +#define __TILE_TABS_GRID_LAYOUT_CONFIGURATION_DECLARE__ +#include "TileTabsGridLayoutConfiguration.h" +#undef __TILE_TABS_GRID_LAYOUT_CONFIGURATION_DECLARE__ + +#include +#include + +#include "CaretAssert.h" +#include "CaretLogger.h" +#include "SystemUtilities.h" + +using namespace caret; + + + +/** + * \class caret::TileTabsGridLayoutConfiguration + * \brief Defines a tile tabs configuration + * \ingroup Common + */ + +/** + * Constructor that creates a 2 by 2 configuration. + */ +TileTabsGridLayoutConfiguration::TileTabsGridLayoutConfiguration() +: TileTabsBaseConfiguration(TileTabsConfigurationLayoutTypeEnum::GRID) +{ + initialize(); +} + +/** + * Destructor. + */ +TileTabsGridLayoutConfiguration::~TileTabsGridLayoutConfiguration() +{ +} + +/** + * Copy constructor. + * + * NOTE: Unique identifier remains the same ! See also: newCopyWithNewUniqueIdentifier() + * + * @param obj + * Object that is copied. + */ +TileTabsGridLayoutConfiguration::TileTabsGridLayoutConfiguration(const TileTabsGridLayoutConfiguration& obj) +: TileTabsBaseConfiguration(obj) +{ + initialize(); + this->copyHelperTileTabsGridLayoutConfiguration(obj); +} + +/** + * Assignment operator. + * + * NOTE: Unique identifier remains the same ! See also: newCopyWithNewUniqueIdentifier() + * + * @param obj + * Data copied from obj to this. + * @return + * Reference to this object. + */ +TileTabsGridLayoutConfiguration& +TileTabsGridLayoutConfiguration::operator=(const TileTabsGridLayoutConfiguration& obj) +{ + if (this != &obj) { + TileTabsBaseConfiguration::operator=(obj); + this->copyHelperTileTabsGridLayoutConfiguration(obj); + } + return *this; +} + +/** + * Copy this instance and give it a new unique identifier. + * Note that copy constructor does not create a new unique identifier. + * + * @return The new Copy. + */ +TileTabsGridLayoutConfiguration* +TileTabsGridLayoutConfiguration::newCopyWithNewUniqueIdentifier() const +{ + TileTabsGridLayoutConfiguration* newCopy = new TileTabsGridLayoutConfiguration(*this); + CaretAssert(newCopy); + return newCopy; +} + + +/** + * Initialize an instance of a tile tabs configuration. + */ +void +TileTabsGridLayoutConfiguration::initialize() +{ + setNumberOfRows(2); + setNumberOfColumns(2); + + m_centeringCorrectionEnabled = false; +} + +/** + * Helps with copying an object of this type. + * @param obj + * Object that is copied. + */ +void +TileTabsGridLayoutConfiguration::copyHelperTileTabsGridLayoutConfiguration(const TileTabsGridLayoutConfiguration& obj) +{ + if (this == &obj) { + return; + } + + copyHelperTileTabsBaseConfiguration(obj); + m_columns = obj.m_columns; + m_rows = obj.m_rows; + m_centeringCorrectionEnabled = obj.m_centeringCorrectionEnabled; +} + +/** + * Copies the tile tabs configuration rows, columns, and + * stretch factors. Name is NOT copied. + */ +void +TileTabsGridLayoutConfiguration::copy(const TileTabsGridLayoutConfiguration& rhs) +{ + AString savedName = getName(); + copyHelperTileTabsGridLayoutConfiguration(rhs); + setName(savedName); +} + +/** + * Get infoformation for the given elemnent in the columns. + * + * @param Information for element. + */ +TileTabsGridRowColumnElement* +TileTabsGridLayoutConfiguration::getColumn(const int32_t columnIndex) +{ + CaretAssertVectorIndex(m_columns, columnIndex); + return &m_columns[columnIndex]; +} + +/** + * Get infoformation for the given elemnent in the columns. + * + * @param Information for element. + */ +const TileTabsGridRowColumnElement* +TileTabsGridLayoutConfiguration::getColumn(const int32_t columnIndex) const +{ + CaretAssertVectorIndex(m_columns, columnIndex); + return &m_columns[columnIndex]; +} + +/** + * Get infoformation for the given elemnent in the rows. + * + * @param Information for element. + */ +TileTabsGridRowColumnElement* +TileTabsGridLayoutConfiguration::getRow(const int32_t rowIndex) +{ + CaretAssertVectorIndex(m_rows, rowIndex); + return &m_rows[rowIndex]; +} + +/** + * Get infoformation for the given elemnent in the rows. + * + * @param Information for element. + */ +const TileTabsGridRowColumnElement* +TileTabsGridLayoutConfiguration::getRow(const int32_t rowIndex) const +{ + CaretAssertVectorIndex(m_rows, rowIndex); + return &m_rows[rowIndex]; +} + +/** + * Get the row heights and column widths for this tile tabs configuration using the + * given window width and height. + * + * @param windowWidth + * Width of window. + * @param windowHeight + * Height of window. + * @param numberOfModelsToDraw + * Number of models to draw. + * @param configurationMode + * The tile tabs configuration mode + * @param rowHeightsOut + * Output containing height of each row. + * @param columnWidthsOut + * Output containing width of each column. + * @return + * True if the ouput is valid, else false. + */ +bool +TileTabsGridLayoutConfiguration::getRowHeightsAndColumnWidthsForWindowSize(const int32_t windowWidth, + const int32_t windowHeight, + const int32_t numberOfModelsToDraw, + const TileTabsGridModeEnum::Enum configurationMode, + std::vector& rowHeightsOut, + std::vector& columnWidthsOut) +{ + /* + * NOTE: When computing widths and heights, do not round. + * Rounding may cause the bottom most row or column to extend + * outside the graphics region. Shrinking the last row or + * column is not desired since it might cause the last model + * to be drawn slightly smaller than the others. + */ + + int32_t numRows = 0; + int32_t numCols = 0; + + rowHeightsOut.clear(); + columnWidthsOut.clear(); + + switch (configurationMode) { + case TileTabsGridModeEnum::AUTOMATIC: + { + /* + * Update number of rows/columns in the default configuration + * so that if a scene is saved, the correct number of rows + * and columns are saved to the scene. + */ + updateAutomaticConfigurationRowsAndColumns(numberOfModelsToDraw); + numRows = getNumberOfRows(); + numCols = getNumberOfColumns(); + + for (int32_t i = 0; i < numRows; i++) { + rowHeightsOut.push_back(windowHeight / numRows); + } + for (int32_t i = 0; i < numCols; i++) { + columnWidthsOut.push_back(windowWidth / numCols); + } + } break; + case TileTabsGridModeEnum::CUSTOM: + { + /* + * Rows/columns from user configuration + */ + numRows = getNumberOfRows(); + numCols = getNumberOfColumns(); + + /* + * Determine height of each row + */ + float rowPercentTotal = 0.0; + float rowStretchTotal = 0.0; + for (int32_t i = 0; i < numRows; i++) { + const TileTabsGridRowColumnElement* e = getRow(i); + switch (e->getStretchType()) { + case TileTabsGridRowColumnStretchTypeEnum::PERCENT: + rowPercentTotal += (e->getPercentStretch() / 100.0); + break; + case TileTabsGridRowColumnStretchTypeEnum::WEIGHT: + rowStretchTotal += e->getWeightStretch(); + break; + } + } + + float windowWeightHeight = windowHeight; + if (rowPercentTotal > 0.0) { + if (rowPercentTotal >= 1.0) { + windowWeightHeight = 0.0; + } + else { + windowWeightHeight = (1.0 - rowPercentTotal) * windowHeight; + } + } + for (int32_t i = 0; i < numRows; i++) { + int32_t h = 0; + const TileTabsGridRowColumnElement* e = getRow(i); + switch (e->getStretchType()) { + case TileTabsGridRowColumnStretchTypeEnum::PERCENT: + h = (e->getPercentStretch() / 100.0) * windowHeight; + break; + case TileTabsGridRowColumnStretchTypeEnum::WEIGHT: + if (rowStretchTotal > 0.0) { + h = static_cast((e->getWeightStretch() / rowStretchTotal) + * windowWeightHeight); + } + break; + } + + rowHeightsOut.push_back(h); + } + + /* + * Determine width of each column + */ + float columnPercentTotal = 0.0; + float columnStretchTotal = 0.0; + for (int32_t i = 0; i < numCols; i++) { + const TileTabsGridRowColumnElement* e = getColumn(i); + switch (e->getStretchType()) { + case TileTabsGridRowColumnStretchTypeEnum::PERCENT: + columnPercentTotal += (e->getPercentStretch() / 100.0); + break; + case TileTabsGridRowColumnStretchTypeEnum::WEIGHT: + columnStretchTotal += e->getWeightStretch(); + break; + } + } + + float windowWeightWidth = windowWidth; + if (columnPercentTotal > 0.0) { + if (columnPercentTotal >= 1.0) { + windowWeightWidth = 0.0; + } + else { + windowWeightWidth = (1.0 - columnPercentTotal) * windowWidth; + } + } + + for (int32_t i = 0; i < numCols; i++) { + int32_t w = 0; + const TileTabsGridRowColumnElement* e = getColumn(i); + switch (e->getStretchType()) { + case TileTabsGridRowColumnStretchTypeEnum::PERCENT: + w = (e->getPercentStretch() / 100.0) * windowWidth; + break; + case TileTabsGridRowColumnStretchTypeEnum::WEIGHT: + if (columnStretchTotal > 0.0) { + w = static_cast((e->getWeightStretch() / columnStretchTotal) + * windowWeightWidth); + } + break; + } + columnWidthsOut.push_back(w); + } + } break; + } + + if ((numRows == static_cast(rowHeightsOut.size())) + && (numCols == static_cast(columnWidthsOut.size()))) { +// /* +// * Verify all rows fit within the window +// */ +// int32_t rowHeightsSum = 0; +// for (int32_t i = 0; i < numRows; i++) { +// rowHeightsSum += rowHeightsOut[i]; +// } +// if (rowHeightsSum > windowHeight) { +// CaretLogSevere("PROGRAM ERROR: Tile Tabs total row heights exceed window height"); +//// rowHeightsOut[numRows - 1] -= (rowHeightsSum - windowHeight); +// } +// +// /* +// * Adjust width of last column so that it does not extend beyond viewport +// */ +// int32_t columnWidthsSum = 0; +// for (int32_t i = 0; i < numCols; i++) { +// columnWidthsSum += columnWidthsOut[i]; +// } +// if (columnWidthsSum > windowWidth) { +// CaretLogSevere("PROGRAM ERROR: Tile Tabs total row heights exceed window height"); +//// columnWidthsOut[numCols - 1] = columnWidthsSum - windowWidth; +// } +// +// CaretLogFiner("Tile Tabs Row Heights: " +// + AString::fromNumbers(rowHeightsOut, ", ")); +// CaretLogFiner("Tile Tabs Column Widths: " +// + AString::fromNumbers(columnWidthsOut, ", ")); + return true; + } + + const QString msg("Row and heights failed rows=" + + AString::number(numRows) + + " rowHeights=" + + AString::number(rowHeightsOut.size()) + + " cols=" + + AString::number(numCols) + + " rowHeights=" + + AString::number(columnWidthsOut.size())); + CaretAssertMessage(0, msg); + CaretLogSevere(msg); + return false; +} + +/** + * @return Number of rows. + */ +int32_t +TileTabsGridLayoutConfiguration::getNumberOfRows() const +{ + return m_rows.size(); +} + +/** + * Set number of rows. + * + * @param numberOfRows + * New number of rows. + */ +void +TileTabsGridLayoutConfiguration::setNumberOfRows(const int32_t numberOfRows) +{ + m_rows.resize(numberOfRows); +} + +/** + * @return Number of columns. + */ +int32_t +TileTabsGridLayoutConfiguration::getNumberOfColumns() const +{ + return m_columns.size(); +} + +/** + * Set number of rows. + * + * @param numberOfColumns + * New number of rows. + */ +void +TileTabsGridLayoutConfiguration::setNumberOfColumns(const int32_t numberOfColumns) +{ + m_columns.resize(numberOfColumns); +} + +/** + * Get the number of rows and columns for an automatic layout with the + * given number of tabs. + * @param numberOfTabs + * Number of tabs. + * @param numberOfRowsOut + * Output with number of rows. + * @param numberOfColumnsOut + * Output with number of columns. + */ +void +TileTabsGridLayoutConfiguration::getRowsAndColumnsForNumberOfTabs(const int32_t numberOfTabs, + int32_t& numberOfRowsOut, + int32_t& numberOfColumnsOut) +{ + CaretAssert(numberOfTabs >= 0); + + numberOfRowsOut = (int)std::sqrt((double)numberOfTabs); + numberOfColumnsOut = numberOfRowsOut; + int32_t row2 = numberOfRowsOut * numberOfRowsOut; + if (row2 < numberOfTabs) { + numberOfColumnsOut++; + } + if ((numberOfRowsOut * numberOfColumnsOut) < numberOfTabs) { + numberOfRowsOut++; + } +} + +/** + * @return True if the centering correction is enabled. + */ +bool +TileTabsGridLayoutConfiguration::isCenteringCorrectionEnabled() const +{ + return m_centeringCorrectionEnabled; +} + +/** + * Set the enabled status of the centering correction + * + * @param status + * New status for enabling the centering correction + */ +void +TileTabsGridLayoutConfiguration::setCenteringCorrectionEnabled(const bool status) +{ + m_centeringCorrectionEnabled = status; +} + + +/** + * Updates the number of rows and columns for the automatic configuration + * based upon the number of tabs. + * + * Since screen width typically exceeds height, ensure the number of + * columns is always greater than the number of rows. + */ +void +TileTabsGridLayoutConfiguration::updateAutomaticConfigurationRowsAndColumns(const int32_t numberOfTabs) +{ + int32_t numRows(0), numCols(0); + getRowsAndColumnsForNumberOfTabs(numberOfTabs, + numRows, + numCols); + + setNumberOfRows(numRows); + setNumberOfColumns(numCols); +} + +/** + * Encode the configuration in XML. + * + * @param xmlTextOut + * Contains XML representation of configuration. + */ +void +TileTabsGridLayoutConfiguration::encodeInXML(AString& xmlTextOut) const +{ + xmlTextOut = encodeVersionInXML(2); +} + +/** + * @return Encoded tile tabs configuration in XML + * using the give XML version of TileTabsGridLayoutConfiguration. + */ +AString +TileTabsGridLayoutConfiguration::encodeVersionInXML(const int32_t versionNumber) const +{ + AString s; + + switch (versionNumber) { + case 1: + s = encodeInXMLWithStreamWriterVersionOne(); + break; + case 2: + s = encodeInXMLWithStreamWriterVersionTwo(); + break; + default: + CaretAssertMessage(0, "Requested invalid version=" + AString::number(versionNumber)); + break; + } + + return s; +} + + +/** + * @return Encoded tile tabs configuration in XML with Stream Writer + */ +AString +TileTabsGridLayoutConfiguration::encodeInXMLWithStreamWriterVersionOne() const +{ + AString xmlString; + QXmlStreamWriter writer(&xmlString); + writer.setAutoFormatting(true); + + writer.writeStartElement(s_v1_rootTagName); + + writer.writeStartElement(s_v1_versionTagName); + writer.writeAttribute(s_v1_versionNumberAttributeName, "1"); + writer.writeEndElement(); + + writer.writeTextElement(s_nameTagName, getName()); + writer.writeTextElement(s_uniqueIdentifierTagName, getUniqueIdentifier()); + + const int32_t numberOfRows = getNumberOfRows(); + writer.writeStartElement(s_v1_rowStretchFactorsTagName); + writer.writeAttribute(s_v1_rowStretchFactorsSelectedCountAttributeName, AString::number(numberOfRows)); + writer.writeAttribute(s_v1_rowStretchFactorsTotalCountAttributeName, AString::number(numberOfRows)); + std::vector rowStretchFactors; + for (const auto e : m_rows) { + rowStretchFactors.push_back(e.getWeightStretch()); + } + writer.writeCharacters(AString::fromNumbers(rowStretchFactors, " ")); + writer.writeEndElement(); + + const int32_t numberOfColumns = getNumberOfColumns(); + writer.writeStartElement(s_v1_columnStretchFactorsTagName); + writer.writeAttribute(s_v1_columnStretchFactorsSelectedCountAttributeName, AString::number(numberOfColumns)); + writer.writeAttribute(s_v1_columnStretchFactorsTotalCountAttributeName, AString::number(numberOfColumns)); + std::vector columnStretchFactors; + for (const auto e : m_columns) { + columnStretchFactors.push_back(e.getWeightStretch()); + } + writer.writeCharacters(AString::fromNumbers(columnStretchFactors, " ")); + writer.writeEndElement(); + + writer.writeEndElement(); + + return xmlString; +} + +/** + * @return Encoded tile tabs configuration in XML with Stream Writer + */ +AString +TileTabsGridLayoutConfiguration::encodeInXMLWithStreamWriterVersionTwo() const +{ + AString xmlString; + QXmlStreamWriter writer(&xmlString); + writer.setAutoFormatting(true); + + writer.writeStartElement(s_v2_rootTagName); + writer.writeAttribute(s_v2_versionAttributeName, "2"); + + writer.writeTextElement(s_nameTagName, getName()); + writer.writeTextElement(s_uniqueIdentifierTagName, getUniqueIdentifier()); + writer.writeTextElement(s_v2_centeringCorrectionName, AString::fromBool(m_centeringCorrectionEnabled)); + + encodeRowColumnElement(writer, s_v2_columnsTagName, m_columns); + encodeRowColumnElement(writer, s_v2_rowsTagName, m_rows); + + writer.writeEndElement(); + + return xmlString; +} + +/** + * Encode a vector of elements into xml. + * + * @param writer + * The XML stream writer. + * @param tagName + * Tag name for enclosing the elements. + * @param elements + * Vector of elements added to XML. + */ +void +TileTabsGridLayoutConfiguration::encodeRowColumnElement(QXmlStreamWriter& writer, + const AString tagName, + const std::vector& elements) const +{ + writer.writeStartElement(tagName); + + for (const auto e : elements) { + writer.writeStartElement(s_v2_elementTagName); + writer.writeAttribute(s_v2_contentTypeAttributeName, TileTabsGridRowColumnContentTypeEnum::toName(e.getContentType())); + writer.writeAttribute(s_v2_stretchTypeAttributeName, TileTabsGridRowColumnStretchTypeEnum::toName(e.getStretchType())); + writer.writeAttribute(s_v2_percentStretchAttributeName, AString::number(e.getPercentStretch(), 'f', 2)); + writer.writeAttribute(s_v2_weightStretchAttributeName, AString::number(e.getWeightStretch(), 'f', 2)); + writer.writeEndElement(); + } + + writer.writeEndElement(); +} + +/** + * Decode the configuration using the given XML stream reader and root element + * If there is an error, xml.raiseError() should be used to specify the error + * and caller of this method can test for the error using xml.isError(). + * + * @param xml + * The XML stream reader. + * @param rootElement + * The root element. + */ +void +TileTabsGridLayoutConfiguration::decodeFromXML(QXmlStreamReader& xml, + const QString& rootElementText) +{ + CaretAssert( ! rootElementText.isEmpty()); + + m_centeringCorrectionEnabled = false; + + if (rootElementText == s_v1_rootTagName) { + decodeFromXMLWithStreamReaderVersionOne(xml); + } + else if (rootElementText == s_v2_rootTagName) { + /* + * Version 2 uses a different root tag than version 1. The reason is that + * the older code for decoding from XML will throw an exception if it + * encounters invalid elements or the version number is invalid. The problem + * is that the exception is not caught and wb_view will terminate. + */ + QString versionNumberText("Unknown"); + const QXmlStreamAttributes atts = xml.attributes(); + if (atts.hasAttribute(s_v2_versionAttributeName)) { + versionNumberText = atts.value(s_v2_versionAttributeName).toString(); + } + + if (versionNumberText == "2") { + decodeFromXMLWithStreamReaderVersionTwo(xml); + } + else { + xml.raiseError("TileTabsGridLayoutConfiguration invalid version=" + + versionNumberText); + } + } + else { + xml.raiseError("TileTabsGridLayoutConfiguration first element is " + + xml.name().toString() + + " but should be " + + s_v1_rootTagName + + " or " + + s_v2_rootTagName); + } + +// const bool debugFlag(false); +// if (debugFlag) { +// AString xmlText = encodeInXMLWithStreamWriterVersionTwo(); +// std::cout << std::endl << "NEW: " << xmlText << std::endl << std::endl; +// AString em; +// TileTabsGridLayoutConfiguration temp; +// QXmlStreamReader tempReader(xmlText); +// tempReader.readNextStartElement(); +// temp.decodeFromXMLWithStreamReaderVersionTwo(tempReader); +// if (tempReader.hasError()) { +// std::cout << "Decode error: " << tempReader.errorString() << std::endl; +// } +// else { +// std::cout << "Decoded: " << temp.toString() << std::endl; +// } +// +// std::cout << std::endl; +// } +// return true; +} + +/** + * Decode Version One of the tile tabs configuration from XML with stream reader. + * + * @param xml + * Stream XML parser. + */ +void +TileTabsGridLayoutConfiguration::decodeFromXMLWithStreamReaderVersionOne(QXmlStreamReader& xml) +{ + std::set invalidElements; + + AString name; + AString uniqueID; + std::vector rowStretchFactors; + std::vector columnStretchFactors; + int32_t numberOfRows(0); + int32_t numberOfColumns(0); + + QString message; + + while ( ! xml.atEnd()) { + xml.readNext(); + + if (xml.isStartElement()) { + const QStringRef tagName(xml.name()); + + if (tagName == s_v1_versionTagName) { + /* ignore */ + } + else if (tagName == s_nameTagName) { + name = xml.readElementText(); + } + else if (tagName == s_uniqueIdentifierTagName) { + uniqueID = xml.readElementText(); + } + else if (tagName == s_v1_rowStretchFactorsTagName) { + QXmlStreamAttributes atts = xml.attributes(); + if (atts.hasAttribute(s_v1_rowStretchFactorsSelectedCountAttributeName)) { + numberOfRows = atts.value(s_v1_rowStretchFactorsSelectedCountAttributeName).toInt(); + } + + AString::toNumbers(xml.readElementText(), rowStretchFactors); + } + else if (tagName == s_v1_columnStretchFactorsTagName) { + QXmlStreamAttributes atts = xml.attributes(); + if (atts.hasAttribute(s_v1_columnStretchFactorsSelectedCountAttributeName)) { + numberOfColumns = atts.value(s_v1_columnStretchFactorsSelectedCountAttributeName).toInt(); + } + AString::toNumbers(xml.readElementText(), columnStretchFactors); + } + else { + invalidElements.insert(tagName.toString()); + xml.skipCurrentElement(); + } + } + } + + static int32_t missingNameCounter = 1; + if (name.isEmpty()) { + name = ("Config_V1_" + + AString::number(missingNameCounter)); + missingNameCounter++; + } + if (uniqueID.isEmpty()) { + uniqueID = SystemUtilities::createUniqueID(); + } + if (rowStretchFactors.empty()) { + message.append(s_v1_rowStretchFactorsTagName + + " not found or invalid. "); + } + if (columnStretchFactors.empty()) { + message.append(s_v1_columnStretchFactorsTagName + + " not found or invalid. "); + } + if (numberOfRows <= 0) { + message.append(s_v1_rowStretchFactorsTagName + + " attribute " + + s_v1_rowStretchFactorsSelectedCountAttributeName + + " is missing or invalid." ); + } + if (numberOfRows <= 0) { + message.append(s_v1_columnStretchFactorsTagName + + " attribute " + + s_v1_columnStretchFactorsSelectedCountAttributeName + + " is missing or invalid." ); + } + + if ( ! invalidElements.empty()) { + /* + * If invalid elements were encountered, don't throw + */ + AString msg("Invalid element(s) ignored: "); + for (const auto s : invalidElements) { + msg.append(s + " "); + } + CaretLogWarning(msg); + } + + if (message.isEmpty()) { + setName(name); + setUniqueIdentifierProtected(uniqueID); + + m_rows.clear(); + m_columns.clear(); + + for (int32_t i = 0; i < numberOfRows; i++) { + TileTabsGridRowColumnElement element; + CaretAssertVectorIndex(rowStretchFactors, i); + element.setWeightStretch(rowStretchFactors[i]); + element.setContentType(TileTabsGridRowColumnContentTypeEnum::TAB); + element.setStretchType(TileTabsGridRowColumnStretchTypeEnum::WEIGHT); + m_rows.push_back(element); + } + CaretAssert(numberOfRows == static_cast(m_rows.size())); + + for (int32_t i = 0; i < numberOfColumns; i++) { + TileTabsGridRowColumnElement element; + CaretAssertVectorIndex(columnStretchFactors, i); + element.setWeightStretch(columnStretchFactors[i]); + element.setContentType(TileTabsGridRowColumnContentTypeEnum::TAB); + element.setStretchType(TileTabsGridRowColumnStretchTypeEnum::WEIGHT); + m_columns.push_back(element); + } + CaretAssert(numberOfColumns == static_cast(m_columns.size())); + } + else { + xml.raiseError(message); + } +} + +/** + * Decode Version Two of the tile tabs configuration from XML with stream reader. + * + * @param xml + * Stream XML parser. + */ +void +TileTabsGridLayoutConfiguration::decodeFromXMLWithStreamReaderVersionTwo(QXmlStreamReader& xml) +{ + m_rows.clear(); + m_columns.clear(); + setName(""); + setUniqueIdentifierProtected(""); + + std::set invalidElements; + + AString name; + AString uniqueID; + + QString message; + + enum class ReadMode { + OTHER, + COLUMNS, + ROWS + }; + ReadMode readMode = ReadMode::OTHER; + + AString centeringCorrectionTextString; + + while ( ! xml.atEnd()) { + xml.readNext(); + + if (xml.isStartElement()) { + const QStringRef tagName(xml.name()); + + if (tagName == s_nameTagName) { + name = xml.readElementText(); + } + else if (tagName == s_uniqueIdentifierTagName) { + uniqueID = xml.readElementText(); + } + else if (tagName == s_v2_columnsTagName) { + readMode = ReadMode::COLUMNS; + } + else if (tagName == s_v2_rowsTagName) { + readMode = ReadMode::ROWS; + } + else if (tagName == s_v2_centeringCorrectionName) { + centeringCorrectionTextString = xml.readElementText(); + } + else if (tagName == s_v2_elementTagName) { + switch (readMode) { + case ReadMode::OTHER: + CaretAssert(0); + break; + case ReadMode::COLUMNS: + { + AString errorMessage; + TileTabsGridRowColumnElement e; + if (decodeRowColumnElement(xml, e, errorMessage)) { + m_columns.push_back(e); + } + else { + message.append(errorMessage); + } + } + break; + case ReadMode::ROWS: + { + AString errorMessage; + TileTabsGridRowColumnElement e; + if (decodeRowColumnElement(xml, e, errorMessage)) { + m_rows.push_back(e); + } + else { + message.append(errorMessage); + } + } + break; + } + } + else { + invalidElements.insert(tagName.toString()); + xml.skipCurrentElement(); + } + } + else if (xml.isEndElement()) { + const QStringRef tagName(xml.name()); + + if (tagName == s_v2_columnsTagName) { + readMode = ReadMode::OTHER; + } + else if (tagName == s_v2_rowsTagName) { + readMode = ReadMode::OTHER; + } + } + } + + static int32_t missingNameCounter = 1; + if (name.isEmpty()) { + name = ("Config_" + + AString::number(missingNameCounter)); + missingNameCounter++; + } + if (uniqueID.isEmpty()) { + uniqueID = SystemUtilities::createUniqueID(); + } + + if ( ! invalidElements.empty()) { + /* + * If invalid elements were encountered, don't throw + */ + AString msg("Invalid element(s) ignored: "); + for (const auto s : invalidElements) { + msg.append(s + " "); + } + CaretLogWarning(msg); + } + + if (message.isEmpty()) { + setName(name); + setUniqueIdentifierProtected(uniqueID); + + /* + * Only set centering correction if it is found. This allows usage + * of the default value in the event this is not found in the XML. + */ + if ( ! centeringCorrectionTextString.isEmpty()) { + m_centeringCorrectionEnabled = centeringCorrectionTextString.toBool(); + } + + } + else { + xml.raiseError(message); + } +} + +/** + * Decode elements in a row or column. + * + * @param reader + * The XML stream reader. + * @param element + * row/column element that is read + * @param errorMessageOut + * Contains error information. + * @return + * True if read successfully, else false. + */ +bool +TileTabsGridLayoutConfiguration::decodeRowColumnElement(QXmlStreamReader& reader, + TileTabsGridRowColumnElement& element, + AString& errorMessageOut) +{ + const QXmlStreamAttributes atts = reader.attributes(); + + errorMessageOut.clear(); + + if (atts.hasAttribute(s_v2_contentTypeAttributeName)) { + bool validFlag(false); + const AString s = atts.value(s_v2_contentTypeAttributeName).toString(); + element.setContentType(TileTabsGridRowColumnContentTypeEnum::fromName(s, &validFlag)); + if ( ! validFlag) { + errorMessageOut.append("Content type \"" + s + "\" is not valid. "); + } + } + else { + errorMessageOut.append("Content type is missing. "); + } + + if (atts.hasAttribute(s_v2_stretchTypeAttributeName)) { + bool validFlag(false); + const AString s = atts.value(s_v2_stretchTypeAttributeName).toString(); + element.setStretchType(TileTabsGridRowColumnStretchTypeEnum::fromName(s, &validFlag)); + if ( ! validFlag) { + errorMessageOut.append("Stretch type \"" + s + "\" is not valid. "); + } + } + else { + errorMessageOut.append("Stretch type is missing. "); + } + + if (atts.hasAttribute(s_v2_percentStretchAttributeName)) { + const float f = atts.value(s_v2_percentStretchAttributeName).toFloat(); + if ((f >= 0.0) && (f < 100.0)) { + element.setPercentStretch(f); + } + else { + errorMessageOut.append("Stretch percentage=" + AString::number(f) + " is invalid."); + } + } + else { + errorMessageOut.append("Stretch percentage is missing. "); + } + + if (atts.hasAttribute(s_v2_weightStretchAttributeName)) { + const float f = atts.value(s_v2_weightStretchAttributeName).toFloat(); + if ((f >= 0.0) && (f < 100.0)) { + element.setWeightStretch(f); + } + else { + errorMessageOut.append("Stretch weight=" + AString::number(f) + " is invalid."); + } + } + else { + errorMessageOut.append("Stretch weight is missing. "); + } + + if (errorMessageOut.isEmpty()) { + return true; + } + return false; +} + +/** + * @return String version of an instance. + */ +AString +TileTabsGridLayoutConfiguration::toString() const +{ + AString s = TileTabsBaseConfiguration::toString(); + + int32_t indx(0); + for (const auto item : m_columns) { + s.append(" Column " + AString::number(indx) + ": " + item.toString() + "\n"); + indx++; + } + indx = 0; + for (const auto item : m_rows) { + s.append(" Row " + AString::number(indx) + ": " + item.toString() + "\n"); + indx++; + } + + return s; +} + diff --git a/src/Common/TileTabsGridLayoutConfiguration.h b/src/Common/TileTabsGridLayoutConfiguration.h new file mode 100644 index 000000000..35eeb543b --- /dev/null +++ b/src/Common/TileTabsGridLayoutConfiguration.h @@ -0,0 +1,178 @@ +#ifndef __TILE_TABS_GRID_LAYOUT_CONFIGURATION_H__ +#define __TILE_TABS_GRID_LAYOUT_CONFIGURATION_H__ + +/*LICENSE_START*/ +/* + * Copyright (C) 2014 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + +#include "CaretException.h" +#include "TileTabsBaseConfiguration.h" +#include "TileTabsGridModeEnum.h" +#include "TileTabsGridRowColumnElement.h" + +class QXmlStreamReader; +class QXmlStreamWriter; + +namespace caret { + + class TileTabsGridLayoutConfiguration : public TileTabsBaseConfiguration { + + public: + TileTabsGridLayoutConfiguration(); + + virtual ~TileTabsGridLayoutConfiguration(); + + TileTabsGridLayoutConfiguration(const TileTabsGridLayoutConfiguration& obj); + + TileTabsGridLayoutConfiguration& operator=(const TileTabsGridLayoutConfiguration& obj); + + void copy(const TileTabsGridLayoutConfiguration& rhs); + + virtual TileTabsGridLayoutConfiguration* newCopyWithNewUniqueIdentifier() const override; + + bool getRowHeightsAndColumnWidthsForWindowSize(const int32_t windowWidth, + const int32_t windowHeight, + const int32_t numberOfModelsToDraw, + const TileTabsGridModeEnum::Enum configurationMode, + std::vector& rowHeightsOut, + std::vector& columnWidthsOut); + + int32_t getNumberOfRows() const; + + void setNumberOfRows(const int32_t numberOfRows); + + int32_t getNumberOfColumns() const; + + void setNumberOfColumns(const int32_t numberOfColumns); + + TileTabsGridRowColumnElement* getColumn(const int32_t columnIndex); + + const TileTabsGridRowColumnElement* getColumn(const int32_t columnIndex) const; + + TileTabsGridRowColumnElement* getRow(const int32_t rowIndex); + + const TileTabsGridRowColumnElement* getRow(const int32_t rowIndex) const; + + void updateAutomaticConfigurationRowsAndColumns(const int32_t numberOfTabs); + + bool isCenteringCorrectionEnabled() const; + + void setCenteringCorrectionEnabled(const bool status); + + virtual AString toString() const override; + + static void getRowsAndColumnsForNumberOfTabs(const int32_t numberOfTabs, + int32_t& numberOfRowsOut, + int32_t& numberOfColumnsOut); + // ADD_NEW_METHODS_HERE + + protected: + public: + virtual void decodeFromXML(QXmlStreamReader& xml, + const QString& rootElement) override; + + virtual void encodeInXML(AString& xmlTextOut) const override; + + private: + void copyHelperTileTabsGridLayoutConfiguration(const TileTabsGridLayoutConfiguration& obj); + + void decodeFromXMLWithStreamReaderVersionOne(QXmlStreamReader& xml); + + void decodeFromXMLWithStreamReaderVersionTwo(QXmlStreamReader& xml); + + AString encodeVersionInXML(const int32_t versionNumber) const; + + AString encodeInXMLWithStreamWriterVersionOne() const; + + AString encodeInXMLWithStreamWriterVersionTwo() const; + + void encodeRowColumnElement(QXmlStreamWriter& writer, + const AString tagName, + const std::vector& elements) const; + + bool decodeRowColumnElement(QXmlStreamReader& reader, + TileTabsGridRowColumnElement& element, + AString& errorMessageOut); + + void initialize(); + + // ADD_NEW_MEMBERS_HERE + + std::vector m_columns; + + std::vector m_rows; + + bool m_centeringCorrectionEnabled = false; + + static const AString s_nameTagName; + static const AString s_uniqueIdentifierTagName; + + static const AString s_v1_rootTagName; + static const AString s_v1_versionTagName; + static const AString s_v1_versionNumberAttributeName; + static const AString s_v1_columnStretchFactorsTagName; + static const AString s_v1_columnStretchFactorsSelectedCountAttributeName; + static const AString s_v1_columnStretchFactorsTotalCountAttributeName; + static const AString s_v1_rowStretchFactorsTagName; + static const AString s_v1_rowStretchFactorsSelectedCountAttributeName; + static const AString s_v1_rowStretchFactorsTotalCountAttributeName; + + static const AString s_v2_rootTagName; + static const AString s_v2_versionAttributeName; + static const AString s_v2_columnsTagName; + static const AString s_v2_contentTypeAttributeName; + static const AString s_v2_elementTagName; + static const AString s_v2_percentStretchAttributeName; + static const AString s_v2_rowsTagName; + static const AString s_v2_stretchTypeAttributeName; + static const AString s_v2_weightStretchAttributeName; + static const AString s_v2_centeringCorrectionName; + + + friend class TileTabsBaseConfiguration; + }; + +#ifdef __TILE_TABS_GRID_LAYOUT_CONFIGURATION_DECLARE__ + const AString TileTabsGridLayoutConfiguration::s_nameTagName = "Name"; + const AString TileTabsGridLayoutConfiguration::s_uniqueIdentifierTagName = "UniqueIdentifier"; + + const AString TileTabsGridLayoutConfiguration::s_v1_rootTagName = "TileTabsGridLayoutConfiguration"; + const AString TileTabsGridLayoutConfiguration::s_v1_versionTagName = "Version"; + const AString TileTabsGridLayoutConfiguration::s_v1_versionNumberAttributeName = "Number"; + const AString TileTabsGridLayoutConfiguration::s_v1_columnStretchFactorsTagName = "ColumnStretchFactors"; + const AString TileTabsGridLayoutConfiguration::s_v1_columnStretchFactorsSelectedCountAttributeName = "SelectedRowCount"; + const AString TileTabsGridLayoutConfiguration::s_v1_columnStretchFactorsTotalCountAttributeName = "TotalRowCount"; + const AString TileTabsGridLayoutConfiguration::s_v1_rowStretchFactorsTagName = "RowStretchFactors"; + const AString TileTabsGridLayoutConfiguration::s_v1_rowStretchFactorsSelectedCountAttributeName = "SelectedColumnCount"; + const AString TileTabsGridLayoutConfiguration::s_v1_rowStretchFactorsTotalCountAttributeName = "TotalColumnCount"; + + const AString TileTabsGridLayoutConfiguration::s_v2_rootTagName = "TileTabsGridLayoutConfigurationTwo"; + const AString TileTabsGridLayoutConfiguration::s_v2_versionAttributeName = "Version"; + const AString TileTabsGridLayoutConfiguration::s_v2_columnsTagName = "Columns"; + const AString TileTabsGridLayoutConfiguration::s_v2_contentTypeAttributeName = "ContentType"; + const AString TileTabsGridLayoutConfiguration::s_v2_elementTagName = "Element"; + const AString TileTabsGridLayoutConfiguration::s_v2_percentStretchAttributeName = "PercentStretch"; + const AString TileTabsGridLayoutConfiguration::s_v2_rowsTagName = "Rows"; + const AString TileTabsGridLayoutConfiguration::s_v2_stretchTypeAttributeName = "StretchType"; + const AString TileTabsGridLayoutConfiguration::s_v2_weightStretchAttributeName = "WeightStretch"; + const AString TileTabsGridLayoutConfiguration::s_v2_centeringCorrectionName = "CenteringCorrection"; +#endif // __TILE_TABS_GRID_LAYOUT_CONFIGURATION_DECLARE__ + +} // namespace +#endif //__TILE_TABS_GRID_LAYOUT_CONFIGURATION_H__ -- GitLab From 0c171332f4f018c23292584065882976ed706e68 Mon Sep 17 00:00:00 2001 From: John Harwell Date: Tue, 16 Jul 2019 14:29:03 -0500 Subject: [PATCH 389/427] WB-860 NIFTI Volume Timeseries Issues: For now, only create dynamic connectivity for a metric or volume file that contains at least 8 maps (time points). --- src/Files/MetricFile.cxx | 3 ++- src/Files/VolumeFile.cxx | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/Files/MetricFile.cxx b/src/Files/MetricFile.cxx index 3cb6687ac..53df0d2ad 100644 --- a/src/Files/MetricFile.cxx +++ b/src/Files/MetricFile.cxx @@ -841,7 +841,8 @@ MetricDynamicConnectivityFile* MetricFile::getMetricDynamicConnectivityFile() { if (m_lazyInitializedDynamicConnectivityFile == NULL) { - if (getNumberOfMaps() > 1) { + const int32_t minimumNumberOfTimePoints(8); + if (getNumberOfMaps() >= minimumNumberOfTimePoints) { m_lazyInitializedDynamicConnectivityFile.reset(new MetricDynamicConnectivityFile(this)); m_lazyInitializedDynamicConnectivityFile->initializeFile(); diff --git a/src/Files/VolumeFile.cxx b/src/Files/VolumeFile.cxx index 05c7a9236..eded9f6da 100644 --- a/src/Files/VolumeFile.cxx +++ b/src/Files/VolumeFile.cxx @@ -2545,7 +2545,8 @@ VolumeFile::getVolumeDynamicConnectivityFile() std::vector dims; getDimensions(dims); if (dims.size() >= 4) { - if (dims[3] > 1) { + const int64_t minimumNumberOfTimePoints(8); + if (dims[3] > minimumNumberOfTimePoints) { m_lazyInitializedDynamicConnectivityFile.reset(new VolumeDynamicConnectivityFile(this)); m_lazyInitializedDynamicConnectivityFile->initializeFile(); -- GitLab From ec47f1fcb9cf6e9deb34d0e3b815e2193f02fb3e Mon Sep 17 00:00:00 2001 From: John Harwell Date: Tue, 16 Jul 2019 16:09:19 -0500 Subject: [PATCH 390/427] WB-860 NIFTI Volume Timeseries Issues: A warning about an unrestored scene element was traced to not correctly setting the palette normalization mode for metric dynamic and volume dynamic files. --- src/Files/GiftiTypeFile.cxx | 67 ++++++++++++++++++++- src/Files/MetricDynamicConnectivityFile.cxx | 3 +- src/Files/VolumeFile.cxx | 9 ++- 3 files changed, 73 insertions(+), 6 deletions(-) diff --git a/src/Files/GiftiTypeFile.cxx b/src/Files/GiftiTypeFile.cxx index 2d1dcd00e..6de85ed84 100644 --- a/src/Files/GiftiTypeFile.cxx +++ b/src/Files/GiftiTypeFile.cxx @@ -898,9 +898,70 @@ GiftiTypeFile::getPaletteNormalizationModesSupported(std::vectorreset(); std::vector dataSum(m_numberOfVertices, 0.0); - const int64_t vertexCount = static_cast(nodeIndices.size()); + int64_t vertexCount(0); for (auto nodeIndex : nodeIndices) { std::vector data; if (getConnectivityForVertexIndex(nodeIndex, data)) { @@ -378,6 +378,7 @@ MetricDynamicConnectivityFile::loadAverageDataForSurfaceNodes(const int32_t surf for (int32_t i = 0; i < m_numberOfVertices; i++) { dataSum[i] += data[i]; } + vertexCount++; } } diff --git a/src/Files/VolumeFile.cxx b/src/Files/VolumeFile.cxx index eded9f6da..360abbe56 100644 --- a/src/Files/VolumeFile.cxx +++ b/src/Files/VolumeFile.cxx @@ -1314,8 +1314,13 @@ VolumeFile::getPaletteNormalizationModesSupported(std::vector Date: Wed, 17 Jul 2019 12:10:15 -0500 Subject: [PATCH 391/427] WB-860 NIFTI Volume Timeseries Issues: Correct dynamic connectivity of an ROI for metric dynamic file. --- src/Common/ConnectivityCorrelation.cxx | 217 +++++++++++++++++--- src/Common/ConnectivityCorrelation.h | 95 +++++++-- src/Files/MetricDynamicConnectivityFile.cxx | 137 ++++++------ src/Files/MetricDynamicConnectivityFile.h | 2 + 4 files changed, 344 insertions(+), 107 deletions(-) diff --git a/src/Common/ConnectivityCorrelation.cxx b/src/Common/ConnectivityCorrelation.cxx index c049cde56..01a355678 100644 --- a/src/Common/ConnectivityCorrelation.cxx +++ b/src/Common/ConnectivityCorrelation.cxx @@ -326,6 +326,80 @@ ConnectivityCorrelation::initializeWithTimePoints(const std::vector& brainordinateIndices, + std::vector& dataOut) +{ + dataOut.resize(m_numberOfBrainordinates); + std::fill(dataOut.begin(), dataOut.end(), 0.0); + + const int64_t numBrainordinatesInROI = static_cast(brainordinateIndices.size()); + if (m_numberOfTimePoints > 1) { + if (numBrainordinatesInROI > 0) { + std::vector dataAverage(m_numberOfTimePoints, 0.0); + + for (int32_t iTime = 0; iTime < m_numberOfTimePoints; iTime++) { + double timePointSum(0.0); + for (int64_t jBrain = 0; jBrain < numBrainordinatesInROI; jBrain++) { + CaretAssertVectorIndex(brainordinateIndices, jBrain); + timePointSum += getDataValue(brainordinateIndices[jBrain], iTime); + } + dataAverage[iTime] = timePointSum / static_cast(numBrainordinatesInROI); + } + + double sum(0.0); + double sumSquared(0.0); + for (int64_t j = 0; j < m_numberOfTimePoints; j++) { + CaretAssertVectorIndex(dataAverage, j); + const float d = dataAverage[j]; + sum += d; + sumSquared += (d * d); + } + const float mean = (sum / m_numberOfTimePoints); + const float ssxxSquared = (sumSquared - (m_numberOfTimePoints * mean * mean)); + const float ssxx = std::sqrt(ssxxSquared); + + const float* dataPtr = &dataAverage[0]; +#pragma omp CARET_PARFOR schedule(dynamic) + for (int32_t iBrain = 0; iBrain < m_numberOfBrainordinates; iBrain++) { + CaretAssertVectorIndex(dataOut, iBrain); + switch (m_dataType) { + case DataTypeEnum::BRAINORDINATES_CONTIGUOUS_DATA: + dataOut[iBrain] = correlationBrainordinateContiguousDataAux(dataPtr, + mean, + ssxx, + iBrain); + break; + case DataTypeEnum::BRAINORDINATES_NON_CONTIGUOUS_DATA: + dataOut[iBrain] = correlationBrainordinateNonContiguousDataAux(dataPtr, + mean, + ssxx, + iBrain); + break; + case DataTypeEnum::INVALID: + CaretAssert(0); + break; + case DataTypeEnum::TIMEPOINTS: + dataOut[iBrain] = correlationTimePointDataAux(dataPtr, + mean, + ssxx, + iBrain); + break; + } + } + } + } +} + + /** * Get correlation from the given brainordinate to all other brainordinates * @@ -336,7 +410,7 @@ ConnectivityCorrelation::initializeWithTimePoints(const std::vector& dataOut) + std::vector& dataOut) { CaretAssert(m_numberOfBrainordinates > 1); CaretAssert(m_numberOfTimePoints > 1); @@ -384,25 +458,53 @@ ConnectivityCorrelation::correlationBrainordinateContiguousData(const int64_t fr CaretAssert(m_dataType == DataTypeEnum::BRAINORDINATES_CONTIGUOUS_DATA); CaretAssertVectorIndex(m_brainordinateData, fromBrainordinateIndex); - CaretAssertVectorIndex(m_brainordinateData, toBrainordinateIndex); const BrainordinateData* fromData = m_brainordinateData[fromBrainordinateIndex].get(); + CaretAssertVectorIndex(m_meanSSData, fromBrainordinateIndex); + const BrainordinateMeanSS* fromMeanSS = m_meanSSData[fromBrainordinateIndex].get(); + + return correlationBrainordinateContiguousDataAux(fromData->m_data, + fromMeanSS->m_mean, + fromMeanSS->m_sqrt_ssxx, + toBrainordinateIndex); +} + +/** + * Get the correlation coefficient for the two given brainordinates + * that contain CONTIGOUS data + * + * @param fromBrainordinateData + * Data for the 'from' brainordinate + * @param fromBrainordinateMean + * Mean of the data for the 'from' brainordinate. + * @param fromBrainordinateSSXX + * Sum-squared of the data for the 'from' brainordinate. + * @param toBrainordinateIndex + * Index of the 'to' brainordinate. + */ +float +ConnectivityCorrelation::correlationBrainordinateContiguousDataAux(const float* fromBrainordinateData, + const float fromBrainordinateMean, + const float fromBrainordinateSSXX, + const int64_t toBrainordinateIndex) const +{ + CaretAssert(m_dataType == DataTypeEnum::BRAINORDINATES_CONTIGUOUS_DATA); + + CaretAssertVectorIndex(m_brainordinateData, toBrainordinateIndex); const BrainordinateData* toGroup = m_brainordinateData[toBrainordinateIndex].get(); - CaretAssertVectorIndex(m_meanSSData, fromBrainordinateIndex); CaretAssertVectorIndex(m_meanSSData, toBrainordinateIndex); - const BrainordinateMeanSS* fromMeanSS = m_meanSSData[fromBrainordinateIndex].get(); const BrainordinateMeanSS* toMeanSS = m_meanSSData[toBrainordinateIndex].get(); - double xySum = dsdot(fromData->m_data, + double xySum = dsdot(fromBrainordinateData, toGroup->m_data, m_numberOfTimePoints); - const double ssxy = xySum - (m_numberOfTimePoints * fromMeanSS->m_mean * toMeanSS->m_mean); + const double ssxy = xySum - (m_numberOfTimePoints * fromBrainordinateMean * toMeanSS->m_mean); float correlationCoefficient = 0.0; - if ((fromMeanSS->m_sqrt_ssxx > 0.0) + if ((fromBrainordinateSSXX > 0.0) && (toMeanSS->m_sqrt_ssxx > 0.0)) { - correlationCoefficient = (ssxy / (fromMeanSS->m_sqrt_ssxx * toMeanSS->m_sqrt_ssxx)); + correlationCoefficient = (ssxy / (fromBrainordinateSSXX * toMeanSS->m_sqrt_ssxx)); } return correlationCoefficient; } @@ -419,40 +521,68 @@ ConnectivityCorrelation::correlationBrainordinateContiguousData(const int64_t fr float ConnectivityCorrelation::correlationBrainordinateNonContiguousData(const int64_t fromBrainordinateIndex, const int64_t toBrainordinateIndex) const +{ + CaretAssertVectorIndex(m_brainordinateData, fromBrainordinateIndex); + + std::vector data(m_numberOfTimePoints); + for (int32_t iTime = 0; iTime < m_numberOfTimePoints; iTime++) { + data[iTime] = getDataValue(fromBrainordinateIndex, iTime); + } + + CaretAssertVectorIndex(m_meanSSData, fromBrainordinateIndex); + const BrainordinateMeanSS* fromMeanSS = m_meanSSData[fromBrainordinateIndex].get(); + + + return correlationBrainordinateNonContiguousDataAux(&data[0], + fromMeanSS->m_mean, + fromMeanSS->m_sqrt_ssxx, + toBrainordinateIndex); +} + +/** + * Get the correlation coefficient for the two given brainordinates + * that contain NON-CONTIGOUS data + * + * @param fromBrainordinateData + * Data for the 'from' brainordinate + * @param fromBrainordinateMean + * Mean of the data for the 'from' brainordinate. + * @param fromBrainordinateSSXX + * Sum-squared of the data for the 'from' brainordinate. + * @param toBrainordinateIndex + * Index of the 'to' brainordinate. + */ +float +ConnectivityCorrelation::correlationBrainordinateNonContiguousDataAux(const float* fromBrainordinateData, + const float fromBrainordinateMean, + const float fromBrainordinateSSXX, + const int64_t toBrainordinateIndex) const { CaretAssert(m_dataType == DataTypeEnum::BRAINORDINATES_NON_CONTIGUOUS_DATA); - CaretAssertVectorIndex(m_brainordinateData, fromBrainordinateIndex); CaretAssertVectorIndex(m_brainordinateData, toBrainordinateIndex); - const BrainordinateData* fromGroup = m_brainordinateData[fromBrainordinateIndex].get(); const BrainordinateData* toGroup = m_brainordinateData[toBrainordinateIndex].get(); - CaretAssertVectorIndex(m_meanSSData, fromBrainordinateIndex); CaretAssertVectorIndex(m_meanSSData, toBrainordinateIndex); - const BrainordinateMeanSS* fromMeanSS = m_meanSSData[fromBrainordinateIndex].get(); const BrainordinateMeanSS* toMeanSS = m_meanSSData[toBrainordinateIndex].get(); - const float* fromData = fromGroup->m_data; - const int64_t fromStride = fromGroup->m_dataStride; const float* toData = toGroup->m_data; const int64_t toStride = toGroup->m_dataStride; - int64_t fromOffset(0); int64_t toOffset(0); double xySum(0.0); for (int32_t i = 0; i < m_numberOfTimePoints; i++) { - xySum += (fromData[fromOffset] * toData[toOffset]); - fromOffset += fromStride; + xySum += (fromBrainordinateData[i] * toData[toOffset]); toOffset += toStride; } - const double ssxy = xySum - (m_numberOfTimePoints * fromMeanSS->m_mean * toMeanSS->m_mean); + const double ssxy = xySum - (m_numberOfTimePoints * fromBrainordinateMean * toMeanSS->m_mean); float correlationCoefficient = 0.0; - if ((fromMeanSS->m_sqrt_ssxx > 0.0) + if ((fromBrainordinateSSXX > 0.0) && (toMeanSS->m_sqrt_ssxx > 0.0)) { - correlationCoefficient = (ssxy / (fromMeanSS->m_sqrt_ssxx * toMeanSS->m_sqrt_ssxx)); + correlationCoefficient = (ssxy / (fromBrainordinateSSXX * toMeanSS->m_sqrt_ssxx)); } return correlationCoefficient; } @@ -475,27 +605,62 @@ ConnectivityCorrelation::correlationTimePointData(const int64_t fromBrainordinat CaretAssert((fromBrainordinateIndex >= 0) && (fromBrainordinateIndex < m_numberOfBrainordinates)); CaretAssert((toBrainordinateIndex >= 0) && (toBrainordinateIndex < m_numberOfBrainordinates)); + std::vector data(m_numberOfTimePoints); + for (int32_t iTime = 0; iTime < m_numberOfTimePoints; iTime++) { + data[iTime] = getDataValue(fromBrainordinateIndex, iTime); + } + CaretAssertVectorIndex(m_meanSSData, fromBrainordinateIndex); - CaretAssertVectorIndex(m_meanSSData, toBrainordinateIndex); const BrainordinateMeanSS* fromMeanSS = m_meanSSData[fromBrainordinateIndex].get(); + + + return correlationTimePointDataAux(&data[0], + fromMeanSS->m_mean, + fromMeanSS->m_sqrt_ssxx, + toBrainordinateIndex); +} + +/** + * Get the correlation coefficient for the two given brainordinates + * that contain NON-CONTIGOUS data + * + * @param fromBrainordinateData + * Data for the 'from' brainordinate + * @param fromBrainordinateMean + * Mean of the data for the 'from' brainordinate. + * @param fromBrainordinateSSXX + * Sum-squared of the data for the 'from' brainordinate. + * @param toBrainordinateIndex + * Index of the 'to' brainordinate. + */ +float +ConnectivityCorrelation::correlationTimePointDataAux(const float* fromBrainordinateData, + const float fromBrainordinateMean, + const float fromBrainordinateSSXX, + const int64_t toBrainordinateIndex) const +{ + CaretAssert(m_dataType == DataTypeEnum::TIMEPOINTS); + + CaretAssert((toBrainordinateIndex >= 0) && (toBrainordinateIndex < m_numberOfBrainordinates)); + + CaretAssertVectorIndex(m_meanSSData, toBrainordinateIndex); const BrainordinateMeanSS* toMeanSS = m_meanSSData[toBrainordinateIndex].get(); double xySum(0.0); for (int32_t i = 0; i < m_numberOfTimePoints; i++) { CaretAssertVectorIndex(m_timePointData, i); const TimePointData* tpd = m_timePointData[i].get(); - const int64_t fromOffset = (fromBrainordinateIndex * tpd->m_dataStride); const int64_t toOffset = (toBrainordinateIndex * tpd->m_dataStride); - xySum += (tpd->m_data[fromOffset] * tpd->m_data[toOffset]); + xySum += (fromBrainordinateData[i] * tpd->m_data[toOffset]); } - const double ssxy = xySum - (m_numberOfTimePoints * fromMeanSS->m_mean * toMeanSS->m_mean); + const double ssxy = xySum - (m_numberOfTimePoints * fromBrainordinateMean * toMeanSS->m_mean); float correlationCoefficient = 0.0; - if ((fromMeanSS->m_sqrt_ssxx > 0.0) + if ((fromBrainordinateSSXX > 0.0) && (toMeanSS->m_sqrt_ssxx > 0.0)) { - correlationCoefficient = (ssxy / (fromMeanSS->m_sqrt_ssxx * toMeanSS->m_sqrt_ssxx)); + correlationCoefficient = (ssxy / (fromBrainordinateSSXX * toMeanSS->m_sqrt_ssxx)); } return correlationCoefficient; } diff --git a/src/Common/ConnectivityCorrelation.h b/src/Common/ConnectivityCorrelation.h index 4c9266308..4f2254959 100644 --- a/src/Common/ConnectivityCorrelation.h +++ b/src/Common/ConnectivityCorrelation.h @@ -25,10 +25,10 @@ #include +#include "CaretAssert.h" #include "CaretObject.h" - namespace caret { class ConnectivityCorrelation : public CaretObject { @@ -63,6 +63,9 @@ namespace caret { void getCorrelationForBrainordinate(const int64_t brainordinateIndex, std::vector& dataOut); + void getCorrelationForBrainordinateROI(const std::vector& brainordinateIndices, + std::vector& dataOut); + // ADD_NEW_METHODS_HERE @@ -77,25 +80,33 @@ namespace caret { ConnectivityCorrelation(const DataTypeEnum dataType); + /** + * Contains timepoints for one brainordinate + */ class BrainordinateData { public: /** * Constructor. * * @param data - * Pointer to data + * Pointer to data containing a brainordinate's timepoints * @param dataStride * Offset between consecutive elements (1 for contiguous data) - * @param mean - * Mean value of 'data' - * @param sqrtSumSquared - * Square root of sum-squared of data */ BrainordinateData(const float* data, const int64_t dataStride) : m_data(data), m_dataStride(dataStride) { } + /* + * @return Data value for this brainordinate at the given time point index + * + * @param timePointIndex + * Index of the timepoint + */ + inline float getTimePointValue(const int64_t timePointIndex) const { + return m_data[timePointIndex * m_dataStride]; + } const float* m_data; const int64_t m_dataStride; @@ -121,6 +132,9 @@ namespace caret { const float m_sqrt_ssxx; }; + /** + * Contains all brainordinate values for one timepoint + */ class TimePointData { public: /** @@ -134,8 +148,16 @@ namespace caret { TimePointData(const float* data, const int64_t dataStride) : m_data(data), - m_dataStride(dataStride) - { + m_dataStride(dataStride) { } + + /* + * @return Data value for this brainordinate at the given time point index + * + * @param timePointIndex + * Index of the timepoint + */ + inline float getBrainordinateValue(const int64_t brainordinateIndex) const { + return m_data[brainordinateIndex * m_dataStride]; } const float* m_data; @@ -143,6 +165,38 @@ namespace caret { const int64_t m_dataStride; }; + /** + * Get the data value for the given brainordinate and timepoint indices + * + * @param brainordinateIndex + * Index of the brainordinate + * @param timePointIndex + * Index of the time point + * @return + * The data value + */ + inline float getDataValue(const int64_t brainordinateIndex, + const int64_t timePointIndex) const { + float dataValue(0.0); + + switch (m_dataType) { + case DataTypeEnum::BRAINORDINATES_CONTIGUOUS_DATA: + case DataTypeEnum::BRAINORDINATES_NON_CONTIGUOUS_DATA: + CaretAssertVectorIndex(m_brainordinateData, brainordinateIndex); + dataValue = m_brainordinateData[brainordinateIndex]->getTimePointValue(timePointIndex); + break; + case DataTypeEnum::INVALID: + CaretAssert(0); + break; + case DataTypeEnum::TIMEPOINTS: + CaretAssertVectorIndex(m_timePointData, timePointIndex); + dataValue = m_timePointData[timePointIndex]->getBrainordinateValue(brainordinateIndex); + break; + } + + return dataValue; + } + bool initializeWithBrainordinates(const std::vector& brainordinateDataPointers, const int64_t numberOfTimePoints, const int64_t nextTimePointStride, @@ -155,25 +209,30 @@ namespace caret { void computeBrainordinateMeanAndSumSquared(); -// void computeDataMeanAndSumSquared(const float* data, -// const int64_t dataCount, -// const int64_t dataStride, -// float& meanOut, -// float& ssxxOut) const; -// -// void computeDataMeanAndSumSquared(const int64_t brainordinateIndex, -// float& meanOut, -// float& sqrtSquaredOut) const; - float correlationBrainordinateContiguousData(const int64_t fromBrainordinateIndex, const int64_t toBrainordinateIndex) const; + float correlationBrainordinateContiguousDataAux(const float* fromBrainordinateData, + const float fromBrainordinateMean, + const float fromBrainordinateSSXX, + const int64_t toBrainordinateIndex) const; + float correlationBrainordinateNonContiguousData(const int64_t fromBrainordinateIndex, const int64_t toBrainordinateIndex) const; + float correlationBrainordinateNonContiguousDataAux(const float* fromBrainordinateData, + const float fromBrainordinateMean, + const float fromBrainordinateSSXX, + const int64_t toBrainordinateIndex) const; + float correlationTimePointData(const int64_t fromBrainordinateIndex, const int64_t toBrainordinateIndex) const; + float correlationTimePointDataAux(const float* fromBrainordinateData, + const float fromBrainordinateMean, + const float fromBrainordinateSSXX, + const int64_t toBrainordinateIndex) const; + DataTypeEnum m_dataType = DataTypeEnum::INVALID; int64_t m_numberOfBrainordinates = -1; diff --git a/src/Files/MetricDynamicConnectivityFile.cxx b/src/Files/MetricDynamicConnectivityFile.cxx index b44906577..050eb5b07 100644 --- a/src/Files/MetricDynamicConnectivityFile.cxx +++ b/src/Files/MetricDynamicConnectivityFile.cxx @@ -365,38 +365,40 @@ MetricDynamicConnectivityFile::loadAverageDataForSurfaceNodes(const int32_t surf if (getNumberOfNodes() != surfaceNumberOfNodes) { return validFlag; } + if (nodeIndices.size() < 2) { + return validFlag; + } + + ConnectivityCorrelation* connCorrelation = getConnectivityCorrelation(); + if (connCorrelation == NULL) { + return validFlag; + } clearVertexValues(); m_connectivityDataLoaded->reset(); - std::vector dataSum(m_numberOfVertices, 0.0); - int64_t vertexCount(0); - for (auto nodeIndex : nodeIndices) { - std::vector data; - if (getConnectivityForVertexIndex(nodeIndex, data)) { - CaretAssert(m_numberOfVertices == static_cast(data.size())); - for (int32_t i = 0; i < m_numberOfVertices; i++) { - dataSum[i] += data[i]; - } - vertexCount++; + float* dataPointer = const_cast(getValuePointerForColumn(0)); + CaretAssert(dataPointer); + + std::vector nodeIndices64(nodeIndices.begin(), nodeIndices.end()); + std::vector data(getNumberOfNodes()); + connCorrelation->getCorrelationForBrainordinateROI(nodeIndices64, + data); + const int64_t numData = static_cast(data.size()); + validFlag = (numData == getNumberOfNodes()); + if (validFlag) { + for (int64_t i = 0; i < numData; i++) { + dataPointer[i] = data[i]; } } - if (vertexCount >= 1.0) { - float* dataPointer = const_cast(getValuePointerForColumn(0)); - CaretAssert(dataPointer); - for (int32_t i = 0; i < m_numberOfVertices; i++) { - dataPointer[i] = static_cast(dataSum[i] / vertexCount); - } - - validFlag = true; - - m_connectivityDataLoaded->setSurfaceAverageNodeLoading(getStructure(), - getNumberOfNodes(), - nodeIndices); - } + + m_connectivityDataLoaded->setSurfaceAverageNodeLoading(getStructure(), + getNumberOfNodes(), + nodeIndices); + const AString mapName("Average_Vertex_Count_" - + AString::number(vertexCount, 'f', 0)); + + AString::number(static_cast(nodeIndices.size()))); m_dataLoadedName = mapName; const int32_t mapIndex(0); setMapName(mapIndex, @@ -424,45 +426,15 @@ MetricDynamicConnectivityFile::getConnectivityForVertexIndex(const int32_t verte std::vector& vertexDataOut) { bool validFlag(false); - if ( ! m_connectivityCorrelationFailedFlag) { - if ((vertexIndex >= 0) - && (vertexIndex < getNumberOfNodes())) { - if (m_connectivityCorrelation == NULL) { - /* - * Need data and timepoint count from parent volume - * IJK dimensions are same in this and parent volume - */ - CaretAssert(m_parentMetricFile); - const int32_t numberOfTimePoints = m_parentMetricFile->getNumberOfMaps(); - CaretAssert(numberOfTimePoints >= 2); - std::vector timePointData; - for (int32_t i = 0; i < numberOfTimePoints; i++) { - const float* dataPtr = m_parentMetricFile->getValuePointerForColumn(i); - CaretAssert(dataPtr); - timePointData.push_back(dataPtr); - } - const int64_t brainordinateStride(1); - AString errorMessage; - ConnectivityCorrelation* cc = ConnectivityCorrelation::newInstanceTimePoints(timePointData, - m_numberOfVertices, - brainordinateStride, - errorMessage); - if (cc != NULL) { - m_connectivityCorrelation.reset(cc); - } - else { - m_connectivityCorrelationFailedFlag = true; - CaretLogSevere("Failed to create connectvity correlation for " - + m_parentMetricFile->getFileNameNoPath()); - } - } - - if (m_connectivityCorrelation) { - m_connectivityCorrelation->getCorrelationForBrainordinate(vertexIndex, - vertexDataOut); - CaretAssert(m_numberOfVertices == static_cast(vertexDataOut.size())); - validFlag = true; - } + if ((vertexIndex >= 0) + && (vertexIndex < getNumberOfNodes())) { + ConnectivityCorrelation* connCorrelation = getConnectivityCorrelation(); + + if (connCorrelation) { + connCorrelation->getCorrelationForBrainordinate(vertexIndex, + vertexDataOut); + CaretAssert(m_numberOfVertices == static_cast(vertexDataOut.size())); + validFlag = true; } } @@ -478,6 +450,45 @@ MetricDynamicConnectivityFile::getConnectivityForVertexIndex(const int32_t verte return validFlag; } +/** + * @return Pointer to connectivity correlation or NULL if not valid + */ +ConnectivityCorrelation* +MetricDynamicConnectivityFile::getConnectivityCorrelation() +{ + if ( ! m_connectivityCorrelationFailedFlag) { + if (m_connectivityCorrelation == NULL) { + /* + * Need data and timepoint count from parent file + */ + CaretAssert(m_parentMetricFile); + const int32_t numberOfTimePoints = m_parentMetricFile->getNumberOfMaps(); + CaretAssert(numberOfTimePoints >= 2); + std::vector timePointData; + for (int32_t i = 0; i < numberOfTimePoints; i++) { + const float* dataPtr = m_parentMetricFile->getValuePointerForColumn(i); + CaretAssert(dataPtr); + timePointData.push_back(dataPtr); + } + const int64_t brainordinateStride(1); + AString errorMessage; + ConnectivityCorrelation* cc = ConnectivityCorrelation::newInstanceTimePoints(timePointData, + m_numberOfVertices, + brainordinateStride, + errorMessage); + if (cc != NULL) { + m_connectivityCorrelation.reset(cc); + } + else { + m_connectivityCorrelationFailedFlag = true; + CaretLogSevere("Failed to create connectvity correlation for " + + m_parentMetricFile->getFileNameNoPath()); + } + } + } + + return m_connectivityCorrelation.get(); +} /** * @return A metric file using the loaded data (will return NULL if there is an error). diff --git a/src/Files/MetricDynamicConnectivityFile.h b/src/Files/MetricDynamicConnectivityFile.h index b0dc344a7..5c36adbfb 100644 --- a/src/Files/MetricDynamicConnectivityFile.h +++ b/src/Files/MetricDynamicConnectivityFile.h @@ -106,6 +106,8 @@ namespace caret { bool getConnectivityForVertexIndex(const int32_t vertexIndex, std::vector& vertexDataOut); + ConnectivityCorrelation* getConnectivityCorrelation(); + const MetricFile* m_parentMetricFile; std::unique_ptr m_sceneAssistant; -- GitLab From f8867efc6fa234bbeab8c4f943e8e8114f7c3c7f Mon Sep 17 00:00:00 2001 From: John Harwell Date: Wed, 17 Jul 2019 12:38:35 -0500 Subject: [PATCH 392/427] WB-860 NIFTI Volume Timeseries Issues: Corrections for ROI dynamic connectivity in Volume Dynamic File. However, data for testing is not available. --- src/Files/VolumeDynamicConnectivityFile.cxx | 147 ++++++++++---------- src/Files/VolumeDynamicConnectivityFile.h | 3 + 2 files changed, 80 insertions(+), 70 deletions(-) diff --git a/src/Files/VolumeDynamicConnectivityFile.cxx b/src/Files/VolumeDynamicConnectivityFile.cxx index 51a7a350d..9b47ddb8c 100644 --- a/src/Files/VolumeDynamicConnectivityFile.cxx +++ b/src/Files/VolumeDynamicConnectivityFile.cxx @@ -333,7 +333,7 @@ VolumeDynamicConnectivityFile::loadMapAverageDataForVoxelIndices(const int64_t v return false; } if ( ! isDataLoadingEnabled()) { - + return false; } if ( ! matchesDimensions(volumeDimensionIJK[0], @@ -342,38 +342,40 @@ VolumeDynamicConnectivityFile::loadMapAverageDataForVoxelIndices(const int64_t v return false; } + ConnectivityCorrelation* connCorrelation = getConnectivityCorrelation(); + if (connCorrelation == NULL) { + return false; + } + /* * Zero out here so that data only gets cleared when data * is to be loaded. */ clearVoxels(); - std::vector averageData(m_numberOfVoxels, 0.0f); - std::vector data(m_numberOfVoxels); - float validDataCount(0.0f); + std::vector brainordinateIndices; for (auto voxel : voxelIndices) { - if (getConnectivityForVoxelIndex(voxel.m_ijk, - data)) { - for (int64_t i = 0; i < m_numberOfVoxels; i++) { - averageData[i] += data[i]; - } - validDataCount += 1.0f; - } + const int64_t offset = getVoxelOffset(voxel.m_ijk[0], voxel.m_ijk[1], voxel.m_ijk[2], 0); + brainordinateIndices.push_back(offset); } - if (validDataCount >= 1.0) { + std::vector data(m_numberOfVoxels); + connCorrelation->getCorrelationForBrainordinateROI(brainordinateIndices, + data); + if (m_numberOfVoxels == static_cast(data.size())) { for (int64_t i = 0; i < m_numberOfVoxels; i++) { - m_voxelData[i] = averageData[i] / validDataCount; + m_voxelData[i] = data[i]; } const int32_t mapIndex(0); - updateScalarColoringForMap(mapIndex); - + const int64_t validDataCount(static_cast(brainordinateIndices.size())); setMapName(mapIndex, ("Average Voxel Count: " + AString::number(validDataCount, 'f', 0))); m_dataLoadedName = ("Average_Voxel_Count_" + AString::number(validDataCount, 'f', 0)); + updateScalarColoringForMap(mapIndex); + return true; } @@ -481,61 +483,17 @@ VolumeDynamicConnectivityFile::getConnectivityForVoxelIndex(const int64_t ijk[3] { bool validFlag(false); - if ( ! m_connectivityCorrelationFailedFlag) { - if (m_voxelData != NULL) { - if (indexValid(ijk)) { - const int64_t voxelCount(m_dimI * m_dimJ * m_dimK); - if (m_connectivityCorrelation == NULL) { - /* - * Need data and timepoint count from parent volume - * IJK dimensions are same in this and parent volume - */ - CaretAssert(m_parentVolumeFile); - const float* parentVoxels = m_parentVolumeFile->getFrame(0); - CaretAssert(parentVoxels); - std::vector parentVolumeDimensions; - m_parentVolumeFile->getDimensions(parentVolumeDimensions); - CaretAssert(parentVolumeDimensions.size() >= 4); - const int64_t timePointCount = parentVolumeDimensions[3]; - - const int64_t numberOfBrainordinates(voxelCount); // Each IJK voxel is a group - const int64_t nextBrainordinateStride(1); // All groups have one element in each 'brick' - const int64_t numberOfTimePoints(timePointCount); // Each group contains timepoints - const int64_t nextTimePointStride(voxelCount); // Each group element is in separate IJK 'brick' - - AString errorMessage; - ConnectivityCorrelation* cc = ConnectivityCorrelation::newInstance(parentVoxels, - numberOfBrainordinates, - nextBrainordinateStride, - numberOfTimePoints, - nextTimePointStride, - errorMessage); - - { - - } - if (cc != NULL) { - m_connectivityCorrelation.reset(cc); - } - else { - m_connectivityCorrelationFailedFlag = true; - CaretLogSevere("Failed to create connectvity correlation for " - + m_parentVolumeFile->getFileNameNoPath()); - } - // m_connectivityCorrelation.reset(new ConnectivityCorrelation(parentVoxels, - // groupCount, - // groupStride, - // groupDataCount, - // groupDataStride)); - } - - if (m_connectivityCorrelation) { - const int64_t myTimePointOffset = getVoxelOffset(ijk[0], ijk[1], ijk[2], 0); - m_connectivityCorrelation->getCorrelationForBrainordinate(myTimePointOffset, - voxelsOut); - CaretAssert(voxelCount == static_cast(voxelsOut.size())); - validFlag = true; - } + ConnectivityCorrelation* connCorrelation = getConnectivityCorrelation(); + if (connCorrelation != NULL) { + if (indexValid(ijk)) { + const int64_t voxelCount(m_dimI * m_dimJ * m_dimK); + + if (connCorrelation) { + const int64_t myTimePointOffset = getVoxelOffset(ijk[0], ijk[1], ijk[2], 0); + connCorrelation->getCorrelationForBrainordinate(myTimePointOffset, + voxelsOut); + CaretAssert(voxelCount == static_cast(voxelsOut.size())); + validFlag = true; } } } @@ -549,6 +507,55 @@ VolumeDynamicConnectivityFile::getConnectivityForVoxelIndex(const int64_t ijk[3] return validFlag; } +/** + * @return Pointer to connectivity correlation or NULL if not valid + */ +ConnectivityCorrelation* +VolumeDynamicConnectivityFile::getConnectivityCorrelation() +{ + if ( ! m_connectivityCorrelationFailedFlag) { + if (m_voxelData != NULL) { + if (m_connectivityCorrelation == NULL) { + /* + * Need data and timepoint count from parent volume + * IJK dimensions are same in this and parent volume + */ + CaretAssert(m_parentVolumeFile); + const float* parentVoxels = m_parentVolumeFile->getFrame(0); + CaretAssert(parentVoxels); + std::vector parentVolumeDimensions; + m_parentVolumeFile->getDimensions(parentVolumeDimensions); + CaretAssert(parentVolumeDimensions.size() >= 4); + const int64_t timePointCount = parentVolumeDimensions[3]; + + const int64_t voxelCount(m_dimI * m_dimJ * m_dimK); + const int64_t numberOfBrainordinates(voxelCount); // Each IJK voxel is a group + const int64_t nextBrainordinateStride(1); // All groups have one element in each 'brick' + const int64_t numberOfTimePoints(timePointCount); // Each group contains timepoints + const int64_t nextTimePointStride(voxelCount); // Each group element is in separate IJK 'brick' + + AString errorMessage; + ConnectivityCorrelation* cc = ConnectivityCorrelation::newInstance(parentVoxels, + numberOfBrainordinates, + nextBrainordinateStride, + numberOfTimePoints, + nextTimePointStride, + errorMessage); + if (cc != NULL) { + m_connectivityCorrelation.reset(cc); + } + else { + m_connectivityCorrelationFailedFlag = true; + CaretLogSevere("Failed to create connectvity correlation for " + + m_parentVolumeFile->getFileNameNoPath()); + } + } + } + } + + return m_connectivityCorrelation.get(); +} + /** * @return True if this matches the given dimensions * diff --git a/src/Files/VolumeDynamicConnectivityFile.h b/src/Files/VolumeDynamicConnectivityFile.h index a515f3b12..250bdd927 100644 --- a/src/Files/VolumeDynamicConnectivityFile.h +++ b/src/Files/VolumeDynamicConnectivityFile.h @@ -124,6 +124,9 @@ namespace caret { bool getConnectivityForVoxelIndex(const int64_t ijk[3], std::vector& voxelsOut) ; + ConnectivityCorrelation* getConnectivityCorrelation(); + + const VolumeFile* m_parentVolumeFile; std::unique_ptr m_sceneAssistant; -- GitLab From 746dacf97125b53e1d9cb2db10e5ca7750882ba4 Mon Sep 17 00:00:00 2001 From: John Harwell Date: Mon, 22 Jul 2019 08:52:28 -0500 Subject: [PATCH 393/427] WB-860 NIFTI Volume Time Series Issues: Correct use of minus sign instead of 'n' in name of scalar file created from loaded volume dynamic connectivity. --- src/Files/VolumeDynamicConnectivityFile.cxx | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Files/VolumeDynamicConnectivityFile.cxx b/src/Files/VolumeDynamicConnectivityFile.cxx index 9b47ddb8c..72111b9c4 100644 --- a/src/Files/VolumeDynamicConnectivityFile.cxx +++ b/src/Files/VolumeDynamicConnectivityFile.cxx @@ -419,7 +419,6 @@ VolumeDynamicConnectivityFile::loadConnectivityForVoxelXYZ(const float xyz[3]) + AString::number(static_cast(xyz[1])) + "_z" + AString::number(static_cast(xyz[2]))); - m_dataLoadedName = m_dataLoadedName.replace('-', 'n'); return true; } -- GitLab From 6d385e7a13e0f786ee7e691b44ad48582167943a Mon Sep 17 00:00:00 2001 From: Tim Coalson Date: Thu, 8 Aug 2019 18:32:51 -0500 Subject: [PATCH 394/427] expand explanations in -arguments-help --- src/Commands/CommandOperationManager.cxx | 54 ++++++++++++++---------- src/Commands/CommandOperationManager.h | 2 +- 2 files changed, 32 insertions(+), 24 deletions(-) diff --git a/src/Commands/CommandOperationManager.cxx b/src/Commands/CommandOperationManager.cxx index e87d9ba93..5a82f4ac9 100644 --- a/src/Commands/CommandOperationManager.cxx +++ b/src/Commands/CommandOperationManager.cxx @@ -615,7 +615,7 @@ CommandOperationManager::runCommand(ProgramParameters& parameters) { printHelpInfo(); } else if (commandSwitch == "-arguments-help") { - printArgumentsHelp(myProgramName); + printArgumentsHelp(); } else if (commandSwitch == "-global-options") { printGlobalOptions(); } else if (commandSwitch == "-cifti-help") { @@ -993,7 +993,7 @@ void CommandOperationManager::printHelpInfo() cout << endl; } -void CommandOperationManager::printArgumentsHelp(const AString& programName) +void CommandOperationManager::printArgumentsHelp() { //guide for wrap, assuming 80 columns: | cout << " To get the help information on a subcommand, run it without any additional" << endl; @@ -1002,9 +1002,9 @@ void CommandOperationManager::printArgumentsHelp(const AString& programName) cout << " easiest way to get this right is to specify options and arguments in the" << endl; cout << " order they are listed. As an example, consider this help information:" << endl; cout << endl;//guide for wrap, assuming 80 columns: | - cout << "$ " << programName << " -volume-math" << endl; + cout << "$ wb_command -volume-math" << endl; cout << "EVALUATE EXPRESSION ON VOLUME FILES" << endl; - cout << " " << programName << " -volume-math" << endl; + cout << " wb_command -volume-math" << endl; cout << " - the expression to evaluate, in quotes" << endl; cout << " - output - the output volume" << endl; cout << endl;//guide for wrap, assuming 80 columns: | @@ -1021,36 +1021,44 @@ void CommandOperationManager::printArgumentsHelp(const AString& programName) cout << " [-repeat] - reuse a single subvolume for each subvolume of calculation" << endl; cout << "..." << endl; cout << endl;//guide for wrap, assuming 80 columns: | - cout << " '' and '' denote mandatory parameters. '[-fixnan]'" << endl; - cout << " denotes an option taking one mandatory parameter '', and" << endl; - cout << " '[-var] - repeatable' denotes a repeatable option with mandatory parameters" << endl; - cout << " '' and '', and two suboptions: '[-subvolume]', which has a" << endl; - cout << " mandatory parameter '', and '[-repeat]', which takes no parameters." << endl; - cout << " Commands also provide additional help info below the section in the example." << endl; - cout << " Each option starts a new scope, and all options and arguments end any scope" << endl; - cout << " that they are not valid in. For example, this command is correct:" << endl; + cout << " '' represents a required input parameter (required parameters" << endl; + cout << " are marked with the < and > symbols), and ' - output' represents" << endl; + cout << " a required output filename (marked by the presence of the word 'output')." << endl; + cout << " '[-fixnan]' represents an option (marked with the [ and ] symbols), taking" << endl; + cout << " one required parameter '' (the indentation level indicates what" << endl; + cout << " parameters and suboptions are associated with a given option), and '[-var] -" << endl; + cout << " repeatable' denotes a repeatable option (marked by the presence of the word" << endl; + cout << " 'repeatable') with required parameters '' and '', and two" << endl; + cout << " suboptions: '[-subvolume]', which has a required parameter '', and" << endl; + cout << " '[-repeat]', which takes no parameters. Commands also provide additional" << endl; + cout << " help info below the arguments section shown in the above example. Each" << endl; + cout << " option starts a new scope, and all options and arguments end any scope that" << endl; + cout << " they are not valid in. For example, this annotated command is correct:" << endl; cout << endl;//guide for wrap, assuming 80 columns: | - cout << "$ " << programName << " -volume-math 'sin(x)' sin_x.nii.gz -fixnan 0 -var x x.nii.gz -subvolume 1" << endl; + cout << "$ wb_command -volume-math 'sin(x)' sin_x.nii.gz -fixnan 0 -var x x.nii.gz" << endl; + cout << "annotation: " << endl; cout << endl; - cout << " as is this one (though less intuitive):" << endl; + cout << " Here is another annotated command that results in the same output, but" << endl; + cout << " uses a different order of the options:" << endl; cout << endl; - cout << "$ " << programName << " -volume-math -fixnan 0 'sin(x)' -var x -subvolume 1 x.nii.gz sin_x.nii.gz" << endl; + cout << "$ wb_command -volume-math -fixnan 0 'sin(x)' -var x x.nii.gz sin_x.nii.gz" << endl; + cout << "annotation: " << endl; cout << endl;//guide for wrap, assuming 80 columns: | - cout << " while this one is not, because the -fixnan option ends the scope of the -var" << endl; - cout << " option before all of its mandatory arguments are given:" << endl; + cout << " This next command is wrong, because the -fixnan option ends the scope of the" << endl; + cout << " -var option before all of its required arguments are given:" << endl; cout << endl; - cout << "$ " << programName << " -volume-math 'sin(x)' sin_x.nii.gz -var x -fixnan 0 x.nii.gz -subvolume 1" << endl; + cout << "wrong: wb_command -volume-math 'sin(x)' sin_x.nii.gz -var x -fixnan 0 x.nii.gz" << endl; cout << endl; //guide for wrap, assuming 80 columns: | - cout << " and this one is incorrect because the -subvolume option occurs after the" << endl; - cout << " scope of the -var option has ended due to -fixnan:" << endl; + cout << " This command is incorrect because the -subvolume option occurs after the" << endl; + cout << " scope of the -var option has ended due to the -fixnan option:" << endl; cout << endl; - cout << "$ " << programName << " -volume-math 'sin(x)' sin_x.nii.gz -var x x.nii.gz -fixnan 0 -subvolume 1" << endl; + cout << "wrong: wb_command -volume-math 'sin(x)' sin_x.nii.gz -var x x.nii.gz -fixnan 0 -subvolume 1" << endl; cout << endl;//guide for wrap, assuming 80 columns: | - cout << " and this one is similarly incorrect because the -subvolume option occurs" << endl; + cout << " This command is similarly incorrect because the -subvolume option occurs" << endl; cout << " after the scope of the -var option has ended due to the volume-out argument:" << endl; cout << endl; - cout << "$ " << programName << " -volume-math 'sin(x)' -fixnan 0 -var x x.nii.gz sin_x.nii.gz -subvolume 1" << endl; + cout << "wrong: wb_command -volume-math 'sin(x)' -var x x.nii.gz sin_x.nii.gz -subvolume 1 -fixnan 0" << endl; cout << endl; } diff --git a/src/Commands/CommandOperationManager.h b/src/Commands/CommandOperationManager.h index b76f33751..c0c110777 100644 --- a/src/Commands/CommandOperationManager.h +++ b/src/Commands/CommandOperationManager.h @@ -65,7 +65,7 @@ namespace caret { void printHelpInfo(); - void printArgumentsHelp(const AString& programName); + void printArgumentsHelp(); void printGlobalOptions(); -- GitLab From 843c3e36251e8c139abeb32f694daac3af32dc16 Mon Sep 17 00:00:00 2001 From: Tim Coalson Date: Wed, 14 Aug 2019 18:12:56 -0500 Subject: [PATCH 395/427] make most volume commands preserve TR, other header fields we generally don't look at --- src/Algorithms/AlgorithmVolumeAffineResample.cxx | 2 +- src/Algorithms/AlgorithmVolumeDilate.cxx | 4 ++-- src/Algorithms/AlgorithmVolumeErode.cxx | 4 ++-- src/Algorithms/AlgorithmVolumeExtrema.cxx | 4 ++-- src/Algorithms/AlgorithmVolumeFillHoles.cxx | 2 +- src/Algorithms/AlgorithmVolumeFindClusters.cxx | 5 +++-- src/Algorithms/AlgorithmVolumeGradient.cxx | 4 ++-- src/Algorithms/AlgorithmVolumeParcelResampling.cxx | 8 ++++---- src/Algorithms/AlgorithmVolumeParcelResamplingGeneric.cxx | 4 ++-- src/Algorithms/AlgorithmVolumeParcelSmoothing.cxx | 4 ++-- src/Algorithms/AlgorithmVolumeROIsFromExtrema.cxx | 2 +- src/Algorithms/AlgorithmVolumeRemoveIslands.cxx | 2 +- src/Algorithms/AlgorithmVolumeSmoothing.cxx | 8 ++++---- src/Algorithms/AlgorithmVolumeTFCE.cxx | 4 ++-- src/Algorithms/AlgorithmVolumeWarpfieldResample.cxx | 2 +- src/Operations/OperationVolumeMerge.cxx | 2 +- 16 files changed, 31 insertions(+), 30 deletions(-) diff --git a/src/Algorithms/AlgorithmVolumeAffineResample.cxx b/src/Algorithms/AlgorithmVolumeAffineResample.cxx index bd23d0434..34dcf405f 100644 --- a/src/Algorithms/AlgorithmVolumeAffineResample.cxx +++ b/src/Algorithms/AlgorithmVolumeAffineResample.cxx @@ -113,7 +113,7 @@ AlgorithmVolumeAffineResample::AlgorithmVolumeAffineResample(ProgressObject* myP outDims[1] = refDims[1]; outDims[2] = refDims[2]; int64_t numMaps = inVol->getNumberOfMaps(), numComponents = inVol->getNumberOfComponents(); - outVol->reinitialize(outDims, refSform, numComponents, inVol->getType()); + outVol->reinitialize(outDims, refSform, numComponents, inVol->getType(), inVol->m_header); FloatMatrix targetToSource = myAffine; targetToSource.resize(4, 4); targetToSource[3][0] = 0.0f; diff --git a/src/Algorithms/AlgorithmVolumeDilate.cxx b/src/Algorithms/AlgorithmVolumeDilate.cxx index cfc05ad70..66132f1c3 100644 --- a/src/Algorithms/AlgorithmVolumeDilate.cxx +++ b/src/Algorithms/AlgorithmVolumeDilate.cxx @@ -432,7 +432,7 @@ AlgorithmVolumeDilate::AlgorithmVolumeDilate(ProgressObject* myProgObj, const Vo } if (subvol == -1) { - volOut->reinitialize(volIn->getOriginalDimensions(), volIn->getSform(), volIn->getNumberOfComponents(), volIn->getType()); + volOut->reinitialize(volIn->getOriginalDimensions(), volIn->getSform(), volIn->getNumberOfComponents(), volIn->getType(), volIn->m_header); for (int i = 0; i < myDims[3]; ++i) { if (volIn->getType() == SubvolumeAttributes::LABEL) @@ -446,7 +446,7 @@ AlgorithmVolumeDilate::AlgorithmVolumeDilate(ProgressObject* myProgObj, const Vo } else { vector outDims = myDims; outDims.resize(3); - volOut->reinitialize(outDims, volIn->getSform(), volIn->getNumberOfComponents(), volIn->getType()); + volOut->reinitialize(outDims, volIn->getSform(), volIn->getNumberOfComponents(), volIn->getType(), volIn->m_header); if (volIn->getType() == SubvolumeAttributes::LABEL) { *(volOut->getMapLabelTable(0)) = *(volIn->getMapLabelTable(subvol)); diff --git a/src/Algorithms/AlgorithmVolumeErode.cxx b/src/Algorithms/AlgorithmVolumeErode.cxx index b77d8a32a..75b1828ac 100644 --- a/src/Algorithms/AlgorithmVolumeErode.cxx +++ b/src/Algorithms/AlgorithmVolumeErode.cxx @@ -202,7 +202,7 @@ AlgorithmVolumeErode::AlgorithmVolumeErode(ProgressObject* myProgObj, const Volu } if (subvol == -1) { - volOut->reinitialize(volIn->getOriginalDimensions(), volIn->getSform(), volIn->getNumberOfComponents(), volIn->getType()); + volOut->reinitialize(volIn->getOriginalDimensions(), volIn->getSform(), volIn->getNumberOfComponents(), volIn->getType(), volIn->m_header); for (int i = 0; i < myDims[3]; ++i) { if (volIn->getType() == SubvolumeAttributes::LABEL) @@ -223,7 +223,7 @@ AlgorithmVolumeErode::AlgorithmVolumeErode(ProgressObject* myProgObj, const Volu } else { vector outDims = myDims; outDims.resize(3); - volOut->reinitialize(outDims, volIn->getSform(), volIn->getNumberOfComponents(), volIn->getType()); + volOut->reinitialize(outDims, volIn->getSform(), volIn->getNumberOfComponents(), volIn->getType(), volIn->m_header); if (volIn->getType() == SubvolumeAttributes::LABEL) { *(volOut->getMapLabelTable(0)) = *(volIn->getMapLabelTable(subvol)); diff --git a/src/Algorithms/AlgorithmVolumeExtrema.cxx b/src/Algorithms/AlgorithmVolumeExtrema.cxx index 7a685ed4d..31002326c 100644 --- a/src/Algorithms/AlgorithmVolumeExtrema.cxx +++ b/src/Algorithms/AlgorithmVolumeExtrema.cxx @@ -181,7 +181,7 @@ AlgorithmVolumeExtrema::AlgorithmVolumeExtrema(ProgressObject* myProgObj, const { outDims[3] = 1; } - myVolOut->reinitialize(outDims, myVolIn->getSform(), myDims[4]); + myVolOut->reinitialize(outDims, myVolIn->getSform(), myDims[4], SubvolumeAttributes::ANATOMY, myVolIn->m_header); if (sumSubvols) { myVolOut->setMapName(0, "sum of extrema"); @@ -228,7 +228,7 @@ AlgorithmVolumeExtrema::AlgorithmVolumeExtrema(ProgressObject* myProgObj, const vector minima, maxima; vector outDims = myDims; outDims.resize(3); - myVolOut->reinitialize(outDims, myVolIn->getSform(), myDims[4]); + myVolOut->reinitialize(outDims, myVolIn->getSform(), myDims[4], SubvolumeAttributes::ANATOMY, myVolIn->m_header); myVolOut->setMapName(0, "extrema of " + myVolIn->getMapName(subvol)); myVolOut->setValueAllVoxels(0.0f); for (int c = 0; c < myDims[4]; ++c) diff --git a/src/Algorithms/AlgorithmVolumeFillHoles.cxx b/src/Algorithms/AlgorithmVolumeFillHoles.cxx index 6104de85c..9fa700bac 100644 --- a/src/Algorithms/AlgorithmVolumeFillHoles.cxx +++ b/src/Algorithms/AlgorithmVolumeFillHoles.cxx @@ -70,7 +70,7 @@ AlgorithmVolumeFillHoles::AlgorithmVolumeFillHoles(ProgressObject* myProgObj, co 0, 0, 1 }; vector dims; myVolIn->getDimensions(dims); - myVolOut->reinitialize(myVolIn->getOriginalDimensions(), myVolIn->getSform(), myVolIn->getNumberOfComponents(), myVolIn->getType()); + myVolOut->reinitialize(myVolIn->getOriginalDimensions(), myVolIn->getSform(), myVolIn->getNumberOfComponents(), myVolIn->getType(), myVolIn->m_header); for (int s = 0; s < dims[3]; ++s) { myVolOut->setMapName(s, myVolIn->getMapName(s)); diff --git a/src/Algorithms/AlgorithmVolumeFindClusters.cxx b/src/Algorithms/AlgorithmVolumeFindClusters.cxx index f73ac2409..d50e038ad 100644 --- a/src/Algorithms/AlgorithmVolumeFindClusters.cxx +++ b/src/Algorithms/AlgorithmVolumeFindClusters.cxx @@ -75,6 +75,7 @@ OperationParameters* AlgorithmVolumeFindClusters::getParameters() ret->setHelpText( AString("Outputs a volume with nonzero integers for all voxels within a large enough cluster, and zeros elsewhere. ") + "The integers denote cluster membership (by default, first cluster found will use value 1, second cluster 2, etc). " + + "Cluster values are not reused across frames of the output, but instead keep counting up. " + "By default, values greater than are considered to be in a cluster, use -less-than to test for values less than the threshold. " + "To apply this as a mask to the data, or to do more complicated thresholding, see -volume-math." ); @@ -307,7 +308,7 @@ AlgorithmVolumeFindClusters::AlgorithmVolumeFindClusters(ProgressObject* myProgO int markVal = startVal; if (subvolNum == -1) { - volOut->reinitialize(volIn->getOriginalDimensions(), volIn->getSform(), dims[4]); + volOut->reinitialize(volIn->getOriginalDimensions(), volIn->getSform(), dims[4], SubvolumeAttributes::ANATOMY, volIn->m_header); volOut->setValueAllVoxels(0.0f); for (int64_t c = 0; c < dims[4]; ++c) { @@ -320,7 +321,7 @@ AlgorithmVolumeFindClusters::AlgorithmVolumeFindClusters(ProgressObject* myProgO } else { vector outDims = volIn->getOriginalDimensions(); outDims.resize(3); - volOut->reinitialize(outDims, volIn->getSform(), dims[4]); + volOut->reinitialize(outDims, volIn->getSform(), dims[4], SubvolumeAttributes::ANATOMY, volIn->m_header); volOut->setValueAllVoxels(0.0f); for (int64_t c = 0; c < dims[4]; ++c) { diff --git a/src/Algorithms/AlgorithmVolumeGradient.cxx b/src/Algorithms/AlgorithmVolumeGradient.cxx index 1b45e24fd..ac28e06e0 100644 --- a/src/Algorithms/AlgorithmVolumeGradient.cxx +++ b/src/Algorithms/AlgorithmVolumeGradient.cxx @@ -154,7 +154,7 @@ AlgorithmVolumeGradient::AlgorithmVolumeGradient(ProgressObject* myProgObj, cons } if (subvolNum == -1) { - volOut->reinitialize(origDims, volIn->getSform(), myDims[4], volIn->getType()); + volOut->reinitialize(origDims, volIn->getSform(), myDims[4], volIn->getType(), volIn->m_header); if (vectorsOut != NULL) { while (origDims.size() < 4) @@ -405,7 +405,7 @@ AlgorithmVolumeGradient::AlgorithmVolumeGradient(ProgressObject* myProgObj, cons } } else { origDims.resize(3); - volOut->reinitialize(origDims, volIn->getSform(), myDims[4], volIn->getType()); + volOut->reinitialize(origDims, volIn->getSform(), myDims[4], volIn->getType(), volIn->m_header); if (vectorsOut != NULL) { origDims.push_back(3); diff --git a/src/Algorithms/AlgorithmVolumeParcelResampling.cxx b/src/Algorithms/AlgorithmVolumeParcelResampling.cxx index e61957d3e..d6bb354e0 100644 --- a/src/Algorithms/AlgorithmVolumeParcelResampling.cxx +++ b/src/Algorithms/AlgorithmVolumeParcelResampling.cxx @@ -228,11 +228,11 @@ void AlgorithmVolumeParcelResampling::resample(LevelProgress& myProgress, const inVol->getDimensions(myDims); if (subvolNum == -1) { - outVol->reinitialize(inVol->getOriginalDimensions(), inVol->getSform(), myDims[4], inVol->getType()); + outVol->reinitialize(inVol->getOriginalDimensions(), inVol->getSform(), myDims[4], inVol->getType(), inVol->m_header); } else { vector newDims = inVol->getOriginalDimensions(); newDims.resize(3);//discard nonspatial dimentions - outVol->reinitialize(newDims, inVol->getSform(), myDims[4], inVol->getType()); + outVol->reinitialize(newDims, inVol->getSform(), myDims[4], inVol->getType(), inVol->m_header); } outVol->setValueAllVoxels(0.0f); const GiftiLabelTable* curTable = curLabel->getMapLabelTable(0); @@ -475,11 +475,11 @@ void AlgorithmVolumeParcelResampling::resampleFixZeros(LevelProgress& myProgress inVol->getDimensions(myDims); if (subvolNum == -1) { - outVol->reinitialize(inVol->getOriginalDimensions(), inVol->getSform(), myDims[4], inVol->getType()); + outVol->reinitialize(inVol->getOriginalDimensions(), inVol->getSform(), myDims[4], inVol->getType(), inVol->m_header); } else { vector newDims = inVol->getOriginalDimensions(); newDims.resize(3);//discard nonspatial dimentions - outVol->reinitialize(newDims, inVol->getSform(), myDims[4], inVol->getType()); + outVol->reinitialize(newDims, inVol->getSform(), myDims[4], inVol->getType(), inVol->m_header); } outVol->setValueAllVoxels(0.0f); const GiftiLabelTable* curTable = curLabel->getMapLabelTable(0); diff --git a/src/Algorithms/AlgorithmVolumeParcelResamplingGeneric.cxx b/src/Algorithms/AlgorithmVolumeParcelResamplingGeneric.cxx index eb55b9bcf..2b6ea2192 100644 --- a/src/Algorithms/AlgorithmVolumeParcelResamplingGeneric.cxx +++ b/src/Algorithms/AlgorithmVolumeParcelResamplingGeneric.cxx @@ -168,11 +168,11 @@ AlgorithmVolumeParcelResamplingGeneric::AlgorithmVolumeParcelResamplingGeneric(P { outDims.push_back(inDims[i]); } - outVol->reinitialize(outDims, newLabel->getSform(), myDims[4], inVol->getType()); + outVol->reinitialize(outDims, newLabel->getSform(), myDims[4], inVol->getType(), inVol->m_header); } else { vector outDims = newLabel->getOriginalDimensions(); outDims.resize(3);//discard nonspatial dimentions - outVol->reinitialize(outDims, newLabel->getSform(), myDims[4], inVol->getType()); + outVol->reinitialize(outDims, newLabel->getSform(), myDims[4], inVol->getType(), inVol->m_header); } outVol->setValueAllVoxels(0.0f); const float* labelFrame = curLabel->getFrame(); diff --git a/src/Algorithms/AlgorithmVolumeParcelSmoothing.cxx b/src/Algorithms/AlgorithmVolumeParcelSmoothing.cxx index ee68f017d..788924fd3 100644 --- a/src/Algorithms/AlgorithmVolumeParcelSmoothing.cxx +++ b/src/Algorithms/AlgorithmVolumeParcelSmoothing.cxx @@ -140,7 +140,7 @@ AlgorithmVolumeParcelSmoothing::AlgorithmVolumeParcelSmoothing(ProgressObject* m } if (subvolNum == -1) { - myOutVol->reinitialize(myVol->getOriginalDimensions(), myVol->getSform(), myDims[4], myVol->getType()); + myOutVol->reinitialize(myVol->getOriginalDimensions(), myVol->getSform(), myDims[4], myVol->getType(), myVol->m_header); myOutVol->setValueAllVoxels(0.0f); for (int whichList = 0; whichList < numLabels; ++whichList) { @@ -204,7 +204,7 @@ AlgorithmVolumeParcelSmoothing::AlgorithmVolumeParcelSmoothing(ProgressObject* m } else { vector newDims = myVol->getOriginalDimensions(); newDims.resize(3);//discard non-spatial extra dimensions - myOutVol->reinitialize(newDims, myVol->getSform(), myDims[4], myVol->getType());//keep components + myOutVol->reinitialize(newDims, myVol->getSform(), myDims[4], myVol->getType(), myVol->m_header);//keep components myOutVol->setValueAllVoxels(0.0f); for (int whichList = 0; whichList < numLabels; ++whichList) { diff --git a/src/Algorithms/AlgorithmVolumeROIsFromExtrema.cxx b/src/Algorithms/AlgorithmVolumeROIsFromExtrema.cxx index 0f119c7eb..41e48e09f 100644 --- a/src/Algorithms/AlgorithmVolumeROIsFromExtrema.cxx +++ b/src/Algorithms/AlgorithmVolumeROIsFromExtrema.cxx @@ -236,7 +236,7 @@ AlgorithmVolumeROIsFromExtrema::AlgorithmVolumeROIsFromExtrema(ProgressObject* m vector outDims = myDims; outDims.resize(4); outDims[3] = extremaCount; - myVolOut->reinitialize(outDims, myVol->getSform()); + myVolOut->reinitialize(outDims, myVol->getSform(), 1, SubvolumeAttributes::ANATOMY, myVol->m_header); vector tempFrame(frameSize, 0.0f); if (sigma > 0.0f) { diff --git a/src/Algorithms/AlgorithmVolumeRemoveIslands.cxx b/src/Algorithms/AlgorithmVolumeRemoveIslands.cxx index 3f669d6ce..f8a8547a3 100644 --- a/src/Algorithms/AlgorithmVolumeRemoveIslands.cxx +++ b/src/Algorithms/AlgorithmVolumeRemoveIslands.cxx @@ -70,7 +70,7 @@ AlgorithmVolumeRemoveIslands::AlgorithmVolumeRemoveIslands(ProgressObject* myPro 0, 0, 1 }; vector dims; myVolIn->getDimensions(dims); - myVolOut->reinitialize(myVolIn->getOriginalDimensions(), myVolIn->getSform(), myVolIn->getNumberOfComponents(), myVolIn->getType()); + myVolOut->reinitialize(myVolIn->getOriginalDimensions(), myVolIn->getSform(), myVolIn->getNumberOfComponents(), myVolIn->getType(), myVolIn->m_header); for (int s = 0; s < dims[3]; ++s) { myVolOut->setMapName(s, myVolIn->getMapName(s)); diff --git a/src/Algorithms/AlgorithmVolumeSmoothing.cxx b/src/Algorithms/AlgorithmVolumeSmoothing.cxx index d3572aa69..ae9540f17 100644 --- a/src/Algorithms/AlgorithmVolumeSmoothing.cxx +++ b/src/Algorithms/AlgorithmVolumeSmoothing.cxx @@ -160,7 +160,7 @@ AlgorithmVolumeSmoothing::AlgorithmVolumeSmoothing(ProgressObject* myProgObj, co if (subvol == -1) { vector origDims = inVol->getOriginalDimensions(); - outVol->reinitialize(origDims, volSpace, myDims[4]); + outVol->reinitialize(origDims, volSpace, myDims[4], inVol->getType(), inVol->m_header); vector lists[3]; for (int s = 0; s < myDims[3]; ++s) { @@ -183,7 +183,7 @@ AlgorithmVolumeSmoothing::AlgorithmVolumeSmoothing(ProgressObject* myProgObj, co newDims[0] = origDims[0]; newDims[1] = origDims[1]; newDims[2] = origDims[2]; - outVol->reinitialize(newDims, volSpace, myDims[4]); + outVol->reinitialize(newDims, volSpace, myDims[4], inVol->getType(), inVol->m_header); vector lists[3]; outVol->setMapName(0, inVol->getMapName(subvol) + ", smooth " + AString::number(kernel)); for (int c = 0; c < myDims[4]; ++c) @@ -244,7 +244,7 @@ AlgorithmVolumeSmoothing::AlgorithmVolumeSmoothing(ProgressObject* myProgObj, co if (subvol == -1) { vector origDims = inVol->getOriginalDimensions(); - outVol->reinitialize(origDims, volSpace, myDims[4]); + outVol->reinitialize(origDims, volSpace, myDims[4], inVol->getType(), inVol->m_header); for (int s = 0; s < myDims[3]; ++s) { outVol->setMapName(s, inVol->getMapName(s) + ", smooth " + AString::number(kernel)); @@ -261,7 +261,7 @@ AlgorithmVolumeSmoothing::AlgorithmVolumeSmoothing(ProgressObject* myProgObj, co newDims[0] = origDims[0]; newDims[1] = origDims[1]; newDims[2] = origDims[2]; - outVol->reinitialize(newDims, volSpace, myDims[4]); + outVol->reinitialize(newDims, volSpace, myDims[4], inVol->getType(), inVol->m_header); outVol->setMapName(0, inVol->getMapName(subvol) + ", smooth " + AString::number(kernel)); for (int c = 0; c < myDims[4]; ++c) { diff --git a/src/Algorithms/AlgorithmVolumeTFCE.cxx b/src/Algorithms/AlgorithmVolumeTFCE.cxx index a4ba0bb24..42c059c7e 100644 --- a/src/Algorithms/AlgorithmVolumeTFCE.cxx +++ b/src/Algorithms/AlgorithmVolumeTFCE.cxx @@ -125,7 +125,7 @@ AlgorithmVolumeTFCE::AlgorithmVolumeTFCE(ProgressObject* myProgObj, const Volume if (myRoi != NULL) roiFrame = myRoi->getFrame(); if (subvolNum == -1) { - myVolOut->reinitialize(myVol->getOriginalDimensions(), myVol->getSform(), dims[4]); + myVolOut->reinitialize(myVol->getOriginalDimensions(), myVol->getSform(), dims[4], myVol->getType(), myVol->m_header); const VolumeFile* toUse = myVol; VolumeFile smoothed; if (presmooth > 0.0f) @@ -149,7 +149,7 @@ AlgorithmVolumeTFCE::AlgorithmVolumeTFCE(ProgressObject* myProgObj, const Volume } else { vector outDims = dims; outDims.resize(3); - myVolOut->reinitialize(outDims, myVol->getSform(), dims[4]); + myVolOut->reinitialize(outDims, myVol->getSform(), dims[4], myVol->getType(), myVol->m_header); const VolumeFile* toUse = myVol; int useFrame = subvolNum; VolumeFile smoothed; diff --git a/src/Algorithms/AlgorithmVolumeWarpfieldResample.cxx b/src/Algorithms/AlgorithmVolumeWarpfieldResample.cxx index e79e211c7..e8baea1c6 100644 --- a/src/Algorithms/AlgorithmVolumeWarpfieldResample.cxx +++ b/src/Algorithms/AlgorithmVolumeWarpfieldResample.cxx @@ -111,7 +111,7 @@ AlgorithmVolumeWarpfieldResample::AlgorithmVolumeWarpfieldResample(ProgressObjec outDims[1] = refDims[1]; outDims[2] = refDims[2]; int64_t numMaps = inVol->getNumberOfMaps(), numComponents = inVol->getNumberOfComponents(); - outVol->reinitialize(outDims, refSform, numComponents, inVol->getType()); + outVol->reinitialize(outDims, refSform, numComponents, inVol->getType(), inVol->m_header); if (inVol->isMappedWithLabelTable()) { if (myMethod != VolumeFile::ENCLOSING_VOXEL) diff --git a/src/Operations/OperationVolumeMerge.cxx b/src/Operations/OperationVolumeMerge.cxx index e56577495..cda6f3e70 100644 --- a/src/Operations/OperationVolumeMerge.cxx +++ b/src/Operations/OperationVolumeMerge.cxx @@ -114,7 +114,7 @@ void OperationVolumeMerge::useParameters(OperationParameters* myParams, Progress vector outDims = firstVol->getOriginalDimensions(); outDims.resize(4); outDims[3] = subvolCount; - volumeOut->reinitialize(outDims, firstVol->getSform(), firstDims[4], firstVol->getType()); + volumeOut->reinitialize(outDims, firstVol->getSform(), firstDims[4], firstVol->getType(), firstVol->m_header); int64_t curOutVol = 0; for (int i = 0; i < numInputs; ++i) { -- GitLab From bcb37676f09e8d6918b8662f26014e2aed5c9b14 Mon Sep 17 00:00:00 2001 From: Tim Coalson Date: Mon, 26 Aug 2019 13:31:33 -0500 Subject: [PATCH 396/427] further improvements to -arguments-help --- src/Commands/CommandOperationManager.cxx | 42 ++++++++++++++---------- 1 file changed, 25 insertions(+), 17 deletions(-) diff --git a/src/Commands/CommandOperationManager.cxx b/src/Commands/CommandOperationManager.cxx index 5a82f4ac9..fff46651a 100644 --- a/src/Commands/CommandOperationManager.cxx +++ b/src/Commands/CommandOperationManager.cxx @@ -997,10 +997,11 @@ void CommandOperationManager::printArgumentsHelp() { //guide for wrap, assuming 80 columns: | cout << " To get the help information on a subcommand, run it without any additional" << endl; - cout << " arguments. Options can occur in any position within the correct scope, and" << endl; - cout << " can have suboptions, which must occur within the scope of the option. The" << endl; - cout << " easiest way to get this right is to specify options and arguments in the" << endl; - cout << " order they are listed. As an example, consider this help information:" << endl; + cout << " arguments. Options can occur in any order, however suboptions and arguments" << endl; + cout << " to options must occur next to their parent option. The easiest way to get" << endl; + cout << " this right is to specify options and arguments in the order they are listed." << endl; + cout << " As an example, consider this abbreviated version of the -volume-math help" << endl; + cout << " information:" << endl; cout << endl;//guide for wrap, assuming 80 columns: | cout << "$ wb_command -volume-math" << endl; cout << "EVALUATE EXPRESSION ON VOLUME FILES" << endl; @@ -1020,6 +1021,10 @@ void CommandOperationManager::printArgumentsHelp() cout << endl;//guide for wrap, assuming 80 columns: | cout << " [-repeat] - reuse a single subvolume for each subvolume of calculation" << endl; cout << "..." << endl; + cout << " The following functions are supported:" << endl; + cout << "..." << endl; + cout << " abs: 1 argument, the absolute value of the argument" << endl; + cout << "..." << endl; cout << endl;//guide for wrap, assuming 80 columns: | cout << " '' represents a required input parameter (required parameters" << endl; cout << " are marked with the < and > symbols), and ' - output' represents" << endl; @@ -1030,35 +1035,38 @@ void CommandOperationManager::printArgumentsHelp() cout << " repeatable' denotes a repeatable option (marked by the presence of the word" << endl; cout << " 'repeatable') with required parameters '' and '', and two" << endl; cout << " suboptions: '[-subvolume]', which has a required parameter '', and" << endl; - cout << " '[-repeat]', which takes no parameters. Commands also provide additional" << endl; - cout << " help info below the arguments section shown in the above example. Each" << endl; - cout << " option starts a new scope, and all options and arguments end any scope that" << endl; - cout << " they are not valid in. For example, this annotated command is correct:" << endl; + cout << " '[-repeat]', which takes no parameters." << endl; + cout << endl;//guide for wrap, assuming 80 columns: | + cout << " Each option starts a new scope, and all options and arguments end any scope" << endl; + cout << " that they are not valid in. This means that for any option, you must" << endl; + cout << " specify all of its arguments and any desired suboptions before specifying" << endl; + cout << " any other option or argument on the same or a previous level. For example," << endl; + cout << " this annotated command is valid:" << endl; cout << endl;//guide for wrap, assuming 80 columns: | - cout << "$ wb_command -volume-math 'sin(x)' sin_x.nii.gz -fixnan 0 -var x x.nii.gz" << endl; + cout << "$ wb_command -volume-math 'abs(x)' abs_x.nii.gz -fixnan 0 -var x x.nii.gz" << endl; cout << "annotation: " << endl; cout << endl; cout << " Here is another annotated command that results in the same output, but" << endl; cout << " uses a different order of the options:" << endl; cout << endl; - cout << "$ wb_command -volume-math -fixnan 0 'sin(x)' -var x x.nii.gz sin_x.nii.gz" << endl; + cout << "$ wb_command -volume-math -fixnan 0 'abs(x)' -var x x.nii.gz abs_x.nii.gz" << endl; cout << "annotation: " << endl; cout << endl;//guide for wrap, assuming 80 columns: | - cout << " This next command is wrong, because the -fixnan option ends the scope of the" << endl; - cout << " -var option before all of its required arguments are given:" << endl; + cout << " This next command is invalid, because the -fixnan option ends the scope of" << endl; + cout << " the -var option before all of its required arguments are given:" << endl; cout << endl; - cout << "wrong: wb_command -volume-math 'sin(x)' sin_x.nii.gz -var x -fixnan 0 x.nii.gz" << endl; + cout << "wrong: wb_command -volume-math 'abs(x)' abs_x.nii.gz -var x -fixnan 0 x.nii.gz" << endl; cout << endl; //guide for wrap, assuming 80 columns: | - cout << " This command is incorrect because the -subvolume option occurs after the" << endl; + cout << " This command is invalid because the -subvolume option occurs after the" << endl; cout << " scope of the -var option has ended due to the -fixnan option:" << endl; cout << endl; - cout << "wrong: wb_command -volume-math 'sin(x)' sin_x.nii.gz -var x x.nii.gz -fixnan 0 -subvolume 1" << endl; + cout << "wrong: wb_command -volume-math 'abs(x)' abs_x.nii.gz -var x x.nii.gz -fixnan 0 -subvolume 1" << endl; cout << endl;//guide for wrap, assuming 80 columns: | - cout << " This command is similarly incorrect because the -subvolume option occurs" << endl; + cout << " This command is similarly invalid because the -subvolume option occurs" << endl; cout << " after the scope of the -var option has ended due to the volume-out argument:" << endl; cout << endl; - cout << "wrong: wb_command -volume-math 'sin(x)' -var x x.nii.gz sin_x.nii.gz -subvolume 1 -fixnan 0" << endl; + cout << "wrong: wb_command -volume-math 'abs(x)' -var x x.nii.gz abs_x.nii.gz -subvolume 1 -fixnan 0" << endl; cout << endl; } -- GitLab From 9b452dba0b15aefad799e9f99560468311f06de9 Mon Sep 17 00:00:00 2001 From: Tim Coalson Date: Fri, 30 Aug 2019 13:43:02 -0500 Subject: [PATCH 397/427] make -*-reduce commands generate warnings when doing reductions along a length-1 dimension --- src/Algorithms/AlgorithmCiftiReduce.cxx | 8 ++++++++ src/Algorithms/AlgorithmMetricReduce.cxx | 4 ++++ src/Algorithms/AlgorithmVolumeReduce.cxx | 4 ++++ 3 files changed, 16 insertions(+) diff --git a/src/Algorithms/AlgorithmCiftiReduce.cxx b/src/Algorithms/AlgorithmCiftiReduce.cxx index e80e07a69..fa7ac7a9b 100644 --- a/src/Algorithms/AlgorithmCiftiReduce.cxx +++ b/src/Algorithms/AlgorithmCiftiReduce.cxx @@ -108,6 +108,10 @@ AlgorithmCiftiReduce::AlgorithmCiftiReduce(ProgressObject* myProgObj, const Cift vector inDims = inputXML.getDimensions(); if (direction == CiftiXML::ALONG_ROW) { + if (inDims[0] == 1) + { + CaretLogWarning("-cifti-reduce is being used for a length=1 reduction on file '" + ciftiIn->getFileName() + "'"); + } vector scratchInRow(inDims[0]); for (MultiDimIterator iter(vector(inDims.begin() + 1, inDims.end())); !iter.atEnd(); ++iter) {// + 1 to exclude row dimension, because getRow/setRow @@ -122,6 +126,10 @@ AlgorithmCiftiReduce::AlgorithmCiftiReduce(ProgressObject* myProgObj, const Cift ciftiOut->setRow(&result, *iter);//if reducing along row, length of output row is 1 } } else { + if (inDims[direction] == 1) + { + CaretLogWarning("-cifti-reduce is being used for a length=1 reduction on file '" + ciftiIn->getFileName() + "'"); + } vector > scratchInRows(inDims[direction], vector(inDims[0])); vector outRow(inDims[0]), reduceScratch(inDims[direction]);//reduction isn't along row, so out rows will be same length as in rows vector otherDims = inDims; diff --git a/src/Algorithms/AlgorithmMetricReduce.cxx b/src/Algorithms/AlgorithmMetricReduce.cxx index 8cac2671d..c6cb98a6e 100644 --- a/src/Algorithms/AlgorithmMetricReduce.cxx +++ b/src/Algorithms/AlgorithmMetricReduce.cxx @@ -86,6 +86,10 @@ AlgorithmMetricReduce::AlgorithmMetricReduce(ProgressObject* myProgObj, const Me int numNodes = metricIn->getNumberOfNodes(); int numCols = metricIn->getNumberOfColumns(); if (numCols < 1 || numNodes < 1) throw AlgorithmException("input must have at least 1 column and 1 vertex"); + if (numCols == 1) + { + CaretLogWarning("-metric-reduce is being used for a length=1 reduction on file '" + metricIn->getFileName() + "'"); + } metricOut->setNumberOfNodesAndColumns(numNodes, 1); metricOut->setStructure(metricIn->getStructure()); metricOut->setColumnName(0, ReductionEnum::toName(myReduce)); diff --git a/src/Algorithms/AlgorithmVolumeReduce.cxx b/src/Algorithms/AlgorithmVolumeReduce.cxx index 3e048e573..3ab252bf3 100644 --- a/src/Algorithms/AlgorithmVolumeReduce.cxx +++ b/src/Algorithms/AlgorithmVolumeReduce.cxx @@ -87,6 +87,10 @@ AlgorithmVolumeReduce::AlgorithmVolumeReduce(ProgressObject* myProgObj, const Vo vector myDims, newDims = volumeIn->getOriginalDimensions(); newDims.resize(3, 1);//have only one subvolume volumeIn->getDimensions(myDims); + if (myDims[3] == 1) + { + CaretLogWarning("-volume-reduce is being used for a length=1 reduction on file '" + volumeIn->getFileName() + "'"); + } volumeOut->reinitialize(newDims, volumeIn->getSform(), myDims[4], volumeIn->getType()); if (volumeIn->getType() == SubvolumeAttributes::LABEL) { -- GitLab From 7f74df0cccca9eb72dc967579cde0273b1f948c8 Mon Sep 17 00:00:00 2001 From: Tim Coalson Date: Fri, 30 Aug 2019 13:44:53 -0500 Subject: [PATCH 398/427] improve help info of -*-find-clusters --- src/Algorithms/AlgorithmCiftiFindClusters.cxx | 1 + src/Algorithms/AlgorithmMetricFindClusters.cxx | 1 + 2 files changed, 2 insertions(+) diff --git a/src/Algorithms/AlgorithmCiftiFindClusters.cxx b/src/Algorithms/AlgorithmCiftiFindClusters.cxx index d06844ca3..6ffb49c52 100644 --- a/src/Algorithms/AlgorithmCiftiFindClusters.cxx +++ b/src/Algorithms/AlgorithmCiftiFindClusters.cxx @@ -90,6 +90,7 @@ OperationParameters* AlgorithmCiftiFindClusters::getParameters() ret->setHelpText( AString("Outputs a cifti file with nonzero integers for all brainordinates within a large enough cluster, and zeros elsewhere. ") + "The integers denote cluster membership (by default, first cluster found will use value 1, second cluster 2, etc). " + + "Cluster values are not reused across maps of the output, but instead keep counting up. " + "The input cifti file must have a brain models mapping on the chosen dimension, columns for .dtseries, and either for .dconn. " + "The ROI should have a brain models mapping along columns, exactly matching the mapping of the chosen direction in the input file. " + "Data outside the ROI is ignored." diff --git a/src/Algorithms/AlgorithmMetricFindClusters.cxx b/src/Algorithms/AlgorithmMetricFindClusters.cxx index 318126413..64cc453eb 100644 --- a/src/Algorithms/AlgorithmMetricFindClusters.cxx +++ b/src/Algorithms/AlgorithmMetricFindClusters.cxx @@ -78,6 +78,7 @@ OperationParameters* AlgorithmMetricFindClusters::getParameters() ret->setHelpText( AString("Outputs a metric with nonzero integers for all vertices within a large enough cluster, and zeros elsewhere. ") + "The integers denote cluster membership (by default, first cluster found will use value 1, second cluster 2, etc). " + + "Cluster values are not reused across maps of the output, but instead keep counting up. " + "By default, values greater than are considered to be in a cluster, use -less-than to test for values less than the threshold. " + "To apply this as a mask to the data, or to do more complicated thresholding, see -metric-math." ); -- GitLab From 6dceafba2e7fbee9238dcd2684e3d52c6f075176 Mon Sep 17 00:00:00 2001 From: John Harwell Date: Wed, 4 Sep 2019 16:14:08 -0500 Subject: [PATCH 399/427] For experimental purposes, added the ability to load the BALSA website within Qt's web browser widget. --- src/CMakeLists.txt | 2 +- src/Common/DeveloperFlagsEnum.cxx | 6 ++++++ src/Common/DeveloperFlagsEnum.h | 3 ++- src/Desktop/CMakeLists.txt | 3 +++ src/GuiQt/BrainBrowserWindow.cxx | 15 +++++++++++++++ src/GuiQt/CMakeLists.txt | 4 ++++ 6 files changed, 31 insertions(+), 2 deletions(-) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index f7c03d9b0..d17a0e0ba 100755 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -197,7 +197,7 @@ IF (WORKBENCH_USE_QT5) SET(WB_QT_OPENGL_MODULE "OpenGL") ENDIF () - FIND_PACKAGE(Qt5 REQUIRED COMPONENTS Concurrent Core Gui Network ${WB_QT_OPENGL_MODULE} PrintSupport Test Widgets Xml) + FIND_PACKAGE(Qt5 REQUIRED COMPONENTS Concurrent Core Gui Network ${WB_QT_OPENGL_MODULE} PrintSupport Test WebEngine WebEngineCore WebEngineWidgets Widgets Xml) IF (WORKBENCH_USE_QT5_QOPENGL_WIDGET) # # QGLWidget is deprecated in Qt 5 and is replaced with QOpenGLWidget diff --git a/src/Common/DeveloperFlagsEnum.cxx b/src/Common/DeveloperFlagsEnum.cxx index cb8bbddc8..e884cbc09 100644 --- a/src/Common/DeveloperFlagsEnum.cxx +++ b/src/Common/DeveloperFlagsEnum.cxx @@ -135,6 +135,12 @@ DeveloperFlagsEnum::initialize() CheckableEnum::YES, false)); + checkableItems.push_back(DeveloperFlagsEnum(DEVELOPER_FLAG_BALSA, + "DEVELOPER_FLAG_BALSA", + "Visit BALSA...", + CheckableEnum::NO, + false)); + std::vector notCheckableItems; enumData.insert(enumData.end(), diff --git a/src/Common/DeveloperFlagsEnum.h b/src/Common/DeveloperFlagsEnum.h index 42c431c83..b8ac7412b 100644 --- a/src/Common/DeveloperFlagsEnum.h +++ b/src/Common/DeveloperFlagsEnum.h @@ -38,7 +38,8 @@ public: DEVELOPER_FLAG_UNUSED, DEVELOPER_FLAG_FLIP_PALETTE_NOT_DATA, DEVELOPER_FLAG_TEXTURE_VOLUME, - DELELOPER_FLAG_VOXEL_SMOOTH + DELELOPER_FLAG_VOXEL_SMOOTH, + DEVELOPER_FLAG_BALSA }; ~DeveloperFlagsEnum(); diff --git a/src/Desktop/CMakeLists.txt b/src/Desktop/CMakeLists.txt index 233e5cedf..c52180c3f 100644 --- a/src/Desktop/CMakeLists.txt +++ b/src/Desktop/CMakeLists.txt @@ -109,6 +109,9 @@ if(Qt5_FOUND) ${QT5_OPENGL_LIB_NAME} Qt5::PrintSupport Qt5::Test + Qt5::WebEngine + Qt5::WebEngineCore + Qt5::WebEngineWidgets Qt5::Widgets Qt5::Xml) endif() diff --git a/src/GuiQt/BrainBrowserWindow.cxx b/src/GuiQt/BrainBrowserWindow.cxx index 0c8064380..cbc75faef 100644 --- a/src/GuiQt/BrainBrowserWindow.cxx +++ b/src/GuiQt/BrainBrowserWindow.cxx @@ -34,6 +34,7 @@ #include #include #include +#include #define __BRAIN_BROWSER_WINDOW_DECLARE__ #include "BrainBrowserWindow.h" @@ -1769,6 +1770,20 @@ BrainBrowserWindow::developerMenuFlagTriggered(QAction* action) * someFunction(); * } */ + if (enumValue == DeveloperFlagsEnum::DEVELOPER_FLAG_BALSA) { + static WuQDialogModal* balsaDialog(NULL); + if (balsaDialog == NULL) { + QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling); + balsaDialog = new WuQDialogModal("BALSA", + this); + QWebEngineView* webView = new QWebEngineView(); + balsaDialog->setCentralWidget(webView, WuQDialogModal::SCROLL_AREA_NEVER); + webView->setUrl(QUrl(QStringLiteral("https://balsa.wustl.edu"))); + webView->resize(600, 800); + } + CaretAssert(balsaDialog); + balsaDialog->show(); + } /* * Update graphics and GUI diff --git a/src/GuiQt/CMakeLists.txt b/src/GuiQt/CMakeLists.txt index e7a651749..8a1a9f80f 100644 --- a/src/GuiQt/CMakeLists.txt +++ b/src/GuiQt/CMakeLists.txt @@ -12,6 +12,10 @@ if(Qt5_FOUND) include_directories(${Qt5OpenGL_INCLUDE_DIRS}) include_directories(${Qt5Network_INCLUDE_DIRS}) include_directories(${Qt5Widgets_INCLUDE_DIRS}) + include_directories(${Qt5WebEngine_INCLUDE_DIRS}) + include_directories(${Qt5WebEngineCore_INCLUDE_DIRS}) + include_directories(${Qt5WebEngineWidgets_INCLUDE_DIRS}) + include_directories(${Qt5WebView_INCLUDE_DIRS}) endif() IF (QT4_FOUND) SET(QT_USE_QTNETWORK TRUE) -- GitLab From 19047cf4facd5f00930e5504bec3a5f6fcf59658 Mon Sep 17 00:00:00 2001 From: John Harwell Date: Fri, 6 Sep 2019 09:06:06 -0500 Subject: [PATCH 400/427] Resolve WB-864 Asymmetric pconn identification show wrong values: Identification information for parcel connectivity (.pconn.nii) files was wrong when the file contained asymmetric data and has been corrected. --- src/CMakeLists.txt | 11 ++- src/Desktop/CMakeLists.txt | 4 +- src/Files/CiftiMappableDataFile.cxx | 101 ++++++++++++---------------- src/GuiQt/BrainBrowserWindow.cxx | 7 ++ src/GuiQt/CMakeLists.txt | 10 +-- 5 files changed, 67 insertions(+), 66 deletions(-) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index d17a0e0ba..8f3626636 100755 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -185,6 +185,15 @@ ENDIF(OPENSSL_FOUND) # https://cmake.org/cmake/help/v3.5/manual/cmake-packages.7.html#manual:cmake-packages(7) # Modules List: http://doc.qt.io/qt-5/qtmodules.html # +SET(WB_WEBKIT_COMPONENTS "") +SET(WB_WEBKIT_LIBS "") +SET(HAVE_QT_WEBKIT False) +IF (HAVE_QT_WEBKIT) + ADD_DEFINITIONS(-DHAVE_WEBKIT) + SET (WB_WEBKIT_COMPONENTS "WebEngine WebEngineCore WebEngineWidgets") + SET (WB_WEBKIT_LIBS "Qt5::WebEngine Qt5::WebEngineCore Qt5::WebEngineWidgets") + +ENDIF (HAVE_QT_WEBKIT) SET(QT_RESULT "Not Found") SET(CARET_QT5_LINK "") IF (WORKBENCH_USE_QT5) @@ -197,7 +206,7 @@ IF (WORKBENCH_USE_QT5) SET(WB_QT_OPENGL_MODULE "OpenGL") ENDIF () - FIND_PACKAGE(Qt5 REQUIRED COMPONENTS Concurrent Core Gui Network ${WB_QT_OPENGL_MODULE} PrintSupport Test WebEngine WebEngineCore WebEngineWidgets Widgets Xml) + FIND_PACKAGE(Qt5 REQUIRED COMPONENTS Concurrent Core Gui Network ${WB_QT_OPENGL_MODULE} PrintSupport Test Widgets Xml ${WB_WEBKIT_COMPONENTS}) IF (WORKBENCH_USE_QT5_QOPENGL_WIDGET) # # QGLWidget is deprecated in Qt 5 and is replaced with QOpenGLWidget diff --git a/src/Desktop/CMakeLists.txt b/src/Desktop/CMakeLists.txt index c52180c3f..b3581ff76 100644 --- a/src/Desktop/CMakeLists.txt +++ b/src/Desktop/CMakeLists.txt @@ -109,9 +109,7 @@ if(Qt5_FOUND) ${QT5_OPENGL_LIB_NAME} Qt5::PrintSupport Qt5::Test - Qt5::WebEngine - Qt5::WebEngineCore - Qt5::WebEngineWidgets + ${WB_WEBKIT_LIBS} Qt5::Widgets Qt5::Xml) endif() diff --git a/src/Files/CiftiMappableDataFile.cxx b/src/Files/CiftiMappableDataFile.cxx index 248efdfa7..dcee6908b 100644 --- a/src/Files/CiftiMappableDataFile.cxx +++ b/src/Files/CiftiMappableDataFile.cxx @@ -4538,74 +4538,59 @@ CiftiMappableDataFile::getMapSurfaceNodeValues(const std::vector& mapIn break; case CiftiMappingType::PARCELS: { - int64_t parcelIndex = -1; - const CiftiParcelsMap& map = ciftiXML.getParcelsMap(m_dataMappingDirectionForCiftiXML); - if (map.getSurfaceNumberOfNodes(structure) == numberOfNodes) { - const std::vector& parcels = map.getParcels(); - parcelIndex = map.getIndexForNode(nodeIndex, - structure); - if ((parcelIndex >= 0) - && (parcelIndex < static_cast(parcels.size()))) { - textValueOut = parcels[parcelIndex].m_name; + + int32_t readingDataParcelIndex = -1; + const CiftiParcelsMap dataMap = ciftiXML.getParcelsMap(m_dataReadingDirectionForCiftiXML); + if (dataMap.getSurfaceNumberOfNodes(structure) == numberOfNodes) { + readingDataParcelIndex = dataMap.getIndexForNode(nodeIndex, structure); + } + + int64_t mappingDataParcelIndex = -1; + if (readingDataParcelIndex >= 0) { + /* + * Only get parcel name if there is a row for reading + */ + const CiftiParcelsMap& mappingMap = ciftiXML.getParcelsMap(m_dataMappingDirectionForCiftiXML); + if (mappingMap.getSurfaceNumberOfNodes(structure) == numberOfNodes) { + const std::vector& mappingParcels = mappingMap.getParcels(); + mappingDataParcelIndex = mappingMap.getIndexForNode(nodeIndex, + structure); + if ((mappingDataParcelIndex >= 0) + && (mappingDataParcelIndex < static_cast(mappingParcels.size()))) { + textValueOut = mappingParcels[mappingDataParcelIndex].m_name; + } } } for (std::vector::const_iterator mapIter = mapIndices.begin(); mapIter != mapIndices.end(); mapIter++) { - const int32_t mapIndex = *mapIter; - - if (parcelIndex >= 0) { - int64_t itemIndex = -1; - switch (ciftiXML.getMappingType(m_dataReadingDirectionForCiftiXML)) { - case CiftiMappingType::BRAIN_MODELS: + if ((mappingDataParcelIndex >= 0) + && (readingDataParcelIndex >= 0)) { + const int64_t numRows = m_ciftiFile->getNumberOfRows(); + const int64_t numCols = m_ciftiFile->getNumberOfColumns(); + + switch (m_dataReadingDirectionForCiftiXML) { + case CiftiXML::ALONG_COLUMN: { - const CiftiBrainModelsMap& map = ciftiXML.getBrainModelsMap(m_dataReadingDirectionForCiftiXML); - if (map.getSurfaceNumberOfNodes(structure) == numberOfNodes) { - itemIndex = map.getIndexForNode(nodeIndex, - structure); - } + std::vector data; + data.resize(numCols); + CaretAssert(readingDataParcelIndex < numRows); + m_ciftiFile->getRow(&data[0], readingDataParcelIndex); + CaretAssertVectorIndex(data, mappingDataParcelIndex); + textValueOut += (" " + AString::number(data[mappingDataParcelIndex])); } break; - case CiftiMappingType::LABELS: - break; - case CiftiMappingType::PARCELS: - itemIndex = mapIndex; - break; - case CiftiMappingType::SCALARS: - itemIndex = mapIndex; - break; - case CiftiMappingType::SERIES: - itemIndex = mapIndex; - break; - } - if (itemIndex >= 0) { - const int64_t numRows = m_ciftiFile->getNumberOfRows(); - const int64_t numCols = m_ciftiFile->getNumberOfColumns(); - - switch (m_dataReadingDirectionForCiftiXML) { - case CiftiXML::ALONG_COLUMN: - { - std::vector data; - data.resize(numRows); - CaretAssert(parcelIndex < numCols); - m_ciftiFile->getColumn(&data[0], parcelIndex); - CaretAssertVectorIndex(data, itemIndex); - textValueOut += (" " + AString::number(data[itemIndex])); - } - break; - case CiftiXML::ALONG_ROW: - { - std::vector data; - data.resize(numCols); - CaretAssert(parcelIndex < numRows); - m_ciftiFile->getRow(&data[0], parcelIndex); - CaretAssertVectorIndex(data, itemIndex); - textValueOut += (" " + AString::number(data[itemIndex])); - } - break; + case CiftiXML::ALONG_ROW: + { + std::vector data; + data.resize(numRows); + CaretAssert(readingDataParcelIndex < numCols); + m_ciftiFile->getColumn(&data[0], readingDataParcelIndex); + CaretAssertVectorIndex(data, mappingDataParcelIndex); + textValueOut += (" " + AString::number(data[mappingDataParcelIndex])); } - + break; } } } diff --git a/src/GuiQt/BrainBrowserWindow.cxx b/src/GuiQt/BrainBrowserWindow.cxx index cbc75faef..7d457a1ec 100644 --- a/src/GuiQt/BrainBrowserWindow.cxx +++ b/src/GuiQt/BrainBrowserWindow.cxx @@ -34,7 +34,9 @@ #include #include #include +#ifdef HAVE_WEBKIT #include +#endif #define __BRAIN_BROWSER_WINDOW_DECLARE__ #include "BrainBrowserWindow.h" @@ -1770,6 +1772,7 @@ BrainBrowserWindow::developerMenuFlagTriggered(QAction* action) * someFunction(); * } */ +#ifdef HAVE_WEBKIT if (enumValue == DeveloperFlagsEnum::DEVELOPER_FLAG_BALSA) { static WuQDialogModal* balsaDialog(NULL); if (balsaDialog == NULL) { @@ -1791,6 +1794,10 @@ BrainBrowserWindow::developerMenuFlagTriggered(QAction* action) EventManager::get()->sendEvent(EventSurfaceColoringInvalidate().getPointer()); EventManager::get()->sendEvent(EventUserInterfaceUpdate().getPointer()); EventManager::get()->sendEvent(EventGraphicsUpdateAllWindows().getPointer()); +#else + WuQMessageBox::informationOk(this, + "Software was built without Qt WebKit, see src/CMakeLists.txt"); +#endif } else { CaretLogSevere("Failed to find develper flag for reading menu: " diff --git a/src/GuiQt/CMakeLists.txt b/src/GuiQt/CMakeLists.txt index 8a1a9f80f..a1fbd8033 100644 --- a/src/GuiQt/CMakeLists.txt +++ b/src/GuiQt/CMakeLists.txt @@ -12,10 +12,12 @@ if(Qt5_FOUND) include_directories(${Qt5OpenGL_INCLUDE_DIRS}) include_directories(${Qt5Network_INCLUDE_DIRS}) include_directories(${Qt5Widgets_INCLUDE_DIRS}) - include_directories(${Qt5WebEngine_INCLUDE_DIRS}) - include_directories(${Qt5WebEngineCore_INCLUDE_DIRS}) - include_directories(${Qt5WebEngineWidgets_INCLUDE_DIRS}) - include_directories(${Qt5WebView_INCLUDE_DIRS}) + if(HAVE_QT_WEBKIT) + include_directories(${Qt5WebEngine_INCLUDE_DIRS}) + include_directories(${Qt5WebEngineCore_INCLUDE_DIRS}) + include_directories(${Qt5WebEngineWidgets_INCLUDE_DIRS}) + include_directories(${Qt5WebView_INCLUDE_DIRS}) + endif(HAVE_QT_WEBKIT) endif() IF (QT4_FOUND) SET(QT_USE_QTNETWORK TRUE) -- GitLab From 7b6815fc10ecaa0a7114aa541dbed35a57165dd1 Mon Sep 17 00:00:00 2001 From: John Harwell Date: Fri, 6 Sep 2019 15:08:57 -0500 Subject: [PATCH 401/427] Added "-foci-import" to wb_command. This command reads foci from text file(s), projects the foci to surface(s) and saves them into a foci file. --- src/Commands/CommandOperationManager.cxx | 2 + src/Operations/CMakeLists.txt | 2 + src/Operations/OperationFociCreate.cxx | 181 +++++++++++++++++++++++ src/Operations/OperationFociCreate.h | 41 +++++ 4 files changed, 226 insertions(+) create mode 100644 src/Operations/OperationFociCreate.cxx create mode 100644 src/Operations/OperationFociCreate.h diff --git a/src/Commands/CommandOperationManager.cxx b/src/Commands/CommandOperationManager.cxx index fff46651a..6ccd240db 100644 --- a/src/Commands/CommandOperationManager.cxx +++ b/src/Commands/CommandOperationManager.cxx @@ -175,6 +175,7 @@ #include "OperationEstimateFiberBinghams.h" #include "OperationFileConvert.h" #include "OperationFileInformation.h" +#include "OperationFociCreate.h" #include "OperationFociGetProjectionVertex.h" #include "OperationFociListCoords.h" #include "OperationGiftiConvert.h" @@ -433,6 +434,7 @@ CommandOperationManager::CommandOperationManager() this->commandOperations.push_back(new CommandParser(new AutoOperationEstimateFiberBinghams())); this->commandOperations.push_back(new CommandParser(new AutoOperationFileConvert())); this->commandOperations.push_back(new CommandParser(new AutoOperationFileInformation())); + this->commandOperations.push_back(new CommandParser(new AutoOperationFociCreate())); this->commandOperations.push_back(new CommandParser(new AutoOperationFociGetProjectionVertex())); this->commandOperations.push_back(new CommandParser(new AutoOperationFociListCoords())); this->commandOperations.push_back(new CommandParser(new AutoOperationGiftiConvert())); diff --git a/src/Operations/CMakeLists.txt b/src/Operations/CMakeLists.txt index c3f1648ad..bf68207bb 100644 --- a/src/Operations/CMakeLists.txt +++ b/src/Operations/CMakeLists.txt @@ -55,6 +55,7 @@ OperationEstimateFiberBinghams.h OperationException.h OperationFileConvert.h OperationFileInformation.h +OperationFociCreate.h OperationFociGetProjectionVertex.h OperationFociListCoords.h OperationGiftiConvert.h @@ -145,6 +146,7 @@ OperationException.cxx OperationEstimateFiberBinghams.cxx OperationFileConvert.cxx OperationFileInformation.cxx +OperationFociCreate.cxx OperationFociGetProjectionVertex.cxx OperationFociListCoords.cxx OperationGiftiConvert.cxx diff --git a/src/Operations/OperationFociCreate.cxx b/src/Operations/OperationFociCreate.cxx new file mode 100644 index 000000000..bede2dc62 --- /dev/null +++ b/src/Operations/OperationFociCreate.cxx @@ -0,0 +1,181 @@ +/*LICENSE_START*/ +/* + * Copyright (C) 2014 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + +#include "OperationFociCreate.h" +#include "OperationException.h" + +#include "CaretLogger.h" +#include "FileInformation.h" +#include "FociFile.h" +#include "Focus.h" +#include "GiftiLabel.h" +#include "GiftiLabelTable.h" +#include "SurfaceProjector.h" + +#include +#include +#include +#include +#include + +using namespace caret; +using namespace std; + +AString OperationFociCreate::getCommandSwitch() +{ + return "-foci-create"; +} + +AString OperationFociCreate::getShortDescription() +{ + return "CREATE A FOCI FILE"; +} + +OperationParameters* OperationFociCreate::getParameters() +{ + OperationParameters* ret = new OperationParameters(); + ret->addFociOutputParameter(1, "output", "the output foci file"); + + ParameterComponent* fociOpt = ret->createRepeatableParameter(2, "-class", "specify class input data"); + fociOpt->addStringParameter(1, "class-name", "name of class"); + fociOpt->addStringParameter(2, "foci-list-file", "text file containing foci names, coordinates, and colors"); + fociOpt->addSurfaceParameter(3, "surface", "surface file for projection of foci list file"); + + ret->setHelpText( + AString("Creates a foci file from names, coordinates, and RGB values in a text file. ") + + "The text file must have the following format (2 lines per focus):\n\n" + + "\n" + " \n...\n\n" + + "Foci names are specified on a separate line from their coordinates and color, in order to let foci names contain spaces. " + + "Whitespace is trimmed from both ends of the foci name, but is kept if it is in the middle of a name. " + + "The values of , , and must be integers from 0 to 255, and will specify the color the foci is drawn as.\n\n" + + "Foci are grouped into classes and the name for the class is specified using the parameter.\n\n" + + "All foci within one text file must be associated with the structure contained in the " + "parameter and are projected to that surface." + ); + return ret; +} + +void OperationFociCreate::useParameters(OperationParameters* myParams, ProgressObject* myProgObj) +{ + LevelProgress myProgress(myProgObj); + + FociFile* outputFociFile = myParams->getOutputFoci(1); + + const vector& myInputs = *(myParams->getRepeatableParameterInstances(2)); + int numInputs = (int)myInputs.size(); + if (numInputs == 0) { + throw OperationException("no inputs specified"); + } + + for (int32_t i = 0; i < numInputs; i++) { + const AString className = myInputs[i]->getString(1).trimmed(); + if (className.isEmpty()) { + throw OperationException("Class name may not be empty"); + } + const AString listFileName = myInputs[i]->getString(2).trimmed(); + if (listFileName.isEmpty()) { + throw OperationException("Foci list (text) file name may not be empty"); + } + const SurfaceFile* surfaceFile = myInputs[i]->getSurface(3); + + SurfaceProjector projector(surfaceFile); + + FileInformation textFileInfo(listFileName); + if (!textFileInfo.exists()) + { + throw OperationException("foci list file doesn't exist: " + + listFileName); + } + fstream fociListFile(listFileName.toLocal8Bit().constData(), fstream::in); + if (!fociListFile.good()) + { + throw OperationException("error reading foci list file:" + + listFileName); + } + + string focusName; + float x, y, z; + int32_t red, green, blue; + int fociCount = 0; + while (fociListFile.good()) + { + ++fociCount;//just for error messages, so start at 1 + getline(fociListFile, focusName); + if (fociListFile.eof() && focusName == "") break;//if end of file trying to read an int, and label name is empty, its really just end of file + fociListFile >> red; + fociListFile >> green; + fociListFile >> blue; + fociListFile >> x; + fociListFile >> y; + + if (!(fociListFile >> z))//yes, that is seriously the correct way to check if input was successfully extracted...so much fail + { + throw OperationException("foci list file is malformed for entry #" + AString::number(fociCount) + ": " + AString(focusName.c_str())); + } + if (red < 0 || red > 255) + { + throw OperationException("bad value for red for entry #" + AString::number(fociCount) + ", " + AString(focusName.c_str()) + ": " + AString::number(red)); + } + if (green < 0 || green > 255) + { + throw OperationException("bad value for green for entry #" + AString::number(fociCount) + ", " + AString(focusName.c_str()) + ": " + AString::number(green)); + } + if (blue < 0 || blue > 255) + { + throw OperationException("bad value for blue for entry #" + AString::number(fociCount) + ", " + AString(focusName.c_str()) + ": " + AString::number(blue)); + } + while (isspace(fociListFile.peek())) + { + fociListFile.ignore();//drop the newline, possible carriage return or other whitespace so that getline doesn't get nothing, and cause int extraction to fail + } + + Focus* focus = new Focus(); + focus->setClassName(className); + focus->setName(AString(focusName.c_str()).trimmed()); + + const float xyz[3] = { x, y, z }; + CaretAssert(focus->getNumberOfProjections() > 0); + focus->getProjection(0)->setStereotaxicXYZ(xyz); + + const int32_t dummyFocusIndex(-1); + projector.projectFocus(dummyFocusIndex, + focus); + + const GiftiLabel* colorLabel = outputFociFile->getNameColorTable()->getLabel(focus->getName()); + if (colorLabel != NULL) { + if ((colorLabel->getRed() != red) + || (colorLabel->getGreen() != green) + || (colorLabel->getBlue() != blue)) { + CaretLogWarning("More than one color for focus named \"" + + focus->getName() + + "\". (All foci with same name use same color)"); + } + } + outputFociFile->getNameColorTable()->addLabel(focus->getName(), + red, green, blue); + outputFociFile->addFocus(focus); + } + } + + if (outputFociFile->getNumberOfFoci() <= 0) { + throw OperationException("No foci were successfully read/projected."); + } +} diff --git a/src/Operations/OperationFociCreate.h b/src/Operations/OperationFociCreate.h new file mode 100644 index 000000000..c12a09290 --- /dev/null +++ b/src/Operations/OperationFociCreate.h @@ -0,0 +1,41 @@ +#ifndef __OPERATION_FOCI_CREATE_H__ +#define __OPERATION_FOCI_CREATE_H__ + +/*LICENSE_START*/ +/* + * Copyright (C) 2014 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + +#include "AbstractOperation.h" + +namespace caret { + + class OperationFociCreate : public AbstractOperation + { + public: + static OperationParameters* getParameters(); + static void useParameters(OperationParameters* myParams, ProgressObject* myProgObj); + static AString getCommandSwitch(); + static AString getShortDescription(); + }; + + typedef TemplateAutoOperation AutoOperationFociCreate; + +} + +#endif //__OPERATION_FOCI_CREATE_H__ -- GitLab From 423ef6f067fb9f34936fb189fa072c2622cc6efd Mon Sep 17 00:00:00 2001 From: John Harwell Date: Fri, 6 Sep 2019 16:16:42 -0500 Subject: [PATCH 402/427] WB-864 asymmetric pconn identification shows wrong values: Additional logic was needed to access the data that had been loaded by the pconn file to determine the correct numerical value (previously is was directly access a row, and the wrong row, in the matrix). --- src/Files/CiftiMappableDataFile.cxx | 43 +++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) diff --git a/src/Files/CiftiMappableDataFile.cxx b/src/Files/CiftiMappableDataFile.cxx index dcee6908b..644cf8034 100644 --- a/src/Files/CiftiMappableDataFile.cxx +++ b/src/Files/CiftiMappableDataFile.cxx @@ -44,6 +44,7 @@ #include "CiftiScalarDataSeriesFile.h" #include "CaretTemporaryFile.h" #include "CiftiXML.h" +#include "ConnectivityDataLoaded.h" #include "DataFileContentInformation.h" #include "EventManager.h" #include "EventCaretPreferencesGet.h" @@ -4545,6 +4546,48 @@ CiftiMappableDataFile::getMapSurfaceNodeValues(const std::vector& mapIn readingDataParcelIndex = dataMap.getIndexForNode(nodeIndex, structure); } + /* + * Special case for matrix type files + */ + const CiftiMappableConnectivityMatrixDataFile* matrixFile = dynamic_cast(this); + if (matrixFile != NULL) { + const ConnectivityDataLoaded* dataLoaded = matrixFile->getConnectivityDataLoaded(); + switch (dataLoaded->getMode()) { + case ConnectivityDataLoaded::MODE_NONE: + return false; + break; + case ConnectivityDataLoaded::MODE_COLUMN: + case ConnectivityDataLoaded::MODE_ROW: + case ConnectivityDataLoaded::MODE_SURFACE_NODE: + case ConnectivityDataLoaded::MODE_SURFACE_NODE_AVERAGE: + case ConnectivityDataLoaded::MODE_VOXEL_IJK_AVERAGE: + case ConnectivityDataLoaded::MODE_VOXEL_XYZ: + { + int mappingDataParcelIndex(-1); + const CiftiParcelsMap& mappingMap = ciftiXML.getParcelsMap(m_dataMappingDirectionForCiftiXML); + if (mappingMap.getSurfaceNumberOfNodes(structure) == numberOfNodes) { + const std::vector& mappingParcels = mappingMap.getParcels(); + mappingDataParcelIndex = mappingMap.getIndexForNode(nodeIndex, + structure); + if ((mappingDataParcelIndex >= 0) + && (mappingDataParcelIndex < static_cast(mappingParcels.size()))) { + textValueOut = mappingParcels[mappingDataParcelIndex].m_name; + + std::vector dataLoaded; + matrixFile->getMapData(0, dataLoaded); + if ((mappingDataParcelIndex >= 0) + && (mappingDataParcelIndex < static_cast(dataLoaded.size()))) { + textValueOut += (" " + AString::number(dataLoaded[mappingDataParcelIndex])); + return true; + } + } + } + + } + break; + } + } + int64_t mappingDataParcelIndex = -1; if (readingDataParcelIndex >= 0) { /* -- GitLab From 8e8d4a971612c9c4ec28e5615eebeda321cd7c53 Mon Sep 17 00:00:00 2001 From: John Harwell Date: Wed, 11 Sep 2019 09:25:58 -0500 Subject: [PATCH 403/427] WB-867 vol_dynconn shows in "open file" filters: Both metric dynconn and volume dynconn have been removed from the file filters in the Open File Dialog. --- src/Common/DataFileTypeEnum.cxx | 98 +++++++++++++++++++---- src/Common/DataFileTypeEnum.h | 6 +- src/GuiQt/IdentifyBrainordinateDialog.cxx | 4 +- 3 files changed, 91 insertions(+), 17 deletions(-) diff --git a/src/Common/DataFileTypeEnum.cxx b/src/Common/DataFileTypeEnum.cxx index 5efd2a2e9..2798a6634 100644 --- a/src/Common/DataFileTypeEnum.cxx +++ b/src/Common/DataFileTypeEnum.cxx @@ -921,24 +921,92 @@ DataFileTypeEnum::getAllEnums(std::vector& allEnums, allEnums.clear(); - const bool includeDenseDynamicFlag = (options & OPTIONS_INCLUDE_CONNECTIVITY_DENSE_DYNAMIC); - const bool includeUnknownFlag = (options & OPTIONS_INCLUDE_UNKNOWN); + const bool includeDenseDynamicFlag = (options & OPTIONS_INCLUDE_CONNECTIVITY_DENSE_DYNAMIC); + const bool includeMetricDynamicFlag = (options & OPTIONS_INCLUDE_METRIC_DENSE_DYNAMIC); + const bool includeVolumeDynamicFlag = (options & OPTIONS_INCLUDE_VOLUME_DENSE_DYNAMIC); + const bool includeUnknownFlag = (options & OPTIONS_INCLUDE_UNKNOWN); - for (std::vector::iterator iter = enumData.begin(); - iter != enumData.end(); - iter++) { - if (iter->enumValue == CONNECTIVITY_DENSE_DYNAMIC) { - if ( ! includeDenseDynamicFlag) { - continue; - } - } - if (iter->enumValue == UNKNOWN) { - if ( ! includeUnknownFlag) { - continue; - } + for (const auto dataType : enumData) { + bool addEnumFlag(true); + + switch (dataType.enumValue) { + case DataFileTypeEnum::ANNOTATION: + break; + case DataFileTypeEnum::ANNOTATION_TEXT_SUBSTITUTION: + break; + case DataFileTypeEnum::BORDER: + break; + case DataFileTypeEnum::CONNECTIVITY_DENSE: + break; + case DataFileTypeEnum::CONNECTIVITY_DENSE_DYNAMIC: + if ( ! includeDenseDynamicFlag) { + addEnumFlag = false; + } + break; + case DataFileTypeEnum::CONNECTIVITY_DENSE_LABEL: + break; + case DataFileTypeEnum::CONNECTIVITY_DENSE_PARCEL: + break; + case DataFileTypeEnum::CONNECTIVITY_PARCEL: + break; + case DataFileTypeEnum::CONNECTIVITY_PARCEL_DENSE: + break; + case DataFileTypeEnum::CONNECTIVITY_PARCEL_LABEL: + break; + case DataFileTypeEnum::CONNECTIVITY_PARCEL_SCALAR: + break; + case DataFileTypeEnum::CONNECTIVITY_PARCEL_SERIES: + break; + case DataFileTypeEnum::CONNECTIVITY_DENSE_SCALAR: + break; + case DataFileTypeEnum::CONNECTIVITY_DENSE_TIME_SERIES: + break; + case DataFileTypeEnum::CONNECTIVITY_FIBER_ORIENTATIONS_TEMPORARY: + break; + case DataFileTypeEnum::CONNECTIVITY_FIBER_TRAJECTORY_TEMPORARY: + break; + case DataFileTypeEnum::CONNECTIVITY_SCALAR_DATA_SERIES: + break; + case DataFileTypeEnum::FOCI: + break; + case DataFileTypeEnum::IMAGE: + break; + case DataFileTypeEnum::LABEL: + break; + case DataFileTypeEnum::METRIC: + break; + case DataFileTypeEnum::METRIC_DYNAMIC: + if ( ! includeMetricDynamicFlag) { + addEnumFlag = false; + } + break; + case DataFileTypeEnum::PALETTE: + break; + case DataFileTypeEnum::RGBA: + break; + case DataFileTypeEnum::SCENE: + break; + case DataFileTypeEnum::SPECIFICATION: + break; + case DataFileTypeEnum::SURFACE: + break; + case DataFileTypeEnum::UNKNOWN: + if ( ! includeUnknownFlag) { + addEnumFlag = false; + } + break; + case DataFileTypeEnum::VOLUME: + break; + case DataFileTypeEnum::VOLUME_DYNAMIC: + if ( ! includeVolumeDynamicFlag) { + addEnumFlag = false; + } + break; } - allEnums.push_back(iter->enumValue); + if (addEnumFlag) { + allEnums.push_back(dataType.enumValue); + } } } diff --git a/src/Common/DataFileTypeEnum.h b/src/Common/DataFileTypeEnum.h index 93c7cb8dc..0143dd8db 100644 --- a/src/Common/DataFileTypeEnum.h +++ b/src/Common/DataFileTypeEnum.h @@ -106,8 +106,12 @@ public: OPTIONS_NONE = 0, /** Include the dense dynamic data file type */ OPTIONS_INCLUDE_CONNECTIVITY_DENSE_DYNAMIC = 1, + /** Include the metric dynamic data file type */ + OPTIONS_INCLUDE_METRIC_DENSE_DYNAMIC = 2, + /** Include the volume dynamic data file type */ + OPTIONS_INCLUDE_VOLUME_DENSE_DYNAMIC = 4, /** Include the unknown data file type */ - OPTIONS_INCLUDE_UNKNOWN = 2 + OPTIONS_INCLUDE_UNKNOWN = 8 }; ~DataFileTypeEnum(); diff --git a/src/GuiQt/IdentifyBrainordinateDialog.cxx b/src/GuiQt/IdentifyBrainordinateDialog.cxx index 4a63cf2fd..a5975c4b8 100644 --- a/src/GuiQt/IdentifyBrainordinateDialog.cxx +++ b/src/GuiQt/IdentifyBrainordinateDialog.cxx @@ -98,7 +98,9 @@ IdentifyBrainordinateDialog::IdentifyBrainordinateDialog(QWidget* parent) */ std::vector allDataFileTypes; DataFileTypeEnum::getAllEnums(allDataFileTypes, - DataFileTypeEnum::OPTIONS_INCLUDE_CONNECTIVITY_DENSE_DYNAMIC); + (DataFileTypeEnum::OPTIONS_INCLUDE_CONNECTIVITY_DENSE_DYNAMIC + | DataFileTypeEnum::OPTIONS_INCLUDE_METRIC_DENSE_DYNAMIC + | DataFileTypeEnum::OPTIONS_INCLUDE_VOLUME_DENSE_DYNAMIC)); std::vector supportedCiftiRowFileTypes; std::vector supportedLabelFileTypes; -- GitLab From bb1ae8da094f219391f57c6db8e0bb4df3accfcd Mon Sep 17 00:00:00 2001 From: John Harwell Date: Wed, 11 Sep 2019 10:00:55 -0500 Subject: [PATCH 404/427] WB-866: Annotation filename extension (.annot) is same as a FreeSurfer File: Removed the ".annot" extension for a new annotation file. "wb_annot" is the only allowable extension. --- src/Common/DataFileTypeEnum.cxx | 1 - src/Files/AnnotationFile.cxx | 9 +++++++++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/src/Common/DataFileTypeEnum.cxx b/src/Common/DataFileTypeEnum.cxx index 2798a6634..94d297af4 100644 --- a/src/Common/DataFileTypeEnum.cxx +++ b/src/Common/DataFileTypeEnum.cxx @@ -123,7 +123,6 @@ DataFileTypeEnum::initialize() "Annotation", "ANNOTATION", false, - "annot", "wb_annot")); enumData.push_back(DataFileTypeEnum(ANNOTATION_TEXT_SUBSTITUTION, diff --git a/src/Files/AnnotationFile.cxx b/src/Files/AnnotationFile.cxx index bd2350966..ac291f87e 100644 --- a/src/Files/AnnotationFile.cxx +++ b/src/Files/AnnotationFile.cxx @@ -1659,6 +1659,15 @@ AnnotationFile::readFile(const AString& filename) void AnnotationFile::writeFile(const AString& filename) { + /* + * JWH, 11 Sep 2019 + * WB-866 removed the ".annot" extension for the annotation file. + * It is possible that a user may have a disk annotation file in + * multiple scenes. If this message caused the user to change + * the file extension from ".annot" to ".wb_annot", it could break + * other scenes if they are not updated. + * So, continue avoiding a log message if ".annot" extension is present. + */ if (!(filename.endsWith(".annot") || filename.endsWith(".wb_annot"))) { CaretLogWarning("annotation file '" + filename + "' should be saved ending in .annot"); -- GitLab From fb27e620b977e363280751492fbba5a7511a4e04 Mon Sep 17 00:00:00 2001 From: Tim Coalson Date: Mon, 16 Sep 2019 17:15:03 -0500 Subject: [PATCH 405/427] improve error messages in -cifti-separate --- src/Algorithms/AlgorithmCiftiSeparate.cxx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Algorithms/AlgorithmCiftiSeparate.cxx b/src/Algorithms/AlgorithmCiftiSeparate.cxx index 271ea6fb0..f26286cbd 100644 --- a/src/Algorithms/AlgorithmCiftiSeparate.cxx +++ b/src/Algorithms/AlgorithmCiftiSeparate.cxx @@ -138,7 +138,7 @@ void AlgorithmCiftiSeparate::useParameters(OperationParameters* myParams, Progre StructureEnum::Enum myStruct = StructureEnum::fromName(structName, &ok); if (!ok) { - throw AlgorithmException("unrecognized structure type"); + throw AlgorithmException("unrecognized structure type: '" + structName + "'"); } MetricFile* metricOut = metricInstances[i]->getOutputMetric(2); MetricFile* roiOut = NULL; @@ -158,7 +158,7 @@ void AlgorithmCiftiSeparate::useParameters(OperationParameters* myParams, Progre StructureEnum::Enum myStruct = StructureEnum::fromName(structName, &ok); if (!ok) { - throw AlgorithmException("unrecognized structure type"); + throw AlgorithmException("unrecognized structure type: '" + structName + "'"); } VolumeFile* volOut = volumeInstances[i]->getOutputVolume(2); VolumeFile* roiOut = NULL; -- GitLab From c1f41cd2e75e354c4358f514fd516722a8a802ea Mon Sep 17 00:00:00 2001 From: Tim Coalson Date: Mon, 16 Sep 2019 17:36:05 -0500 Subject: [PATCH 406/427] missed edit for error messages in -cifti-separate --- src/Algorithms/AlgorithmCiftiSeparate.cxx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Algorithms/AlgorithmCiftiSeparate.cxx b/src/Algorithms/AlgorithmCiftiSeparate.cxx index f26286cbd..886c7c2cb 100644 --- a/src/Algorithms/AlgorithmCiftiSeparate.cxx +++ b/src/Algorithms/AlgorithmCiftiSeparate.cxx @@ -118,7 +118,7 @@ void AlgorithmCiftiSeparate::useParameters(OperationParameters* myParams, Progre StructureEnum::Enum myStruct = StructureEnum::fromName(structName, &ok); if (!ok) { - throw AlgorithmException("unrecognized structure type"); + throw AlgorithmException("unrecognized structure type: '" + structName + "'"); } LabelFile* labelOut = labelInstances[i]->getOutputLabel(2); MetricFile* roiOut = NULL; -- GitLab From ceafdc7d6e8d1b2c9ff486a5ff5eefa2560d2003 Mon Sep 17 00:00:00 2001 From: John Harwell Date: Tue, 17 Sep 2019 09:49:13 -0500 Subject: [PATCH 407/427] Resolve WB-868 Image capture defaults. Defaults are now Dimensions=Custom; Image Dimensions Width=7.5 inches; Height= proportional to width; Resolution=300 pixels/inch; Copy to Clipboard=OFF; Save to File=ON; save to file name empty so user is forced to choose correct path and file name. --- src/Files/ImageCaptureSettings.cxx | 8 ++++++++ src/GuiQt/ImageCaptureDialog.cxx | 24 ++++++++++++++++-------- 2 files changed, 24 insertions(+), 8 deletions(-) diff --git a/src/Files/ImageCaptureSettings.cxx b/src/Files/ImageCaptureSettings.cxx index c1c71a0a6..fab933ec4 100644 --- a/src/Files/ImageCaptureSettings.cxx +++ b/src/Files/ImageCaptureSettings.cxx @@ -65,6 +65,14 @@ SceneableInterface() setPixelWidthAndHeight(512, 512); + /* WB-868 Defautl Custom, 300 DPI, 7.5 inches width, copy to clipboard OFF, save to file ON, no file name*/ + m_dimensionsMode = ImageCaptureDimensionsModeEnum::IMAGE_CAPTURE_DIMENSIONS_MODE_CUSTOM; + setImageResolutionInSelectedUnits(300.0); + setSpatialWidth(7.5); + m_saveToFileEnabled = true; + m_copyToClipboardEnabled = false; + m_imageFileName = ""; + m_sceneAssistant = new SceneClassAssistant(); m_sceneAssistant->add("m_pixelWidth", diff --git a/src/GuiQt/ImageCaptureDialog.cxx b/src/GuiQt/ImageCaptureDialog.cxx index ac3a43aaf..c1460f2d8 100644 --- a/src/GuiQt/ImageCaptureDialog.cxx +++ b/src/GuiQt/ImageCaptureDialog.cxx @@ -1037,6 +1037,14 @@ ImageCaptureDialog::applyButtonClicked() return; } + AString imageFileName = m_imageFileNameLineEdit->text().trimmed(); + if (m_saveImageToFileCheckBox->isChecked()) { + if (imageFileName.isEmpty()) { + WuQMessageBox::errorOk(this, "Save to File name is empty. Choose an Image File."); + return; + } + } + /* * Default to width of window that may exclude empty regions * caused by locking of aspect ratio. @@ -1102,35 +1110,35 @@ ImageCaptureDialog::applyButtonClicked() QApplication::clipboard()->setImage(*imageFile.getAsQImage(), QClipboard::Clipboard); } - + if (m_saveImageToFileCheckBox->isChecked()) { std::vector imageFileExtensions; AString defaultFileExtension; ImageFile::getImageFileExtensions(imageFileExtensions, defaultFileExtension); - AString filename = m_imageFileNameLineEdit->text().trimmed(); + CaretAssert( ! imageFileName.isEmpty()); bool validExtension = false; for (std::vector::iterator extensionIterator = imageFileExtensions.begin(); extensionIterator != imageFileExtensions.end(); extensionIterator++) { - if (filename.endsWith(*extensionIterator)) { + if (imageFileName.endsWith(*extensionIterator)) { validExtension = true; } } - if (validExtension == false) { + if ( ! validExtension) { if (defaultFileExtension.isEmpty() == false) { - filename += ("." + defaultFileExtension); + imageFileName += ("." + defaultFileExtension); } } try { - imageFile.writeFile(filename); + imageFile.writeFile(imageFileName); } catch (const DataFileException& /*e*/) { - QString msg("Unable to save: " + filename); + QString msg("Unable to save: " + imageFileName); WuQMessageBox::errorOk(this, msg); errorFlag = true; } @@ -1139,7 +1147,7 @@ ImageCaptureDialog::applyButtonClicked() ChartMatrixDisplayProperties::setManualScaleModeWindowWidthHeightScaling(1.0, 1.0); - if (errorFlag == false) { + if ( ! errorFlag) { /* * Display over "Capture" (the renamed Apply) button. */ -- GitLab From cc849b558169e88c906e607deb950bb7fcad5aa2 Mon Sep 17 00:00:00 2001 From: Tim Coalson Date: Mon, 23 Sep 2019 16:10:26 -0500 Subject: [PATCH 408/427] specify that -show-scene width/height are pixels --- src/Operations/OperationShowScene.cxx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Operations/OperationShowScene.cxx b/src/Operations/OperationShowScene.cxx index 587402084..19b9e4990 100644 --- a/src/Operations/OperationShowScene.cxx +++ b/src/Operations/OperationShowScene.cxx @@ -107,9 +107,9 @@ OperationShowScene::getParameters() ret->addStringParameter(3, "image-file-name", "output image file name"); - ret->addIntegerParameter(4, "image-width", "width of output image(s)"); + ret->addIntegerParameter(4, "image-width", "width of output image(s), in pixels"); - ret->addIntegerParameter(5, "image-height", "height of output image(s)"); + ret->addIntegerParameter(5, "image-height", "height of output image(s), in pixels"); const QString windowSizeSwitch("-use-window-size"); ret->createOptionalParameter(6, windowSizeSwitch, "Override image size with window size"); -- GitLab From b5da1820838f606727c1df18a79aecf958361ac7 Mon Sep 17 00:00:00 2001 From: Tim Coalson Date: Tue, 24 Sep 2019 17:49:03 -0500 Subject: [PATCH 409/427] add ribbon mapping option to output ROI of what vertices didn't receive data --- .../AlgorithmVolumeToSurfaceMapping.cxx | 31 +++++++++++++++++-- .../AlgorithmVolumeToSurfaceMapping.h | 2 +- 2 files changed, 30 insertions(+), 3 deletions(-) diff --git a/src/Algorithms/AlgorithmVolumeToSurfaceMapping.cxx b/src/Algorithms/AlgorithmVolumeToSurfaceMapping.cxx index 4e84ab5eb..050eb6e4d 100644 --- a/src/Algorithms/AlgorithmVolumeToSurfaceMapping.cxx +++ b/src/Algorithms/AlgorithmVolumeToSurfaceMapping.cxx @@ -74,6 +74,8 @@ OperationParameters* AlgorithmVolumeToSurfaceMapping::getParameters() ribbonOpt->createOptionalParameter(7, "-thin-columns", "use non-overlapping polyhedra"); OptionalParameter* gaussianOpt = ribbonOpt->createOptionalParameter(8, "-gaussian", "reduce weight to voxels that aren't near "); gaussianOpt->addDoubleParameter(1, "scale", "value to multiply the local thickness by, to get the gaussian sigma"); + OptionalParameter* badVertOpt = ribbonOpt->createOptionalParameter(9, "-bad-vertices-out", "output an ROI of which vertices didn't intersect any valid voxels"); + badVertOpt->addMetricOutputParameter(1, "roi-out", "the output metric file of vertices that have no data"); OptionalParameter* ribbonWeights = ribbonOpt->createOptionalParameter(5, "-output-weights", "write the voxel weights for a vertex to a volume file"); ribbonWeights->addIntegerParameter(1, "vertex", "the vertex number to get the voxel weights for, 0-based"); ribbonWeights->addVolumeOutputParameter(2, "weights-out", "volume to write the weights to"); @@ -216,6 +218,12 @@ void AlgorithmVolumeToSurfaceMapping::useParameters(OperationParameters* myParam gaussScale = (float)gaussianOpt->getDouble(1); if (!(gaussScale > 0.0f)) throw AlgorithmException("gaussian scale must be positive"); } + MetricFile* badVertices = NULL; + OptionalParameter* badVertOpt = ribbonOpt->getOptionalParameter(9); + if (badVertOpt->m_present) + { + badVertices = badVertOpt->getOutputMetric(1); + } int weightsOutVertex = -1; VolumeFile* weightsOut = NULL; OptionalParameter* ribbonWeights = ribbonOpt->getOptionalParameter(5); @@ -225,7 +233,7 @@ void AlgorithmVolumeToSurfaceMapping::useParameters(OperationParameters* myParam weightsOut = ribbonWeights->getOutputVolume(2); } AlgorithmVolumeToSurfaceMapping(myProgObj, myVolume, mySurface, myMetricOut, innerSurf, outerSurf, myRoiVol, subdivisions, thinColumns, - mySubVol, gaussScale, weightsOutVertex, weightsOut); + mySubVol, gaussScale, badVertices, weightsOutVertex, weightsOut); OptionalParameter* ribbonWeightsText = ribbonOpt->getOptionalParameter(6); if (ribbonWeightsText->m_present) {//do this after the algorithm, to let it do the error condition checking @@ -362,7 +370,7 @@ AlgorithmVolumeToSurfaceMapping::AlgorithmVolumeToSurfaceMapping(ProgressObject* //ribbon mapping AlgorithmVolumeToSurfaceMapping::AlgorithmVolumeToSurfaceMapping(ProgressObject* myProgObj, const VolumeFile* myVolume, const SurfaceFile* mySurface, MetricFile* myMetricOut, const SurfaceFile* innerSurf, const SurfaceFile* outerSurf, const VolumeFile* roiVol, - const int32_t& subdivisions, const bool& thinColumns, const int64_t& mySubVol, const float& gaussScale, + const int32_t& subdivisions, const bool& thinColumns, const int64_t& mySubVol, const float& gaussScale, MetricFile* badVertices, const int& weightsOutVertex, VolumeFile* weightsOut) : AbstractAlgorithm(myProgObj) { LevelProgress myProgress(myProgObj); @@ -382,6 +390,13 @@ AlgorithmVolumeToSurfaceMapping::AlgorithmVolumeToSurfaceMapping(ProgressObject* int64_t numNodes = mySurface->getNumberOfNodes(); myMetricOut->setNumberOfNodesAndColumns(numNodes, numColumns); myMetricOut->setStructure(mySurface->getStructure()); + vector badVertScratch; + if (badVertices != NULL) + { + badVertices->setNumberOfNodesAndColumns(numNodes, 1); + badVertices->setStructure(mySurface->getStructure()); + badVertScratch.resize(numNodes, 0.0f); + } if (!mySurface->hasNodeCorrespondence(*outerSurf) || !mySurface->hasNodeCorrespondence(*innerSurf)) { throw AlgorithmException("all surfaces must have vertex correspondence"); @@ -444,6 +459,10 @@ AlgorithmVolumeToSurfaceMapping::AlgorithmVolumeToSurfaceMapping(ProgressObject* myScratch[node] /= totalWeight; } else { myScratch[node] = 0.0f; + if (thisCol == 0 && badVertices != NULL) + { + badVertScratch[node] = 1.0f; + } } } myMetricOut->setValuesForColumn(thisCol, myScratch); @@ -477,11 +496,19 @@ AlgorithmVolumeToSurfaceMapping::AlgorithmVolumeToSurfaceMapping(ProgressObject* myScratch[node] /= totalWeight; } else { myScratch[node] = 0.0f; + if (badVertices != NULL) + { + badVertScratch[node] = 1.0f; + } } } myMetricOut->setValuesForColumn(thisCol, myScratch); } } + if (badVertices != NULL) + { + badVertices->setValuesForColumn(0, badVertScratch.data()); + } } void AlgorithmVolumeToSurfaceMapping::precomputeWeightsRibbon(vector >& myWeights, const VolumeSpace& volSpace, diff --git a/src/Algorithms/AlgorithmVolumeToSurfaceMapping.h b/src/Algorithms/AlgorithmVolumeToSurfaceMapping.h index 902f316cc..485147ec7 100644 --- a/src/Algorithms/AlgorithmVolumeToSurfaceMapping.h +++ b/src/Algorithms/AlgorithmVolumeToSurfaceMapping.h @@ -55,7 +55,7 @@ namespace caret { AlgorithmVolumeToSurfaceMapping(ProgressObject* myProgObj, const VolumeFile* myVolume, const SurfaceFile* mySurface, MetricFile* myMetricOut, const SurfaceFile* innerSurf, const SurfaceFile* outerSurf, const VolumeFile* roiVol = NULL, const int32_t& subdivisions = 3, const bool& thinColumns = false, - const int64_t& mySubVol = -1, const float& gaussScale = -1.0f, + const int64_t& mySubVol = -1, const float& gaussScale = -1.0f, MetricFile* badVertices = NULL, const int& weightsOutVertex = -1, VolumeFile* weightsOut = NULL); AlgorithmVolumeToSurfaceMapping(ProgressObject* myProgObj, const VolumeFile* myVolume, const SurfaceFile* mySurface, MetricFile* myMetricOut, const VolumeFile* roiVol, const MetricFile* thickness, const float& sigma, const int64_t& mySubVol = -1, const bool& oldCutoffBug = false); -- GitLab From 59c69faa9fdbe14853d658db15ce2dae1de6a948 Mon Sep 17 00:00:00 2001 From: Tim Coalson Date: Tue, 24 Sep 2019 18:12:40 -0500 Subject: [PATCH 410/427] add the remaining label modify keys commands --- .../AlgorithmCiftiLabelModifyKeys.cxx | 220 +++++++++++++++++ .../AlgorithmCiftiLabelModifyKeys.h | 48 ++++ src/Algorithms/AlgorithmLabelModifyKeys.cxx | 4 +- src/Algorithms/AlgorithmTemplate.cxx.txt | 19 +- .../AlgorithmVolumeLabelModifyKeys.cxx | 225 ++++++++++++++++++ .../AlgorithmVolumeLabelModifyKeys.h | 48 ++++ src/Algorithms/CMakeLists.txt | 4 + src/Commands/CommandOperationManager.cxx | 4 + 8 files changed, 559 insertions(+), 13 deletions(-) create mode 100644 src/Algorithms/AlgorithmCiftiLabelModifyKeys.cxx create mode 100644 src/Algorithms/AlgorithmCiftiLabelModifyKeys.h create mode 100644 src/Algorithms/AlgorithmVolumeLabelModifyKeys.cxx create mode 100644 src/Algorithms/AlgorithmVolumeLabelModifyKeys.h diff --git a/src/Algorithms/AlgorithmCiftiLabelModifyKeys.cxx b/src/Algorithms/AlgorithmCiftiLabelModifyKeys.cxx new file mode 100644 index 000000000..319e9c7c6 --- /dev/null +++ b/src/Algorithms/AlgorithmCiftiLabelModifyKeys.cxx @@ -0,0 +1,220 @@ +/*LICENSE_START*/ +/* + * Copyright (C) 2018 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + +#include "AlgorithmCiftiLabelModifyKeys.h" +#include "AlgorithmException.h" + +#include "CiftiFile.h" +#include "FileInformation.h" +#include "GiftiLabel.h" + +#include +#include + +using namespace caret; +using namespace std; + +AString AlgorithmCiftiLabelModifyKeys::getCommandSwitch() +{ + return "-cifti-label-modify-keys"; +} + +AString AlgorithmCiftiLabelModifyKeys::getShortDescription() +{ + return "CHANGE KEY VALUES IN A DLABEL FILE"; +} + +OperationParameters* AlgorithmCiftiLabelModifyKeys::getParameters() +{ + OperationParameters* ret = new OperationParameters(); + ret->addCiftiParameter(1, "cifti-in", "the input dlabel file"); + ret->addStringParameter(2, "remap-file", "text file with old and new key values"); + ret->addCiftiOutputParameter(3, "cifti-out", "the output dlabel file"); + + OptionalParameter* columnOpt = ret->createOptionalParameter(4, "-column", "select a single column to use"); + columnOpt->addStringParameter(1, "column", "the column number or name"); + + ret->setHelpText( + AString(" should have lines of the form 'oldkey newkey', like so:\n\n") + + "3 5\n5 8\n8 2\n\n" + + "This would change the current label with key '3' to use the key '5' instead, 5 would use 8, and 8 would use 2. " + + "Any collision in key values results in the label that was not specified in the remap file getting remapped to an otherwise unused key. " + + "Remapping more than one key to the same new key, or the same key to more than one new key, results in an error. " + + "This will not change the appearance of the file when displayed, as it will change the key values in the data at the same time." + ); + return ret; +} + +void AlgorithmCiftiLabelModifyKeys::useParameters(OperationParameters* myParams, ProgressObject* myProgObj) +{ + CiftiFile* ciftiIn = myParams->getCifti(1); + AString remapName = myParams->getString(2); + CiftiFile* ciftiOut = myParams->getOutputCifti(3); + int column = -1; + OptionalParameter* columnOpt = myParams->getOptionalParameter(4); + if (columnOpt->m_present) + { + column = ciftiIn->getCiftiXML().getMap(CiftiXML::ALONG_ROW)->getIndexFromNumberOrName(columnOpt->getString(1)); + if (column < 0) throw AlgorithmException("invalid column specified"); + } + FileInformation textFileInfo(remapName); + if (!textFileInfo.exists()) + { + throw AlgorithmException("label list file doesn't exist"); + } + fstream remapFile(remapName.toLocal8Bit().constData(), fstream::in); + if (!remapFile.good()) + { + throw AlgorithmException("error reading label list file"); + } + map remap; + int32_t oldkey, newkey; + while (remapFile >> oldkey >> newkey) + { + if (remap.find(oldkey) != remap.end()) throw AlgorithmException("remapping tried to duplicate label " + AString::number(oldkey)); + remap[oldkey] = newkey; + } + AlgorithmCiftiLabelModifyKeys(myProgObj, ciftiIn, remap, ciftiOut, column); +} + +AlgorithmCiftiLabelModifyKeys::AlgorithmCiftiLabelModifyKeys(ProgressObject* myProgObj, const CiftiFile* ciftiIn, const map remap, CiftiFile* ciftiOut, const int column) : AbstractAlgorithm(myProgObj) +{ + LevelProgress myProgress(myProgObj); + const CiftiXML& xmlIn = ciftiIn->getCiftiXML(); + if (xmlIn.getNumberOfDimensions() != 2) throw AlgorithmException("cifti label modify keys only supports 2D cifti"); + if (xmlIn.getMappingType(CiftiXML::ALONG_ROW) != CiftiMappingType::LABELS) throw AlgorithmException("input cifti file does not have labels mapping along row"); + const CiftiLabelsMap& oldmap = xmlIn.getLabelsMap(CiftiXML::ALONG_ROW); + int64_t numRows = ciftiIn->getNumberOfRows(); + int64_t startCol = 0, endCol = ciftiIn->getNumberOfColumns(); + if (column > -1) + { + startCol = column; + endCol = column + 1; + } + CiftiLabelsMap newmap; + newmap.setLength(endCol - startCol); + vector> valChanges(endCol - startCol); + for (int64_t i = startCol; i < endCol; ++i) + { + newmap.setMapName(i - startCol, oldmap.getMapName(i)); + const GiftiLabelTable* oldTable = oldmap.getMapLabelTable(i); + int32_t oldUnlabeled = oldTable->getUnassignedLabelKey();//because GiftiLabelTable is quirky, we need to check if the unlabeled value ends up as something other than 0 + GiftiLabelTable newTable;//careful, label 0 is created by the constructor + bool setZero = false;//because of this, we need to track if we overwrote it, so that we can pretend it isn't there + for (map::const_iterator iter = remap.begin(); iter != remap.end(); ++iter) + { + const GiftiLabel* oldLabel = oldTable->getLabel(iter->first); + if (oldLabel == NULL) throw AlgorithmException("label key " + AString::number(iter->first) + " does not exist in the input file"); + GiftiLabel newLabel(*oldLabel); + newLabel.setKey(iter->second); + if (iter->first == oldUnlabeled) + { + if (iter->second != 0)//if it isn't the default unlabeled value, then we have to do something + { + if (newTable.getLabel(iter->second) != NULL) throw AlgorithmException("remapping tried to set label " + AString::number(iter->second) + " more than once"); + newTable.deleteLabel(0);//delete the default, since we don't know what overwrites it, if anything + newTable.insertLabel(&newLabel);//insert forces it to use the key in the label, even if it causes a duplicate name (which the original might theoretically have) + } else {//otherwise, just error checking + if (setZero) throw AlgorithmException("remapping tried to set label " + AString::number(iter->second) + " more than once"); + setZero = true;//we do have to track that zero now contains something that can't be overwritten + } + } else { + if (iter->second == 0)//if it remaps to the default unlabeled key, we have to check it differently + { + if (setZero) throw AlgorithmException("remapping tried to set label " + AString::number(iter->second) + " more than once"); + setZero = true; + newTable.insertLabel(&newLabel);//insert forces it to use the key in the label, so it will overwrite the existing default 0 key + } else {//finally, the simple case + if (newTable.getLabel(iter->second) != NULL) throw AlgorithmException("remapping tried to set label " + AString::number(iter->second) + " more than once"); + newTable.insertLabel(&newLabel);//insert forces it to use the key in the label, even if it causes a duplicate name (which the original might theoretically have) + } + } + } + set keys = oldTable->getKeys(), collisions; + for (set::const_iterator iter = keys.begin(); iter != keys.end(); ++iter) + { + if (remap.find(*iter) == remap.end())//skip if it was remapped + { + if (*iter == 0)//again with the special default 0 key + { + if (setZero)//check for collision + { + collisions.insert(*iter); + } else { + setZero = true; + if (*iter != oldUnlabeled)//if its merely the unassigned label already, we can just keep the existing default + { + newTable.insertLabel(oldTable->getLabel(*iter)); + } + } + } else { + if (newTable.getLabel(*iter) == NULL) + { + newTable.insertLabel(oldTable->getLabel(*iter)); + } else {//collision + collisions.insert(*iter); + } + } + } + } + map& valueChanges = valChanges[i - startCol]; + valueChanges = remap;//start with the specified changes, then add the collision changes + for (set::const_iterator iter = collisions.begin(); iter != collisions.end(); ++iter) + {//now deal with collisions + int32_t newKey = newTable.generateUnusedKey(); + GiftiLabel newLabel(*(oldTable->getLabel(*iter))); + newLabel.setKey(newKey); + newTable.insertLabel(&newLabel);//insert forces it to use the key in the label, even if it causes a duplicate name (which the original might theoretically have) + valueChanges[*iter] = newKey; + } + *(newmap.getMapLabelTable(i - startCol)) = newTable; + } + CiftiXML xmlOut = xmlIn; + xmlOut.setMap(CiftiXML::ALONG_ROW, newmap); + ciftiOut->setCiftiXML(xmlOut); + vector scratchRow(ciftiIn->getNumberOfColumns()), outRow(endCol - startCol); + for (int i = 0; i < numRows; ++i) + { + ciftiIn->getRow(scratchRow.data(), i); + for (int j = startCol; j < endCol; ++j) + { + int32_t oldkey = floor(scratchRow[j] + 0.5f); + auto iter = valChanges[j - startCol].find(oldkey); + if (iter == valChanges[j - startCol].end()) + { + outRow[j - startCol] = oldkey; + } else { + outRow[j - startCol] = iter->second; + } + } + ciftiOut->setRow(outRow.data(), i); + } +} + +float AlgorithmCiftiLabelModifyKeys::getAlgorithmInternalWeight() +{ + return 1.0f;//override this if needed, if the progress bar isn't smooth +} + +float AlgorithmCiftiLabelModifyKeys::getSubAlgorithmWeight() +{ + //return AlgorithmInsertNameHere::getAlgorithmWeight();//if you use a subalgorithm + return 0.0f; +} diff --git a/src/Algorithms/AlgorithmCiftiLabelModifyKeys.h b/src/Algorithms/AlgorithmCiftiLabelModifyKeys.h new file mode 100644 index 000000000..96b70737f --- /dev/null +++ b/src/Algorithms/AlgorithmCiftiLabelModifyKeys.h @@ -0,0 +1,48 @@ +#ifndef __ALGORITHM_CIFTI_LABEL_MODIFY_KEYS_H__ +#define __ALGORITHM_CIFTI_LABEL_MODIFY_KEYS_H__ + +/*LICENSE_START*/ +/* + * Copyright (C) 2018 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + +#include "AbstractAlgorithm.h" + +#include + +namespace caret { + + class AlgorithmCiftiLabelModifyKeys : public AbstractAlgorithm + { + AlgorithmCiftiLabelModifyKeys(); + protected: + static float getSubAlgorithmWeight(); + static float getAlgorithmInternalWeight(); + public: + AlgorithmCiftiLabelModifyKeys(ProgressObject* myProgObj, const CiftiFile* ciftiIn, const std::map remap, CiftiFile* ciftiOut, const int column = -1); + static OperationParameters* getParameters(); + static void useParameters(OperationParameters* myParams, ProgressObject* myProgObj); + static AString getCommandSwitch(); + static AString getShortDescription(); + }; + + typedef TemplateAutoOperation AutoAlgorithmCiftiLabelModifyKeys; + +} + +#endif //__ALGORITHM_CIFTI_LABEL_MODIFY_KEYS_H__ diff --git a/src/Algorithms/AlgorithmLabelModifyKeys.cxx b/src/Algorithms/AlgorithmLabelModifyKeys.cxx index b845ffd9d..900d197b1 100644 --- a/src/Algorithms/AlgorithmLabelModifyKeys.cxx +++ b/src/Algorithms/AlgorithmLabelModifyKeys.cxx @@ -60,7 +60,7 @@ OperationParameters* AlgorithmLabelModifyKeys::getParameters() "This would change the current label with key '3' to use the key '5' instead, 5 would use 8, and 8 would use 2. " + "Any collision in key values results in the label that was not specified in the remap file getting remapped to an otherwise unused key. " + "Remapping more than one key to the same new key, or the same key to more than one new key, results in an error. " + - "This will not change the appearance of the file when displayed, it will change the keys in the data at the same time." + "This will not change the appearance of the file when displayed, as it will change the key values in the data at the same time." ); return ret; } @@ -107,7 +107,7 @@ AlgorithmLabelModifyKeys::AlgorithmLabelModifyKeys(ProgressObject* myProgObj, co for (map::const_iterator iter = remap.begin(); iter != remap.end(); ++iter) { const GiftiLabel* oldLabel = oldTable->getLabel(iter->first); - if (oldLabel == NULL) throw AlgorithmException("label " + AString::number(iter->first) + " does not exist in the input label file"); + if (oldLabel == NULL) throw AlgorithmException("label key " + AString::number(iter->first) + " does not exist in the input file"); GiftiLabel newLabel(*oldLabel); newLabel.setKey(iter->second); if (iter->first == oldUnlabeled) diff --git a/src/Algorithms/AlgorithmTemplate.cxx.txt b/src/Algorithms/AlgorithmTemplate.cxx.txt index 107650d1f..e6d29286a 100644 --- a/src/Algorithms/AlgorithmTemplate.cxx.txt +++ b/src/Algorithms/AlgorithmTemplate.cxx.txt @@ -42,8 +42,8 @@ OperationParameters* AlgorithmName::getParameters() //ret->addMetricOutputParameter(2, "metric-out", "the output metric"); - //OptionalParameter* columnSelect = ret->createOptionalParameter(3, "-column", "select a single column"); - //columnSelect->addStringParameter(1, "column", "the column number or name"); + //OptionalParameter* columnOpt = ret->createOptionalParameter(3, "-column", "select a single column"); + //columnOpt->addStringParameter(1, "column", "the column number or name"); ret->setHelpText( AString("This is where you set the help text. ") + @@ -59,15 +59,12 @@ void AlgorithmName::useParameters(OperationParameters* myParams, ProgressObject* { //SurfaceFile* mySurf = myParams->getSurface(1);//gets the surface with key 1 //MetricFile* myMetricOut = myParams->getOutputMetric(2);//gets the output metric with key 2 - /*OptionalParameter* columnSelect = myParams->getOptionalParameter(3);//gets optional parameter with key 3 - int columnNum = -1; - if (columnSelect->m_present) - {//set up to use the single column - columnNum = (int)myMetric->getMapIndexFromNameOrNumber(columnSelect->getString(1)); - if (columnNum < 0) - { - throw AlgorithmException("invalid column specified"); - } + /*OptionalParameter* columnOpt = myParams->getOptionalParameter(3);//gets option with key 3 + int column = -1; + if (columnOpt->m_present) + {//set up to use a single column + column = myMetric->getMapIndexFromNameOrNumber(columnOpt->getString(1)); + if (column < 0) throw AlgorithmException("invalid column specified"); }//*/ AlgorithmName(myProgObj /*INSERT PARAMETERS HERE*/);//executes the algorithm } diff --git a/src/Algorithms/AlgorithmVolumeLabelModifyKeys.cxx b/src/Algorithms/AlgorithmVolumeLabelModifyKeys.cxx new file mode 100644 index 000000000..a6366a038 --- /dev/null +++ b/src/Algorithms/AlgorithmVolumeLabelModifyKeys.cxx @@ -0,0 +1,225 @@ +/*LICENSE_START*/ +/* + * Copyright (C) 2018 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + +#include "AlgorithmVolumeLabelModifyKeys.h" +#include "AlgorithmException.h" + +#include "FileInformation.h" +#include "GiftiLabel.h" +#include "GiftiLabelTable.h" +#include "VolumeFile.h" + +#include +#include + +using namespace caret; +using namespace std; + +AString AlgorithmVolumeLabelModifyKeys::getCommandSwitch() +{ + return "-volume-label-modify-keys"; +} + +AString AlgorithmVolumeLabelModifyKeys::getShortDescription() +{ + return "CHANGE KEY VALUES IN A VOLUME LABEL FILE"; +} + +OperationParameters* AlgorithmVolumeLabelModifyKeys::getParameters() +{ + OperationParameters* ret = new OperationParameters(); + ret->addVolumeParameter(1, "volume-in", "the input volume label file"); + ret->addStringParameter(2, "remap-file", "text file with old and new key values"); + ret->addVolumeOutputParameter(3, "volume-out", "the output volume label file"); + + OptionalParameter* subvolOpt = ret->createOptionalParameter(4, "-subvolume", "select a single subvolume"); + subvolOpt->addStringParameter(1, "subvolume", "the subvolume number or name"); + + ret->setHelpText( + AString(" should have lines of the form 'oldkey newkey', like so:\n\n") + + "3 5\n5 8\n8 2\n\n" + + "This would change the current label with key '3' to use the key '5' instead, 5 would use 8, and 8 would use 2. " + + "Any collision in key values results in the label that was not specified in the remap file getting remapped to an otherwise unused key. " + + "Remapping more than one key to the same new key, or the same key to more than one new key, results in an error. " + + "This will not change the appearance of the file when displayed, as it will change the key values in the data at the same time." + ); + return ret; +} + +void AlgorithmVolumeLabelModifyKeys::useParameters(OperationParameters* myParams, ProgressObject* myProgObj) +{ + VolumeFile* volIn = myParams->getVolume(1); + AString remapName = myParams->getString(2); + VolumeFile* volOut = myParams->getOutputVolume(3); + OptionalParameter* subvolOpt = myParams->getOptionalParameter(4); + int subvol = -1; + if (subvolOpt->m_present) + {//set up to use a single column + subvol = volIn->getMapIndexFromNameOrNumber(subvolOpt->getString(1)); + if (subvol < 0) throw AlgorithmException("invalid column specified"); + } + FileInformation textFileInfo(remapName); + if (!textFileInfo.exists()) + { + throw AlgorithmException("label list file doesn't exist"); + } + fstream remapFile(remapName.toLocal8Bit().constData(), fstream::in); + if (!remapFile.good()) + { + throw AlgorithmException("error reading label list file"); + } + map remap; + int32_t oldkey, newkey; + while (remapFile >> oldkey >> newkey) + { + if (remap.find(oldkey) != remap.end()) throw AlgorithmException("remapping tried to duplicate label " + AString::number(oldkey)); + remap[oldkey] = newkey; + } + AlgorithmVolumeLabelModifyKeys(myProgObj, volIn, remap, volOut, subvol); +} + +AlgorithmVolumeLabelModifyKeys::AlgorithmVolumeLabelModifyKeys(ProgressObject* myProgObj, const VolumeFile* volIn, const map remap, VolumeFile* volOut, const int subvol) : AbstractAlgorithm(myProgObj) +{ + LevelProgress myProgress(myProgObj); + if (volIn->getType() != SubvolumeAttributes::LABEL) + { + throw AlgorithmException("input volume must be a label volume"); + } + vector indims = volIn->getDimensions(); + vector outdims = volIn->getOriginalDimensions(); + if (indims[4] != 1) + { + throw AlgorithmException("multiple components are not allowed in label volumes"); + } + int64_t startVol = 0, endVol = indims[3]; + if (subvol > -1) + { + startVol = subvol; + endVol = subvol + 1; + indims.resize(3); + } + volOut->reinitialize(outdims, volIn->getSform(), 1, SubvolumeAttributes::LABEL); + vector scratchFrame(outdims[0] * outdims[1] * outdims[2]); + for (int64_t s = startVol; s < endVol; ++s) + { + volOut->setMapName(s - startVol, volIn->getMapName(s)); + const GiftiLabelTable* oldTable = volIn->getMapLabelTable(s); + int32_t oldUnlabeled = oldTable->getUnassignedLabelKey();//because GiftiLabelTable is quirky, we need to check if the unlabeled value ends up as something other than 0 + GiftiLabelTable newTable;//careful, label 0 is created by the constructor + bool setZero = false;//because of this, we need to track if we overwrote it, so that we can pretend it isn't there + for (map::const_iterator iter = remap.begin(); iter != remap.end(); ++iter) + { + const GiftiLabel* oldLabel = oldTable->getLabel(iter->first); + if (oldLabel == NULL) throw AlgorithmException("label key " + AString::number(iter->first) + " does not exist in the input file"); + GiftiLabel newLabel(*oldLabel); + newLabel.setKey(iter->second); + if (iter->first == oldUnlabeled) + { + if (iter->second != 0)//if it isn't the default unlabeled value, then we have to do something + { + if (newTable.getLabel(iter->second) != NULL) throw AlgorithmException("remapping tried to set label " + AString::number(iter->second) + " more than once"); + newTable.deleteLabel(0);//delete the default, since we don't know what overwrites it, if anything + newTable.insertLabel(&newLabel);//insert forces it to use the key in the label, even if it causes a duplicate name (which the original might theoretically have) + } else {//otherwise, just error checking + if (setZero) throw AlgorithmException("remapping tried to set label " + AString::number(iter->second) + " more than once"); + setZero = true;//we do have to track that zero now contains something that can't be overwritten + } + } else { + if (iter->second == 0)//if it remaps to the default unlabeled key, we have to check it differently + { + if (setZero) throw AlgorithmException("remapping tried to set label " + AString::number(iter->second) + " more than once"); + setZero = true; + newTable.insertLabel(&newLabel);//insert forces it to use the key in the label, so it will overwrite the existing default 0 key + } else {//finally, the simple case + if (newTable.getLabel(iter->second) != NULL) throw AlgorithmException("remapping tried to set label " + AString::number(iter->second) + " more than once"); + newTable.insertLabel(&newLabel);//insert forces it to use the key in the label, even if it causes a duplicate name (which the original might theoretically have) + } + } + } + set keys = oldTable->getKeys(), collisions; + for (set::const_iterator iter = keys.begin(); iter != keys.end(); ++iter) + { + if (remap.find(*iter) == remap.end())//skip if it was remapped + { + if (*iter == 0)//again with the special default 0 key + { + if (setZero)//check for collision + { + collisions.insert(*iter); + } else { + setZero = true; + if (*iter != oldUnlabeled)//if its merely the unassigned label already, we can just keep the existing default + { + newTable.insertLabel(oldTable->getLabel(*iter)); + } + } + } else { + if (newTable.getLabel(*iter) == NULL) + { + newTable.insertLabel(oldTable->getLabel(*iter)); + } else {//collision + collisions.insert(*iter); + } + } + } + } + map valueChanges = remap;//start with the specified changes, then add the collision changes + for (set::const_iterator iter = collisions.begin(); iter != collisions.end(); ++iter) + {//now deal with collisions + int32_t newKey = newTable.generateUnusedKey(); + GiftiLabel newLabel(*(oldTable->getLabel(*iter))); + newLabel.setKey(newKey); + newTable.insertLabel(&newLabel);//insert forces it to use the key in the label, even if it causes a duplicate name (which the original might theoretically have) + valueChanges[*iter] = newKey; + } + *(volOut->getMapLabelTable(s - startVol)) = newTable; + const float* inframe = volIn->getFrame(s); + for (int64_t k = 0; k < indims[2]; ++k) + { + for (int64_t j = 0; j < indims[1]; ++j) + { + for (int64_t i = 0; i < indims[0]; ++i) + { + int64_t index = volIn->getIndex(i, j, k); + int32_t oldkey = int32_t(floor(inframe[index] + 0.5)); + auto iter = valueChanges.find(oldkey); + if (iter == valueChanges.end()) + { + scratchFrame[index] = oldkey; + } else { + scratchFrame[index] = iter->second; + } + } + } + } + volOut->setFrame(scratchFrame.data(), s - startVol); + } +} + +float AlgorithmVolumeLabelModifyKeys::getAlgorithmInternalWeight() +{ + return 1.0f;//override this if needed, if the progress bar isn't smooth +} + +float AlgorithmVolumeLabelModifyKeys::getSubAlgorithmWeight() +{ + //return AlgorithmInsertNameHere::getAlgorithmWeight();//if you use a subalgorithm + return 0.0f; +} diff --git a/src/Algorithms/AlgorithmVolumeLabelModifyKeys.h b/src/Algorithms/AlgorithmVolumeLabelModifyKeys.h new file mode 100644 index 000000000..6ef19d16e --- /dev/null +++ b/src/Algorithms/AlgorithmVolumeLabelModifyKeys.h @@ -0,0 +1,48 @@ +#ifndef __ALGORITHM_VOLUME_LABEL_MODIFY_KEYS_H__ +#define __ALGORITHM_VOLUME_LABEL_MODIFY_KEYS_H__ + +/*LICENSE_START*/ +/* + * Copyright (C) 2018 Washington University School of Medicine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/*LICENSE_END*/ + +#include "AbstractAlgorithm.h" + +#include + +namespace caret { + + class AlgorithmVolumeLabelModifyKeys : public AbstractAlgorithm + { + AlgorithmVolumeLabelModifyKeys(); + protected: + static float getSubAlgorithmWeight(); + static float getAlgorithmInternalWeight(); + public: + AlgorithmVolumeLabelModifyKeys(ProgressObject* myProgObj, const VolumeFile* volIn, const std::map remap, VolumeFile* volOut, const int subvol = -1); + static OperationParameters* getParameters(); + static void useParameters(OperationParameters* myParams, ProgressObject* myProgObj); + static AString getCommandSwitch(); + static AString getShortDescription(); + }; + + typedef TemplateAutoOperation AutoAlgorithmVolumeLabelModifyKeys; + +} + +#endif //__ALGORITHM_VOLUME_LABEL_MODIFY_KEYS_H__ diff --git a/src/Algorithms/CMakeLists.txt b/src/Algorithms/CMakeLists.txt index 0acf354f7..ea6411e49 100644 --- a/src/Algorithms/CMakeLists.txt +++ b/src/Algorithms/CMakeLists.txt @@ -43,6 +43,7 @@ AlgorithmCiftiFalseCorrelation.h AlgorithmCiftiFindClusters.h AlgorithmCiftiGradient.h AlgorithmCiftiLabelAdjacency.h +AlgorithmCiftiLabelModifyKeys.h AlgorithmCiftiLabelProbability.h AlgorithmCiftiLabelToBorder.h AlgorithmCiftiLabelToROI.h @@ -124,6 +125,7 @@ AlgorithmVolumeExtrema.h AlgorithmVolumeFillHoles.h AlgorithmVolumeFindClusters.h AlgorithmVolumeGradient.h +AlgorithmVolumeLabelModifyKeys.h AlgorithmVolumeLabelProbability.h AlgorithmVolumeLabelToROI.h AlgorithmVolumeLabelToSurfaceMapping.h @@ -162,6 +164,7 @@ AlgorithmCiftiFalseCorrelation.cxx AlgorithmCiftiFindClusters.cxx AlgorithmCiftiGradient.cxx AlgorithmCiftiLabelAdjacency.cxx +AlgorithmCiftiLabelModifyKeys.cxx AlgorithmCiftiLabelProbability.cxx AlgorithmCiftiLabelToBorder.cxx AlgorithmCiftiLabelToROI.cxx @@ -243,6 +246,7 @@ AlgorithmVolumeExtrema.cxx AlgorithmVolumeFillHoles.cxx AlgorithmVolumeFindClusters.cxx AlgorithmVolumeGradient.cxx +AlgorithmVolumeLabelModifyKeys.cxx AlgorithmVolumeLabelProbability.cxx AlgorithmVolumeLabelToROI.cxx AlgorithmVolumeLabelToSurfaceMapping.cxx diff --git a/src/Commands/CommandOperationManager.cxx b/src/Commands/CommandOperationManager.cxx index 6ccd240db..96a126792 100644 --- a/src/Commands/CommandOperationManager.cxx +++ b/src/Commands/CommandOperationManager.cxx @@ -44,6 +44,7 @@ #include "AlgorithmCiftiFindClusters.h" #include "AlgorithmCiftiGradient.h" #include "AlgorithmCiftiLabelAdjacency.h" +#include "AlgorithmCiftiLabelModifyKeys.h" #include "AlgorithmCiftiLabelProbability.h" #include "AlgorithmCiftiLabelToBorder.h" #include "AlgorithmCiftiLabelToROI.h" @@ -123,6 +124,7 @@ #include "AlgorithmVolumeFillHoles.h" #include "AlgorithmVolumeFindClusters.h" #include "AlgorithmVolumeGradient.h" +#include "AlgorithmVolumeLabelModifyKeys.h" #include "AlgorithmVolumeLabelProbability.h" #include "AlgorithmVolumeLabelToROI.h" #include "AlgorithmVolumeLabelToSurfaceMapping.h" @@ -307,6 +309,7 @@ CommandOperationManager::CommandOperationManager() this->commandOperations.push_back(new CommandParser(new AutoAlgorithmCiftiFindClusters())); this->commandOperations.push_back(new CommandParser(new AutoAlgorithmCiftiGradient())); this->commandOperations.push_back(new CommandParser(new AutoAlgorithmCiftiLabelAdjacency())); + this->commandOperations.push_back(new CommandParser(new AutoAlgorithmCiftiLabelModifyKeys())); this->commandOperations.push_back(new CommandParser(new AutoAlgorithmCiftiLabelProbability())); this->commandOperations.push_back(new CommandParser(new AutoAlgorithmCiftiLabelToBorder())); this->commandOperations.push_back(new CommandParser(new AutoAlgorithmCiftiLabelToROI())); @@ -387,6 +390,7 @@ CommandOperationManager::CommandOperationManager() this->commandOperations.push_back(new CommandParser(new AutoAlgorithmVolumeFillHoles())); this->commandOperations.push_back(new CommandParser(new AutoAlgorithmVolumeFindClusters())); this->commandOperations.push_back(new CommandParser(new AutoAlgorithmVolumeGradient())); + this->commandOperations.push_back(new CommandParser(new AutoAlgorithmVolumeLabelModifyKeys())); this->commandOperations.push_back(new CommandParser(new AutoAlgorithmVolumeLabelProbability())); this->commandOperations.push_back(new CommandParser(new AutoAlgorithmVolumeLabelToROI())); this->commandOperations.push_back(new CommandParser(new AutoAlgorithmVolumeLabelToSurfaceMapping())); -- GitLab From 68952e38a98aa3e4a8d89248d0f3421ad56e9c2c Mon Sep 17 00:00:00 2001 From: Tim Coalson Date: Tue, 24 Sep 2019 18:21:31 -0500 Subject: [PATCH 411/427] add option to -zip-scene-file to warn instead of error on missing file --- src/GuiQt/BalsaDatabaseManager.cxx | 6 ++-- src/Operations/OperationZipSceneFile.cxx | 37 +++++++++++++++++++----- src/Operations/OperationZipSceneFile.h | 5 ++-- 3 files changed, 36 insertions(+), 12 deletions(-) diff --git a/src/GuiQt/BalsaDatabaseManager.cxx b/src/GuiQt/BalsaDatabaseManager.cxx index 027739931..192fbff44 100644 --- a/src/GuiQt/BalsaDatabaseManager.cxx +++ b/src/GuiQt/BalsaDatabaseManager.cxx @@ -546,12 +546,12 @@ BalsaDatabaseManager::zipSceneAndDataFiles(const SceneFile* sceneFile, bool successFlag = false; try { - OperationZipSceneFile::createZipFile(sceneFileName, + OperationZipSceneFile::createZipFile(NULL, + sceneFileName, extractToDirectoryName, zipFileName, basePathName, - OperationZipSceneFile::PROGRESS_GUI_EVENT, - NULL); + OperationZipSceneFile::PROGRESS_GUI_EVENT); successFlag = true; } catch (const CaretException& e) { diff --git a/src/Operations/OperationZipSceneFile.cxx b/src/Operations/OperationZipSceneFile.cxx index 37f6a69b6..7051c165c 100644 --- a/src/Operations/OperationZipSceneFile.cxx +++ b/src/Operations/OperationZipSceneFile.cxx @@ -64,6 +64,8 @@ OperationParameters* OperationZipSceneFile::getParameters() OptionalParameter* baseOpt = ret->createOptionalParameter(4, "-base-dir", "specify a directory that all data files are somewhere within, this will become the root of the zipfile's directory structure"); baseOpt->addStringParameter(1, "directory", "the directory"); + + ret->createOptionalParameter(5, "-skip-missing", "any missing files will generate only warnings, and the zip file will be created anyway"); ret->setHelpText("If zip-file already exists, it will be overwritten. " "If -base-dir is not specified, the base directory will be automatically set to the lowest level directory containing all files. " @@ -77,26 +79,29 @@ void OperationZipSceneFile::useParameters(OperationParameters* myParams, Progres AString outputSubDirectory = myParams->getString(2); AString zipFileName = myParams->getString(3); OptionalParameter* baseOpt = myParams->getOptionalParameter(4); + bool skipMissing = myParams->getOptionalParameter(5)->m_present; AString myBaseDir; if (baseOpt->m_present) { myBaseDir = QDir::cleanPath(QDir(baseOpt->getString(1)).absolutePath()); } - OperationZipSceneFile::createZipFile(sceneFileName, + OperationZipSceneFile::createZipFile(myProgObj, + sceneFileName, outputSubDirectory, zipFileName, myBaseDir, PROGRESS_COMMAND_LINE, - myProgObj); + skipMissing); } -void OperationZipSceneFile::createZipFile(const AString& sceneFileName, +void OperationZipSceneFile::createZipFile(ProgressObject* myProgObj, + const AString& sceneFileName, const AString& outputSubDirectory, const AString& zipFileName, const AString& baseDirectory, const ProgressMode progressMode, - ProgressObject* myProgObj) + const bool skipMissing) { LevelProgress myProgress(myProgObj); FileInformation sceneFileInfo(sceneFileName); @@ -225,13 +230,20 @@ void OperationZipSceneFile::createZipFile(const AString& sceneFileName, } int32_t fileIndex = 1; static const char *myUnits[9] = {" B ", " KB", " MB", " GB", " TB", " PB", " EB", " ZB", " YB"}; + int goodFileCount = 0; for (set::iterator iter = allFiles.begin(); iter != allFiles.end(); ++iter, ++fileIndex) { AString dataFileName = *iter; AString unzippedDataFileName = outputSubDirectory + "/" + dataFileName.mid(myBaseDir.size());//we know the string matches to the length of myBaseDir, and is cleaned, so we can just chop the right number of characters off QFile dataFileIn(dataFileName); if (!dataFileIn.open(QFile::ReadOnly)) { - throw OperationException("Unable to open \"" + dataFileName + "\" for reading: " + dataFileIn.errorString()); + if (skipMissing) + { + CaretLogWarning("Skipping unreadable file '" + dataFileName + "'"); + continue; + } else { + throw OperationException("Unable to open \"" + dataFileName + "\" for reading: " + dataFileIn.errorString()); + } } float fileSize = (float)dataFileIn.size(); int unit = 0; @@ -293,15 +305,26 @@ void OperationZipSceneFile::createZipFile(const AString& sceneFileName, case PROGRESS_GUI_EVENT: break; } + ++goodFileCount; } zipFile.close(); switch (progressMode) { case PROGRESS_COMMAND_LINE: + if (goodFileCount != int(allFiles.size())) + { + CaretLogWarning("Zip creation skipped " + AString::number(allFiles.size() - goodFileCount) + " unreadable files"); + } break; case PROGRESS_GUI_EVENT: - progressEvent.setProgress(allFiles.size(), - "Zip created successfully"); + if (goodFileCount == int(allFiles.size())) + { + progressEvent.setProgress(allFiles.size(), + "Zip created successfully"); + } else { + progressEvent.setProgress(allFiles.size(), + "Zip creation skipped " + AString::number(allFiles.size() - goodFileCount) + " unreadable files"); + } EventManager::get()->sendEvent(progressEvent.getPointer()); break; } diff --git a/src/Operations/OperationZipSceneFile.h b/src/Operations/OperationZipSceneFile.h index 66f51cfa2..0630eb014 100644 --- a/src/Operations/OperationZipSceneFile.h +++ b/src/Operations/OperationZipSceneFile.h @@ -37,12 +37,13 @@ namespace caret { static void useParameters(OperationParameters* myParams, ProgressObject* myProgObj); static AString getCommandSwitch(); static AString getShortDescription(); - static void createZipFile(const AString& sceneFileName, + static void createZipFile(ProgressObject* myProgObj, + const AString& sceneFileName, const AString& outputSubDirectory, const AString& zipFileName, const AString& baseDirectory, const ProgressMode progressMode, - ProgressObject* myProgObj); + const bool skipMissing = false); }; typedef TemplateAutoOperation AutoOperationZipSceneFile; -- GitLab From 163aaa256e84df34db6cd42c37b6151820c40a3f Mon Sep 17 00:00:00 2001 From: Tim Coalson Date: Tue, 24 Sep 2019 18:26:51 -0500 Subject: [PATCH 412/427] add missing cmath include --- src/Algorithms/AlgorithmCiftiLabelModifyKeys.cxx | 1 + src/Algorithms/AlgorithmVolumeLabelModifyKeys.cxx | 1 + 2 files changed, 2 insertions(+) diff --git a/src/Algorithms/AlgorithmCiftiLabelModifyKeys.cxx b/src/Algorithms/AlgorithmCiftiLabelModifyKeys.cxx index 319e9c7c6..1729cb7b8 100644 --- a/src/Algorithms/AlgorithmCiftiLabelModifyKeys.cxx +++ b/src/Algorithms/AlgorithmCiftiLabelModifyKeys.cxx @@ -25,6 +25,7 @@ #include "FileInformation.h" #include "GiftiLabel.h" +#include #include #include diff --git a/src/Algorithms/AlgorithmVolumeLabelModifyKeys.cxx b/src/Algorithms/AlgorithmVolumeLabelModifyKeys.cxx index a6366a038..dfa7cf8f2 100644 --- a/src/Algorithms/AlgorithmVolumeLabelModifyKeys.cxx +++ b/src/Algorithms/AlgorithmVolumeLabelModifyKeys.cxx @@ -26,6 +26,7 @@ #include "GiftiLabelTable.h" #include "VolumeFile.h" +#include #include #include -- GitLab From 075530ded9818b7aeb9d888c64daafe93493a0a3 Mon Sep 17 00:00:00 2001 From: John Harwell Date: Wed, 25 Sep 2019 12:30:25 -0500 Subject: [PATCH 413/427] WB-869 Scroll wheel reversed from previous release: Scroll wheel processing has been revised and seems to function correctly. Pushing scroll wheel forward zooms in (volume slice gets bigger) on both Mac and Linux. Still need to test on Windows. Also, no trackpad is available for Linux and Windows testing (such as a trackpad on a laptop). --- src/GuiQt/BrainOpenGLWidget.cxx | 132 ++++++++++++++++++++++---------- 1 file changed, 91 insertions(+), 41 deletions(-) diff --git a/src/GuiQt/BrainOpenGLWidget.cxx b/src/GuiQt/BrainOpenGLWidget.cxx index ef2d437c7..630bf9c8e 100644 --- a/src/GuiQt/BrainOpenGLWidget.cxx +++ b/src/GuiQt/BrainOpenGLWidget.cxx @@ -780,50 +780,100 @@ BrainOpenGLWidget::contextMenuEvent(QContextMenuEvent* contextMenuEvent) void BrainOpenGLWidget::wheelEvent(QWheelEvent* we) { - const int wheelX = we->x(); - const int wheelY = this->windowHeight[this->windowIndex] - we->y(); - const QPoint pixelDelta = we->pixelDelta(); - const QPoint degrees = we->angleDelta(); - int delta(0); - if ( ! pixelDelta.isNull()) { - delta = pixelDelta.y(); + /* + * Notes 24 Sep 2019 + * + * QWheelEvent::pixelDelta() is not used since it is only set on Mac. + * + * Trackpad usage is when QWheelEvent::source()==Qt::MouseEventSynthesizedBySystem + * Mouse usage is when QWheelEvent::source()==Qt::MouseEventNotSynthesized + * + * Inverted flag is only used by Mouse on MacOS. It is true when + * Preferences->Trackpad->Scroll & Zoom is ON. The Wheel's inverted + * flag is ignored since Mac alters the sign of the mouse Y-value. + * It appears that "Scroll Direction:Natural is defaulted ON" on Macs + * + * Forward Wheel or Trackpad (moving away from user) + * ** Negative when MacOS->Preferences->Trackpad->Scroll&Zoom->Scroll Direction:Natural is ON + * ** Positive when MacOS->Preferences->Trackpad->Scroll&Zoom->Scroll Direction:Natural is OFF + * ** Negative when MacOS->Preferences->Mouse->Scroll Direction:Natural is ON + * ** Positive when MacOS->Preferences->Mouse->Scroll Direction:Natural is OFF + * + * ** Positive on Linux + */ + const QPoint angleDelta = we->angleDelta(); + if (angleDelta.isNull()) { + return; } - else if ( ! degrees.isNull()) { - delta = degrees.y(); + + int32_t deltaDegrees = angleDelta.y(); + if (deltaDegrees == 0) { + return; } + + /* + * While the mouse/trackpad flags are not used at this time, + * we also do not have access to a Linux or Windows system + * with a trackpad. + */ + bool mouseFlag(false); + bool trackpadFlag(false); + switch (we->source()) { + case Qt::MouseEventNotSynthesized: + mouseFlag = true; + break; + case Qt::MouseEventSynthesizedByApplication: + break; + case Qt::MouseEventSynthesizedByQt: + break; + case Qt::MouseEventSynthesizedBySystem: + trackpadFlag = true; + break; + } + +#ifdef CARET_OS_MACOSX + /* On Mac, "Scroll Direction:Natural" is default ON and causes a sign flip */ + deltaDegrees = -deltaDegrees; +#endif // CARET_OS_MACOSX + - if (delta != 0) { - if ( ! we->inverted()) { - delta = -delta; - } - - /* - * If not limited, it is way too fast - */ - const int limitValue(8); - delta = MathFunctions::limitRange(delta, -limitValue, limitValue); - - /* - * Use location of mouse press so that the model - * being manipulated does not change if mouse moves - * out of its viewport without releasing the mouse - * button. - */ - const BrainOpenGLViewportContent* viewportContent = this->getViewportContentAtXY(wheelX, - wheelY); - if (viewportContent != NULL) { - MouseEvent mouseEvent(viewportContent, - this, - this->windowIndex, - wheelX, - wheelY, - 0, - delta, - 0, - 0, - this->mouseNewDraggingStartedFlag); - this->selectedUserInputProcessor->mouseLeftDragWithCtrl(mouseEvent); - } + const bool debugFlag(false); + if (debugFlag) { + std::cout << "Angle Delta: " << we->angleDelta().y() << std::endl; + std::cout << "Inverted: " << AString::fromBool(we->inverted()) << std::endl; + std::cout << "Source: " << (int32_t)we->source() << std::endl; + std::cout << "Mouse Flag: " << AString::fromBool(mouseFlag) << std::endl; + std::cout << "Trackpad Flag: " << AString::fromBool(trackpadFlag) << std::endl; + } + + /* + * If not limited, it is way too fast + */ + const int limitValue(8); + deltaDegrees = MathFunctions::limitRange(deltaDegrees, -limitValue, limitValue); + + /* + * Use location of mouse press so that the model + * being manipulated does not change if mouse moves + * out of its viewport without releasing the mouse + * button. + */ + const int wheelX = we->x(); + const int wheelY = this->windowHeight[this->windowIndex] - we->y(); + const BrainOpenGLViewportContent* viewportContent = this->getViewportContentAtXY(wheelX, + wheelY); + if (viewportContent != NULL) { + MouseEvent mouseEvent(viewportContent, + this, + this->windowIndex, + wheelX, + wheelY, + 0, + deltaDegrees, + 0, + 0, + this->mouseNewDraggingStartedFlag); + this->selectedUserInputProcessor->mouseLeftDragWithCtrl(mouseEvent); } we->accept(); -- GitLab From 8dd23715cbfc8f60ec8daf41fb4400fd4b0763d2 Mon Sep 17 00:00:00 2001 From: John Harwell Date: Wed, 25 Sep 2019 12:43:48 -0500 Subject: [PATCH 414/427] Add additional logging (log response content and code at FINE level) when requesting study information from BALSA. --- src/GuiQt/BalsaDatabaseManager.cxx | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/src/GuiQt/BalsaDatabaseManager.cxx b/src/GuiQt/BalsaDatabaseManager.cxx index 027739931..7c9bc5203 100644 --- a/src/GuiQt/BalsaDatabaseManager.cxx +++ b/src/GuiQt/BalsaDatabaseManager.cxx @@ -1316,6 +1316,15 @@ BalsaDatabaseManager::getAllStudyInformation(std::vector& CaretHttpResponse idResponse; CaretHttpManager::httpRequest(caretRequest, idResponse); + idResponse.m_body.push_back('\0'); + AString responseContent(&idResponse.m_body[0]); + CaretLogFine("Get All Study Information Response from " + + studyIdURL + + ", Response Code=" + + AString::number(idResponse.m_responseCode) + + "\nContent:\n" + + responseContent); + if (m_debugFlag) { std::cout << "Request all studies response Code: " << idResponse.m_responseCode << std::endl; } @@ -1340,8 +1349,6 @@ BalsaDatabaseManager::getAllStudyInformation(std::vector& return false; } - idResponse.m_body.push_back('\0'); - AString responseContent(&idResponse.m_body[0]); if (m_debugFlag) { std::cout << "Request all studies reply body:\n" << responseContent << std::endl << std::endl; -- GitLab From 3f518484ffc16beb882b4981bfb8026ac51baf46 Mon Sep 17 00:00:00 2001 From: John Harwell Date: Wed, 25 Sep 2019 15:49:33 -0500 Subject: [PATCH 415/427] If when uploading to BALSA, a JSON error occurs, include the 'offset' in the error dialog from the JSON error information. --- src/GuiQt/BalsaDatabaseManager.cxx | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/src/GuiQt/BalsaDatabaseManager.cxx b/src/GuiQt/BalsaDatabaseManager.cxx index 579e130b0..01137b3c3 100644 --- a/src/GuiQt/BalsaDatabaseManager.cxx +++ b/src/GuiQt/BalsaDatabaseManager.cxx @@ -419,6 +419,8 @@ BalsaDatabaseManager::verifyUploadFileResponse(const std::map& if (jsonDocument.isNull()) { errorMessageOut = ("Process upload response failed. Failed to parse JSON, error:" + jsonError.errorString() + + " Offset=" + + AString::number(jsonError.offset) + "\n\n" "Response Content: " + responseContent); @@ -1034,7 +1036,9 @@ BalsaDatabaseManager::requestStudyID(const AString& databaseURL, &jsonError); if (jsonDocument.isNull()) { errorMessageOut = ("Requesting study ID failed. Failed to parse JSON, error:" - + jsonError.errorString()); + + jsonError.errorString() + + " Offset=" + + AString::number(jsonError.offset)); return false; } @@ -1168,7 +1172,9 @@ BalsaDatabaseManager::getUserRoles(BalsaUserRoles& userRolesOut, &jsonError); if (jsonDocument.isNull()) { errorMessageOut = ("Requesting roles failed. Failed to parse JSON, error:" - + jsonError.errorString()); + + jsonError.errorString() + + " Offset=" + + AString::number(jsonError.offset)); return false; } @@ -1359,7 +1365,9 @@ BalsaDatabaseManager::getAllStudyInformation(std::vector& &jsonError); if (jsonDocument.isNull()) { errorMessageOut = ("Requesting all study information failed. Failed to parse JSON, error:" - + jsonError.errorString()); + + jsonError.errorString() + + " Offset=" + + AString::number(jsonError.offset)); return false; } QByteArray json = jsonDocument.toJson(); -- GitLab From d626e3369b1a6e895bd5cada633f65f284a19e35 Mon Sep 17 00:00:00 2001 From: John Harwell Date: Wed, 25 Sep 2019 16:21:51 -0500 Subject: [PATCH 416/427] Trying to fix JSON Illegal UTF-8 sequence: When passing data to JSON parser a byte array is expected. The QString was using toLatin1() to convert to byte array and has been changed to toUtf8(). --- src/GuiQt/BalsaDatabaseManager.cxx | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/GuiQt/BalsaDatabaseManager.cxx b/src/GuiQt/BalsaDatabaseManager.cxx index 01137b3c3..edd439388 100644 --- a/src/GuiQt/BalsaDatabaseManager.cxx +++ b/src/GuiQt/BalsaDatabaseManager.cxx @@ -414,7 +414,7 @@ BalsaDatabaseManager::verifyUploadFileResponse(const std::map& } QJsonParseError jsonError; - QJsonDocument jsonDocument = QJsonDocument::fromJson(responseContent.toLatin1(), + QJsonDocument jsonDocument = QJsonDocument::fromJson(responseContent.toUtf8(), &jsonError); if (jsonDocument.isNull()) { errorMessageOut = ("Process upload response failed. Failed to parse JSON, error:" @@ -720,7 +720,7 @@ BalsaDatabaseManager::updateSceneIdsFromProcessUploadResponse(SceneFile* sceneFi AString& errorMessageOut) { QJsonParseError jsonError; - QJsonDocument jsonDocument = QJsonDocument::fromJson(jsonContent.toLatin1(), + QJsonDocument jsonDocument = QJsonDocument::fromJson(jsonContent.toUtf8(), &jsonError); if (jsonDocument.isNull()) { errorMessageOut = ("Failed to parse JSON response from procssess upload, error:" @@ -1032,7 +1032,7 @@ BalsaDatabaseManager::requestStudyID(const AString& databaseURL, } QJsonParseError jsonError; - QJsonDocument jsonDocument = QJsonDocument::fromJson(responseContent.toLatin1(), + QJsonDocument jsonDocument = QJsonDocument::fromJson(responseContent.toUtf8(), &jsonError); if (jsonDocument.isNull()) { errorMessageOut = ("Requesting study ID failed. Failed to parse JSON, error:" @@ -1168,7 +1168,7 @@ BalsaDatabaseManager::getUserRoles(BalsaUserRoles& userRolesOut, } QJsonParseError jsonError; - QJsonDocument jsonDocument = QJsonDocument::fromJson(responseContent.toLatin1(), + QJsonDocument jsonDocument = QJsonDocument::fromJson(responseContent.toUtf8(), &jsonError); if (jsonDocument.isNull()) { errorMessageOut = ("Requesting roles failed. Failed to parse JSON, error:" @@ -1361,7 +1361,7 @@ BalsaDatabaseManager::getAllStudyInformation(std::vector& } QJsonParseError jsonError; - QJsonDocument jsonDocument = QJsonDocument::fromJson(responseContent.toLatin1(), + QJsonDocument jsonDocument = QJsonDocument::fromJson(responseContent.toUtf8(), &jsonError); if (jsonDocument.isNull()) { errorMessageOut = ("Requesting all study information failed. Failed to parse JSON, error:" -- GitLab From 78dc2cc2f050be7fe389d8e6f2325113903bc194 Mon Sep 17 00:00:00 2001 From: John Harwell Date: Thu, 26 Sep 2019 09:11:55 -0500 Subject: [PATCH 417/427] WB-869 Scroll wheel reversed from previous release: Removed sign flip of Wheel Event on Mac so that zoom functionality is consistent with zooming found in Google Maps. Thus, on Mac with "Scroll Direction: Natural" OFF, pushing mouse wheel or finger on trackpad forward zooms in. --- src/GuiQt/BrainOpenGLWidget.cxx | 6 ------ 1 file changed, 6 deletions(-) diff --git a/src/GuiQt/BrainOpenGLWidget.cxx b/src/GuiQt/BrainOpenGLWidget.cxx index 630bf9c8e..71c19edce 100644 --- a/src/GuiQt/BrainOpenGLWidget.cxx +++ b/src/GuiQt/BrainOpenGLWidget.cxx @@ -831,12 +831,6 @@ BrainOpenGLWidget::wheelEvent(QWheelEvent* we) break; } -#ifdef CARET_OS_MACOSX - /* On Mac, "Scroll Direction:Natural" is default ON and causes a sign flip */ - deltaDegrees = -deltaDegrees; -#endif // CARET_OS_MACOSX - - const bool debugFlag(false); if (debugFlag) { std::cout << "Angle Delta: " << we->angleDelta().y() << std::endl; -- GitLab From 25419398eb8177c5784d9494f1c95fb2799ca554 Mon Sep 17 00:00:00 2001 From: John Harwell Date: Thu, 26 Sep 2019 11:35:20 -0500 Subject: [PATCH 418/427] On the Scene Dialog in the list of scenes, "Current Scene" (in color red) is displayed above the name of the most recently created or loaded scene. If a scene had a loading error, the "Current Scene" was missing and has been corrected. --- src/GuiQt/SceneDialog.cxx | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/GuiQt/SceneDialog.cxx b/src/GuiQt/SceneDialog.cxx index 1d9aab1d8..cb2a89309 100644 --- a/src/GuiQt/SceneDialog.cxx +++ b/src/GuiQt/SceneDialog.cxx @@ -2769,6 +2769,13 @@ SceneDialog::displayScenePrivateWithErrorMessage(SceneFile* sceneFile, cursor.restoreCursor(); + /* + * Set the active scene + */ + EventSceneActive activeSceneEvent(EventSceneActive::MODE_SET); + activeSceneEvent.setScene(scene); + EventManager::get()->sendEvent(activeSceneEvent.getPointer()); + const AString sceneErrorMessage = sceneAttributes->getErrorMessage(); if (sceneErrorMessage.isEmpty()) { /* @@ -2784,13 +2791,6 @@ SceneDialog::displayScenePrivateWithErrorMessage(SceneFile* sceneFile, CaretPreferences* prefs = SessionManager::get()->getCaretPreferences(); prefs->addToPreviousSceneFiles(sceneFile->getFileName()); } - - /* - * Set the active scene - */ - EventSceneActive activeSceneEvent(EventSceneActive::MODE_SET); - activeSceneEvent.setScene(scene); - EventManager::get()->sendEvent(activeSceneEvent.getPointer()); } else { errorMessageOut = sceneErrorMessage; -- GitLab From dd9e19d9e71512579bacceae1cb9538d2a248a52 Mon Sep 17 00:00:00 2001 From: Tim Coalson Date: Tue, 1 Oct 2019 17:54:08 -0500 Subject: [PATCH 419/427] make -create-signed-distance-volume only read the header from the reference file --- src/Algorithms/AlgorithmCreateSignedDistanceVolume.cxx | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/Algorithms/AlgorithmCreateSignedDistanceVolume.cxx b/src/Algorithms/AlgorithmCreateSignedDistanceVolume.cxx index bb2af5679..65b6e62b9 100644 --- a/src/Algorithms/AlgorithmCreateSignedDistanceVolume.cxx +++ b/src/Algorithms/AlgorithmCreateSignedDistanceVolume.cxx @@ -24,6 +24,7 @@ #include "CaretOMP.h" #include "CaretHeap.h" #include "MathFunctions.h" +#include "NiftiIO.h" #include "SurfaceFile.h" #include @@ -88,10 +89,11 @@ void AlgorithmCreateSignedDistanceVolume::useParameters(OperationParameters* myP vector > volSpace; vector volDims; { - VolumeFile myRefSpace; - myRefSpace.readFile(myRefName); - volSpace = myRefSpace.getSform(); - myRefSpace.getDimensions(volDims); + NiftiIO refSpaceIO; + refSpaceIO.openRead(myRefName); + volDims = refSpaceIO.getDimensions(); + if (volDims.size() < 3) volDims.resize(3, 1); + volSpace = refSpaceIO.getHeader().getSForm(); } volDims.resize(3); VolumeFile* myVolOut = myParams->getOutputVolume(3); -- GitLab From 563f958756d71801e340242027e277a11aa9b8d2 Mon Sep 17 00:00:00 2001 From: Tim Coalson Date: Fri, 18 Oct 2019 14:19:38 -0500 Subject: [PATCH 420/427] memory limit for double correlation is in the right ballpark, though it isn't exact --- .../AlgorithmCiftiCorrelationGradient.cxx | 114 ++++++++++++++---- .../AlgorithmCiftiCorrelationGradient.h | 8 +- 2 files changed, 96 insertions(+), 26 deletions(-) diff --git a/src/Algorithms/AlgorithmCiftiCorrelationGradient.cxx b/src/Algorithms/AlgorithmCiftiCorrelationGradient.cxx index 4af9eea50..261fc72cd 100644 --- a/src/Algorithms/AlgorithmCiftiCorrelationGradient.cxx +++ b/src/Algorithms/AlgorithmCiftiCorrelationGradient.cxx @@ -200,7 +200,7 @@ void AlgorithmCiftiCorrelationGradient::useParameters(OperationParameters* myPar doubleCorr, firstFisher, firstNoDemean, firstCovar); } -AlgorithmCiftiCorrelationGradient::AlgorithmCiftiCorrelationGradient(ProgressObject* myProgObj, const CiftiFile* myCifti, CiftiFile* myCiftiOut, +AlgorithmCiftiCorrelationGradient::AlgorithmCiftiCorrelationGradient(ProgressObject* myProgObj, CiftiFile* myCifti, CiftiFile* myCiftiOut, SurfaceFile* myLeftSurf, SurfaceFile* myRightSurf, SurfaceFile* myCerebSurf, const MetricFile* myLeftAreas, const MetricFile* myRightAreas, const MetricFile* myCerebAreas, const float& surfKern, const float& volKern, const bool& undoFisherInput, const bool& applyFisher, @@ -390,11 +390,60 @@ namespace int64_t m_chunkSize; }; - FirstCorrPlan firstCorrMemoryPlan(int64_t totalRows) + bool firstCorrWarned = false; + + FirstCorrPlan firstCorrMemoryPlan(const int64_t totalRows, const int64_t cacheRowLength, const float memLimitGB, const bool inputIsMemory, const int64_t inputRowLength, const int64_t /*mapSize*/) { - FirstCorrPlan ret;//FIXME: actually do something with the memory limit + FirstCorrPlan ret; ret.m_cacheFullInput = true; ret.m_chunkSize = totalRows; + if (memLimitGB < 0.0f) + { + return ret; + } + int64_t inputRowBytes = sizeof(float) * inputRowLength; + int64_t mem_limit_bytes = int64_t(memLimitGB * 1024 * 1024 * 1024); + int64_t full_input_cache_bytes = inputRowBytes * cacheRowLength;//cache is a part of a square dconn, sized by the column length of the input + int64_t cache_bytes = sizeof(float) * totalRows * cacheRowLength; + int64_t input_memory_bytes = 0; + if (inputIsMemory) + { + input_memory_bytes = full_input_cache_bytes; + } + int64_t in_use_bytes = cache_bytes + input_memory_bytes;//the metric/volume file used to store the second correlation before gradient isn't allocated until after cacheRows() + int64_t available_bytes = mem_limit_bytes - in_use_bytes; + if (available_bytes <= 0) + { + //the user may have specified 0, just use minimum memory... + if (!inputIsMemory && !firstCorrWarned) + { + CaretLogWarning("double correlation specified with extremely low memory limit, this may take a long time and do a lot of IO"); + firstCorrWarned = true; + } + ret.m_chunkSize = 1; + ret.m_cacheFullInput = false; + return ret; + } + if (full_input_cache_bytes > available_bytes) + { + ret.m_cacheFullInput = false; + int64_t maxChunkSize = available_bytes / inputRowBytes; + if (maxChunkSize < 1) maxChunkSize = 1; + int64_t numPasses = (totalRows - 1) / maxChunkSize + 1;//compute number of passes to find the minimum chunk size to do that number of passes + ret.m_chunkSize = (totalRows - 1) / numPasses + 1;//technically, unequal chunks is slightly less computation and same total IO, but equal chunks makes IO more consistent + if (ret.m_chunkSize < 1) + { + if (!inputIsMemory && !firstCorrWarned) + { + CaretLogWarning("double correlation specified with extremely low memory limit, this may take a long time and do a lot of IO"); + firstCorrWarned = true; + } + ret.m_chunkSize = 1; + return ret; + } + } + //default is to cache full input + //unlike ordinary correlation, the memory for storing the in-progress output has already been dictated to us, so there is no advantage to chunking any smaller than the input cache return ret; } } @@ -442,7 +491,7 @@ void AlgorithmCiftiCorrelationGradient::processSurfaceComponent(StructureEnum::E } if (cacheFullInput) { - cacheRows(rowsToCache); + cacheRows(rowsToCache, mapSize); } CaretPointer mySmooth; if (surfKern > 0.0f) @@ -460,7 +509,7 @@ void AlgorithmCiftiCorrelationGradient::processSurfaceComponent(StructureEnum::E { rowsToCache.push_back(myMap[i].m_ciftiIndex); } - cacheRows(rowsToCache); + cacheRows(rowsToCache, mapSize); } int curRow = 0;//because we can't trust the order threads hit the critical section MetricFile computeMetric; @@ -586,7 +635,7 @@ void AlgorithmCiftiCorrelationGradient::processSurfaceComponent(StructureEnum::E } if (cacheFullInput) { - cacheRows(rowsToCache); + cacheRows(rowsToCache, mapSize); } CaretPointer mySmooth; if (surfKern > 0.0f) @@ -604,7 +653,7 @@ void AlgorithmCiftiCorrelationGradient::processSurfaceComponent(StructureEnum::E { rowsToCache.push_back(myMap[i].m_ciftiIndex); } - cacheRows(rowsToCache); + cacheRows(rowsToCache, mapSize); } int numSurfNodes = mySurf->getNumberOfNodes(); #pragma omp CARET_PAR @@ -793,7 +842,7 @@ void AlgorithmCiftiCorrelationGradient::processVolumeComponent(StructureEnum::En } if (cacheFullInput) { - cacheRows(rowsToCache); + cacheRows(rowsToCache, mapSize); } for (int startpos = 0; startpos < mapSize; startpos += numCacheRows) { @@ -806,7 +855,7 @@ void AlgorithmCiftiCorrelationGradient::processVolumeComponent(StructureEnum::En { rowsToCache.push_back(myMap[i].m_ciftiIndex); } - cacheRows(rowsToCache); + cacheRows(rowsToCache, mapSize); } int curRow = 0;//because we can't trust the order threads hit the critical section vector computeDims = newdims; @@ -942,7 +991,7 @@ void AlgorithmCiftiCorrelationGradient::processVolumeComponent(StructureEnum::En } if (cacheFullInput) { - cacheRows(rowsToCache); + cacheRows(rowsToCache, mapSize); } for (int startpos = 0; startpos < mapSize; startpos += numCacheRows) { @@ -955,7 +1004,7 @@ void AlgorithmCiftiCorrelationGradient::processVolumeComponent(StructureEnum::En { rowsToCache.push_back(myMap[i].m_ciftiIndex); } - cacheRows(rowsToCache); + cacheRows(rowsToCache, mapSize); } int curRow = 0;//because we can't trust the order threads hit the critical section vector computeDims = newdims; @@ -1052,7 +1101,7 @@ void AlgorithmCiftiCorrelationGradient::processVolumeComponent(StructureEnum::En } } -void AlgorithmCiftiCorrelationGradient::init(const CiftiFile* input, const float& memLimitGB, const bool& undoFisherInput, const bool& applyFisher, +void AlgorithmCiftiCorrelationGradient::init(CiftiFile* input, const float& memLimitGB, const bool& undoFisherInput, const bool& applyFisher, const bool& covariance, const bool doubleCorr, const bool firstFisher, const bool firstNoDemean, const bool firstCovar) { if (input->getCiftiXML().getMappingType(CiftiXML::ALONG_COLUMN) != CiftiMappingType::BRAIN_MODELS) throw AlgorithmException("input cifti file must have brain models mapping along column"); @@ -1075,7 +1124,6 @@ void AlgorithmCiftiCorrelationGradient::init(const CiftiFile* input, const float m_firstNoDemean = firstNoDemean; m_firstCovar = firstCovar; m_firstCorrInfo.resize(colLength); - //TODO: deal with doubleCorr in getRow/#cacheRows } else { m_numCols = m_inputCifti->getNumberOfColumns(); m_rowLengthFirst = 0;//trick to get the memory computations to work out @@ -1085,7 +1133,7 @@ void AlgorithmCiftiCorrelationGradient::init(const CiftiFile* input, const float m_outColumn.resize(colLength); } -void AlgorithmCiftiCorrelationGradient::cacheRows(const vector& ciftiIndices) +void AlgorithmCiftiCorrelationGradient::cacheRows(const vector& ciftiIndices, const int64_t mapSize) { clearCache();//clear first, to be sure we never keep a cache around too long int64_t numIndices = (int64_t)ciftiIndices.size(); @@ -1098,8 +1146,8 @@ void AlgorithmCiftiCorrelationGradient::cacheRows(const vector& ciftiIn m_rowInfo[ciftiIndices[i]].m_cacheIndex = i; } vector > preparedInput; - FirstCorrPlan plan = firstCorrMemoryPlan(numIndices); - if (plan.m_cacheFullInput) + FirstCorrPlan plan = firstCorrMemoryPlan(numIndices, m_numCols, m_memLimitGB, m_inputCifti->isInMemory(), m_rowLengthFirst, mapSize); + if (plan.m_cacheFullInput)//we could cache full input while still chunking computation, but there is no reason to for first corr... { preparedInput.resize(m_numCols, vector(m_rowLengthFirst)); for (int64_t i = 0; i < m_numCols; ++i) @@ -1219,7 +1267,7 @@ const float* AlgorithmCiftiCorrelationGradient::getRow(const int& ciftiIndex, fl } else { ret = scratchStorage; if (m_doubleCorr) - {//will only happen with a memory limit that prevents caching entire row range - need to take a look at this when memory limit is extended to this case + {//will only happen with a memory limit that prevents caching entire row range - may need a closer look vector fixedRow(m_rowLengthFirst), movingRow(m_rowLengthFirst); m_inputCifti->getRow(fixedRow.data(), ciftiIndex); adjustRow(fixedRow.data(), m_rowLengthFirst, m_firstCorrInfo[ciftiIndex], false, m_firstCovar, m_firstNoDemean); @@ -1245,19 +1293,24 @@ const float* AlgorithmCiftiCorrelationGradient::getRow(const int& ciftiIndex, fl } int AlgorithmCiftiCorrelationGradient::numRowsForMem(const int64_t& inrowBytes, const int64_t& outrowBytes, const int& numRows, bool& cacheFullInputOut) -{ +{//double corr might benefit from some reworking if (m_memLimitGB < 0.0f) { cacheFullInputOut = true; return numRows; } int64_t targetBytes = (int64_t)(m_memLimitGB * 1024 * 1024 * 1024); - bool inputIsMemory = m_inputCifti->isInMemory();//FIXME: double corr - if (inputIsMemory) targetBytes -= numRows * inrowBytes;//count in-memory input against the total too - TODO: if in memory, don't cache input rows at all? + int64_t inputFileSize = sizeof(float) * m_inputCifti->getNumberOfColumns() * m_inputCifti->getNumberOfRows(); + bool inputIsMemory = m_inputCifti->isInMemory();//TODO: if in memory, we don't really need to cache input rows as much... + if (inputIsMemory) + { + targetBytes -= inputFileSize;//count in-memory input against the total too + } targetBytes -= numRows * sizeof(RowInfo) + 2 * outrowBytes;//storage for mean, stdev, and info about caching, output structures if (targetBytes < 1) { - cacheFullInputOut = false;//the most memory conservation possible, though it will take a LOT of time and do a LOT of IO + if (!inputIsMemory) CaretLogWarning("extremely low memory limit used, this may take a long time and do a lot of IO"); + cacheFullInputOut = false;//the most memory conservation possible return 1; } if (inrowBytes * numRows < targetBytes)//if we can cache the full input, compute the number of passes needed and compare @@ -1282,7 +1335,7 @@ int AlgorithmCiftiCorrelationGradient::numRowsForMem(const int64_t& inrowBytes, fullPasses = numPassesPartial - 1; int64_t partialCorrSkip = (fullPasses * numRowsPartial * (numRowsPartial - 1) + (numRows - fullPasses * numRowsPartial) * (numRows - fullPasses * numRowsPartial - 1)) / 2; int ret; - if (partialCorrSkip > fullCorrSkip * 1.05f)//prefer full caching slightly - include a bias factor in options? + if (!m_doubleCorr && partialCorrSkip > fullCorrSkip * 1.05f)//prefer full caching slightly - always cache full if possible when doing double corr, recomputation is much more expensive than rereading {//assume IO and row adjustment (unfisher, subtract mean) won't be the limiting factor, since IO should be balanced during correlation due to evenly sized passes when not fully cached cacheFullInputOut = false; ret = numRowsPartial; @@ -1294,6 +1347,23 @@ int AlgorithmCiftiCorrelationGradient::numRowsForMem(const int64_t& inrowBytes, if (ret > numRows) ret = numRows; return ret; } else {//if we can't cache the whole thing, split passes evenly + if (m_doubleCorr) + { + if (!inputIsMemory) + {//getting a correlation row outside of the cached range will cause a huge IO increase, so convert input to memory if at all possible + if (inputFileSize < targetBytes - (inrowBytes + outrowBytes) * sqrt(numRows))//TODO: consider edge cases + { + m_inputCifti->convertToInMemory(); + inputIsMemory = true; + } else { + if (!firstCorrWarned) + { + CaretLogWarning("double correlation specified with low memory limit, this may take an extremely long time and do a lot of IO"); + firstCorrWarned = true; + } + } + } + } cacheFullInputOut = false; int64_t div = max((int64_t)1, (outrowBytes + inrowBytes) * numRows); #ifdef CARET_OMP diff --git a/src/Algorithms/AlgorithmCiftiCorrelationGradient.h b/src/Algorithms/AlgorithmCiftiCorrelationGradient.h index d60e710de..be7a80ef7 100644 --- a/src/Algorithms/AlgorithmCiftiCorrelationGradient.h +++ b/src/Algorithms/AlgorithmCiftiCorrelationGradient.h @@ -57,14 +57,14 @@ namespace caret { std::vector m_outColumn; int64_t m_numCols; bool m_undoFisherInput, m_applyFisher, m_covariance; - const CiftiFile* m_inputCifti;//so that accesses work through the cache functions + CiftiFile* m_inputCifti; int64_t m_rowLengthFirst;//for -double-correlation bool m_doubleCorr, m_firstCovar, m_firstNoDemean, m_firstFisher; float m_memLimitGB; - void cacheRows(const std::vector& ciftiIndices);//grabs the rows and does whatever it needs to, using as much IO bandwidth and CPU resources as available/needed + void cacheRows(const std::vector& ciftiIndices, const int64_t mapSize);//grabs the rows and does whatever it needs to, using as much IO bandwidth and CPU resources as available/needed void clearCache(); const float* getRow(const int& ciftiIndex, float& rootResidSqr, float* scratchStorage); - void init(const CiftiFile* input, const float& memLimitGB, const bool& undoFisherInput, const bool& applyFisher, const bool& covariance, + void init(CiftiFile* input, const float& memLimitGB, const bool& undoFisherInput, const bool& applyFisher, const bool& covariance, const bool doubleCorr, const bool firstFisher, const bool firstNoDemean, const bool firstCovar); int numRowsForMem(const int64_t& inrowBytes, const int64_t& outrowBytes, const int& numRows, bool& cacheFullInput); //void processSurfaceComponentLocal(StructureEnum::Enum& myStructure, const float& surfKern, const float& memLimitGB, SurfaceFile* mySurf); @@ -77,7 +77,7 @@ namespace caret { static float getSubAlgorithmWeight(); static float getAlgorithmInternalWeight(); public: - AlgorithmCiftiCorrelationGradient(ProgressObject* myProgObj, const CiftiFile* myCifti, CiftiFile* myCiftiOut, + AlgorithmCiftiCorrelationGradient(ProgressObject* myProgObj, CiftiFile* myCifti, CiftiFile* myCiftiOut, SurfaceFile* myLeftSurf = NULL, SurfaceFile* myRightSurf = NULL, SurfaceFile* myCerebSurf = NULL, const MetricFile* myLeftAreas = NULL, const MetricFile* myRightAreas = NULL, const MetricFile* myCerebAreas = NULL, const float& surfKern = -1.0f, const float& volKern = -1.0f, const bool& undoFisherInput = false, const bool& applyFisher = false, -- GitLab From d1146e3a4b5fc7f757affa7ff4813ff8b2119855 Mon Sep 17 00:00:00 2001 From: Tim Coalson Date: Fri, 18 Oct 2019 14:33:02 -0500 Subject: [PATCH 421/427] make -cifti-merge discard XML and header extension memory when no longer needed --- src/Cifti/CiftiFile.cxx | 49 ++++++++++++++++++++------ src/Cifti/CiftiFile.h | 4 +++ src/Nifti/NiftiIO.h | 2 ++ src/Operations/OperationCiftiMerge.cxx | 21 +++++++---- 4 files changed, 59 insertions(+), 17 deletions(-) diff --git a/src/Cifti/CiftiFile.cxx b/src/Cifti/CiftiFile.cxx index dca8229c7..13663ed9b 100644 --- a/src/Cifti/CiftiFile.cxx +++ b/src/Cifti/CiftiFile.cxx @@ -39,7 +39,8 @@ namespace class CiftiOnDiskImpl : public CiftiFile::WriteImplInterface { mutable NiftiIO m_nifti;//because file objects aren't stateless (current position), so reading "changes" them - CiftiXML m_xml;//because we need to parse it to set up the dimensions anyway + vector m_matrixDims;//store the dimensions even if the xml is forgotten + CiftiXML m_xml;//we need to store the xml somewhere before it gets put into CiftiFile's copy public: CiftiOnDiskImpl(const QString& filename);//read-only CiftiOnDiskImpl(const QString& filename, const CiftiXML& xml, const CiftiVersion& version, const bool& swapEndian, @@ -52,6 +53,7 @@ namespace void setRow(const float* dataIn, const std::vector& indexSelect); void setColumn(const float* dataIn, const int64_t& index); void close(); + void dropXML() { m_xml = CiftiXML(); m_nifti.dropExtensions(); } }; class CiftiMemoryImpl : public CiftiFile::WriteImplInterface @@ -120,6 +122,8 @@ void CiftiFile::openFile(const QString& fileName) CaretPointer newRead(new CiftiOnDiskImpl(FileInformation(fileName).getAbsoluteFilePath()));//this constructor opens existing file read-only m_readingImpl = newRead;//it should be noted that if the constructor throws (if the file isn't readable), new guarantees the memory allocated for the object will be freed m_xml = newRead->getCiftiXML(); + newRead->dropXML();//save some memory, we don't need 2 copies of the xml - figure out if there is a better way to prevent copies + m_xmlBroken = false; m_dims = m_xml.getDimensions(); m_onDiskVersion = m_xml.getParsedVersion(); m_fileName = fileName; @@ -131,6 +135,7 @@ void CiftiFile::openURL(const QString& url, const QString& user, const QString& CaretPointer newRead(new CiftiXnatImpl(url, user, pass)); m_readingImpl = newRead; m_xml = newRead->getCiftiXML(); + m_xmlBroken = false; m_dims = m_xml.getDimensions(); m_fileName = url; } @@ -141,12 +146,14 @@ void CiftiFile::openURL(const QString& url) CaretPointer newRead(new CiftiXnatImpl(url)); m_readingImpl = newRead; m_xml = newRead->getCiftiXML(); + m_xmlBroken = false; m_dims = m_xml.getDimensions(); m_fileName = url; } void CiftiFile::setWritingFile(const QString& fileName, const CiftiVersion& writingVersion, const ENDIAN& endian) { + if (fileName != "" && m_xmlBroken) throw DataFileException("can't set cifti writing file when XML mappings have been forgotten"); m_writingFile = FileInformation(fileName).getAbsoluteFilePath();//always resolve paths as soon as they enter CiftiFile, in case some clown changes directory before writing data m_writingImpl.grabNew(NULL);//prevent writing to previous writing implementation, let the next set...() set up for writing m_onDiskVersion = writingVersion;//so that we can do on-disk writing with the old version @@ -175,6 +182,7 @@ void CiftiFile::setWritingDataTypeAndScaling(const int16_t& type, const double& void CiftiFile::writeFile(const QString& fileName, const CiftiVersion& writingVersion, const ENDIAN& endian) { if (m_readingImpl == NULL || m_dims.empty()) throw DataFileException("writeFile called on uninitialized CiftiFile"); + if (m_xmlBroken) throw DataFileException("can't write cifti file when XML mappings have been forgotten"); bool writeSwapped = shouldSwap(endian); FileInformation myInfo(fileName); QString canonicalFilename = myInfo.getCanonicalFilePath();//NOTE: returns EMPTY STRING for nonexistant file @@ -214,6 +222,7 @@ void CiftiFile::close() m_readingImpl.grabNew(NULL); m_dims.clear(); m_xml = CiftiXML(); + m_xmlBroken = false; m_writingFile = ""; m_fileName = ""; m_onDiskVersion = CiftiVersion();//for completeness, it gets reset on open anyway @@ -287,6 +296,7 @@ void CiftiFile::setCiftiXML(const CiftiXML& xml, const bool useOldMetadata) m_xml = xml; } m_dims = xmlDims; + m_xmlBroken = false; } void CiftiFile::setCiftiXML(const CiftiXMLOld& xml, const bool useOldMetadata) @@ -308,6 +318,18 @@ void CiftiFile::setCiftiXML(const CiftiXMLOld& xml, const bool useOldMetadata) setCiftiXML(tempXML, useOldMetadata); } +void CiftiFile::forgetMapping(const int& direction) +{ + if (direction >= m_xml.getNumberOfDimensions()) + { + CaretLogWarning("forgetMapping called on nonexistant dimension"); + return; + } + int64_t mapLength = m_xml.getDimensionLength(direction); + m_xml.setMap(direction, CiftiSeriesMap(mapLength));//keep the xml dimension length the same, because it is used in convertToInMemory + m_xmlBroken = true; +} + void CiftiFile::setRow(const float* dataIn, const vector& indexSelect) { verifyWriteImpl(); @@ -373,6 +395,7 @@ void CiftiFile::verifyWriteImpl() m_writingImpl.grabNew(new CiftiMemoryImpl(m_xml)); } } else {//NOTE: m_onDiskVersion gets set in setWritingFile + if (m_xmlBroken) throw DataFileException("can't write file when XML mappings have been forgotten"); if (m_readingImpl != NULL) { CiftiOnDiskImpl* testImpl = dynamic_cast(m_readingImpl.getPointer()); @@ -514,6 +537,7 @@ CiftiOnDiskImpl::CiftiOnDiskImpl(const QString& filename) } } } + m_matrixDims = m_xml.getDimensions(); } namespace @@ -638,9 +662,9 @@ CiftiOnDiskImpl::CiftiOnDiskImpl(const QString& filename, const CiftiXML& xml, c outExtension->m_bytes[i] = xmlBytes[i]; } outHeader.m_extensions.push_back(outExtension); - vector matrixDims = xml.getDimensions(); + m_matrixDims = xml.getDimensions(); vector niftiDims(4, 1);//the reserved space and time dims - niftiDims.insert(niftiDims.end(), matrixDims.begin(), matrixDims.end()); + niftiDims.insert(niftiDims.end(), m_matrixDims.begin(), m_matrixDims.end()); if (version.hasReversedFirstDims()) { vector headerDims = niftiDims; @@ -655,13 +679,16 @@ CiftiOnDiskImpl::CiftiOnDiskImpl(const QString& filename, const CiftiXML& xml, c outHeader.setDimensions(niftiDims); m_nifti.writeNew(filename, outHeader, 2, true, swapEndian); } - m_xml = xml; + m_matrixDims = xml.getDimensions(); + //m_xml = xml;//a second copy of the being-written xml isn't needed, but might be okay + m_xml = CiftiXML();//use an empty one instead } void CiftiOnDiskImpl::close() { m_nifti.close();//lets this throw when there is a writing problem -}//don't bother resetting m_xml, this instance is about to be destroyed + dropXML(); +} void CiftiOnDiskImpl::getRow(float* dataOut, const vector& indexSelect, const bool& tolerateShortRead) const @@ -671,12 +698,12 @@ void CiftiOnDiskImpl::getRow(float* dataOut, const vector& indexSelect, void CiftiOnDiskImpl::getColumn(float* dataOut, const int64_t& index) const { - CaretAssert(m_xml.getNumberOfDimensions() == 2);//otherwise this shouldn't be called - CaretAssert(index >= 0 && index < m_xml.getDimensionLength(CiftiXML::ALONG_ROW)); + CaretAssert(m_matrixDims.size() == 2);//otherwise this shouldn't be called + CaretAssert(index >= 0 && index < m_matrixDims[0]); CaretLogFine("getColumn called on CiftiOnDiskImpl, this will be slow");//generate logging messages at a low priority vector indexSelect(2); indexSelect[0] = index; - int64_t colLength = m_xml.getDimensionLength(CiftiXML::ALONG_COLUMN); + int64_t colLength = m_matrixDims[1]; for (int64_t i = 0; i < colLength; ++i)//assume if they really want getColumn on disk, they don't want their pagecache obliterated, so read it 1 element at a time { indexSelect[1] = i; @@ -691,12 +718,12 @@ void CiftiOnDiskImpl::setRow(const float* dataIn, const vector& indexSe void CiftiOnDiskImpl::setColumn(const float* dataIn, const int64_t& index) { - CaretAssert(m_xml.getNumberOfDimensions() == 2);//otherwise this shouldn't be called - CaretAssert(index >= 0 && index < m_xml.getDimensionLength(CiftiXML::ALONG_ROW)); + CaretAssert(m_matrixDims.size() == 2);//otherwise this shouldn't be called + CaretAssert(index >= 0 && index < m_matrixDims[0]); CaretLogFine("setColumn called on CiftiOnDiskImpl, this will be slow");//generate logging messages at a low priority vector indexSelect(2); indexSelect[0] = index; - int64_t colLength = m_xml.getDimensionLength(CiftiXML::ALONG_COLUMN); + int64_t colLength = m_matrixDims[1]; for (int64_t i = 0; i < colLength; ++i)//don't do RMW, so write it 1 element at a time { indexSelect[1] = i; diff --git a/src/Cifti/CiftiFile.h b/src/Cifti/CiftiFile.h index 62119ff13..5d1907a05 100644 --- a/src/Cifti/CiftiFile.h +++ b/src/Cifti/CiftiFile.h @@ -50,6 +50,7 @@ namespace caret { m_endianPref = NATIVE; setWritingDataTypeNoScaling();//default argument is float32 + m_xmlBroken = false; } explicit CiftiFile(const QString &fileName);//calls openFile void openFile(const QString& fileName);//starts on-disk reading @@ -86,6 +87,8 @@ namespace caret void setRow(const float* dataIn, const int64_t& index);//backwards compatibility for old CiftiFile + void forgetMapping(const int& direction);//HACK: reduce memory usage by modifying the XML + class ReadImplInterface { public: @@ -114,6 +117,7 @@ namespace caret bool m_doWriteScaling; int16_t m_writingDataType; double m_minScalingVal, m_maxScalingVal; + bool m_xmlBroken;//sentinel for forgetMapping hack void verifyWriteImpl(); static void copyImplData(const ReadImplInterface* from, WriteImplInterface* to, const std::vector& dims); diff --git a/src/Nifti/NiftiIO.h b/src/Nifti/NiftiIO.h index 732a8b77b..63ba3faa6 100644 --- a/src/Nifti/NiftiIO.h +++ b/src/Nifti/NiftiIO.h @@ -58,6 +58,7 @@ namespace caret void overrideDimensions(const std::vector& newDims) { m_dims = newDims; }//HACK: deal with reading/writing CIFTI-1's broken headers void close(); const NiftiHeader& getHeader() const { return m_header; } + void dropExtensions() { m_header.m_extensions.clear(); } const std::vector& getDimensions() const { return m_dims; } int getNumComponents() const; //to read/write 1 frame of a standard volume file, call with fullDims = 3, indexSelect containing indexes for any of dims 4-7 that exist @@ -289,6 +290,7 @@ namespace caret TO NiftiIO::clamp(const FROM& in) { typedef std::numeric_limits mylimits; + if (mylimits::has_infinity && std::isinf(in)) return (TO)in;//in case we use this on float types at some point if (mylimits::max() < in) return mylimits::max(); if (mylimits::lowest() > in) return mylimits::lowest(); /*if (mylimits::is_integer)//here is a c++03 solution to missing ::lowest diff --git a/src/Operations/OperationCiftiMerge.cxx b/src/Operations/OperationCiftiMerge.cxx index 62249a39e..ad85df072 100644 --- a/src/Operations/OperationCiftiMerge.cxx +++ b/src/Operations/OperationCiftiMerge.cxx @@ -47,7 +47,7 @@ OperationParameters* OperationCiftiMerge::getParameters() ret->addCiftiOutputParameter(1, "cifti-out", "output cifti file"); ParameterComponent* ciftiOpt = ret->createRepeatableParameter(2, "-cifti", "specify an input cifti file"); - ciftiOpt->addCiftiParameter(1, "cifti-in", "a cifti file to use columns from"); + ciftiOpt->addStringParameter(1, "cifti-in", "a cifti file to use columns from"); ParameterComponent* columnOpt = ciftiOpt->createRepeatableParameter(2, "-column", "select a single column to use"); columnOpt->addStringParameter(1, "column", "the column number (starting from 1) or name"); OptionalParameter* upToOpt = columnOpt->createOptionalParameter(2, "-up-to", "use an inclusive range of columns"); @@ -69,10 +69,11 @@ void OperationCiftiMerge::useParameters(OperationParameters* myParams, ProgressO LevelProgress myProgress(myProgObj); CiftiFile* ciftiOut = myParams->getOutputCifti(1); const vector& myInputs = *(myParams->getRepeatableParameterInstances(2)); - vector ciftiList; int numInputs = (int)myInputs.size(); if (numInputs == 0) throw OperationException("no inputs specified"); - const CiftiFile* firstCifti = myInputs[0]->getCifti(1); + vector ciftiList(numInputs); + ciftiList[0].openFile(myInputs[0]->getString(1)); + const CiftiFile* firstCifti = &(ciftiList[0]); const CiftiXML& baseXML = firstCifti->getCiftiXML(); if (baseXML.getNumberOfDimensions() != 2) throw OperationException("only 2D cifti are supported"); const CiftiMappingType& baseColMapping = *(baseXML.getMap(CiftiXML::ALONG_COLUMN)), &baseRowMapping = *(baseXML.getMap(CiftiXML::ALONG_ROW)); @@ -88,7 +89,11 @@ void OperationCiftiMerge::useParameters(OperationParameters* myParams, ProgressO int64_t numOutColumns = 0;//output row length for (int i = 0; i < numInputs; ++i) { - const CiftiFile* ciftiIn = myInputs[i]->getCifti(1); + if (i != 0) + { + ciftiList[i].openFile(myInputs[i]->getString(1)); + } + const CiftiFile* ciftiIn = &(ciftiList[i]); vector thisDims = ciftiIn->getDimensions(); const CiftiXML& thisXML = ciftiIn->getCiftiXML(); if (thisXML.getNumberOfDimensions() != 2) throw OperationException("only 2D cifti are supported"); @@ -116,6 +121,10 @@ void OperationCiftiMerge::useParameters(OperationParameters* myParams, ProgressO } else { numOutColumns += thisDims[0]; } + if (i != 0)//don't mess with the first file, we use its mapping for the output file + { + ciftiList[i].forgetMapping(CiftiXML::ALONG_COLUMN);//HACK: release the memory being used to store the dense or parcel mapping, to deal with thousands of inputs + } } CiftiScalarsMap outScalarMap;//we only use one of these CiftiLabelsMap outLabelMap; @@ -143,7 +152,7 @@ void OperationCiftiMerge::useParameters(OperationParameters* myParams, ProgressO int64_t curCol = 0, scratchRowLength = 0; for (int i = 0; i < numInputs; ++i) { - const CiftiFile* ciftiIn = myInputs[i]->getCifti(1); + const CiftiFile* ciftiIn = &(ciftiList[i]); vector thisDims = ciftiIn->getDimensions(); const CiftiXML& thisXML = ciftiIn->getCiftiXML(); const vector& columnOpts = *(myInputs[i]->getRepeatableParameterInstances(2)); @@ -262,7 +271,7 @@ void OperationCiftiMerge::useParameters(OperationParameters* myParams, ProgressO curCol = 0; for (int i = 0; i < numInputs; ++i) { - const CiftiFile* ciftiIn = myInputs[i]->getCifti(1); + const CiftiFile* ciftiIn = &(ciftiList[i]); vector thisDims = ciftiIn->getDimensions(); const CiftiXML& thisXML = ciftiIn->getCiftiXML(); const vector& columnOpts = *(myInputs[i]->getRepeatableParameterInstances(2)); -- GitLab From e59d1700c77811c0bf0f3dcc70f58f93f1d51c14 Mon Sep 17 00:00:00 2001 From: Tim Coalson Date: Fri, 18 Oct 2019 16:57:10 -0500 Subject: [PATCH 422/427] add -skip-missing to -zip-spec-file command also --- src/Operations/OperationZipSceneFile.cxx | 2 +- src/Operations/OperationZipSpecFile.cxx | 25 ++++++++++++++++++------ 2 files changed, 20 insertions(+), 7 deletions(-) diff --git a/src/Operations/OperationZipSceneFile.cxx b/src/Operations/OperationZipSceneFile.cxx index 7051c165c..af06d3e3a 100644 --- a/src/Operations/OperationZipSceneFile.cxx +++ b/src/Operations/OperationZipSceneFile.cxx @@ -79,12 +79,12 @@ void OperationZipSceneFile::useParameters(OperationParameters* myParams, Progres AString outputSubDirectory = myParams->getString(2); AString zipFileName = myParams->getString(3); OptionalParameter* baseOpt = myParams->getOptionalParameter(4); - bool skipMissing = myParams->getOptionalParameter(5)->m_present; AString myBaseDir; if (baseOpt->m_present) { myBaseDir = QDir::cleanPath(QDir(baseOpt->getString(1)).absolutePath()); } + bool skipMissing = myParams->getOptionalParameter(5)->m_present; OperationZipSceneFile::createZipFile(myProgObj, sceneFileName, diff --git a/src/Operations/OperationZipSpecFile.cxx b/src/Operations/OperationZipSpecFile.cxx index bba5abe31..11d83c278 100644 --- a/src/Operations/OperationZipSpecFile.cxx +++ b/src/Operations/OperationZipSpecFile.cxx @@ -60,6 +60,8 @@ OperationParameters* OperationZipSpecFile::getParameters() OptionalParameter* baseOpt = ret->createOptionalParameter(4, "-base-dir", "specify a directory that all data files are somewhere within, this will become the root of the zipfile's directory structure"); baseOpt->addStringParameter(1, "directory", "the directory"); + ret->createOptionalParameter(5, "-skip-missing", "any missing files will generate only warnings, and the zip file will be created anyway"); + ret->setHelpText(AString("If zip-file already exists, it will be overwritten. ") + "If -base-dir is not specified, the directory containing the spec file is used for the base directory. " + "The spec file must contain only relative paths, and no data files may be outside the base directory. " + @@ -86,6 +88,7 @@ void OperationZipSpecFile::useParameters(OperationParameters* myParams, Progress {//this is actually because the path function strips the final "/" from the path, but not when it is just "/" myBaseDir += "/";//so, add the trailing slash to the path } + bool skipMissing = myParams->getOptionalParameter(5)->m_present; if (outputSubDirectory.isEmpty()) { throw OperationException("extract-dir must contain characters"); @@ -152,7 +155,12 @@ void OperationZipSpecFile::useParameters(OperationParameters* myParams, Progress } if (!missingDataFileNames.isEmpty()) { - throw OperationException("These data files do not exist:\n" + missingDataFileNames); + if (skipMissing) + { + CaretLogWarning("These data files do not exist and will be skipped:\n" + missingDataFileNames); + } else { + throw OperationException("These data files do not exist:\n" + missingDataFileNames); + } } if (!outsideBaseDirFiles.isEmpty()) { @@ -181,11 +189,16 @@ void OperationZipSpecFile::useParameters(OperationParameters* myParams, Progress AString unzippedDataFileName = outputSubDirectory + "/" + dataFileName.mid(myBaseDir.size());//we know the string matches to the length of myBaseDir, and is cleaned, so we can just chop the right number of characters off QFile dataFileIn(dataFileName); if (dataFileIn.open(QFile::ReadOnly) == false) { - errorMessage = "Unable to open \"" - + dataFileName - + "\" for reading: " - + dataFileIn.errorString(); - break; + if (skipMissing) + { + continue; + } else { + errorMessage = "Unable to open \"" + + dataFileName + + "\" for reading: " + + dataFileIn.errorString(); + break; + } } float fileSize = (float)dataFileIn.size(); int unit = 0; -- GitLab From 4a9ae8a1f5c261d9d8e769f1596d88db8b76fd02 Mon Sep 17 00:00:00 2001 From: Tim Coalson Date: Fri, 18 Oct 2019 17:45:24 -0500 Subject: [PATCH 423/427] fix crash on loading ancient scene file --- src/Scenes/Scene.cxx | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/Scenes/Scene.cxx b/src/Scenes/Scene.cxx index 8bc2a9d72..58e1a76e7 100644 --- a/src/Scenes/Scene.cxx +++ b/src/Scenes/Scene.cxx @@ -499,7 +499,9 @@ Scene::getSceneInfo() const void Scene::setSceneInfo(SceneInfo* sceneInfo) { - CaretAssert(sceneInfo); + if (sceneInfo == NULL) { + return;//TSC: SceneFileXmlStreamReader will call this with NULL argument when reading ancient scene files + } if (m_sceneInfo != NULL) { delete m_sceneInfo; -- GitLab From 5fef05d51f94fc8225daf372adcd7f560ff07813 Mon Sep 17 00:00:00 2001 From: John Harwell Date: Mon, 21 Oct 2019 11:15:22 -0500 Subject: [PATCH 424/427] Fixed bug with window space color bars always being drawn. If in single tab mode (Tile Tabs OFF), color bars from other tabs were being drawn if the color bar was in window space. --- src/Brain/BrainOpenGLFixedPipeline.cxx | 29 ++++++++++++++------------ 1 file changed, 16 insertions(+), 13 deletions(-) diff --git a/src/Brain/BrainOpenGLFixedPipeline.cxx b/src/Brain/BrainOpenGLFixedPipeline.cxx index eecac8610..8f4002f62 100644 --- a/src/Brain/BrainOpenGLFixedPipeline.cxx +++ b/src/Brain/BrainOpenGLFixedPipeline.cxx @@ -503,8 +503,6 @@ BrainOpenGLFixedPipeline::setTabViewport(const BrainOpenGLViewportContent* vpCon * * @param viewportContents * Contents of the viewports. - * @param windowColorBarsOut - * Output with window color bars in the viewports. */ void BrainOpenGLFixedPipeline::setAnnotationColorBarsForDrawing(const std::vector& viewportContents) @@ -512,32 +510,37 @@ BrainOpenGLFixedPipeline::setAnnotationColorBarsForDrawing(const std::vectorsendEvent(colorBarEvent.getPointer()); - m_annotationColorBarsForDrawing = colorBarEvent.getAnnotationColorBars(); + std::vector allColorBars = colorBarEvent.getAnnotationColorBars(); /* - * Tab index is always set in BrowserTabContent when it receives - * EventAnnotationColorBarGet event. So, the tab index should always - * be valid. The user can place the color bar in window space so - * update the window index so that color bar's in window space - * are drawn and drawn only in the window containing the color bar. + * Find the color bars contained in the viewports and + * exclude color bars not in the viewports (color bars that + * are in other windows or other tabs while in single + * tab view). */ - for (auto colorBar : m_annotationColorBarsForDrawing) { + for (auto colorBar : allColorBars) { const int32_t tabIndex = colorBar->getTabIndex(); for (auto vc : viewportContents) { if (vc->getTabIndex() == tabIndex) { + /* + * While color bars are associated with tabs, the color bar + * may be in window space so always update the window index + * (users can move tabs to other windows). + */ colorBar->setWindowIndex(vc->getWindowIndex()); + m_annotationColorBarsForDrawing.push_back(colorBar); + break; } } } } + /** * Draw models in their respective viewports. * -- GitLab From d3436068d0c7f273082eb05f59da9ce785a6fbd1 Mon Sep 17 00:00:00 2001 From: Tim Coalson Date: Wed, 23 Oct 2019 16:21:30 -0500 Subject: [PATCH 425/427] change version to 1.4.0, rework version to be set by the main CMakeLists.txt --- src/CMakeLists.txt | 1 + src/Common/ApplicationInformation.cxx.in | 7 ++----- src/Common/CMakeLists.txt | 2 ++ src/Desktop/CMakeLists.txt | 4 ++-- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 8f3626636..858a096cd 100755 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -3,6 +3,7 @@ # CMAKE_MINIMUM_REQUIRED (VERSION 2.8) +SET(WB_VERSION "1.4.0") # # Set to true for verbose output when debugging this file # diff --git a/src/Common/ApplicationInformation.cxx.in b/src/Common/ApplicationInformation.cxx.in index 67e8be6fa..701e15408 100644 --- a/src/Common/ApplicationInformation.cxx.in +++ b/src/Common/ApplicationInformation.cxx.in @@ -63,12 +63,9 @@ ApplicationInformation::ApplicationInformation() break; } /* - * When updating version, also update these - * items in Desktop/CMakeLists.txt: - * SET (MACOSX_BUNDLE_SHORT_VERSION_STRING 1.3.2) - * SET (MACOSX_BUNDLE_BUNDLE_VERSION 1.3.2) + * Version now gets set at the top of the main CMakeLists.txt only. */ - this->version = "1.3.2"; + this->version = "@VERSION@"; this->commit = "Commit: @COMMIT@"; this->commitDate = "Commit Date: @COMMIT_DATE@"; diff --git a/src/Common/CMakeLists.txt b/src/Common/CMakeLists.txt index 61a63d0ed..9fec2a38a 100644 --- a/src/Common/CMakeLists.txt +++ b/src/Common/CMakeLists.txt @@ -326,6 +326,7 @@ IF (EXISTS ${GIT_REPOSITORY}) COMMAND ${CMAKE_COMMAND} -DINFILE="${CMAKE_SOURCE_DIR}/Common/ApplicationInformation.cxx.in" -DOUTFILE="${CMAKE_BINARY_DIR}/Common/ApplicationInformation.cxx" + -DVERSION="${WB_VERSION}" -P "${CMAKE_SOURCE_DIR}/CMakeScripts/git_commit_info.cmake.in" WORKING_DIRECTORY ${GIT_REPOSITORY}/.. DEPENDS ApplicationInformation.cxx.in ${CMAKE_SOURCE_DIR}/CMakeScripts/git_commit_info.cmake.in ${GIT_REPOSITORY}/HEAD ${GIT_REPOSITORY}/index @@ -337,6 +338,7 @@ ELSE(EXISTS ${GIT_REPOSITORY}) COMMAND ${CMAKE_COMMAND} -DINFILE="${CMAKE_SOURCE_DIR}/Common/ApplicationInformation.cxx.in" -DOUTFILE="${CMAKE_BINARY_DIR}/Common/ApplicationInformation.cxx" + -DVERSION="${WB_VERSION}" -P "${CMAKE_SOURCE_DIR}/CMakeScripts/git_commit_info.cmake.in" WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}/.. DEPENDS ApplicationInformation.cxx.in ${CMAKE_SOURCE_DIR}/CMakeScripts/git_commit_info.cmake.in diff --git a/src/Desktop/CMakeLists.txt b/src/Desktop/CMakeLists.txt index b3581ff76..cc8b7e457 100644 --- a/src/Desktop/CMakeLists.txt +++ b/src/Desktop/CMakeLists.txt @@ -236,8 +236,8 @@ IF (APPLE) SET (MACOSX_BUNDLE_GUI_IDENTIFIER workbench ) SET (MACOSX_BUNDLE_LONG_VERSION_STRING wb_view) SET (MACOSX_BUNDLE_BUNDLE_NAME wb_view) - SET (MACOSX_BUNDLE_SHORT_VERSION_STRING 1.3.2) - SET (MACOSX_BUNDLE_BUNDLE_VERSION 1.3.2) + SET (MACOSX_BUNDLE_SHORT_VERSION_STRING ${WB_VERSION}) + SET (MACOSX_BUNDLE_BUNDLE_VERSION ${WB_VERSION}) SET (MACOSX_BUNDLE_COPYRIGHT 2015 ) ADD_CUSTOM_COMMAND( -- GitLab From 1fc8652f918cc8d7dcd714a4499ac16f864469e5 Mon Sep 17 00:00:00 2001 From: Tim Coalson Date: Wed, 23 Oct 2019 16:32:53 -0500 Subject: [PATCH 426/427] Add dependency on the file containing the version setting --- src/Common/CMakeLists.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Common/CMakeLists.txt b/src/Common/CMakeLists.txt index 9fec2a38a..f8d6d2db5 100644 --- a/src/Common/CMakeLists.txt +++ b/src/Common/CMakeLists.txt @@ -329,7 +329,7 @@ IF (EXISTS ${GIT_REPOSITORY}) -DVERSION="${WB_VERSION}" -P "${CMAKE_SOURCE_DIR}/CMakeScripts/git_commit_info.cmake.in" WORKING_DIRECTORY ${GIT_REPOSITORY}/.. - DEPENDS ApplicationInformation.cxx.in ${CMAKE_SOURCE_DIR}/CMakeScripts/git_commit_info.cmake.in ${GIT_REPOSITORY}/HEAD ${GIT_REPOSITORY}/index + DEPENDS ApplicationInformation.cxx.in ${CMAKE_SOURCE_DIR}/CMakeScripts/git_commit_info.cmake.in ${CMAKE_SOURCE_DIR}/CMakeLists.txt ${GIT_REPOSITORY}/HEAD ${GIT_REPOSITORY}/index COMMENT "Setting commit info" ) ELSE(EXISTS ${GIT_REPOSITORY}) @@ -341,7 +341,7 @@ ELSE(EXISTS ${GIT_REPOSITORY}) -DVERSION="${WB_VERSION}" -P "${CMAKE_SOURCE_DIR}/CMakeScripts/git_commit_info.cmake.in" WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}/.. - DEPENDS ApplicationInformation.cxx.in ${CMAKE_SOURCE_DIR}/CMakeScripts/git_commit_info.cmake.in + DEPENDS ApplicationInformation.cxx.in ${CMAKE_SOURCE_DIR}/CMakeScripts/git_commit_info.cmake.in ${CMAKE_SOURCE_DIR}/CMakeLists.txt COMMENT "No repository found, setting commit info to 'unknown'. Reconfigure to look for the repository again." ) ENDIF(EXISTS ${GIT_REPOSITORY}) -- GitLab From 4e9b463c21409d6bd559f539e27c6d84078870d2 Mon Sep 17 00:00:00 2001 From: John Harwell Date: Fri, 25 Oct 2019 14:31:47 -0500 Subject: [PATCH 427/427] Resolve WB-871 Allow editing of a palette's color bar font color: The font color is editable via selection of the color bar in Annotate Mode. Font color is saved to scenes. --- src/Annotations/Annotation.cxx | 3 +-- src/GuiQt/AnnotationFontWidget.cxx | 4 ++++ 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/src/Annotations/Annotation.cxx b/src/Annotations/Annotation.cxx index 1394a7752..dac133aaa 100644 --- a/src/Annotations/Annotation.cxx +++ b/src/Annotations/Annotation.cxx @@ -1603,7 +1603,7 @@ Annotation::initializeProperties() setProperty(Property::LINE_ARROWS, lineArrowsFlag); setProperty(Property::TEXT_ALIGNMENT, textFlag); setProperty(Property::TEXT_EDIT, textFlag); - setProperty(Property::TEXT_COLOR, textFlag); + setProperty(Property::TEXT_COLOR, colorBarFlag | textFlag); setProperty(Property::TEXT_FONT_NAME, colorBarFlag | textFlag); setProperty(Property::TEXT_FONT_SIZE, colorBarFlag | textFlag); setProperty(Property::TEXT_FONT_STYLE, textFlag); @@ -1625,7 +1625,6 @@ Annotation::initializeProperties() resetProperty(Property::GROUP); resetProperty(Property::LINE_COLOR); resetProperty(Property::LINE_THICKNESS); - resetProperty(Property::TEXT_COLOR); resetProperty(Property::TEXT_EDIT); setProperty(Property::SCENE_CONTAINS_ATTRIBUTES); diff --git a/src/GuiQt/AnnotationFontWidget.cxx b/src/GuiQt/AnnotationFontWidget.cxx index a343b9515..69265393f 100644 --- a/src/GuiQt/AnnotationFontWidget.cxx +++ b/src/GuiQt/AnnotationFontWidget.cxx @@ -459,6 +459,10 @@ AnnotationFontWidget::updateFontSizeControls() void AnnotationFontWidget::updateFontStyleControls() { + m_boldFontAction->setEnabled(false); + m_italicFontAction->setEnabled(false); + m_underlineFontAction->setEnabled(false); + if ( ! m_annotationsFontStyle.empty()) { bool boldOnFlag = true; bool italicOnFlag = true; -- GitLab