1
$\begingroup$

Blender has a hotkey to control the Tilt of a curve (Ctrl+T) but does not for the Radius. When looking for Radius under User Preferences> Input I can't find it. I was told it's because unlike the Tilt, the Radius does not have an operator.

How would I create an operator using the Operator template in blender for Radius so I can then map it to a hotkey?

$\endgroup$
3
$\begingroup$

There is a shortcut for controlling the radius of a curve, but it isn't that obvious.

You can do Alt+S to perform the Shrink/Fatten command and this will directly control the Radius value. It is actually listed in the Transform panel of the Toolbar (T).

Now if you're really set on seeing the process of making a new operator I've included the code below using the Operator Modal template in Blender's Text Editor (explanation below code):

import bpy
from bpy.props import IntProperty, FloatProperty

#Code needed to make it an add-on
bl_info = {
    "name": "New Shrink/Fatten",
    "author": "Ray Mairlot",
    "version": (1, 0),
    "blender": (2, 79, 0),
    "location": "Space Key> Simple Modal Operator",
    "description": "",
    "warning": "",
    "wiki_url": "",
    "category": "User Interface",
    }


class ModalOperator(bpy.types.Operator):
    """Move an object with the mouse, example"""
    bl_idname = "object.modal_operator"
    bl_label = "Simple Modal Operator"

    first_mouse_x = IntProperty()
    original_values = []
    selected_points = []

    def modal(self, context, event):
        if event.type == 'MOUSEMOVE':

            #Calculate an offset based on the mouse position
            delta = self.first_mouse_x - event.mouse_x

            #Loop over points and assign this new radius
            for index, point in enumerate(self.selected_points):
                point.radius = self.original_values[index] + delta * 0.01

        elif event.type == 'LEFTMOUSE':
            #The operator has successfully finished, simply report 'Finished'
            return {'FINISHED'}

        elif event.type in {'RIGHTMOUSE', 'ESC'}:

            #The operator has been cancelled, restore the original data
            for index, point in enumerate(self.selected_points):
                point.radius = self.original_values[index]

            return {'CANCELLED'}

        return {'RUNNING_MODAL'}

    def invoke(self, context, event):

        #Clear out values from a previous run of the operator
        self.original_values = []
        self.selected_points = []

        if context.object:
            self.first_mouse_x = event.mouse_x

            #Gather selected points by looping over the curve
            for spline in context.object.data.splines:
                for point in spline.bezier_points:
                    if point.select_control_point:
                        self.selected_points.append(point)
                        self.original_values.append(point.radius)

            context.window_manager.modal_handler_add(self)
            return {'RUNNING_MODAL'}
        else:
            self.report({'WARNING'}, "No active object, could not finish")
            return {'CANCELLED'}


keymaps = []

def register():
    bpy.utils.register_class(ModalOperator)

    #Keymap registration
    wm = bpy.context.window_manager
    km = wm.keyconfigs.addon.keymaps.new(name='Curve', space_type='EMPTY')
    kmi = km.keymap_items.new('object.modal_operator', 'Q', 'PRESS', alt=True)
    keymaps.append((km, kmi))


def unregister():
    bpy.utils.unregister_class(ModalOperator)

    #Clearing out the keymaps when the add-on is removed
    for km, kmi in keymaps:
        km.keymap_items.remove(kmi)
        keymaps.clear()


if __name__ == "__main__":
    register()

Some explanation of the code:

A modal operator is a bit more complex than your standard operator as it has to allow interactivity, which means you have to handle the user inputting information and cancelling/confirming the end of the operator.

The basic idea of the script (shown in the invoke function) is that you loop through the bezier points of the curve, find which are selected, and store them (as the operator should probably only operate on the selected points). It also stores the original radius values in case the operator is cancelled and the original data needs to be restored (and so have an initial value to increase/decrease from).

Then, in the modal function we calculate a 'delta' (an offset - the default of the template is that it uses the x location of the mouse) to work out how much the radius should change based on mouse movement. We then loop over all the points again and assign a new value based on that delta.

If the operator is cancelled we loop over the points and restore the original data we stored at the beginning.

The keymaps are handled in the register() and unregister() functions which are run when the add-on is enabled or disabled respectively. To try and avoid overriding any existing shortcuts I am using Alt+Q to activate the operator.

It's not the simplest thing if this is your first script and many parts of the script can be improved (like not just using the 'x' position of the mouse to calculate the delta and making sure the selected object is a curve etc.), but this should give you a starting point to see which topics you need to research further.

|improve this answer|||||
$\endgroup$
  • $\begingroup$ Awesome, thanks for that. That totally solved my problem. Although it technically doesn't answer my question which I am curious about. So I'd rather not mark it as answered if that's ok. $\endgroup$ – FredLierman Oct 6 '17 at 15:23
  • $\begingroup$ Then I suggest looking at something like this: blender.stackexchange.com/questions/3592/… $\endgroup$ – Ray Mairlot Oct 6 '17 at 15:28
  • $\begingroup$ @FredLierman Updated answer with a code example. $\endgroup$ – Ray Mairlot Oct 6 '17 at 16:41
  • $\begingroup$ awesome! Excellent answer. Thank you very much. Appreciated! $\endgroup$ – FredLierman Oct 6 '17 at 16:42

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.