Skip to content

Commit

Permalink
Allow solver to use the 'mmSolver_v2' command.
Browse files Browse the repository at this point in the history
... but mmSolver_v2 command is not used by default, yet.
  • Loading branch information
david-cattermole committed Dec 31, 2022
1 parent d8d5522 commit be8813f
Show file tree
Hide file tree
Showing 23 changed files with 2,135 additions and 280 deletions.
13 changes: 12 additions & 1 deletion python/mmSolver/_api/_execute/actionstate.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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)
Expand Down
89 changes: 79 additions & 10 deletions python/mmSolver/_api/_execute/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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:
Expand All @@ -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()
Expand All @@ -302,13 +341,24 @@ 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)

# 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)
Expand Down Expand Up @@ -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)

Expand All @@ -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:
Expand Down Expand Up @@ -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)
Expand All @@ -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
Expand Down Expand Up @@ -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
)
Expand Down
24 changes: 24 additions & 0 deletions python/mmSolver/_api/_execute/postsolve.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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)
56 changes: 56 additions & 0 deletions python/mmSolver/_api/_execute/presolve.py
Original file line number Diff line number Diff line change
Expand Up @@ -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()

Expand Down Expand Up @@ -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
58 changes: 58 additions & 0 deletions python/mmSolver/_api/constant.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
3 changes: 3 additions & 0 deletions python/mmSolver/_api/solvercamera.py
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
Loading

0 comments on commit be8813f

Please sign in to comment.