Skip to content

Instantly share code, notes, and snippets.

@Taremin
Last active January 9, 2022 21:58
Show Gist options
  • Save Taremin/a6dc1c4f605636193cff6533ba7ab60e to your computer and use it in GitHub Desktop.
Save Taremin/a6dc1c4f605636193cff6533ba7ab60e to your computer and use it in GitHub Desktop.
アクティブスプラインの制御点を概ね等間隔にする Blender Addon (とりあえず実装)
import bpy
bl_info = {
'name': 'Relax Curve Control Point',
'category': '3D View',
'author': 'Taremin',
'location': 'View 3D > Curve > EditMode > Context Menu',
'description': 'カーブの制御点を整列します',
'version': (0, 0, 2),
'blender': (2, 80, 0),
'wiki_url': '',
'tracker_url': '',
'warning': '',
}
modifier_name = '__For_CurveMapping_Workaround_By_RCCP__'
class RCCP_OT_Relax(bpy.types.Operator):
bl_idname = "rccp.relax"
bl_label = "Relax Curve Control Point"
bl_options = {'REGISTER', 'UNDO'}
cuts: bpy.props.IntProperty(name="細分化数", default=100)
div: bpy.props.IntProperty(name="セグメント数", default=20, min=2)
@classmethod
def poll(cls, context):
obj = context.active_object
curve = obj.data.splines.active
if curve is None:
return True
return len(curve.bezier_points) >= 2
def draw(self, context):
layout = self.layout
row = layout.row()
row.prop(self, "cuts")
row.prop(self, "div")
obj = context.active_object
layout.template_curve_mapping(obj.modifiers[modifier_name], 'falloff_curve')
def relax_control_point(self, bp, step):
current_length = 0
idxs = []
i = 0
for idx in range(len(bp) - 1):
p1 = bp[idx]
p2 = bp[idx+1]
length = (p2.co - p1.co).length
if idx == 0:
current_length += length
continue
watermark = step[i]
if (current_length <= watermark) and (watermark < current_length + length):
if abs(current_length - watermark) < abs(current_length + length - watermark):
idxs.append(idx)
else:
idxs.append(idx + 1)
if current_length > watermark and current_length + length > watermark:
i += 1
if i >= len(step):
break
current_length += length
return idxs
def relax_control_point_curve(self, context, bp, total_length):
obj = context.active_object
modifier = obj.modifiers[modifier_name]
curve_profile = modifier.falloff_curve
s = 1.0 / self.div
step = [
curve_profile.evaluate(curve_profile.curves[0], s * (r + 1)) * total_length
for r in range(self.div - 1)
]
return self.relax_control_point(bp, step)
def execute(self, context):
obj = context.active_object
curve = obj.data.splines.active
bp = curve.bezier_points
if obj.modifiers.find(modifier_name) < 0:
bpy.ops.object.modifier_add(type='WARP')
warp = obj.modifiers[-1]
warp.name = modifier_name
warp.show_in_editmode = False
warp.show_viewport = False
warp.show_render = False
warp.show_expanded = False
warp.falloff_type = 'CURVE'
warp.falloff_curve.use_clip = True
bpy.ops.curve.select_all(action='DESELECT')
if len(curve.bezier_points) == 0:
return {'FINISHED'}
point = bp[0]
point.select_control_point = True
bpy.ops.curve.select_linked()
bpy.ops.curve.subdivide(number_cuts=self.cuts)
total_length = 0
r = range(len(bp) - 1)
for idx in r:
p1 = bp[idx]
p2 = bp[idx+1]
total_length += (p2.co - p1.co).length
idxs = [0] + self.relax_control_point_curve(context, bp, total_length) + [len(curve.bezier_points) - 1]
bpy.ops.curve.select_all(action='DESELECT')
point = bp[0]
point.select_control_point = True
bpy.ops.curve.select_linked()
for idx in idxs:
bp[idx].select_control_point = False
bp[idx].select_left_handle = False
bp[idx].select_right_handle = False
bpy.ops.curve.dissolve_verts()
bpy.ops.curve.select_all(action='SELECT')
return {'FINISHED'}
def relax_control_point_button(self, context):
self.layout.operator(
RCCP_OT_Relax.bl_idname,
text="Relax Curve"
)
def register():
bpy.utils.register_class(RCCP_OT_Relax)
bpy.types.VIEW3D_MT_edit_curve_context_menu.append(
relax_control_point_button)
def unregister():
bpy.utils.unregister_class(RCCP_OT_Relax)
bpy.types.VIEW3D_MT_edit_curve_context_menu.remove(
relax_control_point_button)
if __name__ == "__main__":
register()
@Taremin
Copy link
Author

Taremin commented Jan 9, 2022

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment