Newer
Older
# Check the point of the GP strokes with the same coords as
# the nearest points of the curves (with shrinkwrap)
# 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
GP_connection_points = {}
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
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
curve_original_name = self.main_curve.name
CoDEmanX
committed
bpy.ops.object.select_all('INVOKE_REGION_WIN', action='DESELECT')
bpy.context.view_layer.objects.active = self.main_curve
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('EXEC_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_set(True)
bpy.data.objects["SURFSKIO_CRV_ORD"].select_set(True)
bpy.context.view_layer.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
bpy.ops.object.delete({"selected_objects": objects_to_delete})
CoDEmanX
committed
bpy.ops.object.select_all('INVOKE_REGION_WIN', action='DESELECT')
bpy.data.objects[curve_original_name].select_set(True)
bpy.context.view_layer.objects.active = bpy.data.objects[curve_original_name]
CoDEmanX
committed
bpy.ops.curve.select_all('INVOKE_REGION_WIN', action='DESELECT')
CoDEmanX
committed
bpy.context.scene.bsurfaces.SURFSK_gpencil.data.layers.active.clear()
except:
pass
Spivak Vladimir (cwolf3d)
committed
CoDEmanX
committed
CoDEmanX
committed
self.main_curve = bpy.context.object
there_are_GP_strokes = False
# 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")
self.report({'WARNING'}, "Draw grease pencil strokes to connect splines")
CoDEmanX
committed
CoDEmanX
committed
# ----------------------------
# Set first points operator
class CURVE_OT_SURFSK_first_points(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 i not 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
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
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)
)
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)
)
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 = 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
bpy.ops.object.editmode_toggle('INVOKE_REGION_WIN')
CoDEmanX
committed
CoDEmanX
committed
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
# Add-ons Preferences Update Panel
# Define Panel classes for updating
panels = (
VIEW3D_PT_tools_SURFSK_mesh,
VIEW3D_PT_tools_SURFSK_curve
Spivak Vladimir (cwolf3d)
committed
def conver_gpencil_to_curve(self, context, pencil, type):
newCurve = bpy.data.curves.new(type + '_curve', type='CURVE')
CurveObject = object_utils.object_data_add(context, newCurve)
Spivak Vladimir (cwolf3d)
committed
error = False
Spivak Vladimir (cwolf3d)
committed
if type == 'GPensil':
Spivak Vladimir (cwolf3d)
committed
try:
strokes = pencil.data.layers.active.active_frame.strokes
except:
error = True
CurveObject.location = pencil.location
CurveObject.rotation_euler = pencil.rotation_euler
CurveObject.scale = pencil.scale
elif type == 'Annotation':
Spivak Vladimir (cwolf3d)
committed
try:
strokes = bpy.context.annotation_data.layers.active.active_frame.strokes
Spivak Vladimir (cwolf3d)
committed
except:
error = True
CurveObject.location = (0.0, 0.0, 0.0)
CurveObject.rotation_euler = (0.0, 0.0, 0.0)
CurveObject.scale = (1.0, 1.0, 1.0)
Spivak Vladimir (cwolf3d)
committed
Spivak Vladimir (cwolf3d)
committed
if not error:
for i, _stroke in enumerate(strokes):
Spivak Vladimir (cwolf3d)
committed
stroke_points = strokes[i].points
data_list = [ (point.co.x, point.co.y, point.co.z)
Spivak Vladimir (cwolf3d)
committed
for point in stroke_points ]
points_to_add = len(data_list)-1
Spivak Vladimir (cwolf3d)
committed
Spivak Vladimir (cwolf3d)
committed
flat_list = []
for point in data_list:
flat_list.extend(point)
Spivak Vladimir (cwolf3d)
committed
spline = newCurve.splines.new(type='BEZIER')
spline.bezier_points.add(points_to_add)
spline.bezier_points.foreach_set("co", flat_list)
Spivak Vladimir (cwolf3d)
committed
Spivak Vladimir (cwolf3d)
committed
for point in spline.bezier_points:
point.handle_left_type="AUTO"
point.handle_right_type="AUTO"
Spivak Vladimir (cwolf3d)
committed
return CurveObject
else:
return None
Spivak Vladimir (cwolf3d)
committed
4302
4303
4304
4305
4306
4307
4308
4309
4310
4311
4312
4313
4314
4315
4316
4317
4318
4319
4320
4321
4322
def update_panel(self, context):
message = "Bsurfaces GPL Edition: Updating Panel locations has failed"
try:
for panel in panels:
if "bl_rna" in panel.__dict__:
bpy.utils.unregister_class(panel)
for panel in panels:
category = context.preferences.addons[__name__].preferences.category
if category != 'Tool':
panel.bl_category = context.preferences.addons[__name__].preferences.category
else:
context.preferences.addons[__name__].preferences.category = 'Edit'
panel.bl_category = 'Edit'
raise ValueError("You can not install add-ons in the Tool panel")
bpy.utils.register_class(panel)
except Exception as e:
print("\n[{}]\n{}\n\nError:\n{}".format(__name__, message, e))
pass
Spivak Vladimir (cwolf3d)
committed
Spivak Vladimir (cwolf3d)
committed
def makeMaterial(name, diffuse):
if name in bpy.data.materials:
material = bpy.data.materials[name]
material.diffuse_color = diffuse
else:
material = bpy.data.materials.new(name)
material.diffuse_color = diffuse
return material
def update_mesh(self, context):
try:
bpy.ops.object.mode_set('INVOKE_REGION_WIN', mode='OBJECT')
bpy.ops.object.select_all(action='DESELECT')
bpy.context.view_layer.update()
global global_mesh_object
global_mesh_object = bpy.context.scene.bsurfaces.SURFSK_mesh.name
bpy.data.objects[global_mesh_object].select_set(True)
bpy.context.view_layer.objects.active = bpy.data.objects[global_mesh_object]
print("Select mesh object")
def update_gpencil(self, context):
Spivak Vladimir (cwolf3d)
committed
try:
bpy.ops.object.mode_set('INVOKE_REGION_WIN', mode='OBJECT')
bpy.ops.object.select_all(action='DESELECT')
bpy.context.view_layer.update()
global global_gpencil_object
global_gpencil_object = bpy.context.scene.bsurfaces.SURFSK_gpencil.name
bpy.data.objects[global_gpencil_object].select_set(True)
bpy.context.view_layer.objects.active = bpy.data.objects[global_gpencil_object]
print("Select gpencil object")
Spivak Vladimir (cwolf3d)
committed
def update_curve(self, context):
Spivak Vladimir (cwolf3d)
committed
try:
bpy.ops.object.mode_set('INVOKE_REGION_WIN', mode='OBJECT')
bpy.ops.object.select_all(action='DESELECT')
bpy.context.view_layer.update()
global global_curve_object
global_curve_object = bpy.context.scene.bsurfaces.SURFSK_curve.name
bpy.data.objects[global_curve_object].select_set(True)
bpy.context.view_layer.objects.active = bpy.data.objects[global_curve_object]
print("Select curve object")
def update_shade_smooth(self, context):
try:
global global_shade_smooth
global_shade_smooth = bpy.context.scene.bsurfaces.SURFSK_shade_smooth
Spivak Vladimir (cwolf3d)
committed
contex_mode = bpy.context.mode
Spivak Vladimir (cwolf3d)
committed
if bpy.ops.object.mode_set.poll():
bpy.ops.object.mode_set('INVOKE_REGION_WIN', mode='OBJECT')
Spivak Vladimir (cwolf3d)
committed
bpy.ops.object.select_all(action='DESELECT')
global global_mesh_object
global_mesh_object = bpy.context.scene.bsurfaces.SURFSK_mesh.name
bpy.data.objects[global_mesh_object].select_set(True)
Spivak Vladimir (cwolf3d)
committed
if global_shade_smooth:
bpy.ops.object.shade_smooth()
else:
bpy.ops.object.shade_flat()
Spivak Vladimir (cwolf3d)
committed
if contex_mode == "EDIT_MESH":
bpy.ops.object.editmode_toggle('INVOKE_REGION_WIN')
Spivak Vladimir (cwolf3d)
committed
print("Select mesh object")
Spivak Vladimir (cwolf3d)
committed
Spivak Vladimir (cwolf3d)
committed
class BsurfPreferences(AddonPreferences):
# this must match the addon name, use '__package__'
# when defining this in a submodule of a python package.
bl_idname = __name__
CoDEmanX
committed
name="Tab Category",
description="Choose a name for the category of the panel",
Spivak Vladimir (cwolf3d)
committed
default="Edit",
def draw(self, context):
layout = self.layout
row = layout.row()
col = row.column()
col.label(text="Tab Category:")
col.prop(self, "category", text="")
CoDEmanX
committed
# Properties
class BsurfacesProps(PropertyGroup):
Spivak Vladimir (cwolf3d)
committed
SURFSK_guide: EnumProperty(
name="Guide:",
items=[
('Annotation', 'Annotation', 'Annotation'),
('GPencil', 'GPencil', 'GPencil'),
('Curve', 'Curve', 'Curve')
],
default="Annotation"
)
SURFSK_edges_U: IntProperty(
name="Cross",
description="Number of face-loops crossing the strokes",
default=5,
min=1,
max=200
)
SURFSK_edges_V: IntProperty(
name="Follow",
description="Number of face-loops following the strokes",
default=1,
min=1,
max=200
)
SURFSK_cyclic_cross: BoolProperty(
name="Cyclic Cross",
description="Make cyclic the face-loops crossing the strokes",
default=False
)
SURFSK_cyclic_follow: BoolProperty(
name="Cyclic Follow",
description="Make cyclic the face-loops following the strokes",
default=False
)
SURFSK_keep_strokes: BoolProperty(
name="Keep strokes",
description="Keeps the sketched strokes or curves after adding the surface",
default=False
)
SURFSK_automatic_join: BoolProperty(
name="Automatic join",
description="Join automatically vertices of either surfaces "
"generated by crosshatching, or from the borders of closed shapes",
default=True
)
SURFSK_loops_on_strokes: BoolProperty(
name="Loops on strokes",
description="Make the loops match the paths of the strokes",
default=True
)
name="Precision",
description="Precision level of the surface calculation",
default=2,
min=1,
max=100
)
Spivak Vladimir (cwolf3d)
committed
SURFSK_mesh: PointerProperty(
name="Mesh of BSurface",
Spivak Vladimir (cwolf3d)
committed
type=bpy.types.Object,
description="Mesh of BSurface",
update=update_mesh,
)
SURFSK_gpencil: PointerProperty(
name="GreasePencil object",
type=bpy.types.Object,
description="GreasePencil object",
update=update_gpencil,
SURFSK_curve: PointerProperty(
name="Curve object",
Spivak Vladimir (cwolf3d)
committed
type=bpy.types.Object,
description="Curve object",
update=update_curve,
Spivak Vladimir (cwolf3d)
committed
)
SURFSK_shade_smooth: BoolProperty(
name="Shade smooth",
description="Render and display faces smooth, using interpolated Vertex Normals",
default=False,
update=update_shade_smooth,
)
Spivak Vladimir (cwolf3d)
committed
MESH_OT_SURFSK_init,
MESH_OT_SURFSK_add_modifiers,
MESH_OT_SURFSK_add_surface,
MESH_OT_SURFSK_edit_surface,
GPENCIL_OT_SURFSK_add_strokes,
GPENCIL_OT_SURFSK_strokes_to_curves,
Spivak Vladimir (cwolf3d)
committed
GPENCIL_OT_SURFSK_annotation_to_curves,
GPENCIL_OT_SURFSK_add_annotation,
Spivak Vladimir (cwolf3d)
committed
CURVE_OT_SURFSK_edit_curve,
CURVE_OT_SURFSK_reorder_splines,
CURVE_OT_SURFSK_first_points,
BsurfPreferences,
BsurfacesProps
CoDEmanX
committed
def register():
for cls in classes:
bpy.utils.register_class(cls)
Spivak Vladimir (cwolf3d)
committed
for panel in panels:
bpy.utils.register_class(panel)
bpy.types.Scene.bsurfaces = PointerProperty(type=BsurfacesProps)
update_panel(None, bpy.context)
CoDEmanX
committed
for panel in panels:
bpy.utils.unregister_class(panel)
Spivak Vladimir (cwolf3d)
committed
for cls in classes:
bpy.utils.unregister_class(cls)
CoDEmanX
committed
if __name__ == "__main__":