Skip to content

Commit 99d7fe4

Browse files
mmAnimCurveDiffStatistics - Add new data set comparing
I've added some new statistical types for querying the difference between two data sets, and I've renamed some of the command argument flags to better explain what it does. Note; we MUST use 3 characters or less to represent the command "short" arguments. See the following Maya API documentation for MSyntax.addFlag(): Parameters: - shortName: the string representing the short (< 4 character) version of the flag. - longName: the string representing the int (> 3 character) version of the flag. https://help.autodesk.com/view/MAYADEV/2025/ENU/?guid=MAYA_API_REF_cpp_ref_class_m_syntax_html
1 parent 49cbfdf commit 99d7fe4

3 files changed

Lines changed: 369 additions & 51 deletions

File tree

src/mmSolver/cmd/MMAnimCurveDiffStatisticsCmd.cpp

Lines changed: 141 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -64,27 +64,39 @@
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

@@ -105,6 +117,10 @@
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

109125
namespace 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));

src/mmSolver/cmd/MMAnimCurveDiffStatisticsCmd.h

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -52,10 +52,14 @@ class MMAnimCurveDiffStatisticsCmd : public MPxCommand {
5252
, m_calculateRmsDiff(false)
5353
, m_calculateMeanDiff(false)
5454
, m_calculateMedianDiff(false)
55-
, m_calculateVariance(false)
56-
, m_calculateStdDev(false)
55+
, m_calculatePopVariance(false)
56+
, m_calculatePopStdDev(false)
5757
, m_calculatePeakToPeak(false)
5858
, m_calculateSNR(false)
59+
, m_calculateMAE(false)
60+
, m_calculateRMSE(false)
61+
, m_calculateNRMSE(false)
62+
, m_calculateR2(false)
5963
, m_useListInput(false){};
6064
virtual ~MMAnimCurveDiffStatisticsCmd();
6165

@@ -80,10 +84,14 @@ class MMAnimCurveDiffStatisticsCmd : public MPxCommand {
8084
bool m_calculateRmsDiff;
8185
bool m_calculateMeanDiff;
8286
bool m_calculateMedianDiff;
83-
bool m_calculateVariance;
84-
bool m_calculateStdDev;
87+
bool m_calculatePopVariance;
88+
bool m_calculatePopStdDev;
8589
bool m_calculatePeakToPeak;
8690
bool m_calculateSNR;
91+
bool m_calculateMAE;
92+
bool m_calculateRMSE;
93+
bool m_calculateNRMSE;
94+
bool m_calculateR2;
8795

8896
// The two animation curves to compare.
8997
MSelectionList m_selection;

0 commit comments

Comments
 (0)