From 115322ef08ed3cff8751a90139024c8b7f3453b1 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Mon, 4 Jun 2012 15:36:16 +0000 Subject: mask merge (initial copy of editor files) --- source/blender/editors/mask/CMakeLists.txt | 51 + source/blender/editors/mask/SConscript | 9 + source/blender/editors/mask/mask_add.c | 707 +++++++++++++ source/blender/editors/mask/mask_draw.c | 428 ++++++++ source/blender/editors/mask/mask_edit.c | 327 ++++++ source/blender/editors/mask/mask_intern.h | 111 ++ source/blender/editors/mask/mask_ops.c | 1171 ++++++++++++++++++++++ source/blender/editors/mask/mask_relationships.c | 183 ++++ source/blender/editors/mask/mask_select.c | 770 ++++++++++++++ source/blender/editors/mask/mask_shapekey.c | 168 ++++ 10 files changed, 3925 insertions(+) create mode 100644 source/blender/editors/mask/CMakeLists.txt create mode 100644 source/blender/editors/mask/SConscript create mode 100644 source/blender/editors/mask/mask_add.c create mode 100644 source/blender/editors/mask/mask_draw.c create mode 100644 source/blender/editors/mask/mask_edit.c create mode 100644 source/blender/editors/mask/mask_intern.h create mode 100644 source/blender/editors/mask/mask_ops.c create mode 100644 source/blender/editors/mask/mask_relationships.c create mode 100644 source/blender/editors/mask/mask_select.c create mode 100644 source/blender/editors/mask/mask_shapekey.c (limited to 'source/blender/editors/mask') diff --git a/source/blender/editors/mask/CMakeLists.txt b/source/blender/editors/mask/CMakeLists.txt new file mode 100644 index 00000000000..f15e9c27732 --- /dev/null +++ b/source/blender/editors/mask/CMakeLists.txt @@ -0,0 +1,51 @@ +# ***** BEGIN GPL LICENSE BLOCK ***** +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software Foundation, +# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# The Original Code is Copyright (C) 2012 Blender Foundation. +# +# Contributor(s): Blender Foundation, +# Sergey Sharybin +# +# ***** END GPL LICENSE BLOCK ***** + +set(INC + ../include + ../../blenkernel + ../../blenloader + ../../blenlib + ../../makesdna + ../../makesrna + ../../windowmanager + ../../../../intern/guardedalloc + ${GLEW_INCLUDE_PATH} +) + +set(INC_SYS +) + +set(SRC + mask_add.c + mask_draw.c + mask_edit.c + mask_ops.c + mask_relationships.c + mask_select.c + mask_shapekey.c + + mask_intern.h +) + +blender_add_lib(bf_editor_mask "${SRC}" "${INC}" "${INC_SYS}") diff --git a/source/blender/editors/mask/SConscript b/source/blender/editors/mask/SConscript new file mode 100644 index 00000000000..4af000d038d --- /dev/null +++ b/source/blender/editors/mask/SConscript @@ -0,0 +1,9 @@ +#!/usr/bin/python +Import ('env') + +sources = env.Glob('*.c') +defs = [] +incs = '../include ../../blenkernel ../../blenloader ../../blenlib ../../windowmanager ../../makesdna' +incs += ' ../../makesrna #/extern/glew/include #/intern/guardedalloc' + +env.BlenderLib ( 'bf_editors_mask', sources, Split(incs), defs, libtype=['core'], priority=[100] ) diff --git a/source/blender/editors/mask/mask_add.c b/source/blender/editors/mask/mask_add.c new file mode 100644 index 00000000000..c2c2ebbfe42 --- /dev/null +++ b/source/blender/editors/mask/mask_add.c @@ -0,0 +1,707 @@ +/* + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2012 Blender Foundation. + * All rights reserved. + * + * + * Contributor(s): Blender Foundation, + * Sergey Sharybin + * + * ***** END GPL LICENSE BLOCK ***** + */ + +/** \file blender/editors/mask/mask_add.c + * \ingroup edmask + */ + +#include "MEM_guardedalloc.h" + +#include "BLI_utildefines.h" +#include "BLI_listbase.h" +#include "BLI_math.h" + +#include "BKE_context.h" +#include "BKE_curve.h" +#include "BKE_depsgraph.h" +#include "BKE_mask.h" + +#include "DNA_scene_types.h" +#include "DNA_mask_types.h" +#include "DNA_object_types.h" /* SELECT */ + +#include "WM_api.h" +#include "WM_types.h" + +#include "ED_screen.h" +#include "ED_mask.h" +#include "ED_clip.h" +#include "ED_keyframing.h" + +#include "RNA_access.h" +#include "RNA_define.h" + +#include "mask_intern.h" /* own include */ + + +static int find_nearest_diff_point(bContext *C, Mask *mask, const float normal_co[2], int threshold, int feather, + MaskLayer **masklay_r, MaskSpline **spline_r, MaskSplinePoint **point_r, + float *u_r, float tangent[2]) +{ + MaskLayer *masklay, *point_masklay; + MaskSpline *point_spline; + MaskSplinePoint *point = NULL; + float dist, co[2]; + int width, height; + float u; + float scalex, scaley, aspx, aspy; + + ED_mask_size(C, &width, &height); + ED_mask_aspect(C, &aspx, &aspy); + ED_mask_pixelspace_factor(C, &scalex, &scaley); + + co[0] = normal_co[0] * scalex; + co[1] = normal_co[1] * scaley; + + for (masklay = mask->masklayers.first; masklay; masklay = masklay->next) { + MaskSpline *spline; + + if (masklay->restrictflag & (MASK_RESTRICT_VIEW | MASK_RESTRICT_SELECT)) { + continue; + } + + for (spline = masklay->splines.first; spline; spline = spline->next) { + int i; + + for (i = 0; i < spline->tot_point; i++) { + MaskSplinePoint *cur_point = &spline->points[i]; + float *diff_points; + int tot_diff_point; + + diff_points = BKE_mask_point_segment_diff_with_resolution(spline, cur_point, width, height, + &tot_diff_point); + + if (diff_points) { + int i, tot_feather_point, tot_point; + float *feather_points = NULL, *points; + + if (feather) { + feather_points = BKE_mask_point_segment_feather_diff_with_resolution(spline, cur_point, + width, height, + &tot_feather_point); + + points = feather_points; + tot_point = tot_feather_point; + } + else { + points = diff_points; + tot_point = tot_diff_point; + } + + for (i = 0; i < tot_point - 1; i++) { + float cur_dist, a[2], b[2]; + + a[0] = points[2 * i] * scalex; + a[1] = points[2 * i + 1] * scaley; + + b[0] = points[2 * i + 2] * scalex; + b[1] = points[2 * i + 3] * scaley; + + cur_dist = dist_to_line_segment_v2(co, a, b); + + if (point == NULL || cur_dist < dist) { + if (tangent) + sub_v2_v2v2(tangent, &diff_points[2 * i + 2], &diff_points[2 * i]); + + point_masklay = masklay; + point_spline = spline; + point = cur_point; + dist = cur_dist; + u = (float)i / tot_point; + + } + } + + if (feather_points) + MEM_freeN(feather_points); + + MEM_freeN(diff_points); + } + } + } + } + + if (point && dist < threshold) { + if (masklay_r) + *masklay_r = point_masklay; + + if (spline_r) + *spline_r = point_spline; + + if (point_r) + *point_r = point; + + if (u_r) { + u = BKE_mask_spline_project_co(point_spline, point, u, normal_co); + + *u_r = u; + } + + return TRUE; + } + + if (masklay_r) + *masklay_r = NULL; + + if (spline_r) + *spline_r = NULL; + + if (point_r) + *point_r = NULL; + + return FALSE; +} + +/******************** add vertex *********************/ + +static void setup_vertex_point(bContext *C, Mask *mask, MaskSpline *spline, MaskSplinePoint *new_point, + const float point_co[2], const float tangent[2], const float u, + MaskSplinePoint *reference_point, const short reference_adjacent) +{ + MaskSplinePoint *prev_point = NULL; + MaskSplinePoint *next_point = NULL; + BezTriple *bezt; + int width, height; + float co[3]; + const float len = 20.0; /* default length of handle in pixel space */ + + copy_v2_v2(co, point_co); + co[2] = 0.0f; + + ED_mask_size(C, &width, &height); + + /* point coordinate */ + bezt = &new_point->bezt; + + bezt->h1 = bezt->h2 = HD_ALIGN; + + if (reference_point) { + bezt->h1 = bezt->h2 = MAX2(reference_point->bezt.h2, reference_point->bezt.h1); + } + else if (reference_adjacent) { + if (spline->tot_point != 1) { + int index = (int)(new_point - spline->points); + prev_point = &spline->points[(index - 1) % spline->tot_point]; + next_point = &spline->points[(index + 1) % spline->tot_point]; + + bezt->h1 = bezt->h2 = MAX2(prev_point->bezt.h2, next_point->bezt.h1); + + /* note, we may want to copy other attributes later, radius? pressure? color? */ + } + } + + copy_v3_v3(bezt->vec[0], co); + copy_v3_v3(bezt->vec[1], co); + copy_v3_v3(bezt->vec[2], co); + + /* initial offset for handles */ + if (spline->tot_point == 1) { + /* first point of splien is aligned horizontally */ + bezt->vec[0][0] -= len / width; + bezt->vec[2][0] += len / width; + } + else if (tangent) { + float vec[2]; + + copy_v2_v2(vec, tangent); + + vec[0] *= width; + vec[1] *= height; + + mul_v2_fl(vec, len / len_v2(vec)); + + vec[0] /= width; + vec[1] /= height; + + sub_v2_v2(bezt->vec[0], vec); + add_v2_v2(bezt->vec[2], vec); + + if (reference_adjacent) { + BKE_mask_calc_handle_adjacent_interp(mask, spline, new_point, u); + } + } + else { + + /* calculating auto handles works much nicer */ +#if 0 + /* next points are aligning in the direction of previous/next point */ + MaskSplinePoint *point; + float v1[2], v2[2], vec[2]; + float dir = 1.0f; + + if (new_point == spline->points) { + point = new_point + 1; + dir = -1.0f; + } + else + point = new_point - 1; + + if (spline->tot_point < 3) { + v1[0] = point->bezt.vec[1][0] * width; + v1[1] = point->bezt.vec[1][1] * height; + + v2[0] = new_point->bezt.vec[1][0] * width; + v2[1] = new_point->bezt.vec[1][1] * height; + } + else { + if (new_point == spline->points) { + v1[0] = spline->points[1].bezt.vec[1][0] * width; + v1[1] = spline->points[1].bezt.vec[1][1] * height; + + v2[0] = spline->points[spline->tot_point - 1].bezt.vec[1][0] * width; + v2[1] = spline->points[spline->tot_point - 1].bezt.vec[1][1] * height; + } + else { + v1[0] = spline->points[0].bezt.vec[1][0] * width; + v1[1] = spline->points[0].bezt.vec[1][1] * height; + + v2[0] = spline->points[spline->tot_point - 2].bezt.vec[1][0] * width; + v2[1] = spline->points[spline->tot_point - 2].bezt.vec[1][1] * height; + } + } + + sub_v2_v2v2(vec, v1, v2); + mul_v2_fl(vec, len * dir / len_v2(vec)); + + vec[0] /= width; + vec[1] /= height; + + add_v2_v2(bezt->vec[0], vec); + sub_v2_v2(bezt->vec[2], vec); +#else + BKE_mask_calc_handle_point_auto(mask, spline, new_point, TRUE); + BKE_mask_calc_handle_adjacent_interp(mask, spline, new_point, u); + +#endif + } + + BKE_mask_parent_init(&new_point->parent); + + /* select new point */ + MASKPOINT_SEL_ALL(new_point); + ED_mask_select_flush_all(mask); +} + + +/* **** add extrude vertex **** */ + +static void finSelectedSplinePoint(MaskLayer *masklay, MaskSpline **spline, MaskSplinePoint **point, short check_active) +{ + MaskSpline *cur_spline = masklay->splines.first; + + *spline = NULL; + *point = NULL; + + if (check_active) { + if (masklay->act_spline && masklay->act_point && MASKPOINT_ISSEL_ANY(masklay->act_point)) { + *spline = masklay->act_spline; + *point = masklay->act_point; + return; + } + } + + while (cur_spline) { + int i; + + for (i = 0; i < cur_spline->tot_point; i++) { + MaskSplinePoint *cur_point = &cur_spline->points[i]; + + if (MASKPOINT_ISSEL_ANY(cur_point)) { + if (*spline != NULL && *spline != cur_spline) { + *spline = NULL; + *point = NULL; + return; + } + else if (*point) { + *point = NULL; + } + else { + *spline = cur_spline; + *point = cur_point; + } + } + } + + cur_spline = cur_spline->next; + } +} + +/* **** add subdivide vertex **** */ + +static void mask_spline_add_point_at_index(MaskSpline *spline, int point_index) +{ + MaskSplinePoint *new_point_array; + + new_point_array = MEM_callocN(sizeof(MaskSplinePoint) * (spline->tot_point + 1), "add mask vert points"); + + memcpy(new_point_array, spline->points, sizeof(MaskSplinePoint) * (point_index + 1)); + memcpy(new_point_array + point_index + 2, spline->points + point_index + 1, + sizeof(MaskSplinePoint) * (spline->tot_point - point_index - 1)); + + MEM_freeN(spline->points); + spline->points = new_point_array; + spline->tot_point++; +} + +static int add_vertex_subdivide(bContext *C, Mask *mask, const float co[2]) +{ + MaskLayer *masklay; + MaskSpline *spline; + MaskSplinePoint *point = NULL; + const float threshold = 9; + float tangent[2]; + float u; + + if (find_nearest_diff_point(C, mask, co, threshold, FALSE, &masklay, &spline, &point, &u, tangent)) { + MaskSplinePoint *new_point; + int point_index = point - spline->points; + + ED_mask_select_toggle_all(mask, SEL_DESELECT); + + mask_spline_add_point_at_index(spline, point_index); + + new_point = &spline->points[point_index + 1]; + + setup_vertex_point(C, mask, spline, new_point, co, tangent, u, NULL, TRUE); + + /* TODO - we could pass the spline! */ + BKE_mask_layer_shape_changed_add(masklay, BKE_mask_layer_shape_spline_to_index(masklay, spline) + point_index + 1, TRUE, TRUE); + + masklay->act_point = new_point; + + WM_event_add_notifier(C, NC_MASK | NA_EDITED, mask); + + return TRUE; + } + + return FALSE; +} + +static int add_vertex_extrude(bContext *C, Mask *mask, MaskLayer *masklay, const float co[2]) +{ + MaskSpline *spline; + MaskSplinePoint *point; + MaskSplinePoint *new_point = NULL, *ref_point = NULL; + + /* check on which side we want to add the point */ + int point_index; + float tangent_point[2]; + float tangent_co[2]; + int do_cyclic_correct = FALSE; + int do_recalc_src = FALSE; /* when extruding from endpoints only */ + int do_prev; /* use prev point rather then next?? */ + + if (!masklay) { + return FALSE; + } + else { + finSelectedSplinePoint(masklay, &spline, &point, TRUE); + } + + ED_mask_select_toggle_all(mask, SEL_DESELECT); + + point_index = (point - spline->points); + + MASKPOINT_DESEL_ALL(point); + + if ((spline->flag & MASK_SPLINE_CYCLIC) || + (point_index > 0 && point_index != spline->tot_point - 1)) + { + BKE_mask_calc_tangent_polyline(mask, spline, point, tangent_point); + sub_v2_v2v2(tangent_co, co, point->bezt.vec[1]); + + if (dot_v2v2(tangent_point, tangent_co) < 0.0f) { + do_prev = TRUE; + } + else { + do_prev = FALSE; + } + } + else if (((spline->flag & MASK_SPLINE_CYCLIC) == 0) && (point_index == 0)) { + do_prev = TRUE; + do_recalc_src = TRUE; + } + else if (((spline->flag & MASK_SPLINE_CYCLIC) == 0) && (point_index == spline->tot_point - 1)) { + do_prev = FALSE; + do_recalc_src = TRUE; + } + else { + /* should never get here */ + BLI_assert(0); + } + + /* use the point before the active one */ + if (do_prev) { + point_index--; + if (point_index < 0) { + point_index += spline->tot_point; /* wrap index */ + if ((spline->flag & MASK_SPLINE_CYCLIC) == 0) { + do_cyclic_correct = TRUE; + point_index = 0; + } + } + } + +// print_v2("", tangent_point); +// printf("%d\n", point_index); + + mask_spline_add_point_at_index(spline, point_index); + + if (do_cyclic_correct) { + ref_point = &spline->points[point_index + 1]; + new_point = &spline->points[point_index]; + *ref_point = *new_point; + memset(new_point, 0, sizeof(*new_point)); + } + else { + ref_point = &spline->points[point_index]; + new_point = &spline->points[point_index + 1]; + } + + masklay->act_point = new_point; + + setup_vertex_point(C, mask, spline, new_point, co, NULL, 0.5f, ref_point, FALSE); + + if (masklay->splines_shapes.first) { + point_index = (((int)(new_point - spline->points) + 0) % spline->tot_point); + BKE_mask_layer_shape_changed_add(masklay, BKE_mask_layer_shape_spline_to_index(masklay, spline) + point_index, TRUE, TRUE); + } + + if (do_recalc_src) { + /* TODO, update keyframes in time */ + BKE_mask_calc_handle_point_auto(mask, spline, ref_point, FALSE); + } + + WM_event_add_notifier(C, NC_MASK | NA_EDITED, mask); + + return TRUE; +} + +static int add_vertex_new(bContext *C, Mask *mask, MaskLayer *masklay, const float co[2]) +{ + MaskSpline *spline; + MaskSplinePoint *point; + MaskSplinePoint *new_point = NULL, *ref_point = NULL; + + if (!masklay) { + /* if there's no masklay currently operationg on, create new one */ + masklay = BKE_mask_layer_new(mask, ""); + mask->masklay_act = mask->masklay_tot - 1; + spline = NULL; + point = NULL; + } + else { + finSelectedSplinePoint(masklay, &spline, &point, TRUE); + } + + ED_mask_select_toggle_all(mask, SEL_DESELECT); + + if (!spline) { + /* no selected splines in active masklay, create new spline */ + spline = BKE_mask_spline_add(masklay); + } + + masklay->act_spline = spline; + new_point = spline->points; + + masklay->act_point = new_point; + + setup_vertex_point(C, mask, spline, new_point, co, NULL, 0.5f, ref_point, FALSE); + + { + int point_index = (((int)(new_point - spline->points) + 0) % spline->tot_point); + BKE_mask_layer_shape_changed_add(masklay, BKE_mask_layer_shape_spline_to_index(masklay, spline) + point_index, TRUE, TRUE); + } + + WM_event_add_notifier(C, NC_MASK | NA_EDITED, mask); + + return TRUE; +} + +static int add_vertex_exec(bContext *C, wmOperator *op) +{ + Mask *mask = CTX_data_edit_mask(C); + MaskLayer *masklay; + + float co[2]; + + masklay = BKE_mask_layer_active(mask); + + if (masklay && masklay->restrictflag & (MASK_RESTRICT_VIEW | MASK_RESTRICT_SELECT)) { + masklay = NULL; + } + + RNA_float_get_array(op->ptr, "location", co); + + if (masklay && masklay->act_point && MASKPOINT_ISSEL_ANY(masklay->act_point)) { + + /* cheap trick - double click for cyclic */ + MaskSpline *spline = masklay->act_spline; + MaskSplinePoint *point = masklay->act_point; + + int is_sta = (point == spline->points); + int is_end = (point == &spline->points[spline->tot_point - 1]); + + /* then check are we overlapping the mouse */ + if ((is_sta || is_end) && equals_v2v2(co, point->bezt.vec[1])) { + if (spline->flag & MASK_SPLINE_CYCLIC) { + /* nothing to do */ + return OPERATOR_CANCELLED; + } + else { + /* recalc the connecting point as well to make a nice even curve */ + MaskSplinePoint *point_other = is_end ? spline->points : &spline->points[spline->tot_point - 1]; + spline->flag |= MASK_SPLINE_CYCLIC; + + /* TODO, update keyframes in time */ + BKE_mask_calc_handle_point_auto(mask, spline, point, FALSE); + BKE_mask_calc_handle_point_auto(mask, spline, point_other, FALSE); + + /* TODO: only update this spline */ + BKE_mask_update_display(mask, CTX_data_scene(C)->r.cfra); + + WM_event_add_notifier(C, NC_MASK | NA_EDITED, mask); + return OPERATOR_FINISHED; + } + } + + if (!add_vertex_subdivide(C, mask, co)) { + if (!add_vertex_extrude(C, mask, masklay, co)) { + return OPERATOR_CANCELLED; + } + } + } + else { + if (!add_vertex_subdivide(C, mask, co)) { + if (!add_vertex_new(C, mask, masklay, co)) { + return OPERATOR_CANCELLED; + } + } + } + + /* TODO: only update this spline */ + BKE_mask_update_display(mask, CTX_data_scene(C)->r.cfra); + + return OPERATOR_FINISHED; +} + +static int add_vertex_invoke(bContext *C, wmOperator *op, wmEvent *event) +{ + float co[2]; + + ED_mask_mouse_pos(C, event, co); + + RNA_float_set_array(op->ptr, "location", co); + + return add_vertex_exec(C, op); +} + +void MASK_OT_add_vertex(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Add Vertex"; + ot->description = "Add vertex to active spline"; + ot->idname = "MASK_OT_add_vertex"; + + /* api callbacks */ + ot->exec = add_vertex_exec; + ot->invoke = add_vertex_invoke; + ot->poll = ED_maskediting_mask_poll; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + /* properties */ + RNA_def_float_vector(ot->srna, "location", 2, NULL, -FLT_MIN, FLT_MAX, + "Location", "Location of vertex in normalized space", -1.0f, 1.0f); +} + +/******************** add feather vertex *********************/ + +static int add_feather_vertex_exec(bContext *C, wmOperator *op) +{ + Mask *mask = CTX_data_edit_mask(C); + MaskLayer *masklay; + MaskSpline *spline; + MaskSplinePoint *point = NULL; + const float threshold = 9; + float co[2], u; + + RNA_float_get_array(op->ptr, "location", co); + + point = ED_mask_point_find_nearest(C, mask, co, threshold, NULL, NULL, NULL, NULL); + if (point) + return OPERATOR_FINISHED; + + if (find_nearest_diff_point(C, mask, co, threshold, TRUE, &masklay, &spline, &point, &u, NULL)) { + Scene *scene = CTX_data_scene(C); + float w = BKE_mask_point_weight(spline, point, u); + + BKE_mask_point_add_uw(point, u, w); + + BKE_mask_update_display(mask, scene->r.cfra); + + WM_event_add_notifier(C, NC_MASK | NA_EDITED, mask); + + DAG_id_tag_update(&mask->id, 0); + + return OPERATOR_FINISHED; + } + + return OPERATOR_CANCELLED; +} + +static int add_feather_vertex_invoke(bContext *C, wmOperator *op, wmEvent *event) +{ + float co[2]; + + ED_mask_mouse_pos(C, event, co); + + RNA_float_set_array(op->ptr, "location", co); + + return add_feather_vertex_exec(C, op); +} + +void MASK_OT_add_feather_vertex(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Add feather Vertex"; + ot->description = "Add vertex to feather"; + ot->idname = "MASK_OT_add_feather_vertex"; + + /* api callbacks */ + ot->exec = add_feather_vertex_exec; + ot->invoke = add_feather_vertex_invoke; + ot->poll = ED_maskediting_mask_poll; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + /* properties */ + RNA_def_float_vector(ot->srna, "location", 2, NULL, -FLT_MIN, FLT_MAX, + "Location", "Location of vertex in normalized space", -1.0f, 1.0f); +} diff --git a/source/blender/editors/mask/mask_draw.c b/source/blender/editors/mask/mask_draw.c new file mode 100644 index 00000000000..e580088e25b --- /dev/null +++ b/source/blender/editors/mask/mask_draw.c @@ -0,0 +1,428 @@ +/* + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2012 Blender Foundation. + * All rights reserved. + * + * + * Contributor(s): Blender Foundation, + * Sergey Sharybin + * + * ***** END GPL LICENSE BLOCK ***** + */ + +/** \file blender/editors/mask/mask_draw.c + * \ingroup edmask + */ + +#include "MEM_guardedalloc.h" + +#include "BLI_utildefines.h" +#include "BLI_math.h" + +#include "BKE_context.h" +#include "BKE_mask.h" + +#include "DNA_mask_types.h" +#include "DNA_object_types.h" /* SELECT */ + +#include "ED_mask.h" + +#include "BIF_gl.h" +#include "BIF_glutil.h" + +#include "UI_resources.h" + +#include "mask_intern.h" /* own include */ + +static void mask_spline_color_get(MaskLayer *masklay, MaskSpline *spline, const int is_sel, + unsigned char r_rgb[4]) +{ + if (is_sel) { + if (masklay->act_spline == spline) { + r_rgb[0] = r_rgb[1] = r_rgb[2] = 255; + } + else { + r_rgb[0] = 255; + r_rgb[1] = r_rgb[2] = 0; + } + } + else { + r_rgb[0] = 128; + r_rgb[1] = r_rgb[2] = 0; + } + + r_rgb[3] = 255; +} + +static void mask_spline_feather_color_get(MaskLayer *UNUSED(masklay), MaskSpline *UNUSED(spline), const int is_sel, + unsigned char r_rgb[4]) +{ + if (is_sel) { + r_rgb[1] = 255; + r_rgb[0] = r_rgb[2] = 0; + } + else { + r_rgb[1] = 128; + r_rgb[0] = r_rgb[2] = 0; + } + + r_rgb[3] = 255; +} + +#if 0 +static void draw_spline_parents(MaskLayer *UNUSED(masklay), MaskSpline *spline) +{ + int i; + MaskSplinePoint *points_array = BKE_mask_spline_point_array(spline); + + if (!spline->tot_point) + return; + + glColor3ub(0, 0, 0); + glEnable(GL_LINE_STIPPLE); + glLineStipple(1, 0xAAAA); + + glBegin(GL_LINES); + + for (i = 0; i < spline->tot_point; i++) { + MaskSplinePoint *point = &points_array[i]; + BezTriple *bezt = &point->bezt; + + if (point->parent.flag & MASK_PARENT_ACTIVE) { + glVertex2f(bezt->vec[1][0], + bezt->vec[1][1]); + + glVertex2f(bezt->vec[1][0] - point->parent.offset[0], + bezt->vec[1][1] - point->parent.offset[1]); + } + } + + glEnd(); + + glDisable(GL_LINE_STIPPLE); +} +#endif + +/* return non-zero if spline is selected */ +static void draw_spline_points(MaskLayer *masklay, MaskSpline *spline) +{ + const int is_spline_sel = (spline->flag & SELECT) && (masklay->restrictflag & MASK_RESTRICT_SELECT) == 0; + unsigned char rgb_spline[4]; + MaskSplinePoint *points_array = BKE_mask_spline_point_array(spline); + + int i, hsize, tot_feather_point; + float (*feather_points)[2], (*fp)[2]; + + if (!spline->tot_point) + return; + + hsize = UI_GetThemeValuef(TH_HANDLE_VERTEX_SIZE); + + glPointSize(hsize); + + mask_spline_color_get(masklay, spline, is_spline_sel, rgb_spline); + + /* feather points */ + feather_points = fp = BKE_mask_spline_feather_points(spline, &tot_feather_point); + for (i = 0; i < spline->tot_point; i++) { + + /* watch it! this is intentionally not the deform array, only check for sel */ + MaskSplinePoint *point = &spline->points[i]; + + int j; + + for (j = 0; j < point->tot_uw + 1; j++) { + int sel = FALSE; + + if (j == 0) { + sel = MASKPOINT_ISSEL_ANY(point); + } + else { + sel = point->uw[j - 1].flag & SELECT; + } + + if (sel) { + if (point == masklay->act_point) + glColor3f(1.0f, 1.0f, 1.0f); + else + glColor3f(1.0f, 1.0f, 0.0f); + } + else { + glColor3f(0.5f, 0.5f, 0.0f); + } + + glBegin(GL_POINTS); + glVertex2fv(*fp); + glEnd(); + + fp++; + } + } + MEM_freeN(feather_points); + + /* control points */ + for (i = 0; i < spline->tot_point; i++) { + + /* watch it! this is intentionally not the deform array, only check for sel */ + MaskSplinePoint *point = &spline->points[i]; + MaskSplinePoint *point_deform = &points_array[i]; + BezTriple *bezt = &point_deform->bezt; + + float handle[2]; + float *vert = bezt->vec[1]; + int has_handle = BKE_mask_point_has_handle(point); + + BKE_mask_point_handle(point_deform, handle); + + /* draw handle segment */ + if (has_handle) { + glColor3ubv(rgb_spline); + + glBegin(GL_LINES); + glVertex3fv(vert); + glVertex3fv(handle); + glEnd(); + } + + /* draw CV point */ + if (MASKPOINT_ISSEL_KNOT(point)) { + if (point == masklay->act_point) + glColor3f(1.0f, 1.0f, 1.0f); + else + glColor3f(1.0f, 1.0f, 0.0f); + } + else + glColor3f(0.5f, 0.5f, 0.0f); + + glBegin(GL_POINTS); + glVertex3fv(vert); + glEnd(); + + /* draw handle points */ + if (has_handle) { + if (MASKPOINT_ISSEL_HANDLE(point)) { + if (point == masklay->act_point) + glColor3f(1.0f, 1.0f, 1.0f); + else + glColor3f(1.0f, 1.0f, 0.0f); + } + else { + glColor3f(0.5f, 0.5f, 0.0f); + } + + glBegin(GL_POINTS); + glVertex3fv(handle); + glEnd(); + } + } + + glPointSize(1.0f); +} + +/* #define USE_XOR */ + +static void mask_draw_curve_type(MaskSpline *spline, float (*points)[2], int tot_point, + const short is_feather, const short is_smooth, + const unsigned char rgb_spline[4], const char draw_type) +{ + const int draw_method = (spline->flag & MASK_SPLINE_CYCLIC) ? GL_LINE_LOOP : GL_LINE_STRIP; + unsigned char rgb_tmp[4]; + + glEnableClientState(GL_VERTEX_ARRAY); + glVertexPointer(2, GL_FLOAT, 0, points); + + switch (draw_type) { + + case MASK_DT_OUTLINE: + glLineWidth(3); + cpack(0x0); + + glDrawArrays(draw_method, 0, tot_point); + + glLineWidth(1); + glColor4ubv(rgb_spline); + glDrawArrays(draw_method, 0, tot_point); + + break; + + case MASK_DT_DASH: + default: + glEnable(GL_LINE_STIPPLE); + +#ifdef USE_XOR + glEnable(GL_COLOR_LOGIC_OP); + glLogicOp(GL_OR); +#endif + glColor4ubv(rgb_spline); + glLineStipple(3, 0xaaaa); + glEnableClientState(GL_VERTEX_ARRAY); + glVertexPointer(2, GL_FLOAT, 0, points); + glDrawArrays(draw_method, 0, tot_point); + +#ifdef USE_XOR + glDisable(GL_COLOR_LOGIC_OP); +#endif + glColor4ub(0, 0, 0, 255); + glLineStipple(3, 0x5555); + glDrawArrays(draw_method, 0, tot_point); + + glDisable(GL_LINE_STIPPLE); + break; + + + case MASK_DT_BLACK: + case MASK_DT_WHITE: + if (draw_type == MASK_DT_BLACK) { rgb_tmp[0] = rgb_tmp[1] = rgb_tmp[2] = 0; } + else { rgb_tmp[0] = rgb_tmp[1] = rgb_tmp[2] = 255; } + /* alpha values seem too low but gl draws many points that compensate for it */ + if (is_feather) { rgb_tmp[3] = 64; } + else { rgb_tmp[3] = 128; } + + if (is_feather) { + rgb_tmp[0] = (unsigned char)(((short)rgb_tmp[0] + (short)rgb_spline[0]) / 2); + rgb_tmp[1] = (unsigned char)(((short)rgb_tmp[1] + (short)rgb_spline[1]) / 2); + rgb_tmp[2] = (unsigned char)(((short)rgb_tmp[2] + (short)rgb_spline[2]) / 2); + } + + if (is_smooth == FALSE && is_feather) { + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + } + + glColor4ubv(rgb_tmp); + glEnableClientState(GL_VERTEX_ARRAY); + glVertexPointer(2, GL_FLOAT, 0, points); + glDrawArrays(draw_method, 0, tot_point); + + glDrawArrays(draw_method, 0, tot_point); + + if (is_smooth == FALSE && is_feather) { + glDisable(GL_BLEND); + } + + break; + } + + glDisableClientState(GL_VERTEX_ARRAY); + +} + +static void draw_spline_curve(MaskLayer *masklay, MaskSpline *spline, + const char draw_flag, const char draw_type, + int width, int height) +{ + unsigned char rgb_tmp[4]; + + const short is_spline_sel = (spline->flag & SELECT) && (masklay->restrictflag & MASK_RESTRICT_SELECT) == 0; + const short is_smooth = (draw_flag & MASK_DRAWFLAG_SMOOTH); + + int tot_diff_point; + float (*diff_points)[2]; + + int tot_feather_point; + float (*feather_points)[2]; + + diff_points = BKE_mask_spline_differentiate_with_resolution(spline, width, height, &tot_diff_point); + + if (!diff_points) + return; + + if (is_smooth) { + glEnable(GL_LINE_SMOOTH); + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + } + + feather_points = BKE_mask_spline_feather_differentiated_points_with_resolution(spline, width, height, &tot_feather_point); + + /* draw feather */ + mask_spline_feather_color_get(masklay, spline, is_spline_sel, rgb_tmp); + mask_draw_curve_type(spline, feather_points, tot_feather_point, + TRUE, is_smooth, + rgb_tmp, draw_type); + MEM_freeN(feather_points); + + /* draw main curve */ + mask_spline_color_get(masklay, spline, is_spline_sel, rgb_tmp); + mask_draw_curve_type(spline, diff_points, tot_diff_point, + FALSE, is_smooth, + rgb_tmp, draw_type); + MEM_freeN(diff_points); + + if (draw_flag & MASK_DRAWFLAG_SMOOTH) { + glDisable(GL_LINE_SMOOTH); + glDisable(GL_BLEND); + } + + (void)draw_type; +} + +static void draw_masklays(Mask *mask, const char draw_flag, const char draw_type, + int width, int height) +{ + MaskLayer *masklay; + + for (masklay = mask->masklayers.first; masklay; masklay = masklay->next) { + MaskSpline *spline; + + if (masklay->restrictflag & MASK_RESTRICT_VIEW) { + continue; + } + + for (spline = masklay->splines.first; spline; spline = spline->next) { + + /* draw curve itself first... */ + draw_spline_curve(masklay, spline, draw_flag, draw_type, width, height); + +// draw_spline_parents(masklay, spline); + + if (!(masklay->restrictflag & MASK_RESTRICT_SELECT)) { + /* ...and then handles over the curve so they're nicely visible */ + draw_spline_points(masklay, spline); + } + + /* show undeform for testing */ + if (0) { + void *back = spline->points_deform; + + spline->points_deform = NULL; + draw_spline_curve(masklay, spline, draw_flag, draw_type, width, height); +// draw_spline_parents(masklay, spline); + draw_spline_points(masklay, spline); + spline->points_deform = back; + } + } + } +} + +void ED_mask_draw(const bContext *C, + const char draw_flag, const char draw_type) +{ + Mask *mask = CTX_data_edit_mask(C); + int width, height; + + if (!mask) + return; + + /* TODO: for now, in the future better to make sure all utility functions + * are using const specifier for non-changing pointers + */ + ED_mask_size((bContext *)C, &width, &height); + + draw_masklays(mask, draw_flag, draw_type, width, height); +} diff --git a/source/blender/editors/mask/mask_edit.c b/source/blender/editors/mask/mask_edit.c new file mode 100644 index 00000000000..0b8551ddd4c --- /dev/null +++ b/source/blender/editors/mask/mask_edit.c @@ -0,0 +1,327 @@ +/* + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2012 Blender Foundation. + * All rights reserved. + * + * + * Contributor(s): Blender Foundation, + * Sergey Sharybin + * + * ***** END GPL LICENSE BLOCK ***** + */ + +/** \file blender/editors/mask/mask_ops.c + * \ingroup edmask + */ + +#include "MEM_guardedalloc.h" + +#include "BLI_utildefines.h" +#include "BLI_math.h" + +#include "BKE_context.h" +#include "BKE_mask.h" + +#include "WM_api.h" +#include "WM_types.h" + +#include "ED_screen.h" +#include "ED_mask.h" +#include "ED_object.h" /* ED_keymap_proportional_maskmode only */ +#include "ED_clip.h" +#include "ED_transform.h" + +#include "RNA_access.h" + +#include "mask_intern.h" /* own include */ + +/********************** generic poll functions *********************/ + +int ED_maskediting_poll(bContext *C) +{ + SpaceClip *sc = CTX_wm_space_clip(C); + + if (sc) { + return ED_space_clip_maskediting_poll(C); + } + + return FALSE; +} + +int ED_maskediting_mask_poll(bContext *C) +{ + SpaceClip *sc = CTX_wm_space_clip(C); + + if (sc) { + return ED_space_clip_maskediting_mask_poll(C); + } + + return FALSE; +} + +/********************** registration *********************/ + +void ED_mask_mouse_pos(bContext *C, wmEvent *event, float co[2]) +{ + SpaceClip *sc = CTX_wm_space_clip(C); + + if (sc) { + ED_clip_mouse_pos(C, event, co); + BKE_mask_coord_from_movieclip(sc->clip, &sc->user, co, co); + } + else { + /* possible other spaces from which mask editing is available */ + zero_v2(co); + } +} + +/* input: x/y - mval space + * output: xr/yr - mask point space */ +void ED_mask_point_pos(bContext *C, float x, float y, float *xr, float *yr) +{ + SpaceClip *sc = CTX_wm_space_clip(C); + float co[2]; + + if (sc) { + ED_clip_point_stable_pos(C, x, y, &co[0], &co[1]); + BKE_mask_coord_from_movieclip(sc->clip, &sc->user, co, co); + } + else { + /* possible other spaces from which mask editing is available */ + zero_v2(co); + } + + *xr = co[0]; + *yr = co[1]; +} + +void ED_mask_point_pos__reverse(bContext *C, float x, float y, float *xr, float *yr) +{ + SpaceClip *sc = CTX_wm_space_clip(C); + ARegion *ar = CTX_wm_region(C); + + float co[2]; + + if (sc && ar) { + co[0] = x; + co[1] = y; + BKE_mask_coord_to_movieclip(sc->clip, &sc->user, co, co); + ED_clip_point_stable_pos__reverse(sc, ar, co, co); + } + else { + /* possible other spaces from which mask editing is available */ + zero_v2(co); + } + + *xr = co[0]; + *yr = co[1]; +} + +void ED_mask_size(bContext *C, int *width, int *height) +{ + SpaceClip *sc = CTX_wm_space_clip(C); + + if (sc) { + ED_space_clip_mask_size(sc, width, height); + } + else { + /* possible other spaces from which mask editing is available */ + *width = 0; + *height = 0; + } +} + +void ED_mask_aspect(bContext *C, float *aspx, float *aspy) +{ + SpaceClip *sc = CTX_wm_space_clip(C); + + if (sc) { + ED_space_clip_mask_aspect(sc, aspx, aspy); + } + else { + /* possible other spaces from which mask editing is available */ + *aspx = 1.0f; + *aspy = 1.0f; + } +} + +void ED_mask_pixelspace_factor(bContext *C, float *scalex, float *scaley) +{ + SpaceClip *sc = CTX_wm_space_clip(C); + + if (sc) { + ARegion *ar = CTX_wm_region(C); + int width, height; + float zoomx, zoomy, aspx, aspy; + + ED_space_clip_size(sc, &width, &height); + ED_space_clip_zoom(sc, ar, &zoomx, &zoomy); + ED_space_clip_aspect(sc, &aspx, &aspy); + + *scalex = ((float)width * aspx) * zoomx; + *scaley = ((float)height * aspy) * zoomy; + } + else { + /* possible other spaces from which mask editing is available */ + *scalex = 1.0f; + *scaley = 1.0f; + } +} + +/********************** registration *********************/ + +void ED_operatortypes_mask(void) +{ + WM_operatortype_append(MASK_OT_new); + + /* mask layers */ + WM_operatortype_append(MASK_OT_layer_new); + WM_operatortype_append(MASK_OT_layer_remove); + + /* add */ + WM_operatortype_append(MASK_OT_add_vertex); + WM_operatortype_append(MASK_OT_add_feather_vertex); + + /* geometry */ + WM_operatortype_append(MASK_OT_switch_direction); + WM_operatortype_append(MASK_OT_delete); + + /* select */ + WM_operatortype_append(MASK_OT_select); + WM_operatortype_append(MASK_OT_select_all); + WM_operatortype_append(MASK_OT_select_border); + WM_operatortype_append(MASK_OT_select_lasso); + WM_operatortype_append(MASK_OT_select_circle); + WM_operatortype_append(MASK_OT_select_linked_pick); + WM_operatortype_append(MASK_OT_select_linked); + + /* hide/reveal */ + WM_operatortype_append(MASK_OT_hide_view_clear); + WM_operatortype_append(MASK_OT_hide_view_set); + + /* shape */ + WM_operatortype_append(MASK_OT_slide_point); + WM_operatortype_append(MASK_OT_cyclic_toggle); + WM_operatortype_append(MASK_OT_handle_type_set); + + /* relationships */ + WM_operatortype_append(MASK_OT_parent_set); + WM_operatortype_append(MASK_OT_parent_clear); + + /* shapekeys */ + WM_operatortype_append(MASK_OT_shape_key_insert); + WM_operatortype_append(MASK_OT_shape_key_clear); +} + +void ED_keymap_mask(wmKeyConfig *keyconf) +{ + wmKeyMap *keymap; + wmKeyMapItem *kmi; + + keymap = WM_keymap_find(keyconf, "Mask Editor", 0, 0); + keymap->poll = ED_maskediting_poll; + + WM_keymap_add_item(keymap, "MASK_OT_new", NKEY, KM_PRESS, KM_ALT, 0); + + /* mask mode supports PET now */ + ED_keymap_proportional_cycle(keyconf, keymap); + ED_keymap_proportional_maskmode(keyconf, keymap); + + /* geometry */ + WM_keymap_add_item(keymap, "MASK_OT_add_vertex_slide", LEFTMOUSE, KM_PRESS, KM_CTRL, 0); + WM_keymap_add_item(keymap, "MASK_OT_add_feather_vertex_slide", LEFTMOUSE, KM_PRESS, KM_SHIFT, 0); + WM_keymap_add_item(keymap, "MASK_OT_delete", XKEY, KM_PRESS, 0, 0); + WM_keymap_add_item(keymap, "MASK_OT_delete", DELKEY, KM_PRESS, 0, 0); + + /* selection */ + kmi = WM_keymap_add_item(keymap, "MASK_OT_select", SELECTMOUSE, KM_PRESS, 0, 0); + RNA_boolean_set(kmi->ptr, "extend", FALSE); + RNA_boolean_set(kmi->ptr, "deselect", FALSE); + RNA_boolean_set(kmi->ptr, "toggle", FALSE); + kmi = WM_keymap_add_item(keymap, "MASK_OT_select", SELECTMOUSE, KM_PRESS, KM_SHIFT, 0); + RNA_boolean_set(kmi->ptr, "extend", FALSE); + RNA_boolean_set(kmi->ptr, "deselect", FALSE); + RNA_boolean_set(kmi->ptr, "toggle", TRUE); + + kmi = WM_keymap_add_item(keymap, "MASK_OT_select_all", AKEY, KM_PRESS, 0, 0); + RNA_enum_set(kmi->ptr, "action", SEL_TOGGLE); + kmi = WM_keymap_add_item(keymap, "MASK_OT_select_all", IKEY, KM_PRESS, KM_CTRL, 0); + RNA_enum_set(kmi->ptr, "action", SEL_INVERT); + + WM_keymap_add_item(keymap, "MASK_OT_select_linked", LKEY, KM_PRESS, KM_CTRL, 0); + kmi = WM_keymap_add_item(keymap, "MASK_OT_select_linked_pick", LKEY, KM_PRESS, 0, 0); + RNA_boolean_set(kmi->ptr, "deselect", FALSE); + kmi = WM_keymap_add_item(keymap, "MASK_OT_select_linked_pick", LKEY, KM_PRESS, KM_SHIFT, 0); + RNA_boolean_set(kmi->ptr, "deselect", TRUE); + + WM_keymap_add_item(keymap, "MASK_OT_select_border", BKEY, KM_PRESS, 0, 0); + WM_keymap_add_item(keymap, "MASK_OT_select_circle", CKEY, KM_PRESS, 0, 0); + + kmi = WM_keymap_add_item(keymap, "MASK_OT_select_lasso", EVT_TWEAK_A, KM_ANY, KM_CTRL | KM_ALT, 0); + RNA_boolean_set(kmi->ptr, "deselect", FALSE); + kmi = WM_keymap_add_item(keymap, "MASK_OT_select_lasso", EVT_TWEAK_A, KM_ANY, KM_CTRL | KM_SHIFT | KM_ALT, 0); + RNA_boolean_set(kmi->ptr, "deselect", TRUE); + + /* hide/reveal */ + WM_keymap_add_item(keymap, "MASK_OT_hide_view_clear", HKEY, KM_PRESS, KM_ALT, 0); + kmi = WM_keymap_add_item(keymap, "MASK_OT_hide_view_set", HKEY, KM_PRESS, 0, 0); + RNA_boolean_set(kmi->ptr, "unselected", FALSE); + + kmi = WM_keymap_add_item(keymap, "MASK_OT_hide_view_set", HKEY, KM_PRESS, KM_SHIFT, 0); + RNA_boolean_set(kmi->ptr, "unselected", TRUE); + + /* select clip while in maker view, + * this matches View3D functionality where you can select an + * object while in editmode to allow vertex parenting */ + kmi = WM_keymap_add_item(keymap, "CLIP_OT_select", SELECTMOUSE, KM_PRESS, KM_CTRL, 0); + RNA_boolean_set(kmi->ptr, "extend", FALSE); + + /* shape */ + WM_keymap_add_item(keymap, "MASK_OT_cyclic_toggle", CKEY, KM_PRESS, KM_ALT, 0); + WM_keymap_add_item(keymap, "MASK_OT_slide_point", LEFTMOUSE, KM_PRESS, 0, 0); + WM_keymap_add_item(keymap, "MASK_OT_handle_type_set", VKEY, KM_PRESS, 0, 0); + + /* relationships */ + WM_keymap_add_item(keymap, "MASK_OT_parent_set", PKEY, KM_PRESS, KM_CTRL, 0); + WM_keymap_add_item(keymap, "MASK_OT_parent_clear", PKEY, KM_PRESS, KM_ALT, 0); + + WM_keymap_add_item(keymap, "MASK_OT_shape_key_insert", IKEY, KM_PRESS, 0, 0); + WM_keymap_add_item(keymap, "MASK_OT_shape_key_clear", IKEY, KM_PRESS, KM_ALT, 0); + + + transform_keymap_for_space(keyconf, keymap, SPACE_CLIP); +} + +void ED_operatormacros_mask(void) +{ + /* XXX: just for sample */ + wmOperatorType *ot; + wmOperatorTypeMacro *otmacro; + + ot = WM_operatortype_append_macro("MASK_OT_add_vertex_slide", "Add Vertex and Slide", "Add new vertex and slide it", OPTYPE_UNDO | OPTYPE_REGISTER); + ot->description = "Add new vertex and slide it"; + WM_operatortype_macro_define(ot, "MASK_OT_add_vertex"); + otmacro = WM_operatortype_macro_define(ot, "TRANSFORM_OT_translate"); + RNA_boolean_set(otmacro->ptr, "release_confirm", TRUE); + + ot = WM_operatortype_append_macro("MASK_OT_add_feather_vertex_slide", "Add Feather Vertex and Slide", "Add new vertex to feater and slide it", OPTYPE_UNDO | OPTYPE_REGISTER); + ot->description = "Add new feather vertex and slide it"; + WM_operatortype_macro_define(ot, "MASK_OT_add_feather_vertex"); + otmacro = WM_operatortype_macro_define(ot, "MASK_OT_slide_point"); + RNA_boolean_set(otmacro->ptr, "slide_feather", TRUE); +} diff --git a/source/blender/editors/mask/mask_intern.h b/source/blender/editors/mask/mask_intern.h new file mode 100644 index 00000000000..70f13abd44f --- /dev/null +++ b/source/blender/editors/mask/mask_intern.h @@ -0,0 +1,111 @@ +/* + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2011 Blender Foundation. + * All rights reserved. + * + * + * Contributor(s): Blender Foundation, + * Sergey Sharybin + * + * ***** END GPL LICENSE BLOCK ***** + */ + +/** \file blender/editors/mask/mask_intern.h + * \ingroup spclip + */ + +#ifndef __MASK_INTERN_H__ +#define __MASK_INTERN_H__ + +struct bContext; +struct wmEvent; +struct wmOperatorType; + +/* internal exports only */ + +/* mask_add.c */ +void MASK_OT_add_vertex(struct wmOperatorType *ot); +void MASK_OT_add_feather_vertex(struct wmOperatorType *ot); + +/* mask_ops.c */ +void MASK_OT_new(struct wmOperatorType *ot); +void MASK_OT_layer_new(struct wmOperatorType *ot); +void MASK_OT_layer_remove(struct wmOperatorType *ot); +void MASK_OT_cyclic_toggle(struct wmOperatorType *ot); + +void MASK_OT_slide_point(struct wmOperatorType *ot); + +void MASK_OT_delete(struct wmOperatorType *ot); + +void MASK_OT_hide_view_clear(struct wmOperatorType *ot); +void MASK_OT_hide_view_set(struct wmOperatorType *ot); +void MASK_OT_switch_direction(struct wmOperatorType *ot); + +void MASK_OT_handle_type_set(struct wmOperatorType *ot); + +int ED_mask_feather_find_nearest( + struct bContext *C, struct Mask *mask, float normal_co[2], int threshold, + struct MaskLayer **masklay_r, struct MaskSpline **spline_r, struct MaskSplinePoint **point_r, + struct MaskSplinePointUW **uw_r, float *score); + +struct MaskSplinePoint *ED_mask_point_find_nearest( + struct bContext *C, struct Mask *mask, float normal_co[2], int threshold, + struct MaskLayer **masklay_r, struct MaskSpline **spline_r, int *is_handle_r, + float *score); + +/* mask_relationships.c */ +void MASK_OT_parent_set(struct wmOperatorType *ot); +void MASK_OT_parent_clear(struct wmOperatorType *ot); + +/* mask_select.c */ +void MASK_OT_select(struct wmOperatorType *ot); +void MASK_OT_select_all(struct wmOperatorType *ot); + +void MASK_OT_select_border(struct wmOperatorType *ot); +void MASK_OT_select_lasso(struct wmOperatorType *ot); +void MASK_OT_select_circle(struct wmOperatorType *ot); +void MASK_OT_select_linked_pick(struct wmOperatorType *ot); +void MASK_OT_select_linked(struct wmOperatorType *ot); + +int ED_mask_spline_select_check(struct MaskSpline *spline); +int ED_mask_layer_select_check(struct MaskLayer *masklay); +int ED_mask_select_check(struct Mask *mask); + +void ED_mask_spline_select_set(struct MaskSpline *spline, const short do_select); +void ED_mask_layer_select_set(struct MaskLayer *masklay, const short do_select); +void ED_mask_select_toggle_all(struct Mask *mask, int action); +void ED_mask_select_flush_all(struct Mask *mask); + +/* mask_editor.c */ +int ED_maskediting_poll(struct bContext *C); +int ED_maskediting_mask_poll(struct bContext *C); + +void ED_mask_size(struct bContext *C, int *width, int *height); +void ED_mask_aspect(struct bContext *C, float *aspx, float *aspy); + +void ED_mask_pixelspace_factor(struct bContext *C, float *scalex, float *scaley); +void ED_mask_mouse_pos(struct bContext *C, struct wmEvent *event, float co[2]); + +void ED_mask_point_pos(struct bContext *C, float x, float y, float *xr, float *yr); +void ED_mask_point_pos__reverse(struct bContext *C, float x, float y, float *xr, float *yr); + +/* mask_shapekey.c */ +void MASK_OT_shape_key_insert(struct wmOperatorType *ot); +void MASK_OT_shape_key_clear(struct wmOperatorType *ot); + +#endif /* __MASK_INTERN_H__ */ diff --git a/source/blender/editors/mask/mask_ops.c b/source/blender/editors/mask/mask_ops.c new file mode 100644 index 00000000000..cf3a72bdc78 --- /dev/null +++ b/source/blender/editors/mask/mask_ops.c @@ -0,0 +1,1171 @@ +/* + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2012 Blender Foundation. + * All rights reserved. + * + * + * Contributor(s): Blender Foundation, + * Sergey Sharybin + * + * ***** END GPL LICENSE BLOCK ***** + */ + +/** \file blender/editors/mask/mask_ops.c + * \ingroup edmask + */ + +#include "MEM_guardedalloc.h" + +#include "BLI_utildefines.h" +#include "BLI_listbase.h" +#include "BLI_math.h" + +#include "BKE_context.h" +#include "BKE_curve.h" +#include "BKE_depsgraph.h" +#include "BKE_mask.h" + +#include "DNA_scene_types.h" +#include "DNA_mask_types.h" +#include "DNA_object_types.h" /* SELECT */ + +#include "WM_api.h" +#include "WM_types.h" + +#include "ED_screen.h" +#include "ED_mask.h" +#include "ED_clip.h" +#include "ED_keyframing.h" + +#include "RNA_access.h" +#include "RNA_define.h" + +#include "mask_intern.h" /* own include */ + +/******************** utility functions *********************/ + +MaskSplinePoint *ED_mask_point_find_nearest(bContext *C, Mask *mask, float normal_co[2], int threshold, + MaskLayer **masklay_r, MaskSpline **spline_r, int *is_handle_r, + float *score) +{ + MaskLayer *masklay; + MaskLayer *point_masklay = NULL; + MaskSpline *point_spline = NULL; + MaskSplinePoint *point = NULL; + float co[2], aspx, aspy; + float len = FLT_MAX, scalex, scaley; + int is_handle = FALSE, width, height; + + ED_mask_size(C, &width, &height); + ED_mask_aspect(C, &aspx, &aspy); + ED_mask_pixelspace_factor(C, &scalex, &scaley); + + co[0] = normal_co[0] * scalex; + co[1] = normal_co[1] * scaley; + + for (masklay = mask->masklayers.first; masklay; masklay = masklay->next) { + MaskSpline *spline; + + if (masklay->restrictflag & (MASK_RESTRICT_VIEW | MASK_RESTRICT_SELECT)) { + continue; + } + + for (spline = masklay->splines.first; spline; spline = spline->next) { + MaskSplinePoint *points_array = BKE_mask_spline_point_array(spline); + + int i; + + for (i = 0; i < spline->tot_point; i++) { + MaskSplinePoint *cur_point = &spline->points[i]; + MaskSplinePoint *cur_point_deform = &points_array[i]; + float cur_len, vec[2], handle[2]; + + vec[0] = cur_point_deform->bezt.vec[1][0] * scalex; + vec[1] = cur_point_deform->bezt.vec[1][1] * scaley; + + if (BKE_mask_point_has_handle(cur_point)) { + BKE_mask_point_handle(cur_point_deform, handle); + handle[0] *= scalex; + handle[1] *= scaley; + + cur_len = len_v2v2(co, handle); + + if (cur_len < len) { + point_masklay = masklay; + point_spline = spline; + point = cur_point; + len = cur_len; + is_handle = TRUE; + } + } + + cur_len = len_v2v2(co, vec); + + if (cur_len < len) { + point_spline = spline; + point_masklay = masklay; + point = cur_point; + len = cur_len; + is_handle = FALSE; + } + } + } + } + + if (len < threshold) { + if (masklay_r) + *masklay_r = point_masklay; + + if (spline_r) + *spline_r = point_spline; + + if (is_handle_r) + *is_handle_r = is_handle; + + if (score) + *score = len; + + return point; + } + + if (masklay_r) + *masklay_r = NULL; + + if (spline_r) + *spline_r = NULL; + + if (is_handle_r) + *is_handle_r = FALSE; + + return NULL; +} + +int ED_mask_feather_find_nearest(bContext *C, Mask *mask, float normal_co[2], int threshold, + MaskLayer **masklay_r, MaskSpline **spline_r, MaskSplinePoint **point_r, + MaskSplinePointUW **uw_r, float *score) +{ + MaskLayer *masklay, *point_masklay = NULL; + MaskSpline *point_spline = NULL; + MaskSplinePoint *point = NULL; + MaskSplinePointUW *uw = NULL; + float len = FLT_MAX, co[2]; + float scalex, scaley, aspx, aspy; + int width, height; + + ED_mask_size(C, &width, &height); + ED_mask_aspect(C, &aspx, &aspy); + ED_mask_pixelspace_factor(C, &scalex, &scaley); + + co[0] = normal_co[0] * scalex; + co[1] = normal_co[1] * scaley; + + for (masklay = mask->masklayers.first; masklay; masklay = masklay->next) { + MaskSpline *spline; + + for (spline = masklay->splines.first; spline; spline = spline->next) { + //MaskSplinePoint *points_array = BKE_mask_spline_point_array(spline); + + int i, tot_feather_point; + float (*feather_points)[2], (*fp)[2]; + + if (masklay->restrictflag & (MASK_RESTRICT_VIEW | MASK_RESTRICT_SELECT)) { + continue; + } + + feather_points = fp = BKE_mask_spline_feather_points(spline, &tot_feather_point); + + for (i = 0; i < spline->tot_point; i++) { + int j; + MaskSplinePoint *cur_point = &spline->points[i]; + + for (j = 0; j < cur_point->tot_uw + 1; j++) { + float cur_len, vec[2]; + + vec[0] = (*fp)[0] * scalex; + vec[1] = (*fp)[1] * scaley; + + cur_len = len_v2v2(vec, co); + + if (point == NULL || cur_len < len) { + if (j == 0) + uw = NULL; + else + uw = &cur_point->uw[j - 1]; + + point_masklay = masklay; + point_spline = spline; + point = cur_point; + len = cur_len; + } + + fp++; + } + } + + MEM_freeN(feather_points); + } + } + + if (len < threshold) { + if (masklay_r) + *masklay_r = point_masklay; + + if (spline_r) + *spline_r = point_spline; + + if (point_r) + *point_r = point; + + if (uw_r) + *uw_r = uw; + + if (score) + *score = len; + + return TRUE; + } + + if (masklay_r) + *masklay_r = NULL; + + if (spline_r) + *spline_r = NULL; + + if (point_r) + *point_r = NULL; + + return FALSE; +} + + +/******************** create new mask *********************/ + +static int mask_new_exec(bContext *C, wmOperator *op) +{ + SpaceClip *sc = CTX_wm_space_clip(C); + Mask *mask; + char name[MAX_ID_NAME - 2]; + + RNA_string_get(op->ptr, "name", name); + + mask = BKE_mask_new(name); + + if (sc) + ED_space_clip_set_mask(C, sc, mask); + + return OPERATOR_FINISHED; +} + +void MASK_OT_new(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "New Mask"; + ot->description = "Create new mask"; + ot->idname = "MASK_OT_new"; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + /* api callbacks */ + ot->exec = mask_new_exec; + ot->poll = ED_operator_mask; + + /* properties */ + RNA_def_string(ot->srna, "name", "", MAX_ID_NAME - 2, "Name", "Name of new mask"); +} + +/******************** create new masklay *********************/ + +static int masklay_new_exec(bContext *C, wmOperator *op) +{ + Mask *mask = CTX_data_edit_mask(C); + char name[MAX_ID_NAME - 2]; + + RNA_string_get(op->ptr, "name", name); + + BKE_mask_layer_new(mask, name); + mask->masklay_act = mask->masklay_tot - 1; + + WM_event_add_notifier(C, NC_MASK | NA_EDITED, mask); + + return OPERATOR_FINISHED; +} + +void MASK_OT_layer_new(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Add Mask Layer"; + ot->description = "Add new mask layer for masking"; + ot->idname = "MASK_OT_layer_new"; + + /* api callbacks */ + ot->exec = masklay_new_exec; + ot->poll = ED_maskediting_poll; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + /* properties */ + RNA_def_string(ot->srna, "name", "", MAX_ID_NAME - 2, "Name", "Name of new mask layer"); +} + +/******************** remove mask layer *********************/ + +static int masklay_remove_exec(bContext *C, wmOperator *UNUSED(op)) +{ + Mask *mask = CTX_data_edit_mask(C); + MaskLayer *masklay = BKE_mask_layer_active(mask); + + if (masklay) { + BKE_mask_layer_remove(mask, masklay); + + WM_event_add_notifier(C, NC_MASK | NA_EDITED, mask); + } + + return OPERATOR_FINISHED; +} + +void MASK_OT_layer_remove(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Remove Mask Layer"; + ot->description = "Remove mask layer"; + ot->idname = "MASK_OT_layer_remove"; + + /* api callbacks */ + ot->exec = masklay_remove_exec; + ot->poll = ED_maskediting_poll; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; +} + +/******************** slide *********************/ + +#define SLIDE_ACTION_NONE 0 +#define SLIDE_ACTION_POINT 1 +#define SLIDE_ACTION_HANDLE 2 +#define SLIDE_ACTION_FEATHER 3 + +typedef struct SlidePointData { + int action; + + float co[2]; + float vec[3][3]; + + Mask *mask; + MaskLayer *masklay; + MaskSpline *spline, *orig_spline; + MaskSplinePoint *point; + MaskSplinePointUW *uw; + float handle[2], no[2], feather[2]; + int width, height; + float weight; + + short curvature_only, accurate; + short initial_feather, overall_feather; +} SlidePointData; + +static int slide_point_check_initial_feather(MaskSpline *spline) +{ + int i; + + for (i = 0; i < spline->tot_point; i++) { + MaskSplinePoint *point = &spline->points[i]; + int j; + + if (point->bezt.weight != 0.0f) + return FALSE; + + for (j = 0; j < point->tot_uw; j++) { + if (point->uw[j].w != 0.0f) + return FALSE; + } + } + + return TRUE; +} + +static void *slide_point_customdata(bContext *C, wmOperator *op, wmEvent *event) +{ + Mask *mask = CTX_data_edit_mask(C); + SlidePointData *customdata = NULL; + MaskLayer *masklay, *cv_masklay, *feather_masklay; + MaskSpline *spline, *cv_spline, *feather_spline; + MaskSplinePoint *point, *cv_point, *feather_point; + MaskSplinePointUW *uw = NULL; + int is_handle = FALSE, width, height, action = SLIDE_ACTION_NONE; + int slide_feather = RNA_boolean_get(op->ptr, "slide_feather"); + float co[2], cv_score, feather_score; + const float threshold = 19; + + ED_mask_mouse_pos(C, event, co); + ED_mask_size(C, &width, &height); + + cv_point = ED_mask_point_find_nearest(C, mask, co, threshold, &cv_masklay, &cv_spline, &is_handle, &cv_score); + + if (ED_mask_feather_find_nearest(C, mask, co, threshold, &feather_masklay, &feather_spline, &feather_point, &uw, &feather_score)) { + if (slide_feather || !cv_point || feather_score < cv_score) { + action = SLIDE_ACTION_FEATHER; + + masklay = feather_masklay; + spline = feather_spline; + point = feather_point; + } + } + + if (cv_point && action == SLIDE_ACTION_NONE) { + if (is_handle) + action = SLIDE_ACTION_HANDLE; + else + action = SLIDE_ACTION_POINT; + + masklay = cv_masklay; + spline = cv_spline; + point = cv_point; + } + + if (action != SLIDE_ACTION_NONE) { + customdata = MEM_callocN(sizeof(SlidePointData), "mask slide point data"); + + customdata->mask = mask; + customdata->masklay = masklay; + customdata->spline = spline; + customdata->point = point; + customdata->width = width; + customdata->height = height; + customdata->action = action; + customdata->uw = uw; + + if (uw) { + float co[2]; + + customdata->weight = point->bezt.weight; + + customdata->weight = uw->w; + BKE_mask_point_segment_co(spline, point, uw->u, co); + BKE_mask_point_normal(spline, point, uw->u, customdata->no); + + customdata->feather[0] = co[0] + customdata->no[0] * uw->w; + customdata->feather[1] = co[1] + customdata->no[1] * uw->w; + } + else { + BezTriple *bezt = &point->bezt; + + BKE_mask_point_normal(spline, point, 0.0f, customdata->no); + + customdata->feather[0] = bezt->vec[1][0] + customdata->no[0] * bezt->weight; + customdata->feather[1] = bezt->vec[1][1] + customdata->no[1] * bezt->weight; + + customdata->weight = bezt->weight; + } + + if (customdata->action == SLIDE_ACTION_FEATHER) + customdata->initial_feather = slide_point_check_initial_feather(spline); + + copy_m3_m3(customdata->vec, point->bezt.vec); + if (BKE_mask_point_has_handle(point)) + BKE_mask_point_handle(point, customdata->handle); + ED_mask_mouse_pos(C, event, customdata->co); + } + + return customdata; +} + +static int slide_point_invoke(bContext *C, wmOperator *op, wmEvent *event) +{ + SlidePointData *slidedata = slide_point_customdata(C, op, event); + + if (slidedata) { + Mask *mask = CTX_data_edit_mask(C); + + op->customdata = slidedata; + + WM_event_add_modal_handler(C, op); + + if (slidedata->uw) { + if ((slidedata->uw->flag & SELECT) == 0) { + ED_mask_select_toggle_all(mask, SEL_DESELECT); + + slidedata->uw->flag |= SELECT; + + ED_mask_select_flush_all(mask); + } + } + else if (!MASKPOINT_ISSEL_ANY(slidedata->point)) { + ED_mask_select_toggle_all(mask, SEL_DESELECT); + + BKE_mask_point_select_set(slidedata->point, TRUE); + + ED_mask_select_flush_all(mask); + } + + slidedata->masklay->act_spline = slidedata->spline; + slidedata->masklay->act_point = slidedata->point; + + WM_event_add_notifier(C, NC_MASK | ND_SELECT, mask); + + return OPERATOR_RUNNING_MODAL; + } + + return OPERATOR_PASS_THROUGH; +} + +static void slide_point_delta_all_feather(SlidePointData *data, float delta) +{ + int i; + + for (i = 0; i < data->spline->tot_point; i++) { + MaskSplinePoint *point = &data->spline->points[i]; + MaskSplinePoint *orig_point = &data->orig_spline->points[i]; + int j; + + point->bezt.weight = orig_point->bezt.weight + delta; + if (point->bezt.weight < 0.0f) + point->bezt.weight = 0.0f; + + for (j = 0; j < point->tot_uw; j++) { + point->uw[j].w = orig_point->uw[j].w + delta; + if (point->uw[j].w < 0.0f) + point->uw[j].w = 0.0f; + } + } +} + +static void slide_point_restore_spline(SlidePointData *data) +{ + int i; + + for (i = 0; i < data->spline->tot_point; i++) { + MaskSplinePoint *point = &data->spline->points[i]; + MaskSplinePoint *orig_point = &data->orig_spline->points[i]; + int j; + + point->bezt = orig_point->bezt; + + for (j = 0; j < point->tot_uw; j++) + point->uw[j] = orig_point->uw[j]; + } +} + +static void cancel_slide_point(SlidePointData *data) +{ + /* cancel sliding */ + + if (data->orig_spline) { + slide_point_restore_spline(data); + } + else { + if (data->action == SLIDE_ACTION_FEATHER) { + if (data->uw) + data->uw->w = data->weight; + else + data->point->bezt.weight = data->weight; + } + else { + copy_m3_m3(data->point->bezt.vec, data->vec); + } + } +} + +static void free_slide_point_data(SlidePointData *data) +{ + if (data->orig_spline) + BKE_mask_spline_free(data->orig_spline); + + MEM_freeN(data); +} + +static int slide_point_modal(bContext *C, wmOperator *op, wmEvent *event) +{ + SlidePointData *data = (SlidePointData *)op->customdata; + BezTriple *bezt = &data->point->bezt; + float co[2], dco[2]; + + switch (event->type) { + case LEFTCTRLKEY: + case RIGHTCTRLKEY: + case LEFTSHIFTKEY: + case RIGHTSHIFTKEY: + if (ELEM(event->type, LEFTCTRLKEY, RIGHTCTRLKEY)) { + if (data->action == SLIDE_ACTION_FEATHER) + data->overall_feather = event->val == KM_PRESS; + else + data->curvature_only = event->val == KM_PRESS; + } + + if (ELEM(event->type, LEFTSHIFTKEY, RIGHTSHIFTKEY)) + data->accurate = event->val == KM_PRESS; + + /* no break! update CV position */ + + case MOUSEMOVE: + ED_mask_mouse_pos(C, event, co); + sub_v2_v2v2(dco, co, data->co); + + if (data->action == SLIDE_ACTION_HANDLE) { + float delta[2], offco[2]; + + sub_v2_v2v2(delta, data->handle, data->co); + + sub_v2_v2v2(offco, co, data->co); + if (data->accurate) + mul_v2_fl(offco, 0.2f); + add_v2_v2(offco, data->co); + add_v2_v2(offco, delta); + + BKE_mask_point_set_handle(data->point, offco, data->curvature_only, data->handle, data->vec); + } + else if (data->action == SLIDE_ACTION_POINT) { + float delta[2]; + + copy_v2_v2(delta, dco); + if (data->accurate) + mul_v2_fl(delta, 0.2f); + + add_v2_v2v2(bezt->vec[0], data->vec[0], delta); + add_v2_v2v2(bezt->vec[1], data->vec[1], delta); + add_v2_v2v2(bezt->vec[2], data->vec[2], delta); + } + else if (data->action == SLIDE_ACTION_FEATHER) { + float vec[2], no[2], p[2], c[2], w, offco[2]; + float *weight = NULL; + int overall_feather = data->overall_feather || data->initial_feather; + + add_v2_v2v2(offco, data->feather, dco); + + if (data->uw) { + float u = BKE_mask_spline_project_co(data->spline, data->point, data->uw->u, offco); + + if (u > 0.0f && u < 1.0f) { + data->uw->u = u; + + data->uw = BKE_mask_point_sort_uw(data->point, data->uw); + weight = &data->uw->w; + BKE_mask_point_normal(data->spline, data->point, data->uw->u, no); + BKE_mask_point_segment_co(data->spline, data->point, data->uw->u, p); + } + } + else { + weight = &bezt->weight; + copy_v2_v2(no, data->no); + copy_v2_v2(p, bezt->vec[1]); + } + + if (weight) { + sub_v2_v2v2(c, offco, p); + project_v2_v2v2(vec, c, no); + + w = len_v2(vec); + + if (overall_feather) { + float delta; + + if (dot_v2v2(no, vec) <= 0.0f) + w = -w; + + delta = w - data->weight; + + if (data->orig_spline == NULL) { + /* restore weight for currently sliding point, so orig_spline would be created + * with original weights used + */ + *weight = data->weight; + + data->orig_spline = BKE_mask_spline_copy(data->spline); + } + + slide_point_delta_all_feather(data, delta); + } + else { + if (dot_v2v2(no, vec) <= 0.0f) + w = 0.0f; + + if (data->orig_spline) { + /* restore possible overall feather changes */ + slide_point_restore_spline(data); + + BKE_mask_spline_free(data->orig_spline); + data->orig_spline = NULL; + } + + *weight = w; + } + } + } + + WM_event_add_notifier(C, NC_MASK | NA_EDITED, data->mask); + DAG_id_tag_update(&data->mask->id, 0); + + break; + + case LEFTMOUSE: + if (event->val == KM_RELEASE) { + Scene *scene = CTX_data_scene(C); + + free_slide_point_data(op->customdata); + + if (IS_AUTOKEY_ON(scene)) { + ED_mask_layer_shape_auto_key_all(data->mask, CFRA); + } + + WM_event_add_notifier(C, NC_MASK | NA_EDITED, data->mask); + DAG_id_tag_update(&data->mask->id, 0); + + return OPERATOR_FINISHED; + } + + break; + + case ESCKEY: + cancel_slide_point(op->customdata); + + free_slide_point_data(op->customdata); + + WM_event_add_notifier(C, NC_MASK | NA_EDITED, data->mask); + DAG_id_tag_update(&data->mask->id, 0); + + return OPERATOR_CANCELLED; + } + + return OPERATOR_RUNNING_MODAL; +} + +void MASK_OT_slide_point(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Slide Point"; + ot->description = "Slide control points"; + ot->idname = "MASK_OT_slide_point"; + + /* api callbacks */ + ot->invoke = slide_point_invoke; + ot->modal = slide_point_modal; + ot->poll = ED_maskediting_mask_poll; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + RNA_def_boolean(ot->srna, "slide_feather", 0, "Slide Feather", "First try to slide slide feather instead of vertex"); +} + +/******************** toggle cyclic *********************/ + +static int cyclic_toggle_exec(bContext *C, wmOperator *UNUSED(op)) +{ + Mask *mask = CTX_data_edit_mask(C); + MaskLayer *masklay; + + for (masklay = mask->masklayers.first; masklay; masklay = masklay->next) { + MaskSpline *spline; + + if (masklay->restrictflag & (MASK_RESTRICT_VIEW | MASK_RESTRICT_SELECT)) { + continue; + } + + for (spline = masklay->splines.first; spline; spline = spline->next) { + if (ED_mask_spline_select_check(spline)) { + spline->flag ^= MASK_SPLINE_CYCLIC; + } + } + } + + WM_event_add_notifier(C, NC_MASK | NA_EDITED, mask); + + return OPERATOR_FINISHED; +} + +void MASK_OT_cyclic_toggle(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Toggle Cyclic"; + ot->description = "Toggle cyclic for selected splines"; + ot->idname = "MASK_OT_cyclic_toggle"; + + /* api callbacks */ + ot->exec = cyclic_toggle_exec; + ot->poll = ED_maskediting_mask_poll; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; +} + +/******************** delete *********************/ + +static void delete_feather_points(MaskSplinePoint *point) +{ + int i, count = 0; + + if (!point->tot_uw) + return; + + for (i = 0; i < point->tot_uw; i++) { + if ((point->uw[i].flag & SELECT) == 0) + count++; + } + + if (count == 0) { + MEM_freeN(point->uw); + point->uw = NULL; + point->tot_uw = 0; + } + else { + MaskSplinePointUW *new_uw; + int j = 0; + + new_uw = MEM_callocN(count * sizeof(MaskSplinePointUW), "new mask uw points"); + + for (i = 0; i < point->tot_uw; i++) { + if ((point->uw[i].flag & SELECT) == 0) { + new_uw[j++] = point->uw[i]; + } + } + + MEM_freeN(point->uw); + + point->uw = new_uw; + point->tot_uw = count; + } +} + +static int delete_exec(bContext *C, wmOperator *UNUSED(op)) +{ + Mask *mask = CTX_data_edit_mask(C); + MaskLayer *masklay; + int mask_layer_shape_ofs = 0; + + for (masklay = mask->masklayers.first; masklay; masklay = masklay->next) { + MaskSpline *spline; + + if (masklay->restrictflag & (MASK_RESTRICT_VIEW | MASK_RESTRICT_SELECT)) { + continue; + } + + spline = masklay->splines.first; + + while (spline) { + const int tot_point_orig = spline->tot_point; + int i, count = 0; + MaskSpline *next_spline = spline->next; + + /* count unselected points */ + for (i = 0; i < spline->tot_point; i++) { + MaskSplinePoint *point = &spline->points[i]; + + if (!MASKPOINT_ISSEL_ANY(point)) + count++; + } + + if (count == 0) { + + /* delete the whole spline */ + BLI_remlink(&masklay->splines, spline); + BKE_mask_spline_free(spline); + + if (spline == masklay->act_spline) { + masklay->act_spline = NULL; + masklay->act_point = NULL; + } + + BKE_mask_layer_shape_changed_remove(masklay, mask_layer_shape_ofs, tot_point_orig); + } + else { + MaskSplinePoint *new_points; + int j; + + new_points = MEM_callocN(count * sizeof(MaskSplinePoint), "deleteMaskPoints"); + + for (i = 0, j = 0; i < tot_point_orig; i++) { + MaskSplinePoint *point = &spline->points[i]; + + if (!MASKPOINT_ISSEL_ANY(point)) { + if (point == masklay->act_point) + masklay->act_point = &new_points[j]; + + delete_feather_points(point); + + new_points[j] = *point; + j++; + } + else { + if (point == masklay->act_point) + masklay->act_point = NULL; + + BKE_mask_point_free(point); + spline->tot_point--; + + BKE_mask_layer_shape_changed_remove(masklay, mask_layer_shape_ofs + j, 1); + } + } + + mask_layer_shape_ofs += spline->tot_point; + + MEM_freeN(spline->points); + spline->points = new_points; + + ED_mask_select_flush_all(mask); + } + + spline = next_spline; + } + } + + /* TODO: only update edited splines */ + BKE_mask_update_display(mask, CTX_data_scene(C)->r.cfra); + + WM_event_add_notifier(C, NC_MASK | NA_EDITED, mask); + + return OPERATOR_FINISHED; +} + +void MASK_OT_delete(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Delete"; + ot->description = "Delete selected control points or splines"; + ot->idname = "MASK_OT_delete"; + + /* api callbacks */ + ot->invoke = WM_operator_confirm; + ot->exec = delete_exec; + ot->poll = ED_maskediting_mask_poll; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; +} + +/* *** switch direction *** */ +static int mask_switch_direction_exec(bContext *C, wmOperator *UNUSED(op)) +{ + Mask *mask = CTX_data_edit_mask(C); + MaskLayer *masklay; + + int change = FALSE; + + /* do actual selection */ + for (masklay = mask->masklayers.first; masklay; masklay = masklay->next) { + MaskSpline *spline; + + if (masklay->restrictflag & (MASK_RESTRICT_VIEW | MASK_RESTRICT_SELECT)) { + continue; + } + + for (spline = masklay->splines.first; spline; spline = spline->next) { + if (ED_mask_spline_select_check(spline)) { + BKE_mask_spline_direction_switch(masklay, spline); + change = TRUE; + } + } + } + + if (change) { + /* TODO: only update this spline */ + BKE_mask_update_display(mask, CTX_data_scene(C)->r.cfra); + + WM_event_add_notifier(C, NC_MASK | ND_SELECT, mask); + + return OPERATOR_FINISHED; + } + + return OPERATOR_CANCELLED; +} + +void MASK_OT_switch_direction(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Switch Direction"; + ot->description = "Switch direction of selected splines"; + ot->idname = "MASK_OT_switch_direction"; + + /* api callbacks */ + ot->exec = mask_switch_direction_exec; + ot->poll = ED_maskediting_mask_poll; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; +} + + +/******************** set handle type *********************/ + +static int set_handle_type_exec(bContext *C, wmOperator *op) +{ + Mask *mask = CTX_data_edit_mask(C); + MaskLayer *masklay; + int handle_type = RNA_enum_get(op->ptr, "type"); + + for (masklay = mask->masklayers.first; masklay; masklay = masklay->next) { + MaskSpline *spline; + int i; + + if (masklay->restrictflag & (MASK_RESTRICT_VIEW | MASK_RESTRICT_SELECT)) { + continue; + } + + for (spline = masklay->splines.first; spline; spline = spline->next) { + for (i = 0; i < spline->tot_point; i++) { + MaskSplinePoint *point = &spline->points[i]; + + if (MASKPOINT_ISSEL_ANY(point)) { + BezTriple *bezt = &point->bezt; + + bezt->h1 = bezt->h2 = handle_type; + } + } + } + } + + WM_event_add_notifier(C, NC_MASK | ND_DATA, mask); + DAG_id_tag_update(&mask->id, 0); + + return OPERATOR_FINISHED; +} + +void MASK_OT_handle_type_set(wmOperatorType *ot) +{ + static EnumPropertyItem editcurve_handle_type_items[] = { + {HD_AUTO, "AUTO", 0, "Auto", ""}, + {HD_VECT, "VECTOR", 0, "Vector", ""}, + {HD_ALIGN, "ALIGNED", 0, "Aligned", ""}, + {0, NULL, 0, NULL, NULL} + }; + + /* identifiers */ + ot->name = "Set Handle Type"; + ot->description = "Set type of handles for selected control points"; + ot->idname = "MASK_OT_handle_type_set"; + + /* api callbacks */ + ot->invoke = WM_menu_invoke; + ot->exec = set_handle_type_exec; + ot->poll = ED_maskediting_mask_poll; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + /* properties */ + ot->prop = RNA_def_enum(ot->srna, "type", editcurve_handle_type_items, 1, "Type", "Spline type"); +} + + +/* ********* clear/set restrict view *********/ +static int mask_hide_view_clear_exec(bContext *C, wmOperator *UNUSED(op)) +{ + Mask *mask = CTX_data_edit_mask(C); + MaskLayer *masklay; + int changed = FALSE; + + for (masklay = mask->masklayers.first; masklay; masklay = masklay->next) { + + if (masklay->restrictflag & OB_RESTRICT_VIEW) { + ED_mask_layer_select_set(masklay, TRUE); + masklay->restrictflag &= ~OB_RESTRICT_VIEW; + changed = 1; + } + } + + if (changed) { + WM_event_add_notifier(C, NC_MASK | ND_DRAW, mask); + DAG_id_tag_update(&mask->id, 0); + + return OPERATOR_FINISHED; + } + else { + return OPERATOR_CANCELLED; + } +} + +void MASK_OT_hide_view_clear(wmOperatorType *ot) +{ + + /* identifiers */ + ot->name = "Clear Restrict View"; + ot->description = "Reveal the layer by setting the hide flag"; + ot->idname = "MASK_OT_hide_view_clear"; + + /* api callbacks */ + ot->exec = mask_hide_view_clear_exec; + ot->poll = ED_maskediting_mask_poll; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; +} + +static int mask_hide_view_set_exec(bContext *C, wmOperator *op) +{ + Mask *mask = CTX_data_edit_mask(C); + MaskLayer *masklay; + const int unselected = RNA_boolean_get(op->ptr, "unselected"); + int changed = FALSE; + + for (masklay = mask->masklayers.first; masklay; masklay = masklay->next) { + + if (masklay->restrictflag & MASK_RESTRICT_SELECT) { + continue; + } + + if (!unselected) { + if (ED_mask_layer_select_check(masklay)) { + ED_mask_layer_select_set(masklay, FALSE); + + masklay->restrictflag |= OB_RESTRICT_VIEW; + changed = 1; + if (masklay == BKE_mask_layer_active(mask)) { + BKE_mask_layer_active_set(mask, NULL); + } + } + } + else { + if (!ED_mask_layer_select_check(masklay)) { + masklay->restrictflag |= OB_RESTRICT_VIEW; + changed = 1; + if (masklay == BKE_mask_layer_active(mask)) { + BKE_mask_layer_active_set(mask, NULL); + } + } + } + } + + if (changed) { + WM_event_add_notifier(C, NC_MASK | ND_DRAW, mask); + DAG_id_tag_update(&mask->id, 0); + + return OPERATOR_FINISHED; + } + else { + return OPERATOR_CANCELLED; + } +} + +void MASK_OT_hide_view_set(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Set Restrict View"; + ot->description = "Hide the layer by setting the hide flag"; + ot->idname = "MASK_OT_hide_view_set"; + + /* api callbacks */ + ot->exec = mask_hide_view_set_exec; + ot->poll = ED_maskediting_mask_poll; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + RNA_def_boolean(ot->srna, "unselected", 0, "Unselected", "Hide unselected rather than selected layers"); + +} diff --git a/source/blender/editors/mask/mask_relationships.c b/source/blender/editors/mask/mask_relationships.c new file mode 100644 index 00000000000..8a8427c024b --- /dev/null +++ b/source/blender/editors/mask/mask_relationships.c @@ -0,0 +1,183 @@ +/* + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2012 Blender Foundation. + * All rights reserved. + * + * + * Contributor(s): Blender Foundation, + * Campbell Barton + * + * ***** END GPL LICENSE BLOCK ***** + */ + +/** \file blender/editors/mask/mask_relationshops.c + * \ingroup edmask + */ + +#include "MEM_guardedalloc.h" + +#include "BLI_utildefines.h" +#include "BLI_listbase.h" +#include "BLI_math.h" + +#include "BKE_context.h" +#include "BKE_curve.h" +#include "BKE_depsgraph.h" +#include "BKE_mask.h" +#include "BKE_tracking.h" + +#include "DNA_mask_types.h" +#include "DNA_scene_types.h" +#include "DNA_object_types.h" /* SELECT */ + +#include "WM_api.h" +#include "WM_types.h" + +#include "ED_screen.h" +#include "ED_mask.h" +#include "ED_clip.h" + +#include "RNA_access.h" +#include "RNA_define.h" + +#include "mask_intern.h" /* own include */ + +static int mask_parent_clear_exec(bContext *C, wmOperator *UNUSED(op)) +{ + Mask *mask = CTX_data_edit_mask(C); + MaskLayer *masklay; + + for (masklay = mask->masklayers.first; masklay; masklay = masklay->next) { + MaskSpline *spline; + int i; + + if (masklay->restrictflag & (MASK_RESTRICT_VIEW | MASK_RESTRICT_SELECT)) { + continue; + } + + for (spline = masklay->splines.first; spline; spline = spline->next) { + for (i = 0; i < spline->tot_point; i++) { + MaskSplinePoint *point = &spline->points[i]; + + if (MASKPOINT_ISSEL_ANY(point)) { + point->parent.flag &= ~MASK_PARENT_ACTIVE; + } + } + } + } + + WM_event_add_notifier(C, NC_MASK | ND_DATA, mask); + DAG_id_tag_update(&mask->id, 0); + + return OPERATOR_FINISHED; +} + +void MASK_OT_parent_clear(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Clear Parent"; + ot->description = "Clear the masks parenting"; + ot->idname = "MASK_OT_parent_clear"; + + /* api callbacks */ + ot->exec = mask_parent_clear_exec; + + ot->poll = ED_maskediting_mask_poll; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; +} + +static int mask_parent_set_exec(bContext *C, wmOperator *UNUSED(op)) +{ + Mask *mask = CTX_data_edit_mask(C); + MaskLayer *masklay; + + /* parent info */ + SpaceClip *sc; + MovieClip *clip; + MovieTrackingTrack *track; + MovieTrackingMarker *marker; + MovieTrackingObject *tracking; + /* done */ + + float marker_pos_ofs[2]; + float parmask_pos[2]; + + if ((NULL == (sc = CTX_wm_space_clip(C))) || + (NULL == (clip = sc->clip)) || + (NULL == (track = clip->tracking.act_track)) || + (NULL == (marker = BKE_tracking_get_marker(track, sc->user.framenr))) || + (NULL == (tracking = BKE_tracking_active_object(&clip->tracking)))) + { + return OPERATOR_CANCELLED; + } + + add_v2_v2v2(marker_pos_ofs, marker->pos, track->offset); + + BKE_mask_coord_from_movieclip(clip, &sc->user, parmask_pos, marker_pos_ofs); + + for (masklay = mask->masklayers.first; masklay; masklay = masklay->next) { + MaskSpline *spline; + int i; + + if (masklay->restrictflag & (MASK_RESTRICT_VIEW | MASK_RESTRICT_SELECT)) { + continue; + } + + for (spline = masklay->splines.first; spline; spline = spline->next) { + for (i = 0; i < spline->tot_point; i++) { + MaskSplinePoint *point = &spline->points[i]; + + if (MASKPOINT_ISSEL_ANY(point)) { + point->parent.id_type = ID_MC; + point->parent.id = &clip->id; + strcpy(point->parent.parent, tracking->name); + strcpy(point->parent.sub_parent, track->name); + + point->parent.flag |= MASK_PARENT_ACTIVE; + + copy_v2_v2(point->parent.parent_orig, parmask_pos); + } + } + } + } + + WM_event_add_notifier(C, NC_MASK | ND_DATA, mask); + DAG_id_tag_update(&mask->id, 0); + + return OPERATOR_FINISHED; +} + +/** based on #OBJECT_OT_parent_set */ +void MASK_OT_parent_set(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Make Parent"; + ot->description = "Set the masks parenting"; + ot->idname = "MASK_OT_parent_set"; + + /* api callbacks */ + //ot->invoke = mask_parent_set_invoke; + ot->exec = mask_parent_set_exec; + + ot->poll = ED_maskediting_mask_poll; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; +} diff --git a/source/blender/editors/mask/mask_select.c b/source/blender/editors/mask/mask_select.c new file mode 100644 index 00000000000..18d745dcf7b --- /dev/null +++ b/source/blender/editors/mask/mask_select.c @@ -0,0 +1,770 @@ +/* + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2012 Blender Foundation. + * All rights reserved. + * + * + * Contributor(s): Blender Foundation, + * Sergey Sharybin + * + * ***** END GPL LICENSE BLOCK ***** + */ + +/** \file blender/editors/mask/mask_select.c + * \ingroup edmask + */ + +#include "MEM_guardedalloc.h" + +#include "BLI_utildefines.h" +#include "BLI_listbase.h" +#include "BLI_rect.h" +#include "BLI_lasso.h" +#include "BLI_math.h" + +#include "BKE_context.h" +#include "BKE_curve.h" +#include "BKE_depsgraph.h" +#include "BKE_mask.h" + +#include "DNA_mask_types.h" +#include "DNA_object_types.h" /* SELECT */ + +#include "WM_api.h" +#include "WM_types.h" + +#include "ED_screen.h" +#include "ED_mask.h" +#include "ED_clip.h" + +#include "RNA_access.h" +#include "RNA_define.h" + +#include "mask_intern.h" /* own include */ + +/* 'check' select */ +int ED_mask_spline_select_check(MaskSpline *spline) +{ + int i; + + for (i = 0; i < spline->tot_point; i++) { + MaskSplinePoint *point = &spline->points[i]; + + if (MASKPOINT_ISSEL_ANY(point)) + return TRUE; + } + + return FALSE; +} + +int ED_mask_layer_select_check(MaskLayer *masklay) +{ + MaskSpline *spline; + + if (masklay->restrictflag & (MASK_RESTRICT_VIEW | MASK_RESTRICT_SELECT)) { + return FALSE; + } + + for (spline = masklay->splines.first; spline; spline = spline->next) { + if (ED_mask_spline_select_check(spline)) { + return TRUE; + } + } + + return FALSE; +} + +int ED_mask_select_check(Mask *mask) +{ + MaskLayer *masklay; + + for (masklay = mask->masklayers.first; masklay; masklay = masklay->next) { + if (ED_mask_layer_select_check(masklay)) { + return TRUE; + } + } + + return FALSE; +} + +/* 'sel' select */ +void ED_mask_spline_select_set(MaskSpline *spline, const short do_select) +{ + int i; + + if (do_select) + spline->flag |= SELECT; + else + spline->flag &= ~SELECT; + + for (i = 0; i < spline->tot_point; i++) { + MaskSplinePoint *point = &spline->points[i]; + + BKE_mask_point_select_set(point, do_select); + } +} + +void ED_mask_layer_select_set(MaskLayer *masklay, const short do_select) +{ + MaskSpline *spline; + + if (masklay->restrictflag & MASK_RESTRICT_SELECT) { + if (do_select == TRUE) { + return; + } + } + + for (spline = masklay->splines.first; spline; spline = spline->next) { + ED_mask_spline_select_set(spline, do_select); + } +} + +void ED_mask_select_toggle_all(Mask *mask, int action) +{ + MaskLayer *masklay; + + if (action == SEL_TOGGLE) { + if (ED_mask_select_check(mask)) + action = SEL_DESELECT; + else + action = SEL_SELECT; + } + + for (masklay = mask->masklayers.first; masklay; masklay = masklay->next) { + + if (masklay->restrictflag & MASK_RESTRICT_VIEW) { + continue; + } + + ED_mask_layer_select_set(masklay, (action == SEL_SELECT) ? TRUE : FALSE); + } +} + +void ED_mask_select_flush_all(Mask *mask) +{ + MaskLayer *masklay; + + for (masklay = mask->masklayers.first; masklay; masklay = masklay->next) { + MaskSpline *spline; + + for (spline = masklay->splines.first; spline; spline = spline->next) { + int i; + + spline->flag &= ~SELECT; + + /* intentionally _dont_ do this in the masklay loop + * so we clear flags on all splines */ + if (masklay->restrictflag & MASK_RESTRICT_VIEW) { + continue; + } + + for (i = 0; i < spline->tot_point; i++) { + MaskSplinePoint *cur_point = &spline->points[i]; + + if (MASKPOINT_ISSEL_ANY(cur_point)) { + spline->flag |= SELECT; + } + else { + int j; + + for (j = 0; j < cur_point->tot_uw; j++) { + if (cur_point->uw[j].flag & SELECT) { + spline->flag |= SELECT; + break; + } + } + } + } + } + } +} + +/******************** toggle selection *********************/ + +static int select_all_exec(bContext *C, wmOperator *op) +{ + Mask *mask = CTX_data_edit_mask(C); + int action = RNA_enum_get(op->ptr, "action"); + + ED_mask_select_toggle_all(mask, action); + ED_mask_select_flush_all(mask); + + WM_event_add_notifier(C, NC_MASK | ND_SELECT, mask); + + return OPERATOR_FINISHED; +} + +void MASK_OT_select_all(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Select or Deselect All"; + ot->description = "Change selection of all curve points"; + ot->idname = "MASK_OT_select_all"; + + /* api callbacks */ + ot->exec = select_all_exec; + ot->poll = ED_maskediting_mask_poll; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + /* properties */ + WM_operator_properties_select_all(ot); +} + +/******************** select *********************/ + +static int select_exec(bContext *C, wmOperator *op) +{ + Mask *mask = CTX_data_edit_mask(C); + MaskLayer *masklay; + MaskSpline *spline; + MaskSplinePoint *point = NULL; + float co[2]; + short extend = RNA_boolean_get(op->ptr, "extend"); + short deselect = RNA_boolean_get(op->ptr, "deselect"); + short toggle = RNA_boolean_get(op->ptr, "toggle"); + + int is_handle = 0; + const float threshold = 19; + + RNA_float_get_array(op->ptr, "location", co); + + point = ED_mask_point_find_nearest(C, mask, co, threshold, &masklay, &spline, &is_handle, NULL); + + if (extend == 0 && deselect == 0 && toggle == 0) + ED_mask_select_toggle_all(mask, SEL_DESELECT); + + if (point) { + + if (is_handle) { + if (extend) { + masklay->act_spline = spline; + masklay->act_point = point; + + BKE_mask_point_select_set_handle(point, TRUE); + } + else if (deselect) { + BKE_mask_point_select_set_handle(point, FALSE); + } + else { + masklay->act_spline = spline; + masklay->act_point = point; + + if (!MASKPOINT_ISSEL_HANDLE(point)) { + BKE_mask_point_select_set_handle(point, TRUE); + } + else if (toggle) { + BKE_mask_point_select_set_handle(point, FALSE); + } + } + } + else { + if (extend) { + masklay->act_spline = spline; + masklay->act_point = point; + + BKE_mask_point_select_set(point, TRUE); + } + else if (deselect) { + BKE_mask_point_select_set(point, FALSE); + } + else { + masklay->act_spline = spline; + masklay->act_point = point; + + if (!MASKPOINT_ISSEL_ANY(point)) { + BKE_mask_point_select_set(point, TRUE); + } + else if (toggle) { + BKE_mask_point_select_set(point, FALSE); + } + } + } + + masklay->act_spline = spline; + masklay->act_point = point; + + ED_mask_select_flush_all(mask); + + WM_event_add_notifier(C, NC_MASK | ND_SELECT, mask); + + return OPERATOR_FINISHED; + } + else { + MaskSplinePointUW *uw; + + if (ED_mask_feather_find_nearest(C, mask, co, threshold, &masklay, &spline, &point, &uw, NULL)) { + + if (extend) { + masklay->act_spline = spline; + masklay->act_point = point; + + if (uw) uw->flag |= SELECT; + } + else if (deselect) { + if (uw) uw->flag &= ~SELECT; + } + else { + masklay->act_spline = spline; + masklay->act_point = point; + + if (uw) { + if (!(uw->flag & SELECT)) { + uw->flag |= SELECT; + } + else if (toggle) { + uw->flag &= ~SELECT; + } + } + } + + ED_mask_select_flush_all(mask); + + WM_event_add_notifier(C, NC_MASK | ND_SELECT, mask); + + return OPERATOR_FINISHED; + } + } + + return OPERATOR_PASS_THROUGH; +} + +static int select_invoke(bContext *C, wmOperator *op, wmEvent *event) +{ + float co[2]; + + ED_mask_mouse_pos(C, event, co); + + RNA_float_set_array(op->ptr, "location", co); + + return select_exec(C, op); +} + +void MASK_OT_select(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Select"; + ot->description = "Select spline points"; + ot->idname = "MASK_OT_select"; + + /* api callbacks */ + ot->exec = select_exec; + ot->invoke = select_invoke; + ot->poll = ED_maskediting_mask_poll; + + /* flags */ + ot->flag = OPTYPE_UNDO; + + /* properties */ + WM_operator_properties_mouse_select(ot); + + RNA_def_float_vector(ot->srna, "location", 2, NULL, -FLT_MIN, FLT_MAX, + "Location", "Location of vertex in normalized space", -1.0f, 1.0f); +} + + + +/********************** border select operator *********************/ + +static int border_select_exec(bContext *C, wmOperator *op) +{ + Mask *mask = CTX_data_edit_mask(C); + MaskLayer *masklay; + int i; + + rcti rect; + rctf rectf; + int change = FALSE, mode, extend; + + /* get rectangle from operator */ + rect.xmin = RNA_int_get(op->ptr, "xmin"); + rect.ymin = RNA_int_get(op->ptr, "ymin"); + rect.xmax = RNA_int_get(op->ptr, "xmax"); + rect.ymax = RNA_int_get(op->ptr, "ymax"); + + ED_mask_point_pos(C, rect.xmin, rect.ymin, &rectf.xmin, &rectf.ymin); + ED_mask_point_pos(C, rect.xmax, rect.ymax, &rectf.xmax, &rectf.ymax); + + mode = RNA_int_get(op->ptr, "gesture_mode"); + extend = RNA_boolean_get(op->ptr, "extend"); + + /* do actual selection */ + for (masklay = mask->masklayers.first; masklay; masklay = masklay->next) { + MaskSpline *spline; + + if (masklay->restrictflag & (MASK_RESTRICT_VIEW | MASK_RESTRICT_SELECT)) { + continue; + } + + for (spline = masklay->splines.first; spline; spline = spline->next) { + MaskSplinePoint *points_array = BKE_mask_spline_point_array(spline); + + for (i = 0; i < spline->tot_point; i++) { + MaskSplinePoint *point = &spline->points[i]; + MaskSplinePoint *point_deform = &points_array[i]; + + /* TODO: handles? */ + /* TODO: uw? */ + + if (BLI_in_rctf(&rectf, point_deform->bezt.vec[1][0], point_deform->bezt.vec[1][1])) { + BKE_mask_point_select_set(point, mode == GESTURE_MODAL_SELECT); + BKE_mask_point_select_set_handle(point, mode == GESTURE_MODAL_SELECT); + } + else if (!extend) { + BKE_mask_point_select_set(point, FALSE); + BKE_mask_point_select_set_handle(point, FALSE); + } + + change = TRUE; + } + } + } + + if (change) { + ED_mask_select_flush_all(mask); + + WM_event_add_notifier(C, NC_MASK | ND_SELECT, mask); + + return OPERATOR_FINISHED; + } + + return OPERATOR_CANCELLED; +} + +void MASK_OT_select_border(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Border Select"; + ot->description = "Select markers using border selection"; + ot->idname = "MASK_OT_select_border"; + + /* api callbacks */ + ot->invoke = WM_border_select_invoke; + ot->exec = border_select_exec; + ot->modal = WM_border_select_modal; + ot->poll = ED_maskediting_mask_poll; + + /* flags */ + ot->flag = OPTYPE_UNDO; + + /* properties */ + WM_operator_properties_gesture_border(ot, TRUE); +} + +static int do_lasso_select_mask(bContext *C, int mcords[][2], short moves, short select) +{ + Mask *mask = CTX_data_edit_mask(C); + MaskLayer *masklay; + int i; + + rcti rect; + int change = FALSE; + + /* get rectangle from operator */ + BLI_lasso_boundbox(&rect, mcords, moves); + + /* do actual selection */ + for (masklay = mask->masklayers.first; masklay; masklay = masklay->next) { + MaskSpline *spline; + + if (masklay->restrictflag & (MASK_RESTRICT_VIEW | MASK_RESTRICT_SELECT)) { + continue; + } + + for (spline = masklay->splines.first; spline; spline = spline->next) { + MaskSplinePoint *points_array = BKE_mask_spline_point_array(spline); + + for (i = 0; i < spline->tot_point; i++) { + MaskSplinePoint *point = &spline->points[i]; + MaskSplinePoint *point_deform = &points_array[i]; + + /* TODO: handles? */ + /* TODO: uw? */ + + float screen_co[2]; + + /* marker in screen coords */ + ED_mask_point_pos__reverse(C, + point_deform->bezt.vec[1][0], point_deform->bezt.vec[1][1], + &screen_co[0], &screen_co[1]); + + if (BLI_in_rcti(&rect, screen_co[0], screen_co[1]) && + BLI_lasso_is_point_inside(mcords, moves, screen_co[0], screen_co[1], INT_MAX)) + { + BKE_mask_point_select_set(point, select); + BKE_mask_point_select_set_handle(point, select); + } + + change = TRUE; + } + } + } + + if (change) { + ED_mask_select_flush_all(mask); + + WM_event_add_notifier(C, NC_MASK | ND_SELECT, mask); + } + + return change; +} + +static int clip_lasso_select_exec(bContext *C, wmOperator *op) +{ + int mcords_tot; + int (*mcords)[2] = WM_gesture_lasso_path_to_array(C, op, &mcords_tot); + + if (mcords) { + short select; + + select = !RNA_boolean_get(op->ptr, "deselect"); + do_lasso_select_mask(C, mcords, mcords_tot, select); + + MEM_freeN(mcords); + + return OPERATOR_FINISHED; + } + return OPERATOR_PASS_THROUGH; +} + +void MASK_OT_select_lasso(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Lasso Select"; + ot->description = "Select markers using lasso selection"; + ot->idname = "MASK_OT_select_lasso"; + + /* api callbacks */ + ot->invoke = WM_gesture_lasso_invoke; + ot->modal = WM_gesture_lasso_modal; + ot->exec = clip_lasso_select_exec; + ot->poll = ED_maskediting_mask_poll; + ot->cancel = WM_gesture_lasso_cancel; + + /* flags */ + ot->flag = OPTYPE_UNDO; + + /* properties */ + RNA_def_collection_runtime(ot->srna, "path", &RNA_OperatorMousePath, "Path", ""); + RNA_def_boolean(ot->srna, "deselect", 0, "Deselect", "Deselect rather than select items"); + RNA_def_boolean(ot->srna, "extend", 1, "Extend", "Extend selection instead of deselecting everything first"); +} + +/********************** circle select operator *********************/ + +static int mask_spline_point_inside_ellipse(BezTriple *bezt, float offset[2], float ellipse[2]) +{ + /* normalized ellipse: ell[0] = scaleX, ell[1] = scaleY */ + float x, y; + + x = (bezt->vec[1][0] - offset[0]) * ellipse[0]; + y = (bezt->vec[1][1] - offset[1]) * ellipse[1]; + + return x * x + y * y < 1.0f; +} + +static int circle_select_exec(bContext *C, wmOperator *op) +{ + Mask *mask = CTX_data_edit_mask(C); + MaskLayer *masklay; + int i; + + SpaceClip *sc = CTX_wm_space_clip(C); + ARegion *ar = CTX_wm_region(C); + int x, y, radius, width, height, mode, change = FALSE; + float zoomx, zoomy, offset[2], ellipse[2]; + + /* get operator properties */ + x = RNA_int_get(op->ptr, "x"); + y = RNA_int_get(op->ptr, "y"); + radius = RNA_int_get(op->ptr, "radius"); + + mode = RNA_int_get(op->ptr, "gesture_mode"); + + /* TODO - make generic! - this is SpaceClip only! */ + /* compute ellipse and position in unified coordinates */ + ED_space_clip_size(sc, &width, &height); + ED_space_clip_zoom(sc, ar, &zoomx, &zoomy); + width = height = MAX2(width, height); + + ellipse[0] = width * zoomx / radius; + ellipse[1] = height * zoomy / radius; + + ED_mask_point_pos(C, x, y, &offset[0], &offset[1]); + + /* do actual selection */ + for (masklay = mask->masklayers.first; masklay; masklay = masklay->next) { + MaskSpline *spline; + + if (masklay->restrictflag & (MASK_RESTRICT_VIEW | MASK_RESTRICT_SELECT)) { + continue; + } + + for (spline = masklay->splines.first; spline; spline = spline->next) { + MaskSplinePoint *points_array = BKE_mask_spline_point_array(spline); + + for (i = 0; i < spline->tot_point; i++) { + MaskSplinePoint *point = &spline->points[i]; + MaskSplinePoint *point_deform = &points_array[i]; + + if (mask_spline_point_inside_ellipse(&point_deform->bezt, offset, ellipse)) { + BKE_mask_point_select_set(point, mode == GESTURE_MODAL_SELECT); + BKE_mask_point_select_set_handle(point, mode == GESTURE_MODAL_SELECT); + + change = TRUE; + } + } + } + } + + if (change) { + ED_mask_select_flush_all(mask); + + WM_event_add_notifier(C, NC_MASK | ND_SELECT, mask); + + return OPERATOR_FINISHED; + } + + return OPERATOR_CANCELLED; +} + +void MASK_OT_select_circle(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Circle Select"; + ot->description = "Select markers using circle selection"; + ot->idname = "MASK_OT_select_circle"; + + /* api callbacks */ + ot->invoke = WM_gesture_circle_invoke; + ot->modal = WM_gesture_circle_modal; + ot->exec = circle_select_exec; + ot->poll = ED_maskediting_mask_poll; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + /* properties */ + RNA_def_int(ot->srna, "x", 0, INT_MIN, INT_MAX, "X", "", INT_MIN, INT_MAX); + RNA_def_int(ot->srna, "y", 0, INT_MIN, INT_MAX, "Y", "", INT_MIN, INT_MAX); + RNA_def_int(ot->srna, "radius", 0, INT_MIN, INT_MAX, "Radius", "", INT_MIN, INT_MAX); + RNA_def_int(ot->srna, "gesture_mode", 0, INT_MIN, INT_MAX, "Gesture Mode", "", INT_MIN, INT_MAX); +} + +static int mask_select_linked_pick_invoke(bContext *C, wmOperator *op, wmEvent *event) +{ + Mask *mask = CTX_data_edit_mask(C); + MaskLayer *masklay; + MaskSpline *spline; + MaskSplinePoint *point = NULL; + float co[2]; + int do_select = !RNA_boolean_get(op->ptr, "deselect"); + + int is_handle = 0; + const float threshold = 19; + int change = FALSE; + + ED_mask_mouse_pos(C, event, co); + + point = ED_mask_point_find_nearest(C, mask, co, threshold, &masklay, &spline, &is_handle, NULL); + + if (point) { + ED_mask_spline_select_set(spline, do_select); + masklay->act_spline = spline; + masklay->act_point = point; + + change = TRUE; + } + + if (change) { + ED_mask_select_flush_all(mask); + + WM_event_add_notifier(C, NC_MASK | ND_SELECT, mask); + + return OPERATOR_FINISHED; + } + + return OPERATOR_CANCELLED; +} + +void MASK_OT_select_linked_pick(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Select Linked"; + ot->idname = "MASK_OT_select_linked_pick"; + ot->description = "(De)select all points linked to the curve under the mouse cursor"; + + /* api callbacks */ + ot->invoke = mask_select_linked_pick_invoke; + ot->poll = ED_maskediting_mask_poll; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + RNA_def_boolean(ot->srna, "deselect", 0, "Deselect", ""); +} + +static int mask_select_linked_exec(bContext *C, wmOperator *UNUSED(op)) +{ + Mask *mask = CTX_data_edit_mask(C); + MaskLayer *masklay; + + int change = FALSE; + + /* do actual selection */ + for (masklay = mask->masklayers.first; masklay; masklay = masklay->next) { + MaskSpline *spline; + + if (masklay->restrictflag & (MASK_RESTRICT_VIEW | MASK_RESTRICT_SELECT)) { + continue; + } + + for (spline = masklay->splines.first; spline; spline = spline->next) { + if (ED_mask_spline_select_check(spline)) { + ED_mask_spline_select_set(spline, TRUE); + change = TRUE; + } + } + } + + if (change) { + ED_mask_select_flush_all(mask); + + WM_event_add_notifier(C, NC_MASK | ND_SELECT, mask); + + return OPERATOR_FINISHED; + } + + return OPERATOR_CANCELLED; +} + +void MASK_OT_select_linked(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Select Linked All"; + ot->idname = "MASK_OT_select_linked"; + ot->description = "Select all vertices linked to the active mesh"; + + /* api callbacks */ + ot->exec = mask_select_linked_exec; + ot->poll = ED_maskediting_mask_poll; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; +} diff --git a/source/blender/editors/mask/mask_shapekey.c b/source/blender/editors/mask/mask_shapekey.c new file mode 100644 index 00000000000..94aebb9f3a1 --- /dev/null +++ b/source/blender/editors/mask/mask_shapekey.c @@ -0,0 +1,168 @@ +/* + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2012 Blender Foundation. + * All rights reserved. + * + * + * Contributor(s): Blender Foundation, + * Campbell Barton + * + * ***** END GPL LICENSE BLOCK ***** + */ + +/** \file blender/editors/mask/mask_shapekey.c + * \ingroup edmask + */ + +#include "MEM_guardedalloc.h" + +#include "BLI_utildefines.h" +#include "BLI_listbase.h" +#include "BLI_math.h" + +#include "BKE_context.h" +#include "BKE_curve.h" +#include "BKE_depsgraph.h" +#include "BKE_mask.h" + +#include "DNA_mask_types.h" +#include "DNA_scene_types.h" +#include "DNA_object_types.h" /* SELECT */ + +#include "WM_api.h" +#include "WM_types.h" + +#include "ED_screen.h" +#include "ED_mask.h" +#include "ED_clip.h" + +#include "RNA_access.h" +#include "RNA_define.h" + +#include "mask_intern.h" /* own include */ + +static int mask_shape_key_insert_exec(bContext *C, wmOperator *UNUSED(op)) +{ + Scene *scene = CTX_data_scene(C); + const int frame = CFRA; + Mask *mask = CTX_data_edit_mask(C); + MaskLayer *masklay; + int change = FALSE; + + for (masklay = mask->masklayers.first; masklay; masklay = masklay->next) { + MaskLayerShape *masklay_shape; + + if (!ED_mask_layer_select_check(masklay)) { + continue; + } + + masklay_shape = BKE_mask_layer_shape_varify_frame(masklay, frame); + BKE_mask_layer_shape_from_mask(masklay, masklay_shape); + change = TRUE; + } + + if (change) { + WM_event_add_notifier(C, NC_MASK | ND_DATA, mask); + DAG_id_tag_update(&mask->id, 0); + + return OPERATOR_FINISHED; + } + else { + return OPERATOR_CANCELLED; + } +} + +void MASK_OT_shape_key_insert(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Insert Shape Key"; + ot->description = ""; + ot->idname = "MASK_OT_shape_key_insert"; + + /* api callbacks */ + ot->exec = mask_shape_key_insert_exec; + ot->poll = ED_maskediting_mask_poll; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; +} + +static int mask_shape_key_clear_exec(bContext *C, wmOperator *UNUSED(op)) +{ + Scene *scene = CTX_data_scene(C); + const int frame = CFRA; + Mask *mask = CTX_data_edit_mask(C); + MaskLayer *masklay; + int change = FALSE; + + for (masklay = mask->masklayers.first; masklay; masklay = masklay->next) { + MaskLayerShape *masklay_shape; + + if (!ED_mask_layer_select_check(masklay)) { + continue; + } + + masklay_shape = BKE_mask_layer_shape_find_frame(masklay, frame); + + if (masklay_shape) { + BKE_mask_layer_shape_unlink(masklay, masklay_shape); + change = TRUE; + } + } + + if (change) { + WM_event_add_notifier(C, NC_MASK | ND_DATA, mask); + DAG_id_tag_update(&mask->id, 0); + + return OPERATOR_FINISHED; + } + else { + return OPERATOR_CANCELLED; + } +} + +void MASK_OT_shape_key_clear(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Clear Shape Key"; + ot->description = ""; + ot->idname = "MASK_OT_shape_key_clear"; + + /* api callbacks */ + ot->exec = mask_shape_key_clear_exec; + ot->poll = ED_maskediting_mask_poll; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; +} + +int ED_mask_layer_shape_auto_key_all(Mask *mask, const int frame) +{ + MaskLayer *masklay; + int change = FALSE; + + for (masklay = mask->masklayers.first; masklay; masklay = masklay->next) { + MaskLayerShape *masklay_shape; + + masklay_shape = BKE_mask_layer_shape_varify_frame(masklay, frame); + BKE_mask_layer_shape_from_mask(masklay, masklay_shape); + change = TRUE; + } + + return change; +} -- cgit v1.2.3