def preSolve_updateProgress(prog_fn, status_fn): LOG.debug('preSolve_updateProgress') # Start up solver collectionutils.run_progress_func(prog_fn, 0) ts = solveresult.format_timestamp(time.time()) collectionutils.run_status_func(status_fn, 'Solve start (%s)' % ts) api_state.set_solver_running(True) return
def preSolve_updateProgress(prog_fn, status_fn): """ Initialise solver is running, and send info to the Maya GUI before a solve starts. :param prog_fn: Function to use for printing progress messages. :type prog_fn: callable or None :param status_fn: Function to use for printing status messages. :type status_fn: callable or None """ # Start up solver collectionutils.run_progress_func(prog_fn, 0) ts = solveresult.format_timestamp(time.time()) collectionutils.run_status_func(status_fn, 'Solve start (%s)' % ts) api_state.set_solver_running(True) return
def postSolve_setUpdateProgress(progress_min, progress_value, progress_max, solres, # SolveResult or None prog_fn, status_fn): LOG.debug( 'postSolve_setUpdateProgress: ' 'progress_min=%r ' 'progress_value=%r ' 'progress_max=%r ' 'solres=%r ' 'prog_fn=%r ' 'status_fn=%r', progress_min, progress_value, progress_max, solres, prog_fn, status_fn) stop_solving = False # Update progress ratio = float(progress_value) / float(progress_max) percent = float(progress_min) + (ratio * (100.0 - progress_min)) collectionutils.run_progress_func(prog_fn, int(percent)) # Check if the user wants to stop solving. if solres is None: cmd_cancel = False else: cmd_cancel = solres.get_user_interrupted() gui_cancel = api_state.get_user_interrupt() if cmd_cancel is True or gui_cancel is True: msg = 'Cancelled by User' api_state.set_user_interrupt(False) collectionutils.run_status_func(status_fn, 'WARNING: ' + msg) LOG.warning(msg) stop_solving = True if (solres is not None) and (solres.get_success() is False): msg = 'Solver failed!!!' collectionutils.run_status_func(status_fn, 'ERROR: ' + msg) LOG.error(msg) return stop_solving
def execute( self, verbose=False, # TODO: Refactor arguments into a 'Preferences' object # to hold the different preferences. refresh=False, force_update=False, do_isolate=False, # TODO: Allow a dict to be passed to the function # specifying the object type and the visibility status # during solving. This allows us to turn on/off any # object type during solving. If an argument is not # given or is None, the object type visibility will # not be changed. display_image_plane=False, prog_fn=None, status_fn=None, info_fn=None): """ Compile the collection, then pass that data to the 'mmSolver' command. The mmSolver command will return a list of strings, which will then be passed to the SolveResult class so the user can query the raw data using an interface. :param verbose: Print extra solver information while a solve is running. :type verbose: bool :param refresh: Should the solver refresh the viewport while solving? :type refresh: bool :param force_update: Force updating the DG network, to help the solver in case of a Maya evaluation DG bug. :type force_update: bool :param do_isolate: Isolate only solving objects while running solve. :type do_isolate: bool :param display_image_plane: Display Image Planes while solving? :type display_image_plane: bool :param prog_fn: The function used report progress messages to the user. :type prog_fn: callable or None :param status_fn: The function used to report status messages to the user. :type status_fn: callable or None :param info_fn: The function used to report information messages to the user. :type info_fn: callable or None :return: List of SolveResults from the executed collection. :rtype: [SolverResult, ..] """ start_time = time.time() # Ensure the plug-in is loaded, so we fail before trying to run. api_utils.load_plugin() # If 'refresh' is 'on' change all viewports to 'isolate # selected' on only the markers and bundles being solved. This # will speed up computations, especially per-frame solving as # it will not re-compute any invisible nodes (such as rigs or # image planes). panel_objs = {} panel_img_pl_vis = {} panels = viewport_utils.get_all_model_panels() if refresh is True: for panel in panels: if do_isolate is True: state = maya.cmds.isolateSelect(panel, query=True, state=True) nodes = None if state is True: nodes = viewport_utils.get_isolated_nodes(panel) panel_objs[panel] = nodes panel_img_pl_vis[ panel] = viewport_utils.get_image_plane_visibility(panel) # Save current frame, to revert to later on. cur_frame = maya.cmds.currentTime(query=True) try: collectionutils.run_progress_func(prog_fn, 0) ts = solveresult.format_timestamp(time.time()) collectionutils.run_status_func(status_fn, 'Solve start (%s)' % ts) api_state.set_solver_running(True) # Check for validity solres_list = [] if self.is_valid() is False: LOG.warning('Collection not valid: %r', self.get_node()) return solres_list kwargs_list = self._compile() collectionutils.run_progress_func(prog_fn, 1) # Isolate all nodes used in all of the kwargs to be run. # Note; This assumes the isolated objects are visible, but # they may actually be hidden. if refresh is True: s = time.time() if do_isolate is True: isolate_nodes = set() for kwargs in kwargs_list: isolate_nodes |= collectionutils.generate_isolate_nodes( kwargs) if len(isolate_nodes) == 0: raise excep.NotValid isolate_node_list = list(isolate_nodes) for panel in panels: viewport_utils.set_isolated_nodes( panel, isolate_node_list, True) for panel in panels: viewport_utils.set_image_plane_visibility( panel, display_image_plane) e = time.time() LOG.debug('Perform Pre-Isolate; time=%r', e - s) # Set the first current time to the frame before current. # This is to help trigger evaluations on the 'current # frame', if the current frame is the same as the first # frame. frame_list = [] for kwargs in kwargs_list: frame_list += kwargs.get('frame', []) frame_list = list(set(frame_list)) frame_list = list(sorted(frame_list)) is_whole_solve_single_frame = len(frame_list) == 1 if is_whole_solve_single_frame is False: s = time.time() maya.cmds.currentTime( cur_frame - 1, edit=True, update=force_update, ) e = time.time() LOG.debug('Update previous of current time; time=%r', e - s) # Run Solver... marker_nodes = [] start = 0 total = len(kwargs_list) for i, kwargs in enumerate(kwargs_list): frame = kwargs.get('frame') collectionutils.run_status_func(info_fn, 'Evaluating frames %r' % frame) if frame is None or len(frame) == 0: raise excep.NotValid # Write solver flags to a debug file. debug_file_path = kwargs.get('debugFile', None) if debug_file_path is not None: options_file_path = debug_file_path.replace( '.log', '.flags') text = pprint.pformat(kwargs) with open(options_file_path, 'w') as file_: file_.write(text) # HACK: Overriding the verbosity, irrespective of what # the solver verbosity value is set to. if verbose is True: kwargs['verbose'] = True # HACK for single frame solves. save_node_attrs = [] is_single_frame = collectionutils.is_single_frame(kwargs) if is_single_frame is True: save_node_attrs = collectionutils.disconnect_animcurves( kwargs) # Run Solver Maya plug-in command solve_data = maya.cmds.mmSolver(**kwargs) # Revert special HACK for single frame solves if is_single_frame is True: collectionutils.reconnect_animcurves( kwargs, save_node_attrs) # Create SolveResult. solres = solveresult.SolveResult(solve_data) solres_list.append(solres) # Update progress ratio = float(i) / float(total) percent = float(start) + (ratio * (100.0 - start)) collectionutils.run_progress_func(prog_fn, int(percent)) # Check if the user wants to stop solving. cmd_cancel = solres.get_user_interrupted() gui_cancel = api_state.get_user_interrupt() if cmd_cancel is True or gui_cancel is True: msg = 'Cancelled by User' api_state.set_user_interrupt(False) collectionutils.run_status_func(status_fn, 'WARNING: ' + msg) LOG.warning(msg) break if solres.get_success() is False: msg = 'Solver failed!!!' collectionutils.run_status_func(status_fn, 'ERROR: ' + msg) LOG.error(msg) marker_nodes += [x[0] for x in kwargs.get('marker', [])] # Refresh the Viewport. if refresh is True: # TODO: If we solve per-frame without "refresh" # on, then we get wacky solvers # per-frame. Interestingly, the 'force_update' # does not seem to make a difference, just the # 'maya.cmds.refresh' call. # # Test scene file: # ./tests/data/scenes/mmSolverBasicSolveD_before.ma s = time.time() maya.cmds.currentTime( frame[0], edit=True, update=force_update, ) maya.cmds.refresh() e = time.time() LOG.debug('Refresh Viewport; time=%r', e - s) finally: if refresh is True: s = time.time() for panel, objs in panel_objs.items(): if objs is None: # No original objects, disable 'isolate # selected' after resetting the objects. if do_isolate is True: viewport_utils.set_isolated_nodes(panel, [], False) img_pl_vis = panel_img_pl_vis.get(panel, True) viewport_utils.set_image_plane_visibility( panel, img_pl_vis) else: if do_isolate is True: viewport_utils.set_isolated_nodes( panel, list(objs), True) e = time.time() LOG.debug('Finally; reset isolate selected; time=%r', e - s) collectionutils.run_status_func(status_fn, 'Solve Ended') collectionutils.run_progress_func(prog_fn, 100) api_state.set_solver_running(False) maya.cmds.currentTime(cur_frame, edit=True, update=True) # Store output information of the solver. end_time = time.time() duration = end_time - start_time self._set_last_solve_timestamp(end_time) self._set_last_solve_duration(duration) self._set_last_solve_results(solres_list) return solres_list
def execute(col, options=None, validate_mode=None, log_level=None, prog_fn=None, status_fn=None, info_fn=None): """ Compile the collection, then pass that data to the 'mmSolver' command. The mmSolver command will return a list of strings, which will then be passed to the SolveResult class so the user can query the raw data using an interface. :param col: The Collection to execute. :type col: Collection :param options: The options for the execution. :type options: ExecuteOptions :param validate_mode: How should the solve validate? Must be one of the VALIDATE_MODE_VALUE_LIST values. :type validate_mode: str or None :param log_level: The log level for the execution. :type log_level: str :param prog_fn: The function used report progress messages to the user. :type prog_fn: callable or None :param status_fn: The function used to report status messages to the user. :type status_fn: callable or None :param info_fn: The function used to report information messages to the user. :type info_fn: callable or None :return: List of SolveResults from the executed collection. :rtype: [SolverResult, ..] """ if options is None: options = createExecuteOptions() if validate_mode is None: validate_mode = const.VALIDATE_MODE_NONE_VALUE assert validate_mode in const.VALIDATE_MODE_VALUE_LIST if log_level is None: log_level = const.LOG_LEVEL_DEFAULT assert isinstance(log_level, (str, unicode)) start_time = time.time() # Ensure the plug-in is loaded, so we fail before trying to run. api_utils.load_plugin() assert 'mmSolver' in dir(maya.cmds) vp2_state = viewport_utils.get_viewport2_active_state() panels = viewport_utils.get_all_model_panels() panel_objs, panel_node_type_vis = preSolve_queryViewportState( options, panels ) # Save current frame, to revert to later on. cur_frame = maya.cmds.currentTime(query=True) try: if options.disable_viewport_two is True: viewport_utils.set_viewport2_active_state(False) preSolve_updateProgress(prog_fn, status_fn) # Check for validity and compile actions. solres_list = [] withtest = validate_mode in [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() try: s = time.time() action_list, vaction_list = api_compile.collection_compile( col_node, sol_list, mkr_list, attr_list, withtest=withtest, prog_fn=prog_fn, status_fn=status_fn ) e = time.time() LOG.debug('compile time (execute): %r', e - s) except excep.NotValid as e: LOG.warning(e) return solres_list collectionutils.run_progress_func(prog_fn, 1) if validate_mode == 'pre_validate': valid, msg, metrics_list = _run_validate_action_list(vaction_list) if valid is not True: LOG.warning(msg) return solres_list # Prepare frame solve preSolve_setIsolatedNodes(action_list, options, panels) preSolve_triggerEvaluation(action_list, cur_frame, options) # Run Solver Actions... start = 0 total = len(action_list) for i, (action, vaction) in enumerate(zip(action_list, vaction_list)): if isinstance(vaction, api_action.Action) and validate_mode == 'at_runtime': valid, message, metrics = _run_validate_action(vaction) if valid is not True: LOG.warn(message) return func, args, kwargs = api_action.action_to_components(action) func_is_mmsolver = api_action.action_func_is_mmSolver(action) save_node_attrs = None is_single_frame = None if func_is_mmsolver is True: frame = kwargs.get('frame') collectionutils.run_status_func(info_fn, 'Evaluating frames %r' % frame) if frame is None or len(frame) == 0: raise excep.NotValid # Write solver flags to a debug file. debug_file_path = kwargs.get('debugFile', None) if debug_file_path is not None: options_file_path = debug_file_path.replace('.log', '.flags') text = pprint.pformat(kwargs) with open(options_file_path, 'w') as file_: file_.write(text) # Overriding the verbosity, irrespective of what # the solver verbosity value is set to. if log_level is not None and log_level.lower() == 'verbose': kwargs['verbose'] = True # HACK for single frame solves. save_node_attrs = [] is_single_frame = collectionutils.is_single_frame(kwargs) if is_single_frame is True: save_node_attrs = collectionutils.disconnect_animcurves(kwargs) # Run Solver Maya plug-in command solve_data = func(*args, **kwargs) # Revert special HACK for single frame solves if func_is_mmsolver is True: if is_single_frame is True: collectionutils.reconnect_animcurves(kwargs, save_node_attrs) # Create SolveResult. solres = None if solve_data is not None and func_is_mmsolver is True: solres = solveresult.SolveResult(solve_data) solres_list.append(solres) # Update Progress interrupt = postSolve_setUpdateProgress( start, i, total, solres, prog_fn, status_fn ) if interrupt is True: break # Refresh the Viewport. if func_is_mmsolver is True: frame = kwargs.get('frame') postSolve_refreshViewport(options, frame) finally: postSolve_setViewportState( options, panel_objs, panel_node_type_vis ) collectionutils.run_status_func(status_fn, 'Solve Ended') collectionutils.run_progress_func(prog_fn, 100) api_state.set_solver_running(False) if options.disable_viewport_two is True: viewport_utils.set_viewport2_active_state(vp2_state) maya.cmds.currentTime( cur_frame, edit=True, update=options.force_update ) # Store output information of the solver. end_time = time.time() duration = end_time - start_time col._set_last_solve_timestamp(end_time) col._set_last_solve_duration(duration) col._set_last_solve_results(solres_list) return solres_list
def postSolve_setUpdateProgress(progress_min, progress_value, progress_max, solres, prog_fn, status_fn): """ Update the Maya GUI with progress information, and detects users wanting to cancel the solve. :param progress_min: Minimum progress number possible. Usually the number is 0. :type progress_min: int :param progress_value: The actual progress value. The value is usually between 0 and 100 (inclusive). :type progress_value: int :param progress_max: THe maximum progress number possible. Usually the number is 100. :type progress_max: int :param solres: The SolveResult object for the last solved state. :type solres: SolveResult or None :param prog_fn: The function used report progress messages to the user. :type prog_fn: callable or None :param status_fn: The function used to report status messages to the user. :type status_fn: callable or None :returns: Should the solver stop executing or not? Has the user cancelled the solve? :rtype: bool """ stop_solving = False # Update progress ratio = float(progress_value) / float(progress_max) percent = float(progress_min) + (ratio * (100.0 - progress_min)) collectionutils.run_progress_func(prog_fn, int(percent)) # Check if the user wants to stop solving. if solres is None: cmd_cancel = False else: cmd_cancel = solres.get_user_interrupted() gui_cancel = api_state.get_user_interrupt() if cmd_cancel is True or gui_cancel is True: msg = 'Cancelled by User' api_state.set_user_interrupt(False) collectionutils.run_status_func(status_fn, 'WARNING: ' + msg) LOG.warn(msg) stop_solving = True if (solres is not None) and (solres.get_success() is False): msg = 'Solver failed!!!' collectionutils.run_status_func(status_fn, 'ERROR: ' + msg) LOG.error(msg) return stop_solving
def execute(col, options=None, validate_mode=None, log_level=None, prog_fn=None, status_fn=None, info_fn=None): """ Compile the collection, then pass that data to the 'mmSolver' command. The mmSolver command will return a list of strings, which will then be passed to the SolveResult class so the user can query the raw data using an interface. :param col: The Collection to execute. :type col: Collection :param options: The options for the execution. :type options: ExecuteOptions :param validate_mode: How should the solve validate? Must be one of the VALIDATE_MODE_VALUE_LIST values. :type validate_mode: str or None :param log_level: The log level for the execution. :type log_level: str :param prog_fn: The function used report progress messages to the user. :type prog_fn: callable or None :param status_fn: The function used to report status messages to the user. :type status_fn: callable or None :param info_fn: The function used to report information messages to the user. :type info_fn: callable or None :return: List of SolveResults from the executed collection. :rtype: [SolverResult, ..] """ if options is None: options = createExecuteOptions() if validate_mode is None: validate_mode = const.VALIDATE_MODE_PRE_VALIDATE_VALUE assert validate_mode in const.VALIDATE_MODE_VALUE_LIST if log_level is None: log_level = const.LOG_LEVEL_DEFAULT assert isinstance(log_level, pycompat.TEXT_TYPE) validate_runtime = validate_mode == const.VALIDATE_MODE_AT_RUNTIME_VALUE validate_before = validate_mode == const.VALIDATE_MODE_PRE_VALIDATE_VALUE start_time = time.time() # Ensure the plug-in is loaded, so we (do not) fail before trying # to run. api_utils.load_plugin() assert 'mmSolver' in dir(maya.cmds) vp2_state = viewport_utils.get_viewport2_active_state() current_eval_manager_mode = maya.cmds.evaluationManager(query=True, mode=True) panels = viewport_utils.get_all_model_panels() panel_objs, panel_node_type_vis = preSolve_queryViewportState( options, panels) # Save scene state, to revert to later on. cur_frame = maya.cmds.currentTime(query=True) prev_auto_key_state = maya.cmds.autoKeyframe(query=True, state=True) prev_cycle_check = maya.cmds.cycleCheck(query=True, evaluation=True) # State information needed to revert reconnect animation curves in # 'finally' block. kwargs = {} save_node_attrs = [] func_is_mmsolver = False is_single_frame = False try: if options.disable_viewport_two is True: viewport_utils.set_viewport2_active_state(False) maya.cmds.autoKeyframe(edit=True, state=False) maya.cmds.evaluationManager(mode='off') maya.cmds.cycleCheck(evaluation=False) preSolve_updateProgress(prog_fn, status_fn) # Check for validity and compile actions. solres_list = [] withtest = validate_mode in [ const.VALIDATE_MODE_PRE_VALIDATE_VALUE, const.VALIDATE_MODE_AT_RUNTIME_VALUE ] sol_list = col.get_solver_list() mkr_list = col.get_marker_list() attr_list = col.get_attribute_list() try: action_list, vaction_list = api_compile.collection_compile( col, sol_list, mkr_list, attr_list, withtest=withtest, prog_fn=prog_fn, status_fn=status_fn) except excep.NotValid as e: LOG.warn(e) return solres_list collectionutils.run_progress_func(prog_fn, 1) vaction_state_list = [] if validate_before is True: vaction_state_list = _run_validate_action_list(vaction_list) assert len(vaction_list) == len(vaction_state_list) # Prepare frame solve preSolve_setIsolatedNodes(action_list, options, panels) preSolve_triggerEvaluation(action_list, cur_frame, options) # Ensure prediction attributes are created and initialised. collectionutils.set_initial_prediction_attributes( col, attr_list, cur_frame) # Run Solver Actions... message_hashes = set() start = 0 total = len(action_list) number_of_solves = 0 for i, (action, vaction) in enumerate(zip(action_list, vaction_list)): state = None if len(vaction_state_list) > 0: # We have pre-computed the state list. state = vaction_state_list[i] if isinstance(vaction, api_action.Action) and validate_runtime: # We will calculate the state just-in-time. state = _run_validate_action(vaction) if state is not None: if state.status != const.ACTION_STATUS_SUCCESS: assert isinstance(state, ActionState) h = hash(state.message) if h not in message_hashes: LOG.warn(state.message) message_hashes.add(h) # Skip this action, since the test failed. continue func, args, kwargs = api_action.action_to_components(action) func_is_mmsolver = api_action.action_func_is_mmSolver(action) if func_is_mmsolver is True: frame = kwargs.get('frame') collectionutils.run_status_func(info_fn, 'Evaluating frames %r' % frame) if frame is None or len(frame) == 0: raise excep.NotValid # Write solver flags to a debug file. debug_file_path = kwargs.get('debugFile', None) if debug_file_path is not None: options_file_path = debug_file_path.replace( '.log', '.flags') text = pprint.pformat(kwargs) with open(options_file_path, 'w') as file_: file_.write(text) # Overriding the verbosity, irrespective of what the # solver verbosity value is set to. kwargs['verbose'] = False if log_level is not None and log_level.lower() == 'verbose': kwargs['verbose'] = True # HACK for single frame solves. is_single_frame = collectionutils.is_single_frame(kwargs) if is_single_frame is True: save_node_attrs = collectionutils.disconnect_animcurves( kwargs) else: # Reset the data structure so in the 'finally' # block we can detect animcurves are not needing # to be reset. save_node_attrs = [] # Run Solver Maya plug-in command solve_data = func(*args, **kwargs) # Revert special HACK for single frame solves if func_is_mmsolver is True: if is_single_frame is True: collectionutils.reconnect_animcurves( kwargs, save_node_attrs) # Reset the data structure so in the 'finally' # block we can detect animcurves are not needing # to be reset. save_node_attrs = [] # Create SolveResult. solres = None if solve_data is not None and func_is_mmsolver is True: solres = solveresult.SolveResult(solve_data) solres_list.append(solres) if func_is_mmsolver is True and solres.get_success() is True: frame = kwargs.get('frame') if frame is None or len(frame) == 0: raise excep.NotValid single_frame = frame[0] if number_of_solves == 0: collectionutils.set_initial_prediction_attributes( col, attr_list, single_frame) # Count number of solves, so we don't need to set the # initial prediction attributes again. number_of_solves += 1 # Calculate the mean, variance values, and predict the # next attribute value. collectionutils.compute_attribute_value_prediction( col, attr_list, single_frame, ) # Update Progress interrupt = postSolve_setUpdateProgress(start, i, total, solres, prog_fn, status_fn) if interrupt is True: break # Refresh the Viewport. if func_is_mmsolver is True: frame = kwargs.get('frame') 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 len(save_node_attrs): collectionutils.reconnect_animcurves(kwargs, save_node_attrs) postSolve_setViewportState(options, panel_objs, panel_node_type_vis) collectionutils.run_status_func(status_fn, 'Solve Ended') collectionutils.run_progress_func(prog_fn, 100) maya.cmds.evaluationManager(mode=current_eval_manager_mode[0]) maya.cmds.cycleCheck(evaluation=prev_cycle_check) maya.cmds.autoKeyframe(edit=True, state=prev_auto_key_state) api_state.set_solver_running(False) if options.disable_viewport_two is True: viewport_utils.set_viewport2_active_state(vp2_state) maya.cmds.currentTime(cur_frame, edit=True, update=options.force_update) # Store output information of the solver. end_time = time.time() duration = end_time - start_time col._set_last_solve_timestamp(end_time) col._set_last_solve_duration(duration) col._set_last_solve_results(solres_list) return solres_list