Skip to content
GitLab
Explore
Sign in
Primary navigation
Search or go to…
Project
B
blender-addons-contrib
Manage
Activity
Members
Labels
Plan
Issues
Issue boards
Milestones
Wiki
Code
Merge requests
Repository
Branches
Commits
Tags
Repository graph
Compare revisions
Snippets
Build
Pipelines
Jobs
Pipeline schedules
Artifacts
Deploy
Releases
Container registry
Model registry
Operate
Environments
Monitor
Incidents
Analyze
Value stream analytics
Contributor analytics
CI/CD analytics
Repository analytics
Model experiments
Help
Help
Support
GitLab documentation
Compare GitLab plans
GitLab community forum
Contribute to GitLab
Provide feedback
Keyboard shortcuts
?
Snippets
Groups
Projects
Show more breadcrumbs
blender
blender-addons-contrib
Commits
dbcdfeec
Commit
dbcdfeec
authored
Dec 1, 2011
by
Bart Crouch
Browse files
Options
Downloads
Patches
Plain Diff
Moving from upload to contrib.
parent
245d0cdc
No related branches found
No related tags found
No related merge requests found
Changes
1
Show whitespace changes
Inline
Side-by-side
Showing
1 changed file
animation_motion_trail.py
+1736
-0
1736 additions, 0 deletions
animation_motion_trail.py
with
1736 additions
and
0 deletions
animation_motion_trail.py
0 → 100644
+
1736
−
0
View file @
dbcdfeec
# ##### BEGIN GPL LICENSE BLOCK #####
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program 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 General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software Foundation,
# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
# ##### END GPL LICENSE BLOCK #####
# <pep8 compliant>
bl_info
=
{
'
name
'
:
"
Motion Trail
"
,
'
author
'
:
"
Bart Crouch
"
,
'
version
'
:
(
3
,
1
,
0
),
'
blender
'
:
(
2
,
6
,
0
),
'
api
'
:
42181
,
'
location
'
:
"
View3D > Toolbar > Motion Trail tab
"
,
'
warning
'
:
""
,
'
description
'
:
"
Display and edit motion trails in the 3d-view
"
,
'
wiki_url
'
:
"
http://wiki.blender.org/index.php/Extensions:2.5/
"
\
"
Py/Scripts/Animation/Motion_Trail
"
,
'
tracker_url
'
:
"
http://projects.blender.org/tracker/index.php?
"
\
"
func=detail&aid=26374
"
,
'
category
'
:
'
Animation
'
}
import
bgl
import
blf
import
bpy
from
bpy_extras
import
view3d_utils
import
math
import
mathutils
# fake fcurve class, used if no fcurve is found for a path
class
fake_fcurve
():
def
__init__
(
self
,
object
,
index
,
rotation
=
False
,
scale
=
False
):
# location
if
not
rotation
and
not
scale
:
self
.
loc
=
object
.
location
[
index
]
# scale
elif
scale
:
self
.
loc
=
object
.
scale
[
index
]
# rotation
elif
rotation
==
'
QUATERNION
'
:
self
.
loc
=
object
.
rotation_quaternion
[
index
]
elif
rotation
==
'
AXIS_ANGLE
'
:
self
.
loc
=
object
.
rotation_axis_angle
[
index
]
else
:
self
.
loc
=
object
.
rotation_euler
[
index
]
self
.
keyframe_points
=
[]
def
evaluate
(
self
,
frame
):
return
(
self
.
loc
)
def
range
(
self
):
return
([])
# get location curves of the given object
def
get_curves
(
object
,
child
=
False
):
if
object
.
animation_data
and
object
.
animation_data
.
action
:
action
=
object
.
animation_data
.
action
if
child
:
# posebone
curves
=
[
fc
for
fc
in
action
.
fcurves
if
len
(
fc
.
data_path
)
>=
14
\
and
fc
.
data_path
[
-
9
:]
==
'
.location
'
and
\
child
.
name
in
fc
.
data_path
.
split
(
"
\"
"
)]
else
:
# normal object
curves
=
[
fc
for
fc
in
action
.
fcurves
if
\
fc
.
data_path
==
'
location
'
]
elif
object
.
animation_data
and
object
.
animation_data
.
use_nla
:
curves
=
[]
strips
=
[]
for
track
in
object
.
animation_data
.
nla_tracks
:
not_handled
=
[
s
for
s
in
track
.
strips
]
while
not_handled
:
current_strip
=
not_handled
.
pop
(
-
1
)
if
current_strip
.
action
:
strips
.
append
(
current_strip
)
if
current_strip
.
strips
:
# meta strip
not_handled
+=
[
s
for
s
in
current_strip
.
strips
]
for
strip
in
strips
:
if
child
:
# posebone
curves
=
[
fc
for
fc
in
strip
.
action
.
fcurves
if
\
len
(
fc
.
data_path
)
>=
14
and
fc
.
data_path
[
-
9
:]
==
'
.location
'
\
and
child
.
name
in
fc
.
data_path
.
split
(
"
\"
"
)]
else
:
# normal object
curves
=
[
fc
for
fc
in
strip
.
action
.
fcurves
if
\
fc
.
data_path
==
'
location
'
]
if
curves
:
# use first strip with location fcurves
break
else
:
# should not happen?
curves
=
[]
# ensure we have three curves per object
fcx
=
None
fcy
=
None
fcz
=
None
for
fc
in
curves
:
if
fc
.
array_index
==
0
:
fcx
=
fc
elif
fc
.
array_index
==
1
:
fcy
=
fc
elif
fc
.
array_index
==
2
:
fcz
=
fc
if
fcx
==
None
:
fcx
=
fake_fcurve
(
object
,
0
)
if
fcy
==
None
:
fcy
=
fake_fcurve
(
object
,
1
)
if
fcz
==
None
:
fcz
=
fake_fcurve
(
object
,
2
)
return
([
fcx
,
fcy
,
fcz
])
# turn screen coordinates (x,y) into world coordinates vector
def
screen_to_world
(
context
,
x
,
y
):
depth_vector
=
view3d_utils
.
region_2d_to_vector_3d
(
\
context
.
region
,
context
.
region_data
,
[
x
,
y
])
vector
=
view3d_utils
.
region_2d_to_location_3d
(
\
context
.
region
,
context
.
region_data
,
[
x
,
y
],
depth_vector
)
return
(
vector
)
# turn 3d world coordinates vector into screen coordinate integers (x,y)
def
world_to_screen
(
context
,
vector
):
prj
=
context
.
region_data
.
perspective_matrix
*
\
mathutils
.
Vector
((
vector
[
0
],
vector
[
1
],
vector
[
2
],
1.0
))
width_half
=
context
.
region
.
width
/
2.0
height_half
=
context
.
region
.
height
/
2.0
x
=
int
(
width_half
+
width_half
*
(
prj
.
x
/
prj
.
w
))
y
=
int
(
height_half
+
height_half
*
(
prj
.
y
/
prj
.
w
))
# correction for corner cases in perspective mode
if
prj
.
w
<
0
:
if
x
<
0
:
x
=
context
.
region
.
width
*
2
else
:
x
=
context
.
region
.
width
*
-
2
if
y
<
0
:
y
=
context
.
region
.
height
*
2
else
:
y
=
context
.
region
.
height
*
-
2
return
(
x
,
y
)
# calculate location of display_ob in worldspace
def
get_location
(
frame
,
display_ob
,
offset_ob
,
curves
):
if
offset_ob
:
bpy
.
context
.
scene
.
frame_set
(
frame
)
display_mat
=
getattr
(
display_ob
,
"
matrix
"
,
False
)
if
not
display_mat
:
# posebones have "matrix", objects have "matrix_world"
display_mat
=
display_ob
.
matrix_world
if
offset_ob
:
loc
=
display_mat
.
to_translation
()
+
\
offset_ob
.
matrix_world
.
to_translation
()
else
:
loc
=
display_mat
.
to_translation
()
else
:
fcx
,
fcy
,
fcz
=
curves
locx
=
fcx
.
evaluate
(
frame
)
locy
=
fcy
.
evaluate
(
frame
)
locz
=
fcz
.
evaluate
(
frame
)
loc
=
mathutils
.
Vector
([
locx
,
locy
,
locz
])
return
(
loc
)
# get position of keyframes and handles at the start of dragging
def
get_original_animation_data
(
context
,
keyframes
):
keyframes_ori
=
{}
handles_ori
=
{}
if
context
.
active_object
and
context
.
active_object
.
mode
==
'
POSE
'
:
armature_ob
=
context
.
active_object
objects
=
[[
armature_ob
,
pb
,
armature_ob
]
for
pb
in
\
context
.
selected_pose_bones
]
else
:
objects
=
[[
ob
,
False
,
False
]
for
ob
in
context
.
selected_objects
]
for
action_ob
,
child
,
offset_ob
in
objects
:
if
not
action_ob
.
animation_data
:
continue
curves
=
get_curves
(
action_ob
,
child
)
if
len
(
curves
)
==
0
:
continue
fcx
,
fcy
,
fcz
=
curves
if
child
:
display_ob
=
child
else
:
display_ob
=
action_ob
# get keyframe positions
frame_old
=
context
.
scene
.
frame_current
keyframes_ori
[
display_ob
.
name
]
=
{}
for
frame
in
keyframes
[
display_ob
.
name
]:
loc
=
get_location
(
frame
,
display_ob
,
offset_ob
,
curves
)
keyframes_ori
[
display_ob
.
name
][
frame
]
=
[
frame
,
loc
]
# get handle positions
handles_ori
[
display_ob
.
name
]
=
{}
for
frame
in
keyframes
[
display_ob
.
name
]:
handles_ori
[
display_ob
.
name
][
frame
]
=
{}
left_x
=
[
frame
,
fcx
.
evaluate
(
frame
)]
right_x
=
[
frame
,
fcx
.
evaluate
(
frame
)]
for
kf
in
fcx
.
keyframe_points
:
if
kf
.
co
[
0
]
==
frame
:
left_x
=
kf
.
handle_left
[:]
right_x
=
kf
.
handle_right
[:]
break
left_y
=
[
frame
,
fcy
.
evaluate
(
frame
)]
right_y
=
[
frame
,
fcy
.
evaluate
(
frame
)]
for
kf
in
fcy
.
keyframe_points
:
if
kf
.
co
[
0
]
==
frame
:
left_y
=
kf
.
handle_left
[:]
right_y
=
kf
.
handle_right
[:]
break
left_z
=
[
frame
,
fcz
.
evaluate
(
frame
)]
right_z
=
[
frame
,
fcz
.
evaluate
(
frame
)]
for
kf
in
fcz
.
keyframe_points
:
if
kf
.
co
[
0
]
==
frame
:
left_z
=
kf
.
handle_left
[:]
right_z
=
kf
.
handle_right
[:]
break
handles_ori
[
display_ob
.
name
][
frame
][
"
left
"
]
=
[
left_x
,
left_y
,
left_z
]
handles_ori
[
display_ob
.
name
][
frame
][
"
right
"
]
=
[
right_x
,
right_y
,
right_z
]
if
context
.
scene
.
frame_current
!=
frame_old
:
context
.
scene
.
frame_set
(
frame_old
)
return
(
keyframes_ori
,
handles_ori
)
# callback function that calculates positions of all things that need be drawn
def
calc_callback
(
self
,
context
):
if
context
.
active_object
and
context
.
active_object
.
mode
==
'
POSE
'
:
armature_ob
=
context
.
active_object
objects
=
[[
armature_ob
,
pb
,
armature_ob
]
for
pb
in
\
context
.
selected_pose_bones
]
else
:
objects
=
[[
ob
,
False
,
False
]
for
ob
in
context
.
selected_objects
]
if
objects
==
self
.
displayed
:
selection_change
=
False
else
:
selection_change
=
True
if
self
.
lock
and
not
selection_change
and
\
context
.
region_data
.
perspective_matrix
==
self
.
perspective
and
not
\
context
.
window_manager
.
motion_trail
.
force_update
:
return
# dictionaries with key: objectname
self
.
paths
=
{}
# value: list of lists with x, y, colour
self
.
keyframes
=
{}
# value: dict with frame as key and [x,y] as value
self
.
handles
=
{}
# value: dict of dicts
self
.
timebeads
=
{}
# value: dict with frame as key and [x,y] as value
self
.
click
=
{}
# value: list of lists with frame, type, loc-vector
if
selection_change
:
# value: editbone inverted rotation matrix or None
self
.
edit_bones
=
{}
if
selection_change
or
not
self
.
lock
or
context
.
window_manager
.
\
motion_trail
.
force_update
:
# contains locations of path, keyframes and timebeads
self
.
cached
=
{
"
path
"
:{},
"
keyframes
"
:{},
"
timebeads_timing
"
:{},
"
timebeads_speed
"
:{}}
if
self
.
cached
[
"
path
"
]:
use_cache
=
True
else
:
use_cache
=
False
self
.
perspective
=
context
.
region_data
.
perspective_matrix
.
copy
()
self
.
displayed
=
objects
# store, so it can be checked next time
context
.
window_manager
.
motion_trail
.
force_update
=
False
global_undo
=
context
.
user_preferences
.
edit
.
use_global_undo
context
.
user_preferences
.
edit
.
use_global_undo
=
False
for
action_ob
,
child
,
offset_ob
in
objects
:
if
selection_change
:
if
not
child
:
self
.
edit_bones
[
action_ob
.
name
]
=
None
else
:
bpy
.
ops
.
object
.
mode_set
(
mode
=
'
EDIT
'
)
editbones
=
action_ob
.
data
.
edit_bones
mat
=
editbones
[
child
.
name
].
matrix
.
copy
().
to_3x3
().
inverted
()
bpy
.
ops
.
object
.
mode_set
(
mode
=
'
POSE
'
)
self
.
edit_bones
[
child
.
name
]
=
mat
if
not
action_ob
.
animation_data
:
continue
curves
=
get_curves
(
action_ob
,
child
)
if
len
(
curves
)
==
0
:
continue
if
context
.
window_manager
.
motion_trail
.
path_before
==
0
:
range_min
=
context
.
scene
.
frame_start
else
:
range_min
=
max
(
context
.
scene
.
frame_start
,
context
.
scene
.
frame_current
-
\
context
.
window_manager
.
motion_trail
.
path_before
)
if
context
.
window_manager
.
motion_trail
.
path_after
==
0
:
range_max
=
context
.
scene
.
frame_end
else
:
range_max
=
min
(
context
.
scene
.
frame_end
,
context
.
scene
.
frame_current
+
\
context
.
window_manager
.
motion_trail
.
path_after
)
fcx
,
fcy
,
fcz
=
curves
if
child
:
display_ob
=
child
else
:
display_ob
=
action_ob
# get location data of motion path
path
=
[]
speeds
=
[]
frame_old
=
context
.
scene
.
frame_current
step
=
11
-
context
.
window_manager
.
motion_trail
.
path_resolution
if
not
use_cache
:
if
display_ob
.
name
not
in
self
.
cached
[
"
path
"
]:
self
.
cached
[
"
path
"
][
display_ob
.
name
]
=
{}
if
use_cache
and
range_min
-
1
in
self
.
cached
[
"
path
"
][
display_ob
.
name
]:
prev_loc
=
self
.
cached
[
"
path
"
][
display_ob
.
name
][
range_min
-
1
]
else
:
prev_loc
=
get_location
(
range_min
-
1
,
display_ob
,
offset_ob
,
curves
)
self
.
cached
[
"
path
"
][
display_ob
.
name
][
range_min
-
1
]
=
prev_loc
for
frame
in
range
(
range_min
,
range_max
+
1
,
step
):
if
use_cache
and
frame
in
self
.
cached
[
"
path
"
][
display_ob
.
name
]:
loc
=
self
.
cached
[
"
path
"
][
display_ob
.
name
][
frame
]
else
:
loc
=
get_location
(
frame
,
display_ob
,
offset_ob
,
curves
)
self
.
cached
[
"
path
"
][
display_ob
.
name
][
frame
]
=
loc
if
not
context
.
region
or
not
context
.
space_data
:
continue
x
,
y
=
world_to_screen
(
context
,
loc
)
if
context
.
window_manager
.
motion_trail
.
path_style
==
'
simple
'
:
path
.
append
([
x
,
y
,
[
0.0
,
0.0
,
0.0
],
frame
,
action_ob
,
child
])
else
:
dloc
=
(
loc
-
prev_loc
).
length
path
.
append
([
x
,
y
,
dloc
,
frame
,
action_ob
,
child
])
speeds
.
append
(
dloc
)
prev_loc
=
loc
# calculate colour of path
if
context
.
window_manager
.
motion_trail
.
path_style
==
'
speed
'
:
speeds
.
sort
()
min_speed
=
speeds
[
0
]
d_speed
=
speeds
[
-
1
]
-
min_speed
for
i
,
[
x
,
y
,
d_loc
,
frame
,
action_ob
,
child
]
in
enumerate
(
path
):
relative_speed
=
(
d_loc
-
min_speed
)
/
d_speed
# 0.0 to 1.0
red
=
min
(
1.0
,
2.0
*
relative_speed
)
blue
=
min
(
1.0
,
2.0
-
(
2.0
*
relative_speed
))
path
[
i
][
2
]
=
[
red
,
0.0
,
blue
]
elif
context
.
window_manager
.
motion_trail
.
path_style
==
'
acceleration
'
:
accelerations
=
[]
prev_speed
=
0.0
for
i
,
[
x
,
y
,
d_loc
,
frame
,
action_ob
,
child
]
in
enumerate
(
path
):
accel
=
d_loc
-
prev_speed
accelerations
.
append
(
accel
)
path
[
i
][
2
]
=
accel
prev_speed
=
d_loc
accelerations
.
sort
()
min_accel
=
accelerations
[
0
]
max_accel
=
accelerations
[
-
1
]
for
i
,
[
x
,
y
,
accel
,
frame
,
action_ob
,
child
]
in
enumerate
(
path
):
if
accel
<
0
:
relative_accel
=
accel
/
min_accel
# values from 0.0 to 1.0
green
=
1.0
-
relative_accel
path
[
i
][
2
]
=
[
1.0
,
green
,
0.0
]
elif
accel
>
0
:
relative_accel
=
accel
/
max_accel
# values from 0.0 to 1.0
red
=
1.0
-
relative_accel
path
[
i
][
2
]
=
[
red
,
1.0
,
0.0
]
else
:
path
[
i
][
2
]
=
[
1.0
,
1.0
,
0.0
]
self
.
paths
[
display_ob
.
name
]
=
path
# get keyframes and handles
keyframes
=
{}
handle_difs
=
{}
kf_time
=
[]
click
=
[]
if
not
use_cache
:
if
display_ob
.
name
not
in
self
.
cached
[
"
keyframes
"
]:
self
.
cached
[
"
keyframes
"
][
display_ob
.
name
]
=
{}
for
fc
in
curves
:
for
kf
in
fc
.
keyframe_points
:
# handles for location mode
if
context
.
window_manager
.
motion_trail
.
mode
==
'
location
'
:
if
kf
.
co
[
0
]
not
in
handle_difs
:
handle_difs
[
kf
.
co
[
0
]]
=
{
"
left
"
:
mathutils
.
Vector
(),
"
right
"
:
mathutils
.
Vector
(),
"
keyframe_loc
"
:
None
}
handle_difs
[
kf
.
co
[
0
]][
"
left
"
][
fc
.
array_index
]
=
\
(
mathutils
.
Vector
(
kf
.
handle_left
[:])
-
\
mathutils
.
Vector
(
kf
.
co
[:])).
normalized
()[
1
]
handle_difs
[
kf
.
co
[
0
]][
"
right
"
][
fc
.
array_index
]
=
\
(
mathutils
.
Vector
(
kf
.
handle_right
[:])
-
\
mathutils
.
Vector
(
kf
.
co
[:])).
normalized
()[
1
]
# keyframes
if
kf
.
co
[
0
]
in
kf_time
:
continue
kf_time
.
append
(
kf
.
co
[
0
])
co
=
kf
.
co
[
0
]
if
use_cache
and
co
in
\
self
.
cached
[
"
keyframes
"
][
display_ob
.
name
]:
loc
=
self
.
cached
[
"
keyframes
"
][
display_ob
.
name
][
co
]
else
:
loc
=
get_location
(
co
,
display_ob
,
offset_ob
,
curves
)
self
.
cached
[
"
keyframes
"
][
display_ob
.
name
][
co
]
=
loc
if
handle_difs
:
handle_difs
[
co
][
"
keyframe_loc
"
]
=
loc
x
,
y
=
world_to_screen
(
context
,
loc
)
keyframes
[
kf
.
co
[
0
]]
=
[
x
,
y
]
if
context
.
window_manager
.
motion_trail
.
mode
!=
'
speed
'
:
# can't select keyframes in speed mode
click
.
append
([
kf
.
co
[
0
],
"
keyframe
"
,
mathutils
.
Vector
([
x
,
y
]),
action_ob
,
child
])
self
.
keyframes
[
display_ob
.
name
]
=
keyframes
# handles are only shown in location-altering mode
if
context
.
window_manager
.
motion_trail
.
mode
==
'
location
'
and
\
context
.
window_manager
.
motion_trail
.
handle_display
:
# calculate handle positions
handles
=
{}
for
frame
,
vecs
in
handle_difs
.
items
():
if
child
:
# bone space to world space
mat
=
self
.
edit_bones
[
child
.
name
].
copy
().
inverted
()
vec_left
=
vecs
[
"
left
"
]
*
mat
vec_right
=
vecs
[
"
right
"
]
*
mat
else
:
vec_left
=
vecs
[
"
left
"
]
vec_right
=
vecs
[
"
right
"
]
if
vecs
[
"
keyframe_loc
"
]
!=
None
:
vec_keyframe
=
vecs
[
"
keyframe_loc
"
]
else
:
vec_keyframe
=
get_location
(
frame
,
display_ob
,
offset_ob
,
curves
)
x_left
,
y_left
=
world_to_screen
(
context
,
vec_left
*
2
+
\
vec_keyframe
)
x_right
,
y_right
=
world_to_screen
(
context
,
vec_right
*
2
+
\
vec_keyframe
)
handles
[
frame
]
=
{
"
left
"
:[
x_left
,
y_left
],
"
right
"
:[
x_right
,
y_right
]}
click
.
append
([
frame
,
"
handle_left
"
,
mathutils
.
Vector
([
x_left
,
y_left
]),
action_ob
,
child
])
click
.
append
([
frame
,
"
handle_right
"
,
mathutils
.
Vector
([
x_right
,
y_right
]),
action_ob
,
child
])
self
.
handles
[
display_ob
.
name
]
=
handles
# calculate timebeads for timing mode
if
context
.
window_manager
.
motion_trail
.
mode
==
'
timing
'
:
timebeads
=
{}
n
=
context
.
window_manager
.
motion_trail
.
timebeads
*
(
len
(
kf_time
)
\
-
1
)
dframe
=
(
range_max
-
range_min
)
/
(
n
+
1
)
if
not
use_cache
:
if
display_ob
.
name
not
in
self
.
cached
[
"
timebeads_timing
"
]:
self
.
cached
[
"
timebeads_timing
"
][
display_ob
.
name
]
=
{}
for
i
in
range
(
1
,
n
+
1
):
frame
=
range_min
+
i
*
dframe
if
use_cache
and
frame
in
\
self
.
cached
[
"
timebeads_timing
"
][
display_ob
.
name
]:
loc
=
self
.
cached
[
"
timebeads_timing
"
][
display_ob
.
name
]
\
[
frame
]
else
:
loc
=
get_location
(
frame
,
display_ob
,
offset_ob
,
curves
)
self
.
cached
[
"
timebeads_timing
"
][
display_ob
.
name
][
frame
]
=
\
loc
x
,
y
=
world_to_screen
(
context
,
loc
)
timebeads
[
frame
]
=
[
x
,
y
]
click
.
append
([
frame
,
"
timebead
"
,
mathutils
.
Vector
([
x
,
y
]),
action_ob
,
child
])
self
.
timebeads
[
display_ob
.
name
]
=
timebeads
# calculate timebeads for speed mode
if
context
.
window_manager
.
motion_trail
.
mode
==
'
speed
'
:
angles
=
dict
([[
kf
,
{
"
left
"
:[],
"
right
"
:[]}]
for
kf
in
\
self
.
keyframes
[
display_ob
.
name
]])
for
fc
in
curves
:
for
i
,
kf
in
enumerate
(
fc
.
keyframe_points
):
if
i
!=
0
:
angle
=
mathutils
.
Vector
([
-
1
,
0
]).
angle
(
mathutils
.
\
Vector
(
kf
.
handle_left
)
-
mathutils
.
Vector
(
kf
.
co
),
0
)
if
angle
!=
0
:
angles
[
kf
.
co
[
0
]][
"
left
"
].
append
(
angle
)
if
i
!=
len
(
fc
.
keyframe_points
)
-
1
:
angle
=
mathutils
.
Vector
([
1
,
0
]).
angle
(
mathutils
.
\
Vector
(
kf
.
handle_right
)
-
mathutils
.
Vector
(
kf
.
co
),
0
)
if
angle
!=
0
:
angles
[
kf
.
co
[
0
]][
"
right
"
].
append
(
angle
)
timebeads
=
{}
kf_time
.
sort
()
if
not
use_cache
:
if
display_ob
.
name
not
in
self
.
cached
[
"
timebeads_speed
"
]:
self
.
cached
[
"
timebeads_speed
"
][
display_ob
.
name
]
=
{}
for
frame
,
sides
in
angles
.
items
():
if
sides
[
"
left
"
]:
perc
=
(
sum
(
sides
[
"
left
"
])
/
len
(
sides
[
"
left
"
]))
/
\
(
math
.
pi
/
2
)
perc
=
max
(
0.4
,
min
(
1
,
perc
*
5
))
previous
=
kf_time
[
kf_time
.
index
(
frame
)
-
1
]
bead_frame
=
frame
-
perc
*
((
frame
-
previous
-
2
)
/
2
)
if
use_cache
and
bead_frame
in
\
self
.
cached
[
"
timebeads_speed
"
][
display_ob
.
name
]:
loc
=
self
.
cached
[
"
timebeads_speed
"
][
display_ob
.
name
]
\
[
bead_frame
]
else
:
loc
=
get_location
(
bead_frame
,
display_ob
,
offset_ob
,
curves
)
self
.
cached
[
"
timebeads_speed
"
][
display_ob
.
name
]
\
[
bead_frame
]
=
loc
x
,
y
=
world_to_screen
(
context
,
loc
)
timebeads
[
bead_frame
]
=
[
x
,
y
]
click
.
append
([
bead_frame
,
"
timebead
"
,
mathutils
.
\
Vector
([
x
,
y
]),
action_ob
,
child
])
if
sides
[
"
right
"
]:
perc
=
(
sum
(
sides
[
"
right
"
])
/
len
(
sides
[
"
right
"
]))
/
\
(
math
.
pi
/
2
)
perc
=
max
(
0.4
,
min
(
1
,
perc
*
5
))
next
=
kf_time
[
kf_time
.
index
(
frame
)
+
1
]
bead_frame
=
frame
+
perc
*
((
next
-
frame
-
2
)
/
2
)
if
use_cache
and
bead_frame
in
\
self
.
cached
[
"
timebeads_speed
"
][
display_ob
.
name
]:
loc
=
self
.
cached
[
"
timebeads_speed
"
][
display_ob
.
name
]
\
[
bead_frame
]
else
:
loc
=
get_location
(
bead_frame
,
display_ob
,
offset_ob
,
curves
)
self
.
cached
[
"
timebeads_speed
"
][
display_ob
.
name
]
\
[
bead_frame
]
=
loc
x
,
y
=
world_to_screen
(
context
,
loc
)
timebeads
[
bead_frame
]
=
[
x
,
y
]
click
.
append
([
bead_frame
,
"
timebead
"
,
mathutils
.
\
Vector
([
x
,
y
]),
action_ob
,
child
])
self
.
timebeads
[
display_ob
.
name
]
=
timebeads
# add frame positions to click-list
if
context
.
window_manager
.
motion_trail
.
frame_display
:
path
=
self
.
paths
[
display_ob
.
name
]
for
x
,
y
,
colour
,
frame
,
action_ob
,
child
in
path
:
click
.
append
([
frame
,
"
frame
"
,
mathutils
.
Vector
([
x
,
y
]),
action_ob
,
child
])
self
.
click
[
display_ob
.
name
]
=
click
if
context
.
scene
.
frame_current
!=
frame_old
:
context
.
scene
.
frame_set
(
frame_old
)
context
.
user_preferences
.
edit
.
use_global_undo
=
global_undo
# draw in 3d-view
def
draw_callback
(
self
,
context
):
# polling
if
context
.
mode
not
in
[
'
OBJECT
'
,
'
POSE
'
]
or
\
context
.
window_manager
.
motion_trail
.
enabled
!=
1
:
return
# display limits
if
context
.
window_manager
.
motion_trail
.
path_before
!=
0
:
limit_min
=
context
.
scene
.
frame_current
-
\
context
.
window_manager
.
motion_trail
.
path_before
else
:
limit_min
=
-
1e6
if
context
.
window_manager
.
motion_trail
.
path_after
!=
0
:
limit_max
=
context
.
scene
.
frame_current
+
\
context
.
window_manager
.
motion_trail
.
path_after
else
:
limit_max
=
1e6
# draw motion path
bgl
.
glEnable
(
bgl
.
GL_BLEND
)
bgl
.
glLineWidth
(
context
.
window_manager
.
motion_trail
.
path_width
)
alpha
=
1.0
-
(
context
.
window_manager
.
motion_trail
.
path_transparency
/
\
100.0
)
if
context
.
window_manager
.
motion_trail
.
path_style
==
'
simple
'
:
bgl
.
glColor4f
(
0.0
,
0.0
,
0.0
,
alpha
)
for
objectname
,
path
in
self
.
paths
.
items
():
bgl
.
glBegin
(
bgl
.
GL_LINE_STRIP
)
for
x
,
y
,
colour
,
frame
,
action_ob
,
child
in
path
:
if
frame
<
limit_min
or
frame
>
limit_max
:
continue
bgl
.
glVertex2i
(
x
,
y
)
bgl
.
glEnd
()
else
:
for
objectname
,
path
in
self
.
paths
.
items
():
for
i
,
[
x
,
y
,
colour
,
frame
,
action_ob
,
child
]
in
enumerate
(
path
):
if
frame
<
limit_min
or
frame
>
limit_max
:
continue
r
,
g
,
b
=
colour
if
i
!=
0
:
prev_path
=
path
[
i
-
1
]
halfway
=
[(
x
+
prev_path
[
0
])
/
2
,
(
y
+
prev_path
[
1
])
/
2
]
bgl
.
glColor4f
(
r
,
g
,
b
,
alpha
)
bgl
.
glBegin
(
bgl
.
GL_LINE_STRIP
)
bgl
.
glVertex2i
(
int
(
halfway
[
0
]),
int
(
halfway
[
1
]))
bgl
.
glVertex2i
(
x
,
y
)
bgl
.
glEnd
()
if
i
!=
len
(
path
)
-
1
:
next_path
=
path
[
i
+
1
]
halfway
=
[(
x
+
next_path
[
0
])
/
2
,
(
y
+
next_path
[
1
])
/
2
]
bgl
.
glColor4f
(
r
,
g
,
b
,
alpha
)
bgl
.
glBegin
(
bgl
.
GL_LINE_STRIP
)
bgl
.
glVertex2i
(
x
,
y
)
bgl
.
glVertex2i
(
int
(
halfway
[
0
]),
int
(
halfway
[
1
]))
bgl
.
glEnd
()
# draw frames
if
context
.
window_manager
.
motion_trail
.
frame_display
:
bgl
.
glColor4f
(
1.0
,
1.0
,
1.0
,
1.0
)
bgl
.
glPointSize
(
1
)
bgl
.
glBegin
(
bgl
.
GL_POINTS
)
for
objectname
,
path
in
self
.
paths
.
items
():
for
x
,
y
,
colour
,
frame
,
action_ob
,
child
in
path
:
if
frame
<
limit_min
or
frame
>
limit_max
:
continue
if
self
.
active_frame
and
objectname
==
self
.
active_frame
[
0
]
\
and
abs
(
frame
-
self
.
active_frame
[
1
])
<
1e-4
:
bgl
.
glEnd
()
bgl
.
glColor4f
(
1.0
,
0.5
,
0.0
,
1.0
)
bgl
.
glPointSize
(
3
)
bgl
.
glBegin
(
bgl
.
GL_POINTS
)
bgl
.
glVertex2i
(
x
,
y
)
bgl
.
glEnd
()
bgl
.
glColor4f
(
1.0
,
1.0
,
1.0
,
1.0
)
bgl
.
glPointSize
(
1
)
bgl
.
glBegin
(
bgl
.
GL_POINTS
)
else
:
bgl
.
glVertex2i
(
x
,
y
)
bgl
.
glEnd
()
# time beads are shown in speed and timing modes
if
context
.
window_manager
.
motion_trail
.
mode
in
[
'
speed
'
,
'
timing
'
]:
bgl
.
glColor4f
(
0.0
,
1.0
,
0.0
,
1.0
)
bgl
.
glPointSize
(
4
)
bgl
.
glBegin
(
bgl
.
GL_POINTS
)
for
objectname
,
values
in
self
.
timebeads
.
items
():
for
frame
,
coords
in
values
.
items
():
if
frame
<
limit_min
or
frame
>
limit_max
:
continue
if
self
.
active_timebead
and
\
objectname
==
self
.
active_timebead
[
0
]
and
\
abs
(
frame
-
self
.
active_timebead
[
1
])
<
1e-4
:
bgl
.
glEnd
()
bgl
.
glColor4f
(
1.0
,
0.5
,
0.0
,
1.0
)
bgl
.
glBegin
(
bgl
.
GL_POINTS
)
bgl
.
glVertex2i
(
coords
[
0
],
coords
[
1
])
bgl
.
glEnd
()
bgl
.
glColor4f
(
0.0
,
1.0
,
0.0
,
1.0
)
bgl
.
glBegin
(
bgl
.
GL_POINTS
)
else
:
bgl
.
glVertex2i
(
coords
[
0
],
coords
[
1
])
bgl
.
glEnd
()
# handles are only shown in location mode
if
context
.
window_manager
.
motion_trail
.
mode
==
'
location
'
:
# draw handle-lines
bgl
.
glColor4f
(
0.0
,
0.0
,
0.0
,
1.0
)
bgl
.
glLineWidth
(
1
)
bgl
.
glBegin
(
bgl
.
GL_LINES
)
for
objectname
,
values
in
self
.
handles
.
items
():
for
frame
,
sides
in
values
.
items
():
if
frame
<
limit_min
or
frame
>
limit_max
:
continue
for
side
,
coords
in
sides
.
items
():
if
self
.
active_handle
and
\
objectname
==
self
.
active_handle
[
0
]
and
\
side
==
self
.
active_handle
[
2
]
and
\
abs
(
frame
-
self
.
active_handle
[
1
])
<
1e-4
:
bgl
.
glEnd
()
bgl
.
glColor4f
(.
75
,
0.25
,
0.0
,
1.0
)
bgl
.
glBegin
(
bgl
.
GL_LINES
)
bgl
.
glVertex2i
(
self
.
keyframes
[
objectname
][
frame
][
0
],
self
.
keyframes
[
objectname
][
frame
][
1
])
bgl
.
glVertex2i
(
coords
[
0
],
coords
[
1
])
bgl
.
glEnd
()
bgl
.
glColor4f
(
0.0
,
0.0
,
0.0
,
1.0
)
bgl
.
glBegin
(
bgl
.
GL_LINES
)
else
:
bgl
.
glVertex2i
(
self
.
keyframes
[
objectname
][
frame
][
0
],
self
.
keyframes
[
objectname
][
frame
][
1
])
bgl
.
glVertex2i
(
coords
[
0
],
coords
[
1
])
bgl
.
glEnd
()
# draw handles
bgl
.
glColor4f
(
1.0
,
1.0
,
0.0
,
1.0
)
bgl
.
glPointSize
(
4
)
bgl
.
glBegin
(
bgl
.
GL_POINTS
)
for
objectname
,
values
in
self
.
handles
.
items
():
for
frame
,
sides
in
values
.
items
():
if
frame
<
limit_min
or
frame
>
limit_max
:
continue
for
side
,
coords
in
sides
.
items
():
if
self
.
active_handle
and
\
objectname
==
self
.
active_handle
[
0
]
and
\
side
==
self
.
active_handle
[
2
]
and
\
abs
(
frame
-
self
.
active_handle
[
1
])
<
1e-4
:
bgl
.
glEnd
()
bgl
.
glColor4f
(
1.0
,
0.5
,
0.0
,
1.0
)
bgl
.
glBegin
(
bgl
.
GL_POINTS
)
bgl
.
glVertex2i
(
coords
[
0
],
coords
[
1
])
bgl
.
glEnd
()
bgl
.
glColor4f
(
1.0
,
1.0
,
0.0
,
1.0
)
bgl
.
glBegin
(
bgl
.
GL_POINTS
)
else
:
bgl
.
glVertex2i
(
coords
[
0
],
coords
[
1
])
bgl
.
glEnd
()
# draw keyframes
bgl
.
glColor4f
(
1.0
,
1.0
,
0.0
,
1.0
)
bgl
.
glPointSize
(
6
)
bgl
.
glBegin
(
bgl
.
GL_POINTS
)
for
objectname
,
values
in
self
.
keyframes
.
items
():
for
frame
,
coords
in
values
.
items
():
if
frame
<
limit_min
or
frame
>
limit_max
:
continue
if
self
.
active_keyframe
and
\
objectname
==
self
.
active_keyframe
[
0
]
and
\
abs
(
frame
-
self
.
active_keyframe
[
1
])
<
1e-4
:
bgl
.
glEnd
()
bgl
.
glColor4f
(
1.0
,
0.5
,
0.0
,
1.0
)
bgl
.
glBegin
(
bgl
.
GL_POINTS
)
bgl
.
glVertex2i
(
coords
[
0
],
coords
[
1
])
bgl
.
glEnd
()
bgl
.
glColor4f
(
1.0
,
1.0
,
0.0
,
1.0
)
bgl
.
glBegin
(
bgl
.
GL_POINTS
)
else
:
bgl
.
glVertex2i
(
coords
[
0
],
coords
[
1
])
bgl
.
glEnd
()
# draw keyframe-numbers
if
context
.
window_manager
.
motion_trail
.
keyframe_numbers
:
blf
.
size
(
0
,
12
,
72
)
bgl
.
glColor4f
(
1.0
,
1.0
,
0.0
,
1.0
)
for
objectname
,
values
in
self
.
keyframes
.
items
():
for
frame
,
coords
in
values
.
items
():
if
frame
<
limit_min
or
frame
>
limit_max
:
continue
blf
.
position
(
0
,
coords
[
0
]
+
3
,
coords
[
1
]
+
3
,
0
)
text
=
str
(
frame
).
split
(
"
.
"
)
if
len
(
text
)
==
1
:
text
=
text
[
0
]
elif
len
(
text
[
1
])
==
1
and
text
[
1
]
==
"
0
"
:
text
=
text
[
0
]
else
:
text
=
text
[
0
]
+
"
.
"
+
text
[
1
][
0
]
if
self
.
active_keyframe
and
\
objectname
==
self
.
active_keyframe
[
0
]
and
\
abs
(
frame
-
self
.
active_keyframe
[
1
])
<
1e-4
:
bgl
.
glColor4f
(
1.0
,
0.5
,
0.0
,
1.0
)
blf
.
draw
(
0
,
text
)
bgl
.
glColor4f
(
1.0
,
1.0
,
0.0
,
1.0
)
else
:
blf
.
draw
(
0
,
text
)
# restore opengl defaults
bgl
.
glLineWidth
(
1
)
bgl
.
glDisable
(
bgl
.
GL_BLEND
)
bgl
.
glColor4f
(
0.0
,
0.0
,
0.0
,
1.0
)
bgl
.
glPointSize
(
1
)
# change data based on mouse movement
def
drag
(
context
,
event
,
drag_mouse_ori
,
active_keyframe
,
active_handle
,
active_timebead
,
keyframes_ori
,
handles_ori
,
edit_bones
):
# change 3d-location of keyframe
if
context
.
window_manager
.
motion_trail
.
mode
==
'
location
'
and
\
active_keyframe
:
objectname
,
frame
,
frame_ori
,
action_ob
,
child
=
active_keyframe
if
child
:
mat
=
action_ob
.
matrix_world
.
copy
().
inverted
()
*
\
edit_bones
[
child
.
name
].
copy
().
to_4x4
()
else
:
mat
=
1
mouse_ori_world
=
screen_to_world
(
context
,
drag_mouse_ori
[
0
],
drag_mouse_ori
[
1
])
*
mat
vector
=
screen_to_world
(
context
,
event
.
mouse_region_x
,
event
.
mouse_region_y
)
*
mat
d
=
vector
-
mouse_ori_world
loc_ori_ws
=
keyframes_ori
[
objectname
][
frame
][
1
]
loc_ori_bs
=
loc_ori_ws
*
mat
new_loc
=
loc_ori_bs
+
d
curves
=
get_curves
(
action_ob
,
child
)
for
i
,
curve
in
enumerate
(
curves
):
for
kf
in
curve
.
keyframe_points
:
if
kf
.
co
[
0
]
==
frame
:
kf
.
co
[
1
]
=
new_loc
[
i
]
kf
.
handle_left
[
1
]
=
handles_ori
[
objectname
][
frame
]
\
[
"
left
"
][
i
][
1
]
+
d
[
i
]
kf
.
handle_right
[
1
]
=
handles_ori
[
objectname
][
frame
]
\
[
"
right
"
][
i
][
1
]
+
d
[
i
]
break
# change 3d-location of handle
elif
context
.
window_manager
.
motion_trail
.
mode
==
'
location
'
and
\
active_handle
:
objectname
,
frame
,
side
,
action_ob
,
child
=
active_handle
if
child
:
mat
=
action_ob
.
matrix_world
.
copy
().
inverted
()
*
\
edit_bones
[
child
.
name
].
copy
().
to_4x4
()
else
:
mat
=
1
mouse_ori_world
=
screen_to_world
(
context
,
drag_mouse_ori
[
0
],
drag_mouse_ori
[
1
])
*
mat
vector
=
screen_to_world
(
context
,
event
.
mouse_region_x
,
event
.
mouse_region_y
)
*
mat
d
=
vector
-
mouse_ori_world
curves
=
get_curves
(
action_ob
,
child
)
for
i
,
curve
in
enumerate
(
curves
):
for
kf
in
curve
.
keyframe_points
:
if
kf
.
co
[
0
]
==
frame
:
if
side
==
"
left
"
:
# change handle type, if necessary
if
kf
.
handle_left_type
in
[
'
AUTO
'
,
'
ANIM_CLAMPED
'
]:
kf
.
handle_left_type
=
'
ALIGNED
'
elif
kf
.
handle_left_type
==
'
VECTOR
'
:
kf
.
handle_left_type
=
'
FREE
'
# change handle position(s)
kf
.
handle_left
[
1
]
=
handles_ori
[
objectname
][
frame
]
\
[
"
left
"
][
i
][
1
]
+
d
[
i
]
if
kf
.
handle_left_type
in
[
'
ALIGNED
'
,
'
ANIM_CLAMPED
'
,
'
AUTO
'
]:
dif
=
(
abs
(
handles_ori
[
objectname
][
frame
][
"
right
"
]
\
[
i
][
0
]
-
kf
.
co
[
0
])
/
abs
(
kf
.
handle_left
[
0
]
-
\
kf
.
co
[
0
]))
*
d
[
i
]
kf
.
handle_right
[
1
]
=
handles_ori
[
objectname
]
\
[
frame
][
"
right
"
][
i
][
1
]
-
dif
elif
side
==
"
right
"
:
# change handle type, if necessary
if
kf
.
handle_right_type
in
[
'
AUTO
'
,
'
ANIM_CLAMPED
'
]:
kf
.
handle_left_type
=
'
ALIGNED
'
kf
.
handle_right_type
=
'
ALIGNED
'
elif
kf
.
handle_right_type
==
'
VECTOR
'
:
kf
.
handle_left_type
=
'
FREE
'
kf
.
handle_right_type
=
'
FREE
'
# change handle position(s)
kf
.
handle_right
[
1
]
=
handles_ori
[
objectname
][
frame
]
\
[
"
right
"
][
i
][
1
]
+
d
[
i
]
if
kf
.
handle_right_type
in
[
'
ALIGNED
'
,
'
ANIM_CLAMPED
'
,
'
AUTO
'
]:
dif
=
(
abs
(
handles_ori
[
objectname
][
frame
][
"
left
"
]
\
[
i
][
0
]
-
kf
.
co
[
0
])
/
abs
(
kf
.
handle_right
[
0
]
-
\
kf
.
co
[
0
]))
*
d
[
i
]
kf
.
handle_left
[
1
]
=
handles_ori
[
objectname
]
\
[
frame
][
"
left
"
][
i
][
1
]
-
dif
break
# change position of all keyframes on timeline
elif
context
.
window_manager
.
motion_trail
.
mode
==
'
timing
'
and
\
active_timebead
:
objectname
,
frame
,
frame_ori
,
action_ob
,
child
=
active_timebead
curves
=
get_curves
(
action_ob
,
child
)
ranges
=
[
val
for
c
in
curves
for
val
in
c
.
range
()]
ranges
.
sort
()
range_min
=
round
(
ranges
[
0
])
range_max
=
round
(
ranges
[
-
1
])
range
=
range_max
-
range_min
dx_screen
=
-
(
mathutils
.
Vector
([
event
.
mouse_region_x
,
event
.
mouse_region_y
])
-
drag_mouse_ori
)[
0
]
dx_screen
=
dx_screen
/
context
.
region
.
width
*
range
new_frame
=
frame
+
dx_screen
shift_low
=
max
(
1e-4
,
(
new_frame
-
range_min
)
/
(
frame
-
range_min
))
shift_high
=
max
(
1e-4
,
(
range_max
-
new_frame
)
/
(
range_max
-
frame
))
new_mapping
=
{}
for
i
,
curve
in
enumerate
(
curves
):
for
j
,
kf
in
enumerate
(
curve
.
keyframe_points
):
frame_map
=
kf
.
co
[
0
]
if
frame_map
<
range_min
+
1e-4
or
\
frame_map
>
range_max
-
1e-4
:
continue
frame_ori
=
False
for
f
in
keyframes_ori
[
objectname
]:
if
abs
(
f
-
frame_map
)
<
1e-4
:
frame_ori
=
keyframes_ori
[
objectname
][
f
][
0
]
value_ori
=
keyframes_ori
[
objectname
][
f
]
break
if
not
frame_ori
:
continue
if
frame_ori
<=
frame
:
frame_new
=
(
frame_ori
-
range_min
)
*
shift_low
+
\
range_min
else
:
frame_new
=
range_max
-
(
range_max
-
frame_ori
)
*
\
shift_high
frame_new
=
max
(
range_min
+
j
,
min
(
frame_new
,
range_max
-
\
(
len
(
curve
.
keyframe_points
)
-
j
)
+
1
))
d_frame
=
frame_new
-
frame_ori
if
frame_new
not
in
new_mapping
:
new_mapping
[
frame_new
]
=
value_ori
kf
.
co
[
0
]
=
frame_new
kf
.
handle_left
[
0
]
=
handles_ori
[
objectname
][
frame_ori
]
\
[
"
left
"
][
i
][
0
]
+
d_frame
kf
.
handle_right
[
0
]
=
handles_ori
[
objectname
][
frame_ori
]
\
[
"
right
"
][
i
][
0
]
+
d_frame
del
keyframes_ori
[
objectname
]
keyframes_ori
[
objectname
]
=
{}
for
new_frame
,
value
in
new_mapping
.
items
():
keyframes_ori
[
objectname
][
new_frame
]
=
value
# change position of active keyframe on the timeline
elif
context
.
window_manager
.
motion_trail
.
mode
==
'
timing
'
and
\
active_keyframe
:
objectname
,
frame
,
frame_ori
,
action_ob
,
child
=
active_keyframe
if
child
:
mat
=
action_ob
.
matrix_world
.
copy
().
inverted
()
*
\
edit_bones
[
child
.
name
].
copy
().
to_4x4
()
else
:
mat
=
action_ob
.
matrix_world
.
copy
().
inverted
()
mouse_ori_world
=
screen_to_world
(
context
,
drag_mouse_ori
[
0
],
drag_mouse_ori
[
1
])
*
mat
vector
=
screen_to_world
(
context
,
event
.
mouse_region_x
,
event
.
mouse_region_y
)
*
mat
d
=
vector
-
mouse_ori_world
locs_ori
=
[[
f_ori
,
coords
]
for
f_mapped
,
[
f_ori
,
coords
]
in
\
keyframes_ori
[
objectname
].
items
()]
locs_ori
.
sort
()
direction
=
1
range
=
False
for
i
,
[
f_ori
,
coords
]
in
enumerate
(
locs_ori
):
if
abs
(
frame_ori
-
f_ori
)
<
1e-4
:
if
i
==
0
:
# first keyframe, nothing before it
direction
=
-
1
range
=
[
f_ori
,
locs_ori
[
i
+
1
][
0
]]
elif
i
==
len
(
locs_ori
)
-
1
:
# last keyframe, nothing after it
range
=
[
locs_ori
[
i
-
1
][
0
],
f_ori
]
else
:
current
=
mathutils
.
Vector
(
coords
)
next
=
mathutils
.
Vector
(
locs_ori
[
i
+
1
][
1
])
previous
=
mathutils
.
Vector
(
locs_ori
[
i
-
1
][
1
])
angle_to_next
=
d
.
angle
(
next
-
current
,
0
)
angle_to_previous
=
d
.
angle
(
previous
-
current
,
0
)
if
angle_to_previous
<
angle_to_next
:
# mouse movement is in direction of previous keyframe
direction
=
-
1
range
=
[
locs_ori
[
i
-
1
][
0
],
locs_ori
[
i
+
1
][
0
]]
break
direction
*=
-
1
# feels more natural in 3d-view
if
not
range
:
# keyframe not found, is impossible, but better safe than sorry
return
(
active_keyframe
,
active_timebead
,
keyframes_ori
)
# calculate strength of movement
d_screen
=
mathutils
.
Vector
([
event
.
mouse_region_x
,
event
.
mouse_region_y
])
-
drag_mouse_ori
if
d_screen
.
length
!=
0
:
d_screen
=
d_screen
.
length
/
(
abs
(
d_screen
[
0
])
/
d_screen
.
length
*
\
context
.
region
.
width
+
abs
(
d_screen
[
1
])
/
d_screen
.
length
*
\
context
.
region
.
height
)
d_screen
*=
direction
# d_screen value ranges from -1.0 to 1.0
else
:
d_screen
=
0.0
new_frame
=
d_screen
*
(
range
[
1
]
-
range
[
0
])
+
frame_ori
max_frame
=
range
[
1
]
if
max_frame
==
frame_ori
:
max_frame
+=
1
min_frame
=
range
[
0
]
if
min_frame
==
frame_ori
:
min_frame
-=
1
new_frame
=
min
(
max_frame
-
1
,
max
(
min_frame
+
1
,
new_frame
))
d_frame
=
new_frame
-
frame_ori
curves
=
get_curves
(
action_ob
,
child
)
for
i
,
curve
in
enumerate
(
curves
):
for
kf
in
curve
.
keyframe_points
:
if
abs
(
kf
.
co
[
0
]
-
frame
)
<
1e-4
:
kf
.
co
[
0
]
=
new_frame
kf
.
handle_left
[
0
]
=
handles_ori
[
objectname
][
frame_ori
]
\
[
"
left
"
][
i
][
0
]
+
d_frame
kf
.
handle_right
[
0
]
=
handles_ori
[
objectname
][
frame_ori
]
\
[
"
right
"
][
i
][
0
]
+
d_frame
break
active_keyframe
=
[
objectname
,
new_frame
,
frame_ori
,
action_ob
,
child
]
# change position of active timebead on the timeline, thus altering speed
elif
context
.
window_manager
.
motion_trail
.
mode
==
'
speed
'
and
\
active_timebead
:
objectname
,
frame
,
frame_ori
,
action_ob
,
child
=
active_timebead
if
child
:
mat
=
action_ob
.
matrix_world
.
copy
().
inverted
()
*
\
edit_bones
[
child
.
name
].
copy
().
to_4x4
()
else
:
mat
=
1
mouse_ori_world
=
screen_to_world
(
context
,
drag_mouse_ori
[
0
],
drag_mouse_ori
[
1
])
*
mat
vector
=
screen_to_world
(
context
,
event
.
mouse_region_x
,
event
.
mouse_region_y
)
*
mat
d
=
vector
-
mouse_ori_world
# determine direction (to next or previous keyframe)
curves
=
get_curves
(
action_ob
,
child
)
fcx
,
fcy
,
fcz
=
curves
locx
=
fcx
.
evaluate
(
frame_ori
)
locy
=
fcy
.
evaluate
(
frame_ori
)
locz
=
fcz
.
evaluate
(
frame_ori
)
loc_ori
=
mathutils
.
Vector
([
locx
,
locy
,
locz
])
# bonespace
keyframes
=
[
kf
for
kf
in
keyframes_ori
[
objectname
]]
keyframes
.
append
(
frame_ori
)
keyframes
.
sort
()
frame_index
=
keyframes
.
index
(
frame_ori
)
kf_prev
=
keyframes
[
frame_index
-
1
]
kf_next
=
keyframes
[
frame_index
+
1
]
vec_prev
=
(
mathutils
.
Vector
(
keyframes_ori
[
objectname
][
kf_prev
][
1
])
\
*
mat
-
loc_ori
).
normalized
()
vec_next
=
(
mathutils
.
Vector
(
keyframes_ori
[
objectname
][
kf_next
][
1
])
\
*
mat
-
loc_ori
).
normalized
()
d_normal
=
d
.
copy
().
normalized
()
dist_to_next
=
(
d_normal
-
vec_next
).
length
dist_to_prev
=
(
d_normal
-
vec_prev
).
length
if
dist_to_prev
<
dist_to_next
:
direction
=
1
else
:
direction
=
-
1
if
(
kf_next
-
frame_ori
)
<
(
frame_ori
-
kf_prev
):
kf_bead
=
kf_next
side
=
"
left
"
else
:
kf_bead
=
kf_prev
side
=
"
right
"
d_frame
=
d
.
length
*
direction
*
2
# *2 to make it more sensitive
angles
=
[]
for
i
,
curve
in
enumerate
(
curves
):
for
kf
in
curve
.
keyframe_points
:
if
abs
(
kf
.
co
[
0
]
-
kf_bead
)
<
1e-4
:
if
side
==
"
left
"
:
# left side
kf
.
handle_left
[
0
]
=
min
(
handles_ori
[
objectname
]
\
[
kf_bead
][
"
left
"
][
i
][
0
]
+
d_frame
,
kf_bead
-
1
)
angle
=
mathutils
.
Vector
([
-
1
,
0
]).
angle
(
mathutils
.
\
Vector
(
kf
.
handle_left
)
-
mathutils
.
Vector
(
kf
.
co
),
0
)
if
angle
!=
0
:
angles
.
append
(
angle
)
else
:
# right side
kf
.
handle_right
[
0
]
=
max
(
handles_ori
[
objectname
]
\
[
kf_bead
][
"
right
"
][
i
][
0
]
+
d_frame
,
kf_bead
+
1
)
angle
=
mathutils
.
Vector
([
1
,
0
]).
angle
(
mathutils
.
\
Vector
(
kf
.
handle_right
)
-
mathutils
.
Vector
(
kf
.
co
),
0
)
if
angle
!=
0
:
angles
.
append
(
angle
)
break
# update frame of active_timebead
perc
=
(
sum
(
angles
)
/
len
(
angles
))
/
(
math
.
pi
/
2
)
perc
=
max
(
0.4
,
min
(
1
,
perc
*
5
))
if
side
==
"
left
"
:
bead_frame
=
kf_bead
-
perc
*
((
kf_bead
-
kf_prev
-
2
)
/
2
)
else
:
bead_frame
=
kf_bead
+
perc
*
((
kf_next
-
kf_bead
-
2
)
/
2
)
active_timebead
=
[
objectname
,
bead_frame
,
frame_ori
,
action_ob
,
child
]
return
(
active_keyframe
,
active_timebead
,
keyframes_ori
)
# revert changes made by dragging
def
cancel_drag
(
context
,
active_keyframe
,
active_handle
,
active_timebead
,
keyframes_ori
,
handles_ori
,
edit_bones
):
# revert change in 3d-location of active keyframe and its handles
if
context
.
window_manager
.
motion_trail
.
mode
==
'
location
'
and
\
active_keyframe
:
objectname
,
frame
,
frame_ori
,
active_ob
,
child
=
active_keyframe
curves
=
get_curves
(
active_ob
,
child
)
loc_ori
=
keyframes_ori
[
objectname
][
frame
][
1
]
if
child
:
loc_ori
=
loc_ori
*
edit_bones
[
child
.
name
]
*
\
active_ob
.
matrix_world
.
copy
().
inverted
()
for
i
,
curve
in
enumerate
(
curves
):
for
kf
in
curve
.
keyframe_points
:
if
kf
.
co
[
0
]
==
frame
:
kf
.
co
[
1
]
=
loc_ori
[
i
]
kf
.
handle_left
[
1
]
=
handles_ori
[
objectname
][
frame
]
\
[
"
left
"
][
i
][
1
]
kf
.
handle_right
[
1
]
=
handles_ori
[
objectname
][
frame
]
\
[
"
right
"
][
i
][
1
]
break
# revert change in 3d-location of active handle
elif
context
.
window_manager
.
motion_trail
.
mode
==
'
location
'
and
\
active_handle
:
objectname
,
frame
,
side
,
active_ob
,
child
=
active_handle
curves
=
get_curves
(
active_ob
,
child
)
for
i
,
curve
in
enumerate
(
curves
):
for
kf
in
curve
.
keyframe_points
:
if
kf
.
co
[
0
]
==
frame
:
kf
.
handle_left
[
1
]
=
handles_ori
[
objectname
][
frame
]
\
[
"
left
"
][
i
][
1
]
kf
.
handle_right
[
1
]
=
handles_ori
[
objectname
][
frame
]
\
[
"
right
"
][
i
][
1
]
break
# revert position of all keyframes and handles on timeline
elif
context
.
window_manager
.
motion_trail
.
mode
==
'
timing
'
and
\
active_timebead
:
objectname
,
frame
,
frame_ori
,
active_ob
,
child
=
active_timebead
curves
=
get_curves
(
active_ob
,
child
)
for
i
,
curve
in
enumerate
(
curves
):
for
kf
in
curve
.
keyframe_points
:
for
kf_ori
,
[
frame_ori
,
loc
]
in
keyframes_ori
[
objectname
].
\
items
():
if
abs
(
kf
.
co
[
0
]
-
kf_ori
)
<
1e-4
:
kf
.
co
[
0
]
=
frame_ori
kf
.
handle_left
[
0
]
=
handles_ori
[
objectname
]
\
[
frame_ori
][
"
left
"
][
i
][
0
]
kf
.
handle_right
[
0
]
=
handles_ori
[
objectname
]
\
[
frame_ori
][
"
right
"
][
i
][
0
]
break
# revert position of active keyframe and its handles on the timeline
elif
context
.
window_manager
.
motion_trail
.
mode
==
'
timing
'
and
\
active_keyframe
:
objectname
,
frame
,
frame_ori
,
active_ob
,
child
=
active_keyframe
curves
=
get_curves
(
active_ob
,
child
)
for
i
,
curve
in
enumerate
(
curves
):
for
kf
in
curve
.
keyframe_points
:
if
abs
(
kf
.
co
[
0
]
-
frame
)
<
1e-4
:
kf
.
co
[
0
]
=
keyframes_ori
[
objectname
][
frame_ori
][
0
]
kf
.
handle_left
[
0
]
=
handles_ori
[
objectname
][
frame_ori
]
\
[
"
left
"
][
i
][
0
]
kf
.
handle_right
[
0
]
=
handles_ori
[
objectname
][
frame_ori
]
\
[
"
right
"
][
i
][
0
]
break
active_keyframe
=
[
objectname
,
frame_ori
,
frame_ori
,
active_ob
,
child
]
# revert position of handles on the timeline
elif
context
.
window_manager
.
motion_trail
.
mode
==
'
speed
'
and
\
active_timebead
:
objectname
,
frame
,
frame_ori
,
active_ob
,
child
=
active_timebead
curves
=
get_curves
(
active_ob
,
child
)
keyframes
=
[
kf
for
kf
in
keyframes_ori
[
objectname
]]
keyframes
.
append
(
frame_ori
)
keyframes
.
sort
()
frame_index
=
keyframes
.
index
(
frame_ori
)
kf_prev
=
keyframes
[
frame_index
-
1
]
kf_next
=
keyframes
[
frame_index
+
1
]
if
(
kf_next
-
frame_ori
)
<
(
frame_ori
-
kf_prev
):
kf_frame
=
kf_next
else
:
kf_frame
=
kf_prev
for
i
,
curve
in
enumerate
(
curves
):
for
kf
in
curve
.
keyframe_points
:
if
kf
.
co
[
0
]
==
kf_frame
:
kf
.
handle_left
[
0
]
=
handles_ori
[
objectname
][
kf_frame
]
\
[
"
left
"
][
i
][
0
]
kf
.
handle_right
[
0
]
=
handles_ori
[
objectname
][
kf_frame
]
\
[
"
right
"
][
i
][
0
]
break
active_timebead
=
[
objectname
,
frame_ori
,
frame_ori
,
active_ob
,
child
]
return
(
active_keyframe
,
active_timebead
)
# return the handle type of the active selection
def
get_handle_type
(
active_keyframe
,
active_handle
):
if
active_keyframe
:
objectname
,
frame
,
side
,
action_ob
,
child
=
active_keyframe
side
=
"
both
"
elif
active_handle
:
objectname
,
frame
,
side
,
action_ob
,
child
=
active_handle
else
:
# no active handle(s)
return
(
False
)
# properties used when changing handle type
bpy
.
context
.
window_manager
.
motion_trail
.
handle_type_frame
=
frame
bpy
.
context
.
window_manager
.
motion_trail
.
handle_type_side
=
side
bpy
.
context
.
window_manager
.
motion_trail
.
handle_type_action_ob
=
\
action_ob
.
name
if
child
:
bpy
.
context
.
window_manager
.
motion_trail
.
handle_type_child
=
child
.
name
else
:
bpy
.
context
.
window_manager
.
motion_trail
.
handle_type_child
=
""
curves
=
get_curves
(
action_ob
,
child
=
child
)
for
c
in
curves
:
for
kf
in
c
.
keyframe_points
:
if
kf
.
co
[
0
]
==
frame
:
if
side
in
[
"
left
"
,
"
both
"
]:
return
(
kf
.
handle_left_type
)
else
:
return
(
kf
.
handle_right_type
)
return
(
"
AUTO
"
)
# turn the given frame into a keyframe
def
insert_keyframe
(
self
,
context
,
frame
):
objectname
,
frame
,
frame
,
action_ob
,
child
=
frame
curves
=
get_curves
(
action_ob
,
child
)
for
c
in
curves
:
y
=
c
.
evaluate
(
frame
)
if
c
.
keyframe_points
:
c
.
keyframe_points
.
insert
(
frame
,
y
)
bpy
.
context
.
window_manager
.
motion_trail
.
force_update
=
True
calc_callback
(
self
,
context
)
# change the handle type of the active selection
def
set_handle_type
(
self
,
context
):
if
not
context
.
window_manager
.
motion_trail
.
handle_type_enabled
:
return
if
context
.
window_manager
.
motion_trail
.
handle_type_old
==
\
context
.
window_manager
.
motion_trail
.
handle_type
:
# function called because of selection change, not change in type
return
context
.
window_manager
.
motion_trail
.
handle_type_old
=
\
context
.
window_manager
.
motion_trail
.
handle_type
frame
=
bpy
.
context
.
window_manager
.
motion_trail
.
handle_type_frame
side
=
bpy
.
context
.
window_manager
.
motion_trail
.
handle_type_side
action_ob
=
bpy
.
context
.
window_manager
.
motion_trail
.
handle_type_action_ob
action_ob
=
bpy
.
data
.
objects
[
action_ob
]
child
=
bpy
.
context
.
window_manager
.
motion_trail
.
handle_type_child
if
child
:
child
=
action_ob
.
pose
.
bones
[
child
]
new_type
=
context
.
window_manager
.
motion_trail
.
handle_type
curves
=
get_curves
(
action_ob
,
child
=
child
)
for
c
in
curves
:
for
kf
in
c
.
keyframe_points
:
if
kf
.
co
[
0
]
==
frame
:
# align if necessary
if
side
in
[
"
right
"
,
"
both
"
]
and
new_type
in
\
[
"
AUTO
"
,
"
ALIGNED
"
]:
# change right handle
normal
=
(
kf
.
co
-
kf
.
handle_left
).
normalized
()
size
=
(
kf
.
handle_right
[
0
]
-
kf
.
co
[
0
])
/
normal
[
0
]
normal
=
normal
*
size
+
kf
.
co
kf
.
handle_right
[
1
]
=
normal
[
1
]
elif
side
==
"
left
"
and
new_type
in
[
"
AUTO
"
,
"
ALIGNED
"
]:
# change left handle
normal
=
(
kf
.
co
-
kf
.
handle_right
).
normalized
()
size
=
(
kf
.
handle_left
[
0
]
-
kf
.
co
[
0
])
/
normal
[
0
]
normal
=
normal
*
size
+
kf
.
co
kf
.
handle_left
[
1
]
=
normal
[
1
]
# change type
if
side
in
[
"
left
"
,
"
both
"
]:
kf
.
handle_left_type
=
new_type
if
side
in
[
"
right
"
,
"
both
"
]:
kf
.
handle_right_type
=
new_type
context
.
window_manager
.
motion_trail
.
force_update
=
True
class
MotionTrailOperator
(
bpy
.
types
.
Operator
):
'''
Edit motion trails in 3d-view
'''
bl_idname
=
"
view3d.motion_trail
"
bl_label
=
"
Motion Trail
"
_handle1
=
None
_handle2
=
None
def
modal
(
self
,
context
,
event
):
if
context
.
window_manager
.
motion_trail
.
enabled
==
-
1
:
context
.
window_manager
.
motion_trail
.
enabled
=
0
try
:
context
.
region
.
callback_remove
(
self
.
_handle1
)
except
:
pass
try
:
context
.
region
.
callback_remove
(
self
.
_handle2
)
except
:
pass
context
.
area
.
tag_redraw
()
return
{
'
FINISHED
'
}
if
not
context
.
area
:
return
{
'
PASS_THROUGH
'
}
if
not
context
.
region
or
event
.
type
==
'
NONE
'
:
context
.
area
.
tag_redraw
()
return
{
'
PASS_THROUGH
'
}
select
=
context
.
user_preferences
.
inputs
.
select_mouse
if
not
context
.
active_object
or
not
context
.
active_object
.
mode
in
\
[
'
OBJECT
'
,
'
POSE
'
]:
if
self
.
drag
:
self
.
drag
=
False
self
.
lock
=
True
context
.
window_manager
.
motion_trail
.
force_update
=
True
# default hotkeys should still work
if
event
.
type
==
self
.
transform_key
and
event
.
value
==
'
PRESS
'
:
if
bpy
.
ops
.
transform
.
translate
.
poll
():
bpy
.
ops
.
transform
.
translate
(
'
INVOKE_DEFAULT
'
)
elif
event
.
type
==
select
+
'
MOUSE
'
and
event
.
value
==
'
PRESS
'
\
and
not
self
.
drag
and
not
event
.
shift
and
not
event
.
alt
\
and
not
event
.
ctrl
:
if
bpy
.
ops
.
view3d
.
select
.
poll
():
bpy
.
ops
.
view3d
.
select
(
'
INVOKE_DEFAULT
'
)
elif
event
.
type
==
'
LEFTMOUSE
'
and
event
.
value
==
'
PRESS
'
and
not
\
event
.
alt
and
not
event
.
ctrl
and
not
event
.
shift
:
if
eval
(
"
bpy.ops.
"
+
self
.
left_action
+
"
.poll()
"
):
eval
(
"
bpy.ops.
"
+
self
.
left_action
+
"
(
'
INVOKE_DEFAULT
'
)
"
)
return
{
'
PASS_THROUGH
'
}
# check if event was generated within 3d-window, dragging is exception
if
not
self
.
drag
:
if
not
(
0
<
event
.
mouse_region_x
<
context
.
region
.
width
)
or
\
not
(
0
<
event
.
mouse_region_y
<
context
.
region
.
height
):
return
{
'
PASS_THROUGH
'
}
if
event
.
type
==
self
.
transform_key
and
event
.
value
==
'
PRESS
'
and
\
(
self
.
active_keyframe
or
self
.
active_handle
or
self
.
active_timebead
\
or
self
.
active_frame
):
# override default translate()
if
not
self
.
drag
:
# start drag
if
self
.
active_frame
:
insert_keyframe
(
self
,
context
,
self
.
active_frame
)
self
.
active_keyframe
=
self
.
active_frame
self
.
active_frame
=
False
self
.
keyframes_ori
,
self
.
handles_ori
=
\
get_original_animation_data
(
context
,
self
.
keyframes
)
self
.
drag_mouse_ori
=
mathutils
.
Vector
([
event
.
mouse_region_x
,
event
.
mouse_region_y
])
self
.
drag
=
True
self
.
lock
=
False
else
:
# stop drag
self
.
drag
=
False
self
.
lock
=
True
context
.
window_manager
.
motion_trail
.
force_update
=
True
elif
event
.
type
==
self
.
transform_key
and
event
.
value
==
'
PRESS
'
:
# call default translate()
if
bpy
.
ops
.
transform
.
translate
.
poll
():
bpy
.
ops
.
transform
.
translate
(
'
INVOKE_DEFAULT
'
)
elif
(
event
.
type
==
'
ESC
'
and
self
.
drag
and
event
.
value
==
'
PRESS
'
)
\
or
(
event
.
type
==
'
RIGHTMOUSE
'
and
self
.
drag
and
event
.
value
==
\
'
PRESS
'
):
# cancel drag
self
.
drag
=
False
self
.
lock
=
True
context
.
window_manager
.
motion_trail
.
force_update
=
True
self
.
active_keyframe
,
self
.
active_timebead
=
cancel_drag
(
context
,
self
.
active_keyframe
,
self
.
active_handle
,
self
.
active_timebead
,
self
.
keyframes_ori
,
self
.
handles_ori
,
self
.
edit_bones
)
elif
event
.
type
==
'
MOUSEMOVE
'
and
self
.
drag
:
# drag
self
.
active_keyframe
,
self
.
active_timebead
,
self
.
keyframes_ori
=
\
drag
(
context
,
event
,
self
.
drag_mouse_ori
,
self
.
active_keyframe
,
self
.
active_handle
,
self
.
active_timebead
,
self
.
keyframes_ori
,
self
.
handles_ori
,
self
.
edit_bones
)
elif
event
.
type
==
select
+
'
MOUSE
'
and
event
.
value
==
'
PRESS
'
and
\
not
self
.
drag
and
not
event
.
shift
and
not
event
.
alt
and
not
\
event
.
ctrl
:
# select
treshold
=
10
clicked
=
mathutils
.
Vector
([
event
.
mouse_region_x
,
event
.
mouse_region_y
])
self
.
active_keyframe
=
False
self
.
active_handle
=
False
self
.
active_timebead
=
False
self
.
active_frame
=
False
context
.
window_manager
.
motion_trail
.
force_update
=
True
context
.
window_manager
.
motion_trail
.
handle_type_enabled
=
True
found
=
False
if
context
.
window_manager
.
motion_trail
.
path_before
==
0
:
frame_min
=
context
.
scene
.
frame_start
else
:
frame_min
=
max
(
context
.
scene
.
frame_start
,
context
.
scene
.
frame_current
-
\
context
.
window_manager
.
motion_trail
.
path_before
)
if
context
.
window_manager
.
motion_trail
.
path_after
==
0
:
frame_max
=
context
.
scene
.
frame_end
else
:
frame_max
=
min
(
context
.
scene
.
frame_end
,
context
.
scene
.
frame_current
+
\
context
.
window_manager
.
motion_trail
.
path_after
)
for
objectname
,
values
in
self
.
click
.
items
():
if
found
:
break
for
frame
,
type
,
coord
,
action_ob
,
child
in
values
:
if
frame
<
frame_min
or
frame
>
frame_max
:
continue
if
(
coord
-
clicked
).
length
<=
treshold
:
found
=
True
if
type
==
"
keyframe
"
:
self
.
active_keyframe
=
[
objectname
,
frame
,
frame
,
action_ob
,
child
]
elif
type
==
"
handle_left
"
:
self
.
active_handle
=
[
objectname
,
frame
,
"
left
"
,
action_ob
,
child
]
elif
type
==
"
handle_right
"
:
self
.
active_handle
=
[
objectname
,
frame
,
"
right
"
,
action_ob
,
child
]
elif
type
==
"
timebead
"
:
self
.
active_timebead
=
[
objectname
,
frame
,
frame
,
action_ob
,
child
]
elif
type
==
"
frame
"
:
self
.
active_frame
=
[
objectname
,
frame
,
frame
,
action_ob
,
child
]
break
if
not
found
:
context
.
window_manager
.
motion_trail
.
handle_type_enabled
=
False
# no motion trail selections, so pass on to normal select()
if
bpy
.
ops
.
view3d
.
select
.
poll
():
bpy
.
ops
.
view3d
.
select
(
'
INVOKE_DEFAULT
'
)
else
:
handle_type
=
get_handle_type
(
self
.
active_keyframe
,
self
.
active_handle
)
if
handle_type
:
context
.
window_manager
.
motion_trail
.
handle_type_old
=
\
handle_type
context
.
window_manager
.
motion_trail
.
handle_type
=
\
handle_type
else
:
context
.
window_manager
.
motion_trail
.
handle_type_enabled
=
\
False
elif
event
.
type
==
'
LEFTMOUSE
'
and
event
.
value
==
'
PRESS
'
and
\
self
.
drag
:
# stop drag
self
.
drag
=
False
self
.
lock
=
True
context
.
window_manager
.
motion_trail
.
force_update
=
True
elif
event
.
type
==
'
LEFTMOUSE
'
and
event
.
value
==
'
PRESS
'
and
not
\
event
.
alt
and
not
event
.
ctrl
and
not
event
.
shift
:
if
eval
(
"
bpy.ops.
"
+
self
.
left_action
+
"
.poll()
"
):
eval
(
"
bpy.ops.
"
+
self
.
left_action
+
"
(
'
INVOKE_DEFAULT
'
)
"
)
if
context
.
area
:
# not available if other window-type is fullscreen
context
.
area
.
tag_redraw
()
return
{
'
PASS_THROUGH
'
}
def
invoke
(
self
,
context
,
event
):
if
context
.
area
.
type
==
'
VIEW_3D
'
:
# get clashing keymap items
select
=
context
.
user_preferences
.
inputs
.
select_mouse
kms
=
[
bpy
.
context
.
window_manager
.
keyconfigs
.
active
.
\
keymaps
[
'
3D View
'
],
bpy
.
context
.
window_manager
.
keyconfigs
.
\
active
.
keymaps
[
'
Object Mode
'
]]
kmis
=
[]
self
.
left_action
=
None
self
.
right_action
=
None
for
km
in
kms
:
for
kmi
in
km
.
keymap_items
:
if
kmi
.
idname
==
"
transform.translate
"
and
\
kmi
.
map_type
==
'
KEYBOARD
'
and
not
\
kmi
.
properties
.
texture_space
:
kmis
.
append
(
kmi
)
self
.
transform_key
=
kmi
.
type
elif
(
kmi
.
type
==
'
ACTIONMOUSE
'
and
select
==
'
RIGHT
'
)
\
and
not
kmi
.
alt
and
not
kmi
.
any
and
not
kmi
.
ctrl
\
and
not
kmi
.
shift
:
kmis
.
append
(
kmi
)
self
.
left_action
=
kmi
.
idname
elif
kmi
.
type
==
'
SELECTMOUSE
'
and
not
kmi
.
alt
and
not
\
kmi
.
any
and
not
kmi
.
ctrl
and
not
kmi
.
shift
:
kmis
.
append
(
kmi
)
if
select
==
'
RIGHT
'
:
self
.
right_action
=
kmi
.
idname
else
:
self
.
left_action
=
kmi
.
idname
elif
kmi
.
type
==
'
LEFTMOUSE
'
and
not
kmi
.
alt
and
not
\
kmi
.
any
and
not
kmi
.
ctrl
and
not
kmi
.
shift
:
kmis
.
append
(
kmi
)
self
.
left_action
=
kmi
.
idname
if
context
.
window_manager
.
motion_trail
.
enabled
==
0
:
# enable
context
.
window_manager
.
motion_trail
.
enabled
=
1
self
.
active_keyframe
=
False
self
.
active_handle
=
False
self
.
active_timebead
=
False
self
.
active_frame
=
False
self
.
click
=
{}
self
.
drag
=
False
self
.
lock
=
True
self
.
perspective
=
context
.
region_data
.
perspective_matrix
self
.
displayed
=
[]
context
.
window_manager
.
motion_trail
.
force_update
=
True
context
.
window_manager
.
motion_trail
.
handle_type_enabled
=
False
self
.
cached
=
{
"
path
"
:{},
"
keyframes
"
:{},
"
timebeads_timing
"
:{},
"
timebeads_speed
"
:{}}
for
kmi
in
kmis
:
kmi
.
active
=
False
context
.
window_manager
.
modal_handler_add
(
self
)
self
.
_handle1
=
context
.
region
.
callback_add
(
calc_callback
,
(
self
,
context
),
'
POST_VIEW
'
)
self
.
_handle2
=
context
.
region
.
callback_add
(
draw_callback
,
(
self
,
context
),
'
POST_PIXEL
'
)
if
context
.
area
:
context
.
area
.
tag_redraw
()
else
:
# disable
context
.
window_manager
.
motion_trail
.
enabled
=
-
1
for
kmi
in
kmis
:
kmi
.
active
=
True
return
{
'
RUNNING_MODAL
'
}
else
:
self
.
report
({
'
WARNING
'
},
"
View3D not found, cannot run operator
"
)
return
{
'
CANCELLED
'
}
class
MotionTrailPanel
(
bpy
.
types
.
Panel
):
bl_space_type
=
'
VIEW_3D
'
bl_region_type
=
'
TOOLS
'
bl_label
=
"
Motion Trail
"
@classmethod
def
poll
(
cls
,
context
):
if
not
context
.
active_object
:
return
(
False
)
return
(
context
.
active_object
.
mode
in
[
'
OBJECT
'
,
'
POSE
'
])
def
draw
(
self
,
context
):
col
=
self
.
layout
.
column
()
if
context
.
window_manager
.
motion_trail
.
enabled
!=
1
:
col
.
operator
(
"
view3d.motion_trail
"
,
text
=
"
Enable motion trail
"
)
else
:
col
.
operator
(
"
view3d.motion_trail
"
,
text
=
"
Disable motion trail
"
)
box
=
self
.
layout
.
box
()
box
.
prop
(
context
.
window_manager
.
motion_trail
,
"
mode
"
)
#box.prop(context.window_manager.motion_trail, "calculate")
if
context
.
window_manager
.
motion_trail
.
mode
in
[
'
timing
'
]:
box
.
prop
(
context
.
window_manager
.
motion_trail
,
"
timebeads
"
)
box
=
self
.
layout
.
box
()
col
=
box
.
column
()
row
=
col
.
row
()
if
context
.
window_manager
.
motion_trail
.
path_display
:
row
.
prop
(
context
.
window_manager
.
motion_trail
,
"
path_display
"
,
icon
=
"
DOWNARROW_HLT
"
,
text
=
""
,
emboss
=
False
)
else
:
row
.
prop
(
context
.
window_manager
.
motion_trail
,
"
path_display
"
,
icon
=
"
RIGHTARROW
"
,
text
=
""
,
emboss
=
False
)
row
.
label
(
"
Path options
"
)
if
context
.
window_manager
.
motion_trail
.
path_display
:
col
.
prop
(
context
.
window_manager
.
motion_trail
,
"
path_style
"
,
text
=
"
Style
"
)
grouped
=
col
.
column
(
align
=
True
)
grouped
.
prop
(
context
.
window_manager
.
motion_trail
,
"
path_width
"
,
text
=
"
Width
"
)
grouped
.
prop
(
context
.
window_manager
.
motion_trail
,
"
path_transparency
"
,
text
=
"
Transparency
"
)
grouped
.
prop
(
context
.
window_manager
.
motion_trail
,
"
path_resolution
"
)
row
=
grouped
.
row
(
align
=
True
)
row
.
prop
(
context
.
window_manager
.
motion_trail
,
"
path_before
"
)
row
.
prop
(
context
.
window_manager
.
motion_trail
,
"
path_after
"
)
col
=
col
.
column
(
align
=
True
)
col
.
prop
(
context
.
window_manager
.
motion_trail
,
"
keyframe_numbers
"
)
col
.
prop
(
context
.
window_manager
.
motion_trail
,
"
frame_display
"
)
if
context
.
window_manager
.
motion_trail
.
mode
in
[
'
location
'
]:
box
=
self
.
layout
.
box
()
col
=
box
.
column
(
align
=
True
)
col
.
prop
(
context
.
window_manager
.
motion_trail
,
"
handle_display
"
,
text
=
"
Handles
"
)
if
context
.
window_manager
.
motion_trail
.
handle_display
:
row
=
col
.
row
()
row
.
enabled
=
context
.
window_manager
.
motion_trail
.
\
handle_type_enabled
row
.
prop
(
context
.
window_manager
.
motion_trail
,
"
handle_type
"
)
class
MotionTrailProps
(
bpy
.
types
.
PropertyGroup
):
def
internal_update
(
self
,
context
):
context
.
window_manager
.
motion_trail
.
force_update
=
True
if
context
.
area
:
context
.
area
.
tag_redraw
()
# internal use
enabled
=
bpy
.
props
.
IntProperty
(
default
=
0
)
force_update
=
bpy
.
props
.
BoolProperty
(
name
=
"
internal use
"
,
description
=
"
Force calc_callback to fully execute
"
,
default
=
False
)
handle_type_enabled
=
bpy
.
props
.
BoolProperty
(
default
=
False
)
handle_type_frame
=
bpy
.
props
.
FloatProperty
()
handle_type_side
=
bpy
.
props
.
StringProperty
()
handle_type_action_ob
=
bpy
.
props
.
StringProperty
()
handle_type_child
=
bpy
.
props
.
StringProperty
()
handle_type_old
=
bpy
.
props
.
EnumProperty
(
items
=
((
"
AUTO
"
,
""
,
""
),
(
"
VECTOR
"
,
""
,
""
),
(
"
ALIGNED
"
,
""
,
""
),
(
"
FREE
"
,
""
,
""
)),
default
=
'
AUTO
'
,)
# visible in user interface
calculate
=
bpy
.
props
.
EnumProperty
(
name
=
"
Calculate
"
,
items
=
((
"
fast
"
,
"
Fast
"
,
"
Recommended setting, change if the
"
\
"
motion path is positioned incorrectly
"
),
(
"
full
"
,
"
Full
"
,
"
Takes parenting and modifiers into account,
"
\
"
but can be very slow on complicated scenes
"
)),
description
=
"
Calculation method for determining locations
"
,
default
=
'
full
'
,
update
=
internal_update
)
frame_display
=
bpy
.
props
.
BoolProperty
(
name
=
"
Frames
"
,
description
=
"
Display frames,
\n
test
"
,
default
=
True
,
update
=
internal_update
)
handle_display
=
bpy
.
props
.
BoolProperty
(
name
=
"
Display
"
,
description
=
"
Display handles
"
,
default
=
True
,
update
=
internal_update
)
handle_type
=
bpy
.
props
.
EnumProperty
(
name
=
"
Type
"
,
items
=
((
"
AUTO
"
,
"
Automatic
"
,
""
),
(
"
VECTOR
"
,
"
Vector
"
,
""
),
(
"
ALIGNED
"
,
"
Aligned
"
,
""
),
(
"
FREE
"
,
"
Free
"
,
""
)),
description
=
"
Set handle type for the selected handle
"
,
default
=
'
AUTO
'
,
update
=
set_handle_type
)
keyframe_numbers
=
bpy
.
props
.
BoolProperty
(
name
=
"
Keyframe numbers
"
,
description
=
"
Display keyframe numbers
"
,
default
=
False
,
update
=
internal_update
)
mode
=
bpy
.
props
.
EnumProperty
(
name
=
"
Mode
"
,
items
=
((
"
location
"
,
"
Location
"
,
"
Change path that is followed
"
),
(
"
speed
"
,
"
Speed
"
,
"
Change speed between keyframes
"
),
(
"
timing
"
,
"
Timing
"
,
"
Change position of keyframes on timeline
"
)),
description
=
"
Enable editing of certain properties in the 3d-view
"
,
default
=
'
location
'
,
update
=
internal_update
)
path_after
=
bpy
.
props
.
IntProperty
(
name
=
"
After
"
,
description
=
"
Number of frames to show after the current frame,
"
\
"
0 = display all
"
,
default
=
50
,
min
=
0
,
update
=
internal_update
)
path_before
=
bpy
.
props
.
IntProperty
(
name
=
"
Before
"
,
description
=
"
Number of frames to show before the current frame,
"
\
"
0 = display all
"
,
default
=
50
,
min
=
0
,
update
=
internal_update
)
path_display
=
bpy
.
props
.
BoolProperty
(
name
=
"
Path options
"
,
description
=
"
Display path options
"
,
default
=
True
)
path_resolution
=
bpy
.
props
.
IntProperty
(
name
=
"
Resolution
"
,
description
=
"
10 is smoothest, but could be
"
\
"
slow when adjusting keyframes, handles or timebeads
"
,
default
=
10
,
min
=
1
,
max
=
10
,
update
=
internal_update
)
path_style
=
bpy
.
props
.
EnumProperty
(
name
=
"
Path style
"
,
items
=
((
"
acceleration
"
,
"
Acceleration
"
,
"
Gradient based on relative
"
\
"
acceleration
"
),
(
"
simple
"
,
"
Simple
"
,
"
Black line
"
),
(
"
speed
"
,
"
Speed
"
,
"
Gradient based on relative speed
"
)),
description
=
"
Information conveyed by path colour
"
,
default
=
'
simple
'
,
update
=
internal_update
)
path_transparency
=
bpy
.
props
.
IntProperty
(
name
=
"
Path transparency
"
,
description
=
"
Determines visibility of path
"
,
default
=
0
,
min
=
0
,
max
=
100
,
subtype
=
'
PERCENTAGE
'
,
update
=
internal_update
)
path_width
=
bpy
.
props
.
IntProperty
(
name
=
"
Path width
"
,
description
=
"
Width in pixels
"
,
default
=
1
,
min
=
1
,
soft_max
=
5
,
update
=
internal_update
)
timebeads
=
bpy
.
props
.
IntProperty
(
name
=
"
Time beads
"
,
description
=
"
Number of time beads to display per segment
"
,
default
=
5
,
min
=
1
,
soft_max
=
10
,
update
=
internal_update
)
classes
=
[
MotionTrailProps
,
MotionTrailOperator
,
MotionTrailPanel
]
def
register
():
for
c
in
classes
:
bpy
.
utils
.
register_class
(
c
)
bpy
.
types
.
WindowManager
.
motion_trail
=
bpy
.
props
.
PointerProperty
(
\
type
=
MotionTrailProps
)
def
unregister
():
for
c
in
classes
:
bpy
.
utils
.
unregister_class
(
c
)
del
bpy
.
types
.
WindowManager
.
motion_trail
if
__name__
==
"
__main__
"
:
register
()
This diff is collapsed.
Click to expand it.
Preview
0%
Loading
Try again
or
attach a new file
.
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Save comment
Cancel
Please
sign in
to comment