Last active
January 9, 2022 21:58
-
-
Save Taremin/a6dc1c4f605636193cff6533ba7ab60e to your computer and use it in GitHub Desktop.
アクティブスプラインの制御点を概ね等間隔にする Blender Addon (とりあえず実装)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
__For_CurveMapping_Workaround_By_RCCP__が追加されるようになります(Blenderの仕様上必要: https://devtalk.blender.org/t/how-do-you-add-custom-profile-prop-to-operator-like-in-bevel-operator/11521/6)