Newer
Older
self.using_external_curves = True
CoDEmanX
committed
bpy.ops.object.editmode_toggle('INVOKE_REGION_WIN')
CoDEmanX
committed
#### Make sure there are no objects left from erroneous executions of this operator, with the reserved names used here.
for o in bpy.data.objects:
if o.name.find("SURFSKIO_") != -1:
bpy.ops.object.select_all('INVOKE_REGION_WIN', action='DESELECT')
bpy.data.objects[o.name].select = True
bpy.context.scene.objects.active = bpy.data.objects[o.name]
CoDEmanX
committed
CoDEmanX
committed
bpy.ops.object.select_all('INVOKE_REGION_WIN', action='DESELECT')
bpy.data.objects[self.original_curve.name].select = True
bpy.context.scene.objects.active = bpy.data.objects[self.original_curve.name]
CoDEmanX
committed
bpy.ops.object.duplicate('INVOKE_REGION_WIN')
CoDEmanX
committed
self.temporary_curve = bpy.context.scene.objects.active
CoDEmanX
committed
# Deselect all points of the curve
bpy.ops.object.editmode_toggle('INVOKE_REGION_WIN')
bpy.ops.curve.select_all('INVOKE_REGION_WIN', action='DESELECT')
bpy.ops.object.editmode_toggle('INVOKE_REGION_WIN')
CoDEmanX
committed
# Delete splines with only a single isolated point.
for i in range(len(self.temporary_curve.data.splines)):
sp = self.temporary_curve.data.splines[i]
CoDEmanX
committed
if len(sp.bezier_points) == 1:
sp.bezier_points[0].select_control_point = True
CoDEmanX
committed
bpy.ops.object.editmode_toggle('INVOKE_REGION_WIN')
bpy.ops.object.editmode_toggle('INVOKE_REGION_WIN')
CoDEmanX
committed
bpy.ops.object.select_all('INVOKE_REGION_WIN', action='DESELECT')
bpy.data.objects[self.temporary_curve.name].select = True
bpy.context.scene.objects.active = bpy.data.objects[self.temporary_curve.name]
CoDEmanX
committed
#### Set a minimum number of points for crosshatch
minimum_points_num = 15
CoDEmanX
committed
bpy.ops.object.editmode_toggle('INVOKE_REGION_WIN')
# Check if the number of points of each curve has at least the number of points of minimum_points_num, which is a bit more than the face-loops limit. If not, subdivide to reach at least that number of ponts.
for i in range(len(self.temporary_curve.data.splines)):
sp = self.temporary_curve.data.splines[i]
CoDEmanX
committed
if len(sp.bezier_points) < minimum_points_num:
for bp in sp.bezier_points:
bp.select_control_point = True
CoDEmanX
committed
if (len(sp.bezier_points) - 1) != 0:
subdivide_cuts = int((minimum_points_num - len(sp.bezier_points)) / (len(sp.bezier_points) - 1)) + 1 # Formula to get the number of cuts that will make a curve of N number of points have near to "minimum_points_num" points, when subdividing with this number of cuts.
else:
subdivide_cuts = 0
CoDEmanX
committed
bpy.ops.curve.subdivide('INVOKE_REGION_WIN', number_cuts = subdivide_cuts)
bpy.ops.curve.select_all('INVOKE_REGION_WIN', action='DESELECT')
CoDEmanX
committed
bpy.ops.object.editmode_toggle('INVOKE_REGION_WIN')
CoDEmanX
committed
# Detect if the strokes are a crosshatch and do it if it is.
self.crosshatch_surface_invoke(self.temporary_curve)
CoDEmanX
committed
if not self.is_crosshatch:
bpy.ops.object.select_all('INVOKE_REGION_WIN', action='DESELECT')
bpy.data.objects[self.temporary_curve.name].select = True
bpy.context.scene.objects.active = bpy.data.objects[self.temporary_curve.name]
CoDEmanX
committed
bpy.ops.object.editmode_toggle('INVOKE_REGION_WIN')
CoDEmanX
committed
#### Set a minimum number of points for rectangular surfaces.
minimum_points_num = 60
CoDEmanX
committed
# Check if the number of points of each curve has at least the number of points of minimum_points_num, which is a bit more than the face-loops limit. If not, subdivide to reach at least that number of ponts.
for i in range(len(self.temporary_curve.data.splines)):
sp = self.temporary_curve.data.splines[i]
CoDEmanX
committed
if len(sp.bezier_points) < minimum_points_num:
for bp in sp.bezier_points:
bp.select_control_point = True
CoDEmanX
committed
if (len(sp.bezier_points) - 1) != 0:
subdivide_cuts = int((minimum_points_num - len(sp.bezier_points)) / (len(sp.bezier_points) - 1)) + 1 # Formula to get the number of cuts that will make a curve of N number of points have near to "minimum_points_num" points, when subdividing with this number of cuts.
else:
subdivide_cuts = 0
CoDEmanX
committed
bpy.ops.curve.subdivide('INVOKE_REGION_WIN', number_cuts = subdivide_cuts)
bpy.ops.curve.select_all('INVOKE_REGION_WIN', action='DESELECT')
CoDEmanX
committed
bpy.ops.object.editmode_toggle('INVOKE_REGION_WIN')
CoDEmanX
committed
# Save coordinates of the actual strokes (as the "last saved splines").
for sp_idx in range(len(self.temporary_curve.data.splines)):
self.last_strokes_splines_coords.append([])
for bp_idx in range(len(self.temporary_curve.data.splines[sp_idx].bezier_points)):
coords = self.temporary_curve.matrix_world * self.temporary_curve.data.splines[sp_idx].bezier_points[bp_idx].co
self.last_strokes_splines_coords[sp_idx].append([coords[0], coords[1], coords[2]])
CoDEmanX
committed
# Check for cyclic splines, put the first and last points in the middle of their actual positions.
for sp_idx in range(len(self.temporary_curve.data.splines)):
if self.temporary_curve.data.splines[sp_idx].use_cyclic_u == True:
first_p_co = self.last_strokes_splines_coords[sp_idx][0]
last_p_co = self.last_strokes_splines_coords[sp_idx][len(self.last_strokes_splines_coords[sp_idx]) - 1]
CoDEmanX
committed
target_co = [(first_p_co[0] + last_p_co[0]) / 2, (first_p_co[1] + last_p_co[1]) / 2, (first_p_co[2] + last_p_co[2]) / 2]
CoDEmanX
committed
self.last_strokes_splines_coords[sp_idx][0] = target_co
self.last_strokes_splines_coords[sp_idx][len(self.last_strokes_splines_coords[sp_idx]) - 1] = target_co
CoDEmanX
committed
tuple(self.last_strokes_splines_coords)
CoDEmanX
committed
# Estimation of the average length of the segments between each point of the grease pencil strokes. Will be useful to determine whether a curve should be made "Cyclic".
segments_lengths_sum = 0
segments_count = 0
random_spline = self.temporary_curve.data.splines[0].bezier_points
for i in range(0, len(random_spline)):
if i != 0 and len(random_spline) - 1 >= i:
segments_lengths_sum += (random_spline[i - 1].co - random_spline[i].co).length
segments_count += 1
CoDEmanX
committed
self.average_gp_segment_length = segments_lengths_sum / segments_count
CoDEmanX
committed
#### Delete temporary strokes curve object
bpy.ops.object.select_all('INVOKE_REGION_WIN', action='DESELECT')
bpy.data.objects[self.temporary_curve.name].select = True
bpy.context.scene.objects.active = bpy.data.objects[self.temporary_curve.name]
CoDEmanX
committed
CoDEmanX
committed
bpy.ops.object.select_all('INVOKE_REGION_WIN', action='DESELECT')
bpy.data.objects[self.main_object.name].select = True
bpy.context.scene.objects.active = bpy.data.objects[self.main_object.name]
CoDEmanX
committed
bpy.ops.object.editmode_toggle('INVOKE_REGION_WIN')
CoDEmanX
committed
self.execute(context)
bpy.context.user_preferences.edit.use_global_undo = False # Set again since "execute()" will turn it again to its initial value.
CoDEmanX
committed
#### If "Keep strokes" option is not active, delete original strokes curve object.
if (not self.stopping_errors and not self.keep_strokes) or self.is_crosshatch:
bpy.ops.object.editmode_toggle('INVOKE_REGION_WIN')
bpy.ops.object.select_all('INVOKE_REGION_WIN', action='DESELECT')
bpy.data.objects[self.original_curve.name].select = True
bpy.context.scene.objects.active = bpy.data.objects[self.original_curve.name]
CoDEmanX
committed
CoDEmanX
committed
bpy.ops.object.select_all('INVOKE_REGION_WIN', action='DESELECT')
bpy.data.objects[self.main_object.name].select = True
bpy.context.scene.objects.active = bpy.data.objects[self.main_object.name]
CoDEmanX
committed
bpy.ops.object.editmode_toggle('INVOKE_REGION_WIN')
CoDEmanX
committed
#### Delete grease pencil strokes.
if self.strokes_type == "GP_STROKES" and not self.stopping_errors:
bpy.ops.gpencil.active_frame_delete('INVOKE_REGION_WIN')
CoDEmanX
committed
bpy.context.user_preferences.edit.use_global_undo = self.initial_global_undo_state
CoDEmanX
committed
if not self.stopping_errors:
return {"FINISHED"}
else:
return{"CANCELLED"}
CoDEmanX
committed
elif self.strokes_type == "SELECTION_ALONE":
self.is_fill_faces = True
CoDEmanX
committed
created_faces_count = self.fill_with_faces(self.main_object)
CoDEmanX
committed
bpy.ops.object.editmode_toggle('INVOKE_REGION_WIN')
CoDEmanX
committed
if created_faces_count == 0:
self.report({'WARNING'}, "There aren't any strokes.")
return {"CANCELLED"}
else:
return {"FINISHED"}
CoDEmanX
committed
elif self.strokes_type == "EXTERNAL_NO_CURVE":
self.report({'WARNING'}, "The secondary object is not a Curve.")
return{"CANCELLED"}
CoDEmanX
committed
elif self.strokes_type == "MORE_THAN_ONE_EXTERNAL":
self.report({'WARNING'}, "There shouldn't be more than one secondary object selected.")
return{"CANCELLED"}
CoDEmanX
committed
elif self.strokes_type == "SINGLE_GP_STROKE_NO_SELECTION" or self.strokes_type == "SINGLE_CURVE_STROKE_NO_SELECTION":
self.report({'WARNING'}, "It's needed at least one stroke and one selection, or two strokes.")
return{"CANCELLED"}
CoDEmanX
committed
elif self.strokes_type == "NO_STROKES":
self.report({'WARNING'}, "There aren't any strokes.")
return{"CANCELLED"}
CoDEmanX
committed
elif self.strokes_type == "CURVE_WITH_NON_BEZIER_SPLINES":
self.report({'WARNING'}, "All splines must be Bezier.")
return{"CANCELLED"}
CoDEmanX
committed
else:
return{"CANCELLED"}
# Edit strokes operator.
class GPENCIL_OT_SURFSK_edit_strokes(bpy.types.Operator):
bl_idname = "gpencil.surfsk_edit_strokes"
bl_label = "Bsurfaces edit strokes"
bl_description = "Edit the grease pencil strokes or curves used."
CoDEmanX
committed
def execute(self, context):
#### Determine the type of the strokes.
self.strokes_type = get_strokes_type(self.main_object)
#### Check if strokes are grease pencil strokes or a curves object.
selected_objs = bpy.context.selected_objects
if self.strokes_type == "EXTERNAL_CURVE" or self.strokes_type == "SINGLE_CURVE_STROKE_NO_SELECTION":
for ob in selected_objs:
if ob != bpy.context.scene.objects.active:
curve_ob = ob
CoDEmanX
committed
bpy.ops.object.editmode_toggle('INVOKE_REGION_WIN')
CoDEmanX
committed
bpy.ops.object.select_all('INVOKE_REGION_WIN', action='DESELECT')
bpy.data.objects[curve_ob.name].select = True
bpy.context.scene.objects.active = bpy.data.objects[curve_ob.name]
CoDEmanX
committed
bpy.ops.object.editmode_toggle('INVOKE_REGION_WIN')
elif self.strokes_type == "GP_STROKES" or self.strokes_type == "SINGLE_GP_STROKE_NO_SELECTION":
#### Convert grease pencil strokes to curve.
bpy.ops.object.editmode_toggle('INVOKE_REGION_WIN')
bpy.ops.gpencil.convert('INVOKE_REGION_WIN', type='CURVE', use_link_strokes=False)
for ob in bpy.context.selected_objects:
if ob != bpy.context.scene.objects.active and ob.name.startswith("GP_Layer"):
ob_gp_strokes = ob
CoDEmanX
committed
#ob_gp_strokes = bpy.context.object
CoDEmanX
committed
#### Delete grease pencil strokes.
bpy.ops.object.select_all('INVOKE_REGION_WIN', action='DESELECT')
bpy.data.objects[self.main_object.name].select = True
bpy.context.scene.objects.active = bpy.data.objects[self.main_object.name]
CoDEmanX
committed
bpy.ops.gpencil.active_frame_delete('INVOKE_REGION_WIN')
CoDEmanX
committed
#### Clean up curves.
bpy.ops.object.select_all('INVOKE_REGION_WIN', action='DESELECT')
bpy.data.objects[ob_gp_strokes.name].select = True
bpy.context.scene.objects.active = bpy.data.objects[ob_gp_strokes.name]
CoDEmanX
committed
curve_crv = ob_gp_strokes.data
bpy.ops.object.editmode_toggle('INVOKE_REGION_WIN')
bpy.ops.curve.spline_type_set('INVOKE_REGION_WIN', type="BEZIER")
bpy.ops.curve.handle_type_set('INVOKE_REGION_WIN', type="AUTOMATIC")
bpy.data.curves[curve_crv.name].show_handles = False
bpy.data.curves[curve_crv.name].show_normal_face = False
CoDEmanX
committed
elif self.strokes_type == "EXTERNAL_NO_CURVE":
self.report({'WARNING'}, "The secondary object is not a Curve.")
return{"CANCELLED"}
elif self.strokes_type == "MORE_THAN_ONE_EXTERNAL":
self.report({'WARNING'}, "There shouldn't be more than one secondary object selected.")
return{"CANCELLED"}
elif self.strokes_type == "NO_STROKES" or self.strokes_type == "SELECTION_ALONE":
self.report({'WARNING'}, "There aren't any strokes.")
return{"CANCELLED"}
else:
return{"CANCELLED"}
CoDEmanX
committed
def invoke (self, context, event):
self.main_object = bpy.context.object
CoDEmanX
committed
CoDEmanX
committed
return {"FINISHED"}
class CURVE_OT_SURFSK_reorder_splines(bpy.types.Operator):
bl_idname = "curve.surfsk_reorder_splines"
bl_label = "Bsurfaces reorder splines"
bl_description = "Defines the order of the splines by using grease pencil strokes."
bl_options = {'REGISTER', 'UNDO'}
CoDEmanX
committed
def execute(self, context):
objects_to_delete = []
#### Convert grease pencil strokes to curve.
bpy.ops.object.editmode_toggle('INVOKE_REGION_WIN')
bpy.ops.gpencil.convert('INVOKE_REGION_WIN', type='CURVE', use_link_strokes=False)
for ob in bpy.context.selected_objects:
if ob != bpy.context.scene.objects.active and ob.name.startswith("GP_Layer"):
GP_strokes_curve = ob
CoDEmanX
committed
#GP_strokes_curve = bpy.context.object
objects_to_delete.append(GP_strokes_curve)
CoDEmanX
committed
bpy.ops.object.select_all('INVOKE_REGION_WIN', action='DESELECT')
bpy.data.objects[GP_strokes_curve.name].select = True
bpy.context.scene.objects.active = bpy.data.objects[GP_strokes_curve.name]
CoDEmanX
committed
bpy.ops.object.editmode_toggle('INVOKE_REGION_WIN')
bpy.ops.curve.select_all('INVOKE_REGION_WIN', action='SELECT')
bpy.ops.curve.subdivide('INVOKE_REGION_WIN', number_cuts = 100)
bpy.ops.object.editmode_toggle('INVOKE_REGION_WIN')
CoDEmanX
committed
bpy.ops.object.duplicate('INVOKE_REGION_WIN')
GP_strokes_mesh = bpy.context.object
objects_to_delete.append(GP_strokes_mesh)
CoDEmanX
committed
GP_strokes_mesh.data.resolution_u = 1
bpy.ops.object.convert(target='MESH', keep_original=False)
CoDEmanX
committed
bpy.ops.object.select_all('INVOKE_REGION_WIN', action='DESELECT')
bpy.data.objects[self.main_curve.name].select = True
bpy.context.scene.objects.active = bpy.data.objects[self.main_curve.name]
CoDEmanX
committed
bpy.ops.object.duplicate('INVOKE_REGION_WIN')
curves_duplicate_1 = bpy.context.object
objects_to_delete.append(curves_duplicate_1)
CoDEmanX
committed
CoDEmanX
committed
for x in range(round(minimum_points_num / 100)): # Some iterations since the subdivision operator has a limit of 100 subdivisions per iteration.
#### Check if the number of points of each curve has at least the number of points of minimum_points_num. If not, subdivide to reach at least that number of ponts.
for i in range(len(curves_duplicate_1.data.splines)):
sp = curves_duplicate_1.data.splines[i]
CoDEmanX
committed
if len(sp.bezier_points) < minimum_points_num:
for bp in sp.bezier_points:
bp.select_control_point = True
CoDEmanX
committed
if (len(sp.bezier_points) - 1) != 0:
subdivide_cuts = int((minimum_points_num - len(sp.bezier_points)) / (len(sp.bezier_points) - 1)) + 1 # Formula to get the number of cuts that will make a curve of N number of points have near to "minimum_points_num" points, when subdividing with this number of cuts.
else:
subdivide_cuts = 0
CoDEmanX
committed
bpy.ops.object.editmode_toggle('INVOKE_REGION_WIN')
bpy.ops.curve.subdivide('INVOKE_REGION_WIN', number_cuts = subdivide_cuts)
bpy.ops.curve.select_all('INVOKE_REGION_WIN', action='DESELECT')
bpy.ops.object.editmode_toggle('INVOKE_REGION_WIN')
CoDEmanX
committed
bpy.ops.object.duplicate('INVOKE_REGION_WIN')
curves_duplicate_2 = bpy.context.object
objects_to_delete.append(curves_duplicate_2)
CoDEmanX
committed
#### Duplicate the duplicate and add Shrinkwrap to it, with the grease pencil strokes curve as target.
bpy.ops.object.select_all('INVOKE_REGION_WIN', action='DESELECT')
bpy.data.objects[curves_duplicate_2.name].select = True
bpy.context.scene.objects.active = bpy.data.objects[curves_duplicate_2.name]
CoDEmanX
committed
bpy.ops.object.modifier_add('INVOKE_REGION_WIN', type='SHRINKWRAP')
curves_duplicate_2.modifiers["Shrinkwrap"].wrap_method = "NEAREST_VERTEX"
curves_duplicate_2.modifiers["Shrinkwrap"].target = GP_strokes_mesh
bpy.ops.object.modifier_apply('INVOKE_REGION_WIN', apply_as='DATA', modifier='Shrinkwrap')
CoDEmanX
committed
#### Get the distance of each vert from its original position to its position with Shrinkwrap.
nearest_points_coords = {}
for st_idx in range(len(curves_duplicate_1.data.splines)):
for bp_idx in range(len(curves_duplicate_1.data.splines[st_idx].bezier_points)):
bp_1_co = curves_duplicate_1.matrix_world * curves_duplicate_1.data.splines[st_idx].bezier_points[bp_idx].co
bp_2_co = curves_duplicate_2.matrix_world * curves_duplicate_2.data.splines[st_idx].bezier_points[bp_idx].co
CoDEmanX
committed
if bp_idx == 0:
shortest_dist = (bp_1_co - bp_2_co).length
nearest_points_coords[st_idx] = ("%.4f" % bp_2_co[0], "%.4f" % bp_2_co[1], "%.4f" % bp_2_co[2])
CoDEmanX
committed
dist = (bp_1_co - bp_2_co).length
CoDEmanX
committed
if dist < shortest_dist:
nearest_points_coords[st_idx] = ("%.4f" % bp_2_co[0], "%.4f" % bp_2_co[1], "%.4f" % bp_2_co[2])
shortest_dist = dist
CoDEmanX
committed
#### Get all coords of GP strokes points, for comparison.
GP_strokes_coords = []
for st_idx in range(len(GP_strokes_curve.data.splines)):
GP_strokes_coords.append([("%.4f" % x if "%.4f" % x != "-0.00" else "0.00", "%.4f" % y if "%.4f" % y != "-0.00" else "0.00", "%.4f" % z if "%.4f" % z != "-0.00" else "0.00") for x, y, z in [bp.co for bp in GP_strokes_curve.data.splines[st_idx].bezier_points]])
CoDEmanX
committed
#### Check the point of the GP strokes with the same coords as the nearest points of the curves (with shrinkwrap).
GP_connection_points = {} # Dictionary with GP stroke index as index, and a list as value. The list has as index the point index of the GP stroke nearest to the spline, and as value the spline index.
for gp_st_idx in range(len(GP_strokes_coords)):
GPvert_spline_relationship = {}
CoDEmanX
committed
for splines_st_idx in range(len(nearest_points_coords)):
if nearest_points_coords[splines_st_idx] in GP_strokes_coords[gp_st_idx]:
GPvert_spline_relationship[GP_strokes_coords[gp_st_idx].index(nearest_points_coords[splines_st_idx])] = splines_st_idx
CoDEmanX
committed
GP_connection_points[gp_st_idx] = GPvert_spline_relationship
CoDEmanX
committed
#### Get the splines new order.
splines_new_order = []
for i in GP_connection_points:
dict_keys = sorted(GP_connection_points[i].keys()) # Sort dictionaries by key
CoDEmanX
committed
for k in dict_keys:
splines_new_order.append(GP_connection_points[i][k])
CoDEmanX
committed
CoDEmanX
committed
curve_original_name = self.main_curve.name
CoDEmanX
committed
bpy.ops.object.select_all('INVOKE_REGION_WIN', action='DESELECT')
bpy.data.objects[self.main_curve.name].select = True
bpy.context.scene.objects.active = bpy.data.objects[self.main_curve.name]
CoDEmanX
committed
self.main_curve.name = "SURFSKIO_CRV_ORD"
CoDEmanX
committed
bpy.ops.object.editmode_toggle('INVOKE_REGION_WIN')
bpy.ops.curve.select_all('INVOKE_REGION_WIN', action='DESELECT')
bpy.ops.object.editmode_toggle('INVOKE_REGION_WIN')
CoDEmanX
committed
for sp_idx in range(len(self.main_curve.data.splines)):
self.main_curve.data.splines[0].bezier_points[0].select_control_point = True
CoDEmanX
committed
bpy.ops.object.editmode_toggle('INVOKE_REGION_WIN')
bpy.ops.curve.separate('INVOKE_REGION_WIN')
bpy.ops.object.editmode_toggle('INVOKE_REGION_WIN')
CoDEmanX
committed
#### Get the names of the separated splines objects in the original order.
splines_unordered = {}
for o in bpy.data.objects:
if o.name.find("SURFSKIO_CRV_ORD") != -1:
spline_order_string = o.name.partition(".")[2]
CoDEmanX
committed
if spline_order_string != "" and int(spline_order_string) > 0:
spline_order_index = int(spline_order_string) - 1
splines_unordered[spline_order_index] = o.name
CoDEmanX
committed
#### Join all splines objects in final order.
for order_idx in splines_new_order:
bpy.ops.object.select_all('INVOKE_REGION_WIN', action='DESELECT')
bpy.data.objects[splines_unordered[order_idx]].select = True
bpy.data.objects["SURFSKIO_CRV_ORD"].select = True
bpy.context.scene.objects.active = bpy.data.objects["SURFSKIO_CRV_ORD"]
CoDEmanX
committed
bpy.ops.object.join('INVOKE_REGION_WIN')
CoDEmanX
committed
#### Go back to the original name of the curves object.
bpy.context.object.name = curve_original_name
CoDEmanX
committed
#### Delete all unused objects.
for o in objects_to_delete:
bpy.ops.object.select_all('INVOKE_REGION_WIN', action='DESELECT')
bpy.data.objects[o.name].select = True
bpy.context.scene.objects.active = bpy.data.objects[o.name]
CoDEmanX
committed
CoDEmanX
committed
bpy.ops.object.select_all('INVOKE_REGION_WIN', action='DESELECT')
bpy.data.objects[curve_original_name].select = True
bpy.context.scene.objects.active = bpy.data.objects[curve_original_name]
CoDEmanX
committed
bpy.ops.curve.select_all('INVOKE_REGION_WIN', action='DESELECT')
CoDEmanX
committed
bpy.ops.gpencil.active_frame_delete('INVOKE_REGION_WIN')
CoDEmanX
committed
CoDEmanX
committed
def invoke (self, context, event):
self.main_curve = bpy.context.object
CoDEmanX
committed
there_are_GP_strokes = False
try:
#### Get the active grease pencil layer.
strokes_num = len(self.main_curve.grease_pencil.layers.active.active_frame.strokes)
CoDEmanX
committed
if strokes_num > 0:
there_are_GP_strokes = True
except:
pass
CoDEmanX
committed
if there_are_GP_strokes:
self.execute(context)
self.report({'INFO'}, "Splines have been reordered.")
else:
self.report({'WARNING'}, "Draw grease pencil strokes to connect splines.")
CoDEmanX
committed
CoDEmanX
committed
class CURVE_OT_SURFSK_first_points(bpy.types.Operator):
bl_idname = "curve.surfsk_first_points"
bl_label = "Bsurfaces set first points"
bl_description = "Set the selected points as the first point of each spline."
bl_options = {'REGISTER', 'UNDO'}
CoDEmanX
committed
CoDEmanX
committed
#### Check non-cyclic splines to invert.
for i in range(len(self.main_curve.data.splines)):
b_points = self.main_curve.data.splines[i].bezier_points
CoDEmanX
committed
if not i in self.cyclic_splines: # Only for non-cyclic splines
if b_points[len(b_points) - 1].select_control_point:
splines_to_invert.append(i)
CoDEmanX
committed
#### Reorder points of cyclic splines, and set all handles to "Automatic".
CoDEmanX
committed
# Check first selected point.
cyclic_splines_new_first_pt = {}
for i in self.cyclic_splines:
sp = self.main_curve.data.splines[i]
CoDEmanX
committed
for t in range(len(sp.bezier_points)):
bp = sp.bezier_points[t]
if bp.select_control_point or bp.select_right_handle or bp.select_left_handle:
cyclic_splines_new_first_pt[i] = t
break # To take only one if there are more.
CoDEmanX
committed
# Reorder.
for spline_idx in cyclic_splines_new_first_pt:
sp = self.main_curve.data.splines[spline_idx]
CoDEmanX
committed
spline_old_coords = []
for bp_old in sp.bezier_points:
coords = (bp_old.co[0], bp_old.co[1], bp_old.co[2])
CoDEmanX
committed
left_handle_type = str(bp_old.handle_left_type)
left_handle_length = float(bp_old.handle_left.length)
left_handle_xyz = (float(bp_old.handle_left.x), float(bp_old.handle_left.y), float(bp_old.handle_left.z))
CoDEmanX
committed
right_handle_type = str(bp_old.handle_right_type)
right_handle_length = float(bp_old.handle_right.length)
right_handle_xyz = (float(bp_old.handle_right.x), float(bp_old.handle_right.y), float(bp_old.handle_right.z))
CoDEmanX
committed
spline_old_coords.append([coords, left_handle_type, right_handle_type, left_handle_length, right_handle_length, left_handle_xyz, right_handle_xyz])
CoDEmanX
committed
for t in range(len(sp.bezier_points)):
bp = sp.bezier_points
CoDEmanX
committed
if t + cyclic_splines_new_first_pt[spline_idx] + 1 <= len(bp) - 1:
new_index = t + cyclic_splines_new_first_pt[spline_idx] + 1
else:
new_index = t + cyclic_splines_new_first_pt[spline_idx] + 1 - len(bp)
CoDEmanX
committed
bp[t].co = mathutils.Vector(spline_old_coords[new_index][0])
CoDEmanX
committed
bp[t].handle_left.length = spline_old_coords[new_index][3]
bp[t].handle_right.length = spline_old_coords[new_index][4]
CoDEmanX
committed
bp[t].handle_left_type = "FREE"
bp[t].handle_right_type = "FREE"
CoDEmanX
committed
bp[t].handle_left.x = spline_old_coords[new_index][5][0]
bp[t].handle_left.y = spline_old_coords[new_index][5][1]
bp[t].handle_left.z = spline_old_coords[new_index][5][2]
CoDEmanX
committed
bp[t].handle_right.x = spline_old_coords[new_index][6][0]
bp[t].handle_right.y = spline_old_coords[new_index][6][1]
bp[t].handle_right.z = spline_old_coords[new_index][6][2]
CoDEmanX
committed
bp[t].handle_left_type = spline_old_coords[new_index][1]
bp[t].handle_right_type = spline_old_coords[new_index][2]
CoDEmanX
committed
#### Invert the non-cyclic splines designated above.
for i in range(len(splines_to_invert)):
bpy.ops.curve.select_all('INVOKE_REGION_WIN', action='DESELECT')
CoDEmanX
committed
bpy.ops.object.editmode_toggle('INVOKE_REGION_WIN')
self.main_curve.data.splines[splines_to_invert[i]].bezier_points[0].select_control_point = True
bpy.ops.object.editmode_toggle('INVOKE_REGION_WIN')
CoDEmanX
committed
bpy.ops.curve.switch_direction()
CoDEmanX
committed
bpy.ops.curve.select_all('INVOKE_REGION_WIN', action='DESELECT')
CoDEmanX
committed
#### Keep selected the first vert of each spline.
bpy.ops.object.editmode_toggle('INVOKE_REGION_WIN')
for i in range(len(self.main_curve.data.splines)):
if not self.main_curve.data.splines[i].use_cyclic_u:
bp = self.main_curve.data.splines[i].bezier_points[0]
else:
bp = self.main_curve.data.splines[i].bezier_points[len(self.main_curve.data.splines[i].bezier_points) - 1]
CoDEmanX
committed
bp.select_control_point = True
bp.select_right_handle = True
bp.select_left_handle = True
CoDEmanX
committed
CoDEmanX
committed
def invoke (self, context, event):
self.main_curve = bpy.context.object
CoDEmanX
committed
# Check if all curves are Bezier, and detect which ones are cyclic.
self.cyclic_splines = []
for i in range(len(self.main_curve.data.splines)):
if self.main_curve.data.splines[i].type != "BEZIER":
self.report({'WARNING'}, 'All splines must be Bezier type.')
CoDEmanX
committed
return {'CANCELLED'}
else:
if self.main_curve.data.splines[i].use_cyclic_u:
self.cyclic_splines.append(i)
CoDEmanX
committed
self.report({'INFO'}, "First points have been set.")
CoDEmanX
committed
Campbell Barton
committed
return {'FINISHED'}
CoDEmanX
committed
bpy.utils.register_class(VIEW3D_PT_tools_SURFSK_mesh)
bpy.utils.register_class(VIEW3D_PT_tools_SURFSK_curve)
bpy.utils.register_class(GPENCIL_OT_SURFSK_add_surface)
bpy.utils.register_class(GPENCIL_OT_SURFSK_edit_strokes)
bpy.utils.register_class(CURVE_OT_SURFSK_reorder_splines)
bpy.utils.register_class(CURVE_OT_SURFSK_first_points)
CoDEmanX
committed
bpy.types.Scene.SURFSK_cyclic_cross = bpy.props.BoolProperty(
name="Cyclic Cross",
description="Make cyclic the face-loops crossing the strokes.",
default=False)
CoDEmanX
committed
bpy.types.Scene.SURFSK_cyclic_follow = bpy.props.BoolProperty(
name="Cyclic Follow",
description="Make cyclic the face-loops following the strokes.",
default=False)
CoDEmanX
committed
bpy.types.Scene.SURFSK_keep_strokes = bpy.props.BoolProperty(
name="Keep strokes",
description="Keeps the sketched strokes or curves after adding the surface.",
default=False)
CoDEmanX
committed
bpy.types.Scene.SURFSK_automatic_join = bpy.props.BoolProperty(
name="Automatic join",
description="Join automatically vertices of either surfaces generated by crosshatching, or from the borders of closed shapes.",
default=True)
CoDEmanX
committed
bpy.types.Scene.SURFSK_loops_on_strokes = bpy.props.BoolProperty(
name="Loops on strokes",
description="Make the loops match the paths of the strokes.",
default=True)
bpy.types.Scene.SURFSK_precision = bpy.props.IntProperty(
name="Precision",
description="Precision level of the surface calculation.",
default=2,
min=1,
max=100)
CoDEmanX
committed
bpy.utils.unregister_class(VIEW3D_PT_tools_SURFSK_mesh)
bpy.utils.unregister_class(VIEW3D_PT_tools_SURFSK_curve)
bpy.utils.unregister_class(GPENCIL_OT_SURFSK_add_surface)
bpy.utils.unregister_class(GPENCIL_OT_SURFSK_edit_strokes)
bpy.utils.unregister_class(CURVE_OT_SURFSK_reorder_splines)
bpy.utils.unregister_class(CURVE_OT_SURFSK_first_points)
CoDEmanX
committed
del bpy.types.Scene.SURFSK_precision
del bpy.types.Scene.SURFSK_keep_strokes
del bpy.types.Scene.SURFSK_automatic_join
del bpy.types.Scene.SURFSK_cyclic_cross
del bpy.types.Scene.SURFSK_cyclic_follow
del bpy.types.Scene.SURFSK_loops_on_strokes
CoDEmanX
committed
CoDEmanX
committed