Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender-addons.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPullusb <bernou.samuel@gmail.com>2020-10-02 15:20:26 +0300
committerPullusb <bernou.samuel@gmail.com>2020-10-02 15:20:26 +0300
commit9da77e9af4cbf4d91d043c0a670b6e507531b67d (patch)
tree92d60db0435585ece5e9593f36db427bf2335859 /greasepencil_tools/line_reshape.py
parent861eb3e249aef55337bf437edb2220e6cb1865e5 (diff)
greasepencil-addon first push
Diffstat (limited to 'greasepencil_tools/line_reshape.py')
-rw-r--r--greasepencil_tools/line_reshape.py192
1 files changed, 192 insertions, 0 deletions
diff --git a/greasepencil_tools/line_reshape.py b/greasepencil_tools/line_reshape.py
new file mode 100644
index 00000000..608b95b2
--- /dev/null
+++ b/greasepencil_tools/line_reshape.py
@@ -0,0 +1,192 @@
+# ##### 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 #####
+
+'''Based on GP_refine_stroke 0.2.4 - Author: Samuel Bernou'''
+
+import bpy
+
+### --- Vector utils
+
+def mean(*args):
+ '''
+ return mean of all passed value (multiple)
+ If it's a list or tuple return mean of it (only on first list passed).
+ '''
+ if isinstance(args[0], list) or isinstance(args[0], tuple):
+ return mean(*args[0])#send the first list UNPACKED (else infinite recursion as it always evaluate as list)
+ return sum(args) / len(args)
+
+def vector_len_from_coord(a, b):
+ '''
+ Get two points (that has coordinate 'co' attribute) or Vectors (2D or 3D)
+ Return length as float
+ '''
+ from mathutils import Vector
+ if type(a) is Vector:
+ return (a - b).length
+ else:
+ return (a.co - b.co).length
+
+def point_from_dist_in_segment_3d(a, b, ratio):
+ '''return the tuple coords of a point on 3D segment ab according to given ratio (some distance divided by total segment lenght)'''
+ ## ref:https://math.stackexchange.com/questions/175896/finding-a-point-along-a-line-a-certain-distance-away-from-another-point
+ # ratio = dist / seglenght
+ return ( ((1 - ratio) * a[0] + (ratio*b[0])), ((1 - ratio) * a[1] + (ratio*b[1])), ((1 - ratio) * a[2] + (ratio*b[2])) )
+
+def get_stroke_length(s):
+ '''return 3D total length of the stroke'''
+ all_len = 0.0
+ for i in range(0, len(s.points)-1):
+ #print(vector_len_from_coord(s.points[i],s.points[i+1]))
+ all_len += vector_len_from_coord(s.points[i],s.points[i+1])
+ return (all_len)
+
+### --- Functions
+
+def to_straight_line(s, keep_points=True, influence=100, straight_pressure=True):
+ '''
+ keep points : if false only start and end point stay
+ straight_pressure : (not available with keep point) take the mean pressure of all points and apply to stroke.
+ '''
+
+ p_len = len(s.points)
+ if p_len <= 2: # 1 or 2 points only, cancel
+ return
+
+ if not keep_points:
+ if straight_pressure: mean_pressure = mean([p.pressure for p in s.points])#can use a foreach_get but might not be faster.
+ for i in range(p_len-2):
+ s.points.pop(index=1)
+ if straight_pressure:
+ for p in s.points:
+ p.pressure = mean_pressure
+
+ else:
+ A = s.points[0].co
+ B = s.points[-1].co
+ # ab_dist = vector_len_from_coord(A,B)
+ full_dist = get_stroke_length(s)
+ dist_from_start = 0.0
+ coord_list = []
+
+ for i in range(1, p_len-1):#all but first and last
+ dist_from_start += vector_len_from_coord(s.points[i-1],s.points[i])
+ ratio = dist_from_start / full_dist
+ # dont apply directly (change line as we measure it in loop)
+ coord_list.append( point_from_dist_in_segment_3d(A, B, ratio) )
+
+ # apply change
+ for i in range(1, p_len-1):
+ ## Direct super straight 100%
+ #s.points[i].co = coord_list[i-1]
+
+ ## With influence
+ s.points[i].co = point_from_dist_in_segment_3d(s.points[i].co, coord_list[i-1], influence / 100)
+
+ return
+
+def get_last_index(context=None):
+ if not context:
+ context = bpy.context
+ return 0 if context.tool_settings.use_gpencil_draw_onback else -1
+
+### --- OPS
+
+class GP_OT_straightStroke(bpy.types.Operator):
+ bl_idname = "gp.straight_stroke"
+ bl_label = "Straight Stroke"
+ bl_description = "Make stroke a straight line between first and last point, tweak influence in the redo panel\
+ \nshift+click to reset infuence to 100%"
+ bl_options = {"REGISTER", "UNDO"}
+
+ @classmethod
+ def poll(cls, context):
+ return context.active_object is not None and context.object.type == 'GPENCIL'
+ #and context.mode in ('PAINT_GPENCIL', 'EDIT_GPENCIL')
+
+ influence_val : bpy.props.FloatProperty(name="Straight force", description="Straight interpolation percentage",
+ default=100, min=0, max=100, step=2, precision=1, subtype='PERCENTAGE', unit='NONE')
+
+ def execute(self, context):
+ gp = context.object.data
+ gpl = gp.layers
+ if not gpl:
+ return {"CANCELLED"}
+
+ if context.mode == 'PAINT_GPENCIL':
+ if not gpl.active or not gpl.active.active_frame:
+ self.report({'ERROR'}, 'No Grease pencil frame found')
+ return {"CANCELLED"}
+
+ if not len(gpl.active.active_frame.strokes):
+ self.report({'ERROR'}, 'No strokes found.')
+ return {"CANCELLED"}
+
+ s = gpl.active.active_frame.strokes[get_last_index(context)]
+ to_straight_line(s, keep_points=True, influence=self.influence_val)
+
+ elif context.mode == 'EDIT_GPENCIL':
+ ct = 0
+ for l in gpl:
+ if l.lock or l.hide or not l.active_frame:
+ # avoid locked, hided, empty layers
+ continue
+ if gp.use_multiedit:
+ target_frames = [f for f in l.frames if f.select]
+ else:
+ target_frames = [l.active_frame]
+
+ for f in target_frames:
+ for s in f.strokes:
+ if s.select:
+ ct += 1
+ to_straight_line(s, keep_points=True, influence=self.influence_val)
+
+ if not ct:
+ self.report({'ERROR'}, 'No selected stroke found.')
+ return {"CANCELLED"}
+
+ ## filter method
+ # if context.mode == 'PAINT_GPENCIL':
+ # L, F, S = 'ACTIVE', 'ACTIVE', 'LAST'
+ # elif context.mode == 'EDIT_GPENCIL'
+ # L, F, S = 'ALL', 'ACTIVE', 'SELECT'
+ # if gp.use_multiedit: F = 'SELECT'
+ # else : return {"CANCELLED"}
+ # for s in strokelist(t_layer=L, t_frame=F, t_stroke=S):
+ # to_straight_line(s, keep_points=True, influence = self.influence_val)#, straight_pressure=True
+
+ return {"FINISHED"}
+
+ def draw(self, context):
+ layout = self.layout
+ layout.prop(self, "influence_val")
+
+ def invoke(self, context, event):
+ if context.mode not in ('PAINT_GPENCIL', 'EDIT_GPENCIL'):
+ return {"CANCELLED"}
+ if event.shift:
+ self.influence_val = 100
+ return self.execute(context)
+
+
+def register():
+ bpy.utils.register_class(GP_OT_straightStroke)
+
+def unregister():
+ bpy.utils.unregister_class(GP_OT_straightStroke)