diff options
author | Lukas Tönne <lukas.toenne@gmail.com> | 2016-04-20 17:45:29 +0300 |
---|---|---|
committer | Lukas Tönne <lukas.toenne@gmail.com> | 2016-04-20 17:45:29 +0300 |
commit | aae0598aa233d43d0ffc81a070b2ce20d6871c1d (patch) | |
tree | 378100f45cf1db972c671a6886496a5604f7ab29 /source/blender/editors/curve | |
parent | 95d7d3c2a6aa91f1dcb33ff0cdfc751e2886d8cd (diff) | |
parent | d7e4f920fd93a4ae5679e0eb6b228a349ab3b082 (diff) |
Merge branch 'master' into temp_depsgraph_split_ubereval
Diffstat (limited to 'source/blender/editors/curve')
-rw-r--r-- | source/blender/editors/curve/CMakeLists.txt | 8 | ||||
-rw-r--r-- | source/blender/editors/curve/SConscript | 49 | ||||
-rw-r--r-- | source/blender/editors/curve/curve_intern.h | 6 | ||||
-rw-r--r-- | source/blender/editors/curve/curve_ops.c | 6 | ||||
-rw-r--r-- | source/blender/editors/curve/editcurve.c | 219 | ||||
-rw-r--r-- | source/blender/editors/curve/editcurve_add.c | 15 | ||||
-rw-r--r-- | source/blender/editors/curve/editcurve_paint.c | 1180 | ||||
-rw-r--r-- | source/blender/editors/curve/editcurve_select.c | 44 | ||||
-rw-r--r-- | source/blender/editors/curve/editfont.c | 226 |
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; |