Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorCampbell Barton <ideasman42@gmail.com>2012-06-04 19:36:16 +0400
committerCampbell Barton <ideasman42@gmail.com>2012-06-04 19:36:16 +0400
commit115322ef08ed3cff8751a90139024c8b7f3453b1 (patch)
tree766f431495399927d90e2f91b05ef2eaf8be7896 /source/blender/editors
parent8e01389a4e34b81c28aea7ac7fbb590b314624e3 (diff)
mask merge (initial copy of editor files)
Diffstat (limited to 'source/blender/editors')
-rw-r--r--source/blender/editors/mask/CMakeLists.txt51
-rw-r--r--source/blender/editors/mask/SConscript9
-rw-r--r--source/blender/editors/mask/mask_add.c707
-rw-r--r--source/blender/editors/mask/mask_draw.c428
-rw-r--r--source/blender/editors/mask/mask_edit.c327
-rw-r--r--source/blender/editors/mask/mask_intern.h111
-rw-r--r--source/blender/editors/mask/mask_ops.c1171
-rw-r--r--source/blender/editors/mask/mask_relationships.c183
-rw-r--r--source/blender/editors/mask/mask_select.c770
-rw-r--r--source/blender/editors/mask/mask_shapekey.c168
10 files changed, 3925 insertions, 0 deletions
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;
+}