This repository has been archived on 2023-10-09. You can view files and clone it, but cannot push or open issues or pull requests.
Files
blender-archive/source/blender/editors/mask/mask_add.c

849 lines
22 KiB
C

/*
* ***** 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_math.h"
#include "BKE_context.h"
#include "BKE_depsgraph.h"
#include "BKE_mask.h"
#include "DNA_scene_types.h"
#include "DNA_screen_types.h"
#include "DNA_mask_types.h"
#include "WM_api.h"
#include "WM_types.h"
#include "ED_mask.h" /* own include */
#include "ED_screen.h"
#include "RNA_access.h"
#include "RNA_define.h"
#include "mask_intern.h" /* own include */
bool ED_mask_find_nearest_diff_point(const bContext *C,
struct Mask *mask,
const float normal_co[2],
int threshold, bool feather,
float tangent[2],
const bool use_deform,
const bool use_project,
MaskLayer **masklay_r,
MaskSpline **spline_r,
MaskSplinePoint **point_r,
float *u_r,
float *score_r)
{
ScrArea *sa = CTX_wm_area(C);
ARegion *ar = CTX_wm_region(C);
MaskLayer *masklay, *point_masklay;
MaskSpline *point_spline;
MaskSplinePoint *point = NULL;
float dist = FLT_MAX, co[2];
int width, height;
float u;
float scalex, scaley;
ED_mask_get_size(sa, &width, &height);
ED_mask_pixelspace_factor(sa, ar, &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;
MaskSplinePoint *cur_point;
for (i = 0, cur_point = use_deform ? spline->points_deform : spline->points;
i < spline->tot_point;
i++, cur_point++)
{
float *diff_points;
unsigned int tot_diff_point;
diff_points = BKE_mask_point_segment_diff(spline, cur_point, width, height,
&tot_diff_point);
if (diff_points) {
int j, tot_point;
unsigned int tot_feather_point;
float *feather_points = NULL, *points;
if (feather) {
feather_points = BKE_mask_point_segment_feather_diff(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 (j = 0; j < tot_point - 1; j++) {
float cur_dist, a[2], b[2];
a[0] = points[2 * j] * scalex;
a[1] = points[2 * j + 1] * scaley;
b[0] = points[2 * j + 2] * scalex;
b[1] = points[2 * j + 3] * scaley;
cur_dist = dist_to_line_segment_v2(co, a, b);
if (cur_dist < dist) {
if (tangent)
sub_v2_v2v2(tangent, &diff_points[2 * j + 2], &diff_points[2 * j]);
point_masklay = masklay;
point_spline = spline;
point = use_deform ? &spline->points[(cur_point - spline->points_deform)] : cur_point;
dist = cur_dist;
u = (float)j / 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) {
/* TODO(sergey): Projection fails in some weirdo cases.. */
if (use_project) {
u = BKE_mask_spline_project_co(point_spline, point, u, normal_co, MASK_PROJ_ANY);
}
*u_r = u;
}
if (score_r) {
*score_r = dist;
}
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(Mask *mask, MaskSpline *spline, MaskSplinePoint *new_point,
const float point_co[2], const float u,
MaskSplinePoint *reference_point, const bool reference_adjacent)
{
MaskSplinePoint *prev_point = NULL;
MaskSplinePoint *next_point = NULL;
BezTriple *bezt;
float co[3];
copy_v2_v2(co, point_co);
co[2] = 0.0f;
/* point coordinate */
bezt = &new_point->bezt;
bezt->h1 = bezt->h2 = HD_ALIGN;
if (reference_point) {
if (reference_point->bezt.h1 == HD_VECT && reference_point->bezt.h2 == HD_VECT) {
/* If the reference point is sharp try using some smooth point as reference
* for handles.
*/
int point_index = reference_point - spline->points;
int delta = new_point == spline->points ? 1 : -1;
int i = 0;
for (i = 0; i < spline->tot_point - 1; ++i) {
MaskSplinePoint *current_point;
point_index += delta;
if (point_index == -1 || point_index >= spline->tot_point) {
if (spline->flag & MASK_SPLINE_CYCLIC) {
if (point_index == -1) {
point_index = spline->tot_point - 1;
}
else if (point_index >= spline->tot_point) {
point_index = 0;
}
}
else {
break;
}
}
current_point = &spline->points[point_index];
if (current_point->bezt.h1 != HD_VECT || current_point->bezt.h2 != HD_VECT) {
bezt->h1 = bezt->h2 = MAX2(current_point->bezt.h2, current_point->bezt.h1);
break;
}
}
}
else {
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);
BKE_mask_parent_init(&new_point->parent);
if (spline->tot_point != 1) {
BKE_mask_calc_handle_adjacent_interp(spline, new_point, u);
}
/* 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, bool check_active)
{
MaskSpline *cur_spline = masklay->splines.first;
*spline = NULL;
*point = NULL;
if (check_active) {
/* TODO, having an active point but no active spline is possible, why? */
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 bool add_vertex_subdivide(const 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 (ED_mask_find_nearest_diff_point(C, mask, co, threshold, false, tangent, true, true,
&masklay, &spline, &point, &u, NULL))
{
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(mask, spline, new_point, co, 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_spline = spline;
masklay->act_point = new_point;
WM_event_add_notifier(C, NC_MASK | NA_EDITED, mask);
return true;
}
return false;
}
static bool add_vertex_extrude(const 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];
bool do_cyclic_correct = false;
bool 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(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;
}
else if (((spline->flag & MASK_SPLINE_CYCLIC) == 0) && (point_index == spline->tot_point - 1)) {
do_prev = false;
}
else {
do_prev = false; /* quiet warning */
/* 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(mask, spline, new_point, co, 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);
}
WM_event_add_notifier(C, NC_MASK | NA_EDITED, mask);
return true;
}
static bool add_vertex_new(const 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(mask, spline, new_point, co, 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)
{
Scene *scene = CTX_data_scene(C);
Mask *mask = CTX_data_edit_mask(C);
MaskLayer *masklay;
float co[2];
if (mask == NULL) {
/* if there's no active mask, create one */
mask = ED_mask_new(C, NULL);
}
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);
/* TODO, having an active point but no active spline is possible, why? */
if (masklay && masklay->act_spline && 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;
const bool is_sta = (point == spline->points);
const bool 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(spline, point, false);
BKE_mask_calc_handle_point_auto(spline, point_other, false);
/* TODO: only update this spline */
BKE_mask_update_display(mask, 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, CFRA);
return OPERATOR_FINISHED;
}
static int add_vertex_invoke(bContext *C, wmOperator *op, const wmEvent *event)
{
ScrArea *sa = CTX_wm_area(C);
ARegion *ar = CTX_wm_region(C);
float co[2];
ED_mask_mouse_pos(sa, ar, event->mval, 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_operator_mask;
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
/* properties */
RNA_def_float_vector(ot->srna, "location", 2, NULL, -FLT_MAX, 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 (ED_mask_find_nearest_diff_point(C, mask, co, threshold, true, NULL, true, true,
&masklay, &spline, &point, &u, NULL))
{
Scene *scene = CTX_data_scene(C);
float w = BKE_mask_point_weight(spline, point, u);
float weight_scalar = BKE_mask_point_weight_scalar(spline, point, u);
if (weight_scalar != 0.0f) {
w = w / weight_scalar;
}
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, const wmEvent *event)
{
ScrArea *sa = CTX_wm_area(C);
ARegion *ar = CTX_wm_region(C);
float co[2];
ED_mask_mouse_pos(sa, ar, event->mval, 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_maskedit_mask_poll;
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
/* properties */
RNA_def_float_vector(ot->srna, "location", 2, NULL, -FLT_MAX, FLT_MAX,
"Location", "Location of vertex in normalized space", -1.0f, 1.0f);
}
/******************** common primitive functions *********************/
static int create_primitive_from_points(bContext *C, wmOperator *op, const float (*points)[2],
int num_points, char handle_type)
{
ScrArea *sa = CTX_wm_area(C);
Scene *scene = CTX_data_scene(C);
Mask *mask;
MaskLayer *mask_layer;
MaskSpline *new_spline;
float scale, location[2], frame_size[2];
int i, width, height;
int size = RNA_float_get(op->ptr, "size");
ED_mask_get_size(sa, &width, &height);
scale = (float)size / max_ii(width, height);
/* Get location in mask space. */
frame_size[0] = width;
frame_size[1] = height;
RNA_float_get_array(op->ptr, "location", location);
location[0] /= width;
location[1] /= height;
BKE_mask_coord_from_frame(location, location, frame_size);
/* Make it so new primitive is centered to mouse location. */
location[0] -= 0.5f * scale;
location[1] -= 0.5f * scale;
mask_layer = ED_mask_layer_ensure(C);
mask = CTX_data_edit_mask(C);
ED_mask_select_toggle_all(mask, SEL_DESELECT);
new_spline = BKE_mask_spline_add(mask_layer);
new_spline->flag = MASK_SPLINE_CYCLIC | SELECT;
new_spline->tot_point = num_points;
new_spline->points = MEM_recallocN(new_spline->points,
sizeof(MaskSplinePoint) * new_spline->tot_point);
mask_layer->act_spline = new_spline;
mask_layer->act_point = NULL;
for (i = 0; i < num_points; i++) {
MaskSplinePoint *new_point = &new_spline->points[i];
copy_v2_v2(new_point->bezt.vec[1], points[i]);
mul_v2_fl(new_point->bezt.vec[1], scale);
add_v2_v2(new_point->bezt.vec[1], location);
new_point->bezt.h1 = handle_type;
new_point->bezt.h2 = handle_type;
BKE_mask_point_select_set(new_point, true);
}
WM_event_add_notifier(C, NC_MASK | NA_EDITED, mask);
/* TODO: only update this spline */
BKE_mask_update_display(mask, CFRA);
return OPERATOR_FINISHED;
}
static int primitive_add_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event))
{
ScrArea *sa = CTX_wm_area(C);
float cursor[2];
int width, height;
ED_mask_get_size(sa, &width, &height);
ED_mask_cursor_location_get(sa, cursor);
cursor[0] *= width;
cursor[1] *= height;
RNA_float_set_array(op->ptr, "location", cursor);
return op->type->exec(C, op);
}
static void define_prinitive_add_properties(wmOperatorType *ot)
{
RNA_def_float(ot->srna, "size", 100, -FLT_MAX, FLT_MAX,
"Size", "Size of new circle", -FLT_MAX, FLT_MAX);
RNA_def_float_vector(ot->srna, "location", 2, NULL, -FLT_MAX, FLT_MAX,
"Location", "Location of new circle", -FLT_MAX, FLT_MAX);
}
/******************** primitive add circle *********************/
static int primitive_circle_add_exec(bContext *C, wmOperator *op)
{
const float points[4][2] = {{0.0f, 0.5f},
{0.5f, 1.0f},
{1.0f, 0.5f},
{0.5f, 0.0f}};
int num_points = sizeof(points) / (2 * sizeof(float));
create_primitive_from_points(C, op, points, num_points, HD_AUTO);
return OPERATOR_FINISHED;
}
void MASK_OT_primitive_circle_add(wmOperatorType *ot)
{
/* identifiers */
ot->name = "Add Circle";
ot->description = "Add new circle-shaped spline";
ot->idname = "MASK_OT_primitive_circle_add";
/* api callbacks */
ot->exec = primitive_circle_add_exec;
ot->invoke = primitive_add_invoke;
ot->poll = ED_operator_mask;
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
/* properties */
define_prinitive_add_properties(ot);
}
/******************** primitive add suqare *********************/
static int primitive_square_add_exec(bContext *C, wmOperator *op)
{
const float points[4][2] = {{0.0f, 0.0f},
{0.0f, 1.0f},
{1.0f, 1.0f},
{1.0f, 0.0f}};
int num_points = sizeof(points) / (2 * sizeof(float));
create_primitive_from_points(C, op, points, num_points, HD_VECT);
return OPERATOR_FINISHED;
}
void MASK_OT_primitive_square_add(wmOperatorType *ot)
{
/* identifiers */
ot->name = "Add Square";
ot->description = "Add new square-shaped spline";
ot->idname = "MASK_OT_primitive_square_add";
/* api callbacks */
ot->exec = primitive_square_add_exec;
ot->invoke = primitive_add_invoke;
ot->poll = ED_operator_mask;
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
/* properties */
define_prinitive_add_properties(ot);
}