Skip to content

Commit 87b05da

Browse files
Core Solver - Pre-compute Valid Frames
The frames that can or cannot be solved are validated and culled before a proper solve can be started. This is intended to avoid invalid solves from attempting to be solved, and overall produce a more robust solver. We also move the logic of whether the solver is valid from the Python "actionstate.py" file to the C++ core, which gives us more information to make better and more informed decisions about solver validity. This is partially related to gitHub issue #174.
1 parent dbf0545 commit 87b05da

27 files changed

+996
-172
lines changed

python/mmSolver/_api/_execute/actionstate.py

+18-14
Original file line numberDiff line numberDiff line change
@@ -99,8 +99,8 @@ def run_validate_action(vaction):
9999
frames = list(sorted(vkwargs.get('frame', [])))
100100
num_frames = len(frames)
101101
if num_frames == 0 and vfunc_is_mmsolver is True:
102-
msg = 'Failed to validate number of frames: param=%r errors=%r frames=%r'
103-
message = msg % (num_param, num_err, num_frames)
102+
msg = 'Failed to validate frames: frames=%r'
103+
message = msg % num_frames
104104
state = create_action_state(
105105
status=const.ACTION_STATUS_FAILED,
106106
message=message,
@@ -115,8 +115,8 @@ def run_validate_action(vaction):
115115
solve_data = vfunc(*vargs, **vkwargs)
116116

117117
if vfunc_is_mmsolver is False:
118-
msg = 'Validated parameters, errors and frames: param=%r errors=%r frames=%r'
119-
message = msg % (num_param, num_err, num_frames)
118+
msg = 'Validated frames: frames=%r'
119+
message = msg % num_frames
120120
state = create_action_state(
121121
status=const.ACTION_STATUS_SUCCESS,
122122
message=message,
@@ -130,24 +130,28 @@ def run_validate_action(vaction):
130130
if vfunc_is_mmsolver_v2 is True:
131131
solve_data = vkwargs['resultsNode']
132132
if vfunc_is_camera_solve is True:
133-
if const.SOLVER_VERSION_DEFAULT == const.SOLVER_VERSION_TWO:
134-
# Get the collection node given to the camera solve.
135-
solve_data = vargs[0]
133+
# NOTE: This assumes that the camera solver is using
134+
# const.SOLVER_VERSION_TWO.
135+
#
136+
# Get the collection node given to the camera solve.
137+
solve_data = vargs[0]
136138

137139
solres = solveresult.SolveResult(solve_data)
138140

139141
print_stats = solres.get_print_stats()
140142
num_param = print_stats.get('number_of_parameters', 0)
141143
num_err = print_stats.get('number_of_errors', 0)
142-
if num_param == 0 or num_err == 0 or num_param > num_err:
143-
msg = (
144-
'Invalid parameters and errors (param=%r errors=%r frames=%r), '
145-
'skipping solve: %r'
146-
)
147-
message = msg % (num_param, num_err, num_frames, list(sorted(frames)))
144+
solver_valid_frames = solres.get_solver_valid_frame_list()
145+
if len(solver_valid_frames) == 0:
146+
# This error should be raised if ALL the frames in the
147+
# solve are invalid, and if they are invalid then the frames
148+
# should just be skipped and the solve should be continued.
149+
solver_invalid_frames = solres.get_solver_invalid_frame_list()
150+
assert len(solver_invalid_frames) == len(frames)
151+
msg = 'All frames are invalid, skipping solve: %r' % list(sorted(frames))
148152
state = create_action_state(
149153
status=const.ACTION_STATUS_FAILED,
150-
message=message,
154+
message=msg,
151155
error_number=num_err,
152156
parameter_number=num_param,
153157
frames_number=num_frames,

python/mmSolver/_api/solveresult.py

+79-5
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,9 @@
2424
from __future__ import print_function
2525

2626
import collections
27-
import math
2827
import datetime
28+
import math
29+
import pprint
2930

3031
import maya.cmds
3132

@@ -51,9 +52,9 @@ def parse_command_result(cmd_result):
5152
data = collections.defaultdict(list)
5253
for res in cmd_result:
5354
assert isinstance(res, pycompat.TEXT_TYPE)
54-
splt = res.partition(KEY_VALUE_SEP_CHAR)
55-
key = splt[0]
56-
value = splt[-1]
55+
split = res.partition(KEY_VALUE_SEP_CHAR)
56+
key = split[0]
57+
value = split[-1]
5758
if len(key) == 0:
5859
continue
5960
if SPLIT_SEP_CHAR in value:
@@ -142,6 +143,33 @@ def _string_get_solver_stats(input_data):
142143
return solver_stats
143144

144145

146+
def _string_get_solver_frames_stats(input_data):
147+
name_keys = [
148+
('number_of_valid_frames', 'number_of_valid_frames', int, None),
149+
('number_of_invalid_frames', 'number_of_invalid_frames', int, None),
150+
('valid_frames', 'valid_frames', list, int),
151+
('invalid_frames', 'invalid_frames', list, int),
152+
]
153+
index = 0
154+
solver_frames = {}
155+
for name, key, outer_type, inner_type in name_keys:
156+
value = input_data.get(key)
157+
if value is None:
158+
continue
159+
if inner_type is None:
160+
v = _convert_to(name, key, outer_type, value, index)
161+
solver_frames[name] = v
162+
else:
163+
if len(value) == 1:
164+
value = value[0]
165+
values = []
166+
for v in value:
167+
if len(v) > 0:
168+
values.append(inner_type(v))
169+
solver_frames[name] = values
170+
return solver_frames
171+
172+
145173
def _string_get_error_stats(input_data):
146174
name_keys = [
147175
('initial', 'error_initial', float),
@@ -254,6 +282,15 @@ def _get_maya_attr_anim_curve(node, attr_name, existing_attrs):
254282
return anim_curves[0]
255283

256284

285+
def _get_node_frame_list(node, attr_name, existing_attrs):
286+
anim_curve = _get_maya_attr_anim_curve(node, attr_name, existing_attrs)
287+
if anim_curve is None:
288+
return {}
289+
290+
keyframe_times = maya.cmds.keyframe(anim_curve, query=True, timeChange=True) or []
291+
return set(keyframe_times)
292+
293+
257294
def _get_node_frame_error_list(node, attr_name, existing_attrs):
258295
anim_curve = _get_maya_attr_anim_curve(node, attr_name, existing_attrs)
259296
if anim_curve is None:
@@ -343,6 +380,21 @@ def _node_get_solver_stats(node, existing_attrs):
343380
return data
344381

345382

383+
def _node_get_solver_frames_stats(node, existing_attrs):
384+
assert maya.cmds.objExists(node) is True
385+
data = {
386+
'number_of_valid_frames': _get_maya_attr(
387+
node, 'number_of_valid_frames', int, existing_attrs
388+
),
389+
'number_of_invalid_frames': _get_maya_attr(
390+
node, 'number_of_invalid_frames', int, existing_attrs
391+
),
392+
'valid_frames': _get_node_frame_list(node, 'valid_frames', existing_attrs),
393+
'invalid_frames': _get_node_frame_list(node, 'invalid_frames', existing_attrs),
394+
}
395+
return data
396+
397+
346398
def _node_get_print_stats(node, existing_attrs):
347399
assert maya.cmds.objExists(node) is True
348400
data = {
@@ -414,8 +466,8 @@ def __init__(self, *args, **kwargs):
414466
self._solver_stats = _node_get_solver_stats(node, existing_attrs)
415467
self._error_stats = _node_get_error_stats(node, existing_attrs)
416468
self._timer_stats = _node_get_timer_stats(node, existing_attrs)
469+
self._solver_frames_stats = _node_get_solver_frames_stats(data)
417470
self._print_stats = _node_get_print_stats(node, existing_attrs)
418-
419471
self._per_frame_error = _node_get_per_frame_error(node, existing_attrs)
420472
self._per_marker_per_frame_error = _node_get_per_marker_per_frame_error(
421473
node, existing_attrs
@@ -428,6 +480,7 @@ def __init__(self, *args, **kwargs):
428480
self._solver_stats = _string_get_solver_stats(data)
429481
self._error_stats = _string_get_error_stats(data)
430482
self._timer_stats = _string_get_timer_stats(data)
483+
self._solver_frames_stats = _string_get_solver_frames_stats(data)
431484
self._print_stats = _string_get_print_stats(data)
432485
self._per_marker_per_frame_error = _string_get_error_per_marker_per_frame(
433486
data
@@ -509,6 +562,13 @@ def get_print_stats(self):
509562
"""
510563
return self._print_stats.copy()
511564

565+
def get_solver_frames_stats(self):
566+
"""
567+
Details of internal statistics that can be gathered and
568+
printed out.
569+
"""
570+
return self._solver_frames_stats.copy()
571+
512572
def get_frame_list(self):
513573
"""
514574
The list of frames that this solve result contains.
@@ -540,6 +600,20 @@ def get_marker_error_list(self, marker_node=None):
540600
v = self._per_marker_per_frame_error.get(marker_node)
541601
return v
542602

603+
def get_solver_valid_frame_list(self):
604+
"""
605+
The list of valid frames that the solver can solve.
606+
"""
607+
frame_list = self._solver_frames_stats.get('valid_frames', [])
608+
return list(sorted(frame_list))
609+
610+
def get_solver_invalid_frame_list(self):
611+
"""
612+
The list of invalid frames that the solver can solve.
613+
"""
614+
frame_list = self._solver_frames_stats.get('invalid_frames', [])
615+
return list(sorted(frame_list))
616+
543617

544618
def combine_timer_stats(solres_list):
545619
"""

0 commit comments

Comments
 (0)