New freehand curve drawing tool

- Access with Shift-LMB or from the 'Create' toolbar tab.
- Uses curve fitting for bezier curves, with error and corner angle options.
- Optional tablet pressure to curve radius mapping.
- Depth can use the cursor or optionally draw onto the surface,
  for the entire stroke or using the stroke start.
- Stroke plane can optionally be perpendicular to, or aligned to the surface normal.
- Optional radius tapering and for start/end points.
- Supports operator redo and calling from Python.
This commit is contained in:
Campbell Barton 2016-04-15 18:10:05 +10:00
parent e56e7bd1ec
commit 8ac662c77a
11 changed files with 1385 additions and 8 deletions

View File

@ -55,7 +55,7 @@
*
* \returns zero on success, nonzero is reserved for error values.
*/
int curve_fit_cubic_from_points_db(
int curve_fit_cubic_to_points_db(
const double *points,
const unsigned int points_len,
const unsigned int dims,
@ -67,7 +67,7 @@ int curve_fit_cubic_from_points_db(
unsigned int **r_cubic_orig_index,
unsigned int **r_corner_index_array, unsigned int *r_corner_index_len);
int curve_fit_cubic_from_points_fl(
int curve_fit_cubic_to_points_fl(
const float *points,
const unsigned int points_len,
const unsigned int dims,

View File

@ -846,7 +846,7 @@ static void fit_cubic_to_points(
* Take an array of 3d points.
* return the cubic splines
*/
int curve_fit_cubic_from_points_db(
int curve_fit_cubic_to_points_db(
const double *points,
const uint points_len,
const uint dims,
@ -984,9 +984,9 @@ int curve_fit_cubic_from_points_db(
}
/**
* A version of #curve_fit_cubic_from_points_db to handle floats
* A version of #curve_fit_cubic_to_points_db to handle floats
*/
int curve_fit_cubic_from_points_fl(
int curve_fit_cubic_to_points_fl(
const float *points,
const uint points_len,
const uint dims,
@ -1009,7 +1009,7 @@ int curve_fit_cubic_from_points_fl(
float *cubic_array_fl = NULL;
uint cubic_array_len = 0;
int result = curve_fit_cubic_from_points_db(
int result = curve_fit_cubic_to_points_db(
points_db, points_len, dims, error_threshold, corners, corners_len,
&cubic_array_db, &cubic_array_len,
r_cubic_orig_index,

View File

@ -136,6 +136,7 @@ class VIEW3D_PT_tools_add_object(View3DPanel, Panel):
@staticmethod
def draw_add_curve(layout, label=False):
if label:
layout.label(text="Bezier:")
layout.operator("curve.primitive_bezier_curve_add", text="Bezier", icon='CURVE_BEZCURVE')
@ -149,6 +150,10 @@ class VIEW3D_PT_tools_add_object(View3DPanel, Panel):
layout.operator("curve.primitive_nurbs_circle_add", text="Nurbs Circle", icon='CURVE_NCIRCLE')
layout.operator("curve.primitive_nurbs_path_add", text="Path", icon='CURVE_PATH')
layout.separator()
layout.operator("curve.draw", icon='LINE_DATA')
@staticmethod
def draw_add_surface(layout):
layout.operator("surface.primitive_nurbs_surface_curve_add", text="Nurbs Curve", icon='SURFACE_NCURVE')
@ -546,8 +551,61 @@ class VIEW3D_PT_tools_add_curve_edit(View3DPanel, Panel):
VIEW3D_PT_tools_add_object.draw_add_curve(col, label=True)
# ********** default tools for editmode_surface ****************
class VIEW3D_PT_tools_curveedit_options_stroke(View3DPanel, Panel):
bl_category = "Options"
bl_context = "curve_edit"
bl_label = "Curve Stroke"
def draw(self, context):
layout = self.layout
tool_settings = context.tool_settings
cps = tool_settings.curve_paint_settings
col = layout.column()
col.prop(cps, "curve_type")
if cps.curve_type == 'BEZIER':
col.label("Bezier Options:")
col.prop(cps, "error_threshold")
col.prop(cps, "use_corners_detect")
col = layout.column()
col.active = cps.use_corners_detect
col.prop(cps, "corner_angle")
col.label("Pressure Radius:")
row = layout.row(align=True)
rowsub = row.row(align=True)
rowsub.prop(cps, "radius_min", text="Min")
rowsub.prop(cps, "radius_max", text="Max")
row.prop(cps, "use_pressure_radius", text="", icon_only=True)
col = layout.column()
col.label("Taper Radius:")
row = layout.row(align=True)
row.prop(cps, "radius_taper_start", text="Start")
row.prop(cps, "radius_taper_end", text="End")
col = layout.column()
col.label("Projection Depth:")
row = layout.row(align=True)
row.prop(cps, "depth_mode", expand=True)
col = layout.column()
if cps.depth_mode == 'SURFACE':
col.prop(cps, "use_stroke_endpoints")
if cps.use_stroke_endpoints:
colsub = layout.column(align=True)
colsub.prop(cps, "surface_plane", expand=True)
else:
col.prop(cps, "radius_offset")
# ********** default tools for editmode_surface ****************
class VIEW3D_PT_tools_transform_surface(View3DPanel, Panel):
bl_category = "Tools"

View File

@ -616,6 +616,12 @@ void BKE_scene_init(Scene *sce)
sce->toolsettings->skgen_subdivisions[1] = SKGEN_SUB_LENGTH;
sce->toolsettings->skgen_subdivisions[2] = SKGEN_SUB_ANGLE;
sce->toolsettings->curve_paint_settings.curve_type = CU_BEZIER;
sce->toolsettings->curve_paint_settings.flag |= CURVE_PAINT_FLAG_CORNERS_DETECT;
sce->toolsettings->curve_paint_settings.error_threshold = 8;
sce->toolsettings->curve_paint_settings.radius_max = 1.0f;
sce->toolsettings->curve_paint_settings.corner_angle = DEG2RADF(70.0f);
sce->toolsettings->statvis.overhang_axis = OB_NEGZ;
sce->toolsettings->statvis.overhang_min = 0;
sce->toolsettings->statvis.overhang_max = DEG2RADF(45.0f);

View File

@ -1072,5 +1072,17 @@ void blo_do_versions_270(FileData *fd, Library *UNUSED(lib), Main *main)
}
}
}
for (Scene *scene = main->scene.first; scene; scene = scene->id.next) {
CurvePaintSettings *cps = &scene->toolsettings->curve_paint_settings;
if (cps->error_threshold == 0) {
cps->curve_type = CU_BEZIER;
cps->flag |= CURVE_PAINT_FLAG_CORNERS_DETECT;
cps->error_threshold = 8;
cps->radius_max = 1.0f;
cps->corner_angle = DEG2RADF(70.0f);
}
}
}
}

View File

@ -23,20 +23,24 @@ set(INC
../../blenkernel
../../blenlib
../../blentranslation
../../gpu
../../makesdna
../../makesrna
../../windowmanager
../../../../intern/guardedalloc
../../../../intern/glew-mx
../../../../extern/curve_fit_nd
)
set(INC_SYS
${GLEW_INCLUDE_PATH}
)
set(SRC
curve_ops.c
editcurve.c
editcurve_add.c
editcurve_paint.c
editcurve_select.c
editfont.c
@ -47,4 +51,6 @@ if(WITH_INTERNATIONAL)
add_definitions(-DWITH_INTERNATIONAL)
endif()
add_definitions(${GL_DEFINITIONS})
blender_add_lib(bf_editor_curve "${SRC}" "${INC}" "${INC_SYS}")

View File

@ -166,4 +166,7 @@ void SURFACE_OT_primitive_nurbs_surface_cylinder_add(struct wmOperatorType *ot);
void SURFACE_OT_primitive_nurbs_surface_sphere_add(struct wmOperatorType *ot);
void SURFACE_OT_primitive_nurbs_surface_torus_add(struct wmOperatorType *ot);
/* editcurve_paint.c */
void CURVE_OT_draw(struct wmOperatorType *ot);
#endif /* __CURVE_INTERN_H__ */

View File

@ -135,6 +135,7 @@ void ED_operatortypes_curve(void)
WM_operatortype_append(CURVE_OT_make_segment);
WM_operatortype_append(CURVE_OT_spin);
WM_operatortype_append(CURVE_OT_vertex_add);
WM_operatortype_append(CURVE_OT_draw);
WM_operatortype_append(CURVE_OT_extrude);
WM_operatortype_append(CURVE_OT_cyclic_toggle);
@ -234,6 +235,9 @@ void ED_keymap_curve(wmKeyConfig *keyconf)
WM_keymap_add_item(keymap, "CURVE_OT_vertex_add", ACTIONMOUSE, KM_CLICK, KM_CTRL, 0);
kmi = WM_keymap_add_item(keymap, "CURVE_OT_draw", ACTIONMOUSE, KM_PRESS, KM_SHIFT, 0);
RNA_boolean_set(kmi->ptr, "wait_for_input", false);
kmi = WM_keymap_add_item(keymap, "CURVE_OT_select_all", AKEY, KM_PRESS, 0, 0);
RNA_enum_set(kmi->ptr, "action", SEL_TOGGLE);
kmi = WM_keymap_add_item(keymap, "CURVE_OT_select_all", IKEY, KM_PRESS, KM_CTRL, 0);

File diff suppressed because it is too large Load Diff

View File

@ -1266,6 +1266,40 @@ typedef enum {
UNIFIED_PAINT_BRUSH_ALPHA_PRESSURE = (1 << 4)
} UnifiedPaintSettingsFlags;
typedef struct CurvePaintSettings {
char curve_type;
char flag;
char depth_mode;
char surface_plane;
int error_threshold;
float radius_min, radius_max;
float radius_taper_start, radius_taper_end;
float radius_offset;
float corner_angle;
} CurvePaintSettings;
/* CurvePaintSettings.flag */
enum {
CURVE_PAINT_FLAG_CORNERS_DETECT = (1 << 0),
CURVE_PAINT_FLAG_PRESSURE_RADIUS = (1 << 1),
CURVE_PAINT_FLAG_DEPTH_STROKE_ENDPOINTS = (1 << 2),
};
/* CurvePaintSettings.depth_mode */
enum {
CURVE_PAINT_PROJECT_CURSOR = 0,
CURVE_PAINT_PROJECT_SURFACE = 1,
};
/* CurvePaintSettings.surface_plane */
enum {
CURVE_PAINT_SURFACE_PLANE_NORMAL_VIEW = 0,
CURVE_PAINT_SURFACE_PLANE_NORMAL_SURFACE = 1,
CURVE_PAINT_SURFACE_PLANE_VIEW = 2,
};
/* *************************************************************** */
/* Stats */
@ -1417,6 +1451,8 @@ typedef struct ToolSettings {
/* Unified Paint Settings */
struct UnifiedPaintSettings unified_paint_settings;
struct CurvePaintSettings curve_paint_settings;
struct MeshStatVis statvis;
} ToolSettings;

View File

@ -1767,6 +1767,11 @@ static char *rna_UnifiedPaintSettings_path(PointerRNA *UNUSED(ptr))
return BLI_strdup("tool_settings.unified_paint_settings");
}
static char *rna_CurvePaintSettings_path(PointerRNA *UNUSED(ptr))
{
return BLI_strdup("tool_settings.curve_paint_settings");
}
/* generic function to recalc geometry */
static void rna_EditMesh_update(Main *UNUSED(bmain), Scene *scene, PointerRNA *UNUSED(ptr))
{
@ -2497,6 +2502,12 @@ static void rna_def_tool_settings(BlenderRNA *brna)
RNA_def_property_struct_type(prop, "UnifiedPaintSettings");
RNA_def_property_ui_text(prop, "Unified Paint Settings", NULL);
/* Curve Paint Settings */
prop = RNA_def_property(srna, "curve_paint_settings", PROP_POINTER, PROP_NONE);
RNA_def_property_flag(prop, PROP_NEVER_NULL);
RNA_def_property_struct_type(prop, "CurvePaintSettings");
RNA_def_property_ui_text(prop, "Curve Paint Settings", NULL);
/* Mesh Statistics */
prop = RNA_def_property(srna, "statvis", PROP_POINTER, PROP_NONE);
RNA_def_property_flag(prop, PROP_NEVER_NULL);
@ -2595,6 +2606,96 @@ static void rna_def_unified_paint_settings(BlenderRNA *brna)
"when unlocked brush size is given in pixels");
}
static void rna_def_curve_paint_settings(BlenderRNA *brna)
{
StructRNA *srna;
PropertyRNA *prop;
srna = RNA_def_struct(brna, "CurvePaintSettings", NULL);
RNA_def_struct_path_func(srna, "rna_CurvePaintSettings_path");
RNA_def_struct_ui_text(srna, "Curve Paint Settings", "");
static EnumPropertyItem curve_type_items[] = {
{CU_POLY, "POLY", 0, "Poly", ""},
{CU_BEZIER, "BEZIER", 0, "Bezier", ""},
{0, NULL, 0, NULL, NULL}};
prop = RNA_def_property(srna, "curve_type", PROP_ENUM, PROP_NONE);
RNA_def_property_enum_sdna(prop, NULL, "curve_type");
RNA_def_property_enum_items(prop, curve_type_items);
RNA_def_property_ui_text(prop, "Type", "Type of curve to use for new strokes");
prop = RNA_def_property(srna, "use_corners_detect", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "flag", CURVE_PAINT_FLAG_CORNERS_DETECT);
RNA_def_property_ui_text(prop, "Detect Corners", "Detect corners and use non-aligned handles");
prop = RNA_def_property(srna, "use_pressure_radius", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "flag", CURVE_PAINT_FLAG_PRESSURE_RADIUS);
RNA_def_property_ui_icon(prop, ICON_STYLUS_PRESSURE, 0);
RNA_def_property_ui_text(prop, "Use Pressure", "Map tablet pressure to curve radius");
prop = RNA_def_property(srna, "use_stroke_endpoints", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "flag", CURVE_PAINT_FLAG_DEPTH_STROKE_ENDPOINTS);
RNA_def_property_ui_text(prop, "Only First", "Use the start of the stroke for the depth");
prop = RNA_def_property(srna, "error_threshold", PROP_INT, PROP_PIXEL);
RNA_def_property_range(prop, 1, 100);
RNA_def_property_ui_text(prop, "Tolerance", "Allow deviation for a smoother, less preceise line");
prop = RNA_def_property(srna, "corner_angle", PROP_FLOAT, PROP_ANGLE);
RNA_def_property_range(prop, 0, M_PI);
RNA_def_property_ui_text(prop, "Corner Angle", "Angles above this are considered corners");
prop = RNA_def_property(srna, "radius_min", PROP_FLOAT, PROP_NONE);
RNA_def_property_range(prop, 0.0, 100.0);
RNA_def_property_ui_range(prop, 0.0f, 10.0, 10, 2);
RNA_def_property_ui_text(prop, "Radius Min",
"Minimum radius when the minimum pressure is applied (also the minimum when tapering)");
prop = RNA_def_property(srna, "radius_max", PROP_FLOAT, PROP_NONE);
RNA_def_property_range(prop, 0.0, 100.0);
RNA_def_property_ui_range(prop, 0.0f, 10.0, 10, 2);
RNA_def_property_ui_text(prop, "Radius Max",
"Radius to use when the maximum pressure is applied (or when a tablet isn't used)");
prop = RNA_def_property(srna, "radius_taper_start", PROP_FLOAT, PROP_NONE);
RNA_def_property_range(prop, 0.0, 1.0);
RNA_def_property_ui_range(prop, 0.0f, 1.0, 1, 2);
RNA_def_property_ui_text(prop, "Radius Min", "Taper factor for the radius of each point along the curve");
prop = RNA_def_property(srna, "radius_taper_end", PROP_FLOAT, PROP_NONE);
RNA_def_property_range(prop, 0.0, 10.0);
RNA_def_property_ui_range(prop, 0.0f, 1.0, 1, 2);
RNA_def_property_ui_text(prop, "Radius Max", "Taper factor for the radius of each point along the curve");
prop = RNA_def_property(srna, "radius_offset", PROP_FLOAT, PROP_NONE);
RNA_def_property_range(prop, -10.0, 10.0);
RNA_def_property_ui_range(prop, -1.0f, 1.0, 1, 2);
RNA_def_property_ui_text(prop, "Offset", "Offset the stroke from the surface");
static EnumPropertyItem depth_mode_items[] = {
{CURVE_PAINT_PROJECT_CURSOR, "CURSOR", 0, "Cursor", ""},
{CURVE_PAINT_PROJECT_SURFACE, "SURFACE", 0, "Surface", ""},
{0, NULL, 0, NULL, NULL}};
prop = RNA_def_property(srna, "depth_mode", PROP_ENUM, PROP_NONE);
RNA_def_property_enum_sdna(prop, NULL, "depth_mode");
RNA_def_property_enum_items(prop, depth_mode_items);
RNA_def_property_ui_text(prop, "Depth", "Method of projecting depth");
static EnumPropertyItem surface_plane_items[] = {
{CURVE_PAINT_SURFACE_PLANE_NORMAL_VIEW, "NORMAL_VIEW", 0, "Normal/View", "Draw perpendicular to the surface"},
{CURVE_PAINT_SURFACE_PLANE_NORMAL_SURFACE, "NORMAL_SURFACE", 0, "Normal/Surface", "Draw aligned to the surface"},
{CURVE_PAINT_SURFACE_PLANE_VIEW, "VIEW", 0, "View", "Draw aligned to the viewport"},
{0, NULL, 0, NULL, NULL}};
prop = RNA_def_property(srna, "surface_plane", PROP_ENUM, PROP_NONE);
RNA_def_property_enum_sdna(prop, NULL, "surface_plane");
RNA_def_property_enum_items(prop, surface_plane_items);
RNA_def_property_ui_text(prop, "Plane", "Plane for projected stroke");
}
static void rna_def_statvis(BlenderRNA *brna)
{
StructRNA *srna;
@ -6720,6 +6821,7 @@ void RNA_def_scene(BlenderRNA *brna)
RNA_define_animate_sdna(false);
rna_def_tool_settings(brna);
rna_def_unified_paint_settings(brna);
rna_def_curve_paint_settings(brna);
rna_def_statvis(brna);
rna_def_unit_settings(brna);
rna_def_scene_image_format_data(brna);