6464#define MEAN_ABSOLUTE_DIFF_FLAG_SHORT " -mad"
6565#define MEAN_ABSOLUTE_DIFF_FLAG_LONG " -meanAbsoluteDifference"
6666
67- #define RMS_DIFF_FLAG_SHORT " -rms "
67+ #define RMS_DIFF_FLAG_SHORT " -rsd "
6868#define RMS_DIFF_FLAG_LONG " -rootMeanSquareDifference"
6969
70- #define MEAN_DIFF_FLAG_SHORT " -md "
70+ #define MEAN_DIFF_FLAG_SHORT " -mnf "
7171#define MEAN_DIFF_FLAG_LONG " -meanDifference"
7272
73- #define MEDIAN_DIFF_FLAG_SHORT " -mdd "
73+ #define MEDIAN_DIFF_FLAG_SHORT " -mdf "
7474#define MEDIAN_DIFF_FLAG_LONG " -medianDifference"
7575
76- #define VARIANCE_FLAG_SHORT " -var "
77- #define VARIANCE_FLAG_LONG " -variance "
76+ #define POPULATION_VARIANCE_FLAG_SHORT " -pvr "
77+ #define POPULATION_VARIANCE_FLAG_LONG " -populationVariance "
7878
79- #define STANDARD_DEVIATION_FLAG_SHORT " -sd "
80- #define STANDARD_DEVIATION_FLAG_LONG " -standardDeviation "
79+ #define POPULATION_STANDARD_DEVIATION_FLAG_SHORT " -psd "
80+ #define POPULATION_STANDARD_DEVIATION_FLAG_LONG " -populationStandardDeviation "
8181
8282#define PEAK_TO_PEAK_FLAG_SHORT " -ptp"
8383#define PEAK_TO_PEAK_FLAG_LONG " -peakToPeak"
8484
8585#define SIGNAL_TO_NOISE_RATIO_FLAG_SHORT " -snr"
8686#define SIGNAL_TO_NOISE_RATIO_FLAG_LONG " -signalToNoiseRatio"
8787
88+ #define MEAN_ABSOLUTE_ERROR_FLAG_SHORT " -mae"
89+ #define MEAN_ABSOLUTE_ERROR_FLAG_LONG " -meanAbsoluteError"
90+
91+ #define ROOT_MEAN_SQUARE_ERROR_FLAG_SHORT " -rme"
92+ #define ROOT_MEAN_SQUARE_ERROR_FLAG_LONG " -rootMeanSquareError"
93+
94+ #define NORMALIZED_RMSE_FLAG_SHORT " -nse"
95+ #define NORMALIZED_RMSE_FLAG_LONG " -normalizedRootMeanSquareError"
96+
97+ #define R_SQUARED_FLAG_SHORT " -r2"
98+ #define R_SQUARED_FLAG_LONG " -rSquared"
99+
88100#define X_VALUES_FLAG_SHORT " -xv"
89101#define X_VALUES_FLAG_LONG " -xValues"
90102
105117#define STAT_TYPE_POPULATION_VARIANCE 5.0
106118#define STAT_TYPE_MEAN_DIFF 6.0
107119#define STAT_TYPE_MEDIAN_DIFF 7.0
120+ #define STAT_TYPE_MEAN_ABSOLUTE_ERROR 8.0
121+ #define STAT_TYPE_ROOT_MEAN_SQUARE_ERROR 9.0
122+ #define STAT_TYPE_NORMALIZED_RMSE 10.0
123+ #define STAT_TYPE_R_SQUARED 11.0
108124
109125namespace mmsg = mmscenegraph;
110126
@@ -143,13 +159,22 @@ MSyntax MMAnimCurveDiffStatisticsCmd::newSyntax() {
143159 MSyntax::kBoolean );
144160 syntax.addFlag (MEDIAN_DIFF_FLAG_SHORT, MEDIAN_DIFF_FLAG_LONG,
145161 MSyntax::kBoolean );
146- syntax.addFlag (VARIANCE_FLAG_SHORT, VARIANCE_FLAG_LONG, MSyntax::kBoolean );
147- syntax.addFlag (STANDARD_DEVIATION_FLAG_SHORT, STANDARD_DEVIATION_FLAG_LONG,
148- MSyntax::kBoolean );
162+ syntax.addFlag (POPULATION_VARIANCE_FLAG_SHORT,
163+ POPULATION_VARIANCE_FLAG_LONG, MSyntax::kBoolean );
164+ syntax.addFlag (POPULATION_STANDARD_DEVIATION_FLAG_SHORT,
165+ POPULATION_STANDARD_DEVIATION_FLAG_LONG, MSyntax::kBoolean );
149166 syntax.addFlag (PEAK_TO_PEAK_FLAG_SHORT, PEAK_TO_PEAK_FLAG_LONG,
150167 MSyntax::kBoolean );
151168 syntax.addFlag (SIGNAL_TO_NOISE_RATIO_FLAG_SHORT,
152169 SIGNAL_TO_NOISE_RATIO_FLAG_LONG, MSyntax::kBoolean );
170+ syntax.addFlag (MEAN_ABSOLUTE_ERROR_FLAG_SHORT,
171+ MEAN_ABSOLUTE_ERROR_FLAG_LONG, MSyntax::kBoolean );
172+ syntax.addFlag (ROOT_MEAN_SQUARE_ERROR_FLAG_SHORT,
173+ ROOT_MEAN_SQUARE_ERROR_FLAG_LONG, MSyntax::kBoolean );
174+ syntax.addFlag (NORMALIZED_RMSE_FLAG_SHORT, NORMALIZED_RMSE_FLAG_LONG,
175+ MSyntax::kBoolean );
176+ syntax.addFlag (R_SQUARED_FLAG_SHORT, R_SQUARED_FLAG_LONG,
177+ MSyntax::kBoolean );
153178
154179 // List input flags
155180 syntax.addFlag (X_VALUES_FLAG_SHORT, X_VALUES_FLAG_LONG, MSyntax::kDouble );
@@ -295,10 +320,14 @@ MStatus MMAnimCurveDiffStatisticsCmd::parseArgs(const MArgList &args) {
295320 m_calculateRmsDiff = false ;
296321 m_calculateMeanDiff = false ;
297322 m_calculateMedianDiff = false ;
298- m_calculateVariance = false ;
299- m_calculateStdDev = false ;
323+ m_calculatePopVariance = false ;
324+ m_calculatePopStdDev = false ;
300325 m_calculatePeakToPeak = false ;
301326 m_calculateSNR = false ;
327+ m_calculateMAE = false ;
328+ m_calculateRMSE = false ;
329+ m_calculateNRMSE = false ;
330+ m_calculateR2 = false ;
302331
303332 if (argData.isFlagSet (MEAN_ABSOLUTE_DIFF_FLAG_SHORT)) {
304333 status = argData.getFlagArgument (MEAN_ABSOLUTE_DIFF_FLAG_SHORT, 0 ,
@@ -320,14 +349,14 @@ MStatus MMAnimCurveDiffStatisticsCmd::parseArgs(const MArgList &args) {
320349 m_calculateMedianDiff);
321350 MMSOLVER_CHECK_MSTATUS_AND_RETURN_IT (status);
322351 }
323- if (argData.isFlagSet (VARIANCE_FLAG_SHORT )) {
324- status = argData.getFlagArgument (VARIANCE_FLAG_SHORT , 0 ,
325- m_calculateVariance );
352+ if (argData.isFlagSet (POPULATION_VARIANCE_FLAG_SHORT )) {
353+ status = argData.getFlagArgument (POPULATION_VARIANCE_FLAG_SHORT , 0 ,
354+ m_calculatePopVariance );
326355 MMSOLVER_CHECK_MSTATUS_AND_RETURN_IT (status);
327356 }
328- if (argData.isFlagSet (STANDARD_DEVIATION_FLAG_SHORT )) {
329- status = argData.getFlagArgument (STANDARD_DEVIATION_FLAG_SHORT, 0 ,
330- m_calculateStdDev );
357+ if (argData.isFlagSet (POPULATION_STANDARD_DEVIATION_FLAG_SHORT )) {
358+ status = argData.getFlagArgument (
359+ POPULATION_STANDARD_DEVIATION_FLAG_SHORT, 0 , m_calculatePopStdDev );
331360 MMSOLVER_CHECK_MSTATUS_AND_RETURN_IT (status);
332361 }
333362 if (argData.isFlagSet (PEAK_TO_PEAK_FLAG_SHORT)) {
@@ -340,12 +369,33 @@ MStatus MMAnimCurveDiffStatisticsCmd::parseArgs(const MArgList &args) {
340369 m_calculateSNR);
341370 MMSOLVER_CHECK_MSTATUS_AND_RETURN_IT (status);
342371 }
372+ if (argData.isFlagSet (MEAN_ABSOLUTE_ERROR_FLAG_SHORT)) {
373+ status = argData.getFlagArgument (MEAN_ABSOLUTE_ERROR_FLAG_SHORT, 0 ,
374+ m_calculateMAE);
375+ MMSOLVER_CHECK_MSTATUS_AND_RETURN_IT (status);
376+ }
377+ if (argData.isFlagSet (ROOT_MEAN_SQUARE_ERROR_FLAG_SHORT)) {
378+ status = argData.getFlagArgument (ROOT_MEAN_SQUARE_ERROR_FLAG_SHORT, 0 ,
379+ m_calculateRMSE);
380+ MMSOLVER_CHECK_MSTATUS_AND_RETURN_IT (status);
381+ }
382+ if (argData.isFlagSet (NORMALIZED_RMSE_FLAG_SHORT)) {
383+ status = argData.getFlagArgument (NORMALIZED_RMSE_FLAG_SHORT, 0 ,
384+ m_calculateNRMSE);
385+ MMSOLVER_CHECK_MSTATUS_AND_RETURN_IT (status);
386+ }
387+ if (argData.isFlagSet (R_SQUARED_FLAG_SHORT)) {
388+ status =
389+ argData.getFlagArgument (R_SQUARED_FLAG_SHORT, 0 , m_calculateR2);
390+ MMSOLVER_CHECK_MSTATUS_AND_RETURN_IT (status);
391+ }
343392
344393 // If no flags are set, calculate all statistics by default
345394 if (!m_calculateMeanAbsDiff && !m_calculateRmsDiff &&
346395 !m_calculateMeanDiff && !m_calculateMedianDiff &&
347- !m_calculateVariance && !m_calculateStdDev && !m_calculatePeakToPeak &&
348- !m_calculateSNR) {
396+ !m_calculatePopVariance && !m_calculatePopStdDev &&
397+ !m_calculatePeakToPeak && !m_calculateSNR && !m_calculateMAE &&
398+ !m_calculateRMSE && !m_calculateNRMSE && !m_calculateR2) {
349399 MGlobal::displayError (CMD_NAME
350400 " : At least one statistic must be enabled." );
351401 return MS::kFailure ;
@@ -361,12 +411,17 @@ MStatus MMAnimCurveDiffStatisticsCmd::parseArgs(const MArgList &args) {
361411 << m_calculateMeanDiff);
362412 MMSOLVER_MAYA_VRB (CMD_NAME << " : m_calculateMedianDiff="
363413 << m_calculateMedianDiff);
364- MMSOLVER_MAYA_VRB (CMD_NAME << " : m_calculateVariance="
365- << m_calculateVariance);
366- MMSOLVER_MAYA_VRB (CMD_NAME << " : m_calculateStdDev=" << m_calculateStdDev);
414+ MMSOLVER_MAYA_VRB (CMD_NAME << " : m_calculatePopVariance="
415+ << m_calculatePopVariance);
416+ MMSOLVER_MAYA_VRB (CMD_NAME << " : m_calculatePopStdDev="
417+ << m_calculatePopStdDev);
367418 MMSOLVER_MAYA_VRB (CMD_NAME << " : m_calculatePeakToPeak="
368419 << m_calculatePeakToPeak);
369420 MMSOLVER_MAYA_VRB (CMD_NAME << " : m_calculateSNR=" << m_calculateSNR);
421+ MMSOLVER_MAYA_VRB (CMD_NAME << " : m_calculateMAE=" << m_calculateMAE);
422+ MMSOLVER_MAYA_VRB (CMD_NAME << " : m_calculateRMSE=" << m_calculateRMSE);
423+ MMSOLVER_MAYA_VRB (CMD_NAME << " : m_calculateNRMSE=" << m_calculateNRMSE);
424+ MMSOLVER_MAYA_VRB (CMD_NAME << " : m_calculateR2=" << m_calculateR2);
370425 MMSOLVER_MAYA_VRB (CMD_NAME << " : m_useListInput=" << m_useListInput);
371426
372427 return status;
@@ -517,6 +572,10 @@ MStatus MMAnimCurveDiffStatisticsCmd::doIt(const MArgList &args) {
517572 differences.size ()};
518573 rust::Slice<const mmsg::Real> abs_diff_slice{absolute_differences.data (),
519574 absolute_differences.size ()};
575+ rust::Slice<const mmsg::Real> values_y1_slice{values_y1.data (),
576+ values_y1.size ()};
577+ rust::Slice<const mmsg::Real> values_y2_slice{values_y2.data (),
578+ values_y2.size ()};
520579
521580 // Output format:
522581 // [stat_count, statType1, statValue1, statType2, statValue2, ...]
@@ -546,7 +605,7 @@ MStatus MMAnimCurveDiffStatisticsCmd::doIt(const MArgList &args) {
546605 }
547606
548607 // Calculate mean and variance if needed (they come together)
549- if (m_calculateMeanDiff || m_calculateVariance ) {
608+ if (m_calculateMeanDiff || m_calculatePopVariance ) {
550609 if (mmsg::calc_population_variance (diff_slice, mean_diff,
551610 variance_diff)) {
552611 meanCalculated = true ;
@@ -556,7 +615,7 @@ MStatus MMAnimCurveDiffStatisticsCmd::doIt(const MArgList &args) {
556615 statsResults.push_back ({STAT_TYPE_MEAN_DIFF, mean_diff});
557616 MMSOLVER_MAYA_VRB (CMD_NAME << " : mean_diff=" << mean_diff);
558617 }
559- if (m_calculateVariance ) {
618+ if (m_calculatePopVariance ) {
560619 statsResults.push_back (
561620 {STAT_TYPE_POPULATION_VARIANCE, variance_diff});
562621 MMSOLVER_MAYA_VRB (CMD_NAME << " : population_variance="
@@ -570,7 +629,7 @@ MStatus MMAnimCurveDiffStatisticsCmd::doIt(const MArgList &args) {
570629 }
571630
572631 // Calculate population standard deviation.
573- if (m_calculateStdDev ) {
632+ if (m_calculatePopStdDev ) {
574633 if (varianceCalculated) {
575634 // We already have variance, just calculate std dev from it
576635 mmsg::Real std_dev_diff = std::sqrt (variance_diff);
@@ -666,6 +725,62 @@ MStatus MMAnimCurveDiffStatisticsCmd::doIt(const MArgList &args) {
666725 }
667726 }
668727
728+ // Calculate Mean Absolute Error (MAE).
729+ if (m_calculateMAE) {
730+ mmsg::Real mae = 0.0 ;
731+ if (mmsg::calc_mean_absolute_error (values_y2_slice, values_y1_slice,
732+ mae)) {
733+ statsResults.push_back ({STAT_TYPE_MEAN_ABSOLUTE_ERROR, mae});
734+ MMSOLVER_MAYA_VRB (CMD_NAME << " : mean_absolute_error=" << mae);
735+ } else {
736+ MGlobal::displayWarning (
737+ CMD_NAME " : Failed to calculate mean absolute error." );
738+ }
739+ }
740+
741+ // Calculate Root Mean Square Error (RMSE).
742+ if (m_calculateRMSE) {
743+ mmsg::Real rmse = 0.0 ;
744+ if (mmsg::calc_root_mean_square_error (values_y2_slice, values_y1_slice,
745+ rmse)) {
746+ statsResults.push_back ({STAT_TYPE_ROOT_MEAN_SQUARE_ERROR, rmse});
747+ MMSOLVER_MAYA_VRB (CMD_NAME << " : root_mean_square_error=" << rmse);
748+ } else {
749+ MGlobal::displayWarning (
750+ CMD_NAME " : Failed to calculate root mean square error." );
751+ }
752+ }
753+
754+ // Calculate Normalized Root Mean Square Error (NRMSE).
755+ if (m_calculateNRMSE) {
756+ mmsg::Real nrmse = 0.0 ;
757+ if (mmsg::calc_normalized_root_mean_square_error (
758+ values_y2_slice, values_y1_slice, nrmse)) {
759+ statsResults.push_back ({STAT_TYPE_NORMALIZED_RMSE, nrmse});
760+ MMSOLVER_MAYA_VRB (CMD_NAME << " : normalized_root_mean_square_error="
761+ << nrmse);
762+ } else {
763+ MGlobal::displayWarning (
764+ CMD_NAME
765+ " : Failed to calculate normalized root mean square error." );
766+ }
767+ }
768+
769+ // Calculate Coefficient of Determination (R^2 also known as "R-squared").
770+ if (m_calculateR2) {
771+ mmsg::Real r_squared = 0.0 ;
772+ // Note: For R^2, we treat the first curve as "actual" and
773+ // second as "predicted".
774+ if (mmsg::calc_coefficient_of_determination (
775+ values_y1_slice, values_y2_slice, r_squared)) {
776+ statsResults.push_back ({STAT_TYPE_R_SQUARED, r_squared});
777+ MMSOLVER_MAYA_VRB (CMD_NAME << " : r_squared=" << r_squared);
778+ } else {
779+ MGlobal::displayWarning (
780+ CMD_NAME " : Failed to calculate coefficient of determination." );
781+ }
782+ }
783+
669784 // Build result array.
670785 const auto statistics_count = static_cast <double >(statsResults.size ());
671786 result.append (static_cast <double >(statistics_count));
0 commit comments