diff --git a/python/mmSolver/_api/_execute/actionstate.py b/python/mmSolver/_api/_execute/actionstate.py index ed52723e9..681c0de19 100644 --- a/python/mmSolver/_api/_execute/actionstate.py +++ b/python/mmSolver/_api/_execute/actionstate.py @@ -89,7 +89,10 @@ def run_validate_action(vaction): ) return state vfunc, vargs, vkwargs = api_action.action_to_components(vaction) - vfunc_is_mmsolver = api_action.action_func_is_mmSolver(vaction) + vfunc_is_mmsolver_v1 = api_action.action_func_is_mmSolver_v1(vaction) + vfunc_is_mmsolver_v2 = api_action.action_func_is_mmSolver_v2(vaction) + vfunc_is_camera_solve = api_action.action_func_is_camera_solve(vaction) + vfunc_is_mmsolver = any((vfunc_is_mmsolver_v1, vfunc_is_mmsolver_v2)) num_param = 0 num_err = 0 @@ -124,7 +127,15 @@ def run_validate_action(vaction): ) return state + if vfunc_is_mmsolver_v2 is True: + solve_data = vkwargs['resultsNode'] + if vfunc_is_camera_solve is True: + if const.SOLVER_VERSION_DEFAULT == const.SOLVER_VERSION_TWO: + # Get the collection node given to the camera solve. + solve_data = vargs[0] + solres = solveresult.SolveResult(solve_data) + print_stats = solres.get_print_stats() num_param = print_stats.get('number_of_parameters', 0) num_err = print_stats.get('number_of_errors', 0) diff --git a/python/mmSolver/_api/_execute/main.py b/python/mmSolver/_api/_execute/main.py index bf9a39370..69d33538a 100644 --- a/python/mmSolver/_api/_execute/main.py +++ b/python/mmSolver/_api/_execute/main.py @@ -37,6 +37,7 @@ import mmSolver._api.solveresult as solveresult import mmSolver._api.action as api_action import mmSolver._api.solverbase as solverbase +import mmSolver._api.collection as api_collection import mmSolver._api.collectionutils as collectionutils import mmSolver._api.constant as const @@ -126,11 +127,45 @@ def _validate_scene_graph_in_actions(action_list, vaction_list): return is_valid +def _override_actions_set_results_node(col, action_list, vaction_list): + assert isinstance(col, api_collection.Collection) + + col_node = col.get_node() + assert col_node is not None + assert maya.cmds.objExists(col_node) + + new_action_list = [] + new_vaction_list = [] + for action, vaction in zip(action_list, vaction_list): + is_mmsolver_v2 = api_action.action_func_is_mmSolver_v2(action) + vis_mmsolver_v2 = api_action.action_func_is_mmSolver_v2(vaction) + if is_mmsolver_v2 is False or vis_mmsolver_v2 is False: + new_action_list.append(action) + new_vaction_list.append(vaction) + continue + + func, args, kwargs = api_action.action_to_components(action) + vfunc, vargs, vkwargs = api_action.action_to_components(vaction) + kwargs['resultsNode'] = col_node + vkwargs['resultsNode'] = col_node + + new_action = api_action.Action(func=func, args=args, kwargs=kwargs) + new_vaction = api_action.Action(func=vfunc, args=vargs, kwargs=vkwargs) + new_action_list.append(new_action) + new_vaction_list.append(new_vaction) + + action_list = new_action_list + vaction_list = new_vaction_list + return action_list, vaction_list + + def _override_actions_scene_graph_use_maya_dag(action_list, vaction_list): new_action_list = [] new_vaction_list = [] for action, vaction in zip(action_list, vaction_list): - if api_action.action_func_is_mmSolver(action) is not True: + is_mmsolver_v1 = api_action.action_func_is_mmSolver_v1(action) + is_mmsolver_v2 = api_action.action_func_is_mmSolver_v2(action) + if any((is_mmsolver_v1, is_mmsolver_v2)) is not True: continue func, args, kwargs = api_action.action_to_components(action) @@ -260,7 +295,10 @@ def execute( # 'finally' block. kwargs = {} save_node_attrs = [] - func_is_mmsolver = False + marker_relock_nodes = set() + collection_relock_nodes = set() + func_is_mmsolver_v1 = False + func_is_mmsolver_v2 = False is_single_frame = False try: @@ -276,6 +314,7 @@ def execute( const.VALIDATE_MODE_PRE_VALIDATE_VALUE, const.VALIDATE_MODE_AT_RUNTIME_VALUE, ] + col_node = col.get_node() sol_list = col.get_solver_list() mkr_list = col.get_marker_list() attr_list = col.get_attribute_list() @@ -302,6 +341,12 @@ def execute( action_list, vaction_list ) + # Prepare nodes so they can be set from mmSolver commands, + # without needing to unlock attributes (which is not possible + # using MDGModifier API class). + collection_relock_nodes |= executepresolve.preSolve_unlockCollectionAttrs(col) + marker_relock_nodes |= executepresolve.preSolve_unlockMarkerAttrs(mkr_list) + # Prepare frame solve executepresolve.preSolve_setIsolatedNodes(action_list, options, panels) executepresolve.preSolve_triggerEvaluation(action_list, cur_frame, options) @@ -309,6 +354,11 @@ def execute( # Ensure prediction attributes are created and initialised. collectionutils.set_initial_prediction_attributes(col, attr_list, cur_frame) + # Ensure the resultsNode flag is set for mmSolver_v2 commands. + action_list, vaction_list = _override_actions_set_results_node( + col, action_list, vaction_list + ) + vaction_state_list = [] if validate_before is True: vaction_state_list = actionstate.run_validate_action_list(vaction_list) @@ -339,7 +389,9 @@ def execute( continue func, args, kwargs = api_action.action_to_components(action) - func_is_mmsolver = api_action.action_func_is_mmSolver(action) + func_is_mmsolver_v1 = api_action.action_func_is_mmSolver_v1(action) + func_is_mmsolver_v2 = api_action.action_func_is_mmSolver_v2(action) + func_is_mmsolver = any((func_is_mmsolver_v2, func_is_mmsolver_v1)) func_is_scene_graph = api_action.action_func_is_mmSolverSceneGraph(action) func_is_camera_solve = api_action.action_func_is_camera_solve(action) @@ -348,6 +400,7 @@ def execute( # pre-process, so we can skip them now we're properly # solving. continue + if func_is_mmsolver is True: frame = kwargs.get('frame') if frame is None or len(frame) == 0: @@ -384,9 +437,9 @@ def execute( kwargs['sceneGraphMode'] = const.SCENE_GRAPH_MODE_MAYA_DAG elif func_is_camera_solve is True: - root_frames = args[5] - start_frame = args[6] - end_frame = args[7] + root_frames = args[6] + start_frame = args[7] + end_frame = args[8] assert isinstance(root_frames, list) assert isinstance(start_frame, int) assert isinstance(end_frame, int) @@ -411,13 +464,22 @@ def execute( # Create SolveResult. solres = None if solve_data is not None: - if func_is_mmsolver is True or func_is_camera_solve is True: + if func_is_mmsolver_v2 is True: + solve_data = kwargs['resultsNode'] + solres = solveresult.SolveResult(solve_data) + solres_list.append(solres) + elif func_is_mmsolver_v1 is True: solres = solveresult.SolveResult(solve_data) solres_list.append(solres) elif func_is_scene_graph is True: solres = solve_data + elif func_is_camera_solve is True: + if const.SOLVER_VERSION_DEFAULT == const.SOLVER_VERSION_TWO: + solve_data = args[0] # Get the collection node. + solres = solveresult.SolveResult(solve_data) + solres_list.append(solres) - if func_is_mmsolver is True and solres.get_success() is True: + if func_is_mmsolver_v1 is True and solres.get_success() is True: frame = kwargs.get('frame') if frame is None or len(frame) == 0: raise excep.NotValid @@ -447,17 +509,24 @@ def execute( break # Refresh the Viewport. - if func_is_mmsolver is True: + if func_is_mmsolver_v1 is True: frame = kwargs.get('frame') executepostsolve.postSolve_refreshViewport(options, frame) finally: # If something has gone wrong, or the user cancels the solver # without finishing, then we make sure to reconnect animcurves # that were disconnected for single frame solves. - if func_is_mmsolver is True and is_single_frame is True: + if func_is_mmsolver_v1 is True and is_single_frame is True: if len(save_node_attrs): collectionutils.reconnect_animcurves(kwargs, save_node_attrs) + # Re-lock attributes that were unlocked so that the + # mmSolver command could set values on them. + executepostsolve.postSolve_relockCollectionAttrs(collection_relock_nodes) + executepostsolve.postSolve_relockMarkerAttrs(marker_relock_nodes) + collection_relock_nodes = set() + marker_relock_nodes = set() + executepostsolve.postSolve_setViewportState( options, panel_objs, panel_node_type_vis ) diff --git a/python/mmSolver/_api/_execute/postsolve.py b/python/mmSolver/_api/_execute/postsolve.py index e59802f9b..43fbb55a1 100644 --- a/python/mmSolver/_api/_execute/postsolve.py +++ b/python/mmSolver/_api/_execute/postsolve.py @@ -24,6 +24,8 @@ import mmSolver.logger import mmSolver.utils.viewport as viewport_utils +import mmSolver.utils.node as node_utils +import mmSolver._api.constant as const import mmSolver._api.state as api_state import mmSolver._api.solveresult as solveresult import mmSolver._api.collectionutils as collectionutils @@ -165,3 +167,25 @@ def postSolve_setUpdateProgress( collectionutils.run_status_func(status_fn, 'ERROR: ' + msg) LOG.error(msg) return stop_solving + + +def _postSolve_relockAttrs(nodes, possible_attrs): + assert isinstance(nodes, set) + assert isinstance(possible_attrs, set) + for node_name in nodes: + existing_attrs = set(maya.cmds.listAttr(node_name)) + attrs = existing_attrs & possible_attrs + for attr_name in attrs: + plug = '{}.{}'.format(node_name, attr_name) + maya.cmds.setAttr(plug, lock=True) + return + + +def postSolve_relockCollectionAttrs(relock_nodes): + possible_attrs = set(const.COLLECTION_RESULTS_STORE_ATTR_NAMES) + return _postSolve_relockAttrs(relock_nodes, possible_attrs) + + +def postSolve_relockMarkerAttrs(relock_nodes): + possible_attrs = set(const.MARKER_RESULTS_STORE_ATTR_NAMES) + return _postSolve_relockAttrs(relock_nodes, possible_attrs) diff --git a/python/mmSolver/_api/_execute/presolve.py b/python/mmSolver/_api/_execute/presolve.py index 8f359f7ab..20e605fe0 100644 --- a/python/mmSolver/_api/_execute/presolve.py +++ b/python/mmSolver/_api/_execute/presolve.py @@ -27,10 +27,14 @@ import mmSolver.logger import mmSolver.utils.viewport as viewport_utils +import mmSolver._api.constant as const import mmSolver._api.state as api_state import mmSolver._api.excep as excep import mmSolver._api.solveresult as solveresult import mmSolver._api.collectionutils as collectionutils +import mmSolver._api.marker as api_marker +import mmSolver._api.collection as api_collection + LOG = mmSolver.logger.get_logger() @@ -160,3 +164,55 @@ def preSolve_triggerEvaluation(action_list, cur_frame, options): update=options.force_update, ) return + + +def preSolve_unlockCollectionAttrs(col): + """ + Unlock all Collection attributes that will be set by mmSolver + commands. + """ + assert isinstance(col, api_collection.Collection) + + col_node = col.get_node() + assert col_node is not None + assert maya.cmds.objExists(col_node) + relock_nodes = set([col_node]) + + existing_attrs = set(maya.cmds.listAttr(col_node)) + attrs = existing_attrs & set(const.COLLECTION_RESULTS_STORE_ATTR_NAMES) + + for attr_name in attrs: + # The plug may not exist, yet, but after a solve has finished the + # attribute may need to be locked. + plug = '{0}.{1}'.format(col_node, attr_name) + + locked = maya.cmds.getAttr(plug, lock=True) + if locked is True: + maya.cmds.setAttr(plug, lock=False) + return relock_nodes + + +def preSolve_unlockMarkerAttrs(mkr_list): + """ + Unlock all Marker attributes that will be set by mmSolver + commands. + """ + relock_nodes = set() + for mkr in mkr_list: + assert isinstance(mkr, api_marker.Marker) + mkr_node = mkr.get_node() + assert mkr_node is not None + relock_nodes.add(mkr_node) + + existing_attrs = set(maya.cmds.listAttr(mkr_node)) + attrs = existing_attrs & set(const.MARKER_RESULTS_STORE_ATTR_NAMES) + + for attr_name in attrs: + # The plug may not exist, yet, but after a solve has finished the + # attribute may need to be locked. + plug = '{0}.{1}'.format(mkr_node, attr_name) + + locked = maya.cmds.getAttr(plug, lock=True) + if locked is True: + maya.cmds.setAttr(plug, lock=False) + return relock_nodes diff --git a/python/mmSolver/_api/constant.py b/python/mmSolver/_api/constant.py index 6cbc7aba3..9995aeca4 100644 --- a/python/mmSolver/_api/constant.py +++ b/python/mmSolver/_api/constant.py @@ -288,6 +288,64 @@ LINE_ATTR_LONG_NAME_WEIGHT = 'weight' +# Attribute names used to store results on Marker nodes. +MARKER_RESULTS_STORE_ATTR_NAMES = [ + MARKER_ATTR_LONG_NAME_DEVIATION, + MARKER_ATTR_LONG_NAME_AVG_DEVIATION, + MARKER_ATTR_LONG_NAME_MAX_DEVIATION, + MARKER_ATTR_LONG_NAME_MAX_DEV_FRAME, +] + +# Attribute names used to store results on Collection nodes. +COLLECTION_RESULTS_STORE_ATTR_NAMES = [ + # Existing Attributes + COLLECTION_ATTR_LONG_NAME_SOLVE_TIMESTAMP, + COLLECTION_ATTR_LONG_NAME_SOLVE_DURATION, + # + # SolverResult + 'success', + 'reason_num', + 'reason_string', + 'error_final', + 'error_final_average', + 'error_final_maximum', + 'error_final_minimum', + 'iteration_num', + 'iteration_function_num', + 'iteration_jacobian_num', + 'user_interrupted', + # + # SolverObjectCountResult + 'numberOfParameters', + 'numberOfErrors', + 'numberOfMarkerErrors', + 'numberOfAttrStiffnessErrors', + 'numberOfattrSmoothnessErrors', + # + # ErrorMetricsResult + COLLECTION_ATTR_LONG_NAME_DEVIATION, # Same as "error_per_frame". + 'average_deviation', + 'maximum_deviation', + 'maximum_deviation_frame', + 'error_per_marker_per_frame', + # + # SolveValuesResult + 'solve_parameter_list', + 'solve_error_list', + # + # TimerResult + 'timer_solve', + 'timer_function', + 'timer_jacobian', + 'timer_parameter', + 'timer_error', + 'ticks_solve', + 'ticks_function', + 'ticks_jacobian', + 'ticks_parameter', + 'ticks_error', +] + # Default plate fallback values. DEFAULT_PLATE_WIDTH = 2048 DEFAULT_PLATE_HEIGHT = 1556 diff --git a/python/mmSolver/_api/solvercamera.py b/python/mmSolver/_api/solvercamera.py index 82ac6735e..019f19915 100644 --- a/python/mmSolver/_api/solvercamera.py +++ b/python/mmSolver/_api/solvercamera.py @@ -150,8 +150,11 @@ def _compile_camera_solve( assert y.get_state() == const.ATTR_STATE_STATIC assert z.get_state() == const.ATTR_STATE_STATIC + col_node = col.get_node() + func = 'mmSolver._api.solvercamerautils.camera_solve' args = [ + col_node, cam_tfm, cam_shp, mkr_nodes, diff --git a/python/mmSolver/_api/solvercamerautils.py b/python/mmSolver/_api/solvercamerautils.py index 9f6275b21..0e051d6bc 100644 --- a/python/mmSolver/_api/solvercamerautils.py +++ b/python/mmSolver/_api/solvercamerautils.py @@ -226,6 +226,7 @@ def _filter_badly_solved_bundles(mkr_bnd_nodes, value_min, value_max): def _sub_bundle_adjustment( + col_node, cam_tfm, cam_shp, mkr_nodes, @@ -350,20 +351,35 @@ def _sub_bundle_adjustment( # After each pose is added to the camera solve, we must do a # mmSolver refinement with 'MM Scene Graph', solving the # camera position, rotation and bundle positions. - result = maya.cmds.mmSolver( - frame=frames, - solverType=solver_type, - sceneGraphMode=scene_graph_mode, - iterations=iteration_num, - frameSolveMode=frame_solve_mode, - **kwargs - ) - assert result[0] == 'success=1' - + result = None + if const.SOLVER_VERSION_DEFAULT == const.SOLVER_VERSION_ONE: + result = maya.cmds.mmSolver( + frame=frames, + solverType=solver_type, + sceneGraphMode=scene_graph_mode, + iterations=iteration_num, + frameSolveMode=frame_solve_mode, + **kwargs + ) + assert result[0] == 'success=1' + elif const.SOLVER_VERSION_DEFAULT == const.SOLVER_VERSION_TWO: + result = maya.cmds.mmSolver_v2( + frame=frames, + solverType=solver_type, + sceneGraphMode=scene_graph_mode, + iterations=iteration_num, + frameSolveMode=frame_solve_mode, + resultsNode=col_node, + **kwargs + ) + assert result is True + else: + raise NotImplementedError return result def _bundle_adjust( + col_node, cam_tfm, cam_shp, mkr_nodes, @@ -380,6 +396,7 @@ def _bundle_adjust( solver_type=None, ): LOG.debug('_bundle_adjust') + LOG.debug('col_node: %r', col_node) LOG.debug('cam_tfm: %r', cam_tfm) LOG.debug('cam_shp: %r', cam_shp) LOG.debug('mkr_nodes: %r', mkr_nodes) @@ -413,6 +430,7 @@ def _bundle_adjust( result = None if adjust_lens_distortion is False: result = _sub_bundle_adjustment( + col_node, cam_tfm, cam_shp, mkr_nodes, @@ -439,6 +457,7 @@ def _bundle_adjust( # Solve camera transform and bundle positions _sub_bundle_adjustment( + col_node, cam_tfm, cam_shp, mkr_nodes, @@ -457,6 +476,7 @@ def _bundle_adjust( # Solve camera focal length _sub_bundle_adjustment( + col_node, cam_tfm, cam_shp, mkr_nodes, @@ -475,6 +495,7 @@ def _bundle_adjust( # Solve lens distortion _sub_bundle_adjustment( + col_node, cam_tfm, cam_shp, mkr_nodes, @@ -493,6 +514,7 @@ def _bundle_adjust( # Solve camera focal length _sub_bundle_adjustment( + col_node, cam_tfm, cam_shp, mkr_nodes, @@ -511,6 +533,7 @@ def _bundle_adjust( # Solve lens distortion _sub_bundle_adjustment( + col_node, cam_tfm, cam_shp, mkr_nodes, @@ -529,6 +552,7 @@ def _bundle_adjust( # Solve everything result = _sub_bundle_adjustment( + col_node, cam_tfm, cam_shp, mkr_nodes, @@ -549,6 +573,7 @@ def _bundle_adjust( def _solve_relative_poses( + col_node, cam_tfm, cam_shp, mkr_nodes_a, @@ -640,6 +665,7 @@ def _solve_relative_poses( frames = list(sorted(solved_frames)) _bundle_adjust( + col_node, cam_tfm, cam_shp, accumulated_mkr_nodes, @@ -933,6 +959,7 @@ def _precompute_values(mkr_list, root_frames, start_frame, end_frame): # TODO: Make arguments keywords arguments. This will make things # easier to understand for calling code. def camera_solve( + col_node, cam_tfm, cam_shp, mkr_nodes, @@ -949,6 +976,7 @@ def camera_solve( adjust_every_n_poses, solver_type, ): + assert isinstance(col_node, pycompat.TEXT_TYPE) assert isinstance(cam_tfm, pycompat.TEXT_TYPE) assert isinstance(cam_shp, pycompat.TEXT_TYPE) assert isinstance(start_frame, int) @@ -971,6 +999,7 @@ def camera_solve( cam = camera.Camera(shape=cam_shp) mkr_list = [marker.Marker(node=x) for x in mkr_nodes] + LOG.debug('col_node: %s', col_node) LOG.debug('cam: %s', cam) LOG.debug('mkr_list: %s', mkr_list) LOG.debug('cam_shp_node_attrs: %s', cam_shp_node_attrs) @@ -1041,6 +1070,7 @@ def camera_solve( solved_frames, failed_frames, ) = _solve_relative_poses( + col_node, cam_tfm, cam_shp, mkr_nodes_a, @@ -1093,6 +1123,7 @@ def camera_solve( overlapping_frames = list(sorted(overlapping_frames)) _bundle_adjust( + col_node, cam_tfm, cam_shp, [mkr_node], @@ -1117,6 +1148,7 @@ def camera_solve( adjust_mkr_nodes = list(sorted(adjust_mkr_nodes)) _bundle_adjust( + col_node, cam_tfm, cam_shp, adjust_mkr_nodes, @@ -1164,6 +1196,7 @@ def camera_solve( overlapping_frames = list(sorted(overlapping_frames)) _bundle_adjust( + col_node, cam_tfm, cam_shp, [mkr_node], @@ -1188,6 +1221,7 @@ def camera_solve( adjust_mkr_nodes = list(sorted(adjust_mkr_nodes)) _bundle_adjust( + col_node, cam_tfm, cam_shp, adjust_mkr_nodes, @@ -1232,6 +1266,7 @@ def camera_solve( # Solve per-frame. Only animated attributes are solved - bundles # and (static) focal lengths are ignored. result = _bundle_adjust( + col_node, cam_tfm, cam_shp, adjust_mkr_nodes, diff --git a/python/mmSolver/_api/solveresult.py b/python/mmSolver/_api/solveresult.py index a1db3c3d0..593037388 100644 --- a/python/mmSolver/_api/solveresult.py +++ b/python/mmSolver/_api/solveresult.py @@ -27,6 +27,8 @@ import math import datetime +import maya.cmds + import mmSolver.logger import mmSolver.utils.python_compat as pycompat @@ -82,6 +84,7 @@ def _convert_to(name, key, typ, value, index): :returns: A value of 'typ' kind. """ + assert callable(typ) is True msg = 'mmSolver data is incomplete, ' msg += 'a solver error may have occurred: ' msg += 'name={0} key={1} type={2} value={3}' @@ -119,6 +122,261 @@ def _convert_to(name, key, typ, value, index): return v +def _string_get_solver_stats(input_data): + name_keys = [ + ('success', 'success', bool), + ('stop_message', 'reason_string', str), + ('stop_id', 'reason_num', int), + ('iteration_total_calls', 'iteration_num', int), + ('iteration_function_calls', 'iteration_function_num', int), + ('iteration_jacobian_calls', 'iteration_jacobian_num', int), + ('attempts', 'iteration_attempt_num', int), + ('user_interrupted', 'user_interrupted', bool), + ] + index = 0 + solver_stats = {} + for name, key, typ in name_keys: + value = input_data.get(key) + v = _convert_to(name, key, typ, value, index) + solver_stats[name] = v + return solver_stats + + +def _string_get_error_stats(input_data): + name_keys = [ + ('initial', 'error_initial', float), + ('maximum', 'error_maximum', float), + ('final', 'error_final', float), + ('final_average', 'error_final_average', float), + ('final_maximum', 'error_final_maximum', float), + ('final_minimum', 'error_final_minimum', float), + ('jt', 'error_jt', float), + ('dp', 'error_dp', float), + ] + index = 0 + error_stats = {} + for name, key, typ in name_keys: + value = input_data.get(key) + v = _convert_to(name, key, typ, value, index) + error_stats[name] = v + return error_stats + + +def _string_get_timer_stats(input_data): + name_keys = [ + ('solve_seconds', 'timer_solve', float), + ('function_seconds', 'timer_function', float), + ('jacobian_seconds', 'timer_jacobian', float), + ('parameter_seconds', 'timer_parameter', float), + ('error_seconds', 'timer_error', float), + ('solve_ticks', 'ticks_solve', int), + ('function_ticks', 'ticks_function', int), + ('jacobian_ticks', 'ticks_jacobian', int), + ('parameter_ticks', 'ticks_parameter', int), + ('error_ticks', 'ticks_error', int), + ] + index = 0 + timer_stats = {} + for name, key, typ in name_keys: + value = input_data.get(key) + v = _convert_to(name, key, typ, value, index) + timer_stats[name] = v + return timer_stats + + +def _string_get_print_stats(input_data): + name_keys = [ + ('number_of_parameters', 'numberOfParameters', int), + ('number_of_errors', 'numberOfErrors', int), + ] + index = 0 + print_stats = {} + for name, key, typ in name_keys: + value = input_data.get(key) + v = _convert_to(name, key, typ, value, index) + print_stats[name] = v + return print_stats + + +def _string_get_error_per_frame(input_data): + # Common warning message in this method. + msg = 'mmSolver data is incomplete, ' + msg += 'a solver error may have occurred: ' + msg += 'name={0} key={1} type={2} value={3}' + + # Errors per frame + # Allows graphing the errors and detecting problems. + per_frame_error = {} + name = '' + key = 'error_per_frame' + values = input_data.get(key) + if values is None or len(values) == 0: + LOG.debug(msg.format(name, key, 'None', values)) + else: + for value in values: + t = _convert_to(name, key, float, value, 0) + v = _convert_to(name, key, float, value, 1) + per_frame_error[t] = v + return per_frame_error + + +def _string_get_error_per_marker_per_frame(input_data): + # Common warning message in this method. + msg = 'mmSolver data is incomplete, ' + msg += 'a solver error may have occurred: ' + msg += 'name={0} key={1} type={2} value={3}' + + # List of errors, per-marker, per-frame. + # Allows graphing the errors and detecting problems. + per_marker_per_frame_error = collections.defaultdict(dict) + key = 'error_per_marker_per_frame' + values = input_data.get(key) + name = '' + if values is None or len(values) == 0: + LOG.debug(msg.format(name, key, 'None', values)) + else: + for value in values: + mkr = _convert_to(name, key, str, value, 0) + t = _convert_to(name, key, float, value, 1) + v = _convert_to(name, key, float, value, 2) + per_marker_per_frame_error[mkr][t] = v + + return per_marker_per_frame_error + + +def _get_maya_attr_anim_curve(node, attr_name, existing_attrs): + if attr_name not in existing_attrs: + return None + plug = '{}.{}'.format(node, attr_name) + anim_curves = maya.cmds.listConnections(plug, type='animCurve') or [] + if len(anim_curves) == 0: + return None + return anim_curves[0] + + +def _get_node_frame_error_list(node, attr_name, existing_attrs): + anim_curve = _get_maya_attr_anim_curve(node, attr_name, existing_attrs) + if anim_curve is None: + return {} + + keyframe_times = maya.cmds.keyframe(anim_curve, query=True, timeChange=True) or [] + times_and_values = {} + for keyframe_time in keyframe_times: + time = (keyframe_time, keyframe_time) + value = maya.cmds.keyframe(anim_curve, query=True, eval=True, time=time)[0] + if value > 0.0: + times_and_values[keyframe_time] = value + return times_and_values + + +def _get_maya_attr(node, attr_name, typ, existing_attrs): + if attr_name not in existing_attrs: + return typ() + plug = '{}.{}'.format(node, attr_name) + return typ(maya.cmds.getAttr(plug)) + + +def _node_get_error_stats(node, existing_attrs): + assert maya.cmds.objExists(node) is True + data = { + 'initial': _get_maya_attr(node, 'error_initial', float, existing_attrs), + 'maximum': _get_maya_attr(node, 'error_maximum', float, existing_attrs), + 'final': _get_maya_attr(node, 'error_final', float, existing_attrs), + 'final_average': _get_maya_attr( + node, 'error_final_average', float, existing_attrs + ), + 'final_maximum': _get_maya_attr( + node, 'error_final_maximum', float, existing_attrs + ), + 'final_minimum': _get_maya_attr( + node, 'error_final_minimum', float, existing_attrs + ), + 'jt': _get_maya_attr(node, 'error_jt', float, existing_attrs), + 'dp': _get_maya_attr(node, 'error_dp', float, existing_attrs), + } + return data + + +def _node_get_timer_stats(node, existing_attrs): + assert maya.cmds.objExists(node) is True + data = { + 'solve_seconds': _get_maya_attr(node, 'timer_solve', float, existing_attrs), + 'function_seconds': _get_maya_attr( + node, 'timer_function', float, existing_attrs + ), + 'jacobian_seconds': _get_maya_attr( + node, 'timer_jacobian', float, existing_attrs + ), + 'parameter_seconds': _get_maya_attr( + node, 'timer_parameter', float, existing_attrs + ), + 'error_seconds': _get_maya_attr(node, 'timer_error', float, existing_attrs), + 'solve_ticks': _get_maya_attr(node, 'ticks_solve', int, existing_attrs), + 'function_ticks': _get_maya_attr(node, 'ticks_function', int, existing_attrs), + 'jacobian_ticks': _get_maya_attr(node, 'ticks_jacobian', int, existing_attrs), + 'parameter_ticks': _get_maya_attr(node, 'ticks_parameter', int, existing_attrs), + 'error_ticks': _get_maya_attr(node, 'ticks_error', int, existing_attrs), + } + return data + + +def _node_get_solver_stats(node, existing_attrs): + assert maya.cmds.objExists(node) is True + data = { + 'success': _get_maya_attr(node, 'success', bool, existing_attrs), + 'stop_message': _get_maya_attr(node, 'reason_string', str, existing_attrs), + 'stop_id': _get_maya_attr(node, 'reason_num', int, existing_attrs), + 'iteration_total_calls': _get_maya_attr( + node, 'iteration_num', int, existing_attrs + ), + 'iteration_function_calls': _get_maya_attr( + node, 'iteration_function_num', int, existing_attrs + ), + 'iteration_jacobian_calls': _get_maya_attr( + node, 'iteration_jacobian_num', int, existing_attrs + ), + 'attempts': _get_maya_attr(node, 'iteration_attempt_num', int, existing_attrs), + 'user_interrupted': _get_maya_attr( + node, 'user_interrupted', int, existing_attrs + ), + } + return data + + +def _node_get_print_stats(node, existing_attrs): + assert maya.cmds.objExists(node) is True + data = { + 'number_of_parameters': _get_maya_attr( + node, 'numberOfParameters', int, existing_attrs + ), + 'number_of_errors': _get_maya_attr(node, 'numberOfErrors', int, existing_attrs), + } + return data + + +def _node_get_per_frame_error(node, existing_attrs): + return _get_node_frame_error_list(node, 'deviation', existing_attrs) + + +def _node_get_per_marker_per_frame_error(node, existing_attrs): + assert maya.cmds.objExists(node) is True + + # Get all Marker nodes + marker_attrs = [x for x in existing_attrs if x.startswith("mkr___")] + marker_names = set([x.split('___')[1] for x in marker_attrs]) + marker_nodes = [x for x in marker_names if maya.cmds.objExists(x) is True] + marker_nodes = [maya.cmds.ls(x, long=True) or [] for x in marker_nodes] + marker_nodes = set([x[0] for x in marker_nodes if len(x) > 0]) + + data = collections.defaultdict(dict) + for mkr_node in marker_nodes: + mkr_existing_attrs = maya.cmds.listAttr(mkr_node) or [] + data[mkr_node] = _get_node_frame_error_list( + mkr_node, 'deviation', mkr_existing_attrs + ) + return data + + class SolveResult(object): """ The information returned from a solve. @@ -128,7 +386,7 @@ class SolveResult(object): data. """ - def __init__(self, cmd_data): + def __init__(self, *args, **kwargs): """ Create a new SolveResult using command data from *maya.cmds.mmSolver* command. @@ -136,113 +394,35 @@ def __init__(self, cmd_data): :param cmd_data: Command data from mmSolver. :type cmd_data: [[str, ..], ..] """ - if isinstance(cmd_data, list) is False: - msg = 'cmd_data is of type %r, expected a list object.' - raise TypeError(msg % type(cmd_data)) - self._raw_data = list(cmd_data) - data = parse_command_result(cmd_data) - - # Common warning message in this method. - msg = 'mmSolver data is incomplete, ' - msg += 'a solver error may have occurred: ' - msg += 'name={0} key={1} type={2} value={3}' - - # Solver statistics - name_keys = [ - ('success', 'success', bool), - ('stop_message', 'reason_string', str), - ('stop_id', 'reason_num', int), - ('iteration_total_calls', 'iteration_num', int), - ('iteration_function_calls', 'iteration_function_num', int), - ('iteration_jacobian_calls', 'iteration_jacobian_num', int), - ('attempts', 'iteration_attempt_num', int), - ('user_interrupted', 'user_interrupted', bool), - ] - index = 0 - self._solver_stats = {} - for name, key, typ in name_keys: - value = data.get(key) - v = _convert_to(name, key, typ, value, index) - self._solver_stats[name] = v - - # Error statistics - name_keys = [ - ('initial', 'error_initial', float), - ('maximum', 'error_maximum', float), - ('final', 'error_final', float), - ('final_average', 'error_final_average', float), - ('final_maximum', 'error_final_maximum', float), - ('final_minimum', 'error_final_minimum', float), - ('jt', 'error_jt', float), - ('dp', 'error_dp', float), - ] - index = 0 - self._error_stats = {} - for name, key, typ in name_keys: - value = data.get(key) - v = _convert_to(name, key, typ, value, index) - self._error_stats[name] = v - - # Timer statistics - name_keys = [ - ('solve_seconds', 'timer_solve', float), - ('function_seconds', 'timer_function', float), - ('jacobian_seconds', 'timer_jacobian', float), - ('parameter_seconds', 'timer_parameter', float), - ('error_seconds', 'timer_error', float), - ('solve_ticks', 'ticks_solve', int), - ('function_ticks', 'ticks_function', int), - ('jacobian_ticks', 'ticks_jacobian', int), - ('parameter_ticks', 'ticks_parameter', int), - ('error_ticks', 'ticks_error', int), - ] - index = 0 - self._timer_stats = {} - for name, key, typ in name_keys: - value = data.get(key) - v = _convert_to(name, key, typ, value, index) - self._timer_stats[name] = v - - # Print statistics - name_keys = [ - ('number_of_parameters', 'numberOfParameters', int), - ('number_of_errors', 'numberOfErrors', int), - ] - index = 0 - self._print_stats = {} - for name, key, typ in name_keys: - value = data.get(key) - v = _convert_to(name, key, typ, value, index) - self._print_stats[name] = v - - # List of errors, per-marker, per-frame. - # Allows graphing the errors and detecting problems. - self._per_marker_per_frame_error = collections.defaultdict(dict) - key = 'error_per_marker_per_frame' - values = data.get(key) - name = '' - if values is None or len(values) == 0: - LOG.debug(msg.format(name, key, 'None', values)) - else: - for value in values: - mkr = _convert_to(name, key, str, value, 0) - t = _convert_to(name, key, float, value, 1) - v = _convert_to(name, key, float, value, 2) - self._per_marker_per_frame_error[mkr][t] = v - - # Errors per frame - # Allows graphing the errors and detecting problems. - self._per_frame_error = {} - name = '' - key = 'error_per_frame' - values = data.get(key) - if values is None or len(values) == 0: - LOG.debug(msg.format(name, key, 'None', values)) + if isinstance(args[0], pycompat.TEXT_TYPE) is True: + self._input_mode = 0 + node = args[0] + existing_attrs = maya.cmds.listAttr(node) + self._solver_stats = _node_get_solver_stats(node, existing_attrs) + self._error_stats = _node_get_error_stats(node, existing_attrs) + self._timer_stats = _node_get_timer_stats(node, existing_attrs) + self._print_stats = _node_get_print_stats(node, existing_attrs) + + self._per_frame_error = _node_get_per_frame_error(node, existing_attrs) + self._per_marker_per_frame_error = _node_get_per_marker_per_frame_error( + node, existing_attrs + ) + elif isinstance(args[0], list) is True: + self._input_mode = 1 + cmd_data = args[0] + self._raw_data = list(cmd_data) + data = parse_command_result(cmd_data) + self._solver_stats = _string_get_solver_stats(data) + self._error_stats = _string_get_error_stats(data) + self._timer_stats = _string_get_timer_stats(data) + self._print_stats = _string_get_print_stats(data) + self._per_marker_per_frame_error = _string_get_error_per_marker_per_frame( + data + ) + self._per_frame_error = _string_get_error_per_frame(data) else: - for value in values: - t = _convert_to(name, key, float, value, 0) - v = _convert_to(name, key, float, value, 1) - self._per_frame_error[t] = v + msg = 'argument is of type %r, expected a list or str.' + raise TypeError(msg % type(args[0])) return def get_data_raw(self): @@ -252,7 +432,26 @@ def get_data_raw(self): It is possible to re-create this object exactly by saving this raw data and re-initializing the object with this data. """ - return list(self._raw_data) + if self._input_mode == 1: + return list(self._raw_data) + elif self._input_mode == 0: + error_stats = self.get_error_stats() + timer_stats = self.get_timer_stats() + solver_stats = self.get_solver_stats() + print_stats = self.get_print_stats() + per_frame_error = self.get_frame_error_list() + per_marker_per_frame_error = self.get_marker_error_list(marker_node=None) + data = { + 'error_stats': error_stats, + 'timer_stats': timer_stats, + 'solver_stats': solver_stats, + 'print_stats': print_stats, + 'per_frame_error': per_frame_error, + 'per_marker_per_frame_error': per_marker_per_frame_error, + } + return data + else: + raise NotImplementedError def get_success(self): """ diff --git a/python/mmSolver/_api/solverstep.py b/python/mmSolver/_api/solverstep.py index be2080338..c03145ea8 100644 --- a/python/mmSolver/_api/solverstep.py +++ b/python/mmSolver/_api/solverstep.py @@ -486,6 +486,27 @@ def clear_frame_list(self): ########################################## + def get_solver_version(self): + """ + :rtype: int or None + """ + return self._data.get('solver_version', const.SOLVER_VERSION_DEFAULT) + + def set_solver_version(self, value): + """ + :param value: May be SOLVER_VERSION_ONE or SOLVER_VERSION_TWO. + """ + if isinstance(value, int) is False: + raise TypeError('Expected int value type, got %r.' % type(value)) + if value not in const.SOLVER_VERSION_LIST: + raise TypeError( + 'Expected value in list %r, got %r.' + % (const.SOLVER_VERSION_LIST, value) + ) + self._data['solver_version'] = value + + ########################################## + def compile(self, col, mkr_list, attr_list, withtest=False): """ Compiles data given into flags for a single run of 'mmSolver'. @@ -513,7 +534,18 @@ def compile(self, col, mkr_list, attr_list, withtest=False): assert isinstance(attr_list, list) assert self.get_frame_list_length() > 0 - func = 'maya.cmds.mmSolver' + solver_version = self.get_solver_version() + is_solver_v1 = solver_version == 1 + is_solver_v2 = solver_version == 2 + if is_solver_v1 is True: + func = 'maya.cmds.mmSolver' + elif is_solver_v2 is True: + func = 'maya.cmds.mmSolver_v2' + else: + raise NotImplementedError( + 'solver_version has invalid value: %r' % solver_version + ) + args = [] kwargs = dict() kwargs['camera'] = [] @@ -525,12 +557,13 @@ def compile(self, col, mkr_list, attr_list, withtest=False): precomputed_data = self.get_precomputed_data() mkr_state_values = precomputed_data.get(solverbase.MARKER_STATIC_VALUES_KEY) attr_state_values = precomputed_data.get(solverbase.ATTR_STATIC_VALUES_KEY) - attr_stiff_state_values = precomputed_data.get( - solverbase.ATTR_STIFFNESS_STATIC_VALUES_KEY - ) - attr_smooth_state_values = precomputed_data.get( - solverbase.ATTR_SMOOTHNESS_STATIC_VALUES_KEY - ) + if is_solver_v1 is True: + attr_stiff_state_values = precomputed_data.get( + solverbase.ATTR_STIFFNESS_STATIC_VALUES_KEY + ) + attr_smooth_state_values = precomputed_data.get( + solverbase.ATTR_SMOOTHNESS_STATIC_VALUES_KEY + ) # Get Markers and Cameras markers, cameras = api_compile.markersAndCameras_compile_flags( @@ -562,25 +595,27 @@ def compile(self, col, mkr_list, attr_list, withtest=False): # Stiffness Attribute Flags stiff_flags = None - use_stiffness = self.get_use_stiffness() - if use_stiffness is True: - stiff_flags = api_compile.attr_stiffness_compile_flags( - col, - attr_list, - attr_static_values=attr_state_values, - attr_stiff_static_values=attr_stiff_state_values, - ) + if is_solver_v1 is True: + use_stiffness = self.get_use_stiffness() + if use_stiffness is True: + stiff_flags = api_compile.attr_stiffness_compile_flags( + col, + attr_list, + attr_static_values=attr_state_values, + attr_stiff_static_values=attr_stiff_state_values, + ) # Smoothness Attribute Flags smooth_flags = None - use_smoothness = self.get_use_smoothness() - if use_smoothness is True: - smooth_flags = api_compile.attr_smoothness_compile_flags( - col, - attr_list, - attr_static_values=attr_state_values, - attr_smooth_static_values=attr_smooth_state_values, - ) + if is_solver_v1 is True: + use_smoothness = self.get_use_smoothness() + if use_smoothness is True: + smooth_flags = api_compile.attr_smoothness_compile_flags( + col, + attr_list, + attr_static_values=attr_state_values, + attr_smooth_static_values=attr_smooth_state_values, + ) # Get Frames frm_list = self.get_frame_list() @@ -667,20 +702,29 @@ def compile(self, col, mkr_list, attr_list, withtest=False): kwargs['timeEvalMode'] = self.get_time_eval_mode() - value = self.get_remove_unused_markers() - if value is not None: - kwargs['removeUnusedMarkers'] = value + if is_solver_v1 is True: + value = self.get_remove_unused_markers() + if value is not None: + kwargs['removeUnusedMarkers'] = value - value = self.get_remove_unused_attributes() - if value is not None: - kwargs['removeUnusedAttributes'] = value + value = self.get_remove_unused_attributes() + if value is not None: + kwargs['removeUnusedAttributes'] = value + + if is_solver_v2 is True: + key = 'verbose' + if key in kwargs: + del kwargs[key] action = api_action.Action(func=func, args=args, kwargs=kwargs) # Check the inputs and outputs are valid. vaction = None if withtest is True: - assert api_action.action_func_is_mmSolver(action) is True + is_mmsolver_v1 = api_action.action_func_is_mmSolver_v1(action) + is_mmsolver_v2 = api_action.action_func_is_mmSolver_v2(action) + assert any((is_mmsolver_v1, is_mmsolver_v2)) + vfunc = func vargs = list(args) vkwargs = kwargs.copy() @@ -694,4 +738,5 @@ def compile(self, col, mkr_list, attr_list, withtest=False): yield action, vaction +# For backwards compatibility. Solver = SolverStep diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 556bfdc42..305731ef9 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -31,6 +31,9 @@ set(SOURCE_FILES mmSolver/adjust/adjust_cminpack_lmder.cpp mmSolver/adjust/adjust_cminpack_lmdif.cpp mmSolver/adjust/adjust_relationships.cpp + mmSolver/adjust/adjust_results_helpers.cpp + mmSolver/adjust/adjust_results_setMarkerData.cpp + mmSolver/adjust/adjust_results_setSolveData.cpp mmSolver/adjust/adjust_setParameters.cpp mmSolver/adjust/adjust_measureErrors.cpp mmSolver/adjust/adjust_solveFunc.cpp diff --git a/src/mmSolver/adjust/adjust_base.cpp b/src/mmSolver/adjust/adjust_base.cpp index e94ff2849..6b76d7a58 100644 --- a/src/mmSolver/adjust/adjust_base.cpp +++ b/src/mmSolver/adjust/adjust_base.cpp @@ -687,16 +687,17 @@ MStatus splitUsedMarkersAndAttributes(const MarkerPtrList &markerList, } PrintStatOptions constructPrintStats(const MStringArray &printStatsList) { - auto printStats = PrintStatOptions{ - false, // doNotSolve - false, // input - false, // affects - false, // usedSolveObjects - false // deviation - }; + auto printStats = PrintStatOptions(); + printStats.doNotSolve = false; + printStats.input = false; + printStats.affects = false; + printStats.usedSolveObjects = false; + printStats.deviation = false; + if (printStatsList.length() == 0) { return printStats; } + for (uint32_t i = 0; i < printStatsList.length(); ++i) { if (printStatsList[i] == PRINT_STATS_MODE_INPUTS) { printStats.doNotSolve = true; @@ -788,21 +789,17 @@ MStatus solveFrames( SmoothAttrsPtrList &smoothAttrsList, const BoolList2D &markerToAttrList, SolverOptions &solverOptions, // - const PrintStatOptions &printStats, - const MGlobal::MMayaState &mayaSessionState, - // - MDGModifier &out_dgmod, MAnimCurveChange &out_curveChange, + const MGlobal::MMayaState &mayaSessionState, MDGModifier &out_dgmod, + MAnimCurveChange &out_curveChange, MComputation &out_computation, // - MComputation &out_computation, const LogLevel &logLevel, - // - IndexPairList &out_paramToAttrList, IndexPairList &out_errorToMarkerList, + std::vector &out_jacobianList, IndexPairList &out_paramToAttrList, + IndexPairList &out_errorToMarkerList, std::vector &out_markerPosList, std::vector &out_markerWeightList, std::vector &out_errorList, std::vector &out_paramList, std::vector &out_previousParamList, - std::vector &out_jacobianList, // - CommandResult &out_cmdResult) { + const LogLevel &logLevel, CommandResult &out_cmdResult) { MStatus status = MS::kSuccess; out_cmdResult.solverResult.success = true; @@ -871,8 +868,8 @@ MStatus solveFrames( errorToParamList, status); CHECK_MSTATUS_AND_RETURN_IT(status); - if (printStats.input) { - assert(printStats.doNotSolve); + if (out_cmdResult.printStats.input) { + assert(out_cmdResult.printStats.doNotSolve); status = logResultsObjectCounts( numberOfParameters, numberOfErrors, numberOfMarkerErrors, numberOfAttrStiffnessErrors, numberOfAttrSmoothnessErrors, @@ -880,16 +877,16 @@ MStatus solveFrames( CHECK_MSTATUS_AND_RETURN_IT(status); } - if (printStats.usedSolveObjects) { - assert(printStats.doNotSolve); + if (out_cmdResult.printStats.usedSolveObjects) { + assert(out_cmdResult.printStats.doNotSolve); status = logResultsSolveObjectUsage( usedMarkerList, unusedMarkerList, usedAttrList, unusedAttrList, out_cmdResult.solverObjectUsageResult); CHECK_MSTATUS_AND_RETURN_IT(status); } - if (printStats.affects) { - assert(printStats.doNotSolve); + if (out_cmdResult.printStats.affects) { + assert(out_cmdResult.printStats.doNotSolve); status = logResultsMarkerAffectsAttribute(usedMarkerList, usedAttrList, markerToAttrList, out_cmdResult.affectsResult); @@ -916,7 +913,7 @@ MStatus solveFrames( // Bail out of solve if we don't have enough used markers or // attributes. if ((usedMarkerList.empty()) || (usedAttrList.empty())) { - if (printStats.doNotSolve) { + if (out_cmdResult.printStats.doNotSolve) { // If the user is asking to print statistics, then we have // successfully achieved that goal and we cannot continue // to generate statistics, because not enough markers or @@ -936,7 +933,7 @@ MStatus solveFrames( } if (numberOfParameters > numberOfErrors) { - if (printStats.doNotSolve) { + if (out_cmdResult.printStats.doNotSolve) { // If the user is asking to print statistics, then we have // successfully achieved that goal and we cannot continue // to generate statistics, because of an invalid number of @@ -989,7 +986,8 @@ MStatus solveFrames( std::stringstream ss; addFrameListToStringStream(frameList, ss); MMSOLVER_INFO("Frames:" << ss.str()); - } else if (!printStats.doNotSolve && (logLevel >= LogLevel::kVerbose)) { + } else if (!out_cmdResult.printStats.doNotSolve && + (logLevel >= LogLevel::kVerbose)) { MMSOLVER_INFO( "------------------------------------------------------------------" "-------------"); @@ -1004,7 +1002,7 @@ MStatus solveFrames( } // MComputation helper. - if (!printStats.doNotSolve && (logLevel >= LogLevel::kInfo) && + if (!out_cmdResult.printStats.doNotSolve && (logLevel >= LogLevel::kInfo) && (frameCount > 1)) { const bool showProgressBar = true; const bool isInterruptable = true; @@ -1017,7 +1015,7 @@ MStatus solveFrames( // Start Solving SolverTimer timer; timer.startTimestamp = debug::get_timestamp(); - if (!printStats.doNotSolve) { + if (!out_cmdResult.printStats.doNotSolve) { timer.solveBenchTimer.start(); timer.solveBenchTicks.start(); } @@ -1147,7 +1145,7 @@ MStatus solveFrames( double initialErrorAvg = 0.0; double initialErrorMin = std::numeric_limits::max(); double initialErrorMax = -0.0; - if (solverOptions.acceptOnlyBetter || printStats.deviation) { + if (solverOptions.acceptOnlyBetter || out_cmdResult.printStats.deviation) { std::vector frameIndexEnable(frameList.length(), 1); std::vector skipErrorMeasurements(numberOfErrors, 1); measureErrors(numberOfErrors, numberOfMarkerErrors, @@ -1184,7 +1182,7 @@ MStatus solveFrames( out_cmdResult.solverResult.errorMin = initialErrorMin; out_cmdResult.solverResult.errorMax = initialErrorMax; - if (printStats.doNotSolve) { + if (out_cmdResult.printStats.doNotSolve) { logResultsErrorMetrics(numberOfMarkerErrors, userData.markerList, userData.frameList, userData.errorToMarkerList, userData.errorDistanceList, @@ -1257,11 +1255,11 @@ MStatus solveFrames( return status; } - if (!printStats.doNotSolve) { + if (!out_cmdResult.printStats.doNotSolve) { timer.solveBenchTicks.stop(); timer.solveBenchTimer.stop(); } - if (!printStats.doNotSolve && (logLevel >= LogLevel::kInfo) && + if (!out_cmdResult.printStats.doNotSolve && (logLevel >= LogLevel::kInfo) && (frameCount > 1)) { out_computation.endComputation(); } @@ -1372,9 +1370,11 @@ bool solve_v1(SolverOptions &solverOptions, CameraPtrList &cameraList, MStringArray &outResult) { MStatus status = MS::kSuccess; + CommandResult cmdResult; + bool verbose = logLevel >= LogLevel::kDebug; - auto printStats = constructPrintStats(printStatsList); - if (printStats.doNotSolve) { + cmdResult.printStats = constructPrintStats(printStatsList); + if (cmdResult.printStats.doNotSolve) { // When printing statistics, turn off verbosity. verbose = false; } @@ -1478,9 +1478,6 @@ bool solve_v1(SolverOptions &solverOptions, CameraPtrList &cameraList, MGlobal::MMayaState mayaSessionState = MGlobal::mayaState(&status); CHECK_MSTATUS_AND_RETURN_IT(status); - CommandResult cmdResult; - cmdResult.printStats = printStats; - auto frameSolveMode = solverOptions.frameSolveMode; MMSOLVER_VRB("frameSolveMode: " << static_cast(frameSolveMode)); if (frameSolveMode == FrameSolveMode::kAllFrameAtOnce) { @@ -1489,20 +1486,16 @@ bool solve_v1(SolverOptions &solverOptions, CameraPtrList &cameraList, usedAttrList, unusedAttrList, stiffAttrsList, smoothAttrsList, markerToAttrList, solverOptions, // - printStats, mayaSessionState, - // - dgmod, curveChange, + mayaSessionState, dgmod, curveChange, computation, // - computation, logLevel, + jacobianList, paramToAttrList, errorToMarkerList, markerPosList, + markerWeightList, errorList, paramList, previousParamList, // - paramToAttrList, errorToMarkerList, markerPosList, markerWeightList, - errorList, paramList, previousParamList, jacobianList, - // - cmdResult); + logLevel, cmdResult); } else if (frameSolveMode == FrameSolveMode::kPerFrame) { auto frameCount = frameList.length(); - if (!printStats.doNotSolve) { + if (!cmdResult.printStats.doNotSolve) { const bool showProgressBar = true; const bool isInterruptable = true; const bool useWaitCursor = true; @@ -1524,25 +1517,20 @@ bool solve_v1(SolverOptions &solverOptions, CameraPtrList &cameraList, computation.setProgress(i); CommandResult perFrameCmdResult; - perFrameCmdResult.printStats = printStats; + perFrameCmdResult.printStats = cmdResult.printStats; auto frames = MTimeArray(1, frameList[i]); - status = solveFrames(cameraList, bundleList, frames, usedMarkerList, - unusedMarkerList, usedAttrList, unusedAttrList, - stiffAttrsList, smoothAttrsList, - markerToAttrList, solverOptions, - // - printStats, mayaSessionState, - // - dgmod, curveChange, - // - computation, perFrameLogLevel, - // - paramToAttrList, errorToMarkerList, - markerPosList, markerWeightList, errorList, - paramList, previousParamList, jacobianList, - // - perFrameCmdResult); + status = solveFrames( + cameraList, bundleList, frames, usedMarkerList, + unusedMarkerList, usedAttrList, unusedAttrList, stiffAttrsList, + smoothAttrsList, markerToAttrList, solverOptions, + // + mayaSessionState, dgmod, curveChange, computation, + // + jacobianList, paramToAttrList, errorToMarkerList, markerPosList, + markerWeightList, errorList, paramList, previousParamList, + // + perFrameLogLevel, perFrameCmdResult); // Combine results from each iteration. cmdResult.add(perFrameCmdResult); @@ -1573,8 +1561,8 @@ bool solve_v2(SolverOptions &solverOptions, CameraPtrList &cameraList, MStatus status = MS::kSuccess; bool verbose = logLevel >= LogLevel::kDebug; - auto printStats = constructPrintStats(printStatsList); - if (printStats.doNotSolve) { + out_cmdResult.printStats = constructPrintStats(printStatsList); + if (out_cmdResult.printStats.doNotSolve) { // When printing statistics, turn off verbosity. verbose = false; } @@ -1689,20 +1677,16 @@ bool solve_v2(SolverOptions &solverOptions, CameraPtrList &cameraList, usedAttrList, unusedAttrList, stiffAttrsList, smoothAttrsList, markerToAttrList, solverOptions, // - printStats, mayaSessionState, - // - dgmod, curveChange, - // - computation, logLevel, + mayaSessionState, dgmod, curveChange, computation, // - paramToAttrList, errorToMarkerList, markerPosList, markerWeightList, - errorList, paramList, previousParamList, jacobianList, + jacobianList, paramToAttrList, errorToMarkerList, markerPosList, + markerWeightList, errorList, paramList, previousParamList, // - out_cmdResult); + logLevel, out_cmdResult); } else if (frameSolveMode == FrameSolveMode::kPerFrame) { auto frameCount = frameList.length(); - if (!printStats.doNotSolve) { + if (!out_cmdResult.printStats.doNotSolve) { const bool showProgressBar = true; const bool isInterruptable = true; const bool useWaitCursor = true; @@ -1724,25 +1708,20 @@ bool solve_v2(SolverOptions &solverOptions, CameraPtrList &cameraList, computation.setProgress(i); CommandResult perFrameCmdResult; - perFrameCmdResult.printStats = printStats; + perFrameCmdResult.printStats = out_cmdResult.printStats; auto frames = MTimeArray(1, frameList[i]); - status = solveFrames(cameraList, bundleList, frames, usedMarkerList, - unusedMarkerList, usedAttrList, unusedAttrList, - stiffAttrsList, smoothAttrsList, - markerToAttrList, solverOptions, - // - printStats, mayaSessionState, - // - dgmod, curveChange, - // - computation, perFrameLogLevel, - // - paramToAttrList, errorToMarkerList, - markerPosList, markerWeightList, errorList, - paramList, previousParamList, jacobianList, - // - perFrameCmdResult); + status = solveFrames( + cameraList, bundleList, frames, usedMarkerList, + unusedMarkerList, usedAttrList, unusedAttrList, stiffAttrsList, + smoothAttrsList, markerToAttrList, solverOptions, + // + mayaSessionState, dgmod, curveChange, computation, jacobianList, + // + paramToAttrList, errorToMarkerList, markerPosList, + markerWeightList, errorList, paramList, previousParamList, + // + perFrameLogLevel, perFrameCmdResult); // Combine results from each iteration. out_cmdResult.add(perFrameCmdResult); diff --git a/src/mmSolver/adjust/adjust_data.h b/src/mmSolver/adjust/adjust_data.h index 3a48553fa..3e100c7f5 100644 --- a/src/mmSolver/adjust/adjust_data.h +++ b/src/mmSolver/adjust/adjust_data.h @@ -117,6 +117,13 @@ struct PrintStatOptions { // Print deviation details; the individual error values reported // from the solver, per-frame and per-marker-per-frame. bool deviation; + + PrintStatOptions() + : doNotSolve(false) + , input(false) + , affects(false) + , usedSolveObjects(false) + , deviation(false) {} }; struct SolverOptions { @@ -147,6 +154,30 @@ struct SolverOptions { bool solverSupportsAutoDiffCentral; bool solverSupportsParameterBounds; bool solverSupportsRobustLoss; + + SolverOptions() + : iterMax(0) + , tau(0.0) + , eps1(0.0) + , eps2(0.0) + , eps3(0.0) + , delta(0.0) + , autoDiffType(AUTO_DIFF_TYPE_FORWARD) + , autoParamScale(0) + , robustLossType(ROBUST_LOSS_TYPE_TRIVIAL) + , robustLossScale(1.0) + , sceneGraphMode(SceneGraphMode::kMayaDag) + , solverType(SOLVER_TYPE_DEFAULT_VALUE) + , timeEvalMode(TIME_EVAL_MODE_DG_CONTEXT) + , acceptOnlyBetter(false) + , imageWidth(1.0) + , frameSolveMode(FrameSolveMode::kAllFrameAtOnce) + , removeUnusedMarkers(false) + , removeUnusedAttributes(false) + , solverSupportsAutoDiffForward(false) + , solverSupportsAutoDiffCentral(false) + , solverSupportsParameterBounds(false) + , solverSupportsRobustLoss(false) {} }; // The user data given to the solve function. @@ -221,6 +252,8 @@ struct SolverData { MGlobal::MMayaState mayaSessionState; LogLevel logLevel; + + SolverData() = default; }; #endif // MM_SOLVER_CORE_BUNDLE_ADJUST_DATA_H diff --git a/src/mmSolver/adjust/adjust_results_helpers.cpp b/src/mmSolver/adjust/adjust_results_helpers.cpp new file mode 100644 index 000000000..9f17a089f --- /dev/null +++ b/src/mmSolver/adjust/adjust_results_helpers.cpp @@ -0,0 +1,290 @@ +/* + * Copyright (C) 2022 David Cattermole. + * + * This file is part of mmSolver. + * + * mmSolver is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * mmSolver 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with mmSolver. If not, see . + * ==================================================================== + * + * Common functions used to set result data structures. + */ + +#include "adjust_results_helpers.h" + +// Maya +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// MM Solver +#include "adjust_data.h" +#include "mmSolver/utilities/debug_utils.h" + +MStatus create_attr_deviation(const char *attr_name, MObject &node, + MDGModifier &dgmod) { + MStatus status = MS::kSuccess; + MFnNumericAttribute numeric_attr; + status = create_numeric_attr(attr_name, node, dgmod, numeric_attr, + MFnNumericData::kDouble); + numeric_attr.setKeyable(true); + numeric_attr.setMin(-1.0); + numeric_attr.setDefault(-1.0); + CHECK_MSTATUS_AND_RETURN_IT(status); + return status; +} + +MStatus create_attr_averageDeviation(const char *attr_name, MObject &node, + MDGModifier &dgmod) { + return create_attr_deviation(attr_name, node, dgmod); +} + +MStatus create_attr_maximumDeviation(const char *attr_name, MObject &node, + MDGModifier &dgmod) { + return create_attr_deviation(attr_name, node, dgmod); +} + +MStatus create_attr_maxDeviationFrame(const char *attr_name, MObject &node, + MDGModifier &dgmod) { + MStatus status = MS::kSuccess; + MFnNumericAttribute numeric_attr; + status = create_numeric_attr(attr_name, node, dgmod, numeric_attr, + MFnNumericData::kLong); + numeric_attr.setKeyable(true); + numeric_attr.setMin(-1); + numeric_attr.setDefault(-1); + CHECK_MSTATUS_AND_RETURN_IT(status); + return status; +} + +typedef std::unordered_multimap MultiMapDoubleDouble; + +MStatus create_deviation_attrs_on_node( + MObject &node, const MultiMapDoubleDouble &map, + const char *deviation_attr_name, const char *deviation_avg_attr_name, + const char *deviation_max_attr_name, + const char *deviation_max_frame_attr_name, MDGModifier &dgmod, + MAnimCurveChange &curveChange) { + MStatus status = MS::kSuccess; + + // How to create the animation curve. + const auto anim_curve_type = MFnAnimCurve::kAnimCurveTU; + const auto anim_curve_infinity_type = MFnAnimCurve::kConstant; + const auto anim_curve_tangent_in_type = MFnAnimCurve::kTangentGlobal; + const auto anim_curve_tangent_out_type = MFnAnimCurve::kTangentGlobal; + + MTime::Unit ui_unit = MTime::uiUnit(); + + MFnDependencyNode dg_node_fn(node, &status); + CHECK_MSTATUS_AND_RETURN_IT(status); + + int created_attrs_count = 0; + bool deviation_has_anim_curve = false; + + MPlug deviation_plug; + MPlug deviation_avg_plug; + MPlug deviation_max_plug; + MPlug deviation_max_frame_plug; + + MObject deviation_anim_curve_obj; + + status = find_plug(deviation_attr_name, dg_node_fn, deviation_plug); + CHECK_MSTATUS_AND_RETURN_IT(status); + + status = find_plug(deviation_avg_attr_name, dg_node_fn, deviation_avg_plug); + CHECK_MSTATUS_AND_RETURN_IT(status); + + status = find_plug(deviation_max_attr_name, dg_node_fn, deviation_max_plug); + CHECK_MSTATUS_AND_RETURN_IT(status); + + status = find_plug(deviation_max_frame_attr_name, dg_node_fn, + deviation_max_frame_plug); + CHECK_MSTATUS_AND_RETURN_IT(status); + + bool deviation_exists = !deviation_plug.isNull(); + bool deviation_avg_exists = !deviation_avg_plug.isNull(); + bool deviation_max_exists = !deviation_max_plug.isNull(); + bool deviation_max_frame_exists = !deviation_max_frame_plug.isNull(); + + if (!deviation_exists) { + status = create_attr_deviation(deviation_attr_name, node, dgmod); + CHECK_MSTATUS_AND_RETURN_IT(status); + ++created_attrs_count; + } + + if (!deviation_avg_exists) { + status = + create_attr_averageDeviation(deviation_avg_attr_name, node, dgmod); + CHECK_MSTATUS_AND_RETURN_IT(status); + ++created_attrs_count; + } + + if (!deviation_max_exists) { + status = + create_attr_maximumDeviation(deviation_max_attr_name, node, dgmod); + CHECK_MSTATUS_AND_RETURN_IT(status); + ++created_attrs_count; + } + + if (!deviation_max_frame_exists) { + status = create_attr_maxDeviationFrame(deviation_max_frame_attr_name, + node, dgmod); + CHECK_MSTATUS_AND_RETURN_IT(status); + ++created_attrs_count; + } + + if (created_attrs_count > 0) { + dgmod.doIt(); + + if (!deviation_exists) { + status = find_plug(deviation_attr_name, dg_node_fn, deviation_plug); + CHECK_MSTATUS_AND_RETURN_IT(status); + deviation_exists = !deviation_plug.isNull(); + MFnAnimCurve anim_curve_fn; + deviation_anim_curve_obj = anim_curve_fn.create( + deviation_plug, anim_curve_type, &dgmod, &status); + dgmod.doIt(); + deviation_has_anim_curve = true; + } + + if (!deviation_avg_exists) { + status = find_plug(deviation_avg_attr_name, dg_node_fn, + deviation_avg_plug); + CHECK_MSTATUS_AND_RETURN_IT(status); + deviation_avg_exists = !deviation_avg_plug.isNull(); + } + + if (!deviation_max_exists) { + status = find_plug(deviation_max_attr_name, dg_node_fn, + deviation_max_plug); + CHECK_MSTATUS_AND_RETURN_IT(status); + deviation_max_exists = !deviation_max_plug.isNull(); + } + + if (!deviation_max_frame_exists) { + status = find_plug(deviation_max_frame_attr_name, dg_node_fn, + deviation_max_frame_plug); + CHECK_MSTATUS_AND_RETURN_IT(status); + deviation_max_frame_exists = !deviation_max_frame_plug.isNull(); + } + } + + // Make sure we get the deviation animation curve. + if (!deviation_has_anim_curve) { + MObjectArray objects; + const bool find = + MAnimUtil::findAnimation(deviation_plug, objects, &status); + CHECK_MSTATUS_AND_RETURN_IT(status); + + if (find && (objects.length() > 0)) { + deviation_anim_curve_obj = objects[0]; + } else { + MFnAnimCurve anim_curve_fn; + deviation_anim_curve_obj = anim_curve_fn.create( + deviation_plug, anim_curve_type, &dgmod, &status); + CHECK_MSTATUS_AND_RETURN_IT(status); + dgmod.doIt(); + } + deviation_has_anim_curve = true; + } + + assert(deviation_has_anim_curve); + assert(deviation_exists); + assert(deviation_avg_exists); + assert(deviation_max_exists); + assert(deviation_max_frame_exists); + + int value_count = 0; + double value_avg = 0.0; + double value_max = -0.0; + MTime frame_max_deviation(-1.0, ui_unit); + MTime frame_min(99999.0, ui_unit); + MTime frame_max(-1.0, ui_unit); + + MTimeArray times; + MDoubleArray values; + const bool keep_existing_keys = false; + for (const auto &kv : map) { + const auto frame_value = kv.first; + const auto error_value = kv.second; + + MTime time(frame_value, ui_unit); + times.append(time); + values.append(error_value); + + value_avg += error_value; + + ++value_count; + + if (error_value > value_max) { + value_max = error_value; + frame_max_deviation.setValue(frame_value); + } + + if (time < frame_min) { + frame_min = time; + } + if (time > frame_max) { + frame_max = time; + } + } + + // Ensure the frames before the last and first frames have values + // of -1.0, otherwise the values outside the solved range will be + // the last deviation value, and we want there to be no deviation, + // so that tools do not recognise the Marker on those frames. + frame_min -= MTime(1.0, ui_unit); + frame_max += MTime(1.0, ui_unit); + times.append(frame_min); + times.append(frame_max); + values.append(-1.0); + values.append(-1.0); + + if (value_count > 0) { + value_avg = value_avg / static_cast(value_count); + } + + const auto frame_max_deviation_num = + static_cast(frame_max_deviation.asUnits(ui_unit)); + + dgmod.newPlugValueDouble(deviation_avg_plug, value_avg); + dgmod.newPlugValueDouble(deviation_max_plug, value_max); + dgmod.newPlugValueInt(deviation_max_frame_plug, frame_max_deviation_num); + dgmod.doIt(); + + MFnDependencyNode depend_fn(deviation_anim_curve_obj, &status); + CHECK_MSTATUS_AND_RETURN_IT(status); + const MString node_name = depend_fn.name(); + const MString node_type = depend_fn.typeName(); + + MFnAnimCurve deviation_anim_curve_fn(deviation_anim_curve_obj); + status = deviation_anim_curve_fn.addKeys( + ×, &values, anim_curve_tangent_in_type, + anim_curve_tangent_out_type, keep_existing_keys, &curveChange); + CHECK_MSTATUS_AND_RETURN_IT(status); + + status = deviation_anim_curve_fn.setPreInfinityType( + anim_curve_infinity_type, &curveChange); + CHECK_MSTATUS_AND_RETURN_IT(status); + status = deviation_anim_curve_fn.setPostInfinityType( + anim_curve_infinity_type, &curveChange); + CHECK_MSTATUS_AND_RETURN_IT(status); + + return status; +} diff --git a/src/mmSolver/adjust/adjust_results_helpers.h b/src/mmSolver/adjust/adjust_results_helpers.h new file mode 100644 index 000000000..430a0c25e --- /dev/null +++ b/src/mmSolver/adjust/adjust_results_helpers.h @@ -0,0 +1,80 @@ +/* + * Copyright (C) 2022 David Cattermole. + * + * This file is part of mmSolver. + * + * mmSolver is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * mmSolver 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with mmSolver. If not, see . + * ==================================================================== + * + * Common functions used to set result data structures. + */ + +#ifndef MM_SOLVER_CORE_BUNDLE_ADJUST_RESULTS_HELPERS_H +#define MM_SOLVER_CORE_BUNDLE_ADJUST_RESULTS_HELPERS_H + +// STL +#include +#include + +// Maya +#include +#include +#include +#include +#include +#include +#include +#include + +inline MStatus find_plug(const char *attr_name, MFnDependencyNode &dgNodeFn, + MPlug &outPlug) { + const bool wantNetworkedPlug = true; + outPlug = dgNodeFn.findPlug(attr_name, wantNetworkedPlug); + return MS::kSuccess; +} + +inline MStatus create_numeric_attr(const char *attr_name, MObject &node, + MDGModifier &dgmod, + MFnNumericAttribute &numeric_attr, + const MFnNumericData::Type numeric_type) { + MStatus status = MS::kSuccess; + + MObject attr = numeric_attr.create(attr_name, attr_name, numeric_type); + status = dgmod.addAttribute(node, attr); + CHECK_MSTATUS_AND_RETURN_IT(status); + + return status; +} + +inline MStatus create_typed_attr(const char *attr_name, MObject &node, + MDGModifier &dgmod, + MFnTypedAttribute &typed_attr, + const MFnData::Type &data_type) { + MStatus status = MS::kSuccess; + + MObject attr = typed_attr.create(attr_name, attr_name, data_type); + status = dgmod.addAttribute(node, attr); + CHECK_MSTATUS_AND_RETURN_IT(status); + + return status; +} + +MStatus create_deviation_attrs_on_node( + MObject &node, const std::unordered_multimap &map, + const char *deviation_attr_name, const char *deviation_avg_attr_name, + const char *deviation_max_attr_name, + const char *deviation_max_frame_attr_name, MDGModifier &dgmod, + MAnimCurveChange &curveChange); + +#endif // MM_SOLVER_CORE_BUNDLE_ADJUST_RESULTS_HELPERS_H diff --git a/src/mmSolver/adjust/adjust_results_setMarkerData.cpp b/src/mmSolver/adjust/adjust_results_setMarkerData.cpp new file mode 100644 index 000000000..472f9f8db --- /dev/null +++ b/src/mmSolver/adjust/adjust_results_setMarkerData.cpp @@ -0,0 +1,71 @@ +/* + * Copyright (C) 2022 David Cattermole. + * + * This file is part of mmSolver. + * + * mmSolver is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * mmSolver 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with mmSolver. If not, see . + * ==================================================================== + * + * Set data on Marker Maya nodes. + */ + +#include "adjust_results_setMarkerData.h" + +// STL +#include + +// Maya +#include +#include +#include +#include + +// MM Solver +#include "adjust_data.h" +#include "adjust_results.h" +#include "adjust_results_helpers.h" +#include "mmSolver/utilities/debug_utils.h" + +MStatus setErrorMetricsResultDataOnMarkers(ErrorMetricsResult &results, + MarkerPtrList &markerList, + MDGModifier &dgmod, + MAnimCurveChange &curveChange) { + MStatus status = MS::kSuccess; + + const char *deviation_attr_name = "deviation"; + const char *deviation_avg_attr_name = "averageDeviation"; + const char *deviation_max_attr_name = "maximumDeviation"; + const char *deviation_max_frame_attr_name = "maximumDeviationFrame"; + + for (auto &marker : markerList) { + MObject marker_obj = marker->getObject(); + + const MString marker_node_name = marker->getNodeName(); + const char *marker_name = marker_node_name.asChar(); + + const auto search = + results.error_per_marker_per_frame.find(marker_name); + if (search != results.error_per_marker_per_frame.end()) { + auto map = search->second; + + status = create_deviation_attrs_on_node( + marker_obj, map, deviation_attr_name, deviation_avg_attr_name, + deviation_max_attr_name, deviation_max_frame_attr_name, dgmod, + curveChange); + CHECK_MSTATUS_AND_RETURN_IT(status); + } + } + + return status; +} diff --git a/src/mmSolver/adjust/adjust_results_setMarkerData.h b/src/mmSolver/adjust/adjust_results_setMarkerData.h new file mode 100644 index 000000000..c4e394bf1 --- /dev/null +++ b/src/mmSolver/adjust/adjust_results_setMarkerData.h @@ -0,0 +1,42 @@ +/* + * Copyright (C) 2022 David Cattermole. + * + * This file is part of mmSolver. + * + * mmSolver is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * mmSolver 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with mmSolver. If not, see . + * ==================================================================== + * + * Set data on Marker Maya nodes. + */ + +#ifndef MM_SOLVER_CORE_BUNDLE_ADJUST_RESULTS_SET_MARKER_DATA_H +#define MM_SOLVER_CORE_BUNDLE_ADJUST_RESULTS_SET_MARKER_DATA_H + +// Maya +#include +#include +#include + +// MM Solver +#include "adjust_data.h" +#include "adjust_defines.h" +#include "adjust_results.h" +#include "mmSolver/mayahelper/maya_marker.h" + +MStatus setErrorMetricsResultDataOnMarkers(ErrorMetricsResult &results, + MarkerPtrList &markerList, + MDGModifier &dgmod, + MAnimCurveChange &curveChange); + +#endif // MM_SOLVER_CORE_BUNDLE_ADJUST_RESULTS_SET_MARKER_DATA_H diff --git a/src/mmSolver/adjust/adjust_results_setSolveData.cpp b/src/mmSolver/adjust/adjust_results_setSolveData.cpp new file mode 100644 index 000000000..628aa08f4 --- /dev/null +++ b/src/mmSolver/adjust/adjust_results_setSolveData.cpp @@ -0,0 +1,711 @@ +/* + * Copyright (C) 2022 David Cattermole. + * + * This file is part of mmSolver. + * + * mmSolver is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * mmSolver 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with mmSolver. If not, see . + * ==================================================================== + * + * Sets solve data onto Maya nodes. + */ + +#include "adjust_results_setSolveData.h" + +// STL +#include + +// Maya +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// MM Solver +#include "adjust_data.h" +#include "adjust_results.h" +#include "adjust_results_helpers.h" +#include "mmSolver/utilities/debug_utils.h" + +MStatus createResultAttr_boolean(const char *attr_name, MObject &node, + MDGModifier &dgmod) { + MStatus status = MS::kSuccess; + MFnNumericAttribute numeric_attr; + status = create_numeric_attr(attr_name, node, dgmod, numeric_attr, + MFnNumericData::kBoolean); + numeric_attr.setKeyable(true); + numeric_attr.setDefault(true); + CHECK_MSTATUS_AND_RETURN_IT(status); + return status; +} + +MStatus createResultAttr_success(const char *attr_name, MObject &node, + MDGModifier &dgmod) { + return createResultAttr_boolean(attr_name, node, dgmod); +} + +MStatus createResultAttr_reasonNum(const char *attr_name, MObject &node, + MDGModifier &dgmod) { + MStatus status = MS::kSuccess; + MFnNumericAttribute numeric_attr; + status = create_numeric_attr(attr_name, node, dgmod, numeric_attr, + MFnNumericData::kLong); + CHECK_MSTATUS_AND_RETURN_IT(status); + + numeric_attr.setMin(0); + numeric_attr.setDefault(0); + + return status; +} + +MStatus createResultAttr_reasonString(const char *attr_name, MObject &node, + MDGModifier &dgmod) { + MStatus status = MS::kSuccess; + + MFnTypedAttribute typedAttr; + + MFnStringData string_data_fn; + MObject string_default_object = string_data_fn.create(""); + + status = + create_typed_attr(attr_name, node, dgmod, typedAttr, MFnData::kString); + CHECK_MSTATUS_AND_RETURN_IT(status); + + typedAttr.setReadable(true); + typedAttr.setWritable(true); + + return status; +} + +MStatus createResultAttr_userInterrupted(const char *attr_name, MObject &node, + MDGModifier &dgmod) { + return createResultAttr_boolean(attr_name, node, dgmod); +} + +MStatus createResultAttr_deviation(const char *attr_name, MObject &node, + MDGModifier &dgmod) { + MStatus status = MS::kSuccess; + MFnNumericAttribute numeric_attr; + status = create_numeric_attr(attr_name, node, dgmod, numeric_attr, + MFnNumericData::kDouble); + numeric_attr.setKeyable(true); + numeric_attr.setMin(-1.0); + numeric_attr.setDefault(-1.0); + CHECK_MSTATUS_AND_RETURN_IT(status); + return status; +} + +MStatus createResultAttr_averageDeviation(const char *attr_name, MObject &node, + MDGModifier &dgmod) { + return createResultAttr_deviation(attr_name, node, dgmod); +} + +MStatus createResultAttr_maximumDeviation(const char *attr_name, MObject &node, + MDGModifier &dgmod) { + return createResultAttr_deviation(attr_name, node, dgmod); +} + +MStatus createResultAttr_minimumDeviation(const char *attr_name, MObject &node, + MDGModifier &dgmod) { + return createResultAttr_deviation(attr_name, node, dgmod); +} + +MStatus createResultAttr_maxDeviationFrame(const char *attr_name, MObject &node, + MDGModifier &dgmod) { + MStatus status = MS::kSuccess; + MFnNumericAttribute numeric_attr; + status = create_numeric_attr(attr_name, node, dgmod, numeric_attr, + MFnNumericData::kLong); + numeric_attr.setKeyable(true); + numeric_attr.setMin(-1); + numeric_attr.setDefault(-1); + CHECK_MSTATUS_AND_RETURN_IT(status); + return status; +} + +MStatus createResultAttr_objectCount(const char *attr_name, MObject &node, + MDGModifier &dgmod) { + MStatus status = MS::kSuccess; + MFnNumericAttribute numeric_attr; + status = create_numeric_attr(attr_name, node, dgmod, numeric_attr, + MFnNumericData::kLong); + numeric_attr.setMin(0); + numeric_attr.setDefault(0); + CHECK_MSTATUS_AND_RETURN_IT(status); + return status; +} + +MStatus createResultAttr_iterationNum(const char *attr_name, MObject &node, + MDGModifier &dgmod) { + return createResultAttr_objectCount(attr_name, node, dgmod); +} + +MStatus createResultAttr_iterationFunctionNum(const char *attr_name, + MObject &node, + MDGModifier &dgmod) { + return createResultAttr_objectCount(attr_name, node, dgmod); +} + +MStatus createResultAttr_iterationJacobianNum(const char *attr_name, + MObject &node, + MDGModifier &dgmod) { + return createResultAttr_objectCount(attr_name, node, dgmod); +} + +MStatus setSolverResultDataOnNode(SolverResult &results, MObject &node, + MDGModifier &dgmod, + MAnimCurveChange &curveChange) { + MStatus status = MS::kSuccess; + + MFnDependencyNode dg_node_fn(node, &status); + CHECK_MSTATUS_AND_RETURN_IT(status); + + const char *success_attr_name = "success"; + const char *reason_num_attr_name = "reason_num"; + const char *reason_string_attr_name = "reason_string"; + const char *error_final_attr_name = "error_final"; + const char *error_final_avg_attr_name = "error_final_average"; + const char *error_final_max_attr_name = "error_final_maximum"; + const char *error_final_min_attr_name = "error_final_minimum"; + const char *iteration_num_attr_name = "iteration_num"; + const char *iteration_function_num_attr_name = "iteration_function_num"; + const char *iteration_jacobian_num_attr_name = "iteration_jacobian_num"; + const char *user_interrupted_attr_name = "user_interrupted"; + + MPlug success_plug; + MPlug reason_num_plug; + MPlug reason_string_plug; + MPlug error_final_plug; + MPlug error_final_avg_plug; + MPlug error_final_max_plug; + MPlug error_final_min_plug; + MPlug iteration_num_plug; + MPlug iteration_function_num_plug; + MPlug iteration_jacobian_num_plug; + MPlug user_interrupted_plug; + + status = find_plug(success_attr_name, dg_node_fn, success_plug); + CHECK_MSTATUS_AND_RETURN_IT(status); + status = find_plug(reason_num_attr_name, dg_node_fn, reason_num_plug); + CHECK_MSTATUS_AND_RETURN_IT(status); + status = find_plug(reason_string_attr_name, dg_node_fn, reason_string_plug); + CHECK_MSTATUS_AND_RETURN_IT(status); + status = find_plug(error_final_attr_name, dg_node_fn, error_final_plug); + CHECK_MSTATUS_AND_RETURN_IT(status); + status = + find_plug(error_final_avg_attr_name, dg_node_fn, error_final_avg_plug); + CHECK_MSTATUS_AND_RETURN_IT(status); + status = + find_plug(error_final_max_attr_name, dg_node_fn, error_final_max_plug); + CHECK_MSTATUS_AND_RETURN_IT(status); + status = + find_plug(error_final_min_attr_name, dg_node_fn, error_final_min_plug); + CHECK_MSTATUS_AND_RETURN_IT(status); + status = find_plug(iteration_num_attr_name, dg_node_fn, iteration_num_plug); + CHECK_MSTATUS_AND_RETURN_IT(status); + status = find_plug(iteration_function_num_attr_name, dg_node_fn, + iteration_function_num_plug); + CHECK_MSTATUS_AND_RETURN_IT(status); + status = find_plug(iteration_jacobian_num_attr_name, dg_node_fn, + iteration_jacobian_num_plug); + CHECK_MSTATUS_AND_RETURN_IT(status); + status = find_plug(user_interrupted_attr_name, dg_node_fn, + user_interrupted_plug); + CHECK_MSTATUS_AND_RETURN_IT(status); + + bool success_exists = !success_plug.isNull(); + bool reason_num_exists = !reason_num_plug.isNull(); + bool reason_string_exists = !reason_string_plug.isNull(); + bool error_final_exists = !error_final_plug.isNull(); + bool error_final_avg_exists = !error_final_avg_plug.isNull(); + bool error_final_max_exists = !error_final_max_plug.isNull(); + bool error_final_min_exists = !error_final_min_plug.isNull(); + bool iteration_num_exists = !iteration_num_plug.isNull(); + bool iteration_function_num_exists = !iteration_function_num_plug.isNull(); + bool iteration_jacobian_num_exists = !iteration_jacobian_num_plug.isNull(); + bool user_interrupted_exists = !user_interrupted_plug.isNull(); + + int created_attrs_count = 0; + if (!success_exists) { + status = createResultAttr_success(success_attr_name, node, dgmod); + CHECK_MSTATUS_AND_RETURN_IT(status); + ++created_attrs_count; + } + + if (!reason_num_exists) { + status = createResultAttr_reasonNum(reason_num_attr_name, node, dgmod); + CHECK_MSTATUS_AND_RETURN_IT(status); + ++created_attrs_count; + } + + if (!reason_string_exists) { + status = + createResultAttr_reasonString(reason_string_attr_name, node, dgmod); + CHECK_MSTATUS_AND_RETURN_IT(status); + ++created_attrs_count; + } + + if (!error_final_exists) { + status = createResultAttr_deviation(error_final_attr_name, node, dgmod); + CHECK_MSTATUS_AND_RETURN_IT(status); + ++created_attrs_count; + } + + if (!error_final_avg_exists) { + status = createResultAttr_averageDeviation(error_final_avg_attr_name, + node, dgmod); + CHECK_MSTATUS_AND_RETURN_IT(status); + ++created_attrs_count; + } + + if (!error_final_max_exists) { + status = createResultAttr_maximumDeviation(error_final_max_attr_name, + node, dgmod); + CHECK_MSTATUS_AND_RETURN_IT(status); + ++created_attrs_count; + } + + if (!error_final_min_exists) { + status = createResultAttr_minimumDeviation(error_final_min_attr_name, + node, dgmod); + CHECK_MSTATUS_AND_RETURN_IT(status); + ++created_attrs_count; + } + + if (!iteration_num_exists) { + status = + createResultAttr_iterationNum(iteration_num_attr_name, node, dgmod); + CHECK_MSTATUS_AND_RETURN_IT(status); + ++created_attrs_count; + } + + if (!iteration_function_num_exists) { + status = createResultAttr_iterationFunctionNum( + iteration_function_num_attr_name, node, dgmod); + CHECK_MSTATUS_AND_RETURN_IT(status); + ++created_attrs_count; + } + + if (!iteration_jacobian_num_exists) { + status = createResultAttr_iterationJacobianNum( + iteration_jacobian_num_attr_name, node, dgmod); + CHECK_MSTATUS_AND_RETURN_IT(status); + ++created_attrs_count; + } + + if (!user_interrupted_exists) { + status = createResultAttr_userInterrupted(user_interrupted_attr_name, + node, dgmod); + CHECK_MSTATUS_AND_RETURN_IT(status); + ++created_attrs_count; + } + + // Any non-created attributes need to be created before + // proceeding. + if (created_attrs_count > 0) { + dgmod.doIt(); + + if (!success_exists) { + status = find_plug(success_attr_name, dg_node_fn, success_plug); + CHECK_MSTATUS_AND_RETURN_IT(status); + success_exists = !success_plug.isNull(); + } + + if (!reason_num_exists) { + status = + find_plug(reason_num_attr_name, dg_node_fn, reason_num_plug); + CHECK_MSTATUS_AND_RETURN_IT(status); + reason_num_exists = !reason_num_plug.isNull(); + } + + if (!reason_string_exists) { + status = find_plug(reason_string_attr_name, dg_node_fn, + reason_string_plug); + CHECK_MSTATUS_AND_RETURN_IT(status); + reason_string_exists = !reason_string_plug.isNull(); + } + + if (!error_final_exists) { + status = + find_plug(error_final_attr_name, dg_node_fn, error_final_plug); + CHECK_MSTATUS_AND_RETURN_IT(status); + error_final_exists = !error_final_plug.isNull(); + } + + if (!error_final_avg_exists) { + status = find_plug(error_final_avg_attr_name, dg_node_fn, + error_final_avg_plug); + CHECK_MSTATUS_AND_RETURN_IT(status); + error_final_avg_exists = !error_final_avg_plug.isNull(); + } + + if (!error_final_max_exists) { + status = find_plug(error_final_max_attr_name, dg_node_fn, + error_final_max_plug); + CHECK_MSTATUS_AND_RETURN_IT(status); + error_final_max_exists = !error_final_max_plug.isNull(); + } + + if (!error_final_min_exists) { + status = find_plug(error_final_min_attr_name, dg_node_fn, + error_final_min_plug); + CHECK_MSTATUS_AND_RETURN_IT(status); + error_final_min_exists = !error_final_min_plug.isNull(); + } + + if (!iteration_num_exists) { + status = find_plug(iteration_num_attr_name, dg_node_fn, + iteration_num_plug); + CHECK_MSTATUS_AND_RETURN_IT(status); + iteration_num_exists = !iteration_num_plug.isNull(); + } + + if (!iteration_function_num_exists) { + status = find_plug(iteration_function_num_attr_name, dg_node_fn, + iteration_function_num_plug); + CHECK_MSTATUS_AND_RETURN_IT(status); + iteration_function_num_exists = + !iteration_function_num_plug.isNull(); + } + + if (!iteration_jacobian_num_exists) { + status = find_plug(iteration_jacobian_num_attr_name, dg_node_fn, + iteration_jacobian_num_plug); + CHECK_MSTATUS_AND_RETURN_IT(status); + iteration_jacobian_num_exists = + !iteration_jacobian_num_plug.isNull(); + } + + if (!user_interrupted_exists) { + status = find_plug(user_interrupted_attr_name, dg_node_fn, + user_interrupted_plug); + CHECK_MSTATUS_AND_RETURN_IT(status); + user_interrupted_exists = !user_interrupted_plug.isNull(); + } + } + + assert(success_exists); + assert(reason_num_exists); + assert(reason_string_exists); + assert(error_final_exists); + assert(error_final_avg_exists); + assert(error_final_max_exists); + assert(error_final_min_exists); + assert(iteration_num_exists); + assert(iteration_function_num_exists); + assert(iteration_jacobian_num_exists); + assert(user_interrupted_exists); + + dgmod.newPlugValueBool(success_plug, results.success); + dgmod.newPlugValueInt(reason_num_plug, results.reason_number); + dgmod.newPlugValueString(reason_string_plug, + MString(results.reason.c_str())); + dgmod.newPlugValueDouble(error_final_plug, results.errorFinal); + dgmod.newPlugValueDouble(error_final_avg_plug, results.errorAvg); + dgmod.newPlugValueDouble(error_final_max_plug, results.errorMax); + dgmod.newPlugValueDouble(error_final_min_plug, results.errorMin); + dgmod.newPlugValueInt(iteration_num_plug, results.iterations); + dgmod.newPlugValueInt(iteration_function_num_plug, results.functionEvals); + dgmod.newPlugValueInt(iteration_jacobian_num_plug, results.jacobianEvals); + dgmod.newPlugValueBool(user_interrupted_plug, results.user_interrupted); + dgmod.doIt(); + + return status; +} + +MStatus setTimerResultDataOnNode(TimerResult &results, MObject &node, + MDGModifier &dgmod, + MAnimCurveChange &curveChange) { + MStatus status = MS::kSuccess; + + // TODO: Write this. + + return status; +} + +MStatus setSolveValuesResultDataOnNode(SolveValuesResult &results, + MObject &node, MDGModifier &dgmod, + MAnimCurveChange &curveChange) { + MStatus status = MS::kSuccess; + + // TODO: Write this. + + return status; +} + +std::string get_short_node_name(const std::string &in_string) { + const auto start = in_string.rfind('|'); + if (start == -1) { + return std::string(in_string); + } + const auto end = in_string.size(); + return std::string(in_string.substr(start + 1, end)); +} + +MStatus setErrorMetricsResultDataOnNode(ErrorMetricsResult &results, + MObject &node, MDGModifier &dgmod, + MAnimCurveChange &curveChange) { + MStatus status = MS::kSuccess; + + const char *deviation_attr_name = "deviation"; + const char *deviation_avg_attr_name = "average_deviation"; + const char *deviation_max_attr_name = "maximum_deviation"; + const char *deviation_max_frame_attr_name = "maximum_deviation_frame"; + + status = create_deviation_attrs_on_node( + node, results.error_per_frame, deviation_attr_name, + deviation_avg_attr_name, deviation_max_attr_name, + deviation_max_frame_attr_name, dgmod, curveChange); + CHECK_MSTATUS_AND_RETURN_IT(status); + + // Set deviation attributes for each for each marker, on the given + // `node`. + const char *prefix = "mkr___"; + for (auto &kv : results.error_per_marker_per_frame) { + const auto marker_name = get_short_node_name(kv.first); + const auto map = kv.second; + + // MMSOLVER_WRN("results marker name: " << marker_name); + std::stringstream deviation_ss; + std::stringstream deviation_avg_ss; + std::stringstream deviation_max_ss; + std::stringstream deviation_max_frame_ss; + + deviation_ss << prefix << marker_name << "___" << deviation_attr_name; + const std::string marker_deviation_attr_name = deviation_ss.str(); + + deviation_avg_ss << prefix << marker_name << "___" + << deviation_avg_attr_name; + const std::string marker_deviation_avg_attr_name = + deviation_avg_ss.str(); + + deviation_max_ss << prefix << marker_name << "___" + << deviation_max_attr_name; + const std::string marker_deviation_max_attr_name = + deviation_max_ss.str(); + + deviation_max_frame_ss << prefix << marker_name << "___" + << deviation_max_frame_attr_name; + const std::string marker_deviation_max_frame_attr_name = + deviation_max_frame_ss.str(); + + status = create_deviation_attrs_on_node( + node, map, marker_deviation_attr_name.c_str(), + marker_deviation_avg_attr_name.c_str(), + marker_deviation_max_attr_name.c_str(), + marker_deviation_max_frame_attr_name.c_str(), dgmod, curveChange); + CHECK_MSTATUS_AND_RETURN_IT(status); + } + + return status; +} + +MStatus setAffectsResultDataOnNode(AffectsResult &results, MObject &node, + MDGModifier &dgmod, + MAnimCurveChange &curveChange) { + MStatus status = MS::kSuccess; + + return status; +} + +MStatus setSolverObjectUsageResultDataOnNode(SolverObjectUsageResult &results, + MObject &node, MDGModifier &dgmod, + MAnimCurveChange &curveChange) { + MStatus status = MS::kSuccess; + + return status; +} + +MStatus setSolverObjectCountResultDataOnNode(SolverObjectCountResult &results, + MObject &node, MDGModifier &dgmod, + MAnimCurveChange &curveChange) { + MStatus status = MS::kSuccess; + + MFnDependencyNode dg_node_fn(node, &status); + CHECK_MSTATUS_AND_RETURN_IT(status); + + const char *parameters_num_attr_name = "numberOfParameters"; + const char *errors_num_attr_name = "numberOfErrors"; + const char *marker_errors_num_attr_name = "numberOfMarkerErrors"; + const char *stiff_errors_num_attr_name = "numberOfAttrStiffnessErrors"; + const char *smooth_errors_num_attr_name = "numberOfAttrSmoothnessErrors"; + + MPlug parameters_num_plug; + MPlug errors_num_plug; + MPlug marker_errors_num_plug; + MPlug stiff_errors_num_plug; + MPlug smooth_errors_num_plug; + + status = + find_plug(parameters_num_attr_name, dg_node_fn, parameters_num_plug); + CHECK_MSTATUS_AND_RETURN_IT(status); + status = find_plug(errors_num_attr_name, dg_node_fn, errors_num_plug); + CHECK_MSTATUS_AND_RETURN_IT(status); + status = find_plug(marker_errors_num_attr_name, dg_node_fn, + marker_errors_num_plug); + CHECK_MSTATUS_AND_RETURN_IT(status); + status = find_plug(stiff_errors_num_attr_name, dg_node_fn, + stiff_errors_num_plug); + CHECK_MSTATUS_AND_RETURN_IT(status); + status = find_plug(smooth_errors_num_attr_name, dg_node_fn, + smooth_errors_num_plug); + CHECK_MSTATUS_AND_RETURN_IT(status); + + bool parameters_num_exists = !parameters_num_plug.isNull(); + bool errors_num_exists = !errors_num_plug.isNull(); + bool marker_errors_num_exists = !marker_errors_num_plug.isNull(); + bool stiff_errors_num_exists = !stiff_errors_num_plug.isNull(); + bool smooth_errors_num_exists = !smooth_errors_num_plug.isNull(); + + int created_attrs_count = 0; + if (!parameters_num_exists) { + status = + createResultAttr_objectCount(parameters_num_attr_name, node, dgmod); + CHECK_MSTATUS_AND_RETURN_IT(status); + ++created_attrs_count; + } + + if (!errors_num_exists) { + status = + createResultAttr_objectCount(errors_num_attr_name, node, dgmod); + CHECK_MSTATUS_AND_RETURN_IT(status); + ++created_attrs_count; + } + + if (!marker_errors_num_exists) { + status = createResultAttr_objectCount(marker_errors_num_attr_name, node, + dgmod); + CHECK_MSTATUS_AND_RETURN_IT(status); + ++created_attrs_count; + } + + if (!stiff_errors_num_exists) { + status = createResultAttr_objectCount(stiff_errors_num_attr_name, node, + dgmod); + CHECK_MSTATUS_AND_RETURN_IT(status); + ++created_attrs_count; + } + + if (!smooth_errors_num_exists) { + status = createResultAttr_objectCount(smooth_errors_num_attr_name, node, + dgmod); + CHECK_MSTATUS_AND_RETURN_IT(status); + ++created_attrs_count; + } + + // Any non-created attributes need to be created before + // proceeding. + if (created_attrs_count > 0) { + dgmod.doIt(); + + if (!parameters_num_exists) { + status = find_plug(parameters_num_attr_name, dg_node_fn, + parameters_num_plug); + CHECK_MSTATUS_AND_RETURN_IT(status); + parameters_num_exists = !parameters_num_plug.isNull(); + } + + if (!errors_num_exists) { + status = + find_plug(errors_num_attr_name, dg_node_fn, errors_num_plug); + CHECK_MSTATUS_AND_RETURN_IT(status); + errors_num_exists = !errors_num_plug.isNull(); + } + + if (!marker_errors_num_exists) { + status = find_plug(marker_errors_num_attr_name, dg_node_fn, + marker_errors_num_plug); + CHECK_MSTATUS_AND_RETURN_IT(status); + marker_errors_num_exists = !marker_errors_num_plug.isNull(); + } + + if (!stiff_errors_num_exists) { + status = find_plug(stiff_errors_num_attr_name, dg_node_fn, + stiff_errors_num_plug); + CHECK_MSTATUS_AND_RETURN_IT(status); + stiff_errors_num_exists = !stiff_errors_num_plug.isNull(); + } + + if (!smooth_errors_num_exists) { + status = find_plug(smooth_errors_num_attr_name, dg_node_fn, + smooth_errors_num_plug); + CHECK_MSTATUS_AND_RETURN_IT(status); + smooth_errors_num_exists = !smooth_errors_num_plug.isNull(); + } + } + + assert(parameters_num_exists); + assert(errors_num_exists); + assert(marker_errors_num_exists); + assert(stiff_errors_num_exists); + assert(smooth_errors_num_exists); + + dgmod.newPlugValueInt(parameters_num_plug, results.parameter_count); + dgmod.newPlugValueInt(errors_num_plug, results.error_count); + dgmod.newPlugValueInt(marker_errors_num_plug, results.marker_error_count); + dgmod.newPlugValueInt(stiff_errors_num_plug, + results.attr_stiffness_error_count); + dgmod.newPlugValueInt(smooth_errors_num_plug, + results.attr_smoothness_error_count); + dgmod.doIt(); + + return status; +} + +MStatus setCommandResultDataOnNode(CommandResult &results, + const PrintStatOptions &printStats, + MObject &node, MDGModifier &dgmod, + MAnimCurveChange &curveChange) { + MStatus status = MS::kSuccess; + if (node.isNull()) { + return MS::kFailure; + } + + if (printStats.input) { + status = setSolverObjectCountResultDataOnNode( + results.solverObjectCountResult, node, dgmod, curveChange); + CHECK_MSTATUS_AND_RETURN_IT(status); + } + + if (printStats.usedSolveObjects) { + status = setSolverObjectUsageResultDataOnNode( + results.solverObjectUsageResult, node, dgmod, curveChange); + CHECK_MSTATUS_AND_RETURN_IT(status); + } + + if (printStats.affects) { + status = setAffectsResultDataOnNode(results.affectsResult, node, dgmod, + curveChange); + CHECK_MSTATUS_AND_RETURN_IT(status); + } + + status = setSolverResultDataOnNode(results.solverResult, node, dgmod, + curveChange); + CHECK_MSTATUS_AND_RETURN_IT(status); + status = + setTimerResultDataOnNode(results.timerResult, node, dgmod, curveChange); + CHECK_MSTATUS_AND_RETURN_IT(status); + status = setErrorMetricsResultDataOnNode(results.errorMetricsResult, node, + dgmod, curveChange); + CHECK_MSTATUS_AND_RETURN_IT(status); + status = setSolveValuesResultDataOnNode(results.solveValuesResult, node, + dgmod, curveChange); + CHECK_MSTATUS_AND_RETURN_IT(status); + + return status; +} diff --git a/src/mmSolver/adjust/adjust_results_setSolveData.h b/src/mmSolver/adjust/adjust_results_setSolveData.h new file mode 100644 index 000000000..289f8514e --- /dev/null +++ b/src/mmSolver/adjust/adjust_results_setSolveData.h @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2022 David Cattermole. + * + * This file is part of mmSolver. + * + * mmSolver is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * mmSolver 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with mmSolver. If not, see . + * ==================================================================== + * + * Sets solve data onto Maya nodes. + */ + +#ifndef MM_SOLVER_CORE_BUNDLE_ADJUST_RESULTS_SET_SOLVE_DATA_H +#define MM_SOLVER_CORE_BUNDLE_ADJUST_RESULTS_SET_SOLVE_DATA_H + +// Maya +#include +#include +#include + +// MM Solver +#include "adjust_data.h" +#include "adjust_results.h" + +MStatus setCommandResultDataOnNode(CommandResult &results, + const PrintStatOptions &printStats, + MObject &node, MDGModifier &dgmod, + MAnimCurveChange &curveChange); + +#endif // MM_SOLVER_CORE_BUNDLE_ADJUST_RESULTS_SET_SOLVE_DATA_H diff --git a/src/mmSolver/cmd/MMSolver2Cmd.cpp b/src/mmSolver/cmd/MMSolver2Cmd.cpp index 4d5dcc202..d90f221c0 100644 --- a/src/mmSolver/cmd/MMSolver2Cmd.cpp +++ b/src/mmSolver/cmd/MMSolver2Cmd.cpp @@ -34,6 +34,8 @@ // MM Solver #include "mmSolver/adjust/adjust_base.h" +#include "mmSolver/adjust/adjust_results_setMarkerData.h" +#include "mmSolver/adjust/adjust_results_setSolveData.h" #include "mmSolver/cmd/common_arg_flags.h" namespace mmsolver { @@ -62,7 +64,7 @@ MSyntax MMSolver2Cmd::newSyntax() { createSolveObjectSyntax(syntax); createSolveFramesSyntax(syntax); createSolveInfoSyntax_v2(syntax); - createSolveLogSyntax(syntax); + createSolveLogSyntax_v2(syntax); return syntax; } @@ -97,7 +99,9 @@ MStatus MMSolver2Cmd::parseArgs(const MArgList &args) { m_solverOptions.solverSupportsRobustLoss, m_solverOptions.imageWidth); CHECK_MSTATUS_AND_RETURN_IT(status); - status = parseSolveLogArguments(argData, m_printStatsList, m_logLevel); + status = parseSolveLogArguments_v2(argData, m_printStatsList, m_logLevel, + m_resultsNodeObject, + m_setMarkerDeviationAttrs); CHECK_MSTATUS_AND_RETURN_IT(status); return status; @@ -136,6 +140,23 @@ MStatus MMSolver2Cmd::doIt(const MArgList &args) { m_attrList, m_frameList, m_dgmod, m_curveChange, m_computation, m_printStatsList, m_logLevel, m_cmdResult); + // Set the solve results, using m_dgmod and m_curveChange, so that + // the values can be reverted if the user undoes a 'mmSolver_v2' + // command. + if (!m_resultsNodeObject.isNull()) { + status = setCommandResultDataOnNode(m_cmdResult, m_cmdResult.printStats, + m_resultsNodeObject, m_dgmod, + m_curveChange); + CHECK_MSTATUS_AND_RETURN_IT(status); + } + + if (m_setMarkerDeviationAttrs) { + status = setErrorMetricsResultDataOnMarkers( + m_cmdResult.errorMetricsResult, m_markerList, m_dgmod, + m_curveChange); + CHECK_MSTATUS_AND_RETURN_IT(status); + } + MMSolver2Cmd::setResult(ret); if (!ret) { MStreamUtils::stdErrorStream() diff --git a/src/mmSolver/cmd/MMSolver2Cmd.h b/src/mmSolver/cmd/MMSolver2Cmd.h index 7c6ba6309..2dc5c5eab 100644 --- a/src/mmSolver/cmd/MMSolver2Cmd.h +++ b/src/mmSolver/cmd/MMSolver2Cmd.h @@ -92,6 +92,8 @@ class MMSolver2Cmd : public MPxCommand { MStringArray m_printStatsList; LogLevel m_logLevel; CommandResult m_cmdResult; + MObject m_resultsNodeObject; + bool m_setMarkerDeviationAttrs; // Solver Objects CameraPtrList m_cameraList; diff --git a/src/mmSolver/cmd/MMSolverCmd.cpp b/src/mmSolver/cmd/MMSolverCmd.cpp index 7a81cb0b2..5c019dce3 100644 --- a/src/mmSolver/cmd/MMSolverCmd.cpp +++ b/src/mmSolver/cmd/MMSolverCmd.cpp @@ -66,7 +66,7 @@ MSyntax MMSolverCmd::newSyntax() { createAttributeDetailsSyntax(syntax); createSolveFramesSyntax(syntax); createSolveInfoSyntax_v1(syntax); - createSolveLogSyntax(syntax); + createSolveLogSyntax_v1(syntax); return syntax; } @@ -100,7 +100,7 @@ MStatus MMSolverCmd::parseArgs(const MArgList &args) { m_removeUnusedMarkers, m_removeUnusedAttributes, m_imageWidth); CHECK_MSTATUS_AND_RETURN_IT(status); - status = parseSolveLogArguments(argData, m_printStatsList, m_logLevel); + status = parseSolveLogArguments_v1(argData, m_printStatsList, m_logLevel); CHECK_MSTATUS_AND_RETURN_IT(status); return status; diff --git a/src/mmSolver/cmd/arg_flags_solve_log.cpp b/src/mmSolver/cmd/arg_flags_solve_log.cpp index bbadcef2d..7b1b377d0 100644 --- a/src/mmSolver/cmd/arg_flags_solve_log.cpp +++ b/src/mmSolver/cmd/arg_flags_solve_log.cpp @@ -32,47 +32,42 @@ // Internal Objects #include "mmSolver/adjust/adjust_data.h" +#include "mmSolver/mayahelper/maya_utils.h" #include "mmSolver/utilities/debug_utils.h" namespace mmsolver { -void createSolveLogSyntax(MSyntax &syntax) { +void createSolveLogSyntax_v1(MSyntax &syntax) { syntax.addFlag(LOG_LEVEL_FLAG, LOG_LEVEL_FLAG_LONG, MSyntax::kUnsigned); syntax.addFlag(VERBOSE_FLAG, VERBOSE_FLAG_LONG, MSyntax::kBoolean); syntax.addFlag(PRINT_STATS_FLAG, PRINT_STATS_FLAG_LONG, MSyntax::kString); syntax.makeFlagMultiUse(PRINT_STATS_FLAG); } -MStatus parseSolveLogArguments(const MArgDatabase &argData, - MStringArray &out_printStatsList, - LogLevel &out_logLevel) { - MStatus status = MStatus::kSuccess; - - // 'Log Level' can be overwritten by the (deprecated) verbose - // flag. - out_logLevel = LOG_LEVEL_DEFAULT_VALUE; - - // Get 'Verbose' flag. This is deprecated, but kept for backwards - // compatiblity. - if (argData.isFlagSet(VERBOSE_FLAG)) { - bool verbose = VERBOSE_DEFAULT_VALUE; - status = argData.getFlagArgument(VERBOSE_FLAG, 0, verbose); - if (verbose) { - out_logLevel = LogLevel::kVerbose; - } else { - out_logLevel = LogLevel::kInfo; - } - } +void createSolveLogSyntax_v2(MSyntax &syntax) { + syntax.addFlag(LOG_LEVEL_FLAG, LOG_LEVEL_FLAG_LONG, MSyntax::kUnsigned); + syntax.addFlag(PRINT_STATS_FLAG, PRINT_STATS_FLAG_LONG, MSyntax::kString); + syntax.makeFlagMultiUse(PRINT_STATS_FLAG); + syntax.addFlag(RESULTS_NODE_FLAG, RESULTS_NODE_FLAG_LONG, MSyntax::kString); + syntax.addFlag(SET_MARKER_DEVIATION_ATTRS_FLAG, + SET_MARKER_DEVIATION_ATTRS_FLAG_LONG, MSyntax::kBoolean); +} - // Get 'Log Level' +MStatus parseSolveLogArguments_logLevel(const MArgDatabase &argData, + LogLevel &out_logLevel) { + MStatus status = MStatus::kSuccess; if (argData.isFlagSet(LOG_LEVEL_FLAG)) { int logLevelNum = static_cast(LOG_LEVEL_DEFAULT_VALUE); status = argData.getFlagArgument(LOG_LEVEL_FLAG, 0, logLevelNum); CHECK_MSTATUS_AND_RETURN_IT(status); out_logLevel = static_cast(logLevelNum); } + return status; +} - // Get 'Print Statistics' +MStatus parseSolveLogArguments_printStats(const MArgDatabase &argData, + MStringArray &out_printStatsList) { + MStatus status = MStatus::kSuccess; unsigned int printStatsNum = argData.numberOfFlagUses(PRINT_STATS_FLAG); out_printStatsList.clear(); for (auto i = 0; i < printStatsNum; ++i) { @@ -88,6 +83,74 @@ MStatus parseSolveLogArguments(const MArgDatabase &argData, } } } + return status; +} + +MStatus parseSolveLogArguments_v1(const MArgDatabase &argData, + MStringArray &out_printStatsList, + LogLevel &out_logLevel) { + MStatus status = MStatus::kSuccess; + + // 'Log Level' can be overwritten by the (deprecated) verbose + // flag. + out_logLevel = LOG_LEVEL_DEFAULT_VALUE; + + // Get 'Verbose' flag. This is deprecated, but kept for backwards + // compatibility. + if (argData.isFlagSet(VERBOSE_FLAG)) { + bool verbose = VERBOSE_DEFAULT_VALUE; + status = argData.getFlagArgument(VERBOSE_FLAG, 0, verbose); + CHECK_MSTATUS_AND_RETURN_IT(status); + if (verbose) { + out_logLevel = LogLevel::kVerbose; + } else { + out_logLevel = LogLevel::kInfo; + } + } + + // Get 'Log Level' + status = parseSolveLogArguments_logLevel(argData, out_logLevel); + CHECK_MSTATUS_AND_RETURN_IT(status); + + // Get 'Print Statistics' + status = parseSolveLogArguments_printStats(argData, out_printStatsList); + CHECK_MSTATUS_AND_RETURN_IT(status); + + return status; +} + +MStatus parseSolveLogArguments_v2(const MArgDatabase &argData, + MStringArray &out_printStatsList, + LogLevel &out_logLevel, + MObject &out_resultsNodeObject, + bool &out_setMarkerDeviationAttrs) { + MStatus status = MStatus::kSuccess; + + // Get 'Results Node' + if (argData.isFlagSet(RESULTS_NODE_FLAG)) { + MString node_name; + status = argData.getFlagArgument(RESULTS_NODE_FLAG, 0, node_name); + CHECK_MSTATUS_AND_RETURN_IT(status); + status = getAsObject(node_name, out_resultsNodeObject); + CHECK_MSTATUS_AND_RETURN_IT(status); + } + + // Get 'Set Marker Deviation Attrs' flag. + out_setMarkerDeviationAttrs = SET_MARKER_DEVIATION_ATTRS_DEFAULT_VALUE; + if (argData.isFlagSet(SET_MARKER_DEVIATION_ATTRS_FLAG)) { + status = argData.getFlagArgument(SET_MARKER_DEVIATION_ATTRS_FLAG, 0, + out_setMarkerDeviationAttrs); + CHECK_MSTATUS_AND_RETURN_IT(status); + } + + // Get 'Log Level' + out_logLevel = LOG_LEVEL_DEFAULT_VALUE; + status = parseSolveLogArguments_logLevel(argData, out_logLevel); + CHECK_MSTATUS_AND_RETURN_IT(status); + + // Get 'Print Statistics' + status = parseSolveLogArguments_printStats(argData, out_printStatsList); + CHECK_MSTATUS_AND_RETURN_IT(status); return status; } diff --git a/src/mmSolver/cmd/arg_flags_solve_log.h b/src/mmSolver/cmd/arg_flags_solve_log.h index f0e36a272..a3cca200e 100644 --- a/src/mmSolver/cmd/arg_flags_solve_log.h +++ b/src/mmSolver/cmd/arg_flags_solve_log.h @@ -48,15 +48,35 @@ #define PRINT_STATS_FLAG "-pst" #define PRINT_STATS_FLAG_LONG "-printStatistics" +// Defines a Maya node that will be filled with attributes and values +// from the solve. For example, the number of iterations solved will +// be set as an integer on the given node. +#define RESULTS_NODE_FLAG "-rsn" +#define RESULTS_NODE_FLAG_LONG "-resultsNode" + +// If true, the solver will create and set attributes on the Markers for the +// deviation values calculated by the solver. +#define SET_MARKER_DEVIATION_ATTRS_FLAG "-smd" +#define SET_MARKER_DEVIATION_ATTRS_FLAG_LONG "-setMarkerDeviationAttrs" +#define SET_MARKER_DEVIATION_ATTRS_DEFAULT_VALUE true + namespace mmsolver { // Add flags for solver logging to the command syntax. -void createSolveLogSyntax(MSyntax &syntax); +void createSolveLogSyntax_v1(MSyntax &syntax); + +void createSolveLogSyntax_v2(MSyntax &syntax); // Parse arguments into solver logging. -MStatus parseSolveLogArguments(const MArgDatabase &argData, - MStringArray &out_printStatsList, - LogLevel &out_logLevel); +MStatus parseSolveLogArguments_v1(const MArgDatabase &argData, + MStringArray &out_printStatsList, + LogLevel &out_logLevel); + +MStatus parseSolveLogArguments_v2(const MArgDatabase &argData, + MStringArray &out_printStatsList, + LogLevel &out_logLevel, + MObject &out_resultsNodeObject, + bool &out_setMarkerDeviationAttrs); } // namespace mmsolver