Sculpt: Add Line Trim tool #120845

Merged
Hans Goudey merged 3 commits from Sean-Kim/blender:line-trim into main 2024-05-01 14:10:41 +02:00
8 changed files with 153 additions and 1 deletions
Showing only changes of commit c84f4051ab - Show all commits

View File

@ -8009,6 +8009,16 @@ def km_3d_view_tool_sculpt_lasso_trim(params):
)
def km_3d_view_tool_sculpt_line_trim(params):
return (
"3D View Tool: Sculpt, Line Trim",
{"space_type": 'VIEW_3D', "region_type": 'WINDOW'},
{"items": [
("sculpt.trim_line_gesture", params.tool_maybe_tweak_event, None),
]},
)
def km_3d_view_tool_sculpt_line_mask(params):
return (
"3D View Tool: Sculpt, Line Mask",
@ -8798,6 +8808,7 @@ def generate_keymaps(params=None):
km_3d_view_tool_sculpt_lasso_face_set(params),
km_3d_view_tool_sculpt_box_trim(params),
km_3d_view_tool_sculpt_lasso_trim(params),
km_3d_view_tool_sculpt_line_trim(params),
km_3d_view_tool_sculpt_line_mask(params),
km_3d_view_tool_sculpt_line_project(params),
km_3d_view_tool_sculpt_mesh_filter(params),

View File

@ -1543,6 +1543,23 @@ class _defs_sculpt:
draw_settings=draw_settings,
)
@ToolDef.from_fn
def trim_line():
def draw_settings(_context, layout, tool):
props = tool.operator_properties("sculpt.trim_line_gesture")
layout.prop(props, "trim_solver", expand=False)
layout.prop(props, "trim_orientation", expand=False)
layout.prop(props, "use_cursor_depth", expand=False)
layout.prop(props, "use_limit_to_segment", expand=False)
return dict(
idname="builtin.line_trim",
label="Line Trim",
icon="ops.sculpt.line_trim",
widget=None,
keymap=(),
draw_settings=draw_settings,
)
@ToolDef.from_fn
def project_line():
def draw_settings(_context, layout, tool):
@ -3173,6 +3190,7 @@ class VIEW3D_PT_tools_active(ToolSelectPanelHelper, Panel):
(
_defs_sculpt.trim_box,
_defs_sculpt.trim_lasso,
_defs_sculpt.trim_line,
),
_defs_sculpt.project_line,
None,

View File

@ -3690,6 +3690,9 @@ class VIEW3D_MT_sculpt(Menu):
props = layout.operator("sculpt.trim_lasso_gesture", text="Lasso Trim")
props.trim_mode = 'DIFFERENCE'
props = layout.operator("sculpt.trim_line_gesture", text="Line Trim")
props.trim_mode = 'DIFFERENCE'
props = layout.operator("sculpt.trim_box_gesture", text="Box Add")
props.trim_mode = 'JOIN'

View File

@ -230,6 +230,12 @@ std::unique_ptr<GestureData> init_from_line(bContext *C, wmOperator *op)
line_points[1][0] = RNA_int_get(op->ptr, "xend");
line_points[1][1] = RNA_int_get(op->ptr, "yend");
gesture_data->gesture_points.reinitialize(2);
gesture_data->gesture_points[0][0] = line_points[0][0];
gesture_data->gesture_points[0][1] = line_points[0][1];
gesture_data->gesture_points[1][0] = line_points[1][0];
gesture_data->gesture_points[1][1] = line_points[1][1];
gesture_data->line.flip = RNA_boolean_get(op->ptr, "flip");
float plane_points[4][3];

View File

@ -1784,6 +1784,7 @@ void SCULPT_OT_project_line_gesture(wmOperatorType *ot);
namespace blender::ed::sculpt_paint::trim {
void SCULPT_OT_trim_lasso_gesture(wmOperatorType *ot);
void SCULPT_OT_trim_box_gesture(wmOperatorType *ot);
void SCULPT_OT_trim_line_gesture(wmOperatorType *ot);
}
/** \} */

View File

@ -1323,6 +1323,7 @@ void ED_operatortypes_sculpt()
WM_operatortype_append(face_set::SCULPT_OT_face_set_box_gesture);
WM_operatortype_append(trim::SCULPT_OT_trim_box_gesture);
WM_operatortype_append(trim::SCULPT_OT_trim_lasso_gesture);
WM_operatortype_append(trim::SCULPT_OT_trim_line_gesture);
WM_operatortype_append(project::SCULPT_OT_project_line_gesture);
WM_operatortype_append(SCULPT_OT_sample_color);

View File

@ -10,6 +10,7 @@
#include "BLI_math_geom.h"
#include "BLI_math_matrix.h"
#include "BLI_math_vector.h"
#include "BLI_math_vector.hh"
#include "BLI_polyfill_2d.h"
#include "BKE_brush.hh"
@ -17,6 +18,7 @@
#include "BKE_layer.hh"
#include "BKE_lib_id.hh"
#include "BKE_mesh.hh"
#include "BKE_object.hh"
#include "DNA_modifier_types.h"
@ -274,13 +276,65 @@ static void calculate_depth(gesture::GestureData &gesture_data,
r_depth_back = depth_back;
}
/* Calculates a scalar factor to use to ensure a drawn line gesture
* encompasses the entire object to be acted on. */
static float calc_expand_factor(const gesture::GestureData &gesture_data)
{
Object *object = gesture_data.vc.obact;
rcti rect;
const Bounds<float3> bounds = *BKE_object_boundbox_get(object);
paint_convert_bb_to_rect(
&rect, bounds.min, bounds.max, gesture_data.vc.region, gesture_data.vc.rv3d, object);
const float2 min_corner(rect.xmin, rect.ymin);
const float2 max_corner(rect.xmax, rect.ymax);
/* Mutiply the screen space bounds by an arbitrary factor to ensure the created points are
* sufficiently far and enclose the mesh to be operated on. */
return math::distance(min_corner, max_corner) * 2.0f;
}
/* Converts a gesture's points into usable screen points. */
static Span<float2> gesture_to_screen_points(gesture::GestureData &gesture_data)
{
if (gesture_data.shape_type != gesture::ShapeType::Line) {
return gesture_data.gesture_points;
}
const float expand_factor = calc_expand_factor(gesture_data);
float2 start(gesture_data.gesture_points[0]);
float2 end(gesture_data.gesture_points[1]);
const float2 dir = math::normalize(end - start);
if (!gesture_data.line.use_side_planes) {
end = end + dir * expand_factor;
start = start - dir * expand_factor;
}
float2 perp(dir.y, -dir.x);
if (gesture_data.line.flip) {
perp *= -1;
}
const float2 parallel_start = start + perp * expand_factor;
const float2 parallel_end = end + perp * expand_factor;
const Array<float2> result = {{start, end, parallel_end, parallel_start}};
return result;
}
static void generate_geometry(gesture::GestureData &gesture_data)
{
TrimOperation *trim_operation = (TrimOperation *)gesture_data.operation;
Sean-Kim marked this conversation as resolved Outdated

This handling of the line_points as looks rather awkward to me as it currently is, but I wasn't able to think of a good way of handling this. Another alternative here could be to modify the internal GestureData blender::Array in the above line_gesture_to_screen_points but that also had a code smell. Any thoughts on a good approach here?

This handling of the `line_points` as looks rather awkward to me as it currently is, but I wasn't able to think of a good way of handling this. Another alternative here could be to modify the internal `GestureData` `blender::Array` in the above `line_gesture_to_screen_points` but that also had a code smell. Any thoughts on a good approach here?

I think this is fine. But another potentially simpler solution would be changing const Span<float2> screen_points to Array<float2>. The copying shouldn't really matter here.

I think this is fine. But another potentially simpler solution would be changing `const Span<float2> screen_points` to `Array<float2>`. The copying shouldn't really matter here.
ViewContext *vc = &gesture_data.vc;
ARegion *region = vc->region;
const Span<float2> screen_points = gesture_data.gesture_points;
const Span<float2> screen_points = gesture_to_screen_points(gesture_data);
BLI_assert(screen_points.size() > 1);
const int trim_totverts = screen_points.size() * 2;
@ -614,6 +668,11 @@ static void init_operation(gesture::GestureData &gesture_data, wmOperator &op)
if (!trim_operation->initial_hit) {
trim_operation->orientation = OrientationType::View;
}
if (gesture_data.shape_type == gesture::ShapeType::Line) {
/* Line gestures only support Difference, no extrusion. */
trim_operation->mode = OperationType::Difference;
}
}
static void operator_properties(wmOperatorType *ot)
@ -773,6 +832,37 @@ static int gesture_lasso_invoke(bContext *C, wmOperator *op, const wmEvent *even
return WM_gesture_lasso_invoke(C, op, event);
}
static int gesture_line_exec(bContext *C, wmOperator *op)
{
if (!can_exec(*C)) {
return OPERATOR_CANCELLED;
}
std::unique_ptr<gesture::GestureData> gesture_data = gesture::init_from_line(C, op);
if (!gesture_data) {
return OPERATOR_CANCELLED;
}
gesture_data->operation = reinterpret_cast<gesture::Operation *>(
MEM_cnew<TrimOperation>(__func__));
initialize_cursor_info(*C, *op, *gesture_data);
init_operation(*gesture_data, *op);
gesture::apply(*C, *gesture_data, *op);
return OPERATOR_FINISHED;
}
static int gesture_line_invoke(bContext *C, wmOperator *op, const wmEvent *event)
{
if (!can_invoke(*C)) {
return OPERATOR_CANCELLED;
}
RNA_int_set_array(op->ptr, "location", event->mval);
return WM_gesture_straightline_active_side_invoke(C, op, event);
}
void SCULPT_OT_trim_lasso_gesture(wmOperatorType *ot)
{
ot->name = "Trim Lasso Gesture";
@ -814,4 +904,25 @@ void SCULPT_OT_trim_box_gesture(wmOperatorType *ot)
operator_properties(ot);
}
void SCULPT_OT_trim_line_gesture(wmOperatorType *ot)
{
ot->name = "Trim Line Gesture";
ot->idname = "SCULPT_OT_trim_line_gesture";
ot->description = "Trims the mesh divided by the line as you move the brush";
ot->invoke = gesture_line_invoke;
ot->modal = WM_gesture_straightline_oneshot_modal;
ot->exec = gesture_line_exec;
ot->poll = SCULPT_mode_poll_view3d;
ot->flag = OPTYPE_REGISTER;
/* Properties. */
WM_operator_properties_gesture_straightline(ot, WM_CURSOR_EDIT);
gesture::operator_properties(ot, gesture::ShapeType::Line);
operator_properties(ot);
}
} // namespace blender::ed::sculpt_paint::trim

View File

@ -4135,6 +4135,7 @@ static void gesture_straightline_modal_keymap(wmKeyConfig *keyconf)
WM_modalkeymap_assign(keymap, "PAINT_OT_weight_gradient");
WM_modalkeymap_assign(keymap, "MESH_OT_bisect");
WM_modalkeymap_assign(keymap, "PAINT_OT_mask_line_gesture");
WM_modalkeymap_assign(keymap, "SCULPT_OT_trim_line_gesture");
WM_modalkeymap_assign(keymap, "SCULPT_OT_project_line_gesture");
WM_modalkeymap_assign(keymap, "PAINT_OT_hide_show_line_gesture");
}