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:
authorLukas Tönne <lukas.toenne@gmail.com>2016-04-20 17:45:29 +0300
committerLukas Tönne <lukas.toenne@gmail.com>2016-04-20 17:45:29 +0300
commitaae0598aa233d43d0ffc81a070b2ce20d6871c1d (patch)
tree378100f45cf1db972c671a6886496a5604f7ab29 /source/blender/editors/curve
parent95d7d3c2a6aa91f1dcb33ff0cdfc751e2886d8cd (diff)
parentd7e4f920fd93a4ae5679e0eb6b228a349ab3b082 (diff)
Merge branch 'master' into temp_depsgraph_split_ubereval
Diffstat (limited to 'source/blender/editors/curve')
-rw-r--r--source/blender/editors/curve/CMakeLists.txt8
-rw-r--r--source/blender/editors/curve/SConscript49
-rw-r--r--source/blender/editors/curve/curve_intern.h6
-rw-r--r--source/blender/editors/curve/curve_ops.c6
-rw-r--r--source/blender/editors/curve/editcurve.c219
-rw-r--r--source/blender/editors/curve/editcurve_add.c15
-rw-r--r--source/blender/editors/curve/editcurve_paint.c1180
-rw-r--r--source/blender/editors/curve/editcurve_select.c44
-rw-r--r--source/blender/editors/curve/editfont.c226
9 files changed, 1513 insertions, 240 deletions
diff --git a/source/blender/editors/curve/CMakeLists.txt b/source/blender/editors/curve/CMakeLists.txt
index 83346e9550c..ebdf6bb43ff 100644
--- a/source/blender/editors/curve/CMakeLists.txt
+++ b/source/blender/editors/curve/CMakeLists.txt
@@ -23,20 +23,24 @@ set(INC
../../blenkernel
../../blenlib
../../blentranslation
+ ../../gpu
../../makesdna
../../makesrna
../../windowmanager
../../../../intern/guardedalloc
+ ../../../../intern/glew-mx
+ ../../../../extern/curve_fit_nd
)
set(INC_SYS
-
+ ${GLEW_INCLUDE_PATH}
)
set(SRC
curve_ops.c
editcurve.c
editcurve_add.c
+ editcurve_paint.c
editcurve_select.c
editfont.c
@@ -47,4 +51,6 @@ if(WITH_INTERNATIONAL)
add_definitions(-DWITH_INTERNATIONAL)
endif()
+add_definitions(${GL_DEFINITIONS})
+
blender_add_lib(bf_editor_curve "${SRC}" "${INC}" "${INC_SYS}")
diff --git a/source/blender/editors/curve/SConscript b/source/blender/editors/curve/SConscript
deleted file mode 100644
index eadec4f65b6..00000000000
--- a/source/blender/editors/curve/SConscript
+++ /dev/null
@@ -1,49 +0,0 @@
-#!/usr/bin/env python
-#
-# ***** 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) 2006, Blender Foundation
-# All rights reserved.
-#
-# The Original Code is: all of this file.
-#
-# Contributor(s): Nathan Letwory.
-#
-# ***** END GPL LICENSE BLOCK *****
-
-Import ('env')
-
-sources = env.Glob('*.c')
-
-defs = []
-
-incs = [
- '#/intern/guardedalloc',
- '../include',
- '../../blenkernel',
- '../../blenlib',
- '../../blentranslation',
- '../../makesdna',
- '../../makesrna',
- '../../render/extern/include',
- '../../windowmanager',
- ]
-
-if env['WITH_BF_INTERNATIONAL']:
- defs.append('WITH_INTERNATIONAL')
-
-env.BlenderLib('bf_editors_curve', sources, incs, defs, libtype=['core'], priority=[45])
diff --git a/source/blender/editors/curve/curve_intern.h b/source/blender/editors/curve/curve_intern.h
index 29904bf2344..d63616e4f43 100644
--- a/source/blender/editors/curve/curve_intern.h
+++ b/source/blender/editors/curve/curve_intern.h
@@ -79,7 +79,6 @@ void FONT_OT_text_copy(struct wmOperatorType *ot);
void FONT_OT_text_cut(struct wmOperatorType *ot);
void FONT_OT_text_paste(struct wmOperatorType *ot);
void FONT_OT_text_paste_from_file(struct wmOperatorType *ot);
-void FONT_OT_text_paste_from_clipboard(struct wmOperatorType *ot);
void FONT_OT_move(struct wmOperatorType *ot);
void FONT_OT_move_select(struct wmOperatorType *ot);
@@ -134,7 +133,7 @@ bool ED_curve_pick_vert(
/* helper functions */
void ed_editnurb_translate_flag(struct ListBase *editnurb, short flag, const float vec[3]);
-bool ed_editnurb_extrude_flag(struct EditNurb *editnurb, short flag);
+bool ed_editnurb_extrude_flag(struct EditNurb *editnurb, const short flag);
bool ed_editnurb_spin(float viewmat[4][4], struct Object *obedit, const float axis[3], const float cent[3]);
/* editcurve_select.c */
@@ -167,4 +166,7 @@ void SURFACE_OT_primitive_nurbs_surface_cylinder_add(struct wmOperatorType *ot);
void SURFACE_OT_primitive_nurbs_surface_sphere_add(struct wmOperatorType *ot);
void SURFACE_OT_primitive_nurbs_surface_torus_add(struct wmOperatorType *ot);
+/* editcurve_paint.c */
+void CURVE_OT_draw(struct wmOperatorType *ot);
+
#endif /* __CURVE_INTERN_H__ */
diff --git a/source/blender/editors/curve/curve_ops.c b/source/blender/editors/curve/curve_ops.c
index 4828fb3ec5f..d1994c8fc15 100644
--- a/source/blender/editors/curve/curve_ops.c
+++ b/source/blender/editors/curve/curve_ops.c
@@ -66,7 +66,6 @@ void ED_operatortypes_curve(void)
WM_operatortype_append(FONT_OT_text_cut);
WM_operatortype_append(FONT_OT_text_paste);
WM_operatortype_append(FONT_OT_text_paste_from_file);
- WM_operatortype_append(FONT_OT_text_paste_from_clipboard);
WM_operatortype_append(FONT_OT_move);
WM_operatortype_append(FONT_OT_move_select);
@@ -136,6 +135,7 @@ void ED_operatortypes_curve(void)
WM_operatortype_append(CURVE_OT_make_segment);
WM_operatortype_append(CURVE_OT_spin);
WM_operatortype_append(CURVE_OT_vertex_add);
+ WM_operatortype_append(CURVE_OT_draw);
WM_operatortype_append(CURVE_OT_extrude);
WM_operatortype_append(CURVE_OT_cyclic_toggle);
@@ -214,6 +214,7 @@ void ED_keymap_curve(wmKeyConfig *keyconf)
WM_keymap_add_item(keymap, "FONT_OT_text_cut", XKEY, KM_PRESS, KM_CTRL, 0);
WM_keymap_add_item(keymap, "FONT_OT_text_paste", VKEY, KM_PRESS, KM_CTRL, 0);
#ifdef __APPLE__
+ WM_keymap_add_item(keymap, "FONT_OT_select_all", AKEY, KM_PRESS, KM_OSKEY, 0);
WM_keymap_add_item(keymap, "FONT_OT_text_copy", CKEY, KM_PRESS, KM_OSKEY, 0);
WM_keymap_add_item(keymap, "FONT_OT_text_cut", XKEY, KM_PRESS, KM_OSKEY, 0);
WM_keymap_add_item(keymap, "FONT_OT_text_paste", VKEY, KM_PRESS, KM_OSKEY, 0);
@@ -234,6 +235,9 @@ void ED_keymap_curve(wmKeyConfig *keyconf)
WM_keymap_add_item(keymap, "CURVE_OT_vertex_add", ACTIONMOUSE, KM_CLICK, KM_CTRL, 0);
+ kmi = WM_keymap_add_item(keymap, "CURVE_OT_draw", ACTIONMOUSE, KM_PRESS, KM_SHIFT, 0);
+ RNA_boolean_set(kmi->ptr, "wait_for_input", false);
+
kmi = WM_keymap_add_item(keymap, "CURVE_OT_select_all", AKEY, KM_PRESS, 0, 0);
RNA_enum_set(kmi->ptr, "action", SEL_TOGGLE);
kmi = WM_keymap_add_item(keymap, "CURVE_OT_select_all", IKEY, KM_PRESS, KM_CTRL, 0);
diff --git a/source/blender/editors/curve/editcurve.c b/source/blender/editors/curve/editcurve.c
index f7dab0c0935..9df611b3216 100644
--- a/source/blender/editors/curve/editcurve.c
+++ b/source/blender/editors/curve/editcurve.c
@@ -49,6 +49,7 @@
#include "BKE_fcurve.h"
#include "BKE_global.h"
#include "BKE_key.h"
+#include "BKE_library.h"
#include "BKE_main.h"
#include "BKE_report.h"
#include "BKE_animsys.h"
@@ -1177,7 +1178,7 @@ static void remap_hooks_and_vertex_parents(Object *obedit)
}
/* load editNurb in object */
-void load_editNurb(Object *obedit)
+void ED_curve_editnurb_load(Object *obedit)
{
ListBase *editnurb = object_editcurve_get(obedit);
@@ -1209,7 +1210,7 @@ void load_editNurb(Object *obedit)
}
/* make copy in cu->editnurb */
-void make_editNurb(Object *obedit)
+void ED_curve_editnurb_make(Object *obedit)
{
Curve *cu = (Curve *)obedit->data;
EditNurb *editnurb = cu->editnurb;
@@ -1252,7 +1253,7 @@ void make_editNurb(Object *obedit)
}
}
-void free_editNurb(Object *obedit)
+void ED_curve_editnurb_free(Object *obedit)
{
Curve *cu = obedit->data;
@@ -1298,10 +1299,10 @@ static int separate_exec(bContext *C, wmOperator *op)
newob = newbase->object;
newcu = newob->data = BKE_curve_copy(oldcu);
newcu->editnurb = NULL;
- oldcu->id.us--; /* because new curve is a copy: reduce user count */
+ id_us_min(&oldcu->id); /* because new curve is a copy: reduce user count */
/* 3. put new object in editmode, clear it and set separated nurbs */
- make_editNurb(newob);
+ ED_curve_editnurb_make(newob);
newedit = newcu->editnurb;
BKE_nurbList_free(&newedit->nurbs);
BKE_curve_editNurb_keyIndex_free(newedit);
@@ -1309,8 +1310,8 @@ static int separate_exec(bContext *C, wmOperator *op)
BLI_movelisttolist(&newedit->nurbs, &newnurb);
/* 4. put old object out of editmode and delete separated geometry */
- load_editNurb(newob);
- free_editNurb(newob);
+ ED_curve_editnurb_load(newob);
+ ED_curve_editnurb_free(newob);
curve_delete_segments(oldob, true);
DAG_id_tag_update(&oldob->id, OB_RECALC_DATA); /* this is the original one */
@@ -1351,7 +1352,11 @@ static int curve_split_exec(bContext *C, wmOperator *op)
adduplicateflagNurb(obedit, &newnurb, SELECT, true);
if (BLI_listbase_is_empty(&newnurb) == false) {
+ Curve *cu = obedit->data;
+ const int len_orig = BLI_listbase_count(editnurb);
+
curve_delete_segments(obedit, true);
+ cu->actnu -= len_orig - BLI_listbase_count(editnurb);
BLI_movelisttolist(editnurb, &newnurb);
if (ED_curve_updateAnimPaths(obedit->data))
@@ -1385,7 +1390,9 @@ void CURVE_OT_split(wmOperatorType *ot)
/* ******************* FLAGS ********************* */
-static short isNurbselUV(Nurb *nu, int *u, int *v, int flag)
+static bool isNurbselUV(
+ const Nurb *nu, int flag,
+ int *r_u, int *r_v)
{
/* return (u != -1): 1 row in u-direction selected. U has value between 0-pntsv
* return (v != -1): 1 column in v-direction selected. V has value between 0-pntsu
@@ -1393,7 +1400,7 @@ static short isNurbselUV(Nurb *nu, int *u, int *v, int flag)
BPoint *bp;
int a, b, sel;
- *u = *v = -1;
+ *r_u = *r_v = -1;
bp = nu->bp;
for (b = 0; b < nu->pntsv; b++) {
@@ -1402,7 +1409,7 @@ static short isNurbselUV(Nurb *nu, int *u, int *v, int flag)
if (bp->f1 & flag) sel++;
}
if (sel == nu->pntsu) {
- if (*u == -1) *u = b;
+ if (*r_u == -1) *r_u = b;
else return 0;
}
else if (sel > 1) {
@@ -1417,7 +1424,7 @@ static short isNurbselUV(Nurb *nu, int *u, int *v, int flag)
if (bp->f1 & flag) sel++;
}
if (sel == nu->pntsv) {
- if (*v == -1) *v = a;
+ if (*r_v == -1) *r_v = a;
else return 0;
}
else if (sel > 1) {
@@ -1425,8 +1432,8 @@ static short isNurbselUV(Nurb *nu, int *u, int *v, int flag)
}
}
- if (*u == -1 && *v > -1) return 1;
- if (*v == -1 && *u > -1) return 1;
+ if (*r_u == -1 && *r_v > -1) return 1;
+ if (*r_v == -1 && *r_u > -1) return 1;
return 0;
}
@@ -1846,7 +1853,7 @@ bool ed_editnurb_extrude_flag(EditNurb *editnurb, const short flag)
else {
/* which row or column is selected */
- if (isNurbselUV(nu, &u, &v, flag)) {
+ if (isNurbselUV(nu, flag, &u, &v)) {
/* deselect all */
bp = nu->bp;
@@ -1918,18 +1925,30 @@ bool ed_editnurb_extrude_flag(EditNurb *editnurb, const short flag)
return ok;
}
+static bool calc_duplicate_actvert(
+ const ListBase *editnurb, const ListBase *newnurb, Curve *cu,
+ int start, int end, int vert)
+{
+ if ((start <= cu->actvert) && (end > cu->actvert)) {
+ cu->actvert = vert;
+ cu->actnu = BLI_listbase_count(editnurb) + BLI_listbase_count(newnurb);
+ return true;
+ }
+ return false;
+}
+
static void adduplicateflagNurb(Object *obedit, ListBase *newnurb,
const short flag, const bool split)
{
ListBase *editnurb = object_editcurve_get(obedit);
- Nurb *nu = editnurb->last, *newnu;
+ Nurb *nu, *newnu;
BezTriple *bezt, *bezt1;
BPoint *bp, *bp1, *bp2, *bp3;
Curve *cu = (Curve *)obedit->data;
- int a, b, c, starta, enda, diffa, cyclicu, cyclicv, newu, newv;
+ int a, b, c, starta, enda, diffa, cyclicu, cyclicv, newu, newv, i;
char *usel;
- while (nu) {
+ for (i = 0, nu = editnurb->first; nu; i++, nu = nu->next) {
cyclicu = cyclicv = 0;
if (nu->type == CU_BEZIER) {
for (a = 0, bezt = nu->bezt; a < nu->pntsu; a++, bezt++) {
@@ -1950,12 +1969,21 @@ static void adduplicateflagNurb(Object *obedit, ListBase *newnurb,
}
else {
if (enda == nu->pntsu - 1) newu += cyclicu;
+ if (i == cu->actnu) {
+ calc_duplicate_actvert(
+ editnurb, newnurb, cu,
+ starta, starta + diffa, cu->actvert - starta);
+ }
newnu = BKE_nurb_copy(nu, newu, 1);
- BLI_addtail(newnurb, newnu);
memcpy(newnu->bezt, &nu->bezt[starta], diffa * sizeof(BezTriple));
if (newu != diffa) {
memcpy(&newnu->bezt[diffa], nu->bezt, cyclicu * sizeof(BezTriple));
+ if (i == cu->actnu) {
+ calc_duplicate_actvert(
+ editnurb, newnurb, cu,
+ 0, cyclicu, newu - cyclicu + cu->actvert);
+ }
cyclicu = 0;
}
@@ -1964,19 +1992,28 @@ static void adduplicateflagNurb(Object *obedit, ListBase *newnurb,
for (b = 0, bezt1 = newnu->bezt; b < newnu->pntsu; b++, bezt1++) {
select_beztriple(bezt1, SELECT, flag, HIDDEN);
}
+
+ BLI_addtail(newnurb, newnu);
}
}
}
if (cyclicu != 0) {
+ if (i == cu->actnu) {
+ calc_duplicate_actvert(
+ editnurb, newnurb, cu,
+ 0, cyclicu, cu->actvert);
+ }
+
newnu = BKE_nurb_copy(nu, cyclicu, 1);
- BLI_addtail(newnurb, newnu);
memcpy(newnu->bezt, nu->bezt, cyclicu * sizeof(BezTriple));
newnu->flagu &= ~CU_NURB_CYCLIC;
for (b = 0, bezt1 = newnu->bezt; b < newnu->pntsu; b++, bezt1++) {
select_beztriple(bezt1, SELECT, flag, HIDDEN);
}
+
+ BLI_addtail(newnurb, newnu);
}
}
else if (nu->pntsv == 1) { /* because UV Nurb has a different method for dupli */
@@ -1998,12 +2035,21 @@ static void adduplicateflagNurb(Object *obedit, ListBase *newnurb,
}
else {
if (enda == nu->pntsu - 1) newu += cyclicu;
+ if (i == cu->actnu) {
+ calc_duplicate_actvert(
+ editnurb, newnurb, cu,
+ starta, starta + diffa, cu->actvert - starta);
+ }
newnu = BKE_nurb_copy(nu, newu, 1);
- BLI_addtail(newnurb, newnu);
memcpy(newnu->bp, &nu->bp[starta], diffa * sizeof(BPoint));
if (newu != diffa) {
memcpy(&newnu->bp[diffa], nu->bp, cyclicu * sizeof(BPoint));
+ if (i == cu->actnu) {
+ calc_duplicate_actvert(
+ editnurb, newnurb, cu,
+ 0, cyclicu, newu - cyclicu + cu->actvert);
+ }
cyclicu = 0;
}
@@ -2012,19 +2058,28 @@ static void adduplicateflagNurb(Object *obedit, ListBase *newnurb,
for (b = 0, bp1 = newnu->bp; b < newnu->pntsu; b++, bp1++) {
select_bpoint(bp1, SELECT, flag, HIDDEN);
}
+
+ BLI_addtail(newnurb, newnu);
}
}
}
if (cyclicu != 0) {
+ if (i == cu->actnu) {
+ calc_duplicate_actvert(
+ editnurb, newnurb, cu,
+ 0, cyclicu, cu->actvert);
+ }
+
newnu = BKE_nurb_copy(nu, cyclicu, 1);
- BLI_addtail(newnurb, newnu);
memcpy(newnu->bp, nu->bp, cyclicu * sizeof(BPoint));
newnu->flagu &= ~CU_NURB_CYCLIC;
for (b = 0, bp1 = newnu->bp; b < newnu->pntsu; b++, bp1++) {
select_bpoint(bp1, SELECT, flag, HIDDEN);
}
+
+ BLI_addtail(newnurb, newnu);
}
}
else {
@@ -2100,6 +2155,28 @@ static void adduplicateflagNurb(Object *obedit, ListBase *newnurb,
memcpy(&newnu->bp[b * newnu->pntsu], &nu->bp[b * nu->pntsu + a], newu * sizeof(BPoint));
memcpy(&newnu->bp[b * newnu->pntsu + newu], &nu->bp[b * nu->pntsu], cyclicu * sizeof(BPoint));
}
+
+ if (cu->actnu == i) {
+ for (b = 0, diffa = 0; b < newv; b++, diffa += nu->pntsu - newu) {
+ starta = b * nu->pntsu + a;
+ if (calc_duplicate_actvert(
+ editnurb, newnurb, cu,
+ cu->actvert, starta,
+ cu->actvert % nu->pntsu + newu + b * newnu->pntsu))
+ {
+ /* actvert in cyclicu selection */
+ break;
+ }
+ else if (calc_duplicate_actvert(
+ editnurb, newnurb, cu,
+ starta, starta + newu,
+ cu->actvert - starta + b * newnu->pntsu))
+ {
+ /* actvert in 'current' iteration selection */
+ break;
+ }
+ }
+ }
cyclicu = cyclicv = 0;
}
else if ((a / nu->pntsu) + newv == nu->pntsv && cyclicv != 0) {
@@ -2107,6 +2184,14 @@ static void adduplicateflagNurb(Object *obedit, ListBase *newnurb,
newnu = BKE_nurb_copy(nu, newu, newv + cyclicv);
memcpy(newnu->bp, &nu->bp[a], newu * newv * sizeof(BPoint));
memcpy(&newnu->bp[newu * newv], nu->bp, newu * cyclicv * sizeof(BPoint));
+
+ /* check for actvert in cylicv selection */
+ if (cu->actnu == i) {
+ calc_duplicate_actvert(
+ editnurb, newnurb, cu,
+ cu->actvert, a,
+ (newu * newv) + cu->actvert);
+ }
cyclicu = cyclicv = 0;
}
else {
@@ -2115,6 +2200,20 @@ static void adduplicateflagNurb(Object *obedit, ListBase *newnurb,
memcpy(&newnu->bp[b * newu], &nu->bp[b * nu->pntsu + a], newu * sizeof(BPoint));
}
}
+
+ /* general case if not handled by cyclicu or cyclicv */
+ if (cu->actnu == i) {
+ for (b = 0, diffa = 0; b < newv; b++, diffa += nu->pntsu - newu) {
+ starta = b * nu->pntsu + a;
+ if (calc_duplicate_actvert(
+ editnurb, newnurb, cu,
+ starta, starta + newu,
+ cu->actvert - (a / nu->pntsu * nu->pntsu + diffa + (starta % nu->pntsu))))
+ {
+ break;
+ }
+ }
+ }
BLI_addtail(newnurb, newnu);
if (newu != nu->pntsu) newnu->flagu &= ~CU_NURB_CYCLIC;
@@ -2131,6 +2230,20 @@ static void adduplicateflagNurb(Object *obedit, ListBase *newnurb,
for (b = 0; b < newv; b++) {
memcpy(&newnu->bp[b * newu], &nu->bp[b * nu->pntsu], newu * sizeof(BPoint));
}
+
+ /* check for actvert in the unused cyclicuv selection */
+ if (cu->actnu == i) {
+ for (b = 0, diffa = 0; b < newv; b++, diffa += nu->pntsu - newu) {
+ starta = b * nu->pntsu;
+ if (calc_duplicate_actvert(
+ editnurb, newnurb, cu,
+ starta, starta + newu,
+ cu->actvert - (diffa + (starta % nu->pntsu))))
+ {
+ break;
+ }
+ }
+ }
BLI_addtail(newnurb, newnu);
if (newu != nu->pntsu) newnu->flagu &= ~CU_NURB_CYCLIC;
@@ -2144,12 +2257,9 @@ static void adduplicateflagNurb(Object *obedit, ListBase *newnurb,
}
}
}
- nu = nu->prev;
}
if (BLI_listbase_is_empty(newnurb) == false) {
- cu->actnu = cu->actvert = CU_ACT_NONE;
-
for (nu = newnurb->first; nu; nu = nu->next) {
if (nu->type == CU_BEZIER) {
if (split) {
@@ -4163,7 +4273,7 @@ void CURVE_OT_make_segment(wmOperatorType *ot)
/***************** pick select from 3d view **********************/
-bool mouse_nurb(bContext *C, const int mval[2], bool extend, bool deselect, bool toggle)
+bool ED_curve_editnurb_select_pick(bContext *C, const int mval[2], bool extend, bool deselect, bool toggle)
{
Object *obedit = CTX_data_edit_object(C);
Curve *cu = obedit->data;
@@ -4659,7 +4769,7 @@ static bool ed_editcurve_extrude(Curve *cu, EditNurb *editnurb)
/***************** add vertex operator **********************/
-static int ed_editcurve_addvert(Curve *cu, EditNurb *editnurb, const float location[3])
+static int ed_editcurve_addvert(Curve *cu, EditNurb *editnurb, const float location_init[3])
{
Nurb *nu;
@@ -4700,7 +4810,7 @@ static int ed_editcurve_addvert(Curve *cu, EditNurb *editnurb, const float locat
int i;
mid_v3_v3v3(center, minmax[0], minmax[1]);
- sub_v3_v3v3(ofs, location, center);
+ sub_v3_v3v3(ofs, location_init, center);
if ((cu->flag & CU_3D) == 0) {
ofs[2] = 0.0f;
@@ -4722,6 +4832,8 @@ static int ed_editcurve_addvert(Curve *cu, EditNurb *editnurb, const float locat
}
}
}
+
+ BKE_nurb_handles_calc(nu);
}
else {
BPoint *bp;
@@ -4736,6 +4848,14 @@ static int ed_editcurve_addvert(Curve *cu, EditNurb *editnurb, const float locat
changed = true;
}
else {
+ float location[3];
+
+ copy_v3_v3(location, location_init);
+
+ if ((cu->flag & CU_3D) == 0) {
+ location[2] = 0.0f;
+ }
+
/* nothing selected: create a new curve */
nu = BKE_curve_nurb_active_get(cu);
@@ -4753,6 +4873,10 @@ static int ed_editcurve_addvert(Curve *cu, EditNurb *editnurb, const float locat
nurb_new->orderu = 4;
nurb_new->flag |= CU_SMOOTH;
BKE_nurb_bezierPoints_add(nurb_new, 1);
+
+ if ((cu->flag & CU_3D) == 0) {
+ nurb_new->flag |= CU_2D;
+ }
}
BLI_addtail(&editnurb->nurbs, nurb_new);
@@ -4784,6 +4908,10 @@ static int ed_editcurve_addvert(Curve *cu, EditNurb *editnurb, const float locat
nurb_new->flag |= CU_SMOOTH;
nurb_new->orderu = 4;
BKE_nurb_points_add(nurb_new, 1);
+
+ if ((cu->flag & CU_3D) == 0) {
+ nurb_new->flag |= CU_2D;
+ }
}
BLI_addtail(&editnurb->nurbs, nurb_new);
@@ -4867,7 +4995,40 @@ static int add_vertex_invoke(bContext *C, wmOperator *op, const wmEvent *event)
const float mval[2] = {UNPACK2(event->mval)};
float no_dummy[3];
float dist_px_dummy;
- snapObjectsContext(C, mval, &dist_px_dummy, location, no_dummy, SNAP_NOT_OBEDIT);
+ snapObjectsContext(
+ C, mval, SNAP_NOT_OBEDIT,
+ location, no_dummy, &dist_px_dummy);
+ }
+
+ if ((cu->flag & CU_3D) == 0) {
+ const float eps = 1e-6f;
+
+ /* get the view vector to 'location' */
+ float view_dir[3];
+ ED_view3d_global_to_vector(vc.rv3d, location, view_dir);
+
+ /* get the plane */
+ float plane[4];
+ /* only normalize to avoid precision errors */
+ normalize_v3_v3(plane, vc.obedit->obmat[2]);
+ plane[3] = -dot_v3v3(plane, vc.obedit->obmat[3]);
+
+ if (fabsf(dot_v3v3(view_dir, plane)) < eps) {
+ /* can't project on an aligned plane. */
+ }
+ else {
+ float lambda;
+ if (isect_ray_plane_v3(location, view_dir, plane, &lambda, false)) {
+ /* check if we're behind the viewport */
+ float location_test[3];
+ madd_v3_v3v3fl(location_test, location, view_dir, lambda);
+ if ((vc.rv3d->is_persp == false) ||
+ (mul_project_m4_v3_zfac(vc.rv3d->persmat, location_test) > 0.0f))
+ {
+ copy_v3_v3(location, location_test);
+ }
+ }
+ }
}
RNA_float_set_array(op->ptr, "location", location);
@@ -4953,7 +5114,7 @@ void CURVE_OT_extrude(wmOperatorType *ot)
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
/* to give to transform */
- RNA_def_enum(ot->srna, "mode", transform_mode_types, TFM_TRANSLATION, "Mode", "");
+ RNA_def_enum(ot->srna, "mode", rna_enum_transform_mode_types, TFM_TRANSLATION, "Mode", "");
}
/***************** make cyclic operator **********************/
diff --git a/source/blender/editors/curve/editcurve_add.c b/source/blender/editors/curve/editcurve_add.c
index 5347ef05105..cc8e272d4f7 100644
--- a/source/blender/editors/curve/editcurve_add.c
+++ b/source/blender/editors/curve/editcurve_add.c
@@ -105,7 +105,7 @@ static const char *get_surf_defname(int type)
}
-Nurb *add_nurbs_primitive(bContext *C, Object *obedit, float mat[4][4], int type, int newob)
+Nurb *ED_curve_add_nurbs_primitive(bContext *C, Object *obedit, float mat[4][4], int type, int newob)
{
static int xzproj = 0; /* this function calls itself... */
ListBase *editnurb = object_editcurve_get(obedit);
@@ -121,7 +121,6 @@ Nurb *add_nurbs_primitive(bContext *C, Object *obedit, float mat[4][4], int type
const float grid = 1.0f;
const int cutype = (type & CU_TYPE); // poly, bezier, nurbs, etc
const int stype = (type & CU_PRIMITIVE);
- const bool force_3d = (((Curve *)obedit->data)->flag & CU_3D) != 0; /* could be adding to an existing 3D curve */
unit_m4(umat);
unit_m4(viewmat);
@@ -145,7 +144,6 @@ Nurb *add_nurbs_primitive(bContext *C, Object *obedit, float mat[4][4], int type
case CU_PRIM_CURVE: /* curve */
nu->resolu = cu->resolu;
if (cutype == CU_BEZIER) {
- if (!force_3d) nu->flag |= CU_2D;
nu->pntsu = 2;
nu->bezt = (BezTriple *)MEM_callocN(2 * sizeof(BezTriple), "addNurbprim1");
bezt = nu->bezt;
@@ -247,7 +245,6 @@ Nurb *add_nurbs_primitive(bContext *C, Object *obedit, float mat[4][4], int type
nu->resolu = cu->resolu;
if (cutype == CU_BEZIER) {
- if (!force_3d) nu->flag |= CU_2D;
nu->pntsu = 4;
nu->bezt = (BezTriple *)MEM_callocN(sizeof(BezTriple) * 4, "addNurbprim1");
nu->flagu = CU_NURB_CYCLIC;
@@ -346,7 +343,7 @@ Nurb *add_nurbs_primitive(bContext *C, Object *obedit, float mat[4][4], int type
break;
case CU_PRIM_TUBE: /* Cylinder */
if (cutype == CU_NURBS) {
- nu = add_nurbs_primitive(C, obedit, mat, CU_NURBS | CU_PRIM_CIRCLE, 0); /* circle */
+ nu = ED_curve_add_nurbs_primitive(C, obedit, mat, CU_NURBS | CU_PRIM_CIRCLE, 0); /* circle */
nu->resolu = cu->resolu;
nu->flag = CU_SMOOTH;
BLI_addtail(editnurb, nu); /* temporal for extrude and translate */
@@ -423,7 +420,7 @@ Nurb *add_nurbs_primitive(bContext *C, Object *obedit, float mat[4][4], int type
float tmp_vec[3] = {0.f, 0.f, 1.f};
xzproj = 1;
- nu = add_nurbs_primitive(C, obedit, mat, CU_NURBS | CU_PRIM_CIRCLE, 0); /* circle */
+ nu = ED_curve_add_nurbs_primitive(C, obedit, mat, CU_NURBS | CU_PRIM_CIRCLE, 0); /* circle */
xzproj = 0;
nu->resolu = cu->resolu;
nu->resolv = cu->resolv;
@@ -459,6 +456,10 @@ Nurb *add_nurbs_primitive(bContext *C, Object *obedit, float mat[4][4], int type
BLI_assert(nu != NULL);
if (nu) { /* should always be set */
+ if ((obedit->type != OB_SURF) && ((cu->flag & CU_3D) == 0)) {
+ nu->flag |= CU_2D;
+ }
+
nu->flag |= CU_SMOOTH;
cu->actnu = BLI_listbase_count(editnurb);
cu->actvert = CU_ACT_NONE;
@@ -523,7 +524,7 @@ static int curvesurf_prim_add(bContext *C, wmOperator *op, int type, int isSurf)
dia = RNA_float_get(op->ptr, "radius");
mul_mat3_m4_fl(mat, dia);
- nu = add_nurbs_primitive(C, obedit, mat, type, newob);
+ nu = ED_curve_add_nurbs_primitive(C, obedit, mat, type, newob);
editnurb = object_editcurve_get(obedit);
BLI_addtail(editnurb, nu);
diff --git a/source/blender/editors/curve/editcurve_paint.c b/source/blender/editors/curve/editcurve_paint.c
new file mode 100644
index 00000000000..5c74a3e43c2
--- /dev/null
+++ b/source/blender/editors/curve/editcurve_paint.c
@@ -0,0 +1,1180 @@
+/*
+ * ***** 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.
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+/** \file blender/editors/curve/editcurve_paint.c
+ * \ingroup edcurve
+ */
+
+#include "DNA_object_types.h"
+#include "DNA_scene_types.h"
+
+#include "MEM_guardedalloc.h"
+
+#include "BLI_blenlib.h"
+#include "BLI_math.h"
+#include "BLI_mempool.h"
+
+#include "BKE_context.h"
+#include "BKE_curve.h"
+#include "BKE_depsgraph.h"
+#include "BKE_fcurve.h"
+#include "BKE_report.h"
+
+#include "WM_api.h"
+#include "WM_types.h"
+
+#include "ED_space_api.h"
+#include "ED_screen.h"
+#include "ED_view3d.h"
+#include "ED_curve.h"
+
+#include "BIF_gl.h"
+#include "BIF_glutil.h"
+
+#include "curve_intern.h"
+
+#include "UI_resources.h"
+
+#include "RNA_access.h"
+#include "RNA_define.h"
+
+#define USE_SPLINE_FIT
+
+#ifdef USE_SPLINE_FIT
+#include "curve_fit_nd.h"
+#endif
+
+/* Distance between input samples */
+#define STROKE_SAMPLE_DIST_MIN_PX 3
+#define STROKE_SAMPLE_DIST_MAX_PX 6
+
+
+/* -------------------------------------------------------------------- */
+
+/** \name Depth Utilities
+ * \{ */
+
+
+static float depth_read_zbuf(const ViewContext *vc, int x, int y)
+{
+ ViewDepths *vd = vc->rv3d->depths;
+
+ if (vd && vd->depths && x > 0 && y > 0 && x < vd->w && y < vd->h)
+ return vd->depths[y * vd->w + x];
+ else
+ return -1.0f;
+}
+
+static bool depth_unproject(
+ const ARegion *ar, const bglMats *mats,
+ const int mval[2], const float depth,
+ float r_location_world[3])
+{
+ double p[3];
+ if (gluUnProject(
+ (double)ar->winrct.xmin + mval[0] + 0.5,
+ (double)ar->winrct.ymin + mval[1] + 0.5,
+ depth, mats->modelview, mats->projection, (const GLint *)mats->viewport,
+ &p[0], &p[1], &p[2]))
+ {
+ copy_v3fl_v3db(r_location_world, p);
+ return true;
+ }
+ return false;
+}
+
+static bool depth_read_normal(
+ const ViewContext *vc, const bglMats *mats, const int mval[2],
+ float r_normal[3])
+{
+ /* pixels surrounding */
+ bool depths_valid[9] = {false};
+ float coords[9][3] = {{0}};
+
+ ARegion *ar = vc->ar;
+ const ViewDepths *depths = vc->rv3d->depths;
+
+ for (int x = 0, i = 0; x < 2; x++) {
+ for (int y = 0; y < 2; y++) {
+ const int mval_ofs[2] = {mval[0] + (x - 1), mval[1] + (y - 1)};
+
+ float depth = depth_read_zbuf(vc, mval_ofs[0], mval_ofs[1]);
+ if ((depth > depths->depth_range[0]) && (depth < depths->depth_range[1])) {
+ if (depth_unproject(ar, mats, mval_ofs, depth, coords[i])) {
+ depths_valid[i] = true;
+ }
+ }
+ i++;
+ }
+ }
+
+ const int edges[2][6][2] = {
+ /* x edges */
+ {{0, 1}, {1, 2},
+ {3, 4}, {4, 5},
+ {6, 7}, {7, 8}},
+ /* y edges */
+ {{0, 3}, {3, 6},
+ {1, 4}, {4, 7},
+ {2, 5}, {5, 8}},
+ };
+
+ float cross[2][3] = {{0.0f}};
+
+ for (int i = 0; i < 6; i++) {
+ for (int axis = 0; axis < 2; axis++) {
+ if (depths_valid[edges[axis][i][0]] && depths_valid[edges[axis][i][1]]) {
+ float delta[3];
+ sub_v3_v3v3(delta, coords[edges[axis][i][0]], coords[edges[axis][i][1]]);
+ add_v3_v3(cross[axis], delta);
+ }
+ }
+ }
+
+ cross_v3_v3v3(r_normal, cross[0], cross[1]);
+
+ if (normalize_v3(r_normal) != 0.0f) {
+ return true;
+ }
+ else {
+ return false;
+ }
+}
+
+/** \} */
+
+
+/* -------------------------------------------------------------------- */
+
+/** \name StrokeElem / #RNA_OperatorStrokeElement Conversion Functions
+ * \{ */
+
+struct StrokeElem {
+ float mval[2];
+ float location_world[3];
+ float location_local[3];
+ float pressure;
+};
+
+struct CurveDrawData {
+ short init_event_type;
+ short curve_type;
+
+ /* projecting 2D into 3D space */
+ struct {
+ /* use a plane or project to the surface */
+ bool use_plane;
+ float plane[4];
+
+ /* use 'rv3d->depths', note that this will become 'damaged' while drawing, but thats OK. */
+ bool use_depth;
+
+ /* offset projection by this value */
+ bool use_offset;
+ float offset[3]; /* worldspace */
+ } project;
+
+ /* cursor sampling */
+ struct {
+ /* use substeps, needed for nicely interpolating depth */
+ bool use_substeps;
+ } sample;
+
+ struct {
+ float min, max, range;
+ float offset;
+ } radius;
+
+ struct {
+ float mouse[2];
+ /* used incase we can't calculate the depth */
+ float location_world[3];
+
+ float location_world_valid[3];
+
+ const struct StrokeElem *selem;
+ } prev;
+
+ ViewContext vc;
+ bglMats mats;
+ enum {
+ CURVE_DRAW_IDLE = 0,
+ CURVE_DRAW_PAINTING = 1,
+ } state;
+
+ /* StrokeElem */
+ BLI_mempool *stroke_elem_pool;
+
+ void *draw_handle_view;
+};
+
+static float stroke_elem_radius(const struct CurveDrawData *cdd, const struct StrokeElem *selem)
+{
+ const Curve *cu = cdd->vc.obedit->data;
+ return ((selem->pressure * cdd->radius.range) + cdd->radius.min) * cu->ext2;
+}
+
+static void stroke_elem_interp(
+ struct StrokeElem *selem_out,
+ const struct StrokeElem *selem_a, const struct StrokeElem *selem_b, float t)
+{
+ interp_v2_v2v2(selem_out->mval, selem_a->mval, selem_b->mval, t);
+ interp_v3_v3v3(selem_out->location_world, selem_a->location_world, selem_b->location_world, t);
+ interp_v3_v3v3(selem_out->location_local, selem_a->location_local, selem_b->location_local, t);
+ selem_out->pressure = interpf(selem_a->pressure, selem_b->pressure, t);
+}
+
+
+/**
+ * Sets the depth from #StrokeElem.mval
+ */
+static bool stroke_elem_project(
+ const struct CurveDrawData *cdd,
+ const int mval_i[2], const float mval_fl[2],
+ const float radius_offset, const float radius,
+ float r_location_world[3])
+{
+ View3D *v3d = cdd->vc.v3d;
+ ARegion *ar = cdd->vc.ar;
+ RegionView3D *rv3d = cdd->vc.rv3d;
+
+ bool is_location_world_set = false;
+
+ /* project to 'location_world' */
+ if (cdd->project.use_plane) {
+ /* get the view vector to 'location' */
+ float ray_origin[3], ray_direction[3];
+ ED_view3d_win_to_ray(cdd->vc.ar, v3d, mval_fl, ray_origin, ray_direction, false);
+
+ float lambda;
+ if (isect_ray_plane_v3(ray_origin, ray_direction, cdd->project.plane, &lambda, true)) {
+ madd_v3_v3v3fl(r_location_world, ray_origin, ray_direction, lambda);
+ is_location_world_set = true;
+ }
+ }
+ else {
+ const ViewDepths *depths = rv3d->depths;
+ if (depths &&
+ ((unsigned int)mval_i[0] < depths->w) &&
+ ((unsigned int)mval_i[1] < depths->h))
+ {
+ float depth = depth_read_zbuf(&cdd->vc, mval_i[0], mval_i[1]);
+ if ((depth > depths->depth_range[0]) && (depth < depths->depth_range[1])) {
+ if (depth_unproject(ar, &cdd->mats, mval_i, depth, r_location_world)) {
+ is_location_world_set = true;
+
+ if (radius_offset != 0.0f) {
+ float normal[3];
+ if (depth_read_normal(&cdd->vc, &cdd->mats, mval_i, normal)) {
+ madd_v3_v3fl(r_location_world, normal, radius_offset * radius);
+ }
+ }
+ }
+ }
+ }
+ }
+
+ if (is_location_world_set) {
+ if (cdd->project.use_offset) {
+ add_v3_v3(r_location_world, cdd->project.offset);
+ }
+ }
+
+ return is_location_world_set;
+}
+
+static bool stroke_elem_project_fallback(
+ const struct CurveDrawData *cdd,
+ const int mval_i[2], const float mval_fl[2],
+ const float radius_offset, const float radius,
+ const float location_fallback_depth[3],
+ float r_location_world[3], float r_location_local[3])
+{
+ bool is_depth_found = stroke_elem_project(
+ cdd, mval_i, mval_fl,
+ radius_offset, radius,
+ r_location_world);
+ if (is_depth_found == false) {
+ ED_view3d_win_to_3d(cdd->vc.ar, location_fallback_depth, mval_fl, r_location_world);
+ }
+ mul_v3_m4v3(r_location_local, cdd->vc.obedit->imat, r_location_world);
+
+ return is_depth_found;
+}
+
+/**
+ * \note #StrokeElem.mval & #StrokeElem.pressure must be set first.
+ */
+static bool stroke_elem_project_fallback_elem(
+ const struct CurveDrawData *cdd,
+ const float location_fallback_depth[3],
+ struct StrokeElem *selem)
+{
+ const int mval_i[2] = {UNPACK2(selem->mval)};
+ const float radius = stroke_elem_radius(cdd, selem);
+ return stroke_elem_project_fallback(
+ cdd, mval_i, selem->mval,
+ cdd->radius.offset, radius,
+ location_fallback_depth,
+ selem->location_world, selem->location_local);
+}
+
+/** \} */
+
+
+/* -------------------------------------------------------------------- */
+
+/** \name Operator/Stroke Conversion
+ * \{ */
+
+static void curve_draw_stroke_to_operator_elem(
+ wmOperator *op, const struct StrokeElem *selem)
+{
+ PointerRNA itemptr;
+ RNA_collection_add(op->ptr, "stroke", &itemptr);
+
+ RNA_float_set_array(&itemptr, "mouse", selem->mval);
+ RNA_float_set_array(&itemptr, "location", selem->location_world);
+ RNA_float_set(&itemptr, "pressure", selem->pressure);
+}
+
+static void curve_draw_stroke_from_operator_elem(
+ wmOperator *op, PointerRNA *itemptr)
+{
+ struct CurveDrawData *cdd = op->customdata;
+
+ struct StrokeElem *selem = BLI_mempool_calloc(cdd->stroke_elem_pool);
+
+ RNA_float_get_array(itemptr, "mouse", selem->mval);
+ RNA_float_get_array(itemptr, "location", selem->location_world);
+ mul_v3_m4v3(selem->location_local, cdd->vc.obedit->imat, selem->location_world);
+ selem->pressure = RNA_float_get(itemptr, "pressure");
+}
+
+static void curve_draw_stroke_to_operator(wmOperator *op)
+{
+ struct CurveDrawData *cdd = op->customdata;
+
+ BLI_mempool_iter iter;
+ const struct StrokeElem *selem;
+
+ BLI_mempool_iternew(cdd->stroke_elem_pool, &iter);
+ for (selem = BLI_mempool_iterstep(&iter); selem; selem = BLI_mempool_iterstep(&iter)) {
+ curve_draw_stroke_to_operator_elem(op, selem);
+ }
+}
+
+static void curve_draw_stroke_from_operator(wmOperator *op)
+{
+ RNA_BEGIN (op->ptr, itemptr, "stroke")
+ {
+ curve_draw_stroke_from_operator_elem(op, &itemptr);
+ }
+ RNA_END;
+}
+
+/** \} */
+
+
+/* -------------------------------------------------------------------- */
+
+/** \name Operator Callbacks & Helpers
+ * \{ */
+
+static void curve_draw_stroke_3d(const struct bContext *UNUSED(C), ARegion *UNUSED(ar), void *arg)
+{
+ wmOperator *op = arg;
+ struct CurveDrawData *cdd = op->customdata;
+
+ const int stroke_len = BLI_mempool_count(cdd->stroke_elem_pool);
+
+ if (stroke_len == 0) {
+ return;
+ }
+
+ View3D *v3d = cdd->vc.v3d;
+ Object *obedit = cdd->vc.obedit;
+ Curve *cu = obedit->data;
+
+ UI_ThemeColor(TH_WIRE);
+
+ if (cu->ext2 > 0.0f) {
+ GLUquadricObj *qobj = gluNewQuadric();
+
+ gluQuadricDrawStyle(qobj, GLU_FILL);
+
+ BLI_mempool_iter iter;
+ const struct StrokeElem *selem;
+
+ const float location_zero[3] = {0};
+ const float *location_prev = location_zero;
+
+ /* scale to edit-mode space */
+ glPushMatrix();
+ glMultMatrixf(obedit->obmat);
+
+ BLI_mempool_iternew(cdd->stroke_elem_pool, &iter);
+ for (selem = BLI_mempool_iterstep(&iter); selem; selem = BLI_mempool_iterstep(&iter)) {
+ glTranslatef(
+ selem->location_local[0] - location_prev[0],
+ selem->location_local[1] - location_prev[1],
+ selem->location_local[2] - location_prev[2]);
+ location_prev = selem->location_local;
+ const float radius = stroke_elem_radius(cdd, selem);
+ gluSphere(qobj, radius , 12, 8);
+
+ location_prev = selem->location_local;
+ }
+
+ glPopMatrix();
+
+ gluDeleteQuadric(qobj);
+ }
+
+ if (stroke_len > 1) {
+ float (*coord_array)[3] = MEM_mallocN(sizeof(*coord_array) * stroke_len, __func__);
+
+ {
+ BLI_mempool_iter iter;
+ const struct StrokeElem *selem;
+ int i;
+ BLI_mempool_iternew(cdd->stroke_elem_pool, &iter);
+ for (selem = BLI_mempool_iterstep(&iter), i = 0; selem; selem = BLI_mempool_iterstep(&iter), i++) {
+ copy_v3_v3(coord_array[i], selem->location_world);
+ }
+ }
+
+ {
+ glEnable(GL_BLEND);
+ glEnable(GL_LINE_SMOOTH);
+
+ glEnableClientState(GL_VERTEX_ARRAY);
+ glVertexPointer(3, GL_FLOAT, 0, coord_array);
+
+ cpack(0x0);
+ glLineWidth(3.0f);
+ glDrawArrays(GL_LINE_STRIP, 0, stroke_len);
+
+ if (v3d->zbuf)
+ glDisable(GL_DEPTH_TEST);
+
+ cpack(0xffffffff);
+ glLineWidth(1.0f);
+ glDrawArrays(GL_LINE_STRIP, 0, stroke_len);
+
+ if (v3d->zbuf)
+ glEnable(GL_DEPTH_TEST);
+
+ glDisableClientState(GL_VERTEX_ARRAY);
+
+ glDisable(GL_BLEND);
+ glDisable(GL_LINE_SMOOTH);
+ }
+
+ MEM_freeN(coord_array);
+ }
+}
+
+static void curve_draw_event_add(wmOperator *op, const wmEvent *event)
+{
+ struct CurveDrawData *cdd = op->customdata;
+ Object *obedit = cdd->vc.obedit;
+
+ invert_m4_m4(obedit->imat, obedit->obmat);
+
+ struct StrokeElem *selem = BLI_mempool_calloc(cdd->stroke_elem_pool);
+
+ ARRAY_SET_ITEMS(selem->mval, event->mval[0], event->mval[1]);
+
+ /* handle pressure sensitivity (which is supplied by tablets) */
+ if (event->tablet_data) {
+ const wmTabletData *wmtab = event->tablet_data;
+ selem->pressure = wmtab->Pressure;
+ }
+ else {
+ selem->pressure = 1.0f;
+ }
+
+ bool is_depth_found = stroke_elem_project_fallback_elem(
+ cdd, cdd->prev.location_world_valid, selem);
+
+ if (is_depth_found) {
+ /* use the depth if a fallback wasn't used */
+ copy_v3_v3(cdd->prev.location_world_valid, selem->location_world);
+ }
+ copy_v3_v3(cdd->prev.location_world, selem->location_world);
+
+ float len_sq = len_squared_v2v2(cdd->prev.mouse, selem->mval);
+ copy_v2_v2(cdd->prev.mouse, selem->mval);
+
+ if (cdd->sample.use_substeps && cdd->prev.selem) {
+ const struct StrokeElem selem_target = *selem;
+ struct StrokeElem *selem_new_last = selem;
+ if (len_sq >= SQUARE(STROKE_SAMPLE_DIST_MAX_PX)) {
+ int n = (int)ceil(sqrt((double)len_sq)) / STROKE_SAMPLE_DIST_MAX_PX ;
+
+ for (int i = 1; i < n; i++) {
+ struct StrokeElem *selem_new = selem_new_last;
+ stroke_elem_interp(selem_new, cdd->prev.selem, &selem_target, (float)i / n);
+
+ const bool is_depth_found_substep = stroke_elem_project_fallback_elem(
+ cdd, cdd->prev.location_world_valid, selem_new);
+ if (is_depth_found == false) {
+ if (is_depth_found_substep) {
+ copy_v3_v3(cdd->prev.location_world_valid, selem_new->location_world);
+ }
+ }
+
+ selem_new_last = BLI_mempool_calloc(cdd->stroke_elem_pool);
+ }
+ }
+ selem = selem_new_last;
+ *selem_new_last = selem_target;
+ }
+
+ cdd->prev.selem = selem;
+
+ ED_region_tag_redraw(cdd->vc.ar);
+}
+
+static void curve_draw_event_add_first(wmOperator *op, const wmEvent *event)
+{
+ struct CurveDrawData *cdd = op->customdata;
+ const CurvePaintSettings *cps = &cdd->vc.scene->toolsettings->curve_paint_settings;
+
+ /* add first point */
+ curve_draw_event_add(op, event);
+
+ if ((cps->depth_mode == CURVE_PAINT_PROJECT_SURFACE) && cdd->project.use_depth &&
+ (cps->flag & CURVE_PAINT_FLAG_DEPTH_STROKE_ENDPOINTS))
+ {
+ RegionView3D *rv3d = cdd->vc.rv3d;
+
+ cdd->project.use_depth = false;
+ cdd->project.use_plane = true;
+
+ float normal[3] = {0.0f};
+ if (ELEM(cps->surface_plane,
+ CURVE_PAINT_SURFACE_PLANE_NORMAL_VIEW,
+ CURVE_PAINT_SURFACE_PLANE_NORMAL_SURFACE))
+ {
+ if (depth_read_normal(&cdd->vc, &cdd->mats, event->mval, normal)) {
+ if (cps->surface_plane == CURVE_PAINT_SURFACE_PLANE_NORMAL_VIEW) {
+ float cross_a[3], cross_b[3];
+ cross_v3_v3v3(cross_a, rv3d->viewinv[2], normal);
+ cross_v3_v3v3(cross_b, normal, cross_a);
+ copy_v3_v3(normal, cross_b);
+ }
+ }
+ }
+
+ /* CURVE_PAINT_SURFACE_PLANE_VIEW or fallback */
+ if (is_zero_v3(normal)) {
+ copy_v3_v3(normal, rv3d->viewinv[2]);
+ }
+
+ normalize_v3_v3(cdd->project.plane, normal);
+ cdd->project.plane[3] = -dot_v3v3(cdd->project.plane, cdd->prev.location_world_valid);
+
+ /* Special case for when we only have offset applied on the first-hit,
+ * the remaining stroke must be offset too. */
+ if (cdd->radius.offset != 0.0f) {
+ const float mval_fl[2] = {UNPACK2(event->mval)};
+
+ float location_no_offset[3];
+
+ if (stroke_elem_project(
+ cdd, event->mval, mval_fl, 0.0f, 0.0f,
+ location_no_offset))
+ {
+ sub_v3_v3v3(cdd->project.offset, cdd->prev.location_world_valid, location_no_offset);
+ if (!is_zero_v3(cdd->project.offset)) {
+ cdd->project.use_offset = true;
+ }
+ }
+ }
+ /* end special case */
+
+ }
+
+ cdd->init_event_type = event->type;
+ cdd->state = CURVE_DRAW_PAINTING;
+}
+
+static bool curve_draw_init(bContext *C, wmOperator *op, bool is_invoke)
+{
+ BLI_assert(op->customdata == NULL);
+
+ struct CurveDrawData *cdd = MEM_callocN(sizeof(*cdd), __func__);
+
+ if (is_invoke) {
+ view3d_set_viewcontext(C, &cdd->vc);
+ if (ELEM(NULL, cdd->vc.ar, cdd->vc.rv3d, cdd->vc.v3d, cdd->vc.win, cdd->vc.scene)) {
+ MEM_freeN(cdd);
+ BKE_report(op->reports, RPT_ERROR, "Unable to access 3D viewport.");
+ return false;
+ }
+ }
+ else {
+ cdd->vc.scene = CTX_data_scene(C);
+ cdd->vc.obedit = CTX_data_edit_object(C);
+ }
+
+ op->customdata = cdd;
+
+ const CurvePaintSettings *cps = &cdd->vc.scene->toolsettings->curve_paint_settings;
+
+ cdd->curve_type = cps->curve_type;
+
+ cdd->radius.min = cps->radius_min;
+ cdd->radius.max = cps->radius_max;
+ cdd->radius.range = cps->radius_max - cps->radius_min;
+ cdd->radius.offset = cps->radius_offset;
+
+ cdd->stroke_elem_pool = BLI_mempool_create(
+ sizeof(struct StrokeElem), 0, 512, BLI_MEMPOOL_ALLOW_ITER);
+
+ return true;
+}
+
+
+static void curve_draw_exit(wmOperator *op)
+{
+ struct CurveDrawData *cdd = op->customdata;
+ if (cdd) {
+ if (cdd->draw_handle_view) {
+ ED_region_draw_cb_exit(cdd->vc.ar->type, cdd->draw_handle_view);
+ WM_cursor_modal_restore(cdd->vc.win);
+ }
+
+ if (cdd->stroke_elem_pool) {
+ BLI_mempool_destroy(cdd->stroke_elem_pool);
+ }
+
+ MEM_freeN(cdd);
+ op->customdata = NULL;
+ }
+}
+
+/**
+ * Initialize values before calling 'exec' (when running interactively).
+ */
+static void curve_draw_exec_precalc(wmOperator *op)
+{
+ struct CurveDrawData *cdd = op->customdata;
+ const CurvePaintSettings *cps = &cdd->vc.scene->toolsettings->curve_paint_settings;
+ PropertyRNA *prop;
+
+ prop = RNA_struct_find_property(op->ptr, "corner_angle");
+ if (!RNA_property_is_set(op->ptr, prop)) {
+ const float corner_angle = (cps->flag & CURVE_PAINT_FLAG_CORNERS_DETECT) ? cps->corner_angle : M_PI;
+ RNA_property_float_set(op->ptr, prop, corner_angle);
+ }
+
+ prop = RNA_struct_find_property(op->ptr, "error_threshold");
+ if (!RNA_property_is_set(op->ptr, prop)) {
+
+ /* error isnt set so we'll have to calculate it from the pixel values */
+ BLI_mempool_iter iter;
+ const struct StrokeElem *selem, *selem_prev;
+
+ float len_3d = 0.0f, len_2d = 0.0f;
+ float scale_px; /* pixel to local space scale */
+
+ int i = 0;
+ BLI_mempool_iternew(cdd->stroke_elem_pool, &iter);
+ selem_prev = BLI_mempool_iterstep(&iter);
+ for (selem = BLI_mempool_iterstep(&iter); selem; selem = BLI_mempool_iterstep(&iter), i++) {
+ len_3d += len_v3v3(selem->location_local, selem_prev->location_local);
+ len_2d += len_v2v2(selem->mval, selem_prev->mval);
+ selem_prev = selem;
+ }
+ scale_px = ((len_3d > 0.0f) && (len_2d > 0.0f)) ? (len_3d / len_2d) : 0.0f;
+ float error_threshold = (cps->error_threshold * U.pixelsize) * scale_px;
+ RNA_property_float_set(op->ptr, prop, error_threshold);
+ }
+
+ if ((cps->radius_taper_start != 0.0f) ||
+ (cps->radius_taper_end != 0.0f))
+ {
+ /* note, we could try to de-duplicate the length calculations above */
+ const int stroke_len = BLI_mempool_count(cdd->stroke_elem_pool);
+
+ BLI_mempool_iter iter;
+ struct StrokeElem *selem, *selem_prev;
+
+ float *lengths = MEM_mallocN(sizeof(float) * stroke_len, __func__);
+ struct StrokeElem **selem_array = MEM_mallocN(sizeof(*selem_array) * stroke_len, __func__);
+ lengths[0] = 0.0f;
+
+ float len_3d = 0.0f;
+
+ int i = 1;
+ BLI_mempool_iternew(cdd->stroke_elem_pool, &iter);
+ selem_prev = BLI_mempool_iterstep(&iter);
+ selem_array[0] = selem_prev;
+ for (selem = BLI_mempool_iterstep(&iter); selem; selem = BLI_mempool_iterstep(&iter), i++) {
+ const float len_3d_segment = len_v3v3(selem->location_local, selem_prev->location_local);
+ len_3d += len_3d_segment;
+ lengths[i] = len_3d;
+ selem_array[i] = selem;
+ selem_prev = selem;
+ }
+
+ if (cps->radius_taper_start != 0.0) {
+ selem_array[0]->pressure = 0.0f;
+ const float len_taper_max = cps->radius_taper_start * len_3d;
+ for (i = 1; i < stroke_len && lengths[i] < len_taper_max; i++) {
+ selem_array[i]->pressure *= lengths[i] / len_taper_max;
+ }
+ }
+
+ if (cps->radius_taper_end != 0.0) {
+ selem_array[stroke_len - 1]->pressure = 0.0f;
+ const float len_taper_max = cps->radius_taper_end * len_3d;
+ const float len_taper_min = len_3d - len_taper_max;
+ for (i = stroke_len - 2; i > 0 && lengths[i] > len_taper_min; i--) {
+ selem_array[i]->pressure *= (len_3d - lengths[i]) / len_taper_max;
+ }
+ }
+
+ MEM_freeN(lengths);
+ MEM_freeN(selem_array);
+ }
+}
+
+static int curve_draw_exec(bContext *C, wmOperator *op)
+{
+ if (op->customdata == NULL) {
+ if (!curve_draw_init(C, op, false)) {
+ return OPERATOR_CANCELLED;
+ }
+ }
+
+ struct CurveDrawData *cdd = op->customdata;
+
+ const CurvePaintSettings *cps = &cdd->vc.scene->toolsettings->curve_paint_settings;
+ Object *obedit = cdd->vc.scene->obedit;
+ Curve *cu = obedit->data;
+ ListBase *nurblist = object_editcurve_get(obedit);
+
+ int stroke_len = BLI_mempool_count(cdd->stroke_elem_pool);
+
+ const bool is_3d = (cu->flag & CU_3D) != 0;
+ invert_m4_m4(obedit->imat, obedit->obmat);
+
+ if (BLI_mempool_count(cdd->stroke_elem_pool) == 0) {
+ curve_draw_stroke_from_operator(op);
+ stroke_len = BLI_mempool_count(cdd->stroke_elem_pool);
+ }
+
+ ED_curve_deselect_all(cu->editnurb);
+
+ const double radius_min = cps->radius_min;
+ const double radius_max = cps->radius_max;
+ const double radius_range = cps->radius_max - cps->radius_min;
+
+ Nurb *nu = MEM_callocN(sizeof(Nurb), __func__);
+ nu->pntsv = 1;
+ nu->resolu = cu->resolu;
+ nu->resolv = cu->resolv;
+ nu->flag |= CU_SMOOTH;
+
+ const bool use_pressure_radius =
+ (cps->flag & CURVE_PAINT_FLAG_PRESSURE_RADIUS) ||
+ ((cps->radius_taper_start != 0.0f) ||
+ (cps->radius_taper_end != 0.0f));
+
+ if (cdd->curve_type == CU_BEZIER) {
+ nu->type = CU_BEZIER;
+
+#ifdef USE_SPLINE_FIT
+
+ /* Allow to interpolate multiple channels */
+ int dims = 3;
+ struct {
+ int radius;
+ } coords_indices;
+ coords_indices.radius = use_pressure_radius ? dims++ : -1;
+
+ float *coords = MEM_mallocN(sizeof(*coords) * stroke_len * dims, __func__);
+
+ float *cubic_spline = NULL;
+ unsigned int cubic_spline_len = 0;
+
+ /* error in object local space */
+ const float error_threshold = RNA_float_get(op->ptr, "error_threshold");
+ const float corner_angle = RNA_float_get(op->ptr, "corner_angle");
+
+ {
+ BLI_mempool_iter iter;
+ const struct StrokeElem *selem;
+ float *co = coords;
+
+ BLI_mempool_iternew(cdd->stroke_elem_pool, &iter);
+ for (selem = BLI_mempool_iterstep(&iter); selem; selem = BLI_mempool_iterstep(&iter), co += dims) {
+ copy_v3_v3(co, selem->location_local);
+ if (coords_indices.radius != -1) {
+ co[coords_indices.radius] = selem->pressure;
+ }
+ }
+ }
+
+ unsigned int *corners = NULL;
+ unsigned int corners_len = 0;
+
+ if (corner_angle < M_PI) {
+ /* this could be configurable... */
+ const float corner_radius_min = error_threshold / 8;
+ const float corner_radius_max = error_threshold * 2;
+ const unsigned int samples_max = 16;
+
+ curve_fit_corners_detect_fl(
+ (const float *)coords, stroke_len, dims,
+ corner_radius_min, corner_radius_max,
+ samples_max, corner_angle,
+ &corners, &corners_len);
+ }
+
+ unsigned int *corners_index = NULL;
+ unsigned int corners_index_len = 0;
+
+ const int result = curve_fit_cubic_to_points_fl(
+ coords, stroke_len, dims, error_threshold,
+ corners, corners_len,
+ &cubic_spline, &cubic_spline_len,
+ NULL,
+ &corners_index, &corners_index_len);
+
+ MEM_freeN(coords);
+ if (corners) {
+ free(corners);
+ }
+
+ if (result == 0) {
+ nu->pntsu = cubic_spline_len;
+ nu->bezt = MEM_callocN(sizeof(BezTriple) * nu->pntsu, __func__);
+
+ float *co = cubic_spline;
+ BezTriple *bezt = nu->bezt;
+ for (int j = 0; j < cubic_spline_len; j++, bezt++, co += (dims * 3)) {
+ const float *handle_l = co + (dims * 0);
+ const float *pt = co + (dims * 1);
+ const float *handle_r = co + (dims * 2);
+
+ copy_v3_v3(bezt->vec[0], handle_l);
+ copy_v3_v3(bezt->vec[1], pt);
+ copy_v3_v3(bezt->vec[2], handle_r);
+
+ if (coords_indices.radius != -1) {
+ bezt->radius = (pt[coords_indices.radius] * cdd->radius.range) + cdd->radius.min;
+ }
+ else {
+ bezt->radius = radius_max;
+ }
+
+ bezt->h1 = bezt->h2 = HD_ALIGN; /* will set to free in second pass */
+ bezt->f1 = bezt->f2 = bezt->f3 = SELECT;
+ }
+
+ if (corners_index) {
+ /* ignore the first and last */
+ for (unsigned int i = 1; i < corners_index_len - 1; i++) {
+ bezt = &nu->bezt[corners_index[i]];
+ bezt->h1 = bezt->h2 = HD_FREE;
+ }
+ }
+ }
+
+ if (corners_index) {
+ free(corners_index);
+ }
+
+ if (cubic_spline) {
+ free(cubic_spline);
+ }
+
+#else
+ nu->pntsu = stroke_len;
+ nu->bezt = MEM_callocN(nu->pntsu * sizeof(BezTriple), __func__);
+
+ BezTriple *bezt = nu->bezt;
+
+ {
+ BLI_mempool_iter iter;
+ const struct StrokeElem *selem;
+
+ BLI_mempool_iternew(cdd->stroke_elem_pool, &iter);
+ for (selem = BLI_mempool_iterstep(&iter); selem; selem = BLI_mempool_iterstep(&iter)) {
+ copy_v3_v3(bezt->vec[1], selem->location_local);
+ if (!is_3d) {
+ bezt->vec[1][2] = 0.0f;
+ }
+
+ if (use_pressure_radius) {
+ bezt->radius = selem->pressure;
+ }
+ else {
+ bezt->radius = radius_max;
+ }
+
+ bezt->h1 = bezt->h2 = HD_AUTO;
+
+ bezt->f1 |= SELECT;
+ bezt->f2 |= SELECT;
+ bezt->f3 |= SELECT;
+
+ bezt++;
+ }
+ }
+#endif
+
+ BKE_nurb_handles_calc(nu);
+ }
+ else { /* CU_POLY */
+ BLI_mempool_iter iter;
+ const struct StrokeElem *selem;
+
+ nu->pntsu = stroke_len;
+ nu->type = CU_POLY;
+ nu->bp = MEM_callocN(nu->pntsu * sizeof(BPoint), __func__);
+
+ BPoint *bp = nu->bp;
+
+ BLI_mempool_iternew(cdd->stroke_elem_pool, &iter);
+ for (selem = BLI_mempool_iterstep(&iter); selem; selem = BLI_mempool_iterstep(&iter)) {
+ copy_v3_v3(bp->vec, selem->location_local);
+ if (!is_3d) {
+ bp->vec[2] = 0.0f;
+ }
+
+ if (use_pressure_radius) {
+ bp->radius = (selem->pressure * radius_range) + radius_min;
+ }
+ else {
+ bp->radius = cps->radius_max;
+ }
+ bp->f1 = SELECT;
+ bp->vec[3] = 1.0f;
+
+ bp++;
+ }
+
+ BKE_nurb_knot_calc_u(nu);
+ }
+
+ BLI_addtail(nurblist, nu);
+
+ BKE_curve_nurb_active_set(cu, nu);
+ cu->actvert = nu->pntsu - 1;
+
+ WM_event_add_notifier(C, NC_GEOM | ND_DATA, obedit->data);
+ DAG_id_tag_update(obedit->data, 0);
+
+ curve_draw_exit(op);
+
+ return OPERATOR_FINISHED;
+}
+
+static int curve_draw_invoke(bContext *C, wmOperator *op, const wmEvent *event)
+{
+ if (RNA_struct_property_is_set(op->ptr, "stroke")) {
+ return curve_draw_exec(C, op);
+ }
+
+ if (!curve_draw_init(C, op, true)) {
+ return OPERATOR_CANCELLED;
+ }
+
+ struct CurveDrawData *cdd = op->customdata;
+
+ const CurvePaintSettings *cps = &cdd->vc.scene->toolsettings->curve_paint_settings;
+
+ const bool is_modal = RNA_boolean_get(op->ptr, "wait_for_input");
+
+ /* fallback (incase we can't find the depth on first test) */
+ {
+ const float mval_fl[2] = {UNPACK2(event->mval)};
+ float center[3];
+ negate_v3_v3(center, cdd->vc.rv3d->ofs);
+ ED_view3d_win_to_3d(cdd->vc.ar, center, mval_fl, cdd->prev.location_world);
+ copy_v3_v3(cdd->prev.location_world_valid, cdd->prev.location_world);
+ }
+
+ cdd->draw_handle_view = ED_region_draw_cb_activate(
+ cdd->vc.ar->type, curve_draw_stroke_3d, op, REGION_DRAW_POST_VIEW);
+ WM_cursor_modal_set(cdd->vc.win, BC_PAINTBRUSHCURSOR);
+
+ {
+ View3D *v3d = cdd->vc.v3d;
+ RegionView3D *rv3d = cdd->vc.rv3d;
+ Object *obedit = cdd->vc.obedit;
+ Curve *cu = obedit->data;
+
+ const float *plane_no = NULL;
+ const float *plane_co = NULL;
+
+ if ((cu->flag & CU_3D) == 0) {
+ /* 2D overrides other options */
+ plane_co = obedit->obmat[3];
+ plane_no = obedit->obmat[2];
+ cdd->project.use_plane = true;
+ }
+ else {
+ if ((cps->depth_mode == CURVE_PAINT_PROJECT_SURFACE) &&
+ (v3d->drawtype > OB_WIRE))
+ {
+ view3d_get_transformation(cdd->vc.ar, cdd->vc.rv3d, NULL, &cdd->mats);
+
+ /* needed or else the draw matrix can be incorrect */
+ view3d_operator_needs_opengl(C);
+
+ ED_view3d_autodist_init(cdd->vc.scene, cdd->vc.ar, cdd->vc.v3d, 0);
+
+ if (cdd->vc.rv3d->depths) {
+ cdd->vc.rv3d->depths->damaged = true;
+ }
+
+ ED_view3d_depth_update(cdd->vc.ar);
+
+ if (cdd->vc.rv3d->depths != NULL) {
+ cdd->project.use_depth = true;
+ }
+ else {
+ BKE_report(op->reports, RPT_WARNING, "Unable to access depth buffer, using view plane.");
+ cdd->project.use_depth = false;
+ }
+ }
+
+ /* use view plane (when set or as fallback when surface can't be found) */
+ if (cdd->project.use_depth == false) {
+ plane_co = ED_view3d_cursor3d_get(cdd->vc.scene, v3d);;
+ plane_no = rv3d->viewinv[2];
+ cdd->project.use_plane = true;
+ }
+
+ if (cdd->project.use_depth && (cdd->curve_type != CU_POLY)) {
+ cdd->sample.use_substeps = true;
+ }
+ }
+
+ if (cdd->project.use_plane) {
+ normalize_v3_v3(cdd->project.plane, plane_no);
+ cdd->project.plane[3] = -dot_v3v3(cdd->project.plane, plane_co);
+ }
+ }
+
+ if (is_modal == false) {
+ curve_draw_event_add_first(op, event);
+ }
+
+ /* add temp handler */
+ WM_event_add_modal_handler(C, op);
+
+ return OPERATOR_RUNNING_MODAL;
+}
+
+static void curve_draw_cancel(bContext *UNUSED(C), wmOperator *op)
+{
+ curve_draw_exit(op);
+}
+
+
+/* Modal event handling of frame changing */
+static int curve_draw_modal(bContext *C, wmOperator *op, const wmEvent *event)
+{
+ int ret = OPERATOR_RUNNING_MODAL;
+ struct CurveDrawData *cdd = op->customdata;
+
+ UNUSED_VARS(C, op);
+
+ if (event->type == cdd->init_event_type) {
+ if (event->val == KM_RELEASE) {
+ ED_region_tag_redraw(cdd->vc.ar);
+
+ curve_draw_exec_precalc(op);
+
+ curve_draw_stroke_to_operator(op);
+
+ curve_draw_exec(C, op);
+
+ return OPERATOR_FINISHED;
+ }
+ }
+ else if (ELEM(event->type, ESCKEY, RIGHTMOUSE)) {
+ ED_region_tag_redraw(cdd->vc.ar);
+ curve_draw_cancel(C, op);
+ return OPERATOR_CANCELLED;
+ }
+ else if (ELEM(event->type, LEFTMOUSE)) {
+ if (event->val == KM_PRESS) {
+ curve_draw_event_add_first(op, event);
+ }
+ }
+ else if (ELEM(event->type, MOUSEMOVE, INBETWEEN_MOUSEMOVE)) {
+ if (cdd->state == CURVE_DRAW_PAINTING) {
+ const float mval_fl[2] = {UNPACK2(event->mval)};
+ if (len_squared_v2v2(mval_fl, cdd->prev.location_world) > SQUARE(STROKE_SAMPLE_DIST_MIN_PX)) {
+ curve_draw_event_add(op, event);
+ }
+ }
+ }
+
+ return ret;
+}
+
+void CURVE_OT_draw(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name = "Draw Curve";
+ ot->idname = "CURVE_OT_draw";
+ ot->description = "Draw a freehand spline";
+
+ /* api callbacks */
+ ot->exec = curve_draw_exec;
+ ot->invoke = curve_draw_invoke;
+ ot->cancel = curve_draw_cancel;
+ ot->modal = curve_draw_modal;
+ ot->poll = ED_operator_editcurve;
+
+ /* flags */
+ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+
+ /* properties */
+ PropertyRNA *prop;
+
+ prop = RNA_def_float_distance(
+ ot->srna, "error_threshold", 0.0f, 0.0f, 10.0f, "Error",
+ "Error distance threshold (in object units)",
+ 0.0001f, 10.0f);
+ RNA_def_property_ui_range(prop, 0.0, 10, 1, 4);
+
+ prop = RNA_def_float_distance(
+ ot->srna, "corner_angle", DEG2RADF(70.0f), 0.0f, M_PI, "Corner Angle", "", 0.0f, M_PI);
+ RNA_def_property_subtype(prop, PROP_ANGLE);
+
+ prop = RNA_def_collection_runtime(ot->srna, "stroke", &RNA_OperatorStrokeElement, "Stroke", "");
+ RNA_def_property_flag(prop, PROP_SKIP_SAVE);
+
+ prop = RNA_def_boolean(ot->srna, "wait_for_input", true, "Wait for Input", "");
+ RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE);
+}
+
+/** \} */
diff --git a/source/blender/editors/curve/editcurve_select.c b/source/blender/editors/curve/editcurve_select.c
index 0f3942b0c90..a29266294b4 100644
--- a/source/blender/editors/curve/editcurve_select.c
+++ b/source/blender/editors/curve/editcurve_select.c
@@ -999,20 +999,22 @@ void CURVE_OT_select_less(wmOperatorType *ot)
/********************** select random *********************/
-static void curve_select_random(ListBase *editnurb, float randfac, bool select)
+static void curve_select_random(ListBase *editnurb, float randfac, int seed, bool select)
{
Nurb *nu;
BezTriple *bezt;
BPoint *bp;
int a;
+ RNG *rng = BLI_rng_new_srandom(seed);
+
for (nu = editnurb->first; nu; nu = nu->next) {
if (nu->type == CU_BEZIER) {
bezt = nu->bezt;
a = nu->pntsu;
while (a--) {
if (!bezt->hide) {
- if (BLI_frand() < randfac) {
+ if (BLI_rng_get_float(rng) < randfac) {
select_beztriple(bezt, select, SELECT, VISIBLE);
}
}
@@ -1025,7 +1027,7 @@ static void curve_select_random(ListBase *editnurb, float randfac, bool select)
while (a--) {
if (!bp->hide) {
- if (BLI_frand() < randfac) {
+ if (BLI_rng_get_float(rng) < randfac) {
select_bpoint(bp, select, SELECT, VISIBLE);
}
}
@@ -1033,6 +1035,8 @@ static void curve_select_random(ListBase *editnurb, float randfac, bool select)
}
}
}
+
+ BLI_rng_free(rng);
}
static int curve_select_random_exec(bContext *C, wmOperator *op)
@@ -1041,8 +1045,9 @@ static int curve_select_random_exec(bContext *C, wmOperator *op)
ListBase *editnurb = object_editcurve_get(obedit);
const bool select = (RNA_enum_get(op->ptr, "action") == SEL_SELECT);
const float randfac = RNA_float_get(op->ptr, "percent") / 100.0f;
+ const int seed = WM_operator_properties_select_random_seed_increment_get(op);
- curve_select_random(editnurb, randfac, select);
+ curve_select_random(editnurb, randfac, seed, select);
BKE_curve_nurb_vert_active_validate(obedit->data);
WM_event_add_notifier(C, NC_GEOM | ND_SELECT, obedit->data);
@@ -1065,14 +1070,12 @@ void CURVE_OT_select_random(wmOperatorType *ot)
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
/* properties */
- RNA_def_float_percentage(ot->srna, "percent", 50.f, 0.0f, 100.0f,
- "Percent", "Percentage of elements to select randomly", 0.0f, 100.0f);
- WM_operator_properties_select_action_simple(ot, SEL_SELECT);
+ WM_operator_properties_select_random(ot);
}
/********************* every nth number of point *******************/
-static void select_nth_bezt(Nurb *nu, BezTriple *bezt, int nth, int skip, int offset)
+static void select_nth_bezt(Nurb *nu, BezTriple *bezt, const struct CheckerIntervalParams *params)
{
int a, start;
@@ -1082,7 +1085,7 @@ static void select_nth_bezt(Nurb *nu, BezTriple *bezt, int nth, int skip, int of
while (a--) {
const int depth = abs(start - a);
- if ((offset + depth) % (skip + nth) >= skip) {
+ if (WM_operator_properties_checker_interval_test(params, depth)) {
select_beztriple(bezt, DESELECT, SELECT, HIDDEN);
}
@@ -1090,7 +1093,7 @@ static void select_nth_bezt(Nurb *nu, BezTriple *bezt, int nth, int skip, int of
}
}
-static void select_nth_bp(Nurb *nu, BPoint *bp, int nth, int skip, int offset)
+static void select_nth_bp(Nurb *nu, BPoint *bp, const struct CheckerIntervalParams *params)
{
int a, startrow, startpnt;
int row, pnt;
@@ -1105,7 +1108,7 @@ static void select_nth_bp(Nurb *nu, BPoint *bp, int nth, int skip, int offset)
while (a--) {
const int depth = abs(pnt - startpnt) + abs(row - startrow);
- if ((offset + depth) % (skip + nth) >= skip) {
+ if (WM_operator_properties_checker_interval_test(params, depth)) {
select_bpoint(bp, DESELECT, SELECT, HIDDEN);
}
@@ -1119,7 +1122,7 @@ static void select_nth_bp(Nurb *nu, BPoint *bp, int nth, int skip, int offset)
}
}
-bool ED_curve_select_nth(Curve *cu, int nth, int skip, int offset)
+static bool ed_curve_select_nth(Curve *cu, const struct CheckerIntervalParams *params)
{
Nurb *nu = NULL;
void *vert = NULL;
@@ -1128,10 +1131,10 @@ bool ED_curve_select_nth(Curve *cu, int nth, int skip, int offset)
return false;
if (nu->bezt) {
- select_nth_bezt(nu, vert, nth, skip, offset);
+ select_nth_bezt(nu, vert, params);
}
else {
- select_nth_bp(nu, vert, nth, skip, offset);
+ select_nth_bp(nu, vert, params);
}
return true;
@@ -1140,14 +1143,11 @@ bool ED_curve_select_nth(Curve *cu, int nth, int skip, int offset)
static int select_nth_exec(bContext *C, wmOperator *op)
{
Object *obedit = CTX_data_edit_object(C);
- const int nth = RNA_int_get(op->ptr, "nth") - 1;
- const int skip = RNA_int_get(op->ptr, "skip");
- int offset = RNA_int_get(op->ptr, "offset");
+ struct CheckerIntervalParams op_params;
- /* so input of offset zero ends up being (nth - 1) */
- offset = mod_i(offset, nth + skip);
+ WM_operator_properties_checker_interval_from_op(op, &op_params);
- if (!ED_curve_select_nth(obedit->data, nth, skip, offset)) {
+ if (!ed_curve_select_nth(obedit->data, &op_params)) {
if (obedit->type == OB_SURF) {
BKE_report(op->reports, RPT_ERROR, "Surface has not got active point");
}
@@ -1177,9 +1177,7 @@ void CURVE_OT_select_nth(wmOperatorType *ot)
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
- RNA_def_int(ot->srna, "nth", 2, 2, INT_MAX, "Nth Selection", "", 2, 100);
- RNA_def_int(ot->srna, "skip", 1, 1, INT_MAX, "Skip", "", 1, 100);
- RNA_def_int(ot->srna, "offset", 0, INT_MIN, INT_MAX, "Offset", "", -100, 100);
+ WM_operator_properties_checker_interval(ot, false);
}
diff --git a/source/blender/editors/curve/editfont.c b/source/blender/editors/curve/editfont.c
index ea6b6154be8..7c1fe0cadf0 100644
--- a/source/blender/editors/curve/editfont.c
+++ b/source/blender/editors/curve/editfont.c
@@ -51,6 +51,7 @@
#include "BKE_curve.h"
#include "BKE_depsgraph.h"
#include "BKE_font.h"
+#include "BKE_library.h"
#include "BKE_main.h"
#include "BKE_object.h"
#include "BKE_report.h"
@@ -344,50 +345,18 @@ static bool font_paste_utf8(bContext *C, const char *str, const size_t str_len)
static int paste_from_file(bContext *C, ReportList *reports, const char *filename)
{
Object *obedit = CTX_data_edit_object(C);
- FILE *fp;
char *strp;
- int filelen;
+ size_t filelen;
int retval;
- fp = BLI_fopen(filename, "r");
-
- if (!fp) {
+ strp = BLI_file_read_text_as_mem(filename, 1, &filelen);
+ if (strp == NULL) {
BKE_reportf(reports, RPT_ERROR, "Failed to open file '%s'", filename);
return OPERATOR_CANCELLED;
}
+ strp[filelen] = 0;
- fseek(fp, 0L, SEEK_END);
-
- errno = 0;
- filelen = ftell(fp);
- if (filelen == -1) {
- goto fail;
- }
-
- if (filelen <= MAXTEXT) {
- strp = MEM_mallocN(filelen + 4, "tempstr");
-
- fseek(fp, 0L, SEEK_SET);
-
- /* fread() instead of read(), because windows read() converts text
- * to DOS \r\n linebreaks, causing double linebreaks in the 3d text */
- errno = 0;
- filelen = fread(strp, 1, filelen, fp);
- if (filelen == -1) {
- MEM_freeN(strp);
- goto fail;
- }
-
- strp[filelen] = 0;
- }
- else {
- strp = NULL;
- }
-
- fclose(fp);
-
-
- if (strp && font_paste_utf8(C, strp, filelen)) {
+ if (font_paste_utf8(C, strp, filelen)) {
text_update_edited(C, obedit, FO_EDIT);
retval = OPERATOR_FINISHED;
@@ -397,18 +366,9 @@ static int paste_from_file(bContext *C, ReportList *reports, const char *filenam
retval = OPERATOR_CANCELLED;
}
- if (strp) {
- MEM_freeN(strp);
- }
+ MEM_freeN(strp);
return retval;
-
-
- /* failed to seek or read */
-fail:
- BKE_reportf(reports, RPT_ERROR, "Failed to read file '%s', %s", filename, strerror(errno));
- fclose(fp);
- return OPERATOR_CANCELLED;
}
static int paste_from_file_exec(bContext *C, wmOperator *op)
@@ -449,66 +409,9 @@ void FONT_OT_text_paste_from_file(wmOperatorType *ot)
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
/* properties */
- WM_operator_properties_filesel(ot, FILE_TYPE_FOLDER | FILE_TYPE_TEXT, FILE_SPECIAL, FILE_OPENFILE,
- WM_FILESEL_FILEPATH, FILE_DEFAULTDISPLAY, FILE_SORT_ALPHA);
-}
-
-
-/* -------------------------------------------------------------------- */
-/* Paste From Clipboard */
-
-static int paste_from_clipboard(bContext *C, ReportList *reports)
-{
- Object *obedit = CTX_data_edit_object(C);
- char *strp;
- int filelen;
- int retval;
-
- strp = WM_clipboard_text_get(false, &filelen);
- if (strp == NULL) {
- BKE_report(reports, RPT_ERROR, "Clipboard empty");
- return OPERATOR_CANCELLED;
- }
-
- if ((filelen <= MAXTEXT) && font_paste_utf8(C, strp, filelen)) {
- text_update_edited(C, obedit, FO_EDIT);
- retval = OPERATOR_FINISHED;
- }
- else {
- BKE_report(reports, RPT_ERROR, "Clipboard too long");
- retval = OPERATOR_CANCELLED;
- }
- MEM_freeN(strp);
-
- return retval;
-}
-
-static int paste_from_clipboard_exec(bContext *C, wmOperator *op)
-{
- int retval;
-
- retval = paste_from_clipboard(C, op->reports);
-
- return retval;
-}
-
-void FONT_OT_text_paste_from_clipboard(wmOperatorType *ot)
-{
- /* identifiers */
- ot->name = "Paste Clipboard";
- ot->description = "Paste contents from system clipboard";
- ot->idname = "FONT_OT_text_paste_from_clipboard";
-
- /* api callbacks */
- ot->exec = paste_from_clipboard_exec;
- ot->poll = ED_operator_editfont;
-
- /* flags */
- ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
-
- /* properties */
- WM_operator_properties_filesel(ot, FILE_TYPE_FOLDER | FILE_TYPE_TEXT, FILE_SPECIAL, FILE_OPENFILE,
- WM_FILESEL_FILEPATH, FILE_DEFAULTDISPLAY, FILE_SORT_ALPHA);
+ WM_operator_properties_filesel(
+ ot, FILE_TYPE_FOLDER | FILE_TYPE_TEXT, FILE_SPECIAL, FILE_OPENFILE,
+ WM_FILESEL_FILEPATH, FILE_DEFAULTDISPLAY, FILE_SORT_ALPHA);
}
/******************* text to object operator ********************/
@@ -538,7 +441,7 @@ static void txt_add_object(bContext *C, TextLine *firstline, int totline, const
cu = obedit->data;
cu->vfont = BKE_vfont_builtin_get();
- cu->vfont->id.us++;
+ id_us_plus(&cu->vfont->id);
for (tmp = firstline, a = 0; nbytes < MAXTEXT && a < totline; tmp = tmp->next, a++) {
size_t nchars_line, nbytes_line;
@@ -799,10 +702,21 @@ static void copy_selection(Object *obedit)
if (BKE_vfont_select_get(obedit, &selstart, &selend)) {
Curve *cu = obedit->data;
EditFont *ef = cu->editfont;
-
- memcpy(ef->copybuf, ef->textbuf + selstart, ((selend - selstart) + 1) * sizeof(wchar_t));
- ef->copybuf[(selend - selstart) + 1] = 0;
- memcpy(ef->copybufinfo, ef->textbufinfo + selstart, ((selend - selstart) + 1) * sizeof(CharInfo));
+ char *buf = NULL;
+ wchar_t *text_buf;
+ size_t len_utf8;
+
+ /* internal clipboard (for style) */
+ BKE_vfont_clipboard_set(ef->textbuf + selstart, ef->textbufinfo + selstart, selend - selstart + 1);
+ BKE_vfont_clipboard_get(&text_buf, NULL, &len_utf8, NULL);
+
+ /* system clipboard */
+ buf = MEM_mallocN(len_utf8 + 1, __func__);
+ if (buf) {
+ BLI_strncpy_wchar_as_utf8(buf, text_buf, len_utf8 + 1);
+ WM_clipboard_text_set(buf, false);
+ MEM_freeN(buf);
+ }
}
}
@@ -864,11 +778,13 @@ void FONT_OT_text_cut(wmOperatorType *ot)
static bool paste_selection(Object *obedit, ReportList *reports)
{
- Curve *cu = obedit->data;
- EditFont *ef = cu->editfont;
- int len = wcslen(ef->copybuf);
+ wchar_t *text_buf;
+ CharInfo *info_buf;
+ size_t len;
+
+ BKE_vfont_clipboard_get(&text_buf, &info_buf, NULL, &len);
- if (font_paste_wchar(obedit, ef->copybuf, len, ef->copybufinfo)) {
+ if (font_paste_wchar(obedit, text_buf, len, info_buf)) {
return true;
}
else {
@@ -880,13 +796,68 @@ static bool paste_selection(Object *obedit, ReportList *reports)
static int paste_text_exec(bContext *C, wmOperator *op)
{
Object *obedit = CTX_data_edit_object(C);
+ int retval;
+ size_t len_utf8;
+ wchar_t *text_buf;
+
+ /* Store both clipboards as utf8 for comparison,
+ * Give priority to the internal 'vfont' clipboard with its 'CharInfo' text styles
+ * as long as its synchronized with the systems clipboard. */
+ struct {
+ char *buf;
+ int len;
+ } clipboard_system = {NULL}, clipboard_vfont = {NULL};
+
+ clipboard_system.buf = WM_clipboard_text_get(false, &clipboard_system.len);
- if (!paste_selection(obedit, op->reports))
+ if (clipboard_system.buf == NULL) {
return OPERATOR_CANCELLED;
+ }
- text_update_edited(C, obedit, FO_EDIT);
+ BKE_vfont_clipboard_get(&text_buf, NULL, &len_utf8, NULL);
- return OPERATOR_FINISHED;
+ if (text_buf) {
+ clipboard_vfont.buf = MEM_mallocN(len_utf8 + 1, __func__);
+
+ if (clipboard_vfont.buf == NULL) {
+ MEM_freeN(clipboard_system.buf);
+ return OPERATOR_CANCELLED;
+ }
+
+ BLI_strncpy_wchar_as_utf8(clipboard_vfont.buf, text_buf, len_utf8 + 1);
+ }
+
+ if (clipboard_vfont.buf && STREQ(clipboard_vfont.buf, clipboard_system.buf)) {
+ retval = paste_selection(obedit, op->reports) ? OPERATOR_FINISHED : OPERATOR_CANCELLED;
+ }
+ else {
+ if ((clipboard_system.len <= MAXTEXT) &&
+ font_paste_utf8(C, clipboard_system.buf, clipboard_system.len))
+ {
+ text_update_edited(C, obedit, FO_EDIT);
+ retval = OPERATOR_FINISHED;
+ }
+ else {
+ BKE_report(op->reports, RPT_ERROR, "Clipboard too long");
+ retval = OPERATOR_CANCELLED;
+ }
+
+ /* free the existent clipboard buffer */
+ BKE_vfont_clipboard_free();
+ }
+
+ if (retval != OPERATOR_CANCELLED) {
+ text_update_edited(C, obedit, FO_EDIT);
+ }
+
+ /* cleanup */
+ if (clipboard_vfont.buf) {
+ MEM_freeN(clipboard_vfont.buf);
+ }
+
+ MEM_freeN(clipboard_system.buf);
+
+ return retval;
}
void FONT_OT_text_paste(wmOperatorType *ot)
@@ -1509,7 +1480,7 @@ void FONT_OT_textbox_remove(wmOperatorType *ot)
/***************** editmode enter/exit ********************/
-void make_editText(Object *obedit)
+void ED_curve_editfont_make(Object *obedit)
{
Curve *cu = obedit->data;
EditFont *ef = cu->editfont;
@@ -1520,8 +1491,6 @@ void make_editText(Object *obedit)
ef->textbuf = MEM_callocN((MAXTEXT + 4) * sizeof(wchar_t), "texteditbuf");
ef->textbufinfo = MEM_callocN((MAXTEXT + 4) * sizeof(CharInfo), "texteditbufinfo");
- ef->copybuf = MEM_callocN((MAXTEXT + 4) * sizeof(wchar_t), "texteditcopybuf");
- ef->copybufinfo = MEM_callocN((MAXTEXT + 4) * sizeof(CharInfo), "texteditcopybufinfo");
}
/* Convert the original text to wchar_t */
@@ -1545,7 +1514,7 @@ void make_editText(Object *obedit)
BKE_vfont_select_clamp(obedit);
}
-void load_editText(Object *obedit)
+void ED_curve_editfont_load(Object *obedit)
{
Curve *cu = obedit->data;
EditFont *ef = cu->editfont;
@@ -1574,7 +1543,7 @@ void load_editText(Object *obedit)
cu->selend = ef->selend;
}
-void free_editText(Object *obedit)
+void ED_curve_editfont_free(Object *obedit)
{
BKE_curve_editfont_free((Curve *)obedit->data);
}
@@ -1727,7 +1696,7 @@ static int font_open_exec(bContext *C, wmOperator *op)
if (pprop->prop) {
/* when creating new ID blocks, use is already 1, but RNA
* pointer se also increases user, so this compensates it */
- font->id.us--;
+ id_us_min(&font->id);
RNA_id_pointer_create(&font->id, &idptr);
RNA_property_pointer_set(&pprop->ptr, pprop->prop, idptr);
@@ -1784,8 +1753,9 @@ void FONT_OT_open(wmOperatorType *ot)
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
/* properties */
- WM_operator_properties_filesel(ot, FILE_TYPE_FOLDER | FILE_TYPE_FTFONT, FILE_SPECIAL, FILE_OPENFILE,
- WM_FILESEL_FILEPATH | WM_FILESEL_RELPATH, FILE_DEFAULTDISPLAY, FILE_SORT_ALPHA);
+ WM_operator_properties_filesel(
+ ot, FILE_TYPE_FOLDER | FILE_TYPE_FTFONT, FILE_SPECIAL, FILE_OPENFILE,
+ WM_FILESEL_FILEPATH | WM_FILESEL_RELPATH, FILE_DEFAULTDISPLAY, FILE_SORT_ALPHA);
}
/******************* delete operator *********************/
@@ -1885,7 +1855,7 @@ void undo_push_font(bContext *C, const char *name)
/**
* TextBox selection
*/
-bool mouse_font(bContext *C, const int mval[2], bool extend, bool deselect, bool toggle)
+bool ED_curve_editfont_select_pick(bContext *C, const int mval[2], bool extend, bool deselect, bool toggle)
{
Object *obedit = CTX_data_edit_object(C);
Curve *cu = obedit->data;