Curves: Curve Pen

This tool can be used to rapidly edit curves. The current set of
functionalities for Bezier splines are as follows:

The functionalities are divided into three versions of the operator:
* Left-Click
* Ctrl + Left-Click
* Double Click

All current functionalities and their defaults are as follows:
* Extrude Point: Add a point connected to an existing point.
Enabled for Left-Click.
* Extrude Handle Type: Type of the handles of the extruded points.
Can be either Vector or Auto. Defaults to Vector.
* Delete Point: Delete existing point.
Enabled for Ctrl + Left-Click.
* Insert Point: Insert a point into a curve segment.
Enabled for Ctrl + Left-Click.
* Move Segment: Move curve segment.
Enabled for Left-Click.
* Select Point: Select a single point or handle at a time.
Enabled for Left-Click.
* Move point: Move existing points or handles.
Enabled for Left-Click.
* Close Spline: Close spline by clicking the endpoints consecutively.
Defaults to True.
* Close Spline Method: The condition for Close Spline to activate.
Can be one of None, On Press or On Click.
Defaults to On Click for Left-Click and None for the others.
  * None: Functionality is turned off.
  * On Press: Activate on mouse down.
  This makes it possible to move the handles by dragging immediately
  after closing the spline.
  * On Click: Activate on mouse release.
  This makes it possible to avoid triggering the Close Spline
  functionality by dragging afterward.
* Toggle Vector: Toggle handle between Vector and Auto handle types.
Enabled for Double Click on a handle.
* Cycle Handle Type: Cycle between all four handle types.
Enabled for Double Click on the middle point of a Bezier point.

The keybindings for the following functionalities can be adjusted from
the modal keymap
* Free-Align Toggle: Toggle between Free and Align handle types.
Defaults to Left Shift. Activated on hold.
* Move Adjacent Handle: Move the closer handle of the adjacent vertex.
Defaults to Left Ctrl. Activated on hold.
* Move Entire: Move the entire point by moving by grabbing on the handle
Defaults to Spacebar. Activated on hold.
* Link Handles: Mirror the movement of one handle onto the other.
Defaults to Right Ctrl. Activated on press.
* Lock Handle Angle: Move the handle along its current angle.
Defaults to Left Alt. Activated on hold.

All the above functionalities, except for Move Segment and
those that work with handles, work similarly in the case of Poly
and NURBS splines.

Reviewed By: HooglyBoogly, weasel, campbellbarton
Differential Revision: http://developer.blender.org/D12155
This commit is contained in:
Dilith Jayakody 2022-04-03 22:37:22 +05:30
parent 1cdf8b19e5
commit 336082acba
17 changed files with 2078 additions and 74 deletions

View File

@ -205,6 +205,7 @@ ForEachMacros:
- FOREACH_SCENE_COLLECTION_BEGIN
- FOREACH_SCENE_OBJECT_BEGIN
- FOREACH_SELECTED_BASE_BEGIN
- FOREACH_SELECTED_BEZT_BEGIN
- FOREACH_SELECTED_EDITABLE_OBJECT_BEGIN
- FOREACH_SELECTED_OBJECT_BEGIN
- FOREACH_TRANS_DATA_CONTAINER

Binary file not shown.

@ -1 +1 @@
Subproject commit 2a5095eed3028e91624d27ca93e4c65f572b809d
Subproject commit 716dc02ec30c0810513f7b4adc4ae865ae50c4e6

@ -1 +1 @@
Subproject commit 67f1fbca1482d9d9362a4001332e785c3fd5d230
Subproject commit 787ea78f7fa6f0373d80ba1247768402df93f8ad

View File

@ -100,6 +100,7 @@ _km_hierarchy = [
('Paint Stroke Modal', 'EMPTY', 'WINDOW', []),
('Sculpt Expand Modal', 'EMPTY', 'WINDOW', []),
('Paint Curve', 'EMPTY', 'WINDOW', []),
('Curve Pen Modal Map', 'EMPTY', 'WINDOW', []),
('Object Non-modal', 'EMPTY', 'WINDOW', []), # mode change

View File

@ -6158,6 +6158,25 @@ def km_sculpt_expand_modal(_params):
return keymap
def km_curve_pen_modal_map(_params):
items = []
keymap = (
"Curve Pen Modal Map",
{"space_type": 'EMPTY', "region_type": 'WINDOW', "modal": True},
{"items": items},
)
items.extend([
("FREE_ALIGN_TOGGLE", {"type": 'LEFT_SHIFT', "value": 'ANY', "any": True}, None),
("MOVE_ADJACENT", {"type": 'LEFT_CTRL', "value": 'ANY', "any": True}, None),
("MOVE_ENTIRE", {"type": 'SPACE', "value": 'ANY', "any": True}, None),
("LOCK_ANGLE", {"type": 'LEFT_ALT', "value": 'ANY', "any": True}, None),
("LINK_HANDLES", {"type": 'RIGHT_CTRL', "value": 'PRESS', "any": True}, None),
])
return keymap
# Fallback for gizmos that don't have custom a custom key-map.
def km_generic_gizmo(_params):
keymap = (
@ -7078,6 +7097,27 @@ def km_3d_view_tool_edit_curve_draw(params):
)
def km_3d_view_tool_edit_curve_pen(params):
return (
"3D View Tool: Edit Curve, Curve Pen",
{"space_type": 'VIEW_3D', "region_type": 'WINDOW'},
{"items": [
("curve.pen", {"type": params.tool_mouse, "value": 'PRESS'},
{"properties": [
("extrude_point", True),
("move_segment", True),
("select_point", True),
("move_point", True),
("close_spline_method", "ON_CLICK"),
]}),
("curve.pen", {"type": params.tool_mouse, "value": 'PRESS', "ctrl": True},
{"properties": [("insert_point", True), ("delete_point", True)]}),
("curve.pen", {"type": params.tool_mouse, "value": 'DOUBLE_CLICK'},
{"properties": [("toggle_vector", True), ("cycle_handle_type", True),]}),
]},
)
def km_3d_view_tool_edit_curve_tilt(params):
return (
"3D View Tool: Edit Curve, Tilt",
@ -7882,6 +7922,7 @@ def generate_keymaps(params=None):
km_view3d_dolly_modal(params),
km_paint_stroke_modal(params),
km_sculpt_expand_modal(params),
km_curve_pen_modal_map(params),
# Gizmos.
km_generic_gizmo(params),
@ -7960,6 +8001,7 @@ def generate_keymaps(params=None):
km_3d_view_tool_edit_mesh_rip_region(params),
km_3d_view_tool_edit_mesh_rip_edge(params),
km_3d_view_tool_edit_curve_draw(params),
km_3d_view_tool_edit_curve_pen(params),
km_3d_view_tool_edit_curve_radius(params),
km_3d_view_tool_edit_curve_tilt(params),
km_3d_view_tool_edit_curve_randomize(params),

View File

@ -1201,6 +1201,22 @@ class _defs_edit_curve:
keymap=(),
)
@ToolDef.from_fn
def pen():
def draw_settings(_context, layout, tool):
props = tool.operator_properties("curve.pen")
layout.prop(props, "close_spline")
layout.prop(props, "extrude_handle")
return dict(
idname="builtin.pen",
label="Curve Pen",
cursor='CROSSHAIR',
icon="ops.curve.pen",
widget=None,
keymap=(),
draw_settings=draw_settings,
)
@ToolDef.from_fn
def tilt():
return dict(
@ -2881,6 +2897,7 @@ class VIEW3D_PT_tools_active(ToolSelectPanelHelper, Panel):
*_tools_default,
None,
_defs_edit_curve.draw,
_defs_edit_curve.pen,
(
_defs_edit_curve.extrude,
_defs_edit_curve.extrude_cursor,

View File

@ -25,6 +25,7 @@ set(SRC
editcurve_paint.c
editcurve_query.c
editcurve_select.c
editcurve_pen.c
editcurve_undo.c
editfont.c
editfont_undo.c

View File

@ -131,6 +131,16 @@ void CURVE_OT_match_texture_space(struct wmOperatorType *ot);
struct GHash *ED_curve_keyindex_hash_duplicate(struct GHash *keyindex);
void ED_curve_keyindex_update_nurb(struct EditNurb *editnurb, struct Nurb *nu, struct Nurb *newnu);
/* exported for editcurve_pen.c */
int ed_editcurve_addvert(Curve *cu, EditNurb *editnurb, View3D *v3d, const float location_init[3]);
bool curve_toggle_cyclic(View3D *v3d, ListBase *editnurb, int direction);
void ed_dissolve_bez_segment(BezTriple *bezt_prev,
BezTriple *bezt_next,
const Nurb *nu,
const Curve *cu,
const int span_len,
const int span_step[2]);
/* helper functions */
void ed_editnurb_translate_flag(struct ListBase *editnurb,
uint8_t flag,
@ -189,8 +199,23 @@ bool ED_curve_pick_vert(struct ViewContext *vc,
struct BPoint **r_bp,
short *r_handle,
struct Base **r_base);
/**
* \param sel_dist_mul: A multiplier on the default select distance.
*/
bool ED_curve_pick_vert_ex(struct ViewContext *vc,
short sel,
float dist_px,
struct Nurb **r_nurb,
struct BezTriple **r_bezt,
struct BPoint **r_bp,
short *r_handle,
struct Base **r_base);
void ED_curve_nurb_vert_selected_find(
Curve *cu, View3D *v3d, Nurb **r_nu, BezTriple **r_bezt, BPoint **r_bp);
/* editcurve_paint.c */
void CURVE_OT_draw(struct wmOperatorType *ot);
/* editcurve_pen.c */
void CURVE_OT_pen(struct wmOperatorType *ot);
struct wmKeyMap *curve_pen_modal_keymap(struct wmKeyConfig *keyconf);

View File

@ -111,6 +111,7 @@ void ED_operatortypes_curve(void)
WM_operatortype_append(CURVE_OT_spin);
WM_operatortype_append(CURVE_OT_vertex_add);
WM_operatortype_append(CURVE_OT_draw);
WM_operatortype_append(CURVE_OT_pen);
WM_operatortype_append(CURVE_OT_extrude);
WM_operatortype_append(CURVE_OT_cyclic_toggle);
@ -150,4 +151,6 @@ void ED_keymap_curve(wmKeyConfig *keyconf)
/* only set in editmode curve, by space_view3d listener */
keymap = WM_keymap_ensure(keyconf, "Curve", 0, 0);
keymap->poll = ED_operator_editsurfcurve;
curve_pen_modal_keymap(keyconf);
}

View File

@ -4726,6 +4726,14 @@ void CURVE_OT_make_segment(wmOperatorType *ot)
bool ED_curve_editnurb_select_pick(bContext *C,
const int mval[2],
const struct SelectPick_Params *params)
{
return ED_curve_editnurb_select_pick_ex(C, mval, 1.0f, params);
}
bool ED_curve_editnurb_select_pick_ex(bContext *C,
const int mval[2],
const float sel_dist_mul,
const struct SelectPick_Params *params)
{
Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C);
ViewContext vc;
@ -4740,7 +4748,8 @@ bool ED_curve_editnurb_select_pick(bContext *C,
ED_view3d_viewcontext_init(C, &vc, depsgraph);
copy_v2_v2_int(vc.mval, mval);
bool found = ED_curve_pick_vert(&vc, 1, &nu, &bezt, &bp, &hand, &basact);
bool found = ED_curve_pick_vert_ex(
&vc, 1, sel_dist_mul * ED_view3d_select_dist_px(), &nu, &bezt, &bp, &hand, &basact);
if (params->sel_op == SEL_OP_SET) {
if ((found && params->select_passthrough) &&
@ -5344,10 +5353,7 @@ static bool ed_editcurve_extrude(Curve *cu, EditNurb *editnurb, View3D *v3d)
/** \name Add Vertex Operator
* \{ */
static int ed_editcurve_addvert(Curve *cu,
EditNurb *editnurb,
View3D *v3d,
const float location_init[3])
int ed_editcurve_addvert(Curve *cu, EditNurb *editnurb, View3D *v3d, const float location_init[3])
{
float center[3];
float temp[3];
@ -5719,7 +5725,7 @@ void CURVE_OT_extrude(wmOperatorType *ot)
/** \name Make Cyclic Operator
* \{ */
static bool curve_toggle_cyclic(View3D *v3d, ListBase *editnurb, int direction)
bool curve_toggle_cyclic(View3D *v3d, ListBase *editnurb, int direction)
{
BezTriple *bezt;
BPoint *bp;
@ -6502,6 +6508,70 @@ static bool test_bezt_is_sel_any(const void *bezt_v, void *user_data)
return BEZT_ISSEL_ANY_HIDDENHANDLES(v3d, bezt);
}
void ed_dissolve_bez_segment(BezTriple *bezt_prev,
BezTriple *bezt_next,
const Nurb *nu,
const Curve *cu,
const int span_len,
const int span_step[2])
{
int i_span_edge_len = span_len + 1;
const int dims = 3;
const int points_len = ((cu->resolu - 1) * i_span_edge_len) + 1;
float *points = MEM_mallocN(points_len * dims * sizeof(float), __func__);
float *points_stride = points;
const int points_stride_len = (cu->resolu - 1);
for (int segment = 0; segment < i_span_edge_len; segment++) {
BezTriple *bezt_a = &nu->bezt[mod_i((span_step[0] + segment) - 1, nu->pntsu)];
BezTriple *bezt_b = &nu->bezt[mod_i((span_step[0] + segment), nu->pntsu)];
for (int axis = 0; axis < dims; axis++) {
BKE_curve_forward_diff_bezier(bezt_a->vec[1][axis],
bezt_a->vec[2][axis],
bezt_b->vec[0][axis],
bezt_b->vec[1][axis],
points_stride + axis,
points_stride_len,
dims * sizeof(float));
}
points_stride += dims * points_stride_len;
}
BLI_assert(points_stride + dims == points + (points_len * dims));
float tan_l[3], tan_r[3], error_sq_dummy;
uint error_index_dummy;
sub_v3_v3v3(tan_l, bezt_prev->vec[1], bezt_prev->vec[2]);
normalize_v3(tan_l);
sub_v3_v3v3(tan_r, bezt_next->vec[0], bezt_next->vec[1]);
normalize_v3(tan_r);
curve_fit_cubic_to_points_single_fl(points,
points_len,
NULL,
dims,
FLT_EPSILON,
tan_l,
tan_r,
bezt_prev->vec[2],
bezt_next->vec[0],
&error_sq_dummy,
&error_index_dummy);
if (!ELEM(bezt_prev->h2, HD_FREE, HD_ALIGN)) {
bezt_prev->h2 = (bezt_prev->h2 == HD_VECT) ? HD_FREE : HD_ALIGN;
}
if (!ELEM(bezt_next->h1, HD_FREE, HD_ALIGN)) {
bezt_next->h1 = (bezt_next->h1 == HD_VECT) ? HD_FREE : HD_ALIGN;
}
MEM_freeN(points);
}
static int curve_dissolve_exec(bContext *C, wmOperator *UNUSED(op))
{
Main *bmain = CTX_data_main(C);
@ -6523,8 +6593,8 @@ static int curve_dissolve_exec(bContext *C, wmOperator *UNUSED(op))
LISTBASE_FOREACH (Nurb *, nu, editnurb) {
if ((nu->type == CU_BEZIER) && (nu->pntsu > 2)) {
uint span_step[2] = {nu->pntsu, nu->pntsu};
uint span_len;
int span_step[2] = {nu->pntsu, nu->pntsu};
int span_len;
while (BLI_array_iter_span(nu->bezt,
nu->pntsu,
@ -6537,61 +6607,7 @@ static int curve_dissolve_exec(bContext *C, wmOperator *UNUSED(op))
BezTriple *bezt_prev = &nu->bezt[mod_i(span_step[0] - 1, nu->pntsu)];
BezTriple *bezt_next = &nu->bezt[mod_i(span_step[1] + 1, nu->pntsu)];
int i_span_edge_len = span_len + 1;
const uint dims = 3;
const uint points_len = ((cu->resolu - 1) * i_span_edge_len) + 1;
float *points = MEM_mallocN(points_len * dims * sizeof(float), __func__);
float *points_stride = points;
const int points_stride_len = (cu->resolu - 1);
for (int segment = 0; segment < i_span_edge_len; segment++) {
BezTriple *bezt_a = &nu->bezt[mod_i((span_step[0] + segment) - 1, nu->pntsu)];
BezTriple *bezt_b = &nu->bezt[mod_i((span_step[0] + segment), nu->pntsu)];
for (int axis = 0; axis < dims; axis++) {
BKE_curve_forward_diff_bezier(bezt_a->vec[1][axis],
bezt_a->vec[2][axis],
bezt_b->vec[0][axis],
bezt_b->vec[1][axis],
points_stride + axis,
points_stride_len,
dims * sizeof(float));
}
points_stride += dims * points_stride_len;
}
BLI_assert(points_stride + dims == points + (points_len * dims));
float tan_l[3], tan_r[3], error_sq_dummy;
uint error_index_dummy;
sub_v3_v3v3(tan_l, bezt_prev->vec[1], bezt_prev->vec[2]);
normalize_v3(tan_l);
sub_v3_v3v3(tan_r, bezt_next->vec[0], bezt_next->vec[1]);
normalize_v3(tan_r);
curve_fit_cubic_to_points_single_fl(points,
points_len,
NULL,
dims,
FLT_EPSILON,
tan_l,
tan_r,
bezt_prev->vec[2],
bezt_next->vec[0],
&error_sq_dummy,
&error_index_dummy);
if (!ELEM(bezt_prev->h2, HD_FREE, HD_ALIGN)) {
bezt_prev->h2 = (bezt_prev->h2 == HD_VECT) ? HD_FREE : HD_ALIGN;
}
if (!ELEM(bezt_next->h1, HD_FREE, HD_ALIGN)) {
bezt_next->h1 = (bezt_next->h1 == HD_VECT) ? HD_FREE : HD_ALIGN;
}
MEM_freeN(points);
ed_dissolve_bez_segment(bezt_prev, bezt_next, nu, cu, span_len, span_step);
}
}
}

File diff suppressed because it is too large Load Diff

View File

@ -88,13 +88,14 @@ static void ED_curve_pick_vert__do_closest(void *userData,
UNUSED_VARS_NDEBUG(handles_visible);
}
bool ED_curve_pick_vert(ViewContext *vc,
short sel,
Nurb **r_nurb,
BezTriple **r_bezt,
BPoint **r_bp,
short *r_handle,
Base **r_base)
bool ED_curve_pick_vert_ex(ViewContext *vc,
short sel,
float dist_px,
Nurb **r_nurb,
BezTriple **r_bezt,
BPoint **r_bp,
short *r_handle,
Base **r_base)
{
/* (sel == 1): selected gets a disadvantage */
/* in nurb and bezt or bp the nearest is written */
@ -109,7 +110,7 @@ bool ED_curve_pick_vert(ViewContext *vc,
bool is_changed;
} data = {NULL};
data.dist = ED_view3d_select_dist_px();
data.dist = dist_px;
data.hpoint = 0;
data.select = sel;
data.mval_fl[0] = vc->mval[0];
@ -143,6 +144,18 @@ bool ED_curve_pick_vert(ViewContext *vc,
return (data.bezt || data.bp);
}
bool ED_curve_pick_vert(ViewContext *vc,
short sel,
Nurb **r_nurb,
BezTriple **r_bezt,
BPoint **r_bp,
short *r_handle,
Base **r_base)
{
return ED_curve_pick_vert_ex(
vc, sel, ED_view3d_select_dist_px(), r_nurb, r_bezt, r_bp, r_handle, r_base);
}
/** \} */
/* -------------------------------------------------------------------- */

View File

@ -765,6 +765,7 @@ set_property(GLOBAL PROPERTY ICON_GEOM_NAMES
ops.armature.extrude_cursor
ops.armature.extrude_move
ops.curve.draw
ops.curve.pen
ops.curve.extrude_cursor
ops.curve.extrude_move
ops.curve.radius

View File

@ -53,6 +53,13 @@ void ED_curve_editnurb_free(struct Object *obedit);
bool ED_curve_editnurb_select_pick(struct bContext *C,
const int mval[2],
const struct SelectPick_Params *params);
/**
* \param sel_dist_mul: A multiplier on the default select distance.
*/
bool ED_curve_editnurb_select_pick_ex(struct bContext *C,
const int mval[2],
const float sel_dist_mul,
const struct SelectPick_Params *params);
struct Nurb *ED_curve_add_nurbs_primitive(
struct bContext *C, struct Object *obedit, float mat[4][4], int type, int newob);

@ -1 +1 @@
Subproject commit 7fd2ed908b4f50140670caf6786e5ed245b79137
Subproject commit 1e658ca996f11e5ff3398d89bd81f5b719304a57