1 # ##### BEGIN GPL LICENSE BLOCK #####
3 # This program is free software; you can redistribute it and/or
4 # modify it under the terms of the GNU General Public License
5 # as published by the Free Software Foundation; either version 2
6 # of the License, or (at your option) any later version.
8 # This program is distributed in the hope that it will be useful,
9 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # GNU General Public License for more details.
13 # You should have received a copy of the GNU General Public License
14 # along with this program; if not, write to the Free Software Foundation,
15 # Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17 # ##### END GPL LICENSE BLOCK #####
19 '''Based on GP_refine_stroke 0.2.4 - Author: Samuel Bernou'''
27 return mean of all passed value (multiple)
28 If it's a list or tuple return mean of it (only on first list passed).
30 if isinstance(args
[0], list) or isinstance(args
[0], tuple):
31 return mean(*args
[0])#send the first list UNPACKED (else infinite recursion as it always evaluate as list)
32 return sum(args
) / len(args
)
34 def vector_len_from_coord(a
, b
):
36 Get two points (that has coordinate 'co' attribute) or Vectors (2D or 3D)
37 Return length as float
39 from mathutils
import Vector
43 return (a
.co
- b
.co
).length
45 def point_from_dist_in_segment_3d(a
, b
, ratio
):
46 '''return the tuple coords of a point on 3D segment ab according to given ratio (some distance divided by total segment lenght)'''
47 ## ref:https://math.stackexchange.com/questions/175896/finding-a-point-along-a-line-a-certain-distance-away-from-another-point
48 # ratio = dist / seglenght
49 return ( ((1 - ratio
) * a
[0] + (ratio
*b
[0])), ((1 - ratio
) * a
[1] + (ratio
*b
[1])), ((1 - ratio
) * a
[2] + (ratio
*b
[2])) )
51 def get_stroke_length(s
):
52 '''return 3D total length of the stroke'''
54 for i
in range(0, len(s
.points
)-1):
55 #print(vector_len_from_coord(s.points[i],s.points[i+1]))
56 all_len
+= vector_len_from_coord(s
.points
[i
],s
.points
[i
+1])
61 def to_straight_line(s
, keep_points
=True, influence
=100, straight_pressure
=True):
63 keep points : if false only start and end point stay
64 straight_pressure : (not available with keep point) take the mean pressure of all points and apply to stroke.
68 if p_len
<= 2: # 1 or 2 points only, cancel
72 if straight_pressure
: mean_pressure
= mean([p
.pressure
for p
in s
.points
])#can use a foreach_get but might not be faster.
73 for i
in range(p_len
-2):
77 p
.pressure
= mean_pressure
82 # ab_dist = vector_len_from_coord(A,B)
83 full_dist
= get_stroke_length(s
)
87 for i
in range(1, p_len
-1):#all but first and last
88 dist_from_start
+= vector_len_from_coord(s
.points
[i
-1],s
.points
[i
])
89 ratio
= dist_from_start
/ full_dist
90 # dont apply directly (change line as we measure it in loop)
91 coord_list
.append( point_from_dist_in_segment_3d(A
, B
, ratio
) )
94 for i
in range(1, p_len
-1):
95 ## Direct super straight 100%
96 #s.points[i].co = coord_list[i-1]
99 s
.points
[i
].co
= point_from_dist_in_segment_3d(s
.points
[i
].co
, coord_list
[i
-1], influence
/ 100)
103 def get_last_index(context
=None):
105 context
= bpy
.context
106 return 0 if context
.tool_settings
.use_gpencil_draw_onback
else -1
110 class GP_OT_straightStroke(bpy
.types
.Operator
):
111 bl_idname
= "gp.straight_stroke"
112 bl_label
= "Straight Stroke"
113 bl_description
= "Make stroke a straight line between first and last point, tweak influence in the redo panel\
114 \nshift+click to reset infuence to 100%"
115 bl_options
= {"REGISTER", "UNDO"}
118 def poll(cls
, context
):
119 return context
.active_object
is not None and context
.object.type == 'GPENCIL'
120 #and context.mode in ('PAINT_GPENCIL', 'EDIT_GPENCIL')
122 influence_val
: bpy
.props
.FloatProperty(name
="Straight force", description
="Straight interpolation percentage",
123 default
=100, min=0, max=100, step
=2, precision
=1, subtype
='PERCENTAGE', unit
='NONE')
125 def execute(self
, context
):
126 gp
= context
.object.data
131 if context
.mode
== 'PAINT_GPENCIL':
132 if not gpl
.active
or not gpl
.active
.active_frame
:
133 self
.report({'ERROR'}, 'No Grease pencil frame found')
136 if not len(gpl
.active
.active_frame
.strokes
):
137 self
.report({'ERROR'}, 'No strokes found.')
140 s
= gpl
.active
.active_frame
.strokes
[get_last_index(context
)]
141 to_straight_line(s
, keep_points
=True, influence
=self
.influence_val
)
143 elif context
.mode
== 'EDIT_GPENCIL':
146 if l
.lock
or l
.hide
or not l
.active_frame
:
147 # avoid locked, hided, empty layers
150 target_frames
= [f
for f
in l
.frames
if f
.select
]
152 target_frames
= [l
.active_frame
]
154 for f
in target_frames
:
158 to_straight_line(s
, keep_points
=True, influence
=self
.influence_val
)
161 self
.report({'ERROR'}, 'No selected stroke found.')
165 # if context.mode == 'PAINT_GPENCIL':
166 # L, F, S = 'ACTIVE', 'ACTIVE', 'LAST'
167 # elif context.mode == 'EDIT_GPENCIL'
168 # L, F, S = 'ALL', 'ACTIVE', 'SELECT'
169 # if gp.use_multiedit: F = 'SELECT'
170 # else : return {"CANCELLED"}
171 # for s in strokelist(t_layer=L, t_frame=F, t_stroke=S):
172 # to_straight_line(s, keep_points=True, influence = self.influence_val)#, straight_pressure=True
176 def draw(self
, context
):
178 layout
.prop(self
, "influence_val")
180 def invoke(self
, context
, event
):
181 if context
.mode
not in ('PAINT_GPENCIL', 'EDIT_GPENCIL'):
184 self
.influence_val
= 100
185 return self
.execute(context
)
189 bpy
.utils
.register_class(GP_OT_straightStroke
)
192 bpy
.utils
.unregister_class(GP_OT_straightStroke
)