forked from bartvdbraak/blender
GP: New Cutter, Constraints and Segment selection
This commit groups a set of new tools that were tested in grease pencil object branch before moving to master. We decide to do all the development in a separated branch because it could break master during days or weeks before the new tools were ready to deploy. The commit includes: - New Cutter tool to trim strokes and help cleaning up drawings. - New set of constraints and guides to draw different types of shapes. All the credits for this development goes to Charlie Jolly (@charlie), thanks for your help! - Segment selection mode to select strokes between intersections. - New operator to change strokes cap mode. - New option to display only keyframed frames. This option is very important when fill strokes with color. - Multiple small fixes and tweaks. Thanks to @pepeland and @mendio for their ideas, tests, reviews and support. Note: Still pending the final icons for Cutter in Toolbar and Segment Selection in Topbar. @billreynish could help us here?
This commit is contained in:
parent
6dbfd7f6d6
commit
bb9c9d0eaa
@ -2983,6 +2983,8 @@ def km_grease_pencil_stroke_edit_mode(params):
|
||||
{"properties": [("mode", 0)]}),
|
||||
("gpencil.selectmode_toggle", {"type": 'TWO', "value": 'PRESS'},
|
||||
{"properties": [("mode", 1)]}),
|
||||
("gpencil.selectmode_toggle", {"type": 'THREE', "value": 'PRESS'},
|
||||
{"properties": [("mode", 2)]}),
|
||||
])
|
||||
|
||||
if params.apple:
|
||||
@ -3045,7 +3047,25 @@ def km_grease_pencil_stroke_paint_draw_brush(params):
|
||||
# Erase
|
||||
("gpencil.draw", {"type": 'LEFTMOUSE', "value": 'PRESS', "ctrl": True},
|
||||
{"properties": [("mode", 'ERASER'), ("wait_for_input", False)]}),
|
||||
|
||||
# Constrain Guides Speedlines
|
||||
# Freehand
|
||||
("gpencil.draw", {"type": 'O', "value": 'PRESS'}, None),
|
||||
("gpencil.draw", {"type": 'J', "value": 'PRESS'}, None),
|
||||
("gpencil.draw", {"type": 'J', "value": 'PRESS', "alt": True}, None),
|
||||
("gpencil.draw", {"type": 'J', "value": 'PRESS', "shift": True}, None),
|
||||
("gpencil.draw", {"type": 'K', "value": 'PRESS'}, None),
|
||||
("gpencil.draw", {"type": 'K', "value": 'PRESS', "alt": True}, None),
|
||||
("gpencil.draw", {"type": 'K', "value": 'PRESS', "shift": True}, None),
|
||||
("gpencil.draw", {"type": 'L', "value": 'PRESS'}, None),
|
||||
("gpencil.draw", {"type": 'L', "value": 'PRESS', "alt": True}, None),
|
||||
("gpencil.draw", {"type": 'L', "value": 'PRESS', "ctrl": True}, None),
|
||||
("gpencil.draw", {"type": 'V', "value": 'PRESS'}, None),
|
||||
# Mirror or flip
|
||||
("gpencil.draw", {"type": 'M', "value": 'PRESS'}, None),
|
||||
# Mode
|
||||
("gpencil.draw", {"type": 'C', "value": 'PRESS'}, None),
|
||||
# Set reference point
|
||||
("gpencil.draw", {"type": 'C', "value": 'PRESS', "alt": True}, None),
|
||||
# Tablet Mappings for Drawing ------------------ */
|
||||
# For now, only support direct drawing using the eraser, as most users using a tablet
|
||||
# may still want to use that as their primary pointing device!
|
||||
@ -5718,6 +5738,7 @@ def km_3d_view_tool_paint_gpencil_arc(params):
|
||||
]},
|
||||
)
|
||||
|
||||
|
||||
def km_3d_view_tool_paint_gpencil_curve(params):
|
||||
return (
|
||||
"3D View Tool: Paint Gpencil, Curve",
|
||||
@ -5730,6 +5751,19 @@ def km_3d_view_tool_paint_gpencil_curve(params):
|
||||
]},
|
||||
)
|
||||
|
||||
|
||||
def km_3d_view_tool_paint_gpencil_cutter(params):
|
||||
return (
|
||||
"3D View Tool: Paint Gpencil, Cutter",
|
||||
{"space_type": 'VIEW_3D', "region_type": 'WINDOW'},
|
||||
{"items": [
|
||||
("gpencil.stroke_cutter", {"type": params.tool_mouse, "value": 'PRESS'}, None),
|
||||
# Lasso select
|
||||
("gpencil.select_lasso", {"type": params.action_tweak, "value": 'ANY', "ctrl": True, "alt": True}, None),
|
||||
]},
|
||||
)
|
||||
|
||||
|
||||
def km_3d_view_tool_edit_gpencil_select(params):
|
||||
return (
|
||||
"3D View Tool: Edit Gpencil, Select",
|
||||
@ -6040,6 +6074,7 @@ def generate_keymaps(params=None):
|
||||
km_3d_view_tool_paint_gpencil_circle(params),
|
||||
km_3d_view_tool_paint_gpencil_arc(params),
|
||||
km_3d_view_tool_paint_gpencil_curve(params),
|
||||
km_3d_view_tool_paint_gpencil_cutter(params),
|
||||
km_3d_view_tool_edit_gpencil_select(params),
|
||||
km_3d_view_tool_edit_gpencil_select_box(params),
|
||||
km_3d_view_tool_edit_gpencil_select_circle(params),
|
||||
|
@ -145,6 +145,9 @@ class DATA_PT_gpencil_datapanel(Panel):
|
||||
srow.prop(gpl, "clamp_layer", text="",
|
||||
icon='MOD_MASK' if gpl.clamp_layer else 'LAYER_ACTIVE')
|
||||
|
||||
srow = col.row(align=True)
|
||||
srow.prop(gpl, "use_solo_mode", text="Show Only On Keyframed")
|
||||
|
||||
col = row.column()
|
||||
|
||||
sub = col.column(align=True)
|
||||
|
@ -191,6 +191,7 @@ class GreasePencilStrokeEditPanel:
|
||||
col.operator("gpencil.duplicate_move", text="Duplicate")
|
||||
if is_3d_view:
|
||||
col.operator("gpencil.stroke_cyclical_set", text="Toggle Cyclic").type = 'TOGGLE'
|
||||
col.operator_menu_enum("gpencil.stroke_caps_set", text="Toggle Caps...", property="type")
|
||||
|
||||
layout.separator()
|
||||
|
||||
|
@ -1077,6 +1077,16 @@ class _defs_gpencil_paint:
|
||||
),
|
||||
)
|
||||
|
||||
@ToolDef.from_fn
|
||||
def cutter():
|
||||
return dict(
|
||||
text="Cutter",
|
||||
icon="ops.gpencil.stroke_cutter",
|
||||
cursor='KNIFE',
|
||||
widget=None,
|
||||
keymap=(),
|
||||
)
|
||||
|
||||
@ToolDef.from_fn
|
||||
def line():
|
||||
return dict(
|
||||
@ -1141,7 +1151,7 @@ class _defs_gpencil_edit:
|
||||
@ToolDef.from_fn
|
||||
def select():
|
||||
def draw_settings(context, layout, tool):
|
||||
pass
|
||||
layout.prop(context.tool_settings.gpencil_sculpt, "intersection_threshold")
|
||||
return dict(
|
||||
text="Select",
|
||||
icon="ops.generic.select",
|
||||
@ -1155,6 +1165,7 @@ class _defs_gpencil_edit:
|
||||
def draw_settings(context, layout, tool):
|
||||
props = tool.operator_properties("gpencil.select_box")
|
||||
layout.prop(props, "mode", expand=True)
|
||||
layout.prop(context.tool_settings.gpencil_sculpt, "intersection_threshold")
|
||||
return dict(
|
||||
text="Select Box",
|
||||
icon="ops.generic.select_box",
|
||||
@ -1168,6 +1179,7 @@ class _defs_gpencil_edit:
|
||||
def draw_settings(context, layout, tool):
|
||||
props = tool.operator_properties("gpencil.select_lasso")
|
||||
layout.prop(props, "mode", expand=True)
|
||||
layout.prop(context.tool_settings.gpencil_sculpt, "intersection_threshold")
|
||||
return dict(
|
||||
text="Select Lasso",
|
||||
icon="ops.generic.select_lasso",
|
||||
@ -1178,11 +1190,14 @@ class _defs_gpencil_edit:
|
||||
|
||||
@ToolDef.from_fn
|
||||
def circle_select():
|
||||
def draw_settings(context, layout, tool):
|
||||
layout.prop(context.tool_settings.gpencil_sculpt, "intersection_threshold")
|
||||
return dict(
|
||||
text="Select Circle",
|
||||
icon="ops.generic.select_circle",
|
||||
widget=None,
|
||||
keymap=(),
|
||||
draw_settings=draw_settings,
|
||||
)
|
||||
|
||||
@ToolDef.from_fn
|
||||
@ -1629,6 +1644,7 @@ class VIEW3D_PT_tools_active(ToolSelectPanelHelper, Panel):
|
||||
_defs_view3d_generic.cursor,
|
||||
None,
|
||||
_defs_gpencil_paint.generate_from_brushes,
|
||||
_defs_gpencil_paint.cutter,
|
||||
None,
|
||||
_defs_gpencil_paint.line,
|
||||
_defs_gpencil_paint.arc,
|
||||
|
@ -303,9 +303,13 @@ class _draw_left_context_mode:
|
||||
return
|
||||
|
||||
is_paint = True
|
||||
if (tool.name in {"Line", "Box", "Circle", "Arc", "Curve"}):
|
||||
if tool.name in {"Line", "Box", "Circle", "Arc", "Curve"}:
|
||||
is_paint = False
|
||||
elif (not tool.has_datablock):
|
||||
elif tool.name == "Cutter":
|
||||
row = layout.row(align=True)
|
||||
row.prop(context.tool_settings.gpencil_sculpt, "intersection_threshold")
|
||||
return
|
||||
elif not tool.has_datablock:
|
||||
return
|
||||
|
||||
paint = context.tool_settings.gpencil_paint
|
||||
@ -509,6 +513,9 @@ class TOPBAR_PT_gpencil_layers(Panel):
|
||||
srow.prop(gpl, "clamp_layer", text="",
|
||||
icon='MOD_MASK' if gpl.clamp_layer else 'LAYER_ACTIVE')
|
||||
|
||||
srow = col.row(align=True)
|
||||
srow.prop(gpl, "use_solo_mode", text="Show Only On Keyframed")
|
||||
|
||||
col = row.column()
|
||||
|
||||
sub = col.column(align=True)
|
||||
|
@ -245,6 +245,18 @@ class VIEW3D_HT_header(Header):
|
||||
text=lk_name,
|
||||
icon=lk_icon,
|
||||
)
|
||||
|
||||
if object_mode in {'PAINT_GPENCIL'}:
|
||||
if context.workspace.tools.from_space_view3d_mode(object_mode).name == "Draw":
|
||||
settings = tool_settings.gpencil_sculpt.guide
|
||||
row = layout.row(align=True)
|
||||
row.prop(settings, "use_guide", text="", icon='GRID')
|
||||
sub = row.row(align=True)
|
||||
sub.active = settings.use_guide
|
||||
sub.popover(
|
||||
panel="VIEW3D_PT_gpencil_guide",
|
||||
text="Guides"
|
||||
)
|
||||
|
||||
layout.separator_spacer()
|
||||
|
||||
@ -3955,6 +3967,7 @@ class VIEW3D_MT_edit_gpencil(Menu):
|
||||
|
||||
layout.menu("VIEW3D_MT_edit_gpencil_delete")
|
||||
layout.operator("gpencil.stroke_cyclical_set", text="Toggle Cyclic").type = 'TOGGLE'
|
||||
layout.operator_menu_enum("gpencil.stroke_caps_set", text="Toggle Caps...", property="type")
|
||||
|
||||
layout.separator()
|
||||
|
||||
@ -5317,7 +5330,47 @@ class VIEW3D_PT_gpencil_lock(Panel):
|
||||
col = row.column()
|
||||
col.prop(context.tool_settings.gpencil_sculpt, "lock_axis", expand=True)
|
||||
|
||||
|
||||
class VIEW3D_PT_gpencil_guide(Panel):
|
||||
bl_space_type = 'VIEW_3D'
|
||||
bl_region_type = 'HEADER'
|
||||
bl_label = "Guides"
|
||||
|
||||
@staticmethod
|
||||
def draw(self, context):
|
||||
from math import pi
|
||||
settings = context.tool_settings.gpencil_sculpt.guide
|
||||
|
||||
layout = self.layout
|
||||
layout.label(text="Guides")
|
||||
|
||||
col = layout.column()
|
||||
col.active = settings.use_guide
|
||||
col.prop(settings, "type", expand=True)
|
||||
|
||||
if settings.type in {'PARALLEL'}:
|
||||
col.prop(settings, "angle")
|
||||
row = col.row(align=True)
|
||||
|
||||
col.prop(settings, "use_snapping")
|
||||
if settings.use_snapping:
|
||||
|
||||
if settings.type in {'RADIAL'}:
|
||||
col.prop(settings, "angle_snap")
|
||||
else:
|
||||
col.prop(settings, "spacing")
|
||||
|
||||
col.label(text="Reference Point")
|
||||
row = col.row(align=True)
|
||||
row.prop(settings, "reference_point", expand=True)
|
||||
if settings.reference_point in {'CUSTOM'}:
|
||||
col.prop(settings, "location", text="Custom Location")
|
||||
if settings.reference_point in {'OBJECT'}:
|
||||
col.prop(settings, "reference_object", text="Object Location")
|
||||
if not settings.reference_object:
|
||||
col.label(text="No object selected, using cursor")
|
||||
|
||||
|
||||
class VIEW3D_PT_overlay_gpencil_options(Panel):
|
||||
bl_space_type = 'VIEW_3D'
|
||||
bl_region_type = 'HEADER'
|
||||
@ -5560,6 +5613,7 @@ class VIEW3D_MT_gpencil_edit_specials(Menu):
|
||||
layout.operator("gpencil.stroke_join", text="Join").type = 'JOIN'
|
||||
layout.operator("gpencil.stroke_join", text="Join & Copy").type = 'JOINCOPY'
|
||||
layout.operator("gpencil.stroke_flip", text="Flip Direction")
|
||||
layout.operator_menu_enum("gpencil.stroke_caps_set", text="Toggle Caps...", property="type")
|
||||
|
||||
layout.separator()
|
||||
layout.operator("gpencil.frame_duplicate", text="Duplicate Active Frame")
|
||||
@ -5787,6 +5841,7 @@ classes = (
|
||||
VIEW3D_PT_snapping,
|
||||
VIEW3D_PT_gpencil_origin,
|
||||
VIEW3D_PT_gpencil_lock,
|
||||
VIEW3D_PT_gpencil_guide,
|
||||
VIEW3D_PT_transform_orientations,
|
||||
VIEW3D_PT_overlay_gpencil_options,
|
||||
VIEW3D_PT_context_properties,
|
||||
|
@ -170,6 +170,10 @@ void BKE_gpencil_subdivide(struct bGPDstroke *gps, int level, int flag);
|
||||
|
||||
void BKE_gpencil_stroke_2d_flat(
|
||||
const struct bGPDspoint *points, int totpoints, float(*points2d)[2], int *r_direction);
|
||||
void BKE_gpencil_stroke_2d_flat_ref(
|
||||
const struct bGPDspoint *ref_points, int ref_totpoints,
|
||||
const struct bGPDspoint *points, int totpoints,
|
||||
float(*points2d)[2], const float scale, int *r_direction);
|
||||
|
||||
void BKE_gpencil_transform(struct bGPdata *gpd, float mat[4][4]);
|
||||
|
||||
|
@ -1563,6 +1563,8 @@ int BKE_gpencil_get_material_index(Object *ob, Material *ma)
|
||||
/* Get points of stroke always flat to view not affected by camera view or view position */
|
||||
void BKE_gpencil_stroke_2d_flat(const bGPDspoint *points, int totpoints, float(*points2d)[2], int *r_direction)
|
||||
{
|
||||
BLI_assert(totpoints >= 2);
|
||||
|
||||
const bGPDspoint *pt0 = &points[0];
|
||||
const bGPDspoint *pt1 = &points[1];
|
||||
const bGPDspoint *pt3 = &points[(int)(totpoints * 0.75)];
|
||||
@ -1576,7 +1578,15 @@ void BKE_gpencil_stroke_2d_flat(const bGPDspoint *points, int totpoints, float(*
|
||||
sub_v3_v3v3(locx, &pt1->x, &pt0->x);
|
||||
|
||||
/* point vector at 3/4 */
|
||||
sub_v3_v3v3(loc3, &pt3->x, &pt0->x);
|
||||
float v3[3];
|
||||
if (totpoints == 2) {
|
||||
mul_v3_v3fl(v3, &pt3->x, 0.001f);
|
||||
}
|
||||
else {
|
||||
copy_v3_v3(v3, &pt3->x);
|
||||
}
|
||||
|
||||
sub_v3_v3v3(loc3, v3, &pt0->x);
|
||||
|
||||
/* vector orthogonal to polygon plane */
|
||||
cross_v3_v3v3(normal, locx, loc3);
|
||||
@ -1604,3 +1614,86 @@ void BKE_gpencil_stroke_2d_flat(const bGPDspoint *points, int totpoints, float(*
|
||||
*r_direction = (int)locy[2];
|
||||
}
|
||||
|
||||
/* Get points of stroke always flat to view not affected by camera view or view position
|
||||
* using another stroke as reference
|
||||
*/
|
||||
void BKE_gpencil_stroke_2d_flat_ref(
|
||||
const bGPDspoint *ref_points, int ref_totpoints,
|
||||
const bGPDspoint *points, int totpoints,
|
||||
float(*points2d)[2], const float scale, int *r_direction)
|
||||
{
|
||||
BLI_assert(totpoints >= 2);
|
||||
|
||||
const bGPDspoint *pt0 = &ref_points[0];
|
||||
const bGPDspoint *pt1 = &ref_points[1];
|
||||
const bGPDspoint *pt3 = &ref_points[(int)(ref_totpoints * 0.75)];
|
||||
|
||||
float locx[3];
|
||||
float locy[3];
|
||||
float loc3[3];
|
||||
float normal[3];
|
||||
|
||||
/* local X axis (p0 -> p1) */
|
||||
sub_v3_v3v3(locx, &pt1->x, &pt0->x);
|
||||
|
||||
/* point vector at 3/4 */
|
||||
float v3[3];
|
||||
if (totpoints == 2) {
|
||||
mul_v3_v3fl(v3, &pt3->x, 0.001f);
|
||||
}
|
||||
else {
|
||||
copy_v3_v3(v3, &pt3->x);
|
||||
}
|
||||
|
||||
sub_v3_v3v3(loc3,v3, &pt0->x);
|
||||
|
||||
/* vector orthogonal to polygon plane */
|
||||
cross_v3_v3v3(normal, locx, loc3);
|
||||
|
||||
/* local Y axis (cross to normal/x axis) */
|
||||
cross_v3_v3v3(locy, normal, locx);
|
||||
|
||||
/* Normalize vectors */
|
||||
normalize_v3(locx);
|
||||
normalize_v3(locy);
|
||||
|
||||
/* Get all points in local space */
|
||||
for (int i = 0; i < totpoints; i++) {
|
||||
const bGPDspoint *pt = &points[i];
|
||||
float loc[3];
|
||||
float v1[3];
|
||||
float vn[3] = { 0.0f, 0.0f, 0.0f };
|
||||
|
||||
/* apply scale to extremes of the stroke to get better collision detection
|
||||
* the scale is divided to get more control in the UI parameter
|
||||
*/
|
||||
/* first point */
|
||||
if (i == 0) {
|
||||
const bGPDspoint *pt_next = &points[i + 1];
|
||||
sub_v3_v3v3(vn, &pt->x, &pt_next->x);
|
||||
normalize_v3(vn);
|
||||
mul_v3_fl(vn, scale / 10.0f);
|
||||
add_v3_v3v3(v1, &pt->x, vn);
|
||||
}
|
||||
/* last point */
|
||||
else if (i == totpoints - 1) {
|
||||
const bGPDspoint *pt_prev = &points[i - 1];
|
||||
sub_v3_v3v3(vn, &pt->x, &pt_prev->x);
|
||||
normalize_v3(vn);
|
||||
mul_v3_fl(vn, scale / 10.0f);
|
||||
add_v3_v3v3(v1, &pt->x, vn);
|
||||
}
|
||||
else {
|
||||
copy_v3_v3(v1, &pt->x);
|
||||
}
|
||||
|
||||
/* Get local space using first point as origin (ref stroke) */
|
||||
sub_v3_v3v3(loc, v1, &pt0->x);
|
||||
|
||||
points2d[i][0] = dot_v3v3(loc, locx);
|
||||
points2d[i][1] = dot_v3v3(loc, locy);
|
||||
}
|
||||
|
||||
/* Concave (-1), Convex (1), or Autodetect (0)? */
|
||||
*r_direction = (int)locy[2];
|
||||
}
|
||||
|
@ -466,7 +466,7 @@ void BKE_library_foreach_ID_link(Main *bmain, ID *id, LibraryIDLinkCallback call
|
||||
for (TimeMarker *marker = scene->markers.first; marker; marker = marker->next) {
|
||||
CALLBACK_INVOKE(marker->camera, IDWALK_CB_NOP);
|
||||
}
|
||||
|
||||
|
||||
if (toolsett) {
|
||||
CALLBACK_INVOKE(toolsett->particle.scene, IDWALK_CB_NOP);
|
||||
CALLBACK_INVOKE(toolsett->particle.object, IDWALK_CB_NOP);
|
||||
@ -493,6 +493,9 @@ void BKE_library_foreach_ID_link(Main *bmain, ID *id, LibraryIDLinkCallback call
|
||||
if (toolsett->gp_paint) {
|
||||
library_foreach_paint(&data, &toolsett->gp_paint->paint);
|
||||
}
|
||||
|
||||
CALLBACK_INVOKE(toolsett->gp_sculpt.guide.reference_object, IDWALK_CB_NOP);
|
||||
|
||||
}
|
||||
|
||||
if (scene->rigidbody_world) {
|
||||
|
@ -715,6 +715,8 @@ void BKE_scene_init(Scene *sce)
|
||||
CURVE_PRESET_BELL,
|
||||
CURVEMAP_SLOPE_POSITIVE);
|
||||
|
||||
sce->toolsettings->gp_sculpt.guide.spacing = 20.0f;
|
||||
|
||||
sce->physics_settings.gravity[0] = 0.0f;
|
||||
sce->physics_settings.gravity[1] = 0.0f;
|
||||
sce->physics_settings.gravity[2] = -9.81f;
|
||||
|
@ -6010,6 +6010,8 @@ static void lib_link_scene(FileData *fd, Main *main)
|
||||
|
||||
sce->toolsettings->particle.shape_object = newlibadr(fd, sce->id.lib, sce->toolsettings->particle.shape_object);
|
||||
|
||||
sce->toolsettings->gp_sculpt.guide.reference_object = newlibadr(fd, sce->id.lib, sce->toolsettings->gp_sculpt.guide.reference_object);
|
||||
|
||||
for (Base *base_legacy_next, *base_legacy = sce->base.first; base_legacy; base_legacy = base_legacy_next) {
|
||||
base_legacy_next = base_legacy->next;
|
||||
|
||||
|
@ -2741,6 +2741,16 @@ void blo_do_versions_280(FileData *fd, Library *UNUSED(lib), Main *bmain)
|
||||
}
|
||||
}
|
||||
|
||||
/* Grease pencil cutter/select segment intersection threshold */
|
||||
if (!DNA_struct_elem_find(fd->filesdna, "GP_Sculpt_Settings", "float", "isect_threshold")) {
|
||||
for (Scene *scene = bmain->scene.first; scene; scene = scene->id.next) {
|
||||
GP_Sculpt_Settings *gset = &scene->toolsettings->gp_sculpt;
|
||||
if (gset) {
|
||||
gset->isect_threshold = 0.1f;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Fix anamorphic bokeh eevee rna limits.*/
|
||||
for (Camera *ca = bmain->camera.first; ca; ca = ca->id.next) {
|
||||
if (ca->gpu_dof.ratio < 0.01f) {
|
||||
|
@ -390,6 +390,14 @@ GPUBatch *DRW_gpencil_get_buffer_ctrlpoint_geom(bGPdata *gpd)
|
||||
bGPDcontrolpoint *cps = gpd->runtime.cp_points;
|
||||
int totpoints = gpd->runtime.tot_cp_points;
|
||||
|
||||
const DRWContextState *draw_ctx = DRW_context_state_get();
|
||||
Scene *scene = draw_ctx->scene;
|
||||
ToolSettings *ts = scene->toolsettings;
|
||||
|
||||
if (ts->gp_sculpt.guide.use_guide) {
|
||||
totpoints++;
|
||||
}
|
||||
|
||||
static GPUVertFormat format = { 0 };
|
||||
static uint pos_id, color_id, size_id;
|
||||
if (format.attr_len == 0) {
|
||||
@ -415,6 +423,27 @@ GPUBatch *DRW_gpencil_get_buffer_ctrlpoint_geom(bGPdata *gpd)
|
||||
idx++;
|
||||
}
|
||||
|
||||
if (ts->gp_sculpt.guide.use_guide) {
|
||||
float size = 10 * 0.8f;
|
||||
float color[4];
|
||||
float position[3];
|
||||
if (ts->gp_sculpt.guide.reference_point == GP_GUIDE_REF_CUSTOM) {
|
||||
UI_GetThemeColor4fv(TH_GIZMO_PRIMARY, color);
|
||||
copy_v3_v3(position, ts->gp_sculpt.guide.location);
|
||||
}
|
||||
else if (ts->gp_sculpt.guide.reference_point == GP_GUIDE_REF_OBJECT && ts->gp_sculpt.guide.reference_object != NULL) {
|
||||
UI_GetThemeColor4fv(TH_GIZMO_SECONDARY, color);
|
||||
copy_v3_v3(position, ts->gp_sculpt.guide.reference_object->loc);
|
||||
}
|
||||
else {
|
||||
UI_GetThemeColor4fv(TH_REDALERT, color);
|
||||
copy_v3_v3(position, scene->cursor.location);
|
||||
}
|
||||
GPU_vertbuf_attr_set(vbo, pos_id, idx, position);
|
||||
GPU_vertbuf_attr_set(vbo, size_id, idx, &size);
|
||||
GPU_vertbuf_attr_set(vbo, color_id, idx, color);
|
||||
}
|
||||
|
||||
return GPU_batch_create_ex(GPU_PRIM_POINTS, vbo, NULL, GPU_BATCH_OWNS_VBO);
|
||||
}
|
||||
|
||||
|
@ -357,7 +357,7 @@ bool DRW_gpencil_onion_active(bGPdata *gpd)
|
||||
/* create shading group for strokes */
|
||||
DRWShadingGroup *DRW_gpencil_shgroup_stroke_create(
|
||||
GPENCIL_e_data *e_data, GPENCIL_Data *vedata, DRWPass *pass, GPUShader *shader, Object *ob,
|
||||
bGPdata *gpd, MaterialGPencilStyle *gp_style, int id, bool onion)
|
||||
bGPdata *gpd, bGPDstroke *gps, MaterialGPencilStyle *gp_style, int id, bool onion)
|
||||
{
|
||||
GPENCIL_StorageList *stl = ((GPENCIL_Data *)vedata)->stl;
|
||||
const float *viewport_size = DRW_viewport_size_get();
|
||||
@ -391,6 +391,10 @@ DRWShadingGroup *DRW_gpencil_shgroup_stroke_create(
|
||||
}
|
||||
DRW_shgroup_uniform_int(grp, "color_type", &stl->shgroups[id].color_type, 1);
|
||||
DRW_shgroup_uniform_float(grp, "pixfactor", &gpd->pixfactor, 1);
|
||||
|
||||
stl->shgroups[id].caps_mode[0] = gps->caps[0];
|
||||
stl->shgroups[id].caps_mode[1] = gps->caps[1];
|
||||
DRW_shgroup_uniform_int(grp, "caps_mode", &stl->shgroups[id].caps_mode[0], 2);
|
||||
}
|
||||
else {
|
||||
stl->storage->obj_scale = 1.0f;
|
||||
@ -405,6 +409,8 @@ DRWShadingGroup *DRW_gpencil_shgroup_stroke_create(
|
||||
else {
|
||||
DRW_shgroup_uniform_float(grp, "pixfactor", &stl->storage->pixfactor, 1);
|
||||
}
|
||||
const int zero[2] = { 0, 0 };
|
||||
DRW_shgroup_uniform_int(grp, "caps_mode", &zero[0], 2);
|
||||
}
|
||||
|
||||
if ((gpd) && (id > -1)) {
|
||||
@ -1177,7 +1183,8 @@ void DRW_gpencil_populate_buffer_strokes(GPENCIL_e_data *e_data, void *vedata, T
|
||||
if (gpd->runtime.sbuffer_size > 1) {
|
||||
if ((gp_style) && (gp_style->mode == GP_STYLE_MODE_LINE)) {
|
||||
stl->g_data->shgrps_drawing_stroke = DRW_gpencil_shgroup_stroke_create(
|
||||
e_data, vedata, psl->drawing_pass, e_data->gpencil_stroke_sh, NULL, gpd, gp_style, -1, false);
|
||||
e_data, vedata, psl->drawing_pass, e_data->gpencil_stroke_sh, NULL,
|
||||
gpd, NULL, gp_style, -1, false);
|
||||
}
|
||||
else {
|
||||
stl->g_data->shgrps_drawing_stroke = DRW_gpencil_shgroup_point_create(
|
||||
@ -1240,13 +1247,14 @@ void DRW_gpencil_populate_buffer_strokes(GPENCIL_e_data *e_data, void *vedata, T
|
||||
}
|
||||
}
|
||||
|
||||
/* control points */
|
||||
if ((overlay) && (gpd->runtime.tot_cp_points > 0) &&
|
||||
((gpd->runtime.sbuffer_sflag & GP_STROKE_ERASER) == 0) &&
|
||||
((v3d->gizmo_flag & V3D_GIZMO_HIDE) == 0) &&
|
||||
((v3d->gizmo_flag & V3D_GIZMO_HIDE_TOOL) == 0))
|
||||
{
|
||||
/* control points for primitives and speed guide */
|
||||
const bool is_cppoint = (gpd->runtime.tot_cp_points > 0);
|
||||
const bool is_speed_guide = (ts->gp_sculpt.guide.use_guide && (draw_ctx->object_mode == OB_MODE_PAINT_GPENCIL));
|
||||
const bool is_show_gizmo = (((v3d->gizmo_flag & V3D_GIZMO_HIDE) == 0) && ((v3d->gizmo_flag & V3D_GIZMO_HIDE_TOOL) == 0));
|
||||
|
||||
if ((overlay) && (is_cppoint || is_speed_guide) && (is_show_gizmo) &&
|
||||
((gpd->runtime.sbuffer_sflag & GP_STROKE_ERASER) == 0))
|
||||
{
|
||||
DRWShadingGroup *shgrp = DRW_shgroup_create(
|
||||
e_data->gpencil_edit_point_sh, psl->drawing_pass);
|
||||
const float *viewport_size = DRW_viewport_size_get();
|
||||
@ -1357,7 +1365,7 @@ static void DRW_gpencil_shgroups_create(
|
||||
|
||||
shgrp = DRW_gpencil_shgroup_stroke_create(
|
||||
e_data, vedata, psl->stroke_pass, e_data->gpencil_stroke_sh,
|
||||
ob, gpd, gp_style, stl->storage->shgroup_id, elm->onion);
|
||||
ob, gpd, gps, gp_style, stl->storage->shgroup_id, elm->onion);
|
||||
|
||||
DRW_shgroup_call_range_add(
|
||||
shgrp, cache->b_stroke.batch,
|
||||
@ -1472,7 +1480,7 @@ void DRW_gpencil_populate_multiedit(
|
||||
ToolSettings *ts = scene->toolsettings;
|
||||
|
||||
/* check if playing animation */
|
||||
bool playing = stl->storage->is_playing;
|
||||
const bool playing = stl->storage->is_playing;
|
||||
|
||||
/* calc max size of VBOs */
|
||||
gpencil_calc_vertex(stl, cache_ob, cache, gpd, cfra_eval);
|
||||
@ -1541,7 +1549,7 @@ void DRW_gpencil_populate_datablock(
|
||||
bGPDlayer *gpl_active = BKE_gpencil_layer_getactive(gpd);
|
||||
|
||||
/* check if playing animation */
|
||||
bool playing = stl->storage->is_playing;
|
||||
const bool playing = stl->storage->is_playing;
|
||||
|
||||
GpencilBatchCache *cache = gpencil_batch_cache_get(ob, cfra_eval);
|
||||
|
||||
@ -1567,6 +1575,10 @@ void DRW_gpencil_populate_datablock(
|
||||
continue;
|
||||
}
|
||||
|
||||
const bool is_solomode = GPENCIL_PAINT_MODE(gpd) &&
|
||||
(!playing) && (!stl->storage->is_render) &&
|
||||
(gpl->flag & GP_LAYER_SOLO_MODE);
|
||||
|
||||
/* filter view layer to gp layers in the same view layer (for compo) */
|
||||
if ((stl->storage->is_render) && (gpl->viewlayername[0] != '\0')) {
|
||||
if (!STREQ(view_layer->name, gpl->viewlayername)) {
|
||||
@ -1586,6 +1598,11 @@ void DRW_gpencil_populate_datablock(
|
||||
if (gpf == NULL)
|
||||
continue;
|
||||
|
||||
/* if solo mode, display only frames with keyframe in the current frame */
|
||||
if ((is_solomode) && (gpf->framenum != remap_cfra)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
opacity = gpl->opacity;
|
||||
/* if pose mode, maybe the overlay to fade geometry is enabled */
|
||||
if ((draw_ctx->obact) && (draw_ctx->object_mode == OB_MODE_POSE) &&
|
||||
|
@ -114,6 +114,7 @@ typedef struct GPENCIL_shgroup {
|
||||
int texture_clamp;
|
||||
int fill_style;
|
||||
int keep_size;
|
||||
int caps_mode[2];
|
||||
float obj_scale;
|
||||
} GPENCIL_shgroup;
|
||||
|
||||
@ -364,7 +365,8 @@ typedef struct GpencilBatchCache {
|
||||
/* general drawing functions */
|
||||
struct DRWShadingGroup *DRW_gpencil_shgroup_stroke_create(
|
||||
struct GPENCIL_e_data *e_data, struct GPENCIL_Data *vedata, struct DRWPass *pass, struct GPUShader *shader,
|
||||
struct Object *ob, struct bGPdata *gpd, struct MaterialGPencilStyle *gp_style, int id, bool onion);
|
||||
struct Object *ob, struct bGPdata *gpd, struct bGPDstroke *gps,
|
||||
struct MaterialGPencilStyle *gp_style, int id, bool onion);
|
||||
void DRW_gpencil_populate_datablock(
|
||||
struct GPENCIL_e_data *e_data, void *vedata,
|
||||
struct Object *ob, struct tGPencilObjectCache *cache_ob);
|
||||
|
@ -2,6 +2,7 @@ uniform mat4 ModelViewProjectionMatrix;
|
||||
uniform vec2 Viewport;
|
||||
uniform int xraymode;
|
||||
uniform int color_type;
|
||||
uniform int caps_mode[2];
|
||||
|
||||
layout(lines_adjacency) in;
|
||||
layout(triangle_strip, max_vertices = 13) out;
|
||||
@ -23,6 +24,8 @@ out vec2 uvfac;
|
||||
#define GPENCIL_COLOR_TEXTURE 1
|
||||
#define GPENCIL_COLOR_PATTERN 2
|
||||
|
||||
#define GPENCIL_FLATCAP 1
|
||||
|
||||
/* project 3d point to 2d on screen space */
|
||||
vec2 toScreenSpace(vec4 vertex)
|
||||
{
|
||||
@ -159,7 +162,9 @@ void main(void)
|
||||
}
|
||||
|
||||
/* generate the start endcap */
|
||||
if (is_equal(P0,P2) && (color_type == GPENCIL_COLOR_SOLID)){
|
||||
if ((caps_mode[0] != GPENCIL_FLATCAP) && is_equal(P0,P2) &&
|
||||
(color_type == GPENCIL_COLOR_SOLID))
|
||||
{
|
||||
vec4 cap_color = finalColor[1];
|
||||
|
||||
mTexCoord = vec2(2.0, 1.0);
|
||||
@ -205,7 +210,9 @@ void main(void)
|
||||
EmitVertex();
|
||||
|
||||
/* generate the end endcap */
|
||||
if (is_equal(P1,P3) && (color_type == GPENCIL_COLOR_SOLID) && (finaluvdata[2].x > 0)){
|
||||
if ((caps_mode[1] != GPENCIL_FLATCAP) && is_equal(P1,P3) &&
|
||||
(color_type == GPENCIL_COLOR_SOLID) && (finaluvdata[2].x > 0))
|
||||
{
|
||||
vec4 cap_color = finalColor[2];
|
||||
|
||||
mTexCoord = vec2(finaluvdata[2].x, 2.0);
|
||||
|
@ -907,7 +907,7 @@ static void gp_stroke_eraser_dostroke(
|
||||
|
||||
/* Second Pass: Remove any points that are tagged */
|
||||
if (do_cull) {
|
||||
gp_stroke_delete_tagged_points(gpf, gps, gps->next, GP_SPOINT_TAG, false);
|
||||
gp_stroke_delete_tagged_points(gpf, gps, gps->next, GP_SPOINT_TAG, false, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -40,6 +40,7 @@
|
||||
|
||||
#include "BLI_blenlib.h"
|
||||
#include "BLI_ghash.h"
|
||||
#include "BLI_lasso_2d.h"
|
||||
#include "BLI_math.h"
|
||||
#include "BLI_string.h"
|
||||
#include "BLI_string_utils.h"
|
||||
@ -86,6 +87,7 @@
|
||||
#include "ED_object.h"
|
||||
#include "ED_screen.h"
|
||||
#include "ED_view3d.h"
|
||||
#include "ED_select_utils.h"
|
||||
#include "ED_space_api.h"
|
||||
|
||||
#include "DEG_depsgraph.h"
|
||||
@ -260,7 +262,7 @@ void GPENCIL_OT_selectmode_toggle(wmOperatorType *ot)
|
||||
ot->flag = OPTYPE_UNDO | OPTYPE_REGISTER;
|
||||
|
||||
/* properties */
|
||||
prop = RNA_def_int(ot->srna, "mode", 0, 0, 1, "Select mode", "Select mode", 0, 1);
|
||||
prop = RNA_def_int(ot->srna, "mode", 0, 0, 2, "Select mode", "Select mode", 0, 2);
|
||||
RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE);
|
||||
}
|
||||
|
||||
@ -1829,9 +1831,10 @@ typedef struct tGPDeleteIsland {
|
||||
* - Once we start having larger islands than that, the number required
|
||||
* becomes much less
|
||||
* 2) Each island gets converted to a new stroke
|
||||
* If the number of points is <= limit, the stroke is deleted
|
||||
*/
|
||||
void gp_stroke_delete_tagged_points(bGPDframe *gpf, bGPDstroke *gps, bGPDstroke *next_stroke,
|
||||
int tag_flags, bool select)
|
||||
int tag_flags, bool select, int limit)
|
||||
{
|
||||
tGPDeleteIsland *islands = MEM_callocN(sizeof(tGPDeleteIsland) * (gps->totpoints + 1) / 2, "gp_point_islands");
|
||||
bool in_island = false;
|
||||
@ -1929,12 +1932,17 @@ void gp_stroke_delete_tagged_points(bGPDframe *gpf, bGPDstroke *gps, bGPDstroke
|
||||
}
|
||||
}
|
||||
|
||||
/* Add new stroke to the frame */
|
||||
if (next_stroke) {
|
||||
BLI_insertlinkbefore(&gpf->strokes, next_stroke, new_stroke);
|
||||
/* Add new stroke to the frame or delete if below limit */
|
||||
if ((limit > 0) && (new_stroke->totpoints <= limit)) {
|
||||
BKE_gpencil_free_stroke(new_stroke);
|
||||
}
|
||||
else {
|
||||
BLI_addtail(&gpf->strokes, new_stroke);
|
||||
if (next_stroke) {
|
||||
BLI_insertlinkbefore(&gpf->strokes, next_stroke, new_stroke);
|
||||
}
|
||||
else {
|
||||
BLI_addtail(&gpf->strokes, new_stroke);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1995,7 +2003,7 @@ static int gp_delete_selected_points(bContext *C)
|
||||
gps->flag &= ~GP_STROKE_SELECT;
|
||||
|
||||
/* delete unwanted points by splitting stroke into several smaller ones */
|
||||
gp_stroke_delete_tagged_points(gpf, gps, gpsn, GP_SPOINT_SELECT, false);
|
||||
gp_stroke_delete_tagged_points(gpf, gps, gpsn, GP_SPOINT_SELECT, false, 0);
|
||||
|
||||
changed = true;
|
||||
}
|
||||
@ -2507,6 +2515,102 @@ void GPENCIL_OT_stroke_cyclical_set(wmOperatorType *ot)
|
||||
ot->prop = RNA_def_enum(ot->srna, "type", cyclic_type, GP_STROKE_CYCLIC_TOGGLE, "Type", "");
|
||||
}
|
||||
|
||||
/* ******************* Flat Stroke Caps ************************** */
|
||||
|
||||
enum {
|
||||
GP_STROKE_CAPS_TOGGLE_BOTH = 0,
|
||||
GP_STROKE_CAPS_TOGGLE_START = 1,
|
||||
GP_STROKE_CAPS_TOGGLE_END = 2,
|
||||
GP_STROKE_CAPS_TOGGLE_DEFAULT = 3
|
||||
};
|
||||
|
||||
static int gp_stroke_caps_set_exec(bContext *C, wmOperator *op)
|
||||
{
|
||||
bGPdata *gpd = ED_gpencil_data_get_active(C);
|
||||
Object *ob = CTX_data_active_object(C);
|
||||
|
||||
const int type = RNA_enum_get(op->ptr, "type");
|
||||
|
||||
/* sanity checks */
|
||||
if (ELEM(NULL, gpd))
|
||||
return OPERATOR_CANCELLED;
|
||||
|
||||
/* loop all selected strokes */
|
||||
CTX_DATA_BEGIN(C, bGPDlayer *, gpl, editable_gpencil_layers)
|
||||
{
|
||||
if (gpl->actframe == NULL)
|
||||
continue;
|
||||
|
||||
for (bGPDstroke *gps = gpl->actframe->strokes.last; gps; gps = gps->prev) {
|
||||
MaterialGPencilStyle *gp_style = BKE_material_gpencil_settings_get(ob, gps->mat_nr + 1);
|
||||
|
||||
/* skip strokes that are not selected or invalid for current view */
|
||||
if (((gps->flag & GP_STROKE_SELECT) == 0) || ED_gpencil_stroke_can_use(C, gps) == false)
|
||||
continue;
|
||||
/* skip hidden or locked colors */
|
||||
if (!gp_style || (gp_style->flag & GP_STYLE_COLOR_HIDE) || (gp_style->flag & GP_STYLE_COLOR_LOCKED))
|
||||
continue;
|
||||
|
||||
if ((type == GP_STROKE_CAPS_TOGGLE_BOTH) ||
|
||||
(type == GP_STROKE_CAPS_TOGGLE_START))
|
||||
{
|
||||
++gps->caps[0];
|
||||
if (gps->caps[0] >= GP_STROKE_CAP_MAX) {
|
||||
gps->caps[0] = GP_STROKE_CAP_ROUND;
|
||||
}
|
||||
}
|
||||
if ((type == GP_STROKE_CAPS_TOGGLE_BOTH) ||
|
||||
(type == GP_STROKE_CAPS_TOGGLE_END))
|
||||
{
|
||||
++gps->caps[1];
|
||||
if (gps->caps[1] >= GP_STROKE_CAP_MAX) {
|
||||
gps->caps[1] = GP_STROKE_CAP_ROUND;
|
||||
}
|
||||
}
|
||||
if (type == GP_STROKE_CAPS_TOGGLE_DEFAULT) {
|
||||
gps->caps[0] = GP_STROKE_CAP_ROUND;
|
||||
gps->caps[1] = GP_STROKE_CAP_ROUND;
|
||||
}
|
||||
}
|
||||
}
|
||||
CTX_DATA_END;
|
||||
|
||||
/* notifiers */
|
||||
DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY);
|
||||
WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
|
||||
|
||||
return OPERATOR_FINISHED;
|
||||
}
|
||||
|
||||
/**
|
||||
* Change Stroke caps mode Rounded or Flat
|
||||
*/
|
||||
void GPENCIL_OT_stroke_caps_set(wmOperatorType *ot)
|
||||
{
|
||||
static const EnumPropertyItem toggle_type[] = {
|
||||
{GP_STROKE_CAPS_TOGGLE_BOTH, "TOGGLE", 0, "Both", ""},
|
||||
{GP_STROKE_CAPS_TOGGLE_START, "START", 0, "Start", ""},
|
||||
{GP_STROKE_CAPS_TOGGLE_END, "END", 0, "End", ""},
|
||||
{GP_STROKE_CAPS_TOGGLE_DEFAULT, "TOGGLE", 0, "Default", "Set as default rounded"},
|
||||
{0, NULL, 0, NULL, NULL}
|
||||
};
|
||||
|
||||
/* identifiers */
|
||||
ot->name = "Set Caps Mode";
|
||||
ot->idname = "GPENCIL_OT_stroke_caps_set";
|
||||
ot->description = "Change Stroke caps mode (rounded or flat)";
|
||||
|
||||
/* api callbacks */
|
||||
ot->exec = gp_stroke_caps_set_exec;
|
||||
ot->poll = gp_active_layer_poll;
|
||||
|
||||
/* flags */
|
||||
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
|
||||
|
||||
/* properties */
|
||||
ot->prop = RNA_def_enum(ot->srna, "type", toggle_type, GP_STROKE_CAPS_TOGGLE_BOTH, "Type", "");
|
||||
}
|
||||
|
||||
/* ******************* Stroke join ************************** */
|
||||
|
||||
/* Helper: flip stroke */
|
||||
@ -3472,10 +3576,10 @@ static int gp_stroke_separate_exec(bContext *C, wmOperator *op)
|
||||
}
|
||||
|
||||
/* delete selected points from destination stroke */
|
||||
gp_stroke_delete_tagged_points(gpf_dst, gps_dst, NULL, GP_SPOINT_SELECT, false);
|
||||
gp_stroke_delete_tagged_points(gpf_dst, gps_dst, NULL, GP_SPOINT_SELECT, false, 0);
|
||||
|
||||
/* delete selected points from origin stroke */
|
||||
gp_stroke_delete_tagged_points(gpf, gps, gpsn, GP_SPOINT_SELECT, false);
|
||||
gp_stroke_delete_tagged_points(gpf, gps, gpsn, GP_SPOINT_SELECT, false, 0);
|
||||
}
|
||||
/* selected strokes mode */
|
||||
else if (mode == GP_SEPARATE_STROKE) {
|
||||
@ -3609,10 +3713,10 @@ static int gp_stroke_split_exec(bContext *C, wmOperator *UNUSED(op))
|
||||
}
|
||||
|
||||
/* delete selected points from destination stroke */
|
||||
gp_stroke_delete_tagged_points(gpf, gps_dst, NULL, GP_SPOINT_SELECT, true);
|
||||
gp_stroke_delete_tagged_points(gpf, gps_dst, NULL, GP_SPOINT_SELECT, true, 0);
|
||||
|
||||
/* delete selected points from origin stroke */
|
||||
gp_stroke_delete_tagged_points(gpf, gps, gpsn, GP_SPOINT_SELECT, false);
|
||||
gp_stroke_delete_tagged_points(gpf, gps, gpsn, GP_SPOINT_SELECT, false, 0);
|
||||
}
|
||||
}
|
||||
/* select again tagged points */
|
||||
@ -3702,3 +3806,241 @@ void GPENCIL_OT_stroke_smooth(wmOperatorType *ot)
|
||||
RNA_def_boolean(ot->srna, "smooth_strength", false, "Strength", "");
|
||||
RNA_def_boolean(ot->srna, "smooth_uv", false, "UV", "");
|
||||
}
|
||||
|
||||
/* smart stroke cutter for trimming stroke ends */
|
||||
struct GP_SelectLassoUserData {
|
||||
rcti rect;
|
||||
const int(*mcords)[2];
|
||||
int mcords_len;
|
||||
};
|
||||
|
||||
static bool gpencil_test_lasso(
|
||||
bGPDstroke *gps, bGPDspoint *pt,
|
||||
const GP_SpaceConversion *gsc, const float diff_mat[4][4],
|
||||
void *user_data)
|
||||
{
|
||||
const struct GP_SelectLassoUserData *data = user_data;
|
||||
bGPDspoint pt2;
|
||||
int x0, y0;
|
||||
gp_point_to_parent_space(pt, diff_mat, &pt2);
|
||||
gp_point_to_xy(gsc, gps, &pt2, &x0, &y0);
|
||||
/* test if in lasso */
|
||||
return ((!ELEM(V2D_IS_CLIPPED, x0, y0)) &&
|
||||
BLI_rcti_isect_pt(&data->rect, x0, y0) &&
|
||||
BLI_lasso_is_point_inside(data->mcords, data->mcords_len, x0, y0, INT_MAX));
|
||||
}
|
||||
|
||||
typedef bool(*GPencilTestFn)(
|
||||
bGPDstroke *gps, bGPDspoint *pt,
|
||||
const GP_SpaceConversion *gsc, const float diff_mat[4][4], void *user_data);
|
||||
|
||||
static void gpencil_cutter_dissolve(bGPDlayer *hit_layer, bGPDstroke *hit_stroke)
|
||||
{
|
||||
bGPDspoint *pt = NULL;
|
||||
bGPDspoint *pt1 = NULL;
|
||||
int i;
|
||||
|
||||
bGPDstroke *gpsn = hit_stroke->next;
|
||||
|
||||
int totselect = 0;
|
||||
for (i = 0, pt = hit_stroke->points; i < hit_stroke->totpoints; i++, pt++) {
|
||||
if (pt->flag & GP_SPOINT_SELECT) {
|
||||
totselect++;
|
||||
}
|
||||
}
|
||||
|
||||
/* if all points selected delete or only 2 points and 1 selected */
|
||||
if ((totselect == 1) && (hit_stroke->totpoints == 2) ||
|
||||
(hit_stroke->totpoints == totselect))
|
||||
{
|
||||
BLI_remlink(&hit_layer->actframe->strokes, hit_stroke);
|
||||
BKE_gpencil_free_stroke(hit_stroke);
|
||||
hit_stroke = NULL;
|
||||
}
|
||||
|
||||
/* if very small distance delete */
|
||||
if ((hit_stroke) && (hit_stroke->totpoints == 2)) {
|
||||
pt = &hit_stroke->points[0];
|
||||
pt1 = &hit_stroke->points[1];
|
||||
if (len_v3v3(&pt->x, &pt1->x) < 0.001f) {
|
||||
BLI_remlink(&hit_layer->actframe->strokes, hit_stroke);
|
||||
BKE_gpencil_free_stroke(hit_stroke);
|
||||
hit_stroke = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
if (hit_stroke) {
|
||||
/* tag and dissolve (untag new points) */
|
||||
for (i = 0, pt = hit_stroke->points; i < hit_stroke->totpoints; i++, pt++) {
|
||||
if (pt->flag & GP_SPOINT_SELECT) {
|
||||
pt->flag &= ~GP_SPOINT_SELECT;
|
||||
pt->flag |= GP_SPOINT_TAG;
|
||||
}
|
||||
else if (pt->flag & GP_SPOINT_TAG) {
|
||||
pt->flag &= ~GP_SPOINT_TAG;
|
||||
}
|
||||
}
|
||||
gp_stroke_delete_tagged_points(
|
||||
hit_layer->actframe, hit_stroke, gpsn, GP_SPOINT_TAG, false, 1);
|
||||
}
|
||||
}
|
||||
|
||||
static int gpencil_cutter_lasso_select(
|
||||
bContext *C, wmOperator *op,
|
||||
GPencilTestFn is_inside_fn, void *user_data)
|
||||
{
|
||||
bGPdata *gpd = ED_gpencil_data_get_active(C);
|
||||
ScrArea *sa = CTX_wm_area(C);
|
||||
ToolSettings *ts = CTX_data_tool_settings(C);
|
||||
const float scale = ts->gp_sculpt.isect_threshold;
|
||||
|
||||
bGPDspoint *pt;
|
||||
int i;
|
||||
GP_SpaceConversion gsc = { NULL };
|
||||
|
||||
bool changed = false;
|
||||
|
||||
/* sanity checks */
|
||||
if (sa == NULL) {
|
||||
BKE_report(op->reports, RPT_ERROR, "No active area");
|
||||
return OPERATOR_CANCELLED;
|
||||
}
|
||||
|
||||
/* init space conversion stuff */
|
||||
gp_point_conversion_init(C, &gsc);
|
||||
|
||||
/* deselect all strokes first */
|
||||
CTX_DATA_BEGIN(C, bGPDstroke *, gps, editable_gpencil_strokes)
|
||||
{
|
||||
for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) {
|
||||
pt->flag &= ~GP_SPOINT_SELECT;
|
||||
}
|
||||
|
||||
gps->flag &= ~GP_STROKE_SELECT;
|
||||
}
|
||||
CTX_DATA_END;
|
||||
|
||||
/* select points */
|
||||
GP_EDITABLE_STROKES_BEGIN(gpstroke_iter, C, gpl, gps)
|
||||
{
|
||||
int tot_inside = 0;
|
||||
const int oldtot = gps->totpoints;
|
||||
for (i = 0; i < gps->totpoints; i++) {
|
||||
pt = &gps->points[i];
|
||||
if ((pt->flag & GP_SPOINT_SELECT) || (pt->flag & GP_SPOINT_TAG)) {
|
||||
continue;
|
||||
}
|
||||
/* convert point coords to screenspace */
|
||||
const bool is_inside = is_inside_fn(gps, pt, &gsc, gpstroke_iter.diff_mat, user_data);
|
||||
if (is_inside) {
|
||||
tot_inside++;
|
||||
changed = true;
|
||||
pt->flag |= GP_SPOINT_SELECT;
|
||||
gps->flag |= GP_STROKE_SELECT;
|
||||
float r_hita[3], r_hitb[3];
|
||||
if (gps->totpoints > 1) {
|
||||
ED_gpencil_select_stroke_segment(
|
||||
gpl, gps, pt, true, true, scale, r_hita, r_hitb);
|
||||
}
|
||||
/* avoid infinite loops */
|
||||
if (gps->totpoints > oldtot) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
/* if mark all points inside lasso set to remove all stroke */
|
||||
if ((tot_inside == oldtot) ||
|
||||
((tot_inside == 1) && (oldtot == 2)))
|
||||
{
|
||||
for (i = 0; i < gps->totpoints; i++) {
|
||||
pt = &gps->points[i];
|
||||
pt->flag |= GP_SPOINT_SELECT;
|
||||
}
|
||||
}
|
||||
}
|
||||
GP_EDITABLE_STROKES_END(gpstroke_iter);
|
||||
|
||||
/* dissolve selected points */
|
||||
bGPDstroke *gpsn;
|
||||
for (bGPDlayer *gpl = gpd->layers.first; gpl; gpl = gpl->next) {
|
||||
bGPDframe *gpf = gpl->actframe;
|
||||
if (gpf == NULL) {
|
||||
continue;
|
||||
}
|
||||
for (bGPDstroke *gps = gpf->strokes.first; gps; gps = gpsn) {
|
||||
gpsn = gps->next;
|
||||
if (gps->flag & GP_STROKE_SELECT) {
|
||||
gpencil_cutter_dissolve(gpl, gps);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* updates */
|
||||
if (changed) {
|
||||
DEG_id_tag_update(&gpd->id, ID_RECALC_GEOMETRY | ID_RECALC_COPY_ON_WRITE);
|
||||
WM_event_add_notifier(C, NC_GPENCIL | NA_SELECTED, NULL);
|
||||
WM_event_add_notifier(C, NC_GEOM | ND_SELECT, NULL);
|
||||
}
|
||||
|
||||
return OPERATOR_FINISHED;
|
||||
}
|
||||
|
||||
static bool gpencil_cutter_poll(bContext *C)
|
||||
{
|
||||
bGPdata *gpd = ED_gpencil_data_get_active(C);
|
||||
|
||||
if (GPENCIL_PAINT_MODE(gpd)) {
|
||||
if (gpd->layers.first)
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static int gpencil_cutter_exec(bContext *C, wmOperator *op)
|
||||
{
|
||||
ScrArea *sa = CTX_wm_area(C);
|
||||
/* sanity checks */
|
||||
if (sa == NULL) {
|
||||
BKE_report(op->reports, RPT_ERROR, "No active area");
|
||||
return OPERATOR_CANCELLED;
|
||||
}
|
||||
|
||||
struct GP_SelectLassoUserData data = { 0 };
|
||||
data.mcords = WM_gesture_lasso_path_to_array(C, op, &data.mcords_len);
|
||||
|
||||
/* Sanity check. */
|
||||
if (data.mcords == NULL) {
|
||||
return OPERATOR_PASS_THROUGH;
|
||||
}
|
||||
|
||||
/* Compute boundbox of lasso (for faster testing later). */
|
||||
BLI_lasso_boundbox(&data.rect, data.mcords, data.mcords_len);
|
||||
|
||||
gpencil_cutter_lasso_select(C, op, gpencil_test_lasso, &data);
|
||||
|
||||
MEM_freeN((void *)data.mcords);
|
||||
|
||||
return OPERATOR_FINISHED;
|
||||
}
|
||||
|
||||
void GPENCIL_OT_stroke_cutter(wmOperatorType *ot)
|
||||
{
|
||||
/* identifiers */
|
||||
ot->name = "Stroke Cutter";
|
||||
ot->description = "Select section and cut";
|
||||
ot->idname = "GPENCIL_OT_stroke_cutter";
|
||||
|
||||
/* callbacks */
|
||||
ot->invoke = WM_gesture_lasso_invoke;
|
||||
ot->modal = WM_gesture_lasso_modal;
|
||||
ot->exec = gpencil_cutter_exec;
|
||||
ot->poll = gpencil_cutter_poll;
|
||||
ot->cancel = WM_gesture_lasso_cancel;
|
||||
|
||||
/* flag */
|
||||
ot->flag = OPTYPE_UNDO | OPTYPE_USE_EVAL_DATA;
|
||||
|
||||
/* properties */
|
||||
WM_operator_properties_gesture_lasso(ot);
|
||||
}
|
||||
|
@ -234,7 +234,9 @@ void gp_apply_parent(struct Depsgraph *depsgraph, struct Object *obact, bGPdata
|
||||
*/
|
||||
void gp_apply_parent_point(struct Depsgraph *depsgraph, struct Object *obact, bGPdata *gpd, bGPDlayer *gpl, bGPDspoint *pt);
|
||||
|
||||
bool gp_point_xy_to_3d(GP_SpaceConversion *gsc, struct Scene *scene, const float screen_co[2], float r_out[3]);
|
||||
void gp_point_3d_to_xy(const GP_SpaceConversion *gsc, const short flag, const float pt[3], float xy[2]);
|
||||
|
||||
bool gp_point_xy_to_3d(const GP_SpaceConversion *gsc, struct Scene *scene, const float screen_co[2], float r_out[3]);
|
||||
|
||||
/* helper to convert 2d to 3d */
|
||||
void gp_stroke_convertcoords_tpoint(
|
||||
@ -263,7 +265,7 @@ struct GHash *gp_copybuf_validate_colormap(struct bContext *C);
|
||||
|
||||
void gp_stroke_delete_tagged_points(
|
||||
bGPDframe *gpf, bGPDstroke *gps, bGPDstroke *next_stroke,
|
||||
int tag_flags, bool select);
|
||||
int tag_flags, bool select, int limit);
|
||||
int gp_delete_selected_point_wrap(bContext *C);
|
||||
|
||||
void gp_subdivide_stroke(bGPDstroke *gps, const int subdivide);
|
||||
@ -291,12 +293,17 @@ void GPENCIL_OT_annotate(struct wmOperatorType *ot);
|
||||
void GPENCIL_OT_draw(struct wmOperatorType *ot);
|
||||
void GPENCIL_OT_fill(struct wmOperatorType *ot);
|
||||
|
||||
/* Guides ----------------------- */
|
||||
|
||||
void GPENCIL_OT_guide_rotate(struct wmOperatorType *ot);
|
||||
|
||||
/* Paint Modes for operator */
|
||||
typedef enum eGPencil_PaintModes {
|
||||
GP_PAINTMODE_DRAW = 0,
|
||||
GP_PAINTMODE_ERASER,
|
||||
GP_PAINTMODE_DRAW_STRAIGHT,
|
||||
GP_PAINTMODE_DRAW_POLY
|
||||
GP_PAINTMODE_DRAW_POLY,
|
||||
GP_PAINTMODE_SET_CP
|
||||
} eGPencil_PaintModes;
|
||||
|
||||
/* maximum sizes of gp-session buffer */
|
||||
@ -384,7 +391,7 @@ enum {
|
||||
GP_STROKE_LINE = 1,
|
||||
GP_STROKE_CIRCLE = 2,
|
||||
GP_STROKE_ARC = 3,
|
||||
GP_STROKE_CURVE = 4
|
||||
GP_STROKE_CURVE = 4,
|
||||
};
|
||||
|
||||
enum {
|
||||
@ -397,6 +404,7 @@ void GPENCIL_OT_stroke_change_color(struct wmOperatorType *ot);
|
||||
void GPENCIL_OT_stroke_lock_color(struct wmOperatorType *ot);
|
||||
void GPENCIL_OT_stroke_apply_thickness(struct wmOperatorType *ot);
|
||||
void GPENCIL_OT_stroke_cyclical_set(struct wmOperatorType *ot);
|
||||
void GPENCIL_OT_stroke_caps_set(struct wmOperatorType *ot);
|
||||
void GPENCIL_OT_stroke_join(struct wmOperatorType *ot);
|
||||
void GPENCIL_OT_stroke_flip(struct wmOperatorType *ot);
|
||||
void GPENCIL_OT_stroke_subdivide(struct wmOperatorType *ot);
|
||||
@ -406,6 +414,7 @@ void GPENCIL_OT_stroke_separate(struct wmOperatorType *ot);
|
||||
void GPENCIL_OT_stroke_split(struct wmOperatorType *ot);
|
||||
void GPENCIL_OT_stroke_smooth(struct wmOperatorType *ot);
|
||||
void GPENCIL_OT_stroke_merge(struct wmOperatorType *ot);
|
||||
void GPENCIL_OT_stroke_cutter(struct wmOperatorType *ot);
|
||||
|
||||
void GPENCIL_OT_brush_presets_create(struct wmOperatorType *ot);
|
||||
|
||||
|
@ -194,7 +194,7 @@ static void gpencil_dissolve_points(bContext *C)
|
||||
|
||||
for (gps = gpf->strokes.first; gps; gps = gpsn) {
|
||||
gpsn = gps->next;
|
||||
gp_stroke_delete_tagged_points(gpf, gps, gpsn, GP_SPOINT_TAG, false);
|
||||
gp_stroke_delete_tagged_points(gpf, gps, gpsn, GP_SPOINT_TAG, false, 0);
|
||||
}
|
||||
}
|
||||
CTX_DATA_END;
|
||||
|
@ -228,6 +228,10 @@ void ED_operatortypes_gpencil(void)
|
||||
WM_operatortype_append(GPENCIL_OT_draw);
|
||||
WM_operatortype_append(GPENCIL_OT_fill);
|
||||
|
||||
/* Guides ----------------------- */
|
||||
|
||||
WM_operatortype_append(GPENCIL_OT_guide_rotate);
|
||||
|
||||
/* Editing (Strokes) ------------ */
|
||||
|
||||
WM_operatortype_append(GPENCIL_OT_editmode_toggle);
|
||||
@ -301,6 +305,7 @@ void ED_operatortypes_gpencil(void)
|
||||
WM_operatortype_append(GPENCIL_OT_stroke_lock_color);
|
||||
WM_operatortype_append(GPENCIL_OT_stroke_apply_thickness);
|
||||
WM_operatortype_append(GPENCIL_OT_stroke_cyclical_set);
|
||||
WM_operatortype_append(GPENCIL_OT_stroke_caps_set);
|
||||
WM_operatortype_append(GPENCIL_OT_stroke_join);
|
||||
WM_operatortype_append(GPENCIL_OT_stroke_flip);
|
||||
WM_operatortype_append(GPENCIL_OT_stroke_subdivide);
|
||||
@ -310,6 +315,7 @@ void ED_operatortypes_gpencil(void)
|
||||
WM_operatortype_append(GPENCIL_OT_stroke_split);
|
||||
WM_operatortype_append(GPENCIL_OT_stroke_smooth);
|
||||
WM_operatortype_append(GPENCIL_OT_stroke_merge);
|
||||
WM_operatortype_append(GPENCIL_OT_stroke_cutter);
|
||||
|
||||
WM_operatortype_append(GPENCIL_OT_brush_presets_create);
|
||||
|
||||
|
@ -18,7 +18,7 @@
|
||||
* The Original Code is Copyright (C) 2008, Blender Foundation, Joshua Leung
|
||||
* This is a new part of Blender
|
||||
*
|
||||
* Contributor(s): Joshua Leung, Antonio Vazquez
|
||||
* Contributor(s): Joshua Leung, Antonio Vazquez, Charlie Jolly
|
||||
*
|
||||
* ***** END GPL LICENSE BLOCK *****
|
||||
*/
|
||||
@ -119,9 +119,9 @@ typedef enum eGPencil_PaintFlags {
|
||||
GP_PAINTFLAG_SELECTMASK = (1 << 3),
|
||||
GP_PAINTFLAG_HARD_ERASER = (1 << 4),
|
||||
GP_PAINTFLAG_STROKE_ERASER = (1 << 5),
|
||||
GP_PAINTFLAG_REQ_VECTOR = (1 << 6),
|
||||
} eGPencil_PaintFlags;
|
||||
|
||||
|
||||
/* Temporary 'Stroke' Operation data
|
||||
* "p" = op->customdata
|
||||
*/
|
||||
@ -177,6 +177,8 @@ typedef struct tGPsdata {
|
||||
float mval[2];
|
||||
/** previous recorded mouse-position. */
|
||||
float mvalo[2];
|
||||
/** initial recorded mouse-position */
|
||||
float mvali[2];
|
||||
|
||||
/** current stylus pressure. */
|
||||
float pressure;
|
||||
@ -206,35 +208,45 @@ typedef struct tGPsdata {
|
||||
void *erasercursor;
|
||||
|
||||
/* mat settings are only used for 3D view */
|
||||
/** current material. */
|
||||
/** current material */
|
||||
Material *material;
|
||||
|
||||
/** current drawing brush. */
|
||||
/** current drawing brush */
|
||||
Brush *brush;
|
||||
/** default eraser brush. */
|
||||
/** default eraser brush */
|
||||
Brush *eraser;
|
||||
/** 1: line horizontal, 2: line vertical, other: not defined, second element position. */
|
||||
short straight[2];
|
||||
/** lock drawing to one axis. */
|
||||
|
||||
/** 1: line horizontal, 2: line vertical, other: not defined */
|
||||
short straight;
|
||||
/** lock drawing to one axis */
|
||||
int lock_axis;
|
||||
/** the stroke is no fill mode. */
|
||||
/** the stroke is no fill mode */
|
||||
bool disable_fill;
|
||||
|
||||
RNG *rng;
|
||||
|
||||
/** key used for invoking the operator. */
|
||||
/** key used for invoking the operator */
|
||||
short keymodifier;
|
||||
/** shift modifier flag. */
|
||||
/** shift modifier flag */
|
||||
short shift;
|
||||
|
||||
/** size in pixels for uv calculation. */
|
||||
/** size in pixels for uv calculation */
|
||||
float totpixlen;
|
||||
|
||||
/* guide */
|
||||
/** guide spacing */
|
||||
float guide_spacing;
|
||||
/** half guide spacing */
|
||||
float half_spacing;
|
||||
/** origin */
|
||||
float origin[2];
|
||||
|
||||
ReportList *reports;
|
||||
} tGPsdata;
|
||||
|
||||
/* ------ */
|
||||
|
||||
#define STROKE_HORIZONTAL 1
|
||||
#define STROKE_VERTICAL 2
|
||||
|
||||
/* Macros for accessing sensitivity thresholds... */
|
||||
/* minimum number of pixels mouse should move before new point created */
|
||||
#define MIN_MANHATTEN_PX (U.gp_manhattendist)
|
||||
@ -330,11 +342,11 @@ static void gp_get_3d_reference(tGPsdata *p, float vec[3])
|
||||
|
||||
/* Stroke Editing ---------------------------- */
|
||||
/* check if the current mouse position is suitable for adding a new point */
|
||||
static bool gp_stroke_filtermval(tGPsdata *p, const float mval[2], float pmval[2])
|
||||
static bool gp_stroke_filtermval(tGPsdata *p, const float mval[2], float mvalo[2])
|
||||
{
|
||||
Brush *brush = p->brush;
|
||||
int dx = (int)fabsf(mval[0] - pmval[0]);
|
||||
int dy = (int)fabsf(mval[1] - pmval[1]);
|
||||
int dx = (int)fabsf(mval[0] - mvalo[0]);
|
||||
int dy = (int)fabsf(mval[1] - mvalo[1]);
|
||||
brush->gpencil_settings->flag &= ~GP_BRUSH_STABILIZE_MOUSE_TEMP;
|
||||
|
||||
/* if buffer is empty, just let this go through (i.e. so that dots will work) */
|
||||
@ -422,7 +434,7 @@ static void gp_stroke_convertcoords(tGPsdata *p, const float mval[2], float out[
|
||||
* - nothing more needs to be done here, since view_autodist_simple() has already done it
|
||||
*/
|
||||
|
||||
/* verify valid zdepth, if it's wrong, the default darwing mode is used
|
||||
/* verify valid zdepth, if it's wrong, the default drawing mode is used
|
||||
* and the function doesn't return now */
|
||||
if ((depth == NULL) || (*depth <= 1.0f)) {
|
||||
return;
|
||||
@ -1607,7 +1619,7 @@ static void gp_stroke_eraser_dostroke(tGPsdata *p,
|
||||
gp_stroke_soft_refine(gps, cull_thresh);
|
||||
}
|
||||
|
||||
gp_stroke_delete_tagged_points(gpf, gps, gps->next, GP_SPOINT_TAG, false);
|
||||
gp_stroke_delete_tagged_points(gpf, gps, gps->next, GP_SPOINT_TAG, false, 0);
|
||||
}
|
||||
gp_update_cache(p->gpd);
|
||||
}
|
||||
@ -2019,7 +2031,7 @@ static void gp_session_cleanup(tGPsdata *p)
|
||||
/* free stroke buffer */
|
||||
if (gpd->runtime.sbuffer) {
|
||||
/* printf("\t\tGP - free sbuffer\n"); */
|
||||
MEM_freeN(gpd->runtime.sbuffer);
|
||||
MEM_SAFE_FREE(gpd->runtime.sbuffer);
|
||||
gpd->runtime.sbuffer = NULL;
|
||||
}
|
||||
|
||||
@ -2034,10 +2046,10 @@ static void gp_session_free(tGPsdata *p)
|
||||
if (p->rng != NULL) {
|
||||
BLI_rng_free(p->rng);
|
||||
}
|
||||
|
||||
MEM_freeN(p);
|
||||
}
|
||||
|
||||
|
||||
/* init new stroke */
|
||||
static void gp_paint_initstroke(tGPsdata *p, eGPencil_PaintModes paintmode, Depsgraph *depsgraph)
|
||||
{
|
||||
@ -2472,8 +2484,19 @@ static void gpencil_draw_status_indicators(bContext *C, tGPsdata *p)
|
||||
ED_workspace_status_text(C, IFACE_("Grease Pencil Line Session: Hold and drag LMB to draw | "
|
||||
"ESC/Enter to end (or click outside this area)"));
|
||||
break;
|
||||
case GP_PAINTMODE_SET_CP:
|
||||
ED_workspace_status_text(C, IFACE_("Grease Pencil Guides: LMB click and release to place reference point | "
|
||||
"Esc/RMB to cancel"));
|
||||
break;
|
||||
case GP_PAINTMODE_DRAW:
|
||||
ED_workspace_status_text(C, IFACE_("Grease Pencil Freehand Session: Hold and drag LMB to draw"));
|
||||
GP_Sculpt_Guide *guide = &p->scene->toolsettings->gp_sculpt.guide;
|
||||
if (guide->use_guide) {
|
||||
ED_workspace_status_text(C, IFACE_("Grease Pencil Freehand Session: Hold and drag LMB to draw | "
|
||||
"M key to flip guide | O key to move reference point"));
|
||||
}
|
||||
else {
|
||||
ED_workspace_status_text(C, IFACE_("Grease Pencil Freehand Session: Hold and drag LMB to draw"));
|
||||
}
|
||||
break;
|
||||
case GP_PAINTMODE_DRAW_POLY:
|
||||
ED_workspace_status_text(C, IFACE_("Grease Pencil Poly Session: LMB click to place next stroke vertex | "
|
||||
@ -2510,8 +2533,7 @@ static void gpencil_draw_apply(bContext *C, wmOperator *op, tGPsdata *p, Depsgra
|
||||
gp_stroke_doeraser(p);
|
||||
|
||||
/* store used values */
|
||||
p->mvalo[0] = p->mval[0];
|
||||
p->mvalo[1] = p->mval[1];
|
||||
copy_v2_v2(p->mvalo, p->mval);
|
||||
p->opressure = p->pressure;
|
||||
}
|
||||
/* only add current point to buffer if mouse moved (even though we got an event, it might be just noise) */
|
||||
@ -2560,8 +2582,7 @@ static void gpencil_draw_apply(bContext *C, wmOperator *op, tGPsdata *p, Depsgra
|
||||
}
|
||||
|
||||
/* store used values */
|
||||
p->mvalo[0] = p->mval[0];
|
||||
p->mvalo[1] = p->mval[1];
|
||||
copy_v2_v2(p->mvalo, p->mval);
|
||||
p->opressure = p->pressure;
|
||||
p->ocurtime = p->curtime;
|
||||
|
||||
@ -2580,45 +2601,99 @@ static void gpencil_draw_apply(bContext *C, wmOperator *op, tGPsdata *p, Depsgra
|
||||
}
|
||||
}
|
||||
|
||||
/* Helper to rotate point around origin */
|
||||
static void gp_rotate_v2_v2v2fl(float v[2], const float p[2], const float origin[2], const float angle)
|
||||
{
|
||||
float pt[2];
|
||||
float r[2];
|
||||
sub_v2_v2v2(pt, p, origin);
|
||||
rotate_v2_v2fl(r, pt, angle);
|
||||
add_v2_v2v2(v, r, origin);
|
||||
}
|
||||
|
||||
/* Helper to snap value to grid */
|
||||
static float gp_snap_to_grid_fl(float v, const float offset, const float spacing)
|
||||
{
|
||||
if (spacing > 0.0f)
|
||||
return roundf(v / spacing) * spacing + fmodf(offset, spacing);
|
||||
else
|
||||
return v;
|
||||
}
|
||||
|
||||
static void gp_snap_to_grid_v2(float v[2], const float offset[2], const float spacing)
|
||||
{
|
||||
v[0] = gp_snap_to_grid_fl(v[0], offset[0], spacing);
|
||||
v[1] = gp_snap_to_grid_fl(v[1], offset[1], spacing);
|
||||
}
|
||||
|
||||
/* get reference point - screen coords to buffer coords */
|
||||
static void gp_origin_set(wmOperator *op, const int mval[2])
|
||||
{
|
||||
tGPsdata *p = op->customdata;
|
||||
GP_Sculpt_Guide *guide = &p->scene->toolsettings->gp_sculpt.guide;
|
||||
float origin[2];
|
||||
float point[3];
|
||||
copy_v2fl_v2i(origin, mval);
|
||||
gp_stroke_convertcoords(p, origin, point, NULL);
|
||||
if (guide->reference_point == GP_GUIDE_REF_CUSTOM) {
|
||||
copy_v3_v3(guide->location, point);
|
||||
}
|
||||
else if (guide->reference_point == GP_GUIDE_REF_CURSOR) {
|
||||
copy_v3_v3(p->scene->cursor.location, point);
|
||||
}
|
||||
}
|
||||
|
||||
/* get reference point - buffer coords to screen coords */
|
||||
static void gp_origin_get(tGPsdata *p, float origin[2])
|
||||
{
|
||||
GP_Sculpt_Guide *guide = &p->scene->toolsettings->gp_sculpt.guide;
|
||||
float location[3];
|
||||
if (guide->reference_point == GP_GUIDE_REF_CUSTOM) {
|
||||
copy_v3_v3(location, guide->location);
|
||||
}
|
||||
else if (guide->reference_point == GP_GUIDE_REF_OBJECT &&
|
||||
guide->reference_object != NULL) {
|
||||
copy_v3_v3(location, guide->reference_object->loc);
|
||||
}
|
||||
else {
|
||||
copy_v3_v3(location, p->scene->cursor.location);
|
||||
}
|
||||
GP_SpaceConversion *gsc = &p->gsc;
|
||||
gp_point_3d_to_xy(gsc, p->gpd->runtime.sbuffer_sflag, location, origin);
|
||||
}
|
||||
|
||||
/* handle draw event */
|
||||
static void gpencil_draw_apply_event(bContext *C, wmOperator *op, const wmEvent *event, Depsgraph *depsgraph, float x, float y)
|
||||
{
|
||||
tGPsdata *p = op->customdata;
|
||||
GP_Sculpt_Guide *guide = &p->scene->toolsettings->gp_sculpt.guide;
|
||||
PointerRNA itemptr;
|
||||
float mousef[2];
|
||||
int tablet = 0;
|
||||
|
||||
/* convert from window-space to area-space mouse coordinates
|
||||
* add any x,y override position for fake events
|
||||
* NOTE: float to ints conversions, +1 factor is probably used to ensure a bit more accurate rounding...
|
||||
*/
|
||||
p->mval[0] = event->mval[0] + 1.0f - x;
|
||||
p->mval[1] = event->mval[1] + 1.0f - y;
|
||||
p->mval[0] = (float)event->mval[0] - x;
|
||||
p->mval[1] = (float)event->mval[1] - y;
|
||||
p->shift = event->shift;
|
||||
|
||||
/* verify key status for straight lines */
|
||||
if ((event->alt > 0) && (RNA_boolean_get(op->ptr, "disable_straight") == false)) {
|
||||
if (p->straight[0] == 0) {
|
||||
int dx = (int)fabsf(p->mval[0] - p->mvalo[0]);
|
||||
int dy = (int)fabsf(p->mval[1] - p->mvalo[1]);
|
||||
/* verify direction for straight lines */
|
||||
if ((guide->use_guide) || ((event->alt > 0) && (RNA_boolean_get(op->ptr, "disable_straight") == false))) {
|
||||
if (p->straight == 0) {
|
||||
int dx = (int)fabsf(p->mval[0] - p->mvali[0]);
|
||||
int dy = (int)fabsf(p->mval[1] - p->mvali[1]);
|
||||
if ((dx > 0) || (dy > 0)) {
|
||||
/* check mouse direction to replace the other coordinate with previous values */
|
||||
if (dx >= dy) {
|
||||
/* horizontal */
|
||||
p->straight[0] = 1;
|
||||
p->straight[1] = (short)p->mval[1]; /* save y */
|
||||
/* store mouse direction */
|
||||
if (dx > dy) {
|
||||
p->straight = STROKE_HORIZONTAL;
|
||||
}
|
||||
else {
|
||||
/* vertical */
|
||||
p->straight[0] = 2;
|
||||
p->straight[1] = (short)p->mval[0]; /* save x */
|
||||
else if (dx < dy) {
|
||||
p->straight = STROKE_VERTICAL;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
p->straight[0] = 0;
|
||||
}
|
||||
|
||||
p->curtime = PIL_check_seconds_timer();
|
||||
|
||||
@ -2666,29 +2741,164 @@ static void gpencil_draw_apply_event(bContext *C, wmOperator *op, const wmEvent
|
||||
if (p->flags & GP_PAINTFLAG_FIRSTRUN) {
|
||||
p->flags &= ~GP_PAINTFLAG_FIRSTRUN;
|
||||
|
||||
p->mvalo[0] = p->mval[0];
|
||||
p->mvalo[1] = p->mval[1];
|
||||
/* set values */
|
||||
copy_v2_v2(p->mvalo, p->mval);
|
||||
p->opressure = p->pressure;
|
||||
p->inittime = p->ocurtime = p->curtime;
|
||||
p->straight[0] = 0;
|
||||
p->straight[1] = 0;
|
||||
p->straight = 0;
|
||||
|
||||
/* save initial mouse */
|
||||
copy_v2_v2(p->mvali, p->mval);
|
||||
|
||||
/* calculate once and store snapping distance and origin */
|
||||
RegionView3D * rv3d = p->ar->regiondata;
|
||||
float scale = 1.0f;
|
||||
if (rv3d->is_persp) {
|
||||
float vec[3];
|
||||
gp_get_3d_reference(p, vec);
|
||||
mul_m4_v3(rv3d->persmat, vec);
|
||||
scale = vec[2] * rv3d->pixsize;
|
||||
}
|
||||
else {
|
||||
scale = rv3d->pixsize;
|
||||
}
|
||||
p->guide_spacing = guide->spacing / scale;
|
||||
p->half_spacing = p->guide_spacing * 0.5f;
|
||||
gp_origin_get(p, p->origin);
|
||||
|
||||
/* special exception here for too high pressure values on first touch in
|
||||
* windows for some tablets, then we just skip first touch...
|
||||
*/
|
||||
if (tablet && (p->pressure >= 0.99f))
|
||||
if (tablet && (p->pressure >= 0.99f)) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* special exception for grid snapping
|
||||
* it requires direction which needs at least two points
|
||||
*/
|
||||
if (!ELEM(p->paintmode, GP_PAINTMODE_ERASER, GP_PAINTMODE_SET_CP) &&
|
||||
guide->use_guide &&
|
||||
guide->use_snapping &&
|
||||
(guide->type == GP_GUIDE_GRID)) {
|
||||
p->flags |= GP_PAINTFLAG_REQ_VECTOR;
|
||||
}
|
||||
}
|
||||
|
||||
/* check if alt key is pressed and limit to straight lines */
|
||||
if ((p->paintmode != GP_PAINTMODE_ERASER) && (p->straight[0] != 0)) {
|
||||
if (p->straight[0] == 1) {
|
||||
/* horizontal */
|
||||
p->mval[1] = p->straight[1]; /* replace y */
|
||||
/* wait for vector then add initial point */
|
||||
if (p->flags & GP_PAINTFLAG_REQ_VECTOR) {
|
||||
if (p->straight == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
p->flags &= ~GP_PAINTFLAG_REQ_VECTOR;
|
||||
|
||||
/* create fake events */
|
||||
float tmp[2];
|
||||
float pt[2];
|
||||
copy_v2_v2(tmp, p->mval);
|
||||
sub_v2_v2v2(pt, p->mval, p->mvali);
|
||||
gpencil_draw_apply_event(C, op, event, CTX_data_depsgraph(C), pt[0], pt[1]);
|
||||
if (len_v2v2(p->mval, p->mvalo)) {
|
||||
sub_v2_v2v2(pt, p->mval, p->mvalo);
|
||||
gpencil_draw_apply_event(C, op, event, CTX_data_depsgraph(C), pt[0], pt[1]);
|
||||
}
|
||||
copy_v2_v2(p->mval, tmp);
|
||||
}
|
||||
|
||||
/* check if stroke is straight or guided */
|
||||
if ((p->paintmode != GP_PAINTMODE_ERASER)
|
||||
&& ((p->straight) || (guide->use_guide))) {
|
||||
/* guided stroke */
|
||||
if (guide->use_guide) {
|
||||
switch (guide->type) {
|
||||
default:
|
||||
case GP_GUIDE_CIRCULAR:
|
||||
{
|
||||
float distance;
|
||||
distance = len_v2v2(p->mvali, p->origin);
|
||||
|
||||
if (guide->use_snapping && (guide->spacing > 0.0f)) {
|
||||
distance = gp_snap_to_grid_fl(distance, 0.0f, p->guide_spacing);
|
||||
}
|
||||
|
||||
dist_ensure_v2_v2fl(p->mval, p->origin, distance);
|
||||
}
|
||||
break;
|
||||
case GP_GUIDE_RADIAL:
|
||||
{
|
||||
if (guide->use_snapping &&
|
||||
(guide->angle_snap > 0.0f)) {
|
||||
float point[2];
|
||||
float xy[2];
|
||||
float angle;
|
||||
float half_angle = guide->angle_snap * 0.5f;
|
||||
sub_v2_v2v2(xy, p->mvali, p->origin);
|
||||
angle = atan2f(xy[1], xy[0]);
|
||||
angle += (M_PI * 2.0f);
|
||||
angle = fmodf(angle + half_angle, guide->angle_snap);
|
||||
angle -= half_angle;
|
||||
gp_rotate_v2_v2v2fl(point, p->mvali, p->origin, -angle);
|
||||
closest_to_line_v2(p->mval, p->mval, point, p->origin);
|
||||
}
|
||||
else {
|
||||
closest_to_line_v2(p->mval, p->mval, p->mvali, p->origin);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case GP_GUIDE_PARALLEL:
|
||||
{
|
||||
float point[2];
|
||||
float unit[2];
|
||||
copy_v2_v2(unit, p->mvali);
|
||||
unit[0] += 1.0f; /* start from horizontal */
|
||||
gp_rotate_v2_v2v2fl(point, unit, p->mvali, guide->angle);
|
||||
closest_to_line_v2(p->mval, p->mval, p->mvali, point);
|
||||
|
||||
if (guide->use_snapping &&
|
||||
(guide->spacing > 0.0f)) {
|
||||
gp_rotate_v2_v2v2fl(p->mval, p->mval, p->origin, -guide->angle);
|
||||
p->mval[1] = gp_snap_to_grid_fl(p->mval[1] - p->half_spacing, p->origin[1], p->guide_spacing);
|
||||
gp_rotate_v2_v2v2fl(p->mval, p->mval, p->origin, guide->angle);
|
||||
}
|
||||
|
||||
}
|
||||
break;
|
||||
case GP_GUIDE_GRID:
|
||||
{
|
||||
if (guide->use_snapping &&
|
||||
(guide->spacing > 0.0f)) {
|
||||
|
||||
float point[2];
|
||||
float unit[2];
|
||||
float angle;
|
||||
copy_v2_v2(unit, p->mvali);
|
||||
unit[0] += 1.0f; /* start from horizontal */
|
||||
angle = (p->straight == STROKE_VERTICAL) ? M_PI_2 : 0.0f;
|
||||
gp_rotate_v2_v2v2fl(point, unit, p->mvali, angle);
|
||||
closest_to_line_v2(p->mval, p->mval, p->mvali, point);
|
||||
|
||||
if (p->straight == STROKE_HORIZONTAL) {
|
||||
p->mval[1] = gp_snap_to_grid_fl(p->mval[1] - p->half_spacing, p->origin[1], p->guide_spacing);
|
||||
}
|
||||
else {
|
||||
p->mval[0] = gp_snap_to_grid_fl(p->mval[0] - p->half_spacing, p->origin[0], p->guide_spacing);
|
||||
}
|
||||
}
|
||||
else if (p->straight == STROKE_HORIZONTAL) {
|
||||
p->mval[1] = p->mvali[1]; /* replace y */
|
||||
}
|
||||
else {
|
||||
p->mval[0] = p->mvali[0]; /* replace x */
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
else if (p->straight == STROKE_HORIZONTAL) {
|
||||
p->mval[1] = p->mvali[1]; /* replace y */
|
||||
}
|
||||
else {
|
||||
/* vertical */
|
||||
p->mval[0] = p->straight[1]; /* replace x */
|
||||
p->mval[0] = p->mvali[0]; /* replace x */
|
||||
}
|
||||
}
|
||||
|
||||
@ -2742,8 +2952,8 @@ static int gpencil_draw_exec(bContext *C, wmOperator *op)
|
||||
|
||||
/* get relevant data for this point from stroke */
|
||||
RNA_float_get_array(&itemptr, "mouse", mousef);
|
||||
p->mval[0] = (int)mousef[0];
|
||||
p->mval[1] = (int)mousef[1];
|
||||
p->mval[0] = mousef[0];
|
||||
p->mval[1] = mousef[1];
|
||||
p->pressure = RNA_float_get(&itemptr, "pressure");
|
||||
p->curtime = (double)RNA_float_get(&itemptr, "time") + p->inittime;
|
||||
|
||||
@ -2787,6 +2997,93 @@ static int gpencil_draw_exec(bContext *C, wmOperator *op)
|
||||
|
||||
/* ------------------------------- */
|
||||
|
||||
/* handle events for guides */
|
||||
static void gpencil_guide_event_handling(bContext *C, wmOperator *op, const wmEvent *event, tGPsdata *p)
|
||||
{
|
||||
bool add_notifier = false;
|
||||
GP_Sculpt_Guide *guide = &p->scene->toolsettings->gp_sculpt.guide;
|
||||
|
||||
/* Enter or exit set center point mode */
|
||||
if ((event->type == OKEY) && (event->val == KM_RELEASE)) {
|
||||
if (p->paintmode == GP_PAINTMODE_DRAW && guide->reference_point != GP_GUIDE_REF_OBJECT) {
|
||||
add_notifier = true;
|
||||
p->paintmode = GP_PAINTMODE_SET_CP;
|
||||
ED_gpencil_toggle_brush_cursor(C, false, NULL);
|
||||
}
|
||||
}
|
||||
/* Freehand mode, turn off speed guide */
|
||||
else if ((event->type == VKEY) && (event->val == KM_RELEASE)) {
|
||||
guide->use_guide = false;
|
||||
add_notifier = true;
|
||||
}
|
||||
/* Alternate or flip direction */
|
||||
else if ((event->type == MKEY) && (event->val == KM_RELEASE)) {
|
||||
if (guide->type == GP_GUIDE_CIRCULAR) {
|
||||
add_notifier = true;
|
||||
guide->type = GP_GUIDE_RADIAL;
|
||||
}
|
||||
else if (guide->type == GP_GUIDE_RADIAL) {
|
||||
add_notifier = true;
|
||||
guide->type = GP_GUIDE_CIRCULAR;
|
||||
}
|
||||
else if (guide->type == GP_GUIDE_PARALLEL) {
|
||||
add_notifier = true;
|
||||
guide->angle += M_PI_2;
|
||||
guide->angle = angle_compat_rad(guide->angle, M_PI);
|
||||
}
|
||||
else {
|
||||
add_notifier = false;
|
||||
}
|
||||
}
|
||||
/* Line guides */
|
||||
else if ((event->type == LKEY) && (event->val == KM_RELEASE)) {
|
||||
add_notifier = true;
|
||||
guide->use_guide = true;
|
||||
if (event->ctrl) {
|
||||
guide->angle = 0.0f;
|
||||
guide->type = GP_GUIDE_PARALLEL;
|
||||
}
|
||||
else if (event->alt) {
|
||||
guide->type = GP_GUIDE_PARALLEL;
|
||||
guide->angle = RNA_float_get(op->ptr, "guide_last_angle");
|
||||
}
|
||||
else {
|
||||
guide->type = GP_GUIDE_PARALLEL;
|
||||
}
|
||||
}
|
||||
/* Point guide */
|
||||
else if ((event->type == CKEY) && (event->val == KM_RELEASE)) {
|
||||
add_notifier = true;
|
||||
guide->use_guide = true;
|
||||
if (guide->type == GP_GUIDE_CIRCULAR) {
|
||||
guide->type = GP_GUIDE_RADIAL;
|
||||
}
|
||||
else if (guide->type == GP_GUIDE_RADIAL) {
|
||||
guide->type = GP_GUIDE_CIRCULAR;
|
||||
}
|
||||
else {
|
||||
guide->type = GP_GUIDE_CIRCULAR;
|
||||
}
|
||||
}
|
||||
/* Change line angle */
|
||||
else if (ELEM(event->type, JKEY, KKEY) && (event->val == KM_RELEASE)) {
|
||||
add_notifier = true;
|
||||
float angle = guide->angle;
|
||||
float adjust = (float)M_PI / 180.0f;
|
||||
if (event->alt)
|
||||
adjust *= 45.0f;
|
||||
else if (!event->shift)
|
||||
adjust *= 15.0f;
|
||||
angle += (event->type == JKEY) ? adjust : -adjust;
|
||||
angle = angle_compat_rad(angle, M_PI);
|
||||
guide->angle = angle;
|
||||
}
|
||||
|
||||
if (add_notifier) {
|
||||
WM_event_add_notifier(C, NC_SCENE | ND_TOOLSETTINGS | NC_GPENCIL | NA_EDITED, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
/* start of interactive drawing part of operator */
|
||||
static int gpencil_draw_invoke(bContext *C, wmOperator *op, const wmEvent *event)
|
||||
{
|
||||
@ -2836,7 +3133,7 @@ static int gpencil_draw_invoke(bContext *C, wmOperator *op, const wmEvent *event
|
||||
p->status = GP_STATUS_PAINTING;
|
||||
|
||||
/* handle the initial drawing - i.e. for just doing a simple dot */
|
||||
gpencil_draw_apply_event(C, op, event, CTX_data_depsgraph(C), 0, 0);
|
||||
gpencil_draw_apply_event(C, op, event, CTX_data_depsgraph(C), 0.0f, 0.0f);
|
||||
op->flag |= OP_IS_MODAL_CURSOR_REGION;
|
||||
}
|
||||
else {
|
||||
@ -2847,6 +3144,12 @@ static int gpencil_draw_invoke(bContext *C, wmOperator *op, const wmEvent *event
|
||||
|
||||
/* enable paint mode */
|
||||
if (p->sa->spacetype == SPACE_VIEW3D) {
|
||||
|
||||
/* handle speed guide events before drawing inside view3d */
|
||||
if (!ELEM(p->paintmode, GP_PAINTMODE_ERASER, GP_PAINTMODE_SET_CP)) {
|
||||
gpencil_guide_event_handling(C, op, event, p);
|
||||
}
|
||||
|
||||
Object *ob = CTX_data_active_object(C);
|
||||
if (ob && (ob->type == OB_GPENCIL) && ((p->gpd->flag & GP_DATA_STROKE_PAINTMODE) == 0)) {
|
||||
/* FIXME: use the mode switching operator, this misses notifiers, messages. */
|
||||
@ -2952,12 +3255,21 @@ static void gpencil_move_last_stroke_to_back(bContext *C)
|
||||
static void gpencil_add_missing_events(bContext *C, wmOperator *op, const wmEvent *event, tGPsdata *p)
|
||||
{
|
||||
Brush *brush = p->brush;
|
||||
if (brush->gpencil_settings->input_samples == 0) {
|
||||
GP_Sculpt_Guide *guide = &p->scene->toolsettings->gp_sculpt.guide;
|
||||
int input_samples = brush->gpencil_settings->input_samples;
|
||||
|
||||
/* ensure sampling when using circular guide */
|
||||
if (guide->use_guide && (guide->type == GP_GUIDE_CIRCULAR)) {
|
||||
input_samples = GP_MAX_INPUT_SAMPLES;
|
||||
}
|
||||
|
||||
if (input_samples == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
RegionView3D *rv3d = p->ar->regiondata;
|
||||
float defaultpixsize = rv3d->pixsize * 1000.0f;
|
||||
int samples = (GP_MAX_INPUT_SAMPLES - brush->gpencil_settings->input_samples + 1);
|
||||
int samples = (GP_MAX_INPUT_SAMPLES - input_samples + 1);
|
||||
float thickness = (float)brush->size;
|
||||
|
||||
float pt[2], a[2], b[2];
|
||||
@ -2991,8 +3303,8 @@ static void gpencil_add_missing_events(bContext *C, wmOperator *op, const wmEven
|
||||
float factor = ((thickness * dot_factor) / scale) * samples;
|
||||
|
||||
copy_v2_v2(a, p->mvalo);
|
||||
b[0] = event->mval[0] + 1;
|
||||
b[1] = event->mval[1] + 1;
|
||||
b[0] = (float)event->mval[0] + 1.0f;
|
||||
b[1] = (float)event->mval[1] + 1.0f;
|
||||
|
||||
/* get distance in pixels */
|
||||
float dist = len_v2v2(a, b);
|
||||
@ -3003,7 +3315,7 @@ static void gpencil_add_missing_events(bContext *C, wmOperator *op, const wmEven
|
||||
sub_v2_v2v2(pt, b, pt);
|
||||
/* create fake event */
|
||||
gpencil_draw_apply_event(C, op, event, CTX_data_depsgraph(C),
|
||||
(int)pt[0], (int)pt[1]);
|
||||
pt[0], pt[1]);
|
||||
}
|
||||
else if (dist >= factor) {
|
||||
int slices = 2 + (int)((dist - 1.0) / factor);
|
||||
@ -3013,7 +3325,7 @@ static void gpencil_add_missing_events(bContext *C, wmOperator *op, const wmEven
|
||||
sub_v2_v2v2(pt, b, pt);
|
||||
/* create fake event */
|
||||
gpencil_draw_apply_event(C, op, event, CTX_data_depsgraph(C),
|
||||
(int)pt[0], (int)pt[1]);
|
||||
pt[0], pt[1]);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -3023,6 +3335,7 @@ static int gpencil_draw_modal(bContext *C, wmOperator *op, const wmEvent *event)
|
||||
{
|
||||
tGPsdata *p = op->customdata;
|
||||
ToolSettings *ts = CTX_data_tool_settings(C);
|
||||
GP_Sculpt_Guide *guide = &p->scene->toolsettings->gp_sculpt.guide;
|
||||
/* default exit state - pass through to support MMB view nav, etc. */
|
||||
int estate = OPERATOR_PASS_THROUGH;
|
||||
|
||||
@ -3044,6 +3357,43 @@ static int gpencil_draw_modal(bContext *C, wmOperator *op, const wmEvent *event)
|
||||
p->ar = ar;
|
||||
}
|
||||
|
||||
/* special mode for editing control points */
|
||||
if (p->paintmode == GP_PAINTMODE_SET_CP) {
|
||||
wmWindow *win = p->win;
|
||||
WM_cursor_modal_set(win, BC_NSEW_SCROLLCURSOR);
|
||||
bool drawmode = false;
|
||||
|
||||
switch (event->type) {
|
||||
/* cancel */
|
||||
case ESCKEY:
|
||||
case RIGHTMOUSE:
|
||||
{
|
||||
if (ELEM(event->val, KM_RELEASE)) {
|
||||
drawmode = true;
|
||||
}
|
||||
}
|
||||
break;
|
||||
/* set */
|
||||
case LEFTMOUSE:
|
||||
{
|
||||
if (ELEM(event->val, KM_RELEASE)) {
|
||||
gp_origin_set(op, event->mval);
|
||||
drawmode = true;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
if (drawmode) {
|
||||
p->status = GP_STATUS_IDLING;
|
||||
p->paintmode = GP_PAINTMODE_DRAW;
|
||||
ED_gpencil_toggle_brush_cursor(C, true, NULL);
|
||||
DEG_id_tag_update(&p->scene->id, ID_RECALC_COPY_ON_WRITE);
|
||||
}
|
||||
else {
|
||||
return OPERATOR_RUNNING_MODAL;
|
||||
}
|
||||
}
|
||||
|
||||
/* we don't pass on key events, GP is used with key-modifiers - prevents Dkey to insert drivers */
|
||||
if (ISKEYBOARD(event->type)) {
|
||||
if (ELEM(event->type, LEFTARROWKEY, DOWNARROWKEY, RIGHTARROWKEY, UPARROWKEY, ZKEY)) {
|
||||
@ -3067,6 +3417,10 @@ static int gpencil_draw_modal(bContext *C, wmOperator *op, const wmEvent *event)
|
||||
WM_operator_name_call(C, "GPENCIL_OT_blank_frame_add", WM_OP_EXEC_DEFAULT, NULL);
|
||||
estate = OPERATOR_RUNNING_MODAL;
|
||||
}
|
||||
else if ((!ELEM(p->paintmode, GP_PAINTMODE_ERASER, GP_PAINTMODE_SET_CP))) {
|
||||
gpencil_guide_event_handling(C, op, event, p);
|
||||
estate = OPERATOR_RUNNING_MODAL;
|
||||
}
|
||||
else {
|
||||
estate = OPERATOR_RUNNING_MODAL;
|
||||
}
|
||||
@ -3091,6 +3445,7 @@ static int gpencil_draw_modal(bContext *C, wmOperator *op, const wmEvent *event)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
p->status = GP_STATUS_DONE;
|
||||
estate = OPERATOR_FINISHED;
|
||||
}
|
||||
@ -3251,6 +3606,7 @@ static int gpencil_draw_modal(bContext *C, wmOperator *op, const wmEvent *event)
|
||||
else if (event->val == KM_RELEASE) {
|
||||
p->status = GP_STATUS_IDLING;
|
||||
op->flag |= OP_IS_MODAL_CURSOR_REGION;
|
||||
ED_region_tag_redraw(p->ar);
|
||||
}
|
||||
}
|
||||
|
||||
@ -3260,9 +3616,10 @@ static int gpencil_draw_modal(bContext *C, wmOperator *op, const wmEvent *event)
|
||||
if (ELEM(event->type, MOUSEMOVE, INBETWEEN_MOUSEMOVE) || (p->flags & GP_PAINTFLAG_FIRSTRUN)) {
|
||||
/* handle drawing event */
|
||||
/* printf("\t\tGP - add point\n"); */
|
||||
|
||||
gpencil_add_missing_events(C, op, event, p);
|
||||
|
||||
gpencil_draw_apply_event(C, op, event, CTX_data_depsgraph(C), 0, 0);
|
||||
gpencil_draw_apply_event(C, op, event, CTX_data_depsgraph(C), 0.0f, 0.0f);
|
||||
|
||||
/* finish painting operation if anything went wrong just now */
|
||||
if (p->status == GP_STATUS_ERROR) {
|
||||
@ -3326,6 +3683,13 @@ static int gpencil_draw_modal(bContext *C, wmOperator *op, const wmEvent *event)
|
||||
/* process last operations before exiting */
|
||||
switch (estate) {
|
||||
case OPERATOR_FINISHED:
|
||||
/* store stroke angle for parallel guide */
|
||||
if ((p->straight == 0) || (guide->use_guide && (guide->type == GP_GUIDE_CIRCULAR))){
|
||||
float xy[2];
|
||||
sub_v2_v2v2(xy, p->mval, p->mvali);
|
||||
float angle = atan2f(xy[1], xy[0]);
|
||||
RNA_float_set(op->ptr, "guide_last_angle", angle);
|
||||
}
|
||||
/* one last flush before we're done */
|
||||
gpencil_draw_exit(C, op);
|
||||
WM_event_add_notifier(C, NC_GPENCIL | NA_EDITED, NULL);
|
||||
@ -3392,4 +3756,49 @@ void GPENCIL_OT_draw(wmOperatorType *ot)
|
||||
|
||||
prop = RNA_def_boolean(ot->srna, "disable_fill", false, "No Fill Areas", "Disable fill to use stroke as fill boundary");
|
||||
RNA_def_property_flag(prop, PROP_SKIP_SAVE);
|
||||
|
||||
/* guides */
|
||||
prop = RNA_def_float(ot->srna, "guide_last_angle", 0.0f, -10000.0f, 10000.0f, "Angle", "Speed guide angle", -10000.0f, 10000.0f);
|
||||
prop = RNA_def_float_vector(ot->srna, "guide_origin", 3, NULL, -10000.0f, 10000.0f, "Origin", "Speed guide origin", -10000.0f, 10000.0f);
|
||||
}
|
||||
|
||||
/* additional OPs */
|
||||
|
||||
static int gpencil_guide_rotate(bContext *C, wmOperator *op)
|
||||
{
|
||||
ToolSettings *ts = CTX_data_tool_settings(C);
|
||||
GP_Sculpt_Guide *guide = &ts->gp_sculpt.guide;
|
||||
float angle = RNA_float_get(op->ptr, "angle");
|
||||
bool increment = RNA_boolean_get(op->ptr, "increment");
|
||||
if (increment) {
|
||||
float oldangle = guide->angle;
|
||||
oldangle += angle;
|
||||
guide->angle = angle_compat_rad(oldangle, M_PI);
|
||||
}
|
||||
else {
|
||||
guide->angle = angle_compat_rad(angle, M_PI);
|
||||
}
|
||||
|
||||
return OPERATOR_FINISHED;
|
||||
}
|
||||
|
||||
void GPENCIL_OT_guide_rotate(wmOperatorType *ot)
|
||||
{
|
||||
/* identifiers */
|
||||
ot->name = "Rotate Guide Angle";
|
||||
ot->idname = "GPENCIL_OT_guide_rotate";
|
||||
ot->description = "Rotate guide angle";
|
||||
|
||||
/* api callbacks */
|
||||
ot->exec = gpencil_guide_rotate;
|
||||
|
||||
/* flags */
|
||||
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
|
||||
|
||||
PropertyRNA *prop;
|
||||
|
||||
prop = RNA_def_boolean(ot->srna, "increment", true, "Increment", "Increment angle");
|
||||
RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE);
|
||||
prop = RNA_def_float(ot->srna, "angle", 0.0f, -10000.0f, 10000.0f, "Angle", "Guide angle", -10000.0f, 10000.0f);
|
||||
RNA_def_property_flag(prop, PROP_HIDDEN);
|
||||
}
|
||||
|
@ -18,7 +18,7 @@
|
||||
* The Original Code is Copyright (C) 2017, Blender Foundation
|
||||
* This is a new part of Blender
|
||||
*
|
||||
* Contributor(s): Antonio Vazquez
|
||||
* Contributor(s): Antonio Vazquez, Charlie Jolly
|
||||
*
|
||||
* ***** END GPL LICENSE BLOCK *****
|
||||
*
|
||||
@ -248,6 +248,15 @@ static void gp_primitive_update_cps(tGPDprimitive *tgpi)
|
||||
}
|
||||
}
|
||||
|
||||
/* Helper to reflect point */
|
||||
static void gp_reflect_point_v2_v2v2v2(float va[2], const float p[2], const float a[2], const float b[2])
|
||||
{
|
||||
float point[2];
|
||||
closest_to_line_v2(point, p, a, b);
|
||||
va[0] = point[0] - (p[0] - point[0]);
|
||||
va[1] = point[1] - (p[1] - point[1]);
|
||||
}
|
||||
|
||||
/* Poll callback for primitive operators */
|
||||
static bool gpencil_primitive_add_poll(bContext *C)
|
||||
{
|
||||
@ -677,7 +686,8 @@ static void gp_primitive_update_strokes(bContext *C, tGPDprimitive *tgpi)
|
||||
/* compute screen-space coordinates for points */
|
||||
tGPspoint *points2D = tgpi->points;
|
||||
|
||||
switch (tgpi->type) {
|
||||
if (tgpi->tot_edges > 1) {
|
||||
switch (tgpi->type) {
|
||||
case GP_STROKE_BOX:
|
||||
gp_primitive_rectangle(tgpi, points2D);
|
||||
break;
|
||||
@ -694,6 +704,7 @@ static void gp_primitive_update_strokes(bContext *C, tGPDprimitive *tgpi)
|
||||
gp_primitive_bezier(tgpi, points2D);
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* convert screen-coordinates to 3D coordinates */
|
||||
@ -1321,7 +1332,7 @@ static void gpencil_primitive_edit_event_handling(bContext *C, wmOperator *op, w
|
||||
{
|
||||
if ((event->val == KM_PRESS) &&
|
||||
(tgpi->curve) &&
|
||||
(tgpi->orign_type == GP_STROKE_ARC))
|
||||
(ELEM(tgpi->orign_type, GP_STROKE_ARC) ))
|
||||
{
|
||||
tgpi->flip ^= 1;
|
||||
gp_primitive_update_cps(tgpi);
|
||||
@ -1432,22 +1443,22 @@ static int gpencil_primitive_modal(bContext *C, wmOperator *op, const wmEvent *e
|
||||
if (tgpi->flag == IN_MOVE) {
|
||||
|
||||
switch (event->type) {
|
||||
case MOUSEMOVE:
|
||||
gpencil_primitive_move(tgpi, false);
|
||||
gpencil_primitive_update(C, op, tgpi);
|
||||
break;
|
||||
case ESCKEY:
|
||||
case LEFTMOUSE:
|
||||
zero_v2(tgpi->move);
|
||||
case MOUSEMOVE:
|
||||
gpencil_primitive_move(tgpi, false);
|
||||
gpencil_primitive_update(C, op, tgpi);
|
||||
break;
|
||||
case ESCKEY:
|
||||
case LEFTMOUSE:
|
||||
zero_v2(tgpi->move);
|
||||
tgpi->flag = IN_CURVE_EDIT;
|
||||
break;
|
||||
case RIGHTMOUSE:
|
||||
if (event->val == KM_RELEASE) {
|
||||
tgpi->flag = IN_CURVE_EDIT;
|
||||
break;
|
||||
case RIGHTMOUSE:
|
||||
if (event->val == KM_RELEASE) {
|
||||
tgpi->flag = IN_CURVE_EDIT;
|
||||
gpencil_primitive_move(tgpi, true);
|
||||
gpencil_primitive_update(C, op, tgpi);
|
||||
}
|
||||
break;
|
||||
gpencil_primitive_move(tgpi, true);
|
||||
gpencil_primitive_update(C, op, tgpi);
|
||||
}
|
||||
break;
|
||||
}
|
||||
copy_v2_v2(tgpi->mvalo, tgpi->mval);
|
||||
return OPERATOR_RUNNING_MODAL;
|
||||
@ -1543,7 +1554,19 @@ static int gpencil_primitive_modal(bContext *C, wmOperator *op, const wmEvent *e
|
||||
/* done! */
|
||||
return OPERATOR_FINISHED;
|
||||
}
|
||||
case RIGHTMOUSE: /* cancel */
|
||||
case RIGHTMOUSE:
|
||||
{
|
||||
/* exception to cancel current stroke when we have previous strokes in buffer */
|
||||
if (tgpi->tot_stored_edges > 0) {
|
||||
tgpi->flag = IDLE;
|
||||
tgpi->tot_edges = 0;
|
||||
gp_primitive_update_strokes(C, tgpi);
|
||||
gpencil_primitive_interaction_end(C, op, win, tgpi);
|
||||
/* done! */
|
||||
return OPERATOR_FINISHED;
|
||||
}
|
||||
ATTR_FALLTHROUGH;
|
||||
}
|
||||
case ESCKEY:
|
||||
{
|
||||
/* return to normal cursor and header status */
|
||||
|
@ -861,11 +861,14 @@ void GPENCIL_OT_select_less(wmOperatorType *ot)
|
||||
* It would be great to de-duplicate the logic here sometime, but that can wait...
|
||||
*/
|
||||
static bool gp_stroke_do_circle_sel(
|
||||
bGPDlayer *gpl,
|
||||
bGPDstroke *gps, GP_SpaceConversion *gsc,
|
||||
const int mx, const int my, const int radius,
|
||||
const bool select, rcti *rect, float diff_mat[4][4], const int selectmode)
|
||||
const bool select, rcti *rect, float diff_mat[4][4], const int selectmode,
|
||||
const float scale)
|
||||
{
|
||||
bGPDspoint *pt1, *pt2;
|
||||
bGPDspoint *pt1 = NULL;
|
||||
bGPDspoint *pt2 = NULL;
|
||||
int x0 = 0, y0 = 0, x1 = 0, y1 = 0;
|
||||
int i;
|
||||
bool changed = false;
|
||||
@ -958,6 +961,14 @@ static bool gp_stroke_do_circle_sel(
|
||||
}
|
||||
}
|
||||
|
||||
/* expand selection to segment */
|
||||
if ((hit) && (selectmode == GP_SELECTMODE_SEGMENT) && (select)) {
|
||||
float r_hita[3], r_hitb[3];
|
||||
bool hit_select = (bool)(pt1->flag & GP_SPOINT_SELECT);
|
||||
ED_gpencil_select_stroke_segment(
|
||||
gpl, gps, pt1, hit_select, false, scale, r_hita, r_hitb);
|
||||
}
|
||||
|
||||
/* Ensure that stroke selection is in sync with its points */
|
||||
BKE_gpencil_stroke_sync_selection(gps);
|
||||
}
|
||||
@ -971,6 +982,7 @@ static int gpencil_circle_select_exec(bContext *C, wmOperator *op)
|
||||
bGPdata *gpd = ED_gpencil_data_get_active(C);
|
||||
ToolSettings *ts = CTX_data_tool_settings(C);
|
||||
const int selectmode = ts->gpencil_selectmode;
|
||||
const float scale = ts->gp_sculpt.isect_threshold;
|
||||
|
||||
/* if not edit/sculpt mode, the event is catched but not processed */
|
||||
if (GPENCIL_NONE_EDIT_MODE(gpd)) {
|
||||
@ -1012,7 +1024,8 @@ static int gpencil_circle_select_exec(bContext *C, wmOperator *op)
|
||||
GP_EDITABLE_STROKES_BEGIN(gpstroke_iter, C, gpl, gps)
|
||||
{
|
||||
changed |= gp_stroke_do_circle_sel(
|
||||
gps, &gsc, mx, my, radius, select, &rect, gpstroke_iter.diff_mat, selectmode);
|
||||
gpl, gps, &gsc, mx, my, radius, select, &rect,
|
||||
gpstroke_iter.diff_mat, selectmode, scale);
|
||||
}
|
||||
GP_EDITABLE_STROKES_END(gpstroke_iter);
|
||||
|
||||
@ -1074,7 +1087,11 @@ static int gpencil_generic_select_exec(
|
||||
const bool strokemode = (
|
||||
(ts->gpencil_selectmode == GP_SELECTMODE_STROKE) &&
|
||||
((gpd->flag & GP_DATA_STROKE_PAINTMODE) == 0));
|
||||
const bool segmentmode = (
|
||||
(ts->gpencil_selectmode == GP_SELECTMODE_SEGMENT) &&
|
||||
((gpd->flag & GP_DATA_STROKE_PAINTMODE) == 0));
|
||||
const eSelectOp sel_op = RNA_enum_get(op->ptr, "mode");
|
||||
const float scale = ts->gp_sculpt.isect_threshold;
|
||||
|
||||
|
||||
GP_SpaceConversion gsc = {NULL};
|
||||
@ -1124,6 +1141,17 @@ static int gpencil_generic_select_exec(
|
||||
if (sel_op_result != -1) {
|
||||
SET_FLAG_FROM_TEST(pt->flag, sel_op_result, GP_SPOINT_SELECT);
|
||||
changed = true;
|
||||
|
||||
/* expand selection to segment */
|
||||
if ((sel_op_result != -1) &&
|
||||
(segmentmode))
|
||||
{
|
||||
bool hit_select = (bool)(pt->flag & GP_SPOINT_SELECT);
|
||||
float r_hita[3], r_hitb[3];
|
||||
ED_gpencil_select_stroke_segment(
|
||||
gpl, gps, pt, hit_select, false, scale, r_hita, r_hitb);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
else {
|
||||
@ -1336,6 +1364,7 @@ static int gpencil_select_exec(bContext *C, wmOperator *op)
|
||||
ScrArea *sa = CTX_wm_area(C);
|
||||
bGPdata *gpd = ED_gpencil_data_get_active(C);
|
||||
ToolSettings *ts = CTX_data_tool_settings(C);
|
||||
const float scale = ts->gp_sculpt.isect_threshold;
|
||||
|
||||
/* "radius" is simply a threshold (screen space) to make it easier to test with a tolerance */
|
||||
const float radius = 0.50f * U.widget_unit;
|
||||
@ -1350,6 +1379,7 @@ static int gpencil_select_exec(bContext *C, wmOperator *op)
|
||||
|
||||
GP_SpaceConversion gsc = {NULL};
|
||||
|
||||
bGPDlayer *hit_layer = NULL;
|
||||
bGPDstroke *hit_stroke = NULL;
|
||||
bGPDspoint *hit_point = NULL;
|
||||
int hit_distance = radius_squared;
|
||||
@ -1394,6 +1424,7 @@ static int gpencil_select_exec(bContext *C, wmOperator *op)
|
||||
if (pt_distance <= radius_squared) {
|
||||
/* only use this point if it is a better match than the current hit - T44685 */
|
||||
if (pt_distance < hit_distance) {
|
||||
hit_layer = gpl;
|
||||
hit_stroke = gps;
|
||||
hit_point = pt;
|
||||
hit_distance = pt_distance;
|
||||
@ -1454,6 +1485,15 @@ static int gpencil_select_exec(bContext *C, wmOperator *op)
|
||||
/* we're adding selection, so selection must be true */
|
||||
hit_point->flag |= GP_SPOINT_SELECT;
|
||||
hit_stroke->flag |= GP_STROKE_SELECT;
|
||||
|
||||
/* expand selection to segment */
|
||||
if (ts->gpencil_selectmode == GP_SELECTMODE_SEGMENT) {
|
||||
float r_hita[3], r_hitb[3];
|
||||
bool hit_select = (bool)(hit_point->flag & GP_SPOINT_SELECT);
|
||||
ED_gpencil_select_stroke_segment(
|
||||
hit_layer, hit_stroke, hit_point, hit_select,
|
||||
false, scale, r_hita, r_hitb);
|
||||
}
|
||||
}
|
||||
else {
|
||||
/* deselect point */
|
||||
|
@ -36,6 +36,7 @@
|
||||
|
||||
#include "BLI_math.h"
|
||||
#include "BLI_blenlib.h"
|
||||
#include "BLI_ghash.h"
|
||||
#include "BLI_utildefines.h"
|
||||
#include "BLT_translation.h"
|
||||
#include "BLI_rand.h"
|
||||
@ -723,6 +724,62 @@ void gp_point_to_xy_fl(
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* generic based on gp_point_to_xy_fl
|
||||
*/
|
||||
void gp_point_3d_to_xy(const GP_SpaceConversion *gsc, const short flag, const float pt[3], float xy[2])
|
||||
{
|
||||
const ARegion *ar = gsc->ar;
|
||||
const View2D *v2d = gsc->v2d;
|
||||
const rctf *subrect = gsc->subrect;
|
||||
float xyval[2];
|
||||
|
||||
/* sanity checks */
|
||||
BLI_assert((gsc->sa->spacetype == SPACE_VIEW3D));
|
||||
|
||||
if (flag & GP_STROKE_3DSPACE) {
|
||||
if (ED_view3d_project_float_global(ar, pt, xyval, V3D_PROJ_TEST_NOP) == V3D_PROJ_RET_OK) {
|
||||
xy[0] = xyval[0];
|
||||
xy[1] = xyval[1];
|
||||
}
|
||||
else {
|
||||
xy[0] = 0.0f;
|
||||
xy[1] = 0.0f;
|
||||
}
|
||||
}
|
||||
else if (flag & GP_STROKE_2DSPACE) {
|
||||
float vec[3] = { pt[0], pt[1], 0.0f };
|
||||
int t_x, t_y;
|
||||
|
||||
mul_m4_v3(gsc->mat, vec);
|
||||
UI_view2d_view_to_region_clip(v2d, vec[0], vec[1], &t_x, &t_y);
|
||||
|
||||
if ((t_x == t_y) && (t_x == V2D_IS_CLIPPED)) {
|
||||
/* XXX: Or should we just always use the values as-is? */
|
||||
xy[0] = 0.0f;
|
||||
xy[1] = 0.0f;
|
||||
}
|
||||
else {
|
||||
xy[0] = (float)t_x;
|
||||
xy[1] = (float)t_y;
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (subrect == NULL) {
|
||||
/* normal 3D view (or view space) */
|
||||
xy[0] = (pt[0] / 100.0f * ar->winx);
|
||||
xy[1] = (pt[1] / 100.0f * ar->winy);
|
||||
}
|
||||
else {
|
||||
/* camera view, use subrect */
|
||||
xy[0] = ((pt[0] / 100.0f) * BLI_rctf_size_x(subrect)) + subrect->xmin;
|
||||
xy[1] = ((pt[1] / 100.0f) * BLI_rctf_size_y(subrect)) + subrect->ymin;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Project screenspace coordinates to 3D-space
|
||||
*
|
||||
@ -738,7 +795,7 @@ void gp_point_to_xy_fl(
|
||||
*
|
||||
* \warning Assumes that it is getting called in a 3D view only.
|
||||
*/
|
||||
bool gp_point_xy_to_3d(GP_SpaceConversion *gsc, Scene *scene, const float screen_co[2], float r_out[3])
|
||||
bool gp_point_xy_to_3d(const GP_SpaceConversion *gsc, Scene *scene, const float screen_co[2], float r_out[3])
|
||||
{
|
||||
const RegionView3D *rv3d = gsc->ar->regiondata;
|
||||
float rvec[3];
|
||||
@ -1946,4 +2003,347 @@ void ED_gpencil_update_color_uv(Main *bmain, Material *mat)
|
||||
}
|
||||
}
|
||||
}
|
||||
/* ******************************************************** */
|
||||
|
||||
static bool gpencil_check_collision(
|
||||
bGPDstroke *gps, bGPDstroke **gps_array, GHash *all_2d,
|
||||
int totstrokes, float p2d_a1[2], float p2d_a2[2], float r_hit[2])
|
||||
{
|
||||
bool hit = false;
|
||||
/* check segment with all segments of all strokes */
|
||||
for (int s = 0; s < totstrokes; s++) {
|
||||
bGPDstroke *gps_iter = gps_array[s];
|
||||
if (gps_iter->totpoints < 2) {
|
||||
continue;
|
||||
}
|
||||
/* get stroke 2d version */
|
||||
float(*points2d)[2] = BLI_ghash_lookup(all_2d, gps_iter);
|
||||
|
||||
for (int i2 = 0; i2 < gps_iter->totpoints - 1; i2++) {
|
||||
float p2d_b1[2], p2d_b2[2];
|
||||
copy_v2_v2(p2d_b1, points2d[i2]);
|
||||
copy_v2_v2(p2d_b2, points2d[i2 + 1]);
|
||||
|
||||
/* don't self check */
|
||||
if (gps == gps_iter) {
|
||||
if (equals_v2v2(p2d_a1, p2d_b1) || equals_v2v2(p2d_a1, p2d_b2)) {
|
||||
continue;
|
||||
}
|
||||
if (equals_v2v2(p2d_a2, p2d_b1) || equals_v2v2(p2d_a2, p2d_b2)) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
/* check collision */
|
||||
int check = isect_seg_seg_v2_point(p2d_a1, p2d_a2, p2d_b1, p2d_b2, r_hit);
|
||||
if (check > 0) {
|
||||
hit = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (hit) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!hit) {
|
||||
zero_v2(r_hit);
|
||||
}
|
||||
|
||||
return hit;
|
||||
}
|
||||
|
||||
void static gp_copy_points(
|
||||
bGPDstroke *gps, bGPDspoint *pt, bGPDspoint *pt_final, int i, int i2)
|
||||
{
|
||||
copy_v3_v3(&pt_final->x, &pt->x);
|
||||
pt_final->pressure = pt->pressure;
|
||||
pt_final->strength = pt->strength;
|
||||
pt_final->time = pt->time;
|
||||
pt_final->flag = pt->flag;
|
||||
pt_final->uv_fac = pt->uv_fac;
|
||||
pt_final->uv_rot = pt->uv_rot;
|
||||
|
||||
if (gps->dvert != NULL) {
|
||||
MDeformVert *dvert = &gps->dvert[i];
|
||||
MDeformVert *dvert_final = &gps->dvert[i2];
|
||||
|
||||
dvert_final->totweight = dvert->totweight;
|
||||
dvert_final->dw = dvert->dw;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void static gp_insert_point(
|
||||
bGPDstroke *gps,
|
||||
bGPDspoint *a_pt, bGPDspoint *b_pt,
|
||||
float co_a[3], float co_b[3])
|
||||
{
|
||||
bGPDspoint *temp_points;
|
||||
int totnewpoints, oldtotpoints;
|
||||
|
||||
totnewpoints = gps->totpoints;
|
||||
if (a_pt) {
|
||||
totnewpoints++;
|
||||
}
|
||||
if (b_pt) {
|
||||
totnewpoints++;
|
||||
}
|
||||
|
||||
/* duplicate points in a temp area */
|
||||
temp_points = MEM_dupallocN(gps->points);
|
||||
oldtotpoints = gps->totpoints;
|
||||
|
||||
/* look index of base points because memory is changed when resize points array */
|
||||
int a_idx = -1;
|
||||
int b_idx = -1;
|
||||
for (int i = 0; i < oldtotpoints; i++) {
|
||||
bGPDspoint *pt = &gps->points[i];
|
||||
if (pt == a_pt) {
|
||||
a_idx = i;
|
||||
}
|
||||
if (pt == b_pt) {
|
||||
b_idx = i;
|
||||
}
|
||||
}
|
||||
|
||||
/* resize the points arrays */
|
||||
gps->totpoints = totnewpoints;
|
||||
gps->points = MEM_recallocN(gps->points, sizeof(*gps->points) * gps->totpoints);
|
||||
if (gps->dvert != NULL) {
|
||||
gps->dvert = MEM_recallocN(gps->dvert, sizeof(*gps->dvert) * gps->totpoints);
|
||||
}
|
||||
gps->flag |= GP_STROKE_RECALC_GEOMETRY;
|
||||
|
||||
/* copy all points */
|
||||
int i2 = 0;
|
||||
for (int i = 0; i < oldtotpoints; i++) {
|
||||
bGPDspoint *pt = &temp_points[i];
|
||||
bGPDspoint *pt_final = &gps->points[i2];
|
||||
gp_copy_points(gps, pt, pt_final, i, i2);
|
||||
|
||||
/* create new point duplicating point and copy location */
|
||||
if ((i == a_idx) || (i == b_idx)) {
|
||||
i2++;
|
||||
pt_final = &gps->points[i2];
|
||||
gp_copy_points(gps, pt, pt_final, i, i2);
|
||||
copy_v3_v3(&pt_final->x, (i == a_idx) ? co_a : co_b);
|
||||
|
||||
/* unselect */
|
||||
pt_final->flag &= ~GP_SPOINT_SELECT;
|
||||
/* tag to avoid more checking with this point */
|
||||
pt_final->flag |= GP_SPOINT_TAG;
|
||||
}
|
||||
|
||||
i2++;
|
||||
}
|
||||
|
||||
MEM_SAFE_FREE(temp_points);
|
||||
}
|
||||
|
||||
static float gp_calc_factor(float p2d_a1[2], float p2d_a2[2], float r_hit2d[2])
|
||||
{
|
||||
float dist1 = len_squared_v2v2(p2d_a1, p2d_a2);
|
||||
float dist2 = len_squared_v2v2(p2d_a1, r_hit2d);
|
||||
float f = dist1 > 0.0f ? dist2 / dist1 : 0.0f;
|
||||
|
||||
/* apply a correction factor */
|
||||
float v1[2];
|
||||
interp_v2_v2v2(v1, p2d_a1, p2d_a2, f);
|
||||
float dist3 = len_squared_v2v2(p2d_a1, v1);
|
||||
float f1 = dist1 > 0.0f ? dist3 / dist1 : 0.0f;
|
||||
f = f + (f - f1);
|
||||
|
||||
return f;
|
||||
}
|
||||
|
||||
/* extend selection to stroke intersections */
|
||||
int ED_gpencil_select_stroke_segment(
|
||||
bGPDlayer *gpl, bGPDstroke *gps, bGPDspoint *pt,
|
||||
bool select, bool insert, const float scale,
|
||||
float r_hita[3], float r_hitb[3])
|
||||
{
|
||||
const float min_factor = 0.0015f;
|
||||
bGPDspoint *pta1 = NULL;
|
||||
bGPDspoint *pta2 = NULL;
|
||||
float f = 0.0f;
|
||||
int i2 = 0;
|
||||
|
||||
bGPDframe *gpf = gpl->actframe;
|
||||
if (gpf == NULL) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int memsize = BLI_listbase_count(&gpf->strokes);
|
||||
bGPDstroke **gps_array = MEM_callocN(sizeof(bGPDstroke *) * memsize, __func__);
|
||||
|
||||
/* save points */
|
||||
bGPDspoint *oldpoints = MEM_dupallocN(gps->points);
|
||||
|
||||
/* Save list of strokes to check */
|
||||
int totstrokes = 0;
|
||||
for (bGPDstroke *gps_iter = gpf->strokes.first; gps_iter; gps_iter = gps_iter->next) {
|
||||
|
||||
if (gps_iter->totpoints < 2) {
|
||||
continue;
|
||||
}
|
||||
gps_array[totstrokes] = gps_iter;
|
||||
totstrokes++;
|
||||
}
|
||||
|
||||
if (totstrokes == 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* look for index of the current point */
|
||||
int cur_idx = -1;
|
||||
for (int i = 0; i < gps->totpoints; i++) {
|
||||
pta1 = &gps->points[i];
|
||||
if (pta1 == pt) {
|
||||
cur_idx = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (cur_idx < 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* convert all gps points to 2d and save in a hash to avoid recalculation */
|
||||
int direction = 0;
|
||||
float(*points2d)[2] = MEM_mallocN(sizeof(*points2d) * gps->totpoints, "GP Stroke temp 2d points");
|
||||
BKE_gpencil_stroke_2d_flat_ref(
|
||||
gps->points, gps->totpoints,
|
||||
gps->points, gps->totpoints, points2d, scale, &direction);
|
||||
|
||||
GHash *all_2d = BLI_ghash_ptr_new(__func__);
|
||||
|
||||
for (int s = 0; s < totstrokes; s++) {
|
||||
bGPDstroke *gps_iter = gps_array[s];
|
||||
float(*points2d_iter)[2] = MEM_mallocN(sizeof(*points2d_iter) * gps_iter->totpoints, __func__);
|
||||
|
||||
/* the extremes of the stroke are scaled to improve collision detection
|
||||
* for near lines */
|
||||
BKE_gpencil_stroke_2d_flat_ref(
|
||||
gps->points, gps->totpoints,
|
||||
gps_iter->points, gps_iter->totpoints, points2d_iter,
|
||||
scale, &direction);
|
||||
BLI_ghash_insert(all_2d, gps_iter, points2d_iter);
|
||||
}
|
||||
|
||||
bool hit_a = false;
|
||||
bool hit_b = false;
|
||||
float p2d_a1[2] = {0.0f, 0.0f};
|
||||
float p2d_a2[2] = {0.0f, 0.0f};
|
||||
float r_hit2d[2];
|
||||
bGPDspoint *hit_pointa = NULL;
|
||||
bGPDspoint *hit_pointb = NULL;
|
||||
|
||||
/* analyze points before current */
|
||||
if (cur_idx > 0) {
|
||||
for (int i = cur_idx; i >= 0; i--) {
|
||||
pta1 = &gps->points[i];
|
||||
copy_v2_v2(p2d_a1, points2d[i]);
|
||||
|
||||
i2 = i - 1;
|
||||
CLAMP_MIN(i2, 0);
|
||||
pta2 = &gps->points[i2];
|
||||
copy_v2_v2(p2d_a2, points2d[i2]);
|
||||
|
||||
hit_a = gpencil_check_collision(
|
||||
gps, gps_array, all_2d, totstrokes, p2d_a1, p2d_a2, r_hit2d);
|
||||
|
||||
if (select) {
|
||||
pta1->flag |= GP_SPOINT_SELECT;
|
||||
}
|
||||
else {
|
||||
pta1->flag &= ~GP_SPOINT_SELECT;
|
||||
}
|
||||
|
||||
if (hit_a) {
|
||||
f = gp_calc_factor(p2d_a1, p2d_a2, r_hit2d);
|
||||
interp_v3_v3v3(r_hita, &pta1->x, &pta2->x, f);
|
||||
if (f > min_factor) {
|
||||
hit_pointa = pta2; /* first point is second (inverted loop) */
|
||||
}
|
||||
else {
|
||||
pta1->flag &= ~GP_SPOINT_SELECT;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* analyze points after current */
|
||||
for (int i = cur_idx; i < gps->totpoints; i++) {
|
||||
pta1 = &gps->points[i];
|
||||
copy_v2_v2(p2d_a1, points2d[i]);
|
||||
|
||||
i2 = i + 1;
|
||||
CLAMP_MAX(i2, gps->totpoints - 1);
|
||||
pta2 = &gps->points[i2];
|
||||
copy_v2_v2(p2d_a2, points2d[i2]);
|
||||
|
||||
hit_b = gpencil_check_collision(
|
||||
gps, gps_array, all_2d, totstrokes, p2d_a1, p2d_a2, r_hit2d);
|
||||
|
||||
if (select) {
|
||||
pta1->flag |= GP_SPOINT_SELECT;
|
||||
}
|
||||
else {
|
||||
pta1->flag &= ~GP_SPOINT_SELECT;
|
||||
}
|
||||
|
||||
if (hit_b) {
|
||||
f = gp_calc_factor(p2d_a1, p2d_a2, r_hit2d);
|
||||
interp_v3_v3v3(r_hitb, &pta1->x, &pta2->x, f);
|
||||
if (f > min_factor) {
|
||||
hit_pointb = pta1;
|
||||
}
|
||||
else {
|
||||
pta1->flag &= ~GP_SPOINT_SELECT;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* insert new point in the collision points */
|
||||
if (insert) {
|
||||
gp_insert_point(gps, hit_pointa, hit_pointb, r_hita, r_hitb);
|
||||
}
|
||||
|
||||
/* free memory */
|
||||
if (all_2d) {
|
||||
GHashIterator gh_iter;
|
||||
GHASH_ITER(gh_iter, all_2d) {
|
||||
float(*p2d)[2] = BLI_ghashIterator_getValue(&gh_iter);
|
||||
MEM_SAFE_FREE(p2d);
|
||||
}
|
||||
BLI_ghash_free(all_2d, NULL, NULL);
|
||||
}
|
||||
|
||||
/* if no hit, reset selection flag */
|
||||
if ((!hit_a) && (!hit_b)) {
|
||||
for (int i = 0; i < gps->totpoints; i++) {
|
||||
pta1 = &gps->points[i];
|
||||
pta2 = &oldpoints[i];
|
||||
pta1->flag = pta2->flag;
|
||||
}
|
||||
}
|
||||
|
||||
MEM_SAFE_FREE(points2d);
|
||||
MEM_SAFE_FREE(gps_array);
|
||||
MEM_SAFE_FREE(oldpoints);
|
||||
|
||||
/* return type of hit */
|
||||
if ((hit_a) && (hit_b)) {
|
||||
return 3;
|
||||
}
|
||||
else if (hit_a) {
|
||||
return 1;
|
||||
}
|
||||
else if (hit_b) {
|
||||
return 2;
|
||||
}
|
||||
else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
@ -259,4 +259,17 @@ void ED_gpencil_tpoint_to_point(struct ARegion *ar, float origin[3], const struc
|
||||
void ED_gpencil_calc_stroke_uv(struct Object *ob, struct bGPDstroke *gps);
|
||||
void ED_gpencil_update_color_uv(struct Main *bmain, struct Material *mat);
|
||||
|
||||
/* extend selection to stroke intersections
|
||||
* returns:
|
||||
* 0 - No hit
|
||||
* 1 - Hit in point A
|
||||
* 2 - Hit in point B
|
||||
* 3 - Hit in point A and B
|
||||
*/
|
||||
int ED_gpencil_select_stroke_segment(
|
||||
struct bGPDlayer *gpl,
|
||||
struct bGPDstroke *gps, struct bGPDspoint *pt,
|
||||
bool select, bool insert, const float scale,
|
||||
float r_hita[3], float r_hitb[3]);
|
||||
|
||||
#endif /* __ED_GPENCIL_H__ */
|
||||
|
@ -203,7 +203,8 @@ typedef struct bGPDstroke {
|
||||
|
||||
/** Material index. */
|
||||
int mat_nr;
|
||||
char _pad1[4];
|
||||
/** Caps mode for each stroke extreme */
|
||||
short caps[2];
|
||||
|
||||
/** Vertex weight data. */
|
||||
struct MDeformVert *dvert;
|
||||
@ -232,6 +233,15 @@ typedef enum eGPDstroke_Flag {
|
||||
GP_STROKE_ERASER = (1 << 15)
|
||||
} eGPDstroke_Flag;
|
||||
|
||||
/* bGPDstroke->caps */
|
||||
typedef enum eGPDstroke_Caps {
|
||||
/* type of extreme */
|
||||
GP_STROKE_CAP_ROUND = 0,
|
||||
GP_STROKE_CAP_FLAT = 1,
|
||||
|
||||
GP_STROKE_CAP_MAX
|
||||
} GPDstroke_Caps;
|
||||
|
||||
/* ***************************************** */
|
||||
/* GP Frame */
|
||||
|
||||
@ -371,6 +381,8 @@ typedef enum eGPDlayer_Flag {
|
||||
GP_LAYER_UNLOCK_COLOR = (1 << 12),
|
||||
/* Mask Layer */
|
||||
GP_LAYER_USE_MASK = (1 << 13),
|
||||
/* Flag used to display in Paint mode only layers with keyframe */
|
||||
GP_LAYER_SOLO_MODE = (1 << 4),
|
||||
} eGPDlayer_Flag;
|
||||
|
||||
/* bGPDlayer->onion_flag */
|
||||
|
@ -1073,6 +1073,20 @@ typedef struct GP_Sculpt_Data {
|
||||
char _pad[4];
|
||||
} GP_Sculpt_Data;
|
||||
|
||||
/* Settings for a GPencil Speed Guide */
|
||||
typedef struct GP_Sculpt_Guide {
|
||||
char use_guide;
|
||||
char use_snapping;
|
||||
char reference_point;
|
||||
char type;
|
||||
char _pad2[4];
|
||||
float angle;
|
||||
float angle_snap;
|
||||
float spacing;
|
||||
float location[3];
|
||||
struct Object *reference_object;
|
||||
} GP_Sculpt_Guide;
|
||||
|
||||
/* GP_Sculpt_Data.flag */
|
||||
typedef enum eGP_Sculpt_Flag {
|
||||
/* invert the effect of the brush */
|
||||
@ -1106,7 +1120,8 @@ typedef struct GP_Sculpt_Settings {
|
||||
int flag;
|
||||
/** #eGP_Lockaxis_Types lock drawing to one axis. */
|
||||
int lock_axis;
|
||||
char pad1[4];
|
||||
/** Threshold for intersections */
|
||||
float isect_threshold;
|
||||
|
||||
/* weight paint is a submode of sculpt but use its own index. All weight paint
|
||||
* brushes must be defined at the end of the brush array.
|
||||
@ -1118,6 +1133,8 @@ typedef struct GP_Sculpt_Settings {
|
||||
struct CurveMapping *cur_falloff;
|
||||
/** Curve used for primitve tools. */
|
||||
struct CurveMapping *cur_primitive;
|
||||
/** Guides used for paint tools */
|
||||
struct GP_Sculpt_Guide guide;
|
||||
} GP_Sculpt_Settings;
|
||||
|
||||
/* GP_Sculpt_Settings.flag */
|
||||
@ -2317,10 +2334,26 @@ typedef enum eGPencil_Placement_Flags {
|
||||
|
||||
/* ToolSettings.gpencil_selectmode */
|
||||
typedef enum eGPencil_Selectmode_types {
|
||||
GP_SELECTMODE_POINT = 0,
|
||||
GP_SELECTMODE_STROKE = 1
|
||||
GP_SELECTMODE_POINT = 0,
|
||||
GP_SELECTMODE_STROKE = 1,
|
||||
GP_SELECTMODE_SEGMENT = 2
|
||||
} eGPencil_Selectmode_types;
|
||||
|
||||
/* ToolSettings.gpencil_guide_types */
|
||||
typedef enum eGPencil_GuideTypes {
|
||||
GP_GUIDE_CIRCULAR = 0,
|
||||
GP_GUIDE_RADIAL,
|
||||
GP_GUIDE_PARALLEL,
|
||||
GP_GUIDE_GRID
|
||||
} eGPencil_GuideTypes;
|
||||
|
||||
/* ToolSettings.gpencil_guide_references */
|
||||
typedef enum eGPencil_Guide_Reference {
|
||||
GP_GUIDE_REF_CURSOR = 0,
|
||||
GP_GUIDE_REF_CUSTOM,
|
||||
GP_GUIDE_REF_OBJECT
|
||||
} eGPencil_Guide_Reference;
|
||||
|
||||
/* ToolSettings.particle flag */
|
||||
#define PE_KEEP_LENGTHS (1 << 0)
|
||||
#define PE_LOCK_FIRST (1 << 1)
|
||||
|
@ -274,6 +274,7 @@ extern StructRNA RNA_GPencilFrame;
|
||||
extern StructRNA RNA_GPencilInterpolateSettings;
|
||||
extern StructRNA RNA_GPencilLayer;
|
||||
extern StructRNA RNA_GPencilSculptBrush;
|
||||
extern StructRNA RNA_GPencilSculptGuide;
|
||||
extern StructRNA RNA_GPencilSculptSettings;
|
||||
extern StructRNA RNA_GPencilStroke;
|
||||
extern StructRNA RNA_GPencilStrokePoint;
|
||||
|
@ -20,9 +20,9 @@
|
||||
* ***** END GPL LICENSE BLOCK *****
|
||||
*/
|
||||
|
||||
/** \file blender/makesrna/intern/rna_gpencil.c
|
||||
* \ingroup RNA
|
||||
*/
|
||||
/** \file blender/makesrna/intern/rna_gpencil.c
|
||||
* \ingroup RNA
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
@ -49,7 +49,7 @@
|
||||
|
||||
#include "WM_types.h"
|
||||
|
||||
/* parent type */
|
||||
/* parent type */
|
||||
static const EnumPropertyItem parent_type_items[] = {
|
||||
{PAROBJECT, "OBJECT", 0, "Object", "The layer is parented to an object"},
|
||||
{PARSKEL, "ARMATURE", 0, "Armature", ""},
|
||||
@ -87,6 +87,12 @@ static const EnumPropertyItem rna_enum_layer_blend_modes_items[] = {
|
||||
{eGplBlendMode_Divide, "DIVIDE", 0, "Divide", "" },
|
||||
{0, NULL, 0, NULL, NULL }
|
||||
};
|
||||
|
||||
static EnumPropertyItem rna_enum_gpencil_caps_modes_items[] = {
|
||||
{GP_STROKE_CAP_ROUND, "ROUND", 0, "Rounded", ""},
|
||||
{GP_STROKE_CAP_FLAT, "FLAT", 0, "Flat", ""},
|
||||
{0, NULL, 0, NULL, NULL}
|
||||
};
|
||||
#endif
|
||||
|
||||
#ifdef RNA_RUNTIME
|
||||
@ -285,8 +291,8 @@ static void rna_GPencilLayer_parent_bone_set(PointerRNA *ptr, const char *value)
|
||||
|
||||
/* parent types enum */
|
||||
static const EnumPropertyItem *rna_Object_parent_type_itemf(
|
||||
bContext *UNUSED(C), PointerRNA *ptr,
|
||||
PropertyRNA *UNUSED(prop), bool *r_free)
|
||||
bContext *UNUSED(C), PointerRNA *ptr,
|
||||
PropertyRNA *UNUSED(prop), bool *r_free)
|
||||
{
|
||||
bGPDlayer *gpl = (bGPDlayer *)ptr->data;
|
||||
EnumPropertyItem *item = NULL;
|
||||
@ -376,7 +382,7 @@ static int rna_GPencil_active_layer_index_get(PointerRNA *ptr)
|
||||
|
||||
static void rna_GPencil_active_layer_index_set(PointerRNA *ptr, int value)
|
||||
{
|
||||
bGPdata *gpd = (bGPdata *)ptr->id.data;
|
||||
bGPdata *gpd = (bGPdata *)ptr->id.data;
|
||||
bGPDlayer *gpl = BLI_findlink(&gpd->layers, value);
|
||||
|
||||
BKE_gpencil_layer_setactive(gpd, gpl);
|
||||
@ -398,11 +404,11 @@ static void rna_GPencil_active_layer_index_range(PointerRNA *ptr, int *min, int
|
||||
}
|
||||
|
||||
static const EnumPropertyItem *rna_GPencil_active_layer_itemf(
|
||||
bContext *C, PointerRNA *ptr, PropertyRNA *UNUSED(prop), bool *r_free)
|
||||
bContext *C, PointerRNA *ptr, PropertyRNA *UNUSED(prop), bool *r_free)
|
||||
{
|
||||
bGPdata *gpd = (bGPdata *)ptr->id.data;
|
||||
bGPDlayer *gpl;
|
||||
EnumPropertyItem *item = NULL, item_tmp = {0};
|
||||
EnumPropertyItem *item = NULL, item_tmp = { 0 };
|
||||
int totitem = 0;
|
||||
int i = 0;
|
||||
|
||||
@ -507,11 +513,11 @@ static void rna_GPencil_stroke_point_add(ID *id, bGPDstroke *stroke, int count,
|
||||
if (count > 0) {
|
||||
/* create space at the end of the array for extra points */
|
||||
stroke->points = MEM_recallocN_id(stroke->points,
|
||||
sizeof(bGPDspoint) * (stroke->totpoints + count),
|
||||
"gp_stroke_points");
|
||||
sizeof(bGPDspoint) * (stroke->totpoints + count),
|
||||
"gp_stroke_points");
|
||||
stroke->dvert = MEM_recallocN_id(stroke->dvert,
|
||||
sizeof(MDeformVert) * (stroke->totpoints + count),
|
||||
"gp_stroke_weight");
|
||||
sizeof(MDeformVert) * (stroke->totpoints + count),
|
||||
"gp_stroke_weight");
|
||||
|
||||
/* init the pressure and strength values so that old scripts won't need to
|
||||
* be modified to give these initial values...
|
||||
@ -968,6 +974,19 @@ static void rna_def_gpencil_stroke(BlenderRNA *brna)
|
||||
RNA_def_property_ui_text(prop, "Cyclic", "Enable cyclic drawing, closing the stroke");
|
||||
RNA_def_property_update(prop, 0, "rna_GPencil_update");
|
||||
|
||||
/* Caps mode */
|
||||
prop = RNA_def_property(srna, "start_cap_mode", PROP_ENUM, PROP_NONE);
|
||||
RNA_def_property_enum_sdna(prop, NULL, "caps[0]");
|
||||
RNA_def_property_enum_items(prop, rna_enum_gpencil_caps_modes_items);
|
||||
RNA_def_property_ui_text(prop, "Start Cap", "Stroke start extreme cap style");
|
||||
RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, "rna_GPencil_update");
|
||||
|
||||
prop = RNA_def_property(srna, "end_cap_mode", PROP_ENUM, PROP_NONE);
|
||||
RNA_def_property_enum_sdna(prop, NULL, "caps[1]");
|
||||
RNA_def_property_enum_items(prop, rna_enum_gpencil_caps_modes_items);
|
||||
RNA_def_property_ui_text(prop, "End Cap", "Stroke end extreme cap style");
|
||||
RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, "rna_GPencil_update");
|
||||
|
||||
/* No fill: The stroke never must fill area and must use fill color as stroke color (this is a special flag for fill brush) */
|
||||
prop = RNA_def_property(srna, "is_nofill_stroke", PROP_BOOLEAN, PROP_NONE);
|
||||
RNA_def_property_boolean_sdna(prop, NULL, "flag", GP_STROKE_NOFILL);
|
||||
@ -1066,7 +1085,7 @@ static void rna_def_gpencil_frames_api(BlenderRNA *brna, PropertyRNA *cprop)
|
||||
RNA_def_function_ui_description(func, "Add a new grease pencil frame");
|
||||
RNA_def_function_flag(func, FUNC_USE_REPORTS);
|
||||
parm = RNA_def_int(func, "frame_number", 1, MINAFRAME, MAXFRAME, "Frame Number",
|
||||
"The frame on which this sketch appears", MINAFRAME, MAXFRAME);
|
||||
"The frame on which this sketch appears", MINAFRAME, MAXFRAME);
|
||||
RNA_def_parameter_flags(parm, 0, PARM_REQUIRED);
|
||||
parm = RNA_def_pointer(func, "frame", "GPencilFrame", "", "The newly created frame");
|
||||
RNA_def_function_return(func, parm);
|
||||
@ -1261,6 +1280,13 @@ static void rna_def_gpencil_layer(BlenderRNA *brna)
|
||||
"Clamp any pixel outside underlying layers drawing");
|
||||
RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, NULL);
|
||||
|
||||
/* solo mode: Only display frames with keyframe */
|
||||
prop = RNA_def_property(srna, "use_solo_mode", PROP_BOOLEAN, PROP_NONE);
|
||||
RNA_def_property_boolean_sdna(prop, NULL, "flag", GP_LAYER_SOLO_MODE);
|
||||
RNA_def_property_ui_text(prop, "Solo Mode",
|
||||
"In Paint mode display only layers with keyframe in current frame");
|
||||
RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, "rna_GPencil_update");
|
||||
|
||||
/* exposed as layers.active */
|
||||
#if 0
|
||||
prop = RNA_def_property(srna, "active", PROP_BOOLEAN, PROP_NONE);
|
||||
@ -1373,20 +1399,20 @@ static void rna_def_gpencil_layers_api(BlenderRNA *brna, PropertyRNA *cprop)
|
||||
|
||||
prop = RNA_def_property(srna, "active_index", PROP_INT, PROP_UNSIGNED);
|
||||
RNA_def_property_int_funcs(
|
||||
prop,
|
||||
"rna_GPencil_active_layer_index_get",
|
||||
"rna_GPencil_active_layer_index_set",
|
||||
"rna_GPencil_active_layer_index_range");
|
||||
prop,
|
||||
"rna_GPencil_active_layer_index_get",
|
||||
"rna_GPencil_active_layer_index_set",
|
||||
"rna_GPencil_active_layer_index_range");
|
||||
RNA_def_property_ui_text(prop, "Active Layer Index", "Index of active grease pencil layer");
|
||||
RNA_def_property_update(prop, NC_GPENCIL | ND_DATA | NA_SELECTED, NULL);
|
||||
|
||||
/* Active Layer - As an enum (for selecting active layer for annotations) */
|
||||
prop = RNA_def_property(srna, "active_note", PROP_ENUM, PROP_NONE);
|
||||
RNA_def_property_enum_funcs(
|
||||
prop,
|
||||
"rna_GPencil_active_layer_index_get",
|
||||
"rna_GPencil_active_layer_index_set",
|
||||
"rna_GPencil_active_layer_itemf");
|
||||
prop,
|
||||
"rna_GPencil_active_layer_index_get",
|
||||
"rna_GPencil_active_layer_index_set",
|
||||
"rna_GPencil_active_layer_itemf");
|
||||
RNA_def_property_enum_items(prop, DummyRNA_DEFAULT_items); /* purely dynamic, as it maps to user-data */
|
||||
RNA_def_property_ui_text(prop, "Active Note", "Note/Layer to add annotation strokes to");
|
||||
RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, "rna_GPencil_update");
|
||||
@ -1405,7 +1431,7 @@ static void rna_def_gpencil_grid(BlenderRNA *brna)
|
||||
|
||||
RNA_def_struct_path_func(srna, "rna_GreasePencilGrid_path");
|
||||
RNA_def_struct_ui_text(srna, "Grid and Canvas Settings",
|
||||
"Settings for grid and canvas in 3D viewport");
|
||||
"Settings for grid and canvas in 3D viewport");
|
||||
|
||||
prop = RNA_def_property(srna, "scale", PROP_FLOAT, PROP_XYZ);
|
||||
RNA_def_property_float_sdna(prop, NULL, "scale");
|
||||
@ -1510,7 +1536,7 @@ static void rna_def_gpencil_data(BlenderRNA *brna)
|
||||
prop = RNA_def_property(srna, "show_stroke_direction", PROP_BOOLEAN, PROP_NONE);
|
||||
RNA_def_property_boolean_sdna(prop, NULL, "flag", GP_DATA_SHOW_DIRECTION);
|
||||
RNA_def_property_ui_text(prop, "Show Direction", "Show stroke drawing direction with a bigger green dot (start) "
|
||||
"and smaller red dot (end) points");
|
||||
"and smaller red dot (end) points");
|
||||
RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, "rna_GPencil_update");
|
||||
|
||||
prop = RNA_def_property(srna, "show_constant_thickness", PROP_BOOLEAN, PROP_NONE);
|
||||
|
@ -2381,6 +2381,8 @@ static void rna_def_tool_settings(BlenderRNA *brna)
|
||||
static const EnumPropertyItem gpencil_selectmode_items[] = {
|
||||
{GP_SELECTMODE_POINT, "POINT", ICON_GP_SELECT_POINTS, "Point", "Select only points"},
|
||||
{GP_SELECTMODE_STROKE, "STROKE", ICON_GP_SELECT_STROKES, "Stroke", "Select all stroke points" },
|
||||
/* GPXX need better icon for segment */
|
||||
{GP_SELECTMODE_SEGMENT, "SEGMENT", ICON_SHADERFX, "Segment", "Select all stroke points between other strokes" },
|
||||
{0, NULL, 0, NULL, NULL}
|
||||
};
|
||||
|
||||
|
@ -27,6 +27,7 @@
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "BLI_math.h"
|
||||
#include "BLI_utildefines.h"
|
||||
|
||||
#include "RNA_define.h"
|
||||
@ -544,6 +545,10 @@ static char *rna_GPencilSculptBrush_path(PointerRNA *UNUSED(ptr))
|
||||
return BLI_strdup("tool_settings.gpencil_sculpt.brush");
|
||||
}
|
||||
|
||||
static char *rna_GPencilSculptGuide_path(PointerRNA *UNUSED(ptr))
|
||||
{
|
||||
return BLI_strdup("tool_settings.gpencil_sculpt.guide");
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
@ -1194,6 +1199,99 @@ static void rna_def_particle_edit(BlenderRNA *brna)
|
||||
RNA_def_property_ui_text(prop, "Curve", "");
|
||||
}
|
||||
|
||||
/* srna -- gpencil speed guides */
|
||||
static void rna_def_gpencil_guides(BlenderRNA *brna)
|
||||
{
|
||||
StructRNA *srna;
|
||||
PropertyRNA *prop;
|
||||
|
||||
srna = RNA_def_struct(brna, "GPencilSculptGuide", NULL);
|
||||
RNA_def_struct_sdna(srna, "GP_Sculpt_Guide");
|
||||
RNA_def_struct_path_func(srna, "rna_GPencilSculptGuide_path");
|
||||
RNA_def_struct_ui_text(srna, "GPencil Sculpt Guide", "Guides for drawing");
|
||||
|
||||
static const EnumPropertyItem prop_gpencil_guidetypes[] = {
|
||||
{GP_GUIDE_CIRCULAR, "CIRCULAR", 0, "Circular", "Use single point to create rings"},
|
||||
{GP_GUIDE_RADIAL, "RADIAL", 0, "Radial", "Use single point as direction"},
|
||||
{GP_GUIDE_PARALLEL, "PARALLEL", 0, "Parallel", "Parallel lines"},
|
||||
{GP_GUIDE_GRID, "GRID", 0, "Grid", "Grid allows horizontal and vertical lines"},
|
||||
{0, NULL, 0, NULL, NULL}
|
||||
};
|
||||
|
||||
static const EnumPropertyItem prop_gpencil_guide_references[] = {
|
||||
{GP_GUIDE_REF_CURSOR, "CURSOR", 0, "Cursor", "Use cursor as reference point"},
|
||||
{GP_GUIDE_REF_CUSTOM, "CUSTOM", 0, "Custom", "Use custom reference point"},
|
||||
{GP_GUIDE_REF_OBJECT, "OBJECT", 0, "Object", "Use object as reference point"},
|
||||
{0, NULL, 0, NULL, NULL}
|
||||
};
|
||||
|
||||
prop = RNA_def_property(srna, "use_guide", PROP_BOOLEAN, PROP_NONE);
|
||||
RNA_def_property_boolean_sdna(prop, NULL, "use_guide", false);
|
||||
RNA_def_property_boolean_default(prop, false);
|
||||
RNA_def_property_ui_text(prop, "Use Guides", "Enable speed guides");
|
||||
RNA_def_parameter_clear_flags(prop, PROP_ANIMATABLE, 0);
|
||||
RNA_def_property_update(prop, NC_SCENE | ND_TOOLSETTINGS, NULL);
|
||||
|
||||
prop = RNA_def_property(srna, "use_snapping", PROP_BOOLEAN, PROP_NONE);
|
||||
RNA_def_property_boolean_sdna(prop, NULL, "use_snapping", false);
|
||||
RNA_def_property_boolean_default(prop, false);
|
||||
RNA_def_property_ui_text(prop, "Use Snapping", "Enable snapping to guides angle or spacing options");
|
||||
RNA_def_parameter_clear_flags(prop, PROP_ANIMATABLE, 0);
|
||||
RNA_def_property_update(prop, NC_SCENE | ND_TOOLSETTINGS, NULL);
|
||||
|
||||
prop = RNA_def_property(srna, "reference_object", PROP_POINTER, PROP_NONE);
|
||||
RNA_def_property_pointer_sdna(prop, NULL, "reference_object");
|
||||
RNA_def_property_ui_text(prop, "Object", "Object used for reference point");
|
||||
RNA_def_property_flag(prop, PROP_EDITABLE | PROP_ID_SELF_CHECK);
|
||||
RNA_def_property_update(prop, NC_SCENE | ND_TOOLSETTINGS, "rna_ImaPaint_viewport_update");
|
||||
|
||||
prop = RNA_def_property(srna, "reference_point", PROP_ENUM, PROP_NONE);
|
||||
RNA_def_property_enum_sdna(prop, NULL, "reference_point");
|
||||
RNA_def_property_enum_items(prop, prop_gpencil_guide_references);
|
||||
RNA_def_property_ui_text(prop, "Type", "Type of speed guide");
|
||||
RNA_def_parameter_clear_flags(prop, PROP_ANIMATABLE, 0);
|
||||
RNA_def_property_update(prop, NC_SCENE | ND_TOOLSETTINGS, "rna_ImaPaint_viewport_update");
|
||||
|
||||
prop = RNA_def_property(srna, "type", PROP_ENUM, PROP_NONE);
|
||||
RNA_def_property_enum_sdna(prop, NULL, "type");
|
||||
RNA_def_property_enum_items(prop, prop_gpencil_guidetypes);
|
||||
RNA_def_property_ui_text(prop, "Type", "Type of speed guide");
|
||||
RNA_def_parameter_clear_flags(prop, PROP_ANIMATABLE, 0);
|
||||
RNA_def_property_update(prop, NC_SCENE | ND_TOOLSETTINGS, NULL);
|
||||
|
||||
prop = RNA_def_property(srna, "angle", PROP_FLOAT, PROP_ANGLE);
|
||||
RNA_def_property_float_sdna(prop, NULL, "angle");
|
||||
RNA_def_property_range(prop, -(M_PI * 2.0f), (M_PI * 2.0f));
|
||||
RNA_def_property_ui_text(prop, "Angle", "Direction of lines");
|
||||
RNA_def_parameter_clear_flags(prop, PROP_ANIMATABLE, 0);
|
||||
RNA_def_property_update(prop, NC_SCENE | ND_TOOLSETTINGS, NULL);
|
||||
|
||||
prop = RNA_def_property(srna, "angle_snap", PROP_FLOAT, PROP_ANGLE);
|
||||
RNA_def_property_float_sdna(prop, NULL, "angle_snap");
|
||||
RNA_def_property_range(prop, -(M_PI * 2.0f), (M_PI * 2.0f));
|
||||
RNA_def_property_ui_text(prop, "Angle Snap", "Angle snapping");
|
||||
RNA_def_parameter_clear_flags(prop, PROP_ANIMATABLE, 0);
|
||||
RNA_def_property_update(prop, NC_SCENE | ND_TOOLSETTINGS, NULL);
|
||||
|
||||
prop = RNA_def_property(srna, "spacing", PROP_FLOAT, PROP_DISTANCE);
|
||||
RNA_def_property_float_sdna(prop, NULL, "spacing");
|
||||
RNA_def_property_float_default(prop, 0.01f);
|
||||
RNA_def_property_range(prop, 0.0f, FLT_MAX);
|
||||
RNA_def_property_ui_range(prop, 0.0f, FLT_MAX, 1, 3);
|
||||
RNA_def_property_ui_text(prop, "Spacing", "Guide spacing");
|
||||
RNA_def_parameter_clear_flags(prop, PROP_ANIMATABLE, 0);
|
||||
RNA_def_property_update(prop, NC_SCENE | ND_TOOLSETTINGS, NULL);
|
||||
|
||||
prop = RNA_def_property(srna, "location", PROP_FLOAT, PROP_DISTANCE);
|
||||
RNA_def_property_float_sdna(prop, NULL, "location");
|
||||
RNA_def_property_array(prop, 3);
|
||||
RNA_def_parameter_clear_flags(prop, PROP_ANIMATABLE, 0);
|
||||
RNA_def_property_ui_text(prop, "Location", "Custom reference point for guides");
|
||||
RNA_def_property_range(prop, -FLT_MAX, FLT_MAX);
|
||||
RNA_def_property_ui_range(prop, -FLT_MAX, FLT_MAX, 1, 3);
|
||||
RNA_def_property_update(prop, NC_SCENE | ND_TOOLSETTINGS, "rna_ImaPaint_viewport_update");
|
||||
}
|
||||
|
||||
static void rna_def_gpencil_sculpt(BlenderRNA *brna)
|
||||
{
|
||||
static const EnumPropertyItem prop_direction_items[] = {
|
||||
@ -1230,6 +1328,11 @@ static void rna_def_gpencil_sculpt(BlenderRNA *brna)
|
||||
RNA_def_parameter_clear_flags(prop, PROP_ANIMATABLE, 0);
|
||||
RNA_def_property_ui_text(prop, "Brush", "");
|
||||
|
||||
prop = RNA_def_property(srna, "guide", PROP_POINTER, PROP_NONE);
|
||||
RNA_def_property_struct_type(prop, "GPencilSculptGuide");
|
||||
RNA_def_parameter_clear_flags(prop, PROP_ANIMATABLE, 0);
|
||||
RNA_def_property_ui_text(prop, "Guide", "");
|
||||
|
||||
prop = RNA_def_property(srna, "use_select_mask", PROP_BOOLEAN, PROP_NONE);
|
||||
RNA_def_property_boolean_sdna(prop, NULL, "flag", GP_SCULPT_SETT_FLAG_SELECT_MASK);
|
||||
RNA_def_property_ui_text(prop, "Selection Mask", "Only sculpt selected stroke points");
|
||||
@ -1299,6 +1402,14 @@ static void rna_def_gpencil_sculpt(BlenderRNA *brna)
|
||||
RNA_def_parameter_clear_flags(prop, PROP_ANIMATABLE, 0);
|
||||
RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, "rna_GPencil_update");
|
||||
|
||||
/* threshold for cutter */
|
||||
prop = RNA_def_property(srna, "intersection_threshold", PROP_FLOAT, PROP_NONE);
|
||||
RNA_def_property_float_sdna(prop, NULL, "isect_threshold");
|
||||
RNA_def_property_range(prop, 0.0f, 10.0f);
|
||||
RNA_def_property_float_default(prop, 0.1f);
|
||||
RNA_def_property_ui_text(prop, "Threshold", "Threshold for stroke intersections");
|
||||
RNA_def_parameter_clear_flags(prop, PROP_ANIMATABLE, 0);
|
||||
|
||||
/* brush */
|
||||
srna = RNA_def_struct(brna, "GPencilSculptBrush", NULL);
|
||||
RNA_def_struct_sdna(srna, "GP_Sculpt_Data");
|
||||
@ -1383,7 +1494,6 @@ static void rna_def_gpencil_sculpt(BlenderRNA *brna)
|
||||
RNA_def_property_boolean_default(prop, true);
|
||||
RNA_def_property_ui_text(prop, "Enable Cursor", "Enable cursor on screen");
|
||||
RNA_def_parameter_clear_flags(prop, PROP_ANIMATABLE, 0);
|
||||
|
||||
}
|
||||
|
||||
void RNA_def_sculpt_paint(BlenderRNA *brna)
|
||||
@ -1399,6 +1509,7 @@ void RNA_def_sculpt_paint(BlenderRNA *brna)
|
||||
rna_def_vertex_paint(brna);
|
||||
rna_def_image_paint(brna);
|
||||
rna_def_particle_edit(brna);
|
||||
rna_def_gpencil_guides(brna);
|
||||
rna_def_gpencil_sculpt(brna);
|
||||
RNA_define_animate_sdna(true);
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user