4747
4848// MM Solver
4949#include " mmSolver/cmd/anim_curve_cmd_utils.h"
50+ #include " mmSolver/core/types.h"
5051#include " mmSolver/utilities/assert_utils.h"
5152#include " mmSolver/utilities/debug_utils.h"
5253
8485#define SIGNAL_TO_NOISE_RATIO_FLAG_SHORT " -snr"
8586#define SIGNAL_TO_NOISE_RATIO_FLAG_LONG " -signalToNoiseRatio"
8687
88+ #define X_VALUES_FLAG_SHORT " -xv"
89+ #define X_VALUES_FLAG_LONG " -xValues"
90+
91+ #define Y_VALUES_A_FLAG_SHORT " -yva"
92+ #define Y_VALUES_A_FLAG_LONG " -yValuesA"
93+
94+ #define Y_VALUES_B_FLAG_SHORT " -yvb"
95+ #define Y_VALUES_B_FLAG_LONG " -yValuesB"
96+
8797#define CMD_NAME " mmAnimCurveDiffStatistics"
8898
8999// Statistic type identifiers for output
@@ -141,8 +151,18 @@ MSyntax MMAnimCurveDiffStatisticsCmd::newSyntax() {
141151 syntax.addFlag (SIGNAL_TO_NOISE_RATIO_FLAG_SHORT ,
142152 SIGNAL_TO_NOISE_RATIO_FLAG_LONG , MSyntax::kBoolean );
143153
144- // Require exactly 2 animation curves
145- const unsigned int min_curves = 2 ;
154+ // List input flags
155+ syntax.addFlag (X_VALUES_FLAG_SHORT , X_VALUES_FLAG_LONG , MSyntax::kDouble );
156+ syntax.addFlag (Y_VALUES_A_FLAG_SHORT , Y_VALUES_A_FLAG_LONG ,
157+ MSyntax::kDouble );
158+ syntax.addFlag (Y_VALUES_B_FLAG_SHORT , Y_VALUES_B_FLAG_LONG ,
159+ MSyntax::kDouble );
160+ syntax.makeFlagMultiUse (X_VALUES_FLAG_SHORT );
161+ syntax.makeFlagMultiUse (Y_VALUES_A_FLAG_SHORT );
162+ syntax.makeFlagMultiUse (Y_VALUES_B_FLAG_SHORT );
163+
164+ // Require 0 to 2 animation curves (0 when using list inputs)
165+ const unsigned int min_curves = 0 ;
146166 const unsigned int max_curves = 2 ;
147167 syntax.setObjectType (MSyntax::kSelectionList , min_curves, max_curves);
148168 syntax.useSelectionAsDefault (true );
@@ -158,21 +178,104 @@ MStatus MMAnimCurveDiffStatisticsCmd::parseArgs(const MArgList &args) {
158178 MArgDatabase argData (syntax (), args, &status);
159179 MMSOLVER_CHECK_MSTATUS_AND_RETURN_IT (status);
160180
161- // Get animation curves from selection.
162- status = argData.getObjects (m_selection);
163- MMSOLVER_CHECK_MSTATUS_AND_RETURN_IT (status);
181+ // Check if list inputs are provided
182+ bool hasSomeListInputs = argData.isFlagSet (X_VALUES_FLAG_SHORT ) ||
183+ argData.isFlagSet (Y_VALUES_A_FLAG_SHORT ) ||
184+ argData.isFlagSet (Y_VALUES_B_FLAG_SHORT );
185+ bool hasAllListInputs = argData.isFlagSet (X_VALUES_FLAG_SHORT ) &&
186+ argData.isFlagSet (Y_VALUES_A_FLAG_SHORT ) &&
187+ argData.isFlagSet (Y_VALUES_B_FLAG_SHORT );
164188
165- if (m_selection.length () != 2 ) {
166- MGlobal::displayError (CMD_NAME
167- " : Please select exactly two animation curves." );
189+ // All four list flags must be provided, or none at all.
190+ if (hasAllListInputs && !hasAllListInputs) {
191+ MGlobal::displayError (
192+ CMD_NAME
193+ " : All list value flags (xValues, yValuesA, yValuesB) must be "
194+ " provided together, or none at all." );
168195 return MS ::kFailure ;
169196 }
170197
171- // Validate both curves.
172- status = m_selection.getDependNode (0 , m_animCurveObj1);
173- MMSOLVER_CHECK_MSTATUS_AND_RETURN_IT (status);
174- status = m_selection.getDependNode (1 , m_animCurveObj2);
175- MMSOLVER_CHECK_MSTATUS_AND_RETURN_IT (status);
198+ m_useListInput = false ;
199+ if (hasAllListInputs) {
200+ m_useListInput = true ;
201+
202+ Count32 xCount = argData.numberOfFlagUses (X_VALUES_FLAG_SHORT );
203+ Count32 yCount1 = argData.numberOfFlagUses (Y_VALUES_A_FLAG_SHORT );
204+ Count32 yCount2 = argData.numberOfFlagUses (Y_VALUES_A_FLAG_SHORT );
205+
206+ // Validate list lengths
207+ if (xCount != yCount1) {
208+ MGlobal::displayError (
209+ CMD_NAME
210+ " : X and Y value lists for curve 1 must have the same length." );
211+ return MS ::kFailure ;
212+ }
213+
214+ if (xCount != yCount2) {
215+ MGlobal::displayError (
216+ CMD_NAME
217+ " : X and Y value lists for curve 2 must have the same length." );
218+ return MS ::kFailure ;
219+ }
220+
221+ if (xCount < 2 ) {
222+ MGlobal::displayError (
223+ CMD_NAME " : Value lists must contain at least 2 values." );
224+ return MS ::kFailure ;
225+ }
226+
227+ m_xValues.clear ();
228+ m_yValuesA.clear ();
229+ m_yValuesB.clear ();
230+
231+ for (Count32 i = 0 ; i < xCount; ++i) {
232+ MArgList xArgList;
233+ status =
234+ argData.getFlagArgumentList (X_VALUES_FLAG_SHORT , i, xArgList);
235+ MMSOLVER_CHECK_MSTATUS_AND_RETURN_IT (status);
236+ const double value = xArgList.asDouble (0 , &status);
237+ MMSOLVER_CHECK_MSTATUS_AND_RETURN_IT (status);
238+ m_xValues.push_back (value);
239+ }
240+
241+ for (Count32 i = 0 ; i < xCount; ++i) {
242+ MArgList yArgList1;
243+ status = argData.getFlagArgumentList (Y_VALUES_A_FLAG_SHORT , i,
244+ yArgList1);
245+ MMSOLVER_CHECK_MSTATUS_AND_RETURN_IT (status);
246+ const double value = yArgList1.asDouble (0 , &status);
247+ MMSOLVER_CHECK_MSTATUS_AND_RETURN_IT (status);
248+ m_yValuesA.push_back (value);
249+ }
250+
251+ for (Count32 i = 0 ; i < xCount; ++i) {
252+ MArgList yArgList2;
253+ status = argData.getFlagArgumentList (Y_VALUES_B_FLAG_SHORT , i,
254+ yArgList2);
255+ MMSOLVER_CHECK_MSTATUS_AND_RETURN_IT (status);
256+ const double value = yArgList2.asDouble (0 , &status);
257+ MMSOLVER_CHECK_MSTATUS_AND_RETURN_IT (status);
258+ m_yValuesB.push_back (value);
259+ }
260+
261+ } else {
262+ // Get animation curves from selection.
263+ status = argData.getObjects (m_selection);
264+ MMSOLVER_CHECK_MSTATUS_AND_RETURN_IT (status);
265+
266+ if (m_selection.length () != 2 ) {
267+ MGlobal::displayError (CMD_NAME
268+ " : Please select exactly two animation "
269+ " curves or provide list values." );
270+ return MS ::kFailure ;
271+ }
272+
273+ // Validate both curves.
274+ status = m_selection.getDependNode (0 , m_animCurveObj1);
275+ MMSOLVER_CHECK_MSTATUS_AND_RETURN_IT (status);
276+ status = m_selection.getDependNode (1 , m_animCurveObj2);
277+ MMSOLVER_CHECK_MSTATUS_AND_RETURN_IT (status);
278+ }
176279
177280 // Parse optional frame range arguments.
178281 m_startFrame = std::numeric_limits<FrameNumber>::max ();
@@ -264,6 +367,7 @@ MStatus MMAnimCurveDiffStatisticsCmd::parseArgs(const MArgList &args) {
264367 MMSOLVER_MAYA_VRB (CMD_NAME << " : m_calculatePeakToPeak="
265368 << m_calculatePeakToPeak);
266369 MMSOLVER_MAYA_VRB (CMD_NAME << " : m_calculateSNR=" << m_calculateSNR);
370+ MMSOLVER_MAYA_VRB (CMD_NAME << " : m_useListInput=" << m_useListInput);
267371
268372 return status;
269373}
@@ -274,85 +378,116 @@ MStatus MMAnimCurveDiffStatisticsCmd::doIt(const MArgList &args) {
274378 MStatus status = parseArgs (args);
275379 MMSOLVER_CHECK_MSTATUS_AND_RETURN_IT (status);
276380
277- // Set up the animation curve function objects.
278- status = m_animCurveFn1.setObject (m_animCurveObj1);
279- if (status != MS ::kSuccess ) {
280- MGlobal::displayError (
281- CMD_NAME " : First selected object is not an animation curve." );
282- return status;
283- }
381+ // Evaluate both curves.
382+ rust::Vec<mmsg::Real> values_x1;
383+ rust::Vec<mmsg::Real> values_y1;
384+ rust::Vec<mmsg::Real> values_x2;
385+ rust::Vec<mmsg::Real> values_y2;
386+
387+ if (m_useListInput) {
388+ // Use provided list values with interpolation for matching X
389+ // values.
390+ values_x1.reserve (m_xValues.size ());
391+ values_y1.reserve (m_xValues.size ());
392+ values_x2.reserve (m_xValues.size ());
393+ values_y2.reserve (m_xValues.size ());
394+
395+ for (auto i = 0 ; i < m_xValues.size (); i++) {
396+ values_x1.push_back (m_xValues[i]);
397+ }
284398
285- status = m_animCurveFn2.setObject (m_animCurveObj2);
286- if (status != MS ::kSuccess ) {
287- MGlobal::displayError (
288- CMD_NAME " : Second selected object is not an animation curve." );
289- return status;
290- }
399+ for (auto i = 0 ; i < m_yValuesA.size (); i++) {
400+ values_y1.push_back (m_yValuesA[i]);
401+ }
291402
292- // Validate both curves and determine frame ranges.
293- FrameNumber start_frame1 = 0 ;
294- FrameNumber end_frame1 = 0 ;
295- FrameNumber start_frame2 = 0 ;
296- FrameNumber end_frame2 = 0 ;
297- const FrameCount min_keyframe_count = 2 ;
298- const FrameCount min_frame_count = 2 ;
299- const char *cmd_name = CMD_NAME ;
300-
301- bool success1 = validate_anim_curve (
302- cmd_name, m_startFrame, m_endFrame, min_keyframe_count, min_frame_count,
303- m_animCurveFn1, start_frame1, end_frame1);
304- if (!success1) {
305- MGlobal::displayError (CMD_NAME
306- " : Failed to validate first animation curve." );
307- return MS ::kFailure ;
308- }
403+ for (auto i = 0 ; i < m_xValues.size (); i++) {
404+ values_x2.push_back (m_xValues[i]);
405+ }
309406
310- bool success2 = validate_anim_curve (
311- cmd_name, m_startFrame, m_endFrame, min_keyframe_count, min_frame_count,
312- m_animCurveFn2, start_frame2, end_frame2);
313- if (!success2) {
314- MGlobal::displayError (CMD_NAME
315- " : Failed to validate second animation curve." );
316- return MS ::kFailure ;
317- }
407+ for (auto i = 0 ; i < m_yValuesB.size (); i++) {
408+ values_y2.push_back (m_yValuesB[i]);
409+ }
318410
319- // Use the intersection of both frame ranges.
320- FrameNumber start_frame = std::max (start_frame1, start_frame2);
321- FrameNumber end_frame = std::min (end_frame1, end_frame2);
322- if (start_frame > end_frame) {
323- MGlobal::displayError (
324- CMD_NAME " : Animation curves have no overlapping frame range." );
325- return MS ::kFailure ;
326- }
411+ } else {
412+ // Use animation curves
327413
328- MMSOLVER_MAYA_VRB (CMD_NAME << " : start_frame=" << start_frame);
329- MMSOLVER_MAYA_VRB (CMD_NAME << " : end_frame=" << end_frame);
414+ // Set up the animation curve function objects.
415+ status = m_animCurveFn1.setObject (m_animCurveObj1);
416+ if (status != MS ::kSuccess ) {
417+ MGlobal::displayError (
418+ CMD_NAME " : First selected object is not an animation curve." );
419+ return status;
420+ }
330421
331- auto time_unit = MTime::uiUnit ();
332- auto frame_count = static_cast <size_t >(end_frame - start_frame) + 1 ;
422+ status = m_animCurveFn2.setObject (m_animCurveObj2);
423+ if (status != MS ::kSuccess ) {
424+ MGlobal::displayError (
425+ CMD_NAME " : Second selected object is not an animation curve." );
426+ return status;
427+ }
333428
334- // Evaluate both curves.
335- rust::Vec<mmsg::Real> values_x1, values_y1;
336- rust::Vec<mmsg::Real> values_x2, values_y2;
337- values_x1.reserve (frame_count);
338- values_y1.reserve (frame_count);
339- values_x2.reserve (frame_count);
340- values_y2.reserve (frame_count);
341-
342- status = evaluate_curve (start_frame, end_frame, time_unit, m_animCurveFn1,
343- values_x1, values_y1);
344- if (status != MS ::kSuccess ) {
345- MGlobal::displayError (CMD_NAME
346- " : Failed to evaluate first animation curve." );
347- return status;
348- }
429+ // Validate both curves and determine frame ranges.
430+ FrameNumber start_frame1 = 0 ;
431+ FrameNumber end_frame1 = 0 ;
432+ FrameNumber start_frame2 = 0 ;
433+ FrameNumber end_frame2 = 0 ;
434+ const FrameCount min_keyframe_count = 2 ;
435+ const FrameCount min_frame_count = 2 ;
436+ const char *cmd_name = CMD_NAME ;
437+
438+ bool success1 = validate_anim_curve (
439+ cmd_name, m_startFrame, m_endFrame, min_keyframe_count,
440+ min_frame_count, m_animCurveFn1, start_frame1, end_frame1);
441+ if (!success1) {
442+ MGlobal::displayError (
443+ CMD_NAME " : Failed to validate first animation curve." );
444+ return MS ::kFailure ;
445+ }
349446
350- status = evaluate_curve (start_frame, end_frame, time_unit, m_animCurveFn2,
351- values_x2, values_y2);
352- if (status != MS ::kSuccess ) {
353- MGlobal::displayError (CMD_NAME
354- " : Failed to evaluate second animation curve." );
355- return status;
447+ bool success2 = validate_anim_curve (
448+ cmd_name, m_startFrame, m_endFrame, min_keyframe_count,
449+ min_frame_count, m_animCurveFn2, start_frame2, end_frame2);
450+ if (!success2) {
451+ MGlobal::displayError (
452+ CMD_NAME " : Failed to validate second animation curve." );
453+ return MS ::kFailure ;
454+ }
455+
456+ // Use the intersection of both frame ranges.
457+ FrameNumber start_frame = std::max (start_frame1, start_frame2);
458+ FrameNumber end_frame = std::min (end_frame1, end_frame2);
459+ if (start_frame > end_frame) {
460+ MGlobal::displayError (
461+ CMD_NAME " : Animation curves have no overlapping frame range." );
462+ return MS ::kFailure ;
463+ }
464+
465+ MMSOLVER_MAYA_VRB (CMD_NAME << " : start_frame=" << start_frame);
466+ MMSOLVER_MAYA_VRB (CMD_NAME << " : end_frame=" << end_frame);
467+
468+ auto time_unit = MTime::uiUnit ();
469+ auto frame_count = static_cast <size_t >(end_frame - start_frame) + 1 ;
470+
471+ values_x1.reserve (frame_count);
472+ values_y1.reserve (frame_count);
473+ values_x2.reserve (frame_count);
474+ values_y2.reserve (frame_count);
475+
476+ status = evaluate_curve (start_frame, end_frame, time_unit,
477+ m_animCurveFn1, values_x1, values_y1);
478+ if (status != MS ::kSuccess ) {
479+ MGlobal::displayError (
480+ CMD_NAME " : Failed to evaluate first animation curve." );
481+ return status;
482+ }
483+
484+ status = evaluate_curve (start_frame, end_frame, time_unit,
485+ m_animCurveFn2, values_x2, values_y2);
486+ if (status != MS ::kSuccess ) {
487+ MGlobal::displayError (
488+ CMD_NAME " : Failed to evaluate second animation curve." );
489+ return status;
490+ }
356491 }
357492
358493 // Verify we have the same number of values.
0 commit comments